diff --git a/.clang-format b/.clang-format index 3337a7409cf..4b76f7fa43b 100644 --- a/.clang-format +++ b/.clang-format @@ -1,86 +1,191 @@ +--- +Language: Cpp AccessModifierOffset: -4 AlignAfterOpenBracket: AlwaysBreak -AlignConsecutiveAssignments: false -AlignConsecutiveDeclarations: false +AlignArrayOfStructures: None +AlignConsecutiveMacros: None +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignConsecutiveDeclarations: None AlignEscapedNewlines: Left -AlignOperands: true +AlignOperands: Align AlignTrailingComments: false +AllowAllArgumentsOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: false +AllowShortEnumsOnASingleLine: true AllowShortBlocksOnASingleLine: Never AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: None -AllowShortIfStatementsOnASingleLine: true +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: WithoutElse AllowShortLoopsOnASingleLine: true AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: 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 +ColumnLimit: 99 +CommentPragmas: '^ IWYU pragma:' +QualifierAlignment: Leave CompactNamespaces: false -ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true +DeriveLineEnding: true DerivePointerAlignment: false -DisableFormat: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock ExperimentalAutoDetectBinPacking: false +PackConstructorInitializers: BinPack +BasedOnStyle: '' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +AllowAllConstructorInitializersOnNextLine: true FixNamespaceComments: false - -IncludeBlocks: Preserve +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve IncludeCategories: - - Regex: '.*' - Priority: 1 + - 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 -IndentWidth: 4 +IndentExternBlock: AfterExternBlock +IndentRequires: false +IndentWidth: 4 IndentWrappedFunctionNames: true +InsertTrailingCommas: None JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: false +LambdaBodyIndentation: Signature MacroBlockBegin: '' -MacroBlockEnd: '' +MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBinPackProtocolList: Auto ObjCBlockIndentWidth: 4 +ObjCBreakBeforeNestedBlockParam: true ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: true - -# Taken from git's rules PenaltyBreakAssignment: 10 PenaltyBreakBeforeFirstCallParameter: 30 PenaltyBreakComment: 10 PenaltyBreakFirstLessLess: 0 +PenaltyBreakOpenParenthesis: 0 PenaltyBreakString: 10 +PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 100 PenaltyReturnTypeOnItsOwnLine: 60 - +PenaltyIndentedWhitespace: 0 PointerAlignment: Left -ReflowComments: false -SortIncludes: false +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: false +SpacesInAngles: Never +SpacesInConditionalStatement: false SpacesInContainerLiterals: false SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 SpacesInParentheses: false SpacesInSquareBrackets: false -Standard: Cpp03 -TabWidth: 4 -UseTab: Never +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/.github/ISSUE_TEMPLATE/03_feature_request.yml b/.github/ISSUE_TEMPLATE/03_feature_request.yml index 2af114547de..052cd34048a 100644 --- a/.github/ISSUE_TEMPLATE/03_feature_request.yml +++ b/.github/ISSUE_TEMPLATE/03_feature_request.yml @@ -14,7 +14,7 @@ body: description: | Please describe your feature request in as many details as possible. - Describe what it should do. - - Note whetever it is to extend existing functionality or introduce new functionality. + - Note whether it is to extend existing functionality or introduce new functionality. validations: required: true - type: textarea diff --git a/.github/actions/docker/action.yml b/.github/actions/docker/action.yml deleted file mode 100644 index 3608f96e67b..00000000000 --- a/.github/actions/docker/action.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: 'Run in docker' -inputs: - run: # id of input - description: 'A command to run' - required: true - default: '' -runs: - using: 'docker' - image: '../../../docker/Dockerfile' - args: - - ${{ inputs.run }} diff --git a/.github/actions/submit_sdk/action.yml b/.github/actions/submit_sdk/action.yml new file mode 100644 index 00000000000..b515b528559 --- /dev/null +++ b/.github/actions/submit_sdk/action.yml @@ -0,0 +1,78 @@ +name: Submit SDK to Catalog +author: hedger +description: | + This action checks if SDK exists in the catalog and if not, adds and/or publishes it. + +inputs: + catalog-url: + description: The URL of the Catalog API. Must not be empty or end with a /. + required: true + catalog-api-token: + description: The token to use to authenticate with the Catalog API. Must not be empty. + required: true + firmware-api: + description: Fimware's API version, major.minor + required: true + firmware-target: + description: Firmware's target, e.g. f7/f18 + required: true + firmware-version: + description: Firmware's version, e.g. 0.13.37-rc3, or 0.13.37 + required: true + +runs: + using: composite + steps: + - name: Check inputs + shell: bash + run: | + if [ -z "${{ inputs.catalog-url }}" ] ; then + echo "Invalid catalog-url: ${{ inputs.catalog-url }}" + exit 1 + fi + if [ -z "${{ inputs.catalog-api-token }}" ] ; then + echo "Invalid catalog-api-token: ${{ inputs.catalog-api-token }}" + exit 1 + fi + if ! echo "${{ inputs.firmware-api }}" | grep -q "^[0-9]\+\.[0-9]\+$" ; then + echo "Invalid firmware-api: ${{ inputs.firmware-api }}" + exit 1 + fi + if ! echo "${{ inputs.firmware-target }}" | grep -q "^f[0-9]\+$" ; then + echo "Invalid firmware-target: ${{ inputs.firmware-target }}" + exit 1 + fi + if ! echo "${{ inputs.firmware-version }}" | grep -q "^[0-9]\+\.[0-9]\+\.[0-9]\+\(-rc\)\?\([0-9]\+\)\?$" ; then + echo "Invalid firmware-version: ${{ inputs.firmware-version }}" + exit 1 + fi + - name: Submit SDK + shell: bash + run: | + curl -sX 'GET' \ + '${{ inputs.catalog-url }}/api/v0/0/sdk?length=500' \ + -H 'Accept: application/json' > sdk_versions.json + if jq -r -e ".[] | select((.api == \"${{ inputs.firmware-api }}\") and .target == \"${{ inputs.firmware-target }}\")" sdk_versions.json > found_sdk.json ; then + echo "API version ${{ inputs.firmware-api }} already exists in catalog" + if [ $(jq -r -e ".released_at" found_sdk.json) != "null" ] ; then + echo "API version is already released" + exit 0 + fi + if ! echo "${{ inputs.firmware-version }}" | grep -q -- "-rc" ; then + SDK_ID=$(jq -r ._id found_sdk.json) + echo "Marking SDK $SDK_ID as released" + curl -X 'POST' \ + "${{ inputs.catalog-url }}/api/v0/0/sdk/${SDK_ID}/release" \ + -H 'Accept: application/json' \ + -H 'Authorization: Bearer ${{ inputs.catalog-api-token }}' \ + -d '' + fi + else + echo "API version ${{ inputs.firmware-api }} doesn't exist in catalog, adding" + curl -X 'POST' \ + '${{ inputs.catalog-url }}/api/v0/0/sdk' \ + -H 'Accept: application/json' \ + -H 'Authorization: Bearer ${{ inputs.catalog-api-token }}' \ + -H 'Content-Type: application/json' \ + -d "{\"name\": \"${{ inputs.firmware-version }}\", \"target\": \"${{ inputs.firmware-target }}\", \"api\": \"${{ inputs.firmware-api }}\"}" + fi diff --git a/.github/assets/dark_theme_banner.png b/.github/assets/dark_theme_banner.png new file mode 100644 index 00000000000..10a4e8c3009 Binary files /dev/null and b/.github/assets/dark_theme_banner.png differ diff --git a/.github/assets/light_theme_banner.png b/.github/assets/light_theme_banner.png new file mode 100644 index 00000000000..6c6b3b7080a Binary files /dev/null and b/.github/assets/light_theme_banner.png differ diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a1ae875a8ba..38b1d7b6886 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,188 +4,167 @@ on: push: branches: - dev - - "release*" tags: - '*' pull_request: env: - TARGETS: f7 DEFAULT_TARGET: f7 + FBT_TOOLCHAIN_PATH: /runner/_work + FBT_GIT_SUBMODULE_SHALLOW: 1 jobs: main: - runs-on: [self-hosted,FlipperZeroShell] + runs-on: [self-hosted, FlipperZeroShell] + strategy: + fail-fast: false + matrix: + target: [f7, f18] 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: 'Wipe workspace' + run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; - name: 'Checkout code' - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: - fetch-depth: 0 + fetch-depth: 1 ref: ${{ github.event.pull_request.head.sha }} - - name: 'Make artifacts directory' - run: | - test -d artifacts && rm -rf artifacts || true - mkdir artifacts - - - name: 'Generate suffix and folder name' + - name: 'Get commit details' id: names run: | - REF=${{ github.ref }} + BUILD_TYPE='DEBUG=1 COMPACT=0' if [[ ${{ github.event_name }} == 'pull_request' ]]; then - REF=${{ github.head_ref }} - fi - BRANCH_OR_TAG=${REF#refs/*/} - SHA=$(git rev-parse --short HEAD) - - if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then - SUFFIX=${BRANCH_OR_TAG//\//_} + TYPE="pull" + elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then + TYPE="tag" + BUILD_TYPE='DEBUG=0 COMPACT=1' else - SUFFIX=${BRANCH_OR_TAG//\//_}-$(date +'%d%m%Y')-${SHA} + TYPE="other" fi + python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" || cat "${{ github.event_path }}" + echo "event_type=$TYPE" >> $GITHUB_OUTPUT + echo "FBT_BUILD_TYPE=$BUILD_TYPE" >> $GITHUB_ENV + echo "TARGET=${{ matrix.target }}" >> $GITHUB_ENV + echo "TARGET_HW=$(echo "${{ matrix.target }}" | sed 's/f//')" >> $GITHUB_ENV - echo "WORKFLOW_BRANCH_OR_TAG=${BRANCH_OR_TAG}" >> $GITHUB_ENV - echo "DIST_SUFFIX=${SUFFIX}" >> $GITHUB_ENV - echo "::set-output name=artifacts-path::${BRANCH_OR_TAG}" - echo "::set-output name=suffix::${SUFFIX}" - echo "::set-output name=short-hash::${SHA}" - echo "::set-output name=default-target::${DEFAULT_TARGET}" - - - name: 'Bundle scripts' - if: ${{ !github.event.pull_request.head.repo.fork }} - run: | - tar czpf artifacts/flipper-z-any-scripts-${{steps.names.outputs.suffix}}.tgz scripts - - - name: 'Build the firmware' + - name: 'Check API versions for consistency between targets' run: | set -e - for TARGET in ${TARGETS} - do - FBT_TOOLCHAIN_PATH=/opt ./fbt TARGET_HW=`echo ${TARGET} | sed 's/f//'` updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} - done + N_API_HEADER_SIGNATURES=`ls -1 targets/f*/api_symbols.csv | xargs -I {} sh -c "head -n2 {} | md5sum" | sort -u | wc -l` + if [ $N_API_HEADER_SIGNATURES != 1 ] ; then + echo API versions aren\'t matching for available targets. Please update! + echo API versions are: + head -n2 targets/f*/api_symbols.csv + exit 1 + fi - - name: 'Move upload files' - if: ${{ !github.event.pull_request.head.repo.fork }} + - name: 'Build the firmware and apps' + id: build-fw run: | - set -e - for TARGET in ${TARGETS} - do - mv dist/${TARGET}-*/* artifacts/ - done + ./fbt TARGET_HW=$TARGET_HW $FBT_BUILD_TYPE copro_dist updater_package fap_dist + echo "firmware_api=$(./fbt TARGET_HW=$TARGET_HW get_apiversion)" >> $GITHUB_OUTPUT - - name: 'Bundle self-update package' - if: ${{ !github.event.pull_request.head.repo.fork }} - run: | - set -e - for UPDATEBUNDLE in artifacts/*/ - do - BUNDLE_NAME=`echo $UPDATEBUNDLE | cut -d'/' -f2` - echo Packaging ${BUNDLE_NAME} - tar czpf artifacts/flipper-z-${BUNDLE_NAME}.tgz -C artifacts ${BUNDLE_NAME} - rm -rf artifacts/${BUNDLE_NAME} - done - - - name: "Check for uncommited changes" + - name: 'Check for uncommitted changes' run: | git diff --exit-code - - name: 'Bundle resources' - if: ${{ !github.event.pull_request.head.repo.fork }} + - name: 'Copy build output' run: | - tar czpf artifacts/flipper-z-any-resources-${{steps.names.outputs.suffix}}.tgz -C assets resources - - - name: 'Bundle core2 firmware' - if: ${{ !github.event.pull_request.head.repo.fork }} + set -e + rm -rf artifacts map_analyser_files || true + mkdir artifacts map_analyser_files + cp dist/${TARGET}-*/* artifacts/ || true + tar czpf "artifacts/flipper-z-${TARGET}-resources-${SUFFIX}.tgz" \ + -C build/latest resources + tar czpf "artifacts/flipper-z-${TARGET}-debugapps-${SUFFIX}.tgz" \ + -C dist/${TARGET}-*/apps/Debug . + tar czpf "artifacts/flipper-z-${TARGET}-appsymbols-${SUFFIX}.tgz" \ + -C dist/${TARGET}-*/debug_elf . + + - name: 'Copy universal artifacts' + if: ${{ !github.event.pull_request.head.repo.fork && matrix.target == env.DEFAULT_TARGET }} run: | - FBT_TOOLCHAIN_PATH=/opt ./fbt copro_dist - tar czpf artifacts/flipper-z-any-core2_firmware-${{steps.names.outputs.suffix}}.tgz -C assets core2_firmware + tar czpf "artifacts/flipper-z-any-scripts-${SUFFIX}.tgz" scripts + cp build/core2_firmware.tgz "artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz" - name: 'Upload artifacts to update server' if: ${{ !github.event.pull_request.head.repo.fork }} run: | - echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key; - chmod 600 ./deploy_key; - rsync -avzP --delete --mkpath \ - -e 'ssh -p ${{ secrets.RSYNC_DEPLOY_PORT }} -i ./deploy_key' \ - artifacts/ ${{ secrets.RSYNC_DEPLOY_USER }}@${{ secrets.RSYNC_DEPLOY_HOST }}:"${{ secrets.RSYNC_DEPLOY_BASE_PATH }}${{steps.names.outputs.artifacts-path}}/"; - rm ./deploy_key; - - - name: 'Trigger update server reindex' - if: ${{ !github.event.pull_request.head.repo.fork }} - run: curl -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }} - - - name: 'Find Previous Comment' - if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request }} - uses: peter-evans/find-comment@v1 - id: fc + FILES=$(for ARTIFACT in $(find artifacts -maxdepth 1 -not -type d); do echo "-F files=@${ARTIFACT}"; done) + curl --fail -L -H "Token: ${{ secrets.INDEXER_TOKEN }}" \ + -F "branch=${BRANCH_NAME}" \ + -F "version_token=${COMMIT_SHA}" \ + ${FILES[@]} \ + "${{ secrets.INDEXER_URL }}"/firmware/uploadfiles + + - name: 'Copy & analyse map analyser files' + if: ${{ !github.event.pull_request.head.repo.fork && matrix.target == env.DEFAULT_TARGET }} + run: | + cp build/${DEFAULT_TARGET}-firmware-*/firmware.elf.map map_analyser_files/firmware.elf.map + cp build/${DEFAULT_TARGET}-firmware-*/firmware.elf map_analyser_files/firmware.elf + cp ${{ github.event_path }} map_analyser_files/event.json + source scripts/toolchain/fbtenv.sh + get_size() + { + SECTION="$1"; + arm-none-eabi-size \ + -A map_analyser_files/firmware.elf \ + | grep "^$SECTION" | awk '{print $2}' + } + export BSS_SIZE="$(get_size ".bss")" + export TEXT_SIZE="$(get_size ".text")" + export RODATA_SIZE="$(get_size ".rodata")" + export DATA_SIZE="$(get_size ".data")" + export FREE_FLASH_SIZE="$(get_size ".free_flash")" + python3 -m pip install mariadb==1.1.6 cxxfilt==0.3.0 + python3 scripts/map_parser.py map_analyser_files/firmware.elf.map map_analyser_files/firmware.elf.map.all + python3 scripts/map_mariadb_insert.py \ + ${{ secrets.AMAP_MARIADB_USER }} \ + ${{ secrets.AMAP_MARIADB_PASSWORD }} \ + ${{ secrets.AMAP_MARIADB_HOST }} \ + ${{ secrets.AMAP_MARIADB_PORT }} \ + ${{ secrets.AMAP_MARIADB_DATABASE }} \ + map_analyser_files/firmware.elf.map.all + + - name: 'Find previous comment' + if: ${{ !github.event.pull_request.head.repo.fork && matrix.target == env.DEFAULT_TARGET && github.event.pull_request }} + uses: peter-evans/find-comment@v2 + id: find-comment with: issue-number: ${{ github.event.pull_request.number }} comment-author: 'github-actions[bot]' - body-includes: 'Compiled firmware for commit' + body-includes: 'Compiled ${{ matrix.target }} firmware for commit' - name: 'Create or update comment' - if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request}} - uses: peter-evans/create-or-update-comment@v1 + if: ${{ !github.event.pull_request.head.repo.fork && matrix.target == env.DEFAULT_TARGET && github.event.pull_request }} + uses: peter-evans/create-or-update-comment@v3 with: - comment-id: ${{ steps.fc.outputs.comment-id }} + comment-id: ${{ steps.find-comment.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} body: | - **Compiled firmware for commit `${{steps.names.outputs.short-hash}}`:** - - [📦 Update package](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.artifacts-path}}/flipper-z-${{steps.names.outputs.default-target}}-update-${{steps.names.outputs.suffix}}.tgz) - - [📥 DFU file](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.artifacts-path}}/flipper-z-${{steps.names.outputs.default-target}}-full-${{steps.names.outputs.suffix}}.dfu) - - [☁️ Web updater](https://my.flipp.dev/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.artifacts-path}}/flipper-z-${{steps.names.outputs.default-target}}-update-${{steps.names.outputs.suffix}}.tgz&channel=${{steps.names.outputs.artifacts-path}}&version=${{steps.names.outputs.short-hash}}) + **Compiled ${{ matrix.target }} firmware for commit `${{steps.names.outputs.commit_sha}}`:** + - [📦 Update package](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-update-${{steps.names.outputs.suffix}}.tgz) + - [📥 DFU file](https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-full-${{steps.names.outputs.suffix}}.dfu) + - [☁️ Web/App updater](https://lab.flipper.net/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.branch_name}}/flipper-z-${{steps.names.outputs.default_target}}-update-${{steps.names.outputs.suffix}}.tgz&channel=${{steps.names.outputs.branch_name}}&version=${{steps.names.outputs.commit_sha}}) edit-mode: replace - compact: - if: ${{ !startsWith(github.ref, 'refs/tags') }} - 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@v2 + - name: 'SDK submission to staging catalog' + if: ${{ steps.names.outputs.event_type == 'tag' && matrix.target == env.DEFAULT_TARGET }} + uses: ./.github/actions/submit_sdk with: - fetch-depth: 0 - submodules: true - ref: ${{ github.event.pull_request.head.sha }} - - - name: 'Generate suffix and folder name' - id: names - run: | - REF=${{ github.ref }} - if [[ ${{ github.event_name }} == 'pull_request' ]]; then - REF=${{ github.head_ref }} - fi - BRANCH_OR_TAG=${REF#refs/*/} - SHA=$(git rev-parse --short HEAD) - - if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then - SUFFIX=${BRANCH_OR_TAG//\//_} - else - SUFFIX=${BRANCH_OR_TAG//\//_}-$(date +'%d%m%Y')-${SHA} - fi - - echo "WORKFLOW_BRANCH_OR_TAG=${BRANCH_OR_TAG}" >> $GITHUB_ENV - echo "DIST_SUFFIX=${SUFFIX}" >> $GITHUB_ENV - - - name: 'Build the firmware' - run: | - set -e - for TARGET in ${TARGETS} - do - FBT_TOOLCHAIN_PATH=/opt ./fbt TARGET_HW=`echo ${TARGET} | sed 's/f//'` updater_package DEBUG=0 COMPACT=1 - done + catalog-url: ${{ secrets.CATALOG_STAGING_URL }} + catalog-api-token: ${{ secrets.CATALOG_STAGING_API_TOKEN }} + firmware-api: ${{ steps.build-fw.outputs.firmware_api }} + firmware-target: ${{ matrix.target }} + firmware-version: ${{ steps.names.outputs.suffix }} + + - name: 'SDK submission to prod catalog' + if: ${{ steps.names.outputs.event_type == 'tag' && matrix.target == env.DEFAULT_TARGET }} + uses: ./.github/actions/submit_sdk + with: + catalog-url: ${{ secrets.CATALOG_URL }} + catalog-api-token: ${{ secrets.CATALOG_API_TOKEN }} + firmware-api: ${{ steps.build-fw.outputs.firmware_api }} + firmware-target: ${{ matrix.target }} + firmware-version: ${{ steps.names.outputs.suffix }} diff --git a/.github/workflows/build_compact.yml b/.github/workflows/build_compact.yml new file mode 100644 index 00000000000..f45275204a3 --- /dev/null +++ b/.github/workflows/build_compact.yml @@ -0,0 +1,86 @@ +name: 'Compact build' + +on: + pull_request: + +env: + FBT_TOOLCHAIN_PATH: /runner/_work + FBT_GIT_SUBMODULE_SHALLOW: 1 + +jobs: + compact: + runs-on: [self-hosted, FlipperZeroShell] + strategy: + fail-fast: false + matrix: + target: [f7, f18] + steps: + - name: 'Wipe workspace' + run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; + + - name: 'Checkout code' + uses: actions/checkout@v4 + with: + fetch-depth: 1 + submodules: false + 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" || cat "${{ github.event_path }}" + + - name: 'Build the firmware' + id: build-fw + run: | + set -e + TARGET="$(echo '${{ matrix.target }}' | sed 's/f//')"; \ + ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 fap_dist updater_package + echo "sdk-file=$(ls dist/${{ matrix.target }}-*/flipper-z-${{ matrix.target }}-sdk-*.zip)" >> $GITHUB_OUTPUT + echo "hw-target-code=$TARGET" >> $GITHUB_OUTPUT + + - name: Deploy uFBT with SDK + uses: flipperdevices/flipperzero-ufbt-action@v0.1 + with: + task: setup + sdk-file: ${{ steps.build-fw.outputs.sdk-file }} + sdk-hw-target: ${{ steps.build-fw.outputs.hw-target-code }} + + - name: Build test app with SDK + run: | + mkdir testapp + cd testapp + ufbt create APPID=testapp + ufbt + + - name: Build example & external apps with uFBT + run: | + for appdir in 'applications/examples'; do + for app in $(find "$appdir" -maxdepth 1 -mindepth 1 -type d); do + pushd $app + TARGETS_FAM=$(grep "targets" application.fam || echo "${{ matrix.target }}") + if ! grep -q "${{ matrix.target }}" <<< $TARGETS_FAM ; then + echo Skipping unsupported app: $app + popd + continue + fi + echo Building $app + ufbt + popd + done + done + +## Uncomment this for a single job that will run only if all targets are built successfully +# report-status: +# name: Report status +# needs: [compact] +# if: always() && !contains(needs.*.result, 'failure') +# runs-on: [self-hosted, FlipperZeroShell] +# steps: +# - run: echo "All good ✨" ; diff --git a/.github/workflows/check_submodules.yml b/.github/workflows/check_submodules.yml deleted file mode 100644 index f9699be87ae..00000000000 --- a/.github/workflows/check_submodules.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: 'Check submodules branch' - -on: - push: - branches: - - dev - - "release*" - tags: - - '*' - pull_request: - -jobs: - check_protobuf: - 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@v2 - with: - fetch-depth: 0 - - - name: 'Check protobuf branch' - run: | - SUB_PATH="assets/protobuf"; - SUB_BRANCH="dev"; - SUB_COMMITS_MIN=40; - cd "$SUB_PATH"; - SUBMODULE_HASH="$(git rev-parse HEAD)"; - BRANCHES=$(git branch -r --contains "$SUBMODULE_HASH"); - COMMITS_IN_BRANCH="$(git rev-list --count dev)"; - if [ $COMMITS_IN_BRANCH -lt $SUB_COMMITS_MIN ]; then - echo "::set-output name=fails::error"; - echo "::error::Error: Too low commits in $SUB_BRANCH of submodule $SUB_PATH: $COMMITS_IN_BRANCH(expected $SUB_COMMITS_MIN+)"; - exit 1; - fi - if ! grep -q "/$SUB_BRANCH" <<< "$BRANCHES"; then - echo "::set-output name=fails::error"; - echo "::error::Error: Submodule $SUB_PATH is not on branch $SUB_BRANCH"; - exit 1; - fi diff --git a/.github/workflows/lint_and_submodule_check.yml b/.github/workflows/lint_and_submodule_check.yml new file mode 100644 index 00000000000..3f4d8c79bd0 --- /dev/null +++ b/.github/workflows/lint_and_submodule_check.yml @@ -0,0 +1,101 @@ +name: 'Lint sources & check submodule integrity' + +on: + pull_request: + +env: + TARGETS: f7 + FBT_TOOLCHAIN_PATH: /runner/_work + SET_GH_OUTPUT: 1 + +jobs: + lint_sources_check_submodules: + runs-on: [self-hosted, FlipperZeroShell] + steps: + - name: 'Wipe workspace' + run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; + + - name: 'Checkout code' + uses: actions/checkout@v4 + with: + fetch-depth: 2 + ref: ${{ github.sha }} + + - name: 'Check protobuf branch' + run: | + git submodule update --init; + SUB_PATH="assets/protobuf"; + SUB_BRANCH="dev"; + SUB_COMMITS_MIN=40; + cd "$SUB_PATH"; + SUBMODULE_HASH="$(git rev-parse HEAD)"; + BRANCHES=$(git branch -r --contains "$SUBMODULE_HASH"); + COMMITS_IN_BRANCH="$(git rev-list --count dev)"; + if [ $COMMITS_IN_BRANCH -lt $SUB_COMMITS_MIN ]; then + echo "name=fails::error" >> $GITHUB_OUTPUT; + echo "::error::Error: Too few commits in $SUB_BRANCH of submodule $SUB_PATH: $COMMITS_IN_BRANCH(expected $SUB_COMMITS_MIN+)"; + exit 1; + fi + if ! grep -q "/$SUB_BRANCH" <<< "$BRANCHES"; then + echo "name=fails::error" >> $GITHUB_OUTPUT; + echo "::error::Error: Submodule $SUB_PATH is not on branch $SUB_BRANCH"; + exit 1; + fi + + - name: 'Check for new TODOs' + id: check_todos + if: github.event_name == 'pull_request' + run: | + set +e; + git diff --unified=0 --no-color ${{ github.event.pull_request.base.sha }} ${{ github.sha }} | grep -E '^\+' | grep -i -E '(TODO|HACK|FIXME|XXX)[ :]' | grep -v -- '-nofl' > lines.log; + MISSING_TICKETS=$( grep -v -E 'FL-[0-9]+' lines.log ); + if [ -n "$MISSING_TICKETS" ]; then + echo "Error: Missing ticket number in \`TODO\` comment(s)" >> $GITHUB_STEP_SUMMARY; + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY; + echo "$MISSING_TICKETS" >> $GITHUB_STEP_SUMMARY; + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY; + exit 1; + else + echo "No new TODOs without tickets found" >> $GITHUB_STEP_SUMMARY; + fi + + - name: 'Check Python code formatting' + id: syntax_check_py + if: always() + run: | + set +e; + ./fbt -s lint_py 2>&1 | tee lint-py.log; + if [ "${PIPESTATUS[0]}" -ne 0 ]; then + # Save multiline output + echo "errors=1" >> $GITHUB_OUTPUT; + printf "Python Lint errors:\n\`\`\`\n" >> $GITHUB_STEP_SUMMARY; + echo "$(cat lint-py.log)" >> $GITHUB_STEP_SUMMARY; + printf "\n\`\`\`\n" >> $GITHUB_STEP_SUMMARY; + exit 1; + else + echo "Python Lint: all good ✨" >> $GITHUB_STEP_SUMMARY; + fi + + - name: 'Check C++ code formatting' + id: syntax_check_cpp + if: always() + run: | + set +e; + ./fbt -s lint 2>&1 | tee lint-cpp.log; + if [ "${PIPESTATUS[0]}" -ne 0 ]; then + # Save multiline output + echo "errors=1" >> $GITHUB_OUTPUT; + printf "C Lint errors:\n\`\`\`\n" >> $GITHUB_STEP_SUMMARY; + echo "$(cat lint-cpp.log)" >> $GITHUB_STEP_SUMMARY; + printf "\n\`\`\`\n" >> $GITHUB_STEP_SUMMARY; + exit 1; + else + echo "C Lint: all good ✨" >> $GITHUB_STEP_SUMMARY; + fi + + - name: Report code formatting errors + if: ( steps.syntax_check_py.outputs.errors || steps.syntax_check_cpp.outputs.errors ) && github.event.pull_request + run: | + echo "Code formatting errors found"; + echo "Please run './fbt format' or './fbt format_py' to fix them"; + exit 1; diff --git a/.github/workflows/lint_c.yml b/.github/workflows/lint_c.yml deleted file mode 100644 index aaff396ec6f..00000000000 --- 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 - -jobs: - lint_c_cpp: - 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@v2 - with: - fetch-depth: 0 - - - name: 'Check code formatting' - id: syntax_check - run: SET_GH_OUTPUT=1 FBT_TOOLCHAIN_PATH=/opt ./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/lint_python.yml b/.github/workflows/lint_python.yml deleted file mode 100644 index c059f4348ac..00000000000 --- a/.github/workflows/lint_python.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: 'Python Lint' - -on: - push: - branches: - - dev - - "release*" - tags: - - '*' - pull_request: - -jobs: - lint_python: - 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@v2 - with: - fetch-depth: 0 - - - name: 'Check code formatting' - run: SET_GH_OUTPUT=1 FBT_TOOLCHAIN_PATH=/opt ./fbt lint_py diff --git a/.github/workflows/merge_report.yml b/.github/workflows/merge_report.yml new file mode 100644 index 00000000000..90302ce1aa0 --- /dev/null +++ b/.github/workflows/merge_report.yml @@ -0,0 +1,42 @@ +name: 'Check FL ticket in PR name' + +on: + push: + branches: + - dev + +env: + FBT_TOOLCHAIN_PATH: /runner/_work + +jobs: + merge_report: + runs-on: [self-hosted, FlipperZeroShell] + steps: + - name: 'Wipe workspace' + run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; + + - name: 'Checkout code' + uses: actions/checkout@v4 + with: + fetch-depth: 1 + 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" || cat "${{ github.event_path }}" + + - name: 'Check ticket and report' + run: | + source scripts/toolchain/fbtenv.sh + python3 -m pip install slack_sdk + python3 scripts/merge_report_qa.py \ + ${{ secrets.QA_REPORT_SLACK_TOKEN }} \ + ${{ secrets.QA_REPORT_SLACK_CHANNEL }} + diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml new file mode 100644 index 00000000000..4527e292078 --- /dev/null +++ b/.github/workflows/pvs_studio.yml @@ -0,0 +1,88 @@ +name: 'Static C/C++ analysis with PVS-Studio' + +on: + push: + branches: + - dev + pull_request: + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + FBT_TOOLCHAIN_PATH: /runner/_work + FBT_GIT_SUBMODULE_SHALLOW: 1 + +jobs: + analyse_c_cpp: + if: ${{ !github.event.pull_request.head.repo.fork }} + runs-on: [self-hosted, FlipperZeroShell] + steps: + - name: 'Wipe workspace' + run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; + + - name: 'Checkout code' + uses: actions/checkout@v4 + with: + fetch-depth: 1 + 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" || cat "${{ github.event_path }}" + + - 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/reindex.yml b/.github/workflows/reindex.yml index ea850e7051b..82cb0468077 100644 --- a/.github/workflows/reindex.yml +++ b/.github/workflows/reindex.yml @@ -7,8 +7,9 @@ on: jobs: reindex: name: 'Reindex updates' - runs-on: [self-hosted,FlipperZeroShell] + runs-on: [self-hosted, FlipperZeroShell] steps: - name: Trigger reindex run: | - curl -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }} + curl --fail -L -H "Token: ${{ secrets.INDEXER_TOKEN }}" \ + "${{ secrets.INDEXER_URL }}"/firmware/reindex diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml new file mode 100644 index 00000000000..40f72bd0ba5 --- /dev/null +++ b/.github/workflows/unit_tests.yml @@ -0,0 +1,69 @@ +name: 'Unit tests' + +on: + pull_request: + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + FBT_TOOLCHAIN_PATH: /opt + FBT_GIT_SUBMODULE_SHALLOW: 1 + +jobs: + run_units_on_bench: + runs-on: [self-hosted, FlipperZeroUnitTest] + steps: + - name: 'Wipe workspace' + run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; + + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 1 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Get flipper from device manager (mock)' + id: device + run: | + echo "flipper=/dev/ttyACM0" >> $GITHUB_OUTPUT + + - name: 'Flash unit tests firmware' + id: flashing + if: success() + timeout-minutes: 10 + run: | + ./fbt resources firmware_latest flash SWD_TRANSPORT_SERIAL=2A0906016415303030303032 LIB_DEBUG=1 FIRMWARE_APP_SET=unit_tests FORCE=1 + + - name: 'Wait for flipper and format ext' + id: format_ext + if: steps.flashing.outcome == 'success' + timeout-minutes: 5 + run: | + source scripts/toolchain/fbtenv.sh + python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext + + - name: 'Copy assets and unit data, reboot and wait for flipper' + id: copy + if: steps.format_ext.outcome == 'success' + timeout-minutes: 7 + run: | + source scripts/toolchain/fbtenv.sh + python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + rm -rf build/latest/resources/dolphin + python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} -f send build/latest/resources /ext + python3 scripts/power.py -p ${{steps.device.outputs.flipper}} reboot + python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + + - name: 'Run units and validate results' + id: run_units + if: steps.copy.outcome == 'success' + timeout-minutes: 7 + run: | + source scripts/toolchain/fbtenv.sh + python3 scripts/testing/units.py ${{steps.device.outputs.flipper}} + + - name: 'Check GDB output' + if: failure() + run: | + ./fbt gdb_trace_all SWD_TRANSPORT_SERIAL=2A0906016415303030303032 LIB_DEBUG=1 FIRMWARE_APP_SET=unit_tests FORCE=1 diff --git a/.github/workflows/updater_test.yml b/.github/workflows/updater_test.yml new file mode 100644 index 00000000000..b14b618a6eb --- /dev/null +++ b/.github/workflows/updater_test.yml @@ -0,0 +1,75 @@ +name: 'Updater test' + +on: + pull_request: + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + FBT_TOOLCHAIN_PATH: /opt + FBT_GIT_SUBMODULE_SHALLOW: 1 + +jobs: + test_updater_on_bench: + runs-on: [self-hosted, FlipperZeroUpdaterTest] + steps: + - name: 'Wipe workspace' + run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; + + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 1 + submodules: false + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Get flipper from device manager (mock)' + id: device + run: | + echo "flipper=Rekigyn" >> $GITHUB_OUTPUT + echo "stlink=0F020D026415303030303032" >> $GITHUB_OUTPUT + + - name: 'Flashing target firmware' + id: first_full_flash + timeout-minutes: 10 + run: | + source scripts/toolchain/fbtenv.sh + ./fbt flash_usb_full PORT=${{steps.device.outputs.flipper}} FORCE=1 + python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + + - name: 'Validating updater' + id: second_full_flash + timeout-minutes: 10 + if: success() + run: | + source scripts/toolchain/fbtenv.sh + ./fbt flash_usb PORT=${{steps.device.outputs.flipper}} FORCE=1 + python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + + - name: 'Get last release tag' + id: release_tag + if: failure() + run: | + echo "tag=$(git tag -l --sort=-version:refname | grep -v "rc\|RC" | head -1)" >> $GITHUB_OUTPUT + + - name: 'Wipe workspace' + run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; + + - name: 'Checkout latest release' + uses: actions/checkout@v4 + if: failure() + with: + fetch-depth: 1 + ref: ${{ steps.release_tag.outputs.tag }} + + - name: 'Flash last release' + if: failure() + run: | + ./fbt flash SWD_TRANSPORT_SERIAL=${{steps.device.outputs.stlink}} FORCE=1 + + - name: 'Wait for flipper and format ext' + if: failure() + run: | + source scripts/toolchain/fbtenv.sh + python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext diff --git a/.gitignore b/.gitignore index 21f391743ec..d60dcec3f5d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ +*~ *.swp +*.swo *.gdb_history +*.old # LSP @@ -27,26 +30,37 @@ bindings/ .mxproject Brewfile.lock.json -# Visual Studio Code -.vscode/ - -# legendary cmake's -build -CMakeLists.txt - -# bundle output -dist +# Kate +.kateproject +.kateconfig # kde .directory # SCons .sconsign.dblite + + +# Visual Studio Code +/.vscode + +# bundle output +/dist + # SCons build dir -build/ +/build # Toolchain /toolchain # openocd output file openocd.log + +# PVS Studio temporary files +.PVS-Studio/ +PVS-Studio.log +*.PVS-Studio.* + +.gdbinit + +/fbt_options_local.py \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index b580a8c71a8..52cf4a207b7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ [submodule "lib/mlib"] path = lib/mlib url = https://github.com/P-p-H-d/mlib.git -[submodule "lib/STM32CubeWB"] - path = lib/STM32CubeWB - url = https://github.com/Flipper-Zero/STM32CubeWB.git [submodule "lib/littlefs"] path = lib/littlefs url = https://github.com/littlefs-project/littlefs.git @@ -13,6 +10,7 @@ [submodule "assets/protobuf"] path = assets/protobuf url = https://github.com/flipperdevices/flipperzero-protobuf.git + shallow = false [submodule "lib/libusb_stm32"] path = lib/libusb_stm32 url = https://github.com/flipperdevices/libusb_stm32.git @@ -22,9 +20,21 @@ [submodule "lib/microtar"] path = lib/microtar url = https://github.com/amachronic/microtar.git -[submodule "lib/scons"] - path = lib/scons - url = https://github.com/SCons/scons.git [submodule "lib/mbedtls"] path = lib/mbedtls url = https://github.com/Mbed-TLS/mbedtls.git +[submodule "lib/cxxheaderparser"] + path = lib/cxxheaderparser + url = https://github.com/robotpy/cxxheaderparser.git +[submodule "lib/heatshrink"] + path = lib/heatshrink + url = https://github.com/flipperdevices/heatshrink.git +[submodule "lib/st_cmsis_device_wb"] + path = lib/stm32wb_cmsis + url = https://github.com/STMicroelectronics/cmsis_device_wb +[submodule "lib/stm32wbxx_hal_driver"] + path = lib/stm32wb_hal + url = https://github.com/STMicroelectronics/stm32wbxx_hal_driver +[submodule "lib/stm32wb_copro"] + path = lib/stm32wb_copro + url = https://github.com/flipperdevices/stm32wb_copro.git diff --git a/.pvsconfig b/.pvsconfig new file mode 100644 index 00000000000..49c63ad7398 --- /dev/null +++ b/.pvsconfig @@ -0,0 +1,49 @@ +# MLib macros we can't do much about. +//-V:M_LET:1048,1044 +//-V:M_EACH:1048,1044 +//-V:ARRAY_DEF:760,747,568,776,729,712,654 +//-V:LIST_DEF:760,747,568,712,729,654,776 +//-V:BPTREE_DEF2:779,1086,557,773,512 +//-V:DICT_DEF2:779,524,776,760,1044,1001,729,590,568,747,685 +//-V:ALGO_DEF:1048,747,1044 +//-V:TUPLE_DEF2:524,590,1001,760 + +# Non-severe malloc/null pointer deref warnings +//-V::522:2,3 + +# Warning about headers with copyleft license +//-V::1042 + +# Potentially null argument warnings +//-V:memset:575 +//-V:memcpy:575 +//-V:memcmp:575 +//-V:strlen:575 +//-V:strcpy:575 +//-V:strncpy:575 +//-V:strchr:575 + +# For loop warning on M_FOREACH +//-V:for:1044 + +# Bitwise OR +//-V:bit:792 + +# Do not complain about similar code +//-V::525 + +# Common embedded development pointer operations +//-V::566 +//-V::1032 + +# Warnings about length mismatch +//-V:property_value_out:666 + +# Model-related warnings +//-V:with_view_model:1044,1048 + +# Functions that always return the same error code +//-V:picopass_device_decrypt:1048 + +# Examples +//V_EXCLUDE_PATH applications/examples/ \ No newline at end of file diff --git a/.pvsoptions b/.pvsoptions new file mode 100644 index 00000000000..3337d7eb5c2 --- /dev/null +++ b/.pvsoptions @@ -0,0 +1 @@ +--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/cmsis_core -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/microtar -e lib/mlib -e lib/stm32wb_cmsis -e lib/stm32wb_copro -e lib/stm32wb_hal -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* diff --git a/.vscode/.gitignore b/.vscode/.gitignore index 670df7d48f6..481efcdef35 100644 --- a/.vscode/.gitignore +++ b/.vscode/.gitignore @@ -1,4 +1,5 @@ -./c_cpp_properties.json -./launch.json -./settings.json -./tasks.json +/c_cpp_properties.json +/extensions.json +/launch.json +/settings.json +/tasks.json diff --git a/.vscode/example/c_cpp_properties.json b/.vscode/example/c_cpp_properties.json deleted file mode 100644 index db08320c9c9..00000000000 --- a/.vscode/example/c_cpp_properties.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "configurations": [ - { - "name": "Win32", - "compilerPath": "${workspaceFolder}/toolchain/i686-windows/bin/arm-none-eabi-gcc.exe", - "intelliSenseMode": "gcc-arm", - "compileCommands": "${workspaceFolder}/build/latest/compile_commands.json", - "configurationProvider": "ms-vscode.cpptools", - "cStandard": "gnu17", - "cppStandard": "c++17" - }, - { - "name": "Linux", - "compilerPath": "${workspaceFolder}/toolchain/x86_64-linux/bin/arm-none-eabi-gcc", - "intelliSenseMode": "gcc-arm", - "compileCommands": "${workspaceFolder}/build/latest/compile_commands.json", - "configurationProvider": "ms-vscode.cpptools", - "cStandard": "gnu17", - "cppStandard": "c++17" - }, - { - "name": "Mac", - "compilerPath": "${workspaceFolder}/toolchain/x86_64-darwin/bin/arm-none-eabi-gcc", - "intelliSenseMode": "gcc-arm", - "compileCommands": "${workspaceFolder}/build/latest/compile_commands.json", - "configurationProvider": "ms-vscode.cpptools", - "cStandard": "gnu17", - "cppStandard": "c++17" - } - ], - "version": 4 -} \ No newline at end of file diff --git a/.vscode/example/clangd/extensions.json b/.vscode/example/clangd/extensions.json new file mode 100644 index 00000000000..daab417cd01 --- /dev/null +++ b/.vscode/example/clangd/extensions.json @@ -0,0 +1,19 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "ms-python.black-formatter", + "llvm-vs-code-extensions.vscode-clangd", + "amiralizadeh9480.cpp-helper", + "marus25.cortex-debug", + "zxh404.vscode-proto3", + "augustocdias.tasks-shell-input" + ], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [ + "twxs.cmake", + "ms-vscode.cpptools", + "ms-vscode.cmake-tools" + ] +} diff --git a/.vscode/example/cpptools/c_cpp_properties.json b/.vscode/example/cpptools/c_cpp_properties.json new file mode 100644 index 00000000000..d1cac63e966 --- /dev/null +++ b/.vscode/example/cpptools/c_cpp_properties.json @@ -0,0 +1,32 @@ +{ + "configurations": [ + { + "name": "Win32", + "compilerPath": "${workspaceFolder}/toolchain/x86_64-windows/bin/arm-none-eabi-gcc.exe", + "intelliSenseMode": "gcc-arm", + "compileCommands": "${workspaceFolder}/build/latest/compile_commands.json", + "configurationProvider": "ms-vscode.cpptools", + "cStandard": "gnu17", + "cppStandard": "c++17" + }, + { + "name": "Linux", + "compilerPath": "${workspaceFolder}/toolchain/x86_64-linux/bin/arm-none-eabi-gcc", + "intelliSenseMode": "gcc-arm", + "compileCommands": "${workspaceFolder}/build/latest/compile_commands.json", + "configurationProvider": "ms-vscode.cpptools", + "cStandard": "gnu17", + "cppStandard": "c++17" + }, + { + "name": "Mac", + "compilerPath": "${workspaceFolder}/toolchain/x86_64-darwin/bin/arm-none-eabi-gcc", + "intelliSenseMode": "gcc-arm", + "compileCommands": "${workspaceFolder}/build/latest/compile_commands.json", + "configurationProvider": "ms-vscode.cpptools", + "cStandard": "gnu17", + "cppStandard": "c++17" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/example/cpptools/extensions.json b/.vscode/example/cpptools/extensions.json new file mode 100644 index 00000000000..a8babee1cd8 --- /dev/null +++ b/.vscode/example/cpptools/extensions.json @@ -0,0 +1,20 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "ms-python.black-formatter", + "ms-vscode.cpptools", + "amiralizadeh9480.cpp-helper", + "marus25.cortex-debug", + "zxh404.vscode-proto3", + "augustocdias.tasks-shell-input", + "rioj7.command-variable" + ], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [ + "llvm-vs-code-extensions.vscode-clangd", + "twxs.cmake", + "ms-vscode.cmake-tools" + ] +} \ No newline at end of file diff --git a/.vscode/example/launch.json b/.vscode/example/launch.json index bdad042859c..f4b7c6e4838 100644 --- a/.vscode/example/launch.json +++ b/.vscode/example/launch.json @@ -9,8 +9,12 @@ "type": "command", "command": "shellCommand.execute", "args": { - "command": "./fbt get_blackmagic", - "description": "Get Blackmagic device", + "useSingleResult": true, + "env": { + "PATH": "${workspaceFolder}${command:extension.commandvariable.envListSep}${env:PATH}" + }, + "command": "fbt -s get_blackmagic", + "description": "Get Blackmagic device" } } ], @@ -23,15 +27,21 @@ "type": "cortex-debug", "servertype": "openocd", "device": "stlink", - "svdFile": "./debug/STM32WB55_CM4.svd", + "svdFile": "./scripts/debug/STM32WB55_CM4.svd", + // If you're debugging early in the boot process, before OS scheduler is running, + // you have to comment out the following line. "rtos": "FreeRTOS", "configFiles": [ "interface/stlink.cfg", - "./debug/stm32wbx.cfg", + "./scripts/debug/stm32wbx.cfg", ], "postAttachCommands": [ - // "attach 1", - "compare-sections", + "source scripts/debug/flipperversion.py", + "fw-version", + // "compare-sections", + "source scripts/debug/flipperapps.py", + "fap-set-debug-elf-root build/latest/.extapps", + // "source scripts/debug/FreeRTOS/FreeRTOS.py", ] // "showDevDebugOutput": "raw", }, @@ -43,14 +53,18 @@ "type": "cortex-debug", "servertype": "external", "gdbTarget": "${input:BLACKMAGIC}", - "svdFile": "./debug/STM32WB55_CM4.svd", + "svdFile": "./scripts/debug/STM32WB55_CM4.svd", "rtos": "FreeRTOS", "postAttachCommands": [ "monitor swdp_scan", "attach 1", "set confirm off", "set mem inaccessible-by-default off", - "compare-sections", + "source scripts/debug/flipperversion.py", + "fw-version", + "source scripts/debug/flipperapps.py", + "fap-set-debug-elf-root build/latest/.extapps", + // "compare-sections", ] // "showDevDebugOutput": "raw", }, @@ -63,18 +77,37 @@ "servertype": "jlink", "interface": "swd", "device": "STM32WB55RG", - "svdFile": "./debug/STM32WB55_CM4.svd", + "svdFile": "./scripts/debug/STM32WB55_CM4.svd", "rtos": "FreeRTOS", + "postAttachCommands": [ + "source scripts/debug/flipperversion.py", + "fw-version", + "source scripts/debug/flipperapps.py", + "fap-set-debug-elf-root build/latest/.extapps", + ] // "showDevDebugOutput": "raw", }, { - "name": "fbt debug", - "type": "python", - "request": "launch", - "program": "./lib/scons/scripts/scons.py", - "args": [ - "sdk" - ] + "name": "Attach FW (DAP)", + "cwd": "${workspaceFolder}", + "executable": "./build/latest/firmware.elf", + "request": "attach", + "type": "cortex-debug", + "servertype": "openocd", + "device": "cmsis-dap", + "svdFile": "./scripts/debug/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + "configFiles": [ + "interface/cmsis-dap.cfg", + "./scripts/debug/stm32wbx.cfg", + ], + "postAttachCommands": [ + "source scripts/debug/flipperversion.py", + "fw-version", + "source scripts/debug/flipperapps.py", + "fap-set-debug-elf-root build/latest/.extapps", + ], + // "showDevDebugOutput": "raw", }, { "name": "python debug", diff --git a/.vscode/example/settings.json b/.vscode/example/settings.json index 925c2e0763b..efa08157b62 100644 --- a/.vscode/example/settings.json +++ b/.vscode/example/settings.json @@ -6,17 +6,25 @@ "cortex-debug.enableTelemetry": false, "cortex-debug.variableUseNaturalFormat": true, "cortex-debug.showRTOS": true, - "cortex-debug.armToolchainPath.windows": "${workspaceFolder}/toolchain/i686-windows/bin", + "cortex-debug.armToolchainPath.windows": "${workspaceFolder}/toolchain/x86_64-windows/bin", "cortex-debug.armToolchainPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/bin", "cortex-debug.armToolchainPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/bin", - "cortex-debug.openocdPath.windows": "${workspaceFolder}/toolchain/i686-windows/openocd/bin/openocd.exe", + "cortex-debug.openocdPath.windows": "${workspaceFolder}/toolchain/x86_64-windows/openocd/bin/openocd.exe", "cortex-debug.openocdPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/openocd/bin/openocd", "cortex-debug.openocdPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/openocd/bin/openocd", + "cortex-debug.gdbPath.windows": "${workspaceFolder}/toolchain/x86_64-windows/bin/arm-none-eabi-gdb-py.bat", + "cortex-debug.gdbPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/bin/arm-none-eabi-gdb-py", + "cortex-debug.gdbPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/bin/arm-none-eabi-gdb-py", "editor.formatOnSave": true, "files.associations": { "*.scons": "python", "SConscript": "python", "SConstruct": "python", "*.fam": "python", - } + }, + "clangd.arguments": [ + // We might be able to tighten this a bit more to only include the correct toolchain. + "--query-driver=**", + "--compile-commands-dir=${workspaceFolder}/build/latest" + ] } \ No newline at end of file diff --git a/.vscode/example/tasks.json b/.vscode/example/tasks.json index 32efa006827..e36a1fe47ab 100644 --- a/.vscode/example/tasks.json +++ b/.vscode/example/tasks.json @@ -4,41 +4,29 @@ "version": "2.0.0", "tasks": [ { - "label": "[Release] Build", + "label": "[Release] Build Firmware", "group": "build", "type": "shell", "command": "./fbt COMPACT=1 DEBUG=0" }, { - "label": "[Debug] Build", + "label": "[Debug] Build Firmware", "group": "build", "type": "shell", "command": "./fbt" }, { - "label": "[Release] Flash (ST-Link)", + "label": "[Release] Flash (SWD)", "group": "build", "type": "shell", "command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash" }, { - "label": "[Debug] Flash (ST-Link)", + "label": "[Debug] Flash (SWD)", "group": "build", "type": "shell", "command": "./fbt FORCE=1 flash" }, - { - "label": "[Release] Flash (blackmagic)", - "group": "build", - "type": "shell", - "command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash_blackmagic" - }, - { - "label": "[Debug] Flash (blackmagic)", - "group": "build", - "type": "shell", - "command": "./fbt FORCE=1 flash_blackmagic" - }, { "label": "[Release] Flash (JLink)", "group": "build", @@ -93,11 +81,103 @@ "type": "shell", "command": "./fbt FIRMWARE_APP_SET=unit_tests FORCE=1 flash_usb" }, + { + "label": "[Debug] Flash (USB, with resources)", + "group": "build", + "type": "shell", + "command": "./fbt FORCE=1 flash_usb_full" + }, { "label": "[Release] Flash (USB, with resources)", "group": "build", "type": "shell", "command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash_usb_full" }, + { + "label": "[Debug] Create PVS-Studio report", + "group": "build", + "type": "shell", + "command": "./fbt firmware_pvs" + }, + { + "label": "[Debug] Build FAPs", + "group": "build", + "type": "shell", + "command": "./fbt fap_dist" + }, + { + "label": "[Release] Build FAPs", + "group": "build", + "type": "shell", + "command": "./fbt COMPACT=1 DEBUG=0 fap_dist" + }, + { + "label": "[Debug] Build App", + "group": "build", + "type": "shell", + "command": "./fbt build APPSRC=${relativeFileDirname}" + }, + { + "label": "[Release] Build App", + "group": "build", + "type": "shell", + "command": "./fbt COMPACT=1 DEBUG=0 build APPSRC=${relativeFileDirname}" + }, + { + "label": "[Debug] Launch App on Flipper", + "group": "build", + "type": "shell", + "command": "./fbt launch APPSRC=${relativeFileDirname}" + }, + { + "label": "[Release] Launch App on Flipper", + "group": "build", + "type": "shell", + "command": "./fbt COMPACT=1 DEBUG=0 launch APPSRC=${relativeFileDirname}" + }, + { + "label": "[Debug] Launch App on Flipper with Serial Console", + "dependsOrder": "sequence", + "group": "build", + "dependsOn": [ + "[Debug] Launch App on Flipper", + "Serial Console" + ] + }, + { + "label": "[Debug] Build and upload all FAPs to Flipper over USB", + "group": "build", + "type": "shell", + "command": "./fbt fap_deploy" + }, + { + "label": "[Release] Build and upload all FAPs to Flipper over USB", + "group": "build", + "type": "shell", + "command": "./fbt COMPACT=1 DEBUG=0 fap_deploy" + }, + { + // Press Ctrl+] to quit + "label": "Serial Console", + "type": "shell", + "command": "./fbt cli", + "group": "none", + "isBackground": true, + "options": { + "env": { + "FBT_NO_SYNC": "0" + } + }, + "presentation": { + "reveal": "always", + "revealProblems": "never", + "showReuseMessage": false, + "panel": "dedicated", + "focus": true, + "echo": true, + "close": true, + "group": "Logger" + } + } ] } \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index b53ffc24cfc..00000000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. - // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp - // List of extensions which should be recommended for users of this workspace. - "recommendations": [ - "ms-python.black-formatter", - "ms-vscode.cpptools", - "amiralizadeh9480.cpp-helper", - "marus25.cortex-debug", - "zxh404.vscode-proto3", - "augustocdias.tasks-shell-input" - ], - // List of extensions recommended by VS Code that should not be recommended for users of this workspace. - "unwantedRecommendations": [] -} \ No newline at end of file diff --git a/Brewfile b/Brewfile deleted file mode 100644 index d9ba70e0ae1..00000000000 --- a/Brewfile +++ /dev/null @@ -1,6 +0,0 @@ -cask "gcc-arm-embedded" -brew "protobuf" -brew "gdb" -brew "open-ocd" -brew "clang-format" -brew "dfu-util" diff --git a/CODING_STYLE.md b/CODING_STYLE.md index 31426b941b4..002c67f246a 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -3,15 +3,15 @@ Nice to see you reading this document, we really appreciate it. As all documents of this kind it's unable to cover everything. -But it will cover general rules that we enforcing on PR review. +But it will cover general rules that we are enforcing on PR review. -Also we already have automatic rules checking and formatting, -but it got it's limitations and this guide is still mandatory. +Also, we already have automatic rules checking and formatting, +but it got its limitations and this guide is still mandatory. -Some part of this project do have it's own naming and coding guides. +Some part of this project do have its own naming and coding guides. For example: assets. Take a look into `ReadMe.md` in assets folder for more details. -Also 3rd party libraries are none of our concern. +Also, 3rd party libraries are none of our concern. And yes, this set is not final and we are open to discussion. If you want to add/remove/change something here please feel free to open new ticket. @@ -30,7 +30,7 @@ Our guide is inspired by, but not claiming to be compatible with: Code we write is intended to be public. Avoid one-liners from hell and keep code complexity under control. -Try to make code self explanatory and add comments if needed. +Try to make code self-explanatory and add comments if needed. Leave references to standards that you are implementing. Use project wiki to document new/reverse engineered standards. @@ -48,11 +48,11 @@ Almost everything in flipper firmware is built around this concept. # C coding style - Tab is 4 spaces -- Use `fbt format` to reformat source code and check style guide before commit +- Use `./fbt format` to reformat source code and check style guide before commit ## Naming -### Type names are CamelCase +### Type names are PascalCase Examples: @@ -89,7 +89,7 @@ Enforced by linter. Suffixes: - `alloc` - allocate and init instance. C style constructor. Returns pointer to instance. -- `free` - deinit and release instance. C style destructor. Takes pointer to instance. +- `free` - de-init and release instance. C style destructor. Takes pointer to instance. # C++ coding style diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 00a0191e5ed..3f4853116a5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ Thank you for investing your time in contributing to our project! -Read our [Code of Coduct](CODE_OF_CONDUCT.md) to keep our community approachable and respectable. +Read our [Code of Conduct](CODE_OF_CONDUCT.md) to keep our community approachable and respectable. In this guide you will get an overview of the contribution workflow from opening an issue, creating a PR, reviewing, and merging the PR. @@ -17,14 +17,14 @@ See the [ReadMe](ReadMe.md) to get an overview of the project. Here are some hel ## Getting started -Before writing code and creating PR make sure that it aligns with our mission and guidlines: +Before writing code and creating PR make sure that it aligns with our mission and guidelines: - All our devices are intended for research and education. - PR that contains code intended to commit crimes is not going to be accepted. - Your PR must comply with our [Coding Style](CODING_STYLE.md) -- Your PR must contain code compatiable with project [LICENSE](LICENSE). -- PR will only be merged if it pass CI/CD. -- PR will only be merged if it pass review by code owner. +- Your PR must contain code compatible with project [LICENSE](LICENSE). +- PR will only be merged if it passes CI/CD. +- PR will only be merged if it passes review by code owner. Feel free to ask questions in issues if you're not sure. @@ -59,7 +59,7 @@ Commit the changes once you are happy with them. Make sure that code compilation ### Pull Request When you're done making the changes, open a pull request, often referred to as a PR. -- Fill out the "Ready for review" template so we can review your PR. This template helps reviewers understand your changes and the purpose of your pull request. +- Fill out the "Ready for review" template, so we can review your PR. This template helps reviewers understand your changes and the purpose of your pull request. - Don't forget to [link PR to issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) if you are solving one. - Enable the checkbox to [allow maintainer edits](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork) so the branch can be updated for a merge. Once you submit your PR, a Docs team member will review your proposal. We may ask questions or request for additional information. diff --git a/Makefile b/Makefile deleted file mode 100644 index 30f16cbd382..00000000000 --- a/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -$(info +-------------------------------------------------+) -$(info | |) -$(info | Hello, this is Flipper team speaking! |) -$(info | |) -$(info | We've migrated to new build system |) -$(info | It's nice and based on scons |) -$(info | |) -$(info | Crash course: |) -$(info | |) -$(info | `./fbt` |) -$(info | `./fbt flash` |) -$(info | `./fbt debug` |) -$(info | |) -$(info | More details in documentation/fbt.md |) -$(info | |) -$(info | Also Please leave your feedback here: |) -$(info | https://flipp.dev/4RDu |) -$(info | or |) -$(info | https://flipp.dev/2XM8 |) -$(info | |) -$(info +-------------------------------------------------+) diff --git a/ReadMe.md b/ReadMe.md index 101c98f3603..02bea9256ec 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,7 +1,24 @@ +<<<<<<< HEAD # Flipper Zero Firmware (Wetox fork, unofficial!) +======= + + + + A pixel art of a Dophin with text: Flipper Zero Official Repo + -![Show me the code](https://habrastorage.org/webt/eo/m0/e4/eom0e4btudte7nrhnyic-laiog0.png) +# Flipper Zero Firmware +- [Flipper Zero Official Website](https://flipperzero.one). A simple way to explain to your friends what Flipper Zero can do. +- [Flipper Zero Firmware Update](https://update.flipperzero.one). Improvements for your dolphin: latest firmware releases, upgrade tools for PC and mobile devices. +- [User Documentation](https://docs.flipperzero.one). Learn more about your dolphin: specs, usage guides, and anything you want to ask. +>>>>>>> upstream/dev + +# Contributing + +<<<<<<< HEAD Welcome to the [Wetox Flipper Zero](https://wetox.team/)'s Firmware repo! This is a modified unofficial version of the Flipper Zero firmware with some additional features. @@ -11,16 +28,61 @@ This is a modified unofficial version of the Flipper Zero firmware with some add * Removing T5577 passwords via the cli command `rfid clear_pass_t5577` using a dictionary attack * Support for inverted screens via a toggle in the desktop settings +======= +Our main goal is to build a healthy and sustainable community around Flipper, so we're open to any new ideas and contributions. We also have some rules and taboos here, so please read this page and our [Code of Conduct](/CODE_OF_CONDUCT.md) carefully. +>>>>>>> upstream/dev + +## I need help + +The best place to search for answers is our [User Documentation](https://docs.flipperzero.one). If you can't find the answer there, check our [Discord Server](https://flipp.dev/discord) or our [Forum](https://forum.flipperzero.one/). + +## I want to report an issue + +If you've found an issue and want to report it, please check our [Issues](https://github.com/flipperdevices/flipperzero-firmware/issues) page. Make sure the description contains information about the firmware version you're using, your platform, and a clear explanation of the steps to reproduce the issue. + +## I want to contribute code + +Before opening a PR, please confirm that your changes must be contained in the firmware. Many ideas can easily be implemented as external applications and published in the [Flipper Application Catalog](https://github.com/flipperdevices/flipper-application-catalog). If you are unsure, reach out to us on the [Discord Server](https://flipp.dev/discord) or the [Issues](https://github.com/flipperdevices/flipperzero-firmware/issues) page, and we'll help you find the right place for your code. + +Also, please read our [Contribution Guide](/CONTRIBUTING.md) and our [Coding Style](/CODING_STYLE.md), and make sure your code is compatible with our [Project License](/LICENSE). + +Finally, open a [Pull Request](https://github.com/flipperdevices/flipperzero-firmware/pulls) and make sure that CI/CD statuses are all green. + +# Development + +Flipper Zero Firmware is written in C, with some bits and pieces written in C++ and armv7m assembly languages. An intermediate level of C knowledge is recommended for comfortable programming. C, C++, and armv7m assembly languages are supported for Flipper applications. + +# Firmware RoadMap + +[Firmware RoadMap Miro Board](https://miro.com/app/board/uXjVO_3D6xU=/) + +## Requirements + +Supported development platforms: + +- Windows 10+ with PowerShell and Git (x86_64) +- macOS 12+ with Command Line tools (x86_64, arm64) +- Ubuntu 20.04+ with build-essential and Git (x86_64) + +Supported in-circuit debuggers (optional but highly recommended): + +- [Flipper Zero Wi-Fi Development Board](https://shop.flipperzero.one/products/wifi-devboard) +- ST-Link +- J-Link -# Clone the Repository +Flipper Build System will take care of all the other dependencies. + +## Cloning source code + +Make sure you have enough space and clone the source code: -You should clone with ```shell -$ git clone --recursive https://github.com/flipperdevices/flipperzero-firmware.git +git clone --recursive https://github.com/flipperdevices/flipperzero-firmware.git ``` -# Update firmware +## Building +<<<<<<< HEAD Flipper Zero's firmware consists of two components: - Core2 firmware set - proprietary components by ST: FUS + radio stack. FUS is flashed at factory and you should never update it. @@ -135,38 +197,62 @@ heatshrink has to be compiled [from sources](https://github.com/atomicobject/hea ## Compile everything ```sh +======= +Build firmware using Flipper Build Tool: + +```shell +>>>>>>> upstream/dev ./fbt ``` -Check `dist/` for build outputs. - -Use **`flipper-z-{target}-full-{suffix}.dfu`** to flash your device. +## Flashing firmware using an in-circuit debugger -## Flash everything +Connect your in-circuit debugger to your Flipper and flash firmware using Flipper Build Tool: -Connect your device via ST-Link and run: -```sh -./fbt firmware_flash +```shell +./fbt flash ``` Or connect via the blackmagic board and run: ```sh BLACKMAGIC=:2345 make blackmagic_load ``` -# Links +## Flashing firmware using USB +<<<<<<< HEAD * Telegram: [@wetox_flipper](https://t.me/wetox_flipper) +======= +Make sure your Flipper is on, and your firmware is functioning. Connect your Flipper with a USB cable and flash firmware using Flipper Build Tool: + +```shell +./fbt flash_usb +``` + +## Documentation + +- [Flipper Build Tool](/documentation/fbt.md) - building, flashing, and debugging Flipper software +- [Applications](/documentation/AppsOnSDCard.md), [Application Manifest](/documentation/AppManifests.md) - developing, building, deploying, and debugging Flipper applications +- [Hardware combos and Un-bricking](/documentation/KeyCombo.md) - recovering your Flipper from the most nasty situations +- [Flipper File Formats](/documentation/file_formats) - everything about how Flipper stores your data and how you can work with it +- [Universal Remotes](/documentation/UniversalRemotes.md) - contributing your infrared remote to the universal remote database +- And much more in the [documentation](/documentation) folder +>>>>>>> upstream/dev # Project structure -- `applications` - Applications and services used in firmware -- `assets` - Assets used by applications and services -- `furi` - Furi Core: os level primitives and helpers -- `debug` - Debug tool: GDB-plugins, SVD-file and etc -- `docker` - Docker image sources (used for firmware build automation) -- `documentation` - Documentation generation system configs and input files -- `firmware` - Firmware source code -- `lib` - Our and 3rd party libraries, drivers and etc... -- `scripts` - Supplementary scripts and python libraries home - -Also pay attention to `ReadMe.md` files inside of those directories. +- `applications` - applications and services used in firmware +- `assets` - assets used by applications and services +- `furi` - Furi Core: OS-level primitives and helpers +- `documentation` - documentation generation system configs and input files +- `firmware` - firmware source code +- `lib` - our and 3rd party libraries, drivers, etc. +- `scripts` - supplementary scripts and python libraries home + +Also, see `ReadMe.md` files inside those directories for further details. + +# Links + +- Discord: [flipp.dev/discord](https://flipp.dev/discord) +- Website: [flipperzero.one](https://flipperzero.one) +- Forum: [forum.flipperzero.one](https://forum.flipperzero.one/) +- Kickstarter: [kickstarter.com](https://www.kickstarter.com/projects/flipper-devices/flipper-zero-tamagochi-for-hackers) diff --git a/RoadMap.md b/RoadMap.md deleted file mode 100644 index d0bee3fea74..00000000000 --- a/RoadMap.md +++ /dev/null @@ -1,51 +0,0 @@ -# RoadMap (for the stock FW, not the wetox fork) - -# Where we are (0.x.x branch) - -Our goal for 0.x.x branch is to build stable usable apps and API. -First public release that we support in this branch is 0.43.1. Your device most likely came with this version. -You can develop applications but keep in mind that API is not final yet. - -## What's already implemented - -**Applications** - -- SubGhz: all most common protocols, reading RAW for everything else -- 125kHz RFID: all most common protocols -- NFC: reading/emulating Mifare Ultralight, reading MiFare Classic and DESFire, basic EMV, basic NFC-B,F,V -- Infrared: all most common RC protocols, RAW format for everything else -- GPIO: UART bridge, basic GPIO controls -- iButton: DS1990, Cyfral, Metacom -- Bad USB: Full USB Rubber Ducky support, some extras for windows alt codes -- U2F: Full U2F specification support - -**Extras** - -- BLE Keyboard -- Snake game - -**System and HAL** - -- Furi Core -- Furi HAL - -# Where we're going (Version 1) - -Main goal for 1.0.0 is to provide first stable version for both Users and Developers. - -## What we're planning to implement in 1.0.0 - -- Loading applications from SD (tested as PoC, work scheduled for Q2) -- More protocols (gathering feedback) -- User documentation (work in progress) -- FuriCore: get rid of CMSIS API, replace hard real time timers, improve stability and performance (work in progress) -- FuriHal: deep sleep mode, stable API, examples, documentation (work in progress) -- Application improvements (a ton of things that we want to add and improve that are too numerous to list here) - -## When will it happen and where I can see the progress? - -Release 1.0.0 will most likely happen around the end of Q3 - -Development progress can be tracked in our public Miro board: - -https://miro.com/app/board/uXjVO_3D6xU=/?moveToWidget=3458764522498020058&cot=14 diff --git a/SConstruct b/SConstruct index 52fe75a6a9c..b42218a579c 100644 --- a/SConstruct +++ b/SConstruct @@ -1,5 +1,5 @@ # -# Main Fipper Build System entry point +# Main Flipper Build System entry point # # This file is evaluated by scons (the build system) every time fbt is invoked. # Scons constructs all referenced environments & their targets' dependency @@ -7,65 +7,45 @@ # construction of certain targets behind command-line options. import os -import subprocess +from fbt.util import path_as_posix + +DefaultEnvironment(tools=[]) EnsurePythonVersion(3, 8) -DefaultEnvironment(tools=[]) # Progress(["OwO\r", "owo\r", "uwu\r", "owo\r"], interval=15) - -# This environment is created only for loading options & validating file/dir existance +# This environment is created only for loading options & validating file/dir existence fbt_variables = SConscript("site_scons/commandline.scons") -cmd_environment = Environment(tools=[], variables=fbt_variables) -Help(fbt_variables.GenerateHelpText(cmd_environment)) +cmd_environment = Environment( + toolpath=["#/scripts/fbt_tools"], + tools=[ + ("fbt_help", {"vars": fbt_variables}), + ], + variables=fbt_variables, +) # Building basic environment - tools, utility methods, cross-compilation # settings, gcc flags for Cortex-M4, basic builders and more coreenv = SConscript( "site_scons/environ.scons", exports={"VAR_ENV": cmd_environment}, + toolpath=["#/scripts/fbt_tools"], ) SConscript("site_scons/cc.scons", exports={"ENV": coreenv}) -# Store root dir in environment for certain tools -coreenv["ROOT_DIR"] = Dir(".") - - # Create a separate "dist" environment and add construction envs to it distenv = coreenv.Clone( - tools=["fbt_dist", "openocd", "blackmagic", "jflash"], - OPENOCD_GDB_PIPE=[ - "|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}" + tools=[ + "fbt_dist", + "fbt_debugopts", + "openocd", + "blackmagic", + "jflash", ], - GDBOPTS_BASE=[ - "-ex", - "target extended-remote ${GDBREMOTE}", - "-ex", - "set confirm off", - ], - GDBOPTS_BLACKMAGIC=[ - "-ex", - "monitor swdp_scan", - "-ex", - "monitor debug_bmp enable", - "-ex", - "attach 1", - "-ex", - "set mem inaccessible-by-default off", - ], - GDBPYOPTS=[ - "-ex", - "source debug/FreeRTOS/FreeRTOS.py", - "-ex", - "source debug/PyCortexMDebug/PyCortexMDebug.py", - "-ex", - "svd_load ${SVD_FILE}", - "-ex", - "compare-sections", - ], - JFLASHPROJECT="${ROOT_DIR.abspath}/debug/fw.jflash", ENV=os.environ, + UPDATE_BUNDLE_DIR="dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}", + VSCODE_LANG_SERVER=ARGUMENTS.get("LANG_SERVER", "cpptools"), ) firmware_env = distenv.AddFwProject( @@ -87,20 +67,22 @@ if GetOption("fullenv") or any( # Target for self-update package dist_basic_arguments = [ "--bundlever", - '"${UPDATE_VERSION_STRING}"', + "${UPDATE_VERSION_STRING}", ] dist_radio_arguments = [ "--radio", - '"${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}"', + "${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}", "--radiotype", "${COPRO_STACK_TYPE}", "${COPRO_DISCLAIMER}", "--obdata", - '"${ROOT_DIR.abspath}/${COPRO_OB_DATA}"', + "${ROOT_DIR.abspath}/${COPRO_OB_DATA}", + "--stackversion", + "${COPRO_CUBE_VERSION}", ] dist_resource_arguments = [ "-r", - '"${ROOT_DIR.abspath}/assets/resources"', + firmware_env.subst("${RESOURCES_ROOT}"), ] dist_splash_arguments = ( [ @@ -113,7 +95,7 @@ if GetOption("fullenv") or any( selfupdate_dist = distenv.DistCommand( "updater_package", - (distenv["DIST_DEPENDS"], firmware_env["FW_RESOURCES"]), + (distenv["DIST_DEPENDS"], firmware_env["FW_RESOURCES_MANIFEST"]), DIST_EXTRA=[ *dist_basic_arguments, *dist_radio_arguments, @@ -146,7 +128,8 @@ if GetOption("fullenv") or any( # Installation over USB & CLI usb_update_package = distenv.AddUsbFlashTarget( - "#build/usbinstall.flag", (firmware_env["FW_RESOURCES"], selfupdate_dist) + "#build/usbinstall.flag", + (firmware_env["FW_RESOURCES_MANIFEST"], selfupdate_dist), ) distenv.Alias("flash_usb_full", usb_update_package) @@ -160,28 +143,78 @@ if GetOption("fullenv") or any( basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"]) distenv.Default(basic_dist) +dist_dir_name = distenv.GetProjetDirName() +dist_dir = distenv.Dir(f"#/dist/{dist_dir_name}") +external_apps_artifacts = firmware_env["FW_EXTAPPS"] +external_app_list = external_apps_artifacts.application_map.values() + +fap_dist = [ + distenv.Install( + dist_dir.Dir("debug_elf"), + list(app_artifact.debug for app_artifact in external_app_list), + ), + *( + distenv.Install( + dist_dir.File(dist_entry[1]).dir, + app_artifact.compact, + ) + for app_artifact in external_app_list + for dist_entry in app_artifact.dist_entries + ), +] +Depends( + fap_dist, + list(app_artifact.validator for app_artifact in external_app_list), +) +Alias("fap_dist", fap_dist) + +# Copy all faps to device + +fap_deploy = distenv.PhonyTarget( + "fap_deploy", + Action( + [ + [ + "${PYTHON3}", + "${FBT_SCRIPT_DIR}/storage.py", + "-p", + "${FLIP_PORT}", + "send", + "${SOURCE}", + "/ext/apps", + ] + ] + ), + source=firmware_env.Dir(("${RESOURCES_ROOT}/apps")), +) +Depends(fap_deploy, firmware_env["FW_RESOURCES_MANIFEST"]) + + # Target for bundling core2 package for qFlipper copro_dist = distenv.CoproBuilder( - distenv.Dir("assets/core2_firmware"), + "#/build/core2_firmware.tgz", [], ) +distenv.AlwaysBuild(copro_dist) distenv.Alias("copro_dist", copro_dist) -firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env) + +firmware_flash = distenv.AddFwFlashTarget(firmware_env) distenv.Alias("flash", firmware_flash) +# To be implemented in fwflash.py firmware_jflash = distenv.AddJFlashTarget(firmware_env) distenv.Alias("jflash", firmware_jflash) -firmware_bm_flash = distenv.PhonyTarget( - "flash_blackmagic", +distenv.PhonyTarget( + "gdb_trace_all", "$GDB $GDBOPTS $SOURCES $GDBFLASH", source=firmware_env["FW_ELF"], - GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", - GDBREMOTE="${BLACKMAGIC_ADDR}", + GDBOPTS="${GDBOPTS_BASE}", + GDBREMOTE="${OPENOCD_GDB_PIPE}", GDBFLASH=[ "-ex", - "load", + "thread apply all bt", "-ex", "quit", ], @@ -194,6 +227,7 @@ firmware_debug = distenv.PhonyTarget( source=firmware_env["FW_ELF"], GDBOPTS="${GDBOPTS_BASE}", GDBREMOTE="${OPENOCD_GDB_PIPE}", + FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")), ) distenv.Depends(firmware_debug, firmware_flash) @@ -203,16 +237,38 @@ distenv.PhonyTarget( source=firmware_env["FW_ELF"], GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", GDBREMOTE="${BLACKMAGIC_ADDR}", + FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")), ) # Debug alien elf +debug_other_opts = [ + "-ex", + "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py", + # "-ex", + # "source ${FBT_DEBUG_DIR}/FreeRTOS/FreeRTOS.py", + "-ex", + "source ${FBT_DEBUG_DIR}/flipperversion.py", + "-ex", + "fw-version", +] + distenv.PhonyTarget( "debug_other", "${GDBPYCOM}", - GDBPYOPTS='-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ', + GDBOPTS="${GDBOPTS_BASE}", GDBREMOTE="${OPENOCD_GDB_PIPE}", + GDBPYOPTS=debug_other_opts, ) +distenv.PhonyTarget( + "debug_other_blackmagic", + "${GDBPYCOM}", + GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", + GDBREMOTE="${BLACKMAGIC_ADDR}", + GDBPYOPTS=debug_other_opts, +) + + # Just start OpenOCD distenv.PhonyTarget( "openocd", @@ -222,25 +278,28 @@ distenv.PhonyTarget( # Linter distenv.PhonyTarget( "lint", - "${PYTHON3} scripts/lint.py check ${LINT_SOURCES}", - LINT_SOURCES=firmware_env["LINT_SOURCES"], + [["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "check", "${LINT_SOURCES}"]], + LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]], ) distenv.PhonyTarget( "format", - "${PYTHON3} scripts/lint.py format ${LINT_SOURCES}", - LINT_SOURCES=firmware_env["LINT_SOURCES"], + [["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "format", "${LINT_SOURCES}"]], + LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]], ) -# PY_LINT_SOURCES contains recursively-built modules' SConscript files + application manifests +# PY_LINT_SOURCES contains recursively-built modules' SConscript files # Here we add additional Python files residing in repo root firmware_env.Append( PY_LINT_SOURCES=[ # Py code folders "site_scons", "scripts", + "applications", + "applications_user", + "assets", + "targets", # Extra files - "applications/extapps.scons", "SConstruct", "firmware.scons", "fbt_options.py", @@ -249,7 +308,10 @@ firmware_env.Append( black_commandline = "@${PYTHON3} -m black ${PY_BLACK_ARGS} ${PY_LINT_SOURCES}" -black_base_args = ["--include", '"\\.scons|\\.py|SConscript|SConstruct"'] +black_base_args = [ + "--include", + '"(\\.scons|\\.py|SConscript|SConstruct|\\.fam)$"', +] distenv.PhonyTarget( "lint_py", @@ -270,7 +332,14 @@ distenv.PhonyTarget( ) # Start Flipper CLI via PySerial's miniterm -distenv.PhonyTarget("cli", "${PYTHON3} scripts/serial_cli.py") +distenv.PhonyTarget( + "cli", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/serial_cli.py", "-p", "${FLIP_PORT}"]] +) + +# Update WiFi devboard firmware +distenv.PhonyTarget( + "devboard_flash", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/wifi_board.py"]] +) # Find blackmagic probe @@ -279,8 +348,31 @@ distenv.PhonyTarget( "@echo $( ${BLACKMAGIC_ADDR} $)", ) + +# Find STLink probe ids +distenv.PhonyTarget( + "get_stlink", + distenv.Action( + lambda **kw: distenv.GetDevices(), + None, + ), +) + # Prepare vscode environment -vscode_dist = distenv.Install("#.vscode", distenv.Glob("#.vscode/example/*")) +VSCODE_LANG_SERVER = cmd_environment["LANG_SERVER"] +vscode_dist = distenv.Install( + "#.vscode", + [ + distenv.Glob("#.vscode/example/*.json"), + distenv.Glob(f"#.vscode/example/{VSCODE_LANG_SERVER}/*.json"), + ], +) distenv.Precious(vscode_dist) distenv.NoClean(vscode_dist) distenv.Alias("vscode_dist", vscode_dist) + +# Configure shell with build tools +distenv.PhonyTarget( + "env", + "@echo $( ${FBT_SCRIPT_DIR.abspath}/toolchain/fbtenv.sh $)", +) diff --git a/applications/ReadMe.md b/applications/ReadMe.md index cb2f5628d28..de465832aef 100644 --- a/applications/ReadMe.md +++ b/applications/ReadMe.md @@ -1,38 +1,78 @@ # Structure -- `about` - Small About application that shows flipper info -- `accessor` - Wiegand server +## debug + +Applications for factory testing the Flipper. + +- `accessor` - Wiegand server +- `battery_test_app` - Battery debug app +- `blink_test` - LED blinker +- `bt_debug_app` - BT test app. Requires full BT stack installed +- `display_test` - Various display tests & tweaks +- `file_browser_test` - Test UI for file picker +- `keypad_test` - Keypad test +- `lfrfid_debug` - LF RFID debug tool +- `text_box_test` - UI tests +- `uart_echo` - UART mode test +- `unit_tests` - Unit tests +- `usb_mouse` - USB HID test +- `usb_test` - Other USB tests +- `vibro_test` - Vibro test + + +## main + +Applications for main Flipper menu. + - `archive` - Archive and file manager - `bad_usb` - Bad USB application +- `gpio` - GPIO application: includes USART bridge and GPIO control +- `ibutton` - iButton application, onewire keys and more +- `infrared` - Infrared application, controls your IR devices +- `lfrfid` - LF RFID application +- `nfc` - NFC application, HF rfid, EMV and etc +- `subghz` - SubGhz application, 433 fobs and etc +- `u2f` - U2F Application + + +## services + +Background services providing system APIs to applications. + +- `applications.h` - Firmware application list header + - `bt` - BLE service and application - `cli` - Console service and API - `crypto` - Crypto cli tools -- `debug_tools` - Different tools that we use for debug - `desktop` - Desktop service - `dialogs` - Dialogs service: GUI Dialogs for your app - `dolphin` - Dolphin service and supplementary apps -- `gpio` - GPIO application: includes USART bridge and GPIO control - `gui` - GUI service and API -- `ibutton` - iButton application, onewire keys and more - `input` - Input service -- `infrared` - Infrared application, controls your IR devices -- `lfrfid` - LF RFID application -- `lfrfid_debug` - LF RFID debug tool - `loader` - Application loader service -- `music_player` - Music player app (demo) -- `nfc` - NFC application, HF rfid, EMV and etc - `notification` - Notification service - `power` - Power service -- `power_observer` - Power debug tool - `rpc` - RPC service and API -- `scened_app_example` - C++ application example -- `snake_game` - Snake game application - `storage` - Storage service, internal + sdcard + + +## settings + +Small applications providing configuration for basic firmware and its services. + +- `about` - Small About application that shows flipper info +- `bt_settings_app` - Bluetooth options +- `desktop_settings` - Desktop configuration +- `dolphin_passport` - Dolphin passport app +- `notification_settings` - LCD brightness, sound volume, etc configuration +- `power_settings_app` - Basic power options - `storage_settings` - Storage settings app -- `subghz` - SubGhz application, 433 fobs and etc -- `system` - System settings, tools and API -- `tests` - Unit tests and etc -- `u2f` - U2F Application -- `updater` - Update service & application +- `system` - System settings -- `application.h` - Firmware application list header + +## system + +Utility apps not visible in other menus. + +- `storage_move_to_sd` - Data migration tool for internal storage +- `updater` - Update service & application diff --git a/applications/accessor/application.fam b/applications/accessor/application.fam deleted file mode 100644 index 8a94049e14a..00000000000 --- a/applications/accessor/application.fam +++ /dev/null @@ -1,10 +0,0 @@ -App( - appid="accessor", - name="Accessor", - apptype=FlipperAppType.DEBUG, - entry_point="accessor_app", - cdefines=["APP_ACCESSOR"], - requires=["gui"], - stack_size=4 * 1024, - order=40, -) diff --git a/applications/applications.h b/applications/applications.h deleted file mode 100644 index 012e80ddb67..00000000000 --- a/applications/applications.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include -#include - -typedef enum { - FlipperApplicationFlagDefault = 0, - FlipperApplicationFlagInsomniaSafe = (1 << 0), -} FlipperApplicationFlag; - -typedef struct { - const FuriThreadCallback app; - const char* name; - const size_t stack_size; - const Icon* icon; - const FlipperApplicationFlag flags; -} FlipperApplication; - -typedef void (*FlipperOnStartHook)(void); - -extern const char* FLIPPER_AUTORUN_APP_NAME; - -/* Services list - * Spawned on startup - */ -extern const FlipperApplication FLIPPER_SERVICES[]; -extern const size_t FLIPPER_SERVICES_COUNT; - -/* Apps list - * Spawned by loader - */ -extern const FlipperApplication FLIPPER_APPS[]; -extern const size_t FLIPPER_APPS_COUNT; - -/* On system start hooks - * Called by loader, after OS initialization complete - */ -extern const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[]; -extern const size_t FLIPPER_ON_SYSTEM_START_COUNT; - -/* Plugins list - * Spawned by loader - */ -extern const FlipperApplication FLIPPER_PLUGINS[]; -extern const size_t FLIPPER_PLUGINS_COUNT; - -/* Debug menu apps - * Spawned by loader - */ -extern const FlipperApplication FLIPPER_DEBUG_APPS[]; -extern const size_t FLIPPER_DEBUG_APPS_COUNT; - -/* System apps - * Can only be spawned by loader by name - */ -extern const FlipperApplication FLIPPER_SYSTEM_APPS[]; -extern const size_t FLIPPER_SYSTEM_APPS_COUNT; - -/* Seperate scene app holder - * Spawned by loader - */ -extern const FlipperApplication FLIPPER_SCENE; -extern const FlipperApplication FLIPPER_SCENE_APPS[]; -extern const size_t FLIPPER_SCENE_APPS_COUNT; - -extern const FlipperApplication FLIPPER_ARCHIVE; - -/* Settings list - * Spawned by loader - */ -extern const FlipperApplication FLIPPER_SETTINGS_APPS[]; -extern const size_t FLIPPER_SETTINGS_APPS_COUNT; diff --git a/applications/archive/helpers/archive_browser.c b/applications/archive/helpers/archive_browser.c deleted file mode 100644 index 54759dadc95..00000000000 --- a/applications/archive/helpers/archive_browser.c +++ /dev/null @@ -1,496 +0,0 @@ -#include "archive/views/archive_browser_view.h" -#include "archive_files.h" -#include "archive_apps.h" -#include "archive_browser.h" -#include -#include -#include "gui/modules/file_browser_worker.h" -#include "m-string.h" -#include - -static void - archive_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root) { - furi_assert(context); - ArchiveBrowserView* browser = (ArchiveBrowserView*)context; - - int32_t load_offset = 0; - browser->is_root = is_root; - ArchiveTabEnum tab = archive_get_tab(browser); - - if((item_cnt == 0) && (archive_is_home(browser)) && (tab != ArchiveTabBrowser)) { - archive_switch_tab(browser, browser->last_tab_switch_dir); - } else if(!string_start_with_str_p(browser->path, "/app:")) { - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - files_array_reset(model->files); - model->item_cnt = item_cnt; - model->item_idx = (file_idx > 0) ? file_idx : 0; - load_offset = - CLAMP(model->item_idx - FILE_LIST_BUF_LEN / 2, (int32_t)model->item_cnt, 0); - model->array_offset = 0; - model->list_offset = 0; - model->list_loading = true; - model->folder_loading = false; - return false; - }); - archive_update_offset(browser); - - file_browser_worker_load(browser->worker, load_offset, FILE_LIST_BUF_LEN); - } -} - -static void archive_list_load_cb(void* context, uint32_t list_load_offset) { - furi_assert(context); - ArchiveBrowserView* browser = (ArchiveBrowserView*)context; - - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - files_array_reset(model->files); - model->array_offset = list_load_offset; - return false; - }); -} - -static void archive_list_item_cb(void* context, string_t item_path, bool is_folder, bool is_last) { - furi_assert(context); - ArchiveBrowserView* browser = (ArchiveBrowserView*)context; - - if(!is_last) { - archive_add_file_item(browser, is_folder, string_get_cstr(item_path)); - } else { - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - model->list_loading = false; - return true; - }); - } -} - -static void archive_long_load_cb(void* context) { - furi_assert(context); - ArchiveBrowserView* browser = (ArchiveBrowserView*)context; - - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - model->folder_loading = true; - return true; - }); -} - -void archive_file_browser_set_callbacks(ArchiveBrowserView* browser) { - furi_assert(browser); - - file_browser_worker_set_callback_context(browser->worker, browser); - file_browser_worker_set_folder_callback(browser->worker, archive_folder_open_cb); - file_browser_worker_set_list_callback(browser->worker, archive_list_load_cb); - file_browser_worker_set_item_callback(browser->worker, archive_list_item_cb); - file_browser_worker_set_long_load_callback(browser->worker, archive_long_load_cb); -} - -bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx) { - size_t array_size = files_array_size(model->files); - - if((idx >= (uint32_t)model->array_offset + array_size) || - (idx < (uint32_t)model->array_offset)) { - return false; - } - - return true; -} - -void archive_update_offset(ArchiveBrowserView* browser) { - furi_assert(browser); - - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - uint16_t bounds = model->item_cnt > 3 ? 2 : model->item_cnt; - - if((model->item_cnt > 3u) && (model->item_idx >= ((int32_t)model->item_cnt - 1))) { - model->list_offset = model->item_idx - 3; - } else if(model->list_offset < model->item_idx - bounds) { - model->list_offset = - CLAMP(model->item_idx - 2, (int32_t)model->item_cnt - bounds, 0); - } else if(model->list_offset > model->item_idx - bounds) { - model->list_offset = - CLAMP(model->item_idx - 1, (int32_t)model->item_cnt - bounds, 0); - } - - return true; - }); -} - -void archive_update_focus(ArchiveBrowserView* browser, const char* target) { - furi_assert(browser); - furi_assert(target); - - archive_get_items(browser, string_get_cstr(browser->path)); - - if(!archive_file_get_array_size(browser) && archive_is_home(browser)) { - archive_switch_tab(browser, TAB_RIGHT); - } else { - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - uint16_t idx = 0; - while(idx < files_array_size(model->files)) { - ArchiveFile_t* current = files_array_get(model->files, idx); - if(!string_search(current->path, target)) { - model->item_idx = idx + model->array_offset; - break; - } - ++idx; - } - return false; - }); - - archive_update_offset(browser); - } -} - -size_t archive_file_get_array_size(ArchiveBrowserView* browser) { - furi_assert(browser); - - uint16_t size = 0; - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - size = files_array_size(model->files); - return false; - }); - return size; -} - -void archive_set_item_count(ArchiveBrowserView* browser, uint32_t count) { - furi_assert(browser); - - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - model->item_cnt = count; - model->item_idx = CLAMP(model->item_idx, (int32_t)model->item_cnt - 1, 0); - return false; - }); - archive_update_offset(browser); -} - -void archive_file_array_rm_selected(ArchiveBrowserView* browser) { - furi_assert(browser); - uint32_t items_cnt = 0; - - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - files_array_remove_v( - model->files, - model->item_idx - model->array_offset, - model->item_idx - model->array_offset + 1); - model->item_cnt--; - model->item_idx = CLAMP(model->item_idx, (int32_t)model->item_cnt - 1, 0); - items_cnt = model->item_cnt; - return false; - }); - - if((items_cnt == 0) && (archive_is_home(browser))) { - archive_switch_tab(browser, TAB_RIGHT); - } - - archive_update_offset(browser); -} - -void archive_file_array_swap(ArchiveBrowserView* browser, int8_t dir) { - furi_assert(browser); - - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - ArchiveFile_t temp; - size_t array_size = files_array_size(model->files) - 1; - uint8_t swap_idx = CLAMP((size_t)(model->item_idx + dir), array_size, 0u); - - if(model->item_idx == 0 && dir < 0) { - ArchiveFile_t_init(&temp); - files_array_pop_at(&temp, model->files, array_size); - files_array_push_at(model->files, model->item_idx, temp); - ArchiveFile_t_clear(&temp); - } else if(((uint32_t)model->item_idx == array_size) && (dir > 0)) { - ArchiveFile_t_init(&temp); - files_array_pop_at(&temp, model->files, 0); - files_array_push_at(model->files, array_size, temp); - ArchiveFile_t_clear(&temp); - } else { - files_array_swap_at(model->files, model->item_idx, swap_idx); - } - return false; - }); -} - -void archive_file_array_rm_all(ArchiveBrowserView* browser) { - furi_assert(browser); - - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - files_array_reset(model->files); - return false; - }); -} - -void archive_file_array_load(ArchiveBrowserView* browser, int8_t dir) { - furi_assert(browser); - - int32_t offset_new = 0; - - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - if(model->item_cnt > FILE_LIST_BUF_LEN) { - if(dir < 0) { - offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 3; - } else if(dir == 0) { - offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 2; - } else { - offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 1; - } - if(offset_new > 0) { - offset_new = - CLAMP(offset_new, (int32_t)model->item_cnt - FILE_LIST_BUF_LEN, 0); - } else { - offset_new = 0; - } - } - return false; - }); - - file_browser_worker_load(browser->worker, offset_new, FILE_LIST_BUF_LEN); -} - -ArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser) { - furi_assert(browser); - - ArchiveFile_t* selected; - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - selected = files_array_size(model->files) ? - files_array_get(model->files, model->item_idx - model->array_offset) : - NULL; - return false; - }); - return selected; -} - -ArchiveFile_t* archive_get_file_at(ArchiveBrowserView* browser, size_t idx) { - furi_assert(browser); - - ArchiveFile_t* selected; - - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - idx = CLAMP(idx - model->array_offset, files_array_size(model->files), 0u); - selected = files_array_size(model->files) ? files_array_get(model->files, idx) : NULL; - return false; - }); - return selected; -} - -ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser) { - furi_assert(browser); - - ArchiveTabEnum tab_id; - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - tab_id = model->tab_idx; - return false; - }); - return tab_id; -} - -bool archive_is_home(ArchiveBrowserView* browser) { - furi_assert(browser); - - if(browser->is_root) { - return true; - } - - const char* default_path = archive_get_default_path(archive_get_tab(browser)); - return (string_cmp_str(browser->path, default_path) == 0); -} - -const char* archive_get_name(ArchiveBrowserView* browser) { - ArchiveFile_t* selected = archive_get_current_file(browser); - return string_get_cstr(selected->path); -} - -void archive_set_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) { - furi_assert(browser); - - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - model->tab_idx = tab; - return false; - }); -} - -void archive_add_app_item(ArchiveBrowserView* browser, const char* name) { - furi_assert(browser); - furi_assert(name); - - ArchiveFile_t item; - ArchiveFile_t_init(&item); - string_set_str(item.path, name); - archive_set_file_type(&item, name, false, true); - - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - files_array_push_back(model->files, item); - model->item_cnt = files_array_size(model->files); - return false; - }); - ArchiveFile_t_clear(&item); -} - -void archive_add_file_item(ArchiveBrowserView* browser, bool is_folder, const char* name) { - furi_assert(browser); - furi_assert(name); - - ArchiveFile_t item; - - ArchiveFile_t_init(&item); - string_init_set_str(item.path, name); - archive_set_file_type(&item, string_get_cstr(browser->path), is_folder, false); - - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - files_array_push_back(model->files, item); - return false; - }); - ArchiveFile_t_clear(&item); -} - -void archive_show_file_menu(ArchiveBrowserView* browser, bool show) { - furi_assert(browser); - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - if(show) { - if(archive_is_item_in_array(model, model->item_idx)) { - model->menu = true; - model->menu_idx = 0; - ArchiveFile_t* selected = - files_array_get(model->files, model->item_idx - model->array_offset); - selected->fav = archive_is_favorite("%s", string_get_cstr(selected->path)); - } - } else { - model->menu = false; - model->menu_idx = 0; - } - - return true; - }); -} - -void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active) { - furi_assert(browser); - - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - model->move_fav = active; - return true; - }); -} - -static bool archive_is_dir_exists(string_t path) { - if(string_equal_str_p(path, STORAGE_ANY_PATH_PREFIX)) { - return true; - } - bool state = false; - FileInfo file_info; - Storage* storage = furi_record_open(RECORD_STORAGE); - if(storage_common_stat(storage, string_get_cstr(path), &file_info) == FSE_OK) { - if(file_info.flags & FSF_DIRECTORY) { - state = true; - } - } - furi_record_close(RECORD_STORAGE); - return state; -} - -void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { - furi_assert(browser); - ArchiveTabEnum tab = archive_get_tab(browser); - - browser->last_tab_switch_dir = key; - - if(key == InputKeyLeft) { - tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; - } else { - tab = (tab + 1) % ArchiveTabTotal; - } - - browser->is_root = true; - archive_set_tab(browser, tab); - - string_set_str(browser->path, archive_get_default_path(tab)); - bool tab_empty = true; - if(tab == ArchiveTabFavorites) { - if(archive_favorites_count(browser) > 0) { - tab_empty = false; - } - } else if(string_start_with_str_p(browser->path, "/app:")) { - char* app_name = strchr(string_get_cstr(browser->path), ':'); - if(app_name != NULL) { - if(archive_app_is_available(browser, string_get_cstr(browser->path))) { - tab_empty = false; - } - } - } else { - tab = archive_get_tab(browser); - if(archive_is_dir_exists(browser->path)) { - bool skip_assets = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true; - file_browser_worker_set_config( - browser->worker, browser->path, archive_get_tab_ext(tab), skip_assets); - tab_empty = false; // Empty check will be performed later - } else { - tab_empty = true; - } - } - - if((tab_empty) && (tab != ArchiveTabBrowser)) { - archive_switch_tab(browser, key); - } else { - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - model->item_idx = 0; - model->array_offset = 0; - return false; - }); - archive_get_items(browser, string_get_cstr(browser->path)); - archive_update_offset(browser); - } -} - -void archive_enter_dir(ArchiveBrowserView* browser, string_t path) { - furi_assert(browser); - furi_assert(path); - - int32_t idx_temp = 0; - - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - idx_temp = model->item_idx; - return false; - }); - - string_set(browser->path, path); - file_browser_worker_folder_enter(browser->worker, path, idx_temp); -} - -void archive_leave_dir(ArchiveBrowserView* browser) { - furi_assert(browser); - - file_browser_worker_folder_exit(browser->worker); -} - -void archive_refresh_dir(ArchiveBrowserView* browser) { - furi_assert(browser); - - int32_t idx_temp = 0; - - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - idx_temp = model->item_idx; - return false; - }); - file_browser_worker_folder_refresh(browser->worker, idx_temp); -} diff --git a/applications/archive/helpers/archive_favorites.c b/applications/archive/helpers/archive_favorites.c deleted file mode 100644 index 35199242e95..00000000000 --- a/applications/archive/helpers/archive_favorites.c +++ /dev/null @@ -1,350 +0,0 @@ - -#include "archive_favorites.h" -#include "archive_files.h" -#include "archive_apps.h" -#include "archive_browser.h" - -#define ARCHIVE_FAV_FILE_BUF_LEN 32 - -static bool archive_favorites_read_line(File* file, string_t str_result) { - string_reset(str_result); - uint8_t buffer[ARCHIVE_FAV_FILE_BUF_LEN]; - bool result = false; - - do { - uint16_t read_count = storage_file_read(file, buffer, ARCHIVE_FAV_FILE_BUF_LEN); - if(storage_file_get_error(file) != FSE_OK) { - return false; - } - - for(uint16_t i = 0; i < read_count; i++) { - if(buffer[i] == '\n') { - uint32_t position = storage_file_tell(file); - if(storage_file_get_error(file) != FSE_OK) { - return false; - } - - position = position - read_count + i + 1; - - storage_file_seek(file, position, true); - if(storage_file_get_error(file) != FSE_OK) { - return false; - } - - result = true; - break; - } else { - string_push_back(str_result, buffer[i]); - } - } - - if(result || read_count == 0) { - break; - } - } while(true); - - return result; -} - -uint16_t archive_favorites_count(void* context) { - furi_assert(context); - - Storage* fs_api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(fs_api); - - string_t buffer; - string_init(buffer); - - bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); - uint16_t lines = 0; - - if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } - if(!string_size(buffer)) { - continue; // Skip empty lines - } - ++lines; - } - } - - storage_file_close(file); - - string_clear(buffer); - storage_file_free(file); - furi_record_close(RECORD_STORAGE); - - return lines; -} - -static bool archive_favourites_rescan() { - string_t buffer; - string_init(buffer); - Storage* fs_api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(fs_api); - File* fav_item_file = storage_file_alloc(fs_api); - - bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); - if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } - if(!string_size(buffer)) { - continue; - } - - if(string_search(buffer, "/app:") == 0) { - if(archive_app_is_available(NULL, string_get_cstr(buffer))) { - archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer)); - } - } else { - bool file_exists = storage_file_open( - fav_item_file, string_get_cstr(buffer), FSAM_READ, FSOM_OPEN_EXISTING); - if(file_exists) { - storage_file_close(fav_item_file); - archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer)); - } else { - storage_file_close(fav_item_file); - } - } - } - } - - string_clear(buffer); - - storage_file_close(file); - storage_common_remove(fs_api, ARCHIVE_FAV_PATH); - storage_common_rename(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); - storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH); - - storage_file_free(file); - storage_file_free(fav_item_file); - furi_record_close(RECORD_STORAGE); - - return result; -} - -bool archive_favorites_read(void* context) { - furi_assert(context); - - ArchiveBrowserView* browser = context; - Storage* fs_api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(fs_api); - File* fav_item_file = storage_file_alloc(fs_api); - - string_t buffer; - FileInfo file_info; - string_init(buffer); - - bool need_refresh = false; - uint16_t file_count = 0; - - archive_file_array_rm_all(browser); - - bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); - - if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } - if(!string_size(buffer)) { - continue; - } - - if(string_search(buffer, "/app:") == 0) { - if(archive_app_is_available(browser, string_get_cstr(buffer))) { - archive_add_app_item(browser, string_get_cstr(buffer)); - file_count++; - } else { - need_refresh = true; - } - } else { - bool file_exists = storage_file_open( - fav_item_file, string_get_cstr(buffer), FSAM_READ, FSOM_OPEN_EXISTING); - if(file_exists) { - storage_common_stat(fs_api, string_get_cstr(buffer), &file_info); - storage_file_close(fav_item_file); - archive_add_file_item( - browser, (file_info.flags & FSF_DIRECTORY), string_get_cstr(buffer)); - file_count++; - } else { - storage_file_close(fav_item_file); - need_refresh = true; - } - } - - string_reset(buffer); - } - } - storage_file_close(file); - string_clear(buffer); - storage_file_free(file); - storage_file_free(fav_item_file); - furi_record_close(RECORD_STORAGE); - - archive_set_item_count(browser, file_count); - - if(need_refresh) { - archive_favourites_rescan(); - } - - return result; -} - -bool archive_favorites_delete(const char* format, ...) { - string_t buffer; - string_t filename; - va_list args; - va_start(args, format); - string_init_vprintf(filename, format, args); - va_end(args); - - string_init(buffer); - Storage* fs_api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(fs_api); - - bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); - - if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } - if(!string_size(buffer)) { - continue; - } - - if(string_search(buffer, filename)) { - archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer)); - } - } - } - - string_clear(buffer); - string_clear(filename); - - storage_file_close(file); - storage_common_remove(fs_api, ARCHIVE_FAV_PATH); - storage_common_rename(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); - storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH); - - storage_file_free(file); - furi_record_close(RECORD_STORAGE); - - return result; -} - -bool archive_is_favorite(const char* format, ...) { - string_t buffer; - string_t filename; - va_list args; - va_start(args, format); - string_init_vprintf(filename, format, args); - va_end(args); - - string_init(buffer); - Storage* fs_api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(fs_api); - - bool found = false; - bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); - - if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } - if(!string_size(buffer)) { - continue; - } - if(!string_search(buffer, filename)) { - found = true; - break; - } - } - } - - storage_file_close(file); - string_clear(buffer); - string_clear(filename); - storage_file_free(file); - furi_record_close(RECORD_STORAGE); - - return found; -} - -bool archive_favorites_rename(const char* src, const char* dst) { - furi_assert(src); - furi_assert(dst); - - Storage* fs_api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(fs_api); - - string_t path; - string_t buffer; - - string_init(buffer); - string_init(path); - - string_printf(path, "%s", src); - bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); - - if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } - if(!string_size(buffer)) { - continue; - } - - archive_file_append( - ARCHIVE_FAV_TEMP_PATH, - "%s\n", - string_search(buffer, path) ? string_get_cstr(buffer) : dst); - } - } - - string_clear(buffer); - string_clear(path); - - storage_file_close(file); - storage_common_remove(fs_api, ARCHIVE_FAV_PATH); - storage_common_rename(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); - storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH); - - storage_file_free(file); - furi_record_close(RECORD_STORAGE); - - return result; -} - -void archive_add_to_favorites(const char* file_path) { - furi_assert(file_path); - - archive_file_append(ARCHIVE_FAV_PATH, "%s\n", file_path); -} - -void archive_favorites_save(void* context) { - furi_assert(context); - - ArchiveBrowserView* browser = context; - Storage* fs_api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(fs_api); - - for(size_t i = 0; i < archive_file_get_array_size(browser); i++) { - ArchiveFile_t* item = archive_get_file_at(browser, i); - archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(item->path)); - } - - storage_common_remove(fs_api, ARCHIVE_FAV_PATH); - storage_common_rename(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); - storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH); - - storage_file_free(file); - furi_record_close(RECORD_STORAGE); -} diff --git a/applications/archive/helpers/archive_favorites.h b/applications/archive/helpers/archive_favorites.h deleted file mode 100644 index 29eedcdb6f3..00000000000 --- a/applications/archive/helpers/archive_favorites.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -#define ARCHIVE_FAV_PATH ANY_PATH("favorites.txt") -#define ARCHIVE_FAV_TEMP_PATH ANY_PATH("favorites.tmp") - -uint16_t archive_favorites_count(void* context); -bool archive_favorites_read(void* context); -bool archive_favorites_delete(const char* format, ...); -bool archive_is_favorite(const char* format, ...); -bool archive_favorites_rename(const char* src, const char* dst); -void archive_add_to_favorites(const char* file_path); -void archive_favorites_save(void* context); diff --git a/applications/archive/helpers/archive_files.c b/applications/archive/helpers/archive_files.c deleted file mode 100644 index 9f8b4ee1bac..00000000000 --- a/applications/archive/helpers/archive_files.c +++ /dev/null @@ -1,111 +0,0 @@ -#include "archive_files.h" -#include "archive_apps.h" -#include "archive_browser.h" - -#define TAG "Archive" - -#define ASSETS_DIR "assets" - -void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder, bool is_app) { - furi_assert(file); - - file->is_app = is_app; - if(is_app) { - file->type = archive_get_app_filetype(archive_get_app_type(path)); - } else { - for(size_t i = 0; i < COUNT_OF(known_ext); i++) { - if((known_ext[i][0] == '?') || (known_ext[i][0] == '*')) continue; - if(string_search_str(file->path, known_ext[i], 0) != STRING_FAILURE) { - if(i == ArchiveFileTypeBadUsb) { - if(string_search_str(file->path, archive_get_default_path(ArchiveTabBadUsb)) == - 0) { - file->type = i; - return; // *.txt file is a BadUSB script only if it is in BadUSB folder - } - } else { - file->type = i; - return; - } - } - } - - if(is_folder) { - file->type = ArchiveFileTypeFolder; - } else { - file->type = ArchiveFileTypeUnknown; - } - } -} - -bool archive_get_items(void* context, const char* path) { - furi_assert(context); - - bool res = false; - ArchiveBrowserView* browser = context; - - if(archive_get_tab(browser) == ArchiveTabFavorites) { - res = archive_favorites_read(browser); - } else if(strncmp(path, "/app:", 5) == 0) { - res = archive_app_read_dir(browser, path); - } - return res; -} - -void archive_file_append(const char* path, const char* format, ...) { - furi_assert(path); - - string_t string; - va_list args; - va_start(args, format); - string_init_vprintf(string, format, args); - va_end(args); - - Storage* fs_api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(fs_api); - - bool res = storage_file_open(file, path, FSAM_WRITE, FSOM_OPEN_APPEND); - - if(res) { - storage_file_write(file, string_get_cstr(string), string_size(string)); - } - - storage_file_close(file); - storage_file_free(file); - furi_record_close(RECORD_STORAGE); -} - -void archive_delete_file(void* context, const char* format, ...) { - furi_assert(context); - - string_t filename; - va_list args; - va_start(args, format); - string_init_vprintf(filename, format, args); - va_end(args); - - ArchiveBrowserView* browser = context; - Storage* fs_api = furi_record_open(RECORD_STORAGE); - - FileInfo fileinfo; - storage_common_stat(fs_api, string_get_cstr(filename), &fileinfo); - - bool res = false; - - if(fileinfo.flags & FSF_DIRECTORY) { - res = storage_simply_remove_recursive(fs_api, string_get_cstr(filename)); - } else { - res = (storage_common_remove(fs_api, string_get_cstr(filename)) == FSE_OK); - } - - furi_record_close(RECORD_STORAGE); - - if(archive_is_favorite("%s", string_get_cstr(filename))) { - archive_favorites_delete("%s", string_get_cstr(filename)); - } - - if(res) { - archive_file_array_rm_selected(browser); - } - - string_clear(filename); -} diff --git a/applications/archive/helpers/archive_files.h b/applications/archive/helpers/archive_files.h deleted file mode 100644 index 84b7e24a62f..00000000000 --- a/applications/archive/helpers/archive_files.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include -#include -#include - -typedef enum { - ArchiveFileTypeIButton, - ArchiveFileTypeNFC, - ArchiveFileTypeSubGhz, - ArchiveFileTypeLFRFID, - ArchiveFileTypeInfrared, - ArchiveFileTypeBadUsb, - ArchiveFileTypeU2f, - ArchiveFileTypeUpdateManifest, - ArchiveFileTypeFolder, - ArchiveFileTypeUnknown, - ArchiveFileTypeLoading, -} ArchiveFileTypeEnum; - -typedef struct { - string_t path; - ArchiveFileTypeEnum type; - bool fav; - bool is_app; -} ArchiveFile_t; - -static void ArchiveFile_t_init(ArchiveFile_t* obj) { - obj->type = ArchiveFileTypeUnknown; - obj->is_app = false; - obj->fav = false; - string_init(obj->path); -} - -static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { - obj->type = src->type; - obj->is_app = src->is_app; - obj->fav = src->fav; - string_init_set(obj->path, src->path); -} - -static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { - obj->type = src->type; - obj->is_app = src->is_app; - obj->fav = src->fav; - string_set(obj->path, src->path); -} - -static void ArchiveFile_t_clear(ArchiveFile_t* obj) { - string_clear(obj->path); -} - -ARRAY_DEF( - files_array, - ArchiveFile_t, - (INIT(API_2(ArchiveFile_t_init)), - SET(API_6(ArchiveFile_t_set)), - INIT_SET(API_6(ArchiveFile_t_init_set)), - CLEAR(API_2(ArchiveFile_t_clear)))) - -void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder, bool is_app); -bool archive_get_items(void* context, const char* path); -void archive_file_append(const char* path, const char* format, ...); -void archive_delete_file(void* context, const char* format, ...); diff --git a/applications/archive/views/archive_browser_view.c b/applications/archive/views/archive_browser_view.c deleted file mode 100644 index 810d5c8f787..00000000000 --- a/applications/archive/views/archive_browser_view.c +++ /dev/null @@ -1,396 +0,0 @@ -#include "assets_icons.h" -#include "toolbox/path.h" -#include -#include "../archive_i.h" -#include "archive_browser_view.h" -#include "../helpers/archive_browser.h" - -static const char* ArchiveTabNames[] = { - [ArchiveTabFavorites] = "Favorites", - [ArchiveTabIButton] = "iButton", - [ArchiveTabNFC] = "NFC", - [ArchiveTabSubGhz] = "Sub-GHz", - [ArchiveTabLFRFID] = "RFID LF", - [ArchiveTabInfrared] = "Infrared", - [ArchiveTabBadUsb] = "Bad USB", - [ArchiveTabU2f] = "U2F", - [ArchiveTabBrowser] = "Browser", -}; - -static const Icon* ArchiveItemIcons[] = { - [ArchiveFileTypeIButton] = &I_ibutt_10px, - [ArchiveFileTypeNFC] = &I_Nfc_10px, - [ArchiveFileTypeSubGhz] = &I_sub1_10px, - [ArchiveFileTypeLFRFID] = &I_125_10px, - [ArchiveFileTypeInfrared] = &I_ir_10px, - [ArchiveFileTypeBadUsb] = &I_badusb_10px, - [ArchiveFileTypeU2f] = &I_u2f_10px, - [ArchiveFileTypeUpdateManifest] = &I_update_10px, - [ArchiveFileTypeFolder] = &I_dir_10px, - [ArchiveFileTypeUnknown] = &I_unknown_10px, - [ArchiveFileTypeLoading] = &I_loading_10px, -}; - -void archive_browser_set_callback( - ArchiveBrowserView* browser, - ArchiveBrowserViewCallback callback, - void* context) { - furi_assert(browser); - furi_assert(callback); - browser->callback = callback; - browser->context = context; -} - -static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 71, 17, 57, 46); - canvas_set_color(canvas, ColorBlack); - elements_slightly_rounded_frame(canvas, 70, 16, 58, 48); - - string_t menu[MENU_ITEMS]; - - string_init_set_str(menu[0], "Run in app"); - string_init_set_str(menu[1], "Pin"); - string_init_set_str(menu[2], "Rename"); - string_init_set_str(menu[3], "Delete"); - - ArchiveFile_t* selected = files_array_get(model->files, model->item_idx - model->array_offset); - - if((selected->fav) || (model->tab_idx == ArchiveTabFavorites)) { - string_set_str(menu[1], "Unpin"); - } - - if(!archive_is_known_app(selected->type)) { - string_set_str(menu[0], "---"); - string_set_str(menu[1], "---"); - string_set_str(menu[2], "---"); - } else { - if(model->tab_idx == ArchiveTabFavorites) { - string_set_str(menu[2], "Move"); - string_set_str(menu[3], "---"); - } else if(selected->is_app) { - string_set_str(menu[2], "---"); - } - } - - for(size_t i = 0; i < MENU_ITEMS; i++) { - canvas_draw_str(canvas, 82, 27 + i * 11, string_get_cstr(menu[i])); - string_clear(menu[i]); - } - - canvas_draw_icon(canvas, 74, 20 + model->menu_idx * 11, &I_ButtonRight_4x7); -} - -static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar, bool moving) { - uint8_t x_offset = moving ? MOVE_OFFSET : 0; - - canvas_set_color(canvas, ColorBlack); - canvas_draw_box( - canvas, - 0 + x_offset, - 15 + idx * FRAME_HEIGHT, - (scrollbar ? 122 : 127) - x_offset, - FRAME_HEIGHT); - - canvas_set_color(canvas, ColorWhite); - canvas_draw_dot(canvas, 0 + x_offset, 15 + idx * FRAME_HEIGHT); - canvas_draw_dot(canvas, 1 + x_offset, 15 + idx * FRAME_HEIGHT); - canvas_draw_dot(canvas, 0 + x_offset, (15 + idx * FRAME_HEIGHT) + 1); - - canvas_draw_dot(canvas, 0 + x_offset, (15 + idx * FRAME_HEIGHT) + 11); - canvas_draw_dot(canvas, scrollbar ? 121 : 126, 15 + idx * FRAME_HEIGHT); - canvas_draw_dot(canvas, scrollbar ? 121 : 126, (15 + idx * FRAME_HEIGHT) + 11); -} - -static void archive_draw_loading(Canvas* canvas, ArchiveBrowserViewModel* model) { - furi_assert(model); - - uint8_t x = 128 / 2 - 24 / 2; - uint8_t y = 64 / 2 - 24 / 2; - - canvas_draw_icon(canvas, x, y, &A_Loading_24); -} - -static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) { - furi_assert(model); - - size_t array_size = files_array_size(model->files); - bool scrollbar = model->item_cnt > 4; - - for(uint32_t i = 0; i < MIN(model->item_cnt, MENU_ITEMS); ++i) { - string_t str_buf; - string_init(str_buf); - int32_t idx = CLAMP((uint32_t)(i + model->list_offset), model->item_cnt, 0u); - uint8_t x_offset = (model->move_fav && model->item_idx == idx) ? MOVE_OFFSET : 0; - - ArchiveFileTypeEnum file_type = ArchiveFileTypeLoading; - - if(archive_is_item_in_array(model, idx)) { - ArchiveFile_t* file = files_array_get( - model->files, CLAMP(idx - model->array_offset, (int32_t)(array_size - 1), 0)); - path_extract_filename(file->path, str_buf, archive_is_known_app(file->type)); - file_type = file->type; - } else { - string_set_str(str_buf, "---"); - } - - elements_string_fit_width( - canvas, str_buf, (scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX) - x_offset); - - if(model->item_idx == idx) { - archive_draw_frame(canvas, i, scrollbar, model->move_fav); - } else { - canvas_set_color(canvas, ColorBlack); - } - - canvas_draw_icon(canvas, 2 + x_offset, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file_type]); - canvas_draw_str(canvas, 15 + x_offset, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buf)); - - string_clear(str_buf); - } - - if(scrollbar) { - elements_scrollbar_pos(canvas, 126, 15, 49, model->item_idx, model->item_cnt); - } - - if(model->menu) { - render_item_menu(canvas, model); - } -} - -static void archive_render_status_bar(Canvas* canvas, ArchiveBrowserViewModel* model) { - furi_assert(model); - - const char* tab_name = ArchiveTabNames[model->tab_idx]; - - canvas_draw_icon(canvas, 0, 0, &I_Background_128x11); - - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 0, 0, 50, 13); - canvas_draw_box(canvas, 107, 0, 20, 13); - - canvas_set_color(canvas, ColorBlack); - canvas_draw_rframe(canvas, 0, 0, 51, 13, 1); // frame - canvas_draw_line(canvas, 49, 1, 49, 11); // shadow right - canvas_draw_line(canvas, 1, 11, 49, 11); // shadow bottom - canvas_draw_str_aligned(canvas, 25, 9, AlignCenter, AlignBottom, tab_name); - - canvas_draw_rframe(canvas, 107, 0, 21, 13, 1); - canvas_draw_line(canvas, 126, 1, 126, 11); - canvas_draw_line(canvas, 108, 11, 126, 11); - - if(model->move_fav) { - canvas_draw_icon(canvas, 110, 4, &I_ButtonUp_7x4); - canvas_draw_icon(canvas, 117, 4, &I_ButtonDown_7x4); - } else { - canvas_draw_icon(canvas, 111, 2, &I_ButtonLeft_4x7); - canvas_draw_icon(canvas, 119, 2, &I_ButtonRight_4x7); - } - - canvas_set_color(canvas, ColorWhite); - canvas_draw_dot(canvas, 50, 0); - canvas_draw_dot(canvas, 127, 0); - - canvas_set_color(canvas, ColorBlack); -} - -void archive_view_render(Canvas* canvas, void* mdl) { - ArchiveBrowserViewModel* model = mdl; - - archive_render_status_bar(canvas, mdl); - - if(model->folder_loading) { - archive_draw_loading(canvas, model); - } else if(model->item_cnt > 0) { - draw_list(canvas, model); - } else { - canvas_draw_str_aligned( - canvas, GUI_DISPLAY_WIDTH / 2, 40, AlignCenter, AlignCenter, "Empty"); - } -} - -View* archive_browser_get_view(ArchiveBrowserView* browser) { - furi_assert(browser); - return browser->view; -} - -static bool is_file_list_load_required(ArchiveBrowserViewModel* model) { - size_t array_size = files_array_size(model->files); - - if((model->list_loading) || (array_size >= model->item_cnt)) { - return false; - } - - if((model->array_offset > 0) && - (model->item_idx < (model->array_offset + FILE_LIST_BUF_LEN / 4))) { - return true; - } - - if(((model->array_offset + array_size) < model->item_cnt) && - (model->item_idx > (int32_t)(model->array_offset + array_size - FILE_LIST_BUF_LEN / 4))) { - return true; - } - - return false; -} - -bool archive_view_input(InputEvent* event, void* context) { - furi_assert(event); - furi_assert(context); - - ArchiveBrowserView* browser = context; - - bool in_menu; - bool move_fav_mode; - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - in_menu = model->menu; - move_fav_mode = model->move_fav; - return false; - }); - - if(in_menu) { - if(event->type == InputTypeShort) { - if(event->key == InputKeyUp || event->key == InputKeyDown) { - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - if(event->key == InputKeyUp) { - model->menu_idx = ((model->menu_idx - 1) + MENU_ITEMS) % MENU_ITEMS; - } else if(event->key == InputKeyDown) { - model->menu_idx = (model->menu_idx + 1) % MENU_ITEMS; - } - return true; - }); - } - - if(event->key == InputKeyOk) { - uint8_t idx; - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - idx = model->menu_idx; - return false; - }); - browser->callback(file_menu_actions[idx], browser->context); - } else if(event->key == InputKeyBack) { - browser->callback(ArchiveBrowserEventFileMenuClose, browser->context); - } - } - - } else { - if(event->type == InputTypeShort) { - if(event->key == InputKeyLeft || event->key == InputKeyRight) { - if(move_fav_mode) return false; - archive_switch_tab(browser, event->key); - } else if(event->key == InputKeyBack) { - if(move_fav_mode) { - browser->callback(ArchiveBrowserEventExitFavMove, browser->context); - } else { - browser->callback(ArchiveBrowserEventExit, browser->context); - } - } - } - - if((event->key == InputKeyUp || event->key == InputKeyDown) && - (event->type == InputTypeShort || event->type == InputTypeRepeat)) { - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - if(event->key == InputKeyUp) { - model->item_idx = - ((model->item_idx - 1) + model->item_cnt) % model->item_cnt; - if(is_file_list_load_required(model)) { - model->list_loading = true; - browser->callback(ArchiveBrowserEventLoadPrevItems, browser->context); - } - if(move_fav_mode) { - browser->callback(ArchiveBrowserEventFavMoveUp, browser->context); - } - } else if(event->key == InputKeyDown) { - model->item_idx = (model->item_idx + 1) % model->item_cnt; - if(is_file_list_load_required(model)) { - model->list_loading = true; - browser->callback(ArchiveBrowserEventLoadNextItems, browser->context); - } - if(move_fav_mode) { - browser->callback(ArchiveBrowserEventFavMoveDown, browser->context); - } - } - - return true; - }); - archive_update_offset(browser); - } - - if(event->key == InputKeyOk) { - ArchiveFile_t* selected = archive_get_current_file(browser); - - if(selected) { - bool favorites = archive_get_tab(browser) == ArchiveTabFavorites; - bool folder = selected->type == ArchiveFileTypeFolder; - - if(event->type == InputTypeShort) { - if(favorites) { - if(move_fav_mode) { - browser->callback(ArchiveBrowserEventSaveFavMove, browser->context); - } else { - browser->callback(ArchiveBrowserEventFileMenuRun, browser->context); - } - } else if(folder) { - browser->callback(ArchiveBrowserEventEnterDir, browser->context); - } else { - browser->callback(ArchiveBrowserEventFileMenuOpen, browser->context); - } - } else if(event->type == InputTypeLong) { - if(move_fav_mode) { - browser->callback(ArchiveBrowserEventSaveFavMove, browser->context); - } else if(folder || favorites) { - browser->callback(ArchiveBrowserEventFileMenuOpen, browser->context); - } - } - } - } - } - - return true; -} - -ArchiveBrowserView* browser_alloc() { - ArchiveBrowserView* browser = malloc(sizeof(ArchiveBrowserView)); - browser->view = view_alloc(); - view_allocate_model(browser->view, ViewModelTypeLocking, sizeof(ArchiveBrowserViewModel)); - view_set_context(browser->view, browser); - view_set_draw_callback(browser->view, archive_view_render); - view_set_input_callback(browser->view, archive_view_input); - - string_init_set_str(browser->path, archive_get_default_path(TAB_DEFAULT)); - - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - files_array_init(model->files); - model->tab_idx = TAB_DEFAULT; - return true; - }); - - browser->worker = file_browser_worker_alloc(browser->path, "*", false); - archive_file_browser_set_callbacks(browser); - - file_browser_worker_set_callback_context(browser->worker, browser); - - return browser; -} - -void browser_free(ArchiveBrowserView* browser) { - furi_assert(browser); - - file_browser_worker_free(browser->worker); - - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - files_array_clear(model->files); - return false; - }); - - string_clear(browser->path); - - view_free(browser->view); - free(browser); -} diff --git a/applications/bad_usb/application.fam b/applications/bad_usb/application.fam deleted file mode 100644 index 4da34f0de1b..00000000000 --- a/applications/bad_usb/application.fam +++ /dev/null @@ -1,14 +0,0 @@ -App( - appid="bad_usb", - name="Bad USB", - apptype=FlipperAppType.APP, - entry_point="bad_usb_app", - cdefines=["APP_BAD_USB"], - requires=[ - "gui", - "dialogs", - ], - stack_size=2 * 1024, - icon="A_BadUsb_14", - order=70, -) diff --git a/applications/bad_usb/bad_usb_app.c b/applications/bad_usb/bad_usb_app.c deleted file mode 100644 index 09d7d346834..00000000000 --- a/applications/bad_usb/bad_usb_app.c +++ /dev/null @@ -1,110 +0,0 @@ -#include "bad_usb_app_i.h" -#include "m-string.h" -#include -#include -#include -#include - -static bool bad_usb_app_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - BadUsbApp* app = context; - return scene_manager_handle_custom_event(app->scene_manager, event); -} - -static bool bad_usb_app_back_event_callback(void* context) { - furi_assert(context); - BadUsbApp* app = context; - return scene_manager_handle_back_event(app->scene_manager); -} - -static void bad_usb_app_tick_event_callback(void* context) { - furi_assert(context); - BadUsbApp* app = context; - scene_manager_handle_tick_event(app->scene_manager); -} - -BadUsbApp* bad_usb_app_alloc(char* arg) { - BadUsbApp* app = malloc(sizeof(BadUsbApp)); - - string_init(app->file_path); - - if(arg && strlen(arg)) { - string_set_str(app->file_path, arg); - } - - app->gui = furi_record_open(RECORD_GUI); - app->notifications = furi_record_open(RECORD_NOTIFICATION); - app->dialogs = furi_record_open(RECORD_DIALOGS); - - app->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_enable_queue(app->view_dispatcher); - - app->scene_manager = scene_manager_alloc(&bad_usb_scene_handlers, app); - - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - view_dispatcher_set_tick_event_callback( - app->view_dispatcher, bad_usb_app_tick_event_callback, 500); - view_dispatcher_set_custom_event_callback( - app->view_dispatcher, bad_usb_app_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, bad_usb_app_back_event_callback); - - // Custom Widget - app->widget = widget_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadUsbAppViewError, widget_get_view(app->widget)); - - app->bad_usb_view = bad_usb_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadUsbAppViewWork, bad_usb_get_view(app->bad_usb_view)); - - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - if(furi_hal_usb_is_locked()) { - app->error = BadUsbAppErrorCloseRpc; - scene_manager_next_scene(app->scene_manager, BadUsbSceneError); - } else { - if(!string_empty_p(app->file_path)) { - scene_manager_next_scene(app->scene_manager, BadUsbSceneWork); - } else { - string_set_str(app->file_path, BAD_USB_APP_PATH_FOLDER); - scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect); - } - } - - return app; -} - -void bad_usb_app_free(BadUsbApp* app) { - furi_assert(app); - - // Views - view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork); - bad_usb_free(app->bad_usb_view); - - // Custom Widget - view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewError); - widget_free(app->widget); - - // View dispatcher - view_dispatcher_free(app->view_dispatcher); - scene_manager_free(app->scene_manager); - - // Close records - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); - furi_record_close(RECORD_DIALOGS); - - string_clear(app->file_path); - - free(app); -} - -int32_t bad_usb_app(void* p) { - BadUsbApp* bad_usb_app = bad_usb_app_alloc((char*)p); - - view_dispatcher_run(bad_usb_app->view_dispatcher); - - bad_usb_app_free(bad_usb_app); - return 0; -} diff --git a/applications/bad_usb/bad_usb_app_i.h b/applications/bad_usb/bad_usb_app_i.h deleted file mode 100644 index 6378bddfb21..00000000000 --- a/applications/bad_usb/bad_usb_app_i.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "bad_usb_app.h" -#include "scenes/bad_usb_scene.h" -#include "bad_usb_script.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include "views/bad_usb_view.h" - -#define BAD_USB_APP_PATH_FOLDER ANY_PATH("badusb") -#define BAD_USB_APP_EXTENSION ".txt" - -typedef enum { - BadUsbAppErrorNoFiles, - BadUsbAppErrorCloseRpc, -} BadUsbAppError; - -struct BadUsbApp { - Gui* gui; - ViewDispatcher* view_dispatcher; - SceneManager* scene_manager; - NotificationApp* notifications; - DialogsApp* dialogs; - Widget* widget; - - BadUsbAppError error; - string_t file_path; - BadUsb* bad_usb_view; - BadUsbScript* bad_usb_script; -}; - -typedef enum { - BadUsbAppViewError, - BadUsbAppViewWork, -} BadUsbAppView; diff --git a/applications/bad_usb/bad_usb_script.c b/applications/bad_usb/bad_usb_script.c deleted file mode 100644 index 9d9d3e39719..00000000000 --- a/applications/bad_usb/bad_usb_script.c +++ /dev/null @@ -1,624 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "bad_usb_script.h" -#include - -#define TAG "BadUSB" -#define WORKER_TAG TAG "Worker" -#define FILE_BUFFER_LEN 16 - -#define SCRIPT_STATE_ERROR (-1) -#define SCRIPT_STATE_END (-2) -#define SCRIPT_STATE_NEXT_LINE (-3) - -typedef enum { - WorkerEvtToggle = (1 << 0), - WorkerEvtEnd = (1 << 1), - WorkerEvtConnect = (1 << 2), - WorkerEvtDisconnect = (1 << 3), -} WorkerEvtFlags; - -struct BadUsbScript { - FuriHalUsbHidConfig hid_cfg; - BadUsbState st; - string_t file_path; - uint32_t defdelay; - FuriThread* thread; - uint8_t file_buf[FILE_BUFFER_LEN + 1]; - uint8_t buf_start; - uint8_t buf_len; - bool file_end; - string_t line; - - string_t line_prev; - uint32_t repeat_cnt; -}; - -typedef struct { - char* name; - uint16_t keycode; -} DuckyKey; - -static const DuckyKey ducky_keys[] = { - {"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT}, - {"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT}, - {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT}, - {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI}, - {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT}, - - {"CTRL", KEY_MOD_LEFT_CTRL}, - {"CONTROL", KEY_MOD_LEFT_CTRL}, - {"SHIFT", KEY_MOD_LEFT_SHIFT}, - {"ALT", KEY_MOD_LEFT_ALT}, - {"GUI", KEY_MOD_LEFT_GUI}, - {"WINDOWS", KEY_MOD_LEFT_GUI}, - - {"DOWNARROW", HID_KEYBOARD_DOWN_ARROW}, - {"DOWN", HID_KEYBOARD_DOWN_ARROW}, - {"LEFTARROW", HID_KEYBOARD_LEFT_ARROW}, - {"LEFT", HID_KEYBOARD_LEFT_ARROW}, - {"RIGHTARROW", HID_KEYBOARD_RIGHT_ARROW}, - {"RIGHT", HID_KEYBOARD_RIGHT_ARROW}, - {"UPARROW", HID_KEYBOARD_UP_ARROW}, - {"UP", HID_KEYBOARD_UP_ARROW}, - - {"ENTER", HID_KEYBOARD_RETURN}, - {"BREAK", HID_KEYBOARD_PAUSE}, - {"PAUSE", HID_KEYBOARD_PAUSE}, - {"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK}, - {"DELETE", HID_KEYBOARD_DELETE}, - {"BACKSPACE", HID_KEYPAD_BACKSPACE}, - {"END", HID_KEYBOARD_END}, - {"ESC", HID_KEYBOARD_ESCAPE}, - {"ESCAPE", HID_KEYBOARD_ESCAPE}, - {"HOME", HID_KEYBOARD_HOME}, - {"INSERT", HID_KEYBOARD_INSERT}, - {"NUMLOCK", HID_KEYPAD_NUMLOCK}, - {"PAGEUP", HID_KEYBOARD_PAGE_UP}, - {"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN}, - {"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN}, - {"SCROLLOCK", HID_KEYBOARD_SCROLL_LOCK}, - {"SPACE", HID_KEYBOARD_SPACEBAR}, - {"TAB", HID_KEYBOARD_TAB}, - {"MENU", HID_KEYBOARD_APPLICATION}, - {"APP", HID_KEYBOARD_APPLICATION}, - - {"F1", HID_KEYBOARD_F1}, - {"F2", HID_KEYBOARD_F2}, - {"F3", HID_KEYBOARD_F3}, - {"F4", HID_KEYBOARD_F4}, - {"F5", HID_KEYBOARD_F5}, - {"F6", HID_KEYBOARD_F6}, - {"F7", HID_KEYBOARD_F7}, - {"F8", HID_KEYBOARD_F8}, - {"F9", HID_KEYBOARD_F9}, - {"F10", HID_KEYBOARD_F10}, - {"F11", HID_KEYBOARD_F11}, - {"F12", HID_KEYBOARD_F12}, -}; - -static const char ducky_cmd_comment[] = {"REM"}; -static const char ducky_cmd_id[] = {"ID"}; -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_repeat[] = {"REPEAT "}; - -static const char ducky_cmd_altchar[] = {"ALTCHAR "}; -static const char ducky_cmd_altstr_1[] = {"ALTSTRING "}; -static const char ducky_cmd_altstr_2[] = {"ALTCODE "}; - -static const uint8_t numpad_keys[10] = { - HID_KEYPAD_0, - HID_KEYPAD_1, - HID_KEYPAD_2, - HID_KEYPAD_3, - HID_KEYPAD_4, - HID_KEYPAD_5, - HID_KEYPAD_6, - HID_KEYPAD_7, - HID_KEYPAD_8, - HID_KEYPAD_9, -}; - -static bool ducky_get_number(const char* param, uint32_t* val) { - uint32_t value = 0; - if(sscanf(param, "%lu", &value) == 1) { - *val = value; - return true; - } - return false; -} - -static uint32_t ducky_get_command_len(const char* line) { - uint32_t len = strlen(line); - for(uint32_t i = 0; i < len; i++) { - if(line[i] == ' ') return i; - } - return 0; -} - -static bool ducky_is_line_end(const char chr) { - return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n')); -} - -static void ducky_numlock_on() { - if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) { - furi_hal_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK); - furi_hal_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK); - } -} - -static bool ducky_numpad_press(const char num) { - if((num < '0') || (num > '9')) return false; - - uint16_t key = numpad_keys[num - '0']; - furi_hal_hid_kb_press(key); - furi_hal_hid_kb_release(key); - - return true; -} - -static bool ducky_altchar(const char* charcode) { - uint8_t i = 0; - bool state = false; - - FURI_LOG_I(WORKER_TAG, "char %s", charcode); - - furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT); - - while(!ducky_is_line_end(charcode[i])) { - state = ducky_numpad_press(charcode[i]); - if(state == false) break; - i++; - } - - furi_hal_hid_kb_release(KEY_MOD_LEFT_ALT); - return state; -} - -static bool ducky_altstring(const char* param) { - uint32_t i = 0; - bool state = false; - - while(param[i] != '\0') { - if((param[i] < ' ') || (param[i] > '~')) { - i++; - continue; // Skip non-printable chars - } - - char temp_str[4]; - snprintf(temp_str, 4, "%u", param[i]); - - state = ducky_altchar(temp_str); - if(state == false) break; - i++; - } - return state; -} - -static bool ducky_string(const char* param) { - uint32_t i = 0; - while(param[i] != '\0') { - uint16_t keycode = HID_ASCII_TO_KEY(param[i]); - if(keycode != HID_KEYBOARD_NONE) { - furi_hal_hid_kb_press(keycode); - furi_hal_hid_kb_release(keycode); - } - i++; - } - return true; -} - -static uint16_t ducky_get_keycode(const char* param, bool accept_chars) { - for(uint8_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) { - uint8_t key_cmd_len = strlen(ducky_keys[i].name); - if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) && - (ducky_is_line_end(param[key_cmd_len]))) { - return ducky_keys[i].keycode; - } - } - if((accept_chars) && (strlen(param) > 0)) { - return (HID_ASCII_TO_KEY(param[0]) & 0xFF); - } - return 0; -} - -static int32_t ducky_parse_line(BadUsbScript* bad_usb, string_t line) { - uint32_t line_len = string_size(line); - const char* line_tmp = string_get_cstr(line); - bool state = false; - - for(uint32_t i = 0; i < line_len; i++) { - if((line_tmp[i] != ' ') && (line_tmp[i] != '\t') && (line_tmp[i] != '\n')) { - line_tmp = &line_tmp[i]; - break; // Skip spaces and tabs - } - if(i == line_len - 1) return SCRIPT_STATE_NEXT_LINE; // Skip empty lines - } - - FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp); - - // General commands - if(strncmp(line_tmp, ducky_cmd_comment, strlen(ducky_cmd_comment)) == 0) { - // REM - comment line - return (0); - } else if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) { - // ID - executed in ducky_script_preload - return (0); - } else if(strncmp(line_tmp, ducky_cmd_delay, strlen(ducky_cmd_delay)) == 0) { - // DELAY - line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - uint32_t delay_val = 0; - state = ducky_get_number(line_tmp, &delay_val); - if((state) && (delay_val > 0)) { - return (int32_t)delay_val; - } - return SCRIPT_STATE_ERROR; - } else if( - (strncmp(line_tmp, ducky_cmd_defdelay_1, strlen(ducky_cmd_defdelay_1)) == 0) || - (strncmp(line_tmp, ducky_cmd_defdelay_2, strlen(ducky_cmd_defdelay_2)) == 0)) { - // DEFAULT_DELAY - line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - state = ducky_get_number(line_tmp, &bad_usb->defdelay); - return (state) ? (0) : 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]; - state = ducky_string(line_tmp); - return (state) ? (0) : SCRIPT_STATE_ERROR; - } else if(strncmp(line_tmp, ducky_cmd_altchar, strlen(ducky_cmd_altchar)) == 0) { - // ALTCHAR - line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - ducky_numlock_on(); - state = ducky_altchar(line_tmp); - return (state) ? (0) : SCRIPT_STATE_ERROR; - } else if( - (strncmp(line_tmp, ducky_cmd_altstr_1, strlen(ducky_cmd_altstr_1)) == 0) || - (strncmp(line_tmp, ducky_cmd_altstr_2, strlen(ducky_cmd_altstr_2)) == 0)) { - // ALTSTRING - line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - ducky_numlock_on(); - state = ducky_altstring(line_tmp); - return (state) ? (0) : SCRIPT_STATE_ERROR; - } else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) { - // REPEAT - line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - state = ducky_get_number(line_tmp, &bad_usb->repeat_cnt); - return (state) ? (0) : SCRIPT_STATE_ERROR; - } else { - // Special keys + modifiers - uint16_t key = ducky_get_keycode(line_tmp, false); - if(key == HID_KEYBOARD_NONE) return SCRIPT_STATE_ERROR; - if((key & 0xFF00) != 0) { - // It's a modifier key - line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - key |= ducky_get_keycode(line_tmp, true); - } - furi_hal_hid_kb_press(key); - furi_hal_hid_kb_release(key); - return (0); - } - return SCRIPT_STATE_ERROR; -} - -static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) { - if(sscanf(line, "%lX:%lX", &bad_usb->hid_cfg.vid, &bad_usb->hid_cfg.pid) == 2) { - bad_usb->hid_cfg.manuf[0] = '\0'; - bad_usb->hid_cfg.product[0] = '\0'; - - uint8_t id_len = ducky_get_command_len(line); - if(!ducky_is_line_end(line[id_len + 1])) { - sscanf( - &line[id_len + 1], - "%31[^\r\n:]:%31[^\r\n]", - bad_usb->hid_cfg.manuf, - bad_usb->hid_cfg.product); - } - FURI_LOG_D( - WORKER_TAG, - "set id: %04X:%04X mfr:%s product:%s", - bad_usb->hid_cfg.vid, - bad_usb->hid_cfg.pid, - bad_usb->hid_cfg.manuf, - bad_usb->hid_cfg.product); - return true; - } - return false; -} - -static bool ducky_script_preload(BadUsbScript* bad_usb, File* script_file) { - uint8_t ret = 0; - uint32_t line_len = 0; - - string_reset(bad_usb->line); - - do { - ret = storage_file_read(script_file, bad_usb->file_buf, FILE_BUFFER_LEN); - for(uint16_t i = 0; i < ret; i++) { - if(bad_usb->file_buf[i] == '\n' && line_len > 0) { - bad_usb->st.line_nb++; - line_len = 0; - } else { - if(bad_usb->st.line_nb == 0) { // Save first line - string_push_back(bad_usb->line, bad_usb->file_buf[i]); - } - line_len++; - } - } - if(storage_file_eof(script_file)) { - if(line_len > 0) { - bad_usb->st.line_nb++; - break; - } - } - } while(ret > 0); - - const char* line_tmp = string_get_cstr(bad_usb->line); - bool id_set = false; // Looking for ID command at first line - if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) { - id_set = ducky_set_usb_id(bad_usb, &line_tmp[strlen(ducky_cmd_id) + 1]); - } - - if(id_set) { - furi_check(furi_hal_usb_set_config(&usb_hid, &bad_usb->hid_cfg)); - } else { - furi_check(furi_hal_usb_set_config(&usb_hid, NULL)); - } - - storage_file_seek(script_file, 0, true); - string_reset(bad_usb->line); - - return true; -} - -static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_file) { - int32_t delay_val = 0; - - if(bad_usb->repeat_cnt > 0) { - bad_usb->repeat_cnt--; - delay_val = ducky_parse_line(bad_usb, bad_usb->line_prev); - if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line - return 0; - } else if(delay_val < 0) { // Script error - bad_usb->st.error_line = bad_usb->st.line_cur - 1; - FURI_LOG_E(WORKER_TAG, "Unknown command at line %lu", bad_usb->st.line_cur - 1); - return SCRIPT_STATE_ERROR; - } else { - return (delay_val + bad_usb->defdelay); - } - } - - string_set(bad_usb->line_prev, bad_usb->line); - string_reset(bad_usb->line); - - while(1) { - if(bad_usb->buf_len == 0) { - bad_usb->buf_len = storage_file_read(script_file, bad_usb->file_buf, FILE_BUFFER_LEN); - if(storage_file_eof(script_file)) { - if((bad_usb->buf_len < FILE_BUFFER_LEN) && (bad_usb->file_end == false)) { - bad_usb->file_buf[bad_usb->buf_len] = '\n'; - bad_usb->buf_len++; - bad_usb->file_end = true; - } - } - - bad_usb->buf_start = 0; - if(bad_usb->buf_len == 0) return SCRIPT_STATE_END; - } - for(uint8_t i = bad_usb->buf_start; i < (bad_usb->buf_start + bad_usb->buf_len); i++) { - if(bad_usb->file_buf[i] == '\n' && string_size(bad_usb->line) > 0) { - bad_usb->st.line_cur++; - bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1); - bad_usb->buf_start = i + 1; - delay_val = ducky_parse_line(bad_usb, bad_usb->line); - if(delay_val < 0) { - bad_usb->st.error_line = bad_usb->st.line_cur; - FURI_LOG_E(WORKER_TAG, "Unknown command at line %lu", bad_usb->st.line_cur); - return SCRIPT_STATE_ERROR; - } else { - return (delay_val + bad_usb->defdelay); - } - } else { - string_push_back(bad_usb->line, bad_usb->file_buf[i]); - } - } - bad_usb->buf_len = 0; - if(bad_usb->file_end) return SCRIPT_STATE_END; - } - - return 0; -} - -static void bad_usb_hid_state_callback(bool state, void* context) { - furi_assert(context); - BadUsbScript* bad_usb = context; - - if(state == true) - furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtConnect); - else - furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtDisconnect); -} - -static int32_t bad_usb_worker(void* context) { - BadUsbScript* bad_usb = context; - - BadUsbWorkerState worker_state = BadUsbStateInit; - int32_t delay_val = 0; - - FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); - - FURI_LOG_I(WORKER_TAG, "Init"); - File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - string_init(bad_usb->line); - string_init(bad_usb->line_prev); - - furi_hal_hid_set_state_callback(bad_usb_hid_state_callback, bad_usb); - - while(1) { - if(worker_state == BadUsbStateInit) { // State: initialization - if(storage_file_open( - script_file, - string_get_cstr(bad_usb->file_path), - FSAM_READ, - FSOM_OPEN_EXISTING)) { - if((ducky_script_preload(bad_usb, script_file)) && (bad_usb->st.line_nb > 0)) { - if(furi_hal_hid_is_connected()) { - worker_state = BadUsbStateIdle; // Ready to run - } else { - worker_state = BadUsbStateNotConnected; // USB not connected - } - } else { - worker_state = BadUsbStateScriptError; // Script preload error - } - } else { - FURI_LOG_E(WORKER_TAG, "File open error"); - worker_state = BadUsbStateFileError; // File open error - } - bad_usb->st.state = worker_state; - - } else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtConnect, FuriFlagWaitAny, FuriWaitForever); - furi_check((flags & FuriFlagError) == 0); - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtConnect) { - worker_state = BadUsbStateIdle; // Ready to run - } - bad_usb->st.state = worker_state; - - } else if(worker_state == BadUsbStateIdle) { // State: ready to start - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, - FuriFlagWaitAny, - FuriWaitForever); - furi_check((flags & FuriFlagError) == 0); - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtToggle) { // Start executing script - DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); - delay_val = 0; - bad_usb->buf_len = 0; - bad_usb->st.line_cur = 0; - bad_usb->defdelay = 0; - bad_usb->repeat_cnt = 0; - bad_usb->file_end = false; - storage_file_seek(script_file, 0, true); - worker_state = BadUsbStateRunning; - } else if(flags & WorkerEvtDisconnect) { - worker_state = BadUsbStateNotConnected; // USB disconnected - } - bad_usb->st.state = worker_state; - - } else if(worker_state == BadUsbStateRunning) { // State: running - uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur); - delay_val -= delay_cur; - if(!(flags & FuriFlagError)) { - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtToggle) { - worker_state = BadUsbStateIdle; // Stop executing script - furi_hal_hid_kb_release_all(); - } else if(flags & WorkerEvtDisconnect) { - worker_state = BadUsbStateNotConnected; // USB disconnected - furi_hal_hid_kb_release_all(); - } - bad_usb->st.state = worker_state; - continue; - } else if((flags == FuriFlagErrorTimeout) || (flags == FuriFlagErrorResource)) { - if(delay_val > 0) { - bad_usb->st.delay_remain--; - continue; - } - bad_usb->st.state = BadUsbStateRunning; - delay_val = ducky_script_execute_next(bad_usb, script_file); - if(delay_val == SCRIPT_STATE_ERROR) { // Script error - delay_val = 0; - worker_state = BadUsbStateScriptError; - bad_usb->st.state = worker_state; - } else if(delay_val == SCRIPT_STATE_END) { // End of script - delay_val = 0; - worker_state = BadUsbStateIdle; - bad_usb->st.state = BadUsbStateDone; - furi_hal_hid_kb_release_all(); - continue; - } else if(delay_val > 1000) { - bad_usb->st.state = BadUsbStateDelay; // Show long delays - bad_usb->st.delay_remain = delay_val / 1000; - } - } else { - furi_check((flags & FuriFlagError) == 0); - } - - } else if( - (worker_state == BadUsbStateFileError) || - (worker_state == BadUsbStateScriptError)) { // State: error - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd, FuriFlagWaitAny, FuriWaitForever); // Waiting for exit command - furi_check((flags & FuriFlagError) == 0); - if(flags & WorkerEvtEnd) { - break; - } - } - } - - furi_hal_hid_set_state_callback(NULL, NULL); - - furi_hal_usb_set_config(usb_mode_prev, NULL); - - storage_file_close(script_file); - storage_file_free(script_file); - string_clear(bad_usb->line); - string_clear(bad_usb->line_prev); - - FURI_LOG_I(WORKER_TAG, "End"); - - return 0; -} - -BadUsbScript* bad_usb_script_open(string_t file_path) { - furi_assert(file_path); - - BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); - string_init(bad_usb->file_path); - string_set(bad_usb->file_path, file_path); - - bad_usb->st.state = BadUsbStateInit; - - bad_usb->thread = furi_thread_alloc(); - furi_thread_set_name(bad_usb->thread, "BadUsbWorker"); - furi_thread_set_stack_size(bad_usb->thread, 2048); - furi_thread_set_context(bad_usb->thread, bad_usb); - furi_thread_set_callback(bad_usb->thread, bad_usb_worker); - - furi_thread_start(bad_usb->thread); - return bad_usb; -} - -void bad_usb_script_close(BadUsbScript* bad_usb) { - furi_assert(bad_usb); - furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtEnd); - furi_thread_join(bad_usb->thread); - furi_thread_free(bad_usb->thread); - string_clear(bad_usb->file_path); - free(bad_usb); -} - -void bad_usb_script_toggle(BadUsbScript* bad_usb) { - furi_assert(bad_usb); - furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtToggle); -} - -BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb) { - furi_assert(bad_usb); - return &(bad_usb->st); -} diff --git a/applications/bad_usb/bad_usb_script.h b/applications/bad_usb/bad_usb_script.h deleted file mode 100644 index 88921de381e..00000000000 --- a/applications/bad_usb/bad_usb_script.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -typedef struct BadUsbScript BadUsbScript; - -typedef enum { - BadUsbStateInit, - BadUsbStateNotConnected, - BadUsbStateIdle, - BadUsbStateRunning, - BadUsbStateDelay, - BadUsbStateDone, - BadUsbStateScriptError, - BadUsbStateFileError, -} BadUsbWorkerState; - -typedef struct { - BadUsbWorkerState state; - uint16_t line_cur; - uint16_t line_nb; - uint32_t delay_remain; - uint16_t error_line; -} BadUsbState; - -BadUsbScript* bad_usb_script_open(string_t file_path); - -void bad_usb_script_close(BadUsbScript* bad_usb); - -void bad_usb_script_start(BadUsbScript* bad_usb); - -void bad_usb_script_stop(BadUsbScript* bad_usb); - -void bad_usb_script_toggle(BadUsbScript* bad_usb); - -BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb); - -#ifdef __cplusplus -} -#endif diff --git a/applications/bad_usb/scenes/bad_usb_scene_config.h b/applications/bad_usb/scenes/bad_usb_scene_config.h deleted file mode 100644 index 0ab8f54f874..00000000000 --- a/applications/bad_usb/scenes/bad_usb_scene_config.h +++ /dev/null @@ -1,3 +0,0 @@ -ADD_SCENE(bad_usb, file_select, FileSelect) -ADD_SCENE(bad_usb, work, Work) -ADD_SCENE(bad_usb, error, Error) diff --git a/applications/bad_usb/scenes/bad_usb_scene_file_select.c b/applications/bad_usb/scenes/bad_usb_scene_file_select.c deleted file mode 100644 index 1e6ba895a18..00000000000 --- a/applications/bad_usb/scenes/bad_usb_scene_file_select.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "../bad_usb_app_i.h" -#include "furi_hal_power.h" -#include "furi_hal_usb.h" - -static bool bad_usb_file_select(BadUsbApp* bad_usb) { - furi_assert(bad_usb); - - // Input events and views are managed by file_browser - bool res = dialog_file_browser_show( - bad_usb->dialogs, - bad_usb->file_path, - bad_usb->file_path, - BAD_USB_APP_EXTENSION, - true, - &I_badusb_10px, - true); - - return res; -} - -void bad_usb_scene_file_select_on_enter(void* context) { - BadUsbApp* bad_usb = context; - - furi_hal_usb_disable(); - - if(bad_usb_file_select(bad_usb)) { - scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork); - } else { - furi_hal_usb_enable(); - //scene_manager_previous_scene(bad_usb->scene_manager); - view_dispatcher_stop(bad_usb->view_dispatcher); - } -} - -bool bad_usb_scene_file_select_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - // BadUsbApp* bad_usb = context; - return false; -} - -void bad_usb_scene_file_select_on_exit(void* context) { - UNUSED(context); - // BadUsbApp* bad_usb = context; -} diff --git a/applications/bad_usb/scenes/bad_usb_scene_work.c b/applications/bad_usb/scenes/bad_usb_scene_work.c deleted file mode 100644 index 516cbde3af4..00000000000 --- a/applications/bad_usb/scenes/bad_usb_scene_work.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "../bad_usb_script.h" -#include "../bad_usb_app_i.h" -#include "../views/bad_usb_view.h" -#include "furi_hal.h" -#include "m-string.h" -#include "toolbox/path.h" - -void bad_usb_scene_work_ok_callback(InputType type, void* context) { - furi_assert(context); - BadUsbApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, type); -} - -bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) { - BadUsbApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - bad_usb_script_toggle(app->bad_usb_script); - consumed = true; - } else if(event.type == SceneManagerEventTypeTick) { - bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script)); - } - return consumed; -} - -void bad_usb_scene_work_on_enter(void* context) { - BadUsbApp* app = context; - - string_t file_name; - string_init(file_name); - - path_extract_filename(app->file_path, file_name, true); - bad_usb_set_file_name(app->bad_usb_view, string_get_cstr(file_name)); - app->bad_usb_script = bad_usb_script_open(app->file_path); - - string_clear(file_name); - - bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script)); - - bad_usb_set_ok_callback(app->bad_usb_view, bad_usb_scene_work_ok_callback, app); - view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewWork); -} - -void bad_usb_scene_work_on_exit(void* context) { - BadUsbApp* app = context; - bad_usb_script_close(app->bad_usb_script); -} diff --git a/applications/bad_usb/views/bad_usb_view.c b/applications/bad_usb/views/bad_usb_view.c deleted file mode 100644 index 0679669f6b5..00000000000 --- a/applications/bad_usb/views/bad_usb_view.c +++ /dev/null @@ -1,171 +0,0 @@ -#include "bad_usb_view.h" -#include "../bad_usb_script.h" -#include - -#define MAX_NAME_LEN 64 - -struct BadUsb { - View* view; - BadUsbOkCallback callback; - void* context; -}; - -typedef struct { - char file_name[MAX_NAME_LEN]; - BadUsbState state; - uint8_t anim_frame; -} BadUsbModel; - -static void bad_usb_draw_callback(Canvas* canvas, void* _model) { - BadUsbModel* model = _model; - - string_t disp_str; - string_init_set_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, string_get_cstr(disp_str)); - string_reset(disp_str); - - canvas_draw_icon(canvas, 22, 20, &I_UsbTree_48x22); - - if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone)) { - elements_button_center(canvas, "Run"); - } else if((model->state.state == BadUsbStateRunning) || (model->state.state == BadUsbStateDelay)) { - elements_button_center(canvas, "Stop"); - } - - if(model->state.state == BadUsbStateNotConnected) { - canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "Connect"); - canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "to USB"); - } else if(model->state.state == BadUsbStateFileError) { - canvas_draw_icon(canvas, 4, 22, &I_Error_18x18); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "File"); - canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "ERROR"); - } else if(model->state.state == BadUsbStateScriptError) { - canvas_draw_icon(canvas, 4, 22, &I_Error_18x18); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); - canvas_set_font(canvas, FontSecondary); - string_printf(disp_str, "line %u", model->state.error_line); - canvas_draw_str_aligned( - canvas, 127, 46, AlignRight, AlignBottom, string_get_cstr(disp_str)); - string_reset(disp_str); - } else if(model->state.state == BadUsbStateIdle) { - canvas_draw_icon(canvas, 4, 22, &I_Smile_18x18); - canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 114, 36, AlignRight, AlignBottom, "0"); - canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14); - } else if(model->state.state == BadUsbStateRunning) { - if(model->anim_frame == 0) { - canvas_draw_icon(canvas, 4, 19, &I_EviSmile1_18x21); - } else { - canvas_draw_icon(canvas, 4, 19, &I_EviSmile2_18x21); - } - canvas_set_font(canvas, FontBigNumbers); - string_printf(disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); - canvas_draw_str_aligned( - canvas, 114, 36, AlignRight, AlignBottom, string_get_cstr(disp_str)); - string_reset(disp_str); - canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14); - } else if(model->state.state == BadUsbStateDone) { - canvas_draw_icon(canvas, 4, 19, &I_EviSmile1_18x21); - canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 114, 36, AlignRight, AlignBottom, "100"); - string_reset(disp_str); - canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14); - } else if(model->state.state == BadUsbStateDelay) { - if(model->anim_frame == 0) { - canvas_draw_icon(canvas, 4, 19, &I_EviWaiting1_18x21); - } else { - canvas_draw_icon(canvas, 4, 19, &I_EviWaiting2_18x21); - } - canvas_set_font(canvas, FontBigNumbers); - string_printf(disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); - canvas_draw_str_aligned( - canvas, 114, 36, AlignRight, AlignBottom, string_get_cstr(disp_str)); - string_reset(disp_str); - canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14); - canvas_set_font(canvas, FontSecondary); - string_printf(disp_str, "delay %us", model->state.delay_remain); - canvas_draw_str_aligned( - canvas, 127, 46, AlignRight, AlignBottom, string_get_cstr(disp_str)); - string_reset(disp_str); - } else { - canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18); - } - - string_clear(disp_str); -} - -static bool bad_usb_input_callback(InputEvent* event, void* context) { - furi_assert(context); - BadUsb* bad_usb = context; - bool consumed = false; - - if(event->type == InputTypeShort) { - if(event->key == InputKeyOk) { - consumed = true; - furi_assert(bad_usb->callback); - bad_usb->callback(InputTypeShort, bad_usb->context); - } - } - - return consumed; -} - -BadUsb* bad_usb_alloc() { - BadUsb* bad_usb = malloc(sizeof(BadUsb)); - - bad_usb->view = view_alloc(); - view_allocate_model(bad_usb->view, ViewModelTypeLocking, sizeof(BadUsbModel)); - view_set_context(bad_usb->view, bad_usb); - view_set_draw_callback(bad_usb->view, bad_usb_draw_callback); - view_set_input_callback(bad_usb->view, bad_usb_input_callback); - - return bad_usb; -} - -void bad_usb_free(BadUsb* bad_usb) { - furi_assert(bad_usb); - view_free(bad_usb->view); - free(bad_usb); -} - -View* bad_usb_get_view(BadUsb* bad_usb) { - furi_assert(bad_usb); - return bad_usb->view; -} - -void bad_usb_set_ok_callback(BadUsb* bad_usb, BadUsbOkCallback callback, void* context) { - furi_assert(bad_usb); - furi_assert(callback); - with_view_model( - bad_usb->view, (BadUsbModel * model) { - UNUSED(model); - bad_usb->callback = callback; - bad_usb->context = context; - return true; - }); -} - -void bad_usb_set_file_name(BadUsb* bad_usb, const char* name) { - furi_assert(name); - with_view_model( - bad_usb->view, (BadUsbModel * model) { - strlcpy(model->file_name, name, MAX_NAME_LEN); - return true; - }); -} - -void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) { - furi_assert(st); - with_view_model( - bad_usb->view, (BadUsbModel * model) { - memcpy(&(model->state), st, sizeof(BadUsbState)); - model->anim_frame ^= 1; - return true; - }); -} diff --git a/applications/bad_usb/views/bad_usb_view.h b/applications/bad_usb/views/bad_usb_view.h deleted file mode 100755 index 80a47e2ca67..00000000000 --- a/applications/bad_usb/views/bad_usb_view.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include "../bad_usb_script.h" - -typedef struct BadUsb BadUsb; -typedef void (*BadUsbOkCallback)(InputType type, void* context); - -BadUsb* bad_usb_alloc(); - -void bad_usb_free(BadUsb* bad_usb); - -View* bad_usb_get_view(BadUsb* bad_usb); - -void bad_usb_set_ok_callback(BadUsb* bad_usb, BadUsbOkCallback callback, void* context); - -void bad_usb_set_file_name(BadUsb* bad_usb, const char* name); - -void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st); diff --git a/applications/bt/application.fam b/applications/bt/application.fam deleted file mode 100644 index 248386ff305..00000000000 --- a/applications/bt/application.fam +++ /dev/null @@ -1,66 +0,0 @@ -App( - appid="bt", - name="BtSrv", - apptype=FlipperAppType.SERVICE, - entry_point="bt_srv", - cdefines=["SRV_BT"], - requires=[ - "cli", - "dialogs", - ], - provides=[ - "bt_start", - "bt_settings", - "bt_debug", - ], - stack_size=1 * 1024, - order=20, -) - -App( - appid="bt_start", - apptype=FlipperAppType.STARTUP, - entry_point="bt_on_system_start", - order=70, -) - -App( - appid="bt_settings", - name="Bluetooth", - apptype=FlipperAppType.SETTINGS, - entry_point="bt_settings_app", - stack_size=1 * 1024, - requires=[ - "bt", - "gui", - ], - order=10, -) - -App( - appid="bt_debug", - name="Bluetooth Debug", - apptype=FlipperAppType.DEBUG, - entry_point="bt_debug_app", - stack_size=1 * 1024, - requires=[ - "bt", - "gui", - "dialogs", - ], - order=110, -) - -App( - appid="bt_hid", - name="Bluetooth Remote", - apptype=FlipperAppType.PLUGIN, - entry_point="bt_hid_app", - stack_size=1 * 1024, - cdefines=["APP_BLE_HID"], - requires=[ - "bt", - "gui", - ], - order=10, -) diff --git a/applications/bt/bt_debug_app/views/bt_test.c b/applications/bt/bt_debug_app/views/bt_test.c deleted file mode 100755 index 70f57c12c18..00000000000 --- a/applications/bt/bt_debug_app/views/bt_test.c +++ /dev/null @@ -1,422 +0,0 @@ -#include "bt_test.h" - -#include -#include -#include -#include -#include -#include - -struct BtTestParam { - const char* label; - uint8_t current_value_index; - string_t current_value_text; - uint8_t values_count; - BtTestParamChangeCallback change_callback; - void* context; -}; - -ARRAY_DEF(BtTestParamArray, BtTestParam, M_POD_OPLIST); - -struct BtTest { - View* view; - BtTestChangeStateCallback change_state_callback; - BtTestBackCallback back_callback; - void* context; -}; - -typedef struct { - BtTestState state; - BtTestParamArray_t params; - uint8_t position; - uint8_t window_position; - const char* message; - float rssi; - uint32_t packets_num_rx; - uint32_t packets_num_tx; -} BtTestModel; - -#define BT_TEST_START_MESSAGE "Ok - Start" -#define BT_TEST_STOP_MESSAGE "Ok - Stop" - -static void bt_test_process_up(BtTest* bt_test); -static void bt_test_process_down(BtTest* bt_test); -static void bt_test_process_left(BtTest* bt_test); -static void bt_test_process_right(BtTest* bt_test); -static void bt_test_process_ok(BtTest* bt_test); -static void bt_test_process_back(BtTest* bt_test); - -static void bt_test_draw_callback(Canvas* canvas, void* _model) { - BtTestModel* model = _model; - char info_str[32]; - - const uint8_t param_height = 16; - const uint8_t param_width = 123; - - canvas_clear(canvas); - - uint8_t position = 0; - BtTestParamArray_it_t it; - - canvas_set_font(canvas, FontSecondary); - for(BtTestParamArray_it(it, model->params); !BtTestParamArray_end_p(it); - BtTestParamArray_next(it)) { - uint8_t param_position = position - model->window_position; - uint8_t params_on_screen = 3; - uint8_t y_offset = 0; - - if(param_position < params_on_screen) { - const BtTestParam* param = BtTestParamArray_cref(it); - uint8_t param_y = y_offset + (param_position * param_height); - uint8_t param_text_y = param_y + param_height - 4; - - if(position == model->position) { - canvas_set_color(canvas, ColorBlack); - elements_slightly_rounded_box( - canvas, 0, param_y + 1, param_width, param_height - 2); - canvas_set_color(canvas, ColorWhite); - } else { - canvas_set_color(canvas, ColorBlack); - } - - canvas_draw_str(canvas, 6, param_text_y, param->label); - - if(param->current_value_index > 0) { - canvas_draw_str(canvas, 50, param_text_y, "<"); - } - - canvas_draw_str(canvas, 61, param_text_y, string_get_cstr(param->current_value_text)); - - if(param->current_value_index < (param->values_count - 1)) { - canvas_draw_str(canvas, 113, param_text_y, ">"); - } - } - - position++; - } - - elements_scrollbar(canvas, model->position, BtTestParamArray_size(model->params)); - canvas_draw_str(canvas, 6, 60, model->message); - if(model->state == BtTestStateStarted) { - if(model->rssi != 0.0f) { - snprintf(info_str, sizeof(info_str), "RSSI:%3.1f dB", (double)model->rssi); - canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str); - } - } else if(model->state == BtTestStateStopped) { - if(model->packets_num_rx) { - snprintf(info_str, sizeof(info_str), "%ld pack rcv", model->packets_num_rx); - canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str); - } else if(model->packets_num_tx) { - snprintf(info_str, sizeof(info_str), "%ld pack sent", model->packets_num_tx); - canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str); - } - } -} - -static bool bt_test_input_callback(InputEvent* event, void* context) { - BtTest* bt_test = context; - furi_assert(bt_test); - bool consumed = false; - - if(event->type == InputTypeShort) { - switch(event->key) { - case InputKeyUp: - consumed = true; - bt_test_process_up(bt_test); - break; - case InputKeyDown: - consumed = true; - bt_test_process_down(bt_test); - break; - case InputKeyLeft: - consumed = true; - bt_test_process_left(bt_test); - break; - case InputKeyRight: - consumed = true; - bt_test_process_right(bt_test); - break; - case InputKeyOk: - consumed = true; - bt_test_process_ok(bt_test); - break; - case InputKeyBack: - consumed = false; - bt_test_process_back(bt_test); - break; - default: - break; - } - } - - return consumed; -} - -void bt_test_process_up(BtTest* bt_test) { - with_view_model( - bt_test->view, (BtTestModel * model) { - uint8_t params_on_screen = 3; - if(model->position > 0) { - model->position--; - if(((model->position - model->window_position) < 1) && - model->window_position > 0) { - model->window_position--; - } - } else { - model->position = BtTestParamArray_size(model->params) - 1; - if(model->position > (params_on_screen - 1)) { - model->window_position = model->position - (params_on_screen - 1); - } - } - return true; - }); -} - -void bt_test_process_down(BtTest* bt_test) { - with_view_model( - bt_test->view, (BtTestModel * model) { - uint8_t params_on_screen = 3; - if(model->position < (BtTestParamArray_size(model->params) - 1)) { - model->position++; - if((model->position - model->window_position) > (params_on_screen - 2) && - model->window_position < - (BtTestParamArray_size(model->params) - params_on_screen)) { - model->window_position++; - } - } else { - model->position = 0; - model->window_position = 0; - } - return true; - }); -} - -BtTestParam* bt_test_get_selected_param(BtTestModel* model) { - BtTestParam* param = NULL; - - BtTestParamArray_it_t it; - uint8_t position = 0; - for(BtTestParamArray_it(it, model->params); !BtTestParamArray_end_p(it); - BtTestParamArray_next(it)) { - if(position == model->position) { - break; - } - position++; - } - - param = BtTestParamArray_ref(it); - - furi_assert(param); - return param; -} - -void bt_test_process_left(BtTest* bt_test) { - BtTestParam* param; - with_view_model( - bt_test->view, (BtTestModel * model) { - param = bt_test_get_selected_param(model); - if(param->current_value_index > 0) { - param->current_value_index--; - if(param->change_callback) { - model->state = BtTestStateStopped; - model->message = BT_TEST_START_MESSAGE; - model->rssi = 0.0f; - model->packets_num_rx = 0; - model->packets_num_tx = 0; - } - } - return true; - }); - if(param->change_callback) { - param->change_callback(param); - } -} - -void bt_test_process_right(BtTest* bt_test) { - BtTestParam* param; - with_view_model( - bt_test->view, (BtTestModel * model) { - param = bt_test_get_selected_param(model); - if(param->current_value_index < (param->values_count - 1)) { - param->current_value_index++; - if(param->change_callback) { - model->state = BtTestStateStopped; - model->message = BT_TEST_START_MESSAGE; - model->rssi = 0.0f; - model->packets_num_rx = 0; - model->packets_num_tx = 0; - } - } - return true; - }); - if(param->change_callback) { - param->change_callback(param); - } -} - -void bt_test_process_ok(BtTest* bt_test) { - BtTestState state; - with_view_model( - bt_test->view, (BtTestModel * model) { - if(model->state == BtTestStateStarted) { - model->state = BtTestStateStopped; - model->message = BT_TEST_START_MESSAGE; - model->rssi = 0.0f; - model->packets_num_rx = 0; - model->packets_num_tx = 0; - } else if(model->state == BtTestStateStopped) { - model->state = BtTestStateStarted; - model->message = BT_TEST_STOP_MESSAGE; - } - state = model->state; - return true; - }); - if(bt_test->change_state_callback) { - bt_test->change_state_callback(state, bt_test->context); - } -} - -void bt_test_process_back(BtTest* bt_test) { - with_view_model( - bt_test->view, (BtTestModel * model) { - model->state = BtTestStateStopped; - model->rssi = 0.0f; - model->packets_num_rx = 0; - model->packets_num_tx = 0; - return false; - }); - if(bt_test->back_callback) { - bt_test->back_callback(bt_test->context); - } -} - -BtTest* bt_test_alloc() { - BtTest* bt_test = malloc(sizeof(BtTest)); - bt_test->view = view_alloc(); - view_set_context(bt_test->view, bt_test); - view_allocate_model(bt_test->view, ViewModelTypeLocking, sizeof(BtTestModel)); - view_set_draw_callback(bt_test->view, bt_test_draw_callback); - view_set_input_callback(bt_test->view, bt_test_input_callback); - - with_view_model( - bt_test->view, (BtTestModel * model) { - model->state = BtTestStateStopped; - model->message = "Ok - Start"; - BtTestParamArray_init(model->params); - model->position = 0; - model->window_position = 0; - model->rssi = 0.0f; - model->packets_num_tx = 0; - model->packets_num_rx = 0; - return true; - }); - - return bt_test; -} - -void bt_test_free(BtTest* bt_test) { - furi_assert(bt_test); - - with_view_model( - bt_test->view, (BtTestModel * model) { - BtTestParamArray_it_t it; - for(BtTestParamArray_it(it, model->params); !BtTestParamArray_end_p(it); - BtTestParamArray_next(it)) { - string_clear(BtTestParamArray_ref(it)->current_value_text); - } - BtTestParamArray_clear(model->params); - return false; - }); - view_free(bt_test->view); - free(bt_test); -} - -View* bt_test_get_view(BtTest* bt_test) { - furi_assert(bt_test); - return bt_test->view; -} - -BtTestParam* bt_test_param_add( - BtTest* bt_test, - const char* label, - uint8_t values_count, - BtTestParamChangeCallback change_callback, - void* context) { - BtTestParam* param = NULL; - furi_assert(label); - furi_assert(bt_test); - - with_view_model( - bt_test->view, (BtTestModel * model) { - param = BtTestParamArray_push_new(model->params); - param->label = label; - param->values_count = values_count; - param->change_callback = change_callback; - param->context = context; - param->current_value_index = 0; - string_init(param->current_value_text); - return true; - }); - - return param; -} - -void bt_test_set_rssi(BtTest* bt_test, float rssi) { - furi_assert(bt_test); - with_view_model( - bt_test->view, (BtTestModel * model) { - model->rssi = rssi; - return true; - }); -} - -void bt_test_set_packets_tx(BtTest* bt_test, uint32_t packets_num) { - furi_assert(bt_test); - with_view_model( - bt_test->view, (BtTestModel * model) { - model->packets_num_tx = packets_num; - return true; - }); -} - -void bt_test_set_packets_rx(BtTest* bt_test, uint32_t packets_num) { - furi_assert(bt_test); - with_view_model( - bt_test->view, (BtTestModel * model) { - model->packets_num_rx = packets_num; - return true; - }); -} - -void bt_test_set_change_state_callback(BtTest* bt_test, BtTestChangeStateCallback callback) { - furi_assert(bt_test); - furi_assert(callback); - bt_test->change_state_callback = callback; -} - -void bt_test_set_back_callback(BtTest* bt_test, BtTestBackCallback callback) { - furi_assert(bt_test); - furi_assert(callback); - bt_test->back_callback = callback; -} - -void bt_test_set_context(BtTest* bt_test, void* context) { - furi_assert(bt_test); - bt_test->context = context; -} - -void bt_test_set_current_value_index(BtTestParam* param, uint8_t current_value_index) { - param->current_value_index = current_value_index; -} - -void bt_test_set_current_value_text(BtTestParam* param, const char* current_value_text) { - string_set_str(param->current_value_text, current_value_text); -} - -uint8_t bt_test_get_current_value_index(BtTestParam* param) { - return param->current_value_index; -} - -void* bt_test_get_context(BtTestParam* param) { - return param->context; -} diff --git a/applications/bt/bt_hid_app/bt_hid.c b/applications/bt/bt_hid_app/bt_hid.c deleted file mode 100755 index 3189042c0e9..00000000000 --- a/applications/bt/bt_hid_app/bt_hid.c +++ /dev/null @@ -1,198 +0,0 @@ -#include "bt_hid.h" -#include -#include - -#define TAG "BtHidApp" - -enum BtDebugSubmenuIndex { - BtHidSubmenuIndexKeynote, - BtHidSubmenuIndexKeyboard, - BtHidSubmenuIndexMedia, - BtHidSubmenuIndexMouse, -}; - -void bt_hid_submenu_callback(void* context, uint32_t index) { - furi_assert(context); - BtHid* app = context; - if(index == BtHidSubmenuIndexKeynote) { - app->view_id = BtHidViewKeynote; - view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewKeynote); - } else if(index == BtHidSubmenuIndexKeyboard) { - app->view_id = BtHidViewKeyboard; - view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewKeyboard); - } else if(index == BtHidSubmenuIndexMedia) { - app->view_id = BtHidViewMedia; - view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewMedia); - } else if(index == BtHidSubmenuIndexMouse) { - app->view_id = BtHidViewMouse; - view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewMouse); - } -} - -void bt_hid_dialog_callback(DialogExResult result, void* context) { - furi_assert(context); - BtHid* 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, BtHidViewSubmenu); - } -} - -uint32_t bt_hid_exit_confirm_view(void* context) { - UNUSED(context); - return BtHidViewExitConfirm; -} - -uint32_t bt_hid_exit(void* context) { - UNUSED(context); - return VIEW_NONE; -} - -void bt_hid_connection_status_changed_callback(BtStatus status, void* context) { - furi_assert(context); - BtHid* bt_hid = context; - bool connected = (status == BtStatusConnected); - if(connected) { - notification_internal_message(bt_hid->notifications, &sequence_set_blue_255); - } else { - notification_internal_message(bt_hid->notifications, &sequence_reset_blue); - } - bt_hid_keynote_set_connected_status(bt_hid->bt_hid_keynote, connected); - bt_hid_keyboard_set_connected_status(bt_hid->bt_hid_keyboard, connected); - bt_hid_media_set_connected_status(bt_hid->bt_hid_media, connected); - bt_hid_mouse_set_connected_status(bt_hid->bt_hid_mouse, connected); -} - -BtHid* bt_hid_app_alloc() { - BtHid* app = malloc(sizeof(BtHid)); - - // Gui - app->gui = furi_record_open(RECORD_GUI); - - // Bt - app->bt = furi_record_open(RECORD_BT); - - // 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, "Keynote", BtHidSubmenuIndexKeynote, bt_hid_submenu_callback, app); - submenu_add_item( - app->submenu, "Keyboard", BtHidSubmenuIndexKeyboard, bt_hid_submenu_callback, app); - submenu_add_item( - app->submenu, "Media Player", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app); - submenu_add_item(app->submenu, "Mouse", BtHidSubmenuIndexMouse, bt_hid_submenu_callback, app); - view_set_previous_callback(submenu_get_view(app->submenu), bt_hid_exit); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewSubmenu, submenu_get_view(app->submenu)); - - // Dialog view - app->dialog = dialog_ex_alloc(); - dialog_ex_set_result_callback(app->dialog, bt_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, BtHidViewExitConfirm, dialog_ex_get_view(app->dialog)); - - // Keynote view - app->bt_hid_keynote = bt_hid_keynote_alloc(); - view_set_previous_callback( - bt_hid_keynote_get_view(app->bt_hid_keynote), bt_hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewKeynote, bt_hid_keynote_get_view(app->bt_hid_keynote)); - - // Keyboard view - app->bt_hid_keyboard = bt_hid_keyboard_alloc(); - view_set_previous_callback( - bt_hid_keyboard_get_view(app->bt_hid_keyboard), bt_hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewKeyboard, bt_hid_keyboard_get_view(app->bt_hid_keyboard)); - - // Media view - app->bt_hid_media = bt_hid_media_alloc(); - view_set_previous_callback(bt_hid_media_get_view(app->bt_hid_media), bt_hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewMedia, bt_hid_media_get_view(app->bt_hid_media)); - - // Mouse view - app->bt_hid_mouse = bt_hid_mouse_alloc(); - view_set_previous_callback(bt_hid_mouse_get_view(app->bt_hid_mouse), bt_hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewMouse, bt_hid_mouse_get_view(app->bt_hid_mouse)); - - // TODO switch to menu after Media is done - app->view_id = BtHidViewKeynote; - view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); - - return app; -} - -void bt_hid_app_free(BtHid* app) { - furi_assert(app); - - // Reset notification - notification_internal_message(app->notifications, &sequence_reset_blue); - - // Free views - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewSubmenu); - submenu_free(app->submenu); - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewExitConfirm); - dialog_ex_free(app->dialog); - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewKeynote); - bt_hid_keynote_free(app->bt_hid_keynote); - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewKeyboard); - bt_hid_keyboard_free(app->bt_hid_keyboard); - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewMedia); - bt_hid_media_free(app->bt_hid_media); - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewMouse); - bt_hid_mouse_free(app->bt_hid_mouse); - view_dispatcher_free(app->view_dispatcher); - - // Close records - furi_record_close(RECORD_GUI); - app->gui = NULL; - furi_record_close(RECORD_NOTIFICATION); - app->notifications = NULL; - furi_record_close(RECORD_BT); - app->bt = NULL; - - // Free rest - free(app); -} - -int32_t bt_hid_app(void* p) { - UNUSED(p); - // Switch profile to Hid - BtHid* app = bt_hid_app_alloc(); - bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); - // Change profile - if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) { - FURI_LOG_E(TAG, "Failed to switch profile"); - bt_hid_app_free(app); - return -1; - } - furi_hal_bt_start_advertising(); - - view_dispatcher_run(app->view_dispatcher); - - bt_set_status_changed_callback(app->bt, NULL, NULL); - // Change back profile to Serial - bt_set_profile(app->bt, BtProfileSerial); - - bt_hid_app_free(app); - - return 0; -} diff --git a/applications/bt/bt_hid_app/bt_hid.h b/applications/bt/bt_hid_app/bt_hid.h deleted file mode 100644 index 81d092db4d0..00000000000 --- a/applications/bt/bt_hid_app/bt_hid.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include -#include "views/bt_hid_keynote.h" -#include "views/bt_hid_keyboard.h" -#include "views/bt_hid_media.h" -#include "views/bt_hid_mouse.h" - -typedef struct { - Bt* bt; - Gui* gui; - NotificationApp* notifications; - ViewDispatcher* view_dispatcher; - Submenu* submenu; - DialogEx* dialog; - BtHidKeynote* bt_hid_keynote; - BtHidKeyboard* bt_hid_keyboard; - BtHidMedia* bt_hid_media; - BtHidMouse* bt_hid_mouse; - uint32_t view_id; -} BtHid; - -typedef enum { - BtHidViewSubmenu, - BtHidViewKeynote, - BtHidViewKeyboard, - BtHidViewMedia, - BtHidViewMouse, - BtHidViewExitConfirm, -} BtHidView; diff --git a/applications/bt/bt_hid_app/views/bt_hid_keyboard.c b/applications/bt/bt_hid_app/views/bt_hid_keyboard.c deleted file mode 100644 index 1088e2959d6..00000000000 --- a/applications/bt/bt_hid_app/views/bt_hid_keyboard.c +++ /dev/null @@ -1,384 +0,0 @@ -#include "bt_hid_keyboard.h" -#include -#include -#include -#include -#include - -struct BtHidKeyboard { - 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]; -} BtHidKeyboardModel; - -typedef struct { - uint8_t width; - char* key; - const Icon* icon; - char* shift_key; - uint8_t value; -} BtHidKeyboardKey; - -typedef struct { - int8_t x; - int8_t y; -} BtHidKeyboardPoint; - -// 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 6 -#define COLUMN_COUNT 12 - -// 0 width items are not drawn, but there value is used -const BtHidKeyboardKey bt_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_up7x9, .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 = 3, .icon = NULL, .key = "Ctrl", .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 = 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 = 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 = 0, .icon = NULL, .value = HID_KEYBOARD_TAB}, - }, -}; - -static void bt_hid_keyboard_to_upper(char* str) { - while(*str) { - *str = toupper((unsigned char)*str); - str++; - } -} - -static void bt_hid_keyboard_draw_key( - Canvas* canvas, - BtHidKeyboardModel* model, - uint8_t x, - uint8_t y, - BtHidKeyboardKey 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)) { - bt_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 bt_hid_keyboard_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - BtHidKeyboardModel* model = context; - - // Header - if(!model->connected) { - canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keyboard"); - elements_multiline_text_aligned( - canvas, 4, 60, AlignLeft, AlignBottom, "Waiting for Connection..."); - return; // Dont render the keyboard if we are not yet connected - } - - 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 BtHidKeyboardKey* keyboardKeyRow = bt_hid_keyboard_keyset[y]; - uint8_t x = 0; - for(uint8_t i = 0; i < COLUMN_COUNT; i++) { - BtHidKeyboardKey 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; - bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE; - bt_hid_keyboard_draw_key( - canvas, - model, - x, - y - initY, - key, - (!model->ok_pressed && keySelected) || backSelected); - x += key.width; - } - } -} - -static uint8_t bt_hid_keyboard_get_selected_key(BtHidKeyboardModel* model) { - BtHidKeyboardKey key = bt_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 bt_hid_keyboard_get_select_key(BtHidKeyboardModel* model, BtHidKeyboardPoint 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 && bt_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 && bt_hid_keyboard_keyset[model->y][model->x].width == - 0); // Skip zero width keys, pretend they are one key -} - -static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent* event) { - with_view_model( - bt_hid_keyboard->view, (BtHidKeyboardModel * 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 = bt_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_bt_hid_kb_press(model->modifier_code | model->last_key_code); - } else if(event->type == InputTypeRelease) { - // Release happens after short and long presses - furi_hal_bt_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_bt_hid_kb_press(HID_KEYBOARD_DELETE); - furi_hal_bt_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) { - bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 0, .y = -1}); - } else if(event->key == InputKeyDown) { - bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 0, .y = 1}); - } else if(event->key == InputKeyLeft) { - bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = -1, .y = 0}); - } else if(event->key == InputKeyRight) { - bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 1, .y = 0}); - } - } - return true; - }); -} - -static bool bt_hid_keyboard_input_callback(InputEvent* event, void* context) { - furi_assert(context); - BtHidKeyboard* bt_hid_keyboard = context; - bool consumed = false; - - if(event->type == InputTypeLong && event->key == InputKeyBack) { - furi_hal_bt_hid_kb_release_all(); - } else { - bt_hid_keyboard_process(bt_hid_keyboard, event); - consumed = true; - } - - return consumed; -} - -BtHidKeyboard* bt_hid_keyboard_alloc() { - BtHidKeyboard* bt_hid_keyboard = malloc(sizeof(BtHidKeyboard)); - bt_hid_keyboard->view = view_alloc(); - view_set_context(bt_hid_keyboard->view, bt_hid_keyboard); - view_allocate_model(bt_hid_keyboard->view, ViewModelTypeLocking, sizeof(BtHidKeyboardModel)); - view_set_draw_callback(bt_hid_keyboard->view, bt_hid_keyboard_draw_callback); - view_set_input_callback(bt_hid_keyboard->view, bt_hid_keyboard_input_callback); - - return bt_hid_keyboard; -} - -void bt_hid_keyboard_free(BtHidKeyboard* bt_hid_keyboard) { - furi_assert(bt_hid_keyboard); - view_free(bt_hid_keyboard->view); - free(bt_hid_keyboard); -} - -View* bt_hid_keyboard_get_view(BtHidKeyboard* bt_hid_keyboard) { - furi_assert(bt_hid_keyboard); - return bt_hid_keyboard->view; -} - -void bt_hid_keyboard_set_connected_status(BtHidKeyboard* bt_hid_keyboard, bool connected) { - furi_assert(bt_hid_keyboard); - with_view_model( - bt_hid_keyboard->view, (BtHidKeyboardModel * model) { - model->connected = connected; - return true; - }); -} diff --git a/applications/bt/bt_hid_app/views/bt_hid_keyboard.h b/applications/bt/bt_hid_app/views/bt_hid_keyboard.h deleted file mode 100644 index b2cc928e2a9..00000000000 --- a/applications/bt/bt_hid_app/views/bt_hid_keyboard.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct BtHidKeyboard BtHidKeyboard; - -BtHidKeyboard* bt_hid_keyboard_alloc(); - -void bt_hid_keyboard_free(BtHidKeyboard* bt_hid_keyboard); - -View* bt_hid_keyboard_get_view(BtHidKeyboard* bt_hid_keyboard); - -void bt_hid_keyboard_set_connected_status(BtHidKeyboard* bt_hid_keyboard, bool connected); diff --git a/applications/bt/bt_hid_app/views/bt_hid_keynote.c b/applications/bt/bt_hid_app/views/bt_hid_keynote.c deleted file mode 100755 index 60a1ebc0877..00000000000 --- a/applications/bt/bt_hid_app/views/bt_hid_keynote.c +++ /dev/null @@ -1,201 +0,0 @@ -#include "bt_hid_keynote.h" -#include -#include -#include -#include - -struct BtHidKeynote { - View* view; -}; - -typedef struct { - bool left_pressed; - bool up_pressed; - bool right_pressed; - bool down_pressed; - bool ok_pressed; - bool back_pressed; - bool connected; -} BtHidKeynoteModel; - -static void bt_hid_keynote_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 bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - BtHidKeynoteModel* 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, "Keynote"); - canvas_set_font(canvas, FontSecondary); - - // 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); - } - bt_hid_keynote_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); - } - bt_hid_keynote_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); - } - bt_hid_keynote_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); - } - bt_hid_keynote_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, 110, 49, &I_Ok_btn_9x9); - elements_multiline_text_aligned(canvas, 76, 56, AlignLeft, AlignBottom, "Back"); -} - -static void bt_hid_keynote_process(BtHidKeynote* bt_hid_keynote, InputEvent* event) { - with_view_model( - bt_hid_keynote->view, (BtHidKeynoteModel * model) { - if(event->type == InputTypePress) { - if(event->key == InputKeyUp) { - model->up_pressed = true; - furi_hal_bt_hid_kb_press(HID_KEYBOARD_UP_ARROW); - } else if(event->key == InputKeyDown) { - model->down_pressed = true; - furi_hal_bt_hid_kb_press(HID_KEYBOARD_DOWN_ARROW); - } else if(event->key == InputKeyLeft) { - model->left_pressed = true; - furi_hal_bt_hid_kb_press(HID_KEYBOARD_LEFT_ARROW); - } else if(event->key == InputKeyRight) { - model->right_pressed = true; - furi_hal_bt_hid_kb_press(HID_KEYBOARD_RIGHT_ARROW); - } else if(event->key == InputKeyOk) { - model->ok_pressed = true; - furi_hal_bt_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_bt_hid_kb_release(HID_KEYBOARD_UP_ARROW); - } else if(event->key == InputKeyDown) { - model->down_pressed = false; - furi_hal_bt_hid_kb_release(HID_KEYBOARD_DOWN_ARROW); - } else if(event->key == InputKeyLeft) { - model->left_pressed = false; - furi_hal_bt_hid_kb_release(HID_KEYBOARD_LEFT_ARROW); - } else if(event->key == InputKeyRight) { - model->right_pressed = false; - furi_hal_bt_hid_kb_release(HID_KEYBOARD_RIGHT_ARROW); - } else if(event->key == InputKeyOk) { - model->ok_pressed = false; - furi_hal_bt_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_bt_hid_kb_press(HID_KEYBOARD_DELETE); - furi_hal_bt_hid_kb_release(HID_KEYBOARD_DELETE); - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_AC_BACK); - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_AC_BACK); - } - } - return true; - }); -} - -static bool bt_hid_keynote_input_callback(InputEvent* event, void* context) { - furi_assert(context); - BtHidKeynote* bt_hid_keynote = context; - bool consumed = false; - - if(event->type == InputTypeLong && event->key == InputKeyBack) { - furi_hal_bt_hid_kb_release_all(); - } else { - bt_hid_keynote_process(bt_hid_keynote, event); - consumed = true; - } - - return consumed; -} - -BtHidKeynote* bt_hid_keynote_alloc() { - BtHidKeynote* bt_hid_keynote = malloc(sizeof(BtHidKeynote)); - bt_hid_keynote->view = view_alloc(); - view_set_context(bt_hid_keynote->view, bt_hid_keynote); - view_allocate_model(bt_hid_keynote->view, ViewModelTypeLocking, sizeof(BtHidKeynoteModel)); - view_set_draw_callback(bt_hid_keynote->view, bt_hid_keynote_draw_callback); - view_set_input_callback(bt_hid_keynote->view, bt_hid_keynote_input_callback); - - return bt_hid_keynote; -} - -void bt_hid_keynote_free(BtHidKeynote* bt_hid_keynote) { - furi_assert(bt_hid_keynote); - view_free(bt_hid_keynote->view); - free(bt_hid_keynote); -} - -View* bt_hid_keynote_get_view(BtHidKeynote* bt_hid_keynote) { - furi_assert(bt_hid_keynote); - return bt_hid_keynote->view; -} - -void bt_hid_keynote_set_connected_status(BtHidKeynote* bt_hid_keynote, bool connected) { - furi_assert(bt_hid_keynote); - with_view_model( - bt_hid_keynote->view, (BtHidKeynoteModel * model) { - model->connected = connected; - return true; - }); -} diff --git a/applications/bt/bt_hid_app/views/bt_hid_keynote.h b/applications/bt/bt_hid_app/views/bt_hid_keynote.h deleted file mode 100644 index 05b121457db..00000000000 --- a/applications/bt/bt_hid_app/views/bt_hid_keynote.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct BtHidKeynote BtHidKeynote; - -BtHidKeynote* bt_hid_keynote_alloc(); - -void bt_hid_keynote_free(BtHidKeynote* bt_hid_keynote); - -View* bt_hid_keynote_get_view(BtHidKeynote* bt_hid_keynote); - -void bt_hid_keynote_set_connected_status(BtHidKeynote* bt_hid_keynote, bool connected); diff --git a/applications/bt/bt_hid_app/views/bt_hid_media.c b/applications/bt/bt_hid_app/views/bt_hid_media.c deleted file mode 100755 index b384f47cf18..00000000000 --- a/applications/bt/bt_hid_app/views/bt_hid_media.c +++ /dev/null @@ -1,189 +0,0 @@ -#include "bt_hid_media.h" -#include -#include -#include -#include - -struct BtHidMedia { - View* view; -}; - -typedef struct { - bool left_pressed; - bool up_pressed; - bool right_pressed; - bool down_pressed; - bool ok_pressed; - bool connected; -} BtHidMediaModel; - -static void bt_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 bt_hid_media_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - BtHidMediaModel* 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, "Media"); - canvas_set_font(canvas, FontSecondary); - - // Keypad circles - canvas_draw_icon(canvas, 76, 8, &I_Circles_47x47); - - // Up - if(model->up_pressed) { - canvas_draw_icon(canvas, 93, 9, &I_Pressed_Button_13x13); - 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_draw_icon(canvas, 93, 41, &I_Pressed_Button_13x13); - 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_draw_icon(canvas, 77, 25, &I_Pressed_Button_13x13); - canvas_set_color(canvas, ColorWhite); - } - bt_hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft); - bt_hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft); - canvas_set_color(canvas, ColorBlack); - - // Right - if(model->right_pressed) { - canvas_draw_icon(canvas, 109, 25, &I_Pressed_Button_13x13); - canvas_set_color(canvas, ColorWhite); - } - bt_hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight); - bt_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); - } - bt_hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight); - canvas_draw_line(canvas, 100, 29, 100, 33); - canvas_draw_line(canvas, 102, 29, 102, 33); -} - -static void bt_hid_media_process_press(BtHidMedia* bt_hid_media, InputEvent* event) { - with_view_model( - bt_hid_media->view, (BtHidMediaModel * model) { - if(event->key == InputKeyUp) { - model->up_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_INCREMENT); - } else if(event->key == InputKeyDown) { - model->down_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_DECREMENT); - } else if(event->key == InputKeyLeft) { - model->left_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_SCAN_PREVIOUS_TRACK); - } else if(event->key == InputKeyRight) { - model->right_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_SCAN_NEXT_TRACK); - } else if(event->key == InputKeyOk) { - model->ok_pressed = true; - furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_PLAY_PAUSE); - } - return true; - }); -} - -static void bt_hid_media_process_release(BtHidMedia* bt_hid_media, InputEvent* event) { - with_view_model( - bt_hid_media->view, (BtHidMediaModel * model) { - if(event->key == InputKeyUp) { - model->up_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_INCREMENT); - } else if(event->key == InputKeyDown) { - model->down_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_DECREMENT); - } else if(event->key == InputKeyLeft) { - model->left_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_SCAN_PREVIOUS_TRACK); - } else if(event->key == InputKeyRight) { - model->right_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_SCAN_NEXT_TRACK); - } else if(event->key == InputKeyOk) { - model->ok_pressed = false; - furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_PLAY_PAUSE); - } - return true; - }); -} - -static bool bt_hid_media_input_callback(InputEvent* event, void* context) { - furi_assert(context); - BtHidMedia* bt_hid_media = context; - bool consumed = false; - - if(event->type == InputTypePress) { - bt_hid_media_process_press(bt_hid_media, event); - consumed = true; - } else if(event->type == InputTypeRelease) { - bt_hid_media_process_release(bt_hid_media, event); - consumed = true; - } else if(event->type == InputTypeShort) { - if(event->key == InputKeyBack) { - furi_hal_bt_hid_consumer_key_release_all(); - } - } - - return consumed; -} - -BtHidMedia* bt_hid_media_alloc() { - BtHidMedia* bt_hid_media = malloc(sizeof(BtHidMedia)); - bt_hid_media->view = view_alloc(); - view_set_context(bt_hid_media->view, bt_hid_media); - view_allocate_model(bt_hid_media->view, ViewModelTypeLocking, sizeof(BtHidMediaModel)); - view_set_draw_callback(bt_hid_media->view, bt_hid_media_draw_callback); - view_set_input_callback(bt_hid_media->view, bt_hid_media_input_callback); - - return bt_hid_media; -} - -void bt_hid_media_free(BtHidMedia* bt_hid_media) { - furi_assert(bt_hid_media); - view_free(bt_hid_media->view); - free(bt_hid_media); -} - -View* bt_hid_media_get_view(BtHidMedia* bt_hid_media) { - furi_assert(bt_hid_media); - return bt_hid_media->view; -} - -void bt_hid_media_set_connected_status(BtHidMedia* bt_hid_media, bool connected) { - furi_assert(bt_hid_media); - with_view_model( - bt_hid_media->view, (BtHidMediaModel * model) { - model->connected = connected; - return true; - }); -} diff --git a/applications/bt/bt_hid_app/views/bt_hid_media.h b/applications/bt/bt_hid_app/views/bt_hid_media.h deleted file mode 100644 index 804239dce9b..00000000000 --- a/applications/bt/bt_hid_app/views/bt_hid_media.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct BtHidMedia BtHidMedia; - -BtHidMedia* bt_hid_media_alloc(); - -void bt_hid_media_free(BtHidMedia* bt_hid_media); - -View* bt_hid_media_get_view(BtHidMedia* bt_hid_media); - -void bt_hid_media_set_connected_status(BtHidMedia* bt_hid_media, bool connected); diff --git a/applications/bt/bt_hid_app/views/bt_hid_mouse.c b/applications/bt/bt_hid_app/views/bt_hid_mouse.c deleted file mode 100644 index fb1537a2c9d..00000000000 --- a/applications/bt/bt_hid_app/views/bt_hid_mouse.c +++ /dev/null @@ -1,207 +0,0 @@ -#include "bt_hid_mouse.h" -#include -#include -#include -#include - -struct BtHidMouse { - 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; -} BtHidMouseModel; - -static void bt_hid_mouse_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - BtHidMouseModel* 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, 60, AlignLeft, AlignBottom, "Selecting..."); - } - - // Keypad circles - canvas_draw_icon(canvas, 64, 8, &I_Circles_47x47); - - // Up - if(model->up_pressed) { - canvas_draw_icon(canvas, 81, 9, &I_Pressed_Button_13x13); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up7x9); - canvas_set_color(canvas, ColorBlack); - - // Down - if(model->down_pressed) { - canvas_draw_icon(canvas, 81, 41, &I_Pressed_Button_13x13); - 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_draw_icon(canvas, 65, 25, &I_Pressed_Button_13x13); - 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_draw_icon(canvas, 97, 25, &I_Pressed_Button_13x13); - 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_Pressed_Button_13x13); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 83, 27, &I_Ok_btn_9x9); - canvas_set_color(canvas, ColorBlack); - - // Back - if(model->right_mouse_pressed) { - canvas_draw_icon(canvas, 108, 48, &I_Pressed_Button_13x13); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 110, 50, &I_Ok_btn_9x9); -} - -static void bt_hid_mouse_process(BtHidMouse* bt_hid_mouse, InputEvent* event) { - with_view_model( - bt_hid_mouse->view, (BtHidMouseModel * model) { - if(event->key == InputKeyBack) { - if(event->type == InputTypeShort) { - furi_hal_bt_hid_mouse_press(HID_MOUSE_BTN_RIGHT); - furi_hal_bt_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_bt_hid_mouse_press(HID_MOUSE_BTN_LEFT); - furi_hal_bt_hid_mouse_release(HID_MOUSE_BTN_LEFT); - model->left_mouse_held = false; - } else if(event->type == InputTypeLong) { - furi_hal_bt_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_bt_hid_mouse_move(MOUSE_MOVE_SHORT, 0); - } else if(event->type == InputTypeRepeat) { - furi_hal_bt_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_bt_hid_mouse_move(-MOUSE_MOVE_SHORT, 0); - } else if(event->type == InputTypeRepeat) { - furi_hal_bt_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_bt_hid_mouse_move(0, MOUSE_MOVE_SHORT); - } else if(event->type == InputTypeRepeat) { - furi_hal_bt_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_bt_hid_mouse_move(0, -MOUSE_MOVE_SHORT); - } else if(event->type == InputTypeRepeat) { - furi_hal_bt_hid_mouse_move(0, -MOUSE_MOVE_LONG); - } else if(event->type == InputTypeRelease) { - model->up_pressed = false; - } - } - return true; - }); -} - -static bool bt_hid_mouse_input_callback(InputEvent* event, void* context) { - furi_assert(context); - BtHidMouse* bt_hid_mouse = context; - bool consumed = false; - - if(event->type == InputTypeLong && event->key == InputKeyBack) { - furi_hal_bt_hid_mouse_release_all(); - } else { - bt_hid_mouse_process(bt_hid_mouse, event); - consumed = true; - } - - return consumed; -} - -BtHidMouse* bt_hid_mouse_alloc() { - BtHidMouse* bt_hid_mouse = malloc(sizeof(BtHidMouse)); - bt_hid_mouse->view = view_alloc(); - view_set_context(bt_hid_mouse->view, bt_hid_mouse); - view_allocate_model(bt_hid_mouse->view, ViewModelTypeLocking, sizeof(BtHidMouseModel)); - view_set_draw_callback(bt_hid_mouse->view, bt_hid_mouse_draw_callback); - view_set_input_callback(bt_hid_mouse->view, bt_hid_mouse_input_callback); - - return bt_hid_mouse; -} - -void bt_hid_mouse_free(BtHidMouse* bt_hid_mouse) { - furi_assert(bt_hid_mouse); - view_free(bt_hid_mouse->view); - free(bt_hid_mouse); -} - -View* bt_hid_mouse_get_view(BtHidMouse* bt_hid_mouse) { - furi_assert(bt_hid_mouse); - return bt_hid_mouse->view; -} - -void bt_hid_mouse_set_connected_status(BtHidMouse* bt_hid_mouse, bool connected) { - furi_assert(bt_hid_mouse); - with_view_model( - bt_hid_mouse->view, (BtHidMouseModel * model) { - model->connected = connected; - return true; - }); -} diff --git a/applications/bt/bt_hid_app/views/bt_hid_mouse.h b/applications/bt/bt_hid_app/views/bt_hid_mouse.h deleted file mode 100644 index a82971d7689..00000000000 --- a/applications/bt/bt_hid_app/views/bt_hid_mouse.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct BtHidMouse BtHidMouse; - -BtHidMouse* bt_hid_mouse_alloc(); - -void bt_hid_mouse_free(BtHidMouse* bt_hid_mouse); - -View* bt_hid_mouse_get_view(BtHidMouse* bt_hid_mouse); - -void bt_hid_mouse_set_connected_status(BtHidMouse* bt_hid_mouse, bool connected); diff --git a/applications/bt/bt_service/bt_api.c b/applications/bt/bt_service/bt_api.c deleted file mode 100644 index 3de896d5a3c..00000000000 --- a/applications/bt/bt_service/bt_api.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "bt_i.h" - -bool bt_set_profile(Bt* bt, BtProfile profile) { - furi_assert(bt); - - // Send message - bool result = false; - BtMessage message = { - .type = BtMessageTypeSetProfile, .data.profile = profile, .result = &result}; - furi_check( - furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); - // Wait for unlock - furi_event_flag_wait(bt->api_event, BT_API_UNLOCK_EVENT, FuriFlagWaitAny, FuriWaitForever); - - return result; -} - -void bt_disconnect(Bt* bt) { - furi_assert(bt); - - // Send message - BtMessage message = {.type = BtMessageTypeDisconnect}; - furi_check( - furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); - // Wait for unlock - furi_event_flag_wait(bt->api_event, BT_API_UNLOCK_EVENT, FuriFlagWaitAny, FuriWaitForever); -} - -void bt_set_status_changed_callback(Bt* bt, BtStatusChangedCallback callback, void* context) { - furi_assert(bt); - - bt->status_changed_cb = callback; - bt->status_changed_ctx = context; -} - -void bt_forget_bonded_devices(Bt* bt) { - furi_assert(bt); - BtMessage message = {.type = BtMessageTypeForgetBondedDevices}; - furi_check( - furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); -} diff --git a/applications/bt/bt_service/bt_i.h b/applications/bt/bt_service/bt_i.h deleted file mode 100644 index a45a36c9eaa..00000000000 --- a/applications/bt/bt_service/bt_i.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -#include "bt.h" - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include "../bt_settings.h" - -#define BT_API_UNLOCK_EVENT (1UL << 0) - -typedef enum { - BtMessageTypeUpdateStatus, - BtMessageTypeUpdateBatteryLevel, - BtMessageTypeUpdatePowerState, - BtMessageTypePinCodeShow, - BtMessageTypeKeysStorageUpdated, - BtMessageTypeSetProfile, - BtMessageTypeDisconnect, - BtMessageTypeForgetBondedDevices, -} BtMessageType; - -typedef union { - uint32_t pin_code; - uint8_t battery_level; - BtProfile profile; -} BtMessageData; - -typedef struct { - BtMessageType type; - BtMessageData data; - bool* result; -} BtMessage; - -struct Bt { - uint8_t* bt_keys_addr_start; - uint16_t bt_keys_size; - uint16_t max_packet_size; - BtSettings bt_settings; - BtStatus status; - BtProfile profile; - FuriMessageQueue* message_queue; - NotificationApp* notification; - Gui* gui; - ViewPort* statusbar_view_port; - ViewPort* pin_code_view_port; - uint32_t pin_code; - DialogsApp* dialogs; - DialogMessage* dialog_message; - Power* power; - Rpc* rpc; - RpcSession* rpc_session; - FuriEventFlag* rpc_event; - FuriEventFlag* api_event; - BtStatusChangedCallback status_changed_cb; - void* status_changed_ctx; -}; diff --git a/applications/bt/bt_service/bt_keys_storage.c b/applications/bt/bt_service/bt_keys_storage.c deleted file mode 100644 index 91d97d67e98..00000000000 --- a/applications/bt/bt_service/bt_keys_storage.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "bt_keys_storage.h" - -#include -#include -#include - -#define BT_KEYS_STORAGE_PATH INT_PATH(BT_KEYS_STORAGE_FILE_NAME) -#define BT_KEYS_STORAGE_VERSION (0) -#define BT_KEYS_STORAGE_MAGIC (0x18) - -bool bt_keys_storage_load(Bt* bt) { - furi_assert(bt); - bool file_loaded = false; - - furi_hal_bt_get_key_storage_buff(&bt->bt_keys_addr_start, &bt->bt_keys_size); - furi_hal_bt_nvm_sram_sem_acquire(); - file_loaded = saved_struct_load( - BT_KEYS_STORAGE_PATH, - bt->bt_keys_addr_start, - bt->bt_keys_size, - BT_KEYS_STORAGE_MAGIC, - BT_KEYS_STORAGE_VERSION); - furi_hal_bt_nvm_sram_sem_release(); - - return file_loaded; -} - -bool bt_keys_storage_save(Bt* bt) { - furi_assert(bt); - furi_assert(bt->bt_keys_addr_start); - bool file_saved = false; - - furi_hal_bt_nvm_sram_sem_acquire(); - file_saved = saved_struct_save( - BT_KEYS_STORAGE_PATH, - bt->bt_keys_addr_start, - bt->bt_keys_size, - BT_KEYS_STORAGE_MAGIC, - BT_KEYS_STORAGE_VERSION); - furi_hal_bt_nvm_sram_sem_release(); - - return file_saved; -} - -bool bt_keys_storage_delete(Bt* bt) { - furi_assert(bt); - bool delete_succeed = false; - bool bt_is_active = furi_hal_bt_is_active(); - - furi_hal_bt_stop_advertising(); - delete_succeed = furi_hal_bt_clear_white_list(); - if(bt_is_active) { - furi_hal_bt_start_advertising(); - } - - return delete_succeed; -} diff --git a/applications/bt/bt_service/bt_keys_storage.h b/applications/bt/bt_service/bt_keys_storage.h deleted file mode 100644 index b82b1035f5c..00000000000 --- a/applications/bt/bt_service/bt_keys_storage.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include "bt_i.h" -#include "bt_keys_filename.h" - -bool bt_keys_storage_load(Bt* bt); - -bool bt_keys_storage_save(Bt* bt); - -bool bt_keys_storage_delete(Bt* bt); diff --git a/applications/cli/application.fam b/applications/cli/application.fam deleted file mode 100644 index 73c70aa44ed..00000000000 --- a/applications/cli/application.fam +++ /dev/null @@ -1,9 +0,0 @@ -App( - appid="cli", - name="CliSrv", - apptype=FlipperAppType.SERVICE, - entry_point="cli_srv", - cdefines=["SRV_CLI"], - stack_size=4 * 1024, - order=30, -) diff --git a/applications/cli/cli_command_gpio.c b/applications/cli/cli_command_gpio.c deleted file mode 100644 index d5ec8d65440..00000000000 --- a/applications/cli/cli_command_gpio.c +++ /dev/null @@ -1,228 +0,0 @@ -#include "cli_command_gpio.h" - -#include -#include -#include - -typedef struct { - const GpioPin* pin; - const char* name; - const bool debug; -} CliCommandGpio; - -const CliCommandGpio cli_command_gpio_pins[] = { - {.pin = &gpio_ext_pc0, .name = "PC0", .debug = false}, - {.pin = &gpio_ext_pc1, .name = "PC1", .debug = false}, - {.pin = &gpio_ext_pc3, .name = "PC3", .debug = false}, - {.pin = &gpio_ext_pb2, .name = "PB2", .debug = false}, - {.pin = &gpio_ext_pb3, .name = "PB3", .debug = false}, - {.pin = &gpio_ext_pa4, .name = "PA4", .debug = false}, - {.pin = &gpio_ext_pa6, .name = "PA6", .debug = false}, - {.pin = &gpio_ext_pa7, .name = "PA7", .debug = false}, - /* Dangerous pins, may damage hardware */ - {.pin = &gpio_infrared_rx, .name = "PA0", .debug = true}, - {.pin = &gpio_usart_rx, .name = "PB7", .debug = true}, - {.pin = &gpio_speaker, .name = "PB8", .debug = true}, - {.pin = &gpio_infrared_tx, .name = "PB9", .debug = true}, -}; - -void cli_command_gpio_print_usage() { - printf("Usage:\r\n"); - printf("gpio \r\n"); - printf("Cmd list:\r\n"); - printf("\tmode <0|1>\t - Set gpio mode: 0 - input, 1 - output\r\n"); - printf("\tset <0|1>\t - Set gpio value\r\n"); - printf("\tread \t - Read gpio value\r\n"); -} - -static bool pin_name_to_int(string_t pin_name, size_t* result) { - bool found = false; - bool debug = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); - for(size_t i = 0; i < COUNT_OF(cli_command_gpio_pins); i++) { - if(!string_cmp(pin_name, cli_command_gpio_pins[i].name)) { - if(!cli_command_gpio_pins[i].debug || (cli_command_gpio_pins[i].debug && debug)) { - *result = i; - found = true; - break; - } - } - } - - return found; -} - -static void gpio_print_pins(void) { - printf("Wrong pin name. Available pins: "); - bool debug = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); - for(size_t i = 0; i < COUNT_OF(cli_command_gpio_pins); i++) { - if(!cli_command_gpio_pins[i].debug || (cli_command_gpio_pins[i].debug && debug)) { - printf("%s ", cli_command_gpio_pins[i].name); - } - } -} - -typedef enum { OK, ERR_CMD_SYNTAX, ERR_PIN, ERR_VALUE } GpioParseError; - -static GpioParseError gpio_command_parse(string_t args, size_t* pin_num, uint8_t* value) { - string_t pin_name; - string_init(pin_name); - - size_t ws = string_search_char(args, ' '); - if(ws == STRING_FAILURE) { - return ERR_CMD_SYNTAX; - } - - string_set_n(pin_name, args, 0, ws); - string_right(args, ws); - string_strim(args); - - if(!pin_name_to_int(pin_name, pin_num)) { - string_clear(pin_name); - return ERR_PIN; - } - - string_clear(pin_name); - - if(!string_cmp(args, "0")) { - *value = 0; - } else if(!string_cmp(args, "1")) { - *value = 1; - } else { - return ERR_VALUE; - } - - return OK; -} - -void cli_command_gpio_mode(Cli* cli, string_t args, void* context) { - UNUSED(cli); - UNUSED(context); - - size_t num = 0; - uint8_t value = 255; - - GpioParseError err = gpio_command_parse(args, &num, &value); - - if(ERR_CMD_SYNTAX == err) { - cli_print_usage("gpio mode", " <0|1>", string_get_cstr(args)); - return; - } else if(ERR_PIN == err) { - gpio_print_pins(); - return; - } else if(ERR_VALUE == err) { - printf("Value is invalid. Enter 1 for input or 0 for output"); - return; - } - - if(cli_command_gpio_pins[num].debug) { - printf( - "Changeing this pin mode may damage hardware. Are you sure you want to continue? (y/n)?\r\n"); - char c = cli_getc(cli); - if(c != 'y' && c != 'Y') { - printf("Cancelled.\r\n"); - return; - } - } - - if(value == 1) { // output - furi_hal_gpio_write(cli_command_gpio_pins[num].pin, false); - furi_hal_gpio_init_simple(cli_command_gpio_pins[num].pin, GpioModeOutputPushPull); - printf("Pin %s is now an output (low)", cli_command_gpio_pins[num].name); - } else { // input - furi_hal_gpio_init_simple(cli_command_gpio_pins[num].pin, GpioModeInput); - printf("Pin %s is now an input", cli_command_gpio_pins[num].name); - } -} - -void cli_command_gpio_read(Cli* cli, string_t args, void* context) { - UNUSED(cli); - UNUSED(context); - - size_t num = 0; - if(!pin_name_to_int(args, &num)) { - gpio_print_pins(); - return; - } - - if(LL_GPIO_MODE_INPUT != - LL_GPIO_GetPinMode( - cli_command_gpio_pins[num].pin->port, cli_command_gpio_pins[num].pin->pin)) { - printf("Err: pin %s is not set as an input.", cli_command_gpio_pins[num].name); - return; - } - - uint8_t val = !!furi_hal_gpio_read(cli_command_gpio_pins[num].pin); - - printf("Pin %s <= %u", cli_command_gpio_pins[num].name, val); -} - -void cli_command_gpio_set(Cli* cli, string_t args, void* context) { - UNUSED(context); - - size_t num = 0; - uint8_t value = 0; - GpioParseError err = gpio_command_parse(args, &num, &value); - - if(ERR_CMD_SYNTAX == err) { - cli_print_usage("gpio set", " <0|1>", string_get_cstr(args)); - return; - } else if(ERR_PIN == err) { - gpio_print_pins(); - return; - } else if(ERR_VALUE == err) { - printf("Value is invalid. Enter 1 for high or 0 for low"); - return; - } - - if(LL_GPIO_MODE_OUTPUT != - LL_GPIO_GetPinMode( - cli_command_gpio_pins[num].pin->port, cli_command_gpio_pins[num].pin->pin)) { - printf("Err: pin %s is not set as an output.", cli_command_gpio_pins[num].name); - return; - } - - // Extra check if debug pins used - if(cli_command_gpio_pins[num].debug) { - printf( - "Setting this pin may damage hardware. Are you sure you want to continue? (y/n)?\r\n"); - char c = cli_getc(cli); - if(c != 'y' && c != 'Y') { - printf("Cancelled.\r\n"); - return; - } - } - - furi_hal_gpio_write(cli_command_gpio_pins[num].pin, !!value); - printf("Pin %s => %u", cli_command_gpio_pins[num].name, !!value); -} - -void cli_command_gpio(Cli* cli, string_t args, void* context) { - string_t cmd; - string_init(cmd); - - do { - if(!args_read_string_and_trim(args, cmd)) { - cli_command_gpio_print_usage(); - break; - } - - if(string_cmp_str(cmd, "mode") == 0) { - cli_command_gpio_mode(cli, args, context); - break; - } - - if(string_cmp_str(cmd, "set") == 0) { - cli_command_gpio_set(cli, args, context); - break; - } - - if(string_cmp_str(cmd, "read") == 0) { - cli_command_gpio_read(cli, args, context); - break; - } - - cli_command_gpio_print_usage(); - } while(false); - - string_clear(cmd); -} diff --git a/applications/cli/cli_command_gpio.h b/applications/cli/cli_command_gpio.h deleted file mode 100644 index c9b908a0853..00000000000 --- a/applications/cli/cli_command_gpio.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "cli_i.h" - -void cli_command_gpio(Cli* cli, string_t args, void* context); diff --git a/applications/cli/cli_commands.c b/applications/cli/cli_commands.c deleted file mode 100644 index 177a274a106..00000000000 --- a/applications/cli/cli_commands.c +++ /dev/null @@ -1,335 +0,0 @@ -#include "cli_commands.h" -#include "cli_command_gpio.h" - -#include -#include -#include -#include -#include -#include -#include - -// 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); - UNUSED(last); - printf("%-30s: %s\r\n", key, value); -} - -/* - * Device Info Command - * This command is intended to be used by humans - */ -void cli_command_device_info(Cli* cli, string_t args, void* context) { - UNUSED(cli); - UNUSED(args); - furi_hal_info_get(cli_command_device_info_callback, context); -} - -void cli_command_help(Cli* cli, string_t args, void* context) { - UNUSED(args); - UNUSED(context); - printf("Commands we have:"); - - // Command count - const size_t commands_count = CliCommandTree_size(cli->commands); - const size_t commands_count_mid = commands_count / 2 + commands_count % 2; - - // Use 2 iterators from start and middle to show 2 columns - CliCommandTree_it_t it_left; - CliCommandTree_it(it_left, cli->commands); - CliCommandTree_it_t it_right; - CliCommandTree_it(it_right, cli->commands); - for(size_t i = 0; i < commands_count_mid; i++) CliCommandTree_next(it_right); - - // Iterate throw tree - for(size_t i = 0; i < commands_count_mid; i++) { - printf("\r\n"); - // Left Column - if(!CliCommandTree_end_p(it_left)) { - printf("%-30s", string_get_cstr(*CliCommandTree_ref(it_left)->key_ptr)); - CliCommandTree_next(it_left); - } - // Right Column - if(!CliCommandTree_end_p(it_right)) { - printf("%s", string_get_cstr(*CliCommandTree_ref(it_right)->key_ptr)); - CliCommandTree_next(it_right); - } - }; - - if(string_size(args) > 0) { - cli_nl(); - printf("Also I have no clue what '"); - printf("%s", string_get_cstr(args)); - printf("' is."); - } -} - -void cli_command_date(Cli* cli, string_t args, void* context) { - UNUSED(cli); - UNUSED(context); - - FuriHalRtcDateTime datetime = {0}; - - if(string_size(args) > 0) { - uint16_t hours, minutes, seconds, month, day, year, weekday; - int ret = sscanf( - string_get_cstr(args), - "%hu-%hu-%hu %hu:%hu:%hu %hu", - &year, - &month, - &day, - &hours, - &minutes, - &seconds, - &weekday); - - // Some variables are going to discard upper byte - // There will be some funky behaviour which is not breaking anything - datetime.hour = hours; - datetime.minute = minutes; - datetime.second = seconds; - datetime.weekday = weekday; - datetime.month = month; - datetime.day = day; - datetime.year = year; - - if(ret != 7) { - printf( - "Invalid datetime format, use `%s`. sscanf %d %s", - "%Y-%m-%d %H:%M:%S %u", - ret, - string_get_cstr(args)); - return; - } - - if(!furi_hal_rtc_validate_datetime(&datetime)) { - printf("Invalid datetime data"); - return; - } - - furi_hal_rtc_set_datetime(&datetime); - // Verification - furi_hal_rtc_get_datetime(&datetime); - printf( - "New datetime is: " CLI_DATE_FORMAT, - datetime.year, - datetime.month, - datetime.day, - datetime.hour, - datetime.minute, - datetime.second, - datetime.weekday); - } else { - furi_hal_rtc_get_datetime(&datetime); - printf( - CLI_DATE_FORMAT, - datetime.year, - datetime.month, - datetime.day, - datetime.hour, - datetime.minute, - datetime.second, - datetime.weekday); - } -} - -#define CLI_COMMAND_LOG_RING_SIZE 2048 -#define CLI_COMMAND_LOG_BUFFER_SIZE 64 - -void cli_command_log_tx_callback(const uint8_t* buffer, size_t size, void* context) { - xStreamBufferSend(context, buffer, size, 0); -} - -void cli_command_log(Cli* cli, string_t args, void* context) { - UNUSED(args); - UNUSED(context); - StreamBufferHandle_t ring = xStreamBufferCreate(CLI_COMMAND_LOG_RING_SIZE, 1); - uint8_t buffer[CLI_COMMAND_LOG_BUFFER_SIZE]; - - furi_hal_console_set_tx_callback(cli_command_log_tx_callback, ring); - - printf("Press CTRL+C to stop...\r\n"); - while(!cli_cmd_interrupt_received(cli)) { - size_t ret = xStreamBufferReceive(ring, buffer, CLI_COMMAND_LOG_BUFFER_SIZE, 50); - cli_write(cli, buffer, ret); - } - - furi_hal_console_set_tx_callback(NULL, NULL); - - vStreamBufferDelete(ring); -} - -void cli_command_vibro(Cli* cli, string_t args, void* context) { - UNUSED(cli); - UNUSED(context); - if(!string_cmp(args, "0")) { - NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); - notification_message_block(notification, &sequence_reset_vibro); - furi_record_close(RECORD_NOTIFICATION); - } else if(!string_cmp(args, "1")) { - NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); - notification_message_block(notification, &sequence_set_vibro_on); - furi_record_close(RECORD_NOTIFICATION); - } else { - cli_print_usage("vibro", "<1|0>", string_get_cstr(args)); - } -} - -void cli_command_debug(Cli* cli, string_t args, void* context) { - UNUSED(cli); - UNUSED(context); - if(!string_cmp(args, "0")) { - furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); - loader_update_menu(); - printf("Debug disabled."); - } else if(!string_cmp(args, "1")) { - furi_hal_rtc_set_flag(FuriHalRtcFlagDebug); - loader_update_menu(); - printf("Debug enabled."); - } else { - cli_print_usage("debug", "<1|0>", string_get_cstr(args)); - } -} - -void cli_command_led(Cli* cli, string_t args, void* context) { - UNUSED(cli); - UNUSED(context); - // Get first word as light name - NotificationMessage notification_led_message; - string_t light_name; - string_init(light_name); - size_t ws = string_search_char(args, ' '); - if(ws == STRING_FAILURE) { - cli_print_usage("led", " <0-255>", string_get_cstr(args)); - string_clear(light_name); - return; - } else { - string_set_n(light_name, args, 0, ws); - string_right(args, ws); - string_strim(args); - } - // Check light name - if(!string_cmp(light_name, "r")) { - notification_led_message.type = NotificationMessageTypeLedRed; - } else if(!string_cmp(light_name, "g")) { - notification_led_message.type = NotificationMessageTypeLedGreen; - } else if(!string_cmp(light_name, "b")) { - notification_led_message.type = NotificationMessageTypeLedBlue; - } else if(!string_cmp(light_name, "bl")) { - notification_led_message.type = NotificationMessageTypeLedDisplayBacklight; - } else { - cli_print_usage("led", " <0-255>", string_get_cstr(args)); - string_clear(light_name); - return; - } - string_clear(light_name); - // Read light value from the rest of the string - char* end_ptr; - uint32_t value = strtoul(string_get_cstr(args), &end_ptr, 0); - if(!(value < 256 && *end_ptr == '\0')) { - cli_print_usage("led", " <0-255>", string_get_cstr(args)); - return; - } - - // Set led value - notification_led_message.data.led.value = value; - - // Form notification sequence - const NotificationSequence notification_sequence = { - ¬ification_led_message, - NULL, - }; - - // Send notification - NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); - notification_internal_message_block(notification, ¬ification_sequence); - furi_record_close(RECORD_NOTIFICATION); -} - -void cli_command_ps(Cli* cli, string_t args, void* context) { - UNUSED(cli); - UNUSED(args); - UNUSED(context); - - const uint8_t threads_num_max = 32; - 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"); - for(uint8_t i = 0; i < thread_num; i++) { - TaskControlBlock* tcb = (TaskControlBlock*)threads_ids[i]; - printf( - "%-20s 0x%-12lx %-8d %-8ld %-8ld\r\n", - furi_thread_get_name(threads_ids[i]), - (uint32_t)tcb->pxStack, - memmgr_heap_get_thread_memory(threads_ids[i]), - (uint32_t)(tcb->pxEndOfStack - tcb->pxStack + 1) * sizeof(StackType_t), - furi_thread_get_stack_space(threads_ids[i])); - } - printf("\r\nTotal: %d", thread_num); -} - -void cli_command_free(Cli* cli, string_t args, void* context) { - UNUSED(cli); - UNUSED(args); - UNUSED(context); - - printf("Free heap size: %d\r\n", memmgr_get_free_heap()); - printf("Total heap size: %d\r\n", memmgr_get_total_heap()); - printf("Minimum heap size: %d\r\n", memmgr_get_minimum_free_heap()); - printf("Maximum heap block: %d\r\n", memmgr_heap_get_max_free_block()); -} - -void cli_command_free_blocks(Cli* cli, string_t args, void* context) { - UNUSED(cli); - UNUSED(args); - UNUSED(context); - - memmgr_heap_printf_free_blocks(); -} - -void cli_command_i2c(Cli* cli, string_t args, void* context) { - UNUSED(cli); - UNUSED(args); - UNUSED(context); - - furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); - printf("Scanning external i2c on PC0(SCL)/PC1(SDA)\r\n" - "Clock: 100khz, 7bit address\r\n" - "\r\n"); - printf(" | 0 1 2 3 4 5 6 7 8 9 A B C D E F\r\n"); - printf("--+--------------------------------\r\n"); - for(uint8_t row = 0; row < 0x8; row++) { - printf("%x | ", row); - for(uint8_t column = 0; column <= 0xF; column++) { - bool ret = furi_hal_i2c_is_device_ready( - &furi_hal_i2c_handle_external, ((row << 4) + column) << 1, 2); - printf("%c ", ret ? '#' : '-'); - } - printf("\r\n"); - } - furi_hal_i2c_release(&furi_hal_i2c_handle_external); -} - -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_help, NULL); - cli_add_command(cli, "help", CliCommandFlagParallelSafe, cli_command_help, NULL); - - cli_add_command(cli, "date", CliCommandFlagParallelSafe, cli_command_date, NULL); - cli_add_command(cli, "log", CliCommandFlagParallelSafe, cli_command_log, NULL); - cli_add_command(cli, "debug", CliCommandFlagDefault, cli_command_debug, NULL); - cli_add_command(cli, "ps", CliCommandFlagParallelSafe, cli_command_ps, NULL); - cli_add_command(cli, "free", CliCommandFlagParallelSafe, cli_command_free, NULL); - cli_add_command(cli, "free_blocks", CliCommandFlagParallelSafe, cli_command_free_blocks, NULL); - - cli_add_command(cli, "vibro", CliCommandFlagDefault, cli_command_vibro, NULL); - cli_add_command(cli, "led", CliCommandFlagDefault, cli_command_led, NULL); - cli_add_command(cli, "gpio", CliCommandFlagDefault, cli_command_gpio, NULL); - cli_add_command(cli, "i2c", CliCommandFlagDefault, cli_command_i2c, NULL); -} diff --git a/applications/cli/cli_vcp.c b/applications/cli/cli_vcp.c deleted file mode 100644 index 5a8b44dcf30..00000000000 --- a/applications/cli/cli_vcp.c +++ /dev/null @@ -1,326 +0,0 @@ -#include -#include -#include -#include -#include "cli_i.h" - -#define TAG "CliVcp" - -#define USB_CDC_PKT_LEN CDC_DATA_SZ -#define VCP_RX_BUF_SIZE (USB_CDC_PKT_LEN * 3) -#define VCP_TX_BUF_SIZE (USB_CDC_PKT_LEN * 3) - -#define VCP_IF_NUM 0 - -typedef enum { - VcpEvtStop = (1 << 0), - VcpEvtConnect = (1 << 1), - VcpEvtDisconnect = (1 << 2), - VcpEvtStreamRx = (1 << 3), - VcpEvtRx = (1 << 4), - VcpEvtStreamTx = (1 << 5), - VcpEvtTx = (1 << 6), -} WorkerEvtFlags; - -#define VCP_THREAD_FLAG_ALL \ - (VcpEvtStop | VcpEvtConnect | VcpEvtDisconnect | VcpEvtRx | VcpEvtTx | VcpEvtStreamRx | \ - VcpEvtStreamTx) - -typedef struct { - FuriThread* thread; - - StreamBufferHandle_t tx_stream; - StreamBufferHandle_t rx_stream; - - volatile bool connected; - volatile bool running; - - FuriHalUsbInterface* usb_if_prev; - - uint8_t data_buffer[USB_CDC_PKT_LEN]; -} CliVcp; - -static int32_t vcp_worker(void* context); -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 CdcCallbacks cdc_cb = { - vcp_on_cdc_tx_complete, - vcp_on_cdc_rx, - vcp_state_callback, - vcp_on_cdc_control_line, - NULL, -}; - -static CliVcp* vcp = NULL; - -static const uint8_t ascii_soh = 0x01; -static const uint8_t ascii_eot = 0x04; - -static void cli_vcp_init() { - if(vcp == NULL) { - vcp = malloc(sizeof(CliVcp)); - vcp->tx_stream = xStreamBufferCreate(VCP_TX_BUF_SIZE, 1); - vcp->rx_stream = xStreamBufferCreate(VCP_RX_BUF_SIZE, 1); - } - furi_assert(vcp->thread == NULL); - - vcp->connected = false; - - vcp->thread = furi_thread_alloc(); - furi_thread_set_name(vcp->thread, "CliVcpWorker"); - furi_thread_set_stack_size(vcp->thread, 1024); - furi_thread_set_callback(vcp->thread, vcp_worker); - furi_thread_start(vcp->thread); - - FURI_LOG_I(TAG, "Init OK"); -} - -static void cli_vcp_deinit() { - furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtStop); - furi_thread_join(vcp->thread); - furi_thread_free(vcp->thread); - vcp->thread = NULL; -} - -static int32_t vcp_worker(void* context) { - UNUSED(context); - bool tx_idle = true; - size_t missed_rx = 0; - uint8_t last_tx_pkt_len = 0; - - // Switch USB to VCP mode (if it is not set yet) - vcp->usb_if_prev = furi_hal_usb_get_config(); - if((vcp->usb_if_prev != &usb_cdc_single) && (vcp->usb_if_prev != &usb_cdc_dual)) { - furi_hal_usb_set_config(&usb_cdc_single, NULL); - } - furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL); - - FURI_LOG_D(TAG, "Start"); - vcp->running = true; - - while(1) { - uint32_t flags = - furi_thread_flags_wait(VCP_THREAD_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever); - furi_assert((flags & FuriFlagError) == 0); - - // VCP session opened - if(flags & VcpEvtConnect) { -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "Connect"); -#endif - if(vcp->connected == false) { - vcp->connected = true; - xStreamBufferSend(vcp->rx_stream, &ascii_soh, 1, FuriWaitForever); - } - } - - // VCP session closed - if(flags & VcpEvtDisconnect) { -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "Disconnect"); -#endif - if(vcp->connected == true) { - vcp->connected = false; - xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); - xStreamBufferSend(vcp->rx_stream, &ascii_eot, 1, FuriWaitForever); - } - } - - // Rx buffer was read, maybe there is enough space for new data? - if((flags & VcpEvtStreamRx) && (missed_rx > 0)) { -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "StreamRx"); -#endif - if(xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) { - flags |= VcpEvtRx; - missed_rx--; - } - } - - // New data received - if(flags & VcpEvtRx) { - if(xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) { - int32_t len = furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN); -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "Rx %d", len); -#endif - if(len > 0) { - furi_check( - xStreamBufferSend(vcp->rx_stream, vcp->data_buffer, len, FuriWaitForever) == - (size_t)len); - } - } else { -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "Rx missed"); -#endif - missed_rx++; - } - } - - // New data in Tx buffer - if(flags & VcpEvtStreamTx) { -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "StreamTx"); -#endif - if(tx_idle) { - flags |= VcpEvtTx; - } - } - - // CDC write transfer done - if(flags & VcpEvtTx) { - size_t len = - xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "Tx %d", len); -#endif - if(len > 0) { // Some data left in Tx buffer. Sending it now - tx_idle = false; - furi_hal_cdc_send(VCP_IF_NUM, vcp->data_buffer, len); - last_tx_pkt_len = len; - } else { // There is nothing to send. - if(last_tx_pkt_len == 64) { - // Send extra zero-length packet if last packet len is 64 to indicate transfer end - furi_hal_cdc_send(VCP_IF_NUM, NULL, 0); - } else { - // Set flag to start next transfer instantly - tx_idle = true; - } - last_tx_pkt_len = 0; - } - } - - if(flags & VcpEvtStop) { - vcp->connected = false; - vcp->running = false; - furi_hal_cdc_set_callbacks(VCP_IF_NUM, NULL, NULL); - // Restore previous USB mode (if it was set during init) - if((vcp->usb_if_prev != &usb_cdc_single) && (vcp->usb_if_prev != &usb_cdc_dual)) { - furi_hal_usb_unlock(); - furi_hal_usb_set_config(vcp->usb_if_prev, NULL); - } - xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); - xStreamBufferSend(vcp->rx_stream, &ascii_eot, 1, FuriWaitForever); - break; - } - } - FURI_LOG_D(TAG, "End"); - return 0; -} - -static size_t cli_vcp_rx(uint8_t* buffer, size_t size, uint32_t timeout) { - furi_assert(vcp); - furi_assert(buffer); - - if(vcp->running == false) { - return 0; - } - -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "rx %u start", size); -#endif - - size_t rx_cnt = 0; - - while(size > 0) { - size_t batch_size = size; - if(batch_size > VCP_RX_BUF_SIZE) batch_size = VCP_RX_BUF_SIZE; - - size_t len = xStreamBufferReceive(vcp->rx_stream, buffer, batch_size, timeout); -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "rx %u ", batch_size); -#endif - if(len == 0) break; - furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtStreamRx); - size -= len; - buffer += len; - rx_cnt += len; - } - -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "rx %u end", size); -#endif - return rx_cnt; -} - -static void cli_vcp_tx(const uint8_t* buffer, size_t size) { - furi_assert(vcp); - furi_assert(buffer); - - if(vcp->running == false) { - return; - } - -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "tx %u start", size); -#endif - - while(size > 0 && vcp->connected) { - size_t batch_size = size; - if(batch_size > USB_CDC_PKT_LEN) batch_size = USB_CDC_PKT_LEN; - - xStreamBufferSend(vcp->tx_stream, buffer, batch_size, FuriWaitForever); - furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtStreamTx); -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "tx %u", batch_size); -#endif - - size -= batch_size; - buffer += batch_size; - } - -#ifdef CLI_VCP_DEBUG - FURI_LOG_D(TAG, "tx %u end", size); -#endif -} - -static void cli_vcp_tx_stdout(const char* data, size_t size) { - cli_vcp_tx((const uint8_t*)data, size); -} - -static void vcp_state_callback(void* context, uint8_t state) { - UNUSED(context); - if(state == 0) { - furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtDisconnect); - } -} - -static void vcp_on_cdc_control_line(void* context, uint8_t state) { - UNUSED(context); - // bit 0: DTR state, bit 1: RTS state - bool dtr = state & (1 << 0); - - if(dtr == true) { - furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtConnect); - } else { - furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtDisconnect); - } -} - -static void vcp_on_cdc_rx(void* context) { - UNUSED(context); - uint32_t ret = furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtRx); - furi_check((ret & FuriFlagError) == 0); -} - -static void vcp_on_cdc_tx_complete(void* context) { - UNUSED(context); - furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtTx); -} - -static bool cli_vcp_is_connected(void) { - furi_assert(vcp); - return vcp->connected; -} - -CliSession cli_vcp = { - cli_vcp_init, - cli_vcp_deinit, - cli_vcp_rx, - cli_vcp_tx, - cli_vcp_tx_stdout, - cli_vcp_is_connected, -}; diff --git a/applications/accessor/accessor.cpp b/applications/debug/accessor/accessor.cpp similarity index 100% rename from applications/accessor/accessor.cpp rename to applications/debug/accessor/accessor.cpp diff --git a/applications/accessor/accessor_app.cpp b/applications/debug/accessor/accessor_app.cpp similarity index 97% rename from applications/accessor/accessor_app.cpp rename to applications/debug/accessor/accessor_app.cpp index 2e3e27ec456..2e40b3c35d7 100644 --- a/applications/accessor/accessor_app.cpp +++ b/applications/debug/accessor/accessor_app.cpp @@ -31,9 +31,10 @@ void AccessorApp::run(void) { onewire_host_stop(onewire_host); } -AccessorApp::AccessorApp() { +AccessorApp::AccessorApp() + : text_store{0} { notification = static_cast(furi_record_open(RECORD_NOTIFICATION)); - onewire_host = onewire_host_alloc(); + onewire_host = onewire_host_alloc(&gpio_ibutton); furi_hal_power_enable_otg(); } diff --git a/applications/accessor/accessor_app.h b/applications/debug/accessor/accessor_app.h similarity index 100% rename from applications/accessor/accessor_app.h rename to applications/debug/accessor/accessor_app.h diff --git a/applications/accessor/accessor_event.h b/applications/debug/accessor/accessor_event.h similarity index 100% rename from applications/accessor/accessor_event.h rename to applications/debug/accessor/accessor_event.h diff --git a/applications/accessor/accessor_view_manager.cpp b/applications/debug/accessor/accessor_view_manager.cpp similarity index 100% rename from applications/accessor/accessor_view_manager.cpp rename to applications/debug/accessor/accessor_view_manager.cpp diff --git a/applications/accessor/accessor_view_manager.h b/applications/debug/accessor/accessor_view_manager.h similarity index 100% rename from applications/accessor/accessor_view_manager.h rename to applications/debug/accessor/accessor_view_manager.h diff --git a/applications/debug/accessor/application.fam b/applications/debug/accessor/application.fam new file mode 100644 index 00000000000..65a6c86663a --- /dev/null +++ b/applications/debug/accessor/application.fam @@ -0,0 +1,11 @@ +App( + appid="accessor", + name="Accessor", + apptype=FlipperAppType.DEBUG, + targets=["f7"], + entry_point="accessor_app", + requires=["gui"], + stack_size=4 * 1024, + order=40, + fap_category="Debug", +) diff --git a/applications/accessor/helpers/wiegand.cpp b/applications/debug/accessor/helpers/wiegand.cpp similarity index 96% rename from applications/accessor/helpers/wiegand.cpp rename to applications/debug/accessor/helpers/wiegand.cpp index 79c9f723b2c..10b284eaacd 100644 --- a/applications/accessor/helpers/wiegand.cpp +++ b/applications/debug/accessor/helpers/wiegand.cpp @@ -71,7 +71,7 @@ void WIEGAND::end() { } void WIEGAND::ReadD0() { - _bitCount++; // Increament bit count for Interrupt connected to D0 + _bitCount++; // Increment bit count for Interrupt connected to D0 if(_bitCount > 31) // If bit count more than 31, process high bits { _cardTempHigh |= ((0x80000000 & _cardTemp) >> 31); // shift value to high bits @@ -171,13 +171,10 @@ bool WIEGAND::DoWiegandConversion() { return true; } else { _lastWiegand = sysTick; - _bitCount = 0; - _cardTemp = 0; - _cardTempHigh = 0; return false; } - // TODO: Handle validation failure case! + // TODO FL-3490: Handle validation failure case! } else if(4 == _bitCount) { // 4-bit Wiegand codes have no data integrity check so we just // read the LOW nibble. diff --git a/applications/accessor/helpers/wiegand.h b/applications/debug/accessor/helpers/wiegand.h similarity index 100% rename from applications/accessor/helpers/wiegand.h rename to applications/debug/accessor/helpers/wiegand.h diff --git a/applications/accessor/scene/accessor_scene_generic.h b/applications/debug/accessor/scene/accessor_scene_generic.h similarity index 100% rename from applications/accessor/scene/accessor_scene_generic.h rename to applications/debug/accessor/scene/accessor_scene_generic.h diff --git a/applications/accessor/scene/accessor_scene_start.cpp b/applications/debug/accessor/scene/accessor_scene_start.cpp similarity index 100% rename from applications/accessor/scene/accessor_scene_start.cpp rename to applications/debug/accessor/scene/accessor_scene_start.cpp diff --git a/applications/accessor/scene/accessor_scene_start.h b/applications/debug/accessor/scene/accessor_scene_start.h similarity index 100% rename from applications/accessor/scene/accessor_scene_start.h rename to applications/debug/accessor/scene/accessor_scene_start.h diff --git a/applications/debug/application.fam b/applications/debug/application.fam new file mode 100644 index 00000000000..cdbf8fe18b6 --- /dev/null +++ b/applications/debug/application.fam @@ -0,0 +1,17 @@ +App( + appid="debug_apps", + name="Basic debug apps bundle", + apptype=FlipperAppType.METAPACKAGE, + provides=[ + "blink_test", + "vibro_test", + "keypad_test", + "usb_test", + "usb_mouse", + "uart_echo", + "display_test", + "text_box_test", + "file_browser_test", + "speaker_debug", + ], +) diff --git a/applications/debug/battery_test_app/application.fam b/applications/debug/battery_test_app/application.fam new file mode 100644 index 00000000000..5f4acd83d5f --- /dev/null +++ b/applications/debug/battery_test_app/application.fam @@ -0,0 +1,14 @@ +App( + appid="battery_test", + name="Battery Test", + apptype=FlipperAppType.DEBUG, + entry_point="battery_test_app", + requires=[ + "gui", + "power", + ], + stack_size=1 * 1024, + order=130, + fap_category="Debug", + fap_libs=["assets"], +) diff --git a/applications/power/battery_test_app/battery_test_app.c b/applications/debug/battery_test_app/battery_test_app.c old mode 100755 new mode 100644 similarity index 92% rename from applications/power/battery_test_app/battery_test_app.c rename to applications/debug/battery_test_app/battery_test_app.c index ab6889dc8df..eabf3c04b44 --- a/applications/power/battery_test_app/battery_test_app.c +++ b/applications/debug/battery_test_app/battery_test_app.c @@ -27,7 +27,7 @@ static void battery_test_battery_info_update_model(void* context) { .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); notification_message(app->notifications, &sequence_display_backlight_on); } @@ -48,13 +48,13 @@ BatteryTestApp* battery_test_alloc() { 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_set_previous_callback( - battery_info_get_view(app->batery_info), battery_test_exit_confirm_view); + battery_info_get_view(app->battery_info), battery_test_exit_confirm_view); view_dispatcher_add_view( app->view_dispatcher, BatteryTestAppViewBatteryInfo, - battery_info_get_view(app->batery_info)); + battery_info_get_view(app->battery_info)); app->dialog = dialog_ex_alloc(); dialog_ex_set_header(app->dialog, "Close Battery Test?", 64, 12, AlignCenter, AlignTop); @@ -76,7 +76,7 @@ void battery_test_free(BatteryTestApp* app) { // Views view_dispatcher_remove_view(app->view_dispatcher, BatteryTestAppViewBatteryInfo); - battery_info_free(app->batery_info); + battery_info_free(app->battery_info); view_dispatcher_remove_view(app->view_dispatcher, BatteryTestAppViewExitDialog); dialog_ex_free(app->dialog); // View dispatcher diff --git a/applications/power/battery_test_app/battery_test_app.h b/applications/debug/battery_test_app/battery_test_app.h similarity index 82% rename from applications/power/battery_test_app/battery_test_app.h rename to applications/debug/battery_test_app/battery_test_app.h index 9c17626da92..497cdde823f 100644 --- a/applications/power/battery_test_app/battery_test_app.h +++ b/applications/debug/battery_test_app/battery_test_app.h @@ -6,14 +6,15 @@ #include #include -#include +// FIXME +#include "../settings/power_settings_app/views/battery_info.h" typedef struct { Power* power; Gui* gui; NotificationApp* notifications; ViewDispatcher* view_dispatcher; - BatteryInfo* batery_info; + BatteryInfo* battery_info; DialogEx* dialog; PowerInfo info; } BatteryTestApp; diff --git a/applications/debug/battery_test_app/views/battery_info.c b/applications/debug/battery_test_app/views/battery_info.c new file mode 100644 index 00000000000..5353a2e2a67 --- /dev/null +++ b/applications/debug/battery_test_app/views/battery_info.c @@ -0,0 +1,148 @@ +#include "battery_info.h" +#include +#include +#include + +#define LOW_CHARGE_THRESHOLD 10 +#define HIGH_DRAIN_CURRENT_THRESHOLD 100 + +struct BatteryInfo { + View* view; +}; + +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); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, x - 4, y + 16, 24, 6); + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val); +}; + +static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { + char emote[20] = {}; + char header[20] = {}; + char value[20] = {}; + + int32_t drain_current = data->gauge_current * (-1000); + uint32_t charge_current = data->gauge_current * 1000; + + // Draw battery + canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28); + if(charge_current > 0) { + canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14); + } else if(drain_current > HIGH_DRAIN_CURRENT_THRESHOLD) { + canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14); + } else if(data->charge < LOW_CHARGE_THRESHOLD) { + canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14); + } else { + canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNormal_29x14); + } + + // Draw bubble + elements_bubble(canvas, 53, 0, 71, 39); + + // Set text + if(charge_current > 0) { + snprintf(emote, sizeof(emote), "%s", "Yummy!"); + snprintf(header, sizeof(header), "%s", "Charging at"); + snprintf( + value, + sizeof(value), + "%lu.%luV %lumA", + (uint32_t)(data->vbus_voltage), + (uint32_t)(data->vbus_voltage * 10) % 10, + charge_current); + } else if(drain_current > 0) { + snprintf( + emote, + sizeof(emote), + "%s", + drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "Oh no!" : "Om-nom-nom!"); + snprintf(header, sizeof(header), "%s", "Consumption is"); + snprintf( + value, + sizeof(value), + "%ld %s", + drain_current, + drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); + } else if(drain_current != 0) { + snprintf(header, 20, "..."); + } else if(data->charging_voltage < 4.2) { + // Non-default battery charging limit, mention it + snprintf(emote, sizeof(emote), "Charged!"); + snprintf(header, sizeof(header), "Limited to"); + snprintf( + value, + sizeof(value), + "%lu.%luV", + (uint32_t)(data->charging_voltage), + (uint32_t)(data->charging_voltage * 10) % 10); + } else { + snprintf(header, sizeof(header), "Charged!"); + } + + canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote); + canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header); + canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value); +}; + +static void battery_info_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + BatteryInfoModel* model = context; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + draw_battery(canvas, model, 0, 5); + + char batt_level[10]; + char temperature[10]; + char voltage[10]; + char health[10]; + + snprintf(batt_level, sizeof(batt_level), "%lu%%", (uint32_t)model->charge); + snprintf(temperature, sizeof(temperature), "%lu C", (uint32_t)model->gauge_temperature); + snprintf( + voltage, + sizeof(voltage), + "%lu.%01lu V", + (uint32_t)model->gauge_voltage, + (uint32_t)(model->gauge_voltage * 10) % 10UL); + snprintf(health, sizeof(health), "%d%%", model->health); + + draw_stat(canvas, 8, 42, &I_Battery_16x16, batt_level); + draw_stat(canvas, 40, 42, &I_Temperature_16x16, temperature); + draw_stat(canvas, 72, 42, &I_Voltage_16x16, voltage); + draw_stat(canvas, 104, 42, &I_Health_16x16, health); +} + +BatteryInfo* battery_info_alloc() { + BatteryInfo* battery_info = malloc(sizeof(BatteryInfo)); + battery_info->view = view_alloc(); + view_set_context(battery_info->view, battery_info); + view_allocate_model(battery_info->view, ViewModelTypeLocking, sizeof(BatteryInfoModel)); + view_set_draw_callback(battery_info->view, battery_info_draw_callback); + + return battery_info; +} + +void battery_info_free(BatteryInfo* battery_info) { + furi_assert(battery_info); + view_free(battery_info->view); + free(battery_info); +} + +View* battery_info_get_view(BatteryInfo* battery_info) { + furi_assert(battery_info); + return battery_info->view; +} + +void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data) { + furi_assert(battery_info); + furi_assert(data); + with_view_model( + battery_info->view, + BatteryInfoModel * model, + { memcpy(model, data, sizeof(BatteryInfoModel)); }, + true); +} diff --git a/applications/debug/battery_test_app/views/battery_info.h b/applications/debug/battery_test_app/views/battery_info.h new file mode 100644 index 00000000000..7bfacf69e27 --- /dev/null +++ b/applications/debug/battery_test_app/views/battery_info.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +typedef struct BatteryInfo BatteryInfo; + +typedef struct { + float vbus_voltage; + float gauge_voltage; + float gauge_current; + float gauge_temperature; + float charging_voltage; + uint8_t charge; + uint8_t health; +} BatteryInfoModel; + +BatteryInfo* battery_info_alloc(); + +void battery_info_free(BatteryInfo* battery_info); + +View* battery_info_get_view(BatteryInfo* battery_info); + +void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data); diff --git a/applications/debug/blink_test/application.fam b/applications/debug/blink_test/application.fam new file mode 100644 index 00000000000..d7d873fb9a0 --- /dev/null +++ b/applications/debug/blink_test/application.fam @@ -0,0 +1,10 @@ +App( + appid="blink_test", + name="Blink Test", + apptype=FlipperAppType.DEBUG, + entry_point="blink_test_app", + requires=["gui"], + stack_size=1 * 1024, + order=10, + fap_category="Debug", +) diff --git a/applications/debug_tools/blink_test.c b/applications/debug/blink_test/blink_test.c similarity index 100% rename from applications/debug_tools/blink_test.c rename to applications/debug/blink_test/blink_test.c diff --git a/applications/debug/bt_debug_app/application.fam b/applications/debug/bt_debug_app/application.fam new file mode 100644 index 00000000000..8ed1ccc054f --- /dev/null +++ b/applications/debug/bt_debug_app/application.fam @@ -0,0 +1,18 @@ +App( + appid="bt_debug", + name="Bluetooth Debug", + apptype=FlipperAppType.DEBUG, + entry_point="bt_debug_app", + cdefines=["SRV_BT"], + requires=[ + "bt", + "gui", + "dialogs", + ], + provides=[ + "bt_debug", + ], + stack_size=1 * 1024, + order=110, + fap_category="Debug", +) diff --git a/applications/bt/bt_debug_app/bt_debug_app.c b/applications/debug/bt_debug_app/bt_debug_app.c similarity index 96% rename from applications/bt/bt_debug_app/bt_debug_app.c rename to applications/debug/bt_debug_app/bt_debug_app.c index ac442de0a29..bf13f6570a0 100644 --- a/applications/bt/bt_debug_app/bt_debug_app.c +++ b/applications/debug/bt_debug_app/bt_debug_app.c @@ -31,9 +31,6 @@ uint32_t bt_debug_start_view(void* context) { BtDebugApp* bt_debug_app_alloc() { BtDebugApp* app = malloc(sizeof(BtDebugApp)); - // Load settings - bt_settings_load(&app->settings); - // Gui app->gui = furi_record_open(RECORD_GUI); @@ -98,20 +95,22 @@ void bt_debug_app_free(BtDebugApp* app) { int32_t bt_debug_app(void* p) { UNUSED(p); if(!furi_hal_bt_is_testing_supported()) { - FURI_LOG_E(TAG, "Incorrect radio stack: radio testing fetures are absent."); + FURI_LOG_E(TAG, "Incorrect radio stack: radio testing features are absent."); DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); dialog_message_show_storage_error(dialogs, "Incorrect\nRadioStack"); return 255; } BtDebugApp* app = bt_debug_app_alloc(); + // Was bt active? + const bool was_active = furi_hal_bt_is_active(); // Stop advertising furi_hal_bt_stop_advertising(); view_dispatcher_run(app->view_dispatcher); // Restart advertising - if(app->settings.enabled) { + if(was_active) { furi_hal_bt_start_advertising(); } bt_debug_app_free(app); diff --git a/applications/bt/bt_debug_app/bt_debug_app.h b/applications/debug/bt_debug_app/bt_debug_app.h similarity index 91% rename from applications/bt/bt_debug_app/bt_debug_app.h rename to applications/debug/bt_debug_app/bt_debug_app.h index c3657626e46..0ad94d7ddc1 100644 --- a/applications/bt/bt_debug_app/bt_debug_app.h +++ b/applications/debug/bt_debug_app/bt_debug_app.h @@ -4,15 +4,14 @@ #include #include #include +#include + #include -#include #include "views/bt_carrier_test.h" #include "views/bt_packet_test.h" -#include "../bt_settings.h" typedef struct { - BtSettings settings; Gui* gui; ViewDispatcher* view_dispatcher; Submenu* submenu; diff --git a/applications/bt/bt_debug_app/views/bt_carrier_test.c b/applications/debug/bt_debug_app/views/bt_carrier_test.c old mode 100755 new mode 100644 similarity index 99% rename from applications/bt/bt_debug_app/views/bt_carrier_test.c rename to applications/debug/bt_debug_app/views/bt_carrier_test.c index c09aa3fdfaa..8e224049574 --- a/applications/bt/bt_debug_app/views/bt_carrier_test.c +++ b/applications/debug/bt_debug_app/views/bt_carrier_test.c @@ -1,7 +1,7 @@ #include "bt_carrier_test.h" #include "bt_test.h" #include "bt_test_types.h" -#include "furi_hal_bt.h" +#include struct BtCarrierTest { BtTest* bt_test; diff --git a/applications/bt/bt_debug_app/views/bt_carrier_test.h b/applications/debug/bt_debug_app/views/bt_carrier_test.h similarity index 100% rename from applications/bt/bt_debug_app/views/bt_carrier_test.h rename to applications/debug/bt_debug_app/views/bt_carrier_test.h diff --git a/applications/bt/bt_debug_app/views/bt_packet_test.c b/applications/debug/bt_debug_app/views/bt_packet_test.c similarity index 99% rename from applications/bt/bt_debug_app/views/bt_packet_test.c rename to applications/debug/bt_debug_app/views/bt_packet_test.c index 7cbc3c5c537..8a56a30031a 100644 --- a/applications/bt/bt_debug_app/views/bt_packet_test.c +++ b/applications/debug/bt_debug_app/views/bt_packet_test.c @@ -1,7 +1,7 @@ #include "bt_packet_test.h" #include "bt_test.h" #include "bt_test_types.h" -#include "furi_hal_bt.h" +#include struct BtPacketTest { BtTest* bt_test; diff --git a/applications/bt/bt_debug_app/views/bt_packet_test.h b/applications/debug/bt_debug_app/views/bt_packet_test.h similarity index 100% rename from applications/bt/bt_debug_app/views/bt_packet_test.h rename to applications/debug/bt_debug_app/views/bt_packet_test.h diff --git a/applications/debug/bt_debug_app/views/bt_test.c b/applications/debug/bt_debug_app/views/bt_test.c new file mode 100644 index 00000000000..cd52b8650e5 --- /dev/null +++ b/applications/debug/bt_debug_app/views/bt_test.c @@ -0,0 +1,434 @@ +#include "bt_test.h" + +#include +#include + +#include +#include +#include +#include +#include + +struct BtTestParam { + const char* label; + uint8_t current_value_index; + FuriString* current_value_text; + uint8_t values_count; + BtTestParamChangeCallback change_callback; + void* context; +}; + +ARRAY_DEF(BtTestParamArray, BtTestParam, M_POD_OPLIST); + +struct BtTest { + View* view; + BtTestChangeStateCallback change_state_callback; + BtTestBackCallback back_callback; + void* context; +}; + +typedef struct { + BtTestState state; + BtTestParamArray_t params; + uint8_t position; + uint8_t window_position; + const char* message; + float rssi; + uint32_t packets_num_rx; + uint32_t packets_num_tx; +} BtTestModel; + +#define BT_TEST_START_MESSAGE "Ok - Start" +#define BT_TEST_STOP_MESSAGE "Ok - Stop" + +static void bt_test_process_up(BtTest* bt_test); +static void bt_test_process_down(BtTest* bt_test); +static void bt_test_process_left(BtTest* bt_test); +static void bt_test_process_right(BtTest* bt_test); +static void bt_test_process_ok(BtTest* bt_test); +static void bt_test_process_back(BtTest* bt_test); + +static void bt_test_draw_callback(Canvas* canvas, void* _model) { + BtTestModel* model = _model; + char info_str[32]; + + const uint8_t param_height = 16; + const uint8_t param_width = 123; + + canvas_clear(canvas); + + uint8_t position = 0; + BtTestParamArray_it_t it; + + canvas_set_font(canvas, FontSecondary); + for(BtTestParamArray_it(it, model->params); !BtTestParamArray_end_p(it); + BtTestParamArray_next(it)) { + uint8_t param_position = position - model->window_position; + uint8_t params_on_screen = 3; + uint8_t y_offset = 0; + + if(param_position < params_on_screen) { + const BtTestParam* param = BtTestParamArray_cref(it); + uint8_t param_y = y_offset + (param_position * param_height); + uint8_t param_text_y = param_y + param_height - 4; + + if(position == model->position) { + canvas_set_color(canvas, ColorBlack); + elements_slightly_rounded_box( + canvas, 0, param_y + 1, param_width, param_height - 2); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_set_color(canvas, ColorBlack); + } + + canvas_draw_str(canvas, 6, param_text_y, param->label); + + if(param->current_value_index > 0) { + canvas_draw_str(canvas, 50, param_text_y, "<"); + } + + canvas_draw_str( + canvas, 61, param_text_y, furi_string_get_cstr(param->current_value_text)); + + if(param->current_value_index < (param->values_count - 1)) { + canvas_draw_str(canvas, 113, param_text_y, ">"); + } + } + + position++; + } + + elements_scrollbar(canvas, model->position, BtTestParamArray_size(model->params)); + canvas_draw_str(canvas, 6, 60, model->message); + if(model->state == BtTestStateStarted) { + if(!float_is_equal(model->rssi, 0.0f)) { + snprintf(info_str, sizeof(info_str), "RSSI:%3.1f dB", (double)model->rssi); + canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str); + } + } else if(model->state == BtTestStateStopped) { + if(model->packets_num_rx) { + snprintf(info_str, sizeof(info_str), "%" PRIu32 " pack rcv", model->packets_num_rx); + canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str); + } else if(model->packets_num_tx) { + snprintf(info_str, sizeof(info_str), "%" PRIu32 " pack sent", model->packets_num_tx); + canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str); + } + } +} + +static bool bt_test_input_callback(InputEvent* event, void* context) { + BtTest* bt_test = context; + furi_assert(bt_test); + bool consumed = false; + + if(event->type == InputTypeShort) { + switch(event->key) { + case InputKeyUp: + consumed = true; + bt_test_process_up(bt_test); + break; + case InputKeyDown: + consumed = true; + bt_test_process_down(bt_test); + break; + case InputKeyLeft: + consumed = true; + bt_test_process_left(bt_test); + break; + case InputKeyRight: + consumed = true; + bt_test_process_right(bt_test); + break; + case InputKeyOk: + consumed = true; + bt_test_process_ok(bt_test); + break; + case InputKeyBack: + consumed = false; + bt_test_process_back(bt_test); + break; + default: + break; + } + } + + return consumed; +} + +void bt_test_process_up(BtTest* bt_test) { + with_view_model( // -V658 + bt_test->view, + BtTestModel * model, + { + uint8_t params_on_screen = 3; + if(model->position > 0) { + model->position--; + if(((model->position - model->window_position) < 1) && + model->window_position > 0) { + model->window_position--; + } + } else { + model->position = BtTestParamArray_size(model->params) - 1; + if(model->position > (params_on_screen - 1)) { + model->window_position = model->position - (params_on_screen - 1); + } + } + }, + true); +} + +void bt_test_process_down(BtTest* bt_test) { + with_view_model( + bt_test->view, + BtTestModel * model, + { + uint8_t params_on_screen = 3; + if(model->position < (BtTestParamArray_size(model->params) - 1)) { + model->position++; + if((model->position - model->window_position) > (params_on_screen - 2) && + model->window_position < + (BtTestParamArray_size(model->params) - params_on_screen)) { + model->window_position++; + } + } else { + model->position = 0; + model->window_position = 0; + } + }, + true); +} + +BtTestParam* bt_test_get_selected_param(BtTestModel* model) { + BtTestParam* param = NULL; + + BtTestParamArray_it_t it; + uint8_t position = 0; + for(BtTestParamArray_it(it, model->params); !BtTestParamArray_end_p(it); + BtTestParamArray_next(it)) { + if(position == model->position) { + break; + } + position++; + } + + param = BtTestParamArray_ref(it); + + furi_assert(param); + return param; +} + +void bt_test_process_left(BtTest* bt_test) { + BtTestParam* param; + with_view_model( + bt_test->view, + BtTestModel * model, + { + param = bt_test_get_selected_param(model); + if(param->current_value_index > 0) { + param->current_value_index--; + if(param->change_callback) { + model->state = BtTestStateStopped; + model->message = BT_TEST_START_MESSAGE; + model->rssi = 0.0f; + model->packets_num_rx = 0; + model->packets_num_tx = 0; + } + } + }, + true); + if(param->change_callback) { + param->change_callback(param); + } +} + +void bt_test_process_right(BtTest* bt_test) { + BtTestParam* param; + with_view_model( + bt_test->view, + BtTestModel * model, + { + param = bt_test_get_selected_param(model); + if(param->current_value_index < (param->values_count - 1)) { + param->current_value_index++; + if(param->change_callback) { + model->state = BtTestStateStopped; + model->message = BT_TEST_START_MESSAGE; + model->rssi = 0.0f; + model->packets_num_rx = 0; + model->packets_num_tx = 0; + } + } + }, + true); + if(param->change_callback) { + param->change_callback(param); + } +} + +void bt_test_process_ok(BtTest* bt_test) { + BtTestState state; + with_view_model( + bt_test->view, + BtTestModel * model, + { + if(model->state == BtTestStateStarted) { + model->state = BtTestStateStopped; + model->message = BT_TEST_START_MESSAGE; + model->rssi = 0.0f; + model->packets_num_rx = 0; + model->packets_num_tx = 0; + } else if(model->state == BtTestStateStopped) { + model->state = BtTestStateStarted; + model->message = BT_TEST_STOP_MESSAGE; + } + state = model->state; + }, + true); + if(bt_test->change_state_callback) { + bt_test->change_state_callback(state, bt_test->context); + } +} + +void bt_test_process_back(BtTest* bt_test) { + with_view_model( + bt_test->view, + BtTestModel * model, + { + model->state = BtTestStateStopped; + model->rssi = 0.0f; + model->packets_num_rx = 0; + model->packets_num_tx = 0; + }, + false); + if(bt_test->back_callback) { + bt_test->back_callback(bt_test->context); + } +} + +BtTest* bt_test_alloc() { + BtTest* bt_test = malloc(sizeof(BtTest)); + bt_test->view = view_alloc(); + view_set_context(bt_test->view, bt_test); + view_allocate_model(bt_test->view, ViewModelTypeLocking, sizeof(BtTestModel)); + view_set_draw_callback(bt_test->view, bt_test_draw_callback); + view_set_input_callback(bt_test->view, bt_test_input_callback); + + with_view_model( + bt_test->view, + BtTestModel * model, + { + model->state = BtTestStateStopped; + model->message = "Ok - Start"; + BtTestParamArray_init(model->params); + model->position = 0; + model->window_position = 0; + model->rssi = 0.0f; + model->packets_num_tx = 0; + model->packets_num_rx = 0; + }, + true); + + return bt_test; +} + +void bt_test_free(BtTest* bt_test) { + furi_assert(bt_test); + + with_view_model( + bt_test->view, + BtTestModel * model, + { + BtTestParamArray_it_t it; + for(BtTestParamArray_it(it, model->params); !BtTestParamArray_end_p(it); + BtTestParamArray_next(it)) { + furi_string_free(BtTestParamArray_ref(it)->current_value_text); + } + BtTestParamArray_clear(model->params); + }, + false); + view_free(bt_test->view); + free(bt_test); +} + +View* bt_test_get_view(BtTest* bt_test) { + furi_assert(bt_test); + return bt_test->view; +} + +BtTestParam* bt_test_param_add( + BtTest* bt_test, + const char* label, + uint8_t values_count, + BtTestParamChangeCallback change_callback, + void* context) { + BtTestParam* param = NULL; + furi_assert(label); + furi_assert(bt_test); + + with_view_model( + bt_test->view, + BtTestModel * model, + { + param = BtTestParamArray_push_new(model->params); + param->label = label; + param->values_count = values_count; + param->change_callback = change_callback; + param->context = context; + param->current_value_index = 0; + param->current_value_text = furi_string_alloc(); + }, + true); + + return param; +} + +void bt_test_set_rssi(BtTest* bt_test, float rssi) { + furi_assert(bt_test); + with_view_model( + bt_test->view, BtTestModel * model, { model->rssi = rssi; }, true); +} + +void bt_test_set_packets_tx(BtTest* bt_test, uint32_t packets_num) { + furi_assert(bt_test); + with_view_model( + bt_test->view, BtTestModel * model, { model->packets_num_tx = packets_num; }, true); +} + +void bt_test_set_packets_rx(BtTest* bt_test, uint32_t packets_num) { + furi_assert(bt_test); + with_view_model( + bt_test->view, BtTestModel * model, { model->packets_num_rx = packets_num; }, true); +} + +void bt_test_set_change_state_callback(BtTest* bt_test, BtTestChangeStateCallback callback) { + furi_assert(bt_test); + furi_assert(callback); + bt_test->change_state_callback = callback; +} + +void bt_test_set_back_callback(BtTest* bt_test, BtTestBackCallback callback) { + furi_assert(bt_test); + furi_assert(callback); + bt_test->back_callback = callback; +} + +void bt_test_set_context(BtTest* bt_test, void* context) { + furi_assert(bt_test); + bt_test->context = context; +} + +void bt_test_set_current_value_index(BtTestParam* param, uint8_t current_value_index) { + param->current_value_index = current_value_index; +} + +void bt_test_set_current_value_text(BtTestParam* param, const char* current_value_text) { + furi_string_set(param->current_value_text, current_value_text); +} + +uint8_t bt_test_get_current_value_index(BtTestParam* param) { + return param->current_value_index; +} + +void* bt_test_get_context(BtTestParam* param) { + return param->context; +} diff --git a/applications/bt/bt_debug_app/views/bt_test.h b/applications/debug/bt_debug_app/views/bt_test.h old mode 100755 new mode 100644 similarity index 100% rename from applications/bt/bt_debug_app/views/bt_test.h rename to applications/debug/bt_debug_app/views/bt_test.h diff --git a/applications/bt/bt_debug_app/views/bt_test_types.h b/applications/debug/bt_debug_app/views/bt_test_types.h similarity index 100% rename from applications/bt/bt_debug_app/views/bt_test_types.h rename to applications/debug/bt_debug_app/views/bt_test_types.h diff --git a/applications/debug/ccid_test/application.fam b/applications/debug/ccid_test/application.fam new file mode 100644 index 00000000000..ad907677085 --- /dev/null +++ b/applications/debug/ccid_test/application.fam @@ -0,0 +1,15 @@ +App( + appid="ccid_test", + name="CCID Debug", + apptype=FlipperAppType.DEBUG, + entry_point="ccid_test_app", + requires=[ + "gui", + ], + provides=[ + "ccid_test", + ], + stack_size=1 * 1024, + order=120, + fap_category="Debug", +) diff --git a/applications/debug/ccid_test/ccid_test_app.c b/applications/debug/ccid_test/ccid_test_app.c new file mode 100644 index 00000000000..3d7fba39de5 --- /dev/null +++ b/applications/debug/ccid_test/ccid_test_app.c @@ -0,0 +1,169 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include "iso7816_t0_apdu.h" + +typedef enum { + EventTypeInput, +} EventType; + +typedef struct { + Gui* gui; + ViewPort* view_port; + FuriMessageQueue* event_queue; + FuriHalUsbCcidConfig ccid_cfg; +} CcidTestApp; + +typedef struct { + union { + InputEvent input; + }; + EventType type; +} CcidTestAppEvent; + +typedef enum { + CcidTestSubmenuIndexInsertSmartcard, + CcidTestSubmenuIndexRemoveSmartcard, + CcidTestSubmenuIndexInsertSmartcardReader +} SubmenuIndex; + +void icc_power_on_callback(uint8_t* atrBuffer, uint32_t* atrlen, void* context) { + UNUSED(context); + + iso7816_answer_to_reset(atrBuffer, atrlen); +} + +//dataBlock points to the buffer +//dataBlockLen tells reader how nany bytes should be read +void xfr_datablock_callback( + const uint8_t* dataBlock, + uint32_t dataBlockLen, + uint8_t* responseDataBlock, + uint32_t* responseDataBlockLen, + void* context) { + UNUSED(context); + + struct ISO7816_Command_APDU commandAPDU; + iso7816_read_command_apdu(&commandAPDU, dataBlock, dataBlockLen); + + struct ISO7816_Response_APDU responseAPDU; + //class not supported + responseAPDU.SW1 = 0x6E; + responseAPDU.SW2 = 0x00; + + iso7816_write_response_apdu(&responseAPDU, responseDataBlock, responseDataBlockLen); +} + +static const CcidCallbacks ccid_cb = { + icc_power_on_callback, + xfr_datablock_callback, +}; + +static void ccid_test_app_render_callback(Canvas* canvas, void* ctx) { + UNUSED(ctx); + canvas_clear(canvas); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 10, "CCID Test App"); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 63, "Hold [back] to exit"); +} + +static void ccid_test_app_input_callback(InputEvent* input_event, void* ctx) { + FuriMessageQueue* event_queue = ctx; + + CcidTestAppEvent event; + event.type = EventTypeInput; + event.input = *input_event; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +uint32_t ccid_test_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +CcidTestApp* ccid_test_app_alloc() { + CcidTestApp* app = malloc(sizeof(CcidTestApp)); + + // Gui + app->gui = furi_record_open(RECORD_GUI); + + //viewport + app->view_port = view_port_alloc(); + gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen); + view_port_draw_callback_set(app->view_port, ccid_test_app_render_callback, NULL); + + //message queue + app->event_queue = furi_message_queue_alloc(8, sizeof(CcidTestAppEvent)); + furi_check(app->event_queue); + view_port_input_callback_set(app->view_port, ccid_test_app_input_callback, app->event_queue); + + return app; +} + +void ccid_test_app_free(CcidTestApp* app) { + furi_assert(app); + + //message queue + furi_message_queue_free(app->event_queue); + + //view port + gui_remove_view_port(app->gui, app->view_port); + view_port_free(app->view_port); + + // Close gui record + furi_record_close(RECORD_GUI); + app->gui = NULL; + + // Free rest + free(app); +} + +int32_t ccid_test_app(void* p) { + UNUSED(p); + + //setup view + CcidTestApp* app = ccid_test_app_alloc(); + + //setup CCID USB + // On linux: set VID PID using: /usr/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Info.plist + app->ccid_cfg.vid = 0x1234; + app->ccid_cfg.pid = 0x5678; + + FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); + furi_hal_usb_unlock(); + furi_hal_ccid_set_callbacks((CcidCallbacks*)&ccid_cb); + furi_check(furi_hal_usb_set_config(&usb_ccid, &app->ccid_cfg) == true); + + //handle button events + CcidTestAppEvent event; + while(1) { + FuriStatus event_status = + furi_message_queue_get(app->event_queue, &event, FuriWaitForever); + + if(event_status == FuriStatusOk) { + if(event.type == EventTypeInput) { + if(event.input.type == InputTypeLong && event.input.key == InputKeyBack) { + break; + } + } + } + view_port_update(app->view_port); + } + + //tear down USB + furi_hal_usb_set_config(usb_mode_prev, NULL); + furi_hal_ccid_set_callbacks(NULL); + + //teardown view + ccid_test_app_free(app); + return 0; +} diff --git a/applications/debug/ccid_test/iso7816_t0_apdu.c b/applications/debug/ccid_test/iso7816_t0_apdu.c new file mode 100644 index 00000000000..7c690a6944e --- /dev/null +++ b/applications/debug/ccid_test/iso7816_t0_apdu.c @@ -0,0 +1,37 @@ +/* Implements rudimentary iso7816-3 support for APDU (T=0) */ +#include +#include +#include +#include "iso7816_t0_apdu.h" + +void iso7816_answer_to_reset(uint8_t* dataBuffer, uint32_t* atrlen) { + //minimum valid ATR: https://smartcard-atr.apdu.fr/parse?ATR=3B+00 + uint8_t AtrBuffer[2] = { + 0x3B, //TS (direct convention) + 0x00 // T0 (Y(1): b0000, K: 0 (historical bytes)) + }; + *atrlen = 2; + + memcpy(dataBuffer, AtrBuffer, sizeof(uint8_t) * (*atrlen)); +} + +void iso7816_read_command_apdu( + struct ISO7816_Command_APDU* command, + const uint8_t* dataBuffer, + uint32_t dataLen) { + UNUSED(dataLen); + command->CLA = dataBuffer[0]; + command->INS = dataBuffer[1]; + command->P1 = dataBuffer[2]; + command->P2 = dataBuffer[3]; + command->Lc = dataBuffer[4]; +} + +void iso7816_write_response_apdu( + const struct ISO7816_Response_APDU* response, + uint8_t* dataBuffer, + uint32_t* dataLen) { + dataBuffer[0] = response->SW1; + dataBuffer[1] = response->SW2; + *dataLen = 2; +} \ No newline at end of file diff --git a/applications/debug/ccid_test/iso7816_t0_apdu.h b/applications/debug/ccid_test/iso7816_t0_apdu.h new file mode 100644 index 00000000000..5ca13eb6041 --- /dev/null +++ b/applications/debug/ccid_test/iso7816_t0_apdu.h @@ -0,0 +1,32 @@ +#ifndef _ISO7816_T0_APDU_H_ +#define _ISO7816_T0_APDU_H_ + +#include + +struct ISO7816_Command_APDU { + //header + uint8_t CLA; + uint8_t INS; + uint8_t P1; + uint8_t P2; + + //body + uint8_t Lc; + uint8_t Le; +} FURI_PACKED; + +struct ISO7816_Response_APDU { + uint8_t SW1; + uint8_t SW2; +} FURI_PACKED; + +void iso7816_answer_to_reset(uint8_t* atrBuffer, uint32_t* atrlen); +void iso7816_read_command_apdu( + struct ISO7816_Command_APDU* command, + const uint8_t* dataBuffer, + uint32_t dataLen); +void iso7816_write_response_apdu( + const struct ISO7816_Response_APDU* response, + uint8_t* dataBuffer, + uint32_t* dataLen); +#endif //_ISO7816_T0_APDU_H_ diff --git a/applications/debug/crash_test/application.fam b/applications/debug/crash_test/application.fam new file mode 100644 index 00000000000..357efe667ac --- /dev/null +++ b/applications/debug/crash_test/application.fam @@ -0,0 +1,9 @@ +App( + appid="crash_test", + name="Crash Test", + apptype=FlipperAppType.DEBUG, + entry_point="crash_test_app", + requires=["gui"], + stack_size=1 * 1024, + fap_category="Debug", +) diff --git a/applications/debug/crash_test/crash_test.c b/applications/debug/crash_test/crash_test.c new file mode 100644 index 00000000000..92f1668be9f --- /dev/null +++ b/applications/debug/crash_test/crash_test.c @@ -0,0 +1,128 @@ +#include +#include + +#include +#include +#include + +#define TAG "CrashTest" + +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + Submenu* submenu; +} CrashTest; + +typedef enum { + CrashTestViewSubmenu, +} CrashTestView; + +typedef enum { + CrashTestSubmenuCheck, + CrashTestSubmenuCheckMessage, + CrashTestSubmenuAssert, + CrashTestSubmenuAssertMessage, + CrashTestSubmenuCrash, + CrashTestSubmenuHalt, +} CrashTestSubmenu; + +static void crash_test_submenu_callback(void* context, uint32_t index) { + CrashTest* instance = (CrashTest*)context; + UNUSED(instance); + + switch(index) { + case CrashTestSubmenuCheck: + furi_check(false); + break; + case CrashTestSubmenuCheckMessage: + furi_check(false, "Crash test: furi_check with message"); + break; + case CrashTestSubmenuAssert: + furi_assert(false); + break; + case CrashTestSubmenuAssertMessage: + furi_assert(false, "Crash test: furi_assert with message"); + break; + case CrashTestSubmenuCrash: + furi_crash("Crash test: furi_crash"); + break; + case CrashTestSubmenuHalt: + furi_halt("Crash test: furi_halt"); + break; + default: + furi_crash("Programming error"); + } +} + +static uint32_t crash_test_exit_callback(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +CrashTest* crash_test_alloc() { + CrashTest* instance = malloc(sizeof(CrashTest)); + + View* view = NULL; + + instance->gui = furi_record_open(RECORD_GUI); + instance->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(instance->view_dispatcher); + view_dispatcher_attach_to_gui( + instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen); + + // Menu + instance->submenu = submenu_alloc(); + view = submenu_get_view(instance->submenu); + view_set_previous_callback(view, crash_test_exit_callback); + view_dispatcher_add_view(instance->view_dispatcher, CrashTestViewSubmenu, view); + submenu_add_item( + instance->submenu, "Check", CrashTestSubmenuCheck, crash_test_submenu_callback, instance); + submenu_add_item( + instance->submenu, + "Check with message", + CrashTestSubmenuCheckMessage, + crash_test_submenu_callback, + instance); + submenu_add_item( + instance->submenu, "Assert", CrashTestSubmenuAssert, crash_test_submenu_callback, instance); + submenu_add_item( + instance->submenu, + "Assert with message", + CrashTestSubmenuAssertMessage, + crash_test_submenu_callback, + instance); + submenu_add_item( + instance->submenu, "Crash", CrashTestSubmenuCrash, crash_test_submenu_callback, instance); + submenu_add_item( + instance->submenu, "Halt", CrashTestSubmenuHalt, crash_test_submenu_callback, instance); + + return instance; +} + +void crash_test_free(CrashTest* instance) { + view_dispatcher_remove_view(instance->view_dispatcher, CrashTestViewSubmenu); + submenu_free(instance->submenu); + + view_dispatcher_free(instance->view_dispatcher); + furi_record_close(RECORD_GUI); + + free(instance); +} + +int32_t crash_test_run(CrashTest* instance) { + view_dispatcher_switch_to_view(instance->view_dispatcher, CrashTestViewSubmenu); + view_dispatcher_run(instance->view_dispatcher); + return 0; +} + +int32_t crash_test_app(void* p) { + UNUSED(p); + + CrashTest* instance = crash_test_alloc(); + + int32_t ret = crash_test_run(instance); + + crash_test_free(instance); + + return ret; +} diff --git a/applications/debug/direct_draw/application.fam b/applications/debug/direct_draw/application.fam new file mode 100644 index 00000000000..11b3bc6ba38 --- /dev/null +++ b/applications/debug/direct_draw/application.fam @@ -0,0 +1,10 @@ +App( + appid="direct_draw", + name="Direct Draw", + apptype=FlipperAppType.DEBUG, + entry_point="direct_draw_app", + requires=["gui", "input"], + stack_size=2 * 1024, + order=70, + fap_category="Debug", +) diff --git a/applications/debug/direct_draw/direct_draw.c b/applications/debug/direct_draw/direct_draw.c new file mode 100644 index 00000000000..63e03530a7a --- /dev/null +++ b/applications/debug/direct_draw/direct_draw.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include + +#define BUFFER_SIZE (32U) + +typedef struct { + FuriPubSub* input; + FuriPubSubSubscription* input_subscription; + Gui* gui; + Canvas* canvas; + bool stop; + uint32_t counter; +} DirectDraw; + +static void gui_input_events_callback(const void* value, void* ctx) { + furi_assert(value); + furi_assert(ctx); + + DirectDraw* instance = ctx; + const InputEvent* event = value; + + if(event->key == InputKeyBack && event->type == InputTypeShort) { + instance->stop = true; + } +} + +static DirectDraw* direct_draw_alloc() { + DirectDraw* instance = malloc(sizeof(DirectDraw)); + + instance->input = furi_record_open(RECORD_INPUT_EVENTS); + instance->gui = furi_record_open(RECORD_GUI); + instance->canvas = gui_direct_draw_acquire(instance->gui); + + instance->input_subscription = + furi_pubsub_subscribe(instance->input, gui_input_events_callback, instance); + + return instance; +} + +static void direct_draw_free(DirectDraw* instance) { + furi_pubsub_unsubscribe(instance->input, instance->input_subscription); + + instance->canvas = NULL; + gui_direct_draw_release(instance->gui); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_INPUT_EVENTS); +} + +static void direct_draw_block(Canvas* canvas, uint32_t size, uint32_t counter) { + size += 16; + uint8_t width = canvas_width(canvas) - size; + uint8_t height = canvas_height(canvas) - size; + + uint8_t x = counter % width; + if((counter / width) % 2) { + x = width - x; + } + + uint8_t y = counter % height; + if((counter / height) % 2) { + y = height - y; + } + + canvas_draw_box(canvas, x, y, size, size); +} + +static void direct_draw_run(DirectDraw* instance) { + size_t start = DWT->CYCCNT; + size_t counter = 0; + float fps = 0; + + furi_thread_set_current_priority(FuriThreadPriorityIdle); + + do { + size_t elapsed = DWT->CYCCNT - start; + char buffer[BUFFER_SIZE] = {0}; + + if(elapsed >= 64000000) { + fps = (float)counter / ((float)elapsed / 64000000.0f); + + start = DWT->CYCCNT; + counter = 0; + } + snprintf(buffer, BUFFER_SIZE, "FPS: %.1f", (double)fps); + + canvas_reset(instance->canvas); + canvas_set_color(instance->canvas, ColorXOR); + direct_draw_block(instance->canvas, instance->counter % 16, instance->counter); + direct_draw_block(instance->canvas, instance->counter * 2 % 16, instance->counter * 2); + direct_draw_block(instance->canvas, instance->counter * 3 % 16, instance->counter * 3); + direct_draw_block(instance->canvas, instance->counter * 4 % 16, instance->counter * 4); + direct_draw_block(instance->canvas, instance->counter * 5 % 16, instance->counter * 5); + canvas_draw_str(instance->canvas, 10, 10, buffer); + canvas_commit(instance->canvas); + + counter++; + instance->counter++; + furi_thread_yield(); + } while(!instance->stop); +} + +int32_t direct_draw_app(void* p) { + UNUSED(p); + + DirectDraw* instance = direct_draw_alloc(); + direct_draw_run(instance); + direct_draw_free(instance); + + return 0; +} diff --git a/applications/debug/display_test/application.fam b/applications/debug/display_test/application.fam new file mode 100644 index 00000000000..7b2357b01d8 --- /dev/null +++ b/applications/debug/display_test/application.fam @@ -0,0 +1,11 @@ +App( + appid="display_test", + name="Display Test", + apptype=FlipperAppType.DEBUG, + entry_point="display_test_app", + requires=["gui"], + fap_libs=["u8g2"], + stack_size=1 * 1024, + order=120, + fap_category="Debug", +) diff --git a/applications/debug_tools/display_test/display_test.c b/applications/debug/display_test/display_test.c similarity index 96% rename from applications/debug_tools/display_test/display_test.c rename to applications/debug/display_test/display_test.c index 947e4dc8b23..8065a23a1f9 100644 --- a/applications/debug_tools/display_test/display_test.c +++ b/applications/debug/display_test/display_test.c @@ -91,7 +91,6 @@ static void display_test_reload_config(DisplayTest* instance) { instance->config_contrast, instance->config_regulation_ratio, instance->config_bias); - gui_update(instance->gui); } static void display_config_set_bias(VariableItem* item) { @@ -113,11 +112,11 @@ static void display_config_set_regulation_ratio(VariableItem* item) { static void display_config_set_contrast(VariableItem* item) { DisplayTest* instance = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); - string_t temp; - string_init(temp); - string_cat_printf(temp, "%d", index); - variable_item_set_current_value_text(item, string_get_cstr(temp)); - string_clear(temp); + FuriString* temp; + temp = furi_string_alloc(); + furi_string_cat_printf(temp, "%d", index); + variable_item_set_current_value_text(item, furi_string_get_cstr(temp)); + furi_string_free(temp); instance->config_contrast = index; display_test_reload_config(instance); } @@ -145,7 +144,7 @@ DisplayTest* display_test_alloc() { view_set_previous_callback(view, display_test_previous_callback); view_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewConfigure, view); - // Configurtion items + // Configuration items VariableItem* item; instance->config_bias = false; instance->config_contrast = 32; diff --git a/applications/debug_tools/display_test/display_test.h b/applications/debug/display_test/display_test.h similarity index 100% rename from applications/debug_tools/display_test/display_test.h rename to applications/debug/display_test/display_test.h diff --git a/applications/debug_tools/display_test/view_display_test.c b/applications/debug/display_test/view_display_test.c similarity index 96% rename from applications/debug_tools/display_test/view_display_test.c rename to applications/debug/display_test/view_display_test.c index f00fe009445..b47c74c6c2c 100644 --- a/applications/debug_tools/display_test/view_display_test.c +++ b/applications/debug/display_test/view_display_test.c @@ -110,7 +110,9 @@ static bool view_display_test_input_callback(InputEvent* event, void* context) { bool consumed = false; if(event->type == InputTypeShort || event->type == InputTypeRepeat) { with_view_model( - instance->view, (ViewDisplayTestModel * model) { + instance->view, + ViewDisplayTestModel * model, + { if(event->key == InputKeyLeft && model->test > 0) { model->test--; consumed = true; @@ -129,8 +131,8 @@ static bool view_display_test_input_callback(InputEvent* event, void* context) { model->flip_flop = !model->flip_flop; consumed = true; } - return consumed; - }); + }, + consumed); } return consumed; @@ -149,10 +151,7 @@ static void view_display_test_exit(void* context) { static void view_display_test_timer_callback(void* context) { ViewDisplayTest* instance = context; with_view_model( - instance->view, (ViewDisplayTestModel * model) { - model->counter++; - return true; - }); + instance->view, ViewDisplayTestModel * model, { model->counter++; }, true); } ViewDisplayTest* view_display_test_alloc() { diff --git a/applications/debug_tools/display_test/view_display_test.h b/applications/debug/display_test/view_display_test.h similarity index 100% rename from applications/debug_tools/display_test/view_display_test.h rename to applications/debug/display_test/view_display_test.h diff --git a/applications/debug/example_custom_font/application.fam b/applications/debug/example_custom_font/application.fam new file mode 100644 index 00000000000..06c0a7f6183 --- /dev/null +++ b/applications/debug/example_custom_font/application.fam @@ -0,0 +1,9 @@ +App( + appid="example_custom_font", + name="Example: custom font", + apptype=FlipperAppType.DEBUG, + entry_point="example_custom_font_main", + requires=["gui"], + stack_size=1 * 1024, + fap_category="Debug", +) diff --git a/applications/debug/example_custom_font/example_custom_font.c b/applications/debug/example_custom_font/example_custom_font.c new file mode 100644 index 00000000000..15eeb5f02a4 --- /dev/null +++ b/applications/debug/example_custom_font/example_custom_font.c @@ -0,0 +1,98 @@ +#include +#include + +#include +#include + +//This arrays contains the font itself. You can use any u8g2 font you want + +/* +Fontname: -Raccoon-Fixed4x6-Medium-R-Normal--6-60-75-75-P-40-ISO10646-1 +Copyright: +Glyphs: 95/203 +BBX Build Mode: 0 +*/ +const uint8_t u8g2_font_tom_thumb_4x6_tr[725] = + "_\0\2\2\2\3\3\4\4\3\6\0\377\5\377\5\0\0\352\1\330\2\270 \5\340\315\0!\6\265\310" + "\254\0\42\6\213\313$\25#\10\227\310\244\241\206\12$\10\227\310\215\70b\2%\10\227\310d\324F\1" + "&\10\227\310(\65R\22'\5\251\313\10(\6\266\310\251\62)\10\226\310\304\224\24\0*\6\217\312\244" + "\16+\7\217\311\245\225\0,\6\212\310)\0-\5\207\312\14.\5\245\310\4/\7\227\310Ve\4\60" + "\7\227\310-k\1\61\6\226\310\255\6\62\10\227\310h\220\312\1\63\11\227\310h\220\62X\0\64\10\227" + "\310$\65b\1\65\10\227\310\214\250\301\2\66\10\227\310\315\221F\0\67\10\227\310\314TF\0\70\10\227" + "\310\214\64\324\10\71\10\227\310\214\64\342\2:\6\255\311\244\0;\7\222\310e\240\0<\10\227\310\246\32" + "d\20=\6\217\311l\60>\11\227\310d\220A*\1\77\10\227\310\314\224a\2@\10\227\310UC\3" + "\1A\10\227\310UC\251\0B\10\227\310\250\264\322\2C\7\227\310\315\32\10D\10\227\310\250d-\0" + "E\10\227\310\214\70\342\0F\10\227\310\214\70b\4G\10\227\310\315\221\222\0H\10\227\310$\65\224\12" + "I\7\227\310\254X\15J\7\227\310\226\252\2K\10\227\310$\265\222\12L\7\227\310\304\346\0M\10\227" + "\310\244\61\224\12N\10\227\310\244q\250\0O\7\227\310UV\5P\10\227\310\250\264b\4Q\10\227\310" + "Uj$\1R\10\227\310\250\64V\1S\10\227\310m\220\301\2T\7\227\310\254\330\2U\7\227\310$" + "W\22V\10\227\310$\253L\0W\10\227\310$\65\206\12X\10\227\310$\325R\1Y\10\227\310$U" + "V\0Z\7\227\310\314T\16[\7\227\310\214X\16\134\10\217\311d\220A\0]\7\227\310\314r\4^" + "\5\213\313\65_\5\207\310\14`\6\212\313\304\0a\7\223\310\310\65\2b\10\227\310D\225\324\2c\7" + "\223\310\315\14\4d\10\227\310\246\245\222\0e\6\223\310\235\2f\10\227\310\246\264b\2g\10\227\307\35" + "\61%\0h\10\227\310D\225\254\0i\6\265\310\244\1j\10\233\307f\30U\5k\10\227\310\304\264T" + "\1l\7\227\310\310\326\0m\7\223\310 -#include +#include + +#include #include #include +#include +#include static bool file_browser_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -47,9 +47,9 @@ FileBrowserApp* file_browser_app_alloc(char* arg) { app->widget = widget_alloc(); - string_init(app->file_path); + app->file_path = furi_string_alloc(); app->file_browser = file_browser_alloc(app->file_path); - file_browser_configure(app->file_browser, "*", true, &I_badusb_10px, true); + file_browser_configure(app->file_browser, "*", NULL, true, false, &I_badusb_10px, true); view_dispatcher_add_view( app->view_dispatcher, FileBrowserAppViewStart, widget_get_view(app->widget)); @@ -84,7 +84,7 @@ void file_browser_app_free(FileBrowserApp* app) { furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_DIALOGS); - string_clear(app->file_path); + furi_string_free(app->file_path); free(app); } diff --git a/applications/debug_tools/file_browser_test/file_browser_app_i.h b/applications/debug/file_browser_test/file_browser_app_i.h similarity index 96% rename from applications/debug_tools/file_browser_test/file_browser_app_i.h rename to applications/debug/file_browser_test/file_browser_app_i.h index 6e8412c9bd3..20f4c3a038b 100644 --- a/applications/debug_tools/file_browser_test/file_browser_app_i.h +++ b/applications/debug/file_browser_test/file_browser_app_i.h @@ -22,7 +22,7 @@ struct FileBrowserApp { Widget* widget; FileBrowser* file_browser; - string_t file_path; + FuriString* file_path; }; typedef enum { diff --git a/applications/debug/file_browser_test/icons/badusb_10px.png b/applications/debug/file_browser_test/icons/badusb_10px.png new file mode 100644 index 00000000000..037474aa3bc Binary files /dev/null and b/applications/debug/file_browser_test/icons/badusb_10px.png differ diff --git a/applications/debug_tools/file_browser_test/scenes/file_browser_scene.c b/applications/debug/file_browser_test/scenes/file_browser_scene.c similarity index 100% rename from applications/debug_tools/file_browser_test/scenes/file_browser_scene.c rename to applications/debug/file_browser_test/scenes/file_browser_scene.c diff --git a/applications/debug_tools/file_browser_test/scenes/file_browser_scene.h b/applications/debug/file_browser_test/scenes/file_browser_scene.h similarity index 100% rename from applications/debug_tools/file_browser_test/scenes/file_browser_scene.h rename to applications/debug/file_browser_test/scenes/file_browser_scene.h diff --git a/applications/debug_tools/file_browser_test/scenes/file_browser_scene_browser.c b/applications/debug/file_browser_test/scenes/file_browser_scene_browser.c similarity index 92% rename from applications/debug_tools/file_browser_test/scenes/file_browser_scene_browser.c rename to applications/debug/file_browser_test/scenes/file_browser_scene_browser.c index d9bd1ac5314..0bf70e9c63f 100644 --- a/applications/debug_tools/file_browser_test/scenes/file_browser_scene_browser.c +++ b/applications/debug/file_browser_test/scenes/file_browser_scene_browser.c @@ -1,8 +1,5 @@ #include "../file_browser_app_i.h" -#include -#include -#include "furi_hal.h" -#include "m-string.h" +#include #define DEFAULT_PATH "/" #define EXTENSION "*" diff --git a/applications/debug_tools/file_browser_test/scenes/file_browser_scene_config.h b/applications/debug/file_browser_test/scenes/file_browser_scene_config.h similarity index 100% rename from applications/debug_tools/file_browser_test/scenes/file_browser_scene_config.h rename to applications/debug/file_browser_test/scenes/file_browser_scene_config.h diff --git a/applications/debug_tools/file_browser_test/scenes/file_browser_scene_result.c b/applications/debug/file_browser_test/scenes/file_browser_scene_result.c similarity index 84% rename from applications/debug_tools/file_browser_test/scenes/file_browser_scene_result.c rename to applications/debug/file_browser_test/scenes/file_browser_scene_result.c index 53576cef4b4..4c68d80f773 100644 --- a/applications/debug_tools/file_browser_test/scenes/file_browser_scene_result.c +++ b/applications/debug/file_browser_test/scenes/file_browser_scene_result.c @@ -1,6 +1,5 @@ #include "../file_browser_app_i.h" -#include "furi_hal.h" -#include "m-string.h" +#include void file_browser_scene_result_ok_callback(InputType type, void* context) { furi_assert(context); @@ -24,7 +23,13 @@ void file_browser_scene_result_on_enter(void* context) { FileBrowserApp* app = context; widget_add_string_multiline_element( - app->widget, 64, 10, AlignCenter, AlignTop, FontSecondary, string_get_cstr(app->file_path)); + app->widget, + 64, + 10, + AlignCenter, + AlignTop, + FontSecondary, + furi_string_get_cstr(app->file_path)); view_dispatcher_switch_to_view(app->view_dispatcher, FileBrowserAppViewResult); } diff --git a/applications/debug_tools/file_browser_test/scenes/file_browser_scene_start.c b/applications/debug/file_browser_test/scenes/file_browser_scene_start.c similarity index 94% rename from applications/debug_tools/file_browser_test/scenes/file_browser_scene_start.c rename to applications/debug/file_browser_test/scenes/file_browser_scene_start.c index b3381f0ad0e..9eb26944ffc 100644 --- a/applications/debug_tools/file_browser_test/scenes/file_browser_scene_start.c +++ b/applications/debug/file_browser_test/scenes/file_browser_scene_start.c @@ -19,7 +19,7 @@ bool file_browser_scene_start_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - string_set_str(app->file_path, ANY_PATH("badusb/demo_windows.txt")); + furi_string_set(app->file_path, ANY_PATH("badusb/demo_windows.txt")); scene_manager_next_scene(app->scene_manager, FileBrowserSceneBrowser); consumed = true; } else if(event.type == SceneManagerEventTypeTick) { diff --git a/applications/debug/keypad_test/application.fam b/applications/debug/keypad_test/application.fam new file mode 100644 index 00000000000..90851950b22 --- /dev/null +++ b/applications/debug/keypad_test/application.fam @@ -0,0 +1,10 @@ +App( + appid="keypad_test", + name="Keypad Test", + apptype=FlipperAppType.DEBUG, + entry_point="keypad_test_app", + requires=["gui"], + stack_size=1 * 1024, + order=30, + fap_category="Debug", +) diff --git a/applications/debug_tools/keypad_test.c b/applications/debug/keypad_test/keypad_test.c similarity index 78% rename from applications/debug_tools/keypad_test.c rename to applications/debug/keypad_test/keypad_test.c index 2470baf8d2d..9e8881defaf 100644 --- a/applications/debug_tools/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/lfrfid_debug/application.fam b/applications/debug/lfrfid_debug/application.fam new file mode 100644 index 00000000000..323f77818fc --- /dev/null +++ b/applications/debug/lfrfid_debug/application.fam @@ -0,0 +1,16 @@ +App( + appid="lfrfid_debug", + name="LF-RFID Debug", + apptype=FlipperAppType.DEBUG, + targets=["f7"], + entry_point="lfrfid_debug_app", + requires=[ + "gui", + ], + provides=[ + "lfrfid_debug", + ], + stack_size=1 * 1024, + order=100, + fap_category="Debug", +) diff --git a/applications/debug/lfrfid_debug/lfrfid_debug.c b/applications/debug/lfrfid_debug/lfrfid_debug.c new file mode 100644 index 00000000000..63d66b68b9c --- /dev/null +++ b/applications/debug/lfrfid_debug/lfrfid_debug.c @@ -0,0 +1,81 @@ +#include "lfrfid_debug_i.h" + +static bool lfrfid_debug_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + LfRfidDebug* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool lfrfid_debug_back_event_callback(void* context) { + furi_assert(context); + LfRfidDebug* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static LfRfidDebug* lfrfid_debug_alloc() { + LfRfidDebug* app = malloc(sizeof(LfRfidDebug)); + + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&lfrfid_debug_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, lfrfid_debug_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, lfrfid_debug_back_event_callback); + + // Open GUI record + app->gui = furi_record_open(RECORD_GUI); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Submenu + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, LfRfidDebugViewSubmenu, submenu_get_view(app->submenu)); + + // Tune view + app->tune_view = lfrfid_debug_view_tune_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + LfRfidDebugViewTune, + lfrfid_debug_view_tune_get_view(app->tune_view)); + + return app; +} + +static void lfrfid_debug_free(LfRfidDebug* app) { + furi_assert(app); + + // Submenu + view_dispatcher_remove_view(app->view_dispatcher, LfRfidDebugViewSubmenu); + submenu_free(app->submenu); + + // Tune view + view_dispatcher_remove_view(app->view_dispatcher, LfRfidDebugViewTune); + lfrfid_debug_view_tune_free(app->tune_view); + + // View Dispatcher + view_dispatcher_free(app->view_dispatcher); + + // Scene Manager + scene_manager_free(app->scene_manager); + + // GUI + furi_record_close(RECORD_GUI); + app->gui = NULL; + + free(app); +} + +int32_t lfrfid_debug_app(void* p) { + UNUSED(p); + LfRfidDebug* app = lfrfid_debug_alloc(); + + scene_manager_next_scene(app->scene_manager, LfRfidDebugSceneStart); + + view_dispatcher_run(app->view_dispatcher); + + lfrfid_debug_free(app); + + return 0; +} \ No newline at end of file diff --git a/applications/debug/lfrfid_debug/lfrfid_debug_i.h b/applications/debug/lfrfid_debug/lfrfid_debug_i.h new file mode 100644 index 00000000000..8e4e72388f1 --- /dev/null +++ b/applications/debug/lfrfid_debug/lfrfid_debug_i.h @@ -0,0 +1,30 @@ +#pragma once +#include +#include + +#include +#include +#include +#include + +#include + +#include "views/lfrfid_debug_view_tune.h" +#include "scenes/lfrfid_debug_scene.h" + +typedef struct LfRfidDebug LfRfidDebug; + +struct LfRfidDebug { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + + // Common Views + Submenu* submenu; + LfRfidTuneView* tune_view; +}; + +typedef enum { + LfRfidDebugViewSubmenu, + LfRfidDebugViewTune, +} LfRfidDebugView; diff --git a/applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_start.c b/applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_start.c new file mode 100644 index 00000000000..2fc6706e38c --- /dev/null +++ b/applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_start.c @@ -0,0 +1,44 @@ +#include "../lfrfid_debug_i.h" + +typedef enum { + SubmenuIndexTune, +} SubmenuIndex; + +static void lfrfid_debug_scene_start_submenu_callback(void* context, uint32_t index) { + LfRfidDebug* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void lfrfid_debug_scene_start_on_enter(void* context) { + LfRfidDebug* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, "Tune", SubmenuIndexTune, lfrfid_debug_scene_start_submenu_callback, app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidDebugSceneStart)); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidDebugViewSubmenu); +} + +bool lfrfid_debug_scene_start_on_event(void* context, SceneManagerEvent event) { + LfRfidDebug* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexTune) { + scene_manager_next_scene(app->scene_manager, LfRfidDebugSceneTune); + consumed = true; + } + } + + return consumed; +} + +void lfrfid_debug_scene_start_on_exit(void* context) { + LfRfidDebug* app = context; + + submenu_reset(app->submenu); +} diff --git a/applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_tune.c b/applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_tune.c new file mode 100644 index 00000000000..ac2e2b80611 --- /dev/null +++ b/applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_tune.c @@ -0,0 +1,52 @@ +#include "../lfrfid_debug_i.h" +#include + +static void comparator_trigger_callback(bool level, void* comp_ctx) { + UNUSED(comp_ctx); + furi_hal_gpio_write(&gpio_ext_pa7, !level); +} + +void lfrfid_debug_view_tune_callback(void* context) { + LfRfidDebug* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, 0xBA); +} + +void lfrfid_debug_scene_tune_on_enter(void* context) { + LfRfidDebug* app = context; + + furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull); + + furi_hal_rfid_comp_set_callback(comparator_trigger_callback, app); + furi_hal_rfid_comp_start(); + + furi_hal_rfid_tim_read_start(125000, 0.5); + + lfrfid_debug_view_tune_set_callback(app->tune_view, lfrfid_debug_view_tune_callback, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidDebugViewTune); +} + +bool lfrfid_debug_scene_tune_on_event(void* context, SceneManagerEvent event) { + UNUSED(event); + + LfRfidDebug* app = context; + bool consumed = false; + + if(lfrfid_debug_view_tune_is_dirty(app->tune_view)) { + furi_hal_rfid_set_read_period(lfrfid_debug_view_tune_get_arr(app->tune_view)); + furi_hal_rfid_set_read_pulse(lfrfid_debug_view_tune_get_ccr(app->tune_view)); + } + + return consumed; +} + +void lfrfid_debug_scene_tune_on_exit(void* context) { + UNUSED(context); + + furi_hal_rfid_comp_stop(); + furi_hal_rfid_comp_set_callback(NULL, NULL); + + furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog); + furi_hal_rfid_tim_read_stop(); + furi_hal_rfid_pins_reset(); +} diff --git a/applications/debug/lfrfid_debug/scenes/lfrfid_debug_scene.c b/applications/debug/lfrfid_debug/scenes/lfrfid_debug_scene.c new file mode 100644 index 00000000000..e6288e923b2 --- /dev/null +++ b/applications/debug/lfrfid_debug/scenes/lfrfid_debug_scene.c @@ -0,0 +1,30 @@ +#include "lfrfid_debug_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const lfrfid_debug_on_enter_handlers[])(void*) = { +#include "lfrfid_debug_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 lfrfid_debug_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "lfrfid_debug_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 lfrfid_debug_on_exit_handlers[])(void* context) = { +#include "lfrfid_debug_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers lfrfid_debug_scene_handlers = { + .on_enter_handlers = lfrfid_debug_on_enter_handlers, + .on_event_handlers = lfrfid_debug_on_event_handlers, + .on_exit_handlers = lfrfid_debug_on_exit_handlers, + .scene_num = LfRfidDebugSceneNum, +}; diff --git a/applications/debug/lfrfid_debug/scenes/lfrfid_debug_scene.h b/applications/debug/lfrfid_debug/scenes/lfrfid_debug_scene.h new file mode 100644 index 00000000000..8fc74f72407 --- /dev/null +++ b/applications/debug/lfrfid_debug/scenes/lfrfid_debug_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) LfRfidDebugScene##id, +typedef enum { +#include "lfrfid_debug_scene_config.h" + LfRfidDebugSceneNum, +} LfRfidDebugScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers lfrfid_debug_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "lfrfid_debug_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 "lfrfid_debug_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 "lfrfid_debug_scene_config.h" +#undef ADD_SCENE diff --git a/applications/debug/lfrfid_debug/scenes/lfrfid_debug_scene_config.h b/applications/debug/lfrfid_debug/scenes/lfrfid_debug_scene_config.h new file mode 100644 index 00000000000..f3cca47b88d --- /dev/null +++ b/applications/debug/lfrfid_debug/scenes/lfrfid_debug_scene_config.h @@ -0,0 +1,2 @@ +ADD_SCENE(lfrfid_debug, start, Start) +ADD_SCENE(lfrfid_debug, tune, Tune) diff --git a/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c b/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c new file mode 100644 index 00000000000..9e48a7e27fe --- /dev/null +++ b/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c @@ -0,0 +1,252 @@ +#include "lfrfid_debug_view_tune.h" +#include + +#define TEMP_STR_LEN 128 + +struct LfRfidTuneView { + View* view; +}; + +typedef struct { + bool dirty; + bool fine; + uint32_t ARR; + uint32_t CCR; + int pos; + void (*update_callback)(void* context); + void* update_context; +} LfRfidTuneViewModel; + +static void lfrfid_debug_view_tune_draw_callback(Canvas* canvas, void* _model) { + LfRfidTuneViewModel* model = _model; + canvas_set_color(canvas, ColorBlack); + + if(model->fine) { + canvas_draw_box( + canvas, + 128 - canvas_string_width(canvas, "Fine") - 4, + 0, + canvas_string_width(canvas, "Fine") + 4, + canvas_current_font_height(canvas) + 1); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_str_aligned(canvas, 128 - 2, 2, AlignRight, AlignTop, "Fine"); + canvas_set_color(canvas, ColorBlack); + + char buffer[TEMP_STR_LEN + 1]; + double freq = ((float)SystemCoreClock / ((float)model->ARR + 1)); + double duty = ((float)model->CCR + 1) / ((float)model->ARR + 1) * 100.0f; + snprintf( + buffer, + TEMP_STR_LEN, + "%sARR: %lu\n" + "freq = %.4f\n" + "%sCCR: %lu\n" + "duty = %.4f", + model->pos == 0 ? ">" : "", + model->ARR, + freq, + model->pos == 1 ? ">" : "", + model->CCR, + duty); + elements_multiline_text_aligned(canvas, 2, 2, AlignLeft, AlignTop, buffer); +} + +static void lfrfid_debug_view_tune_button_up(LfRfidTuneView* tune_view) { + with_view_model( + tune_view->view, + LfRfidTuneViewModel * model, + { + if(model->pos > 0) model->pos--; + }, + true); +} + +static void lfrfid_debug_view_tune_button_down(LfRfidTuneView* tune_view) { + with_view_model( + tune_view->view, + LfRfidTuneViewModel * model, + { + if(model->pos < 1) model->pos++; + }, + true); +} + +static void lfrfid_debug_view_tune_button_left(LfRfidTuneView* tune_view) { + with_view_model( + tune_view->view, + LfRfidTuneViewModel * model, + { + if(model->pos == 0) { + if(model->fine) { + model->ARR -= 1; + } else { + model->ARR -= 10; + } + } else if(model->pos == 1) { + if(model->fine) { + model->CCR -= 1; + } else { + model->CCR -= 10; + } + } + + model->dirty = true; + }, + true); +} + +static void lfrfid_debug_view_tune_button_right(LfRfidTuneView* tune_view) { + with_view_model( + tune_view->view, + LfRfidTuneViewModel * model, + { + if(model->pos == 0) { + if(model->fine) { + model->ARR += 1; + } else { + model->ARR += 10; + } + } else if(model->pos == 1) { + if(model->fine) { + model->CCR += 1; + } else { + model->CCR += 10; + } + } + + model->dirty = true; + }, + true); +} + +static void lfrfid_debug_view_tune_button_ok(LfRfidTuneView* tune_view) { + with_view_model( + tune_view->view, LfRfidTuneViewModel * model, { model->fine = !model->fine; }, true); +} + +static bool lfrfid_debug_view_tune_input_callback(InputEvent* event, void* context) { + LfRfidTuneView* tune_view = context; + bool consumed = false; + + // Process key presses only + if(event->type == InputTypeShort || event->type == InputTypeRepeat) { + consumed = true; + + switch(event->key) { + case InputKeyLeft: + lfrfid_debug_view_tune_button_left(tune_view); + break; + case InputKeyRight: + lfrfid_debug_view_tune_button_right(tune_view); + break; + case InputKeyUp: + lfrfid_debug_view_tune_button_up(tune_view); + break; + case InputKeyDown: + lfrfid_debug_view_tune_button_down(tune_view); + break; + case InputKeyOk: + lfrfid_debug_view_tune_button_ok(tune_view); + break; + default: + consumed = false; + break; + } + + if(event->key == InputKeyLeft || event->key == InputKeyRight) { + with_view_model( + tune_view->view, + LfRfidTuneViewModel * model, + { + if(model->update_callback) { + model->update_callback(model->update_context); + } + }, + false); + } + } + + return consumed; +} + +LfRfidTuneView* lfrfid_debug_view_tune_alloc() { + LfRfidTuneView* tune_view = malloc(sizeof(LfRfidTuneView)); + tune_view->view = view_alloc(); + view_set_context(tune_view->view, tune_view); + view_allocate_model(tune_view->view, ViewModelTypeLocking, sizeof(LfRfidTuneViewModel)); + lfrfid_debug_view_tune_clean(tune_view); + view_set_draw_callback(tune_view->view, lfrfid_debug_view_tune_draw_callback); + view_set_input_callback(tune_view->view, lfrfid_debug_view_tune_input_callback); + + return tune_view; +} + +void lfrfid_debug_view_tune_free(LfRfidTuneView* tune_view) { + view_free(tune_view->view); + free(tune_view); +} + +View* lfrfid_debug_view_tune_get_view(LfRfidTuneView* tune_view) { + return tune_view->view; +} + +void lfrfid_debug_view_tune_clean(LfRfidTuneView* tune_view) { + with_view_model( + tune_view->view, + LfRfidTuneViewModel * model, + { + model->dirty = true; + model->fine = false; + model->ARR = 511; + model->CCR = 255; + model->pos = 0; + model->update_callback = NULL; + model->update_context = NULL; + }, + true); +} + +bool lfrfid_debug_view_tune_is_dirty(LfRfidTuneView* tune_view) { + bool result = false; + with_view_model( + tune_view->view, + LfRfidTuneViewModel * model, + { + result = model->dirty; + model->dirty = false; + }, + false); + + return result; +} + +uint32_t lfrfid_debug_view_tune_get_arr(LfRfidTuneView* tune_view) { + uint32_t result = false; + with_view_model( + tune_view->view, LfRfidTuneViewModel * model, { result = model->ARR; }, false); + + return result; +} + +uint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view) { + uint32_t result = false; + with_view_model( + tune_view->view, LfRfidTuneViewModel * model, { result = model->CCR; }, false); + + return result; +} + +void lfrfid_debug_view_tune_set_callback( + LfRfidTuneView* tune_view, + void (*callback)(void* context), + void* context) { + with_view_model( + tune_view->view, + LfRfidTuneViewModel * model, + { + model->update_callback = callback; + model->update_context = context; + }, + false); +} diff --git a/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.h b/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.h new file mode 100644 index 00000000000..be54b63f9a8 --- /dev/null +++ b/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.h @@ -0,0 +1,23 @@ +#pragma once +#include + +typedef struct LfRfidTuneView LfRfidTuneView; + +LfRfidTuneView* lfrfid_debug_view_tune_alloc(); + +void lfrfid_debug_view_tune_free(LfRfidTuneView* tune_view); + +View* lfrfid_debug_view_tune_get_view(LfRfidTuneView* tune_view); + +void lfrfid_debug_view_tune_clean(LfRfidTuneView* tune_view); + +bool lfrfid_debug_view_tune_is_dirty(LfRfidTuneView* tune_view); + +uint32_t lfrfid_debug_view_tune_get_arr(LfRfidTuneView* tune_view); + +uint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view); + +void lfrfid_debug_view_tune_set_callback( + LfRfidTuneView* tune_view, + void (*callback)(void* context), + void* context); diff --git a/applications/debug/locale_test/application.fam b/applications/debug/locale_test/application.fam new file mode 100644 index 00000000000..d341122f99d --- /dev/null +++ b/applications/debug/locale_test/application.fam @@ -0,0 +1,10 @@ +App( + appid="locale_test", + name="Locale Test", + apptype=FlipperAppType.DEBUG, + entry_point="locale_test_app", + requires=["gui", "locale"], + stack_size=2 * 1024, + order=70, + fap_category="Debug", +) diff --git a/applications/debug/locale_test/locale_test.c b/applications/debug/locale_test/locale_test.c new file mode 100644 index 00000000000..003df55dca4 --- /dev/null +++ b/applications/debug/locale_test/locale_test.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include + +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + View* view; +} LocaleTestApp; + +static void locale_test_view_draw_callback(Canvas* canvas, void* _model) { + UNUSED(_model); + + // Prepare canvas + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + + FuriString* tmp_string = furi_string_alloc(); + + float temp = 25.3f; + LocaleMeasurementUnits units = locale_get_measurement_unit(); + if(units == LocaleMeasurementUnitsMetric) { + furi_string_printf(tmp_string, "Temp: %5.1fC", (double)temp); + } else { + temp = locale_celsius_to_fahrenheit(temp); + furi_string_printf(tmp_string, "Temp: %5.1fF", (double)temp); + } + canvas_draw_str(canvas, 0, 10, furi_string_get_cstr(tmp_string)); + + FuriHalRtcDateTime datetime; + furi_hal_rtc_get_datetime(&datetime); + + locale_format_time(tmp_string, &datetime, locale_get_time_format(), false); + canvas_draw_str(canvas, 0, 25, furi_string_get_cstr(tmp_string)); + + locale_format_date(tmp_string, &datetime, locale_get_date_format(), "/"); + canvas_draw_str(canvas, 0, 40, furi_string_get_cstr(tmp_string)); + + furi_string_free(tmp_string); +} + +static bool locale_test_view_input_callback(InputEvent* event, void* context) { + UNUSED(event); + UNUSED(context); + return false; +} + +static uint32_t locale_test_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +static LocaleTestApp* locale_test_alloc() { + LocaleTestApp* app = malloc(sizeof(LocaleTestApp)); + + // Gui + app->gui = furi_record_open(RECORD_GUI); + + // 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); + + // Views + app->view = view_alloc(); + view_set_draw_callback(app->view, locale_test_view_draw_callback); + view_set_input_callback(app->view, locale_test_view_input_callback); + + view_set_previous_callback(app->view, locale_test_exit); + view_dispatcher_add_view(app->view_dispatcher, 0, app->view); + view_dispatcher_switch_to_view(app->view_dispatcher, 0); + + return app; +} + +static void locale_test_free(LocaleTestApp* app) { + furi_assert(app); + + // Free views + view_dispatcher_remove_view(app->view_dispatcher, 0); + + view_free(app->view); + view_dispatcher_free(app->view_dispatcher); + + // Close gui record + furi_record_close(RECORD_GUI); + app->gui = NULL; + + // Free rest + free(app); +} + +int32_t locale_test_app(void* p) { + UNUSED(p); + LocaleTestApp* app = locale_test_alloc(); + view_dispatcher_run(app->view_dispatcher); + locale_test_free(app); + return 0; +} diff --git a/applications/debug/rpc_debug_app/application.fam b/applications/debug/rpc_debug_app/application.fam new file mode 100644 index 00000000000..d71065afae4 --- /dev/null +++ b/applications/debug/rpc_debug_app/application.fam @@ -0,0 +1,10 @@ +App( + appid="rpc_debug", + name="RPC Debug", + apptype=FlipperAppType.DEBUG, + entry_point="rpc_debug_app", + requires=["gui", "rpc_start", "notification"], + stack_size=2 * 1024, + order=10, + fap_category="Debug", +) diff --git a/applications/debug/rpc_debug_app/rpc_debug_app.c b/applications/debug/rpc_debug_app/rpc_debug_app.c new file mode 100644 index 00000000000..2a0756383c2 --- /dev/null +++ b/applications/debug/rpc_debug_app/rpc_debug_app.c @@ -0,0 +1,167 @@ +#include "rpc_debug_app.h" +#include + +#include + +static bool rpc_debug_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + RpcDebugApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool rpc_debug_app_back_event_callback(void* context) { + furi_assert(context); + RpcDebugApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void rpc_debug_app_tick_event_callback(void* context) { + furi_assert(context); + RpcDebugApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +static void + rpc_debug_app_format_hex(const uint8_t* data, size_t data_size, char* buf, size_t buf_size) { + if(data == NULL || data_size == 0) { + strncpy(buf, "", buf_size); + return; + } + + const size_t byte_width = 3; + const size_t line_width = 7; + + data_size = MIN(data_size, buf_size / (byte_width + 1)); + + for(size_t i = 0; i < data_size; ++i) { + char* p = buf + (i * byte_width); + char sep = !((i + 1) % line_width) ? '\n' : ' '; + snprintf(p, byte_width + 1, "%02X%c", data[i], sep); + } + + buf[buf_size - 1] = '\0'; +} + +static void rpc_debug_app_rpc_command_callback(const RpcAppSystemEvent* event, void* context) { + furi_assert(context); + RpcDebugApp* app = context; + furi_assert(app->rpc); + + if(event->type == RpcAppEventTypeSessionClose) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + rpc_system_app_set_callback(app->rpc, NULL, NULL); + app->rpc = NULL; + } else if(event->type == RpcAppEventTypeAppExit) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + rpc_system_app_confirm(app->rpc, true); + } else if(event->type == RpcAppEventTypeDataExchange) { + furi_assert(event->data.type == RpcAppSystemEventDataTypeBytes); + + rpc_debug_app_format_hex( + event->data.bytes.ptr, event->data.bytes.size, app->text_store, TEXT_STORE_SIZE); + + view_dispatcher_send_custom_event( + app->view_dispatcher, RpcDebugAppCustomEventRpcDataExchange); + } else { + rpc_system_app_confirm(app->rpc, false); + } +} + +static bool rpc_debug_app_rpc_init_rpc(RpcDebugApp* app, const char* args) { + bool ret = false; + if(args && strlen(args)) { + uint32_t rpc = 0; + if(sscanf(args, "RPC %lX", &rpc) == 1) { + app->rpc = (RpcAppSystem*)rpc; + rpc_system_app_set_callback(app->rpc, rpc_debug_app_rpc_command_callback, app); + rpc_system_app_send_started(app->rpc); + ret = true; + } + } + return ret; +} + +static RpcDebugApp* rpc_debug_app_alloc() { + RpcDebugApp* app = malloc(sizeof(RpcDebugApp)); + + app->gui = furi_record_open(RECORD_GUI); + app->notifications = furi_record_open(RECORD_NOTIFICATION); + app->scene_manager = scene_manager_alloc(&rpc_debug_app_scene_handlers, app); + app->view_dispatcher = view_dispatcher_alloc(); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, rpc_debug_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, rpc_debug_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, rpc_debug_app_tick_event_callback, 100); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_enable_queue(app->view_dispatcher); + + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, RpcDebugAppViewWidget, widget_get_view(app->widget)); + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, RpcDebugAppViewSubmenu, submenu_get_view(app->submenu)); + app->text_box = text_box_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, RpcDebugAppViewTextBox, text_box_get_view(app->text_box)); + app->text_input = text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, RpcDebugAppViewTextInput, text_input_get_view(app->text_input)); + app->byte_input = byte_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, RpcDebugAppViewByteInput, byte_input_get_view(app->byte_input)); + + return app; +} + +static void rpc_debug_app_free(RpcDebugApp* app) { + view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewByteInput); + view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewTextInput); + view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewTextBox); + view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewSubmenu); + view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewWidget); + + free(app->byte_input); + free(app->text_input); + free(app->text_box); + free(app->submenu); + free(app->widget); + + free(app->scene_manager); + free(app->view_dispatcher); + + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + furi_record_close(RECORD_GUI); + app->gui = NULL; + + if(app->rpc) { + rpc_system_app_set_callback(app->rpc, NULL, NULL); + rpc_system_app_send_exited(app->rpc); + app->rpc = NULL; + } + + free(app); +} + +int32_t rpc_debug_app(void* args) { + RpcDebugApp* app = rpc_debug_app_alloc(); + + if(rpc_debug_app_rpc_init_rpc(app, args)) { + notification_message(app->notifications, &sequence_display_backlight_on); + scene_manager_next_scene(app->scene_manager, RpcDebugAppSceneStart); + } else { + scene_manager_next_scene(app->scene_manager, RpcDebugAppSceneStartDummy); + } + + view_dispatcher_run(app->view_dispatcher); + + rpc_debug_app_free(app); + return 0; +} diff --git a/applications/debug/rpc_debug_app/rpc_debug_app.h b/applications/debug/rpc_debug_app/rpc_debug_app.h new file mode 100644 index 00000000000..100fc124cf7 --- /dev/null +++ b/applications/debug/rpc_debug_app/rpc_debug_app.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "scenes/rpc_debug_app_scene.h" + +#define DATA_STORE_SIZE 64U +#define TEXT_STORE_SIZE 64U + +typedef struct { + Gui* gui; + RpcAppSystem* rpc; + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + NotificationApp* notifications; + + Widget* widget; + Submenu* submenu; + TextBox* text_box; + TextInput* text_input; + ByteInput* byte_input; + + char text_store[TEXT_STORE_SIZE]; + uint8_t data_store[DATA_STORE_SIZE]; +} RpcDebugApp; + +typedef enum { + RpcDebugAppViewWidget, + RpcDebugAppViewSubmenu, + RpcDebugAppViewTextBox, + RpcDebugAppViewTextInput, + RpcDebugAppViewByteInput, +} RpcDebugAppView; + +typedef enum { + // Reserve first 100 events for button types and indexes, starting from 0 + RpcDebugAppCustomEventInputErrorCode = 100, + RpcDebugAppCustomEventInputErrorText, + RpcDebugAppCustomEventInputDataExchange, + RpcDebugAppCustomEventRpcDataExchange, +} RpcDebugAppCustomEvent; diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene.c new file mode 100644 index 00000000000..2c593cdebcb --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene.c @@ -0,0 +1,30 @@ +#include "rpc_debug_app_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const rpc_debug_app_on_enter_handlers[])(void*) = { +#include "rpc_debug_app_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 rpc_debug_app_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "rpc_debug_app_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 rpc_debug_app_on_exit_handlers[])(void* context) = { +#include "rpc_debug_app_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers rpc_debug_app_scene_handlers = { + .on_enter_handlers = rpc_debug_app_on_enter_handlers, + .on_event_handlers = rpc_debug_app_on_event_handlers, + .on_exit_handlers = rpc_debug_app_on_exit_handlers, + .scene_num = RpcDebugAppSceneNum, +}; diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene.h b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene.h new file mode 100644 index 00000000000..eb3424fb1e7 --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) RpcDebugAppScene##id, +typedef enum { +#include "rpc_debug_app_scene_config.h" + RpcDebugAppSceneNum, +} RpcDebugAppScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers rpc_debug_app_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "rpc_debug_app_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 "rpc_debug_app_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 "rpc_debug_app_scene_config.h" +#undef ADD_SCENE diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_config.h b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_config.h new file mode 100644 index 00000000000..8f163253dcd --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_config.h @@ -0,0 +1,8 @@ +ADD_SCENE(rpc_debug_app, start, Start) +ADD_SCENE(rpc_debug_app, start_dummy, StartDummy) +ADD_SCENE(rpc_debug_app, test_app_error, TestAppError) +ADD_SCENE(rpc_debug_app, test_data_exchange, TestDataExchange) +ADD_SCENE(rpc_debug_app, input_error_code, InputErrorCode) +ADD_SCENE(rpc_debug_app, input_error_text, InputErrorText) +ADD_SCENE(rpc_debug_app, input_data_exchange, InputDataExchange) +ADD_SCENE(rpc_debug_app, receive_data_exchange, ReceiveDataExchange) diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_data_exchange.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_data_exchange.c new file mode 100644 index 00000000000..3ae2ba44d2c --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_data_exchange.c @@ -0,0 +1,40 @@ +#include "../rpc_debug_app.h" + +static void rpc_debug_app_scene_input_data_exchange_result_callback(void* context) { + RpcDebugApp* app = context; + view_dispatcher_send_custom_event( + app->view_dispatcher, RpcDebugAppCustomEventInputDataExchange); +} + +void rpc_debug_app_scene_input_data_exchange_on_enter(void* context) { + RpcDebugApp* app = context; + byte_input_set_header_text(app->byte_input, "Enter data to exchange"); + byte_input_set_result_callback( + app->byte_input, + rpc_debug_app_scene_input_data_exchange_result_callback, + NULL, + app, + app->data_store, + DATA_STORE_SIZE); + view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewByteInput); +} + +bool rpc_debug_app_scene_input_data_exchange_on_event(void* context, SceneManagerEvent event) { + RpcDebugApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == RpcDebugAppCustomEventInputDataExchange) { + rpc_system_app_exchange_data(app->rpc, app->data_store, DATA_STORE_SIZE); + scene_manager_previous_scene(app->scene_manager); + consumed = true; + } + } + + return consumed; +} + +void rpc_debug_app_scene_input_data_exchange_on_exit(void* context) { + RpcDebugApp* app = context; + UNUSED(app); +} diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c new file mode 100644 index 00000000000..367ca7a4ff1 --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c @@ -0,0 +1,64 @@ +#include "../rpc_debug_app.h" + +static bool rpc_debug_app_scene_input_error_code_validator_callback( + const char* text, + FuriString* error, + void* context) { + UNUSED(context); + + for(; *text; ++text) { + const char c = *text; + if(c < '0' || c > '9') { + furi_string_printf(error, "%s", "Please enter\na number!"); + return false; + } + } + + return true; +} + +static void rpc_debug_app_scene_input_error_code_result_callback(void* context) { + RpcDebugApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventInputErrorCode); +} + +void rpc_debug_app_scene_input_error_code_on_enter(void* context) { + RpcDebugApp* app = context; + strncpy(app->text_store, "666", TEXT_STORE_SIZE); + text_input_set_header_text(app->text_input, "Enter error code"); + text_input_set_validator( + app->text_input, rpc_debug_app_scene_input_error_code_validator_callback, NULL); + text_input_set_result_callback( + app->text_input, + rpc_debug_app_scene_input_error_code_result_callback, + app, + app->text_store, + TEXT_STORE_SIZE, + true); + view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextInput); +} + +bool rpc_debug_app_scene_input_error_code_on_event(void* context, SceneManagerEvent event) { + RpcDebugApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == RpcDebugAppCustomEventInputErrorCode) { + char* end; + int error_code = strtol(app->text_store, &end, 10); + if(!*end) { + rpc_system_app_set_error_code(app->rpc, error_code); + } + scene_manager_previous_scene(app->scene_manager); + consumed = true; + } + } + + return consumed; +} + +void rpc_debug_app_scene_input_error_code_on_exit(void* context) { + RpcDebugApp* app = context; + text_input_reset(app->text_input); + text_input_set_validator(app->text_input, NULL, NULL); +} diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_text.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_text.c new file mode 100644 index 00000000000..b07f8f4af27 --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_text.c @@ -0,0 +1,40 @@ +#include "../rpc_debug_app.h" + +static void rpc_debug_app_scene_input_error_text_result_callback(void* context) { + RpcDebugApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventInputErrorText); +} + +void rpc_debug_app_scene_input_error_text_on_enter(void* context) { + RpcDebugApp* app = context; + strncpy(app->text_store, "I'm a scary error message!", TEXT_STORE_SIZE); + text_input_set_header_text(app->text_input, "Enter error text"); + text_input_set_result_callback( + app->text_input, + rpc_debug_app_scene_input_error_text_result_callback, + app, + app->text_store, + TEXT_STORE_SIZE, + true); + view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextInput); +} + +bool rpc_debug_app_scene_input_error_text_on_event(void* context, SceneManagerEvent event) { + RpcDebugApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == RpcDebugAppCustomEventInputErrorText) { + rpc_system_app_set_error_text(app->rpc, app->text_store); + scene_manager_previous_scene(app->scene_manager); + consumed = true; + } + } + + return consumed; +} + +void rpc_debug_app_scene_input_error_text_on_exit(void* context) { + RpcDebugApp* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_receive_data_exchange.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_receive_data_exchange.c new file mode 100644 index 00000000000..10d5e36f624 --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_receive_data_exchange.c @@ -0,0 +1,33 @@ +#include "../rpc_debug_app.h" + +void rpc_debug_app_scene_receive_data_exchange_on_enter(void* context) { + RpcDebugApp* app = context; + strncpy(app->text_store, "Received data will appear here...", TEXT_STORE_SIZE); + + text_box_set_text(app->text_box, app->text_store); + text_box_set_font(app->text_box, TextBoxFontHex); + + view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextBox); +} + +bool rpc_debug_app_scene_receive_data_exchange_on_event(void* context, SceneManagerEvent event) { + RpcDebugApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == RpcDebugAppCustomEventRpcDataExchange) { + rpc_system_app_confirm(app->rpc, true); + notification_message(app->notifications, &sequence_blink_cyan_100); + notification_message(app->notifications, &sequence_display_backlight_on); + text_box_set_text(app->text_box, app->text_store); + consumed = true; + } + } + + return consumed; +} + +void rpc_debug_app_scene_receive_data_exchange_on_exit(void* context) { + RpcDebugApp* app = context; + text_box_reset(app->text_box); +} diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_start.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_start.c new file mode 100644 index 00000000000..9461649ba8a --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_start.c @@ -0,0 +1,57 @@ +#include "../rpc_debug_app.h" + +enum SubmenuIndex { + SubmenuIndexTestAppError, + SubmenuIndexTestDataExchange, +}; + +static void rpc_debug_app_scene_start_submenu_callback(void* context, uint32_t index) { + RpcDebugApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void rpc_debug_app_scene_start_on_enter(void* context) { + RpcDebugApp* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Test App Error", + SubmenuIndexTestAppError, + rpc_debug_app_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "Test Data Exchange", + SubmenuIndexTestDataExchange, + rpc_debug_app_scene_start_submenu_callback, + app); + + submenu_set_selected_item(submenu, SubmenuIndexTestAppError); + view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu); +} + +bool rpc_debug_app_scene_start_on_event(void* context, SceneManagerEvent event) { + RpcDebugApp* app = context; + SceneManager* scene_manager = app->scene_manager; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + const uint32_t submenu_index = event.event; + if(submenu_index == SubmenuIndexTestAppError) { + scene_manager_next_scene(scene_manager, RpcDebugAppSceneTestAppError); + consumed = true; + } else if(submenu_index == SubmenuIndexTestDataExchange) { + scene_manager_next_scene(scene_manager, RpcDebugAppSceneTestDataExchange); + consumed = true; + } + } + + return consumed; +} + +void rpc_debug_app_scene_start_on_exit(void* context) { + RpcDebugApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_start_dummy.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_start_dummy.c new file mode 100644 index 00000000000..5a30a079a3d --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_start_dummy.c @@ -0,0 +1,30 @@ +#include "../rpc_debug_app.h" + +void rpc_debug_app_scene_start_dummy_on_enter(void* context) { + RpcDebugApp* app = context; + widget_add_text_box_element( + app->widget, + 0, + 0, + 128, + 64, + AlignCenter, + AlignCenter, + "This application\nis meant to be run\nin \e#RPC\e# mode.", + false); + view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewWidget); +} + +bool rpc_debug_app_scene_start_dummy_on_event(void* context, SceneManagerEvent event) { + RpcDebugApp* app = context; + UNUSED(app); + UNUSED(event); + + bool consumed = false; + return consumed; +} + +void rpc_debug_app_scene_start_dummy_on_exit(void* context) { + RpcDebugApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_test_app_error.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_test_app_error.c new file mode 100644 index 00000000000..865d475ae83 --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_test_app_error.c @@ -0,0 +1,57 @@ +#include "../rpc_debug_app.h" + +typedef enum { + SubmenuIndexSetErrorCode, + SubmenuIndexSetErrorText, +} SubmenuIndex; + +static void rpc_debug_app_scene_test_app_error_submenu_callback(void* context, uint32_t index) { + RpcDebugApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void rpc_debug_app_scene_test_app_error_on_enter(void* context) { + RpcDebugApp* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Set Error Code", + SubmenuIndexSetErrorCode, + rpc_debug_app_scene_test_app_error_submenu_callback, + app); + submenu_add_item( + submenu, + "Set Error Text", + SubmenuIndexSetErrorText, + rpc_debug_app_scene_test_app_error_submenu_callback, + app); + + submenu_set_selected_item(submenu, SubmenuIndexSetErrorCode); + view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu); +} + +bool rpc_debug_app_scene_test_app_error_on_event(void* context, SceneManagerEvent event) { + RpcDebugApp* app = context; + SceneManager* scene_manager = app->scene_manager; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + const uint32_t submenu_index = event.event; + if(submenu_index == SubmenuIndexSetErrorCode) { + scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputErrorCode); + consumed = true; + } else if(submenu_index == SubmenuIndexSetErrorText) { + scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputErrorText); + consumed = true; + } + } + + return consumed; +} + +void rpc_debug_app_scene_test_app_error_on_exit(void* context) { + RpcDebugApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_test_data_exchange.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_test_data_exchange.c new file mode 100644 index 00000000000..f90f480c2a9 --- /dev/null +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_test_data_exchange.c @@ -0,0 +1,58 @@ +#include "../rpc_debug_app.h" + +typedef enum { + SubmenuIndexSendData, + SubmenuIndexReceiveData, +} SubmenuIndex; + +static void + rpc_debug_app_scene_test_data_exchange_submenu_callback(void* context, uint32_t index) { + RpcDebugApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void rpc_debug_app_scene_test_data_exchange_on_enter(void* context) { + RpcDebugApp* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Send Data", + SubmenuIndexSendData, + rpc_debug_app_scene_test_data_exchange_submenu_callback, + app); + submenu_add_item( + submenu, + "Receive Data", + SubmenuIndexReceiveData, + rpc_debug_app_scene_test_data_exchange_submenu_callback, + app); + + submenu_set_selected_item(submenu, SubmenuIndexSendData); + view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu); +} + +bool rpc_debug_app_scene_test_data_exchange_on_event(void* context, SceneManagerEvent event) { + RpcDebugApp* app = context; + SceneManager* scene_manager = app->scene_manager; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + const uint32_t submenu_index = event.event; + if(submenu_index == SubmenuIndexSendData) { + scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputDataExchange); + consumed = true; + } else if(submenu_index == SubmenuIndexReceiveData) { + scene_manager_next_scene(scene_manager, RpcDebugAppSceneReceiveDataExchange); + consumed = true; + } + } + + return consumed; +} + +void rpc_debug_app_scene_test_data_exchange_on_exit(void* context) { + RpcDebugApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/debug/speaker_debug/application.fam b/applications/debug/speaker_debug/application.fam new file mode 100644 index 00000000000..68d8b188bdd --- /dev/null +++ b/applications/debug/speaker_debug/application.fam @@ -0,0 +1,11 @@ +App( + appid="speaker_debug", + name="Speaker Debug", + apptype=FlipperAppType.DEBUG, + entry_point="speaker_debug_app", + requires=["gui", "notification"], + stack_size=2 * 1024, + order=10, + fap_category="Debug", + fap_libs=["music_worker"], +) diff --git a/applications/debug/speaker_debug/speaker_debug.c b/applications/debug/speaker_debug/speaker_debug.c new file mode 100644 index 00000000000..e01d5b8ec44 --- /dev/null +++ b/applications/debug/speaker_debug/speaker_debug.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include + +#define TAG "SpeakerDebug" +#define CLI_COMMAND "speaker_debug" + +typedef enum { + SpeakerDebugAppMessageTypeStop, +} SpeakerDebugAppMessageType; + +typedef struct { + SpeakerDebugAppMessageType type; +} SpeakerDebugAppMessage; + +typedef struct { + MusicWorker* music_worker; + FuriMessageQueue* message_queue; + Cli* cli; +} SpeakerDebugApp; + +static SpeakerDebugApp* speaker_app_alloc() { + SpeakerDebugApp* app = (SpeakerDebugApp*)malloc(sizeof(SpeakerDebugApp)); + app->music_worker = music_worker_alloc(); + app->message_queue = furi_message_queue_alloc(8, sizeof(SpeakerDebugAppMessage)); + app->cli = furi_record_open(RECORD_CLI); + return app; +} + +static void speaker_app_free(SpeakerDebugApp* app) { + music_worker_free(app->music_worker); + furi_message_queue_free(app->message_queue); + furi_record_close(RECORD_CLI); + free(app); +} + +static void speaker_app_cli(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + + SpeakerDebugApp* app = (SpeakerDebugApp*)context; + SpeakerDebugAppMessage message; + FuriString* cmd = furi_string_alloc(); + + if(!args_read_string_and_trim(args, cmd)) { + furi_string_free(cmd); + printf("Usage:\r\n"); + printf("\t" CLI_COMMAND " stop\r\n"); + return; + } + + if(furi_string_cmp(cmd, "stop") == 0) { + message.type = SpeakerDebugAppMessageTypeStop; + FuriStatus status = furi_message_queue_put(app->message_queue, &message, 100); + if(status != FuriStatusOk) { + printf("Failed to send message\r\n"); + } else { + printf("Stopping\r\n"); + } + } else { + printf("Usage:\r\n"); + printf("\t" CLI_COMMAND " stop\r\n"); + } + + furi_string_free(cmd); +} + +static bool speaker_app_music_play(SpeakerDebugApp* app, const char* rtttl) { + if(music_worker_is_playing(app->music_worker)) { + music_worker_stop(app->music_worker); + } + + if(!music_worker_load_rtttl_from_string(app->music_worker, rtttl)) { + FURI_LOG_E(TAG, "Failed to load RTTTL"); + return false; + } + + music_worker_set_volume(app->music_worker, 1.0f); + music_worker_start(app->music_worker); + + return true; +} + +static void speaker_app_music_stop(SpeakerDebugApp* app) { + if(music_worker_is_playing(app->music_worker)) { + music_worker_stop(app->music_worker); + } +} + +static void speaker_app_run(SpeakerDebugApp* app, const char* arg) { + if(!arg || !speaker_app_music_play(app, arg)) { + FURI_LOG_E(TAG, "Provided RTTTL is invalid"); + return; + } + + cli_add_command(app->cli, CLI_COMMAND, CliCommandFlagParallelSafe, speaker_app_cli, app); + + SpeakerDebugAppMessage message; + FuriStatus status; + while(true) { + status = furi_message_queue_get(app->message_queue, &message, FuriWaitForever); + + if(status == FuriStatusOk) { + if(message.type == SpeakerDebugAppMessageTypeStop) { + speaker_app_music_stop(app); + break; + } + } + } + + cli_delete_command(app->cli, CLI_COMMAND); +} + +int32_t speaker_debug_app(void* arg) { + SpeakerDebugApp* app = speaker_app_alloc(); + speaker_app_run(app, arg); + speaker_app_free(app); + return 0; +} diff --git a/applications/debug/subghz_test/application.fam b/applications/debug/subghz_test/application.fam new file mode 100644 index 00000000000..1b3e19d73f9 --- /dev/null +++ b/applications/debug/subghz_test/application.fam @@ -0,0 +1,14 @@ +App( + appid="subghz_test", + name="Sub-Ghz test", + apptype=FlipperAppType.DEBUG, + targets=["f7"], + entry_point="subghz_test_app", + requires=["gui"], + stack_size=4 * 1024, + order=50, + fap_icon="subghz_test_10px.png", + fap_category="Debug", + fap_icon_assets="images", + fap_version="0.1", +) diff --git a/applications/debug/subghz_test/helpers/subghz_test_event.h b/applications/debug/subghz_test/helpers/subghz_test_event.h new file mode 100644 index 00000000000..a0a851976ad --- /dev/null +++ b/applications/debug/subghz_test/helpers/subghz_test_event.h @@ -0,0 +1,7 @@ +#pragma once + +typedef enum { + //SubGhzTestCustomEvent + SubGhzTestCustomEventStartId = 100, + SubGhzTestCustomEventSceneShowOnlyRX, +} SubGhzTestCustomEvent; diff --git a/applications/debug/subghz_test/helpers/subghz_test_frequency.c b/applications/debug/subghz_test/helpers/subghz_test_frequency.c new file mode 100644 index 00000000000..ed1ba704e4d --- /dev/null +++ b/applications/debug/subghz_test/helpers/subghz_test_frequency.c @@ -0,0 +1,38 @@ +#include "subghz_test_frequency.h" + +const uint32_t subghz_frequencies_testing[] = { + /* 300 - 348 */ + 300000000, + 304500000, + 310000000, + 312025000, + 313250000, + 313625000, + 315000000, + 315225000, + 321950000, + 348000000, + /* 387 - 464 */ + 387000000, + 433075000, /* LPD433 first */ + 433825000, + 433920000, /* LPD433 mid */ + 434420000, + 434775000, /* LPD433 last channels */ + 438900000, + 464000000, + /* 779 - 928 */ + 779000000, + 868150000, + 868350000, + 868550000, + 915000000, + 925000000, + 926500000, + 927950000, + 928000000, +}; + +const uint32_t subghz_frequencies_count_testing = + sizeof(subghz_frequencies_testing) / sizeof(uint32_t); +const uint32_t subghz_frequencies_433_92_testing = 13; diff --git a/applications/debug/subghz_test/helpers/subghz_test_frequency.h b/applications/debug/subghz_test/helpers/subghz_test_frequency.h new file mode 100644 index 00000000000..7dd1423f968 --- /dev/null +++ b/applications/debug/subghz_test/helpers/subghz_test_frequency.h @@ -0,0 +1,6 @@ +#pragma once +#include "../subghz_test_app_i.h" + +extern const uint32_t subghz_frequencies_testing[]; +extern const uint32_t subghz_frequencies_count_testing; +extern const uint32_t subghz_frequencies_433_92_testing; diff --git a/applications/debug/subghz_test/helpers/subghz_test_types.h b/applications/debug/subghz_test/helpers/subghz_test_types.h new file mode 100644 index 00000000000..03be6459ec1 --- /dev/null +++ b/applications/debug/subghz_test/helpers/subghz_test_types.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#define SUBGHZ_TEST_VERSION_APP "0.1" +#define SUBGHZ_TEST_DEVELOPED "SkorP" +#define SUBGHZ_TEST_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" + +typedef enum { + SubGhzTestViewVariableItemList, + SubGhzTestViewSubmenu, + SubGhzTestViewStatic, + SubGhzTestViewCarrier, + SubGhzTestViewPacket, + SubGhzTestViewWidget, + SubGhzTestViewPopup, +} SubGhzTestView; diff --git a/applications/debug/subghz_test/images/DolphinCommon_56x48.png b/applications/debug/subghz_test/images/DolphinCommon_56x48.png new file mode 100644 index 00000000000..089aaed8350 Binary files /dev/null and b/applications/debug/subghz_test/images/DolphinCommon_56x48.png differ diff --git a/applications/debug/subghz_test/protocol/math.c b/applications/debug/subghz_test/protocol/math.c new file mode 100644 index 00000000000..24202ad1c62 --- /dev/null +++ b/applications/debug/subghz_test/protocol/math.c @@ -0,0 +1,244 @@ +#include "math.h" + +uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count) { + uint64_t reverse_key = 0; + for(uint8_t i = 0; i < bit_count; i++) { + reverse_key = reverse_key << 1 | bit_read(key, i); + } + return reverse_key; +} + +uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count) { + uint8_t parity = 0; + for(uint8_t i = 0; i < bit_count; i++) { + parity += bit_read(key, i); + } + return parity & 0x01; +} + +uint8_t subghz_protocol_blocks_crc4( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init << 4; // LSBs are unused + uint8_t poly = polynomial << 4; + uint8_t bit; + + while(size--) { + remainder ^= *message++; + for(bit = 0; bit < 8; bit++) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ poly; + } else { + remainder = (remainder << 1); + } + } + } + return remainder >> 4 & 0x0f; // discard the LSBs +} + +uint8_t subghz_protocol_blocks_crc7( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init << 1; // LSB is unused + uint8_t poly = polynomial << 1; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ poly; + } else { + remainder = (remainder << 1); + } + } + } + return remainder >> 1 & 0x7f; // discard the LSB +} + +uint8_t subghz_protocol_blocks_crc8( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ polynomial; + } else { + remainder = (remainder << 1); + } + } + } + return remainder; +} + +uint8_t subghz_protocol_blocks_crc8le( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = subghz_protocol_blocks_reverse_key(init, 8); + polynomial = subghz_protocol_blocks_reverse_key(polynomial, 8); + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 1) { + remainder = (remainder >> 1) ^ polynomial; + } else { + remainder = (remainder >> 1); + } + } + } + return remainder; +} + +uint16_t subghz_protocol_blocks_crc16lsb( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init) { + uint16_t remainder = init; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 1) { + remainder = (remainder >> 1) ^ polynomial; + } else { + remainder = (remainder >> 1); + } + } + } + return remainder; +} + +uint16_t subghz_protocol_blocks_crc16( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init) { + uint16_t remainder = init; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte] << 8; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 0x8000) { + remainder = (remainder << 1) ^ polynomial; + } else { + remainder = (remainder << 1); + } + } + } + return remainder; +} + +uint8_t subghz_protocol_blocks_lfsr_digest8( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key) { + uint8_t sum = 0; + for(size_t byte = 0; byte < size; ++byte) { + uint8_t data = message[byte]; + for(int i = 7; i >= 0; --i) { + // XOR key into sum if data bit is set + if((data >> i) & 1) sum ^= key; + + // roll the key right (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped LSB as MSB) + if(key & 1) + key = (key >> 1) ^ gen; + else + key = (key >> 1); + } + } + return sum; +} + +uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key) { + uint8_t sum = 0; + // Process message from last byte to first byte (reflected) + for(int byte = size - 1; byte >= 0; --byte) { + uint8_t data = message[byte]; + // Process individual bits of each byte (reflected) + for(uint8_t i = 0; i < 8; ++i) { + // XOR key into sum if data bit is set + if((data >> i) & 1) { + sum ^= key; + } + + // roll the key left (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped lsb as MSB) + if(key & 0x80) + key = (key << 1) ^ gen; + else + key = (key << 1); + } + } + return sum; +} + +uint16_t subghz_protocol_blocks_lfsr_digest16( + uint8_t const message[], + size_t size, + uint16_t gen, + uint16_t key) { + uint16_t sum = 0; + for(size_t byte = 0; byte < size; ++byte) { + uint8_t data = message[byte]; + for(int8_t i = 7; i >= 0; --i) { + // if data bit is set then xor with key + if((data >> i) & 1) sum ^= key; + + // roll the key right (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped LSB as MSB) + if(key & 1) + key = (key >> 1) ^ gen; + else + key = (key >> 1); + } + } + return sum; +} + +uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size) { + uint32_t result = 0; + for(size_t i = 0; i < size; ++i) { + result += message[i]; + } + return (uint8_t)result; +} + +uint8_t subghz_protocol_blocks_parity8(uint8_t byte) { + byte ^= byte >> 4; + byte &= 0xf; + return (0x6996 >> byte) & 1; +} + +uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size) { + uint8_t result = 0; + for(size_t i = 0; i < size; ++i) { + result ^= subghz_protocol_blocks_parity8(message[i]); + } + return result; +} + +uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size) { + uint8_t result = 0; + for(size_t i = 0; i < size; ++i) { + result ^= message[i]; + } + return result; +} \ No newline at end of file diff --git a/applications/debug/subghz_test/protocol/math.h b/applications/debug/subghz_test/protocol/math.h new file mode 100644 index 00000000000..dcea3da5faf --- /dev/null +++ b/applications/debug/subghz_test/protocol/math.h @@ -0,0 +1,222 @@ +#pragma once + +#include +#include +#include + +#define bit_read(value, bit) (((value) >> (bit)) & 0x01) +#define bit_set(value, bit) \ + ({ \ + __typeof__(value) _one = (1); \ + (value) |= (_one << (bit)); \ + }) +#define bit_clear(value, bit) \ + ({ \ + __typeof__(value) _one = (1); \ + (value) &= ~(_one << (bit)); \ + }) +#define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit)) +#define DURATION_DIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y))) + +#ifdef __cplusplus +extern "C" { +#endif + +/** Flip the data bitwise + * + * @param key In data + * @param bit_count number of data bits + * + * @return Reverse data + */ +uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count); + +/** Get parity the data bitwise + * + * @param key In data + * @param bit_count number of data bits + * + * @return parity + */ +uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count); + +/** CRC-4 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc4( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** CRC-7 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc7( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** Generic Cyclic Redundancy Check CRC-8. Example polynomial: 0x31 = x8 + x5 + + * x4 + 1 (x8 is implicit) Example polynomial: 0x80 = x8 + x7 (a normal + * bit-by-bit parity XOR) + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial byte is from x^7 to x^0 (x^8 is implicitly one) + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc8( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** "Little-endian" Cyclic Redundancy Check CRC-8 LE Input and output are + * reflected, i.e. least significant bit is shifted in first + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc8le( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** CRC-16 LSB. Input and output are reflected, i.e. least significant bit is + * shifted in first. Note that poly and init already need to be reflected + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint16_t subghz_protocol_blocks_crc16lsb( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init); + +/** CRC-16 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint16_t subghz_protocol_blocks_crc16( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init); + +/** Digest-8 by "LFSR-based Toeplitz hash" + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ +uint8_t subghz_protocol_blocks_lfsr_digest8( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key); + +/** Digest-8 by "LFSR-based Toeplitz hash", byte reflect, bit reflect + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ +uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key); + +/** Digest-16 by "LFSR-based Toeplitz hash" + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ +uint16_t subghz_protocol_blocks_lfsr_digest16( + uint8_t const message[], + size_t size, + uint16_t gen, + uint16_t key); + +/** Compute Addition of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return summation value + */ +uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size); + +/** Compute bit parity of a single byte (8 bits) + * + * @param byte single byte to check + * + * @return 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_parity8(uint8_t byte); + +/** Compute bit parity of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size); + +/** Compute XOR (byte-wide parity) of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return summation value, per bit-position 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/protocols/princeton_for_testing.c b/applications/debug/subghz_test/protocol/princeton_for_testing.c similarity index 98% rename from lib/subghz/protocols/princeton_for_testing.c rename to applications/debug/subghz_test/protocol/princeton_for_testing.c index 43078d2e230..334a8241bbe 100644 --- a/lib/subghz/protocols/princeton_for_testing.c +++ b/applications/debug/subghz_test/protocol/princeton_for_testing.c @@ -1,7 +1,7 @@ #include "princeton_for_testing.h" -#include "furi_hal.h" -#include "../blocks/math.h" +#include +#include "math.h" /* * Help @@ -94,12 +94,12 @@ void subghz_encoder_princeton_for_testing_print_log(void* context) { ((float)instance->time_high / (instance->time_high + instance->time_low)) * 100; FURI_LOG_I( TAG "Encoder", - "Radio tx_time=%dus ON=%dus, OFF=%dus, DutyCycle=%d,%d%%", + "Radio tx_time=%luus ON=%luus, OFF=%luus, DutyCycle=%lu,%lu%%", instance->time_high + instance->time_low, instance->time_high, instance->time_low, (uint32_t)duty_cycle, - (uint32_t)((duty_cycle - (uint32_t)duty_cycle) * 100)); + (uint32_t)((duty_cycle - (uint32_t)duty_cycle) * 100UL)); } LevelDuration subghz_encoder_princeton_for_testing_yield(void* context) { diff --git a/lib/subghz/protocols/princeton_for_testing.h b/applications/debug/subghz_test/protocol/princeton_for_testing.h similarity index 97% rename from lib/subghz/protocols/princeton_for_testing.h rename to applications/debug/subghz_test/protocol/princeton_for_testing.h index 07a37ec5f7f..7b4201d38a9 100644 --- a/lib/subghz/protocols/princeton_for_testing.h +++ b/applications/debug/subghz_test/protocol/princeton_for_testing.h @@ -1,6 +1,8 @@ #pragma once -#include "base.h" +//#include "base.h" +#include +#include /** SubGhzDecoderPrinceton anonymous type */ typedef struct SubGhzDecoderPrinceton SubGhzDecoderPrinceton; diff --git a/applications/debug/subghz_test/scenes/subghz_test_scene.c b/applications/debug/subghz_test/scenes/subghz_test_scene.c new file mode 100644 index 00000000000..ff439ef0f8d --- /dev/null +++ b/applications/debug/subghz_test/scenes/subghz_test_scene.c @@ -0,0 +1,30 @@ +#include "../subghz_test_app_i.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const subghz_test_scene_on_enter_handlers[])(void*) = { +#include "subghz_test_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 subghz_test_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "subghz_test_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 subghz_test_scene_on_exit_handlers[])(void* context) = { +#include "subghz_test_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers subghz_test_scene_handlers = { + .on_enter_handlers = subghz_test_scene_on_enter_handlers, + .on_event_handlers = subghz_test_scene_on_event_handlers, + .on_exit_handlers = subghz_test_scene_on_exit_handlers, + .scene_num = SubGhzTestSceneNum, +}; diff --git a/applications/debug/subghz_test/scenes/subghz_test_scene.h b/applications/debug/subghz_test/scenes/subghz_test_scene.h new file mode 100644 index 00000000000..0e6e06481b5 --- /dev/null +++ b/applications/debug/subghz_test/scenes/subghz_test_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) SubGhzTestScene##id, +typedef enum { +#include "subghz_test_scene_config.h" + SubGhzTestSceneNum, +} SubGhzTestScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers subghz_test_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "subghz_test_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 "subghz_test_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 "subghz_test_scene_config.h" +#undef ADD_SCENE diff --git a/applications/debug/subghz_test/scenes/subghz_test_scene_about.c b/applications/debug/subghz_test/scenes/subghz_test_scene_about.c new file mode 100644 index 00000000000..64263d73887 --- /dev/null +++ b/applications/debug/subghz_test/scenes/subghz_test_scene_about.c @@ -0,0 +1,66 @@ +#include "../subghz_test_app_i.h" + +void subghz_test_scene_about_widget_callback(GuiButtonType result, InputType type, void* context) { + SubGhzTestApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void subghz_test_scene_about_on_enter(void* context) { + SubGhzTestApp* app = context; + + FuriString* temp_str; + temp_str = furi_string_alloc(); + furi_string_printf(temp_str, "\e#%s\n", "Information"); + + furi_string_cat_printf(temp_str, "Version: %s\n", SUBGHZ_TEST_VERSION_APP); + furi_string_cat_printf(temp_str, "Developed by: %s\n", SUBGHZ_TEST_DEVELOPED); + furi_string_cat_printf(temp_str, "Github: %s\n\n", SUBGHZ_TEST_GITHUB); + + furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); + furi_string_cat_printf( + temp_str, + "This application is designed\nto test the functionality of the\nbuilt-in CC1101 module.\n\n"); + + widget_add_text_box_element( + app->widget, + 0, + 0, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! \e!\n", + false); + widget_add_text_box_element( + app->widget, + 0, + 2, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! Sub-Ghz Test \e!\n", + false); + widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewWidget); +} + +bool subghz_test_scene_about_on_event(void* context, SceneManagerEvent event) { + SubGhzTestApp* app = context; + bool consumed = false; + UNUSED(app); + UNUSED(event); + + return consumed; +} + +void subghz_test_scene_about_on_exit(void* context) { + SubGhzTestApp* app = context; + + // Clear views + widget_reset(app->widget); +} diff --git a/applications/debug/subghz_test/scenes/subghz_test_scene_carrier.c b/applications/debug/subghz_test/scenes/subghz_test_scene_carrier.c new file mode 100644 index 00000000000..41ff5c8c6e1 --- /dev/null +++ b/applications/debug/subghz_test/scenes/subghz_test_scene_carrier.c @@ -0,0 +1,29 @@ +#include "../subghz_test_app_i.h" + +void subghz_test_scene_carrier_callback(SubGhzTestCarrierEvent event, void* context) { + furi_assert(context); + SubGhzTestApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void subghz_test_scene_carrier_on_enter(void* context) { + SubGhzTestApp* app = context; + subghz_test_carrier_set_callback( + app->subghz_test_carrier, subghz_test_scene_carrier_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewCarrier); +} + +bool subghz_test_scene_carrier_on_event(void* context, SceneManagerEvent event) { + SubGhzTestApp* app = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzTestCarrierEventOnlyRx) { + scene_manager_next_scene(app->scene_manager, SubGhzTestSceneShowOnlyRx); + return true; + } + } + return false; +} + +void subghz_test_scene_carrier_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/debug/subghz_test/scenes/subghz_test_scene_config.h b/applications/debug/subghz_test/scenes/subghz_test_scene_config.h new file mode 100644 index 00000000000..80a42c3761a --- /dev/null +++ b/applications/debug/subghz_test/scenes/subghz_test_scene_config.h @@ -0,0 +1,6 @@ +ADD_SCENE(subghz_test, start, Start) +ADD_SCENE(subghz_test, about, About) +ADD_SCENE(subghz_test, carrier, Carrier) +ADD_SCENE(subghz_test, packet, Packet) +ADD_SCENE(subghz_test, static, Static) +ADD_SCENE(subghz_test, show_only_rx, ShowOnlyRx) diff --git a/applications/debug/subghz_test/scenes/subghz_test_scene_packet.c b/applications/debug/subghz_test/scenes/subghz_test_scene_packet.c new file mode 100644 index 00000000000..b43a4d0cb39 --- /dev/null +++ b/applications/debug/subghz_test/scenes/subghz_test_scene_packet.c @@ -0,0 +1,29 @@ +#include "../subghz_test_app_i.h" + +void subghz_test_scene_packet_callback(SubGhzTestPacketEvent event, void* context) { + furi_assert(context); + SubGhzTestApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void subghz_test_scene_packet_on_enter(void* context) { + SubGhzTestApp* app = context; + subghz_test_packet_set_callback( + app->subghz_test_packet, subghz_test_scene_packet_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewPacket); +} + +bool subghz_test_scene_packet_on_event(void* context, SceneManagerEvent event) { + SubGhzTestApp* app = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzTestPacketEventOnlyRx) { + scene_manager_next_scene(app->scene_manager, SubGhzTestSceneShowOnlyRx); + return true; + } + } + return false; +} + +void subghz_test_scene_packet_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/debug/subghz_test/scenes/subghz_test_scene_show_only_rx.c b/applications/debug/subghz_test/scenes/subghz_test_scene_show_only_rx.c new file mode 100644 index 00000000000..3d5a54355c3 --- /dev/null +++ b/applications/debug/subghz_test/scenes/subghz_test_scene_show_only_rx.c @@ -0,0 +1,49 @@ +#include "../subghz_test_app_i.h" +#include + +void subghz_test_scene_show_only_rx_popup_callback(void* context) { + SubGhzTestApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SubGhzTestCustomEventSceneShowOnlyRX); +} + +void subghz_test_scene_show_only_rx_on_enter(void* context) { + SubGhzTestApp* app = context; + + // Setup view + Popup* popup = app->popup; + + const char* header_text = "Transmission is blocked"; + const char* message_text = "Transmission on\nthis frequency is\nrestricted in\nyour region"; + if(!furi_hal_region_is_provisioned()) { + header_text = "Firmware update needed"; + message_text = "Please update\nfirmware before\nusing this feature\nflipp.dev/upd"; + } + + popup_set_header(popup, header_text, 63, 3, AlignCenter, AlignTop); + popup_set_text(popup, message_text, 0, 17, AlignLeft, AlignTop); + popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); + + popup_set_timeout(popup, 1500); + popup_set_context(popup, app); + popup_set_callback(popup, subghz_test_scene_show_only_rx_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewPopup); +} + +bool subghz_test_scene_show_only_rx_on_event(void* context, SceneManagerEvent event) { + SubGhzTestApp* app = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzTestCustomEventSceneShowOnlyRX) { + scene_manager_previous_scene(app->scene_manager); + return true; + } + } + return false; +} + +void subghz_test_scene_show_only_rx_on_exit(void* context) { + SubGhzTestApp* app = context; + Popup* popup = app->popup; + + popup_reset(popup); +} diff --git a/applications/debug/subghz_test/scenes/subghz_test_scene_start.c b/applications/debug/subghz_test/scenes/subghz_test_scene_start.c new file mode 100644 index 00000000000..cf3b08163d2 --- /dev/null +++ b/applications/debug/subghz_test/scenes/subghz_test_scene_start.c @@ -0,0 +1,77 @@ +#include "../subghz_test_app_i.h" + +typedef enum { + SubmenuIndexSubGhzTestCarrier, + SubmenuIndexSubGhzTestPacket, + SubmenuIndexSubGhzTestStatic, + SubmenuIndexSubGhzTestAbout, +} SubmenuIndex; + +void subghz_test_scene_start_submenu_callback(void* context, uint32_t index) { + SubGhzTestApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void subghz_test_scene_start_on_enter(void* context) { + SubGhzTestApp* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Carrier", + SubmenuIndexSubGhzTestCarrier, + subghz_test_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "Packet", + SubmenuIndexSubGhzTestPacket, + subghz_test_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "Static", + SubmenuIndexSubGhzTestStatic, + subghz_test_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "About", + SubmenuIndexSubGhzTestAbout, + subghz_test_scene_start_submenu_callback, + app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, SubGhzTestSceneStart)); + + view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewSubmenu); +} + +bool subghz_test_scene_start_on_event(void* context, SceneManagerEvent event) { + SubGhzTestApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexSubGhzTestAbout) { + scene_manager_next_scene(app->scene_manager, SubGhzTestSceneAbout); + consumed = true; + } else if(event.event == SubmenuIndexSubGhzTestCarrier) { + scene_manager_next_scene(app->scene_manager, SubGhzTestSceneCarrier); + consumed = true; + } else if(event.event == SubmenuIndexSubGhzTestPacket) { + scene_manager_next_scene(app->scene_manager, SubGhzTestScenePacket); + consumed = true; + } else if(event.event == SubmenuIndexSubGhzTestStatic) { + scene_manager_next_scene(app->scene_manager, SubGhzTestSceneStatic); + consumed = true; + } + scene_manager_set_scene_state(app->scene_manager, SubGhzTestSceneStart, event.event); + } + + return consumed; +} + +void subghz_test_scene_start_on_exit(void* context) { + SubGhzTestApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/debug/subghz_test/scenes/subghz_test_scene_static.c b/applications/debug/subghz_test/scenes/subghz_test_scene_static.c new file mode 100644 index 00000000000..a008d2438ff --- /dev/null +++ b/applications/debug/subghz_test/scenes/subghz_test_scene_static.c @@ -0,0 +1,29 @@ +#include "../subghz_test_app_i.h" + +void subghz_test_scene_static_callback(SubGhzTestStaticEvent event, void* context) { + furi_assert(context); + SubGhzTestApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void subghz_test_scene_static_on_enter(void* context) { + SubGhzTestApp* app = context; + subghz_test_static_set_callback( + app->subghz_test_static, subghz_test_scene_static_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, SubGhzTestViewStatic); +} + +bool subghz_test_scene_static_on_event(void* context, SceneManagerEvent event) { + SubGhzTestApp* app = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzTestStaticEventOnlyRx) { + scene_manager_next_scene(app->scene_manager, SubGhzTestSceneShowOnlyRx); + return true; + } + } + return false; +} + +void subghz_test_scene_static_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/debug/subghz_test/subghz_test_10px.png b/applications/debug/subghz_test/subghz_test_10px.png new file mode 100644 index 00000000000..10dac0ecaac Binary files /dev/null and b/applications/debug/subghz_test/subghz_test_10px.png differ diff --git a/applications/debug/subghz_test/subghz_test_app.c b/applications/debug/subghz_test/subghz_test_app.c new file mode 100644 index 00000000000..704941fb28e --- /dev/null +++ b/applications/debug/subghz_test/subghz_test_app.c @@ -0,0 +1,139 @@ +#include "subghz_test_app_i.h" + +#include +#include + +static bool subghz_test_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + SubGhzTestApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool subghz_test_app_back_event_callback(void* context) { + furi_assert(context); + SubGhzTestApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void subghz_test_app_tick_event_callback(void* context) { + furi_assert(context); + SubGhzTestApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +SubGhzTestApp* subghz_test_app_alloc() { + SubGhzTestApp* app = malloc(sizeof(SubGhzTestApp)); + + // GUI + app->gui = furi_record_open(RECORD_GUI); + + // View Dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&subghz_test_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, subghz_test_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, subghz_test_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, subghz_test_app_tick_event_callback, 100); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Open Notification record + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + // SubMenu + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, SubGhzTestViewSubmenu, submenu_get_view(app->submenu)); + + // Widget + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, SubGhzTestViewWidget, widget_get_view(app->widget)); + + // Popup + app->popup = popup_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, SubGhzTestViewPopup, popup_get_view(app->popup)); + + // Carrier Test Module + app->subghz_test_carrier = subghz_test_carrier_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + SubGhzTestViewCarrier, + subghz_test_carrier_get_view(app->subghz_test_carrier)); + + // Packet Test + app->subghz_test_packet = subghz_test_packet_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + SubGhzTestViewPacket, + subghz_test_packet_get_view(app->subghz_test_packet)); + + // Static send + app->subghz_test_static = subghz_test_static_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + SubGhzTestViewStatic, + subghz_test_static_get_view(app->subghz_test_static)); + + scene_manager_next_scene(app->scene_manager, SubGhzTestSceneStart); + + return app; +} + +void subghz_test_app_free(SubGhzTestApp* app) { + furi_assert(app); + + // Submenu + view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewSubmenu); + submenu_free(app->submenu); + + // Widget + view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewWidget); + widget_free(app->widget); + + // Popup + view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewPopup); + popup_free(app->popup); + + // Carrier Test + view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewCarrier); + subghz_test_carrier_free(app->subghz_test_carrier); + + // Packet Test + view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewPacket); + subghz_test_packet_free(app->subghz_test_packet); + + // Static + view_dispatcher_remove_view(app->view_dispatcher, SubGhzTestViewStatic); + subghz_test_static_free(app->subghz_test_static); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + + // Close records + furi_record_close(RECORD_GUI); + + free(app); +} + +int32_t subghz_test_app(void* p) { + UNUSED(p); + SubGhzTestApp* subghz_test_app = subghz_test_app_alloc(); + + view_dispatcher_run(subghz_test_app->view_dispatcher); + + subghz_test_app_free(subghz_test_app); + + return 0; +} diff --git a/applications/debug/subghz_test/subghz_test_app_i.c b/applications/debug/subghz_test/subghz_test_app_i.c new file mode 100644 index 00000000000..0ec6635a0eb --- /dev/null +++ b/applications/debug/subghz_test/subghz_test_app_i.c @@ -0,0 +1,5 @@ +#include "subghz_test_app_i.h" + +#include + +#define TAG "SubGhzTest" diff --git a/applications/debug/subghz_test/subghz_test_app_i.h b/applications/debug/subghz_test/subghz_test_app_i.h new file mode 100644 index 00000000000..c96f9c4ee4c --- /dev/null +++ b/applications/debug/subghz_test/subghz_test_app_i.h @@ -0,0 +1,32 @@ +#pragma once + +#include "helpers/subghz_test_types.h" +#include "helpers/subghz_test_event.h" + +#include "scenes/subghz_test_scene.h" +#include +#include +#include +#include +#include +#include +#include + +#include "views/subghz_test_static.h" +#include "views/subghz_test_carrier.h" +#include "views/subghz_test_packet.h" + +typedef struct SubGhzTestApp SubGhzTestApp; + +struct SubGhzTestApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + NotificationApp* notifications; + Submenu* submenu; + Widget* widget; + Popup* popup; + SubGhzTestStatic* subghz_test_static; + SubGhzTestCarrier* subghz_test_carrier; + SubGhzTestPacket* subghz_test_packet; +}; diff --git a/applications/subghz/views/subghz_test_carrier.c b/applications/debug/subghz_test/views/subghz_test_carrier.c similarity index 92% rename from applications/subghz/views/subghz_test_carrier.c rename to applications/debug/subghz_test/views/subghz_test_carrier.c index 6729eaad8ea..53e309b7c98 100644 --- a/applications/subghz/views/subghz_test_carrier.c +++ b/applications/debug/subghz_test/views/subghz_test_carrier.c @@ -1,6 +1,7 @@ #include "subghz_test_carrier.h" -#include "../subghz_i.h" -#include "../helpers/subghz_testing.h" +#include "../subghz_test_app_i.h" +#include "../helpers/subghz_test_frequency.h" +#include #include #include @@ -89,7 +90,9 @@ bool subghz_test_carrier_input(InputEvent* event, void* context) { } with_view_model( - subghz_test_carrier->view, (SubGhzTestCarrierModel * model) { + subghz_test_carrier->view, + SubGhzTestCarrierModel * model, + { furi_hal_subghz_idle(); if(event->key == InputKeyLeft) { @@ -125,9 +128,8 @@ bool subghz_test_carrier_input(InputEvent* event, void* context) { SubGhzTestCarrierEventOnlyRx, subghz_test_carrier->context); } } - - return true; - }); + }, + true); return true; } @@ -137,20 +139,22 @@ void subghz_test_carrier_enter(void* context) { SubGhzTestCarrier* subghz_test_carrier = context; furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); with_view_model( - subghz_test_carrier->view, (SubGhzTestCarrierModel * model) { + subghz_test_carrier->view, + SubGhzTestCarrierModel * model, + { model->frequency = subghz_frequencies_433_92_testing; // 433 model->real_frequency = furi_hal_subghz_set_frequency(subghz_frequencies_testing[model->frequency]); model->path = FuriHalSubGhzPathIsolate; // isolate model->rssi = 0.0f; model->status = SubGhzTestCarrierModelStatusRx; - return true; - }); + }, + true); furi_hal_subghz_rx(); @@ -172,13 +176,14 @@ void subghz_test_carrier_rssi_timer_callback(void* context) { SubGhzTestCarrier* subghz_test_carrier = context; with_view_model( - subghz_test_carrier->view, (SubGhzTestCarrierModel * model) { + subghz_test_carrier->view, + SubGhzTestCarrierModel * model, + { if(model->status == SubGhzTestCarrierModelStatusRx) { model->rssi = furi_hal_subghz_get_rssi(); - return true; } - return false; - }); + }, + false); } SubGhzTestCarrier* subghz_test_carrier_alloc() { diff --git a/applications/subghz/views/subghz_test_carrier.h b/applications/debug/subghz_test/views/subghz_test_carrier.h similarity index 100% rename from applications/subghz/views/subghz_test_carrier.h rename to applications/debug/subghz_test/views/subghz_test_carrier.h diff --git a/applications/subghz/views/subghz_test_packet.c b/applications/debug/subghz_test/views/subghz_test_packet.c similarity index 92% rename from applications/subghz/views/subghz_test_packet.c rename to applications/debug/subghz_test/views/subghz_test_packet.c index c83aebec9c3..1f345829617 100644 --- a/applications/subghz/views/subghz_test_packet.c +++ b/applications/debug/subghz_test/views/subghz_test_packet.c @@ -1,13 +1,14 @@ #include "subghz_test_packet.h" -#include "../subghz_i.h" -#include "../helpers/subghz_testing.h" +#include "../subghz_test_app_i.h" +#include "../helpers/subghz_test_frequency.h" +#include #include #include #include #include #include -#include +#include "../protocol/princeton_for_testing.h" #define SUBGHZ_TEST_PACKET_COUNT 500 @@ -55,7 +56,6 @@ static void subghz_test_packet_rx_callback(bool level, uint32_t duration, void* subghz_decoder_princeton_for_testing_parse(instance->decoder, level, duration); } -//todo static void subghz_test_packet_rx_pt_callback(SubGhzDecoderPrinceton* parser, void* context) { UNUSED(parser); furi_assert(context); @@ -68,7 +68,9 @@ static void subghz_test_packet_rssi_timer_callback(void* context) { SubGhzTestPacket* instance = context; with_view_model( - instance->view, (SubGhzTestPacketModel * model) { + instance->view, + SubGhzTestPacketModel * model, + { if(model->status == SubGhzTestPacketModelStatusRx) { model->rssi = furi_hal_subghz_get_rssi(); model->packets = instance->packet_rx; @@ -77,8 +79,8 @@ static void subghz_test_packet_rssi_timer_callback(void* context) { SUBGHZ_TEST_PACKET_COUNT - subghz_encoder_princeton_for_testing_get_repeat_left(instance->encoder); } - return true; - }); + }, + true); } static void subghz_test_packet_draw(Canvas* canvas, SubGhzTestPacketModel* model) { @@ -112,7 +114,7 @@ static void subghz_test_packet_draw(Canvas* canvas, SubGhzTestPacketModel* model snprintf(buffer, sizeof(buffer), "Path: %d - %s", model->path, path_name); canvas_draw_str(canvas, 0, 31, buffer); - snprintf(buffer, sizeof(buffer), "Packets: %d", model->packets); + snprintf(buffer, sizeof(buffer), "Packets: %zu", model->packets); canvas_draw_str(canvas, 0, 42, buffer); if(model->status == SubGhzTestPacketModelStatusRx) { @@ -137,7 +139,9 @@ static bool subghz_test_packet_input(InputEvent* event, void* context) { } with_view_model( - instance->view, (SubGhzTestPacketModel * model) { + instance->view, + SubGhzTestPacketModel * model, + { if(model->status == SubGhzTestPacketModelStatusRx) { furi_hal_subghz_stop_async_rx(); } else if(model->status == SubGhzTestPacketModelStatusTx) { @@ -179,9 +183,8 @@ static bool subghz_test_packet_input(InputEvent* event, void* context) { instance->callback(SubGhzTestPacketEventOnlyRx, instance->context); } } - - return true; - }); + }, + true); return true; } @@ -191,18 +194,20 @@ void subghz_test_packet_enter(void* context) { SubGhzTestPacket* instance = context; furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); with_view_model( - instance->view, (SubGhzTestPacketModel * model) { + instance->view, + SubGhzTestPacketModel * model, + { model->frequency = subghz_frequencies_433_92_testing; model->real_frequency = furi_hal_subghz_set_frequency(subghz_frequencies_testing[model->frequency]); model->path = FuriHalSubGhzPathIsolate; // isolate model->rssi = 0.0f; model->status = SubGhzTestPacketModelStatusRx; - return true; - }); + }, + true); furi_hal_subghz_start_async_rx(subghz_test_packet_rx_callback, instance); @@ -217,15 +222,17 @@ void subghz_test_packet_exit(void* context) { // Reinitialize IC to default state with_view_model( - instance->view, (SubGhzTestPacketModel * model) { + instance->view, + SubGhzTestPacketModel * model, + { if(model->status == SubGhzTestPacketModelStatusRx) { furi_hal_subghz_stop_async_rx(); } else if(model->status == SubGhzTestPacketModelStatusTx) { subghz_encoder_princeton_for_testing_stop(instance->encoder, furi_get_tick()); furi_hal_subghz_stop_async_tx(); } - return true; - }); + }, + true); furi_hal_subghz_sleep(); } diff --git a/applications/subghz/views/subghz_test_packet.h b/applications/debug/subghz_test/views/subghz_test_packet.h similarity index 100% rename from applications/subghz/views/subghz_test_packet.h rename to applications/debug/subghz_test/views/subghz_test_packet.h diff --git a/applications/subghz/views/subghz_test_static.c b/applications/debug/subghz_test/views/subghz_test_static.c similarity index 93% rename from applications/subghz/views/subghz_test_static.c rename to applications/debug/subghz_test/views/subghz_test_static.c index 7af54c3c05a..6764fd5ca9f 100644 --- a/applications/subghz/views/subghz_test_static.c +++ b/applications/debug/subghz_test/views/subghz_test_static.c @@ -1,13 +1,14 @@ #include "subghz_test_static.h" -#include "../subghz_i.h" -#include "../helpers/subghz_testing.h" +#include "../subghz_test_app_i.h" +#include "../helpers/subghz_test_frequency.h" +#include #include #include #include #include #include -#include +#include "../protocol/princeton_for_testing.h" #define TAG "SubGhzTestStatic" @@ -77,7 +78,9 @@ bool subghz_test_static_input(InputEvent* event, void* context) { } with_view_model( - instance->view, (SubGhzTestStaticModel * model) { + instance->view, + SubGhzTestStaticModel * model, + { if(event->type == InputTypeShort) { if(event->key == InputKeyLeft) { if(model->frequency > 0) model->frequency--; @@ -128,9 +131,8 @@ bool subghz_test_static_input(InputEvent* event, void* context) { } furi_record_close(RECORD_NOTIFICATION); } - - return true; - }); + }, + true); return true; } @@ -140,20 +142,21 @@ void subghz_test_static_enter(void* context) { SubGhzTestStatic* instance = context; furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); furi_hal_gpio_write(&gpio_cc1101_g0, false); instance->status_tx = SubGhzTestStaticStatusIDLE; with_view_model( - instance->view, (SubGhzTestStaticModel * model) { + instance->view, + SubGhzTestStaticModel * model, + { model->frequency = subghz_frequencies_433_92_testing; model->real_frequency = subghz_frequencies_testing[model->frequency]; model->button = 0; - - return true; - }); + }, + true); } void subghz_test_static_exit(void* context) { diff --git a/applications/subghz/views/subghz_test_static.h b/applications/debug/subghz_test/views/subghz_test_static.h similarity index 100% rename from applications/subghz/views/subghz_test_static.h rename to applications/debug/subghz_test/views/subghz_test_static.h diff --git a/applications/debug/text_box_test/application.fam b/applications/debug/text_box_test/application.fam new file mode 100644 index 00000000000..823c21d0688 --- /dev/null +++ b/applications/debug/text_box_test/application.fam @@ -0,0 +1,10 @@ +App( + appid="text_box_test", + name="Text Box Test", + apptype=FlipperAppType.DEBUG, + entry_point="text_box_test_app", + requires=["gui"], + stack_size=1 * 1024, + order=140, + fap_category="Debug", +) diff --git a/applications/debug_tools/text_box_test.c b/applications/debug/text_box_test/text_box_test.c similarity index 83% rename from applications/debug_tools/text_box_test.c rename to applications/debug/text_box_test/text_box_test.c index d7194ffee41..b980f686e18 100644 --- a/applications/debug_tools/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 new file mode 100644 index 00000000000..7b030bcfa68 --- /dev/null +++ b/applications/debug/uart_echo/application.fam @@ -0,0 +1,10 @@ +App( + appid="uart_echo", + name="UART Echo", + apptype=FlipperAppType.DEBUG, + entry_point="uart_echo_app", + requires=["gui"], + stack_size=2 * 1024, + order=70, + fap_category="Debug", +) diff --git a/applications/debug_tools/uart_echo.c b/applications/debug/uart_echo/uart_echo.c similarity index 77% rename from applications/debug_tools/uart_echo.c rename to applications/debug/uart_echo/uart_echo.c index 7a0f5c3933c..4bede9ab45f 100644 --- a/applications/debug_tools/uart_echo.c +++ b/applications/debug/uart_echo/uart_echo.c @@ -1,10 +1,8 @@ #include -#include #include #include #include #include -#include #include #include #include @@ -12,6 +10,8 @@ #define LINES_ON_SCREEN 6 #define COLUMNS_ON_SCREEN 21 +#define TAG "UartEcho" +#define DEFAULT_BAUD_RATE 230400 typedef struct UartDumpModel UartDumpModel; @@ -21,11 +21,11 @@ typedef struct { ViewDispatcher* view_dispatcher; View* view; FuriThread* worker_thread; - StreamBufferHandle_t rx_stream; + FuriStreamBuffer* rx_stream; } UartEchoApp; typedef struct { - string_t text; + FuriString* text; } ListElement; struct UartDumpModel { @@ -64,10 +64,11 @@ static void uart_echo_view_draw_callback(Canvas* canvas, void* _model) { canvas, 0, (i + 1) * (canvas_current_font_height(canvas) - 1), - string_get_cstr(model->list[i]->text)); + furi_string_get_cstr(model->list[i]->text)); if(i == model->line) { - uint8_t width = canvas_string_width(canvas, string_get_cstr(model->list[i]->text)); + uint8_t width = + canvas_string_width(canvas, furi_string_get_cstr(model->list[i]->text)); canvas_draw_box( canvas, @@ -92,13 +93,11 @@ static uint32_t uart_echo_exit(void* context) { static void uart_echo_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { furi_assert(context); - BaseType_t xHigherPriorityTaskWoken = pdFALSE; UartEchoApp* app = context; if(ev == UartIrqEventRXNE) { - xStreamBufferSendFromISR(app->rx_stream, &data, 1, &xHigherPriorityTaskWoken); + furi_stream_buffer_send(app->rx_stream, &data, 1, 0); furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventRx); - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } @@ -113,7 +112,7 @@ static void uart_echo_push_to_list(UartDumpModel* model, const char data) { model->escape = true; } else if((data >= ' ' && data <= '~') || (data == '\n' || data == '\r')) { bool new_string_needed = false; - if(string_size(model->list[model->line]->text) >= COLUMNS_ON_SCREEN) { + if(furi_string_size(model->list[model->line]->text) >= COLUMNS_ON_SCREEN) { new_string_needed = true; } else if((data == '\n' || data == '\r')) { // pack line breaks @@ -132,13 +131,13 @@ static void uart_echo_push_to_list(UartDumpModel* model, const char data) { model->list[i - 1] = model->list[i]; } - string_reset(first->text); + furi_string_reset(first->text); model->list[model->line] = first; } } if(data != '\n' && data != '\r') { - string_push_back(model->list[model->line]->text, data); + furi_string_push_back(model->list[model->line]->text, data); } } model->last_char = data; @@ -158,35 +157,34 @@ static int32_t uart_echo_worker(void* context) { size_t length = 0; do { uint8_t data[64]; - length = xStreamBufferReceive(app->rx_stream, data, 64, 0); + length = furi_stream_buffer_receive(app->rx_stream, data, 64, 0); if(length > 0) { furi_hal_uart_tx(FuriHalUartIdUSART1, data, length); with_view_model( - app->view, (UartDumpModel * model) { + app->view, + UartDumpModel * model, + { for(size_t i = 0; i < length; i++) { uart_echo_push_to_list(model, data[i]); } - return false; - }); + }, + false); } } while(length > 0); notification_message(app->notification, &sequence_notification); with_view_model( - app->view, (UartDumpModel * model) { - UNUSED(model); - return true; - }); + app->view, UartDumpModel * model, { UNUSED(model); }, true); } } return 0; } -static UartEchoApp* uart_echo_app_alloc() { +static UartEchoApp* uart_echo_app_alloc(uint32_t baudrate) { UartEchoApp* app = malloc(sizeof(UartEchoApp)); - app->rx_stream = xStreamBufferCreate(2048, 1); + app->rx_stream = furi_stream_buffer_alloc(2048, 1); // Gui app->gui = furi_record_open(RECORD_GUI); @@ -203,55 +201,55 @@ static UartEchoApp* uart_echo_app_alloc() { view_set_input_callback(app->view, uart_echo_view_input_callback); view_allocate_model(app->view, ViewModelTypeLocking, sizeof(UartDumpModel)); with_view_model( - app->view, (UartDumpModel * model) { + app->view, + UartDumpModel * model, + { for(size_t i = 0; i < LINES_ON_SCREEN; i++) { model->line = 0; model->escape = false; model->list[i] = malloc(sizeof(ListElement)); - string_init(model->list[i]->text); + model->list[i]->text = furi_string_alloc(); } - return true; - }); + }, + true); view_set_previous_callback(app->view, uart_echo_exit); view_dispatcher_add_view(app->view_dispatcher, 0, app->view); view_dispatcher_switch_to_view(app->view_dispatcher, 0); + app->worker_thread = furi_thread_alloc_ex("UsbUartWorker", 1024, uart_echo_worker, app); + furi_thread_start(app->worker_thread); + // Enable uart listener furi_hal_console_disable(); - furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200); + furi_hal_uart_set_br(FuriHalUartIdUSART1, baudrate); furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app); - app->worker_thread = furi_thread_alloc(); - furi_thread_set_name(app->worker_thread, "UsbUartWorker"); - furi_thread_set_stack_size(app->worker_thread, 1024); - furi_thread_set_context(app->worker_thread, app); - furi_thread_set_callback(app->worker_thread, uart_echo_worker); - furi_thread_start(app->worker_thread); - return app; } static void uart_echo_app_free(UartEchoApp* app) { furi_assert(app); + furi_hal_console_enable(); // this will also clear IRQ callback so thread is no longer referenced + furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventStop); furi_thread_join(app->worker_thread); furi_thread_free(app->worker_thread); - furi_hal_console_enable(); - // Free views view_dispatcher_remove_view(app->view_dispatcher, 0); with_view_model( - app->view, (UartDumpModel * model) { + app->view, + UartDumpModel * model, + { for(size_t i = 0; i < LINES_ON_SCREEN; i++) { - string_clear(model->list[i]->text); + furi_string_free(model->list[i]->text); free(model->list[i]); } - return true; - }); + }, + true); view_free(app->view); view_dispatcher_free(app->view_dispatcher); @@ -260,15 +258,25 @@ static void uart_echo_app_free(UartEchoApp* app) { furi_record_close(RECORD_NOTIFICATION); app->gui = NULL; - vStreamBufferDelete(app->rx_stream); + furi_stream_buffer_free(app->rx_stream); // Free rest free(app); } int32_t uart_echo_app(void* p) { - UNUSED(p); - UartEchoApp* app = uart_echo_app_alloc(); + uint32_t baudrate = DEFAULT_BAUD_RATE; + if(p) { + const char* baudrate_str = p; + if(sscanf(baudrate_str, "%lu", &baudrate) != 1) { + FURI_LOG_E(TAG, "Invalid baudrate: %s", baudrate_str); + baudrate = DEFAULT_BAUD_RATE; + } + } + + FURI_LOG_I(TAG, "Using baudrate: %lu", baudrate); + + UartEchoApp* app = uart_echo_app_alloc(baudrate); view_dispatcher_run(app->view_dispatcher); uart_echo_app_free(app); return 0; diff --git a/applications/debug/unit_tests/application.fam b/applications/debug/unit_tests/application.fam new file mode 100644 index 00000000000..aa25dab279b --- /dev/null +++ b/applications/debug/unit_tests/application.fam @@ -0,0 +1,20 @@ +App( + appid="unit_tests", + apptype=FlipperAppType.STARTUP, + entry_point="unit_tests_on_system_start", + cdefines=["APP_UNIT_TESTS"], + requires=["system_settings"], + provides=["delay_test"], + resources="resources", + order=100, +) + +App( + appid="delay_test", + name="Delay Test", + apptype=FlipperAppType.SYSTEM, + entry_point="delay_test_app", + stack_size=1 * 1024, + requires=["unit_tests"], + order=110, +) diff --git a/applications/debug/unit_tests/bt/bt_test.c b/applications/debug/unit_tests/bt/bt_test.c new file mode 100644 index 00000000000..32cf6533f88 --- /dev/null +++ b/applications/debug/unit_tests/bt/bt_test.c @@ -0,0 +1,110 @@ +#include +#include +#include "../minunit.h" + +#include +#include + +#define BT_TEST_KEY_STORAGE_FILE_PATH EXT_PATH("unit_tests/bt_test.keys") +#define BT_TEST_NVM_RAM_BUFF_SIZE (507 * 4) // The same as in ble NVM storage + +typedef struct { + Storage* storage; + BtKeysStorage* bt_keys_storage; + uint8_t* nvm_ram_buff_dut; + uint8_t* nvm_ram_buff_ref; +} BtTest; + +BtTest* bt_test = NULL; + +void bt_test_alloc() { + bt_test = malloc(sizeof(BtTest)); + bt_test->storage = furi_record_open(RECORD_STORAGE); + bt_test->nvm_ram_buff_dut = malloc(BT_TEST_NVM_RAM_BUFF_SIZE); + bt_test->nvm_ram_buff_ref = malloc(BT_TEST_NVM_RAM_BUFF_SIZE); + bt_test->bt_keys_storage = bt_keys_storage_alloc(BT_TEST_KEY_STORAGE_FILE_PATH); + bt_keys_storage_set_ram_params( + bt_test->bt_keys_storage, bt_test->nvm_ram_buff_dut, BT_TEST_NVM_RAM_BUFF_SIZE); +} + +void bt_test_free() { + furi_check(bt_test); + free(bt_test->nvm_ram_buff_ref); + free(bt_test->nvm_ram_buff_dut); + bt_keys_storage_free(bt_test->bt_keys_storage); + furi_record_close(RECORD_STORAGE); + free(bt_test); + bt_test = NULL; +} + +static void bt_test_keys_storage_profile() { + // Emulate nvm change on initial connection + const int nvm_change_size_on_connection = 88; + for(size_t i = 0; i < nvm_change_size_on_connection; i++) { + bt_test->nvm_ram_buff_dut[i] = rand(); + bt_test->nvm_ram_buff_ref[i] = bt_test->nvm_ram_buff_dut[i]; + } + // Emulate update storage on initial connect + mu_assert( + bt_keys_storage_update( + bt_test->bt_keys_storage, bt_test->nvm_ram_buff_dut, nvm_change_size_on_connection), + "Failed to update key storage on initial connect"); + memset(bt_test->nvm_ram_buff_dut, 0, BT_TEST_NVM_RAM_BUFF_SIZE); + mu_assert(bt_keys_storage_load(bt_test->bt_keys_storage), "Failed to load NVM"); + mu_assert( + memcmp( + bt_test->nvm_ram_buff_ref, bt_test->nvm_ram_buff_dut, nvm_change_size_on_connection) == + 0, + "Wrong buffer loaded"); + + const int nvm_disconnect_update_offset = 84; + const int nvm_disconnect_update_size = 324; + const int nvm_total_size = nvm_change_size_on_connection - + (nvm_change_size_on_connection - nvm_disconnect_update_offset) + + nvm_disconnect_update_size; + // Emulate update storage on initial disconnect + for(size_t i = nvm_disconnect_update_offset; + i < nvm_disconnect_update_offset + nvm_disconnect_update_size; + i++) { + bt_test->nvm_ram_buff_dut[i] = rand(); + bt_test->nvm_ram_buff_ref[i] = bt_test->nvm_ram_buff_dut[i]; + } + mu_assert( + bt_keys_storage_update( + bt_test->bt_keys_storage, + &bt_test->nvm_ram_buff_dut[nvm_disconnect_update_offset], + nvm_disconnect_update_size), + "Failed to update key storage on initial disconnect"); + memset(bt_test->nvm_ram_buff_dut, 0, BT_TEST_NVM_RAM_BUFF_SIZE); + mu_assert(bt_keys_storage_load(bt_test->bt_keys_storage), "Failed to load NVM"); + mu_assert( + memcmp(bt_test->nvm_ram_buff_ref, bt_test->nvm_ram_buff_dut, nvm_total_size) == 0, + "Wrong buffer loaded"); +} + +static void bt_test_keys_remove_test_file() { + mu_assert( + storage_simply_remove(bt_test->storage, BT_TEST_KEY_STORAGE_FILE_PATH), + "Can't remove test file"); +} + +MU_TEST(bt_test_keys_storage_serial_profile) { + furi_check(bt_test); + + bt_test_keys_remove_test_file(); + bt_test_keys_storage_profile(); + bt_test_keys_remove_test_file(); +} + +MU_TEST_SUITE(test_bt) { + bt_test_alloc(); + + MU_RUN_TEST(bt_test_keys_storage_serial_profile); + + bt_test_free(); +} + +int run_minunit_test_bt() { + MU_RUN_SUITE(test_bt); + return MU_EXIT_CODE; +} diff --git a/applications/debug/unit_tests/dialogs/dialogs_file_browser_options.c b/applications/debug/unit_tests/dialogs/dialogs_file_browser_options.c new file mode 100644 index 00000000000..2d5bad4c8a5 --- /dev/null +++ b/applications/debug/unit_tests/dialogs/dialogs_file_browser_options.c @@ -0,0 +1,32 @@ +#include + +#include "../minunit.h" + +MU_TEST(test_dialog_file_browser_set_basic_options_should_init_all_fields) { + mu_assert( + sizeof(DialogsFileBrowserOptions) == 28, + "Changes to `DialogsFileBrowserOptions` should also be reflected in `dialog_file_browser_set_basic_options`"); + + DialogsFileBrowserOptions options; + dialog_file_browser_set_basic_options(&options, ".fap", NULL); + // note: this assertions can safely be changed, their primary purpose is to remind the maintainer + // to update `dialog_file_browser_set_basic_options` by including all structure fields in it + mu_assert_string_eq(".fap", options.extension); + mu_assert_null(options.base_path); + mu_assert(options.skip_assets, "`skip_assets` should default to `true"); + mu_assert(options.hide_dot_files, "`hide_dot_files` should default to `true"); + mu_assert_null(options.icon); + mu_assert(options.hide_ext, "`hide_ext` should default to `true"); + mu_assert_null(options.item_loader_callback); + mu_assert_null(options.item_loader_context); +} + +MU_TEST_SUITE(dialogs_file_browser_options) { + MU_RUN_TEST(test_dialog_file_browser_set_basic_options_should_init_all_fields); +} + +int run_minunit_test_dialogs_file_browser_options() { + MU_RUN_SUITE(dialogs_file_browser_options); + + return MU_EXIT_CODE; +} diff --git a/applications/unit_tests/flipper_format/flipper_format_string_test.c b/applications/debug/unit_tests/flipper_format/flipper_format_string_test.c similarity index 95% rename from applications/unit_tests/flipper_format/flipper_format_string_test.c rename to applications/debug/unit_tests/flipper_format/flipper_format_string_test.c index b22b333a394..920a22a43bf 100644 --- a/applications/unit_tests/flipper_format/flipper_format_string_test.c +++ b/applications/debug/unit_tests/flipper_format/flipper_format_string_test.c @@ -58,7 +58,7 @@ static const char* test_data_win = "Filetype: Flipper Format test\r\n" #define ARRAY_W_BSIZE(x) (x), (sizeof(x)) MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) { - string_t tmpstr; + FuriString* tmpstr; uint32_t version; uint32_t uint32_data[COUNT_OF(test_uint_data)]; int32_t int32_data[COUNT_OF(test_int_data)]; @@ -101,14 +101,14 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) { mu_assert_int_eq(position_before, stream_tell(flipper_format_get_raw_stream(flipper_format))); // read test - string_init(tmpstr); + tmpstr = furi_string_alloc(); mu_check(flipper_format_read_header(flipper_format, tmpstr, &version)); - mu_assert_string_eq(test_filetype, string_get_cstr(tmpstr)); + mu_assert_string_eq(test_filetype, furi_string_get_cstr(tmpstr)); mu_assert_int_eq(test_version, version); mu_check(flipper_format_read_string(flipper_format, test_string_key, tmpstr)); - mu_assert_string_eq(test_string_data, string_get_cstr(tmpstr)); + mu_assert_string_eq(test_string_data, furi_string_get_cstr(tmpstr)); mu_check(flipper_format_get_value_count(flipper_format, test_int_key, &count)); mu_assert_int_eq(COUNT_OF(test_int_data), count); @@ -133,7 +133,7 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) { mu_check(!flipper_format_read_string(flipper_format, "Key that doesn't exist", tmpstr)); - string_clear(tmpstr); + furi_string_free(tmpstr); // update data mu_check(flipper_format_rewind(flipper_format)); @@ -155,14 +155,14 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) { uint8_t hex_updated_data[COUNT_OF(test_hex_updated_data)]; mu_check(flipper_format_rewind(flipper_format)); - string_init(tmpstr); + tmpstr = furi_string_alloc(); mu_check(flipper_format_read_header(flipper_format, tmpstr, &version)); - mu_assert_string_eq(test_filetype, string_get_cstr(tmpstr)); + mu_assert_string_eq(test_filetype, furi_string_get_cstr(tmpstr)); mu_assert_int_eq(test_version, version); mu_check(flipper_format_read_string(flipper_format, test_string_key, tmpstr)); - mu_assert_string_eq(test_string_updated_data, string_get_cstr(tmpstr)); + mu_assert_string_eq(test_string_updated_data, furi_string_get_cstr(tmpstr)); mu_check(flipper_format_get_value_count(flipper_format, test_int_key, &count)); mu_assert_int_eq(COUNT_OF(test_int_updated_data), count); @@ -190,7 +190,7 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) { mu_check(!flipper_format_read_string(flipper_format, "Key that doesn't exist", tmpstr)); - string_clear(tmpstr); + furi_string_free(tmpstr); // update data mu_check(flipper_format_rewind(flipper_format)); @@ -214,14 +214,14 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) { uint8_t hex_new_data[COUNT_OF(test_hex_new_data)]; mu_check(flipper_format_rewind(flipper_format)); - string_init(tmpstr); + tmpstr = furi_string_alloc(); mu_check(flipper_format_read_header(flipper_format, tmpstr, &version)); - mu_assert_string_eq(test_filetype, string_get_cstr(tmpstr)); + mu_assert_string_eq(test_filetype, furi_string_get_cstr(tmpstr)); mu_assert_int_eq(test_version, version); mu_check(flipper_format_read_string(flipper_format, test_string_key, tmpstr)); - mu_assert_string_eq(test_string_updated_2_data, string_get_cstr(tmpstr)); + mu_assert_string_eq(test_string_updated_2_data, furi_string_get_cstr(tmpstr)); mu_check(flipper_format_get_value_count(flipper_format, test_int_key, &count)); mu_assert_int_eq(COUNT_OF(test_int_updated_2_data), count); @@ -255,7 +255,7 @@ MU_TEST_1(flipper_format_read_and_update_test, FlipperFormat* flipper_format) { mu_check(!flipper_format_read_string(flipper_format, "Key that doesn't exist", tmpstr)); - string_clear(tmpstr); + furi_string_free(tmpstr); // delete key test mu_check(flipper_format_rewind(flipper_format)); diff --git a/applications/unit_tests/flipper_format/flipper_format_test.c b/applications/debug/unit_tests/flipper_format/flipper_format_test.c similarity index 90% rename from applications/unit_tests/flipper_format/flipper_format_test.c rename to applications/debug/unit_tests/flipper_format/flipper_format_test.c index 86e2df21eff..012e905b690 100644 --- a/applications/unit_tests/flipper_format/flipper_format_test.c +++ b/applications/debug/unit_tests/flipper_format/flipper_format_test.c @@ -57,6 +57,23 @@ static const char* test_data_win = "Filetype: Flipper File test\r\n" "Hex data: DE AD BE"; #define READ_TEST_FLP "ff_flp.test" +#define READ_TEST_ODD "ff_oddities.test" +static const char* test_data_odd = "Filetype: Flipper File test\n" + // Tabs before newline + "Version: 666\t\t\n" + "# This is comment\n" + // Windows newline in a UNIX file + "String data: String\r\n" + // Trailing whitespace + "Int32 data: 1234 -6345 7813 0 \n" + // Extra whitespace + "Uint32 data: 1234 0 5678 9098 7654321 \n" + // Mixed whitespace + "Float data: 1.5\t \t1000.0\n" + // Leading tabs after key + "Bool data:\t\ttrue false\n" + // Mixed trailing whitespace + "Hex data: DE AD BE\t "; // data created by user on linux machine static const char* test_file_linux = TEST_DIR READ_TEST_NIX; @@ -64,6 +81,8 @@ static const char* test_file_linux = TEST_DIR READ_TEST_NIX; static const char* test_file_windows = TEST_DIR READ_TEST_WIN; // data created by flipper itself static const char* test_file_flipper = TEST_DIR READ_TEST_FLP; +// data containing odd user input +static const char* test_file_oddities = TEST_DIR READ_TEST_ODD; static bool storage_write_string(const char* path, const char* data) { Storage* storage = furi_record_open(RECORD_STORAGE); @@ -102,8 +121,8 @@ static bool test_read(const char* file_name) { bool result = false; FlipperFormat* file = flipper_format_file_alloc(storage); - string_t string_value; - string_init(string_value); + FuriString* string_value; + string_value = furi_string_alloc(); uint32_t uint32_value; void* scratchpad = malloc(512); @@ -111,11 +130,11 @@ static bool test_read(const char* file_name) { if(!flipper_format_file_open_existing(file, file_name)) break; if(!flipper_format_read_header(file, string_value, &uint32_value)) break; - if(string_cmp_str(string_value, test_filetype) != 0) break; + if(furi_string_cmp_str(string_value, test_filetype) != 0) break; if(uint32_value != test_version) break; if(!flipper_format_read_string(file, test_string_key, string_value)) break; - if(string_cmp_str(string_value, test_string_data) != 0) break; + if(furi_string_cmp_str(string_value, test_string_data) != 0) break; if(!flipper_format_get_value_count(file, test_int_key, &uint32_value)) break; if(uint32_value != COUNT_OF(test_int_data)) break; @@ -150,7 +169,7 @@ static bool test_read(const char* file_name) { } while(false); free(scratchpad); - string_clear(string_value); + furi_string_free(string_value); flipper_format_free(file); @@ -164,8 +183,8 @@ static bool test_read_updated(const char* file_name) { bool result = false; FlipperFormat* file = flipper_format_file_alloc(storage); - string_t string_value; - string_init(string_value); + FuriString* string_value; + string_value = furi_string_alloc(); uint32_t uint32_value; void* scratchpad = malloc(512); @@ -173,11 +192,11 @@ static bool test_read_updated(const char* file_name) { if(!flipper_format_file_open_existing(file, file_name)) break; if(!flipper_format_read_header(file, string_value, &uint32_value)) break; - if(string_cmp_str(string_value, test_filetype) != 0) break; + if(furi_string_cmp_str(string_value, test_filetype) != 0) break; if(uint32_value != test_version) break; if(!flipper_format_read_string(file, test_string_key, string_value)) break; - if(string_cmp_str(string_value, test_string_updated_data) != 0) break; + if(furi_string_cmp_str(string_value, test_string_updated_data) != 0) break; if(!flipper_format_get_value_count(file, test_int_key, &uint32_value)) break; if(uint32_value != COUNT_OF(test_int_updated_data)) break; @@ -228,7 +247,7 @@ static bool test_read_updated(const char* file_name) { } while(false); free(scratchpad); - string_clear(string_value); + furi_string_free(string_value); flipper_format_free(file); @@ -401,14 +420,14 @@ static bool test_read_multikey(const char* file_name) { bool result = false; FlipperFormat* file = flipper_format_file_alloc(storage); - string_t string_value; - string_init(string_value); + FuriString* string_value; + string_value = furi_string_alloc(); uint32_t uint32_value; do { if(!flipper_format_file_open_existing(file, file_name)) break; if(!flipper_format_read_header(file, string_value, &uint32_value)) break; - if(string_cmp_str(string_value, test_filetype) != 0) break; + if(furi_string_cmp_str(string_value, test_filetype) != 0) break; if(uint32_value != test_version) break; bool error = false; @@ -429,7 +448,7 @@ static bool test_read_multikey(const char* file_name) { result = true; } while(false); - string_clear(string_value); + furi_string_free(string_value); flipper_format_free(file); furi_record_close(RECORD_STORAGE); @@ -503,6 +522,12 @@ MU_TEST(flipper_format_multikey_test) { mu_assert(test_read_multikey(TEST_DIR "ff_multiline.test"), "Multikey read test error"); } +MU_TEST(flipper_format_oddities_test) { + mu_assert( + storage_write_string(test_file_oddities, test_data_odd), "Write test error [Oddities]"); + mu_assert(test_read(test_file_linux), "Read test error [Oddities]"); +} + MU_TEST_SUITE(flipper_format) { tests_setup(); MU_RUN_TEST(flipper_format_write_test); @@ -516,6 +541,7 @@ MU_TEST_SUITE(flipper_format) { MU_RUN_TEST(flipper_format_update_2_test); MU_RUN_TEST(flipper_format_update_2_result_test); MU_RUN_TEST(flipper_format_multikey_test); + MU_RUN_TEST(flipper_format_oddities_test); tests_teardown(); } diff --git a/applications/debug/unit_tests/float_tools/float_tools_test.c b/applications/debug/unit_tests/float_tools/float_tools_test.c new file mode 100644 index 00000000000..fc5b4ecfd8b --- /dev/null +++ b/applications/debug/unit_tests/float_tools/float_tools_test.c @@ -0,0 +1,60 @@ +#include +#include + +#include "../minunit.h" + +MU_TEST(float_tools_equal_test) { + mu_check(float_is_equal(FLT_MAX, FLT_MAX)); + mu_check(float_is_equal(FLT_MIN, FLT_MIN)); + mu_check(float_is_equal(-FLT_MAX, -FLT_MAX)); + mu_check(float_is_equal(-FLT_MIN, -FLT_MIN)); + + mu_check(!float_is_equal(FLT_MIN, FLT_MAX)); + mu_check(!float_is_equal(-FLT_MIN, FLT_MAX)); + mu_check(!float_is_equal(FLT_MIN, -FLT_MAX)); + mu_check(!float_is_equal(-FLT_MIN, -FLT_MAX)); + + const float pi = 3.14159f; + mu_check(float_is_equal(pi, pi)); + mu_check(float_is_equal(-pi, -pi)); + mu_check(!float_is_equal(pi, -pi)); + mu_check(!float_is_equal(-pi, pi)); + + const float one_third = 1.f / 3.f; + const float one_third_dec = 0.3333333f; + mu_check(one_third != one_third_dec); + mu_check(float_is_equal(one_third, one_third_dec)); + + const float big_num = 1.e12f; + const float med_num = 95.389f; + const float smol_num = 1.e-12f; + mu_check(float_is_equal(big_num, big_num)); + mu_check(float_is_equal(med_num, med_num)); + mu_check(float_is_equal(smol_num, smol_num)); + mu_check(!float_is_equal(smol_num, big_num)); + mu_check(!float_is_equal(med_num, smol_num)); + mu_check(!float_is_equal(big_num, med_num)); + + const float more_than_one = 1.f + FLT_EPSILON; + const float less_than_one = 1.f - FLT_EPSILON; + mu_check(!float_is_equal(more_than_one, less_than_one)); + mu_check(!float_is_equal(more_than_one, -less_than_one)); + mu_check(!float_is_equal(-more_than_one, less_than_one)); + mu_check(!float_is_equal(-more_than_one, -less_than_one)); + + const float slightly_more_than_one = 1.f + FLT_EPSILON / 2.f; + const float slightly_less_than_one = 1.f - FLT_EPSILON / 2.f; + mu_check(float_is_equal(slightly_more_than_one, slightly_less_than_one)); + mu_check(float_is_equal(-slightly_more_than_one, -slightly_less_than_one)); + mu_check(!float_is_equal(slightly_more_than_one, -slightly_less_than_one)); + mu_check(!float_is_equal(-slightly_more_than_one, slightly_less_than_one)); +} + +MU_TEST_SUITE(float_tools_suite) { + MU_RUN_TEST(float_tools_equal_test); +} + +int run_minunit_test_float_tools() { + MU_RUN_SUITE(float_tools_suite); + return MU_EXIT_CODE; +} diff --git a/applications/debug/unit_tests/furi/furi_memmgr_test.c b/applications/debug/unit_tests/furi/furi_memmgr_test.c new file mode 100644 index 00000000000..a28632cf4da --- /dev/null +++ b/applications/debug/unit_tests/furi/furi_memmgr_test.c @@ -0,0 +1,38 @@ +#include "../minunit.h" +#include +#include +#include + +void test_furi_memmgr() { + void* ptr; + + // allocate memory case + ptr = malloc(100); + mu_check(ptr != NULL); + // test that memory is zero-initialized after allocation + for(int i = 0; i < 100; i++) { + mu_assert_int_eq(0, ((uint8_t*)ptr)[i]); + } + free(ptr); + + // reallocate memory case + ptr = malloc(100); + memset(ptr, 66, 100); + ptr = realloc(ptr, 200); + mu_check(ptr != NULL); + + // test that memory is really reallocated + for(int i = 0; i < 100; i++) { + mu_assert_int_eq(66, ((uint8_t*)ptr)[i]); + } + + free(ptr); + + // allocate and zero-initialize array (calloc) + ptr = calloc(100, 2); + mu_check(ptr != NULL); + for(int i = 0; i < 100 * 2; i++) { + mu_assert_int_eq(0, ((uint8_t*)ptr)[i]); + } + free(ptr); +} diff --git a/applications/unit_tests/furi/furi_pubsub_test.c b/applications/debug/unit_tests/furi/furi_pubsub_test.c similarity index 100% rename from applications/unit_tests/furi/furi_pubsub_test.c rename to applications/debug/unit_tests/furi/furi_pubsub_test.c diff --git a/applications/debug/unit_tests/furi/furi_record_test.c b/applications/debug/unit_tests/furi/furi_record_test.c new file mode 100644 index 00000000000..236e1efc56b --- /dev/null +++ b/applications/debug/unit_tests/furi/furi_record_test.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include "../minunit.h" + +#define TEST_RECORD_NAME "test/holding" + +void test_furi_create_open() { + // Test that record does not exist + mu_check(furi_record_exists(TEST_RECORD_NAME) == false); + + // Create record + uint8_t test_data = 0; + furi_record_create(TEST_RECORD_NAME, (void*)&test_data); + + // Test that record exists + mu_check(furi_record_exists(TEST_RECORD_NAME) == true); + + // Open it + void* record = furi_record_open(TEST_RECORD_NAME); + mu_assert_pointers_eq(record, &test_data); + + // Close it + furi_record_close(TEST_RECORD_NAME); + + // Clean up + furi_record_destroy(TEST_RECORD_NAME); + + // Test that record does not exist + mu_check(furi_record_exists(TEST_RECORD_NAME) == false); +} diff --git a/applications/debug/unit_tests/furi/furi_string_test.c b/applications/debug/unit_tests/furi/furi_string_test.c new file mode 100644 index 00000000000..6cbcc0dcc7a --- /dev/null +++ b/applications/debug/unit_tests/furi/furi_string_test.c @@ -0,0 +1,469 @@ +#include +#include "../minunit.h" + +static void test_setup(void) { +} + +static void test_teardown(void) { +} + +static FuriString* furi_string_alloc_vprintf_test(const char format[], ...) { + va_list args; + va_start(args, format); + FuriString* string = furi_string_alloc_vprintf(format, args); + va_end(args); + return string; +} + +MU_TEST(mu_test_furi_string_alloc_free) { + FuriString* tmp; + FuriString* string; + + // test alloc and free + string = furi_string_alloc(); + mu_check(string != NULL); + mu_check(furi_string_empty(string)); + furi_string_free(string); + + // test furi_string_alloc_set_str and free + string = furi_string_alloc_set_str("test"); + mu_check(string != NULL); + mu_check(!furi_string_empty(string)); + mu_check(furi_string_cmp(string, "test") == 0); + furi_string_free(string); + + // test furi_string_alloc_set and free + tmp = furi_string_alloc_set("more"); + string = furi_string_alloc_set(tmp); + furi_string_free(tmp); + mu_check(string != NULL); + mu_check(!furi_string_empty(string)); + mu_check(furi_string_cmp(string, "more") == 0); + furi_string_free(string); + + // test alloc_printf and free + string = furi_string_alloc_printf("test %d %s %c 0x%02x", 1, "two", '3', 0x04); + mu_check(string != NULL); + mu_check(!furi_string_empty(string)); + mu_check(furi_string_cmp(string, "test 1 two 3 0x04") == 0); + furi_string_free(string); + + // test alloc_vprintf and free + string = furi_string_alloc_vprintf_test("test %d %s %c 0x%02x", 4, "five", '6', 0x07); + mu_check(string != NULL); + mu_check(!furi_string_empty(string)); + mu_check(furi_string_cmp(string, "test 4 five 6 0x07") == 0); + furi_string_free(string); + + // test alloc_move and free + tmp = furi_string_alloc_set("move"); + string = furi_string_alloc_move(tmp); + mu_check(string != NULL); + mu_check(!furi_string_empty(string)); + mu_check(furi_string_cmp(string, "move") == 0); + furi_string_free(string); +} + +MU_TEST(mu_test_furi_string_mem) { + FuriString* string = furi_string_alloc_set("test"); + mu_check(string != NULL); + mu_check(!furi_string_empty(string)); + + // TODO FL-3493: how to test furi_string_reserve? + + // test furi_string_reset + furi_string_reset(string); + mu_check(furi_string_empty(string)); + + // test furi_string_swap + furi_string_set(string, "test"); + FuriString* swap_string = furi_string_alloc_set("swap"); + furi_string_swap(string, swap_string); + mu_check(furi_string_cmp(string, "swap") == 0); + mu_check(furi_string_cmp(swap_string, "test") == 0); + furi_string_free(swap_string); + + // test furi_string_move + FuriString* move_string = furi_string_alloc_set("move"); + furi_string_move(string, move_string); + mu_check(furi_string_cmp(string, "move") == 0); + // move_string is now empty + // and tested by leaked memory check at the end of the tests + + furi_string_set(string, "abracadabra"); + + // test furi_string_hash + mu_assert_int_eq(0xc3bc16d7, furi_string_hash(string)); + + // test furi_string_size + mu_assert_int_eq(11, furi_string_size(string)); + + // test furi_string_empty + mu_check(!furi_string_empty(string)); + furi_string_reset(string); + mu_check(furi_string_empty(string)); + + furi_string_free(string); +} + +MU_TEST(mu_test_furi_string_getters) { + FuriString* string = furi_string_alloc_set("test"); + + // test furi_string_get_char + mu_check(furi_string_get_char(string, 0) == 't'); + mu_check(furi_string_get_char(string, 1) == 'e'); + mu_check(furi_string_get_char(string, 2) == 's'); + mu_check(furi_string_get_char(string, 3) == 't'); + + // test furi_string_get_cstr + mu_assert_string_eq("test", furi_string_get_cstr(string)); + furi_string_free(string); +} + +static FuriString* furi_string_vprintf_test(FuriString* string, const char format[], ...) { + va_list args; + va_start(args, format); + furi_string_vprintf(string, format, args); + va_end(args); + return string; +} + +MU_TEST(mu_test_furi_string_setters) { + FuriString* tmp; + FuriString* string = furi_string_alloc(); + + // test furi_string_set_str + furi_string_set_str(string, "test"); + mu_assert_string_eq("test", furi_string_get_cstr(string)); + + // test furi_string_set + tmp = furi_string_alloc_set("more"); + furi_string_set(string, tmp); + furi_string_free(tmp); + mu_assert_string_eq("more", furi_string_get_cstr(string)); + + // test furi_string_set_strn + furi_string_set_strn(string, "test", 2); + mu_assert_string_eq("te", furi_string_get_cstr(string)); + + // test furi_string_set_char + furi_string_set_char(string, 0, 'a'); + furi_string_set_char(string, 1, 'b'); + mu_assert_string_eq("ab", furi_string_get_cstr(string)); + + // test furi_string_set_n + tmp = furi_string_alloc_set("dodecahedron"); + furi_string_set_n(string, tmp, 4, 5); + furi_string_free(tmp); + mu_assert_string_eq("cahed", furi_string_get_cstr(string)); + + // test furi_string_printf + furi_string_printf(string, "test %d %s %c 0x%02x", 1, "two", '3', 0x04); + mu_assert_string_eq("test 1 two 3 0x04", furi_string_get_cstr(string)); + + // test furi_string_vprintf + furi_string_vprintf_test(string, "test %d %s %c 0x%02x", 4, "five", '6', 0x07); + mu_assert_string_eq("test 4 five 6 0x07", furi_string_get_cstr(string)); + + furi_string_free(string); +} + +static FuriString* furi_string_cat_vprintf_test(FuriString* string, const char format[], ...) { + va_list args; + va_start(args, format); + furi_string_cat_vprintf(string, format, args); + va_end(args); + return string; +} + +MU_TEST(mu_test_furi_string_appends) { + FuriString* tmp; + FuriString* string = furi_string_alloc(); + + // test furi_string_push_back + furi_string_push_back(string, 't'); + furi_string_push_back(string, 'e'); + furi_string_push_back(string, 's'); + furi_string_push_back(string, 't'); + mu_assert_string_eq("test", furi_string_get_cstr(string)); + furi_string_push_back(string, '!'); + mu_assert_string_eq("test!", furi_string_get_cstr(string)); + + // test furi_string_cat_str + furi_string_cat_str(string, "test"); + mu_assert_string_eq("test!test", furi_string_get_cstr(string)); + + // test furi_string_cat + tmp = furi_string_alloc_set("more"); + furi_string_cat(string, tmp); + furi_string_free(tmp); + mu_assert_string_eq("test!testmore", furi_string_get_cstr(string)); + + // test furi_string_cat_printf + furi_string_cat_printf(string, "test %d %s %c 0x%02x", 1, "two", '3', 0x04); + mu_assert_string_eq("test!testmoretest 1 two 3 0x04", furi_string_get_cstr(string)); + + // test furi_string_cat_vprintf + furi_string_cat_vprintf_test(string, "test %d %s %c 0x%02x", 4, "five", '6', 0x07); + mu_assert_string_eq( + "test!testmoretest 1 two 3 0x04test 4 five 6 0x07", furi_string_get_cstr(string)); + + furi_string_free(string); +} + +MU_TEST(mu_test_furi_string_compare) { + FuriString* string_1 = furi_string_alloc_set("string_1"); + FuriString* string_2 = furi_string_alloc_set("string_2"); + + // test furi_string_cmp + mu_assert_int_eq(0, furi_string_cmp(string_1, string_1)); + mu_assert_int_eq(0, furi_string_cmp(string_2, string_2)); + mu_assert_int_eq(-1, furi_string_cmp(string_1, string_2)); + mu_assert_int_eq(1, furi_string_cmp(string_2, string_1)); + + // test furi_string_cmp_str + mu_assert_int_eq(0, furi_string_cmp_str(string_1, "string_1")); + mu_assert_int_eq(0, furi_string_cmp_str(string_2, "string_2")); + mu_assert_int_eq(-1, furi_string_cmp_str(string_1, "string_2")); + mu_assert_int_eq(1, furi_string_cmp_str(string_2, "string_1")); + + // test furi_string_cmpi + furi_string_set(string_1, "string"); + furi_string_set(string_2, "StrIng"); + mu_assert_int_eq(0, furi_string_cmpi(string_1, string_1)); + mu_assert_int_eq(0, furi_string_cmpi(string_2, string_2)); + mu_assert_int_eq(0, furi_string_cmpi(string_1, string_2)); + mu_assert_int_eq(0, furi_string_cmpi(string_2, string_1)); + furi_string_set(string_1, "string_1"); + furi_string_set(string_2, "StrIng_2"); + mu_assert_int_eq(32, furi_string_cmp(string_1, string_2)); + mu_assert_int_eq(-32, furi_string_cmp(string_2, string_1)); + mu_assert_int_eq(-1, furi_string_cmpi(string_1, string_2)); + mu_assert_int_eq(1, furi_string_cmpi(string_2, string_1)); + + // test furi_string_cmpi_str + furi_string_set(string_1, "string"); + mu_assert_int_eq(0, furi_string_cmp_str(string_1, "string")); + mu_assert_int_eq(32, furi_string_cmp_str(string_1, "String")); + mu_assert_int_eq(32, furi_string_cmp_str(string_1, "STring")); + mu_assert_int_eq(32, furi_string_cmp_str(string_1, "STRing")); + mu_assert_int_eq(32, furi_string_cmp_str(string_1, "STRIng")); + mu_assert_int_eq(32, furi_string_cmp_str(string_1, "STRINg")); + mu_assert_int_eq(32, furi_string_cmp_str(string_1, "STRING")); + mu_assert_int_eq(0, furi_string_cmpi_str(string_1, "string")); + mu_assert_int_eq(0, furi_string_cmpi_str(string_1, "String")); + mu_assert_int_eq(0, furi_string_cmpi_str(string_1, "STring")); + mu_assert_int_eq(0, furi_string_cmpi_str(string_1, "STRing")); + mu_assert_int_eq(0, furi_string_cmpi_str(string_1, "STRIng")); + mu_assert_int_eq(0, furi_string_cmpi_str(string_1, "STRINg")); + mu_assert_int_eq(0, furi_string_cmpi_str(string_1, "STRING")); + + furi_string_free(string_1); + furi_string_free(string_2); +} + +MU_TEST(mu_test_furi_string_search) { + // 012345678901234567 + FuriString* haystack = furi_string_alloc_set("test321test123test"); + FuriString* needle = furi_string_alloc_set("test"); + + // test furi_string_search + mu_assert_int_eq(0, furi_string_search(haystack, needle)); + mu_assert_int_eq(7, furi_string_search(haystack, needle, 1)); + mu_assert_int_eq(14, furi_string_search(haystack, needle, 8)); + mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search(haystack, needle, 15)); + + FuriString* tmp = furi_string_alloc_set("testnone"); + mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search(haystack, tmp)); + furi_string_free(tmp); + + // test furi_string_search_str + mu_assert_int_eq(0, furi_string_search_str(haystack, "test")); + mu_assert_int_eq(7, furi_string_search_str(haystack, "test", 1)); + mu_assert_int_eq(14, furi_string_search_str(haystack, "test", 8)); + mu_assert_int_eq(4, furi_string_search_str(haystack, "321")); + mu_assert_int_eq(11, furi_string_search_str(haystack, "123")); + mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search_str(haystack, "testnone")); + mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search_str(haystack, "test", 15)); + + // test furi_string_search_char + mu_assert_int_eq(0, furi_string_search_char(haystack, 't')); + mu_assert_int_eq(1, furi_string_search_char(haystack, 'e')); + mu_assert_int_eq(2, furi_string_search_char(haystack, 's')); + mu_assert_int_eq(3, furi_string_search_char(haystack, 't', 1)); + mu_assert_int_eq(7, furi_string_search_char(haystack, 't', 4)); + mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search_char(haystack, 'x')); + + // test furi_string_search_rchar + mu_assert_int_eq(17, furi_string_search_rchar(haystack, 't')); + mu_assert_int_eq(15, furi_string_search_rchar(haystack, 'e')); + mu_assert_int_eq(16, furi_string_search_rchar(haystack, 's')); + mu_assert_int_eq(13, furi_string_search_rchar(haystack, '3')); + mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search_rchar(haystack, '3', 14)); + mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_search_rchar(haystack, 'x')); + + furi_string_free(haystack); + furi_string_free(needle); +} + +MU_TEST(mu_test_furi_string_equality) { + FuriString* string = furi_string_alloc_set("test"); + FuriString* string_eq = furi_string_alloc_set("test"); + FuriString* string_neq = furi_string_alloc_set("test2"); + + // test furi_string_equal + mu_check(furi_string_equal(string, string_eq)); + mu_check(!furi_string_equal(string, string_neq)); + + // test furi_string_equal_str + mu_check(furi_string_equal_str(string, "test")); + mu_check(!furi_string_equal_str(string, "test2")); + mu_check(furi_string_equal_str(string_neq, "test2")); + mu_check(!furi_string_equal_str(string_neq, "test")); + + furi_string_free(string); + furi_string_free(string_eq); + furi_string_free(string_neq); +} + +MU_TEST(mu_test_furi_string_replace) { + FuriString* needle = furi_string_alloc_set("test"); + FuriString* replace = furi_string_alloc_set("replace"); + FuriString* string = furi_string_alloc_set("test123test"); + + // test furi_string_replace_at + furi_string_replace_at(string, 4, 3, "!biglongword!"); + mu_assert_string_eq("test!biglongword!test", furi_string_get_cstr(string)); + + // test furi_string_replace + mu_assert_int_eq(17, furi_string_replace(string, needle, replace, 1)); + mu_assert_string_eq("test!biglongword!replace", furi_string_get_cstr(string)); + mu_assert_int_eq(0, furi_string_replace(string, needle, replace)); + mu_assert_string_eq("replace!biglongword!replace", furi_string_get_cstr(string)); + mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_replace(string, needle, replace)); + mu_assert_string_eq("replace!biglongword!replace", furi_string_get_cstr(string)); + + // test furi_string_replace_str + mu_assert_int_eq(20, furi_string_replace_str(string, "replace", "test", 1)); + mu_assert_string_eq("replace!biglongword!test", furi_string_get_cstr(string)); + mu_assert_int_eq(0, furi_string_replace_str(string, "replace", "test")); + mu_assert_string_eq("test!biglongword!test", furi_string_get_cstr(string)); + mu_assert_int_eq(FURI_STRING_FAILURE, furi_string_replace_str(string, "replace", "test")); + mu_assert_string_eq("test!biglongword!test", furi_string_get_cstr(string)); + + // test furi_string_replace_all + furi_string_replace_all(string, needle, replace); + mu_assert_string_eq("replace!biglongword!replace", furi_string_get_cstr(string)); + + // test furi_string_replace_all_str + furi_string_replace_all_str(string, "replace", "test"); + mu_assert_string_eq("test!biglongword!test", furi_string_get_cstr(string)); + + furi_string_free(string); + furi_string_free(needle); + furi_string_free(replace); +} + +MU_TEST(mu_test_furi_string_start_end) { + FuriString* string = furi_string_alloc_set("start_end"); + FuriString* start = furi_string_alloc_set("start"); + FuriString* end = furi_string_alloc_set("end"); + + // test furi_string_start_with + mu_check(furi_string_start_with(string, start)); + mu_check(!furi_string_start_with(string, end)); + + // test furi_string_start_with_str + mu_check(furi_string_start_with_str(string, "start")); + mu_check(!furi_string_start_with_str(string, "end")); + + // test furi_string_end_with + mu_check(furi_string_end_with(string, end)); + mu_check(!furi_string_end_with(string, start)); + + // test furi_string_end_with_str + mu_check(furi_string_end_with_str(string, "end")); + mu_check(!furi_string_end_with_str(string, "start")); + + furi_string_free(string); + furi_string_free(start); + furi_string_free(end); +} + +MU_TEST(mu_test_furi_string_trim) { + FuriString* string = furi_string_alloc_set("biglongstring"); + + // test furi_string_left + furi_string_left(string, 7); + mu_assert_string_eq("biglong", furi_string_get_cstr(string)); + + // test furi_string_right + furi_string_right(string, 3); + mu_assert_string_eq("long", furi_string_get_cstr(string)); + + // test furi_string_mid + furi_string_mid(string, 1, 2); + mu_assert_string_eq("on", furi_string_get_cstr(string)); + + // test furi_string_trim + furi_string_set(string, " \n\r\tbiglongstring \n\r\t "); + furi_string_trim(string); + mu_assert_string_eq("biglongstring", furi_string_get_cstr(string)); + furi_string_set(string, "aaaabaaaabbaaabaaaabbtestaaaaaabbaaabaababaa"); + furi_string_trim(string, "ab"); + mu_assert_string_eq("test", furi_string_get_cstr(string)); + + furi_string_free(string); +} + +MU_TEST(mu_test_furi_string_utf8) { + FuriString* utf8_string = furi_string_alloc_set("イルカ"); + + // test furi_string_utf8_length + mu_assert_int_eq(9, furi_string_size(utf8_string)); + mu_assert_int_eq(3, furi_string_utf8_length(utf8_string)); + + // test furi_string_utf8_decode + const uint8_t dolphin_emoji_array[4] = {0xF0, 0x9F, 0x90, 0xAC}; + FuriStringUTF8State state = FuriStringUTF8StateStarting; + FuriStringUnicodeValue value = 0; + furi_string_utf8_decode(dolphin_emoji_array[0], &state, &value); + mu_assert_int_eq(FuriStringUTF8StateDecoding3, state); + furi_string_utf8_decode(dolphin_emoji_array[1], &state, &value); + mu_assert_int_eq(FuriStringUTF8StateDecoding2, state); + furi_string_utf8_decode(dolphin_emoji_array[2], &state, &value); + mu_assert_int_eq(FuriStringUTF8StateDecoding1, state); + furi_string_utf8_decode(dolphin_emoji_array[3], &state, &value); + mu_assert_int_eq(FuriStringUTF8StateStarting, state); + mu_assert_int_eq(0x1F42C, value); + + // test furi_string_utf8_push + furi_string_set(utf8_string, ""); + furi_string_utf8_push(utf8_string, value); + mu_assert_string_eq("🐬", furi_string_get_cstr(utf8_string)); + + furi_string_free(utf8_string); +} + +MU_TEST_SUITE(test_suite) { + MU_SUITE_CONFIGURE(&test_setup, &test_teardown); + + MU_RUN_TEST(mu_test_furi_string_alloc_free); + MU_RUN_TEST(mu_test_furi_string_mem); + MU_RUN_TEST(mu_test_furi_string_getters); + MU_RUN_TEST(mu_test_furi_string_setters); + MU_RUN_TEST(mu_test_furi_string_appends); + MU_RUN_TEST(mu_test_furi_string_compare); + MU_RUN_TEST(mu_test_furi_string_search); + MU_RUN_TEST(mu_test_furi_string_equality); + MU_RUN_TEST(mu_test_furi_string_replace); + MU_RUN_TEST(mu_test_furi_string_start_end); + MU_RUN_TEST(mu_test_furi_string_trim); + MU_RUN_TEST(mu_test_furi_string_utf8); +} + +int run_minunit_test_furi_string() { + MU_RUN_SUITE(test_suite); + + return MU_EXIT_CODE; +} \ No newline at end of file diff --git a/applications/unit_tests/furi/furi_test.c b/applications/debug/unit_tests/furi/furi_test.c similarity index 88% rename from applications/unit_tests/furi/furi_test.c rename to applications/debug/unit_tests/furi/furi_test.c index eed9e420597..33ec5fd019b 100644 --- a/applications/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_hal/furi_hal_crypto_tests.c b/applications/debug/unit_tests/furi_hal/furi_hal_crypto_tests.c new file mode 100644 index 00000000000..b06d51130f9 --- /dev/null +++ b/applications/debug/unit_tests/furi_hal/furi_hal_crypto_tests.c @@ -0,0 +1,602 @@ +#include +#include +#include +#include "../minunit.h" + +static const uint8_t key_ctr_1[32] = { + 0x77, 0x6B, 0xEF, 0xF2, 0x85, 0x1D, 0xB0, 0x6F, 0x4C, 0x8A, 0x05, 0x42, 0xC8, 0x69, 0x6F, 0x6C, + 0x6A, 0x81, 0xAF, 0x1E, 0xEC, 0x96, 0xB4, 0xD3, 0x7F, 0xC1, 0xD6, 0x89, 0xE6, 0xC1, 0xC1, 0x04, +}; +static const uint8_t iv_ctr_1[16] = { + 0x00, + 0x00, + 0x00, + 0x60, + 0xDB, + 0x56, + 0x72, + 0xC9, + 0x7A, + 0xA8, + 0xF0, + 0xB2, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_1[16] = { + 0x53, + 0x69, + 0x6E, + 0x67, + 0x6C, + 0x65, + 0x20, + 0x62, + 0x6C, + 0x6F, + 0x63, + 0x6B, + 0x20, + 0x6D, + 0x73, + 0x67, +}; +static const uint8_t tv_ctr_ct_1[16] = { + 0x14, + 0x5A, + 0xD0, + 0x1D, + 0xBF, + 0x82, + 0x4E, + 0xC7, + 0x56, + 0x08, + 0x63, + 0xDC, + 0x71, + 0xE3, + 0xE0, + 0xC0, +}; + +static const uint8_t key_ctr_2[32] = { + 0x77, 0x6B, 0xEF, 0xF2, 0x85, 0x1D, 0xB0, 0x6F, 0x4C, 0x8A, 0x05, 0x42, 0xC8, 0x69, 0x6F, 0x6C, + 0x6A, 0x81, 0xAF, 0x1E, 0xEC, 0x96, 0xB4, 0xD3, 0x7F, 0xC1, 0xD6, 0x89, 0xE6, 0xC1, 0xC1, 0x04, +}; +static const uint8_t iv_ctr_2[16] = { + 0x00, + 0x00, + 0x00, + 0x60, + 0xDB, + 0x56, + 0x72, + 0xC9, + 0x7A, + 0xA8, + 0xF0, + 0xB2, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_2[0] = {}; +//static const uint8_t tv_ctr_ct_2[0] = {}; + +static const uint8_t key_ctr_3[32] = { + 0xF6, 0xD6, 0x6D, 0x6B, 0xD5, 0x2D, 0x59, 0xBB, 0x07, 0x96, 0x36, 0x58, 0x79, 0xEF, 0xF8, 0x86, + 0xC6, 0x6D, 0xD5, 0x1A, 0x5B, 0x6A, 0x99, 0x74, 0x4B, 0x50, 0x59, 0x0C, 0x87, 0xA2, 0x38, 0x84, +}; +static const uint8_t iv_ctr_3[16] = { + 0x00, + 0xFA, + 0xAC, + 0x24, + 0xC1, + 0x58, + 0x5E, + 0xF1, + 0x5A, + 0x43, + 0xD8, + 0x75, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_3[32] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, +}; +static const uint8_t tv_ctr_ct_3[32] = { + 0xF0, 0x5E, 0x23, 0x1B, 0x38, 0x94, 0x61, 0x2C, 0x49, 0xEE, 0x00, 0x0B, 0x80, 0x4E, 0xB2, 0xA9, + 0xB8, 0x30, 0x6B, 0x50, 0x8F, 0x83, 0x9D, 0x6A, 0x55, 0x30, 0x83, 0x1D, 0x93, 0x44, 0xAF, 0x1C, +}; + +static const uint8_t key_ctr_4[32] = { + 0xFF, 0x7A, 0x61, 0x7C, 0xE6, 0x91, 0x48, 0xE4, 0xF1, 0x72, 0x6E, 0x2F, 0x43, 0x58, 0x1D, 0xE2, + 0xAA, 0x62, 0xD9, 0xF8, 0x05, 0x53, 0x2E, 0xDF, 0xF1, 0xEE, 0xD6, 0x87, 0xFB, 0x54, 0x15, 0x3D, +}; +static const uint8_t iv_ctr_4[16] = { + 0x00, + 0x1C, + 0xC5, + 0xB7, + 0x51, + 0xA5, + 0x1D, + 0x70, + 0xA1, + 0xC1, + 0x11, + 0x48, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_4[36] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, +}; +static const uint8_t tv_ctr_ct_4[36] = { + 0xEB, 0x6C, 0x52, 0x82, 0x1D, 0x0B, 0xBB, 0xF7, 0xCE, 0x75, 0x94, 0x46, + 0x2A, 0xCA, 0x4F, 0xAA, 0xB4, 0x07, 0xDF, 0x86, 0x65, 0x69, 0xFD, 0x07, + 0xF4, 0x8C, 0xC0, 0xB5, 0x83, 0xD6, 0x07, 0x1F, 0x1E, 0xC0, 0xE6, 0xB8, +}; + +static const uint8_t key_ctr_5[32] = { + 0xFF, 0x7A, 0x61, 0x7C, 0xE6, 0x91, 0x48, 0xE4, 0xF1, 0x72, 0x6E, 0x2F, 0x43, 0x58, 0x1D, 0xE2, + 0xAA, 0x62, 0xD9, 0xF8, 0x05, 0x53, 0x2E, 0xDF, 0xF1, 0xEE, 0xD6, 0x87, 0xFB, 0x54, 0x15, 0x3D, +}; +static const uint8_t iv_ctr_5[16] = { + 0x00, + 0x1C, + 0xC5, + 0xB7, + 0x51, + 0xA5, + 0x1D, + 0x70, + 0xA1, + 0xC1, + 0x11, + 0x48, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_5[0] = {}; +//static const uint8_t tv_ctr_ct_5[0] = {}; + +static const uint8_t key_gcm_1[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const uint8_t iv_gcm_1[16] = { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t pt_gcm_1[0] = {}; +//static const uint8_t tv_gcm_ct_1[0] = {}; +static const uint8_t aad_gcm_1[0] = {}; +static const uint8_t tv_gcm_tag_1[16] = { + 0x53, + 0x0F, + 0x8A, + 0xFB, + 0xC7, + 0x45, + 0x36, + 0xB9, + 0xA9, + 0x63, + 0xB4, + 0xF1, + 0xC4, + 0xCB, + 0x73, + 0x8B, +}; + +static const uint8_t key_gcm_2[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const uint8_t iv_gcm_2[16] = { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t pt_gcm_2[16] = { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t tv_gcm_ct_2[16] = { + 0xCE, + 0xA7, + 0x40, + 0x3D, + 0x4D, + 0x60, + 0x6B, + 0x6E, + 0x07, + 0x4E, + 0xC5, + 0xD3, + 0xBA, + 0xF3, + 0x9D, + 0x18, +}; +static const uint8_t aad_gcm_2[0] = {}; +static const uint8_t tv_gcm_tag_2[16] = { + 0xD0, + 0xD1, + 0xC8, + 0xA7, + 0x99, + 0x99, + 0x6B, + 0xF0, + 0x26, + 0x5B, + 0x98, + 0xB5, + 0xD4, + 0x8A, + 0xB9, + 0x19, +}; + +static const uint8_t key_gcm_3[32] = { + 0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08, + 0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08, +}; +static const uint8_t iv_gcm_3[16] = { + 0xCA, + 0xFE, + 0xBA, + 0xBE, + 0xFA, + 0xCE, + 0xDB, + 0xAD, + 0xDE, + 0xCA, + 0xF8, + 0x88, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t pt_gcm_3[64] = { + 0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x06, 0xE5, 0xA5, 0x59, 0x09, 0xC5, 0xAF, 0xF5, 0x26, 0x9A, + 0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA, 0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31, 0x8A, 0x72, + 0x1C, 0x3C, 0x0C, 0x95, 0x95, 0x68, 0x09, 0x53, 0x2F, 0xCF, 0x0E, 0x24, 0x49, 0xA6, 0xB5, 0x25, + 0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0x0D, 0xE6, 0x57, 0xBA, 0x63, 0x7B, 0x39, 0x1A, 0xAF, 0xD2, 0x55, +}; +static const uint8_t tv_gcm_ct_3[64] = { + 0x52, 0x2D, 0xC1, 0xF0, 0x99, 0x56, 0x7D, 0x07, 0xF4, 0x7F, 0x37, 0xA3, 0x2A, 0x84, 0x42, 0x7D, + 0x64, 0x3A, 0x8C, 0xDC, 0xBF, 0xE5, 0xC0, 0xC9, 0x75, 0x98, 0xA2, 0xBD, 0x25, 0x55, 0xD1, 0xAA, + 0x8C, 0xB0, 0x8E, 0x48, 0x59, 0x0D, 0xBB, 0x3D, 0xA7, 0xB0, 0x8B, 0x10, 0x56, 0x82, 0x88, 0x38, + 0xC5, 0xF6, 0x1E, 0x63, 0x93, 0xBA, 0x7A, 0x0A, 0xBC, 0xC9, 0xF6, 0x62, 0x89, 0x80, 0x15, 0xAD, +}; +static const uint8_t aad_gcm_3[0] = {}; +static const uint8_t tv_gcm_tag_3[16] = { + 0xB0, + 0x94, + 0xDA, + 0xC5, + 0xD9, + 0x34, + 0x71, + 0xBD, + 0xEC, + 0x1A, + 0x50, + 0x22, + 0x70, + 0xE3, + 0xCC, + 0x6C, +}; + +static const uint8_t key_gcm_4[32] = { + 0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08, + 0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08, +}; +static const uint8_t iv_gcm_4[16] = { + 0xCA, + 0xFE, + 0xBA, + 0xBE, + 0xFA, + 0xCE, + 0xDB, + 0xAD, + 0xDE, + 0xCA, + 0xF8, + 0x88, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t pt_gcm_4[60] = { + 0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x06, 0xE5, 0xA5, 0x59, 0x09, 0xC5, 0xAF, 0xF5, 0x26, + 0x9A, 0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA, 0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31, + 0x8A, 0x72, 0x1C, 0x3C, 0x0C, 0x95, 0x95, 0x68, 0x09, 0x53, 0x2F, 0xCF, 0x0E, 0x24, 0x49, + 0xA6, 0xB5, 0x25, 0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0x0D, 0xE6, 0x57, 0xBA, 0x63, 0x7B, 0x39, +}; +static const uint8_t tv_gcm_ct_4[60] = { + 0x52, 0x2D, 0xC1, 0xF0, 0x99, 0x56, 0x7D, 0x07, 0xF4, 0x7F, 0x37, 0xA3, 0x2A, 0x84, 0x42, + 0x7D, 0x64, 0x3A, 0x8C, 0xDC, 0xBF, 0xE5, 0xC0, 0xC9, 0x75, 0x98, 0xA2, 0xBD, 0x25, 0x55, + 0xD1, 0xAA, 0x8C, 0xB0, 0x8E, 0x48, 0x59, 0x0D, 0xBB, 0x3D, 0xA7, 0xB0, 0x8B, 0x10, 0x56, + 0x82, 0x88, 0x38, 0xC5, 0xF6, 0x1E, 0x63, 0x93, 0xBA, 0x7A, 0x0A, 0xBC, 0xC9, 0xF6, 0x62, +}; +static const uint8_t aad_gcm_4[20] = { + 0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED, + 0xFA, 0xCE, 0xDE, 0xAD, 0xBE, 0xEF, 0xAB, 0xAD, 0xDA, 0xD2, +}; +static const uint8_t tv_gcm_tag_4[16] = { + 0x76, + 0xFC, + 0x6E, + 0xCE, + 0x0F, + 0x4E, + 0x17, + 0x68, + 0xCD, + 0xDF, + 0x88, + 0x53, + 0xBB, + 0x2D, + 0x55, + 0x1B, +}; + +static void furi_hal_crypto_ctr_setup() { +} + +static void furi_hal_crypto_ctr_teardown() { +} + +static void furi_hal_crypto_gcm_setup() { +} + +static void furi_hal_crypto_gcm_teardown() { +} + +MU_TEST(furi_hal_crypto_ctr_1) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_1)]; + + ret = furi_hal_crypto_ctr(key_ctr_1, iv_ctr_1, pt_ctr_1, ct, sizeof(pt_ctr_1)); + mu_assert(ret, "CTR 1 failed"); + mu_assert_mem_eq(tv_ctr_ct_1, ct, sizeof(pt_ctr_1)); +} + +MU_TEST(furi_hal_crypto_ctr_2) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_2)]; + + ret = furi_hal_crypto_ctr(key_ctr_2, iv_ctr_2, pt_ctr_2, ct, sizeof(pt_ctr_2)); + mu_assert(ret, "CTR 2 failed"); + //mu_assert_mem_eq(tv_ctr_ct_2, ct, sizeof(pt_ctr_2)); +} + +MU_TEST(furi_hal_crypto_ctr_3) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_3)]; + + ret = furi_hal_crypto_ctr(key_ctr_3, iv_ctr_3, pt_ctr_3, ct, sizeof(pt_ctr_3)); + mu_assert(ret, "CTR 3 failed"); + mu_assert_mem_eq(tv_ctr_ct_3, ct, sizeof(pt_ctr_3)); +} + +MU_TEST(furi_hal_crypto_ctr_4) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_4)]; + + ret = furi_hal_crypto_ctr(key_ctr_4, iv_ctr_4, pt_ctr_4, ct, sizeof(pt_ctr_4)); + mu_assert(ret, "CTR 4 failed"); + mu_assert_mem_eq(tv_ctr_ct_4, ct, sizeof(pt_ctr_4)); +} + +MU_TEST(furi_hal_crypto_ctr_5) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_5)]; + + ret = furi_hal_crypto_ctr(key_ctr_5, iv_ctr_5, pt_ctr_5, ct, sizeof(pt_ctr_5)); + mu_assert(ret, "CTR 5 failed"); + //mu_assert_mem_eq(tv_ctr_ct_5, ct, sizeof(pt_ctr_5)); +} + +MU_TEST(furi_hal_crypto_gcm_1) { + bool ret = false; + uint8_t pt[sizeof(pt_gcm_1)]; + uint8_t ct[sizeof(pt_gcm_1)]; + uint8_t tag_enc[16]; + uint8_t tag_dec[16]; + + ret = furi_hal_crypto_gcm( + key_gcm_1, + iv_gcm_1, + aad_gcm_1, + sizeof(aad_gcm_1), + pt_gcm_1, + ct, + sizeof(pt_gcm_1), + tag_enc, + false); + mu_assert(ret, "GCM 1 encryption failed"); + //mu_assert_mem_eq(tv_gcm_ct_1, ct, sizeof(pt_gcm_1)); + mu_assert_mem_eq(tv_gcm_tag_1, tag_enc, 16); + + ret = furi_hal_crypto_gcm( + key_gcm_1, iv_gcm_1, aad_gcm_1, sizeof(aad_gcm_1), ct, pt, sizeof(pt_gcm_1), tag_dec, true); + mu_assert(ret, "GCM 1 decryption failed"); + //mu_assert_mem_eq(pt_gcm_1, pt, sizeof(pt_gcm_1)); + mu_assert_mem_eq(tv_gcm_tag_1, tag_dec, 16); +} + +MU_TEST(furi_hal_crypto_gcm_2) { + bool ret = false; + uint8_t pt[sizeof(pt_gcm_2)]; + uint8_t ct[sizeof(pt_gcm_2)]; + uint8_t tag_enc[16]; + uint8_t tag_dec[16]; + + ret = furi_hal_crypto_gcm( + key_gcm_2, + iv_gcm_2, + aad_gcm_2, + sizeof(aad_gcm_2), + pt_gcm_2, + ct, + sizeof(pt_gcm_2), + tag_enc, + false); + mu_assert(ret, "GCM 2 encryption failed"); + mu_assert_mem_eq(tv_gcm_ct_2, ct, sizeof(pt_gcm_2)); + mu_assert_mem_eq(tv_gcm_tag_2, tag_enc, 16); + + ret = furi_hal_crypto_gcm( + key_gcm_2, iv_gcm_2, aad_gcm_2, sizeof(aad_gcm_2), ct, pt, sizeof(pt_gcm_2), tag_dec, true); + mu_assert(ret, "GCM 2 decryption failed"); + mu_assert_mem_eq(pt_gcm_2, pt, sizeof(pt_gcm_2)); + mu_assert_mem_eq(tv_gcm_tag_2, tag_dec, 16); +} + +MU_TEST(furi_hal_crypto_gcm_3) { + bool ret = false; + uint8_t pt[sizeof(pt_gcm_3)]; + uint8_t ct[sizeof(pt_gcm_3)]; + uint8_t tag_enc[16]; + uint8_t tag_dec[16]; + + ret = furi_hal_crypto_gcm( + key_gcm_3, + iv_gcm_3, + aad_gcm_3, + sizeof(aad_gcm_3), + pt_gcm_3, + ct, + sizeof(pt_gcm_3), + tag_enc, + false); + mu_assert(ret, "GCM 3 encryption failed"); + mu_assert_mem_eq(tv_gcm_ct_3, ct, sizeof(pt_gcm_3)); + mu_assert_mem_eq(tv_gcm_tag_3, tag_enc, 16); + + ret = furi_hal_crypto_gcm( + key_gcm_3, iv_gcm_3, aad_gcm_3, sizeof(aad_gcm_3), ct, pt, sizeof(pt_gcm_3), tag_dec, true); + mu_assert(ret, "GCM 3 decryption failed"); + mu_assert_mem_eq(pt_gcm_3, pt, sizeof(pt_gcm_3)); + mu_assert_mem_eq(tv_gcm_tag_3, tag_dec, 16); +} + +MU_TEST(furi_hal_crypto_gcm_4) { + bool ret = false; + uint8_t pt[sizeof(pt_gcm_4)]; + uint8_t ct[sizeof(pt_gcm_4)]; + uint8_t tag_enc[16]; + uint8_t tag_dec[16]; + + ret = furi_hal_crypto_gcm( + key_gcm_4, + iv_gcm_4, + aad_gcm_4, + sizeof(aad_gcm_4), + pt_gcm_4, + ct, + sizeof(pt_gcm_4), + tag_enc, + false); + mu_assert(ret, "GCM 4 encryption failed"); + mu_assert_mem_eq(tv_gcm_ct_4, ct, sizeof(pt_gcm_4)); + mu_assert_mem_eq(tv_gcm_tag_4, tag_enc, 16); + + ret = furi_hal_crypto_gcm( + key_gcm_4, iv_gcm_4, aad_gcm_4, sizeof(aad_gcm_4), ct, pt, sizeof(pt_gcm_4), tag_dec, true); + mu_assert(ret, "GCM 4 decryption failed"); + mu_assert_mem_eq(pt_gcm_4, pt, sizeof(pt_gcm_4)); + mu_assert_mem_eq(tv_gcm_tag_4, tag_dec, 16); +} + +MU_TEST_SUITE(furi_hal_crypto_ctr_test) { + MU_SUITE_CONFIGURE(&furi_hal_crypto_ctr_setup, &furi_hal_crypto_ctr_teardown); + MU_RUN_TEST(furi_hal_crypto_ctr_1); + MU_RUN_TEST(furi_hal_crypto_ctr_2); + MU_RUN_TEST(furi_hal_crypto_ctr_3); + MU_RUN_TEST(furi_hal_crypto_ctr_4); + MU_RUN_TEST(furi_hal_crypto_ctr_5); +} + +MU_TEST_SUITE(furi_hal_crypto_gcm_test) { + MU_SUITE_CONFIGURE(&furi_hal_crypto_gcm_setup, &furi_hal_crypto_gcm_teardown); + MU_RUN_TEST(furi_hal_crypto_gcm_1); + MU_RUN_TEST(furi_hal_crypto_gcm_2); + MU_RUN_TEST(furi_hal_crypto_gcm_3); + MU_RUN_TEST(furi_hal_crypto_gcm_4); +} + +int run_minunit_test_furi_hal_crypto() { + MU_RUN_SUITE(furi_hal_crypto_ctr_test); + MU_RUN_SUITE(furi_hal_crypto_gcm_test); + return MU_EXIT_CODE; +} diff --git a/applications/debug/unit_tests/furi_hal/furi_hal_tests.c b/applications/debug/unit_tests/furi_hal/furi_hal_tests.c new file mode 100644 index 00000000000..2dbaa4d8689 --- /dev/null +++ b/applications/debug/unit_tests/furi_hal/furi_hal_tests.c @@ -0,0 +1,231 @@ +#include +#include +#include +#include +#include "../minunit.h" + +#define DATA_SIZE 4 +#define EEPROM_ADDRESS 0b10101000 +#define EEPROM_ADDRESS_HIGH (EEPROM_ADDRESS | 0b10) +#define EEPROM_SIZE 512 +#define EEPROM_PAGE_SIZE 16 +#define EEPROM_WRITE_DELAY_MS 6 + +static void furi_hal_i2c_int_setup() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); +} + +static void furi_hal_i2c_int_teardown() { + furi_hal_i2c_release(&furi_hal_i2c_handle_power); +} + +static void furi_hal_i2c_ext_setup() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); +} + +static void furi_hal_i2c_ext_teardown() { + furi_hal_i2c_release(&furi_hal_i2c_handle_external); +} + +MU_TEST(furi_hal_i2c_int_1b) { + bool ret = false; + uint8_t data_one = 0; + + // 1 byte: read, write, read + ret = furi_hal_i2c_read_reg_8( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + LP5562_CHANNEL_BLUE_CURRENT_REGISTER, + &data_one, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "0 read_reg_8 failed"); + mu_assert(data_one != 0, "0 invalid data"); + ret = furi_hal_i2c_write_reg_8( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + LP5562_CHANNEL_BLUE_CURRENT_REGISTER, + data_one, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "1 write_reg_8 failed"); + ret = furi_hal_i2c_read_reg_8( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + LP5562_CHANNEL_BLUE_CURRENT_REGISTER, + &data_one, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "2 read_reg_8 failed"); + mu_assert(data_one != 0, "2 invalid data"); +} + +MU_TEST(furi_hal_i2c_int_3b) { + bool ret = false; + uint8_t data_many[DATA_SIZE] = {0}; + + // 3 byte: read, write, read + data_many[0] = LP5562_CHANNEL_BLUE_CURRENT_REGISTER; + ret = furi_hal_i2c_tx( + &furi_hal_i2c_handle_power, LP5562_ADDRESS, data_many, 1, LP5562_I2C_TIMEOUT); + mu_assert(ret, "3 tx failed"); + ret = furi_hal_i2c_rx( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + data_many + 1, + DATA_SIZE - 1, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "4 rx failed"); + for(size_t i = 0; i < DATA_SIZE; i++) mu_assert(data_many[i] != 0, "4 invalid data_many"); + + ret = furi_hal_i2c_tx( + &furi_hal_i2c_handle_power, LP5562_ADDRESS, data_many, DATA_SIZE, LP5562_I2C_TIMEOUT); + mu_assert(ret, "5 tx failed"); + + ret = furi_hal_i2c_tx( + &furi_hal_i2c_handle_power, LP5562_ADDRESS, data_many, 1, LP5562_I2C_TIMEOUT); + mu_assert(ret, "6 tx failed"); + ret = furi_hal_i2c_rx( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + data_many + 1, + DATA_SIZE - 1, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "7 rx failed"); + for(size_t i = 0; i < DATA_SIZE; i++) mu_assert(data_many[i] != 0, "7 invalid data_many"); +} + +MU_TEST(furi_hal_i2c_int_1b_fail) { + bool ret = false; + uint8_t data_one = 0; + + // 1 byte: fail, read, fail, write, fail, read + data_one = 0; + ret = furi_hal_i2c_read_reg_8( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS + 0x10, + LP5562_CHANNEL_BLUE_CURRENT_REGISTER, + &data_one, + LP5562_I2C_TIMEOUT); + mu_assert(!ret, "8 read_reg_8 failed"); + mu_assert(data_one == 0, "8 invalid data"); + ret = furi_hal_i2c_read_reg_8( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + LP5562_CHANNEL_BLUE_CURRENT_REGISTER, + &data_one, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "9 read_reg_8 failed"); + mu_assert(data_one != 0, "9 invalid data"); +} + +MU_TEST(furi_hal_i2c_int_ext_3b) { + bool ret = false; + uint8_t data_many[DATA_SIZE] = {0}; + + // 3 byte: read + data_many[0] = LP5562_CHANNEL_BLUE_CURRENT_REGISTER; + ret = furi_hal_i2c_tx_ext( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + false, + data_many, + 1, + FuriHalI2cBeginStart, + FuriHalI2cEndAwaitRestart, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "3 tx failed"); + + // Send a RESTART condition, then read the 3 bytes one after the other + ret = furi_hal_i2c_rx_ext( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + false, + data_many + 1, + 1, + FuriHalI2cBeginRestart, + FuriHalI2cEndPause, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "4 rx failed"); + mu_assert(data_many[1] != 0, "4 invalid data"); + ret = furi_hal_i2c_rx_ext( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + false, + data_many + 2, + 1, + FuriHalI2cBeginResume, + FuriHalI2cEndPause, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "5 rx failed"); + mu_assert(data_many[2] != 0, "5 invalid data"); + ret = furi_hal_i2c_rx_ext( + &furi_hal_i2c_handle_power, + LP5562_ADDRESS, + false, + data_many + 3, + 1, + FuriHalI2cBeginResume, + FuriHalI2cEndStop, + LP5562_I2C_TIMEOUT); + mu_assert(ret, "6 rx failed"); + mu_assert(data_many[3] != 0, "6 invalid data"); +} + +MU_TEST(furi_hal_i2c_ext_eeprom) { + if(!furi_hal_i2c_is_device_ready(&furi_hal_i2c_handle_external, EEPROM_ADDRESS, 100)) { + printf("no device connected, skipping\r\n"); + return; + } + + bool ret = false; + uint8_t buffer[EEPROM_SIZE] = {0}; + + for(size_t page = 0; page < (EEPROM_SIZE / EEPROM_PAGE_SIZE); ++page) { + // Fill page buffer + for(size_t page_byte = 0; page_byte < EEPROM_PAGE_SIZE; ++page_byte) { + // Each byte is its position in the EEPROM modulo 256 + uint8_t byte = ((page * EEPROM_PAGE_SIZE) + page_byte) % 256; + + buffer[page_byte] = byte; + } + + uint8_t address = (page < 16) ? EEPROM_ADDRESS : EEPROM_ADDRESS_HIGH; + + ret = furi_hal_i2c_write_mem( + &furi_hal_i2c_handle_external, + address, + page * EEPROM_PAGE_SIZE, + buffer, + EEPROM_PAGE_SIZE, + 20); + + mu_assert(ret, "EEPROM write failed"); + furi_delay_ms(EEPROM_WRITE_DELAY_MS); + } + + ret = furi_hal_i2c_read_mem( + &furi_hal_i2c_handle_external, EEPROM_ADDRESS, 0, buffer, EEPROM_SIZE, 100); + + mu_assert(ret, "EEPROM read failed"); + + for(size_t pos = 0; pos < EEPROM_SIZE; ++pos) { + mu_assert_int_eq(pos % 256, buffer[pos]); + } +} + +MU_TEST_SUITE(furi_hal_i2c_int_suite) { + MU_SUITE_CONFIGURE(&furi_hal_i2c_int_setup, &furi_hal_i2c_int_teardown); + MU_RUN_TEST(furi_hal_i2c_int_1b); + MU_RUN_TEST(furi_hal_i2c_int_3b); + MU_RUN_TEST(furi_hal_i2c_int_ext_3b); + MU_RUN_TEST(furi_hal_i2c_int_1b_fail); +} + +MU_TEST_SUITE(furi_hal_i2c_ext_suite) { + MU_SUITE_CONFIGURE(&furi_hal_i2c_ext_setup, &furi_hal_i2c_ext_teardown); + MU_RUN_TEST(furi_hal_i2c_ext_eeprom); +} + +int run_minunit_test_furi_hal() { + MU_RUN_SUITE(furi_hal_i2c_int_suite); + MU_RUN_SUITE(furi_hal_i2c_ext_suite); + return MU_EXIT_CODE; +} diff --git a/applications/unit_tests/infrared/infrared_test.c b/applications/debug/unit_tests/infrared/infrared_test.c similarity index 83% rename from applications/unit_tests/infrared/infrared_test.c rename to applications/debug/unit_tests/infrared/infrared_test.c index 32266c48e59..294c2da9a55 100644 --- a/applications/unit_tests/infrared/infrared_test.c +++ b/applications/debug/unit_tests/infrared/infrared_test.c @@ -11,7 +11,7 @@ typedef struct { InfraredDecoderHandler* decoder_handler; InfraredEncoderHandler* encoder_handler; - string_t file_path; + FuriString* file_path; FlipperFormat* ff; } InfraredTest; @@ -23,26 +23,26 @@ static void infrared_test_alloc() { test->decoder_handler = infrared_alloc_decoder(); test->encoder_handler = infrared_alloc_encoder(); test->ff = flipper_format_buffered_file_alloc(storage); - string_init(test->file_path); + test->file_path = furi_string_alloc(); } static void infrared_test_free() { - furi_assert(test); + furi_check(test); infrared_free_decoder(test->decoder_handler); infrared_free_encoder(test->encoder_handler); flipper_format_free(test->ff); - string_clear(test->file_path); + furi_string_free(test->file_path); furi_record_close(RECORD_STORAGE); free(test); test = NULL; } static bool infrared_test_prepare_file(const char* protocol_name) { - string_t file_type; - string_init(file_type); + FuriString* file_type; + file_type = furi_string_alloc(); bool success = false; - string_printf( + furi_string_printf( test->file_path, "%s%s%s%s", IR_TEST_FILES_DIR, @@ -52,14 +52,15 @@ static bool infrared_test_prepare_file(const char* protocol_name) { do { uint32_t format_version; - if(!flipper_format_buffered_file_open_existing(test->ff, string_get_cstr(test->file_path))) + if(!flipper_format_buffered_file_open_existing( + test->ff, furi_string_get_cstr(test->file_path))) break; if(!flipper_format_read_header(test->ff, file_type, &format_version)) break; - if(string_cmp_str(file_type, "IR tests file") || format_version != 1) break; + if(furi_string_cmp_str(file_type, "IR tests file") || format_version != 1) break; success = true; } while(false); - string_clear(file_type); + furi_string_free(file_type); return success; } @@ -68,18 +69,18 @@ static bool infrared_test_load_raw_signal( const char* signal_name, uint32_t** timings, uint32_t* timings_count) { - string_t buf; - string_init(buf); + FuriString* buf; + buf = furi_string_alloc(); bool success = false; do { bool is_name_found = false; for(; !is_name_found && flipper_format_read_string(ff, "name", buf); - is_name_found = !string_cmp_str(buf, signal_name)) + is_name_found = !furi_string_cmp(buf, signal_name)) ; if(!is_name_found) break; - if(!flipper_format_read_string(ff, "type", buf) || string_cmp_str(buf, "raw")) break; + if(!flipper_format_read_string(ff, "type", buf) || furi_string_cmp_str(buf, "raw")) break; if(!flipper_format_get_value_count(ff, "data", timings_count)) break; if(!*timings_count) break; @@ -91,18 +92,18 @@ static bool infrared_test_load_raw_signal( success = true; } while(false); - string_clear(buf); + furi_string_free(buf); return success; } static bool infrared_test_read_message(FlipperFormat* ff, InfraredMessage* message) { - string_t buf; - string_init(buf); + FuriString* buf; + buf = furi_string_alloc(); bool success = false; do { if(!flipper_format_read_string(ff, "protocol", buf)) break; - message->protocol = infrared_get_protocol_by_name(string_get_cstr(buf)); + message->protocol = infrared_get_protocol_by_name(furi_string_get_cstr(buf)); if(!infrared_is_protocol_valid(message->protocol)) break; if(!flipper_format_read_hex(ff, "address", (uint8_t*)&message->address, sizeof(uint32_t))) break; @@ -112,7 +113,7 @@ static bool infrared_test_read_message(FlipperFormat* ff, InfraredMessage* messa success = true; } while(false); - string_clear(buf); + furi_string_free(buf); return success; } @@ -121,18 +122,19 @@ static bool infrared_test_load_messages( const char* signal_name, InfraredMessage** messages, uint32_t* messages_count) { - string_t buf; - string_init(buf); + FuriString* buf; + buf = furi_string_alloc(); bool success = false; do { bool is_name_found = false; for(; !is_name_found && flipper_format_read_string(ff, "name", buf); - is_name_found = !string_cmp_str(buf, signal_name)) + is_name_found = !furi_string_cmp(buf, signal_name)) ; if(!is_name_found) break; - if(!flipper_format_read_string(ff, "type", buf) || string_cmp_str(buf, "parsed_array")) + if(!flipper_format_read_string(ff, "type", buf) || + furi_string_cmp_str(buf, "parsed_array")) break; if(!flipper_format_read_uint32(ff, "count", messages_count, 1)) break; if(!*messages_count) break; @@ -151,7 +153,7 @@ static bool infrared_test_load_messages( success = true; } while(false); - string_clear(buf); + furi_string_free(buf); return success; } @@ -213,26 +215,26 @@ static void infrared_test_run_encoder(InfraredProtocol protocol, uint32_t test_i InfraredMessage* input_messages; uint32_t input_messages_count; - string_t buf; - string_init(buf); + FuriString* buf; + buf = furi_string_alloc(); const char* protocol_name = infrared_get_protocol_name(protocol); mu_assert(infrared_test_prepare_file(protocol_name), "Failed to prepare test file"); - string_printf(buf, "encoder_input%d", test_index); + furi_string_printf(buf, "encoder_input%ld", test_index); mu_assert( infrared_test_load_messages( - test->ff, string_get_cstr(buf), &input_messages, &input_messages_count), + test->ff, furi_string_get_cstr(buf), &input_messages, &input_messages_count), "Failed to load messages from file"); - string_printf(buf, "encoder_expected%d", test_index); + furi_string_printf(buf, "encoder_expected%ld", test_index); mu_assert( infrared_test_load_raw_signal( - test->ff, string_get_cstr(buf), &expected_timings, &expected_timings_count), + test->ff, furi_string_get_cstr(buf), &expected_timings, &expected_timings_count), "Failed to load raw signal from file"); flipper_format_buffered_file_close(test->ff); - string_clear(buf); + furi_string_free(buf); uint32_t j = 0; timings = malloc(sizeof(uint32_t) * timings_count); @@ -267,22 +269,22 @@ static void infrared_test_run_encoder_decoder(InfraredProtocol protocol, uint32_ uint32_t input_messages_count; bool level = false; - string_t buf; - string_init(buf); + FuriString* buf; + buf = furi_string_alloc(); timings = malloc(sizeof(uint32_t) * timings_count); const char* protocol_name = infrared_get_protocol_name(protocol); mu_assert(infrared_test_prepare_file(protocol_name), "Failed to prepare test file"); - string_printf(buf, "encoder_decoder_input%d", test_index); + furi_string_printf(buf, "encoder_decoder_input%ld", test_index); mu_assert( infrared_test_load_messages( - test->ff, string_get_cstr(buf), &input_messages, &input_messages_count), + test->ff, furi_string_get_cstr(buf), &input_messages, &input_messages_count), "Failed to load messages from file"); flipper_format_buffered_file_close(test->ff); - string_clear(buf); + furi_string_free(buf); for(uint32_t message_counter = 0; message_counter < input_messages_count; ++message_counter) { const InfraredMessage* message_encoded = &input_messages[message_counter]; @@ -327,25 +329,27 @@ static void infrared_test_run_decoder(InfraredProtocol protocol, uint32_t test_i InfraredMessage* messages; uint32_t messages_count; - string_t buf; - string_init(buf); + FuriString* buf; + buf = furi_string_alloc(); mu_assert( infrared_test_prepare_file(infrared_get_protocol_name(protocol)), "Failed to prepare test file"); - string_printf(buf, "decoder_input%d", test_index); + furi_string_printf(buf, "decoder_input%ld", test_index); mu_assert( - infrared_test_load_raw_signal(test->ff, string_get_cstr(buf), &timings, &timings_count), + infrared_test_load_raw_signal( + test->ff, furi_string_get_cstr(buf), &timings, &timings_count), "Failed to load raw signal from file"); - string_printf(buf, "decoder_expected%d", test_index); + furi_string_printf(buf, "decoder_expected%ld", test_index); mu_assert( - infrared_test_load_messages(test->ff, string_get_cstr(buf), &messages, &messages_count), + infrared_test_load_messages( + test->ff, furi_string_get_cstr(buf), &messages, &messages_count), "Failed to load messages from file"); flipper_format_buffered_file_close(test->ff); - string_clear(buf); + furi_string_free(buf); InfraredMessage message_decoded_check_local; bool level = 0; @@ -420,6 +424,8 @@ MU_TEST(infrared_test_decoder_mixed) { infrared_test_run_decoder(InfraredProtocolRC5, 5); infrared_test_run_decoder(InfraredProtocolSamsung32, 1); infrared_test_run_decoder(InfraredProtocolSIRC, 3); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 1); + infrared_test_run_decoder(InfraredProtocolRCA, 1); } MU_TEST(infrared_test_decoder_nec) { @@ -485,6 +491,24 @@ MU_TEST(infrared_test_encoder_rc6) { infrared_test_run_encoder(InfraredProtocolRC6, 1); } +MU_TEST(infrared_test_decoder_kaseikyo) { + infrared_test_run_decoder(InfraredProtocolKaseikyo, 1); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 2); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 3); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 4); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 5); + infrared_test_run_decoder(InfraredProtocolKaseikyo, 6); +} + +MU_TEST(infrared_test_decoder_rca) { + infrared_test_run_decoder(InfraredProtocolRCA, 1); + infrared_test_run_decoder(InfraredProtocolRCA, 2); + infrared_test_run_decoder(InfraredProtocolRCA, 3); + infrared_test_run_decoder(InfraredProtocolRCA, 4); + infrared_test_run_decoder(InfraredProtocolRCA, 5); + infrared_test_run_decoder(InfraredProtocolRCA, 6); +} + MU_TEST(infrared_test_encoder_decoder_all) { infrared_test_run_encoder_decoder(InfraredProtocolNEC, 1); infrared_test_run_encoder_decoder(InfraredProtocolNECext, 1); @@ -494,6 +518,8 @@ MU_TEST(infrared_test_encoder_decoder_all) { infrared_test_run_encoder_decoder(InfraredProtocolRC6, 1); infrared_test_run_encoder_decoder(InfraredProtocolRC5, 1); infrared_test_run_encoder_decoder(InfraredProtocolSIRC, 1); + infrared_test_run_encoder_decoder(InfraredProtocolKaseikyo, 1); + infrared_test_run_encoder_decoder(InfraredProtocolRCA, 1); } MU_TEST_SUITE(infrared_test) { @@ -511,6 +537,8 @@ MU_TEST_SUITE(infrared_test) { MU_RUN_TEST(infrared_test_decoder_nec); MU_RUN_TEST(infrared_test_decoder_samsung32); MU_RUN_TEST(infrared_test_decoder_necext1); + MU_RUN_TEST(infrared_test_decoder_kaseikyo); + MU_RUN_TEST(infrared_test_decoder_rca); MU_RUN_TEST(infrared_test_decoder_mixed); MU_RUN_TEST(infrared_test_encoder_decoder_all); } diff --git a/applications/debug/unit_tests/lfrfid/bit_lib_test.c b/applications/debug/unit_tests/lfrfid/bit_lib_test.c new file mode 100644 index 00000000000..dcb69de7f4f --- /dev/null +++ b/applications/debug/unit_tests/lfrfid/bit_lib_test.c @@ -0,0 +1,473 @@ +#include +#include "../minunit.h" +#include + +MU_TEST(test_bit_lib_increment_index) { + uint32_t index = 0; + + // test increment + for(uint32_t i = 0; i < 31; ++i) { + bit_lib_increment_index(index, 32); + mu_assert_int_eq(i + 1, index); + } + + // test wrap around + for(uint32_t i = 0; i < 512; ++i) { + bit_lib_increment_index(index, 32); + mu_assert_int_less_than(32, index); + } +} + +MU_TEST(test_bit_lib_is_set) { + uint32_t value = 0x0000FFFF; + + for(uint32_t i = 0; i < 16; ++i) { + mu_check(bit_lib_bit_is_set(value, i)); + mu_check(!bit_lib_bit_is_not_set(value, i)); + } + + for(uint32_t i = 16; i < 32; ++i) { + mu_check(!bit_lib_bit_is_set(value, i)); + mu_check(bit_lib_bit_is_not_set(value, i)); + } +} + +MU_TEST(test_bit_lib_push) { +#define TEST_BIT_LIB_PUSH_DATA_SIZE 4 + uint8_t data[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0}; + uint8_t expected_data_1[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0x00, 0x0F, 0xFF}; + uint8_t expected_data_2[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0xFF, 0xF0, 0x00}; + uint8_t expected_data_3[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xFF, 0x00, 0x00, 0xFF}; + uint8_t expected_data_4[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF}; + uint8_t expected_data_5[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0x00, 0x00, 0x00, 0x00}; + uint8_t expected_data_6[TEST_BIT_LIB_PUSH_DATA_SIZE] = {0xCC, 0xCC, 0xCC, 0xCC}; + + for(uint32_t i = 0; i < 12; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true); + } + mu_assert_mem_eq(expected_data_1, data, TEST_BIT_LIB_PUSH_DATA_SIZE); + + for(uint32_t i = 0; i < 12; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false); + } + mu_assert_mem_eq(expected_data_2, data, TEST_BIT_LIB_PUSH_DATA_SIZE); + + for(uint32_t i = 0; i < 4; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false); + } + for(uint32_t i = 0; i < 8; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true); + } + mu_assert_mem_eq(expected_data_3, data, TEST_BIT_LIB_PUSH_DATA_SIZE); + + for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 8; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true); + } + mu_assert_mem_eq(expected_data_4, data, TEST_BIT_LIB_PUSH_DATA_SIZE); + + for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 8; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false); + } + mu_assert_mem_eq(expected_data_5, data, TEST_BIT_LIB_PUSH_DATA_SIZE); + + for(uint32_t i = 0; i < TEST_BIT_LIB_PUSH_DATA_SIZE * 2; ++i) { + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true); + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, true); + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false); + bit_lib_push_bit(data, TEST_BIT_LIB_PUSH_DATA_SIZE, false); + } + mu_assert_mem_eq(expected_data_6, data, TEST_BIT_LIB_PUSH_DATA_SIZE); +} + +MU_TEST(test_bit_lib_set_bit) { + uint8_t value[2] = {0x00, 0xFF}; + bit_lib_set_bit(value, 15, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xFE}), 2); + bit_lib_set_bit(value, 14, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xFC}), 2); + bit_lib_set_bit(value, 13, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xF8}), 2); + bit_lib_set_bit(value, 12, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xF0}), 2); + bit_lib_set_bit(value, 11, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xE0}), 2); + bit_lib_set_bit(value, 10, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0xC0}), 2); + bit_lib_set_bit(value, 9, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0x80}), 2); + bit_lib_set_bit(value, 8, false); + mu_assert_mem_eq(value, ((uint8_t[]){0x00, 0x00}), 2); + + bit_lib_set_bit(value, 7, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x01, 0x00}), 2); + bit_lib_set_bit(value, 6, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x03, 0x00}), 2); + bit_lib_set_bit(value, 5, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x07, 0x00}), 2); + bit_lib_set_bit(value, 4, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x0F, 0x00}), 2); + bit_lib_set_bit(value, 3, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x1F, 0x00}), 2); + bit_lib_set_bit(value, 2, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x3F, 0x00}), 2); + bit_lib_set_bit(value, 1, true); + mu_assert_mem_eq(value, ((uint8_t[]){0x7F, 0x00}), 2); + bit_lib_set_bit(value, 0, true); + mu_assert_mem_eq(value, ((uint8_t[]){0xFF, 0x00}), 2); +} + +MU_TEST(test_bit_lib_set_bits) { + uint8_t value[2] = {0b00000000, 0b11111111}; + // set 4 bits to 0b0100 from 12 index + bit_lib_set_bits(value, 12, 0b0100, 4); + // [0100] + mu_assert_mem_eq(value, ((uint8_t[]){0b00000000, 0b11110100}), 2); + + // set 2 bits to 0b11 from 11 index + bit_lib_set_bits(value, 11, 0b11, 2); + // [11] + mu_assert_mem_eq(value, ((uint8_t[]){0b00000000, 0b11111100}), 2); + + // set 3 bits to 0b111 from 0 index + bit_lib_set_bits(value, 0, 0b111, 3); + // [111] + mu_assert_mem_eq(value, ((uint8_t[]){0b11100000, 0b11111100}), 2); + + // set 8 bits to 0b11111000 from 3 index + bit_lib_set_bits(value, 3, 0b11111000, 8); + // [11111 000] + mu_assert_mem_eq(value, ((uint8_t[]){0b11111111, 0b00011100}), 2); +} + +MU_TEST(test_bit_lib_get_bit) { + uint8_t value[2] = {0b00000000, 0b11111111}; + for(uint32_t i = 0; i < 8; ++i) { + mu_check(bit_lib_get_bit(value, i) == false); + } + for(uint32_t i = 8; i < 16; ++i) { + mu_check(bit_lib_get_bit(value, i) == true); + } +} + +MU_TEST(test_bit_lib_get_bits) { + uint8_t value[2] = {0b00000000, 0b11111111}; + mu_assert_int_eq(0b00000000, bit_lib_get_bits(value, 0, 8)); + mu_assert_int_eq(0b00000001, bit_lib_get_bits(value, 1, 8)); + mu_assert_int_eq(0b00000011, bit_lib_get_bits(value, 2, 8)); + mu_assert_int_eq(0b00000111, bit_lib_get_bits(value, 3, 8)); + mu_assert_int_eq(0b00001111, bit_lib_get_bits(value, 4, 8)); + mu_assert_int_eq(0b00011111, bit_lib_get_bits(value, 5, 8)); + mu_assert_int_eq(0b00111111, bit_lib_get_bits(value, 6, 8)); + mu_assert_int_eq(0b01111111, bit_lib_get_bits(value, 7, 8)); + mu_assert_int_eq(0b11111111, bit_lib_get_bits(value, 8, 8)); +} + +MU_TEST(test_bit_lib_get_bits_16) { + uint8_t value[2] = {0b00001001, 0b10110001}; + mu_assert_int_eq(0b0, bit_lib_get_bits_16(value, 0, 1)); + mu_assert_int_eq(0b00, bit_lib_get_bits_16(value, 0, 2)); + mu_assert_int_eq(0b000, bit_lib_get_bits_16(value, 0, 3)); + mu_assert_int_eq(0b0000, bit_lib_get_bits_16(value, 0, 4)); + mu_assert_int_eq(0b00001, bit_lib_get_bits_16(value, 0, 5)); + mu_assert_int_eq(0b000010, bit_lib_get_bits_16(value, 0, 6)); + mu_assert_int_eq(0b0000100, bit_lib_get_bits_16(value, 0, 7)); + mu_assert_int_eq(0b00001001, bit_lib_get_bits_16(value, 0, 8)); + mu_assert_int_eq(0b000010011, bit_lib_get_bits_16(value, 0, 9)); + mu_assert_int_eq(0b0000100110, bit_lib_get_bits_16(value, 0, 10)); + mu_assert_int_eq(0b00001001101, bit_lib_get_bits_16(value, 0, 11)); + mu_assert_int_eq(0b000010011011, bit_lib_get_bits_16(value, 0, 12)); + mu_assert_int_eq(0b0000100110110, bit_lib_get_bits_16(value, 0, 13)); + mu_assert_int_eq(0b00001001101100, bit_lib_get_bits_16(value, 0, 14)); + mu_assert_int_eq(0b000010011011000, bit_lib_get_bits_16(value, 0, 15)); + mu_assert_int_eq(0b0000100110110001, bit_lib_get_bits_16(value, 0, 16)); +} + +MU_TEST(test_bit_lib_get_bits_32) { + uint8_t value[4] = {0b00001001, 0b10110001, 0b10001100, 0b01100010}; + mu_assert_int_eq(0b0, bit_lib_get_bits_32(value, 0, 1)); + mu_assert_int_eq(0b00, bit_lib_get_bits_32(value, 0, 2)); + mu_assert_int_eq(0b000, bit_lib_get_bits_32(value, 0, 3)); + mu_assert_int_eq(0b0000, bit_lib_get_bits_32(value, 0, 4)); + mu_assert_int_eq(0b00001, bit_lib_get_bits_32(value, 0, 5)); + mu_assert_int_eq(0b000010, bit_lib_get_bits_32(value, 0, 6)); + mu_assert_int_eq(0b0000100, bit_lib_get_bits_32(value, 0, 7)); + mu_assert_int_eq(0b00001001, bit_lib_get_bits_32(value, 0, 8)); + mu_assert_int_eq(0b000010011, bit_lib_get_bits_32(value, 0, 9)); + mu_assert_int_eq(0b0000100110, bit_lib_get_bits_32(value, 0, 10)); + mu_assert_int_eq(0b00001001101, bit_lib_get_bits_32(value, 0, 11)); + mu_assert_int_eq(0b000010011011, bit_lib_get_bits_32(value, 0, 12)); + mu_assert_int_eq(0b0000100110110, bit_lib_get_bits_32(value, 0, 13)); + mu_assert_int_eq(0b00001001101100, bit_lib_get_bits_32(value, 0, 14)); + mu_assert_int_eq(0b000010011011000, bit_lib_get_bits_32(value, 0, 15)); + mu_assert_int_eq(0b0000100110110001, bit_lib_get_bits_32(value, 0, 16)); + mu_assert_int_eq(0b00001001101100011, bit_lib_get_bits_32(value, 0, 17)); + mu_assert_int_eq(0b000010011011000110, bit_lib_get_bits_32(value, 0, 18)); + mu_assert_int_eq(0b0000100110110001100, bit_lib_get_bits_32(value, 0, 19)); + mu_assert_int_eq(0b00001001101100011000, bit_lib_get_bits_32(value, 0, 20)); + mu_assert_int_eq(0b000010011011000110001, bit_lib_get_bits_32(value, 0, 21)); + mu_assert_int_eq(0b0000100110110001100011, bit_lib_get_bits_32(value, 0, 22)); + mu_assert_int_eq(0b00001001101100011000110, bit_lib_get_bits_32(value, 0, 23)); + mu_assert_int_eq(0b000010011011000110001100, bit_lib_get_bits_32(value, 0, 24)); + mu_assert_int_eq(0b0000100110110001100011000, bit_lib_get_bits_32(value, 0, 25)); + mu_assert_int_eq(0b00001001101100011000110001, bit_lib_get_bits_32(value, 0, 26)); + mu_assert_int_eq(0b000010011011000110001100011, bit_lib_get_bits_32(value, 0, 27)); + mu_assert_int_eq(0b0000100110110001100011000110, bit_lib_get_bits_32(value, 0, 28)); + mu_assert_int_eq(0b00001001101100011000110001100, bit_lib_get_bits_32(value, 0, 29)); + mu_assert_int_eq(0b000010011011000110001100011000, bit_lib_get_bits_32(value, 0, 30)); + mu_assert_int_eq(0b0000100110110001100011000110001, bit_lib_get_bits_32(value, 0, 31)); + mu_assert_int_eq(0b00001001101100011000110001100010, bit_lib_get_bits_32(value, 0, 32)); +} + +MU_TEST(test_bit_lib_test_parity_u32) { + // test even parity + mu_assert_int_eq(bit_lib_test_parity_32(0b00000000, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000001, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000010, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000011, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000100, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000101, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000110, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000111, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001000, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001001, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001010, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001011, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001100, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001101, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001110, BitLibParityEven), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001111, BitLibParityEven), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00010000, BitLibParityEven), 1); + + // test odd parity + mu_assert_int_eq(bit_lib_test_parity_32(0b00000000, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000001, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000010, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000011, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000100, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000101, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000110, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00000111, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001000, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001001, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001010, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001011, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001100, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001101, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001110, BitLibParityOdd), 0); + mu_assert_int_eq(bit_lib_test_parity_32(0b00001111, BitLibParityOdd), 1); + mu_assert_int_eq(bit_lib_test_parity_32(0b00010000, BitLibParityOdd), 0); +} + +MU_TEST(test_bit_lib_test_parity) { + // next data contains valid parity for 1-3 nibble and invalid for 4 nibble + uint8_t data_always_0_parity[2] = {0b11101110, 0b11101111}; + uint8_t data_always_1_parity[2] = {0b00010001, 0b00010000}; + uint8_t data_always_odd_parity[2] = {0b00000011, 0b11110111}; + uint8_t data_always_even_parity[2] = {0b00010111, 0b10110011}; + + // test alawys 0 parity + mu_check(bit_lib_test_parity(data_always_0_parity, 0, 12, BitLibParityAlways0, 4)); + mu_check(bit_lib_test_parity(data_always_0_parity, 4, 8, BitLibParityAlways0, 4)); + mu_check(bit_lib_test_parity(data_always_0_parity, 8, 4, BitLibParityAlways0, 4)); + mu_check(bit_lib_test_parity(data_always_1_parity, 12, 4, BitLibParityAlways0, 4)); + + mu_check(!bit_lib_test_parity(data_always_0_parity, 0, 16, BitLibParityAlways0, 4)); + mu_check(!bit_lib_test_parity(data_always_0_parity, 4, 12, BitLibParityAlways0, 4)); + mu_check(!bit_lib_test_parity(data_always_0_parity, 8, 8, BitLibParityAlways0, 4)); + mu_check(!bit_lib_test_parity(data_always_0_parity, 12, 4, BitLibParityAlways0, 4)); + + // test alawys 1 parity + mu_check(bit_lib_test_parity(data_always_1_parity, 0, 12, BitLibParityAlways1, 4)); + mu_check(bit_lib_test_parity(data_always_1_parity, 4, 8, BitLibParityAlways1, 4)); + mu_check(bit_lib_test_parity(data_always_1_parity, 8, 4, BitLibParityAlways1, 4)); + mu_check(bit_lib_test_parity(data_always_0_parity, 12, 4, BitLibParityAlways1, 4)); + + mu_check(!bit_lib_test_parity(data_always_1_parity, 0, 16, BitLibParityAlways1, 4)); + mu_check(!bit_lib_test_parity(data_always_1_parity, 4, 12, BitLibParityAlways1, 4)); + mu_check(!bit_lib_test_parity(data_always_1_parity, 8, 8, BitLibParityAlways1, 4)); + mu_check(!bit_lib_test_parity(data_always_1_parity, 12, 4, BitLibParityAlways1, 4)); + + // test odd parity + mu_check(bit_lib_test_parity(data_always_odd_parity, 0, 12, BitLibParityOdd, 4)); + mu_check(bit_lib_test_parity(data_always_odd_parity, 4, 8, BitLibParityOdd, 4)); + mu_check(bit_lib_test_parity(data_always_odd_parity, 8, 4, BitLibParityOdd, 4)); + mu_check(bit_lib_test_parity(data_always_even_parity, 12, 4, BitLibParityOdd, 4)); + + mu_check(!bit_lib_test_parity(data_always_odd_parity, 0, 16, BitLibParityOdd, 4)); + mu_check(!bit_lib_test_parity(data_always_odd_parity, 4, 12, BitLibParityOdd, 4)); + mu_check(!bit_lib_test_parity(data_always_odd_parity, 8, 8, BitLibParityOdd, 4)); + mu_check(!bit_lib_test_parity(data_always_odd_parity, 12, 4, BitLibParityOdd, 4)); + + // test even parity + mu_check(bit_lib_test_parity(data_always_even_parity, 0, 12, BitLibParityEven, 4)); + mu_check(bit_lib_test_parity(data_always_even_parity, 4, 8, BitLibParityEven, 4)); + mu_check(bit_lib_test_parity(data_always_even_parity, 8, 4, BitLibParityEven, 4)); + mu_check(bit_lib_test_parity(data_always_odd_parity, 12, 4, BitLibParityEven, 4)); + + mu_check(!bit_lib_test_parity(data_always_even_parity, 0, 16, BitLibParityEven, 4)); + mu_check(!bit_lib_test_parity(data_always_even_parity, 4, 12, BitLibParityEven, 4)); + mu_check(!bit_lib_test_parity(data_always_even_parity, 8, 8, BitLibParityEven, 4)); + mu_check(!bit_lib_test_parity(data_always_even_parity, 12, 4, BitLibParityEven, 4)); +} + +MU_TEST(test_bit_lib_remove_bit_every_nth) { + // TODO FL-3494: more tests + uint8_t data_i[1] = {0b00001111}; + uint8_t data_o[1] = {0b00011111}; + size_t length; + + length = bit_lib_remove_bit_every_nth(data_i, 0, 8, 3); + mu_assert_int_eq(6, length); + mu_assert_mem_eq(data_o, data_i, 1); +} + +MU_TEST(test_bit_lib_reverse_bits) { + uint8_t data_1_i[2] = {0b11001010, 0b00011111}; + uint8_t data_1_o[2] = {0b11111000, 0b01010011}; + + // reverse bits [0..15] + bit_lib_reverse_bits(data_1_i, 0, 16); + mu_assert_mem_eq(data_1_o, data_1_i, 2); + + uint8_t data_2_i[2] = {0b11001010, 0b00011111}; + uint8_t data_2_o[2] = {0b11001000, 0b01011111}; + + // reverse bits [4..11] + bit_lib_reverse_bits(data_2_i, 4, 8); + mu_assert_mem_eq(data_2_o, data_2_i, 2); +} + +MU_TEST(test_bit_lib_copy_bits) { + uint8_t data_1_i[2] = {0b11001010, 0b00011111}; + uint8_t data_1_o[2] = {0}; + + // data_1_o[0..15] = data_1_i[0..15] + bit_lib_copy_bits(data_1_o, 0, 16, data_1_i, 0); + mu_assert_mem_eq(data_1_i, data_1_o, 2); + + memset(data_1_o, 0, 2); + // data_1_o[4..11] = data_1_i[0..7] + bit_lib_copy_bits(data_1_o, 4, 8, data_1_i, 0); + mu_assert_mem_eq(((uint8_t[]){0b00001100, 0b10100000}), data_1_o, 2); +} + +MU_TEST(test_bit_lib_get_bit_count) { + mu_assert_int_eq(0, bit_lib_get_bit_count(0)); + mu_assert_int_eq(1, bit_lib_get_bit_count(0b1)); + mu_assert_int_eq(1, bit_lib_get_bit_count(0b10)); + mu_assert_int_eq(2, bit_lib_get_bit_count(0b11)); + mu_assert_int_eq(4, bit_lib_get_bit_count(0b11000011)); + mu_assert_int_eq(6, bit_lib_get_bit_count(0b11000011000011)); + mu_assert_int_eq(8, bit_lib_get_bit_count(0b11111111)); + mu_assert_int_eq(16, bit_lib_get_bit_count(0b11111110000000000000000111111111)); + mu_assert_int_eq(32, bit_lib_get_bit_count(0b11111111111111111111111111111111)); +} + +MU_TEST(test_bit_lib_reverse_16_fast) { + mu_assert_int_eq(0b0000000000000000, bit_lib_reverse_16_fast(0b0000000000000000)); + mu_assert_int_eq(0b1000000000000000, bit_lib_reverse_16_fast(0b0000000000000001)); + mu_assert_int_eq(0b1100000000000000, bit_lib_reverse_16_fast(0b0000000000000011)); + mu_assert_int_eq(0b0000100000001001, bit_lib_reverse_16_fast(0b1001000000010000)); +} + +MU_TEST(test_bit_lib_crc16) { + uint8_t data[9] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'}; + uint8_t data_size = 9; + + // Algorithm + // Check Poly Init RefIn RefOut XorOut + // CRC-16/CCITT-FALSE + // 0x29B1 0x1021 0xFFFF false false 0x0000 + mu_assert_int_eq(0x29B1, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, false, false, 0x0000)); + // CRC-16/ARC + // 0xBB3D 0x8005 0x0000 true true 0x0000 + mu_assert_int_eq(0xBB3D, bit_lib_crc16(data, data_size, 0x8005, 0x0000, true, true, 0x0000)); + // CRC-16/AUG-CCITT + // 0xE5CC 0x1021 0x1D0F false false 0x0000 + mu_assert_int_eq(0xE5CC, bit_lib_crc16(data, data_size, 0x1021, 0x1D0F, false, false, 0x0000)); + // CRC-16/BUYPASS + // 0xFEE8 0x8005 0x0000 false false 0x0000 + mu_assert_int_eq(0xFEE8, bit_lib_crc16(data, data_size, 0x8005, 0x0000, false, false, 0x0000)); + // CRC-16/CDMA2000 + // 0x4C06 0xC867 0xFFFF false false 0x0000 + mu_assert_int_eq(0x4C06, bit_lib_crc16(data, data_size, 0xC867, 0xFFFF, false, false, 0x0000)); + // CRC-16/DDS-110 + // 0x9ECF 0x8005 0x800D false false 0x0000 + mu_assert_int_eq(0x9ECF, bit_lib_crc16(data, data_size, 0x8005, 0x800D, false, false, 0x0000)); + // CRC-16/DECT-R + // 0x007E 0x0589 0x0000 false false 0x0001 + mu_assert_int_eq(0x007E, bit_lib_crc16(data, data_size, 0x0589, 0x0000, false, false, 0x0001)); + // CRC-16/DECT-X + // 0x007F 0x0589 0x0000 false false 0x0000 + mu_assert_int_eq(0x007F, bit_lib_crc16(data, data_size, 0x0589, 0x0000, false, false, 0x0000)); + // CRC-16/DNP + // 0xEA82 0x3D65 0x0000 true true 0xFFFF + mu_assert_int_eq(0xEA82, bit_lib_crc16(data, data_size, 0x3D65, 0x0000, true, true, 0xFFFF)); + // CRC-16/EN-13757 + // 0xC2B7 0x3D65 0x0000 false false 0xFFFF + mu_assert_int_eq(0xC2B7, bit_lib_crc16(data, data_size, 0x3D65, 0x0000, false, false, 0xFFFF)); + // CRC-16/GENIBUS + // 0xD64E 0x1021 0xFFFF false false 0xFFFF + mu_assert_int_eq(0xD64E, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, false, false, 0xFFFF)); + // CRC-16/MAXIM + // 0x44C2 0x8005 0x0000 true true 0xFFFF + mu_assert_int_eq(0x44C2, bit_lib_crc16(data, data_size, 0x8005, 0x0000, true, true, 0xFFFF)); + // CRC-16/MCRF4XX + // 0x6F91 0x1021 0xFFFF true true 0x0000 + mu_assert_int_eq(0x6F91, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, true, true, 0x0000)); + // CRC-16/RIELLO + // 0x63D0 0x1021 0xB2AA true true 0x0000 + mu_assert_int_eq(0x63D0, bit_lib_crc16(data, data_size, 0x1021, 0xB2AA, true, true, 0x0000)); + // CRC-16/T10-DIF + // 0xD0DB 0x8BB7 0x0000 false false 0x0000 + mu_assert_int_eq(0xD0DB, bit_lib_crc16(data, data_size, 0x8BB7, 0x0000, false, false, 0x0000)); + // CRC-16/TELEDISK + // 0x0FB3 0xA097 0x0000 false false 0x0000 + mu_assert_int_eq(0x0FB3, bit_lib_crc16(data, data_size, 0xA097, 0x0000, false, false, 0x0000)); + // CRC-16/TMS37157 + // 0x26B1 0x1021 0x89EC true true 0x0000 + mu_assert_int_eq(0x26B1, bit_lib_crc16(data, data_size, 0x1021, 0x89EC, true, true, 0x0000)); + // CRC-16/USB + // 0xB4C8 0x8005 0xFFFF true true 0xFFFF + mu_assert_int_eq(0xB4C8, bit_lib_crc16(data, data_size, 0x8005, 0xFFFF, true, true, 0xFFFF)); + // CRC-A + // 0xBF05 0x1021 0xC6C6 true true 0x0000 + mu_assert_int_eq(0xBF05, bit_lib_crc16(data, data_size, 0x1021, 0xC6C6, true, true, 0x0000)); + // CRC-16/KERMIT + // 0x2189 0x1021 0x0000 true true 0x0000 + mu_assert_int_eq(0x2189, bit_lib_crc16(data, data_size, 0x1021, 0x0000, true, true, 0x0000)); + // CRC-16/MODBUS + // 0x4B37 0x8005 0xFFFF true true 0x0000 + mu_assert_int_eq(0x4B37, bit_lib_crc16(data, data_size, 0x8005, 0xFFFF, true, true, 0x0000)); + // CRC-16/X-25 + // 0x906E 0x1021 0xFFFF true true 0xFFFF + mu_assert_int_eq(0x906E, bit_lib_crc16(data, data_size, 0x1021, 0xFFFF, true, true, 0xFFFF)); + // CRC-16/XMODEM + // 0x31C3 0x1021 0x0000 false false 0x0000 + mu_assert_int_eq(0x31C3, bit_lib_crc16(data, data_size, 0x1021, 0x0000, false, false, 0x0000)); +} + +MU_TEST_SUITE(test_bit_lib) { + MU_RUN_TEST(test_bit_lib_increment_index); + MU_RUN_TEST(test_bit_lib_is_set); + MU_RUN_TEST(test_bit_lib_push); + MU_RUN_TEST(test_bit_lib_set_bit); + MU_RUN_TEST(test_bit_lib_set_bits); + MU_RUN_TEST(test_bit_lib_get_bit); + MU_RUN_TEST(test_bit_lib_get_bits); + MU_RUN_TEST(test_bit_lib_get_bits_16); + MU_RUN_TEST(test_bit_lib_get_bits_32); + MU_RUN_TEST(test_bit_lib_test_parity_u32); + MU_RUN_TEST(test_bit_lib_test_parity); + MU_RUN_TEST(test_bit_lib_remove_bit_every_nth); + MU_RUN_TEST(test_bit_lib_copy_bits); + MU_RUN_TEST(test_bit_lib_reverse_bits); + MU_RUN_TEST(test_bit_lib_get_bit_count); + MU_RUN_TEST(test_bit_lib_reverse_16_fast); + MU_RUN_TEST(test_bit_lib_crc16); +} + +int run_minunit_test_bit_lib() { + MU_RUN_SUITE(test_bit_lib); + return MU_EXIT_CODE; +} \ No newline at end of file diff --git a/applications/debug/unit_tests/lfrfid/lfrfid_protocols.c b/applications/debug/unit_tests/lfrfid/lfrfid_protocols.c new file mode 100644 index 00000000000..4401cbb4d3c --- /dev/null +++ b/applications/debug/unit_tests/lfrfid/lfrfid_protocols.c @@ -0,0 +1,464 @@ +#include +#include "../minunit.h" +#include +#include +#include + +#define LF_RFID_READ_TIMING_MULTIPLIER 8 + +#define EM_TEST_DATA \ + { 0x58, 0x00, 0x85, 0x64, 0x02 } +#define EM_TEST_DATA_SIZE 5 +#define EM_TEST_EMULATION_TIMINGS_COUNT (64 * 2) + +const int8_t em_test_timings[EM_TEST_EMULATION_TIMINGS_COUNT] = { + 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, -32, + 32, 32, -32, -32, 32, 32, -32, -32, 32, 32, -32, -32, 32, -32, 32, -32, 32, 32, -32, + -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, + 32, 32, -32, -32, 32, -32, 32, -32, 32, 32, -32, -32, 32, 32, -32, -32, 32, 32, -32, + -32, 32, -32, 32, 32, -32, 32, -32, -32, 32, -32, 32, -32, 32, 32, -32, -32, 32, -32, + 32, 32, -32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, 32, -32, + -32, 32, 32, -32, -32, 32, -32, 32, -32, 32, -32, 32, -32, 32, +}; + +#define HID10301_TEST_DATA \ + { 0x8D, 0x48, 0xA8 } +#define HID10301_TEST_DATA_SIZE 3 +#define HID10301_TEST_EMULATION_TIMINGS_COUNT (541 * 2) + +const int8_t hid10301_test_timings[HID10301_TEST_EMULATION_TIMINGS_COUNT] = { + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, +}; + +#define IOPROX_XSF_TEST_DATA \ + { 0x65, 0x01, 0x05, 0x39 } +#define IOPROX_XSF_TEST_DATA_SIZE 4 +#define IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT (468 * 2) + +const int8_t ioprox_xsf_test_timings[IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT] = { + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, + 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, 4, -4, + 4, -4, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, + 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, 5, -5, +}; + +#define INDALA26_EMULATION_TIMINGS_COUNT (1024 * 2) +#define INDALA26_TEST_DATA \ + { 0x3B, 0x73, 0x64, 0xA8 } +#define INDALA26_TEST_DATA_SIZE 4 + +const int8_t indala26_test_timings[INDALA26_EMULATION_TIMINGS_COUNT] = { + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, + 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 1, +}; + +MU_TEST(test_lfrfid_protocol_em_read_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq(EM_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolEM4100)); + mu_assert_string_eq("EM4100", protocol_dict_get_name(dict, LFRFIDProtocolEM4100)); + mu_assert_string_eq("EM-Micro", protocol_dict_get_manufacturer(dict, LFRFIDProtocolEM4100)); + + const uint8_t data[EM_TEST_DATA_SIZE] = EM_TEST_DATA; + + protocol_dict_decoders_start(dict); + + ProtocolId protocol = PROTOCOL_NO; + PulseGlue* pulse_glue = pulse_glue_alloc(); + + for(size_t i = 0; i < EM_TEST_EMULATION_TIMINGS_COUNT * 10; i++) { + bool pulse_pop = pulse_glue_push( + pulse_glue, + em_test_timings[i % EM_TEST_EMULATION_TIMINGS_COUNT] >= 0, + abs(em_test_timings[i % EM_TEST_EMULATION_TIMINGS_COUNT]) * + LF_RFID_READ_TIMING_MULTIPLIER); + + if(pulse_pop) { + uint32_t length, period; + pulse_glue_pop(pulse_glue, &length, &period); + + protocol = protocol_dict_decoders_feed(dict, true, period); + if(protocol != PROTOCOL_NO) break; + + protocol = protocol_dict_decoders_feed(dict, false, length - period); + if(protocol != PROTOCOL_NO) break; + } + } + + pulse_glue_free(pulse_glue); + + mu_assert_int_eq(LFRFIDProtocolEM4100, protocol); + uint8_t received_data[EM_TEST_DATA_SIZE] = {0}; + protocol_dict_get_data(dict, protocol, received_data, EM_TEST_DATA_SIZE); + + mu_assert_mem_eq(data, received_data, EM_TEST_DATA_SIZE); + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_em_emulate_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq(EM_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolEM4100)); + mu_assert_string_eq("EM4100", protocol_dict_get_name(dict, LFRFIDProtocolEM4100)); + mu_assert_string_eq("EM-Micro", protocol_dict_get_manufacturer(dict, LFRFIDProtocolEM4100)); + + const uint8_t data[EM_TEST_DATA_SIZE] = EM_TEST_DATA; + + protocol_dict_set_data(dict, LFRFIDProtocolEM4100, data, EM_TEST_DATA_SIZE); + mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolEM4100)); + + for(size_t i = 0; i < EM_TEST_EMULATION_TIMINGS_COUNT; i++) { + LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolEM4100); + + if(level_duration_get_level(level_duration)) { + mu_assert_int_eq(em_test_timings[i], level_duration_get_duration(level_duration)); + } else { + mu_assert_int_eq(em_test_timings[i], -level_duration_get_duration(level_duration)); + } + } + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_h10301_read_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq( + HID10301_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolH10301)); + mu_assert_string_eq("H10301", protocol_dict_get_name(dict, LFRFIDProtocolH10301)); + mu_assert_string_eq("HID", protocol_dict_get_manufacturer(dict, LFRFIDProtocolH10301)); + + const uint8_t data[HID10301_TEST_DATA_SIZE] = HID10301_TEST_DATA; + + protocol_dict_decoders_start(dict); + + ProtocolId protocol = PROTOCOL_NO; + PulseGlue* pulse_glue = pulse_glue_alloc(); + + for(size_t i = 0; i < HID10301_TEST_EMULATION_TIMINGS_COUNT * 10; i++) { + bool pulse_pop = pulse_glue_push( + pulse_glue, + hid10301_test_timings[i % HID10301_TEST_EMULATION_TIMINGS_COUNT] >= 0, + abs(hid10301_test_timings[i % HID10301_TEST_EMULATION_TIMINGS_COUNT]) * + LF_RFID_READ_TIMING_MULTIPLIER); + + if(pulse_pop) { + uint32_t length, period; + pulse_glue_pop(pulse_glue, &length, &period); + + protocol = protocol_dict_decoders_feed(dict, true, period); + if(protocol != PROTOCOL_NO) break; + + protocol = protocol_dict_decoders_feed(dict, false, length - period); + if(protocol != PROTOCOL_NO) break; + } + } + + pulse_glue_free(pulse_glue); + + mu_assert_int_eq(LFRFIDProtocolH10301, protocol); + uint8_t received_data[HID10301_TEST_DATA_SIZE] = {0}; + protocol_dict_get_data(dict, protocol, received_data, HID10301_TEST_DATA_SIZE); + + mu_assert_mem_eq(data, received_data, HID10301_TEST_DATA_SIZE); + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_h10301_emulate_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq( + HID10301_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolH10301)); + mu_assert_string_eq("H10301", protocol_dict_get_name(dict, LFRFIDProtocolH10301)); + mu_assert_string_eq("HID", protocol_dict_get_manufacturer(dict, LFRFIDProtocolH10301)); + + const uint8_t data[HID10301_TEST_DATA_SIZE] = HID10301_TEST_DATA; + + protocol_dict_set_data(dict, LFRFIDProtocolH10301, data, HID10301_TEST_DATA_SIZE); + mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolH10301)); + + for(size_t i = 0; i < HID10301_TEST_EMULATION_TIMINGS_COUNT; i++) { + LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolH10301); + + if(level_duration_get_level(level_duration)) { + mu_assert_int_eq( + hid10301_test_timings[i], level_duration_get_duration(level_duration)); + } else { + mu_assert_int_eq( + hid10301_test_timings[i], -level_duration_get_duration(level_duration)); + } + } + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_ioprox_xsf_read_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq( + IOPROX_XSF_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIOProxXSF)); + mu_assert_string_eq("IoProxXSF", protocol_dict_get_name(dict, LFRFIDProtocolIOProxXSF)); + mu_assert_string_eq("Kantech", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIOProxXSF)); + + const uint8_t data[IOPROX_XSF_TEST_DATA_SIZE] = IOPROX_XSF_TEST_DATA; + + protocol_dict_decoders_start(dict); + + ProtocolId protocol = PROTOCOL_NO; + PulseGlue* pulse_glue = pulse_glue_alloc(); + + for(size_t i = 0; i < IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT * 10; i++) { + bool pulse_pop = pulse_glue_push( + pulse_glue, + ioprox_xsf_test_timings[i % IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT] >= 0, + abs(ioprox_xsf_test_timings[i % IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT]) * + LF_RFID_READ_TIMING_MULTIPLIER); + + if(pulse_pop) { + uint32_t length, period; + pulse_glue_pop(pulse_glue, &length, &period); + + protocol = protocol_dict_decoders_feed(dict, true, period); + if(protocol != PROTOCOL_NO) break; + + protocol = protocol_dict_decoders_feed(dict, false, length - period); + if(protocol != PROTOCOL_NO) break; + } + } + + pulse_glue_free(pulse_glue); + + mu_assert_int_eq(LFRFIDProtocolIOProxXSF, protocol); + uint8_t received_data[IOPROX_XSF_TEST_DATA_SIZE] = {0}; + protocol_dict_get_data(dict, protocol, received_data, IOPROX_XSF_TEST_DATA_SIZE); + + mu_assert_mem_eq(data, received_data, IOPROX_XSF_TEST_DATA_SIZE); + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_ioprox_xsf_emulate_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq( + IOPROX_XSF_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIOProxXSF)); + mu_assert_string_eq("IoProxXSF", protocol_dict_get_name(dict, LFRFIDProtocolIOProxXSF)); + mu_assert_string_eq("Kantech", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIOProxXSF)); + + const uint8_t data[IOPROX_XSF_TEST_DATA_SIZE] = IOPROX_XSF_TEST_DATA; + + protocol_dict_set_data(dict, LFRFIDProtocolIOProxXSF, data, IOPROX_XSF_TEST_DATA_SIZE); + mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolIOProxXSF)); + + for(size_t i = 0; i < IOPROX_XSF_TEST_EMULATION_TIMINGS_COUNT; i++) { + LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolIOProxXSF); + + if(level_duration_get_level(level_duration)) { + mu_assert_int_eq( + ioprox_xsf_test_timings[i], level_duration_get_duration(level_duration)); + } else { + mu_assert_int_eq( + ioprox_xsf_test_timings[i], -level_duration_get_duration(level_duration)); + } + } + + protocol_dict_free(dict); +} + +MU_TEST(test_lfrfid_protocol_inadala26_emulate_simple) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + mu_assert_int_eq( + INDALA26_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIndala26)); + mu_assert_string_eq("Indala26", protocol_dict_get_name(dict, LFRFIDProtocolIndala26)); + mu_assert_string_eq("Motorola", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIndala26)); + + const uint8_t data[INDALA26_TEST_DATA_SIZE] = INDALA26_TEST_DATA; + + protocol_dict_set_data(dict, LFRFIDProtocolIndala26, data, INDALA26_TEST_DATA_SIZE); + mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolIndala26)); + + for(size_t i = 0; i < INDALA26_EMULATION_TIMINGS_COUNT; i++) { + LevelDuration level_duration = protocol_dict_encoder_yield(dict, LFRFIDProtocolIndala26); + + if(level_duration_get_level(level_duration)) { + mu_assert_int_eq( + indala26_test_timings[i], level_duration_get_duration(level_duration)); + } else { + mu_assert_int_eq( + indala26_test_timings[i], -level_duration_get_duration(level_duration)); + } + } + + protocol_dict_free(dict); +} + +MU_TEST_SUITE(test_lfrfid_protocols_suite) { + MU_RUN_TEST(test_lfrfid_protocol_em_read_simple); + MU_RUN_TEST(test_lfrfid_protocol_em_emulate_simple); + + MU_RUN_TEST(test_lfrfid_protocol_h10301_read_simple); + MU_RUN_TEST(test_lfrfid_protocol_h10301_emulate_simple); + + MU_RUN_TEST(test_lfrfid_protocol_ioprox_xsf_read_simple); + MU_RUN_TEST(test_lfrfid_protocol_ioprox_xsf_emulate_simple); + + MU_RUN_TEST(test_lfrfid_protocol_inadala26_emulate_simple); +} + +int run_minunit_test_lfrfid_protocols() { + MU_RUN_SUITE(test_lfrfid_protocols_suite); + return MU_EXIT_CODE; +} \ No newline at end of file diff --git a/applications/debug/unit_tests/manifest/manifest.c b/applications/debug/unit_tests/manifest/manifest.c new file mode 100644 index 00000000000..19370b0e13f --- /dev/null +++ b/applications/debug/unit_tests/manifest/manifest.c @@ -0,0 +1,75 @@ +#include +#include "../minunit.h" +#include + +#define TAG "Manifest" + +MU_TEST(manifest_type_test) { + mu_assert(ResourceManifestEntryTypeUnknown == 0, "ResourceManifestEntryTypeUnknown != 0\r\n"); + mu_assert(ResourceManifestEntryTypeVersion == 1, "ResourceManifestEntryTypeVersion != 1\r\n"); + mu_assert( + ResourceManifestEntryTypeTimestamp == 2, "ResourceManifestEntryTypeTimestamp != 2\r\n"); + mu_assert( + ResourceManifestEntryTypeDirectory == 3, "ResourceManifestEntryTypeDirectory != 3\r\n"); + mu_assert(ResourceManifestEntryTypeFile == 4, "ResourceManifestEntryTypeFile != 4\r\n"); +} + +MU_TEST(manifest_iteration_test) { + bool result = true; + size_t counters[5] = {0}; + + Storage* storage = furi_record_open(RECORD_STORAGE); + ResourceManifestReader* manifest_reader = resource_manifest_reader_alloc(storage); + do { + // Open manifest file + if(!resource_manifest_reader_open(manifest_reader, EXT_PATH("unit_tests/Manifest_test"))) { + result = false; + break; + } + + // Iterate forward + ResourceManifestEntry* entry_ptr = NULL; + while((entry_ptr = resource_manifest_reader_next(manifest_reader))) { + FURI_LOG_D(TAG, "F:%u:%s", entry_ptr->type, furi_string_get_cstr(entry_ptr->name)); + if(entry_ptr->type > 4) { + mu_fail("entry_ptr->type > 4\r\n"); + result = false; + break; + } + counters[entry_ptr->type]++; + } + if(!result) break; + + // Iterate backward + while((entry_ptr = resource_manifest_reader_previous(manifest_reader))) { + FURI_LOG_D(TAG, "B:%u:%s", entry_ptr->type, furi_string_get_cstr(entry_ptr->name)); + if(entry_ptr->type > 4) { + mu_fail("entry_ptr->type > 4\r\n"); + result = false; + break; + } + counters[entry_ptr->type]--; + } + } while(false); + + resource_manifest_reader_free(manifest_reader); + furi_record_close(RECORD_STORAGE); + + mu_assert(counters[ResourceManifestEntryTypeUnknown] == 0, "Unknown counter != 0\r\n"); + mu_assert(counters[ResourceManifestEntryTypeVersion] == 0, "Version counter != 0\r\n"); + mu_assert(counters[ResourceManifestEntryTypeTimestamp] == 0, "Timestamp counter != 0\r\n"); + mu_assert(counters[ResourceManifestEntryTypeDirectory] == 0, "Directory counter != 0\r\n"); + mu_assert(counters[ResourceManifestEntryTypeFile] == 0, "File counter != 0\r\n"); + + mu_assert(result, "Manifest forward iterate failed\r\n"); +} + +MU_TEST_SUITE(manifest_suite) { + MU_RUN_TEST(manifest_type_test); + MU_RUN_TEST(manifest_iteration_test); +} + +int run_minunit_test_manifest() { + MU_RUN_SUITE(manifest_suite); + return MU_EXIT_CODE; +} \ No newline at end of file diff --git a/applications/unit_tests/minunit.h b/applications/debug/unit_tests/minunit.h similarity index 83% rename from applications/unit_tests/minunit.h rename to applications/debug/unit_tests/minunit.h index d1efd13e61f..083db5a9a9a 100644 --- a/applications/unit_tests/minunit.h +++ b/applications/debug/unit_tests/minunit.h @@ -81,6 +81,7 @@ __attribute__((unused)) static void (*minunit_teardown)(void) = NULL; void minunit_print_progress(void); void minunit_print_fail(const char* error); +void minunit_printf_warning(const char* format, ...); /* Definitions */ #define MU_TEST(method_name) static void method_name(void) @@ -150,47 +151,51 @@ void minunit_print_fail(const char* error); minunit_end_proc_timer - minunit_proc_timer);) #define MU_EXIT_CODE minunit_fail +/* Warnings */ +#define mu_warn(message) \ + MU__SAFE_BLOCK(minunit_printf_warning("%s:%d: %s", __FILE__, __LINE__, message);) + /* Assertions */ -#define mu_check(test) \ - MU__SAFE_BLOCK( \ - minunit_assert++; if(!(test)) { \ - snprintf( \ - minunit_last_message, \ - MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %s", \ - __func__, \ - __FILE__, \ - __LINE__, \ - #test); \ - minunit_status = 1; \ - return; \ +#define mu_check(test) \ + MU__SAFE_BLOCK( \ + minunit_assert++; if(!(test)) { \ + snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\r\n\t%s:%d: %s", \ + __func__, \ + __FILE__, \ + __LINE__, \ + #test); \ + minunit_status = 1; \ + return; \ } else { minunit_print_progress(); }) -#define mu_fail(message) \ - MU__SAFE_BLOCK(minunit_assert++; snprintf( \ - minunit_last_message, \ - MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %s", \ - __func__, \ - __FILE__, \ - __LINE__, \ - message); \ - minunit_status = 1; \ +#define mu_fail(message) \ + MU__SAFE_BLOCK(minunit_assert++; snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\r\n\t%s:%d: %s", \ + __func__, \ + __FILE__, \ + __LINE__, \ + message); \ + minunit_status = 1; \ return;) -#define mu_assert(test, message) \ - MU__SAFE_BLOCK( \ - minunit_assert++; if(!(test)) { \ - snprintf( \ - minunit_last_message, \ - MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %s", \ - __func__, \ - __FILE__, \ - __LINE__, \ - message); \ - minunit_status = 1; \ - return; \ +#define mu_assert(test, message) \ + MU__SAFE_BLOCK( \ + minunit_assert++; if(!(test)) { \ + snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\r\n\t%s:%d: %s", \ + __func__, \ + __FILE__, \ + __LINE__, \ + message); \ + minunit_status = 1; \ + return; \ } else { minunit_print_progress(); }) #define mu_assert_int_eq(expected, result) \ @@ -201,7 +206,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %d expected but was %d", \ + "%s failed:\r\n\t%s:%d: %d expected but was %d", \ __func__, \ __FILE__, \ __LINE__, \ @@ -219,7 +224,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: expected different results but both were %d", \ + "%s failed:\r\n\t%s:%d: expected different results but both were %d", \ __func__, \ __FILE__, \ __LINE__, \ @@ -236,7 +241,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %d <= %d", \ + "%s failed:\r\n\t%s:%d: %d <= %d", \ __func__, \ __FILE__, \ __LINE__, \ @@ -254,7 +259,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %d >= %d", \ + "%s failed:\r\n\t%s:%d: %d >= %d", \ __func__, \ __FILE__, \ __LINE__, \ @@ -274,7 +279,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %d was not between (inclusive) %d and %d", \ + "%s failed:\r\n\t%s:%d: %d was not between (inclusive) %d and %d", \ __func__, \ __FILE__, \ __LINE__, \ @@ -302,7 +307,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: expected to be one of %s but was %d", \ + "%s failed:\r\n\t%s:%d: expected to be one of %s but was %d", \ __func__, \ __FILE__, \ __LINE__, \ @@ -316,12 +321,12 @@ void minunit_print_fail(const char* error); MU__SAFE_BLOCK( \ double minunit_tmp_e; double minunit_tmp_r; minunit_assert++; minunit_tmp_e = (expected); \ minunit_tmp_r = (result); \ - if(fabs(minunit_tmp_e - minunit_tmp_r) > MINUNIT_EPSILON) { \ + if(fabs(minunit_tmp_e - minunit_tmp_r) > (double)MINUNIT_EPSILON) { \ int minunit_significant_figures = 1 - log10(MINUNIT_EPSILON); \ snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %.*g expected but was %.*g", \ + "%s failed:\r\n\t%s:%d: %.*g expected but was %.*g", \ __func__, \ __FILE__, \ __LINE__, \ @@ -341,7 +346,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %f <= %f", \ + "%s failed:\r\n\t%s:%d: %f <= %f", \ __func__, \ __FILE__, \ __LINE__, \ @@ -359,7 +364,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %f >= %f", \ + "%s failed:\r\n\t%s:%d: %f >= %f", \ __func__, \ __FILE__, \ __LINE__, \ @@ -379,7 +384,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: %f was not between (inclusive) %f and %f", \ + "%s failed:\r\n\t%s:%d: %f was not between (inclusive) %f and %f", \ __func__, \ __FILE__, \ __LINE__, \ @@ -400,7 +405,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: '%s' expected but was '%s'", \ + "%s failed:\r\n\t%s:%d: '%s' expected but was '%s'", \ __func__, \ __FILE__, \ __LINE__, \ @@ -410,13 +415,41 @@ void minunit_print_fail(const char* error); return; \ } else { minunit_print_progress(); }) +#define mu_assert_mem_eq(expected, result, size) \ + MU__SAFE_BLOCK( \ + const void* minunit_tmp_e = expected; const void* minunit_tmp_r = result; \ + minunit_assert++; \ + if(memcmp(minunit_tmp_e, minunit_tmp_r, size)) { \ + snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\r\n\t%s:%d: mem not equal\r\n\tEXP RES", \ + __func__, \ + __FILE__, \ + __LINE__); \ + for(size_t __index = 0; __index < size; __index++) { \ + if(strlen(minunit_last_message) > MINUNIT_MESSAGE_LEN - 20) break; \ + uint8_t __e = ((uint8_t*)minunit_tmp_e)[__index]; \ + uint8_t __r = ((uint8_t*)minunit_tmp_r)[__index]; \ + snprintf( \ + minunit_last_message + strlen(minunit_last_message), \ + MINUNIT_MESSAGE_LEN - strlen(minunit_last_message), \ + "\r\n\t%02X %s %02X", \ + __e, \ + ((__e == __r) ? ".." : "!="), \ + __r); \ + } \ + minunit_status = 1; \ + return; \ + } else { minunit_print_progress(); }) + #define mu_assert_null(result) \ MU__SAFE_BLOCK( \ minunit_assert++; if(result == NULL) { minunit_print_progress(); } else { \ snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: Expected result was not NULL", \ + "%s failed:\r\n\t%s:%d: Expected result was not NULL", \ __func__, \ __FILE__, \ __LINE__); \ @@ -430,7 +463,7 @@ void minunit_print_fail(const char* error); snprintf( \ minunit_last_message, \ MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: Expected result was not NULL", \ + "%s failed:\r\n\t%s:%d: Expected result was not NULL", \ __func__, \ __FILE__, \ __LINE__); \ @@ -438,32 +471,32 @@ void minunit_print_fail(const char* error); return; \ }) -#define mu_assert_pointers_eq(pointer1, pointer2) \ - MU__SAFE_BLOCK( \ - minunit_assert++; if(pointer1 == pointer2) { minunit_print_progress(); } else { \ - snprintf( \ - minunit_last_message, \ - MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: Expected the pointers to point to the same memory location", \ - __func__, \ - __FILE__, \ - __LINE__); \ - minunit_status = 1; \ - return; \ +#define mu_assert_pointers_eq(pointer1, pointer2) \ + MU__SAFE_BLOCK( \ + minunit_assert++; if(pointer1 == pointer2) { minunit_print_progress(); } else { \ + snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\r\n\t%s:%d: Expected the pointers to point to the same memory location", \ + __func__, \ + __FILE__, \ + __LINE__); \ + minunit_status = 1; \ + return; \ }) -#define mu_assert_pointers_not_eq(pointer1, pointer2) \ - MU__SAFE_BLOCK( \ - minunit_assert++; if(pointer1 != pointer2) { minunit_print_progress(); } else { \ - snprintf( \ - minunit_last_message, \ - MINUNIT_MESSAGE_LEN, \ - "%s failed:\n\t%s:%d: Expected the pointers to point to the same memory location", \ - __func__, \ - __FILE__, \ - __LINE__); \ - minunit_status = 1; \ - return; \ +#define mu_assert_pointers_not_eq(pointer1, pointer2) \ + MU__SAFE_BLOCK( \ + minunit_assert++; if(pointer1 != pointer2) { minunit_print_progress(); } else { \ + snprintf( \ + minunit_last_message, \ + MINUNIT_MESSAGE_LEN, \ + "%s failed:\r\n\t%s:%d: Expected the pointers to point to the same memory location", \ + __func__, \ + __FILE__, \ + __LINE__); \ + minunit_status = 1; \ + return; \ }) /* diff --git a/applications/unit_tests/minunit_vars.h b/applications/debug/unit_tests/minunit_vars.h similarity index 100% rename from applications/unit_tests/minunit_vars.h rename to applications/debug/unit_tests/minunit_vars.h diff --git a/applications/unit_tests/minunit_vars_ex.h b/applications/debug/unit_tests/minunit_vars_ex.h similarity index 100% rename from applications/unit_tests/minunit_vars_ex.h rename to applications/debug/unit_tests/minunit_vars_ex.h diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c new file mode 100644 index 00000000000..0dcd09046df --- /dev/null +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -0,0 +1,554 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../minunit.h" + +#define TAG "NfcTest" + +#define NFC_TEST_NFC_DEV_PATH EXT_PATH("unit_tests/nfc/nfc_device_test.nfc") +#define NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH EXT_PATH("unit_tests/mf_dict.nfc") + +typedef struct { + Storage* storage; +} NfcTest; + +static NfcTest* nfc_test = NULL; + +static void nfc_test_alloc() { + nfc_test = malloc(sizeof(NfcTest)); + nfc_test->storage = furi_record_open(RECORD_STORAGE); +} + +static void nfc_test_free() { + furi_check(nfc_test); + + furi_record_close(RECORD_STORAGE); + free(nfc_test); + nfc_test = NULL; +} + +static void nfc_test_save_and_load(NfcDevice* nfc_device_ref) { + NfcDevice* nfc_device_dut = nfc_device_alloc(); + + mu_assert( + nfc_device_save(nfc_device_ref, NFC_TEST_NFC_DEV_PATH), "nfc_device_save() failed\r\n"); + + mu_assert( + nfc_device_load(nfc_device_dut, NFC_TEST_NFC_DEV_PATH), "nfc_device_load() failed\r\n"); + + mu_assert( + nfc_device_is_equal(nfc_device_ref, nfc_device_dut), + "nfc_device_data_dut != nfc_device_data_ref\r\n"); + + mu_assert( + storage_simply_remove(nfc_test->storage, NFC_TEST_NFC_DEV_PATH), + "storage_simply_remove() failed\r\n"); + + nfc_device_free(nfc_device_dut); +} + +static void iso14443_3a_file_test(uint8_t uid_len) { + NfcDevice* nfc_device = nfc_device_alloc(); + + Iso14443_3aData* data = iso14443_3a_alloc(); + data->uid_len = uid_len; + furi_hal_random_fill_buf(data->uid, uid_len); + furi_hal_random_fill_buf(data->atqa, sizeof(data->atqa)); + furi_hal_random_fill_buf(&data->sak, 1); + + nfc_device_set_data(nfc_device, NfcProtocolIso14443_3a, data); + nfc_test_save_and_load(nfc_device); + + iso14443_3a_free(data); + nfc_device_free(nfc_device); +} + +static void nfc_file_test_with_generator(NfcDataGeneratorType type) { + NfcDevice* nfc_device_ref = nfc_device_alloc(); + + nfc_data_generator_fill_data(type, nfc_device_ref); + nfc_test_save_and_load(nfc_device_ref); + + nfc_device_free(nfc_device_ref); +} + +MU_TEST(iso14443_3a_4b_file_test) { + iso14443_3a_file_test(4); +} + +MU_TEST(iso14443_3a_7b_file_test) { + iso14443_3a_file_test(7); +} + +MU_TEST(mf_ultralight_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralight); +} + +MU_TEST(mf_ultralight_ev1_11_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralightEV1_11); +} + +MU_TEST(mf_ultralight_ev1_h11_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralightEV1_H11); +} + +MU_TEST(mf_ultralight_ev1_21_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralightEV1_21); +} + +MU_TEST(mf_ultralight_ev1_h21_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfUltralightEV1_H21); +} + +MU_TEST(mf_ultralight_ntag_203_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeNTAG203); +} + +MU_TEST(mf_ultralight_ntag_213_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeNTAG213); +} + +MU_TEST(mf_ultralight_ntag_215_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeNTAG215); +} + +MU_TEST(mf_ultralight_ntag_216_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeNTAG216); +} + +MU_TEST(mf_ultralight_ntag_i2c_1k_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeNTAGI2C1k); +} + +MU_TEST(mf_ultralight_ntag_i2c_2k_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeNTAGI2C2k); +} + +MU_TEST(mf_ultralight_ntag_i2c_plus_1k_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeNTAGI2CPlus1k); +} + +MU_TEST(mf_ultralight_ntag_i2c_plus_2k_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeNTAGI2CPlus2k); +} + +MU_TEST(mf_classic_mini_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassicMini); +} + +MU_TEST(mf_classic_1k_4b_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassic1k_4b); +} + +MU_TEST(mf_classic_1k_7b_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassic1k_7b); +} + +MU_TEST(mf_classic_4k_4b_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassic4k_4b); +} + +MU_TEST(mf_classic_4k_7b_file_test) { + nfc_file_test_with_generator(NfcDataGeneratorTypeMfClassic4k_7b); +} + +MU_TEST(iso14443_3a_reader) { + Nfc* poller = nfc_alloc(); + Nfc* listener = nfc_alloc(); + + Iso14443_3aData iso14443_3a_listener_data = { + .uid_len = 7, + .uid = {0x04, 0x51, 0x5C, 0xFA, 0x6F, 0x73, 0x81}, + .atqa = {0x44, 0x00}, + .sak = 0x00, + }; + NfcListener* iso3_listener = + nfc_listener_alloc(listener, NfcProtocolIso14443_3a, &iso14443_3a_listener_data); + nfc_listener_start(iso3_listener, NULL, NULL); + + Iso14443_3aData iso14443_3a_poller_data = {}; + mu_assert( + iso14443_3a_poller_sync_read(poller, &iso14443_3a_poller_data) == Iso14443_3aErrorNone, + "iso14443_3a_poller_sync_read() failed"); + + nfc_listener_stop(iso3_listener); + mu_assert( + iso14443_3a_is_equal(&iso14443_3a_poller_data, &iso14443_3a_listener_data), + "Data not matches"); + + nfc_listener_free(iso3_listener); + nfc_free(listener); + nfc_free(poller); +} + +static void mf_ultralight_reader_test(const char* path) { + FURI_LOG_I(TAG, "Testing file: %s", path); + Nfc* poller = nfc_alloc(); + Nfc* listener = nfc_alloc(); + + NfcDevice* nfc_device = nfc_device_alloc(); + mu_assert(nfc_device_load(nfc_device, path), "nfc_device_load() failed\r\n"); + + MfUltralightData* data = + (MfUltralightData*)nfc_device_get_data(nfc_device, NfcProtocolMfUltralight); + + uint32_t features = mf_ultralight_get_feature_support_set(data->type); + bool pwd_supported = + mf_ultralight_support_feature(features, MfUltralightFeatureSupportPasswordAuth); + uint8_t pwd_num = mf_ultralight_get_pwd_page_num(data->type); + const uint8_t zero_pwd[4] = {0, 0, 0, 0}; + + if(pwd_supported && !memcmp(data->page[pwd_num].data, zero_pwd, sizeof(zero_pwd))) { + data->pages_read -= 2; + } + + NfcListener* mfu_listener = nfc_listener_alloc(listener, NfcProtocolMfUltralight, data); + + nfc_listener_start(mfu_listener, NULL, NULL); + + MfUltralightData* mfu_data = mf_ultralight_alloc(); + MfUltralightError error = mf_ultralight_poller_sync_read_card(poller, mfu_data); + mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed"); + + nfc_listener_stop(mfu_listener); + nfc_listener_free(mfu_listener); + + mu_assert( + mf_ultralight_is_equal(mfu_data, nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)), + "Data not matches"); + + mf_ultralight_free(mfu_data); + nfc_device_free(nfc_device); + nfc_free(listener); + nfc_free(poller); +} + +MU_TEST(mf_ultralight_11_reader) { + mf_ultralight_reader_test(EXT_PATH("unit_tests/nfc/Ultralight_11.nfc")); +} + +MU_TEST(mf_ultralight_21_reader) { + mf_ultralight_reader_test(EXT_PATH("unit_tests/nfc/Ultralight_21.nfc")); +} + +MU_TEST(ntag_215_reader) { + mf_ultralight_reader_test(EXT_PATH("unit_tests/nfc/Ntag215.nfc")); +} + +MU_TEST(ntag_216_reader) { + mf_ultralight_reader_test(EXT_PATH("unit_tests/nfc/Ntag216.nfc")); +} + +MU_TEST(ntag_213_locked_reader) { + FURI_LOG_I(TAG, "Testing Ntag215 locked file"); + Nfc* poller = nfc_alloc(); + Nfc* listener = nfc_alloc(); + + NfcDeviceData* nfc_device = nfc_device_alloc(); + mu_assert( + nfc_device_load(nfc_device, EXT_PATH("unit_tests/nfc/Ntag213_locked.nfc")), + "nfc_device_load() failed\r\n"); + + NfcListener* mfu_listener = nfc_listener_alloc( + listener, + NfcProtocolMfUltralight, + nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)); + nfc_listener_start(mfu_listener, NULL, NULL); + + MfUltralightData* mfu_data = mf_ultralight_alloc(); + MfUltralightError error = mf_ultralight_poller_sync_read_card(poller, mfu_data); + mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed"); + + nfc_listener_stop(mfu_listener); + nfc_listener_free(mfu_listener); + + MfUltralightConfigPages* config = NULL; + const MfUltralightData* mfu_ref_data = + nfc_device_get_data(nfc_device, NfcProtocolMfUltralight); + mu_assert( + mf_ultralight_get_config_page(mfu_ref_data, &config), + "mf_ultralight_get_config_page() failed"); + uint16_t pages_locked = config->auth0; + + mu_assert(mfu_data->pages_read == pages_locked, "Unexpected pages read"); + + mf_ultralight_free(mfu_data); + nfc_device_free(nfc_device); + nfc_free(listener); + nfc_free(poller); +} + +static void mf_ultralight_write() { + Nfc* poller = nfc_alloc(); + Nfc* listener = nfc_alloc(); + + NfcDevice* nfc_device = nfc_device_alloc(); + nfc_data_generator_fill_data(NfcDataGeneratorTypeMfUltralightEV1_21, nfc_device); + + NfcListener* mfu_listener = nfc_listener_alloc( + listener, + NfcProtocolMfUltralight, + nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)); + nfc_listener_start(mfu_listener, NULL, NULL); + + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + // Initial read + MfUltralightError error = mf_ultralight_poller_sync_read_card(poller, mfu_data); + mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed"); + + mu_assert( + mf_ultralight_is_equal(mfu_data, nfc_device_get_data(nfc_device, NfcProtocolMfUltralight)), + "Data not matches"); + + // Write random data + for(size_t i = 5; i < 15; i++) { + MfUltralightPage page = {}; + FURI_LOG_D(TAG, "Writing page %d", i); + furi_hal_random_fill_buf(page.data, sizeof(MfUltralightPage)); + mfu_data->page[i] = page; + error = mf_ultralight_poller_sync_write_page(poller, i, &page); + mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_write_page() failed"); + } + + // Verification read + error = mf_ultralight_poller_sync_read_card(poller, mfu_data); + mu_assert(error == MfUltralightErrorNone, "mf_ultralight_poller_sync_read_card() failed"); + + nfc_listener_stop(mfu_listener); + const MfUltralightData* mfu_listener_data = + nfc_listener_get_data(mfu_listener, NfcProtocolMfUltralight); + + mu_assert(mf_ultralight_is_equal(mfu_data, mfu_listener_data), "Data not matches"); + + nfc_listener_free(mfu_listener); + mf_ultralight_free(mfu_data); + nfc_device_free(nfc_device); + nfc_free(listener); + nfc_free(poller); +} + +static void mf_classic_reader() { + Nfc* poller = nfc_alloc(); + Nfc* listener = nfc_alloc(); + + NfcDevice* nfc_device = nfc_device_alloc(); + nfc_data_generator_fill_data(NfcDataGeneratorTypeMfClassic4k_7b, nfc_device); + NfcListener* mfc_listener = nfc_listener_alloc( + listener, NfcProtocolMfClassic, nfc_device_get_data(nfc_device, NfcProtocolMfClassic)); + nfc_listener_start(mfc_listener, NULL, NULL); + + MfClassicBlock block = {}; + MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; + + mf_classic_poller_sync_read_block(poller, 0, &key, MfClassicKeyTypeA, &block); + + nfc_listener_stop(mfc_listener); + nfc_listener_free(mfc_listener); + + const MfClassicData* mfc_data = nfc_device_get_data(nfc_device, NfcProtocolMfClassic); + mu_assert(memcmp(&mfc_data->block[0], &block, sizeof(MfClassicBlock)) == 0, "Data mismatch"); + + nfc_device_free(nfc_device); + nfc_free(listener); + nfc_free(poller); +} + +static void mf_classic_write() { + Nfc* poller = nfc_alloc(); + Nfc* listener = nfc_alloc(); + + NfcDevice* nfc_device = nfc_device_alloc(); + nfc_data_generator_fill_data(NfcDataGeneratorTypeMfClassic4k_7b, nfc_device); + NfcListener* mfc_listener = nfc_listener_alloc( + listener, NfcProtocolMfClassic, nfc_device_get_data(nfc_device, NfcProtocolMfClassic)); + nfc_listener_start(mfc_listener, NULL, NULL); + + MfClassicBlock block_write = {}; + MfClassicBlock block_read = {}; + furi_hal_random_fill_buf(block_write.data, sizeof(MfClassicBlock)); + MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; + + mf_classic_poller_sync_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write); + mf_classic_poller_sync_read_block(poller, 1, &key, MfClassicKeyTypeA, &block_read); + + nfc_listener_stop(mfc_listener); + nfc_listener_free(mfc_listener); + + mu_assert(memcmp(&block_read, &block_write, sizeof(MfClassicBlock)) == 0, "Data mismatch"); + + nfc_device_free(nfc_device); + nfc_free(listener); + nfc_free(poller); +} + +static void mf_classic_value_block() { + Nfc* poller = nfc_alloc(); + Nfc* listener = nfc_alloc(); + + NfcDevice* nfc_device = nfc_device_alloc(); + nfc_data_generator_fill_data(NfcDataGeneratorTypeMfClassic4k_7b, nfc_device); + NfcListener* mfc_listener = nfc_listener_alloc( + listener, NfcProtocolMfClassic, nfc_device_get_data(nfc_device, NfcProtocolMfClassic)); + nfc_listener_start(mfc_listener, NULL, NULL); + + MfClassicKey key = {.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; + + int32_t value = 228; + MfClassicBlock block_write = {}; + mf_classic_value_to_block(value, 1, &block_write); + + MfClassicError error = MfClassicErrorNone; + error = mf_classic_poller_sync_write_block(poller, 1, &key, MfClassicKeyTypeA, &block_write); + mu_assert(error == MfClassicErrorNone, "Write failed"); + + int32_t data = 200; + int32_t new_value = 0; + error = + mf_classic_poller_sync_change_value(poller, 1, &key, MfClassicKeyTypeA, data, &new_value); + mu_assert(error == MfClassicErrorNone, "Value increment failed"); + mu_assert(new_value == value + data, "Value not match"); + + error = + mf_classic_poller_sync_change_value(poller, 1, &key, MfClassicKeyTypeA, -data, &new_value); + mu_assert(error == MfClassicErrorNone, "Value decrement failed"); + mu_assert(new_value == value, "Value not match"); + + nfc_listener_stop(mfc_listener); + nfc_listener_free(mfc_listener); + nfc_device_free(nfc_device); + nfc_free(listener); + nfc_free(poller); +} + +MU_TEST(mf_classic_dict_test) { + Storage* storage = furi_record_open(RECORD_STORAGE); + if(storage_common_stat(storage, NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, NULL) == FSE_OK) { + mu_assert( + storage_simply_remove(storage, NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH), + "Remove test dict failed"); + } + + NfcDict* dict = nfc_dict_alloc( + NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); + mu_assert(dict != NULL, "nfc_dict_alloc() failed"); + + size_t dict_keys_total = nfc_dict_get_total_keys(dict); + mu_assert(dict_keys_total == 0, "nfc_dict_keys_total() failed"); + + const uint32_t test_key_num = 30; + MfClassicKey* key_arr_ref = malloc(test_key_num * sizeof(MfClassicKey)); + for(size_t i = 0; i < test_key_num; i++) { + furi_hal_random_fill_buf(key_arr_ref[i].data, sizeof(MfClassicKey)); + mu_assert( + nfc_dict_add_key(dict, key_arr_ref[i].data, sizeof(MfClassicKey)), "add key failed"); + + size_t dict_keys_total = nfc_dict_get_total_keys(dict); + mu_assert(dict_keys_total == (i + 1), "nfc_dict_keys_total() failed"); + } + + nfc_dict_free(dict); + + dict = nfc_dict_alloc( + NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); + mu_assert(dict != NULL, "nfc_dict_alloc() failed"); + + dict_keys_total = nfc_dict_get_total_keys(dict); + mu_assert(dict_keys_total == test_key_num, "nfc_dict_keys_total() failed"); + + MfClassicKey key_dut = {}; + size_t key_idx = 0; + while(nfc_dict_get_next_key(dict, key_dut.data, sizeof(MfClassicKey))) { + mu_assert( + memcmp(key_arr_ref[key_idx].data, key_dut.data, sizeof(MfClassicKey)) == 0, + "Loaded key data mismatch"); + key_idx++; + } + + uint32_t delete_keys_idx[] = {1, 3, 9, 11, 19, 27}; + + for(size_t i = 0; i < COUNT_OF(delete_keys_idx); i++) { + MfClassicKey* key = &key_arr_ref[delete_keys_idx[i]]; + mu_assert( + nfc_dict_is_key_present(dict, key->data, sizeof(MfClassicKey)), + "nfc_dict_is_key_present() failed"); + mu_assert( + nfc_dict_delete_key(dict, key->data, sizeof(MfClassicKey)), + "nfc_dict_delete_key() failed"); + } + + dict_keys_total = nfc_dict_get_total_keys(dict); + mu_assert( + dict_keys_total == test_key_num - COUNT_OF(delete_keys_idx), + "nfc_dict_keys_total() failed"); + + nfc_dict_free(dict); + free(key_arr_ref); + + mu_assert( + storage_simply_remove(storage, NFC_APP_MF_CLASSIC_DICT_UNIT_TEST_PATH), + "Remove test dict failed"); +} + +MU_TEST_SUITE(nfc) { + nfc_test_alloc(); + + MU_RUN_TEST(iso14443_3a_reader); + MU_RUN_TEST(mf_ultralight_11_reader); + MU_RUN_TEST(mf_ultralight_21_reader); + MU_RUN_TEST(ntag_215_reader); + MU_RUN_TEST(ntag_216_reader); + MU_RUN_TEST(ntag_213_locked_reader); + + MU_RUN_TEST(mf_ultralight_write); + + MU_RUN_TEST(iso14443_3a_4b_file_test); + MU_RUN_TEST(iso14443_3a_7b_file_test); + + MU_RUN_TEST(mf_ultralight_file_test); + MU_RUN_TEST(mf_ultralight_ev1_11_file_test); + MU_RUN_TEST(mf_ultralight_ev1_h11_file_test); + MU_RUN_TEST(mf_ultralight_ev1_21_file_test); + MU_RUN_TEST(mf_ultralight_ev1_h21_file_test); + MU_RUN_TEST(mf_ultralight_ntag_203_file_test); + MU_RUN_TEST(mf_ultralight_ntag_213_file_test); + MU_RUN_TEST(mf_ultralight_ntag_215_file_test); + MU_RUN_TEST(mf_ultralight_ntag_216_file_test); + MU_RUN_TEST(mf_ultralight_ntag_i2c_1k_file_test); + MU_RUN_TEST(mf_ultralight_ntag_i2c_2k_file_test); + MU_RUN_TEST(mf_ultralight_ntag_i2c_plus_1k_file_test); + MU_RUN_TEST(mf_ultralight_ntag_i2c_plus_2k_file_test); + + MU_RUN_TEST(mf_classic_mini_file_test); + MU_RUN_TEST(mf_classic_1k_4b_file_test); + MU_RUN_TEST(mf_classic_1k_7b_file_test); + MU_RUN_TEST(mf_classic_4k_4b_file_test); + MU_RUN_TEST(mf_classic_4k_7b_file_test); + MU_RUN_TEST(mf_classic_reader); + + MU_RUN_TEST(mf_classic_write); + MU_RUN_TEST(mf_classic_value_block); + + MU_RUN_TEST(mf_classic_dict_test); + + nfc_test_free(); +} + +int run_minunit_test_nfc() { + MU_RUN_SUITE(nfc); + return MU_EXIT_CODE; +} diff --git a/applications/debug/unit_tests/nfc/nfc_transport.c b/applications/debug/unit_tests/nfc/nfc_transport.c new file mode 100644 index 00000000000..e9f4e21341a --- /dev/null +++ b/applications/debug/unit_tests/nfc/nfc_transport.c @@ -0,0 +1,473 @@ +#ifdef FW_CFG_unit_tests + +#include +#include +#include + +#include + +#define NFC_MAX_BUFFER_SIZE (256) + +typedef enum { + NfcTransportLogLevelWarning, + NfcTransportLogLevelInfo, +} NfcTransportLogLevel; + +FuriMessageQueue* poller_queue = NULL; +FuriMessageQueue* listener_queue = NULL; + +typedef enum { + NfcMessageTypeTx, + NfcMessageTypeTimeout, + NfcMessageTypeAbort, +} NfcMessageType; + +typedef struct { + uint16_t data_bits; + uint8_t data[NFC_MAX_BUFFER_SIZE]; +} NfcMessageData; + +typedef struct { + NfcMessageType type; + NfcMessageData data; +} NfcMessage; + +typedef enum { + NfcStateIdle, + NfcStateReady, + NfcStateReset, +} NfcState; + +typedef enum { + Iso14443_3aColResStatusIdle, + Iso14443_3aColResStatusInProgress, + Iso14443_3aColResStatusDone, +} Iso14443_3aColResStatus; + +typedef struct { + Iso14443_3aSensResp sens_resp; + Iso14443_3aSddResp sdd_resp[2]; + Iso14443_3aSelResp sel_resp[2]; +} Iso14443_3aColResData; + +struct Nfc { + NfcState state; + + Iso14443_3aColResStatus col_res_status; + Iso14443_3aColResData col_res_data; + + NfcEventCallback callback; + void* context; + + NfcMode mode; + + FuriThread* worker_thread; +}; + +static void nfc_test_print( + NfcTransportLogLevel log_level, + const char* message, + uint8_t* buffer, + uint16_t bits) { + FuriString* str = furi_string_alloc(); + size_t bytes = (bits + 7) / 8; + + for(size_t i = 0; i < bytes; i++) { + furi_string_cat_printf(str, " %02X", buffer[i]); + } + if(log_level == NfcTransportLogLevelWarning) { + FURI_LOG_W(message, "%s", furi_string_get_cstr(str)); + } else { + FURI_LOG_I(message, "%s", furi_string_get_cstr(str)); + } + + furi_string_free(str); +} + +static void nfc_prepare_col_res_data( + Nfc* instance, + uint8_t* uid, + uint8_t uid_len, + uint8_t* atqa, + uint8_t sak) { + memcpy(instance->col_res_data.sens_resp.sens_resp, atqa, 2); + + if(uid_len == 7) { + instance->col_res_data.sdd_resp[0].nfcid[0] = 0x88; + memcpy(&instance->col_res_data.sdd_resp[0].nfcid[1], uid, 3); + uint8_t bss = 0; + for(size_t i = 0; i < 4; i++) { + bss ^= instance->col_res_data.sdd_resp[0].nfcid[i]; + } + instance->col_res_data.sdd_resp[0].bss = bss; + instance->col_res_data.sel_resp[0].sak = 0x04; + + memcpy(instance->col_res_data.sdd_resp[1].nfcid, &uid[3], 4); + bss = 0; + for(size_t i = 0; i < 4; i++) { + bss ^= instance->col_res_data.sdd_resp[1].nfcid[i]; + } + instance->col_res_data.sdd_resp[1].bss = bss; + instance->col_res_data.sel_resp[1].sak = sak; + + } else { + furi_crash("Not supporting not 7 bytes"); + } +} + +Nfc* nfc_alloc() { + Nfc* instance = malloc(sizeof(Nfc)); + + return instance; +} + +void nfc_free(Nfc* instance) { + furi_check(instance); + + free(instance); +} + +void nfc_config(Nfc* instance, NfcMode mode, NfcTech tech) { + UNUSED(instance); + UNUSED(tech); + + instance->mode = mode; +} + +void nfc_set_fdt_poll_fc(Nfc* instance, uint32_t fdt_poll_fc) { + UNUSED(instance); + UNUSED(fdt_poll_fc); +} + +void nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc) { + UNUSED(instance); + UNUSED(fdt_listen_fc); +} + +void nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc) { + UNUSED(instance); + UNUSED(mask_rx_time_fc); +} + +void nfc_set_fdt_poll_poll_us(Nfc* instance, uint32_t fdt_poll_poll_us) { + UNUSED(instance); + UNUSED(fdt_poll_poll_us); +} + +void nfc_set_guard_time_us(Nfc* instance, uint32_t guard_time_us) { + UNUSED(instance); + UNUSED(guard_time_us); +} + +NfcError nfc_iso14443a_listener_set_col_res_data( + Nfc* instance, + uint8_t* uid, + uint8_t uid_len, + uint8_t* atqa, + uint8_t sak) { + furi_check(instance); + furi_check(uid); + furi_check(atqa); + + nfc_prepare_col_res_data(instance, uid, uid_len, atqa, sak); + + return NfcErrorNone; +} + +static int32_t nfc_worker_poller(void* context) { + Nfc* instance = context; + furi_check(instance->callback); + + instance->state = NfcStateReady; + NfcCommand command = NfcCommandContinue; + NfcEvent event = {}; + + while(true) { + event.type = NfcEventTypePollerReady; + command = instance->callback(event, instance->context); + if(command == NfcCommandStop) { + break; + } + } + + instance->state = NfcStateIdle; + + return 0; +} + +static void nfc_worker_listener_pass_col_res(Nfc* instance, uint8_t* rx_data, uint16_t rx_bits) { + furi_check(instance->col_res_status != Iso14443_3aColResStatusDone); + BitBuffer* tx_buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE); + + bool processed = false; + + if((rx_bits == 7) && (rx_data[0] == 0x52)) { + instance->col_res_status = Iso14443_3aColResStatusInProgress; + bit_buffer_copy_bytes( + tx_buffer, + instance->col_res_data.sens_resp.sens_resp, + sizeof(instance->col_res_data.sens_resp.sens_resp)); + nfc_listener_tx(instance, tx_buffer); + processed = true; + } else if(rx_bits == 2 * 8) { + if((rx_data[0] == 0x93) && (rx_data[1] == 0x20)) { + bit_buffer_copy_bytes( + tx_buffer, + (const uint8_t*)&instance->col_res_data.sdd_resp[0], + sizeof(Iso14443_3aSddResp)); + nfc_listener_tx(instance, tx_buffer); + processed = true; + } else if((rx_data[0] == 0x95) && (rx_data[1] == 0x20)) { + bit_buffer_copy_bytes( + tx_buffer, + (const uint8_t*)&instance->col_res_data.sdd_resp[1], + sizeof(Iso14443_3aSddResp)); + nfc_listener_tx(instance, tx_buffer); + processed = true; + } + } else if(rx_bits == 9 * 8) { + if((rx_data[0] == 0x93) && (rx_data[1] == 0x70)) { + bit_buffer_set_size_bytes(tx_buffer, 1); + bit_buffer_set_byte(tx_buffer, 0, instance->col_res_data.sel_resp[0].sak); + iso14443_crc_append(Iso14443CrcTypeA, tx_buffer); + nfc_listener_tx(instance, tx_buffer); + processed = true; + } else if((rx_data[0] == 0x95) && (rx_data[1] == 0x70)) { + bit_buffer_set_size_bytes(tx_buffer, 1); + bit_buffer_set_byte(tx_buffer, 0, instance->col_res_data.sel_resp[1].sak); + iso14443_crc_append(Iso14443CrcTypeA, tx_buffer); + nfc_listener_tx(instance, tx_buffer); + instance->col_res_status = Iso14443_3aColResStatusDone; + NfcEvent event = {.type = NfcEventTypeListenerActivated}; + instance->callback(event, instance->context); + + processed = true; + } + } + + if(!processed) { + NfcMessage message = {.type = NfcMessageTypeTimeout}; + furi_message_queue_put(poller_queue, &message, FuriWaitForever); + } + + bit_buffer_free(tx_buffer); +} + +static int32_t nfc_worker_listener(void* context) { + Nfc* instance = context; + furi_check(instance->callback); + + NfcMessage message = {}; + + NfcEventData event_data = {}; + event_data.buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE); + NfcEvent nfc_event = {.data = event_data}; + + while(true) { + furi_message_queue_get(listener_queue, &message, FuriWaitForever); + bit_buffer_copy_bits(event_data.buffer, message.data.data, message.data.data_bits); + if((message.data.data[0] == 0x52) && (message.data.data_bits == 7)) { + instance->col_res_status = Iso14443_3aColResStatusIdle; + } + + if(message.type == NfcMessageTypeAbort) { + break; + } else if(message.type == NfcMessageTypeTx) { + nfc_test_print( + NfcTransportLogLevelInfo, "RDR", message.data.data, message.data.data_bits); + if(instance->col_res_status != Iso14443_3aColResStatusDone) { + nfc_worker_listener_pass_col_res( + instance, message.data.data, message.data.data_bits); + } else { + instance->state = NfcStateReady; + nfc_event.type = NfcEventTypeRxEnd; + instance->callback(nfc_event, instance->context); + } + } + } + + instance->state = NfcStateIdle; + instance->col_res_status = Iso14443_3aColResStatusIdle; + memset(&instance->col_res_data, 0, sizeof(instance->col_res_data)); + bit_buffer_free(nfc_event.data.buffer); + + return 0; +} + +void nfc_start(Nfc* instance, NfcEventCallback callback, void* context) { + furi_check(instance); + furi_check(instance->worker_thread == NULL); + + if(instance->mode == NfcModeListener) { + furi_check(listener_queue == NULL); + // Check that poller didn't start + furi_check(poller_queue == NULL); + } else { + furi_check(poller_queue == NULL); + // Check that poller is started after listener + furi_check(listener_queue); + } + + instance->callback = callback; + instance->context = context; + + if(instance->mode == NfcModeListener) { + listener_queue = furi_message_queue_alloc(4, sizeof(NfcMessage)); + } else { + poller_queue = furi_message_queue_alloc(4, sizeof(NfcMessage)); + } + + instance->worker_thread = furi_thread_alloc(); + furi_thread_set_context(instance->worker_thread, instance); + furi_thread_set_priority(instance->worker_thread, FuriThreadPriorityHigh); + furi_thread_set_stack_size(instance->worker_thread, 8 * 1024); + + if(instance->mode == NfcModeListener) { + furi_thread_set_name(instance->worker_thread, "NfcWorkerListener"); + furi_thread_set_callback(instance->worker_thread, nfc_worker_listener); + } else { + furi_thread_set_name(instance->worker_thread, "NfcWorkerPoller"); + furi_thread_set_callback(instance->worker_thread, nfc_worker_poller); + } + + furi_thread_start(instance->worker_thread); +} + +void nfc_stop(Nfc* instance) { + furi_check(instance); + furi_check(instance->worker_thread); + + if(instance->mode == NfcModeListener) { + NfcMessage message = {.type = NfcMessageTypeAbort}; + furi_message_queue_put(listener_queue, &message, FuriWaitForever); + furi_thread_join(instance->worker_thread); + + furi_message_queue_free(listener_queue); + listener_queue = NULL; + + furi_thread_free(instance->worker_thread); + instance->worker_thread = NULL; + } else { + furi_thread_join(instance->worker_thread); + + furi_message_queue_free(poller_queue); + poller_queue = NULL; + + furi_thread_free(instance->worker_thread); + instance->worker_thread = NULL; + } +} + +// Called from worker thread + +NfcError nfc_listener_tx(Nfc* instance, const BitBuffer* tx_buffer) { + furi_check(instance); + furi_check(poller_queue); + furi_check(listener_queue); + furi_check(tx_buffer); + + NfcMessage message = {}; + message.type = NfcMessageTypeTx; + message.data.data_bits = bit_buffer_get_size(tx_buffer); + bit_buffer_write_bytes(tx_buffer, message.data.data, bit_buffer_get_size_bytes(tx_buffer)); + + furi_message_queue_put(poller_queue, &message, FuriWaitForever); + + return NfcErrorNone; +} + +NfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer* tx_buffer) { + return nfc_listener_tx(instance, tx_buffer); +} + +NfcError + nfc_poller_trx(Nfc* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, uint32_t fwt) { + furi_check(instance); + furi_check(tx_buffer); + furi_check(rx_buffer); + furi_check(poller_queue); + furi_check(listener_queue); + UNUSED(fwt); + + NfcError error = NfcErrorNone; + + NfcMessage message = {}; + message.type = NfcMessageTypeTx; + message.data.data_bits = bit_buffer_get_size(tx_buffer); + bit_buffer_write_bytes(tx_buffer, message.data.data, bit_buffer_get_size_bytes(tx_buffer)); + // Tx + furi_check(furi_message_queue_put(listener_queue, &message, FuriWaitForever) == FuriStatusOk); + // Rx + FuriStatus status = furi_message_queue_get(poller_queue, &message, 50); + + if(status == FuriStatusErrorTimeout) { + error = NfcErrorTimeout; + } else if(message.type == NfcMessageTypeTx) { + bit_buffer_copy_bits(rx_buffer, message.data.data, message.data.data_bits); + nfc_test_print( + NfcTransportLogLevelWarning, "TAG", message.data.data, message.data.data_bits); + } else if(message.type == NfcMessageTypeTimeout) { + error = NfcErrorTimeout; + } + + return error; +} + +NfcError nfc_iso14443a_poller_trx_custom_parity( + Nfc* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + return nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt); +} + +// Technology specific API + +NfcError nfc_iso14443a_poller_trx_short_frame( + Nfc* instance, + NfcIso14443aShortFrame frame, + BitBuffer* rx_buffer, + uint32_t fwt) { + UNUSED(frame); + + BitBuffer* tx_buffer = bit_buffer_alloc(32); + bit_buffer_set_size(tx_buffer, 7); + bit_buffer_set_byte(tx_buffer, 0, 0x52); + + NfcError error = nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt); + + bit_buffer_free(tx_buffer); + + return error; +} + +NfcError nfc_iso14443a_poller_trx_sdd_frame( + Nfc* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + return nfc_poller_trx(instance, tx_buffer, rx_buffer, fwt); +} + +NfcError nfc_iso15693_listener_tx_sof(Nfc* instance) { + UNUSED(instance); + + return NfcErrorNone; +} + +NfcError nfc_felica_listener_set_sensf_res_data( + Nfc* instance, + const uint8_t* idm, + const uint8_t idm_len, + const uint8_t* pmm, + const uint8_t pmm_len) { + furi_assert(instance); + furi_assert(idm); + furi_assert(pmm); + furi_assert(idm_len == 8); + furi_assert(pmm_len == 8); + + return NfcErrorNone; +} + +#endif diff --git a/applications/debug/unit_tests/power/power_test.c b/applications/debug/unit_tests/power/power_test.c new file mode 100644 index 00000000000..a9b66b22111 --- /dev/null +++ b/applications/debug/unit_tests/power/power_test.c @@ -0,0 +1,69 @@ +#include +#include +#include "../minunit.h" + +static void power_test_deinit(void) { + // Try to reset to default charge voltage limit + furi_hal_power_set_battery_charge_voltage_limit(4.208f); +} + +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_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_limit_floating_imprecision) { + // 4.016f should act as 4.016 V, even with floating point imprecision + 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_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_charge_voltage_limit(3.900f); + mu_assert_double_eq(3.888, furi_hal_power_get_battery_charge_voltage_limit()); + + 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_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_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_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(); +} + +int run_minunit_test_power() { + MU_RUN_SUITE(test_power_suite); + return MU_EXIT_CODE; +} diff --git a/applications/debug/unit_tests/protocol_dict/protocol_dict_test.c b/applications/debug/unit_tests/protocol_dict/protocol_dict_test.c new file mode 100644 index 00000000000..73e77ec900c --- /dev/null +++ b/applications/debug/unit_tests/protocol_dict/protocol_dict_test.c @@ -0,0 +1,222 @@ +#include +#include "../minunit.h" +#include + +typedef enum { + TestDictProtocol0, + TestDictProtocol1, + + TestDictProtocolMax, +} TestDictProtocols; + +/*********************** PROTOCOL 0 START ***********************/ + +typedef struct { + uint32_t data; + size_t encoder_counter; +} Protocol0Data; + +static const uint32_t protocol_0_decoder_result = 0xDEADBEEF; + +static void* protocol_0_alloc() { + void* data = malloc(sizeof(Protocol0Data)); + return data; +} + +static void protocol_0_free(Protocol0Data* data) { + free(data); +} + +static uint8_t* protocol_0_get_data(Protocol0Data* data) { + return (uint8_t*)&data->data; +} + +static void protocol_0_decoder_start(Protocol0Data* data) { + data->data = 0; +} + +static bool protocol_0_decoder_feed(Protocol0Data* data, bool level, uint32_t duration) { + if(level && duration == 666) { + data->data = protocol_0_decoder_result; + return true; + } else { + return false; + } +} + +static bool protocol_0_encoder_start(Protocol0Data* data) { + data->encoder_counter = 0; + return true; +} + +static LevelDuration protocol_0_encoder_yield(Protocol0Data* data) { + data->encoder_counter++; + return level_duration_make(data->encoder_counter % 2, data->data); +} + +/*********************** PROTOCOL 1 START ***********************/ + +typedef struct { + uint64_t data; + size_t encoder_counter; +} Protocol1Data; + +static const uint64_t protocol_1_decoder_result = 0x1234567890ABCDEF; + +static void* protocol_1_alloc() { + void* data = malloc(sizeof(Protocol1Data)); + return data; +} + +static void protocol_1_free(Protocol1Data* data) { + free(data); +} + +static uint8_t* protocol_1_get_data(Protocol1Data* data) { + return (uint8_t*)&data->data; +} + +static void protocol_1_decoder_start(Protocol1Data* data) { + data->data = 0; +} + +static bool protocol_1_decoder_feed(Protocol1Data* data, bool level, uint32_t duration) { + if(level && duration == 543) { + data->data = 0x1234567890ABCDEF; + return true; + } else { + return false; + } +} + +static bool protocol_1_encoder_start(Protocol1Data* data) { + data->encoder_counter = 0; + return true; +} + +static LevelDuration protocol_1_encoder_yield(Protocol1Data* data) { + data->encoder_counter++; + return level_duration_make(!(data->encoder_counter % 2), 100); +} + +/*********************** PROTOCOLS DESCRIPTION ***********************/ +static const ProtocolBase protocol_0 = { + .name = "Protocol 0", + .manufacturer = "Manufacturer 0", + .data_size = 4, + .alloc = (ProtocolAlloc)protocol_0_alloc, + .free = (ProtocolFree)protocol_0_free, + .get_data = (ProtocolGetData)protocol_0_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_0_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_0_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_0_encoder_start, + .yield = (ProtocolEncoderYield)protocol_0_encoder_yield, + }, +}; + +static const ProtocolBase protocol_1 = { + .name = "Protocol 1", + .manufacturer = "Manufacturer 1", + .data_size = 8, + .alloc = (ProtocolAlloc)protocol_1_alloc, + .free = (ProtocolFree)protocol_1_free, + .get_data = (ProtocolGetData)protocol_1_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_1_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_1_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_1_encoder_start, + .yield = (ProtocolEncoderYield)protocol_1_encoder_yield, + }, +}; + +static const ProtocolBase* test_protocols_base[] = { + [TestDictProtocol0] = &protocol_0, + [TestDictProtocol1] = &protocol_1, +}; + +MU_TEST(test_protocol_dict) { + ProtocolDict* dict = protocol_dict_alloc(test_protocols_base, TestDictProtocolMax); + size_t max_data_size = protocol_dict_get_max_data_size(dict); + mu_assert_int_eq(8, max_data_size); + uint8_t* data = malloc(max_data_size); + + protocol_dict_decoders_start(dict); + ProtocolId protocol_id = PROTOCOL_NO; + + for(size_t i = 0; i < 100; i++) { + protocol_id = protocol_dict_decoders_feed(dict, i % 2, 100); + mu_assert_int_eq(PROTOCOL_NO, protocol_id); + } + + // trigger protocol 1 + protocol_id = protocol_dict_decoders_feed(dict, true, 543); + mu_assert_int_eq(TestDictProtocol1, protocol_id); + + mu_assert_string_eq("Protocol 1", protocol_dict_get_name(dict, protocol_id)); + mu_assert_string_eq("Manufacturer 1", protocol_dict_get_manufacturer(dict, protocol_id)); + + size_t data_size = protocol_dict_get_data_size(dict, protocol_id); + mu_assert_int_eq(8, data_size); + + protocol_dict_get_data(dict, protocol_id, data, data_size); + mu_assert_mem_eq(&protocol_1_decoder_result, data, data_size); + + // trigger protocol 0 + protocol_id = protocol_dict_decoders_feed(dict, true, 666); + mu_assert_int_eq(TestDictProtocol0, protocol_id); + + mu_assert_string_eq("Protocol 0", protocol_dict_get_name(dict, protocol_id)); + mu_assert_string_eq("Manufacturer 0", protocol_dict_get_manufacturer(dict, protocol_id)); + + data_size = protocol_dict_get_data_size(dict, protocol_id); + mu_assert_int_eq(4, data_size); + + protocol_dict_get_data(dict, protocol_id, data, data_size); + mu_assert_mem_eq(&protocol_0_decoder_result, data, data_size); + + protocol_dict_decoders_start(dict); + + protocol_id = TestDictProtocol0; + + const uint8_t protocol_0_test_data[4] = {100, 0, 0, 0}; + protocol_dict_set_data(dict, protocol_id, protocol_0_test_data, 4); + + mu_check(protocol_dict_encoder_start(dict, protocol_id)); + + LevelDuration level; + level = protocol_dict_encoder_yield(dict, protocol_id); + mu_assert_int_eq(true, level_duration_get_level(level)); + mu_assert_int_eq(100, level_duration_get_duration(level)); + level = protocol_dict_encoder_yield(dict, protocol_id); + mu_assert_int_eq(false, level_duration_get_level(level)); + mu_assert_int_eq(100, level_duration_get_duration(level)); + level = protocol_dict_encoder_yield(dict, protocol_id); + mu_assert_int_eq(true, level_duration_get_level(level)); + mu_assert_int_eq(100, level_duration_get_duration(level)); + + mu_check(protocol_dict_encoder_start(dict, protocol_id)); + level = protocol_dict_encoder_yield(dict, protocol_id); + mu_assert_int_eq(true, level_duration_get_level(level)); + mu_assert_int_eq(100, level_duration_get_duration(level)); + + protocol_dict_free(dict); + free(data); +} + +MU_TEST_SUITE(test_protocol_dict_suite) { + MU_RUN_TEST(test_protocol_dict); +} + +int run_minunit_test_protocol_dict() { + MU_RUN_SUITE(test_protocol_dict_suite); + return MU_EXIT_CODE; +} \ No newline at end of file diff --git a/applications/debug/unit_tests/resources/unit_tests/Manifest_test b/applications/debug/unit_tests/resources/unit_tests/Manifest_test new file mode 100644 index 00000000000..db2979ee6f9 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/Manifest_test @@ -0,0 +1,76 @@ +V:0 +T:1672935435 +D:infrared +D:nfc +D:subghz +F:4bff70f2a2ae771f81de5cfb090b3d74:3952:infrared/test_kaseikyo.irtest +F:8556d32d7c54e66771d9da78d007d379:21463:infrared/test_nec.irtest +F:860c0c475573878842180a6cb50c85c7:2012:infrared/test_nec42.irtest +F:2b3cbf3fe7d3642190dfb8362dcc0ed6:3522:infrared/test_nec42ext.irtest +F:c74bbd7f885ab8fbc3b3363598041bc1:18976:infrared/test_necext.irtest +F:cab5e604abcb233bcb27903baec24462:7460:infrared/test_rc5.irtest +F:3d22b3ec2531bb8f4842c9c0c6a8d97c:547:infrared/test_rc5x.irtest +F:c9cb9fa4decbdd077741acb845f21343:8608:infrared/test_rc6.irtest +F:97de943385bc6ad1c4a58fc4fedb5244:16975:infrared/test_samsung32.irtest +F:4eb36c62d4f2e737a3e4a64b5ff0a8e7:41623:infrared/test_sirc.irtest +F:e4ec3299cbe1f528fb1b9b45aac53556:4182:nfc/nfc_nfca_signal_long.nfc +F:af4d10974834c2703ad29e859eea78c2:1020:nfc/nfc_nfca_signal_short.nfc +F:224d12457a26774d8d2aa0d4b3a15652:160:subghz/ansonic.sub +F:ce9fc98dc01230387a340332316774f1:13642:subghz/ansonic_raw.sub +F:f958927b656d0804036c28b4a31ff856:157:subghz/bett.sub +F:b4b17b2603fa3a144dbea4d9ede9f61d:5913:subghz/bett_raw.sub +F:370a0c62be967b420da5e60ffcdc078b:157:subghz/came.sub +F:0156915c656d8c038c6d555d34349a36:6877:subghz/came_atomo_raw.sub +F:111a8b796661f3cbd6f49f756cf91107:8614:subghz/came_raw.sub +F:2101b0a5a72c87f9dce77223b2885aa7:162:subghz/came_twee.sub +F:c608b78b8e4646eeb94db37644623254:10924:subghz/came_twee_raw.sub +F:c4a55acddb68fc3111d592c9292022a8:21703:subghz/cenmax_raw.sub +F:51d6bd600345954b9c84a5bc6e999313:159:subghz/clemsa.sub +F:14fa0d5931a32674bfb2ddf288f3842b:21499:subghz/clemsa_raw.sub +F:f38b6dfa0920199200887b2cd5c0a385:161:subghz/doitrand.sub +F:c7e53da8e3588a2c0721aa794699ccd4:24292:subghz/doitrand_raw.sub +F:cc73b6f4d05bfe30c67a0d18b63e58d9:159:subghz/doorhan.sub +F:22fec89c5cc43504ad4391e61e12c7e0:10457:subghz/doorhan_raw.sub +F:3a97d8bd32ddaff42932b4c3033ee2d2:12732:subghz/faac_slh_raw.sub +F:06d3226f5330665f48d41c49e34fed15:159:subghz/gate_tx.sub +F:8b150a8d38ac7c4f7063ee0d42050399:13827:subghz/gate_tx_raw.sub +F:a7904e17b0c18c083ae1acbefc330c7a:159:subghz/holtek.sub +F:72bb528255ef1c135cb3f436414897d3:173:subghz/holtek_ht12x.sub +F:54ceacb8c156f9534fc7ee0a0911f4da:11380:subghz/holtek_ht12x_raw.sub +F:4a9567c1543cf3e7bb5350b635d9076f:31238:subghz/holtek_raw.sub +F:ca86c0d78364d704ff62b0698093d396:162:subghz/honeywell_wdb.sub +F:f606548c935adc8d8bc804326ef67543:38415:subghz/honeywell_wdb_raw.sub +F:20bba4b0aec006ced7e82513f9459e31:15532:subghz/hormann_hsm_raw.sub +F:3392f2db6aa7777e937db619b86203bb:10637:subghz/ido_117_111_raw.sub +F:cc5c7968527cc233ef11a08986e31bf2:167:subghz/intertechno_v3.sub +F:70bceb941739260ab9f6162cfdeb0347:18211:subghz/intertechno_v3_raw.sub +F:bc9a4622f3e22fd7f82eb3f26e61f59b:44952:subghz/kia_seed_raw.sub +F:6b6e95fc70ea481dc6184d291466d16a:159:subghz/linear.sub +F:77aaa9005db54c0357451ced081857b2:14619:subghz/linear_raw.sub +F:1a618e21e6ffa9984d465012e704c450:161:subghz/magellan.sub +F:bf43cb85d79e20644323d6acad87e028:5808:subghz/magellan_raw.sub +F:4ef17320f936ee88e92582a9308b2faa:161:subghz/marantec.sub +F:507a8413a1603ad348eea945123fb7cc:21155:subghz/marantec_raw.sub +F:22b69dc490d5425488342b5c5a838d55:161:subghz/megacode.sub +F:4f8fe9bef8bdd9c52f3f77e829f8986f:6205:subghz/megacode_raw.sub +F:b39f62cb108c2fa9916e0a466596ab87:18655:subghz/nero_radio_raw.sub +F:d0d70f8183032096805a41e1808c093b:26436:subghz/nero_sketch_raw.sub +F:c6999bd0eefd0fccf34820e17bcbc8ba:161:subghz/nice_flo.sub +F:9b1200600b9ec2a73166797ff243fbfc:3375:subghz/nice_flo_raw.sub +F:b52bafb098282676d1c7163bfb0d6e73:8773:subghz/nice_flor_s_raw.sub +F:e4df94dfdee2efadf2ed9a1e9664f8b2:163:subghz/phoenix_v2.sub +F:8ec066976df93fba6335b3f6dc47014c:8548:subghz/phoenix_v2_raw.sub +F:2b1192e4898aaf274caebbb493b9f96e:164:subghz/power_smart.sub +F:8b8195cab1d9022fe38e802383fb923a:3648:subghz/power_smart_raw.sub +F:1ccf1289533e0486a1d010d934ad7b06:170:subghz/princeton.sub +F:8bccc506a61705ec429aecb879e5d7ce:7344:subghz/princeton_raw.sub +F:0bda91d783e464165190c3b3d16666a7:38724:subghz/scher_khan_magic_code.sub +F:116d7e1a532a0c9e00ffeee105f7138b:166:subghz/security_pls_1_0.sub +F:441fc7fc6fa11ce0068fde3f6145177b:69413:subghz/security_pls_1_0_raw.sub +F:e5e33c24c5e55f592ca892b5aa8fa31f:208:subghz/security_pls_2_0.sub +F:2614f0aef367042f8623719d765bf2c0:62287:subghz/security_pls_2_0_raw.sub +F:8eb533544c4c02986800c90e935184ff:168:subghz/smc5326.sub +F:fc67a4fe7e0b3bc81a1c8da8caca7658:4750:subghz/smc5326_raw.sub +F:24196a4c4af1eb03404a2ee434c864bf:4096:subghz/somfy_keytis_raw.sub +F:6a5ece145a5694e543d99bf1b970baf0:9741:subghz/somfy_telis_raw.sub +F:0ad046bfa9ec872e92141a69bbf03d92:382605:subghz/test_random_raw.sub diff --git a/applications/debug/unit_tests/resources/unit_tests/infrared/test_kaseikyo.irtest b/applications/debug/unit_tests/resources/unit_tests/infrared/test_kaseikyo.irtest new file mode 100644 index 00000000000..d0142fecdc3 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/infrared/test_kaseikyo.irtest @@ -0,0 +1,105 @@ +Filetype: IR tests file +Version: 1 +# +name: decoder_input1 +type: raw +data: 1000000 3363 1685 407 436 411 432 415 1240 434 410 437 1245 439 404 433 1249 435 408 439 431 406 1249 435 435 412 405 442 1241 433 1249 435 408 439 405 442 428 409 434 413 430 407 411 436 433 414 429 408 1248 436 407 440 1243 441 428 409 434 413 431 406 1249 435 1248 436 406 441 1242 442 1240 434 409 438 431 416 428 409 408 439 430 407 411 436 407 440 429 408 436 411 432 415 402 435 1247 437 1245 439 1243 441 1238 436 +# +name: decoder_expected1 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 1B 00 00 00 +repeat: false +# +name: decoder_input2 +type: raw +data: 1000000 3365 1683 409 434 413 431 406 1276 408 435 412 1270 414 429 408 1248 436 434 413 430 407 1275 409 434 413 431 406 1276 408 1248 436 433 414 430 407 437 410 433 414 429 408 436 411 432 415 428 409 1246 438 432 415 1267 407 437 410 433 414 429 408 436 411 432 415 1266 408 1250 434 1248 436 432 415 429 408 435 412 432 415 428 409 434 413 430 407 437 410 433 414 429 408 436 411 432 415 428 409 435 412 1240 434 +# +name: decoder_expected2 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 1C 00 00 00 +repeat: false +# +name: decoder_input3 +type: raw +data: 1000000 3361 1661 442 427 410 434 413 1243 441 428 409 1247 437 432 415 1241 433 410 437 407 440 1242 432 437 410 407 440 1242 442 1241 433 436 411 407 440 430 407 436 411 406 441 402 435 435 412 431 416 1240 434 410 437 1245 439 404 433 411 436 407 440 403 434 436 411 432 415 429 408 1249 435 1247 437 1245 439 430 407 1250 434 434 413 404 433 438 409 434 413 1243 441 1241 433 410 437 1245 439 430 407 1250 434 432 415 +# +name: decoder_expected3 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 70 01 00 00 +repeat: false +# +name: decoder_input4 +type: raw +data: 1000000 3365 1656 436 406 441 402 435 1248 436 406 441 1242 432 410 437 1246 438 404 433 410 437 1246 438 404 433 437 410 1245 491 1190 442 401 436 435 412 431 416 427 410 433 414 429 408 435 412 431 416 1240 434 435 412 1244 440 1241 433 436 411 433 414 402 435 409 438 405 442 402 435 1247 437 1244 440 1241 433 437 410 1245 439 430 407 410 437 406 441 402 435 409 438 1243 441 402 435 1247 437 406 441 1240 434 433 414 +# +name: decoder_expected4 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 70 01 00 00 +repeat: false +# +name: decoder_input5 +type: raw +data: 1000000 3357 1665 438 431 416 428 409 1247 437 432 415 1241 433 436 411 1245 439 430 407 436 411 1245 439 430 407 437 410 1246 438 1243 441 428 409 436 411 432 415 428 409 435 412 431 416 427 410 434 413 1243 441 427 410 1247 437 1245 439 430 407 437 410 1246 438 1244 440 429 408 1250 434 1248 488 355 440 429 408 436 411 432 415 428 408 435 412 431 416 428 409 1247 437 432 415 428 409 1248 436 1246 490 1191 441 1240 434 +# +name: decoder_expected5 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 1B 00 00 00 +repeat: false +# +name: decoder_input6 +type: raw +data: 1000000 3358 1664 439 430 407 437 410 1245 439 430 407 1250 434 434 413 1243 441 428 409 435 412 1244 440 428 409 435 412 1244 440 1242 432 437 410 434 413 430 407 436 411 432 415 428 409 435 412 431 416 1240 434 435 412 1244 440 1242 442 427 410 434 413 1243 441 427 409 1247 437 433 414 429 408 436 411 432 415 428 409 435 412 431 416 427 410 434 413 1243 441 1240 486 357 438 432 415 1240 434 436 411 432 415 425 412 +# +name: decoder_expected6 +type: parsed_array +count: 1 +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 05 00 00 00 +repeat: false +# +name: encoder_decoder_input1 +type: parsed_array +count: 4 +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 1B 00 00 00 +repeat: false +# +protocol: Kaseikyo +address: 41 54 32 00 +command: 70 01 00 00 +repeat: false +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 05 00 00 00 +repeat: false +# +protocol: Kaseikyo +address: 43 54 32 00 +command: 1B 00 00 00 +repeat: false +# diff --git a/assets/unit_tests/infrared/test_nec.irtest b/applications/debug/unit_tests/resources/unit_tests/infrared/test_nec.irtest similarity index 100% rename from assets/unit_tests/infrared/test_nec.irtest rename to applications/debug/unit_tests/resources/unit_tests/infrared/test_nec.irtest diff --git a/assets/unit_tests/infrared/test_nec42.irtest b/applications/debug/unit_tests/resources/unit_tests/infrared/test_nec42.irtest similarity index 100% rename from assets/unit_tests/infrared/test_nec42.irtest rename to applications/debug/unit_tests/resources/unit_tests/infrared/test_nec42.irtest diff --git a/assets/unit_tests/infrared/test_nec42ext.irtest b/applications/debug/unit_tests/resources/unit_tests/infrared/test_nec42ext.irtest similarity index 100% rename from assets/unit_tests/infrared/test_nec42ext.irtest rename to applications/debug/unit_tests/resources/unit_tests/infrared/test_nec42ext.irtest diff --git a/assets/unit_tests/infrared/test_necext.irtest b/applications/debug/unit_tests/resources/unit_tests/infrared/test_necext.irtest similarity index 100% rename from assets/unit_tests/infrared/test_necext.irtest rename to applications/debug/unit_tests/resources/unit_tests/infrared/test_necext.irtest diff --git a/assets/unit_tests/infrared/test_rc5.irtest b/applications/debug/unit_tests/resources/unit_tests/infrared/test_rc5.irtest similarity index 100% rename from assets/unit_tests/infrared/test_rc5.irtest rename to applications/debug/unit_tests/resources/unit_tests/infrared/test_rc5.irtest diff --git a/assets/unit_tests/infrared/test_rc5x.irtest b/applications/debug/unit_tests/resources/unit_tests/infrared/test_rc5x.irtest similarity index 100% rename from assets/unit_tests/infrared/test_rc5x.irtest rename to applications/debug/unit_tests/resources/unit_tests/infrared/test_rc5x.irtest diff --git a/assets/unit_tests/infrared/test_rc6.irtest b/applications/debug/unit_tests/resources/unit_tests/infrared/test_rc6.irtest similarity index 100% rename from assets/unit_tests/infrared/test_rc6.irtest rename to applications/debug/unit_tests/resources/unit_tests/infrared/test_rc6.irtest diff --git a/applications/debug/unit_tests/resources/unit_tests/infrared/test_rca.irtest b/applications/debug/unit_tests/resources/unit_tests/infrared/test_rca.irtest new file mode 100644 index 00000000000..bfe34e8e41e --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/infrared/test_rca.irtest @@ -0,0 +1,105 @@ +Filetype: IR tests file +Version: 1 +# +name: decoder_input1 +type: raw +data: 1000000 3994 3969 552 1945 551 1945 552 1945 551 1945 552 946 551 947 550 1947 548 951 546 1953 542 979 518 1979 517 981 492 1006 491 1006 492 1006 492 1006 492 2005 492 2005 492 1006 492 2005 492 1006 492 2005 492 1006 492 2006 491 +# +name: decoder_expected1 +type: parsed_array +count: 1 +# +protocol: RCA +address: 0F 00 00 00 +command: 54 00 00 00 +repeat: false +# +name: decoder_input2 +type: raw +data: 1000000 4055 3941 605 1891 551 1947 550 1946 551 1946 551 947 551 947 550 1947 549 949 548 1951 545 1977 519 1978 519 1979 518 980 518 980 518 980 518 980 518 1979 518 1979 518 981 517 1979 518 980 518 980 518 980 518 980 518 +# +name: decoder_expected2 +type: parsed_array +count: 1 +# +protocol: RCA +address: 0F 00 00 00 +command: F4 00 00 00 +repeat: false +# +name: decoder_input3 +type: raw +data: 1000000 4027 3970 551 1946 550 1946 551 1946 551 1946 551 946 551 947 550 1947 549 949 547 1951 545 1978 518 1979 492 1006 492 1007 491 1006 492 1006 492 1006 492 2006 491 2006 491 1006 492 2006 491 1007 491 1007 491 1006 492 2006 491 +# +name: decoder_expected3 +type: parsed_array +count: 1 +# +protocol: RCA +address: 0F 00 00 00 +command: 74 00 00 00 +repeat: false +# +name: decoder_input4 +type: raw +data: 1000000 4021 3941 551 1946 550 1946 551 1946 551 1945 552 946 551 947 550 1947 549 950 547 1952 544 1977 519 979 519 1979 518 980 518 980 518 980 518 980 518 1979 518 1979 518 980 518 1979 518 980 518 980 518 1979 518 980 518 +# +name: decoder_expected4 +type: parsed_array +count: 1 +# +protocol: RCA +address: 0F 00 00 00 +command: B4 00 00 00 +repeat: false +# +name: decoder_input5 +type: raw +data: 1000000 4022 3941 551 1946 551 1946 577 1919 578 1919 578 920 552 946 551 1946 550 947 550 1949 547 1952 544 978 520 979 519 980 518 980 518 980 518 980 518 1979 518 1979 518 980 518 1979 518 980 518 980 518 1979 518 1980 517 +# +name: decoder_expected5 +type: parsed_array +count: 1 +# +protocol: RCA +address: 0F 00 00 00 +command: 34 00 00 00 +repeat: false +# +name: decoder_input6 +type: raw +data: 1000000 3995 3968 552 1944 552 1946 550 1946 550 1946 551 947 550 948 549 1947 549 1949 547 1952 544 1978 518 1979 492 2005 492 1006 492 1006 492 1006 492 1006 492 2005 492 2005 492 1006 492 1006 492 1006 492 1006 492 1006 492 1006 492 +# +name: decoder_expected6 +type: parsed_array +count: 1 +# +protocol: RCA +address: 0F 00 00 00 +command: FC 00 00 00 +repeat: false +# +name: encoder_decoder_input1 +type: parsed_array +count: 4 +# +protocol: RCA +address: 0F 00 00 00 +command: 74 00 00 00 +repeat: false +# +protocol: RCA +address: 0F 00 00 00 +command: B4 00 00 00 +repeat: false +# +protocol: RCA +address: 0F 00 00 00 +command: 34 00 00 00 +repeat: false +# +protocol: RCA +address: 0F 00 00 00 +command: FC 00 00 00 +repeat: false +# diff --git a/assets/unit_tests/infrared/test_samsung32.irtest b/applications/debug/unit_tests/resources/unit_tests/infrared/test_samsung32.irtest similarity index 100% rename from assets/unit_tests/infrared/test_samsung32.irtest rename to applications/debug/unit_tests/resources/unit_tests/infrared/test_samsung32.irtest diff --git a/assets/unit_tests/infrared/test_sirc.irtest b/applications/debug/unit_tests/resources/unit_tests/infrared/test_sirc.irtest similarity index 100% rename from assets/unit_tests/infrared/test_sirc.irtest rename to applications/debug/unit_tests/resources/unit_tests/infrared/test_sirc.irtest diff --git a/applications/debug/unit_tests/resources/unit_tests/nfc/Ntag213_locked.nfc b/applications/debug/unit_tests/resources/unit_tests/nfc/Ntag213_locked.nfc new file mode 100644 index 00000000000..32f7771165f --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/nfc/Ntag213_locked.nfc @@ -0,0 +1,66 @@ +Filetype: Flipper NFC device +Version: 3 +# Nfc device type can be UID, Mifare Ultralight, Mifare Classic +Device type: NTAG213 +# UID, ATQA and SAK are common for all formats +UID: 04 AC 6B 72 BA 6C 80 +ATQA: 00 44 +SAK: 00 +# Mifare Ultralight specific data +Data format version: 1 +Signature: 2D AE BC AF 84 B8 85 87 C2 FB FE 76 13 58 86 72 8E 1D 3C B5 DA 24 23 44 E5 63 4D 4C 82 FB D7 18 +Mifare version: 00 04 04 02 01 00 0F 03 +Counter 0: 0 +Tearing 0: 00 +Counter 1: 0 +Tearing 1: 00 +Counter 2: 0 +Tearing 2: 00 +Pages total: 45 +Pages read: 45 +Page 0: 04 AC 6B 4B +Page 1: 72 BA 6C 80 +Page 2: 24 48 00 00 +Page 3: E1 10 12 00 +Page 4: 00 00 41 50 +Page 5: 00 00 31 31 +Page 6: 00 20 09 28 +Page 7: 00 03 31 59 +Page 8: 91 DF D3 00 +Page 9: 00 00 00 00 +Page 10: 00 00 00 00 +Page 11: 00 00 00 00 +Page 12: 00 00 00 00 +Page 13: 00 00 00 00 +Page 14: 00 00 00 00 +Page 15: 00 00 00 00 +Page 16: 00 00 00 00 +Page 17: 00 00 00 00 +Page 18: 00 00 00 00 +Page 19: 00 00 00 00 +Page 20: 00 00 00 00 +Page 21: 00 00 00 00 +Page 22: 00 00 00 00 +Page 23: 00 00 00 00 +Page 24: 00 00 00 00 +Page 25: 00 00 00 00 +Page 26: 00 00 00 00 +Page 27: 00 00 00 00 +Page 28: 00 00 00 00 +Page 29: 00 00 00 00 +Page 30: 00 00 00 00 +Page 31: 00 00 00 00 +Page 32: 00 00 00 00 +Page 33: 00 00 00 00 +Page 34: 00 00 00 00 +Page 35: 00 00 00 00 +Page 36: 00 00 00 00 +Page 37: 00 00 00 00 +Page 38: 00 00 00 00 +Page 39: 00 00 00 00 +Page 40: 00 00 00 BD +Page 41: 04 00 00 04 +Page 42: C0 05 00 00 +Page 43: 95 3F 52 FF +Page 44: 00 00 00 00 +Failed authentication attempts: 0 diff --git a/applications/debug/unit_tests/resources/unit_tests/nfc/Ntag215.nfc b/applications/debug/unit_tests/resources/unit_tests/nfc/Ntag215.nfc new file mode 100644 index 00000000000..420af08a66d --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/nfc/Ntag215.nfc @@ -0,0 +1,156 @@ +Filetype: Flipper NFC device +Version: 3 +# Nfc device type can be UID, Mifare Ultralight, Mifare Classic +Device type: NTAG215 +# UID, ATQA and SAK are common for all formats +UID: 04 51 5C FA 6F 73 81 +ATQA: 00 44 +SAK: 00 +# Mifare Ultralight specific data +Data format version: 1 +Signature: 42 21 E4 6C 79 6A 81 5E EA 0D 93 6D 85 EE 4B 0C 2A 00 D5 77 F1 C5 67 F3 63 75 F8 EB 86 48 5E 6B +Mifare version: 00 04 04 02 01 00 11 03 +Counter 0: 0 +Tearing 0: 00 +Counter 1: 0 +Tearing 1: 00 +Counter 2: 00 +Tearing 2: 00 +Pages total: 135 +Pages read: 135 +Page 0: 04 51 5C 81 +Page 1: FA 6F 73 81 +Page 2: 67 48 0F E0 +Page 3: F1 10 FF EE +Page 4: A5 00 00 00 +Page 5: 90 42 74 71 +Page 6: FD 8F 50 61 +Page 7: C5 65 1B 54 +Page 8: EF 68 D0 8E +Page 9: 3D 35 DB 83 +Page 10: D3 00 29 F6 +Page 11: 42 2A A5 5C +Page 12: F1 69 0A FC +Page 13: B6 44 E9 6B +Page 14: 77 41 88 81 +Page 15: 86 31 CB AD +Page 16: B1 DE F1 AB +Page 17: DF 96 C2 C5 +Page 18: C1 26 99 96 +Page 19: 85 AF 9F 0E +Page 20: 58 FE ED DC +Page 21: 0A 0A 00 01 +Page 22: 03 C1 05 02 +Page 23: 38 39 34 33 +Page 24: 49 2D 4E 5C +Page 25: 5B 21 0F 44 +Page 26: 3F 3F 76 69 +Page 27: B4 72 D8 38 +Page 28: A0 35 53 51 +Page 29: 53 EB A6 7C +Page 30: 3E 8B 97 C0 +Page 31: 00 7A 45 13 +Page 32: 3A 8B D4 0F +Page 33: 31 C2 32 CC +Page 34: B4 24 A6 1B +Page 35: D3 F5 4A 1F +Page 36: CD 8F 1D 64 +Page 37: 01 F4 DF C2 +Page 38: 11 16 C2 C5 +Page 39: 30 6D 49 AF +Page 40: 10 D4 7C 3C +Page 41: 6E 36 4E 08 +Page 42: 95 76 BC 84 +Page 43: 35 50 DD F0 +Page 44: 21 0F EE D9 +Page 45: 85 19 54 5F +Page 46: 3E A9 04 20 +Page 47: 1B 97 E4 39 +Page 48: FF 0A 45 F6 +Page 49: 13 D4 3E DD +Page 50: 97 42 FC 67 +Page 51: 6A AC 78 96 +Page 52: D1 DA 25 23 +Page 53: BF 4D B3 76 +Page 54: F1 21 ED 15 +Page 55: BD 55 11 C4 +Page 56: 4E 8C E9 23 +Page 57: C0 C4 6D 5A +Page 58: 58 25 FF 95 +Page 59: 3C 2B 7A 57 +Page 60: 66 BE A0 61 +Page 61: BC FC 4A 31 +Page 62: 4D AC EE 81 +Page 63: BE 1A 86 04 +Page 64: F6 D7 5E B3 +Page 65: E7 A8 A2 86 +Page 66: E9 40 AB 47 +Page 67: C8 36 E4 3E +Page 68: A7 4D D3 EA +Page 69: 83 9A 64 F7 +Page 70: 96 6B 5D BF +Page 71: 4E A2 A6 0F +Page 72: BD 3D BE 7C +Page 73: 22 0C 68 51 +Page 74: 0F 9A B8 AE +Page 75: 38 2C C4 CD +Page 76: 53 D8 DD 18 +Page 77: A6 5D 35 87 +Page 78: C9 6D 99 59 +Page 79: 61 9F B6 DC +Page 80: E6 22 0F 99 +Page 81: 39 82 79 60 +Page 82: 58 2E BE F7 +Page 83: EF F7 95 62 +Page 84: D5 06 1B 58 +Page 85: 65 05 A9 08 +Page 86: 75 ED 5D 90 +Page 87: 5A E1 7E C9 +Page 88: 35 D6 29 BB +Page 89: D0 67 6C F9 +Page 90: A0 FF 0B 93 +Page 91: 22 EA A3 3F +Page 92: E2 BD BD 58 +Page 93: BE 93 D9 94 +Page 94: 41 CC 7E 40 +Page 95: E6 8C 5A 43 +Page 96: 65 C1 24 94 +Page 97: B9 97 61 13 +Page 98: AD 74 FF 21 +Page 99: 0F EC F6 03 +Page 100: 89 5D 89 E5 +Page 101: 8D 11 F8 D7 +Page 102: 33 43 79 2E +Page 103: 23 E5 29 B5 +Page 104: 53 98 13 FF +Page 105: E8 79 8B 33 +Page 106: 45 6C 34 38 +Page 107: 3B 69 28 D7 +Page 108: D2 80 B0 2F +Page 109: D0 18 D5 DD +Page 110: 6C 2D D9 97 +Page 111: CA 78 B4 A2 +Page 112: B7 3E B8 79 +Page 113: A2 BE 54 E4 +Page 114: C8 28 0C 4A +Page 115: 81 E7 EC 1C +Page 116: 39 93 6F 70 +Page 117: 75 77 5C FC +Page 118: 66 58 0C 1C +Page 119: 9F 70 2E C8 +Page 120: 52 4A 52 BD +Page 121: 56 D5 6A 15 +Page 122: 54 1B 33 90 +Page 123: 44 11 C1 07 +Page 124: 11 5C BA 80 +Page 125: 10 14 20 9A +Page 126: 4A D8 E6 36 +Page 127: DA B8 59 E5 +Page 128: 5E 48 95 DA +Page 129: 96 6A 26 85 +Page 130: 01 00 0F BD +Page 131: 00 00 00 04 +Page 132: 5F 00 00 00 +Page 133: 00 00 00 00 +Page 134: 00 00 00 00 +Failed authentication attempts: 0 diff --git a/applications/debug/unit_tests/resources/unit_tests/nfc/Ntag216.nfc b/applications/debug/unit_tests/resources/unit_tests/nfc/Ntag216.nfc new file mode 100644 index 00000000000..debe43858fd --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/nfc/Ntag216.nfc @@ -0,0 +1,252 @@ +Filetype: Flipper NFC device +Version: 2 +# Nfc device type can be UID, Mifare Ultralight, Mifare Classic, Bank card +Device type: NTAG216 +# UID, ATQA and SAK are common for all formats +UID: 04 D9 65 0A 32 5E 80 +ATQA: 44 00 +SAK: 00 +# Mifare Ultralight specific data +Data format version: 1 +Signature: 48 2A F2 01 0F F2 F5 A7 9A D5 79 6E CB 14 54 48 98 D1 57 5D 8A 23 A9 B0 E8 20 02 3E CD C8 16 DB +Mifare version: 00 04 04 02 01 00 13 03 +Counter 0: 0 +Tearing 0: 00 +Counter 1: 0 +Tearing 1: 00 +Counter 2: 0 +Tearing 2: 00 +Pages total: 231 +Pages read: 231 +Page 0: 04 D9 65 30 +Page 1: 0A 32 5E 80 +Page 2: E6 48 00 00 +Page 3: E1 10 6D 00 +Page 4: 03 37 D1 01 +Page 5: 33 55 04 6D +Page 6: 2E 79 6F 75 +Page 7: 74 75 62 65 +Page 8: 2E 63 6F 6D +Page 9: 2F 77 61 74 +Page 10: 63 68 3F 76 +Page 11: 3D 62 78 71 +Page 12: 4C 73 72 6C +Page 13: 61 6B 4B 38 +Page 14: 26 66 65 61 +Page 15: 74 75 72 65 +Page 16: 3D 79 6F 75 +Page 17: 74 75 2E 62 +Page 18: 65 FE 00 00 +Page 19: 00 00 00 00 +Page 20: 00 00 00 00 +Page 21: 00 00 00 00 +Page 22: 00 00 00 00 +Page 23: 00 00 00 00 +Page 24: 00 00 00 00 +Page 25: 00 00 00 00 +Page 26: 00 00 00 00 +Page 27: 00 00 00 00 +Page 28: 00 00 00 00 +Page 29: 00 00 00 00 +Page 30: 00 00 00 00 +Page 31: 00 00 00 00 +Page 32: 00 00 00 00 +Page 33: 00 00 00 00 +Page 34: 00 00 00 00 +Page 35: 00 00 00 00 +Page 36: 00 00 00 00 +Page 37: 00 00 00 00 +Page 38: 00 00 00 00 +Page 39: 00 00 00 00 +Page 40: 00 00 00 00 +Page 41: 00 00 00 00 +Page 42: 00 00 00 00 +Page 43: 00 00 00 00 +Page 44: 00 00 00 00 +Page 45: 00 00 00 00 +Page 46: 00 00 00 00 +Page 47: 00 00 00 00 +Page 48: 00 00 00 00 +Page 49: 00 00 00 00 +Page 50: 00 00 00 00 +Page 51: 00 00 00 00 +Page 52: 00 00 00 00 +Page 53: 00 00 00 00 +Page 54: 00 00 00 00 +Page 55: 00 00 00 00 +Page 56: 00 00 00 00 +Page 57: 00 00 00 00 +Page 58: 00 00 00 00 +Page 59: 00 00 00 00 +Page 60: 00 00 00 00 +Page 61: 00 00 00 00 +Page 62: 00 00 00 00 +Page 63: 00 00 00 00 +Page 64: 00 00 00 00 +Page 65: 00 00 00 00 +Page 66: 00 00 00 00 +Page 67: 00 00 00 00 +Page 68: 00 00 00 00 +Page 69: 00 00 00 00 +Page 70: 00 00 00 00 +Page 71: 00 00 00 00 +Page 72: 00 00 00 00 +Page 73: 00 00 00 00 +Page 74: 00 00 00 00 +Page 75: 00 00 00 00 +Page 76: 00 00 00 00 +Page 77: 00 00 00 00 +Page 78: 00 00 00 00 +Page 79: 00 00 00 00 +Page 80: 00 00 00 00 +Page 81: 00 00 00 00 +Page 82: 00 00 00 00 +Page 83: 00 00 00 00 +Page 84: 00 00 00 00 +Page 85: 00 00 00 00 +Page 86: 00 00 00 00 +Page 87: 00 00 00 00 +Page 88: 00 00 00 00 +Page 89: 00 00 00 00 +Page 90: 00 00 00 00 +Page 91: 00 00 00 00 +Page 92: 00 00 00 00 +Page 93: 00 00 00 00 +Page 94: 00 00 00 00 +Page 95: 00 00 00 00 +Page 96: 00 00 00 00 +Page 97: 00 00 00 00 +Page 98: 00 00 00 00 +Page 99: 00 00 00 00 +Page 100: 00 00 00 00 +Page 101: 00 00 00 00 +Page 102: 00 00 00 00 +Page 103: 00 00 00 00 +Page 104: 00 00 00 00 +Page 105: 00 00 00 00 +Page 106: 00 00 00 00 +Page 107: 00 00 00 00 +Page 108: 00 00 00 00 +Page 109: 00 00 00 00 +Page 110: 00 00 00 00 +Page 111: 00 00 00 00 +Page 112: 00 00 00 00 +Page 113: 00 00 00 00 +Page 114: 00 00 00 00 +Page 115: 00 00 00 00 +Page 116: 00 00 00 00 +Page 117: 00 00 00 00 +Page 118: 00 00 00 00 +Page 119: 00 00 00 00 +Page 120: 00 00 00 00 +Page 121: 00 00 00 00 +Page 122: 00 00 00 00 +Page 123: 00 00 00 00 +Page 124: 00 00 00 00 +Page 125: 00 00 00 00 +Page 126: 00 00 00 00 +Page 127: 00 00 00 00 +Page 128: 00 00 00 00 +Page 129: 00 00 00 00 +Page 130: 00 00 00 00 +Page 131: 00 00 00 00 +Page 132: 00 00 00 00 +Page 133: 00 00 00 00 +Page 134: 00 00 00 00 +Page 135: 00 00 00 00 +Page 136: 00 00 00 00 +Page 137: 00 00 00 00 +Page 138: 00 00 00 00 +Page 139: 00 00 00 00 +Page 140: 00 00 00 00 +Page 141: 00 00 00 00 +Page 142: 00 00 00 00 +Page 143: 00 00 00 00 +Page 144: 00 00 00 00 +Page 145: 00 00 00 00 +Page 146: 00 00 00 00 +Page 147: 00 00 00 00 +Page 148: 00 00 00 00 +Page 149: 00 00 00 00 +Page 150: 00 00 00 00 +Page 151: 00 00 00 00 +Page 152: 00 00 00 00 +Page 153: 00 00 00 00 +Page 154: 00 00 00 00 +Page 155: 00 00 00 00 +Page 156: 00 00 00 00 +Page 157: 00 00 00 00 +Page 158: 00 00 00 00 +Page 159: 00 00 00 00 +Page 160: 00 00 00 00 +Page 161: 00 00 00 00 +Page 162: 00 00 00 00 +Page 163: 00 00 00 00 +Page 164: 00 00 00 00 +Page 165: 00 00 00 00 +Page 166: 00 00 00 00 +Page 167: 00 00 00 00 +Page 168: 00 00 00 00 +Page 169: 00 00 00 00 +Page 170: 00 00 00 00 +Page 171: 00 00 00 00 +Page 172: 00 00 00 00 +Page 173: 00 00 00 00 +Page 174: 00 00 00 00 +Page 175: 00 00 00 00 +Page 176: 00 00 00 00 +Page 177: 00 00 00 00 +Page 178: 00 00 00 00 +Page 179: 00 00 00 00 +Page 180: 00 00 00 00 +Page 181: 00 00 00 00 +Page 182: 00 00 00 00 +Page 183: 00 00 00 00 +Page 184: 00 00 00 00 +Page 185: 00 00 00 00 +Page 186: 00 00 00 00 +Page 187: 00 00 00 00 +Page 188: 00 00 00 00 +Page 189: 00 00 00 00 +Page 190: 00 00 00 00 +Page 191: 00 00 00 00 +Page 192: 00 00 00 00 +Page 193: 00 00 00 00 +Page 194: 00 00 00 00 +Page 195: 00 00 00 00 +Page 196: 00 00 00 00 +Page 197: 00 00 00 00 +Page 198: 00 00 00 00 +Page 199: 00 00 00 00 +Page 200: 00 00 00 00 +Page 201: 00 00 00 00 +Page 202: 00 00 00 00 +Page 203: 00 00 00 00 +Page 204: 00 00 00 00 +Page 205: 00 00 00 00 +Page 206: 00 00 00 00 +Page 207: 00 00 00 00 +Page 208: 00 00 00 00 +Page 209: 00 00 00 00 +Page 210: 00 00 00 00 +Page 211: 00 00 00 00 +Page 212: 00 00 00 00 +Page 213: 00 00 00 00 +Page 214: 00 00 00 00 +Page 215: 00 00 00 00 +Page 216: 00 00 00 00 +Page 217: 00 00 00 00 +Page 218: 00 00 00 00 +Page 219: 00 00 00 00 +Page 220: 00 00 00 00 +Page 221: 00 00 00 00 +Page 222: 00 00 00 00 +Page 223: 00 00 00 00 +Page 224: 00 00 00 00 +Page 225: 00 00 00 00 +Page 226: 00 00 00 BD +Page 227: 04 00 00 FF +Page 228: 00 05 00 00 +Page 229: 00 00 00 00 +Page 230: 00 00 00 00 +Failed authentication attempts: 0 diff --git a/applications/debug/unit_tests/resources/unit_tests/nfc/Ultralight_11.nfc b/applications/debug/unit_tests/resources/unit_tests/nfc/Ultralight_11.nfc new file mode 100644 index 00000000000..22441289dd0 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/nfc/Ultralight_11.nfc @@ -0,0 +1,41 @@ +Filetype: Flipper NFC device +Version: 3 +# Nfc device type can be UID, Mifare Ultralight, Mifare Classic +Device type: Mifare Ultralight 11 +# UID, ATQA and SAK are common for all formats +UID: 04 15 74 F2 B0 5E 81 +ATQA: 00 44 +SAK: 00 +# Mifare Ultralight specific data +Data format version: 1 +Signature: A4 37 7D E5 8C 2F 88 D8 04 60 41 6E 3A C8 CD DB 19 94 26 12 C5 D0 12 B0 EB 88 05 72 89 F2 A5 61 +Mifare version: 00 04 03 01 01 00 0B 03 +Counter 0: 0 +Tearing 0: BD +Counter 1: 0 +Tearing 1: BD +Counter 2: 0 +Tearing 2: BD +Pages total: 20 +Pages read: 20 +Page 0: 04 15 74 ED +Page 1: F2 B0 5E 81 +Page 2: 9D 48 F8 FF +Page 3: C1 31 3E 3F +Page 4: B0 00 F0 02 +Page 5: 2F B3 45 A0 +Page 6: D4 9C 02 F2 +Page 7: 4A B1 ED FF +Page 8: C8 01 00 02 +Page 9: 4F B3 46 70 +Page 10: EE F6 60 B0 +Page 11: B6 C6 12 1B +Page 12: B9 1E 49 C3 +Page 13: 49 DF 7A 57 +Page 14: 08 52 2A 11 +Page 15: 28 0A 28 59 +Page 16: 00 00 00 FF +Page 17: 00 05 00 00 +Page 18: FF FF FF FF +Page 19: 00 00 00 00 +Failed authentication attempts: 0 diff --git a/applications/debug/unit_tests/resources/unit_tests/nfc/Ultralight_21.nfc b/applications/debug/unit_tests/resources/unit_tests/nfc/Ultralight_21.nfc new file mode 100644 index 00000000000..dc01e93a602 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/nfc/Ultralight_21.nfc @@ -0,0 +1,62 @@ +Filetype: Flipper NFC device +Version: 3 +# Nfc device type can be UID, Mifare Ultralight, Mifare Classic +Device type: Mifare Ultralight 21 +# UID, ATQA and SAK are common for all formats +UID: 34 BF AB B1 AE 73 D6 +ATQA: 00 44 +SAK: 00 +# Mifare Ultralight specific data +Data format version: 1 +Signature: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Mifare version: 00 34 21 01 01 00 0E 03 +Counter 0: 0 +Tearing 0: 00 +Counter 1: 0 +Tearing 1: 00 +Counter 2: 0 +Tearing 2: 00 +Pages total: 41 +Pages read: 41 +Page 0: 34 BF AB A8 +Page 1: B1 AE 73 D6 +Page 2: BA 00 70 08 +Page 3: FF FF FF FC +Page 4: 45 D9 BB A0 +Page 5: 5D 9D FA 00 +Page 6: 80 70 38 40 +Page 7: 12 30 02 00 +Page 8: 00 00 00 00 +Page 9: 00 00 00 00 +Page 10: AC A1 0D E4 +Page 11: 80 70 38 40 +Page 12: 00 57 A0 01 +Page 13: 00 08 C1 40 +Page 14: 00 00 00 00 +Page 15: AC A1 0D E4 +Page 16: 00 00 00 00 +Page 17: 00 00 00 00 +Page 18: 00 00 00 00 +Page 19: 00 00 00 00 +Page 20: 00 00 00 00 +Page 21: 00 00 00 00 +Page 22: 00 00 00 00 +Page 23: 00 00 00 00 +Page 24: 00 00 00 00 +Page 25: 00 00 00 00 +Page 26: 00 00 00 00 +Page 27: 00 00 00 00 +Page 28: 00 00 00 00 +Page 29: 00 00 00 00 +Page 30: 00 00 00 00 +Page 31: 00 00 00 00 +Page 32: 00 00 00 00 +Page 33: 00 00 00 00 +Page 34: 00 00 00 00 +Page 35: 00 00 00 00 +Page 36: 00 00 00 BD +Page 37: 00 00 00 FF +Page 38: 00 05 00 00 +Page 39: FF FF FF FF +Page 40: 00 00 00 00 +Failed authentication attempts: 0 diff --git a/assets/unit_tests/nfc/nfc_nfca_signal_long.nfc b/applications/debug/unit_tests/resources/unit_tests/nfc/nfc_nfca_signal_long.nfc similarity index 99% rename from assets/unit_tests/nfc/nfc_nfca_signal_long.nfc rename to applications/debug/unit_tests/resources/unit_tests/nfc/nfc_nfca_signal_long.nfc index fae69cb5cc4..dd6a2ff8e67 100644 --- a/assets/unit_tests/nfc/nfc_nfca_signal_long.nfc +++ b/applications/debug/unit_tests/resources/unit_tests/nfc/nfc_nfca_signal_long.nfc @@ -3,4 +3,4 @@ Version: 1 Data length: 18 Plain data: f1 99 41 43 a1 2f 23 01 de f3 c5 8d 91 4b 1e 50 4a c9 Timings length: 1304 -Timings: 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 640 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 0 +Timings: 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 640 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 diff --git a/assets/unit_tests/nfc/nfc_nfca_signal_short.nfc b/applications/debug/unit_tests/resources/unit_tests/nfc/nfc_nfca_signal_short.nfc similarity index 97% rename from assets/unit_tests/nfc/nfc_nfca_signal_short.nfc rename to applications/debug/unit_tests/resources/unit_tests/nfc/nfc_nfca_signal_short.nfc index 3b7e2d9e973..f447fca26ef 100644 --- a/assets/unit_tests/nfc/nfc_nfca_signal_short.nfc +++ b/applications/debug/unit_tests/resources/unit_tests/nfc/nfc_nfca_signal_short.nfc @@ -3,4 +3,4 @@ Version: 1 Data length: 4 Plain data: 14 d8 a0 c9 Timings length: 296 -Timings: 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 36 37 37 37 37 36 37 0 +Timings: 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 36 37 37 37 37 36 37 37 diff --git a/applications/debug/unit_tests/resources/unit_tests/storage/md5.txt b/applications/debug/unit_tests/resources/unit_tests/storage/md5.txt new file mode 100644 index 00000000000..777e390be8c --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/storage/md5.txt @@ -0,0 +1 @@ +Yo dawg, I heard you like md5... \ No newline at end of file diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/alutech_at_4n_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/alutech_at_4n_raw.sub new file mode 100644 index 00000000000..ae5db9715be --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/alutech_at_4n_raw.sub @@ -0,0 +1,10 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +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 diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/ansonic.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/ansonic.sub new file mode 100644 index 00000000000..a7219b12ce9 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/ansonic.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Ansonic +Bit: 12 +Key: 00 00 00 00 00 00 05 5A diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/ansonic_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/ansonic_raw.sub new file mode 100644 index 00000000000..6d4c78ebe69 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/ansonic_raw.sub @@ -0,0 +1,11 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 57 -144 401 -86 173 -202 143 -258 133 -88 257 -144 287 -58 402 -56 259 -230 259 -86 85 -96 95 -174 286 -162 57 -230 253 -400 229 -88 536 -58 85 -72 167 -110 263 -72 229 -58 85 -86 87 -262 119 -288 163 -210 321 -320 186 -140 261 -96 143 -456 117 -216 143 -246 239 -102 121 -72 71 -191 167 -263 191 -96 239 -80 57 -116 143 -118 167 -168 215 -288 191 -106 287 -114 517 -88 113 -394 173 -70 215 -100 661 -86 201 -114 259 -58 287 -86 57 -202 399 -200 57 -288 229 -144 115 -1425 83 -142 173 -86 459 -112 223 -144 201 -116 143 -114 196 -17422 287 -614 1075 -1132 533 -560 1125 -1112 561 -530 1133 -1088 585 -512 1129 -1104 563 -1102 571 -1072 591 -524 1137 -538 1115 -19392 589 -522 1139 -1090 569 -520 1137 -1100 587 -520 1135 -1092 569 -514 1155 -1076 599 -1062 585 -1086 585 -532 1125 -528 1145 -19396 581 -504 1155 -1064 619 -506 1145 -1080 595 -508 1149 -1062 587 -536 1129 -1088 585 -1090 589 -1062 587 -510 1177 -506 1149 -19394 587 -524 1147 -1076 597 -512 1129 -1072 617 -498 1159 -1066 599 -514 1147 -1076 595 -1060 613 -1060 589 -506 1179 -510 1155 -19378 605 -514 1129 -1098 601 -488 1177 -1062 583 -510 1161 -1072 587 -516 1169 -1058 587 -1082 589 -1076 593 -532 1121 -536 1127 -19444 549 -542 1129 -1092 601 -510 1147 -1098 581 -506 1123 -1100 585 -510 1147 -1106 591 -1060 597 -1060 595 -510 1151 -534 1157 -19390 593 -512 1163 -1064 597 -510 1173 -1042 617 -510 1159 -1056 579 -556 1133 -1054 609 -1060 607 -1078 585 -528 1147 -516 1135 -19444 559 -534 1165 -1052 605 -510 1149 -1062 599 -520 1173 -1054 591 -540 1111 -1116 573 -1088 593 -1042 615 -534 1117 -536 1129 -19436 569 -530 1157 -1052 605 -504 1177 -1062 591 -536 1113 -1114 589 -514 1125 -1112 563 -1084 587 -1080 589 -536 1123 -532 1165 -19392 599 -524 1143 -1080 595 -540 1125 -1090 563 -534 1149 -1084 567 -534 1147 -1092 587 -1086 585 -1064 565 -558 1123 -532 1143 -19450 549 -556 1121 -1086 585 -534 1137 -1094 583 -516 1133 -1114 563 -536 1123 -1108 573 -1082 597 -1078 565 -532 1143 -524 1135 -19464 561 -528 1149 -1066 613 -508 1151 -1076 587 -532 1125 -1076 609 -506 1175 -1076 563 -1106 563 -1084 591 -534 1157 -484 1179 -19414 589 -528 1131 -1096 587 -520 1163 -1080 563 -550 1121 -1098 555 -562 1117 -1082 575 -1112 563 -1104 559 -558 1121 -530 1149 -19442 591 -498 1175 -1066 585 -534 1121 -1114 565 -540 1115 -1096 585 -538 1123 -1110 559 -1086 585 -1084 589 -530 1125 -558 1121 -19454 563 -542 1163 -1044 585 -558 1131 -1092 571 -536 1133 -1088 587 -518 1149 -1086 601 -1058 587 -1080 611 -510 +RAW_Data: 1125 -536 1145 -19444 567 -542 1151 -1086 581 -534 1133 -1084 583 -530 1141 -1090 587 -516 1121 -1114 591 -1062 595 -1092 567 -538 1131 -536 1145 -19448 589 -516 1143 -1076 591 -540 1115 -1090 591 -538 1115 -1096 597 -512 1155 -1078 581 -1082 585 -1104 559 -530 1153 -516 1147 -19438 589 -526 1157 -1056 609 -534 1137 -1078 585 -532 1149 -1076 585 -536 1123 -1104 587 -1062 597 -1082 587 -536 1121 -554 1121 -19464 555 -560 1123 -1104 563 -542 1131 -1096 565 -536 1143 -1060 621 -492 1175 -1078 571 -1100 577 -1092 591 -494 1167 -538 1117 -19452 593 -538 1133 -1074 603 -510 1157 -1062 595 -548 1115 -1102 573 -538 1139 -1094 567 -1094 583 -1092 567 -536 1127 -560 1113 -19478 563 -534 1151 -1084 569 -534 1135 -1116 565 -534 1135 -1084 583 -532 1155 -1060 587 -1084 589 -1084 581 -536 1125 -546 1141 -19460 577 -536 1123 -1102 565 -554 1119 -1096 593 -520 1133 -1108 563 -546 1131 -1088 569 -1094 573 -1100 583 -526 1147 -540 1123 -19466 587 -530 1129 -1100 587 -510 1143 -1084 609 -510 1131 -1110 567 -542 1121 -1102 567 -1110 563 -1106 567 -532 1149 -518 1145 -19458 573 -534 1147 -1082 575 -556 1149 -1048 589 -532 1157 -1088 565 -536 1129 -1088 589 -1106 563 -1086 587 -536 1129 -548 1147 -4298 103 -56 87 -344 87 -258 85 -278 215 -70 77 -344 279 -56 87 -56 197 -198 143 -288 362 -86 605 -144 57 -268 95 -114 143 -144 173 -144 143 -402 57 -202 259 -391 652 -340 427 -230 173 -356 97 -144 111 -246 219 -96 191 -114 173 -288 115 -56 109 -106 199 -106 73 -130 57 -172 85 -260 373 -56 629 -200 690 -230 273 -120 85 -460 85 -314 77 -78 111 -88 401 -116 171 -312 71 -500 81 -224 229 -88 257 -370 181 -172 200 -116 535 -174 113 -294 213 -359 445 -144 258 -114 115 -202 675 -509 239 -432 373 -538 85 -58 113 -86 761 -104 113 -318 443 -70 143 -144 647 -204 111 -334 87 -114 115 -144 113 -188 177 -144 199 -260 143 -86 87 -622 57 -116 171 -58 139 -222 55 -346 315 -76 345 -114 139 -171 195 -52 53 -98 119 -144 143 -244 95 -72 95 -96 167 -302 253 -186 307 -444 287 -449 115 -172 57 -172 316 -202 85 -370 697 -116 57 -144 171 -202 259 -114 85 -144 87 -315 85 -58 201 -116 171 -272 121 -358 171 -403 113 -86 115 -202 489 -229 115 -392 95 -116 171 -140 93 -102 143 -543 245 -358 215 -120 387 -288 171 -202 221 -202 115 -748 57 -316 143 -260 143 -288 115 -316 115 -58 85 -288 143 -460 485 -96 71 -104 199 -96 199 -202 143 -86 201 -116 85 -230 211 -288 115 -605 365 -126 53 -172 +RAW_Data: 317 -144 57 -486 53 -282 115 -585 97 -72 229 -174 257 -440 225 -86 173 -518 243 -167 95 -259 137 -96 694 -58 227 -80 279 -287 71 -72 301 -72 121 -106 51 -84 57 -58 199 -260 143 -288 219 -174 113 -681 115 -172 403 -58 113 -116 113 -432 171 -202 55 -108 95 -212 113 -72 527 -166 95 -212 195 -108 603 -142 239 -296 173 -346 373 -287 53 -80 79 -72 95 -238 95 -312 167 -618 143 -288 95 -72 95 -72 141 -210 55 -258 143 -328 305 -58 87 -86 315 -116 195 -218 85 -290 285 -220 215 -189 201 -58 57 -645 119 -96 71 -144 119 -406 143 -72 191 -72 631 -268 344 -56 115 -260 315 -140 455 -518 57 -58 171 -144 488 -86 219 -232 257 -144 85 -174 171 -260 115 -56 87 -166 197 -58 83 -56 85 -288 113 -410 115 -172 163 -202 113 -58 201 -144 201 -86 143 -264 167 -212 113 -116 139 -72 181 -287 343 -430 201 -260 201 -462 143 -192 301 -230 191 -454 187 -144 315 -164 143 -477 165 -58 201 -114 143 -490 115 -86 201 -58 113 -88 85 -58 203 -198 375 -86 171 -346 95 -88 257 -170 81 -56 143 -172 335 -230 173 -202 133 -471 187 -264 215 -86 115 -198 159 -72 179 -112 195 -116 449 -216 93 -96 167 -216 71 -216 71 -166 235 -86 447 -102 101 -226 195 -213 71 -144 215 -144 215 -261 241 -136 269 -142 263 -311 215 -172 201 -144 265 -168 71 -404 259 -86 85 -230 115 -650 143 -202 749 -512 248 -316 201 -154 71 -96 95 -360 105 -56 57 -432 95 -288 95 -286 95 -96 166 -144 93 -144 167 -150 904 -162 95 -526 287 -244 95 -240 383 -120 167 -394 430 -854 95 -72 143 -194 227 -120 167 -264 405 -144 143 -72 143 -72 141 -120 187 -86 143 -164 170 -96 143 -58 143 -86 402 -166 153 -120 95 -96 69 -96 71 -359 404 -338 71 -225 93 -74 97 -54 161 -114 319 -288 113 -116 459 -202 115 -114 115 -116 143 -86 57 -56 87 -114 85 -375 113 -58 311 -240 203 -288 95 -72 119 -383 213 -384 115 -86 171 -58 53 -104 401 -58 115 -86 373 -116 143 -144 161 -216 406 -72 263 -96 215 -72 95 -94 167 -96 191 -240 95 -94 214 -120 403 -116 200 -114 57 -172 220 -120 137 -364 334 -392 115 -260 199 -116 373 -188 95 -110 143 -172 87 -114 172 -230 57 -316 201 -56 249 -485 171 -202 87 -86 85 -144 345 -86 171 -58 259 -58 295 -120 95 -120 71 -192 635 -118 167 -96 375 -72 119 -120 261 -144 167 -96 95 -96 923 -215 71 -433 71 -477 +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 \ No newline at end of file diff --git a/assets/unit_tests/subghz/bett.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/bett.sub similarity index 100% rename from assets/unit_tests/subghz/bett.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/bett.sub diff --git a/assets/unit_tests/subghz/bett_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/bett_raw.sub similarity index 100% rename from assets/unit_tests/subghz/bett_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/bett_raw.sub diff --git a/assets/unit_tests/subghz/came.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/came.sub similarity index 100% rename from assets/unit_tests/subghz/came.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/came.sub diff --git a/assets/unit_tests/subghz/came_atomo_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/came_atomo_raw.sub similarity index 100% rename from assets/unit_tests/subghz/came_atomo_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/came_atomo_raw.sub diff --git a/assets/unit_tests/subghz/came_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/came_raw.sub similarity index 100% rename from assets/unit_tests/subghz/came_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/came_raw.sub diff --git a/assets/unit_tests/subghz/came_twee.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/came_twee.sub similarity index 100% rename from assets/unit_tests/subghz/came_twee.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/came_twee.sub diff --git a/assets/unit_tests/subghz/came_twee_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/came_twee_raw.sub similarity index 100% rename from assets/unit_tests/subghz/came_twee_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/came_twee_raw.sub diff --git a/assets/unit_tests/subghz/cenmax_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/cenmax_raw.sub similarity index 100% rename from assets/unit_tests/subghz/cenmax_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/cenmax_raw.sub diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/clemsa.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/clemsa.sub new file mode 100644 index 00000000000..b07d031f090 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/clemsa.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Clemsa +Bit: 18 +Key: 00 00 00 00 00 02 FC AA diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/clemsa_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/clemsa_raw.sub new file mode 100644 index 00000000000..5f86de98c61 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/clemsa_raw.sub @@ -0,0 +1,14 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: -334 10811 -4320 65 -100 65 -698 10157 -7550 65 -200 165 -166 133 -66 531 -66 331 -102 197 -132 133 -298 99 -132 263 -200 261 -988 99 -262 131 -296 97 -132 229 -100 459 -100 131 -132 393 -100 1119 -100 8325 -6376 133 -366 131 -562 65 -1034 131 -198 563 -168 365 -66 229 -332 297 -100 231 -166 429 -132 295 -166 97 -100 195 -724 97 -132 97 -1088 163 -200 1651 -100 2885 -8520 365 -166 97 -1558 163 -198 163 -132 465 -134 131 -66 267 -198 65 -232 299 -66 165 -166 65 -498 165 -100 233 -200 133 -166 131 -68 821 -100 263 -66 7633 -7610 1259 -200 99 -98 165 -1196 99 -132 263 -266 99 -200 463 -66 627 -66 1981 -98 7801 -4004 97 -628 65 -264 133 -1088 163 -134 131 -928 297 -166 133 -134 131 -266 297 -596 229 -164 427 -564 197 -166 265 -198 65 -100 559 -6708 131 -132 131 -430 527 -200 367 -66 263 -198 233 -98 299 -68 365 -296 465 -132 855 -66 857 -98 4741 -8312 99 -364 163 -200 133 -1428 529 -132 65 -166 595 -1392 97 -100 97 -132 99 -264 199 -828 99 -398 297 -66 233 -98 861 -100 663 -100 2357 -100 5075 -4640 131 -3312 231 -100 363 -132 99 -296 99 -132 165 -132 363 -98 165 -130 65 -98 165 -132 163 -130 63 -164 297 -198 9769 -3852 133 -98 67 -1226 329 -526 99 -164 295 -496 1713 -196 1681 -130 131 -132 5497 -7230 65 -1150 133 -330 259 -66 329 -100 97 -988 165 -134 197 -166 67 -100 361 -68 461 -100 231 -132 165 -66 365 -264 231 -100 99 -98 265 -696 99 -166 199 -100 101 -962 7101 -6484 363 -760 363 -132 265 -134 431 -264 329 -66 427 -330 263 -164 593 -130 231 -130 627 -66 399 -432 329 -526 131 -100 591 -166 9305 -4044 65 -3532 361 -98 163 -66 461 -264 197 -98 391 -66 329 -132 165 -136 463 -66 529 -166 131 -100 199 -264 133 -66 361 -98 689 -66 229 -198 627 -66 297 -100 261 -66 1685 -134 7883 -3932 229 -728 133 -98 133 -862 65 -132 99 -498 297 -166 133 -332 197 -132 693 -198 97 -656 1087 -64 6209 -6164 131 -266 99 -496 165 -432 67 -100 97 -330 821 -98 361 -100 493 -164 133 -66 197 -200 431 -66 65 -66 399 -66 331 -200 199 -402 131 -664 10955 -5314 65 -262 97 -198 97 -724 99 -196 65 -592 327 -66 625 -262 131 -66 197 -64 427 -132 65 -628 265 -332 329 -368 789 -66 8809 -6238 129 -328 295 -232 363 -98 431 -100 199 -98 261 -530 561 -592 263 -132 1645 -5358 65 -764 65 -330 165 -1158 197 -432 265 -98 397 -166 463 -498 561 -398 199 -66 199 -66 3479 +RAW_Data: -12124 165 -626 65 -890 229 -362 1329 -66 2187 -98 2081 -66 725 -134 3309 -9856 165 -166 263 -198 65 -960 653 -66 261 -66 821 -66 12463 -4032 97 -166 97 -924 65 -464 97 -68 163 -198 165 -100 263 -232 97 -366 633 -12244 65 -332 231 -200 197 -134 197 -234 2457 -134 399 -132 923 -198 197 -64 331 -132 295 -66 11377 -3896 163 -98 65 -194 131 -132 231 -366 131 -132 65 -1050 197 -200 299 -66 3007 -100 11685 -6172 133 -1154 491 -100 293 -200 65 -98 429 -266 463 -64 797 -98 265 -266 397 -132 1227 -66 8485 -4224 97 -166 65 -100 199 -2706 65 -66 263 -98 299 -298 231 -100 499 -66 97 -134 295 -66 431 -198 565 -66 2093 -100 533 -4056 65 -1482 229 -1160 165 -168 299 -166 459 -66 165 -134 99 -100 497 -166 397 -200 431 -200 65 -66 661 -164 529 -66 4671 -8442 131 -100 65 -66 165 -530 131 -132 597 -66 963 -488 275 -2806 2641 -438 2639 -420 2691 -412 2677 -404 2669 -416 2693 -416 361 -2730 367 -2724 2665 -422 355 -2726 2687 -394 399 -2698 2697 -390 375 -2732 2701 -388 355 -21244 2697 -416 377 -2722 2675 -416 2689 -388 2707 -396 2679 -414 2689 -392 2717 -388 375 -2706 383 -2732 2673 -396 365 -2734 2695 -386 377 -2728 2689 -384 389 -2730 2671 -386 379 -21252 2693 -416 347 -2748 2687 -396 2707 -384 2701 -388 2679 -412 2701 -418 2669 -404 363 -2726 385 -2698 2699 -416 355 -2706 2689 -416 365 -2736 2673 -386 415 -2692 2709 -378 361 -21270 2685 -452 311 -2774 2637 -442 2665 -418 2661 -448 2653 -408 2703 -384 2687 -410 365 -2738 355 -2742 2689 -382 371 -2738 2677 -384 415 -2700 2673 -410 363 -2730 2701 -386 357 -21270 2705 -398 361 -2740 2689 -386 2679 -414 2687 -410 2673 -418 2697 -386 2681 -412 383 -2702 395 -2706 2703 -380 385 -2696 2709 -418 355 -2702 2699 -418 361 -2700 2717 -386 375 -21242 2697 -418 355 -2748 2695 -382 2683 -410 2703 -380 2707 -384 2705 -404 2675 -416 383 -2700 359 -2728 2695 -382 385 -2728 2687 -416 357 -2708 2705 -386 389 -2700 2717 -388 373 -21234 2721 -418 353 -2724 2667 -416 2705 -380 2693 -386 2711 -384 2691 -422 2699 -398 365 -2726 385 -2700 2703 -378 361 -2728 2697 -420 357 -2704 2711 -388 377 -2734 2667 -406 363 -21260 2699 -418 361 -2702 2711 -380 2695 -416 2689 -402 2687 -384 2695 -416 2677 -408 361 -2732 385 -2704 2707 -414 325 -2730 2695 -418 361 -2728 2669 -414 385 -2702 2701 -414 355 -21246 2705 -378 379 -2728 2693 -384 2715 -380 2717 -386 2695 -390 2709 -388 2683 -420 355 -2730 385 -2700 2685 -416 347 -2748 2683 -396 365 -2740 2667 -416 385 -2696 2693 -414 349 -21260 2689 -452 319 -2762 2631 -476 2627 -430 2689 +RAW_Data: -416 2667 -412 2671 -416 2689 -424 347 -2732 353 -2738 2669 -410 363 -2728 2689 -410 349 -2742 2677 -386 415 -2696 2705 -380 361 -21254 2717 -386 413 -2700 2693 -380 2695 -416 2691 -398 2679 -416 2699 -384 2709 -382 367 -2726 361 -2730 2707 -382 377 -2728 2675 -386 411 -2684 2713 -414 357 -2704 2707 -388 357 -21276 2699 -382 385 -2724 2689 -416 2661 -416 2685 -384 2723 -388 2703 -400 2677 -416 385 -2696 357 -2724 2713 -384 375 -2726 2673 -420 355 -2728 2685 -396 397 -2688 2697 -408 363 -21252 2667 -484 311 -2766 2635 -476 2637 -420 2665 -448 2651 -408 2701 -384 2697 -406 355 -2758 327 -2736 2685 -420 361 -2728 2683 -384 385 -2726 2685 -414 357 -2708 2711 -388 375 -21246 2689 -448 323 -2750 2695 -378 2715 -386 2687 -392 2711 -384 2683 -416 2705 -412 327 -2732 387 -2728 2679 -416 357 -2738 2659 -418 363 -2732 2677 -386 413 -2700 2695 -380 391 -21258 2679 -406 383 -2706 2695 -384 2703 -418 2679 -404 2705 -380 2687 -418 2669 -410 359 -2726 387 -2696 2707 -416 357 -2710 2693 -418 361 -2730 2691 -380 385 -2730 2677 -414 357 -21254 2711 -382 385 -2700 2713 -414 2667 -384 2705 -418 2675 -406 2699 -382 2689 -418 365 -2726 377 -2708 2701 -384 389 -2698 2709 -398 361 -2738 2669 -416 385 -2692 2687 -418 357 -77456 131 -398 197 -132 295 -330 97 -132 229 -164 459 -164 295 -264 393 -264 719 -64 427 -98 855 -134 395 -98 297 -164 263 -262 65 -100 63 -132 197 -328 1185 -66 9359 -6420 261 -664 131 -100 299 -134 301 -232 363 -232 299 -200 165 -166 427 -230 299 -164 361 -394 1025 -100 225 -820 165 -1248 491 -100 293 -66 261 -264 131 -98 589 -164 655 -132 427 -132 295 -164 129 -132 163 -328 263 -196 627 -566 129 -100 131 -98 2377 -130 1255 -3878 297 -232 195 -132 65 -98 165 -596 397 -266 99 -198 363 -98 923 -100 431 -66 1383 -3724 297 -166 165 -66 99 -398 265 -266 463 -232 133 -232 65 -230 65 -266 959 -200 99 -298 231 -68 65 -100 97 -398 363 -132 199 -134 133 -134 133 -266 593 -66 363 -66 827 -2374 65 -1724 399 -166 265 -100 331 -198 165 -398 233 -98 233 -66 165 -266 97 -66 231 -132 165 -298 395 -234 99 -132 65 -100 99 -132 131 -66 297 -264 197 -194 229 -530 2189 -166 9577 -3702 199 -98 465 -398 97 -134 395 -132 429 -100 529 -68 263 -132 265 -368 263 -860 97 -100 163 -196 427 -98 163 -166 327 -98 493 -166 327 -98 233 -5094 99 -198 97 -100 65 -1250 131 -560 855 -66 855 -262 859 -164 10219 -7528 761 -66 1121 -100 429 -298 331 -232 263 -166 261 -166 265 -100 1427 -98 9787 -6682 131 -564 429 +RAW_Data: -66 529 -66 2519 -66 265 -68 10101 -1794 65 -1890 393 -562 97 -132 197 -98 493 -330 97 -164 97 -230 327 -326 99 -100 97 -164 65 -132 293 -98 297 -166 161 -130 297 -230 1391 -68 11185 -3800 229 -230 297 -66 65 -198 65 -466 99 -464 99 -430 67 -698 295 -132 165 -164 1095 -66 299 -66 1321 -264 12675 -66 99 -166 229 -134 65 -330 165 -164 65 -890 131 -830 67 -66 1157 -100 167 -168 265 -66 827 -66 2047 -100 261 -594 2279 -134 10701 -3890 163 -1384 67 -98 99 -1322 99 -98 65 -398 823 -66 65 -68 927 -100 495 -132 593 -100 165 -198 1387 -1022 131 -728 99 -662 97 -462 495 -200 829 -330 563 -100 297 -330 65 -598 165 -592 295 -166 131 -764 165 -164 565 -66 131 -166 165 -66 9675 -5052 165 -2878 199 -66 265 -432 265 -66 267 -898 163 -132 231 -198 229 -164 97 -100 4445 -66 7853 -636 199 -662 265 -298 233 -1428 331 -134 1791 -66 1649 -66 297 -100 361 -198 559 -98 363 -200 1315 -66 265 -98 1049 -132 1647 -66 265 -822 295 -526 131 -1712 199 -166 231 -200 165 -66 265 -166 97 -132 163 -164 395 -630 495 -168 297 -298 229 -266 629 -200 133 -132 133 -166 65 -132 99 -100 131 -66 67 -98 133 -496 1391 -98 1751 -164 359 -132 97 -164 263 -64 691 -66 199 -66 293 -98 589 -198 11299 -3968 65 -68 65 -2702 65 -1186 927 -166 65 -66 429 -134 197 -134 529 -200 67 -66 231 -100 2151 -4014 97 -1486 99 -464 65 -330 129 -330 331 -134 599 -66 497 -200 165 -66 661 -166 6881 -8830 295 -100 197 -232 725 -134 299 -166 229 -166 525 -198 295 -66 459 -66 329 -230 595 -98 299 -132 329 -66 99 -98 163 -134 229 -100 8345 -6726 131 -132 295 -66 1579 -66 329 -98 501 -132 231 -66 491 -298 331 -266 363 -132 1193 -168 8847 -4194 199 -828 65 -100 195 -262 197 -298 65 -898 65 -132 629 -66 229 -100 291 -100 623 -66 295 -66 461 -132 529 -632 597 -132 65 -100 97 -134 297 -100 297 -166 397 -168 527 -134 9603 -3850 99 -200 67 -896 959 -198 165 -100 229 -266 531 -64 165 -132 163 -296 3715 -11994 165 -1492 429 -68 263 -100 265 -330 199 -64 495 -132 363 -66 63 -166 297 -398 65 -100 231 -332 199 -100 7683 -4916 65 -1294 297 -1022 1325 -166 393 -132 165 -498 1255 -134 197 -198 427 -164 329 -132 631 -594 199 -196 99 -100 265 -134 1457 -100 3649 -8592 67 -268 131 -332 99 -100 65 -760 101 -198 297 -168 199 -132 369 -100 97 -132 99 -232 397 -198 99 -134 97 -100 231 -332 131 -796 329 -266 263 +RAW_Data: -100 10841 -4030 163 -164 197 -398 195 -592 65 -132 63 -430 295 -298 263 -200 3517 -132 3763 -12296 99 -330 361 -98 99 -200 65 -430 165 -166 2327 -100 4051 -66 9653 -3478 197 -66 163 -198 167 -66 65 -598 165 -298 131 -666 199 -198 299 -298 165 -200 565 -66 797 -98 1125 -98 825 -100 4113 -6956 65 -5536 165 -266 99 -232 461 -198 65 -200 1989 -66 295 -66 723 -66 65 -98 329 -98 955 -66 559 -232 331 -66 10851 -1048 65 -3748 65 -498 99 -1392 99 -794 529 -98 331 -98 397 -164 363 -394 331 -266 299 -230 165 -66 3001 -568 197 -2872 2579 -468 2637 -472 2599 -488 2647 -426 2653 -448 2665 -392 379 -2734 381 -2700 2691 -388 377 -2732 2669 -420 355 -2726 2687 -394 403 -2706 2687 -388 377 -21248 2717 -388 377 -2738 2659 -408 2703 -382 2689 -416 2679 -408 2701 -382 2687 -418 365 -2736 365 -2722 2689 -384 391 -2696 2707 -386 379 -2734 2665 -410 361 -2726 2703 -418 357 -21246 2679 -466 297 -2768 2657 -448 2627 -434 2669 -450 2653 -416 2673 -408 2697 -386 383 -2728 369 -2706 2701 -382 387 -2726 2687 -418 357 -2708 2693 -418 361 -2702 2709 -396 401 -21232 2689 -406 361 -2736 2695 -386 2695 -406 2695 -382 2687 -418 2675 -410 2693 -414 375 -2692 389 -2706 2701 -404 363 -2724 2695 -388 389 -2702 2719 -358 405 -2704 2701 -402 363 -21262 2677 -414 367 -2738 2677 -386 2693 -420 2701 -400 2677 -386 2695 -420 2669 -430 369 -2718 353 -2730 2673 -412 361 -2734 2691 -420 357 -2698 2701 -394 401 -2702 2687 -424 347 -21244 2701 -418 365 -2726 2703 -382 2697 -420 2675 -400 2685 -384 2721 -398 2667 -418 355 -2744 343 -2722 2703 -420 353 -2724 2689 -396 363 -2736 2687 -390 377 -2730 2697 -386 357 -21274 2683 -414 375 -2726 2667 -420 2703 -398 2677 -388 2695 -420 2699 -398 2671 -384 415 -2698 357 -2738 2695 -382 383 -2724 2685 -416 357 -2706 2707 -384 391 -2726 2671 -384 415 -21238 2651 -476 293 -2776 2653 -462 2641 -446 2661 -422 2663 -418 2689 -412 2683 -414 357 -2706 385 -2698 2715 -378 379 -2710 2719 -388 377 -2708 2695 -406 361 -2724 2689 -416 361 -21244 2703 -386 413 -2698 2697 -414 2689 -384 2685 -386 2719 -378 2701 -416 2689 -386 377 -2728 387 -2700 2673 -410 361 -2730 2695 -420 357 -2732 2679 -386 377 -2734 2699 -378 361 -21262 2697 -392 405 -2702 2687 -406 2703 -382 2703 -418 2671 -406 2677 -416 2695 -386 387 -2700 381 -2704 2695 -418 357 -2712 2721 -388 375 -2702 2693 -408 363 -2730 2693 -420 355 -21248 2653 -494 289 -91206 131 -132 97 -232 559 -132 591 -98 691 -66 131 -130 297 -66 231 -66 331 -66 433 -100 499 -132 231 -166 197 -134 593 -100 11707 -4456 133 -200 131 +RAW_Data: -66 133 -66 97 -166 561 -100 895 -132 1323 -66 10873 -3752 99 -722 229 -394 97 -66 99 -98 99 -328 297 -328 265 -298 3089 -132 10573 -1460 133 -432 99 -232 99 -132 333 -232 731 -164 65 -166 165 -132 131 -330 65 -98 131 -596 65 -198 133 -98 397 -568 65 -132 1157 -166 195 -130 131 -64 99 -66 63 -198 265 -98 297 -66 63 -166 295 -100 1747 -232 6099 -11348 199 -528 297 -266 97 -598 99 -198 231 -64 4433 -334 65 -298 65 -3284 67 -530 97 -432 133 -2356 493 -68 231 -168 297 -266 427 -100 559 -98 229 -460 197 -66 261 -132 65 -98 565 -132 231 -66 497 -100 3491 -12356 65 -660 197 -198 165 -132 331 -134 65 -98 2651 -134 4531 -10850 65 -1322 263 -68 431 -232 165 -134 165 -202 231 -300 5625 -66 6951 -8162 65 -398 99 -596 65 -132 461 -598 429 -132 97 -132 463 -232 229 -98 329 -100 397 -100 363 -100 231 -200 163 -200 961 -66 693 -100 397 -134 10601 -3872 263 -100 165 -100 131 -198 99 -696 233 -1524 331 -132 131 -164 229 -132 493 -98 631 -134 231 -100 595 -66 295 -66 5965 -8248 99 -296 99 -98 397 -66 65 -924 229 -398 299 -98 1425 -130 565 -198 827 -262 429 -598 725 -704 729 -12290 131 -98 99 -98 65 -100 163 -164 65 -494 231 -100 97 -100 863 -66 1751 -3948 165 -100 195 -66 165 -296 65 -2042 99 -200 495 -132 557 -100 827 -98 167 -66 433 -100 661 -164 689 -98 10803 -3906 231 -296 295 -232 99 -234 131 -332 395 -266 1283 -164 755 -466 397 -164 335 -66 1355 -14376 557 -66 331 -68 431 -134 599 -364 229 -100 763 -98 265 -132 525 -166 99 -396 495 -98 3867 -134 595 -168 865 -166 503 -200 467 -134 8145 -458 235 -794 599 -458 265 -436 231 -426 333 -368 299 -730 689 -360 327 -370 363 -326 367 -668 733 -328 363 -302 397 -328 371 -296 393 -666 725 -622 795 -634 433 -264 1023 -228 5041 -762 585 -466 233 -826 235 -470 631 -368 299 -402 299 -726 361 -328 331 -370 363 -332 701 -298 401 -692 369 -302 759 -268 461 -236 435 -622 423 -260 465 -266 719 -13608 65 -624 197 -558 921 -164 1315 -134 465 -134 263 -100 295 -132 293 -66 329 -98 197 -132 9977 -5036 197 -798 333 -828 295 -100 197 -100 165 -66 665 -100 763 -300 297 -166 165 -98 823 -8348 229 -100 427 -196 263 -624 197 -134 797 -100 263 -68 529 -132 233 -134 165 -264 131 -132 559 -66 263 -228 927 -132 731 -102 1061 -66 863 -8206 131 -332 299 -166 461 -100 99 -66 429 -66 3271 -98 465 -100 401 -232 331 -66 397 -430 10341 +RAW_Data: -5434 65 -298 133 -132 131 -68 231 -200 661 -132 9517 -424 97 -1456 99 -1694 393 -100 131 -560 131 -196 197 -298 65 -428 229 -196 297 -266 131 -166 2435 -66 10161 -11230 65 -1320 131 -298 265 -532 231 -200 1291 -68 631 -66 12645 -4048 133 -66 67 -132 167 -266 163 -66 397 -132 197 -132 299 -98 197 -198 2903 -66 2361 -66 9627 -3588 197 -332 165 -68 331 -68 197 -132 99 -100 663 -66 363 -230 231 -166 131 -100 201 -298 163 -132 133 -202 363 -300 397 -102 263 -100 165 -66 1221 -66 1479 -132 165 -98 229 -12976 263 -66 363 -134 231 -66 629 -132 327 -100 97 -130 99 -164 227 -64 297 -132 397 -164 425 -198 97 -198 99 -66 365 -164 199 -102 97 -66 1817 -13524 231 -134 16907 -4086 233 -630 65 -396 201 -66 165 -198 67 -198 99 -664 2117 -166 12473 -446 2649 -440 2661 -420 2651 -422 2681 -418 2703 -400 365 -2724 387 -2696 2695 -414 357 -2704 2707 -386 389 -2700 2687 -392 405 -2706 2695 -402 363 -21268 2707 -388 377 -2706 2691 -404 2699 -382 2717 -382 2707 -378 2693 -416 2687 -396 363 -2736 355 -2748 2659 -416 365 -2708 2715 -388 377 -2708 2697 -404 363 -2730 2673 -420 355 -21268 2655 -460 319 -2766 2663 -448 2631 -436 2665 -418 2683 -410 2681 -416 2701 -386 383 -2700 375 -2744 2669 -416 353 -2730 2685 -416 357 -2708 2721 -380 369 -2724 2697 -382 385 -21260 2701 -418 353 -2720 2673 -418 2675 -408 2693 -384 2715 -386 2717 -386 2691 -404 363 -2732 387 -2702 2669 -412 359 -2736 2699 -380 381 -2728 2675 -416 381 -2720 2675 -414 347 -21280 2685 -390 377 -2724 2689 -416 2673 -408 2705 -382 2695 -410 2689 -414 2661 -418 385 -2704 369 -2704 2693 -416 375 -2726 2661 -420 355 -2728 2711 -388 375 -2702 2691 -410 363 -21252 2659 -488 287 -2794 2651 -448 2629 -436 2671 -416 2695 -416 2663 -406 2699 -384 383 -2730 367 -2702 2695 -418 385 -2702 2685 -412 349 -2744 2693 -366 389 -2714 2693 -394 381 -21266 2685 -418 363 -2730 2683 -382 2693 -418 2675 -410 2699 -384 2719 -382 2707 -380 359 -2734 387 -2704 2709 -380 361 -2732 2699 -418 357 -2728 2667 -416 383 -2696 2709 -380 391 -21228 2685 -458 307 -2800 2647 -412 2659 -432 2667 -416 2695 -416 2675 -406 2675 -416 383 -2700 361 -2730 2687 -414 375 -2696 2701 -420 353 -2720 2711 -382 367 -2728 2675 -416 385 -21222 2735 -386 355 -2744 2687 -396 2679 -418 2701 -386 2705 -382 2681 -410 2697 -384 385 -2736 365 -2704 2715 -384 377 -2696 2697 -416 349 -2722 2707 -386 379 -2732 2671 -410 361 -21258 2681 -464 297 -2796 2629 -456 2655 -420 2661 -448 2663 -404 2695 -382 2715 -380 371 -2740 355 -2744 2679 -384 391 -2728 2675 -388 379 +RAW_Data: -2728 2695 -414 357 -2704 2705 -418 357 -21262 2673 -416 383 -2696 2709 -380 2703 -384 2699 -418 2671 -408 2695 -382 2713 -386 379 -2730 357 -2732 2695 -384 383 -2730 2679 -416 357 -2708 2701 -410 349 -2736 2697 -382 385 -21252 2669 -478 289 -2790 2647 -426 2651 -444 2653 -430 2659 -418 2695 -414 2681 -402 349 -2738 383 -2722 2677 -414 347 -2744 2691 -382 369 -2730 2691 -384 383 -2734 2679 -414 347 -21264 2705 -386 379 -2736 2667 -410 2695 -382 2715 -380 2709 -420 2665 -392 2713 -382 383 -2730 365 -2728 2665 -418 383 -2696 2693 -418 357 -2710 2711 -380 375 -2718 2701 -416 357 -21238 2677 -484 311 -2766 2635 -444 2657 -420 2663 -422 2695 -416 2667 -428 2675 -396 363 -73890 133 -98 131 -132 129 -658 99 -66 853 -100 63 -100 361 -98 1589 -66 1231 -132 65 -100 297 -198 65 -132 265 -66 9857 -4672 165 -1030 97 -1394 65 -200 2687 -68 6873 -8336 99 -1156 97 -66 163 -232 163 -262 197 -132 295 -132 263 -166 953 -100 263 -130 393 -164 295 -64 329 -66 393 -164 823 -130 165 -66 6133 -8436 165 -164 265 -266 65 -362 197 -696 3181 -132 363 -98 65 -166 131 -66 399 -132 663 -396 329 -66 7335 -7578 497 -230 627 -264 99 -366 99 -132 131 -134 265 -498 163 -100 1323 -66 265 -66 1129 -100 399 -132 365 -100 795 -68 397 -98 597 -364 297 -132 361 -132 265 -132 8591 -4740 65 -100 131 -166 199 -1088 97 -296 99 -528 131 -98 661 -66 401 -198 1157 -166 361 -164 495 -100 165 -66 297 -100 1423 -66 3067 -5658 67 -6406 197 -1092 65 -530 659 -68 265 -100 991 -68 231 -230 297 -66 327 -66 131 -132 659 -134 131 -100 1183 -132 263 -98 621 -66 2075 -6976 65 -5138 67 -132 129 -664 67 -132 165 -100 331 -466 231 -68 467 -98 563 -66 231 -100 531 -66 465 -66 1023 -166 297 -134 3409 -12290 67 -164 99 -532 133 -166 263 -66 231 -66 721 -64 131 -68 959 -134 495 -100 299 -98 497 -98 365 -100 397 -232 297 -98 531 -66 3029 -12216 265 -132 99 -364 199 -234 131 -66 431 -166 333 -166 397 -132 327 -100 395 -66 197 -132 395 -66 527 -98 295 -100 97 -98 789 -132 363 -132 297 -200 2815 -4914 65 -6620 65 -462 65 -134 297 -66 497 -264 231 -198 2773 -134 365 -100 831 -166 131 -100 297 -132 861 -132 299 -100 561 -66 1381 -6946 65 -5516 231 -266 97 -1362 1093 -68 1621 -134 165 -332 297 -98 361 -228 97 -132 797 -98 3487 -13224 229 -164 65 -132 913 -66 1123 -98 527 -134 929 -98 723 -100 12259 -270 165 -132 67 -132 165 -1326 99 -98 65 -1194 431 -66 695 -66 733 -134 197 +RAW_Data: -134 10801 -166 67 -6130 133 -198 231 -334 365 -98 229 -132 165 -68 231 -166 14501 -524 65 -328 131 -498 129 -1288 65 -494 163 -64 165 -66 527 -132 131 -132 1019 -198 129 -166 393 -198 65 -164 6411 -66 3255 -10642 65 -1320 165 -164 493 -492 559 -264 2555 -66 695 -66 1657 -164 855 -66 4001 -10526 97 -596 133 -298 67 -264 65 -300 65 -100 263 -166 231 -134 99 -100 2703 -68 13643 -4922 297 -100 65 -232 133 -198 331 -300 231 -66 331 -100 12047 -3872 97 -196 65 -494 329 -66 65 -890 97 -98 229 -164 195 -596 797 -66 861 -132 65 -66 231 -100 565 -66 65 -66 1297 -132 265 -66 363 -134 265 -364 297 -164 299 -134 297 -134 495 -98 11309 -3790 131 -1380 65 -758 65 -164 129 -460 65 -360 199 -100 563 -68 497 -198 363 -266 263 -100 165 -66 697 -66 1933 -13594 65 -762 1223 -132 1119 -196 361 -134 131 -100 793 -166 695 -68 231 -68 463 -66 11727 -4204 363 -264 131 -132 133 -1124 97 -100 163 -100 327 -100 331 -198 397 -66 397 -100 395 -100 163 -66 197 -564 1059 -7962 65 -100 65 -198 129 -362 99 -394 197 -296 495 -100 1357 -68 459 -66 593 -66 265 -68 301 -132 465 -66 231 -200 397 -66 397 -232 199 -298 12077 -4350 231 -796 363 -198 133 -264 65 -1132 597 -332 3295 -100 755 -98 231 -164 97 -264 459 -166 759 -164 3265 -12138 99 -232 99 -1228 1025 -100 393 -66 531 -132 693 -132 1063 -66 427 -64 297 -294 229 -98 9723 -5404 67 -466 99 -796 267 -98 201 -100 167 -264 461 -98 1415 -66 861 -66 267 -66 331 -134 1663 -66 2089 -7012 65 -100 101 -4804 431 -728 99 -100 65 -100 995 -134 165 -66 929 -100 65 -66 927 -100 1093 -168 99 -100 497 -66 665 -200 6517 -8312 165 -66 129 -66 559 -166 99 -430 65 -398 67 -66 593 -198 459 -132 261 -132 263 -130 723 -66 459 -100 325 -166 67 -198 559 -66 493 -66 11475 -3896 99 -266 99 -66 197 -1092 129 -198 361 -166 163 -98 263 -196 759 -100 265 -100 365 -630 4635 -12748 65 -1712 461 -100 497 -66 395 -98 265 -98 229 -164 529 -132 297 -66 565 -132 987 -132 8665 -2820 2265 -450 313 -2774 2643 -442 325 -2772 2665 -416 359 -2734 2667 -386 379 -21274 2657 -474 293 -2810 2619 -466 2613 -476 2629 -452 2663 -388 2683 -418 2705 -400 365 -2722 387 -2700 2697 -380 361 -2732 2691 -418 361 -2732 2667 -416 383 -2698 2697 -416 357 -21238 2715 -384 383 -2732 2685 -416 2667 -416 2695 -398 2671 -418 2687 -390 2713 -382 383 -2730 365 -2728 2661 -416 379 -2716 2685 -384 379 -2720 2703 -378 401 -2718 2671 diff --git a/assets/unit_tests/subghz/doitrand.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/doitrand.sub similarity index 100% rename from assets/unit_tests/subghz/doitrand.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/doitrand.sub diff --git a/assets/unit_tests/subghz/doitrand_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/doitrand_raw.sub similarity index 100% rename from assets/unit_tests/subghz/doitrand_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/doitrand_raw.sub diff --git a/assets/unit_tests/subghz/doorhan.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/doorhan.sub similarity index 100% rename from assets/unit_tests/subghz/doorhan.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/doorhan.sub diff --git a/assets/unit_tests/subghz/doorhan_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/doorhan_raw.sub similarity index 100% rename from assets/unit_tests/subghz/doorhan_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/doorhan_raw.sub diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/dooya.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/dooya.sub new file mode 100644 index 00000000000..0767a1a738f --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/dooya.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Dooya +Bit: 40 +Key: 00 00 00 E1 DC 03 05 11 diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/dooya_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/dooya_raw.sub new file mode 100644 index 00000000000..6c3ca1627a7 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/dooya_raw.sub @@ -0,0 +1,8 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +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 diff --git a/assets/unit_tests/subghz/faac_slh_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/faac_slh_raw.sub similarity index 100% rename from assets/unit_tests/subghz/faac_slh_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/faac_slh_raw.sub diff --git a/assets/unit_tests/subghz/gate_tx.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/gate_tx.sub similarity index 100% rename from assets/unit_tests/subghz/gate_tx.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/gate_tx.sub diff --git a/assets/unit_tests/subghz/gate_tx_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/gate_tx_raw.sub similarity index 100% rename from assets/unit_tests/subghz/gate_tx_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/gate_tx_raw.sub diff --git a/assets/unit_tests/subghz/holtek.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/holtek.sub similarity index 86% rename from assets/unit_tests/subghz/holtek.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/holtek.sub index b77a759d2ee..8dbd3ea203a 100644 --- a/assets/unit_tests/subghz/holtek.sub +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/holtek.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz Key File Version: 1 -Frequency: 418000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: Holtek Bit: 40 diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/holtek_ht12x.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/holtek_ht12x.sub new file mode 100644 index 00000000000..09e20b13423 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/holtek_ht12x.sub @@ -0,0 +1,8 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Holtek_HT12X +Bit: 12 +Key: 00 00 00 00 00 00 0F FB +TE: 205 diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/holtek_ht12x_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/holtek_ht12x_raw.sub new file mode 100644 index 00000000000..1aeedac38b7 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/holtek_ht12x_raw.sub @@ -0,0 +1,10 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 97 -264 65 -1890 231 -366 165 -232 99 -166 297 -68 401 -100 97 -100 759 -264 823 -132 919 -98 65 -230 367 -98 1055 -52 51 -104 101 -126 173 -148 151 -100 153 -222 77 -288 53 -280 331 -212 81 -108 246 -156 267 -268 183 -1260 53 -622 51 -468 183 -264 105 -990 77 -421 75 -52 53 -214 185 -504 75 -563 131 -338 165 -82 133 -80 53 -236 231 -208 282 -836 187 -406 81 -102 181 -54 107 -415 81 -54 109 -298 55 -406 323 -136 299 -136 437 -52 133 -294 53 -110 133 -420 333 -402 269 -574 79 -270 163 -594 124 -52 51 -320 79 -324 109 -78 105 -236 79 -292 53 -432 181 -110 187 -268 107 -78 79 -188 79 -78 75 -130 75 -128 103 -628 79 -322 79 -136 81 -196 85 -296 51 -349 159 -325 131 -80 131 -80 402 -110 55 -194 109 -108 53 -108 135 -224 81 -162 57 -228 161 -357 105 -54 103 -347 279 -294 422 -152 51 -104 99 -146 147 -205 77 -216 107 -52 181 -52 107 -158 105 -52 133 -134 139 -418 291 -102 103 -106 105 -584 103 -540 53 -52 153 -208 227 -128 307 -184 105 -328 109 -862 129 -196 55 -244 51 -1501 55 -82 81 -326 79 -500 103 -406 207 -130 75 -126 155 -202 53 -376 135 -158 53 -428 107 -467 167 -138 81 -106 79 -346 157 -84 143 -144 85 -136 53 -308 189 -248 79 -711 109 -592 107 -220 107 -104 77 -583 131 -158 53 -110 135 -191 107 -748 105 -478 77 -398 107 -138 83 -410 127 -477 103 -162 119 -72 103 -442 129 -237 107 -664 175 -74 147 -406 53 -78 127 -178 51 -102 101 -259 125 -202 257 -210 77 -52 77 -130 251 -230 77 -76 77 -178 155 -98 97 -100 153 -54 185 -82 79 -82 135 -398 241 -130 77 -76 311 -210 53 -238 181 -404 241 -352 189 -270 265 -164 215 -998 157 -82 77 -106 51 -54 75 -100 328 -178 103 -224 253 -341 147 -334 446 -76 125 -100 75 -126 173 -690 75 -250 51 -554 81 -300 53 -188 53 -106 265 -1024 105 -646 221 -82 191 -362 75 -234 131 -128 83 -224 55 -240 77 -376 157 -78 187 -358 389 -76 103 -128 199 -513 83 -492 129 -618 123 -52 129 -52 79 -376 101 -688 105 -110 55 -84 179 -152 51 -486 358 -276 99 -74 73 -679 51 -210 113 -196 365 -104 223 -104 53 -430 55 -140 55 -84 153 -202 181 -358 149 -820 103 -80 215 -108 309 -368 105 -538 154 -732 233 -588 53 -253 155 -288 55 -136 55715 -102 407 -100 259 -78 157 -130 206 -52 258 -78 441 -104 77 -80 492 -104 182 -104 79 -155 +RAW_Data: 287 -78 807 -104 1588 -52 2952 -78 1018 -240 341 -262 361 -258 335 -280 309 -100 509 -280 307 -302 307 -6968 321 -290 293 -298 323 -294 289 -314 285 -334 285 -330 257 -330 281 -328 293 -310 281 -124 479 -330 281 -326 267 -6994 295 -324 267 -320 287 -340 259 -332 257 -358 257 -354 257 -328 257 -352 257 -352 255 -150 455 -360 255 -326 255 -7024 253 -352 255 -352 255 -354 267 -326 269 -352 239 -346 261 -362 233 -358 257 -356 255 -152 455 -334 255 -354 255 -7006 267 -342 267 -352 241 -350 263 -342 259 -362 233 -356 257 -356 231 -378 231 -380 231 -150 455 -358 231 -378 229 -7050 229 -378 229 -352 253 -352 239 -372 239 -380 243 -348 235 -368 231 -388 231 -356 233 -176 431 -384 229 -380 231 -7028 239 -368 241 -372 241 -350 239 -372 233 -366 233 -384 231 -358 231 -380 231 -378 231 -176 429 -384 205 -402 205 -7064 213 -380 243 -378 215 -374 233 -368 233 -386 231 -382 205 -382 231 -380 229 -380 231 -176 429 -386 203 -378 231 -7062 229 -378 229 -378 229 -378 229 -378 213 -396 203 -404 203 -404 203 -404 203 -404 203 -198 431 -376 215 -376 239 -7064 235 -364 231 -386 231 -384 205 -408 205 -380 231 -380 231 -380 231 -378 207 -404 205 -200 431 -384 205 -404 205 -7118 205 -408 205 -406 205 -408 205 -408 205 -408 205 -436 179 -440 283 -460 75 -1486 105 -54 103 -784 107 -218 113 -824 125 -2140 85 -583 51 -1291 53 -722 113 -622 107 -1176 51 -52 75 -152 113 -56 111 -784 105 -436 81 -112 85 -272 53 -406 81 -1345 105 -3008 53 -1418 105 -254 107 -874 131 -818 53 -595 77 -130 123 -1088 213 -724 131 -793 99 -428 75 -288 51 -334 101 -1471 81 -274 53 -164 107 -2618 129 -532 55 -366 297 -160 83 -166 184 -208 57 -196 328 -1640 235 -1743 79 -594 51 -76 77 -270 163 -728 51 -408 55 -354 101 -126 153 -184 133 -392 53 -604 77 -804 127 -965 51 -78 103 -668 262 -210 237 -439 75 -340 77 -106 105 -1775 79 -284 51 -3100 157 -433 55 -1355 107 -198 165 -82 79 -770 79 -1587 81 -638 79 -530 103 -703 51 -396 71 -956 151 -248 53 -553 103 -154 75 -806 75 -660 53 -698 127 -1210 53 -3175 79 -608 245 -1590 207 -164 107 -232 51 -1094 99 -695 135 -955 53 -804 217 -587 55 -1452 163 -1232 79 -968 79 -720 81 -1110 129 -1194 105 -1736 79 -386 79 -184 77 -652 75 -2442 103 -555 129 -125 75 -1115 81 -108 135 -980 53 -808 105 -1001 155 -1068 131 -850 99 -268 51 -1106 159 -2408 79 -612 51 -1191 +RAW_Data: 135 -212 77 -2816 137 -980 51 -1921 79 -1554 53 -906 83 -1042 99 -5161 51 -290 75 -558 51 -260 77 -738 99 -2128 105 -1384 53 -108 79 -188 55 -830 77 -5690 109 -2652 51 -1374 53 -2469 79 -2163 203 -152 71 -1459 79 -2741 105 -262 75 -1194 75 -228 153 -210 107 -3236 107 -1500 77 -3562 79 -170 55 -540 53 -1270 51 -304 51 -2071 137 -134 105 -266 101 -834 51 -8117 73 -3292 81 -732 181 -1539 53 -132 109 -1375 77 -132 81 -1061 77 -1620 79 -1373 105 -643 183 -190 53 -620 77 -1633 55 -931 51 -398 135 -924 111 -1122 51 -128 125 -2361 97 -2571 51 -3563 51 -794 77 -844 81 -788 81 -2040 77 -232 77 -1000 131 -322 73 -848 55 -336 107 -1546 81 -602 79 -954 107 -1824 73 -1131 240 -1048 197 -2442 73 -720 127 -78 77 -574 79 -624 101 -5754 103 -1829 149 -719 77 -982 53 -250 53 -634 161 -2201 75 -3483 149 -356 73 -98 123 -176 75 -702 231 -1633 121 -704 53 -1163 51 -1227 73 -368 123 -258 103 -530 279 -628 53 -884 55 -82 101 -102 135 -162 107 -1264 159 -3370 77 -3538 131 -130 81 -2460 129 -588 51 -514 77 -376 53 -616 53 -956 51 -1508 53 -1044 53 -1160 261 -442 53 -640 83 -300 79 -1723 75 -124 125 -532 103 -500 79 -764 53 -1220 279 -458 77 -548 159 -450 223 -718 55 -716 51 -716 203 -732 79 -6974 53 -1086 51 -1794 79 -882 53 -4221 53 -1070 77 -1126 75 -2307 155 -1233 131 -156 111 -1352 51 -478 51 -154 75 -724 51 -635 103 -652 77 -294 75 -268 81 -1046 177 -1778 51 -298 109 -336 77 -80 53 -192 133 -556 99 -394 81 -654 51 -106 79 -1893 55 -240 55 -136 55 -2680 77 -136 81 -164 53 -576 135 -3304 131 -654 103 -788 53 -224 187 -354 79 -1872 81 -290 207 -410 77 -1132 55 -112 57 -512 51 -2736 123 -404 239 -1283 51 -1526 213 -720 133 -354 109 -396 77 -1518 105 -632 53 -164 53 -1090 55 -730 79 -1705 75 -370 75 -378 79 -2022 81 -2480 103 -1108 79 -106 53 -1784 105 -512 219 -669 179 -795 105 -376 105 -514 53 -402 53 -282 83 -554 133 -448 105 -972 79 -490 81 -1184 77 -216 109 -132 51 -164 135 -1570 51 -1329 79 -1218 53 -2450 75 -1246 51 -3825 77 -1114 53 -1718 51 -846 79 -826 165 -306 75 -3821 51 -606 55 -880 107 -56 55 -5505 53 -504 79 -1356 77 -569 51 -2268 149 -358 53 -1979 51 -106 103 -176 221 -508 51 -110 53 -3896 127 -234 79 -1012 79 -84 243 -2860 53 -1010 73 -286 133 -2198 151 -370 129 -1942 101 -154 109 -702 79 -696 51 -302 +RAW_Data: 51 -744 83 -496 79 -672 77 -170 111 -264 103 -472 51 -524 75 -1172 51 -240 51963 -254 333 -278 333 -276 333 -278 305 -302 307 -76 531 -280 331 -276 331 -6938 347 -268 319 -272 319 -292 311 -312 283 -306 309 -304 283 -328 293 -310 281 -326 293 -110 479 -322 295 -322 291 -6964 283 -334 283 -304 283 -328 283 -328 281 -328 255 -354 265 -338 255 -352 255 -352 255 -124 481 -338 277 -328 279 -6984 269 -350 265 -320 287 -340 257 -332 257 -358 255 -356 255 -328 257 -354 257 -352 255 -150 455 -360 229 -376 231 -7040 239 -348 267 -350 239 -346 261 -364 231 -360 257 -356 231 -380 231 -378 231 -352 257 -150 453 -360 229 -378 229 -7032 239 -372 227 -378 227 -380 241 -352 241 -374 233 -366 233 -384 233 -356 231 -380 231 -176 431 -384 229 -354 255 -7030 231 -376 231 -376 229 -378 229 -378 229 -352 255 -352 255 -352 229 -378 229 -378 229 -174 427 -384 229 -376 241 -7040 241 -350 235 -368 233 -388 231 -358 233 -380 231 -380 229 -380 231 -380 229 -380 211 -192 421 -366 227 -380 227 -7068 239 -378 215 -402 209 -394 207 -388 231 -386 231 -384 205 -408 205 -408 205 -408 205 -202 409 -412 207 -1400 105 -1002 51 -9495 53 -250 57 -1093 155 -124 73 -344 75 -2759 83 -468 53 -738 77 -134 53 -1581 51 -106 127 -1209 121 -956 51 -918 83 -276 85 -1696 125 -618 81 -1666 51 -152 101 -1324 107 -54 141 -586 75 -784 55 -1828 51 -4052 81 -480 53 -218 141 -1346 105 -1152 127 -776 53 -426 135 -390 105 -939 81 -887 71 -492 107 -1311 105 -1844 101 -1340 77 -2586 51 -2637 51 -1626 105 -54 53 -1672 151 -2830 57 -3143 51 -1859 79 -929 179 -78 77 -890 73 -894 79 -80 79 -1184 53 -323 53 -1344 79 -636 53 -1808 55 -3048 79 -2287 53 -572 51 -822 51 -608 77 -1772 75 -2521 79 -162 81 -664 163 -110 83 -524 53 -930 53 -1816 79 -1305 51 -816 53 -1358 55 -822 55 -594 81 -2230 55 -234 77 -600 201 -3174 151 -2534 71 -122 51 -1370 81 -3130 127 -236 79 -728 101 -1472 53 -800 127 -528 51 -802 77 -52 99 -3144 77 -6346 51 -1090 81 -588 79 -292 169 -2345 107 -370 187 -1218 81 -296 75 -696 51 -516 77 -2154 75 -558 75 -816 103 -2200 125 -2766 229 -376 151 -3375 79 -1466 53 -535 167 -524 217 -54 131 -3408 51 -54 109 -1886 77 -732 83 -536 99 -3128 103 -168 57 -1852 51 -574 79 -296 155 -844 99 -767 147 -2406 99 -1014 81 -4460 175 -226 195 -454 73 -2236 53 -818 155 -352 83 -752 79 -5083 51 -1716 +RAW_Data: 77 -1925 51 -1760 81 -162 53 -1469 79 -362 53 -471 103 -750 53 -562 127 -238 79 -250 107 -52 53 -210 83 -504 79 -1566 107 -82 79 -968 53 -458 131 -1944 301 -594 79 -382 51 -1196 79 -3963 183 -670 111 -360 105 -1990 53 -1084 79 -658 73 -301 73 -264 75 -1335 79 -1110 81 -1856 159 -3986 75 -1262 53 -1189 53 -158 53 -903 81 -1135 133 -350 107 -178 151 -1096 73 -2734 81 -1280 107 -950 79 -52 53 -5912 53 -1653 111 -1601 79 -742 51 -1104 55 -1254 51 -867 53 -136 109 -813 79 -1082 53 -395 53 -54 109 -504 79 -218 109 -114 57 -1620 157 -3003 79 -656 53 -510 209 -1933 107 -1197 159 -5508 79 -1164 77 -1466 77 -742 101 -124 71 -2049 85 -144 109 -1304 105 -2310 55 -381 83 -114 113 -944 103 -184 83 -558 55 -2064 109 -760 75 -1036 77 -574 51 -134 131 -224 57 -104 53 -440 53 -1262 183 -2454 51 -1966 73 -1950 125 -1095 51 -480 121 -1994 57 -1930 103 -786 79 -2272 105 -3312 51 -746 127 -144 133 -1608 77 -692 51 -1136 53 -164 55 -573 55 -3110 53 -1558 105 -6248 53 -1051 111 -886 105 -2234 103 -106 53 -1256 101 -1446 111 -974 79 -851 81 -136 193 -3392 83 -582 103 -1197 111 -196 55 -906 51 -742 77 -2038 71 -686 53 -1943 51 -134 51 -852 51 -1658 133 -2050 161 -388 77 -326 81 -412 55 -1137 81 -3256 55 -1516 53 -1414 117 -372 51 -1144 199 -3087 51 -430 75 -1856 151 -128 95 -192 398 -1207 77 -280 51 -2716 51 -808 53 -78 77 -1524 109 -54 79 -410 79 -132 53 -770 109 -2066 185 -368 131 -2102 125 -1037 75 -780 127 -128 51 -176 53 -1982 77 -140 111 -1046 109 -3166 169 -1956 77 -4040 79 -2778 73 -204 129 -1546 82945 -150 359 -252 333 -76 533 -280 319 -286 305 -6960 319 -300 323 -270 317 -290 313 -308 283 -306 309 -304 283 -328 291 -310 281 -326 281 -124 481 -334 279 -302 305 -6970 293 -318 301 -304 279 -328 279 -328 277 -330 277 -330 293 -298 295 -320 287 -314 283 -128 489 -334 255 -330 281 -7012 265 -340 265 -344 251 -354 253 -354 269 -322 293 -322 263 -344 259 -336 257 -360 257 -152 457 -358 231 -354 257 -7060 237 -364 255 -354 237 -366 255 -354 255 -354 267 -352 241 -352 265 -348 261 -342 259 -154 439 -388 231 -358 255 -7114 237 -370 235 -394 233 -362 233 -386 233 -386 233 -386 233 -386 231 -388 233 -414 233 -180 441 -392 233 -1832 53 -564 53 -370 289 -867 201 -78 103 -352 213 -586 103 -1226 165 -112 55 -300 105 -975 107 -358 77 -410 55 -1777 51 -973 51 -828 diff --git a/assets/unit_tests/subghz/holtek_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/holtek_raw.sub similarity index 99% rename from assets/unit_tests/subghz/holtek_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/holtek_raw.sub index 8eac0a398d1..d63c4a00a9f 100644 --- a/assets/unit_tests/subghz/holtek_raw.sub +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/holtek_raw.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 418000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: 2243 -98 331 -100 1129 -66 761 -100 1393 -100 165 -66 2883 -64 357 -66 4703 -68 927 -98 233 -134 461 -66 3855 -134 165 -98 1281 -100 2053 -66 3061 -98 331 -98 8981 -66 365 -66 631 -100 1027 -100 4521 -134 597 -66 3187 -66 2619 -100 3011 -98 1151 -66 953 -100 1423 -66 1755 -166 333 -98 1557 -66 761 -66 865 -66 4837 -132 357 -132 2419 -100 1023 -66 65 -66 2507 -66 131 -66 761 -66 997 -66 333 -100 2259 -68 431 -100 2523 -66 987 -100 363 -66 363 -66 1197 -68 1589 -164 951 -96 5351 -66 697 -100 163 -100 4683 -66 2265 -68 2051 -64 457 -64 3005 -132 1057 -66 2221 -100 1661 -98 695 -100 99 -66 861 -66 1957 -100 731 -132 1857 -100 3177 -98 1807 -98 463 -66 499 -134 1129 -100 3737 -100 1889 -66 263 -98 623 -66 2103 -98 3165 -66 131 -100 195 -66 691 -66 67 -132 531 -66 1857 -100 199 -68 97 -68 197 -68 697 -68 233 -100 3749 -134 1691 -68 3289 -66 3751 -68 65 -100 853 -66 531 -132 1299 -66 1585 -98 65 -98 1577 -66 785 -98 1151 -66 165 -68 397 -100 4255 -100 857 -100 1017 -66 1575 -130 1255 -234 1923 -66 199 -102 301 -66 231 -66 691 -64 227 -64 195 -66 1257 -100 2353 -100 235 -100 1163 -66 5423 -66 2049 -66 1807 -66 523 -198 693 -100 367 -100 597 -100 4013 -100 233 -166 365 -66 1827 -100 1491 -100 785 -64 885 -66 599 -134 2847 -100 667 -100 4943 -98 3319 -98 6729 -98 361 -96 391 -66 723 -132 503 -66 1583 -166 297 -234 2045 -66 1185 -134 661 -66 195 -66 291 -164 523 -98 1679 -134 233 -132 761 -394 855 -100 2003 -164 261 -66 229 -96 953 -66 3889 -66 929 -66 993 -68 3099 -132 1673 -66 1833 -100 563 -100 1131 -100 3219 -232 4411 -100 1095 -100 5315 -100 631 -198 461 -198 1907 -100 1743 -68 863 -132 4013 -64 295 -66 3883 -100 2707 -198 923 -100 2539 -166 629 -100 563 -100 3783 -68 893 -66 2987 -98 2357 -98 1665 -66 599 -66 1259 -232 165 -66 1361 -66 1645 -166 1543 -66 565 -66 401 -134 465 -100 831 -98 2405 -100 1055 -66 2109 -100 1161 -68 431 -100 265 -68 235 -66 463 -66 3453 -100 433 -66 2693 -132 263 -166 729 -134 763 -134 1327 -100 397 -234 795 -68 563 -66 1625 -98 267 -66 4835 -66 197 -66 589 -66 7575 -100 1959 -100 131 -68 297 -134 261 -98 433 -66 1427 -66 2421 -100 2925 -166 1921 -134 1645 -66 97 -132 5423 -100 2423 -98 1065 -66 1715 -132 963 -66 2403 -66 1117 -328 1981 -66 527 -100 427 -164 865 -66 2129 -232 165 -68 165 -66 131 -366 131 -100 2613 -450 diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/honeywell_wdb.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/honeywell_wdb.sub new file mode 100644 index 00000000000..0f2316d5e54 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/honeywell_wdb.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Honeywell +Bit: 48 +Key: 00 00 0E DB 70 20 00 01 diff --git a/assets/unit_tests/subghz/honeywell_wdb_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/honeywell_wdb_raw.sub similarity index 99% rename from assets/unit_tests/subghz/honeywell_wdb_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/honeywell_wdb_raw.sub index 0fbd6d23e43..a8bbaa8fb91 100644 --- a/assets/unit_tests/subghz/honeywell_wdb_raw.sub +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/honeywell_wdb_raw.sub @@ -1,7 +1,7 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 868250000 -Preset: FuriHalSubGhzPreset2FSKDev476Async +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: 414 -1374 51 -108 79 -216 189 -584 53 -214 53 -400 215 -264 129 -322 77 -288 79 -54 107 -184 51 -446 53 -160 161 -305 263 -104 264 -134 53 -806 267 -80 187 -314 53 -158 83 -1172 243 -833 79 -188 345 -208 77 -182 225 -100 107 -106 187 -190 105 -432 176 -174 119 -328 79 -432 79 -102 99 -514 127 -218 79 -184 77 -52 101 -356 53 -615 243 -266 181 -102 212 -138 81 -486 205 -574 77 -256 155 -152 217 -78 75 -1010 393 -346 79 -488 107 -284 121 -72 215 -174 183 -54 257 -332 179 -52 79 -238 53 -636 161 -80 227 -950 346 -160 109 -564 295 -160 299 -160 435 -487 312 -52 105 -292 77 -246 137 -498 366 -104 255 -362 97 -210 53 -555 374 -206 129 -96 303 -280 179 -172 75 -102 99 -76 353 -228 131 -252 147 -130 79 -304 103 -82 139 -52 103 -183 77 -284 75 -454 55 -188 77 -222 161 -128 107 -136 187 -270 53 -371 184 -364 103 -284 129 -52 103 -321 81 -554 261 -1048 107 -106 243 -280 103 -478 215 -530 53 -108 53 -236 203 -180 51 -78 77 -338 81 -82 53 -231 75 -124 256 -232 227 -448 131 -340 131 -266 107 -346 51 -254 75 -134 210 -182 103 -280 127 -122 305 -310 255 -528 77 -513 79 -214 209 -102 53 -80 133 -1727 237 -78 79 -242 53 -296 133 -532 53 -513 53 -54 131 -190 53 -661 129 -218 107 -394 103 -554 157 -112 343 -314 103 -283 79 -304 135 -56 111 -272 189 -370 271 -270 105 -956 79 -184 77 -868 307 -156 129 -731 51 -200 161 -84 81 -326 51 -54 157 -168 369 -152 101 -188 81 -398 239 -132 215 -54 105 -182 317 -206 99 -198 97 -274 157 -271 121 -268 200 -330 499 -184 55 -434 133 -646 283 -152 255 -428 101 -350 199 -124 145 -304 308 -102 75 -386 107 -186 129 -534 101 -180 175 -100 151 -98 53 -78 133 -794 183 -150 83 -220 185 -280 83 -537 107 -308 55 -138 79 -714 79 -538 77 -106 133 -242 263 -236 75 -884 215 -136 81 -270 133 -624 105 -132 79 -848 291 -373 79 -244 55 -619 203 -202 77 -156 103 -176 99 -408 107 -318 77 -316 79 -500 55 -691 51 -340 129 -266 53 -486 103 -376 103 -185 51 -156 81 -106 77 -104 79 -502 208 -292 133 -432 105 -52 105 -80 51 -158 317 -372 181 -844 51 -270 107 -312 79 -302 83 -444 53 -640 77 -268 183 -138 85 -192 79 -158 131 -132 155 -220 81 -245 127 -386 185 -296 53 -608 77 -308 51 -822 105 -832 51 -850 155 -242 105 -422 79 -270 155 -76 77 -162 55 -414 RAW_Data: 109 -630 77 -190 53 -52 131 -860 53 -1300 83 -1122 99 -432 53 -724 81 -390 159 -106 81 -2607 79 -474 75 -694 151 -146 73 -104 53 -380 77 -278 77 -128 73 -148 73 -248 99 -314 75 -154 97 -2225 51 -952 183 -970 77 -1210 51 -326 103 -368 151 -252 121 -784 51 -108 187 -256 227 -242 105 -104 150 -128 77 -1287 173 -2513 77 -1144 105 -1268 111 -3964 101 -1348 81 -2718 81 -302 79 -1999 55 -1268 57 -7269 53 -424 83 -3736 105 -1552 51 -8460 57 -5224 101 -244 121 -4124 103 -10775 133 -1858 73 -20679 51 -5532 97 -1718 103 -3764 51 -6496 79 -792 53 -13269 81 -18787 57 -9140 83 -3302 53 -11039 55 -26162 79 -1713 103 -5860 53 -9589 55 -8389 81 -254 85 -16287 73 -8336 53 -6237 75 -828 55 -19389 73 -8028 79 -6284 83 -3460 77 -13504 75 -3198 75 -1280 51 -1948 51 -758 79 -22076 75 -1681 73 -21693 57 -8106 53 -838 51 -10074 51 -4760 55 -492 79 -6558 79 -22996 75 -13904 53 -5564 55 -16578 83 -25603 77 -410 75 -16694 81 -606 53 -2987 53 -3898 107 -3248 79 -5168 55 -754 81 -20662 53 -11066 51 -8624 79 -26384 79 -2214 81 -7442 79 -12488 53 -1656 71 -4508 55 -15680 57 -6669 51 -5410 71 -6411 53 -6082 79 -13772 53 -5945 55 -574 73 -11921 51 -3472 55 -2323 55 -10414 51 -16069 81 -2678 77 -10775 53 -2106 110 -11794 55 -17082 51 -6184 71 -8376 79 -7152 77 -2691 53 -6332 53 -2074 57 -6804 51 -166 53 -2254 53 -452 51 -6771 53 -42749 53 -6658 73 -2034 111 -3440 109 -626 53 -7291 85 -2546 53 -5287 51 -9902 53 -10040 53 -366 79 -13848 51 -3739 77 -47536 51 -354 77 -228 77 -712 53 -530 53 -162 55 -12640 71 -1708 51 -1034 77 -12891 79 -3334 51 -7644 79 -12676 81 -3036 53 -2038 53 -180 57 -818 55 -21459 71 -3009 51 -6572 53 -4015 105 -642 77 -23618 53 -4921 83 -3026 73 -3672 53 -12654 81 -8632 51 -9419 171 -19195 105 -13041 55 -21910 51 -1051 77 -10292 51 -12884 51 -6589 53 -8718 51 -2510 103 -15406 55 -6014 99 -966 73 -2725 53 -12715 51 -4228 55 -3192 57 -8672 51 -14740 51 -17032 75 -11111 1761 -182 399 -106 215 -270 355 -132 329 -150 177 -312 315 -170 169 -290 331 -162 173 -286 187 -312 163 -300 329 -164 311 -162 171 -316 289 -176 297 -194 311 -164 143 -324 167 -314 163 -310 161 -312 163 -300 175 -300 331 -162 173 -312 161 -312 163 -314 161 -298 189 -300 165 -312 161 -312 189 -276 173 -316 193 -310 163 -312 161 -310 163 -300 175 -296 193 -284 191 -284 187 -310 163 -300 175 -300 169 -320 177 -314 485 -464 177 -292 353 -134 diff --git a/assets/unit_tests/subghz/hormann_hsm_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/hormann_hsm_raw.sub similarity index 99% rename from assets/unit_tests/subghz/hormann_hsm_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/hormann_hsm_raw.sub index bf82d46cfe1..ac14f5819b5 100644 --- a/assets/unit_tests/subghz/hormann_hsm_raw.sub +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/hormann_hsm_raw.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 868350000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: 1718, -32700, 32700, -32700, 11939, -494, 1047, -496, 1015, -516, 1041, -476, 1037, -510, 1035, -512, 1035, -484, 1047, -492, 1031, -504, 513, -1042, 499, -1020, 1041, -512, 511, -1004, 1035, -512, 507, -1042, 481, -1050, 1017, -522, 489, -1040, 1013, -514, 513, -1010, 543, -1008, 513, -1012, 1045, -522, 1003, -512, 1029, -518, 1009, -514, 1039, -476, 541, -1004, 509, -1038, 1035, -482, 539, -1008, 1033, -490, 539, -1018, 1009, -508, 1059, -482, 1041, -512, 1005, -508, 541, -1006, 513, -1008, 535, -1000, 1035, -522, 525, -1016, 515, -1006, 1021, -514, 1045, -478, 12441, -482, 1045, -512, 1005, -512, 1035, -514, 1037, -482, 1035, -520, 1031, -504, 1035, -486, 1041, -510, 501, -1018, 513, -1042, 1007, -512, 509, -1042, 1007, -506, 541, -1010, 507, -1016, 1031, -520, 513, -1010, 1021, -516, 515, -1046, 477, -1042, 513, -1012, 1051, -484, 1043, -512, 1027, -484, 1043, -514, 1007, -512, 509, -1040, 507, -1036, 1011, -526, 493, -1032, 1033, -486, 509, -1056, 1011, -514, 1007, -512, 1037, -514, 1001, -516, 507, -1034, 517, -1036, 493, -1018, 1041, -502, 525, -1016, 513, -1040, 1005, -512, 1033, -506, 12423, -478, 1039, -514, 1009, -516, 1033, -522, 1001, -522, 1039, -510, 1027, -484, 1041, -512, 1007, -510, 509, -1038, 507, -1034, 1037, -482, 507, -1042, 1037, -520, 487, -1014, 527, -1022, 1007, -512, 509, -1038, 1037, -512, 509, -1008, 507, -1044, 507, -1018, 1027, -514, 1005, -528, 1017, -514, 1041, -512, 1003, -512, 509, -1042, 507, -1010, 1035, -520, 507, -1018, 1033, -508, 511, -1038, 991, -516, 1041, -512, 1005, -512, 1035, -514, 507, -1040, 507, -1014, 519, -1014, 1041, -510, 499, -1018, 515, -1046, 1005, -514, 1011, -520, 12425, -480, 1035, -516, 1035, -492, 1031, -522, 1009, -508, 1021, -518, 1041, -512, 1007, -512, 1037, -512, 509, -1008, 519, -1016, 1029, -522, 513, -1008, 1025, -516, 515, -1044, 479, -1038, 1037, -482, 541, -1008, 1035, -520, 507, -1010, 503, -1026, 515, -1042, 1005, -512, 1037, -512, 1005, -520, 1015, -520, 1039, -510, 499, -1018, 515, -1042, 1007, -512, 507, -1040, 1011, -520, 507, -1016, 1031, -520, 1009, -540, 989, -518, 1043, -512, 511, -1006, 507, -1038, 505, -1036, 1001, -516, 509, -1050, 491, -1052, 1009, -500, 1021, -516, 12409, -524, 1015, -516, 1009, -510, 1037, -512, 1033, -506, 1039, -480, 1035, -524, 1019, -522, 1015, -500, 525, -1014, 515, -1040, 1005, -512, 509, -1042, 1011, -524, 493, -1018, 517, -1036, 1017, -514, 515, -1008, 1035, -514, 507, -1040, 505, -1034, 507, -1010, 1035, -492, 1031, -520, 1009, -540, 991, -516, 1043, -514, 511, -1010, 513, -1044, 1015, -492, 531, -1020, 1009, -528, 521, -1012, 1009, -510, 1037, -512, 1037, -482, 1033, -518, 505, -1032, 507, -1016, 537, -1000, 1025, -520, 513, -1034, 519, -1010, 1007, -512, 1039, -514, 12395, -510, 1037, -512, 1037, -482, 1045, -490, 1057, -488, 1045, -496, 1017, -516, 1041, -514, 1005, -512, 507, -1046, 481, -1044, 1035, -494, 505, -1048, 1011, -502, 527, -1018, 513, -1042, 1005, -516, 513, -1010, 1035, -520, 505, -1032, 507, -1022, 503, -1034, 1013, -504, 1023, -516, 1041, -512, 1005, -512, 1037, -514, 507, -1010, 519, -1050 diff --git a/assets/unit_tests/subghz/ido_117_111_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/ido_117_111_raw.sub similarity index 100% rename from assets/unit_tests/subghz/ido_117_111_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/ido_117_111_raw.sub diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/intertechno_v3.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/intertechno_v3.sub new file mode 100644 index 00000000000..af68b745555 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/intertechno_v3.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Intertechno_V3 +Bit: 32 +Key: 00 00 00 00 3F 86 C5 9F diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/intertechno_v3_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/intertechno_v3_raw.sub new file mode 100644 index 00000000000..2cfc79511cd --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/intertechno_v3_raw.sub @@ -0,0 +1,13 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 15041 -66 15883 -66 12643 -66 12681 -66 3413 -68 2713 -68 33389 -66 1445 -66 1279 -68 1027 -66 6911 -98 25229 -66 3967 -100 3019 -100 6131 -66 955 -66 3605 -66 12411 -98 1419 -66 3593 -68 2753 -66 2457 -66 6007 -66 627 -100 1597 -66 3071 -98 22749 -66 333 -66 12829 -66 4313 -132 855 -66 44097 -64 20391 -98 29999 -66 3539 -98 557 -66 1489 -100 4081 -100 3857 -64 2895 -132 2261 -166 3089 -66 2429 -68 34467 -66 3585 -66 3087 -66 3329 -132 5287 -66 1063 -98 15259 -100 2535 -66 995 -66 13057 -100 24233 -68 531 -100 26415 -66 1761 -100 2717 -66 4071 -100 12191 -66 23367 -68 2323 -66 19809 -248 245 -1388 255 -242 275 -1358 273 -1370 277 -246 277 -1368 275 -246 275 -1362 275 -244 275 -1364 275 -244 275 -1362 275 -244 275 -1328 273 -278 273 -1358 275 -246 275 -238 263 -1384 275 -246 273 -1358 275 -244 273 -1358 275 -246 275 -1360 275 -1344 277 -246 275 -1358 275 -244 275 -234 263 -1382 277 -1344 277 -246 279 -1362 275 -246 271 -234 261 -1380 275 -246 273 -1360 275 -246 275 -1366 277 -1340 277 -248 279 -238 263 -1382 275 -1344 277 -246 279 -1364 277 -244 275 -234 263 -1382 277 -244 273 -1358 275 -1344 277 -248 279 -1368 275 -244 273 -1360 239 -280 271 -1358 275 -244 275 -1358 275 -174 269 -10298 289 -2660 267 -238 299 -1356 275 -244 275 -1356 275 -1344 277 -248 277 -1360 275 -246 275 -1328 309 -244 273 -1358 277 -244 275 -1356 275 -246 273 -1326 309 -244 275 -1356 275 -246 273 -234 263 -1380 277 -246 273 -1326 309 -244 273 -1356 277 -246 277 -1358 275 -1338 279 -248 279 -1364 275 -246 273 -234 261 -1380 277 -1344 279 -250 277 -1330 309 -244 273 -232 261 -1384 275 -246 273 -1356 275 -248 275 -1360 275 -1340 279 -248 277 -236 263 -1380 277 -1342 279 -248 279 -1366 275 -246 273 -234 263 -1380 275 -246 275 -1358 275 -1340 279 -248 281 -1336 309 -244 273 -1358 275 -246 273 -1360 275 -244 273 -1358 275 -176 267 -10306 257 -2646 299 -234 301 -1354 277 -246 275 -1356 277 -1340 279 -250 279 -1332 309 -244 275 -1358 275 -248 273 -1326 309 -246 273 -1326 309 -244 275 -1356 277 -248 275 -1328 309 -246 273 -234 261 -1382 277 -246 277 -1326 309 -244 275 -1358 277 -246 277 -1356 277 -1346 277 -250 277 -1358 277 -246 275 -234 263 -1382 279 -1346 279 -248 281 -1330 307 -246 273 -236 261 -1380 277 -246 277 -1360 277 -246 277 -1360 275 -1344 279 -248 279 -236 263 -1384 277 -1340 279 -250 281 -1338 307 -246 271 -234 261 -1384 277 -246 275 -1356 277 -1340 279 -250 283 -1336 309 -246 273 -1356 277 -246 273 -1360 277 -246 +RAW_Data: 275 -1328 309 -174 269 -10296 289 -2648 267 -238 299 -1356 277 -246 275 -1324 307 -1342 279 -250 277 -1330 309 -244 275 -1362 277 -244 275 -1356 275 -248 273 -1328 309 -244 273 -1328 309 -244 275 -1360 277 -246 275 -234 259 -1384 277 -246 275 -1360 275 -246 273 -1358 277 -248 277 -1362 275 -1344 277 -248 277 -1328 307 -246 273 -236 261 -1384 277 -1348 279 -248 279 -1360 277 -246 273 -234 263 -1388 275 -246 275 -1360 277 -248 279 -1368 277 -1344 279 -248 279 -240 265 -1386 275 -1342 279 -286 247 -1372 275 -248 275 -238 265 -1386 277 -248 275 -1360 275 -1344 277 -286 247 -1374 275 -246 275 -1362 277 -246 275 -1360 277 -248 275 -1326 307 -174 269 -10290 287 -2654 269 -236 301 -1352 275 -248 273 -1326 311 -1340 277 -248 277 -1328 309 -244 273 -1358 275 -244 275 -1326 309 -244 273 -1356 277 -244 273 -1356 275 -246 275 -1358 275 -244 275 -234 261 -1382 277 -246 273 -1358 275 -246 273 -1360 277 -246 273 -1324 309 -1340 277 -248 277 -1328 307 -246 271 -234 259 -1382 277 -1346 279 -248 277 -1330 309 -244 271 -232 259 -1382 277 -244 275 -1356 277 -248 273 -1354 277 -1342 277 -248 275 -236 261 -1380 277 -1344 277 -248 279 -1330 307 -246 273 -234 261 -1378 277 -246 273 -1356 277 -1342 277 -248 277 -1330 309 -244 273 -1322 307 -246 273 -1326 309 -244 273 -1322 309 -176 267 -10298 257 -2682 265 -236 299 -1324 309 -248 273 -1324 311 -1342 277 -246 279 -1360 277 -244 275 -1362 275 -244 275 -1358 275 -244 275 -1360 275 -246 273 -1360 275 -244 277 -1360 275 -246 273 -234 263 -1384 275 -246 273 -1358 275 -246 275 -1360 277 -246 277 -1356 277 -1342 279 -248 277 -1364 275 -244 275 -234 261 -1384 275 -1344 277 -250 279 -1366 275 -246 273 -236 263 -1384 277 -246 275 -1358 277 -246 277 -1362 277 -1342 279 -248 279 -236 265 -1382 277 -1346 277 -248 281 -1366 275 -246 275 -234 265 -1384 275 -246 273 -1358 277 -1344 279 -248 279 -1364 275 -244 275 -1324 309 -246 273 -1324 307 -246 273 -1326 309 -174 267 -118796 133 -100 131 -892 329 -166 199 -132 131 -166 99 -100 265 -264 4663 -134 4889 -100 365 -98 5921 -100 5903 -68 4877 -98 2953 -98 1645 -64 1687 -66 981 -98 10769 -66 18319 -66 4831 -66 13301 -66 893 -132 5967 -100 15949 -66 3749 -66 497 -100 625 -66 1147 -66 469 -66 1261 -66 3651 -100 265 -100 26741 -68 6873 -66 4485 -100 2667 -68 3159 -68 2857 -132 2655 -66 12903 -66 1277 -66 1711 -66 787 -100 1327 -198 727 -64 1677 -100 1187 -66 1019 -66 891 -66 4303 -100 11297 -66 3923 -254 253 -1380 247 -292 253 -1344 +RAW_Data: 277 -1346 277 -250 279 -1364 275 -244 275 -1362 275 -244 275 -1356 275 -246 273 -1358 241 -278 273 -1356 275 -246 273 -1360 275 -246 273 -234 263 -1382 275 -244 273 -1358 275 -246 273 -1360 275 -246 273 -1358 275 -1340 277 -248 277 -1362 275 -246 273 -234 261 -1380 277 -1344 277 -248 279 -1362 275 -244 273 -236 261 -1380 275 -244 275 -1360 275 -246 275 -1358 275 -1346 277 -246 275 -236 263 -1384 275 -1342 277 -248 277 -1364 277 -244 273 -234 261 -1378 277 -246 273 -1356 277 -1340 277 -248 281 -1334 307 -246 271 -1356 275 -246 273 -1358 275 -244 273 -1326 309 -174 267 -10296 257 -2650 297 -232 263 -1384 277 -244 273 -1358 275 -1340 279 -248 279 -1328 309 -244 275 -1328 307 -244 273 -1356 275 -244 275 -1358 275 -246 273 -1324 309 -244 275 -1328 307 -244 273 -234 261 -1382 275 -246 273 -1326 309 -244 273 -1358 275 -246 273 -1358 275 -1338 279 -248 279 -1330 309 -244 273 -232 261 -1380 277 -1344 279 -248 279 -1330 309 -244 271 -234 261 -1382 275 -246 273 -1358 277 -244 275 -1330 309 -1338 277 -246 277 -236 263 -1380 277 -1342 277 -248 279 -1364 275 -246 273 -232 261 -1380 275 -248 275 -1328 307 -1338 277 -248 279 -1334 309 -244 271 -1358 275 -244 275 -1324 307 -246 271 -1328 309 -174 265 -10270 291 -2640 297 -232 297 -1350 277 -248 275 -1326 309 -1340 277 -248 277 -1328 309 -244 273 -1358 275 -246 273 -1326 309 -244 273 -1354 275 -246 273 -1330 307 -244 273 -1358 275 -246 273 -234 263 -1380 275 -246 273 -1358 275 -246 273 -1360 275 -244 273 -1358 275 -1340 277 -248 279 -1364 275 -244 273 -232 261 -1380 277 -1342 279 -250 279 -1332 307 -244 271 -234 261 -1378 277 -246 273 -1358 275 -248 275 -1360 275 -1340 277 -248 275 -236 263 -1382 277 -1344 277 -246 277 -1364 275 -246 273 -234 259 -1380 275 -246 273 -1362 275 -1342 275 -248 277 -1334 309 -244 271 -1356 275 -244 275 -1326 307 -244 273 -1356 275 -176 267 -10290 289 -2644 267 -238 301 -1320 309 -246 273 -1324 309 -1340 277 -248 277 -1328 307 -246 273 -1326 307 -246 273 -1324 309 -246 273 -1322 309 -246 273 -1322 307 -246 275 -1326 309 -246 273 -234 259 -1382 275 -246 275 -1322 309 -246 273 -1326 309 -246 273 -1326 309 -1340 277 -248 275 -1326 309 -246 273 -232 261 -1380 279 -1346 277 -250 277 -1328 309 -244 271 -232 261 -1380 277 -246 273 -1358 275 -248 273 -1328 307 -1340 277 -248 277 -236 261 -1380 277 -1344 277 -248 279 -1328 309 -244 275 -232 261 -1378 277 -248 273 -1326 309 -1344 277 -248 277 -1358 277 -246 273 -1328 307 -244 271 -1324 309 -244 +RAW_Data: 273 -1324 309 -174 267 -10270 289 -2638 297 -234 297 -1352 275 -248 275 -1328 307 -1340 277 -248 275 -1330 309 -244 273 -1358 275 -244 275 -1326 309 -244 271 -1356 275 -244 275 -1326 307 -246 273 -1326 309 -244 273 -234 261 -1378 275 -248 275 -1326 309 -244 271 -1356 277 -248 273 -1328 309 -1338 277 -248 277 -1328 309 -244 271 -232 261 -1380 277 -1348 279 -248 277 -1328 307 -246 271 -234 259 -1384 275 -244 275 -1356 277 -246 275 -1326 309 -1344 275 -248 275 -236 261 -1378 277 -1342 277 -250 279 -1334 309 -244 271 -232 261 -1380 277 -246 273 -1326 307 -1344 277 -248 277 -1328 309 -246 273 -1326 309 -244 271 -1324 309 -244 273 -1324 307 -176 267 -10288 287 -2618 299 -236 299 -1354 277 -244 273 -1326 307 -1340 279 -248 275 -1328 309 -244 275 -1326 309 -246 273 -1324 307 -246 273 -1322 309 -244 273 -1322 309 -244 275 -1328 309 -246 273 -232 261 -1380 277 -246 275 -1324 309 -244 273 -1356 277 -246 275 -1324 309 -1340 279 -246 277 -1328 309 -244 273 -232 261 -1382 277 -1344 279 -250 277 -1324 309 -246 273 -234 261 -1380 277 -246 273 -1358 277 -246 273 -1328 309 -1340 277 -248 275 -236 261 -1380 275 -1344 279 -248 279 -1360 277 -244 273 -234 261 -1380 277 -246 275 -1354 277 -1344 277 -248 277 -1328 311 -246 273 -1324 307 -244 273 -1324 309 -244 273 -1320 309 -176 269 -118210 761 -168 267 -66 563 -132 99 -132 3543 -66 5345 -100 4355 -66 4617 -68 20503 -166 2379 -132 293 -98 4117 -66 1151 -98 3353 -66 3485 -66 2491 -66 6133 -66 233 -68 16307 -68 16959 -98 357 -66 5419 -134 799 -100 327 -100 791 -66 2481 -66 963 -100 3481 -98 1679 -134 2473 -100 227 -68 3087 -66 11527 -130 4305 -98 435 -66 563 -100 2887 -100 267 -66 1787 -66 9655 -66 4793 -100 2119 -66 359 -98 1313 -132 3393 -234 995 -66 2681 -98 99 -130 1379 -100 3757 -100 21695 -132 5135 -100 693 -98 4631 -100 2325 -68 4937 -66 10409 -98 897 -100 1287 -66 2565 -66 3753 -66 4055 -66 2023 -68 1961 -68 629 -66 431 -66 5039 -66 2155 -100 2673 -66 1163 -98 6539 -100 825 -66 1197 -100 3053 -66 13973 -68 15515 -100 1861 -66 1027 -66 797 -98 959 -98 787 -132 787 -64 3811 -132 1747 -66 6683 -66 1033 -68 24927 -66 1259 -100 1125 -68 663 -66 1687 -66 4357 -132 4567 -66 3969 -98 3317 -132 433 -134 6043 -66 3249 -100 431 -98 2367 -100 11265 -66 5085 -68 2355 -64 1815 -66 1395 -274 241 -1366 275 -244 275 -1362 275 -1338 277 -284 243 -1368 239 -278 275 -1362 275 -244 275 -1360 241 -278 273 -1356 275 -246 275 -1360 239 -280 275 -1360 +RAW_Data: 275 -244 275 -234 263 -1386 239 -280 273 -1356 275 -244 273 -1360 275 -244 277 -1364 275 -1336 277 -248 277 -1366 275 -244 273 -234 263 -1386 275 -1340 277 -248 279 -1364 275 -244 275 -234 263 -1384 273 -244 275 -1358 275 -244 275 -1364 275 -1342 275 -248 277 -236 265 -1384 275 -1340 277 -282 243 -1366 275 -246 273 -236 263 -1382 277 -244 275 -1358 275 -1342 277 -248 277 -1364 275 -246 275 -1360 239 -280 273 -1358 241 -278 275 -1356 275 -210 233 -10302 257 -2652 297 -232 297 -1354 277 -244 275 -1358 275 -1340 279 -248 279 -1360 275 -246 275 -1360 275 -246 273 -1360 275 -244 275 -1328 309 -242 273 -1324 309 -244 275 -1360 275 -246 273 -234 261 -1384 275 -246 273 -1358 275 -244 275 -1358 277 -248 273 -1358 275 -1340 279 -248 277 -1334 307 -242 273 -232 261 -1380 277 -1348 277 -250 277 -1364 275 -244 275 -234 261 -1380 277 -244 275 -1358 277 -246 277 -1360 277 -1342 275 -248 275 -236 263 -1380 277 -1344 277 -248 279 -1368 275 -244 275 -232 261 -1382 277 -244 275 -1356 275 -1344 277 -248 279 -1362 275 -246 275 -1360 275 -246 273 -1356 275 -246 273 -1356 275 -176 267 -10302 257 -2648 299 -234 297 -1352 277 -246 275 -1326 309 -1340 279 -248 277 -1330 309 -244 275 -1328 309 -244 273 -1324 309 -244 275 -1324 309 -246 273 -1324 307 -246 275 -1328 309 -244 273 -234 261 -1378 277 -248 275 -1328 309 -244 273 -1356 277 -248 275 -1326 309 -1344 277 -248 275 -1326 309 -246 273 -234 259 -1380 277 -1348 281 -248 279 -1328 307 -246 273 -234 259 -1382 277 -246 275 -1360 275 -248 275 -1324 309 -1340 279 -248 277 -238 261 -1382 277 -1344 277 -248 279 -1330 311 -244 273 -234 259 -1378 277 -248 275 -1326 309 -1340 279 -248 279 -1336 307 -246 271 -1324 309 -244 275 -1324 307 -246 273 -1326 309 -174 269 -10296 257 -2648 299 -234 297 -1352 277 -248 273 -1326 309 -1342 277 -248 277 -1328 309 -246 275 -1328 309 -244 273 -1326 309 -244 273 -1322 309 -244 273 -1328 307 -244 275 -1328 309 -246 273 -234 261 -1382 277 -246 275 -1326 309 -244 273 -1352 277 -248 275 -1330 309 -1340 277 -248 277 -1328 309 -244 275 -232 261 -1384 277 -1342 279 -250 279 -1328 309 -244 273 -234 263 -1380 277 -246 273 -1360 277 -246 275 -1326 309 -1340 277 -250 277 -236 263 -1382 277 -1342 277 -248 279 -1362 277 -246 273 -234 263 -1382 277 -244 275 -1356 277 -1340 279 -248 279 -1362 275 -246 275 -1328 307 -246 273 -1356 275 -246 273 -1356 275 -174 269 -10292 287 -2650 269 -236 301 -1354 275 -248 273 -1358 275 -1340 279 -248 277 -1332 307 -246 275 -1328 +RAW_Data: 309 -244 273 -1324 309 -244 273 -1356 275 -246 273 -1358 275 -244 277 -1330 309 -244 273 -234 261 -1382 277 -244 275 -1358 275 -246 273 -1356 277 -248 275 -1360 275 -1340 277 -248 277 -1360 275 -246 273 -236 261 -1382 279 -1344 279 -248 279 -1360 277 -244 273 -234 261 -1380 277 -246 275 -1360 277 -246 273 -1360 275 -1342 279 -248 275 -236 263 -1382 275 -1344 279 -248 279 -1362 277 -246 273 -234 263 -1380 277 -246 275 -1356 275 -1342 277 -248 281 -1336 307 -246 271 -1354 277 -246 275 -1328 307 -244 273 -1352 277 -176 269 -10300 257 -2650 299 -232 297 -1354 277 -246 275 -1356 277 -1342 277 -248 279 -1328 309 -244 275 -1360 275 -246 273 -1328 307 -246 273 -1356 277 -246 277 -1326 309 -244 277 -1360 277 -246 273 -234 263 -1384 277 -246 275 -1324 309 -246 275 -1358 277 -246 277 -1360 277 -1344 277 -248 277 -1326 309 -246 273 -236 261 -1382 277 -1348 279 -250 281 -1330 307 -246 273 -234 263 -1386 277 -244 275 -1356 277 -248 277 -1362 277 -1342 277 -250 277 -238 263 -1384 277 -1342 277 -250 281 -1332 309 -246 273 -234 263 -1380 277 -246 275 -1360 277 -1342 279 -248 281 -1334 307 -246 273 -1356 275 -248 275 -1328 309 -244 275 -1324 309 -176 269 -115034 163 -362 67 -894 529 -166 14663 -98 4135 -66 3681 -100 299 -68 9829 -66 3517 -64 21569 -66 3251 -66 2209 -64 23701 -66 3359 -68 1057 -66 723 -66 299 -134 765 -66 589 -98 1687 -134 2153 -66 3081 -68 10447 -66 11643 -66 2451 -66 2277 -66 2897 -66 755 -100 5539 -64 5117 -132 4867 -134 3931 -64 625 -66 1317 -98 11597 -66 2255 -66 1165 -66 1123 -66 6371 -100 699 -68 1811 -66 621 -68 2191 -64 1291 -134 3003 -66 2423 -64 1463 -66 663 -100 1127 -100 6169 -100 489 -100 6087 -100 2027 -66 1195 -66 13195 -66 557 -66 40423 -98 1919 -100 1061 -132 201 -66 2553 -132 12549 -66 1789 -100 921 -134 1067 -66 729 -66 10029 -66 3909 -100 265 -100 16017 -134 21177 -68 2461 -66 2215 -68 1197 -66 5911 -66 2645 -66 3419 -132 16275 -64 5091 -68 2123 -66 2677 -64 10305 -66 12381 -100 427 -166 25331 -66 2457 -66 11859 -248 279 -1368 275 -246 275 -1360 275 -1340 277 -246 279 -1364 239 -278 275 -1358 275 -244 275 -1362 239 -278 273 -1358 239 -280 271 -1360 241 -278 273 -1360 275 -244 275 -234 261 -1384 239 -280 273 -1356 275 -244 273 -1360 275 -244 275 -1358 275 -1344 277 -248 275 -1358 275 -244 273 -236 261 -1384 275 -1342 279 -246 279 -1360 275 -244 275 -234 263 -1384 239 -278 273 -1358 275 -244 275 -1362 275 -1342 275 -248 275 -238 263 -1382 275 -1344 275 -248 +RAW_Data: 277 -1364 275 -244 273 -234 263 -1380 275 -246 273 -1358 275 -1342 277 -246 279 -1366 275 -244 273 -1362 239 -278 239 -1386 275 -246 273 -1360 241 -208 269 -10290 257 -2686 265 -232 265 -1384 275 -246 275 -1358 275 -1344 277 -248 275 -1358 275 -246 275 -1360 277 -244 273 -1326 309 -244 271 -1354 275 -244 275 -1358 275 -246 273 -1358 275 -246 273 -234 263 -1378 275 -246 275 -1360 275 -244 273 -1356 275 -246 275 -1360 275 -1342 277 -246 277 -1360 275 -246 273 -232 261 -1382 277 -1342 279 -248 279 -1360 275 -244 275 -232 261 -1380 277 -244 275 -1356 277 -246 277 -1360 275 -1342 277 -246 275 -236 263 -1384 275 -1342 277 -248 277 -1362 275 -246 273 -234 261 -1378 277 -246 275 -1328 307 -1340 277 -246 279 -1366 275 -244 273 -1326 307 -244 273 -1324 309 -244 273 -1356 275 -174 267 -10304 255 -2648 297 -230 263 -1382 277 -244 275 -1330 307 -1338 277 -248 277 -1330 309 -244 273 -1356 275 -246 273 -1362 275 -244 273 -1356 275 -244 273 -1326 307 -244 273 -1360 273 -246 273 -236 261 -1380 275 -244 275 -1328 307 -244 273 -1358 275 -244 275 -1360 275 -1342 277 -246 277 -1364 275 -244 271 -232 261 -1384 277 -1340 279 -248 279 -1360 275 -246 273 -234 261 -1380 275 -244 275 -1360 277 -244 275 -1356 275 -1342 279 -246 277 -236 263 -1382 275 -1340 277 -248 279 -1366 275 -246 271 -234 261 -1382 277 -244 275 -1354 275 -1342 277 -248 277 -1364 273 -246 273 -1362 275 -244 271 -1360 275 -244 273 -1358 275 -174 267 -10272 289 -2646 265 -262 261 -1382 277 -244 275 -1356 275 -1342 277 -248 277 -1364 275 -244 275 -1360 275 -244 273 -1358 275 -244 273 -1358 275 -244 273 -1326 307 -244 275 -1358 275 -246 273 -234 261 -1382 275 -246 273 -1358 275 -244 273 -1358 275 -246 275 -1360 275 -1338 277 -248 277 -1362 277 -244 271 -234 261 -1380 277 -1344 279 -248 277 -1332 273 -278 271 -234 261 -1382 275 -244 275 -1356 277 -246 275 -1360 277 -1340 277 -246 277 -234 263 -1384 275 -1342 277 -248 277 -1366 275 -244 273 -234 261 -1380 275 -246 273 -1360 275 -1340 277 -246 279 -1334 307 -244 273 -1356 275 -246 273 -1360 275 -244 271 -1354 277 -174 269 -10300 257 -2648 297 -230 263 -1384 277 -244 273 -1356 277 -1342 277 -248 277 -1362 275 -244 275 -1330 307 -244 273 -1324 309 -244 273 -1324 307 -246 273 -1326 307 -244 273 -1358 275 -246 273 -234 261 -1380 277 -246 273 -1358 275 -244 275 -1354 277 -248 275 -1360 275 -1338 279 -246 277 -1360 275 -244 273 -234 261 -1378 279 -1344 279 -248 279 -1330 309 -244 271 -232 261 -1380 277 -246 273 -1360 +RAW_Data: 277 -244 275 -1360 275 -1340 277 -246 277 -236 261 -1380 275 -1346 277 -248 277 -1362 275 -246 273 -234 263 -1380 275 -244 275 -1358 275 -1340 277 -248 279 -1334 309 -244 273 -1324 307 -246 273 -1356 275 -244 273 -1356 275 -174 269 -10302 257 -2644 297 -232 263 -1384 277 -246 275 -1354 275 -1344 277 -248 275 -1360 275 -246 275 -1358 275 -246 273 -1326 307 -246 273 -1324 307 -244 273 -1328 307 -244 273 -1358 275 -244 273 -236 261 -1380 275 -246 273 -1358 275 -244 273 -1358 275 -246 273 -1360 275 -1344 275 -248 275 -1360 275 -244 273 -234 261 -1378 277 -1344 279 -248 277 -1362 275 -246 273 -234 261 -1378 275 -244 275 -1360 275 -246 275 -1358 275 -1344 277 -246 277 -234 263 -1380 275 -1338 279 -246 281 -1368 275 -244 271 -234 261 -1386 275 -244 271 -1358 275 -1342 277 -246 279 -1362 275 -244 275 -1326 273 -278 273 -1358 239 -278 273 -1358 275 -174 267 -127478 195 -964 2317 -66 763 -98 1455 -100 16109 -66 5683 -98 11469 -66 34413 -66 5443 -66 11613 -66 2737 -66 12191 -66 2951 -68 1851 -68 1895 -68 2643 diff --git a/assets/unit_tests/subghz/kia_seed_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/kia_seed_raw.sub similarity index 99% rename from assets/unit_tests/subghz/kia_seed_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/kia_seed_raw.sub index b3453536b84..c78e6d34aa0 100644 --- a/assets/unit_tests/subghz/kia_seed_raw.sub +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/kia_seed_raw.sub @@ -1,7 +1,7 @@ Filetype: Flipper SubGhz RAW File Version: 1 Frequency: 433920000 -Preset: FuriHalSubGhzPreset2FSKDev476Async +Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: 51 -5136 1113 -104 127 -606 179 -126 325 -102 147 -200 205 -80 107 -190 81 -54 135 -378 181 -204 199 -104 121 -146 51 -230 75 -76 227 -102 415 -236 159 -54 159 -212 77 -312 73 -74 75 -128 155 -102 125 -358 125 -198 173 -608 75 -248 203 -558 53 -186 367 -180 151 -132 133 -138 135 -424 51 -240 53 -132 703 -80 79 -80 129 -294 77 -102 193 -270 77 -52 131 -180 51 -76 77 -76 51 -666 77 -104 101 -54 77 -164 107 -622 615 -294 133 -138 217 -180 75 -104 185 -80 107 -134 159 -398 181 -74 97 -198 73 -76 99 -102 127 -248 71 -128 211 -106 109 -104 101 -384 311 -398 51 -240 111 -84 81 -110 81 -668 105 -54 77 -158 79 -342 493 -78 153 -152 179 -122 223 -696 139 -590 81 -212 83 -322 129 -132 307 -346 129 -78 77 -232 77 -104 503 -52 187 -290 159 -108 159 -108 51 -136 53 -132 103 -566 177 -326 129 -176 177 -52 355 -308 129 -176 73 -104 151 -262 193 -168 111 -162 247 -52 131 -54 107 -84 83 -218 135 -402 107 -300 105 -220 81 -82 137 -82 163 -238 51 -192 293 -184 319 -454 81 -132 131 -132 79 -866 131 -212 163 -112 133 -106 211 -110 189 -214 51 -410 107 -454 131 -562 157 -52 367 -378 165 -110 81 -134 81 -82 75 -104 217 -638 81 -84 109 -160 81 -160 135 -54 53 -478 239 -78 209 -54 107 -52 155 -238 53 -184 105 -318 105 -370 233 -54 109 -246 217 -110 245 -110 209 -80 129 -514 79 -54 79 -314 155 -156 109 -292 79 -504 51 -644 157 -132 53 -208 53 -76 103 -76 77 -260 105 -82 133 -212 163 -136 53 -380 313 -178 193 -172 207 -82 79 -186 107 -136 53 -136 159 -156 97 -98 99 -254 179 -106 135 -214 307 -310 77 -76 123 -242 129 -52 103 -584 103 -178 203 -324 101 -572 253 -78 125 -126 129 -408 131 -52 129 -76 51 -132 79 -248 79 -414 51 -338 717 -322 79 -180 105 -294 109 -254 241 -76 251 -52 101 -152 125 -260 155 -80 77 -292 51 -226 209 -338 147 -100 51 -366 53 -348 133 -268 79 -366 55 -224 111 -248 81 -218 137 -168 79 -288 291 -132 51 -238 79 -78 103 -102 329 -102 123 -178 53 -202 51 -130 79 -138 51 -126 151 -180 99 -388 75 -126 51 -636 329 -150 99 -152 73 -122 177 -124 151 -386 297 -72 103 -130 51 -256 207 -354 75 -124 71 -148 351 -262 51 -540 77 -200 101 -132 77 -362 77 -254 103 -106 79 -296 51 -130 361 -240 51 -358 51 -284 201 -158 185 -288 183 -424 77 -130 RAW_Data: 121 -146 99 -124 327 -74 99 -148 99 -252 183 -112 161 -216 263 -232 77 -102 103 -416 77 -382 345 -160 131 -110 167 -138 79 -158 133 -54 163 -84 107 -80 107 -108 107 -136 225 -82 321 -108 79 -106 107 -106 55 -162 103 -160 133 -192 53 -84 269 -106 77 -162 53 -136 161 -82 135 -346 77 -864 107 -140 55 -250 189 -128 201 -502 157 -564 105 -52 107 -1150 53 -218 237 -76 159 -1010 129 -196 139 -244 101 -270 77 -106 133 -110 109 -254 501 -496 237 -692 107 -54 159 -134 347 -210 51 -190 55 -276 103 -184 229 -98 149 -252 123 -156 75 -150 537 -236 103 -204 101 -100 73 -368 283 -102 77 -652 229 -206 99 -150 197 -206 53 -80 131 -274 101 -186 159 -396 51 -188 81 -188 83 -350 105 -428 129 -128 79 -78 53 -104 351 -284 235 -130 79 -242 51 -186 77 -134 135 -434 131 -552 53 -136 107 -140 141 -252 53 -54 53 -82 51 -770 79 -130 103 -432 127 -208 153 -226 131 -248 101 -902 125 -206 129 -102 77 -154 77 -188 105 -322 105 -166 133 -474 77 -728 161 -190 107 -54 133 -80 79 -190 53 -162 53 -404 77 -334 105 -52 153 -728 423 -700 133 -476 53 -162 53 -542 159 -168 189 -134 163 -134 81 -106 133 -54 267 -108 163 -242 165 -56 167 -294 81 -136 185 -660 51 -304 157 -54 109 -378 133 -272 159 -304 135 -110 53 -402 105 -630 185 -78 257 -422 173 -252 247 -100 123 -330 107 -166 109 -502 127 -156 219 -76 101 -348 71 -586 149 -154 73 -74 77 -358 131 -270 81 -108 159 -516 295 -346 53 -240 157 -240 55 -562 133 -110 213 -320 103 -560 79 -286 51 -134 55 -452 79 -136 79 -80 77 -52 185 -504 79 -216 79 -242 135 -262 75 -206 125 -472 111 -564 79 -322 79 -432 217 -456 167 -272 103 -664 53 -302 127 -138 241 -184 53 -80 81 -192 395 -604 133 -136 53 -1044 105 -166 215 -426 133 -1222 101 -348 127 -124 123 -248 153 -492 177 -102 103 -180 77 -204 151 -282 101 -74 103 -76 101 -170 73 -506 101 -368 51 -124 193 -236 53 -310 77 -130 155 -366 229 -526 129 -428 83 -922 51 -106 131 -82 219 -706 79 -80 81 -162 133 -276 195 -220 51 -266 81 -54 51 -238 53 -686 77 -354 229 -164 195 -164 81 -294 81 -132 197 -238 73 -202 181 -180 149 -128 207 -192 111 -56 187 -808 77 -608 79 -898 77 -208 97 -200 73 -184 75 -78 203 -664 105 -384 103 -102 129 -238 131 -1346 137 -526 105 -390 129 -138 81 -484 103 -222 109 -268 51 -240 diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/kinggates_stylo4k_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/kinggates_stylo4k_raw.sub new file mode 100644 index 00000000000..49b19000210 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/kinggates_stylo4k_raw.sub @@ -0,0 +1,11 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +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/assets/unit_tests/subghz/linear.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/linear.sub similarity index 86% rename from assets/unit_tests/subghz/linear.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/linear.sub index 19ab5d72213..d5ca8ff9b86 100644 --- a/assets/unit_tests/subghz/linear.sub +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/linear.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz Key File Version: 1 -Frequency: 300000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: Linear Bit: 10 diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/linear_delta3.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/linear_delta3.sub new file mode 100644 index 00000000000..f005074284d --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/linear_delta3.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: LinearDelta3 +Bit: 8 +Key: 00 00 00 00 00 00 00 D0 diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/linear_delta3_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/linear_delta3_raw.sub new file mode 100644 index 00000000000..1973622a535 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/linear_delta3_raw.sub @@ -0,0 +1,8 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +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: -2034 2007 -2038 1969 -2076 1965 -34708 493 -3564 451 -3570 1965 -2074 449 -3548 2003 -2044 1987 -2038 1999 -2030 1991 -34710 493 -3602 403 -3612 1943 -2092 419 -3596 1963 -2062 1963 -2042 2001 -2064 1967 -34716 497 -3616 357 -3648 1903 -2132 399 -3596 1963 -2068 1977 -2052 1967 -2046 2019 -34684 497 -3614 359 -3650 1909 -2100 405 -3630 1925 -2098 1965 -2066 1965 -2056 1971 -34712 477 -3634 371 -3628 1931 -2104 391 -3624 1939 -2066 1975 -2052 2005 -2036 1985 -34714 449 -3668 337 -3664 1901 -2124 417 -3594 1963 -2048 1995 -2028 1993 -2066 1971 -34698 463 -3642 353 -3650 1943 -2066 433 -3594 1963 -2066 1995 -2034 1997 -2046 1981 -34730 479 -3560 445 -3562 1997 -2032 485 -3560 1965 -2062 1989 -2044 1999 -2032 1971 -34724 463 -3608 399 -3620 1943 -2096 421 -3592 1961 -2074 1979 -2036 2011 -2032 1971 -34734 469 -3558 485 -3552 1999 -2028 473 -3552 2003 -2032 2003 -2032 1997 -2044 1993 -34704 443 -3602 431 -3596 1967 -2076 447 -3556 1975 -2058 1997 -2040 1991 -2048 1971 -161100 97 -428 165 -200 395 -428 97 -100 559 -130 97 -164 129 -98 391 -98 295 -166 52395 -66 16239 -66 42541 -66 755 -132 14015 -98 2885 -68 10385 -98 40045 -100 987 -68 25539 -66 19799 -98 136101 -100 5141 -66 5709 -68 23177 -66 11097 -66 329 -100 261 -66 15755 -98 20575 -66 3645 -100 51411 -66 14441 -132 4467 -66 3965 -132 3707 -66 33107 -66 10373 -66 1775 -66 4185 -132 1429 -68 4675 -100 13419 -66 33985 diff --git a/assets/unit_tests/subghz/linear_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/linear_raw.sub similarity index 99% rename from assets/unit_tests/subghz/linear_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/linear_raw.sub index c143fa028a9..5bc6782f5f7 100644 --- a/assets/unit_tests/subghz/linear_raw.sub +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/linear_raw.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 300000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: 361 -4326 2555 -28090 65 -302 65 -1890 131 -2062 2547 -95068 2565 -48020 67 -47036 2551 -30016 2561 -47778 67 -14884 2481 -95118 2537 -95132 2549 -48496 65 -21526 2571 -16342 97 -4142 65 -1938 2543 -95008 2551 -44030 1579 -548 1469 -588 433 -1568 469 -1540 479 -1550 1489 -538 473 -1574 1467 -538 1493 -566 443 -22130 693 -3742 321 -1702 341 -1690 1353 -628 391 -1666 1401 -584 1445 -596 391 -22192 1487 -554 1471 -550 447 -1584 453 -1568 445 -1578 1469 -550 459 -1598 1465 -540 1461 -576 445 -22164 1479 -546 1501 -544 453 -1568 447 -1580 445 -1584 1451 -560 443 -1594 1473 -548 1479 -556 441 -22160 1475 -548 1487 -554 449 -1578 451 -1586 451 -1572 1469 -552 449 -1578 1473 -558 1469 -552 449 -22142 1507 -546 1479 -548 457 -1574 447 -1580 447 -1584 1475 -554 441 -1592 1465 -546 1473 -546 483 -22132 1157 -1082 67 -166 65 -198 65 -1150 257 -1706 339 -1666 365 -1642 1409 -610 407 -1626 1417 -620 1435 -580 409 -22184 1495 -572 1463 -552 447 -1584 453 -1574 447 -1580 1473 -548 459 -1568 1501 -520 1501 -552 463 -22124 535 -7674 339 -1680 1345 -678 361 -1664 1395 -618 1411 -646 373 -22194 1511 -526 1477 -550 483 -1542 479 -1562 471 -1566 1483 -546 445 -1574 1471 -534 1497 -548 483 -169938 2539 -89172 167 -5768 2551 -93014 967 -252 1571 -5844 2503 -87590 65 -5948 2523 -64008 1605 -520 1473 -554 449 -1584 449 -1576 447 -1546 1505 -546 451 -1574 1469 -546 1475 -548 459 -22160 1525 -506 1501 -516 479 -1550 469 -1570 469 -1570 1471 -538 471 -1568 1489 -516 1501 -554 443 -22152 1507 -522 1511 -518 481 -1580 451 -1542 483 -1548 1505 -546 451 -1570 1471 -552 1473 -548 455 -22180 1505 -522 1501 -544 451 -1560 475 -1548 481 -1556 1477 -552 475 -1546 1505 -516 1517 -516 473 -22148 1515 -518 1499 -546 485 -1570 443 -1580 447 -1582 1475 -526 483 -1548 1507 -546 1479 -558 449 -22138 1495 -586 1437 -616 403 -1594 429 -1608 417 -1606 1465 -556 433 -1606 1465 -536 1499 -544 445 -22170 1401 -792 1245 -770 271 -1716 325 -1680 359 -1664 1397 -620 393 -1644 1407 -614 1433 -586 429 -22202 1477 -572 1469 -548 449 -1576 457 -1578 479 -1544 1473 -590 429 -1570 1491 -540 1509 -554 429 -22196 1481 -556 1467 -580 439 -1592 431 -1608 415 -1608 1441 -586 463 -1568 1467 -552 1489 -534 473 -22166 1485 -574 1469 -580 453 -1572 443 -1578 447 -1578 1477 -554 447 -1580 1479 -550 1481 -550 475 -22160 1525 -542 1467 -540 483 -1574 447 -1582 451 -1578 1481 -558 447 -1560 1493 -550 1505 -544 449 -22182 1435 -646 1391 -664 369 -1642 385 -1652 395 -1600 1463 -584 417 -1606 1467 -576 1443 -586 429 -148588 275 -106 847 -128 877 -100 281 -97826 2541 -92318 2505 -12474 2563 -73674 2555 -41600 2543 -76554 1601 -516 1471 -554 diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/magellan.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/magellan.sub new file mode 100644 index 00000000000..11684803dff --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/magellan.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Magellan +Bit: 32 +Key: 00 00 00 00 37 AE 48 28 diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/magellan_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/magellan_raw.sub new file mode 100644 index 00000000000..0d70576bab5 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/magellan_raw.sub @@ -0,0 +1,8 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 29262 361 -68 2635 -66 24113 -66 1131 -100 4157 -66 26253 -130 621 -18438 99 -298 231 -66 197 -496 753 -230 7503 -16526 65 -396 65 -296 99 -196 293 -64 429 -132 397 -66 329 -66 37701 -66 13475 -100 54967 -64 18209 -18340 97 -462 197 -98 587 -232 97 -100 259 -98 197 -262 297 -64 557 -100 599 -100 333 -234 42493 -13212 6449 -206 173 -214 217 -176 195 -218 181 -218 181 -182 217 -182 217 -176 187 -214 215 -180 217 -182 217 -182 217 -178 185 -424 1177 -388 387 -240 381 -214 181 -398 211 -380 419 -176 217 -394 203 -394 205 -380 189 -402 421 -168 219 -398 393 -190 191 -398 205 -406 185 -402 381 -212 215 -362 241 -378 421 -176 377 -218 197 -378 427 -210 393 -172 429 -172 397 -212 217 -362 389 -228 197 -372 417 -204 395 -210 181 -398 391 -192 201 -216888 761 -200 299 -166 695 -132 15435 -66 5611 -66 21049 -66 4947 -66 2355 -66 1921 -100 2223 -100 2107 -100 397 -98 3643 -66 5301 -98 14205 -66 37371 -246 175 -216 179 -216 177 -224 149 -246 159 -228 181 -212 201 -204 159 -244 151 -254 169 -214 181 -210 197 -182 181 -454 1141 -444 357 -228 361 -246 177 -396 209 -412 367 -188 187 -434 201 -394 185 -406 193 -402 377 -238 181 -386 381 -234 153 -424 205 -412 157 -412 383 -240 181 -398 203 -392 385 -236 371 -212 179 -400 383 -240 359 -210 375 -220 381 -246 175 -394 383 -240 181 -398 363 -222 379 -246 175 -394 383 -204 217 -182856 99 -66 99 -300 133 -402 65 -198 99 -328 65 -100 491 -164 593 -100 3547 -64 361 -66 789 -68 2521 -66 22883 -66 2659 -98 3309 -130 3789 -100 9689 -17178 99 -1388 65 -266 197 -100 131 -134 99 -232 627 -130 233 -66 1949 -100 14567 -198 165 -256 181 -208 159 -214 183 -220 163 -244 149 -246 159 -236 181 -254 141 -226 151 -246 157 -228 181 -212 201 -400 1163 -428 379 -230 355 -244 177 -396 207 -412 367 -222 157 -418 189 -410 207 -412 171 -430 357 -226 165 -404 413 -204 181 -428 173 -428 169 -426 353 -236 173 -414 173 -408 381 -244 337 -222 201 -408 397 -208 393 -204 395 -208 359 -246 177 -394 387 -200 205 -380 415 -202 395 -208 181 -432 357 -226 169 -195084 65 -300 763 -66 297 -364 593 -68 2883 -66 1357 -68 363 -98 3841 -66 3119 -66 5153 -66 4023 -268 143 -246 133 -290 141 -250 139 -254 141 -226 181 -248 137 -254 143 -252 139 -252 143 -230 181 -250 139 -254 145 -436 1135 -448 349 -240 347 -254 157 -434 167 -426 377 -226 157 -434 167 -426 155 -440 163 -434 375 -206 215 -380 381 -234 153 +RAW_Data: -424 205 -412 159 -412 381 -240 181 -398 203 -392 387 -236 369 -212 179 -400 383 -240 359 -244 339 -222 381 -246 175 -394 383 -240 181 -398 363 -222 381 -244 175 -392 383 -240 181 -184002 99 -360 63 -330 65 -132 129 -232 97 -198 295 -328 6031 -66 831 -132 3417 -66 2187 -64 2183 -100 6535 -66 1127 -66 2569 -66 2031 -66 2271 -66 2183 -66 3815 -66 3803 -66 493 -66 1909 -66 1627 -98 4805 -17512 67 -2164 131 -498 265 -430 163 -98 97 -64 99 -230 99 -100 229 -230 165 -196 63 -132 99 -66 927 -66 14955 -66 19621 -68 2627 -66 14305 -68 23247 -66 2891 -66 3941 -66 3021 -212 173 -242 181 -218 181 -214 181 -208 157 -250 141 -248 181 -218 179 -214 179 -210 159 -250 179 -214 181 -218 181 -404 1153 -404 389 -244 375 -192 181 -436 161 -414 383 -240 181 -398 205 -392 201 -394 205 -394 365 -246 177 -396 383 -204 217 -398 171 -426 167 -428 353 -242 173 -420 173 -408 373 -220 403 -208 175 -422 381 -194 399 -228 357 -246 355 -210 215 -400 387 -208 181 -398 391 -226 353 -246 177 -398 383 -204 217 -185098 163 -166 525 -98 293 -100 63 -66 229 -66 1183 -66 1507 -66 3089 -98 30187 -66 2847 -19112 133 -364 131 -394 97 -166 295 -66 229 -164 227 -66 263 -130 623 -98 2071 -66 493 -66 787 -98 691 -64 10249 -132 3879 -66 1949 -66 3453 -198 23157 -66 2845 -100 1193 -66 1587 -100 3797 -98 3187 -100 3319 -66 22119 -98 5513 -226 155 -244 153 -256 131 -248 151 -246 159 -262 121 -274 133 -272 127 -244 153 -254 167 -248 145 -244 133 -252 177 -398 1169 -418 381 -238 359 -242 141 -430 169 -426 357 -274 139 -422 171 -442 173 -428 167 -426 353 -236 171 -416 379 -226 149 -436 161 -438 173 -406 381 -234 153 -424 205 -380 389 -244 359 -206 215 -384 381 -246 335 -224 383 -246 355 -244 179 -404 385 -206 181 -432 359 -226 355 -246 175 -398 383 -240 181 -179760 97 -168 727 -66 97 -332 1389 -66 2793 -66 4955 -100 12453 -100 2425 -66 21965 -66 3809 -68 1683 -66 3095 -66 2153 -64 999 -208 173 -220 181 -214 191 -196 181 -212 183 -220 191 -212 181 -214 191 -198 181 -212 181 -222 191 -212 181 -214 191 -416 1167 -424 369 -220 373 -210 209 -390 207 -376 403 -190 187 -418 189 -408 209 -412 173 -428 357 -226 169 -404 399 -208 179 -412 209 -396 169 -428 355 -230 201 -378 205 -406 381 -244 339 -222 193 -400 413 -204 393 -208 347 -220 401 -210 175 -422 383 -202 217 -398 365 -222 377 -246 175 -390 385 -204 217 -179890 165 -1552 131 -164 65 +RAW_Data: -1448 361 -17056 131 -134 233 -1462 131 -166 953 -100 261 -164 5077 -272 137 -268 143 -252 141 -248 143 -246 159 -252 141 -244 143 -290 107 -276 145 -244 131 -250 179 -248 143 -252 141 -414 1165 -424 373 -236 359 -242 145 -434 169 -428 355 -230 169 -442 173 -434 157 -406 193 -402 379 -238 181 -422 335 -252 157 -434 167 -428 185 -406 381 -208 211 -390 207 -410 381 -200 373 -236 171 -414 383 -202 393 -210 379 -220 373 -208 211 -390 383 -204 217 -398 365 -220 379 -244 175 -394 381 -240 181 -161030 97 -166 167 -930 593 -2670 1091 -132 229 -98 461 -164 1649 -66 6311 -100 44723 -16832 67 -2656 131 -132 99 -132 263 -100 399 -68 893 -18950 99 -164 165 -198 525 -998 335 -66 565 -66 1057 -17880 97 -360 195 -262 131 -332 625 -98 197 -230 455 -98 9343 -16498 67 -368 131 -598 65 -1066 333 -300 789 -130 757 -66 87207 -16554 97 -3520 97 -786 591 -64 461 -98 21495 -66 24811 -18448 131 -296 491 -134 163 -760 1091 -230 893 -66 927 -68 4581 -68 32965 -64 45217 -17292 131 -1684 231 -132 327 -64 163 -330 263 -230 25751 diff --git a/assets/unit_tests/subghz/marantec.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/marantec.sub similarity index 100% rename from assets/unit_tests/subghz/marantec.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/marantec.sub diff --git a/assets/unit_tests/subghz/marantec_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/marantec_raw.sub similarity index 100% rename from assets/unit_tests/subghz/marantec_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/marantec_raw.sub diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/mastercode.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/mastercode.sub new file mode 100644 index 00000000000..f50abbfe8f5 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/mastercode.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok270Async +Protocol: Mastercode +Bit: 36 +Key: 00 00 00 0B 7E 00 3C 08 diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/mastercode_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/mastercode_raw.sub new file mode 100644 index 00000000000..69d3f396cdb --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/mastercode_raw.sub @@ -0,0 +1,6 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok270Async +Protocol: RAW +RAW_Data: 10389 -66 405095 -102 207 -106 1165 -130 963739 -1232 899 -2250 2003 -1190 2017 -1202 911 -2256 2021 -1162 2045 -1134 2047 -1164 2047 -1138 2031 -1180 2039 -1182 949 -2190 995 -2214 961 -2228 963 -2198 963 -2214 977 -2212 975 -2210 975 -2208 971 -2200 963 -2210 993 -2184 2075 -1130 2051 -1142 2055 -1136 2047 -1178 965 -2236 933 -2220 975 -2184 999 -2222 967 -2208 969 -2214 979 -2202 2027 -1156 975 -2242 943 -16080 2023 -1162 967 -2220 2057 -1114 2061 -1124 1007 -2242 2025 -1134 2055 -1168 2017 -1138 2075 -1134 2053 -1136 2075 -1130 979 -2214 979 -2174 999 -2182 1001 -2204 977 -2206 1003 -2188 979 -2176 999 -2182 1009 -2176 1009 -2176 1001 -2212 2029 -1116 2091 -1102 2109 -1092 2095 -1126 1001 -2150 1011 -2180 1011 -2180 1009 -2178 1009 -2172 1009 -2166 1001 -2198 2065 -1136 975 -2220 971 -16018 2097 -1166 951 -2240 2009 -1186 2011 -1160 979 -2208 2035 -1134 2053 -1138 2061 -1158 2045 -1152 2029 -1152 2051 -1166 963 -2188 993 -2222 951 -2214 963 -2220 965 -2212 979 -2212 977 -2180 1003 -2202 965 -2218 975 -2216 967 -2188 2061 -1124 2083 -1126 2071 -1130 2059 -1134 993 -2188 979 -2240 947 -2204 979 -2214 971 -2214 973 -2210 971 -2206 2053 -1130 979 -2216 969 -16056 2053 -1134 1001 -2224 2021 -1150 2051 -1154 953 -2240 2045 -1146 2023 -1168 2033 -1144 2065 -1146 2055 -1130 2071 -1160 961 -2192 973 -2190 1005 -2214 975 -2206 967 -2206 975 -2206 967 -2208 975 -2212 967 -2212 979 -2218 977 -2178 2063 -1156 2035 -1160 2061 -1126 2065 -1130 981 -2186 1003 -2210 977 -2208 973 -2202 977 -2200 965 -2248 943 -2206 2039 -1190 941 -48536 65 -7254 263 -68 363 -102 131 -232 263 -264 751 -230 225 -822 397 -634 231 -268 263 -134 267 -64 867 -132 305 -138 67 -100 331 -98 891 -66 455 -66 531 -100 299 -134 897 -98 693 -132 291 -132 333 -98 337 -68 331 diff --git a/assets/unit_tests/subghz/megacode.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/megacode.sub similarity index 86% rename from assets/unit_tests/subghz/megacode.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/megacode.sub index e968fd9bd1d..787e84b0dfd 100644 --- a/assets/unit_tests/subghz/megacode.sub +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/megacode.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz Key File Version: 1 -Frequency: 318000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: MegaCode Bit: 24 diff --git a/assets/unit_tests/subghz/megacode_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/megacode_raw.sub similarity index 99% rename from assets/unit_tests/subghz/megacode_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/megacode_raw.sub index c2235d8c52f..8a061cdfd8e 100644 --- a/assets/unit_tests/subghz/megacode_raw.sub +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/megacode_raw.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 318000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: -754 361 -17246 131 -8734 65 -71908 65 -27774 65 -1230 65 -13826 99 -800 65 -634 67 -796 99 -47716 65 -18338 67 -18176 131 -7986 65 -1084 131 -2090 65 -48694 163 -40926 65 -4538 65 -10224 65 -9874 20955 -1970 1023 -4928 1007 -4946 1011 -7910 981 -1992 1021 -7898 1013 -1962 1007 -7896 1019 -4930 995 -4962 987 -1974 1029 -4924 1019 -4920 1025 -7896 1005 -1964 1027 -7894 1007 -4932 1033 -1948 1009 -7916 1009 -1982 1001 -4942 1011 -7880 1011 -2000 1003 -13820 1011 -1990 985 -4944 1021 -4932 995 -7916 1025 -1944 1025 -7892 1015 -1970 1025 -7892 1001 -4934 1031 -4926 1015 -1970 989 -4938 1007 -4954 1019 -7874 1013 -2000 985 -7896 1011 -4950 1001 -1968 1023 -7900 999 -1968 1007 -4968 973 -7936 985 -1970 1009 -13856 1013 -1968 981 -4962 1009 -4926 1005 -7924 1003 -1968 1013 -7892 1003 -1992 985 -7904 1011 -4936 1023 -4948 997 -1960 1015 -4950 987 -4970 993 -7896 1005 -1970 1009 -7928 1005 -4914 1023 -1984 1001 -7894 1011 -1970 999 -4962 1003 -7890 1007 -1992 1013 -13810 1011 -1994 987 -4938 1005 -4964 999 -7904 1013 -1974 987 -7910 1021 -1964 1015 -7894 1021 -4912 1019 -4948 1007 -1970 1017 -4916 1021 -4946 985 -7914 1003 -1972 1009 -7898 1041 -4912 1013 -1994 985 -7892 1027 -1964 1013 -4938 1003 -7904 1023 -1966 1017 -13824 1025 -1968 983 -4950 1007 -4944 999 -7900 1029 -1964 1015 -7914 981 -2000 981 -7932 979 -4936 1021 -4926 1013 -1998 979 -4946 1035 -4908 1031 -7896 999 -1964 1025 -7902 1021 -4914 1023 -1966 1015 -7908 1007 -1970 1003 -4952 981 -7910 1001 -1974 1003 -13832 1015 -1976 999 -4958 1019 -4938 985 -7900 1025 -1966 1011 -7914 1015 -1964 1009 -7906 1017 -4908 1027 -4916 1011 -2000 981 -4954 1007 -4926 1017 -7920 1011 -1958 1009 -7906 1019 -4912 1025 -1980 1009 -7900 1013 -1948 1027 -4916 1011 -7918 1005 -1976 1003 -13846 1013 -1970 1005 -4914 1041 -4916 1007 -7918 987 -2000 987 -7902 1013 -1970 1015 -7894 1007 -4936 1019 -4944 1009 -1970 1013 -4918 1019 -4954 977 -7914 1019 -1964 1007 -7906 1003 -4956 983 -1982 1027 -7896 1005 -1962 1027 -4930 1007 -7906 999 -1972 1009 -13850 1009 -1952 1027 -4944 983 -4954 1023 -7894 1007 -1952 1031 -7882 1035 -1970 1011 -7894 1007 -4936 1025 -4920 1023 -1968 1011 -4928 1009 -4920 1015 -7902 1015 -1966 1021 -7894 1011 -4948 997 -1970 1009 -7902 1027 -1978 1011 -4930 999 -7918 1007 -1966 1013 -13824 1015 -1966 1027 -4918 1027 -4912 1021 -7888 1009 -1998 989 -7918 1019 -1960 989 -7930 983 -4946 1025 -4920 1013 -1970 1015 -4950 1009 -4926 1005 -7892 1037 -1942 1011 -7926 1005 -4918 1007 -1996 985 -7928 1011 -1964 993 -4946 1025 -7880 1003 -1972 1009 -13854 1011 -1948 1019 -4956 983 -4946 1025 -7898 1003 -1966 1025 -7900 1019 -1964 1011 -7890 1021 -4912 1017 -4930 1013 -1968 1015 -4952 1009 -4932 1007 -7920 1009 -1972 979 -7924 1009 -4944 1003 -1970 1015 -7894 1017 diff --git a/assets/unit_tests/subghz/nero_radio_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/nero_radio_raw.sub similarity index 99% rename from assets/unit_tests/subghz/nero_radio_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/nero_radio_raw.sub index 265ed20ad6f..b1da069d173 100644 --- a/assets/unit_tests/subghz/nero_radio_raw.sub +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/nero_radio_raw.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 434420000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: 1487 -32700 235 -202 229 -218 217 -176 197 -220 217 -210 191 -218 217 -210 189 -218 217 -210 187 -212 181 -230 203 -194 211 -212 213 -226 197 -188 211 -212 213 -226 197 -186 211 -212 213 -228 195 -186 211 -212 239 -172 235 -218 181 -212 193 -218 219 -208 191 -212 211 -200 205 -224 181 -212 213 -228 197 -188 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 213 -226 197 -186 211 -212 237 -174 235 -218 789 -244 205 -384 415 -230 185 -418 419 -226 179 -436 419 -190 435 -198 415 -210 215 -406 187 -436 421 -190 213 -438 193 -404 413 -224 185 -430 411 -238 181 -432 201 -428 199 -404 207 -420 205 -426 385 -244 213 -400 411 -192 221 -400 239 -412 205 -426 205 -394 203 -412 243 -390 203 -426 417 -214 215 -402 211 -428 203 -392 205 -406 231 -396 411 -232 203 -408 207 -424 393 -232 203 -408 209 -422 395 -232 397 -244 391 -202 219 -430 389 -238 405 -208 425 -206 217 -394 389 -238 207 -388 243 -382 411 -244 383 -1234 219 -406 225 -198 255 -182 175 -204 211 -212 201 -208 227 -182 211 -214 227 -198 187 -212 211 -214 225 -198 185 -212 211 -240 171 -230 181 -212 213 -226 199 -188 211 -210 213 -228 195 -188 211 -212 213 -226 197 -186 211 -212 213 -228 195 -186 211 -212 213 -228 195 -188 209 -212 239 -172 227 -182 211 -212 227 -198 189 -212 211 -214 225 -198 187 -212 241 -182 227 -198 185 -212 211 -214 227 -196 185 -212 211 -240 173 -234 219 -182 209 -194 833 -210 213 -410 409 -232 199 -406 415 -204 215 -428 387 -244 391 -204 425 -206 215 -428 205 -382 419 -226 181 -434 195 -440 413 -192 219 -400 447 -202 217 -396 203 -418 189 -438 183 -436 195 -408 443 -194 217 -400 445 -204 217 -396 201 -430 199 -404 209 -422 205 -426 205 -412 193 -420 417 -202 223 -402 239 -408 207 -392 237 -382 223 -412 409 -226 191 -430 209 -416 395 -232 197 -404 209 -422 395 -232 397 -242 393 -202 217 -432 389 -238 407 -206 393 -238 217 -396 387 -238 209 -386 243 -388 411 -240 407 -4594 195 -408 245 -214 173 -218 211 -182 231 -204 223 -180 211 -214 227 -198 187 -212 211 -214 225 -198 185 -212 211 -214 227 -196 185 -212 211 -214 227 -196 185 -212 211 -240 173 -234 217 -182 211 -194 219 -218 207 -192 211 -212 199 -206 223 -182 211 -214 227 -198 187 -212 211 -240 171 -236 217 -182 211 -194 217 -220 207 -192 211 -212 201 -206 193 -212 241 -182 227 -200 187 -212 209 -240 173 -228 diff --git a/assets/unit_tests/subghz/nero_sketch_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/nero_sketch_raw.sub similarity index 99% rename from assets/unit_tests/subghz/nero_sketch_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/nero_sketch_raw.sub index 6660b5c4208..4ac213366d7 100644 --- a/assets/unit_tests/subghz/nero_sketch_raw.sub +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/nero_sketch_raw.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 315000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: -2704 519 -368 279 -698 297 -712 259 -716 309 -688 647 -356 289 -710 615 -384 275 -692 345 -654 321 -696 619 -376 317 -658 335 -652 325 -684 655 -336 317 -698 295 -682 325 -678 639 -376 319 -662 331 -652 677 -352 631 -326 677 -326 315 -682 323 -680 317 -670 315 -702 637 -362 315 -654 679 -352 315 -658 315 -690 321 -676 647 -346 317 -660 337 -688 289 -686 655 -338 1007 -334 317 -350 301 -344 343 -350 315 -334 325 -352 313 -334 313 -346 349 -316 333 -326 351 -314 333 -348 315 -350 301 -344 343 -352 315 -302 353 -352 313 -334 313 -346 315 -350 301 -358 351 -314 333 -316 345 -350 303 -344 343 -352 313 -334 323 -350 315 -334 313 -344 349 -304 343 -340 317 -350 301 -356 315 -350 301 -346 345 -350 301 -344 343 -352 315 -302 353 -352 313 -334 313 -346 315 -350 333 -326 353 -314 333 -314 347 -350 1315 -320 681 -346 297 -680 317 -672 315 -700 311 -672 649 -354 323 -684 643 -352 313 -658 347 -654 323 -664 651 -376 317 -660 333 -652 323 -684 655 -338 317 -688 321 -676 293 -704 639 -338 353 -662 299 -684 647 -350 667 -326 677 -326 313 -676 323 -680 317 -670 315 -700 639 -362 315 -688 647 -352 313 -658 351 -662 297 -682 647 -350 349 -656 311 -688 321 -660 653 -342 1007 -334 317 -350 303 -344 343 -350 317 -332 325 -352 315 -336 311 -346 349 -316 333 -328 351 -316 303 -344 345 -350 303 -342 343 -316 351 -300 355 -350 315 -334 313 -344 315 -352 331 -328 351 -314 333 -316 345 -350 301 -344 341 -352 315 -332 323 -352 315 -334 313 -344 349 -316 333 -328 351 -316 333 -314 347 -350 301 -344 341 -352 315 -334 323 -352 315 -336 311 -346 313 -352 331 -328 351 -316 303 -344 345 -350 303 -342 343 -352 1281 -352 645 -346 333 -678 315 -672 313 -702 311 -672 643 -354 325 -676 645 -352 313 -690 315 -654 323 -704 649 -344 317 -660 335 -652 323 -684 671 -346 317 -660 335 -684 291 -682 671 -346 317 -660 335 -652 677 -352 627 -364 645 -324 351 -648 323 -682 317 -674 347 -656 643 -360 313 -676 649 -350 315 -692 307 -688 287 -690 653 -338 353 -664 297 -678 319 -684 655 -338 983 -362 315 -350 303 -342 341 -352 315 -334 323 -352 313 -334 313 -346 313 -352 333 -326 351 -316 303 -344 345 -350 303 -342 343 -316 351 -300 355 -352 313 -334 313 -346 313 -352 301 -358 351 -314 305 -344 345 -350 301 -342 343 -352 315 -302 353 -352 315 -334 313 -344 349 -304 341 -342 317 -350 301 -354 317 -350 301 -344 345 -350 315 diff --git a/assets/unit_tests/subghz/nice_flo.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/nice_flo.sub similarity index 100% rename from assets/unit_tests/subghz/nice_flo.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/nice_flo.sub diff --git a/assets/unit_tests/subghz/nice_flo_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/nice_flo_raw.sub similarity index 100% rename from assets/unit_tests/subghz/nice_flo_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/nice_flo_raw.sub diff --git a/assets/unit_tests/subghz/nice_flor_s_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/nice_flor_s_raw.sub similarity index 100% rename from assets/unit_tests/subghz/nice_flor_s_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/nice_flor_s_raw.sub diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/nice_one_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/nice_one_raw.sub new file mode 100644 index 00000000000..169b3f08899 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/nice_one_raw.sub @@ -0,0 +1,12 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +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 diff --git a/assets/unit_tests/subghz/phoenix_v2.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/phoenix_v2.sub similarity index 100% rename from assets/unit_tests/subghz/phoenix_v2.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/phoenix_v2.sub diff --git a/assets/unit_tests/subghz/phoenix_v2_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/phoenix_v2_raw.sub similarity index 100% rename from assets/unit_tests/subghz/phoenix_v2_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/phoenix_v2_raw.sub diff --git a/assets/unit_tests/subghz/power_smart.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/power_smart.sub similarity index 87% rename from assets/unit_tests/subghz/power_smart.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/power_smart.sub index 445458b8c00..5dd922bdafd 100644 --- a/assets/unit_tests/subghz/power_smart.sub +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/power_smart.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz Key File Version: 1 -Frequency: 433420000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: Power Smart Bit: 64 diff --git a/assets/unit_tests/subghz/power_smart_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/power_smart_raw.sub similarity index 98% rename from assets/unit_tests/subghz/power_smart_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/power_smart_raw.sub index 0aafc87cab8..c52d30d61cc 100644 --- a/assets/unit_tests/subghz/power_smart_raw.sub +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/power_smart_raw.sub @@ -1,7 +1,7 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 433420000 -Preset: FuriHalSubGhzPresetOok270Async +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: -68 521674 -200 213 -258 217 -218 217 -246 193 -242 211 -466 451 -450 225 -212 211 -242 211 -238 243 -200 223 -254 419 -226 221 -432 251 -212 467 -240 203 -450 449 -246 205 -238 217 -218 217 -444 439 -478 431 -230 211 -462 247 -214 435 -454 449 -456 441 -442 471 -448 449 -246 205 -238 215 -220 217 -244 201 -254 217 -430 235 -220 461 -198 223 -442 275 -212 429 -458 245 -212 203 -228 253 -218 431 -448 451 -442 235 -218 463 -426 485 -214 209 -234 221 -254 219 -214 207 -228 253 -428 459 -418 245 -216 243 -194 241 -212 241 -212 211 -244 441 -228 215 -460 217 -244 435 -236 235 -440 439 -246 213 -208 229 -256 217 -430 445 -450 445 -236 253 -420 225 -232 429 -450 465 -448 439 -448 449 -454 453 -230 225 -214 241 -212 211 -242 211 -244 237 -442 237 -218 429 -230 221 -442 241 -246 425 -458 245 -212 203 -228 255 -218 427 -448 451 -446 235 -256 425 -462 417 -244 251 -208 201 -256 217 -218 209 -232 255 -420 451 -448 217 -242 211 -238 209 -234 221 -256 217 -218 437 -238 217 -464 203 -256 419 -226 223 -464 441 -244 213 -240 197 -254 219 -430 445 -452 479 -204 253 -420 225 -222 463 -452 449 -450 449 -452 447 -450 451 -238 215 -220 217 -244 199 -256 217 -218 207 -440 241 -254 423 -238 217 -462 197 -224 471 -448 225 -220 213 -242 211 -212 465 -446 449 -456 245 -214 437 -456 449 -232 233 -216 219 -254 207 -204 253 -218 219 -440 451 -456 237 -216 217 -254 209 -202 253 -218 219 -210 437 -244 253 -422 237 -218 423 -226 247 -430 451 -242 203 -228 253 -220 217 -442 449 -454 449 -240 217 -430 237 -218 461 -426 453 -450 465 -448 451 -440 443 -246 215 -250 201 -224 255 -218 215 -208 227 -442 239 -208 457 -238 219 -464 201 -256 425 -464 201 -256 217 -210 241 -216 451 -426 445 -486 207 -236 441 -430 479 -194 223 -256 217 -218 209 -234 253 -218 217 -438 449 -454 239 -216 217 -256 207 -202 255 -218 217 -210 439 -244 253 -424 235 -218 457 -224 229 -436 449 -230 221 -214 211 -242 211 -468 447 -450 449 -250 207 -454 239 -218 431 -446 451 -448 443 -486 447 -448 431 -240 207 -232 223 -254 219 -216 209 -230 253 -426 233 -234 433 -244 213 -432 237 -218 463 -446 221 -212 211 -242 239 -208 457 -456 451 -456 223 -212 467 -450 453 -204 253 -218 211 -240 215 -218 255 -208 201 -442 479 -452 203 -254 217 -212 241 -216 217 -256 207 -202 439 -240 245 -430 237 -218 463 -240 217 -428 465 -202 253 -218 213 -234 219 RAW_Data: -444 443 -452 455 -244 213 -440 237 -254 427 -450 429 -480 417 -454 477 -430 445 -244 217 -250 203 -224 253 -220 215 -206 221 -462 217 -212 467 -234 231 -440 239 -220 427 -446 219 -242 211 -244 237 -206 451 -458 451 -458 203 -254 427 -450 441 -246 213 -242 193 -244 211 -242 211 -212 241 -442 451 -454 245 -214 241 -198 255 -218 217 -210 225 -212 461 -246 213 -440 225 -242 435 -240 209 -456 457 -246 211 -204 229 -254 219 -428 445 -452 445 -238 253 -426 231 -232 435 -448 455 -448 439 -446 449 -456 451 -230 223 -214 241 -212 211 -242 211 -244 237 -442 237 -218 427 -232 221 -442 239 -246 427 -458 245 -212 205 -228 253 -220 427 -446 471 -442 237 -216 451 -462 411 -244 251 -216 205 -222 211 -242 211 -242 211 -480 453 -418 245 -252 207 -202 253 -218 219 -210 229 -256 425 -232 221 -430 251 -212 469 -204 235 -446 441 -246 213 -242 193 -244 211 -464 451 -456 441 -248 215 -450 201 -256 427 -452 457 -450 455 -450 439 -446 451 -238 217 -256 207 -202 255 -218 217 -210 231 -442 237 -244 423 -240 217 -464 203 -254 419 -454 227 -210 243 -212 241 -212 477 -418 453 -484 199 -226 431 -267004 97 -422 97 -164 65 -490 97 -2222 559 -66 163 -64 295 -100 497 -100 263 -98 361 -460 793 -66 1803 -100 339 -68 1733 diff --git a/assets/unit_tests/subghz/princeton.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/princeton.sub similarity index 100% rename from assets/unit_tests/subghz/princeton.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/princeton.sub diff --git a/assets/unit_tests/subghz/princeton_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/princeton_raw.sub similarity index 100% rename from assets/unit_tests/subghz/princeton_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/princeton_raw.sub diff --git a/assets/unit_tests/subghz/scher_khan_magic_code.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/scher_khan_magic_code.sub similarity index 99% rename from assets/unit_tests/subghz/scher_khan_magic_code.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/scher_khan_magic_code.sub index 81467b0a74f..19d6c816016 100644 --- a/assets/unit_tests/subghz/scher_khan_magic_code.sub +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/scher_khan_magic_code.sub @@ -1,7 +1,7 @@ Filetype: Flipper SubGhz RAW File Version: 1 Frequency: 433920000 -Preset: FuriHalSubGhzPreset2FSKDev238Async +Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: 95 -240 191 -412 187 -152 95 -120 263 -220 373 -116 199 -88 239 -334 503 -142 167 -174 85 -172 143 -86 85 -96 119 -196 259 -96 167 -148 201 -96 481 -120 237 -264 143 -144 105 -790 257 -88 243 -144 143 -86 553 -216 103 -174 143 -142 119 -144 119 -224 307 -144 239 -120 127 -390 189 -168 119 -312 191 -142 215 -96 103 -86 307 -274 191 -104 143 -96 167 -120 357 -390 651 -218 197 -170 143 -164 85 -210 143 -116 143 -86 255 -160 95 -114 199 -88 545 -288 173 -114 115 -202 85 -316 145 -144 113 -562 105 -254 85 -144 287 -116 115 -202 87 -346 287 -244 103 -370 143 -230 143 -144 85 -88 113 -230 231 -984 119 -144 171 -144 201 -174 115 -288 113 -192 277 -114 345 -202 249 -316 83 -228 119 -146 151 -116 85 -202 85 -144 437 -272 115 -144 85 -86 173 -142 163 -260 205 -280 339 -88 229 -194 363 -144 585 -394 599 -144 85 -100 151 -574 171 -174 85 -114 239 -288 211 -410 115 -144 489 -116 115 -116 201 -336 95 -96 93 -120 95 -128 115 -116 257 -174 141 -292 113 -162 165 -192 287 -192 161 -192 1125 -202 85 -222 461 -144 113 -116 115 -114 201 -288 259 -288 401 -174 85 -106 119 -96 431 -130 85 -114 375 -494 95 -96 167 -288 281 -104 411 -214 335 -144 119 -534 85 -144 143 -144 229 -246 97 -82 85 -260 201 -304 403 -116 391 -172 229 -448 81 -82 95 -116 201 -86 521 -144 119 -336 119 -142 95 -144 119 -96 267 -170 283 -288 95 -190 167 -288 199 -116 247 -144 87 -144 85 -114 215 -260 115 -116 113 -338 103 -240 167 -96 119 -120 95 -96 405 -188 85 -116 191 -334 143 -164 85 -230 115 -154 119 -166 143 -288 195 -144 87 -86 343 -232 113 -202 115 -86 201 -278 201 -86 85 -86 303 -286 95 -120 95 -168 167 -118 129 -94 95 -432 151 -218 119 -186 235 -230 167 -632 143 -144 231 -156 153 -282 147 -96 143 -592 261 -120 191 -96 167 -576 333 -342 171 -462 173 -514 157 -432 87 -312 397 -378 287 -374 403 -230 117 -264 257 -170 189 -346 137 -528 117 -240 805 -454 539 -288 171 -86 173 -286 153 -288 115 -172 173 -158 399 -114 85 -116 201 -116 85 -216 215 -432 113 -100 113 -338 123 -146 289 -86 143 -114 343 -94 95 -96 311 -110 171 -86 143 -88 969 -312 119 -316 401 -134 255 -202 323 -86 87 -114 253 -228 95 -172 85 -170 165 -86 343 -192 119 -144 213 -112 191 -334 287 -96 359 -500 137 -120 261 -242 119 -118 283 -210 167 -178 287 -174 343 -268 RAW_Data: 185 -96 119 -978 215 -120 213 -96 265 -172 261 -168 225 -462 307 -312 87 -172 115 -288 423 -84 87 -230 143 -404 109 -114 233 -216 165 -240 95 -288 95 -192 215 -238 335 -116 199 -170 135 -144 109 -408 285 -130 97 -98 115 -86 85 -86 87 -120 167 -144 119 -264 171 -316 231 -202 257 -274 115 -114 421 -86 85 -202 143 -144 201 -86 229 -86 143 -116 115 -86 115 -114 85 -202 143 -288 163 -186 483 -202 167 -212 373 -288 115 -86 171 -116 113 -230 231 -230 201 -172 171 -318 113 -390 101 -200 317 -404 287 -220 171 -232 199 -490 171 -144 173 -168 223 -202 173 -200 115 -172 119 -288 209 -220 85 -86 85 -480 423 -450 201 -364 411 -216 143 -120 219 -538 513 -330 199 -120 119 -96 127 -138 141 -140 117 -312 429 -182 259 -422 1027 -280 303 -210 131 -168 85 -86 201 -230 547 -174 113 -88 259 -202 109 -96 215 -144 291 -168 151 -316 85 -258 287 -260 279 -302 171 -284 189 -168 95 -96 309 -240 167 -384 165 -96 169 -226 95 -216 271 -122 195 -102 113 -116 339 -168 85 -192 583 -114 297 -406 167 -326 209 -212 119 -262 143 -86 87 -114 125 -172 115 -172 173 -576 219 -192 335 -120 85 -116 171 -202 711 -116 85 -172 373 -232 381 -144 95 -244 173 -210 119 -198 85 -86 143 -312 211 -96 115 -86 113 -528 103 -150 191 -144 119 -98 333 -172 215 -114 87 -86 529 -490 85 -356 239 -374 143 -260 457 -312 157 -394 131 -116 353 -172 229 -490 171 -144 189 -166 305 -144 85 -288 201 -356 147 -106 167 -740 143 -312 95 -242 137 -88 465 -126 239 -358 167 -120 1385 -130 385 -192 95 -120 817 -218 805 -140 171 -394 143 -86 371 -430 143 -216 187 -84 143 -316 173 -86 115 -202 85 -114 215 -96 167 -316 107 -368 313 -120 95 -382 197 -216 289 -388 119 -264 109 -86 113 -202 963 -172 111 -104 113 -346 115 -266 255 -88 229 -202 109 -288 357 -348 365 -226 381 -144 455 -144 901 -532 531 -108 103 -142 225 -144 143 -230 85 -144 141 -142 645 -82 167 -116 143 -172 403 -230 85 -144 87 -288 229 -144 115 -144 371 -82 107 -306 333 -116 113 -88 143 -86 229 -174 617 -96 677 -286 95 -240 95 -168 141 -216 95 -212 209 -120 95 -144 95 -240 165 -116 765 -230 229 -88 141 -192 97 -86 111 -114 231 -172 143 -144 365 -96 291 -144 129 -144 87 -168 141 -120 215 -144 143 -238 215 -192 95 -96 95 -144 403 -216 163 -102 155 -120 95 -120 263 -120 403 -374 431 -230 303 -134 791 -288 diff --git a/assets/unit_tests/subghz/security_pls_1_0.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/security_pls_1_0.sub similarity index 87% rename from assets/unit_tests/subghz/security_pls_1_0.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/security_pls_1_0.sub index f0909a77671..42fe7900b2e 100644 --- a/assets/unit_tests/subghz/security_pls_1_0.sub +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/security_pls_1_0.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz Key File Version: 1 -Frequency: 310000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: Security+ 1.0 Bit: 42 diff --git a/assets/unit_tests/subghz/security_pls_1_0_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/security_pls_1_0_raw.sub similarity index 99% rename from assets/unit_tests/subghz/security_pls_1_0_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/security_pls_1_0_raw.sub index 5542663d67a..ceecad35bda 100644 --- a/assets/unit_tests/subghz/security_pls_1_0_raw.sub +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/security_pls_1_0_raw.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 390000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: 297 -1592 167 -1594 231 -366 65 -598 65 -600 197 -98 199 -1098 99 -98 65 -862 133 -628 165 -597 361 -6828 65 -1668 231 -632 65 -792 163 -1284 163 -5554 199 -1588 165 -5300 97 -992 65 -432 197 -2566 99 -2340 63 -5932 65 -1462 65 -1062 131 -2454 65 -1446 133 -1682 65 -1260 65 -368 131 -1482 65 -134 131 -1512 197 -1710 131 -824 99 -66 133 -464 131 -866 65 -698 65 -200 65 -894 99 -1692 233 -1130 97 -2160 265 -1392 99 -132 131 -166 65 -1318 327 -1548 163 -6980 165 -994 65 -698 131 -1580 129 -132 63 -758 97 -466 99 -1590 395 -1024 97 -600 99 -732 63 -228 129 -98 165 -292 99 -696 231 -232 197 -166 133 -132 131 -430 165 -664 199 -1596 197 -530 65 -1054 63 -3474 165 -4504 65 -2980 99 -268 133 -2356 131 -798 99 -132 99 -796 97 -1692 97 -3804 199 -166 67 -66 97 -132 99 -5058 129 -1512 163 -1290 65 -298 199 -398 65 -1034 65 -1332 167 -3448 65 -1750 65 -1186 131 -462 65 -920 65 -166 97 -362 131 -1704 131 -1748 65 -2124 65 -1064 133 -1694 197 -5148 99 -566 131 -1696 99 -598 65 -698 231 -692 267 -2490 99 -1618 165 -1760 197 -2152 165 -1226 195 -100 99 -66 131 -100 99 -400 65 -456 131 -198 165 -368 261 -826 365 -858 99 -132 163 -460 229 -264 65 -664 165 -234 231 -98 165 -100 99 -1986 99 -2526 65 -832 167 -1762 231 -728 67 -530 133 -2082 97 -960 67 -498 199 -1658 133 -1488 99 -2652 99 -1258 165 -5284 99 -1660 65 -1790 65 -432 65 -3804 197 -2170 131 -196 129 -66 97 -832 99 -2466 97 -1622 99 -1624 163 -394 163 -1018 133 -1856 99 -430 67 -826 197 -3156 65 -166 97 -558 65 -1690 131 -1498 99 -66 65 -2716 161 -658 99 -1054 65 -230 65 -2074 97 -2042 65 -3074 263 -1698 165 -492 65 -400 131 -196 131 -368 165 -134 203 -168 201 -134 165 -398 301 -628 99 -1032 99 -830 65 -66 65 -432 229 -230 161 -394 197 -228 197 -330 327 -66 197 -822 231 -392 131 -892 393 -1456 131 -64 163 -1418 65 -166 165 -1428 133 -1526 163 -954 65 -888 99 -1956 133 -1194 131 -3514 65 -1728 97 -662 67 -1590 167 -2714 65 -930 65 -166 65 -728 131 -2752 65 -434 133 -1828 99 -3704 129 -1912 133 -1544 227 -494 133 -202 97 -466 65 -200 131 -1580 263 -1708 65 -626 65 -626 199 -166 133 -3446 99 -3420 99 -398 131 -1164 131 -7018 131 -3468 63 -1658 199 -956 101 -598 99 -1790 97 -1192 65 -362 133 -630 65 -100 429 -794 65 -696 231 -1130 65 -268 395 -730 131 -466 431 -66 233 -168 197 -998 97 -264 65 -796 163 -236 diff --git a/assets/unit_tests/subghz/security_pls_2_0.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/security_pls_2_0.sub similarity index 89% rename from assets/unit_tests/subghz/security_pls_2_0.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/security_pls_2_0.sub index a13ab0fc81d..85abdb3e190 100644 --- a/assets/unit_tests/subghz/security_pls_2_0.sub +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/security_pls_2_0.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz Key File Version: 1 -Frequency: 315000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: Security+ 2.0 Bit: 62 diff --git a/assets/unit_tests/subghz/security_pls_2_0_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/security_pls_2_0_raw.sub similarity index 99% rename from assets/unit_tests/subghz/security_pls_2_0_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/security_pls_2_0_raw.sub index 4cd9822c49d..f3c0b4992f4 100644 --- a/assets/unit_tests/subghz/security_pls_2_0_raw.sub +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/security_pls_2_0_raw.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 390000000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: -130 1115 -68 55471 -1962 167 -1664 99 -364 427 -232 759 -132 199 -100 267 -134 527 -132 99 -1292 99 -266 65 -68 131 -762 327 -98 393 -298 37611 -730 131 -428 1487 -3910 327 -100 295 -98 491 -492 65 -98 197 -860 97 -98 131 -856 131 -134 231 -792 229 -66 919 -198 7527 -7444 131 -722 97 -230 65 -98 527 -100 267 -68 229 -262 361 -528 195 -624 131 -164 495 -66 1697 -66 1791 -132 3715 -8320 65 -394 165 -166 461 -1094 297 -532 131 -764 197 -98 261 -230 165 -100 6235 -12144 265 -600 131 -134 65 -298 233 -958 163 -196 97 -756 263 -98 465 -134 97 -164 3051 -16552 431 -68 131 -166 131 -264 331 -298 561 -166 689 -66 557 -264 3173 -8316 65 -166 531 -298 197 -234 233 -134 233 -892 163 -296 65 -890 161 -232 331 -100 397 -15072 199 -100 99 -394 165 -164 129 -132 197 -132 199 -798 99 -1032 67 -398 427 -100 391 -98 363 -166 297 -66 2869 -66 2299 -12300 97 -660 99 -662 263 -594 131 -662 133 -66 199 -332 97 -134 99 -132 65 -134 131 -100 431 -100 2143 -66 429 -8840 97 -6976 97 -66 397 -66 65 -166 131 -230 131 -294 99 -228 97 -230 129 -756 133 -1298 1293 -132 529 -100 2567 -15654 229 -328 97 -198 131 -66 195 -1492 131 -398 99 -134 99 -366 265 -168 99 -100 235 -100 131 -396 299 -200 1029 -66 6461 -6804 99 -1062 65 -960 65 -232 131 -98 195 -1708 63 -196 261 -164 331 -66 261 -12094 65 -298 265 -198 163 -592 131 -2402 63 -66 297 -198 97 -66 393 -264 4003 -15878 231 -98 261 -496 229 -264 65 -858 131 -994 133 -66 331 -66 429 -100 165 -132 297 -15004 99 -1466 97 -266 165 -198 463 -796 231 -66 131 -298 99 -100 133 -134 167 -430 99 -66 365 -100 297 -134 265 -132 563 -98 1217 -66 6399 -8742 99 -592 99 -426 397 -2338 199 -66 995 -134 229 -132 65 -164 3989 -66 3675 -6962 165 -466 65 -564 399 -66 199 -134 263 -396 97 -132 97 -100 97 -428 393 -624 131 -988 229 -66 363 -230 791 -164 7883 -8286 165 -134 99 -66 197 -100 99 -68 131 -164 163 -398 197 -2162 2005 -66 97 -100 4365 -98 1255 -12012 99 -132 165 -462 65 -166 97 -564 65 -100 331 -794 199 -364 261 -496 331 -132 823 -66 6233 -10976 165 -764 165 -200 195 -296 97 -19176 195 -230 129 -658 131 -132 293 -66 133 -860 65 -858 131 -64 229 -66 227 -66 161 -66 9051 -7316 65 -1494 131 -98 165 -198 65 -134 365 -398 297 -100 3969 -14874 99 -998 65 -564 67 -364 263 -132 163 -528 197 -132 65 -264 65 -264 431 -100 301 -66 297 diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/smc5326.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/smc5326.sub new file mode 100644 index 00000000000..eab7aac99b8 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/smc5326.sub @@ -0,0 +1,8 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: SMC5326 +Bit: 25 +Key: 00 00 00 00 01 7D 55 80 +TE: 210 diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/smc5326_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/smc5326_raw.sub new file mode 100644 index 00000000000..f2e3edbf0ec --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/smc5326_raw.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +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 diff --git a/assets/unit_tests/subghz/somfy_keytis_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/somfy_keytis_raw.sub similarity index 99% rename from assets/unit_tests/subghz/somfy_keytis_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/somfy_keytis_raw.sub index 21145c064ab..2bed74fe111 100644 --- a/assets/unit_tests/subghz/somfy_keytis_raw.sub +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/somfy_keytis_raw.sub @@ -1,6 +1,6 @@ Filetype: Flipper SubGhz RAW File Version: 1 -Frequency: 433420000 +Frequency: 433920000 Preset: FuriHalSubGhzPresetOok650Async Protocol: RAW RAW_Data: 1753 -32700 2581 -2598 2513 -2604 2535 -2568 2529 -2602 2519 -2608 2523 -2574 2529 -2598 2517 -2604 2537 -2596 2521 -2574 2523 -2600 2515 -2530 4829 -1304 1305 -1308 1309 -678 669 -1280 1305 -680 639 -678 653 -1308 1273 -1334 645 -686 645 -684 611 -716 645 -684 1273 -1340 1269 -1338 641 -684 643 -684 643 -684 609 -714 1273 -682 643 -680 643 -1340 643 -684 1271 -1310 645 -684 1303 -682 643 -1308 1301 -1312 645 -682 1275 -1350 649 -644 649 -702 647 -678 679 -654 645 -646 681 -644 683 -642 1309 -672 647 -690 645 -1314 635 -680 1297 -688 651 -1316 647 -682 617 -686 649 -682 1307 -660 639 -704 639 -1304 1293 -688 653 -672 621 -696 653 -664 653 -668 651 -670 647 -704 613 -704 617 -704 613 -708 613 -708 613 -710 613 -1336 665 -652 1285 -680 655 -1340 583 -1734 2523 -2570 2543 -2572 2529 -2564 2533 -2570 2531 -2596 2519 -2478 4805 -1310 1299 -1314 1313 -680 637 -1328 1289 -684 623 -690 653 -1322 1289 -1308 651 -668 651 -688 615 -688 679 -654 1305 -1304 1305 -1304 645 -686 645 -688 643 -652 643 -712 1273 -680 653 -680 651 -1312 647 -682 1273 -1342 639 -680 1271 -718 613 -1348 1281 -1310 639 -680 1295 -1314 653 -678 645 -682 645 -678 675 -648 651 -678 651 -678 647 -678 1281 -698 613 -706 613 -1336 669 -658 1293 -684 639 -1330 629 -686 653 -686 653 -654 1309 -678 621 -1340 1273 -712 603 -712 639 -672 653 -664 651 -666 651 -670 651 -702 617 -672 649 -704 613 -706 615 -706 615 -708 613 -1334 1293 -688 621 -1316 659 -686 1281 -2338 2501 -2600 2511 -2606 2491 -2624 2481 -2610 2521 -2560 2527 -2506 4797 -1308 1299 -1312 1317 -678 635 -1294 1323 -682 619 -692 635 -1334 1291 -1312 653 -658 679 -656 647 -688 643 -654 1303 -1336 1301 -1304 643 -690 641 -654 675 -654 643 -682 1303 -678 655 -680 645 -1308 677 -654 1309 -1310 645 -682 1275 -686 647 -1320 1281 -1318 679 -642 1317 -1300 667 -636 677 -660 673 -656 705 -612 671 -670 643 -670 681 -646 1309 -660 669 -674 637 -1310 673 -658 1293 -690 653 -1290 657 -688 649 -656 679 -658 1303 -676 635 -1314 657 -658 1307 -678 669 -670 621 -690 635 -672 649 -674 647 -674 647 -674 647 -708 613 -710 645 -676 647 -676 613 -1336 631 -688 1285 -680 655 -1306 647 -688 611 -1706 2535 -2592 2529 -2570 2533 -2566 2543 -2570 2529 -2572 2533 -2474 4807 -1294 1325 -1314 1279 -680 665 -1290 1323 -648 659 -688 653 -1290 1317 -1310 657 -656 679 -658 641 -688 643 -656 1303 -1334 1299 -1302 675 -646 675 -644 677 -658 643 -688 1303 -676 641 -676 633 -1314 691 -654 1277 -1336 649 -656 1305 -674 667 -1284 1315 -1308 653 -656 1305 -1306 679 -658 645 -690 diff --git a/assets/unit_tests/subghz/somfy_telis_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/somfy_telis_raw.sub similarity index 100% rename from assets/unit_tests/subghz/somfy_telis_raw.sub rename to applications/debug/unit_tests/resources/unit_tests/subghz/somfy_telis_raw.sub diff --git a/applications/debug/unit_tests/resources/unit_tests/subghz/test_random_raw.sub b/applications/debug/unit_tests/resources/unit_tests/subghz/test_random_raw.sub new file mode 100644 index 00000000000..949a444585e --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/subghz/test_random_raw.sub @@ -0,0 +1,198 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 1160 -296 263 -166 65 -66 133 -98 133 -494 97 -226 361 -100 295 -66 131 -164 397 -66 431 -198 989 -100 431 -66 131 -66 197 -1052 231 -400 165 -102 199 -100 199 -100 299 -68 229 -234 167 -98 201 -136 267 -132 165 -100 131 -66 67 -66 131 -68 231 -100 99 -134 165 -132 665 -66 231 -100 267 -66 197 -100 659 -66 231 -266 197 -98 229 -990 297 -98 67 -796 99 -562 231 -300 65 -100 363 -1094 163 -336 331 -132 327 -98 261 -98 293 -100 265 -100 1265 -66 335 -134 267 -98 333 -134 99 -66 331 -66 195 -66 227 -164 263 -100 65 -692 163 -1978 97 -232 63 -132 131 -66 263 -66 293 -98 727 -132 65 -66 97 -66 295 -132 97 -66 263 -100 365 -100 165 -268 463 -66 65 -66 265 -1326 97 -100 197 -888 297 -130 327 -98 229 -264 65 -496 131 -266 195 -132 259 -132 361 -132 165 -98 927 -332 99 -66 99 -100 197 -658 65 -100 197 -164 133 -766 197 -236 199 -168 65 -98 233 -198 65 -100 129 -166 197 -790 97 -394 393 -130 557 -100 65 -230 333 -132 1389 -134 65 -66 99 -100 265 -134 99 -100 97 -460 327 -166 233 -266 199 -562 67 -898 197 -100 165 -1100 231 -100 263 -100 263 -132 1161 -66 463 -98 133 -98 165 -130 131 -66 65 -264 97 -266 97 -66 163 -166 97 -66 229 -296 99 -132 391 -98 327 -98 163 -200 631 -196 169 -102 131 -166 1327 -134 1361 -132 331 -134 99 -396 195 -364 97 -132 163 -66 99 -264 133 -200 165 -334 99 -134 65 -1332 165 -132 133 -298 367 -1124 295 -66 129 -66 593 -66 329 -264 397 -66 533 -66 461 -66 133 -132 65 -266 65 -132 99 -232 131 -2186 297 -98 165 -100 65 -66 97 -100 63 -566 99 -100 133 -66 333 -332 231 -364 231 -68 165 -300 497 -100 763 -100 365 -98 165 -200 65 -100 65 -696 65 -134 65 -262 65 -430 97 -66 131 -98 131 -330 293 -198 97 -132 163 -628 133 -98 131 -198 559 -98 459 -64 753 -98 361 -100 165 -132 133 -364 99 -66 197 -100 65 -132 197 -896 199 -66 65 -432 231 -168 231 -100 299 -528 131 -232 99 -134 97 -66 299 -66 197 -68 495 -100 65 -98 65 -100 563 -828 197 -732 267 -1260 65 -100 165 -898 65 -430 165 -496 199 -66 231 -66 131 -132 195 -66 193 -294 1259 -134 165 -100 199 -66 199 -166 233 -430 97 -366 97 -166 65 -1196 97 -132 97 -64 131 -196 97 -66 131 -960 229 -98 99 -264 233 -134 297 -98 467 -100 165 -98 365 -98 735 -98 197 -1464 233 -626 301 -432 97 -132 +RAW_Data: 231 -100 163 -362 163 -132 261 -298 65 -232 67 -132 297 -100 229 -228 497 -66 395 -98 593 -132 165 -132 67 -100 397 -132 99 -600 297 -66 231 -132 495 -136 65 -860 197 -98 129 -260 129 -100 131 -134 331 -268 165 -98 167 -66 231 -68 131 -232 65 -396 99 -100 663 -200 231 -66 1067 -66 99 -166 331 -132 65 -196 65 -134 133 -66 795 -66 397 -100 233 -132 199 -66 233 -200 433 -362 67 -298 327 -100 397 -66 165 -66 233 -232 529 -66 165 -100 67 -66 397 -66 689 -100 195 -462 325 -164 227 -132 261 -426 331 -98 133 -66 97 -296 97 -98 197 -66 97 -428 163 -526 359 -132 327 -164 99 -66 231 -396 329 -64 525 -66 593 -856 195 -100 129 -132 227 -166 229 -130 129 -98 293 -132 163 -98 195 -130 195 -98 97 -66 165 -100 99 -66 199 -466 265 -66 131 -132 197 -198 163 -232 1857 -100 265 -264 67 -264 265 -368 297 -102 133 -66 233 -98 167 -66 201 -66 133 -134 167 -166 65 -168 99 -400 165 -498 97 -298 131 -198 227 -132 327 -98 97 -66 297 -230 593 -132 67 -132 1227 -68 199 -134 197 -102 131 -232 365 -962 133 -298 297 -66 265 -266 333 -230 165 -102 165 -234 65 -98 131 -98 133 -66 167 -134 1357 -134 599 -532 265 -268 99 -298 199 -100 165 -166 131 -100 231 -266 65 -68 231 -334 67 -266 97 -134 65 -198 133 -100 163 -336 165 -234 65 -332 163 -690 97 -564 263 -64 165 -66 499 -100 563 -230 229 -132 99 -166 165 -132 199 -66 497 -66 659 -166 233 -68 331 -100 331 -66 165 -202 133 -896 133 -228 163 -196 131 -66 229 -130 263 -164 65 -98 263 -164 889 -66 261 -164 99 -494 97 -132 131 -64 65 -462 65 -262 99 -332 65 -102 65 -496 99 -132 197 -230 299 -134 65 -166 99 -430 99 -100 563 -66 165 -330 199 -264 197 -68 231 -100 367 -134 365 -68 299 -68 495 -68 165 -100 65 -498 397 -66 327 -100 195 -98 163 -132 163 -360 197 -98 65 -462 97 -526 427 -100 1419 -66 265 -66 333 -98 299 -98 99 -132 97 -100 197 -134 97 -100 231 -68 133 -100 165 -100 97 -100 133 -98 133 -66 165 -166 267 -132 229 -68 299 -66 97 -964 131 -66 133 -98 99 -66 297 -132 131 -68 167 -134 201 -68 167 -66 301 -166 133 -68 365 -100 527 -130 165 -130 233 -262 95 -628 97 -66 231 -168 99 -498 99 -100 229 -100 99 -166 299 -232 65 -100 99 -468 131 -68 197 -100 99 -66 297 -330 99 -100 463 -100 231 -68 133 -234 131 -134 231 -98 +RAW_Data: 561 -264 131 -100 65 -98 99 -66 133 -134 165 -132 65 -68 97 -68 131 -896 329 -198 65 -100 295 -230 199 -264 97 -196 327 -132 131 -100 131 -100 1293 -66 131 -202 463 -198 65 -68 99 -298 65 -232 333 -566 199 -168 99 -168 65 -498 295 -134 167 -134 199 -100 431 -100 65 -530 233 -164 65 -98 297 -134 363 -100 557 -100 163 -100 261 -66 619 -132 231 -296 97 -596 97 -228 131 -66 97 -66 197 -66 97 -360 229 -98 129 -330 99 -130 99 -130 65 -100 65 -132 97 -460 63 -100 265 -330 331 -98 197 -66 855 -464 165 -66 99 -198 99 -868 231 -66 167 -636 329 -530 165 -130 131 -132 99 -528 165 -64 65 -164 65 -166 129 -162 425 -66 231 -100 133 -98 231 -68 333 -98 165 -200 361 -98 99 -66 297 -68 163 -168 65 -498 99 -232 295 -696 99 -266 131 -100 65 -100 99 -628 133 -132 65 -366 265 -398 261 -100 529 -100 297 -132 2535 -596 65 -932 199 -66 199 -866 65 -298 165 -66 67 -132 65 -100 165 -200 131 -166 299 -200 167 -166 199 -334 65 -694 365 -132 727 -232 265 -66 133 -102 65 -100 531 -232 65 -132 65 -100 131 -562 197 -66 297 -66 265 -100 433 -432 495 -332 197 -98 167 -332 97 -66 297 -758 65 -228 99 -194 295 -130 99 -98 261 -98 163 -66 231 -134 1029 -68 399 -200 133 -132 99 -266 163 -264 163 -130 229 -66 689 -166 131 -988 65 -592 97 -132 129 -164 197 -432 65 -100 165 -134 65 -166 133 -232 563 -66 231 -66 599 -66 265 -100 199 -498 231 -464 327 -262 131 -100 327 -430 197 -266 99 -132 65 -66 133 -132 101 -200 65 -100 433 -166 99 -266 265 -98 97 -66 229 -100 263 -200 631 -100 199 -66 167 -300 753 -262 457 -132 231 -98 163 -164 227 -164 65 -262 129 -594 229 -790 131 -66 129 -428 197 -164 197 -200 133 -100 131 -564 65 -134 661 -134 2925 -68 231 -134 167 -132 99 -98 133 -98 167 -98 197 -1086 329 -296 65 -130 65 -264 197 -132 163 -98 165 -66 97 -132 129 -100 261 -66 97 -66 197 -326 95 -134 629 -98 65 -262 65 -132 197 -264 129 -98 163 -230 131 -528 261 -298 197 -64 131 -394 131 -764 99 -132 99 -66 99 -464 367 -98 165 -98 1159 -134 963 -66 633 -562 133 -132 99 -66 131 -564 197 -134 263 -132 195 -66 131 -262 459 -332 197 -66 133 -1060 133 -66 331 -662 197 -134 231 -100 299 -232 1087 -232 131 -100 265 -462 131 -200 433 -168 101 -432 97 -196 65 -132 327 -100 129 -226 163 -100 163 -132 +RAW_Data: 227 -330 129 -466 165 -200 163 -66 97 -130 97 -66 197 -164 457 -166 463 -166 361 -66 163 -66 97 -198 65 -64 99 -300 65 -68 263 -98 99 -134 197 -100 165 -134 231 -66 297 -98 67 -132 365 -66 197 -228 99 -428 195 -132 65 -330 265 -628 63 -130 97 -132 195 -232 195 -130 263 -132 167 -100 133 -166 1229 -264 165 -332 233 -1230 199 -102 167 -100 401 -166 229 -852 197 -264 229 -130 163 -198 65 -98 229 -66 199 -100 335 -98 899 -134 393 -528 97 -66 97 -300 99 -300 99 -664 329 -198 99 -400 131 -198 299 -330 99 -66 65 -132 67 -298 527 -66 493 -132 1085 -198 425 -232 197 -66 131 -164 165 -66 197 -1480 197 -330 65 -494 65 -330 197 -296 99 -66 97 -592 99 -164 97 -426 329 -66 131 -98 99 -66 265 -98 433 -66 131 -234 267 -234 65 -266 99 -98 65 -166 97 -164 197 -198 65 -866 67 -66 231 -430 131 -200 65 -100 631 -198 265 -526 99 -230 129 -68 165 -232 229 -98 427 -328 1053 -134 431 -198 365 -430 131 -100 131 -398 231 -134 1195 -132 131 -134 661 -100 333 -398 97 -400 97 -234 165 -468 65 -100 99 -66 299 -266 67 -98 397 -166 987 -132 97 -64 65 -266 97 -852 297 -132 163 -428 65 -298 99 -1124 97 -266 233 -66 165 -332 65 -800 297 -98 131 -164 469 -100 97 -658 165 -66 133 -132 531 -134 131 -98 297 -722 65 -132 821 -100 229 -132 459 -66 197 -98 985 -330 131 -100 133 -100 101 -198 299 -100 65 -134 363 -100 67 -830 131 -132 99 -100 299 -66 297 -366 99 -166 197 -66 131 -364 131 -200 331 -134 265 -530 99 -564 65 -68 231 -132 233 -134 165 -66 165 -66 431 -102 465 -132 99 -132 99 -364 133 -98 67 -198 133 -66 65 -264 99 -298 231 -100 199 -728 297 -132 65 -134 433 -926 263 -66 227 -528 65 -130 263 -100 97 -98 723 -100 131 -134 531 -66 267 -400 199 -66 65 -100 99 -498 99 -332 529 -98 197 -460 131 -296 231 -296 97 -66 65 -426 131 -132 133 -100 99 -66 99 -964 463 -68 631 -68 1325 -232 365 -68 163 -432 131 -432 301 -234 165 -432 99 -398 65 -100 133 -166 433 -230 163 -236 67 -132 97 -300 65 -66 199 -132 2355 -296 327 -98 65 -66 99 -294 197 -232 133 -100 67 -134 97 -66 65 -760 65 -98 131 -166 197 -132 63 -132 163 -98 163 -754 429 -230 163 -66 695 -132 199 -98 433 -100 1197 -394 491 -460 65 -460 65 -166 97 -68 131 -134 233 -66 299 -762 231 -200 265 -198 499 -132 167 -766 +RAW_Data: 265 -66 165 -134 165 -66 365 -132 2091 -134 231 -632 297 -630 195 -264 65 -296 65 -64 131 -298 165 -100 99 -100 99 -562 165 -398 99 -566 99 -166 133 -334 99 -134 357 -68 97 -332 459 -130 161 -230 163 -132 163 -98 195 -164 97 -130 65 -1262 265 -166 563 -964 133 -100 99 -796 199 -868 67 -134 65 -232 67 -66 199 -132 295 -98 821 -332 1097 -296 129 -758 65 -200 67 -98 531 -198 265 -66 863 -66 65 -300 363 -100 99 -132 165 -100 267 -134 165 -234 231 -100 533 -198 1057 -200 497 -68 531 -100 63 -66 133 -100 163 -330 63 -230 161 -100 97 -66 131 -298 165 -68 167 -732 265 -396 265 -100 99 -166 99 -200 65 -596 131 -696 263 -200 135 -100 133 -66 65 -98 131 -66 399 -66 367 -130 765 -298 97 -98 231 -130 197 -130 65 -294 131 -98 195 -66 129 -66 131 -428 131 -66 197 -660 133 -66 131 -332 165 -628 65 -724 99 -164 131 -98 65 -166 395 -68 435 -68 299 -66 1323 -400 231 -298 99 -264 199 -428 131 -230 65 -1188 99 -98 163 -526 327 -66 197 -394 165 -66 65 -596 857 -66 163 -66 591 -166 129 -234 167 -624 265 -294 263 -500 131 -164 65 -98 195 -166 97 -100 195 -198 131 -1214 1017 -166 97 -428 425 -66 1183 -430 261 -230 65 -196 97 -100 195 -364 65 -328 65 -560 97 -1084 197 -694 99 -132 65 -132 97 -66 229 -332 163 -330 197 -66 327 -100 195 -98 327 -98 993 -132 697 -332 665 -166 199 -234 99 -168 199 -266 201 -66 199 -462 231 -298 195 -66 165 -884 165 -196 131 -66 365 -98 231 -98 329 -430 165 -66 1195 -300 133 -100 163 -132 65 -462 131 -196 163 -228 197 -992 163 -162 95 -824 131 -98 263 -98 97 -166 65 -98 131 -100 65 -496 131 -100 265 -134 199 -68 199 -98 329 -132 131 -66 983 -198 99 -132 295 -66 261 -460 329 -164 131 -426 65 -268 65 -466 131 -66 131 -230 263 -230 297 -200 895 -66 231 -460 1055 -134 433 -100 65 -66 231 -398 131 -400 97 -68 165 -330 297 -330 133 -396 99 -66 263 -262 97 -1644 295 -66 297 -100 729 -132 335 -68 491 -66 231 -326 393 -68 329 -166 65 -922 99 -1652 197 -266 265 -132 99 -432 265 -134 293 -134 65 -166 231 -232 199 -64 631 -400 163 -400 99 -66 131 -730 265 -100 195 -66 195 -330 163 -132 131 -166 67 -830 99 -66 297 -664 97 -134 65 -100 295 -66 133 -166 997 -66 299 -66 795 -98 493 -68 131 -166 131 -100 165 -198 67 -98 99 -598 65 -266 97 -268 199 -132 +RAW_Data: 65 -364 97 -298 97 -692 129 -264 261 -66 263 -564 297 -298 97 -232 131 -66 97 -66 361 -100 199 -68 433 -66 331 -68 333 -438 165 -598 99 -134 65 -100 65 -264 399 -266 67 -764 197 -232 297 -498 199 -858 65 -100 97 -130 131 -230 231 -230 97 -362 229 -66 497 -66 329 -132 65 -64 423 -98 393 -132 65 -664 65 -198 99 -68 331 -100 97 -332 133 -166 199 -98 693 -332 429 -100 265 -166 363 -100 167 -66 497 -100 1365 -202 333 -200 65 -132 199 -100 99 -330 131 -300 229 -832 65 -764 65 -132 263 -200 131 -1026 97 -196 97 -132 165 -98 525 -232 431 -66 659 -66 461 -98 97 -132 65 -100 261 -66 129 -66 163 -100 229 -364 131 -1560 165 -364 133 -268 165 -132 99 -200 65 -434 97 -68 263 -298 131 -230 199 -132 199 -100 165 -68 1829 -134 463 -100 265 -890 395 -364 131 -362 97 -260 97 -462 65 -66 457 -296 197 -268 263 -66 363 -132 895 -66 435 -164 403 -464 165 -928 163 -134 167 -400 263 -660 131 -100 63 -428 199 -132 199 -700 397 -232 335 -66 497 -166 1425 -66 231 -102 231 -134 99 -132 133 -134 99 -530 133 -66 131 -66 165 -166 199 -266 65 -100 131 -100 527 -300 925 -266 363 -530 131 -898 165 -66 199 -134 165 -232 525 -100 1509 -100 99 -200 99 -168 67 -300 163 -404 263 -132 67 -66 263 -664 65 -494 199 -100 165 -100 263 -264 131 -264 161 -66 65 -428 131 -296 163 -264 559 -166 597 -132 199 -498 65 -66 263 -162 163 -66 65 -66 131 -762 197 -162 65 -98 297 -262 163 -66 131 -130 97 -100 131 -564 199 -100 333 -166 165 -66 1653 -100 689 -296 131 -230 229 -526 65 -462 131 -100 197 -398 99 -264 133 -428 233 -134 265 -132 65 -200 97 -100 131 -100 265 -232 99 -232 429 -196 263 -166 669 -230 293 -298 561 -100 65 -100 229 -100 99 -830 99 -132 65 -1058 133 -564 99 -100 131 -826 165 -100 101 -100 65 -100 99 -398 329 -266 65 -66 557 -132 67 -132 627 -464 99 -66 163 -200 99 -66 99 -232 65 -66 165 -132 167 -266 99 -598 65 -432 65 -134 99 -330 197 -202 65 -266 133 -598 99 -68 163 -66 165 -66 1957 -430 393 -134 97 -134 97 -530 65 -428 161 -1150 65 -64 131 -594 229 -326 131 -196 197 -132 65 -566 297 -68 367 -64 1119 -464 163 -334 165 -134 163 -102 99 -66 199 -460 99 -100 99 -132 67 -266 99 -98 99 -532 197 -992 129 -362 231 -166 65 -132 133 -98 599 -98 795 -66 235 -100 333 -232 331 -98 99 -430 +RAW_Data: 131 -266 297 -232 231 -100 267 -266 397 -232 131 -132 133 -668 65 -98 99 -68 131 -398 1325 -66 333 -166 99 -100 199 -960 99 -66 231 -132 167 -134 199 -100 165 -100 495 -132 367 -332 99 -266 495 -66 65 -66 99 -66 65 -134 163 -134 199 -164 199 -98 531 -132 497 -100 65 -100 97 -530 99 -100 131 -68 131 -200 265 -102 97 -332 365 -66 265 -434 163 -68 97 -232 265 -132 65 -264 133 -198 497 -100 197 -68 729 -66 165 -330 165 -762 265 -430 397 -3844 67 -432 1197 -66 631 -66 521 -458 131 -132 197 -264 65 -922 163 -1586 99 -492 229 -530 65 -634 131 -134 65 -398 99 -398 363 -366 295 -1158 99 -134 65 -298 431 -432 97 -634 131 -66 133 -298 97 -466 99 -66 65 -332 65 -498 65 -962 133 -132 99 -464 233 -598 165 -196 199 -68 331 -66 231 -166 65 -200 133 -100 197 -162 393 -264 327 -1052 65 -164 163 -560 99 -98 63 -394 165 -132 99 -166 99 -100 97 -234 131 -1028 131 -66 99 -394 99 -164 97 -298 163 -66 359 -296 197 -66 499 -98 167 -462 695 -132 131 -134 97 -264 65 -100 265 -132 197 -896 195 -166 65 -262 99 -492 97 -196 99 -66 163 -164 97 -1414 133 -364 65 -132 99 -362 131 -164 195 -296 67 -334 197 -98 227 -66 265 -666 165 -430 167 -364 65 -98 97 -230 67 -364 133 -132 165 -134 65 -100 197 -230 97 -462 131 -296 195 -262 99 -98 327 -530 97 -464 99 -198 167 -234 97 -628 99 -266 131 -134 165 -166 65 -630 393 -100 197 -964 99 -400 163 -230 129 -264 97 -100 333 -332 165 -100 67 -66 199 -200 99 -166 133 -992 99 -66 163 -1590 65 -134 65 -366 331 -68 131 -1692 197 -166 99 -132 99 -100 429 -134 65 -100 367 -164 65 -166 329 -166 131 -234 99 -564 99 -100 99 -66 99 -100 263 -164 201 -436 131 -100 133 -268 231 -232 99 -366 65 -498 131 -100 131 -300 265 -432 199 -132 67 -100 163 -300 331 -100 263 -296 129 -68 97 -100 165 -234 99 -234 101 -66 67 -100 97 -1328 97 -264 99 -3316 261 -134 65 -300 65 -102 199 -98 99 -100 231 -1424 165 -164 195 -130 99 -396 129 -598 199 -264 97 -566 65 -762 99 -132 197 -464 131 -66 65 -494 97 -66 229 -66 525 -428 65 -428 229 -920 459 -556 97 -300 199 -100 99 -466 67 -1362 233 -430 131 -132 131 -294 197 -130 163 -98 163 -132 131 -1020 165 -132 165 -98 97 -232 329 -132 165 -130 129 -132 193 -592 197 -130 97 -2076 133 -164 163 -162 97 -98 63 -760 131 -100 +RAW_Data: 659 -560 97 -98 165 -66 65 -396 163 -662 97 -230 133 -598 97 -796 399 -132 165 -132 133 -66 197 -66 131 -398 297 -232 231 -364 199 -430 65 -100 165 -1096 231 -98 165 -98 197 -560 297 -132 65 -1260 429 -824 97 -68 97 -664 199 -296 495 -630 263 -966 99 -234 167 -66 65 -264 231 -68 197 -68 367 -66 163 -132 163 -430 233 -100 399 -134 167 -98 165 -98 99 -132 231 -230 193 -98 161 -396 261 -494 163 -1290 265 -664 65 -66 461 -100 563 -66 431 -100 1087 -198 165 -166 165 -100 899 -102 169 -200 99 -200 265 -66 231 -68 165 -100 133 -396 231 -366 99 -466 65 -234 497 -66 133 -200 133 -100 99 -100 65 -398 199 -264 101 -132 201 -366 195 -228 229 -134 625 -66 163 -66 293 -164 523 -100 97 -196 163 -396 229 -330 99 -98 65 -100 359 -562 195 -98 131 -98 229 -132 63 -822 65 -100 99 -432 229 -100 261 -66 327 -100 325 -198 2007 -132 261 -98 65 -100 397 -164 133 -66 99 -332 65 -98 65 -696 99 -298 231 -260 97 -296 199 -98 229 -396 163 -398 65 -398 197 -100 363 -66 797 -66 199 -100 1259 -132 331 -330 265 -800 197 -200 97 -166 99 -730 133 -432 99 -168 265 -1060 165 -96 129 -164 163 -66 97 -64 295 -68 397 -100 1091 -98 533 -364 261 -64 163 -920 99 -98 65 -164 195 -462 99 -98 97 -198 97 -100 261 -230 131 -594 263 -100 163 -196 129 -328 97 -98 297 -66 129 -66 131 -66 229 -228 461 -100 1127 -64 359 -1052 231 -298 131 -232 163 -428 65 -230 261 -66 97 -366 131 -68 197 -332 231 -460 229 -262 461 -166 629 -200 367 -198 1745 -528 65 -266 67 -166 131 -564 165 -134 135 -1358 197 -198 229 -98 165 -690 131 -196 197 -66 261 -164 263 -98 63 -132 65 -100 229 -100 197 -100 299 -66 465 -66 165 -68 165 -432 131 -530 63 -330 65 -198 97 -326 97 -66 65 -132 195 -498 65 -98 165 -260 95 -264 295 -130 131 -98 197 -526 65 -896 65 -100 429 -98 163 -132 527 -132 1189 -330 299 -166 165 -232 97 -66 99 -198 67 -168 133 -100 167 -200 165 -100 101 -630 199 -596 199 -1354 99 -198 163 -64 227 -132 163 -98 163 -132 425 -100 461 -262 259 -130 195 -98 65 -130 97 -166 129 -66 165 -98 97 -658 99 -1374 163 -132 165 -328 195 -98 99 -496 63 -132 67 -98 197 -130 99 -66 129 -98 1147 -66 1219 -1120 165 -166 263 -66 65 -100 197 -130 99 -164 99 -100 99 -262 199 -332 97 -66 233 -760 65 -464 363 -696 131 -68 97 -68 +RAW_Data: 329 -164 197 -302 429 -296 229 -232 99 -364 97 -98 131 -98 293 -164 133 -200 165 -270 233 -366 131 -400 399 -100 99 -464 133 -66 299 -198 297 -100 199 -66 365 -66 563 -464 425 -166 365 -166 101 -298 165 -166 99 -300 331 -264 329 -298 65 -200 99 -66 165 -1290 129 -594 99 -66 197 -262 197 -164 163 -100 229 -98 163 -96 1709 -100 65 -66 363 -132 99 -824 361 -1388 165 -98 99 -1952 99 -398 263 -200 201 -300 231 -134 265 -166 133 -66 399 -98 233 -232 461 -66 99 -132 165 -100 65 -298 135 -498 99 -394 297 -296 67 -66 165 -432 165 -98 97 -66 265 -66 563 -98 199 -66 265 -232 597 -100 691 -98 631 -132 99 -632 199 -730 97 -132 99 -528 361 -492 131 -296 65 -460 65 -66 97 -164 99 -262 263 -66 97 -98 263 -64 491 -66 851 -100 161 -132 97 -926 99 -100 99 -98 97 -632 163 -326 231 -290 99 -132 199 -66 199 -166 297 -68 167 -564 1059 -1018 1009 -1016 969 -1042 987 -1008 1005 -988 1013 -1032 483 -550 483 -550 225 -282 517 -522 261 -294 499 -560 495 -554 231 -298 269 -266 233 -298 495 -524 521 -520 263 -290 263 -268 271 -308 241 -274 507 -524 259 -260 521 -522 521 -520 523 -518 523 -518 527 -552 231 -298 265 -266 497 -522 263 -258 521 -524 261 -294 499 -558 233 -298 269 -274 271 -270 499 -520 261 -290 229 -296 267 -268 505 -560 233 -298 271 -272 507 -524 487 -550 487 -550 259 -288 225 -298 269 -306 239 -308 241 -276 507 -522 261 -258 259 -294 265 -268 269 -270 271 -304 239 -308 241 -274 273 -270 499 -520 489 -550 259 -288 229 -298 503 -558 231 -334 237 -274 997 -1008 1003 -984 1003 -1034 977 -1006 1013 -986 999 -1046 513 -514 511 -514 257 -280 513 -520 261 -292 497 -556 497 -554 233 -298 267 -268 233 -298 495 -522 527 -518 261 -290 263 -266 273 -306 243 -274 507 -524 261 -258 521 -522 523 -518 523 -516 523 -552 493 -552 231 -298 267 -266 497 -522 263 -256 523 -522 263 -294 497 -558 231 -300 269 -274 271 -272 497 -522 259 -290 227 -298 267 -270 505 -560 231 -300 271 -272 507 -522 521 -518 489 -548 259 -288 225 -298 269 -304 241 -308 241 -276 505 -524 259 -258 259 -294 265 -268 269 -270 271 -306 239 -308 241 -274 271 -270 499 -520 519 -518 261 -288 229 -298 503 -560 231 -300 269 -272 999 -1010 1013 -982 1007 -1018 973 -1016 999 -1010 987 -1044 481 -554 483 -520 273 -292 473 -552 245 -324 487 -532 501 -558 267 -268 269 -268 231 -298 495 -522 523 -520 259 -292 +RAW_Data: 263 -268 271 -308 241 -276 507 -522 261 -256 523 -522 523 -518 521 -518 525 -518 525 -554 229 -298 267 -268 495 -524 261 -258 521 -522 263 -294 499 -556 231 -300 271 -272 271 -270 499 -522 261 -290 227 -296 269 -270 505 -556 233 -298 271 -274 505 -524 521 -516 487 -548 259 -288 227 -298 271 -304 241 -308 241 -274 507 -524 259 -258 259 -294 265 -268 267 -270 271 -272 273 -308 241 -276 271 -270 499 -520 489 -550 259 -288 261 -264 505 -558 233 -300 269 -274 997 -1008 1003 -986 1005 -1002 1011 -1010 987 -1006 997 -1044 479 -546 513 -512 259 -280 511 -518 263 -292 497 -556 497 -556 231 -300 267 -266 233 -298 497 -522 523 -518 263 -290 263 -268 273 -306 241 -276 507 -522 261 -258 521 -522 523 -518 521 -518 525 -518 525 -554 231 -298 265 -266 499 -522 261 -258 521 -522 263 -294 499 -556 231 -300 271 -272 273 -270 499 -520 261 -288 229 -296 267 -270 505 -560 231 -300 271 -274 505 -524 489 -548 487 -548 261 -286 225 -298 271 -306 239 -308 241 -274 507 -524 259 -258 257 -296 267 -266 269 -270 271 -272 273 -308 239 -276 273 -268 497 -520 491 -550 261 -288 227 -296 505 -560 233 -298 269 -274 997 -1012 1009 -988 1007 -996 1009 -1008 1003 -982 999 -1044 513 -512 511 -514 257 -282 511 -520 261 -294 497 -556 497 -556 231 -300 265 -268 233 -298 495 -522 525 -518 263 -290 263 -268 271 -306 243 -274 509 -522 261 -256 523 -522 523 -518 491 -548 523 -552 493 -552 231 -298 265 -268 497 -520 261 -290 493 -522 263 -292 497 -558 231 -300 271 -272 271 -270 501 -520 261 -290 227 -296 267 -270 505 -558 233 -298 271 -274 507 -522 489 -550 487 -550 227 -318 225 -298 269 -304 241 -308 239 -276 507 -522 261 -258 259 -296 265 -266 269 -270 271 -304 239 -308 241 -274 273 -270 497 -520 491 -550 259 -288 227 -298 505 -558 233 -330 237 -274 999 -1012 979 -1016 1005 -1018 977 -1016 983 -1038 977 -1036 475 -550 509 -514 257 -276 503 -548 259 -288 491 -556 497 -556 231 -298 265 -268 233 -298 497 -522 491 -550 261 -292 263 -300 237 -308 241 -276 505 -524 261 -290 491 -522 521 -518 523 -516 525 -550 495 -554 231 -296 231 -300 497 -522 261 -290 491 -522 261 -294 497 -558 231 -334 237 -274 271 -270 499 -520 261 -290 229 -298 267 -268 505 -558 231 -300 269 -274 505 -524 489 -550 487 -548 259 -288 227 -298 269 -306 239 -308 241 -274 509 -524 259 -256 259 -296 265 -268 269 -268 271 -304 239 -308 241 -276 271 -270 497 -522 489 -550 +RAW_Data: 261 -288 227 -296 505 -558 231 -300 271 -272 1001 -1012 977 -1026 1007 -992 1011 -1008 1001 -1002 1001 -1014 513 -512 513 -514 257 -282 513 -520 263 -292 497 -556 497 -556 231 -300 267 -268 231 -298 495 -522 525 -518 261 -292 263 -266 271 -308 241 -276 507 -522 261 -288 491 -522 523 -518 523 -516 523 -520 525 -554 231 -296 265 -268 497 -522 259 -290 491 -522 263 -294 499 -556 265 -266 271 -274 271 -270 499 -520 261 -290 227 -298 267 -270 505 -558 233 -300 269 -274 505 -524 519 -516 487 -550 259 -286 259 -266 269 -306 241 -308 239 -276 507 -522 261 -256 259 -296 267 -266 269 -270 269 -272 273 -308 241 -276 271 -270 497 -520 521 -518 259 -290 259 -264 503 -560 265 -266 271 -274 999 -1014 977 -1016 1007 -984 1009 -1014 999 -1010 985 -1044 483 -520 519 -522 253 -276 529 -512 257 -282 517 -556 497 -554 231 -298 267 -268 233 -298 497 -522 521 -518 261 -290 265 -268 273 -308 241 -276 505 -524 259 -258 521 -520 525 -518 521 -518 525 -518 525 -552 231 -296 267 -268 497 -522 261 -256 523 -522 263 -294 499 -560 231 -298 269 -274 271 -270 497 -522 259 -292 227 -298 267 -268 505 -558 233 -300 271 -272 507 -522 521 -516 487 -550 259 -286 227 -298 271 -304 239 -308 241 -276 507 -520 261 -256 259 -296 265 -268 269 -268 273 -272 273 -306 241 -276 273 -268 497 -520 521 -516 261 -290 227 -298 505 -556 265 -266 271 -274 999 -1008 1005 -986 1005 -1000 1007 -1008 1001 -996 1009 -1016 515 -514 513 -514 259 -282 515 -520 263 -292 497 -556 499 -554 231 -300 265 -268 233 -298 497 -522 523 -518 261 -290 263 -302 239 -308 241 -276 505 -524 259 -258 521 -522 523 -518 523 -518 523 -518 525 -552 231 -298 265 -268 497 -524 259 -258 521 -524 261 -294 499 -556 233 -298 271 -272 273 -270 497 -522 259 -290 229 -296 267 -270 505 -558 231 -124104 99 -334 97 -794 165 -232 99 -136 595 -260 65 -66 163 -100 1353 -100 231 -134 233 -100 231 -962 99 -432 131 -134 231 -234 133 -98 99 -430 65 -100 99 -396 99 -1228 197 -66 97 -200 299 -100 265 -66 99 -262 99 -64 1447 -66 163 -98 65 -562 197 -66 129 -100 197 -164 65 -262 65 -264 65 -164 65 -1490 99 -66 99 -66 65 -166 197 -134 99 -66 131 -100 99 -66 97 -66 231 -266 465 -66 299 -66 461 -98 295 -132 65 -528 229 -164 129 -396 65 -962 133 -566 99 -198 163 -830 197 -100 201 -400 67 -66 99 -132 299 -66 463 -168 231 -66 433 -66 895 -956 65 -828 229 -468 +RAW_Data: 197 -100 131 -130 97 -132 295 -66 97 -430 133 -198 65 -526 727 -66 229 -262 163 -98 525 -66 853 -132 165 -66 399 -166 99 -268 397 -134 233 -66 67 -298 131 -1130 133 -100 65 -134 297 -198 329 -134 197 -66 129 -590 133 -164 131 -132 131 -132 131 -98 2973 -230 65 -664 165 -134 165 -432 99 -166 165 -496 201 -132 99 -100 197 -68 131 -632 67 -98 133 -132 695 -100 293 -100 131 -98 199 -66 297 -100 199 -100 335 -100 265 -430 165 -66 297 -664 99 -264 199 -98 165 -134 167 -200 99 -100 65 -98 99 -296 65 -164 163 -494 99 -100 267 -198 99 -426 199 -134 99 -232 131 -330 65 -134 131 -134 201 -100 463 -232 97 -296 1087 -100 363 -134 97 -132 197 -300 131 -234 233 -990 133 -232 65 -266 65 -264 199 -200 265 -396 65 -134 165 -66 165 -66 65 -330 231 -300 431 -198 331 -266 99 -232 131 -432 65 -266 131 -100 265 -66 599 -498 97 -98 65 -298 197 -98 227 -328 231 -232 163 -660 197 -132 65 -134 363 -66 197 -166 631 -66 361 -132 231 -132 427 -328 99 -898 199 -332 365 -98 199 -162 197 -230 97 -166 561 -198 263 -196 295 -100 65 -428 65 -98 99 -166 199 -132 131 -100 165 -98 131 -66 1161 -66 167 -166 529 -364 163 -460 231 -196 291 -296 65 -66 97 -398 65 -100 133 -658 165 -164 195 -230 99 -132 229 -98 129 -430 691 -68 165 -100 167 -66 435 -100 597 -66 331 -98 99 -298 263 -262 131 -492 129 -788 97 -428 133 -100 65 -100 65 -98 131 -998 199 -98 235 -100 965 -232 793 -98 861 -100 199 -68 529 -398 195 -960 397 -100 131 -266 201 -66 65 -98 99 -132 233 -168 261 -66 131 -428 165 -66 261 -528 65 -66 195 -166 261 -66 361 -164 525 -200 697 -364 97 -66 131 -558 163 -266 65 -298 331 -464 199 -100 165 -100 97 -66 65 -994 331 -166 131 -534 199 -300 67 -98 329 -266 163 -66 227 -66 133 -66 261 -100 559 -466 231 -260 197 -68 227 -98 97 -96 65 -296 163 -296 131 -164 195 -230 263 -66 393 -230 457 -134 131 -534 133 -630 99 -98 399 -100 265 -266 431 -134 67 -100 789 -1584 165 -624 131 -134 263 -396 99 -692 165 -98 165 -398 295 -66 99 -592 163 -132 131 -328 265 -66 231 -100 363 -98 335 -66 167 -98 499 -100 397 -68 131 -100 231 -100 165 -134 165 -1228 99 -66 233 -1386 63 -924 99 -130 131 -392 231 -166 133 -166 165 -198 501 -66 265 -66 299 -98 299 -100 299 -134 231 -68 165 -232 99 -66 65 -200 165 -100 +RAW_Data: 67 -132 199 -302 65 -1058 199 -798 133 -334 165 -894 65 -132 233 -164 329 -130 493 -66 893 -200 631 -100 1207 -984 1013 -1010 1017 -988 997 -1010 1013 -986 1007 -1032 257 -254 253 -292 265 -266 501 -524 525 -520 261 -294 265 -306 241 -276 507 -522 261 -256 521 -524 521 -518 261 -290 495 -558 497 -556 265 -264 267 -268 265 -264 497 -522 261 -292 261 -268 269 -274 273 -274 275 -276 273 -270 231 -296 265 -264 497 -522 527 -520 261 -294 267 -304 241 -276 273 -270 495 -520 261 -256 293 -264 269 -270 507 -556 265 -266 271 -274 507 -522 521 -516 517 -516 259 -256 291 -266 271 -272 273 -274 275 -276 507 -522 259 -256 259 -296 265 -268 269 -270 271 -270 273 -274 275 -276 507 -522 521 -514 519 -516 259 -254 291 -266 269 -274 511 -558 267 -266 991 -1010 1009 -1000 999 -974 1023 -1012 1009 -990 1007 -1032 225 -284 253 -292 263 -264 501 -524 525 -518 263 -296 265 -306 241 -276 507 -522 261 -256 521 -520 523 -518 261 -290 497 -556 499 -554 265 -266 1535 -556 1321 -340 201 -168 1009 -246 209 -304 1249 -320 489 -196 1129 -338 209 -316 1011 -562 197 -260 539 -66 219 -194 815 -260 515 -132 247 -144 825 -260 815 -272 1301 -262 519 -66 215 -230 533 -132 217 -178 1049 -134 213 -178 347 -208 507 -134 247 -142 511 -192 213 -94 519 -274 521 -278 793 -278 815 -278 521 -132 229 -154 503 -198 839 -228 1597 -262 259 -292 1553 -526 1593 -458 1103 -528 1345 -268 235 -298 1293 -260 523 -194 189 -122 525 -196 221 -94 805 -306 1041 -166 233 -144 493 -228 221 -72 1041 -238 253 -342 791 -314 375 -162 823 -256 1019 -166 213 -154 507 -194 213 -124 815 -266 505 -196 221 -122 407 -166 391 -134 983 -262 219 -312 1213 -360 479 -166 1191 -322 223 -262 1043 -560 229 -166 1035 -206 455 -330 1007 -332 469 -198 837 -196 251 -92 471 -264 519 -330 439 -100 419 -132 421 -132 439 -100 453 -230 549 -330 739 -332 769 -302 347 -210 1025 -98 285 -180 603 -416 619 -404 757 -340 305 -226 463 -134 267 -196 313 -192 1175 -512 1563 -522 1279 -288 253 -290 1553 -524 1579 -492 1075 -528 655 -306 343 -188 373 -164 647 -410 457 -170 615 -344 767 -308 469 -100 275 -178 361 -174 621 -416 625 -418 355 -204 593 -446 583 -466 289 -224 355 -202 327 -206 463 -168 287 -106 399 -134 753 -338 757 -326 283 -230 331 -204 1027 -66 277 -210 487 -100 1219 -324 249 -220 1093 -558 229 -132 995 -282 449 -264 1089 -304 475 -132 1017 -446 483 -68 1235 -338 211 -252 515 -102 243 -176 +RAW_Data: 759 -296 497 -132 213 -214 333 -202 329 -204 331 -240 301 -206 369 -170 595 -442 609 -406 605 -440 347 -194 515 -164 211 -154 1041 -198 193 -180 1503 -516 1361 -258 291 -232 1083 -540 1557 -488 1303 -294 253 -288 1549 -524 393 -172 343 -174 533 -132 617 -264 629 -444 479 -200 217 -144 421 -106 1049 -174 243 -140 1025 -172 855 -166 229 -122 801 -314 1045 -134 239 -178 493 -164 217 -124 1047 -164 221 -122 525 -228 225 -100 457 -66 1041 -226 833 -196 1891 -298 523 -302 411 -100 1011 -290 477 -238 1123 -298 491 -66 1033 -478 1797 -320 225 -162 1001 -242 455 -300 1035 -296 231 -332 767 -302 345 -188 369 -194 567 -452 473 -172 233 -160 471 -208 279 -70 483 -342 605 -432 1511 -172 501 -352 1255 -526 1545 -514 1329 -298 263 -266 1063 -538 1553 -488 615 -310 385 -170 391 -136 621 -404 647 -410 389 -160 405 -132 997 -134 279 -178 987 -134 893 -166 241 -154 409 -130 667 -384 477 -200 613 -308 747 -306 483 -134 283 -146 377 -156 779 -298 795 -304 311 -188 797 -302 505 -502 815 -268 301 -240 305 -240 305 -242 271 -236 561 -470 777 -292 283 -260 1285 -298 267 -274 1573 -520 1483 -70 237 -322 219 -126 991 -314 449 -264 1045 -330 235 -304 1307 -340 209 -244 985 -100 475 -430 603 -426 347 -188 375 -176 513 -168 605 -288 367 -170 1365 -268 1527 -516 1471 -138 497 -386 1233 -486 1571 -514 1339 -262 291 -264 303 -244 821 -208 555 -496 543 -464 545 -98 249 -142 417 -140 1043 -140 241 -166 597 -424 813 -280 519 -100 675 -268 601 -442 515 -132 251 -144 367 -168 1053 -132 215 -178 387 -140 513 -206 211 -108 513 -238 1305 -188 867 -190 213 -94 1043 -280 1013 -276 2453 -282 1009 -262 787 -260 515 -260 1055 -232 225 -62 471 -100 489 -292 491 -196 1095 -298 233 -302 1329 -340 245 -176 1017 -136 459 -356 1043 -198 235 -320 1559 -550 1509 -206 253 -284 1277 -296 231 -298 1365 -276 1527 -528 1473 -100 505 -416 1195 -524 553 -302 739 -334 469 -196 251 -92 499 -234 263 -310 677 -324 1031 -264 165 -132 471 -568 201 -338 473 -558 499 -554 231 -300 233 -302 231 -300 497 -522 261 -292 229 -298 271 -306 239 -306 241 -274 273 -270 233 -298 231 -298 497 -556 493 -518 263 -294 265 -304 241 -276 273 -270 497 -520 261 -290 225 -298 267 -270 505 -558 233 -298 271 -274 505 -522 491 -550 487 -546 227 -286 259 -296 301 -114218 231 -100 97 -402 397 -298 99 -100 363 -164 201 -132 699 -100 1193 -100 199 -100 167 -66 429 -200 99 -1060 99 -400 99 -332 65 -168 +RAW_Data: 199 -232 199 -100 99 -630 67 -962 231 -198 65 -366 427 -100 231 -102 299 -100 299 -66 593 -132 63 -98 457 -426 165 -724 261 -98 163 -164 263 -98 99 -230 195 -862 65 -132 133 -498 131 -266 65 -66 197 -298 663 -298 829 -132 299 -460 197 -930 99 -66 399 -132 99 -960 165 -134 231 -132 99 -66 395 -726 67 -132 99 -132 231 -132 597 -98 593 -66 133 -198 1131 -98 267 -298 97 -724 525 -196 623 -132 199 -98 295 -98 65 -98 97 -66 197 -264 497 -98 163 -134 233 -100 297 -198 131 -568 297 -100 99 -66 99 -98 465 -100 97 -100 199 -398 1051 -1046 951 -1046 967 -1018 1009 -1004 1003 -1012 993 -1046 237 -288 465 -554 245 -290 253 -286 247 -276 509 -558 497 -554 497 -522 523 -518 259 -254 519 -520 263 -292 263 -266 269 -306 239 -308 241 -274 273 -270 499 -520 487 -550 261 -288 493 -554 231 -296 267 -306 241 -276 271 -270 233 -298 495 -522 261 -294 493 -556 497 -554 495 -554 231 -296 265 -266 497 -524 261 -290 227 -298 267 -268 505 -558 231 -332 239 -272 507 -522 521 -518 487 -550 259 -286 227 -296 271 -304 241 -308 241 -274 507 -522 261 -258 259 -296 265 -266 269 -270 271 -272 271 -308 241 -274 507 -522 489 -548 259 -256 259 -296 499 -558 229 -298 269 -306 241 -274 999 -1012 981 -1026 975 -1024 1011 -980 995 -1012 1019 -1012 261 -274 501 -514 259 -282 253 -292 265 -268 507 -556 497 -556 497 -522 523 -520 227 -286 519 -520 261 -292 261 -266 271 -308 239 -306 241 -274 273 -270 497 -520 489 -550 261 -288 493 -556 231 -294 267 -304 241 -274 273 -272 233 -298 495 -520 261 -292 495 -558 493 -556 493 -552 231 -298 265 -268 499 -522 261 -290 227 -296 267 -270 505 -560 231 -298 269 -274 505 -524 489 -552 487 -550 227 -318 225 -298 269 -304 241 -306 241 -276 507 -522 261 -256 259 -296 265 -268 269 -270 271 -304 239 -308 241 -274 507 -524 487 -550 259 -254 259 -296 499 -558 231 -298 267 -304 241 -276 999 -1014 979 -1016 1005 -988 1007 -1014 1001 -1014 993 -1012 273 -288 465 -552 247 -290 251 -286 247 -278 507 -556 495 -556 497 -522 527 -518 227 -286 517 -522 261 -292 229 -300 269 -304 241 -306 241 -276 273 -270 497 -520 487 -550 261 -290 491 -556 231 -296 267 -304 241 -274 273 -270 233 -298 495 -522 261 -292 495 -556 497 -552 495 -554 231 -296 265 -268 499 -522 259 -292 227 -296 267 -268 505 -560 231 -300 271 -274 505 -524 489 -550 485 -550 227 -318 225 -298 269 -304 241 -308 241 -274 +RAW_Data: 507 -522 261 -258 259 -296 231 -302 267 -270 271 -304 239 -308 241 -276 505 -524 487 -550 227 -288 257 -296 499 -558 229 -298 269 -304 241 -276 997 -1012 981 -1026 977 -1026 977 -1014 1001 -1014 993 -1046 235 -288 495 -528 239 -326 219 -290 253 -288 485 -564 501 -556 497 -522 493 -552 227 -286 515 -522 261 -292 231 -300 269 -304 239 -308 241 -274 273 -270 499 -520 489 -550 259 -290 489 -556 231 -296 267 -306 239 -276 273 -270 233 -298 497 -520 261 -292 493 -558 495 -554 495 -552 231 -298 233 -300 497 -522 261 -290 227 -296 267 -270 505 -558 231 -334 237 -272 507 -522 491 -552 485 -552 227 -318 225 -296 269 -304 239 -308 241 -276 505 -524 261 -290 227 -294 231 -302 269 -268 271 -304 239 -308 241 -276 507 -524 487 -552 227 -286 259 -294 501 -556 229 -298 269 -306 241 -276 997 -1010 1003 -986 1007 -998 1009 -1008 1001 -986 1001 -1048 227 -276 503 -550 259 -254 257 -298 267 -270 505 -558 497 -554 497 -524 521 -518 229 -286 517 -522 263 -290 263 -266 269 -306 241 -306 241 -274 273 -270 497 -520 521 -516 259 -290 493 -556 231 -296 269 -304 241 -274 273 -270 233 -298 495 -524 261 -290 493 -558 495 -552 497 -552 231 -298 265 -266 497 -522 259 -292 227 -298 267 -270 505 -558 231 -300 269 -274 505 -524 489 -550 487 -550 229 -286 257 -296 269 -304 241 -308 241 -276 507 -520 261 -258 259 -294 267 -266 269 -270 269 -306 239 -306 241 -276 507 -524 489 -548 259 -256 257 -294 501 -558 231 -296 269 -306 239 -276 999 -1012 979 -1028 975 -1024 1007 -1000 1001 -1000 1007 -1012 259 -280 507 -518 259 -290 225 -298 267 -270 503 -558 497 -554 497 -524 525 -518 227 -286 517 -522 261 -292 263 -266 269 -306 239 -308 241 -276 271 -270 499 -520 487 -550 259 -288 493 -556 229 -298 267 -304 241 -274 273 -270 235 -298 495 -520 261 -292 495 -556 495 -556 493 -554 231 -298 233 -300 497 -520 261 -290 227 -296 267 -270 505 -558 233 -298 271 -272 507 -522 491 -550 485 -550 227 -318 227 -296 271 -304 239 -308 241 -276 507 -522 261 -258 259 -294 265 -268 267 -270 271 -304 239 -308 241 -274 507 -524 489 -550 227 -286 257 -296 499 -558 231 -298 267 -304 241 -274 999 -1010 1001 -1004 983 -1004 1011 -1008 1003 -998 1003 -1018 257 -282 509 -518 259 -290 225 -298 267 -270 503 -560 497 -558 497 -520 521 -520 227 -286 517 -522 259 -292 263 -266 271 -304 239 -308 241 -276 273 -268 499 -522 487 -550 259 -288 491 -556 229 -298 267 -304 241 -274 +RAW_Data: 273 -272 233 -296 495 -522 261 -294 493 -556 493 -554 495 -556 229 -298 265 -266 499 -520 261 -290 227 -296 267 -270 505 -558 233 -298 271 -272 507 -524 489 -548 489 -550 227 -316 227 -296 269 -306 239 -308 241 -274 507 -524 259 -258 259 -294 265 -266 269 -270 271 -304 239 -308 239 -276 507 -524 489 -550 227 -286 257 -294 501 -556 231 -296 269 -306 239 -276 999 -1012 981 -1018 1005 -980 1009 -1018 1003 -1008 977 -1038 255 -276 499 -516 259 -282 253 -290 265 -268 505 -556 499 -554 497 -520 525 -518 227 -288 517 -522 261 -292 229 -300 269 -304 239 -308 241 -276 273 -268 497 -522 489 -550 261 -288 489 -556 229 -296 267 -304 241 -276 271 -270 233 -300 495 -522 261 -292 495 -556 495 -552 495 -554 229 -298 231 -300 499 -522 261 -290 227 -296 267 -268 507 -558 231 -298 271 -272 505 -524 489 -552 487 -550 227 -318 225 -296 267 -304 239 -308 241 -274 507 -524 261 -290 227 -296 231 -300 269 -270 269 -306 239 -306 241 -274 505 -524 489 -550 227 -320 225 -296 499 -556 231 -298 267 -306 241 -274 999 -1012 979 -1016 1005 -982 1009 -1014 1003 -1010 979 -1044 237 -292 473 -552 247 -284 245 -276 277 -276 501 -546 515 -548 485 -550 487 -548 225 -286 515 -520 263 -290 263 -266 269 -306 239 -306 243 -274 273 -270 495 -520 491 -548 261 -288 491 -554 231 -296 267 -304 241 -276 273 -270 233 -298 495 -522 261 -290 495 -556 495 -520 527 -554 229 -298 267 -268 497 -522 259 -258 259 -296 269 -270 505 -558 231 -300 269 -274 507 -522 521 -514 489 -548 259 -256 289 -264 271 -272 273 -308 241 -276 505 -522 259 -258 257 -296 267 -266 271 -270 269 -272 273 -306 241 -274 507 -522 521 -516 259 -254 259 -296 501 -556 231 -296 269 -306 241 -276 1001 -1010 979 -1016 1003 -986 1009 -1014 1001 -1006 1011 -1006 253 -276 497 -516 259 -282 255 -292 267 -270 507 -556 497 -554 499 -522 523 -518 259 -254 517 -522 261 -292 265 -266 269 -272 273 -308 243 -274 273 -268 497 -522 521 -516 259 -288 491 -554 231 -298 267 -306 241 -276 273 -270 231 -298 493 -522 261 -292 495 -556 495 -520 527 -554 231 -296 267 -268 497 -520 261 -290 229 -296 267 -270 505 -558 233 -298 271 -272 507 -524 489 -550 487 -548 259 -252 291 -264 271 -272 273 -308 241 -274 507 -520 261 -258 259 -296 265 -266 269 -270 271 -270 273 -308 241 -276 505 -524 487 -548 259 -256 255 -298 501 -556 231 -298 267 -306 241 -276 999 -1004 1007 -988 1009 -998 1011 -1012 991 -982 1025 -1010 +RAW_Data: 269 -290 465 -554 247 -290 251 -286 247 -278 505 -556 497 -554 497 -522 525 -516 227 -286 519 -522 261 -292 261 -266 271 -304 239 -308 241 -276 273 -270 497 -520 489 -550 259 -256 521 -556 231 -296 269 -306 241 -274 273 -270 233 -296 495 -522 261 -292 495 -556 495 -520 527 -554 231 -296 267 -266 499 -518 261 -290 229 -298 267 -268 507 -556 233 -298 271 -272 507 -526 487 -550 485 -548 259 -254 291 -264 271 -272 275 -308 241 -274 505 -522 259 -258 259 -296 265 -268 267 -270 271 -272 273 -274 273 -276 507 -520 521 -516 259 -254 291 -262 501 -556 231 -298 269 -306 241 -276 1001 -1010 979 -1018 1007 -982 1009 -1016 999 -984 1019 -1008 257 -274 499 -548 257 -254 257 -296 267 -270 505 -558 497 -554 499 -520 523 -516 259 -256 517 -524 261 -290 263 -266 271 -270 275 -274 275 -274 273 -268 499 -518 519 -516 261 -288 493 -524 263 -296 269 -270 275 -276 271 -270 233 -298 495 -518 263 -292 493 -524 529 -522 527 -522 265 -294 265 -266 497 -520 261 -258 293 -264 267 -270 507 -556 267 -264 271 -274 507 -522 523 -516 485 -548 259 -254 291 -264 271 -272 275 -274 275 -276 507 -520 259 -256 257 -296 265 -268 271 -270 271 -272 273 -272 275 -276 505 -520 523 -514 257 -256 289 -264 503 -524 263 -296 269 -272 275 -276 999 -1004 1001 -998 1019 -972 1007 -1006 1001 -996 1009 -1020 257 -280 511 -520 259 -256 293 -262 269 -272 505 -556 497 -554 497 -522 523 -518 259 -252 517 -522 261 -292 263 -266 273 -272 275 -272 277 -274 271 -236 529 -522 521 -514 261 -256 523 -522 297 -264 269 -270 275 -276 273 -236 265 -264 529 -520 261 -290 497 -522 527 -520 529 -552 265 -264 267 -234 531 -520 259 -258 291 -264 269 -272 507 -554 265 -266 271 -274 507 -522 519 -514 521 -514 259 -256 291 -266 271 -272 275 -274 275 -274 507 -522 259 -256 289 -264 265 -270 269 -270 269 -272 273 -274 275 -276 505 -522 519 -514 259 -254 291 -264 501 -522 299 -264 269 -272 277 -276 999 -978 1045 -958 1011 -1024 1007 -978 1019 -978 1013 -1008 271 -292 503 -490 283 -288 249 -278 273 -268 501 -522 529 -520 531 -520 525 -516 257 -254 519 -520 263 -292 265 -266 271 -274 273 -274 275 -276 269 -236 529 -520 521 -516 259 -256 523 -522 297 -264 269 -272 277 -274 273 -236 265 -262 527 -522 259 -292 495 -524 529 -520 529 -518 297 -264 267 -234 531 -520 259 -256 293 -264 269 -270 505 -556 267 -264 271 -274 507 -520 521 -518 517 -514 259 -256 293 -266 271 -270 +RAW_Data: 275 -306 241 -276 507 -520 259 -258 257 -296 267 -266 269 -268 273 -272 273 -274 275 -274 505 -524 521 -514 259 -254 289 -264 501 -556 231 -298 269 -270 277 -276 997 -980 1011 -996 1009 -990 1045 -980 999 -1010 1015 -1008 271 -256 501 -536 251 -272 245 -276 277 -286 499 -558 499 -554 497 -522 523 -516 259 -254 517 -522 261 -292 265 -266 271 -272 273 -274 275 -274 271 -236 531 -520 521 -516 259 -256 523 -524 263 -296 269 -270 277 -276 273 -236 265 -262 527 -520 263 -292 495 -556 497 -518 527 -552 263 -266 267 -268 497 -520 261 -258 291 -264 269 -268 507 -554 267 -264 271 -276 507 -522 521 -514 519 -516 259 -254 293 -264 271 -272 273 -274 275 -274 507 -522 259 -256 291 -262 265 -268 271 -270 271 -272 273 -272 275 -276 505 -520 521 -516 259 -254 289 -264 501 -526 263 -298 267 -272 275 -276 999 -1004 1003 -986 1007 -1000 1013 -1010 985 -1014 1015 -1006 275 -254 501 -524 283 -256 251 -284 275 -272 501 -554 499 -554 497 -520 525 -516 259 -254 517 -520 261 -292 263 -268 271 -274 273 -274 275 -276 271 -236 529 -520 521 -516 259 -256 523 -524 295 -264 269 -272 277 -274 273 -270 231 -264 527 -522 263 -258 527 -522 527 -520 529 -552 265 -266 265 -268 497 -520 259 -258 293 -262 269 -272 507 -556 265 -264 271 -276 507 -520 521 -516 519 -514 259 -254 293 -264 271 -272 277 -274 275 -274 507 -522 259 -256 257 -294 267 -268 269 -270 271 -272 273 -274 275 -274 507 -522 519 -516 259 -254 289 -264 503 -522 263 -298 267 -272 277 -276 999 -1008 1009 -984 1009 -984 1005 -1012 999 -1012 1013 -1004 255 -274 499 -544 257 -252 289 -262 269 -270 507 -556 497 -552 497 -520 525 -518 259 -254 519 -520 261 -294 263 -266 271 -272 275 -274 275 -274 273 -234 531 -520 519 -516 261 -256 523 -522 263 -298 269 -272 275 -276 273 -270 231 -264 527 -522 261 -292 493 -524 359 -91592 331 -232 65 -2392 131 -496 329 -2040 231 -66 201 -200 133 -920 65 -232 97 -100 491 -100 195 -198 131 -196 295 -98 197 -66 655 -166 131 -428 99 -296 65 -330 163 -66 163 -98 97 -66 99 -1778 165 -66 195 -854 129 -494 297 -98 97 -66 197 -260 261 -262 165 -132 293 -66 423 -166 9931 -462 67 -762 99 -268 199 -862 65 -760 165 -232 131 -434 133 -1126 363 -132 165 -134 67 -332 97 -466 97 -66 265 -100 133 -100 231 -1590 65 -334 199 -98 67 -1592 201 -100 131 -66 165 -164 97 -1714 327 -66 329 -98 595 -100 993 -132 65 -100 165 -232 65 -100 +RAW_Data: 97 -100 293 -330 99 -98 163 -458 99 -196 131 -66 163 -426 167 -298 99 -462 99 -66 67 -462 229 -66 195 -100 757 -66 425 -66 427 -100 529 -134 265 -1430 131 -302 97 -66 97 -132 65 -66 229 -100 97 -1530 133 -264 563 -66 1165 -200 1025 -198 167 -1294 265 -132 197 -962 131 -300 131 -66 99 -366 165 -198 167 -830 197 -230 195 -434 299 -98 965 -132 265 -894 99 -98 65 -860 461 -66 97 -398 67 -928 165 -200 199 -66 199 -1456 197 -334 133 -132 501 -198 1579 -66 1151 -132 463 -98 10275 -528 97 -966 263 -760 65 -264 197 -134 65 -512 479 -526 455 -572 443 -572 437 -550 451 -548 447 -552 481 -526 457 -542 483 -538 469 -548 449 -548 447 -554 449 -1048 975 -558 463 -548 451 -1046 449 -554 953 -1054 981 -540 443 -550 475 -1014 985 -1052 981 -516 475 -1048 473 -520 487 -534 463 -548 943 -1044 969 -554 449 -558 465 -1042 979 -520 485 -514 473 -548 453 -1042 973 -1048 981 -1014 483 -546 971 -522 473 -556 445 -1032 493 -51306 533 -514 457 -546 451 -550 477 -522 475 -542 471 -554 453 -538 467 -548 451 -546 447 -550 477 -524 473 -554 451 -540 469 -1044 977 -554 451 -546 441 -1042 483 -514 975 -1048 977 -522 493 -516 481 -1046 977 -1018 979 -552 453 -1042 481 -520 485 -516 471 -550 983 -1014 981 -546 477 -518 489 -1012 975 -554 451 -544 467 -550 451 -1042 975 -1050 949 -1046 483 -548 941 -554 483 -514 469 -1046 447 -51364 513 -518 451 -554 451 -544 469 -548 451 -546 481 -514 475 -522 475 -554 443 -572 443 -548 453 -546 479 -514 477 -554 447 -1050 975 -522 493 -512 483 -1040 471 -538 969 -1032 985 -534 467 -1046 977 -528 485 -1016 965 -1042 481 -524 489 -522 973 -524 493 -514 481 -1040 969 -1050 979 -1016 481 -514 475 -552 977 -522 479 -1028 493 -512 481 -546 975 -520 485 -514 473 -548 453 -544 481 -1016 981 -51834 519 -526 465 -514 481 -546 479 -514 473 -522 471 -558 487 -514 469 -548 451 -546 479 -514 475 -522 471 -538 471 -536 485 -1048 961 -546 445 -552 483 -1016 479 -536 965 -1042 977 -534 475 -1042 465 -548 979 -1014 485 -516 977 -1042 479 -514 1005 -1018 975 -1018 483 -544 975 -520 473 -1058 471 -516 975 -548 453 -546 479 -516 479 -522 471 -1052 985 -520 489 -1006 1005 -528 459 -1050 479 -518 489 -51332 483 -542 453 -546 445 -550 477 -522 483 -554 449 -554 461 -514 483 -548 445 -552 441 -588 447 -548 441 -550 455 -548 481 -1042 967 -540 441 -552 481 -1024 489 -514 975 -1034 971 -1050 973 -548 451 -548 447 -1048 485 -540 +RAW_Data: 453 -542 467 -548 945 -1048 981 -544 449 -1056 453 -546 481 -516 973 -554 449 -544 469 -1046 975 -526 487 -508 485 -1038 489 -512 483 -516 479 -522 1001 -508 483 -1036 983 -514 475 -51832 491 -574 411 -604 399 -618 389 -618 385 -622 383 -610 415 -578 411 -602 437 -552 455 -550 449 -554 445 -584 445 -546 441 -1046 981 -522 485 -546 447 -1054 451 -546 973 -1052 953 -1046 965 -1042 977 -556 451 -542 467 -514 483 -1040 969 -1050 479 -532 961 -546 445 -552 481 -520 485 -516 473 -548 453 -546 481 -516 481 -522 475 -1046 481 -532 961 -1044 481 -530 461 -554 449 -524 987 -1040 969 -1052 449 -51326 525 -532 465 -548 451 -548 445 -552 443 -586 431 -546 477 -524 491 -514 481 -546 445 -552 441 -554 485 -516 473 -548 487 -1010 977 -554 451 -540 469 -1042 475 -508 1007 -1034 967 -1042 477 -518 983 -516 479 -524 487 -536 473 -1018 481 -526 987 -540 479 -516 475 -524 485 -540 453 -544 469 -1044 975 -526 483 -1014 505 -518 981 -1010 473 -556 485 -514 469 -548 451 -546 971 -542 483 -520 485 -1010 493 -512 975 -51842 491 -606 339 -662 333 -652 357 -654 385 -628 357 -642 377 -604 403 -622 391 -584 417 -590 421 -614 409 -574 439 -554 421 -1114 909 -578 415 -578 449 -1094 423 -550 975 -1034 969 -1038 477 -522 493 -514 975 -554 483 -1012 967 -1048 977 -518 481 -1028 987 -510 479 -1034 1005 -520 485 -534 465 -1014 1007 -492 517 -1012 999 -514 483 -1016 487 -520 975 -1060 949 -1050 457 -550 479 -530 467 -51296 517 -552 451 -578 421 -570 447 -572 407 -592 427 -584 417 -586 417 -578 415 -576 445 -576 447 -546 447 -568 465 -550 453 -1046 977 -510 467 -542 479 -1050 449 -542 973 -1052 483 -518 973 -514 483 -546 447 -552 483 -520 471 -1036 471 -516 1011 -1012 487 -506 1017 -510 471 -548 451 -1042 475 -510 483 -542 487 -506 983 -1036 987 -1046 975 -1020 979 -516 483 -1042 973 -1028 997 -512 471 -550 487 -51298 65 -1910 229 -230 99 -66 165 -922 97 -164 197 -66 131 -426 65 -68 231 -264 95 -66 361 -196 1023 -66 227 -98 195 -66 229 -98 4711 -100 233 -98 99 -66 1025 -66 10443 -130 233 -168 465 -66 2057 -100 6537 -66 821 -132 1841 -98 165 -98 7675 -66 1343 -232 67 -132 131 -134 329 -100 365 -132 3561 -66 297 -134 1691 -100 861 -66 497 -66 1359 -66 197 -198 429 -66 929 -66 2675 -100 261 -98 99 -98 459 -166 197 -66 195 -130 625 -98 295 -132 455 -66 391 -130 197 -132 5171 -66 1597 -66 1085 -68 1025 -66 565 -100 131 -132 4741 -66 559 -66 2343 -98 +RAW_Data: 4573 -66 2455 -98 2333 -130 5301 -66 531 -66 1895 -66 163 -68 5167 -98 591 -100 1521 -166 7219 -100 2149 -100 1443 -98 1149 -66 9647 -66 5157 -100 1321 -164 917 -100 2083 -132 7551 -134 165 -68 1169 -100 1263 -68 3619 -166 963 -100 1229 -198 2393 -68 1661 -98 2109 -66 131 -68 299 -100 5029 -66 957 -100 1185 -66 1653 -98 4925 -66 1201 -66 1827 -96 163 -132 501 -66 165 -66 763 -100 163 -98 821 -66 4555 -264 1353 -132 499 -100 163 -66 1749 -100 1063 -132 1577 -100 763 -68 331 -102 299 -100 429 -66 265 -234 331 -66 1963 -68 627 -132 231 -102 763 -66 497 -66 265 -68 2061 -68 463 -66 297 -100 1393 -430 295 -100 823 -98 97 -100 631 -102 529 -66 1251 -68 1195 -66 361 -66 195 -100 4929 -134 231 -100 561 -66 829 -68 893 -66 6729 -66 901 -68 1259 -66 65 -166 131 -232 65 -100 131 -132 131 -100 131 -564 661 -98 329 -66 199 -66 263 -66 165 -68 97 -68 363 -566 197 -1064 395 -862 133 -200 99 -198 199 -596 229 -994 295 -928 163 -130 395 -98 229 -66 99 -98 997 -66 133 -566 165 -232 99 -100 99 -100 99 -530 99 -66 133 -66 165 -764 99 -398 199 -296 165 -96 129 -328 131 -560 227 -298 99 -100 465 -100 397 -66 67 -98 331 -132 199 -232 201 -438 263 -166 329 -166 463 -632 131 -398 99 -134 131 -1590 263 -66 67 -196 163 -298 195 -1220 227 -292 297 -132 591 -298 295 -100 493 -66 163 -162 325 -134 197 -64 197 -164 97 -460 65 -166 195 -266 97 -164 327 -264 229 -234 165 -66 163 -630 299 -532 197 -796 131 -100 65 -466 65 -98 99 -328 361 -66 197 -130 295 -66 197 -692 99 -166 99 -166 99 -66 131 -100 99 -66 165 -134 233 -130 267 -498 265 -66 167 -1192 67 -364 463 -132 133 -300 197 -100 99 -298 629 -66 331 -132 629 -134 65 -132 231 -100 65 -1392 65 -166 131 -230 199 -466 97 -100 233 -730 263 -296 99 -98 133 -100 163 -134 67 -100 131 -98 229 -100 231 -132 497 -264 989 -164 233 -98 65 -102 495 -264 197 -826 99 -496 133 -132 365 -64 131 -66 99 -98 65 -132 65 -100 329 -66 165 -100 131 -700 165 -492 231 -130 195 -130 131 -230 197 -200 691 -132 99 -66 331 -264 165 -100 131 -730 131 -200 131 -498 99 -100 197 -100 131 -66 165 -68 165 -534 65 -498 401 -166 65 -592 65 -98 65 -100 131 -66 231 -132 133 -100 299 -198 133 -164 265 -100 433 -66 429 -330 631 -98 163 -66 265 -232 65 -66 199 -100 165 -198 233 -66 131 -234 67 -564 +RAW_Data: 163 -400 165 -464 131 -362 561 -332 263 -98 133 -132 165 -100 661 -232 591 -328 65 -98 133 -368 131 -560 97 -100 165 -464 131 -296 129 -558 131 -98 99 -196 65 -98 163 -100 97 -394 261 -558 259 -98 97 -426 299 -364 365 -66 599 -166 363 -428 231 -134 625 -98 359 -394 195 -164 197 -66 97 -98 263 -490 195 -98 261 -460 163 -100 199 -330 131 -198 163 -66 293 -66 885 -100 1315 -234 333 -234 365 -200 133 -596 197 -166 295 -100 197 -232 165 -66 131 -336 197 -268 231 -100 99 -696 163 -262 331 -132 197 -132 131 -132 361 -100 525 -66 195 -132 227 -232 163 -262 1233 -68 797 -98 427 -98 231 -200 331 -132 129 -98 163 -66 129 -100 97 -100 5243 -198 1295 -66 197 -66 165 -66 499 -23298 65 -68 131 -564 329 -100 133 -298 101 -166 199 -300 65 -764 133 -266 297 -130 165 -132 265 -66 2793 -198 4161 -66 1747 -100 1729 -98 603 -132 363 -530 99 -266 129 -100 231 -396 129 -198 133 -98 991 -132 397 -462 131 -330 295 -132 197 -134 99 -262 99 -232 229 -268 263 -1448 229 -68 65 -198 99 -134 165 -996 429 -396 631 -632 727 -98 297 -100 199 -134 65 -332 65 -234 165 -362 99 -428 263 -232 67 -1158 165 -196 129 -228 131 -132 195 -98 163 -698 65 -234 531 -728 295 -130 97 -98 229 -366 499 -66 99 -66 131 -66 297 -232 165 -100 65 -400 233 -66 265 -134 131 -134 165 -132 233 -134 97 -300 397 -102 65 -98 99 -98 6595 -132 65 -164 131 -100 529 -66 2157 -66 163 -100 99 -466 99 -530 163 -166 195 -264 197 -66 199 -66 199 -100 11433 -164 1055 -562 497 -134 233 -100 399 -100 131 -66 163 -234 733 -264 163 -236 65 -430 67 -332 133 -132 133 -66 465 -66 603 -98 265 -98 199 -66 3567 -100 391 -166 525 -132 1055 -98 627 -264 333 -100 795 -66 165 -66 297 -66 393 -134 263 -100 1317 -98 131 -98 529 -98 293 -66 99 -66 163 -100 493 -64 265 -164 231 -100 199 -132 265 -66 265 -100 99 -134 199 -132 431 -100 365 -132 231 -68 201 -132 891 -100 831 -98 167 -100 1191 -64 329 -66 393 -98 131 -64 131 -98 229 -132 2693 -132 10455 -66 297 -98 631 -100 431 -100 433 -98 165 -134 65 -100 65 -68 863 -132 499 -100 267 -134 99 -66 989 -66 657 -100 757 -594 395 -66 199 -66 463 -330 663 -264 4219 -66 851 -98 129 -132 915 -66 129 -66 363 -98 99 -100 699 -66 4847 -66 2153 -134 1993 -66 5935 -66 1435 -100 2119 -66 9535 -296 197 -624 131 -1446 129 -362 65 -198 +RAW_Data: 67 -298 197 -226 131 -164 595 -66 463 -98 131 -198 395 -262 131 -66 1773 -516 455 -548 481 -516 479 -556 451 -554 451 -542 469 -548 451 -546 447 -552 449 -554 471 -542 477 -518 489 -512 481 -1044 977 -506 485 -540 469 -1044 481 -518 975 -1050 947 -548 489 -512 481 -1042 967 -1020 979 -550 455 -1038 477 -520 471 -556 483 -514 969 -1044 977 -536 473 -544 449 -1054 955 -552 447 -554 485 -514 471 -1046 975 -1020 485 -518 1003 -512 483 -548 447 -554 447 -556 485 -1010 491 -51312 527 -512 453 -544 477 -540 477 -514 473 -538 473 -524 471 -558 453 -542 467 -514 485 -546 479 -514 473 -522 471 -534 501 -1014 1001 -512 481 -514 479 -1052 459 -544 979 -1016 1009 -516 473 -522 469 -1044 995 -1010 1007 -524 475 -1004 495 -512 483 -544 479 -514 1003 -1018 977 -522 489 -512 479 -1034 1003 -522 487 -512 467 -514 483 -1044 975 -1020 485 -524 991 -514 477 -550 473 -524 473 -536 471 -1030 489 -51306 517 -520 493 -512 483 -546 445 -550 477 -520 469 -544 479 -518 495 -512 481 -514 475 -550 477 -526 471 -538 473 -538 471 -1018 979 -548 451 -546 479 -1010 473 -554 965 -1034 989 -516 479 -1048 967 -554 451 -1022 977 -1034 475 -554 473 -510 1003 -512 481 -546 443 -1064 969 -1034 977 -1020 483 -546 477 -516 967 -538 473 -1052 449 -562 461 -514 481 -546 477 -514 477 -522 999 -510 485 -1038 985 -51816 479 -3892 63 -722 289 -742 265 -734 271 -762 231 -722 291 -720 321 -644 373 -642 339 -626 395 -1114 911 -610 415 -580 413 -1098 425 -548 979 -1054 955 -544 443 -1080 449 -548 939 -1054 473 -550 949 -1048 481 -514 973 -1050 975 -1050 453 -544 971 -538 473 -1032 471 -538 967 -554 449 -524 495 -512 481 -546 479 -1008 473 -556 487 -512 965 -546 481 -516 473 -1036 487 -522 483 -97226 691 -134 1391 -396 231 -166 1025 -98 199 -132 99 -266 395 -100 165 -132 231 -98 689 -66 493 -98 65 -98 557 -134 365 -68 131 -164 425 -132 259 -98 1255 -232 195 -66 495 -64 1651 -100 199 -100 1099 -100 331 -132 265 -168 6433 -66 28525 -100 395 -66 395 -100 259 -164 559 -100 463 -66 327 -66 131 -100 685 -66 395 -98 199 -100 431 -66 101 -100 231 -66 333 -66 399 -266 165 -68 99 -100 165 -398 299 -98 197 -824 97 -132 229 -66 295 -264 97 -230 131 -394 469 -266 397 -198 165 -270 133 -562 165 -66 229 -266 99 -232 363 -530 427 -730 99 -166 97 -534 297 -100 297 -134 133 -132 67 -100 67 -66 361 -430 397 -330 65 -198 165 -200 167 -68 131 -132 199 -68 233 -100 +RAW_Data: 99 -234 65 -300 263 -562 163 -66 199 -166 165 -132 99 -264 131 -262 261 -64 459 -66 427 -132 65 -132 131 -100 97 -228 163 -164 523 -328 297 -66 131 -332 199 -266 65 -134 165 -66 197 -68 697 -98 165 -134 233 -496 231 -164 63 -66 197 -264 297 -166 99 -198 131 -100 365 -266 299 -232 99 -66 627 -132 1415 -100 195 -198 165 -98 327 -98 523 -98 131 -228 229 -230 295 -694 499 -100 531 -66 131 -134 165 -300 165 -132 265 -300 997 -102 1921 -134 723 -98 195 -132 99 -164 163 -68 397 -100 331 -132 165 -266 467 -232 131 -66 465 -198 463 -132 165 -66 329 -100 2691 -100 431 -166 397 -100 199 -134 529 -100 1053 -66 18073 -98 1251 -130 1425 -66 497 -98 6625 -10518 1205 -578 607 -588 599 -612 597 -586 605 -582 587 -1214 1171 -1180 1207 -582 609 -588 613 -578 613 -592 611 -576 613 -1184 583 -580 1205 -1206 1169 -614 577 -590 629 -576 601 -1182 609 -598 1177 -578 627 -588 603 -1178 1207 -582 589 -1178 1209 -1178 613 -588 613 -576 1215 -1174 1181 -1204 1183 -604 579 -1218 1175 -604 579 -1186 1225 -562 615 -1184 611 -578 1207 -578 605 -1184 609 -568 1209 -580 629 -582 603 -574 605 -580 603 -612 577 -610 603 -77146 1139 -774 423 -1374 419 -716 495 -708 493 -682 1089 -1308 1095 -688 519 -638 539 -656 551 -638 539 -656 549 -638 575 -622 551 -1206 1179 -1204 585 -616 577 -620 1189 -594 581 -602 581 -1186 603 -614 1169 -1200 1207 -1176 613 -592 1181 -600 581 -1214 583 -580 611 -588 615 -576 615 -588 1173 -1212 579 -590 613 -590 587 -614 1169 -1212 579 -612 591 -580 1201 -588 615 -578 613 -1180 587 -616 1169 -1210 1201 -580 579 -622 591 -1170 1203 -612 575 -614 593 -77174 1201 -608 589 -1210 583 -622 549 -606 585 -626 1169 -1204 579 -608 1205 -578 601 -586 621 -582 589 -614 575 -1214 575 -620 563 -612 1193 -592 579 -1204 1183 -606 577 -1224 559 -614 1195 -588 611 -1182 581 -606 1205 -1176 611 -586 579 -628 1173 -1208 581 -604 1167 -616 571 -1216 1171 -618 585 -1210 1177 -1208 581 -586 1205 -580 589 -616 571 -1212 1171 -616 587 -1206 573 -594 615 -588 589 -614 1169 -618 583 -1212 1177 -578 609 -590 613 -578 611 -77168 1211 -612 565 -1242 549 -628 577 -620 583 -586 1173 -616 587 -612 587 -588 615 -578 611 -586 579 -628 579 -620 585 -1180 1197 -1194 1183 -600 583 -622 583 -1184 1193 -1180 1197 -604 577 -1214 585 -614 1169 -1210 1165 -614 577 -622 591 -580 603 -1182 609 -602 1173 -1204 577 -598 1203 -610 571 -616 575 -1202 581 -608 611 -592 577 -602 1203 -1172 611 -588 611 -578 611 -592 +RAW_Data: 1171 -622 591 -580 603 -1180 1207 -580 599 -1208 579 -622 1173 -612 575 -590 613 -123688 65 -3788 133 -896 65 -168 65 -926 297 -168 65 -100 365 -66 297 -98 201 -1162 131 -428 195 -66 297 -166 99 -760 99 -196 131 -132 65 -98 197 -66 493 -264 99 -264 821 -66 793 -100 3007 -98 2067 -66 2411 -430 97 -98 197 -130 297 -230 465 -98 4123 -100 391 -132 163 -66 751 -164 161 -990 759 -64 1055 -264 129 -166 429 -66 3943 -66 399 -100 665 -432 431 -132 99 -132 97 -68 97 -330 295 -164 131 -66 291 -66 589 -264 563 -100 1227 -166 233 -298 699 -100 863 -534 131 -332 1619 -100 1595 -132 2191 -164 465 -66 233 -68 299 -66 561 -134 369 -68 233 -134 297 -168 463 -166 795 -100 265 -68 131 -100 3629 -66 227 -66 853 -66 529 -100 1191 -98 501 -132 1417 -64 229 -132 1115 -66 463 -66 231 -64 361 -66 231 -98 989 -98 265 -300 1125 -66 897 -68 197 -100 529 -66 787 -164 163 -66 2705 -66 1899 -132 929 -64 1197 -132 4387 -98 263 -96 261 -100 751 -66 229 -10520 1245 -566 609 -564 615 -572 613 -590 615 -594 589 -1174 1203 -1204 1195 -564 615 -592 617 -580 611 -560 627 -576 603 -1186 609 -564 1213 -1176 1211 -578 623 -588 611 -1176 1205 -582 583 -582 613 -588 611 -578 625 -1152 1205 -592 615 -1176 1217 -1172 601 -576 605 -590 1193 -1186 1191 -1184 1227 -576 615 -1150 1209 -604 613 -1152 1227 -576 591 -1188 613 -578 1207 -576 607 -1184 607 -600 1179 -578 629 -588 607 -582 587 -1182 613 -586 1209 -77154 1187 -666 541 -1242 523 -676 535 -618 553 -644 1163 -1212 1169 -628 577 -620 551 -616 609 -588 589 -614 573 -586 597 -610 599 -1182 1209 -1182 581 -592 613 -604 1169 -1216 577 -604 1175 -1206 1175 -1208 1173 -1206 579 -596 1201 -610 571 -1212 605 -584 587 -610 575 -618 583 -612 1171 -1210 577 -588 613 -580 611 -590 1205 -1170 611 -578 611 -592 1171 -622 591 -578 603 -1182 611 -600 1177 -1206 1177 -610 597 -586 601 -1178 615 -590 589 -578 1205 -77194 1193 -618 583 -1208 575 -624 577 -606 583 -588 1205 -1178 611 -572 1201 -612 575 -588 615 -576 611 -590 579 -1206 585 -616 575 -622 1183 -600 577 -1216 1177 -604 581 -592 611 -608 583 -1178 1197 -1196 573 -616 1199 -1174 611 -586 589 -614 1167 -1214 575 -620 1185 -600 577 -1216 1177 -604 575 -1220 1159 -1222 585 -582 1181 -622 579 -592 611 -1200 1175 -588 585 -1208 605 -576 605 -620 561 -612 1189 -606 581 -1212 1177 -1208 581 -586 1207 -77162 599 -4312 397 -788 425 -772 1027 -722 471 -708 487 -686 503 -678 521 -686 503 -680 523 -652 +RAW_Data: 535 -1274 1109 -1240 1175 -614 559 -650 533 -1240 1177 -616 559 -1208 575 -628 577 -604 577 -620 1175 -1208 1171 -614 605 -578 601 -588 599 -1178 615 -588 1185 -1202 589 -614 1167 -618 583 -612 591 -1186 599 -584 601 -582 591 -618 1199 -1176 603 -580 591 -616 573 -616 1179 -624 581 -584 613 -1184 1173 -622 591 -1172 611 -592 579 -610 613 -588 1173 -122626 229 -330 197 -98 65 -98 97 -100 229 -166 129 -232 65 -66 131 -394 97 -66 97 -66 395 -66 557 -66 657 -64 489 -66 327 -100 65 -98 461 -64 5371 -64 691 -98 859 -66 631 -166 797 -66 299 -132 65 -66 4463 -134 331 -134 233 -132 1495 -264 699 -98 263 -134 1031 -692514 65 -100 97 -1392 131 -398 65 -134 201 -832 4061 -132 523 -66 431 -66 99 -132 99 -100 831 -66 165 -66 1969 -164 8553 -100 297 -68 523 -100 197 -130 957 -98 361 -98 2015 -98 263 -130 195 -130 4497 -66 999 -66 1357 -66 1189 -132 929 -66 4649 -66 263 -66 131 -64 657 -100 195 -68 231 -100 1025 -100 565 -68 931 -98 985 -98 661 -164 363 -100 501 -66 3719 -66 2015 -66 663 -132 299 -330 823 -66 1661 -66 4179 -66 433 -66 1523 -68 697 -68 197 -100 199 -66 1265 -98 629 -68 299 -168 629 -100 3539 -166 331 -66 495 -98 327 -132 589 -166 499 -66 463 -100 331 -66 627 -98 489 -64 129 -130 361 -98 195 -130 65 -100 793 -98 391 -66 1515 -66 129 -66 757 -132 561 -98 653 -64 197 -98 1577 -296 163 -100 789 -66 689 -66 3829 -100 533 -398 467 -166 531 -66 331 -198 753 -64 557 -98 693 -66 165 -100 4907 -134 199 -100 131 -66 333 -198 1345 -66 3781 -98 267 -100 597 -232 1449 -66 723 -330 197 -164 1021 -132 561 -98 591 -100 755 -66 559 -100 229 -228 131 -66 163 -198 689 -100 3365 -100 99 -68 431 -100 797 -66 431 -100 401 -68 397 -134 295 -66 297 -64 65 -100 1085 -132 3135 -66 497 -66 201 -98 99 -168 299 -66 165 -68 267 -366 231 -66 463 -266 733 -132 397 -66 297 -100 4563 -66 163 -66 293 -100 755 -100 697 -100 563 -100 4661 -132 65 -100 97 -494 691 -98 63 -66 459 -232 295 -230 229 -198 495 -66 457 -266 425 -685602 231 -202 133 -66 331 -66 167 -132 699 -98 231 -66 301 -66 299 -596 399 -66 557 -230 7389 -66 733 -98 657 -66 723 -100 789 -68 1661 -132 627 -66 1087 -134 97 -66 5173 -134 693 -134 1261 -132 4435 -66 131 -134 199 -66 891 -66 493 -100 397 -66 365 -100 297 -134 4287 -66 501 -166 1281 -100 195 -100 1151 -100 467 -266 265 -98 +RAW_Data: 2027 -68 131 -66 5771 -98 2171 -130 163 -66 7573 -66 661 -66 653 -66 357 -130 7217 -98 229 -132 393 -66 997 -66 4689 -100 97 -134 9081 -66 163 -132 493 -100 1217 -132 529 -230 131 -66 235 -100 133 -134 1359 -66 5243 -98 329 -66 167 -100 299 -100 431 -66 7349 -132 1059 -66 1393 -100 1317 -66 629 -98 429 -100 495 -100 365 -66 397 -234 1489 -100 4599 -100 399 -100 1855 -68 433 -98 1393 -132 7409 -100 5607 -66 463 -66 665 -68 2553 -134 167 -102 2263 -686936 99 -800 67 -98 99 -66 133 -230 265 -66 3631 -100 229 -130 99 -66 825 -100 99 -232 231 -66 521 -66 593 -98 1413 -130 359 -66 4639 -66 131 -68 97 -202 799 -166 99 -68 365 -66 1595 -66 2987 -66 423 -100 131 -98 791 -132 433 -100 893 -66 4831 -100 101 -98 629 -66 1221 -98 197 -66 625 -98 261 -66 559 -66 665 -66 4409 -66 331 -268 2017 -166 199 -68 333 -98 1553 -68 401 -166 563 -66 393 -100 195 -66 1485 -230 265 -100 4353 -68 431 -100 2383 -66 6737 -98 493 -66 1017 -68 5915 -66 1085 -198 527 -66 4223 -100 163 -64 131 -66 1187 -98 393 -66 1747 -130 263 -66 597 -100 501 -100 431 -132 1491 -100 131 -66 599 -68 2595 -100 497 -68 265 -100 763 -100 265 -100 165 -66 595 -68 331 -100 1587 -66 163 -66 99 -164 559 -130 457 -66 1611 -100 991 -66 4403 -100 1427 -164 565 -132 499 -66 827 -298 299 -100 4637 -166 263 -100 995 -66 1083 -428 365 -424 369 -454 377 -420 377 -418 409 -418 377 -424 381 -436 357 -438 387 -444 383 -414 385 -4058 783 -452 797 -434 811 -412 417 -862 773 -474 779 -484 387 -874 383 -850 403 -822 809 -450 415 -834 807 -444 773 -482 381 -870 787 -478 791 -436 805 -448 803 -420 411 -842 403 -836 415 -838 391 -864 807 -480 395 -850 387 -840 385 -844 391 -858 381 -878 783 -452 795 -464 807 -450 817 -448 377 -858 807 -446 777 -450 789 -454 813 -448 819 -430 809 -448 411 -838 807 -446 385 -862 379 -870 769 -468 801 -446 809 -456 379 -872 409 -858 781 -448 383 -846 409 -846 775 -478 381 -848 415 -858 809 -444 813 -422 393 -868 781 -448 385 -850 409 -860 777 -480 385 -866 805 -444 805 -418 411 -808 391 -15820 421 -372 417 -388 419 -386 437 -394 395 -400 421 -408 385 -412 415 -384 415 -418 383 -420 383 -420 385 -4030 825 -410 845 -420 811 -418 423 -838 821 -420 815 -456 385 -878 419 -816 407 -816 835 -416 409 -842 821 -422 811 -456 387 -876 789 -454 825 -428 801 -414 821 -442 399 -850 381 -852 +RAW_Data: 417 -824 447 -812 809 -462 413 -848 421 -816 399 -822 413 -850 419 -816 803 -440 809 -450 815 -446 821 -456 403 -820 821 -420 819 -418 807 -456 811 -412 821 -454 807 -470 379 -850 823 -416 423 -824 409 -844 789 -450 815 -432 807 -446 409 -838 439 -822 811 -414 409 -844 419 -822 803 -442 413 -848 409 -844 815 -448 811 -424 421 -838 787 -450 411 -810 407 -864 811 -418 415 -866 803 -448 817 -418 419 -776 417 -15782 437 -396 427 -378 407 -410 415 -376 415 -410 419 -388 419 -390 419 -388 411 -382 417 -400 409 -398 423 -4008 821 -420 815 -422 815 -446 405 -830 813 -450 811 -452 379 -876 407 -828 419 -810 813 -458 377 -842 823 -454 783 -458 385 -876 789 -456 827 -432 811 -412 821 -420 435 -820 393 -862 421 -810 429 -852 809 -444 409 -836 421 -812 425 -820 393 -828 421 -846 821 -432 809 -446 821 -448 821 -422 393 -854 787 -454 801 -432 803 -446 803 -446 821 -446 793 -472 381 -840 835 -418 415 -838 399 -820 837 -414 835 -410 835 -418 437 -858 389 -846 817 -432 385 -840 421 -812 827 -432 415 -848 421 -816 809 -472 811 -418 393 -866 785 -450 381 -852 419 -820 807 -440 411 -874 801 -448 803 -426 407 -804 413 -15800 397 -394 435 -388 409 -388 411 -386 447 -382 417 -384 417 -386 419 -388 415 -390 417 -420 385 -420 385 -4032 805 -440 803 -446 801 -416 437 -826 811 -448 809 -454 409 -844 407 -828 419 -834 817 -432 385 -842 819 -418 845 -428 415 -846 787 -456 831 -430 805 -414 821 -454 401 -834 381 -840 409 -866 415 -834 801 -472 379 -842 407 -832 419 -842 387 -832 409 -842 823 -454 785 -462 809 -448 817 -418 419 -846 795 -436 809 -414 829 -428 811 -450 811 -456 809 -444 409 -840 819 -420 383 -852 419 -822 799 -442 807 -452 815 -448 419 -854 413 -814 809 -422 409 -838 421 -844 795 -434 415 -848 419 -818 807 -476 809 -420 393 -834 813 -450 383 -852 417 -824 805 -474 381 -850 821 -454 805 -432 379 -808 417 -119860 67 -134 67 -98 133 -764 65 -398 133 -132 665 -200 597 -64 461 -66 4203 -96 227 -98 655 -98 327 -66 819 -66 589 -132 5777 -66 1495 -328 231 -130 131 -66 463 -66 265 -68 131 -68 429 -66 195 -98 5117 -100 797 -100 299 -166 459 -100 1731 -66 1759 -66 563 -66 825 -98 1081 -68 5321 -232 1291 -134 1955 -100 523 -68 795 -66 767 -66 3589 -66 827 -66 267 -100 2395 -100 4745 -66 2343 -66 1057 -100 1363 -100 6775 -166 329 -66 593 -66 1719 -98 6079 -100 427 -66 523 -66 525 -98 +RAW_Data: 1195 -66 1157 -68 333 -100 533 -66 4957 -134 731 -66 297 -66 97 -98 2989 -66 6633 -66 981 -98 983 -66 993 -98 2015 -132 65 -68 857 -232 723 -66 165 -66 1283 -100 853 -66 5575 -102 627 -98 367 -100 265 -66 853 -66 261 -66 195 -66 751 -66 195 -98 657 -132 953 -66 163 -296 429 -264 263 -66 1511 -98 263 -262 297 -68 263 -66 2523 -66 163 -132 295 -96 1447 -66 199 -66 463 -68 795 -66 1795 -100 701 -66 1333 -66 99 -66 131 -264 1293 -100 99 -66 299 -68 397 -66 399 -66 329 -100 595 -100 795 -66 131 -66 2015 -100 295 -98 2673 -130 1571 -66 753 -66 887 -98 723 -98 821 -100 131 -66 889 -66 793 -66 1057 -132 399 -100 397 -298 165 -66 299 -100 465 -100 233 -132 763 -66 1391 -130 2395 -66 895 -132 99 -134 2029 -166 799 -100 593 -98 195 -64 131 -66 1545 -66 785 -100 97 -98 131 -98 1753 -130 295 -66 619 -198 853 -166 233 -66 1393 -98 1783 -66 291 -132 691 -100 997 -66 133 -332 231 -66 131 -66 525 -66 263 -64 2435 -68 2967 -68 1101 -100 165 -68 599 -66 167 -134 235 -134 297 -68 497 -384 377 -448 377 -418 375 -454 377 -432 369 -424 377 -438 377 -436 377 -430 381 -434 359 -436 387 -4046 383 -852 797 -436 387 -870 385 -848 403 -850 809 -454 379 -876 415 -822 405 -852 381 -846 417 -822 801 -440 415 -846 415 -856 805 -446 383 -874 379 -834 405 -838 813 -460 783 -446 817 -444 793 -472 383 -880 381 -862 773 -442 411 -842 377 -858 413 -846 387 -870 775 -484 771 -480 805 -420 415 -848 795 -438 811 -414 817 -452 803 -470 777 -450 819 -450 417 -822 801 -442 385 -876 385 -848 795 -438 809 -452 819 -450 385 -882 375 -848 811 -422 413 -840 383 -846 823 -434 385 -874 381 -886 771 -476 811 -420 411 -840 809 -450 385 -850 413 -832 809 -444 383 -880 771 -486 803 -430 403 -806 383 -15810 425 -384 401 -420 393 -420 391 -386 421 -422 389 -386 423 -386 423 -388 419 -388 421 -388 421 -388 421 -4026 407 -840 813 -418 411 -842 397 -113844 97 -66 97 -164 99 -328 99 -232 299 -366 99 -1066 165 -66 623 -134 1197 -68 931 -66 599 -66 99 -234 2323 -132 1281 -66 495 -198 555 -414 383 -418 383 -418 417 -386 417 -418 383 -422 375 -422 379 -436 385 -444 351 -446 383 -416 385 -4076 379 -844 421 -812 395 -854 377 -862 813 -452 385 -840 815 -450 799 -488 383 -822 807 -446 803 -420 419 -842 403 -852 779 -450 407 -856 387 -880 805 -430 385 -840 809 -454 385 -856 377 -856 +RAW_Data: 777 -484 783 -454 807 -476 807 -410 797 -454 773 -462 383 -872 789 -454 787 -454 383 -870 797 -474 407 -822 813 -446 775 -448 785 -456 785 -484 783 -452 385 -872 385 -882 777 -442 809 -444 375 -864 777 -450 385 -886 789 -452 783 -470 383 -884 391 -858 779 -448 383 -848 385 -852 411 -856 379 -848 419 -852 777 -504 783 -418 801 -448 375 -852 811 -446 777 -450 813 -448 801 -458 419 -810 783 -424 379 -16422 413 -400 399 -396 405 -426 375 -424 411 -390 411 -390 413 -420 383 -420 383 -418 383 -418 415 -418 385 -4062 419 -816 383 -850 383 -852 409 -856 781 -448 385 -852 805 -468 779 -480 383 -844 819 -420 815 -418 391 -860 387 -880 763 -462 383 -876 417 -852 813 -446 381 -848 769 -456 397 -858 363 -882 779 -450 811 -454 807 -480 777 -446 803 -452 779 -432 383 -874 771 -488 765 -456 383 -878 795 -488 385 -854 773 -448 785 -456 777 -484 783 -450 771 -478 377 -860 407 -880 809 -418 813 -416 391 -862 779 -450 385 -888 791 -452 781 -468 381 -876 409 -834 777 -452 383 -872 377 -856 385 -866 405 -854 355 -898 787 -488 765 -460 777 -446 383 -852 807 -432 809 -446 783 -458 817 -454 391 -822 799 -418 399 -16448 421 -388 381 -424 417 -390 377 -456 379 -406 385 -442 383 -416 385 -418 387 -418 387 -452 355 -450 357 -4096 383 -840 387 -846 389 -858 365 -882 785 -444 385 -886 793 -452 781 -470 415 -844 791 -452 779 -432 379 -872 377 -884 779 -448 383 -872 379 -896 799 -430 411 -834 789 -456 383 -838 421 -846 799 -428 807 -444 803 -480 803 -446 767 -472 777 -448 383 -852 791 -488 775 -466 381 -848 793 -486 389 -852 775 -448 803 -446 801 -454 779 -468 775 -448 389 -896 387 -878 775 -438 807 -448 367 -848 813 -444 385 -848 789 -490 767 -458 383 -912 363 -850 813 -444 381 -848 385 -850 409 -854 385 -870 387 -846 803 -464 811 -418 801 -446 375 -884 779 -444 779 -484 779 -446 797 -472 381 -850 773 -416 407 -16410 409 -422 369 -410 403 -410 409 -410 379 -412 411 -408 377 -442 377 -428 379 -440 347 -444 381 -416 381 -4076 411 -844 375 -848 383 -850 385 -884 757 -488 385 -856 775 -480 769 -478 409 -840 787 -448 777 -450 391 -860 387 -848 793 -458 383 -878 417 -852 813 -412 383 -874 787 -454 389 -844 393 -854 785 -482 781 -454 807 -480 777 -446 801 -452 779 -434 381 -876 801 -454 765 -460 381 -878 787 -488 411 -826 775 -446 801 -458 779 -450 809 -450 803 -444 379 -862 419 -878 777 -438 807 -446 367 -878 +RAW_Data: 777 -446 383 -850 791 -486 783 -468 381 -882 393 -856 777 -446 383 -848 383 -854 411 -858 379 -848 419 -850 775 -506 783 -450 769 -446 377 -852 813 -446 777 -484 779 -448 795 -454 417 -824 777 -414 405 -122598 99 -134 65 -100 199 -832 99 -200 829 -98 163 -100 165 -66 197 -100 361 -98 723 -66 2987 -100 561 -460 1415 -100 527 -164 621 -98 459 -100 797 -66 497 -66 365 -132 401 -134 131 -66 699 -100 231 -66 327 -164 197 -66 299 -66 359 -66 395 -66 1773 -98 591 -66 295 -64 261 -98 833 -132 4215 -66 735 -98 331 -100 431 -66 761 -266 331 -102 497 -200 5637 -66 97 -198 921 -100 531 -66 299 -100 1559 -98 99 -100 233 -100 4173 -68 431 -66 599 -100 231 -66 429 -166 331 -132 431 -100 489 -66 359 -98 131 -66 261 -134 5083 -134 299 -100 1921 -68 197 -136 5299 -100 263 -64 721 -100 399 -130 429 -100 99 -98 1351 -100 295 -66 1315 -164 129 -98 393 -164 925 -66 663 -68 131 -66 229 -100 231 -100 99 -98 427 -66 325 -130 165 -98 197 -130 197 -128 327 -100 429 -66 1227 -100 893 -98 3491 -68 699 -66 365 -100 529 -98 229 -396 261 -66 561 -98 227 -98 329 -426 393 -130 363 -66 133 -166 1353 -100 393 -200 197 -100 199 -100 333 -66 961 -66 199 -66 197 -64 593 -66 627 -66 1317 -66 165 -198 199 -98 99 -66 3621 -98 597 -98 165 -66 4527 -200 263 -66 2751 -100 2131 -66 229 -64 721 -132 1261 -266 629 -98 201 -68 1323 -100 1689 -66 233 -326 1283 -66 261 -66 1381 -66 201 -100 4351 -66 331 -100 565 -134 2025 -66 563 -198 1657 -98 3057 -68 397 -98 297 -66 231 -100 931 -98 365 -66 1127 -66 165 -132 201 -100 99 -66 961 -100 99 -66 425 -66 1087 -166 463 -64 6307 -530 329 -66 297 -100 329 -66 201 -100 1123 -66 723 -98 523 -64 985 -66 131 -134 99 -66 4897 -66 1451 -132 199 -66 659 -66 195 -66 557 -66 263 -68 569 -100 661 -164 1975 -100 1361 -66 3647 -66 335 -100 363 -100 919 -66 887 -166 1775 -166 501 -66 495 -66 2753 -98 887 -66 3259 -66 821 -464 797 -134 567 -66 2401 -98 5415 -100 435 -100 99 -100 427 -66 599 -68 465 -132 435 -100 1495 -66 301 -68 233 -200 431 -66 397 -100 265 -68 231 -100 461 -234 199 -98 893 -132 265 -298 131 -166 2055 -68 199 -66 497 -66 1697 -66 1857 -66 5599 -66 2351 -66 131 -100 4929 -66 265 -134 561 -66 765 -132 861 -66 5351 -134 265 -102 99 -66 563 -66 267 -136 333 -98 233 -100 601 -66 365 -98 +RAW_Data: 1023 -66 5413 -232 631 -134 8367 -66 1583 -196 1901 -98 129 -66 1381 -98 3681 -98 1583 -98 133 -100 263 -134 1731 -100 465 -66 629 -66 4609 -166 201 -132 1995 -66 333 -66 499 -100 723 -134 561 -66 5317 -66 333 -66 1135 -66 1193 -66 1227 -100 885 -132 163 -230 719 -100 2305 -66 1025 -166 397 -68 793 -100 1381 -132 5385 -66 623 -164 763 -98 357 -396 3739 -134 4665 -66 393 -130 1153 -98 687 -66 229 -100 961 -66 467 -66 1301 -66 3647 -98 363 -66 329 -68 1589 -100 261 -66 5647 -66 2039 -66 297 -164 523 -66 197 -100 933 -66 99 -164 67 -66 97 -64 197 -64 559 -132 1151 -64 229 -98 2249 -64 1151 -66 533 -134 299 -134 665 -98 559 -98 1253 -66 2687 -134 995 -132 233 -68 131 -68 467 -132 297 -66 233 -66 167 -66 165 -134 201 -98 165 -166 1421 -428 333 -480 327 -448 361 -462 367 -424 371 -452 377 -420 379 -420 411 -418 381 -422 379 -424 381 -4072 773 -450 393 -852 397 -858 777 -448 811 -458 779 -480 793 -458 809 -448 385 -854 789 -426 411 -844 391 -852 815 -450 801 -444 415 -848 407 -848 383 -850 799 -450 409 -854 773 -450 395 -862 419 -844 401 -852 809 -456 379 -838 417 -848 397 -822 805 -446 411 -840 419 -848 799 -470 811 -418 411 -840 385 -844 423 -838 381 -882 369 -882 777 -480 383 -856 395 -862 385 -842 819 -432 387 -840 419 -850 399 -854 381 -850 403 -884 811 -418 415 -822 417 -826 405 -850 811 -420 817 -446 801 -452 811 -472 381 -840 833 -410 807 -454 785 -458 803 -444 805 -448 801 -458 417 -844 795 -440 809 -382 417 -16778 459 -354 455 -386 385 -422 419 -388 411 -382 405 -420 397 -398 421 -408 387 -410 385 -414 415 -418 383 -4046 787 -424 413 -844 427 -818 817 -416 835 -428 811 -450 811 -458 803 -448 409 -838 785 -450 383 -840 417 -862 809 -442 811 -416 421 -872 385 -850 403 -820 809 -452 397 -828 815 -446 413 -840 409 -838 413 -872 807 -412 411 -848 407 -824 415 -844 783 -460 379 -876 385 -850 807 -474 809 -418 411 -838 419 -810 429 -818 423 -838 419 -846 797 -436 413 -878 381 -828 411 -848 811 -418 411 -840 419 -846 397 -852 387 -870 387 -850 803 -442 413 -844 407 -824 415 -848 781 -462 809 -412 837 -448 803 -458 419 -810 827 -434 811 -414 801 -454 815 -450 779 -458 803 -450 395 -860 809 -416 809 -424 379 -16828 405 -414 379 -412 413 -410 383 -406 423 -396 399 -392 433 -382 407 -412 413 -380 411 -414 409 -394 413 -4044 805 -422 393 -854 395 -826 813 -448 +RAW_Data: 811 -456 811 -412 821 -486 775 -438 411 -844 801 -442 395 -838 413 -838 803 -448 801 -456 413 -840 407 -836 417 -834 805 -446 387 -848 801 -438 415 -844 407 -848 417 -848 819 -432 385 -844 419 -816 429 -826 809 -446 411 -838 419 -848 797 -468 805 -418 393 -866 385 -846 389 -854 409 -838 415 -838 799 -472 379 -886 367 -850 417 -846 781 -458 379 -842 419 -848 401 -854 381 -884 369 -884 783 -450 415 -820 407 -848 413 -814 809 -460 809 -444 805 -448 801 -460 417 -844 795 -434 811 -412 801 -450 809 -436 805 -448 805 -446 417 -864 809 -412 803 -416 409 -16802 423 -386 411 -382 405 -420 377 -122006 97 -98 165 -66 97 -132 65 -428 427 -132 3981 -66 265 -66 161 -66 327 -66 1625 -66 727 -100 4773 -134 863 -66 665 -66 2605 -98 4311 -98 99 -66 1249 -98 263 -100 5105 -68 461 -100 5065 -66 965 -68 799 -164 5737 -19106 327 -196 131 -362 131 -332 229 -134 363 -1162 297 -132 1197 -100 99 -132 231 -66 425 -200 267 -100 395 -134 331 -134 267 -100 563 -100 231 -66 655 -66 427 -66 299 -98 197 -132 3997 -66 463 -134 133 -134 1827 -132 1847 -66 131 -66 933 -100 3749 -132 131 -328 887 -98 457 -66 457 -100 593 -98 1813 -66 3895 -98 1117 -66 199 -66 881 -98 987 -66 397 -98 749 -66 195 -100 953 -100 819 -100 1017 -98 1915 -100 1707 -130 3865 -98 567 -132 265 -132 333 -66 1033 -132 791 -100 165 -100 1261 -66 531 -68 4337 -68 1191 -100 933 -100 767 -66 499 -164 363 -132 65 -100 5559 -66 233 -66 167 -68 265 -232 195 -100 329 -98 1795 -132 5043 -132 197 -66 223 -132 265 -200 525 -66 1147 -132 793 -100 4221 -132 703 -66 197 -132 595 -100 3891 -100 333 -100 1451 -164 261 -328 199 -166 1277 -1574 1699 -490 1705 -516 1663 -516 1695 -518 1699 -484 617 -1538 609 -1562 585 -1576 1693 -518 587 -1576 1701 -514 581 -1554 1737 -484 1737 -484 1711 -528 583 -1572 599 -1576 609 -1530 615 -1560 1719 -494 1739 -520 581 -1560 631 -1534 609 -16396 609 -1576 1707 -490 1713 -516 1697 -520 1699 -508 1711 -530 579 -1572 599 -1576 609 -1544 1707 -518 587 -1570 1731 -484 605 -1578 1699 -516 1729 -476 1753 -494 605 -1568 609 -1566 593 -1540 607 -1566 1733 -510 1705 -512 599 -1578 611 -1544 615 -16410 631 -1536 1711 -530 1703 -500 1731 -516 1737 -518 1741 -480 639 -1566 599 -1568 615 -1580 1743 -482 641 -1534 1751 -486 655 -1542 1735 -520 1735 -514 1745 -512 595 -1570 613 -1546 651 -1548 603 -1574 1767 -484 1745 -516 609 -1570 595 -1580 611 -16402 631 -1542 1733 -486 1747 -480 1773 -484 +RAW_Data: 1729 -486 1783 -462 643 -1542 637 -1548 613 -1562 1743 -480 611 -1566 1747 -490 623 -1540 1747 -502 1739 -492 1759 -500 615 -1544 639 -1532 617 -1556 623 -1542 1777 -462 1771 -490 623 -1540 619 -1570 609 -16422 629 -1546 1747 -486 1739 -474 1737 -516 1713 -506 1751 -488 621 -1574 611 -1558 617 -1542 1729 -520 619 -1534 1735 -512 611 -1546 1743 -508 1727 -514 1729 -482 647 -1554 615 -1546 605 -1568 613 -1560 1713 -512 1737 -486 607 -1568 613 -1562 621 -16400 643 -1538 1715 -518 1709 -482 1725 -514 1723 -520 1711 -530 581 -1570 603 -1596 575 -1592 1693 -516 601 -1580 1703 -520 591 -1574 1737 -484 1741 -482 1761 -482 605 -1576 611 -1560 613 -1548 623 -1538 1739 -478 1715 -520 587 -1556 603 -1568 607 -16414 661 -1524 1743 -478 1727 -486 1727 -486 1747 -490 1731 -522 587 -1558 623 -1546 623 -1574 1737 -484 597 -1566 1739 -480 635 -1560 1745 -464 1745 -516 1729 -504 619 -1546 623 -1546 623 -1574 609 -1560 1743 -486 1747 -480 639 -1572 337 -80934 65 -702 65 -960 165 -132 333 -100 1031 -134 163 -100 199 -398 2413 -66 853 -96 3999 -66 1027 -68 495 -66 431 -98 197 -134 65 -100 431 -68 663 -596 197 -132 525 -100 425 -100 397 -100 429 -100 263 -166 4697 -132 291 -228 459 -64 957 -66 233 -66 723 -364 5193 -198 759 -66 359 -98 231 -98 425 -66 265 -98 457 -66 3309 -198 863 -66 263 -166 393 -66 1025 -166 331 -134 463 -102 4119 -66 331 -66 465 -66 395 -66 267 -100 365 -136 97 -100 333 -134 363 -100 265 -100 799 -66 463 -132 1917 -100 2273 -132 99 -66 2537 -98 97 -100 195 -66 657 -98 1145 -66 359 -66 423 -98 227 -66 5391 -66 4069 -100 795 -130 455 -66 3877 -100 165 -98 365 -198 2085 -66 1059 -66 333 -66 7359 -102 431 -66 1349 -100 131 -66 459 -66 327 -66 4189 -98 533 -100 563 -66 365 -66 465 -66 863 -66 493 -66 1317 -66 3459 -98 99 -66 1153 -130 1875 -166 867 -232 5309 -132 363 -66 301 -66 1333 -166 1525 -66 865 -166 5313 -98 295 -98 363 -98 197 -98 229 -64 557 -100 97 -164 199 -66 99 -66 291 -66 393 -98 163 -326 327 -100 529 -134 467 -68 165 -66 201 -66 6277 -66 229 -64 361 -66 227 -66 1215 -66 525 -66 131 -66 1053 -66 1147 -66 1463 -100 565 -402 1127 -66 1329 -98 331 -66 97 -68 461 -66 265 -68 65 -66 1197 -66 831 -66 761 -66 233 -66 4805 -66 395 -68 297 -100 827 -198 625 -100 399 -66 1191 -68 429 -100 629 -66 499 -66 195 -100 2485 -66 655 -100 4745 -68 297 -66 1965 -134 4807 -68 99 -134 627 -66 233 -132 499 -66 +RAW_Data: 5047 -98 165 -102 167 -66 533 -66 133 -100 1359 -166 493 -100 2385 -100 3759 -66 635 -132 331 -100 2115 -100 431 -66 5673 -66 631 -66 231 -100 1589 -100 9979 -100 2797 -394 327 -66 687 -66 899 -100 5425 -134 461 -100 623 -132 5885 -68 399 -100 165 -98 165 -100 1761 -230 697 -66 227 -98 919 -66 1921 -134 1099 -66 331 -66 333 -166 531 -100 793 -66 129 -164 197 -66 131 -98 887 -100 131 -198 295 -66 197 -68 761 -68 827 -100 297 -100 1585 -100 1083 -66 199 -66 1553 -68 433 -100 631 -232 165 -66 297 -234 431 -66 2297 -100 233 -66 565 -230 129 -196 4915 -66 297 -66 529 -134 365 -132 433 -66 297 -166 359 -66 695 -68 1259 -130 689 -66 1645 -98 525 -100 791 -66 295 -66 1413 -64 165 -68 261 -100 1627 -134 1677 -66 357 -66 633 -132 197 -132 1061 -100 5353 -66 233 -100 897 -66 395 -66 459 -66 129 -100 897 -134 4679 -66 857 -130 1841 -66 1023 -134 5191 -98 361 -98 527 -66 1021 -98 591 -66 493 -66 955 -100 231 -98 295 -364 99 -66 4471 -66 99 -132 1193 -66 563 -66 267 -68 661 -66 1261 -134 827 -98 4705 -66 1643 -132 2273 -166 263 -66 459 -100 3483 -66 565 -100 2155 -130 195 -100 1695 -100 529 -100 1053 -202 133 -100 885 -98 297 -98 853 -68 531 -66 263 -100 4855 -66 331 -66 563 -64 391 -164 333 -68 97 -132 1789 -132 165 -132 267 -66 1191 -66 727 -100 197 -66 601 -98 231 -100 331 -200 465 -66 863 -66 97 -98 361 -66 231 -100 959 -66 525 -66 167 -98 531 -100 4877 -100 829 -66 331 -66 363 -66 589 -64 227 -98 393 -132 99 -98 197 -132 197 -100 763 -100 399 -100 627 -98 301 -66 1595 -1492 511 -1044 513 -1040 507 -1042 1005 -558 1007 -554 1005 -574 1007 -548 1005 -546 1003 -542 477 -1072 1007 -516 1013 -556 1009 -552 513 -1076 975 -550 1009 -554 1013 -524 1017 -552 487 -1042 999 -580 993 -584 493 -1050 985 -580 977 -576 483 -1044 515 -1044 1007 -516 1005 -586 1011 -550 481 -1072 1013 -548 1013 -548 975 -552 515 -1042 475 -1050 1017 -574 491 -1084 983 -570 475 -1056 525 -1048 979 -546 515 -1042 479 -1040 1003 -584 515 -1040 515 -1044 513 -1046 515 -1042 1009 -548 1007 -516 509 -1040 1003 -524 1513 -19022 1537 -1536 477 -1048 507 -1032 509 -1046 1001 -570 1013 -572 979 -580 483 -1064 499 -1052 981 -546 513 -1044 1009 -518 1009 -586 1009 -548 513 -1040 1011 -548 1009 -546 1009 -546 1005 -516 511 -1044 977 -620 981 -570 495 -1050 1013 -534 999 -550 525 -1048 485 -1044 1009 -514 1003 -586 1009 -546 515 -1074 979 -546 +RAW_Data: 1009 -542 1009 -546 513 -1040 479 -1040 1003 -586 513 -1040 1007 -576 483 -1074 483 -1044 1011 -548 479 -1044 511 -1042 1003 -588 477 -1076 479 -1074 513 -1040 513 -1040 1009 -548 1007 -516 509 -1040 1003 -522 1527 -18990 1541 -1550 481 -1042 509 -1048 481 -1046 1001 -588 1017 -554 985 -572 477 -1060 1023 -548 981 -544 515 -1040 1011 -514 1003 -586 1007 -548 513 -1042 1007 -576 981 -546 1013 -548 1007 -516 509 -1042 1005 -574 1005 -576 481 -1076 981 -578 977 -546 513 -1044 513 -1040 1005 -518 1007 -590 971 -584 479 -1074 1011 -548 1007 -548 1005 -516 511 -1040 509 -1044 975 -588 519 -1052 1003 -552 501 -1046 513 -1062 993 -548 485 -1042 515 -1040 1003 -574 485 -1076 485 -1076 485 -1076 483 -1042 1009 -548 1009 -516 509 -1040 1003 -520 1527 -19010 1537 -1524 481 -1076 481 -1046 511 -1044 1013 -554 1013 -552 519 -1044 1005 -556 497 -1050 983 -578 483 -1042 1011 -516 1009 -586 1009 -550 513 -115324 65 -2458 65 -66 231 -896 231 -68 131 -434 1191 -66 399 -66 295 -100 627 -132 561 -66 755 -100 593 -100 1155 -98 163 -66 3687 -100 793 -98 229 -98 425 -100 623 -98 393 -132 131 -132 393 -66 395 -66 259 -132 259 -132 99 -100 463 -166 5379 -132 631 -66 1131 -66 819 -164 1115 -66 1525 -66 3617 -98 297 -98 65 -66 497 -68 199 -100 267 -166 593 -66 787 -132 197 -68 5473 -68 527 -164 329 -66 229 -64 195 -230 359 -100 263 -66 391 -98 821 -66 165 -66 2935 -100 65 -68 1057 -66 267 -68 561 -98 559 -98 329 -166 367 -100 1925 -134 999 -66 399 -98 529 -66 299 -134 299 -164 4801 -132 299 -98 595 -66 265 -68 1259 -98 327 -132 755 -198 4311 -130 721 -68 1259 -200 399 -302 495 -100 1259 -66 665 -66 331 -66 531 -66 3979 -134 461 -66 1313 -66 325 -66 753 -68 329 -98 427 -66 5079 -66 299 -134 527 -68 165 -100 333 -100 1091 -66 195 -164 195 -66 4411 -66 199 -100 1695 -66 567 -66 435 -234 1415 -98 325 -98 97 -98 329 -66 165 -268 131 -164 1905 -132 1123 -68 263 -166 199 -234 533 -134 491 -98 293 -66 989 -98 4053 -134 299 -166 499 -98 893 -66 463 -134 5411 -66 4201 -100 4949 -66 533 -166 1327 -66 131 -100 1717 -100 2787 -166 471 -66 1051 -98 1183 -66 2895 -68 2289 -66 797 -66 295 -68 627 -200 697 -100 163 -66 5101 -100 1791 -66 965 -134 7887 -66 265 -100 99 -134 1891 -100 559 -66 661 -98 167 -68 465 -68 1851 -164 4741 -98 397 -66 261 -98 955 -198 567 -98 233 -132 699 -100 465 -66 265 -164 331 -66 131 -98 765 -100 461 -98 1125 -64 +RAW_Data: 3701 -100 1033 -66 197 -68 4443 -68 595 -134 565 -66 499 -66 199 -134 5461 -66 645 -344 635 -326 677 -648 345 -312 697 -310 669 -350 667 -348 667 -634 345 -312 669 -650 373 -636 323 -348 635 -23014 363 -320 669 -322 641 -650 373 -312 667 -350 667 -346 667 -332 681 -642 351 -312 667 -640 319 -666 347 -318 645 -23022 349 -326 641 -352 659 -656 335 -350 661 -334 651 -354 657 -330 703 -658 329 -316 655 -646 349 -668 341 -318 625 -23038 323 -348 653 -322 663 -654 335 -354 661 -336 653 -322 685 -330 701 -652 321 -346 637 -648 351 -658 333 -354 623 -23022 319 -336 677 -320 661 -654 335 -354 661 -336 679 -322 687 -296 703 -656 331 -318 667 -652 353 -620 375 -318 625 -23036 321 -346 655 -322 661 -652 371 -320 659 -338 677 -322 691 -296 705 -654 333 -350 635 -634 345 -672 353 -300 635 -23040 349 -314 667 -314 665 -642 349 -352 645 -354 651 -344 693 -318 693 -644 345 -300 679 -626 361 -648 323 -354 651 -22990 345 -338 661 -346 653 -652 325 -350 647 -322 681 -350 673 -346 653 -654 321 -354 645 -646 349 -656 351 -340 609 -23046 321 -344 637 -328 679 -648 345 -314 697 -316 667 -348 669 -338 677 -646 351 -316 655 -646 343 -658 351 -314 655 -23016 333 -352 627 -328 681 -646 345 -350 655 -346 655 -322 691 -320 675 -652 337 -350 637 -666 315 -672 351 -300 669 -22998 349 -318 655 -340 653 -646 349 -348 667 -314 667 -352 669 -338 679 -646 351 -316 655 -646 345 -658 351 -314 653 -23020 335 -318 659 -322 665 -672 345 -316 659 -342 671 -330 677 -324 683 -650 341 -354 631 -656 349 -654 345 -352 621 -23002 329 -352 667 -322 641 -684 343 -316 659 -340 677 -322 695 -326 675 -654 333 -354 623 -648 353 -658 331 -354 629 -123230 99 -66 197 -568 97 -594 65 -100 229 -130 589 -134 5281 -66 99 -98 2761 -66 1385 -132 299 -66 4967 -230 1991 -200 627 -66 1515 -232 5531 -132 1693 -98 995 -66 465 -100 399 -66 893 -66 4501 -66 363 -66 197 -66 1989 -66 1727 -198 99 -66 499 -100 427 -98 1649 -132 165 -164 97 -98 295 -132 325 -98 131 -66 295 -66 657 -64 625 -66 295 -130 1515 -68 231 -66 455 -64 521 -66 559 -66 1977 -98 167 -166 335 -66 493 -66 233 -68 335 -66 199 -266 165 -134 167 -68 431 -100 425 -98 163 -98 197 -66 359 -98 691 -98 229 -66 689 -66 195 -66 785 -66 163 -230 231 -196 259 -66 425 -98 265 -102 597 -100 199 -100 231 -100 231 -68 565 -66 133 -166 165 -100 229 -66 629 -134 65 -132 +RAW_Data: 231 -68 2745 -66 4279 -132 1093 -200 663 -98 2791 -66 299 -66 4055 -64 691 -66 529 -66 165 -68 865 -66 629 -66 133 -100 597 -266 297 -68 4367 -66 261 -130 519 -68 1265 -66 2189 -66 1423 -66 131 -66 4185 -66 165 -100 333 -132 1125 -66 363 -66 701 -66 1523 -534 3887 -66 1941 -100 293 -66 327 -164 131 -98 589 -98 133 -66 129 -66 429 -98 1415 -132 1021 -130 555 -100 1791 -134 299 -66 165 -66 331 -100 927 -298 231 -66 97 -68 1025 -66 297 -66 199 -68 699 -66 97 -66 97 -66 261 -166 393 -98 129 -98 161 -100 425 -100 1873 -66 759 -66 799 -100 427 -66 299 -66 1031 -134 267 -100 2349 -68 2659 -100 2097 -132 629 -100 1129 -166 231 -66 231 -66 1719 -132 427 -200 5213 -66 1399 -66 3055 -132 7835 -66 1327 -66 4625 -66 359 -66 2607 -420 373 -452 345 -452 347 -454 379 -422 377 -450 349 -466 359 -434 359 -436 389 -442 353 -444 385 -4052 781 -448 797 -436 379 -872 803 -430 807 -448 813 -458 779 -478 803 -450 805 -430 801 -446 381 -848 815 -444 789 -472 381 -846 819 -454 803 -434 413 -840 375 -858 409 -838 387 -852 395 -864 815 -450 385 -854 391 -870 383 -846 389 -852 395 -860 383 -844 393 -886 781 -448 415 -858 411 -816 417 -820 405 -850 411 -848 779 -458 805 -446 805 -450 803 -458 415 -846 793 -434 387 -840 417 -848 791 -436 809 -448 805 -450 409 -860 383 -848 825 -434 387 -840 417 -846 793 -434 385 -874 383 -888 375 -882 779 -458 807 -416 803 -456 381 -842 821 -434 813 -450 413 -836 809 -444 811 -426 395 -828 409 -17022 421 -414 417 -382 417 -384 417 -386 417 -418 385 -420 409 -398 395 -398 391 -440 387 -408 387 -412 415 -4026 819 -420 793 -430 421 -838 811 -454 781 -462 813 -446 787 -488 777 -440 809 -450 793 -426 411 -844 817 -430 809 -448 397 -862 817 -452 783 -462 385 -838 419 -812 427 -820 393 -866 421 -810 823 -436 413 -842 409 -866 385 -844 389 -854 397 -826 419 -846 387 -854 813 -440 409 -848 419 -844 389 -856 399 -822 417 -844 783 -460 803 -446 801 -448 801 -454 417 -846 799 -438 385 -842 421 -846 793 -436 809 -448 821 -418 419 -848 407 -848 811 -420 409 -840 421 -810 823 -436 383 -874 407 -858 387 -844 821 -438 807 -414 835 -418 417 -844 797 -436 809 -450 395 -864 811 -452 785 -428 423 -804 415 -17028 413 -430 363 -450 339 -448 375 -450 343 -450 375 -452 371 -420 381 -432 351 -470 349 -442 381 -410 383 -4072 773 -446 805 -426 411 -844 787 -464 811 -448 +RAW_Data: 793 -456 813 -450 789 -460 805 -416 803 -456 381 -844 825 -434 811 -448 415 -834 811 -446 813 -458 357 -870 383 -846 391 -852 395 -862 383 -872 793 -468 379 -874 397 -860 383 -844 387 -854 365 -856 415 -848 387 -854 819 -450 383 -886 375 -850 383 -850 417 -826 403 -852 777 -450 805 -446 805 -454 817 -450 395 -850 819 -418 415 -848 397 -850 779 -448 805 -458 777 -482 387 -850 393 -864 809 -450 385 -854 379 -862 773 -474 383 -846 415 -858 377 -882 785 -458 805 -414 803 -456 381 -844 821 -434 809 -448 379 -868 815 -452 789 -432 391 -832 413 -17034 411 -398 427 -380 407 -412 413 -378 413 -412 411 -382 417 -396 417 -396 409 -414 379 -436 387 -410 385 -4044 819 -420 789 -426 411 -840 809 -456 821 -430 809 -448 821 -418 831 -430 805 -450 811 -414 409 -838 811 -454 783 -456 409 -848 819 -448 791 -440 415 -814 421 -814 431 -824 413 -850 385 -850 805 -474 379 -850 419 -828 411 -848 381 -850 417 -822 405 -852 381 -850 823 -452 409 -856 381 -840 417 -830 407 -852 379 -850 821 -422 827 -432 807 -448 821 -418 419 -850 803 -438 409 -810 407 -854 811 -448 809 -418 815 -446 419 -858 417 -814 811 -422 409 -838 421 -812 827 -434 415 -848 419 -816 411 -848 813 -456 805 -412 833 -418 413 -822 807 -452 817 -418 419 -850 803 -472 809 -420 397 -794 411 -17034 429 -394 435 -388 409 -386 411 -416 415 -384 415 -386 417 -386 417 -420 385 -420 383 -422 385 -422 417 -4022 805 -414 807 -448 407 -838 785 -454 809 -456 811 -412 833 -448 801 -454 809 -420 815 -420 409 -838 821 -420 813 -458 387 -876 787 -454 827 -432 385 -840 421 -814 429 -820 421 -836 421 -844 795 -436 409 -874 377 -866 383 -836 425 -824 409 -844 407 -822 415 -836 821 -434 409 -874 377 -866 387 -844 389 -854 397 -824 813 -450 813 -452 811 -412 831 -442 407 -864 783 -450 381 -840 419 -822 805 -442 809 -450 817 -448 419 -850 413 -814 813 -422 409 -840 419 -812 823 -434 415 -846 419 -818 409 -848 811 -458 805 -414 831 -416 415 -820 807 -450 801 -446 409 -864 819 -452 783 -428 393 -834 413 -17032 409 -432 363 -424 369 -448 375 -448 343 -450 375 -450 339 -454 375 -434 377 -432 351 -464 357 -436 389 -4050 773 -444 785 -466 385 -874 771 -452 793 -464 811 -446 803 -448 803 -454 809 -418 813 -422 411 -838 817 -448 781 -462 411 -846 797 -486 777 -438 415 -842 383 -850 403 -852 379 -848 405 -848 811 -448 415 -856 395 -856 387 -846 385 -834 391 -868 +RAW_Data: 387 -844 429 -852 781 -446 409 -862 419 -844 399 -824 385 -838 409 -854 811 -444 779 -452 815 -448 801 -472 385 -842 825 -434 387 -838 421 -812 827 -432 805 -450 819 -420 415 -850 411 -850 811 -422 393 -864 387 -846 787 -462 385 -876 385 -848 407 -848 811 -458 803 -414 831 -416 413 -852 773 -448 803 -446 405 -882 387 -113196 99 -362 163 -66 65 -1810 97 -66 165 -196 129 -98 523 -98 1525 -100 1095 -100 725 -66 6021 -66 261 -64 195 -98 293 -130 1745 -98 99 -132 397 -98 363 -98 199 -98 5181 -166 299 -66 429 -66 367 -66 431 -100 629 -134 1297 -98 229 -66 163 -100 4973 -132 995 -66 165 -98 395 -66 297 -66 1711 -200 5349 -100 1493 -232 663 -198 1721 -66 301 -100 5563 -66 1151 -66 331 -66 227 -66 97 -98 163 -100 65 -98 1975 -66 361 -66 97 -230 525 -66 657 -66 863 -66 4121 -230 231 -66 401 -132 129 -98 259 -98 263 -66 955 -100 327 -132 5011 -170 299 -64 763 -66 435 -100 297 -66 401 -132 827 -200 2287 -64 8629 -100 897 -66 1689 -100 2345 -132 97 -100 1155 -68 1915 -396 787 -66 1021 -130 1057 -100 267 -132 829 -100 4749 -68 599 -66 397 -100 329 -64 163 -296 99 -132 329 -66 4881 -66 2091 -66 165 -66 231 -98 359 -100 663 -98 297 -66 5775 -100 233 -98 699 -98 131 -66 361 -100 297 -66 331 -66 1017 -98 233 -66 231 -132 197 -98 1721 -130 523 -66 197 -98 953 -164 231 -164 231 -98 795 -100 1155 -66 753 -166 825 -66 4525 -98 955 -298 625 -66 885 -66 4759 -100 367 -66 495 -66 597 -100 465 -66 265 -100 601 -98 467 -230 463 -66 4177 -66 495 -66 693 -100 831 -66 431 -68 363 -132 295 -98 131 -166 265 -66 331 -100 3189 -66 395 -298 625 -64 327 -362 631 -66 333 -100 3927 -98 1743 -66 295 -100 197 -68 793 -66 891 -132 65 -100 5247 -132 427 -66 199 -66 263 -68 629 -68 297 -100 1345 -98 263 -66 693 -130 591 -68 263 -100 299 -66 399 -68 565 -200 231 -102 231 -100 497 -68 2259 -236 297 -66 663 -100 493 -98 133 -66 597 -132 265 -166 2459 -66 1857 -134 627 -100 827 -320 705 -354 695 -354 693 -354 721 -320 727 -322 733 -678 377 -358 677 -356 705 -644 381 -672 381 -348 691 -676 383 -672 415 -640 419 -324 697 -680 383 -650 381 -654 407 -658 365 -664 395 -382 685 -350 719 -350 653 -16570 709 -380 651 -384 687 -352 691 -346 701 -336 709 -362 715 -654 409 -352 681 -354 669 -678 383 -648 417 -326 711 -646 417 -648 413 -648 415 -326 711 -642 +RAW_Data: 383 -684 377 -656 367 -684 387 -672 385 -350 685 -384 685 -386 651 -16538 737 -332 705 -334 705 -332 705 -362 707 -320 719 -354 723 -646 417 -324 697 -354 693 -678 385 -652 379 -344 691 -698 361 -686 385 -654 417 -350 685 -652 385 -674 355 -700 357 -664 391 -664 393 -364 685 -364 709 -360 677 -16510 773 -310 695 -354 709 -320 733 -320 727 -322 729 -322 733 -680 387 -326 699 -352 693 -678 385 -648 381 -330 739 -648 415 -648 413 -650 413 -328 675 -682 379 -656 371 -688 353 -686 381 -652 413 -352 689 -386 693 -348 669 -16546 733 -348 697 -344 701 -346 699 -352 685 -354 721 -354 695 -644 423 -340 699 -332 711 -650 383 -652 419 -324 721 -642 419 -646 385 -680 387 -356 687 -646 387 -682 379 -656 401 -654 385 -654 383 -352 717 -352 723 -352 659 -16524 759 -324 697 -374 675 -358 679 -356 709 -352 703 -354 703 -680 375 -358 677 -354 709 -644 385 -680 385 -326 713 -680 385 -650 415 -648 415 -326 713 -648 383 -656 405 -654 385 -654 383 -652 413 -350 689 -386 693 -348 687 -16514 759 -322 699 -348 699 -336 721 -322 709 -354 707 -354 707 -680 385 -326 703 -354 691 -680 383 -638 417 -328 709 -646 419 -650 415 -648 413 -328 675 -682 381 -654 369 -688 385 -656 381 -684 381 -350 721 -354 695 -348 689 -16510 763 -322 697 -350 703 -350 701 -348 703 -350 701 -350 701 -678 379 -350 685 -350 697 -666 379 -662 395 -326 719 -654 417 -656 385 -650 409 -358 677 -680 377 -646 379 -686 367 -680 379 -662 395 -362 687 -364 709 -358 649 -16538 763 -324 699 -348 701 -346 687 -362 711 -322 707 -356 705 -682 377 -358 685 -354 707 -644 385 -682 385 -328 711 -648 417 -648 415 -648 415 -328 679 -684 379 -656 371 -686 353 -684 383 -684 381 -350 689 -386 687 -372 653 -16534 757 -328 689 -320 737 -320 729 -320 731 -320 729 -320 731 -644 407 -360 711 -320 707 -644 387 -650 419 -326 709 -644 423 -646 417 -648 415 -326 711 -646 383 -652 407 -656 387 -656 381 -654 413 -352 689 -352 723 -338 689 -16532 729 -328 711 -354 703 -320 695 -352 727 -322 727 -322 733 -678 375 -358 685 -354 671 -678 385 -648 417 -328 709 -646 417 -648 415 -650 413 -328 679 -684 379 -656 371 -664 393 -652 387 -684 383 -350 723 -350 695 -348 665 -16554 763 -308 713 -322 713 -322 731 -320 729 -322 727 -320 735 -678 387 -326 701 -356 689 -678 385 -650 381 -346 725 -652 387 -654 415 -644 415 -354 685 -654 383 -674 355 -662 391 -662 391 -664 395 -364 687 -362 +RAW_Data: 713 -326 679 -123960 161 -132 263 -624 263 -66 327 -162 131 -1094 131 -98 723 -66 621 -66 459 -66 229 -66 329 -66 591 -66 881 -66 3747 -66 1783 -66 3219 -68 263 -100 695 -66 165 -66 99 -66 1193 -98 531 -234 263 -68 7149 -66 461 -100 3253 -66 595 -134 561 -66 325 -66 685 -66 199 -98 359 -130 99 -98 787 -100 925 -100 5157 -66 591 -98 233 -100 827 -66 1345 -66 10323 -200 3355 -100 367 -100 965 -98 495 -98 233 -102 1923 -66 6641 -68 263 -68 299 -100 705 -198 827 -100 529 -68 625 -66 493 -66 3641 -98 897 -68 333 -100 6513 -68 825 -66 597 -132 231 -100 659 -100 1099 -102 963 -98 831 -68 261 -68 299 -134 4477 -66 561 -66 927 -98 393 -66 1871 -166 327 -132 229 -100 63 -66 561 -1216 479 -414 205 -636 517 -358 235 -642 243 -612 237 -628 227 -614 237 -682 241 -604 269 -628 525 -336 513 -348 537 -346 245 -600 513 -326 265 -644 279 -606 273 -596 257 -620 281 -556 281 -594 553 -314 283 -572 549 -324 299 -586 317 -550 567 -310 319 -558 555 -300 315 -554 281 -572 571 -324 557 -312 279 -598 289 -578 313 -552 577 -320 533 -312 559 -290 573 -342 285 -580 525 -350 279 -594 547 -318 283 -578 559 -310 269 -568 567 -346 281 -578 311 -564 545 -328 545 -350 513 -318 561 -316 283 -572 253 -628 579 -316 541 -318 279 -602 289 -574 313 -552 283 -560 555 -320 531 -288 1123 -1164 537 -332 279 -576 581 -320 283 -578 277 -568 287 -568 305 -550 315 -590 315 -564 275 -600 545 -328 543 -314 559 -320 285 -546 561 -310 285 -628 263 -608 283 -584 281 -600 269 -596 291 -558 549 -314 287 -554 577 -350 281 -598 257 -582 581 -320 283 -582 527 -344 269 -566 303 -548 579 -318 593 -314 283 -564 291 -578 313 -558 545 -318 571 -314 525 -292 569 -336 319 -562 569 -312 283 -596 549 -300 317 -556 553 -320 275 -566 543 -362 281 -594 281 -580 555 -324 541 -340 549 -318 543 -320 283 -540 309 -596 555 -338 549 -318 281 -580 311 -562 291 -578 275 -586 547 -320 511 -318 1099 -1150 557 -336 283 -594 539 -318 317 -534 291 -606 273 -552 317 -556 283 -604 287 -600 277 -586 547 -348 533 -320 541 -340 285 -542 565 -302 277 -632 283 -572 287 -594 303 -576 283 -594 283 -544 565 -310 285 -562 547 -362 283 -594 279 -580 551 -322 285 -604 553 -318 285 -548 281 -602 527 -346 557 -326 277 -588 283 -596 281 -578 553 -294 571 -302 551 -314 549 -352 279 -602 545 -324 275 -590 545 -318 319 -540 555 -324 279 -560 +RAW_Data: 545 -350 317 -574 273 -594 557 -302 549 -352 521 -318 567 -294 277 -582 275 -624 537 -340 565 -290 301 -582 319 -566 285 -568 299 -544 547 -316 549 -318 1103 -1150 543 -326 275 -586 547 -352 281 -574 309 -562 257 -588 275 -590 283 -592 317 -574 271 -594 557 -336 549 -318 543 -318 283 -574 547 -290 283 -634 283 -584 277 -600 257 -604 311 -556 317 -560 541 -320 283 -538 583 -320 297 -582 317 -564 567 -314 283 -562 581 -298 313 -548 283 -572 539 -354 555 -346 245 -598 289 -580 313 -552 575 -316 535 -292 569 -304 547 -348 279 -602 555 -326 279 -594 551 -320 283 -546 565 -310 285 -562 547 -364 283 -594 281 -600 525 -344 557 -290 565 -314 555 -320 287 -562 283 -580 585 -322 555 -310 283 -588 317 -538 309 -564 289 -576 543 -312 549 -320 1071 -1150 579 -290 301 -584 545 -352 283 -574 273 -566 291 -582 277 -588 283 -592 279 -610 273 -596 557 -300 549 -352 533 -316 279 -580 543 -312 285 -596 315 -568 307 -564 291 -580 313 -556 283 -584 537 -316 283 -570 545 -356 307 -590 283 -562 565 -346 247 -594 553 -302 279 -590 283 -570 541 -356 557 -310 279 -600 289 -576 313 -554 545 -316 565 -288 569 -302 547 -348 279 -598 557 -334 283 -572 541 -322 283 -600 547 -320 245 -598 543 -328 313 -574 285 -596 533 -342 549 -316 535 -332 531 -330 273 -586 281 -606 541 -322 565 -312 279 -600 289 -576 275 -588 281 -572 571 -290 543 -304 1117 -1144 557 -326 281 -592 547 -320 281 -580 311 -564 257 -602 275 -586 283 -590 315 -572 273 -594 555 -336 549 -318 523 -320 283 -578 557 -294 277 -618 317 -560 315 -568 273 -602 257 -604 277 -590 547 -318 285 -564 537 -350 317 -560 293 -598 551 -320 283 -564 565 -312 283 -562 291 -580 543 -348 555 -318 283 -602 257 -604 275 -590 545 -318 537 -348 527 -292 569 -336 319 -560 535 -348 283 -596 551 -298 313 -556 545 -318 277 -564 579 -330 283 -594 279 -600 523 -344 561 -290 567 -312 549 -318 283 -566 279 -600 549 -324 585 -308 285 -588 281 -600 277 -572 273 -594 551 -298 543 -312 1077 -1150 585 -290 289 -606 547 -316 319 -540 309 -566 289 -576 275 -586 281 -622 277 -570 291 -602 543 -314 547 -318 569 -312 283 -564 545 -328 273 -618 283 -578 309 -562 291 -580 313 -556 283 -586 537 -314 283 -570 577 -320 311 -588 283 -598 527 -346 269 -564 573 -312 285 -554 317 -564 539 -352 553 -322 291 -578 283 -594 281 -566 563 -302 563 -292 569 -312 549 -348 281 -598 529 -340 283 -570 +RAW_Data: 573 -290 285 -604 549 -318 245 -598 545 -358 281 -572 287 -596 567 -310 547 -320 535 -316 561 -294 277 -586 277 -624 557 -316 561 -326 281 -558 171 -112742 297 -66 231 -100 133 -98 131 -66 231 -66 65 -330 231 -790 165 -400 99 -434 459 -66 233 -132 1581 -98 163 -66 1049 -132 1849 -100 1855 -98 5435 -100 301 -68 761 -66 3115 -100 231 -68 2421 -68 765 -66 167 -68 233 -134 997 -66 233 -66 1061 -66 833 -98 565 -66 369 -102 131 -100 5197 -132 853 -232 825 -100 265 -100 765 -100 631 -66 827 -66 1749 -66 265 -136 165 -66 399 -100 3821 -98 793 -66 599 -100 231 -68 635 -68 197 -134 5091 -132 299 -68 1327 -100 757 -98 629 -66 601 -66 2887 -100 1167 -98 1879 -66 131 -98 295 -162 7033 -66 655 -68 1025 -100 261 -66 295 -66 195 -132 65 -66 9407 -100 4337 -68 795 -100 361 -132 99 -66 4915 -198 819 -100 327 -64 1611 -132 429 -100 331 -66 4037 -68 729 -66 827 -398 2751 -66 295 -66 4365 -98 1187 -66 389 -100 231 -166 1945 -66 431 -450 587 -458 587 -458 587 -458 587 -458 625 -424 623 -748 309 -424 627 -392 657 -716 343 -720 307 -408 657 -694 365 -692 365 -694 363 -410 653 -382 651 -382 651 -716 321 -704 355 -702 357 -370 717 -368 673 -364 675 -16544 735 -352 673 -362 677 -356 709 -320 733 -320 733 -352 703 -680 385 -358 669 -354 693 -678 381 -638 417 -328 709 -646 417 -646 417 -648 413 -326 713 -326 713 -326 713 -648 415 -650 379 -652 409 -346 723 -346 721 -346 653 -16532 777 -296 703 -360 709 -326 709 -348 689 -352 727 -320 731 -678 385 -326 701 -354 691 -678 385 -650 381 -346 725 -652 385 -656 415 -656 381 -386 685 -352 689 -330 711 -650 385 -652 419 -648 385 -328 751 -320 727 -352 671 -16536 757 -318 709 -320 731 -322 695 -352 725 -322 727 -322 731 -678 381 -348 705 -320 697 -676 383 -670 381 -346 687 -678 385 -680 383 -682 385 -326 717 -324 717 -326 711 -672 385 -650 415 -650 379 -330 737 -324 749 -320 699 -16538 747 -328 681 -354 705 -320 733 -320 729 -320 729 -354 703 -678 377 -358 677 -356 703 -642 385 -680 385 -328 711 -680 385 -650 417 -646 417 -326 713 -326 711 -326 713 -646 417 -650 379 -652 411 -344 725 -346 723 -346 653 -16538 707 -404 657 -376 653 -380 687 -382 655 -382 691 -380 679 -668 395 -366 671 -360 675 -682 381 -650 383 -358 687 -680 383 -682 383 -682 385 -326 715 -326 713 -326 715 -682 385 -650 379 -654 409 -346 723 -346 723 -348 651 -16540 743 -324 +RAW_Data: 713 -324 717 -356 687 -354 687 -354 727 -320 729 -682 379 -350 671 -354 693 -678 381 -672 379 -350 687 -678 381 -672 381 -674 413 -348 667 -356 691 -354 689 -678 381 -672 379 -672 381 -350 721 -320 727 -354 671 -16544 747 -328 689 -354 703 -320 731 -320 729 -320 729 -322 733 -680 385 -326 701 -354 693 -678 385 -650 381 -344 693 -686 387 -670 387 -670 385 -350 685 -352 689 -350 695 -672 363 -688 393 -660 395 -328 741 -332 711 -360 685 -16498 781 -328 679 -354 707 -320 731 -318 729 -320 731 -320 733 -680 373 -360 687 -352 705 -642 389 -648 419 -326 707 -646 419 -648 417 -648 415 -326 711 -326 711 -324 713 -646 419 -648 381 -652 409 -346 725 -348 719 -348 651 -16530 775 -298 703 -360 709 -326 711 -326 713 -354 723 -320 725 -646 409 -358 677 -354 707 -644 385 -650 417 -328 709 -646 419 -648 417 -650 415 -326 711 -326 711 -326 711 -648 381 -684 379 -652 407 -346 723 -346 689 -382 651 -16542 739 -316 715 -324 715 -350 689 -354 723 -322 725 -322 729 -680 375 -358 677 -354 705 -644 385 -680 385 -328 713 -678 387 -650 415 -650 413 -328 711 -326 711 -326 713 -648 381 -684 379 -652 407 -346 723 -346 717 -316 705 -16506 781 -288 717 -354 689 -356 687 -354 723 -320 725 -320 731 -680 381 -348 705 -320 695 -676 381 -672 381 -346 687 -678 381 -674 381 -672 419 -324 695 -356 687 -354 687 -678 385 -650 381 -682 379 -346 727 -346 721 -346 653 -16534 739 -346 693 -342 699 -344 699 -342 731 -346 687 -382 685 -686 383 -352 683 -352 689 -680 357 -664 391 -362 687 -654 409 -662 395 -662 393 -366 671 -360 679 -354 703 -646 419 -646 385 -648 417 -328 711 -354 725 -354 671 -16506 783 -316 705 -322 729 -320 727 -320 727 -320 727 -352 703 -678 377 -358 677 -354 707 -644 381 -672 383 -346 691 -678 381 -672 415 -638 419 -326 699 -354 689 -350 689 -678 381 -672 381 -672 383 -348 721 -322 727 -354 669 -16544 747 -328 689 -352 703 -320 727 -320 727 -322 731 -322 733 -680 385 -326 701 -356 691 -678 385 -650 379 -346 689 -686 387 -670 387 -654 417 -352 683 -354 689 -346 687 -656 377 -686 379 -666 393 -362 687 -364 715 -358 649 -129582 163 -694 263 -100 197 -68 199 -166 199 -100 131 -200 65 -662 629 -66 625 -164 65 -100 987 -66 587 -66 163 -66 95 -66 4701 -300 1593 -66 587 -98 587 -98 1907 -100 4575 -264 195 -296 1449 -132 161 -132 195 -66 359 -132 129 -66 5365 -66 65 -132 595 -100 731 -134 299 -68 1361 -100 +RAW_Data: 3975 -100 1023 -98 197 -132 887 -98 195 -100 261 -100 229 -132 1677 -98 5099 -132 1973 -98 2701 -130 4541 -102 165 -66 163 -66 163 -132 591 -66 791 -66 199 -102 4581 -68 1487 -100 161 -98 2113 -66 469 -66 3741 -100 4299 -66 163 -64 391 -166 65 -66 2619 -100 399 -66 4573 -66 2117 -132 6427 -66 1019 -132 529 -66 165 -66 797 -200 165 -102 199 -66 4713 -68 3163 -232 993 -100 363 -98 267 -66 5573 -98 263 -98 261 -66 197 -66 131 -100 2179 -264 4751 -166 467 -100 1727 -100 233 -100 2019 -100 4073 -66 861 -132 1359 -134 201 -66 235 -430 297 -66 599 -68 727 -100 263 -68 265 -68 4727 -100 499 -64 199 -134 65 -66 329 -100 97 -164 525 -64 485 -64 5219 -66 1149 -66 229 -230 1713 -66 131 -98 97 -66 1053 -66 5951 -66 2599 -100 131 -132 955 -132 259 -132 97 -66 99 -98 691 -66 889 -66 4877 -132 1543 -66 1147 -132 65 -298 793 -100 1329 -66 563 -66 595 -232 231 -66 165 -134 4975 -100 791 -100 231 -66 793 -68 1393 -100 463 -132 597 -132 165 -100 4941 -98 425 -132 165 -132 65 -68 231 -66 1033 -66 1003 -132 5379 -166 1745 -66 5759 -66 719 -200 2589 -66 5447 -66 2057 -234 1721 -100 467 -66 5485 -100 65 -66 1187 -134 599 -68 1131 -64 5741 -230 131 -66 819 -100 297 -68 133 -68 231 -132 5413 -100 131 -166 1157 -264 331 -66 727 -66 1283 -66 1513 -98 593 -66 265 -102 233 -66 797 -100 165 -134 665 -100 791 -132 99 -168 5317 -100 299 -100 929 -66 429 -66 835 -100 763 -100 165 -100 3941 -68 231 -68 699 -68 231 -68 1091 -66 299 -100 265 -100 365 -200 531 -66 761 -100 365 -68 5283 -100 299 -100 563 -166 999 -66 657 -132 5585 -64 131 -98 97 -132 361 -66 555 -98 293 -66 1247 -66 521 -196 263 -66 497 -132 6527 -66 399 -66 729 -100 197 -68 6605 -66 1115 -132 919 -98 7149 -66 1297 -66 197 -100 1151 -98 463 -266 197 -98 427 -68 891 -68 3379 -66 397 -68 67 -100 297 -98 983 -232 165 -132 883 -68 3773 -66 263 -64 653 -166 795 -100 397 -98 5091 -68 429 -66 297 -164 227 -98 621 -164 691 -130 2735 -64 1513 -98 299 -66 963 -166 333 -66 993 -100 4049 -66 555 -100 293 -98 815 -64 133 -66 729 -66 165 -66 1493 -66 1183 -234 165 -66 267 -202 1031 -100 693 -64 261 -66 261 -66 4057 -98 327 -98 425 -98 291 -100 65 -100 931 -68 297 -68 527 -68 463 -66 5135 -66 163 -98 5887 -4482 477 -1448 469 -1438 479 -1430 485 -518 467 -1440 451 -532 451 -1440 477 -1444 +RAW_Data: 471 -1474 451 -1446 481 -518 449 -1446 483 -514 475 -520 445 -502 491 -546 453 -1470 449 -1450 473 -1432 473 -1444 479 -482 473 -1460 461 -1404 481 -1484 461 -1444 449 -1446 483 -1440 477 -522 453 -1436 479 -1414 479 -1428 473 -1444 481 -1444 471 -1440 481 -1426 473 -1446 483 -520 451 -508 491 -1402 481 -1448 469 -1474 447 -556 443 -538 467 -514 481 -518 449 -1432 483 -482 485 -1418 4439 -4500 485 -1452 479 -1408 507 -1412 479 -504 495 -1406 483 -526 449 -1432 479 -1444 471 -1450 477 -1448 469 -516 479 -1430 481 -522 449 -510 475 -488 503 -522 481 -1466 461 -1446 447 -1448 481 -1442 477 -484 481 -1440 477 -1410 481 -1456 459 -1438 479 -520 483 -506 481 -538 463 -514 479 -1408 469 -516 481 -514 509 -1440 477 -518 451 -1436 483 -524 485 -478 475 -1444 469 -514 477 -540 477 -1438 475 -520 485 -1402 485 -1420 487 -1436 473 -512 463 -514 479 -1404 4423 -4516 489 -1442 483 -1420 473 -1440 471 -506 481 -1426 471 -512 471 -1408 483 -1484 463 -1442 475 -1448 465 -514 481 -1438 477 -484 481 -512 473 -490 499 -548 481 -1420 469 -1442 487 -1418 481 -1446 463 -516 479 -1408 471 -1442 449 -1486 461 -514 481 -524 491 -510 479 -1404 509 -1410 481 -500 491 -478 475 -1476 479 -520 489 -1434 451 -526 489 -1402 483 -524 487 -1404 479 -490 481 -1468 485 -1426 473 -1440 487 -524 451 -1454 477 -508 463 -1406 483 -492 483 -1406 4439 -4518 491 -1438 481 -1420 471 -1442 487 -522 451 -1418 479 -506 495 -1404 481 -1444 503 -1418 479 -1446 469 -516 481 -1438 479 -486 483 -506 475 -520 473 -522 479 -1464 459 -1436 475 -1444 467 -1442 481 -496 491 -1404 481 -1412 481 -1462 457 -1468 481 -514 475 -1412 483 -512 475 -1444 465 -1408 483 -1414 481 -538 507 -516 485 -512 477 -514 475 -492 503 -490 483 -1414 481 -1426 487 -514 509 -516 451 -542 475 -1430 479 -1414 479 -504 493 -514 475 -482 473 -1428 4417 -4510 483 -1440 481 -1452 475 -1438 461 -512 479 -1416 487 -506 489 -1406 479 -1448 487 -1444 459 -1434 479 -520 487 -1414 477 -518 473 -502 479 -504 491 -546 449 -1466 485 -1416 471 -1438 481 -1414 493 -514 477 -1406 471 -1442 483 -1456 461 -518 483 -1446 471 -514 479 -520 485 -476 477 -516 473 -1428 481 -1440 481 -1448 473 -512 473 -1436 481 -1426 473 -512 481 -1416 481 -1426 473 -1444 475 -518 507 -1422 491 -1436 481 -496 489 -1404 481 -1418 481 -502 491 -1400 4449 -4492 493 -1438 481 -1422 469 -1444 487 -522 449 -1450 471 -486 475 -1440 483 -1454 459 -1440 479 -1446 469 -516 481 -1436 477 -486 483 -510 475 -488 501 -518 421 -129440 263 -162 65 -362 +RAW_Data: 129 -264 65 -266 199 -132 67 -332 99 -98 3287 -100 4663 -132 4127 -98 331 -68 99 -66 5149 -200 1759 -66 65 -100 1051 -132 619 -66 5371 -132 1873 -232 195 -130 657 -132 357 -66 229 -66 4517 -100 365 -64 2447 -132 261 -66 633 -100 1153 -100 593 -132 259 -66 1589 -134 229 -66 65 -66 99 -66 491 -66 3789 -100 2059 -102 301 -66 663 -98 327 -98 5013 -98 4239 -164 987 -98 197 -66 163 -66 1479 -100 897 -66 99 -166 827 -66 465 -100 67 -100 563 -100 5179 -66 2043 -100 985 -66 1355 -66 431 -66 729 -232 165 -352 689 -346 685 -364 709 -322 709 -354 705 -354 701 -680 377 -360 675 -356 709 -644 385 -682 383 -328 711 -680 383 -648 415 -648 415 -328 711 -318 717 -326 713 -680 383 -650 379 -684 377 -330 741 -324 717 -354 691 -16506 751 -352 703 -322 693 -354 693 -354 725 -320 729 -322 731 -680 383 -356 669 -354 695 -678 385 -650 379 -344 691 -696 363 -686 387 -686 383 -352 685 -350 691 -346 699 -668 365 -684 367 -682 379 -366 685 -362 709 -360 677 -16540 731 -354 667 -352 699 -350 689 -362 711 -322 713 -354 707 -680 387 -326 737 -320 691 -676 383 -638 417 -316 715 -676 387 -648 421 -644 419 -326 711 -326 709 -326 713 -642 421 -648 415 -618 413 -346 725 -348 721 -314 685 -16528 775 -298 733 -326 709 -326 711 -324 715 -350 725 -320 731 -646 421 -326 699 -354 689 -678 383 -638 415 -318 711 -678 387 -648 417 -648 417 -326 715 -324 707 -318 717 -678 387 -648 381 -684 379 -346 725 -346 719 -348 651 -16544 741 -352 691 -354 687 -356 691 -348 691 -326 747 -320 743 -646 407 -330 725 -320 707 -644 389 -648 423 -296 735 -646 421 -648 417 -646 417 -326 707 -326 709 -324 711 -646 421 -648 381 -652 411 -346 725 -314 753 -316 683 -16546 649 -538 489 -574 455 -574 487 -542 557 -442 593 -474 575 -802 263 -464 599 -428 605 -752 319 -716 321 -422 639 -720 351 -720 319 -730 361 -366 669 -362 673 -356 705 -684 345 -686 381 -682 383 -326 715 -356 723 -354 669 -16540 747 -330 715 -320 707 -320 731 -320 729 -322 729 -354 701 -680 377 -358 675 -356 707 -646 381 -672 383 -328 717 -678 385 -680 383 -648 415 -326 717 -326 713 -326 713 -646 417 -652 379 -686 375 -346 723 -346 721 -346 653 -16542 775 -316 693 -354 687 -348 699 -354 691 -348 719 -332 711 -658 403 -354 677 -356 707 -644 385 -682 385 -328 713 -646 419 -650 415 -648 415 -326 713 -326 713 -326 711 -646 417 -648 379 -652 411 -344 725 -346 721 -346 +RAW_Data: 653 -16540 741 -348 691 -348 685 -354 687 -350 697 -378 705 -332 719 -652 405 -352 677 -354 705 -644 385 -682 387 -326 713 -646 419 -648 415 -650 415 -326 713 -326 713 -326 713 -646 381 -684 379 -652 407 -346 723 -346 719 -314 705 -16508 781 -290 715 -354 687 -354 689 -354 723 -320 729 -320 731 -646 419 -324 701 -354 689 -680 385 -650 379 -350 711 -648 417 -648 413 -638 415 -326 715 -324 713 -324 713 -648 417 -650 379 -652 411 -344 725 -346 723 -348 651 -16538 745 -324 711 -324 717 -324 717 -354 687 -352 725 -322 727 -678 375 -360 685 -354 705 -644 385 -648 419 -326 709 -648 419 -648 413 -650 415 -326 707 -318 715 -326 713 -646 419 -648 379 -684 379 -344 725 -346 719 -348 653 -16544 741 -336 697 -334 703 -332 737 -328 707 -326 717 -352 725 -646 415 -348 671 -352 693 -678 381 -638 415 -318 715 -678 383 -672 383 -674 383 -348 693 -354 687 -356 689 -678 387 -648 381 -684 379 -332 737 -326 745 -320 695 -16504 781 -286 737 -322 693 -354 691 -354 727 -318 729 -322 731 -680 381 -348 705 -320 697 -678 383 -650 381 -360 707 -648 415 -650 413 -650 413 -326 677 -362 675 -360 711 -648 381 -684 379 -654 405 -344 723 -346 685 -382 651 -16544 743 -350 689 -352 685 -352 691 -346 685 -364 709 -358 711 -650 407 -352 679 -354 671 -678 385 -648 417 -328 709 -648 415 -648 415 -648 413 -328 677 -360 711 -328 713 -648 381 -684 377 -656 405 -346 689 -380 687 -382 653 -16540 741 -352 689 -320 719 -354 689 -336 719 -322 711 -352 707 -680 407 -328 709 -322 711 -644 387 -680 385 -328 711 -678 385 -648 417 -648 415 -326 715 -326 713 -326 711 -648 419 -648 381 -652 409 -346 723 -348 719 -348 651 -130012 261 -132 261 -198 199 -130 791 -66 531 -66 231 -100 463 -66 299 -66 299 -100 785 -332 131 -66 267 -66 4069 -66 657 -100 589 -100 129 -100 523 -166 1325 -66 99 -132 4995 -98 429 -132 1151 -100 231 -130 361 -98 261 -66 1709 -98 165 -98 4413 -100 723 -132 63 -66 259 -98 359 -66 427 -68 665 -266 297 -330 961 -166 2625 -66 763 -98 65 -98 327 -166 463 -198 269 -100 529 -132 97 -98 227 -66 259 -162 327 -198 429 -66 295 -66 133 -162 3959 -234 789 -266 97 -230 827 -98 129 -100 97 -66 97 -66 361 -98 65 -66 525 -100 165 -64 757 -266 163 -166 3769 -166 563 -132 399 -68 563 -68 363 -66 133 -100 263 -66 529 -100 1065 -102 989 -132 1091 -100 729 -66 497 -98 133 -66 67 -98 565 -66 265 -234 +RAW_Data: 467 -166 499 -100 97 -66 4339 -100 165 -134 2121 -66 697 -134 531 -100 3557 -100 829 -130 461 -98 263 -300 199 -68 199 -66 733 -66 431 -132 365 -66 435 -66 637 -68 2411 -66 821 -66 65 -100 331 -100 693 -66 527 -200 131 -268 1959 -66 363 -464 5279 -132 199 -66 165 -200 329 -64 197 -98 1511 -132 525 -66 4411 -66 99 -100 131 -66 1161 -100 431 -66 165 -66 4607 -66 337 -68 267 -134 1623 -66 497 -66 295 -100 401 -100 65 -100 4201 -66 165 -68 233 -168 825 -100 599 -100 1857 -68 2819 -66 325 -164 359 -98 131 -298 99 -66 197 -66 4209 -100 1031 -66 131 -100 263 -66 267 -66 363 -100 1163 -132 633 -102 3097 -100 799 -66 265 -66 331 -100 763 -134 897 -100 333 -66 599 -66 467 -132 99 -66 3491 -66 821 -66 327 -66 557 -164 661 -68 165 -362 197 -98 757 -98 99 -98 525 -98 557 -134 1027 -132 197 -66 663 -100 991 -66 493 -68 131 -132 863 -132 529 -132 165 -102 397 -66 4095 -200 2929 -66 689 -66 397 -66 229 -98 163 -96 163 -98 227 -66 1063 -330 165 -66 495 -100 1021 -66 2569 -132 667 -134 331 -66 329 -64 555 -100 329 -66 227 -230 2225 -66 5081 -98 265 -66 669 -132 363 -100 265 -66 263 -132 731 -100 497 -98 199 -100 431 -166 233 -100 3069 -66 721 -132 329 -64 795 -66 535 -66 265 -100 531 -66 233 -3824 129 -706 305 -696 295 -688 323 -686 351 -644 353 -646 373 -644 375 -592 427 -584 421 -1078 909 -610 413 -602 399 -1080 451 -554 951 -1078 937 -552 455 -548 451 -1084 949 -1046 971 -516 481 -1046 449 -554 473 -542 475 -516 983 -1046 947 -552 451 -530 493 -1042 973 -522 473 -538 473 -540 471 -1040 975 -1006 477 -550 473 -508 1001 -522 487 -534 465 -548 451 -1044 447 -51358 455 -608 373 -628 365 -654 355 -652 353 -658 373 -640 373 -600 401 -618 389 -614 383 -622 415 -566 443 -580 407 -590 425 -1080 943 -564 413 -578 445 -1062 455 -548 943 -1078 947 -562 463 -548 451 -1044 967 -1050 983 -518 487 -1008 477 -524 485 -538 477 -510 1007 -1010 973 -556 453 -546 467 -1042 973 -542 447 -556 473 -510 475 -1044 979 -1016 499 -514 481 -526 991 -514 479 -522 483 -536 487 -1012 493 -51302 493 -3786 199 -670 339 -656 329 -684 321 -646 385 -628 373 -646 375 -626 365 -618 419 -586 417 -1088 909 -610 407 -590 427 -1082 415 -576 939 -1080 941 -584 425 -1074 941 -542 443 -1078 973 -1046 451 -548 445 -550 967 -554 475 -508 473 -1044 977 -1054 957 -1044 465 -516 483 -546 975 -520 485 -1048 461 -514 +RAW_Data: 483 -546 445 -552 975 -1018 979 -552 455 -1044 977 -51850 457 -4674 299 -704 305 -694 297 -720 323 -654 355 -626 385 -642 341 -632 401 -620 389 -1116 883 -610 413 -602 401 -1112 419 -552 943 -1066 937 -590 427 -1078 449 -556 951 -1044 475 -556 921 -1076 445 -550 981 -1030 985 -1012 485 -522 977 -528 495 -1010 479 -554 975 -506 479 -538 473 -548 453 -544 481 -1010 473 -554 985 -530 465 -514 481 -546 479 -1010 473 -556 475 -51320 459 -688 263 -754 225 -750 289 -742 297 -640 339 -658 365 -652 355 -648 353 -644 363 -640 375 -626 397 -586 419 -584 417 -1104 911 -578 445 -560 429 -1080 449 -554 937 -1082 941 -1082 947 -558 421 -584 449 -1034 455 -548 483 -516 479 -540 967 -1050 977 -520 489 -1038 443 -552 471 -538 967 -554 453 -540 467 -1042 977 -522 469 -544 477 -1016 481 -514 1009 -1014 481 -534 963 -1044 977 -554 443 -111144 233 -134 197 -196 131 -132 2559 -98 699 -98 233 -100 895 -100 897 -98 297 -230 295 -64 527 -66 895 -132 65 -98 4231 -134 667 -100 65 -66 297 -100 231 -102 563 -100 2053 -300 3873 -100 957 -66 291 -66 1225 -66 1095 -100 1029 -98 235 -100 4947 -66 131 -132 1159 -232 4881 -130 1649 -130 429 -66 463 -66 327 -266 133 -66 5073 -66 229 -100 697 -134 1353 -66 133 -130 4655 -264 299 -132 1065 -100 65 -100 199 -66 631 -66 1531 -98 233 -100 4353 -66 661 -100 233 -66 167 -68 1657 -66 267 -100 397 -98 761 -66 493 -68 761 -64 823 -98 525 -98 163 -66 197 -98 231 -66 167 -66 133 -98 953 -66 689 -66 735 -66 163 -98 229 -100 595 -168 195 -130 263 -132 957 -66 493 -98 65 -132 723 -98 431 -166 4275 -132 333 -100 97 -68 827 -66 491 -198 327 -264 4271 -134 1087 -100 797 -134 131 -302 695 -66 261 -236 465 -66 4191 -66 663 -100 433 -66 1325 -100 229 -132 295 -100 261 -100 431 -66 531 -130 195 -98 4647 -66 229 -66 359 -264 525 -98 365 -232 165 -264 457 -100 231 -66 629 -366 229 -66 3411 -68 1163 -132 763 -66 165 -66 1657 -66 393 -132 521 -262 5821 -132 1221 -98 129 -100 1345 -66 425 -198 165 -66 525 -66 299 -100 465 -66 497 -100 599 -298 4711 -100 397 -66 99 -132 627 -66 793 -98 327 -98 97 -68 265 -66 99 -98 265 -1152602 163 -1918 65 -330 197 -98 97 -198 721 -66 897 -232 4175 -166 299 -134 299 -132 331 -100 363 -66 499 -166 527 -98 1327 -68 931 -66 701 -198 265 -298 131 -100 301 -132 1029 -132 295 -134 3515 -66 197 -100 427 -100 99 -100 165 -98 231 -100 +RAW_Data: 4709 -232 163 -66 129 -96 1557 -100 229 -64 621 -100 4377 -100 1895 -66 2321 -66 4485 -66 167 -66 67 -66 1395 -68 65 -132 429 -134 1855 -68 231 -100 5991 -366 629 -100 2525 -68 233 -264 295 -98 853 -132 4433 -66 163 -66 457 -98 657 -66 331 -66 825 -66 785 -166 293 -66 559 -100 4747 -228 887 -66 895 -130 1457 -100 6935 -100 229 -66 329 -162 1051 -66 461 -100 97 -100 4427 -66 859 -68 299 -100 165 -100 1249 -132 197 -198 4129 -66 299 -268 927 -164 557 -98 261 -66 2419 -66 4739 -100 97 -66 2309 -66 97 -66 987 -198 325 -64 5109 -66 199 -100 1059 -66 723 -100 763 -100 5355 -98 201 -134 265 -100 165 -100 827 -66 595 -100 297 -100 231 -132 4787 -100 361 -100 429 -200 733 -66 633 -66 531 -330 4215 -68 1361 -200 5943 -66 1117 -132 953 -66 261 -132 199 -134 235 -200 261 -66 165 -132 65 -98 395 -266 197 -98 791 -134 229 -100 1195 -68 363 -100 99 -66 525 -98 759 -66 663 -98 163 -98 3411 -100 1033 -132 133 -134 133 -100 363 -100 663 -66 131 -66 331 -100 299 -134 65 -66 1025 -100 397 -66 2051 -66 697 -66 605 -390 415 -398 409 -400 393 -438 387 -410 387 -410 385 -448 383 -414 385 -416 387 -416 387 -448 387 -4030 787 -422 449 -810 425 -816 423 -838 417 -844 399 -852 805 -456 811 -412 837 -418 415 -814 823 -434 385 -878 385 -848 799 -474 383 -850 819 -452 405 -820 385 -844 415 -842 403 -848 809 -456 779 -446 417 -862 413 -814 417 -852 785 -452 799 -436 811 -450 415 -832 807 -444 411 -838 803 -446 407 -850 807 -410 807 -452 819 -418 817 -448 815 -434 809 -450 409 -840 817 -418 417 -818 417 -862 773 -476 773 -450 803 -446 409 -866 419 -848 793 -436 387 -838 419 -846 793 -438 415 -846 409 -846 811 -446 813 -422 423 -838 783 -448 413 -838 379 -866 807 -450 383 -862 807 -450 813 -412 415 -816 387 -15812 443 -358 433 -386 409 -386 447 -350 451 -382 419 -384 421 -384 419 -386 421 -386 421 -388 419 -388 419 -4024 837 -410 415 -814 421 -848 399 -822 413 -850 419 -824 841 -444 809 -422 815 -416 413 -842 801 -438 411 -844 409 -846 811 -446 413 -832 841 -414 409 -840 419 -812 425 -820 393 -866 811 -418 841 -430 415 -846 419 -820 405 -850 807 -418 819 -416 809 -454 395 -854 817 -450 419 -848 807 -412 413 -850 785 -450 811 -432 809 -412 837 -416 831 -458 811 -452 389 -854 787 -454 407 -824 413 -814 821 -448 815 -432 809 -450 395 -864 421 -814 827 -434 387 -840 421 -814 +RAW_Data: 825 -434 415 -846 421 -816 837 -444 809 -420 423 -806 813 -450 383 -840 417 -864 807 -444 381 -848 821 -454 805 -434 379 -806 417 -15798 421 -428 321 -504 321 -474 323 -508 293 -508 325 -468 323 -476 365 -444 365 -444 333 -466 359 -464 331 -4092 749 -490 351 -870 381 -882 361 -888 381 -838 411 -860 807 -450 815 -434 809 -414 417 -824 803 -442 413 -846 417 -854 773 -476 381 -882 793 -426 413 -844 387 -854 397 -826 417 -848 787 -462 813 -448 395 -864 419 -812 429 -818 821 -418 809 -448 783 -460 411 -846 785 -486 399 -850 811 -414 415 -828 801 -442 809 -450 789 -456 807 -452 785 -458 805 -450 411 -836 819 -418 419 -820 415 -830 805 -474 777 -450 815 -450 417 -852 413 -812 811 -424 411 -838 419 -844 795 -436 385 -878 409 -844 809 -444 813 -424 423 -838 777 -450 413 -842 399 -860 777 -448 413 -868 803 -448 791 -424 411 -812 413 -15768 435 -396 429 -358 435 -378 411 -410 413 -380 439 -394 409 -406 379 -412 407 -414 409 -382 413 -410 417 -3998 837 -414 409 -840 421 -810 427 -820 421 -838 421 -844 797 -472 805 -418 815 -408 419 -850 799 -438 415 -816 421 -852 799 -438 411 -874 803 -416 413 -842 403 -836 413 -842 395 -826 813 -452 807 -458 407 -846 419 -822 409 -816 839 -118766 99 -132 819 -98 917 -132 233 -100 167 -66 533 -66 525 -64 163 -98 947 -66 327 -66 425 -132 229 -66 331 -68 1949 -98 261 -132 261 -66 1187 -66 295 -100 5279 -68 595 -98 527 -66 1293 -68 97 -134 527 -68 4543 -66 229 -64 927 -332 199 -132 363 -264 825 -100 829 -68 565 -200 3743 -66 2027 -66 1425 -100 1301 -66 229 -196 99 -98 4645 -422 353 -446 385 -418 385 -416 387 -450 357 -450 355 -450 357 -450 359 -450 357 -450 359 -450 359 -4096 779 -448 771 -474 777 -444 779 -480 775 -482 367 -880 779 -446 387 -918 781 -418 819 -416 813 -454 779 -448 385 -882 379 -854 779 -484 385 -872 787 -454 793 -456 781 -448 385 -850 405 -854 779 -448 803 -458 785 -484 395 -854 787 -430 809 -446 383 -842 793 -486 383 -856 777 -478 379 -906 381 -824 803 -432 807 -446 785 -442 783 -482 785 -454 381 -872 375 -918 787 -418 815 -418 391 -860 785 -448 385 -876 787 -460 775 -484 387 -890 385 -834 777 -450 385 -852 417 -854 379 -856 381 -882 369 -876 777 -482 791 -454 771 -446 377 -886 779 -446 383 -874 769 -458 819 -450 785 -456 779 -420 383 -16402 383 -444 385 -416 385 -448 355 -452 355 -450 359 -450 355 -450 357 -452 359 -450 +RAW_Data: 357 -450 359 -450 357 -4096 777 -448 799 -446 761 -464 775 -478 769 -454 419 -846 799 -466 381 -876 799 -440 801 -440 775 -448 811 -448 379 -872 385 -848 803 -464 381 -876 781 -454 783 -448 783 -448 411 -836 421 -848 763 -494 775 -482 779 -480 379 -858 779 -448 777 -448 379 -866 787 -484 353 -886 785 -462 383 -878 411 -854 775 -444 769 -454 807 -454 789 -452 803 -446 377 -864 419 -878 779 -436 809 -414 415 -860 775 -434 383 -876 787 -486 765 -490 349 -906 379 -858 777 -446 383 -842 409 -856 387 -850 393 -856 391 -856 785 -518 763 -460 779 -446 385 -852 803 -430 381 -872 801 -452 783 -472 777 -450 785 -428 377 -16446 411 -422 355 -478 353 -448 353 -448 359 -448 361 -450 357 -450 357 -450 357 -450 359 -446 353 -450 361 -4086 807 -442 769 -454 809 -452 783 -448 773 -480 381 -860 777 -472 383 -908 769 -448 801 -452 779 -432 809 -448 365 -882 387 -846 795 -456 381 -910 787 -426 809 -446 779 -446 379 -862 407 -846 783 -472 779 -482 777 -482 391 -860 783 -446 779 -448 395 -852 777 -482 385 -872 773 -448 417 -888 385 -834 785 -456 777 -448 801 -456 769 -458 809 -444 391 -858 421 -878 777 -434 807 -444 377 -856 777 -448 387 -872 803 -450 801 -452 389 -886 391 -858 757 -454 381 -854 391 -856 387 -848 397 -866 377 -872 797 -488 781 -470 773 -446 369 -878 751 -476 383 -848 787 -482 763 -494 777 -448 789 -400 401 -16398 419 -418 355 -434 385 -442 351 -446 383 -450 353 -450 357 -450 355 -450 357 -448 359 -450 357 -450 357 -4088 805 -414 803 -458 781 -446 781 -482 783 -450 383 -882 771 -468 383 -884 783 -456 783 -446 779 -450 783 -456 417 -844 371 -888 777 -482 367 -910 769 -452 771 -460 777 -446 379 -886 385 -848 787 -456 807 -446 815 -482 379 -858 783 -446 779 -448 395 -852 775 -480 357 -872 803 -448 409 -890 383 -850 763 -460 779 -446 803 -460 777 -480 781 -448 381 -870 379 -914 769 -440 799 -434 383 -872 767 -486 383 -856 777 -478 771 -480 379 -890 383 -850 767 -462 385 -872 351 -882 367 -866 383 -878 365 -878 775 -516 767 -460 779 -448 383 -848 803 -464 349 -876 785 -486 763 -492 777 -412 787 -454 359 -16460 421 -392 415 -392 405 -412 379 -432 385 -440 351 -448 385 -416 385 -418 387 -450 355 -450 357 -450 357 -4092 775 -446 799 -454 769 -458 805 -444 769 -452 405 -882 785 -446 383 -884 813 -418 809 -454 785 -452 779 -448 375 -886 387 -848 787 -456 379 -908 801 -418 785 -452 813 -448 +RAW_Data: 379 -838 421 -846 767 -494 777 -480 777 -480 379 -858 779 -446 779 -448 379 -898 755 -482 385 -842 803 -446 409 -890 383 -850 765 -462 775 -446 801 -458 775 -484 783 -450 379 -876 383 -886 783 -474 775 -446 365 -880 779 -448 385 -850 793 -486 779 -468 381 -884 391 -856 779 -446 383 -850 383 -854 409 -854 355 -900 385 -848 805 -464 813 -452 779 -420 419 -844 763 -460 383 -876 799 -454 769 -494 775 -444 771 -426 411 -16408 355 -436 383 -444 383 -416 385 -418 385 -452 353 -450 357 -450 357 -452 357 -446 355 -446 387 -410 391 -4094 775 -448 789 -430 807 -446 777 -450 813 -450 385 -848 807 -468 381 -884 783 -456 777 -123134 99 -266 65 -594 99 -232 301 -134 2911 -134 861 -66 529 -98 569 -66 1593 -68 429 -66 5405 -132 263 -98 821 -66 397 -66 361 -66 491 -100 165 -66 495 -66 3677 -132 825 -68 529 -134 299 -66 165 -68 661 -68 203 -134 135 -100 429 -100 397 -98 859 -66 365 -166 399 -98 2811 -68 229 -66 567 -164 1153 -168 167 -134 265 -100 631 -66 1493 -132 65 -98 199 -68 4269 -132 365 -202 1493 -132 299 -66 131 -66 465 -66 231 -98 229 -96 165 -130 3593 -100 927 -100 231 -200 1825 -68 265 -66 1557 -100 65 -100 463 -68 495 -100 2591 -232 1027 -100 229 -98 461 -66 1847 -100 265 -66 4369 -270 131 -100 131 -66 1725 -66 397 -98 1659 -66 329 -132 6605 -234 397 -134 1427 -100 231 -166 5117 -66 663 -98 499 -66 1331 -66 931 -66 563 -166 4219 -66 265 -164 491 -166 263 -164 557 -100 1483 -100 361 -98 461 -66 497 -102 4575 -100 1327 -98 265 -234 131 -100 399 -100 565 -100 637 -100 463 -64 3975 -166 197 -202 333 -132 199 -132 693 -68 631 -66 165 -100 265 -66 297 -200 165 -166 199 -364 3717 -198 761 -66 197 -134 395 -66 99 -66 695 -198 797 -132 397 -200 431 -102 6087 -198 199 -100 399 -100 361 -100 333 -100 827 -166 1135 -132 333 -100 497 -68 3915 -130 363 -98 897 -66 99 -66 297 -230 395 -66 195 -66 5099 -100 687 -66 915 -66 165 -66 95 -230 1181 -98 593 -98 301 -198 4807 -132 431 -232 367 -198 629 -164 199 -68 65 -100 331 -100 431 -132 1397 -66 165 -332 297 -66 1131 -466 533 -132 463 -198 861 -98 293 -66 4561 -100 459 -66 691 -130 557 -292 393 -66 161 -66 425 -66 163 -130 1147 -96 4103 -100 399 -132 395 -66 429 -66 131 -68 197 -98 199 -98 167 -100 65 -232 67 -132 165 -68 235 -66 467 -68 1027 -66 4281 -198 261 -66 861 -166 231 -198 333 -66 +RAW_Data: 231 -196 1265 -100 133 -68 933 -100 7293 -66 331 -132 295 -68 1157 -98 261 -132 1223 -200 929 -98 1291 -68 3909 -132 199 -100 2729 -66 195 -134 829 -132 263 -262 329 -68 1559 -100 497 -66 5119 -64 949 -66 533 -166 397 -68 559 -98 263 -98 951 -66 3273 -134 665 -100 65 -100 231 -66 267 -132 267 -100 1131 -132 133 -66 129 -130 885 -66 193 -164 229 -264 65 -66 889 -130 259 -164 689 -66 4127 -130 229 -66 1545 -98 199 -100 131 -100 2059 -66 329 -98 197 -66 4261 -166 791 -98 199 -68 465 -66 233 -66 265 -66 2983 -64 327 -98 229 -66 2403 -132 259 -98 953 -100 1657 -100 761 -498 799 -66 433 -200 495 -98 229 -66 1179 -68 5479 -232 627 -66 693 -66 265 -66 265 -66 463 -100 231 -68 499 -66 133 -100 299 -66 4899 -66 195 -164 621 -130 229 -130 231 -98 261 -66 97 -334 397 -100 425 -98 729 -134 65 -134 367 -102 463 -66 3197 -132 893 -66 495 -164 461 -66 197 -164 1715 -100 663 -66 99 -166 265 -100 201 -98 233 -66 133 -100 829 -132 361 -68 561 -98 199 -66 431 -134 263 -100 2071 -100 661 -66 461 -98 227 -66 1149 -66 297 -98 631 -98 329 -66 463 -66 263 -66 951 -64 3587 -66 1413 -66 919 -66 689 -132 847 -132 459 -100 161 -196 4049 -66 301 -68 397 -134 427 -98 229 -100 263 -134 335 -68 397 -98 259 -100 823 -100 399 -66 165 -66 3571 -132 563 -166 231 -100 197 -132 867 -66 433 -68 761 -98 335 -100 99 -98 99 -66 1751 -230 1019 -100 765 -100 231 -68 365 -134 4643 -66 367 -68 297 -66 531 -166 1333 -66 531 -66 67 -98 959 -66 791 -66 3787 -66 261 -132 721 -362 133 -66 897 -134 629 -100 1395 -368 1127 -132 895 -100 463 -66 1793 -66 5239 -66 331 -66 1095 -68 433 -68 6899 -98 1527 -132 267 -66 67 -66 131 -100 495 -66 4027 -68 999 -132 959 -134 265 -66 199 -68 299 -132 999 -134 65 -66 497 -98 67 -66 265 -132 4751 -66 231 -98 263 -98 463 -66 99 -66 859 -66 327 -66 435 -66 2987 -100 993 -68 1025 -66 1491 -68 1095 -100 297 -66 4409 -98 787 -132 2569 -66 957 -68 795 -64 593 -100 335 -66 1675 -100 7041 -134 1727 -134 65 -100 1029 -132 1027 -66 4653 -66 65 -66 995 -100 1129 -66 267 -66 231 -68 469 -66 465 -134 4047 -68 165 -100 597 -166 729 -100 665 -66 1115 -68 293 -130 297 -66 1607 -398 393 -410 409 -410 415 -378 413 -412 413 -382 417 -396 419 -396 409 -382 409 -404 423 -408 385 -4052 417 -820 401 -820 809 -450 +RAW_Data: 823 -420 417 -848 797 -472 381 -876 791 -428 413 -844 817 -426 379 -878 385 -848 403 -852 807 -452 379 -878 801 -452 801 -438 385 -838 419 -846 399 -852 379 -850 315 -110416 645 -1548 1691 -486 1667 -518 1695 -488 1671 -498 1697 -508 571 -1588 583 -1570 561 -1574 1689 -504 583 -1560 1679 -520 587 -1574 1677 -488 1701 -484 1697 -520 1677 -492 1693 -488 581 -1566 591 -1574 601 -1546 607 -1568 577 -1566 581 -1576 579 -16394 591 -1548 1679 -518 1671 -520 1679 -516 1663 -514 1689 -486 583 -1580 579 -1578 579 -1566 1701 -494 571 -1568 1697 -514 563 -1570 1693 -506 1669 -516 1695 -496 1681 -518 1667 -506 573 -1576 603 -1544 613 -1568 597 -1538 607 -1566 581 -1580 577 -16400 619 -1534 1703 -494 1677 -480 1697 -506 1675 -518 1669 -518 587 -1580 581 -1562 579 -1576 1683 -488 589 -1582 1677 -524 583 -1570 1653 -510 1703 -518 1641 -518 1693 -502 1677 -516 579 -1570 579 -1578 581 -1566 615 -1566 585 -1564 585 -1580 585 -16408 595 -1546 1685 -518 1673 -490 1669 -548 1665 -518 1675 -494 587 -1552 595 -1570 605 -1556 1673 -520 581 -1578 1665 -520 581 -1558 1701 -484 1681 -516 1667 -516 1697 -508 1675 -490 603 -1570 577 -1566 593 -1572 603 -1576 575 -1568 579 -1578 579 -16400 625 -1554 1667 -500 1699 -482 1691 -506 1679 -518 1673 -520 585 -1566 579 -1560 611 -1548 1697 -488 577 -1602 1651 -518 583 -1578 1667 -508 1691 -522 1675 -512 1665 -502 1699 -482 605 -1574 585 -1566 601 -1570 577 -1576 581 -1576 579 -1566 579 -16452 591 -1534 1693 -508 1683 -518 1673 -520 1673 -488 1711 -514 575 -1562 589 -1570 603 -1568 1667 -518 589 -1578 1677 -486 605 -1570 1701 -484 1701 -504 1697 -514 1667 -510 1717 -518 581 -1578 615 -91426 233 -298 197 -332 593 -162 261 -98 131 -68 163 -692 821 -132 227 -64 131 -66 197 -132 163 -66 263 -68 363 -134 461 -98 65 -66 229 -298 295 -66 3173 -64 987 -164 425 -98 193 -98 693 -66 723 -196 391 -496 431 -100 229 -68 499 -100 63 -66 165 -66 397 -98 133 -100 533 -166 1179 -166 361 -100 3973 -98 825 -100 131 -98 723 -66 1053 -66 131 -68 197 -100 199 -100 331 -66 165 -100 429 -68 231 -66 567 -66 533 -100 593 -68 1463 -66 2093 -100 599 -66 361 -66 361 -132 261 -462 263 -66 621 -98 555 -100 131 -68 4609 -200 199 -164 1033 -66 863 -132 131 -68 331 -100 4565 -98 229 -66 1313 -130 589 -166 3959 -100 531 -100 263 -132 361 -166 229 -64 165 -66 231 -328 261 -66 229 -66 165 -100 235 -100 299 -132 265 -266 165 -66 4417 -100 261 -68 1625 -64 165 -98 229 -160 259 -98 99 -332 593 -166 167 -102 +RAW_Data: 199 -66 1063 -98 267 -100 263 -332 5513 -64 229 -232 685 -232 661 -98 431 -66 1021 -66 4085 -64 655 -66 2313 -66 531 -66 263 -100 197 -134 67 -132 901 -66 1591 -66 4217 -100 827 -100 165 -66 299 -166 1987 -166 4097 -66 365 -66 131 -332 131 -100 625 -66 361 -98 131 -196 1679 -66 163 -98 293 -100 2275 -98 1689 -66 825 -98 627 -164 297 -398 461 -66 4707 -66 795 -134 329 -132 597 -66 777 -6038 371 -1818 167 -668 693 -622 1573 -638 465 -1666 503 -1626 541 -1622 1635 -518 577 -1578 1669 -512 567 -1606 1667 -502 1677 -516 1693 -500 1673 -518 1675 -522 587 -1562 581 -1558 625 -1546 587 -1558 611 -1560 571 -1568 609 -16352 633 -1548 1689 -476 1707 -490 1687 -482 1709 -502 1675 -520 579 -1578 577 -1568 577 -1568 1705 -486 581 -1586 1695 -486 579 -1568 1679 -520 1673 -520 1669 -508 1707 -498 1677 -514 577 -1566 615 -1550 581 -1568 615 -1532 617 -1546 617 -1532 615 -16406 599 -1530 1691 -514 1695 -484 1679 -530 1659 -518 1669 -514 601 -1568 577 -1578 577 -1568 1699 -492 573 -1576 1707 -486 577 -1600 1655 -518 1669 -530 1691 -486 1673 -514 1691 -502 585 -1576 585 -1580 577 -1592 573 -1576 571 -1570 613 -1570 559 -16430 619 -1538 1703 -462 1723 -484 1697 -488 1691 -488 1715 -462 635 -1534 607 -1556 589 -1580 1685 -488 621 -1538 1711 -492 589 -1548 1717 -478 1693 -514 1687 -504 1693 -484 1705 -482 605 -1570 579 -1566 619 -1550 581 -1580 579 -1568 615 -1536 619 -16382 625 -1534 1709 -490 1683 -514 1673 -504 1677 -518 1671 -518 587 -1560 577 -1588 587 -1580 1681 -482 609 -1558 1677 -520 581 -1576 1663 -530 1659 -518 1673 -514 1691 -504 1697 -510 587 -1572 575 -1564 591 -1570 603 -1574 577 -1568 579 -1578 579 -16430 599 -1564 1693 -484 1695 -486 1675 -500 1713 -484 1709 -488 579 -1568 593 -1576 603 -1570 1665 -520 581 -1560 1699 -482 603 -1580 1677 -518 1667 -520 1671 -488 1711 -482 1699 -516 599 -1570 577 -1578 577 -1568 579 -1570 617 -1566 581 -1560 587 -16424 629 -1542 1677 -482 1695 -514 1687 -484 1711 -494 1675 -514 571 -1584 587 -1572 599 -1542 1695 -520 585 -1544 1701 -510 593 -1568 1665 -514 1689 -506 1669 -508 1679 -528 1669 -520 579 -1576 579 -1568 581 -1582 581 -1578 579 -1568 615 -1564 585 -16412 631 -1540 1683 -516 1671 -510 1653 -506 1691 -518 1671 -514 601 -1570 577 -1578 577 -1578 1665 -498 603 -1566 1693 -514 563 -1576 1699 -506 1663 -514 1701 -478 1687 -520 1669 -500 603 -1568 605 -1558 589 -1574 601 -1542 607 -1576 577 -1576 577 -16434 597 -1534 1695 -516 1687 -488 1707 -486 1683 -518 1693 -496 583 -1574 599 -1542 605 -1578 1665 -500 611 -1550 1711 -478 605 -1558 +RAW_Data: 1679 -518 1675 -502 1699 -482 1693 -506 1679 -510 613 -1562 581 -1590 569 -1570 605 -1560 587 -1572 599 -1542 607 -16400 625 -1560 1669 -508 1677 -518 1667 -518 1695 -486 1709 -464 601 -1566 607 -1560 589 -1576 1691 -476 615 -1562 1677 -486 625 -1542 1709 -486 1703 -484 1697 -520 1673 -506 1695 -498 585 -1570 603 -1544 609 -1572 579 -1570 583 -1580 581 -1582 579 -16410 629 -1524 1715 -484 1679 -518 1673 -516 1663 -518 1697 -504 583 -1576 577 -1592 587 -1552 1681 -514 579 -1580 1681 -516 577 -1566 1705 -494 1689 -488 1705 -486 1695 -520 1675 -522 587 -1574 579 -1584 581 -1576 599 -1570 575 -1578 579 -1564 613 -89724 99 -1166 129 -1158 167 -532 163 -98 361 -198 299 -298 197 -66 2489 -66 3797 -66 1937 -66 1479 -98 557 -66 361 -66 1709 -66 6741 -98 1889 -132 5693 -68 7367 -100 8643 -100 2655 -100 3727 -66 1023 -66 865 -66 1161 -100 6287 -66 3987 -66 1961 -132 3307 -68 2183 -66 2429 -132 4179 -66 753 -66 461 -66 621 -64 5761 -100 331 -66 1683 -198 465 -66 629 -68 7341 -66 357 -198 261 -66 593 -234 331 -100 593 -64 293 -64 1611 -100 459 -100 297 -100 561 -134 1725 -66 6901 -100 1407 -68 8017 -200 1989 -66 5721 -100 199 -166 1251 -66 359 -130 9965 -66 4361 -100 819 -100 1051 -66 2187 -64 555 -66 861 -66 5889 -66 +RAW_Data: 361 -4326 2555 -28090 65 -302 65 -1890 131 -2062 2547 -95068 2565 -48020 67 -47036 2551 -30016 2561 -47778 67 -14884 2481 -95118 2537 -95132 2549 -48496 65 -21526 2571 -16342 97 -4142 65 -1938 2543 -95008 2551 -44030 1579 -548 1469 -588 433 -1568 469 -1540 479 -1550 1489 -538 473 -1574 1467 -538 1493 -566 443 -22130 693 -3742 321 -1702 341 -1690 1353 -628 391 -1666 1401 -584 1445 -596 391 -22192 1487 -554 1471 -550 447 -1584 453 -1568 445 -1578 1469 -550 459 -1598 1465 -540 1461 -576 445 -22164 1479 -546 1501 -544 453 -1568 447 -1580 445 -1584 1451 -560 443 -1594 1473 -548 1479 -556 441 -22160 1475 -548 1487 -554 449 -1578 451 -1586 451 -1572 1469 -552 449 -1578 1473 -558 1469 -552 449 -22142 1507 -546 1479 -548 457 -1574 447 -1580 447 -1584 1475 -554 441 -1592 1465 -546 1473 -546 483 -22132 1157 -1082 67 -166 65 -198 65 -1150 257 -1706 339 -1666 365 -1642 1409 -610 407 -1626 1417 -620 1435 -580 409 -22184 1495 -572 1463 -552 447 -1584 453 -1574 447 -1580 1473 -548 459 -1568 1501 -520 1501 -552 463 -22124 535 -7674 339 -1680 1345 -678 361 -1664 1395 -618 1411 -646 373 -22194 1511 -526 1477 -550 483 -1542 479 -1562 471 -1566 1483 -546 445 -1574 1471 -534 1497 -548 483 -169938 2539 -89172 167 -5768 2551 -93014 967 -252 1571 -5844 2503 -87590 65 -5948 2523 -64008 1605 -520 1473 -554 449 -1584 449 -1576 447 -1546 1505 -546 451 -1574 1469 -546 1475 -548 459 -22160 1525 -506 1501 -516 479 -1550 469 -1570 469 -1570 1471 -538 471 -1568 1489 -516 1501 -554 443 -22152 1507 -522 1511 -518 481 -1580 451 -1542 483 -1548 1505 -546 451 -1570 1471 -552 1473 -548 455 -22180 1505 -522 1501 -544 451 -1560 475 -1548 481 -1556 1477 -552 475 -1546 1505 -516 1517 -516 473 -22148 1515 -518 1499 -546 485 -1570 443 -1580 447 -1582 1475 -526 483 -1548 1507 -546 1479 -558 449 -22138 1495 -586 1437 -616 403 -1594 429 -1608 417 -1606 1465 -556 433 -1606 1465 -536 1499 -544 445 -22170 1401 -792 1245 -770 271 -1716 325 -1680 359 -1664 1397 -620 393 -1644 1407 -614 1433 -586 429 -22202 1477 -572 1469 -548 449 -1576 457 -1578 479 -1544 1473 -590 429 -1570 1491 -540 1509 -554 429 -22196 1481 -556 1467 -580 439 -1592 431 -1608 415 -1608 1441 -586 463 -1568 1467 -552 1489 -534 473 -22166 1485 -574 1469 -580 453 -1572 443 -1578 447 -1578 1477 -554 447 -1580 1479 -550 1481 -550 475 -22160 1525 -542 1467 -540 483 -1574 447 -1582 451 -1578 1481 -558 447 -1560 1493 -550 1505 -544 449 -22182 1435 -646 1391 -664 369 -1642 385 -1652 395 -1600 1463 -584 417 -1606 1467 -576 1443 -586 429 -148588 275 -106 847 -128 877 -100 281 -97826 2541 -92318 2505 -12474 2563 -73674 2555 -41600 2543 -76554 1601 -516 1471 -554 +RAW_Data: 487 -1556 453 -1570 441 -1590 1467 -556 469 -1538 1501 -522 1501 -552 463 -22124 1513 -514 1509 -548 445 -1582 461 -1568 443 -1574 1467 -536 465 -1574 1499 -516 1503 -556 441 -22150 1537 -484 1495 -544 479 -1558 459 -1574 447 -1580 1471 -548 459 -1576 1471 -554 1471 -550 459 -22160 1517 -516 1503 -546 447 -1574 473 -1546 481 -1576 1479 -520 467 -1572 1497 -550 1469 -556 441 -22156 1533 -528 1495 -530 469 -1568 445 -1566 485 -1548 1505 -544 451 -1572 1471 -554 1503 -516 495 -22160 1511 -520 1477 -546 483 -1542 485 -1546 481 -1542 1509 -526 455 -1578 1507 -544 1481 -524 485 -22146 1543 -510 1481 -552 475 -1546 473 -1568 481 -1540 1503 -522 473 -1556 1507 -558 1491 -518 479 -22164 1523 -520 1503 -544 453 -1566 479 -1550 469 -1572 1493 -546 447 -1576 1477 -552 1509 -544 451 -22178 639 -596 65 -926 63 -1954 237 -1680 355 -1670 383 -1636 1423 -596 439 -1578 1471 -574 1465 -576 445 -22182 1439 -676 1335 -702 339 -1648 387 -1648 409 -1608 1429 -618 419 -1612 1447 -578 1439 -614 425 -22186 1497 -530 1505 -556 471 -1540 479 -1582 431 -1570 1497 -546 481 -1536 1497 -554 1501 -556 463 -22178 1503 -566 1433 -586 433 -1604 445 -1576 441 -1580 1503 -548 453 -1572 1473 -578 1467 -558 469 -149080 311 -1034 895 -1084 939 -1006 337 -78790 97 -6890 2535 -28048 2511 -64482 2573 -88138 65 -6956 2515 -95140 2509 -61986 1589 -522 1475 -548 449 -1576 475 -1548 481 -1546 1475 -564 443 -1564 1477 -580 1477 -554 449 -22166 1503 -532 1481 -544 447 -1576 449 -1572 473 -1548 1473 -580 451 -1568 1463 -552 1477 -552 461 -22164 1521 -528 1493 -548 443 -1580 461 -1540 475 -1574 1473 -554 471 -1568 1489 -516 1497 -538 471 -22154 1385 -786 1245 -796 261 -1702 341 -1650 391 -1644 1411 -608 409 -1620 1415 -624 1409 -614 413 -22178 1533 -498 1519 -546 445 -1576 449 -1578 457 -1568 1503 -546 451 -1570 1467 -554 1503 -552 463 -22162 1401 -774 1257 -754 297 -1678 353 -1650 383 -1666 1397 -618 385 -1642 1437 -612 1433 -586 427 -22194 1483 -552 1477 -546 485 -1544 453 -1576 481 -1576 1479 -560 449 -1548 1507 -546 1479 -558 447 -22170 1413 -786 1245 -776 295 -1664 337 -1680 387 -1652 1385 -638 407 -1614 1441 -582 1451 -606 409 -22204 1503 -528 1507 -550 465 -1534 481 -1584 429 -1572 1497 -548 447 -1580 1481 -550 1503 -546 455 -22166 1519 -552 1469 -580 419 -1612 419 -1596 447 -1582 1479 -554 447 -1582 1479 -550 1519 -518 479 -134730 169 -172 559 -194 579 -676 257 -87040 99 -7384 2269 -488 313 -37888 65 -12084 2515 -33298 2247 -474 309 -42656 63 -51970 2233 -456 309 -94658 2233 -430 325 -90692 357 -186 2417 -518 1517 -514 475 -1550 467 -1570 471 -1538 1499 -556 443 -1564 1483 -552 1501 -524 +RAW_Data: 481 -22144 1433 -668 1373 -666 337 -1680 355 -1634 383 -1634 1431 -622 399 -1606 1443 -582 1443 -586 431 -22166 1521 -512 1497 -548 447 -1580 459 -1568 447 -1574 1473 -554 465 -1572 1497 -516 1507 -554 453 -22148 1503 -540 1489 -546 479 -1542 449 -1578 457 -1576 1475 -546 483 -1540 1507 -552 1473 -548 453 -22154 1539 -516 1469 -586 431 -1574 447 -1576 479 -1558 1485 -554 439 -1580 1501 -550 1457 -552 473 -22154 1521 -550 1485 -540 447 -1578 457 -1578 447 -1572 1471 -556 485 -1540 1483 -552 1501 -556 443 -22186 1437 -662 1345 -704 339 -1688 361 -1646 375 -1640 1399 -616 421 -1612 1441 -614 1435 -586 429 -22200 1513 -520 1503 -544 453 -1572 447 -1580 483 -1550 1475 -552 475 -1548 1501 -554 1491 -540 475 -22184 1499 -530 1485 -548 457 -1570 477 -1578 443 -1576 1501 -546 449 -1562 1499 -538 1493 -572 443 -22166 1471 -612 1429 -624 401 -1608 417 -1620 431 -1600 1461 -582 419 -1610 1441 -582 1473 -548 459 -205658 1761 -436 863 -68558 2597 -23434 1775 -438 519 -94592 1803 -422 829 -86706 1727 -434 527 -66 319 -94536 1753 -444 865 -238 1489 -652 1385 -656 357 -1668 365 -1628 403 -1612 1439 -614 403 -1626 1425 -588 1443 -584 409 -22168 1531 -528 1491 -530 469 -1568 443 -1564 483 -1538 1505 -546 455 -1570 1469 -556 1503 -518 491 -22128 1455 -660 1359 -686 355 -1632 385 -1632 411 -1640 1411 -596 423 -1606 1437 -580 1443 -596 423 -22188 1495 -550 1467 -534 473 -1566 481 -1540 483 -1572 1477 -552 449 -1582 1477 -558 1471 -552 449 -22174 543 -3692 235 -1680 357 -1668 353 -1698 1375 -636 371 -1644 1409 -610 1437 -578 449 -22180 1499 -542 1491 -542 479 -1540 481 -1566 449 -1562 1499 -554 465 -1568 1463 -554 1493 -536 473 -22168 1405 -774 1257 -788 267 -1676 355 -1672 369 -1660 1387 -624 417 -1606 1427 -624 1425 -586 415 -22208 1507 -552 1489 -548 443 -1562 483 -1550 481 -1546 1505 -558 447 -1586 1473 -548 1481 -554 479 -22166 1383 -786 1245 -776 295 -1664 373 -1646 387 -1644 1403 -642 411 -1624 1417 -594 1445 -612 409 -22210 1515 -516 1503 -554 453 -1564 457 -1572 447 -1580 1501 -554 465 -1570 1467 -554 1493 -570 443 -195814 1353 -442 1267 -37900 99 -53710 411 -142 2207 -8336 2487 -46990 1301 -452 1317 -86162 65 -8376 1289 -444 1327 -94552 1311 -488 1251 -1726 1531 -544 1467 -580 417 -1578 457 -1578 443 -1576 1475 -566 441 -1568 1503 -512 1509 -526 455 -22168 1473 -582 1469 -538 483 -1538 459 -1574 447 -1580 1471 -582 429 -1572 1469 -552 1473 -548 461 -22170 1437 -640 1401 -620 393 -1636 385 -1634 409 -1604 1457 -588 415 -1608 1431 -590 1459 -554 449 -22188 1483 -556 1487 -542 445 -1578 459 -1576 443 -1574 1473 -556 469 -1570 1459 -550 1503 -554 443 -22160 1469 -590 +RAW_Data: 1469 -582 409 -1620 429 -1608 415 -1602 1455 -568 441 -1578 1475 -552 1479 -560 467 -22166 1483 -572 1463 -546 451 -1580 453 -1580 481 -1542 1475 -588 451 -1570 1453 -550 1503 -540 469 -22158 1505 -554 1475 -554 487 -1556 453 -1562 477 -1546 1495 -546 479 -1544 1511 -554 1477 -532 471 -22180 1471 -602 1437 -602 407 -1610 421 -1614 421 -1606 1433 -582 457 -1578 1475 -550 1469 -586 465 -22164 1515 -546 1469 -546 483 -1572 447 -1558 471 -1580 1471 -578 451 -1568 1495 -542 1497 -530 471 -22194 1433 -644 1393 -662 371 -1640 385 -1642 413 -1628 1431 -584 431 -1602 1469 -540 1501 -576 443 -22180 1479 -592 1439 -608 407 -1620 427 -1610 415 -1606 1465 -570 441 -1612 1441 -586 1451 -576 445 -22196 1533 -522 1489 -540 477 -1574 481 -1544 455 -1568 1505 -546 459 -1572 1501 -556 1497 -530 471 -210202 685 -540 475 -138 839 -138 239 -9950 2501 -71112 761 -462 511 -66 1297 -85614 97 -8850 745 -466 1821 -94546 827 -468 481 -140 815 -164 237 -32934 1583 -540 1477 -554 441 -1582 447 -1580 453 -1568 1465 -554 479 -1556 1487 -558 1477 -550 453 -22174 1503 -516 1483 -584 429 -1572 449 -1576 447 -1588 1475 -558 449 -1580 1473 -546 1485 -556 449 -22168 1469 -586 1443 -614 401 -1624 401 -1608 415 -1608 1463 -570 441 -1574 1471 -586 1449 -572 445 -22164 1401 -772 1255 -756 301 -1674 353 -1708 329 -1662 1397 -650 387 -1632 1405 -614 1433 -584 431 -22200 1489 -566 1463 -582 419 -1580 457 -1596 447 -1576 1473 -554 443 -1580 1469 -582 1451 -550 479 -22174 1387 -786 1251 -792 257 -1704 331 -1690 337 -1706 1377 -632 395 -1632 1433 -586 1451 -584 413 -22198 1541 -512 1485 -554 481 -1574 445 -1560 461 -1568 1495 -556 469 -1568 1495 -518 1503 -554 469 -22160 1541 -516 1501 -556 443 -1564 495 -1536 479 -1550 1517 -544 445 -1576 1507 -556 1485 -542 445 -22202 1501 -530 1503 -554 465 -1574 445 -1582 461 -1574 1469 -546 479 -1568 1471 -556 1513 -550 463 -22170 1473 -614 1419 -602 439 -1614 417 -1610 415 -1590 1465 -588 429 -1606 1469 -586 1455 -570 441 -22200 1407 -760 1277 -772 267 -1692 363 -1678 355 -1668 1393 -660 369 -1642 1441 -584 1449 -574 445 -22206 1533 -528 1497 -530 471 -1572 449 -1576 455 -1578 1507 -544 455 -1572 1503 -556 1467 -560 469 -22188 1439 -678 1347 -672 375 -1648 393 -1646 387 -1636 1431 -588 431 -1610 1439 -618 1437 -596 437 -143610 67 -510 99 -66 223 -272 173 -70 273 -204 235 -64 483 -26542 2521 -65448 297 -348 2417 -76720 65 -8388 65 -9450 203 -332 587 -72 885 -92 899 -27534 67 -39500 221 -406 521 -72 885 -98 919 -29920 1581 -534 1491 -548 445 -1580 461 -1544 477 -1574 1465 -556 473 -1568 1455 -554 1501 -524 483 -22136 1529 -498 +RAW_Data: -754 361 -17246 131 -8734 65 -71908 65 -27774 65 -1230 65 -13826 99 -800 65 -634 67 -796 99 -47716 65 -18338 67 -18176 131 -7986 65 -1084 131 -2090 65 -48694 163 -40926 65 -4538 65 -10224 65 -9874 20955 -1970 1023 -4928 1007 -4946 1011 -7910 981 -1992 1021 -7898 1013 -1962 1007 -7896 1019 -4930 995 -4962 987 -1974 1029 -4924 1019 -4920 1025 -7896 1005 -1964 1027 -7894 1007 -4932 1033 -1948 1009 -7916 1009 -1982 1001 -4942 1011 -7880 1011 -2000 1003 -13820 1011 -1990 985 -4944 1021 -4932 995 -7916 1025 -1944 1025 -7892 1015 -1970 1025 -7892 1001 -4934 1031 -4926 1015 -1970 989 -4938 1007 -4954 1019 -7874 1013 -2000 985 -7896 1011 -4950 1001 -1968 1023 -7900 999 -1968 1007 -4968 973 -7936 985 -1970 1009 -13856 1013 -1968 981 -4962 1009 -4926 1005 -7924 1003 -1968 1013 -7892 1003 -1992 985 -7904 1011 -4936 1023 -4948 997 -1960 1015 -4950 987 -4970 993 -7896 1005 -1970 1009 -7928 1005 -4914 1023 -1984 1001 -7894 1011 -1970 999 -4962 1003 -7890 1007 -1992 1013 -13810 1011 -1994 987 -4938 1005 -4964 999 -7904 1013 -1974 987 -7910 1021 -1964 1015 -7894 1021 -4912 1019 -4948 1007 -1970 1017 -4916 1021 -4946 985 -7914 1003 -1972 1009 -7898 1041 -4912 1013 -1994 985 -7892 1027 -1964 1013 -4938 1003 -7904 1023 -1966 1017 -13824 1025 -1968 983 -4950 1007 -4944 999 -7900 1029 -1964 1015 -7914 981 -2000 981 -7932 979 -4936 1021 -4926 1013 -1998 979 -4946 1035 -4908 1031 -7896 999 -1964 1025 -7902 1021 -4914 1023 -1966 1015 -7908 1007 -1970 1003 -4952 981 -7910 1001 -1974 1003 -13832 1015 -1976 999 -4958 1019 -4938 985 -7900 1025 -1966 1011 -7914 1015 -1964 1009 -7906 1017 -4908 1027 -4916 1011 -2000 981 -4954 1007 -4926 1017 -7920 1011 -1958 1009 -7906 1019 -4912 1025 -1980 1009 -7900 1013 -1948 1027 -4916 1011 -7918 1005 -1976 1003 -13846 1013 -1970 1005 -4914 1041 -4916 1007 -7918 987 -2000 987 -7902 1013 -1970 1015 -7894 1007 -4936 1019 -4944 1009 -1970 1013 -4918 1019 -4954 977 -7914 1019 -1964 1007 -7906 1003 -4956 983 -1982 1027 -7896 1005 -1962 1027 -4930 1007 -7906 999 -1972 1009 -13850 1009 -1952 1027 -4944 983 -4954 1023 -7894 1007 -1952 1031 -7882 1035 -1970 1011 -7894 1007 -4936 1025 -4920 1023 -1968 1011 -4928 1009 -4920 1015 -7902 1015 -1966 1021 -7894 1011 -4948 997 -1970 1009 -7902 1027 -1978 1011 -4930 999 -7918 1007 -1966 1013 -13824 1015 -1966 1027 -4918 1027 -4912 1021 -7888 1009 -1998 989 -7918 1019 -1960 989 -7930 983 -4946 1025 -4920 1013 -1970 1015 -4950 1009 -4926 1005 -7892 1037 -1942 1011 -7926 1005 -4918 1007 -1996 985 -7928 1011 -1964 993 -4946 1025 -7880 1003 -1972 1009 -13854 1011 -1948 1019 -4956 983 -4946 1025 -7898 1003 -1966 1025 -7900 1019 -1964 1011 -7890 1021 -4912 1017 -4930 1013 -1968 1015 -4952 1009 -4932 1007 -7920 1009 -1972 979 -7924 1009 -4944 1003 -1970 1015 -7894 1017 +RAW_Data: 131 -534 99 -798 2211 -134 297 -66 361 -66 197 -198 97 -398 199 -100 163 -100 65 -464 133 -134 233 -68 229 -696 199 -200 265 -330 65 -1218 163 -166 97 -426 229 -698 165 -100 99 -134 165 -372 65 -1426 65 -266 99 -794 65 -832 199 -464 165 -698 99 -266 165 -794 99 -298 133 -734 165 -266 99 -864 131 -232 1893 -166 1465 -1484 263 -560 99 -230 65 -462 197 -66 97 -890 165 -130 97 -132 163 -232 97 -132 161 -162 263 -262 99 -362 65 -432 65 -830 97 -234 99 -628 199 -66 99 -166 101 -268 265 -594 131 -1030 97 -1156 595 -800 167 -1920 65 -464 165 -264 131 -66 167 -134 233 -632 329 -100 97 -166 199 -466 199 -98 267 -200 97 -266 199 -300 65 -496 563 -100 101 -166 165 -730 233 -166 131 -590 97 -232 131 -164 359 -428 3159 -792 65 -624 197 -98 229 -132 393 -398 199 -200 133 -366 99 -132 67 -528 265 -730 197 -166 65 -200 397 -1358 131 -1712 363 -1462 299 -132 67 -1792 65 -794 99 -368 131 -134 165 -134 67 -266 265 -430 2315 -98 1473 -100 129 -66 229 -198 261 -166 97 -426 97 -100 229 -496 197 -162 163 -392 231 -166 229 -660 131 -366 297 -166 299 -232 97 -332 301 -866 133 -596 267 -364 99 -502 99 -1988 131 -134 65 -66 165 -366 231 -266 165 -690 99 -234 65 -498 67 -166 133 -762 2459 -166 1019 -264 99 -200 265 -562 65 -328 295 -330 197 -360 131 -98 227 -232 65 -426 259 -166 99 -496 131 -500 399 -992 67 -432 133 -66 99 -1394 231 -1524 133 -430 65 -886 65 -1716 131 -2818 63 -430 229 -296 97 -1706 131 -164 65 -1478 131 -458 99 -1568 165 -756 197 -890 99 -1088 359 -660 97 -98 229 -166 65 -500 99 -266 65 -566 65 -432 365 -200 99 -798 99 -296 327 -1218 165 -132 99 -466 165 -168 133 -632 363 -1482 163 -802 131 -862 133 -1332 65 -100 431 -830 97 -596 165 -3422 99 -896 65 -766 97 -3766 65 -132 65 -1152 163 -1810 99 -230 97 -654 229 -458 165 -200 67 -166 297 -134 99 -4840 133 -762 67 -764 231 -1248 97 -1780 97 -3332 65 -100 261 -1186 99 -3360 99 -364 97 -1738 163 -294 97 -4918 129 -1550 295 -1028 199 -1264 99 -1560 99 -2156 167 -1556 231 -1852 167 -998 67 -1116 65 -562 167 -334 133 -266 165 -394 99 -166 97 -3082 99 -232 301 -5148 131 -502 67 -732 197 -434 199 -200 99 -432 65 -232 199 -464 133 -1060 97 -1582 97 -98 97 -924 129 -462 229 -560 65 -132 97 -166 161 -164 97 -166 229 -360 97 -132 63 -198 131 -162 525 -1714 67 -732 99 -958 +RAW_Data: 65 -1166 165 -200 199 -166 133 -100 65 -1294 99 -166 265 -992 297 -728 65 -332 431 -166 261 -132 65 -132 65 -98 363 -230 163 -430 165 -230 129 -660 129 -66 131 -398 2453 -132 1329 -166 1187 -232 1657 -166 499 -68 99 -132 233 -894 99 -232 99 -134 363 -628 67 -998 131 -234 99 -266 197 -398 97 -200 167 -334 197 -430 429 -168 263 -1160 99 -364 131 -264 97 -1318 131 -790 99 -432 131 -132 199 -100 131 -930 133 -1424 99 -398 165 -1486 299 -166 165 -334 2325 -66 795 -102 397 -100 533 -100 429 -98 131 -464 65 -532 329 -366 165 -332 99 -432 295 -132 99 -662 67 -332 331 -1226 131 -102 199 -562 469 -430 97 -232 63 -328 65 -1914 133 -466 131 -832 163 -1660 331 -464 199 -828 131 -696 65 -132 2139 -66 2051 -66 263 -790 99 -566 429 -428 99 -330 297 -698 65 -200 197 -68 461 -1292 131 -168 165 -634 131 -234 133 -594 163 -360 165 -700 99 -964 133 -1026 65 -532 197 -1520 263 -688 65 -888 295 -658 65 -824 67 -598 97 -426 65 -130 161 -132 63 -658 99 -64 165 -522 199 -298 65 -428 197 -560 65 -200 165 -100 167 -594 229 -100 297 -268 65 -100 459 -200 97 -334 2997 -200 597 -498 97 -626 97 -134 363 -166 333 -266 65 -664 331 -300 299 -568 529 -662 263 -626 67 -594 99 -266 165 -230 361 -462 67 -66 131 -694 397 -1490 167 -132 165 -768 165 -130 165 -466 99 -298 99 -952 163 -428 97 -296 97 -360 99 -196 65 -658 65 -4052 229 -6240 165 -228 65 -426 99 -994 99 -398 363 -1512 161 -2042 65 -592 131 -2866 65 -500 167 -5246 97 -462 67 -1156 99 -2428 99 -960 65 -132 99 -164 65 -166 329 -2484 263 -232 67 -1368 99 -996 267 -498 67 -4612 131 -4866 65 -100 363 -464 231 -264 231 -262 297 -1594 99 -1596 65 -136 133 -1662 67 -1694 429 -1290 197 -198 293 -858 99 -1976 165 -66 97 -460 131 -460 97 -328 163 -1624 397 -300 67 -166 97 -430 299 -300 299 -1792 65 -4438 131 -622 67 -500 131 -1064 65 -400 65 -426 261 -1644 131 -3468 165 -1684 99 -866 101 -66 231 -464 65 -498 133 -492 97 -1712 99 -98 197 -66 265 -1586 199 -1334 99 -1948 97 -200 99 -266 65 -666 131 -132 99 -264 195 -3066 99 -3512 99 -1746 65 -598 97 -6068 167 -600 99 -1262 65 -166 65 -134 65 -132 131 -1520 99 -1254 97 -1828 197 -1294 129 -66 65 -1248 97 -754 165 -6542 231 -662 197 -560 359 -98 97 -130 99 -3080 63 -952 129 -296 65 -230 63 -428 99 -1788 133 -100 65 -462 199 -66 165 -1094 99 -66 +RAW_Data: 167 -3190 265 -1128 195 -298 525 -166 65 -594 597 -664 131 -498 165 -232 65 -296 129 -296 65 -494 297 -498 297 -596 201 -332 2027 -132 593 -68 891 -66 893 -98 365 -396 97 -398 99 -300 199 -366 163 -598 99 -100 131 -234 65 -132 331 -532 165 -166 231 -1660 133 -532 265 -100 131 -100 131 -298 199 -396 199 -334 197 -166 67 -334 131 -1662 165 -298 97 -534 65 -628 65 -534 65 -266 97 -826 133 -696 65 -294 233 -198 165 -134 2081 -66 929 -198 167 -300 99 -232 297 -234 199 -662 67 -166 99 -66 233 -132 433 -232 265 -300 297 -132 65 -132 97 -496 199 -360 465 -132 65 -132 297 -460 293 -64 161 -262 97 -492 199 -200 233 -132 97 -396 99 -858 199 -864 65 -66 99 -532 131 -1608 297 -1550 163 -432 229 -66 163 -394 1953 -134 1491 -100 991 -66 165 -232 265 -130 299 -432 167 -724 363 -134 167 -200 231 -66 199 -98 395 -66 165 -266 99 -332 197 -268 265 -466 265 -166 493 -362 65 -132 99 -360 199 -200 133 -758 99 -330 165 -298 163 -428 199 -368 165 -334 131 -500 163 -986 97 -394 329 -196 97 -558 195 -230 97 -532 2373 -232 165 -232 795 -132 267 -168 363 -166 297 -298 131 -134 65 -466 131 -464 65 -232 67 -266 229 -364 231 -232 297 -464 65 -198 133 -398 529 -134 65 -364 163 -732 233 -298 299 -134 231 -66 129 -264 397 -132 131 -200 329 -430 131 -132 265 -1292 65 -362 131 -264 97 -1020 165 -398 131 -364 65 -300 265 -924 195 -396 331 -166 167 -132 433 -198 99 -464 297 -266 165 -396 131 -1226 165 -694 65 -230 197 -896 65 -594 2969 -164 461 -298 133 -330 133 -234 65 -134 131 -66 65 -68 97 -66 99 -132 231 -694 363 -298 267 -268 231 -368 131 -300 99 -134 65 -166 461 -396 263 -66 165 -298 131 -66 229 -1644 163 -1610 229 -658 97 -132 129 -162 97 -428 65 -66 129 -1912 65 -1194 233 -2218 133 -360 65 -132 229 -3428 65 -262 65 -3208 133 -1224 99 -296 65 -2212 65 -496 65 -200 199 -498 65 -1144 65 -1764 165 -1726 65 -434 65 -898 165 -2354 67 -134 99 -464 99 -792 263 -954 1161 -100 295 -296 131 -132 457 -462 363 -200 725 -298 131 -134 231 -68 531 -166 667 -298 99 -66 595 -794 165 -202 199 -68 99 -298 165 -68 231 -202 201 -200 97 -1360 197 -266 165 -334 99 -234 165 -200 165 -100 131 -664 65 -566 397 -100 165 -132 199 -132 165 -988 197 -394 99 -164 65 -294 99 -262 63 -1288 233 -132 199 -168 265 -64 99 -728 133 -1192 65 -334 165 -132 199 -200 +RAW_Data: 131 -400 65 -398 165 -866 131 -264 393 -1680 231 -1494 299 -894 265 -364 65 -132 131 -960 99 -296 165 -824 65 -1716 99 -664 97 -134 165 -1526 165 -634 131 -166 135 -466 231 -696 2331 -100 329 -100 961 -198 65 -164 199 -98 331 -132 65 -298 731 -66 163 -332 265 -894 65 -132 265 -300 97 -334 229 -100 65 -1026 233 -366 329 -922 99 -266 329 -428 97 -462 361 -758 163 -1886 65 -498 229 -558 65 -600 267 -68 65 -1592 133 -202 2299 -332 393 -132 329 -326 231 -232 97 -590 231 -136 201 -1226 265 -530 199 -134 65 -232 297 -398 65 -564 65 -1458 65 -500 99 -302 99 -1120 65 -66 131 -98 197 -66 133 -66 65 -730 99 -200 197 -730 165 -664 297 -1462 165 -664 133 -168 65 -168 133 -368 197 -466 1957 -66 1525 -266 131 -366 65 -394 65 -98 163 -296 99 -130 265 -696 65 -398 131 -1128 197 -134 67 -894 97 -296 231 -166 401 -98 131 -928 265 -398 131 -798 131 -298 165 -830 65 -230 231 -168 97 -1252 131 -590 65 -460 229 -692 97 -332 65 -366 265 -732 2297 -66 197 -66 131 -166 231 -100 99 -298 365 -168 361 -130 129 -66 161 -198 97 -626 593 -66 99 -98 99 -556 297 -132 65 -134 65 -526 295 -196 197 -68 163 -264 327 -130 855 -66 393 -132 131 -100 65 -98 227 -164 197 -758 131 -268 99 -430 65 -1686 131 -1652 197 -788 63 -726 165 -100 131 -264 197 -1026 197 -1250 131 -1650 65 -1980 97 -132 63 -1424 97 -198 99 -100 65 -1090 65 -398 99 -1686 99 -754 63 -1714 97 -1842 65 -2988 65 -4980 165 -1624 65 -232 67 -1464 65 -3048 65 -166 97 -1986 65 -1062 99 -862 131 -2732 99 -2360 97 -1620 65 -1326 65 -330 197 -3238 67 -266 131 -2968 67 -132 135 -1688 131 -1718 97 -1648 65 -166 65 -1292 65 -1908 99 -424 131 -1222 197 -798 99 -700 97 -1758 165 -1562 99 -396 165 -166 99 -98 65 -366 65 -762 131 -332 233 -498 133 -98 163 -562 65 -68 131 -858 65 -896 65 -366 233 -532 197 -168 65 -68 65 -630 195 -724 231 -296 361 -164 261 -2862 233 -232 99 -3186 67 -232 163 -496 165 -1620 97 -1622 131 -7022 267 -268 165 -466 63 -528 295 -200 197 -332 65 -1656 131 -5478 65 -1296 99 -130 161 -694 67 -1694 131 -734 99 -2590 99 -1056 65 -432 327 -66 165 -164 65 -1120 229 -1656 165 -1128 131 -1232 99 -1560 65 -568 131 -360 231 -1598 99 -3250 67 -1818 65 -1216 263 -3328 65 -1508 133 -2600 65 -528 65 -2418 65 -460 97 -100 97 -164 65 -1560 133 -1822 163 -296 165 -1090 165 -168 133 -1264 +RAW_Data: 229 -1458 165 -66 133 -1626 197 -132 65 -394 65 -724 229 -132 65 -200 263 -296 97 -264 131 -130 229 -132 161 -1230 431 -132 99 -234 233 -362 99 -698 67 -334 65 -366 299 -398 99 -328 131 -396 165 -1458 99 -926 2239 -66 763 -68 1727 -266 65 -364 65 -464 365 -366 97 -664 165 -200 265 -234 261 -264 395 -362 329 -690 261 -164 495 -232 131 -326 165 -264 99 -198 397 -928 299 -268 299 -166 99 -298 167 -436 65 -266 67 -498 231 -896 367 -428 97 -332 133 -628 165 -1394 231 -926 131 -232 3127 -132 921 -428 263 -66 97 -230 97 -1022 329 -430 97 -166 65 -198 63 -100 65 -66 97 -66 361 -132 67 -232 131 -268 99 -366 99 -98 331 -364 99 -428 97 -66 491 -164 99 -164 65 -428 131 -260 429 -1380 263 -1564 65 -300 65 -498 99 -364 167 -200 231 -264 67 -264 365 -266 231 -262 65 -724 65 -130 2213 -130 361 -166 2065 -166 165 -100 133 -264 299 -332 99 -98 229 -198 65 -328 131 -100 263 -628 131 -330 231 -98 65 -132 129 -264 65 -130 163 -692 131 -66 99 -532 133 -234 99 -232 367 -298 401 -702 165 -498 399 -166 65 -132 131 -400 165 -1232 165 -1296 65 -232 165 -300 97 -396 2033 -198 463 -230 67 -68 563 -262 295 -330 197 -1190 197 -66 167 -66 263 -430 263 -100 163 -198 167 -102 165 -100 99 -234 67 -66 267 -396 197 -296 131 -1190 331 -200 67 -234 231 -530 263 -1610 263 -264 329 -196 129 -98 131 -132 227 -132 425 -526 129 -694 299 -856 63 -132 165 -166 165 -436 297 -1430 65 -66 99 -198 167 -266 65 -864 267 -996 65 -500 265 -1456 297 -726 163 -66 579 -1508 523 -1504 509 -1006 1033 -486 1513 -1514 507 -510 1537 -982 1011 -528 1509 -512 1495 -1034 999 -1016 999 -516 1539 -1014 989 -1510 527 -486 1537 -1508 517 -1512 485 -1546 483 -524 1519 -484 1529 -60168 1539 -1506 507 -1006 1015 -1016 1007 -1512 509 -1006 1033 -506 1495 -1544 497 -1014 1007 -1008 1001 -1544 477 -1024 1011 -1538 485 -1512 525 -1010 1005 -482 1529 -1018 1005 -1020 1009 -1532 475 -540 1507 -512 1491 -59188 513 -1506 515 -1512 519 -1020 985 -506 1519 -1538 491 -504 1533 -998 1013 -516 1505 -522 1505 -1018 1005 -1002 1035 -494 1525 -1014 995 -1546 483 -524 1513 -1504 515 -1502 509 -1512 509 -514 1527 -514 1501 -60186 1539 -1506 509 -1008 1003 -1016 1009 -1520 515 -1014 989 -518 1509 -1544 485 -1016 999 -1016 1003 -1544 483 -1016 1017 -1506 515 -1534 475 -1036 987 -520 1507 -1012 1035 -1006 1017 -1502 503 -526 1503 -508 1533 -59160 523 -1508 507 -1502 537 -984 1013 -528 1505 -1504 503 -520 1537 -994 +RAW_Data: -1968 997 -4952 1011 -7896 1013 -1954 1005 -13860 985 -2000 979 -4952 1005 -4946 993 -7930 979 -1990 1009 -7908 1017 -1968 1011 -7900 985 -4946 1027 -4920 1009 -1964 1015 -4942 1009 -263646 67 -9708 99 -68890 97 -48712 65 -62028 65 -4652 65 -108870 99 -71758 99 -3200 97 -47548 65 -7036 65 -104448 97 -38184 99 -6502 97 -17756 65 -10136 20983 -1968 999 -4950 1005 -4940 1001 -7898 1025 -1966 1011 -7876 1017 -1966 1023 -7918 981 -4936 1027 -4946 975 -2006 979 -4946 1017 -4954 977 -7930 983 -1980 1003 -7920 989 -4952 1007 -1970 1013 -7896 1007 -1970 1015 -4954 987 -7900 1007 -1970 1007 -13860 993 -1972 1007 -4956 1013 -4926 1005 -7908 995 -1972 1029 -7914 987 -1986 1009 -7906 981 -4956 1009 -4930 1005 -1966 1039 -4938 1007 -4916 1009 -7918 1019 -1968 989 -7892 1017 -4934 1027 -1956 1039 -7898 985 -1974 1007 -4960 981 -7930 985 -1996 989 -13824 1045 -1946 1025 -4928 1009 -4942 997 -7926 1011 -1968 1003 -7894 1023 -1962 993 -7902 1017 -4948 999 -4948 1007 -1968 1015 -4918 1045 -4926 999 -7888 1039 -1972 1013 -7888 1005 -4948 1001 -1970 1011 -7886 1013 -1974 1023 -4942 1005 -7890 1033 -1952 1021 -13806 1047 -1964 1001 -4936 1009 -4950 1001 -7882 1031 -1962 1007 -7906 1021 -1966 1011 -7908 985 -4946 1027 -4918 1011 -1968 1017 -4948 1009 -4932 1015 -7882 1009 -1964 1013 -7900 1017 -4950 1001 -1970 1013 -7916 1013 -1944 1031 -4916 1029 -7888 1007 -1976 1005 -13848 1013 -1968 995 -4952 1003 -4946 991 -7920 1019 -1968 983 -7904 1015 -1966 1019 -7894 1011 -4942 1027 -4924 1011 -1966 1015 -4944 1005 -4926 1007 -7914 1007 -1972 1013 -7884 1013 -4940 1027 -1968 1007 -7906 1001 -1980 999 -4944 1013 -7880 1009 -2000 1003 -13830 997 -1974 1009 -4930 1029 -4938 1013 -7880 1011 -1998 1001 -7908 997 -1966 1025 -7902 1013 -4938 983 -4962 979 -1998 1011 -4932 1005 -4924 1011 -7922 1009 -1970 1015 -7890 1011 -4946 1003 -1970 1013 -7892 1013 -1964 1027 -4928 1021 -7886 1007 -1980 1001 -13846 1013 -1968 999 -4948 1009 -4938 993 -7926 1011 -1964 991 -7926 1011 -1962 993 -7898 1041 -4906 1009 -4960 999 -1964 1023 -4932 1007 -4928 1013 -7898 1015 -1968 1013 -7894 1017 -4948 997 -1968 1031 -7892 1027 -1970 1005 -4914 1039 -7884 1023 -1964 1011 -13838 1013 -1948 1023 -4924 1015 -4942 1025 -7872 1029 -1964 1007 -7898 1025 -1960 1025 -7898 1021 -4912 1023 -4920 1011 -1970 1013 -4954 1011 -4926 1003 -7910 1011 -1968 1013 -7890 1009 -4938 1023 -1982 1007 -7902 1013 -1940 1023 -4926 1011 -7922 1007 -1980 997 -13816 1045 -1964 1001 -4948 1005 -4938 993 -7904 1005 -1966 1035 -7888 1027 -1964 1011 -7906 1015 -4916 1027 -4920 1011 -1968 1019 -4944 1009 -4928 1001 -7924 1009 -1972 979 -7914 1013 -4948 1001 -1970 1013 -7892 1015 -1966 1027 -4928 1021 -7872 1035 -1968 1011 -13832 1011 -1980 983 -4956 987 -4948 1025 -7892 1005 -1966 1007 -7914 1021 -1962 1007 +RAW_Data: -7912 985 -4942 1023 -4926 1007 -1992 981 -4944 1035 -4920 1007 -7904 1025 -1962 991 -7934 981 -4948 995 -2004 1007 -7880 1007 -1992 983 -4946 1027 -7890 1007 -1982 999 -13830 1011 -1968 1025 -4926 1013 -4942 997 -7898 1027 -1960 1023 -7910 983 -2000 979 -7906 1015 -4940 1025 -4920 1011 -1968 1013 -225644 65 -2082 65 -155560 133 -5172 65 -1102 131 -48576 99 -24714 67 -6858 65 -1314 67 -38246 65 -64888 65 -4564 67 -59374 99 -20160 99 -17606 65 -42096 97 -11950 131 -29302 65 -19034 99 -32020 97 -366 65 -4430 131 -14620 99 -17318 65 -5556 +RAW_Data: 297 -1592 167 -1594 231 -366 65 -598 65 -600 197 -98 199 -1098 99 -98 65 -862 133 -628 165 -597 361 -6828 65 -1668 231 -632 65 -792 163 -1284 163 -5554 199 -1588 165 -5300 97 -992 65 -432 197 -2566 99 -2340 63 -5932 65 -1462 65 -1062 131 -2454 65 -1446 133 -1682 65 -1260 65 -368 131 -1482 65 -134 131 -1512 197 -1710 131 -824 99 -66 133 -464 131 -866 65 -698 65 -200 65 -894 99 -1692 233 -1130 97 -2160 265 -1392 99 -132 131 -166 65 -1318 327 -1548 163 -6980 165 -994 65 -698 131 -1580 129 -132 63 -758 97 -466 99 -1590 395 -1024 97 -600 99 -732 63 -228 129 -98 165 -292 99 -696 231 -232 197 -166 133 -132 131 -430 165 -664 199 -1596 197 -530 65 -1054 63 -3474 165 -4504 65 -2980 99 -268 133 -2356 131 -798 99 -132 99 -796 97 -1692 97 -3804 199 -166 67 -66 97 -132 99 -5058 129 -1512 163 -1290 65 -298 199 -398 65 -1034 65 -1332 167 -3448 65 -1750 65 -1186 131 -462 65 -920 65 -166 97 -362 131 -1704 131 -1748 65 -2124 65 -1064 133 -1694 197 -5148 99 -566 131 -1696 99 -598 65 -698 231 -692 267 -2490 99 -1618 165 -1760 197 -2152 165 -1226 195 -100 99 -66 131 -100 99 -400 65 -456 131 -198 165 -368 261 -826 365 -858 99 -132 163 -460 229 -264 65 -664 165 -234 231 -98 165 -100 99 -1986 99 -2526 65 -832 167 -1762 231 -728 67 -530 133 -2082 97 -960 67 -498 199 -1658 133 -1488 99 -2652 99 -1258 165 -5284 99 -1660 65 -1790 65 -432 65 -3804 197 -2170 131 -196 129 -66 97 -832 99 -2466 97 -1622 99 -1624 163 -394 163 -1018 133 -1856 99 -430 67 -826 197 -3156 65 -166 97 -558 65 -1690 131 -1498 99 -66 65 -2716 161 -658 99 -1054 65 -230 65 -2074 97 -2042 65 -3074 263 -1698 165 -492 65 -400 131 -196 131 -368 165 -134 203 -168 201 -134 165 -398 301 -628 99 -1032 99 -830 65 -66 65 -432 229 -230 161 -394 197 -228 197 -330 327 -66 197 -822 231 -392 131 -892 393 -1456 131 -64 163 -1418 65 -166 165 -1428 133 -1526 163 -954 65 -888 99 -1956 133 -1194 131 -3514 65 -1728 97 -662 67 -1590 167 -2714 65 -930 65 -166 65 -728 131 -2752 65 -434 133 -1828 99 -3704 129 -1912 133 -1544 227 -494 133 -202 97 -466 65 -200 131 -1580 263 -1708 65 -626 65 -626 199 -166 133 -3446 99 -3420 99 -398 131 -1164 131 -7018 131 -3468 63 -1658 199 -956 101 -598 99 -1790 97 -1192 65 -362 133 -630 65 -100 429 -794 65 -696 231 -1130 65 -268 395 -730 131 -466 431 -66 233 -168 197 -998 97 -264 65 -796 163 -236 +RAW_Data: 265 -100 133 -1464 99 -464 67 -592 229 -362 131 -262 163 -134 325 -164 163 -130 97 -164 65 -724 97 -132 97 -958 131 -594 131 -264 161 -98 229 -100 97 -890 131 -364 99 -198 97 -262 131 -234 2497 -134 65 -68 165 -400 563 -400 433 -496 99 -100 131 -266 99 -100 65 -200 231 -894 65 -166 463 -200 201 -1228 131 -266 65 -496 99 -300 97 -202 165 -896 365 -132 163 -1698 165 -132 99 -168 67 -166 67 -796 165 -400 131 -200 165 -796 165 -98 2161 -66 65 -166 293 -228 887 -66 395 -656 297 -100 97 -530 97 -66 97 -594 65 -428 97 -332 65 -400 131 -164 261 -66 299 -334 65 -596 97 -166 99 -1184 161 -460 131 -132 229 -398 299 -664 165 -722 165 -166 263 -300 99 -232 199 -266 267 -660 97 -758 133 -530 65 -632 99 -264 329 -1028 67 -166 2451 -562 427 -464 163 -198 229 -264 229 -790 295 -164 297 -132 263 -726 267 -66 99 -364 197 -564 99 -1162 67 -960 97 -198 163 -630 133 -66 163 -132 195 -1288 197 -430 197 -1546 65 -890 97 -394 231 -924 2173 -100 65 -594 165 -100 99 -528 99 -166 99 -298 497 -66 65 -100 131 -198 265 -362 131 -198 293 -294 129 -1150 263 -262 65 -100 131 -494 231 -166 231 -468 99 -66 165 -1262 197 -298 65 -298 397 -432 297 -498 361 -200 65 -466 131 -166 101 -496 231 -364 233 -64 197 -196 65 -426 265 -198 99 -1218 97 -200 67 -232 131 -200 167 -200 99 -66 165 -928 67 -1722 99 -764 65 -892 99 -232 133 -300 133 -1080 621 -1526 487 -1504 507 -506 1517 -1524 519 -1508 509 -1512 447 -546 1503 -1038 1035 -1482 547 -976 1007 -1208 823 -490 1503 -532 1481 -1048 989 -1534 499 -524 1507 -514 1499 -1014 1007 -524 1503 -1530 473 -60198 1533 -1516 481 -1040 985 -1044 981 -1554 479 -1530 479 -1044 1007 -1502 511 -1538 483 -516 1503 -1542 473 -1016 1033 -1510 483 -1542 505 -1002 1003 -508 1521 -1010 1015 -1512 507 -526 1515 -518 1505 -1016 1007 -59156 529 -1528 463 -1580 447 -542 1507 -1544 469 -1554 481 -1512 499 -518 1507 -1016 1007 -1512 511 -1012 1021 -1010 1007 -518 1529 -472 1555 -982 1023 -1502 505 -508 1537 -488 1545 -1012 1003 -480 1531 -1516 507 -60180 493 -12296 821 -1698 299 -1686 387 -590 1465 -1570 459 -1046 979 -1546 475 -1534 489 -1044 1009 -482 1529 -1032 973 -1538 513 -480 1537 -492 1523 -1012 1017 -59154 497 -11798 301 -704 1325 -1148 913 -1612 417 -1102 897 -1118 945 -586 1437 -568 1465 -1058 977 -1534 477 -550 1495 -502 1515 -1006 1037 -494 1527 -1510 489 -60174 531 -16794 355 -640 1399 -1608 449 -1050 989 -1540 461 -1546 477 -1068 973 -518 +RAW_Data: 1503 -1024 1009 -1536 473 -552 1503 -498 1517 -1010 1017 -59162 513 -9838 227 -1670 371 -604 1425 -1116 949 -1554 477 -1030 981 -1042 973 -550 1477 -532 1511 -1010 1003 -1544 511 -496 1515 -516 1509 -1018 1007 -488 1539 -1504 503 -60216 1525 -1530 485 -1018 999 -1012 1019 -1538 475 -1546 477 -1012 1025 -1506 517 -1530 505 -510 1507 -1512 513 -1010 999 -1516 507 -1520 513 -1012 1015 -506 1527 -1014 991 -1542 483 -524 1517 -482 1529 -1008 1005 -137640 165 -1660 165 -1362 231 -3114 67 -2286 131 -1652 197 -1448 195 -1648 97 -3534 163 -988 65 -556 65 -132 133 -2594 199 -2054 65 -1918 65 -1458 163 -690 65 -1134 131 -1194 67 -1158 131 -1656 199 -198 99 -298 199 -2676 67 -2122 231 -1762 263 -1560 131 -1228 99 -198 197 -830 99 -166 97 -6274 99 -1058 163 -1676 97 -1788 65 -5742 99 -3890 131 -2682 195 -166 65 -1394 265 -432 99 -368 99 -100 67 -198 163 -564 133 -992 131 -266 133 -962 233 -200 131 -196 231 -560 131 -66 65 -764 131 -1058 133 -564 99 -1792 133 -2820 65 -3480 165 -9536 99 -66 97 -626 97 -1828 199 -1860 99 -66 197 -1060 99 -1746 65 -1690 99 -198 99 -1660 165 -1764 65 -1558 199 -1456 99 -164 65 -1318 395 -5966 131 -928 165 -1026 99 -534 65 -434 99 -100 97 -100 97 -3272 65 -2168 131 -2904 231 -1564 67 -958 263 -3780 65 -166 99 -3506 99 -1594 165 -1492 197 -164 63 -1382 197 -1162 65 -364 133 -822 65 -498 495 -230 165 -366 65 -464 231 -666 231 -198 65 -332 163 -566 65 -164 67 -332 231 -232 265 -198 97 -1254 361 -200 65 -2056 129 -430 131 -100 65 -1588 263 -198 97 -2992 97 -528 297 -134 97 -368 427 -66 67 -132 65 -132 367 -330 65 -266 229 -66 65 -66 65 -198 231 -66 133 -528 363 -162 161 -230 525 -230 65 -230 461 -132 327 -330 295 -130 197 -230 165 -270 265 -464 65 -698 265 -264 229 -400 99 -3476 97 -1490 199 -134 99 -2970 165 -1618 167 -1598 65 -1162 99 -234 167 -1162 135 -464 65 -664 165 -1126 197 -362 97 -1222 65 -3496 65 -7024 131 -1592 131 -530 97 -6464 231 -998 263 -428 65 -698 131 -198 131 -1414 63 -1346 165 -1060 65 -1664 167 -566 263 -666 165 -566 331 -100 131 -1522 167 -368 199 -896 229 -232 99 -968 99 -168 231 -66 197 -68 131 -168 133 -400 131 -268 233 -596 233 -132 97 -494 131 -1710 197 -1616 67 -592 99 -1226 267 -268 65 -266 133 -1986 165 -332 99 -366 131 -2700 97 -1398 65 -130 269 -198 65 -1664 131 -3180 165 -992 65 -866 99 -166 163 -2782 65 -2354 265 -5176 65 -66 163 -1290 67 -164 99 -2584 67 -3084 +RAW_Data: 195 -364 65 -164 229 -958 63 -166 193 -130 65 -556 99 -332 199 -430 197 -996 297 -1426 235 -1160 2053 -166 1063 -100 501 -132 535 -198 67 -66 165 -98 165 -460 365 -366 97 -432 329 -264 133 -100 165 -328 197 -360 1087 -264 97 -166 63 -166 233 -98 195 -294 97 -264 163 -266 197 -100 359 -66 65 -1958 165 -694 99 -166 99 -596 299 -466 97 -66 99 -696 231 -1492 297 -1554 165 -1680 165 -368 99 -166 99 -168 65 -794 197 -194 2581 -98 1151 -592 99 -426 197 -328 295 -164 65 -232 163 -530 165 -264 129 -98 229 -294 493 -426 99 -66 163 -164 261 -264 129 -166 229 -68 65 -558 131 -132 65 -132 197 -1550 361 -98 67 -132 99 -132 131 -786 99 -198 301 -366 165 -530 99 -234 2159 -264 1691 -166 367 -132 231 -100 197 -166 97 -366 163 -68 131 -366 165 -268 133 -430 233 -100 133 -132 199 -932 561 -1416 231 -794 199 -1296 165 -564 165 -666 99 -490 97 -760 163 -1582 295 -464 267 -330 2561 -134 931 -66 65 -132 99 -264 63 -132 99 -198 97 -364 129 -460 65 -230 263 -164 163 -100 365 -100 131 -398 97 -530 65 -266 299 -1028 133 -100 97 -166 65 -296 65 -166 133 -498 331 -962 99 -98 199 -1328 165 -200 65 -234 99 -400 231 -632 65 -232 199 -530 65 -866 429 -958 197 -368 165 -668 65 -984 163 -100 129 -1088 259 -752 97 -522 131 -892 65 -298 67 -364 199 -132 65 -788 97 -396 363 -1052 99 -228 131 -98 99 -1526 463 -330 131 -898 263 -332 97 -996 163 -494 99 -2950 65 -798 131 -2490 165 -1424 65 -1694 65 -1822 65 -1756 67 -3820 65 -2536 97 -2410 65 -1030 131 -404 263 -732 165 -1566 197 -1554 199 -400 65 -100 99 -566 165 -3584 65 -764 101 -2630 65 -2896 163 -364 67 -100 65 -1228 263 -232 63 -884 65 -4092 133 -1622 325 -166 99 -2352 65 -500 65 -1324 99 -366 65 -1592 297 -5134 131 -1130 65 -1962 99 -66 99 -1454 67 -1130 99 -134 199 -134 165 -1222 229 -166 131 -464 197 -196 263 -234 99 -534 65 -132 131 -166 133 -134 199 -1590 231 -66 131 -1160 131 -300 65 -698 199 -462 133 -3446 99 -2876 65 -596 65 -1716 133 -2886 97 -134 199 -1628 131 -1790 67 -2556 615 -1510 453 -1584 443 -1566 485 -1014 977 -1054 1003 -516 1501 -1010 1007 -520 1495 -1034 1005 -1530 481 -514 1541 -492 1523 -1012 1001 -1508 521 -1016 999 -512 1527 -1506 509 -488 1521 -1040 989 -1538 473 -60194 1519 -1540 511 -1010 1001 -1020 1009 -1516 515 -1010 1001 -518 1507 -524 1503 -1534 493 -1510 501 -1022 1011 -516 1507 -522 1507 -1512 509 -1506 519 -516 +RAW_Data: -130 1115 -68 55471 -1962 167 -1664 99 -364 427 -232 759 -132 199 -100 267 -134 527 -132 99 -1292 99 -266 65 -68 131 -762 327 -98 393 -298 37611 -730 131 -428 1487 -3910 327 -100 295 -98 491 -492 65 -98 197 -860 97 -98 131 -856 131 -134 231 -792 229 -66 919 -198 7527 -7444 131 -722 97 -230 65 -98 527 -100 267 -68 229 -262 361 -528 195 -624 131 -164 495 -66 1697 -66 1791 -132 3715 -8320 65 -394 165 -166 461 -1094 297 -532 131 -764 197 -98 261 -230 165 -100 6235 -12144 265 -600 131 -134 65 -298 233 -958 163 -196 97 -756 263 -98 465 -134 97 -164 3051 -16552 431 -68 131 -166 131 -264 331 -298 561 -166 689 -66 557 -264 3173 -8316 65 -166 531 -298 197 -234 233 -134 233 -892 163 -296 65 -890 161 -232 331 -100 397 -15072 199 -100 99 -394 165 -164 129 -132 197 -132 199 -798 99 -1032 67 -398 427 -100 391 -98 363 -166 297 -66 2869 -66 2299 -12300 97 -660 99 -662 263 -594 131 -662 133 -66 199 -332 97 -134 99 -132 65 -134 131 -100 431 -100 2143 -66 429 -8840 97 -6976 97 -66 397 -66 65 -166 131 -230 131 -294 99 -228 97 -230 129 -756 133 -1298 1293 -132 529 -100 2567 -15654 229 -328 97 -198 131 -66 195 -1492 131 -398 99 -134 99 -366 265 -168 99 -100 235 -100 131 -396 299 -200 1029 -66 6461 -6804 99 -1062 65 -960 65 -232 131 -98 195 -1708 63 -196 261 -164 331 -66 261 -12094 65 -298 265 -198 163 -592 131 -2402 63 -66 297 -198 97 -66 393 -264 4003 -15878 231 -98 261 -496 229 -264 65 -858 131 -994 133 -66 331 -66 429 -100 165 -132 297 -15004 99 -1466 97 -266 165 -198 463 -796 231 -66 131 -298 99 -100 133 -134 167 -430 99 -66 365 -100 297 -134 265 -132 563 -98 1217 -66 6399 -8742 99 -592 99 -426 397 -2338 199 -66 995 -134 229 -132 65 -164 3989 -66 3675 -6962 165 -466 65 -564 399 -66 199 -134 263 -396 97 -132 97 -100 97 -428 393 -624 131 -988 229 -66 363 -230 791 -164 7883 -8286 165 -134 99 -66 197 -100 99 -68 131 -164 163 -398 197 -2162 2005 -66 97 -100 4365 -98 1255 -12012 99 -132 165 -462 65 -166 97 -564 65 -100 331 -794 199 -364 261 -496 331 -132 823 -66 6233 -10976 165 -764 165 -200 195 -296 97 -19176 195 -230 129 -658 131 -132 293 -66 133 -860 65 -858 131 -64 229 -66 227 -66 161 -66 9051 -7316 65 -1494 131 -98 165 -198 65 -134 365 -398 297 -100 3969 -14874 99 -998 65 -564 67 -364 263 -132 163 -528 197 -132 65 -264 65 -264 431 -100 301 -66 297 +RAW_Data: -164 6323 -10770 65 -1420 227 -196 263 -198 197 -1086 99 -98 163 -164 163 -426 65 -362 97 -264 295 -132 197 -13586 65 -1808 131 -166 301 -66 465 -432 165 -330 65 -332 297 -962 99 -266 97 -166 265 -132 327 -198 329 -98 293 -14984 99 -862 131 -166 331 -68 165 -98 233 -132 201 -300 197 -364 133 -662 99 -398 99 -166 65 -432 133 -132 1447 -5882 197 -1082 65 -198 163 -1580 129 -264 67 -632 625 -134 165 -68 827 -100 165 -100 99 -164 3949 -9126 67 -164 131 -986 241 -534 309 -208 267 -226 247 -250 247 -248 247 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 247 -250 249 -504 257 -248 245 -246 247 -248 501 -258 247 -246 245 -248 247 -248 249 -248 249 -504 513 -226 243 -274 243 -500 513 -238 253 -256 253 -492 231 -262 485 -514 223 -244 527 -478 507 -274 255 -218 253 -498 261 -224 251 -250 503 -258 247 -246 243 -246 247 -504 515 -238 251 -472 277 -242 497 -246 289 -220 247 -498 513 -476 255 -68610 311 -208 235 -256 249 -248 247 -248 249 -248 249 -248 249 -250 249 -248 249 -250 247 -250 249 -248 249 -250 249 -248 249 -248 249 -250 249 -504 257 -248 245 -246 247 -248 503 -514 479 -274 253 -256 217 -254 247 -236 255 -250 247 -502 513 -240 251 -256 253 -254 249 -240 227 -252 249 -502 259 -246 245 -246 501 -514 237 -254 473 -280 253 -250 239 -230 251 -250 247 -504 257 -246 501 -226 245 -276 245 -246 245 -248 247 -504 515 -478 255 -244 495 -274 253 -476 511 -240 241 -69142 321 -218 243 -230 251 -248 247 -248 247 -250 247 -250 247 -250 249 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -506 257 -248 243 -246 247 -248 501 -258 247 -246 245 -246 247 -248 249 -248 249 -504 513 -238 251 -254 255 -490 499 -228 253 -250 249 -506 259 -248 499 -478 257 -244 495 -510 509 -238 255 -254 253 -494 227 -254 249 -248 505 -258 247 -246 245 -248 247 -504 513 -238 253 -472 279 -254 491 -226 253 -250 249 -504 513 -478 257 -68880 315 -216 241 -258 249 -248 247 -248 247 -248 249 -248 249 -250 249 -250 247 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -250 249 -504 257 -246 245 -246 247 -248 503 -516 477 -274 253 -256 217 -254 247 -236 255 -250 249 -502 481 -272 253 -254 255 -254 249 -238 227 -250 249 -504 257 -248 243 -246 501 -516 237 -254 473 -280 253 -250 239 -230 251 -250 247 -504 257 -248 499 -228 245 -274 245 -246 245 -248 247 +RAW_Data: -504 513 -478 257 -244 495 -274 253 -500 483 -232 265 -69136 287 -256 249 -238 227 -250 249 -248 249 -248 249 -248 249 -250 247 -250 247 -250 249 -250 247 -250 249 -250 249 -248 249 -248 249 -250 249 -504 257 -248 245 -246 245 -248 503 -260 245 -246 245 -246 247 -248 249 -250 247 -504 513 -238 251 -256 253 -496 499 -228 249 -250 249 -504 257 -248 499 -478 257 -244 495 -510 509 -238 255 -254 255 -492 227 -256 249 -248 505 -258 247 -246 245 -248 245 -504 513 -238 253 -472 279 -254 491 -228 253 -250 249 -504 513 -478 257 -68886 309 -208 235 -256 249 -248 249 -248 249 -248 249 -250 247 -250 249 -250 247 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -250 249 -504 257 -248 245 -246 245 -248 503 -514 477 -274 253 -254 217 -256 247 -238 255 -250 247 -504 513 -238 251 -256 253 -256 249 -240 227 -252 249 -504 257 -248 243 -246 501 -514 237 -252 473 -280 253 -250 241 -228 253 -248 249 -504 257 -246 499 -226 245 -276 245 -246 245 -248 247 -506 511 -478 257 -244 495 -272 255 -474 511 -240 241 -69150 323 -220 251 -242 229 -252 247 -248 247 -248 249 -248 249 -248 249 -250 249 -248 249 -250 249 -248 249 -250 247 -250 249 -248 249 -250 249 -504 257 -248 243 -248 247 -248 503 -258 245 -246 245 -248 245 -250 247 -250 247 -504 515 -236 253 -254 253 -496 499 -228 251 -250 247 -504 257 -248 499 -478 257 -244 495 -510 509 -240 253 -256 253 -492 229 -254 249 -250 505 -258 247 -246 245 -246 247 -504 513 -238 253 -472 247 -272 501 -246 253 -254 243 -494 513 -476 255 -68934 287 -254 213 -240 257 -250 249 -248 247 -250 247 -250 247 -250 249 -250 247 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -250 247 -506 257 -248 245 -246 245 -248 503 -516 479 -272 255 -254 217 -256 245 -236 255 -250 247 -504 513 -238 253 -254 253 -256 249 -238 229 -252 249 -504 257 -246 245 -246 501 -514 237 -254 473 -280 253 -250 239 -230 251 -250 247 -504 257 -248 499 -226 245 -276 245 -246 245 -248 247 -504 511 -478 255 -244 495 -256 243 -496 509 -274 253 -69130 309 -218 253 -256 249 -240 227 -252 249 -250 247 -248 249 -248 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 247 -250 249 -250 249 -504 259 -246 245 -246 247 -248 501 -258 247 -244 245 -248 247 -248 247 -250 249 -504 511 -226 245 -276 243 -500 477 -272 253 -254 255 -494 231 -262 481 -514 225 -246 527 -478 507 -256 241 -244 243 -494 257 -244 243 +RAW_Data: -246 527 -228 243 -276 245 -246 245 -500 513 -238 253 -474 279 -254 489 -228 251 -250 249 -506 513 -478 255 -68936 287 -218 249 -236 255 -248 247 -248 247 -248 249 -248 249 -248 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -250 247 -250 249 -506 257 -246 245 -246 245 -248 503 -514 477 -272 255 -254 255 -218 247 -240 255 -250 247 -502 511 -226 273 -244 243 -244 247 -246 247 -250 247 -506 259 -246 245 -246 501 -512 225 -244 527 -238 251 -256 253 -254 251 -242 227 -508 257 -216 527 -226 275 -244 243 -246 245 -248 247 -504 513 -478 257 -242 527 -238 255 -472 511 -242 281 -119614 299 -200 65 -470 65 -466 297 -630 97 -1592 133 -166 299 -66 231 -100 131 -98 265 -134 165 -166 433 -100 2287 -9916 231 -166 65 -98 199 -166 133 -166 165 -756 65 -724 195 -428 231 -260 263 -98 229 -130 261 -66 163 -264 65 -132 1181 -66 6315 -6798 131 -296 559 -334 131 -166 233 -132 165 -66 133 -264 99 -66 65 -366 99 -630 301 -166 97 -100 167 -164 535 -202 7269 -8266 197 -1022 131 -756 99 -98 99 -164 163 -990 65 -530 163 -230 297 -136 635 -66 2113 -8426 67 -6674 97 -722 197 -362 263 -232 165 -134 99 -234 297 -362 129 -198 131 -556 297 -68 167 -98 331 -200 165 -66 295 -66 8689 -4994 65 -1750 165 -762 163 -864 135 -100 167 -694 1093 -66 695 -102 99 -100 9899 -1650 297 -1216 97 -66 99 -396 65 -198 165 -164 233 -1658 199 -98 465 -134 463 -166 1883 -98 6283 -7302 99 -932 133 -696 263 -298 97 -98 165 -1708 131 -820 229 -98 231 -130 163 -590 131 -130 99 -66 97 -16220 261 -1062 265 -998 197 -1290 97 -362 165 -494 895 -264 7839 -7804 99 -66 99 -364 231 -630 133 -166 427 -496 131 -1252 263 -100 233 -66 133 -132 165 -66 259 -98 3109 -10438 101 -5322 99 -100 65 -666 65 -166 331 -98 197 -132 233 -662 261 -1516 559 -66 263 -130 689 -132 229 -64 3613 -15976 231 -166 133 -66 399 -264 99 -132 295 -366 97 -1692 99 -398 529 -68 397 -130 899 -164 3559 -98 1197 -12106 199 -98 65 -166 99 -266 99 -134 231 -100 133 -132 297 -430 99 -1394 299 -64 397 -166 99 -100 465 -200 331 -132 599 -100 2333 -15214 65 -1230 231 -266 265 -432 165 -398 65 -532 333 -632 65 -232 957 -98 9785 -6320 97 -830 167 -166 133 -732 299 -958 327 -98 197 -66 229 -164 327 -98 653 -66 7993 -6418 65 -1284 97 -458 129 -196 197 -166 393 -134 99 -332 427 -132 131 -66 133 -98 233 -66 133 -364 163 -566 4873 -16030 97 +RAW_Data: -360 65 -364 65 -68 857 -98 65 -232 131 -264 63 -98 391 -396 65 -130 99 -98 65 -66 861 -166 265 -166 7611 -10336 65 -1822 165 -300 165 -166 295 -134 199 -100 67 -264 165 -166 99 -500 99 -198 97 -200 165 -268 197 -130 65 -300 629 -166 561 -132 333 -132 7459 -6294 131 -1096 165 -964 197 -332 65 -166 129 -132 99 -130 99 -100 97 -134 65 -164 131 -494 165 -396 97 -164 131 -198 99 -232 229 -66 821 -64 131 -14954 97 -788 65 -100 263 -66 99 -300 65 -400 131 -198 293 -294 163 -132 65 -692 99 -132 131 -200 1847 -132 8773 -5968 133 -330 65 -66 295 -430 197 -166 565 -132 467 -98 65 -430 165 -262 131 -528 131 -296 131 -100 131 -66 557 -166 787 -98 3221 -16236 299 -166 133 -562 199 -1692 99 -66 65 -364 65 -366 231 -168 367 -100 5541 -14968 297 -164 97 -132 163 -328 99 -532 99 -134 131 -370 397 -66 397 -98 293 -98 197 -98 1151 -66 7019 -6746 129 -296 163 -954 261 -230 229 -64 231 -264 431 -100 99 -466 165 -100 333 -166 133 -666 695 -200 67 -134 397 -100 1667 -7686 97 -426 195 -266 97 -330 63 -98 99 -594 97 -132 133 -270 131 -600 131 -362 833 -98 297 -166 199 -66 99 -200 65 -66 197 -100 2963 -98 1125 -2238 199 -554 275 -242 273 -212 271 -242 241 -242 271 -244 241 -244 273 -244 245 -246 247 -248 247 -250 247 -250 249 -250 249 -248 249 -248 249 -250 249 -504 257 -248 245 -246 247 -248 503 -258 247 -244 245 -246 247 -248 249 -250 247 -504 513 -228 243 -244 273 -246 245 -500 513 -478 257 -242 241 -244 273 -244 499 -512 225 -244 525 -228 273 -496 475 -508 507 -240 253 -254 255 -494 261 -222 505 -258 245 -246 245 -246 245 -250 247 -506 513 -478 257 -242 241 -244 273 -244 499 -68858 319 -218 245 -230 251 -248 245 -248 247 -250 247 -250 249 -248 249 -248 249 -250 249 -250 249 -248 249 -248 249 -250 249 -248 249 -250 247 -506 257 -246 245 -246 247 -248 503 -516 477 -274 253 -474 511 -478 527 -484 511 -240 253 -472 281 -254 489 -484 513 -478 513 -480 255 -242 523 -472 507 -276 253 -462 275 -240 491 -258 247 -244 245 -246 247 -502 515 -238 253 -254 253 -492 497 -230 253 -250 247 -250 247 -250 247 -250 247 -506 257 -68880 279 -248 241 -266 223 -252 249 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -506 225 -280 213 -278 245 -248 503 -260 215 -276 245 -246 247 -248 247 -250 247 -504 481 -272 251 +RAW_Data: -254 255 -254 213 -506 483 -514 271 -254 217 -292 217 -248 497 -516 237 -254 473 -278 251 -488 489 -516 477 -272 253 -254 219 -494 261 -260 481 -260 215 -278 247 -248 247 -248 247 -504 481 -514 235 -290 217 -256 253 -244 497 -69132 311 -212 235 -258 249 -248 247 -248 249 -248 249 -248 249 -250 249 -250 247 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -506 257 -248 245 -246 245 -248 503 -512 479 -272 253 -474 513 -478 527 -482 513 -238 253 -472 281 -254 487 -486 515 -478 509 -480 255 -242 529 -480 509 -240 255 -496 271 -234 487 -260 247 -244 245 -248 247 -504 511 -226 243 -244 275 -498 479 -272 253 -254 255 -254 249 -238 225 -252 247 -504 257 -68882 319 -188 265 -280 235 -228 251 -250 249 -248 249 -248 249 -248 249 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -250 247 -250 249 -504 257 -248 245 -246 247 -248 503 -258 247 -244 245 -248 245 -250 247 -250 249 -504 515 -238 251 -256 253 -256 249 -472 515 -480 271 -256 253 -218 255 -248 497 -482 271 -252 473 -280 253 -490 487 -516 477 -274 253 -256 217 -496 261 -224 507 -258 249 -246 245 -248 247 -248 249 -504 511 -478 255 -244 243 -244 275 -246 499 -69134 311 -208 231 -254 249 -246 247 -250 247 -248 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -248 249 -250 249 -248 249 -506 259 -246 245 -246 247 -246 503 -516 479 -274 253 -464 519 -496 487 -516 477 -274 255 -498 249 -248 469 -514 479 -516 483 -488 277 -254 491 -498 487 -260 247 -498 227 -244 529 -228 243 -274 245 -246 245 -502 515 -238 251 -256 253 -496 497 -226 251 -248 249 -248 249 -248 249 -250 249 -504 257 -68898 247 -274 213 -312 185 -312 185 -312 185 -312 185 -312 185 -312 185 -310 185 -310 185 -310 215 -278 217 -278 215 -278 217 -278 217 -280 215 -532 237 -288 217 -290 215 -276 461 -260 219 -280 217 -280 217 -278 215 -280 247 -506 481 -270 251 -256 217 -290 213 -502 483 -514 271 -254 253 -218 255 -246 499 -482 271 -254 473 -282 253 -488 485 -516 477 -274 253 -256 217 -496 263 -224 507 -260 247 -246 245 -246 247 -248 249 -504 513 -478 255 -242 243 -244 275 -246 499 -69122 319 -220 243 -228 251 -248 247 -248 247 -250 247 -250 249 -248 249 -250 249 -248 249 -248 249 -250 249 -250 247 -250 249 -250 247 -250 249 -504 257 -248 245 -246 247 -246 505 -514 479 -274 253 -476 511 -476 493 -516 481 -270 253 -474 279 -254 487 -484 513 +RAW_Data: -478 507 -506 225 -272 495 -482 505 -254 241 -494 255 -242 525 -226 241 -274 243 -242 243 -496 509 -274 253 -254 219 -494 491 -258 249 -248 247 -248 249 -248 249 -248 249 -504 257 -68896 281 -214 241 -260 247 -248 247 -248 247 -250 247 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -250 249 -504 257 -248 245 -246 245 -248 503 -260 245 -246 245 -248 247 -248 247 -248 249 -504 513 -238 251 -254 255 -254 251 -474 515 -478 273 -254 255 -218 253 -248 499 -514 237 -254 473 -280 253 -490 485 -516 479 -274 253 -256 217 -496 263 -224 507 -258 247 -248 245 -246 247 -250 247 -504 513 -478 257 -244 241 -244 275 -246 499 -69128 321 -218 241 -230 249 -248 247 -246 249 -248 249 -248 249 -248 249 -250 249 -250 247 -250 247 -250 249 -250 247 -250 249 -250 249 -248 249 -504 257 -248 245 -246 245 -250 503 -514 477 -272 255 -474 515 -480 491 -518 479 -274 253 -474 281 -254 483 -486 479 -512 507 -506 223 -242 527 -482 509 -240 253 -496 273 -236 487 -260 247 -246 245 -246 247 -504 513 -238 251 -256 253 -492 495 -228 253 -250 249 -250 249 -250 249 -248 249 -504 257 -68860 273 -242 233 -256 249 -250 247 -248 247 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -504 257 -248 245 -246 247 -248 503 -258 247 -244 245 -248 247 -248 247 -248 249 -504 513 -238 253 -254 253 -256 249 -474 515 -480 273 -254 255 -218 253 -248 497 -516 237 -252 473 -280 253 -490 487 -514 479 -274 253 -254 219 -496 263 -224 507 -258 247 -246 245 -246 247 -248 249 -504 513 -476 257 -242 243 -244 273 -246 499 -68728 129 -604 249 -242 243 -276 213 -276 245 -244 245 -246 245 -278 215 -278 215 -278 247 -248 247 -248 247 -248 249 -248 247 -250 247 -250 247 -250 247 -504 259 -246 245 -246 247 -248 503 -514 477 -272 255 -474 511 -478 527 -484 481 -272 253 -472 281 -254 485 -486 511 -478 511 -480 255 -242 527 -482 505 -254 241 -494 253 -242 495 -254 241 -244 273 -244 243 -494 509 -238 255 -254 255 -492 489 -260 247 -248 245 -248 247 -248 249 -248 249 -504 257 -129658 99 -98 131 -132 97 -100 97 -132 131 -98 131 -496 297 -266 163 -198 99 -398 165 -626 133 -198 531 -166 67 -66 431 -132 331 -100 65 -132 99 -100 2725 -9960 65 -3686 295 -1552 99 -362 195 -100 887 -98 263 -100 1495 -8372 67 -298 99 -100 131 -332 133 -198 233 -398 65 -1060 99 -164 327 +RAW_Data: 2243 -98 331 -100 1129 -66 761 -100 1393 -100 165 -66 2883 -64 357 -66 4703 -68 927 -98 233 -134 461 -66 3855 -134 165 -98 1281 -100 2053 -66 3061 -98 331 -98 8981 -66 365 -66 631 -100 1027 -100 4521 -134 597 -66 3187 -66 2619 -100 3011 -98 1151 -66 953 -100 1423 -66 1755 -166 333 -98 1557 -66 761 -66 865 -66 4837 -132 357 -132 2419 -100 1023 -66 65 -66 2507 -66 131 -66 761 -66 997 -66 333 -100 2259 -68 431 -100 2523 -66 987 -100 363 -66 363 -66 1197 -68 1589 -164 951 -96 5351 -66 697 -100 163 -100 4683 -66 2265 -68 2051 -64 457 -64 3005 -132 1057 -66 2221 -100 1661 -98 695 -100 99 -66 861 -66 1957 -100 731 -132 1857 -100 3177 -98 1807 -98 463 -66 499 -134 1129 -100 3737 -100 1889 -66 263 -98 623 -66 2103 -98 3165 -66 131 -100 195 -66 691 -66 67 -132 531 -66 1857 -100 199 -68 97 -68 197 -68 697 -68 233 -100 3749 -134 1691 -68 3289 -66 3751 -68 65 -100 853 -66 531 -132 1299 -66 1585 -98 65 -98 1577 -66 785 -98 1151 -66 165 -68 397 -100 4255 -100 857 -100 1017 -66 1575 -130 1255 -234 1923 -66 199 -102 301 -66 231 -66 691 -64 227 -64 195 -66 1257 -100 2353 -100 235 -100 1163 -66 5423 -66 2049 -66 1807 -66 523 -198 693 -100 367 -100 597 -100 4013 -100 233 -166 365 -66 1827 -100 1491 -100 785 -64 885 -66 599 -134 2847 -100 667 -100 4943 -98 3319 -98 6729 -98 361 -96 391 -66 723 -132 503 -66 1583 -166 297 -234 2045 -66 1185 -134 661 -66 195 -66 291 -164 523 -98 1679 -134 233 -132 761 -394 855 -100 2003 -164 261 -66 229 -96 953 -66 3889 -66 929 -66 993 -68 3099 -132 1673 -66 1833 -100 563 -100 1131 -100 3219 -232 4411 -100 1095 -100 5315 -100 631 -198 461 -198 1907 -100 1743 -68 863 -132 4013 -64 295 -66 3883 -100 2707 -198 923 -100 2539 -166 629 -100 563 -100 3783 -68 893 -66 2987 -98 2357 -98 1665 -66 599 -66 1259 -232 165 -66 1361 -66 1645 -166 1543 -66 565 -66 401 -134 465 -100 831 -98 2405 -100 1055 -66 2109 -100 1161 -68 431 -100 265 -68 235 -66 463 -66 3453 -100 433 -66 2693 -132 263 -166 729 -134 763 -134 1327 -100 397 -234 795 -68 563 -66 1625 -98 267 -66 4835 -66 197 -66 589 -66 7575 -100 1959 -100 131 -68 297 -134 261 -98 433 -66 1427 -66 2421 -100 2925 -166 1921 -134 1645 -66 97 -132 5423 -100 2423 -98 1065 -66 1715 -132 963 -66 2403 -66 1117 -328 1981 -66 527 -100 427 -164 865 -66 2129 -232 165 -68 165 -66 131 -366 131 -100 2613 -450 +RAW_Data: 937 -900 447 -454 969 -884 479 -466 939 -452 935 -454 981 -454 943 -452 955 -458 945 -452 979 -444 943 -486 945 -448 951 -464 977 -440 967 -478 935 -480 951 -456 969 -460 975 -450 973 -450 979 -470 977 -450 981 -910 485 -466 939 -928 489 -448 971 -940 445 -484 951 -928 485 -484 945 -948 479 -456 973 -918 481 -944 451 -928 471 -478 995 -912 487 -472 977 -15948 479 -444 1021 -910 521 -444 995 -942 471 -480 991 -450 1013 -452 1023 -446 1001 -484 981 -486 1007 -458 1015 -470 1007 -450 1033 -452 1005 -480 1005 -458 1013 -470 1007 -482 1015 -450 1023 -482 1009 -462 1015 -468 1011 -484 983 -482 1021 -944 487 -484 1015 -942 471 -486 1003 -948 503 -478 991 -948 501 -478 1031 -914 521 -486 991 -946 519 -916 511 -944 485 -474 1009 -974 487 -482 1015 -16224 521 -468 1005 -970 503 -478 1001 -954 505 -458 1035 -484 1019 -482 1039 -482 1019 -486 1031 -490 1009 -478 1033 -486 1033 -486 1011 -494 1039 -478 1039 -450 1049 -486 1033 -488 1005 -476 1071 -448 1067 -486 1017 -468 1045 -482 1045 -484 1015 -952 515 -482 1043 -944 519 -482 1049 -950 519 -454 1039 -956 523 -484 1011 -960 505 -486 1065 -956 509 -926 539 -944 519 -480 1017 -984 521 -454 1037 -16440 553 -440 1043 -976 507 -460 1069 -940 513 -486 1041 -480 1067 -482 1033 -476 1061 -472 1043 -510 1049 -486 1041 -482 1043 -482 1065 -476 1037 -486 1069 -492 1037 -484 1047 -504 1047 -486 1041 -484 1041 -514 1015 -520 1049 -476 1053 -490 1041 -980 519 -486 1043 -962 507 -482 1049 -994 507 -500 1043 -946 507 -516 1033 -982 517 -478 1049 -984 509 -976 505 -950 527 -490 1039 -980 519 -486 1047 -111258 195 -428 263 -162 163 -362 97 -132 65 -98 163 -132 825 -100 795 -100 1795 -134 587 -66 229 -100 1349 -164 3261 -66 2305 -132 2219 -66 5549 -234 497 -132 201 -66 667 -298 2369 -68 4381 -66 3909 -134 923 -98 723 -100 1651 -168 1197 -100 65 -66 199 -68 195 -100 197 -134 1135 -66 2787 -66 3163 -68 231 -68 197 -100 6675 -100 667 -98 1125 -66 67 -98 2423 -66 2017 -332 2949 -100 1129 -68 1655 -100 1229 -66 1285 -130 163 -132 1315 -66 525 -98 295 -100 131 -64 427 -132 2207 -98 1153 -66 99 -100 697 -98 1397 -166 863 -66 1393 -132 5005 -66 497 -100 1753 -100 597 -66 1667 -66 397 -100 961 -66 763 -134 859 -64 689 -98 1917 -134 199 -234 167 -100 131 -166 2061 -66 1521 -98 759 -100 983 -66 825 -166 459 -66 2049 -166 1615 -100 829 -234 631 -66 465 -66 1493 -68 433 -66 1623 -132 65 -100 1133 -132 3083 -66 199 -132 199 -68 1257 -66 +RAW_Data: 265 -68 1061 -98 533 -100 1233 -68 1721 -68 995 -100 2535 -66 4193 -232 727 -100 727 -100 2773 -66 133 -98 399 -134 233 -232 67 -66 497 -100 267 -132 1127 -134 1063 -66 565 -132 97 -132 523 -132 919 -66 891 -66 855 -98 495 -66 3363 -296 3199 -98 563 -66 133 -100 495 -98 1165 -134 1161 -166 1849 -98 853 -132 5647 -134 563 -98 1827 -100 131 -100 1125 -132 1659 -132 265 -68 1121 -66 465 -232 431 -68 3589 -98 197 -68 97 -164 1717 -66 1645 -66 397 -66 97 -68 231 -166 631 -100 627 -66 1757 -66 131 -164 527 -98 1285 -328 1213 -134 2059 -100 1791 -68 931 -66 1611 -66 1511 -66 2211 -66 2597 -100 2545 -98 197 -162 1089 -98 589 -360 495 -132 1685 -202 1095 -100 729 -100 2825 -100 231 -100 567 -100 231 -66 1027 -66 131 -68 525 -132 1613 -232 461 -232 1597 -66 627 -198 231 -98 131 -98 65 -100 1229 -68 2507 -64 1349 -66 195 -134 97 -66 1321 -100 855 -132 163 -132 1151 -100 1025 -164 329 -66 891 -98 951 -132 163 -166 591 -98 1149 -132 955 -66 1329 -98 923 -66 331 -64 4269 -66 797 -134 399 -98 267 -170 197 -100 429 -198 1225 -100 331 -100 231 -132 1463 -100 597 -164 331 -66 1863 -134 659 -98 5507 -100 719 -100 131 -64 655 -164 1579 -98 1423 -130 1381 -98 1317 -132 467 -66 495 -132 361 -132 4417 -98 631 -364 299 -100 1499 -132 267 -68 663 -98 691 -100 2433 -66 953 -98 721 -66 1355 -232 897 -134 897 -134 365 -100 267 -132 2059 -132 199 -102 797 -68 695 -66 601 -66 265 -68 499 -66 1327 -164 1355 -64 1279 -66 3257 -66 1351 -66 131 -96 359 -132 499 -232 623 -96 427 -68 1909 -98 591 -98 4671 -100 4541 -66 1491 -66 3347 -98 1277 -100 1679 -198 295 -130 357 -98 697 -98 865 -100 2817 -66 329 -98 787 -64 1117 -66 3313 -202 1721 -100 199 -100 399 -66 199 -132 1891 -100 235 -100 201 -134 765 -166 761 -132 1529 -66 629 -202 861 -130 3501 -98 1377 -100 1741 -164 1509 -66 735 -68 733 -66 265 -166 2015 -134 131 -100 663 -100 2995 -132 1577 -98 1885 -66 2461 -100 1189 -66 1425 -100 201 -100 1691 -100 199 -98 499 -166 233 -100 233 -134 661 -68 1393 -100 295 -164 2079 -66 1289 -66 329 -198 599 -100 465 -98 3995 -98 199 -268 2045 -264 199 -66 1593 -66 165 -68 1561 -164 629 -66 635 -100 1251 -230 2733 -66 1727 -66 629 -100 1229 -132 731 -66 163 -198 131 -100 693 -66 3223 -68 565 -132 1091 -134 531 -100 3223 -68 729 -100 1527 -134 895 -166 1265 -66 527 -100 201 -200 1463 -66 1233 -132 +RAW_Data: 2397 -200 167 -234 1803 -66 821 -100 1351 -66 1687 -100 165 -66 233 -66 1125 -100 2203 -132 197 -98 97 -66 593 -164 4187 -102 529 -66 1161 -68 799 -66 427 -232 263 -66 589 -68 495 -68 197 -100 525 -66 327 -98 427 -130 1551 -66 727 -102 133 -234 265 -98 459 -66 2337 -64 585 -68 297 -68 691 -98 1857 -134 665 -132 365 -66 931 -166 495 -430 689 -196 1191 -98 465 -100 931 -366 1351 -102 3185 -164 1151 -98 2465 -66 2193 -100 331 -134 165 -98 267 -166 1985 -98 889 -132 765 -66 531 -100 1449 -166 457 -98 1715 -66 299 -166 3131 -130 197 -98 817 -294 793 -100 97 -66 3415 -164 1019 -98 1675 -132 197 -100 133 -68 199 -134 3319 -298 297 -66 791 -66 1029 -134 2153 -100 1629 -132 1391 -68 1229 -100 665 -66 2039 -164 461 -64 261 -66 395 -202 395 -166 3159 -134 2253 -166 265 -132 395 -66 887 -98 163 -66 589 -98 227 -130 1151 -230 293 -66 591 -68 527 -132 2883 -100 231 -66 99 -232 761 -134 499 -64 929 -100 167 -300 2259 -100 691 -164 459 -66 493 -132 163 -64 1283 -164 757 -132 295 -264 1023 -100 197 -198 6635 -198 2407 -100 2091 -132 1531 -66 1889 -100 199 -134 3567 -100 2981 -100 263 -198 425 -164 595 -100 231 -68 2691 -66 965 -100 2907 -98 367 -132 885 -198 1721 -100 659 -100 97 -296 495 -166 299 -134 397 -132 699 -66 1165 -66 465 -68 197 -66 659 -66 1543 -66 819 -164 2913 -98 1061 -66 5475 -132 167 -100 1035 -66 3427 -298 429 -166 2723 -66 831 -98 133 -66 133 -66 495 -98 701 -66 1063 -98 1991 -100 3319 -66 263 -66 233 -66 695 -66 593 -132 595 -66 553 -66 459 -66 197 -164 2241 -66 165 -68 959 -98 1587 -166 65 -102 233 -66 465 -134 1227 -100 2359 -66 1959 -198 331 -232 165 -102 531 -100 63 -66 1999 -68 265 -100 429 -66 657 -166 297 -132 823 -100 129 -132 4511 -164 659 -68 299 -66 593 -66 99 -134 65 -100 397 -66 1561 -66 697 -100 429 -66 265 -134 361 -132 195 -130 1319 -66 133 -66 265 -100 397 -268 895 -100 363 -134 433 -66 133 -100 2321 -68 +RAW_Data: -98 3573 -98 533 -68 961 -68 729 -132 559 -166 2189 -100 131 -68 657 -100 1387 -132 133 -68 2255 -68 429 -66 231 -134 793 -100 887 -98 361 -166 2141 -66 227 -130 663 -100 759 -100 1161 -134 1821 -66 327 -98 985 -130 757 -132 131 -132 1693 -66 361 -98 1411 -100 591 -132 1025 -66 663 -66 1065 -166 1059 -166 365 -66 723 -100 1659 -132 1883 -98 785 -132 1031 -66 261 -66 2501 -98 297 -66 1195 -100 691 -134 3009 -100 3921 -66 861 -66 363 -132 3361 -132 723 -66 459 -164 163 -164 333 -66 1291 -98 821 -230 591 -164 97 -262 361 -66 689 -66 733 -66 233 -134 1627 -66 533 -66 195 -100 521 -100 493 -98 493 -98 2173 -66 2037 -132 165 -100 429 -132 695 -100 67 -132 465 -68 1491 -100 1257 -66 965 -100 365 -68 929 -132 561 -66 899 -132 597 -132 861 -100 2627 -166 197 -98 2079 -66 2223 -100 1791 -364 895 -132 1027 -132 235 -68 599 -132 829 -66 197 -132 695 -66 133 -66 531 -68 333 -64 563 -66 265 -132 369 -134 2239 -164 4269 -100 793 -66 1495 -198 821 -164 133 -66 867 -66 797 -66 429 -66 365 -166 1729 -168 959 -100 1417 -66 233 -100 2579 -166 993 -164 461 -66 1529 -68 961 -66 1049 -98 1061 -132 2847 -66 229 -66 397 -134 263 -100 3285 -66 4115 -66 1547 -134 297 -132 431 -100 2895 -100 563 -66 1491 -66 399 -100 721 -66 395 -68 399 -66 1289 -66 293 -164 2307 -98 525 -66 3663 -64 927 -132 499 -134 1127 -264 397 -98 399 -198 131 -100 333 -100 663 -164 921 -166 1481 -262 691 -64 659 -64 2167 -98 3689 -100 833 -100 2085 -66 697 -100 595 -66 923 -134 893 -232 265 -98 367 -66 1157 -66 263 -130 1017 -66 623 -66 753 -100 2873 -132 395 -198 2787 -100 861 -132 3847 -100 297 -66 233 -98 1333 -100 495 -100 1325 -134 367 -66 595 -66 361 -230 4931 -66 1821 -98 329 -98 365 -168 333 -300 897 -100 2777 -66 1945 -132 2601 -66 951 -66 425 -98 789 -98 359 -64 1051 -66 1443 -132 851 -98 625 -100 97 -66 731 -232 263 -134 2757 -68 3021 -166 265 -100 1633 -132 427 -66 233 -98 799 -100 1059 -100 263 -98 557 -68 1063 -66 461 -100 1023 -98 163 -198 1481 -132 1227 -98 327 -100 327 -66 1317 -66 1853 -66 1061 -134 1287 -66 1315 -66 1345 -132 723 -66 1225 -68 1463 -166 3261 -98 2883 -66 563 -100 821 -100 2077 -166 3137 -66 565 -66 1355 -234 1415 -132 165 -66 397 -132 493 -132 563 -166 893 -66 1193 -66 1249 -100 333 -132 2083 -66 921 -100 1225 -262 861 -166 1321 -100 895 -100 591 -98 1249 +RAW_Data: -98 97 -66 6825 -66 231 -68 14077 -66 1787 -66 1547 -64 2617 -66 2925 -66 1723 -132 1529 -66 865 -166 827 -198 431 -66 495 -66 1121 -198 1327 -100 397 -130 557 -66 97 -100 261 -98 723 -98 557 -98 463 -98 463 -100 325 -66 3703 -100 465 -198 1123 -98 2545 -66 361 -66 857 -64 3455 -132 663 -98 1991 -200 825 -100 919 -98 893 -164 1749 -66 7759 -132 3321 -66 1807 -132 527 -66 393 -100 817 -130 657 -164 1485 -98 2367 -66 4171 -100 197 -130 3665 -134 1059 -132 597 -66 533 -66 1023 -98 1253 -134 2021 -100 231 -100 233 -66 197 -66 199 -66 1961 -168 729 -100 531 -100 461 -98 1361 -100 11161 -100 659 -166 229 -98 1675 -98 1027 -100 2063 -298 431 -100 99 -134 1059 -66 199 -100 763 -134 231 -66 233 -102 1761 -98 331 -68 757 -132 425 -64 457 -132 99 -66 2091 -66 567 -164 2121 -68 2125 -132 595 -200 759 -102 797 -132 1345 -66 429 -132 1019 -66 195 -66 791 -68 1227 -68 797 -132 1591 -200 199 -134 165 -66 1053 -66 559 -98 853 -164 825 -100 329 -98 891 -196 689 -132 657 -100 2341 -98 1119 -66 1883 -100 2607 -100 467 -100 1067 -164 6935 -66 2409 -132 855 -66 1809 -98 1119 -164 65 -66 199 -100 233 -132 931 -132 563 -66 1393 -132 567 -66 301 -68 1295 -66 529 -98 793 -66 131 -134 533 -132 827 -132 731 -332 1251 -98 921 -98 327 -198 361 -234 529 -66 1577 -132 97 -134 199 -100 1099 -68 1193 -132 991 -100 953 -98 2895 -166 1679 -98 161 -130 129 -66 1019 -100 261 -264 531 -100 263 -134 299 -68 495 -98 831 -100 531 -66 1357 -100 2051 -100 229 -98 829 -66 427 -66 859 -134 995 -68 665 -66 1793 -134 361 -100 2349 -66 331 -100 197 -66 1591 -66 959 -66 431 -234 2219 -332 661 -66 1487 -100 3381 -68 261 -164 463 -134 3377 -68 1127 -134 691 -66 529 -132 99 -66 6687 -98 889 -132 197 -164 725 -100 963 -66 2947 -132 327 -132 889 -66 393 -98 1581 -100 193 -130 97 -66 293 -66 1675 -100 1887 -98 2017 -100 597 -66 293 -98 557 -100 259 -98 985 -100 1727 -100 165 -100 301 -232 329 -100 533 -98 727 -100 761 -66 961 -68 2759 -100 2019 -66 855 -230 859 -98 1215 -98 1887 -98 131 -98 819 -166 227 -130 723 -132 625 -66 501 -66 429 -66 831 -66 1291 -66 331 -132 431 -132 1389 -100 265 -166 1461 -66 1907 -490 911 -962 409 -508 919 -944 419 -486 913 -526 915 -480 925 -488 911 -488 943 -476 925 -486 947 -462 945 -482 933 -484 951 -460 977 -440 967 -484 945 -468 945 -466 975 -450 983 +RAW_Data: -444 977 -458 975 -456 975 -444 965 -940 485 -430 967 -928 487 -448 981 -910 489 -446 963 -944 485 -430 975 -930 489 -912 485 -896 487 -452 977 -916 509 -454 977 -922 479 -448 985 -15898 487 -456 973 -926 493 -448 1001 -918 481 -484 981 -478 979 -450 1013 -476 977 -450 1017 -476 983 -448 999 -480 985 -484 983 -478 985 -484 981 -480 983 -476 1001 -474 979 -496 973 -486 1015 -476 977 -476 1005 -484 973 -486 1007 -920 505 -456 1011 -918 509 -458 1011 -950 497 -452 1011 -920 507 -458 1007 -950 477 -958 483 -942 481 -480 1003 -918 515 -446 1039 -922 505 -458 1007 -16154 501 -462 1013 -934 517 -474 999 -958 483 -478 993 -484 1037 -450 1027 -482 1027 -452 1037 -482 1025 -444 1035 -490 1009 -482 1013 -484 1001 -484 1039 -474 1003 -484 1045 -464 1011 -480 1049 -446 1037 -492 1007 -482 1013 -510 1005 -494 1005 -970 485 -482 1017 -974 473 -498 1007 -974 481 -486 1047 -948 479 -482 1051 -948 497 -952 519 -938 485 -482 1041 -946 517 -478 1013 -944 503 -472 415 -83640 97 -200 65 -632 131 -300 365 -364 231 -132 1061 -100 3047 -100 1393 -66 499 -66 1091 -66 333 -66 2353 -100 2273 -66 1427 -166 563 -132 1559 -132 427 -100 925 -98 13469 -100 2019 -98 821 -132 1097 -68 297 -100 897 -100 1129 -166 465 -166 961 -66 597 -100 165 -66 267 -100 201 -100 765 -134 297 -66 165 -66 3081 -100 1293 -100 1289 -136 233 -66 357 -66 1155 -166 295 -100 1197 -68 1089 -98 425 -132 1187 -100 523 -98 463 -98 197 -98 131 -98 493 -66 393 -98 2797 -164 359 -232 325 -66 229 -164 625 -98 1215 -164 425 -66 589 -98 195 -66 1083 -100 197 -68 1557 -66 1427 -66 525 -66 429 -132 863 -66 1129 -166 831 -98 265 -98 1183 -66 3157 -100 2735 -98 2819 -166 4645 -66 301 -68 1395 -132 1097 -100 897 -198 629 -200 1419 -132 493 -66 521 -132 697 -100 695 -66 459 -298 859 -66 559 -100 1029 -100 4113 -66 1167 -66 14017 -66 2123 -68 525 -132 861 -100 329 -66 399 -134 1523 -132 327 -64 691 -98 463 -132 1803 -132 853 -166 715 -66 953 -66 525 -98 723 -132 989 -132 461 -98 459 -164 2239 -66 1185 -66 589 -100 1945 -230 1483 -66 399 -66 265 -168 965 -66 197 -168 699 -68 1125 -68 529 -98 491 -66 987 -130 525 -168 397 -66 597 -100 561 -132 1353 -66 391 -132 393 -66 591 -98 557 -98 787 -66 463 -100 199 -134 395 -100 759 -66 295 -130 261 -98 229 -100 99 -100 1595 -66 699 -100 499 -66 595 -98 327 -132 957 -132 331 -100 493 -100 1313 -66 295 -132 197 -198 1279 +RAW_Data: -66 9461 -100 329 -68 27921 -66 24331 -68 13415 -66 6439 -98 133 -66 4193 -98 395 -66 653 -66 983 -66 163 -66 955 -132 1791 -66 861 -100 363 -132 1659 -132 667 -166 467 -134 429 -166 265 -66 4065 -98 293 -98 3183 -130 555 -98 163 -162 259 -100 661 -100 7057 -100 931 -100 1297 -66 2559 -98 1193 -100 333 -100 563 -132 65 -100 793 -66 855 -64 659 -100 929 -102 893 -132 689 -66 3475 -68 1361 -198 331 -134 691 -66 295 -66 425 -164 731 -266 921 -100 599 -100 165 -66 227 -98 1091 -66 263 -66 1215 -100 227 -164 657 -66 953 -132 359 -66 1845 -66 1779 -132 753 -164 393 -66 731 -66 1195 -66 533 -66 797 -132 1623 -98 1281 -100 493 -98 659 -98 2417 -166 799 -132 1259 -100 559 -134 595 -166 199 -66 1461 -198 865 -100 459 -66 463 -166 165 -100 497 -66 1097 -66 1579 -100 1449 -98 885 -98 263 -100 1097 -132 627 -68 329 -132 487 -132 427 -132 361 -66 525 -98 687 -66 1161 -100 263 -66 729 -100 229 -98 559 -66 1213 -100 1015 -66 795 -66 5475 -66 4043 -66 1683 -166 1151 -132 429 -98 1447 -68 261 -98 985 -100 429 -100 1289 -198 2269 -132 7999 -98 1591 -132 3233 -66 861 -66 2087 -98 557 -98 719 -66 981 -98 563 -100 199 -100 523 -100 2319 -134 833 -100 495 -132 197 -66 295 -64 989 -66 1059 -198 7343 -66 2023 -66 963 -66 593 -66 2401 -100 491 -100 959 -66 297 -134 999 -132 99 -68 3609 -230 97 -198 1911 -66 265 -100 1195 -132 633 -132 595 -66 1381 -66 491 -66 1681 -100 297 -100 1827 -132 2269 -100 1351 -132 1513 -66 1225 -134 231 -66 1523 -100 363 -200 1227 -66 2943 -66 923 -134 2249 -66 1809 -100 1121 -132 265 -66 827 -98 199 -66 201 -100 3279 -100 565 -132 1689 -66 395 -66 2979 -134 1065 -66 367 -168 3585 -200 463 -100 563 -66 97 -166 2293 -66 265 -134 1255 -132 2401 -66 1579 -166 365 -100 861 -298 261 -98 761 -66 363 -132 657 -130 63 -130 557 -66 131 -130 2041 -100 233 -66 1791 -100 925 -134 265 -100 1063 -100 301 -168 661 -66 657 -64 263 -64 197 -66 1853 -100 663 -98 231 -66 731 -100 5539 -166 197 -68 1423 -134 361 -68 1727 -68 929 -100 1397 -134 1885 -66 1661 -66 265 -66 1183 -66 295 -166 263 -166 165 -66 329 -66 465 -100 1159 -134 697 -100 2443 -100 393 -98 1093 -66 953 -296 787 -132 425 -66 2019 -66 461 -98 1201 -100 397 -132 3551 -100 1431 -264 725 -330 1455 -66 263 -100 531 -296 499 -100 265 -100 163 -66 1145 -132 1313 -98 2101 -98 261 -132 1083 -66 5403 +RAW_Data: -66 2223 -66 11583 -66 131 -66 5071 -66 3723 -132 1415 -132 6905 -64 9685 -102 4739 -66 3355 -66 5301 -98 29993 -508 897 -950 437 -490 909 -974 423 -510 935 -486 925 -508 943 -490 937 -494 947 -482 941 -484 979 -440 979 -456 1003 -462 975 -460 971 -458 977 -468 973 -484 975 -472 971 -450 1011 -452 1003 -446 983 -480 979 -450 1017 -908 487 -472 971 -944 471 -450 999 -944 485 -468 977 -918 493 -448 1011 -934 499 -920 487 -914 487 -452 1011 -912 521 -446 1009 -904 517 -468 1007 -16000 531 -444 1009 -908 519 -468 1009 -912 515 -466 1009 -452 1021 -466 1011 -450 1019 -480 1017 -448 1045 -448 1019 -460 1041 -450 1019 -480 1019 -450 1039 -474 1001 -480 1021 -484 1005 -476 1015 -480 1017 -484 1011 -486 1017 -464 1041 -446 1047 -922 503 -458 1037 -946 513 -442 1047 -938 503 -480 1023 -916 537 -450 1049 -926 521 -904 519 -942 519 -450 1055 -910 519 -486 1033 -916 519 -486 1029 -16258 533 -464 1015 -940 515 -456 1053 -946 511 -482 1051 -434 1075 -442 1075 -448 1065 -440 1065 -450 1049 -480 1067 -462 1041 -446 1075 -450 1063 -460 1053 -480 1047 -450 1075 -446 1079 -452 1055 -478 1051 -448 1067 -444 1065 -480 753 -66842 99 -1090 465 -332 131 -68 131 -134 99 -132 167 -200 429 -100 1809 -132 2385 -230 265 -102 597 -134 1025 -66 365 -100 361 -66 825 -168 1331 -100 797 -132 431 -132 299 -198 661 -168 501 -100 463 -164 329 -66 559 -98 391 -98 1085 -198 1939 -66 1871 -164 2251 -134 493 -66 719 -198 361 -98 361 -64 197 -132 391 -164 691 -300 489 -98 2139 -66 1413 -66 1875 -196 557 -66 263 -132 1359 -66 1397 -66 631 -100 793 -132 723 -100 65 -66 529 -134 463 -68 789 -100 227 -66 923 -100 2649 -166 363 -66 395 -200 295 -130 1757 -68 2057 -100 1023 -66 359 -66 391 -132 1679 -66 359 -66 1217 -98 663 -98 463 -100 821 -98 165 -98 1589 -132 2367 -98 559 -132 1079 -100 9617 -66 3669 -134 1787 -68 1679 -132 361 -66 555 -100 661 -66 1523 -100 2057 -198 1025 -66 4177 -100 165 -66 265 -132 465 -134 299 -232 265 -100 1125 -132 1461 -132 1295 -100 499 -132 367 -68 263 -66 331 -66 365 -100 1643 -130 197 -132 997 -98 867 -98 1191 -100 2945 -100 2339 -98 1779 -66 295 -132 597 -66 165 -100 665 -100 463 -66 331 -66 593 -100 459 -68 489 -164 855 -66 261 -64 163 -100 4449 -100 859 -100 699 -132 199 -100 1685 -66 301 -132 2317 -68 231 -100 827 -66 1749 -132 99 -64 1185 -100 329 -100 1253 -66 1127 -98 827 -198 363 -132 265 -134 365 -66 297 -66 1125 -66 261 +RAW_Data: -266 29863 -66 2443 -66 5113 -100 5947 -21026 99 -134 301 -132 199 -132 131 -266 163 -196 131 -66 365 -66 465 -98 13819 -98 525 -98 329 -100 893 -66 1259 -66 431 -98 427 -130 1051 -392 463 -200 795 -164 399 -66 1489 -66 1377 -100 1423 -132 597 -100 689 -68 1559 -100 2263 -100 1327 -98 1059 -98 497 -66 595 -132 265 -66 299 -66 199 -66 563 -134 627 -66 165 -134 889 -66 2751 -232 893 -264 231 -66 299 -132 467 -132 861 -68 1263 -164 795 -66 2601 -100 429 -66 1525 -66 961 -98 265 -98 997 -66 233 -68 695 -100 697 -66 795 -66 1195 -66 1223 -68 2173 -66 467 -66 827 -66 535 -68 697 -100 1221 -166 165 -100 365 -132 723 -66 829 -132 2091 -232 265 -66 195 -66 459 -262 499 -100 461 -68 759 -100 1087 -66 259 -164 2845 -66 1365 -98 561 -200 331 -168 201 -166 1397 -198 197 -66 697 -68 1713 -68 293 -134 1317 -66 593 -328 395 -100 499 -132 2251 -100 563 -134 333 -134 1921 -134 1187 -68 561 -132 933 -66 797 -100 631 -100 399 -132 929 -66 2769 -66 851 -130 2047 -66 265 -100 7219 -66 1987 -66 299 -98 2199 -134 1063 -98 2843 -98 655 -132 231 -66 1123 -198 2137 -64 327 -66 3183 -66 1127 -66 631 -100 263 -102 3173 -132 267 -68 1289 -98 1593 -66 2415 -66 1185 -66 359 -132 1051 -66 2169 -66 427 -98 395 -132 793 -98 293 -166 727 -134 131 -100 1287 -98 427 -98 687 -164 823 -64 853 -66 865 -100 763 -66 2025 -100 959 -66 1891 -64 793 -100 763 -66 729 -166 99 -98 399 -134 763 -100 4203 -66 1321 -230 4023 -98 1053 -66 985 -98 1383 -66 3559 -164 1515 -100 2899 -66 797 -134 1169 -100 3055 -134 1615 -66 429 -100 495 -64 1583 -134 923 -66 921 -66 723 -68 1359 -98 787 -98 425 -100 393 -64 1189 -98 263 -98 491 -100 1455 -98 +RAW_Data: -202 531 -66 531 -66 1093 -66 1389 -66 1551 -134 2699 -66 1291 -132 65 -64 657 -98 1083 -164 393 -98 1359 -134 1461 -66 393 -100 561 -130 2113 -132 597 -66 431 -102 1759 -302 985 -66 235 -100 1395 -66 901 -66 1061 -100 463 -66 5673 -66 227 -66 225 -66 855 -66 1581 -132 2503 -100 657 -66 2535 -98 259 -64 1015 -66 231 -132 1197 -66 827 -166 9641 -66 1823 -132 1565 -132 299 -66 797 -66 1631 -132 327 -132 2227 -232 433 -68 499 -100 1793 -66 1161 -132 525 -66 129 -100 361 -66 1765 -132 229 -66 491 -132 2255 -100 3043 -332 299 -100 499 -100 267 -68 2967 -66 991 -100 729 -100 633 -66 529 -98 825 -100 1033 -100 331 -66 723 -100 725 -264 2987 -68 825 -66 2601 -134 333 -100 3181 -134 1059 -100 299 -134 3279 -100 1221 -132 659 -66 3157 -98 1595 -132 1561 -98 201 -134 465 -66 1843 -130 589 -66 1413 -66 331 -100 333 -66 661 -100 265 -68 201 -234 1027 -166 297 -100 1161 -132 1561 -134 629 -66 431 -66 1025 -98 427 -198 1527 -66 793 -66 1903 -66 131 -130 1285 -66 299 -134 397 -98 229 -132 499 -132 4747 -100 2355 -100 263 -132 1915 -132 1749 -132 759 -66 2253 -100 4545 -66 391 -100 521 -100 1083 -100 929 -134 565 -66 2355 -66 1331 -66 167 -100 465 -100 1727 -132 633 -330 433 -66 897 -132 165 -134 331 -98 627 -66 231 -66 167 -66 1397 -66 729 -132 1397 -68 165 -66 1627 -134 2187 -66 231 -134 795 -200 6469 -232 829 -66 3929 -66 891 -98 1977 -100 525 -68 859 -66 921 -264 1029 -68 959 -134 1555 -66 259 -100 687 -66 429 -264 663 -66 1559 -100 1127 -100 2327 -132 1913 -64 4193 -132 293 -98 99 -100 5613 -132 1351 -66 1545 -66 1677 -66 295 -64 1943 -100 595 -132 1959 -166 765 -66 1389 -100 823 -66 1749 -66 1217 -100 597 -100 297 -66 2019 -98 165 -100 4165 -100 67 -100 2477 -262 295 -66 919 -200 3555 -66 229 -66 2531 -98 557 -66 2525 -66 1463 -100 1293 -68 197 -68 1391 -66 1421 -66 595 -164 327 -68 2285 -66 593 -98 99 -68 463 -98 1063 -100 165 -68 99 -100 631 -66 1085 -66 859 -98 6599 -66 1429 -66 233 -66 397 -98 231 -132 1975 -132 333 -66 131 -134 3373 -100 4277 -66 1363 -232 2893 -166 3133 -64 951 -66 2815 -100 425 -98 327 -66 599 -68 1031 -98 133 -68 633 -68 429 -100 1129 -66 327 -130 2679 -66 1321 -100 463 -200 367 -98 667 -66 493 -132 885 -98 2183 -166 559 -98 981 -66 3201 -164 593 -66 493 -130 1923 -166 565 -100 2421 -98 461 -66 1427 -130 1955 -64 197 -66 1643 +RAW_Data: -132 2291 -66 3057 -68 2521 -166 333 -134 503 -400 3235 -66 2329 -68 995 -100 333 -100 97 -166 1757 -100 397 -100 165 -66 2755 -132 297 -134 163 -100 565 -100 1793 -100 1813 -162 1293 -98 97 -66 999 -66 1763 -68 261 -68 2391 -100 765 -364 859 -100 1855 -98 1399 -230 463 -134 301 -198 397 -100 961 -68 431 -134 695 -202 133 -100 365 -66 925 -98 165 -66 365 -132 663 -98 4573 -134 1479 -66 1019 -66 629 -66 233 -68 201 -66 569 -66 295 -134 1755 -296 3199 -100 3261 -168 3373 -132 1425 -100 759 -66 895 -98 201 -100 265 -166 99 -66 695 -66 1091 -66 855 -168 299 -100 229 -164 589 -66 521 -66 655 -134 329 -98 493 -200 429 -66 929 -66 673 -100 953 -66 823 -66 1283 -66 1979 -68 233 -66 1547 -164 589 -132 597 -66 131 -66 265 -100 761 -200 759 -66 689 -332 263 -100 1227 -68 1067 -164 2945 -100 959 -100 995 -100 399 -100 1193 -100 625 -66 399 -66 3021 -134 393 -66 4805 -66 1095 -68 231 -332 399 -166 1663 -68 561 -66 927 -98 1085 -164 1155 -98 627 -66 265 -132 263 -130 2211 -66 2159 -66 1029 -264 2669 -66 295 -66 8747 -100 329 -232 625 -134 429 -68 1329 -168 1355 -98 987 -66 1545 -98 1015 -98 699 -134 133 -134 1263 -66 4687 -166 8299 -66 1349 -434 933 -906 443 -452 949 -894 441 -480 909 -486 907 -456 947 -448 949 -434 969 -454 931 -460 941 -448 933 -450 979 -450 945 -450 935 -458 935 -486 927 -456 947 -450 951 -482 945 -428 975 -446 967 -452 955 -458 945 -912 485 -434 971 -902 481 -450 949 -926 451 -478 941 -920 481 -450 949 -920 473 -450 955 -916 471 -452 981 -918 449 -486 945 -910 483 -924 473 -15780 479 -450 967 -948 449 -482 943 -944 485 -468 941 -484 979 -446 1001 -444 999 -446 967 -484 969 -482 979 -478 947 -484 985 -470 973 -458 983 -492 971 -458 979 -494 971 -458 983 -494 973 -458 981 -498 975 -458 1013 -466 973 -952 475 -458 973 -950 489 -450 1011 -916 481 -478 1001 -918 481 -478 1001 -916 481 -478 1001 -920 483 -480 983 -946 479 -458 1013 -932 485 -952 479 -16040 493 -476 1009 -912 515 -464 1007 -910 517 -464 1007 -450 1001 -488 1001 -484 1011 -450 1019 -458 1039 -450 1031 -454 1005 -484 1009 -458 1015 -476 1009 -478 1035 -462 1015 -468 1007 -480 1001 -486 1015 -460 1041 -450 1017 -484 1019 -482 1009 -952 477 -494 1003 -944 485 -478 1013 -944 519 -482 1013 -942 487 -482 1013 -946 487 -484 1015 -944 487 -484 1015 -946 519 -454 1019 -942 505 -952 487 -16238 517 -468 1007 -942 521 -482 1011 -944 519 +RAW_Data: -450 1019 -482 1035 -480 1033 -460 1047 -476 1017 -484 1007 -484 1051 -484 1027 -452 1039 -478 1035 -458 1049 -480 1017 -480 1035 -480 1035 -472 1047 -484 1027 -454 1039 -480 1033 -488 1031 -488 1009 -962 521 -486 1017 -966 485 -490 1015 -139210 229 -98 461 -364 165 -334 131 -168 2121 -66 1049 -66 1215 -166 297 -136 1449 -100 3877 -100 1495 -234 331 -64 1345 -262 393 -100 529 -132 2921 -164 1223 -132 1807 -66 765 -66 397 -98 3405 -132 2123 -230 231 -66 2541 -100 2489 -98 4397 -132 461 -98 293 -64 991 -66 1125 -166 401 -100 131 -100 99 -100 265 -100 2555 -100 499 -98 1361 -134 265 -166 895 -100 2253 -100 1057 -100 129 -296 1147 -198 197 -66 1163 -66 1935 -98 1675 -66 1103 -100 891 -100 989 -164 1019 -66 2967 -68 1293 -166 3161 -66 133 -264 1065 -100 731 -66 1693 -66 529 -100 165 -68 865 -66 825 -232 1117 -196 2401 -66 3051 -296 229 -132 1843 -132 1687 -68 1119 -68 299 -68 97 -66 4741 -66 197 -200 2319 -100 1097 -66 3765 -66 131 -100 695 -132 2753 -66 2287 -100 1129 -68 331 -98 1433 -132 893 -100 465 -100 801 -66 529 -66 1515 -264 393 -98 263 -66 1831 -166 3533 -100 633 -100 1051 -100 331 -98 795 -134 959 -132 1229 -100 627 -132 2517 -66 165 -98 131 -66 2301 -166 163 -134 465 -66 2767 -66 1019 -66 401 -134 397 -232 893 -66 397 -66 833 -66 199 -66 303 -66 2775 -66 2069 -98 1841 -100 399 -66 793 -98 2793 -68 3769 -100 867 -66 861 -100 399 -66 1859 -100 631 -132 755 -100 689 -66 163 -64 2045 -64 2191 -102 1127 -68 727 -68 625 -164 1381 -66 1153 -132 1115 -98 1017 -100 491 -100 593 -132 991 -98 1415 -98 4813 -66 331 -98 131 -102 1847 -98 197 -68 263 -100 3265 -66 431 -100 493 -98 435 -134 133 -68 1185 -134 395 -100 131 -66 399 -134 767 -134 1125 -66 429 -198 3185 -100 2261 -66 523 -230 2475 -168 1297 -66 3243 -66 1853 -100 1657 -66 459 -66 827 -100 263 -66 303 -234 197 -166 1167 -100 2299 -66 1329 -68 461 -100 763 -132 3819 -366 757 -66 591 -164 621 -98 1445 -100 2155 -100 231 -100 631 -68 1161 -66 131 -166 67 -98 1915 -166 1891 -66 1261 -68 999 -164 165 -132 133 -168 2695 -68 1055 -198 97 -98 229 -66 229 -66 1215 -66 885 -100 303 -132 297 -164 619 -198 459 -64 989 -66 229 -66 597 -134 693 -64 1255 -100 65 -132 331 -66 199 -98 529 -100 2831 -98 1259 -66 4855 -100 1163 -166 299 -66 395 -98 3141 -66 1319 -66 2139 -100 161 -132 261 -130 821 -200 263 -134 931 -330 65 -98 99 -134 793 +RAW_Data: -66 597 -100 231 -68 167 -66 1659 -100 733 -66 1631 -100 165 -66 199 -66 233 -166 165 -100 1925 -68 595 -198 1785 -134 2177 -134 131 -66 1049 -98 3087 -132 195 -64 589 -66 397 -134 329 -66 2565 -164 327 -100 689 -64 1775 -100 5183 -132 1187 -66 329 -66 395 -132 165 -98 261 -98 1247 -64 1217 -66 927 -66 997 -66 199 -98 1419 -66 531 -166 1231 -66 697 -100 97 -66 563 -66 161 -264 3205 -200 525 -98 293 -100 291 -100 133 -66 759 -66 659 -100 983 -64 523 -130 431 -166 919 -66 1097 -100 1757 -66 1119 -66 917 -98 2647 -166 1247 -66 165 -264 1189 -100 899 -134 597 -68 2323 -66 1893 -66 1095 -100 533 -64 965 -100 1817 -130 1215 -66 1879 -64 821 -164 1117 -132 263 -132 131 -66 557 -66 431 -132 661 -100 1183 -98 629 -100 1679 -132 259 -66 623 -98 431 -66 399 -164 923 -100 297 -66 165 -166 2521 -198 99 -66 431 -132 1225 -66 1063 -68 131 -136 631 -66 163 -100 99 -298 965 -68 465 -68 465 -298 2545 -134 2639 -230 1489 -66 299 -66 1991 -234 65 -132 693 -134 429 -102 101 -68 461 -66 3333 -64 1229 -68 333 -66 265 -66 885 -64 3163 -100 467 -66 2651 -164 1221 -100 1527 -66 1259 -134 431 -232 1259 -100 6029 -164 297 -98 1151 -66 1415 -100 5289 -66 2467 -100 493 -132 495 -200 1121 -66 129 -66 757 -166 327 -130 5477 -66 1227 -230 395 -100 265 -132 497 -132 1133 -132 361 -100 1051 -164 3089 -132 1583 -100 65 -68 2315 -100 529 -132 2157 -68 1257 -66 1975 -98 427 -98 1347 -66 719 -164 857 -66 165 -66 1029 -132 297 -132 467 -100 731 -130 1985 -98 199 -166 899 -100 1391 -166 3425 -100 261 -132 721 -66 4845 -98 1193 -68 1225 -66 721 -100 1015 -64 983 -66 557 -130 693 -98 99 -64 1091 -98 197 -100 2321 -66 431 -134 727 -66 467 -102 891 -98 167 -134 2619 -66 393 -64 97 -100 589 -98 1583 -164 301 -68 1481 -98 295 -98 959 -66 365 -98 1253 -66 231 -100 1255 -132 1813 -132 1645 -100 361 -132 395 -100 427 -164 1197 -98 1001 -100 861 -66 1161 -98 195 -100 197 -66 1429 -66 663 -66 1427 -98 665 -66 699 -100 663 -66 855 -196 161 -100 361 -98 823 -66 227 -66 621 -132 1853 -230 461 -230 623 -100 557 -98 229 -98 133 -134 1291 -66 533 -166 627 -134 195 -134 593 -64 591 -66 1019 -66 1049 -262 297 -100 2921 -66 133 -66 963 -134 165 -100 +RAW_Data: -68 521674 -200 213 -258 217 -218 217 -246 193 -242 211 -466 451 -450 225 -212 211 -242 211 -238 243 -200 223 -254 419 -226 221 -432 251 -212 467 -240 203 -450 449 -246 205 -238 217 -218 217 -444 439 -478 431 -230 211 -462 247 -214 435 -454 449 -456 441 -442 471 -448 449 -246 205 -238 215 -220 217 -244 201 -254 217 -430 235 -220 461 -198 223 -442 275 -212 429 -458 245 -212 203 -228 253 -218 431 -448 451 -442 235 -218 463 -426 485 -214 209 -234 221 -254 219 -214 207 -228 253 -428 459 -418 245 -216 243 -194 241 -212 241 -212 211 -244 441 -228 215 -460 217 -244 435 -236 235 -440 439 -246 213 -208 229 -256 217 -430 445 -450 445 -236 253 -420 225 -232 429 -450 465 -448 439 -448 449 -454 453 -230 225 -214 241 -212 211 -242 211 -244 237 -442 237 -218 429 -230 221 -442 241 -246 425 -458 245 -212 203 -228 255 -218 427 -448 451 -446 235 -256 425 -462 417 -244 251 -208 201 -256 217 -218 209 -232 255 -420 451 -448 217 -242 211 -238 209 -234 221 -256 217 -218 437 -238 217 -464 203 -256 419 -226 223 -464 441 -244 213 -240 197 -254 219 -430 445 -452 479 -204 253 -420 225 -222 463 -452 449 -450 449 -452 447 -450 451 -238 215 -220 217 -244 199 -256 217 -218 207 -440 241 -254 423 -238 217 -462 197 -224 471 -448 225 -220 213 -242 211 -212 465 -446 449 -456 245 -214 437 -456 449 -232 233 -216 219 -254 207 -204 253 -218 219 -440 451 -456 237 -216 217 -254 209 -202 253 -218 219 -210 437 -244 253 -422 237 -218 423 -226 247 -430 451 -242 203 -228 253 -220 217 -442 449 -454 449 -240 217 -430 237 -218 461 -426 453 -450 465 -448 451 -440 443 -246 215 -250 201 -224 255 -218 215 -208 227 -442 239 -208 457 -238 219 -464 201 -256 425 -464 201 -256 217 -210 241 -216 451 -426 445 -486 207 -236 441 -430 479 -194 223 -256 217 -218 209 -234 253 -218 217 -438 449 -454 239 -216 217 -256 207 -202 255 -218 217 -210 439 -244 253 -424 235 -218 457 -224 229 -436 449 -230 221 -214 211 -242 211 -468 447 -450 449 -250 207 -454 239 -218 431 -446 451 -448 443 -486 447 -448 431 -240 207 -232 223 -254 219 -216 209 -230 253 -426 233 -234 433 -244 213 -432 237 -218 463 -446 221 -212 211 -242 239 -208 457 -456 451 -456 223 -212 467 -450 453 -204 253 -218 211 -240 215 -218 255 -208 201 -442 479 -452 203 -254 217 -212 241 -216 217 -256 207 -202 439 -240 245 -430 237 -218 463 -240 217 -428 465 -202 253 -218 213 -234 219 +RAW_Data: -444 443 -452 455 -244 213 -440 237 -254 427 -450 429 -480 417 -454 477 -430 445 -244 217 -250 203 -224 253 -220 215 -206 221 -462 217 -212 467 -234 231 -440 239 -220 427 -446 219 -242 211 -244 237 -206 451 -458 451 -458 203 -254 427 -450 441 -246 213 -242 193 -244 211 -242 211 -212 241 -442 451 -454 245 -214 241 -198 255 -218 217 -210 225 -212 461 -246 213 -440 225 -242 435 -240 209 -456 457 -246 211 -204 229 -254 219 -428 445 -452 445 -238 253 -426 231 -232 435 -448 455 -448 439 -446 449 -456 451 -230 223 -214 241 -212 211 -242 211 -244 237 -442 237 -218 427 -232 221 -442 239 -246 427 -458 245 -212 205 -228 253 -220 427 -446 471 -442 237 -216 451 -462 411 -244 251 -216 205 -222 211 -242 211 -242 211 -480 453 -418 245 -252 207 -202 253 -218 219 -210 229 -256 425 -232 221 -430 251 -212 469 -204 235 -446 441 -246 213 -242 193 -244 211 -464 451 -456 441 -248 215 -450 201 -256 427 -452 457 -450 455 -450 439 -446 451 -238 217 -256 207 -202 255 -218 217 -210 231 -442 237 -244 423 -240 217 -464 203 -254 419 -454 227 -210 243 -212 241 -212 477 -418 453 -484 199 -226 431 -267004 97 -422 97 -164 65 -490 97 -2222 559 -66 163 -64 295 -100 497 -100 263 -98 361 -460 793 -66 1803 -100 339 -68 1733 +RAW_Data: 12477 16271 -64 12623 -102 40819 -98 3729 -66 14371 -66 14943 -64 5931 -66 11147 -68 74641 -102 54299 -70 18441 -66 82993 -100 66161 -68 61869 -66 6627 -66 12987 -68 30427 -68 25761 -98 6305 -66 5019 -64 30857 -132 23929 -68 25129 -39378 317 -2020 311 -2010 2033 -312 2017 -282 325 -2018 291 -2024 2035 -316 2023 -276 319 -2028 297 -2032 2029 -284 2023 -314 321 -2016 319 -2012 2003 -310 2015 -318 321 -1984 327 -16000 351 -1974 321 -2020 2041 -276 2047 -288 327 -1992 327 -2000 2055 -282 2053 -274 305 -2022 301 -2014 2049 -286 2055 -274 319 -1992 329 -2006 2065 -282 2023 -316 323 -1988 303 -16008 323 -2014 315 -1990 2053 -284 2061 -274 319 -1988 319 -2038 2033 -286 2055 -264 339 -2008 289 -2034 2035 -284 2061 -262 339 -2008 287 -2040 2037 -286 2035 -268 347 -2006 317 -15988 311 -2014 343 -2014 2027 -300 2031 -258 311 -2024 323 -2028 2023 -280 2051 -318 285 -2024 309 -2002 2039 -312 2017 -318 289 -2020 293 -2028 2035 -316 2023 -276 321 -2030 299 -15982 345 -1988 345 -2014 2029 -268 2069 -258 337 -1998 321 -2024 2025 -320 2025 -276 321 -1994 331 -2000 2057 -282 2043 -282 305 -2034 291 -2024 2039 -320 2019 -278 323 -1992 333 -15976 389 -1978 321 -1986 2069 -276 2053 -288 303 -2020 315 -1980 2067 -258 2075 -258 337 -1996 321 -2030 2027 -284 2059 -274 319 -2004 319 -2016 2033 -282 2059 -264 345 -2006 289 -16006 355 -1984 315 -2018 2033 -312 2015 -284 327 -2000 329 -2000 2063 -282 2049 -284 327 -1984 319 -2018 2035 -284 2057 -264 343 -2004 289 -2032 2059 -280 2019 -316 323 -1984 321 -15968 387 -1978 321 -1984 2069 -276 2049 -288 325 -1996 303 -2004 2055 -284 2049 -278 325 -2018 321 -1984 2075 -276 2049 -288 327 -1990 325 -1996 2031 -318 2025 -278 323 -2018 289 -16010 357 -1982 315 -2018 2035 -278 2053 -288 307 -2022 321 -2000 2049 -274 2045 -284 327 -2022 311 -2014 2033 -276 2053 -286 327 -2000 323 -2000 2061 -282 2015 -316 325 -2002 289 -15994 353 -1980 357 -1990 2035 -312 2015 -286 327 -2020 289 -2022 2033 -318 2019 -278 323 -2004 319 -2016 2059 -282 2023 -316 327 -1970 321 -2018 2059 -280 2019 -318 325 -1972 321 -15988 353 -1982 357 -1972 2071 -276 2051 -288 327 -1988 323 -1996 2061 -280 2053 -284 325 -1984 321 -2014 2031 -282 2059 -276 321 -2018 289 -2036 2031 -282 2059 -276 321 -2018 319 -15984 341 -1984 343 -2016 2029 -298 2033 -258 309 -2028 321 -2032 2035 -284 2017 -298 337 -2008 289 -2034 2027 -314 2023 -310 285 -2016 321 -2014 2035 -282 2027 -308 317 -2026 297 -15982 357 -1984 345 -1982 2067 -264 2057 -258 341 -1996 317 -2026 2057 -282 2017 -318 325 -1972 319 -2018 2059 -280 2029 -302 315 -2006 319 -2006 2059 -274 2047 -276 307 +RAW_Data: -2024 311 -15984 349 -2004 317 -2012 2029 -276 2047 -312 305 -2018 311 -2018 2037 -258 2077 -258 337 -1998 321 -2026 2031 -278 2043 -284 327 -2020 311 -2018 2031 -278 2051 -288 325 -2004 323 -15962 353 -2018 317 -2014 2007 -308 2019 -320 287 -2024 325 -1998 2035 -314 2025 -302 311 -2004 319 -2010 2027 -312 2011 -310 309 -2028 303 -2018 2027 -304 2007 -294 309 -2024 323 -16002 361 -1946 347 -2008 2027 -286 2045 -312 325 -1978 319 -2024 2043 -276 2049 -286 325 -1988 341 -1978 2061 -278 2049 -284 327 -1990 321 -2020 2029 -318 2021 -278 323 -2008 321 -15990 355 -1982 355 -1984 2031 -284 2049 -278 325 -2018 321 -2000 2047 -276 2049 -286 327 -1994 341 -1976 2067 -274 2043 -284 303 -2018 341 -1982 2061 -266 2069 -258 339 -1998 321 -16002 311 -2014 329 -2012 2027 -318 1999 -298 339 -2008 287 -2042 2031 -282 2027 -306 317 -2012 291 -2044 2037 -284 2033 -302 315 -2004 321 -2012 2037 -282 2027 -306 317 -2028 297 -15980 345 -2018 285 -2026 2037 -320 2017 -278 323 -2024 299 -2002 2069 -278 2055 -286 325 -2002 289 -2024 2037 -282 2063 -276 321 -2016 287 -2036 2025 -320 2023 -278 323 -2018 287 -16006 355 -1982 355 -1986 2033 -302 2039 -282 307 -2032 309 -2014 2043 -264 2057 -258 341 -1992 319 -2036 2033 -284 2029 -306 319 -2000 319 -2014 2035 -282 2061 -266 343 -2006 289 -16018 331 -2010 313 -2014 2021 -320 2017 -278 323 -2010 321 -2016 2037 -284 2033 -304 317 -2018 287 -2036 2037 -284 2025 -296 339 -1978 321 -2028 2057 -282 2021 -278 359 -1972 321 -15990 355 -1978 355 -1990 2035 -300 2015 -312 305 -2016 315 -2016 2053 -290 2013 -316 277 -2020 353 -1980 2059 -256 2077 -276 307 -2020 311 -2012 2047 -258 2073 -256 341 -1998 321 -15976 369 -1976 313 -2018 2051 -280 2043 -290 317 -2004 321 -2024 2023 -282 2053 -282 327 -2020 313 -2020 2035 -278 2055 -284 325 -1986 321 -2016 2033 -280 2049 -282 325 -2020 313 -15986 315 -2026 319 -2028 2003 -318 2013 -294 339 -2008 289 -2032 2027 -316 2019 -302 311 -2004 321 -2014 2033 -314 1995 -308 317 -2028 297 -2032 2035 -276 2023 -316 321 -2014 289 -15998 347 -2014 283 -2020 2035 -292 2031 -288 311 -2024 321 -2036 2001 -310 2027 -318 275 -2054 277 -2026 2029 -296 2027 -292 311 -2024 321 -2030 2031 -282 2033 -304 313 -2004 321 -15992 329 -2010 349 -1980 2037 -292 2051 -256 339 -1998 319 -2034 2029 -286 2055 -264 339 -1978 321 -2030 2027 -280 2043 -316 327 -1984 315 -2016 2063 -278 2041 -288 327 -1990 325 -15964 389 -1978 319 -1984 2063 -256 2055 -292 307 -2016 323 -2028 2027 -286 2057 -274 319 -2010 319 -2010 2033 -282 2061 -276 321 -2018 289 -2032 2027 -318 2021 -278 321 -2020 319 -15974 355 -1980 357 -2000 2035 +RAW_Data: -254 2079 -276 309 -2016 309 -2012 2035 -296 2063 -258 337 -1992 321 -2030 2037 -286 2027 -294 333 -1978 321 -2034 2029 -286 2031 -306 315 -2008 321 -15972 355 -1984 315 -2014 2037 -310 2011 -318 323 -1984 329 -2002 2033 -316 2029 -278 323 -2026 299 -2030 2035 -278 2019 -316 325 -1980 323 -2018 2053 -252 2075 -280 327 -1984 355 -15954 345 -2008 309 -2016 2045 -266 2065 -258 341 -235740 101 -202 65 -734 133 -372 401 -68 269 -236 505 -68 235 -234 875 -68 13969 -100 14297 -70 3863 -96 59337 -104 11859 -68 17409 -68 7317 -66 11443 -64 15589 -66 4381 -98 32297 -168 45445 -100 59295 -100 41417 -66 1539 -66 23001 +RAW_Data: 1549 -1166 409 -1138 363 -1144 407 -1144 385 -408 1599 -1138 377 -430 1593 -1138 421 -1110 393 -1126 409 -1136 377 -1162 353 -24626 823 -1176 379 -1150 353 -1174 349 -1158 377 -454 1541 -450 1561 -1190 383 -1118 411 -398 1557 -460 1571 -450 1547 -482 1537 -450 1561 -482 1547 -470 1537 -450 1547 -482 1539 -1176 387 -422 1541 -474 1537 -448 1545 -444 1539 -450 1543 -1136 375 -414 1549 -1142 417 -1102 387 -1118 385 -1140 381 -406 1549 -1180 383 -392 1549 -1140 417 -1122 383 -1116 383 -1140 415 -1102 355 -24628 823 -1188 357 -1180 343 -1172 385 -1150 361 -440 1513 -450 1563 -1184 369 -1158 381 -448 1539 -448 1527 -482 1545 -456 1545 -450 1533 -454 1579 -450 1561 -458 1513 -450 1555 -1182 389 -390 1571 -436 1571 -418 1581 -446 1505 -456 1511 -1160 391 -390 1339 -1108 381 -1098 361 -1150 309 -1124 331 -416 1065 -1142 353 -420 1049 -1148 319 -1156 345 -1128 347 -1134 317 -1138 319 -24716 687 -1178 309 -1138 317 -1166 319 -1162 327 -408 1039 -448 1067 -1152 321 -1132 313 -436 1065 -426 1039 -442 1039 -448 1049 -418 1041 -450 1035 -426 1039 -444 1041 -418 1047 -1144 339 -402 1069 -414 1071 -422 1051 -450 1029 -424 1069 -1144 335 -418 1043 -1178 319 -1134 321 -1174 309 -1164 313 -454 1049 -1148 317 -410 1051 -1160 353 -1124 321 -1166 349 -1122 329 -1178 277 -24734 677 -1178 299 -1182 309 -1154 337 -1144 307 -430 1047 -448 1039 -1174 319 -1164 289 -452 1033 -444 1031 -450 1035 -426 1063 -412 1047 -448 1041 -446 1033 -426 1041 -438 1049 -1152 289 -438 1071 -414 1031 -448 1039 -460 1033 -414 1079 -1146 309 -428 1049 -1178 311 -1154 337 -1144 329 -1162 319 -408 1051 -1160 353 -404 1059 -1164 317 -1154 325 -1140 347 -1152 333 -1138 285 -24770 657 -1178 315 -1162 289 -1184 313 -1152 329 -446 1039 -416 1047 -1170 343 -1128 351 -418 1021 -454 1047 -416 1061 -422 1033 -454 1043 -446 1039 -416 1065 -414 1041 -424 1033 -1178 301 -450 1041 -416 1051 -452 1045 -422 1033 -454 1043 -1172 283 -436 1065 -1146 329 -1176 311 -1150 337 -1148 309 -430 1049 -1174 299 -432 1061 -1180 289 -1178 311 -1162 347 -1150 307 -1160 311 -24722 607 -1296 203 -1264 207 -1266 227 -1252 239 -498 969 -506 1013 -1214 275 -1166 289 -480 1005 -484 1003 -476 1009 -454 1009 -480 1025 -458 1003 -448 1045 -448 1009 -450 1035 -1146 321 -444 1033 -444 1029 -444 1041 -442 1041 -416 1081 -1148 343 -396 1081 -1146 345 -1120 341 -1150 345 -1120 343 -420 1077 -1146 321 -420 1053 -1152 355 -1122 321 -1162 349 -1150 297 -1178 311 -24720 523 -4340 133 -1370 99 -636 67 -1406 393 -98 397 -1320 207 -1260 225 -498 975 -522 937 -540 977 -488 973 -510 987 -490 1005 -456 1003 -476 +RAW_Data: 1015 -450 1005 -1194 289 -454 1011 -478 1009 -478 1007 -450 1033 -446 1035 -1158 353 -404 1057 -1168 309 -1162 349 -1150 309 -1158 313 -452 1047 -1146 321 -420 1059 -1152 355 -1126 319 -1160 345 -1128 353 -1152 309 -78850 165 -1196 97 -3256 65 -626 165 -130 525 -66 625 -430 891 -492 163 -792 163 -66 197 -100 595 -132 229 -15220 165 -464 97 -66 197 -264 99 -998 67 -198 195 -132 65 -296 163 -198 65 -198 691 -66 985 -134 97 -66 1485 -15420 197 -200 63 -132 97 -526 231 -64 263 -754 425 -198 97 -166 97 -132 495 -100 555 -164 391 -98 261 -98 1221 -9074 97 -2604 65 -1942 195 -590 853 -132 1225 -66 987 -1058 97 -100 131 -132 593 -98 425 -66 3965 -11360 65 -730 299 -98 99 -66 133 -232 327 -132 65 -456 163 -132 97 -196 327 -364 561 -264 1851 -234 1191 -8710 65 -596 163 -134 99 -234 97 -168 131 -496 165 -202 65 -20716 231 -68 231 -134 363 -100 133 -132 133 -198 231 -168 131 -166 99 -162 131 -196 295 -66 261 -166 955 -98 695 -66 1215 -9510 65 -3698 165 -328 65 -492 131 -66 129 -692 231 -64 163 -132 163 -98 229 -132 131 -134 97 -100 563 -15732 197 -796 165 -132 99 -562 97 -168 295 -462 99 -66 131 -332 133 -100 299 -66 329 -132 301 -68 267 -12994 299 -98 197 -228 295 -922 63 -988 65 -100 129 -164 823 -98 931 -66 331 -98 955 -9454 231 -100 163 -134 99 -796 133 -628 263 -430 67 -364 399 -98 365 -66 889 -66 5041 -4044 131 -166 265 -298 231 -98 197 -232 197 -164 163 -198 197 -98 65 -132 3525 -15068 67 -698 133 -1056 199 -2322 959 -200 297 -232 1223 -13532 65 -1650 197 -198 65 -330 129 -100 295 -164 65 -66 131 -1290 99 -134 231 -262 889 -98 231 -64 489 -66 663 -66 563 -15458 97 -362 229 -98 165 -496 131 -266 65 -98 165 -832 729 -66 133 -18682 231 -298 263 -132 363 -132 195 -132 361 -232 197 -1480 131 -164 163 -12854 65 -2522 299 -166 1357 -132 99 -98 399 -166 329 -264 395 -64 195 -196 1055 -132 1417 -14994 197 -132 459 -824 131 -428 133 -832 231 -200 65 -432 231 -100 467 -66 1151 -100 1741 -8616 97 -6904 1085 -198 261 -196 261 -232 265 -132 563 -166 65 -166 197 -100 331 -134 2009 -8664 97 -164 65 -64 263 -132 357 -64 97 -166 493 -166 165 -98 195 -130 361 -854 891 -66 365 -166 1019 -15320 97 -198 331 -166 1359 -266 229 -134 65 -100 331 -98 65 -132 265 -20774 65 -2222 97 -66 229 -132 161 -162 133 -18134 229 -198 65 -132 197 -200 263 -364 97 -100 427 -526 65 -460 131 -428 +RAW_Data: 655 -98 2625 -8562 99 -432 97 -1924 67 -632 199 -498 65 -100 1819 -132 197 -228 263 -232 133 -296 229 -66 97 -20434 199 -432 65 -764 65 -232 233 -232 133 -334 463 -232 329 -98 357 -130 131 -132 1423 -4018 133 -1984 65 -2926 99 -930 97 -430 1293 -100 531 -66 493 -100 131 -100 429 -134 465 -132 3063 -9636 67 -3696 97 -132 229 -298 131 -694 627 -132 1247 -132 297 -166 133 -66 199 -166 663 -21440 133 -400 131 -130 99 -66 531 -232 229 -134 131 -202 165 -564 131 -1258 65 -100 133 -132 299 -166 1325 -66 833 -66 1521 -9032 97 -1544 295 -98 231 -164 261 -66 131 -394 361 -296 163 -298 463 -66 195 -96 721 -13330 65 -468 99 -134 65 -4696 199 -166 659 -98 361 -198 229 -132 557 -166 625 -164 229 -66 329 -100 131 -130 263 -66 1221 -8436 329 -198 99 -66 99 -66 165 -398 65 -1326 97 -794 165 -592 131 -66 265 -266 99 -430 2421 -100 465 -100 199 -66 699 -100 65 -132 1351 -66 897 -130 1653 -15200 231 -264 195 -296 99 -328 295 -296 163 -98 129 -98 295 -264 131 -398 65 -232 97 -98 887 -132 3157 -12396 199 -134 131 -66 231 -200 267 -132 265 -500 97 -732 131 -200 165 -396 763 -166 859 -66 1391 -9164 65 -3808 165 -66 131 -1692 65 -100 1017 -330 65 -132 65 -196 685 -198 65 -198 165 -98 231 -68 99 -22854 231 -168 67 -200 65 -66 263 -100 201 -302 65 -134 65 -22948 165 -396 263 -134 131 -68 165 -862 231 -1494 1261 -66 399 -302 3089 -6572 65 -396 65 -4140 7317 -5010 7415 -15982 811 -1174 381 -1122 381 -1148 351 -1164 345 -466 1509 -482 1541 -1176 387 -1144 363 -416 1519 -478 1525 -470 1533 -460 1539 -444 1535 -460 1531 -476 1527 -448 1513 -482 1509 -1178 379 -398 1549 -444 1539 -450 1547 -446 1541 -450 1545 -1142 381 -426 1529 -1172 381 -1140 387 -1114 395 -1130 379 -438 1531 -1172 381 -398 1555 -1192 351 -1146 407 -1122 381 -1146 381 -1126 349 -24662 821 -1174 379 -1122 383 -1146 415 -1102 385 -426 1567 -432 1567 -1178 381 -1120 385 -444 1511 -482 1515 -480 1539 -452 1545 -446 1545 -486 1541 -442 1555 -456 1495 -466 1531 -1148 387 -422 1509 -466 1533 -456 1539 -470 1503 -458 1539 -1164 351 -424 1509 -1190 353 -1148 369 -1130 379 -1136 317 -404 1331 -1128 319 -406 1231 -1140 337 -1114 349 -1122 329 -1140 349 -1120 293 -24712 627 -1236 237 -1226 233 -1230 271 -1228 239 -480 1017 -452 1039 -1184 293 -1172 275 -464 1041 -444 1007 -450 1033 -448 1035 -430 1047 -450 1031 -428 1047 -444 1005 -466 1015 -1158 289 -460 1047 -430 1033 -440 1039 -442 1033 -442 1037 -1168 309 -436 1051 -1140 +RAW_Data: 343 -1158 311 -1152 347 -1120 341 -420 1041 -1178 319 -422 1051 -1150 321 -1152 345 -1126 351 -1118 345 -1158 313 -24714 689 -1170 317 -1154 323 -1138 349 -1154 301 -448 1037 -416 1083 -1150 345 -1124 313 -454 1047 -418 1031 -446 1045 -448 1037 -416 1061 -412 1043 -442 1053 -426 1039 -436 1033 -1170 309 -418 1039 -454 1049 -418 1059 -424 1049 -452 1029 -1158 319 -454 1031 -1152 353 -1152 289 -1176 311 -1162 349 -418 1051 -1148 319 -454 1027 -1152 353 -1124 319 -1162 347 -1152 333 -1144 311 -24722 685 -1138 343 -1138 329 -1162 353 -1130 325 -412 1069 -414 1085 -1150 311 -1160 311 -422 1081 -416 1031 -444 1049 -414 1071 -416 1063 -430 1037 -440 1039 -412 1049 -450 1039 -1140 351 -420 1049 -418 1065 -420 1045 -448 1041 -414 1053 -1148 345 -398 1085 -1140 315 -1166 355 -1126 319 -1158 351 -398 1071 -1140 317 -432 1071 -1138 343 -1130 339 -1144 331 -1160 319 -1162 289 -24740 693 -1130 353 -1156 319 -1136 347 -1150 301 -448 1041 -416 1063 -1178 297 -1180 311 -428 1047 -448 1037 -416 1065 -428 1037 -442 1051 -428 1039 -440 1051 -428 1037 -434 1033 -1172 311 -414 1071 -420 1049 -428 1035 -440 1069 -414 1051 -1150 343 -398 1083 -1148 345 -1120 341 -1148 347 -1122 341 -416 1071 -1148 319 -420 1061 -1152 353 -1126 321 -1158 349 -1150 297 -1176 277 -24762 631 -1238 239 -1232 271 -1230 243 -1210 271 -470 1007 -476 993 -1222 281 -1202 269 -480 1011 -454 1035 -446 1039 -446 1041 -416 1047 -450 1045 -420 1033 -454 1015 -444 1051 -1154 321 -408 1071 -414 1065 -398 1065 -440 1049 -428 1037 -1172 317 -434 1069 -1138 317 -1152 327 -1170 315 -1164 319 -410 1081 -1162 319 -404 1061 -1166 317 -1154 325 -1140 347 -1152 333 -1148 311 -24742 691 -1138 341 -1138 317 -1164 353 -1124 319 -434 1069 -412 1069 -1148 325 -1140 347 -398 1075 -414 1069 -414 1051 -452 1047 -422 1051 -416 1057 -428 1049 -450 1029 -420 1033 -1166 335 -418 1041 -416 1063 -412 1079 -412 1071 -414 1065 -1148 325 -410 1069 -1142 351 -1152 309 -1160 349 -1116 329 -436 1051 -1162 319 -426 1051 -1148 327 -1172 311 -1156 337 -1150 345 -1120 305 -83468 65 -1920 133 -98 397 -1062 199 -464 165 -728 165 -168 97 -466 497 -132 1123 -66 3163 -12612 229 -198 65 -134 363 -66 67 -132 97 -896 197 -132 199 -166 165 -166 67 -134 165 -334 365 -134 2963 -15534 231 -132 231 -168 165 -262 97 -266 65 -724 65 -166 97 -198 295 -98 131 -132 563 -100 1483 -8892 99 -954 131 -234 67 -432 1087 -98 687 -132 163 -8794 165 -6460 165 -332 331 -498 99 -200 199 -266 67 -100 131 -596 165 -332 67 -98 299 -100 265 -68 633 -100 5793 -4102 99 -198 65 -566 +RAW_Data: 65 -658 99 -132 165 -332 167 -198 99 -132 133 -2530 65 -166 795 -300 197 -366 227 -262 361 -66 2073 -20762 65 -598 231 -264 97 -592 327 -132 295 -132 297 -100 363 -68 763 -20474 63 -166 757 -200 391 -100 97 -134 131 -134 591 -98 261 -262 229 -64 195 -100 195 -100 65 -132 261 -100 919 -66 333 -13642 65 -2324 131 -130 193 -462 195 -930 865 -66 2157 -100 1923 -12176 65 -168 197 -196 131 -1250 65 -132 391 -232 659 -66 393 -100 459 -230 197 -296 3479 -12728 99 -166 131 -134 131 -366 131 -98 97 -198 263 -164 231 -2074 693 -66 597 -66 433 -98 2509 -15286 65 -198 65 -198 165 -200 461 -132 757 -16060 65 -6768 199 -98 131 -262 163 -130 197 -198 165 -398 233 -334 65 -132 131 -166 331 -134 231 -564 365 -66 265 -100 465 -66 433 -100 6915 -6612 99 -264 65 -230 63 -4800 97 -66 393 -134 65 -100 165 -100 265 -66 165 -1818 99 -230 331 -66 265 -13068 131 -100 295 -298 427 -132 131 -1530 65 -100 165 -500 165 -132 995 -68 1685 -15810 263 -130 163 -462 231 -100 233 -632 165 -528 327 -196 197 -198 1253 -100 3583 -6278 97 -788 99 -3200 65 -166 133 -438 99 -132 165 -300 329 -166 1723 -134 725 -164 133 -16880 97 -960 99 -696 65 -200 231 -132 299 -66 297 -164 131 -730 197 -23052 365 -66 99 -332 131 -166 297 -494 429 -1326 265 -132 295 -66 367 -232 263 -66 857 -15396 395 -130 229 -98 233 -132 331 -364 65 -100 529 -68 231 -830 297 -100 233 -66 8533 -5582 101 -168 67 -3968 231 -66 129 -132 163 -1154 97 -166 199 -166 233 -132 467 -134 263 -66 431 -66 363 -66 957 -100 1821 -132 497 -132 1159 -15358 397 -66 99 -98 65 -66 265 -564 65 -68 97 -166 99 -100 165 -134 297 -896 165 -330 97 -134 963 -132 4737 -7096 65 -5664 265 -266 97 -166 265 -598 65 -332 65 -66 65 -198 231 -132 329 -100 65 -17202 99 -1564 595 -98 329 -198 227 -66 459 -230 97 -1480 63 -66 131 -166 99 -166 931 -8662 133 -398 99 -134 265 -98 299 -264 233 -66 99 -100 99 -66 97 -166 199 -166 265 -234 5785 -10012 163 -2324 331 -196 331 -134 65 -100 297 -100 165 -100 131 -332 163 -302 297 -164 199 -300 199 -166 229 -68 99 -68 959 -15384 365 -134 263 -1818 229 -698 99 -232 131 -100 789 -66 491 -132 4039 -12084 99 -134 331 -796 265 -132 265 -362 167 -2310 65 -98 131 -100 525 -164 295 -15032 297 -560 197 -330 131 -196 397 -266 197 -200 263 -68 261 -496 329 -166 361 -166 1517 -66 331 -10126 97 -3736 99 -1626 131 -100 +RAW_Data: 3487 -68 18413 -66 6689 -17938 65 -68 495 -98 1129 -860 697 -166 395 -100 463 -100 163 -14692 263 -794 65 -64 99 -2922 131 -200 97 -168 99 -100 199 -962 495 -68 165 -98 299 -198 133 -168 3917 -98 963 -132 461 -100 1089 -166 331 -134 633 -164 201 -100 363 -164 335 -200 265 -68 531 -166 699 -132 529 -166 397 -98 895 -168 65 -100 399 -366 463 -66 197 -134 431 -66 163 -98 623 -166 301 -98 263 -98 263 -66 197 -98 329 -98 525 -66 331 -200 1025 -66 629 -132 763 -166 233 -66 431 -132 133 -100 429 -264 165 -132 299 -166 429 -68 4605 -134 593 -134 4917 -60834 167 -10282 1941 -1122 865 -2086 921 -1072 1921 -1042 939 -1056 945 -1044 939 -1052 945 -1024 971 -1024 973 -1002 969 -1010 989 -1996 1007 -994 979 -1014 1965 -1014 977 -1020 969 -2004 1993 -982 1011 -982 1005 -984 977 -2008 981 -1006 1993 -1966 999 -994 1007 -982 1005 -984 1007 -996 975 -1016 1987 -996 973 -1016 969 -1004 1013 -1970 1011 -984 1999 -1970 1995 -984 1021 -1968 1005 -994 1003 -970 999 -984 1023 -974 1015 -8920 1995 -1014 1001 -1966 1013 -986 1991 -986 1007 -996 973 -1012 999 -978 985 -1006 1005 -984 997 -978 1015 -1000 973 -1982 1025 -982 985 -1008 1973 -1004 1007 -982 983 -1998 2009 -972 1009 -980 999 -978 1011 -1970 1011 -986 1999 -1978 1007 -988 1007 -994 975 -1012 1003 -972 1009 -978 1987 -986 1007 -982 1017 -994 971 -1990 1009 -984 1997 -1978 1999 -1010 993 -1964 1005 -1000 1007 -980 997 -984 1017 -972 999 -8932 2031 -980 989 -1988 985 -1016 1983 -986 1007 -994 975 -1016 975 -998 977 -1012 999 -982 985 -1006 983 -1018 993 -1968 1017 -996 975 -1016 1965 -1012 979 -1018 971 -2004 1991 -978 1003 -984 1015 -992 973 -2004 1005 -976 1991 -2008 975 -986 1005 -998 977 -1012 999 -978 985 -1010 1973 -1004 1013 -980 981 -1004 1007 -1996 975 -986 1993 -2006 1961 -1002 1013 -1974 985 -1008 1011 -978 997 -1006 981 -1012 991 -8918 8005 -3924 1887 -174620 131 -3974 131 -298 559 -230 65 -132 265 -98 863 -168 333 -66 299 -234 463 -166 331 -102 697 -200 199 -98 265 -100 663 -166 331 -66 599 -132 99 -66 1261 -66 399 -100 265 -100 1199 -66 265 -166 101 -66 599 -232 197 -100 561 -66 499 -132 797 -132 427 -66 265 -298 465 -66 565 -198 97 -100 695 -100 531 -132 267 -66 429 -98 231 -100 331 -100 333 -98 363 -198 67 -100 165 -100 231 -100 729 -134 1061 -100 493 -164 665 -132 259 -128 257 -130 329 -98 361 -66 263 -100 227 -66 491 -98 295 -166 229 -132 229 -130 263 -130 233 -100 331 -232 99 -100 165 -66 667 -164 795 -66 695 -68 429 -132 +RAW_Data: 559 -66 16863 -66 19215 -12276 65 -2086 131 -2188 65 -132 67 -166 299 -364 65 -100 131 -468 233 -200 497 -498 297 -100 363 -198 329 -130 327 -164 229 -64 427 -164 657 -132 829 -532 895 -66 297 -130 425 -166 363 -132 329 -100 465 -100 465 -198 65 -262 429 -98 495 -100 295 -528 131 -132 1129 -134 259 -130 327 -100 397 -66 261 -230 99 -66 229 -132 625 -68 131 -100 587 -66 329 -64 293 -130 1741 -132 325 -132 197 -98 261 -98 99 -132 561 -166 165 -66 391 -66 329 -98 231 -132 465 -100 331 -300 199 -132 299 -132 165 -164 229 -98 565 -100 395 -132 131 -100 327 -130 97 -98 163 -132 293 -98 557 -66 199 -166 233 -132 265 -166 627 -100 265 -132 595 -166 99 -98 199 -100 267 -166 795 -166 265 -166 297 -66 131 -166 331 -134 793 -164 263 -198 299 -100 265 -166 257 -100 197 -198 599 -132 333 -66 265 -100 229 -100 165 -204 465 -166 97 -232 365 -166 431 -68 199 -166 99 -236 365 -132 1297 -66 1361 -100 989 -98 297 -132 12467 -232 361 -100 263 -132 261 -196 229 -296 65 -66 329 -164 229 -164 263 -66 563 -232 3415 -132 11115 -100 97 -100 363 -164 99 -66 199 -100 99 -66 561 -164 259 -168 233 -132 333 -166 567 -98 297 -66 399 -66 399 -298 327 -132 685 -98 393 -98 819 -100 231 -270 429 -68 461 -132 131 -134 1121 -230 787 -132 1393 -132 233 -100 331 -66 497 -166 563 -196 425 -98 525 -66 723 -98 265 -134 365 -66 1297 -166 433 -98 165 -134 231 -134 263 -100 369 -198 99 -100 631 -634 331 -132 565 -132 265 -102 267 -98 467 -200 297 -130 731 -100 367 -98 229 -130 99 -98 263 -164 493 -100 297 -198 429 -66 425 -66 1021 -164 295 -198 97 -198 263 -132 425 -98 493 -98 197 -98 593 -264 65 -294 197 -66 295 -66 561 -196 565 -166 2675 -132 19393 -134 5403 -164 369 -166 467 -132 361 -526 395 -66 361 -132 529 -66 431 -66 465 -166 363 -98 65 -98 229 -98 359 -100 197 -132 201 -100 467 -166 397 -100 297 -130 229 -98 361 -98 199 -130 297 -66 659 -132 261 -98 819 -98 393 -132 329 -132 557 -132 163 -130 657 -66 295 -134 1189 -100 399 -102 861 -100 369 -132 331 -134 263 -666 65 -134 427 -64 197 -98 559 -68 297 -98 493 -164 197 -164 525 -66 131 -98 493 -66 595 -100 233 -100 265 -132 65 -66 465 -266 527 -98 855 -132 493 -66 393 -298 331 -100 131 -66 859 -198 495 -100 265 -98 367 -66 727 -196 535 -66 263 -66 397 -332 65 -198 331 -134 297 -330 +RAW_Data: 261 -100 3291 -68 2553 -19050 1855 -100 1259 -200 165 -100 39479 -100 233 -68 1027 -166 595 -66 231 -168 199 -98 265 -232 563 -166 795 -98 99 -460 65 -362 1017 -98 229 -98 625 -66 231 -98 161 -196 363 -98 363 -100 631 -132 297 -66 333 -300 99 -66 295 -230 985 -100 623 -132 1319 -100 65 -68 231 -232 197 -232 331 -100 199 -168 567 -166 133 -232 823 -396 327 -132 855 -232 165 -100 401 -166 599 -198 167 -132 299 -198 663 -132 165 -134 67 -66 1687 -66 1705 -100 265 -232 297 -100 531 -66 333 -100 1263 -66 297 -164 299 -98 431 -398 97 -66 199 -296 231 -232 925 -100 131 -132 229 -164 361 -66 267 -166 197 -100 491 -132 261 -132 1183 -98 891 -100 265 -100 99 -100 725 -100 303 -60886 131 -10310 1911 -1226 749 -2146 899 -1084 1905 -1070 917 -1044 973 -1042 927 -1050 949 -1044 965 -1012 949 -1046 965 -1014 977 -2008 981 -980 995 -1012 1969 -1018 971 -1014 975 -2014 1967 -1016 971 -1014 977 -984 1003 -2006 973 -1000 2001 -1978 1013 -980 1001 -972 1009 -978 991 -1010 983 -1004 1989 -1012 983 -978 995 -1014 983 -2002 977 -984 2023 -1968 1997 -986 999 -1970 1003 -1000 1009 -978 1017 -974 1009 -978 1001 -8948 1975 -1016 973 -2004 999 -978 2001 -986 1001 -978 1009 -986 1001 -1010 975 -998 1011 -992 975 -982 1005 -1000 1009 -1970 1011 -982 1009 -984 2003 -978 1001 -976 1015 -1966 1999 -1010 989 -982 989 -1004 1001 -1978 1013 -978 2005 -1966 1019 -972 1011 -980 997 -976 1011 -1004 999 -966 1997 -1000 999 -1002 975 -986 999 -1994 1007 -980 1993 -1978 2001 -1008 991 -1970 1009 -998 975 -1016 971 -1006 1013 -980 1001 -8922 2001 -994 1009 -1972 997 -1012 1971 -984 1005 -1012 977 -998 1009 -978 1011 -984 977 -1012 979 -1016 977 -1010 977 -2010 981 -1006 971 -1020 1985 -984 1005 -978 999 -1990 1997 -980 997 -1010 981 -1014 989 -1976 1005 -966 1995 -1998 997 -982 1021 -972 1003 -986 997 -1012 975 -982 1995 -1014 999 -980 983 -1004 1003 -1976 1007 -980 1983 -2012 1983 -998 977 -2010 973 -1014 973 -1018 973 -1014 975 -1016 971 -8956 7999 -3926 1865 -177334 165 -166 199 -496 431 -66 1319 -132 297 -164 457 -68 231 -100 863 -98 603 -100 663 -100 789 -262 491 -166 729 -100 795 -364 365 -132 293 -100 197 -98 625 -200 231 -132 229 -132 227 -100 755 -526 329 -134 793 -100 167 -200 335 -66 65 -134 265 -66 201 -66 1023 -100 63 -21396 133 -198 65 -66 263 -98 393 -134 65 -66 329 -68 199 -432 263 -394 131 -130 195 -66 687 -66 295 -164 229 -196 97 -98 559 -98 1283 -98 531 -66 331 -68 435 -100 393 -628 431 -132 99 -66 +RAW_Data: 329 -66 2519 -100 6759 -12690 463 -232 97 -3252 65 -2592 65 -132 99 -198 67 -496 99 -300 231 -132 233 -66 165 -132 131 -164 201 -66 4113 -66 1987 -68 15385 -98 8199 -16614 165 -1258 495 -1354 229 -166 297 -134 3653 -100 12533 -66 3759 -162 591 -132 361 -130 527 -100 327 -200 65 -66 461 -132 327 -132 461 -100 559 -198 263 -132 161 -98 329 -132 327 -68 395 -66 165 -132 163 -196 261 -66 361 -98 229 -132 301 -134 761 -66 399 -166 99 -66 461 -98 857 -98 131 -262 359 -132 199 -100 299 -166 363 -132 297 -132 199 -132 265 -166 133 -100 301 -268 463 -98 231 -134 265 -98 527 -298 265 -136 531 -132 363 -132 129 -130 527 -132 297 -66 297 -68 397 -466 99 -132 697 -98 233 -464 131 -132 365 -134 465 -98 635 -98 299 -66 497 -132 197 -132 489 -166 327 -98 1151 -130 295 -132 623 -164 97 -68 297 -100 1293 -100 1759 -66 1259 -132 729 -100 797 -66 99 -66 231 -98 529 -166 331 -166 299 -202 431 -134 467 -462 329 -462 1051 -98 625 -66 195 -100 461 -196 429 -132 197 -66 129 -66 229 -166 589 -164 629 -66 1053 -166 231 -98 263 -132 329 -132 267 -132 427 -130 65 -130 229 -166 4121 -66 15899 -19980 495 -264 165 -432 65 -132 65 -662 663 -100 793 -66 723 -164 565 -100 363 -166 199 -98 465 -100 299 -168 265 -198 695 -132 131 -268 497 -66 265 -134 199 -98 231 -134 499 -100 989 -132 429 -196 327 -132 131 -164 427 -66 263 -66 525 -132 1091 -100 793 -132 491 -66 195 -526 921 -134 363 -98 693 -230 165 -98 1711 -132 293 -66 659 -196 231 -66 261 -66 163 -66 1349 -166 363 -100 65 -100 299 -100 393 -66 495 -134 229 -98 131 -98 463 -362 361 -132 231 -66 491 -396 361 -132 97 -66 163 -198 229 -294 229 -198 165 -164 65 -100 623 -66 195 -98 261 -130 65 -98 563 -66 267 -166 1057 -632 531 -132 463 -100 663 -166 133 -100 569 -164 197 -66 431 -164 261 -132 363 -100 427 -164 263 -198 623 -66 589 -166 133 -166 199 -66 199 -268 65 -98 427 -66 163 -132 563 -198 363 -166 397 -98 1025 -66 197 -66 163 -132 693 -164 195 -66 427 -100 131 -66 433 -132 199 -402 231 -66 335 -100 993 -100 463 -232 65 -100 65 -66 7165 -66 6785 -66 895 -66 923 -198 97 -164 689 -166 361 -198 99 -66 753 -498 365 -66 567 -232 397 -66 199 -132 233 -200 995 -296 365 -166 597 -100 199 -134 99 -166 197 -332 431 -166 331 -66 199 -100 431 -232 493 -166 457 -98 231 -132 427 -230 559 -98 +RAW_Data: 261 -132 1051 -66 7501 -98 22107 -19232 2271 -166 133 -60834 165 -10284 595 -5446 885 -1106 1891 -1088 897 -1068 949 -1050 929 -1040 947 -1048 959 -1008 985 -1008 973 -1012 993 -2004 979 -998 975 -1014 1987 -998 977 -1014 971 -1998 2007 -974 1013 -976 999 -982 1023 -1962 1005 -1000 1969 -2010 977 -1016 971 -1004 1009 -980 985 -1002 979 -1014 1977 -1012 979 -1016 971 -1012 977 -1986 1021 -974 2005 -1968 2007 -976 1011 -1966 1009 -984 1009 -984 985 -1020 969 -1002 997 -8952 1995 -986 1017 -1988 989 -976 1979 -1028 975 -988 1009 -986 1007 -992 973 -1014 1001 -974 1011 -978 995 -1006 979 -2004 975 -1022 973 -984 1999 -1014 977 -984 1003 -2004 1995 -976 1005 -998 999 -982 981 -2002 983 -986 1989 -1998 1009 -976 999 -1010 991 -978 1013 -968 1011 -986 1995 -982 1023 -974 981 -1020 993 -1970 1007 -996 1995 -1982 1999 -986 993 -2002 985 -998 975 -1014 969 -1004 1009 -978 997 -8962 1989 -990 1009 -1954 1025 -984 2007 -962 1011 -980 1001 -982 1009 -1002 1007 -980 1005 -976 1007 -980 997 -976 1009 -2002 977 -988 1005 -982 2001 -976 1009 -988 1005 -1968 2029 -974 1003 -990 1011 -958 1005 -1972 1039 -978 1999 -1978 1009 -954 1037 -978 1007 -954 1039 -980 1011 -958 1999 -974 1031 -990 993 -972 1011 -1980 1011 -978 2009 -1968 1993 -982 1003 -1974 1007 -1006 1001 -986 985 -986 1001 -982 1033 -8924 8013 -3924 1851 -170400 493 -132 131 -164 65 -724 461 -164 693 -100 265 -100 229 -100 199 -98 363 -232 363 -100 131 -68 531 -166 165 -134 265 -66 727 -300 131 -198 99 -132 99 -134 597 -102 295 -130 131 -230 129 -98 689 -264 263 -134 231 -134 199 -132 131 -68 295 -100 163 -130 591 -98 327 -164 231 -66 589 -98 2077 -134 1389 -66 2913 -66 3045 -66 4463 -98 391 -132 489 -164 295 -66 523 -166 329 -98 195 -98 165 -100 165 -66 231 -100 895 -98 265 -132 595 -132 297 -494 261 -198 331 -100 131 -134 429 -68 231 -298 1193 -66 99 -100 627 -132 331 -98 393 -98 657 -132 163 -392 359 -132 163 -64 655 -164 263 -64 531 -98 265 -164 499 -100 229 -64 459 -100 199 -66 165 -130 197 -132 393 -66 163 -228 229 -132 65 -132 263 -100 357 -98 991 -328 595 -132 197 -130 759 -66 131 -166 267 -100 1251 -64 361 -66 395 -954 559 -132 295 -98 327 -100 295 -100 367 -198 391 -298 363 -132 495 -132 523 -66 97 -132 763 -198 131 -66 197 -100 525 -130 1059 -64 461 -166 563 -134 331 -66 463 -134 329 -198 199 -166 265 -266 1061 -66 331 -366 65 -66 1225 -100 299 -66 299 -132 133 -298 265 -100 1129 -364 163 -460 297 -132 297 -100 299 -100 +RAW_Data: -465 9659 -100 885 -162 2507 -98 1805 -64 427 -98 1879 -198 489 -66 525 -98 851 -100 525 -66 819 -66 395 -98 459 -66 1155 -66 863 -68 265 -98 2359 -68 435 -166 2031 -66 829 -100 561 -66 701 -66 1365 -134 731 -132 655 -68 1623 -66 263 -98 1087 -98 591 -64 987 -132 299 -66 885 -66 1087 -66 2605 -100 303 -100 731 -132 1323 -66 267 -100 627 -66 431 -100 397 -100 1027 -66 559 -102 1565 -100 865 -66 629 -100 327 -66 1281 -98 851 -132 593 -132 429 -100 2931 -134 759 -298 2985 -132 1129 -66 1131 -166 235 -100 725 -66 199 -134 365 -64 293 -66 489 -130 15135 -132 899 -100 691 -100 1271 -134 1161 -66 1789 -100 293 -64 651 -130 719 -100 1061 -66 529 -66 501 -132 1523 -68 231 -68 895 -100 1557 -134 895 -66 2297 -68 4497 -134 1319 -64 525 -98 949 -66 463 -132 399 -134 1129 -66 263 -170 1027 -100 763 -98 265 -68 603 -66 563 -166 1221 -68 1253 -66 1163 -66 697 -134 699 -98 1929 -68 931 -100 399 -100 1589 -98 663 -98 261 -66 337 -100 497 -98 767 -66 471 -66 365 -66 1363 -68 967 -300 461 -66 879 -98 623 -98 623 -100 725 -98 1685 -66 1061 -264 1523 -66 665 -130 2239 -66 229 -130 1509 -66 763 -100 1301 -132 12207 -98 261 -98 3707 -66 323 -298 821 -66 623 -98 2467 -66 2177 -164 625 -100 629 -132 1263 -132 1027 -100 2093 -132 397 -98 1855 -66 2515 -64 391 -132 1163 -66 503 -68 499 -98 2383 -98 1143 -66 523 -66 821 -98 525 -98 163 -164 1775 -68 231 -134 2159 -100 3065 -66 401 -134 925 -200 165 -18904 99 -296 261 -1018 99 -198 235 -166 329 -298 133 -266 1877 -132 227 -198 797 -66 933 -100 1093 -98 197 -66 723 -98 293 -132 953 -66 1507 -132 261 -64 1183 -98 393 -66 461 -100 863 -66 463 -100 865 -66 2721 -200 11457 -66 4203 -100 969 -66 765 -234 229 -100 491 -66 433 -66 231 -134 1189 -100 1015 -96 1349 -66 687 -132 655 -100 365 -132 397 -132 761 -100 1393 -66 525 -100 397 -132 327 -68 495 -66 429 -68 533 -134 529 -66 565 -102 695 -102 1353 -66 793 -228 197 -100 801 -64 657 -98 1439 -98 393 -98 359 -130 2707 -100 1101 -166 1523 -100 961 -100 397 -66 1217 -66 531 -166 199 -102 365 -166 1863 -66 367 -66 361 -68 703 -100 763 -100 299 -66 501 -200 231 -100 2879 -98 2005 -66 825 -134 263 -66 1155 -132 929 -66 335 -100 13393 -100 4113 -266 99 -198 1357 -66 1015 -100 1627 -66 999 -132 1329 -132 529 -66 2591 -100 2545 -68 429 -100 499 -166 961 -66 467 -66 699 +RAW_Data: -100 44175 -12032 99 -400 99 -334 65 -490 165 -328 65 -298 367 -1488 131 -166 131 -264 65 -66 99 -558 197 -198 129 -164 263 -100 2897 -66 33999 -98 1313 -166 657 -132 261 -66 197 -98 1477 -98 229 -66 1319 -66 231 -68 1199 -66 763 -100 857 -134 1963 -18216 99 -500 165 -330 65 -360 163 -202 65 -100 99 -366 267 -402 163 -68 233 -164 65 -66 167 -396 365 -100 1527 -66 393 -132 757 -66 1759 -100 821 -98 233 -66 1197 -66 399 -130 495 -100 435 -100 365 -66 199 -66 6399 -66 3541 -132 369 -132 1361 -98 597 -132 729 -68 733 -66 693 -100 199 -68 565 -134 363 -66 917 -68 267 -134 757 -132 1415 -132 327 -98 1053 -68 897 -166 263 -296 793 -102 297 -98 391 -100 893 -100 233 -68 563 -100 729 -66 431 -66 403 -68 397 -66 601 -164 563 -66 861 -132 1793 -66 637 -134 297 -66 525 -66 1421 -66 265 -100 463 -66 835 -100 699 -100 1029 -132 265 -166 567 -164 2155 -66 263 -66 1945 -100 10073 -132 463 -100 799 -66 729 -100 1655 -66 233 -66 265 -134 1961 -132 733 -98 301 -134 367 -134 1033 -64 889 -166 1463 -100 1327 -100 573 -132 497 -100 903 -100 1951 -66 427 -132 895 -198 1387 -98 723 -66 363 -100 263 -134 263 -166 561 -300 363 -198 929 -132 999 -168 65 -134 867 -132 719 -64 6625 -100 3415 -66 1553 -132 757 -130 329 -66 1625 -66 199 -66 265 -100 365 -132 267 -66 235 -100 703 -100 431 -100 601 -68 565 -100 399 -100 365 -100 365 -98 633 -100 2067 -100 495 -98 1121 -66 1253 -100 433 -132 199 -132 567 -100 99 -98 829 -198 363 -98 1717 -66 301 -132 403 -66 1791 -66 401 -100 633 -98 697 -66 1127 -66 433 -66 965 -100 2485 -66 461 -66 899 -98 231 -100 763 -100 523 -198 361 -132 1911 -66 2307 -168 265 -100 659 -134 461 -66 1165 -68 1423 -66 2693 -66 499 -66 733 -134 893 -98 435 -134 2359 -132 197 -68 661 -100 231 -68 369 -100 231 -66 495 -132 1751 -68 233 -68 1765 -132 233 -130 491 -162 855 -98 2113 -100 333 -134 765 -132 529 -66 293 -66 987 -132 329 -64 293 -98 99 -98 361 -100 367 -166 267 -68 261 -100 997 -408 855 -456 845 -868 417 -882 397 -900 411 -416 869 -884 417 -844 441 -846 427 -456 843 -452 831 -876 409 -898 409 -430 837 -462 839 -462 847 -854 417 -876 437 -880 397 -886 385 -486 841 -874 411 -868 415 -892 383 -878 439 -418 847 -466 835 -882 427 -440 843 -870 417 -882 411 -878 435 -846 427 -886 385 -886 415 -880 427 -870 409 -454 837 -888 419 +RAW_Data: -448 837 -448 835 -880 413 -448 869 -414 885 -420 849 -450 843 -876 443 -440 841 -438 873 -862 413 -448 855 -876 413 -25804 2597 -422 879 -418 867 -884 419 -844 445 -876 399 -454 849 -886 417 -878 415 -872 419 -418 893 -410 865 -876 415 -876 437 -418 851 -450 875 -418 877 -874 405 -866 419 -884 413 -878 437 -418 883 -850 415 -882 425 -874 409 -884 419 -452 851 -418 885 -850 415 -452 859 -876 409 -898 407 -862 449 -844 449 -874 409 -872 435 -870 397 -882 425 -426 875 -868 413 -452 871 -418 881 -874 413 -448 839 -460 847 -424 879 -420 867 -890 417 -416 887 -418 885 -860 413 -450 869 -848 437 -25854 475 -3978 135 -398 65 -1128 225 -1058 225 -1032 299 -562 749 -994 307 -956 341 -946 369 -488 809 -494 803 -930 353 -932 383 -474 815 -510 815 -468 811 -928 377 -914 381 -914 407 -882 399 -454 839 -890 413 -900 409 -870 417 -880 425 -442 843 -476 841 -864 417 -454 867 -882 417 -876 411 -876 435 -846 429 -882 421 -886 419 -850 445 -872 411 -450 853 -878 419 -430 881 -418 867 -890 417 -416 889 -418 885 -432 871 -416 855 -878 451 -438 845 -458 843 -884 419 -446 837 -880 449 -25864 497 -9138 659 -1080 233 -1046 297 -976 295 -558 737 -578 763 -942 339 -972 341 -530 785 -492 807 -486 807 -916 397 -918 357 -922 387 -928 383 -482 801 -906 395 -898 407 -888 415 -882 411 -480 841 -448 839 -878 415 -448 871 -876 411 -880 437 -880 397 -884 423 -884 419 -882 413 -886 387 -912 397 -466 843 -866 413 -448 869 -456 837 -888 419 -450 837 -450 851 -450 871 -454 837 -888 419 -450 835 -450 871 -884 417 -448 837 -880 415 -172818 65 -1022 197 -1852 10287 -134 695 -132 263 -130 1717 -66 625 -100 1031 -68 301 -68 265 -100 665 -98 1059 -100 931 -66 1093 -132 333 -68 3011 -132 459 -66 659 -98 521 -98 1511 -98 163 -98 857 -132 231 -98 491 -66 587 -66 393 -98 1513 -98 263 -130 529 -100 299 -100 1545 -132 1313 -66 399 -68 299 -134 201 -68 265 -98 691 -132 1099 -66 427 -100 461 -264 427 -134 327 -100 227 -64 493 -66 633 -66 501 -66 2067 -228 595 -132 97 -66 231 -66 299 -66 925 -98 661 -100 433 -134 231 -166 999 -98 691 -66 197 -66 293 -66 265 -362 1557 -100 231 -134 265 -68 433 -66 2483 -66 333 -100 233 -166 917 -132 295 -132 949 -164 1775 -100 7629 -66 7259 -66 263 -202 1263 -100 265 -68 861 -166 365 -98 233 -164 569 -66 199 -100 399 -98 1189 -130 261 -100 655 -164 723 -264 231 -100 327 -130 395 +RAW_Data: -130 84629 -16530 199 -594 163 -562 65 -164 65 -230 1383 -100 1031 -66 427 -66 401 -68 265 -102 233 -134 923 -100 493 -66 555 -132 619 -66 3165 -66 463 -198 1025 -68 233 -100 957 -132 793 -134 1233 -100 1553 -98 431 -100 1429 -100 393 -164 259 -166 65 -100 297 -134 263 -68 1797 -66 887 -100 497 -100 565 -134 363 -96 1649 -130 393 -98 327 -100 563 -68 891 -68 24545 -68 231 -134 167 -100 131 -68 863 -102 627 -164 267 -166 631 -66 569 -100 797 -66 231 -166 301 -102 569 -134 1423 -66 1115 -66 759 -66 599 -68 1265 -268 527 -134 531 -98 269 -68 231 -66 325 -98 329 -66 399 -64 393 -98 459 -98 921 -66 457 -100 2541 -266 927 -68 231 -64 357 -98 495 -166 133 -100 529 -98 1091 -100 199 -66 597 -100 931 -66 663 -134 333 -166 763 -68 599 -100 333 -102 333 -232 363 -66 1491 -132 697 -134 629 -66 369 -66 561 -134 863 -98 895 -100 199 -98 763 -100 299 -168 561 -100 331 -100 467 -100 1231 -100 1821 -66 229 -66 325 -66 1419 -66 195 -66 553 -132 363 -164 857 -66 491 -66 227 -98 227 -98 529 -100 2093 -164 501 -132 1441 -98 1095 -100 263 -66 931 -66 1397 -66 1627 -98 433 -66 697 -200 363 -266 297 -100 301 -200 263 -98 565 -134 65 -102 297 -100 165 -100 929 -266 1325 -66 3213 -100 4403 -100 665 -134 1859 -66 631 -66 967 -200 165 -134 1551 -66 791 -100 331 -132 163 -64 163 -98 361 -132 557 -98 629 -66 885 -100 1087 -100 959 -100 535 -66 265 -66 1931 -66 465 -66 1165 -100 565 -100 865 -100 493 -66 597 -166 1293 -100 463 -68 563 -66 565 -66 595 -100 197 -68 929 -66 665 -134 399 -66 665 -100 1255 -166 935 -98 1791 -98 265 -136 501 -100 1581 -66 265 -66 567 -98 699 -134 +RAW_Data: 12477 16271 -64 12623 -102 40819 -98 3729 -66 14371 -66 14943 -64 5931 -66 11147 -68 74641 -102 54299 -70 18441 -66 82993 -100 66161 -68 61869 -66 6627 -66 12987 -68 30427 -68 25761 -98 6305 -66 5019 -64 30857 -132 23929 -68 25129 -39378 317 -2020 311 -2010 2033 -312 2017 -282 325 -2018 291 -2024 2035 -316 2023 -276 319 -2028 297 -2032 2029 -284 2023 -314 321 -2016 319 -2012 2003 -310 2015 -318 321 -1984 327 -16000 351 -1974 321 -2020 2041 -276 2047 -288 327 -1992 327 -2000 2055 -282 2053 -274 305 -2022 301 -2014 2049 -286 2055 -274 319 -1992 329 -2006 2065 -282 2023 -316 323 -1988 303 -16008 323 -2014 315 -1990 2053 -284 2061 -274 319 -1988 319 -2038 2033 -286 2055 -264 339 -2008 289 -2034 2035 -284 2061 -262 339 -2008 287 -2040 2037 -286 2035 -268 347 -2006 317 -15988 311 -2014 343 -2014 2027 -300 2031 -258 311 -2024 323 -2028 2023 -280 2051 -318 285 -2024 309 -2002 2039 -312 2017 -318 289 -2020 293 -2028 2035 -316 2023 -276 321 -2030 299 -15982 345 -1988 345 -2014 2029 -268 2069 -258 337 -1998 321 -2024 2025 -320 2025 -276 321 -1994 331 -2000 2057 -282 2043 -282 305 -2034 291 -2024 2039 -320 2019 -278 323 -1992 333 -15976 389 -1978 321 -1986 2069 -276 2053 -288 303 -2020 315 -1980 2067 -258 2075 -258 337 -1996 321 -2030 2027 -284 2059 -274 319 -2004 319 -2016 2033 -282 2059 -264 345 -2006 289 -16006 355 -1984 315 -2018 2033 -312 2015 -284 327 -2000 329 -2000 2063 -282 2049 -284 327 -1984 319 -2018 2035 -284 2057 -264 343 -2004 289 -2032 2059 -280 2019 -316 323 -1984 321 -15968 387 -1978 321 -1984 2069 -276 2049 -288 325 -1996 303 -2004 2055 -284 2049 -278 325 -2018 321 -1984 2075 -276 2049 -288 327 -1990 325 -1996 2031 -318 2025 -278 323 -2018 289 -16010 357 -1982 315 -2018 2035 -278 2053 -288 307 -2022 321 -2000 2049 -274 2045 -284 327 -2022 311 -2014 2033 -276 2053 -286 327 -2000 323 -2000 2061 -282 2015 -316 325 -2002 289 -15994 353 -1980 357 -1990 2035 -312 2015 -286 327 -2020 289 -2022 2033 -318 2019 -278 323 -2004 319 -2016 2059 -282 2023 -316 327 -1970 321 -2018 2059 -280 2019 -318 325 -1972 321 -15988 353 -1982 357 -1972 2071 -276 2051 -288 327 -1988 323 -1996 2061 -280 2053 -284 325 -1984 321 -2014 2031 -282 2059 -276 321 -2018 289 -2036 2031 -282 2059 -276 321 -2018 319 -15984 341 -1984 343 -2016 2029 -298 2033 -258 309 -2028 321 -2032 2035 -284 2017 -298 337 -2008 289 -2034 2027 -314 2023 -310 285 -2016 321 -2014 2035 -282 2027 -308 317 -2026 297 -15982 357 -1984 345 -1982 2067 -264 2057 -258 341 -1996 317 -2026 2057 -282 2017 -318 325 -1972 319 -2018 2059 -280 2029 -302 315 -2006 319 -2006 2059 -274 2047 -276 307 +RAW_Data: -2024 311 -15984 349 -2004 317 -2012 2029 -276 2047 -312 305 -2018 311 -2018 2037 -258 2077 -258 337 -1998 321 -2026 2031 -278 2043 -284 327 -2020 311 -2018 2031 -278 2051 -288 325 -2004 323 -15962 353 -2018 317 -2014 2007 -308 2019 -320 287 -2024 325 -1998 2035 -314 2025 -302 311 -2004 319 -2010 2027 -312 2011 -310 309 -2028 303 -2018 2027 -304 2007 -294 309 -2024 323 -16002 361 -1946 347 -2008 2027 -286 2045 -312 325 -1978 319 -2024 2043 -276 2049 -286 325 -1988 341 -1978 2061 -278 2049 -284 327 -1990 321 -2020 2029 -318 2021 -278 323 -2008 321 -15990 355 -1982 355 -1984 2031 -284 2049 -278 325 -2018 321 -2000 2047 -276 2049 -286 327 -1994 341 -1976 2067 -274 2043 -284 303 -2018 341 -1982 2061 -266 2069 -258 339 -1998 321 -16002 311 -2014 329 -2012 2027 -318 1999 -298 339 -2008 287 -2042 2031 -282 2027 -306 317 -2012 291 -2044 2037 -284 2033 -302 315 -2004 321 -2012 2037 -282 2027 -306 317 -2028 297 -15980 345 -2018 285 -2026 2037 -320 2017 -278 323 -2024 299 -2002 2069 -278 2055 -286 325 -2002 289 -2024 2037 -282 2063 -276 321 -2016 287 -2036 2025 -320 2023 -278 323 -2018 287 -16006 355 -1982 355 -1986 2033 -302 2039 -282 307 -2032 309 -2014 2043 -264 2057 -258 341 -1992 319 -2036 2033 -284 2029 -306 319 -2000 319 -2014 2035 -282 2061 -266 343 -2006 289 -16018 331 -2010 313 -2014 2021 -320 2017 -278 323 -2010 321 -2016 2037 -284 2033 -304 317 -2018 287 -2036 2037 -284 2025 -296 339 -1978 321 -2028 2057 -282 2021 -278 359 -1972 321 -15990 355 -1978 355 -1990 2035 -300 2015 -312 305 -2016 315 -2016 2053 -290 2013 -316 277 -2020 353 -1980 2059 -256 2077 -276 307 -2020 311 -2012 2047 -258 2073 -256 341 -1998 321 -15976 369 -1976 313 -2018 2051 -280 2043 -290 317 -2004 321 -2024 2023 -282 2053 -282 327 -2020 313 -2020 2035 -278 2055 -284 325 -1986 321 -2016 2033 -280 2049 -282 325 -2020 313 -15986 315 -2026 319 -2028 2003 -318 2013 -294 339 -2008 289 -2032 2027 -316 2019 -302 311 -2004 321 -2014 2033 -314 1995 -308 317 -2028 297 -2032 2035 -276 2023 -316 321 -2014 289 -15998 347 -2014 283 -2020 2035 -292 2031 -288 311 -2024 321 -2036 2001 -310 2027 -318 275 -2054 277 -2026 2029 -296 2027 -292 311 -2024 321 -2030 2031 -282 2033 -304 313 -2004 321 -15992 329 -2010 349 -1980 2037 -292 2051 -256 339 -1998 319 -2034 2029 -286 2055 -264 339 -1978 321 -2030 2027 -280 2043 -316 327 -1984 315 -2016 2063 -278 2041 -288 327 -1990 325 -15964 389 -1978 319 -1984 2063 -256 2055 -292 307 -2016 323 -2028 2027 -286 2057 -274 319 -2010 319 -2010 2033 -282 2061 -276 321 -2018 289 -2032 2027 -318 2021 -278 321 -2020 319 -15974 355 -1980 357 -2000 2035 +RAW_Data: -254 2079 -276 309 -2016 309 -2012 2035 -296 2063 -258 337 -1992 321 -2030 2037 -286 2027 -294 333 -1978 321 -2034 2029 -286 2031 -306 315 -2008 321 -15972 355 -1984 315 -2014 2037 -310 2011 -318 323 -1984 329 -2002 2033 -316 2029 -278 323 -2026 299 -2030 2035 -278 2019 -316 325 -1980 323 -2018 2053 -252 2075 -280 327 -1984 355 -15954 345 -2008 309 -2016 2045 -266 2065 -258 341 -235740 101 -202 65 -734 133 -372 401 -68 269 -236 505 -68 235 -234 875 -68 13969 -100 14297 -70 3863 -96 59337 -104 11859 -68 17409 -68 7317 -66 11443 -64 15589 -66 4381 -98 32297 -168 45445 -100 59295 -100 41417 -66 1539 -66 23001 +RAW_Data: 171 -316 311 -154 187 -292 343 -134 337 -136 199 -286 311 -182 157 -294 327 -160 171 -316 161 -312 159 -314 333 -170 311 -164 141 -324 305 -164 299 -176 301 -170 167 -340 133 -338 163 -312 159 -294 189 -298 165 -314 311 -180 155 -294 189 -300 165 -312 187 -284 189 -312 159 -294 189 -296 167 -314 161 -310 189 -312 159 -292 189 -296 167 -312 161 -312 159 -314 193 -268 175 -304 193 -314 163 -312 159 -312 193 -294 495 -458 161 -338 301 -168 165 -304 331 -134 199 -286 309 -184 309 -170 141 -318 331 -162 143 -316 309 -182 157 -294 189 -302 163 -312 309 -184 311 -170 141 -318 331 -162 299 -150 319 -196 139 -314 165 -334 137 -340 159 -312 161 -302 149 -318 331 -162 173 -312 161 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -312 159 -296 189 -300 193 -284 187 -284 189 -312 159 -292 189 -298 167 -312 161 -312 189 -300 173 -294 195 -282 191 -310 485 -472 167 -300 331 -134 171 -312 333 -170 169 -290 331 -162 299 -176 155 -300 333 -162 171 -300 323 -150 145 -340 159 -320 167 -302 319 -150 319 -166 169 -312 331 -162 299 -150 319 -194 139 -314 165 -308 163 -340 133 -338 163 -312 157 -296 327 -160 171 -316 163 -310 161 -312 163 -298 177 -302 193 -314 163 -310 161 -312 163 -300 173 -300 169 -318 177 -290 189 -300 165 -312 161 -312 189 -284 185 -292 189 -300 191 -286 187 -284 191 -312 471 -486 167 -314 313 -152 159 -300 335 -162 169 -308 319 -150 319 -166 169 -312 331 -162 143 -314 311 -180 157 -294 189 -300 165 -314 309 -182 311 -168 141 -318 333 -162 299 -174 295 -194 141 -314 165 -336 137 -338 135 -338 161 -312 159 -294 327 -160 171 -316 161 -312 159 -314 191 -270 201 -278 193 -314 163 -310 161 -312 163 -300 175 -300 169 -316 177 -292 189 -300 165 -312 161 -312 189 -284 183 -294 189 -298 193 -284 187 -312 163 -312 497 -460 145 -320 325 -168 171 -290 333 -162 171 -314 305 -160 317 -168 169 -290 333 -162 173 -312 283 -182 159 -294 189 -302 165 -312 309 -182 313 -168 143 -318 331 -162 301 -148 321 -194 141 -312 167 -334 135 -340 161 -310 163 -312 159 -294 327 -160 171 -316 163 -310 161 -312 163 -300 175 -302 195 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -300 165 -312 161 -310 189 -284 185 -292 189 -300 191 -286 187 -312 161 -300 487 -484 151 -318 335 -138 169 -322 305 -164 171 -314 305 -190 295 -166 171 -290 333 -162 173 -312 283 -182 159 -296 191 -300 +RAW_Data: 165 -312 333 -160 311 -170 141 -318 331 -164 299 -148 321 -194 139 -316 165 -336 135 -340 133 -338 163 -300 149 -320 329 -164 171 -314 159 -312 163 -300 175 -298 193 -310 163 -312 159 -312 163 -300 175 -296 193 -312 163 -312 159 -312 163 -300 175 -298 167 -320 175 -290 191 -296 193 -286 187 -310 163 -300 175 -296 501 -458 187 -288 347 -154 151 -316 325 -162 171 -316 313 -152 301 -194 141 -316 331 -162 143 -314 311 -182 155 -294 189 -302 163 -314 309 -182 311 -168 143 -318 331 -162 301 -174 295 -194 141 -314 165 -336 135 -340 133 -338 163 -312 159 -294 327 -158 173 -314 163 -312 159 -306 173 -314 165 -306 191 -284 189 -284 189 -312 157 -294 191 -300 163 -314 187 -284 189 -284 187 -292 189 -296 165 -314 161 -312 189 -284 185 -294 189 -298 193 -286 485 -492 167 -298 323 -150 175 -302 331 -164 171 -300 321 -150 317 -166 167 -308 331 -134 171 -316 309 -182 129 -318 189 -302 163 -314 309 -182 311 -170 141 -318 331 -162 301 -148 321 -194 139 -314 167 -334 137 -338 161 -310 163 -312 159 -294 327 -160 171 -316 161 -312 159 -312 193 -270 175 -304 193 -314 163 -312 159 -312 161 -314 161 -296 189 -304 165 -312 187 -284 189 -276 173 -318 195 -278 193 -284 187 -312 163 -300 175 -298 169 -318 511 -460 147 -318 327 -168 169 -292 331 -164 171 -314 305 -160 315 -168 171 -290 333 -162 171 -314 283 -182 159 -294 189 -300 165 -314 307 -184 311 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -336 135 -340 133 -338 163 -312 157 -294 327 -160 171 -316 163 -310 161 -304 175 -314 167 -304 191 -286 187 -284 189 -284 187 -292 189 -300 165 -312 189 -284 187 -286 185 -294 189 -296 165 -314 161 -312 187 -286 185 -294 189 -298 193 -284 485 -494 167 -298 321 -150 177 -312 313 -168 171 -292 331 -164 299 -174 151 -316 325 -162 171 -316 289 -176 151 -316 187 -296 167 -312 311 -180 307 -170 141 -318 331 -162 299 -150 319 -196 139 -314 165 -334 137 -340 159 -312 161 -302 149 -320 329 -164 171 -314 159 -312 163 -300 175 -298 193 -310 163 -312 161 -310 163 -312 161 -296 189 -302 191 -286 187 -284 189 -312 159 -292 189 -296 167 -314 161 -310 189 -284 187 -292 189 -300 193 -284 485 -494 167 -296 323 -150 175 -312 313 -170 169 -292 333 -162 299 -176 151 -314 327 -160 171 -316 291 -176 149 -316 189 -296 165 -314 309 -182 307 -168 141 -318 331 -164 299 -148 321 -194 141 -314 165 -334 137 -338 161 -312 +RAW_Data: 161 -302 147 -320 331 -162 171 -314 161 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 173 -296 193 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -296 195 -284 189 -284 189 -312 159 -296 513 -468 159 -300 333 -162 171 -300 323 -150 171 -314 325 -162 311 -164 171 -314 305 -160 159 -296 327 -160 171 -316 161 -312 161 -312 335 -170 311 -164 141 -324 321 -154 299 -194 309 -164 143 -326 167 -314 161 -312 159 -312 163 -302 173 -300 331 -162 173 -312 161 -312 163 -300 175 -300 167 -320 153 -312 191 -296 167 -312 161 -312 189 -312 159 -292 189 -298 167 -312 161 -312 159 -314 193 -268 177 -302 193 -316 161 -312 159 -312 193 -294 497 -456 187 -312 303 -166 165 -306 331 -132 201 -286 309 -182 313 -168 143 -318 331 -162 143 -314 311 -182 157 -294 189 -302 165 -312 309 -182 311 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -338 161 -312 161 -300 149 -320 331 -162 171 -314 161 -310 163 -302 173 -298 193 -310 165 -310 161 -310 163 -312 161 -296 189 -300 193 -286 187 -284 189 -312 157 -294 189 -298 165 -314 161 -310 189 -284 187 -292 189 -300 193 -284 485 -494 167 -298 321 -150 171 -318 323 -162 171 -314 289 -176 297 -194 141 -314 331 -162 143 -316 309 -182 155 -294 189 -302 163 -314 309 -182 311 -168 143 -318 331 -162 301 -148 321 -194 139 -314 167 -334 137 -338 161 -310 163 -300 149 -320 331 -162 171 -314 161 -312 163 -300 173 -298 193 -310 163 -312 159 -312 163 -300 175 -296 193 -312 163 -312 159 -312 163 -300 175 -298 167 -320 175 -290 191 -296 193 -286 187 -310 163 -312 161 -296 513 -468 159 -300 333 -162 171 -302 321 -150 175 -300 333 -162 311 -162 171 -316 289 -176 153 -314 327 -160 171 -316 163 -312 159 -312 307 -168 335 -138 169 -320 307 -164 297 -176 301 -194 141 -318 165 -336 137 -338 133 -338 163 -312 159 -294 327 -160 171 -316 161 -312 161 -312 163 -298 177 -302 169 -318 177 -292 189 -298 167 -312 189 -284 189 -312 157 -294 189 -298 167 -312 161 -312 159 -312 193 -270 203 -302 169 -314 161 -312 189 -310 467 -484 145 -318 327 -168 143 -316 331 -162 173 -312 307 -160 313 -170 141 -318 331 -164 143 -342 281 -184 157 -294 189 -302 165 -312 307 -184 313 -168 141 -320 331 -162 299 -150 319 -194 141 -314 165 -336 137 -338 135 -338 161 -312 159 -294 327 -158 173 -314 163 -310 161 -312 165 -298 175 -304 193 -312 163 -312 159 -312 +RAW_Data: 163 -300 175 -298 169 -318 177 -290 191 -298 165 -314 161 -310 189 -284 185 -292 189 -298 193 -284 189 -310 163 -312 497 -460 145 -320 325 -170 169 -290 333 -162 171 -314 307 -158 317 -168 169 -290 333 -162 171 -314 281 -182 159 -296 189 -300 165 -312 309 -182 313 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 137 -338 135 -338 161 -312 159 -294 327 -160 173 -314 163 -312 159 -312 163 -300 175 -302 195 -312 163 -312 159 -312 163 -300 175 -298 169 -318 175 -292 189 -300 165 -312 161 -312 187 -284 185 -292 189 -300 193 -284 187 -312 161 -314 471 -486 167 -314 313 -152 159 -300 335 -162 169 -308 321 -156 329 -170 127 -318 321 -176 155 -300 333 -164 169 -302 165 -314 161 -310 329 -168 299 -166 151 -328 311 -154 331 -168 299 -178 143 -318 153 -322 167 -332 149 -320 151 -318 165 -304 321 -156 181 -290 189 -300 173 -294 177 -294 189 -302 173 -294 177 -294 189 -302 173 -294 177 -318 193 -278 173 -294 179 -292 191 -302 173 -294 177 -294 189 -302 173 -320 177 -294 195 -278 175 -318 491 -460 163 -314 333 -158 159 -294 327 -160 171 -316 311 -180 307 -170 141 -318 331 -162 143 -316 309 -182 157 -294 189 -302 165 -312 309 -182 313 -168 141 -318 331 -164 299 -148 321 -194 141 -314 165 -334 137 -338 135 -338 161 -312 159 -294 329 -158 173 -314 163 -310 161 -306 173 -314 165 -304 193 -284 187 -286 189 -276 197 -294 193 -310 163 -312 161 -310 163 -300 175 -296 193 -310 165 -284 187 -310 163 -300 175 -300 169 -318 177 -316 483 -466 187 -292 341 -134 171 -308 319 -150 179 -300 333 -164 309 -162 171 -316 313 -152 153 -316 327 -160 171 -316 163 -310 161 -312 307 -166 337 -136 171 -322 305 -164 299 -174 301 -194 141 -318 165 -336 137 -338 161 -310 163 -300 149 -320 331 -162 171 -314 161 -312 163 -300 173 -296 195 -310 163 -312 159 -312 163 -312 159 -296 189 -302 165 -312 189 -284 189 -284 185 -294 189 -296 165 -314 161 -310 189 -284 187 -294 189 -298 193 -284 485 -494 167 -298 321 -150 177 -312 313 -168 171 -290 333 -162 299 -176 151 -316 325 -162 171 -316 289 -176 151 -314 189 -296 165 -314 309 -182 305 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -340 133 -338 161 -302 149 -320 329 -164 171 -286 187 -312 163 -300 173 -298 193 -310 163 -312 159 -312 163 -312 159 -296 189 -302 191 -286 187 -284 189 -312 159 -292 191 -296 165 -314 161 -310 189 -284 187 -292 +RAW_Data: 189 -300 193 -284 485 -494 167 -298 321 -150 177 -300 327 -150 167 -328 305 -160 319 -168 171 -290 333 -162 171 -314 281 -184 159 -296 189 -300 165 -314 307 -184 311 -170 141 -318 333 -162 299 -150 319 -194 139 -314 167 -334 135 -340 133 -338 163 -312 159 -294 327 -158 173 -314 163 -312 159 -312 165 -298 177 -302 193 -314 163 -310 161 -312 163 -300 173 -300 167 -318 177 -292 189 -300 165 -312 161 -312 187 -284 185 -292 189 -300 191 -286 187 -284 189 -300 487 -484 149 -320 333 -138 171 -322 321 -154 151 -316 327 -160 311 -164 173 -312 307 -160 161 -298 355 -132 173 -314 163 -310 161 -312 335 -170 311 -164 143 -322 307 -164 323 -150 301 -196 141 -318 165 -334 137 -338 161 -312 161 -302 147 -320 331 -162 173 -312 161 -312 163 -300 173 -298 195 -310 163 -312 159 -312 163 -312 159 -296 189 -300 193 -284 189 -284 189 -284 185 -294 189 -296 165 -314 161 -312 189 -284 185 -294 189 -298 193 -286 485 -468 193 -296 323 -150 147 -340 313 -168 171 -292 331 -164 299 -174 151 -316 325 -160 173 -316 289 -176 149 -316 189 -294 165 -314 309 -182 307 -170 141 -318 331 -164 299 -148 321 -194 139 -314 167 -306 165 -338 133 -338 163 -312 159 -294 327 -160 173 -314 163 -312 161 -310 165 -300 175 -300 169 -318 177 -290 189 -298 165 -314 187 -284 189 -312 159 -294 189 -298 165 -314 161 -310 161 -306 173 -316 193 -280 191 -312 159 -312 163 -300 515 -460 161 -312 305 -190 135 -328 329 -134 201 -286 309 -182 311 -168 143 -318 331 -162 143 -316 309 -182 157 -294 189 -300 165 -312 309 -184 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -334 137 -340 159 -312 163 -300 149 -318 331 -162 173 -312 161 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 173 -296 193 -312 163 -312 159 -312 163 -312 161 -298 189 -300 165 -312 161 -312 189 -312 159 -292 191 -300 191 -286 485 -468 193 -298 323 -148 147 -342 323 -160 173 -314 289 -176 297 -196 139 -314 331 -162 143 -314 311 -180 157 -292 191 -300 165 -312 311 -182 309 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -338 161 -312 161 -312 159 -294 329 -158 173 -314 163 -310 161 -312 165 -298 175 -304 193 -312 163 -310 161 -310 163 -302 173 -300 169 -318 175 -292 189 -300 165 -312 161 -310 189 -278 173 -316 195 -280 191 -312 159 -312 163 -300 515 -460 163 -310 305 -190 137 -328 329 -134 199 -288 309 -182 311 -168 143 -318 +RAW_Data: 331 -162 143 -316 309 -182 157 -292 191 -300 165 -312 309 -182 313 -168 141 -318 331 -164 299 -174 295 -194 141 -314 165 -334 137 -340 133 -338 163 -312 159 -292 327 -160 171 -316 163 -310 161 -312 165 -298 175 -304 193 -312 163 -312 159 -312 163 -300 175 -300 169 -316 177 -292 189 -298 167 -312 161 -310 189 -278 173 -316 193 -282 191 -312 159 -312 163 -300 515 -460 163 -310 305 -190 137 -326 331 -132 201 -286 309 -184 309 -170 141 -318 333 -162 143 -314 311 -180 159 -294 189 -300 165 -312 309 -184 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 135 -340 159 -312 163 -300 149 -320 329 -164 171 -314 159 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 175 -294 195 -310 163 -312 161 -310 163 -300 175 -298 169 -318 153 -314 191 -296 193 -286 187 -284 189 -312 161 -294 509 -468 159 -298 335 -162 169 -304 323 -150 175 -300 335 -162 311 -160 173 -316 313 -152 151 -316 327 -160 171 -316 163 -312 159 -312 307 -168 335 -138 169 -322 305 -164 299 -176 299 -194 141 -316 167 -334 137 -340 159 -312 161 -302 147 -320 331 -162 173 -312 161 -312 163 -300 175 -296 193 -310 165 -310 161 -312 161 -302 173 -296 195 -310 163 -312 161 -310 163 -300 175 -298 169 -318 177 -290 189 -296 193 -286 187 -310 163 -300 175 -296 501 -484 161 -288 347 -154 151 -316 325 -160 173 -314 313 -152 301 -196 141 -314 331 -162 143 -316 309 -182 157 -294 189 -300 165 -314 309 -182 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 137 -338 133 -338 163 -312 159 -294 327 -158 173 -316 161 -312 159 -312 163 -298 177 -302 193 -314 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -298 165 -314 161 -310 189 -284 193 -292 179 -308 167 -314 161 -312 189 -302 481 -460 173 -320 303 -168 169 -294 331 -164 171 -314 307 -160 319 -168 169 -292 331 -162 143 -342 283 -182 159 -294 189 -302 165 -312 309 -184 311 -168 143 -318 331 -162 299 -150 319 -194 141 -314 165 -336 135 -340 133 -338 163 -312 159 -294 327 -158 173 -316 161 -312 159 -312 165 -298 177 -302 193 -312 163 -312 161 -310 163 -314 161 -296 189 -302 165 -312 187 -284 189 -284 185 -294 189 -296 165 -314 187 -284 189 -302 173 -294 195 -310 491 -458 163 -318 319 -156 153 -314 327 -160 171 -316 311 -152 327 -170 141 -316 331 -164 143 -314 309 -182 157 -294 189 -300 165 -314 309 -182 311 -168 143 -316 333 -162 299 -150 +RAW_Data: 15041 -66 15883 -66 12643 -66 12681 -66 3413 -68 2713 -68 33389 -66 1445 -66 1279 -68 1027 -66 6911 -98 25229 -66 3967 -100 3019 -100 6131 -66 955 -66 3605 -66 12411 -98 1419 -66 3593 -68 2753 -66 2457 -66 6007 -66 627 -100 1597 -66 3071 -98 22749 -66 333 -66 12829 -66 4313 -132 855 -66 44097 -64 20391 -98 29999 -66 3539 -98 557 -66 1489 -100 4081 -100 3857 -64 2895 -132 2261 -166 3089 -66 2429 -68 34467 -66 3585 -66 3087 -66 3329 -132 5287 -66 1063 -98 15259 -100 2535 -66 995 -66 13057 -100 24233 -68 531 -100 26415 -66 1761 -100 2717 -66 4071 -100 12191 -66 23367 -68 2323 -66 19809 -248 245 -1388 255 -242 275 -1358 273 -1370 277 -246 277 -1368 275 -246 275 -1362 275 -244 275 -1364 275 -244 275 -1362 275 -244 275 -1328 273 -278 273 -1358 275 -246 275 -238 263 -1384 275 -246 273 -1358 275 -244 273 -1358 275 -246 275 -1360 275 -1344 277 -246 275 -1358 275 -244 275 -234 263 -1382 277 -1344 277 -246 279 -1362 275 -246 271 -234 261 -1380 275 -246 273 -1360 275 -246 275 -1366 277 -1340 277 -248 279 -238 263 -1382 275 -1344 277 -246 279 -1364 277 -244 275 -234 263 -1382 277 -244 273 -1358 275 -1344 277 -248 279 -1368 275 -244 273 -1360 239 -280 271 -1358 275 -244 275 -1358 275 -174 269 -10298 289 -2660 267 -238 299 -1356 275 -244 275 -1356 275 -1344 277 -248 277 -1360 275 -246 275 -1328 309 -244 273 -1358 277 -244 275 -1356 275 -246 273 -1326 309 -244 275 -1356 275 -246 273 -234 263 -1380 277 -246 273 -1326 309 -244 273 -1356 277 -246 277 -1358 275 -1338 279 -248 279 -1364 275 -246 273 -234 261 -1380 277 -1344 279 -250 277 -1330 309 -244 273 -232 261 -1384 275 -246 273 -1356 275 -248 275 -1360 275 -1340 279 -248 277 -236 263 -1380 277 -1342 279 -248 279 -1366 275 -246 273 -234 263 -1380 275 -246 275 -1358 275 -1340 279 -248 281 -1336 309 -244 273 -1358 275 -246 273 -1360 275 -244 273 -1358 275 -176 267 -10306 257 -2646 299 -234 301 -1354 277 -246 275 -1356 277 -1340 279 -250 279 -1332 309 -244 275 -1358 275 -248 273 -1326 309 -246 273 -1326 309 -244 275 -1356 277 -248 275 -1328 309 -246 273 -234 261 -1382 277 -246 277 -1326 309 -244 275 -1358 277 -246 277 -1356 277 -1346 277 -250 277 -1358 277 -246 275 -234 263 -1382 279 -1346 279 -248 281 -1330 307 -246 273 -236 261 -1380 277 -246 277 -1360 277 -246 277 -1360 275 -1344 279 -248 279 -236 263 -1384 277 -1340 279 -250 281 -1338 307 -246 271 -234 261 -1384 277 -246 275 -1356 277 -1340 279 -250 283 -1336 309 -246 273 -1356 277 -246 273 -1360 277 -246 +RAW_Data: 275 -1328 309 -174 269 -10296 289 -2648 267 -238 299 -1356 277 -246 275 -1324 307 -1342 279 -250 277 -1330 309 -244 275 -1362 277 -244 275 -1356 275 -248 273 -1328 309 -244 273 -1328 309 -244 275 -1360 277 -246 275 -234 259 -1384 277 -246 275 -1360 275 -246 273 -1358 277 -248 277 -1362 275 -1344 277 -248 277 -1328 307 -246 273 -236 261 -1384 277 -1348 279 -248 279 -1360 277 -246 273 -234 263 -1388 275 -246 275 -1360 277 -248 279 -1368 277 -1344 279 -248 279 -240 265 -1386 275 -1342 279 -286 247 -1372 275 -248 275 -238 265 -1386 277 -248 275 -1360 275 -1344 277 -286 247 -1374 275 -246 275 -1362 277 -246 275 -1360 277 -248 275 -1326 307 -174 269 -10290 287 -2654 269 -236 301 -1352 275 -248 273 -1326 311 -1340 277 -248 277 -1328 309 -244 273 -1358 275 -244 275 -1326 309 -244 273 -1356 277 -244 273 -1356 275 -246 275 -1358 275 -244 275 -234 261 -1382 277 -246 273 -1358 275 -246 273 -1360 277 -246 273 -1324 309 -1340 277 -248 277 -1328 307 -246 271 -234 259 -1382 277 -1346 279 -248 277 -1330 309 -244 271 -232 259 -1382 277 -244 275 -1356 277 -248 273 -1354 277 -1342 277 -248 275 -236 261 -1380 277 -1344 277 -248 279 -1330 307 -246 273 -234 261 -1378 277 -246 273 -1356 277 -1342 277 -248 277 -1330 309 -244 273 -1322 307 -246 273 -1326 309 -244 273 -1322 309 -176 267 -10298 257 -2682 265 -236 299 -1324 309 -248 273 -1324 311 -1342 277 -246 279 -1360 277 -244 275 -1362 275 -244 275 -1358 275 -244 275 -1360 275 -246 273 -1360 275 -244 277 -1360 275 -246 273 -234 263 -1384 275 -246 273 -1358 275 -246 275 -1360 277 -246 277 -1356 277 -1342 279 -248 277 -1364 275 -244 275 -234 261 -1384 275 -1344 277 -250 279 -1366 275 -246 273 -236 263 -1384 277 -246 275 -1358 277 -246 277 -1362 277 -1342 279 -248 279 -236 265 -1382 277 -1346 277 -248 281 -1366 275 -246 275 -234 265 -1384 275 -246 273 -1358 277 -1344 279 -248 279 -1364 275 -244 275 -1324 309 -246 273 -1324 307 -246 273 -1326 309 -174 267 -118796 133 -100 131 -892 329 -166 199 -132 131 -166 99 -100 265 -264 4663 -134 4889 -100 365 -98 5921 -100 5903 -68 4877 -98 2953 -98 1645 -64 1687 -66 981 -98 10769 -66 18319 -66 4831 -66 13301 -66 893 -132 5967 -100 15949 -66 3749 -66 497 -100 625 -66 1147 -66 469 -66 1261 -66 3651 -100 265 -100 26741 -68 6873 -66 4485 -100 2667 -68 3159 -68 2857 -132 2655 -66 12903 -66 1277 -66 1711 -66 787 -100 1327 -198 727 -64 1677 -100 1187 -66 1019 -66 891 -66 4303 -100 11297 -66 3923 -254 253 -1380 247 -292 253 -1344 +RAW_Data: 277 -1346 277 -250 279 -1364 275 -244 275 -1362 275 -244 275 -1356 275 -246 273 -1358 241 -278 273 -1356 275 -246 273 -1360 275 -246 273 -234 263 -1382 275 -244 273 -1358 275 -246 273 -1360 275 -246 273 -1358 275 -1340 277 -248 277 -1362 275 -246 273 -234 261 -1380 277 -1344 277 -248 279 -1362 275 -244 273 -236 261 -1380 275 -244 275 -1360 275 -246 275 -1358 275 -1346 277 -246 275 -236 263 -1384 275 -1342 277 -248 277 -1364 277 -244 273 -234 261 -1378 277 -246 273 -1356 277 -1340 277 -248 281 -1334 307 -246 271 -1356 275 -246 273 -1358 275 -244 273 -1326 309 -174 267 -10296 257 -2650 297 -232 263 -1384 277 -244 273 -1358 275 -1340 279 -248 279 -1328 309 -244 275 -1328 307 -244 273 -1356 275 -244 275 -1358 275 -246 273 -1324 309 -244 275 -1328 307 -244 273 -234 261 -1382 275 -246 273 -1326 309 -244 273 -1358 275 -246 273 -1358 275 -1338 279 -248 279 -1330 309 -244 273 -232 261 -1380 277 -1344 279 -248 279 -1330 309 -244 271 -234 261 -1382 275 -246 273 -1358 277 -244 275 -1330 309 -1338 277 -246 277 -236 263 -1380 277 -1342 277 -248 279 -1364 275 -246 273 -232 261 -1380 275 -248 275 -1328 307 -1338 277 -248 279 -1334 309 -244 271 -1358 275 -244 275 -1324 307 -246 271 -1328 309 -174 265 -10270 291 -2640 297 -232 297 -1350 277 -248 275 -1326 309 -1340 277 -248 277 -1328 309 -244 273 -1358 275 -246 273 -1326 309 -244 273 -1354 275 -246 273 -1330 307 -244 273 -1358 275 -246 273 -234 263 -1380 275 -246 273 -1358 275 -246 273 -1360 275 -244 273 -1358 275 -1340 277 -248 279 -1364 275 -244 273 -232 261 -1380 277 -1342 279 -250 279 -1332 307 -244 271 -234 261 -1378 277 -246 273 -1358 275 -248 275 -1360 275 -1340 277 -248 275 -236 263 -1382 277 -1344 277 -246 277 -1364 275 -246 273 -234 259 -1380 275 -246 273 -1362 275 -1342 275 -248 277 -1334 309 -244 271 -1356 275 -244 275 -1326 307 -244 273 -1356 275 -176 267 -10290 289 -2644 267 -238 301 -1320 309 -246 273 -1324 309 -1340 277 -248 277 -1328 307 -246 273 -1326 307 -246 273 -1324 309 -246 273 -1322 309 -246 273 -1322 307 -246 275 -1326 309 -246 273 -234 259 -1382 275 -246 275 -1322 309 -246 273 -1326 309 -246 273 -1326 309 -1340 277 -248 275 -1326 309 -246 273 -232 261 -1380 279 -1346 277 -250 277 -1328 309 -244 271 -232 261 -1380 277 -246 273 -1358 275 -248 273 -1328 307 -1340 277 -248 277 -236 261 -1380 277 -1344 277 -248 279 -1328 309 -244 275 -232 261 -1378 277 -248 273 -1326 309 -1344 277 -248 277 -1358 277 -246 273 -1328 307 -244 271 -1324 309 -244 +RAW_Data: 273 -1324 309 -174 267 -10270 289 -2638 297 -234 297 -1352 275 -248 275 -1328 307 -1340 277 -248 275 -1330 309 -244 273 -1358 275 -244 275 -1326 309 -244 271 -1356 275 -244 275 -1326 307 -246 273 -1326 309 -244 273 -234 261 -1378 275 -248 275 -1326 309 -244 271 -1356 277 -248 273 -1328 309 -1338 277 -248 277 -1328 309 -244 271 -232 261 -1380 277 -1348 279 -248 277 -1328 307 -246 271 -234 259 -1384 275 -244 275 -1356 277 -246 275 -1326 309 -1344 275 -248 275 -236 261 -1378 277 -1342 277 -250 279 -1334 309 -244 271 -232 261 -1380 277 -246 273 -1326 307 -1344 277 -248 277 -1328 309 -246 273 -1326 309 -244 271 -1324 309 -244 273 -1324 307 -176 267 -10288 287 -2618 299 -236 299 -1354 277 -244 273 -1326 307 -1340 279 -248 275 -1328 309 -244 275 -1326 309 -246 273 -1324 307 -246 273 -1322 309 -244 273 -1322 309 -244 275 -1328 309 -246 273 -232 261 -1380 277 -246 275 -1324 309 -244 273 -1356 277 -246 275 -1324 309 -1340 279 -246 277 -1328 309 -244 273 -232 261 -1382 277 -1344 279 -250 277 -1324 309 -246 273 -234 261 -1380 277 -246 273 -1358 277 -246 273 -1328 309 -1340 277 -248 275 -236 261 -1380 275 -1344 279 -248 279 -1360 277 -244 273 -234 261 -1380 277 -246 275 -1354 277 -1344 277 -248 277 -1328 311 -246 273 -1324 307 -244 273 -1324 309 -244 273 -1320 309 -176 269 -118210 761 -168 267 -66 563 -132 99 -132 3543 -66 5345 -100 4355 -66 4617 -68 20503 -166 2379 -132 293 -98 4117 -66 1151 -98 3353 -66 3485 -66 2491 -66 6133 -66 233 -68 16307 -68 16959 -98 357 -66 5419 -134 799 -100 327 -100 791 -66 2481 -66 963 -100 3481 -98 1679 -134 2473 -100 227 -68 3087 -66 11527 -130 4305 -98 435 -66 563 -100 2887 -100 267 -66 1787 -66 9655 -66 4793 -100 2119 -66 359 -98 1313 -132 3393 -234 995 -66 2681 -98 99 -130 1379 -100 3757 -100 21695 -132 5135 -100 693 -98 4631 -100 2325 -68 4937 -66 10409 -98 897 -100 1287 -66 2565 -66 3753 -66 4055 -66 2023 -68 1961 -68 629 -66 431 -66 5039 -66 2155 -100 2673 -66 1163 -98 6539 -100 825 -66 1197 -100 3053 -66 13973 -68 15515 -100 1861 -66 1027 -66 797 -98 959 -98 787 -132 787 -64 3811 -132 1747 -66 6683 -66 1033 -68 24927 -66 1259 -100 1125 -68 663 -66 1687 -66 4357 -132 4567 -66 3969 -98 3317 -132 433 -134 6043 -66 3249 -100 431 -98 2367 -100 11265 -66 5085 -68 2355 -64 1815 -66 1395 -274 241 -1366 275 -244 275 -1362 275 -1338 277 -284 243 -1368 239 -278 275 -1362 275 -244 275 -1360 241 -278 273 -1356 275 -246 275 -1360 239 -280 275 -1360 +RAW_Data: 275 -244 275 -234 263 -1386 239 -280 273 -1356 275 -244 273 -1360 275 -244 277 -1364 275 -1336 277 -248 277 -1366 275 -244 273 -234 263 -1386 275 -1340 277 -248 279 -1364 275 -244 275 -234 263 -1384 273 -244 275 -1358 275 -244 275 -1364 275 -1342 275 -248 277 -236 265 -1384 275 -1340 277 -282 243 -1366 275 -246 273 -236 263 -1382 277 -244 275 -1358 275 -1342 277 -248 277 -1364 275 -246 275 -1360 239 -280 273 -1358 241 -278 275 -1356 275 -210 233 -10302 257 -2652 297 -232 297 -1354 277 -244 275 -1358 275 -1340 279 -248 279 -1360 275 -246 275 -1360 275 -246 273 -1360 275 -244 275 -1328 309 -242 273 -1324 309 -244 275 -1360 275 -246 273 -234 261 -1384 275 -246 273 -1358 275 -244 275 -1358 277 -248 273 -1358 275 -1340 279 -248 277 -1334 307 -242 273 -232 261 -1380 277 -1348 277 -250 277 -1364 275 -244 275 -234 261 -1380 277 -244 275 -1358 277 -246 277 -1360 277 -1342 275 -248 275 -236 263 -1380 277 -1344 277 -248 279 -1368 275 -244 275 -232 261 -1382 277 -244 275 -1356 275 -1344 277 -248 279 -1362 275 -246 275 -1360 275 -246 273 -1356 275 -246 273 -1356 275 -176 267 -10302 257 -2648 299 -234 297 -1352 277 -246 275 -1326 309 -1340 279 -248 277 -1330 309 -244 275 -1328 309 -244 273 -1324 309 -244 275 -1324 309 -246 273 -1324 307 -246 275 -1328 309 -244 273 -234 261 -1378 277 -248 275 -1328 309 -244 273 -1356 277 -248 275 -1326 309 -1344 277 -248 275 -1326 309 -246 273 -234 259 -1380 277 -1348 281 -248 279 -1328 307 -246 273 -234 259 -1382 277 -246 275 -1360 275 -248 275 -1324 309 -1340 279 -248 277 -238 261 -1382 277 -1344 277 -248 279 -1330 311 -244 273 -234 259 -1378 277 -248 275 -1326 309 -1340 279 -248 279 -1336 307 -246 271 -1324 309 -244 275 -1324 307 -246 273 -1326 309 -174 269 -10296 257 -2648 299 -234 297 -1352 277 -248 273 -1326 309 -1342 277 -248 277 -1328 309 -246 275 -1328 309 -244 273 -1326 309 -244 273 -1322 309 -244 273 -1328 307 -244 275 -1328 309 -246 273 -234 261 -1382 277 -246 275 -1326 309 -244 273 -1352 277 -248 275 -1330 309 -1340 277 -248 277 -1328 309 -244 275 -232 261 -1384 277 -1342 279 -250 279 -1328 309 -244 273 -234 263 -1380 277 -246 273 -1360 277 -246 275 -1326 309 -1340 277 -250 277 -236 263 -1382 277 -1342 277 -248 279 -1362 277 -246 273 -234 263 -1382 277 -244 275 -1356 277 -1340 279 -248 279 -1362 275 -246 275 -1328 307 -246 273 -1356 275 -246 273 -1356 275 -174 269 -10292 287 -2650 269 -236 301 -1354 275 -248 273 -1358 275 -1340 279 -248 277 -1332 307 -246 275 -1328 +RAW_Data: 309 -244 273 -1324 309 -244 273 -1356 275 -246 273 -1358 275 -244 277 -1330 309 -244 273 -234 261 -1382 277 -244 275 -1358 275 -246 273 -1356 277 -248 275 -1360 275 -1340 277 -248 277 -1360 275 -246 273 -236 261 -1382 279 -1344 279 -248 279 -1360 277 -244 273 -234 261 -1380 277 -246 275 -1360 277 -246 273 -1360 275 -1342 279 -248 275 -236 263 -1382 275 -1344 279 -248 279 -1362 277 -246 273 -234 263 -1380 277 -246 275 -1356 275 -1342 277 -248 281 -1336 307 -246 271 -1354 277 -246 275 -1328 307 -244 273 -1352 277 -176 269 -10300 257 -2650 299 -232 297 -1354 277 -246 275 -1356 277 -1342 277 -248 279 -1328 309 -244 275 -1360 275 -246 273 -1328 307 -246 273 -1356 277 -246 277 -1326 309 -244 277 -1360 277 -246 273 -234 263 -1384 277 -246 275 -1324 309 -246 275 -1358 277 -246 277 -1360 277 -1344 277 -248 277 -1326 309 -246 273 -236 261 -1382 277 -1348 279 -250 281 -1330 307 -246 273 -234 263 -1386 277 -244 275 -1356 277 -248 277 -1362 277 -1342 277 -250 277 -238 263 -1384 277 -1342 277 -250 281 -1332 309 -246 273 -234 263 -1380 277 -246 275 -1360 277 -1342 279 -248 281 -1334 307 -246 273 -1356 275 -248 275 -1328 309 -244 275 -1324 309 -176 269 -115034 163 -362 67 -894 529 -166 14663 -98 4135 -66 3681 -100 299 -68 9829 -66 3517 -64 21569 -66 3251 -66 2209 -64 23701 -66 3359 -68 1057 -66 723 -66 299 -134 765 -66 589 -98 1687 -134 2153 -66 3081 -68 10447 -66 11643 -66 2451 -66 2277 -66 2897 -66 755 -100 5539 -64 5117 -132 4867 -134 3931 -64 625 -66 1317 -98 11597 -66 2255 -66 1165 -66 1123 -66 6371 -100 699 -68 1811 -66 621 -68 2191 -64 1291 -134 3003 -66 2423 -64 1463 -66 663 -100 1127 -100 6169 -100 489 -100 6087 -100 2027 -66 1195 -66 13195 -66 557 -66 40423 -98 1919 -100 1061 -132 201 -66 2553 -132 12549 -66 1789 -100 921 -134 1067 -66 729 -66 10029 -66 3909 -100 265 -100 16017 -134 21177 -68 2461 -66 2215 -68 1197 -66 5911 -66 2645 -66 3419 -132 16275 -64 5091 -68 2123 -66 2677 -64 10305 -66 12381 -100 427 -166 25331 -66 2457 -66 11859 -248 279 -1368 275 -246 275 -1360 275 -1340 277 -246 279 -1364 239 -278 275 -1358 275 -244 275 -1362 239 -278 273 -1358 239 -280 271 -1360 241 -278 273 -1360 275 -244 275 -234 261 -1384 239 -280 273 -1356 275 -244 273 -1360 275 -244 275 -1358 275 -1344 277 -248 275 -1358 275 -244 273 -236 261 -1384 275 -1342 279 -246 279 -1360 275 -244 275 -234 263 -1384 239 -278 273 -1358 275 -244 275 -1362 275 -1342 275 -248 275 -238 263 -1382 275 -1344 275 -248 +RAW_Data: 277 -1364 275 -244 273 -234 263 -1380 275 -246 273 -1358 275 -1342 277 -246 279 -1366 275 -244 273 -1362 239 -278 239 -1386 275 -246 273 -1360 241 -208 269 -10290 257 -2686 265 -232 265 -1384 275 -246 275 -1358 275 -1344 277 -248 275 -1358 275 -246 275 -1360 277 -244 273 -1326 309 -244 271 -1354 275 -244 275 -1358 275 -246 273 -1358 275 -246 273 -234 263 -1378 275 -246 275 -1360 275 -244 273 -1356 275 -246 275 -1360 275 -1342 277 -246 277 -1360 275 -246 273 -232 261 -1382 277 -1342 279 -248 279 -1360 275 -244 275 -232 261 -1380 277 -244 275 -1356 277 -246 277 -1360 275 -1342 277 -246 275 -236 263 -1384 275 -1342 277 -248 277 -1362 275 -246 273 -234 261 -1378 277 -246 275 -1328 307 -1340 277 -246 279 -1366 275 -244 273 -1326 307 -244 273 -1324 309 -244 273 -1356 275 -174 267 -10304 255 -2648 297 -230 263 -1382 277 -244 275 -1330 307 -1338 277 -248 277 -1330 309 -244 273 -1356 275 -246 273 -1362 275 -244 273 -1356 275 -244 273 -1326 307 -244 273 -1360 273 -246 273 -236 261 -1380 275 -244 275 -1328 307 -244 273 -1358 275 -244 275 -1360 275 -1342 277 -246 277 -1364 275 -244 271 -232 261 -1384 277 -1340 279 -248 279 -1360 275 -246 273 -234 261 -1380 275 -244 275 -1360 277 -244 275 -1356 275 -1342 279 -246 277 -236 263 -1382 275 -1340 277 -248 279 -1366 275 -246 271 -234 261 -1382 277 -244 275 -1354 275 -1342 277 -248 277 -1364 273 -246 273 -1362 275 -244 271 -1360 275 -244 273 -1358 275 -174 267 -10272 289 -2646 265 -262 261 -1382 277 -244 275 -1356 275 -1342 277 -248 277 -1364 275 -244 275 -1360 275 -244 273 -1358 275 -244 273 -1358 275 -244 273 -1326 307 -244 275 -1358 275 -246 273 -234 261 -1382 275 -246 273 -1358 275 -244 273 -1358 275 -246 275 -1360 275 -1338 277 -248 277 -1362 277 -244 271 -234 261 -1380 277 -1344 279 -248 277 -1332 273 -278 271 -234 261 -1382 275 -244 275 -1356 277 -246 275 -1360 277 -1340 277 -246 277 -234 263 -1384 275 -1342 277 -248 277 -1366 275 -244 273 -234 261 -1380 275 -246 273 -1360 275 -1340 277 -246 279 -1334 307 -244 273 -1356 275 -246 273 -1360 275 -244 271 -1354 277 -174 269 -10300 257 -2648 297 -230 263 -1384 277 -244 273 -1356 277 -1342 277 -248 277 -1362 275 -244 275 -1330 307 -244 273 -1324 309 -244 273 -1324 307 -246 273 -1326 307 -244 273 -1358 275 -246 273 -234 261 -1380 277 -246 273 -1358 275 -244 275 -1354 277 -248 275 -1360 275 -1338 279 -246 277 -1360 275 -244 273 -234 261 -1378 279 -1344 279 -248 279 -1330 309 -244 271 -232 261 -1380 277 -246 273 -1360 +RAW_Data: 277 -244 275 -1360 275 -1340 277 -246 277 -236 261 -1380 275 -1346 277 -248 277 -1362 275 -246 273 -234 263 -1380 275 -244 275 -1358 275 -1340 277 -248 279 -1334 309 -244 273 -1324 307 -246 273 -1356 275 -244 273 -1356 275 -174 269 -10302 257 -2644 297 -232 263 -1384 277 -246 275 -1354 275 -1344 277 -248 275 -1360 275 -246 275 -1358 275 -246 273 -1326 307 -246 273 -1324 307 -244 273 -1328 307 -244 273 -1358 275 -244 273 -236 261 -1380 275 -246 273 -1358 275 -244 273 -1358 275 -246 273 -1360 275 -1344 275 -248 275 -1360 275 -244 273 -234 261 -1378 277 -1344 279 -248 277 -1362 275 -246 273 -234 261 -1378 275 -244 275 -1360 275 -246 275 -1358 275 -1344 277 -246 277 -234 263 -1380 275 -1338 279 -246 281 -1368 275 -244 271 -234 261 -1386 275 -244 271 -1358 275 -1342 277 -246 279 -1362 275 -244 275 -1326 273 -278 273 -1358 239 -278 273 -1358 275 -174 267 -127478 195 -964 2317 -66 763 -98 1455 -100 16109 -66 5683 -98 11469 -66 34413 -66 5443 -66 11613 -66 2737 -66 12191 -66 2951 -68 1851 -68 1895 -68 2643 +RAW_Data: 29262 361 -68 2635 -66 24113 -66 1131 -100 4157 -66 26253 -130 621 -18438 99 -298 231 -66 197 -496 753 -230 7503 -16526 65 -396 65 -296 99 -196 293 -64 429 -132 397 -66 329 -66 37701 -66 13475 -100 54967 -64 18209 -18340 97 -462 197 -98 587 -232 97 -100 259 -98 197 -262 297 -64 557 -100 599 -100 333 -234 42493 -13212 6449 -206 173 -214 217 -176 195 -218 181 -218 181 -182 217 -182 217 -176 187 -214 215 -180 217 -182 217 -182 217 -178 185 -424 1177 -388 387 -240 381 -214 181 -398 211 -380 419 -176 217 -394 203 -394 205 -380 189 -402 421 -168 219 -398 393 -190 191 -398 205 -406 185 -402 381 -212 215 -362 241 -378 421 -176 377 -218 197 -378 427 -210 393 -172 429 -172 397 -212 217 -362 389 -228 197 -372 417 -204 395 -210 181 -398 391 -192 201 -216888 761 -200 299 -166 695 -132 15435 -66 5611 -66 21049 -66 4947 -66 2355 -66 1921 -100 2223 -100 2107 -100 397 -98 3643 -66 5301 -98 14205 -66 37371 -246 175 -216 179 -216 177 -224 149 -246 159 -228 181 -212 201 -204 159 -244 151 -254 169 -214 181 -210 197 -182 181 -454 1141 -444 357 -228 361 -246 177 -396 209 -412 367 -188 187 -434 201 -394 185 -406 193 -402 377 -238 181 -386 381 -234 153 -424 205 -412 157 -412 383 -240 181 -398 203 -392 385 -236 371 -212 179 -400 383 -240 359 -210 375 -220 381 -246 175 -394 383 -240 181 -398 363 -222 379 -246 175 -394 383 -204 217 -182856 99 -66 99 -300 133 -402 65 -198 99 -328 65 -100 491 -164 593 -100 3547 -64 361 -66 789 -68 2521 -66 22883 -66 2659 -98 3309 -130 3789 -100 9689 -17178 99 -1388 65 -266 197 -100 131 -134 99 -232 627 -130 233 -66 1949 -100 14567 -198 165 -256 181 -208 159 -214 183 -220 163 -244 149 -246 159 -236 181 -254 141 -226 151 -246 157 -228 181 -212 201 -400 1163 -428 379 -230 355 -244 177 -396 207 -412 367 -222 157 -418 189 -410 207 -412 171 -430 357 -226 165 -404 413 -204 181 -428 173 -428 169 -426 353 -236 173 -414 173 -408 381 -244 337 -222 201 -408 397 -208 393 -204 395 -208 359 -246 177 -394 387 -200 205 -380 415 -202 395 -208 181 -432 357 -226 169 -195084 65 -300 763 -66 297 -364 593 -68 2883 -66 1357 -68 363 -98 3841 -66 3119 -66 5153 -66 4023 -268 143 -246 133 -290 141 -250 139 -254 141 -226 181 -248 137 -254 143 -252 139 -252 143 -230 181 -250 139 -254 145 -436 1135 -448 349 -240 347 -254 157 -434 167 -426 377 -226 157 -434 167 -426 155 -440 163 -434 375 -206 215 -380 381 -234 153 +RAW_Data: -424 205 -412 159 -412 381 -240 181 -398 203 -392 387 -236 369 -212 179 -400 383 -240 359 -244 339 -222 381 -246 175 -394 383 -240 181 -398 363 -222 381 -244 175 -392 383 -240 181 -184002 99 -360 63 -330 65 -132 129 -232 97 -198 295 -328 6031 -66 831 -132 3417 -66 2187 -64 2183 -100 6535 -66 1127 -66 2569 -66 2031 -66 2271 -66 2183 -66 3815 -66 3803 -66 493 -66 1909 -66 1627 -98 4805 -17512 67 -2164 131 -498 265 -430 163 -98 97 -64 99 -230 99 -100 229 -230 165 -196 63 -132 99 -66 927 -66 14955 -66 19621 -68 2627 -66 14305 -68 23247 -66 2891 -66 3941 -66 3021 -212 173 -242 181 -218 181 -214 181 -208 157 -250 141 -248 181 -218 179 -214 179 -210 159 -250 179 -214 181 -218 181 -404 1153 -404 389 -244 375 -192 181 -436 161 -414 383 -240 181 -398 205 -392 201 -394 205 -394 365 -246 177 -396 383 -204 217 -398 171 -426 167 -428 353 -242 173 -420 173 -408 373 -220 403 -208 175 -422 381 -194 399 -228 357 -246 355 -210 215 -400 387 -208 181 -398 391 -226 353 -246 177 -398 383 -204 217 -185098 163 -166 525 -98 293 -100 63 -66 229 -66 1183 -66 1507 -66 3089 -98 30187 -66 2847 -19112 133 -364 131 -394 97 -166 295 -66 229 -164 227 -66 263 -130 623 -98 2071 -66 493 -66 787 -98 691 -64 10249 -132 3879 -66 1949 -66 3453 -198 23157 -66 2845 -100 1193 -66 1587 -100 3797 -98 3187 -100 3319 -66 22119 -98 5513 -226 155 -244 153 -256 131 -248 151 -246 159 -262 121 -274 133 -272 127 -244 153 -254 167 -248 145 -244 133 -252 177 -398 1169 -418 381 -238 359 -242 141 -430 169 -426 357 -274 139 -422 171 -442 173 -428 167 -426 353 -236 171 -416 379 -226 149 -436 161 -438 173 -406 381 -234 153 -424 205 -380 389 -244 359 -206 215 -384 381 -246 335 -224 383 -246 355 -244 179 -404 385 -206 181 -432 359 -226 355 -246 175 -398 383 -240 181 -179760 97 -168 727 -66 97 -332 1389 -66 2793 -66 4955 -100 12453 -100 2425 -66 21965 -66 3809 -68 1683 -66 3095 -66 2153 -64 999 -208 173 -220 181 -214 191 -196 181 -212 183 -220 191 -212 181 -214 191 -198 181 -212 181 -222 191 -212 181 -214 191 -416 1167 -424 369 -220 373 -210 209 -390 207 -376 403 -190 187 -418 189 -408 209 -412 173 -428 357 -226 169 -404 399 -208 179 -412 209 -396 169 -428 355 -230 201 -378 205 -406 381 -244 339 -222 193 -400 413 -204 393 -208 347 -220 401 -210 175 -422 383 -202 217 -398 365 -222 377 -246 175 -390 385 -204 217 -179890 165 -1552 131 -164 65 +RAW_Data: -1448 361 -17056 131 -134 233 -1462 131 -166 953 -100 261 -164 5077 -272 137 -268 143 -252 141 -248 143 -246 159 -252 141 -244 143 -290 107 -276 145 -244 131 -250 179 -248 143 -252 141 -414 1165 -424 373 -236 359 -242 145 -434 169 -428 355 -230 169 -442 173 -434 157 -406 193 -402 379 -238 181 -422 335 -252 157 -434 167 -428 185 -406 381 -208 211 -390 207 -410 381 -200 373 -236 171 -414 383 -202 393 -210 379 -220 373 -208 211 -390 383 -204 217 -398 365 -220 379 -244 175 -394 381 -240 181 -161030 97 -166 167 -930 593 -2670 1091 -132 229 -98 461 -164 1649 -66 6311 -100 44723 -16832 67 -2656 131 -132 99 -132 263 -100 399 -68 893 -18950 99 -164 165 -198 525 -998 335 -66 565 -66 1057 -17880 97 -360 195 -262 131 -332 625 -98 197 -230 455 -98 9343 -16498 67 -368 131 -598 65 -1066 333 -300 789 -130 757 -66 87207 -16554 97 -3520 97 -786 591 -64 461 -98 21495 -66 24811 -18448 131 -296 491 -134 163 -760 1091 -230 893 -66 927 -68 4581 -68 32965 -64 45217 -17292 131 -1684 231 -132 327 -64 163 -330 263 -230 25751 +RAW_Data: -66 529 -66 2519 -66 265 -68 10101 -1794 65 -1890 393 -562 97 -132 197 -98 493 -330 97 -164 97 -230 327 -326 99 -100 97 -164 65 -132 293 -98 297 -166 161 -130 297 -230 1391 -68 11185 -3800 229 -230 297 -66 65 -198 65 -466 99 -464 99 -430 67 -698 295 -132 165 -164 1095 -66 299 -66 1321 -264 12675 -66 99 -166 229 -134 65 -330 165 -164 65 -890 131 -830 67 -66 1157 -100 167 -168 265 -66 827 -66 2047 -100 261 -594 2279 -134 10701 -3890 163 -1384 67 -98 99 -1322 99 -98 65 -398 823 -66 65 -68 927 -100 495 -132 593 -100 165 -198 1387 -1022 131 -728 99 -662 97 -462 495 -200 829 -330 563 -100 297 -330 65 -598 165 -592 295 -166 131 -764 165 -164 565 -66 131 -166 165 -66 9675 -5052 165 -2878 199 -66 265 -432 265 -66 267 -898 163 -132 231 -198 229 -164 97 -100 4445 -66 7853 -636 199 -662 265 -298 233 -1428 331 -134 1791 -66 1649 -66 297 -100 361 -198 559 -98 363 -200 1315 -66 265 -98 1049 -132 1647 -66 265 -822 295 -526 131 -1712 199 -166 231 -200 165 -66 265 -166 97 -132 163 -164 395 -630 495 -168 297 -298 229 -266 629 -200 133 -132 133 -166 65 -132 99 -100 131 -66 67 -98 133 -496 1391 -98 1751 -164 359 -132 97 -164 263 -64 691 -66 199 -66 293 -98 589 -198 11299 -3968 65 -68 65 -2702 65 -1186 927 -166 65 -66 429 -134 197 -134 529 -200 67 -66 231 -100 2151 -4014 97 -1486 99 -464 65 -330 129 -330 331 -134 599 -66 497 -200 165 -66 661 -166 6881 -8830 295 -100 197 -232 725 -134 299 -166 229 -166 525 -198 295 -66 459 -66 329 -230 595 -98 299 -132 329 -66 99 -98 163 -134 229 -100 8345 -6726 131 -132 295 -66 1579 -66 329 -98 501 -132 231 -66 491 -298 331 -266 363 -132 1193 -168 8847 -4194 199 -828 65 -100 195 -262 197 -298 65 -898 65 -132 629 -66 229 -100 291 -100 623 -66 295 -66 461 -132 529 -632 597 -132 65 -100 97 -134 297 -100 297 -166 397 -168 527 -134 9603 -3850 99 -200 67 -896 959 -198 165 -100 229 -266 531 -64 165 -132 163 -296 3715 -11994 165 -1492 429 -68 263 -100 265 -330 199 -64 495 -132 363 -66 63 -166 297 -398 65 -100 231 -332 199 -100 7683 -4916 65 -1294 297 -1022 1325 -166 393 -132 165 -498 1255 -134 197 -198 427 -164 329 -132 631 -594 199 -196 99 -100 265 -134 1457 -100 3649 -8592 67 -268 131 -332 99 -100 65 -760 101 -198 297 -168 199 -132 369 -100 97 -132 99 -232 397 -198 99 -134 97 -100 231 -332 131 -796 329 -266 263 +RAW_Data: -100 10841 -4030 163 -164 197 -398 195 -592 65 -132 63 -430 295 -298 263 -200 3517 -132 3763 -12296 99 -330 361 -98 99 -200 65 -430 165 -166 2327 -100 4051 -66 9653 -3478 197 -66 163 -198 167 -66 65 -598 165 -298 131 -666 199 -198 299 -298 165 -200 565 -66 797 -98 1125 -98 825 -100 4113 -6956 65 -5536 165 -266 99 -232 461 -198 65 -200 1989 -66 295 -66 723 -66 65 -98 329 -98 955 -66 559 -232 331 -66 10851 -1048 65 -3748 65 -498 99 -1392 99 -794 529 -98 331 -98 397 -164 363 -394 331 -266 299 -230 165 -66 3001 -568 197 -2872 2579 -468 2637 -472 2599 -488 2647 -426 2653 -448 2665 -392 379 -2734 381 -2700 2691 -388 377 -2732 2669 -420 355 -2726 2687 -394 403 -2706 2687 -388 377 -21248 2717 -388 377 -2738 2659 -408 2703 -382 2689 -416 2679 -408 2701 -382 2687 -418 365 -2736 365 -2722 2689 -384 391 -2696 2707 -386 379 -2734 2665 -410 361 -2726 2703 -418 357 -21246 2679 -466 297 -2768 2657 -448 2627 -434 2669 -450 2653 -416 2673 -408 2697 -386 383 -2728 369 -2706 2701 -382 387 -2726 2687 -418 357 -2708 2693 -418 361 -2702 2709 -396 401 -21232 2689 -406 361 -2736 2695 -386 2695 -406 2695 -382 2687 -418 2675 -410 2693 -414 375 -2692 389 -2706 2701 -404 363 -2724 2695 -388 389 -2702 2719 -358 405 -2704 2701 -402 363 -21262 2677 -414 367 -2738 2677 -386 2693 -420 2701 -400 2677 -386 2695 -420 2669 -430 369 -2718 353 -2730 2673 -412 361 -2734 2691 -420 357 -2698 2701 -394 401 -2702 2687 -424 347 -21244 2701 -418 365 -2726 2703 -382 2697 -420 2675 -400 2685 -384 2721 -398 2667 -418 355 -2744 343 -2722 2703 -420 353 -2724 2689 -396 363 -2736 2687 -390 377 -2730 2697 -386 357 -21274 2683 -414 375 -2726 2667 -420 2703 -398 2677 -388 2695 -420 2699 -398 2671 -384 415 -2698 357 -2738 2695 -382 383 -2724 2685 -416 357 -2706 2707 -384 391 -2726 2671 -384 415 -21238 2651 -476 293 -2776 2653 -462 2641 -446 2661 -422 2663 -418 2689 -412 2683 -414 357 -2706 385 -2698 2715 -378 379 -2710 2719 -388 377 -2708 2695 -406 361 -2724 2689 -416 361 -21244 2703 -386 413 -2698 2697 -414 2689 -384 2685 -386 2719 -378 2701 -416 2689 -386 377 -2728 387 -2700 2673 -410 361 -2730 2695 -420 357 -2732 2679 -386 377 -2734 2699 -378 361 -21262 2697 -392 405 -2702 2687 -406 2703 -382 2703 -418 2671 -406 2677 -416 2695 -386 387 -2700 381 -2704 2695 -418 357 -2712 2721 -388 375 -2702 2693 -408 363 -2730 2693 -420 355 -21248 2653 -494 289 -91206 131 -132 97 -232 559 -132 591 -98 691 -66 131 -130 297 -66 231 -66 331 -66 433 -100 499 -132 231 -166 197 -134 593 -100 11707 -4456 133 -200 131 +RAW_Data: -66 133 -66 97 -166 561 -100 895 -132 1323 -66 10873 -3752 99 -722 229 -394 97 -66 99 -98 99 -328 297 -328 265 -298 3089 -132 10573 -1460 133 -432 99 -232 99 -132 333 -232 731 -164 65 -166 165 -132 131 -330 65 -98 131 -596 65 -198 133 -98 397 -568 65 -132 1157 -166 195 -130 131 -64 99 -66 63 -198 265 -98 297 -66 63 -166 295 -100 1747 -232 6099 -11348 199 -528 297 -266 97 -598 99 -198 231 -64 4433 -334 65 -298 65 -3284 67 -530 97 -432 133 -2356 493 -68 231 -168 297 -266 427 -100 559 -98 229 -460 197 -66 261 -132 65 -98 565 -132 231 -66 497 -100 3491 -12356 65 -660 197 -198 165 -132 331 -134 65 -98 2651 -134 4531 -10850 65 -1322 263 -68 431 -232 165 -134 165 -202 231 -300 5625 -66 6951 -8162 65 -398 99 -596 65 -132 461 -598 429 -132 97 -132 463 -232 229 -98 329 -100 397 -100 363 -100 231 -200 163 -200 961 -66 693 -100 397 -134 10601 -3872 263 -100 165 -100 131 -198 99 -696 233 -1524 331 -132 131 -164 229 -132 493 -98 631 -134 231 -100 595 -66 295 -66 5965 -8248 99 -296 99 -98 397 -66 65 -924 229 -398 299 -98 1425 -130 565 -198 827 -262 429 -598 725 -704 729 -12290 131 -98 99 -98 65 -100 163 -164 65 -494 231 -100 97 -100 863 -66 1751 -3948 165 -100 195 -66 165 -296 65 -2042 99 -200 495 -132 557 -100 827 -98 167 -66 433 -100 661 -164 689 -98 10803 -3906 231 -296 295 -232 99 -234 131 -332 395 -266 1283 -164 755 -466 397 -164 335 -66 1355 -14376 557 -66 331 -68 431 -134 599 -364 229 -100 763 -98 265 -132 525 -166 99 -396 495 -98 3867 -134 595 -168 865 -166 503 -200 467 -134 8145 -458 235 -794 599 -458 265 -436 231 -426 333 -368 299 -730 689 -360 327 -370 363 -326 367 -668 733 -328 363 -302 397 -328 371 -296 393 -666 725 -622 795 -634 433 -264 1023 -228 5041 -762 585 -466 233 -826 235 -470 631 -368 299 -402 299 -726 361 -328 331 -370 363 -332 701 -298 401 -692 369 -302 759 -268 461 -236 435 -622 423 -260 465 -266 719 -13608 65 -624 197 -558 921 -164 1315 -134 465 -134 263 -100 295 -132 293 -66 329 -98 197 -132 9977 -5036 197 -798 333 -828 295 -100 197 -100 165 -66 665 -100 763 -300 297 -166 165 -98 823 -8348 229 -100 427 -196 263 -624 197 -134 797 -100 263 -68 529 -132 233 -134 165 -264 131 -132 559 -66 263 -228 927 -132 731 -102 1061 -66 863 -8206 131 -332 299 -166 461 -100 99 -66 429 -66 3271 -98 465 -100 401 -232 331 -66 397 -430 10341 +RAW_Data: -5434 65 -298 133 -132 131 -68 231 -200 661 -132 9517 -424 97 -1456 99 -1694 393 -100 131 -560 131 -196 197 -298 65 -428 229 -196 297 -266 131 -166 2435 -66 10161 -11230 65 -1320 131 -298 265 -532 231 -200 1291 -68 631 -66 12645 -4048 133 -66 67 -132 167 -266 163 -66 397 -132 197 -132 299 -98 197 -198 2903 -66 2361 -66 9627 -3588 197 -332 165 -68 331 -68 197 -132 99 -100 663 -66 363 -230 231 -166 131 -100 201 -298 163 -132 133 -202 363 -300 397 -102 263 -100 165 -66 1221 -66 1479 -132 165 -98 229 -12976 263 -66 363 -134 231 -66 629 -132 327 -100 97 -130 99 -164 227 -64 297 -132 397 -164 425 -198 97 -198 99 -66 365 -164 199 -102 97 -66 1817 -13524 231 -134 16907 -4086 233 -630 65 -396 201 -66 165 -198 67 -198 99 -664 2117 -166 12473 -446 2649 -440 2661 -420 2651 -422 2681 -418 2703 -400 365 -2724 387 -2696 2695 -414 357 -2704 2707 -386 389 -2700 2687 -392 405 -2706 2695 -402 363 -21268 2707 -388 377 -2706 2691 -404 2699 -382 2717 -382 2707 -378 2693 -416 2687 -396 363 -2736 355 -2748 2659 -416 365 -2708 2715 -388 377 -2708 2697 -404 363 -2730 2673 -420 355 -21268 2655 -460 319 -2766 2663 -448 2631 -436 2665 -418 2683 -410 2681 -416 2701 -386 383 -2700 375 -2744 2669 -416 353 -2730 2685 -416 357 -2708 2721 -380 369 -2724 2697 -382 385 -21260 2701 -418 353 -2720 2673 -418 2675 -408 2693 -384 2715 -386 2717 -386 2691 -404 363 -2732 387 -2702 2669 -412 359 -2736 2699 -380 381 -2728 2675 -416 381 -2720 2675 -414 347 -21280 2685 -390 377 -2724 2689 -416 2673 -408 2705 -382 2695 -410 2689 -414 2661 -418 385 -2704 369 -2704 2693 -416 375 -2726 2661 -420 355 -2728 2711 -388 375 -2702 2691 -410 363 -21252 2659 -488 287 -2794 2651 -448 2629 -436 2671 -416 2695 -416 2663 -406 2699 -384 383 -2730 367 -2702 2695 -418 385 -2702 2685 -412 349 -2744 2693 -366 389 -2714 2693 -394 381 -21266 2685 -418 363 -2730 2683 -382 2693 -418 2675 -410 2699 -384 2719 -382 2707 -380 359 -2734 387 -2704 2709 -380 361 -2732 2699 -418 357 -2728 2667 -416 383 -2696 2709 -380 391 -21228 2685 -458 307 -2800 2647 -412 2659 -432 2667 -416 2695 -416 2675 -406 2675 -416 383 -2700 361 -2730 2687 -414 375 -2696 2701 -420 353 -2720 2711 -382 367 -2728 2675 -416 385 -21222 2735 -386 355 -2744 2687 -396 2679 -418 2701 -386 2705 -382 2681 -410 2697 -384 385 -2736 365 -2704 2715 -384 377 -2696 2697 -416 349 -2722 2707 -386 379 -2732 2671 -410 361 -21258 2681 -464 297 -2796 2629 -456 2655 -420 2661 -448 2663 -404 2695 -382 2715 -380 371 -2740 355 -2744 2679 -384 391 -2728 2675 -388 379 +RAW_Data: -2728 2695 -414 357 -2704 2705 -418 357 -21262 2673 -416 383 -2696 2709 -380 2703 -384 2699 -418 2671 -408 2695 -382 2713 -386 379 -2730 357 -2732 2695 -384 383 -2730 2679 -416 357 -2708 2701 -410 349 -2736 2697 -382 385 -21252 2669 -478 289 -2790 2647 -426 2651 -444 2653 -430 2659 -418 2695 -414 2681 -402 349 -2738 383 -2722 2677 -414 347 -2744 2691 -382 369 -2730 2691 -384 383 -2734 2679 -414 347 -21264 2705 -386 379 -2736 2667 -410 2695 -382 2715 -380 2709 -420 2665 -392 2713 -382 383 -2730 365 -2728 2665 -418 383 -2696 2693 -418 357 -2710 2711 -380 375 -2718 2701 -416 357 -21238 2677 -484 311 -2766 2635 -444 2657 -420 2663 -422 2695 -416 2667 -428 2675 -396 363 -73890 133 -98 131 -132 129 -658 99 -66 853 -100 63 -100 361 -98 1589 -66 1231 -132 65 -100 297 -198 65 -132 265 -66 9857 -4672 165 -1030 97 -1394 65 -200 2687 -68 6873 -8336 99 -1156 97 -66 163 -232 163 -262 197 -132 295 -132 263 -166 953 -100 263 -130 393 -164 295 -64 329 -66 393 -164 823 -130 165 -66 6133 -8436 165 -164 265 -266 65 -362 197 -696 3181 -132 363 -98 65 -166 131 -66 399 -132 663 -396 329 -66 7335 -7578 497 -230 627 -264 99 -366 99 -132 131 -134 265 -498 163 -100 1323 -66 265 -66 1129 -100 399 -132 365 -100 795 -68 397 -98 597 -364 297 -132 361 -132 265 -132 8591 -4740 65 -100 131 -166 199 -1088 97 -296 99 -528 131 -98 661 -66 401 -198 1157 -166 361 -164 495 -100 165 -66 297 -100 1423 -66 3067 -5658 67 -6406 197 -1092 65 -530 659 -68 265 -100 991 -68 231 -230 297 -66 327 -66 131 -132 659 -134 131 -100 1183 -132 263 -98 621 -66 2075 -6976 65 -5138 67 -132 129 -664 67 -132 165 -100 331 -466 231 -68 467 -98 563 -66 231 -100 531 -66 465 -66 1023 -166 297 -134 3409 -12290 67 -164 99 -532 133 -166 263 -66 231 -66 721 -64 131 -68 959 -134 495 -100 299 -98 497 -98 365 -100 397 -232 297 -98 531 -66 3029 -12216 265 -132 99 -364 199 -234 131 -66 431 -166 333 -166 397 -132 327 -100 395 -66 197 -132 395 -66 527 -98 295 -100 97 -98 789 -132 363 -132 297 -200 2815 -4914 65 -6620 65 -462 65 -134 297 -66 497 -264 231 -198 2773 -134 365 -100 831 -166 131 -100 297 -132 861 -132 299 -100 561 -66 1381 -6946 65 -5516 231 -266 97 -1362 1093 -68 1621 -134 165 -332 297 -98 361 -228 97 -132 797 -98 3487 -13224 229 -164 65 -132 913 -66 1123 -98 527 -134 929 -98 723 -100 12259 -270 165 -132 67 -132 165 -1326 99 -98 65 -1194 431 -66 695 -66 733 -134 197 +RAW_Data: -134 10801 -166 67 -6130 133 -198 231 -334 365 -98 229 -132 165 -68 231 -166 14501 -524 65 -328 131 -498 129 -1288 65 -494 163 -64 165 -66 527 -132 131 -132 1019 -198 129 -166 393 -198 65 -164 6411 -66 3255 -10642 65 -1320 165 -164 493 -492 559 -264 2555 -66 695 -66 1657 -164 855 -66 4001 -10526 97 -596 133 -298 67 -264 65 -300 65 -100 263 -166 231 -134 99 -100 2703 -68 13643 -4922 297 -100 65 -232 133 -198 331 -300 231 -66 331 -100 12047 -3872 97 -196 65 -494 329 -66 65 -890 97 -98 229 -164 195 -596 797 -66 861 -132 65 -66 231 -100 565 -66 65 -66 1297 -132 265 -66 363 -134 265 -364 297 -164 299 -134 297 -134 495 -98 11309 -3790 131 -1380 65 -758 65 -164 129 -460 65 -360 199 -100 563 -68 497 -198 363 -266 263 -100 165 -66 697 -66 1933 -13594 65 -762 1223 -132 1119 -196 361 -134 131 -100 793 -166 695 -68 231 -68 463 -66 11727 -4204 363 -264 131 -132 133 -1124 97 -100 163 -100 327 -100 331 -198 397 -66 397 -100 395 -100 163 -66 197 -564 1059 -7962 65 -100 65 -198 129 -362 99 -394 197 -296 495 -100 1357 -68 459 -66 593 -66 265 -68 301 -132 465 -66 231 -200 397 -66 397 -232 199 -298 12077 -4350 231 -796 363 -198 133 -264 65 -1132 597 -332 3295 -100 755 -98 231 -164 97 -264 459 -166 759 -164 3265 -12138 99 -232 99 -1228 1025 -100 393 -66 531 -132 693 -132 1063 -66 427 -64 297 -294 229 -98 9723 -5404 67 -466 99 -796 267 -98 201 -100 167 -264 461 -98 1415 -66 861 -66 267 -66 331 -134 1663 -66 2089 -7012 65 -100 101 -4804 431 -728 99 -100 65 -100 995 -134 165 -66 929 -100 65 -66 927 -100 1093 -168 99 -100 497 -66 665 -200 6517 -8312 165 -66 129 -66 559 -166 99 -430 65 -398 67 -66 593 -198 459 -132 261 -132 263 -130 723 -66 459 -100 325 -166 67 -198 559 -66 493 -66 11475 -3896 99 -266 99 -66 197 -1092 129 -198 361 -166 163 -98 263 -196 759 -100 265 -100 365 -630 4635 -12748 65 -1712 461 -100 497 -66 395 -98 265 -98 229 -164 529 -132 297 -66 565 -132 987 -132 8665 -2820 2265 -450 313 -2774 2643 -442 325 -2772 2665 -416 359 -2734 2667 -386 379 -21274 2657 -474 293 -2810 2619 -466 2613 -476 2629 -452 2663 -388 2683 -418 2705 -400 365 -2722 387 -2700 2697 -380 361 -2732 2691 -418 361 -2732 2667 -416 383 -2698 2697 -416 357 -21238 2715 -384 383 -2732 2685 -416 2667 -416 2695 -398 2671 -418 2687 -390 2713 -382 383 -2730 365 -2728 2661 -416 379 -2716 2685 -384 379 -2720 2703 -378 401 -2718 2671 +RAW_Data: 889 -130 325 -64 457 -560 165 -68 199 -170 67 -66 265 -132 133 -666 67 -166 431 -66 201 -98 297 -100 595 -66 199 -134 65 -100 795 -132 99 -168 501 -200 331 -132 265 -102 265 -134 423 -98 521 -226 65 -166 431 -134 99 -100 133 -464 195 -326 623 -100 673 -98 321 -200 65 -136 369 -166 65 -68 97 -166 165 -334 265 -102 231 -166 101 -170 65 -170 265 -136 931 -100 133 -134 563 -66 333 -100 427 -66 163 -390 231 -66 193 -130 461 -166 557 -100 99 -198 263 -100 197 -294 231 -232 299 -134 199 -170 267 -134 631 -98 235 -100 499 -68 463 -100 65 -134 335 -170 273 -134 297 -100 67 -66 197 -166 67 -134 301 -168 537 -470 99 -134 433 -132 199 -192 261 -100 523 -164 459 -132 259 -332 359 -64 227 -96 131 -132 687 -132 363 -136 329 -434 99 -334 133 -100 401 -132 233 -700 233 -170 337 -66 371 -68 233 -202 531 -266 731 -66 465 -100 167 -100 133 -232 335 -166 239 -102 367 -232 231 -100 167 -134 201 -136 301 -168 199 -300 231 -98 237 -134 233 -102 329 -132 261 -134 199 -66 265 -136 99 -170 167 -134 199 -166 167 -136 367 -298 197 -200 99 -166 469 -136 439 -66 303 -134 295 -100 433 -134 899 -266 363 -132 197 -160 555 -324 129 -96 97 -128 257 -132 97 -394 257 -98 195 -166 459 -332 395 -132 633 -134 301 -100 131 -332 169 -168 395 -166 263 -540 783 -100 287 -130 295 -96 225 -296 133 -98 99 -100 461 -164 545 -130 99 -66 301 -68 265 -100 235 -134 235 -70 333 -102 497 -66 233 -364 301 -170 103 -66 165 -336 733 -200 133 -100 263 -102 65 -136 465 -200 1035 -198 165 -170 67 -302 631 -100 429 -332 65 -128 129 -130 159 -128 159 -66 161 -96 325 -164 261 -100 197 -162 65 -96 99 -130 65 -102 333 -100 199 -98 389 -330 129 -128 229 -66 425 -366 229 -64 261 -100 227 -96 227 -526 301 -200 97 -66 699 -334 67 -100 399 -198 787 -98 297 -134 429 -100 3245 -64 527 -98 131 -526 633 -68 133 -302 1459 -164 971 -102 237 -136 1439 -266 1131 -66 599 -200 303 -332 325 -130 389 -166 371 -66 333 -102 65 -100 233 -234 327 -266 233 -166 297 -100 225 -130 163 -336 99 -596 199 -330 131 -66 331 -338 263 -358 197 -168 877 -66 227 -96 63 -130 263 -162 225 -290 197 -198 357 -132 297 -262 165 -456 227 -98 399 -296 95 -132 99 -98 457 -200 199 -168 535 -100 567 -134 327 -130 193 -130 683 -102 101 -132 233 -170 943 -166 827 -66 267 -102 503 -68 1325 -164 +RAW_Data: 1607 -68 233 -166 1167 -70 531 -134 335 -168 131 -66 299 -402 899 -66 461 -66 457 -98 953 -98 165 -66 293 -230 881 -64 393 -166 589 -66 289 -66 1093 -204 333 -98 2745 -132 2019 -170 925 -68 269 -102 1469 -136 2301 -68 1355 -100 527 -66 975 -68 1445 -98 2397 -100 1733 -66 703 -100 995 -100 135 -136 235 -202 167 -134 2071 -166 339 -170 201 -268 129 -66 465 -66 365 -100 197 -164 129 -98 161 -96 423 -66 675 -66 1543 -136 567 -200 767 -202 65 -100 1401 -66 623 -136 567 -234 67 -236 197 -194 97 -66 263 -66 1827 -392 1893 -98 165 -268 133 -132 231 -162 225 -98 695 -198 563 -100 301 -332 267 -102 341 -66 99 -132 1299 -130 525 -68 161 -96 357 -98 353 -100 131 -100 131 -98 163 -132 323 -100 535 -66 1323 -130 133 -66 235 -134 1497 -132 387 -98 129 -162 2623 -134 163 -68 167 -66 959 -232 495 -68 131 -134 867 -134 865 -66 333 -98 305 -134 231 -98 765 -198 397 -432 165 -66 165 -366 265 -102 541 -100 261 -162 331 -134 457 -66 491 -196 97 -266 193 -262 65 -166 231 -266 497 -360 263 -98 587 -164 259 -98 231 -66 359 -100 267 -102 271 -168 97 -262 63 -66 261 -130 227 -130 295 -164 65 -66 265 -200 597 -134 267 -170 603 -100 97 -466 231 -264 97 -168 99 -66 65 -200 199 -100 267 -404 303 -102 201 -204 235 -134 131 -198 335 -298 327 -130 291 -164 63 -162 295 -262 197 -130 95 -130 195 -96 159 -130 161 -66 231 -100 165 -66 199 -134 363 -66 267 -168 165 -168 167 -100 165 -530 363 -432 99 -232 65 -132 395 -328 229 -98 197 -132 161 -96 191 -292 197 -204 133 -100 399 -166 531 -332 235 -168 99 -66 325 -158 553 -132 129 -226 231 -134 99 -462 129 -64 289 -100 193 -66 355 -164 291 -198 131 -298 197 -198 373 -268 335 -234 427 -68 199 -132 267 -232 131 -66 783 -326 63 -162 161 -130 227 -66 259 -562 233 -464 303 -102 201 -334 301 -134 297 -198 229 -66 127 -166 99 -100 197 -198 571 -66 457 -134 361 -424 131 -328 163 -98 63 -100 505 -102 201 -1094 229 -164 65 -230 789 -236 2505 -166 201 -170 163 -64 1139 -66 927 -100 295 -198 723 -100 365 -66 459 -196 3033 -272 199 -66 499 -202 1319 -232 295 -298 131 -362 97 -164 129 -132 65 -98 197 -130 129 -98 261 -130 97 -98 229 -96 425 -66 227 -166 483 -66 163 -326 567 -68 235 -68 67 -66 167 -66 235 -330 425 -164 63 -66 427 -102 167 -66 669 -132 429 -200 65 -102 133 -100 197 -368 +RAW_Data: 65 -134 2481 -228 65 -130 229 -228 763 -136 603 -166 1619 -98 1763 -102 837 -166 321 -66 951 -130 2067 -66 259 -132 1835 -66 437 -102 701 -66 565 -68 363 -70 1113 -66 1989 -164 257 -128 351 -162 1055 -232 265 -170 309 -200 435 -166 833 -102 2467 -132 595 -66 773 -166 1615 -98 131 -96 485 -64 517 -166 197 -68 1231 -68 403 -100 263 -134 233 -100 503 -100 333 -266 729 -66 199 -100 369 -68 1239 -100 197 -68 299 -170 337 -100 825 -132 163 -66 4205 -64 161 -100 635 -66 907 -66 1017 -166 1709 -100 201 -266 657 -68 463 -166 331 -164 293 -64 259 -162 129 -262 597 -134 701 -136 67 -168 235 -136 303 -170 1417 -66 263 -98 857 -100 659 -166 97 -100 2497 -64 2495 -98 719 -128 227 -130 2217 -164 623 -264 719 -134 329 -98 1371 -100 553 -294 165 -66 1163 -100 329 -196 649 -200 1123 -68 263 -100 593 -266 333 -102 1133 -136 131 -132 603 -200 1819 -66 489 -66 563 -266 1113 -230 165 -66 423 -68 335 -100 101 -100 1073 -132 897 -100 101 -100 499 -134 173 -138 763 -238 371 -130 403 -166 203 -102 271 -136 269 -166 99 -168 263 -96 425 -66 331 -234 133 -400 231 -132 453 -66 459 -164 199 -68 237 -132 163 -198 161 -196 265 -132 65 -64 195 -130 357 -164 663 -68 167 -600 131 -98 133 -304 203 -134 433 -98 261 -130 199 -100 237 -100 229 -326 99 -98 331 -132 99 -294 165 -66 303 -134 99 -232 133 -136 99 -68 267 -198 233 -138 67 -166 367 -100 333 -168 267 -200 369 -266 135 -404 1939 -132 231 -160 161 -64 293 -98 331 -132 339 -104 135 -100 197 -430 263 -202 233 -64 195 -162 129 -64 227 -298 265 -68 697 -66 301 -68 231 -300 131 -368 769 -234 265 -98 195 -324 97 -752 229 -126 355 -98 257 -98 287 -64 427 -132 295 -262 197 -170 369 -102 267 -100 169 -68 201 -102 2551 -136 635 -134 639 -134 99 -132 197 -200 371 -66 731 -132 199 -138 733 -304 433 -68 729 -440 197 -68 99 -102 165 -266 261 -164 491 -296 489 -194 257 -164 133 -134 237 -68 335 -98 227 -130 229 -98 295 -98 231 -202 267 -236 233 -136 331 -130 195 -128 261 -430 261 -162 97 -224 99 -130 193 -96 197 -162 229 -396 97 -98 227 -364 267 -100 99 -100 233 -236 697 -164 227 -196 63 -98 327 -230 325 -66 129 -196 95 -98 195 -130 325 -430 131 -194 129 -454 161 -196 235 -68 433 -134 667 -164 355 -236 101 -98 2143 -134 1827 -198 63 -198 65 -64 2859 -64 619 -66 97 -130 3157 -66 679 -194 1491 -98 +RAW_Data: 951 -64 393 -100 955 -132 4715 -100 131 -66 199 -204 1541 -66 929 -130 1347 -166 665 -132 233 -132 67 -102 433 -100 595 -228 997 -66 505 -68 133 -98 231 -68 571 -134 1371 -232 231 -270 135 -102 97 -66 867 -100 269 -68 967 -100 1649 -66 65 -66 951 -68 65 -202 363 -200 779 -102 1449 -294 419 -130 361 -230 1079 -164 163 -260 893 -102 333 -100 533 -166 467 -100 135 -66 135 -202 369 -100 199 -100 269 -134 301 -166 229 -66 101 -134 199 -134 1293 -64 779 -62 831 -66 1243 -68 267 -102 197 -100 395 -98 455 -64 621 -132 877 -98 199 -100 2101 -134 503 -100 2035 -134 735 -236 475 -136 237 -132 133 -134 1229 -100 133 -66 167 -68 2655 -100 1807 -100 1095 -264 825 -98 163 -66 491 -98 161 -128 953 -100 773 -100 131 -66 67 -134 457 -130 63 -64 389 -98 715 -66 425 -300 97 -100 1515 -66 303 -68 99 -98 721 -64 887 -132 65 -132 165 -66 635 -68 2801 -66 1561 -100 751 -98 129 -64 725 -136 201 -100 333 -204 573 -104 1745 -134 99 -66 129 -64 595 -134 167 -102 337 -134 567 -134 1131 -138 1207 -100 269 -68 135 -100 1143 -134 2139 -68 1701 -162 991 -596 431 -66 99 -132 657 -66 391 -320 357 -260 259 -98 429 -66 163 -228 65 -130 227 -66 261 -166 99 -98 131 -366 199 -134 463 -102 201 -98 231 -102 639 -238 301 -568 169 -610 265 -102 841 -198 297 -100 335 -132 263 -266 265 -68 469 -134 267 -68 933 -298 333 -298 729 -168 135 -136 437 -132 1137 -134 199 -68 265 -132 463 -166 129 -130 227 -98 297 -98 65 -132 97 -202 199 -232 305 -66 165 -198 365 -66 99 -98 299 -170 65 -136 301 -232 99 -564 133 -132 233 -170 99 -102 131 -134 65 -204 101 -98 297 -98 167 -762 233 -298 99 -326 395 -66 299 -132 369 -504 333 -98 483 -200 457 -164 63 -164 329 -162 65 -622 231 -268 131 -132 133 -134 131 -134 131 -66 99 -100 231 -66 167 -336 165 -98 197 -100 97 -264 321 -98 521 -132 163 -130 129 -294 297 -134 101 -102 265 -168 497 -68 197 -68 499 -134 269 -398 267 -130 203 -302 65 -498 271 -136 465 -292 131 -294 163 -198 329 -96 129 -98 193 -130 391 -330 165 -134 167 -170 297 -102 133 -136 135 -366 199 -132 423 -132 395 -168 65 -166 401 -98 229 -98 329 -98 99 -130 129 -228 261 -160 127 -426 389 -162 193 -132 131 -100 231 -168 67 -304 201 -68 765 -132 161 -162 193 -64 195 -64 295 -130 787 -98 419 -528 429 -66 363 -134 131 -100 133 -200 331 -98 +RAW_Data: 431 -66 1167 -68 937 -68 1003 -66 99 -132 941 -134 65 -66 365 -274 165 -236 367 -96 557 -134 675 -66 261 -164 127 -96 391 -164 161 -98 391 -292 163 -98 519 -196 165 -98 523 -66 195 -160 3343 -66 661 -100 2589 -136 307 -100 629 -136 639 -100 133 -168 405 -100 267 -66 465 -132 1171 -64 749 -64 165 -98 983 -100 163 -202 537 -66 327 -100 669 -100 401 -236 2885 -164 439 -134 97 -426 1931 -66 1385 -98 715 -98 519 -66 289 -162 97 -360 297 -166 163 -66 289 -66 555 -334 167 -230 429 -102 267 -132 943 -136 401 -68 929 -130 193 -68 467 -198 335 -66 963 -100 597 -132 197 -260 523 -232 1115 -102 1935 -66 1395 -134 305 -100 99 -66 199 -66 1071 -66 2357 -66 367 -498 769 -234 163 -130 191 -64 1211 -200 133 -102 201 -100 561 -366 361 -98 195 -100 537 -64 165 -196 1041 -332 133 -102 441 -230 4217 -66 1033 -66 167 -66 933 -100 565 -66 331 -164 673 -104 441 -66 533 -66 2095 -164 525 -66 297 -170 965 -198 421 -100 663 -832 65 -100 331 -164 231 -166 135 -168 237 -466 761 -134 891 -196 791 -198 257 -160 161 -98 293 -66 1081 -98 229 -130 327 -66 1301 -200 331 -166 101 -66 461 -100 2619 -132 1663 -98 1609 -134 499 -332 165 -370 67 -264 97 -96 259 -98 701 -402 197 -128 527 -236 233 -102 167 -134 303 -134 99 -166 299 -132 165 -200 467 -68 305 -168 207 -102 465 -102 729 -136 101 -374 327 -96 259 -98 467 -202 65 -66 673 -98 335 -404 135 -66 339 -204 99 -366 233 -68 365 -166 133 -102 867 -198 163 -162 163 -294 463 -332 165 -68 269 -268 331 -100 131 -166 299 -132 231 -400 263 -164 131 -266 267 -264 367 -66 371 -134 229 -104 267 -232 67 -466 265 -100 101 -100 165 -200 65 -200 301 -66 199 -168 233 -98 267 -66 67 -134 261 -196 261 -234 427 -294 65 -194 193 -66 259 -132 849 -96 63 -198 167 -294 95 -98 361 -164 261 -196 131 -132 437 -100 597 -262 327 -162 295 -98 295 -164 259 -196 425 -230 321 -66 195 -66 261 -496 99 -200 529 -132 133 -966 133 -132 165 -66 63 -128 491 -402 65 -262 299 -66 299 -202 265 -100 99 -668 97 -134 65 -100 101 -66 65 -266 691 -66 431 -166 167 -134 199 -370 899 -134 99 -100 1093 -166 163 -166 399 -98 327 -100 99 -168 135 -200 133 -202 429 -98 65 -98 197 -556 65 -66 97 -326 331 -166 333 -200 135 -100 235 -234 265 -98 65 -68 135 -66 335 -66 133 -298 99 -66 233 -164 435 -232 97 -132 97 -392 +RAW_Data: 99 -198 819 -66 1235 -98 321 -132 1091 -66 1307 -98 3059 -164 3305 -64 227 -98 591 -98 129 -66 229 -98 2143 -98 939 -68 563 -100 361 -232 945 -164 257 -96 229 -230 387 -64 195 -130 981 -294 587 -162 193 -98 1337 -66 293 -98 2665 -66 297 -98 647 -66 459 -132 491 -164 489 -96 595 -66 899 -66 837 -64 1151 -196 259 -98 357 -164 891 -132 1359 -134 197 -98 97 -98 261 -64 229 -96 461 -136 693 -100 201 -98 865 -66 599 -100 517 -132 709 -66 293 -298 655 -66 197 -130 129 -66 197 -98 4291 -66 673 -66 667 -132 1473 -132 133 -104 99 -66 163 -168 333 -134 1743 -132 1097 -132 99 -68 167 -602 1323 -352 99 -166 753 -98 423 -98 97 -66 1317 -228 1309 -98 1849 -66 1939 -132 601 -100 665 -100 1875 -66 695 -132 425 -66 425 -66 263 -134 165 -134 99 -98 829 -66 601 -166 131 -102 565 -66 301 -100 1099 -100 601 -138 533 -66 667 -234 561 -66 99 -68 2741 -98 199 -100 531 -168 101 -434 1027 -68 431 -66 403 -132 99 -98 565 -132 135 -100 399 -166 271 -236 233 -166 197 -366 99 -66 99 -168 503 -66 199 -170 207 -100 673 -368 99 -66 263 -168 133 -98 397 -268 337 -66 131 -132 231 -132 501 -134 99 -168 567 -138 103 -136 267 -298 231 -134 197 -160 321 -332 231 -98 131 -164 257 -64 163 -328 395 -66 331 -202 65 -168 133 -68 167 -100 233 -102 335 -66 197 -326 1101 -132 589 -100 811 -132 399 -136 269 -102 497 -66 559 -100 129 -98 855 -68 637 -102 65 -200 875 -68 233 -166 167 -66 529 -202 235 -102 231 -66 1237 -66 733 -98 1723 -132 101 -100 297 -66 829 -232 197 -100 367 -134 169 -166 167 -434 633 -100 235 -200 131 -134 233 -100 131 -100 331 -134 495 -432 65 -528 161 -130 295 -132 337 -136 133 -166 165 -100 269 -240 201 -336 133 -166 165 -238 199 -202 431 -434 99 -134 501 -166 231 -96 559 -202 167 -66 717 -98 987 -198 65 -64 163 -64 227 -98 555 -164 199 -64 361 -66 163 -98 129 -162 97 -130 161 -460 197 -230 681 -98 197 -98 329 -100 267 -266 291 -264 65 -100 329 -100 459 -200 363 -98 165 -134 231 -134 301 -134 231 -302 99 -132 101 -134 267 -136 233 -68 393 -422 163 -166 361 -166 99 -134 365 -134 133 -336 401 -66 495 -132 401 -168 133 -402 501 -136 1093 -862 165 -132 293 -300 289 -66 131 -164 391 -134 99 -360 359 -130 323 -200 423 -98 195 -162 295 -132 161 -98 129 -782 131 -426 227 -64 259 -166 63 -160 323 -98 261 -230 +RAW_Data: 231 -66 921 -66 355 -64 1019 -98 227 -258 163 -66 597 -232 1313 -132 163 -404 467 -236 901 -164 483 -98 195 -96 489 -134 103 -238 169 -66 67 -68 299 -100 497 -68 65 -134 1635 -304 1153 -100 539 -168 265 -200 499 -166 535 -100 397 -168 931 -100 131 -66 631 -134 897 -270 1233 -100 65 -132 131 -334 663 -66 163 -66 131 -132 705 -98 571 -200 433 -100 237 -234 229 -132 1627 -66 569 -100 715 -66 1863 -272 265 -68 301 -98 465 -68 97 -134 99 -66 395 -136 1405 -66 529 -132 63 -196 579 -132 413 -260 129 -136 101 -166 1201 -134 833 -134 393 -66 335 -172 201 -68 1027 -96 753 -64 815 -66 97 -64 1341 -132 289 -160 127 -66 99 -228 1083 -96 163 -66 259 -64 159 -98 2409 -168 767 -200 367 -66 1675 -66 1067 -98 3407 -200 99 -66 1403 -166 99 -134 439 -200 329 -136 599 -66 637 -66 835 -66 1099 -98 99 -66 463 -166 165 -100 461 -164 3037 -66 655 -66 97 -98 229 -130 355 -132 1443 -66 527 -98 881 -98 229 -162 127 -96 583 -64 65 -162 489 -166 885 -194 257 -98 1539 -66 293 -166 229 -132 655 -98 757 -49522 271 -758 689 -1264 737 -670 293 -1152 811 -1144 341 -664 773 -678 327 -1118 807 -1144 835 -1146 781 -1126 873 -1096 347 -622 877 -624 321 -1106 843 -1098 871 -1098 843 -1106 379 -610 841 -584 381 -1122 365 -602 845 -1116 837 -610 381 -1056 889 -1078 383 -614 827 -1110 877 -592 353 -1108 845 -1120 839 -1120 347 -602 849 -1110 865 -612 361 -1072 869 -1114 351 -618 861 -618 343 -1090 853 -1106 387 -618 797 -674 347 -1084 389 -574 867 -584 381 -1114 841 -1102 845 -1116 839 -1112 843 -1098 875 -1086 383 -584 865 -588 375 -1100 861 -1112 851 -1084 853 -1108 847 -1106 381 -584 857 -610 383 -1080 357 -602 871 -602 385 -1084 383 -616 823 -610 373 -1086 381 -590 871 -1084 839 -628 353 -1102 875 -1100 349 -9404 875 -1060 871 -1086 887 -1088 879 -1058 863 -1086 855 -1132 845 -1078 871 -1076 857 -1098 881 -1082 861 -1088 843 -1120 853 -1074 879 -1074 879 -1068 889 -614 341 -1090 387 -616 863 -624 345 -1088 391 -590 857 -612 385 -1058 393 -596 843 -1088 889 -1078 879 -578 387 -1082 875 -1076 415 -550 881 -1070 877 -592 391 -1114 821 -1104 373 -620 821 -624 361 -1072 903 -1086 855 -1092 843 -1086 905 -1054 387 -614 863 -618 347 -1088 853 -1114 845 -1090 867 -1070 381 -610 885 -584 385 -1052 407 -578 877 -1052 899 -600 389 -1048 907 -1074 383 -586 877 -1072 877 -594 359 -1076 875 -1082 891 -1088 363 -616 855 -1084 857 -592 381 -1088 883 -1086 385 -572 +RAW_Data: 889 -624 353 -1082 853 -1096 379 -594 853 -624 353 -1092 417 -582 847 -612 385 -1076 847 -1080 883 -1052 913 -1044 907 -1076 849 -1088 383 -602 867 -616 361 -1068 901 -1072 865 -1104 831 -1080 879 -1098 397 -586 855 -626 355 -1084 381 -592 873 -616 351 -1084 385 -624 821 -620 359 -1086 387 -584 883 -1086 877 -592 355 -1106 853 -1086 387 -69570 97 -100 99 -2620 131 -636 333 -102 235 -236 67 -68 363 -66 201 -100 567 -102 267 -164 101 -134 65 -68 197 -68 297 -166 671 -100 469 -336 165 -100 201 -66 169 -230 169 -204 329 -624 67 -98 265 -232 193 -168 299 -100 235 -138 101 -370 165 -294 333 -622 231 -130 129 -130 353 -132 195 -162 359 -164 67 -68 333 -100 133 -688 235 -236 497 -198 293 -98 129 -296 293 -164 229 -128 229 -132 193 -400 165 -66 163 -98 361 -164 355 -196 587 -164 131 -98 263 -554 99 -130 129 -130 191 -464 99 -132 67 -100 167 -604 329 -66 199 -68 133 -102 163 -66 2971 -132 785 -66 329 -96 323 -100 201 -136 301 -66 1959 -166 867 -134 467 -66 297 -100 835 -100 753 -166 165 -64 67 -370 335 -66 559 -232 165 -334 65 -162 129 -354 163 -64 131 -134 265 -300 263 -132 267 -296 327 -198 99 -132 535 -132 469 -866 231 -860 99 -232 503 -134 99 -198 233 -134 267 -200 97 -358 297 -164 259 -98 227 -166 135 -66 323 -100 97 -294 131 -164 129 -98 295 -96 129 -426 299 -100 67 -102 623 -100 163 -194 127 -360 563 -134 199 -428 493 -98 229 -130 257 -64 165 -100 131 -98 163 -692 357 -64 161 -98 321 -64 389 -230 65 -692 227 -130 261 -132 231 -162 287 -298 97 -460 393 -130 301 -168 331 -100 269 -202 101 -134 201 -102 99 -132 199 -204 235 -664 65 -562 133 -328 463 -100 291 -194 159 -162 227 -98 293 -328 165 -128 227 -574 535 -332 197 -168 65 -300 131 -66 389 -1078 131 -64 259 -64 223 -98 257 -164 63 -328 433 -134 65 -602 131 -68 333 -136 369 -66 297 -264 427 -66 97 -130 429 -102 133 -136 203 -240 167 -236 329 -526 67 -132 133 -168 331 -360 65 -66 331 -296 267 -134 469 -132 595 -230 661 -662 299 -100 265 -200 203 -168 801 -100 133 -68 399 -132 99 -100 161 -390 65 -298 65 -98 261 -130 161 -128 257 -66 67 -134 621 -98 227 -328 99 -230 129 -294 193 -96 195 -318 425 -526 129 -196 163 -162 65 -132 293 -130 63 -66 325 -128 63 -130 293 -66 199 -200 269 -206 133 -198 325 -98 163 -100 97 -98 261 -164 67 -98 167 -430 131 -494 131 -164 +RAW_Data: 97 -98 861 -66 1199 -166 231 -100 651 -166 197 -104 439 -98 131 -64 493 -98 883 -96 99 -98 3327 -66 131 -264 733 -134 2133 -166 131 -102 303 -136 535 -134 701 -98 355 -228 131 -202 99 -134 99 -100 791 -166 169 -202 671 -100 741 -100 263 -66 165 -68 935 -132 197 -198 673 -100 605 -66 1457 -98 1195 -166 2347 -134 505 -100 1469 -66 391 -100 229 -100 1171 -98 939 -100 459 -170 369 -134 231 -162 127 -98 95 -66 195 -98 195 -66 299 -100 331 -98 65 -232 369 -132 201 -68 167 -166 1481 -102 501 -160 1257 -66 2307 -64 623 -164 2079 -66 1101 -98 423 -64 659 -68 431 -136 99 -100 435 -130 167 -168 835 -200 135 -104 133 -100 503 -68 1437 -232 821 -132 357 -96 463 -66 263 -64 683 -132 165 -96 655 -166 3939 -100 1169 -132 2443 -98 197 -132 425 -234 233 -162 1043 -66 197 -100 2793 -134 167 -104 675 -100 197 -134 1367 -102 763 -132 265 -230 133 -102 365 -100 167 -66 1069 -66 837 -100 295 -160 97 -64 129 -132 617 -164 197 -100 133 -136 337 -172 133 -66 557 -98 951 -66 263 -130 587 -66 729 -196 335 -166 933 -432 369 -100 199 -296 225 -98 355 -66 129 -64 557 -98 289 -66 355 -128 193 -162 267 -134 299 -98 165 -170 303 -640 1031 -134 99 -66 135 -68 771 -166 171 -104 201 -134 131 -68 635 -428 661 -292 749 -430 1161 -100 905 -98 65 -98 657 -262 2837 -132 67 -66 265 -132 631 -66 1037 -296 97 -98 1703 -302 367 -100 505 -232 497 -362 333 -134 591 -100 755 -232 67 -130 587 -66 231 -168 65 -332 99 -66 267 -232 393 -134 65 -132 131 -428 133 -200 165 -202 199 -168 165 -102 269 -100 333 -852 201 -134 233 -202 65 -200 563 -768 265 -136 169 -102 169 -598 333 -202 267 -134 267 -328 163 -130 625 -500 199 -200 99 -270 65 -134 65 -198 65 -100 99 -596 493 -66 99 -66 331 -232 103 -136 373 -168 831 -170 65 -672 163 -102 133 -136 331 -100 333 -234 101 -100 99 -200 99 -100 201 -302 199 -600 301 -202 135 -134 705 -166 435 -530 97 -198 131 -198 195 -66 163 -392 293 -66 295 -370 229 -198 65 -100 405 -134 165 -134 133 -170 337 -236 205 -274 267 -134 329 -132 195 -132 503 -132 133 -136 133 -334 197 -196 299 -168 101 -100 233 -100 439 -134 301 -332 331 -298 433 -406 433 -68 167 -100 203 -100 101 -102 99 -328 397 -234 205 -168 133 -364 63 -202 397 -198 95 -394 267 -134 569 -66 201 -102 133 -136 101 -102 99 -132 99 -196 197 -498 197 -102 135 -170 +RAW_Data: 331 -164 63 -162 1267 -66 163 -130 129 -66 725 -164 231 -64 853 -66 101 -134 199 -102 99 -68 365 -66 357 -130 815 -64 357 -98 97 -98 97 -66 65 -466 231 -172 3749 -66 849 -130 917 -64 327 -64 1013 -98 555 -332 795 -100 571 -132 769 -132 401 -134 1297 -134 377 -138 435 -100 401 -100 667 -100 1761 -66 667 -66 1533 -236 233 -98 885 -130 457 -66 999 -66 165 -66 833 -134 695 -166 501 -66 499 -200 329 -64 197 -134 441 -100 2099 -98 491 -134 197 -130 2225 -132 65 -100 689 -64 193 -160 159 -96 195 -98 323 -164 259 -98 535 -472 771 -66 665 -270 665 -66 595 -266 2191 -64 643 -98 1287 -98 741 -100 233 -200 569 -194 261 -68 637 -100 97 -66 491 -158 395 -138 1017 -66 627 -262 559 -64 327 -98 263 -134 99 -102 201 -102 337 -66 167 -68 679 -100 471 -134 195 -66 133 -202 693 -96 197 -98 391 -164 99 -98 3883 -194 461 -100 237 -168 1891 -68 301 -68 969 -166 1439 -294 551 -130 389 -98 99 -196 167 -102 505 -66 569 -234 901 -98 407 -136 469 -66 769 -98 769 -166 1263 -266 297 -98 1701 -200 203 -168 329 -232 65 -100 329 -164 803 -100 135 -200 233 -166 135 -272 265 -134 197 -100 133 -134 539 -232 197 -396 165 -366 263 -68 233 -102 365 -132 233 -100 135 -266 199 -234 167 -232 97 -524 127 -128 389 -98 305 -364 261 -130 257 -162 589 -464 361 -66 229 -134 161 -100 203 -432 265 -66 199 -66 199 -366 229 -236 99 -134 99 -100 131 -168 133 -100 131 -236 267 -132 297 -264 291 -132 167 -234 65 -100 199 -66 333 -730 237 -440 365 -102 99 -100 99 -132 99 -100 1429 -134 427 -100 97 -100 131 -164 799 -170 1077 -100 431 -66 133 -168 737 -134 197 -230 65 -102 803 -132 491 -98 429 -198 471 -134 365 -66 299 -236 65 -66 2837 -102 399 -64 585 -64 523 -196 97 -98 295 -196 555 -160 261 -500 299 -396 333 -236 133 -68 327 -100 199 -204 699 -66 701 -100 65 -164 65 -370 195 -196 97 -66 193 -130 129 -360 195 -130 231 -96 291 -64 455 -228 293 -196 291 -162 97 -194 621 -130 847 -66 395 -66 161 -128 193 -130 293 -98 231 -170 67 -134 297 -360 167 -266 263 -526 263 -132 229 -98 191 -160 159 -100 721 -234 101 -100 99 -130 259 -258 265 -632 687 -164 133 -134 631 -100 199 -102 165 -560 299 -200 265 -332 431 -870 99 -266 503 -364 135 -66 269 -68 499 -100 265 -102 263 -102 569 -234 719 -132 99 -196 419 -262 163 -688 95 -66 165 -128 95 -66 +RAW_Data: 295 -98 987 -196 517 -100 489 -66 355 -132 563 -198 867 -134 1413 -134 541 -134 767 -100 193 -98 1799 -102 467 -134 299 -96 323 -66 261 -100 259 -66 229 -96 851 -66 369 -266 469 -66 101 -98 163 -136 267 -432 859 -130 523 -66 197 -134 1027 -132 227 -194 393 -98 807 -166 235 -100 133 -66 165 -102 133 -136 371 -162 1411 -132 865 -200 471 -100 133 -68 299 -66 633 -98 329 -234 401 -98 1505 -132 133 -134 331 -262 163 -66 261 -98 289 -64 201 -68 1055 -96 391 -66 951 -298 265 -202 297 -66 401 -68 131 -100 1733 -98 941 -66 803 -98 847 -64 3701 -100 721 -160 357 -166 1799 -66 329 -100 99 -102 363 -198 167 -136 197 -66 567 -66 199 -236 1247 -166 2455 -68 1107 -200 235 -100 2355 -130 913 -98 877 -98 163 -196 97 -66 427 -100 801 -134 867 -98 263 -68 441 -134 561 -98 1671 -134 865 -68 935 -132 163 -102 975 -66 1343 -132 1339 -134 369 -100 1107 -66 1167 -168 631 -232 835 -66 1027 -132 333 -166 265 -98 1207 -98 223 -98 455 -64 2095 -134 933 -136 233 -68 335 -136 305 -100 1737 -66 427 -100 263 -130 323 -66 227 -66 717 -100 265 -100 65 -128 355 -66 367 -132 95 -230 229 -100 131 -64 493 -132 291 -396 393 -130 259 -196 227 -288 397 -68 229 -430 99 -302 237 -700 65 -66 65 -100 133 -200 101 -336 133 -166 237 -202 67 -302 67 -68 333 -132 263 -102 267 -296 163 -166 233 -168 363 -64 295 -298 537 -166 431 -200 431 -166 63 -258 363 -164 563 -234 199 -68 299 -100 325 -754 295 -196 65 -98 165 -132 301 -134 131 -134 97 -68 405 -68 233 -134 271 -134 67 -168 101 -136 133 -366 99 -132 67 -132 265 -200 233 -100 201 -136 101 -66 263 -132 129 -66 293 -582 263 -132 1103 -134 203 -168 97 -66 197 -264 131 -168 133 -132 65 -134 199 -134 101 -100 131 -436 99 -232 97 -398 231 -362 65 -202 301 -396 297 -98 199 -134 265 -164 101 -168 267 -102 405 -170 99 -102 397 -132 97 -98 295 -98 1179 -100 135 -136 131 -134 765 -134 465 -168 439 -232 403 -100 65 -134 931 -100 169 -136 237 -68 231 -234 199 -68 401 -134 541 -166 429 -166 1607 -368 533 -66 363 -66 133 -134 433 -166 297 -238 201 -100 201 -170 199 -134 273 -136 99 -134 167 -238 133 -66 265 -134 165 -132 165 -132 97 -228 723 -198 415 -64 491 -298 257 -66 231 -192 225 -96 227 -98 193 -96 521 -198 65 -66 231 -166 163 -98 465 -66 133 -132 195 -130 225 -162 521 -130 63 -66 199 -228 +RAW_Data: 817 -162 449 -160 719 -198 469 -68 133 -68 1101 -132 593 -230 1105 -100 131 -134 231 -66 329 -196 685 -96 557 -68 1263 -68 101 -68 397 -100 65 -66 625 -66 97 -132 1099 -66 493 -66 757 -98 1151 -66 303 -134 1901 -66 99 -100 665 -262 991 -98 791 -66 1925 -168 865 -232 835 -98 505 -102 99 -100 535 -100 169 -134 427 -132 863 -68 167 -134 975 -100 133 -268 1339 -100 1453 -66 1445 -162 195 -64 3623 -66 237 -68 1063 -308 1449 -98 1111 -132 167 -102 855 -270 199 -134 297 -134 267 -168 863 -234 637 -66 567 -230 99 -200 3325 -198 845 -66 289 -66 131 -66 815 -130 1093 -100 167 -100 429 -98 1703 -166 195 -64 971 -98 163 -192 195 -168 439 -132 329 -132 67 -134 67 -134 1591 -168 407 -100 867 -68 399 -134 661 -100 663 -66 237 -136 395 -232 131 -66 695 -100 627 -264 913 -66 1083 -98 287 -66 199 -132 335 -100 1031 -68 99 -100 3815 -98 165 -66 129 -98 163 -128 563 -98 779 -96 223 -64 161 -164 2025 -66 1741 -172 101 -136 203 -102 665 -100 475 -64 167 -100 637 -98 997 -170 1207 -136 233 -166 233 -168 635 -132 199 -100 235 -270 199 -98 131 -102 169 -170 293 -98 323 -164 427 -334 233 -168 267 -68 369 -100 263 -368 101 -66 665 -98 265 -100 133 -100 99 -168 133 -66 133 -132 133 -66 269 -134 435 -68 267 -136 271 -500 163 -100 163 -166 355 -132 97 -98 323 -194 63 -688 463 -130 97 -396 65 -100 357 -194 461 -98 161 -130 223 -162 165 -352 461 -300 267 -166 233 -464 329 -100 293 -362 163 -228 289 -66 229 -66 195 -162 325 -66 261 -98 127 -424 299 -302 367 -68 265 -272 429 -98 161 -98 393 -296 65 -130 161 -196 261 -66 473 -234 97 -98 263 -160 323 -98 67 -132 697 -298 99 -134 233 -202 97 -134 301 -200 307 -100 101 -134 865 -166 231 -202 233 -100 301 -170 169 -102 169 -200 65 -98 595 -166 231 -234 661 -66 473 -334 165 -304 365 -266 97 -502 363 -134 133 -236 65 -100 99 -134 99 -170 235 -66 333 -100 195 -100 133 -300 133 -102 301 -304 65 -100 99 -100 131 -202 135 -134 65 -200 363 -66 263 -498 67 -68 295 -194 321 -368 435 -100 97 -664 99 -100 569 -66 133 -66 67 -134 199 -136 101 -68 301 -68 405 -198 133 -132 581 -132 165 -98 159 -98 197 -66 229 -130 131 -294 133 -96 423 -100 427 -300 357 -132 291 -64 95 -194 455 -98 263 -100 359 -196 65 -162 227 -162 157 -96 157 -230 589 -132 325 -134 535 -66 267 -100 135 -302 +RAW_Data: 131 -134 599 -166 393 -98 369 -236 197 -100 401 -232 569 -134 135 -70 337 -134 101 -136 135 -100 1895 -66 401 -170 503 -66 1633 -66 601 -66 355 -96 683 -100 729 -68 133 -132 433 -68 569 -100 133 -68 201 -132 835 -100 465 -68 527 -98 193 -200 1129 -166 535 -100 199 -98 259 -132 227 -64 1597 -98 261 -192 753 -100 911 -66 667 -298 131 -100 263 -66 1051 -230 787 -66 935 -66 233 -98 885 -236 431 -66 197 -162 521 -68 167 -196 263 -96 589 -98 517 -66 1439 -64 777 -66 3219 -132 679 -134 205 -68 507 -198 749 -200 199 -168 167 -100 133 -134 201 -68 731 -66 495 -198 737 -66 237 -68 135 -100 167 -234 1535 -68 873 -66 373 -66 67 -232 297 -68 65 -66 1095 -68 327 -130 63 -132 1715 -66 2261 -100 321 -132 197 -164 457 -232 1291 -132 405 -68 1001 -68 1133 -272 471 -66 99 -134 1403 -68 167 -68 1091 -336 933 -134 1207 -132 265 -68 267 -66 99 -366 265 -66 1469 -258 367 -168 429 -132 129 -66 491 -132 343 -100 65 -100 263 -136 199 -164 273 -204 791 -100 901 -66 167 -98 165 -64 559 -132 619 -132 1087 -128 2283 -398 1467 -164 259 -130 1927 -130 421 -98 1085 -66 705 -68 1843 -168 875 -170 203 -136 341 -640 199 -66 133 -554 161 -196 63 -66 521 -292 163 -160 95 -158 127 -192 197 -100 587 -130 397 -662 261 -66 193 -130 259 -66 361 -64 459 -98 197 -560 655 -130 389 -66 1135 -100 133 -130 131 -98 1011 -100 561 -66 685 -164 457 -132 2469 -200 609 -66 665 -66 67 -132 327 -200 1657 -134 919 -132 651 -100 327 -230 191 -130 263 -358 95 -130 549 -98 99 -68 299 -100 461 -132 99 -472 165 -134 99 -66 99 -132 399 -102 169 -102 697 -166 233 -132 333 -632 197 -164 865 -266 101 -68 533 -166 299 -100 163 -228 259 -66 327 -200 65 -66 229 -100 363 -230 197 -336 165 -102 893 -300 65 -132 231 -370 265 -230 99 -98 229 -518 199 -100 401 -724 225 -98 63 -96 231 -64 291 -292 65 -98 131 -98 159 -158 127 -194 161 -292 65 -98 133 -66 297 -66 303 -168 97 -168 231 -234 269 -532 135 -168 99 -168 301 -528 99 -506 199 -368 399 -132 329 -372 99 -68 133 -264 197 -100 201 -200 67 -134 131 -270 133 -134 133 -198 327 -200 65 -100 331 -262 161 -166 469 -534 167 -738 131 -100 367 -232 101 -100 265 -604 65 -170 99 -166 299 -102 169 -132 99 -398 229 -330 197 -166 335 -366 97 -98 131 -200 269 -100 199 -168 131 -134 537 -98 265 -100 335 -236 99 -366 +RAW_Data: 459 -100 453 -130 419 -130 519 -96 63 -130 2077 -66 767 -64 127 -134 1961 -296 529 -202 637 -134 527 -100 201 -68 633 -66 163 -360 1029 -68 765 -100 867 -66 503 -100 131 -66 841 -98 165 -68 237 -66 509 -100 501 -302 235 -66 99 -164 227 -130 551 -196 327 -66 1571 -132 99 -68 867 -66 163 -96 161 -130 129 -130 549 -130 487 -166 1801 -66 229 -66 197 -232 325 -66 425 -198 131 -64 295 -166 735 -66 533 -98 227 -130 129 -262 425 -100 263 -66 129 -132 97 -168 971 -170 405 -68 199 -134 475 -202 297 -98 1445 -98 395 -196 161 -66 225 -134 1803 -100 473 -102 1499 -66 199 -100 701 -132 165 -68 133 -102 303 -98 735 -102 805 -100 827 -100 235 -100 65 -266 637 -68 693 -66 1383 -228 819 -66 233 -304 435 -198 203 -136 1135 -270 1709 -64 227 -64 581 -134 505 -66 2203 -64 293 -64 753 -66 551 -132 747 -64 1303 -64 463 -66 229 -102 1877 -266 871 -166 1357 -64 819 -66 465 -198 693 -68 165 -64 95 -128 3785 -132 1465 -100 299 -102 329 -164 595 -134 1029 -66 299 -168 1263 -166 331 -68 967 -100 101 -102 603 -260 165 -132 467 -66 233 -66 235 -102 475 -100 135 -68 301 -134 297 -98 131 -102 269 -466 99 -134 237 -166 135 -168 203 -102 265 -68 503 -66 233 -66 637 -134 101 -200 199 -166 293 -554 361 -328 367 -264 533 -238 167 -68 135 -170 99 -300 591 -298 133 -236 299 -66 231 -368 263 -232 435 -136 133 -102 133 -200 133 -134 163 -134 167 -168 299 -66 265 -100 133 -240 135 -132 263 -170 269 -200 501 -396 263 -98 227 -132 129 -292 427 -66 165 -102 627 -602 99 -66 301 -168 199 -100 563 -330 165 -134 233 -136 65 -332 499 -100 131 -232 325 -96 65 -132 195 -98 393 -624 323 -68 133 -98 195 -162 231 -100 263 -132 231 -102 133 -236 99 -236 231 -166 65 -102 133 -268 101 -102 299 -136 267 -164 493 -64 229 -258 291 -326 263 -198 391 -134 167 -202 365 -594 133 -102 201 -134 503 -396 429 -204 169 -400 197 -170 267 -132 403 -466 297 -98 469 -234 395 -132 233 -100 165 -100 165 -66 197 -68 297 -166 501 -134 133 -100 65 -166 631 -68 297 -134 199 -100 165 -68 299 -266 133 -66 165 -100 231 -490 557 -134 371 -164 299 -170 733 -164 239 -334 335 -66 299 -300 199 -170 103 -100 233 -102 641 -168 65 -100 995 -66 265 -160 259 -130 129 -226 425 -100 355 -726 97 -688 99 -66 233 -266 299 -942 167 -102 167 -166 65 -100 367 -136 99 -134 199 -134 267 -164 +RAW_Data: 67 -68 233 -66 899 -66 163 -96 485 -98 355 -130 943 -100 235 -168 499 -104 1367 -98 297 -100 635 -68 1169 -100 67 -134 835 -264 959 -164 129 -98 419 -196 589 -66 421 -66 1717 -100 133 -100 265 -134 227 -356 455 -166 163 -66 1055 -100 1455 -134 463 -98 2191 -132 295 -132 335 -66 709 -64 619 -98 959 -68 835 -170 603 -134 1033 -134 635 -168 759 -232 397 -198 397 -164 1267 -166 257 -198 1295 -100 239 -104 563 -204 335 -198 203 -68 901 -68 1255 -134 1697 -66 793 -66 1691 -68 201 -100 765 -66 165 -132 131 -230 131 -66 917 -66 335 -338 231 -170 827 -98 199 -136 301 -196 65 -98 199 -200 765 -134 403 -98 333 -68 1691 -132 2565 -64 569 -170 1255 -264 65 -132 1243 -132 2527 -66 259 -66 1739 -100 1309 -198 167 -238 337 -66 131 -68 1973 -362 299 -100 1387 -96 129 -164 423 -230 3875 -96 4283 -98 165 -98 515 -134 469 -68 171 -102 1163 -100 65 -298 461 -66 367 -136 205 -168 371 -98 491 -164 161 -262 1093 -100 299 -100 269 -334 1205 -98 63 -98 261 -64 457 -98 +RAW_Data: 57 -144 401 -86 173 -202 143 -258 133 -88 257 -144 287 -58 402 -56 259 -230 259 -86 85 -96 95 -174 286 -162 57 -230 253 -400 229 -88 536 -58 85 -72 167 -110 263 -72 229 -58 85 -86 87 -262 119 -288 163 -210 321 -320 186 -140 261 -96 143 -456 117 -216 143 -246 239 -102 121 -72 71 -191 167 -263 191 -96 239 -80 57 -116 143 -118 167 -168 215 -288 191 -106 287 -114 517 -88 113 -394 173 -70 215 -100 661 -86 201 -114 259 -58 287 -86 57 -202 399 -200 57 -288 229 -144 115 -1425 83 -142 173 -86 459 -112 223 -144 201 -116 143 -114 196 -17422 287 -614 1075 -1132 533 -560 1125 -1112 561 -530 1133 -1088 585 -512 1129 -1104 563 -1102 571 -1072 591 -524 1137 -538 1115 -19392 589 -522 1139 -1090 569 -520 1137 -1100 587 -520 1135 -1092 569 -514 1155 -1076 599 -1062 585 -1086 585 -532 1125 -528 1145 -19396 581 -504 1155 -1064 619 -506 1145 -1080 595 -508 1149 -1062 587 -536 1129 -1088 585 -1090 589 -1062 587 -510 1177 -506 1149 -19394 587 -524 1147 -1076 597 -512 1129 -1072 617 -498 1159 -1066 599 -514 1147 -1076 595 -1060 613 -1060 589 -506 1179 -510 1155 -19378 605 -514 1129 -1098 601 -488 1177 -1062 583 -510 1161 -1072 587 -516 1169 -1058 587 -1082 589 -1076 593 -532 1121 -536 1127 -19444 549 -542 1129 -1092 601 -510 1147 -1098 581 -506 1123 -1100 585 -510 1147 -1106 591 -1060 597 -1060 595 -510 1151 -534 1157 -19390 593 -512 1163 -1064 597 -510 1173 -1042 617 -510 1159 -1056 579 -556 1133 -1054 609 -1060 607 -1078 585 -528 1147 -516 1135 -19444 559 -534 1165 -1052 605 -510 1149 -1062 599 -520 1173 -1054 591 -540 1111 -1116 573 -1088 593 -1042 615 -534 1117 -536 1129 -19436 569 -530 1157 -1052 605 -504 1177 -1062 591 -536 1113 -1114 589 -514 1125 -1112 563 -1084 587 -1080 589 -536 1123 -532 1165 -19392 599 -524 1143 -1080 595 -540 1125 -1090 563 -534 1149 -1084 567 -534 1147 -1092 587 -1086 585 -1064 565 -558 1123 -532 1143 -19450 549 -556 1121 -1086 585 -534 1137 -1094 583 -516 1133 -1114 563 -536 1123 -1108 573 -1082 597 -1078 565 -532 1143 -524 1135 -19464 561 -528 1149 -1066 613 -508 1151 -1076 587 -532 1125 -1076 609 -506 1175 -1076 563 -1106 563 -1084 591 -534 1157 -484 1179 -19414 589 -528 1131 -1096 587 -520 1163 -1080 563 -550 1121 -1098 555 -562 1117 -1082 575 -1112 563 -1104 559 -558 1121 -530 1149 -19442 591 -498 1175 -1066 585 -534 1121 -1114 565 -540 1115 -1096 585 -538 1123 -1110 559 -1086 585 -1084 589 -530 1125 -558 1121 -19454 563 -542 1163 -1044 585 -558 1131 -1092 571 -536 1133 -1088 587 -518 1149 -1086 601 -1058 587 -1080 611 -510 +RAW_Data: 1125 -536 1145 -19444 567 -542 1151 -1086 581 -534 1133 -1084 583 -530 1141 -1090 587 -516 1121 -1114 591 -1062 595 -1092 567 -538 1131 -536 1145 -19448 589 -516 1143 -1076 591 -540 1115 -1090 591 -538 1115 -1096 597 -512 1155 -1078 581 -1082 585 -1104 559 -530 1153 -516 1147 -19438 589 -526 1157 -1056 609 -534 1137 -1078 585 -532 1149 -1076 585 -536 1123 -1104 587 -1062 597 -1082 587 -536 1121 -554 1121 -19464 555 -560 1123 -1104 563 -542 1131 -1096 565 -536 1143 -1060 621 -492 1175 -1078 571 -1100 577 -1092 591 -494 1167 -538 1117 -19452 593 -538 1133 -1074 603 -510 1157 -1062 595 -548 1115 -1102 573 -538 1139 -1094 567 -1094 583 -1092 567 -536 1127 -560 1113 -19478 563 -534 1151 -1084 569 -534 1135 -1116 565 -534 1135 -1084 583 -532 1155 -1060 587 -1084 589 -1084 581 -536 1125 -546 1141 -19460 577 -536 1123 -1102 565 -554 1119 -1096 593 -520 1133 -1108 563 -546 1131 -1088 569 -1094 573 -1100 583 -526 1147 -540 1123 -19466 587 -530 1129 -1100 587 -510 1143 -1084 609 -510 1131 -1110 567 -542 1121 -1102 567 -1110 563 -1106 567 -532 1149 -518 1145 -19458 573 -534 1147 -1082 575 -556 1149 -1048 589 -532 1157 -1088 565 -536 1129 -1088 589 -1106 563 -1086 587 -536 1129 -548 1147 -4298 103 -56 87 -344 87 -258 85 -278 215 -70 77 -344 279 -56 87 -56 197 -198 143 -288 362 -86 605 -144 57 -268 95 -114 143 -144 173 -144 143 -402 57 -202 259 -391 652 -340 427 -230 173 -356 97 -144 111 -246 219 -96 191 -114 173 -288 115 -56 109 -106 199 -106 73 -130 57 -172 85 -260 373 -56 629 -200 690 -230 273 -120 85 -460 85 -314 77 -78 111 -88 401 -116 171 -312 71 -500 81 -224 229 -88 257 -370 181 -172 200 -116 535 -174 113 -294 213 -359 445 -144 258 -114 115 -202 675 -509 239 -432 373 -538 85 -58 113 -86 761 -104 113 -318 443 -70 143 -144 647 -204 111 -334 87 -114 115 -144 113 -188 177 -144 199 -260 143 -86 87 -622 57 -116 171 -58 139 -222 55 -346 315 -76 345 -114 139 -171 195 -52 53 -98 119 -144 143 -244 95 -72 95 -96 167 -302 253 -186 307 -444 287 -449 115 -172 57 -172 316 -202 85 -370 697 -116 57 -144 171 -202 259 -114 85 -144 87 -315 85 -58 201 -116 171 -272 121 -358 171 -403 113 -86 115 -202 489 -229 115 -392 95 -116 171 -140 93 -102 143 -543 245 -358 215 -120 387 -288 171 -202 221 -202 115 -748 57 -316 143 -260 143 -288 115 -316 115 -58 85 -288 143 -460 485 -96 71 -104 199 -96 199 -202 143 -86 201 -116 85 -230 211 -288 115 -605 365 -126 53 -172 +RAW_Data: 317 -144 57 -486 53 -282 115 -585 97 -72 229 -174 257 -440 225 -86 173 -518 243 -167 95 -259 137 -96 694 -58 227 -80 279 -287 71 -72 301 -72 121 -106 51 -84 57 -58 199 -260 143 -288 219 -174 113 -681 115 -172 403 -58 113 -116 113 -432 171 -202 55 -108 95 -212 113 -72 527 -166 95 -212 195 -108 603 -142 239 -296 173 -346 373 -287 53 -80 79 -72 95 -238 95 -312 167 -618 143 -288 95 -72 95 -72 141 -210 55 -258 143 -328 305 -58 87 -86 315 -116 195 -218 85 -290 285 -220 215 -189 201 -58 57 -645 119 -96 71 -144 119 -406 143 -72 191 -72 631 -268 344 -56 115 -260 315 -140 455 -518 57 -58 171 -144 488 -86 219 -232 257 -144 85 -174 171 -260 115 -56 87 -166 197 -58 83 -56 85 -288 113 -410 115 -172 163 -202 113 -58 201 -144 201 -86 143 -264 167 -212 113 -116 139 -72 181 -287 343 -430 201 -260 201 -462 143 -192 301 -230 191 -454 187 -144 315 -164 143 -477 165 -58 201 -114 143 -490 115 -86 201 -58 113 -88 85 -58 203 -198 375 -86 171 -346 95 -88 257 -170 81 -56 143 -172 335 -230 173 -202 133 -471 187 -264 215 -86 115 -198 159 -72 179 -112 195 -116 449 -216 93 -96 167 -216 71 -216 71 -166 235 -86 447 -102 101 -226 195 -213 71 -144 215 -144 215 -261 241 -136 269 -142 263 -311 215 -172 201 -144 265 -168 71 -404 259 -86 85 -230 115 -650 143 -202 749 -512 248 -316 201 -154 71 -96 95 -360 105 -56 57 -432 95 -288 95 -286 95 -96 166 -144 93 -144 167 -150 904 -162 95 -526 287 -244 95 -240 383 -120 167 -394 430 -854 95 -72 143 -194 227 -120 167 -264 405 -144 143 -72 143 -72 141 -120 187 -86 143 -164 170 -96 143 -58 143 -86 402 -166 153 -120 95 -96 69 -96 71 -359 404 -338 71 -225 93 -74 97 -54 161 -114 319 -288 113 -116 459 -202 115 -114 115 -116 143 -86 57 -56 87 -114 85 -375 113 -58 311 -240 203 -288 95 -72 119 -383 213 -384 115 -86 171 -58 53 -104 401 -58 115 -86 373 -116 143 -144 161 -216 406 -72 263 -96 215 -72 95 -94 167 -96 191 -240 95 -94 214 -120 403 -116 200 -114 57 -172 220 -120 137 -364 334 -392 115 -260 199 -116 373 -188 95 -110 143 -172 87 -114 172 -230 57 -316 201 -56 249 -485 171 -202 87 -86 85 -144 345 -86 171 -58 259 -58 295 -120 95 -120 71 -192 635 -118 167 -96 375 -72 119 -120 261 -144 167 -96 95 -96 923 -215 71 -433 71 -477 +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: 2442 -312 275 -972 949 -310 941 -322 923 -342 921 -352 923 -334 281 -954 945 -350 279 -958 907 -354 289 -980 909 -352 281 -962 907 -330 311 -964 913 -350 317 -930 933 -344 921 -352 893 -330 311 -954 943 -318 315 -958 909 -324 947 -7854 953 -322 289 -948 939 -354 927 -332 911 -324 943 -344 917 -318 317 -964 905 -344 303 -942 947 -312 319 -960 913 -348 281 -958 941 -322 295 -978 905 -350 279 -962 931 -328 947 -324 939 -346 267 -964 935 -348 283 -938 953 -318 931 -7868 935 -346 269 -968 953 -310 941 -322 921 -330 935 -342 931 -318 311 -962 939 -290 337 -950 909 -352 317 -924 943 -324 313 -938 941 -318 317 -932 939 -344 301 -938 933 -350 921 -322 959 -310 301 -942 933 -352 317 -926 957 -314 919 -7868 943 -314 317 -958 909 -322 951 -344 919 -352 921 -324 937 -326 281 -964 941 -318 317 -930 939 -344 301 -938 933 -352 281 -962 953 -314 317 -922 933 -330 315 -954 943 -318 921 -342 943 -320 291 -980 909 -354 281 -962 943 -296 967 -7836 943 -332 309 -950 935 -318 929 -340 943 -320 921 -344 921 -354 283 -960 943 -296 309 -964 945 -318 279 -964 941 -322 333 -944 939 -314 279 -992 903 -342 319 -932 933 -330 931 -340 929 -348 281 -964 935 -334 281 -970 927 -346 921 -7862 951 -314 319 -922 953 -320 923 -346 921 -320 965 -298 943 -324 313 -942 941 -320 317 -930 941 -344 303 -940 945 -312 321 -940 953 -314 303 -960 933 -348 287 -962 911 -352 917 -350 905 -324 333 -918 971 -322 317 -924 945 -324 937 -7872 919 -324 317 -942 941 -318 933 -330 943 -324 943 -310 951 -318 317 -930 939 -344 301 -938 933 -352 317 -926 953 -314 319 -924 939 -324 331 -950 907 -354 315 -926 945 -324 939 -312 953 -318 317 -930 937 -344 301 -940 947 -348 909 -7864 949 -310 319 -956 915 -350 919 -348 905 -322 963 -296 935 -348 317 -922 951 -322 295 -976 939 -314 281 -996 915 -326 307 -940 959 -310 301 -966 935 -346 285 -958 915 -348 921 -348 903 -354 303 -948 911 -350 315 -926 945 -324 941 -7874 943 -290 319 -942 973 -318 929 -314 937 -328 941 -324 939 -310 303 -962 933 -352 285 -962 949 -314 319 -924 951 -320 293 -948 941 -354 283 -962 943 -294 309 -966 943 -320 931 -328 943 -326 311 -940 939 -320 309 -958 933 -338 943 -7840 933 -352 277 -964 941 -322 923 -344 923 -350 931 -310 955 -320 291 -974 907 -350 281 -958 963 -298 313 -956 945 -314 311 -960 937 -312 311 -966 909 -324 319 -944 941 -354 929 -298 945 -324 315 -940 +RAW_Data: 943 -354 281 -964 905 -330 933 -7868 951 -324 315 -938 943 -354 893 -330 943 -324 943 -344 919 -318 317 -962 903 -344 301 -974 903 -350 317 -932 931 -342 269 -972 949 -346 285 -938 955 -310 301 -964 935 -348 921 -320 921 -344 301 -940 935 -350 317 -930 929 -318 937 -7872 939 -344 301 -940 947 -346 917 -322 921 -344 923 -352 927 -334 281 -970 925 -334 277 -982 943 -318 317 -932 931 -344 301 -936 935 -350 281 -960 957 -312 303 -960 935 -346 907 -322 929 -344 301 -942 935 -350 317 -924 955 -312 951 -7858 919 -342 309 -940 949 -348 909 -322 923 -344 923 -352 923 -336 317 -924 945 -312 311 -966 921 -340 317 -924 947 -350 281 -958 941 -322 291 -976 905 -350 279 -960 935 -342 943 -320 919 -330 311 -958 943 -320 315 -932 935 -344 919 -7866 957 -312 303 -964 917 -342 945 -320 923 -344 923 -354 929 -298 315 -956 941 -318 315 -960 911 -324 317 -942 939 -354 281 -964 941 -294 311 -968 943 -318 317 -932 937 -330 931 -350 919 -348 283 -960 917 -350 317 -922 939 -322 965 -7864 921 -324 329 -950 909 -354 923 -336 913 -322 947 -344 919 -354 281 -962 941 -294 311 -960 935 -354 281 -962 939 -294 311 -964 937 -354 281 -964 941 -296 309 -964 939 -318 931 -330 945 -324 315 -940 939 -354 281 -964 909 -344 921 -7862 963 -304 307 -976 933 -320 929 -328 941 -324 939 -348 915 -320 317 -930 939 -344 301 -940 965 -320 319 -926 953 -312 303 -960 933 -312 321 -960 913 -348 319 -924 943 -320 959 -310 921 -354 319 -924 943 -324 311 -938 941 -318 957 -7862 943 -318 317 -932 933 -344 925 -352 897 -332 943 -324 943 -346 267 -966 951 -310 321 -960 911 -350 281 -958 949 -320 291 -978 937 -316 279 -964 949 -326 309 -944 943 -314 959 -318 933 -336 317 -934 933 -344 267 -964 937 -350 905 -7896 943 -318 319 -926 955 -314 919 -350 935 -324 941 -294 967 -312 303 -962 933 -348 285 -960 917 -348 317 -922 941 -322 329 -950 907 -354 315 -926 943 -326 313 -940 941 -352 893 -332 949 -324 315 -938 941 -352 283 -962 943 -310 925 -7890 931 -344 269 -968 949 -310 943 -320 923 -350 937 -310 955 -318 317 -930 935 -344 301 -942 947 -346 285 -958 915 -346 317 -924 951 -322 295 -982 905 -352 317 -924 945 -324 941 -346 917 -318 317 -962 905 -330 311 -956 937 -352 897 -7878 939 -354 283 -960 941 -294 965 -312 953 -318 385 -201512 165 -198 265 -526 229 -298 755 -164 61687 -17310 131 -1056 99 -296 195 -296 65 -66 1617 +RAW_Data: 77 -76 131 -244 81 -210 55 -1428 53 -344 53 -238 51 -448 51 -804 125 -1490 51 -452 79 -1816 51 -176 197 -700 133 -563 51 -386 79 -474 109 -626 55 -266 103 -616 283 -1932 51 -1034 51 -2809 75 -244 83 -5339 77 -260 105 -839 107 -1806 53 -1408 81 -810 135 -488 187 -1469 73 -2596 75 -74 51 -726 113 -136 83 -406 55 -194 133 -606 55 -1018 55 -1774 51 -1954 75 -910 51 -944 137 -1337 51 -1606 101 -566 75 -584 51 -1470 133 -242 159 -2798 51 -1568 97 -100 71 -556 77 -1234 53 -320 53 -274 68337 -252 333 -278 333 -74 533 -280 307 -276 331 -6948 347 -262 329 -278 329 -278 329 -278 303 -304 303 -302 303 -304 303 -278 329 -278 329 -74 533 -294 325 -270 317 -6944 335 -282 309 -304 309 -304 281 -328 293 -310 281 -326 281 -326 281 -326 279 -302 305 -100 503 -306 305 -302 293 -6992 295 -294 291 -314 285 -336 283 -334 257 -328 283 -328 291 -310 281 -328 279 -328 279 -124 479 -332 279 -328 255 -7012 295 -324 271 -330 267 -346 261 -342 259 -334 257 -358 255 -356 257 -354 231 -354 255 -152 453 -356 257 -352 255 -7024 255 -352 257 -352 255 -354 255 -326 257 -352 255 -352 255 -354 255 -352 253 -352 255 -150 453 -356 255 -354 253 -7030 255 -352 267 -344 251 -354 253 -354 253 -354 267 -322 267 -350 261 -344 257 -364 231 -154 459 -360 257 -354 231 -7062 231 -380 231 -380 231 -352 257 -352 257 -352 257 -352 257 -352 257 -352 255 -354 231 -174 457 -360 229 -378 229 -7084 239 -364 239 -366 229 -380 229 -378 229 -380 229 -378 255 -354 255 -354 255 -352 255 -150 457 -364 253 -354 255 -9542 101 -3126 53 -814 109 -406 51 -162 109 -2219 183 -496 103 -1369 81 -603 99 -2172 79 -1103 75 -676 77 -560 103 -378 51 -654 95 -888 155 -1322 111 -1626 53 -182 51 -166 83 -52 181 -182 71 -2132 77 -2839 103 -4022 79 -362 81 -466 75 -970 203 -998 51 -2085 51 -1853 99 -328 75 -346 55 -1949 79 -2648 79 -434 75 -6757 51 -1920 109 -306 51 -612 101 -996 77 -764 81 -790 125 -1489 99 -430 77 -4142 165 -372 101 -198 71 -1688 51 -1636 99 -434 81 -794 135 -1973 79 -188 109 -2678 81 -196 109 -2099 51 -504 77 -1854 51 -910 107 -948 75 -122 131 -78 79 -1781 103 -3344 111 -406 79 -184 51 -408 103 -54 79 -1474 127 -1789 213 -683 131 -348 161 -5237 53 -2675 101 -52 105 -474 103 -1336 99 -3548 105 -1724 161 -2180 107 -2514 97 -3784 51 -910 77 -505 71 -494 131 -1154 79 -2295 75 -350 161 -274 81 -222 +RAW_Data: 107 -1501 77 -1518 53 -704 113 -390 107 -650 73 -932 51 -3641 169 -704 187 -574 79 -332 51 -3765 51 -1042 75 -1413 103 -2163 75 -218 73 -4118 73 -716 51 -1720 51 -176 145 -817 79 -602 55 -1270 53 -2290 81 -346 79 -1840 53 -596 97 -1135 155 -1672 157 -1150 53 -52 101 -2753 153 -1546 158 -698 79 -2962 215 -490 161 -766 51 -2170 55 -811 51 -694 51 -1461 103 -2590 149 -3785 130 -54 103 -1108 103 -3978 51 -1626 81 -1825 109 -452 129 -1000 79 -1651 157 -276 53 -104 81 -2440 81 -1780 53 -1554 51 -512 131 -2508 99 -176 73 -914 51 -76 81 -202 111 -690 109 -790 109 -584 53 -244 79 -706 73 -550 129 -142 127 -546 77 -296 53 -874 105 -2623 51 -1004 77 -3131 79 -552 81 -2008 187 -1168 55 -1173 51 -2146 99 -700 51 -1580 101 -252 75 -474 77 -569 73 -5817 77 -614 77 -712 149 -228 73 -562 201 -274 127 -648 77 -578 75 -1810 109 -2106 171 -5996 81 -366 159 -274 55 -1228 77 -3386 53 -106 81 -1024 133 -2331 53 -2636 53 -780 149 -842 79 -2288 53 -2807 107 -1410 51 -620 75 -428 71 -272 75 -1140 103 -4912 55 -2261 53 -716 53 -3093 109 -502 111 -1492 53 -4317 51 -500 83 -338 129 -698 105 -1565 103 -1874 105 -344 77 -546 79 -2826 105 -260 75 -616 103 -1254 113 -2687 77 -977 73 -246 97 -1054 109 -4681 67101 -252 357 -252 333 -276 333 -252 355 -254 333 -276 331 -6932 347 -272 353 -242 343 -268 339 -286 309 -282 309 -306 307 -304 283 -328 281 -328 281 -102 505 -308 291 -314 279 -6996 281 -326 279 -328 277 -328 277 -328 279 -328 277 -328 279 -328 279 -328 277 -330 277 -124 483 -322 297 -298 293 -6972 283 -336 281 -332 257 -330 281 -328 283 -328 281 -330 265 -338 255 -352 253 -352 255 -150 455 -336 279 -328 279 -6992 295 -322 265 -348 261 -342 259 -334 259 -356 257 -356 255 -330 257 -354 257 -352 255 -152 455 -358 239 -368 253 -7026 243 -352 243 -350 265 -344 261 -362 231 -358 255 -356 257 -354 231 -380 229 -354 255 -150 455 -360 231 -376 231 -7050 229 -378 229 -378 229 -378 229 -378 229 -378 229 -378 229 -352 255 -352 253 -352 255 -150 455 -362 229 -378 227 -7050 253 -354 229 -378 229 -378 229 -378 227 -378 229 -378 229 -378 229 -378 229 -376 229 -174 431 -374 243 -378 241 -7032 261 -370 233 -362 233 -384 233 -356 233 -380 231 -380 231 -380 231 -380 231 -378 231 -176 431 -384 231 -380 211 -7114 231 -384 231 -384 205 -408 207 -408 207 -408 231 -384 231 -412 207 -412 181 -530 77 -1144 +RAW_Data: 79 -4798 53 -918 83 -4847 51 -755 103 -732 81 -388 55 -1026 77 -1506 101 -242 107 -469 51 -2026 79 -686 77 -348 51 -104 131 -860 129 -148 73 -446 75 -440 97 -306 99 -600 51 -626 105 -1350 95 -674 83 -230 119 -1714 135 -396 155 -1111 109 -652 111 -482 51 -506 55 -1715 103 -968 207 -1156 81 -164 57 -404 99 -508 205 -126 75 -1417 51 -186 77 -588 53 -54 103 -2854 73 -1010 53 -800 51 -2494 53 -106 105 -52 51 -104 79 -1116 51 -654 103 -220 77 -162 71 -5385 137 -2232 79 -1159 79 -250 57 -108 79 -164 107 -1660 79 -3927 129 -992 73 -1913 51 -1430 51 -1498 55 -514 103 -586 81 -386 53 -2402 175 -1994 85 -3431 53 -3209 99 -372 79 -78 53 -1338 75 -682 97 -680 51 -206 101 -1708 101 -452 131 -1397 161 -2272 53 -456 77 -1413 193 -270 109 -466 53 -2432 77 -222 189 -474 107 -774 171 -192 79 -1327 75 -2141 51 -908 135 -3866 75 -804 129 -468 101 -1040 79 -1470 55 -869 77 -1448 105 -160 55 -1916 240 -588 79 -1587 53 -922 79 -2292 181 -1448 51 -552 77 -2189 75 -2545 77 -384 300 -2478 101 -1092 73 -558 79 -132 105 -884 103 -1177 109 -880 79 -2431 109 -1006 105 -468 53 -1378 235 -684 75 -285 73 -604 129 -528 77 -1582 51 -1240 105 -2750 75 -252 51 -1024 95 -1891 51 -864 107 -326 83 -887 159 -1058 163 -322 105 -722 83 -388 81 -936 155 -880 55 -220 83 -2123 135 -2100 73 -1926 103 -1633 149 -526 51 -324 51 -1538 103 -164 137 -964 81 -152 111 -781 225 -655 53 -2888 105 -151 131 -454 53 -4109 77 -1052 53 -178 163 -910 51 -733 207 -2070 53 -474 79 -54 53 -818 51 -1228 53 -2262 79 -788 79 -480 73 -2747 83 -316 183 -1880 105 -862 53 -662 53 -2287 153 -1630 51 -817 243 -806 55 -510 51 -1389 75 -986 135 -498 109 -532 131 -5521 99 -2948 209 -764 75 -1168 75 -886 83 -2065 53 -710 51 -596 77 -374 73 -628 99 -732 51 -202 73 -632 53 -222 55 -511 79 -4884 53 -1826 81 -1266 107 -356 55 -110 113 -280 83 -756 169 -252 81 -1854 51 -1556 157 -258 75 -748 53 -1438 291 -244 71 -1092 77 -1220 229 -1055 181 -1182 71 -1284 77 -864 79 -138 53 -160 53 -952 81 -80 127 -1272 51 -590 103 -502 77 -634 101 -74 51 -224 101 -912 77 -562 51 -164 83 -396 105 -4643 111 -3293 133 -1395 107 -3047 137 -2353 53 -298 83 -54 81 -80 53 -162 83 -392 105 -606 107 -787 53 -928 51 -2800 161 -1146 51 -182 103 -536 103 -994 81 -2044 83 -732 133 -1881 133 -2160 75 -178 +RAW_Data: 75 -1694 101 -122 73 -864 51 -250 129 -406 77 -630 77 -610 101 -781 125 -128 51 -5075 77 -1992 83 -1272 176 -2100 53 -2044 53 -1234 79 -1704 157 -519 99 -2374 101 -100 103 -202 51 -360 77 -1962 103 -2153 77 -1820 191 -164 167 -1320 77 -1718 127 -1374 81 -1047 53 -54 79 -632 53 -656 51 -128 81 -216 51 -755 79 -2692 103 -1478 125 -452 51 -896 157 -3679 135 -632 105 -134 55 -112 77 -588 79 -188 55 -1118 79 -1152 51 -1950 109 -1858 103 -1104 81 -580 131 -226 255 -2932 77 -1536 51 -1044 159 -2135 67667 -252 333 -278 333 -276 333 -74 533 -280 307 -276 345 -6930 331 -276 329 -278 329 -278 327 -278 349 -270 325 -270 317 -290 313 -308 283 -306 309 -100 509 -306 283 -328 281 -6972 307 -302 281 -326 281 -326 281 -328 279 -328 281 -326 279 -328 279 -326 281 -328 279 -124 481 -308 281 -326 279 -6998 281 -326 279 -328 279 -328 277 -330 277 -328 279 -328 279 -328 277 -304 303 -302 303 -100 503 -306 295 -322 293 -6968 287 -342 259 -336 283 -332 257 -356 255 -328 283 -328 257 -352 257 -352 257 -352 255 -150 455 -334 281 -326 281 -6996 265 -342 253 -354 253 -354 253 -354 253 -354 267 -326 269 -350 263 -342 257 -336 259 -152 459 -360 257 -354 231 -7038 267 -338 255 -352 253 -354 253 -354 239 -354 269 -352 239 -372 233 -366 233 -358 257 -154 457 -360 231 -378 231 -7050 231 -380 231 -378 231 -380 231 -378 231 -354 255 -352 257 -352 255 -354 231 -378 229 -176 453 -358 229 -378 231 -7076 231 -378 231 -380 231 -380 229 -354 255 -354 257 -352 257 -352 257 -352 257 -354 255 -150 455 -358 265 -364 229 -4941 101 -1058 153 -670 157 -532 124 -1396 133 -82 165 -162 153 -258 207 -156 131 -1582 85 -714 53 -774 103 -396 274 -110 131 -1965 55 -402 159 -1026 79 -590 77 -3531 57 -500 51 -4770 109 -722 77 -186 53 -298 79 -502 165 -808 77 -438 53 -382 101 -1914 75 -504 77 -1969 135 -5517 99 -576 51 -608 243 -684 53 -2058 315 -1384 79 -1079 77 -232 79 -212 155 -1500 137 -258 75 -975 204 -752 83 -2542 51 -484 103 -78 77 -210 53 -922 157 -1900 107 -2173 83 -384 101 -80 128 -814 183 -978 127 -772 105 -2073 51 -708 53 -300 83 -739 237 -884 131 -3412 157 -1752 81 -164 83 -3373 53 -1406 105 -3809 79 -432 51 -724 77 -548 53 -1955 79 -807 81 -2096 103 -490 105 -1196 109 -108 79 -394 71 -1159 129 -126 143 -340 107 -556 81 -2390 135 -106 133 -690 133 -4347 189 -290 51 -110 53 -78 103 -1101 51 -1362 +RAW_Data: 83 -320 81 -4648 101 -3726 173 -1418 85 -348 53 -2994 79 -1390 51 -1656 107 -764 53 -134 79 -1619 131 -932 55 -2810 107 -3218 79 -765 107 -654 103 -1498 77 -228 51 -134 247 -1526 51 -3903 103 -1495 179 -282 77 -392 53 -1756 105 -368 111 -486 51 -298 53 -216 113 -358 51 -266 187 -1059 81 -780 105 -238 51 -482 53 -791 109 -2169 77 -5304 53 -398 79 -650 51 -54 51 -1789 73 -198 101 -1580 101 -746 97 -4518 53 -744 51 -1064 101 -928 111 -392 185 -869 103 -320 133 -704 81 -244 53 -1628 75 -634 79 -666 183 -1276 83 -218 107 -1163 55 -1276 127 -1144 73 -1400 81 -266 77 -568 129 -806 121 -1420 103 -848 77 -982 103 -2132 81 -1610 101 -1218 55 -2208 75 -2735 53 -921 53 -724 51 -472 83 -3164 185 -400 77 -812 81 -306 215 -2167 53 -130 53 -272 81 -400 79 -1272 81 -418 51 -1381 73 -340 101 -2169 81 -2330 137 -2698 99 -2340 99 -126 51 -1714 55 -488 81 -3500 51 -404 77 -1422 77 -856 215 -80 51 -2308 53 -134 77 -2036 75 -5175 129 -946 239 -638 53 -244 55 -564 105 -826 71 -1632 77 -106 129 -246 135 -366 79 -724 79 -1535 57 -1085 113 -1320 79 -3111 127 -1578 75 -324 75 -102 173 -364 79 -1374 53 -1508 107 -622 51 -526 109 -584 187 -2648 51 -106 79 -380 103 -604 51 -1244 73 -5766 107 -1934 177 -702 51 -1277 53 -1643 79 -1446 81 -4098 75 -574 103 -432 189 -1436 107 -454 79 -132 105 -136 81 -112 113 -942 239 -1238 79 -952 157 -340 51 -314 191 -456 53 -3368 101 -150 99 -464 51 -718 73 -770 101 -150 73 -2132 75 -557 77 -680 81 -3512 151 -760 75 -332 75 -1212 131 -1468 79 -1955 101 -541 75 -344 79 -2146 53 -2299 97 -720 79 -2518 79 -3807 51 -1272 75 -352 77 -52 75 -586 53 -1142 79 -82 81 -2400 157 -324 81 -268 103 -1154 81 -1175 79 -1191 51 -1074 53 -2566 137 -854 75 -1497 51 -4533 51 -2290 51 -344 77 -348 55 -1182 77 -897 135 -874 51 -1064 51 -208 55 -140 55 -1334 133 -1238 157 -1669 113 -2128 75 -848 85 -510 83590 -126 333 -280 331 -252 331 -6946 331 -276 331 -276 329 -278 329 -276 331 -276 331 -276 331 -276 347 -238 351 -254 353 -268 323 -270 345 -6924 335 -282 307 -304 307 -304 281 -304 307 -302 307 -302 305 -302 307 -302 305 -302 281 -124 507 -282 305 -302 305 -6984 279 -328 277 -328 279 -328 277 -330 277 -304 303 -302 305 -302 305 -302 303 -304 303 -100 507 -314 295 -298 293 -6986 283 -334 281 -306 283 -328 283 -328 281 -328 283 -328 255 -352 +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/applications/unit_tests/rpc/rpc_test.c b/applications/debug/unit_tests/rpc/rpc_test.c similarity index 89% rename from applications/unit_tests/rpc/rpc_test.c rename to applications/debug/unit_tests/rpc/rpc_test.c index d31311af6c4..3faf6157211 100644 --- a/applications/unit_tests/rpc/rpc_test.c +++ b/applications/debug/unit_tests/rpc/rpc_test.c @@ -1,25 +1,30 @@ -#include "flipper.pb.h" #include #include -#include "pb_decode.h" -#include -#include "rpc/rpc_i.h" -#include "storage.pb.h" -#include "storage/filesystem_api_defines.h" -#include "storage/storage.h" #include -#include "../minunit.h" #include -#include -#include -#include -#include -#include -#include + +#include +#include + +#include +#include #include +#include #include +#include + +#include +#include + +#include +#include "../minunit.h" + #include -#include +#include +#include +#include +#include +#include LIST_DEF(MsgList, PB_Main, M_POD_OPLIST) #define M_OPL_MsgList_t() LIST_OPLIST(MsgList) @@ -34,10 +39,10 @@ static uint32_t command_id = 0; typedef struct { RpcSession* session; - StreamBufferHandle_t output_stream; + FuriStreamBuffer* output_stream; SemaphoreHandle_t close_session_semaphore; SemaphoreHandle_t terminate_semaphore; - TickType_t timeout; + uint32_t timeout; } RpcSessionContext; static RpcSessionContext rpc_session[TEST_RPC_SESSIONS]; @@ -68,7 +73,6 @@ static RpcSessionContext rpc_session[TEST_RPC_SESSIONS]; } while(0) static void output_bytes_callback(void* ctx, uint8_t* got_bytes, size_t got_size); -static void clean_directory(Storage* fs_api, const char* clean_dir); static void test_rpc_add_empty_to_list(MsgList_t msg_list, PB_CommandStatus status, uint32_t command_id); static void test_rpc_encode_and_feed(MsgList_t msg_list, uint8_t session); @@ -85,12 +89,12 @@ static void test_rpc_setup(void) { rpc = furi_record_open(RECORD_RPC); for(int i = 0; !(rpc_session[0].session) && (i < 10000); ++i) { - rpc_session[0].session = rpc_session_open(rpc); + rpc_session[0].session = rpc_session_open(rpc, RpcOwnerUnknown); furi_delay_tick(1); } furi_check(rpc_session[0].session); - rpc_session[0].output_stream = xStreamBufferCreate(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(); @@ -105,12 +109,12 @@ static void test_rpc_setup_second_session(void) { furi_check(!(rpc_session[1].session)); for(int i = 0; !(rpc_session[1].session) && (i < 10000); ++i) { - rpc_session[1].session = rpc_session_open(rpc); + rpc_session[1].session = rpc_session_open(rpc, RpcOwnerUnknown); furi_delay_tick(1); } furi_check(rpc_session[1].session); - rpc_session[1].output_stream = xStreamBufferCreate(1000, 1); + rpc_session[1].output_stream = furi_stream_buffer_alloc(1000, 1); rpc_session_set_send_bytes_callback(rpc_session[1].session, output_bytes_callback); rpc_session[1].close_session_semaphore = xSemaphoreCreateBinary(); rpc_session[1].terminate_semaphore = xSemaphoreCreateBinary(); @@ -126,7 +130,7 @@ static void test_rpc_teardown(void) { rpc_session_close(rpc_session[0].session); furi_check(xSemaphoreTake(rpc_session[0].terminate_semaphore, portMAX_DELAY)); furi_record_close(RECORD_RPC); - vStreamBufferDelete(rpc_session[0].output_stream); + furi_stream_buffer_free(rpc_session[0].output_stream); vSemaphoreDelete(rpc_session[0].close_session_semaphore); vSemaphoreDelete(rpc_session[0].terminate_semaphore); ++command_id; @@ -141,7 +145,7 @@ static void test_rpc_teardown_second_session(void) { xSemaphoreTake(rpc_session[1].terminate_semaphore, 0); rpc_session_close(rpc_session[1].session); furi_check(xSemaphoreTake(rpc_session[1].terminate_semaphore, portMAX_DELAY)); - vStreamBufferDelete(rpc_session[1].output_stream); + furi_stream_buffer_free(rpc_session[1].output_stream); vSemaphoreDelete(rpc_session[1].close_session_semaphore); vSemaphoreDelete(rpc_session[1].terminate_semaphore); ++command_id; @@ -150,11 +154,41 @@ static void test_rpc_teardown_second_session(void) { rpc_session[1].session = NULL; } +static void test_rpc_storage_clean_directory(Storage* fs_api, const char* clean_dir) { + furi_check(fs_api); + furi_check(clean_dir); + storage_simply_remove_recursive(fs_api, clean_dir); + FS_Error error = storage_common_mkdir(fs_api, clean_dir); + furi_check(error == FSE_OK); +} + +static void test_rpc_storage_create_file(Storage* fs_api, const char* path, size_t size) { + File* file = storage_file_alloc(fs_api); + + bool success = false; + do { + if(!storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) break; + if(!storage_file_seek(file, size, true)) break; + success = true; + } while(false); + + storage_file_close(file); + storage_file_free(file); + + furi_check(success); +} + static void test_rpc_storage_setup(void) { test_rpc_setup(); Storage* fs_api = furi_record_open(RECORD_STORAGE); - clean_directory(fs_api, TEST_DIR_NAME); + test_rpc_storage_clean_directory(fs_api, TEST_DIR_NAME); + test_rpc_storage_create_file(fs_api, TEST_DIR_NAME "/file100", 100); + test_rpc_storage_create_file(fs_api, TEST_DIR_NAME "/file250", 250); + test_rpc_storage_create_file(fs_api, TEST_DIR_NAME "/file500", 200); + test_rpc_storage_create_file(fs_api, TEST_DIR_NAME "/file1000", 1000); + test_rpc_storage_create_file(fs_api, TEST_DIR_NAME "/file2500", 2500); + test_rpc_storage_create_file(fs_api, TEST_DIR_NAME "/file5000", 5000); furi_record_close(RECORD_STORAGE); } @@ -162,7 +196,7 @@ static void test_rpc_storage_teardown(void) { test_rpc_teardown(); Storage* fs_api = furi_record_open(RECORD_STORAGE); - clean_directory(fs_api, TEST_DIR_NAME); + test_rpc_storage_clean_directory(fs_api, TEST_DIR_NAME); furi_record_close(RECORD_STORAGE); } @@ -180,42 +214,12 @@ static void test_rpc_session_terminated_callback(void* context) { xSemaphoreGive(callbacks_context->terminate_semaphore); } -static void clean_directory(Storage* fs_api, const char* clean_dir) { - furi_check(fs_api); - furi_check(clean_dir); - - File* dir = storage_file_alloc(fs_api); - if(storage_dir_open(dir, clean_dir)) { - FileInfo fileinfo; - char* name = malloc(MAX_NAME_LENGTH + 1); - while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) { - 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) { - clean_directory(fs_api, fullname); - } - FS_Error error = storage_common_remove(fs_api, fullname); - furi_check(error == FSE_OK); - free(fullname); - } - free(name); - } else { - FS_Error error = storage_common_mkdir(fs_api, clean_dir); - (void)error; - furi_check(error == FSE_OK); - } - - storage_dir_close(dir); - storage_file_free(dir); -} - static void test_rpc_print_message_list(MsgList_t msg_list) { #if DEBUG_PRINT MsgList_reverse(msg_list); for M_EACH(msg, msg_list, MsgList_t) { - rpc_print_message(msg); + rpc_debug_print_message(msg); } MsgList_reverse(msg_list); #else @@ -268,8 +272,8 @@ static PB_CommandStatus test_rpc_storage_get_file_error(File* file) { static void output_bytes_callback(void* ctx, uint8_t* got_bytes, size_t got_size) { RpcSessionContext* callbacks_context = ctx; - size_t bytes_sent = - xStreamBufferSend(callbacks_context->output_stream, got_bytes, got_size, FuriWaitForever); + size_t bytes_sent = furi_stream_buffer_send( + callbacks_context->output_stream, got_bytes, got_size, FuriWaitForever); (void)bytes_sent; furi_check(bytes_sent == got_size); } @@ -283,6 +287,27 @@ static void test_rpc_add_ping_to_list(MsgList_t msg_list, bool request, uint32_t response->which_content = (request == PING_REQUEST) ? PB_Main_system_ping_request_tag : PB_Main_system_ping_response_tag; } +static void test_rpc_fill_basic_message(PB_Main* message, uint16_t tag, uint32_t command_id) { + message->command_id = command_id; + message->command_status = PB_CommandStatus_OK; + message->cb_content.funcs.encode = NULL; + message->which_content = tag; + message->has_next = false; +} + +static void test_rpc_create_storage_list_request( + PB_Main* message, + const char* path, + bool include_md5, + uint32_t command_id, + uint32_t filter_max_size) { + furi_check(message); + furi_check(path); + test_rpc_fill_basic_message(message, PB_Main_storage_list_request_tag, command_id); + message->content.storage_list_request.path = strdup(path); + message->content.storage_list_request.include_md5 = include_md5; + message->content.storage_list_request.filter_max_size = filter_max_size; +} static void test_rpc_create_simple_message( PB_Main* message, @@ -295,11 +320,7 @@ static void test_rpc_create_simple_message( if(str) { str_copy = strdup(str); } - message->command_id = command_id; - message->command_status = PB_CommandStatus_OK; - message->cb_content.funcs.encode = NULL; - message->which_content = tag; - message->has_next = false; + test_rpc_fill_basic_message(message, tag, command_id); switch(tag) { case PB_Main_storage_info_request_tag: message->content.storage_info_request.path = str_copy; @@ -307,9 +328,6 @@ static void test_rpc_create_simple_message( case PB_Main_storage_stat_request_tag: message->content.storage_stat_request.path = str_copy; break; - case PB_Main_storage_list_request_tag: - message->content.storage_list_request.path = str_copy; - break; case PB_Main_storage_mkdir_request_tag: message->content.storage_mkdir_request.path = str_copy; break; @@ -420,6 +438,7 @@ static void } mu_check(result_msg_file->size == expected_msg_file->size); mu_check(result_msg_file->type == expected_msg_file->type); + mu_assert_string_eq(expected_msg_file->md5sum, result_msg_file->md5sum); if(result_msg_file->data && result_msg_file->type != PB_Storage_File_FileType_DIR) { mu_check(!result_msg_file->data == !expected_msg_file->data); // Zlo: WTF??? @@ -431,10 +450,10 @@ static void } static void test_rpc_compare_messages(PB_Main* result, PB_Main* expected) { - mu_check(result->command_id == expected->command_id); - mu_check(result->command_status == expected->command_status); - mu_check(result->has_next == expected->has_next); - mu_check(result->which_content == expected->which_content); + mu_assert_int_eq(expected->command_id, result->command_id); + mu_assert_int_eq(expected->command_status, result->command_status); + mu_assert_int_eq(expected->has_next, result->has_next); + mu_assert_int_eq(expected->which_content, result->which_content); if(result->command_status != PB_CommandStatus_OK) { mu_check(result->which_content == PB_Main_empty_tag); } @@ -531,10 +550,11 @@ static bool test_rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_ RpcSessionContext* session_context = istream->state; size_t bytes_received = 0; - TickType_t now = xTaskGetTickCount(); + uint32_t now = furi_get_tick(); int32_t time_left = session_context->timeout - now; time_left = MAX(time_left, 0); - bytes_received = xStreamBufferReceive(session_context->output_stream, buf, count, time_left); + bytes_received = + furi_stream_buffer_receive(session_context->output_stream, buf, count, time_left); return (count == bytes_received); } @@ -570,13 +590,36 @@ static void message->content.storage_list_response.file[2].name = str; } +static bool test_rpc_system_storage_list_filter( + const FileInfo* fileinfo, + const char* name, + size_t filter_max_size) { + bool result = false; + + do { + if(!path_contains_only_ascii(name)) break; + if(filter_max_size) { + if(fileinfo->size > filter_max_size) break; + } + result = true; + } while(false); + + return result; +} + static void test_rpc_storage_list_create_expected_list( MsgList_t msg_list, const char* path, - uint32_t command_id) { + uint32_t command_id, + bool append_md5, + size_t filter_max_size) { Storage* fs_api = furi_record_open(RECORD_STORAGE); File* dir = storage_file_alloc(fs_api); + FuriString* md5 = furi_string_alloc(); + FuriString* md5_path = furi_string_alloc(); + File* file = storage_file_alloc(fs_api); + PB_Main response = { .command_id = command_id, .has_next = false, @@ -607,14 +650,24 @@ static void test_rpc_storage_list_create_expected_list( i = 0; } - if(path_contains_only_ascii(name)) { - list->file[i].type = (fileinfo.flags & FSF_DIRECTORY) ? - PB_Storage_File_FileType_DIR : - PB_Storage_File_FileType_FILE; + if(test_rpc_system_storage_list_filter(&fileinfo, name, filter_max_size)) { + 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() */ list->file[i].name = name; + + if(append_md5 && !file_info_is_dir(&fileinfo)) { + furi_string_printf(md5_path, "%s/%s", path, name); + + if(md5_string_calc_file(file, furi_string_get_cstr(md5_path), md5, NULL)) { + char* md5sum = list->file[i].md5sum; + size_t md5sum_size = sizeof(list->file[i].md5sum); + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); + } + } + ++i; } } else { @@ -627,6 +680,10 @@ static void test_rpc_storage_list_create_expected_list( response.has_next = false; MsgList_push_back(msg_list, response); + furi_string_free(md5); + furi_string_free(md5_path); + storage_file_free(file); + storage_dir_close(dir); storage_file_free(dir); @@ -637,7 +694,7 @@ static void test_rpc_decode_and_compare(MsgList_t expected_msg_list, uint8_t ses furi_check(!MsgList_empty_p(expected_msg_list)); furi_check(session < TEST_RPC_SESSIONS); - rpc_session[session].timeout = xTaskGetTickCount() + MAX_RECEIVE_OUTPUT_TIMEOUT; + rpc_session[session].timeout = furi_get_tick() + MAX_RECEIVE_OUTPUT_TIMEOUT; pb_istream_t istream = { .callback = test_rpc_pb_stream_read, .state = &rpc_session[session], @@ -661,7 +718,7 @@ static void test_rpc_decode_and_compare(MsgList_t expected_msg_list, uint8_t ses pb_release(&PB_Main_msg, &result); } - rpc_session[session].timeout = xTaskGetTickCount() + 50; + rpc_session[session].timeout = furi_get_tick() + 50; if(pb_decode_ex(&istream, &PB_Main_msg, &result, PB_DECODE_DELIMITED)) { mu_fail("decoded more than expected"); } @@ -676,16 +733,21 @@ static void test_rpc_free_msg_list(MsgList_t msg_list) { MsgList_clear(msg_list); } -static void test_rpc_storage_list_run(const char* path, uint32_t command_id) { +static void test_rpc_storage_list_run( + const char* path, + uint32_t command_id, + bool md5, + size_t filter_max_size) { PB_Main request; MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_list_request_tag, path, command_id); + test_rpc_create_storage_list_request(&request, path, md5, command_id, filter_max_size); if(!strcmp(path, "/")) { test_rpc_storage_list_create_expected_list_root(expected_msg_list, command_id); } else { - test_rpc_storage_list_create_expected_list(expected_msg_list, path, command_id); + test_rpc_storage_list_create_expected_list( + expected_msg_list, path, command_id, md5, filter_max_size); } test_rpc_encode_and_feed_one(&request, 0); test_rpc_decode_and_compare(expected_msg_list, 0); @@ -695,15 +757,32 @@ static void test_rpc_storage_list_run(const char* path, uint32_t command_id) { } MU_TEST(test_storage_list) { - test_rpc_storage_list_run("/", ++command_id); - test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id); - - test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id); - test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id); - test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id); - test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id); - test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id); - test_rpc_storage_list_run("error_path", ++command_id); + test_rpc_storage_list_run("/", ++command_id, false, 0); + test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id, false, 0); + test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, false, 0); + test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, false, 0); + test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id, false, 0); + test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id, false, 0); + test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id, false, 0); + test_rpc_storage_list_run("error_path", ++command_id, false, 0); +} + +MU_TEST(test_storage_list_md5) { + test_rpc_storage_list_run("/", ++command_id, true, 0); + test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id, true, 0); + test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, true, 0); + test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, true, 0); + test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id, true, 0); + test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id, true, 0); + test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id, true, 0); + test_rpc_storage_list_run("error_path", ++command_id, true, 0); +} + +MU_TEST(test_storage_list_size) { + test_rpc_storage_list_run(TEST_DIR_NAME, ++command_id, false, 0); + test_rpc_storage_list_run(TEST_DIR_NAME, ++command_id, false, 1); + test_rpc_storage_list_run(TEST_DIR_NAME, ++command_id, false, 1000); + test_rpc_storage_list_run(TEST_DIR_NAME, ++command_id, false, 2500); } static void @@ -873,7 +952,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; @@ -1230,33 +1309,15 @@ MU_TEST(test_storage_mkdir) { static void test_storage_calculate_md5sum(const char* path, char* md5sum, size_t md5sum_size) { Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); + FuriString* md5 = furi_string_alloc(); - if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) { - const uint16_t once_read_size = 512; - const uint8_t hash_size = MD5SUM_SIZE; - uint8_t* data = malloc(once_read_size); - uint8_t* hash = malloc(sizeof(uint8_t) * hash_size); - md5_context* md5_ctx = malloc(sizeof(md5_context)); - - md5_starts(md5_ctx); - while(true) { - uint16_t read_size = storage_file_read(file, data, once_read_size); - if(read_size == 0) break; - md5_update(md5_ctx, data, read_size); - } - md5_finish(md5_ctx, hash); - free(md5_ctx); - - for(uint8_t i = 0; i < hash_size; i++) { - md5sum += snprintf(md5sum, md5sum_size, "%02x", hash[i]); - } - - free(hash); - free(data); + if(md5_string_calc_file(file, path, md5, NULL)) { + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); } else { furi_check(0); } + furi_string_free(md5); storage_file_close(file); storage_file_free(file); @@ -1434,6 +1495,8 @@ MU_TEST_SUITE(test_rpc_storage) { MU_RUN_TEST(test_storage_info); MU_RUN_TEST(test_storage_stat); MU_RUN_TEST(test_storage_list); + MU_RUN_TEST(test_storage_list_md5); + MU_RUN_TEST(test_storage_list_size); MU_RUN_TEST(test_storage_read); MU_RUN_TEST(test_storage_write_read); MU_RUN_TEST(test_storage_write); diff --git a/applications/unit_tests/storage/dirwalk_test.c b/applications/debug/unit_tests/storage/dirwalk_test.c similarity index 82% rename from applications/unit_tests/storage/dirwalk_test.c rename to applications/debug/unit_tests/storage/dirwalk_test.c index db3d91a96e0..19ac336fffb 100644 --- a/applications/unit_tests/storage/dirwalk_test.c +++ b/applications/debug/unit_tests/storage/dirwalk_test.c @@ -75,7 +75,7 @@ typedef struct { bool visited; } StorageTestPath; -DICT_DEF2(StorageTestPathDict, string_t, STRING_OPLIST, StorageTestPath, M_POD_OPLIST) +DICT_DEF2(StorageTestPathDict, FuriString*, FURI_STRING_OPLIST, StorageTestPath, M_POD_OPLIST) static StorageTestPathDict_t* storage_test_paths_alloc(const StorageTestPathDesc paths[], size_t paths_count) { @@ -83,15 +83,15 @@ static StorageTestPathDict_t* StorageTestPathDict_init(*data); for(size_t i = 0; i < paths_count; i++) { - string_t key; - string_init_set(key, paths[i].path); + FuriString* key; + key = furi_string_alloc_set(paths[i].path); StorageTestPath value = { .is_dir = paths[i].is_dir, .visited = false, }; StorageTestPathDict_set_at(*data, key, value); - string_clear(key); + furi_string_free(key); } return data; @@ -102,7 +102,7 @@ static void storage_test_paths_free(StorageTestPathDict_t* data) { free(data); } -static bool storage_test_paths_mark(StorageTestPathDict_t* data, string_t path, bool is_dir) { +static bool storage_test_paths_mark(StorageTestPathDict_t* data, FuriString* path, bool is_dir) { bool found = false; StorageTestPath* record = StorageTestPathDict_get(*data, path); @@ -139,7 +139,7 @@ static bool write_file_13DA(Storage* storage, const char* path) { File* file = storage_file_alloc(storage); bool result = false; if(storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { - result = storage_file_write(file, "13DA", 4) == 4; + result = (storage_file_write(file, "13DA", 4) == 4); } storage_file_close(file); storage_file_free(file); @@ -148,27 +148,27 @@ static bool write_file_13DA(Storage* storage, const char* path) { } static void storage_dirs_create(Storage* storage, const char* base) { - string_t path; - string_init(path); + FuriString* path; + path = furi_string_alloc(); storage_common_mkdir(storage, base); for(size_t i = 0; i < COUNT_OF(storage_test_dirwalk_paths); i++) { - string_printf(path, "%s/%s", base, storage_test_dirwalk_paths[i]); - storage_common_mkdir(storage, string_get_cstr(path)); + furi_string_printf(path, "%s/%s", base, storage_test_dirwalk_paths[i]); + storage_common_mkdir(storage, furi_string_get_cstr(path)); } for(size_t i = 0; i < COUNT_OF(storage_test_dirwalk_files); i++) { - string_printf(path, "%s/%s", base, storage_test_dirwalk_files[i]); - write_file_13DA(storage, string_get_cstr(path)); + furi_string_printf(path, "%s/%s", base, storage_test_dirwalk_files[i]); + write_file_13DA(storage, furi_string_get_cstr(path)); } - string_clear(path); + furi_string_free(path); } MU_TEST_1(test_dirwalk_full, Storage* storage) { - string_t path; - string_init(path); + FuriString* path; + path = furi_string_alloc(); FileInfo fileinfo; StorageTestPathDict_t* paths = @@ -178,12 +178,12 @@ MU_TEST_1(test_dirwalk_full, Storage* storage) { mu_check(dir_walk_open(dir_walk, EXT_PATH("dirwalk"))); while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) { - string_right(path, strlen(EXT_PATH("dirwalk/"))); - mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY))); + furi_string_right(path, strlen(EXT_PATH("dirwalk/"))); + mu_check(storage_test_paths_mark(paths, path, file_info_is_dir(&fileinfo))); } dir_walk_free(dir_walk); - string_clear(path); + furi_string_free(path); mu_check(storage_test_paths_check(paths) == false); @@ -191,8 +191,8 @@ MU_TEST_1(test_dirwalk_full, Storage* storage) { } MU_TEST_1(test_dirwalk_no_recursive, Storage* storage) { - string_t path; - string_init(path); + FuriString* path; + path = furi_string_alloc(); FileInfo fileinfo; StorageTestPathDict_t* paths = storage_test_paths_alloc( @@ -203,12 +203,12 @@ MU_TEST_1(test_dirwalk_no_recursive, Storage* storage) { mu_check(dir_walk_open(dir_walk, EXT_PATH("dirwalk"))); while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) { - string_right(path, strlen(EXT_PATH("dirwalk/"))); - mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY))); + furi_string_right(path, strlen(EXT_PATH("dirwalk/"))); + mu_check(storage_test_paths_mark(paths, path, file_info_is_dir(&fileinfo))); } dir_walk_free(dir_walk); - string_clear(path); + furi_string_free(path); mu_check(storage_test_paths_check(paths) == false); @@ -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; @@ -230,8 +230,8 @@ static bool test_dirwalk_filter_no_folder_ext(const char* name, FileInfo* filein } MU_TEST_1(test_dirwalk_filter, Storage* storage) { - string_t path; - string_init(path); + FuriString* path; + path = furi_string_alloc(); FileInfo fileinfo; StorageTestPathDict_t* paths = storage_test_paths_alloc( @@ -242,12 +242,12 @@ MU_TEST_1(test_dirwalk_filter, Storage* storage) { mu_check(dir_walk_open(dir_walk, EXT_PATH("dirwalk"))); while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) { - string_right(path, strlen(EXT_PATH("dirwalk/"))); - mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY))); + furi_string_right(path, strlen(EXT_PATH("dirwalk/"))); + mu_check(storage_test_paths_mark(paths, path, file_info_is_dir(&fileinfo))); } dir_walk_free(dir_walk); - string_clear(path); + furi_string_free(path); mu_check(storage_test_paths_check(paths) == false); diff --git a/applications/debug/unit_tests/storage/storage_test.c b/applications/debug/unit_tests/storage/storage_test.c new file mode 100644 index 00000000000..5ea36935b13 --- /dev/null +++ b/applications/debug/unit_tests/storage/storage_test.c @@ -0,0 +1,714 @@ +#include "../minunit.h" +#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); + storage_simply_remove(storage, STORAGE_LOCKED_FILE); + mu_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_WRITE, FSOM_CREATE_NEW)); + mu_check(storage_file_write(file, "0123", 4) == 4); + mu_check(storage_file_close(file)); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +} + +static void storage_file_open_lock_teardown() { + Storage* storage = furi_record_open(RECORD_STORAGE); + mu_check(storage_simply_remove(storage, STORAGE_LOCKED_FILE)); + furi_record_close(RECORD_STORAGE); +} + +static int32_t storage_file_locker(void* ctx) { + Storage* storage = furi_record_open(RECORD_STORAGE); + FuriSemaphore* semaphore = ctx; + File* file = storage_file_alloc(storage); + furi_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)); + furi_semaphore_release(semaphore); + furi_delay_ms(1000); + + furi_check(storage_file_close(file)); + furi_record_close(RECORD_STORAGE); + storage_file_free(file); + return 0; +} + +MU_TEST(storage_file_open_lock) { + Storage* storage = furi_record_open(RECORD_STORAGE); + bool result = false; + FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0); + File* file = storage_file_alloc(storage); + + // file_locker thread start + FuriThread* locker_thread = + furi_thread_alloc_ex("StorageFileLocker", 2048, storage_file_locker, semaphore); + furi_thread_start(locker_thread); + + // wait for file lock + furi_semaphore_acquire(semaphore, FuriWaitForever); + furi_semaphore_free(semaphore); + + result = storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING); + storage_file_close(file); + + // file_locker thread stop + mu_check(furi_thread_join(locker_thread)); + furi_thread_free(locker_thread); + + // clean data + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + + mu_assert(result, "cannot open locked file"); +} + +MU_TEST(storage_file_open_close) { + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file; + + file = storage_file_alloc(storage); + mu_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)); + storage_file_close(file); + storage_file_free(file); + + for(size_t i = 0; i < 10; i++) { + file = storage_file_alloc(storage); + mu_check( + storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)); + storage_file_free(file); + } + + furi_record_close(RECORD_STORAGE); +} + +static bool storage_file_read_write_test(File* file, uint8_t* data, size_t test_size) { + const char* filename = UNIT_TESTS_PATH("storage_chunk.test"); + + // fill with pattern + for(size_t i = 0; i < test_size; i++) { + data[i] = (i % 113); + } + + bool result = false; + do { + if(!storage_file_open(file, filename, FSAM_WRITE, FSOM_CREATE_ALWAYS)) break; + if(test_size != storage_file_write(file, data, test_size)) break; + storage_file_close(file); + + // reset data + memset(data, 0, test_size); + + if(!storage_file_open(file, filename, FSAM_READ, FSOM_OPEN_EXISTING)) break; + if(test_size != storage_file_read(file, data, test_size)) break; + storage_file_close(file); + + // check that data is correct + for(size_t i = 0; i < test_size; i++) { + if(data[i] != (i % 113)) { + break; + } + } + + result = true; + } while(false); + + return result; +} + +MU_TEST(storage_file_read_write_64k) { + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + size_t size_1k = 1024; + size_t size_64k = size_1k + size_1k * 63; + size_t size_65k = size_64k + size_1k; + size_t size_max = size_65k + 8; + + size_t max_ram_block = memmgr_heap_get_max_free_block(); + + if(max_ram_block < size_max) { + mu_warn("Not enough RAM for >64k block test"); + } else { + uint8_t* data = malloc(size_max); + mu_check(storage_file_read_write_test(file, data, size_1k)); + mu_check(storage_file_read_write_test(file, data, size_64k)); + mu_check(storage_file_read_write_test(file, data, size_65k)); + mu_check(storage_file_read_write_test(file, data, size_max)); + free(data); + } + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +} + +MU_TEST_SUITE(storage_file) { + storage_file_open_lock_setup(); + MU_RUN_TEST(storage_file_open_close); + MU_RUN_TEST(storage_file_open_lock); + storage_file_open_lock_teardown(); +} + +MU_TEST_SUITE(storage_file_64k) { + MU_RUN_TEST(storage_file_read_write_64k); +} + +MU_TEST(storage_dir_open_close) { + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file; + + file = storage_file_alloc(storage); + mu_check(storage_dir_open(file, STORAGE_LOCKED_DIR)); + storage_dir_close(file); + storage_file_free(file); + + for(size_t i = 0; i < 10; i++) { + file = storage_file_alloc(storage); + mu_check(storage_dir_open(file, STORAGE_LOCKED_DIR)); + storage_file_free(file); + } + + furi_record_close(RECORD_STORAGE); +} + +static int32_t storage_dir_locker(void* ctx) { + Storage* storage = furi_record_open(RECORD_STORAGE); + FuriSemaphore* semaphore = ctx; + File* file = storage_file_alloc(storage); + furi_check(storage_dir_open(file, STORAGE_LOCKED_DIR)); + furi_semaphore_release(semaphore); + furi_delay_ms(100); + + furi_check(storage_dir_close(file)); + furi_record_close(RECORD_STORAGE); + storage_file_free(file); + return 0; +} + +MU_TEST(storage_dir_open_lock) { + Storage* storage = furi_record_open(RECORD_STORAGE); + bool result = false; + FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0); + File* file = storage_file_alloc(storage); + + // file_locker thread start + FuriThread* locker_thread = + furi_thread_alloc_ex("StorageDirLocker", 2048, storage_dir_locker, semaphore); + furi_thread_start(locker_thread); + + // wait for dir lock + furi_semaphore_acquire(semaphore, FuriWaitForever); + furi_semaphore_free(semaphore); + + result = storage_dir_open(file, STORAGE_LOCKED_DIR); + storage_dir_close(file); + + // file_locker thread stop + mu_check(furi_thread_join(locker_thread)); + furi_thread_free(locker_thread); + + // clean data + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + + 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[] = { + "1", + "11", + "111", + "1/2", + "1/22", + "1/222", + "11/1", + "111/2", + "111/22", + "111/22/33", +}; + +static const char* const storage_copy_test_files[] = { + "file.test", + "1/file.test", + "111/22/33/file.test", +}; + +static bool write_file_13DA(Storage* storage, const char* path) { + File* file = storage_file_alloc(storage); + bool result = false; + if(storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + result = storage_file_write(file, "13DA", 4) == 4; + } + storage_file_close(file); + storage_file_free(file); + + return result; +} + +static bool check_file_13DA(Storage* storage, const char* path) { + File* file = storage_file_alloc(storage); + bool result = false; + if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) { + char data[10] = {0}; + result = storage_file_read(file, data, 4) == 4; + if(result) { + result = memcmp(data, "13DA", 4) == 0; + } + } + storage_file_close(file); + storage_file_free(file); + + return result; +} + +static void storage_dir_create(Storage* storage, const char* base) { + FuriString* path; + path = furi_string_alloc(); + + storage_common_mkdir(storage, base); + + for(size_t i = 0; i < COUNT_OF(storage_copy_test_paths); i++) { + furi_string_printf(path, "%s/%s", base, storage_copy_test_paths[i]); + storage_common_mkdir(storage, furi_string_get_cstr(path)); + } + + for(size_t i = 0; i < COUNT_OF(storage_copy_test_files); i++) { + furi_string_printf(path, "%s/%s", base, storage_copy_test_files[i]); + write_file_13DA(storage, furi_string_get_cstr(path)); + } + + furi_string_free(path); +} + +static void storage_dir_remove(Storage* storage, const char* base) { + storage_simply_remove_recursive(storage, base); +} + +static bool storage_dir_rename_check(Storage* storage, const char* base) { + bool result = false; + FuriString* path; + path = furi_string_alloc(); + + result = (storage_common_stat(storage, base, NULL) == FSE_OK); + + if(result) { + for(size_t i = 0; i < COUNT_OF(storage_copy_test_paths); i++) { + furi_string_printf(path, "%s/%s", base, storage_copy_test_paths[i]); + result = (storage_common_stat(storage, furi_string_get_cstr(path), NULL) == FSE_OK); + if(!result) { + break; + } + } + } + + if(result) { + for(size_t i = 0; i < COUNT_OF(storage_copy_test_files); i++) { + furi_string_printf(path, "%s/%s", base, storage_copy_test_files[i]); + result = check_file_13DA(storage, furi_string_get_cstr(path)); + if(!result) { + break; + } + } + } + + furi_string_free(path); + return result; +} + +MU_TEST(storage_file_rename) { + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + mu_check(write_file_13DA(storage, EXT_PATH("file.old"))); + mu_check(check_file_13DA(storage, EXT_PATH("file.old"))); + mu_assert_int_eq( + FSE_OK, storage_common_rename(storage, EXT_PATH("file.old"), EXT_PATH("file.new"))); + mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("file.old"), NULL)); + mu_assert_int_eq(FSE_OK, storage_common_stat(storage, EXT_PATH("file.new"), NULL)); + mu_check(check_file_13DA(storage, EXT_PATH("file.new"))); + mu_assert_int_eq(FSE_OK, storage_common_remove(storage, EXT_PATH("file.new"))); + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +} + +MU_TEST(storage_dir_rename) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + storage_dir_create(storage, EXT_PATH("dir.old")); + + mu_check(storage_dir_rename_check(storage, EXT_PATH("dir.old"))); + + mu_assert_int_eq( + FSE_OK, storage_common_rename(storage, EXT_PATH("dir.old"), EXT_PATH("dir.new"))); + mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("dir.old"), NULL)); + mu_check(storage_dir_rename_check(storage, EXT_PATH("dir.new"))); + + storage_dir_remove(storage, EXT_PATH("dir.new")); + mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("dir.new"), NULL)); + + furi_record_close(RECORD_STORAGE); +} + +MU_TEST_SUITE(storage_rename) { + MU_RUN_TEST(storage_file_rename); + MU_RUN_TEST(storage_dir_rename); + + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_dir_remove(storage, EXT_PATH("dir.old")); + storage_dir_remove(storage, EXT_PATH("dir.new")); + 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, "/data/test"); + int32_t ret = storage_file_create(storage, "/data/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, "/data")); + 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); +} + +#define MD5_HASH_SIZE (16) +#include + +MU_TEST(test_md5_calc) { + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + const char* path = UNIT_TESTS_PATH("storage/md5.txt"); + const char* md5_cstr = "2a456fa43e75088fdde41c93159d62a2"; + const uint8_t md5[MD5_HASH_SIZE] = { + 0x2a, + 0x45, + 0x6f, + 0xa4, + 0x3e, + 0x75, + 0x08, + 0x8f, + 0xdd, + 0xe4, + 0x1c, + 0x93, + 0x15, + 0x9d, + 0x62, + 0xa2, + }; + + uint8_t md5_output[MD5_HASH_SIZE]; + FuriString* md5_output_str = furi_string_alloc(); + memset(md5_output, 0, MD5_HASH_SIZE); + + mu_check(md5_calc_file(file, path, md5_output, NULL)); + mu_check(md5_string_calc_file(file, path, md5_output_str, NULL)); + + mu_assert_mem_eq(md5, md5_output, MD5_HASH_SIZE); + mu_assert_string_eq(md5_cstr, furi_string_get_cstr(md5_output_str)); + + storage_file_free(file); + furi_string_free(md5_output_str); + 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); +} + +MU_TEST_SUITE(test_md5_calc_suite) { + MU_RUN_TEST(test_md5_calc); +} + +int run_minunit_test_storage() { + MU_RUN_SUITE(storage_file); + MU_RUN_SUITE(storage_file_64k); + MU_RUN_SUITE(storage_dir); + MU_RUN_SUITE(storage_rename); + MU_RUN_SUITE(test_data_path); + MU_RUN_SUITE(test_storage_common); + MU_RUN_SUITE(test_md5_calc_suite); + return MU_EXIT_CODE; +} diff --git a/applications/unit_tests/stream/stream_test.c b/applications/debug/unit_tests/stream/stream_test.c similarity index 88% rename from applications/unit_tests/stream/stream_test.c rename to applications/debug/unit_tests/stream/stream_test.c index b5a2d398065..2fa3b21a297 100644 --- a/applications/unit_tests/stream/stream_test.c +++ b/applications/debug/unit_tests/stream/stream_test.c @@ -18,8 +18,8 @@ static const char* stream_test_right_data = MU_TEST_1(stream_composite_subtest, Stream* stream) { const size_t data_size = 128; uint8_t data[data_size]; - string_t string_lee; - string_init_set(string_lee, "lee"); + FuriString* string_lee; + string_lee = furi_string_alloc_set("lee"); // test that stream is empty // "" -> "" @@ -72,8 +72,32 @@ MU_TEST_1(stream_composite_subtest, Stream* stream) { mu_check(stream_seek(stream, -3, StreamOffsetFromEnd)); mu_check(stream_tell(stream) == 4); - // write string with replacemet + // test seeks to char. content: '1337_69' + stream_rewind(stream); + mu_check(stream_seek_to_char(stream, '3', StreamDirectionForward)); + mu_check(stream_tell(stream) == 1); + mu_check(stream_seek_to_char(stream, '3', StreamDirectionForward)); + mu_check(stream_tell(stream) == 2); + mu_check(stream_seek_to_char(stream, '_', StreamDirectionForward)); + mu_check(stream_tell(stream) == 4); + mu_check(stream_seek_to_char(stream, '9', StreamDirectionForward)); + mu_check(stream_tell(stream) == 6); + mu_check(!stream_seek_to_char(stream, '9', StreamDirectionForward)); + mu_check(stream_tell(stream) == 6); + mu_check(stream_seek_to_char(stream, '_', StreamDirectionBackward)); + mu_check(stream_tell(stream) == 4); + mu_check(stream_seek_to_char(stream, '3', StreamDirectionBackward)); + mu_check(stream_tell(stream) == 2); + mu_check(stream_seek_to_char(stream, '3', StreamDirectionBackward)); + mu_check(stream_tell(stream) == 1); + mu_check(!stream_seek_to_char(stream, '3', StreamDirectionBackward)); + mu_check(stream_tell(stream) == 1); + mu_check(stream_seek_to_char(stream, '1', StreamDirectionBackward)); + mu_check(stream_tell(stream) == 0); + + // write string with replacement // "1337_69" -> "1337lee" + mu_check(stream_seek(stream, 4, StreamOffsetFromStart)); mu_check(stream_write_string(stream, string_lee) == 3); mu_check(stream_size(stream) == 7); mu_check(stream_tell(stream) == 7); @@ -219,21 +243,21 @@ MU_TEST_1(stream_composite_subtest, Stream* stream) { mu_check(stream_eof(stream)); mu_assert_int_eq(0, stream_tell(stream)); - // insert formated string at the end of stream + // insert formatted string at the end of stream // "" -> "dio666" mu_check(stream_insert_format(stream, "%s%d", "dio", 666)); mu_assert_int_eq(6, stream_size(stream)); mu_check(stream_eof(stream)); mu_assert_int_eq(6, stream_tell(stream)); - // insert formated string at the end of stream + // insert formatted string at the end of stream // "dio666" -> "dio666zlo555" mu_check(stream_insert_format(stream, "%s%d", "zlo", 555)); mu_assert_int_eq(12, stream_size(stream)); mu_check(stream_eof(stream)); mu_assert_int_eq(12, stream_tell(stream)); - // insert formated string at the 6 pos + // insert formatted string at the 6 pos // "dio666" -> "dio666baba13zlo555" mu_check(stream_seek(stream, 6, StreamOffsetFromStart)); mu_check(stream_insert_format(stream, "%s%d", "baba", 13)); @@ -267,7 +291,7 @@ MU_TEST_1(stream_composite_subtest, Stream* stream) { mu_assert_int_eq(9, stream_tell(stream)); mu_check(stream_eof(stream)); - string_clear(string_lee); + furi_string_free(string_lee); } MU_TEST(stream_composite_test) { @@ -416,10 +440,10 @@ MU_TEST(stream_buffered_write_after_read_test) { } MU_TEST(stream_buffered_large_file_test) { - string_t input_data; - string_t output_data; - string_init(input_data); - string_init(output_data); + FuriString* input_data; + FuriString* output_data; + input_data = furi_string_alloc(); + output_data = furi_string_alloc(); Storage* storage = furi_record_open(RECORD_STORAGE); @@ -429,7 +453,7 @@ MU_TEST(stream_buffered_large_file_test) { const size_t rep_count = data_size / line_size + 1; for(size_t i = 0; i < rep_count; ++i) { - string_cat_printf(input_data, "%s\n", stream_test_data); + furi_string_cat_printf(input_data, "%s\n", stream_test_data); } // write test data to file @@ -437,8 +461,8 @@ MU_TEST(stream_buffered_large_file_test) { mu_check(buffered_file_stream_open( stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); mu_assert_int_eq(0, stream_size(stream)); - mu_assert_int_eq(string_size(input_data), stream_write_string(stream, input_data)); - mu_assert_int_eq(string_size(input_data), stream_size(stream)); + mu_assert_int_eq(furi_string_size(input_data), stream_write_string(stream, input_data)); + mu_assert_int_eq(furi_string_size(input_data), stream_size(stream)); const size_t substr_start = 8; const size_t substr_len = 11; @@ -475,23 +499,23 @@ MU_TEST(stream_buffered_large_file_test) { // read the whole file mu_check(stream_rewind(stream)); - string_t tmp; - string_init(tmp); + FuriString* tmp; + tmp = furi_string_alloc(); while(stream_read_line(stream, tmp)) { - string_cat(output_data, tmp); + furi_string_cat(output_data, tmp); } - string_clear(tmp); + furi_string_free(tmp); // check against generated data - mu_assert_int_eq(string_size(input_data), string_size(output_data)); - mu_check(string_equal_p(input_data, output_data)); + mu_assert_int_eq(furi_string_size(input_data), furi_string_size(output_data)); + mu_check(furi_string_equal(input_data, output_data)); mu_check(stream_eof(stream)); stream_free(stream); furi_record_close(RECORD_STORAGE); - string_clear(input_data); - string_clear(output_data); + furi_string_free(input_data); + furi_string_free(output_data); } MU_TEST_SUITE(stream_suite) { diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c new file mode 100644 index 00000000000..60c7abd0323 --- /dev/null +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -0,0 +1,908 @@ +#include +#include +#include "../minunit.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define TAG "SubGhzTest" +#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 329 +#define TEST_TIMEOUT 10000 + +static SubGhzEnvironment* environment_handler; +static SubGhzReceiver* receiver_handler; +//static SubGhzTransmitter* transmitter_handler; +static SubGhzFileEncoderWorker* file_worker_encoder_handler; +static uint16_t subghz_test_decoder_count = 0; + +static void subghz_test_rx_callback( + SubGhzReceiver* receiver, + SubGhzProtocolDecoderBase* decoder_base, + void* context) { + UNUSED(receiver); + UNUSED(context); + FuriString* text; + text = furi_string_alloc(); + subghz_protocol_decoder_base_get_string(decoder_base, text); + subghz_receiver_reset(receiver_handler); + FURI_LOG_T(TAG, "\r\n%s", furi_string_get_cstr(text)); + furi_string_free(text); + subghz_test_decoder_count++; +} + +static void subghz_test_init(void) { + environment_handler = subghz_environment_alloc(); + subghz_environment_set_came_atomo_rainbow_table_file_name( + 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); + + subghz_devices_init(); + + receiver_handler = subghz_receiver_alloc_init(environment_handler); + subghz_receiver_set_filter(receiver_handler, SubGhzProtocolFlag_Decodable); + subghz_receiver_set_rx_callback(receiver_handler, subghz_test_rx_callback, NULL); +} + +static void subghz_test_deinit(void) { + subghz_devices_deinit(); + subghz_receiver_free(receiver_handler); + subghz_environment_free(environment_handler); +} + +static bool subghz_decoder_test(const char* path, const char* name_decoder) { + subghz_test_decoder_count = 0; + uint32_t test_start = furi_get_tick(); + + SubGhzProtocolDecoderBase* decoder = + subghz_receiver_search_decoder_base_by_name(receiver_handler, name_decoder); + + if(decoder) { + file_worker_encoder_handler = subghz_file_encoder_worker_alloc(); + if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path, NULL)) { + // the worker needs a file in order to open and read part of the file + furi_delay_ms(100); + + LevelDuration level_duration; + while(furi_get_tick() - test_start < TEST_TIMEOUT) { + level_duration = + subghz_file_encoder_worker_get_level_duration(file_worker_encoder_handler); + if(!level_duration_is_reset(level_duration)) { + bool level = level_duration_get_level(level_duration); + uint32_t duration = level_duration_get_duration(level_duration); + // Yield, to load data inside the worker + furi_thread_yield(); + decoder->protocol->decoder->feed(decoder, level, duration); + } else { + break; + } + } + furi_delay_ms(10); + } + if(subghz_file_encoder_worker_is_running(file_worker_encoder_handler)) { + subghz_file_encoder_worker_stop(file_worker_encoder_handler); + } + subghz_file_encoder_worker_free(file_worker_encoder_handler); + } + FURI_LOG_T(TAG, "Decoder count parse %d", subghz_test_decoder_count); + if(furi_get_tick() - test_start > TEST_TIMEOUT) { + printf("Test decoder %s ERROR TimeOut\r\n", name_decoder); + return false; + } else { + return subghz_test_decoder_count ? true : false; + } +} + +static bool subghz_decode_random_test(const char* path) { + subghz_test_decoder_count = 0; + subghz_receiver_reset(receiver_handler); + uint32_t test_start = furi_get_tick(); + + file_worker_encoder_handler = subghz_file_encoder_worker_alloc(); + if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path, NULL)) { + // the worker needs a file in order to open and read part of the file + furi_delay_ms(100); + + LevelDuration level_duration; + while(furi_get_tick() - test_start < TEST_TIMEOUT * 10) { + level_duration = + subghz_file_encoder_worker_get_level_duration(file_worker_encoder_handler); + if(!level_duration_is_reset(level_duration)) { + bool level = level_duration_get_level(level_duration); + uint32_t duration = level_duration_get_duration(level_duration); + // Yield, to load data inside the worker + furi_thread_yield(); + subghz_receiver_decode(receiver_handler, level, duration); + } else { + break; + } + } + furi_delay_ms(10); + if(subghz_file_encoder_worker_is_running(file_worker_encoder_handler)) { + subghz_file_encoder_worker_stop(file_worker_encoder_handler); + } + subghz_file_encoder_worker_free(file_worker_encoder_handler); + } + FURI_LOG_D(TAG, "Decoder count parse %d", subghz_test_decoder_count); + if(furi_get_tick() - test_start > TEST_TIMEOUT * 10) { + printf("Random test ERROR TimeOut\r\n"); + return false; + } else if(subghz_test_decoder_count == TEST_RANDOM_COUNT_PARSE) { + return true; + } else { + return false; + } +} + +static bool subghz_encoder_test(const char* path) { + subghz_test_decoder_count = 0; + uint32_t test_start = furi_get_tick(); + FuriString* temp_str; + temp_str = furi_string_alloc(); + bool file_load = false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + + do { + if(!flipper_format_file_open_existing(fff_data_file, path)) { + FURI_LOG_E(TAG, "Error open file %s", path); + break; + } + + if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { + FURI_LOG_E(TAG, "Missing Preset"); + break; + } + + if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { + FURI_LOG_E(TAG, "Missing Protocol"); + break; + } + file_load = true; + } while(false); + if(file_load) { + SubGhzTransmitter* transmitter = + subghz_transmitter_alloc_init(environment_handler, furi_string_get_cstr(temp_str)); + subghz_transmitter_deserialize(transmitter, fff_data_file); + + SubGhzProtocolDecoderBase* decoder = subghz_receiver_search_decoder_base_by_name( + receiver_handler, furi_string_get_cstr(temp_str)); + + if(decoder) { + LevelDuration level_duration; + while(furi_get_tick() - test_start < TEST_TIMEOUT) { + level_duration = subghz_transmitter_yield(transmitter); + if(!level_duration_is_reset(level_duration)) { + bool level = level_duration_get_level(level_duration); + uint32_t duration = level_duration_get_duration(level_duration); + decoder->protocol->decoder->feed(decoder, level, duration); + } else { + break; + } + } + furi_delay_ms(10); + } + subghz_transmitter_free(transmitter); + } + flipper_format_free(fff_data_file); + FURI_LOG_T(TAG, "Decoder count parse %d", subghz_test_decoder_count); + if(furi_get_tick() - test_start > TEST_TIMEOUT) { + printf("Test encoder %s ERROR TimeOut\r\n", furi_string_get_cstr(temp_str)); + subghz_test_decoder_count = 0; + } + furi_string_free(temp_str); + + return subghz_test_decoder_count ? true : false; +} + +MU_TEST(subghz_keystore_test) { + mu_assert( + subghz_environment_load_keystore(environment_handler, KEYSTORE_DIR_NAME), + "Test keystore error"); +} + +typedef enum { + SubGhzHalAsyncTxTestTypeNormal, + SubGhzHalAsyncTxTestTypeInvalidStart, + SubGhzHalAsyncTxTestTypeInvalidMid, + SubGhzHalAsyncTxTestTypeInvalidEnd, + SubGhzHalAsyncTxTestTypeResetStart, + SubGhzHalAsyncTxTestTypeResetMid, + SubGhzHalAsyncTxTestTypeResetEnd, +} SubGhzHalAsyncTxTestType; + +typedef struct { + SubGhzHalAsyncTxTestType type; + size_t pos; +} SubGhzHalAsyncTxTest; + +#define SUBGHZ_HAL_TEST_DURATION 1 + +static LevelDuration subghz_hal_async_tx_test_yield(void* context) { + SubGhzHalAsyncTxTest* test = context; + bool is_odd = test->pos % 2; + + if(test->type == SubGhzHalAsyncTxTestTypeNormal) { + if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + test->pos++; + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + test->pos++; + return level_duration_reset(); + } else { + furi_crash("Yield after reset"); + } + } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidStart) { + if(test->pos == 0) { + test->pos++; + return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION); + } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + test->pos++; + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + test->pos++; + return level_duration_reset(); + } else { + furi_crash("Yield after reset"); + } + } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidMid) { + if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { + test->pos++; + return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION); + } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + test->pos++; + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + test->pos++; + return level_duration_reset(); + } else { + furi_crash("Yield after reset"); + } + } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidEnd) { + if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) { + test->pos++; + return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION); + } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + test->pos++; + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) { + test->pos++; + return level_duration_reset(); + } else { + furi_crash("Yield after reset"); + } + } else if(test->type == SubGhzHalAsyncTxTestTypeResetStart) { + if(test->pos == 0) { + test->pos++; + return level_duration_reset(); + } else { + furi_crash("Yield after reset"); + } + } else if(test->type == SubGhzHalAsyncTxTestTypeResetMid) { + if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { + test->pos++; + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) { + test->pos++; + return level_duration_reset(); + } else { + furi_crash("Yield after reset"); + } + } else if(test->type == SubGhzHalAsyncTxTestTypeResetEnd) { + if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) { + test->pos++; + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION); + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) { + test->pos++; + return level_duration_reset(); + } else { + furi_crash("Yield after reset"); + } + } else { + furi_crash("Programming error"); + } +} + +bool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) { + SubGhzHalAsyncTxTest test = {0}; + test.type = type; + furi_hal_subghz_reset(); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); + furi_hal_subghz_set_frequency_and_path(433920000); + + if(!furi_hal_subghz_start_async_tx(subghz_hal_async_tx_test_yield, &test)) { + mu_warn("SubGHZ transmission is prohibited"); + return false; + } + + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(30000000); + + while(!furi_hal_subghz_is_async_tx_complete()) { + if(furi_hal_cortex_timer_is_expired(timer)) { + return false; + } + furi_delay_ms(10); + } + furi_hal_subghz_stop_async_tx(); + furi_hal_subghz_sleep(); + + return true; +} + +MU_TEST(subghz_hal_async_tx_test) { + mu_assert( + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeNormal), + "Test furi_hal_async_tx normal"); + mu_assert( + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidStart), + "Test furi_hal_async_tx invalid start"); + mu_assert( + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidMid), + "Test furi_hal_async_tx invalid mid"); + mu_assert( + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidEnd), + "Test furi_hal_async_tx invalid end"); + mu_assert( + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetStart), + "Test furi_hal_async_tx reset start"); + mu_assert( + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetMid), + "Test furi_hal_async_tx reset mid"); + mu_assert( + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetEnd), + "Test furi_hal_async_tx reset end"); +} + +//test decoders +MU_TEST(subghz_decoder_came_atomo_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/came_atomo_raw.sub"), SUBGHZ_PROTOCOL_CAME_ATOMO_NAME), + "Test decoder " SUBGHZ_PROTOCOL_CAME_ATOMO_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_came_test) { + mu_assert( + subghz_decoder_test(EXT_PATH("unit_tests/subghz/came_raw.sub"), SUBGHZ_PROTOCOL_CAME_NAME), + "Test decoder " SUBGHZ_PROTOCOL_CAME_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_came_twee_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/came_twee_raw.sub"), SUBGHZ_PROTOCOL_CAME_TWEE_NAME), + "Test decoder " SUBGHZ_PROTOCOL_CAME_TWEE_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_faac_slh_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/faac_slh_raw.sub"), SUBGHZ_PROTOCOL_FAAC_SLH_NAME), + "Test decoder " SUBGHZ_PROTOCOL_FAAC_SLH_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_gate_tx_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/gate_tx_raw.sub"), SUBGHZ_PROTOCOL_GATE_TX_NAME), + "Test decoder " SUBGHZ_PROTOCOL_GATE_TX_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_hormann_hsm_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/hormann_hsm_raw.sub"), SUBGHZ_PROTOCOL_HORMANN_HSM_NAME), + "Test decoder " SUBGHZ_PROTOCOL_HORMANN_HSM_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_ido_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/ido_117_111_raw.sub"), SUBGHZ_PROTOCOL_IDO_NAME), + "Test decoder " SUBGHZ_PROTOCOL_IDO_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_keeloq_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/doorhan_raw.sub"), SUBGHZ_PROTOCOL_KEELOQ_NAME), + "Test decoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_kia_seed_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/kia_seed_raw.sub"), SUBGHZ_PROTOCOL_KIA_NAME), + "Test decoder " SUBGHZ_PROTOCOL_KIA_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_nero_radio_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/nero_radio_raw.sub"), SUBGHZ_PROTOCOL_NERO_RADIO_NAME), + "Test decoder " SUBGHZ_PROTOCOL_NERO_RADIO_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_nero_sketch_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/nero_sketch_raw.sub"), SUBGHZ_PROTOCOL_NERO_SKETCH_NAME), + "Test decoder " SUBGHZ_PROTOCOL_NERO_SKETCH_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_nice_flo_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/nice_flo_raw.sub"), SUBGHZ_PROTOCOL_NICE_FLO_NAME), + "Test decoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_nice_flor_s_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/nice_flor_s_raw.sub"), SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME), + "Test decoder " SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_princeton_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/Princeton_raw.sub"), SUBGHZ_PROTOCOL_PRINCETON_NAME), + "Test decoder " SUBGHZ_PROTOCOL_PRINCETON_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_scher_khan_magic_code_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/scher_khan_magic_code.sub"), + SUBGHZ_PROTOCOL_SCHER_KHAN_NAME), + "Test decoder " SUBGHZ_PROTOCOL_SCHER_KHAN_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_somfy_keytis_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/Somfy_keytis_raw.sub"), SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME), + "Test decoder " SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_somfy_telis_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/somfy_telis_raw.sub"), SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME), + "Test decoder " SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_star_line_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/cenmax_raw.sub"), SUBGHZ_PROTOCOL_STAR_LINE_NAME), + "Test decoder " SUBGHZ_PROTOCOL_STAR_LINE_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_linear_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/linear_raw.sub"), SUBGHZ_PROTOCOL_LINEAR_NAME), + "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( + EXT_PATH("unit_tests/subghz/megacode_raw.sub"), SUBGHZ_PROTOCOL_MEGACODE_NAME), + "Test decoder " SUBGHZ_PROTOCOL_MEGACODE_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_secplus_v1_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/security_pls_1_0_raw.sub"), + SUBGHZ_PROTOCOL_SECPLUS_V1_NAME), + "Test decoder " SUBGHZ_PROTOCOL_SECPLUS_V1_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_secplus_v2_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/security_pls_2_0_raw.sub"), + SUBGHZ_PROTOCOL_SECPLUS_V2_NAME), + "Test decoder " SUBGHZ_PROTOCOL_SECPLUS_V2_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_holtek_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/holtek_raw.sub"), SUBGHZ_PROTOCOL_HOLTEK_NAME), + "Test decoder " SUBGHZ_PROTOCOL_HOLTEK_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_power_smart_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/power_smart_raw.sub"), SUBGHZ_PROTOCOL_POWER_SMART_NAME), + "Test decoder " SUBGHZ_PROTOCOL_POWER_SMART_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_marantec_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/marantec_raw.sub"), SUBGHZ_PROTOCOL_MARANTEC_NAME), + "Test decoder " SUBGHZ_PROTOCOL_MARANTEC_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_bett_test) { + mu_assert( + subghz_decoder_test(EXT_PATH("unit_tests/subghz/bett_raw.sub"), SUBGHZ_PROTOCOL_BETT_NAME), + "Test decoder " SUBGHZ_PROTOCOL_BETT_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_doitrand_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/doitrand_raw.sub"), SUBGHZ_PROTOCOL_DOITRAND_NAME), + "Test decoder " SUBGHZ_PROTOCOL_DOITRAND_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_phoenix_v2_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/phoenix_v2_raw.sub"), SUBGHZ_PROTOCOL_PHOENIX_V2_NAME), + "Test decoder " SUBGHZ_PROTOCOL_PHOENIX_V2_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_honeywell_wdb_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/honeywell_wdb_raw.sub"), + SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME), + "Test decoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_magellan_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/magellan_raw.sub"), SUBGHZ_PROTOCOL_MAGELLAN_NAME), + "Test decoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_intertechno_v3_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/intertechno_v3_raw.sub"), + SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME), + "Test decoder " SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_clemsa_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/clemsa_raw.sub"), SUBGHZ_PROTOCOL_CLEMSA_NAME), + "Test decoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_ansonic_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/ansonic_raw.sub"), SUBGHZ_PROTOCOL_ANSONIC_NAME), + "Test decoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_smc5326_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/smc5326_raw.sub"), SUBGHZ_PROTOCOL_SMC5326_NAME), + "Test decoder " SUBGHZ_PROTOCOL_SMC5326_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_holtek_ht12x_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/holtek_ht12x_raw.sub"), SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME), + "Test decoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n"); +} + +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"); +} + +MU_TEST(subghz_decoder_mastercode_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/mastercode_raw.sub"), SUBGHZ_PROTOCOL_MASTERCODE_NAME), + "Test decoder " SUBGHZ_PROTOCOL_MASTERCODE_NAME " error\r\n"); +} + +//test encoders +MU_TEST(subghz_encoder_princeton_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/princeton.sub")), + "Test encoder " SUBGHZ_PROTOCOL_PRINCETON_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_came_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/came.sub")), + "Test encoder " SUBGHZ_PROTOCOL_CAME_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_came_twee_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/came_twee.sub")), + "Test encoder " SUBGHZ_PROTOCOL_CAME_TWEE_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_gate_tx_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/gate_tx.sub")), + "Test encoder " SUBGHZ_PROTOCOL_GATE_TX_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_nice_flo_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/nice_flo.sub")), + "Test encoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_keeloq_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/doorhan.sub")), + "Test encoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_linear_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/linear.sub")), + "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")), + "Test encoder " SUBGHZ_PROTOCOL_MEGACODE_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_holtek_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/holtek.sub")), + "Test encoder " SUBGHZ_PROTOCOL_HOLTEK_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_secplus_v1_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/security_pls_1_0.sub")), + "Test encoder " SUBGHZ_PROTOCOL_SECPLUS_V1_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_secplus_v2_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/security_pls_2_0.sub")), + "Test encoder " SUBGHZ_PROTOCOL_SECPLUS_V2_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_power_smart_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/power_smart.sub")), + "Test encoder " SUBGHZ_PROTOCOL_POWER_SMART_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_marantec_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/marantec.sub")), + "Test encoder " SUBGHZ_PROTOCOL_MARANTEC_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_bett_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/bett.sub")), + "Test encoder " SUBGHZ_PROTOCOL_BETT_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_doitrand_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/doitrand.sub")), + "Test encoder " SUBGHZ_PROTOCOL_DOITRAND_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_phoenix_v2_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/phoenix_v2.sub")), + "Test encoder " SUBGHZ_PROTOCOL_PHOENIX_V2_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_honeywell_wdb_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/honeywell_wdb.sub")), + "Test encoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_magellan_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/magellan.sub")), + "Test encoder " SUBGHZ_PROTOCOL_MAGELLAN_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_intertechno_v3_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/intertechno_v3.sub")), + "Test encoder " SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_clemsa_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/clemsa.sub")), + "Test encoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_ansonic_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/ansonic.sub")), + "Test encoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_smc5326_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/smc5326.sub")), + "Test encoder " SUBGHZ_PROTOCOL_SMC5326_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_holtek_ht12x_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/holtek_ht12x.sub")), + "Test encoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n"); +} + +MU_TEST(subghz_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_encoder_mastercode_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/mastercode.sub")), + "Test encoder " SUBGHZ_PROTOCOL_MASTERCODE_NAME " error\r\n"); +} + +MU_TEST(subghz_random_test) { + mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); +} + +MU_TEST_SUITE(subghz) { + subghz_test_init(); + MU_RUN_TEST(subghz_keystore_test); + + MU_RUN_TEST(subghz_hal_async_tx_test); + + MU_RUN_TEST(subghz_decoder_came_atomo_test); + MU_RUN_TEST(subghz_decoder_came_test); + MU_RUN_TEST(subghz_decoder_came_twee_test); + MU_RUN_TEST(subghz_decoder_faac_slh_test); + MU_RUN_TEST(subghz_decoder_gate_tx_test); + MU_RUN_TEST(subghz_decoder_hormann_hsm_test); + MU_RUN_TEST(subghz_decoder_ido_test); + MU_RUN_TEST(subghz_decoder_keeloq_test); + MU_RUN_TEST(subghz_decoder_kia_seed_test); + MU_RUN_TEST(subghz_decoder_nero_radio_test); + MU_RUN_TEST(subghz_decoder_nero_sketch_test); + MU_RUN_TEST(subghz_decoder_nice_flo_test); + MU_RUN_TEST(subghz_decoder_nice_flor_s_test); + MU_RUN_TEST(subghz_decoder_princeton_test); + MU_RUN_TEST(subghz_decoder_scher_khan_magic_code_test); + MU_RUN_TEST(subghz_decoder_somfy_keytis_test); + 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); + MU_RUN_TEST(subghz_decoder_holtek_test); + MU_RUN_TEST(subghz_decoder_power_smart_test); + MU_RUN_TEST(subghz_decoder_marantec_test); + MU_RUN_TEST(subghz_decoder_bett_test); + MU_RUN_TEST(subghz_decoder_doitrand_test); + MU_RUN_TEST(subghz_decoder_phoenix_v2_test); + MU_RUN_TEST(subghz_decoder_honeywell_wdb_test); + MU_RUN_TEST(subghz_decoder_magellan_test); + 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_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_decoder_mastercode_test); + + MU_RUN_TEST(subghz_encoder_princeton_test); + MU_RUN_TEST(subghz_encoder_came_test); + MU_RUN_TEST(subghz_encoder_came_twee_test); + MU_RUN_TEST(subghz_encoder_gate_tx_test); + MU_RUN_TEST(subghz_encoder_nice_flo_test); + MU_RUN_TEST(subghz_encoder_keeloq_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); + MU_RUN_TEST(subghz_encoder_secplus_v2_test); + MU_RUN_TEST(subghz_encoder_power_smart_test); + MU_RUN_TEST(subghz_encoder_marantec_test); + MU_RUN_TEST(subghz_encoder_bett_test); + MU_RUN_TEST(subghz_encoder_doitrand_test); + MU_RUN_TEST(subghz_encoder_phoenix_v2_test); + MU_RUN_TEST(subghz_encoder_honeywell_wdb_test); + MU_RUN_TEST(subghz_encoder_magellan_test); + MU_RUN_TEST(subghz_encoder_intertechno_v3_test); + MU_RUN_TEST(subghz_encoder_clemsa_test); + MU_RUN_TEST(subghz_encoder_ansonic_test); + MU_RUN_TEST(subghz_encoder_smc5326_test); + MU_RUN_TEST(subghz_encoder_holtek_ht12x_test); + MU_RUN_TEST(subghz_encoder_dooya_test); + MU_RUN_TEST(subghz_encoder_mastercode_test); + + MU_RUN_TEST(subghz_random_test); + subghz_test_deinit(); +} + +int run_minunit_test_subghz() { + MU_RUN_SUITE(subghz); + return MU_EXIT_CODE; +} diff --git a/applications/debug/unit_tests/test_index.c b/applications/debug/unit_tests/test_index.c new file mode 100644 index 00000000000..d7afaa3c4f3 --- /dev/null +++ b/applications/debug/unit_tests/test_index.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include "minunit_vars.h" +#include +#include +#include + +#define TAG "UnitTests" + +int run_minunit_test_furi(); +int run_minunit_test_furi_hal(); +int run_minunit_test_furi_hal_crypto(); +int run_minunit_test_furi_string(); +int run_minunit_test_infrared(); +int run_minunit_test_rpc(); +int run_minunit_test_manifest(); +int run_minunit_test_flipper_format(); +int run_minunit_test_flipper_format_string(); +int run_minunit_test_stream(); +int run_minunit_test_storage(); +int run_minunit_test_subghz(); +int run_minunit_test_dirwalk(); +int run_minunit_test_power(); +int run_minunit_test_protocol_dict(); +int run_minunit_test_lfrfid_protocols(); +int run_minunit_test_nfc(); +int run_minunit_test_bit_lib(); +int run_minunit_test_float_tools(); +int run_minunit_test_bt(); +int run_minunit_test_dialogs_file_browser_options(); + +typedef int (*UnitTestEntry)(); + +typedef struct { + const char* name; + const UnitTestEntry entry; +} UnitTest; + +const UnitTest unit_tests[] = { + {.name = "furi", .entry = run_minunit_test_furi}, + {.name = "furi_hal", .entry = run_minunit_test_furi_hal}, + {.name = "furi_hal_crypto", .entry = run_minunit_test_furi_hal_crypto}, + {.name = "furi_string", .entry = run_minunit_test_furi_string}, + {.name = "storage", .entry = run_minunit_test_storage}, + {.name = "stream", .entry = run_minunit_test_stream}, + {.name = "dirwalk", .entry = run_minunit_test_dirwalk}, + {.name = "manifest", .entry = run_minunit_test_manifest}, + {.name = "flipper_format", .entry = run_minunit_test_flipper_format}, + {.name = "flipper_format_string", .entry = run_minunit_test_flipper_format_string}, + {.name = "rpc", .entry = run_minunit_test_rpc}, + {.name = "subghz", .entry = run_minunit_test_subghz}, + {.name = "infrared", .entry = run_minunit_test_infrared}, + {.name = "nfc", .entry = run_minunit_test_nfc}, + {.name = "power", .entry = run_minunit_test_power}, + {.name = "protocol_dict", .entry = run_minunit_test_protocol_dict}, + {.name = "lfrfid", .entry = run_minunit_test_lfrfid_protocols}, + {.name = "bit_lib", .entry = run_minunit_test_bit_lib}, + {.name = "float_tools", .entry = run_minunit_test_float_tools}, + {.name = "bt", .entry = run_minunit_test_bt}, + {.name = "dialogs_file_browser_options", + .entry = run_minunit_test_dialogs_file_browser_options}, +}; + +void minunit_print_progress() { + static const char progress[] = {'\\', '|', '/', '-'}; + static uint8_t progress_counter = 0; + static uint32_t last_tick = 0; + uint32_t current_tick = furi_get_tick(); + if(current_tick - last_tick > 20) { + last_tick = current_tick; + printf("[%c]\033[3D", progress[++progress_counter % COUNT_OF(progress)]); + fflush(stdout); + } +} + +void minunit_print_fail(const char* str) { + printf(_FURI_LOG_CLR_E "%s\r\n" _FURI_LOG_CLR_RESET, str); +} + +void minunit_printf_warning(const char* format, ...) { + FuriString* str = furi_string_alloc(); + va_list args; + va_start(args, format); + furi_string_vprintf(str, format, args); + va_end(args); + printf(_FURI_LOG_CLR_W "%s\r\n" _FURI_LOG_CLR_RESET, furi_string_get_cstr(str)); + furi_string_free(str); +} + +void unit_tests_cli(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(args); + UNUSED(context); + minunit_run = 0; + minunit_assert = 0; + minunit_fail = 0; + minunit_status = 0; + + Loader* loader = furi_record_open(RECORD_LOADER); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + + // TODO FL-3491: lock device while test running + if(loader_is_locked(loader)) { + printf("RPC: stop all applications to run tests\r\n"); + notification_message(notification, &sequence_blink_magenta_100); + } else { + notification_message_block(notification, &sequence_set_only_blue_255); + + uint32_t heap_before = memmgr_get_free_heap(); + uint32_t cycle_counter = furi_get_tick(); + + for(size_t i = 0; i < COUNT_OF(unit_tests); i++) { + if(cli_cmd_interrupt_received(cli)) { + break; + } + + if(furi_string_size(args)) { + if(furi_string_cmp_str(args, unit_tests[i].name) == 0) { + unit_tests[i].entry(); + } else { + printf("Skipping %s\r\n", unit_tests[i].name); + } + } else { + unit_tests[i].entry(); + } + } + + if(minunit_run != 0) { + printf("\r\nFailed tests: %u\r\n", minunit_fail); + + // Time report + cycle_counter = (furi_get_tick() - cycle_counter); + printf("Consumed: %lu ms\r\n", cycle_counter); + + // Wait for tested services and apps to deallocate memory + furi_delay_ms(200); + uint32_t heap_after = memmgr_get_free_heap(); + printf("Leaked: %ld\r\n", heap_before - heap_after); + + // Final Report + if(minunit_fail == 0) { + notification_message(notification, &sequence_success); + printf("Status: PASSED\r\n"); + } else { + notification_message(notification, &sequence_error); + printf("Status: FAILED\r\n"); + } + } + } + + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_LOADER); +} + +void unit_tests_on_system_start() { +#ifdef SRV_CLI + Cli* cli = furi_record_open(RECORD_CLI); + + // We need to launch apps from tests, so we cannot lock loader + cli_add_command(cli, "unit_tests", CliCommandFlagParallelSafe, unit_tests_cli, NULL); + furi_record_close(RECORD_CLI); +#endif +} diff --git a/applications/debug/unit_tests/varint/varint_test.c b/applications/debug/unit_tests/varint/varint_test.c new file mode 100644 index 00000000000..8faab136883 --- /dev/null +++ b/applications/debug/unit_tests/varint/varint_test.c @@ -0,0 +1,88 @@ +#include +#include +#include "../minunit.h" +#include +#include + +MU_TEST(test_varint_basic_u) { + mu_assert_int_eq(1, varint_uint32_length(0)); + mu_assert_int_eq(5, varint_uint32_length(UINT32_MAX)); + + uint8_t data[8] = {}; + uint32_t out_value; + + mu_assert_int_eq(1, varint_uint32_pack(0, data)); + mu_assert_int_eq(1, varint_uint32_unpack(&out_value, data, 8)); + mu_assert_int_eq(0, out_value); + + mu_assert_int_eq(5, varint_uint32_pack(UINT32_MAX, data)); + mu_assert_int_eq(5, varint_uint32_unpack(&out_value, data, 8)); + mu_assert_int_eq(UINT32_MAX, out_value); +} + +MU_TEST(test_varint_basic_i) { + mu_assert_int_eq(5, varint_int32_length(INT32_MIN / 2)); + mu_assert_int_eq(1, varint_int32_length(0)); + mu_assert_int_eq(5, varint_int32_length(INT32_MAX / 2)); + + mu_assert_int_eq(2, varint_int32_length(127)); + mu_assert_int_eq(2, varint_int32_length(-127)); + + uint8_t data[8] = {}; + int32_t out_value; + mu_assert_int_eq(1, varint_int32_pack(0, data)); + mu_assert_int_eq(1, varint_int32_unpack(&out_value, data, 8)); + mu_assert_int_eq(0, out_value); + + mu_assert_int_eq(2, varint_int32_pack(127, data)); + mu_assert_int_eq(2, varint_int32_unpack(&out_value, data, 8)); + mu_assert_int_eq(127, out_value); + + mu_assert_int_eq(2, varint_int32_pack(-127, data)); + mu_assert_int_eq(2, varint_int32_unpack(&out_value, data, 8)); + mu_assert_int_eq(-127, out_value); + + mu_assert_int_eq(5, varint_int32_pack(INT32_MAX, data)); + mu_assert_int_eq(5, varint_int32_unpack(&out_value, data, 8)); + mu_assert_int_eq(INT32_MAX, out_value); + + mu_assert_int_eq(5, varint_int32_pack(INT32_MIN / 2 + 1, data)); + mu_assert_int_eq(5, varint_int32_unpack(&out_value, data, 8)); + mu_assert_int_eq(INT32_MIN / 2 + 1, out_value); +} + +MU_TEST(test_varint_rand_u) { + uint8_t data[8] = {}; + uint32_t out_value; + + for(size_t i = 0; i < 200000; i++) { + uint32_t rand_value = rand(); + mu_assert_int_eq( + varint_uint32_pack(rand_value, data), varint_uint32_unpack(&out_value, data, 8)); + mu_assert_int_eq(rand_value, out_value); + } +} + +MU_TEST(test_varint_rand_i) { + uint8_t data[8] = {}; + int32_t out_value; + + for(size_t i = 0; i < 200000; i++) { + int32_t rand_value = rand() + (INT32_MIN / 2 + 1); + mu_assert_int_eq( + varint_int32_pack(rand_value, data), varint_int32_unpack(&out_value, data, 8)); + mu_assert_int_eq(rand_value, out_value); + } +} + +MU_TEST_SUITE(test_varint_suite) { + MU_RUN_TEST(test_varint_basic_u); + MU_RUN_TEST(test_varint_basic_i); + MU_RUN_TEST(test_varint_rand_u); + MU_RUN_TEST(test_varint_rand_i); +} + +int run_minunit_test_varint() { + MU_RUN_SUITE(test_varint_suite); + return MU_EXIT_CODE; +} \ No newline at end of file diff --git a/applications/debug/usb_mouse/application.fam b/applications/debug/usb_mouse/application.fam new file mode 100644 index 00000000000..7747613d58a --- /dev/null +++ b/applications/debug/usb_mouse/application.fam @@ -0,0 +1,10 @@ +App( + appid="usb_mouse", + name="USB Mouse Demo", + apptype=FlipperAppType.DEBUG, + entry_point="usb_mouse_app", + requires=["gui"], + stack_size=1 * 1024, + order=60, + fap_category="Debug", +) diff --git a/applications/debug_tools/usb_mouse.c b/applications/debug/usb_mouse/usb_mouse.c similarity index 100% rename from applications/debug_tools/usb_mouse.c rename to applications/debug/usb_mouse/usb_mouse.c diff --git a/applications/debug/usb_test/application.fam b/applications/debug/usb_test/application.fam new file mode 100644 index 00000000000..463bb4a26ef --- /dev/null +++ b/applications/debug/usb_test/application.fam @@ -0,0 +1,10 @@ +App( + appid="usb_test", + name="USB Test", + apptype=FlipperAppType.DEBUG, + entry_point="usb_test_app", + requires=["gui"], + stack_size=1 * 1024, + order=50, + fap_category="Debug", +) diff --git a/applications/debug_tools/usb_test.c b/applications/debug/usb_test/usb_test.c similarity index 100% rename from applications/debug_tools/usb_test.c rename to applications/debug/usb_test/usb_test.c diff --git a/applications/debug/vibro_test/application.fam b/applications/debug/vibro_test/application.fam new file mode 100644 index 00000000000..c35a7223f8a --- /dev/null +++ b/applications/debug/vibro_test/application.fam @@ -0,0 +1,10 @@ +App( + appid="vibro_test", + name="Vibro Test", + apptype=FlipperAppType.DEBUG, + entry_point="vibro_test_app", + requires=["gui"], + stack_size=1 * 1024, + order=20, + fap_category="Debug", +) diff --git a/applications/debug_tools/vibro_test.c b/applications/debug/vibro_test/vibro_test.c similarity index 100% rename from applications/debug_tools/vibro_test.c rename to applications/debug/vibro_test/vibro_test.c diff --git a/applications/debug_tools/application.fam b/applications/debug_tools/application.fam deleted file mode 100644 index 8cb495b0c4a..00000000000 --- a/applications/debug_tools/application.fam +++ /dev/null @@ -1,115 +0,0 @@ -App( - appid="debug_apps", - name="Basic debug apps bundle", - apptype=FlipperAppType.METAPACKAGE, - provides=[ - "blink_test", - "vibro_test", - "keypad_test", - "usb_test", - "usb_mouse", - "uart_echo", - "display_test", - "text_box_test", - "file_browser_test", - ], -) - -App( - appid="blink_test", - name="Blink Test", - apptype=FlipperAppType.DEBUG, - entry_point="blink_test_app", - cdefines=["APP_BLINK"], - requires=["gui"], - stack_size=1 * 1024, - order=10, -) - -App( - appid="vibro_test", - name="Vibro Test", - apptype=FlipperAppType.DEBUG, - entry_point="vibro_test_app", - cdefines=["APP_VIBRO_TEST"], - requires=["gui"], - stack_size=1 * 1024, - order=20, -) - -App( - appid="keypad_test", - name="Keypad Test", - apptype=FlipperAppType.DEBUG, - entry_point="keypad_test_app", - cdefines=["APP_KEYPAD_TEST"], - requires=["gui"], - stack_size=1 * 1024, - order=30, -) - -App( - appid="usb_test", - name="USB Test", - apptype=FlipperAppType.DEBUG, - entry_point="usb_test_app", - cdefines=["APP_USB_TEST"], - requires=["gui"], - stack_size=1 * 1024, - order=50, -) - -App( - appid="usb_mouse", - name="USB Mouse Demo", - apptype=FlipperAppType.DEBUG, - entry_point="usb_mouse_app", - cdefines=["APP_USB_MOUSE"], - requires=["gui"], - stack_size=1 * 1024, - order=60, -) - -App( - appid="uart_echo", - name="UART Echo", - apptype=FlipperAppType.DEBUG, - entry_point="uart_echo_app", - cdefines=["APP_UART_ECHO"], - requires=["gui"], - stack_size=2 * 1024, - order=70, -) - -App( - appid="display_test", - name="Display Test", - apptype=FlipperAppType.DEBUG, - entry_point="display_test_app", - cdefines=["APP_DISPLAY_TEST"], - requires=["gui"], - stack_size=1 * 1024, - order=120, -) - -App( - appid="text_box_test", - name="Text Box Test", - apptype=FlipperAppType.DEBUG, - entry_point="text_box_test_app", - cdefines=["APP_TEXT_BOX_TEST"], - requires=["gui"], - stack_size=1 * 1024, - order=140, -) - -App( - appid="file_browser_test", - name="File Browser Test", - apptype=FlipperAppType.DEBUG, - entry_point="file_browser_app", - cdefines=["APP_FILE_BROWSER_TEST"], - requires=["gui"], - stack_size=2 * 1024, - order=150, -) diff --git a/applications/desktop/application.fam b/applications/desktop/application.fam deleted file mode 100644 index 5f3dcdfdac9..00000000000 --- a/applications/desktop/application.fam +++ /dev/null @@ -1,30 +0,0 @@ -App( - appid="desktop", - name="DesktopSrv", - apptype=FlipperAppType.SERVICE, - entry_point="desktop_srv", - cdefines=["SRV_DESKTOP"], - requires=[ - "gui", - "dolphin", - "storage", - "input", - ], - provides=["desktop_settings"], - conflicts=["updater"], - stack_size=2 * 1024, - order=60, -) - -App( - appid="desktop_settings", - name="Desktop", - apptype=FlipperAppType.SETTINGS, - entry_point="desktop_settings_app", - requires=[ - "desktop", - "gui", - ], - stack_size=1 * 1024, - order=50, -) diff --git a/applications/desktop/desktop.c b/applications/desktop/desktop.c deleted file mode 100644 index 578066a6a41..00000000000 --- a/applications/desktop/desktop.c +++ /dev/null @@ -1,338 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "animations/animation_manager.h" -#include "desktop/scenes/desktop_scene.h" -#include "desktop/scenes/desktop_scene_i.h" -#include "desktop/views/desktop_view_locked.h" -#include "desktop/views/desktop_view_pin_input.h" -#include "desktop/views/desktop_view_pin_timeout.h" -#include "desktop_i.h" -#include "helpers/pin_lock.h" -#include "helpers/slideshow_filename.h" - -static void desktop_auto_lock_arm(Desktop*); -static void desktop_auto_lock_inhibit(Desktop*); -static void desktop_start_auto_lock_timer(Desktop*); - -static void desktop_loader_callback(const void* message, void* context) { - furi_assert(context); - Desktop* desktop = context; - const LoaderEvent* event = message; - - if(event->type == LoaderEventTypeApplicationStarted) { - view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalBeforeAppStarted); - } else if(event->type == LoaderEventTypeApplicationStopped) { - view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAfterAppFinished); - } -} - -static void desktop_lock_icon_callback(Canvas* canvas, void* context) { - UNUSED(context); - furi_assert(canvas); - canvas_draw_icon(canvas, 0, 0, &I_Lock_8x8); -} - -static bool desktop_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - Desktop* desktop = (Desktop*)context; - - switch(event) { - case DesktopGlobalBeforeAppStarted: - animation_manager_unload_and_stall_animation(desktop->animation_manager); - desktop_auto_lock_inhibit(desktop); - return true; - case DesktopGlobalAfterAppFinished: - animation_manager_load_and_continue_animation(desktop->animation_manager); - // TODO: Implement a message mechanism for loading settings and (optionally) - // locking and unlocking - LOAD_DESKTOP_SETTINGS(&desktop->settings); - desktop_auto_lock_arm(desktop); - return true; - case DesktopGlobalAutoLock: - if(!loader_is_locked(desktop->loader)) { - desktop_lock(desktop); - } - return true; - } - - return scene_manager_handle_custom_event(desktop->scene_manager, event); -} - -static bool desktop_back_event_callback(void* context) { - furi_assert(context); - Desktop* desktop = (Desktop*)context; - return scene_manager_handle_back_event(desktop->scene_manager); -} - -static void desktop_tick_event_callback(void* context) { - furi_assert(context); - Desktop* app = context; - scene_manager_handle_tick_event(app->scene_manager); -} - -static void desktop_input_event_callback(const void* value, void* context) { - furi_assert(value); - furi_assert(context); - const InputEvent* event = value; - Desktop* desktop = context; - if(event->type == InputTypePress) { - desktop_start_auto_lock_timer(desktop); - } -} - -static void desktop_auto_lock_timer_callback(void* context) { - furi_assert(context); - Desktop* desktop = context; - view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAutoLock); -} - -static void desktop_start_auto_lock_timer(Desktop* desktop) { - furi_timer_start( - desktop->auto_lock_timer, furi_ms_to_ticks(desktop->settings.auto_lock_delay_ms)); -} - -static void desktop_stop_auto_lock_timer(Desktop* desktop) { - furi_timer_stop(desktop->auto_lock_timer); -} - -static void desktop_auto_lock_arm(Desktop* desktop) { - if(desktop->settings.auto_lock_delay_ms) { - desktop->input_events_subscription = furi_pubsub_subscribe( - desktop->input_events_pubsub, desktop_input_event_callback, desktop); - desktop_start_auto_lock_timer(desktop); - } -} - -static void desktop_auto_lock_inhibit(Desktop* desktop) { - desktop_stop_auto_lock_timer(desktop); - if(desktop->input_events_subscription) { - furi_pubsub_unsubscribe(desktop->input_events_pubsub, desktop->input_events_subscription); - desktop->input_events_subscription = NULL; - } -} - -void desktop_lock(Desktop* desktop) { - desktop_auto_lock_inhibit(desktop); - scene_manager_set_scene_state( - desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); - scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); - notification_message(desktop->notification, &sequence_display_backlight_off_delay_1000); -} - -void desktop_unlock(Desktop* desktop) { - view_port_enabled_set(desktop->lock_viewport, false); - Gui* gui = furi_record_open(RECORD_GUI); - gui_set_lockdown(gui, false); - furi_record_close(RECORD_GUI); - desktop_view_locked_unlock(desktop->locked_view); - scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain); - desktop_auto_lock_arm(desktop); -} - -Desktop* desktop_alloc() { - Desktop* desktop = malloc(sizeof(Desktop)); - - desktop->animation_manager = animation_manager_alloc(); - desktop->gui = furi_record_open(RECORD_GUI); - desktop->scene_thread = furi_thread_alloc(); - desktop->view_dispatcher = view_dispatcher_alloc(); - desktop->scene_manager = scene_manager_alloc(&desktop_scene_handlers, desktop); - - view_dispatcher_enable_queue(desktop->view_dispatcher); - view_dispatcher_attach_to_gui( - desktop->view_dispatcher, desktop->gui, ViewDispatcherTypeDesktop); - view_dispatcher_set_tick_event_callback( - desktop->view_dispatcher, desktop_tick_event_callback, 500); - - view_dispatcher_set_event_callback_context(desktop->view_dispatcher, desktop); - view_dispatcher_set_custom_event_callback( - desktop->view_dispatcher, desktop_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - desktop->view_dispatcher, desktop_back_event_callback); - - desktop->lock_menu = desktop_lock_menu_alloc(); - desktop->debug_view = desktop_debug_alloc(); - desktop->hw_mismatch_popup = popup_alloc(); - desktop->locked_view = desktop_view_locked_alloc(); - desktop->pin_input_view = desktop_view_pin_input_alloc(); - desktop->pin_timeout_view = desktop_view_pin_timeout_alloc(); - desktop->slideshow_view = desktop_view_slideshow_alloc(); - - desktop->main_view_stack = view_stack_alloc(); - desktop->main_view = desktop_main_alloc(); - View* dolphin_view = animation_manager_get_animation_view(desktop->animation_manager); - view_stack_add_view(desktop->main_view_stack, desktop_main_get_view(desktop->main_view)); - view_stack_add_view(desktop->main_view_stack, dolphin_view); - view_stack_add_view( - desktop->main_view_stack, desktop_view_locked_get_view(desktop->locked_view)); - - /* locked view (as animation view) attends in 2 scenes: main & locked, - * because it has to draw "Unlocked" label on main scene */ - desktop->locked_view_stack = view_stack_alloc(); - view_stack_add_view(desktop->locked_view_stack, dolphin_view); - view_stack_add_view( - desktop->locked_view_stack, desktop_view_locked_get_view(desktop->locked_view)); - - view_dispatcher_add_view( - desktop->view_dispatcher, - DesktopViewIdMain, - view_stack_get_view(desktop->main_view_stack)); - view_dispatcher_add_view( - desktop->view_dispatcher, - DesktopViewIdLocked, - view_stack_get_view(desktop->locked_view_stack)); - view_dispatcher_add_view( - desktop->view_dispatcher, - DesktopViewIdLockMenu, - desktop_lock_menu_get_view(desktop->lock_menu)); - view_dispatcher_add_view( - desktop->view_dispatcher, DesktopViewIdDebug, desktop_debug_get_view(desktop->debug_view)); - view_dispatcher_add_view( - desktop->view_dispatcher, - DesktopViewIdHwMismatch, - popup_get_view(desktop->hw_mismatch_popup)); - view_dispatcher_add_view( - desktop->view_dispatcher, - DesktopViewIdPinTimeout, - desktop_view_pin_timeout_get_view(desktop->pin_timeout_view)); - view_dispatcher_add_view( - desktop->view_dispatcher, - DesktopViewIdPinInput, - desktop_view_pin_input_get_view(desktop->pin_input_view)); - view_dispatcher_add_view( - desktop->view_dispatcher, - DesktopViewIdSlideshow, - desktop_view_slideshow_get_view(desktop->slideshow_view)); - - // Lock icon - desktop->lock_viewport = view_port_alloc(); - view_port_set_width(desktop->lock_viewport, icon_get_width(&I_Lock_8x8)); - view_port_draw_callback_set(desktop->lock_viewport, desktop_lock_icon_callback, desktop); - view_port_enabled_set(desktop->lock_viewport, false); - gui_add_view_port(desktop->gui, desktop->lock_viewport, GuiLayerStatusBarLeft); - - // Special case: autostart application is already running - desktop->loader = furi_record_open(RECORD_LOADER); - if(loader_is_locked(desktop->loader) && - animation_manager_is_animation_loaded(desktop->animation_manager)) { - animation_manager_unload_and_stall_animation(desktop->animation_manager); - } - - desktop->notification = furi_record_open(RECORD_NOTIFICATION); - desktop->app_start_stop_subscription = furi_pubsub_subscribe( - loader_get_pubsub(desktop->loader), desktop_loader_callback, desktop); - - desktop->input_events_pubsub = furi_record_open(RECORD_INPUT_EVENTS); - desktop->input_events_subscription = NULL; - - desktop->auto_lock_timer = - furi_timer_alloc(desktop_auto_lock_timer_callback, FuriTimerTypeOnce, desktop); - - return desktop; -} - -void desktop_free(Desktop* desktop) { - furi_assert(desktop); - - furi_pubsub_unsubscribe( - loader_get_pubsub(desktop->loader), desktop->app_start_stop_subscription); - - if(desktop->input_events_subscription) { - furi_pubsub_unsubscribe(desktop->input_events_pubsub, desktop->input_events_subscription); - desktop->input_events_subscription = NULL; - } - - desktop->loader = NULL; - desktop->input_events_pubsub = NULL; - furi_record_close(RECORD_LOADER); - furi_record_close(RECORD_NOTIFICATION); - furi_record_close(RECORD_INPUT_EVENTS); - - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdMain); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLockMenu); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdLocked); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdDebug); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdHwMismatch); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdPinInput); - view_dispatcher_remove_view(desktop->view_dispatcher, DesktopViewIdPinTimeout); - - view_dispatcher_free(desktop->view_dispatcher); - scene_manager_free(desktop->scene_manager); - - animation_manager_free(desktop->animation_manager); - view_stack_free(desktop->main_view_stack); - desktop_main_free(desktop->main_view); - view_stack_free(desktop->locked_view_stack); - desktop_view_locked_free(desktop->locked_view); - desktop_lock_menu_free(desktop->lock_menu); - desktop_view_locked_free(desktop->locked_view); - desktop_debug_free(desktop->debug_view); - popup_free(desktop->hw_mismatch_popup); - desktop_view_pin_timeout_free(desktop->pin_timeout_view); - - furi_record_close(RECORD_GUI); - desktop->gui = NULL; - - furi_thread_free(desktop->scene_thread); - - furi_record_close("menu"); - - furi_timer_free(desktop->auto_lock_timer); - - free(desktop); -} - -static bool desktop_check_file_flag(const char* flag_path) { - Storage* storage = furi_record_open(RECORD_STORAGE); - bool exists = storage_common_stat(storage, flag_path, NULL) == FSE_OK; - furi_record_close(RECORD_STORAGE); - - return exists; -} - -int32_t desktop_srv(void* p) { - UNUSED(p); - Desktop* desktop = desktop_alloc(); - - bool loaded = LOAD_DESKTOP_SETTINGS(&desktop->settings); - if(!loaded) { - memset(&desktop->settings, 0, sizeof(desktop->settings)); - SAVE_DESKTOP_SETTINGS(&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/desktop/desktop.h b/applications/desktop/desktop.h deleted file mode 100644 index f5608207d0a..00000000000 --- a/applications/desktop/desktop.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -typedef struct Desktop Desktop; diff --git a/applications/desktop/desktop_settings/desktop_settings.h b/applications/desktop/desktop_settings/desktop_settings.h deleted file mode 100644 index 2cf7e987323..00000000000 --- a/applications/desktop/desktop_settings/desktop_settings.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include "desktop_settings_filename.h" - -#include -#include -#include -#include -#include - -#define DESKTOP_SETTINGS_VER (4) - -#define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME) -#define DESKTOP_SETTINGS_MAGIC (0x17) -#define PIN_MAX_LENGTH 12 - -#define DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG "run_pin_setup" - -#define SAVE_DESKTOP_SETTINGS(x) \ - saved_struct_save( \ - DESKTOP_SETTINGS_PATH, \ - (x), \ - sizeof(DesktopSettings), \ - DESKTOP_SETTINGS_MAGIC, \ - DESKTOP_SETTINGS_VER) - -#define LOAD_DESKTOP_SETTINGS(x) \ - saved_struct_load( \ - DESKTOP_SETTINGS_PATH, \ - (x), \ - sizeof(DesktopSettings), \ - DESKTOP_SETTINGS_MAGIC, \ - DESKTOP_SETTINGS_VER) - -#define MAX_PIN_SIZE 10 -#define MIN_PIN_SIZE 4 - -typedef struct { - InputKey data[MAX_PIN_SIZE]; - uint8_t length; -} PinCode; - -typedef struct { - uint16_t favorite_primary; - uint16_t favorite_secondary; - PinCode pin_code; - uint8_t is_locked; - uint32_t auto_lock_delay_ms; - bool is_inverted; -} DesktopSettings; diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_favorite.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_favorite.c deleted file mode 100644 index 0ec18af7ac6..00000000000 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_favorite.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "../desktop_settings_app.h" -#include "applications.h" -#include "desktop_settings_scene.h" - -static void desktop_settings_scene_favorite_submenu_callback(void* context, uint32_t index) { - DesktopSettingsApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, index); -} - -void desktop_settings_scene_favorite_on_enter(void* context) { - DesktopSettingsApp* app = context; - Submenu* submenu = app->submenu; - submenu_reset(submenu); - - for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { - submenu_add_item( - submenu, - FLIPPER_APPS[i].name, - i, - desktop_settings_scene_favorite_submenu_callback, - app); - } - - uint32_t primary_favorite = - scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); - - submenu_set_header( - app->submenu, primary_favorite ? "Primary favorite app:" : "Secondary favorite app:"); - - if(primary_favorite) { - submenu_set_selected_item(app->submenu, app->settings.favorite_primary); - } else { - submenu_set_selected_item(app->submenu, app->settings.favorite_secondary); - } - view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); -} - -bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent event) { - DesktopSettingsApp* app = context; - bool consumed = false; - - uint32_t primary_favorite = - scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); - - if(event.type == SceneManagerEventTypeCustom) { - if(primary_favorite) { - app->settings.favorite_primary = event.event; - } else { - app->settings.favorite_secondary = event.event; - } - scene_manager_previous_scene(app->scene_manager); - consumed = true; - } - return consumed; -} - -void desktop_settings_scene_favorite_on_exit(void* context) { - DesktopSettingsApp* app = context; - SAVE_DESKTOP_SETTINGS(&app->settings); - submenu_reset(app->submenu); -} diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_i.h b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_i.h deleted file mode 100644 index 230fec873da..00000000000 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_i.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#define SCENE_STATE_PIN_AUTH_DISABLE (0) -#define SCENE_STATE_PIN_AUTH_CHANGE_PIN (1) - -#define SCENE_STATE_PIN_ERROR_MISMATCH (0) -#define SCENE_STATE_PIN_ERROR_WRONG (1) diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c deleted file mode 100644 index ae6f0d74daf..00000000000 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c +++ /dev/null @@ -1,130 +0,0 @@ -#include -#include - -#include "../desktop_settings_app.h" -#include "desktop_settings_scene.h" - -#define SCENE_EVENT_SELECT_FAVORITE_PRIMARY 0 -#define SCENE_EVENT_SELECT_FAVORITE_SECONDARY 1 -#define SCENE_EVENT_SELECT_PIN_SETUP 2 -#define SCENE_EVENT_SELECT_AUTO_LOCK_DELAY 3 - -#define INVERTED 1 -#define NOT_INVERTED 0 - -#define AUTO_LOCK_DELAY_COUNT 6 -const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = { - "OFF", - "30s", - "60s", - "2min", - "5min", - "10min", -}; - -const uint32_t auto_lock_delay_value[AUTO_LOCK_DELAY_COUNT] = - {0, 30000, 60000, 120000, 300000, 600000}; - -#define INVERTED_COUNT 2 -const char* const inverted_text[INVERTED_COUNT] = { - "OFF", - "ON", -}; - -const uint32_t inverted_value[INVERTED_COUNT] = {NOT_INVERTED, INVERTED}; - -static void desktop_settings_scene_start_var_list_enter_callback(void* context, uint32_t index) { - DesktopSettingsApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, index); -} - -static void desktop_settings_scene_start_auto_lock_delay_changed(VariableItem* item) { - DesktopSettingsApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, auto_lock_delay_text[index]); - app->settings.auto_lock_delay_ms = auto_lock_delay_value[index]; -} - -static void desktop_settings_scene_start_inverted_changed(VariableItem* item) { - DesktopSettingsApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, inverted_text[index]); - app->settings.is_inverted = inverted_value[index]; -} - -void desktop_settings_scene_start_on_enter(void* context) { - DesktopSettingsApp* app = context; - VariableItemList* variable_item_list = app->variable_item_list; - - 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, "Secondary Favorite App", 1, NULL, NULL); - - variable_item_list_add(variable_item_list, "PIN Setup", 1, NULL, NULL); - - item = variable_item_list_add( - variable_item_list, - "Auto Lock Time", - AUTO_LOCK_DELAY_COUNT, - desktop_settings_scene_start_auto_lock_delay_changed, - app); - - value_index = value_index_uint32( - app->settings.auto_lock_delay_ms, auto_lock_delay_value, AUTO_LOCK_DELAY_COUNT); - 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, - "Invert screen", - INVERTED_COUNT, - desktop_settings_scene_start_inverted_changed, - app); - - value_index = value_index_uint32(app->settings.is_inverted, inverted_value, INVERTED_COUNT); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, inverted_text[value_index]); - - 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); -} - -bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent event) { - DesktopSettingsApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case SCENE_EVENT_SELECT_FAVORITE_PRIMARY: - scene_manager_set_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite, 1); - 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, 0); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); - consumed = true; - break; - case SCENE_EVENT_SELECT_PIN_SETUP: - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinMenu); - consumed = true; - break; - case SCENE_EVENT_SELECT_AUTO_LOCK_DELAY: - consumed = true; - break; - } - } - return consumed; -} - -void desktop_settings_scene_start_on_exit(void* context) { - DesktopSettingsApp* app = context; - variable_item_list_reset(app->variable_item_list); - SAVE_DESKTOP_SETTINGS(&app->settings); -} diff --git a/applications/desktop/helpers/pin_lock.c b/applications/desktop/helpers/pin_lock.c deleted file mode 100644 index 0495b675d74..00000000000 --- a/applications/desktop/helpers/pin_lock.c +++ /dev/null @@ -1,139 +0,0 @@ - -#include -#include -#include -#include -#include -#include - -#include "../helpers/pin_lock.h" -#include "../desktop_i.h" -#include - -static const NotificationSequence sequence_pin_fail = { - &message_display_backlight_on, - - &message_red_255, - &message_vibro_on, - &message_delay_100, - &message_vibro_off, - &message_red_0, - - &message_delay_250, - - &message_red_255, - &message_vibro_on, - &message_delay_100, - &message_vibro_off, - &message_red_0, - NULL, -}; - -static const uint8_t desktop_helpers_fails_timeout[] = { - 0, - 0, - 0, - 0, - 30, - 60, - 90, - 120, - 150, - 180, - /* +60 for every next fail */ -}; - -void desktop_pin_lock_error_notify() { - NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); - notification_message(notification, &sequence_pin_fail); - furi_record_close(RECORD_NOTIFICATION); -} - -uint32_t desktop_pin_lock_get_fail_timeout() { - uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); - uint32_t pin_timeout = 0; - uint32_t max_index = COUNT_OF(desktop_helpers_fails_timeout) - 1; - if(pin_fails <= max_index) { - pin_timeout = desktop_helpers_fails_timeout[pin_fails]; - } else { - pin_timeout = desktop_helpers_fails_timeout[max_index] + (pin_fails - max_index) * 60; - } - - return pin_timeout; -} - -void desktop_pin_lock(DesktopSettings* settings) { - furi_assert(settings); - - furi_hal_rtc_set_pin_fails(0); - furi_hal_rtc_set_flag(FuriHalRtcFlagLock); - Cli* cli = furi_record_open(RECORD_CLI); - cli_session_close(cli); - furi_record_close(RECORD_CLI); - settings->is_locked = 1; - SAVE_DESKTOP_SETTINGS(settings); -} - -void desktop_pin_unlock(DesktopSettings* settings) { - furi_assert(settings); - - furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); - Cli* cli = furi_record_open(RECORD_CLI); - cli_session_open(cli, &cli_vcp); - furi_record_close(RECORD_CLI); - settings->is_locked = 0; - SAVE_DESKTOP_SETTINGS(settings); -} - -void desktop_pin_lock_init(DesktopSettings* settings) { - furi_assert(settings); - - if(settings->pin_code.length > 0) { - if(settings->is_locked == 1) { - furi_hal_rtc_set_flag(FuriHalRtcFlagLock); - } else { - if(desktop_pin_lock_is_locked()) { - settings->is_locked = 1; - SAVE_DESKTOP_SETTINGS(settings); - } - } - } else { - furi_hal_rtc_set_pin_fails(0); - furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); - } - - if(desktop_pin_lock_is_locked()) { - Cli* cli = furi_record_open(RECORD_CLI); - cli_session_close(cli); - furi_record_close(RECORD_CLI); - } -} - -bool desktop_pin_lock_verify(const PinCode* pin_set, const PinCode* pin_entered) { - bool result = false; - if(desktop_pins_are_equal(pin_set, pin_entered)) { - furi_hal_rtc_set_pin_fails(0); - result = true; - } else { - uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); - furi_hal_rtc_set_pin_fails(pin_fails + 1); - result = false; - } - return result; -} - -bool desktop_pin_lock_is_locked() { - return furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock); -} - -bool desktop_pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2) { - furi_assert(pin_code1); - furi_assert(pin_code2); - bool result = false; - - if(pin_code1->length == pin_code2->length) { - result = !memcmp(pin_code1->data, pin_code2->data, pin_code1->length); - } - - return result; -} diff --git a/applications/desktop/helpers/pin_lock.h b/applications/desktop/helpers/pin_lock.h deleted file mode 100644 index 4454cda54f5..00000000000 --- a/applications/desktop/helpers/pin_lock.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include -#include -#include "../desktop.h" -#include "../desktop_settings/desktop_settings.h" - -void desktop_pin_lock_error_notify(); - -uint32_t desktop_pin_lock_get_fail_timeout(); - -void desktop_pin_lock(DesktopSettings* settings); - -void desktop_pin_unlock(DesktopSettings* settings); - -bool desktop_pin_lock_is_locked(); - -void desktop_pin_lock_init(DesktopSettings* settings); - -bool desktop_pin_lock_verify(const PinCode* pin_set, const PinCode* pin_entered); - -bool desktop_pins_are_equal(const PinCode* pin_code1, const PinCode* pin_code2); diff --git a/applications/desktop/scenes/desktop_scene_debug.c b/applications/desktop/scenes/desktop_scene_debug.c deleted file mode 100644 index e79c56e111a..00000000000 --- a/applications/desktop/scenes/desktop_scene_debug.c +++ /dev/null @@ -1,65 +0,0 @@ - -#include -#include - -#include "../desktop_i.h" -#include "../views/desktop_view_debug.h" -#include "desktop_scene.h" - -void desktop_scene_debug_callback(DesktopEvent event, void* context) { - Desktop* desktop = (Desktop*)context; - view_dispatcher_send_custom_event(desktop->view_dispatcher, event); -} - -void desktop_scene_debug_on_enter(void* context) { - Desktop* desktop = (Desktop*)context; - - desktop_debug_get_dolphin_data(desktop->debug_view); - - desktop_debug_set_callback(desktop->debug_view, desktop_scene_debug_callback, desktop); - view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdDebug); -} - -bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) { - Desktop* desktop = (Desktop*)context; - Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case DesktopDebugEventExit: - scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); - dolphin_flush(dolphin); - consumed = true; - break; - - case DesktopDebugEventDeed: - dolphin_deed(dolphin, DolphinDeedTestRight); - desktop_debug_get_dolphin_data(desktop->debug_view); - consumed = true; - break; - - case DesktopDebugEventWrongDeed: - dolphin_deed(dolphin, DolphinDeedTestLeft); - desktop_debug_get_dolphin_data(desktop->debug_view); - consumed = true; - break; - - case DesktopDebugEventSaveState: - dolphin_flush(dolphin); - consumed = true; - break; - - default: - break; - } - } - - furi_record_close(RECORD_DOLPHIN); - return consumed; -} - -void desktop_scene_debug_on_exit(void* context) { - Desktop* desktop = (Desktop*)context; - desktop_debug_reset_screen_idx(desktop->debug_view); -} diff --git a/applications/desktop/scenes/desktop_scene_lock_menu.c b/applications/desktop/scenes/desktop_scene_lock_menu.c deleted file mode 100644 index a86bb184b6c..00000000000 --- a/applications/desktop/scenes/desktop_scene_lock_menu.c +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "../desktop_i.h" -#include "../desktop_settings/desktop_settings.h" -#include "../views/desktop_view_lock_menu.h" -#include "desktop_scene_i.h" -#include "desktop_scene.h" -#include "../helpers/pin_lock.h" - -#define TAG "DesktopSceneLock" - -void desktop_scene_lock_menu_callback(DesktopEvent event, void* context) { - Desktop* desktop = (Desktop*)context; - view_dispatcher_send_custom_event(desktop->view_dispatcher, event); -} - -void desktop_scene_lock_menu_on_enter(void* context) { - Desktop* desktop = (Desktop*)context; - - LOAD_DESKTOP_SETTINGS(&desktop->settings); - scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); - desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop); - desktop_lock_menu_pin_set(desktop->lock_menu, desktop->settings.pin_code.length > 0); - desktop_lock_menu_set_idx(desktop->lock_menu, 0); - - view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdLockMenu); -} - -bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { - Desktop* desktop = (Desktop*)context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeTick) { - bool check_pin_changed = - scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLockMenu); - if(check_pin_changed) { - LOAD_DESKTOP_SETTINGS(&desktop->settings); - if(desktop->settings.pin_code.length > 0) { - desktop_lock_menu_pin_set(desktop->lock_menu, 1); - scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); - } - } - } else if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case DesktopLockMenuEventLock: - scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); - desktop_lock(desktop); - consumed = true; - break; - case DesktopLockMenuEventPinLock: - if(desktop->settings.pin_code.length > 0) { - desktop_pin_lock(&desktop->settings); - desktop_lock(desktop); - } else { - LoaderStatus status = - loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG); - if(status == LoaderStatusOk) { - scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 1); - } else { - FURI_LOG_E(TAG, "Unable to start desktop settings"); - } - } - consumed = true; - break; - case DesktopLockMenuEventExit: - scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); - scene_manager_search_and_switch_to_previous_scene( - desktop->scene_manager, DesktopSceneMain); - consumed = true; - break; - default: - break; - } - } - return consumed; -} - -void desktop_scene_lock_menu_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/desktop/scenes/desktop_scene_main.c b/applications/desktop/scenes/desktop_scene_main.c deleted file mode 100644 index bc4101ffd5b..00000000000 --- a/applications/desktop/scenes/desktop_scene_main.c +++ /dev/null @@ -1,180 +0,0 @@ -#include -#include -#include -#include -#include - -#include "../desktop_i.h" -#include "../views/desktop_events.h" -#include "../views/desktop_view_main.h" -#include "desktop_scene.h" -#include "desktop_scene_i.h" - -#define TAG "DesktopSrv" - -static void desktop_scene_main_new_idle_animation_callback(void* context) { - furi_assert(context); - Desktop* desktop = context; - view_dispatcher_send_custom_event( - desktop->view_dispatcher, DesktopAnimationEventNewIdleAnimation); -} - -static void desktop_scene_main_check_animation_callback(void* context) { - furi_assert(context); - Desktop* desktop = context; - view_dispatcher_send_custom_event( - desktop->view_dispatcher, DesktopAnimationEventCheckAnimation); -} - -static void desktop_scene_main_interact_animation_callback(void* context) { - furi_assert(context); - Desktop* desktop = context; - view_dispatcher_send_custom_event( - desktop->view_dispatcher, DesktopAnimationEventInteractAnimation); -} - -#ifdef APP_ARCHIVE -static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* flipper_app) { - furi_assert(desktop); - furi_assert(flipper_app); - furi_assert(flipper_app->app); - furi_assert(flipper_app->name); - - if(furi_thread_get_state(desktop->scene_thread) != FuriThreadStateStopped) { - FURI_LOG_E("Desktop", "Thread is already running"); - return; - } - - furi_thread_set_name(desktop->scene_thread, flipper_app->name); - furi_thread_set_stack_size(desktop->scene_thread, flipper_app->stack_size); - furi_thread_set_callback(desktop->scene_thread, flipper_app->app); - - furi_thread_start(desktop->scene_thread); -} -#endif - -void desktop_scene_main_callback(DesktopEvent event, void* context) { - Desktop* desktop = (Desktop*)context; - view_dispatcher_send_custom_event(desktop->view_dispatcher, event); -} - -void desktop_scene_main_on_enter(void* context) { - Desktop* desktop = (Desktop*)context; - DesktopMainView* main_view = desktop->main_view; - - animation_manager_set_context(desktop->animation_manager, desktop); - animation_manager_set_new_idle_callback( - desktop->animation_manager, desktop_scene_main_new_idle_animation_callback); - animation_manager_set_check_callback( - desktop->animation_manager, desktop_scene_main_check_animation_callback); - animation_manager_set_interact_callback( - desktop->animation_manager, desktop_scene_main_interact_animation_callback); - - desktop_main_set_callback(main_view, desktop_scene_main_callback, desktop); - - view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdMain); -} - -bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { - Desktop* desktop = (Desktop*)context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case DesktopMainEventOpenMenu: - loader_show_menu(); - consumed = true; - break; - - case DesktopMainEventOpenLockMenu: - scene_manager_next_scene(desktop->scene_manager, DesktopSceneLockMenu); - consumed = true; - break; - - case DesktopMainEventOpenDebug: - scene_manager_next_scene(desktop->scene_manager, DesktopSceneDebug); - consumed = true; - break; - - case DesktopMainEventOpenArchive: -#ifdef APP_ARCHIVE - desktop_switch_to_app(desktop, &FLIPPER_ARCHIVE); -#endif - consumed = true; - break; - - case DesktopMainEventOpenPowerOff: { - LoaderStatus status = loader_start(desktop->loader, "Power", "off"); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } - consumed = true; - break; - } - - case DesktopMainEventOpenFavoritePrimary: - LOAD_DESKTOP_SETTINGS(&desktop->settings); - if(desktop->settings.favorite_primary < FLIPPER_APPS_COUNT) { - LoaderStatus status = loader_start( - desktop->loader, FLIPPER_APPS[desktop->settings.favorite_primary].name, NULL); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } - } else { - FURI_LOG_E(TAG, "Can't find primary favorite application"); - } - consumed = true; - break; - case DesktopMainEventOpenFavoriteSecondary: - LOAD_DESKTOP_SETTINGS(&desktop->settings); - if(desktop->settings.favorite_secondary < FLIPPER_APPS_COUNT) { - LoaderStatus status = loader_start( - desktop->loader, - FLIPPER_APPS[desktop->settings.favorite_secondary].name, - NULL); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } - } else { - FURI_LOG_E(TAG, "Can't find secondary favorite application"); - } - consumed = true; - break; - case DesktopAnimationEventCheckAnimation: - animation_manager_check_blocking_process(desktop->animation_manager); - consumed = true; - break; - case DesktopAnimationEventNewIdleAnimation: - animation_manager_new_idle_process(desktop->animation_manager); - consumed = true; - break; - case DesktopAnimationEventInteractAnimation: - if(!animation_manager_interact_process(desktop->animation_manager)) { - LoaderStatus status = loader_start(desktop->loader, "Passport", NULL); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } - } - consumed = true; - break; - case DesktopLockedEventUpdate: - desktop_view_locked_update(desktop->locked_view); - consumed = true; - break; - - default: - break; - } - } - - return consumed; -} - -void desktop_scene_main_on_exit(void* context) { - Desktop* desktop = (Desktop*)context; - - animation_manager_set_new_idle_callback(desktop->animation_manager, NULL); - animation_manager_set_check_callback(desktop->animation_manager, NULL); - animation_manager_set_interact_callback(desktop->animation_manager, NULL); - animation_manager_set_context(desktop->animation_manager, desktop); -} diff --git a/applications/desktop/views/desktop_events.h b/applications/desktop/views/desktop_events.h deleted file mode 100644 index 5d130be9bdf..00000000000 --- a/applications/desktop/views/desktop_events.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -typedef enum { - DesktopMainEventOpenLockMenu, - DesktopMainEventOpenArchive, - DesktopMainEventOpenFavoritePrimary, - DesktopMainEventOpenFavoriteSecondary, - DesktopMainEventOpenMenu, - DesktopMainEventOpenDebug, - DesktopMainEventOpenPassport, /**< Broken, don't use it */ - DesktopMainEventOpenPowerOff, - - DesktopLockedEventUnlocked, - DesktopLockedEventUpdate, - DesktopLockedEventShowPinInput, - - DesktopPinInputEventResetWrongPinLabel, - DesktopPinInputEventUnlocked, - DesktopPinInputEventUnlockFailed, - DesktopPinInputEventBack, - - DesktopPinTimeoutExit, - - DesktopDebugEventDeed, - DesktopDebugEventWrongDeed, - DesktopDebugEventSaveState, - DesktopDebugEventExit, - - DesktopLockMenuEventLock, - DesktopLockMenuEventPinLock, - DesktopLockMenuEventExit, - - DesktopAnimationEventCheckAnimation, - DesktopAnimationEventNewIdleAnimation, - DesktopAnimationEventInteractAnimation, - - DesktopSlideshowCompleted, - DesktopSlideshowPoweroff, - - // Global events - DesktopGlobalBeforeAppStarted, - DesktopGlobalAfterAppFinished, - DesktopGlobalAutoLock, -} DesktopEvent; diff --git a/applications/desktop/views/desktop_view_debug.c b/applications/desktop/views/desktop_view_debug.c deleted file mode 100644 index c965432f17c..00000000000 --- a/applications/desktop/views/desktop_view_debug.c +++ /dev/null @@ -1,200 +0,0 @@ -#include -#include -#include -#include -#include - -#include "../desktop_i.h" -#include "desktop_view_debug.h" - -void desktop_debug_set_callback( - DesktopDebugView* debug_view, - DesktopDebugViewCallback callback, - void* context) { - furi_assert(debug_view); - furi_assert(callback); - debug_view->callback = callback; - debug_view->context = context; -} - -void desktop_debug_render(Canvas* canvas, void* model) { - canvas_clear(canvas); - DesktopDebugViewModel* m = model; - const Version* ver; - char buffer[64]; - - static const char* headers[] = {"Device Info:", "Dolphin Info:"}; - - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 64, 1 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignTop, headers[m->screen]); - canvas_set_font(canvas, FontSecondary); - - if(m->screen != DesktopViewStatsMeta) { - // Hardware version - const char* my_name = furi_hal_version_get_name_ptr(); - snprintf( - buffer, - sizeof(buffer), - "%d.F%dB%dC%d %s:%s %s", - furi_hal_version_get_hw_version(), - 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_region_get_name(), - my_name ? my_name : "Unknown"); - canvas_draw_str(canvas, 0, 19 + STATUS_BAR_Y_SHIFT, buffer); - - ver = furi_hal_version_get_firmware_version(); - const BleGlueC2Info* c2_ver = NULL; -#ifdef SRV_BT - c2_ver = ble_glue_get_c2_info(); -#endif - if(!ver) { - canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, "No info"); - return; - } - - snprintf( - buffer, - sizeof(buffer), - "%s [%s]", - version_get_version(ver), - version_get_builddate(ver)); - canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, buffer); - - snprintf( - buffer, - sizeof(buffer), - "%s%s [%s] %s", - version_get_dirty_flag(ver) ? "[!] " : "", - version_get_githash(ver), - version_get_gitbranchnum(ver), - 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), version_get_gitbranch(ver)); - canvas_draw_str(canvas, 0, 50 + STATUS_BAR_Y_SHIFT, buffer); - - } else { - Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); - DolphinStats stats = dolphin_stats(dolphin); - furi_record_close(RECORD_DOLPHIN); - - uint32_t current_lvl = stats.level; - uint32_t remaining = dolphin_state_xp_to_levelup(m->icounter); - - canvas_set_font(canvas, FontSecondary); - snprintf(buffer, sizeof(buffer), "Icounter: %ld Butthurt %ld", m->icounter, m->butthurt); - canvas_draw_str(canvas, 5, 19 + STATUS_BAR_Y_SHIFT, buffer); - - snprintf( - buffer, - sizeof(buffer), - "Level: %ld To level up: %ld", - current_lvl, - (remaining == (uint32_t)(-1) ? remaining : 0)); - canvas_draw_str(canvas, 5, 29 + STATUS_BAR_Y_SHIFT, buffer); - - // even if timestamp is uint64_t, it's safe to cast it to uint32_t, because furi_hal_rtc_datetime_to_timestamp only returns uint32_t - snprintf(buffer, sizeof(buffer), "%ld", (uint32_t)m->timestamp); - - canvas_draw_str(canvas, 5, 39 + STATUS_BAR_Y_SHIFT, buffer); - canvas_draw_str(canvas, 0, 49 + STATUS_BAR_Y_SHIFT, "[< >] icounter value [ok] save"); - } -} - -View* desktop_debug_get_view(DesktopDebugView* debug_view) { - furi_assert(debug_view); - return debug_view->view; -} - -bool desktop_debug_input(InputEvent* event, void* context) { - furi_assert(event); - furi_assert(context); - - DesktopDebugView* debug_view = context; - - if(event->type != InputTypeShort && event->type != InputTypeRepeat) { - return false; - } - - DesktopViewStatsScreens current = 0; - with_view_model( - debug_view->view, (DesktopDebugViewModel * model) { - -#ifdef SRV_DOLPHIN_STATE_DEBUG - if((event->key == InputKeyDown) || (event->key == InputKeyUp)) { - model->screen = !model->screen; - } -#endif - current = model->screen; - return true; - }); - - size_t count = (event->type == InputTypeRepeat) ? 10 : 1; - if(current == DesktopViewStatsMeta) { - if(event->key == InputKeyLeft) { - while(count-- > 0) { - debug_view->callback(DesktopDebugEventWrongDeed, debug_view->context); - } - } else if(event->key == InputKeyRight) { - while(count-- > 0) { - debug_view->callback(DesktopDebugEventDeed, debug_view->context); - } - } else if(event->key == InputKeyOk) { - debug_view->callback(DesktopDebugEventSaveState, debug_view->context); - } else { - return false; - } - } - - if(event->key == InputKeyBack) { - debug_view->callback(DesktopDebugEventExit, debug_view->context); - } - - return true; -} - -DesktopDebugView* desktop_debug_alloc() { - DesktopDebugView* debug_view = malloc(sizeof(DesktopDebugView)); - debug_view->view = view_alloc(); - view_allocate_model(debug_view->view, ViewModelTypeLocking, sizeof(DesktopDebugViewModel)); - view_set_context(debug_view->view, debug_view); - view_set_draw_callback(debug_view->view, (ViewDrawCallback)desktop_debug_render); - view_set_input_callback(debug_view->view, desktop_debug_input); - - return debug_view; -} - -void desktop_debug_free(DesktopDebugView* debug_view) { - furi_assert(debug_view); - - view_free(debug_view->view); - free(debug_view); -} - -void desktop_debug_get_dolphin_data(DesktopDebugView* debug_view) { - Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); - DolphinStats stats = dolphin_stats(dolphin); - with_view_model( - debug_view->view, (DesktopDebugViewModel * model) { - model->icounter = stats.icounter; - model->butthurt = stats.butthurt; - model->timestamp = stats.timestamp; - return true; - }); - - furi_record_close(RECORD_DOLPHIN); -} - -void desktop_debug_reset_screen_idx(DesktopDebugView* debug_view) { - with_view_model( - debug_view->view, (DesktopDebugViewModel * model) { - model->screen = 0; - return true; - }); -} diff --git a/applications/desktop/views/desktop_view_debug.h b/applications/desktop/views/desktop_view_debug.h deleted file mode 100644 index f6af16b2e96..00000000000 --- a/applications/desktop/views/desktop_view_debug.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include -#include -#include "desktop_events.h" - -typedef struct DesktopDebugView DesktopDebugView; - -typedef void (*DesktopDebugViewCallback)(DesktopEvent event, void* context); - -// Debug info -typedef enum { - DesktopViewStatsFw, - DesktopViewStatsMeta, - DesktopViewStatsTotalCount, -} DesktopViewStatsScreens; - -struct DesktopDebugView { - View* view; - DesktopDebugViewCallback callback; - void* context; -}; - -typedef struct { - uint32_t icounter; - uint32_t butthurt; - uint64_t timestamp; - DesktopViewStatsScreens screen; -} DesktopDebugViewModel; - -void desktop_debug_set_callback( - DesktopDebugView* debug_view, - DesktopDebugViewCallback callback, - void* context); - -View* desktop_debug_get_view(DesktopDebugView* debug_view); - -DesktopDebugView* desktop_debug_alloc(); -void desktop_debug_free(DesktopDebugView* debug_view); - -void desktop_debug_get_dolphin_data(DesktopDebugView* debug_view); -void desktop_debug_reset_screen_idx(DesktopDebugView* debug_view); diff --git a/applications/desktop/views/desktop_view_lock_menu.c b/applications/desktop/views/desktop_view_lock_menu.c deleted file mode 100644 index 97d3c4898e5..00000000000 --- a/applications/desktop/views/desktop_view_lock_menu.c +++ /dev/null @@ -1,130 +0,0 @@ -#include -#include - -#include "../desktop_i.h" -#include "desktop_view_lock_menu.h" - -#define LOCK_MENU_ITEMS_NB 3 - -void desktop_lock_menu_set_callback( - DesktopLockMenuView* lock_menu, - DesktopLockMenuViewCallback callback, - void* context) { - furi_assert(lock_menu); - furi_assert(callback); - lock_menu->callback = callback; - lock_menu->context = context; -} - -void desktop_lock_menu_pin_set(DesktopLockMenuView* lock_menu, bool pin_is_set) { - with_view_model( - lock_menu->view, (DesktopLockMenuViewModel * model) { - model->pin_set = pin_is_set; - return true; - }); -} - -void desktop_lock_menu_set_idx(DesktopLockMenuView* lock_menu, uint8_t idx) { - furi_assert(idx < LOCK_MENU_ITEMS_NB); - with_view_model( - lock_menu->view, (DesktopLockMenuViewModel * model) { - model->idx = idx; - return true; - }); -} - -static void lock_menu_callback(void* context, uint8_t index) { - furi_assert(context); - DesktopLockMenuView* lock_menu = context; - switch(index) { - case 0: // lock - lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context); - break; - case 1: // lock - lock_menu->callback(DesktopLockMenuEventPinLock, lock_menu->context); - break; - default: // wip message - with_view_model( - lock_menu->view, (DesktopLockMenuViewModel * model) { - model->hint_timeout = HINT_TIMEOUT; - return true; - }); - break; - } -} - -void desktop_lock_menu_render(Canvas* canvas, void* model) { - const char* Lockmenu_Items[LOCK_MENU_ITEMS_NB] = {"Lock", "Lock with PIN", "DUMB mode"}; - - DesktopLockMenuViewModel* m = model; - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - canvas_draw_icon(canvas, -57, 0 + STATUS_BAR_Y_SHIFT, &I_DoorLeft_70x55); - canvas_draw_icon(canvas, 116, 0 + STATUS_BAR_Y_SHIFT, &I_DoorRight_70x55); - canvas_set_font(canvas, FontSecondary); - - for(uint8_t i = 0; i < LOCK_MENU_ITEMS_NB; ++i) { - const char* str = Lockmenu_Items[i]; - - if(i == 1 && !m->pin_set) str = "Set PIN"; - if(m->hint_timeout && m->idx == 2 && m->idx == i) str = "Not Implemented"; - - if(str != NULL) - canvas_draw_str_aligned( - canvas, 64, 9 + (i * 17) + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter, str); - - if(m->idx == i) elements_frame(canvas, 15, 1 + (i * 17) + STATUS_BAR_Y_SHIFT, 98, 15); - } -} - -View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu) { - furi_assert(lock_menu); - return lock_menu->view; -} - -bool desktop_lock_menu_input(InputEvent* event, void* context) { - furi_assert(event); - furi_assert(context); - - DesktopLockMenuView* lock_menu = context; - uint8_t idx; - - if(event->type != InputTypeShort) return false; - with_view_model( - lock_menu->view, (DesktopLockMenuViewModel * model) { - model->hint_timeout = 0; // clear hint timeout - if(event->key == InputKeyUp) { - model->idx = CLAMP(model->idx - 1, LOCK_MENU_ITEMS_NB - 1, 0); - } else if(event->key == InputKeyDown) { - model->idx = CLAMP(model->idx + 1, LOCK_MENU_ITEMS_NB - 1, 0); - } - idx = model->idx; - return true; - }); - - if(event->key == InputKeyBack) { - lock_menu->callback(DesktopLockMenuEventExit, lock_menu->context); - } else if(event->key == InputKeyOk) { - lock_menu_callback(lock_menu, idx); - } - - return true; -} - -DesktopLockMenuView* desktop_lock_menu_alloc() { - DesktopLockMenuView* lock_menu = malloc(sizeof(DesktopLockMenuView)); - lock_menu->view = view_alloc(); - view_allocate_model(lock_menu->view, ViewModelTypeLocking, sizeof(DesktopLockMenuViewModel)); - view_set_context(lock_menu->view, lock_menu); - view_set_draw_callback(lock_menu->view, (ViewDrawCallback)desktop_lock_menu_render); - view_set_input_callback(lock_menu->view, desktop_lock_menu_input); - - return lock_menu; -} - -void desktop_lock_menu_free(DesktopLockMenuView* lock_menu_view) { - furi_assert(lock_menu_view); - - view_free(lock_menu_view->view); - free(lock_menu_view); -} diff --git a/applications/desktop/views/desktop_view_main.c b/applications/desktop/views/desktop_view_main.c deleted file mode 100644 index 2b1d61f41f7..00000000000 --- a/applications/desktop/views/desktop_view_main.c +++ /dev/null @@ -1,104 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "../desktop_i.h" -#include "desktop_view_main.h" - -struct DesktopMainView { - View* view; - DesktopMainViewCallback callback; - void* context; - TimerHandle_t poweroff_timer; -}; - -#define DESKTOP_MAIN_VIEW_POWEROFF_TIMEOUT 5000 - -static void desktop_main_poweroff_timer_callback(TimerHandle_t timer) { - DesktopMainView* main_view = pvTimerGetTimerID(timer); - main_view->callback(DesktopMainEventOpenPowerOff, main_view->context); -} - -void desktop_main_set_callback( - DesktopMainView* main_view, - DesktopMainViewCallback callback, - void* context) { - furi_assert(main_view); - furi_assert(callback); - main_view->callback = callback; - main_view->context = context; -} - -View* desktop_main_get_view(DesktopMainView* main_view) { - furi_assert(main_view); - return main_view->view; -} - -bool desktop_main_input(InputEvent* event, void* context) { - furi_assert(event); - furi_assert(context); - - DesktopMainView* main_view = context; - - if(event->type == InputTypeShort) { - if(event->key == InputKeyOk) { - main_view->callback(DesktopMainEventOpenMenu, main_view->context); - } else if(event->key == InputKeyUp) { - main_view->callback(DesktopMainEventOpenLockMenu, main_view->context); - } else if(event->key == InputKeyDown) { - main_view->callback(DesktopMainEventOpenArchive, main_view->context); - } else if(event->key == InputKeyLeft) { - main_view->callback(DesktopMainEventOpenFavoritePrimary, main_view->context); - } else if(event->key == InputKeyRight) { - main_view->callback(DesktopMainEventOpenPassport, main_view->context); - } - } else if(event->type == InputTypeLong) { - if(event->key == InputKeyDown) { - main_view->callback(DesktopMainEventOpenDebug, main_view->context); - } else if(event->key == InputKeyLeft) { - main_view->callback(DesktopMainEventOpenFavoriteSecondary, main_view->context); - } - } - - if(event->key == InputKeyBack) { - if(event->type == InputTypePress) { - xTimerChangePeriod( - main_view->poweroff_timer, - pdMS_TO_TICKS(DESKTOP_MAIN_VIEW_POWEROFF_TIMEOUT), - portMAX_DELAY); - } else if(event->type == InputTypeRelease) { - xTimerStop(main_view->poweroff_timer, portMAX_DELAY); - } - } - - return true; -} - -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); - - main_view->poweroff_timer = xTimerCreate( - NULL, - pdMS_TO_TICKS(DESKTOP_MAIN_VIEW_POWEROFF_TIMEOUT), - pdFALSE, - main_view, - desktop_main_poweroff_timer_callback); - - return main_view; -} - -void desktop_main_free(DesktopMainView* main_view) { - furi_assert(main_view); - view_free(main_view->view); - furi_timer_free(main_view->poweroff_timer); - free(main_view); -} diff --git a/applications/desktop/views/desktop_view_pin_setup_done.c b/applications/desktop/views/desktop_view_pin_setup_done.c deleted file mode 100644 index 1d82aeaac89..00000000000 --- a/applications/desktop/views/desktop_view_pin_setup_done.c +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../desktop_i.h" -#include "desktop_view_pin_setup_done.h" - -struct DesktopViewPinSetupDone { - View* view; - DesktopViewPinSetupDoneDoneCallback callback; - void* context; -}; - -static void desktop_view_pin_done_draw(Canvas* canvas, void* model) { - furi_assert(canvas); - furi_assert(model); - - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned( - canvas, 64, 0, AlignCenter, AlignTop, "Prepare to use\narrows as\nPIN symbols"); - - canvas_set_font(canvas, FontSecondary); - elements_multiline_text(canvas, 58, 24, "Prepare to use\narrows as\nPIN symbols"); - - canvas_draw_icon(canvas, 16, 18, &I_Pin_attention_dpad_29x29); - elements_button_right(canvas, "Next"); -} - -static bool desktop_view_pin_done_input(InputEvent* event, void* context) { - furi_assert(event); - furi_assert(context); - - DesktopViewPinSetupDone* instance = context; - bool consumed = false; - - if((event->key == InputKeyRight) && (event->type == InputTypeShort)) { - instance->callback(instance->context); - consumed = true; - } - - return consumed; -} - -void desktop_view_pin_done_set_callback( - DesktopViewPinSetupDone* instance, - DesktopViewPinSetupDoneDoneCallback callback, - void* context) { - furi_assert(instance); - furi_assert(callback); - instance->callback = callback; - instance->context = context; -} - -DesktopViewPinSetupDone* desktop_view_pin_done_alloc() { - DesktopViewPinSetupDone* view = malloc(sizeof(DesktopViewPinSetupDone)); - view->view = view_alloc(); - view_allocate_model(view->view, ViewModelTypeLockFree, 1); - view_set_context(view->view, view); - view_set_draw_callback(view->view, desktop_view_pin_done_draw); - view_set_input_callback(view->view, desktop_view_pin_done_input); - - return view; -} - -void desktop_view_pin_done_free(DesktopViewPinSetupDone* instance) { - furi_assert(instance); - - view_free(instance->view); - free(instance); -} - -View* desktop_view_pin_done_get_view(DesktopViewPinSetupDone* instance) { - furi_assert(instance); - return instance->view; -} diff --git a/applications/desktop/views/desktop_view_pin_setup_done.h b/applications/desktop/views/desktop_view_pin_setup_done.h deleted file mode 100644 index b55677dc587..00000000000 --- a/applications/desktop/views/desktop_view_pin_setup_done.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -typedef struct DesktopViewPinSetupDone DesktopViewPinSetupDone; - -typedef void (*DesktopViewPinSetupDoneDoneCallback)(void*); - -void desktop_view_pin_done_set_callback( - DesktopViewPinSetupDone* instance, - DesktopViewPinSetupDoneDoneCallback callback, - void* context); -DesktopViewPinSetupDone* desktop_view_pin_done_alloc(); -void desktop_view_pin_done_free(DesktopViewPinSetupDone* instance); -View* desktop_view_pin_done_get_view(DesktopViewPinSetupDone* instance); diff --git a/applications/dialogs/application.fam b/applications/dialogs/application.fam deleted file mode 100644 index 0a18c79a3ba..00000000000 --- a/applications/dialogs/application.fam +++ /dev/null @@ -1,10 +0,0 @@ -App( - appid="dialogs", - name="DialogsSrv", - apptype=FlipperAppType.SERVICE, - entry_point="dialogs_srv", - cdefines=["SRV_DIALOGS"], - requires=["gui"], - stack_size=1 * 1024, - order=40, -) diff --git a/applications/dialogs/dialogs.c b/applications/dialogs/dialogs.c deleted file mode 100644 index 381da1635f4..00000000000 --- a/applications/dialogs/dialogs.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "dialogs/dialogs_message.h" -#include "dialogs_i.h" -#include "dialogs_api_lock.h" -#include "dialogs_module_file_browser.h" -#include "dialogs_module_message.h" - -static DialogsApp* dialogs_app_alloc() { - DialogsApp* app = malloc(sizeof(DialogsApp)); - app->message_queue = furi_message_queue_alloc(8, sizeof(DialogsAppMessage)); - - return app; -} - -static void dialogs_app_process_message(DialogsApp* app, DialogsAppMessage* message) { - UNUSED(app); - switch(message->command) { - case DialogsAppCommandFileBrowser: - message->return_data->bool_value = - dialogs_app_process_module_file_browser(&message->data->file_browser); - break; - case DialogsAppCommandDialog: - message->return_data->dialog_value = - dialogs_app_process_module_message(&message->data->dialog); - break; - } - API_LOCK_UNLOCK(message->lock); -} - -int32_t dialogs_srv(void* p) { - UNUSED(p); - DialogsApp* app = dialogs_app_alloc(); - furi_record_create(RECORD_DIALOGS, app); - - DialogsAppMessage message; - while(1) { - if(furi_message_queue_get(app->message_queue, &message, FuriWaitForever) == FuriStatusOk) { - dialogs_app_process_message(app, &message); - } - } - - return 0; -} diff --git a/applications/dialogs/dialogs.h b/applications/dialogs/dialogs.h deleted file mode 100644 index 946ab6cf03a..00000000000 --- a/applications/dialogs/dialogs.h +++ /dev/null @@ -1,140 +0,0 @@ -#pragma once -#include -#include -#include "m-string.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/****************** COMMON ******************/ - -#define RECORD_DIALOGS "dialogs" - -typedef struct DialogsApp DialogsApp; - -/****************** FILE BROWSER ******************/ - -/** - * Shows and processes the file browser dialog - * @param context api pointer - * @param result_path selected file path string pointer - * @param path preselected file path string pointer - * @param extension file extension to be offered for selection - * @param skip_assets true - do not show assets folders - * @param icon file icon pointer, NULL for default icon - * @param hide_ext true - hide extensions for files - * @return bool whether a file was selected - */ -bool dialog_file_browser_show( - DialogsApp* context, - string_ptr result_path, - string_ptr path, - const char* extension, - bool skip_assets, - const Icon* icon, - bool hide_ext); - -/****************** MESSAGE ******************/ - -/** - * Message result type - */ -typedef enum { - DialogMessageButtonBack, - DialogMessageButtonLeft, - DialogMessageButtonCenter, - DialogMessageButtonRight, -} DialogMessageButton; - -/** - * Message struct - */ -typedef struct DialogMessage DialogMessage; - -/** - * Allocate and fill message - * @return DialogMessage* - */ -DialogMessage* dialog_message_alloc(); - -/** - * Free message struct - * @param message message pointer - */ -void dialog_message_free(DialogMessage* message); - -/** - * Set message text - * @param message message pointer - * @param text text, can be NULL if you don't want to display the text - * @param x x position - * @param y y position - * @param horizontal horizontal alignment - * @param vertical vertical alignment - */ -void dialog_message_set_text( - DialogMessage* message, - const char* text, - uint8_t x, - uint8_t y, - Align horizontal, - Align vertical); - -/** - * Set message header - * @param message message pointer - * @param text text, can be NULL if you don't want to display the header - * @param x x position - * @param y y position - * @param horizontal horizontal alignment - * @param vertical vertical alignment - */ -void dialog_message_set_header( - DialogMessage* message, - const char* text, - uint8_t x, - uint8_t y, - Align horizontal, - Align vertical); - -/** - * Set message icon - * @param message message pointer - * @param icon icon pointer, can be NULL if you don't want to display the icon - * @param x x position - * @param y y position - */ -void dialog_message_set_icon(DialogMessage* message, const Icon* icon, uint8_t x, uint8_t y); - -/** - * Set message buttons text, button text can be NULL if you don't want to display and process some buttons - * @param message message pointer - * @param left left button text, can be NULL if you don't want to display the left button - * @param center center button text, can be NULL if you don't want to display the center button - * @param right right button text, can be NULL if you don't want to display the right button - */ -void dialog_message_set_buttons( - DialogMessage* message, - const char* left, - const char* center, - const char* right); - -/** - * Show message from filled struct - * @param context api pointer - * @param message message struct pointer to be shown - * @return DialogMessageButton type - */ -DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* message); - -/** - * Show SD error message (with question sign) - * @param context - * @param error_text - */ -void dialog_message_show_storage_error(DialogsApp* context, const char* error_text); - -#ifdef __cplusplus -} -#endif diff --git a/applications/dialogs/dialogs_api.c b/applications/dialogs/dialogs_api.c deleted file mode 100644 index 72aa2a1b852..00000000000 --- a/applications/dialogs/dialogs_api.c +++ /dev/null @@ -1,80 +0,0 @@ -#include "dialogs/dialogs_message.h" -#include "dialogs_i.h" -#include "dialogs_api_lock.h" -#include "m-string.h" - -/****************** File browser ******************/ - -bool dialog_file_browser_show( - DialogsApp* context, - string_ptr result_path, - string_ptr path, - const char* extension, - bool skip_assets, - const Icon* icon, - bool hide_ext) { - FuriApiLock lock = API_LOCK_INIT_LOCKED(); - furi_check(lock != NULL); - - DialogsAppData data = { - .file_browser = { - .extension = extension, - .result_path = result_path, - .file_icon = icon, - .hide_ext = hide_ext, - .skip_assets = skip_assets, - .preselected_filename = path, - - }}; - - DialogsAppReturn return_data; - DialogsAppMessage message = { - .lock = lock, - .command = DialogsAppCommandFileBrowser, - .data = &data, - .return_data = &return_data, - }; - - furi_check( - furi_message_queue_put(context->message_queue, &message, FuriWaitForever) == FuriStatusOk); - API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(lock); - - return return_data.bool_value; -} - -/****************** Message ******************/ - -DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* dialog_message) { - FuriApiLock lock = API_LOCK_INIT_LOCKED(); - furi_check(lock != NULL); - - DialogsAppData data = { - .dialog = { - .message = dialog_message, - }}; - - DialogsAppReturn return_data; - DialogsAppMessage message = { - .lock = lock, - .command = DialogsAppCommandDialog, - .data = &data, - .return_data = &return_data, - }; - - furi_check( - furi_message_queue_put(context->message_queue, &message, FuriWaitForever) == FuriStatusOk); - API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(lock); - - return return_data.dialog_value; -} - -/****************** Storage error ******************/ - -void dialog_message_show_storage_error(DialogsApp* context, const char* error_text) { - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_text(message, error_text, 88, 32, AlignCenter, AlignCenter); - dialog_message_set_icon(message, &I_SDQuestion_35x43, 5, 6); - dialog_message_set_buttons(message, "Back", NULL, NULL); - dialog_message_show(context, message); - dialog_message_free(message); -} diff --git a/applications/dialogs/dialogs_api_lock.h b/applications/dialogs/dialogs_api_lock.h deleted file mode 100644 index a165803f423..00000000000 --- a/applications/dialogs/dialogs_api_lock.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -typedef FuriEventFlag* FuriApiLock; - -#define API_LOCK_EVENT (1U << 0) - -#define API_LOCK_INIT_LOCKED() furi_event_flag_alloc(); - -#define API_LOCK_WAIT_UNTIL_UNLOCK(_lock) \ - furi_event_flag_wait(_lock, API_LOCK_EVENT, FuriFlagWaitAny, FuriWaitForever); - -#define API_LOCK_FREE(_lock) furi_event_flag_free(_lock); - -#define API_LOCK_UNLOCK(_lock) furi_event_flag_set(_lock, API_LOCK_EVENT); - -#define API_LOCK_WAIT_UNTIL_UNLOCK_AND_FREE(_lock) \ - API_LOCK_WAIT_UNTIL_UNLOCK(_lock); \ - API_LOCK_FREE(_lock); diff --git a/applications/dialogs/dialogs_i.h b/applications/dialogs/dialogs_i.h deleted file mode 100644 index 36bee0248b6..00000000000 --- a/applications/dialogs/dialogs_i.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "dialogs.h" -#include "dialogs_message.h" -#include "view_holder.h" - -#ifdef __cplusplus -extern "C" { -#endif -struct DialogsApp { - FuriMessageQueue* message_queue; -}; - -#ifdef __cplusplus -} -#endif diff --git a/applications/dialogs/dialogs_module_file_browser.c b/applications/dialogs/dialogs_module_file_browser.c deleted file mode 100644 index f5355571a13..00000000000 --- a/applications/dialogs/dialogs_module_file_browser.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "dialogs_i.h" -#include "dialogs_api_lock.h" -#include "gui/modules/file_browser.h" - -typedef struct { - FuriApiLock lock; - bool result; -} DialogsAppFileBrowserContext; - -static void dialogs_app_file_browser_back_callback(void* context) { - furi_assert(context); - DialogsAppFileBrowserContext* file_browser_context = context; - file_browser_context->result = false; - API_LOCK_UNLOCK(file_browser_context->lock); -} - -static void dialogs_app_file_browser_callback(void* context) { - furi_assert(context); - DialogsAppFileBrowserContext* file_browser_context = context; - file_browser_context->result = true; - API_LOCK_UNLOCK(file_browser_context->lock); -} - -bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrowser* data) { - bool ret = false; - Gui* gui = furi_record_open(RECORD_GUI); - - DialogsAppFileBrowserContext* file_browser_context = - malloc(sizeof(DialogsAppFileBrowserContext)); - file_browser_context->lock = API_LOCK_INIT_LOCKED(); - - ViewHolder* view_holder = view_holder_alloc(); - view_holder_attach_to_gui(view_holder, gui); - view_holder_set_back_callback( - view_holder, dialogs_app_file_browser_back_callback, file_browser_context); - - FileBrowser* file_browser = file_browser_alloc(data->result_path); - file_browser_set_callback( - file_browser, dialogs_app_file_browser_callback, file_browser_context); - file_browser_configure( - file_browser, data->extension, data->skip_assets, data->file_icon, data->hide_ext); - file_browser_start(file_browser, data->preselected_filename); - - view_holder_set_view(view_holder, file_browser_get_view(file_browser)); - view_holder_start(view_holder); - API_LOCK_WAIT_UNTIL_UNLOCK(file_browser_context->lock); - - ret = file_browser_context->result; - - view_holder_stop(view_holder); - view_holder_free(view_holder); - file_browser_stop(file_browser); - file_browser_free(file_browser); - API_LOCK_FREE(file_browser_context->lock); - free(file_browser_context); - furi_record_close(RECORD_GUI); - - return ret; -} diff --git a/applications/dolphin/application.fam b/applications/dolphin/application.fam deleted file mode 100644 index 1ee0e3cfe4a..00000000000 --- a/applications/dolphin/application.fam +++ /dev/null @@ -1,23 +0,0 @@ -App( - appid="dolphin", - name="DolphinSrv", - apptype=FlipperAppType.SERVICE, - entry_point="dolphin_srv", - cdefines=["SRV_DOLPHIN"], - stack_size=1 * 1024, - order=50, -) - -App( - appid="passport", - name="Passport", - apptype=FlipperAppType.SETTINGS, - entry_point="passport_app", - cdefines=["APP_PASSPORT"], - requires=[ - "gui", - "dolphin", - ], - stack_size=1 * 1024, - order=60, -) diff --git a/applications/dolphin/dolphin.h b/applications/dolphin/dolphin.h deleted file mode 100644 index 41a6a6089ce..00000000000 --- a/applications/dolphin/dolphin.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include -#include "gui/view.h" -#include "helpers/dolphin_deed.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define RECORD_DOLPHIN "dolphin" - -typedef struct Dolphin Dolphin; - -typedef struct { - uint32_t icounter; - uint32_t butthurt; - uint64_t timestamp; - uint8_t level; - bool level_up_is_pending; -} DolphinStats; - -typedef enum { - DolphinPubsubEventUpdate, -} DolphinPubsubEvent; - -#define DOLPHIN_DEED(deed) \ - do { \ - Dolphin* dolphin = (Dolphin*)furi_record_open("dolphin"); \ - dolphin_deed(dolphin, deed); \ - furi_record_close("dolphin"); \ - } while(0) - -/** Deed complete notification. Call it on deed completion. - * See dolphin_deed.h for available deeds. In futures it will become part of assets. - * Thread safe, async - */ -void dolphin_deed(Dolphin* dolphin, DolphinDeed deed); - -/** Retrieve dolphin stats - * Thread safe, blocking - */ -DolphinStats dolphin_stats(Dolphin* dolphin); - -/** Flush dolphin queue and save state - * Thread safe, blocking - */ -void dolphin_flush(Dolphin* dolphin); - -void dolphin_upgrade_level(Dolphin* dolphin); - -FuriPubSub* dolphin_get_pubsub(Dolphin* dolphin); - -#ifdef __cplusplus -} -#endif diff --git a/applications/drivers/application.fam b/applications/drivers/application.fam new file mode 100644 index 00000000000..dc70e630c9f --- /dev/null +++ b/applications/drivers/application.fam @@ -0,0 +1,6 @@ +# Placeholder +App( + appid="drivers", + name="Drivers device", + apptype=FlipperAppType.METAPACKAGE, +) diff --git a/applications/drivers/subghz/application.fam b/applications/drivers/subghz/application.fam new file mode 100644 index 00000000000..2ee114833bb --- /dev/null +++ b/applications/drivers/subghz/application.fam @@ -0,0 +1,9 @@ +App( + appid="radio_device_cc1101_ext", + apptype=FlipperAppType.PLUGIN, + targets=["f7"], + entry_point="subghz_device_cc1101_ext_ep", + requires=["subghz"], + sdk_headers=["cc1101_ext/cc1101_ext_interconnect.h"], + fap_libs=["hwdrivers"], +) diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c new file mode 100644 index 00000000000..c7083162889 --- /dev/null +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c @@ -0,0 +1,797 @@ +#include "cc1101_ext.h" +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#define TAG "SubGhzDeviceCc1101Ext" + +#define SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO &gpio_ext_pb2 + +/* DMA Channels definition */ +#define SUBGHZ_DEVICE_CC1101_EXT_DMA DMA2 +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL LL_DMA_CHANNEL_3 +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_CHANNEL LL_DMA_CHANNEL_4 +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL LL_DMA_CHANNEL_5 +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ FuriHalInterruptIdDma2Ch3 +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF \ + SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF \ + SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_CHANNEL +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF \ + SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL + +/** Low level buffer dimensions and guard times */ +#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL (256) +#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF \ + (SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL / 2) +#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME 999 << 1 + +/** SubGhz state */ +typedef enum { + SubGhzDeviceCC1101ExtStateInit, /**< Init pending */ + SubGhzDeviceCC1101ExtStateIdle, /**< Idle, energy save mode */ + SubGhzDeviceCC1101ExtStateAsyncRx, /**< Async RX started */ + SubGhzDeviceCC1101ExtStateAsyncTx, /**< Async TX started, DMA and timer is on */ + SubGhzDeviceCC1101ExtStateAsyncTxEnd, /**< Async TX complete, cleanup needed */ +} SubGhzDeviceCC1101ExtState; + +/** SubGhz regulation, receive transmission on the current frequency for the + * region */ +typedef enum { + SubGhzDeviceCC1101ExtRegulationOnlyRx, /**only Rx*/ + SubGhzDeviceCC1101ExtRegulationTxRx, /**TxRx*/ +} SubGhzDeviceCC1101ExtRegulation; + +typedef struct { + uint32_t* buffer; + LevelDuration carry_ld; + SubGhzDeviceCC1101ExtCallback callback; + void* callback_context; + uint32_t gpio_tx_buff[2]; + uint32_t debug_gpio_buff[2]; +} SubGhzDeviceCC1101ExtAsyncTx; + +typedef struct { + uint32_t capture_delta_duration; + SubGhzDeviceCC1101ExtCaptureCallback capture_callback; + void* capture_callback_context; +} SubGhzDeviceCC1101ExtAsyncRx; + +typedef struct { + volatile SubGhzDeviceCC1101ExtState state; + volatile SubGhzDeviceCC1101ExtRegulation regulation; + const GpioPin* async_mirror_pin; + FuriHalSpiBusHandle* spi_bus_handle; + const GpioPin* g0_pin; + SubGhzDeviceCC1101ExtAsyncTx async_tx; + SubGhzDeviceCC1101ExtAsyncRx async_rx; +} SubGhzDeviceCC1101Ext; + +static SubGhzDeviceCC1101Ext* subghz_device_cc1101_ext = NULL; + +static bool subghz_device_cc1101_ext_check_init() { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateInit); + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateIdle; + + bool ret = false; + CC1101Status cc1101_status = {0}; + + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(100 * 1000); + do { + // Reset + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init( + subghz_device_cc1101_ext->spi_bus_handle->miso, + GpioModeInput, + GpioPullUp, + GpioSpeedLow); + + cc1101_status = cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle); + if(cc1101_status.CHIP_RDYn != 0) { + //timeout or error + break; + } + cc1101_status = cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); + if(cc1101_status.CHIP_RDYn != 0) { + //timeout or error + break; + } + // Prepare GD0 for power on self test + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeInput, GpioPullUp, GpioSpeedLow); + + // GD0 low + cc1101_status = cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW); + if(cc1101_status.CHIP_RDYn != 0) { + //timeout or error + break; + } + while(furi_hal_gpio_read(subghz_device_cc1101_ext->g0_pin) != false) { + if(furi_hal_cortex_timer_is_expired(timer)) { + //timeout + break; + } + } + if(furi_hal_cortex_timer_is_expired(timer)) { + //timeout + break; + } + + // GD0 high + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeInput, GpioPullDown, GpioSpeedLow); + cc1101_status = cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, + CC1101_IOCFG0, + CC1101IocfgHW | CC1101_IOCFG_INV); + if(cc1101_status.CHIP_RDYn != 0) { + //timeout or error + break; + } + while(furi_hal_gpio_read(subghz_device_cc1101_ext->g0_pin) != true) { + if(furi_hal_cortex_timer_is_expired(timer)) { + //timeout + break; + } + } + if(furi_hal_cortex_timer_is_expired(timer)) { + //timeout + break; + } + + // Reset GD0 to floating state + cc1101_status = cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); + if(cc1101_status.CHIP_RDYn != 0) { + //timeout or error + break; + } + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + // Go to sleep + cc1101_status = cc1101_shutdown(subghz_device_cc1101_ext->spi_bus_handle); + if(cc1101_status.CHIP_RDYn != 0) { + //timeout or error + break; + } + ret = true; + } while(false); + + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + + if(ret) { + FURI_LOG_I(TAG, "Init OK"); + } else { + FURI_LOG_E(TAG, "Init failed"); + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } + return ret; +} + +bool subghz_device_cc1101_ext_alloc() { + furi_assert(subghz_device_cc1101_ext == NULL); + subghz_device_cc1101_ext = malloc(sizeof(SubGhzDeviceCC1101Ext)); + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateInit; + subghz_device_cc1101_ext->regulation = SubGhzDeviceCC1101ExtRegulationTxRx; + subghz_device_cc1101_ext->async_mirror_pin = NULL; + subghz_device_cc1101_ext->spi_bus_handle = &furi_hal_spi_bus_handle_external; + subghz_device_cc1101_ext->g0_pin = SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO; + + subghz_device_cc1101_ext->async_rx.capture_delta_duration = 0; + + furi_hal_spi_bus_handle_init(subghz_device_cc1101_ext->spi_bus_handle); + return subghz_device_cc1101_ext_check_init(); +} + +void subghz_device_cc1101_ext_free() { + furi_assert(subghz_device_cc1101_ext != NULL); + furi_hal_spi_bus_handle_deinit(subghz_device_cc1101_ext->spi_bus_handle); + free(subghz_device_cc1101_ext); + subghz_device_cc1101_ext = NULL; +} + +void subghz_device_cc1101_ext_set_async_mirror_pin(const GpioPin* pin) { + subghz_device_cc1101_ext->async_mirror_pin = pin; +} + +const GpioPin* subghz_device_cc1101_ext_get_data_gpio() { + return subghz_device_cc1101_ext->g0_pin; +} + +bool subghz_device_cc1101_ext_is_connect() { + bool ret = false; + + if(subghz_device_cc1101_ext == NULL) { // not initialized + ret = subghz_device_cc1101_ext_alloc(); + subghz_device_cc1101_ext_free(); + } else { // initialized + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + uint8_t partnumber = cc1101_get_partnumber(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + ret = (partnumber != 0) && (partnumber != 0xFF); + } + + return ret; +} + +void subghz_device_cc1101_ext_sleep() { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateIdle); + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + + cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle); + + cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); + furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + cc1101_shutdown(subghz_device_cc1101_ext->spi_bus_handle); + + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_dump_state() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + printf( + "[subghz_device_cc1101_ext] cc1101 chip %d, version %d\r\n", + cc1101_get_partnumber(subghz_device_cc1101_ext->spi_bus_handle), + cc1101_get_version(subghz_device_cc1101_ext->spi_bus_handle)); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_load_custom_preset(const uint8_t* preset_data) { + //load config + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle); + uint32_t i = 0; + uint8_t pa[8] = {0}; + while(preset_data[i]) { + cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, preset_data[i], preset_data[i + 1]); + i += 2; + } + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + + //load pa table + memcpy(&pa[0], &preset_data[i + 2], 8); + subghz_device_cc1101_ext_load_patable(pa); + + //show debug + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + i = 0; + FURI_LOG_D(TAG, "Loading custom preset"); + while(preset_data[i]) { + FURI_LOG_D(TAG, "Reg[%lu]: %02X=%02X", i, preset_data[i], preset_data[i + 1]); + i += 2; + } + for(uint8_t y = i; y < i + 10; y++) { + FURI_LOG_D(TAG, "PA[%u]: %02X", y, preset_data[y]); + } + } +} + +void subghz_device_cc1101_ext_load_registers(const uint8_t* data) { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle); + uint32_t i = 0; + while(data[i]) { + cc1101_write_reg(subghz_device_cc1101_ext->spi_bus_handle, data[i], data[i + 1]); + i += 2; + } + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_load_patable(const uint8_t data[8]) { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_set_pa_table(subghz_device_cc1101_ext->spi_bus_handle, data); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_write_packet(const uint8_t* data, uint8_t size) { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_flush_tx(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_write_reg(subghz_device_cc1101_ext->spi_bus_handle, CC1101_FIFO, size); + cc1101_write_fifo(subghz_device_cc1101_ext->spi_bus_handle, data, size); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_flush_rx() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_flush_rx(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_flush_tx() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_flush_tx(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +bool subghz_device_cc1101_ext_rx_pipe_not_empty() { + CC1101RxBytes status[1]; + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_read_reg( + subghz_device_cc1101_ext->spi_bus_handle, + (CC1101_STATUS_RXBYTES) | CC1101_BURST, + (uint8_t*)status); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + if(status->NUM_RXBYTES > 0) { + return true; + } else { + return false; + } +} + +bool subghz_device_cc1101_ext_is_rx_data_crc_valid() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + uint8_t data[1]; + cc1101_read_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_STATUS_LQI | CC1101_BURST, data); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + if(((data[0] >> 7) & 0x01)) { + return true; + } else { + return false; + } +} + +void subghz_device_cc1101_ext_read_packet(uint8_t* data, uint8_t* size) { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_read_fifo(subghz_device_cc1101_ext->spi_bus_handle, data, size); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_shutdown() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + // Reset and shutdown + cc1101_shutdown(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_reset() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_idle() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_rx() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_switch_to_rx(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +bool subghz_device_cc1101_ext_tx() { + if(subghz_device_cc1101_ext->regulation != SubGhzDeviceCC1101ExtRegulationTxRx) return false; + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_switch_to_tx(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + return true; +} + +float subghz_device_cc1101_ext_get_rssi() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + int32_t rssi_dec = cc1101_get_rssi(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + + float rssi = rssi_dec; + if(rssi_dec >= 128) { + rssi = ((rssi - 256.0f) / 2.0f) - 74.0f; + } else { + rssi = (rssi / 2.0f) - 74.0f; + } + + return rssi; +} + +uint8_t subghz_device_cc1101_ext_get_lqi() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + uint8_t data[1]; + cc1101_read_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_STATUS_LQI | CC1101_BURST, data); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + return data[0] & 0x7F; +} + +bool subghz_device_cc1101_ext_is_frequency_valid(uint32_t value) { + if(!(value >= 299999755 && value <= 348000335) && + !(value >= 386999938 && value <= 464000000) && + !(value >= 778999847 && value <= 928000000)) { + return false; + } + + return true; +} + +uint32_t subghz_device_cc1101_ext_set_frequency(uint32_t value) { + if(furi_hal_region_is_frequency_allowed(value)) { + subghz_device_cc1101_ext->regulation = SubGhzDeviceCC1101ExtRegulationTxRx; + } else { + subghz_device_cc1101_ext->regulation = SubGhzDeviceCC1101ExtRegulationOnlyRx; + } + + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + uint32_t real_frequency = + cc1101_set_frequency(subghz_device_cc1101_ext->spi_bus_handle, value); + cc1101_calibrate(subghz_device_cc1101_ext->spi_bus_handle); + + while(true) { + CC1101Status status = cc1101_get_status(subghz_device_cc1101_ext->spi_bus_handle); + if(status.STATE == CC1101StateIDLE) break; + } + + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + return real_frequency; +} + +static bool subghz_device_cc1101_ext_start_debug() { + bool ret = false; + if(subghz_device_cc1101_ext->async_mirror_pin != NULL) { + furi_hal_gpio_init( + subghz_device_cc1101_ext->async_mirror_pin, + GpioModeOutputPushPull, + GpioPullNo, + GpioSpeedVeryHigh); + ret = true; + } + return ret; +} + +static bool subghz_device_cc1101_ext_stop_debug() { + bool ret = false; + if(subghz_device_cc1101_ext->async_mirror_pin != NULL) { + furi_hal_gpio_init( + subghz_device_cc1101_ext->async_mirror_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + ret = true; + } + return ret; +} + +static void subghz_device_cc1101_ext_capture_ISR() { + if(!furi_hal_gpio_read(subghz_device_cc1101_ext->g0_pin)) { + if(subghz_device_cc1101_ext->async_rx.capture_callback) { + if(subghz_device_cc1101_ext->async_mirror_pin != NULL) + furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false); + + subghz_device_cc1101_ext->async_rx.capture_callback( + true, + LL_TIM_GetCounter(TIM17) << 1, + (void*)subghz_device_cc1101_ext->async_rx.capture_callback_context); + } + } else { + if(subghz_device_cc1101_ext->async_rx.capture_callback) { + if(subghz_device_cc1101_ext->async_mirror_pin != NULL) + furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, true); + + subghz_device_cc1101_ext->async_rx.capture_callback( + false, + LL_TIM_GetCounter(TIM17) << 1, + (void*)subghz_device_cc1101_ext->async_rx.capture_callback_context); + } + } + LL_TIM_SetCounter(TIM17, 4); //8>>1 +} + +void subghz_device_cc1101_ext_start_async_rx( + SubGhzDeviceCC1101ExtCaptureCallback callback, + void* context) { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateIdle); + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncRx; + + subghz_device_cc1101_ext->async_rx.capture_callback = callback; + subghz_device_cc1101_ext->async_rx.capture_callback_context = context; + + furi_hal_bus_enable(FuriHalBusTIM17); + + // Configure TIM + //Set the timer resolution to 2 us + LL_TIM_SetPrescaler(TIM17, (64 << 1) - 1); + LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetAutoReload(TIM17, 0xFFFF); + LL_TIM_SetClockDivision(TIM17, LL_TIM_CLOCKDIVISION_DIV1); + + // Timer: advanced + LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(TIM17); + LL_TIM_DisableDMAReq_TRIG(TIM17); + LL_TIM_DisableIT_TRIG(TIM17); + + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedVeryHigh); + furi_hal_gpio_remove_int_callback(subghz_device_cc1101_ext->g0_pin); + furi_hal_gpio_add_int_callback( + subghz_device_cc1101_ext->g0_pin, + subghz_device_cc1101_ext_capture_ISR, + subghz_device_cc1101_ext->async_rx.capture_callback); + + // Start timer + LL_TIM_SetCounter(TIM17, 0); + LL_TIM_EnableCounter(TIM17); + + // Start debug + subghz_device_cc1101_ext_start_debug(); + + // Switch to RX + subghz_device_cc1101_ext_rx(); + + //Clear the variable after the end of the session + subghz_device_cc1101_ext->async_rx.capture_delta_duration = 0; +} + +void subghz_device_cc1101_ext_stop_async_rx() { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncRx); + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateIdle; + + // Shutdown radio + subghz_device_cc1101_ext_idle(); + + FURI_CRITICAL_ENTER(); + furi_hal_bus_disable(FuriHalBusTIM17); + + // Stop debug + subghz_device_cc1101_ext_stop_debug(); + + FURI_CRITICAL_EXIT(); + furi_hal_gpio_remove_int_callback(subghz_device_cc1101_ext->g0_pin); + furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +static void subghz_device_cc1101_ext_async_tx_refill(uint32_t* buffer, size_t samples) { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx); + while(samples > 0) { + bool is_odd = samples % 2; + LevelDuration ld; + if(level_duration_is_reset(subghz_device_cc1101_ext->async_tx.carry_ld)) { + ld = subghz_device_cc1101_ext->async_tx.callback( + subghz_device_cc1101_ext->async_tx.callback_context); + } else { + ld = subghz_device_cc1101_ext->async_tx.carry_ld; + subghz_device_cc1101_ext->async_tx.carry_ld = level_duration_reset(); + } + + if(level_duration_is_wait(ld)) { + *buffer = SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME; + buffer++; + samples--; + } else if(level_duration_is_reset(ld)) { + *buffer = 0; + buffer++; + samples--; + LL_DMA_DisableIT_HT(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + LL_DMA_DisableIT_TC(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + LL_TIM_EnableIT_UPDATE(TIM17); + break; + } else { + bool level = level_duration_get_level(ld); + + // Inject guard time if level is incorrect + if(is_odd != level) { + *buffer = SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME; + buffer++; + samples--; + + // Special case: prevent buffer overflow if sample is last + if(samples == 0) { + subghz_device_cc1101_ext->async_tx.carry_ld = ld; + break; + } + } + + uint32_t duration = level_duration_get_duration(ld); + furi_assert(duration > 0); + *buffer = duration >> 1; + buffer++; + samples--; + } + } +} + +static void subghz_device_cc1101_ext_async_tx_dma_isr() { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx); + +#if SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL == LL_DMA_CHANNEL_3 + if(LL_DMA_IsActiveFlag_HT3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) { + LL_DMA_ClearFlag_HT3(SUBGHZ_DEVICE_CC1101_EXT_DMA); + subghz_device_cc1101_ext_async_tx_refill( + subghz_device_cc1101_ext->async_tx.buffer, + SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF); + } + if(LL_DMA_IsActiveFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) { + LL_DMA_ClearFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA); + subghz_device_cc1101_ext_async_tx_refill( + subghz_device_cc1101_ext->async_tx.buffer + + SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF, + SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF); + } +#else +#error Update this code. Would you kindly? +#endif +} + +static void subghz_device_cc1101_ext_async_tx_timer_isr() { + if(LL_TIM_IsActiveFlag_UPDATE(TIM17)) { + if(LL_TIM_GetAutoReload(TIM17) == 0) { + LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); + if(subghz_device_cc1101_ext->async_mirror_pin != NULL) + furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false); + LL_TIM_DisableCounter(TIM17); + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTxEnd; + } + LL_TIM_ClearFlag_UPDATE(TIM17); + } +} + +bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callback, void* context) { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateIdle); + furi_assert(callback); + + //If transmission is prohibited by regional settings + if(subghz_device_cc1101_ext->regulation != SubGhzDeviceCC1101ExtRegulationTxRx) return false; + + subghz_device_cc1101_ext->async_tx.callback = callback; + subghz_device_cc1101_ext->async_tx.callback_context = context; + + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTx; + + subghz_device_cc1101_ext->async_tx.buffer = + malloc(SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL * sizeof(uint32_t)); + + //Signal generation with mem-to-mem DMA + furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + + // Configure DMA update timer + LL_DMA_SetMemoryAddress( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, (uint32_t)subghz_device_cc1101_ext->async_tx.buffer); + LL_DMA_SetPeriphAddress(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, (uint32_t) & (TIM17->ARR)); + LL_DMA_ConfigTransfer( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | + LL_DMA_MODE_NORMAL); + LL_DMA_SetDataLength( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL); + LL_DMA_SetPeriphRequest(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, LL_DMAMUX_REQ_TIM17_UP); + + LL_DMA_EnableIT_TC(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + LL_DMA_EnableIT_HT(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + LL_DMA_EnableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + + furi_hal_interrupt_set_isr( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ, subghz_device_cc1101_ext_async_tx_dma_isr, NULL); + + furi_hal_bus_enable(FuriHalBusTIM17); + + // Configure TIM + // Set the timer resolution to 2 us + LL_TIM_SetPrescaler(TIM17, (64 << 1) - 1); + LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetAutoReload(TIM17, 0xFFFF); + LL_TIM_SetClockDivision(TIM17, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(TIM17); + + furi_hal_interrupt_set_isr( + FuriHalInterruptIdTim1TrgComTim17, subghz_device_cc1101_ext_async_tx_timer_isr, NULL); + + subghz_device_cc1101_ext_async_tx_refill( + subghz_device_cc1101_ext->async_tx.buffer, SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL); + + // Configure tx gpio dma + const GpioPin* gpio = subghz_device_cc1101_ext->g0_pin; + + subghz_device_cc1101_ext->async_tx.gpio_tx_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER; + subghz_device_cc1101_ext->async_tx.gpio_tx_buff[1] = gpio->pin; + + LL_DMA_SetMemoryAddress( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF, + (uint32_t)subghz_device_cc1101_ext->async_tx.gpio_tx_buff); + LL_DMA_SetPeriphAddress(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF, (uint32_t) & (gpio->port->BSRR)); + LL_DMA_ConfigTransfer( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | + LL_DMA_PRIORITY_HIGH); + LL_DMA_SetDataLength(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF, 2); + LL_DMA_SetPeriphRequest(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF, LL_DMAMUX_REQ_TIM17_UP); + LL_DMA_EnableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF); + + // Start debug + if(subghz_device_cc1101_ext_start_debug()) { + gpio = subghz_device_cc1101_ext->async_mirror_pin; + subghz_device_cc1101_ext->async_tx.debug_gpio_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER; + subghz_device_cc1101_ext->async_tx.debug_gpio_buff[1] = gpio->pin; + + LL_DMA_SetMemoryAddress( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF, + (uint32_t)subghz_device_cc1101_ext->async_tx.debug_gpio_buff); + LL_DMA_SetPeriphAddress( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF, (uint32_t) & (gpio->port->BSRR)); + LL_DMA_ConfigTransfer( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | + LL_DMA_PRIORITY_LOW); + LL_DMA_SetDataLength(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF, 2); + LL_DMA_SetPeriphRequest(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF, LL_DMAMUX_REQ_TIM17_UP); + LL_DMA_EnableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF); + } + + // Start counter + LL_TIM_EnableDMAReq_UPDATE(TIM17); + LL_TIM_GenerateEvent_UPDATE(TIM17); + + subghz_device_cc1101_ext_tx(); + + LL_TIM_SetCounter(TIM17, 0); + LL_TIM_EnableCounter(TIM17); + + return true; +} + +bool subghz_device_cc1101_ext_is_async_tx_complete() { + return subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd; +} + +void subghz_device_cc1101_ext_stop_async_tx() { + furi_assert( + subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx || + subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd); + + // Shutdown radio + subghz_device_cc1101_ext_idle(); + + // Deinitialize Timer + FURI_CRITICAL_ENTER(); + furi_hal_bus_disable(FuriHalBusTIM17); + furi_hal_interrupt_set_isr(FuriHalInterruptIdTim1TrgComTim17, NULL, NULL); + + // Deinitialize DMA + LL_DMA_DeInit(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF); + furi_hal_interrupt_set_isr(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ, NULL, NULL); + + // Deinitialize GPIO + furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); + furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + // Stop debug + if(subghz_device_cc1101_ext_stop_debug()) { + LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF); + } + + FURI_CRITICAL_EXIT(); + + free(subghz_device_cc1101_ext->async_tx.buffer); + + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateIdle; +} diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext.h b/applications/drivers/subghz/cc1101_ext/cc1101_ext.h new file mode 100644 index 00000000000..d972fcb6618 --- /dev/null +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext.h @@ -0,0 +1,206 @@ +/** + * @file furi_hal_subghz.h + * SubGhz HAL API + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Mirror RX/TX async modulation signal to specified pin + * + * @warning Configures pin to output mode. Make sure it is not connected + * directly to power or ground. + * + * @param[in] pin pointer to the gpio pin structure or NULL to disable + */ +void subghz_device_cc1101_ext_set_async_mirror_pin(const GpioPin* pin); + +/** Get data GPIO + * + * @return pointer to the gpio pin structure + */ +const GpioPin* subghz_device_cc1101_ext_get_data_gpio(); + +/** Initialize device + * + * @return true if success + */ +bool subghz_device_cc1101_ext_alloc(); + +/** Deinitialize device + */ +void subghz_device_cc1101_ext_free(); + +/** Check and switch to power save mode Used by internal API-HAL + * initialization routine Can be used to reinitialize device to safe state and + * send it to sleep + */ +bool subghz_device_cc1101_ext_is_connect(); + +/** Send device to sleep mode + */ +void subghz_device_cc1101_ext_sleep(); + +/** Dump info to stdout + */ +void subghz_device_cc1101_ext_dump_state(); + +/** Load custom registers from preset + * + * @param preset_data registers to load + */ +void subghz_device_cc1101_ext_load_custom_preset(const uint8_t* preset_data); + +/** Load registers + * + * @param data Registers data + */ +void subghz_device_cc1101_ext_load_registers(const uint8_t* data); + +/** Load PATABLE + * + * @param data 8 uint8_t values + */ +void subghz_device_cc1101_ext_load_patable(const uint8_t data[8]); + +/** Write packet to FIFO + * + * @param data bytes array + * @param size size + */ +void subghz_device_cc1101_ext_write_packet(const uint8_t* data, uint8_t size); + +/** Check if receive pipe is not empty + * + * @return true if not empty + */ +bool subghz_device_cc1101_ext_rx_pipe_not_empty(); + +/** Check if received data crc is valid + * + * @return true if valid + */ +bool subghz_device_cc1101_ext_is_rx_data_crc_valid(); + +/** Read packet from FIFO + * + * @param data pointer + * @param size size + */ +void subghz_device_cc1101_ext_read_packet(uint8_t* data, uint8_t* size); + +/** Flush rx FIFO buffer + */ +void subghz_device_cc1101_ext_flush_rx(); + +/** Flush tx FIFO buffer + */ +void subghz_device_cc1101_ext_flush_tx(); + +/** Shutdown Issue SPWD command + * @warning registers content will be lost + */ +void subghz_device_cc1101_ext_shutdown(); + +/** Reset Issue reset command + * @warning registers content will be lost + */ +void subghz_device_cc1101_ext_reset(); + +/** Switch to Idle + */ +void subghz_device_cc1101_ext_idle(); + +/** Switch to Receive + */ +void subghz_device_cc1101_ext_rx(); + +/** Switch to Transmit + * + * @return true if the transfer is allowed by belonging to the region + */ +bool subghz_device_cc1101_ext_tx(); + +/** Get RSSI value in dBm + * + * @return RSSI value + */ +float subghz_device_cc1101_ext_get_rssi(); + +/** Get LQI + * + * @return LQI value + */ +uint8_t subghz_device_cc1101_ext_get_lqi(); + +/** Check if frequency is in valid range + * + * @param value frequency in Hz + * + * @return true if frequency is valid, otherwise false + */ +bool subghz_device_cc1101_ext_is_frequency_valid(uint32_t value); + +/** Set frequency + * + * @param value frequency in Hz + * + * @return real frequency in Hz + */ +uint32_t subghz_device_cc1101_ext_set_frequency(uint32_t value); + +/* High Level API */ + +/** Signal Timings Capture callback */ +typedef void (*SubGhzDeviceCC1101ExtCaptureCallback)(bool level, uint32_t duration, void* context); + +/** Enable signal timings capture Initializes GPIO and TIM2 for timings capture + * + * @param callback SubGhzDeviceCC1101ExtCaptureCallback + * @param context callback context + */ +void subghz_device_cc1101_ext_start_async_rx( + SubGhzDeviceCC1101ExtCaptureCallback callback, + void* context); + +/** Disable signal timings capture Resets GPIO and TIM2 + */ +void subghz_device_cc1101_ext_stop_async_rx(); + +/** Async TX callback type + * @param context callback context + * @return LevelDuration + */ +typedef LevelDuration (*SubGhzDeviceCC1101ExtCallback)(void* context); + +/** Start async TX Initializes GPIO, TIM2 and DMA1 for signal output + * + * @param callback SubGhzDeviceCC1101ExtCallback + * @param context callback context + * + * @return true if the transfer is allowed by belonging to the region + */ +bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callback, void* context); + +/** Wait for async transmission to complete + * + * @return true if TX complete + */ +bool subghz_device_cc1101_ext_is_async_tx_complete(); + +/** Stop async transmission and cleanup resources Resets GPIO, TIM2, and DMA1 + */ +void subghz_device_cc1101_ext_stop_async_tx(); + +#ifdef __cplusplus +} +#endif diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.c b/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.c new file mode 100644 index 00000000000..1f119315436 --- /dev/null +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.c @@ -0,0 +1,110 @@ +#include "cc1101_ext_interconnect.h" +#include "cc1101_ext.h" +#include + +#define TAG "SubGhzDeviceCc1101Ext" + +static bool subghz_device_cc1101_ext_interconnect_is_frequency_valid(uint32_t frequency) { + bool ret = subghz_device_cc1101_ext_is_frequency_valid(frequency); + if(!ret) { + furi_crash("SubGhz: Incorrect frequency."); + } + return ret; +} + +static uint32_t subghz_device_cc1101_ext_interconnect_set_frequency(uint32_t frequency) { + subghz_device_cc1101_ext_interconnect_is_frequency_valid(frequency); + return subghz_device_cc1101_ext_set_frequency(frequency); +} + +static bool subghz_device_cc1101_ext_interconnect_start_async_tx(void* callback, void* context) { + return subghz_device_cc1101_ext_start_async_tx( + (SubGhzDeviceCC1101ExtCallback)callback, context); +} + +static void subghz_device_cc1101_ext_interconnect_start_async_rx(void* callback, void* context) { + subghz_device_cc1101_ext_start_async_rx( + (SubGhzDeviceCC1101ExtCaptureCallback)callback, context); +} + +static void subghz_device_cc1101_ext_interconnect_load_preset( + FuriHalSubGhzPreset preset, + uint8_t* preset_data) { + switch(preset) { + case FuriHalSubGhzPresetOok650Async: + subghz_device_cc1101_ext_load_custom_preset( + subghz_device_cc1101_preset_ook_650khz_async_regs); + break; + case FuriHalSubGhzPresetOok270Async: + subghz_device_cc1101_ext_load_custom_preset( + subghz_device_cc1101_preset_ook_270khz_async_regs); + break; + case FuriHalSubGhzPreset2FSKDev238Async: + subghz_device_cc1101_ext_load_custom_preset( + subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs); + break; + case FuriHalSubGhzPreset2FSKDev476Async: + subghz_device_cc1101_ext_load_custom_preset( + subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs); + break; + case FuriHalSubGhzPresetMSK99_97KbAsync: + subghz_device_cc1101_ext_load_custom_preset( + subghz_device_cc1101_preset_msk_99_97kb_async_regs); + break; + case FuriHalSubGhzPresetGFSK9_99KbAsync: + subghz_device_cc1101_ext_load_custom_preset( + subghz_device_cc1101_preset_gfsk_9_99kb_async_regs); + break; + + default: + subghz_device_cc1101_ext_load_custom_preset(preset_data); + } +} + +const SubGhzDeviceInterconnect subghz_device_cc1101_ext_interconnect = { + .begin = subghz_device_cc1101_ext_alloc, + .end = subghz_device_cc1101_ext_free, + .is_connect = subghz_device_cc1101_ext_is_connect, + .reset = subghz_device_cc1101_ext_reset, + .sleep = subghz_device_cc1101_ext_sleep, + .idle = subghz_device_cc1101_ext_idle, + .load_preset = subghz_device_cc1101_ext_interconnect_load_preset, + .set_frequency = subghz_device_cc1101_ext_interconnect_set_frequency, + .is_frequency_valid = subghz_device_cc1101_ext_is_frequency_valid, + .set_async_mirror_pin = subghz_device_cc1101_ext_set_async_mirror_pin, + .get_data_gpio = subghz_device_cc1101_ext_get_data_gpio, + + .set_tx = subghz_device_cc1101_ext_tx, + .flush_tx = subghz_device_cc1101_ext_flush_tx, + .start_async_tx = subghz_device_cc1101_ext_interconnect_start_async_tx, + .is_async_complete_tx = subghz_device_cc1101_ext_is_async_tx_complete, + .stop_async_tx = subghz_device_cc1101_ext_stop_async_tx, + + .set_rx = subghz_device_cc1101_ext_rx, + .flush_rx = subghz_device_cc1101_ext_flush_rx, + .start_async_rx = subghz_device_cc1101_ext_interconnect_start_async_rx, + .stop_async_rx = subghz_device_cc1101_ext_stop_async_rx, + + .get_rssi = subghz_device_cc1101_ext_get_rssi, + .get_lqi = subghz_device_cc1101_ext_get_lqi, + + .rx_pipe_not_empty = subghz_device_cc1101_ext_rx_pipe_not_empty, + .is_rx_data_crc_valid = subghz_device_cc1101_ext_is_rx_data_crc_valid, + .read_packet = subghz_device_cc1101_ext_read_packet, + .write_packet = subghz_device_cc1101_ext_write_packet, +}; + +const SubGhzDevice subghz_device_cc1101_ext = { + .name = SUBGHZ_DEVICE_CC1101_EXT_NAME, + .interconnect = &subghz_device_cc1101_ext_interconnect, +}; + +static const FlipperAppPluginDescriptor subghz_device_cc1101_ext_descriptor = { + .appid = SUBGHZ_RADIO_DEVICE_PLUGIN_APP_ID, + .ep_api_version = SUBGHZ_RADIO_DEVICE_PLUGIN_API_VERSION, + .entry_point = &subghz_device_cc1101_ext, +}; + +const FlipperAppPluginDescriptor* subghz_device_cc1101_ext_ep() { + return &subghz_device_cc1101_ext_descriptor; +} \ No newline at end of file diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h b/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h new file mode 100644 index 00000000000..cf1ff3ee07f --- /dev/null +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h @@ -0,0 +1,8 @@ +#pragma once +#include + +#define SUBGHZ_DEVICE_CC1101_EXT_NAME "cc1101_ext" + +typedef struct SubGhzDeviceCC1101Ext SubGhzDeviceCC1101Ext; + +const FlipperAppPluginDescriptor* subghz_device_cc1101_ext_ep(); diff --git a/applications/examples/application.fam b/applications/examples/application.fam new file mode 100644 index 00000000000..347411fac1c --- /dev/null +++ b/applications/examples/application.fam @@ -0,0 +1,6 @@ +# Placeholder +App( + appid="example_apps", + name="Example apps bundle", + apptype=FlipperAppType.METAPACKAGE, +) diff --git a/applications/examples/example_apps_assets/README.md b/applications/examples/example_apps_assets/README.md new file mode 100644 index 00000000000..024c0877be7 --- /dev/null +++ b/applications/examples/example_apps_assets/README.md @@ -0,0 +1,58 @@ +# Apps Assets folder Example + +This example shows how to use the Apps Assets folder to store data that is not part of the application itself, but is required for its operation, and that data is provided with the application. + +## What is the Apps Assets Folder? + +The **Apps Assets** folder is a folder where external applications unpack their assets. + +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 Assets 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 Assets folder will be `/ext/apps_assets/snake_game`. But using raw paths is not recommended, because the path to the Apps Assets folder can change in the future. Use the `/assets` alias instead. + +## How to get the path to the Apps Assets folder? + +You can use `/assets` alias to get the path to the current application data folder. For example, if you want to open a file `database.txt` in the Apps Assets folder, you can use the next path: `/data/database.txt`. But this way is not recommended, because even the `/assets` alias can change in the future. + +We recommend to use the `APP_ASSETS_PATH` macro to get the path to the Apps Assets folder. For example, if you want to open a file `database.txt` in the Apps Assets folder, you can use the next path: `APP_ASSETS_PATH("database.txt")`. + +## What is the difference between the Apps Assets folder and the Apps Data folder? + +The Apps Assets folder is used to store the data provided with the application. For example, if you want to create a game, you can store game levels (content data) in the Apps Assets folder. + +The Apps Data folder is used to store data generated by the application. For example, if you want to create a game, you can save the progress of the game (user-generated data) in the Apps Data folder. + +## How to provide the data with the app? + +To provide data with an application, you need to create a folder inside your application folder (eg "files") and place the data in it. After that, you need to add `fap_file_assets="files"` to your application.fam file. + +For example, if you want to provide game levels with the application, you need to create a "levels" folder inside the "files" folder and put the game levels in it. After that, you need to add `fap_file_assets="files"` to your application.fam file. The final application folder structure will look like this: + +``` +snake_game +├── application.fam +├── snake_game.c +└── files + └── levels + ├── level1.txt + ├── level2.txt + └── level3.txt +``` + +When app is launched, the `files` folder will be unpacked to the Apps Assets folder. The final structure of the Apps Assets folder will look like this: + +``` +/assets +├── .assets.signature +└── levels + ├── level1.txt + ├── level2.txt + └── level3.txt +``` + +## When will the data be unpacked? + +The data is unpacked when the application starts, if the application is launched for the first time, or if the data within the application is updated. + +When an application is compiled, the contents of the "files" folder are hashed and stored within the application itself. When the application starts, this hash is compared to the hash stored in the `.assets.signature` file. If the hashes differ or the `.assets.signature` file does not exist, the application folder is deleted and the new data is unpacked. diff --git a/applications/examples/example_apps_assets/application.fam b/applications/examples/example_apps_assets/application.fam new file mode 100644 index 00000000000..4f324277dba --- /dev/null +++ b/applications/examples/example_apps_assets/application.fam @@ -0,0 +1,10 @@ +App( + appid="example_apps_assets", + name="Example: Apps Assets", + apptype=FlipperAppType.EXTERNAL, + entry_point="example_apps_assets_main", + requires=["gui"], + stack_size=4 * 1024, + fap_category="Examples", + fap_file_assets="files", +) diff --git a/applications/examples/example_apps_assets/example_apps_assets.c b/applications/examples/example_apps_assets/example_apps_assets.c new file mode 100644 index 00000000000..2c2cc8a8747 --- /dev/null +++ b/applications/examples/example_apps_assets/example_apps_assets.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include + +// Define log tag +#define TAG "ExampleAppsAssets" + +static void example_apps_data_print_file_content(Storage* storage, const char* path) { + Stream* stream = file_stream_alloc(storage); + FuriString* line = furi_string_alloc(); + + FURI_LOG_I(TAG, "----------------------------------------"); + FURI_LOG_I(TAG, "File \"%s\" content:", path); + if(file_stream_open(stream, path, FSAM_READ, FSOM_OPEN_EXISTING)) { + while(stream_read_line(stream, line)) { + furi_string_replace_all(line, "\r", ""); + furi_string_replace_all(line, "\n", ""); + FURI_LOG_I(TAG, "%s", furi_string_get_cstr(line)); + } + } else { + FURI_LOG_E(TAG, "Failed to open file"); + } + FURI_LOG_I(TAG, "----------------------------------------"); + + furi_string_free(line); + file_stream_close(stream); + stream_free(stream); +} + +// Application entry point +int32_t example_apps_assets_main(void* p) { + // Mark argument as unused + UNUSED(p); + + // Open storage + Storage* storage = furi_record_open(RECORD_STORAGE); + + example_apps_data_print_file_content(storage, APP_ASSETS_PATH("test_asset.txt")); + example_apps_data_print_file_content(storage, APP_ASSETS_PATH("poems/a jelly-fish.txt")); + example_apps_data_print_file_content(storage, APP_ASSETS_PATH("poems/theme in yellow.txt")); + example_apps_data_print_file_content(storage, APP_ASSETS_PATH("poems/my shadow.txt")); + + // Close storage + furi_record_close(RECORD_STORAGE); + + return 0; +} diff --git a/applications/examples/example_apps_assets/files/poems/a jelly-fish.txt b/applications/examples/example_apps_assets/files/poems/a jelly-fish.txt new file mode 100644 index 00000000000..46a5a4dff23 --- /dev/null +++ b/applications/examples/example_apps_assets/files/poems/a jelly-fish.txt @@ -0,0 +1,24 @@ +A Jelly-Fish by Marianne Moore + +Visible, invisible, +A fluctuating charm, +An amber-colored amethyst +Inhabits it; your arm +Approaches, and +It opens and +It closes; +You have meant +To catch it, +And it shrivels; +You abandon +Your intent— +It opens, and it +Closes and you +Reach for it— +The blue +Surrounding it +Grows cloudy, and +It floats away +From you. + +source: "https://poets.org/anthology/poems-your-poetry-project-public-domain" \ No newline at end of file diff --git a/applications/examples/example_apps_assets/files/poems/my shadow.txt b/applications/examples/example_apps_assets/files/poems/my shadow.txt new file mode 100644 index 00000000000..e113e7df514 --- /dev/null +++ b/applications/examples/example_apps_assets/files/poems/my shadow.txt @@ -0,0 +1,23 @@ +My Shadow by Robert Louis Stevenson + +I have a little shadow that goes in and out with me, +And what can be the use of him is more than I can see. +He is very, very like me from the heels up to the head; +And I see him jump before me, when I jump into my bed. + +The funniest thing about him is the way he likes to grow— +Not at all like proper children, which is always very slow; +For he sometimes shoots up taller like an India-rubber ball, +And he sometimes gets so little that there’s none of him at all. + +He hasn’t got a notion of how children ought to play, +And can only make a fool of me in every sort of way. +He stays so close beside me, he’s a coward you can see; +I’d think shame to stick to nursie as that shadow sticks to me! + +One morning, very early, before the sun was up, +I rose and found the shining dew on every buttercup; +But my lazy little shadow, like an arrant sleepy-head, +Had stayed at home behind me and was fast asleep in bed. + +source: "https://poets.org/anthology/poems-your-poetry-project-public-domain" \ No newline at end of file diff --git a/applications/examples/example_apps_assets/files/poems/theme in yellow.txt b/applications/examples/example_apps_assets/files/poems/theme in yellow.txt new file mode 100644 index 00000000000..f392287bd86 --- /dev/null +++ b/applications/examples/example_apps_assets/files/poems/theme in yellow.txt @@ -0,0 +1,19 @@ +Theme in Yellow by Carl Sandburg + +I spot the hills +With yellow balls in autumn. +I light the prairie cornfields +Orange and tawny gold clusters +And I am called pumpkins. +On the last of October +When dusk is fallen +Children join hands +And circle round me +Singing ghost songs +And love to the harvest moon; +I am a jack-o'-lantern +With terrible teeth +And the children know +I am fooling. + +source: "https://poets.org/anthology/poems-your-poetry-project-public-domain" \ No newline at end of file diff --git a/applications/examples/example_apps_assets/files/test_asset.txt b/applications/examples/example_apps_assets/files/test_asset.txt new file mode 100644 index 00000000000..1adcb55ee5e --- /dev/null +++ b/applications/examples/example_apps_assets/files/test_asset.txt @@ -0,0 +1 @@ +## This is test file content \ No newline at end of file diff --git a/applications/examples/example_apps_data/README.md b/applications/examples/example_apps_data/README.md new file mode 100644 index 00000000000..0e51daf18cd --- /dev/null +++ b/applications/examples/example_apps_data/README.md @@ -0,0 +1,24 @@ +# 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 `/data` alias instead. + +## How to get the path to the Apps Data folder? + +You can use `/data` 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: `/data/config.txt`. But this way is not recommended, because even the `/data` 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")`. + +## What is the difference between the Apps Assets folder and the Apps Data folder? + +The Apps Assets folder is used to store the data provided with the application. For example, if you want to create a game, you can store game levels (content data) in the Apps Assets folder. + +The Apps Data folder is used to store data generated by the application. For example, if you want to create a game, you can save the progress of the game (user-generated data) in the Apps Data folder. diff --git a/applications/examples/example_apps_data/application.fam b/applications/examples/example_apps_data/application.fam new file mode 100644 index 00000000000..f44dca97d9a --- /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 00000000000..7a297b01cfd --- /dev/null +++ b/applications/examples/example_apps_data/example_apps_data.c @@ -0,0 +1,40 @@ +#include +#include + +// Define log tag +#define TAG "ExampleAppsData" + +// 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/examples/example_images/ReadMe.md b/applications/examples/example_images/ReadMe.md new file mode 100644 index 00000000000..d884a0a975a --- /dev/null +++ b/applications/examples/example_images/ReadMe.md @@ -0,0 +1,24 @@ +# Application icons +To use icons, do the following: +* add a line to the application manifest: `fap_icon_assets="folder"`, where `folder` points to the folder where your icons are located +* add `#include "application_id_icons.h"` to the application code, where `application_id` is the appid from the manifest +* every icon in the folder will be available as a `I_icon_name` variable, where `icon_name` is the name of the icon file without the extension + +## Example +We have an application with the following manifest: +``` +App( + appid="example_images", + ... + fap_icon_assets="images", +) +``` + +So the icons are in the `images` folder and will be available in the generated `example_images_icons.h` file. + +The example code is located in `example_images_main.c` and contains the following line: +``` +#include "example_images_icons.h" +``` + +Image `dolphin_71x25.png` is available as `I_dolphin_71x25`. diff --git a/applications/examples/example_images/application.fam b/applications/examples/example_images/application.fam new file mode 100644 index 00000000000..9a5f8e03048 --- /dev/null +++ b/applications/examples/example_images/application.fam @@ -0,0 +1,10 @@ +App( + appid="example_images", + name="Example: Images", + apptype=FlipperAppType.EXTERNAL, + entry_point="example_images_main", + requires=["gui"], + stack_size=1 * 1024, + fap_category="Examples", + fap_icon_assets="images", +) diff --git a/applications/examples/example_images/example_images.c b/applications/examples/example_images/example_images.c new file mode 100644 index 00000000000..b00818cd668 --- /dev/null +++ b/applications/examples/example_images/example_images.c @@ -0,0 +1,81 @@ +#include +#include + +#include +#include + +/* Magic happens here -- this file is generated by fbt. + * Just set fap_icon_assets in application.fam and #include {APPID}_icons.h */ +#include "example_images_icons.h" + +typedef struct { + uint8_t x, y; +} ImagePosition; + +static ImagePosition image_position = {.x = 0, .y = 0}; + +// Screen is 128x64 px +static void app_draw_callback(Canvas* canvas, void* ctx) { + UNUSED(ctx); + + canvas_clear(canvas); + canvas_draw_icon(canvas, image_position.x % 128, image_position.y % 64, &I_dolphin_71x25); +} + +static void app_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +int32_t example_images_main(void* p) { + UNUSED(p); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + // Configure view port + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, app_draw_callback, view_port); + view_port_input_callback_set(view_port, app_input_callback, event_queue); + + // Register view port in GUI + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + InputEvent event; + + bool running = true; + while(running) { + if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) { + if((event.type == InputTypePress) || (event.type == InputTypeRepeat)) { + switch(event.key) { + case InputKeyLeft: + image_position.x -= 2; + break; + case InputKeyRight: + image_position.x += 2; + break; + case InputKeyUp: + image_position.y -= 2; + break; + case InputKeyDown: + image_position.y += 2; + break; + default: + running = false; + break; + } + } + } + view_port_update(view_port); + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_message_queue_free(event_queue); + + furi_record_close(RECORD_GUI); + + return 0; +} diff --git a/applications/examples/example_images/images/dolphin_71x25.png b/applications/examples/example_images/images/dolphin_71x25.png new file mode 100644 index 00000000000..6b3f8aa59b9 Binary files /dev/null and b/applications/examples/example_images/images/dolphin_71x25.png differ diff --git a/applications/examples/example_plugins/application.fam b/applications/examples/example_plugins/application.fam new file mode 100644 index 00000000000..d9a36da564b --- /dev/null +++ b/applications/examples/example_plugins/application.fam @@ -0,0 +1,34 @@ +App( + appid="example_plugins", + name="Example: App w/plugin", + apptype=FlipperAppType.EXTERNAL, + entry_point="example_plugins_app", + stack_size=2 * 1024, + fap_category="Examples", + sources=["*.c", "!plugin*.c"], +) + +App( + appid="example_plugins_multi", + name="Example: App w/plugins", + apptype=FlipperAppType.EXTERNAL, + entry_point="example_plugins_multi_app", + stack_size=2 * 1024, + fap_category="Examples", +) + +App( + appid="example_plugin1", + apptype=FlipperAppType.PLUGIN, + entry_point="example_plugin1_ep", + requires=["example_plugins", "example_plugins_multi"], + sources=["plugin1.c"], +) + +App( + appid="example_plugin2", + apptype=FlipperAppType.PLUGIN, + entry_point="example_plugin2_ep", + requires=["example_plugins_multi"], + sources=["plugin2.c"], +) diff --git a/applications/examples/example_plugins/example_plugins.c b/applications/examples/example_plugins/example_plugins.c new file mode 100644 index 00000000000..7e71e0d2eb5 --- /dev/null +++ b/applications/examples/example_plugins/example_plugins.c @@ -0,0 +1,70 @@ +/* + * An example of a plugin host application. + * Loads a single plugin and calls its methods. + */ + +#include "plugin_interface.h" + +#include + +#include +#include +#include + +#define TAG "ExamplePlugins" + +int32_t example_plugins_app(void* p) { + UNUSED(p); + + FURI_LOG_I(TAG, "Starting"); + + Storage* storage = furi_record_open(RECORD_STORAGE); + + FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface); + + do { + FlipperApplicationPreloadStatus preload_res = + flipper_application_preload(app, APP_DATA_PATH("plugins/example_plugin1.fal")); + + if(preload_res != FlipperApplicationPreloadStatusSuccess) { + FURI_LOG_E(TAG, "Failed to preload plugin"); + break; + } + + if(!flipper_application_is_plugin(app)) { + FURI_LOG_E(TAG, "Plugin file is not a library"); + break; + } + + FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(app); + if(load_status != FlipperApplicationLoadStatusSuccess) { + FURI_LOG_E(TAG, "Failed to load plugin file"); + break; + } + + const FlipperAppPluginDescriptor* app_descriptor = + flipper_application_plugin_get_descriptor(app); + + FURI_LOG_I( + TAG, + "Loaded plugin for appid '%s', API %lu", + app_descriptor->appid, + app_descriptor->ep_api_version); + + furi_check(app_descriptor->ep_api_version == PLUGIN_API_VERSION); + furi_check(strcmp(app_descriptor->appid, PLUGIN_APP_ID) == 0); + + const ExamplePlugin* plugin = app_descriptor->entry_point; + + FURI_LOG_I(TAG, "Plugin name: %s", plugin->name); + FURI_LOG_I(TAG, "Plugin method1: %d", plugin->method1()); + FURI_LOG_I(TAG, "Plugin method2(7,8): %d", plugin->method2(7, 8)); + FURI_LOG_I(TAG, "Plugin method2(1337,228): %d", plugin->method2(1337, 228)); + } while(false); + flipper_application_free(app); + + furi_record_close(RECORD_STORAGE); + FURI_LOG_I(TAG, "Goodbye!"); + + return 0; +} diff --git a/applications/examples/example_plugins/example_plugins_multi.c b/applications/examples/example_plugins/example_plugins_multi.c new file mode 100644 index 00000000000..3525b39ea4a --- /dev/null +++ b/applications/examples/example_plugins/example_plugins_multi.c @@ -0,0 +1,43 @@ +/* + * An example of an advanced plugin host application. + * It uses PluginManager to load all plugins from a directory + */ + +#include "plugin_interface.h" + +#include +#include +#include + +#include + +#define TAG "ExamplePlugins" + +int32_t example_plugins_multi_app(void* p) { + UNUSED(p); + + FURI_LOG_I(TAG, "Starting"); + + PluginManager* manager = + plugin_manager_alloc(PLUGIN_APP_ID, PLUGIN_API_VERSION, firmware_api_interface); + + if(plugin_manager_load_all(manager, APP_DATA_PATH("plugins")) != PluginManagerErrorNone) { + FURI_LOG_E(TAG, "Failed to load all libs"); + return 0; + } + + uint32_t plugin_count = plugin_manager_get_count(manager); + FURI_LOG_I(TAG, "Loaded %lu plugin(s)", plugin_count); + + for(uint32_t i = 0; i < plugin_count; i++) { + const ExamplePlugin* plugin = plugin_manager_get_ep(manager, i); + FURI_LOG_I(TAG, "plugin name: %s", plugin->name); + FURI_LOG_I(TAG, "plugin method1: %d", plugin->method1()); + FURI_LOG_I(TAG, "plugin method2(7,8): %d", plugin->method2(7, 8)); + } + + plugin_manager_free(manager); + FURI_LOG_I(TAG, "Goodbye!"); + + return 0; +} diff --git a/applications/examples/example_plugins/plugin1.c b/applications/examples/example_plugins/plugin1.c new file mode 100644 index 00000000000..15621935339 --- /dev/null +++ b/applications/examples/example_plugins/plugin1.c @@ -0,0 +1,32 @@ +/* A simple plugin implementing example_plugins application's plugin interface */ + +#include "plugin_interface.h" + +#include + +static int example_plugin1_method1() { + return 42; +} + +static int example_plugin1_method2(int arg1, int arg2) { + return arg1 + arg2; +} + +/* Actual implementation of app<>plugin interface */ +static const ExamplePlugin example_plugin1 = { + .name = "Demo App Plugin 1", + .method1 = &example_plugin1_method1, + .method2 = &example_plugin1_method2, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor example_plugin1_descriptor = { + .appid = PLUGIN_APP_ID, + .ep_api_version = PLUGIN_API_VERSION, + .entry_point = &example_plugin1, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* example_plugin1_ep() { + return &example_plugin1_descriptor; +} diff --git a/applications/examples/example_plugins/plugin2.c b/applications/examples/example_plugins/plugin2.c new file mode 100644 index 00000000000..0b774dad21a --- /dev/null +++ b/applications/examples/example_plugins/plugin2.c @@ -0,0 +1,32 @@ +/* Second plugin implementing example_plugins application's plugin interface */ + +#include "plugin_interface.h" + +#include + +static int example_plugin2_method1() { + return 1337; +} + +static int example_plugin2_method2(int arg1, int arg2) { + return arg1 - arg2; +} + +/* Actual implementation of app<>plugin interface */ +static const ExamplePlugin example_plugin2 = { + .name = "Demo App Plugin 2", + .method1 = &example_plugin2_method1, + .method2 = &example_plugin2_method2, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor example_plugin2_descriptor = { + .appid = PLUGIN_APP_ID, + .ep_api_version = PLUGIN_API_VERSION, + .entry_point = &example_plugin2, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* example_plugin2_ep() { + return &example_plugin2_descriptor; +} diff --git a/applications/examples/example_plugins/plugin_interface.h b/applications/examples/example_plugins/plugin_interface.h new file mode 100644 index 00000000000..25d95d29436 --- /dev/null +++ b/applications/examples/example_plugins/plugin_interface.h @@ -0,0 +1,12 @@ +#pragma once + +/* Common interface between a plugin and host application */ + +#define PLUGIN_APP_ID "example_plugins" +#define PLUGIN_API_VERSION 1 + +typedef struct { + const char* name; + int (*method1)(); + int (*method2)(int, int); +} ExamplePlugin; diff --git a/applications/examples/example_plugins_advanced/app_api.c b/applications/examples/example_plugins_advanced/app_api.c new file mode 100644 index 00000000000..42b3a1860dd --- /dev/null +++ b/applications/examples/example_plugins_advanced/app_api.c @@ -0,0 +1,25 @@ +#include "app_api.h" + +/* Actual implementation of app's API and its private state */ + +static uint32_t accumulator = 0; + +void app_api_accumulator_set(uint32_t value) { + accumulator = value; +} + +uint32_t app_api_accumulator_get() { + return accumulator; +} + +void app_api_accumulator_add(uint32_t value) { + accumulator += value; +} + +void app_api_accumulator_sub(uint32_t value) { + accumulator -= value; +} + +void app_api_accumulator_mul(uint32_t value) { + accumulator *= value; +} diff --git a/applications/examples/example_plugins_advanced/app_api.h b/applications/examples/example_plugins_advanced/app_api.h new file mode 100644 index 00000000000..7035b79f52b --- /dev/null +++ b/applications/examples/example_plugins_advanced/app_api.h @@ -0,0 +1,25 @@ +#pragma once + +/* + * This file contains an API that is internally implemented by the application + * It is also exposed to plugins to allow them to use the application's API. + */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void app_api_accumulator_set(uint32_t value); + +uint32_t app_api_accumulator_get(); + +void app_api_accumulator_add(uint32_t value); + +void app_api_accumulator_sub(uint32_t value); + +void app_api_accumulator_mul(uint32_t value); + +#ifdef __cplusplus +} +#endif diff --git a/applications/examples/example_plugins_advanced/app_api_interface.h b/applications/examples/example_plugins_advanced/app_api_interface.h new file mode 100644 index 00000000000..d0db44c4aa0 --- /dev/null +++ b/applications/examples/example_plugins_advanced/app_api_interface.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +/* + * Resolver interface with private application's symbols. + * Implementation is contained in app_api_table.c + */ +extern const ElfApiInterface* const application_api_interface; \ No newline at end of file diff --git a/applications/examples/example_plugins_advanced/app_api_table.cpp b/applications/examples/example_plugins_advanced/app_api_table.cpp new file mode 100644 index 00000000000..aacfb8c181d --- /dev/null +++ b/applications/examples/example_plugins_advanced/app_api_table.cpp @@ -0,0 +1,27 @@ +#include +#include + +/* + * This file contains an implementation of a symbol table + * with private app's symbols. It is used by composite API resolver + * to load plugins that use internal application's APIs. + */ +#include "app_api_table_i.h" + +static_assert(!has_hash_collisions(app_api_table), "Detected API method hash collision!"); + +constexpr HashtableApiInterface applicaton_hashtable_api_interface{ + { + .api_version_major = 0, + .api_version_minor = 0, + /* generic resolver using pre-sorted array */ + .resolver_callback = &elf_resolve_from_hashtable, + }, + /* pointers to application's API table boundaries */ + .table_cbegin = app_api_table.cbegin(), + .table_cend = app_api_table.cend(), +}; + +/* Casting to generic resolver to use in Composite API resolver */ +extern "C" const ElfApiInterface* const application_api_interface = + &applicaton_hashtable_api_interface; diff --git a/applications/examples/example_plugins_advanced/app_api_table_i.h b/applications/examples/example_plugins_advanced/app_api_table_i.h new file mode 100644 index 00000000000..17cc8be5f93 --- /dev/null +++ b/applications/examples/example_plugins_advanced/app_api_table_i.h @@ -0,0 +1,13 @@ +#include "app_api.h" + +/* + * A list of app's private functions and objects to expose for plugins. + * It is used to generate a table of symbols for import resolver to use. + * TBD: automatically generate this table from app's header files + */ +static constexpr auto app_api_table = sort(create_array_t( + API_METHOD(app_api_accumulator_set, void, (uint32_t)), + API_METHOD(app_api_accumulator_get, uint32_t, ()), + API_METHOD(app_api_accumulator_add, void, (uint32_t)), + API_METHOD(app_api_accumulator_sub, void, (uint32_t)), + API_METHOD(app_api_accumulator_mul, void, (uint32_t)))); \ No newline at end of file diff --git a/applications/examples/example_plugins_advanced/application.fam b/applications/examples/example_plugins_advanced/application.fam new file mode 100644 index 00000000000..10fdc042ff2 --- /dev/null +++ b/applications/examples/example_plugins_advanced/application.fam @@ -0,0 +1,27 @@ +App( + appid="example_advanced_plugins", + name="Example: advanced plugins", + apptype=FlipperAppType.EXTERNAL, + entry_point="example_advanced_plugins_app", + stack_size=2 * 1024, + fap_category="Examples", + sources=["*.c*", "!plugin*.c"], +) + +App( + appid="advanced_plugin1", + apptype=FlipperAppType.PLUGIN, + entry_point="advanced_plugin1_ep", + requires=["example_advanced_plugins"], + sources=["plugin1.c"], + fal_embedded=True, +) + +App( + appid="advanced_plugin2", + apptype=FlipperAppType.PLUGIN, + entry_point="advanced_plugin2_ep", + requires=["example_advanced_plugins"], + sources=["plugin2.c"], + fal_embedded=True, +) diff --git a/applications/examples/example_plugins_advanced/example_advanced_plugins.c b/applications/examples/example_plugins_advanced/example_advanced_plugins.c new file mode 100644 index 00000000000..77ab8205103 --- /dev/null +++ b/applications/examples/example_plugins_advanced/example_advanced_plugins.c @@ -0,0 +1,51 @@ +#include "app_api.h" +#include "plugin_interface.h" +#include "app_api_interface.h" + +#include +#include +#include + +#include + +#define TAG "ExampleAdvancedPlugins" + +int32_t example_advanced_plugins_app(void* p) { + UNUSED(p); + + FURI_LOG_I(TAG, "Starting"); + + CompositeApiResolver* resolver = composite_api_resolver_alloc(); + composite_api_resolver_add(resolver, firmware_api_interface); + composite_api_resolver_add(resolver, application_api_interface); + + PluginManager* manager = plugin_manager_alloc( + PLUGIN_APP_ID, PLUGIN_API_VERSION, composite_api_resolver_get(resolver)); + + do { + // For built-in .fals (fal_embedded==True), use APP_ASSETS_PATH + // Otherwise, use APP_DATA_PATH + if(plugin_manager_load_all(manager, APP_ASSETS_PATH("plugins")) != + PluginManagerErrorNone) { + FURI_LOG_E(TAG, "Failed to load all libs"); + break; + } + + uint32_t plugin_count = plugin_manager_get_count(manager); + FURI_LOG_I(TAG, "Loaded libs: %lu", plugin_count); + + for(uint32_t i = 0; i < plugin_count; i++) { + const AdvancedPlugin* plugin = plugin_manager_get_ep(manager, i); + FURI_LOG_I(TAG, "plugin name: %s. Calling methods", plugin->name); + plugin->method1(228); + plugin->method2(); + FURI_LOG_I(TAG, "Accumulator: %lu", app_api_accumulator_get()); + } + } while(0); + + plugin_manager_free(manager); + composite_api_resolver_free(resolver); + FURI_LOG_I(TAG, "Goodbye!"); + + return 0; +} diff --git a/applications/examples/example_plugins_advanced/plugin1.c b/applications/examples/example_plugins_advanced/plugin1.c new file mode 100644 index 00000000000..bf0ab50b42e --- /dev/null +++ b/applications/examples/example_plugins_advanced/plugin1.c @@ -0,0 +1,40 @@ +/* + * This plugin uses both firmware's API interface and private application headers. + * It can be loaded by a plugin manager that uses CompoundApiInterface, + * which combines both interfaces. + */ + +#include "app_api.h" +#include "plugin_interface.h" + +#include +#include + +static void advanced_plugin1_method1(int arg1) { + /* This function is implemented inside host application */ + app_api_accumulator_add(arg1); +} + +static void advanced_plugin1_method2() { + /* Accumulator value is stored inside host application */ + FURI_LOG_I("TEST", "Plugin 1, accumulator: %lu", app_api_accumulator_get()); +} + +/* Actual implementation of app<>plugin interface */ +static const AdvancedPlugin advanced_plugin1 = { + .name = "Advanced Plugin 1", + .method1 = &advanced_plugin1_method1, + .method2 = &advanced_plugin1_method2, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor advanced_plugin1_descriptor = { + .appid = PLUGIN_APP_ID, + .ep_api_version = PLUGIN_API_VERSION, + .entry_point = &advanced_plugin1, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* advanced_plugin1_ep() { + return &advanced_plugin1_descriptor; +} diff --git a/applications/examples/example_plugins_advanced/plugin2.c b/applications/examples/example_plugins_advanced/plugin2.c new file mode 100644 index 00000000000..f0b2f726db3 --- /dev/null +++ b/applications/examples/example_plugins_advanced/plugin2.c @@ -0,0 +1,40 @@ +/* + * This plugin uses both firmware's API interface and private application headers. + * It can be loaded by a plugin manager that uses CompoundApiInterface, + * which combines both interfaces. + */ + +#include "app_api.h" +#include "plugin_interface.h" + +#include +#include + +static void advanced_plugin2_method1(int arg1) { + /* This function is implemented inside host application */ + app_api_accumulator_mul(arg1); +} + +static void advanced_plugin2_method2() { + /* Accumulator value is stored inside host application */ + FURI_LOG_I("TEST", "Plugin 2, accumulator: %lu", app_api_accumulator_get()); +} + +/* Actual implementation of app<>plugin interface */ +static const AdvancedPlugin advanced_plugin2 = { + .name = "Advanced Plugin 2", + .method1 = &advanced_plugin2_method1, + .method2 = &advanced_plugin2_method2, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor advanced_plugin2_descriptor = { + .appid = PLUGIN_APP_ID, + .ep_api_version = PLUGIN_API_VERSION, + .entry_point = &advanced_plugin2, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* advanced_plugin2_ep() { + return &advanced_plugin2_descriptor; +} diff --git a/applications/examples/example_plugins_advanced/plugin_interface.h b/applications/examples/example_plugins_advanced/plugin_interface.h new file mode 100644 index 00000000000..d99b335ff05 --- /dev/null +++ b/applications/examples/example_plugins_advanced/plugin_interface.h @@ -0,0 +1,12 @@ +#pragma once + +/* Common interface between a plugin and host application */ + +#define PLUGIN_APP_ID "example_plugins_advanced" +#define PLUGIN_API_VERSION 1 + +typedef struct { + const char* name; + void (*method1)(int); + void (*method2)(); +} AdvancedPlugin; diff --git a/applications/examples/example_thermo/README.md b/applications/examples/example_thermo/README.md new file mode 100644 index 00000000000..d298de64309 --- /dev/null +++ b/applications/examples/example_thermo/README.md @@ -0,0 +1,44 @@ +# 1-Wire Thermometer +This example application demonstrates the use of the 1-Wire library with a DS18B20 thermometer. +It also covers basic GUI, input handling, threads and localisation. + +## Electrical connections +Before launching the application, connect the sensor to Flipper's external GPIO according to the table below: +| DS18B20 | Flipper | +| :-----: | :-----: | +| VDD | 9 | +| GND | 18 | +| DQ | 17 | + +*NOTE 1*: GND is also available on pins 8 and 11. + +*NOTE 2*: For any other pin than 17, connect an external 4.7k pull-up resistor to pin 9. + +## Launching the application +In order to launch this demo, follow the steps below: +1. Make sure your Flipper has an SD card installed. +2. Connect your Flipper to the computer via a USB cable. +3. Run `./fbt launch APPSRC=example_thermo` in your terminal emulator of choice. + +## Changing the data pin +It is possible to use other GPIO pin as a 1-Wire data pin. In order to change it, set the `THERMO_GPIO_PIN` macro to any of the options listed below: + +```c +/* Possible GPIO pin choices: + - gpio_ext_pc0 + - gpio_ext_pc1 + - gpio_ext_pc3 + - gpio_ext_pb2 + - gpio_ext_pb3 + - gpio_ext_pa4 + - gpio_ext_pa6 + - gpio_ext_pa7 + - gpio_ibutton +*/ + +#define THERMO_GPIO_PIN (gpio_ibutton) +``` +Do not forget about the external pull-up resistor as these pins do not have one built-in. + +With the changes been made, recompile and launch the application again. +The on-screen text should reflect it by asking to connect the thermometer to another pin. diff --git a/applications/examples/example_thermo/application.fam b/applications/examples/example_thermo/application.fam new file mode 100644 index 00000000000..b4a05c7f96f --- /dev/null +++ b/applications/examples/example_thermo/application.fam @@ -0,0 +1,10 @@ +App( + appid="example_thermo", + name="Example: Thermometer", + apptype=FlipperAppType.EXTERNAL, + entry_point="example_thermo_main", + requires=["gui"], + stack_size=1 * 1024, + fap_icon="example_thermo_10px.png", + fap_category="Examples", +) diff --git a/applications/examples/example_thermo/example_thermo.c b/applications/examples/example_thermo/example_thermo.c new file mode 100644 index 00000000000..5abd963a190 --- /dev/null +++ b/applications/examples/example_thermo/example_thermo.c @@ -0,0 +1,364 @@ +/* + * This file contains an example application that reads and displays + * the temperature from a DS18B20 1-wire thermometer. + * + * It also covers basic GUI, input handling, threads and localisation. + * + * References: + * [1] DS18B20 Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/DS18B20.pdf + */ + +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +#define UPDATE_PERIOD_MS 1000UL +#define TEXT_STORE_SIZE 64U + +#define DS18B20_CMD_SKIP_ROM 0xccU +#define DS18B20_CMD_CONVERT 0x44U +#define DS18B20_CMD_READ_SCRATCHPAD 0xbeU + +#define DS18B20_CFG_RESOLUTION_POS 5U +#define DS18B20_CFG_RESOLUTION_MASK 0x03U +#define DS18B20_DECIMAL_PART_MASK 0x0fU + +#define DS18B20_SIGN_MASK 0xf0U + +/* Possible GPIO pin choices: + - gpio_ext_pc0 + - gpio_ext_pc1 + - gpio_ext_pc3 + - gpio_ext_pb2 + - gpio_ext_pb3 + - gpio_ext_pa4 + - gpio_ext_pa6 + - gpio_ext_pa7 + - gpio_ibutton +*/ + +#define THERMO_GPIO_PIN (gpio_ibutton) + +/* Flags which the reader thread responds to */ +typedef enum { + ReaderThreadFlagExit = 1, +} ReaderThreadFlag; + +typedef union { + struct { + uint8_t temp_lsb; /* Least significant byte of the temperature */ + uint8_t temp_msb; /* Most significant byte of the temperature */ + uint8_t user_alarm_high; /* User register 1 (Temp high alarm) */ + uint8_t user_alarm_low; /* User register 2 (Temp low alarm) */ + uint8_t config; /* Configuration register */ + uint8_t reserved[3]; /* Not used */ + uint8_t crc; /* CRC checksum for error detection */ + } fields; + uint8_t bytes[9]; +} DS18B20Scratchpad; + +/* Application context structure */ +typedef struct { + Gui* gui; + ViewPort* view_port; + FuriThread* reader_thread; + FuriMessageQueue* event_queue; + OneWireHost* onewire; + float temp_celsius; + bool has_device; +} ExampleThermoContext; + +/*************** 1-Wire Communication and Processing *****************/ + +/* Commands the thermometer to begin measuring the temperature. */ +static void example_thermo_request_temperature(ExampleThermoContext* context) { + OneWireHost* onewire = context->onewire; + + /* All 1-wire transactions must happen in a critical section, i.e + not interrupted by other threads. */ + FURI_CRITICAL_ENTER(); + + bool success = false; + do { + /* Each communication with a 1-wire device starts by a reset. + The function will return true if a device responded with a presence pulse. */ + if(!onewire_host_reset(onewire)) break; + /* After the reset, a ROM operation must follow. + If there is only one device connected, the "Skip ROM" command is most appropriate + (it can also be used to address all of the connected devices in some cases).*/ + onewire_host_write(onewire, DS18B20_CMD_SKIP_ROM); + /* After the ROM operation, a device-specific command is issued. + In this case, it's a request to start measuring the temperature. */ + onewire_host_write(onewire, DS18B20_CMD_CONVERT); + + success = true; + } while(false); + + context->has_device = success; + + FURI_CRITICAL_EXIT(); +} + +/* Reads the measured temperature from the thermometer. */ +static void example_thermo_read_temperature(ExampleThermoContext* context) { + /* If there was no device detected, don't try to read the temperature */ + if(!context->has_device) { + return; + } + + OneWireHost* onewire = context->onewire; + + /* All 1-wire transactions must happen in a critical section, i.e + not interrupted by other threads. */ + FURI_CRITICAL_ENTER(); + + bool success = false; + + do { + DS18B20Scratchpad buf; + + /* Attempt reading the temperature 10 times before giving up */ + size_t attempts_left = 10; + do { + /* Each communication with a 1-wire device starts by a reset. + The function will return true if a device responded with a presence pulse. */ + if(!onewire_host_reset(onewire)) continue; + + /* After the reset, a ROM operation must follow. + If there is only one device connected, the "Skip ROM" command is most appropriate + (it can also be used to address all of the connected devices in some cases).*/ + onewire_host_write(onewire, DS18B20_CMD_SKIP_ROM); + + /* After the ROM operation, a device-specific command is issued. + This time, it will be the "Read Scratchpad" command which will + prepare the device's internal buffer memory for reading. */ + onewire_host_write(onewire, DS18B20_CMD_READ_SCRATCHPAD); + + /* The actual reading happens here. A total of 9 bytes is read. */ + onewire_host_read_bytes(onewire, buf.bytes, sizeof(buf.bytes)); + + /* Calculate the checksum and compare it with one provided by the device. */ + const uint8_t crc = maxim_crc8(buf.bytes, sizeof(buf.bytes) - 1, MAXIM_CRC8_INIT); + + /* Checksums match, exit the loop */ + if(crc == buf.fields.crc) break; + + } while(--attempts_left); + + if(attempts_left == 0) break; + + /* Get the measurement resolution from the configuration register. (See [1] page 9) */ + const uint8_t resolution_mode = (buf.fields.config >> DS18B20_CFG_RESOLUTION_POS) & + DS18B20_CFG_RESOLUTION_MASK; + + /* Generate a mask for undefined bits in the decimal part. (See [1] page 6) */ + const uint8_t decimal_mask = + (DS18B20_DECIMAL_PART_MASK << (DS18B20_CFG_RESOLUTION_MASK - resolution_mode)) & + DS18B20_DECIMAL_PART_MASK; + + /* Get the integer and decimal part of the temperature (See [1] page 6) */ + const uint8_t integer_part = (buf.fields.temp_msb << 4U) | (buf.fields.temp_lsb >> 4U); + const uint8_t decimal_part = buf.fields.temp_lsb & decimal_mask; + + /* Calculate the sign of the temperature (See [1] page 6) */ + const bool is_negative = (buf.fields.temp_msb & DS18B20_SIGN_MASK) != 0; + + /* Combine the integer and decimal part together */ + const float temp_celsius_abs = integer_part + decimal_part / 16.f; + + /* Set the appropriate sign */ + context->temp_celsius = is_negative ? -temp_celsius_abs : temp_celsius_abs; + + success = true; + } while(false); + + context->has_device = success; + + FURI_CRITICAL_EXIT(); +} + +/* Periodically requests measurements and reads temperature. This function runs in a separare thread. */ +static int32_t example_thermo_reader_thread_callback(void* ctx) { + ExampleThermoContext* context = ctx; + + for(;;) { + /* Tell the termometer to start measuring the temperature. The process may take up to 750ms. */ + example_thermo_request_temperature(context); + + /* Wait for the measurement to finish. At the same time wait for an exit signal. */ + const uint32_t flags = + furi_thread_flags_wait(ReaderThreadFlagExit, FuriFlagWaitAny, UPDATE_PERIOD_MS); + + /* If an exit signal was received, return from this thread. */ + if(flags != (unsigned)FuriFlagErrorTimeout) break; + + /* The measurement is now ready, read it from the termometer. */ + example_thermo_read_temperature(context); + } + + return 0; +} + +/*************** GUI, Input and Main Loop *****************/ + +/* Draw the GUI of the application. The screen is completely redrawn during each call. */ +static void example_thermo_draw_callback(Canvas* canvas, void* ctx) { + ExampleThermoContext* context = ctx; + char text_store[TEXT_STORE_SIZE]; + const size_t middle_x = canvas_width(canvas) / 2U; + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, middle_x, 12, AlignCenter, AlignBottom, "Thermometer Demo"); + canvas_draw_line(canvas, 0, 16, 128, 16); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, middle_x, 30, AlignCenter, AlignBottom, "Connect thermometer"); + + snprintf( + text_store, + TEXT_STORE_SIZE, + "to GPIO pin %ld", + furi_hal_resources_get_ext_pin_number(&THERMO_GPIO_PIN)); + canvas_draw_str_aligned(canvas, middle_x, 42, AlignCenter, AlignBottom, text_store); + + canvas_set_font(canvas, FontKeyboard); + + if(context->has_device) { + float temp; + char temp_units; + + /* The application is locale-aware. + Change Settings->System->Units to check it out. */ + switch(locale_get_measurement_unit()) { + case LocaleMeasurementUnitsMetric: + temp = context->temp_celsius; + temp_units = 'C'; + break; + case LocaleMeasurementUnitsImperial: + temp = locale_celsius_to_fahrenheit(context->temp_celsius); + temp_units = 'F'; + break; + default: + furi_crash("Illegal measurement units"); + } + /* If a reading is available, display it */ + snprintf(text_store, TEXT_STORE_SIZE, "Temperature: %+.1f%c", (double)temp, temp_units); + } else { + /* Or show a message that no data is available */ + strncpy(text_store, "-- No data --", TEXT_STORE_SIZE); + } + + canvas_draw_str_aligned(canvas, middle_x, 58, AlignCenter, AlignBottom, text_store); +} + +/* This function is called from the GUI thread. All it does is put the event + into the application's queue so it can be processed later. */ +static void example_thermo_input_callback(InputEvent* event, void* ctx) { + ExampleThermoContext* context = ctx; + furi_message_queue_put(context->event_queue, event, FuriWaitForever); +} + +/* Starts the reader thread and handles the input */ +static void example_thermo_run(ExampleThermoContext* context) { + /* Enable power on external pins */ + furi_hal_power_enable_otg(); + + /* Configure the hardware in host mode */ + onewire_host_start(context->onewire); + + /* Start the reader thread. It will talk to the thermometer in the background. */ + furi_thread_start(context->reader_thread); + + /* An endless loop which handles the input*/ + for(bool is_running = true; is_running;) { + InputEvent event; + /* Wait for an input event. Input events come from the GUI thread via a callback. */ + const FuriStatus status = + furi_message_queue_get(context->event_queue, &event, FuriWaitForever); + + /* This application is only interested in short button presses. */ + if((status != FuriStatusOk) || (event.type != InputTypeShort)) { + continue; + } + + /* When the user presses the "Back" button, break the loop and exit the application. */ + if(event.key == InputKeyBack) { + is_running = false; + } + } + + /* Signal the reader thread to cease operation and exit */ + furi_thread_flags_set(furi_thread_get_id(context->reader_thread), ReaderThreadFlagExit); + + /* Wait for the reader thread to finish */ + furi_thread_join(context->reader_thread); + + /* Reset the hardware */ + onewire_host_stop(context->onewire); + + /* Disable power on external pins */ + furi_hal_power_disable_otg(); +} + +/******************** Initialisation & startup *****************************/ + +/* Allocate the memory and initialise the variables */ +static ExampleThermoContext* example_thermo_context_alloc() { + ExampleThermoContext* context = malloc(sizeof(ExampleThermoContext)); + + context->view_port = view_port_alloc(); + view_port_draw_callback_set(context->view_port, example_thermo_draw_callback, context); + view_port_input_callback_set(context->view_port, example_thermo_input_callback, context); + + context->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + context->reader_thread = furi_thread_alloc(); + furi_thread_set_stack_size(context->reader_thread, 1024U); + furi_thread_set_context(context->reader_thread, context); + furi_thread_set_callback(context->reader_thread, example_thermo_reader_thread_callback); + + context->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(context->gui, context->view_port, GuiLayerFullscreen); + + context->onewire = onewire_host_alloc(&THERMO_GPIO_PIN); + + return context; +} + +/* Release the unused resources and deallocate memory */ +static void example_thermo_context_free(ExampleThermoContext* context) { + view_port_enabled_set(context->view_port, false); + gui_remove_view_port(context->gui, context->view_port); + + onewire_host_free(context->onewire); + furi_thread_free(context->reader_thread); + furi_message_queue_free(context->event_queue); + view_port_free(context->view_port); + + furi_record_close(RECORD_GUI); +} + +/* The application's entry point. Execution starts from here. */ +int32_t example_thermo_main(void* p) { + UNUSED(p); + + /* Allocate all of the necessary structures */ + ExampleThermoContext* context = example_thermo_context_alloc(); + + /* Start the application's main loop. It won't return until the application was requested to exit. */ + example_thermo_run(context); + + /* Release all unneeded resources */ + example_thermo_context_free(context); + + return 0; +} diff --git a/applications/examples/example_thermo/example_thermo_10px.png b/applications/examples/example_thermo/example_thermo_10px.png new file mode 100644 index 00000000000..3d527f306c2 Binary files /dev/null and b/applications/examples/example_thermo/example_thermo_10px.png differ diff --git a/applications/extapps.scons b/applications/extapps.scons deleted file mode 100644 index 11b59757620..00000000000 --- a/applications/extapps.scons +++ /dev/null @@ -1,58 +0,0 @@ -Import("ENV") - - -from fbt.appmanifest import FlipperAppType - -appenv = ENV.Clone(tools=["fbt_extapps"]) - -appenv.Replace( - LINKER_SCRIPT="application-ext", - STRIPFLAGS=[ - "--strip-debug", - "--strip-unneeded", - "-d", - "-g", - "-S", - ], -) - -appenv.AppendUnique( - CCFLAGS=[ - "-Os", - "-ggdb3", - "-mword-relocations", - "-mlong-calls", - "-fno-common", - "-nostdlib", - "-fvisibility=hidden", - ], - LINKFLAGS=[ - "-r", - "-s", - # "-Bsymbolic", - "-nostartfiles", - "-mlong-calls", - "-fno-common", - "-nostdlib", - "-Wl,--gc-sections", - "-Wl,--no-export-dynamic", - "-fvisibility=hidden", - "-Wl,-e${APP_ENTRY}", - "-Xlinker", - "-Map=${TARGET}.map", - ], -) - - -extapps = [] -for apptype in (FlipperAppType.PLUGIN, FlipperAppType.EXTERNAL): - for app in appenv["APPBUILD"].get_apps_of_type(apptype): - extapps.append(appenv.BuildAppElf(app)) - -# Ugly access to global option -if extra_app_list := GetOption("extra_ext_apps"): - for extra_app in extra_app_list.split(","): - extapps.append(appenv.BuildAppElf(appenv["APPMGR"].get(extra_app))) - -Alias(appenv["FIRMWARE_BUILD_CFG"] + "_extapps", extapps) -Return("extapps") diff --git a/applications/gpio/application.fam b/applications/gpio/application.fam deleted file mode 100644 index 64f8db5b098..00000000000 --- a/applications/gpio/application.fam +++ /dev/null @@ -1,11 +0,0 @@ -App( - appid="gpio", - name="GPIO", - apptype=FlipperAppType.APP, - entry_point="gpio_app", - cdefines=["APP_GPIO"], - requires=["gui"], - stack_size=1 * 1024, - icon="A_GPIO_14", - order=50, -) diff --git a/applications/gpio/gpio_item.c b/applications/gpio/gpio_item.c deleted file mode 100644 index 2d0f5f6762b..00000000000 --- a/applications/gpio/gpio_item.c +++ /dev/null @@ -1,51 +0,0 @@ -#include "gpio_item.h" - -#include - -typedef struct { - const char* name; - const GpioPin* pin; -} GpioItem; - -static const GpioItem gpio_item[GPIO_ITEM_COUNT] = { - {"1.2: PA7", &gpio_ext_pa7}, - {"1.3: PA6", &gpio_ext_pa6}, - {"1.4: PA4", &gpio_ext_pa4}, - {"1.5: PB3", &gpio_ext_pb3}, - {"1.6: PB2", &gpio_ext_pb2}, - {"1.7: PC3", &gpio_ext_pc3}, - {"2.7: PC1", &gpio_ext_pc1}, - {"2.8: PC0", &gpio_ext_pc0}, -}; - -void gpio_item_configure_pin(uint8_t index, GpioMode mode) { - furi_assert(index < GPIO_ITEM_COUNT); - furi_hal_gpio_write(gpio_item[index].pin, false); - furi_hal_gpio_init(gpio_item[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh); -} - -void gpio_item_configure_all_pins(GpioMode mode) { - for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { - gpio_item_configure_pin(i, mode); - } -} - -void gpio_item_set_pin(uint8_t index, bool level) { - furi_assert(index < GPIO_ITEM_COUNT); - furi_hal_gpio_write(gpio_item[index].pin, level); -} - -void gpio_item_set_all_pins(bool level) { - for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { - gpio_item_set_pin(i, level); - } -} - -const char* gpio_item_get_pin_name(uint8_t index) { - furi_assert(index < GPIO_ITEM_COUNT + 1); - if(index == GPIO_ITEM_COUNT) { - return "ALL"; - } else { - return gpio_item[index].name; - } -} diff --git a/applications/gpio/gpio_item.h b/applications/gpio/gpio_item.h deleted file mode 100644 index 5cb2b86c1ba..00000000000 --- a/applications/gpio/gpio_item.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -#define GPIO_ITEM_COUNT 8 - -void gpio_item_configure_pin(uint8_t index, GpioMode mode); - -void gpio_item_configure_all_pins(GpioMode mode); - -void gpio_item_set_pin(uint8_t index, bool level); - -void gpio_item_set_all_pins(bool level); - -const char* gpio_item_get_pin_name(uint8_t index); diff --git a/applications/gpio/scenes/gpio_scene_usb_uart_config.c b/applications/gpio/scenes/gpio_scene_usb_uart_config.c deleted file mode 100644 index 653a306aa56..00000000000 --- a/applications/gpio/scenes/gpio_scene_usb_uart_config.c +++ /dev/null @@ -1,137 +0,0 @@ -#include "../usb_uart_bridge.h" -#include "../gpio_app_i.h" -#include "furi_hal.h" - -typedef enum { - UsbUartLineIndexVcp, - UsbUartLineIndexBaudrate, - UsbUartLineIndexUart, - UsbUartLineIndexFlow, -} LineIndex; - -static UsbUartConfig* cfg_set; - -static const char* vcp_ch[] = {"0 (CLI)", "1"}; -static const char* uart_ch[] = {"13,14", "15,16"}; -static const char* flow_pins[] = {"None", "2,3", "6,7"}; -static const char* baudrate_mode[] = {"Host"}; -static const uint32_t baudrate_list[] = { - 2400, - 9600, - 19200, - 38400, - 57600, - 115200, - 230400, - 460800, - 921600, -}; - -bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -static void line_vcp_cb(VariableItem* item) { - GpioApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, vcp_ch[index]); - - cfg_set->vcp_ch = index; - usb_uart_set_config(app->usb_uart_bridge, cfg_set); -} - -static void line_port_cb(VariableItem* item) { - GpioApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, uart_ch[index]); - - if(index == 0) - cfg_set->uart_ch = FuriHalUartIdUSART1; - else if(index == 1) - cfg_set->uart_ch = FuriHalUartIdLPUART1; - usb_uart_set_config(app->usb_uart_bridge, cfg_set); -} - -static void line_flow_cb(VariableItem* item) { - GpioApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, flow_pins[index]); - - cfg_set->flow_pins = index; - usb_uart_set_config(app->usb_uart_bridge, cfg_set); -} - -static void line_baudrate_cb(VariableItem* item) { - GpioApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - char br_text[8]; - - if(index > 0) { - snprintf(br_text, 7, "%lu", baudrate_list[index - 1]); - variable_item_set_current_value_text(item, br_text); - cfg_set->baudrate = baudrate_list[index - 1]; - } else { - variable_item_set_current_value_text(item, baudrate_mode[index]); - cfg_set->baudrate = 0; - } - cfg_set->baudrate_mode = index; - usb_uart_set_config(app->usb_uart_bridge, cfg_set); -} - -void gpio_scene_usb_uart_cfg_on_enter(void* context) { - GpioApp* app = context; - VariableItemList* var_item_list = app->var_item_list; - - cfg_set = malloc(sizeof(UsbUartConfig)); - usb_uart_get_config(app->usb_uart_bridge, cfg_set); - - VariableItem* item; - char br_text[8]; - - item = variable_item_list_add(var_item_list, "USB Channel", 2, line_vcp_cb, app); - variable_item_set_current_value_index(item, cfg_set->vcp_ch); - variable_item_set_current_value_text(item, vcp_ch[cfg_set->vcp_ch]); - - item = variable_item_list_add( - var_item_list, - "Baudrate", - sizeof(baudrate_list) / sizeof(baudrate_list[0]) + 1, - line_baudrate_cb, - app); - variable_item_set_current_value_index(item, cfg_set->baudrate_mode); - if(cfg_set->baudrate_mode > 0) { - snprintf(br_text, 7, "%lu", baudrate_list[cfg_set->baudrate_mode - 1]); - variable_item_set_current_value_text(item, br_text); - } else { - variable_item_set_current_value_text(item, baudrate_mode[cfg_set->baudrate_mode]); - } - - item = variable_item_list_add(var_item_list, "UART Pins", 2, line_port_cb, app); - variable_item_set_current_value_index(item, cfg_set->uart_ch); - variable_item_set_current_value_text(item, uart_ch[cfg_set->uart_ch]); - - item = variable_item_list_add(var_item_list, "RTS/DTR Pins", 3, line_flow_cb, app); - variable_item_set_current_value_index(item, cfg_set->flow_pins); - variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]); - - variable_item_list_set_selected_item( - var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUartCfg)); - - view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUartCfg); -} - -void gpio_scene_usb_uart_cfg_on_exit(void* context) { - GpioApp* app = context; - scene_manager_set_scene_state( - app->scene_manager, - GpioAppViewUsbUartCfg, - variable_item_list_get_selected_item_index(app->var_item_list)); - variable_item_list_reset(app->var_item_list); - free(cfg_set); -} diff --git a/applications/gpio/views/gpio_test.c b/applications/gpio/views/gpio_test.c deleted file mode 100755 index 89c09f86492..00000000000 --- a/applications/gpio/views/gpio_test.c +++ /dev/null @@ -1,131 +0,0 @@ -#include "gpio_test.h" -#include "../gpio_item.h" - -#include - -struct GpioTest { - View* view; - GpioTestOkCallback callback; - void* context; -}; - -typedef struct { - uint8_t pin_idx; -} GpioTestModel; - -static bool gpio_test_process_left(GpioTest* gpio_test); -static bool gpio_test_process_right(GpioTest* gpio_test); -static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event); - -static void gpio_test_draw_callback(Canvas* canvas, void* _model) { - GpioTestModel* model = _model; - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "GPIO Output Mode Test"); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned( - canvas, 64, 16, AlignCenter, AlignTop, "Press < or > to change pin"); - elements_multiline_text_aligned( - canvas, 64, 32, AlignCenter, AlignTop, gpio_item_get_pin_name(model->pin_idx)); -} - -static bool gpio_test_input_callback(InputEvent* event, void* context) { - furi_assert(context); - GpioTest* gpio_test = context; - bool consumed = false; - - if(event->type == InputTypeShort) { - if(event->key == InputKeyRight) { - consumed = gpio_test_process_right(gpio_test); - } else if(event->key == InputKeyLeft) { - consumed = gpio_test_process_left(gpio_test); - } - } else if(event->key == InputKeyOk) { - consumed = gpio_test_process_ok(gpio_test, event); - } - - return consumed; -} - -static bool gpio_test_process_left(GpioTest* gpio_test) { - with_view_model( - gpio_test->view, (GpioTestModel * model) { - if(model->pin_idx) { - model->pin_idx--; - } - return true; - }); - return true; -} - -static bool gpio_test_process_right(GpioTest* gpio_test) { - with_view_model( - gpio_test->view, (GpioTestModel * model) { - if(model->pin_idx < GPIO_ITEM_COUNT) { - model->pin_idx++; - } - return true; - }); - return true; -} - -static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event) { - bool consumed = false; - - with_view_model( - gpio_test->view, (GpioTestModel * model) { - if(event->type == InputTypePress) { - if(model->pin_idx < GPIO_ITEM_COUNT) { - gpio_item_set_pin(model->pin_idx, true); - } else { - gpio_item_set_all_pins(true); - } - consumed = true; - } else if(event->type == InputTypeRelease) { - if(model->pin_idx < GPIO_ITEM_COUNT) { - gpio_item_set_pin(model->pin_idx, false); - } else { - gpio_item_set_all_pins(false); - } - consumed = true; - } - gpio_test->callback(event->type, gpio_test->context); - return true; - }); - - return consumed; -} - -GpioTest* gpio_test_alloc() { - GpioTest* gpio_test = malloc(sizeof(GpioTest)); - - gpio_test->view = view_alloc(); - view_allocate_model(gpio_test->view, ViewModelTypeLocking, sizeof(GpioTestModel)); - view_set_context(gpio_test->view, gpio_test); - view_set_draw_callback(gpio_test->view, gpio_test_draw_callback); - view_set_input_callback(gpio_test->view, gpio_test_input_callback); - - return gpio_test; -} - -void gpio_test_free(GpioTest* gpio_test) { - furi_assert(gpio_test); - view_free(gpio_test->view); - free(gpio_test); -} - -View* gpio_test_get_view(GpioTest* gpio_test) { - furi_assert(gpio_test); - return gpio_test->view; -} - -void gpio_test_set_ok_callback(GpioTest* gpio_test, GpioTestOkCallback callback, void* context) { - furi_assert(gpio_test); - furi_assert(callback); - with_view_model( - gpio_test->view, (GpioTestModel * model) { - UNUSED(model); - gpio_test->callback = callback; - gpio_test->context = context; - return false; - }); -} diff --git a/applications/gui/application.fam b/applications/gui/application.fam deleted file mode 100644 index 0116f5b88a3..00000000000 --- a/applications/gui/application.fam +++ /dev/null @@ -1,13 +0,0 @@ -App( - appid="gui", - name="GuiSrv", - apptype=FlipperAppType.SERVICE, - entry_point="gui_srv", - cdefines=["SRV_GUI"], - requires=[ - "input", - "notification", - ], - stack_size=2 * 1024, - order=70, -) diff --git a/applications/gui/canvas.c b/applications/gui/canvas.c deleted file mode 100644 index 1e275aafb43..00000000000 --- a/applications/gui/canvas.c +++ /dev/null @@ -1,389 +0,0 @@ -#include "canvas_i.h" -#include "icon_i.h" -#include "icon_animation_i.h" - -#include -#include -#include -#include - -const CanvasFontParameters canvas_font_params[FontTotalNumber] = { - [FontPrimary] = {.leading_default = 12, .leading_min = 11, .height = 8, .descender = 2}, - [FontSecondary] = {.leading_default = 11, .leading_min = 9, .height = 7, .descender = 2}, - [FontKeyboard] = {.leading_default = 11, .leading_min = 9, .height = 7, .descender = 2}, - [FontBigNumbers] = {.leading_default = 18, .leading_min = 16, .height = 15, .descender = 0}, -}; - -Canvas* canvas_init() { - Canvas* canvas = malloc(sizeof(Canvas)); - - // Setup u8g2 - u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); - canvas->orientation = CanvasOrientationHorizontal; - // Initialize display - u8g2_InitDisplay(&canvas->fb); - // Wake up display - u8g2_SetPowerSave(&canvas->fb, 0); - - // Clear buffer and send to device - canvas_clear(canvas); - canvas_commit(canvas); - - return canvas; -} - -void canvas_free(Canvas* canvas) { - furi_assert(canvas); - free(canvas); -} - -void canvas_reset(Canvas* canvas) { - furi_assert(canvas); - - canvas_clear(canvas); - - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontSecondary); - canvas_set_font_direction(canvas, CanvasDirectionLeftToRight); -} - -void canvas_commit(Canvas* canvas) { - furi_assert(canvas); - u8g2_SendBuffer(&canvas->fb); -} - -uint8_t* canvas_get_buffer(Canvas* canvas) { - furi_assert(canvas); - return u8g2_GetBufferPtr(&canvas->fb); -} - -size_t canvas_get_buffer_size(Canvas* canvas) { - furi_assert(canvas); - return u8g2_GetBufferTileWidth(&canvas->fb) * u8g2_GetBufferTileHeight(&canvas->fb) * 8; -} - -void canvas_frame_set( - Canvas* canvas, - uint8_t offset_x, - uint8_t offset_y, - uint8_t width, - uint8_t height) { - furi_assert(canvas); - canvas->offset_x = offset_x; - canvas->offset_y = offset_y; - canvas->width = width; - canvas->height = height; -} - -uint8_t canvas_width(Canvas* canvas) { - furi_assert(canvas); - return canvas->width; -} - -uint8_t canvas_height(Canvas* canvas) { - furi_assert(canvas); - return canvas->height; -} - -uint8_t canvas_current_font_height(Canvas* canvas) { - furi_assert(canvas); - uint8_t font_height = u8g2_GetMaxCharHeight(&canvas->fb); - - if(canvas->fb.font == u8g2_font_haxrcorp4089_tr) { - font_height += 1; - } - - return font_height; -} - -CanvasFontParameters* canvas_get_font_params(Canvas* canvas, Font font) { - furi_assert(canvas); - furi_assert(font < FontTotalNumber); - return (CanvasFontParameters*)&canvas_font_params[font]; -} - -void canvas_clear(Canvas* canvas) { - furi_assert(canvas); - u8g2_ClearBuffer(&canvas->fb); -} - -void canvas_set_color(Canvas* canvas, Color color) { - furi_assert(canvas); - u8g2_SetDrawColor(&canvas->fb, color); -} - -void canvas_set_font_direction(Canvas* canvas, CanvasDirection dir) { - furi_assert(canvas); - u8g2_SetFontDirection(&canvas->fb, dir); -} - -void canvas_invert_color(Canvas* canvas) { - canvas->fb.draw_color = !canvas->fb.draw_color; -} - -void canvas_set_font(Canvas* canvas, Font font) { - furi_assert(canvas); - u8g2_SetFontMode(&canvas->fb, 1); - if(font == FontPrimary) { - u8g2_SetFont(&canvas->fb, u8g2_font_helvB08_tr); - } else if(font == FontSecondary) { - u8g2_SetFont(&canvas->fb, u8g2_font_haxrcorp4089_tr); - } else if(font == FontKeyboard) { - u8g2_SetFont(&canvas->fb, u8g2_font_profont11_mr); - } else if(font == FontBigNumbers) { - u8g2_SetFont(&canvas->fb, u8g2_font_profont22_tn); - } else { - furi_crash(NULL); - } -} - -void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str) { - furi_assert(canvas); - if(!str) return; - x += canvas->offset_x; - y += canvas->offset_y; - u8g2_DrawStr(&canvas->fb, x, y, str); -} - -void canvas_draw_str_aligned( - Canvas* canvas, - uint8_t x, - uint8_t y, - Align horizontal, - Align vertical, - const char* str) { - furi_assert(canvas); - if(!str) return; - x += canvas->offset_x; - y += canvas->offset_y; - - switch(horizontal) { - case AlignLeft: - break; - case AlignRight: - x -= u8g2_GetStrWidth(&canvas->fb, str); - break; - case AlignCenter: - x -= (u8g2_GetStrWidth(&canvas->fb, str) / 2); - break; - default: - furi_crash(NULL); - break; - } - - switch(vertical) { - case AlignTop: - y += u8g2_GetAscent(&canvas->fb); - break; - case AlignBottom: - break; - case AlignCenter: - y += (u8g2_GetAscent(&canvas->fb) / 2); - break; - default: - furi_crash(NULL); - break; - } - - u8g2_DrawStr(&canvas->fb, x, y, str); -} - -uint16_t canvas_string_width(Canvas* canvas, const char* str) { - furi_assert(canvas); - if(!str) return 0; - return u8g2_GetStrWidth(&canvas->fb, str); -} - -uint8_t canvas_glyph_width(Canvas* canvas, char symbol) { - furi_assert(canvas); - return u8g2_GetGlyphWidth(&canvas->fb, symbol); -} - -void canvas_draw_bitmap( - Canvas* canvas, - uint8_t x, - uint8_t y, - uint8_t width, - uint8_t height, - const uint8_t* compressed_bitmap_data) { - furi_assert(canvas); - - x += canvas->offset_x; - y += canvas->offset_y; - uint8_t* bitmap_data = NULL; - furi_hal_compress_icon_decode(compressed_bitmap_data, &bitmap_data); - u8g2_DrawXBM(&canvas->fb, x, y, width, height, bitmap_data); -} - -void canvas_draw_icon_animation( - Canvas* canvas, - uint8_t x, - uint8_t y, - IconAnimation* icon_animation) { - furi_assert(canvas); - furi_assert(icon_animation); - - x += canvas->offset_x; - y += canvas->offset_y; - uint8_t* icon_data = NULL; - furi_hal_compress_icon_decode(icon_animation_get_data(icon_animation), &icon_data); - u8g2_DrawXBM( - &canvas->fb, - x, - y, - icon_animation_get_width(icon_animation), - icon_animation_get_height(icon_animation), - icon_data); -} - -void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) { - furi_assert(canvas); - furi_assert(icon); - - x += canvas->offset_x; - y += canvas->offset_y; - uint8_t* icon_data = NULL; - furi_hal_compress_icon_decode(icon_get_data(icon), &icon_data); - u8g2_DrawXBM(&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data); -} - -void canvas_draw_dot(Canvas* canvas, uint8_t x, uint8_t y) { - furi_assert(canvas); - x += canvas->offset_x; - y += canvas->offset_y; - u8g2_DrawPixel(&canvas->fb, x, y); -} - -void canvas_draw_box(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height) { - furi_assert(canvas); - x += canvas->offset_x; - y += canvas->offset_y; - u8g2_DrawBox(&canvas->fb, x, y, width, height); -} - -void canvas_draw_rbox( - Canvas* canvas, - uint8_t x, - uint8_t y, - uint8_t width, - uint8_t height, - uint8_t radius) { - furi_assert(canvas); - x += canvas->offset_x; - y += canvas->offset_y; - u8g2_DrawRBox(&canvas->fb, x, y, width, height, radius); -} - -void canvas_draw_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height) { - furi_assert(canvas); - x += canvas->offset_x; - y += canvas->offset_y; - u8g2_DrawFrame(&canvas->fb, x, y, width, height); -} - -void canvas_draw_rframe( - Canvas* canvas, - uint8_t x, - uint8_t y, - uint8_t width, - uint8_t height, - uint8_t radius) { - furi_assert(canvas); - x += canvas->offset_x; - y += canvas->offset_y; - u8g2_DrawRFrame(&canvas->fb, x, y, width, height, radius); -} - -void canvas_draw_line(Canvas* canvas, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) { - furi_assert(canvas); - x1 += canvas->offset_x; - y1 += canvas->offset_y; - x2 += canvas->offset_x; - y2 += canvas->offset_y; - u8g2_DrawLine(&canvas->fb, x1, y1, x2, y2); -} - -void canvas_draw_circle(Canvas* canvas, uint8_t x, uint8_t y, uint8_t radius) { - furi_assert(canvas); - x += canvas->offset_x; - y += canvas->offset_y; - u8g2_DrawCircle(&canvas->fb, x, y, radius, U8G2_DRAW_ALL); -} - -void canvas_draw_disc(Canvas* canvas, uint8_t x, uint8_t y, uint8_t radius) { - furi_assert(canvas); - x += canvas->offset_x; - y += canvas->offset_y; - u8g2_DrawDisc(&canvas->fb, x, y, radius, U8G2_DRAW_ALL); -} - -void canvas_draw_triangle( - Canvas* canvas, - uint8_t x, - uint8_t y, - uint8_t base, - uint8_t height, - CanvasDirection dir) { - furi_assert(canvas); - if(dir == CanvasDirectionBottomToTop) { - canvas_draw_line(canvas, x - base / 2, y, x + base / 2, y); - canvas_draw_line(canvas, x - base / 2, y, x, y - height + 1); - canvas_draw_line(canvas, x, y - height + 1, x + base / 2, y); - } else if(dir == CanvasDirectionTopToBottom) { - canvas_draw_line(canvas, x - base / 2, y, x + base / 2, y); - canvas_draw_line(canvas, x - base / 2, y, x, y + height - 1); - canvas_draw_line(canvas, x, y + height - 1, x + base / 2, y); - } else if(dir == CanvasDirectionRightToLeft) { - canvas_draw_line(canvas, x, y - base / 2, x, y + base / 2); - canvas_draw_line(canvas, x, y - base / 2, x - height + 1, y); - canvas_draw_line(canvas, x - height + 1, y, x, y + base / 2); - } else if(dir == CanvasDirectionLeftToRight) { - canvas_draw_line(canvas, x, y - base / 2, x, y + base / 2); - canvas_draw_line(canvas, x, y - base / 2, x + height - 1, y); - canvas_draw_line(canvas, x + height - 1, y, x, y + base / 2); - } -} - -void canvas_draw_xbm( - Canvas* canvas, - uint8_t x, - uint8_t y, - uint8_t w, - uint8_t h, - const uint8_t* bitmap) { - furi_assert(canvas); - x += canvas->offset_x; - y += canvas->offset_y; - u8g2_DrawXBM(&canvas->fb, x, y, w, h, bitmap); -} - -void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch) { - furi_assert(canvas); - x += canvas->offset_x; - y += canvas->offset_y; - u8g2_DrawGlyph(&canvas->fb, x, y, ch); -} - -void canvas_set_bitmap_mode(Canvas* canvas, bool alpha) { - u8g2_SetBitmapMode(&canvas->fb, alpha ? 1 : 0); -} - -void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation) { - furi_assert(canvas); - if(canvas->orientation != orientation) { - canvas->orientation = orientation; - if(canvas->orientation == CanvasOrientationHorizontal) { - FURI_SWAP(canvas->width, canvas->height); - u8g2_SetDisplayRotation(&canvas->fb, U8G2_R0); - } else if(canvas->orientation == CanvasOrientationVertical) { - FURI_SWAP(canvas->width, canvas->height); - u8g2_SetDisplayRotation(&canvas->fb, U8G2_R3); - } else { - furi_assert(0); - } - } -} - -CanvasOrientation canvas_get_orientation(const Canvas* canvas) { - return canvas->orientation; -} diff --git a/applications/gui/canvas_i.h b/applications/gui/canvas_i.h deleted file mode 100644 index d5594c24a08..00000000000 --- a/applications/gui/canvas_i.h +++ /dev/null @@ -1,90 +0,0 @@ -/** - * @file canvas_i.h - * GUI: internal Canvas API - */ - -#pragma once - -#include "canvas.h" -#include - -/** Canvas structure - */ -struct Canvas { - u8g2_t fb; - CanvasOrientation orientation; - uint8_t offset_x; - uint8_t offset_y; - uint8_t width; - uint8_t height; -}; - -/** Allocate memory and initialize canvas - * - * @return Canvas instance - */ -Canvas* canvas_init(); - -/** Free canvas memory - * - * @param canvas Canvas instance - */ -void canvas_free(Canvas* canvas); - -/** Reset canvas drawing tools configuration - * - * @param canvas Canvas instance - */ -void canvas_reset(Canvas* canvas); - -/** Commit canvas. Send buffer to display - * - * @param canvas Canvas instance - */ -void canvas_commit(Canvas* canvas); - -/** Get canvas buffer. - * - * @param canvas Canvas instance - * - * @return pointer to buffer - */ -uint8_t* canvas_get_buffer(Canvas* canvas); - -/** Get canvas buffer size. - * - * @param canvas Canvas instance - * - * @return size of canvas in bytes - */ -size_t canvas_get_buffer_size(Canvas* canvas); - -/** Set drawing region relative to real screen buffer - * - * @param canvas Canvas instance - * @param offset_x x coordinate offset - * @param offset_y y coordinate offset - * @param width width - * @param height height - */ -void canvas_frame_set( - Canvas* canvas, - uint8_t offset_x, - uint8_t offset_y, - uint8_t width, - uint8_t height); - -/** Set canvas orientation - * - * @param canvas Canvas instance - * @param orientation CanvasOrientation - */ -void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation); - -/** Get canvas orientation - * - * @param canvas Canvas instance - * - * @return CanvasOrientation - */ -CanvasOrientation canvas_get_orientation(const Canvas* canvas); diff --git a/applications/gui/gui.c b/applications/gui/gui.c deleted file mode 100644 index 6b4b9a0a705..00000000000 --- a/applications/gui/gui.c +++ /dev/null @@ -1,522 +0,0 @@ -#include "gui/canvas.h" -#include "gui_i.h" - -#define TAG "GuiSrv" - -ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) { - // Iterating backward - ViewPortArray_it_t it; - ViewPortArray_it_last(it, array); - while(!ViewPortArray_end_p(it)) { - ViewPort* view_port = *ViewPortArray_ref(it); - if(view_port_is_enabled(view_port)) { - return view_port; - } - ViewPortArray_previous(it); - } - return NULL; -} - -void gui_update(Gui* gui) { - furi_assert(gui); - furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_DRAW); -} - -void gui_input_events_callback(const void* value, void* ctx) { - furi_assert(value); - furi_assert(ctx); - - Gui* gui = ctx; - - furi_message_queue_put(gui->input_queue, value, FuriWaitForever); - furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_INPUT); -} - -// Only Fullscreen supports vertical display for now -bool gui_redraw_fs(Gui* gui) { - canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal); - canvas_frame_set(gui->canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT); - ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]); - if(view_port) { - view_port_draw(view_port, gui->canvas); - return true; - } else { - return false; - } -} - -static void gui_redraw_status_bar(Gui* gui, bool need_attention) { - ViewPortArray_it_t it; - uint8_t left_used = 0; - uint8_t right_used = 0; - uint8_t width; - canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal); - canvas_frame_set( - gui->canvas, GUI_STATUS_BAR_X, GUI_STATUS_BAR_Y, GUI_DISPLAY_WIDTH, GUI_STATUS_BAR_HEIGHT); - - /* for support black theme - paint white area and - * draw icon with transparent white color - */ - canvas_set_color(gui->canvas, ColorWhite); - canvas_draw_box(gui->canvas, 1, 1, 9, 7); - canvas_draw_box(gui->canvas, 7, 3, 58, 6); - canvas_draw_box(gui->canvas, 61, 1, 32, 7); - canvas_draw_box(gui->canvas, 89, 3, 38, 6); - canvas_set_color(gui->canvas, ColorBlack); - canvas_set_bitmap_mode(gui->canvas, 1); - canvas_draw_icon(gui->canvas, 0, 0, &I_Background_128x11); - canvas_set_bitmap_mode(gui->canvas, 0); - - // Right side - uint8_t x = GUI_DISPLAY_WIDTH - 1; - ViewPortArray_it(it, gui->layers[GuiLayerStatusBarRight]); - while(!ViewPortArray_end_p(it) && right_used < GUI_STATUS_BAR_WIDTH) { - ViewPort* view_port = *ViewPortArray_ref(it); - if(view_port_is_enabled(view_port)) { - width = view_port_get_width(view_port); - if(!width) width = 8; - // Recalculate next position - right_used += (width + 2); - x -= (width + 2); - // Prepare work area background - canvas_frame_set( - gui->canvas, - x - 1, - GUI_STATUS_BAR_Y + 1, - width + 2, - GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); - canvas_set_color(gui->canvas, ColorWhite); - canvas_draw_box( - gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas)); - canvas_set_color(gui->canvas, ColorBlack); - // ViewPort draw - canvas_frame_set( - gui->canvas, x, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT); - view_port_draw(view_port, gui->canvas); - } - ViewPortArray_next(it); - } - // Draw frame around icons on the right - if(right_used) { - canvas_frame_set( - gui->canvas, - GUI_DISPLAY_WIDTH - 3 - right_used, - GUI_STATUS_BAR_Y, - right_used + 3, - GUI_STATUS_BAR_HEIGHT); - canvas_set_color(gui->canvas, ColorBlack); - canvas_draw_rframe( - gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1); - canvas_draw_line( - gui->canvas, - canvas_width(gui->canvas) - 2, - 1, - canvas_width(gui->canvas) - 2, - canvas_height(gui->canvas) - 2); - canvas_draw_line( - gui->canvas, - 1, - canvas_height(gui->canvas) - 2, - canvas_width(gui->canvas) - 2, - canvas_height(gui->canvas) - 2); - } - - // Left side - x = 2; - ViewPortArray_it(it, gui->layers[GuiLayerStatusBarLeft]); - while(!ViewPortArray_end_p(it) && (right_used + left_used) < GUI_STATUS_BAR_WIDTH) { - ViewPort* view_port = *ViewPortArray_ref(it); - if(view_port_is_enabled(view_port)) { - width = view_port_get_width(view_port); - if(!width) width = 8; - // Prepare work area background - canvas_frame_set( - gui->canvas, - x - 1, - GUI_STATUS_BAR_Y + 1, - width + 2, - GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); - canvas_set_color(gui->canvas, ColorWhite); - canvas_draw_box( - gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas)); - canvas_set_color(gui->canvas, ColorBlack); - // ViewPort draw - canvas_frame_set( - gui->canvas, x, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT); - view_port_draw(view_port, gui->canvas); - // Recalculate next position - left_used += (width + 2); - x += (width + 2); - } - ViewPortArray_next(it); - } - // Extra notification - if(need_attention) { - width = icon_get_width(&I_Attention_5x8); - // Prepare work area background - canvas_frame_set( - gui->canvas, - x - 1, - GUI_STATUS_BAR_Y + 1, - width + 2, - GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); - canvas_set_color(gui->canvas, ColorWhite); - canvas_draw_box(gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas)); - canvas_set_color(gui->canvas, ColorBlack); - // Draw Icon - canvas_frame_set( - gui->canvas, x, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT); - canvas_draw_icon(gui->canvas, 0, 0, &I_Attention_5x8); - // Recalculate next position - left_used += (width + 2); - x += (width + 2); - } - // Draw frame around icons on the left - if(left_used) { - canvas_frame_set(gui->canvas, 0, 0, left_used + 3, GUI_STATUS_BAR_HEIGHT); - canvas_draw_rframe( - gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1); - canvas_draw_line( - gui->canvas, - canvas_width(gui->canvas) - 2, - 1, - canvas_width(gui->canvas) - 2, - canvas_height(gui->canvas) - 2); - canvas_draw_line( - gui->canvas, - 1, - canvas_height(gui->canvas) - 2, - canvas_width(gui->canvas) - 2, - canvas_height(gui->canvas) - 2); - } -} - -bool gui_redraw_window(Gui* gui) { - canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal); - canvas_frame_set(gui->canvas, GUI_WINDOW_X, GUI_WINDOW_Y, GUI_WINDOW_WIDTH, GUI_WINDOW_HEIGHT); - ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]); - if(view_port) { - view_port_draw(view_port, gui->canvas); - return true; - } - return false; -} - -bool gui_redraw_desktop(Gui* gui) { - canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal); - canvas_frame_set(gui->canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT); - ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]); - if(view_port) { - view_port_draw(view_port, gui->canvas); - return true; - } - - return false; -} - -void gui_redraw(Gui* gui) { - furi_assert(gui); - gui_lock(gui); - - canvas_reset(gui->canvas); - - if(gui->lockdown) { - gui_redraw_desktop(gui); - bool need_attention = - (gui_view_port_find_enabled(gui->layers[GuiLayerWindow]) != 0 || - gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]) != 0); - gui_redraw_status_bar(gui, need_attention); - } else { - if(!gui_redraw_fs(gui)) { - if(!gui_redraw_window(gui)) { - gui_redraw_desktop(gui); - } - gui_redraw_status_bar(gui, false); - } - } - - canvas_commit(gui->canvas); - for - M_EACH(p, gui->canvas_callback_pair, CanvasCallbackPairArray_t) { - p->callback( - canvas_get_buffer(gui->canvas), canvas_get_buffer_size(gui->canvas), p->context); - } - gui_unlock(gui); -} - -void gui_input(Gui* gui, InputEvent* input_event) { - furi_assert(gui); - furi_assert(input_event); - - // Check input complementarity - uint8_t key_bit = (1 << input_event->key); - if(input_event->type == InputTypeRelease) { - gui->ongoing_input &= ~key_bit; - } else if(input_event->type == InputTypePress) { - gui->ongoing_input |= key_bit; - } else if(!(gui->ongoing_input & key_bit)) { - FURI_LOG_D( - TAG, - "non-complementary input, discarding key: %s type: %s, sequence: %p", - input_get_key_name(input_event->key), - input_get_type_name(input_event->type), - input_event->sequence); - return; - } - - gui_lock(gui); - - ViewPort* view_port = NULL; - - if(gui->lockdown) { - view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]); - } else { - view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]); - if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]); - if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]); - } - - if(!(gui->ongoing_input & ~key_bit) && input_event->type == InputTypePress) { - gui->ongoing_input_view_port = view_port; - } - - if(view_port && view_port == gui->ongoing_input_view_port) { - view_port_input(view_port, input_event); - } else if(gui->ongoing_input_view_port && input_event->type == InputTypeRelease) { - FURI_LOG_D( - TAG, - "ViewPort changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port", - gui->ongoing_input_view_port, - view_port, - input_get_key_name(input_event->key), - input_get_type_name(input_event->type), - input_event->sequence); - view_port_input(gui->ongoing_input_view_port, input_event); - } else { - FURI_LOG_D( - TAG, - "ViewPort changed while key press %p -> %p. Discarding key: %s, type: %s, sequence: %p", - gui->ongoing_input_view_port, - view_port, - input_get_key_name(input_event->key), - input_get_type_name(input_event->type), - input_event->sequence); - } - - gui_unlock(gui); -} - -void gui_lock(Gui* gui) { - furi_assert(gui); - furi_check(furi_mutex_acquire(gui->mutex, FuriWaitForever) == FuriStatusOk); -} - -void gui_unlock(Gui* gui) { - furi_assert(gui); - furi_check(furi_mutex_release(gui->mutex) == FuriStatusOk); -} - -void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer) { - furi_assert(gui); - furi_assert(view_port); - furi_check(layer < GuiLayerMAX); - // Only fullscreen supports Vertical orientation for now - furi_assert( - (layer == GuiLayerFullscreen) || (view_port->orientation != ViewPortOrientationVertical)); - - gui_lock(gui); - // Verify that view port is not yet added - ViewPortArray_it_t it; - for(size_t i = 0; i < GuiLayerMAX; i++) { - ViewPortArray_it(it, gui->layers[i]); - while(!ViewPortArray_end_p(it)) { - furi_assert(*ViewPortArray_ref(it) != view_port); - ViewPortArray_next(it); - } - } - // Add view port and link with gui - ViewPortArray_push_back(gui->layers[layer], view_port); - view_port_gui_set(view_port, gui); - gui_unlock(gui); - - // Request redraw - gui_update(gui); -} - -void gui_remove_view_port(Gui* gui, ViewPort* view_port) { - furi_assert(gui); - furi_assert(view_port); - - gui_lock(gui); - view_port_gui_set(view_port, NULL); - ViewPortArray_it_t it; - for(size_t i = 0; i < GuiLayerMAX; i++) { - ViewPortArray_it(it, gui->layers[i]); - while(!ViewPortArray_end_p(it)) { - if(*ViewPortArray_ref(it) == view_port) { - ViewPortArray_remove(gui->layers[i], it); - } else { - ViewPortArray_next(it); - } - } - } - if(gui->ongoing_input_view_port == view_port) { - gui->ongoing_input_view_port = NULL; - } - gui_unlock(gui); - - // Request redraw - gui_update(gui); -} - -void gui_view_port_send_to_front(Gui* gui, ViewPort* view_port) { - furi_assert(gui); - furi_assert(view_port); - - gui_lock(gui); - // Remove - GuiLayer layer = GuiLayerMAX; - ViewPortArray_it_t it; - for(size_t i = 0; i < GuiLayerMAX; i++) { - ViewPortArray_it(it, gui->layers[i]); - while(!ViewPortArray_end_p(it)) { - if(*ViewPortArray_ref(it) == view_port) { - ViewPortArray_remove(gui->layers[i], it); - furi_assert(layer == GuiLayerMAX); - layer = i; - } else { - ViewPortArray_next(it); - } - } - } - furi_assert(layer != GuiLayerMAX); - // Return to the top - ViewPortArray_push_back(gui->layers[layer], view_port); - gui_unlock(gui); - - // Request redraw - gui_update(gui); -} - -void gui_view_port_send_to_back(Gui* gui, ViewPort* view_port) { - furi_assert(gui); - furi_assert(view_port); - - gui_lock(gui); - // Remove - GuiLayer layer = GuiLayerMAX; - ViewPortArray_it_t it; - for(size_t i = 0; i < GuiLayerMAX; i++) { - ViewPortArray_it(it, gui->layers[i]); - while(!ViewPortArray_end_p(it)) { - if(*ViewPortArray_ref(it) == view_port) { - ViewPortArray_remove(gui->layers[i], it); - furi_assert(layer == GuiLayerMAX); - layer = i; - } else { - ViewPortArray_next(it); - } - } - } - furi_assert(layer != GuiLayerMAX); - // Return to the top - ViewPortArray_push_at(gui->layers[layer], 0, view_port); - gui_unlock(gui); - - // Request redraw - gui_update(gui); -} - -void gui_add_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, void* context) { - furi_assert(gui); - - const CanvasCallbackPair p = {callback, context}; - - gui_lock(gui); - furi_assert(CanvasCallbackPairArray_count(gui->canvas_callback_pair, p) == 0); - CanvasCallbackPairArray_push_back(gui->canvas_callback_pair, p); - gui_unlock(gui); - - // Request redraw - gui_update(gui); -} - -void gui_remove_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, void* context) { - furi_assert(gui); - - const CanvasCallbackPair p = {callback, context}; - - gui_lock(gui); - furi_assert(CanvasCallbackPairArray_count(gui->canvas_callback_pair, p) == 1); - CanvasCallbackPairArray_remove_val(gui->canvas_callback_pair, p); - gui_unlock(gui); -} - -size_t gui_get_framebuffer_size(Gui* gui) { - furi_assert(gui); - return canvas_get_buffer_size(gui->canvas); -} - -void gui_set_lockdown(Gui* gui, bool lockdown) { - furi_assert(gui); - - gui_lock(gui); - gui->lockdown = lockdown; - gui_unlock(gui); - - // Request redraw - gui_update(gui); -} - -Gui* gui_alloc() { - Gui* gui = malloc(sizeof(Gui)); - // Thread ID - gui->thread_id = furi_thread_get_current_id(); - // Allocate mutex - gui->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - furi_check(gui->mutex); - // Layers - for(size_t i = 0; i < GuiLayerMAX; i++) { - ViewPortArray_init(gui->layers[i]); - } - // Drawing canvas - gui->canvas = canvas_init(); - CanvasCallbackPairArray_init(gui->canvas_callback_pair); - - // Input - gui->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); - gui->input_events = furi_record_open(RECORD_INPUT_EVENTS); - - furi_check(gui->input_events); - furi_pubsub_subscribe(gui->input_events, gui_input_events_callback, gui); - - return gui; -} - -int32_t gui_srv(void* p) { - UNUSED(p); - Gui* gui = gui_alloc(); - - furi_record_create(RECORD_GUI, gui); - - while(1) { - uint32_t flags = - furi_thread_flags_wait(GUI_THREAD_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever); - // Process and dispatch input - if(flags & GUI_THREAD_FLAG_INPUT) { - // Process till queue become empty - InputEvent input_event; - while(furi_message_queue_get(gui->input_queue, &input_event, 0) == FuriStatusOk) { - gui_input(gui, &input_event); - } - } - // Process and dispatch draw call - if(flags & GUI_THREAD_FLAG_DRAW) { - // Clear flags that arrived on input step - furi_thread_flags_clear(GUI_THREAD_FLAG_DRAW); - gui_redraw(gui); - } - } - - return 0; -} diff --git a/applications/gui/gui_i.h b/applications/gui/gui_i.h deleted file mode 100644 index b3fd0aa7c61..00000000000 --- a/applications/gui/gui_i.h +++ /dev/null @@ -1,88 +0,0 @@ -/** - * @file gui_i.h - * GUI: main API internals - */ - -#pragma once - -#include "gui.h" - -#include -#include -#include -#include - -#include "canvas.h" -#include "canvas_i.h" -#include "view_port.h" -#include "view_port_i.h" - -#define GUI_DISPLAY_WIDTH 128 -#define GUI_DISPLAY_HEIGHT 64 - -#define GUI_STATUS_BAR_X 0 -#define GUI_STATUS_BAR_Y 0 -#define GUI_STATUS_BAR_WIDTH GUI_DISPLAY_WIDTH -/* 0-1 pixels for upper thin frame - * 2-9 pixels for icons (battery, sd card, etc) - * 10-12 pixels for lower bold line */ -#define GUI_STATUS_BAR_HEIGHT 13 -/* icon itself area (battery, sd card, etc) excluding frame. - * painted 2 pixels below GUI_STATUS_BAR_X. - */ -#define GUI_STATUS_BAR_WORKAREA_HEIGHT 8 - -#define GUI_WINDOW_X 0 -#define GUI_WINDOW_Y GUI_STATUS_BAR_HEIGHT -#define GUI_WINDOW_WIDTH GUI_DISPLAY_WIDTH -#define GUI_WINDOW_HEIGHT (GUI_DISPLAY_HEIGHT - GUI_WINDOW_Y) - -#define GUI_THREAD_FLAG_DRAW (1 << 0) -#define GUI_THREAD_FLAG_INPUT (1 << 1) -#define GUI_THREAD_FLAG_ALL (GUI_THREAD_FLAG_DRAW | GUI_THREAD_FLAG_INPUT) - -ARRAY_DEF(ViewPortArray, ViewPort*, M_PTR_OPLIST); - -typedef struct { - GuiCanvasCommitCallback callback; - void* context; -} CanvasCallbackPair; - -ARRAY_DEF(CanvasCallbackPairArray, CanvasCallbackPair, M_POD_OPLIST); - -#define M_OPL_CanvasCallbackPairArray_t() ARRAY_OPLIST(CanvasCallbackPairArray, M_POD_OPLIST) - -ALGO_DEF(CanvasCallbackPairArray, CanvasCallbackPairArray_t); - -/** Gui structure */ -struct Gui { - // Thread and lock - FuriThreadId thread_id; - FuriMutex* mutex; - - // Layers and Canvas - bool lockdown; - ViewPortArray_t layers[GuiLayerMAX]; - Canvas* canvas; - CanvasCallbackPairArray_t canvas_callback_pair; - - // Input - FuriMessageQueue* input_queue; - FuriPubSub* input_events; - uint8_t ongoing_input; - ViewPort* ongoing_input_view_port; -}; - -ViewPort* gui_view_port_find_enabled(ViewPortArray_t array); - -/** Update GUI, request redraw - * - * @param gui Gui instance - */ -void gui_update(Gui* gui); - -void gui_input_events_callback(const void* value, void* ctx); - -void gui_lock(Gui* gui); - -void gui_unlock(Gui* gui); diff --git a/applications/gui/modules/button_menu.c b/applications/gui/modules/button_menu.c deleted file mode 100644 index 84fea7888b4..00000000000 --- a/applications/gui/modules/button_menu.c +++ /dev/null @@ -1,343 +0,0 @@ -#include "button_menu.h" -#include "gui/canvas.h" -#include "gui/elements.h" -#include "input/input.h" -#include -#include -#include - -#define ITEM_FIRST_OFFSET 17 -#define ITEM_NEXT_OFFSET 4 -#define ITEM_HEIGHT 14 -#define ITEM_WIDTH 64 -#define BUTTONS_PER_SCREEN 6 - -struct ButtonMenuItem { - const char* label; - int32_t index; - ButtonMenuItemCallback callback; - ButtonMenuItemType type; - void* callback_context; -}; - -ARRAY_DEF(ButtonMenuItemArray, ButtonMenuItem, M_POD_OPLIST); - -struct ButtonMenu { - View* view; - bool freeze_input; -}; - -typedef struct { - ButtonMenuItemArray_t items; - uint8_t position; - const char* header; -} ButtonMenuModel; - -static void button_menu_draw_control_button( - Canvas* canvas, - uint8_t item_position, - const char* text, - bool selected) { - furi_assert(canvas); - furi_assert(text); - - uint8_t item_x = 0; - uint8_t item_y = ITEM_FIRST_OFFSET + (item_position * (ITEM_HEIGHT + ITEM_NEXT_OFFSET)); - - canvas_set_color(canvas, ColorBlack); - - if(selected) { - elements_slightly_rounded_box(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_str_aligned( - canvas, - item_x + (ITEM_WIDTH / 2), - item_y + (ITEM_HEIGHT / 2), - AlignCenter, - AlignCenter, - text); -} - -static void button_menu_draw_common_button( - Canvas* canvas, - uint8_t item_position, - const char* text, - bool selected) { - furi_assert(canvas); - furi_assert(text); - - uint8_t item_x = 0; - uint8_t item_y = ITEM_FIRST_OFFSET + (item_position * (ITEM_HEIGHT + ITEM_NEXT_OFFSET)); - - canvas_set_color(canvas, ColorBlack); - - if(selected) { - canvas_draw_rbox(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT, 5); - canvas_set_color(canvas, ColorWhite); - } else { - canvas_draw_rframe(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT, 5); - } - - string_t disp_str; - string_init_set_str(disp_str, text); - elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 6); - - canvas_draw_str_aligned( - canvas, - item_x + (ITEM_WIDTH / 2), - item_y + (ITEM_HEIGHT / 2), - AlignCenter, - AlignCenter, - string_get_cstr(disp_str)); - - string_clear(disp_str); -} - -static void button_menu_view_draw_callback(Canvas* canvas, void* _model) { - furi_assert(canvas); - furi_assert(_model); - - ButtonMenuModel* model = (ButtonMenuModel*)_model; - canvas_set_font(canvas, FontSecondary); - - uint8_t item_position = 0; - int8_t active_screen = model->position / BUTTONS_PER_SCREEN; - size_t items_size = ButtonMenuItemArray_size(model->items); - int8_t max_screen = ((int16_t)items_size - 1) / BUTTONS_PER_SCREEN; - ButtonMenuItemArray_it_t it; - - if(active_screen > 0) { - canvas_draw_icon(canvas, 28, 1, &I_InfraredArrowUp_4x8); - } - - if(max_screen > active_screen) { - canvas_draw_icon(canvas, 28, 123, &I_InfraredArrowDown_4x8); - } - - if(model->header) { - string_t disp_str; - string_init_set_str(disp_str, model->header); - elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 6); - canvas_draw_str_aligned( - canvas, 32, 10, AlignCenter, AlignCenter, string_get_cstr(disp_str)); - string_clear(disp_str); - } - - for(ButtonMenuItemArray_it(it, model->items); !ButtonMenuItemArray_end_p(it); - ButtonMenuItemArray_next(it), ++item_position) { - if(active_screen == (item_position / BUTTONS_PER_SCREEN)) { - if(ButtonMenuItemArray_cref(it)->type == ButtonMenuItemTypeControl) { - button_menu_draw_control_button( - canvas, - item_position % BUTTONS_PER_SCREEN, - ButtonMenuItemArray_cref(it)->label, - (item_position == model->position)); - } else if(ButtonMenuItemArray_cref(it)->type == ButtonMenuItemTypeCommon) { - button_menu_draw_common_button( - canvas, - item_position % BUTTONS_PER_SCREEN, - ButtonMenuItemArray_cref(it)->label, - (item_position == model->position)); - } - } - } -} - -static void button_menu_process_up(ButtonMenu* button_menu) { - furi_assert(button_menu); - - with_view_model( - button_menu->view, (ButtonMenuModel * model) { - if(model->position > 0) { - model->position--; - } else { - model->position = ButtonMenuItemArray_size(model->items) - 1; - } - return true; - }); -} - -static void button_menu_process_down(ButtonMenu* button_menu) { - furi_assert(button_menu); - - with_view_model( - button_menu->view, (ButtonMenuModel * model) { - if(model->position < (ButtonMenuItemArray_size(model->items) - 1)) { - model->position++; - } else { - model->position = 0; - } - return true; - }); -} - -static void button_menu_process_ok(ButtonMenu* button_menu, InputType type) { - furi_assert(button_menu); - - ButtonMenuItem* item = NULL; - - with_view_model( - button_menu->view, (ButtonMenuModel * model) { - if(model->position < (ButtonMenuItemArray_size(model->items))) { - item = ButtonMenuItemArray_get(model->items, model->position); - } - return false; - }); - - if(item) { - if(item->type == ButtonMenuItemTypeControl) { - if(type == InputTypeShort) { - if(item && item->callback) { - item->callback(item->callback_context, item->index, type); - } - } - } - if(item->type == ButtonMenuItemTypeCommon) { - if((type == InputTypePress) || (type == InputTypeRelease)) { - if(item && item->callback) { - item->callback(item->callback_context, item->index, type); - } - } - } - } -} - -static bool button_menu_view_input_callback(InputEvent* event, void* context) { - furi_assert(event); - - ButtonMenu* button_menu = context; - bool consumed = false; - - if(event->key == InputKeyOk) { - if((event->type == InputTypeRelease) || (event->type == InputTypePress)) { - consumed = true; - button_menu->freeze_input = (event->type == InputTypePress); - button_menu_process_ok(button_menu, event->type); - } else if(event->type == InputTypeShort) { - consumed = true; - button_menu_process_ok(button_menu, event->type); - } - } - - if(!button_menu->freeze_input && - ((event->type == InputTypeRepeat) || (event->type == InputTypeShort))) { - switch(event->key) { - case InputKeyUp: - consumed = true; - button_menu_process_up(button_menu); - break; - case InputKeyDown: - consumed = true; - button_menu_process_down(button_menu); - break; - default: - break; - } - } - - return consumed; -} - -View* button_menu_get_view(ButtonMenu* button_menu) { - furi_assert(button_menu); - return button_menu->view; -} - -void button_menu_reset(ButtonMenu* button_menu) { - furi_assert(button_menu); - - with_view_model( - button_menu->view, (ButtonMenuModel * model) { - ButtonMenuItemArray_reset(model->items); - model->position = 0; - model->header = NULL; - return true; - }); -} - -void button_menu_set_header(ButtonMenu* button_menu, const char* header) { - furi_assert(button_menu); - - with_view_model( - button_menu->view, (ButtonMenuModel * model) { - model->header = header; - return true; - }); -} - -ButtonMenuItem* button_menu_add_item( - ButtonMenu* button_menu, - const char* label, - int32_t index, - ButtonMenuItemCallback callback, - ButtonMenuItemType type, - void* callback_context) { - ButtonMenuItem* item = NULL; - furi_assert(label); - furi_assert(button_menu); - - with_view_model( - button_menu->view, (ButtonMenuModel * model) { - item = ButtonMenuItemArray_push_new(model->items); - item->label = label; - item->index = index; - item->type = type; - item->callback = callback; - item->callback_context = callback_context; - return true; - }); - - return item; -} - -ButtonMenu* button_menu_alloc(void) { - ButtonMenu* button_menu = malloc(sizeof(ButtonMenu)); - button_menu->view = view_alloc(); - view_set_orientation(button_menu->view, ViewOrientationVertical); - view_set_context(button_menu->view, button_menu); - view_allocate_model(button_menu->view, ViewModelTypeLocking, sizeof(ButtonMenuModel)); - view_set_draw_callback(button_menu->view, button_menu_view_draw_callback); - view_set_input_callback(button_menu->view, button_menu_view_input_callback); - - with_view_model( - button_menu->view, (ButtonMenuModel * model) { - ButtonMenuItemArray_init(model->items); - model->position = 0; - model->header = NULL; - return true; - }); - - button_menu->freeze_input = false; - return button_menu; -} - -void button_menu_free(ButtonMenu* button_menu) { - furi_assert(button_menu); - - with_view_model( - button_menu->view, (ButtonMenuModel * model) { - ButtonMenuItemArray_clear(model->items); - return true; - }); - view_free(button_menu->view); - free(button_menu); -} - -void button_menu_set_selected_item(ButtonMenu* button_menu, uint32_t index) { - furi_assert(button_menu); - - with_view_model( - button_menu->view, (ButtonMenuModel * model) { - uint8_t item_position = 0; - ButtonMenuItemArray_it_t it; - for(ButtonMenuItemArray_it(it, model->items); !ButtonMenuItemArray_end_p(it); - ButtonMenuItemArray_next(it), ++item_position) { - if((uint32_t)ButtonMenuItemArray_cref(it)->index == index) { - model->position = item_position; - break; - } - } - return true; - }); -} diff --git a/applications/gui/modules/button_panel.c b/applications/gui/modules/button_panel.c deleted file mode 100644 index e3ae59a3671..00000000000 --- a/applications/gui/modules/button_panel.c +++ /dev/null @@ -1,383 +0,0 @@ -#include "button_panel.h" -#include "furi_hal_resources.h" -#include "gui/canvas.h" -#include -#include -#include -#include -#include -#include - -typedef struct { - // uint16_t to support multi-screen, wide button panel - uint16_t x; - uint16_t y; - Font font; - const char* str; -} LabelElement; - -LIST_DEF(LabelList, LabelElement, M_POD_OPLIST) -#define M_OPL_LabelList_t() LIST_OPLIST(LabelList) - -typedef struct { - uint16_t x; - uint16_t y; - const Icon* name; - const Icon* name_selected; -} IconElement; - -typedef struct ButtonItem { - uint32_t index; - ButtonItemCallback callback; - IconElement icon; - void* callback_context; -} ButtonItem; - -ARRAY_DEF(ButtonArray, ButtonItem*, M_PTR_OPLIST); -#define M_OPL_ButtonArray_t() ARRAY_OPLIST(ButtonArray, M_PTR_OPLIST) -ARRAY_DEF(ButtonMatrix, ButtonArray_t); -#define M_OPL_ButtonMatrix_t() ARRAY_OPLIST(ButtonMatrix, M_OPL_ButtonArray_t()) - -struct ButtonPanel { - View* view; -}; - -typedef struct { - ButtonMatrix_t button_matrix; - LabelList_t labels; - uint16_t reserve_x; - uint16_t reserve_y; - uint16_t selected_item_x; - uint16_t selected_item_y; -} ButtonPanelModel; - -static ButtonItem** button_panel_get_item(ButtonPanelModel* model, size_t x, size_t y); -static void button_panel_process_up(ButtonPanel* button_panel); -static void button_panel_process_down(ButtonPanel* button_panel); -static void button_panel_process_left(ButtonPanel* button_panel); -static void button_panel_process_right(ButtonPanel* button_panel); -static void button_panel_process_ok(ButtonPanel* button_panel); -static void button_panel_view_draw_callback(Canvas* canvas, void* _model); -static bool button_panel_view_input_callback(InputEvent* event, void* context); - -ButtonPanel* button_panel_alloc() { - ButtonPanel* button_panel = malloc(sizeof(ButtonPanel)); - button_panel->view = view_alloc(); - view_set_orientation(button_panel->view, ViewOrientationVertical); - view_set_context(button_panel->view, button_panel); - view_allocate_model(button_panel->view, ViewModelTypeLocking, sizeof(ButtonPanelModel)); - view_set_draw_callback(button_panel->view, button_panel_view_draw_callback); - view_set_input_callback(button_panel->view, button_panel_view_input_callback); - - with_view_model( - button_panel->view, (ButtonPanelModel * model) { - model->reserve_x = 0; - model->reserve_y = 0; - model->selected_item_x = 0; - model->selected_item_y = 0; - ButtonMatrix_init(model->button_matrix); - LabelList_init(model->labels); - return true; - }); - - return button_panel; -} - -void button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t reserve_y) { - furi_check(reserve_x > 0); - furi_check(reserve_y > 0); - - with_view_model( - button_panel->view, (ButtonPanelModel * model) { - model->reserve_x = reserve_x; - model->reserve_y = reserve_y; - ButtonMatrix_reserve(model->button_matrix, model->reserve_y); - for(size_t i = 0; i > model->reserve_y; ++i) { - ButtonArray_t* array = ButtonMatrix_get(model->button_matrix, i); - ButtonArray_init(*array); - ButtonArray_reserve(*array, reserve_x); - // TODO: do we need to clear allocated memory of ptr-s to ButtonItem ?? - } - LabelList_init(model->labels); - return true; - }); -} - -void button_panel_free(ButtonPanel* button_panel) { - furi_assert(button_panel); - - button_panel_reset(button_panel); - - with_view_model( - button_panel->view, (ButtonPanelModel * model) { - LabelList_clear(model->labels); - ButtonMatrix_clear(model->button_matrix); - return true; - }); - - view_free(button_panel->view); - free(button_panel); -} - -void button_panel_reset(ButtonPanel* button_panel) { - furi_assert(button_panel); - - with_view_model( - button_panel->view, (ButtonPanelModel * model) { - for(size_t x = 0; x < model->reserve_x; ++x) { - for(size_t y = 0; y < model->reserve_y; ++y) { - ButtonItem** button_item = button_panel_get_item(model, x, y); - free(*button_item); - *button_item = NULL; - } - } - model->reserve_x = 0; - model->reserve_y = 0; - LabelList_reset(model->labels); - ButtonMatrix_reset(model->button_matrix); - return true; - }); -} - -static ButtonItem** button_panel_get_item(ButtonPanelModel* model, size_t x, size_t y) { - furi_assert(model); - - furi_check(x < model->reserve_x); - furi_check(y < model->reserve_y); - ButtonArray_t* button_array = ButtonMatrix_safe_get(model->button_matrix, x); - ButtonItem** button_item = ButtonArray_safe_get(*button_array, y); - return button_item; -} - -void button_panel_add_item( - ButtonPanel* button_panel, - uint32_t index, - uint16_t matrix_place_x, - uint16_t matrix_place_y, - uint16_t x, - uint16_t y, - const Icon* icon_name, - const Icon* icon_name_selected, - ButtonItemCallback callback, - void* callback_context) { - furi_assert(button_panel); - - with_view_model( - button_panel->view, (ButtonPanelModel * model) { - ButtonItem** button_item_ptr = - button_panel_get_item(model, matrix_place_x, matrix_place_y); - furi_check(*button_item_ptr == NULL); - *button_item_ptr = malloc(sizeof(ButtonItem)); - ButtonItem* button_item = *button_item_ptr; - button_item->callback = callback; - button_item->callback_context = callback_context; - button_item->icon.x = x; - button_item->icon.y = y; - button_item->icon.name = icon_name; - button_item->icon.name_selected = icon_name_selected; - button_item->index = index; - return true; - }); -} - -View* button_panel_get_view(ButtonPanel* button_panel) { - furi_assert(button_panel); - return button_panel->view; -} - -static void button_panel_view_draw_callback(Canvas* canvas, void* _model) { - furi_assert(canvas); - furi_assert(_model); - - ButtonPanelModel* model = _model; - - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - for(size_t x = 0; x < model->reserve_x; ++x) { - for(size_t y = 0; y < model->reserve_y; ++y) { - ButtonItem* button_item = *button_panel_get_item(model, x, y); - const Icon* icon_name = button_item->icon.name; - if((model->selected_item_x == x) && (model->selected_item_y == y)) { - icon_name = button_item->icon.name_selected; - } - canvas_draw_icon(canvas, button_item->icon.x, button_item->icon.y, icon_name); - } - } - - for - M_EACH(label, model->labels, LabelList_t) { - canvas_set_font(canvas, label->font); - canvas_draw_str(canvas, label->x, label->y, label->str); - } -} - -static void button_panel_process_down(ButtonPanel* button_panel) { - with_view_model( - button_panel->view, (ButtonPanelModel * model) { - uint16_t new_selected_item_x = model->selected_item_x; - uint16_t new_selected_item_y = model->selected_item_y; - size_t i; - - if(new_selected_item_y >= (model->reserve_y - 1)) return false; - - ++new_selected_item_y; - - for(i = 0; i < model->reserve_x; ++i) { - new_selected_item_x = (model->selected_item_x + i) % model->reserve_x; - if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) { - break; - } - } - if(i == model->reserve_x) return false; - - model->selected_item_x = new_selected_item_x; - model->selected_item_y = new_selected_item_y; - - return true; - }); -} - -static void button_panel_process_up(ButtonPanel* button_panel) { - with_view_model( - button_panel->view, (ButtonPanelModel * model) { - size_t new_selected_item_x = model->selected_item_x; - size_t new_selected_item_y = model->selected_item_y; - size_t i; - - if(new_selected_item_y <= 0) return false; - - --new_selected_item_y; - - for(i = 0; i < model->reserve_x; ++i) { - new_selected_item_x = (model->selected_item_x + i) % model->reserve_x; - if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) { - break; - } - } - if(i == model->reserve_x) return false; - - model->selected_item_x = new_selected_item_x; - model->selected_item_y = new_selected_item_y; - return true; - }); -} - -static void button_panel_process_left(ButtonPanel* button_panel) { - with_view_model( - button_panel->view, (ButtonPanelModel * model) { - size_t new_selected_item_x = model->selected_item_x; - size_t new_selected_item_y = model->selected_item_y; - size_t i; - - if(new_selected_item_x <= 0) return false; - - --new_selected_item_x; - - for(i = 0; i < model->reserve_y; ++i) { - new_selected_item_y = (model->selected_item_y + i) % model->reserve_y; - if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) { - break; - } - } - if(i == model->reserve_y) return false; - - model->selected_item_x = new_selected_item_x; - model->selected_item_y = new_selected_item_y; - return true; - }); -} - -static void button_panel_process_right(ButtonPanel* button_panel) { - with_view_model( - button_panel->view, (ButtonPanelModel * model) { - uint16_t new_selected_item_x = model->selected_item_x; - uint16_t new_selected_item_y = model->selected_item_y; - size_t i; - - if(new_selected_item_x >= (model->reserve_x - 1)) return false; - - ++new_selected_item_x; - - for(i = 0; i < model->reserve_y; ++i) { - new_selected_item_y = (model->selected_item_y + i) % model->reserve_y; - if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) { - break; - } - } - if(i == model->reserve_y) return false; - - model->selected_item_x = new_selected_item_x; - model->selected_item_y = new_selected_item_y; - return true; - }); -} - -void button_panel_process_ok(ButtonPanel* button_panel) { - ButtonItem* button_item = NULL; - - with_view_model( - button_panel->view, (ButtonPanelModel * model) { - button_item = - *button_panel_get_item(model, model->selected_item_x, model->selected_item_y); - return true; - }); - - if(button_item && button_item->callback) { - button_item->callback(button_item->callback_context, button_item->index); - } -} - -static bool button_panel_view_input_callback(InputEvent* event, void* context) { - ButtonPanel* button_panel = context; - furi_assert(button_panel); - bool consumed = false; - - if(event->type == InputTypeShort) { - switch(event->key) { - case InputKeyUp: - consumed = true; - button_panel_process_up(button_panel); - break; - case InputKeyDown: - consumed = true; - button_panel_process_down(button_panel); - break; - case InputKeyLeft: - consumed = true; - button_panel_process_left(button_panel); - break; - case InputKeyRight: - consumed = true; - button_panel_process_right(button_panel); - break; - case InputKeyOk: - consumed = true; - button_panel_process_ok(button_panel); - break; - default: - break; - } - } - - return consumed; -} - -void button_panel_add_label( - ButtonPanel* button_panel, - uint16_t x, - uint16_t y, - Font font, - const char* label_str) { - furi_assert(button_panel); - - with_view_model( - button_panel->view, (ButtonPanelModel * model) { - LabelElement* label = LabelList_push_raw(model->labels); - label->x = x; - label->y = y; - label->font = font; - label->str = label_str; - return true; - }); -} diff --git a/applications/gui/modules/byte_input.c b/applications/gui/modules/byte_input.c deleted file mode 100644 index bb18a107ac6..00000000000 --- a/applications/gui/modules/byte_input.c +++ /dev/null @@ -1,781 +0,0 @@ -#include "byte_input.h" -#include -#include - -struct ByteInput { - View* view; -}; - -typedef struct { - const uint8_t value; - const uint8_t x; - const uint8_t y; -} ByteInputKey; - -typedef struct { - const char* header; - uint8_t* bytes; - uint8_t bytes_count; - - ByteInputCallback input_callback; - ByteChangedCallback changed_callback; - void* callback_context; - - bool selected_high_nibble; - uint8_t selected_byte; - int8_t selected_row; // row -1 - input, row 0 & 1 - keyboard - uint8_t selected_column; - uint8_t first_visible_byte; -} ByteInputModel; - -static const uint8_t keyboard_origin_x = 7; -static const uint8_t keyboard_origin_y = 31; -static const uint8_t keyboard_row_count = 2; -static const uint8_t enter_symbol = '\r'; -static const uint8_t backspace_symbol = '\b'; -static const uint8_t max_drawable_bytes = 8; - -static const ByteInputKey keyboard_keys_row_1[] = { - {'0', 0, 12}, - {'1', 11, 12}, - {'2', 22, 12}, - {'3', 33, 12}, - {'4', 44, 12}, - {'5', 55, 12}, - {'6', 66, 12}, - {'7', 77, 12}, - {backspace_symbol, 103, 4}, -}; - -static const ByteInputKey keyboard_keys_row_2[] = { - {'8', 0, 26}, - {'9', 11, 26}, - {'A', 22, 26}, - {'B', 33, 26}, - {'C', 44, 26}, - {'D', 55, 26}, - {'E', 66, 26}, - {'F', 77, 26}, - {enter_symbol, 95, 17}, -}; - -/** - * @brief Get row size - * - * @param row_index Index of row - * @return uint8_t Row size - */ -static uint8_t byte_input_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(ByteInputKey); - break; - case 2: - row_size = sizeof(keyboard_keys_row_2) / sizeof(ByteInputKey); - break; - } - - return row_size; -} - -/** - * @brief Get row pointer - * - * @param row_index Index of row - * @return const ByteInputKey* Row pointer - */ -static const ByteInputKey* byte_input_get_row(uint8_t row_index) { - const ByteInputKey* row = NULL; - - switch(row_index + 1) { - case 1: - row = keyboard_keys_row_1; - break; - case 2: - row = keyboard_keys_row_2; - break; - } - - return row; -} - -/** - * @brief Get text from nibble - * - * @param byte byte value - * @param high_nibble Get from high nibble, otherwise low nibble - * @return char nibble text - */ -static char byte_input_get_nibble_text(uint8_t byte, bool high_nibble) { - if(high_nibble) { - byte = byte >> 4; - } - byte = byte & 0x0F; - - switch(byte & 0x0F) { - case 0x0: - case 0x1: - case 0x2: - case 0x3: - case 0x4: - case 0x5: - case 0x6: - case 0x7: - case 0x8: - case 0x9: - byte = byte + '0'; - break; - case 0xA: - case 0xB: - case 0xC: - case 0xD: - case 0xE: - case 0xF: - byte = byte - 0xA + 'A'; - break; - default: - byte = '!'; - break; - } - - return byte; -} - -/** - * @brief Draw input box (common view) - * - * @param canvas - * @param model - */ -static void byte_input_draw_input(Canvas* canvas, ByteInputModel* model) { - const uint8_t text_x = 8; - const uint8_t text_y = 25; - - elements_slightly_rounded_frame(canvas, 6, 14, 116, 15); - - canvas_draw_icon(canvas, 2, 19, &I_ButtonLeftSmall_3x5); - canvas_draw_icon(canvas, 123, 19, &I_ButtonRightSmall_3x5); - - for(uint8_t i = model->first_visible_byte; - i < model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes); - i++) { - uint8_t byte_position = i - model->first_visible_byte; - - if(i == model->selected_byte) { - canvas_draw_frame(canvas, text_x + byte_position * 14, text_y - 9, 15, 11); - - if(model->selected_high_nibble) { - canvas_draw_glyph( - canvas, - text_x + 8 + byte_position * 14, - text_y, - byte_input_get_nibble_text(model->bytes[i], false)); - canvas_draw_box(canvas, text_x + 1 + byte_position * 14, text_y - 8, 7, 9); - canvas_invert_color(canvas); - canvas_draw_line( - canvas, - text_x + 14 + byte_position * 14, - text_y - 6, - text_x + 14 + byte_position * 14, - text_y - 2); - canvas_draw_glyph( - canvas, - text_x + 2 + byte_position * 14, - text_y, - byte_input_get_nibble_text(model->bytes[i], true)); - canvas_invert_color(canvas); - } else { - canvas_draw_box(canvas, text_x + 7 + byte_position * 14, text_y - 8, 7, 9); - canvas_draw_glyph( - canvas, - text_x + 2 + byte_position * 14, - text_y, - byte_input_get_nibble_text(model->bytes[i], true)); - canvas_invert_color(canvas); - canvas_draw_line( - canvas, - text_x + byte_position * 14, - text_y - 6, - text_x + byte_position * 14, - text_y - 2); - canvas_draw_glyph( - canvas, - text_x + 8 + byte_position * 14, - text_y, - byte_input_get_nibble_text(model->bytes[i], false)); - canvas_invert_color(canvas); - } - } else { - canvas_draw_glyph( - canvas, - text_x + 2 + byte_position * 14, - text_y, - byte_input_get_nibble_text(model->bytes[i], true)); - canvas_draw_glyph( - canvas, - text_x + 8 + byte_position * 14, - text_y, - byte_input_get_nibble_text(model->bytes[i], false)); - } - } - - if(model->bytes_count - model->first_visible_byte > max_drawable_bytes) { - canvas_draw_icon(canvas, 123, 21, &I_ButtonRightSmall_3x5); - } - - if(model->first_visible_byte > 0) { - canvas_draw_icon(canvas, 1, 21, &I_ButtonLeftSmall_3x5); - } -} - -/** - * @brief Draw input box (selected view) - * - * @param canvas - * @param model - */ -static void byte_input_draw_input_selected(Canvas* canvas, ByteInputModel* model) { - const uint8_t text_x = 7; - const uint8_t text_y = 25; - - canvas_draw_box(canvas, 0, 12, 127, 19); - canvas_invert_color(canvas); - - elements_slightly_rounded_frame(canvas, 6, 14, 115, 15); - canvas_draw_icon(canvas, 2, 19, &I_ButtonLeftSmall_3x5); - canvas_draw_icon(canvas, 122, 19, &I_ButtonRightSmall_3x5); - - for(uint8_t i = model->first_visible_byte; - i < model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes); - i++) { - uint8_t byte_position = i - model->first_visible_byte; - - if(i == model->selected_byte) { - canvas_draw_box(canvas, text_x + 1 + byte_position * 14, text_y - 9, 13, 11); - canvas_invert_color(canvas); - canvas_draw_glyph( - canvas, - text_x + 2 + byte_position * 14, - text_y, - byte_input_get_nibble_text(model->bytes[i], true)); - canvas_draw_glyph( - canvas, - text_x + 8 + byte_position * 14, - text_y, - byte_input_get_nibble_text(model->bytes[i], false)); - canvas_invert_color(canvas); - } else { - canvas_draw_glyph( - canvas, - text_x + 2 + byte_position * 14, - text_y, - byte_input_get_nibble_text(model->bytes[i], true)); - canvas_draw_glyph( - canvas, - text_x + 8 + byte_position * 14, - text_y, - byte_input_get_nibble_text(model->bytes[i], false)); - } - } - - if(model->bytes_count - model->first_visible_byte > max_drawable_bytes) { - canvas_draw_icon(canvas, 123, 21, &I_ButtonRightSmall_3x5); - } - - if(model->first_visible_byte > 0) { - canvas_draw_icon(canvas, 1, 21, &I_ButtonLeftSmall_3x5); - } - - canvas_invert_color(canvas); -} - -/** - * @brief Set nibble at position - * - * @param data where to set nibble - * @param position byte position - * @param value char value - * @param high_nibble set high nibble - */ -static void byte_input_set_nibble(uint8_t* data, uint8_t position, char value, bool high_nibble) { - switch(value) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - value = value - '0'; - break; - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - value = value - 'A' + 10; - break; - default: - value = 0; - break; - } - - if(high_nibble) { - data[position] &= 0x0F; - data[position] |= value << 4; - } else { - data[position] &= 0xF0; - data[position] |= value; - } -} - -/** - * @brief What currently selected - * - * @return true - keyboard selected, false - input selected - */ -static bool byte_input_keyboard_selected(ByteInputModel* model) { - return model->selected_row >= 0; -} - -/** - * @brief Do transition from keyboard - * - * @param model - */ -static void byte_input_transition_from_keyboard(ByteInputModel* model) { - model->selected_row += 1; - model->selected_high_nibble = true; -} - -/** - * @brief Increase selected byte position - * - * @param model - */ -static void byte_input_inc_selected_byte(ByteInputModel* model) { - if(model->selected_byte < model->bytes_count - 1) { - model->selected_byte += 1; - - if(model->bytes_count > max_drawable_bytes) { - if(model->selected_byte - model->first_visible_byte > (max_drawable_bytes - 2)) { - if(model->first_visible_byte < model->bytes_count - max_drawable_bytes) { - model->first_visible_byte++; - } - } - } - } -} - -/** - * @brief Decrease selected byte position - * - * @param model - */ -static void byte_input_dec_selected_byte(ByteInputModel* model) { - if(model->selected_byte > 0) { - model->selected_byte -= 1; - - if(model->selected_byte - model->first_visible_byte < 1) { - if(model->first_visible_byte > 0) { - model->first_visible_byte--; - } - } - } -} - -/** - * @brief Call input callback - * - * @param model - */ -static void byte_input_call_input_callback(ByteInputModel* model) { - if(model->input_callback != NULL) { - model->input_callback(model->callback_context); - } -} - -/** - * @brief Call changed callback - * - * @param model - */ -static void byte_input_call_changed_callback(ByteInputModel* model) { - if(model->changed_callback != NULL) { - model->changed_callback(model->callback_context); - } -} - -/** - * @brief Clear selected byte - */ - -static void byte_input_clear_selected_byte(ByteInputModel* model) { - model->bytes[model->selected_byte] = 0; - model->selected_high_nibble = true; - byte_input_dec_selected_byte(model); - byte_input_call_changed_callback(model); -} - -/** - * @brief Handle up button - * - * @param model - */ -static void byte_input_handle_up(ByteInputModel* model) { - if(model->selected_row > -1) { - model->selected_row -= 1; - } -} - -/** - * @brief Handle down button - * - * @param model - */ -static void byte_input_handle_down(ByteInputModel* model) { - if(byte_input_keyboard_selected(model)) { - if(model->selected_row < keyboard_row_count - 1) { - model->selected_row += 1; - } - } else { - byte_input_transition_from_keyboard(model); - } -} - -/** - * @brief Handle left button - * - * @param model - */ -static void byte_input_handle_left(ByteInputModel* model) { - if(byte_input_keyboard_selected(model)) { - if(model->selected_column > 0) { - model->selected_column -= 1; - } else { - model->selected_column = byte_input_get_row_size(model->selected_row) - 1; - } - } else { - byte_input_dec_selected_byte(model); - } -} - -/** - * @brief Handle right button - * - * @param model - */ -static void byte_input_handle_right(ByteInputModel* model) { - if(byte_input_keyboard_selected(model)) { - if(model->selected_column < byte_input_get_row_size(model->selected_row) - 1) { - model->selected_column += 1; - } else { - model->selected_column = 0; - } - } else { - byte_input_inc_selected_byte(model); - } -} - -/** - * @brief Handle OK button - * - * @param model - */ -static void byte_input_handle_ok(ByteInputModel* model) { - if(byte_input_keyboard_selected(model)) { - uint8_t value = byte_input_get_row(model->selected_row)[model->selected_column].value; - - if(value == enter_symbol) { - byte_input_call_input_callback(model); - } else if(value == backspace_symbol) { - byte_input_clear_selected_byte(model); - } else { - byte_input_set_nibble( - model->bytes, model->selected_byte, value, model->selected_high_nibble); - if(model->selected_high_nibble == true) { - model->selected_high_nibble = false; - } else { - byte_input_inc_selected_byte(model); - model->selected_high_nibble = true; - } - byte_input_call_changed_callback(model); - } - } else { - byte_input_transition_from_keyboard(model); - } -} - -/** - * @brief Draw callback - * - * @param canvas - * @param _model - */ -static void byte_input_view_draw_callback(Canvas* canvas, void* _model) { - ByteInputModel* model = _model; - - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - canvas_draw_str(canvas, 2, 9, model->header); - - canvas_set_font(canvas, FontKeyboard); - - if(model->selected_row == -1) { - byte_input_draw_input_selected(canvas, model); - } else { - byte_input_draw_input(canvas, model); - } - - for(uint8_t row = 0; row < keyboard_row_count; row++) { - const uint8_t column_count = byte_input_get_row_size(row); - const ByteInputKey* keys = byte_input_get_row(row); - - for(size_t column = 0; column < column_count; column++) { - if(keys[column].value == enter_symbol) { - 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].value == backspace_symbol) { - 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 - 3, - keyboard_origin_y + keys[column].y - 10, - 11, - 13); - canvas_set_color(canvas, ColorWhite); - } else if(model->selected_row == -1 && row == 0 && model->selected_column == column) { - canvas_set_color(canvas, ColorBlack); - canvas_draw_frame( - canvas, - keyboard_origin_x + keys[column].x - 3, - keyboard_origin_y + keys[column].y - 10, - 11, - 13); - } else { - canvas_set_color(canvas, ColorBlack); - } - - canvas_draw_glyph( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, - keys[column].value); - } - } - } -} - -/** - * @brief Input callback - * - * @param event - * @param context - * @return true - * @return false - */ -static bool byte_input_view_input_callback(InputEvent* event, void* context) { - ByteInput* byte_input = context; - furi_assert(byte_input); - bool consumed = false; - - if(event->type == InputTypeShort || event->type == InputTypeRepeat) { - switch(event->key) { - case InputKeyLeft: - with_view_model( - byte_input->view, (ByteInputModel * model) { - byte_input_handle_left(model); - return true; - }); - consumed = true; - break; - case InputKeyRight: - with_view_model( - byte_input->view, (ByteInputModel * model) { - byte_input_handle_right(model); - return true; - }); - consumed = true; - break; - case InputKeyUp: - with_view_model( - byte_input->view, (ByteInputModel * model) { - byte_input_handle_up(model); - return true; - }); - consumed = true; - break; - case InputKeyDown: - with_view_model( - byte_input->view, (ByteInputModel * model) { - byte_input_handle_down(model); - return true; - }); - consumed = true; - break; - case InputKeyOk: - with_view_model( - byte_input->view, (ByteInputModel * model) { - byte_input_handle_ok(model); - return true; - }); - consumed = true; - break; - default: - break; - } - } - - if((event->type == InputTypeLong || event->type == InputTypeRepeat) && - event->key == InputKeyBack) { - with_view_model( - byte_input->view, (ByteInputModel * model) { - byte_input_clear_selected_byte(model); - return true; - }); - consumed = true; - } - - return consumed; -} - -/** - * @brief Reset all input-related data in model - * - * @param model ByteInputModel - */ -static void byte_input_reset_model_input_data(ByteInputModel* model) { - model->bytes = NULL; - model->bytes_count = 0; - model->selected_high_nibble = true; - model->selected_byte = 0; - model->selected_row = 0; - model->selected_column = 0; - model->first_visible_byte = 0; -} - -/** - * @brief Allocate and initialize byte input. This byte input is used to enter bytes. - * - * @return ByteInput instance pointer - */ -ByteInput* byte_input_alloc() { - ByteInput* byte_input = malloc(sizeof(ByteInput)); - byte_input->view = view_alloc(); - view_set_context(byte_input->view, byte_input); - view_allocate_model(byte_input->view, ViewModelTypeLocking, sizeof(ByteInputModel)); - view_set_draw_callback(byte_input->view, byte_input_view_draw_callback); - view_set_input_callback(byte_input->view, byte_input_view_input_callback); - - with_view_model( - byte_input->view, (ByteInputModel * model) { - model->header = ""; - model->input_callback = NULL; - model->changed_callback = NULL; - model->callback_context = NULL; - byte_input_reset_model_input_data(model); - return true; - }); - - return byte_input; -} - -/** - * @brief Deinitialize and free byte input - * - * @param byte_input Byte input instance - */ -void byte_input_free(ByteInput* byte_input) { - furi_assert(byte_input); - view_free(byte_input->view); - free(byte_input); -} - -/** - * @brief Get byte input view - * - * @param byte_input byte input instance - * @return View instance that can be used for embedding - */ -View* byte_input_get_view(ByteInput* byte_input) { - furi_assert(byte_input); - return byte_input->view; -} - -/** - * @brief Deinitialize and free byte input - * - * @param byte_input byte input instance - * @param input_callback input callback fn - * @param changed_callback changed callback fn - * @param callback_context callback context - * @param bytes buffer to use - * @param bytes_count buffer length - */ -void byte_input_set_result_callback( - ByteInput* byte_input, - ByteInputCallback input_callback, - ByteChangedCallback changed_callback, - void* callback_context, - uint8_t* bytes, - uint8_t bytes_count) { - with_view_model( - byte_input->view, (ByteInputModel * model) { - byte_input_reset_model_input_data(model); - model->input_callback = input_callback; - model->changed_callback = changed_callback; - model->callback_context = callback_context; - model->bytes = bytes; - model->bytes_count = bytes_count; - return true; - }); -} - -/** - * @brief Set byte input header text - * - * @param byte_input byte input instance - * @param text text to be shown - */ -void byte_input_set_header_text(ByteInput* byte_input, const char* text) { - with_view_model( - byte_input->view, (ByteInputModel * model) { - model->header = text; - return true; - }); -} diff --git a/applications/gui/modules/dialog.c b/applications/gui/modules/dialog.c deleted file mode 100644 index 5d36f93f7a1..00000000000 --- a/applications/gui/modules/dialog.c +++ /dev/null @@ -1,131 +0,0 @@ -#include "dialog.h" -#include -#include - -struct Dialog { - View* view; - void* context; - DialogResultCallback callback; -}; - -typedef struct { - const char* header_text; - const char* text; - const char* left_text; - const char* right_text; -} DialogModel; - -static void dialog_view_draw_callback(Canvas* canvas, void* _model) { - DialogModel* model = _model; - uint8_t canvas_center = canvas_width(canvas) / 2; - - // Prepare canvas - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - // Draw header - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, canvas_center, 17, AlignCenter, AlignBottom, model->header_text); - - // Draw text - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned( - canvas, canvas_center, 32, AlignCenter, AlignCenter, model->text); - - // Draw buttons - elements_button_left(canvas, model->left_text); - elements_button_right(canvas, model->right_text); -} - -static bool dialog_view_input_callback(InputEvent* event, void* context) { - Dialog* dialog = context; - bool consumed = false; - - // Process key presses only - if(event->type == InputTypeShort && dialog->callback) { - if(event->key == InputKeyLeft) { - dialog->callback(DialogResultLeft, dialog->context); - consumed = true; - } else if(event->key == InputKeyRight) { - dialog->callback(DialogResultRight, dialog->context); - consumed = true; - } else if(event->key == InputKeyBack) { - dialog->callback(DialogResultBack, dialog->context); - consumed = true; - } - } - - return consumed; -} - -Dialog* dialog_alloc() { - Dialog* dialog = malloc(sizeof(Dialog)); - dialog->view = view_alloc(); - view_set_context(dialog->view, dialog); - view_allocate_model(dialog->view, ViewModelTypeLockFree, sizeof(DialogModel)); - view_set_draw_callback(dialog->view, dialog_view_draw_callback); - view_set_input_callback(dialog->view, dialog_view_input_callback); - return dialog; -} - -void dialog_free(Dialog* dialog) { - furi_assert(dialog); - view_free(dialog->view); - free(dialog); -} - -View* dialog_get_view(Dialog* dialog) { - furi_assert(dialog); - return dialog->view; -} - -void dialog_set_result_callback(Dialog* dialog, DialogResultCallback callback) { - furi_assert(dialog); - dialog->callback = callback; -} - -void dialog_set_context(Dialog* dialog, void* context) { - furi_assert(dialog); - dialog->context = context; -} - -void dialog_set_header_text(Dialog* dialog, const char* text) { - furi_assert(dialog); - furi_assert(text); - with_view_model( - dialog->view, (DialogModel * model) { - model->header_text = text; - return true; - }); -} - -void dialog_set_text(Dialog* dialog, const char* text) { - furi_assert(dialog); - furi_assert(text); - with_view_model( - dialog->view, (DialogModel * model) { - model->text = text; - return true; - }); -} - -void dialog_set_left_button_text(Dialog* dialog, const char* text) { - furi_assert(dialog); - furi_assert(text); - with_view_model( - dialog->view, (DialogModel * model) { - model->left_text = text; - return true; - }); -} - -void dialog_set_right_button_text(Dialog* dialog, const char* text) { - furi_assert(dialog); - furi_assert(text); - with_view_model( - dialog->view, (DialogModel * model) { - model->right_text = text; - return true; - }); -} diff --git a/applications/gui/modules/dialog.h b/applications/gui/modules/dialog.h deleted file mode 100644 index e9160a351fb..00000000000 --- a/applications/gui/modules/dialog.h +++ /dev/null @@ -1,95 +0,0 @@ -/** - * @file dialog.h - * GUI: Dialog view module API - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Dialog anonymous structure */ -typedef struct Dialog Dialog; - -/** Dialog result */ -typedef enum { - DialogResultLeft, - DialogResultRight, - DialogResultBack, -} DialogResult; - -/** Dialog result callback type - * @warning comes from GUI thread - */ -typedef void (*DialogResultCallback)(DialogResult result, void* context); - -/** Allocate and initialize dialog - * - * This dialog used to ask simple questions like Yes/ - * - * @return Dialog instance - */ -Dialog* dialog_alloc(); - -/** Deinitialize and free dialog - * - * @param dialog Dialog instance - */ -void dialog_free(Dialog* dialog); - -/** Get dialog view - * - * @param dialog Dialog instance - * - * @return View instance that can be used for embedding - */ -View* dialog_get_view(Dialog* dialog); - -/** Set dialog result callback - * - * @param dialog Dialog instance - * @param callback result callback function - */ -void dialog_set_result_callback(Dialog* dialog, DialogResultCallback callback); - -/** Set dialog context - * - * @param dialog Dialog instance - * @param context context pointer, will be passed to result callback - */ -void dialog_set_context(Dialog* dialog, void* context); - -/** Set dialog header text - * - * @param dialog Dialog instance - * @param text text to be shown - */ -void dialog_set_header_text(Dialog* dialog, const char* text); - -/** Set dialog text - * - * @param dialog Dialog instance - * @param text text to be shown - */ -void dialog_set_text(Dialog* dialog, const char* text); - -/** Set left button text - * - * @param dialog Dialog instance - * @param text text to be shown - */ -void dialog_set_left_button_text(Dialog* dialog, const char* text); - -/** Set right button text - * - * @param dialog Dialog instance - * @param text text to be shown - */ -void dialog_set_right_button_text(Dialog* dialog, const char* text); - -#ifdef __cplusplus -} -#endif diff --git a/applications/gui/modules/file_browser.c b/applications/gui/modules/file_browser.c deleted file mode 100644 index a987eb1ef7d..00000000000 --- a/applications/gui/modules/file_browser.c +++ /dev/null @@ -1,526 +0,0 @@ -#include "file_browser.h" -#include "assets_icons.h" -#include "file_browser_worker.h" -#include -#include -#include -#include "furi_hal_resources.h" -#include "m-string.h" -#include -#include -#include -#include "toolbox/path.h" - -#define LIST_ITEMS 5u -#define MAX_LEN_PX 110 -#define FRAME_HEIGHT 12 -#define Y_OFFSET 3 - -#define ITEM_LIST_LEN_MAX 100 - -typedef enum { - BrowserItemTypeLoading, - BrowserItemTypeBack, - BrowserItemTypeFolder, - BrowserItemTypeFile, -} BrowserItemType; - -typedef struct { - string_t path; - BrowserItemType type; -} BrowserItem_t; - -static void BrowserItem_t_init(BrowserItem_t* obj) { - obj->type = BrowserItemTypeLoading; - string_init(obj->path); -} - -static void BrowserItem_t_init_set(BrowserItem_t* obj, const BrowserItem_t* src) { - obj->type = src->type; - string_init_set(obj->path, src->path); -} - -static void BrowserItem_t_set(BrowserItem_t* obj, const BrowserItem_t* src) { - obj->type = src->type; - string_set(obj->path, src->path); -} - -static void BrowserItem_t_clear(BrowserItem_t* obj) { - string_clear(obj->path); -} - -ARRAY_DEF( - items_array, - BrowserItem_t, - (INIT(API_2(BrowserItem_t_init)), - SET(API_6(BrowserItem_t_set)), - INIT_SET(API_6(BrowserItem_t_init_set)), - CLEAR(API_2(BrowserItem_t_clear)))) - -struct FileBrowser { - View* view; - BrowserWorker* worker; - const char* ext_filter; - bool skip_assets; - - FileBrowserCallback callback; - void* context; - - string_ptr result_path; -}; - -typedef struct { - items_array_t items; - - bool is_root; - bool folder_loading; - bool list_loading; - uint32_t item_cnt; - int32_t item_idx; - int32_t array_offset; - int32_t list_offset; - - const Icon* file_icon; - bool hide_ext; -} FileBrowserModel; - -static const Icon* BrowserItemIcons[] = { - [BrowserItemTypeLoading] = &I_loading_10px, - [BrowserItemTypeBack] = &I_back_10px, - [BrowserItemTypeFolder] = &I_dir_10px, - [BrowserItemTypeFile] = &I_unknown_10px, -}; - -static void file_browser_view_draw_callback(Canvas* canvas, void* _model); -static bool file_browser_view_input_callback(InputEvent* event, void* context); - -static void - browser_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root); -static void browser_list_load_cb(void* context, uint32_t list_load_offset); -static void browser_list_item_cb(void* context, string_t item_path, bool is_folder, bool is_last); -static void browser_long_load_cb(void* context); - -FileBrowser* file_browser_alloc(string_ptr result_path) { - furi_assert(result_path); - FileBrowser* browser = malloc(sizeof(FileBrowser)); - browser->view = view_alloc(); - view_allocate_model(browser->view, ViewModelTypeLocking, sizeof(FileBrowserModel)); - view_set_context(browser->view, browser); - view_set_draw_callback(browser->view, file_browser_view_draw_callback); - view_set_input_callback(browser->view, file_browser_view_input_callback); - - browser->result_path = result_path; - - with_view_model( - browser->view, (FileBrowserModel * model) { - items_array_init(model->items); - return false; - }); - - return browser; -} - -void file_browser_free(FileBrowser* browser) { - furi_assert(browser); - - with_view_model( - browser->view, (FileBrowserModel * model) { - items_array_clear(model->items); - return false; - }); - - view_free(browser->view); - free(browser); -} - -View* file_browser_get_view(FileBrowser* browser) { - furi_assert(browser); - return browser->view; -} - -void file_browser_configure( - FileBrowser* browser, - const char* extension, - bool skip_assets, - const Icon* file_icon, - bool hide_ext) { - furi_assert(browser); - - browser->ext_filter = extension; - browser->skip_assets = skip_assets; - - with_view_model( - browser->view, (FileBrowserModel * model) { - model->file_icon = file_icon; - model->hide_ext = hide_ext; - return false; - }); -} - -void file_browser_start(FileBrowser* browser, string_t path) { - furi_assert(browser); - browser->worker = file_browser_worker_alloc(path, browser->ext_filter, browser->skip_assets); - file_browser_worker_set_callback_context(browser->worker, browser); - file_browser_worker_set_folder_callback(browser->worker, browser_folder_open_cb); - file_browser_worker_set_list_callback(browser->worker, browser_list_load_cb); - file_browser_worker_set_item_callback(browser->worker, browser_list_item_cb); - file_browser_worker_set_long_load_callback(browser->worker, browser_long_load_cb); -} - -void file_browser_stop(FileBrowser* browser) { - furi_assert(browser); - file_browser_worker_free(browser->worker); - with_view_model( - browser->view, (FileBrowserModel * model) { - items_array_reset(model->items); - model->item_cnt = 0; - model->item_idx = 0; - model->array_offset = 0; - model->list_offset = 0; - return false; - }); -} - -void file_browser_set_callback(FileBrowser* browser, FileBrowserCallback callback, void* context) { - browser->context = context; - browser->callback = callback; -} - -static bool browser_is_item_in_array(FileBrowserModel* model, uint32_t idx) { - size_t array_size = items_array_size(model->items); - - if((idx >= (uint32_t)model->array_offset + array_size) || - (idx < (uint32_t)model->array_offset)) { - return false; - } - return true; -} - -static bool browser_is_list_load_required(FileBrowserModel* model) { - size_t array_size = items_array_size(model->items); - uint32_t item_cnt = (model->is_root) ? model->item_cnt : model->item_cnt - 1; - - if((model->list_loading) || (array_size >= item_cnt)) { - return false; - } - - if((model->array_offset > 0) && - (model->item_idx < (model->array_offset + ITEM_LIST_LEN_MAX / 4))) { - return true; - } - - if(((model->array_offset + array_size) < item_cnt) && - (model->item_idx > (int32_t)(model->array_offset + array_size - ITEM_LIST_LEN_MAX / 4))) { - return true; - } - - return false; -} - -static void browser_update_offset(FileBrowser* browser) { - furi_assert(browser); - - with_view_model( - browser->view, (FileBrowserModel * model) { - uint16_t bounds = model->item_cnt > (LIST_ITEMS - 1) ? 2 : model->item_cnt; - - if((model->item_cnt > (LIST_ITEMS - 1)) && - (model->item_idx >= ((int32_t)model->item_cnt - 1))) { - model->list_offset = model->item_idx - (LIST_ITEMS - 1); - } else if(model->list_offset < model->item_idx - bounds) { - model->list_offset = CLAMP( - model->item_idx - (int32_t)(LIST_ITEMS - 2), - (int32_t)model->item_cnt - bounds, - 0); - } else if(model->list_offset > model->item_idx - bounds) { - model->list_offset = - CLAMP(model->item_idx - 1, (int32_t)model->item_cnt - bounds, 0); - } - - return false; - }); -} - -static void - browser_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root) { - furi_assert(context); - FileBrowser* browser = (FileBrowser*)context; - - int32_t load_offset = 0; - - with_view_model( - browser->view, (FileBrowserModel * model) { - items_array_reset(model->items); - if(is_root) { - model->item_cnt = item_cnt; - model->item_idx = (file_idx > 0) ? file_idx : 0; - load_offset = - CLAMP(model->item_idx - ITEM_LIST_LEN_MAX / 2, (int32_t)model->item_cnt, 0); - } else { - model->item_cnt = item_cnt + 1; - model->item_idx = file_idx + 1; - load_offset = CLAMP( - model->item_idx - ITEM_LIST_LEN_MAX / 2 - 1, (int32_t)model->item_cnt - 1, 0); - } - model->array_offset = 0; - model->list_offset = 0; - model->is_root = is_root; - model->list_loading = true; - model->folder_loading = false; - return true; - }); - browser_update_offset(browser); - - file_browser_worker_load(browser->worker, load_offset, ITEM_LIST_LEN_MAX); -} - -static void browser_list_load_cb(void* context, uint32_t list_load_offset) { - furi_assert(context); - FileBrowser* browser = (FileBrowser*)context; - - BrowserItem_t back_item; - BrowserItem_t_init(&back_item); - back_item.type = BrowserItemTypeBack; - - with_view_model( - browser->view, (FileBrowserModel * model) { - items_array_reset(model->items); - model->array_offset = list_load_offset; - if(!model->is_root) { - if(list_load_offset == 0) { - items_array_push_back(model->items, back_item); - } else { - model->array_offset += 1; - } - } - return false; - }); - - BrowserItem_t_clear(&back_item); -} - -static void browser_list_item_cb(void* context, string_t item_path, bool is_folder, bool is_last) { - furi_assert(context); - FileBrowser* browser = (FileBrowser*)context; - - BrowserItem_t item; - - if(!is_last) { - BrowserItem_t_init(&item); - string_set(item.path, item_path); - item.type = (is_folder) ? BrowserItemTypeFolder : BrowserItemTypeFile; - - with_view_model( - browser->view, (FileBrowserModel * model) { - items_array_push_back(model->items, item); - return false; - }); - BrowserItem_t_clear(&item); - } else { - with_view_model( - browser->view, (FileBrowserModel * model) { - model->list_loading = false; - return true; - }); - } -} - -static void browser_long_load_cb(void* context) { - furi_assert(context); - FileBrowser* browser = (FileBrowser*)context; - - with_view_model( - browser->view, (FileBrowserModel * model) { - model->folder_loading = true; - return true; - }); -} - -static void browser_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { - canvas_set_color(canvas, ColorBlack); - canvas_draw_box( - canvas, 0, Y_OFFSET + idx * FRAME_HEIGHT, (scrollbar ? 122 : 127), FRAME_HEIGHT); - - canvas_set_color(canvas, ColorWhite); - canvas_draw_dot(canvas, 0, Y_OFFSET + idx * FRAME_HEIGHT); - canvas_draw_dot(canvas, 1, Y_OFFSET + idx * FRAME_HEIGHT); - canvas_draw_dot(canvas, 0, (Y_OFFSET + idx * FRAME_HEIGHT) + 1); - - canvas_draw_dot(canvas, 0, (Y_OFFSET + idx * FRAME_HEIGHT) + (FRAME_HEIGHT - 1)); - canvas_draw_dot(canvas, scrollbar ? 121 : 126, Y_OFFSET + idx * FRAME_HEIGHT); - canvas_draw_dot( - canvas, scrollbar ? 121 : 126, (Y_OFFSET + idx * FRAME_HEIGHT) + (FRAME_HEIGHT - 1)); -} - -static void browser_draw_loading(Canvas* canvas, FileBrowserModel* model) { - UNUSED(model); - - uint8_t x = 128 / 2 - 24 / 2; - uint8_t y = 64 / 2 - 24 / 2; - - canvas_draw_icon(canvas, x, y, &A_Loading_24); -} - -static void browser_draw_list(Canvas* canvas, FileBrowserModel* model) { - uint32_t array_size = items_array_size(model->items); - bool show_scrollbar = model->item_cnt > LIST_ITEMS; - - string_t filename; - string_init(filename); - - for(uint32_t i = 0; i < MIN(model->item_cnt, LIST_ITEMS); i++) { - int32_t idx = CLAMP((uint32_t)(i + model->list_offset), model->item_cnt, 0u); - - BrowserItemType item_type = BrowserItemTypeLoading; - - if(browser_is_item_in_array(model, idx)) { - BrowserItem_t* item = items_array_get( - model->items, CLAMP(idx - model->array_offset, (int32_t)(array_size - 1), 0)); - item_type = item->type; - path_extract_filename( - item->path, filename, (model->hide_ext) && (item_type == BrowserItemTypeFile)); - } else { - string_set_str(filename, "---"); - } - - if(item_type == BrowserItemTypeBack) { - string_set_str(filename, ". ."); - } - - elements_string_fit_width( - canvas, filename, (show_scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX)); - - if(model->item_idx == idx) { - browser_draw_frame(canvas, i, show_scrollbar); - } else { - canvas_set_color(canvas, ColorBlack); - } - - if((item_type == BrowserItemTypeFile) && (model->file_icon)) { - canvas_draw_icon(canvas, 2, Y_OFFSET + 1 + i * FRAME_HEIGHT, model->file_icon); - } else if(BrowserItemIcons[item_type] != NULL) { - canvas_draw_icon( - canvas, 2, Y_OFFSET + 1 + i * FRAME_HEIGHT, BrowserItemIcons[item_type]); - } - canvas_draw_str(canvas, 15, Y_OFFSET + 9 + i * FRAME_HEIGHT, string_get_cstr(filename)); - } - - if(show_scrollbar) { - elements_scrollbar_pos( - canvas, - 126, - Y_OFFSET, - canvas_height(canvas) - Y_OFFSET, - model->item_idx, - model->item_cnt); - } - - string_clear(filename); -} - -static void file_browser_view_draw_callback(Canvas* canvas, void* _model) { - FileBrowserModel* model = _model; - - if(model->folder_loading) { - browser_draw_loading(canvas, model); - } else { - browser_draw_list(canvas, model); - } -} - -static bool file_browser_view_input_callback(InputEvent* event, void* context) { - FileBrowser* browser = context; - furi_assert(browser); - bool consumed = false; - bool is_loading = false; - - with_view_model( - browser->view, (FileBrowserModel * model) { - is_loading = model->folder_loading; - return false; - }); - - if(is_loading) { - return false; - } else if(event->key == InputKeyUp || event->key == InputKeyDown) { - if(event->type == InputTypeShort || event->type == InputTypeRepeat) { - with_view_model( - browser->view, (FileBrowserModel * model) { - if(event->key == InputKeyUp) { - model->item_idx = - ((model->item_idx - 1) + model->item_cnt) % model->item_cnt; - if(browser_is_list_load_required(model)) { - model->list_loading = true; - int32_t load_offset = CLAMP( - model->item_idx - ITEM_LIST_LEN_MAX / 4 * 3, - (int32_t)model->item_cnt - ITEM_LIST_LEN_MAX, - 0); - file_browser_worker_load( - browser->worker, load_offset, ITEM_LIST_LEN_MAX); - } - } else if(event->key == InputKeyDown) { - model->item_idx = (model->item_idx + 1) % model->item_cnt; - if(browser_is_list_load_required(model)) { - model->list_loading = true; - int32_t load_offset = CLAMP( - model->item_idx - ITEM_LIST_LEN_MAX / 4 * 1, - (int32_t)model->item_cnt - ITEM_LIST_LEN_MAX, - 0); - file_browser_worker_load( - browser->worker, load_offset, ITEM_LIST_LEN_MAX); - } - } - return true; - }); - browser_update_offset(browser); - consumed = true; - } - } else if(event->key == InputKeyOk) { - if(event->type == InputTypeShort) { - BrowserItem_t* selected_item = NULL; - int32_t select_index = 0; - with_view_model( - browser->view, (FileBrowserModel * model) { - if(browser_is_item_in_array(model, model->item_idx)) { - selected_item = - items_array_get(model->items, model->item_idx - model->array_offset); - select_index = model->item_idx; - if((!model->is_root) && (select_index > 0)) { - select_index -= 1; - } - } - return false; - }); - - if(selected_item) { - if(selected_item->type == BrowserItemTypeBack) { - file_browser_worker_folder_exit(browser->worker); - } else if(selected_item->type == BrowserItemTypeFolder) { - file_browser_worker_folder_enter( - browser->worker, selected_item->path, select_index); - } else if(selected_item->type == BrowserItemTypeFile) { - string_set(browser->result_path, selected_item->path); - if(browser->callback) { - browser->callback(browser->context); - } - } - } - consumed = true; - } - } else if(event->key == InputKeyLeft) { - if(event->type == InputTypeShort) { - bool is_root = false; - with_view_model( - browser->view, (FileBrowserModel * model) { - is_root = model->is_root; - return false; - }); - if(!is_root) { - file_browser_worker_folder_exit(browser->worker); - } - consumed = true; - } - } - - return consumed; -} diff --git a/applications/gui/modules/file_browser.h b/applications/gui/modules/file_browser.h deleted file mode 100644 index ebc64509afe..00000000000 --- a/applications/gui/modules/file_browser.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * @file file_browser.h - * GUI: FileBrowser view module API - */ - -#pragma once - -#include "m-string.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct FileBrowser FileBrowser; -typedef void (*FileBrowserCallback)(void* context); - -FileBrowser* file_browser_alloc(string_ptr result_path); - -void file_browser_free(FileBrowser* browser); - -View* file_browser_get_view(FileBrowser* browser); - -void file_browser_configure( - FileBrowser* browser, - const char* extension, - bool skip_assets, - const Icon* file_icon, - bool hide_ext); - -void file_browser_start(FileBrowser* browser, string_t path); - -void file_browser_stop(FileBrowser* browser); - -void file_browser_set_callback(FileBrowser* browser, FileBrowserCallback callback, void* context); - -#ifdef __cplusplus -} -#endif diff --git a/applications/gui/modules/file_browser_worker.c b/applications/gui/modules/file_browser_worker.c deleted file mode 100644 index 36df6cc83c7..00000000000 --- a/applications/gui/modules/file_browser_worker.c +++ /dev/null @@ -1,458 +0,0 @@ -#include "file_browser_worker.h" -#include -#include -#include "m-string.h" -#include "storage/filesystem_api_defines.h" -#include -#include -#include -#include -#include -#include "toolbox/path.h" - -#define TAG "BrowserWorker" - -#define ASSETS_DIR "assets" -#define BROWSER_ROOT STORAGE_ANY_PATH_PREFIX -#define FILE_NAME_LEN_MAX 256 -#define LONG_LOAD_THRESHOLD 100 - -typedef enum { - WorkerEvtStop = (1 << 0), - WorkerEvtLoad = (1 << 1), - WorkerEvtFolderEnter = (1 << 2), - WorkerEvtFolderExit = (1 << 3), - WorkerEvtFolderRefresh = (1 << 4), - WorkerEvtConfigChange = (1 << 5), -} WorkerEvtFlags; - -#define WORKER_FLAGS_ALL \ - (WorkerEvtStop | WorkerEvtLoad | WorkerEvtFolderEnter | WorkerEvtFolderExit | \ - WorkerEvtFolderRefresh | WorkerEvtConfigChange) - -ARRAY_DEF(idx_last_array, int32_t) - -struct BrowserWorker { - FuriThread* thread; - - string_t filter_extension; - string_t path_next; - int32_t item_sel_idx; - uint32_t load_offset; - uint32_t load_count; - bool skip_assets; - idx_last_array_t idx_last; - - void* cb_ctx; - BrowserWorkerFolderOpenCallback folder_cb; - BrowserWorkerListLoadCallback list_load_cb; - BrowserWorkerListItemCallback list_item_cb; - BrowserWorkerLongLoadCallback long_load_cb; -}; - -static bool browser_path_is_file(string_t path) { - bool state = false; - FileInfo file_info; - Storage* storage = furi_record_open(RECORD_STORAGE); - if(storage_common_stat(storage, string_get_cstr(path), &file_info) == FSE_OK) { - if((file_info.flags & FSF_DIRECTORY) == 0) { - state = true; - } - } - furi_record_close(RECORD_STORAGE); - return state; -} - -static bool browser_path_trim(string_t path) { - bool is_root = false; - size_t filename_start = string_search_rchar(path, '/'); - string_left(path, filename_start); - if((string_empty_p(path)) || (filename_start == STRING_FAILURE)) { - string_set_str(path, BROWSER_ROOT); - is_root = true; - } - return is_root; -} - -static bool browser_filter_by_name(BrowserWorker* browser, string_t name, bool is_folder) { - if(is_folder) { - // Skip assets folders (if enabled) - if(browser->skip_assets) { - return ((string_cmp_str(name, ASSETS_DIR) == 0) ? (false) : (true)); - } else { - return true; - } - } else { - // Filter files by extension - if((string_empty_p(browser->filter_extension)) || - (string_cmp_str(browser->filter_extension, "*") == 0)) { - return true; - } - if(string_end_with_string_p(name, browser->filter_extension)) { - return true; - } - } - return false; -} - -static bool browser_folder_check_and_switch(string_t path) { - FileInfo file_info; - Storage* storage = furi_record_open(RECORD_STORAGE); - bool is_root = false; - - if(string_search_rchar(path, '/') == 0) { - is_root = true; - } - - while(1) { - // Check if folder is existing and navigate back if not - if(storage_common_stat(storage, string_get_cstr(path), &file_info) == FSE_OK) { - if(file_info.flags & FSF_DIRECTORY) { - break; - } - } - if(is_root) { - break; - } - is_root = browser_path_trim(path); - } - furi_record_close(RECORD_STORAGE); - return is_root; -} - -static bool browser_folder_init( - BrowserWorker* browser, - string_t path, - string_t filename, - uint32_t* item_cnt, - int32_t* file_idx) { - bool state = false; - FileInfo file_info; - uint32_t total_files_cnt = 0; - - Storage* storage = furi_record_open(RECORD_STORAGE); - File* directory = storage_file_alloc(storage); - - char name_temp[FILE_NAME_LEN_MAX]; - string_t name_str; - string_init(name_str); - - *item_cnt = 0; - *file_idx = -1; - - if(storage_dir_open(directory, string_get_cstr(path))) { - state = true; - while(1) { - if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) { - break; - } - if((storage_file_get_error(directory) == FSE_OK) && (name_temp[0] != '\0')) { - total_files_cnt++; - string_set_str(name_str, name_temp); - if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { - if(!string_empty_p(filename)) { - if(string_cmp(name_str, filename) == 0) { - *file_idx = *item_cnt; - } - } - (*item_cnt)++; - } - if(total_files_cnt == LONG_LOAD_THRESHOLD) { - // There are too many files in folder and counting them will take some time - send callback to app - if(browser->long_load_cb) { - browser->long_load_cb(browser->cb_ctx); - } - } - } - } - } - - string_clear(name_str); - - storage_dir_close(directory); - storage_file_free(directory); - - furi_record_close(RECORD_STORAGE); - - return state; -} - -static bool - browser_folder_load(BrowserWorker* browser, string_t path, uint32_t offset, uint32_t count) { - FileInfo file_info; - - Storage* storage = furi_record_open(RECORD_STORAGE); - File* directory = storage_file_alloc(storage); - - char name_temp[FILE_NAME_LEN_MAX]; - string_t name_str; - string_init(name_str); - - uint32_t items_cnt = 0; - - do { - if(!storage_dir_open(directory, string_get_cstr(path))) { - break; - } - - items_cnt = 0; - while(items_cnt < offset) { - if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) { - break; - } - if(storage_file_get_error(directory) == FSE_OK) { - string_set_str(name_str, name_temp); - if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { - items_cnt++; - } - } else { - break; - } - } - if(items_cnt != offset) { - break; - } - - if(browser->list_load_cb) { - browser->list_load_cb(browser->cb_ctx, offset); - } - - items_cnt = 0; - while(items_cnt < count) { - if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) { - break; - } - if(storage_file_get_error(directory) == FSE_OK) { - string_set_str(name_str, name_temp); - if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { - string_printf(name_str, "%s/%s", string_get_cstr(path), name_temp); - if(browser->list_item_cb) { - browser->list_item_cb( - browser->cb_ctx, name_str, (file_info.flags & FSF_DIRECTORY), false); - } - items_cnt++; - } - } else { - break; - } - } - if(browser->list_item_cb) { - browser->list_item_cb(browser->cb_ctx, NULL, false, true); - } - } while(0); - - string_clear(name_str); - - storage_dir_close(directory); - storage_file_free(directory); - - furi_record_close(RECORD_STORAGE); - - return (items_cnt == count); -} - -static int32_t browser_worker(void* context) { - BrowserWorker* browser = (BrowserWorker*)context; - furi_assert(browser); - FURI_LOG_D(TAG, "Start"); - - uint32_t items_cnt = 0; - string_t path; - string_init_set_str(path, BROWSER_ROOT); - browser->item_sel_idx = -1; - - string_t filename; - string_init(filename); - - furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtConfigChange); - - while(1) { - uint32_t flags = - furi_thread_flags_wait(WORKER_FLAGS_ALL, FuriFlagWaitAny, FuriWaitForever); - furi_assert((flags & FuriFlagError) == 0); - - if(flags & WorkerEvtConfigChange) { - // If start path is a path to the file - try finding index of this file in a folder - if(browser_path_is_file(browser->path_next)) { - path_extract_filename(browser->path_next, filename, false); - } - idx_last_array_reset(browser->idx_last); - - furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderEnter); - } - - if(flags & WorkerEvtFolderEnter) { - string_set(path, browser->path_next); - bool is_root = browser_folder_check_and_switch(path); - - // Push previous selected item index to history array - idx_last_array_push_back(browser->idx_last, browser->item_sel_idx); - - int32_t file_idx = 0; - browser_folder_init(browser, path, filename, &items_cnt, &file_idx); - FURI_LOG_D( - TAG, - "Enter folder: %s items: %u idx: %d", - string_get_cstr(path), - items_cnt, - file_idx); - if(browser->folder_cb) { - browser->folder_cb(browser->cb_ctx, items_cnt, file_idx, is_root); - } - string_reset(filename); - } - - if(flags & WorkerEvtFolderExit) { - browser_path_trim(path); - bool is_root = browser_folder_check_and_switch(path); - - int32_t file_idx = 0; - browser_folder_init(browser, path, filename, &items_cnt, &file_idx); - if(idx_last_array_size(browser->idx_last) > 0) { - // Pop previous selected item index from history array - idx_last_array_pop_back(&file_idx, browser->idx_last); - } - FURI_LOG_D( - TAG, "Exit to: %s items: %u idx: %d", string_get_cstr(path), items_cnt, file_idx); - if(browser->folder_cb) { - browser->folder_cb(browser->cb_ctx, items_cnt, file_idx, is_root); - } - } - - if(flags & WorkerEvtFolderRefresh) { - bool is_root = browser_folder_check_and_switch(path); - - int32_t file_idx = 0; - string_reset(filename); - browser_folder_init(browser, path, filename, &items_cnt, &file_idx); - FURI_LOG_D( - TAG, - "Refresh folder: %s items: %u idx: %d", - string_get_cstr(path), - items_cnt, - browser->item_sel_idx); - if(browser->folder_cb) { - browser->folder_cb(browser->cb_ctx, items_cnt, browser->item_sel_idx, is_root); - } - } - - if(flags & WorkerEvtLoad) { - FURI_LOG_D(TAG, "Load offset: %u cnt: %u", browser->load_offset, browser->load_count); - browser_folder_load(browser, path, browser->load_offset, browser->load_count); - } - - if(flags & WorkerEvtStop) { - break; - } - } - - string_clear(filename); - string_clear(path); - - FURI_LOG_D(TAG, "End"); - return 0; -} - -BrowserWorker* file_browser_worker_alloc(string_t path, const char* filter_ext, bool skip_assets) { - BrowserWorker* browser = malloc(sizeof(BrowserWorker)); - - idx_last_array_init(browser->idx_last); - - string_init_set_str(browser->filter_extension, filter_ext); - browser->skip_assets = skip_assets; - string_init_set(browser->path_next, path); - - browser->thread = furi_thread_alloc(); - furi_thread_set_name(browser->thread, "BrowserWorker"); - furi_thread_set_stack_size(browser->thread, 2048); - furi_thread_set_context(browser->thread, browser); - furi_thread_set_callback(browser->thread, browser_worker); - furi_thread_start(browser->thread); - - return browser; -} - -void file_browser_worker_free(BrowserWorker* browser) { - furi_assert(browser); - - furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtStop); - furi_thread_join(browser->thread); - furi_thread_free(browser->thread); - - string_clear(browser->filter_extension); - string_clear(browser->path_next); - - idx_last_array_clear(browser->idx_last); - - free(browser); -} - -void file_browser_worker_set_callback_context(BrowserWorker* browser, void* context) { - furi_assert(browser); - browser->cb_ctx = context; -} - -void file_browser_worker_set_folder_callback( - BrowserWorker* browser, - BrowserWorkerFolderOpenCallback cb) { - furi_assert(browser); - browser->folder_cb = cb; -} - -void file_browser_worker_set_list_callback( - BrowserWorker* browser, - BrowserWorkerListLoadCallback cb) { - furi_assert(browser); - browser->list_load_cb = cb; -} - -void file_browser_worker_set_item_callback( - BrowserWorker* browser, - BrowserWorkerListItemCallback cb) { - furi_assert(browser); - browser->list_item_cb = cb; -} - -void file_browser_worker_set_long_load_callback( - BrowserWorker* browser, - BrowserWorkerLongLoadCallback cb) { - furi_assert(browser); - browser->long_load_cb = cb; -} - -void file_browser_worker_set_config( - BrowserWorker* browser, - string_t path, - const char* filter_ext, - bool skip_assets) { - furi_assert(browser); - string_set(browser->path_next, path); - string_set_str(browser->filter_extension, filter_ext); - browser->skip_assets = skip_assets; - furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtConfigChange); -} - -void file_browser_worker_folder_enter(BrowserWorker* browser, string_t path, int32_t item_idx) { - furi_assert(browser); - string_set(browser->path_next, path); - browser->item_sel_idx = item_idx; - furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderEnter); -} - -void file_browser_worker_folder_exit(BrowserWorker* browser) { - furi_assert(browser); - furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderExit); -} - -void file_browser_worker_folder_refresh(BrowserWorker* browser, int32_t item_idx) { - furi_assert(browser); - browser->item_sel_idx = item_idx; - furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderRefresh); -} - -void file_browser_worker_load(BrowserWorker* browser, uint32_t offset, uint32_t count) { - furi_assert(browser); - browser->load_offset = offset; - browser->load_count = count; - furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtLoad); -} diff --git a/applications/gui/modules/submenu.c b/applications/gui/modules/submenu.c deleted file mode 100644 index 9fefeca0802..00000000000 --- a/applications/gui/modules/submenu.c +++ /dev/null @@ -1,296 +0,0 @@ -#include "submenu.h" - -#include -#include -#include - -struct Submenu { - View* view; -}; - -typedef struct { - const char* label; - uint32_t index; - SubmenuItemCallback callback; - void* callback_context; -} SubmenuItem; - -ARRAY_DEF(SubmenuItemArray, SubmenuItem, M_POD_OPLIST); - -typedef struct { - SubmenuItemArray_t items; - const char* header; - uint8_t position; - uint8_t window_position; -} SubmenuModel; - -static void submenu_process_up(Submenu* submenu); -static void submenu_process_down(Submenu* submenu); -static void submenu_process_ok(Submenu* submenu); - -static void submenu_view_draw_callback(Canvas* canvas, void* _model) { - SubmenuModel* model = _model; - - const uint8_t item_height = 16; - const uint8_t item_width = 123; - - canvas_clear(canvas); - - uint8_t position = 0; - SubmenuItemArray_it_t it; - - if(model->header) { - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 4, 11, model->header); - } - - canvas_set_font(canvas, FontSecondary); - for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it); - SubmenuItemArray_next(it)) { - uint8_t item_position = position - model->window_position; - uint8_t items_on_screen = model->header ? 3 : 4; - uint8_t y_offset = model->header ? 16 : 0; - - if(item_position < items_on_screen) { - if(position == model->position) { - canvas_set_color(canvas, ColorBlack); - elements_slightly_rounded_box( - canvas, - 0, - y_offset + (item_position * item_height) + 1, - item_width, - item_height - 2); - canvas_set_color(canvas, ColorWhite); - } else { - canvas_set_color(canvas, ColorBlack); - } - - string_t disp_str; - string_init_set_str(disp_str, SubmenuItemArray_cref(it)->label); - elements_string_fit_width(canvas, disp_str, item_width - 20); - - canvas_draw_str( - canvas, - 6, - y_offset + (item_position * item_height) + item_height - 4, - string_get_cstr(disp_str)); - - string_clear(disp_str); - } - - position++; - } - - elements_scrollbar(canvas, model->position, SubmenuItemArray_size(model->items)); -} - -static bool submenu_view_input_callback(InputEvent* event, void* context) { - Submenu* submenu = context; - furi_assert(submenu); - bool consumed = false; - - if(event->type == InputTypeShort) { - switch(event->key) { - case InputKeyUp: - consumed = true; - submenu_process_up(submenu); - break; - case InputKeyDown: - consumed = true; - submenu_process_down(submenu); - break; - case InputKeyOk: - consumed = true; - submenu_process_ok(submenu); - break; - default: - break; - } - } else if(event->type == InputTypeRepeat) { - if(event->key == InputKeyUp) { - consumed = true; - submenu_process_up(submenu); - } else if(event->key == InputKeyDown) { - consumed = true; - submenu_process_down(submenu); - } - } - - return consumed; -} - -Submenu* submenu_alloc() { - Submenu* submenu = malloc(sizeof(Submenu)); - submenu->view = view_alloc(); - view_set_context(submenu->view, submenu); - view_allocate_model(submenu->view, ViewModelTypeLocking, sizeof(SubmenuModel)); - view_set_draw_callback(submenu->view, submenu_view_draw_callback); - view_set_input_callback(submenu->view, submenu_view_input_callback); - - with_view_model( - submenu->view, (SubmenuModel * model) { - SubmenuItemArray_init(model->items); - model->position = 0; - model->window_position = 0; - model->header = NULL; - return true; - }); - - return submenu; -} - -void submenu_free(Submenu* submenu) { - furi_assert(submenu); - - with_view_model( - submenu->view, (SubmenuModel * model) { - SubmenuItemArray_clear(model->items); - return true; - }); - view_free(submenu->view); - free(submenu); -} - -View* submenu_get_view(Submenu* submenu) { - furi_assert(submenu); - return submenu->view; -} - -void submenu_add_item( - Submenu* submenu, - const char* label, - uint32_t index, - SubmenuItemCallback callback, - void* callback_context) { - SubmenuItem* item = NULL; - furi_assert(label); - furi_assert(submenu); - - with_view_model( - submenu->view, (SubmenuModel * model) { - item = SubmenuItemArray_push_new(model->items); - item->label = label; - item->index = index; - item->callback = callback; - item->callback_context = callback_context; - return true; - }); -} - -void submenu_reset(Submenu* submenu) { - furi_assert(submenu); - - with_view_model( - submenu->view, (SubmenuModel * model) { - SubmenuItemArray_reset(model->items); - model->position = 0; - model->window_position = 0; - model->header = NULL; - return true; - }); -} - -void submenu_set_selected_item(Submenu* submenu, uint32_t index) { - with_view_model( - submenu->view, (SubmenuModel * model) { - uint32_t position = 0; - SubmenuItemArray_it_t it; - for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it); - SubmenuItemArray_next(it)) { - if(index == SubmenuItemArray_cref(it)->index) { - break; - } - position++; - } - - if(position >= SubmenuItemArray_size(model->items)) { - position = 0; - } - - model->position = position; - model->window_position = position; - - if(model->window_position > 0) { - model->window_position -= 1; - } - - uint8_t items_on_screen = model->header ? 3 : 4; - - if(SubmenuItemArray_size(model->items) <= items_on_screen) { - model->window_position = 0; - } else { - if(model->window_position >= - (SubmenuItemArray_size(model->items) - items_on_screen)) { - model->window_position = - (SubmenuItemArray_size(model->items) - items_on_screen); - } - } - - return true; - }); -} - -void submenu_process_up(Submenu* submenu) { - with_view_model( - submenu->view, (SubmenuModel * model) { - uint8_t items_on_screen = model->header ? 3 : 4; - if(model->position > 0) { - model->position--; - if(((model->position - model->window_position) < 1) && - model->window_position > 0) { - model->window_position--; - } - } else { - model->position = SubmenuItemArray_size(model->items) - 1; - if(model->position > (items_on_screen - 1)) { - model->window_position = model->position - (items_on_screen - 1); - } - } - return true; - }); -} - -void submenu_process_down(Submenu* submenu) { - with_view_model( - submenu->view, (SubmenuModel * model) { - uint8_t items_on_screen = model->header ? 3 : 4; - if(model->position < (SubmenuItemArray_size(model->items) - 1)) { - model->position++; - if((model->position - model->window_position) > (items_on_screen - 2) && - model->window_position < - (SubmenuItemArray_size(model->items) - items_on_screen)) { - model->window_position++; - } - } else { - model->position = 0; - model->window_position = 0; - } - return true; - }); -} - -void submenu_process_ok(Submenu* submenu) { - SubmenuItem* item = NULL; - - with_view_model( - submenu->view, (SubmenuModel * model) { - if(model->position < (SubmenuItemArray_size(model->items))) { - item = SubmenuItemArray_get(model->items, model->position); - } - return true; - }); - - if(item && item->callback) { - item->callback(item->callback_context, item->index); - } -} - -void submenu_set_header(Submenu* submenu, const char* header) { - furi_assert(submenu); - - with_view_model( - submenu->view, (SubmenuModel * model) { - model->header = header; - return true; - }); -} diff --git a/applications/gui/modules/text_box.c b/applications/gui/modules/text_box.c deleted file mode 100755 index 52307ec25d5..00000000000 --- a/applications/gui/modules/text_box.c +++ /dev/null @@ -1,214 +0,0 @@ -#include "text_box.h" -#include "gui/canvas.h" -#include -#include -#include -#include - -struct TextBox { - View* view; -}; - -typedef struct { - const char* text; - char* text_pos; - string_t text_formatted; - int32_t scroll_pos; - int32_t scroll_num; - TextBoxFont font; - TextBoxFocus focus; - bool formatted; -} TextBoxModel; - -static void text_box_process_down(TextBox* text_box) { - with_view_model( - text_box->view, (TextBoxModel * model) { - if(model->scroll_pos < model->scroll_num - 1) { - model->scroll_pos++; - // Search next line start - while(*model->text_pos++ != '\n') - ; - } - return true; - }); -} - -static void text_box_process_up(TextBox* text_box) { - with_view_model( - text_box->view, (TextBoxModel * model) { - if(model->scroll_pos > 0) { - model->scroll_pos--; - // Reach last symbol of previous line - model->text_pos--; - // Search prevous line start - while((model->text_pos != model->text) && (*(--model->text_pos) != '\n')) - ; - if(*model->text_pos == '\n') { - model->text_pos++; - } - } - return true; - }); -} - -static void text_box_insert_endline(Canvas* canvas, TextBoxModel* model) { - size_t i = 0; - size_t line_width = 0; - const char* str = model->text; - size_t line_num = 0; - - const size_t text_width = 120; - - while(str[i] != '\0') { - char symb = str[i++]; - if(symb != '\n') { - size_t glyph_width = canvas_glyph_width(canvas, symb); - if(line_width + glyph_width > text_width) { - line_num++; - line_width = 0; - string_push_back(model->text_formatted, '\n'); - } - line_width += glyph_width; - } else { - line_num++; - line_width = 0; - } - string_push_back(model->text_formatted, symb); - } - line_num++; - model->text = string_get_cstr(model->text_formatted); - model->text_pos = (char*)model->text; - if(model->focus == TextBoxFocusEnd && line_num > 5) { - // Set text position to 5th line from the end - for(uint8_t i = 0; i < line_num - 5; i++) { - while(*model->text_pos++ != '\n') { - }; - } - model->scroll_num = line_num - 4; - model->scroll_pos = line_num - 5; - } else { - model->scroll_num = MAX(line_num - 4, 0u); - model->scroll_pos = 0; - } -} - -static void text_box_view_draw_callback(Canvas* canvas, void* _model) { - TextBoxModel* model = _model; - - canvas_clear(canvas); - if(model->font == TextBoxFontText) { - canvas_set_font(canvas, FontSecondary); - } else if(model->font == TextBoxFontHex) { - canvas_set_font(canvas, FontKeyboard); - } - - if(!model->formatted) { - text_box_insert_endline(canvas, model); - model->formatted = true; - } - - elements_slightly_rounded_frame(canvas, 0, 0, 124, 64); - elements_multiline_text(canvas, 3, 11, model->text_pos); - elements_scrollbar(canvas, model->scroll_pos, model->scroll_num); -} - -static bool text_box_view_input_callback(InputEvent* event, void* context) { - furi_assert(context); - - TextBox* text_box = context; - bool consumed = false; - if(event->type == InputTypeShort) { - if(event->key == InputKeyDown) { - text_box_process_down(text_box); - consumed = true; - } else if(event->key == InputKeyUp) { - text_box_process_up(text_box); - consumed = true; - } - } - return consumed; -} - -TextBox* text_box_alloc() { - TextBox* text_box = malloc(sizeof(TextBox)); - text_box->view = view_alloc(); - view_set_context(text_box->view, text_box); - view_allocate_model(text_box->view, ViewModelTypeLocking, sizeof(TextBoxModel)); - view_set_draw_callback(text_box->view, text_box_view_draw_callback); - view_set_input_callback(text_box->view, text_box_view_input_callback); - - with_view_model( - text_box->view, (TextBoxModel * model) { - model->text = NULL; - string_init_set_str(model->text_formatted, ""); - model->formatted = false; - model->font = TextBoxFontText; - return true; - }); - - return text_box; -} - -void text_box_free(TextBox* text_box) { - furi_assert(text_box); - - with_view_model( - text_box->view, (TextBoxModel * model) { - string_clear(model->text_formatted); - return true; - }); - view_free(text_box->view); - free(text_box); -} - -View* text_box_get_view(TextBox* text_box) { - furi_assert(text_box); - return text_box->view; -} - -void text_box_reset(TextBox* text_box) { - furi_assert(text_box); - - with_view_model( - text_box->view, (TextBoxModel * model) { - model->text = NULL; - string_set_str(model->text_formatted, ""); - model->font = TextBoxFontText; - model->focus = TextBoxFocusStart; - return true; - }); -} - -void text_box_set_text(TextBox* text_box, const char* text) { - furi_assert(text_box); - furi_assert(text); - - with_view_model( - text_box->view, (TextBoxModel * model) { - model->text = text; - string_reset(model->text_formatted); - string_reserve(model->text_formatted, strlen(text)); - model->formatted = false; - return true; - }); -} - -void text_box_set_font(TextBox* text_box, TextBoxFont font) { - furi_assert(text_box); - - with_view_model( - text_box->view, (TextBoxModel * model) { - model->font = font; - return true; - }); -} - -void text_box_set_focus(TextBox* text_box, TextBoxFocus focus) { - furi_assert(text_box); - - with_view_model( - text_box->view, (TextBoxModel * model) { - model->focus = focus; - return true; - }); -} diff --git a/applications/gui/modules/validators.c b/applications/gui/modules/validators.c deleted file mode 100644 index ac293f8cb1b..00000000000 --- a/applications/gui/modules/validators.c +++ /dev/null @@ -1,55 +0,0 @@ -#include -#include "validators.h" -#include "applications/storage/storage.h" - -struct ValidatorIsFile { - char* app_path_folder; - const char* app_extension; - char* current_name; -}; - -bool validator_is_file_callback(const char* text, string_t 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; - string_t path; - string_init_printf(path, "%s/%s%s", instance->app_path_folder, text, instance->app_extension); - Storage* storage = furi_record_open(RECORD_STORAGE); - if(storage_common_stat(storage, string_get_cstr(path), NULL) == FSE_OK) { - ret = false; - string_printf(error, "This name\nexists!\nChoose\nanother one."); - } else { - ret = true; - } - string_clear(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; - 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/gui/view_port.c b/applications/gui/view_port.c deleted file mode 100644 index 8069a02e5a2..00000000000 --- a/applications/gui/view_port.c +++ /dev/null @@ -1,139 +0,0 @@ -#include "view_port_i.h" - -#include - -#include "gui.h" -#include "gui_i.h" - -// TODO add mutex to view_port ops - -static void view_port_rotate_buttons(InputEvent* event) { - switch(event->key) { - case InputKeyUp: - event->key = InputKeyRight; - break; - case InputKeyDown: - event->key = InputKeyLeft; - break; - case InputKeyRight: - event->key = InputKeyDown; - break; - case InputKeyLeft: - event->key = InputKeyUp; - break; - default: - break; - } -} - -static void view_port_setup_canvas_orientation(const ViewPort* view_port, Canvas* canvas) { - if(view_port->orientation == ViewPortOrientationHorizontal) { - canvas_set_orientation(canvas, CanvasOrientationHorizontal); - } else if(view_port->orientation == ViewPortOrientationVertical) { - canvas_set_orientation(canvas, CanvasOrientationVertical); - } -} - -ViewPort* view_port_alloc() { - ViewPort* view_port = malloc(sizeof(ViewPort)); - view_port->orientation = ViewPortOrientationHorizontal; - view_port->is_enabled = true; - return view_port; -} - -void view_port_free(ViewPort* view_port) { - furi_assert(view_port); - furi_check(view_port->gui == NULL); - free(view_port); -} - -void view_port_set_width(ViewPort* view_port, uint8_t width) { - furi_assert(view_port); - view_port->width = width; -} - -uint8_t view_port_get_width(ViewPort* view_port) { - furi_assert(view_port); - return view_port->width; -} - -void view_port_set_height(ViewPort* view_port, uint8_t height) { - furi_assert(view_port); - view_port->height = height; -} - -uint8_t view_port_get_height(ViewPort* view_port) { - furi_assert(view_port); - return view_port->height; -} - -void view_port_enabled_set(ViewPort* view_port, bool enabled) { - furi_assert(view_port); - if(view_port->is_enabled != enabled) { - view_port->is_enabled = enabled; - if(view_port->gui) gui_update(view_port->gui); - } -} - -bool view_port_is_enabled(ViewPort* view_port) { - furi_assert(view_port); - return view_port->is_enabled; -} - -void view_port_draw_callback_set(ViewPort* view_port, ViewPortDrawCallback callback, void* context) { - furi_assert(view_port); - view_port->draw_callback = callback; - view_port->draw_callback_context = context; -} - -void view_port_input_callback_set( - ViewPort* view_port, - ViewPortInputCallback callback, - void* context) { - furi_assert(view_port); - view_port->input_callback = callback; - view_port->input_callback_context = context; -} - -void view_port_update(ViewPort* view_port) { - furi_assert(view_port); - if(view_port->gui && view_port->is_enabled) gui_update(view_port->gui); -} - -void view_port_gui_set(ViewPort* view_port, Gui* gui) { - furi_assert(view_port); - view_port->gui = gui; -} - -void view_port_draw(ViewPort* view_port, Canvas* canvas) { - furi_assert(view_port); - furi_assert(canvas); - furi_check(view_port->gui); - - if(view_port->draw_callback) { - view_port_setup_canvas_orientation(view_port, canvas); - view_port->draw_callback(canvas, view_port->draw_callback_context); - } -} - -void view_port_input(ViewPort* view_port, InputEvent* event) { - furi_assert(view_port); - furi_assert(event); - furi_check(view_port->gui); - - if(view_port->input_callback) { - if(view_port_get_orientation(view_port) == ViewPortOrientationVertical) { - view_port_rotate_buttons(event); - } - view_port->input_callback(event, view_port->input_callback_context); - } -} - -void view_port_set_orientation(ViewPort* view_port, ViewPortOrientation orientation) { - furi_assert(view_port); - view_port->orientation = orientation; -} - -ViewPortOrientation view_port_get_orientation(const ViewPort* view_port) { - return view_port->orientation; -} diff --git a/applications/ibutton/application.fam b/applications/ibutton/application.fam deleted file mode 100644 index 0bc6f8a9b65..00000000000 --- a/applications/ibutton/application.fam +++ /dev/null @@ -1,23 +0,0 @@ -App( - appid="ibutton", - name="iButton", - apptype=FlipperAppType.APP, - entry_point="ibutton_app", - cdefines=["APP_IBUTTON"], - requires=[ - "gui", - "dialogs", - ], - provides=["ibutton_start"], - icon="A_iButton_14", - stack_size=2 * 1024, - order=60, -) - -App( - appid="ibutton_start", - apptype=FlipperAppType.STARTUP, - entry_point="ibutton_on_system_start", - requires=["ibutton"], - order=60, -) diff --git a/applications/ibutton/ibutton.c b/applications/ibutton/ibutton.c deleted file mode 100644 index 7ee1110e149..00000000000 --- a/applications/ibutton/ibutton.c +++ /dev/null @@ -1,362 +0,0 @@ -#include "ibutton.h" -#include "assets_icons.h" -#include "ibutton_i.h" -#include "ibutton/scenes/ibutton_scene.h" -#include "m-string.h" -#include -#include -#include - -#define TAG "iButtonApp" - -static const NotificationSequence sequence_blink_set_yellow = { - &message_blink_set_color_yellow, - NULL, -}; - -static const NotificationSequence sequence_blink_set_magenta = { - &message_blink_set_color_magenta, - NULL, -}; - -static const NotificationSequence* ibutton_notification_sequences[] = { - &sequence_error, - &sequence_success, - &sequence_blink_start_cyan, - &sequence_blink_start_magenta, - &sequence_blink_set_yellow, - &sequence_blink_set_magenta, - &sequence_set_red_255, - &sequence_reset_red, - &sequence_set_green_255, - &sequence_reset_green, - &sequence_blink_stop, -}; - -static void ibutton_make_app_folder(iButton* ibutton) { - if(!storage_simply_mkdir(ibutton->storage, IBUTTON_APP_FOLDER)) { - dialog_message_show_storage_error(ibutton->dialogs, "Cannot create\napp folder"); - } -} - -bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show_dialog) { - FlipperFormat* file = flipper_format_file_alloc(ibutton->storage); - bool result = false; - string_t data; - string_init(data); - - do { - if(!flipper_format_file_open_existing(file, string_get_cstr(key_path))) break; - - // header - uint32_t version; - if(!flipper_format_read_header(file, data, &version)) break; - if(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(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); - string_clear(data); - - if((!result) && (show_dialog)) { - dialog_message_show_storage_error(ibutton->dialogs, "Cannot load\nkey file"); - } - - return result; -} - -static void ibutton_rpc_command_callback(RpcAppSystemEvent event, void* context) { - furi_assert(context); - iButton* ibutton = 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; - } 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); - } -} - -bool ibutton_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - iButton* ibutton = context; - return scene_manager_handle_custom_event(ibutton->scene_manager, event); -} - -bool ibutton_back_event_callback(void* context) { - furi_assert(context); - iButton* ibutton = context; - return scene_manager_handle_back_event(ibutton->scene_manager); -} - -void ibutton_tick_event_callback(void* context) { - furi_assert(context); - iButton* ibutton = context; - scene_manager_handle_tick_event(ibutton->scene_manager); -} - -iButton* ibutton_alloc() { - iButton* ibutton = malloc(sizeof(iButton)); - - string_init(ibutton->file_path); - - ibutton->scene_manager = scene_manager_alloc(&ibutton_scene_handlers, ibutton); - - ibutton->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_enable_queue(ibutton->view_dispatcher); - view_dispatcher_set_event_callback_context(ibutton->view_dispatcher, ibutton); - view_dispatcher_set_custom_event_callback( - ibutton->view_dispatcher, ibutton_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - ibutton->view_dispatcher, ibutton_back_event_callback); - view_dispatcher_set_tick_event_callback( - ibutton->view_dispatcher, ibutton_tick_event_callback, 100); - - 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->submenu = submenu_alloc(); - view_dispatcher_add_view( - ibutton->view_dispatcher, iButtonViewSubmenu, submenu_get_view(ibutton->submenu)); - - ibutton->byte_input = byte_input_alloc(); - view_dispatcher_add_view( - ibutton->view_dispatcher, iButtonViewByteInput, byte_input_get_view(ibutton->byte_input)); - - ibutton->text_input = text_input_alloc(); - view_dispatcher_add_view( - ibutton->view_dispatcher, iButtonViewTextInput, text_input_get_view(ibutton->text_input)); - - ibutton->popup = popup_alloc(); - view_dispatcher_add_view( - ibutton->view_dispatcher, iButtonViewPopup, popup_get_view(ibutton->popup)); - - ibutton->widget = widget_alloc(); - view_dispatcher_add_view( - ibutton->view_dispatcher, iButtonViewWidget, widget_get_view(ibutton->widget)); - - ibutton->dialog_ex = dialog_ex_alloc(); - view_dispatcher_add_view( - ibutton->view_dispatcher, iButtonViewDialogEx, dialog_ex_get_view(ibutton->dialog_ex)); - - return ibutton; -} - -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, iButtonViewWidget); - widget_free(ibutton->widget); - - view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewPopup); - popup_free(ibutton->popup); - - view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewTextInput); - text_input_free(ibutton->text_input); - - view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewByteInput); - byte_input_free(ibutton->byte_input); - - view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewSubmenu); - submenu_free(ibutton->submenu); - - 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; - - furi_record_close(RECORD_DIALOGS); - ibutton->dialogs = NULL; - - furi_record_close(RECORD_GUI); - ibutton->gui = NULL; - - ibutton_worker_stop_thread(ibutton->key_worker); - ibutton_worker_free(ibutton->key_worker); - ibutton_key_free(ibutton->key); - - string_clear(ibutton->file_path); - - free(ibutton); -} - -bool ibutton_file_select(iButton* ibutton) { - bool success = dialog_file_browser_show( - ibutton->dialogs, - ibutton->file_path, - ibutton->file_path, - IBUTTON_APP_EXTENSION, - true, - &I_ibutt_10px, - true); - - if(success) { - success = ibutton_load_key_data(ibutton, ibutton->file_path, true); - } - - return success; -} - -bool ibutton_save_key(iButton* ibutton, const char* key_name) { - // Create ibutton directory if necessary - ibutton_make_app_folder(ibutton); - - FlipperFormat* file = flipper_format_file_alloc(ibutton->storage); - iButtonKey* key = ibutton->key; - - bool result = false; - - do { - // Check if we has old key - if(string_end_with_str_p(ibutton->file_path, IBUTTON_APP_EXTENSION)) { - // First remove old key - ibutton_delete_key(ibutton); - - // Remove old key name from path - size_t filename_start = string_search_rchar(ibutton->file_path, '/'); - string_left(ibutton->file_path, filename_start); - } - - string_cat_printf(ibutton->file_path, "/%s%s", key_name, IBUTTON_APP_EXTENSION); - - // Open file for write - if(!flipper_format_file_open_always(file, 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) { - dialog_message_show_storage_error(ibutton->dialogs, "Cannot save\nkey file"); - } - - return result; -} - -bool ibutton_delete_key(iButton* ibutton) { - bool result = false; - result = storage_simply_remove(ibutton->storage, string_get_cstr(ibutton->file_path)); - - 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); -} - -void ibutton_notification_message(iButton* ibutton, uint32_t message) { - furi_assert(message < sizeof(ibutton_notification_sequences) / sizeof(NotificationSequence*)); - notification_message(ibutton->notifications, ibutton_notification_sequences[message]); -} - -int32_t ibutton_app(void* p) { - 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) { - 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); - } else { - string_set_str(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 - } - } - } - - if(rpc_mode) { - view_dispatcher_attach_to_gui( - ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeDesktop); - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc); - } else { - view_dispatcher_attach_to_gui( - ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen); - if(key_loaded) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); - } else { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart); - } - } - - 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); - } - ibutton_free(ibutton); - return 0; -} diff --git a/applications/ibutton/ibutton_cli.c b/applications/ibutton/ibutton_cli.c deleted file mode 100644 index d36d3dffd23..00000000000 --- a/applications/ibutton/ibutton_cli.c +++ /dev/null @@ -1,318 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -static void ibutton_cli(Cli* cli, string_t args, void* context); -static void onewire_cli(Cli* cli, string_t args, void* context); - -// app cli function -void ibutton_on_system_start() { -#ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - cli_add_command(cli, "ikey", CliCommandFlagDefault, ibutton_cli, cli); - cli_add_command(cli, "onewire", CliCommandFlagDefault, onewire_cli, cli); - furi_record_close(RECORD_CLI); -#else - UNUSED(ibutton_cli); - UNUSED(onewire_cli); -#endif -} - -void ibutton_cli_print_usage() { - printf("Usage:\r\n"); - printf("ikey read\r\n"); - printf("ikey emulate \r\n"); - printf("ikey write Dallas \r\n"); - printf("\t choose from:\r\n"); - printf("\tDallas (8 bytes key_data)\r\n"); - printf("\tCyfral (2 bytes key_data)\r\n"); - printf("\tMetakom (4 bytes key_data), must contain correct parity\r\n"); - printf("\t are hex-formatted\r\n"); -}; - -bool ibutton_cli_get_key_type(string_t data, iButtonKeyType* type) { - bool result = false; - - if(string_cmp_str(data, "Dallas") == 0 || string_cmp_str(data, "dallas") == 0) { - result = true; - *type = iButtonKeyDS1990; - } else if(string_cmp_str(data, "Cyfral") == 0 || string_cmp_str(data, "cyfral") == 0) { - result = true; - *type = iButtonKeyCyfral; - } else if(string_cmp_str(data, "Metakom") == 0 || string_cmp_str(data, "metakom") == 0) { - result = true; - *type = iButtonKeyMetakom; - } - - 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); - - 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]); - } - - printf("\r\n"); -} - -#define EVENT_FLAG_IBUTTON_COMPLETE (1 << 0) - -static void ibutton_cli_worker_read_cb(void* context) { - furi_assert(context); - FuriEventFlag* event = 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(); - FuriEventFlag* event = furi_event_flag_alloc(); - - ibutton_worker_start_thread(worker); - ibutton_worker_read_set_callback(worker, ibutton_cli_worker_read_cb, event); - - 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"); - } - } - break; - } - - if(cli_cmd_interrupt_received(cli)) break; - } - ibutton_worker_stop(worker); - - ibutton_worker_stop_thread(worker); - ibutton_worker_free(worker); - ibutton_key_free(key); - - furi_event_flag_free(event); -}; - -typedef struct { - FuriEventFlag* event; - iButtonWorkerWriteResult result; -} iButtonWriteContext; - -static void ibutton_cli_worker_write_cb(void* context, iButtonWorkerWriteResult result) { - furi_assert(context); - iButtonWriteContext* write_context = (iButtonWriteContext*)context; - write_context->result = result; - furi_event_flag_set(write_context->event, EVENT_FLAG_IBUTTON_COMPLETE); -} - -void ibutton_cli_write(Cli* cli, string_t args) { - iButtonKey* key = ibutton_key_alloc(); - iButtonWorker* worker = ibutton_worker_alloc(); - iButtonKeyType type; - iButtonWriteContext write_context; - uint8_t key_data[IBUTTON_KEY_DATA_SIZE]; - string_t data; - - write_context.event = furi_event_flag_alloc(); - - string_init(data); - 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)) { - ibutton_cli_print_usage(); - break; - } - - if(!ibutton_cli_get_key_type(data, &type)) { - 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); - printf("Press Ctrl+C to abort\r\n"); - - ibutton_worker_write_start(worker, key); - while(true) { - uint32_t flags = furi_event_flag_wait( - write_context.event, EVENT_FLAG_IBUTTON_COMPLETE, FuriFlagWaitAny, 100); - - if(flags & EVENT_FLAG_IBUTTON_COMPLETE) { - if(write_context.result == iButtonWorkerWriteSameKey || - write_context.result == iButtonWorkerWriteOK) { - printf("Write success\r\n"); - break; - } else if(write_context.result == iButtonWorkerWriteCannotWrite) { - printf("Write fail\r\n"); - break; - } - } - - if(cli_cmd_interrupt_received(cli)) break; - } - ibutton_worker_stop(worker); - } while(false); - - string_clear(data); - ibutton_worker_stop_thread(worker); - ibutton_worker_free(worker); - ibutton_key_free(key); - - furi_event_flag_free(write_context.event); -}; - -void ibutton_cli_emulate(Cli* cli, string_t args) { - iButtonKey* key = ibutton_key_alloc(); - iButtonWorker* worker = ibutton_worker_alloc(); - iButtonKeyType type; - uint8_t key_data[IBUTTON_KEY_DATA_SIZE]; - string_t data; - - string_init(data); - ibutton_worker_start_thread(worker); - - do { - if(!args_read_string_and_trim(args, data)) { - 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); - 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); - - string_clear(data); - ibutton_worker_stop_thread(worker); - ibutton_worker_free(worker); - ibutton_key_free(key); -}; - -static void ibutton_cli(Cli* cli, string_t args, void* context) { - UNUSED(context); - string_t cmd; - string_init(cmd); - - if(!args_read_string_and_trim(args, cmd)) { - string_clear(cmd); - ibutton_cli_print_usage(); - return; - } - - if(string_cmp_str(cmd, "read") == 0) { - ibutton_cli_read(cli); - } else if(string_cmp_str(cmd, "write") == 0) { - ibutton_cli_write(cli, args); - } else if(string_cmp_str(cmd, "emulate") == 0) { - ibutton_cli_emulate(cli, args); - } else { - ibutton_cli_print_usage(); - } - - string_clear(cmd); -} - -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(); - uint8_t address[8]; - bool done = false; - - printf("Search started\r\n"); - - onewire_host_start(onewire); - furi_hal_power_enable_otg(); - - while(!done) { - if(onewire_host_search(onewire, address, NORMAL_SEARCH) != 1) { - printf("Search finished\r\n"); - onewire_host_reset_search(onewire); - done = true; - } else { - printf("Found: "); - for(uint8_t i = 0; i < 8; i++) { - printf("%02X", address[i]); - } - printf("\r\n"); - } - furi_delay_ms(100); - } - - furi_hal_power_disable_otg(); - onewire_host_free(onewire); -} - -void onewire_cli(Cli* cli, string_t args, void* context) { - UNUSED(context); - string_t cmd; - string_init(cmd); - - if(!args_read_string_and_trim(args, cmd)) { - string_clear(cmd); - onewire_cli_print_usage(); - return; - } - - if(string_cmp_str(cmd, "search") == 0) { - onewire_cli_search(cli); - } - - string_clear(cmd); -} diff --git a/applications/ibutton/ibutton_custom_event.h b/applications/ibutton/ibutton_custom_event.h deleted file mode 100644 index 1e2f0300dca..00000000000 --- a/applications/ibutton/ibutton_custom_event.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -enum iButtonCustomEvent { - // Reserve first 100 events for button types and indexes, starting from 0 - iButtonCustomEventReserved = 100, - - iButtonCustomEventBack, - iButtonCustomEventTextEditResult, - iButtonCustomEventByteEditResult, - iButtonCustomEventWorkerEmulated, - iButtonCustomEventWorkerRead, - - iButtonCustomEventRpcLoad, - iButtonCustomEventRpcExit, - iButtonCustomEventRpcSessionClose, -}; diff --git a/applications/ibutton/ibutton_i.h b/applications/ibutton/ibutton_i.h deleted file mode 100644 index fd11b8c198e..00000000000 --- a/applications/ibutton/ibutton_i.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once - -#include "ibutton.h" - -#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" - -struct iButton { - SceneManager* scene_manager; - ViewDispatcher* view_dispatcher; - - Gui* gui; - Storage* storage; - DialogsApp* dialogs; - NotificationApp* notifications; - - iButtonWorker* key_worker; - iButtonKey* key; - - string_t file_path; - char text_store[IBUTTON_TEXT_STORE_SIZE + 1]; - - Submenu* submenu; - ByteInput* byte_input; - TextInput* text_input; - Popup* popup; - Widget* widget; - DialogEx* dialog_ex; - - void* rpc_ctx; -}; - -typedef enum { - iButtonViewSubmenu, - iButtonViewByteInput, - iButtonViewTextInput, - iButtonViewPopup, - iButtonViewWidget, - iButtonViewDialogEx, -} iButtonView; - -typedef enum { - iButtonNotificationMessageError, - iButtonNotificationMessageSuccess, - iButtonNotificationMessageReadStart, - iButtonNotificationMessageEmulateStart, - iButtonNotificationMessageYellowBlink, - iButtonNotificationMessageEmulateBlink, - iButtonNotificationMessageRedOn, - iButtonNotificationMessageRedOff, - iButtonNotificationMessageGreenOn, - iButtonNotificationMessageGreenOff, - iButtonNotificationMessageBlinkStop, -} iButtonNotificationMessage; - -bool ibutton_file_select(iButton* ibutton); -bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show_dialog); -bool ibutton_save_key(iButton* ibutton, const char* key_name); -bool ibutton_delete_key(iButton* ibutton); -void ibutton_text_store_set(iButton* ibutton, const char* text, ...); -void ibutton_text_store_clear(iButton* ibutton); -void ibutton_notification_message(iButton* ibutton, uint32_t message); diff --git a/applications/ibutton/scenes/ibutton_scene_add_type.c b/applications/ibutton/scenes/ibutton_scene_add_type.c deleted file mode 100644 index 9a0583a58b9..00000000000 --- a/applications/ibutton/scenes/ibutton_scene_add_type.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "../ibutton_i.h" -#include "m-string.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); - - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddType)); - - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu); -} - -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"); - } - - string_set_str(ibutton->file_path, IBUTTON_APP_FOLDER); - ibutton_key_clear_data(key); - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddValue); - } - - return consumed; -} - -void ibutton_scene_add_type_on_exit(void* context) { - iButton* ibutton = context; - submenu_reset(ibutton->submenu); -} diff --git a/applications/ibutton/scenes/ibutton_scene_add_value.c b/applications/ibutton/scenes/ibutton_scene_add_value.c deleted file mode 100644 index b3ec11a502a..00000000000 --- a/applications/ibutton/scenes/ibutton_scene_add_value.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "../ibutton_i.h" - -#include - -void ibutton_scene_add_type_byte_input_callback(void* context) { - iButton* ibutton = context; - view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventByteEditResult); -} - -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); - - 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)); - - 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)); - - 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); - 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); - DOLPHIN_DEED(DolphinDeedIbuttonAdd); - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName); - } - } - - return consumed; -} - -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/ibutton/scenes/ibutton_scene_delete_confirm.c b/applications/ibutton/scenes/ibutton_scene_delete_confirm.c deleted file mode 100644 index 51f1f27947b..00000000000 --- a/applications/ibutton/scenes/ibutton_scene_delete_confirm.c +++ /dev/null @@ -1,98 +0,0 @@ -#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); - - string_t key_name; - string_init(key_name); - path_extract_filename(ibutton->file_path, key_name, true); - - ibutton_text_store_set(ibutton, "\e#Delete %s?\e#", string_get_cstr(key_name)); - widget_add_text_box_element( - widget, 0, 0, 128, 27, AlignCenter, AlignCenter, ibutton->text_store, false); - 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); - - 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, 45, 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, 45, 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, 45, AlignCenter, AlignBottom, FontSecondary, "Metakom"); - break; - } - widget_add_string_element( - widget, 64, 33, AlignCenter, AlignBottom, FontSecondary, ibutton->text_store); - - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); - - string_clear(key_name); -} - -bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) { - iButton* ibutton = context; - SceneManager* scene_manager = ibutton->scene_manager; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == GuiButtonTypeRight) { - if(ibutton_delete_key(ibutton)) { - scene_manager_next_scene(scene_manager, iButtonSceneDeleteSuccess); - } - //TODO: What if the key could not be deleted? - } else if(event.event == GuiButtonTypeLeft) { - scene_manager_previous_scene(scene_manager); - } - } - - return consumed; -} - -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/ibutton/scenes/ibutton_scene_emulate.c b/applications/ibutton/scenes/ibutton_scene_emulate.c deleted file mode 100644 index 3420f8471c7..00000000000 --- a/applications/ibutton/scenes/ibutton_scene_emulate.c +++ /dev/null @@ -1,131 +0,0 @@ -#include "../ibutton_i.h" -#include -#include -#include - -#define EMULATE_TIMEOUT_TICKS 10 - -static void ibutton_scene_emulate_callback(void* context, bool emulated) { - iButton* ibutton = context; - if(emulated) { - view_dispatcher_send_custom_event( - ibutton->view_dispatcher, iButtonCustomEventWorkerEmulated); - } -} - -void ibutton_scene_emulate_on_enter(void* context) { - iButton* ibutton = context; - Popup* popup = ibutton->popup; - iButtonKey* key = ibutton->key; - - const uint8_t* key_data = ibutton_key_get_data_p(key); - - string_t key_name; - string_init(key_name); - if(string_end_with_str_p(ibutton->file_path, IBUTTON_APP_EXTENSION)) { - path_extract_filename(ibutton->file_path, key_name, true); - } - - uint8_t line_count = 2; - DOLPHIN_DEED(DolphinDeedIbuttonEmulate); - - // check that stored key has name - if(!string_empty_p(key_name)) { - ibutton_text_store_set(ibutton, "emulating\n%s", string_get_cstr(key_name)); - line_count = 2; - } else { - // if not, show key data - switch(ibutton_key_get_type(key)) { - case iButtonKeyDS1990: - ibutton_text_store_set( - ibutton, - "emulating\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]); - line_count = 3; - break; - case iButtonKeyCyfral: - ibutton_text_store_set(ibutton, "emulating\n%02X %02X", key_data[0], key_data[1]); - line_count = 2; - break; - case iButtonKeyMetakom: - ibutton_text_store_set( - ibutton, - "emulating\n%02X %02X %02X %02X", - key_data[0], - key_data[1], - key_data[2], - key_data[3]); - line_count = 2; - break; - } - } - - switch(line_count) { - case 3: - popup_set_header(popup, "iButton", 82, 18, AlignCenter, AlignBottom); - popup_set_text(popup, ibutton->text_store, 82, 22, AlignCenter, AlignTop); - break; - - default: - popup_set_header(popup, "iButton", 82, 24, AlignCenter, AlignBottom); - popup_set_text(popup, ibutton->text_store, 82, 28, AlignCenter, AlignTop); - break; - } - - popup_set_icon(popup, 2, 10, &I_iButtonKey_49x44); - - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); - - ibutton_worker_emulate_set_callback( - ibutton->key_worker, ibutton_scene_emulate_callback, ibutton); - ibutton_worker_emulate_start(ibutton->key_worker, key); - - string_clear(key_name); - - ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); -} - -bool ibutton_scene_emulate_on_event(void* context, SceneManagerEvent event) { - iButton* ibutton = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeTick) { - uint32_t cnt = scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneEmulate); - if(cnt > 0) { - cnt--; - if(cnt == 0) { - ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateBlink); - } - scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneEmulate, cnt); - } - consumed = true; - } else if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == iButtonCustomEventWorkerEmulated) { - if(scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneEmulate) == 0) { - ibutton_notification_message(ibutton, iButtonNotificationMessageYellowBlink); - } - scene_manager_set_scene_state( - ibutton->scene_manager, iButtonSceneEmulate, EMULATE_TIMEOUT_TICKS); - } - } - - return consumed; -} - -void ibutton_scene_emulate_on_exit(void* context) { - iButton* ibutton = context; - Popup* popup = ibutton->popup; - ibutton_worker_stop(ibutton->key_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); - ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); -} diff --git a/applications/ibutton/scenes/ibutton_scene_info.c b/applications/ibutton/scenes/ibutton_scene_info.c deleted file mode 100644 index bd364ada8d0..00000000000 --- a/applications/ibutton/scenes/ibutton_scene_info.c +++ /dev/null @@ -1,68 +0,0 @@ -#include "../ibutton_i.h" -#include - -void ibutton_scene_info_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); - - string_t key_name; - string_init(key_name); - path_extract_filename(ibutton->file_path, key_name, true); - - ibutton_text_store_set(ibutton, "%s", string_get_cstr(key_name)); - widget_add_text_box_element( - widget, 0, 0, 128, 28, AlignCenter, AlignCenter, ibutton->text_store, false); - - 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, 51, AlignCenter, AlignBottom, FontSecondary, "Dallas"); - 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, 51, AlignCenter, AlignBottom, FontSecondary, "Metakom"); - break; - - case iButtonKeyCyfral: - ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]); - widget_add_string_element( - widget, 64, 51, AlignCenter, AlignBottom, FontSecondary, "Cyfral"); - break; - } - - widget_add_string_element( - widget, 64, 35, AlignCenter, AlignBottom, FontPrimary, ibutton->text_store); - - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); - - string_clear(key_name); -} - -bool ibutton_scene_info_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void ibutton_scene_info_on_exit(void* context) { - iButton* ibutton = context; - ibutton_text_store_clear(ibutton); - widget_reset(ibutton->widget); -} diff --git a/applications/ibutton/scenes/ibutton_scene_read.c b/applications/ibutton/scenes/ibutton_scene_read.c deleted file mode 100644 index 7af351f0624..00000000000 --- a/applications/ibutton/scenes/ibutton_scene_read.c +++ /dev/null @@ -1,75 +0,0 @@ -#include "../ibutton_i.h" -#include - -static void ibutton_scene_read_callback(void* context) { - iButton* ibutton = context; - view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventWorkerRead); -} - -void ibutton_scene_read_on_enter(void* context) { - iButton* ibutton = context; - Popup* popup = ibutton->popup; - iButtonKey* key = ibutton->key; - iButtonWorker* worker = ibutton->key_worker; - DOLPHIN_DEED(DolphinDeedIbuttonRead); - - popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom); - popup_set_text(popup, "Waiting\nfor key ...", 95, 30, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 5, &I_DolphinWait_61x59); - - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); - string_set_str(ibutton->file_path, IBUTTON_APP_FOLDER); - - ibutton_worker_read_set_callback(worker, ibutton_scene_read_callback, ibutton); - ibutton_worker_read_start(worker, key); - - ibutton_notification_message(ibutton, iButtonNotificationMessageReadStart); -} - -bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) { - iButton* ibutton = context; - SceneManager* scene_manager = ibutton->scene_manager; - bool consumed = false; - - if(event.type == SceneManagerEventTypeTick) { - consumed = true; - } 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) { - ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess); - ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOn); - DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess); - scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess); - } - } - } - - return consumed; -} - -void ibutton_scene_read_on_exit(void* context) { - iButton* ibutton = context; - Popup* popup = ibutton->popup; - ibutton_worker_stop(ibutton->key_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); - - ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); -} diff --git a/applications/ibutton/scenes/ibutton_scene_read_crc_error.c b/applications/ibutton/scenes/ibutton_scene_read_crc_error.c deleted file mode 100644 index f822ff6a287..00000000000 --- a/applications/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/ibutton/scenes/ibutton_scene_read_key_menu.c b/applications/ibutton/scenes/ibutton_scene_read_key_menu.c deleted file mode 100644 index 921b24fc18b..00000000000 --- a/applications/ibutton/scenes/ibutton_scene_read_key_menu.c +++ /dev/null @@ -1,63 +0,0 @@ -#include "../ibutton_i.h" - -typedef enum { - SubmenuIndexSave, - SubmenuIndexEmulate, - SubmenuIndexWrite, -} SubmenuIndex; - -void ibutton_scene_read_key_menu_submenu_callback(void* context, uint32_t index) { - iButton* ibutton = context; - view_dispatcher_send_custom_event(ibutton->view_dispatcher, index); -} - -void ibutton_scene_read_key_menu_on_enter(void* context) { - iButton* ibutton = context; - Submenu* submenu = ibutton->submenu; - - submenu_add_item( - submenu, "Save", SubmenuIndexSave, ibutton_scene_read_key_menu_submenu_callback, ibutton); - submenu_add_item( - submenu, - "Emulate", - SubmenuIndexEmulate, - ibutton_scene_read_key_menu_submenu_callback, - ibutton); - if(ibutton_key_get_type(ibutton->key) == iButtonKeyDS1990) { - submenu_add_item( - submenu, - "Write", - SubmenuIndexWrite, - 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; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state( - ibutton->scene_manager, iButtonSceneReadKeyMenu, event.event); - consumed = true; - if(event.event == SubmenuIndexSave) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName); - } else if(event.event == SubmenuIndexEmulate) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); - } else if(event.event == SubmenuIndexWrite) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite); - } - } - - return consumed; -} - -void ibutton_scene_read_key_menu_on_exit(void* context) { - iButton* ibutton = context; - submenu_reset(ibutton->submenu); -} diff --git a/applications/ibutton/scenes/ibutton_scene_read_not_key_error.c b/applications/ibutton/scenes/ibutton_scene_read_not_key_error.c deleted file mode 100644 index 8a7528031c6..00000000000 --- a/applications/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/ibutton/scenes/ibutton_scene_read_success.c b/applications/ibutton/scenes/ibutton_scene_read_success.c deleted file mode 100644 index 1c2bcdd292f..00000000000 --- a/applications/ibutton/scenes/ibutton_scene_read_success.c +++ /dev/null @@ -1,82 +0,0 @@ -#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); -} - -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); - - 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; - } - - 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); - - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx); -} - -bool ibutton_scene_read_success_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_next_scene(scene_manager, iButtonSceneRetryConfirm); - } - } - - return consumed; -} - -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); - - ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOff); -} diff --git a/applications/ibutton/scenes/ibutton_scene_rpc.c b/applications/ibutton/scenes/ibutton_scene_rpc.c deleted file mode 100644 index 0755c8ff81d..00000000000 --- a/applications/ibutton/scenes/ibutton_scene_rpc.c +++ /dev/null @@ -1,81 +0,0 @@ -#include "../ibutton_i.h" -#include -#include - -void ibutton_scene_rpc_on_enter(void* context) { - iButton* ibutton = context; - Popup* popup = ibutton->popup; - - popup_set_header(popup, "iButton", 82, 28, AlignCenter, AlignBottom); - popup_set_text(popup, "RPC mode", 82, 32, AlignCenter, AlignTop); - - popup_set_icon(popup, 2, 14, &I_iButtonKey_49x44); - - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); - - notification_message(ibutton->notifications, &sequence_display_backlight_on); -} - -bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - iButton* ibutton = context; - Popup* popup = ibutton->popup; - - bool consumed = false; - - 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 && (string_empty_p(ibutton->file_path))) { - string_set_str(ibutton->file_path, arg); - if(ibutton_load_key_data(ibutton, ibutton->file_path, false)) { - ibutton_worker_emulate_start(ibutton->key_worker, ibutton->key); - string_t key_name; - string_init(key_name); - if(string_end_with_str_p(ibutton->file_path, IBUTTON_APP_EXTENSION)) { - path_extract_filename(ibutton->file_path, key_name, true); - } - - if(!string_empty_p(key_name)) { - ibutton_text_store_set( - ibutton, "emulating\n%s", string_get_cstr(key_name)); - } else { - ibutton_text_store_set(ibutton, "emulating"); - } - popup_set_text(popup, ibutton->text_store, 82, 32, AlignCenter, AlignTop); - - ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); - - string_clear(key_name); - result = true; - } else { - string_reset(ibutton->file_path); - } - } - rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventLoadFile, result); - } else if(event.event == iButtonCustomEventRpcExit) { - rpc_system_app_confirm(ibutton->rpc_ctx, 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); - } - } - - return consumed; -} - -void ibutton_scene_rpc_on_exit(void* context) { - iButton* ibutton = context; - Popup* popup = ibutton->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); - - ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); -} diff --git a/applications/ibutton/scenes/ibutton_scene_save_name.c b/applications/ibutton/scenes/ibutton_scene_save_name.c deleted file mode 100644 index be64038740b..00000000000 --- a/applications/ibutton/scenes/ibutton_scene_save_name.c +++ /dev/null @@ -1,82 +0,0 @@ -#include "../ibutton_i.h" -#include "m-string.h" -#include -#include - -static void ibutton_scene_save_name_text_input_callback(void* context) { - iButton* ibutton = context; - view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventTextEditResult); -} - -void ibutton_scene_save_name_on_enter(void* context) { - iButton* ibutton = context; - TextInput* text_input = ibutton->text_input; - - string_t key_name; - string_init(key_name); - if(string_end_with_str_p(ibutton->file_path, IBUTTON_APP_EXTENSION)) { - path_extract_filename(ibutton->file_path, key_name, true); - } - - const bool key_name_is_empty = string_empty_p(key_name); - if(key_name_is_empty) { - set_random_name(ibutton->text_store, IBUTTON_TEXT_STORE_SIZE); - } else { - ibutton_text_store_set(ibutton, "%s", string_get_cstr(key_name)); - } - - text_input_set_header_text(text_input, "Name the key"); - text_input_set_result_callback( - text_input, - ibutton_scene_save_name_text_input_callback, - ibutton, - ibutton->text_store, - IBUTTON_KEY_NAME_SIZE, - key_name_is_empty); - - string_t folder_path; - string_init(folder_path); - - path_extract_dirname(string_get_cstr(ibutton->file_path), folder_path); - - ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - string_get_cstr(folder_path), IBUTTON_APP_EXTENSION, string_get_cstr(key_name)); - text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); - - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewTextInput); - - string_clear(key_name); - string_clear(folder_path); -} - -bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) { - iButton* ibutton = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == iButtonCustomEventTextEditResult) { - if(ibutton_save_key(ibutton, ibutton->text_store)) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveSuccess); - } else { - const uint32_t possible_scenes[] = { - iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType}; - scene_manager_search_and_switch_to_previous_scene_one_of( - ibutton->scene_manager, possible_scenes, COUNT_OF(possible_scenes)); - } - } - } - - return consumed; -} - -void ibutton_scene_save_name_on_exit(void* context) { - iButton* ibutton = context; - TextInput* text_input = ibutton->text_input; - - void* validator_context = text_input_get_validator_callback_context(text_input); - text_input_set_validator(text_input, NULL, NULL); - validator_is_file_free((ValidatorIsFile*)validator_context); - - text_input_reset(text_input); -} diff --git a/applications/ibutton/scenes/ibutton_scene_save_success.c b/applications/ibutton/scenes/ibutton_scene_save_success.c deleted file mode 100644 index 6c24a897c73..00000000000 --- a/applications/ibutton/scenes/ibutton_scene_save_success.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "../ibutton_i.h" -#include - -static void ibutton_scene_save_success_popup_callback(void* context) { - iButton* ibutton = context; - view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventBack); -} - -void ibutton_scene_save_success_on_enter(void* context) { - iButton* ibutton = context; - Popup* popup = ibutton->popup; - DOLPHIN_DEED(DolphinDeedIbuttonSave); - - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); - - popup_set_callback(popup, ibutton_scene_save_success_popup_callback); - popup_set_context(popup, ibutton); - popup_set_timeout(popup, 1500); - popup_enable_timeout(popup); - - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); -} - -bool ibutton_scene_save_success_on_event(void* context, SceneManagerEvent event) { - iButton* ibutton = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == iButtonCustomEventBack) { - const uint32_t possible_scenes[] = { - iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType}; - scene_manager_search_and_switch_to_previous_scene_one_of( - ibutton->scene_manager, possible_scenes, COUNT_OF(possible_scenes)); - } - } - - return consumed; -} - -void ibutton_scene_save_success_on_exit(void* context) { - iButton* ibutton = context; - Popup* popup = ibutton->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); -} diff --git a/applications/ibutton/scenes/ibutton_scene_saved_key_menu.c b/applications/ibutton/scenes/ibutton_scene_saved_key_menu.c deleted file mode 100644 index 3d588dd02a6..00000000000 --- a/applications/ibutton/scenes/ibutton_scene_saved_key_menu.c +++ /dev/null @@ -1,78 +0,0 @@ -#include "../ibutton_i.h" - -enum SubmenuIndex { - SubmenuIndexEmulate, - SubmenuIndexWrite, - 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) { - submenu_add_item( - submenu, - "Write", - SubmenuIndexWrite, - ibutton_scene_saved_key_menu_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); - - 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; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state( - ibutton->scene_manager, iButtonSceneSavedKeyMenu, event.event); - consumed = true; - if(event.event == SubmenuIndexEmulate) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); - } else if(event.event == SubmenuIndexWrite) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite); - } else if(event.event == SubmenuIndexEdit) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddValue); - } else if(event.event == SubmenuIndexDelete) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneDeleteConfirm); - } else if(event.event == SubmenuIndexInfo) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneInfo); - } - } - - return consumed; -} - -void ibutton_scene_saved_key_menu_on_exit(void* context) { - iButton* ibutton = context; - submenu_reset(ibutton->submenu); -} diff --git a/applications/ibutton/scenes/ibutton_scene_start.c b/applications/ibutton/scenes/ibutton_scene_start.c deleted file mode 100644 index c2844cde91f..00000000000 --- a/applications/ibutton/scenes/ibutton_scene_start.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "../ibutton_i.h" -#include "ibutton/scenes/ibutton_scene.h" - -enum SubmenuIndex { - SubmenuIndexRead, - SubmenuIndexSaved, - 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); - - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneStart)); - - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu); -} - -bool ibutton_scene_start_on_event(void* context, SceneManagerEvent event) { - iButton* ibutton = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneStart, event.event); - consumed = true; - if(event.event == SubmenuIndexRead) { - scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead); - } else if(event.event == SubmenuIndexSaved) { - string_set_str(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); - } - } - - return consumed; -} - -void ibutton_scene_start_on_exit(void* context) { - iButton* ibutton = context; - submenu_reset(ibutton->submenu); -} diff --git a/applications/ibutton/scenes/ibutton_scene_write.c b/applications/ibutton/scenes/ibutton_scene_write.c deleted file mode 100644 index 195758d2ca1..00000000000 --- a/applications/ibutton/scenes/ibutton_scene_write.c +++ /dev/null @@ -1,124 +0,0 @@ -#include "../ibutton_i.h" -#include "m-string.h" -#include "toolbox/path.h" - -typedef enum { - iButtonSceneWriteStateDefault, - iButtonSceneWriteStateBlinkYellow, -} iButtonSceneWriteState; - -static void ibutton_scene_write_callback(void* context, iButtonWorkerWriteResult result) { - iButton* ibutton = context; - view_dispatcher_send_custom_event(ibutton->view_dispatcher, result); -} - -void ibutton_scene_write_on_enter(void* context) { - iButton* ibutton = context; - Popup* popup = ibutton->popup; - iButtonKey* key = ibutton->key; - iButtonWorker* worker = ibutton->key_worker; - - const uint8_t* key_data = ibutton_key_get_data_p(key); - - string_t key_name; - string_init(key_name); - if(string_end_with_str_p(ibutton->file_path, IBUTTON_APP_EXTENSION)) { - path_extract_filename(ibutton->file_path, key_name, true); - } - - uint8_t line_count = 2; - - // check that stored key has name - if(!string_empty_p(key_name)) { - ibutton_text_store_set(ibutton, "writing\n%s", string_get_cstr(key_name)); - line_count = 2; - } else { - // if not, show key data - switch(ibutton_key_get_type(key)) { - case iButtonKeyDS1990: - ibutton_text_store_set( - ibutton, - "writing\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]); - line_count = 3; - break; - case iButtonKeyCyfral: - ibutton_text_store_set(ibutton, "writing\n%02X %02X", key_data[0], key_data[1]); - line_count = 2; - break; - case iButtonKeyMetakom: - ibutton_text_store_set( - ibutton, - "writing\n%02X %02X %02X %02X", - key_data[0], - key_data[1], - key_data[2], - key_data[3]); - line_count = 2; - break; - } - } - - switch(line_count) { - case 3: - popup_set_header(popup, "iButton", 82, 18, AlignCenter, AlignBottom); - popup_set_text(popup, ibutton->text_store, 82, 22, AlignCenter, AlignTop); - break; - - default: - popup_set_header(popup, "iButton", 82, 24, AlignCenter, AlignBottom); - popup_set_text(popup, ibutton->text_store, 82, 28, AlignCenter, AlignTop); - break; - } - - popup_set_icon(popup, 2, 10, &I_iButtonKey_49x44); - - view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); - - ibutton_worker_write_set_callback(worker, ibutton_scene_write_callback, ibutton); - ibutton_worker_write_start(worker, key); - - string_clear(key_name); - - ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); -} - -bool ibutton_scene_write_on_event(void* context, SceneManagerEvent event) { - iButton* ibutton = context; - SceneManager* scene_manager = ibutton->scene_manager; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if((event.event == iButtonWorkerWriteOK) || (event.event == iButtonWorkerWriteSameKey)) { - scene_manager_next_scene(scene_manager, iButtonSceneWriteSuccess); - } else if(event.event == iButtonWorkerWriteNoDetect) { - ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateBlink); - } else if(event.event == iButtonWorkerWriteCannotWrite) { - ibutton_notification_message(ibutton, iButtonNotificationMessageYellowBlink); - } - - } else if(event.type == SceneManagerEventTypeTick) { - consumed = true; - } - - return consumed; -} - -void ibutton_scene_write_on_exit(void* context) { - iButton* ibutton = context; - Popup* popup = ibutton->popup; - ibutton_worker_stop(ibutton->key_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); - - ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); -} diff --git a/applications/infrared/application.fam b/applications/infrared/application.fam deleted file mode 100644 index 6f76ed429a6..00000000000 --- a/applications/infrared/application.fam +++ /dev/null @@ -1,23 +0,0 @@ -App( - appid="infrared", - name="Infrared", - apptype=FlipperAppType.APP, - entry_point="infrared_app", - cdefines=["APP_INFRARED"], - requires=[ - "gui", - "dialogs", - ], - provides=["infrared_start"], - icon="A_Infrared_14", - stack_size=3 * 1024, - order=40, -) - -App( - appid="infrared_start", - apptype=FlipperAppType.STARTUP, - entry_point="infrared_on_system_start", - requires=["infrared"], - order=20, -) diff --git a/applications/infrared/infrared.c b/applications/infrared/infrared.c deleted file mode 100644 index 60809e7871e..00000000000 --- a/applications/infrared/infrared.c +++ /dev/null @@ -1,466 +0,0 @@ -#include "infrared_i.h" - -#include -#include - -static const NotificationSequence* infrared_notification_sequences[] = { - &sequence_success, - &sequence_set_only_green_255, - &sequence_reset_green, - &sequence_solid_yellow, - &sequence_reset_rgb, - &sequence_blink_start_cyan, - &sequence_blink_start_magenta, - &sequence_blink_stop, -}; - -static void infrared_make_app_folder(Infrared* infrared) { - if(!storage_simply_mkdir(infrared->storage, INFRARED_APP_FOLDER)) { - dialog_message_show_storage_error(infrared->dialogs, "Cannot create\napp folder"); - } -} - -static bool infrared_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - Infrared* infrared = context; - return scene_manager_handle_custom_event(infrared->scene_manager, event); -} - -static bool infrared_back_event_callback(void* context) { - furi_assert(context); - Infrared* infrared = context; - return scene_manager_handle_back_event(infrared->scene_manager); -} - -static void infrared_tick_event_callback(void* context) { - furi_assert(context); - Infrared* infrared = context; - scene_manager_handle_tick_event(infrared->scene_manager); -} - -static void infrared_rpc_command_callback(RpcAppSystemEvent event, void* context) { - furi_assert(context); - Infrared* infrared = context; - furi_assert(infrared->rpc_ctx); - - if(event == RpcAppEventSessionClose) { - view_dispatcher_send_custom_event( - infrared->view_dispatcher, InfraredCustomEventTypeRpcSessionClose); - rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL); - infrared->rpc_ctx = NULL; - } else if(event == RpcAppEventAppExit) { - view_dispatcher_send_custom_event( - infrared->view_dispatcher, InfraredCustomEventTypeRpcExit); - } else if(event == RpcAppEventLoadFile) { - view_dispatcher_send_custom_event( - infrared->view_dispatcher, InfraredCustomEventTypeRpcLoad); - } else if(event == RpcAppEventButtonPress) { - view_dispatcher_send_custom_event( - infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPress); - } else if(event == RpcAppEventButtonRelease) { - view_dispatcher_send_custom_event( - infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonRelease); - } else { - rpc_system_app_confirm(infrared->rpc_ctx, event, false); - } -} - -static void infrared_find_vacant_remote_name(string_t name, const char* path) { - Storage* storage = furi_record_open(RECORD_STORAGE); - - string_t base_path; - string_init_set_str(base_path, path); - - if(string_end_with_str_p(base_path, INFRARED_APP_EXTENSION)) { - size_t filename_start = string_search_rchar(base_path, '/'); - string_left(base_path, filename_start); - } - - string_printf(base_path, "%s/%s%s", path, string_get_cstr(name), INFRARED_APP_EXTENSION); - - FS_Error status = storage_common_stat(storage, string_get_cstr(base_path), NULL); - - if(status == FSE_OK) { - /* If the suggested name is occupied, try another one (name2, name3, etc) */ - size_t dot = string_search_rchar(base_path, '.'); - string_left(base_path, dot); - - string_t path_temp; - string_init(path_temp); - - uint32_t i = 1; - do { - string_printf( - path_temp, "%s%u%s", string_get_cstr(base_path), ++i, INFRARED_APP_EXTENSION); - status = storage_common_stat(storage, string_get_cstr(path_temp), NULL); - } while(status == FSE_OK); - - string_clear(path_temp); - - if(status == FSE_NOT_EXIST) { - string_cat_printf(name, "%u", i); - } - } - - string_clear(base_path); - furi_record_close(RECORD_STORAGE); -} - -static Infrared* infrared_alloc() { - Infrared* infrared = malloc(sizeof(Infrared)); - - string_init(infrared->file_path); - - InfraredAppState* app_state = &infrared->app_state; - app_state->is_learning_new_remote = false; - app_state->is_debug_enabled = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); - app_state->edit_target = InfraredEditTargetNone; - app_state->edit_mode = InfraredEditModeNone; - app_state->current_button_index = InfraredButtonIndexNone; - - infrared->scene_manager = scene_manager_alloc(&infrared_scene_handlers, infrared); - infrared->view_dispatcher = view_dispatcher_alloc(); - - infrared->gui = furi_record_open(RECORD_GUI); - - ViewDispatcher* view_dispatcher = infrared->view_dispatcher; - view_dispatcher_enable_queue(view_dispatcher); - view_dispatcher_set_event_callback_context(view_dispatcher, infrared); - view_dispatcher_set_custom_event_callback(view_dispatcher, infrared_custom_event_callback); - view_dispatcher_set_navigation_event_callback(view_dispatcher, infrared_back_event_callback); - view_dispatcher_set_tick_event_callback(view_dispatcher, infrared_tick_event_callback, 100); - - infrared->storage = furi_record_open(RECORD_STORAGE); - infrared->dialogs = furi_record_open(RECORD_DIALOGS); - infrared->notifications = furi_record_open(RECORD_NOTIFICATION); - - infrared->worker = infrared_worker_alloc(); - infrared->remote = infrared_remote_alloc(); - infrared->received_signal = infrared_signal_alloc(); - infrared->brute_force = infrared_brute_force_alloc(); - - infrared->submenu = submenu_alloc(); - view_dispatcher_add_view( - view_dispatcher, InfraredViewSubmenu, submenu_get_view(infrared->submenu)); - - infrared->text_input = text_input_alloc(); - view_dispatcher_add_view( - view_dispatcher, InfraredViewTextInput, text_input_get_view(infrared->text_input)); - - infrared->dialog_ex = dialog_ex_alloc(); - view_dispatcher_add_view( - view_dispatcher, InfraredViewDialogEx, dialog_ex_get_view(infrared->dialog_ex)); - - infrared->button_menu = button_menu_alloc(); - view_dispatcher_add_view( - view_dispatcher, InfraredViewButtonMenu, button_menu_get_view(infrared->button_menu)); - - infrared->popup = popup_alloc(); - view_dispatcher_add_view(view_dispatcher, InfraredViewPopup, popup_get_view(infrared->popup)); - - infrared->view_stack = view_stack_alloc(); - view_dispatcher_add_view( - view_dispatcher, InfraredViewStack, view_stack_get_view(infrared->view_stack)); - - if(app_state->is_debug_enabled) { - infrared->debug_view = infrared_debug_view_alloc(); - view_dispatcher_add_view( - view_dispatcher, - InfraredViewDebugView, - infrared_debug_view_get_view(infrared->debug_view)); - } - - infrared->button_panel = button_panel_alloc(); - infrared->loading = loading_alloc(); - infrared->progress = infrared_progress_view_alloc(); - - return infrared; -} - -static void infrared_free(Infrared* infrared) { - furi_assert(infrared); - ViewDispatcher* view_dispatcher = infrared->view_dispatcher; - InfraredAppState* app_state = &infrared->app_state; - - if(infrared->rpc_ctx) { - rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL); - rpc_system_app_send_exited(infrared->rpc_ctx); - infrared->rpc_ctx = NULL; - } - - view_dispatcher_remove_view(view_dispatcher, InfraredViewSubmenu); - submenu_free(infrared->submenu); - - view_dispatcher_remove_view(view_dispatcher, InfraredViewTextInput); - text_input_free(infrared->text_input); - - view_dispatcher_remove_view(view_dispatcher, InfraredViewDialogEx); - dialog_ex_free(infrared->dialog_ex); - - view_dispatcher_remove_view(view_dispatcher, InfraredViewButtonMenu); - button_menu_free(infrared->button_menu); - - view_dispatcher_remove_view(view_dispatcher, InfraredViewPopup); - popup_free(infrared->popup); - - view_dispatcher_remove_view(view_dispatcher, InfraredViewStack); - view_stack_free(infrared->view_stack); - - if(app_state->is_debug_enabled) { - view_dispatcher_remove_view(view_dispatcher, InfraredViewDebugView); - infrared_debug_view_free(infrared->debug_view); - } - - button_panel_free(infrared->button_panel); - loading_free(infrared->loading); - infrared_progress_view_free(infrared->progress); - - view_dispatcher_free(view_dispatcher); - scene_manager_free(infrared->scene_manager); - - infrared_brute_force_free(infrared->brute_force); - infrared_signal_free(infrared->received_signal); - infrared_remote_free(infrared->remote); - infrared_worker_free(infrared->worker); - - furi_record_close(RECORD_NOTIFICATION); - infrared->notifications = NULL; - - furi_record_close(RECORD_DIALOGS); - infrared->dialogs = NULL; - - furi_record_close(RECORD_GUI); - infrared->gui = NULL; - - string_clear(infrared->file_path); - - free(infrared); -} - -bool infrared_add_remote_with_button( - Infrared* infrared, - const char* button_name, - InfraredSignal* signal) { - InfraredRemote* remote = infrared->remote; - - string_t new_name, new_path; - string_init_set_str(new_name, INFRARED_DEFAULT_REMOTE_NAME); - string_init_set_str(new_path, INFRARED_APP_FOLDER); - - infrared_find_vacant_remote_name(new_name, string_get_cstr(new_path)); - string_cat_printf(new_path, "/%s%s", string_get_cstr(new_name), INFRARED_APP_EXTENSION); - - infrared_remote_reset(remote); - infrared_remote_set_name(remote, string_get_cstr(new_name)); - infrared_remote_set_path(remote, string_get_cstr(new_path)); - - string_clear(new_name); - string_clear(new_path); - return infrared_remote_add_button(remote, button_name, signal); -} - -bool infrared_rename_current_remote(Infrared* infrared, const char* name) { - InfraredRemote* remote = infrared->remote; - const char* remote_path = infrared_remote_get_path(remote); - - if(!strcmp(infrared_remote_get_name(remote), name)) { - return true; - } - - string_t new_name; - string_init_set_str(new_name, name); - - infrared_find_vacant_remote_name(new_name, remote_path); - - string_t new_path; - string_init_set(new_path, infrared_remote_get_path(remote)); - if(string_end_with_str_p(new_path, INFRARED_APP_EXTENSION)) { - size_t filename_start = string_search_rchar(new_path, '/'); - string_left(new_path, filename_start); - } - string_cat_printf(new_path, "/%s%s", string_get_cstr(new_name), INFRARED_APP_EXTENSION); - - Storage* storage = furi_record_open(RECORD_STORAGE); - - FS_Error status = storage_common_rename( - storage, infrared_remote_get_path(remote), string_get_cstr(new_path)); - infrared_remote_set_name(remote, string_get_cstr(new_name)); - infrared_remote_set_path(remote, string_get_cstr(new_path)); - - string_clear(new_name); - string_clear(new_path); - - furi_record_close(RECORD_STORAGE); - return (status == FSE_OK || status == FSE_EXIST); -} - -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; - } - - if(infrared_signal_is_raw(signal)) { - InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); - infrared_worker_set_raw_signal(infrared->worker, raw->timings, raw->timings_size); - } else { - InfraredMessage* message = infrared_signal_get_message(signal); - infrared_worker_set_decoded_signal(infrared->worker, message); - } - - DOLPHIN_DEED(DolphinDeedIrSend); - infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); - - infrared_worker_tx_set_get_signal_callback( - infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared); - infrared_worker_tx_start(infrared->worker); -} - -void infrared_tx_start_button_index(Infrared* infrared, size_t button_index) { - furi_assert(button_index < infrared_remote_get_button_count(infrared->remote)); - - InfraredRemoteButton* button = infrared_remote_get_button(infrared->remote, 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); -} - -void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text, ...) { - va_list args; - va_start(args, text); - - vsnprintf(infrared->text_store[bank], INFRARED_TEXT_STORE_SIZE, text, args); - - va_end(args); -} - -void infrared_text_store_clear(Infrared* infrared, uint32_t bank) { - memset(infrared->text_store[bank], 0, INFRARED_TEXT_STORE_SIZE); -} - -void infrared_play_notification_message(Infrared* infrared, uint32_t message) { - furi_assert(message < sizeof(infrared_notification_sequences) / sizeof(NotificationSequence*)); - notification_message(infrared->notifications, infrared_notification_sequences[message]); -} - -void infrared_show_loading_popup(Infrared* infrared, bool show) { - TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); - ViewStack* view_stack = infrared->view_stack; - Loading* loading = infrared->loading; - - if(show) { - // Raise timer priority so that animations can play - vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1); - view_stack_add_view(view_stack, loading_get_view(loading)); - } else { - view_stack_remove_view(view_stack, loading_get_view(loading)); - // Restore default timer priority - vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); - } -} - -void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { - furi_assert(context); - Infrared* infrared = context; - - if(infrared_worker_signal_is_decoded(received_signal)) { - infrared_signal_set_message( - infrared->received_signal, infrared_worker_get_decoded_signal(received_signal)); - } else { - const uint32_t* timings; - size_t timings_size; - infrared_worker_get_raw_signal(received_signal, &timings, &timings_size); - infrared_signal_set_raw_signal( - infrared->received_signal, - timings, - timings_size, - INFRARED_COMMON_CARRIER_FREQUENCY, - INFRARED_COMMON_DUTY_CYCLE); - } - - view_dispatcher_send_custom_event( - infrared->view_dispatcher, InfraredCustomEventTypeSignalReceived); -} - -void infrared_text_input_callback(void* context) { - furi_assert(context); - Infrared* infrared = context; - view_dispatcher_send_custom_event( - infrared->view_dispatcher, InfraredCustomEventTypeTextEditDone); -} - -void infrared_popup_closed_callback(void* context) { - furi_assert(context); - Infrared* infrared = context; - view_dispatcher_send_custom_event( - infrared->view_dispatcher, InfraredCustomEventTypePopupClosed); -} - -int32_t infrared_app(void* p) { - Infrared* infrared = infrared_alloc(); - - infrared_make_app_folder(infrared); - - bool is_remote_loaded = false; - bool is_rpc_mode = false; - - if(p && strlen(p)) { - uint32_t rpc_ctx = 0; - if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { - infrared->rpc_ctx = (void*)rpc_ctx; - rpc_system_app_set_callback( - infrared->rpc_ctx, infrared_rpc_command_callback, infrared); - rpc_system_app_send_started(infrared->rpc_ctx); - is_rpc_mode = true; - } else { - string_set_str(infrared->file_path, (const char*)p); - is_remote_loaded = infrared_remote_load(infrared->remote, infrared->file_path); - if(!is_remote_loaded) { - dialog_message_show_storage_error( - infrared->dialogs, "Failed to load\nselected remote"); - return -1; - } - } - } - - if(is_rpc_mode) { - view_dispatcher_attach_to_gui( - infrared->view_dispatcher, infrared->gui, ViewDispatcherTypeDesktop); - scene_manager_next_scene(infrared->scene_manager, InfraredSceneRpc); - } else { - view_dispatcher_attach_to_gui( - infrared->view_dispatcher, infrared->gui, ViewDispatcherTypeFullscreen); - if(is_remote_loaded) { - scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); - } else { - scene_manager_next_scene(infrared->scene_manager, InfraredSceneStart); - } - } - - view_dispatcher_run(infrared->view_dispatcher); - - infrared_free(infrared); - return 0; -} diff --git a/applications/infrared/infrared.h b/applications/infrared/infrared.h deleted file mode 100644 index e5eeb11772f..00000000000 --- a/applications/infrared/infrared.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -typedef struct Infrared Infrared; diff --git a/applications/infrared/infrared_brute_force.c b/applications/infrared/infrared_brute_force.c deleted file mode 100644 index 8dbc2301274..00000000000 --- a/applications/infrared/infrared_brute_force.c +++ /dev/null @@ -1,155 +0,0 @@ -#include "infrared_brute_force.h" - -#include -#include -#include -#include - -#include "infrared_signal.h" - -typedef struct { - uint32_t index; - uint32_t count; -} InfraredBruteForceRecord; - -DICT_DEF2( - InfraredBruteForceRecordDict, - string_t, - STRING_OPLIST, - InfraredBruteForceRecord, - M_POD_OPLIST); - -struct InfraredBruteForce { - FlipperFormat* ff; - const char* db_filename; - string_t current_record_name; - InfraredBruteForceRecordDict_t records; -}; - -InfraredBruteForce* infrared_brute_force_alloc() { - InfraredBruteForce* brute_force = malloc(sizeof(InfraredBruteForce)); - brute_force->ff = NULL; - brute_force->db_filename = NULL; - string_init(brute_force->current_record_name); - InfraredBruteForceRecordDict_init(brute_force->records); - return brute_force; -} - -void infrared_brute_force_free(InfraredBruteForce* brute_force) { - furi_assert(!brute_force->ff); - InfraredBruteForceRecordDict_clear(brute_force->records); - string_clear(brute_force->current_record_name); - free(brute_force); -} - -void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename) { - brute_force->db_filename = db_filename; -} - -bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { - furi_assert(brute_force->db_filename); - bool success = false; - - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); - - success = flipper_format_buffered_file_open_existing(ff, brute_force->db_filename); - if(success) { - string_t signal_name; - string_init(signal_name); - while(flipper_format_read_string(ff, "name", signal_name)) { - InfraredBruteForceRecord* record = - InfraredBruteForceRecordDict_get(brute_force->records, signal_name); - if(record) { - ++(record->count); - } - } - string_clear(signal_name); - } - - flipper_format_free(ff); - furi_record_close(RECORD_STORAGE); - return success; -} - -bool infrared_brute_force_start( - InfraredBruteForce* brute_force, - uint32_t index, - uint32_t* record_count) { - bool success = false; - *record_count = 0; - - InfraredBruteForceRecordDict_it_t it; - for(InfraredBruteForceRecordDict_it(it, brute_force->records); - !InfraredBruteForceRecordDict_end_p(it); - InfraredBruteForceRecordDict_next(it)) { - const InfraredBruteForceRecordDict_itref_t* record = InfraredBruteForceRecordDict_cref(it); - if(record->value.index == index) { - *record_count = record->value.count; - if(*record_count) { - string_set(brute_force->current_record_name, record->key); - } - break; - } - } - - if(*record_count) { - Storage* storage = furi_record_open(RECORD_STORAGE); - brute_force->ff = flipper_format_buffered_file_alloc(storage); - success = - flipper_format_buffered_file_open_existing(brute_force->ff, brute_force->db_filename); - if(!success) { - flipper_format_free(brute_force->ff); - brute_force->ff = NULL; - furi_record_close(RECORD_STORAGE); - } - } - return success; -} - -bool infrared_brute_force_is_started(InfraredBruteForce* brute_force) { - return brute_force->ff; -} - -void infrared_brute_force_stop(InfraredBruteForce* brute_force) { - furi_assert(string_size(brute_force->current_record_name)); - furi_assert(brute_force->ff); - - string_reset(brute_force->current_record_name); - flipper_format_free(brute_force->ff); - furi_record_close(RECORD_STORAGE); - brute_force->ff = NULL; -} - -bool infrared_brute_force_send_next(InfraredBruteForce* brute_force) { - furi_assert(string_size(brute_force->current_record_name)); - furi_assert(brute_force->ff); - bool success = false; - - string_t signal_name; - string_init(signal_name); - InfraredSignal* signal = infrared_signal_alloc(); - - do { - success = infrared_signal_read(signal, brute_force->ff, signal_name); - } while(success && !string_equal_p(brute_force->current_record_name, signal_name)); - - if(success) { - infrared_signal_transmit(signal); - } - - infrared_signal_free(signal); - string_clear(signal_name); - return success; -} - -void infrared_brute_force_add_record( - InfraredBruteForce* brute_force, - uint32_t index, - const char* name) { - InfraredBruteForceRecord value = {.index = index, .count = 0}; - string_t key; - string_init_set_str(key, name); - InfraredBruteForceRecordDict_set_at(brute_force->records, key, value); - string_clear(key); -} diff --git a/applications/infrared/infrared_brute_force.h b/applications/infrared/infrared_brute_force.h deleted file mode 100644 index acf0d7b6e12..00000000000 --- a/applications/infrared/infrared_brute_force.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include -#include - -typedef struct InfraredBruteForce InfraredBruteForce; - -InfraredBruteForce* infrared_brute_force_alloc(); -void infrared_brute_force_free(InfraredBruteForce* brute_force); -void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename); -bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force); -bool infrared_brute_force_start( - InfraredBruteForce* brute_force, - uint32_t index, - uint32_t* record_count); -bool infrared_brute_force_is_started(InfraredBruteForce* brute_force); -void infrared_brute_force_stop(InfraredBruteForce* brute_force); -bool infrared_brute_force_send_next(InfraredBruteForce* brute_force); -void infrared_brute_force_add_record( - InfraredBruteForce* brute_force, - uint32_t index, - const char* name); diff --git a/applications/infrared/infrared_cli.c b/applications/infrared/infrared_cli.c deleted file mode 100644 index aae02e8fd14..00000000000 --- a/applications/infrared/infrared_cli.c +++ /dev/null @@ -1,201 +0,0 @@ -#include -#include -#include -#include -#include - -#include "infrared_signal.h" - -#define INFRARED_CLI_BUF_SIZE 10 - -static void infrared_cli_start_ir_rx(Cli* cli, string_t args); -static void infrared_cli_start_ir_tx(Cli* cli, string_t args); - -static const struct { - const char* cmd; - void (*process_function)(Cli* cli, string_t args); -} infrared_cli_commands[] = { - {.cmd = "rx", .process_function = infrared_cli_start_ir_rx}, - {.cmd = "tx", .process_function = infrared_cli_start_ir_tx}, -}; - -static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { - furi_assert(received_signal); - char buf[100]; - size_t buf_cnt; - Cli* cli = (Cli*)context; - - if(infrared_worker_signal_is_decoded(received_signal)) { - const InfraredMessage* message = infrared_worker_get_decoded_signal(received_signal); - buf_cnt = snprintf( - buf, - sizeof(buf), - "%s, A:0x%0*lX, C:0x%0*lX%s\r\n", - infrared_get_protocol_name(message->protocol), - ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4), - message->address, - ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), - message->command, - message->repeat ? " R" : ""); - cli_write(cli, (uint8_t*)buf, buf_cnt); - } else { - const uint32_t* timings; - size_t timings_cnt; - infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt); - - buf_cnt = snprintf(buf, sizeof(buf), "RAW, %d samples:\r\n", timings_cnt); - cli_write(cli, (uint8_t*)buf, buf_cnt); - for(size_t i = 0; i < timings_cnt; ++i) { - buf_cnt = snprintf(buf, sizeof(buf), "%lu ", timings[i]); - cli_write(cli, (uint8_t*)buf, buf_cnt); - } - buf_cnt = snprintf(buf, sizeof(buf), "\r\n"); - cli_write(cli, (uint8_t*)buf, buf_cnt); - } -} - -static void infrared_cli_start_ir_rx(Cli* cli, string_t args) { - UNUSED(cli); - UNUSED(args); - InfraredWorker* worker = infrared_worker_alloc(); - infrared_worker_rx_start(worker); - infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli); - - printf("Receiving INFRARED...\r\nPress Ctrl+C to abort\r\n"); - while(!cli_cmd_interrupt_received(cli)) { - furi_delay_ms(50); - } - - infrared_worker_rx_stop(worker); - infrared_worker_free(worker); -} - -static void infrared_cli_print_usage(void) { - printf("Usage:\r\n"); - printf("\tir rx\r\n"); - printf("\tir tx
\r\n"); - printf("\t and
are hex-formatted\r\n"); - printf("\tAvailable protocols:"); - for(int i = 0; infrared_is_protocol_valid((InfraredProtocol)i); ++i) { - printf(" %s", infrared_get_protocol_name((InfraredProtocol)i)); - } - printf("\r\n"); - printf("\tRaw format:\r\n"); - printf("\tir tx RAW F: DC: ...\r\n"); - printf( - "\tFrequency (%d - %d), Duty cycle (0 - 100), max 512 samples\r\n", - INFRARED_MIN_FREQUENCY, - INFRARED_MAX_FREQUENCY); -} - -static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) { - char protocol_name[32]; - InfraredMessage message; - int parsed = sscanf(str, "%31s %lX %lX", protocol_name, &message.address, &message.command); - - if(parsed != 3) { - return false; - } - - message.protocol = infrared_get_protocol_by_name(protocol_name); - message.repeat = false; - infrared_signal_set_message(signal, &message); - return infrared_signal_is_valid(signal); -} - -static bool infrared_cli_parse_raw(const char* str, InfraredSignal* signal) { - char frequency_str[INFRARED_CLI_BUF_SIZE]; - char duty_cycle_str[INFRARED_CLI_BUF_SIZE]; - int parsed = sscanf(str, "RAW F:%9s DC:%9s", frequency_str, duty_cycle_str); - - if(parsed != 2) { - return false; - } - - uint32_t* timings = malloc(sizeof(uint32_t) * MAX_TIMINGS_AMOUNT); - uint32_t frequency = atoi(frequency_str); - float duty_cycle = (float)atoi(duty_cycle_str) / 100; - - str += strlen(frequency_str) + strlen(duty_cycle_str) + INFRARED_CLI_BUF_SIZE; - - size_t timings_size = 0; - while(1) { - while(*str == ' ') { - ++str; - } - - char timing_str[INFRARED_CLI_BUF_SIZE]; - if(sscanf(str, "%9s", timing_str) != 1) { - break; - } - - str += strlen(timing_str); - uint32_t timing = atoi(timing_str); - - if((timing <= 0) || (timings_size >= MAX_TIMINGS_AMOUNT)) { - break; - } - - timings[timings_size] = timing; - ++timings_size; - } - - infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle); - free(timings); - - return infrared_signal_is_valid(signal); -} - -static void infrared_cli_start_ir_tx(Cli* cli, string_t args) { - UNUSED(cli); - const char* str = string_get_cstr(args); - InfraredSignal* signal = infrared_signal_alloc(); - - bool success = infrared_cli_parse_message(str, signal) || infrared_cli_parse_raw(str, signal); - if(success) { - infrared_signal_transmit(signal); - } else { - printf("Wrong arguments.\r\n"); - infrared_cli_print_usage(); - } - - infrared_signal_free(signal); -} - -static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) { - UNUSED(context); - if(furi_hal_infrared_is_busy()) { - printf("INFRARED is busy. Exiting."); - return; - } - - size_t i = 0; - for(; i < COUNT_OF(infrared_cli_commands); ++i) { - size_t size = strlen(infrared_cli_commands[i].cmd); - bool cmd_found = !strncmp(string_get_cstr(args), infrared_cli_commands[i].cmd, size); - if(cmd_found) { - if(string_size(args) == size) { - break; - } - if(string_get_cstr(args)[size] == ' ') { - string_right(args, size + 1); - break; - } - } - } - - if(i < COUNT_OF(infrared_cli_commands)) { - infrared_cli_commands[i].process_function(cli, args); - } else { - infrared_cli_print_usage(); - } -} -void infrared_on_system_start() { -#ifdef SRV_CLI - Cli* cli = (Cli*)furi_record_open(RECORD_CLI); - cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL); - furi_record_close(RECORD_CLI); -#else - UNUSED(infrared_cli_start_ir); -#endif -} diff --git a/applications/infrared/infrared_i.h b/applications/infrared/infrared_i.h deleted file mode 100644 index 9521525875f..00000000000 --- a/applications/infrared/infrared_i.h +++ /dev/null @@ -1,140 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -#include "infrared.h" -#include "infrared_remote.h" -#include "infrared_brute_force.h" -#include "infrared_custom_event.h" - -#include "scenes/infrared_scene.h" -#include "views/infrared_progress_view.h" -#include "views/infrared_debug_view.h" - -#include "rpc/rpc_app.h" - -#define INFRARED_FILE_NAME_SIZE 100 -#define INFRARED_TEXT_STORE_NUM 2 -#define INFRARED_TEXT_STORE_SIZE 128 - -#define INFRARED_MAX_BUTTON_NAME_LENGTH 22 -#define INFRARED_MAX_REMOTE_NAME_LENGTH 22 - -#define INFRARED_APP_FOLDER ANY_PATH("infrared") -#define INFRARED_APP_EXTENSION ".ir" - -#define INFRARED_DEFAULT_REMOTE_NAME "Remote" -#define INFRARED_LOG_TAG "InfraredApp" - -typedef enum { - InfraredButtonIndexNone = -1, -} InfraredButtonIndex; - -typedef enum { - InfraredEditTargetNone, - InfraredEditTargetRemote, - InfraredEditTargetButton, -} InfraredEditTarget; - -typedef enum { - InfraredEditModeNone, - InfraredEditModeRename, - InfraredEditModeDelete, -} InfraredEditMode; - -typedef struct { - bool is_learning_new_remote; - bool is_debug_enabled; - bool is_transmitting; - InfraredEditTarget edit_target : 8; - InfraredEditMode edit_mode : 8; - int32_t current_button_index; -} InfraredAppState; - -struct Infrared { - SceneManager* scene_manager; - ViewDispatcher* view_dispatcher; - - Gui* gui; - Storage* storage; - DialogsApp* dialogs; - NotificationApp* notifications; - InfraredWorker* worker; - InfraredRemote* remote; - InfraredSignal* received_signal; - InfraredBruteForce* brute_force; - - Submenu* submenu; - TextInput* text_input; - DialogEx* dialog_ex; - ButtonMenu* button_menu; - Popup* popup; - - ViewStack* view_stack; - InfraredDebugView* debug_view; - - ButtonPanel* button_panel; - Loading* loading; - InfraredProgressView* progress; - - string_t file_path; - char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1]; - InfraredAppState app_state; - - void* rpc_ctx; -}; - -typedef enum { - InfraredViewSubmenu, - InfraredViewTextInput, - InfraredViewDialogEx, - InfraredViewButtonMenu, - InfraredViewPopup, - InfraredViewStack, - InfraredViewDebugView, -} InfraredView; - -typedef enum { - InfraredNotificationMessageSuccess, - InfraredNotificationMessageGreenOn, - InfraredNotificationMessageGreenOff, - InfraredNotificationMessageYellowOn, - InfraredNotificationMessageYellowOff, - InfraredNotificationMessageBlinkStartRead, - InfraredNotificationMessageBlinkStartSend, - InfraredNotificationMessageBlinkStop, -} InfraredNotificationMessage; - -bool infrared_add_remote_with_button(Infrared* infrared, const char* name, InfraredSignal* signal); -bool infrared_rename_current_remote(Infrared* infrared, const char* name); -void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal); -void infrared_tx_start_button_index(Infrared* infrared, size_t button_index); -void infrared_tx_start_received(Infrared* infrared); -void infrared_tx_stop(Infrared* infrared); -void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text, ...); -void infrared_text_store_clear(Infrared* infrared, uint32_t bank); -void infrared_play_notification_message(Infrared* infrared, uint32_t message); -void infrared_show_loading_popup(Infrared* infrared, bool show); - -void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal); -void infrared_text_input_callback(void* context); -void infrared_popup_closed_callback(void* context); diff --git a/applications/infrared/infrared_remote.c b/applications/infrared/infrared_remote.c deleted file mode 100644 index 4417c3c733f..00000000000 --- a/applications/infrared/infrared_remote.c +++ /dev/null @@ -1,189 +0,0 @@ -#include "infrared_remote.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#define TAG "InfraredRemote" - -ARRAY_DEF(InfraredButtonArray, InfraredRemoteButton*, M_PTR_OPLIST); - -struct InfraredRemote { - InfraredButtonArray_t buttons; - string_t name; - string_t path; -}; - -static void infrared_remote_clear_buttons(InfraredRemote* remote) { - InfraredButtonArray_it_t it; - for(InfraredButtonArray_it(it, remote->buttons); !InfraredButtonArray_end_p(it); - InfraredButtonArray_next(it)) { - infrared_remote_button_free(*InfraredButtonArray_cref(it)); - } - InfraredButtonArray_reset(remote->buttons); -} - -InfraredRemote* infrared_remote_alloc() { - InfraredRemote* remote = malloc(sizeof(InfraredRemote)); - InfraredButtonArray_init(remote->buttons); - string_init(remote->name); - string_init(remote->path); - return remote; -} - -void infrared_remote_free(InfraredRemote* remote) { - infrared_remote_clear_buttons(remote); - InfraredButtonArray_clear(remote->buttons); - string_clear(remote->path); - string_clear(remote->name); - free(remote); -} - -void infrared_remote_reset(InfraredRemote* remote) { - infrared_remote_clear_buttons(remote); - string_reset(remote->name); - string_reset(remote->path); -} - -void infrared_remote_set_name(InfraredRemote* remote, const char* name) { - string_set_str(remote->name, name); -} - -const char* infrared_remote_get_name(InfraredRemote* remote) { - return string_get_cstr(remote->name); -} - -void infrared_remote_set_path(InfraredRemote* remote, const char* path) { - string_set_str(remote->path, path); -} - -const char* infrared_remote_get_path(InfraredRemote* remote) { - return string_get_cstr(remote->path); -} - -size_t infrared_remote_get_button_count(InfraredRemote* remote) { - return InfraredButtonArray_size(remote->buttons); -} - -InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index) { - furi_assert(index < InfraredButtonArray_size(remote->buttons)); - return *InfraredButtonArray_get(remote->buttons, index); -} - -bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* name, size_t* index) { - for(size_t i = 0; i < InfraredButtonArray_size(remote->buttons); i++) { - InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, i); - if(!strcmp(infrared_remote_button_get_name(button), name)) { - *index = i; - return true; - } - } - return false; -} - -bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal) { - InfraredRemoteButton* button = infrared_remote_button_alloc(); - infrared_remote_button_set_name(button, name); - infrared_remote_button_set_signal(button, signal); - InfraredButtonArray_push_back(remote->buttons, button); - return infrared_remote_store(remote); -} - -bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index) { - furi_assert(index < InfraredButtonArray_size(remote->buttons)); - InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, index); - infrared_remote_button_set_name(button, new_name); - return infrared_remote_store(remote); -} - -bool infrared_remote_delete_button(InfraredRemote* remote, size_t index) { - furi_assert(index < InfraredButtonArray_size(remote->buttons)); - InfraredRemoteButton* button; - InfraredButtonArray_pop_at(&button, remote->buttons, index); - infrared_remote_button_free(button); - return infrared_remote_store(remote); -} - -bool infrared_remote_store(InfraredRemote* remote) { - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* ff = flipper_format_file_alloc(storage); - const char* path = string_get_cstr(remote->path); - - FURI_LOG_I(TAG, "store file: \'%s\'", path); - - bool success = flipper_format_file_open_always(ff, path) && - flipper_format_write_header_cstr(ff, "IR signals file", 1); - if(success) { - InfraredButtonArray_it_t it; - for(InfraredButtonArray_it(it, remote->buttons); !InfraredButtonArray_end_p(it); - InfraredButtonArray_next(it)) { - InfraredRemoteButton* button = *InfraredButtonArray_cref(it); - success = infrared_signal_save( - infrared_remote_button_get_signal(button), - ff, - infrared_remote_button_get_name(button)); - if(!success) { - break; - } - } - } - - flipper_format_free(ff); - furi_record_close(RECORD_STORAGE); - return success; -} - -bool infrared_remote_load(InfraredRemote* remote, string_t path) { - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); - - string_t buf; - string_init(buf); - - FURI_LOG_I(TAG, "load file: \'%s\'", string_get_cstr(path)); - bool success = flipper_format_buffered_file_open_existing(ff, string_get_cstr(path)); - - if(success) { - uint32_t version; - success = flipper_format_read_header(ff, buf, &version) && - !string_cmp_str(buf, "IR signals file") && (version == 1); - } - - if(success) { - path_extract_filename(path, buf, true); - infrared_remote_clear_buttons(remote); - infrared_remote_set_name(remote, string_get_cstr(buf)); - infrared_remote_set_path(remote, string_get_cstr(path)); - - for(bool can_read = true; can_read;) { - InfraredRemoteButton* button = infrared_remote_button_alloc(); - can_read = infrared_signal_read(infrared_remote_button_get_signal(button), ff, buf); - if(can_read) { - infrared_remote_button_set_name(button, string_get_cstr(buf)); - InfraredButtonArray_push_back(remote->buttons, button); - } else { - infrared_remote_button_free(button); - } - } - } - - string_clear(buf); - flipper_format_free(ff); - furi_record_close(RECORD_STORAGE); - return success; -} - -bool infrared_remote_remove(InfraredRemote* remote) { - Storage* storage = furi_record_open(RECORD_STORAGE); - - FS_Error status = storage_common_remove(storage, string_get_cstr(remote->path)); - infrared_remote_reset(remote); - - furi_record_close(RECORD_STORAGE); - return (status == FSE_OK || status == FSE_NOT_EXIST); -} diff --git a/applications/infrared/infrared_remote.h b/applications/infrared/infrared_remote.h deleted file mode 100644 index b6f63a198cb..00000000000 --- a/applications/infrared/infrared_remote.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -#include "infrared_remote_button.h" - -typedef struct InfraredRemote InfraredRemote; - -InfraredRemote* infrared_remote_alloc(); -void infrared_remote_free(InfraredRemote* remote); -void infrared_remote_reset(InfraredRemote* remote); - -void infrared_remote_set_name(InfraredRemote* remote, const char* name); -const char* infrared_remote_get_name(InfraredRemote* remote); - -void infrared_remote_set_path(InfraredRemote* remote, const char* path); -const char* infrared_remote_get_path(InfraredRemote* remote); - -size_t infrared_remote_get_button_count(InfraredRemote* remote); -InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index); -bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* name, size_t* index); - -bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal); -bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index); -bool infrared_remote_delete_button(InfraredRemote* remote, size_t index); - -bool infrared_remote_store(InfraredRemote* remote); -bool infrared_remote_load(InfraredRemote* remote, string_t path); -bool infrared_remote_remove(InfraredRemote* remote); diff --git a/applications/infrared/infrared_remote_button.c b/applications/infrared/infrared_remote_button.c deleted file mode 100644 index 7525ce48f7e..00000000000 --- a/applications/infrared/infrared_remote_button.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "infrared_remote_button.h" - -#include -#include - -struct InfraredRemoteButton { - string_t name; - InfraredSignal* signal; -}; - -InfraredRemoteButton* infrared_remote_button_alloc() { - InfraredRemoteButton* button = malloc(sizeof(InfraredRemoteButton)); - string_init(button->name); - button->signal = infrared_signal_alloc(); - return button; -} - -void infrared_remote_button_free(InfraredRemoteButton* button) { - string_clear(button->name); - infrared_signal_free(button->signal); - free(button); -} - -void infrared_remote_button_set_name(InfraredRemoteButton* button, const char* name) { - string_set_str(button->name, name); -} - -const char* infrared_remote_button_get_name(InfraredRemoteButton* button) { - return string_get_cstr(button->name); -} - -void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal) { - infrared_signal_set_signal(button->signal, signal); -} - -InfraredSignal* infrared_remote_button_get_signal(InfraredRemoteButton* button) { - return button->signal; -} diff --git a/applications/infrared/infrared_remote_button.h b/applications/infrared/infrared_remote_button.h deleted file mode 100644 index f25b759b566..00000000000 --- a/applications/infrared/infrared_remote_button.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "infrared_signal.h" - -typedef struct InfraredRemoteButton InfraredRemoteButton; - -InfraredRemoteButton* infrared_remote_button_alloc(); -void infrared_remote_button_free(InfraredRemoteButton* button); - -void infrared_remote_button_set_name(InfraredRemoteButton* button, const char* name); -const char* infrared_remote_button_get_name(InfraredRemoteButton* button); - -void infrared_remote_button_set_signal(InfraredRemoteButton* button, InfraredSignal* signal); -InfraredSignal* infrared_remote_button_get_signal(InfraredRemoteButton* button); diff --git a/applications/infrared/infrared_signal.c b/applications/infrared/infrared_signal.c deleted file mode 100644 index b76717dd35e..00000000000 --- a/applications/infrared/infrared_signal.c +++ /dev/null @@ -1,264 +0,0 @@ -#include "infrared_signal.h" - -#include -#include -#include -#include -#include - -#define TAG "InfraredSignal" - -struct InfraredSignal { - bool is_raw; - union { - InfraredMessage message; - InfraredRawSignal raw; - } payload; -}; - -static void infrared_signal_clear_timings(InfraredSignal* signal) { - if(signal->is_raw) { - free(signal->payload.raw.timings); - signal->payload.raw.timings_size = 0; - signal->payload.raw.timings = NULL; - } -} - -static bool infrared_signal_is_message_valid(InfraredMessage* message) { - if(!infrared_is_protocol_valid(message->protocol)) { - FURI_LOG_E(TAG, "Unknown protocol"); - return false; - } - - uint32_t address_length = infrared_get_protocol_address_length(message->protocol); - uint32_t address_mask = (1UL << address_length) - 1; - - if(message->address != (message->address & address_mask)) { - FURI_LOG_E( - TAG, - "Address is out of range (mask 0x%08lX): 0x%lX\r\n", - address_mask, - message->address); - return false; - } - - uint32_t command_length = infrared_get_protocol_command_length(message->protocol); - uint32_t command_mask = (1UL << command_length) - 1; - - if(message->command != (message->command & command_mask)) { - FURI_LOG_E( - TAG, - "Command is out of range (mask 0x%08lX): 0x%lX\r\n", - command_mask, - message->command); - return false; - } - - return true; -} - -static bool infrared_signal_is_raw_valid(InfraredRawSignal* raw) { - if((raw->frequency > INFRARED_MAX_FREQUENCY) || (raw->frequency < INFRARED_MIN_FREQUENCY)) { - FURI_LOG_E( - TAG, - "Frequency is out of range (%lX - %lX): %lX", - INFRARED_MIN_FREQUENCY, - INFRARED_MAX_FREQUENCY, - raw->frequency); - return false; - - } else if((raw->duty_cycle <= 0) || (raw->duty_cycle > 1)) { - FURI_LOG_E(TAG, "Duty cycle is out of range (0 - 1): %f", (double)raw->duty_cycle); - return false; - - } else if((raw->timings_size <= 0) || (raw->timings_size > MAX_TIMINGS_AMOUNT)) { - FURI_LOG_E( - TAG, - "Timings amount is out of range (0 - %lX): %lX", - MAX_TIMINGS_AMOUNT, - raw->timings_size); - return false; - } - - return true; -} - -static inline bool infrared_signal_save_message(InfraredMessage* message, FlipperFormat* ff) { - const char* protocol_name = infrared_get_protocol_name(message->protocol); - return flipper_format_write_string_cstr(ff, "type", "parsed") && - flipper_format_write_string_cstr(ff, "protocol", protocol_name) && - flipper_format_write_hex(ff, "address", (uint8_t*)&message->address, 4) && - flipper_format_write_hex(ff, "command", (uint8_t*)&message->command, 4); -} - -static inline bool infrared_signal_save_raw(InfraredRawSignal* raw, FlipperFormat* ff) { - furi_assert(raw->timings_size <= MAX_TIMINGS_AMOUNT); - return flipper_format_write_string_cstr(ff, "type", "raw") && - flipper_format_write_uint32(ff, "frequency", &raw->frequency, 1) && - flipper_format_write_float(ff, "duty_cycle", &raw->duty_cycle, 1) && - flipper_format_write_uint32(ff, "data", raw->timings, raw->timings_size); -} - -static inline bool infrared_signal_read_message(InfraredSignal* signal, FlipperFormat* ff) { - string_t buf; - string_init(buf); - bool success = false; - - do { - if(!flipper_format_read_string(ff, "protocol", buf)) break; - - InfraredMessage message; - message.protocol = infrared_get_protocol_by_name(string_get_cstr(buf)); - - success = flipper_format_read_hex(ff, "address", (uint8_t*)&message.address, 4) && - flipper_format_read_hex(ff, "command", (uint8_t*)&message.command, 4) && - infrared_signal_is_message_valid(&message); - - if(!success) break; - - infrared_signal_set_message(signal, &message); - } while(0); - - string_clear(buf); - return success; -} - -static inline bool infrared_signal_read_raw(InfraredSignal* signal, FlipperFormat* ff) { - uint32_t timings_size, frequency; - float duty_cycle; - - bool success = flipper_format_read_uint32(ff, "frequency", &frequency, 1) && - flipper_format_read_float(ff, "duty_cycle", &duty_cycle, 1) && - flipper_format_get_value_count(ff, "data", &timings_size); - - if(!success || timings_size > MAX_TIMINGS_AMOUNT) { - return false; - } - - uint32_t* timings = malloc(sizeof(uint32_t) * timings_size); - success = flipper_format_read_uint32(ff, "data", timings, timings_size); - - if(success) { - infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle); - } - - free(timings); - return success; -} - -InfraredSignal* infrared_signal_alloc() { - InfraredSignal* signal = malloc(sizeof(InfraredSignal)); - - signal->is_raw = false; - signal->payload.message.protocol = InfraredProtocolUnknown; - - return signal; -} - -void infrared_signal_free(InfraredSignal* signal) { - infrared_signal_clear_timings(signal); - free(signal); -} - -bool infrared_signal_is_raw(InfraredSignal* signal) { - return signal->is_raw; -} - -bool infrared_signal_is_valid(InfraredSignal* signal) { - return signal->is_raw ? infrared_signal_is_raw_valid(&signal->payload.raw) : - infrared_signal_is_message_valid(&signal->payload.message); -} - -void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other) { - if(other->is_raw) { - const InfraredRawSignal* raw = &other->payload.raw; - infrared_signal_set_raw_signal( - signal, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle); - } else { - const InfraredMessage* message = &other->payload.message; - infrared_signal_set_message(signal, message); - } -} - -void infrared_signal_set_raw_signal( - InfraredSignal* signal, - const uint32_t* timings, - size_t timings_size, - uint32_t frequency, - float duty_cycle) { - infrared_signal_clear_timings(signal); - - signal->is_raw = true; - - signal->payload.raw.timings_size = timings_size; - signal->payload.raw.frequency = frequency; - signal->payload.raw.duty_cycle = duty_cycle; - - signal->payload.raw.timings = malloc(timings_size * sizeof(uint32_t)); - memcpy(signal->payload.raw.timings, timings, timings_size * sizeof(uint32_t)); -} - -InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal) { - furi_assert(signal->is_raw); - return &signal->payload.raw; -} - -void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message) { - infrared_signal_clear_timings(signal); - - signal->is_raw = false; - signal->payload.message = *message; -} - -InfraredMessage* infrared_signal_get_message(InfraredSignal* signal) { - furi_assert(!signal->is_raw); - return &signal->payload.message; -} - -bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name) { - if(!flipper_format_write_comment_cstr(ff, "") || - !flipper_format_write_string_cstr(ff, "name", name)) { - return false; - } else if(signal->is_raw) { - return infrared_signal_save_raw(&signal->payload.raw, ff); - } else { - return infrared_signal_save_message(&signal->payload.message, ff); - } -} - -bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, string_t name) { - string_t buf; - string_init(buf); - bool success = false; - - do { - if(!flipper_format_read_string(ff, "name", buf)) break; - string_set(name, buf); - if(!flipper_format_read_string(ff, "type", buf)) break; - if(!string_cmp_str(buf, "raw")) { - success = infrared_signal_read_raw(signal, ff); - } else if(!string_cmp_str(buf, "parsed")) { - success = infrared_signal_read_message(signal, ff); - } else { - FURI_LOG_E(TAG, "Unknown type of signal (allowed - raw/parsed) "); - } - } while(0); - - string_clear(buf); - return success; -} - -void infrared_signal_transmit(InfraredSignal* signal) { - if(signal->is_raw) { - InfraredRawSignal* raw_signal = &signal->payload.raw; - infrared_send_raw_ext( - raw_signal->timings, - raw_signal->timings_size, - true, - raw_signal->frequency, - raw_signal->duty_cycle); - } else { - InfraredMessage* message = &signal->payload.message; - infrared_send(message, 1); - } -} diff --git a/applications/infrared/infrared_signal.h b/applications/infrared/infrared_signal.h deleted file mode 100644 index 2dbaa75faab..00000000000 --- a/applications/infrared/infrared_signal.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include - -typedef struct InfraredSignal InfraredSignal; - -typedef struct { - size_t timings_size; - uint32_t* timings; - uint32_t frequency; - float duty_cycle; -} InfraredRawSignal; - -InfraredSignal* infrared_signal_alloc(); -void infrared_signal_free(InfraredSignal* signal); - -bool infrared_signal_is_raw(InfraredSignal* signal); -bool infrared_signal_is_valid(InfraredSignal* signal); - -void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other); - -void infrared_signal_set_raw_signal( - InfraredSignal* signal, - const uint32_t* timings, - size_t timings_size, - uint32_t frequency, - float duty_cycle); -InfraredRawSignal* infrared_signal_get_raw_signal(InfraredSignal* signal); - -void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message); -InfraredMessage* infrared_signal_get_message(InfraredSignal* signal); - -bool infrared_signal_save(InfraredSignal* signal, FlipperFormat* ff, const char* name); -bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, string_t name); - -void infrared_signal_transmit(InfraredSignal* signal); diff --git a/applications/infrared/scenes/infrared_scene_edit_delete.c b/applications/infrared/scenes/infrared_scene_edit_delete.c deleted file mode 100644 index 4dfc054fbd2..00000000000 --- a/applications/infrared/scenes/infrared_scene_edit_delete.c +++ /dev/null @@ -1,112 +0,0 @@ -#include "../infrared_i.h" - -static void - infrared_scene_edit_delete_dialog_result_callback(DialogExResult result, void* context) { - Infrared* infrared = context; - view_dispatcher_send_custom_event(infrared->view_dispatcher, result); -} - -void infrared_scene_edit_delete_on_enter(void* context) { - Infrared* infrared = context; - DialogEx* dialog_ex = infrared->dialog_ex; - InfraredRemote* remote = infrared->remote; - - const InfraredEditTarget edit_target = infrared->app_state.edit_target; - if(edit_target == InfraredEditTargetButton) { - int32_t current_button_index = infrared->app_state.current_button_index; - furi_assert(current_button_index != InfraredButtonIndexNone); - - dialog_ex_set_header(dialog_ex, "Delete Button?", 64, 0, AlignCenter, AlignTop); - InfraredRemoteButton* current_button = - infrared_remote_get_button(remote, current_button_index); - InfraredSignal* signal = infrared_remote_button_get_signal(current_button); - - if(infrared_signal_is_raw(signal)) { - const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); - infrared_text_store_set( - infrared, - 0, - "%s\nRAW\n%ld samples", - infrared_remote_button_get_name(current_button), - raw->timings_size); - - } else { - const InfraredMessage* message = infrared_signal_get_message(signal); - infrared_text_store_set( - infrared, - 0, - "%s\n%s\nA=0x%0*lX C=0x%0*lX", - infrared_remote_button_get_name(current_button), - infrared_get_protocol_name(message->protocol), - ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4), - message->address, - ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), - message->command); - } - - } else if(edit_target == InfraredEditTargetRemote) { - dialog_ex_set_header(dialog_ex, "Delete Remote?", 64, 0, AlignCenter, AlignTop); - infrared_text_store_set( - infrared, - 0, - "%s\n with %lu buttons", - infrared_remote_get_name(remote), - infrared_remote_get_button_count(remote)); - } else { - furi_assert(0); - } - - dialog_ex_set_text(dialog_ex, infrared->text_store[0], 64, 31, AlignCenter, AlignCenter); - dialog_ex_set_icon(dialog_ex, 0, 0, NULL); - dialog_ex_set_left_button_text(dialog_ex, "Cancel"); - dialog_ex_set_right_button_text(dialog_ex, "Delete"); - dialog_ex_set_result_callback(dialog_ex, infrared_scene_edit_delete_dialog_result_callback); - dialog_ex_set_context(dialog_ex, context); - - view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx); -} - -bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; - SceneManager* scene_manager = infrared->scene_manager; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == DialogExResultLeft) { - scene_manager_previous_scene(scene_manager); - consumed = true; - } else if(event.event == DialogExResultRight) { - bool success = false; - InfraredRemote* remote = infrared->remote; - InfraredAppState* app_state = &infrared->app_state; - const InfraredEditTarget edit_target = app_state->edit_target; - - if(edit_target == InfraredEditTargetButton) { - furi_assert(app_state->current_button_index != InfraredButtonIndexNone); - success = infrared_remote_delete_button(remote, app_state->current_button_index); - app_state->current_button_index = InfraredButtonIndexNone; - } else if(edit_target == InfraredEditTargetRemote) { - success = infrared_remote_remove(remote); - app_state->current_button_index = InfraredButtonIndexNone; - } else { - furi_assert(0); - } - - if(success) { - scene_manager_next_scene(scene_manager, InfraredSceneEditDeleteDone); - } else { - const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; - scene_manager_search_and_switch_to_previous_scene_one_of( - scene_manager, possible_scenes, COUNT_OF(possible_scenes)); - } - consumed = true; - } - } - - return consumed; -} - -void infrared_scene_edit_delete_on_exit(void* context) { - Infrared* infrared = context; - UNUSED(infrared); -} diff --git a/applications/infrared/scenes/infrared_scene_edit_rename.c b/applications/infrared/scenes/infrared_scene_edit_rename.c deleted file mode 100644 index ebe7c2a96d4..00000000000 --- a/applications/infrared/scenes/infrared_scene_edit_rename.c +++ /dev/null @@ -1,107 +0,0 @@ -#include "../infrared_i.h" - -#include -#include - -void infrared_scene_edit_rename_on_enter(void* context) { - Infrared* infrared = context; - InfraredRemote* remote = infrared->remote; - TextInput* text_input = infrared->text_input; - size_t enter_name_length = 0; - - const InfraredEditTarget edit_target = infrared->app_state.edit_target; - if(edit_target == InfraredEditTargetButton) { - text_input_set_header_text(text_input, "Name the button"); - - const int32_t current_button_index = infrared->app_state.current_button_index; - furi_assert(current_button_index != InfraredButtonIndexNone); - - InfraredRemoteButton* current_button = - infrared_remote_get_button(remote, current_button_index); - enter_name_length = INFRARED_MAX_BUTTON_NAME_LENGTH; - strncpy( - infrared->text_store[0], - infrared_remote_button_get_name(current_button), - enter_name_length); - - } else if(edit_target == InfraredEditTargetRemote) { - text_input_set_header_text(text_input, "Name the remote"); - enter_name_length = INFRARED_MAX_REMOTE_NAME_LENGTH; - strncpy(infrared->text_store[0], infrared_remote_get_name(remote), enter_name_length); - - string_t folder_path; - string_init(folder_path); - - if(string_end_with_str_p(infrared->file_path, INFRARED_APP_EXTENSION)) { - path_extract_dirname(string_get_cstr(infrared->file_path), folder_path); - } - - ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - string_get_cstr(folder_path), - INFRARED_APP_EXTENSION, - infrared_remote_get_name(remote)); - text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); - - string_clear(folder_path); - } else { - furi_assert(0); - } - - text_input_set_result_callback( - text_input, - infrared_text_input_callback, - context, - infrared->text_store[0], - enter_name_length, - false); - - view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewTextInput); -} - -bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; - InfraredRemote* remote = infrared->remote; - SceneManager* scene_manager = infrared->scene_manager; - InfraredAppState* app_state = &infrared->app_state; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == InfraredCustomEventTypeTextEditDone) { - bool success = false; - const InfraredEditTarget edit_target = app_state->edit_target; - if(edit_target == InfraredEditTargetButton) { - const int32_t current_button_index = app_state->current_button_index; - furi_assert(current_button_index != InfraredButtonIndexNone); - success = infrared_remote_rename_button( - remote, infrared->text_store[0], current_button_index); - app_state->current_button_index = InfraredButtonIndexNone; - } else if(edit_target == InfraredEditTargetRemote) { - success = infrared_rename_current_remote(infrared, infrared->text_store[0]); - } else { - furi_assert(0); - } - - if(success) { - scene_manager_next_scene(scene_manager, InfraredSceneEditRenameDone); - } else { - scene_manager_search_and_switch_to_previous_scene( - scene_manager, InfraredSceneRemoteList); - } - consumed = true; - } - } - - return consumed; -} - -void infrared_scene_edit_rename_on_exit(void* context) { - Infrared* infrared = context; - TextInput* text_input = infrared->text_input; - - void* validator_context = text_input_get_validator_callback_context(text_input); - text_input_set_validator(text_input, NULL, NULL); - - if(validator_context) { - validator_is_file_free((ValidatorIsFile*)validator_context); - } -} diff --git a/applications/infrared/scenes/infrared_scene_learn_enter_name.c b/applications/infrared/scenes/infrared_scene_learn_enter_name.c deleted file mode 100644 index b7f4179ea2d..00000000000 --- a/applications/infrared/scenes/infrared_scene_learn_enter_name.c +++ /dev/null @@ -1,66 +0,0 @@ -#include "../infrared_i.h" - -void infrared_scene_learn_enter_name_on_enter(void* context) { - Infrared* infrared = context; - TextInput* text_input = infrared->text_input; - InfraredSignal* signal = infrared->received_signal; - - if(infrared_signal_is_raw(signal)) { - InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); - infrared_text_store_set(infrared, 0, "RAW_%d", raw->timings_size); - } else { - InfraredMessage* message = infrared_signal_get_message(signal); - infrared_text_store_set( - infrared, - 0, - "%.4s_%0*lX", - infrared_get_protocol_name(message->protocol), - ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), - message->command); - } - - text_input_set_header_text(text_input, "Name the button"); - text_input_set_result_callback( - text_input, - infrared_text_input_callback, - context, - infrared->text_store[0], - INFRARED_MAX_BUTTON_NAME_LENGTH, - true); - view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewTextInput); -} - -bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; - InfraredSignal* signal = infrared->received_signal; - SceneManager* scene_manager = infrared->scene_manager; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == InfraredCustomEventTypeTextEditDone) { - bool success = false; - if(infrared->app_state.is_learning_new_remote) { - success = - infrared_add_remote_with_button(infrared, infrared->text_store[0], signal); - } else { - success = - infrared_remote_add_button(infrared->remote, infrared->text_store[0], signal); - } - - if(success) { - scene_manager_next_scene(scene_manager, InfraredSceneLearnDone); - } else { - scene_manager_search_and_switch_to_previous_scene( - scene_manager, InfraredSceneRemoteList); - } - consumed = true; - } - } - - return consumed; -} - -void infrared_scene_learn_enter_name_on_exit(void* context) { - Infrared* infrared = context; - UNUSED(infrared); -} diff --git a/applications/infrared/scenes/infrared_scene_remote_list.c b/applications/infrared/scenes/infrared_scene_remote_list.c deleted file mode 100644 index b0038c1a33f..00000000000 --- a/applications/infrared/scenes/infrared_scene_remote_list.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "../infrared_i.h" - -void infrared_scene_remote_list_on_enter(void* context) { - Infrared* infrared = context; - SceneManager* scene_manager = infrared->scene_manager; - ViewDispatcher* view_dispatcher = infrared->view_dispatcher; - - bool success = dialog_file_browser_show( - infrared->dialogs, - infrared->file_path, - infrared->file_path, - INFRARED_APP_EXTENSION, - true, - &I_ir_10px, - true); - - if(success) { - view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); - view_dispatcher_switch_to_view(view_dispatcher, InfraredViewStack); - - infrared_show_loading_popup(infrared, true); - success = infrared_remote_load(infrared->remote, infrared->file_path); - infrared_show_loading_popup(infrared, false); - } - - if(success) { - scene_manager_next_scene(scene_manager, InfraredSceneRemote); - } else { - scene_manager_previous_scene(scene_manager); - } -} - -bool infrared_scene_remote_list_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - bool consumed = false; - - return consumed; -} - -void infrared_scene_remote_list_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/infrared/scenes/infrared_scene_rpc.c b/applications/infrared/scenes/infrared_scene_rpc.c deleted file mode 100644 index ca7bbd8df45..00000000000 --- a/applications/infrared/scenes/infrared_scene_rpc.c +++ /dev/null @@ -1,100 +0,0 @@ -#include "../infrared_i.h" -#include "gui/canvas.h" - -typedef enum { - InfraredRpcStateIdle, - InfraredRpcStateLoaded, - InfraredRpcStateSending, -} InfraredRpcState; - -void infrared_scene_rpc_on_enter(void* context) { - Infrared* infrared = context; - Popup* popup = infrared->popup; - - popup_set_header(popup, "Infrared", 89, 42, AlignCenter, AlignBottom); - popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop); - - popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61); - - popup_set_context(popup, context); - popup_set_callback(popup, infrared_popup_closed_callback); - - view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup); - - scene_manager_set_scene_state(infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateIdle); - - notification_message(infrared->notifications, &sequence_display_backlight_on); -} - -bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - InfraredRpcState state = - scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneRpc); - if(event.event == InfraredCustomEventTypeBackPressed) { - view_dispatcher_stop(infrared->view_dispatcher); - } else if(event.event == InfraredCustomEventTypePopupClosed) { - view_dispatcher_stop(infrared->view_dispatcher); - } else if(event.event == InfraredCustomEventTypeRpcLoad) { - bool result = false; - const char* arg = rpc_system_app_get_data(infrared->rpc_ctx); - if(arg && (state == InfraredRpcStateIdle)) { - string_set_str(infrared->file_path, arg); - result = infrared_remote_load(infrared->remote, infrared->file_path); - if(result) { - scene_manager_set_scene_state( - infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded); - } - } - const char* remote_name = infrared_remote_get_name(infrared->remote); - - infrared_text_store_set(infrared, 0, "loaded\n%s", remote_name); - popup_set_text( - infrared->popup, infrared->text_store[0], 89, 44, AlignCenter, AlignTop); - - rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventLoadFile, result); - } else if(event.event == InfraredCustomEventTypeRpcButtonPress) { - bool result = false; - const char* arg = rpc_system_app_get_data(infrared->rpc_ctx); - if(arg && (state == InfraredRpcStateLoaded)) { - size_t button_index = 0; - if(infrared_remote_find_button_by_name(infrared->remote, arg, &button_index)) { - infrared_tx_start_button_index(infrared, button_index); - result = true; - scene_manager_set_scene_state( - infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateSending); - } - } - rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, result); - } else if(event.event == InfraredCustomEventTypeRpcButtonRelease) { - bool result = false; - if(state == InfraredRpcStateSending) { - infrared_tx_stop(infrared); - result = true; - scene_manager_set_scene_state( - infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded); - } - rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, result); - } else if(event.event == InfraredCustomEventTypeRpcExit) { - scene_manager_stop(infrared->scene_manager); - view_dispatcher_stop(infrared->view_dispatcher); - rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventAppExit, true); - } else if(event.event == InfraredCustomEventTypeRpcSessionClose) { - scene_manager_stop(infrared->scene_manager); - view_dispatcher_stop(infrared->view_dispatcher); - } - } - return consumed; -} - -void infrared_scene_rpc_on_exit(void* context) { - Infrared* infrared = context; - if(scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneRpc) == - InfraredRpcStateSending) { - infrared_tx_stop(infrared); - } - popup_reset(infrared->popup); -} diff --git a/applications/infrared/scenes/infrared_scene_universal.c b/applications/infrared/scenes/infrared_scene_universal.c deleted file mode 100644 index cc6568834cc..00000000000 --- a/applications/infrared/scenes/infrared_scene_universal.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "../infrared_i.h" - -typedef enum { - SubmenuIndexUniversalTV, - SubmenuIndexUniversalAudio, - SubmenuIndexUniversalAirConditioner, -} SubmenuIndex; - -static void infrared_scene_universal_submenu_callback(void* context, uint32_t index) { - Infrared* infrared = context; - view_dispatcher_send_custom_event(infrared->view_dispatcher, index); -} - -void infrared_scene_universal_on_enter(void* context) { - Infrared* infrared = context; - Submenu* submenu = infrared->submenu; - - submenu_add_item( - submenu, - "TVs", - SubmenuIndexUniversalTV, - infrared_scene_universal_submenu_callback, - context); - submenu_set_selected_item(submenu, 0); - - view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu); -} - -bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; - SceneManager* scene_manager = infrared->scene_manager; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexUniversalTV) { - scene_manager_next_scene(scene_manager, InfraredSceneUniversalTV); - consumed = true; - } else if(event.event == SubmenuIndexUniversalAudio) { - //TODO Implement Audio universal remote - consumed = true; - } else if(event.event == SubmenuIndexUniversalAirConditioner) { - //TODO Implement A/C universal remote - consumed = true; - } - } - - return consumed; -} - -void infrared_scene_universal_on_exit(void* context) { - Infrared* infrared = context; - submenu_reset(infrared->submenu); -} diff --git a/applications/infrared/scenes/infrared_scene_universal_tv.c b/applications/infrared/scenes/infrared_scene_universal_tv.c deleted file mode 100644 index 583f21fa34a..00000000000 --- a/applications/infrared/scenes/infrared_scene_universal_tv.c +++ /dev/null @@ -1,111 +0,0 @@ -#include "../infrared_i.h" - -#include "common/infrared_scene_universal_common.h" - -void infrared_scene_universal_tv_on_enter(void* context) { - infrared_scene_universal_common_on_enter(context); - - Infrared* infrared = context; - ButtonPanel* button_panel = infrared->button_panel; - InfraredBruteForce* brute_force = infrared->brute_force; - - infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/tv.ir")); - - button_panel_reserve(button_panel, 2, 3); - uint32_t i = 0; - button_panel_add_item( - button_panel, - i, - 0, - 0, - 3, - 19, - &I_Power_25x27, - &I_Power_hvr_25x27, - infrared_scene_universal_common_item_callback, - context); - infrared_brute_force_add_record(brute_force, i++, "POWER"); - button_panel_add_item( - button_panel, - i, - 1, - 0, - 36, - 19, - &I_Mute_25x27, - &I_Mute_hvr_25x27, - infrared_scene_universal_common_item_callback, - context); - infrared_brute_force_add_record(brute_force, i++, "MUTE"); - button_panel_add_item( - button_panel, - i, - 0, - 1, - 3, - 66, - &I_Vol_up_25x27, - &I_Vol_up_hvr_25x27, - infrared_scene_universal_common_item_callback, - context); - infrared_brute_force_add_record(brute_force, i++, "VOL+"); - button_panel_add_item( - button_panel, - i, - 1, - 1, - 36, - 66, - &I_Up_25x27, - &I_Up_hvr_25x27, - infrared_scene_universal_common_item_callback, - context); - infrared_brute_force_add_record(brute_force, i++, "CH+"); - button_panel_add_item( - button_panel, - i, - 0, - 2, - 3, - 98, - &I_Vol_down_25x27, - &I_Vol_down_hvr_25x27, - infrared_scene_universal_common_item_callback, - context); - infrared_brute_force_add_record(brute_force, i++, "VOL-"); - button_panel_add_item( - button_panel, - i, - 1, - 2, - 36, - 98, - &I_Down_25x27, - &I_Down_hvr_25x27, - infrared_scene_universal_common_item_callback, - context); - infrared_brute_force_add_record(brute_force, i++, "CH-"); - - button_panel_add_label(button_panel, 6, 11, FontPrimary, "TV remote"); - button_panel_add_label(button_panel, 9, 64, FontSecondary, "Vol"); - button_panel_add_label(button_panel, 43, 64, FontSecondary, "Ch"); - - view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); - view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); - - infrared_show_loading_popup(infrared, true); - bool success = infrared_brute_force_calculate_messages(brute_force); - infrared_show_loading_popup(infrared, false); - - if(!success) { - scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases); - } -} - -bool infrared_scene_universal_tv_on_event(void* context, SceneManagerEvent event) { - return infrared_scene_universal_common_on_event(context, event); -} - -void infrared_scene_universal_tv_on_exit(void* context) { - infrared_scene_universal_common_on_exit(context); -} diff --git a/applications/input/application.fam b/applications/input/application.fam deleted file mode 100644 index 545630e6ef3..00000000000 --- a/applications/input/application.fam +++ /dev/null @@ -1,9 +0,0 @@ -App( - appid="input", - name="InputSrv", - apptype=FlipperAppType.SERVICE, - entry_point="input_srv", - cdefines=["SRV_INPUT"], - stack_size=1 * 1024, - order=80, -) diff --git a/applications/input/input.c b/applications/input/input.c deleted file mode 100644 index 7b8433aeffe..00000000000 --- a/applications/input/input.c +++ /dev/null @@ -1,138 +0,0 @@ -#include "input_i.h" - -#define GPIO_Read(input_pin) (furi_hal_gpio_read(input_pin.pin->gpio) ^ (input_pin.pin->inverted)) - -static Input* input = NULL; - -inline static void input_timer_start(FuriTimer* timer_id, uint32_t ticks) { - TimerHandle_t hTimer = (TimerHandle_t)timer_id; - furi_check(xTimerChangePeriod(hTimer, ticks, portMAX_DELAY) == pdPASS); -} - -inline static void input_timer_stop(FuriTimer* timer_id) { - TimerHandle_t hTimer = (TimerHandle_t)timer_id; - furi_check(xTimerStop(hTimer, portMAX_DELAY) == pdPASS); - // xTimerStop is not actually stopping timer, - // Instead it places stop event into timer queue - // This code ensures that timer is stopped - while(xTimerIsTimerActive(hTimer) == pdTRUE) furi_delay_tick(1); -} - -void input_press_timer_callback(void* arg) { - InputPinState* input_pin = arg; - InputEvent event; - event.sequence = input_pin->counter; - event.key = input_pin->pin->key; - input_pin->press_counter++; - if(input_pin->press_counter == INPUT_LONG_PRESS_COUNTS) { - event.type = InputTypeLong; - furi_pubsub_publish(input->event_pubsub, &event); - } else if(input_pin->press_counter > INPUT_LONG_PRESS_COUNTS) { - input_pin->press_counter--; - event.type = InputTypeRepeat; - furi_pubsub_publish(input->event_pubsub, &event); - } -} - -void input_isr(void* _ctx) { - UNUSED(_ctx); - furi_thread_flags_set(input->thread_id, INPUT_THREAD_FLAG_ISR); -} - -const char* input_get_key_name(InputKey key) { - for(size_t i = 0; i < input_pins_count; i++) { - if(input_pins[i].key == key) { - return input_pins[i].name; - } - } - return "Unknown"; -} - -const char* input_get_type_name(InputType type) { - switch(type) { - case InputTypePress: - return "Press"; - case InputTypeRelease: - return "Release"; - case InputTypeShort: - return "Short"; - case InputTypeLong: - return "Long"; - case InputTypeRepeat: - return "Repeat"; - } - return "Unknown"; -} - -int32_t input_srv(void* p) { - UNUSED(p); - input = malloc(sizeof(Input)); - input->thread_id = furi_thread_get_current_id(); - input->event_pubsub = furi_pubsub_alloc(); - furi_record_create(RECORD_INPUT_EVENTS, input->event_pubsub); - -#ifdef SRV_CLI - input->cli = furi_record_open(RECORD_CLI); - if(input->cli) { - cli_add_command(input->cli, "input", CliCommandFlagParallelSafe, input_cli, input); - } -#endif - - input->pin_states = malloc(input_pins_count * sizeof(InputPinState)); - - for(size_t i = 0; i < input_pins_count; i++) { - furi_hal_gpio_add_int_callback(input_pins[i].gpio, input_isr, NULL); - input->pin_states[i].pin = &input_pins[i]; - input->pin_states[i].state = GPIO_Read(input->pin_states[i]); - input->pin_states[i].debounce = INPUT_DEBOUNCE_TICKS_HALF; - input->pin_states[i].press_timer = furi_timer_alloc( - input_press_timer_callback, FuriTimerTypePeriodic, &input->pin_states[i]); - input->pin_states[i].press_counter = 0; - } - - while(1) { - bool is_changing = false; - for(size_t i = 0; i < input_pins_count; i++) { - bool state = GPIO_Read(input->pin_states[i]); - if(input->pin_states[i].debounce > 0 && - input->pin_states[i].debounce < INPUT_DEBOUNCE_TICKS) { - is_changing = true; - input->pin_states[i].debounce += (state ? 1 : -1); - } else if(input->pin_states[i].state != state) { - input->pin_states[i].state = state; - - // Common state info - InputEvent event; - event.key = input->pin_states[i].pin->key; - - // Short / Long / Repeat timer routine - if(state) { - input->counter++; - input->pin_states[i].counter = input->counter; - event.sequence = input->pin_states[i].counter; - input_timer_start(input->pin_states[i].press_timer, INPUT_PRESS_TICKS); - } else { - event.sequence = input->pin_states[i].counter; - input_timer_stop(input->pin_states[i].press_timer); - if(input->pin_states[i].press_counter < INPUT_LONG_PRESS_COUNTS) { - event.type = InputTypeShort; - furi_pubsub_publish(input->event_pubsub, &event); - } - input->pin_states[i].press_counter = 0; - } - - // Send Press/Release event - event.type = input->pin_states[i].state ? InputTypePress : InputTypeRelease; - furi_pubsub_publish(input->event_pubsub, &event); - } - } - - if(is_changing) { - furi_delay_tick(1); - } else { - furi_thread_flags_wait(INPUT_THREAD_FLAG_ISR, FuriFlagWaitAny, FuriWaitForever); - } - } - - return 0; -} diff --git a/applications/input/input.h b/applications/input/input.h deleted file mode 100644 index 001ab1e2e83..00000000000 --- a/applications/input/input.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @file input.h - * Input: main API - */ - -#pragma once - -#include - -#define RECORD_INPUT_EVENTS "input_events" - -/** Input Types - * Some of them are physical events and some logical - */ -typedef enum { - InputTypePress, /**< Press event, emitted after debounce */ - InputTypeRelease, /**< Release event, emitted after debounce */ - InputTypeShort, /**< Short event, emitted after InputTypeRelease done withing INPUT_LONG_PRESS interval */ - InputTypeLong, /**< Long event, emmited after INPUT_LONG_PRESS interval, asynchronouse to InputTypeRelease */ - InputTypeRepeat, /**< Repeat event, emmited with INPUT_REPEATE_PRESS period after InputTypeLong event */ -} InputType; - -/** Input Event, dispatches with FuriPubSub */ -typedef struct { - uint32_t sequence; - InputKey key; - InputType type; -} InputEvent; - -/** Get human readable input key name - * @param key - InputKey - * @return string - */ -const char* input_get_key_name(InputKey key); - -/** Get human readable input type name - * @param type - InputType - * @return string - */ -const char* input_get_type_name(InputType type); diff --git a/applications/input/input_cli.c b/applications/input/input_cli.c deleted file mode 100644 index 037ac53e52b..00000000000 --- a/applications/input/input_cli.c +++ /dev/null @@ -1,125 +0,0 @@ -#include "input_i.h" - -#include -#include -#include - -static void input_cli_usage() { - printf("Usage:\r\n"); - printf("input \r\n"); - printf("Cmd list:\r\n"); - printf("\tdump\t\t\t - dump input events\r\n"); - printf("\tsend \t - send input event\r\n"); -} - -static void input_cli_dump_events_callback(const void* value, void* ctx) { - furi_assert(value); - furi_assert(ctx); - FuriMessageQueue* input_queue = ctx; - furi_message_queue_put(input_queue, value, FuriWaitForever); -} - -static void input_cli_dump(Cli* cli, string_t args, Input* input) { - UNUSED(args); - FuriMessageQueue* input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); - FuriPubSubSubscription* input_subscription = - furi_pubsub_subscribe(input->event_pubsub, input_cli_dump_events_callback, input_queue); - - InputEvent input_event; - printf("Press CTRL+C to stop\r\n"); - while(!cli_cmd_interrupt_received(cli)) { - if(furi_message_queue_get(input_queue, &input_event, 100) == FuriStatusOk) { - printf( - "key: %s type: %s\r\n", - input_get_key_name(input_event.key), - input_get_type_name(input_event.type)); - } - } - - furi_pubsub_unsubscribe(input->event_pubsub, input_subscription); - furi_message_queue_free(input_queue); -} - -static void input_cli_send_print_usage() { - printf("Invalid arguments. Usage:\r\n"); - printf("\tinput send \r\n"); - printf("\t\t \t - one of 'up', 'down', 'left', 'right', 'back', 'ok'\r\n"); - printf("\t\t \t - one of 'press', 'release', 'short', 'long'\r\n"); -} - -static void input_cli_send(Cli* cli, string_t args, Input* input) { - UNUSED(cli); - InputEvent event; - string_t key_str; - string_init(key_str); - bool parsed = false; - - do { - // Parse Key - if(!args_read_string_and_trim(args, key_str)) { - break; - } - if(!string_cmp(key_str, "up")) { - event.key = InputKeyUp; - } else if(!string_cmp(key_str, "down")) { - event.key = InputKeyDown; - } else if(!string_cmp(key_str, "left")) { - event.key = InputKeyLeft; - } else if(!string_cmp(key_str, "right")) { - event.key = InputKeyRight; - } else if(!string_cmp(key_str, "ok")) { - event.key = InputKeyOk; - } else if(!string_cmp(key_str, "back")) { - event.key = InputKeyBack; - } else { - break; - } - // Parse Type - if(!string_cmp(args, "press")) { - event.type = InputTypePress; - } else if(!string_cmp(args, "release")) { - event.type = InputTypeRelease; - } else if(!string_cmp(args, "short")) { - event.type = InputTypeShort; - } else if(!string_cmp(args, "long")) { - event.type = InputTypeLong; - } else { - break; - } - parsed = true; - } while(false); - - if(parsed) { - furi_pubsub_publish(input->event_pubsub, &event); - } else { - input_cli_send_print_usage(); - } - string_clear(key_str); -} - -void input_cli(Cli* cli, string_t args, void* context) { - furi_assert(cli); - furi_assert(context); - Input* input = context; - string_t cmd; - string_init(cmd); - - do { - if(!args_read_string_and_trim(args, cmd)) { - input_cli_usage(); - break; - } - if(string_cmp_str(cmd, "dump") == 0) { - input_cli_dump(cli, args, input); - break; - } - if(string_cmp_str(cmd, "send") == 0) { - input_cli_send(cli, args, input); - break; - } - - input_cli_usage(); - } while(false); - - string_clear(cmd); -} diff --git a/applications/lfrfid/application.fam b/applications/lfrfid/application.fam deleted file mode 100644 index 8722bb4f8cd..00000000000 --- a/applications/lfrfid/application.fam +++ /dev/null @@ -1,26 +0,0 @@ -App( - appid="lfrfid", - name="125 kHz RFID", - apptype=FlipperAppType.APP, - entry_point="lfrfid_app", - cdefines=["APP_LF_RFID"], - requires=[ - "gui", - "dialogs", - ], - provides=[ - "lfrfid_start", - "lfrfid_debug", - ], - icon="A_125khz_14", - stack_size=2 * 1024, - order=20, -) - -App( - appid="lfrfid_start", - apptype=FlipperAppType.STARTUP, - entry_point="lfrfid_on_system_start", - requires=["lfrfid"], - order=50, -) diff --git a/applications/lfrfid/helpers/decoder_analyzer.cpp b/applications/lfrfid/helpers/decoder_analyzer.cpp deleted file mode 100644 index 8d344b8809a..00000000000 --- a/applications/lfrfid/helpers/decoder_analyzer.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "decoder_analyzer.h" -#include -#include - -// FIXME: unused args? -bool DecoderAnalyzer::read(uint8_t* /* _data */, uint8_t /* _data_size */) { - bool result = false; - - if(ready) { - result = true; - - for(size_t i = 0; i < data_size; i++) { - printf("%lu ", data[i]); - if((i + 1) % 8 == 0) printf("\r\n"); - } - printf("\r\n--------\r\n"); - - ready = false; - } - - return result; -} - -void DecoderAnalyzer::process_front(bool polarity, uint32_t time) { - UNUSED(polarity); - if(ready) return; - - data[data_index] = time; - - if(data_index < data_size) { - data_index++; - } else { - data_index = 0; - ready = true; - } -} - -DecoderAnalyzer::DecoderAnalyzer() { - data = reinterpret_cast(calloc(data_size, sizeof(uint32_t))); - furi_check(data); - data_index = 0; - ready = false; -} - -DecoderAnalyzer::~DecoderAnalyzer() { - free(data); -} - -void DecoderAnalyzer::reset_state() { -} diff --git a/applications/lfrfid/helpers/decoder_analyzer.h b/applications/lfrfid/helpers/decoder_analyzer.h deleted file mode 100644 index eecea6edd92..00000000000 --- a/applications/lfrfid/helpers/decoder_analyzer.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include -#include - -class DecoderAnalyzer { -public: - bool read(uint8_t* data, uint8_t data_size); - void process_front(bool polarity, uint32_t time); - - DecoderAnalyzer(); - ~DecoderAnalyzer(); - -private: - void reset_state(); - - std::atomic ready; - - static const uint32_t data_size = 2048; - uint32_t data_index = 0; - uint32_t* data; -}; diff --git a/applications/lfrfid/helpers/decoder_emmarin.cpp b/applications/lfrfid/helpers/decoder_emmarin.cpp deleted file mode 100644 index daa8e238c23..00000000000 --- a/applications/lfrfid/helpers/decoder_emmarin.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "emmarin.h" -#include "decoder_emmarin.h" -#include -#include - -constexpr uint32_t clocks_in_us = 64; -constexpr uint32_t short_time = 255 * clocks_in_us; -constexpr uint32_t long_time = 510 * clocks_in_us; -constexpr uint32_t jitter_time = 100 * clocks_in_us; - -constexpr uint32_t short_time_low = short_time - jitter_time; -constexpr uint32_t short_time_high = short_time + jitter_time; -constexpr uint32_t long_time_low = long_time - jitter_time; -constexpr uint32_t long_time_high = long_time + jitter_time; - -void DecoderEMMarin::reset_state() { - ready = false; - read_data = 0; - manchester_advance( - manchester_saved_state, ManchesterEventReset, &manchester_saved_state, nullptr); -} - -bool DecoderEMMarin::read(uint8_t* data, uint8_t data_size) { - bool result = false; - - if(ready) { - result = true; - em_marin.decode( - reinterpret_cast(&read_data), sizeof(uint64_t), data, data_size); - ready = false; - } - - return result; -} - -void DecoderEMMarin::process_front(bool polarity, uint32_t time) { - if(ready) return; - if(time < short_time_low) return; - - ManchesterEvent event = ManchesterEventReset; - - if(time > short_time_low && time < short_time_high) { - if(polarity) { - event = ManchesterEventShortHigh; - } else { - event = ManchesterEventShortLow; - } - } else if(time > long_time_low && time < long_time_high) { - if(polarity) { - event = ManchesterEventLongHigh; - } else { - event = ManchesterEventLongLow; - } - } - - if(event != ManchesterEventReset) { - bool data; - bool data_ok = - manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data); - - if(data_ok) { - read_data = (read_data << 1) | data; - - ready = em_marin.can_be_decoded( - reinterpret_cast(&read_data), sizeof(uint64_t)); - } - } -} - -DecoderEMMarin::DecoderEMMarin() { - reset_state(); -} diff --git a/applications/lfrfid/helpers/decoder_emmarin.h b/applications/lfrfid/helpers/decoder_emmarin.h deleted file mode 100644 index a3b48d71ce1..00000000000 --- a/applications/lfrfid/helpers/decoder_emmarin.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include -#include -#include -#include "protocols/protocol_emmarin.h" -class DecoderEMMarin { -public: - bool read(uint8_t* data, uint8_t data_size); - void process_front(bool polarity, uint32_t time); - - DecoderEMMarin(); - -private: - void reset_state(); - - uint64_t read_data = 0; - std::atomic ready; - - ManchesterState manchester_saved_state; - ProtocolEMMarin em_marin; -}; diff --git a/applications/lfrfid/helpers/decoder_gpio_out.cpp b/applications/lfrfid/helpers/decoder_gpio_out.cpp deleted file mode 100644 index dfb43426725..00000000000 --- a/applications/lfrfid/helpers/decoder_gpio_out.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "decoder_gpio_out.h" -#include -#include - -void DecoderGpioOut::process_front(bool polarity, uint32_t /* time */) { - furi_hal_gpio_write(&gpio_ext_pa7, polarity); -} - -DecoderGpioOut::DecoderGpioOut() { - furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull); -} - -DecoderGpioOut::~DecoderGpioOut() { - furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog); -} diff --git a/applications/lfrfid/helpers/decoder_gpio_out.h b/applications/lfrfid/helpers/decoder_gpio_out.h deleted file mode 100644 index 087720dfde7..00000000000 --- a/applications/lfrfid/helpers/decoder_gpio_out.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include -#include - -class DecoderGpioOut { -public: - void process_front(bool polarity, uint32_t time); - - DecoderGpioOut(); - ~DecoderGpioOut(); - -private: - void reset_state(); -}; diff --git a/applications/lfrfid/helpers/decoder_hid26.cpp b/applications/lfrfid/helpers/decoder_hid26.cpp deleted file mode 100644 index e530b508927..00000000000 --- a/applications/lfrfid/helpers/decoder_hid26.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include "decoder_hid26.h" -#include - -constexpr uint32_t clocks_in_us = 64; - -constexpr uint32_t jitter_time_us = 20; -constexpr uint32_t min_time_us = 64; -constexpr uint32_t max_time_us = 80; - -constexpr uint32_t min_time = (min_time_us - jitter_time_us) * clocks_in_us; -constexpr uint32_t mid_time = ((max_time_us - min_time_us) / 2 + min_time_us) * clocks_in_us; -constexpr uint32_t max_time = (max_time_us + jitter_time_us) * clocks_in_us; - -bool DecoderHID26::read(uint8_t* data, uint8_t data_size) { - bool result = false; - furi_assert(data_size >= 3); - - if(ready) { - result = true; - hid.decode( - reinterpret_cast(&stored_data), sizeof(uint32_t) * 3, data, data_size); - ready = false; - } - - return result; -} - -void DecoderHID26::process_front(bool polarity, uint32_t time) { - if(ready) return; - - if(polarity == true) { - last_pulse_time = time; - } else { - last_pulse_time += time; - - if(last_pulse_time > min_time && last_pulse_time < max_time) { - bool pulse; - - if(last_pulse_time < mid_time) { - // 6 pulses - pulse = false; - } else { - // 5 pulses - pulse = true; - } - - if(last_pulse == pulse) { - pulse_count++; - - if(pulse) { - if(pulse_count > 4) { - pulse_count = 0; - store_data(1); - } - } else { - if(pulse_count > 5) { - pulse_count = 0; - store_data(0); - } - } - } else { - if(last_pulse) { - if(pulse_count > 2) { - store_data(1); - } - } else { - if(pulse_count > 3) { - store_data(0); - } - } - - pulse_count = 0; - last_pulse = pulse; - } - } - } -} - -DecoderHID26::DecoderHID26() { - reset_state(); -} - -void DecoderHID26::store_data(bool data) { - stored_data[0] = (stored_data[0] << 1) | ((stored_data[1] >> 31) & 1); - stored_data[1] = (stored_data[1] << 1) | ((stored_data[2] >> 31) & 1); - stored_data[2] = (stored_data[2] << 1) | data; - - if(hid.can_be_decoded(reinterpret_cast(&stored_data), sizeof(uint32_t) * 3)) { - ready = true; - } -} - -void DecoderHID26::reset_state() { - last_pulse = false; - pulse_count = 0; - ready = false; - last_pulse_time = 0; -} diff --git a/applications/lfrfid/helpers/decoder_hid26.h b/applications/lfrfid/helpers/decoder_hid26.h deleted file mode 100644 index c73ff88c400..00000000000 --- a/applications/lfrfid/helpers/decoder_hid26.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include -#include -#include "protocols/protocol_hid_h10301.h" - -class DecoderHID26 { -public: - bool read(uint8_t* data, uint8_t data_size); - void process_front(bool polarity, uint32_t time); - DecoderHID26(); - -private: - uint32_t last_pulse_time = 0; - bool last_pulse; - uint8_t pulse_count; - - uint32_t stored_data[3] = {0, 0, 0}; - void store_data(bool data); - - std::atomic ready; - - void reset_state(); - ProtocolHID10301 hid; -}; diff --git a/applications/lfrfid/helpers/decoder_indala.cpp b/applications/lfrfid/helpers/decoder_indala.cpp deleted file mode 100644 index 100dde73bdf..00000000000 --- a/applications/lfrfid/helpers/decoder_indala.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "decoder_indala.h" -#include - -constexpr uint32_t clocks_in_us = 64; -constexpr uint32_t us_per_bit = 255; - -bool DecoderIndala::read(uint8_t* data, uint8_t data_size) { - bool result = false; - - if(ready) { - result = true; - if(cursed_data_valid) { - indala.decode( - reinterpret_cast(&cursed_raw_data), - sizeof(uint64_t), - data, - data_size); - } else { - indala.decode( - reinterpret_cast(&raw_data), sizeof(uint64_t), data, data_size); - } - reset_state(); - } - - return result; -} - -void DecoderIndala::process_front(bool polarity, uint32_t time) { - if(ready) return; - - process_internal(polarity, time, &raw_data); - if(ready) return; - - if(polarity) { - time = time + 110; - } else { - time = time - 110; - } - - process_internal(!polarity, time, &cursed_raw_data); - if(ready) { - cursed_data_valid = true; - } -} - -void DecoderIndala::process_internal(bool polarity, uint32_t time, uint64_t* data) { - time /= clocks_in_us; - time += (us_per_bit / 2); - - uint32_t bit_count = (time / us_per_bit); - - if(bit_count < 64) { - for(uint32_t i = 0; i < bit_count; i++) { - *data = (*data << 1) | polarity; - - if((*data >> 32) == 0xa0000000ULL) { - if(indala.can_be_decoded( - reinterpret_cast(data), sizeof(uint64_t))) { - ready = true; - break; - } - } - } - } -} - -DecoderIndala::DecoderIndala() { - reset_state(); -} - -void DecoderIndala::reset_state() { - raw_data = 0; - cursed_raw_data = 0; - ready = false; - cursed_data_valid = false; -} diff --git a/applications/lfrfid/helpers/decoder_indala.h b/applications/lfrfid/helpers/decoder_indala.h deleted file mode 100644 index 5fbde7b6bdf..00000000000 --- a/applications/lfrfid/helpers/decoder_indala.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include -#include -#include -#include "protocols/protocol_indala_40134.h" - -class DecoderIndala { -public: - bool read(uint8_t* data, uint8_t data_size); - void process_front(bool polarity, uint32_t time); - - void process_internal(bool polarity, uint32_t time, uint64_t* data); - - DecoderIndala(); - -private: - void reset_state(); - - uint64_t raw_data; - uint64_t cursed_raw_data; - - std::atomic ready; - std::atomic cursed_data_valid; - ProtocolIndala40134 indala; -}; diff --git a/applications/lfrfid/helpers/decoder_ioprox.cpp b/applications/lfrfid/helpers/decoder_ioprox.cpp deleted file mode 100644 index 7b44d3ceae4..00000000000 --- a/applications/lfrfid/helpers/decoder_ioprox.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "decoder_ioprox.h" -#include -#include -#include - -constexpr uint32_t clocks_in_us = 64; - -constexpr uint32_t jitter_time_us = 20; -constexpr uint32_t min_time_us = 64; -constexpr uint32_t max_time_us = 80; -constexpr uint32_t baud_time_us = 500; - -constexpr uint32_t min_time = (min_time_us - jitter_time_us) * clocks_in_us; -constexpr uint32_t mid_time = ((max_time_us - min_time_us) / 2 + min_time_us) * clocks_in_us; -constexpr uint32_t max_time = (max_time_us + jitter_time_us) * clocks_in_us; -constexpr uint32_t baud_time = baud_time_us * clocks_in_us; - -bool DecoderIoProx::read(uint8_t* data, uint8_t data_size) { - bool result = false; - furi_assert(data_size >= 4); - - if(ready) { - result = true; - ioprox.decode(raw_data, sizeof(raw_data), data, data_size); - ready = false; - } - - return result; -} - -void DecoderIoProx::process_front(bool is_rising_edge, uint32_t time) { - if(ready) { - return; - } - - // Always track the time that's gone by. - current_period_duration += time; - demodulation_sample_duration += time; - - // If a baud time has elapsed, we're at a sample point. - if(demodulation_sample_duration >= baud_time) { - // Start a new baud period... - demodulation_sample_duration = 0; - demodulated_value_invalid = false; - - // ... and if we didn't have any baud errors, capture a sample. - if(!demodulated_value_invalid) { - store_data(current_demodulated_value); - } - } - - // - // FSK demodulator. - // - - // If this isn't a rising edge, this isn't a pulse of interest. - // We're done. - if(!is_rising_edge) { - return; - } - - bool is_valid_low = (current_period_duration > min_time) && - (current_period_duration <= mid_time); - bool is_valid_high = (current_period_duration > mid_time) && - (current_period_duration < max_time); - - // If this is between the minimum and our threshold, this is a logical 0. - if(is_valid_low) { - current_demodulated_value = false; - } - // Otherwise, if between our threshold and the max time, it's a logical 1. - else if(is_valid_high) { - current_demodulated_value = true; - } - // Otherwise, invalidate this sample. - else { - demodulated_value_invalid = true; - } - - // We're starting a new period; track that. - current_period_duration = 0; -} - -DecoderIoProx::DecoderIoProx() { - reset_state(); -} - -void DecoderIoProx::store_data(bool data) { - for(int i = 0; i < 7; ++i) { - raw_data[i] = (raw_data[i] << 1) | ((raw_data[i + 1] >> 7) & 1); - } - raw_data[7] = (raw_data[7] << 1) | data; - - if(ioprox.can_be_decoded(raw_data, sizeof(raw_data))) { - ready = true; - } -} - -void DecoderIoProx::reset_state() { - current_demodulated_value = false; - demodulated_value_invalid = false; - - current_period_duration = 0; - demodulation_sample_duration = 0; - - ready = false; -} diff --git a/applications/lfrfid/helpers/decoder_ioprox.h b/applications/lfrfid/helpers/decoder_ioprox.h deleted file mode 100644 index aff4a47786a..00000000000 --- a/applications/lfrfid/helpers/decoder_ioprox.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include -#include -#include "protocols/protocol_ioprox.h" - -class DecoderIoProx { -public: - bool read(uint8_t* data, uint8_t data_size); - void process_front(bool polarity, uint32_t time); - DecoderIoProx(); - -private: - uint32_t current_period_duration = 0; - uint32_t demodulation_sample_duration = 0; - - bool current_demodulated_value = false; - bool demodulated_value_invalid = false; - - uint8_t raw_data[8] = {0}; - void store_data(bool data); - - std::atomic ready; - - void reset_state(); - ProtocolIoProx ioprox; -}; diff --git a/applications/lfrfid/helpers/emmarin.h b/applications/lfrfid/helpers/emmarin.h deleted file mode 100644 index ff8273bf767..00000000000 --- a/applications/lfrfid/helpers/emmarin.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include - -#define EM_HEADER_POS 55 -#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS) - -#define EM_FIRST_ROW_POS 50 -#define EM_ROW_COUNT 10 - -#define EM_COLUMN_POS 4 -#define EM_STOP_POS 0 -#define EM_STOP_MASK (0x1LLU << EM_STOP_POS) - -#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | EM_STOP_MASK) -#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK) diff --git a/applications/lfrfid/helpers/encoder_emmarin.cpp b/applications/lfrfid/helpers/encoder_emmarin.cpp deleted file mode 100644 index c329ab40046..00000000000 --- a/applications/lfrfid/helpers/encoder_emmarin.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "encoder_emmarin.h" -#include "protocols/protocol_emmarin.h" -#include - -void EncoderEM::init(const uint8_t* data, const uint8_t data_size) { - ProtocolEMMarin em_marin; - em_marin.encode(data, data_size, reinterpret_cast(&card_data), sizeof(uint64_t)); - - card_data_index = 0; -} - -// data transmitted as manchester encoding -// 0 - high2low -// 1 - low2high -void EncoderEM::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) { - *period = clocks_per_bit; - *pulse = clocks_per_bit / 2; - *polarity = (card_data >> (63 - card_data_index)) & 1; - - card_data_index++; - if(card_data_index >= 64) { - card_data_index = 0; - } -} diff --git a/applications/lfrfid/helpers/encoder_emmarin.h b/applications/lfrfid/helpers/encoder_emmarin.h deleted file mode 100644 index 560daec1b5a..00000000000 --- a/applications/lfrfid/helpers/encoder_emmarin.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "encoder_generic.h" - -class EncoderEM : public EncoderGeneric { -public: - /** - * @brief init data to emulate - * - * @param data 1 byte FC, next 4 byte SN - * @param data_size must be 5 - */ - void init(const uint8_t* data, const uint8_t data_size) final; - - void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final; - -private: - // clock pulses per bit - static const uint8_t clocks_per_bit = 64; - - uint64_t card_data; - uint8_t card_data_index; -}; diff --git a/applications/lfrfid/helpers/encoder_generic.h b/applications/lfrfid/helpers/encoder_generic.h deleted file mode 100644 index dc6ae850396..00000000000 --- a/applications/lfrfid/helpers/encoder_generic.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include -#include - -class EncoderGeneric { -public: - /** - * @brief init encoder - * - * @param data data array - * @param data_size data array size - */ - virtual void init(const uint8_t* data, const uint8_t data_size) = 0; - - /** - * @brief Get the next timer pulse - * - * @param polarity pulse polarity true = high2low, false = low2high - * @param period overall period time in timer clicks - * @param pulse pulse time in timer clicks - */ - virtual void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) = 0; - - virtual ~EncoderGeneric(){}; - -private: -}; diff --git a/applications/lfrfid/helpers/encoder_hid_h10301.cpp b/applications/lfrfid/helpers/encoder_hid_h10301.cpp deleted file mode 100644 index 09f637fee10..00000000000 --- a/applications/lfrfid/helpers/encoder_hid_h10301.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "encoder_hid_h10301.h" -#include "protocols/protocol_hid_h10301.h" -#include - -void EncoderHID_H10301::init(const uint8_t* data, const uint8_t data_size) { - ProtocolHID10301 hid; - hid.encode(data, data_size, reinterpret_cast(&card_data), sizeof(card_data) * 3); - - card_data_index = 0; -} - -void EncoderHID_H10301::write_bit(bool bit, uint8_t position) { - write_raw_bit(bit, position + 0); - write_raw_bit(!bit, position + 1); -} - -void EncoderHID_H10301::write_raw_bit(bool bit, uint8_t position) { - if(bit) { - card_data[position / 32] |= 1UL << (31 - (position % 32)); - } else { - card_data[position / 32] &= ~(1UL << (31 - (position % 32))); - } -} - -void EncoderHID_H10301::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) { - uint8_t bit = (card_data[card_data_index / 32] >> (31 - (card_data_index % 32))) & 1; - - bool advance = fsk->next(bit, period); - if(advance) { - card_data_index++; - if(card_data_index >= (32 * card_data_max)) { - card_data_index = 0; - } - } - - *polarity = true; - *pulse = *period / 2; -} - -EncoderHID_H10301::EncoderHID_H10301() { - fsk = new OscFSK(8, 10, 50); -} - -EncoderHID_H10301::~EncoderHID_H10301() { - delete fsk; -} diff --git a/applications/lfrfid/helpers/encoder_hid_h10301.h b/applications/lfrfid/helpers/encoder_hid_h10301.h deleted file mode 100644 index 8cc5aa5cbfa..00000000000 --- a/applications/lfrfid/helpers/encoder_hid_h10301.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include "encoder_generic.h" -#include "osc_fsk.h" - -class EncoderHID_H10301 : public EncoderGeneric { -public: - /** - * @brief init data to emulate - * - * @param data 1 byte FC, next 2 byte SN - * @param data_size must be 3 - */ - void init(const uint8_t* data, const uint8_t data_size) final; - void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final; - EncoderHID_H10301(); - ~EncoderHID_H10301(); - -private: - static const uint8_t card_data_max = 3; - uint32_t card_data[card_data_max]; - uint8_t card_data_index; - void write_bit(bool bit, uint8_t position); - void write_raw_bit(bool bit, uint8_t position); - - OscFSK* fsk; -}; diff --git a/applications/lfrfid/helpers/encoder_indala_40134.cpp b/applications/lfrfid/helpers/encoder_indala_40134.cpp deleted file mode 100644 index 764237d1fee..00000000000 --- a/applications/lfrfid/helpers/encoder_indala_40134.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "encoder_indala_40134.h" -#include "protocols/protocol_indala_40134.h" -#include - -void EncoderIndala_40134::init(const uint8_t* data, const uint8_t data_size) { - ProtocolIndala40134 indala; - indala.encode(data, data_size, reinterpret_cast(&card_data), sizeof(card_data)); - - last_bit = card_data & 1; - card_data_index = 0; - current_polarity = true; -} - -void EncoderIndala_40134::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) { - *period = 2; - *pulse = 1; - *polarity = current_polarity; - - bit_clock_index++; - if(bit_clock_index >= clock_per_bit) { - bit_clock_index = 0; - - bool current_bit = (card_data >> (63 - card_data_index)) & 1; - - if(current_bit != last_bit) { - current_polarity = !current_polarity; - } - - last_bit = current_bit; - - card_data_index++; - if(card_data_index >= 64) { - card_data_index = 0; - } - } -} diff --git a/applications/lfrfid/helpers/encoder_indala_40134.h b/applications/lfrfid/helpers/encoder_indala_40134.h deleted file mode 100644 index eda29457f37..00000000000 --- a/applications/lfrfid/helpers/encoder_indala_40134.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include "encoder_generic.h" - -class EncoderIndala_40134 : public EncoderGeneric { -public: - /** - * @brief init data to emulate - * - * @param data indala raw data - * @param data_size must be 5 - */ - void init(const uint8_t* data, const uint8_t data_size) final; - - void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final; - -private: - uint64_t card_data; - uint8_t card_data_index; - uint8_t bit_clock_index; - bool last_bit; - bool current_polarity; - static const uint8_t clock_per_bit = 16; -}; diff --git a/applications/lfrfid/helpers/encoder_ioprox.cpp b/applications/lfrfid/helpers/encoder_ioprox.cpp deleted file mode 100644 index 4177991863c..00000000000 --- a/applications/lfrfid/helpers/encoder_ioprox.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "encoder_ioprox.h" -#include "protocols/protocol_ioprox.h" -#include - -void EncoderIoProx::init(const uint8_t* data, const uint8_t data_size) { - ProtocolIoProx ioprox; - ioprox.encode(data, data_size, card_data, sizeof(card_data)); - card_data_index = 0; -} - -void EncoderIoProx::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) { - uint8_t bit = (card_data[card_data_index / 8] >> (7 - (card_data_index % 8))) & 1; - - bool advance = fsk->next(bit, period); - if(advance) { - card_data_index++; - if(card_data_index >= (8 * card_data_max)) { - card_data_index = 0; - } - } - - *polarity = true; - *pulse = *period / 2; -} - -EncoderIoProx::EncoderIoProx() { - fsk = new OscFSK(8, 10, 64); -} - -EncoderIoProx::~EncoderIoProx() { - delete fsk; -} diff --git a/applications/lfrfid/helpers/encoder_ioprox.h b/applications/lfrfid/helpers/encoder_ioprox.h deleted file mode 100644 index 568b406711a..00000000000 --- a/applications/lfrfid/helpers/encoder_ioprox.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include "encoder_generic.h" -#include "osc_fsk.h" - -class EncoderIoProx : public EncoderGeneric { -public: - /** - * @brief init data to emulate - * - * @param data 1 byte FC, 1 byte Version, 2 bytes code - * @param data_size must be 4 - */ - void init(const uint8_t* data, const uint8_t data_size) final; - void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final; - EncoderIoProx(); - ~EncoderIoProx(); - -private: - static const uint8_t card_data_max = 8; - - uint8_t card_data[card_data_max]; - uint8_t card_data_index; - - OscFSK* fsk; -}; diff --git a/applications/lfrfid/helpers/key_info.cpp b/applications/lfrfid/helpers/key_info.cpp deleted file mode 100644 index 4803cd6dc25..00000000000 --- a/applications/lfrfid/helpers/key_info.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "key_info.h" -#include - -const char* lfrfid_key_get_type_string(LfrfidKeyType type) { - switch(type) { - case LfrfidKeyType::KeyEM4100: - return "EM4100"; - break; - case LfrfidKeyType::KeyH10301: - return "H10301"; - break; - case LfrfidKeyType::KeyI40134: - return "I40134"; - break; - case LfrfidKeyType::KeyIoProxXSF: - return "IoProxXSF"; - break; - } - - return "Unknown"; -} - -const char* lfrfid_key_get_manufacturer_string(LfrfidKeyType type) { - switch(type) { - case LfrfidKeyType::KeyEM4100: - return "EM-Marin"; - break; - case LfrfidKeyType::KeyH10301: - return "HID"; - break; - case LfrfidKeyType::KeyI40134: - return "Indala"; - break; - case LfrfidKeyType::KeyIoProxXSF: - return "Kantech"; - } - - return "Unknown"; -} - -bool lfrfid_key_get_string_type(const char* string, LfrfidKeyType* type) { - bool result = true; - - if(strcmp("EM4100", string) == 0) { - *type = LfrfidKeyType::KeyEM4100; - } else if(strcmp("H10301", string) == 0) { - *type = LfrfidKeyType::KeyH10301; - } else if(strcmp("I40134", string) == 0) { - *type = LfrfidKeyType::KeyI40134; - } else if(strcmp("IoProxXSF", string) == 0) { - *type = LfrfidKeyType::KeyIoProxXSF; - } else { - result = false; - } - - return result; -} - -uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type) { - switch(type) { - case LfrfidKeyType::KeyEM4100: - return 5; - break; - case LfrfidKeyType::KeyH10301: - return 3; - break; - case LfrfidKeyType::KeyI40134: - return 3; - break; - case LfrfidKeyType::KeyIoProxXSF: - return 4; - break; - } - - return 0; -} diff --git a/applications/lfrfid/helpers/key_info.h b/applications/lfrfid/helpers/key_info.h deleted file mode 100644 index 75a0a94063f..00000000000 --- a/applications/lfrfid/helpers/key_info.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include - -static const uint8_t LFRFID_KEY_SIZE = 8; -static const uint8_t LFRFID_KEY_NAME_SIZE = 22; - -enum class LfrfidKeyType : uint8_t { - KeyEM4100, - KeyH10301, - KeyI40134, - KeyIoProxXSF, -}; - -const char* lfrfid_key_get_type_string(LfrfidKeyType type); -const char* lfrfid_key_get_manufacturer_string(LfrfidKeyType type); -bool lfrfid_key_get_string_type(const char* string, LfrfidKeyType* type); -uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type); diff --git a/applications/lfrfid/helpers/osc_fsk.cpp b/applications/lfrfid/helpers/osc_fsk.cpp deleted file mode 100644 index 5f3b5367b7e..00000000000 --- a/applications/lfrfid/helpers/osc_fsk.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "osc_fsk.h" - -OscFSK::OscFSK(uint16_t _freq_low, uint16_t _freq_hi, uint16_t _osc_phase_max) - : freq{_freq_low, _freq_hi} - , osc_phase_max(_osc_phase_max) { - osc_phase_current = 0; -} - -bool OscFSK::next(bool bit, uint16_t* period) { - bool advance = false; - *period = freq[bit]; - osc_phase_current += *period; - - if(osc_phase_current > osc_phase_max) { - advance = true; - osc_phase_current -= osc_phase_max; - } - - return advance; -} diff --git a/applications/lfrfid/helpers/osc_fsk.h b/applications/lfrfid/helpers/osc_fsk.h deleted file mode 100644 index eaaaa10ad0e..00000000000 --- a/applications/lfrfid/helpers/osc_fsk.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include - -/** - * This code tries to fit the periods into a given number of cycles (phases) by taking cycles from the next cycle of periods. - */ -class OscFSK { -public: - /** - * Get next period - * @param bit bit value - * @param period return period - * @return bool whether to advance to the next bit - */ - bool next(bool bit, uint16_t* period); - - /** - * FSK ocillator constructor - * - * @param freq_low bit 0 freq - * @param freq_hi bit 1 freq - * @param osc_phase_max max oscillator phase - */ - OscFSK(uint16_t freq_low, uint16_t freq_hi, uint16_t osc_phase_max); - -private: - const uint16_t freq[2]; - const uint16_t osc_phase_max; - int32_t osc_phase_current; -}; diff --git a/applications/lfrfid/helpers/protocols/protocol_emmarin.cpp b/applications/lfrfid/helpers/protocols/protocol_emmarin.cpp deleted file mode 100644 index 4ac180e300b..00000000000 --- a/applications/lfrfid/helpers/protocols/protocol_emmarin.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include "protocol_emmarin.h" -#include - -#define EM_HEADER_POS 55 -#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS) - -#define EM_FIRST_ROW_POS 50 - -#define EM_ROW_COUNT 10 -#define EM_COLUMN_COUNT 4 -#define EM_BITS_PER_ROW_COUNT (EM_COLUMN_COUNT + 1) - -#define EM_COLUMN_POS 4 -#define EM_STOP_POS 0 -#define EM_STOP_MASK (0x1LLU << EM_STOP_POS) - -#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | EM_STOP_MASK) -#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK) - -typedef uint64_t EMMarinCardData; - -void write_nibble(bool low_nibble, uint8_t data, EMMarinCardData* card_data) { - uint8_t parity_sum = 0; - uint8_t start = 0; - if(!low_nibble) start = 4; - - for(int8_t i = (start + 3); i >= start; i--) { - parity_sum += (data >> i) & 1; - *card_data = (*card_data << 1) | ((data >> i) & 1); - } - - *card_data = (*card_data << 1) | ((parity_sum % 2) & 1); -} - -uint8_t ProtocolEMMarin::get_encoded_data_size() { - return sizeof(EMMarinCardData); -} - -uint8_t ProtocolEMMarin::get_decoded_data_size() { - return 5; -} - -void ProtocolEMMarin::encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - EMMarinCardData card_data; - - // header - card_data = 0b111111111; - - // data - for(uint8_t i = 0; i < get_decoded_data_size(); i++) { - write_nibble(false, decoded_data[i], &card_data); - write_nibble(true, decoded_data[i], &card_data); - } - - // column parity and stop bit - uint8_t parity_sum; - - for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) { - parity_sum = 0; - for(uint8_t i = 1; i <= EM_ROW_COUNT; i++) { - uint8_t parity_bit = (card_data >> (i * EM_BITS_PER_ROW_COUNT - 1)) & 1; - parity_sum += parity_bit; - } - card_data = (card_data << 1) | ((parity_sum % 2) & 1); - } - - // stop bit - card_data = (card_data << 1) | 0; - - memcpy(encoded_data, &card_data, get_encoded_data_size()); -} - -void ProtocolEMMarin::decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - uint8_t decoded_data_index = 0; - EMMarinCardData card_data = *(reinterpret_cast(encoded_data)); - - // clean result - memset(decoded_data, 0, decoded_data_size); - - // header - for(uint8_t i = 0; i < 9; i++) { - card_data = card_data << 1; - } - - // nibbles - uint8_t value = 0; - for(uint8_t r = 0; r < EM_ROW_COUNT; r++) { - uint8_t nibble = 0; - for(uint8_t i = 0; i < 5; i++) { - if(i < 4) nibble = (nibble << 1) | (card_data & (1LLU << 63) ? 1 : 0); - card_data = card_data << 1; - } - value = (value << 4) | nibble; - if(r % 2) { - decoded_data[decoded_data_index] |= value; - decoded_data_index++; - value = 0; - } - } -} - -bool ProtocolEMMarin::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) { - furi_check(encoded_data_size >= get_encoded_data_size()); - const EMMarinCardData* card_data = reinterpret_cast(encoded_data); - - // check header and stop bit - if((*card_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return false; - - // check row parity - for(uint8_t i = 0; i < EM_ROW_COUNT; i++) { - uint8_t parity_sum = 0; - - for(uint8_t j = 0; j < EM_BITS_PER_ROW_COUNT; j++) { - parity_sum += (*card_data >> (EM_FIRST_ROW_POS - i * EM_BITS_PER_ROW_COUNT + j)) & 1; - } - - if((parity_sum % 2)) { - return false; - } - } - - // check columns parity - for(uint8_t i = 0; i < EM_COLUMN_COUNT; i++) { - uint8_t parity_sum = 0; - - for(uint8_t j = 0; j < EM_ROW_COUNT + 1; j++) { - parity_sum += (*card_data >> (EM_COLUMN_POS - i + j * EM_BITS_PER_ROW_COUNT)) & 1; - } - - if((parity_sum % 2)) { - return false; - } - } - - return true; -} diff --git a/applications/lfrfid/helpers/protocols/protocol_emmarin.h b/applications/lfrfid/helpers/protocols/protocol_emmarin.h deleted file mode 100644 index 7a866f9097e..00000000000 --- a/applications/lfrfid/helpers/protocols/protocol_emmarin.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "protocol_generic.h" - -class ProtocolEMMarin : public ProtocolGeneric { -public: - uint8_t get_encoded_data_size() final; - uint8_t get_decoded_data_size() final; - - void encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) final; - - void decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) final; - - bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final; -}; diff --git a/applications/lfrfid/helpers/protocols/protocol_generic.h b/applications/lfrfid/helpers/protocols/protocol_generic.h deleted file mode 100644 index d593f708995..00000000000 --- a/applications/lfrfid/helpers/protocols/protocol_generic.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once -#include "stdint.h" -#include "stdbool.h" - -class ProtocolGeneric { -public: - /** - * @brief Get the encoded data size - * - * @return uint8_t size of encoded data in bytes - */ - virtual uint8_t get_encoded_data_size() = 0; - - /** - * @brief Get the decoded data size - * - * @return uint8_t size of decoded data in bytes - */ - virtual uint8_t get_decoded_data_size() = 0; - - /** - * @brief encode decoded data - * - * @param decoded_data - * @param decoded_data_size - * @param encoded_data - * @param encoded_data_size - */ - virtual void encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) = 0; - - /** - * @brief decode encoded data - * - * @param encoded_data - * @param encoded_data_size - * @param decoded_data - * @param decoded_data_size - */ - virtual void decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) = 0; - - /** - * @brief fast check that data can be correctly decoded - * - * @param encoded_data - * @param encoded_data_size - * @return true - can be correctly decoded - * @return false - cannot be correctly decoded - */ - virtual bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) = 0; - - virtual ~ProtocolGeneric(){}; -}; diff --git a/applications/lfrfid/helpers/protocols/protocol_hid_h10301.cpp b/applications/lfrfid/helpers/protocols/protocol_hid_h10301.cpp deleted file mode 100644 index b718388388d..00000000000 --- a/applications/lfrfid/helpers/protocols/protocol_hid_h10301.cpp +++ /dev/null @@ -1,238 +0,0 @@ -#include "protocol_hid_h10301.h" -#include - -typedef uint32_t HID10301CardData; -constexpr uint8_t HID10301Count = 3; -constexpr uint8_t HID10301BitSize = sizeof(HID10301CardData) * 8; - -static void write_raw_bit(bool bit, uint8_t position, HID10301CardData* card_data) { - if(bit) { - card_data[position / HID10301BitSize] |= - 1UL << (HID10301BitSize - (position % HID10301BitSize) - 1); - } else { - card_data[position / (sizeof(HID10301CardData) * 8)] &= - ~(1UL << (HID10301BitSize - (position % HID10301BitSize) - 1)); - } -} - -static void write_bit(bool bit, uint8_t position, HID10301CardData* card_data) { - write_raw_bit(bit, position + 0, card_data); - write_raw_bit(!bit, position + 1, card_data); -} - -uint8_t ProtocolHID10301::get_encoded_data_size() { - return sizeof(HID10301CardData) * HID10301Count; -} - -uint8_t ProtocolHID10301::get_decoded_data_size() { - return 3; -} - -void ProtocolHID10301::encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - HID10301CardData card_data[HID10301Count] = {0, 0, 0}; - - uint32_t fc_cn = (decoded_data[0] << 16) | (decoded_data[1] << 8) | decoded_data[2]; - - // even parity sum calculation (high 12 bits of data) - uint8_t even_parity_sum = 0; - for(int8_t i = 12; i < 24; i++) { - if(((fc_cn >> i) & 1) == 1) { - even_parity_sum++; - } - } - - // odd parity sum calculation (low 12 bits of data) - uint8_t odd_parity_sum = 1; - for(int8_t i = 0; i < 12; i++) { - if(((fc_cn >> i) & 1) == 1) { - odd_parity_sum++; - } - } - - // 0x1D preamble - write_raw_bit(0, 0, card_data); - write_raw_bit(0, 1, card_data); - write_raw_bit(0, 2, card_data); - write_raw_bit(1, 3, card_data); - write_raw_bit(1, 4, card_data); - write_raw_bit(1, 5, card_data); - write_raw_bit(0, 6, card_data); - write_raw_bit(1, 7, card_data); - - // company / OEM code 1 - write_bit(0, 8, card_data); - write_bit(0, 10, card_data); - write_bit(0, 12, card_data); - write_bit(0, 14, card_data); - write_bit(0, 16, card_data); - write_bit(0, 18, card_data); - write_bit(1, 20, card_data); - - // card format / length 1 - write_bit(0, 22, card_data); - write_bit(0, 24, card_data); - write_bit(0, 26, card_data); - write_bit(0, 28, card_data); - write_bit(0, 30, card_data); - write_bit(0, 32, card_data); - write_bit(0, 34, card_data); - write_bit(0, 36, card_data); - write_bit(0, 38, card_data); - write_bit(0, 40, card_data); - write_bit(1, 42, card_data); - - // even parity bit - write_bit((even_parity_sum % 2), 44, card_data); - - // data - for(uint8_t i = 0; i < 24; i++) { - write_bit((fc_cn >> (23 - i)) & 1, 46 + (i * 2), card_data); - } - - // odd parity bit - write_bit((odd_parity_sum % 2), 94, card_data); - - memcpy(encoded_data, &card_data, get_encoded_data_size()); -} - -void ProtocolHID10301::decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - const HID10301CardData* card_data = reinterpret_cast(encoded_data); - - // data decoding - uint32_t result = 0; - - // decode from word 1 - // coded with 01 = 0, 10 = 1 transitions - for(int8_t i = 9; i >= 0; i--) { - switch((*(card_data + 1) >> (2 * i)) & 0b11) { - case 0b01: - result = (result << 1) | 0; - break; - case 0b10: - result = (result << 1) | 1; - break; - default: - break; - } - } - - // decode from word 2 - // coded with 01 = 0, 10 = 1 transitions - for(int8_t i = 15; i >= 0; i--) { - switch((*(card_data + 2) >> (2 * i)) & 0b11) { - case 0b01: - result = (result << 1) | 0; - break; - case 0b10: - result = (result << 1) | 1; - break; - default: - break; - } - } - - uint8_t data[3] = {(uint8_t)(result >> 17), (uint8_t)(result >> 9), (uint8_t)(result >> 1)}; - - memcpy(decoded_data, &data, get_decoded_data_size()); -} - -bool ProtocolHID10301::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) { - furi_check(encoded_data_size >= get_encoded_data_size()); - - const HID10301CardData* card_data = reinterpret_cast(encoded_data); - - // packet preamble - // raw data - if(*(encoded_data + 3) != 0x1D) { - return false; - } - - // encoded company/oem - // coded with 01 = 0, 10 = 1 transitions - // stored in word 0 - if((*card_data >> 10 & 0x3FFF) != 0x1556) { - return false; - } - - // encoded format/length - // coded with 01 = 0, 10 = 1 transitions - // stored in word 0 and word 1 - if((((*card_data & 0x3FF) << 12) | ((*(card_data + 1) >> 20) & 0xFFF)) != 0x155556) { - return false; - } - - // data decoding - uint32_t result = 0; - - // decode from word 1 - // coded with 01 = 0, 10 = 1 transitions - for(int8_t i = 9; i >= 0; i--) { - switch((*(card_data + 1) >> (2 * i)) & 0b11) { - case 0b01: - result = (result << 1) | 0; - break; - case 0b10: - result = (result << 1) | 1; - break; - default: - return false; - break; - } - } - - // decode from word 2 - // coded with 01 = 0, 10 = 1 transitions - for(int8_t i = 15; i >= 0; i--) { - switch((*(card_data + 2) >> (2 * i)) & 0b11) { - case 0b01: - result = (result << 1) | 0; - break; - case 0b10: - result = (result << 1) | 1; - break; - default: - return false; - break; - } - } - - // trailing parity (odd) test - uint8_t parity_sum = 0; - for(int8_t i = 0; i < 13; i++) { - if(((result >> i) & 1) == 1) { - parity_sum++; - } - } - - if((parity_sum % 2) != 1) { - return false; - } - - // leading parity (even) test - parity_sum = 0; - for(int8_t i = 13; i < 26; i++) { - if(((result >> i) & 1) == 1) { - parity_sum++; - } - } - - if((parity_sum % 2) == 1) { - return false; - } - - return true; -} diff --git a/applications/lfrfid/helpers/protocols/protocol_hid_h10301.h b/applications/lfrfid/helpers/protocols/protocol_hid_h10301.h deleted file mode 100644 index fbd6e0b2bf9..00000000000 --- a/applications/lfrfid/helpers/protocols/protocol_hid_h10301.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "protocol_generic.h" - -class ProtocolHID10301 : public ProtocolGeneric { -public: - uint8_t get_encoded_data_size() final; - uint8_t get_decoded_data_size() final; - - void encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) final; - - void decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) final; - - bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final; -}; diff --git a/applications/lfrfid/helpers/protocols/protocol_indala_40134.cpp b/applications/lfrfid/helpers/protocols/protocol_indala_40134.cpp deleted file mode 100644 index 482339def33..00000000000 --- a/applications/lfrfid/helpers/protocols/protocol_indala_40134.cpp +++ /dev/null @@ -1,237 +0,0 @@ -#include "protocol_indala_40134.h" -#include - -typedef uint64_t Indala40134CardData; - -static void set_bit(bool bit, uint8_t position, Indala40134CardData* card_data) { - position = (sizeof(Indala40134CardData) * 8) - 1 - position; - if(bit) { - *card_data |= 1ull << position; - } else { - *card_data &= ~(1ull << position); - } -} - -static bool get_bit(uint8_t position, const Indala40134CardData* card_data) { - position = (sizeof(Indala40134CardData) * 8) - 1 - position; - return (*card_data >> position) & 1; -} - -uint8_t ProtocolIndala40134::get_encoded_data_size() { - return sizeof(Indala40134CardData); -} - -uint8_t ProtocolIndala40134::get_decoded_data_size() { - return 3; -} - -void ProtocolIndala40134::encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - uint32_t fc_and_card = (decoded_data[0] << 16) | (decoded_data[1] << 8) | decoded_data[2]; - Indala40134CardData card_data = 0; - - // preamble - set_bit(1, 0, &card_data); - set_bit(1, 2, &card_data); - set_bit(1, 32, &card_data); - - // factory code - set_bit(((fc_and_card >> 23) & 1), 57, &card_data); - set_bit(((fc_and_card >> 22) & 1), 49, &card_data); - set_bit(((fc_and_card >> 21) & 1), 44, &card_data); - set_bit(((fc_and_card >> 20) & 1), 47, &card_data); - set_bit(((fc_and_card >> 19) & 1), 48, &card_data); - set_bit(((fc_and_card >> 18) & 1), 53, &card_data); - set_bit(((fc_and_card >> 17) & 1), 39, &card_data); - set_bit(((fc_and_card >> 16) & 1), 58, &card_data); - - // card number - set_bit(((fc_and_card >> 15) & 1), 42, &card_data); - set_bit(((fc_and_card >> 14) & 1), 45, &card_data); - set_bit(((fc_and_card >> 13) & 1), 43, &card_data); - set_bit(((fc_and_card >> 12) & 1), 40, &card_data); - set_bit(((fc_and_card >> 11) & 1), 52, &card_data); - set_bit(((fc_and_card >> 10) & 1), 36, &card_data); - set_bit(((fc_and_card >> 9) & 1), 35, &card_data); - set_bit(((fc_and_card >> 8) & 1), 51, &card_data); - set_bit(((fc_and_card >> 7) & 1), 46, &card_data); - set_bit(((fc_and_card >> 6) & 1), 33, &card_data); - set_bit(((fc_and_card >> 5) & 1), 37, &card_data); - set_bit(((fc_and_card >> 4) & 1), 54, &card_data); - set_bit(((fc_and_card >> 3) & 1), 56, &card_data); - set_bit(((fc_and_card >> 2) & 1), 59, &card_data); - set_bit(((fc_and_card >> 1) & 1), 50, &card_data); - set_bit(((fc_and_card >> 0) & 1), 41, &card_data); - - // checksum - uint8_t checksum = 0; - checksum += ((fc_and_card >> 14) & 1); - checksum += ((fc_and_card >> 12) & 1); - checksum += ((fc_and_card >> 9) & 1); - checksum += ((fc_and_card >> 8) & 1); - checksum += ((fc_and_card >> 6) & 1); - checksum += ((fc_and_card >> 5) & 1); - checksum += ((fc_and_card >> 2) & 1); - checksum += ((fc_and_card >> 0) & 1); - - // wiegand parity bits - // even parity sum calculation (high 12 bits of data) - uint8_t even_parity_sum = 0; - for(int8_t i = 12; i < 24; i++) { - if(((fc_and_card >> i) & 1) == 1) { - even_parity_sum++; - } - } - - // odd parity sum calculation (low 12 bits of data) - uint8_t odd_parity_sum = 1; - for(int8_t i = 0; i < 12; i++) { - if(((fc_and_card >> i) & 1) == 1) { - odd_parity_sum++; - } - } - - // even parity bit - set_bit((even_parity_sum % 2), 34, &card_data); - - // odd parity bit - set_bit((odd_parity_sum % 2), 38, &card_data); - - // checksum - if((checksum & 1) == 1) { - set_bit(0, 62, &card_data); - set_bit(1, 63, &card_data); - } else { - set_bit(1, 62, &card_data); - set_bit(0, 63, &card_data); - } - - memcpy(encoded_data, &card_data, get_encoded_data_size()); -} - -// factory code -static uint8_t get_fc(const Indala40134CardData* card_data) { - uint8_t fc = 0; - - fc = fc << 1 | get_bit(57, card_data); - fc = fc << 1 | get_bit(49, card_data); - fc = fc << 1 | get_bit(44, card_data); - fc = fc << 1 | get_bit(47, card_data); - fc = fc << 1 | get_bit(48, card_data); - fc = fc << 1 | get_bit(53, card_data); - fc = fc << 1 | get_bit(39, card_data); - fc = fc << 1 | get_bit(58, card_data); - - return fc; -} - -// card number -static uint16_t get_cn(const Indala40134CardData* card_data) { - uint16_t cn = 0; - - cn = cn << 1 | get_bit(42, card_data); - cn = cn << 1 | get_bit(45, card_data); - cn = cn << 1 | get_bit(43, card_data); - cn = cn << 1 | get_bit(40, card_data); - cn = cn << 1 | get_bit(52, card_data); - cn = cn << 1 | get_bit(36, card_data); - cn = cn << 1 | get_bit(35, card_data); - cn = cn << 1 | get_bit(51, card_data); - cn = cn << 1 | get_bit(46, card_data); - cn = cn << 1 | get_bit(33, card_data); - cn = cn << 1 | get_bit(37, card_data); - cn = cn << 1 | get_bit(54, card_data); - cn = cn << 1 | get_bit(56, card_data); - cn = cn << 1 | get_bit(59, card_data); - cn = cn << 1 | get_bit(50, card_data); - cn = cn << 1 | get_bit(41, card_data); - - return cn; -} - -void ProtocolIndala40134::decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - const Indala40134CardData* card_data = - reinterpret_cast(encoded_data); - - uint8_t fc = get_fc(card_data); - uint16_t card = get_cn(card_data); - - decoded_data[0] = fc; - decoded_data[1] = card >> 8; - decoded_data[2] = card; -} - -bool ProtocolIndala40134::can_be_decoded( - const uint8_t* encoded_data, - const uint8_t encoded_data_size) { - furi_check(encoded_data_size >= get_encoded_data_size()); - bool can_be_decoded = false; - - const Indala40134CardData* card_data = - reinterpret_cast(encoded_data); - - do { - // preambula - if((*card_data >> 32) != 0xa0000000UL) break; - - // data - const uint32_t fc_and_card = get_fc(card_data) << 16 | get_cn(card_data); - - // checksum - const uint8_t checksum = get_bit(62, card_data) << 1 | get_bit(63, card_data); - uint8_t checksum_sum = 0; - checksum_sum += ((fc_and_card >> 14) & 1); - checksum_sum += ((fc_and_card >> 12) & 1); - checksum_sum += ((fc_and_card >> 9) & 1); - checksum_sum += ((fc_and_card >> 8) & 1); - checksum_sum += ((fc_and_card >> 6) & 1); - checksum_sum += ((fc_and_card >> 5) & 1); - checksum_sum += ((fc_and_card >> 2) & 1); - checksum_sum += ((fc_and_card >> 0) & 1); - checksum_sum = checksum_sum & 0b1; - - if(checksum_sum == 1 && checksum == 0b01) { - } else if(checksum_sum == 0 && checksum == 0b10) { - } else { - break; - } - - // wiegand parity bits - // even parity sum calculation (high 12 bits of data) - const bool even_parity = get_bit(34, card_data); - uint8_t even_parity_sum = 0; - for(int8_t i = 12; i < 24; i++) { - if(((fc_and_card >> i) & 1) == 1) { - even_parity_sum++; - } - } - if(even_parity_sum % 2 != even_parity) break; - - // odd parity sum calculation (low 12 bits of data) - const bool odd_parity = get_bit(38, card_data); - uint8_t odd_parity_sum = 1; - for(int8_t i = 0; i < 12; i++) { - if(((fc_and_card >> i) & 1) == 1) { - odd_parity_sum++; - } - } - if(odd_parity_sum % 2 != odd_parity) break; - - can_be_decoded = true; - } while(false); - - return can_be_decoded; -} diff --git a/applications/lfrfid/helpers/protocols/protocol_indala_40134.h b/applications/lfrfid/helpers/protocols/protocol_indala_40134.h deleted file mode 100644 index d378bb2ceef..00000000000 --- a/applications/lfrfid/helpers/protocols/protocol_indala_40134.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "protocol_generic.h" - -class ProtocolIndala40134 : public ProtocolGeneric { -public: - uint8_t get_encoded_data_size() final; - uint8_t get_decoded_data_size() final; - - void encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) final; - - void decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) final; - - bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final; -}; diff --git a/applications/lfrfid/helpers/protocols/protocol_ioprox.cpp b/applications/lfrfid/helpers/protocols/protocol_ioprox.cpp deleted file mode 100644 index b3b6a0e598e..00000000000 --- a/applications/lfrfid/helpers/protocols/protocol_ioprox.cpp +++ /dev/null @@ -1,193 +0,0 @@ -#include "protocol_ioprox.h" -#include -#include - -/** - * Writes a bit into the output buffer. - */ -static void write_bit(bool bit, uint8_t position, uint8_t* data) { - if(bit) { - data[position / 8] |= 1UL << (7 - (position % 8)); - } else { - data[position / 8] &= ~(1UL << (7 - (position % 8))); - } -} - -/** - * Writes up to eight contiguous bits into the output buffer. - */ -static void write_bits(uint8_t byte, uint8_t position, uint8_t* data, uint8_t length) { - furi_check(length <= 8); - furi_check(length > 0); - - for(uint8_t i = 0; i < length; ++i) { - uint8_t shift = 7 - i; - write_bit((byte >> shift) & 1, position + i, data); - } -} - -uint8_t ProtocolIoProx::get_encoded_data_size() { - return 8; -} - -uint8_t ProtocolIoProx::get_decoded_data_size() { - return 4; -} - -void ProtocolIoProx::encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - // Packet to transmit: - // - // 0 10 20 30 40 50 60 - // v v v v v v v - // 01234567 8 90123456 7 89012345 6 78901234 5 67890123 4 56789012 3 45678901 23 - // ----------------------------------------------------------------------------- - // 00000000 0 11110000 1 facility 1 version_ 1 code-one 1 code-two 1 checksum 11 - - // Preamble. - write_bits(0b00000000, 0, encoded_data, 8); - write_bit(0, 8, encoded_data); - - write_bits(0b11110000, 9, encoded_data, 8); - write_bit(1, 17, encoded_data); - - // Facility code. - write_bits(decoded_data[0], 18, encoded_data, 8); - write_bit(1, 26, encoded_data); - - // Version - write_bits(decoded_data[1], 27, encoded_data, 8); - write_bit(1, 35, encoded_data); - - // Code one - write_bits(decoded_data[2], 36, encoded_data, 8); - write_bit(1, 44, encoded_data); - - // Code two - write_bits(decoded_data[3], 45, encoded_data, 8); - write_bit(1, 53, encoded_data); - - // Checksum - write_bits(compute_checksum(encoded_data, 8), 54, encoded_data, 8); - write_bit(1, 62, encoded_data); - write_bit(1, 63, encoded_data); -} - -void ProtocolIoProx::decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) { - furi_check(decoded_data_size >= get_decoded_data_size()); - furi_check(encoded_data_size >= get_encoded_data_size()); - - // Packet structure: - // (Note: the second word seems fixed; but this may not be a guarantee; - // it currently has no meaning.) - // - //0 1 2 3 4 5 6 7 - //v v v v v v v v - //01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF - //----------------------------------------------------------------------- - //00000000 01111000 01FFFFFF FF1VVVVV VVV1CCCC CCCC1CCC CCCCC1XX XXXXXX11 - // - // F = facility code - // V = version - // C = code - // X = checksum - - // Facility code - decoded_data[0] = (encoded_data[2] << 2) | (encoded_data[3] >> 6); - - // Version code. - decoded_data[1] = (encoded_data[3] << 3) | (encoded_data[4] >> 5); - - // Code bytes. - decoded_data[2] = (encoded_data[4] << 4) | (encoded_data[5] >> 4); - decoded_data[3] = (encoded_data[5] << 5) | (encoded_data[6] >> 3); -} - -bool ProtocolIoProx::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) { - furi_check(encoded_data_size >= get_encoded_data_size()); - - // Packet framing - // - //0 1 2 3 4 5 6 7 - //v v v v v v v v - //01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF - //----------------------------------------------------------------------- - //00000000 01______ _1______ __1_____ ___1____ ____1___ _____1XX XXXXXX11 - // - // _ = variable data - // 0 = preamble 0 - // 1 = framing 1 - // X = checksum - - // Validate the packet preamble is there... - if(encoded_data[0] != 0b00000000) { - return false; - } - if((encoded_data[1] >> 6) != 0b01) { - return false; - } - - // ... check for known ones... - if((encoded_data[2] & 0b01000000) == 0) { - return false; - } - if((encoded_data[3] & 0b00100000) == 0) { - return false; - } - if((encoded_data[4] & 0b00010000) == 0) { - return false; - } - if((encoded_data[5] & 0b00001000) == 0) { - return false; - } - if((encoded_data[6] & 0b00000100) == 0) { - return false; - } - if((encoded_data[7] & 0b00000011) == 0) { - return false; - } - - // ... and validate our checksums. - uint8_t checksum = compute_checksum(encoded_data, 8); - uint8_t checkval = (encoded_data[6] << 6) | (encoded_data[7] >> 2); - - if(checksum != checkval) { - return false; - } - - return true; -} - -uint8_t ProtocolIoProx::compute_checksum(const uint8_t* data, const uint8_t data_size) { - furi_check(data_size == get_encoded_data_size()); - - // Packet structure: - // - //0 1 2 3 4 5 6 7 - //v v v v v v v v - //01234567 8 9ABCDEF0 1 23456789 A BCDEF012 3 456789AB C DEF01234 5 6789ABCD EF - //00000000 0 VVVVVVVV 1 WWWWWWWW 1 XXXXXXXX 1 YYYYYYYY 1 ZZZZZZZZ 1 CHECKSUM 11 - // - // algorithm as observed by the proxmark3 folks - // CHECKSUM == 0xFF - (V + W + X + Y + Z) - - uint8_t checksum = 0; - - checksum += (data[1] << 1) | (data[2] >> 7); // VVVVVVVVV - checksum += (data[2] << 2) | (data[3] >> 6); // WWWWWWWWW - checksum += (data[3] << 3) | (data[4] >> 5); // XXXXXXXXX - checksum += (data[4] << 4) | (data[5] >> 4); // YYYYYYYYY - checksum += (data[5] << 5) | (data[6] >> 3); // ZZZZZZZZZ - - return 0xFF - checksum; -} diff --git a/applications/lfrfid/helpers/protocols/protocol_ioprox.h b/applications/lfrfid/helpers/protocols/protocol_ioprox.h deleted file mode 100644 index 2fff53b171d..00000000000 --- a/applications/lfrfid/helpers/protocols/protocol_ioprox.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include "protocol_generic.h" - -class ProtocolIoProx : public ProtocolGeneric { -public: - uint8_t get_encoded_data_size() final; - uint8_t get_decoded_data_size() final; - - void encode( - const uint8_t* decoded_data, - const uint8_t decoded_data_size, - uint8_t* encoded_data, - const uint8_t encoded_data_size) final; - - void decode( - const uint8_t* encoded_data, - const uint8_t encoded_data_size, - uint8_t* decoded_data, - const uint8_t decoded_data_size) final; - - bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final; - -private: - /** Computes the IoProx checksum of the provided (decoded) data. */ - uint8_t compute_checksum(const uint8_t* data, const uint8_t data_size); -}; diff --git a/applications/lfrfid/helpers/pulse_joiner.cpp b/applications/lfrfid/helpers/pulse_joiner.cpp deleted file mode 100644 index c72019b1438..00000000000 --- a/applications/lfrfid/helpers/pulse_joiner.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "pulse_joiner.h" -#include - -bool PulseJoiner::push_pulse(bool polarity, uint16_t period, uint16_t pulse) { - bool result = false; - furi_check((pulse_index + 1) < pulse_max); - - if(polarity == false && pulse_index == 0) { - // first negative pulse is ommited - - } else { - pulses[pulse_index].polarity = polarity; - pulses[pulse_index].time = pulse; - pulse_index++; - } - - if(period > pulse) { - pulses[pulse_index].polarity = !polarity; - pulses[pulse_index].time = period - pulse; - pulse_index++; - } - - if(pulse_index >= 4) { - // we know that first pulse is always high - // so we wait 2 edges, hi2low and next low2hi - - uint8_t edges_count = 0; - bool last_polarity = pulses[0].polarity; - - for(uint8_t i = 1; i < pulse_index; i++) { - if(pulses[i].polarity != last_polarity) { - edges_count++; - last_polarity = pulses[i].polarity; - } - } - - if(edges_count >= 2) { - result = true; - } - } - - return result; -} - -void PulseJoiner::pop_pulse(uint16_t* period, uint16_t* pulse) { - furi_check(pulse_index <= (pulse_max + 1)); - - uint16_t tmp_period = 0; - uint16_t tmp_pulse = 0; - uint8_t edges_count = 0; - bool last_polarity = pulses[0].polarity; - uint8_t next_fist_pulse = 0; - - for(uint8_t i = 0; i < pulse_max; i++) { - // count edges - if(pulses[i].polarity != last_polarity) { - edges_count++; - last_polarity = pulses[i].polarity; - } - - // wait for 2 edges - if(edges_count == 2) { - next_fist_pulse = i; - break; - } - - // sum pulse time - if(pulses[i].polarity) { - tmp_period += pulses[i].time; - tmp_pulse += pulses[i].time; - } else { - tmp_period += pulses[i].time; - } - pulse_index--; - } - - *period = tmp_period; - *pulse = tmp_pulse; - - // remove counted periods and shift data - for(uint8_t i = 0; i < pulse_max; i++) { - if((next_fist_pulse + i) < pulse_max) { - pulses[i].polarity = pulses[next_fist_pulse + i].polarity; - pulses[i].time = pulses[next_fist_pulse + i].time; - } else { - break; - } - } -} - -PulseJoiner::PulseJoiner() { - for(uint8_t i = 0; i < pulse_max; i++) { - pulses[i] = {false, 0}; - } -} diff --git a/applications/lfrfid/helpers/pulse_joiner.h b/applications/lfrfid/helpers/pulse_joiner.h deleted file mode 100644 index 1639d837167..00000000000 --- a/applications/lfrfid/helpers/pulse_joiner.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once -#include "stdint.h" - -class PulseJoiner { -public: - /** - * @brief Push timer pulse. First negative pulse is ommited. - * - * @param polarity pulse polarity: true = high2low, false = low2high - * @param period overall period time in timer clicks - * @param pulse pulse time in timer clicks - * - * @return true - next pulse can and must be popped immediatly - */ - bool push_pulse(bool polarity, uint16_t period, uint16_t pulse); - - /** - * @brief Get the next timer pulse. Call only if push_pulse returns true. - * - * @param period overall period time in timer clicks - * @param pulse pulse time in timer clicks - */ - void pop_pulse(uint16_t* period, uint16_t* pulse); - - PulseJoiner(); - -private: - struct Pulse { - bool polarity; - uint16_t time; - }; - - uint8_t pulse_index = 0; - static const uint8_t pulse_max = 6; - Pulse pulses[pulse_max]; -}; diff --git a/applications/lfrfid/helpers/rfid_key.cpp b/applications/lfrfid/helpers/rfid_key.cpp deleted file mode 100644 index 2d99d40f26e..00000000000 --- a/applications/lfrfid/helpers/rfid_key.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "rfid_key.h" -#include -#include - -RfidKey::RfidKey() { - clear(); -} - -RfidKey::~RfidKey() { -} - -void RfidKey::set_type(LfrfidKeyType _type) { - type = _type; -} - -void RfidKey::set_data(const uint8_t* _data, const uint8_t _data_size) { - furi_assert(_data_size <= data.size()); - for(uint8_t i = 0; i < _data_size; i++) { - data[i] = _data[i]; - } -} - -void RfidKey::set_name(const char* _name) { - strlcpy(name, _name, get_name_length()); -} - -LfrfidKeyType RfidKey::get_type() { - return type; -} - -const uint8_t* RfidKey::get_data() { - return &data[0]; -} - -const char* RfidKey::get_type_text() { - return lfrfid_key_get_type_string(type); -} - -uint8_t RfidKey::get_type_data_count() const { - return lfrfid_key_get_type_data_count(type); -} - -char* RfidKey::get_name() { - return name; -} - -uint8_t RfidKey::get_name_length() { - return LFRFID_KEY_NAME_SIZE; -} - -void RfidKey::clear() { - set_name(""); - set_type(LfrfidKeyType::KeyEM4100); - data.fill(0); -} - -RfidKey& RfidKey::operator=(const RfidKey& rhs) { - if(this == &rhs) return *this; - - set_type(rhs.type); - set_name(rhs.name); - set_data(&rhs.data[0], get_type_data_count()); - - return *this; -} diff --git a/applications/lfrfid/helpers/rfid_key.h b/applications/lfrfid/helpers/rfid_key.h deleted file mode 100644 index 29b87cf9c0b..00000000000 --- a/applications/lfrfid/helpers/rfid_key.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include "key_info.h" -#include - -class RfidKey { -public: - RfidKey(); - ~RfidKey(); - - void set_type(LfrfidKeyType type); - void set_data(const uint8_t* data, const uint8_t data_size); - void set_name(const char* name); - - LfrfidKeyType get_type(); - const uint8_t* get_data(); - const char* get_type_text(); - uint8_t get_type_data_count() const; - char* get_name(); - uint8_t get_name_length(); - void clear(); - RfidKey& operator=(const RfidKey& rhs); - -private: - std::array data; - LfrfidKeyType type; - char name[LFRFID_KEY_NAME_SIZE + 1]; -}; diff --git a/applications/lfrfid/helpers/rfid_reader.cpp b/applications/lfrfid/helpers/rfid_reader.cpp deleted file mode 100644 index 029b1cb4bd3..00000000000 --- a/applications/lfrfid/helpers/rfid_reader.cpp +++ /dev/null @@ -1,175 +0,0 @@ -#include "rfid_reader.h" -#include -#include -#include - -/** - * @brief private violation assistant for RfidReader - */ -struct RfidReaderAccessor { - static void decode(RfidReader& rfid_reader, bool polarity) { - rfid_reader.decode(polarity); - } -}; - -void RfidReader::decode(bool polarity) { - uint32_t current_dwt_value = DWT->CYCCNT; - uint32_t period = current_dwt_value - last_dwt_value; - last_dwt_value = current_dwt_value; - -#ifdef RFID_GPIO_DEBUG - decoder_gpio_out.process_front(polarity, period); -#endif - - switch(type) { - case Type::Normal: - decoder_em.process_front(polarity, period); - decoder_hid26.process_front(polarity, period); - decoder_ioprox.process_front(polarity, period); - break; - case Type::Indala: - decoder_em.process_front(polarity, period); - decoder_hid26.process_front(polarity, period); - decoder_ioprox.process_front(polarity, period); - decoder_indala.process_front(polarity, period); - break; - } - - detect_ticks++; -} - -bool RfidReader::switch_timer_elapsed() { - const uint32_t seconds_to_switch = furi_kernel_get_tick_frequency() * 2.0f; - return (furi_get_tick() - switch_os_tick_last) > seconds_to_switch; -} - -void RfidReader::switch_timer_reset() { - switch_os_tick_last = furi_get_tick(); -} - -void RfidReader::switch_mode() { - switch(type) { - case Type::Normal: - type = Type::Indala; - furi_hal_rfid_change_read_config(62500.0f, 0.25f); - break; - case Type::Indala: - type = Type::Normal; - furi_hal_rfid_change_read_config(125000.0f, 0.5f); - break; - } - - switch_timer_reset(); -} - -static void comparator_trigger_callback(bool level, void* comp_ctx) { - RfidReader* _this = static_cast(comp_ctx); - - RfidReaderAccessor::decode(*_this, !level); -} - -RfidReader::RfidReader() { -} - -void RfidReader::start() { - type = Type::Normal; - - furi_hal_rfid_pins_read(); - furi_hal_rfid_tim_read(125000, 0.5); - furi_hal_rfid_tim_read_start(); - start_comparator(); - - switch_timer_reset(); - last_read_count = 0; -} - -void RfidReader::start_forced(RfidReader::Type _type) { - start(); - if(_type == Type::Indala) { - switch_mode(); - } -} - -void RfidReader::stop() { - furi_hal_rfid_pins_reset(); - furi_hal_rfid_tim_read_stop(); - furi_hal_rfid_tim_reset(); - stop_comparator(); -} - -bool RfidReader::read(LfrfidKeyType* _type, uint8_t* data, uint8_t data_size, bool switch_enable) { - bool result = false; - bool something_read = false; - - // reading - if(decoder_em.read(data, data_size)) { - *_type = LfrfidKeyType::KeyEM4100; - something_read = true; - } - - if(decoder_hid26.read(data, data_size)) { - *_type = LfrfidKeyType::KeyH10301; - something_read = true; - } - - if(decoder_ioprox.read(data, data_size)) { - *_type = LfrfidKeyType::KeyIoProxXSF; - something_read = true; - } - - if(decoder_indala.read(data, data_size)) { - *_type = LfrfidKeyType::KeyI40134; - something_read = true; - } - - // validation - if(something_read) { - switch_timer_reset(); - - if(last_read_type == *_type && memcmp(last_read_data, data, data_size) == 0) { - last_read_count = last_read_count + 1; - - if(last_read_count > 2) { - result = true; - } - } else { - last_read_type = *_type; - memcpy(last_read_data, data, data_size); - last_read_count = 0; - } - } - - // mode switching - if(switch_enable && switch_timer_elapsed()) { - switch_mode(); - last_read_count = 0; - } - - return result; -} - -bool RfidReader::detect() { - bool detected = false; - if(detect_ticks > 10) { - detected = true; - } - detect_ticks = 0; - - return detected; -} - -bool RfidReader::any_read() { - return last_read_count > 0; -} - -void RfidReader::start_comparator(void) { - furi_hal_rfid_comp_set_callback(comparator_trigger_callback, this); - last_dwt_value = DWT->CYCCNT; - - furi_hal_rfid_comp_start(); -} - -void RfidReader::stop_comparator(void) { - furi_hal_rfid_comp_stop(); - furi_hal_rfid_comp_set_callback(NULL, NULL); -} diff --git a/applications/lfrfid/helpers/rfid_reader.h b/applications/lfrfid/helpers/rfid_reader.h deleted file mode 100644 index 903bbecf93e..00000000000 --- a/applications/lfrfid/helpers/rfid_reader.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once -//#include "decoder_analyzer.h" -#include "decoder_gpio_out.h" -#include "decoder_emmarin.h" -#include "decoder_hid26.h" -#include "decoder_indala.h" -#include "decoder_ioprox.h" -#include "key_info.h" - -//#define RFID_GPIO_DEBUG 1 - -class RfidReader { -public: - enum class Type : uint8_t { - Normal, - Indala, - }; - - RfidReader(); - void start(); - void start_forced(RfidReader::Type type); - void stop(); - bool read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size, bool switch_enable = true); - - bool detect(); - bool any_read(); - -private: - friend struct RfidReaderAccessor; - - //DecoderAnalyzer decoder_analyzer; -#ifdef RFID_GPIO_DEBUG - DecoderGpioOut decoder_gpio_out; -#endif - DecoderEMMarin decoder_em; - DecoderHID26 decoder_hid26; - DecoderIndala decoder_indala; - DecoderIoProx decoder_ioprox; - - uint32_t last_dwt_value; - - void start_comparator(void); - void stop_comparator(void); - - void decode(bool polarity); - - uint32_t detect_ticks; - - uint32_t switch_os_tick_last; - bool switch_timer_elapsed(); - void switch_timer_reset(); - void switch_mode(); - - LfrfidKeyType last_read_type; - uint8_t last_read_data[LFRFID_KEY_SIZE]; - uint8_t last_read_count; - - Type type = Type::Normal; -}; diff --git a/applications/lfrfid/helpers/rfid_timer_emulator.cpp b/applications/lfrfid/helpers/rfid_timer_emulator.cpp deleted file mode 100644 index f5337c31dd9..00000000000 --- a/applications/lfrfid/helpers/rfid_timer_emulator.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "rfid_timer_emulator.h" - -RfidTimerEmulator::RfidTimerEmulator() { -} - -RfidTimerEmulator::~RfidTimerEmulator() { - std::map::iterator it; - - for(it = encoders.begin(); it != encoders.end(); ++it) { - delete it->second; - } - - encoders.clear(); -} - -void RfidTimerEmulator::start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size) { - if(encoders.count(type)) { - current_encoder = encoders.find(type)->second; - - if(data_size >= lfrfid_key_get_type_data_count(type)) { - current_encoder->init(data, data_size); - - furi_hal_rfid_tim_emulate(125000); - furi_hal_rfid_pins_emulate(); - - furi_hal_rfid_tim_emulate_start(RfidTimerEmulator::timer_update_callback, this); - } - } else { - // not found - } -} - -void RfidTimerEmulator::stop() { - furi_hal_rfid_tim_emulate_stop(); - furi_hal_rfid_tim_reset(); - furi_hal_rfid_pins_reset(); -} - -void RfidTimerEmulator::timer_update_callback(void* ctx) { - RfidTimerEmulator* _this = static_cast(ctx); - - bool result; - bool polarity; - uint16_t period; - uint16_t pulse; - - do { - _this->current_encoder->get_next(&polarity, &period, &pulse); - result = _this->pulse_joiner.push_pulse(polarity, period, pulse); - } while(result == false); - - _this->pulse_joiner.pop_pulse(&period, &pulse); - - furi_hal_rfid_set_emulate_period(period - 1); - furi_hal_rfid_set_emulate_pulse(pulse); -} diff --git a/applications/lfrfid/helpers/rfid_timer_emulator.h b/applications/lfrfid/helpers/rfid_timer_emulator.h deleted file mode 100644 index 2129a1c7f86..00000000000 --- a/applications/lfrfid/helpers/rfid_timer_emulator.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include -#include "key_info.h" -#include "encoder_generic.h" -#include "encoder_emmarin.h" -#include "encoder_hid_h10301.h" -#include "encoder_indala_40134.h" -#include "encoder_ioprox.h" -#include "pulse_joiner.h" -#include - -class RfidTimerEmulator { -public: - RfidTimerEmulator(); - ~RfidTimerEmulator(); - void start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size); - void stop(); - -private: - EncoderGeneric* current_encoder = nullptr; - - std::map encoders = { - {LfrfidKeyType::KeyEM4100, new EncoderEM()}, - {LfrfidKeyType::KeyH10301, new EncoderHID_H10301()}, - {LfrfidKeyType::KeyI40134, new EncoderIndala_40134()}, - {LfrfidKeyType::KeyIoProxXSF, new EncoderIoProx()}, - }; - - PulseJoiner pulse_joiner; - static void timer_update_callback(void* ctx); -}; diff --git a/applications/lfrfid/helpers/rfid_worker.cpp b/applications/lfrfid/helpers/rfid_worker.cpp deleted file mode 100644 index af15a340fba..00000000000 --- a/applications/lfrfid/helpers/rfid_worker.cpp +++ /dev/null @@ -1,136 +0,0 @@ -#include "rfid_worker.h" - -RfidWorker::RfidWorker() { -} - -RfidWorker::~RfidWorker() { -} - -void RfidWorker::start_read() { - reader.start(); -} - -bool RfidWorker::read() { - static const uint8_t data_size = LFRFID_KEY_SIZE; - uint8_t data[data_size] = {0}; - LfrfidKeyType type; - - bool result = reader.read(&type, data, data_size); - - if(result) { - key.set_type(type); - key.set_data(data, data_size); - }; - - return result; -} - -bool RfidWorker::detect() { - return reader.detect(); -} - -bool RfidWorker::any_read() { - return reader.any_read(); -} - -void RfidWorker::stop_read() { - reader.stop(); -} - -void RfidWorker::start_write() { - write_result = WriteResult::Nothing; - write_sequence = new TickSequencer(); - validate_counts = 0; - - write_sequence->do_every_tick(1, std::bind(&RfidWorker::sq_write, this)); - write_sequence->do_after_tick(2, std::bind(&RfidWorker::sq_write_start_validate, this)); - write_sequence->do_every_tick(30, std::bind(&RfidWorker::sq_write_validate, this)); - write_sequence->do_every_tick(1, std::bind(&RfidWorker::sq_write_stop_validate, this)); -} - -RfidWorker::WriteResult RfidWorker::write() { - write_sequence->tick(); - return write_result; -} - -void RfidWorker::stop_write() { - delete write_sequence; - reader.stop(); -} - -void RfidWorker::start_emulate() { - emulator.start(key.get_type(), key.get_data(), key.get_type_data_count()); -} - -void RfidWorker::stop_emulate() { - emulator.stop(); -} - -void RfidWorker::sq_write() { - for(size_t i = 0; i < 5; i++) { - switch(key.get_type()) { - case LfrfidKeyType::KeyEM4100: - writer.start(); - writer.write_em(key.get_data()); - writer.stop(); - break; - case LfrfidKeyType::KeyH10301: - writer.start(); - writer.write_hid(key.get_data()); - writer.stop(); - break; - case LfrfidKeyType::KeyI40134: - writer.start(); - writer.write_indala(key.get_data()); - writer.stop(); - break; - case LfrfidKeyType::KeyIoProxXSF: - writer.start(); - writer.write_ioprox(key.get_data()); - writer.stop(); - break; - } - } -} - -void RfidWorker::sq_write_start_validate() { - switch(key.get_type()) { - case LfrfidKeyType::KeyEM4100: - case LfrfidKeyType::KeyH10301: - case LfrfidKeyType::KeyIoProxXSF: - reader.start_forced(RfidReader::Type::Normal); - break; - case LfrfidKeyType::KeyI40134: - reader.start_forced(RfidReader::Type::Indala); - break; - } -} - -void RfidWorker::sq_write_validate() { - static const uint8_t data_size = LFRFID_KEY_SIZE; - uint8_t data[data_size] = {0}; - LfrfidKeyType type; - - bool result = reader.read(&type, data, data_size); - - if(result && (write_result != WriteResult::Ok)) { - if(validate_counts > (5 * 60)) { - write_result = WriteResult::NotWritable; - } - - if(type == key.get_type()) { - if(memcmp(data, key.get_data(), key.get_type_data_count()) == 0) { - write_result = WriteResult::Ok; - validate_counts = 0; - } else { - validate_counts++; - } - } else { - validate_counts++; - } - }; -} - -void RfidWorker::sq_write_stop_validate() { - reader.stop(); -} diff --git a/applications/lfrfid/helpers/rfid_worker.h b/applications/lfrfid/helpers/rfid_worker.h deleted file mode 100644 index 2c49ad14e06..00000000000 --- a/applications/lfrfid/helpers/rfid_worker.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once -#include "key_info.h" -#include "rfid_reader.h" -#include "rfid_writer.h" -#include "rfid_timer_emulator.h" -#include "rfid_key.h" -#include "state_sequencer.h" - -class RfidWorker { -public: - RfidWorker(); - ~RfidWorker(); - - void start_read(); - bool read(); - bool detect(); - bool any_read(); - void stop_read(); - - enum class WriteResult : uint8_t { - Ok, - NotWritable, - Nothing, - }; - - void start_write(); - WriteResult write(); - void stop_write(); - - void start_emulate(); - void stop_emulate(); - - RfidKey key; - -private: - RfidWriter writer; - RfidReader reader; - RfidTimerEmulator emulator; - - WriteResult write_result; - TickSequencer* write_sequence; - - void sq_write(); - void sq_write_start_validate(); - void sq_write_validate(); - uint16_t validate_counts; - void sq_write_stop_validate(); -}; diff --git a/applications/lfrfid/helpers/rfid_writer.cpp b/applications/lfrfid/helpers/rfid_writer.cpp deleted file mode 100644 index bb9d3fb65e0..00000000000 --- a/applications/lfrfid/helpers/rfid_writer.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#include "rfid_writer.h" -#include "protocols/protocol_ioprox.h" -#include -#include "protocols/protocol_emmarin.h" -#include "protocols/protocol_hid_h10301.h" -#include "protocols/protocol_indala_40134.h" - -/** - * @brief all timings are specified in field clocks (field clock = 125 kHz, 8 us) - * - */ -class T55xxTiming { -public: - constexpr static const uint16_t wait_time = 400; - constexpr static const uint8_t start_gap = 30; - constexpr static const uint8_t write_gap = 18; - constexpr static const uint8_t data_0 = 24; - constexpr static const uint8_t data_1 = 56; - constexpr static const uint16_t program = 700; -}; - -class T55xxCmd { -public: - constexpr static const uint8_t opcode_page_0 = 0b10; - constexpr static const uint8_t opcode_page_1 = 0b11; - constexpr static const uint8_t opcode_reset = 0b00; -}; - -RfidWriter::RfidWriter() { -} - -RfidWriter::~RfidWriter() { -} - -void RfidWriter::start() { - furi_hal_rfid_tim_read(125000, 0.5); - furi_hal_rfid_pins_read(); - furi_hal_rfid_tim_read_start(); - - // do not ground the antenna - furi_hal_rfid_pin_pull_release(); -} - -void RfidWriter::stop() { - furi_hal_rfid_tim_read_stop(); - furi_hal_rfid_tim_reset(); - furi_hal_rfid_pins_reset(); -} - -void RfidWriter::write_gap(uint32_t gap_time) { - furi_hal_rfid_tim_read_stop(); - furi_delay_us(gap_time * 8); - furi_hal_rfid_tim_read_start(); -} - -void RfidWriter::write_bit(bool value) { - if(value) { - furi_delay_us(T55xxTiming::data_1 * 8); - } else { - furi_delay_us(T55xxTiming::data_0 * 8); - } - write_gap(T55xxTiming::write_gap); -} - -void RfidWriter::write_byte(uint8_t value) { - for(uint8_t i = 0; i < 8; i++) { - write_bit((value >> i) & 1); - } -} - -void RfidWriter::write_block( - uint8_t page, - uint8_t block, - bool lock_bit, - uint32_t data, - bool password_enable, - uint32_t password) { - furi_delay_us(T55xxTiming::wait_time * 8); - - //client: https://github.com/Proxmark/proxmark3/blob/6116334485ca77343eda51c557cdc81032afcf38/client/cmdlft55xx.c#L944 - //hardware: https://github.com/Proxmark/proxmark3/blob/555fa197730c061bbf0ab01334e99bc47fb3dc06/armsrc/lfops.c#L1465 - //hardware: https://github.com/Proxmark/proxmark3/blob/555fa197730c061bbf0ab01334e99bc47fb3dc06/armsrc/lfops.c#L1396 - - // start gap - write_gap(T55xxTiming::start_gap); - - // opcode - switch(page) { - case 0: - write_bit(1); - write_bit(0); - break; - case 1: - write_bit(1); - write_bit(1); - break; - default: - furi_check(false); - break; - } - - // password - if(password_enable) { - for(uint8_t i = 0; i < 32; i++) { - write_bit((password >> (31 - i)) & 1); - } - } - - // lock bit - write_bit(lock_bit); - - // data - for(uint8_t i = 0; i < 32; i++) { - write_bit((data >> (31 - i)) & 1); - } - - // block address - write_bit((block >> 2) & 1); - write_bit((block >> 1) & 1); - write_bit((block >> 0) & 1); - - furi_delay_us(T55xxTiming::program * 8); - - furi_delay_us(T55xxTiming::wait_time * 8); - write_reset(); -} - -void RfidWriter::write_reset() { - write_gap(T55xxTiming::start_gap); - write_bit(1); - write_bit(0); -} - -void RfidWriter::write_em(const uint8_t em_data[5]) { - ProtocolEMMarin em_card; - uint64_t em_encoded_data; - em_card.encode(em_data, 5, reinterpret_cast(&em_encoded_data), sizeof(uint64_t)); - const uint32_t em_config_block_data = 0b00000000000101001000000001000000; - - FURI_CRITICAL_ENTER(); - write_block( - 0, - 0, - false, - em_config_block_data, - false, - 0x0); // если конфиг блок запишется с PWR битом 0, то следующие записи блока не пройдут, если в них будет пароль? Нужен какой-то механизм для ситуации снятия пароля. Или отдельную функцию, которая читает нулевой блок с паролем, меняет в нем биты AOR/PWD на 0, и записывает обратно? - write_block(0, 1, false, em_encoded_data, false, 0x0); - write_block(0, 2, false, em_encoded_data >> 32, false, 0x0); - write_reset(); - FURI_CRITICAL_EXIT(); -} - -void RfidWriter::write_hid(const uint8_t hid_data[3]) { - ProtocolHID10301 hid_card; - uint32_t card_data[3]; - hid_card.encode(hid_data, 3, reinterpret_cast(&card_data), sizeof(card_data) * 3); - - const uint32_t hid_config_block_data = 0b00000000000100000111000001100000; - - FURI_CRITICAL_ENTER(); - write_block(0, 0, false, hid_config_block_data, false, 0x0); - write_block(0, 1, false, card_data[0], false, 0x0); - write_block(0, 2, false, card_data[1], false, 0x0); - write_block(0, 3, false, card_data[2], false, 0x0); - write_reset(); - FURI_CRITICAL_EXIT(); -} - -/** Endian fixup. Translates an ioprox block into a t5577 block */ -static uint32_t ioprox_encode_block(const uint8_t block_data[4]) { - uint8_t raw_card_data[] = {block_data[3], block_data[2], block_data[1], block_data[0]}; - return *reinterpret_cast(&raw_card_data); -} - -void RfidWriter::write_ioprox(const uint8_t ioprox_data[4]) { - ProtocolIoProx ioprox_card; - - uint8_t encoded_data[8]; - ioprox_card.encode(ioprox_data, 4, encoded_data, sizeof(encoded_data)); - - const uint32_t ioprox_config_block_data = 0b00000000000101000111000001000000; - - FURI_CRITICAL_ENTER(); - write_block(0, 0, false, ioprox_config_block_data, false, 0x0); - write_block(0, 1, false, ioprox_encode_block(&encoded_data[0]), false, 0x0); - write_block(0, 2, false, ioprox_encode_block(&encoded_data[4]), false, 0x0); - write_reset(); - FURI_CRITICAL_EXIT(); -} - -void RfidWriter::write_indala(const uint8_t indala_data[3]) { - ProtocolIndala40134 indala_card; - uint32_t card_data[2]; - indala_card.encode( - indala_data, 3, reinterpret_cast(&card_data), sizeof(card_data) * 2); - - const uint32_t indala_config_block_data = 0b00000000000010000001000001000000; - - FURI_CRITICAL_ENTER(); - write_block(0, 0, false, indala_config_block_data, false, 0x0); - write_block(0, 1, false, card_data[0], false, 0x0); - write_block(0, 2, false, card_data[1], false, 0x0); - write_reset(); - FURI_CRITICAL_EXIT(); -} diff --git a/applications/lfrfid/helpers/rfid_writer.h b/applications/lfrfid/helpers/rfid_writer.h deleted file mode 100644 index 6efaeb379d8..00000000000 --- a/applications/lfrfid/helpers/rfid_writer.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include "stdint.h" - -class RfidWriter { -public: - RfidWriter(); - ~RfidWriter(); - void start(); - void stop(); - void write_em(const uint8_t em_data[5]); - void write_hid(const uint8_t hid_data[3]); - void write_ioprox(const uint8_t ioprox_data[4]); - void write_indala(const uint8_t indala_data[3]); - void write_block(uint8_t page, uint8_t block, bool lock_bit, uint32_t data, bool password_enable, uint32_t password); - void write_reset(); - -private: - void write_gap(uint32_t gap_time); - void write_bit(bool value); - void write_byte(uint8_t value); -}; diff --git a/applications/lfrfid/helpers/state_sequencer.cpp b/applications/lfrfid/helpers/state_sequencer.cpp deleted file mode 100644 index e6718df5cd9..00000000000 --- a/applications/lfrfid/helpers/state_sequencer.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "state_sequencer.h" -#include "stdio.h" - -TickSequencer::TickSequencer() { -} - -TickSequencer::~TickSequencer() { -} - -void TickSequencer::tick() { - if(tick_count == list_it->first) { - tick_count = 0; - - list_it++; - if(list_it == list.end()) { - list_it = list.begin(); - } - } - - list_it->second(); - tick_count++; -} - -void TickSequencer::reset() { - list_it = list.begin(); - tick_count = 0; -} - -void TickSequencer::clear() { - list.clear(); - reset(); -} - -void TickSequencer::do_every_tick(uint32_t tick_count, std::function fn) { - list.push_back(std::make_pair(tick_count, fn)); - reset(); -} - -void TickSequencer::do_after_tick(uint32_t tick_count, std::function fn) { - if(tick_count > 1) { - list.push_back( - std::make_pair(tick_count - 1, std::bind(&TickSequencer::do_nothing, this))); - } - list.push_back(std::make_pair(1, fn)); - - reset(); -} - -void TickSequencer::do_nothing() { -} diff --git a/applications/lfrfid/helpers/state_sequencer.h b/applications/lfrfid/helpers/state_sequencer.h deleted file mode 100644 index 12512ab5172..00000000000 --- a/applications/lfrfid/helpers/state_sequencer.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include "stdint.h" -#include -#include - -class TickSequencer { -public: - TickSequencer(); - ~TickSequencer(); - - void tick(); - void reset(); - void clear(); - - void do_every_tick(uint32_t tick_count, std::function fn); - void do_after_tick(uint32_t tick_count, std::function fn); - -private: - std::list > > list; - std::list > >::iterator list_it; - - uint32_t tick_count; - - void do_nothing(); -}; diff --git a/applications/lfrfid/lfrfid_app.cpp b/applications/lfrfid/lfrfid_app.cpp deleted file mode 100644 index f1a575de540..00000000000 --- a/applications/lfrfid/lfrfid_app.cpp +++ /dev/null @@ -1,236 +0,0 @@ -#include "lfrfid_app.h" -#include "assets_icons.h" -#include -#include "m-string.h" -#include "scene/lfrfid_app_scene_start.h" -#include "scene/lfrfid_app_scene_read.h" -#include "scene/lfrfid_app_scene_read_success.h" -#include "scene/lfrfid_app_scene_retry_confirm.h" -#include "scene/lfrfid_app_scene_exit_confirm.h" -#include "scene/lfrfid_app_scene_read_menu.h" -#include "scene/lfrfid_app_scene_write.h" -#include "scene/lfrfid_app_scene_write_success.h" -#include "scene/lfrfid_app_scene_emulate.h" -#include "scene/lfrfid_app_scene_save_name.h" -#include "scene/lfrfid_app_scene_save_success.h" -#include "scene/lfrfid_app_scene_select_key.h" -#include "scene/lfrfid_app_scene_saved_key_menu.h" -#include "scene/lfrfid_app_scene_save_data.h" -#include "scene/lfrfid_app_scene_save_type.h" -#include "scene/lfrfid_app_scene_saved_info.h" -#include "scene/lfrfid_app_scene_delete_confirm.h" -#include "scene/lfrfid_app_scene_delete_success.h" -#include "scene/lfrfid_app_scene_rpc.h" - -#include -#include - -#include - -const char* LfRfidApp::app_folder = ANY_PATH("lfrfid"); -const char* LfRfidApp::app_extension = ".rfid"; -const char* LfRfidApp::app_filetype = "Flipper RFID key"; - -LfRfidApp::LfRfidApp() - : scene_controller{this} - , notification{"notification"} - , storage{"storage"} - , dialogs{"dialogs"} - , text_store(40) { - string_init_set_str(file_path, app_folder); -} - -LfRfidApp::~LfRfidApp() { - string_clear(file_path); - if(rpc_ctx) { - rpc_system_app_set_callback(rpc_ctx, NULL, NULL); - rpc_system_app_send_exited(rpc_ctx); - } -} - -static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) { - furi_assert(context); - LfRfidApp* app = static_cast(context); - - if(rpc_event == RpcAppEventSessionClose) { - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::RpcSessionClose; - app->view_controller.send_event(&event); - // Detach RPC - rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL); - app->rpc_ctx = NULL; - } else if(rpc_event == RpcAppEventAppExit) { - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Exit; - app->view_controller.send_event(&event); - } else if(rpc_event == RpcAppEventLoadFile) { - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::RpcLoadFile; - app->view_controller.send_event(&event); - } else { - rpc_system_app_confirm(app->rpc_ctx, rpc_event, false); - } -} - -void LfRfidApp::run(void* _args) { - const char* args = reinterpret_cast(_args); - - make_app_folder(); - - if(args && strlen(args)) { - uint32_t rpc_ctx_ptr = 0; - if(sscanf(args, "RPC %lX", &rpc_ctx_ptr) == 1) { - rpc_ctx = (RpcAppSystem*)rpc_ctx_ptr; - rpc_system_app_set_callback(rpc_ctx, rpc_command_callback, this); - rpc_system_app_send_started(rpc_ctx); - view_controller.attach_to_gui(ViewDispatcherTypeDesktop); - scene_controller.add_scene(SceneType::Rpc, new LfRfidAppSceneRpc()); - scene_controller.process(100, SceneType::Rpc); - } else { - string_set_str(file_path, args); - load_key_data(file_path, &worker.key, true); - view_controller.attach_to_gui(ViewDispatcherTypeFullscreen); - scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate()); - scene_controller.process(100, SceneType::Emulate); - } - - } else { - view_controller.attach_to_gui(ViewDispatcherTypeFullscreen); - scene_controller.add_scene(SceneType::Start, new LfRfidAppSceneStart()); - scene_controller.add_scene(SceneType::Read, new LfRfidAppSceneRead()); - scene_controller.add_scene(SceneType::RetryConfirm, new LfRfidAppSceneRetryConfirm()); - scene_controller.add_scene(SceneType::ExitConfirm, new LfRfidAppSceneExitConfirm()); - scene_controller.add_scene(SceneType::ReadSuccess, new LfRfidAppSceneReadSuccess()); - scene_controller.add_scene(SceneType::ReadKeyMenu, new LfRfidAppSceneReadKeyMenu()); - scene_controller.add_scene(SceneType::Write, new LfRfidAppSceneWrite()); - scene_controller.add_scene(SceneType::WriteSuccess, new LfRfidAppSceneWriteSuccess()); - scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate()); - scene_controller.add_scene(SceneType::SaveName, new LfRfidAppSceneSaveName()); - scene_controller.add_scene(SceneType::SaveSuccess, new LfRfidAppSceneSaveSuccess()); - scene_controller.add_scene(SceneType::SelectKey, new LfRfidAppSceneSelectKey()); - scene_controller.add_scene(SceneType::SavedKeyMenu, new LfRfidAppSceneSavedKeyMenu()); - scene_controller.add_scene(SceneType::SaveData, new LfRfidAppSceneSaveData()); - scene_controller.add_scene(SceneType::SaveType, new LfRfidAppSceneSaveType()); - scene_controller.add_scene(SceneType::SavedInfo, new LfRfidAppSceneSavedInfo()); - scene_controller.add_scene(SceneType::DeleteConfirm, new LfRfidAppSceneDeleteConfirm()); - scene_controller.add_scene(SceneType::DeleteSuccess, new LfRfidAppSceneDeleteSuccess()); - scene_controller.process(100); - } -} - -bool LfRfidApp::save_key(RfidKey* key) { - bool result = false; - - make_app_folder(); - - if(string_end_with_str_p(file_path, app_extension)) { - size_t filename_start = string_search_rchar(file_path, '/'); - string_left(file_path, filename_start); - } - - string_cat_printf(file_path, "/%s%s", key->get_name(), app_extension); - - result = save_key_data(file_path, key); - return result; -} - -bool LfRfidApp::load_key_from_file_select(bool need_restore) { - if(!need_restore) { - string_set_str(file_path, app_folder); - } - - bool result = dialog_file_browser_show( - dialogs, file_path, file_path, app_extension, true, &I_125_10px, true); - - if(result) { - result = load_key_data(file_path, &worker.key, true); - } - - return result; -} - -bool LfRfidApp::delete_key(RfidKey* key) { - UNUSED(key); - return storage_simply_remove(storage, string_get_cstr(file_path)); -} - -bool LfRfidApp::load_key_data(string_t path, RfidKey* key, bool show_dialog) { - FlipperFormat* file = flipper_format_file_alloc(storage); - bool result = false; - string_t str_result; - string_init(str_result); - - do { - if(!flipper_format_file_open_existing(file, string_get_cstr(path))) break; - - // header - uint32_t version; - if(!flipper_format_read_header(file, str_result, &version)) break; - if(string_cmp_str(str_result, app_filetype) != 0) break; - if(version != 1) break; - - // key type - LfrfidKeyType type; - RfidKey loaded_key; - - if(!flipper_format_read_string(file, "Key type", str_result)) break; - if(!lfrfid_key_get_string_type(string_get_cstr(str_result), &type)) break; - loaded_key.set_type(type); - - // key data - uint8_t key_data[loaded_key.get_type_data_count()] = {}; - if(!flipper_format_read_hex(file, "Data", key_data, loaded_key.get_type_data_count())) - break; - loaded_key.set_data(key_data, loaded_key.get_type_data_count()); - - path_extract_filename(path, str_result, true); - loaded_key.set_name(string_get_cstr(str_result)); - - *key = loaded_key; - result = true; - } while(0); - - flipper_format_free(file); - string_clear(str_result); - - if((!result) && (show_dialog)) { - dialog_message_show_storage_error(dialogs, "Cannot load\nkey file"); - } - - return result; -} - -bool LfRfidApp::save_key_data(string_t path, RfidKey* key) { - FlipperFormat* file = flipper_format_file_alloc(storage); - bool result = false; - - do { - if(!flipper_format_file_open_always(file, string_get_cstr(path))) break; - if(!flipper_format_write_header_cstr(file, app_filetype, 1)) break; - if(!flipper_format_write_comment_cstr(file, "Key type can be EM4100, H10301 or I40134")) - break; - if(!flipper_format_write_string_cstr( - file, "Key type", lfrfid_key_get_type_string(key->get_type()))) - break; - if(!flipper_format_write_comment_cstr( - file, "Data size for EM4100 is 5, for H10301 is 3, for I40134 is 3")) - break; - if(!flipper_format_write_hex(file, "Data", key->get_data(), key->get_type_data_count())) - break; - result = true; - } while(0); - - flipper_format_free(file); - - if(!result) { - dialog_message_show_storage_error(dialogs, "Cannot save\nkey file"); - } - - return result; -} - -void LfRfidApp::make_app_folder() { - if(!storage_simply_mkdir(storage, app_folder)) { - dialog_message_show_storage_error(dialogs, "Cannot create\napp folder"); - } -} diff --git a/applications/lfrfid/lfrfid_app.h b/applications/lfrfid/lfrfid_app.h deleted file mode 100644 index db022c9aaef..00000000000 --- a/applications/lfrfid/lfrfid_app.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once -#include "m-string.h" -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include "view/container_vm.h" - -#include -#include -#include - -#include "helpers/rfid_worker.h" -#include "rpc/rpc_app.h" - -class LfRfidApp { -public: - enum class EventType : uint8_t { - GENERIC_EVENT_ENUM_VALUES, - Next, - MenuSelected, - Stay, - Retry, - Exit, - EmulateStart, - RpcLoadFile, - RpcSessionClose, - }; - - enum class SceneType : uint8_t { - GENERIC_SCENE_ENUM_VALUES, - Read, - ReadSuccess, - RetryConfirm, - ExitConfirm, - ReadKeyMenu, - Write, - WriteSuccess, - Emulate, - SaveName, - SaveSuccess, - SelectKey, - SavedKeyMenu, - SaveData, - SaveType, - SavedInfo, - DeleteConfirm, - DeleteSuccess, - Rpc, - }; - - class Event { - public: - union { - int32_t menu_index; - } payload; - - EventType type; - }; - - SceneController, LfRfidApp> scene_controller; - ViewController - view_controller; - - ~LfRfidApp(); - LfRfidApp(); - - RecordController notification; - RecordController storage; - RecordController dialogs; - - RfidWorker worker; - - TextStore text_store; - - string_t file_path; - - RpcAppSystem* rpc_ctx; - - void run(void* args); - - static const char* app_folder; - static const char* app_extension; - static const char* app_filetype; - - bool save_key(RfidKey* key); - bool load_key_from_file_select(bool need_restore); - bool delete_key(RfidKey* key); - - bool load_key_data(string_t path, RfidKey* key, bool show_dialog); - bool save_key_data(string_t path, RfidKey* key); - - void make_app_folder(); -}; diff --git a/applications/lfrfid/lfrfid_app_launcher.cpp b/applications/lfrfid/lfrfid_app_launcher.cpp deleted file mode 100644 index 2855850f569..00000000000 --- a/applications/lfrfid/lfrfid_app_launcher.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "lfrfid_app.h" - -// app enter function -extern "C" int32_t lfrfid_app(void* args) { - LfRfidApp* app = new LfRfidApp(); - app->run(args); - delete app; - - return 0; -} diff --git a/applications/lfrfid/lfrfid_cli.cpp b/applications/lfrfid/lfrfid_cli.cpp deleted file mode 100644 index 8de8e63a8e3..00000000000 --- a/applications/lfrfid/lfrfid_cli.cpp +++ /dev/null @@ -1,225 +0,0 @@ -#include -#include -#include -#include -#include - -#include "helpers/rfid_reader.h" -#include "helpers/rfid_writer.h" -#include "helpers/rfid_timer_emulator.h" - -static void lfrfid_cli(Cli* cli, string_t args, void* context); - -// app cli function -extern "C" void lfrfid_on_system_start() { -#ifdef SRV_CLI - Cli* cli = static_cast(furi_record_open("cli")); - cli_add_command(cli, "rfid", CliCommandFlagDefault, lfrfid_cli, NULL); - furi_record_close("cli"); -#else - UNUSED(lfrfid_cli); -#endif -} - -void lfrfid_cli_print_usage() { - printf("Usage:\r\n"); - printf("rfid read \r\n"); - printf("rfid \r\n"); - printf("\t choose from:\r\n"); - printf("\tEM4100, EM-Marin (5 bytes key_data)\r\n"); - printf("\tH10301, HID26 (3 bytes key_data)\r\n"); - printf("\tI40134, Indala (3 bytes key_data)\r\n"); - printf("\tIoProxXSF, IoProx (4 bytes key_data)\r\n"); - printf("\t are hex-formatted\r\n"); -}; - -static bool lfrfid_cli_get_key_type(string_t data, LfrfidKeyType* type) { - bool result = false; - - if(string_cmp_str(data, "EM4100") == 0 || string_cmp_str(data, "EM-Marin") == 0) { - result = true; - *type = LfrfidKeyType::KeyEM4100; - } else if(string_cmp_str(data, "H10301") == 0 || string_cmp_str(data, "HID26") == 0) { - result = true; - *type = LfrfidKeyType::KeyH10301; - } else if(string_cmp_str(data, "I40134") == 0 || string_cmp_str(data, "Indala") == 0) { - result = true; - *type = LfrfidKeyType::KeyI40134; - } else if(string_cmp_str(data, "IoProxXSF") == 0 || string_cmp_str(data, "IoProx") == 0) { - result = true; - *type = LfrfidKeyType::KeyIoProxXSF; - } - - return result; -} - -static void lfrfid_cli_read(Cli* cli, string_t args) { - RfidReader reader; - string_t type_string; - string_init(type_string); - bool simple_mode = true; - LfrfidKeyType type; - RfidReader::Type reader_type = RfidReader::Type::Normal; - static const uint8_t data_size = LFRFID_KEY_SIZE; - uint8_t data[data_size] = {0}; - - if(args_read_string_and_trim(args, type_string)) { - simple_mode = false; - - if(string_cmp_str(type_string, "normal") == 0) { - reader_type = RfidReader::Type::Normal; - } else if(string_cmp_str(type_string, "indala") == 0) { - reader_type = RfidReader::Type::Indala; - } else { - lfrfid_cli_print_usage(); - string_clear(type_string); - return; - } - } - - if(simple_mode) { - reader.start(); - } else { - reader.start_forced(reader_type); - } - - printf("Reading RFID...\r\nPress Ctrl+C to abort\r\n"); - while(!cli_cmd_interrupt_received(cli)) { - if(reader.read(&type, data, data_size, simple_mode)) { - printf("%s", lfrfid_key_get_type_string(type)); - printf(" "); - - for(uint8_t i = 0; i < lfrfid_key_get_type_data_count(type); i++) { - printf("%02X", data[i]); - } - printf("\r\n"); - break; - } - furi_delay_ms(100); - } - - printf("Reading stopped\r\n"); - reader.stop(); - - string_clear(type_string); -} - -static void lfrfid_cli_write(Cli* cli, string_t args) { - UNUSED(cli); - UNUSED(args); - // TODO implement rfid write - printf("Not Implemented :(\r\n"); -} - -static void lfrfid_cli_t5577_clear_password_and_config_to_EM(Cli* cli, string_t args) { - UNUSED(cli); - UNUSED(args); - RfidWriter writer; - const uint32_t default_passwords[] = { - 0x51243648, 0x000D8787, 0x19920427, 0x50524F58, 0xF9DCEBA0, 0x65857569, 0x05D73B9F, - 0x89A69E60, 0x314159E0, 0xAA55BBBB, 0xA5B4C3D2, 0x1C0B5848, 0x00434343, 0x444E4752, - 0x4E457854, 0x44B44CAE, 0x88661858, 0xE9920427, 0x575F4F4B, 0x50520901, 0x20206666, - 0x65857569, 0x5469616E, 0x7686962A, 0xC0F5009A, 0x07CEE75D, 0xfeedbeef, 0xdeadc0de, - 0x00000000, 0x11111111, 0x22222222, 0x33333333, 0x44444444, 0x55555555, 0x66666666, - 0x77777777, 0x88888888, 0x99999999, 0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC, 0xDDDDDDDD, - 0xEEEEEEEE, 0xFFFFFFFF, 0xa0a1a2a3, 0xb0b1b2b3, 0x50415353, 0x00000001, 0x00000002, - 0x0000000a, 0x0000000b, 0x01020304, 0x02030405, 0x03040506, 0x04050607, 0x05060708, - 0x06070809, 0x0708090A, 0x08090A0B, 0x090A0B0C, 0x0A0B0C0D, 0x0B0C0D0E, 0x0C0D0E0F, - 0x01234567, 0x12345678, 0x10000000, 0x20000000, 0x30000000, 0x40000000, 0x50000000, - 0x60000000, 0x70000000, 0x80000000, 0x90000000, 0xA0000000, 0xB0000000, 0xC0000000, - 0xD0000000, 0xE0000000, 0xF0000000, 0x10101010, 0x01010101, 0x11223344, 0x22334455, - 0x33445566, 0x44556677, 0x55667788, 0x66778899, 0x778899AA, 0x8899AABB, 0x99AABBCC, - 0xAABBCCDD, 0xBBCCDDEE, 0xCCDDEEFF, 0x0CB7E7FC, 0xFABADA11, 0x87654321, 0x12341234, - 0x69696969, 0x12121212, 0x12344321, 0x1234ABCD, 0x11112222, 0x13131313, 0x10041004, - 0x31415926, 0xabcd1234, 0x20002000, 0x19721972, 0xaa55aa55, 0x55aa55aa, 0x4f271149, - 0x07d7bb0b, 0x9636ef8f, 0xb5f44686, 0x9E3779B9, 0xC6EF3720, 0x7854794A, 0xF1EA5EED, - 0x69314718, 0x57721566, 0x93C467E3, 0x27182818, 0x50415353}; - const uint8_t default_passwords_len = sizeof(default_passwords) / sizeof(uint32_t); - const uint32_t em_config_block_data = - 0b00000000000101001000000001000000; //no pwd&aor config block - - printf( - "Clearing T5577 password (use default passwords) and configuring the tag to default (em-marine)...\r\n"); - - for(uint8_t i = 0; i < default_passwords_len; i++) { - printf( - "Trying key %u of %u: 0x%08lX\r\n", i + 1, default_passwords_len, default_passwords[i]); - FURI_CRITICAL_ENTER(); - writer.start(); - writer.write_block(0, 0, false, em_config_block_data, true, default_passwords[i]); - writer.write_reset(); - writer.stop(); - FURI_CRITICAL_EXIT(); - furi_delay_ms(100); - } - - printf("Done\r\n"); -} - -static void lfrfid_cli_emulate(Cli* cli, string_t args) { - string_t data; - string_init(data); - RfidTimerEmulator emulator; - - static const uint8_t data_size = LFRFID_KEY_SIZE; - uint8_t key_data[data_size] = {0}; - uint8_t key_data_size = 0; - LfrfidKeyType type; - - if(!args_read_string_and_trim(args, data)) { - lfrfid_cli_print_usage(); - string_clear(data); - return; - } - - if(!lfrfid_cli_get_key_type(data, &type)) { - lfrfid_cli_print_usage(); - string_clear(data); - return; - } - - key_data_size = lfrfid_key_get_type_data_count(type); - - if(!args_read_hex_bytes(args, key_data, key_data_size)) { - lfrfid_cli_print_usage(); - string_clear(data); - return; - } - - emulator.start(type, key_data, key_data_size); - - printf("Emulating RFID...\r\nPress Ctrl+C to abort\r\n"); - while(!cli_cmd_interrupt_received(cli)) { - furi_delay_ms(100); - } - printf("Emulation stopped\r\n"); - emulator.stop(); - - string_clear(data); -} - -static void lfrfid_cli(Cli* cli, string_t args, void* context) { - UNUSED(context); - string_t cmd; - string_init(cmd); - - if(!args_read_string_and_trim(args, cmd)) { - string_clear(cmd); - lfrfid_cli_print_usage(); - return; - } - - if(string_cmp_str(cmd, "read") == 0) { - lfrfid_cli_read(cli, args); - } else if(string_cmp_str(cmd, "write") == 0) { - lfrfid_cli_write(cli, args); - } else if(string_cmp_str(cmd, "emulate") == 0) { - lfrfid_cli_emulate(cli, args); - } else if(string_cmp_str(cmd, "clear_pass_t5577") == 0) { - lfrfid_cli_t5577_clear_password_and_config_to_EM(cli, args); - } else { - lfrfid_cli_print_usage(); - } - - string_clear(cmd); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.cpp b/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.cpp deleted file mode 100644 index 236ca8c29fa..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include "lfrfid_app_scene_delete_confirm.h" -#include "../view/elements/button_element.h" -#include "../view/elements/icon_element.h" -#include "../view/elements/string_element.h" - -void LfRfidAppSceneDeleteConfirm::on_enter(LfRfidApp* app, bool /* need_restore */) { - string_init(string_data); - string_init(string_decrypted); - string_init(string_header); - - auto container = app->view_controller.get(); - - auto button = container->add(); - button->set_type(ButtonElement::Type::Left, "Back"); - button->set_callback(app, LfRfidAppSceneDeleteConfirm::back_callback); - - button = container->add(); - button->set_type(ButtonElement::Type::Right, "Delete"); - button->set_callback(app, LfRfidAppSceneDeleteConfirm::delete_callback); - - auto line_1 = container->add(); - auto line_2 = container->add(); - auto line_3 = container->add(); - auto line_4 = container->add(); - - RfidKey& key = app->worker.key; - const uint8_t* data = key.get_data(); - - for(uint8_t i = 0; i < key.get_type_data_count(); i++) { - if(i != 0) { - string_cat_printf(string_data, " "); - } - string_cat_printf(string_data, "%02X", data[i]); - } - - string_printf(string_header, "Delete %s?", key.get_name()); - line_1->set_text( - string_get_cstr(string_header), 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary); - line_2->set_text( - string_get_cstr(string_data), 64, 29, 0, AlignCenter, AlignBottom, FontSecondary); - - switch(key.get_type()) { - case LfrfidKeyType::KeyEM4100: - string_printf( - string_decrypted, "%03u,%05u", data[2], (uint16_t)((data[3] << 8) | (data[4]))); - - break; - case LfrfidKeyType::KeyH10301: - case LfrfidKeyType::KeyI40134: - string_printf( - string_decrypted, "FC: %u ID: %u", data[0], (uint16_t)((data[1] << 8) | (data[2]))); - break; - case LfrfidKeyType::KeyIoProxXSF: - string_printf( - string_decrypted, - "FC: %u VC: %u ID: %u", - data[0], - data[1], - (uint16_t)((data[2] << 8) | (data[3]))); - break; - } - line_3->set_text( - string_get_cstr(string_decrypted), 64, 39, 0, AlignCenter, AlignBottom, FontSecondary); - - line_4->set_text( - lfrfid_key_get_type_string(key.get_type()), - 64, - 49, - 0, - AlignCenter, - AlignBottom, - FontSecondary); - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneDeleteConfirm::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Next) { - app->delete_key(&app->worker.key); - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::DeleteSuccess); - consumed = true; - } else if(event->type == LfRfidApp::EventType::Stay) { - app->scene_controller.switch_to_previous_scene(); - consumed = true; - } else if(event->type == LfRfidApp::EventType::Back) { - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneDeleteConfirm::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); - string_clear(string_data); - string_clear(string_decrypted); - string_clear(string_header); -} - -void LfRfidAppSceneDeleteConfirm::back_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Stay; - app->view_controller.send_event(&event); -} - -void LfRfidAppSceneDeleteConfirm::delete_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Next; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.h b/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.h deleted file mode 100644 index e30764f0232..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneDeleteConfirm : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void back_callback(void* context); - static void delete_callback(void* context); - - string_t string_header; - string_t string_data; - string_t string_decrypted; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_delete_success.cpp b/applications/lfrfid/scene/lfrfid_app_scene_delete_success.cpp deleted file mode 100644 index 9820338d126..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_delete_success.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "lfrfid_app_scene_delete_success.h" - -void LfRfidAppSceneDeleteSuccess::on_enter(LfRfidApp* app, bool /* need_restore */) { - auto popup = app->view_controller.get(); - - popup->set_icon(0, 2, &I_DolphinMafia_115x62); - popup->set_header("Deleted", 83, 19, AlignLeft, AlignBottom); - popup->set_context(app); - popup->set_callback(LfRfidAppSceneDeleteSuccess::timeout_callback); - popup->set_timeout(1500); - popup->enable_timeout(); - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneDeleteSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Back) { - app->scene_controller.search_and_switch_to_previous_scene( - {LfRfidApp::SceneType::SelectKey}); - - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneDeleteSuccess::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneDeleteSuccess::timeout_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Back; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_delete_success.h b/applications/lfrfid/scene/lfrfid_app_scene_delete_success.h deleted file mode 100644 index 009b9f25dba..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_delete_success.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneDeleteSuccess : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void timeout_callback(void* context); -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp b/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp deleted file mode 100644 index cad4f17c7e2..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "lfrfid_app_scene_emulate.h" -#include -#include - -void LfRfidAppSceneEmulate::on_enter(LfRfidApp* app, bool /* need_restore */) { - string_init(data_string); - - DOLPHIN_DEED(DolphinDeedRfidEmulate); - const uint8_t* data = app->worker.key.get_data(); - - for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) { - string_cat_printf(data_string, "%02X", data[i]); - } - - auto popup = app->view_controller.get(); - - popup->set_header("Emulating", 89, 30, AlignCenter, AlignTop); - if(strlen(app->worker.key.get_name())) { - popup->set_text(app->worker.key.get_name(), 89, 43, AlignCenter, AlignTop); - } else { - popup->set_text(string_get_cstr(data_string), 89, 43, AlignCenter, AlignTop); - } - popup->set_icon(0, 3, &I_RFIDDolphinSend_97x61); - - app->view_controller.switch_to(); - app->worker.start_emulate(); - - notification_message(app->notification, &sequence_blink_start_magenta); -} - -bool LfRfidAppSceneEmulate::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - UNUSED(app); - UNUSED(event); - bool consumed = false; - return consumed; -} - -void LfRfidAppSceneEmulate::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); - app->worker.stop_emulate(); - string_clear(data_string); - notification_message(app->notification, &sequence_blink_stop); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_emulate.h b/applications/lfrfid/scene/lfrfid_app_scene_emulate.h deleted file mode 100644 index 937e49af9b1..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_emulate.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneEmulate : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - string_t data_string; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.cpp b/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.cpp deleted file mode 100644 index be070b40e00..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "lfrfid_app_scene_exit_confirm.h" -#include "../view/elements/button_element.h" -#include "../view/elements/icon_element.h" -#include "../view/elements/string_element.h" - -void LfRfidAppSceneExitConfirm::on_enter(LfRfidApp* app, bool /* need_restore */) { - auto container = app->view_controller.get(); - - auto button = container->add(); - button->set_type(ButtonElement::Type::Left, "Exit"); - button->set_callback(app, LfRfidAppSceneExitConfirm::exit_callback); - - button = container->add(); - button->set_type(ButtonElement::Type::Right, "Stay"); - button->set_callback(app, LfRfidAppSceneExitConfirm::stay_callback); - - auto line_1 = container->add(); - auto line_2 = container->add(); - - line_1->set_text("Exit to RFID Menu?", 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary); - line_2->set_text( - "All unsaved data will be lost!", 64, 31, 0, AlignCenter, AlignBottom, FontSecondary); - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneExitConfirm::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Next) { - app->scene_controller.search_and_switch_to_previous_scene({LfRfidApp::SceneType::Start}); - consumed = true; - } else if(event->type == LfRfidApp::EventType::Stay) { - app->scene_controller.switch_to_previous_scene(); - consumed = true; - } else if(event->type == LfRfidApp::EventType::Back) { - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneExitConfirm::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneExitConfirm::exit_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Next; - app->view_controller.send_event(&event); -} - -void LfRfidAppSceneExitConfirm::stay_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Stay; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.h b/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.h deleted file mode 100644 index a0a1023258b..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneExitConfirm : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void exit_callback(void* context); - static void stay_callback(void* context); -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read.cpp b/applications/lfrfid/scene/lfrfid_app_scene_read.cpp deleted file mode 100644 index 67279a163e1..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_read.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "lfrfid_app_scene_read.h" -#include - -void LfRfidAppSceneRead::on_enter(LfRfidApp* app, bool /* need_restore */) { - auto popup = app->view_controller.get(); - - DOLPHIN_DEED(DolphinDeedRfidRead); - popup->set_header("Reading\nLF RFID", 89, 34, AlignCenter, AlignTop); - popup->set_icon(0, 3, &I_RFIDDolphinReceive_97x61); - - app->view_controller.switch_to(); - app->worker.start_read(); -} - -bool LfRfidAppSceneRead::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Tick) { - if(app->worker.read()) { - DOLPHIN_DEED(DolphinDeedRfidReadSuccess); - notification_message(app->notification, &sequence_success); - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ReadSuccess); - } else { - if(app->worker.any_read()) { - notification_message(app->notification, &sequence_blink_yellow_10); - } else if(app->worker.detect()) { - notification_message(app->notification, &sequence_blink_yellow_10); - } else { - notification_message(app->notification, &sequence_blink_cyan_10); - } - } - } - - return consumed; -} - -void LfRfidAppSceneRead::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); - app->worker.stop_read(); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read.h b/applications/lfrfid/scene/lfrfid_app_scene_read.h deleted file mode 100644 index b5035b72a02..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_read.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneRead : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read_menu.cpp b/applications/lfrfid/scene/lfrfid_app_scene_read_menu.cpp deleted file mode 100644 index 76c91230654..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_read_menu.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "lfrfid_app_scene_read_menu.h" - -typedef enum { - SubmenuSave, - SubmenuEmulate, - SubmenuWrite, -} SubmenuIndex; - -void LfRfidAppSceneReadKeyMenu::on_enter(LfRfidApp* app, bool need_restore) { - auto submenu = app->view_controller.get(); - - submenu->add_item("Save", SubmenuSave, submenu_callback, app); - submenu->add_item("Emulate", SubmenuEmulate, submenu_callback, app); - submenu->add_item("Write", SubmenuWrite, submenu_callback, app); - - if(need_restore) { - submenu->set_selected_item(submenu_item_selected); - } - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneReadKeyMenu::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::MenuSelected) { - submenu_item_selected = event->payload.menu_index; - switch(event->payload.menu_index) { - case SubmenuWrite: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Write); - break; - case SubmenuSave: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveName); - break; - case SubmenuEmulate: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Emulate); - break; - } - consumed = true; - } else if(event->type == LfRfidApp::EventType::Back) { - app->scene_controller.switch_to_previous_scene(); - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneReadKeyMenu::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneReadKeyMenu::submenu_callback(void* context, uint32_t index) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - - event.type = LfRfidApp::EventType::MenuSelected; - event.payload.menu_index = index; - - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read_menu.h b/applications/lfrfid/scene/lfrfid_app_scene_read_menu.h deleted file mode 100644 index 2b50b96f9a9..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_read_menu.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneReadKeyMenu : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void submenu_callback(void* context, uint32_t index); - uint32_t submenu_item_selected = 0; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read_success.cpp b/applications/lfrfid/scene/lfrfid_app_scene_read_success.cpp deleted file mode 100644 index 010cac2cfa5..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_read_success.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include "lfrfid_app_scene_read_success.h" -#include "../view/elements/button_element.h" -#include "../view/elements/icon_element.h" -#include "../view/elements/string_element.h" - -void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool /* need_restore */) { - string_init(string[0]); - string_init(string[1]); - string_init(string[2]); - string_init(string[3]); - - auto container = app->view_controller.get(); - - auto button = container->add(); - button->set_type(ButtonElement::Type::Left, "Retry"); - button->set_callback(app, LfRfidAppSceneReadSuccess::back_callback); - - button = container->add(); - button->set_type(ButtonElement::Type::Right, "More"); - button->set_callback(app, LfRfidAppSceneReadSuccess::more_callback); - - auto icon = container->add(); - icon->set_icon(3, 12, &I_RFIDBigChip_37x36); - - auto header = container->add(); - header->set_text(app->worker.key.get_type_text(), 89, 3, 0, AlignCenter); - - auto line_1_text = container->add(); - auto line_2l_text = container->add(); - auto line_2r_text = container->add(); - auto line_3_text = container->add(); - - auto line_1_value = container->add(); - auto line_2l_value = container->add(); - auto line_2r_value = container->add(); - auto line_3_value = container->add(); - - const uint8_t* data = app->worker.key.get_data(); - - switch(app->worker.key.get_type()) { - case LfrfidKeyType::KeyEM4100: - line_1_text->set_text("HEX:", 65, 23, 0, AlignRight, AlignBottom, FontSecondary); - line_2l_text->set_text("Mod:", 65, 35, 0, AlignRight, AlignBottom, FontSecondary); - line_3_text->set_text("ID:", 65, 47, 0, AlignRight, AlignBottom, FontSecondary); - - for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) { - string_cat_printf(string[0], "%02X", data[i]); - } - - string_printf(string[1], "Manchester"); - string_printf(string[2], "%03u,%05u", data[2], (uint16_t)((data[3] << 8) | (data[4]))); - - line_1_value->set_text( - string_get_cstr(string[0]), 68, 23, 0, AlignLeft, AlignBottom, FontSecondary); - line_2l_value->set_text( - string_get_cstr(string[1]), 68, 35, 0, AlignLeft, AlignBottom, FontSecondary); - line_3_value->set_text( - string_get_cstr(string[2]), 68, 47, 0, AlignLeft, AlignBottom, FontSecondary); - break; - case LfrfidKeyType::KeyH10301: - case LfrfidKeyType::KeyI40134: - line_1_text->set_text("HEX:", 65, 23, 0, AlignRight, AlignBottom, FontSecondary); - line_2l_text->set_text("FC:", 65, 35, 0, AlignRight, AlignBottom, FontSecondary); - line_3_text->set_text("Card:", 65, 47, 0, AlignRight, AlignBottom, FontSecondary); - - for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) { - string_cat_printf(string[0], "%02X", data[i]); - } - - string_printf(string[1], "%u", data[0]); - string_printf(string[2], "%u", (uint16_t)((data[1] << 8) | (data[2]))); - - line_1_value->set_text( - string_get_cstr(string[0]), 68, 23, 0, AlignLeft, AlignBottom, FontSecondary); - line_2l_value->set_text( - string_get_cstr(string[1]), 68, 35, 0, AlignLeft, AlignBottom, FontSecondary); - line_3_value->set_text( - string_get_cstr(string[2]), 68, 47, 0, AlignLeft, AlignBottom, FontSecondary); - break; - - case LfrfidKeyType::KeyIoProxXSF: - line_1_text->set_text("HEX:", 65, 23, 0, AlignRight, AlignBottom, FontSecondary); - line_2l_text->set_text("FC:", 65, 35, 0, AlignRight, AlignBottom, FontSecondary); - line_2r_text->set_text("VС:", 95, 35, 0, AlignRight, AlignBottom, FontSecondary); - line_3_text->set_text("Card:", 65, 47, 0, AlignRight, AlignBottom, FontSecondary); - - for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) { - string_cat_printf(string[0], "%02X", data[i]); - } - - string_printf(string[1], "%u", data[0]); - string_printf(string[2], "%u", (uint16_t)((data[2] << 8) | (data[3]))); - string_printf(string[3], "%u", data[1]); - - line_1_value->set_text( - string_get_cstr(string[0]), 68, 23, 0, AlignLeft, AlignBottom, FontSecondary); - line_2l_value->set_text( - string_get_cstr(string[1]), 68, 35, 0, AlignLeft, AlignBottom, FontSecondary); - line_2r_value->set_text( - string_get_cstr(string[3]), 98, 35, 0, AlignLeft, AlignBottom, FontSecondary); - line_3_value->set_text( - string_get_cstr(string[2]), 68, 47, 0, AlignLeft, AlignBottom, FontSecondary); - - break; - } - - app->view_controller.switch_to(); - - notification_message_block(app->notification, &sequence_set_green_255); -} - -bool LfRfidAppSceneReadSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Next) { - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ReadKeyMenu); - consumed = true; - } else if(event->type == LfRfidApp::EventType::Retry) { - app->scene_controller.switch_to_next_scene({LfRfidApp::SceneType::RetryConfirm}); - consumed = true; - } else if(event->type == LfRfidApp::EventType::Back) { - app->scene_controller.switch_to_next_scene({LfRfidApp::SceneType::ExitConfirm}); - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneReadSuccess::on_exit(LfRfidApp* app) { - notification_message_block(app->notification, &sequence_reset_green); - app->view_controller.get()->clean(); - string_clear(string[0]); - string_clear(string[1]); - string_clear(string[2]); -} - -void LfRfidAppSceneReadSuccess::back_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Retry; - app->view_controller.send_event(&event); -} - -void LfRfidAppSceneReadSuccess::more_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Next; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read_success.h b/applications/lfrfid/scene/lfrfid_app_scene_read_success.h deleted file mode 100644 index ac0e3c1b5c1..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_read_success.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneReadSuccess : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void back_callback(void* context); - static void more_callback(void* context); - - string_t string[3]; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.cpp b/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.cpp deleted file mode 100644 index c181223238f..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "lfrfid_app_scene_retry_confirm.h" -#include "../view/elements/button_element.h" -#include "../view/elements/icon_element.h" -#include "../view/elements/string_element.h" - -void LfRfidAppSceneRetryConfirm::on_enter(LfRfidApp* app, bool /* need_restore */) { - auto container = app->view_controller.get(); - - auto button = container->add(); - button->set_type(ButtonElement::Type::Left, "Exit"); - button->set_callback(app, LfRfidAppSceneRetryConfirm::exit_callback); - - button = container->add(); - button->set_type(ButtonElement::Type::Right, "Stay"); - button->set_callback(app, LfRfidAppSceneRetryConfirm::stay_callback); - - auto line_1 = container->add(); - auto line_2 = container->add(); - - line_1->set_text("Return to Reading?", 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary); - line_2->set_text( - "All unsaved data will be lost!", 64, 29, 0, AlignCenter, AlignBottom, FontSecondary); - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneRetryConfirm::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Next) { - app->scene_controller.search_and_switch_to_previous_scene({LfRfidApp::SceneType::Read}); - consumed = true; - } else if(event->type == LfRfidApp::EventType::Stay) { - app->scene_controller.switch_to_previous_scene(); - consumed = true; - } else if(event->type == LfRfidApp::EventType::Back) { - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneRetryConfirm::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneRetryConfirm::exit_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Next; - app->view_controller.send_event(&event); -} - -void LfRfidAppSceneRetryConfirm::stay_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Stay; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.h b/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.h deleted file mode 100644 index 01b7329c90a..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneRetryConfirm : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void exit_callback(void* context); - static void stay_callback(void* context); -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp b/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp deleted file mode 100644 index 54a57c9a2ec..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "lfrfid_app_scene_rpc.h" -#include -#include -#include - -void LfRfidAppSceneRpc::on_enter(LfRfidApp* app, bool /* need_restore */) { - auto popup = app->view_controller.get(); - - popup->set_header("LF RFID", 89, 42, AlignCenter, AlignBottom); - popup->set_text("RPC mode", 89, 44, AlignCenter, AlignTop); - popup->set_icon(0, 12, &I_RFIDDolphinSend_97x61); - - app->view_controller.switch_to(); - - notification_message(app->notification, &sequence_display_backlight_on); -} - -bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - UNUSED(app); - UNUSED(event); - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Exit) { - consumed = true; - LfRfidApp::Event view_event; - view_event.type = LfRfidApp::EventType::Back; - app->view_controller.send_event(&view_event); - rpc_system_app_confirm(app->rpc_ctx, RpcAppEventAppExit, true); - } else if(event->type == LfRfidApp::EventType::RpcSessionClose) { - consumed = true; - LfRfidApp::Event view_event; - view_event.type = LfRfidApp::EventType::Back; - app->view_controller.send_event(&view_event); - } else if(event->type == LfRfidApp::EventType::RpcLoadFile) { - const char* arg = rpc_system_app_get_data(app->rpc_ctx); - consumed = true; - bool result = false; - if(arg && !emulating) { - string_set_str(app->file_path, arg); - if(app->load_key_data(app->file_path, &(app->worker.key), false)) { - app->worker.start_emulate(); - emulating = true; - - auto popup = app->view_controller.get(); - app->text_store.set("emulating\n%s", app->worker.key.get_name()); - popup->set_text(app->text_store.text, 89, 44, AlignCenter, AlignTop); - - notification_message(app->notification, &sequence_blink_start_magenta); - result = true; - } - } - rpc_system_app_confirm(app->rpc_ctx, RpcAppEventLoadFile, result); - } - - return consumed; -} - -void LfRfidAppSceneRpc::on_exit(LfRfidApp* app) { - if(emulating) { - app->worker.stop_emulate(); - notification_message(app->notification, &sequence_blink_stop); - } - app->view_controller.get()->clean(); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_rpc.h b/applications/lfrfid/scene/lfrfid_app_scene_rpc.h deleted file mode 100644 index f630dfd35e1..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_rpc.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneRpc : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - bool emulating = false; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_data.cpp b/applications/lfrfid/scene/lfrfid_app_scene_save_data.cpp deleted file mode 100644 index 3a13e6838eb..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_data.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "lfrfid_app_scene_save_data.h" -#include - -void LfRfidAppSceneSaveData::on_enter(LfRfidApp* app, bool need_restore) { - auto byte_input = app->view_controller.get(); - RfidKey& key = app->worker.key; - - if(need_restore) printf("restored\r\n"); - - if(need_restore) { - key.set_data(old_key_data, key.get_type_data_count()); - } else { - memcpy(old_key_data, key.get_data(), key.get_type_data_count()); - } - - memcpy(new_key_data, key.get_data(), key.get_type_data_count()); - byte_input->set_header_text("Enter the data in hex"); - - byte_input->set_result_callback( - save_callback, NULL, app, new_key_data, app->worker.key.get_type_data_count()); - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneSaveData::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - RfidKey& key = app->worker.key; - - if(event->type == LfRfidApp::EventType::Next) { - key.set_data(new_key_data, key.get_type_data_count()); - DOLPHIN_DEED(DolphinDeedRfidAdd); - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveName); - } - - return consumed; -} - -void LfRfidAppSceneSaveData::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneSaveData::save_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Next; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_data.h b/applications/lfrfid/scene/lfrfid_app_scene_save_data.h deleted file mode 100644 index 6458ae649e4..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_data.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneSaveData : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void save_callback(void* context); - uint8_t old_key_data[LFRFID_KEY_SIZE] = { - 0xAA, - 0xAA, - 0xAA, - 0xAA, - 0xAA, - 0xAA, - 0xAA, - 0xAA, - }; - - uint8_t new_key_data[LFRFID_KEY_SIZE] = { - 0xBB, - 0xBB, - 0xBB, - 0xBB, - 0xBB, - 0xBB, - 0xBB, - 0xBB, - }; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp b/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp deleted file mode 100644 index d7ba2c9edba..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "lfrfid_app_scene_save_name.h" -#include "m-string.h" -#include -#include - -void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool /* need_restore */) { - const char* key_name = app->worker.key.get_name(); - - bool key_name_empty = !strcmp(key_name, ""); - if(key_name_empty) { - string_set_str(app->file_path, app->app_folder); - set_random_name(app->text_store.text, app->text_store.text_size); - } else { - app->text_store.set("%s", key_name); - } - - auto text_input = app->view_controller.get(); - text_input->set_header_text("Name the card"); - - text_input->set_result_callback( - save_callback, - app, - app->text_store.text, - app->worker.key.get_name_length(), - key_name_empty); - - string_t folder_path; - string_init(folder_path); - - path_extract_dirname(string_get_cstr(app->file_path), folder_path); - - ValidatorIsFile* validator_is_file = - validator_is_file_alloc_init(string_get_cstr(folder_path), app->app_extension, key_name); - text_input->set_validator(validator_is_file_callback, validator_is_file); - - string_clear(folder_path); - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneSaveName::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Next) { - if(strlen(app->worker.key.get_name())) { - app->delete_key(&app->worker.key); - } - - app->worker.key.set_name(app->text_store.text); - - if(app->save_key(&app->worker.key)) { - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveSuccess); - } else { - app->scene_controller.search_and_switch_to_previous_scene( - {LfRfidApp::SceneType::ReadKeyMenu}); - } - } - - return consumed; -} - -void LfRfidAppSceneSaveName::on_exit(LfRfidApp* app) { - void* validator_context = - app->view_controller.get()->get_validator_callback_context(); - app->view_controller.get()->set_validator(NULL, NULL); - validator_is_file_free((ValidatorIsFile*)validator_context); - - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneSaveName::save_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Next; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_name.h b/applications/lfrfid/scene/lfrfid_app_scene_save_name.h deleted file mode 100644 index ced42cc0e45..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_name.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneSaveName : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void save_callback(void* context); -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_success.cpp b/applications/lfrfid/scene/lfrfid_app_scene_save_success.cpp deleted file mode 100644 index 64efafa7314..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_success.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "lfrfid_app_scene_save_success.h" -#include -#include -#include - -void LfRfidAppSceneSaveSuccess::on_enter(LfRfidApp* app, bool /* need_restore */) { - auto popup = app->view_controller.get(); - - DOLPHIN_DEED(DolphinDeedRfidSave); - popup->set_icon(32, 5, &I_DolphinNice_96x59); - popup->set_header("Saved!", 5, 7, AlignLeft, AlignTop); - popup->set_context(app); - popup->set_callback(LfRfidAppSceneSaveSuccess::timeout_callback); - popup->set_timeout(1500); - popup->enable_timeout(); - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneSaveSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Back) { - bool result = app->scene_controller.has_previous_scene( - {LfRfidApp::SceneType::ReadKeyMenu, LfRfidApp::SceneType::SelectKey}); - - if(result) { - app->scene_controller.search_and_switch_to_previous_scene( - {LfRfidApp::SceneType::ReadKeyMenu, LfRfidApp::SceneType::SelectKey}); - } else { - app->scene_controller.search_and_switch_to_another_scene( - {LfRfidApp::SceneType::SaveType}, LfRfidApp::SceneType::SelectKey); - } - - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneSaveSuccess::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneSaveSuccess::timeout_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Back; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_success.h b/applications/lfrfid/scene/lfrfid_app_scene_save_success.h deleted file mode 100644 index 62273a76be3..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_success.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneSaveSuccess : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void timeout_callback(void* context); -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_type.cpp b/applications/lfrfid/scene/lfrfid_app_scene_save_type.cpp deleted file mode 100644 index 334bb1a03d9..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_type.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "lfrfid_app_scene_save_type.h" - -void LfRfidAppSceneSaveType::on_enter(LfRfidApp* app, bool need_restore) { - auto submenu = app->view_controller.get(); - - for(uint8_t i = 0; i <= keys_count; i++) { - string_init_printf( - submenu_name[i], - "%s %s", - lfrfid_key_get_manufacturer_string(static_cast(i)), - lfrfid_key_get_type_string(static_cast(i))); - submenu->add_item(string_get_cstr(submenu_name[i]), i, submenu_callback, app); - } - - if(need_restore) { - submenu->set_selected_item(submenu_item_selected); - } - - app->view_controller.switch_to(); - - // clear key name - app->worker.key.set_name(""); -} - -bool LfRfidAppSceneSaveType::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::MenuSelected) { - submenu_item_selected = event->payload.menu_index; - app->worker.key.set_type(static_cast(event->payload.menu_index)); - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveData); - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneSaveType::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); - for(uint8_t i = 0; i <= keys_count; i++) { - string_clear(submenu_name[i]); - } -} - -void LfRfidAppSceneSaveType::submenu_callback(void* context, uint32_t index) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - - event.type = LfRfidApp::EventType::MenuSelected; - event.payload.menu_index = index; - - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_type.h b/applications/lfrfid/scene/lfrfid_app_scene_save_type.h deleted file mode 100644 index 847c0dabbf3..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_type.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneSaveType : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void submenu_callback(void* context, uint32_t index); - uint32_t submenu_item_selected = 0; - static const uint8_t keys_count = static_cast(LfrfidKeyType::KeyIoProxXSF); - string_t submenu_name[keys_count + 1]; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_saved_info.cpp b/applications/lfrfid/scene/lfrfid_app_scene_saved_info.cpp deleted file mode 100644 index dd4a3d4ebff..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_saved_info.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "lfrfid_app_scene_saved_info.h" -#include "../view/elements/button_element.h" -#include "../view/elements/icon_element.h" -#include "../view/elements/string_element.h" - -void LfRfidAppSceneSavedInfo::on_enter(LfRfidApp* app, bool /* need_restore */) { - string_init(string_data); - string_init(string_decrypted); - - auto container = app->view_controller.get(); - - auto button = container->add(); - button->set_type(ButtonElement::Type::Left, "Back"); - button->set_callback(app, LfRfidAppSceneSavedInfo::back_callback); - - auto line_1 = container->add(); - auto line_2 = container->add(); - auto line_3 = container->add(); - auto line_4 = container->add(); - - RfidKey& key = app->worker.key; - const uint8_t* data = key.get_data(); - - for(uint8_t i = 0; i < key.get_type_data_count(); i++) { - if(i != 0) { - string_cat_printf(string_data, " "); - } - string_cat_printf(string_data, "%02X", data[i]); - } - - line_1->set_text(key.get_name(), 64, 17, 128 - 2, AlignCenter, AlignBottom, FontSecondary); - line_2->set_text( - string_get_cstr(string_data), 64, 29, 0, AlignCenter, AlignBottom, FontPrimary); - - switch(key.get_type()) { - case LfrfidKeyType::KeyEM4100: - string_printf( - string_decrypted, "%03u,%05u", data[2], (uint16_t)((data[3] << 8) | (data[4]))); - - break; - case LfrfidKeyType::KeyH10301: - case LfrfidKeyType::KeyI40134: - string_printf( - string_decrypted, "FC: %u ID: %u", data[0], (uint16_t)((data[1] << 8) | (data[2]))); - break; - case LfrfidKeyType::KeyIoProxXSF: - string_printf( - string_decrypted, - "FC: %u VC: %u ID: %u", - data[0], - data[1], - (uint16_t)((data[2] << 8) | (data[3]))); - break; - } - line_3->set_text( - string_get_cstr(string_decrypted), 64, 39, 0, AlignCenter, AlignBottom, FontSecondary); - - line_4->set_text( - lfrfid_key_get_type_string(key.get_type()), - 64, - 49, - 0, - AlignCenter, - AlignBottom, - FontSecondary); - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneSavedInfo::on_event(LfRfidApp* /* app */, LfRfidApp::Event* /* event */) { - return false; -} - -void LfRfidAppSceneSavedInfo::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); - string_clear(string_data); - string_clear(string_decrypted); -} - -void LfRfidAppSceneSavedInfo::back_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Back; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_saved_info.h b/applications/lfrfid/scene/lfrfid_app_scene_saved_info.h deleted file mode 100644 index 5aa33e8ad7b..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_saved_info.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneSavedInfo : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void back_callback(void* context); - - string_t string_data; - string_t string_decrypted; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.cpp b/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.cpp deleted file mode 100644 index e6677fe8daf..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "lfrfid_app_scene_saved_key_menu.h" - -typedef enum { - SubmenuEmulate, - SubmenuWrite, - SubmenuEdit, - SubmenuDelete, - SubmenuInfo, -} SubmenuIndex; - -void LfRfidAppSceneSavedKeyMenu::on_enter(LfRfidApp* app, bool need_restore) { - auto submenu = app->view_controller.get(); - - submenu->add_item("Emulate", SubmenuEmulate, submenu_callback, app); - submenu->add_item("Write", SubmenuWrite, submenu_callback, app); - submenu->add_item("Edit", SubmenuEdit, submenu_callback, app); - submenu->add_item("Delete", SubmenuDelete, submenu_callback, app); - submenu->add_item("Info", SubmenuInfo, submenu_callback, app); - - if(need_restore) { - submenu->set_selected_item(submenu_item_selected); - } - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneSavedKeyMenu::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::MenuSelected) { - submenu_item_selected = event->payload.menu_index; - switch(event->payload.menu_index) { - case SubmenuEmulate: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Emulate); - break; - case SubmenuWrite: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Write); - break; - case SubmenuEdit: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveData); - break; - case SubmenuDelete: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::DeleteConfirm); - break; - case SubmenuInfo: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SavedInfo); - break; - } - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneSavedKeyMenu::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneSavedKeyMenu::submenu_callback(void* context, uint32_t index) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - - event.type = LfRfidApp::EventType::MenuSelected; - event.payload.menu_index = index; - - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.h b/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.h deleted file mode 100644 index 69a6e5e58b4..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneSavedKeyMenu : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void submenu_callback(void* context, uint32_t index); - uint32_t submenu_item_selected = 0; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_select_key.cpp b/applications/lfrfid/scene/lfrfid_app_scene_select_key.cpp deleted file mode 100644 index 6d5df73cb8c..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_select_key.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "lfrfid_app_scene_select_key.h" - -void LfRfidAppSceneSelectKey::on_enter(LfRfidApp* app, bool need_restore) { - if(app->load_key_from_file_select(need_restore)) { - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SavedKeyMenu); - } else { - app->scene_controller.switch_to_previous_scene(); - } -} - -bool LfRfidAppSceneSelectKey::on_event(LfRfidApp* /* app */, LfRfidApp::Event* /* event */) { - return false; -} - -void LfRfidAppSceneSelectKey::on_exit(LfRfidApp* /* app */) { -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_select_key.h b/applications/lfrfid/scene/lfrfid_app_scene_select_key.h deleted file mode 100644 index be565a91c56..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_select_key.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneSelectKey : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_start.cpp b/applications/lfrfid/scene/lfrfid_app_scene_start.cpp deleted file mode 100644 index f5afad5c988..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_start.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "lfrfid_app_scene_start.h" - -typedef enum { - SubmenuRead, - SubmenuSaved, - SubmenuAddManually, -} SubmenuIndex; - -void LfRfidAppSceneStart::on_enter(LfRfidApp* app, bool need_restore) { - auto submenu = app->view_controller.get(); - - submenu->add_item("Read", SubmenuRead, submenu_callback, app); - submenu->add_item("Saved", SubmenuSaved, submenu_callback, app); - submenu->add_item("Add Manually", SubmenuAddManually, submenu_callback, app); - - if(need_restore) { - submenu->set_selected_item(submenu_item_selected); - } - - app->view_controller.switch_to(); - - // clear key - app->worker.key.clear(); -} - -bool LfRfidAppSceneStart::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::MenuSelected) { - submenu_item_selected = event->payload.menu_index; - switch(event->payload.menu_index) { - case SubmenuRead: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Read); - break; - case SubmenuSaved: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SelectKey); - break; - case SubmenuAddManually: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveType); - break; - } - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneStart::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneStart::submenu_callback(void* context, uint32_t index) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - - event.type = LfRfidApp::EventType::MenuSelected; - event.payload.menu_index = index; - - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_start.h b/applications/lfrfid/scene/lfrfid_app_scene_start.h deleted file mode 100644 index 255590d6acc..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_start.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneStart : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void submenu_callback(void* context, uint32_t index); - uint32_t submenu_item_selected = 0; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_write.cpp b/applications/lfrfid/scene/lfrfid_app_scene_write.cpp deleted file mode 100644 index 274ba31582f..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_write.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "lfrfid_app_scene_write.h" - -void LfRfidAppSceneWrite::on_enter(LfRfidApp* app, bool /* need_restore */) { - card_not_supported = false; - string_init(data_string); - - const uint8_t* data = app->worker.key.get_data(); - - for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) { - string_cat_printf(data_string, "%02X", data[i]); - } - - auto popup = app->view_controller.get(); - - popup->set_header("Writing", 89, 30, AlignCenter, AlignTop); - if(strlen(app->worker.key.get_name())) { - popup->set_text(app->worker.key.get_name(), 89, 43, AlignCenter, AlignTop); - } else { - popup->set_text(string_get_cstr(data_string), 89, 43, AlignCenter, AlignTop); - } - popup->set_icon(0, 3, &I_RFIDDolphinSend_97x61); - - app->view_controller.switch_to(); - app->worker.start_write(); -} - -bool LfRfidAppSceneWrite::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Tick) { - RfidWorker::WriteResult result = app->worker.write(); - - switch(result) { - case RfidWorker::WriteResult::Nothing: - notification_message(app->notification, &sequence_blink_magenta_10); - break; - case RfidWorker::WriteResult::Ok: - notification_message(app->notification, &sequence_success); - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::WriteSuccess); - break; - case RfidWorker::WriteResult::NotWritable: - if(!card_not_supported) { - auto popup = app->view_controller.get(); - popup->set_icon(72, 17, &I_DolphinCommon_56x48); - popup->set_header("Still trying to write...", 64, 3, AlignCenter, AlignTop); - popup->set_text( - "Make sure this\ncard is writable\nand not\nprotected.", - 3, - 17, - AlignLeft, - AlignTop); - card_not_supported = true; - } - notification_message(app->notification, &sequence_blink_yellow_10); - break; - } - } - - return consumed; -} - -void LfRfidAppSceneWrite::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); - app->worker.stop_write(); - string_clear(data_string); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_write.h b/applications/lfrfid/scene/lfrfid_app_scene_write.h deleted file mode 100644 index 3abadebab5a..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_write.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneWrite : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - string_t data_string; - bool card_not_supported; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_write_success.cpp b/applications/lfrfid/scene/lfrfid_app_scene_write_success.cpp deleted file mode 100644 index 3cf00183d71..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_write_success.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "lfrfid_app_scene_write_success.h" - -void LfRfidAppSceneWriteSuccess::on_enter(LfRfidApp* app, bool /* need_restore */) { - auto popup = app->view_controller.get(); - popup->set_header("Successfully\nwritten!", 94, 3, AlignCenter, AlignTop); - popup->set_icon(0, 6, &I_RFIDDolphinSuccess_108x57); - popup->set_context(app); - popup->set_callback(LfRfidAppSceneWriteSuccess::timeout_callback); - popup->set_timeout(1500); - popup->enable_timeout(); - - app->view_controller.switch_to(); - notification_message_block(app->notification, &sequence_set_green_255); -} - -bool LfRfidAppSceneWriteSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Back) { - app->scene_controller.search_and_switch_to_previous_scene( - {LfRfidApp::SceneType::ReadKeyMenu, LfRfidApp::SceneType::SelectKey}); - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneWriteSuccess::on_exit(LfRfidApp* app) { - notification_message_block(app->notification, &sequence_reset_green); - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneWriteSuccess::timeout_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Back; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_write_success.h b/applications/lfrfid/scene/lfrfid_app_scene_write_success.h deleted file mode 100644 index 4ac9f0892f8..00000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_write_success.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneWriteSuccess : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void timeout_callback(void* context); -}; diff --git a/applications/lfrfid/view/container_vm.cpp b/applications/lfrfid/view/container_vm.cpp deleted file mode 100644 index 3c01ba30423..00000000000 --- a/applications/lfrfid/view/container_vm.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include "container_vm.h" -#include "elements/generic_element.h" -#include "elements/string_element.h" -#include "elements/icon_element.h" -#include "elements/button_element.h" -#include - -class ContainerVMData { -public: - ContainerVMData(){}; - - ~ContainerVMData() { - for(auto& it : elements) delete it; - }; - - std::list elements; - - template T add(const T element, View* view) { - elements.push_back(element); - element->set_parent_view(view); - return element; - } - - void clean() { - for(auto& it : elements) delete it; - elements.clear(); - } -}; - -struct ContainerVMModel { - ContainerVMData* data; -}; - -ContainerVM::ContainerVM() { - view = view_alloc(); - view_set_context(view, this); - view_allocate_model(view, ViewModelTypeLocking, sizeof(ContainerVMModel)); - - with_view_model_cpp(view, ContainerVMModel, model, { - model->data = new ContainerVMData(); - return true; - }); - - view_set_draw_callback(view, view_draw_callback); - view_set_input_callback(view, view_input_callback); -} - -ContainerVM::~ContainerVM() { - with_view_model_cpp(view, ContainerVMModel, model, { - delete model->data; - model->data = NULL; - return false; - }); - - view_free(view); -} - -View* ContainerVM::get_view() { - return view; -} - -void ContainerVM::clean() { - with_view_model_cpp(view, ContainerVMModel, model, { - model->data->clean(); - return true; - }); -} - -template T* ContainerVM::add() { - T* element = new T(); - - with_view_model_cpp(view, ContainerVMModel, model, { - model->data->add(element, view); - return true; - }); - - return element; -} - -void ContainerVM::view_draw_callback(Canvas* canvas, void* model) { - ContainerVMData* data = static_cast(model)->data; - - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontPrimary); - - for(const auto& element : data->elements) { - element->draw(canvas); - } -} - -bool ContainerVM::view_input_callback(InputEvent* event, void* context) { - bool consumed = false; - View* view = static_cast(context)->view; - - with_view_model_cpp(view, ContainerVMModel, model, { - for(const auto& element : model->data->elements) { - if(element->input(event)) { - consumed = true; - } - - if(consumed) { - break; - } - } - - return consumed; - }); - - return consumed; -} - -template StringElement* ContainerVM::add(); -template IconElement* ContainerVM::add(); -template ButtonElement* ContainerVM::add(); diff --git a/applications/lfrfid/view/container_vm.h b/applications/lfrfid/view/container_vm.h deleted file mode 100644 index 011baa2e98c..00000000000 --- a/applications/lfrfid/view/container_vm.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include - -class ContainerVM : public GenericViewModule { -public: - ContainerVM(); - ~ContainerVM() final; - View* get_view() final; - void clean() final; - - template T* add(); - -private: - View* view; - static void view_draw_callback(Canvas* canvas, void* model); - static bool view_input_callback(InputEvent* event, void* context); -}; diff --git a/applications/lfrfid/view/elements/button_element.cpp b/applications/lfrfid/view/elements/button_element.cpp deleted file mode 100644 index 58e1ac3e1c9..00000000000 --- a/applications/lfrfid/view/elements/button_element.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "button_element.h" -#include - -ButtonElement::ButtonElement() { -} - -ButtonElement::~ButtonElement() { -} - -void ButtonElement::draw(Canvas* canvas) { - if(text != nullptr) { - canvas_set_font(canvas, FontSecondary); - switch(type) { - case Type::Left: - elements_button_left(canvas, text); - break; - case Type::Center: - elements_button_center(canvas, text); - break; - case Type::Right: - elements_button_right(canvas, text); - break; - } - } -} - -bool ButtonElement::input(InputEvent* event) { - bool consumed = false; - if(event->type == InputTypeShort && callback != nullptr) { - switch(type) { - case Type::Left: - if(event->key == InputKeyLeft) { - callback(context); - consumed = true; - } - break; - case Type::Center: - if(event->key == InputKeyOk) { - callback(context); - consumed = true; - } - break; - case Type::Right: - if(event->key == InputKeyRight) { - callback(context); - consumed = true; - } - break; - } - } - - return consumed; -} - -void ButtonElement::set_type(Type _type, const char* _text) { - lock_model(); - type = _type; - text = _text; - unlock_model(true); -} - -void ButtonElement::set_callback(void* _context, ButtonElementCallback _callback) { - context = _context; - callback = _callback; -} diff --git a/applications/lfrfid/view/elements/button_element.h b/applications/lfrfid/view/elements/button_element.h deleted file mode 100644 index eb964427738..00000000000 --- a/applications/lfrfid/view/elements/button_element.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include "generic_element.h" - -typedef void (*ButtonElementCallback)(void* context); - -class ButtonElement : public GenericElement { -public: - ButtonElement(); - ~ButtonElement() final; - void draw(Canvas* canvas) final; - bool input(InputEvent* event) final; - - enum class Type : uint8_t { - Left, - Center, - Right, - }; - - void set_type(Type type, const char* text); - void set_callback(void* context, ButtonElementCallback callback); - -private: - Type type = Type::Left; - const char* text = nullptr; - - void* context = nullptr; - ButtonElementCallback callback = nullptr; -}; diff --git a/applications/lfrfid/view/elements/generic_element.cpp b/applications/lfrfid/view/elements/generic_element.cpp deleted file mode 100644 index e0f08d15c0f..00000000000 --- a/applications/lfrfid/view/elements/generic_element.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "generic_element.h" - -void GenericElement::lock_model() { - furi_assert(view != nullptr); - view_get_model(view); -} - -void GenericElement::unlock_model(bool need_redraw) { - furi_assert(view != nullptr); - view_commit_model(view, need_redraw); -} - -void GenericElement::set_parent_view(View* _view) { - view = _view; -} diff --git a/applications/lfrfid/view/elements/generic_element.h b/applications/lfrfid/view/elements/generic_element.h deleted file mode 100644 index f5a58b2d923..00000000000 --- a/applications/lfrfid/view/elements/generic_element.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include -#include - -class GenericElement { -public: - GenericElement(){}; - virtual ~GenericElement(){}; - virtual void draw(Canvas* canvas) = 0; - virtual bool input(InputEvent* event) = 0; - - // TODO that must be accessible only to ContainerVMData - void set_parent_view(View* view); - - // TODO that must be accessible only to inheritors - void lock_model(); - void unlock_model(bool need_redraw); - -private: - View* view = nullptr; -}; diff --git a/applications/lfrfid/view/elements/icon_element.cpp b/applications/lfrfid/view/elements/icon_element.cpp deleted file mode 100644 index 0b6fba7dad4..00000000000 --- a/applications/lfrfid/view/elements/icon_element.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "icon_element.h" - -IconElement::IconElement() { -} - -IconElement::~IconElement() { -} - -void IconElement::draw(Canvas* canvas) { - if(icon != NULL) { - canvas_draw_icon(canvas, x, y, icon); - } -} - -bool IconElement::input(InputEvent* /* event */) { - return false; -} - -void IconElement::set_icon(uint8_t _x, uint8_t _y, const Icon* _icon) { - lock_model(); - icon = _icon; - x = _x; - y = _y; - unlock_model(true); -} diff --git a/applications/lfrfid/view/elements/icon_element.h b/applications/lfrfid/view/elements/icon_element.h deleted file mode 100644 index a08202741ea..00000000000 --- a/applications/lfrfid/view/elements/icon_element.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include "generic_element.h" - -class IconElement : public GenericElement { -public: - IconElement(); - ~IconElement() final; - void draw(Canvas* canvas) final; - bool input(InputEvent* event) final; - - void set_icon(uint8_t x = 0, uint8_t y = 0, const Icon* icon = NULL); - -private: - const Icon* icon = NULL; - uint8_t x = 0; - uint8_t y = 0; -}; diff --git a/applications/lfrfid/view/elements/string_element.cpp b/applications/lfrfid/view/elements/string_element.cpp deleted file mode 100644 index 44c11e01a4c..00000000000 --- a/applications/lfrfid/view/elements/string_element.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "string_element.h" -#include - -StringElement::StringElement() { -} - -StringElement::~StringElement() { -} - -void StringElement::draw(Canvas* canvas) { - if(text) { - string_t line; - string_init(line); - string_set_str(line, text); - - canvas_set_font(canvas, font); - if(fit_width != 0) { - elements_string_fit_width(canvas, line, fit_width); - } - elements_multiline_text_aligned(canvas, x, y, horizontal, vertical, string_get_cstr(line)); - - string_clear(line); - } -} - -bool StringElement::input(InputEvent* /* event */) { - return false; -} - -void StringElement::set_text( - const char* _text, - uint8_t _x, - uint8_t _y, - uint8_t _fit_w, - Align _horizontal, - Align _vertical, - Font _font) { - lock_model(); - text = _text; - x = _x; - y = _y; - fit_width = _fit_w; - horizontal = _horizontal; - vertical = _vertical; - font = _font; - unlock_model(true); -} diff --git a/applications/lfrfid/view/elements/string_element.h b/applications/lfrfid/view/elements/string_element.h deleted file mode 100644 index 173fdd60197..00000000000 --- a/applications/lfrfid/view/elements/string_element.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include "generic_element.h" - -class StringElement : public GenericElement { -public: - StringElement(); - ~StringElement() final; - void draw(Canvas* canvas) final; - bool input(InputEvent* event) final; - - void set_text( - const char* text = NULL, - uint8_t x = 0, - uint8_t y = 0, - uint8_t fit_width = 0, - Align horizontal = AlignLeft, - Align vertical = AlignTop, - Font font = FontPrimary); - -private: - const char* text = NULL; - uint8_t x = 0; - uint8_t y = 0; - uint8_t fit_width = 0; - Align horizontal = AlignLeft; - Align vertical = AlignTop; - Font font = FontPrimary; -}; diff --git a/applications/lfrfid_debug/application.fam b/applications/lfrfid_debug/application.fam deleted file mode 100644 index 910f65cf3cb..00000000000 --- a/applications/lfrfid_debug/application.fam +++ /dev/null @@ -1,12 +0,0 @@ -App( - appid="lfrfid_debug", - name="LF-RFID Debug", - apptype=FlipperAppType.DEBUG, - entry_point="lfrfid_debug_app", - requires=[ - "gui", - "lfrfid", - ], - stack_size=1 * 1024, - order=100, -) diff --git a/applications/lfrfid_debug/lfrfid_debug_app.cpp b/applications/lfrfid_debug/lfrfid_debug_app.cpp deleted file mode 100644 index ef970e3617a..00000000000 --- a/applications/lfrfid_debug/lfrfid_debug_app.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "lfrfid_debug_app.h" -#include "scene/lfrfid_debug_app_scene_start.h" -#include "scene/lfrfid_debug_app_scene_tune.h" - -LfRfidDebugApp::LfRfidDebugApp() - : scene_controller{this} { -} - -LfRfidDebugApp::~LfRfidDebugApp() { -} - -void LfRfidDebugApp::run() { - view_controller.attach_to_gui(ViewDispatcherTypeFullscreen); - scene_controller.add_scene(SceneType::Start, new LfRfidDebugAppSceneStart()); - scene_controller.add_scene(SceneType::TuneScene, new LfRfidDebugAppSceneTune()); - scene_controller.process(100); -} diff --git a/applications/lfrfid_debug/lfrfid_debug_app.h b/applications/lfrfid_debug/lfrfid_debug_app.h deleted file mode 100644 index fee183aec40..00000000000 --- a/applications/lfrfid_debug/lfrfid_debug_app.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once -#include -#include - -#include -#include -#include - -#include -#include "view_modules/lfrfid_view_tune_vm.h" - -class LfRfidDebugApp { -public: - enum class EventType : uint8_t { - GENERIC_EVENT_ENUM_VALUES, - MenuSelected, - }; - - enum class SceneType : uint8_t { - GENERIC_SCENE_ENUM_VALUES, - TuneScene, - }; - - class Event { - public: - union { - int32_t menu_index; - } payload; - - EventType type; - }; - - SceneController, LfRfidDebugApp> scene_controller; - ViewController view_controller; - - ~LfRfidDebugApp(); - LfRfidDebugApp(); - - void run(); -}; diff --git a/applications/lfrfid_debug/lfrfid_debug_app_launcher.cpp b/applications/lfrfid_debug/lfrfid_debug_app_launcher.cpp deleted file mode 100644 index 4551a17cb4f..00000000000 --- a/applications/lfrfid_debug/lfrfid_debug_app_launcher.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "lfrfid_debug_app.h" - -// app enter function -extern "C" int32_t lfrfid_debug_app(void* p) { - UNUSED(p); - LfRfidDebugApp* app = new LfRfidDebugApp(); - app->run(); - delete app; - - return 0; -} diff --git a/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_start.cpp b/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_start.cpp deleted file mode 100644 index 873e152a102..00000000000 --- a/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_start.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "lfrfid_debug_app_scene_start.h" - -typedef enum { - SubmenuTune, -} SubmenuIndex; - -void LfRfidDebugAppSceneStart::on_enter(LfRfidDebugApp* app, bool need_restore) { - auto submenu = app->view_controller.get(); - auto callback = cbc::obtain_connector(this, &LfRfidDebugAppSceneStart::submenu_callback); - - submenu->add_item("Tune", SubmenuTune, callback, app); - - if(need_restore) { - submenu->set_selected_item(submenu_item_selected); - } - app->view_controller.switch_to(); -} - -bool LfRfidDebugAppSceneStart::on_event(LfRfidDebugApp* app, LfRfidDebugApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidDebugApp::EventType::MenuSelected) { - submenu_item_selected = event->payload.menu_index; - switch(event->payload.menu_index) { - case SubmenuTune: - app->scene_controller.switch_to_next_scene(LfRfidDebugApp::SceneType::TuneScene); - break; - } - consumed = true; - } - - return consumed; -} - -void LfRfidDebugAppSceneStart::on_exit(LfRfidDebugApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidDebugAppSceneStart::submenu_callback(void* context, uint32_t index) { - LfRfidDebugApp* app = static_cast(context); - LfRfidDebugApp::Event event; - - event.type = LfRfidDebugApp::EventType::MenuSelected; - event.payload.menu_index = index; - - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_start.h b/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_start.h deleted file mode 100644 index 7fc0b07d97d..00000000000 --- a/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_start.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "../lfrfid_debug_app.h" - -class LfRfidDebugAppSceneStart : public GenericScene { -public: - void on_enter(LfRfidDebugApp* app, bool need_restore) final; - bool on_event(LfRfidDebugApp* app, LfRfidDebugApp::Event* event) final; - void on_exit(LfRfidDebugApp* app) final; - -private: - void submenu_callback(void* context, uint32_t index); - uint32_t submenu_item_selected = 0; -}; diff --git a/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_tune.cpp b/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_tune.cpp deleted file mode 100644 index 4b6276497f5..00000000000 --- a/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_tune.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "lfrfid_debug_app_scene_tune.h" -#include - -static void comparator_trigger_callback(bool level, void* comp_ctx) { - UNUSED(comp_ctx); - furi_hal_gpio_write(&gpio_ext_pa7, !level); -} - -void LfRfidDebugAppSceneTune::on_enter(LfRfidDebugApp* app, bool /* need_restore */) { - app->view_controller.switch_to(); - furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull); - - furi_hal_rfid_comp_set_callback(comparator_trigger_callback, this); - furi_hal_rfid_comp_start(); - - furi_hal_rfid_pins_read(); - furi_hal_rfid_tim_read(125000, 0.5); - furi_hal_rfid_tim_read_start(); -} - -bool LfRfidDebugAppSceneTune::on_event(LfRfidDebugApp* app, LfRfidDebugApp::Event* /* event */) { - bool consumed = false; - - LfRfidViewTuneVM* tune = app->view_controller; - - if(tune->is_dirty()) { - furi_hal_rfid_set_read_period(tune->get_ARR()); - furi_hal_rfid_set_read_pulse(tune->get_CCR()); - } - - return consumed; -} - -void LfRfidDebugAppSceneTune::on_exit(LfRfidDebugApp* /* app */) { - furi_hal_rfid_comp_stop(); - furi_hal_rfid_comp_set_callback(NULL, NULL); - - furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog); - furi_hal_rfid_tim_read_stop(); - furi_hal_rfid_tim_reset(); - furi_hal_rfid_pins_reset(); -} diff --git a/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_tune.h b/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_tune.h deleted file mode 100644 index 53399efc9b7..00000000000 --- a/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_tune.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../lfrfid_debug_app.h" - -class LfRfidDebugAppSceneTune : public GenericScene { -public: - void on_enter(LfRfidDebugApp* app, bool need_restore) final; - bool on_event(LfRfidDebugApp* app, LfRfidDebugApp::Event* event) final; - void on_exit(LfRfidDebugApp* app) final; -}; diff --git a/applications/lfrfid_debug/view_modules/lfrfid_view_tune_vm.cpp b/applications/lfrfid_debug/view_modules/lfrfid_view_tune_vm.cpp deleted file mode 100644 index 5c244b92ca3..00000000000 --- a/applications/lfrfid_debug/view_modules/lfrfid_view_tune_vm.cpp +++ /dev/null @@ -1,214 +0,0 @@ -#include "lfrfid_view_tune_vm.h" -#include -#include - -struct LfRfidViewTuneVMModel { - bool dirty; - bool fine; - uint32_t ARR; - uint32_t CCR; - int pos; -}; - -void LfRfidViewTuneVM::view_draw_callback(Canvas* canvas, void* _model) { - LfRfidViewTuneVMModel* model = reinterpret_cast(_model); - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - if(model->fine) { - canvas_draw_box( - canvas, - 128 - canvas_string_width(canvas, "Fine") - 4, - 0, - canvas_string_width(canvas, "Fine") + 4, - canvas_current_font_height(canvas) + 1); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_str_aligned(canvas, 128 - 2, 2, AlignRight, AlignTop, "Fine"); - canvas_set_color(canvas, ColorBlack); - - constexpr uint8_t buffer_size = 128; - char buffer[buffer_size + 1]; - double freq = ((float)SystemCoreClock / ((float)model->ARR + 1)); - double duty = ((float)model->CCR + 1) / ((float)model->ARR + 1) * 100.0f; - snprintf( - buffer, - buffer_size, - "%sARR: %lu\n" - "freq = %.4f\n" - "%sCCR: %lu\n" - "duty = %.4f", - model->pos == 0 ? ">" : "", - model->ARR, - freq, - model->pos == 1 ? ">" : "", - model->CCR, - duty); - elements_multiline_text_aligned(canvas, 2, 2, AlignLeft, AlignTop, buffer); -} - -bool LfRfidViewTuneVM::view_input_callback(InputEvent* event, void* context) { - LfRfidViewTuneVM* _this = reinterpret_cast(context); - bool consumed = false; - - // Process key presses only - if(event->type == InputTypeShort || event->type == InputTypeRepeat) { - consumed = true; - - switch(event->key) { - case InputKeyLeft: - _this->button_left(); - break; - case InputKeyRight: - _this->button_right(); - break; - case InputKeyUp: - _this->button_up(); - break; - case InputKeyDown: - _this->button_down(); - break; - case InputKeyOk: - _this->button_ok(); - break; - default: - consumed = false; - break; - } - } - - return consumed; -} - -void LfRfidViewTuneVM::button_up() { - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - if(model->pos > 0) model->pos--; - return true; - }); -} - -void LfRfidViewTuneVM::button_down() { - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - if(model->pos < 1) model->pos++; - return true; - }); -} - -void LfRfidViewTuneVM::button_left() { - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - if(model->pos == 0) { - if(model->fine) { - model->ARR -= 1; - } else { - model->ARR -= 10; - } - } else if(model->pos == 1) { - if(model->fine) { - model->CCR -= 1; - } else { - model->CCR -= 10; - } - } - - model->dirty = true; - return true; - }); -} - -void LfRfidViewTuneVM::button_right() { - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - if(model->pos == 0) { - if(model->fine) { - model->ARR += 1; - } else { - model->ARR += 10; - } - } else if(model->pos == 1) { - if(model->fine) { - model->CCR += 1; - } else { - model->CCR += 10; - } - } - - model->dirty = true; - return true; - }); -} - -void LfRfidViewTuneVM::button_ok() { - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - model->fine = !model->fine; - return true; - }); -} - -LfRfidViewTuneVM::LfRfidViewTuneVM() { - view = view_alloc(); - view_set_context(view, this); - view_allocate_model(view, ViewModelTypeLocking, sizeof(LfRfidViewTuneVMModel)); - - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - model->dirty = true; - model->fine = false; - model->ARR = 511; - model->CCR = 255; - model->pos = 0; - return true; - }); - - view_set_draw_callback( - view, cbc::obtain_connector(this, &LfRfidViewTuneVM::view_draw_callback)); - view_set_input_callback( - view, cbc::obtain_connector(this, &LfRfidViewTuneVM::view_input_callback)); -} - -LfRfidViewTuneVM::~LfRfidViewTuneVM() { - view_free(view); -} - -View* LfRfidViewTuneVM::get_view() { - return view; -} - -void LfRfidViewTuneVM::clean() { - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - model->dirty = true; - model->fine = false; - model->ARR = 511; - model->CCR = 255; - model->pos = 0; - return true; - }); -} - -bool LfRfidViewTuneVM::is_dirty() { - bool result; - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - result = model->dirty; - model->dirty = false; - return false; - }); - - return result; -} - -uint32_t LfRfidViewTuneVM::get_ARR() { - uint32_t result; - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - result = model->ARR; - return false; - }); - - return result; -} - -uint32_t LfRfidViewTuneVM::get_CCR() { - uint32_t result; - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - result = model->CCR; - return false; - }); - - return result; -} diff --git a/applications/lfrfid_debug/view_modules/lfrfid_view_tune_vm.h b/applications/lfrfid_debug/view_modules/lfrfid_view_tune_vm.h deleted file mode 100644 index 7fb18565483..00000000000 --- a/applications/lfrfid_debug/view_modules/lfrfid_view_tune_vm.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include -#include - -class LfRfidViewTuneVM : public GenericViewModule { -public: - LfRfidViewTuneVM(); - ~LfRfidViewTuneVM() final; - View* get_view() final; - void clean() final; - - bool is_dirty(); - uint32_t get_ARR(); - uint32_t get_CCR(); - -private: - View* view; - void view_draw_callback(Canvas* canvas, void* _model); - bool view_input_callback(InputEvent* event, void* context); - - void button_up(); - void button_down(); - void button_left(); - void button_right(); - void button_ok(); -}; diff --git a/applications/loader/application.fam b/applications/loader/application.fam deleted file mode 100644 index c1ba4e5490b..00000000000 --- a/applications/loader/application.fam +++ /dev/null @@ -1,10 +0,0 @@ -App( - appid="loader", - name="LoaderSrv", - apptype=FlipperAppType.SERVICE, - entry_point="loader_srv", - cdefines=["SRV_LOADER"], - requires=["gui"], - stack_size=2 * 1024, - order=90, -) diff --git a/applications/loader/loader.c b/applications/loader/loader.c deleted file mode 100644 index 9ece7f27144..00000000000 --- a/applications/loader/loader.c +++ /dev/null @@ -1,492 +0,0 @@ -#include "applications.h" -#include -#include "loader/loader.h" -#include "loader_i.h" - -#define TAG "LoaderSrv" - -#define LOADER_THREAD_FLAG_SHOW_MENU (1 << 0) -#define LOADER_THREAD_FLAG_ALL (LOADER_THREAD_FLAG_SHOW_MENU) - -static Loader* loader_instance = NULL; - -static bool - loader_start_application(const FlipperApplication* application, const char* arguments) { - loader_instance->application = application; - - furi_assert(loader_instance->application_arguments == NULL); - if(arguments && strlen(arguments) > 0) { - loader_instance->application_arguments = strdup(arguments); - } - - FURI_LOG_I(TAG, "Starting: %s", loader_instance->application->name); - - furi_thread_set_name(loader_instance->application_thread, loader_instance->application->name); - furi_thread_set_stack_size( - loader_instance->application_thread, loader_instance->application->stack_size); - furi_thread_set_context( - loader_instance->application_thread, loader_instance->application_arguments); - furi_thread_set_callback( - loader_instance->application_thread, loader_instance->application->app); - - furi_thread_start(loader_instance->application_thread); - - 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); - - 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"); -} - -static FlipperApplication const* loader_find_application_by_name_in_list( - const char* name, - const FlipperApplication* list, - const uint32_t n_apps) { - for(size_t i = 0; i < n_apps; i++) { - if(strcmp(name, list[i].name) == 0) { - return &list[i]; - } - } - return NULL; -} - -const FlipperApplication* loader_find_application_by_name(const char* name) { - const FlipperApplication* application = NULL; - application = loader_find_application_by_name_in_list(name, FLIPPER_APPS, FLIPPER_APPS_COUNT); - if(!application) { - application = - loader_find_application_by_name_in_list(name, FLIPPER_PLUGINS, FLIPPER_PLUGINS_COUNT); - } - if(!application) { - application = loader_find_application_by_name_in_list( - name, FLIPPER_SETTINGS_APPS, FLIPPER_SETTINGS_APPS_COUNT); - } - if(!application) { - application = loader_find_application_by_name_in_list( - name, FLIPPER_SYSTEM_APPS, FLIPPER_SYSTEM_APPS_COUNT); - } - if(!application) { - application = loader_find_application_by_name_in_list( - name, FLIPPER_DEBUG_APPS, FLIPPER_DEBUG_APPS_COUNT); - } - - return application; -} - -void loader_cli_open(Cli* cli, string_t args, Loader* instance) { - UNUSED(cli); - if(loader_is_locked(instance)) { - printf("Can't start, furi application is running"); - return; - } - - string_t application_name; - string_init(application_name); - - do { - if(!args_read_probably_quoted_string_and_trim(args, application_name)) { - printf("No application provided\r\n"); - break; - } - - const FlipperApplication* application = - loader_find_application_by_name(string_get_cstr(application_name)); - if(!application) { - printf("%s doesn't exists\r\n", string_get_cstr(application_name)); - break; - } - - string_strim(args); - if(!loader_start_application(application, string_get_cstr(args))) { - printf("Can't start, furi application is running"); - return; - } else { - // We must to increment lock counter to keep balance - // TODO: rewrite whole thing, it's complex as hell - FURI_CRITICAL_ENTER(); - instance->lock_count++; - FURI_CRITICAL_EXIT(); - } - } while(false); - - string_clear(application_name); -} - -void loader_cli_list(Cli* cli, string_t args, Loader* instance) { - UNUSED(cli); - UNUSED(args); - UNUSED(instance); - printf("Applications:\r\n"); - for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { - printf("\t%s\r\n", FLIPPER_APPS[i].name); - } - - printf("Plugins:\r\n"); - for(size_t i = 0; i < FLIPPER_PLUGINS_COUNT; i++) { - printf("\t%s\r\n", FLIPPER_PLUGINS[i].name); - } - - 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); - } - } -} - -static void loader_cli(Cli* cli, string_t args, void* _ctx) { - furi_assert(_ctx); - Loader* instance = _ctx; - - string_t cmd; - string_init(cmd); - - do { - if(!args_read_string_and_trim(args, cmd)) { - loader_cli_print_usage(); - break; - } - - if(string_cmp_str(cmd, "list") == 0) { - loader_cli_list(cli, args, instance); - break; - } - - if(string_cmp_str(cmd, "open") == 0) { - loader_cli_open(cli, args, instance); - break; - } - - loader_cli_print_usage(); - } while(false); - - string_clear(cmd); -} - -LoaderStatus loader_start(Loader* instance, const char* name, const char* args) { - UNUSED(instance); - furi_assert(name); - - const FlipperApplication* application = loader_find_application_by_name(name); - - if(!application) { - FURI_LOG_E(TAG, "Can't find application with name %s", name); - return LoaderStatusErrorUnknownApp; - } - - if(!loader_lock(loader_instance)) { - FURI_LOG_E(TAG, "Loader is locked"); - return LoaderStatusErrorAppStarted; - } - - if(!loader_start_application(application, args)) { - return LoaderStatusErrorInternal; - } - - return LoaderStatusOk; -} - -bool loader_lock(Loader* instance) { - FURI_CRITICAL_ENTER(); - bool result = false; - if(instance->lock_count == 0) { - instance->lock_count++; - result = true; - } - FURI_CRITICAL_EXIT(); - return result; -} - -void loader_unlock(Loader* instance) { - FURI_CRITICAL_ENTER(); - if(instance->lock_count > 0) instance->lock_count--; - FURI_CRITICAL_EXIT(); -} - -bool loader_is_locked(Loader* instance) { - return instance->lock_count > 0; -} - -static void loader_thread_state_callback(FuriThreadState thread_state, void* context) { - furi_assert(context); - - Loader* instance = context; - LoaderEvent event; - - if(thread_state == FuriThreadStateRunning) { - event.type = LoaderEventTypeApplicationStarted; - furi_pubsub_publish(loader_instance->pubsub, &event); - - if(!loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe) { - furi_hal_power_insomnia_enter(); - } - } else if(thread_state == FuriThreadStateStopped) { - FURI_LOG_I( - TAG, - "Application thread stopped. Free heap: %d. Thread allocation balance: %d.", - memmgr_get_free_heap(), - furi_thread_get_heap_size(instance->application_thread)); - - if(loader_instance->application_arguments) { - free(loader_instance->application_arguments); - loader_instance->application_arguments = NULL; - } - - if(!loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe) { - furi_hal_power_insomnia_exit(); - } - loader_unlock(instance); - - event.type = LoaderEventTypeApplicationStopped; - furi_pubsub_publish(loader_instance->pubsub, &event); - } -} - -static uint32_t loader_hide_menu(void* context) { - UNUSED(context); - return VIEW_NONE; -} - -static uint32_t loader_back_to_primary_menu(void* context) { - furi_assert(context); - Submenu* submenu = context; - submenu_set_selected_item(submenu, 0); - return LoaderMenuViewPrimary; -} - -static Loader* loader_alloc() { - Loader* instance = malloc(sizeof(Loader)); - - instance->application_thread = furi_thread_alloc(); - furi_thread_enable_heap_trace(instance->application_thread); - furi_thread_set_state_context(instance->application_thread, instance); - furi_thread_set_state_callback(instance->application_thread, loader_thread_state_callback); - - instance->pubsub = furi_pubsub_alloc(); - -#ifdef SRV_CLI - instance->cli = furi_record_open(RECORD_CLI); - cli_add_command( - instance->cli, RECORD_LOADER, CliCommandFlagParallelSafe, loader_cli, instance); -#else - UNUSED(loader_cli); -#endif - - instance->loader_thread = furi_thread_get_current_id(); - - // Gui - instance->gui = furi_record_open(RECORD_GUI); - instance->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_attach_to_gui( - instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen); - // Primary menu - instance->primary_menu = menu_alloc(); - view_set_previous_callback(menu_get_view(instance->primary_menu), loader_hide_menu); - view_dispatcher_add_view( - instance->view_dispatcher, LoaderMenuViewPrimary, menu_get_view(instance->primary_menu)); - // Plugins menu - instance->plugins_menu = submenu_alloc(); - view_set_context(submenu_get_view(instance->plugins_menu), instance->plugins_menu); - view_set_previous_callback( - submenu_get_view(instance->plugins_menu), loader_back_to_primary_menu); - view_dispatcher_add_view( - instance->view_dispatcher, - LoaderMenuViewPlugins, - submenu_get_view(instance->plugins_menu)); - // Debug menu - instance->debug_menu = submenu_alloc(); - view_set_context(submenu_get_view(instance->debug_menu), instance->debug_menu); - view_set_previous_callback( - submenu_get_view(instance->debug_menu), loader_back_to_primary_menu); - view_dispatcher_add_view( - instance->view_dispatcher, LoaderMenuViewDebug, submenu_get_view(instance->debug_menu)); - // Settings menu - instance->settings_menu = submenu_alloc(); - view_set_context(submenu_get_view(instance->settings_menu), instance->settings_menu); - view_set_previous_callback( - submenu_get_view(instance->settings_menu), loader_back_to_primary_menu); - view_dispatcher_add_view( - instance->view_dispatcher, - LoaderMenuViewSettings, - submenu_get_view(instance->settings_menu)); - - view_dispatcher_enable_queue(instance->view_dispatcher); - - return instance; -} - -static void loader_free(Loader* instance) { - furi_assert(instance); - - if(instance->cli) { - furi_record_close(RECORD_CLI); - } - - furi_pubsub_free(instance->pubsub); - - furi_thread_free(instance->application_thread); - - menu_free(loader_instance->primary_menu); - view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPrimary); - submenu_free(loader_instance->plugins_menu); - view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPlugins); - submenu_free(loader_instance->debug_menu); - view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewDebug); - submenu_free(loader_instance->settings_menu); - view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewSettings); - view_dispatcher_free(loader_instance->view_dispatcher); - - furi_record_close(RECORD_GUI); - - free(instance); - instance = NULL; -} - -static void loader_build_menu() { - FURI_LOG_I(TAG, "Building main menu"); - size_t i; - for(i = 0; i < FLIPPER_APPS_COUNT; i++) { - menu_add_item( - loader_instance->primary_menu, - FLIPPER_APPS[i].name, - FLIPPER_APPS[i].icon, - i, - loader_menu_callback, - (void*)&FLIPPER_APPS[i]); - } - if(FLIPPER_PLUGINS_COUNT != 0) { - menu_add_item( - loader_instance->primary_menu, - "Plugins", - &A_Plugins_14, - i++, - loader_submenu_callback, - (void*)LoaderMenuViewPlugins); - } - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - menu_add_item( - loader_instance->primary_menu, - "Debug Tools", - &A_Debug_14, - i++, - loader_submenu_callback, - (void*)LoaderMenuViewDebug); - } - menu_add_item( - loader_instance->primary_menu, - "Settings", - &A_Settings_14, - i++, - loader_submenu_callback, - (void*)LoaderMenuViewSettings); -} - -static void loader_build_submenu() { - 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, - FLIPPER_PLUGINS[i].name, - i, - loader_menu_callback, - (void*)&FLIPPER_PLUGINS[i]); - } - - FURI_LOG_I(TAG, "Building debug menu"); - for(i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) { - submenu_add_item( - loader_instance->debug_menu, - FLIPPER_DEBUG_APPS[i].name, - i, - loader_menu_callback, - (void*)&FLIPPER_DEBUG_APPS[i]); - } - - FURI_LOG_I(TAG, "Building settings menu"); - for(i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { - submenu_add_item( - loader_instance->settings_menu, - FLIPPER_SETTINGS_APPS[i].name, - i, - loader_menu_callback, - (void*)&FLIPPER_SETTINGS_APPS[i]); - } -} - -void loader_show_menu() { - furi_assert(loader_instance); - furi_thread_flags_set(loader_instance->loader_thread, LOADER_THREAD_FLAG_SHOW_MENU); -} - -void loader_update_menu() { - menu_reset(loader_instance->primary_menu); - loader_build_menu(); -} - -int32_t loader_srv(void* p) { - UNUSED(p); - FURI_LOG_I(TAG, "Executing system start hooks"); - for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) { - FLIPPER_ON_SYSTEM_START[i](); - } - - FURI_LOG_I(TAG, "Starting"); - loader_instance = loader_alloc(); - - loader_build_menu(); - loader_build_submenu(); - - FURI_LOG_I(TAG, "Started"); - - furi_record_create(RECORD_LOADER, loader_instance); - - if(FLIPPER_AUTORUN_APP_NAME && strlen(FLIPPER_AUTORUN_APP_NAME)) { - loader_start(loader_instance, FLIPPER_AUTORUN_APP_NAME, NULL); - } - - while(1) { - uint32_t flags = - furi_thread_flags_wait(LOADER_THREAD_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever); - if(flags & LOADER_THREAD_FLAG_SHOW_MENU) { - menu_set_selected_item(loader_instance->primary_menu, 0); - view_dispatcher_switch_to_view( - loader_instance->view_dispatcher, LoaderMenuViewPrimary); - view_dispatcher_run(loader_instance->view_dispatcher); - } - } - - furi_record_destroy(RECORD_LOADER); - loader_free(loader_instance); - - return 0; -} - -FuriPubSub* loader_get_pubsub(Loader* instance) { - return instance->pubsub; -} diff --git a/applications/loader/loader.h b/applications/loader/loader.h deleted file mode 100644 index 8f95d81b2be..00000000000 --- a/applications/loader/loader.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include -#include - -#define RECORD_LOADER "loader" - -typedef struct Loader Loader; - -typedef enum { - LoaderStatusOk, - LoaderStatusErrorAppStarted, - LoaderStatusErrorUnknownApp, - LoaderStatusErrorInternal, -} LoaderStatus; - -typedef enum { - LoaderEventTypeApplicationStarted, - LoaderEventTypeApplicationStopped -} LoaderEventType; - -typedef struct { - LoaderEventType type; -} LoaderEvent; - -/** Start application - * @param name - application name - * @param args - application arguments - * @retval true on success - */ -LoaderStatus loader_start(Loader* instance, const char* name, const char* args); - -/** Lock application start - * @retval true on success - */ -bool loader_lock(Loader* instance); - -/** Unlock application start */ -void loader_unlock(Loader* instance); - -/** Get loader lock status */ -bool loader_is_locked(Loader* instance); - -/** Show primary loader */ -void loader_show_menu(); - -/** Show primary loader */ -void loader_update_menu(); - -/** Show primary loader */ -FuriPubSub* loader_get_pubsub(Loader* instance); diff --git a/applications/loader/loader_i.h b/applications/loader/loader_i.h deleted file mode 100644 index db91f806c5c..00000000000 --- a/applications/loader/loader_i.h +++ /dev/null @@ -1,43 +0,0 @@ -#include "loader.h" - -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include - -struct Loader { - FuriThreadId loader_thread; - - const FlipperApplication* application; - FuriThread* application_thread; - char* application_arguments; - - Cli* cli; - Gui* gui; - - ViewDispatcher* view_dispatcher; - Menu* primary_menu; - Submenu* plugins_menu; - Submenu* debug_menu; - Submenu* settings_menu; - - volatile uint8_t lock_count; - - FuriPubSub* pubsub; -}; - -typedef enum { - LoaderMenuViewPrimary, - LoaderMenuViewPlugins, - LoaderMenuViewDebug, - LoaderMenuViewSettings, -} LoaderMenuView; diff --git a/applications/main/application.fam b/applications/main/application.fam new file mode 100644 index 00000000000..0a90ee2243f --- /dev/null +++ b/applications/main/application.fam @@ -0,0 +1,31 @@ +App( + appid="main_apps", + name="Basic applications for main menu", + apptype=FlipperAppType.METAPACKAGE, + provides=[ + "gpio", + "ibutton", + "infrared", + "lfrfid", + "nfc", + "subghz", + "bad_usb", + "u2f", + "archive", + "main_apps_on_start", + ], +) + +App( + appid="main_apps_on_start", + name="On start hooks", + apptype=FlipperAppType.METAPACKAGE, + provides=[ + "ibutton_start", + "onewire_start", + "subghz_start", + "infrared_start", + "lfrfid_start", + "nfc_start", + ], +) diff --git a/applications/archive/application.fam b/applications/main/archive/application.fam similarity index 100% rename from applications/archive/application.fam rename to applications/main/archive/application.fam diff --git a/applications/archive/archive.c b/applications/main/archive/archive.c similarity index 96% rename from applications/archive/archive.c rename to applications/main/archive/archive.c index bbe532c8c0d..b8609cf2d88 100644 --- a/applications/archive/archive.c +++ b/applications/main/archive/archive.c @@ -1,5 +1,4 @@ #include "archive_i.h" -#include "m-string.h" bool archive_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -18,7 +17,7 @@ ArchiveApp* archive_alloc() { archive->gui = furi_record_open(RECORD_GUI); archive->text_input = text_input_alloc(); - string_init(archive->fav_move_str); + archive->fav_move_str = furi_string_alloc(); archive->view_dispatcher = view_dispatcher_alloc(); archive->scene_manager = scene_manager_alloc(&archive_scene_handlers, archive); @@ -58,7 +57,7 @@ void archive_free(ArchiveApp* archive) { view_dispatcher_free(archive->view_dispatcher); scene_manager_free(archive->scene_manager); browser_free(archive->browser); - string_clear(archive->fav_move_str); + furi_string_free(archive->fav_move_str); text_input_free(archive->text_input); diff --git a/applications/archive/archive.h b/applications/main/archive/archive.h similarity index 100% rename from applications/archive/archive.h rename to applications/main/archive/archive.h diff --git a/applications/archive/archive_i.h b/applications/main/archive/archive_i.h similarity index 96% rename from applications/archive/archive_i.h rename to applications/main/archive/archive_i.h index 865e837dcbc..d444aef8fcb 100644 --- a/applications/archive/archive_i.h +++ b/applications/main/archive/archive_i.h @@ -28,7 +28,7 @@ struct ArchiveApp { TextInput* text_input; Widget* widget; FuriPubSubSubscription* loader_stop_subscription; - string_t fav_move_str; + FuriString* fav_move_str; char text_store[MAX_NAME_LEN]; char file_extension[MAX_EXT_LEN + 1]; }; diff --git a/applications/archive/helpers/archive_apps.c b/applications/main/archive/helpers/archive_apps.c similarity index 77% rename from applications/archive/helpers/archive_apps.c rename to applications/main/archive/helpers/archive_apps.c index 9a3f825f1c4..c8ad67625f1 100644 --- a/applications/archive/helpers/archive_apps.c +++ b/applications/main/archive/helpers/archive_apps.c @@ -13,7 +13,7 @@ ArchiveAppTypeEnum archive_get_app_type(const char* path) { } app_name++; - for(size_t i = 0; i < COUNT_OF(known_apps); i++) { + for(size_t i = 0; i < COUNT_OF(known_apps); i++) { //-V1008 if(strncmp(app_name, known_apps[i], strlen(known_apps[i])) == 0) { return i; } @@ -29,23 +29,13 @@ bool archive_app_is_available(void* context, const char* path) { if(app == ArchiveAppTypeU2f) { bool file_exists = false; - Storage* fs_api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(fs_api); - - file_exists = - storage_file_open(file, ANY_PATH("u2f/key.u2f"), FSAM_READ, FSOM_OPEN_EXISTING); - if(file_exists) { - storage_file_close(file); - file_exists = - storage_file_open(file, ANY_PATH("u2f/cnt.u2f"), FSAM_READ, FSOM_OPEN_EXISTING); - if(file_exists) { - storage_file_close(file); - } + Storage* storage = furi_record_open(RECORD_STORAGE); + + if(storage_file_exists(storage, ANY_PATH("u2f/key.u2f"))) { + file_exists = storage_file_exists(storage, ANY_PATH("u2f/cnt.u2f")); } - storage_file_free(file); furi_record_close(RECORD_STORAGE); - return file_exists; } else { return false; diff --git a/applications/archive/helpers/archive_apps.h b/applications/main/archive/helpers/archive_apps.h similarity index 100% rename from applications/archive/helpers/archive_apps.h rename to applications/main/archive/helpers/archive_apps.h diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c new file mode 100644 index 00000000000..51457fe81fe --- /dev/null +++ b/applications/main/archive/helpers/archive_browser.c @@ -0,0 +1,566 @@ +#include "archive_files.h" +#include "archive_apps.h" +#include "archive_browser.h" +#include "../views/archive_browser_view.h" + +#include +#include +#include +#include +#include + +static void + archive_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root) { + furi_assert(context); + ArchiveBrowserView* browser = (ArchiveBrowserView*)context; + + int32_t load_offset = 0; + browser->is_root = is_root; + ArchiveTabEnum tab = archive_get_tab(browser); + + if((item_cnt == 0) && (archive_is_home(browser)) && (tab != ArchiveTabBrowser)) { + archive_switch_tab(browser, browser->last_tab_switch_dir); + } else if(!furi_string_start_with_str(browser->path, "/app:")) { + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + files_array_reset(model->files); + model->item_cnt = item_cnt; + model->item_idx = (file_idx > 0) ? file_idx : 0; + load_offset = + CLAMP(model->item_idx - FILE_LIST_BUF_LEN / 2, (int32_t)model->item_cnt, 0); + model->array_offset = 0; + model->list_offset = 0; + model->list_loading = true; + model->folder_loading = false; + }, + false); + archive_update_offset(browser); + + file_browser_worker_load(browser->worker, load_offset, FILE_LIST_BUF_LEN); + } +} + +static void archive_list_load_cb(void* context, uint32_t list_load_offset) { + furi_assert(context); + ArchiveBrowserView* browser = (ArchiveBrowserView*)context; + + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + files_array_reset(model->files); + model->array_offset = list_load_offset; + }, + false); +} + +static void + archive_list_item_cb(void* context, FuriString* item_path, bool is_folder, bool is_last) { + furi_assert(context); + ArchiveBrowserView* browser = (ArchiveBrowserView*)context; + + if(!is_last) { + archive_add_file_item(browser, is_folder, furi_string_get_cstr(item_path)); + } else { + bool load_again = false; + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + model->list_loading = false; + if(archive_is_file_list_load_required(model)) { + load_again = true; + } + }, + true); + if(load_again) { + archive_file_array_load(browser, 0); + } + } +} + +static void archive_long_load_cb(void* context) { + furi_assert(context); + ArchiveBrowserView* browser = (ArchiveBrowserView*)context; + + with_view_model( + browser->view, ArchiveBrowserViewModel * model, { model->folder_loading = true; }, true); +} + +static void archive_file_browser_set_path( + ArchiveBrowserView* browser, + FuriString* path, + const char* filter_ext, + bool skip_assets, + bool hide_dot_files) { + furi_assert(browser); + if(!browser->worker_running) { + browser->worker = + file_browser_worker_alloc(path, NULL, filter_ext, skip_assets, hide_dot_files); + file_browser_worker_set_callback_context(browser->worker, browser); + file_browser_worker_set_folder_callback(browser->worker, archive_folder_open_cb); + file_browser_worker_set_list_callback(browser->worker, archive_list_load_cb); + file_browser_worker_set_item_callback(browser->worker, archive_list_item_cb); + file_browser_worker_set_long_load_callback(browser->worker, archive_long_load_cb); + browser->worker_running = true; + } else { + furi_assert(browser->worker); + file_browser_worker_set_config( + browser->worker, path, filter_ext, skip_assets, hide_dot_files); + } +} + +bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx) { + size_t array_size = files_array_size(model->files); + + if((idx >= (uint32_t)model->array_offset + array_size) || + (idx < (uint32_t)model->array_offset)) { + return false; + } + + return true; +} + +bool archive_is_file_list_load_required(ArchiveBrowserViewModel* model) { + size_t array_size = files_array_size(model->files); + + if((model->list_loading) || (array_size >= model->item_cnt)) { + return false; + } + + if((model->array_offset > 0) && + (model->item_idx < (model->array_offset + FILE_LIST_BUF_LEN / 4))) { + return true; + } + + if(((model->array_offset + array_size) < model->item_cnt) && + (model->item_idx > (int32_t)(model->array_offset + array_size - FILE_LIST_BUF_LEN / 4))) { + return true; + } + + return false; +} + +void archive_update_offset(ArchiveBrowserView* browser) { + furi_assert(browser); + + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + uint16_t bounds = model->item_cnt > 3 ? 2 : model->item_cnt; + + if((model->item_cnt > 3u) && (model->item_idx >= ((int32_t)model->item_cnt - 1))) { + model->list_offset = model->item_idx - 3; + } else if(model->list_offset < model->item_idx - bounds) { + model->list_offset = + CLAMP(model->item_idx - 2, (int32_t)model->item_cnt - bounds, 0); + } else if(model->list_offset > model->item_idx - bounds) { + model->list_offset = + CLAMP(model->item_idx - 1, (int32_t)model->item_cnt - bounds, 0); + } + }, + true); +} + +void archive_update_focus(ArchiveBrowserView* browser, const char* target) { + furi_assert(browser); + furi_assert(target); + + archive_get_items(browser, furi_string_get_cstr(browser->path)); + + if(!archive_file_get_array_size(browser) && archive_is_home(browser)) { + archive_switch_tab(browser, TAB_RIGHT); + } else { + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + uint16_t idx = 0; + while(idx < files_array_size(model->files)) { + ArchiveFile_t* current = files_array_get(model->files, idx); + if(!furi_string_search(current->path, target)) { + model->item_idx = idx + model->array_offset; + break; + } + ++idx; + } + }, + false); + + archive_update_offset(browser); + } +} + +size_t archive_file_get_array_size(ArchiveBrowserView* browser) { + furi_assert(browser); + + uint16_t size = 0; + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { size = files_array_size(model->files); }, + false); + return size; +} + +void archive_set_item_count(ArchiveBrowserView* browser, uint32_t count) { + furi_assert(browser); + + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + model->item_cnt = count; + model->item_idx = CLAMP(model->item_idx, (int32_t)model->item_cnt - 1, 0); + }, + false); + archive_update_offset(browser); +} + +void archive_file_array_rm_selected(ArchiveBrowserView* browser) { + furi_assert(browser); + uint32_t items_cnt = 0; + + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + files_array_remove_v( + model->files, + model->item_idx - model->array_offset, + model->item_idx - model->array_offset + 1); + model->item_cnt--; + model->item_idx = CLAMP(model->item_idx, (int32_t)model->item_cnt - 1, 0); + items_cnt = model->item_cnt; + }, + false); + + if((items_cnt == 0) && (archive_is_home(browser))) { + archive_switch_tab(browser, TAB_RIGHT); + } + + archive_update_offset(browser); +} + +void archive_file_array_swap(ArchiveBrowserView* browser, int8_t dir) { + furi_assert(browser); + + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + ArchiveFile_t temp; + size_t array_size = files_array_size(model->files) - 1; + uint8_t swap_idx = CLAMP((size_t)(model->item_idx + dir), array_size, 0u); + + if(model->item_idx == 0 && dir < 0) { + ArchiveFile_t_init(&temp); + files_array_pop_at(&temp, model->files, array_size); + files_array_push_at(model->files, model->item_idx, temp); + ArchiveFile_t_clear(&temp); + } else if(((uint32_t)model->item_idx == array_size) && (dir > 0)) { + ArchiveFile_t_init(&temp); + files_array_pop_at(&temp, model->files, 0); + files_array_push_at(model->files, array_size, temp); + ArchiveFile_t_clear(&temp); + } else { + files_array_swap_at(model->files, model->item_idx, swap_idx); + } + }, + false); +} + +void archive_file_array_rm_all(ArchiveBrowserView* browser) { + furi_assert(browser); + + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { files_array_reset(model->files); }, + false); +} + +void archive_file_array_load(ArchiveBrowserView* browser, int8_t dir) { + furi_assert(browser); + + int32_t offset_new = 0; + + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + if(model->item_cnt > FILE_LIST_BUF_LEN) { + if(dir < 0) { + offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 3; + } else if(dir == 0) { + offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 2; + } else { + offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 1; + } + if(offset_new > 0) { + offset_new = CLAMP(offset_new, (int32_t)model->item_cnt, 0); + } else { + offset_new = 0; + } + } + }, + false); + + file_browser_worker_load(browser->worker, offset_new, FILE_LIST_BUF_LEN); +} + +ArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser) { + furi_assert(browser); + + ArchiveFile_t* selected = NULL; + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + selected = files_array_size(model->files) ? + files_array_get(model->files, model->item_idx - model->array_offset) : + NULL; + }, + false); + return selected; +} + +ArchiveFile_t* archive_get_file_at(ArchiveBrowserView* browser, size_t idx) { + furi_assert(browser); + + ArchiveFile_t* selected = NULL; + + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + idx = CLAMP(idx - model->array_offset, files_array_size(model->files), 0u); + selected = files_array_size(model->files) ? files_array_get(model->files, idx) : NULL; + }, + false); + return selected; +} + +ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser) { + furi_assert(browser); + + ArchiveTabEnum tab_id = 0; + with_view_model( + browser->view, ArchiveBrowserViewModel * model, { tab_id = model->tab_idx; }, false); + return tab_id; +} + +bool archive_is_home(ArchiveBrowserView* browser) { + furi_assert(browser); + + if(browser->is_root) { + return true; + } + + const char* default_path = archive_get_default_path(archive_get_tab(browser)); + return (furi_string_cmp_str(browser->path, default_path) == 0); +} + +const char* archive_get_name(ArchiveBrowserView* browser) { + ArchiveFile_t* selected = archive_get_current_file(browser); + return furi_string_get_cstr(selected->path); +} + +void archive_set_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) { + furi_assert(browser); + + with_view_model( + browser->view, ArchiveBrowserViewModel * model, { model->tab_idx = tab; }, false); +} + +void archive_add_app_item(ArchiveBrowserView* browser, const char* name) { + furi_assert(browser); + furi_assert(name); + + ArchiveFile_t item; + ArchiveFile_t_init(&item); + furi_string_set(item.path, name); + archive_set_file_type(&item, name, false, true); + + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + files_array_push_back(model->files, item); + model->item_cnt = files_array_size(model->files); + }, + false); + ArchiveFile_t_clear(&item); +} + +static bool archive_get_fap_meta(FuriString* file_path, FuriString* fap_name, uint8_t** icon_ptr) { + Storage* storage = furi_record_open(RECORD_STORAGE); + bool success = false; + if(flipper_application_load_name_and_icon(file_path, storage, icon_ptr, fap_name)) { + success = true; + } + furi_record_close(RECORD_STORAGE); + return success; +} + +void archive_add_file_item(ArchiveBrowserView* browser, bool is_folder, const char* name) { + furi_assert(browser); + furi_assert(name); + + ArchiveFile_t item; + ArchiveFile_t_init(&item); + + furi_string_set(item.path, name); + archive_set_file_type(&item, furi_string_get_cstr(browser->path), is_folder, false); + if(item.type == ArchiveFileTypeApplication) { + item.custom_icon_data = malloc(FAP_MANIFEST_MAX_ICON_SIZE); + if(!archive_get_fap_meta(item.path, item.custom_name, &item.custom_icon_data)) { + free(item.custom_icon_data); + item.custom_icon_data = NULL; + } + } + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { files_array_push_back(model->files, item); }, + false); + ArchiveFile_t_clear(&item); +} + +void archive_show_file_menu(ArchiveBrowserView* browser, bool show) { + furi_assert(browser); + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + if(show) { + if(archive_is_item_in_array(model, model->item_idx)) { + model->menu = true; + model->menu_idx = 0; + ArchiveFile_t* selected = + files_array_get(model->files, model->item_idx - model->array_offset); + selected->fav = + archive_is_favorite("%s", furi_string_get_cstr(selected->path)); + } + } else { + model->menu = false; + model->menu_idx = 0; + } + }, + true); +} + +void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active) { + furi_assert(browser); + + with_view_model( + browser->view, ArchiveBrowserViewModel * model, { model->move_fav = active; }, true); +} + +static bool archive_is_dir_exists(FuriString* path) { + if(furi_string_equal(path, STORAGE_ANY_PATH_PREFIX)) { + return true; + } + bool state = false; + 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_is_dir(&file_info)) { + state = true; + } + } + furi_record_close(RECORD_STORAGE); + return state; +} + +void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { + furi_assert(browser); + ArchiveTabEnum tab = archive_get_tab(browser); + + browser->last_tab_switch_dir = key; + + if(key == InputKeyLeft) { + tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; + } else { + tab = (tab + 1) % ArchiveTabTotal; + } + + browser->is_root = true; + archive_set_tab(browser, tab); + + furi_string_set(browser->path, archive_get_default_path(tab)); + bool tab_empty = true; + if(tab == ArchiveTabFavorites) { + if(archive_favorites_count(browser) > 0) { + tab_empty = false; + } + } else if(furi_string_start_with_str(browser->path, "/app:")) { + char* app_name = strchr(furi_string_get_cstr(browser->path), ':'); + if(app_name != NULL) { + if(archive_app_is_available(browser, furi_string_get_cstr(browser->path))) { + tab_empty = false; + } + } + } else { + tab = archive_get_tab(browser); + if(archive_is_dir_exists(browser->path)) { + bool skip_assets = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true; + // Hide dot files everywhere except Browser + bool hide_dot_files = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true; + archive_file_browser_set_path( + browser, browser->path, archive_get_tab_ext(tab), skip_assets, hide_dot_files); + tab_empty = false; // Empty check will be performed later + } + } + + if((tab_empty) && (tab != ArchiveTabBrowser)) { + archive_switch_tab(browser, key); + } else { + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + model->item_idx = 0; + model->array_offset = 0; + }, + false); + archive_get_items(browser, furi_string_get_cstr(browser->path)); + archive_update_offset(browser); + } +} + +void archive_enter_dir(ArchiveBrowserView* browser, FuriString* path) { + furi_assert(browser); + furi_assert(path); + + int32_t idx_temp = 0; + + with_view_model( + 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); +} + +void archive_refresh_dir(ArchiveBrowserView* browser) { + furi_assert(browser); + + int32_t idx_temp = 0; + + with_view_model( + browser->view, ArchiveBrowserViewModel * model, { idx_temp = model->item_idx; }, false); + file_browser_worker_folder_refresh(browser->worker, idx_temp); +} diff --git a/applications/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h similarity index 89% rename from applications/archive/helpers/archive_browser.h rename to applications/main/archive/helpers/archive_browser.h index d6c79817a27..5e66a3dbbcb 100644 --- a/applications/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -3,9 +3,9 @@ #include "../archive_i.h" #include -#define TAB_RIGHT InputKeyRight // Default tab swith direction +#define TAB_RIGHT InputKeyRight // Default tab switch direction #define TAB_DEFAULT ArchiveTabFavorites // Start tab -#define FILE_LIST_BUF_LEN 100 +#define FILE_LIST_BUF_LEN 50 static const char* tab_default_paths[] = { [ArchiveTabFavorites] = "/app:favorites", @@ -16,6 +16,7 @@ static const char* tab_default_paths[] = { [ArchiveTabInfrared] = ANY_PATH("infrared"), [ArchiveTabBadUsb] = ANY_PATH("badusb"), [ArchiveTabU2f] = "/app:u2f", + [ArchiveTabApplications] = ANY_PATH("apps"), [ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX, }; @@ -27,6 +28,7 @@ static const char* known_ext[] = { [ArchiveFileTypeInfrared] = ".ir", [ArchiveFileTypeBadUsb] = ".txt", [ArchiveFileTypeU2f] = "?", + [ArchiveFileTypeApplication] = ".fap", [ArchiveFileTypeUpdateManifest] = ".fuf", [ArchiveFileTypeFolder] = "?", [ArchiveFileTypeUnknown] = "*", @@ -41,6 +43,7 @@ static const ArchiveFileTypeEnum known_type[] = { [ArchiveTabInfrared] = ArchiveFileTypeInfrared, [ArchiveTabBadUsb] = ArchiveFileTypeBadUsb, [ArchiveTabU2f] = ArchiveFileTypeU2f, + [ArchiveTabApplications] = ArchiveFileTypeApplication, [ArchiveTabBrowser] = ArchiveFileTypeUnknown, }; @@ -61,6 +64,7 @@ inline bool archive_is_known_app(ArchiveFileTypeEnum type) { } bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx); +bool archive_is_file_list_load_required(ArchiveBrowserViewModel* model); void archive_update_offset(ArchiveBrowserView* browser); void archive_update_focus(ArchiveBrowserView* browser, const char* target); @@ -84,7 +88,6 @@ void archive_show_file_menu(ArchiveBrowserView* browser, bool show); void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active); void archive_switch_tab(ArchiveBrowserView* browser, InputKey key); -void archive_enter_dir(ArchiveBrowserView* browser, string_t name); +void archive_enter_dir(ArchiveBrowserView* browser, FuriString* name); void archive_leave_dir(ArchiveBrowserView* browser); void archive_refresh_dir(ArchiveBrowserView* browser); -void archive_file_browser_set_callbacks(ArchiveBrowserView* browser); diff --git a/applications/main/archive/helpers/archive_favorites.c b/applications/main/archive/helpers/archive_favorites.c new file mode 100644 index 00000000000..682aa6b3830 --- /dev/null +++ b/applications/main/archive/helpers/archive_favorites.c @@ -0,0 +1,339 @@ + +#include "archive_favorites.h" +#include "archive_files.h" +#include "archive_apps.h" +#include "archive_browser.h" + +#define ARCHIVE_FAV_FILE_BUF_LEN 32 + +static bool archive_favorites_read_line(File* file, FuriString* str_result) { + furi_string_reset(str_result); + uint8_t buffer[ARCHIVE_FAV_FILE_BUF_LEN]; + bool result = false; + + do { + size_t read_count = storage_file_read(file, buffer, ARCHIVE_FAV_FILE_BUF_LEN); + if(storage_file_get_error(file) != FSE_OK) { + return false; + } + + for(size_t i = 0; i < read_count; i++) { + if(buffer[i] == '\n') { + uint32_t position = storage_file_tell(file); + if(storage_file_get_error(file) != FSE_OK) { + return false; + } + + position = position - read_count + i + 1; + + storage_file_seek(file, position, true); + if(storage_file_get_error(file) != FSE_OK) { + return false; + } + + result = true; + break; + } else { + furi_string_push_back(str_result, buffer[i]); + } + } + + if(result || read_count == 0) { + break; + } + } while(true); + + return result; +} + +uint16_t archive_favorites_count(void* context) { + furi_assert(context); + + Storage* fs_api = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(fs_api); + + FuriString* buffer; + buffer = furi_string_alloc(); + + bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); + uint16_t lines = 0; + + if(result) { + while(1) { + if(!archive_favorites_read_line(file, buffer)) { + break; + } + if(!furi_string_size(buffer)) { + continue; // Skip empty lines + } + ++lines; + } + } + + storage_file_close(file); + + furi_string_free(buffer); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + + return lines; +} + +static bool archive_favourites_rescan() { + FuriString* buffer; + buffer = furi_string_alloc(); + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); + if(result) { + while(1) { + if(!archive_favorites_read_line(file, buffer)) { + break; + } + if(!furi_string_size(buffer)) { + continue; + } + + if(furi_string_search(buffer, "/app:") == 0) { + if(archive_app_is_available(NULL, furi_string_get_cstr(buffer))) { + archive_file_append( + ARCHIVE_FAV_TEMP_PATH, "%s\n", furi_string_get_cstr(buffer)); + } + } else { + if(storage_file_exists(storage, furi_string_get_cstr(buffer))) { + archive_file_append( + ARCHIVE_FAV_TEMP_PATH, "%s\n", furi_string_get_cstr(buffer)); + } + } + } + } + + furi_string_free(buffer); + + storage_file_close(file); + storage_common_remove(storage, ARCHIVE_FAV_PATH); + storage_common_rename(storage, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + storage_common_remove(storage, ARCHIVE_FAV_TEMP_PATH); + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + + return result; +} + +bool archive_favorites_read(void* context) { + furi_assert(context); + + ArchiveBrowserView* browser = context; + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + FuriString* buffer; + FileInfo file_info; + buffer = furi_string_alloc(); + + bool need_refresh = false; + uint16_t file_count = 0; + + archive_file_array_rm_all(browser); + + bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); + + if(result) { + while(1) { + if(!archive_favorites_read_line(file, buffer)) { + break; + } + if(!furi_string_size(buffer)) { + continue; + } + + if(furi_string_search(buffer, "/app:") == 0) { + if(archive_app_is_available(browser, furi_string_get_cstr(buffer))) { + archive_add_app_item(browser, furi_string_get_cstr(buffer)); + file_count++; + } else { + need_refresh = true; + } + } else { + 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_is_dir(&file_info), furi_string_get_cstr(buffer)); + file_count++; + } else { + need_refresh = true; + } + } + + furi_string_reset(buffer); + } + } + storage_file_close(file); + furi_string_free(buffer); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + + archive_set_item_count(browser, file_count); + + if(need_refresh) { //-V547 + archive_favourites_rescan(); + } + + return result; +} + +bool archive_favorites_delete(const char* format, ...) { + FuriString* buffer; + FuriString* filename; + va_list args; + va_start(args, format); + filename = furi_string_alloc_vprintf(format, args); + va_end(args); + + buffer = furi_string_alloc(); + Storage* fs_api = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(fs_api); + + bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); + + if(result) { + while(1) { + if(!archive_favorites_read_line(file, buffer)) { + break; + } + if(!furi_string_size(buffer)) { + continue; + } + + if(furi_string_search(buffer, filename)) { + archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", furi_string_get_cstr(buffer)); + } + } + } + + furi_string_free(buffer); + furi_string_free(filename); + + storage_file_close(file); + storage_common_remove(fs_api, ARCHIVE_FAV_PATH); + storage_common_rename(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH); + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + + return result; +} + +bool archive_is_favorite(const char* format, ...) { + FuriString* buffer; + FuriString* filename; + va_list args; + va_start(args, format); + filename = furi_string_alloc_vprintf(format, args); + va_end(args); + + buffer = furi_string_alloc(); + Storage* fs_api = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(fs_api); + + bool found = false; + bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); + + if(result) { + while(1) { + if(!archive_favorites_read_line(file, buffer)) { + break; + } + if(!furi_string_size(buffer)) { + continue; + } + if(!furi_string_search(buffer, filename)) { + found = true; + break; + } + } + } + + storage_file_close(file); + furi_string_free(buffer); + furi_string_free(filename); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + + return found; +} + +bool archive_favorites_rename(const char* src, const char* dst) { + furi_assert(src); + furi_assert(dst); + + Storage* fs_api = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(fs_api); + + FuriString* path; + FuriString* buffer; + + buffer = furi_string_alloc(); + path = furi_string_alloc(); + + furi_string_printf(path, "%s", src); + bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); + + if(result) { + while(1) { + if(!archive_favorites_read_line(file, buffer)) { + break; + } + if(!furi_string_size(buffer)) { + continue; + } + + archive_file_append( + ARCHIVE_FAV_TEMP_PATH, + "%s\n", + furi_string_search(buffer, path) ? furi_string_get_cstr(buffer) : dst); + } + } + + furi_string_free(buffer); + furi_string_free(path); + + storage_file_close(file); + storage_common_remove(fs_api, ARCHIVE_FAV_PATH); + storage_common_rename(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH); + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + + return result; +} + +void archive_add_to_favorites(const char* file_path) { + furi_assert(file_path); + + archive_file_append(ARCHIVE_FAV_PATH, "%s\n", file_path); +} + +void archive_favorites_save(void* context) { + furi_assert(context); + + ArchiveBrowserView* browser = context; + Storage* fs_api = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(fs_api); + + for(size_t i = 0; i < archive_file_get_array_size(browser); i++) { + ArchiveFile_t* item = archive_get_file_at(browser, i); + archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", furi_string_get_cstr(item->path)); + } + + storage_common_remove(fs_api, ARCHIVE_FAV_PATH); + storage_common_rename(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + storage_common_remove(fs_api, ARCHIVE_FAV_TEMP_PATH); + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +} diff --git a/applications/main/archive/helpers/archive_favorites.h b/applications/main/archive/helpers/archive_favorites.h new file mode 100644 index 00000000000..db89433782a --- /dev/null +++ b/applications/main/archive/helpers/archive_favorites.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#define ARCHIVE_FAV_PATH ANY_PATH("favorites.txt") +#define ARCHIVE_FAV_TEMP_PATH ANY_PATH("favorites.tmp") + +uint16_t archive_favorites_count(void* context); +bool archive_favorites_read(void* context); +bool archive_favorites_delete(const char* format, ...) _ATTRIBUTE((__format__(__printf__, 1, 2))); +bool archive_is_favorite(const char* format, ...) _ATTRIBUTE((__format__(__printf__, 1, 2))); +bool archive_favorites_rename(const char* src, const char* dst); +void archive_add_to_favorites(const char* file_path); +void archive_favorites_save(void* context); diff --git a/applications/main/archive/helpers/archive_files.c b/applications/main/archive/helpers/archive_files.c new file mode 100644 index 00000000000..a8bd937c99b --- /dev/null +++ b/applications/main/archive/helpers/archive_files.c @@ -0,0 +1,111 @@ +#include "archive_files.h" +#include "archive_apps.h" +#include "archive_browser.h" + +#define TAG "Archive" + +#define ASSETS_DIR "assets" + +void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder, bool is_app) { + furi_assert(file); + + file->is_app = is_app; + if(is_app) { + file->type = archive_get_app_filetype(archive_get_app_type(path)); + } else { + for(size_t i = 0; i < COUNT_OF(known_ext); i++) { + if((known_ext[i][0] == '?') || (known_ext[i][0] == '*')) continue; + if(furi_string_search(file->path, known_ext[i], 0) != FURI_STRING_FAILURE) { + if(i == ArchiveFileTypeBadUsb) { + if(furi_string_search( + file->path, archive_get_default_path(ArchiveTabBadUsb)) == 0) { + file->type = i; + return; // *.txt file is a BadUSB script only if it is in BadUSB folder + } + } else { + file->type = i; + return; + } + } + } + + if(is_folder) { + file->type = ArchiveFileTypeFolder; + } else { + file->type = ArchiveFileTypeUnknown; + } + } +} + +bool archive_get_items(void* context, const char* path) { + furi_assert(context); + + bool res = false; + ArchiveBrowserView* browser = context; + + if(archive_get_tab(browser) == ArchiveTabFavorites) { + res = archive_favorites_read(browser); + } else if(strncmp(path, "/app:", 5) == 0) { + res = archive_app_read_dir(browser, path); + } + return res; +} + +void archive_file_append(const char* path, const char* format, ...) { + furi_assert(path); + + FuriString* string; + va_list args; + va_start(args, format); + string = furi_string_alloc_vprintf(format, args); + va_end(args); + + Storage* fs_api = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(fs_api); + + bool res = storage_file_open(file, path, FSAM_WRITE, FSOM_OPEN_APPEND); + + if(res) { + storage_file_write(file, furi_string_get_cstr(string), furi_string_size(string)); + } + + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +} + +void archive_delete_file(void* context, const char* format, ...) { + furi_assert(context); + + FuriString* filename; + va_list args; + va_start(args, format); + filename = furi_string_alloc_vprintf(format, args); + va_end(args); + + ArchiveBrowserView* browser = context; + Storage* fs_api = furi_record_open(RECORD_STORAGE); + + FileInfo fileinfo; + storage_common_stat(fs_api, furi_string_get_cstr(filename), &fileinfo); + + bool res = false; + + 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); + } + + furi_record_close(RECORD_STORAGE); + + if(archive_is_favorite("%s", furi_string_get_cstr(filename))) { + archive_favorites_delete("%s", furi_string_get_cstr(filename)); + } + + if(res) { + archive_file_array_rm_selected(browser); + } + + furi_string_free(filename); +} diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h new file mode 100644 index 00000000000..1822befa358 --- /dev/null +++ b/applications/main/archive/helpers/archive_files.h @@ -0,0 +1,92 @@ +#pragma once + +#include +#include +#include + +#define FAP_MANIFEST_MAX_ICON_SIZE 32 + +typedef enum { + ArchiveFileTypeIButton, + ArchiveFileTypeNFC, + ArchiveFileTypeSubGhz, + ArchiveFileTypeLFRFID, + ArchiveFileTypeInfrared, + ArchiveFileTypeBadUsb, + ArchiveFileTypeU2f, + ArchiveFileTypeUpdateManifest, + ArchiveFileTypeApplication, + ArchiveFileTypeFolder, + ArchiveFileTypeUnknown, + ArchiveFileTypeLoading, +} ArchiveFileTypeEnum; + +typedef struct { + FuriString* path; + ArchiveFileTypeEnum type; + uint8_t* custom_icon_data; + FuriString* custom_name; + bool fav; + bool is_app; +} ArchiveFile_t; + +static void ArchiveFile_t_init(ArchiveFile_t* obj) { + obj->path = furi_string_alloc(); + obj->type = ArchiveFileTypeUnknown; + obj->custom_icon_data = NULL; + obj->custom_name = furi_string_alloc(); + obj->fav = false; + obj->is_app = false; +} + +static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { + obj->path = furi_string_alloc_set(src->path); + obj->type = src->type; + if(src->custom_icon_data) { + obj->custom_icon_data = malloc(FAP_MANIFEST_MAX_ICON_SIZE); + memcpy(obj->custom_icon_data, src->custom_icon_data, FAP_MANIFEST_MAX_ICON_SIZE); + } else { + obj->custom_icon_data = NULL; + } + obj->custom_name = furi_string_alloc_set(src->custom_name); + obj->fav = src->fav; + obj->is_app = src->is_app; +} + +static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { + furi_string_set(obj->path, src->path); + obj->type = src->type; + if(src->custom_icon_data) { + obj->custom_icon_data = malloc(FAP_MANIFEST_MAX_ICON_SIZE); + memcpy(obj->custom_icon_data, src->custom_icon_data, FAP_MANIFEST_MAX_ICON_SIZE); + } else { + obj->custom_icon_data = NULL; + } + furi_string_set(obj->custom_name, src->custom_name); + obj->fav = src->fav; + obj->is_app = src->is_app; +} + +static void ArchiveFile_t_clear(ArchiveFile_t* obj) { + furi_string_free(obj->path); + if(obj->custom_icon_data) { + free(obj->custom_icon_data); + obj->custom_icon_data = NULL; + } + furi_string_free(obj->custom_name); +} + +ARRAY_DEF( + files_array, + ArchiveFile_t, + (INIT(API_2(ArchiveFile_t_init)), + SET(API_6(ArchiveFile_t_set)), + INIT_SET(API_6(ArchiveFile_t_init_set)), + CLEAR(API_2(ArchiveFile_t_clear)))) + +void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder, bool is_app); +bool archive_get_items(void* context, const char* path); +void archive_file_append(const char* path, const char* format, ...) + _ATTRIBUTE((__format__(__printf__, 2, 3))); +void archive_delete_file(void* context, const char* format, ...) + _ATTRIBUTE((__format__(__printf__, 2, 3))); diff --git a/applications/archive/scenes/archive_scene.c b/applications/main/archive/scenes/archive_scene.c similarity index 100% rename from applications/archive/scenes/archive_scene.c rename to applications/main/archive/scenes/archive_scene.c diff --git a/applications/archive/scenes/archive_scene.h b/applications/main/archive/scenes/archive_scene.h similarity index 100% rename from applications/archive/scenes/archive_scene.h rename to applications/main/archive/scenes/archive_scene.h diff --git a/applications/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c similarity index 81% rename from applications/archive/scenes/archive_scene_browser.c rename to applications/main/archive/scenes/archive_scene_browser.c index e22ac792fcb..370830a0018 100644 --- a/applications/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -5,22 +5,35 @@ #include "../helpers/archive_browser.h" #include "../views/archive_browser_view.h" #include "archive/scenes/archive_scene.h" +#include #define TAG "ArchiveSceneBrowser" #define SCENE_STATE_DEFAULT (0) #define SCENE_STATE_NEED_REFRESH (1) -static const char* flipper_app_name[] = { - [ArchiveFileTypeIButton] = "iButton", - [ArchiveFileTypeNFC] = "NFC", - [ArchiveFileTypeSubGhz] = "Sub-GHz", - [ArchiveFileTypeLFRFID] = "125 kHz RFID", - [ArchiveFileTypeInfrared] = "Infrared", - [ArchiveFileTypeBadUsb] = "Bad USB", - [ArchiveFileTypeU2f] = "U2F", - [ArchiveFileTypeUpdateManifest] = "UpdaterApp", -}; +static const char* archive_get_flipper_app_name(ArchiveFileTypeEnum file_type) { + switch(file_type) { + case ArchiveFileTypeIButton: + return "iButton"; + case ArchiveFileTypeNFC: + return "NFC"; + case ArchiveFileTypeSubGhz: + return "Sub-GHz"; + case ArchiveFileTypeLFRFID: + return "125 kHz RFID"; + case ArchiveFileTypeInfrared: + return "Infrared"; + case ArchiveFileTypeBadUsb: + return "Bad USB"; + case ArchiveFileTypeU2f: + return "U2F"; + case ArchiveFileTypeUpdateManifest: + return "UpdaterApp"; + default: + return NULL; + } +} static void archive_loader_callback(const void* message, void* context) { furi_assert(message); @@ -38,20 +51,20 @@ static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selec UNUSED(browser); Loader* loader = furi_record_open(RECORD_LOADER); - LoaderStatus status; - if(selected->is_app) { - char* param = strrchr(string_get_cstr(selected->path), '/'); - if(param != NULL) { - param++; + const char* app_name = archive_get_flipper_app_name(selected->type); + + if(app_name) { + if(selected->is_app) { + char* param = strrchr(furi_string_get_cstr(selected->path), '/'); + if(param != NULL) { + param++; + } + loader_start_with_gui_error(loader, app_name, param); + } else { + loader_start_with_gui_error(loader, app_name, furi_string_get_cstr(selected->path)); } - status = loader_start(loader, flipper_app_name[selected->type], param); } else { - status = loader_start( - loader, flipper_app_name[selected->type], string_get_cstr(selected->path)); - } - - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); + loader_start_with_gui_error(loader, furi_string_get_cstr(selected->path), NULL); } furi_record_close(RECORD_LOADER); @@ -115,7 +128,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { case ArchiveBrowserEventFileMenuPin: { const char* name = archive_get_name(browser); if(favorites) { - archive_favorites_delete(name); + archive_favorites_delete("%s", name); archive_file_array_rm_selected(browser); archive_show_file_menu(browser, false); } else if(archive_is_known_app(selected->type)) { @@ -132,7 +145,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { case ArchiveBrowserEventFileMenuRename: if(favorites) { browser->callback(ArchiveBrowserEventEnterFavMove, browser->context); - } else if((archive_is_known_app(selected->type)) && (selected->is_app == false)) { + } else if(selected->is_app == false) { archive_show_file_menu(browser, false); scene_manager_set_scene_state( archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH); @@ -142,6 +155,8 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { break; case ArchiveBrowserEventFileMenuDelete: if(archive_get_tab(browser) != ArchiveTabFavorites) { + scene_manager_set_scene_state( + archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH); scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneDelete); } consumed = true; @@ -159,13 +174,13 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case ArchiveBrowserEventEnterFavMove: - string_set(archive->fav_move_str, selected->path); + furi_string_set(archive->fav_move_str, selected->path); archive_show_file_menu(browser, false); archive_favorites_move_mode(archive->browser, true); consumed = true; break; case ArchiveBrowserEventExitFavMove: - archive_update_focus(browser, string_get_cstr(archive->fav_move_str)); + archive_update_focus(browser, furi_string_get_cstr(archive->fav_move_str)); archive_favorites_move_mode(archive->browser, false); consumed = true; break; diff --git a/applications/archive/scenes/archive_scene_config.h b/applications/main/archive/scenes/archive_scene_config.h similarity index 100% rename from applications/archive/scenes/archive_scene_config.h rename to applications/main/archive/scenes/archive_scene_config.h diff --git a/applications/archive/scenes/archive_scene_delete.c b/applications/main/archive/scenes/archive_scene_delete.c similarity index 94% rename from applications/archive/scenes/archive_scene_delete.c rename to applications/main/archive/scenes/archive_scene_delete.c index d882fd066e0..d3964d0febf 100644 --- a/applications/archive/scenes/archive_scene_delete.c +++ b/applications/main/archive/scenes/archive_scene_delete.c @@ -4,7 +4,6 @@ #include "../helpers/archive_apps.h" #include "../helpers/archive_browser.h" #include "toolbox/path.h" -#include "m-string.h" #define SCENE_DELETE_CUSTOM_EVENT (0UL) #define MAX_TEXT_INPUT_LEN 22 @@ -26,18 +25,18 @@ void archive_scene_delete_on_enter(void* context) { widget_add_button_element( app->widget, GuiButtonTypeRight, "Delete", archive_scene_delete_widget_callback, app); - string_t filename; - string_init(filename); + FuriString* filename; + filename = furi_string_alloc(); ArchiveFile_t* current = archive_get_current_file(app->browser); path_extract_filename(current->path, filename, false); char delete_str[64]; - snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", string_get_cstr(filename)); + snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", furi_string_get_cstr(filename)); widget_add_text_box_element( app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str, false); - string_clear(filename); + furi_string_free(filename); view_dispatcher_switch_to_view(app->view_dispatcher, ArchiveViewWidget); } diff --git a/applications/archive/scenes/archive_scene_rename.c b/applications/main/archive/scenes/archive_scene_rename.c similarity index 79% rename from applications/archive/scenes/archive_scene_rename.c rename to applications/main/archive/scenes/archive_scene_rename.c index 293fa89affd..37f860a9a58 100644 --- a/applications/archive/scenes/archive_scene_rename.c +++ b/applications/main/archive/scenes/archive_scene_rename.c @@ -19,10 +19,10 @@ void archive_scene_rename_on_enter(void* context) { TextInput* text_input = archive->text_input; ArchiveFile_t* current = archive_get_current_file(archive->browser); - string_t filename; - string_init(filename); + FuriString* filename; + filename = furi_string_alloc(); path_extract_filename(current->path, filename, true); - strlcpy(archive->text_store, string_get_cstr(filename), MAX_NAME_LEN); + strlcpy(archive->text_store, furi_string_get_cstr(filename), MAX_NAME_LEN); path_extract_extension(current->path, archive->file_extension, MAX_EXT_LEN); @@ -37,10 +37,10 @@ void archive_scene_rename_on_enter(void* context) { false); ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - string_get_cstr(archive->browser->path), archive->file_extension, ""); + furi_string_get_cstr(archive->browser->path), archive->file_extension, ""); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); - string_clear(filename); + furi_string_free(filename); view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput); } @@ -56,19 +56,21 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { const char* path_src = archive_get_name(archive->browser); ArchiveFile_t* file = archive_get_current_file(archive->browser); - string_t path_dst; - string_init(path_dst); + FuriString* path_dst; + + path_dst = furi_string_alloc(); path_extract_dirname(path_src, path_dst); - string_cat_printf(path_dst, "/%s%s", archive->text_store, known_ext[file->type]); + furi_string_cat_printf( + path_dst, "/%s%s", archive->text_store, archive->file_extension); - storage_common_rename(fs_api, path_src, string_get_cstr(path_dst)); + storage_common_rename(fs_api, path_src, furi_string_get_cstr(path_dst)); furi_record_close(RECORD_STORAGE); if(file->fav) { - archive_favorites_rename(path_src, string_get_cstr(path_dst)); + archive_favorites_rename(path_src, furi_string_get_cstr(path_dst)); } - string_clear(path_dst); + furi_string_free(path_dst); scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneBrowser); consumed = true; diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c new file mode 100644 index 00000000000..ba147f74c8c --- /dev/null +++ b/applications/main/archive/views/archive_browser_view.c @@ -0,0 +1,482 @@ +#include "assets_icons.h" +#include "toolbox/path.h" +#include +#include "../archive_i.h" +#include "archive_browser_view.h" +#include "../helpers/archive_browser.h" + +#define SCROLL_INTERVAL (333) +#define SCROLL_DELAY (2) + +static const char* ArchiveTabNames[] = { + [ArchiveTabFavorites] = "Favorites", + [ArchiveTabIButton] = "iButton", + [ArchiveTabNFC] = "NFC", + [ArchiveTabSubGhz] = "Sub-GHz", + [ArchiveTabLFRFID] = "RFID LF", + [ArchiveTabInfrared] = "Infrared", + [ArchiveTabBadUsb] = "Bad USB", + [ArchiveTabU2f] = "U2F", + [ArchiveTabApplications] = "Apps", + [ArchiveTabBrowser] = "Browser", +}; + +static const Icon* ArchiveItemIcons[] = { + [ArchiveFileTypeIButton] = &I_ibutt_10px, + [ArchiveFileTypeNFC] = &I_Nfc_10px, + [ArchiveFileTypeSubGhz] = &I_sub1_10px, + [ArchiveFileTypeLFRFID] = &I_125_10px, + [ArchiveFileTypeInfrared] = &I_ir_10px, + [ArchiveFileTypeBadUsb] = &I_badusb_10px, + [ArchiveFileTypeU2f] = &I_u2f_10px, + [ArchiveFileTypeUpdateManifest] = &I_update_10px, + [ArchiveFileTypeFolder] = &I_dir_10px, + [ArchiveFileTypeUnknown] = &I_unknown_10px, + [ArchiveFileTypeLoading] = &I_loading_10px, + [ArchiveFileTypeApplication] = &I_unknown_10px, +}; + +void archive_browser_set_callback( + ArchiveBrowserView* browser, + ArchiveBrowserViewCallback callback, + void* context) { + furi_assert(browser); + furi_assert(callback); + browser->callback = callback; + browser->context = context; +} + +static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 71, 17, 57, 46); + canvas_set_color(canvas, ColorBlack); + elements_slightly_rounded_frame(canvas, 70, 16, 58, 48); + + FuriString* menu[MENU_ITEMS]; + + menu[0] = furi_string_alloc_set("Run in app"); + menu[1] = furi_string_alloc_set("Pin"); + menu[2] = furi_string_alloc_set("Rename"); + menu[3] = furi_string_alloc_set("Delete"); + + ArchiveFile_t* selected = files_array_get(model->files, model->item_idx - model->array_offset); + + if((selected->fav) || (model->tab_idx == ArchiveTabFavorites)) { + furi_string_set(menu[1], "Unpin"); + } + + if(!archive_is_known_app(selected->type)) { + furi_string_set(menu[0], "---"); + furi_string_set(menu[1], "---"); + } else { + if(model->tab_idx == ArchiveTabFavorites) { + furi_string_set(menu[2], "Move"); + furi_string_set(menu[3], "---"); + } else if(selected->is_app) { + furi_string_set(menu[2], "---"); + } + } + + for(size_t i = 0; i < MENU_ITEMS; i++) { + canvas_draw_str(canvas, 82, 27 + i * 11, furi_string_get_cstr(menu[i])); + furi_string_free(menu[i]); + } + + canvas_draw_icon(canvas, 74, 20 + model->menu_idx * 11, &I_ButtonRight_4x7); +} + +static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar, bool moving) { + uint8_t x_offset = moving ? MOVE_OFFSET : 0; + + canvas_set_color(canvas, ColorBlack); + canvas_draw_box( + canvas, + 0 + x_offset, + 15 + idx * FRAME_HEIGHT, + (scrollbar ? 122 : 127) - x_offset, + FRAME_HEIGHT); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, 0 + x_offset, 15 + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, 1 + x_offset, 15 + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, 0 + x_offset, (15 + idx * FRAME_HEIGHT) + 1); + + canvas_draw_dot(canvas, 0 + x_offset, (15 + idx * FRAME_HEIGHT) + 11); + canvas_draw_dot(canvas, scrollbar ? 121 : 126, 15 + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, scrollbar ? 121 : 126, (15 + idx * FRAME_HEIGHT) + 11); +} + +static void archive_draw_loading(Canvas* canvas, ArchiveBrowserViewModel* model) { + furi_assert(model); + + uint8_t x = 128 / 2 - 24 / 2; + uint8_t y = 64 / 2 - 24 / 2; + + canvas_draw_icon(canvas, x, y, &A_Loading_24); +} + +static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) { + furi_assert(model); + + size_t array_size = files_array_size(model->files); + bool scrollbar = model->item_cnt > 4; + + for(uint32_t i = 0; i < MIN(model->item_cnt, MENU_ITEMS); ++i) { + FuriString* str_buf; + str_buf = furi_string_alloc(); + int32_t idx = CLAMP((uint32_t)(i + model->list_offset), model->item_cnt, 0u); + uint8_t x_offset = (model->move_fav && model->item_idx == idx) ? MOVE_OFFSET : 0; + + ArchiveFileTypeEnum file_type = ArchiveFileTypeLoading; + uint8_t* custom_icon_data = NULL; + + if(archive_is_item_in_array(model, idx)) { + ArchiveFile_t* file = files_array_get( + model->files, CLAMP(idx - model->array_offset, (int32_t)(array_size - 1), 0)); + file_type = file->type; + if(file_type == ArchiveFileTypeApplication) { + if(file->custom_icon_data) { + custom_icon_data = file->custom_icon_data; + furi_string_set(str_buf, file->custom_name); + } else { + file_type = ArchiveFileTypeUnknown; + path_extract_filename(file->path, str_buf, archive_is_known_app(file->type)); + } + } else { + path_extract_filename(file->path, str_buf, archive_is_known_app(file->type)); + } + } else { + furi_string_set(str_buf, "---"); + } + + size_t scroll_counter = model->scroll_counter; + + if(model->item_idx == idx) { + archive_draw_frame(canvas, i, scrollbar, model->move_fav); + if(scroll_counter < SCROLL_DELAY) { + scroll_counter = 0; + } else { + scroll_counter -= SCROLL_DELAY; + } + } else { + canvas_set_color(canvas, ColorBlack); + scroll_counter = 0; + } + + if(custom_icon_data) { + canvas_draw_bitmap( + canvas, 2 + x_offset, 16 + i * FRAME_HEIGHT, 11, 10, custom_icon_data); + } else { + canvas_draw_icon( + canvas, 2 + x_offset, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file_type]); + } + + elements_scrollable_text_line( + canvas, + 15 + x_offset, + 24 + i * FRAME_HEIGHT, + ((scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX) - x_offset), + str_buf, + scroll_counter, + (model->item_idx != idx)); + + furi_string_free(str_buf); + } + + if(scrollbar) { + elements_scrollbar_pos(canvas, 126, 15, 49, model->item_idx, model->item_cnt); + } + + if(model->menu) { + render_item_menu(canvas, model); + } +} + +static void archive_render_status_bar(Canvas* canvas, ArchiveBrowserViewModel* model) { + furi_assert(model); + + const char* tab_name = ArchiveTabNames[model->tab_idx]; + + canvas_draw_icon(canvas, 0, 0, &I_Background_128x11); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 0, 0, 50, 13); + canvas_draw_box(canvas, 107, 0, 20, 13); + + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe(canvas, 0, 0, 51, 13, 1); // frame + canvas_draw_line(canvas, 49, 1, 49, 11); // shadow right + canvas_draw_line(canvas, 1, 11, 49, 11); // shadow bottom + canvas_draw_str_aligned(canvas, 25, 9, AlignCenter, AlignBottom, tab_name); + + canvas_draw_rframe(canvas, 107, 0, 21, 13, 1); + canvas_draw_line(canvas, 126, 1, 126, 11); + canvas_draw_line(canvas, 108, 11, 126, 11); + + if(model->move_fav) { + canvas_draw_icon(canvas, 110, 4, &I_ButtonUp_7x4); + canvas_draw_icon(canvas, 117, 4, &I_ButtonDown_7x4); + } else { + canvas_draw_icon(canvas, 111, 2, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 119, 2, &I_ButtonRight_4x7); + } + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, 50, 0); + canvas_draw_dot(canvas, 127, 0); + + canvas_set_color(canvas, ColorBlack); +} + +static void archive_view_render(Canvas* canvas, void* mdl) { + ArchiveBrowserViewModel* model = mdl; + + archive_render_status_bar(canvas, mdl); + + if(model->folder_loading) { + archive_draw_loading(canvas, model); + } else if(model->item_cnt > 0) { + draw_list(canvas, model); + } else { + canvas_draw_str_aligned( + canvas, GUI_DISPLAY_WIDTH / 2, 40, AlignCenter, AlignCenter, "Empty"); + } +} + +View* archive_browser_get_view(ArchiveBrowserView* browser) { + furi_assert(browser); + return browser->view; +} + +static void file_list_rollover(ArchiveBrowserViewModel* model) { + if(!model->list_loading && files_array_size(model->files) < model->item_cnt) { + files_array_reset(model->files); + } +} + +static bool archive_view_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + + ArchiveBrowserView* browser = context; + + bool in_menu; + bool move_fav_mode; + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + in_menu = model->menu; + move_fav_mode = model->move_fav; + }, + false); + + if(in_menu) { + if(event->type == InputTypeShort) { + if(event->key == InputKeyUp || event->key == InputKeyDown) { + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + if(event->key == InputKeyUp) { + model->menu_idx = ((model->menu_idx - 1) + MENU_ITEMS) % MENU_ITEMS; + } else if(event->key == InputKeyDown) { + model->menu_idx = (model->menu_idx + 1) % MENU_ITEMS; + } + }, + true); + } + + if(event->key == InputKeyOk) { + uint8_t idx; + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { idx = model->menu_idx; }, + false); + browser->callback(file_menu_actions[idx], browser->context); + } else if(event->key == InputKeyBack) { + browser->callback(ArchiveBrowserEventFileMenuClose, browser->context); + } + } + + } else { + if(event->type == InputTypeShort) { + if(event->key == InputKeyLeft || event->key == InputKeyRight) { + if(move_fav_mode) return false; + archive_switch_tab(browser, event->key); + } else if(event->key == InputKeyBack) { + if(move_fav_mode) { + browser->callback(ArchiveBrowserEventExitFavMove, browser->context); + } else { + browser->callback(ArchiveBrowserEventExit, browser->context); + } + } + } + + if((event->key == InputKeyUp || event->key == InputKeyDown) && + (event->type == InputTypeShort || event->type == InputTypeRepeat)) { + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + int32_t scroll_speed = 1; + if(model->button_held_for_ticks > 5) { + if(model->button_held_for_ticks % 2) { + scroll_speed = 0; + } else { + scroll_speed = model->button_held_for_ticks > 9 ? 4 : 2; + } + } + + if(event->key == InputKeyUp) { + if(model->item_idx < scroll_speed) { + model->button_held_for_ticks = 0; + model->item_idx = model->item_cnt - 1; + file_list_rollover(model); + } else { + model->item_idx = + ((model->item_idx - scroll_speed) + model->item_cnt) % + model->item_cnt; + } + if(archive_is_file_list_load_required(model)) { + model->list_loading = true; + browser->callback(ArchiveBrowserEventLoadPrevItems, browser->context); + } + if(move_fav_mode) { + browser->callback(ArchiveBrowserEventFavMoveUp, browser->context); + } + model->scroll_counter = 0; + model->button_held_for_ticks += 1; + } else if(event->key == InputKeyDown) { + int32_t count = model->item_cnt; + if(model->item_idx + scroll_speed >= count) { + model->button_held_for_ticks = 0; + model->item_idx = 0; + file_list_rollover(model); + } else { + model->item_idx = (model->item_idx + scroll_speed) % model->item_cnt; + } + if(archive_is_file_list_load_required(model)) { + model->list_loading = true; + browser->callback(ArchiveBrowserEventLoadNextItems, browser->context); + } + if(move_fav_mode) { + browser->callback(ArchiveBrowserEventFavMoveDown, browser->context); + } + model->scroll_counter = 0; + model->button_held_for_ticks += 1; + } + }, + true); + archive_update_offset(browser); + } + + if(event->key == InputKeyOk) { + ArchiveFile_t* selected = archive_get_current_file(browser); + + if(selected) { + bool favorites = archive_get_tab(browser) == ArchiveTabFavorites; + bool folder = selected->type == ArchiveFileTypeFolder; + + if(event->type == InputTypeShort) { + if(favorites) { + if(move_fav_mode) { + browser->callback(ArchiveBrowserEventSaveFavMove, browser->context); + } else { + browser->callback(ArchiveBrowserEventFileMenuRun, browser->context); + } + } else if(folder) { + browser->callback(ArchiveBrowserEventEnterDir, browser->context); + } else { + browser->callback(ArchiveBrowserEventFileMenuOpen, browser->context); + } + } else if(event->type == InputTypeLong) { + if(move_fav_mode) { + browser->callback(ArchiveBrowserEventSaveFavMove, browser->context); + } else if(folder || favorites) { + browser->callback(ArchiveBrowserEventFileMenuOpen, browser->context); + } + } + } + } + } + + if(event->type == InputTypeRelease) { + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { model->button_held_for_ticks = 0; }, + true); + } + + return true; +} + +static void browser_scroll_timer(void* context) { + furi_assert(context); + ArchiveBrowserView* browser = context; + with_view_model( + browser->view, ArchiveBrowserViewModel * model, { model->scroll_counter++; }, true); +} + +static void browser_view_enter(void* context) { + furi_assert(context); + ArchiveBrowserView* browser = context; + with_view_model( + browser->view, ArchiveBrowserViewModel * model, { model->scroll_counter = 0; }, true); + furi_timer_start(browser->scroll_timer, SCROLL_INTERVAL); +} + +static void browser_view_exit(void* context) { + furi_assert(context); + ArchiveBrowserView* browser = context; + furi_timer_stop(browser->scroll_timer); +} + +ArchiveBrowserView* browser_alloc() { + ArchiveBrowserView* browser = malloc(sizeof(ArchiveBrowserView)); + browser->view = view_alloc(); + view_allocate_model(browser->view, ViewModelTypeLocking, sizeof(ArchiveBrowserViewModel)); + view_set_context(browser->view, browser); + view_set_draw_callback(browser->view, archive_view_render); + view_set_input_callback(browser->view, archive_view_input); + view_set_enter_callback(browser->view, browser_view_enter); + view_set_exit_callback(browser->view, browser_view_exit); + + browser->scroll_timer = furi_timer_alloc(browser_scroll_timer, FuriTimerTypePeriodic, browser); + + browser->path = furi_string_alloc_set(archive_get_default_path(TAB_DEFAULT)); + + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + files_array_init(model->files); + model->tab_idx = TAB_DEFAULT; + }, + true); + + return browser; +} + +void browser_free(ArchiveBrowserView* browser) { + furi_assert(browser); + + furi_timer_free(browser->scroll_timer); + + if(browser->worker_running) { + file_browser_worker_free(browser->worker); + } + + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { files_array_clear(model->files); }, + false); + + furi_string_free(browser->path); + + view_free(browser->view); + free(browser); +} diff --git a/applications/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h similarity index 92% rename from applications/archive/views/archive_browser_view.h rename to applications/main/archive/views/archive_browser_view.h index 2de04a166c0..25490aedd3e 100644 --- a/applications/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -1,14 +1,15 @@ #pragma once +#include "../helpers/archive_files.h" +#include "../helpers/archive_favorites.h" + #include #include #include #include -#include +#include #include -#include "../helpers/archive_files.h" -#include "../helpers/archive_favorites.h" -#include "gui/modules/file_browser_worker.h" +#include #define MAX_LEN_PX 110 #define MAX_NAME_LEN 255 @@ -26,6 +27,7 @@ typedef enum { ArchiveTabIButton, ArchiveTabBadUsb, ArchiveTabU2f, + ArchiveTabApplications, ArchiveTabBrowser, ArchiveTabTotal, } ArchiveTabEnum; @@ -74,11 +76,13 @@ typedef enum { struct ArchiveBrowserView { View* view; BrowserWorker* worker; + bool worker_running; ArchiveBrowserViewCallback callback; void* context; - string_t path; + FuriString* path; InputKey last_tab_switch_dir; bool is_root; + FuriTimer* scroll_timer; }; typedef struct { @@ -95,6 +99,9 @@ typedef struct { int32_t item_idx; int32_t array_offset; int32_t list_offset; + size_t scroll_counter; + + uint32_t button_held_for_ticks; } ArchiveBrowserViewModel; void archive_browser_set_callback( diff --git a/applications/main/bad_usb/application.fam b/applications/main/bad_usb/application.fam new file mode 100644 index 00000000000..9844e248df9 --- /dev/null +++ b/applications/main/bad_usb/application.fam @@ -0,0 +1,13 @@ +App( + appid="bad_usb", + name="Bad USB", + apptype=FlipperAppType.MENUEXTERNAL, + entry_point="bad_usb_app", + stack_size=2 * 1024, + icon="A_BadUsb_14", + order=70, + resources="resources", + fap_libs=["assets"], + fap_icon="icon.png", + fap_category="USB", +) diff --git a/applications/main/bad_usb/bad_usb_app.c b/applications/main/bad_usb/bad_usb_app.c new file mode 100644 index 00000000000..ea97c448782 --- /dev/null +++ b/applications/main/bad_usb/bad_usb_app.c @@ -0,0 +1,185 @@ +#include "bad_usb_app_i.h" +#include "bad_usb_settings_filename.h" +#include +#include +#include +#include + +#define BAD_USB_SETTINGS_PATH BAD_USB_APP_BASE_FOLDER "/" BAD_USB_SETTINGS_FILE_NAME + +static bool bad_usb_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + BadUsbApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool bad_usb_app_back_event_callback(void* context) { + furi_assert(context); + BadUsbApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void bad_usb_app_tick_event_callback(void* context) { + furi_assert(context); + BadUsbApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +static void bad_usb_load_settings(BadUsbApp* app) { + File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + if(storage_file_open(settings_file, BAD_USB_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + char chr; + while((storage_file_read(settings_file, &chr, 1) == 1) && + !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_usb_save_settings(BadUsbApp* app) { + File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + if(storage_file_open(settings_file, BAD_USB_SETTINGS_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) { + storage_file_write( + settings_file, + furi_string_get_cstr(app->keyboard_layout), + furi_string_size(app->keyboard_layout)); + storage_file_write(settings_file, "\n", 1); + } + storage_file_close(settings_file); + storage_file_free(settings_file); +} + +BadUsbApp* bad_usb_app_alloc(char* arg) { + BadUsbApp* app = malloc(sizeof(BadUsbApp)); + + app->bad_usb_script = NULL; + + app->file_path = furi_string_alloc(); + app->keyboard_layout = furi_string_alloc(); + if(arg && strlen(arg)) { + furi_string_set(app->file_path, arg); + } + + bad_usb_load_settings(app); + + app->gui = furi_record_open(RECORD_GUI); + app->notifications = furi_record_open(RECORD_NOTIFICATION); + app->dialogs = furi_record_open(RECORD_DIALOGS); + + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + + app->scene_manager = scene_manager_alloc(&bad_usb_scene_handlers, app); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, bad_usb_app_tick_event_callback, 500); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, bad_usb_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, bad_usb_app_back_event_callback); + + // Custom Widget + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadUsbAppViewError, widget_get_view(app->widget)); + + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadUsbAppViewConfig, submenu_get_view(app->submenu)); + + app->bad_usb_view = bad_usb_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadUsbAppViewWork, bad_usb_get_view(app->bad_usb_view)); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + if(furi_hal_usb_is_locked()) { + app->error = BadUsbAppErrorCloseRpc; + app->usb_if_prev = NULL; + scene_manager_next_scene(app->scene_manager, BadUsbSceneError); + } else { + app->usb_if_prev = furi_hal_usb_get_config(); + furi_check(furi_hal_usb_set_config(NULL, NULL)); + + if(!furi_string_empty(app->file_path)) { + app->bad_usb_script = bad_usb_script_open(app->file_path); + bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout); + scene_manager_next_scene(app->scene_manager, BadUsbSceneWork); + } else { + furi_string_set(app->file_path, BAD_USB_APP_BASE_FOLDER); + scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect); + } + } + + return app; +} + +void bad_usb_app_free(BadUsbApp* app) { + furi_assert(app); + + if(app->bad_usb_script) { + bad_usb_script_close(app->bad_usb_script); + app->bad_usb_script = NULL; + } + + // Views + view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork); + bad_usb_free(app->bad_usb_view); + + // Custom Widget + view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewError); + widget_free(app->widget); + + // Submenu + view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfig); + submenu_free(app->submenu); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Close records + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_DIALOGS); + + bad_usb_save_settings(app); + + furi_string_free(app->file_path); + furi_string_free(app->keyboard_layout); + + if(app->usb_if_prev) { + furi_check(furi_hal_usb_set_config(app->usb_if_prev, NULL)); + } + + free(app); +} + +int32_t bad_usb_app(void* p) { + BadUsbApp* bad_usb_app = bad_usb_app_alloc((char*)p); + + view_dispatcher_run(bad_usb_app->view_dispatcher); + + bad_usb_app_free(bad_usb_app); + return 0; +} diff --git a/applications/bad_usb/bad_usb_app.h b/applications/main/bad_usb/bad_usb_app.h similarity index 100% rename from applications/bad_usb/bad_usb_app.h rename to applications/main/bad_usb/bad_usb_app.h diff --git a/applications/main/bad_usb/bad_usb_app_i.h b/applications/main/bad_usb/bad_usb_app_i.h new file mode 100644 index 00000000000..cf1c02ebc7f --- /dev/null +++ b/applications/main/bad_usb/bad_usb_app_i.h @@ -0,0 +1,51 @@ +#pragma once + +#include "bad_usb_app.h" +#include "scenes/bad_usb_scene.h" +#include "helpers/ducky_script.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "views/bad_usb_view.h" +#include + +#define BAD_USB_APP_BASE_FOLDER ANY_PATH("badusb") +#define BAD_USB_APP_PATH_LAYOUT_FOLDER BAD_USB_APP_BASE_FOLDER "/assets/layouts" +#define BAD_USB_APP_SCRIPT_EXTENSION ".txt" +#define BAD_USB_APP_LAYOUT_EXTENSION ".kl" + +typedef enum { + BadUsbAppErrorNoFiles, + BadUsbAppErrorCloseRpc, +} BadUsbAppError; + +struct BadUsbApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + NotificationApp* notifications; + DialogsApp* dialogs; + Widget* widget; + Submenu* submenu; + + BadUsbAppError error; + FuriString* file_path; + FuriString* keyboard_layout; + BadUsb* bad_usb_view; + BadUsbScript* bad_usb_script; + + FuriHalUsbInterface* usb_if_prev; +}; + +typedef enum { + BadUsbAppViewError, + BadUsbAppViewWork, + BadUsbAppViewConfig, +} BadUsbAppView; \ No newline at end of file diff --git a/applications/main/bad_usb/bad_usb_settings_filename.h b/applications/main/bad_usb/bad_usb_settings_filename.h new file mode 100644 index 00000000000..12ba8f31c4c --- /dev/null +++ b/applications/main/bad_usb/bad_usb_settings_filename.h @@ -0,0 +1,3 @@ +#pragma once + +#define BAD_USB_SETTINGS_FILE_NAME ".badusb.settings" diff --git a/applications/main/bad_usb/helpers/ducky_script.c b/applications/main/bad_usb/helpers/ducky_script.c new file mode 100644 index 00000000000..c4aa9106229 --- /dev/null +++ b/applications/main/bad_usb/helpers/ducky_script.c @@ -0,0 +1,708 @@ +#include +#include +#include +#include +#include +#include +#include +#include "ducky_script.h" +#include "ducky_script_i.h" +#include + +#define TAG "BadUsb" +#define WORKER_TAG TAG "Worker" + +#define BADUSB_ASCII_TO_KEY(script, x) \ + (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE) + +typedef enum { + WorkerEvtStartStop = (1 << 0), + WorkerEvtPauseResume = (1 << 1), + WorkerEvtEnd = (1 << 2), + WorkerEvtConnect = (1 << 3), + WorkerEvtDisconnect = (1 << 4), +} WorkerEvtFlags; + +static const char ducky_cmd_id[] = {"ID"}; + +static const uint8_t numpad_keys[10] = { + HID_KEYPAD_0, + HID_KEYPAD_1, + HID_KEYPAD_2, + HID_KEYPAD_3, + HID_KEYPAD_4, + HID_KEYPAD_5, + HID_KEYPAD_6, + HID_KEYPAD_7, + HID_KEYPAD_8, + HID_KEYPAD_9, +}; + +uint32_t ducky_get_command_len(const char* line) { + uint32_t len = strlen(line); + for(uint32_t i = 0; i < len; i++) { + if(line[i] == ' ') return i; + } + return 0; +} + +bool ducky_is_line_end(const char chr) { + return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n')); +} + +uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars) { + uint16_t keycode = ducky_get_keycode_by_name(param); + if(keycode != HID_KEYBOARD_NONE) { + return keycode; + } + + if((accept_chars) && (strlen(param) > 0)) { + return (BADUSB_ASCII_TO_KEY(bad_usb, param[0]) & 0xFF); + } + return 0; +} + +bool ducky_get_number(const char* param, uint32_t* val) { + uint32_t value = 0; + if(sscanf(param, "%lu", &value) == 1) { + *val = value; + return true; + } + return false; +} + +void ducky_numlock_on() { + if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) { + furi_hal_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK); + furi_hal_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK); + } +} +bool ducky_numpad_press(const char num) { + if((num < '0') || (num > '9')) return false; + + uint16_t key = numpad_keys[num - '0']; + furi_hal_hid_kb_press(key); + furi_hal_hid_kb_release(key); + + return true; +} + +bool ducky_altchar(const char* charcode) { + uint8_t i = 0; + bool state = false; + + furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT); + + while(!ducky_is_line_end(charcode[i])) { + state = ducky_numpad_press(charcode[i]); + if(state == false) break; + i++; + } + + furi_hal_hid_kb_release(KEY_MOD_LEFT_ALT); + return state; +} + +bool ducky_altstring(const char* param) { + uint32_t i = 0; + bool state = false; + + while(param[i] != '\0') { + if((param[i] < ' ') || (param[i] > '~')) { + i++; + continue; // Skip non-printable chars + } + + char temp_str[4]; + snprintf(temp_str, 4, "%u", param[i]); + + state = ducky_altchar(temp_str); + if(state == false) break; + i++; + } + return state; +} + +int32_t ducky_error(BadUsbScript* bad_usb, const char* text, ...) { + va_list args; + va_start(args, text); + + vsnprintf(bad_usb->st.error, sizeof(bad_usb->st.error), text, args); + + va_end(args); + return SCRIPT_STATE_ERROR; +} + +bool ducky_string(BadUsbScript* bad_usb, const char* param) { + uint32_t i = 0; + + while(param[i] != '\0') { + if(param[i] != '\n') { + uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_usb, param[i]); + if(keycode != HID_KEYBOARD_NONE) { + furi_hal_hid_kb_press(keycode); + furi_hal_hid_kb_release(keycode); + } + } else { + furi_hal_hid_kb_press(HID_KEYBOARD_RETURN); + furi_hal_hid_kb_release(HID_KEYBOARD_RETURN); + } + i++; + } + bad_usb->stringdelay = 0; + return true; +} + +static bool ducky_string_next(BadUsbScript* bad_usb) { + if(bad_usb->string_print_pos >= furi_string_size(bad_usb->string_print)) { + return true; + } + + char print_char = furi_string_get_char(bad_usb->string_print, bad_usb->string_print_pos); + + if(print_char != '\n') { + uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_usb, print_char); + if(keycode != HID_KEYBOARD_NONE) { + furi_hal_hid_kb_press(keycode); + furi_hal_hid_kb_release(keycode); + } + } else { + furi_hal_hid_kb_press(HID_KEYBOARD_RETURN); + furi_hal_hid_kb_release(HID_KEYBOARD_RETURN); + } + + bad_usb->string_print_pos++; + + return false; +} + +static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) { + uint32_t line_len = furi_string_size(line); + const char* line_tmp = furi_string_get_cstr(line); + + if(line_len == 0) { + return SCRIPT_STATE_NEXT_LINE; // Skip empty lines + } + FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp); + + // Ducky Lang Functions + int32_t cmd_result = ducky_execute_cmd(bad_usb, line_tmp); + if(cmd_result != SCRIPT_STATE_CMD_UNKNOWN) { + return cmd_result; + } + + // Special keys + modifiers + uint16_t key = ducky_get_keycode(bad_usb, line_tmp, false); + if(key == HID_KEYBOARD_NONE) { + return ducky_error(bad_usb, "No keycode defined for %s", line_tmp); + } + if((key & 0xFF00) != 0) { + // It's a modifier key + line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; + key |= ducky_get_keycode(bad_usb, line_tmp, true); + } + furi_hal_hid_kb_press(key); + furi_hal_hid_kb_release(key); + return 0; +} + +static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) { + if(sscanf(line, "%lX:%lX", &bad_usb->hid_cfg.vid, &bad_usb->hid_cfg.pid) == 2) { + bad_usb->hid_cfg.manuf[0] = '\0'; + bad_usb->hid_cfg.product[0] = '\0'; + + uint8_t id_len = ducky_get_command_len(line); + if(!ducky_is_line_end(line[id_len + 1])) { + sscanf( + &line[id_len + 1], + "%31[^\r\n:]:%31[^\r\n]", + bad_usb->hid_cfg.manuf, + bad_usb->hid_cfg.product); + } + FURI_LOG_D( + WORKER_TAG, + "set id: %04lX:%04lX mfr:%s product:%s", + bad_usb->hid_cfg.vid, + bad_usb->hid_cfg.pid, + bad_usb->hid_cfg.manuf, + bad_usb->hid_cfg.product); + return true; + } + return false; +} + +static bool ducky_script_preload(BadUsbScript* bad_usb, File* script_file) { + uint8_t ret = 0; + uint32_t line_len = 0; + + furi_string_reset(bad_usb->line); + + do { + ret = storage_file_read(script_file, bad_usb->file_buf, FILE_BUFFER_LEN); + for(uint16_t i = 0; i < ret; i++) { + if(bad_usb->file_buf[i] == '\n' && line_len > 0) { + bad_usb->st.line_nb++; + line_len = 0; + } else { + if(bad_usb->st.line_nb == 0) { // Save first line + furi_string_push_back(bad_usb->line, bad_usb->file_buf[i]); + } + line_len++; + } + } + if(storage_file_eof(script_file)) { + if(line_len > 0) { + bad_usb->st.line_nb++; + break; + } + } + } while(ret > 0); + + const char* line_tmp = furi_string_get_cstr(bad_usb->line); + bool id_set = false; // Looking for ID command at first line + if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) { + id_set = ducky_set_usb_id(bad_usb, &line_tmp[strlen(ducky_cmd_id) + 1]); + } + + if(id_set) { + furi_check(furi_hal_usb_set_config(&usb_hid, &bad_usb->hid_cfg)); + } else { + furi_check(furi_hal_usb_set_config(&usb_hid, NULL)); + } + + storage_file_seek(script_file, 0, true); + furi_string_reset(bad_usb->line); + + return true; +} + +static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_file) { + int32_t delay_val = 0; + + if(bad_usb->repeat_cnt > 0) { + bad_usb->repeat_cnt--; + delay_val = ducky_parse_line(bad_usb, bad_usb->line_prev); + if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line + return 0; + } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays + return delay_val; + } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button + return delay_val; + } else if(delay_val < 0) { // Script error + bad_usb->st.error_line = bad_usb->st.line_cur - 1; + FURI_LOG_E(WORKER_TAG, "Unknown command at line %zu", bad_usb->st.line_cur - 1U); + return SCRIPT_STATE_ERROR; + } else { + return (delay_val + bad_usb->defdelay); + } + } + + furi_string_set(bad_usb->line_prev, bad_usb->line); + furi_string_reset(bad_usb->line); + + while(1) { + if(bad_usb->buf_len == 0) { + bad_usb->buf_len = storage_file_read(script_file, bad_usb->file_buf, FILE_BUFFER_LEN); + if(storage_file_eof(script_file)) { + if((bad_usb->buf_len < FILE_BUFFER_LEN) && (bad_usb->file_end == false)) { + bad_usb->file_buf[bad_usb->buf_len] = '\n'; + bad_usb->buf_len++; + bad_usb->file_end = true; + } + } + + bad_usb->buf_start = 0; + if(bad_usb->buf_len == 0) return SCRIPT_STATE_END; + } + for(uint8_t i = bad_usb->buf_start; i < (bad_usb->buf_start + bad_usb->buf_len); i++) { + if(bad_usb->file_buf[i] == '\n' && furi_string_size(bad_usb->line) > 0) { + bad_usb->st.line_cur++; + bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1); + bad_usb->buf_start = i + 1; + furi_string_trim(bad_usb->line); + delay_val = ducky_parse_line(bad_usb, bad_usb->line); + if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line + return 0; + } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays + return delay_val; + } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button + return delay_val; + } else if(delay_val < 0) { + bad_usb->st.error_line = bad_usb->st.line_cur; + FURI_LOG_E(WORKER_TAG, "Unknown command at line %zu", bad_usb->st.line_cur); + return SCRIPT_STATE_ERROR; + } else { + return (delay_val + bad_usb->defdelay); + } + } else { + furi_string_push_back(bad_usb->line, bad_usb->file_buf[i]); + } + } + bad_usb->buf_len = 0; + if(bad_usb->file_end) return SCRIPT_STATE_END; + } + + return 0; +} + +static void bad_usb_hid_state_callback(bool state, void* context) { + furi_assert(context); + BadUsbScript* bad_usb = context; + + if(state == true) { + furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtConnect); + } else { + furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtDisconnect); + } +} + +static uint32_t bad_usb_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 { + uint32_t state = furi_thread_flags_clear(flags); + furi_check((state & FuriFlagError) == 0); + } + return flags; +} + +static int32_t bad_usb_worker(void* context) { + BadUsbScript* bad_usb = context; + + BadUsbWorkerState worker_state = BadUsbStateInit; + BadUsbWorkerState pause_state = BadUsbStateRunning; + int32_t delay_val = 0; + + FURI_LOG_I(WORKER_TAG, "Init"); + File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + bad_usb->line = furi_string_alloc(); + bad_usb->line_prev = furi_string_alloc(); + bad_usb->string_print = furi_string_alloc(); + + furi_hal_hid_set_state_callback(bad_usb_hid_state_callback, bad_usb); + + while(1) { + if(worker_state == BadUsbStateInit) { // State: initialization + if(storage_file_open( + script_file, + furi_string_get_cstr(bad_usb->file_path), + FSAM_READ, + FSOM_OPEN_EXISTING)) { + if((ducky_script_preload(bad_usb, script_file)) && (bad_usb->st.line_nb > 0)) { + if(furi_hal_hid_is_connected()) { + worker_state = BadUsbStateIdle; // Ready to run + } else { + worker_state = BadUsbStateNotConnected; // USB not connected + } + } else { + worker_state = BadUsbStateScriptError; // Script preload error + } + } else { + FURI_LOG_E(WORKER_TAG, "File open error"); + worker_state = BadUsbStateFileError; // File open error + } + bad_usb->st.state = worker_state; + + } else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever); + + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtConnect) { + worker_state = BadUsbStateIdle; // Ready to run + } else if(flags & WorkerEvtStartStop) { + worker_state = BadUsbStateWillRun; // Will run when USB is connected + } + bad_usb->st.state = worker_state; + + } else if(worker_state == BadUsbStateIdle) { // State: ready to start + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtDisconnect, FuriWaitForever); + + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtStartStop) { // Start executing script + dolphin_deed(DolphinDeedBadUsbPlayScript); + delay_val = 0; + bad_usb->buf_len = 0; + bad_usb->st.line_cur = 0; + bad_usb->defdelay = 0; + bad_usb->stringdelay = 0; + bad_usb->repeat_cnt = 0; + bad_usb->key_hold_nb = 0; + bad_usb->file_end = false; + storage_file_seek(script_file, 0, true); + worker_state = BadUsbStateRunning; + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadUsbStateNotConnected; // USB disconnected + } + bad_usb->st.state = worker_state; + + } else if(worker_state == BadUsbStateWillRun) { // State: start on connection + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever); + + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtConnect) { // Start executing script + dolphin_deed(DolphinDeedBadUsbPlayScript); + delay_val = 0; + bad_usb->buf_len = 0; + bad_usb->st.line_cur = 0; + bad_usb->defdelay = 0; + bad_usb->stringdelay = 0; + bad_usb->repeat_cnt = 0; + bad_usb->file_end = false; + storage_file_seek(script_file, 0, true); + // extra time for PC to recognize Flipper as keyboard + flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtStartStop, + FuriFlagWaitAny | FuriFlagNoClear, + 1500); + if(flags == (unsigned)FuriFlagErrorTimeout) { + // If nothing happened - start script execution + worker_state = BadUsbStateRunning; + } else if(flags & WorkerEvtStartStop) { + worker_state = BadUsbStateIdle; + furi_thread_flags_clear(WorkerEvtStartStop); + } + } else if(flags & WorkerEvtStartStop) { // Cancel scheduled execution + worker_state = BadUsbStateNotConnected; + } + bad_usb->st.state = worker_state; + + } else if(worker_state == BadUsbStateRunning) { // State: running + uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); + uint32_t flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + FuriFlagWaitAny, + delay_cur); + + delay_val -= delay_cur; + if(!(flags & FuriFlagError)) { + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtStartStop) { + worker_state = BadUsbStateIdle; // Stop executing script + furi_hal_hid_kb_release_all(); + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadUsbStateNotConnected; // USB disconnected + furi_hal_hid_kb_release_all(); + } else if(flags & WorkerEvtPauseResume) { + pause_state = BadUsbStateRunning; + worker_state = BadUsbStatePaused; // Pause + } + bad_usb->st.state = worker_state; + continue; + } else if( + (flags == (unsigned)FuriFlagErrorTimeout) || + (flags == (unsigned)FuriFlagErrorResource)) { + if(delay_val > 0) { + bad_usb->st.delay_remain--; + continue; + } + bad_usb->st.state = BadUsbStateRunning; + delay_val = ducky_script_execute_next(bad_usb, script_file); + if(delay_val == SCRIPT_STATE_ERROR) { // Script error + delay_val = 0; + worker_state = BadUsbStateScriptError; + bad_usb->st.state = worker_state; + furi_hal_hid_kb_release_all(); + } else if(delay_val == SCRIPT_STATE_END) { // End of script + delay_val = 0; + worker_state = BadUsbStateIdle; + bad_usb->st.state = BadUsbStateDone; + furi_hal_hid_kb_release_all(); + continue; + } else if(delay_val == SCRIPT_STATE_STRING_START) { // Start printing string with delays + delay_val = bad_usb->defdelay; + bad_usb->string_print_pos = 0; + worker_state = BadUsbStateStringDelay; + } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // set state to wait for user input + worker_state = BadUsbStateWaitForBtn; + bad_usb->st.state = BadUsbStateWaitForBtn; // Show long delays + } else if(delay_val > 1000) { + bad_usb->st.state = BadUsbStateDelay; // Show long delays + bad_usb->st.delay_remain = delay_val / 1000; + } + } else { + furi_check((flags & FuriFlagError) == 0); + } + } else if(worker_state == BadUsbStateWaitForBtn) { // State: Wait for button Press + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + FuriWaitForever); + if(!(flags & FuriFlagError)) { + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtStartStop) { + delay_val = 0; + worker_state = BadUsbStateRunning; + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadUsbStateNotConnected; // USB disconnected + furi_hal_hid_kb_release_all(); + } + bad_usb->st.state = worker_state; + continue; + } + } else if(worker_state == BadUsbStatePaused) { // State: Paused + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + FuriWaitForever); + if(!(flags & FuriFlagError)) { + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtStartStop) { + worker_state = BadUsbStateIdle; // Stop executing script + bad_usb->st.state = worker_state; + furi_hal_hid_kb_release_all(); + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadUsbStateNotConnected; // USB disconnected + bad_usb->st.state = worker_state; + furi_hal_hid_kb_release_all(); + } else if(flags & WorkerEvtPauseResume) { + if(pause_state == BadUsbStateRunning) { + if(delay_val > 0) { + bad_usb->st.state = BadUsbStateDelay; + bad_usb->st.delay_remain = delay_val / 1000; + } else { + bad_usb->st.state = BadUsbStateRunning; + delay_val = 0; + } + worker_state = BadUsbStateRunning; // Resume + } else if(pause_state == BadUsbStateStringDelay) { + bad_usb->st.state = BadUsbStateRunning; + worker_state = BadUsbStateStringDelay; // Resume + } + } + continue; + } + } else if(worker_state == BadUsbStateStringDelay) { // State: print string with delays + uint32_t flags = bad_usb_flags_get( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + bad_usb->stringdelay); + + if(!(flags & FuriFlagError)) { + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtStartStop) { + worker_state = BadUsbStateIdle; // Stop executing script + furi_hal_hid_kb_release_all(); + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadUsbStateNotConnected; // USB disconnected + furi_hal_hid_kb_release_all(); + } else if(flags & WorkerEvtPauseResume) { + pause_state = BadUsbStateStringDelay; + worker_state = BadUsbStatePaused; // Pause + } + bad_usb->st.state = worker_state; + continue; + } else if( + (flags == (unsigned)FuriFlagErrorTimeout) || + (flags == (unsigned)FuriFlagErrorResource)) { + bool string_end = ducky_string_next(bad_usb); + if(string_end) { + bad_usb->stringdelay = 0; + worker_state = BadUsbStateRunning; + } + } else { + furi_check((flags & FuriFlagError) == 0); + } + } else if( + (worker_state == BadUsbStateFileError) || + (worker_state == BadUsbStateScriptError)) { // State: error + uint32_t flags = + bad_usb_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command + + if(flags & WorkerEvtEnd) { + break; + } + } + } + + furi_hal_hid_set_state_callback(NULL, NULL); + + storage_file_close(script_file); + storage_file_free(script_file); + furi_string_free(bad_usb->line); + furi_string_free(bad_usb->line_prev); + furi_string_free(bad_usb->string_print); + + FURI_LOG_I(WORKER_TAG, "End"); + + return 0; +} + +static void bad_usb_script_set_default_keyboard_layout(BadUsbScript* bad_usb) { + furi_assert(bad_usb); + memset(bad_usb->layout, HID_KEYBOARD_NONE, sizeof(bad_usb->layout)); + memcpy(bad_usb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_usb->layout))); +} + +BadUsbScript* bad_usb_script_open(FuriString* file_path) { + furi_assert(file_path); + + BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); + bad_usb->file_path = furi_string_alloc(); + furi_string_set(bad_usb->file_path, file_path); + bad_usb_script_set_default_keyboard_layout(bad_usb); + + bad_usb->st.state = BadUsbStateInit; + bad_usb->st.error[0] = '\0'; + + bad_usb->thread = furi_thread_alloc_ex("BadUsbWorker", 2048, bad_usb_worker, bad_usb); + furi_thread_start(bad_usb->thread); + return bad_usb; +} //-V773 + +void bad_usb_script_close(BadUsbScript* bad_usb) { + furi_assert(bad_usb); + furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtEnd); + furi_thread_join(bad_usb->thread); + furi_thread_free(bad_usb->thread); + furi_string_free(bad_usb->file_path); + free(bad_usb); +} + +void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layout_path) { + furi_assert(bad_usb); + + if((bad_usb->st.state == BadUsbStateRunning) || (bad_usb->st.state == BadUsbStateDelay)) { + // do not update keyboard layout while a script is running + return; + } + + File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + if(!furi_string_empty(layout_path)) { //-V1051 + if(storage_file_open( + layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) { + uint16_t layout[128]; + if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) { + memcpy(bad_usb->layout, layout, sizeof(layout)); + } + } + storage_file_close(layout_file); + } else { + bad_usb_script_set_default_keyboard_layout(bad_usb); + } + storage_file_free(layout_file); +} + +void bad_usb_script_start_stop(BadUsbScript* bad_usb) { + furi_assert(bad_usb); + furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtStartStop); +} + +void bad_usb_script_pause_resume(BadUsbScript* bad_usb) { + furi_assert(bad_usb); + furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtPauseResume); +} + +BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb) { + furi_assert(bad_usb); + return &(bad_usb->st); +} diff --git a/applications/main/bad_usb/helpers/ducky_script.h b/applications/main/bad_usb/helpers/ducky_script.h new file mode 100644 index 00000000000..dca61ed4e43 --- /dev/null +++ b/applications/main/bad_usb/helpers/ducky_script.h @@ -0,0 +1,54 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef enum { + BadUsbStateInit, + BadUsbStateNotConnected, + BadUsbStateIdle, + BadUsbStateWillRun, + BadUsbStateRunning, + BadUsbStateDelay, + BadUsbStateStringDelay, + BadUsbStateWaitForBtn, + BadUsbStatePaused, + BadUsbStateDone, + BadUsbStateScriptError, + BadUsbStateFileError, +} BadUsbWorkerState; + +typedef struct { + BadUsbWorkerState state; + size_t line_cur; + size_t line_nb; + uint32_t delay_remain; + size_t error_line; + char error[64]; +} BadUsbState; + +typedef struct BadUsbScript BadUsbScript; + +BadUsbScript* bad_usb_script_open(FuriString* file_path); + +void bad_usb_script_close(BadUsbScript* bad_usb); + +void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layout_path); + +void bad_usb_script_start(BadUsbScript* bad_usb); + +void bad_usb_script_stop(BadUsbScript* bad_usb); + +void bad_usb_script_start_stop(BadUsbScript* bad_usb); + +void bad_usb_script_pause_resume(BadUsbScript* bad_usb); + +BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/bad_usb/helpers/ducky_script_commands.c b/applications/main/bad_usb/helpers/ducky_script_commands.c new file mode 100644 index 00000000000..a5bc7c8cf95 --- /dev/null +++ b/applications/main/bad_usb/helpers/ducky_script_commands.c @@ -0,0 +1,196 @@ +#include +#include +#include "ducky_script.h" +#include "ducky_script_i.h" + +typedef int32_t (*DuckyCmdCallback)(BadUsbScript* bad_usb, const char* line, int32_t param); + +typedef struct { + char* name; + DuckyCmdCallback callback; + int32_t param; +} DuckyCmd; + +static int32_t ducky_fnc_delay(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + uint32_t delay_val = 0; + bool state = ducky_get_number(line, &delay_val); + if((state) && (delay_val > 0)) { + return (int32_t)delay_val; + } + + return ducky_error(bad_usb, "Invalid number %s", line); +} + +static int32_t ducky_fnc_defdelay(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + bool state = ducky_get_number(line, &bad_usb->defdelay); + if(!state) { + return ducky_error(bad_usb, "Invalid number %s", line); + } + return 0; +} + +static int32_t ducky_fnc_strdelay(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + bool state = ducky_get_number(line, &bad_usb->stringdelay); + if(!state) { + return ducky_error(bad_usb, "Invalid number %s", line); + } + return 0; +} + +static int32_t ducky_fnc_string(BadUsbScript* bad_usb, const char* line, int32_t param) { + line = &line[ducky_get_command_len(line) + 1]; + furi_string_set_str(bad_usb->string_print, line); + if(param == 1) { + furi_string_cat(bad_usb->string_print, "\n"); + } + + if(bad_usb->stringdelay == 0) { // stringdelay not set - run command immediately + bool state = ducky_string(bad_usb, furi_string_get_cstr(bad_usb->string_print)); + if(!state) { + return ducky_error(bad_usb, "Invalid string %s", line); + } + } else { // stringdelay is set - run command in thread to keep handling external events + return SCRIPT_STATE_STRING_START; + } + + return 0; +} + +static int32_t ducky_fnc_repeat(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + bool state = ducky_get_number(line, &bad_usb->repeat_cnt); + if((!state) || (bad_usb->repeat_cnt == 0)) { + return ducky_error(bad_usb, "Invalid number %s", line); + } + return 0; +} + +static int32_t ducky_fnc_sysrq(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + uint16_t key = ducky_get_keycode(bad_usb, line, true); + furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); + furi_hal_hid_kb_press(key); + furi_hal_hid_kb_release_all(); + return 0; +} + +static int32_t ducky_fnc_altchar(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + ducky_numlock_on(); + bool state = ducky_altchar(line); + if(!state) { + return ducky_error(bad_usb, "Invalid altchar %s", line); + } + return 0; +} + +static int32_t ducky_fnc_altstring(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + ducky_numlock_on(); + bool state = ducky_altstring(line); + if(!state) { + return ducky_error(bad_usb, "Invalid altstring %s", line); + } + return 0; +} + +static int32_t ducky_fnc_hold(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + uint16_t key = ducky_get_keycode(bad_usb, line, true); + if(key == HID_KEYBOARD_NONE) { + return ducky_error(bad_usb, "No keycode defined for %s", line); + } + bad_usb->key_hold_nb++; + if(bad_usb->key_hold_nb > (HID_KB_MAX_KEYS - 1)) { + return ducky_error(bad_usb, "Too many keys are hold"); + } + furi_hal_hid_kb_press(key); + return 0; +} + +static int32_t ducky_fnc_release(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + uint16_t key = ducky_get_keycode(bad_usb, line, true); + if(key == HID_KEYBOARD_NONE) { + return ducky_error(bad_usb, "No keycode defined for %s", line); + } + if(bad_usb->key_hold_nb == 0) { + return ducky_error(bad_usb, "No keys are hold"); + } + bad_usb->key_hold_nb--; + furi_hal_hid_kb_release(key); + return 0; +} + +static int32_t ducky_fnc_waitforbutton(BadUsbScript* bad_usb, const char* line, int32_t param) { + UNUSED(param); + UNUSED(bad_usb); + UNUSED(line); + + return SCRIPT_STATE_WAIT_FOR_BTN; +} + +static const DuckyCmd ducky_commands[] = { + {"REM", NULL, -1}, + {"ID", NULL, -1}, + {"DELAY", ducky_fnc_delay, -1}, + {"STRING", ducky_fnc_string, 0}, + {"STRINGLN", ducky_fnc_string, 1}, + {"DEFAULT_DELAY", ducky_fnc_defdelay, -1}, + {"DEFAULTDELAY", ducky_fnc_defdelay, -1}, + {"STRINGDELAY", ducky_fnc_strdelay, -1}, + {"STRING_DELAY", ducky_fnc_strdelay, -1}, + {"REPEAT", ducky_fnc_repeat, -1}, + {"SYSRQ", ducky_fnc_sysrq, -1}, + {"ALTCHAR", ducky_fnc_altchar, -1}, + {"ALTSTRING", ducky_fnc_altstring, -1}, + {"ALTCODE", ducky_fnc_altstring, -1}, + {"HOLD", ducky_fnc_hold, -1}, + {"RELEASE", ducky_fnc_release, -1}, + {"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1}, +}; + +#define TAG "BadUsb" +#define WORKER_TAG TAG "Worker" + +int32_t ducky_execute_cmd(BadUsbScript* bad_usb, const char* line) { + size_t cmd_word_len = strcspn(line, " "); + for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) { + size_t cmd_compare_len = strlen(ducky_commands[i].name); + + if(cmd_compare_len != cmd_word_len) { + continue; + } + + if(strncmp(line, ducky_commands[i].name, cmd_compare_len) == 0) { + if(ducky_commands[i].callback == NULL) { + return 0; + } else { + return ((ducky_commands[i].callback)(bad_usb, line, ducky_commands[i].param)); + } + } + } + + return SCRIPT_STATE_CMD_UNKNOWN; +} diff --git a/applications/main/bad_usb/helpers/ducky_script_i.h b/applications/main/bad_usb/helpers/ducky_script_i.h new file mode 100644 index 00000000000..84c7ef9de6c --- /dev/null +++ b/applications/main/bad_usb/helpers/ducky_script_i.h @@ -0,0 +1,70 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "ducky_script.h" + +#define SCRIPT_STATE_ERROR (-1) +#define SCRIPT_STATE_END (-2) +#define SCRIPT_STATE_NEXT_LINE (-3) +#define SCRIPT_STATE_CMD_UNKNOWN (-4) +#define SCRIPT_STATE_STRING_START (-5) +#define SCRIPT_STATE_WAIT_FOR_BTN (-6) + +#define FILE_BUFFER_LEN 16 + +struct BadUsbScript { + FuriHalUsbHidConfig hid_cfg; + FuriThread* thread; + BadUsbState st; + + FuriString* file_path; + uint8_t file_buf[FILE_BUFFER_LEN + 1]; + uint8_t buf_start; + uint8_t buf_len; + bool file_end; + + uint32_t defdelay; + uint32_t stringdelay; + uint16_t layout[128]; + + FuriString* line; + FuriString* line_prev; + uint32_t repeat_cnt; + uint8_t key_hold_nb; + + FuriString* string_print; + size_t string_print_pos; +}; + +uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars); + +uint32_t ducky_get_command_len(const char* line); + +bool ducky_is_line_end(const char chr); + +uint16_t ducky_get_keycode_by_name(const char* param); + +bool ducky_get_number(const char* param, uint32_t* val); + +void ducky_numlock_on(void); + +bool ducky_numpad_press(const char num); + +bool ducky_altchar(const char* charcode); + +bool ducky_altstring(const char* param); + +bool ducky_string(BadUsbScript* bad_usb, const char* param); + +int32_t ducky_execute_cmd(BadUsbScript* bad_usb, const char* line); + +int32_t ducky_error(BadUsbScript* bad_usb, const char* text, ...); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/bad_usb/helpers/ducky_script_keycodes.c b/applications/main/bad_usb/helpers/ducky_script_keycodes.c new file mode 100644 index 00000000000..da2fc22f79f --- /dev/null +++ b/applications/main/bad_usb/helpers/ducky_script_keycodes.c @@ -0,0 +1,79 @@ +#include +#include +#include "ducky_script_i.h" + +typedef struct { + char* name; + uint16_t keycode; +} DuckyKey; + +static const DuckyKey ducky_keys[] = { + {"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT}, + {"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT}, + {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT}, + {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI}, + {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT}, + {"GUI-CTRL", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL}, + + {"CTRL", KEY_MOD_LEFT_CTRL}, + {"CONTROL", KEY_MOD_LEFT_CTRL}, + {"SHIFT", KEY_MOD_LEFT_SHIFT}, + {"ALT", KEY_MOD_LEFT_ALT}, + {"GUI", KEY_MOD_LEFT_GUI}, + {"WINDOWS", KEY_MOD_LEFT_GUI}, + + {"DOWNARROW", HID_KEYBOARD_DOWN_ARROW}, + {"DOWN", HID_KEYBOARD_DOWN_ARROW}, + {"LEFTARROW", HID_KEYBOARD_LEFT_ARROW}, + {"LEFT", HID_KEYBOARD_LEFT_ARROW}, + {"RIGHTARROW", HID_KEYBOARD_RIGHT_ARROW}, + {"RIGHT", HID_KEYBOARD_RIGHT_ARROW}, + {"UPARROW", HID_KEYBOARD_UP_ARROW}, + {"UP", HID_KEYBOARD_UP_ARROW}, + + {"ENTER", HID_KEYBOARD_RETURN}, + {"BREAK", HID_KEYBOARD_PAUSE}, + {"PAUSE", HID_KEYBOARD_PAUSE}, + {"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK}, + {"DELETE", HID_KEYBOARD_DELETE_FORWARD}, + {"BACKSPACE", HID_KEYBOARD_DELETE}, + {"END", HID_KEYBOARD_END}, + {"ESC", HID_KEYBOARD_ESCAPE}, + {"ESCAPE", HID_KEYBOARD_ESCAPE}, + {"HOME", HID_KEYBOARD_HOME}, + {"INSERT", HID_KEYBOARD_INSERT}, + {"NUMLOCK", HID_KEYPAD_NUMLOCK}, + {"PAGEUP", HID_KEYBOARD_PAGE_UP}, + {"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN}, + {"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN}, + {"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK}, + {"SPACE", HID_KEYBOARD_SPACEBAR}, + {"TAB", HID_KEYBOARD_TAB}, + {"MENU", HID_KEYBOARD_APPLICATION}, + {"APP", HID_KEYBOARD_APPLICATION}, + + {"F1", HID_KEYBOARD_F1}, + {"F2", HID_KEYBOARD_F2}, + {"F3", HID_KEYBOARD_F3}, + {"F4", HID_KEYBOARD_F4}, + {"F5", HID_KEYBOARD_F5}, + {"F6", HID_KEYBOARD_F6}, + {"F7", HID_KEYBOARD_F7}, + {"F8", HID_KEYBOARD_F8}, + {"F9", HID_KEYBOARD_F9}, + {"F10", HID_KEYBOARD_F10}, + {"F11", HID_KEYBOARD_F11}, + {"F12", HID_KEYBOARD_F12}, +}; + +uint16_t ducky_get_keycode_by_name(const char* param) { + for(size_t i = 0; i < COUNT_OF(ducky_keys); i++) { + size_t key_cmd_len = strlen(ducky_keys[i].name); + if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) && + (ducky_is_line_end(param[key_cmd_len]))) { + return ducky_keys[i].keycode; + } + } + + return HID_KEYBOARD_NONE; +} diff --git a/applications/main/bad_usb/icon.png b/applications/main/bad_usb/icon.png new file mode 100644 index 00000000000..037474aa3bc Binary files /dev/null and b/applications/main/bad_usb/icon.png differ diff --git a/applications/main/bad_usb/resources/badusb/Install_qFlipper_macOS.txt b/applications/main/bad_usb/resources/badusb/Install_qFlipper_macOS.txt new file mode 100644 index 00000000000..252954ecbdb --- /dev/null +++ b/applications/main/bad_usb/resources/badusb/Install_qFlipper_macOS.txt @@ -0,0 +1,16 @@ +ID 05ac:021e Apple:Keyboard +REM Keep these 3 lines IF (and only if) it's the first time you are performing a badKB attack against a specific macOS target. +REM In fact, it helps Flipper Zero bypass the macOS keyboard setup assistant. Otherwise the attack will not start. +REM Author: 47LeCoste +REM Version 1.0 (Flipper Ducky) +REM Target: macOS +DELAY 3000 +F4 +DELAY 2500 +STRING Terminal +DELAY 2500 +ENTER +DELAY 1500 +STRING (cd /tmp && curl -L -o qFlipper.dmg https://update.flipperzero.one/qFlipper/release/macos-amd64/dmg && hdiutil attach qFlipper.dmg && app_volume=$(ls /Volumes | grep -i "qFlipper") && (test -e /Applications/qFlipper.app && rm -rf /Applications/qFlipper.app ); cp -R "/Volumes/$app_volume/qFlipper.app" /Applications/ && hdiutil detach "/Volumes/$app_volume" && rm qFlipper.dmg && open /Applications/qFlipper.app) +DELAY 1000 +ENTER diff --git a/applications/main/bad_usb/resources/badusb/Install_qFlipper_windows.txt b/applications/main/bad_usb/resources/badusb/Install_qFlipper_windows.txt new file mode 100644 index 00000000000..94282321512 --- /dev/null +++ b/applications/main/bad_usb/resources/badusb/Install_qFlipper_windows.txt @@ -0,0 +1,42 @@ +REM Written by @dexv +DELAY 2000 +GUI r +DELAY 500 +STRING powershell +ENTER +DELAY 1000 +STRING $url = "https://update.flipperzero.one/qFlipper/release/windows-amd64/portable" +ENTER +STRING $output = "$env:USERPROFILE\Documents\qFlipper.zip" +ENTER +STRING $destination = "$env:USERPROFILE\Documents\qFlipper" +ENTER +STRING $shortcutPath = "$env:USERPROFILE\Desktop\qFlipper.lnk" +ENTER +STRING $scriptPath = "$env:USERPROFILE\Documents\qFlipperInstall.ps1" +ENTER +STRING $driverPath = "$destination\STM32 Driver" +ENTER +STRING $installBat = "$driverPath\install.bat" +ENTER +STRING (New-Object System.Net.WebClient).DownloadFile($url, $output) +ENTER +STRING Expand-Archive -Path $output -DestinationPath $destination -Force +ENTER +STRING Set-Location -Path $destination +ENTER +STRING Start-Process -FilePath ".\qFlipper.exe" +ENTER +STRING Start-Process -Wait -FilePath "cmd.exe" -ArgumentList "/c $installBat" +ENTER +STRING $shell = New-Object -ComObject WScript.Shell +ENTER +STRING $shortcut = $shell.CreateShortcut($shortcutPath) +ENTER +STRING $shortcut.TargetPath = "$destination\qFlipper.exe" +ENTER +STRING $shortcut.Save() +ENTER +DELAY 500 +STRING "powershell -ExecutionPolicy Bypass -File $scriptPath" +ENTER diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/ba-BA.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/ba-BA.kl new file mode 100644 index 00000000000..379f6c649c9 Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/ba-BA.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/cz_CS.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/cz_CS.kl new file mode 100644 index 00000000000..d42e9a4ab6d Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/cz_CS.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/da-DA.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/da-DA.kl new file mode 100644 index 00000000000..b3fcc6a9928 Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/da-DA.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/de-CH.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/de-CH.kl new file mode 100644 index 00000000000..1704bc9dbaa Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/de-CH.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/de-DE.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/de-DE.kl new file mode 100644 index 00000000000..67b05c042f1 Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/de-DE.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/dvorak.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/dvorak.kl new file mode 100644 index 00000000000..06b519240e3 Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/dvorak.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/en-UK.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/en-UK.kl new file mode 100644 index 00000000000..0f18b39cc3b Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/en-UK.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/en-US.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/en-US.kl new file mode 100644 index 00000000000..8089d825788 Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/en-US.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/es-ES.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/es-ES.kl new file mode 100644 index 00000000000..15e9d7997c3 Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/es-ES.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/fr-BE.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/fr-BE.kl new file mode 100644 index 00000000000..ea9e553e894 Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/fr-BE.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/fr-CA.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/fr-CA.kl new file mode 100644 index 00000000000..9de8a4732d3 Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/fr-CA.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/fr-CH.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/fr-CH.kl new file mode 100644 index 00000000000..1704bc9dbaa Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/fr-CH.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/fr-FR-mac.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/fr-FR-mac.kl new file mode 100644 index 00000000000..2887aae0fda Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/fr-FR-mac.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/fr-FR.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/fr-FR.kl new file mode 100644 index 00000000000..f9193297e58 Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/fr-FR.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/hr-HR.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/hr-HR.kl new file mode 100644 index 00000000000..379f6c649c9 Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/hr-HR.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/hu-HU.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/hu-HU.kl new file mode 100644 index 00000000000..90a68e32868 Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/hu-HU.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/it-IT.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/it-IT.kl new file mode 100644 index 00000000000..059e428808a Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/it-IT.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/nb-NO.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/nb-NO.kl new file mode 100644 index 00000000000..381945bf2fd Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/nb-NO.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/nl-NL.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/nl-NL.kl new file mode 100644 index 00000000000..597d1db8c21 Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/nl-NL.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/pt-BR.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/pt-BR.kl new file mode 100644 index 00000000000..d36421cfc45 Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/pt-BR.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/pt-PT.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/pt-PT.kl new file mode 100644 index 00000000000..02450419a81 Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/pt-PT.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/si-SI.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/si-SI.kl new file mode 100644 index 00000000000..379f6c649c9 Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/si-SI.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/sk-SK.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/sk-SK.kl new file mode 100755 index 00000000000..0c67458a6a7 Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/sk-SK.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/sv-SE.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/sv-SE.kl new file mode 100644 index 00000000000..5c55bb9ef1d Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/sv-SE.kl differ diff --git a/applications/main/bad_usb/resources/badusb/assets/layouts/tr-TR.kl b/applications/main/bad_usb/resources/badusb/assets/layouts/tr-TR.kl new file mode 100644 index 00000000000..6377b770365 Binary files /dev/null and b/applications/main/bad_usb/resources/badusb/assets/layouts/tr-TR.kl differ diff --git a/applications/main/bad_usb/resources/badusb/demo_macos.txt b/applications/main/bad_usb/resources/badusb/demo_macos.txt new file mode 100644 index 00000000000..82543b28fd9 --- /dev/null +++ b/applications/main/bad_usb/resources/badusb/demo_macos.txt @@ -0,0 +1,86 @@ +ID 1234:5678 Apple:Keyboard +REM You can change these values to VID/PID of original Apple keyboard +REM to bypass Keyboard Setup Assistant + +REM This is BadUSB demo script for macOS + +REM Open terminal window +DELAY 1000 +GUI SPACE +DELAY 500 +STRING terminal +DELAY 500 +ENTER +DELAY 750 + +REM Copy-Paste previous string +UP +CTRL c + +REM Bigger shell script example +STRING cat > /dev/null << EOF +ENTER + +STRING Hello World! +ENTER + +DEFAULT_DELAY 50 + +STRING = +REPEAT 59 +ENTER +ENTER + +STRING _.-------.._ -, +ENTER +HOME +STRING .-"'''"--..,,_/ /'-, -, \ +ENTER +HOME +STRING .:" /:/ /'\ \ ,_..., '. | | +ENTER +HOME +STRING / ,----/:/ /'\ _\~'_-"' _; +ENTER +HOME +STRING ' / /'"""'\ \ \.~'_-' ,-"'/ +ENTER +HOME +STRING | | | 0 | | .-' ,/' / +ENTER +HOME +STRING | ,..\ \ ,.-"' ,/' / +ENTER +HOME +STRING ; : '/'""\' ,/--==,/-----, +ENTER +HOME +STRING | '-...| -.___-Z:_______J...---; +ENTER +HOME +STRING : ' _-' +ENTER +HOME +STRING _L_ _ ___ ___ ___ ___ ____--"' +ENTER +HOME +STRING | __|| | |_ _|| _ \| _ \| __|| _ \ +ENTER +HOME +STRING | _| | |__ | | | _/| _/| _| | / +ENTER +HOME +STRING |_| |____||___||_| |_| |___||_|_\ +ENTER +HOME +ENTER + +STRING Flipper Zero BadUSB feature is compatible with USB Rubber Ducky script format +ENTER +STRING More information about script syntax can be found here: +ENTER +STRING https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/file_formats/BadUsbScriptFormat.md +ENTER + +STRING EOF +ENTER diff --git a/assets/resources/badusb/demo_windows.txt b/applications/main/bad_usb/resources/badusb/demo_windows.txt similarity index 88% rename from assets/resources/badusb/demo_windows.txt rename to applications/main/bad_usb/resources/badusb/demo_windows.txt index df43535105a..2ed33b3c053 100644 --- a/assets/resources/badusb/demo_windows.txt +++ b/applications/main/bad_usb/resources/badusb/demo_windows.txt @@ -78,7 +78,7 @@ ENTER STRING Flipper Zero BadUSB feature is compatible with USB Rubber Ducky script format ENTER -STRING More information about script synax can be found here: +STRING More information about script syntax can be found here: ENTER -STRING https://github.com/hak5darren/USB-Rubber-Ducky/wiki/Duckyscript +STRING https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/file_formats/BadUsbScriptFormat.md ENTER diff --git a/applications/bad_usb/scenes/bad_usb_scene.c b/applications/main/bad_usb/scenes/bad_usb_scene.c similarity index 100% rename from applications/bad_usb/scenes/bad_usb_scene.c rename to applications/main/bad_usb/scenes/bad_usb_scene.c diff --git a/applications/bad_usb/scenes/bad_usb_scene.h b/applications/main/bad_usb/scenes/bad_usb_scene.h similarity index 100% rename from applications/bad_usb/scenes/bad_usb_scene.h rename to applications/main/bad_usb/scenes/bad_usb_scene.h diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config.c b/applications/main/bad_usb/scenes/bad_usb_scene_config.c new file mode 100644 index 00000000000..5477c5e8072 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config.c @@ -0,0 +1,53 @@ +#include "../bad_usb_app_i.h" +#include "furi_hal_power.h" +#include "furi_hal_usb.h" + +enum SubmenuIndex { + SubmenuIndexKeyboardLayout, +}; + +void bad_usb_scene_config_submenu_callback(void* context, uint32_t index) { + BadUsbApp* bad_usb = context; + view_dispatcher_send_custom_event(bad_usb->view_dispatcher, index); +} + +void bad_usb_scene_config_on_enter(void* context) { + BadUsbApp* bad_usb = context; + Submenu* submenu = bad_usb->submenu; + + submenu_add_item( + submenu, + "Keyboard Layout (global)", + SubmenuIndexKeyboardLayout, + bad_usb_scene_config_submenu_callback, + bad_usb); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(bad_usb->scene_manager, BadUsbSceneConfig)); + + view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfig); +} + +bool bad_usb_scene_config_on_event(void* context, SceneManagerEvent event) { + BadUsbApp* bad_usb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfig, event.event); + consumed = true; + if(event.event == SubmenuIndexKeyboardLayout) { + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout); + } else { + furi_crash("Unknown key type"); + } + } + + return consumed; +} + +void bad_usb_scene_config_on_exit(void* context) { + BadUsbApp* bad_usb = context; + Submenu* submenu = bad_usb->submenu; + + submenu_reset(submenu); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config.h b/applications/main/bad_usb/scenes/bad_usb_scene_config.h new file mode 100644 index 00000000000..423aedc51bd --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config.h @@ -0,0 +1,5 @@ +ADD_SCENE(bad_usb, file_select, FileSelect) +ADD_SCENE(bad_usb, work, Work) +ADD_SCENE(bad_usb, error, Error) +ADD_SCENE(bad_usb, config, Config) +ADD_SCENE(bad_usb, config_layout, ConfigLayout) diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c new file mode 100644 index 00000000000..5d70b801b25 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c @@ -0,0 +1,52 @@ +#include "../bad_usb_app_i.h" +#include "furi_hal_power.h" +#include "furi_hal_usb.h" +#include + +static bool bad_usb_layout_select(BadUsbApp* bad_usb) { + furi_assert(bad_usb); + + FuriString* predefined_path; + predefined_path = furi_string_alloc(); + if(!furi_string_empty(bad_usb->keyboard_layout)) { + furi_string_set(predefined_path, bad_usb->keyboard_layout); + } else { + furi_string_set(predefined_path, BAD_USB_APP_PATH_LAYOUT_FOLDER); + } + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, BAD_USB_APP_LAYOUT_EXTENSION, &I_keyboard_10px); + browser_options.base_path = BAD_USB_APP_PATH_LAYOUT_FOLDER; + browser_options.skip_assets = false; + + // Input events and views are managed by file_browser + bool res = dialog_file_browser_show( + bad_usb->dialogs, bad_usb->keyboard_layout, predefined_path, &browser_options); + + furi_string_free(predefined_path); + return res; +} + +void bad_usb_scene_config_layout_on_enter(void* context) { + BadUsbApp* bad_usb = context; + + if(bad_usb_layout_select(bad_usb)) { + bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout); + scene_manager_search_and_switch_to_previous_scene(bad_usb->scene_manager, BadUsbSceneWork); + } else { + scene_manager_previous_scene(bad_usb->scene_manager); + } +} + +bool bad_usb_scene_config_layout_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + // BadUsbApp* bad_usb = context; + return false; +} + +void bad_usb_scene_config_layout_on_exit(void* context) { + UNUSED(context); + // BadUsbApp* bad_usb = context; +} diff --git a/applications/bad_usb/scenes/bad_usb_scene_error.c b/applications/main/bad_usb/scenes/bad_usb_scene_error.c similarity index 100% rename from applications/bad_usb/scenes/bad_usb_scene_error.c rename to applications/main/bad_usb/scenes/bad_usb_scene_error.c diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c new file mode 100644 index 00000000000..d6f05a1edec --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c @@ -0,0 +1,50 @@ +#include "../bad_usb_app_i.h" +#include +#include +#include + +static bool bad_usb_file_select(BadUsbApp* bad_usb) { + furi_assert(bad_usb); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, BAD_USB_APP_SCRIPT_EXTENSION, &I_badusb_10px); + browser_options.base_path = BAD_USB_APP_BASE_FOLDER; + browser_options.skip_assets = true; + + // Input events and views are managed by file_browser + bool res = dialog_file_browser_show( + bad_usb->dialogs, bad_usb->file_path, bad_usb->file_path, &browser_options); + + return res; +} + +void bad_usb_scene_file_select_on_enter(void* context) { + BadUsbApp* bad_usb = context; + + if(bad_usb->bad_usb_script) { + bad_usb_script_close(bad_usb->bad_usb_script); + bad_usb->bad_usb_script = NULL; + } + + if(bad_usb_file_select(bad_usb)) { + bad_usb->bad_usb_script = bad_usb_script_open(bad_usb->file_path); + bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout); + + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork); + } else { + view_dispatcher_stop(bad_usb->view_dispatcher); + } +} + +bool bad_usb_scene_file_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + // BadUsbApp* bad_usb = context; + return false; +} + +void bad_usb_scene_file_select_on_exit(void* context) { + UNUSED(context); + // BadUsbApp* bad_usb = context; +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_work.c b/applications/main/bad_usb/scenes/bad_usb_scene_work.c new file mode 100644 index 00000000000..ad33a124d2a --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_work.c @@ -0,0 +1,59 @@ +#include "../helpers/ducky_script.h" +#include "../bad_usb_app_i.h" +#include "../views/bad_usb_view.h" +#include +#include "toolbox/path.h" + +void bad_usb_scene_work_button_callback(InputKey key, void* context) { + furi_assert(context); + BadUsbApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, key); +} + +bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) { + BadUsbApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == InputKeyLeft) { + if(bad_usb_is_idle_state(app->bad_usb_view)) { + scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig); + } + consumed = true; + } else if(event.event == InputKeyOk) { + bad_usb_script_start_stop(app->bad_usb_script); + consumed = true; + } else if(event.event == InputKeyRight) { + bad_usb_script_pause_resume(app->bad_usb_script); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeTick) { + bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script)); + } + return consumed; +} + +void bad_usb_scene_work_on_enter(void* context) { + BadUsbApp* app = context; + + FuriString* file_name; + file_name = furi_string_alloc(); + path_extract_filename(app->file_path, file_name, true); + bad_usb_set_file_name(app->bad_usb_view, furi_string_get_cstr(file_name)); + furi_string_free(file_name); + + FuriString* layout; + layout = furi_string_alloc(); + path_extract_filename(app->keyboard_layout, layout, true); + bad_usb_set_layout(app->bad_usb_view, furi_string_get_cstr(layout)); + furi_string_free(layout); + + bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script)); + + bad_usb_set_button_callback(app->bad_usb_view, bad_usb_scene_work_button_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewWork); +} + +void bad_usb_scene_work_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c new file mode 100644 index 00000000000..588b260c45e --- /dev/null +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -0,0 +1,280 @@ +#include "bad_usb_view.h" +#include "../helpers/ducky_script.h" +#include +#include +#include + +#define MAX_NAME_LEN 64 + +struct BadUsb { + View* view; + BadUsbButtonCallback callback; + void* context; +}; + +typedef struct { + char file_name[MAX_NAME_LEN]; + char layout[MAX_NAME_LEN]; + BadUsbState state; + bool pause_wait; + uint8_t anim_frame; +} BadUsbModel; + +static void bad_usb_draw_callback(Canvas* canvas, void* _model) { + BadUsbModel* model = _model; + + FuriString* disp_str; + disp_str = furi_string_alloc_set(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)); + + if(strlen(model->layout) == 0) { + furi_string_set(disp_str, "(default)"); + } else { + furi_string_printf(disp_str, "(%s)", model->layout); + } + 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)); + + furi_string_reset(disp_str); + + canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22); + + BadUsbWorkerState state = model->state.state; + + if((state == BadUsbStateIdle) || (state == BadUsbStateDone) || + (state == BadUsbStateNotConnected)) { + elements_button_center(canvas, "Run"); + elements_button_left(canvas, "Config"); + } else if((state == BadUsbStateRunning) || (state == BadUsbStateDelay)) { + elements_button_center(canvas, "Stop"); + if(!model->pause_wait) { + elements_button_right(canvas, "Pause"); + } + } else if(state == BadUsbStatePaused) { + elements_button_center(canvas, "End"); + elements_button_right(canvas, "Resume"); + } else if(state == BadUsbStateWaitForBtn) { + elements_button_center(canvas, "Press to continue"); + } else if(state == BadUsbStateWillRun) { + elements_button_center(canvas, "Cancel"); + } + + if(state == BadUsbStateNotConnected) { + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "to USB"); + } else if(state == BadUsbStateWillRun) { + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect"); + } else if(state == BadUsbStateFileError) { + canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR"); + } else if(state == BadUsbStateScriptError) { + canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); + canvas_set_font(canvas, FontSecondary); + furi_string_printf(disp_str, "line %zu", model->state.error_line); + canvas_draw_str_aligned( + canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); + + furi_string_set_str(disp_str, model->state.error); + elements_string_fit_width(canvas, disp_str, canvas_width(canvas)); + canvas_draw_str_aligned( + canvas, 127, 56, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); + } else if(state == BadUsbStateIdle) { + canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18); + canvas_set_font(canvas, FontBigNumbers); + canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0"); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + } else if(state == BadUsbStateRunning) { + if(model->anim_frame == 0) { + canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); + } else { + canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21); + } + canvas_set_font(canvas, FontBigNumbers); + furi_string_printf( + disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb); + canvas_draw_str_aligned( + canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + } else if(state == BadUsbStateDone) { + canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); + canvas_set_font(canvas, FontBigNumbers); + canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100"); + furi_string_reset(disp_str); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + } else if(state == BadUsbStateDelay) { + if(model->anim_frame == 0) { + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); + } else { + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); + } + canvas_set_font(canvas, FontBigNumbers); + furi_string_printf( + disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb); + canvas_draw_str_aligned( + canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + canvas_set_font(canvas, FontSecondary); + furi_string_printf(disp_str, "delay %lus", model->state.delay_remain); + canvas_draw_str_aligned( + canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); + } else if((state == BadUsbStatePaused) || (state == BadUsbStateWaitForBtn)) { + if(model->anim_frame == 0) { + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); + } else { + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); + } + canvas_set_font(canvas, FontBigNumbers); + furi_string_printf( + disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb); + canvas_draw_str_aligned( + canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 127, 50, AlignRight, AlignBottom, "Paused"); + furi_string_reset(disp_str); + } else { + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); + } + + furi_string_free(disp_str); +} + +static bool bad_usb_input_callback(InputEvent* event, void* context) { + furi_assert(context); + BadUsb* bad_usb = context; + bool consumed = false; + + if(event->type == InputTypeShort) { + if(event->key == InputKeyLeft) { + consumed = true; + furi_assert(bad_usb->callback); + bad_usb->callback(event->key, bad_usb->context); + } else if(event->key == InputKeyOk) { + with_view_model( + bad_usb->view, BadUsbModel * model, { model->pause_wait = false; }, true); + consumed = true; + furi_assert(bad_usb->callback); + bad_usb->callback(event->key, bad_usb->context); + } else if(event->key == InputKeyRight) { + with_view_model( + bad_usb->view, + BadUsbModel * model, + { + if((model->state.state == BadUsbStateRunning) || + (model->state.state == BadUsbStateDelay)) { + model->pause_wait = true; + } + }, + true); + consumed = true; + furi_assert(bad_usb->callback); + bad_usb->callback(event->key, bad_usb->context); + } + } + + return consumed; +} + +BadUsb* bad_usb_alloc() { + BadUsb* bad_usb = malloc(sizeof(BadUsb)); + + bad_usb->view = view_alloc(); + view_allocate_model(bad_usb->view, ViewModelTypeLocking, sizeof(BadUsbModel)); + view_set_context(bad_usb->view, bad_usb); + view_set_draw_callback(bad_usb->view, bad_usb_draw_callback); + view_set_input_callback(bad_usb->view, bad_usb_input_callback); + + return bad_usb; +} + +void bad_usb_free(BadUsb* bad_usb) { + furi_assert(bad_usb); + view_free(bad_usb->view); + free(bad_usb); +} + +View* bad_usb_get_view(BadUsb* bad_usb) { + furi_assert(bad_usb); + return bad_usb->view; +} + +void bad_usb_set_button_callback(BadUsb* bad_usb, BadUsbButtonCallback callback, void* context) { + furi_assert(bad_usb); + furi_assert(callback); + with_view_model( + bad_usb->view, + BadUsbModel * model, + { + UNUSED(model); + bad_usb->callback = callback; + bad_usb->context = context; + }, + true); +} + +void bad_usb_set_file_name(BadUsb* bad_usb, const char* name) { + furi_assert(name); + with_view_model( + bad_usb->view, + BadUsbModel * model, + { strlcpy(model->file_name, name, MAX_NAME_LEN); }, + true); +} + +void bad_usb_set_layout(BadUsb* bad_usb, const char* layout) { + furi_assert(layout); + with_view_model( + bad_usb->view, + BadUsbModel * model, + { strlcpy(model->layout, layout, MAX_NAME_LEN); }, + true); +} + +void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) { + furi_assert(st); + with_view_model( + bad_usb->view, + BadUsbModel * model, + { + memcpy(&(model->state), st, sizeof(BadUsbState)); + model->anim_frame ^= 1; + if(model->state.state == BadUsbStatePaused) { + model->pause_wait = false; + } + }, + true); +} + +bool bad_usb_is_idle_state(BadUsb* bad_usb) { + bool is_idle = false; + with_view_model( + bad_usb->view, + BadUsbModel * model, + { + if((model->state.state == BadUsbStateIdle) || + (model->state.state == BadUsbStateDone) || + (model->state.state == BadUsbStateNotConnected)) { + is_idle = true; + } + }, + false); + return is_idle; +} diff --git a/applications/main/bad_usb/views/bad_usb_view.h b/applications/main/bad_usb/views/bad_usb_view.h new file mode 100644 index 00000000000..6210835f016 --- /dev/null +++ b/applications/main/bad_usb/views/bad_usb_view.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include "../helpers/ducky_script.h" + +typedef struct BadUsb BadUsb; +typedef void (*BadUsbButtonCallback)(InputKey key, void* context); + +BadUsb* bad_usb_alloc(); + +void bad_usb_free(BadUsb* bad_usb); + +View* bad_usb_get_view(BadUsb* bad_usb); + +void bad_usb_set_button_callback(BadUsb* bad_usb, BadUsbButtonCallback callback, void* context); + +void bad_usb_set_file_name(BadUsb* bad_usb, const char* name); + +void bad_usb_set_layout(BadUsb* bad_usb, const char* layout); + +void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st); + +bool bad_usb_is_idle_state(BadUsb* bad_usb); diff --git a/applications/main/gpio/application.fam b/applications/main/gpio/application.fam new file mode 100644 index 00000000000..76391992178 --- /dev/null +++ b/applications/main/gpio/application.fam @@ -0,0 +1,12 @@ +App( + appid="gpio", + name="GPIO", + apptype=FlipperAppType.MENUEXTERNAL, + entry_point="gpio_app", + stack_size=1 * 1024, + icon="A_GPIO_14", + order=50, + fap_libs=["assets"], + fap_icon="icon.png", + fap_category="GPIO", +) diff --git a/applications/gpio/gpio_app.c b/applications/main/gpio/gpio_app.c similarity index 89% rename from applications/gpio/gpio_app.c rename to applications/main/gpio/gpio_app.c index b8afdc8ea1e..020fbf79a12 100644 --- a/applications/gpio/gpio_app.c +++ b/applications/main/gpio/gpio_app.c @@ -25,6 +25,7 @@ GpioApp* gpio_app_alloc() { GpioApp* app = malloc(sizeof(GpioApp)); app->gui = furi_record_open(RECORD_GUI); + app->gpio_items = gpio_items_alloc(); app->view_dispatcher = view_dispatcher_alloc(); app->scene_manager = scene_manager_alloc(&gpio_scene_handlers, app); @@ -42,12 +43,17 @@ GpioApp* gpio_app_alloc() { app->notifications = furi_record_open(RECORD_NOTIFICATION); + // Dialog view + app->dialog = dialog_ex_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, GpioAppViewExitConfirm, dialog_ex_get_view(app->dialog)); + app->var_item_list = variable_item_list_alloc(); view_dispatcher_add_view( app->view_dispatcher, GpioAppViewVarItemList, variable_item_list_get_view(app->var_item_list)); - app->gpio_test = gpio_test_alloc(); + app->gpio_test = gpio_test_alloc(app->gpio_items); view_dispatcher_add_view( app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test)); @@ -78,10 +84,12 @@ void gpio_app_free(GpioApp* app) { view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUart); view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCfg); view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUartCloseRpc); + view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewExitConfirm); variable_item_list_free(app->var_item_list); widget_free(app->widget); gpio_test_free(app->gpio_test); gpio_usb_uart_free(app->gpio_usb_uart); + dialog_ex_free(app->dialog); // View dispatcher view_dispatcher_free(app->view_dispatcher); @@ -91,6 +99,7 @@ void gpio_app_free(GpioApp* app) { furi_record_close(RECORD_GUI); furi_record_close(RECORD_NOTIFICATION); + gpio_items_free(app->gpio_items); free(app); } diff --git a/applications/gpio/gpio_app.h b/applications/main/gpio/gpio_app.h similarity index 100% rename from applications/gpio/gpio_app.h rename to applications/main/gpio/gpio_app.h diff --git a/applications/gpio/gpio_app_i.h b/applications/main/gpio/gpio_app_i.h similarity index 79% rename from applications/gpio/gpio_app_i.h rename to applications/main/gpio/gpio_app_i.h index fa91e8f79db..d54ffd36826 100644 --- a/applications/gpio/gpio_app_i.h +++ b/applications/main/gpio/gpio_app_i.h @@ -1,7 +1,7 @@ #pragma once #include "gpio_app.h" -#include "gpio_item.h" +#include "gpio_items.h" #include "scenes/gpio_scene.h" #include "gpio_custom_event.h" #include "usb_uart_bridge.h" @@ -13,8 +13,10 @@ #include #include #include +#include #include "views/gpio_test.h" #include "views/gpio_usb_uart.h" +#include struct GpioApp { Gui* gui; @@ -22,11 +24,15 @@ struct GpioApp { ViewDispatcher* view_dispatcher; SceneManager* scene_manager; Widget* widget; + DialogEx* dialog; VariableItemList* var_item_list; + VariableItem* var_item_flow; GpioTest* gpio_test; GpioUsbUart* gpio_usb_uart; + GPIOItems* gpio_items; UsbUartBridge* usb_uart_bridge; + UsbUartConfig* usb_uart_cfg; }; typedef enum { @@ -35,4 +41,5 @@ typedef enum { GpioAppViewUsbUart, GpioAppViewUsbUartCfg, GpioAppViewUsbUartCloseRpc, + GpioAppViewExitConfirm, } GpioAppView; diff --git a/applications/gpio/gpio_custom_event.h b/applications/main/gpio/gpio_custom_event.h similarity index 87% rename from applications/gpio/gpio_custom_event.h rename to applications/main/gpio/gpio_custom_event.h index 2bf3e5a8b37..72b8feccd00 100644 --- a/applications/gpio/gpio_custom_event.h +++ b/applications/main/gpio/gpio_custom_event.h @@ -9,4 +9,5 @@ typedef enum { GpioCustomEventErrorBack, GpioUsbUartEventConfig, + GpioUsbUartEventConfigSet, } GpioCustomEvent; diff --git a/applications/main/gpio/gpio_items.c b/applications/main/gpio/gpio_items.c new file mode 100644 index 00000000000..746abe032ae --- /dev/null +++ b/applications/main/gpio/gpio_items.c @@ -0,0 +1,71 @@ +#include "gpio_items.h" + +#include + +struct GPIOItems { + GpioPinRecord* pins; + size_t count; +}; + +GPIOItems* gpio_items_alloc() { + GPIOItems* items = malloc(sizeof(GPIOItems)); + + items->count = 0; + for(size_t i = 0; i < gpio_pins_count; i++) { + if(!gpio_pins[i].debug) { + items->count++; + } + } + + items->pins = malloc(sizeof(GpioPinRecord) * items->count); + size_t index = 0; + for(size_t i = 0; i < gpio_pins_count; i++) { + if(!gpio_pins[i].debug) { + items->pins[index].pin = gpio_pins[i].pin; + items->pins[index].name = gpio_pins[i].name; + index++; + } + } + return items; +} + +void gpio_items_free(GPIOItems* items) { + free(items->pins); + free(items); +} + +uint8_t gpio_items_get_count(GPIOItems* items) { + return items->count; +} + +void gpio_items_configure_pin(GPIOItems* items, uint8_t index, GpioMode mode) { + furi_assert(index < items->count); + furi_hal_gpio_write(items->pins[index].pin, false); + furi_hal_gpio_init(items->pins[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh); +} + +void gpio_items_configure_all_pins(GPIOItems* items, GpioMode mode) { + for(uint8_t i = 0; i < items->count; i++) { + gpio_items_configure_pin(items, i, mode); + } +} + +void gpio_items_set_pin(GPIOItems* items, uint8_t index, bool level) { + furi_assert(index < items->count); + furi_hal_gpio_write(items->pins[index].pin, level); +} + +void gpio_items_set_all_pins(GPIOItems* items, bool level) { + for(uint8_t i = 0; i < items->count; i++) { + gpio_items_set_pin(items, i, level); + } +} + +const char* gpio_items_get_pin_name(GPIOItems* items, uint8_t index) { + furi_assert(index < items->count + 1); + if(index == items->count) { + return "ALL"; + } else { + return items->pins[index].name; + } +} diff --git a/applications/main/gpio/gpio_items.h b/applications/main/gpio/gpio_items.h new file mode 100644 index 00000000000..68afbe6934e --- /dev/null +++ b/applications/main/gpio/gpio_items.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct GPIOItems GPIOItems; + +GPIOItems* gpio_items_alloc(); + +void gpio_items_free(GPIOItems* items); + +uint8_t gpio_items_get_count(GPIOItems* items); + +void gpio_items_configure_pin(GPIOItems* items, uint8_t index, GpioMode mode); + +void gpio_items_configure_all_pins(GPIOItems* items, GpioMode mode); + +void gpio_items_set_pin(GPIOItems* items, uint8_t index, bool level); + +void gpio_items_set_all_pins(GPIOItems* items, bool level); + +const char* gpio_items_get_pin_name(GPIOItems* items, uint8_t index); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/gpio/icon.png b/applications/main/gpio/icon.png new file mode 100644 index 00000000000..4a6eccf0584 Binary files /dev/null and b/applications/main/gpio/icon.png differ diff --git a/applications/gpio/scenes/gpio_scene.c b/applications/main/gpio/scenes/gpio_scene.c similarity index 100% rename from applications/gpio/scenes/gpio_scene.c rename to applications/main/gpio/scenes/gpio_scene.c diff --git a/applications/gpio/scenes/gpio_scene.h b/applications/main/gpio/scenes/gpio_scene.h similarity index 100% rename from applications/gpio/scenes/gpio_scene.h rename to applications/main/gpio/scenes/gpio_scene.h diff --git a/applications/gpio/scenes/gpio_scene_config.h b/applications/main/gpio/scenes/gpio_scene_config.h similarity index 81% rename from applications/gpio/scenes/gpio_scene_config.h rename to applications/main/gpio/scenes/gpio_scene_config.h index 3406e42d3a2..d6fd24d19d3 100644 --- a/applications/gpio/scenes/gpio_scene_config.h +++ b/applications/main/gpio/scenes/gpio_scene_config.h @@ -3,3 +3,4 @@ ADD_SCENE(gpio, test, Test) ADD_SCENE(gpio, usb_uart, UsbUart) ADD_SCENE(gpio, usb_uart_cfg, UsbUartCfg) ADD_SCENE(gpio, usb_uart_close_rpc, UsbUartCloseRpc) +ADD_SCENE(gpio, exit_confirm, ExitConfirm) diff --git a/applications/main/gpio/scenes/gpio_scene_exit_confirm.c b/applications/main/gpio/scenes/gpio_scene_exit_confirm.c new file mode 100644 index 00000000000..efb0734a314 --- /dev/null +++ b/applications/main/gpio/scenes/gpio_scene_exit_confirm.c @@ -0,0 +1,44 @@ +#include "gpio_app_i.h" + +void gpio_scene_exit_confirm_dialog_callback(DialogExResult result, void* context) { + GpioApp* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, result); +} + +void gpio_scene_exit_confirm_on_enter(void* context) { + GpioApp* app = context; + DialogEx* dialog = app->dialog; + + dialog_ex_set_context(dialog, app); + dialog_ex_set_left_button_text(dialog, "Exit"); + dialog_ex_set_right_button_text(dialog, "Stay"); + dialog_ex_set_header(dialog, "Exit USB-UART?", 22, 12, AlignLeft, AlignTop); + dialog_ex_set_result_callback(dialog, gpio_scene_exit_confirm_dialog_callback); + + view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewExitConfirm); +} + +bool gpio_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { + GpioApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + consumed = scene_manager_previous_scene(app->scene_manager); + } else if(event.event == DialogExResultLeft) { + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, GpioSceneStart); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = true; + } + + return consumed; +} + +void gpio_scene_exit_confirm_on_exit(void* context) { + GpioApp* app = context; + + // Clean view + dialog_ex_reset(app->dialog); +} diff --git a/applications/gpio/scenes/gpio_scene_start.c b/applications/main/gpio/scenes/gpio_scene_start.c similarity index 96% rename from applications/gpio/scenes/gpio_scene_start.c rename to applications/main/gpio/scenes/gpio_scene_start.c index 41b745233fa..42193648800 100644 --- a/applications/gpio/scenes/gpio_scene_start.c +++ b/applications/main/gpio/scenes/gpio_scene_start.c @@ -1,6 +1,7 @@ #include "../gpio_app_i.h" -#include "furi_hal_power.h" -#include "furi_hal_usb.h" +#include +#include +#include enum GpioItem { GpioItemUsbUart, @@ -88,6 +89,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/gpio/scenes/gpio_scene_test.c b/applications/main/gpio/scenes/gpio_scene_test.c similarity index 78% rename from applications/gpio/scenes/gpio_scene_test.c rename to applications/main/gpio/scenes/gpio_scene_test.c index b015d80902e..9940b95d459 100644 --- a/applications/gpio/scenes/gpio_scene_test.c +++ b/applications/main/gpio/scenes/gpio_scene_test.c @@ -12,8 +12,9 @@ void gpio_scene_test_ok_callback(InputType type, void* context) { } void gpio_scene_test_on_enter(void* context) { + furi_assert(context); GpioApp* app = context; - gpio_item_configure_all_pins(GpioModeOutputPushPull); + gpio_items_configure_all_pins(app->gpio_items, GpioModeOutputPushPull); gpio_test_set_ok_callback(app->gpio_test, gpio_scene_test_ok_callback, app); view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewGpioTest); } @@ -25,6 +26,7 @@ bool gpio_scene_test_on_event(void* context, SceneManagerEvent event) { } void gpio_scene_test_on_exit(void* context) { - UNUSED(context); - gpio_item_configure_all_pins(GpioModeAnalog); + furi_assert(context); + GpioApp* app = context; + gpio_items_configure_all_pins(app->gpio_items, GpioModeAnalog); } diff --git a/applications/gpio/scenes/gpio_scene_usb_uart.c b/applications/main/gpio/scenes/gpio_scene_usb_uart.c similarity index 93% rename from applications/gpio/scenes/gpio_scene_usb_uart.c rename to applications/main/gpio/scenes/gpio_scene_usb_uart.c index aa41aaf9878..9a3514ca4f5 100644 --- a/applications/gpio/scenes/gpio_scene_usb_uart.c +++ b/applications/main/gpio/scenes/gpio_scene_usb_uart.c @@ -19,7 +19,7 @@ void gpio_scene_usb_uart_on_enter(void* context) { uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUart); if(prev_state == 0) { scene_usb_uart = malloc(sizeof(SceneUsbUartBridge)); - scene_usb_uart->cfg.vcp_ch = 0; // TODO: settings load + scene_usb_uart->cfg.vcp_ch = 0; scene_usb_uart->cfg.uart_ch = 0; scene_usb_uart->cfg.flow_pins = 0; scene_usb_uart->cfg.baudrate_mode = 0; @@ -42,6 +42,9 @@ bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state(app->scene_manager, GpioSceneUsbUart, 1); scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCfg); return true; + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(app->scene_manager, GpioSceneExitConfirm); + return true; } else if(event.type == SceneManagerEventTypeTick) { uint32_t tx_cnt_last = scene_usb_uart->state.tx_cnt; uint32_t rx_cnt_last = scene_usb_uart->state.rx_cnt; diff --git a/applications/gpio/scenes/gpio_scene_usb_uart_close_rpc.c b/applications/main/gpio/scenes/gpio_scene_usb_uart_close_rpc.c similarity index 100% rename from applications/gpio/scenes/gpio_scene_usb_uart_close_rpc.c rename to applications/main/gpio/scenes/gpio_scene_usb_uart_close_rpc.c diff --git a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c new file mode 100644 index 00000000000..e2ab6626447 --- /dev/null +++ b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c @@ -0,0 +1,172 @@ +#include "../usb_uart_bridge.h" +#include "../gpio_app_i.h" +#include + +typedef enum { + UsbUartLineIndexVcp, + UsbUartLineIndexBaudrate, + UsbUartLineIndexUart, + UsbUartLineIndexFlow, +} LineIndex; + +static const char* vcp_ch[] = {"0 (CLI)", "1"}; +static const char* uart_ch[] = {"13,14", "15,16"}; +static const char* flow_pins[] = {"None", "2,3", "6,7", "16,15"}; +static const char* baudrate_mode[] = {"Host"}; +static const uint32_t baudrate_list[] = { + 1200, + 2400, + 4800, + 9600, + 19200, + 28800, + 38400, + 57600, + 115200, + 230400, + 460800, + 921600, +}; + +bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) { + GpioApp* app = context; + furi_assert(app); + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GpioUsbUartEventConfigSet) { + usb_uart_set_config(app->usb_uart_bridge, app->usb_uart_cfg); + return true; + } + } + return false; +} + +void line_ensure_flow_invariant(GpioApp* app) { + // GPIO pins PC0, PC1 (16,15) are unavailable for RTS/DTR when LPUART is + // selected. This function enforces that invariant by resetting flow_pins + // to None if it is configured to 16,15 when LPUART is selected. + + uint8_t available_flow_pins = app->usb_uart_cfg->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4; + VariableItem* item = app->var_item_flow; + variable_item_set_values_count(item, available_flow_pins); + + if(app->usb_uart_cfg->flow_pins >= available_flow_pins) { + app->usb_uart_cfg->flow_pins = 0; + + variable_item_set_current_value_index(item, app->usb_uart_cfg->flow_pins); + variable_item_set_current_value_text(item, flow_pins[app->usb_uart_cfg->flow_pins]); + } +} + +static void line_vcp_cb(VariableItem* item) { + GpioApp* app = variable_item_get_context(item); + furi_assert(app); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, vcp_ch[index]); + + app->usb_uart_cfg->vcp_ch = index; + view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet); +} + +static void line_port_cb(VariableItem* item) { + GpioApp* app = variable_item_get_context(item); + furi_assert(app); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, uart_ch[index]); + + if(index == 0) + app->usb_uart_cfg->uart_ch = FuriHalUartIdUSART1; + else if(index == 1) + app->usb_uart_cfg->uart_ch = FuriHalUartIdLPUART1; + + line_ensure_flow_invariant(app); + view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet); +} + +static void line_flow_cb(VariableItem* item) { + GpioApp* app = variable_item_get_context(item); + furi_assert(app); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, flow_pins[index]); + + app->usb_uart_cfg->flow_pins = index; + view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet); +} + +static void line_baudrate_cb(VariableItem* item) { + GpioApp* app = variable_item_get_context(item); + furi_assert(app); + uint8_t index = variable_item_get_current_value_index(item); + + char br_text[8]; + + if(index > 0) { + snprintf(br_text, 7, "%lu", baudrate_list[index - 1]); + variable_item_set_current_value_text(item, br_text); + app->usb_uart_cfg->baudrate = baudrate_list[index - 1]; + } else { + variable_item_set_current_value_text(item, baudrate_mode[index]); + app->usb_uart_cfg->baudrate = 0; + } + app->usb_uart_cfg->baudrate_mode = index; + view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet); +} + +void gpio_scene_usb_uart_cfg_on_enter(void* context) { + GpioApp* app = context; + furi_assert(app); + VariableItemList* var_item_list = app->var_item_list; + + app->usb_uart_cfg = malloc(sizeof(UsbUartConfig)); + usb_uart_get_config(app->usb_uart_bridge, app->usb_uart_cfg); + + VariableItem* item; + char br_text[8]; + + item = variable_item_list_add(var_item_list, "USB Channel", 2, line_vcp_cb, app); + variable_item_set_current_value_index(item, app->usb_uart_cfg->vcp_ch); + variable_item_set_current_value_text(item, vcp_ch[app->usb_uart_cfg->vcp_ch]); + + item = variable_item_list_add( + var_item_list, + "Baudrate", + sizeof(baudrate_list) / sizeof(baudrate_list[0]) + 1, + line_baudrate_cb, + app); + variable_item_set_current_value_index(item, app->usb_uart_cfg->baudrate_mode); + if(app->usb_uart_cfg->baudrate_mode > 0) { + snprintf(br_text, 7, "%lu", baudrate_list[app->usb_uart_cfg->baudrate_mode - 1]); + variable_item_set_current_value_text(item, br_text); + } else { + variable_item_set_current_value_text( + item, baudrate_mode[app->usb_uart_cfg->baudrate_mode]); + } + + item = variable_item_list_add(var_item_list, "UART Pins", 2, line_port_cb, app); + variable_item_set_current_value_index(item, app->usb_uart_cfg->uart_ch); + variable_item_set_current_value_text(item, uart_ch[app->usb_uart_cfg->uart_ch]); + + item = variable_item_list_add( + var_item_list, "RTS/DTR Pins", COUNT_OF(flow_pins), line_flow_cb, app); + variable_item_set_current_value_index(item, app->usb_uart_cfg->flow_pins); + variable_item_set_current_value_text(item, flow_pins[app->usb_uart_cfg->flow_pins]); + app->var_item_flow = item; + line_ensure_flow_invariant(app); + + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUartCfg)); + + view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUartCfg); +} + +void gpio_scene_usb_uart_cfg_on_exit(void* context) { + GpioApp* app = context; + scene_manager_set_scene_state( + app->scene_manager, + GpioAppViewUsbUartCfg, + variable_item_list_get_selected_item_index(app->var_item_list)); + variable_item_list_reset(app->var_item_list); + free(app->usb_uart_cfg); +} diff --git a/applications/gpio/usb_uart_bridge.c b/applications/main/gpio/usb_uart_bridge.c similarity index 90% rename from applications/gpio/usb_uart_bridge.c rename to applications/main/gpio/usb_uart_bridge.c index 4623c4af1cf..9bc759dc895 100644 --- a/applications/gpio/usb_uart_bridge.c +++ b/applications/main/gpio/usb_uart_bridge.c @@ -1,10 +1,10 @@ #include "usb_uart_bridge.h" -#include "furi_hal.h" -#include -#include #include "usb_cdc.h" -#include "cli/cli_vcp.h" -#include "cli/cli.h" +#include +#include +#include +#include +#include #define USB_CDC_PKT_LEN CDC_DATA_SZ #define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5) @@ -15,6 +15,7 @@ static const GpioPin* flow_pins[][2] = { {&gpio_ext_pa7, &gpio_ext_pa6}, // 2, 3 {&gpio_ext_pb2, &gpio_ext_pc3}, // 6, 7 + {&gpio_ext_pc0, &gpio_ext_pc1}, // 16, 15 }; typedef enum { @@ -43,7 +44,7 @@ struct UsbUartBridge { FuriThread* thread; FuriThread* tx_thread; - StreamBufferHandle_t rx_stream; + FuriStreamBuffer* rx_stream; FuriMutex* usb_mutex; @@ -51,6 +52,8 @@ struct UsbUartBridge { UsbUartState st; + FuriApiLock cfg_lock; + uint8_t rx_buf[USB_CDC_PKT_LEN]; }; @@ -74,12 +77,10 @@ static int32_t usb_uart_tx_thread(void* context); static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { UsbUartBridge* usb_uart = (UsbUartBridge*)context; - BaseType_t xHigherPriorityTaskWoken = pdFALSE; if(ev == UartIrqEventRXNE) { - xStreamBufferSendFromISR(usb_uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken); + furi_stream_buffer_send(usb_uart->rx_stream, &data, 1, 0); furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtRxDone); - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } } @@ -156,16 +157,13 @@ static int32_t usb_uart_worker(void* context) { memcpy(&usb_uart->cfg, &usb_uart->cfg_new, sizeof(UsbUartConfig)); - usb_uart->rx_stream = xStreamBufferCreate(USB_UART_RX_BUF_SIZE, 1); + usb_uart->rx_stream = furi_stream_buffer_alloc(USB_UART_RX_BUF_SIZE, 1); usb_uart->tx_sem = furi_semaphore_alloc(1, 1); usb_uart->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - usb_uart->tx_thread = furi_thread_alloc(); - furi_thread_set_name(usb_uart->tx_thread, "UsbUartTxWorker"); - furi_thread_set_stack_size(usb_uart->tx_thread, 512); - furi_thread_set_context(usb_uart->tx_thread, usb_uart); - furi_thread_set_callback(usb_uart->tx_thread, usb_uart_tx_thread); + usb_uart->tx_thread = + furi_thread_alloc_ex("UsbUartTxWorker", 512, usb_uart_tx_thread, usb_uart); usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch); usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch); @@ -186,11 +184,11 @@ static int32_t usb_uart_worker(void* context) { while(1) { uint32_t events = furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); - furi_check((events & FuriFlagError) == 0); + furi_check(!(events & FuriFlagError)); if(events & WorkerEvtStop) break; if(events & WorkerEvtRxDone) { - size_t len = - xStreamBufferReceive(usb_uart->rx_stream, usb_uart->rx_buf, USB_CDC_PKT_LEN, 0); + size_t len = furi_stream_buffer_receive( + usb_uart->rx_stream, usb_uart->rx_buf, USB_CDC_PKT_LEN, 0); if(len > 0) { if(furi_semaphore_acquire(usb_uart->tx_sem, 100) == FuriStatusOk) { usb_uart->st.rx_cnt += len; @@ -199,7 +197,7 @@ static int32_t usb_uart_worker(void* context) { furi_hal_cdc_send(usb_uart->cfg.vcp_ch, usb_uart->rx_buf, len); furi_check(furi_mutex_release(usb_uart->usb_mutex) == FuriStatusOk); } else { - xStreamBufferReset(usb_uart->rx_stream); + furi_stream_buffer_reset(usb_uart->rx_stream); } } } @@ -249,6 +247,7 @@ static int32_t usb_uart_worker(void* context) { usb_uart->cfg.flow_pins = usb_uart->cfg_new.flow_pins; events |= WorkerEvtCtrlLineSet; } + api_lock_unlock(usb_uart->cfg_lock); } if(events & WorkerEvtLineCfgSet) { if(usb_uart->cfg.baudrate == 0) @@ -270,7 +269,7 @@ static int32_t usb_uart_worker(void* context) { furi_thread_join(usb_uart->tx_thread); furi_thread_free(usb_uart->tx_thread); - vStreamBufferDelete(usb_uart->rx_stream); + furi_stream_buffer_free(usb_uart->rx_stream); furi_mutex_free(usb_uart->usb_mutex); furi_semaphore_free(usb_uart->tx_sem); @@ -290,7 +289,7 @@ static int32_t usb_uart_tx_thread(void* context) { while(1) { uint32_t events = furi_thread_flags_wait(WORKER_ALL_TX_EVENTS, FuriFlagWaitAny, FuriWaitForever); - furi_check((events & FuriFlagError) == 0); + furi_check(!(events & FuriFlagError)); if(events & WorkerEvtTxStop) break; if(events & WorkerEvtCdcRx) { furi_check(furi_mutex_acquire(usb_uart->usb_mutex, FuriWaitForever) == FuriStatusOk); @@ -340,11 +339,7 @@ UsbUartBridge* usb_uart_enable(UsbUartConfig* cfg) { memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig)); - usb_uart->thread = furi_thread_alloc(); - furi_thread_set_name(usb_uart->thread, "UsbUartWorker"); - furi_thread_set_stack_size(usb_uart->thread, 1024); - furi_thread_set_context(usb_uart->thread, usb_uart); - furi_thread_set_callback(usb_uart->thread, usb_uart_worker); + usb_uart->thread = furi_thread_alloc_ex("UsbUartWorker", 1024, usb_uart_worker, usb_uart); furi_thread_start(usb_uart->thread); return usb_uart; @@ -361,8 +356,10 @@ void usb_uart_disable(UsbUartBridge* usb_uart) { void usb_uart_set_config(UsbUartBridge* 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(UsbUartBridge* usb_uart, UsbUartConfig* cfg) { diff --git a/applications/gpio/usb_uart_bridge.h b/applications/main/gpio/usb_uart_bridge.h similarity index 100% rename from applications/gpio/usb_uart_bridge.h rename to applications/main/gpio/usb_uart_bridge.h diff --git a/applications/main/gpio/views/gpio_test.c b/applications/main/gpio/views/gpio_test.c new file mode 100644 index 00000000000..c154a7275b4 --- /dev/null +++ b/applications/main/gpio/views/gpio_test.c @@ -0,0 +1,149 @@ +#include "gpio_test.h" +#include "../gpio_items.h" + +#include + +struct GpioTest { + View* view; + GpioTestOkCallback callback; + void* context; +}; + +typedef struct { + uint8_t pin_idx; + GPIOItems* gpio_items; +} GpioTestModel; + +static bool gpio_test_process_left(GpioTest* gpio_test); +static bool gpio_test_process_right(GpioTest* gpio_test); +static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event); + +static void gpio_test_draw_callback(Canvas* canvas, void* _model) { + GpioTestModel* model = _model; + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "GPIO Output Mode Test"); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned( + canvas, 64, 16, AlignCenter, AlignTop, "Press < or > to change pin"); + elements_multiline_text_aligned( + canvas, + 64, + 32, + AlignCenter, + AlignTop, + gpio_items_get_pin_name(model->gpio_items, model->pin_idx)); +} + +static bool gpio_test_input_callback(InputEvent* event, void* context) { + furi_assert(context); + GpioTest* gpio_test = context; + bool consumed = false; + + if(event->type == InputTypeShort) { + if(event->key == InputKeyRight) { + consumed = gpio_test_process_right(gpio_test); + } else if(event->key == InputKeyLeft) { + consumed = gpio_test_process_left(gpio_test); + } + } else if(event->key == InputKeyOk) { + consumed = gpio_test_process_ok(gpio_test, event); + } + + return consumed; +} + +static bool gpio_test_process_left(GpioTest* gpio_test) { + with_view_model( + gpio_test->view, + GpioTestModel * model, + { + if(model->pin_idx) { + model->pin_idx--; + } + }, + true); + return true; +} + +static bool gpio_test_process_right(GpioTest* gpio_test) { + with_view_model( + gpio_test->view, + GpioTestModel * model, + { + if(model->pin_idx < gpio_items_get_count(model->gpio_items)) { + model->pin_idx++; + } + }, + true); + return true; +} + +static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event) { + bool consumed = false; + + with_view_model( + gpio_test->view, + GpioTestModel * model, + { + if(event->type == InputTypePress) { + if(model->pin_idx < gpio_items_get_count(model->gpio_items)) { + gpio_items_set_pin(model->gpio_items, model->pin_idx, true); + } else { + gpio_items_set_all_pins(model->gpio_items, true); + } + consumed = true; + } else if(event->type == InputTypeRelease) { + if(model->pin_idx < gpio_items_get_count(model->gpio_items)) { + gpio_items_set_pin(model->gpio_items, model->pin_idx, false); + } else { + gpio_items_set_all_pins(model->gpio_items, false); + } + consumed = true; + } + gpio_test->callback(event->type, gpio_test->context); + }, + true); + + return consumed; +} + +GpioTest* gpio_test_alloc(GPIOItems* gpio_items) { + GpioTest* gpio_test = malloc(sizeof(GpioTest)); + + gpio_test->view = view_alloc(); + view_allocate_model(gpio_test->view, ViewModelTypeLocking, sizeof(GpioTestModel)); + + with_view_model( + gpio_test->view, GpioTestModel * model, { model->gpio_items = gpio_items; }, false); + + view_set_context(gpio_test->view, gpio_test); + view_set_draw_callback(gpio_test->view, gpio_test_draw_callback); + view_set_input_callback(gpio_test->view, gpio_test_input_callback); + + return gpio_test; +} + +void gpio_test_free(GpioTest* gpio_test) { + furi_assert(gpio_test); + view_free(gpio_test->view); + free(gpio_test); +} + +View* gpio_test_get_view(GpioTest* gpio_test) { + furi_assert(gpio_test); + return gpio_test->view; +} + +void gpio_test_set_ok_callback(GpioTest* gpio_test, GpioTestOkCallback callback, void* context) { + furi_assert(gpio_test); + furi_assert(callback); + with_view_model( + gpio_test->view, + GpioTestModel * model, + { + UNUSED(model); + gpio_test->callback = callback; + gpio_test->context = context; + }, + false); +} diff --git a/applications/gpio/views/gpio_test.h b/applications/main/gpio/views/gpio_test.h old mode 100755 new mode 100644 similarity index 80% rename from applications/gpio/views/gpio_test.h rename to applications/main/gpio/views/gpio_test.h index 5cbd11e82cf..38fcbc5fb09 --- a/applications/gpio/views/gpio_test.h +++ b/applications/main/gpio/views/gpio_test.h @@ -1,11 +1,13 @@ #pragma once +#include "../gpio_items.h" + #include typedef struct GpioTest GpioTest; typedef void (*GpioTestOkCallback)(InputType type, void* context); -GpioTest* gpio_test_alloc(); +GpioTest* gpio_test_alloc(GPIOItems* gpio_items); void gpio_test_free(GpioTest* gpio_test); diff --git a/applications/gpio/views/gpio_usb_uart.c b/applications/main/gpio/views/gpio_usb_uart.c similarity index 92% rename from applications/gpio/views/gpio_usb_uart.c rename to applications/main/gpio/views/gpio_usb_uart.c index d2d371b6822..3234309a294 100644 --- a/applications/gpio/views/gpio_usb_uart.c +++ b/applications/main/gpio/views/gpio_usb_uart.c @@ -1,6 +1,6 @@ #include "../usb_uart_bridge.h" #include "../gpio_app_i.h" -#include "furi_hal.h" +#include #include struct GpioUsbUart { @@ -54,7 +54,7 @@ static void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str_aligned(canvas, 116, 24, AlignRight, AlignBottom, temp_str); } else { canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, "KB."); + canvas_draw_str_aligned(canvas, 127, 24, AlignRight, AlignBottom, "KiB."); canvas_set_font(canvas, FontKeyboard); snprintf(temp_str, 18, "%lu", model->tx_cnt / 1024); canvas_draw_str_aligned(canvas, 111, 24, AlignRight, AlignBottom, temp_str); @@ -68,7 +68,7 @@ static void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str_aligned(canvas, 116, 41, AlignRight, AlignBottom, temp_str); } else { canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 127, 41, AlignRight, AlignBottom, "KB."); + canvas_draw_str_aligned(canvas, 127, 41, AlignRight, AlignBottom, "KiB."); canvas_set_font(canvas, FontKeyboard); snprintf(temp_str, 18, "%lu", model->rx_cnt / 1024); canvas_draw_str_aligned(canvas, 111, 41, AlignRight, AlignBottom, temp_str); @@ -80,9 +80,9 @@ static void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) { canvas_draw_icon(canvas, 48, 14, &I_ArrowUpEmpty_14x15); if(model->rx_active) - canvas_draw_icon(canvas, 48, 34, &I_ArrowDownFilled_14x15); + canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180); else - canvas_draw_icon(canvas, 48, 34, &I_ArrowDownEmpty_14x15); + canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpEmpty_14x15, IconRotation180); } static bool gpio_usb_uart_input_callback(InputEvent* event, void* context) { @@ -129,12 +129,14 @@ void gpio_usb_uart_set_callback(GpioUsbUart* usb_uart, GpioUsbUartCallback callb furi_assert(callback); with_view_model( - usb_uart->view, (GpioUsbUartModel * model) { + usb_uart->view, + GpioUsbUartModel * model, + { UNUSED(model); usb_uart->callback = callback; usb_uart->context = context; - return false; - }); + }, + false); } void gpio_usb_uart_update_state(GpioUsbUart* instance, UsbUartConfig* cfg, UsbUartState* st) { @@ -143,7 +145,9 @@ void gpio_usb_uart_update_state(GpioUsbUart* instance, UsbUartConfig* cfg, UsbUa furi_assert(st); with_view_model( - instance->view, (GpioUsbUartModel * model) { + instance->view, + GpioUsbUartModel * model, + { model->baudrate = st->baudrate_cur; model->vcp_port = cfg->vcp_ch; model->tx_pin = (cfg->uart_ch == 0) ? (13) : (15); @@ -152,6 +156,6 @@ void gpio_usb_uart_update_state(GpioUsbUart* instance, UsbUartConfig* cfg, UsbUa model->rx_active = (model->rx_cnt != st->rx_cnt); model->tx_cnt = st->tx_cnt; model->rx_cnt = st->rx_cnt; - return true; - }); + }, + true); } diff --git a/applications/gpio/views/gpio_usb_uart.h b/applications/main/gpio/views/gpio_usb_uart.h old mode 100755 new mode 100644 similarity index 100% rename from applications/gpio/views/gpio_usb_uart.h rename to applications/main/gpio/views/gpio_usb_uart.h diff --git a/applications/main/ibutton/application.fam b/applications/main/ibutton/application.fam new file mode 100644 index 00000000000..01c02ec23d6 --- /dev/null +++ b/applications/main/ibutton/application.fam @@ -0,0 +1,22 @@ +App( + appid="ibutton", + name="iButton", + apptype=FlipperAppType.MENUEXTERNAL, + targets=["f7"], + entry_point="ibutton_app", + icon="A_iButton_14", + stack_size=2 * 1024, + order=60, + fap_libs=["assets"], + fap_icon="icon.png", + fap_category="iButton", +) + +App( + appid="ibutton_start", + apptype=FlipperAppType.STARTUP, + targets=["f7"], + entry_point="ibutton_on_system_start", + sources=["ibutton_cli.c"], + order=60, +) diff --git a/applications/main/ibutton/ibutton.c b/applications/main/ibutton/ibutton.c new file mode 100644 index 00000000000..afd51f7c9e1 --- /dev/null +++ b/applications/main/ibutton/ibutton.c @@ -0,0 +1,314 @@ +#include "ibutton_i.h" + +#include +#include + +#define TAG "IButtonApp" + +static const NotificationSequence sequence_blink_set_yellow = { + &message_blink_set_color_yellow, + NULL, +}; + +static const NotificationSequence sequence_blink_set_magenta = { + &message_blink_set_color_magenta, + NULL, +}; + +static const NotificationSequence* ibutton_notification_sequences[] = { + &sequence_error, + &sequence_success, + &sequence_blink_start_cyan, + &sequence_blink_start_magenta, + &sequence_blink_set_yellow, + &sequence_blink_set_magenta, + &sequence_set_red_255, + &sequence_reset_red, + &sequence_set_green_255, + &sequence_reset_green, + &sequence_blink_stop, +}; + +static void ibutton_make_app_folder(iButton* ibutton) { + 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"); + } + + furi_record_close(RECORD_STORAGE); +} + +static void ibutton_rpc_command_callback(const RpcAppSystemEvent* event, void* context) { + furi_assert(context); + iButton* ibutton = context; + + if(event->type == RpcAppEventTypeSessionClose) { + view_dispatcher_send_custom_event( + ibutton->view_dispatcher, iButtonCustomEventRpcSessionClose); + rpc_system_app_set_callback(ibutton->rpc, NULL, NULL); + ibutton->rpc = NULL; + } else if(event->type == RpcAppEventTypeAppExit) { + view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit); + } else if(event->type == RpcAppEventTypeLoadFile) { + furi_assert(event->data.type == RpcAppSystemEventDataTypeString); + furi_string_set(ibutton->file_path, event->data.string); + view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoadFile); + } else { + rpc_system_app_confirm(ibutton->rpc, false); + } +} + +bool ibutton_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + iButton* ibutton = context; + return scene_manager_handle_custom_event(ibutton->scene_manager, event); +} + +bool ibutton_back_event_callback(void* context) { + furi_assert(context); + iButton* ibutton = context; + return scene_manager_handle_back_event(ibutton->scene_manager); +} + +void ibutton_tick_event_callback(void* context) { + furi_assert(context); + iButton* ibutton = context; + scene_manager_handle_tick_event(ibutton->scene_manager); +} + +iButton* ibutton_alloc() { + iButton* ibutton = malloc(sizeof(iButton)); + + ibutton->file_path = furi_string_alloc(); + + ibutton->scene_manager = scene_manager_alloc(&ibutton_scene_handlers, ibutton); + + ibutton->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(ibutton->view_dispatcher); + view_dispatcher_set_event_callback_context(ibutton->view_dispatcher, ibutton); + view_dispatcher_set_custom_event_callback( + ibutton->view_dispatcher, ibutton_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + ibutton->view_dispatcher, ibutton_back_event_callback); + view_dispatcher_set_tick_event_callback( + ibutton->view_dispatcher, ibutton_tick_event_callback, 100); + + ibutton->gui = furi_record_open(RECORD_GUI); + + ibutton->dialogs = furi_record_open(RECORD_DIALOGS); + ibutton->notifications = furi_record_open(RECORD_NOTIFICATION); + + 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( + ibutton->view_dispatcher, iButtonViewSubmenu, submenu_get_view(ibutton->submenu)); + + ibutton->byte_input = byte_input_alloc(); + view_dispatcher_add_view( + ibutton->view_dispatcher, iButtonViewByteInput, byte_input_get_view(ibutton->byte_input)); + + ibutton->text_input = text_input_alloc(); + view_dispatcher_add_view( + ibutton->view_dispatcher, iButtonViewTextInput, text_input_get_view(ibutton->text_input)); + + ibutton->popup = popup_alloc(); + view_dispatcher_add_view( + ibutton->view_dispatcher, iButtonViewPopup, popup_get_view(ibutton->popup)); + + ibutton->widget = widget_alloc(); + view_dispatcher_add_view( + ibutton->view_dispatcher, iButtonViewWidget, widget_get_view(ibutton->widget)); + + ibutton->loading = loading_alloc(); + view_dispatcher_add_view( + ibutton->view_dispatcher, iButtonViewLoading, loading_get_view(ibutton->loading)); + + return ibutton; +} + +void ibutton_free(iButton* ibutton) { + furi_assert(ibutton); + + view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewLoading); + loading_free(ibutton->loading); + + view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewWidget); + widget_free(ibutton->widget); + + view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewPopup); + popup_free(ibutton->popup); + + view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewTextInput); + text_input_free(ibutton->text_input); + + view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewByteInput); + byte_input_free(ibutton->byte_input); + + view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewSubmenu); + submenu_free(ibutton->submenu); + + view_dispatcher_free(ibutton->view_dispatcher); + scene_manager_free(ibutton->scene_manager); + + furi_record_close(RECORD_NOTIFICATION); + ibutton->notifications = NULL; + + furi_record_close(RECORD_DIALOGS); + ibutton->dialogs = NULL; + + furi_record_close(RECORD_GUI); + ibutton->gui = NULL; + + 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_load_key(iButton* ibutton, bool show_error) { + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewLoading); + + const bool success = ibutton_protocols_load( + ibutton->protocols, ibutton->key, furi_string_get_cstr(ibutton->file_path)); + + if(success) { + 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); + } else if(show_error) { + dialog_message_show_storage_error(ibutton->dialogs, "Cannot load\nkey file"); + } + + return success; +} + +bool ibutton_select_and_load_key(iButton* ibutton) { + DialogsFileBrowserOptions browser_options; + bool success = false; + dialog_file_browser_set_basic_options( + &browser_options, IBUTTON_APP_FILENAME_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); + } + + do { + if(!dialog_file_browser_show( + ibutton->dialogs, ibutton->file_path, ibutton->file_path, &browser_options)) + break; + success = ibutton_load_key(ibutton, true); + } while(!success); + + return success; +} + +bool ibutton_save_key(iButton* ibutton) { + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewLoading); + + ibutton_make_app_folder(ibutton); + + iButtonKey* key = ibutton->key; + const bool success = + ibutton_protocols_save(ibutton->protocols, key, furi_string_get_cstr(ibutton->file_path)); + + if(!success) { + dialog_message_show_storage_error(ibutton->dialogs, "Cannot save\nkey file"); + } + + return success; +} + +bool ibutton_delete_key(iButton* ibutton) { + bool result = false; + + 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_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) { + furi_assert(message < sizeof(ibutton_notification_sequences) / sizeof(NotificationSequence*)); + notification_message(ibutton->notifications, ibutton_notification_sequences[message]); +} + +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; + + if((arg != NULL) && (strlen(arg) != 0)) { + if(sscanf(arg, "RPC %lX", (uint32_t*)&ibutton->rpc) == 1) { + FURI_LOG_D(TAG, "Running in RPC mode"); + + 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*)arg); + key_loaded = ibutton_load_key(ibutton, true); + } + } + + 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); + if(key_loaded) { //-V547 + scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); + dolphin_deed(DolphinDeedIbuttonEmulate); + } else { + scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart); + } + } + + view_dispatcher_run(ibutton->view_dispatcher); + + 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/ibutton/ibutton.h b/applications/main/ibutton/ibutton.h similarity index 100% rename from applications/ibutton/ibutton.h rename to applications/main/ibutton/ibutton.h diff --git a/applications/main/ibutton/ibutton_cli.c b/applications/main/ibutton/ibutton_cli.c new file mode 100644 index 00000000000..54bc808b5ee --- /dev/null +++ b/applications/main/ibutton/ibutton_cli.c @@ -0,0 +1,254 @@ +#include +#include + +#include +#include + +#include +#include +#include + +static void ibutton_cli(Cli* cli, FuriString* args, void* context); + +// app cli function +void ibutton_on_system_start() { +#ifdef SRV_CLI + Cli* cli = furi_record_open(RECORD_CLI); + cli_add_command(cli, "ikey", CliCommandFlagDefault, ibutton_cli, cli); + furi_record_close(RECORD_CLI); +#else + UNUSED(ibutton_cli); +#endif +} + +static void ibutton_cli_print_usage() { + printf("Usage:\r\n"); + printf("ikey read\r\n"); + printf("ikey emulate \r\n"); + printf("ikey write Dallas \r\n"); + printf("\t choose from:\r\n"); + printf("\tDallas (8 bytes key_data)\r\n"); + printf("\tCyfral (2 bytes key_data)\r\n"); + printf("\tMetakom (4 bytes key_data), must contain correct parity\r\n"); + printf("\t are hex-formatted\r\n"); +}; + +static bool ibutton_cli_parse_key(iButtonProtocols* protocols, iButtonKey* key, FuriString* args) { + bool result = false; + FuriString* name = furi_string_alloc(); + + 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; +} + +static void ibutton_cli_print_key(iButtonProtocols* protocols, iButtonKey* key) { + const char* name = ibutton_protocols_get_name(protocols, ibutton_key_get_protocol_id(key)); + + 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"); +} + +#define EVENT_FLAG_IBUTTON_COMPLETE (1 << 0) + +static void ibutton_cli_worker_read_cb(void* context) { + furi_assert(context); + FuriEventFlag* event = context; + furi_event_flag_set(event, EVENT_FLAG_IBUTTON_COMPLETE); +} + +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); + ibutton_worker_read_set_callback(worker, ibutton_cli_worker_read_cb, event); + + 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(protocols, key); + break; + } + + if(cli_cmd_interrupt_received(cli)) break; + } + + ibutton_worker_stop(worker); + ibutton_worker_stop_thread(worker); + + ibutton_key_free(key); + ibutton_worker_free(worker); + ibutton_protocols_free(protocols); + + furi_event_flag_free(event); +}; + +typedef struct { + FuriEventFlag* event; + iButtonWorkerWriteResult result; +} iButtonWriteContext; + +static void ibutton_cli_worker_write_cb(void* context, iButtonWorkerWriteResult result) { + furi_assert(context); + iButtonWriteContext* write_context = (iButtonWriteContext*)context; + write_context->result = result; + furi_event_flag_set(write_context->event, EVENT_FLAG_IBUTTON_COMPLETE); +} + +void ibutton_cli_write(Cli* cli, FuriString* args) { + 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(); + + ibutton_worker_start_thread(worker); + ibutton_worker_write_set_callback(worker, ibutton_cli_worker_write_cb, &write_context); + + do { + if(!ibutton_cli_parse_key(protocols, key, args)) { + ibutton_cli_print_usage(); + break; + } + + if(!(ibutton_protocols_get_features(protocols, ibutton_key_get_protocol_id(key)) & + iButtonProtocolFeatureWriteBlank)) { + ibutton_cli_print_usage(); + break; + } + + printf("Writing key "); + ibutton_cli_print_key(protocols, key); + printf("Press Ctrl+C to abort\r\n"); + + 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); + + if(flags & EVENT_FLAG_IBUTTON_COMPLETE) { + if(write_context.result == iButtonWorkerWriteSameKey || + write_context.result == iButtonWorkerWriteOK) { + printf("Write success\r\n"); + break; + } else if(write_context.result == iButtonWorkerWriteCannotWrite) { + printf("Write fail\r\n"); + break; + } + } + + if(cli_cmd_interrupt_received(cli)) break; + } + } while(false); + + ibutton_worker_stop(worker); + ibutton_worker_stop_thread(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) { + iButtonProtocols* protocols = ibutton_protocols_alloc(); + iButtonWorker* worker = ibutton_worker_alloc(protocols); + iButtonKey* key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(protocols)); + + ibutton_worker_start_thread(worker); + + do { + if(!ibutton_cli_parse_key(protocols, key, args)) { + ibutton_cli_print_usage(); + break; + } + + printf("Emulating 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); + }; + + } while(false); + + ibutton_worker_stop(worker); + ibutton_worker_stop_thread(worker); + + ibutton_key_free(key); + ibutton_worker_free(worker); + ibutton_protocols_free(protocols); +}; + +void ibutton_cli(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(context); + FuriString* cmd; + cmd = furi_string_alloc(); + + if(!args_read_string_and_trim(args, cmd)) { + furi_string_free(cmd); + ibutton_cli_print_usage(); + return; + } + + if(furi_string_cmp_str(cmd, "read") == 0) { + ibutton_cli_read(cli); + } else if(furi_string_cmp_str(cmd, "write") == 0) { + ibutton_cli_write(cli, args); + } else if(furi_string_cmp_str(cmd, "emulate") == 0) { + ibutton_cli_emulate(cli, args); + } else { + ibutton_cli_print_usage(); + } + + furi_string_free(cmd); +} diff --git a/applications/main/ibutton/ibutton_custom_event.h b/applications/main/ibutton/ibutton_custom_event.h new file mode 100644 index 00000000000..b0246d31094 --- /dev/null +++ b/applications/main/ibutton/ibutton_custom_event.h @@ -0,0 +1,21 @@ +#pragma once + +typedef enum { + // Reserve first 100 events for button types and indexes, starting from 0 + iButtonCustomEventReserved = 100, + + iButtonCustomEventBack, + iButtonCustomEventTextEditResult, + iButtonCustomEventByteEditChanged, + iButtonCustomEventByteEditResult, + iButtonCustomEventWorkerEmulated, + iButtonCustomEventWorkerRead, + iButtonCustomEventWorkerWriteOK, + iButtonCustomEventWorkerWriteSameKey, + iButtonCustomEventWorkerWriteNoDetect, + iButtonCustomEventWorkerWriteCannotWrite, + + iButtonCustomEventRpcLoadFile, + iButtonCustomEventRpcExit, + iButtonCustomEventRpcSessionClose, +} iButtonCustomEvent; diff --git a/applications/main/ibutton/ibutton_i.h b/applications/main/ibutton/ibutton_i.h new file mode 100644 index 00000000000..c6a35f888b4 --- /dev/null +++ b/applications/main/ibutton/ibutton_i.h @@ -0,0 +1,100 @@ +#pragma once + +#include "ibutton.h" + +#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_APP_FOLDER ANY_PATH("ibutton") +#define IBUTTON_APP_FILENAME_PREFIX "iBtn" +#define IBUTTON_APP_FILENAME_EXTENSION ".ibtn" + +#define IBUTTON_KEY_NAME_SIZE 22 + +typedef enum { + iButtonWriteModeInvalid, + iButtonWriteModeBlank, + iButtonWriteModeCopy, +} iButtonWriteMode; + +struct iButton { + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + + Gui* gui; + Storage* storage; + DialogsApp* dialogs; + NotificationApp* notifications; + RpcAppSystem* rpc; + + iButtonKey* key; + iButtonWorker* worker; + iButtonProtocols* protocols; + iButtonWriteMode write_mode; + + FuriString* file_path; + char key_name[IBUTTON_KEY_NAME_SIZE + 1]; + + Submenu* submenu; + ByteInput* byte_input; + TextInput* text_input; + Popup* popup; + Widget* widget; + Loading* loading; +}; + +typedef enum { + iButtonViewSubmenu, + iButtonViewByteInput, + iButtonViewTextInput, + iButtonViewPopup, + iButtonViewWidget, + iButtonViewLoading, +} iButtonView; + +typedef enum { + iButtonNotificationMessageError, + iButtonNotificationMessageSuccess, + iButtonNotificationMessageReadStart, + iButtonNotificationMessageEmulateStart, + iButtonNotificationMessageYellowBlink, + iButtonNotificationMessageEmulateBlink, + iButtonNotificationMessageRedOn, + iButtonNotificationMessageRedOff, + iButtonNotificationMessageGreenOn, + iButtonNotificationMessageGreenOff, + iButtonNotificationMessageBlinkStop, +} iButtonNotificationMessage; + +bool ibutton_select_and_load_key(iButton* ibutton); +bool ibutton_load_key(iButton* ibutton, bool show_error); +bool ibutton_save_key(iButton* ibutton); +bool ibutton_delete_key(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/icon.png b/applications/main/ibutton/icon.png new file mode 100644 index 00000000000..2fdaf123a65 Binary files /dev/null and b/applications/main/ibutton/icon.png differ diff --git a/applications/ibutton/scenes/ibutton_scene.c b/applications/main/ibutton/scenes/ibutton_scene.c similarity index 100% rename from applications/ibutton/scenes/ibutton_scene.c rename to applications/main/ibutton/scenes/ibutton_scene.c diff --git a/applications/ibutton/scenes/ibutton_scene.h b/applications/main/ibutton/scenes/ibutton_scene.h similarity index 100% rename from applications/ibutton/scenes/ibutton_scene.h rename to applications/main/ibutton/scenes/ibutton_scene.h diff --git a/applications/main/ibutton/scenes/ibutton_scene_add_type.c b/applications/main/ibutton/scenes/ibutton_scene_add_type.c new file mode 100644 index 00000000000..968bbac1cf7 --- /dev/null +++ b/applications/main/ibutton/scenes/ibutton_scene_add_type.c @@ -0,0 +1,54 @@ +#include "../ibutton_i.h" + +void ibutton_scene_add_type_on_enter(void* context) { + iButton* ibutton = context; + Submenu* submenu = ibutton->submenu; + + FuriString* tmp = furi_string_alloc(); + + for(uint32_t protocol_id = 0; protocol_id < ibutton_protocols_get_protocol_count(); + ++protocol_id) { + furi_string_printf( + tmp, + "%s %s", + ibutton_protocols_get_manufacturer(ibutton->protocols, protocol_id), + 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) { + const iButtonProtocolId protocol_id = event.event; + + 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; +} + +void ibutton_scene_add_type_on_exit(void* context) { + iButton* ibutton = context; + submenu_reset(ibutton->submenu); +} diff --git a/applications/main/ibutton/scenes/ibutton_scene_add_value.c b/applications/main/ibutton/scenes/ibutton_scene_add_value.c new file mode 100644 index 00000000000..71b852115e6 --- /dev/null +++ b/applications/main/ibutton/scenes/ibutton_scene_add_value.c @@ -0,0 +1,61 @@ +#include "../ibutton_i.h" + +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; + byte_input_set_header_text(ibutton->byte_input, "Enter the 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, + ibutton_scene_add_type_byte_changed_callback, + context, + editable_data.ptr, + editable_data.size); + + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewByteInput); +} + +bool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) { + iButton* ibutton = context; + SceneManager* scene_manager = ibutton->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == iButtonCustomEventByteEditResult) { + 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, true)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + scene_manager, iButtonSceneStart); + } + } + } + + return consumed; +} + +void ibutton_scene_add_value_on_exit(void* context) { + iButton* ibutton = context; + + byte_input_set_result_callback(ibutton->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(ibutton->byte_input, NULL); +} diff --git a/applications/ibutton/scenes/ibutton_scene_config.h b/applications/main/ibutton/scenes/ibutton_scene_config.h similarity index 88% rename from applications/ibutton/scenes/ibutton_scene_config.h rename to applications/main/ibutton/scenes/ibutton_scene_config.h index 87fa1a036a7..79f6791b3b9 100644 --- a/applications/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 new file mode 100644 index 00000000000..b293af952ce --- /dev/null +++ b/applications/main/ibutton/scenes/ibutton_scene_delete_confirm.c @@ -0,0 +1,54 @@ +#include "../ibutton_i.h" +#include + +void ibutton_scene_delete_confirm_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, "Back", ibutton_widget_callback, context); + widget_add_button_element( + widget, GuiButtonTypeRight, "Delete", ibutton_widget_callback, context); + + furi_string_printf(tmp, "\e#Delete %s?\e#", ibutton->key_name); + widget_add_text_box_element( + widget, 0, 0, 128, 23, AlignCenter, AlignCenter, furi_string_get_cstr(tmp), false); + + furi_string_reset(tmp); + ibutton_protocols_render_brief_data(ibutton->protocols, key, tmp); + + widget_add_string_multiline_element( + widget, 128 / 2, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp)); + + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); + furi_string_free(tmp); +} + +bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) { + iButton* ibutton = context; + SceneManager* scene_manager = ibutton->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + 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); + } + } else if(event.event == GuiButtonTypeLeft) { + scene_manager_previous_scene(scene_manager); + } + } + + return consumed; +} + +void ibutton_scene_delete_confirm_on_exit(void* context) { + iButton* ibutton = context; + widget_reset(ibutton->widget); +} diff --git a/applications/ibutton/scenes/ibutton_scene_delete_success.c b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c similarity index 86% rename from applications/ibutton/scenes/ibutton_scene_delete_success.c rename to applications/main/ibutton/scenes/ibutton_scene_delete_success.c index 8b7d9dfc02f..9ff165e4a31 100644 --- a/applications/ibutton/scenes/ibutton_scene_delete_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c @@ -39,10 +39,5 @@ void ibutton_scene_delete_success_on_exit(void* context) { iButton* ibutton = context; Popup* popup = ibutton->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); + popup_reset(popup); } diff --git a/applications/main/ibutton/scenes/ibutton_scene_emulate.c b/applications/main/ibutton/scenes/ibutton_scene_emulate.c new file mode 100644 index 00000000000..713b8331c36 --- /dev/null +++ b/applications/main/ibutton/scenes/ibutton_scene_emulate.c @@ -0,0 +1,77 @@ +#include "../ibutton_i.h" +#include +#include + +#define EMULATE_TIMEOUT_TICKS 10 + +static void ibutton_scene_emulate_callback(void* context, bool emulated) { + iButton* ibutton = context; + if(emulated) { + view_dispatcher_send_custom_event( + ibutton->view_dispatcher, iButtonCustomEventWorkerEmulated); + } +} + +void ibutton_scene_emulate_on_enter(void* context) { + iButton* ibutton = context; + iButtonKey* key = ibutton->key; + + Widget* widget = ibutton->widget; + FuriString* tmp = furi_string_alloc(); + + widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44); + + 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, 88, 10, AlignCenter, AlignTop, FontPrimary, "iButton\nemulating"); + + 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) { + iButton* ibutton = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeTick) { + uint32_t cnt = scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneEmulate); + if(cnt > 0) { + if(--cnt == 0) { + ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateBlink); + } + scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneEmulate, cnt); + } + consumed = true; + } else if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == iButtonCustomEventWorkerEmulated) { + if(scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneEmulate) == 0) { + ibutton_notification_message(ibutton, iButtonNotificationMessageYellowBlink); + } + scene_manager_set_scene_state( + ibutton->scene_manager, iButtonSceneEmulate, EMULATE_TIMEOUT_TICKS); + } + } + + return consumed; +} + +void ibutton_scene_emulate_on_exit(void* context) { + iButton* ibutton = context; + ibutton_worker_stop(ibutton->worker); + widget_reset(ibutton->widget); + ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); +} diff --git a/applications/ibutton/scenes/ibutton_scene_exit_confirm.c b/applications/main/ibutton/scenes/ibutton_scene_exit_confirm.c similarity index 98% rename from applications/ibutton/scenes/ibutton_scene_exit_confirm.c rename to applications/main/ibutton/scenes/ibutton_scene_exit_confirm.c index 2367e12177e..9029a4b7b02 100644 --- a/applications/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 new file mode 100644 index 00000000000..cf44d6a8654 --- /dev/null +++ b/applications/main/ibutton/scenes/ibutton_scene_info.c @@ -0,0 +1,54 @@ +#include "../ibutton_i.h" + +void ibutton_scene_info_on_enter(void* context) { + iButton* ibutton = context; + iButtonKey* key = ibutton->key; + Widget* widget = ibutton->widget; + + const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key); + + FuriString* tmp = furi_string_alloc(); + + furi_string_printf( + tmp, + "\e#%s [%s]\e#", + ibutton->key_name, + ibutton_protocols_get_name(ibutton->protocols, protocol_id)); + + widget_add_text_box_element( + widget, 0, 2, 128, 12, AlignLeft, AlignTop, furi_string_get_cstr(tmp), true); + + 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)); + + if(ibutton_protocols_get_features(ibutton->protocols, protocol_id) & + iButtonProtocolFeatureExtData) { + widget_add_button_element( + widget, GuiButtonTypeRight, "More", ibutton_widget_callback, context); + } + + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); + furi_string_free(tmp); +} + +bool ibutton_scene_info_on_event(void* context, SceneManagerEvent event) { + 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; + widget_reset(ibutton->widget); +} diff --git a/applications/main/ibutton/scenes/ibutton_scene_read.c b/applications/main/ibutton/scenes/ibutton_scene_read.c new file mode 100644 index 00000000000..f360c3ac43a --- /dev/null +++ b/applications/main/ibutton/scenes/ibutton_scene_read.c @@ -0,0 +1,61 @@ +#include "../ibutton_i.h" +#include + +static void ibutton_scene_read_callback(void* context) { + iButton* ibutton = context; + view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventWorkerRead); +} + +void ibutton_scene_read_on_enter(void* context) { + iButton* ibutton = context; + Popup* popup = ibutton->popup; + iButtonKey* key = ibutton->key; + iButtonWorker* worker = ibutton->worker; + + popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom); + popup_set_text(popup, "Apply key to\nFlipper's back", 95, 30, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 5, &I_DolphinWait_61x59); + + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); + + ibutton_worker_read_set_callback(worker, ibutton_scene_read_callback, ibutton); + ibutton_worker_read_start(worker, key); + + ibutton_notification_message(ibutton, iButtonNotificationMessageReadStart); +} + +bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) { + iButton* ibutton = context; + SceneManager* scene_manager = ibutton->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeTick) { + consumed = true; + } else if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == iButtonCustomEventWorkerRead) { + 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); + } + } + } + + return consumed; +} + +void ibutton_scene_read_on_exit(void* context) { + iButton* ibutton = context; + Popup* popup = ibutton->popup; + 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); + + ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); +} 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 00000000000..e966384bfc0 --- /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 new file mode 100644 index 00000000000..1555f2cc20c --- /dev/null +++ b/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c @@ -0,0 +1,100 @@ +#include "../ibutton_i.h" +#include + +typedef enum { + SubmenuIndexSave, + SubmenuIndexEmulate, + SubmenuIndexViewData, + SubmenuIndexWriteBlank, + SubmenuIndexWriteCopy, +} SubmenuIndex; + +void ibutton_scene_read_key_menu_submenu_callback(void* context, uint32_t index) { + iButton* ibutton = context; + view_dispatcher_send_custom_event(ibutton->view_dispatcher, index); +} + +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( + submenu, + "Emulate", + SubmenuIndexEmulate, + ibutton_scene_read_key_menu_submenu_callback, + ibutton); + + if(features & iButtonProtocolFeatureExtData) { + submenu_add_item( + submenu, + "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(scene_manager, iButtonSceneReadKeyMenu, event.event); + consumed = true; + + if(event.event == SubmenuIndexSave) { + scene_manager_next_scene(scene_manager, iButtonSceneSaveName); + } else if(event.event == SubmenuIndexEmulate) { + scene_manager_next_scene(scene_manager, iButtonSceneEmulate); + dolphin_deed(DolphinDeedIbuttonEmulate); + } 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; +} + +void ibutton_scene_read_key_menu_on_exit(void* context) { + iButton* ibutton = context; + submenu_reset(ibutton->submenu); +} diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_success.c b/applications/main/ibutton/scenes/ibutton_scene_read_success.c new file mode 100644 index 00000000000..2e50bc99647 --- /dev/null +++ b/applications/main/ibutton/scenes/ibutton_scene_read_success.c @@ -0,0 +1,66 @@ +#include "../ibutton_i.h" + +#include + +void ibutton_scene_read_success_on_enter(void* context) { + iButton* ibutton = context; + iButtonKey* key = ibutton->key; + Widget* widget = ibutton->widget; + + FuriString* tmp = furi_string_alloc(); + + const iButtonProtocolId protocol_id = ibutton_key_get_protocol_id(key); + + 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) { + 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 == GuiButtonTypeRight) { + scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu); + } else if(event.event == GuiButtonTypeLeft) { + scene_manager_next_scene(scene_manager, iButtonSceneRetryConfirm); + } + } + + return consumed; +} + +void ibutton_scene_read_success_on_exit(void* context) { + iButton* ibutton = context; + + widget_reset(ibutton->widget); + + ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOff); +} diff --git a/applications/ibutton/scenes/ibutton_scene_retry_confirm.c b/applications/main/ibutton/scenes/ibutton_scene_retry_confirm.c similarity index 98% rename from applications/ibutton/scenes/ibutton_scene_retry_confirm.c rename to applications/main/ibutton/scenes/ibutton_scene_retry_confirm.c index 7f8c95b1e88..34de5b8778b 100644 --- a/applications/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 new file mode 100644 index 00000000000..f4f193a47e8 --- /dev/null +++ b/applications/main/ibutton/scenes/ibutton_scene_rpc.c @@ -0,0 +1,64 @@ +#include "../ibutton_i.h" + +void ibutton_scene_rpc_on_enter(void* context) { + iButton* ibutton = context; + Popup* popup = ibutton->popup; + + popup_set_header(popup, "iButton", 82, 28, AlignCenter, AlignBottom); + popup_set_text(popup, "RPC mode", 82, 32, AlignCenter, AlignTop); + + popup_set_icon(popup, 2, 14, &I_iButtonKey_49x44); + + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); + + notification_message(ibutton->notifications, &sequence_display_backlight_on); +} + +bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) { + iButton* ibutton = context; + Popup* popup = ibutton->popup; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + + if(event.event == iButtonCustomEventRpcLoadFile) { + bool result = false; + + if(ibutton_load_key(ibutton, false)) { + 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); + + result = true; + } + + rpc_system_app_confirm(ibutton->rpc, result); + + } else if(event.event == iButtonCustomEventRpcExit) { + rpc_system_app_confirm(ibutton->rpc, 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); + } + } + + return consumed; +} + +void ibutton_scene_rpc_on_exit(void* context) { + iButton* ibutton = context; + Popup* popup = ibutton->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); + + ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); +} diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_name.c b/applications/main/ibutton/scenes/ibutton_scene_save_name.c new file mode 100644 index 00000000000..e6236dc3592 --- /dev/null +++ b/applications/main/ibutton/scenes/ibutton_scene_save_name.c @@ -0,0 +1,88 @@ +#include "../ibutton_i.h" + +#include +#include + +#include + +static void ibutton_scene_save_name_text_input_callback(void* context) { + iButton* ibutton = context; + view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventTextEditResult); +} + +void ibutton_scene_save_name_on_enter(void* context) { + iButton* ibutton = context; + TextInput* text_input = ibutton->text_input; + + const bool is_new_file = furi_string_empty(ibutton->file_path); + + if(is_new_file) { + name_generator_make_auto( + ibutton->key_name, IBUTTON_KEY_NAME_SIZE, IBUTTON_APP_FILENAME_PREFIX); + } + + text_input_set_header_text(text_input, "Name the key"); + text_input_set_result_callback( + text_input, + ibutton_scene_save_name_text_input_callback, + ibutton, + ibutton->key_name, + IBUTTON_KEY_NAME_SIZE, + is_new_file); + + ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( + IBUTTON_APP_FOLDER, IBUTTON_APP_FILENAME_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); +} + +bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) { + iButton* ibutton = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == iButtonCustomEventTextEditResult) { + furi_string_printf( + ibutton->file_path, + "%s/%s%s", + IBUTTON_APP_FOLDER, + ibutton->key_name, + IBUTTON_APP_FILENAME_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 + } else if(scene_manager_has_previous_scene( + ibutton->scene_manager, iButtonSceneAddType)) { + dolphin_deed(DolphinDeedIbuttonAdd); + } else { + dolphin_deed(DolphinDeedIbuttonSave); + } + + } else { + const uint32_t possible_scenes[] = { + iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType}; + scene_manager_search_and_switch_to_previous_scene_one_of( + ibutton->scene_manager, possible_scenes, COUNT_OF(possible_scenes)); + } + } + } + + return consumed; +} + +void ibutton_scene_save_name_on_exit(void* context) { + iButton* ibutton = context; + TextInput* text_input = ibutton->text_input; + + void* validator_context = text_input_get_validator_callback_context(text_input); + text_input_set_validator(text_input, NULL, NULL); + validator_is_file_free((ValidatorIsFile*)validator_context); + + text_input_reset(text_input); +} diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_success.c b/applications/main/ibutton/scenes/ibutton_scene_save_success.c new file mode 100644 index 00000000000..8b16d2929a5 --- /dev/null +++ b/applications/main/ibutton/scenes/ibutton_scene_save_success.c @@ -0,0 +1,43 @@ +#include "../ibutton_i.h" + +static void ibutton_scene_save_success_popup_callback(void* context) { + iButton* ibutton = context; + view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventBack); +} + +void ibutton_scene_save_success_on_enter(void* context) { + iButton* ibutton = context; + Popup* popup = ibutton->popup; + + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); + + popup_set_callback(popup, ibutton_scene_save_success_popup_callback); + popup_set_context(popup, ibutton); + popup_set_timeout(popup, 1500); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); +} + +bool ibutton_scene_save_success_on_event(void* context, SceneManagerEvent event) { + iButton* ibutton = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == iButtonCustomEventBack) { + scene_manager_search_and_switch_to_another_scene( + ibutton->scene_manager, iButtonSceneSelectKey); + } + } + + return consumed; +} + +void ibutton_scene_save_success_on_exit(void* context) { + iButton* ibutton = context; + Popup* popup = ibutton->popup; + + popup_reset(popup); +} diff --git a/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c new file mode 100644 index 00000000000..fc0cf42e2ff --- /dev/null +++ b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c @@ -0,0 +1,78 @@ +#include "../ibutton_i.h" +#include + +enum SubmenuIndex { + SubmenuIndexEmulate, + SubmenuIndexWriteBlank, + SubmenuIndexWriteCopy, + SubmenuIndexEdit, + SubmenuIndexDelete, + SubmenuIndexInfo, +}; + +void ibutton_scene_saved_key_menu_on_enter(void* context) { + iButton* ibutton = context; + Submenu* submenu = ibutton->submenu; + + 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 Blank", SubmenuIndexWriteBlank, ibutton_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(scene_manager, iButtonSceneSavedKeyMenu, event.event); + consumed = true; + if(event.event == SubmenuIndexEmulate) { + scene_manager_next_scene(scene_manager, iButtonSceneEmulate); + dolphin_deed(DolphinDeedIbuttonEmulate); + } 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(scene_manager, iButtonSceneAddValue); + } else if(event.event == SubmenuIndexDelete) { + scene_manager_next_scene(scene_manager, iButtonSceneDeleteConfirm); + } else if(event.event == SubmenuIndexInfo) { + 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; +} + +void ibutton_scene_saved_key_menu_on_exit(void* context) { + iButton* ibutton = context; + submenu_reset(ibutton->submenu); +} diff --git a/applications/ibutton/scenes/ibutton_scene_select_key.c b/applications/main/ibutton/scenes/ibutton_scene_select_key.c similarity index 92% rename from applications/ibutton/scenes/ibutton_scene_select_key.c rename to applications/main/ibutton/scenes/ibutton_scene_select_key.c index 32169a9c08d..ebd504b1f13 100644 --- a/applications/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 new file mode 100644 index 00000000000..63a4cf869d3 --- /dev/null +++ b/applications/main/ibutton/scenes/ibutton_scene_start.c @@ -0,0 +1,50 @@ +#include "../ibutton_i.h" +#include "ibutton/scenes/ibutton_scene.h" +#include + +enum SubmenuIndex { + SubmenuIndexRead, + SubmenuIndexSaved, + SubmenuIndexAdd, +}; + +void ibutton_scene_start_on_enter(void* context) { + iButton* ibutton = context; + Submenu* submenu = ibutton->submenu; + + 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)); + + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu); +} + +bool ibutton_scene_start_on_event(void* context, SceneManagerEvent event) { + iButton* ibutton = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(ibutton->scene_manager, iButtonSceneStart, event.event); + consumed = true; + if(event.event == SubmenuIndexRead) { + scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead); + dolphin_deed(DolphinDeedIbuttonRead); + } else if(event.event == SubmenuIndexSaved) { + scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey); + } else if(event.event == SubmenuIndexAdd) { + scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddType); + } + } + + return consumed; +} + +void ibutton_scene_start_on_exit(void* context) { + iButton* ibutton = context; + submenu_reset(ibutton->submenu); +} 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 00000000000..7e063d7ec84 --- /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 new file mode 100644 index 00000000000..63be635069c --- /dev/null +++ b/applications/main/ibutton/scenes/ibutton_scene_write.c @@ -0,0 +1,102 @@ +#include "../ibutton_i.h" + +typedef enum { + iButtonSceneWriteStateDefault, + iButtonSceneWriteStateBlinkYellow, +} iButtonSceneWriteState; + +static inline iButtonCustomEvent + ibutton_scene_write_to_custom_event(iButtonWorkerWriteResult result) { + switch(result) { + case iButtonWorkerWriteOK: + return iButtonCustomEventWorkerWriteOK; + case iButtonWorkerWriteSameKey: + return iButtonCustomEventWorkerWriteSameKey; + case iButtonWorkerWriteNoDetect: + return iButtonCustomEventWorkerWriteNoDetect; + case iButtonWorkerWriteCannotWrite: + return iButtonCustomEventWorkerWriteCannotWrite; + default: + furi_crash(); + } +} + +static void ibutton_scene_write_callback(void* context, iButtonWorkerWriteResult result) { + iButton* ibutton = context; + view_dispatcher_send_custom_event( + ibutton->view_dispatcher, ibutton_scene_write_to_custom_event(result)); +} + +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; + FuriString* tmp = furi_string_alloc(); + + widget_add_icon_element(widget, 3, 10, &I_iButtonKey_49x44); + + furi_string_printf( + tmp, + "%s\n[%s]", + ibutton->key_name, + ibutton_protocols_get_name(ibutton->protocols, protocol_id)); + + 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, 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) { + iButton* ibutton = context; + SceneManager* scene_manager = ibutton->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if((event.event == iButtonCustomEventWorkerWriteOK) || + (event.event == iButtonCustomEventWorkerWriteSameKey)) { + scene_manager_next_scene(scene_manager, iButtonSceneWriteSuccess); + } else if(event.event == iButtonCustomEventWorkerWriteNoDetect) { + ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateBlink); + } else if(event.event == iButtonCustomEventWorkerWriteCannotWrite) { + ibutton_notification_message(ibutton, iButtonNotificationMessageYellowBlink); + } + } + + return consumed; +} + +void ibutton_scene_write_on_exit(void* context) { + iButton* ibutton = context; + ibutton->write_mode = iButtonWriteModeInvalid; + + ibutton_worker_stop(ibutton->worker); + widget_reset(ibutton->widget); + + ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); +} diff --git a/applications/ibutton/scenes/ibutton_scene_write_success.c b/applications/main/ibutton/scenes/ibutton_scene_write_success.c similarity index 89% rename from applications/ibutton/scenes/ibutton_scene_write_success.c rename to applications/main/ibutton/scenes/ibutton_scene_write_success.c index 3acb1dea04c..17cd53d08d6 100644 --- a/applications/ibutton/scenes/ibutton_scene_write_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_write_success.c @@ -43,10 +43,5 @@ void ibutton_scene_write_success_on_exit(void* context) { iButton* ibutton = context; Popup* popup = ibutton->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); + popup_reset(popup); } diff --git a/applications/main/infrared/application.fam b/applications/main/infrared/application.fam new file mode 100644 index 00000000000..575bebbe481 --- /dev/null +++ b/applications/main/infrared/application.fam @@ -0,0 +1,28 @@ +App( + appid="infrared", + name="Infrared", + apptype=FlipperAppType.MENUEXTERNAL, + entry_point="infrared_app", + targets=["f7"], + icon="A_Infrared_14", + stack_size=3 * 1024, + order=40, + sources=["*.c", "!infrared_cli.c"], + resources="resources", + fap_libs=["assets"], + fap_icon="icon.png", + fap_category="Infrared", +) + +App( + appid="infrared_start", + apptype=FlipperAppType.STARTUP, + targets=["f7"], + entry_point="infrared_on_system_start", + sources=[ + "infrared_cli.c", + "infrared_brute_force.c", + "infrared_signal.c", + ], + order=20, +) diff --git a/applications/main/infrared/icon.png b/applications/main/infrared/icon.png new file mode 100644 index 00000000000..22c986180a2 Binary files /dev/null and b/applications/main/infrared/icon.png differ diff --git a/applications/main/infrared/infrared_app.c b/applications/main/infrared/infrared_app.c new file mode 100644 index 00000000000..645659bbc5d --- /dev/null +++ b/applications/main/infrared/infrared_app.c @@ -0,0 +1,510 @@ +#include "infrared_app_i.h" + +#include +#include +#include + +#define TAG "InfraredApp" + +#define INFRARED_TX_MIN_INTERVAL_MS 50U + +static const NotificationSequence* + infrared_notification_sequences[InfraredNotificationMessageCount] = { + &sequence_success, + &sequence_set_only_green_255, + &sequence_reset_green, + &sequence_solid_yellow, + &sequence_reset_rgb, + &sequence_blink_start_cyan, + &sequence_blink_start_magenta, + &sequence_blink_stop, +}; + +static void infrared_make_app_folder(InfraredApp* infrared) { + if(!storage_simply_mkdir(infrared->storage, INFRARED_APP_FOLDER)) { + infrared_show_error_message(infrared, "Cannot create\napp folder"); + } +} + +static bool infrared_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + InfraredApp* infrared = context; + return scene_manager_handle_custom_event(infrared->scene_manager, event); +} + +static bool infrared_back_event_callback(void* context) { + furi_assert(context); + InfraredApp* infrared = context; + return scene_manager_handle_back_event(infrared->scene_manager); +} + +static void infrared_tick_event_callback(void* context) { + furi_assert(context); + InfraredApp* infrared = context; + scene_manager_handle_tick_event(infrared->scene_manager); +} + +static void infrared_rpc_command_callback(const RpcAppSystemEvent* event, void* context) { + furi_assert(context); + InfraredApp* infrared = context; + furi_assert(infrared->rpc_ctx); + + if(event->type == RpcAppEventTypeSessionClose) { + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeRpcSessionClose); + rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL); + infrared->rpc_ctx = NULL; + } else if(event->type == RpcAppEventTypeAppExit) { + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeRpcExit); + } else if(event->type == RpcAppEventTypeLoadFile) { + furi_assert(event->data.type == RpcAppSystemEventDataTypeString); + furi_string_set(infrared->file_path, event->data.string); + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeRpcLoadFile); + } else if(event->type == RpcAppEventTypeButtonPress) { + furi_assert( + event->data.type == RpcAppSystemEventDataTypeString || + event->data.type == RpcAppSystemEventDataTypeInt32); + if(event->data.type == RpcAppSystemEventDataTypeString) { + furi_string_set(infrared->button_name, event->data.string); + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPressName); + } else { + infrared->app_state.current_button_index = event->data.i32; + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPressIndex); + } + } else if(event->type == RpcAppEventTypeButtonRelease) { + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonRelease); + } else { + rpc_system_app_confirm(infrared->rpc_ctx, false); + } +} + +static void infrared_find_vacant_remote_name(FuriString* name, const char* path) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + FuriString* base_path; + base_path = furi_string_alloc_set(path); + + if(furi_string_end_with(base_path, INFRARED_APP_EXTENSION)) { + size_t filename_start = furi_string_search_rchar(base_path, '/'); + furi_string_left(base_path, filename_start); + } + + furi_string_printf( + base_path, "%s/%s%s", path, furi_string_get_cstr(name), INFRARED_APP_EXTENSION); + + FS_Error status = storage_common_stat(storage, furi_string_get_cstr(base_path), NULL); + + if(status == FSE_OK) { + /* If the suggested name is occupied, try another one (name2, name3, etc) */ + size_t dot = furi_string_search_rchar(base_path, '.'); + furi_string_left(base_path, dot); + + FuriString* path_temp; + path_temp = furi_string_alloc(); + + uint32_t i = 1; + do { + furi_string_printf( + path_temp, "%s%lu%s", furi_string_get_cstr(base_path), ++i, INFRARED_APP_EXTENSION); + status = storage_common_stat(storage, furi_string_get_cstr(path_temp), NULL); + } while(status == FSE_OK); + + furi_string_free(path_temp); + + if(status == FSE_NOT_EXIST) { + furi_string_cat_printf(name, "%lu", i); + } + } + + furi_string_free(base_path); + furi_record_close(RECORD_STORAGE); +} + +static InfraredApp* infrared_alloc() { + InfraredApp* infrared = malloc(sizeof(InfraredApp)); + + infrared->file_path = furi_string_alloc(); + infrared->button_name = furi_string_alloc(); + + InfraredAppState* app_state = &infrared->app_state; + app_state->is_learning_new_remote = false; + app_state->is_debug_enabled = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); + app_state->edit_target = InfraredEditTargetNone; + app_state->edit_mode = InfraredEditModeNone; + app_state->current_button_index = InfraredButtonIndexNone; + + infrared->scene_manager = scene_manager_alloc(&infrared_scene_handlers, infrared); + infrared->view_dispatcher = view_dispatcher_alloc(); + + infrared->gui = furi_record_open(RECORD_GUI); + + ViewDispatcher* view_dispatcher = infrared->view_dispatcher; + view_dispatcher_enable_queue(view_dispatcher); + view_dispatcher_set_event_callback_context(view_dispatcher, infrared); + view_dispatcher_set_custom_event_callback(view_dispatcher, infrared_custom_event_callback); + view_dispatcher_set_navigation_event_callback(view_dispatcher, infrared_back_event_callback); + view_dispatcher_set_tick_event_callback(view_dispatcher, infrared_tick_event_callback, 100); + + infrared->storage = furi_record_open(RECORD_STORAGE); + infrared->dialogs = furi_record_open(RECORD_DIALOGS); + infrared->notifications = furi_record_open(RECORD_NOTIFICATION); + + infrared->worker = infrared_worker_alloc(); + infrared->remote = infrared_remote_alloc(); + infrared->current_signal = infrared_signal_alloc(); + infrared->brute_force = infrared_brute_force_alloc(); + + infrared->submenu = submenu_alloc(); + view_dispatcher_add_view( + view_dispatcher, InfraredViewSubmenu, submenu_get_view(infrared->submenu)); + + infrared->text_input = text_input_alloc(); + view_dispatcher_add_view( + view_dispatcher, InfraredViewTextInput, text_input_get_view(infrared->text_input)); + + infrared->dialog_ex = dialog_ex_alloc(); + view_dispatcher_add_view( + view_dispatcher, InfraredViewDialogEx, dialog_ex_get_view(infrared->dialog_ex)); + + infrared->button_menu = button_menu_alloc(); + view_dispatcher_add_view( + view_dispatcher, InfraredViewButtonMenu, button_menu_get_view(infrared->button_menu)); + + infrared->popup = popup_alloc(); + view_dispatcher_add_view(view_dispatcher, InfraredViewPopup, popup_get_view(infrared->popup)); + + infrared->view_stack = view_stack_alloc(); + view_dispatcher_add_view( + view_dispatcher, InfraredViewStack, view_stack_get_view(infrared->view_stack)); + + infrared->move_view = infrared_move_view_alloc(); + view_dispatcher_add_view( + view_dispatcher, InfraredViewMove, infrared_move_view_get_view(infrared->move_view)); + + if(app_state->is_debug_enabled) { + infrared->debug_view = infrared_debug_view_alloc(); + view_dispatcher_add_view( + view_dispatcher, + InfraredViewDebugView, + infrared_debug_view_get_view(infrared->debug_view)); + } + + infrared->button_panel = button_panel_alloc(); + infrared->loading = loading_alloc(); + infrared->progress = infrared_progress_view_alloc(); + + return infrared; +} + +static void infrared_free(InfraredApp* infrared) { + furi_assert(infrared); + ViewDispatcher* view_dispatcher = infrared->view_dispatcher; + InfraredAppState* app_state = &infrared->app_state; + + if(infrared->rpc_ctx) { + rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL); + rpc_system_app_send_exited(infrared->rpc_ctx); + infrared->rpc_ctx = NULL; + } + + view_dispatcher_remove_view(view_dispatcher, InfraredViewSubmenu); + submenu_free(infrared->submenu); + + view_dispatcher_remove_view(view_dispatcher, InfraredViewTextInput); + text_input_free(infrared->text_input); + + view_dispatcher_remove_view(view_dispatcher, InfraredViewDialogEx); + dialog_ex_free(infrared->dialog_ex); + + view_dispatcher_remove_view(view_dispatcher, InfraredViewButtonMenu); + button_menu_free(infrared->button_menu); + + view_dispatcher_remove_view(view_dispatcher, InfraredViewPopup); + popup_free(infrared->popup); + + view_dispatcher_remove_view(view_dispatcher, InfraredViewStack); + view_stack_free(infrared->view_stack); + + view_dispatcher_remove_view(view_dispatcher, InfraredViewMove); + infrared_move_view_free(infrared->move_view); + + if(app_state->is_debug_enabled) { + view_dispatcher_remove_view(view_dispatcher, InfraredViewDebugView); + infrared_debug_view_free(infrared->debug_view); + } + + button_panel_free(infrared->button_panel); + loading_free(infrared->loading); + infrared_progress_view_free(infrared->progress); + + view_dispatcher_free(view_dispatcher); + scene_manager_free(infrared->scene_manager); + + infrared_brute_force_free(infrared->brute_force); + infrared_signal_free(infrared->current_signal); + infrared_remote_free(infrared->remote); + infrared_worker_free(infrared->worker); + + furi_record_close(RECORD_NOTIFICATION); + infrared->notifications = NULL; + + furi_record_close(RECORD_DIALOGS); + infrared->dialogs = NULL; + + furi_record_close(RECORD_GUI); + infrared->gui = NULL; + + furi_string_free(infrared->file_path); + furi_string_free(infrared->button_name); + + free(infrared); +} + +bool infrared_add_remote_with_button( + const InfraredApp* infrared, + const char* button_name, + const InfraredSignal* signal) { + InfraredRemote* remote = infrared->remote; + + FuriString* new_name = furi_string_alloc_set(INFRARED_DEFAULT_REMOTE_NAME); + FuriString* new_path = furi_string_alloc_set(INFRARED_APP_FOLDER); + + infrared_find_vacant_remote_name(new_name, furi_string_get_cstr(new_path)); + furi_string_cat_printf( + new_path, "/%s%s", furi_string_get_cstr(new_name), INFRARED_APP_EXTENSION); + + bool success = false; + + do { + if(!infrared_remote_create(remote, furi_string_get_cstr(new_path))) break; + if(!infrared_remote_append_signal(remote, signal, button_name)) break; + success = true; + } while(false); + + furi_string_free(new_name); + furi_string_free(new_path); + + return success; +} + +bool infrared_rename_current_remote(const InfraredApp* infrared, const char* new_name) { + InfraredRemote* remote = infrared->remote; + const char* old_path = infrared_remote_get_path(remote); + + if(!strcmp(infrared_remote_get_name(remote), new_name)) { + return true; + } + + FuriString* new_name_fstr = furi_string_alloc_set(new_name); + FuriString* new_path_fstr = furi_string_alloc_set(old_path); + + infrared_find_vacant_remote_name(new_name_fstr, old_path); + + if(furi_string_end_with(new_path_fstr, INFRARED_APP_EXTENSION)) { + path_extract_dirname(old_path, new_path_fstr); + } + + path_append(new_path_fstr, furi_string_get_cstr(new_name_fstr)); + furi_string_cat(new_path_fstr, INFRARED_APP_EXTENSION); + + const bool success = infrared_remote_rename(remote, furi_string_get_cstr(new_path_fstr)); + + furi_string_free(new_name_fstr); + furi_string_free(new_path_fstr); + + return success; +} + +void infrared_tx_start(InfraredApp* infrared) { + if(infrared->app_state.is_transmitting) { + return; + } + + 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(infrared->current_signal)) { + const InfraredRawSignal* raw = infrared_signal_get_raw_signal(infrared->current_signal); + infrared_worker_set_raw_signal( + infrared->worker, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle); + } else { + const InfraredMessage* message = infrared_signal_get_message(infrared->current_signal); + infrared_worker_set_decoded_signal(infrared->worker, message); + } + + dolphin_deed(DolphinDeedIrSend); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); + + 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(InfraredApp* infrared, size_t button_index) { + furi_assert(button_index < infrared_remote_get_signal_count(infrared->remote)); + + if(infrared_remote_load_signal(infrared->remote, infrared->current_signal, button_index)) { + infrared_tx_start(infrared); + } else { + infrared_show_error_message( + infrared, + "Failed to load\n\"%s\"", + infrared_remote_get_signal_name(infrared->remote, button_index)); + } +} + +void infrared_tx_stop(InfraredApp* infrared) { + if(!infrared->app_state.is_transmitting) { + return; + } + + 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(InfraredApp* infrared, uint32_t bank, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + vsnprintf(infrared->text_store[bank], INFRARED_TEXT_STORE_SIZE, fmt, args); + + va_end(args); +} + +void infrared_text_store_clear(InfraredApp* infrared, uint32_t bank) { + memset(infrared->text_store[bank], 0, INFRARED_TEXT_STORE_SIZE + 1); +} + +void infrared_play_notification_message( + const InfraredApp* infrared, + InfraredNotificationMessage message) { + furi_assert(message < InfraredNotificationMessageCount); + notification_message(infrared->notifications, infrared_notification_sequences[message]); +} + +void infrared_show_loading_popup(const InfraredApp* infrared, bool show) { + ViewStack* view_stack = infrared->view_stack; + Loading* loading = infrared->loading; + + if(show) { + // Raise timer priority so that animations can play + furi_timer_set_thread_priority(FuriTimerThreadPriorityElevated); + view_stack_add_view(view_stack, loading_get_view(loading)); + } else { + view_stack_remove_view(view_stack, loading_get_view(loading)); + // Restore default timer priority + furi_timer_set_thread_priority(FuriTimerThreadPriorityNormal); + } +} + +void infrared_show_error_message(const InfraredApp* infrared, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + FuriString* message = furi_string_alloc_vprintf(fmt, args); + dialog_message_show_storage_error(infrared->dialogs, furi_string_get_cstr(message)); + + furi_string_free(message); + va_end(args); +} + +void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { + furi_assert(context); + InfraredApp* infrared = context; + + if(infrared_worker_signal_is_decoded(received_signal)) { + infrared_signal_set_message( + infrared->current_signal, infrared_worker_get_decoded_signal(received_signal)); + } else { + const uint32_t* timings; + size_t timings_size; + infrared_worker_get_raw_signal(received_signal, &timings, &timings_size); + infrared_signal_set_raw_signal( + infrared->current_signal, + timings, + timings_size, + INFRARED_COMMON_CARRIER_FREQUENCY, + INFRARED_COMMON_DUTY_CYCLE); + } + + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeSignalReceived); +} + +void infrared_text_input_callback(void* context) { + furi_assert(context); + InfraredApp* infrared = context; + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeTextEditDone); +} + +void infrared_popup_closed_callback(void* context) { + furi_assert(context); + InfraredApp* infrared = context; + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypePopupClosed); +} + +int32_t infrared_app(void* p) { + InfraredApp* infrared = infrared_alloc(); + + infrared_make_app_folder(infrared); + + bool is_remote_loaded = false; + bool is_rpc_mode = false; + + if(p && strlen(p)) { + uint32_t rpc_ctx = 0; + if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { + infrared->rpc_ctx = (void*)rpc_ctx; + rpc_system_app_set_callback( + infrared->rpc_ctx, infrared_rpc_command_callback, infrared); + rpc_system_app_send_started(infrared->rpc_ctx); + is_rpc_mode = true; + } else { + const char* file_path = (const char*)p; + is_remote_loaded = infrared_remote_load(infrared->remote, file_path); + + if(!is_remote_loaded) { + infrared_show_error_message(infrared, "Failed to load\n\"%s\"", file_path); + return -1; + } + + furi_string_set(infrared->file_path, file_path); + } + } + + if(is_rpc_mode) { + view_dispatcher_attach_to_gui( + infrared->view_dispatcher, infrared->gui, ViewDispatcherTypeDesktop); + scene_manager_next_scene(infrared->scene_manager, InfraredSceneRpc); + } else { + view_dispatcher_attach_to_gui( + infrared->view_dispatcher, infrared->gui, ViewDispatcherTypeFullscreen); + if(is_remote_loaded) { //-V547 + scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); + } else { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneStart); + } + } + + view_dispatcher_run(infrared->view_dispatcher); + + infrared_free(infrared); + return 0; +} diff --git a/applications/main/infrared/infrared_app.h b/applications/main/infrared/infrared_app.h new file mode 100644 index 00000000000..a6f87402a94 --- /dev/null +++ b/applications/main/infrared/infrared_app.h @@ -0,0 +1,15 @@ +/** + * @file infrared_app.h + * @brief Infrared application - start here. + * + * @see infrared_app_i.h for the main application data structure and functions. + * @see infrared_signal.h for the infrared signal library - loading, storing and transmitting signals. + * @see infrared_remote.hl for the infrared remote library - loading, storing and manipulating remotes. + * @see infrared_brute_force.h for the infrared brute force - loading and transmitting multiple signals. + */ +#pragma once + +/** + * @brief InfraredApp opaque type declaration. + */ +typedef struct InfraredApp InfraredApp; diff --git a/applications/main/infrared/infrared_app_i.h b/applications/main/infrared/infrared_app_i.h new file mode 100644 index 00000000000..c35d3fa410a --- /dev/null +++ b/applications/main/infrared/infrared_app_i.h @@ -0,0 +1,289 @@ +/** + * @file infrared_app_i.h + * @brief Main Infrared application types and functions. + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include "infrared_app.h" +#include "infrared_remote.h" +#include "infrared_brute_force.h" +#include "infrared_custom_event.h" + +#include "scenes/infrared_scene.h" +#include "views/infrared_progress_view.h" +#include "views/infrared_debug_view.h" +#include "views/infrared_move_view.h" + +#include "rpc/rpc_app.h" + +#define INFRARED_FILE_NAME_SIZE 100 +#define INFRARED_TEXT_STORE_NUM 2 +#define INFRARED_TEXT_STORE_SIZE 128 + +#define INFRARED_MAX_BUTTON_NAME_LENGTH 22 +#define INFRARED_MAX_REMOTE_NAME_LENGTH 22 + +#define INFRARED_APP_FOLDER ANY_PATH("infrared") +#define INFRARED_APP_EXTENSION ".ir" + +#define INFRARED_DEFAULT_REMOTE_NAME "Remote" +#define INFRARED_LOG_TAG "InfraredApp" + +/** + * @brief Enumeration of invalid remote button indices. + */ +typedef enum { + InfraredButtonIndexNone = -1, /**< No button is currently selected. */ +} InfraredButtonIndex; + +/** + * @brief Enumeration of editing targets. + */ +typedef enum { + InfraredEditTargetNone, /**< No editing target is selected. */ + InfraredEditTargetRemote, /**< Whole remote is selected as editing target. */ + InfraredEditTargetButton, /**< Single button is selected as editing target. */ +} InfraredEditTarget; + +/** + * @brief Enumeration of editing modes. + */ +typedef enum { + InfraredEditModeNone, /**< No editing mode is selected. */ + InfraredEditModeRename, /**< Rename mode is selected. */ + InfraredEditModeDelete, /**< Delete mode is selected. */ +} InfraredEditMode; + +/** + * @brief Infrared application state type. + */ +typedef struct { + bool is_learning_new_remote; /**< Learning new remote or adding to an existing one. */ + bool is_debug_enabled; /**< Whether to enable or disable debugging features. */ + bool is_transmitting; /**< Whether a signal is currently being transmitted. */ + InfraredEditTarget edit_target : 8; /**< Selected editing target (a remote or a button). */ + InfraredEditMode edit_mode : 8; /**< Selected editing operation (rename or delete). */ + int32_t current_button_index; /**< Selected button index (move destination). */ + int32_t prev_button_index; /**< Previous button index (move source). */ + uint32_t last_transmit_time; /**< Lat time a signal was transmitted. */ +} InfraredAppState; + +/** + * @brief Infrared application type. + */ +struct InfraredApp { + SceneManager* scene_manager; /**< Pointer to a SceneManager instance. */ + ViewDispatcher* view_dispatcher; /**< Pointer to a ViewDispatcher instance. */ + + Gui* gui; /**< Pointer to a Gui instance. */ + Storage* storage; /**< Pointer to a Storage instance. */ + DialogsApp* dialogs; /**< Pointer to a DialogsApp instance. */ + NotificationApp* notifications; /**< Pointer to a NotificationApp instance. */ + InfraredWorker* worker; /**< Used to send or receive signals. */ + InfraredRemote* remote; /**< Holds the currently loaded remote. */ + InfraredSignal* current_signal; /**< Holds the currently loaded signal. */ + InfraredBruteForce* brute_force; /**< Used for the Universal Remote feature. */ + + Submenu* submenu; /**< Standard view for displaying application menus. */ + TextInput* text_input; /**< Standard view for receiving user text input. */ + DialogEx* dialog_ex; /**< Standard view for displaying dialogs. */ + ButtonMenu* button_menu; /**< Custom view for interacting with IR remotes. */ + Popup* popup; /**< Standard view for displaying messages. */ + + ViewStack* view_stack; /**< Standard view for displaying stacked interfaces. */ + InfraredDebugView* debug_view; /**< Custom view for displaying debug information. */ + InfraredMoveView* move_view; /**< Custom view for rearranging buttons in a remote. */ + + ButtonPanel* button_panel; /**< Standard view for displaying control panels. */ + Loading* loading; /**< Standard view for informing about long operations. */ + InfraredProgressView* progress; /**< Custom view for showing brute force progress. */ + + FuriString* file_path; /**< Full path to the currently loaded file. */ + FuriString* button_name; /** Name of the button requested in RPC mode. */ + /** Arbitrary text storage for various inputs. */ + char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1]; + InfraredAppState app_state; /**< Application state. */ + + void* rpc_ctx; /**< Pointer to the RPC context object. */ +}; + +/** + * @brief Enumeration of all used view types. + */ +typedef enum { + InfraredViewSubmenu, + InfraredViewTextInput, + InfraredViewDialogEx, + InfraredViewButtonMenu, + InfraredViewPopup, + InfraredViewStack, + InfraredViewDebugView, + InfraredViewMove, +} InfraredView; + +/** + * @brief Enumeration of all notification message types. + */ +typedef enum { + InfraredNotificationMessageSuccess, /**< Play a short happy tune. */ + InfraredNotificationMessageGreenOn, /**< Turn green LED on. */ + InfraredNotificationMessageGreenOff, /**< Turn green LED off. */ + InfraredNotificationMessageYellowOn, /**< Turn yellow LED on. */ + InfraredNotificationMessageYellowOff, /**< Turn yellow LED off. */ + InfraredNotificationMessageBlinkStartRead, /**< Blink the LED to indicate receiver mode. */ + InfraredNotificationMessageBlinkStartSend, /**< Blink the LED to indicate transmitter mode. */ + InfraredNotificationMessageBlinkStop, /**< Stop blinking the LED. */ + InfraredNotificationMessageCount, /**< Special value equal to the message type count. */ +} InfraredNotificationMessage; + +/** + * @brief Add a new remote with a single signal. + * + * The filename will be automatically generated depending on + * the names and number of other files in the infrared data directory. + * + * @param[in] infrared pointer to the application instance. + * @param[in] name pointer to a zero-terminated string containing the signal name. + * @param[in] signal pointer to the signal to be added. + * @return true if the remote was successfully created, false otherwise. + */ +bool infrared_add_remote_with_button( + const InfraredApp* infrared, + const char* name, + const InfraredSignal* signal); + +/** + * @brief Rename the currently loaded remote. + * + * @param[in] infrared pointer to the application instance. + * @param[in] new_name pointer to a zero-terminated string containing the new remote name. + * @return true if the remote was successfully renamed, false otherwise. + */ +bool infrared_rename_current_remote(const InfraredApp* infrared, const char* new_name); + +/** + * @brief Begin transmission of the currently loaded signal. + * + * The signal will be repeated indefinitely until stopped. + * + * @param[in,out] infrared pointer to the application instance. + */ +void infrared_tx_start(InfraredApp* infrared); + +/** + * @brief Load a signal under the given index and begin transmission. + * + * The signal will be repeated indefinitely until stopped. + * + * @param[in,out] infrared pointer to the application instance. + * @param[in] button_index index of the signal to be loaded. + * @returns true if the signal could be loaded, false otherwise. + */ +void infrared_tx_start_button_index(InfraredApp* infrared, size_t button_index); + +/** + * @brief Stop transmission of the currently loaded signal. + * + * @param[in,out] infrared pointer to the application instance. + */ +void infrared_tx_stop(InfraredApp* infrared); + +/** + * @brief Set the internal text store with formatted text. + * + * @param[in,out] infrared pointer to the application instance. + * @param[in] bank index of text store bank (0 or 1). + * @param[in] fmt pointer to a zero-terminated string containing the format text. + * @param[in] ... additional arguments. + */ +void infrared_text_store_set(InfraredApp* infrared, uint32_t bank, const char* fmt, ...) + _ATTRIBUTE((__format__(__printf__, 3, 4))); + +/** + * @brief Clear the internal text store. + * + * @param[in,out] infrared pointer to the application instance. + * @param[in] bank index of text store bank (0 or 1). + */ +void infrared_text_store_clear(InfraredApp* infrared, uint32_t bank); + +/** + * @brief Play a sound and/or blink the LED. + * + * @param[in] infrared pointer to the application instance. + * @param[in] message type of the message to play. + */ +void infrared_play_notification_message( + const InfraredApp* infrared, + InfraredNotificationMessage message); + +/** + * @brief Show a loading pop-up screen. + * + * In order for this to work, a Stack view must be currently active and + * the main view must be added to it. + * + * @param[in] infrared pointer to the application instance. + * @param[in] show whether to show or hide the pop-up. + */ +void infrared_show_loading_popup(const InfraredApp* infrared, bool show); + +/** + * @brief Show a formatted error messsage. + * + * @param[in] infrared pointer to the application instance. + * @param[in] fmt pointer to a zero-terminated string containing the format text. + * @param[in] ... additional arguments. + */ +void infrared_show_error_message(const InfraredApp* infrared, const char* fmt, ...) + _ATTRIBUTE((__format__(__printf__, 2, 3))); + +/** + * @brief Common received signal callback. + * + * Called when the worker has received a complete infrared signal. + * + * @param[in,out] context pointer to the user-specified context object. + * @param[in] received_signal pointer to the received signal. + */ +void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal); + +/** + * @brief Common text input callback. + * + * Called when the input has been accepted by the user. + * + * @param[in,out] context pointer to the user-specified context object. + */ +void infrared_text_input_callback(void* context); + +/** + * @brief Common popup close callback. + * + * Called when the popup has been closed either by the user or after a timeout. + * + * @param[in,out] context pointer to the user-specified context object. + */ +void infrared_popup_closed_callback(void* context); diff --git a/applications/main/infrared/infrared_brute_force.c b/applications/main/infrared/infrared_brute_force.c new file mode 100644 index 00000000000..373c7270f58 --- /dev/null +++ b/applications/main/infrared/infrared_brute_force.c @@ -0,0 +1,155 @@ +#include "infrared_brute_force.h" + +#include +#include +#include + +#include "infrared_signal.h" + +typedef struct { + uint32_t index; + uint32_t count; +} InfraredBruteForceRecord; + +DICT_DEF2( + InfraredBruteForceRecordDict, + FuriString*, + FURI_STRING_OPLIST, + InfraredBruteForceRecord, + M_POD_OPLIST); + +struct InfraredBruteForce { + FlipperFormat* ff; + const char* db_filename; + FuriString* current_record_name; + InfraredSignal* current_signal; + InfraredBruteForceRecordDict_t records; + bool is_started; +}; + +InfraredBruteForce* infrared_brute_force_alloc() { + InfraredBruteForce* brute_force = malloc(sizeof(InfraredBruteForce)); + brute_force->ff = NULL; + brute_force->db_filename = NULL; + brute_force->current_signal = NULL; + brute_force->is_started = false; + brute_force->current_record_name = furi_string_alloc(); + InfraredBruteForceRecordDict_init(brute_force->records); + return brute_force; +} + +void infrared_brute_force_free(InfraredBruteForce* brute_force) { + furi_assert(!brute_force->is_started); + InfraredBruteForceRecordDict_clear(brute_force->records); + furi_string_free(brute_force->current_record_name); + free(brute_force); +} + +void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename) { + furi_assert(!brute_force->is_started); + brute_force->db_filename = db_filename; +} + +bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force) { + furi_assert(!brute_force->is_started); + furi_assert(brute_force->db_filename); + bool success = false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + + success = flipper_format_buffered_file_open_existing(ff, brute_force->db_filename); + if(success) { + FuriString* signal_name; + signal_name = furi_string_alloc(); + while(flipper_format_read_string(ff, "name", signal_name)) { + InfraredBruteForceRecord* record = + InfraredBruteForceRecordDict_get(brute_force->records, signal_name); + if(record) { //-V547 + ++(record->count); + } + } + furi_string_free(signal_name); + } + + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + return success; +} + +bool infrared_brute_force_start( + InfraredBruteForce* brute_force, + uint32_t index, + uint32_t* record_count) { + furi_assert(!brute_force->is_started); + bool success = false; + *record_count = 0; + + InfraredBruteForceRecordDict_it_t it; + for(InfraredBruteForceRecordDict_it(it, brute_force->records); + !InfraredBruteForceRecordDict_end_p(it); + InfraredBruteForceRecordDict_next(it)) { + const InfraredBruteForceRecordDict_itref_t* record = InfraredBruteForceRecordDict_cref(it); + if(record->value.index == index) { + *record_count = record->value.count; + if(*record_count) { + furi_string_set(brute_force->current_record_name, record->key); + } + break; + } + } + + if(*record_count) { + Storage* storage = furi_record_open(RECORD_STORAGE); + brute_force->ff = flipper_format_buffered_file_alloc(storage); + brute_force->current_signal = infrared_signal_alloc(); + brute_force->is_started = true; + success = + flipper_format_buffered_file_open_existing(brute_force->ff, brute_force->db_filename); + if(!success) infrared_brute_force_stop(brute_force); + } + return success; +} + +bool infrared_brute_force_is_started(const InfraredBruteForce* brute_force) { + return brute_force->is_started; +} + +void infrared_brute_force_stop(InfraredBruteForce* brute_force) { + furi_assert(brute_force->is_started); + furi_string_reset(brute_force->current_record_name); + infrared_signal_free(brute_force->current_signal); + flipper_format_free(brute_force->ff); + brute_force->current_signal = NULL; + brute_force->ff = NULL; + brute_force->is_started = false; + furi_record_close(RECORD_STORAGE); +} + +bool infrared_brute_force_send_next(InfraredBruteForce* brute_force) { + furi_assert(brute_force->is_started); + const bool success = infrared_signal_search_by_name_and_read( + brute_force->current_signal, + brute_force->ff, + furi_string_get_cstr(brute_force->current_record_name)); + if(success) { + infrared_signal_transmit(brute_force->current_signal); + } + return success; +} + +void infrared_brute_force_add_record( + InfraredBruteForce* brute_force, + uint32_t index, + const char* name) { + InfraredBruteForceRecord value = {.index = index, .count = 0}; + FuriString* key; + key = furi_string_alloc_set(name); + InfraredBruteForceRecordDict_set_at(brute_force->records, key, value); + furi_string_free(key); +} + +void infrared_brute_force_reset(InfraredBruteForce* brute_force) { + furi_assert(!brute_force->is_started); + InfraredBruteForceRecordDict_reset(brute_force->records); +} diff --git a/applications/main/infrared/infrared_brute_force.h b/applications/main/infrared/infrared_brute_force.h new file mode 100644 index 00000000000..33677d2ec18 --- /dev/null +++ b/applications/main/infrared/infrared_brute_force.h @@ -0,0 +1,110 @@ +/** + * @file infrared_brute_force.h + * @brief Infrared signal brute-forcing library. + * + * The BruteForce library is used to send large quantities of signals, + * sorted by a category. It is used to implement the Universal Remote + * feature. + */ +#pragma once + +#include +#include + +/** + * @brief InfraredBruteForce opaque type declaration. + */ +typedef struct InfraredBruteForce InfraredBruteForce; + +/** + * @brief Create a new InfraredBruteForce instance. + * + * @returns pointer to the created instance. + */ +InfraredBruteForce* infrared_brute_force_alloc(); + +/** + * @brief Delete an InfraredBruteForce instance. + * + * @param[in,out] brute_force pointer to the instance to be deleted. + */ +void infrared_brute_force_free(InfraredBruteForce* brute_force); + +/** + * @brief Set an InfraredBruteForce instance to use a signal database contained in a file. + * + * @param[in,out] brute_force pointer to the instance to be configured. + * @param[in] db_filename pointer to a zero-terminated string containing a full path to the database file. + */ +void infrared_brute_force_set_db_filename(InfraredBruteForce* brute_force, const char* db_filename); + +/** + * @brief Build a signal dictionary from a previously set database file. + * + * This function must be called each time after setting the database via + * a infrared_brute_force_set_db_filename() call. + * + * @param[in,out] brute_force pointer to the instance to be updated. + * @returns true on success, false otherwise. + */ +bool infrared_brute_force_calculate_messages(InfraredBruteForce* brute_force); + +/** + * @brief Start transmitting signals from a category stored in an InfraredBruteForce's instance dictionary. + * + * @param[in,out] brute_force pointer to the instance to be started. + * @param[in] index index of the signal category in the dictionary. + * @returns true on success, false otherwise. + */ +bool infrared_brute_force_start( + InfraredBruteForce* brute_force, + uint32_t index, + uint32_t* record_count); + +/** + * @brief Determine whether the transmission was started. + * + * @param[in] brute_force pointer to the instance to be tested. + * @returns true if transmission was started, false otherwise. + */ +bool infrared_brute_force_is_started(const InfraredBruteForce* brute_force); + +/** + * @brief Stop transmitting the signals. + * + * @param[in] brute_force pointer to the instance to be stopped. + */ +void infrared_brute_force_stop(InfraredBruteForce* brute_force); + +/** + * @brief Send the next signal from the chosen category. + * + * This function is called repeatedly until no more signals are left + * in the chosen signal category. + * + * @warning Transmission must be started first by calling infrared_brute_force_start() + * before calling this function. + * + * @param[in,out] brute_force pointer to the instance to be used. + * @returns true if the next signal existed and could be transmitted, false otherwise. + */ +bool infrared_brute_force_send_next(InfraredBruteForce* brute_force); + +/** + * @brief Add a signal category to an InfraredBruteForce instance's dictionary. + * + * @param[in,out] brute_force pointer to the instance to be updated. + * @param[in] index index of the category to be added. + * @param[in] name name of the category to be added. + */ +void infrared_brute_force_add_record( + InfraredBruteForce* brute_force, + uint32_t index, + const char* name); + +/** + * @brief Reset an InfraredBruteForce instance. + * + * @param[in,out] brute_force pointer to the instance to be reset. + */ +void infrared_brute_force_reset(InfraredBruteForce* brute_force); diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c new file mode 100644 index 00000000000..c960ffa2856 --- /dev/null +++ b/applications/main/infrared/infrared_cli.c @@ -0,0 +1,519 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "infrared_signal.h" +#include "infrared_brute_force.h" + +#define INFRARED_CLI_BUF_SIZE 10 +#define INFRARED_ASSETS_FOLDER "infrared/assets" +#define INFRARED_BRUTE_FORCE_DUMMY_INDEX 0 + +DICT_DEF2(dict_signals, FuriString*, FURI_STRING_OPLIST, int, M_DEFAULT_OPLIST) + +static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args); +static void infrared_cli_start_ir_tx(Cli* cli, FuriString* args); +static void infrared_cli_process_decode(Cli* cli, FuriString* args); +static void infrared_cli_process_universal(Cli* cli, FuriString* args); + +static const struct { + const char* cmd; + void (*process_function)(Cli* cli, FuriString* args); +} infrared_cli_commands[] = { + {.cmd = "rx", .process_function = infrared_cli_start_ir_rx}, + {.cmd = "tx", .process_function = infrared_cli_start_ir_tx}, + {.cmd = "decode", .process_function = infrared_cli_process_decode}, + {.cmd = "universal", .process_function = infrared_cli_process_universal}, +}; + +static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { + furi_assert(received_signal); + char buf[100]; + size_t buf_cnt; + Cli* cli = (Cli*)context; + + if(infrared_worker_signal_is_decoded(received_signal)) { + const InfraredMessage* message = infrared_worker_get_decoded_signal(received_signal); + buf_cnt = snprintf( + buf, + sizeof(buf), + "%s, A:0x%0*lX, C:0x%0*lX%s\r\n", + infrared_get_protocol_name(message->protocol), + ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4), + message->address, + ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), + message->command, + message->repeat ? " R" : ""); + cli_write(cli, (uint8_t*)buf, buf_cnt); + } else { + const uint32_t* timings; + size_t timings_cnt; + infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt); + + buf_cnt = snprintf(buf, sizeof(buf), "RAW, %zu samples:\r\n", timings_cnt); + cli_write(cli, (uint8_t*)buf, buf_cnt); + for(size_t i = 0; i < timings_cnt; ++i) { + buf_cnt = snprintf(buf, sizeof(buf), "%lu ", timings[i]); + cli_write(cli, (uint8_t*)buf, buf_cnt); + } + buf_cnt = snprintf(buf, sizeof(buf), "\r\n"); + cli_write(cli, (uint8_t*)buf, buf_cnt); + } +} + +static void infrared_cli_print_usage(void) { + printf("Usage:\r\n"); + printf("\tir rx [raw]\r\n"); + printf("\tir tx
\r\n"); + printf("\t and
are hex-formatted\r\n"); + printf("\tAvailable protocols:"); + for(int i = 0; infrared_is_protocol_valid((InfraredProtocol)i); ++i) { + printf(" %s", infrared_get_protocol_name((InfraredProtocol)i)); + } + printf("\r\n"); + printf("\tRaw format:\r\n"); + printf("\tir tx RAW F: DC: ...\r\n"); + printf( + "\tFrequency (%d - %d), Duty cycle (0 - 100), max 512 samples\r\n", + INFRARED_MIN_FREQUENCY, + INFRARED_MAX_FREQUENCY); + printf("\tir decode []\r\n"); + printf("\tir universal \r\n"); + printf("\tir universal list \r\n"); + // TODO FL-3496: Do not hardcode universal remote names + printf("\tAvailable universal remotes: tv audio ac projector\r\n"); +} + +static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) { + UNUSED(cli); + + bool enable_decoding = true; + + if(!furi_string_empty(args)) { + if(!furi_string_cmp_str(args, "raw")) { + enable_decoding = false; + } else { + printf("Wrong arguments.\r\n"); + infrared_cli_print_usage(); + return; + } + } + + InfraredWorker* worker = infrared_worker_alloc(); + infrared_worker_rx_enable_signal_decoding(worker, enable_decoding); + infrared_worker_rx_start(worker); + infrared_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli); + + printf("Receiving %s INFRARED...\r\nPress Ctrl+C to abort\r\n", enable_decoding ? "" : "RAW"); + while(!cli_cmd_interrupt_received(cli)) { + furi_delay_ms(50); + } + + infrared_worker_rx_stop(worker); + infrared_worker_free(worker); +} + +static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) { + char protocol_name[32]; + InfraredMessage message; + int parsed = sscanf(str, "%31s %lX %lX", protocol_name, &message.address, &message.command); + + if(parsed != 3) { + return false; + } + + message.protocol = infrared_get_protocol_by_name(protocol_name); + message.repeat = false; + infrared_signal_set_message(signal, &message); + return infrared_signal_is_valid(signal); +} + +static bool infrared_cli_parse_raw(const char* str, InfraredSignal* signal) { + char frequency_str[INFRARED_CLI_BUF_SIZE]; + char duty_cycle_str[INFRARED_CLI_BUF_SIZE]; + int parsed = sscanf(str, "RAW F:%9s DC:%9s", frequency_str, duty_cycle_str); + + if(parsed != 2) { + return false; + } + + uint32_t* timings = malloc(sizeof(uint32_t) * MAX_TIMINGS_AMOUNT); + uint32_t frequency = atoi(frequency_str); + float duty_cycle = (float)atoi(duty_cycle_str) / 100; + + str += strlen(frequency_str) + strlen(duty_cycle_str) + INFRARED_CLI_BUF_SIZE; + + size_t timings_size = 0; + while(1) { + while(*str == ' ') { + ++str; + } + + char timing_str[INFRARED_CLI_BUF_SIZE]; + if(sscanf(str, "%9s", timing_str) != 1) { + break; + } + + str += strlen(timing_str); + uint32_t timing = atoi(timing_str); + + if((timing <= 0) || (timings_size >= MAX_TIMINGS_AMOUNT)) { + break; + } + + timings[timings_size] = timing; + ++timings_size; + } + + infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle); + free(timings); + + return infrared_signal_is_valid(signal); +} + +static void infrared_cli_start_ir_tx(Cli* cli, FuriString* args) { + UNUSED(cli); + const char* str = furi_string_get_cstr(args); + InfraredSignal* signal = infrared_signal_alloc(); + + bool success = infrared_cli_parse_message(str, signal) || infrared_cli_parse_raw(str, signal); + if(success) { + infrared_signal_transmit(signal); + } else { + printf("Wrong arguments.\r\n"); + infrared_cli_print_usage(); + } + + infrared_signal_free(signal); +} + +static bool + infrared_cli_save_signal(InfraredSignal* signal, FlipperFormat* file, const char* name) { + bool ret = infrared_signal_save(signal, file, name); + if(!ret) { + printf("Failed to save signal: \"%s\"\r\n", name); + } + return ret; +} + +static bool infrared_cli_decode_raw_signal( + const InfraredRawSignal* raw_signal, + InfraredDecoderHandler* decoder, + FlipperFormat* output_file, + const char* signal_name) { + InfraredSignal* signal = infrared_signal_alloc(); + bool ret = false, level = true, is_decoded = false; + + size_t i; + for(i = 0; i < raw_signal->timings_size; ++i) { + // TODO FL-3523: Any infrared_check_decoder_ready() magic? + const InfraredMessage* message = infrared_decode(decoder, level, raw_signal->timings[i]); + + if(message) { + is_decoded = true; + printf( + "Protocol: %s address: 0x%lX command: 0x%lX %s\r\n", + infrared_get_protocol_name(message->protocol), + message->address, + message->command, + (message->repeat ? "R" : "")); + if(output_file && !message->repeat) { + infrared_signal_set_message(signal, message); + if(!infrared_cli_save_signal(signal, output_file, signal_name)) break; + } + } + + level = !level; + } + + if(i == raw_signal->timings_size) { + if(!is_decoded && output_file) { + infrared_signal_set_raw_signal( + signal, + raw_signal->timings, + raw_signal->timings_size, + raw_signal->frequency, + raw_signal->duty_cycle); + ret = infrared_cli_save_signal(signal, output_file, signal_name); + } else { + ret = true; + } + } + + infrared_reset_decoder(decoder); + infrared_signal_free(signal); + return ret; +} + +static bool infrared_cli_decode_file(FlipperFormat* input_file, FlipperFormat* output_file) { + bool ret = false; + + InfraredSignal* signal = infrared_signal_alloc(); + InfraredDecoderHandler* decoder = infrared_alloc_decoder(); + + FuriString* tmp; + tmp = furi_string_alloc(); + + while(infrared_signal_read(signal, input_file, tmp)) { + ret = false; + if(!infrared_signal_is_valid(signal)) { + printf("Invalid signal\r\n"); + break; + } + if(!infrared_signal_is_raw(signal)) { + if(output_file && + !infrared_cli_save_signal(signal, output_file, furi_string_get_cstr(tmp))) { + break; + } else { + printf("Skipping decoded signal\r\n"); + continue; + } + } + const InfraredRawSignal* raw_signal = infrared_signal_get_raw_signal(signal); + printf( + "Raw signal: %s, %zu samples\r\n", + furi_string_get_cstr(tmp), + raw_signal->timings_size); + if(!infrared_cli_decode_raw_signal( + raw_signal, decoder, output_file, furi_string_get_cstr(tmp))) + break; + ret = true; + } + + infrared_free_decoder(decoder); + infrared_signal_free(signal); + furi_string_free(tmp); + + return ret; +} + +static void infrared_cli_process_decode(Cli* cli, FuriString* args) { + UNUSED(cli); + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* input_file = flipper_format_buffered_file_alloc(storage); + FlipperFormat* output_file = NULL; + + uint32_t version; + FuriString *tmp, *header, *input_path, *output_path; + tmp = furi_string_alloc(); + header = furi_string_alloc(); + input_path = furi_string_alloc(); + output_path = furi_string_alloc(); + + do { + if(!args_read_probably_quoted_string_and_trim(args, input_path)) { + printf("Wrong arguments.\r\n"); + infrared_cli_print_usage(); + break; + } + args_read_probably_quoted_string_and_trim(args, output_path); + if(!flipper_format_buffered_file_open_existing( + input_file, furi_string_get_cstr(input_path))) { + printf( + "Failed to open file for reading: \"%s\"\r\n", furi_string_get_cstr(input_path)); + break; + } + if(!flipper_format_read_header(input_file, header, &version) || + (!furi_string_start_with_str(header, "IR")) || version != 1) { + printf( + "Invalid or corrupted input file: \"%s\"\r\n", furi_string_get_cstr(input_path)); + break; + } + if(!furi_string_empty(output_path)) { + printf("Writing output to file: \"%s\"\r\n", furi_string_get_cstr(output_path)); + output_file = flipper_format_file_alloc(storage); + } + if(output_file && + !flipper_format_file_open_always(output_file, furi_string_get_cstr(output_path))) { + printf( + "Failed to open file for writing: \"%s\"\r\n", furi_string_get_cstr(output_path)); + break; + } + if(output_file && !flipper_format_write_header(output_file, header, version)) { + printf( + "Failed to write to the output file: \"%s\"\r\n", + furi_string_get_cstr(output_path)); + break; + } + if(!infrared_cli_decode_file(input_file, output_file)) { + break; + } + printf("File successfully decoded.\r\n"); + } while(false); + + furi_string_free(tmp); + furi_string_free(header); + furi_string_free(input_path); + furi_string_free(output_path); + + flipper_format_free(input_file); + if(output_file) flipper_format_free(output_file); + furi_record_close(RECORD_STORAGE); +} + +static void infrared_cli_list_remote_signals(FuriString* remote_name) { + if(furi_string_empty(remote_name)) { + printf("Missing remote name.\r\n"); + return; + } + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + FuriString* remote_path = furi_string_alloc_printf( + "%s/%s.ir", EXT_PATH(INFRARED_ASSETS_FOLDER), furi_string_get_cstr(remote_name)); + + do { + if(!flipper_format_buffered_file_open_existing(ff, furi_string_get_cstr(remote_path))) { + printf("Invalid remote name.\r\n"); + break; + } + + dict_signals_t signals_dict; + dict_signals_init(signals_dict); + + FuriString* key = furi_string_alloc(); + FuriString* signal_name = furi_string_alloc(); + + printf("Valid signals:\r\n"); + int max = 1; + while(flipper_format_read_string(ff, "name", signal_name)) { + furi_string_set_str(key, furi_string_get_cstr(signal_name)); + int* v = dict_signals_get(signals_dict, key); + if(v != NULL) { //-V547 + (*v)++; + max = M_MAX(*v, max); + } else { + dict_signals_set_at(signals_dict, key, 1); + } + } + + dict_signals_it_t it; + for(dict_signals_it(it, signals_dict); !dict_signals_end_p(it); dict_signals_next(it)) { + const struct dict_signals_pair_s* pair = dict_signals_cref(it); + printf("\t%s\r\n", furi_string_get_cstr(pair->key)); + } + + furi_string_free(key); + furi_string_free(signal_name); + dict_signals_clear(signals_dict); + + } while(false); + + flipper_format_free(ff); + furi_string_free(remote_path); + furi_record_close(RECORD_STORAGE); +} + +static void + infrared_cli_brute_force_signals(Cli* cli, FuriString* remote_name, FuriString* signal_name) { + InfraredBruteForce* brute_force = infrared_brute_force_alloc(); + FuriString* remote_path = furi_string_alloc_printf( + "%s/%s.ir", EXT_PATH(INFRARED_ASSETS_FOLDER), furi_string_get_cstr(remote_name)); + + infrared_brute_force_set_db_filename(brute_force, furi_string_get_cstr(remote_path)); + infrared_brute_force_add_record( + brute_force, INFRARED_BRUTE_FORCE_DUMMY_INDEX, furi_string_get_cstr(signal_name)); + + do { + if(furi_string_empty(signal_name)) { + printf("Missing signal name.\r\n"); + break; + } + if(!infrared_brute_force_calculate_messages(brute_force)) { + printf("Invalid remote name.\r\n"); + break; + } + + uint32_t record_count; + bool running = infrared_brute_force_start( + brute_force, INFRARED_BRUTE_FORCE_DUMMY_INDEX, &record_count); + + if(record_count <= 0) { + printf("Invalid signal name.\r\n"); + break; + } + + printf("Sending %lu signal(s)...\r\n", record_count); + printf("Press Ctrl-C to stop.\r\n"); + + int records_sent = 0; + while(running) { + running = infrared_brute_force_send_next(brute_force); + + if(cli_cmd_interrupt_received(cli)) break; + + printf("\r%d%% complete.", (int)((float)records_sent++ / (float)record_count * 100)); + fflush(stdout); + } + + infrared_brute_force_stop(brute_force); + } while(false); + + furi_string_free(remote_path); + infrared_brute_force_reset(brute_force); + infrared_brute_force_free(brute_force); +} + +static void infrared_cli_process_universal(Cli* cli, FuriString* args) { + FuriString* arg1 = furi_string_alloc(); + FuriString* arg2 = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, arg1)) break; + if(!args_read_string_and_trim(args, arg2)) break; + } while(false); + + if(furi_string_empty(arg1)) { + printf("Wrong arguments.\r\n"); + infrared_cli_print_usage(); + } else if(furi_string_equal_str(arg1, "list")) { + infrared_cli_list_remote_signals(arg2); + } else { + infrared_cli_brute_force_signals(cli, arg1, arg2); + } + + furi_string_free(arg1); + furi_string_free(arg2); +} + +static void infrared_cli_start_ir(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + if(furi_hal_infrared_is_busy()) { + printf("INFRARED is busy. Exiting."); + return; + } + + FuriString* command; + command = furi_string_alloc(); + args_read_string_and_trim(args, command); + + size_t i = 0; + for(; i < COUNT_OF(infrared_cli_commands); ++i) { + size_t cmd_len = strlen(infrared_cli_commands[i].cmd); + if(!strncmp(furi_string_get_cstr(command), infrared_cli_commands[i].cmd, cmd_len)) { + break; + } + } + + if(i < COUNT_OF(infrared_cli_commands)) { + infrared_cli_commands[i].process_function(cli, args); + } else { + infrared_cli_print_usage(); + } + + furi_string_free(command); +} +void infrared_on_system_start() { +#ifdef SRV_CLI + Cli* cli = (Cli*)furi_record_open(RECORD_CLI); + cli_add_command(cli, "ir", CliCommandFlagDefault, infrared_cli_start_ir, NULL); + furi_record_close(RECORD_CLI); +#else + UNUSED(infrared_cli_start_ir); +#endif +} diff --git a/applications/infrared/infrared_custom_event.h b/applications/main/infrared/infrared_custom_event.h similarity index 92% rename from applications/infrared/infrared_custom_event.h rename to applications/main/infrared/infrared_custom_event.h index 09440ddec18..30bb0f729cd 100644 --- a/applications/infrared/infrared_custom_event.h +++ b/applications/main/infrared/infrared_custom_event.h @@ -15,9 +15,10 @@ enum InfraredCustomEventType { InfraredCustomEventTypeButtonSelected, InfraredCustomEventTypeBackPressed, - InfraredCustomEventTypeRpcLoad, + InfraredCustomEventTypeRpcLoadFile, InfraredCustomEventTypeRpcExit, - InfraredCustomEventTypeRpcButtonPress, + InfraredCustomEventTypeRpcButtonPressName, + InfraredCustomEventTypeRpcButtonPressIndex, InfraredCustomEventTypeRpcButtonRelease, InfraredCustomEventTypeRpcSessionClose, }; diff --git a/applications/main/infrared/infrared_remote.c b/applications/main/infrared/infrared_remote.c new file mode 100644 index 00000000000..cab241c1258 --- /dev/null +++ b/applications/main/infrared/infrared_remote.c @@ -0,0 +1,427 @@ +#include "infrared_remote.h" + +#include + +#include +#include +#include + +#define TAG "InfraredRemote" + +#define INFRARED_FILE_HEADER "IR signals file" +#define INFRARED_FILE_VERSION (1) + +ARRAY_DEF(StringArray, const char*, M_CSTR_DUP_OPLIST); //-V575 + +struct InfraredRemote { + StringArray_t signal_names; + FuriString* name; + FuriString* path; +}; + +typedef struct { + InfraredRemote* remote; + FlipperFormat* ff_in; + FlipperFormat* ff_out; + FuriString* signal_name; + InfraredSignal* signal; + size_t signal_index; +} InfraredBatch; + +typedef struct { + size_t signal_index; + const char* signal_name; + const InfraredSignal* signal; +} InfraredBatchTarget; + +typedef bool ( + *InfraredBatchCallback)(const InfraredBatch* batch, const InfraredBatchTarget* target); + +InfraredRemote* infrared_remote_alloc() { + InfraredRemote* remote = malloc(sizeof(InfraredRemote)); + StringArray_init(remote->signal_names); + remote->name = furi_string_alloc(); + remote->path = furi_string_alloc(); + return remote; +} + +void infrared_remote_free(InfraredRemote* remote) { + StringArray_clear(remote->signal_names); + furi_string_free(remote->path); + furi_string_free(remote->name); + free(remote); +} + +void infrared_remote_reset(InfraredRemote* remote) { + StringArray_reset(remote->signal_names); + furi_string_reset(remote->name); + furi_string_reset(remote->path); +} + +const char* infrared_remote_get_name(const InfraredRemote* remote) { + return furi_string_get_cstr(remote->name); +} + +static void infrared_remote_set_path(InfraredRemote* remote, const char* path) { + furi_string_set(remote->path, path); + path_extract_filename(remote->path, remote->name, true); +} + +const char* infrared_remote_get_path(const InfraredRemote* remote) { + return furi_string_get_cstr(remote->path); +} + +size_t infrared_remote_get_signal_count(const InfraredRemote* remote) { + return StringArray_size(remote->signal_names); +} + +const char* infrared_remote_get_signal_name(const InfraredRemote* remote, size_t index) { + furi_assert(index < infrared_remote_get_signal_count(remote)); + return *StringArray_cget(remote->signal_names, index); +} + +bool infrared_remote_load_signal( + const InfraredRemote* remote, + InfraredSignal* signal, + size_t index) { + furi_assert(index < infrared_remote_get_signal_count(remote)); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + + bool success = false; + + do { + const char* path = furi_string_get_cstr(remote->path); + if(!flipper_format_buffered_file_open_existing(ff, path)) break; + + if(!infrared_signal_search_by_index_and_read(signal, ff, index)) { + const char* signal_name = infrared_remote_get_signal_name(remote, index); + FURI_LOG_E(TAG, "Failed to load signal '%s' from file '%s'", signal_name, path); + break; + } + + success = true; + } while(false); + + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + + return success; +} + +bool infrared_remote_get_signal_index( + const InfraredRemote* remote, + const char* name, + size_t* index) { + uint32_t i = 0; + StringArray_it_t it; + + for(StringArray_it(it, remote->signal_names); !StringArray_end_p(it); + StringArray_next(it), ++i) { + if(strcmp(*StringArray_cref(it), name) == 0) { + *index = i; + return true; + } + } + + return false; +} + +bool infrared_remote_append_signal( + InfraredRemote* remote, + const InfraredSignal* signal, + const char* name) { + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_file_alloc(storage); + + bool success = false; + const char* path = furi_string_get_cstr(remote->path); + + do { + if(!flipper_format_file_open_append(ff, path)) break; + if(!infrared_signal_save(signal, ff, name)) break; + + StringArray_push_back(remote->signal_names, name); + success = true; + } while(false); + + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + + return success; +} + +static bool infrared_remote_batch_start( + InfraredRemote* remote, + InfraredBatchCallback batch_callback, + const InfraredBatchTarget* target) { + FuriString* tmp = furi_string_alloc(); + Storage* storage = furi_record_open(RECORD_STORAGE); + + InfraredBatch batch_context = { + .remote = remote, + .ff_in = flipper_format_buffered_file_alloc(storage), + .ff_out = flipper_format_buffered_file_alloc(storage), + .signal_name = furi_string_alloc(), + .signal = infrared_signal_alloc(), + .signal_index = 0, + }; + + const char* path_in = furi_string_get_cstr(remote->path); + const char* path_out; + + FS_Error status; + + do { + furi_string_printf(tmp, "%s.temp%08x.swp", path_in, rand()); + path_out = furi_string_get_cstr(tmp); + status = storage_common_stat(storage, path_out, NULL); + } while(status == FSE_OK || status == FSE_EXIST); + + bool success = false; + + do { + if(!flipper_format_buffered_file_open_existing(batch_context.ff_in, path_in)) break; + if(!flipper_format_buffered_file_open_always(batch_context.ff_out, path_out)) break; + if(!flipper_format_write_header_cstr( + batch_context.ff_out, INFRARED_FILE_HEADER, INFRARED_FILE_VERSION)) + break; + + const size_t signal_count = infrared_remote_get_signal_count(remote); + + for(; batch_context.signal_index < signal_count; ++batch_context.signal_index) { + if(!infrared_signal_read( + batch_context.signal, batch_context.ff_in, batch_context.signal_name)) + break; + if(!batch_callback(&batch_context, target)) break; + } + + if(batch_context.signal_index != signal_count) break; + + if(!flipper_format_buffered_file_close(batch_context.ff_out)) break; + if(!flipper_format_buffered_file_close(batch_context.ff_in)) break; + + const FS_Error status = storage_common_rename(storage, path_out, path_in); + success = (status == FSE_OK || status == FSE_EXIST); + } while(false); + + infrared_signal_free(batch_context.signal); + furi_string_free(batch_context.signal_name); + flipper_format_free(batch_context.ff_out); + flipper_format_free(batch_context.ff_in); + furi_string_free(tmp); + + furi_record_close(RECORD_STORAGE); + + return success; +} + +static bool infrared_remote_insert_signal_callback( + const InfraredBatch* batch, + const InfraredBatchTarget* target) { + // Insert a signal under the specified index + if(batch->signal_index == target->signal_index) { + if(!infrared_signal_save(target->signal, batch->ff_out, target->signal_name)) return false; + StringArray_push_at( + batch->remote->signal_names, target->signal_index, target->signal_name); + } + + // Write the rest normally + return infrared_signal_save( + batch->signal, batch->ff_out, furi_string_get_cstr(batch->signal_name)); +} + +bool infrared_remote_insert_signal( + InfraredRemote* remote, + const InfraredSignal* signal, + const char* name, + size_t index) { + if(index >= infrared_remote_get_signal_count(remote)) { + return infrared_remote_append_signal(remote, signal, name); + } + + const InfraredBatchTarget insert_target = { + .signal_index = index, + .signal_name = name, + .signal = signal, + }; + + return infrared_remote_batch_start( + remote, infrared_remote_insert_signal_callback, &insert_target); +} + +static bool infrared_remote_rename_signal_callback( + const InfraredBatch* batch, + const InfraredBatchTarget* target) { + const char* signal_name; + + if(batch->signal_index == target->signal_index) { + // Rename the signal at requested index + signal_name = target->signal_name; + StringArray_set_at(batch->remote->signal_names, batch->signal_index, signal_name); + } else { + // Use the original name otherwise + signal_name = furi_string_get_cstr(batch->signal_name); + } + + return infrared_signal_save(batch->signal, batch->ff_out, signal_name); +} + +bool infrared_remote_rename_signal(InfraredRemote* remote, size_t index, const char* new_name) { + furi_assert(index < infrared_remote_get_signal_count(remote)); + + const InfraredBatchTarget rename_target = { + .signal_index = index, + .signal_name = new_name, + .signal = NULL, + }; + + return infrared_remote_batch_start( + remote, infrared_remote_rename_signal_callback, &rename_target); +} + +static bool infrared_remote_delete_signal_callback( + const InfraredBatch* batch, + const InfraredBatchTarget* target) { + if(batch->signal_index == target->signal_index) { + // Do not save the signal to be deleted, remove it from the signal name list instead + StringArray_remove_v( + batch->remote->signal_names, batch->signal_index, batch->signal_index + 1); + } else { + // Pass other signals through + return infrared_signal_save( + batch->signal, batch->ff_out, furi_string_get_cstr(batch->signal_name)); + } + + return true; +} + +bool infrared_remote_delete_signal(InfraredRemote* remote, size_t index) { + furi_assert(index < infrared_remote_get_signal_count(remote)); + + const InfraredBatchTarget delete_target = { + .signal_index = index, + .signal_name = NULL, + .signal = NULL, + }; + + return infrared_remote_batch_start( + remote, infrared_remote_delete_signal_callback, &delete_target); +} + +bool infrared_remote_move_signal(InfraredRemote* remote, size_t index, size_t new_index) { + const size_t signal_count = infrared_remote_get_signal_count(remote); + furi_assert(index < signal_count); + furi_assert(new_index < signal_count); + + if(index == new_index) return true; + + InfraredSignal* signal = infrared_signal_alloc(); + char* signal_name = strdup(infrared_remote_get_signal_name(remote, index)); + + bool success = false; + + do { + if(!infrared_remote_load_signal(remote, signal, index)) break; + if(!infrared_remote_delete_signal(remote, index)) break; + if(!infrared_remote_insert_signal(remote, signal, signal_name, new_index)) break; + + success = true; + } while(false); + + free(signal_name); + infrared_signal_free(signal); + + return success; +} + +bool infrared_remote_create(InfraredRemote* remote, const char* path) { + FURI_LOG_I(TAG, "Creating new file: '%s'", path); + + infrared_remote_reset(remote); + infrared_remote_set_path(remote, path); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_file_alloc(storage); + + bool success = false; + + do { + if(!flipper_format_file_open_always(ff, path)) break; + if(!flipper_format_write_header_cstr(ff, INFRARED_FILE_HEADER, INFRARED_FILE_VERSION)) + break; + + success = true; + } while(false); + + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + + return success; +} + +bool infrared_remote_load(InfraredRemote* remote, const char* path) { + FURI_LOG_I(TAG, "Loading file: '%s'", path); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + + FuriString* tmp = furi_string_alloc(); + bool success = false; + + do { + if(!flipper_format_buffered_file_open_existing(ff, path)) break; + + uint32_t version; + if(!flipper_format_read_header(ff, tmp, &version)) break; + + if(!furi_string_equal(tmp, INFRARED_FILE_HEADER) || (version != INFRARED_FILE_VERSION)) + break; + + infrared_remote_set_path(remote, path); + StringArray_reset(remote->signal_names); + + while(infrared_signal_read_name(ff, tmp)) { + StringArray_push_back(remote->signal_names, furi_string_get_cstr(tmp)); + } + + success = true; + } while(false); + + furi_string_free(tmp); + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + + return success; +} + +bool infrared_remote_rename(InfraredRemote* remote, const char* new_path) { + const char* old_path = infrared_remote_get_path(remote); + + Storage* storage = furi_record_open(RECORD_STORAGE); + const FS_Error status = storage_common_rename(storage, old_path, new_path); + furi_record_close(RECORD_STORAGE); + + const bool success = (status == FSE_OK || status == FSE_EXIST); + + if(success) { + infrared_remote_set_path(remote, new_path); + } + + return success; +} + +bool infrared_remote_remove(InfraredRemote* remote) { + Storage* storage = furi_record_open(RECORD_STORAGE); + const FS_Error status = storage_common_remove(storage, infrared_remote_get_path(remote)); + furi_record_close(RECORD_STORAGE); + + const bool success = (status == FSE_OK || status == FSE_NOT_EXIST); + + if(success) { + infrared_remote_reset(remote); + } + + return success; +} diff --git a/applications/main/infrared/infrared_remote.h b/applications/main/infrared/infrared_remote.h new file mode 100644 index 00000000000..7477cd3b5af --- /dev/null +++ b/applications/main/infrared/infrared_remote.h @@ -0,0 +1,229 @@ +/** + * @file infrared_remote.h + * @brief Infrared remote library. + * + * An infrared remote contains zero or more infrared signals which + * have a (possibly non-unique) name each. + * + * The current implementation does load only the names into the memory, + * while the signals themselves are loaded on-demand one by one. In theory, + * this should allow for quite large remotes with relatively bulky signals. + */ +#pragma once + +#include "infrared_signal.h" + +/** + * @brief InfraredRemote opaque type declaration. + */ +typedef struct InfraredRemote InfraredRemote; + +/** + * @brief Create a new InfraredRemote instance. + * + * @returns pointer to the created instance. + */ +InfraredRemote* infrared_remote_alloc(); + +/** + * @brief Delete an InfraredRemote instance. + * + * @param[in,out] remote pointer to the instance to be deleted. + */ +void infrared_remote_free(InfraredRemote* remote); + +/** + * @brief Reset an InfraredRemote instance. + * + * Resetting a remote clears its signal name list and + * the associated file path. + * + * @param[in,out] remote pointer to the instance to be deleted. + */ +void infrared_remote_reset(InfraredRemote* remote); + +/** + * @brief Get an InfraredRemote instance's name. + * + * The name is deduced from the file path. + * + * The return value remains valid unless one of the following functions is called: + * - infrared_remote_reset() + * - infrared_remote_load() + * - infrared_remote_create() + * + * @param[in] remote pointer to the instance to be queried. + * @returns pointer to a zero-terminated string containing the name. + */ +const char* infrared_remote_get_name(const InfraredRemote* remote); + +/** + * @brief Get an InfraredRemote instance's file path. + * + * Same return value validity considerations as infrared_remote_get_name(). + * + * @param[in] remote pointer to the instance to be queried. + * @returns pointer to a zero-terminated string containing the path. + */ +const char* infrared_remote_get_path(const InfraredRemote* remote); + +/** + * @brief Get the number of signals listed in an InfraredRemote instance. + * + * @param[in] remote pointer to the instance to be queried. + * @returns number of signals, zero or more + */ +size_t infrared_remote_get_signal_count(const InfraredRemote* remote); + +/** + * @brief Get the name of a signal listed in an InfraredRemote instance. + * + * @param[in] remote pointer to the instance to be queried. + * @param[in] index index of the signal in question. Must be less than the total signal count. + */ +const char* infrared_remote_get_signal_name(const InfraredRemote* remote, size_t index); + +/** + * @brief Get the index of a signal listed in an InfraredRemote instance by its name. + * + * @param[in] remote pointer to the instance to be queried. + * @param[in] name pointer to a zero-terminated string containig the name of the signal in question. + * @param[out] index pointer to the variable to hold the signal index. + * @returns true if a signal with the given name was found, false otherwise. + */ +bool infrared_remote_get_signal_index( + const InfraredRemote* remote, + const char* name, + size_t* index); + +/** + * @brief Load a signal listed in an InfraredRemote instance. + * + * As mentioned above, the signals are loaded on-demand. The user code must call this function + * each time it wants to interact with a new signal. + * + * @param[in] remote pointer to the instance to load from. + * @param[out] signal pointer to the signal to load into. Must be allocated. + * @param[in] index index of the signal to be loaded. Must be less than the total signal count. + * @return true if the signal was successfully loaded, false otherwise. + */ +bool infrared_remote_load_signal( + const InfraredRemote* remote, + InfraredSignal* signal, + size_t index); + +/** + * @brief Append a signal to the file associated with an InfraredRemote instance. + * + * The file path must be somehow initialised first by calling either infrared_remote_load() or + * infrared_remote_create(). As the name suggests, the signal will be put in the end of the file. + * + * @param[in,out] remote pointer to the instance to append to. + * @param[in] signal pointer to the signal to be appended. + * @param[in] name pointer to a zero-terminated string containing the name of the signal. + * @returns true if the signal was successfully appended, false otherwise. + */ +bool infrared_remote_append_signal( + InfraredRemote* remote, + const InfraredSignal* signal, + const char* name); + +/** + * @brief Insert a signal to the file associated with an InfraredRemote instance. + * + * Same behaviour as infrared_remote_append_signal(), but the user code can decide where to + * put the signal in the file. + * + * Index values equal to or greater than the total signal count will result in behaviour + * identical to infrared_remote_append_signal(). + * + * @param[in,out] remote pointer to the instance to insert to. + * @param[in] signal pointer to the signal to be inserted. + * @param[in] name pointer to a zero-terminated string containing the name of the signal. + * @param[in] index the index under which the signal shall be inserted. + * @returns true if the signal was successfully inserted, false otherwise. + */ +bool infrared_remote_insert_signal( + InfraredRemote* remote, + const InfraredSignal* signal, + const char* name, + size_t index); + +/** + * @brief Rename a signal in the file associated with an InfraredRemote instance. + * + * Only changes the signal's name, but neither its position nor contents. + * + * @param[in,out] remote pointer to the instance to be modified. + * @param[in] index index of the signal to be renamed. Must be less than the total signal count. + * @param[in] new_name pointer to a zero-terminated string containig the signal's new name. + * @returns true if the signal was successfully renamed, false otherwise. + */ +bool infrared_remote_rename_signal(InfraredRemote* remote, size_t index, const char* new_name); + +/** + * @brief Change a signal's position in the file associated with an InfraredRemote instance. + * + * Only changes the signal's position (index), but neither its name nor contents. + * + * @param[in,out] remote pointer to the instance to be modified. + * @param[in] index index of the signal to be moved. Must be less than the total signal count. + * @param[in] new_index index of the signal to be moved. Must be less than the total signal count. + */ +bool infrared_remote_move_signal(InfraredRemote* remote, size_t index, size_t new_index); + +/** + * @brief Delete a signal in the file associated with an InfraredRemote instance. + * + * @param[in,out] remote pointer to the instance to be modified. + * @param[in] index index of the signal to be deleted. Must be less than the total signal count. + * @returns true if the signal was successfully deleted, false otherwise. + */ +bool infrared_remote_delete_signal(InfraredRemote* remote, size_t index); + +/** + * @brief Create a new file and associate it with an InfraredRemote instance. + * + * The instance will be reset and given a new empty file with just the header. + * + * @param[in,out] remote pointer to the instance to be assigned with a new file. + * @param[in] path pointer to a zero-terminated string containing the full file path. + * @returns true if the file was successfully created, false otherwise. + */ +bool infrared_remote_create(InfraredRemote* remote, const char* path); + +/** + * @brief Associate an InfraredRemote instance with a file and load the signal names from it. + * + * The instance will be reset and fill its signal name list from the given file. + * The file must already exist and be valid. + * + * @param[in,out] remote pointer to the instance to be assigned with an existing file. + * @param[in] path pointer to a zero-terminated string containing the full file path. + * @returns true if the file was successfully loaded, false otherwise. + */ +bool infrared_remote_load(InfraredRemote* remote, const char* path); + +/** + * @brief Rename the file associated with an InfraredRemote instance. + * + * Only renames the file, no signals are added, moved or deleted. + * + * @param[in,out] remote pointer to the instance to be modified. + * @param[in] new_path pointer to a zero-terminated string containing the new full file path. + * @returns true if the file was successfully renamed, false otherwise. + */ +bool infrared_remote_rename(InfraredRemote* remote, const char* new_path); + +/** + * @brief Remove the file associated with an InfraredRemote instance. + * + * This operation is irreversible and fully deletes the remote file + * from the underlying filesystem. + * After calling this function, the instance becomes invalid until + * infrared_remote_create() or infrared_remote_load() are successfully executed. + * + * @param[in,out] remote pointer to the instance to be modified. + * @returns true if the file was successfully removed, false otherwise. + */ +bool infrared_remote_remove(InfraredRemote* remote); diff --git a/applications/main/infrared/infrared_signal.c b/applications/main/infrared/infrared_signal.c new file mode 100644 index 00000000000..2b0d3479111 --- /dev/null +++ b/applications/main/infrared/infrared_signal.c @@ -0,0 +1,318 @@ +#include "infrared_signal.h" + +#include +#include +#include +#include +#include + +#define TAG "InfraredSignal" + +#define INFRARED_SIGNAL_NAME_KEY "name" + +struct InfraredSignal { + bool is_raw; + union { + InfraredMessage message; + InfraredRawSignal raw; + } payload; +}; + +static void infrared_signal_clear_timings(InfraredSignal* signal) { + if(signal->is_raw) { + free(signal->payload.raw.timings); + signal->payload.raw.timings_size = 0; + signal->payload.raw.timings = NULL; + } +} + +static bool infrared_signal_is_message_valid(const InfraredMessage* message) { + if(!infrared_is_protocol_valid(message->protocol)) { + FURI_LOG_E(TAG, "Unknown protocol"); + return false; + } + + uint32_t address_length = infrared_get_protocol_address_length(message->protocol); + uint32_t address_mask = (1UL << address_length) - 1; + + if(message->address != (message->address & address_mask)) { + FURI_LOG_E( + TAG, + "Address is out of range (mask 0x%08lX): 0x%lX\r\n", + address_mask, + message->address); + return false; + } + + uint32_t command_length = infrared_get_protocol_command_length(message->protocol); + uint32_t command_mask = (1UL << command_length) - 1; + + if(message->command != (message->command & command_mask)) { + FURI_LOG_E( + TAG, + "Command is out of range (mask 0x%08lX): 0x%lX\r\n", + command_mask, + message->command); + return false; + } + + return true; +} + +static bool infrared_signal_is_raw_valid(const InfraredRawSignal* raw) { + if((raw->frequency > INFRARED_MAX_FREQUENCY) || (raw->frequency < INFRARED_MIN_FREQUENCY)) { + FURI_LOG_E( + TAG, + "Frequency is out of range (%X - %X): %lX", + INFRARED_MIN_FREQUENCY, + INFRARED_MAX_FREQUENCY, + raw->frequency); + return false; + + } else if((raw->duty_cycle <= 0) || (raw->duty_cycle > 1)) { + FURI_LOG_E(TAG, "Duty cycle is out of range (0 - 1): %f", (double)raw->duty_cycle); + return false; + + } else if((raw->timings_size <= 0) || (raw->timings_size > MAX_TIMINGS_AMOUNT)) { + FURI_LOG_E( + TAG, + "Timings amount is out of range (0 - %X): %zX", + MAX_TIMINGS_AMOUNT, + raw->timings_size); + return false; + } + + return true; +} + +static inline bool + infrared_signal_save_message(const InfraredMessage* message, FlipperFormat* ff) { + const char* protocol_name = infrared_get_protocol_name(message->protocol); + return flipper_format_write_string_cstr(ff, "type", "parsed") && + flipper_format_write_string_cstr(ff, "protocol", protocol_name) && + flipper_format_write_hex(ff, "address", (uint8_t*)&message->address, 4) && + flipper_format_write_hex(ff, "command", (uint8_t*)&message->command, 4); +} + +static inline bool infrared_signal_save_raw(const InfraredRawSignal* raw, FlipperFormat* ff) { + furi_assert(raw->timings_size <= MAX_TIMINGS_AMOUNT); + return flipper_format_write_string_cstr(ff, "type", "raw") && + flipper_format_write_uint32(ff, "frequency", &raw->frequency, 1) && + flipper_format_write_float(ff, "duty_cycle", &raw->duty_cycle, 1) && + flipper_format_write_uint32(ff, "data", raw->timings, raw->timings_size); +} + +static inline bool infrared_signal_read_message(InfraredSignal* signal, FlipperFormat* ff) { + FuriString* buf; + buf = furi_string_alloc(); + bool success = false; + + do { + if(!flipper_format_read_string(ff, "protocol", buf)) break; + + InfraredMessage message; + message.protocol = infrared_get_protocol_by_name(furi_string_get_cstr(buf)); + + success = flipper_format_read_hex(ff, "address", (uint8_t*)&message.address, 4) && + flipper_format_read_hex(ff, "command", (uint8_t*)&message.command, 4) && + infrared_signal_is_message_valid(&message); + + if(!success) break; + + infrared_signal_set_message(signal, &message); + } while(0); + + furi_string_free(buf); + return success; +} + +static inline bool infrared_signal_read_raw(InfraredSignal* signal, FlipperFormat* ff) { + uint32_t timings_size, frequency; + float duty_cycle; + + bool success = flipper_format_read_uint32(ff, "frequency", &frequency, 1) && + flipper_format_read_float(ff, "duty_cycle", &duty_cycle, 1) && + flipper_format_get_value_count(ff, "data", &timings_size); + + if(!success || timings_size > MAX_TIMINGS_AMOUNT) { + return false; + } + + uint32_t* timings = malloc(sizeof(uint32_t) * timings_size); + success = flipper_format_read_uint32(ff, "data", timings, timings_size); + + if(success) { + infrared_signal_set_raw_signal(signal, timings, timings_size, frequency, duty_cycle); + } + + free(timings); + return success; +} + +static bool infrared_signal_read_body(InfraredSignal* signal, FlipperFormat* ff) { + FuriString* tmp = furi_string_alloc(); + + bool success = false; + + do { + if(!flipper_format_read_string(ff, "type", tmp)) break; + if(furi_string_equal(tmp, "raw")) { + success = infrared_signal_read_raw(signal, ff); + } else if(furi_string_equal(tmp, "parsed")) { + success = infrared_signal_read_message(signal, ff); + } else { + FURI_LOG_E(TAG, "Unknown signal type"); + } + } while(false); + + furi_string_free(tmp); + return success; +} + +InfraredSignal* infrared_signal_alloc() { + InfraredSignal* signal = malloc(sizeof(InfraredSignal)); + + signal->is_raw = false; + signal->payload.message.protocol = InfraredProtocolUnknown; + + return signal; +} + +void infrared_signal_free(InfraredSignal* signal) { + infrared_signal_clear_timings(signal); + free(signal); +} + +bool infrared_signal_is_raw(const InfraredSignal* signal) { + return signal->is_raw; +} + +bool infrared_signal_is_valid(const InfraredSignal* signal) { + return signal->is_raw ? infrared_signal_is_raw_valid(&signal->payload.raw) : + infrared_signal_is_message_valid(&signal->payload.message); +} + +void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other) { + if(other->is_raw) { + const InfraredRawSignal* raw = &other->payload.raw; + infrared_signal_set_raw_signal( + signal, raw->timings, raw->timings_size, raw->frequency, raw->duty_cycle); + } else { + const InfraredMessage* message = &other->payload.message; + infrared_signal_set_message(signal, message); + } +} + +void infrared_signal_set_raw_signal( + InfraredSignal* signal, + const uint32_t* timings, + size_t timings_size, + uint32_t frequency, + float duty_cycle) { + infrared_signal_clear_timings(signal); + + signal->is_raw = true; + + signal->payload.raw.timings_size = timings_size; + signal->payload.raw.frequency = frequency; + signal->payload.raw.duty_cycle = duty_cycle; + + signal->payload.raw.timings = malloc(timings_size * sizeof(uint32_t)); + memcpy(signal->payload.raw.timings, timings, timings_size * sizeof(uint32_t)); +} + +const InfraredRawSignal* infrared_signal_get_raw_signal(const InfraredSignal* signal) { + furi_assert(signal->is_raw); + return &signal->payload.raw; +} + +void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message) { + infrared_signal_clear_timings(signal); + + signal->is_raw = false; + signal->payload.message = *message; +} + +const InfraredMessage* infrared_signal_get_message(const InfraredSignal* signal) { + furi_assert(!signal->is_raw); + return &signal->payload.message; +} + +bool infrared_signal_save(const InfraredSignal* signal, FlipperFormat* ff, const char* name) { + if(!flipper_format_write_comment_cstr(ff, "") || + !flipper_format_write_string_cstr(ff, INFRARED_SIGNAL_NAME_KEY, name)) { + return false; + } else if(signal->is_raw) { + return infrared_signal_save_raw(&signal->payload.raw, ff); + } else { + return infrared_signal_save_message(&signal->payload.message, ff); + } +} + +bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name) { + bool success = false; + + do { + if(!infrared_signal_read_name(ff, name)) break; + if(!infrared_signal_read_body(signal, ff)) break; + + success = true; //-V779 + } while(false); + + return success; +} + +bool infrared_signal_read_name(FlipperFormat* ff, FuriString* name) { + return flipper_format_read_string(ff, INFRARED_SIGNAL_NAME_KEY, name); +} + +bool infrared_signal_search_by_name_and_read( + InfraredSignal* signal, + FlipperFormat* ff, + const char* name) { + bool success = false; + FuriString* tmp = furi_string_alloc(); + + while(infrared_signal_read_name(ff, tmp)) { + if(furi_string_equal(tmp, name)) { + success = infrared_signal_read_body(signal, ff); + break; + } + } + + furi_string_free(tmp); + return success; +} + +bool infrared_signal_search_by_index_and_read( + InfraredSignal* signal, + FlipperFormat* ff, + size_t index) { + bool success = false; + FuriString* tmp = furi_string_alloc(); + + for(uint32_t i = 0; infrared_signal_read_name(ff, tmp); ++i) { + if(i == index) { + success = infrared_signal_read_body(signal, ff); + break; + } + } + + furi_string_free(tmp); + return success; +} + +void infrared_signal_transmit(const InfraredSignal* signal) { + if(signal->is_raw) { + const InfraredRawSignal* raw_signal = &signal->payload.raw; + infrared_send_raw_ext( + raw_signal->timings, + raw_signal->timings_size, + true, + raw_signal->frequency, + raw_signal->duty_cycle); + } else { + const InfraredMessage* message = &signal->payload.message; + infrared_send(message, 1); + } +} diff --git a/applications/main/infrared/infrared_signal.h b/applications/main/infrared/infrared_signal.h new file mode 100644 index 00000000000..cfa4cfa94e2 --- /dev/null +++ b/applications/main/infrared/infrared_signal.h @@ -0,0 +1,205 @@ +/** + * @file infrared_signal.h + * @brief Infrared signal library. + * + * Infrared signals may be of two types: + * - known to the infrared signal decoder, or *parsed* signals + * - the rest, or *raw* signals, which are treated merely as a set of timings. + */ +#pragma once + +#include +#include + +/** + * @brief InfraredSignal opaque type declaration. + */ +typedef struct InfraredSignal InfraredSignal; + +/** + * @brief Raw signal type definition. + * + * Measurement units used: + * - time: microseconds (uS) + * - frequency: Hertz (Hz) + * - duty_cycle: no units, fraction between 0 and 1. + */ +typedef struct { + size_t timings_size; /**< Number of elements in the timings array. */ + uint32_t* timings; /**< Pointer to an array of timings describing the signal. */ + uint32_t frequency; /**< Carrier frequency of the signal. */ + float duty_cycle; /**< Duty cycle of the signal. */ +} InfraredRawSignal; + +/** + * @brief Create a new InfraredSignal instance. + * + * @returns pointer to the instance created. + */ +InfraredSignal* infrared_signal_alloc(); + +/** + * @brief Delete an InfraredSignal instance. + * + * @param[in,out] signal pointer to the instance to be deleted. + */ +void infrared_signal_free(InfraredSignal* signal); + +/** + * @brief Test whether an InfraredSignal instance holds a raw signal. + * + * @param[in] signal pointer to the instance to be tested. + * @returns true if the instance holds a raw signal, false otherwise. + */ +bool infrared_signal_is_raw(const InfraredSignal* signal); + +/** + * @brief Test whether an InfraredSignal instance holds any signal. + * + * @param[in] signal pointer to the instance to be tested. + * @returns true if the instance holds raw signal, false otherwise. + */ +bool infrared_signal_is_valid(const InfraredSignal* signal); + +/** + * @brief Set an InfraredInstance to hold the signal from another one. + * + * Any instance's previous contents will be automatically deleted before + * copying the source instance's contents. + * + * @param[in,out] signal pointer to the destination instance. + * @param[in] other pointer to the source instance. + */ +void infrared_signal_set_signal(InfraredSignal* signal, const InfraredSignal* other); + +/** + * @brief Set an InfraredInstance to hold a raw signal. + * + * Any instance's previous contents will be automatically deleted before + * copying the raw signal. + * + * After this call, infrared_signal_is_raw() will return true. + * + * @param[in,out] signal pointer to the destination instance. + * @param[in] timings pointer to an array containing the raw signal timings. + * @param[in] timings_size number of elements in the timings array. + * @param[in] frequency signal carrier frequency, in Hertz. + * @param[in] duty_cycle signal duty cycle, fraction between 0 and 1. + */ +void infrared_signal_set_raw_signal( + InfraredSignal* signal, + const uint32_t* timings, + size_t timings_size, + uint32_t frequency, + float duty_cycle); + +/** + * @brief Get the raw signal held by an InfraredSignal instance. + * + * @warning the instance MUST hold a *raw* signal, otherwise undefined behaviour will occur. + * + * @param[in] signal pointer to the instance to be queried. + * @returns pointer to the raw signal structure held by the instance. + */ +const InfraredRawSignal* infrared_signal_get_raw_signal(const InfraredSignal* signal); + +/** + * @brief Set an InfraredInstance to hold a parsed signal. + * + * Any instance's previous contents will be automatically deleted before + * copying the raw signal. + * + * After this call, infrared_signal_is_raw() will return false. + * + * @param[in,out] signal pointer to the destination instance. + * @param[in] message pointer to the message containing the parsed signal. + */ +void infrared_signal_set_message(InfraredSignal* signal, const InfraredMessage* message); + +/** + * @brief Get the parsed signal held by an InfraredSignal instance. + * + * @warning the instance MUST hold a *parsed* signal, otherwise undefined behaviour will occur. + * + * @param[in] signal pointer to the instance to be queried. + * @returns pointer to the parsed signal structure held by the instance. + */ +const InfraredMessage* infrared_signal_get_message(const InfraredSignal* signal); + +/** + * @brief Read a signal from a FlipperFormat file into an InfraredSignal instance. + * + * The file must be allocated and open prior to this call. The seek position determines + * which signal will be read (if there is more than one in the file). Calling this function + * repeatedly will result in all signals in the file to be read until no more are left. + * + * @param[in,out] signal pointer to the instance to be read into. + * @param[in,out] ff pointer to the FlipperFormat file instance to read from. + * @param[out] name pointer to the string to hold the signal name. Must be properly allocated. + * @returns true if a signal was successfully read, false otherwise (e.g. no more signals to read). + */ +bool infrared_signal_read(InfraredSignal* signal, FlipperFormat* ff, FuriString* name); + +/** + * @brief Read a signal name from a FlipperFormat file. + * + * Same behaviour as infrared_signal_read(), but only the name is read. + * + * @param[in,out] ff pointer to the FlipperFormat file instance to read from. + * @param[out] name pointer to the string to hold the signal name. Must be properly allocated. + * @returns true if a signal name was successfully read, false otherwise (e.g. no more signals to read). + */ +bool infrared_signal_read_name(FlipperFormat* ff, FuriString* name); + +/** + * @brief Read a signal with a particular name from a FlipperFormat file into an InfraredSignal instance. + * + * This function will look for a signal with the given name and if found, attempt to read it. + * Same considerations apply as to infrared_signal_read(). + * + * @param[in,out] signal pointer to the instance to be read into. + * @param[in,out] ff pointer to the FlipperFormat file instance to read from. + * @param[in] name pointer to a zero-terminated string containing the requested signal name. + * @returns true if a signal was found and successfully read, false otherwise (e.g. the signal was not found). + */ +bool infrared_signal_search_by_name_and_read( + InfraredSignal* signal, + FlipperFormat* ff, + const char* name); + +/** + * @brief Read a signal with a particular index from a FlipperFormat file into an InfraredSignal instance. + * + * This function will look for a signal with the given index and if found, attempt to read it. + * Same considerations apply as to infrared_signal_read(). + * + * @param[in,out] signal pointer to the instance to be read into. + * @param[in,out] ff pointer to the FlipperFormat file instance to read from. + * @param[in] index the requested signal index. + * @returns true if a signal was found and successfully read, false otherwise (e.g. the signal was not found). + */ +bool infrared_signal_search_by_index_and_read( + InfraredSignal* signal, + FlipperFormat* ff, + size_t index); + +/** + * @brief Save a signal contained in an InfraredSignal instance to a FlipperFormat file. + * + * The file must be allocated and open prior to this call. Additionally, an appropriate header + * must be already written into the file. + * + * @param[in] signal pointer to the instance holding the signal to be saved. + * @param[in,out] ff pointer to the FlipperFormat file instance to write to. + * @param[in] name pointer to a zero-terminated string contating the name of the signal. + */ +bool infrared_signal_save(const InfraredSignal* signal, FlipperFormat* ff, const char* name); + +/** + * @brief Transmit a signal contained in an InfraredSignal instance. + * + * The transmission happens once per call using the built-in hardware (via HAL calls). + * + * @param[in] signal pointer to the instance holding the signal to be transmitted. + */ +void infrared_signal_transmit(const InfraredSignal* signal); diff --git a/applications/main/infrared/resources/infrared/assets/ac.ir b/applications/main/infrared/resources/infrared/assets/ac.ir new file mode 100644 index 00000000000..ac7628e1785 --- /dev/null +++ b/applications/main/infrared/resources/infrared/assets/ac.ir @@ -0,0 +1,620 @@ +Filetype: IR library file +Version: 1 +# +# Model: Electrolux EACM-16 HP/N3 +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 502 3436 510 475 509 476 508 477 507 477 507 479 505 480 504 480 504 490 504 481 502 482 501 483 563 420 511 474 510 475 509 476 508 485 561 423 508 476 508 477 507 478 506 479 505 480 504 481 503 517 508 476 508 478 506 479 505 479 505 481 503 483 521 1456 501 498 507 479 505 480 504 481 503 482 501 483 563 421 562 422 509 499 506 479 505 480 504 481 503 482 502 484 510 1451 506 479 505 1542 562 1396 509 471 502 476 508 469 504 3425 511 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 507 3430 506 479 505 480 504 481 503 481 503 483 501 485 509 1453 504 1465 503 482 502 483 511 473 500 485 509 476 508 477 507 478 506 487 507 477 507 478 506 479 505 480 504 482 502 483 501 484 500 523 503 482 502 484 500 485 509 476 508 476 508 478 506 1456 501 501 504 482 502 483 501 484 500 485 509 476 508 477 507 1455 502 509 506 479 505 1457 500 485 509 476 508 1454 503 482 502 483 501 568 499 1459 509 1450 507 471 502 474 510 3421 505 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 504 3433 503 482 502 484 510 474 510 475 509 476 508 478 506 1456 564 1405 510 475 509 476 508 502 482 477 507 478 506 479 505 480 504 489 505 480 504 481 503 482 502 483 511 473 511 474 510 475 509 509 506 479 505 480 504 481 503 482 512 473 511 474 510 476 508 1469 509 475 509 476 508 477 507 478 506 479 505 480 504 481 503 505 510 475 509 502 482 503 481 504 480 505 478 507 477 1459 509 560 507 1451 506 473 511 493 480 1450 507 3422 503 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 525 3615 530 506 561 474 562 474 562 473 563 473 531 505 562 1502 528 1542 562 474 562 474 530 505 531 504 532 504 532 504 616 419 533 510 589 447 526 509 527 509 527 509 527 508 528 508 528 507 529 542 525 510 526 509 527 509 527 509 527 508 528 508 528 1535 527 524 533 503 533 503 533 502 534 502 534 502 534 501 535 501 525 534 533 502 534 502 534 501 535 502 534 1529 533 503 533 503 533 587 533 497 528 501 524 1536 526 501 524 3609 526 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 531 3406 530 455 529 456 528 457 537 447 537 448 535 450 534 1429 528 1442 536 448 536 449 534 451 532 452 532 453 530 454 530 455 529 464 530 454 529 456 528 457 537 448 536 449 535 450 533 451 533 490 535 449 534 450 534 451 533 452 532 453 531 455 529 1433 534 1443 535 449 535 450 534 452 531 453 530 454 530 455 529 456 538 472 532 452 532 454 530 1433 535 1427 530 1432 536 1427 530 1431 537 1511 530 448 536 1422 535 1423 534 1422 535 3395 530 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 506 3430 506 478 506 479 505 480 504 481 503 482 502 484 500 1463 505 1465 503 482 502 483 501 484 500 485 509 476 508 477 507 478 506 486 508 477 507 478 506 479 505 480 504 481 503 482 502 483 500 523 502 482 502 483 501 484 500 485 509 476 508 478 506 1455 502 498 507 478 506 479 505 481 503 482 501 483 500 484 500 485 509 500 505 481 502 482 502 1461 507 1455 502 1459 509 476 508 477 507 563 504 1453 504 1454 503 1454 503 1453 504 3426 499 +# +# Model: Hisense Generic +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8974 4505 598 1647 595 1651 591 539 592 542 600 537 594 547 595 549 593 1662 591 532 599 1649 593 1659 594 540 602 538 593 548 594 552 600 535 596 528 593 535 596 536 595 540 602 538 593 548 593 551 601 532 599 525 596 1651 591 539 592 543 599 1660 593 1669 594 1672 601 533 598 526 595 533 598 533 598 536 595 543 599 543 599 547 595 540 602 524 597 530 601 530 601 534 597 541 601 541 601 545 597 522 599 7938 591 532 599 528 593 537 594 541 601 537 594 546 596 549 593 1663 600 524 597 530 601 530 601 533 598 540 591 550 592 553 599 536 595 528 593 536 595 536 595 540 591 547 595 548 593 552 600 535 596 529 592 536 595 535 596 538 593 545 597 546 596 550 592 542 600 524 597 531 600 531 600 534 597 542 600 542 600 545 597 538 593 530 601 526 595 537 594 540 591 547 595 546 595 550 592 543 599 526 595 532 599 531 601 535 596 542 600 542 600 546 596 538 593 531 600 1648 594 536 595 539 592 1669 594 1669 594 1671 602 1637 595 7947 592 532 599 529 592 539 592 543 599 540 591 551 601 544 598 537 594 531 600 1647 595 535 596 539 592 545 597 546 596 550 592 543 599 526 595 533 598 534 597 538 593 545 597 546 596 550 602 534 597 527 594 533 598 533 598 536 595 544 598 543 599 547 595 540 591 533 598 529 592 539 592 1663 600 538 593 547 595 551 591 543 599 524 597 530 591 539 592 542 600 537 594 547 595 550 592 542 600 525 596 1651 592 538 593 1662 591 546 596 545 597 548 594 523 629 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8990 4494 599 1648 595 1654 599 533 598 537 594 544 598 544 598 548 594 1662 601 523 598 1651 592 1660 593 542 600 539 593 550 592 553 599 536 596 527 594 531 601 1648 595 538 593 542 600 540 592 551 601 532 600 1643 600 1650 593 538 593 542 600 1660 593 1671 592 1673 601 534 598 527 594 534 597 533 599 536 595 543 599 542 600 545 597 537 595 530 591 536 596 535 596 538 593 544 598 543 599 546 596 522 599 7935 595 530 591 536 596 536 596 539 592 545 597 544 598 547 595 1660 593 530 591 536 596 535 596 536 595 542 600 541 591 552 600 534 597 525 596 531 601 529 592 541 601 537 595 546 596 548 594 540 591 532 600 527 594 536 595 538 594 544 598 543 599 546 596 538 594 531 600 527 594 536 595 539 593 545 597 543 599 546 596 538 593 530 591 535 596 532 599 532 600 536 595 543 599 544 598 535 596 525 596 530 591 538 593 538 593 542 600 540 591 551 601 532 600 1640 593 1651 592 1655 598 535 596 1657 596 1663 601 1661 592 1641 592 7941 599 526 595 533 599 532 600 535 597 541 601 541 601 544 598 537 595 1651 592 535 597 535 597 538 594 545 597 545 597 548 594 540 592 532 600 528 593 539 593 542 600 539 592 549 593 551 601 533 598 524 597 528 593 536 595 538 593 544 598 543 599 546 596 539 593 531 601 528 593 538 593 1661 592 546 596 545 597 547 595 539 592 532 600 527 594 536 596 538 593 543 599 542 600 544 598 535 596 1646 597 531 601 529 592 1663 601 537 595 547 595 550 592 524 597 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8982 4489 604 1643 600 1649 594 537 594 540 602 537 594 547 595 550 592 1664 599 525 596 1652 601 1651 592 542 600 538 593 549 593 552 600 534 597 1646 597 530 601 1651 602 534 597 541 601 541 601 544 598 537 594 529 592 1655 598 533 598 536 595 542 600 542 600 546 596 539 592 532 599 527 594 537 594 540 591 546 596 545 597 548 594 540 602 523 598 529 592 539 592 542 600 539 592 549 593 550 592 524 597 7929 600 525 596 532 599 533 598 537 594 543 599 542 600 544 598 1654 599 524 597 530 591 538 593 540 591 544 598 543 599 544 598 535 596 526 595 531 600 529 592 544 598 541 601 542 600 546 596 540 602 522 599 529 602 530 601 534 597 541 601 541 601 545 597 539 592 532 599 528 593 539 592 541 601 537 594 547 595 551 601 535 596 529 592 536 595 537 594 541 601 538 593 549 593 553 599 536 595 529 592 536 595 534 597 537 594 544 598 543 599 546 596 539 592 1653 600 1650 593 1660 593 543 599 541 601 541 601 546 596 1643 600 7943 596 529 602 527 594 538 593 541 601 538 593 549 593 553 599 536 595 1649 594 535 596 535 596 539 592 546 596 546 596 549 593 542 600 525 596 531 600 530 601 533 598 540 602 539 592 552 600 535 596 527 594 533 598 533 598 536 595 543 599 543 599 545 597 537 594 530 601 526 595 536 595 1660 593 546 596 547 595 550 602 533 598 526 595 533 598 534 597 538 593 546 596 547 595 551 601 534 597 1647 596 532 599 532 599 1656 597 541 601 542 600 545 597 522 599 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8967 4495 597 1645 597 1648 594 535 596 537 594 542 600 541 601 543 599 1655 598 525 596 1651 592 1658 595 539 592 545 597 544 598 546 596 538 593 1648 595 532 599 1648 594 539 592 545 597 543 599 545 597 536 595 528 593 1651 592 538 593 541 601 1654 599 1661 592 1671 592 541 601 523 598 529 592 538 593 541 601 536 595 544 598 547 595 539 592 531 600 526 595 535 596 537 594 543 599 541 601 543 598 518 593 7937 602 522 599 528 593 537 594 539 592 545 597 544 598 546 596 1656 597 525 596 530 601 528 593 539 592 544 598 543 599 544 598 535 596 526 595 531 600 530 601 532 599 538 593 547 595 549 593 540 602 521 600 526 595 535 596 537 594 543 599 541 601 543 599 536 595 527 594 532 600 530 601 532 599 538 593 546 596 548 594 539 592 531 600 525 596 534 597 535 596 540 591 549 593 551 601 532 599 524 597 529 592 537 594 538 593 543 599 540 591 551 601 532 599 1641 591 1654 599 1650 593 540 591 1664 599 1660 593 1671 592 1643 600 7922 596 528 593 533 598 532 599 535 596 540 591 549 593 552 600 533 598 1644 599 529 592 538 593 539 592 544 598 541 590 550 592 539 592 528 593 531 590 537 594 536 595 539 592 546 596 546 596 536 595 526 595 529 592 535 596 535 596 538 593 546 596 546 596 535 596 524 597 527 594 533 598 1649 593 541 601 538 593 549 593 538 593 528 593 532 599 528 593 539 592 542 600 538 593 548 594 538 593 1643 599 525 596 532 599 1649 593 541 601 538 593 548 594 520 591 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8970 4496 597 1648 595 1652 601 530 602 533 598 541 601 541 601 543 599 1652 601 523 598 1649 594 1656 597 538 593 545 597 545 597 549 593 541 601 523 598 529 592 1658 595 540 592 546 596 545 597 548 594 541 601 523 598 529 592 539 593 542 600 538 593 1668 595 1670 593 1662 591 533 599 529 592 539 593 542 600 538 593 547 595 549 593 542 600 524 597 530 602 529 592 543 599 539 593 549 593 551 601 516 595 7937 593 532 599 527 594 536 596 539 592 546 596 545 597 548 594 1661 592 532 600 528 593 538 593 541 601 537 594 547 595 550 602 533 599 526 595 533 599 533 599 536 595 543 599 541 601 544 598 536 595 529 592 535 596 535 596 538 594 544 598 544 598 547 595 541 601 523 598 528 593 537 594 540 602 536 595 546 596 550 602 533 598 526 595 532 600 531 600 534 597 541 601 541 601 545 597 539 593 532 600 528 593 538 593 541 601 537 594 547 595 550 602 532 599 523 598 527 594 1653 600 533 598 538 593 1664 599 1662 591 523 598 7926 593 529 592 534 597 532 599 534 597 538 593 546 596 547 595 537 594 1648 595 532 600 532 599 536 595 543 599 543 599 546 596 540 602 522 599 529 592 538 594 541 601 536 595 546 596 549 593 542 600 523 598 529 592 538 593 541 601 538 593 548 594 552 600 534 597 527 594 534 597 534 597 1657 596 543 599 543 599 548 594 542 600 524 597 530 601 530 602 533 598 538 593 547 595 551 601 533 599 1644 599 528 593 538 593 1661 592 545 597 545 597 548 594 524 597 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8972 4491 592 1651 592 1655 598 532 599 535 597 542 600 541 601 544 598 1656 597 526 595 1652 591 1658 595 539 593 545 597 545 597 546 596 537 594 529 592 535 596 1653 600 534 597 541 601 539 592 552 600 533 598 525 596 530 591 538 593 539 592 1665 598 1662 591 1673 601 533 598 526 595 533 598 532 600 534 597 540 591 548 594 550 592 542 600 523 598 528 593 536 595 537 594 543 599 542 600 543 599 517 594 7937 593 531 601 526 595 535 597 537 594 542 600 541 601 543 599 1654 599 523 598 528 593 536 596 538 594 542 600 541 590 552 600 532 599 524 597 528 593 536 595 537 595 541 601 539 593 551 591 542 600 522 599 527 594 536 595 537 594 543 599 540 591 552 600 532 600 523 598 527 594 535 596 537 595 542 600 540 591 552 600 532 600 523 598 528 593 536 595 538 593 543 599 541 601 543 599 535 596 527 594 532 600 531 601 534 597 540 592 549 593 552 600 534 597 525 596 529 592 1655 598 534 597 1656 597 1661 592 1671 592 1644 599 7934 596 529 592 535 597 535 597 538 593 544 598 543 599 545 597 538 593 1650 593 535 596 534 597 536 595 540 591 547 595 547 595 536 595 526 595 529 592 536 595 535 596 539 593 546 596 547 595 538 593 528 593 531 601 529 592 541 601 536 596 545 597 548 594 540 592 532 600 526 595 535 596 1656 597 541 601 540 592 553 599 534 597 526 595 532 599 531 600 533 598 539 593 548 594 552 600 535 596 1647 596 531 590 538 593 1656 597 538 594 545 597 545 597 518 593 +# +# Model: Daichi DA25AVQS1-W +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9131 4318 815 389 817 1523 784 391 815 1523 784 1494 813 422 784 391 816 421 785 1524 784 392 814 422 783 1525 782 424 782 422 784 422 784 422 784 396 809 423 783 422 782 423 783 423 783 1496 812 1525 782 424 783 423 783 422 783 423 783 394 811 1526 782 424 782 1524 782 423 783 423 782 1525 782 423 675 19938 810 425 781 424 674 502 811 423 675 531 675 531 675 531 676 530 675 531 675 531 675 502 704 530 675 530 676 530 676 502 704 530 676 530 676 530 675 530 675 530 676 530 675 530 676 530 676 529 677 529 677 529 677 530 677 529 677 1631 676 529 677 1630 678 1631 677 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9076 4455 677 1629 678 528 677 528 677 1630 677 529 677 530 676 530 676 531 675 531 675 531 675 531 675 531 674 531 675 532 674 531 674 532 674 531 675 532 674 531 674 531 674 532 674 1633 674 1633 674 532 674 532 674 532 674 532 674 532 674 1634 673 533 673 1634 673 533 674 532 674 1633 674 533 673 19965 674 531 674 532 675 531 675 531 675 531 675 531 675 531 675 531 675 532 674 531 675 531 676 531 674 531 675 531 675 531 674 531 675 531 675 532 674 532 674 531 675 532 674 532 674 531 675 531 675 532 674 532 674 532 674 532 674 1633 674 1633 674 532 674 532 674 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9076 4453 679 1628 703 503 703 503 702 1607 700 506 699 507 699 507 699 507 699 1609 698 1610 698 1609 699 507 699 508 698 508 698 508 698 508 698 508 698 507 699 508 698 508 698 508 697 1609 698 1610 698 508 698 508 698 508 698 508 698 509 697 1610 697 508 698 1610 698 508 698 508 698 1610 697 509 697 19941 699 507 699 507 699 507 699 508 698 508 698 508 698 508 698 508 697 508 698 508 698 507 699 508 698 508 698 508 698 508 698 508 698 508 698 508 697 509 697 509 697 508 698 508 698 508 698 508 698 509 697 508 697 509 697 509 696 509 697 1635 673 533 673 1611 696 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 148 110377 9082 4422 707 499 706 500 704 1603 703 1606 701 505 700 507 699 506 700 507 699 506 700 1607 700 1608 700 1609 699 506 699 507 699 506 699 507 699 507 699 506 700 507 699 507 699 507 699 1608 699 1607 699 507 699 507 699 507 699 507 699 507 699 1609 699 507 699 1608 699 507 699 507 699 1609 699 507 699 19940 700 506 700 506 699 507 699 507 699 506 700 506 700 507 699 507 699 507 700 507 699 506 699 507 699 507 699 507 699 507 699 507 699 507 699 507 699 507 698 507 700 507 699 507 699 507 699 507 699 507 699 508 698 508 698 507 699 507 699 508 698 1610 699 508 698 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9081 4423 707 499 706 500 704 1604 702 1606 701 505 701 506 700 506 700 506 700 1608 700 1607 700 1609 699 506 700 506 700 506 700 506 700 506 700 507 699 506 700 506 700 507 699 507 699 1608 700 1608 699 507 699 507 699 507 699 507 699 507 699 1608 699 507 698 1609 698 507 699 507 699 1609 699 507 699 19938 699 506 700 506 700 507 699 506 699 506 700 506 699 506 700 507 699 507 699 507 700 506 699 507 699 507 699 507 699 507 699 507 698 507 699 507 699 507 699 507 699 507 699 507 699 507 699 507 700 507 699 507 698 507 699 507 699 1609 699 507 698 1609 699 1609 699 +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9106 4398 731 499 706 500 705 502 702 504 701 505 701 505 701 1606 701 505 701 1607 701 505 701 506 700 1607 700 506 700 506 700 505 700 505 701 506 700 506 700 506 699 506 700 506 700 1607 700 506 700 506 700 506 700 505 701 506 700 506 700 1608 699 506 700 1608 699 506 700 506 700 1608 700 506 700 19941 701 1606 700 505 701 505 701 506 700 505 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 701 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 699 506 700 506 700 1608 700 1607 700 506 700 506 700 +# +# Model: Saturn CS-TL09CHR +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3014 1708 488 1059 464 1085 461 362 461 363 460 387 436 1085 462 362 461 402 436 1084 463 1083 463 364 459 1083 463 363 460 386 437 1082 518 1044 464 386 437 1108 439 1108 438 386 464 360 463 1082 464 360 490 352 459 1084 462 362 460 363 460 363 460 364 459 364 459 364 459 380 459 364 459 364 459 364 460 364 459 364 459 364 459 364 459 380 459 364 459 365 458 1088 459 364 459 365 458 1088 458 365 458 380 459 365 458 1088 459 365 458 364 459 365 459 365 458 365 458 380 458 1088 459 1088 459 1088 458 365 458 365 458 365 458 365 458 381 458 365 458 365 458 365 458 365 458 365 459 365 458 365 458 381 458 366 457 366 457 366 457 366 457 366 458 365 458 366 458 381 457 366 457 366 457 366 457 366 457 366 457 366 457 366 458 382 457 366 457 366 457 367 456 367 457 367 456 367 456 390 433 406 433 367 456 367 456 390 433 391 433 390 433 390 433 390 433 406 433 391 432 1114 432 391 432 391 432 391 432 391 432 1114 433 396 433 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3011 1710 462 1084 462 1085 486 356 467 356 444 364 484 1060 462 364 483 357 458 1085 461 1085 461 388 435 1085 461 388 435 388 435 1084 462 1099 463 387 436 1084 462 1085 461 388 461 362 461 1084 462 362 461 378 460 1087 459 365 458 365 458 366 457 366 457 366 457 366 457 382 457 366 457 366 457 366 457 367 456 367 456 367 456 367 456 382 457 367 456 367 456 1090 457 367 456 367 456 1090 457 367 456 382 457 1090 456 1090 457 367 456 367 456 367 456 367 456 367 456 382 457 1091 456 1091 456 1091 456 1090 456 367 456 367 456 367 457 382 457 367 456 367 456 367 456 367 456 368 456 367 456 368 455 383 456 367 456 367 456 368 455 368 455 368 455 368 456 368 455 383 456 368 455 368 455 368 455 368 455 368 455 368 455 368 455 383 456 368 455 368 455 368 455 368 455 368 455 368 455 368 455 384 455 368 455 368 455 368 455 368 455 368 455 368 455 368 455 383 456 1091 456 1091 456 368 456 1091 455 368 455 368 455 1091 456 373 456 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3012 1709 462 1083 463 1084 463 361 462 362 484 355 469 1059 463 387 436 402 437 1083 464 1083 464 362 462 1080 520 354 469 354 469 1026 521 1068 518 354 390 1108 439 1108 438 386 463 360 464 1081 465 359 464 375 463 1083 463 361 462 362 461 363 460 363 460 364 459 364 459 380 459 364 459 364 459 365 458 365 458 365 458 365 458 365 458 380 459 365 458 365 458 1089 458 365 459 365 458 1089 458 366 457 382 456 1090 457 1113 433 390 433 390 433 390 433 390 433 390 433 405 434 390 433 390 433 390 433 1113 434 390 433 390 433 390 433 405 434 390 433 390 433 390 434 390 433 390 433 390 433 390 433 405 434 390 433 390 433 390 433 390 433 390 433 390 433 390 434 405 433 390 433 390 433 390 433 390 433 390 433 390 433 390 433 406 433 390 433 390 433 390 433 390 433 390 433 391 432 391 432 406 433 390 433 391 432 391 432 391 433 390 433 390 433 391 432 406 433 391 432 391 432 1114 433 391 432 391 432 391 432 1114 433 396 433 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3011 1712 461 1087 459 1088 459 364 459 389 434 366 458 1086 461 388 435 404 435 1086 460 1085 461 388 435 1085 461 365 458 388 435 1084 462 1098 464 387 436 1084 462 1084 462 388 461 362 461 1084 462 362 461 378 460 1086 460 364 459 365 458 365 458 365 458 366 457 365 458 381 458 366 457 366 458 366 457 366 457 366 457 366 457 365 458 381 458 366 458 366 457 1089 458 366 457 366 457 1089 458 366 457 381 458 1089 458 366 457 366 457 366 457 366 458 366 457 366 457 381 458 366 457 366 457 366 457 366 457 366 457 366 457 366 457 381 458 366 457 366 457 366 458 366 457 366 457 366 457 366 457 382 457 366 457 366 457 366 457 366 457 366 457 366 457 366 457 382 457 366 457 366 457 367 456 367 456 367 457 366 457 366 457 382 457 367 457 366 457 367 457 366 457 367 457 366 457 366 457 382 457 367 456 367 456 367 456 367 456 367 456 367 456 367 456 382 457 367 456 1090 457 367 456 1091 456 1090 457 1090 456 367 456 373 456 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3045 1677 493 1023 524 1024 522 354 469 354 469 354 469 1026 576 353 470 350 487 1001 492 1054 492 354 469 1054 492 355 468 354 469 1054 492 1070 491 354 468 1056 438 1109 438 386 437 385 438 1107 439 385 438 400 464 1081 465 359 464 360 463 361 462 362 461 388 435 388 435 404 434 389 434 389 434 389 434 389 434 389 434 389 434 389 434 405 434 389 434 389 434 1112 434 389 434 389 434 1113 434 389 434 405 434 1112 435 389 434 366 458 365 458 365 458 365 458 365 458 381 458 365 458 366 457 365 458 1089 457 366 457 366 457 366 457 382 456 367 433 390 433 391 432 391 432 391 432 391 432 391 432 406 433 391 432 391 432 390 433 391 432 391 432 391 432 391 432 406 433 391 432 391 433 391 432 391 432 391 432 391 432 391 432 407 432 391 432 391 432 391 432 392 431 391 433 391 432 391 432 430 409 393 430 392 431 392 431 392 432 391 457 367 432 392 431 408 431 392 431 1138 433 391 408 392 431 392 456 367 456 1113 408 421 433 +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3013 1709 463 1085 462 1084 463 387 461 355 469 355 444 1084 463 387 461 378 436 1084 462 1084 487 355 469 1059 463 387 460 355 444 1083 463 1098 464 386 437 1083 463 1083 489 361 463 360 463 1082 464 361 462 376 462 1085 461 363 460 364 459 364 459 365 458 365 459 364 459 380 459 365 458 365 458 365 458 365 458 365 458 365 458 365 459 380 458 365 459 365 458 365 459 365 458 365 458 1089 458 365 458 380 459 1088 459 365 458 365 458 365 458 365 459 365 458 365 458 381 458 365 458 365 458 365 458 1089 458 365 458 365 458 365 458 381 458 365 458 365 458 365 458 365 458 365 458 365 458 365 458 381 457 366 457 366 457 366 457 366 457 366 458 365 458 366 457 381 458 366 458 366 457 366 457 366 457 366 457 366 457 366 457 381 458 366 457 366 457 366 457 366 457 366 457 366 457 366 457 382 457 366 457 366 457 366 457 366 457 366 457 367 457 366 457 382 457 367 456 1090 457 1090 456 1090 457 1090 457 1090 456 367 457 372 457 +# +# Model: Olimpia Splendid OS-SEAMH09EI +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4402 4336 561 1641 538 554 535 1616 563 1640 539 554 535 557 532 1645 534 560 539 551 538 1639 540 553 536 556 533 1644 535 1642 537 556 533 1620 558 558 531 561 538 554 535 1641 538 1639 540 1638 530 1620 559 1647 532 1643 536 1641 538 1613 566 553 536 557 532 560 539 553 536 558 531 560 529 1647 532 535 564 1613 555 537 563 1614 565 554 535 559 530 1645 534 559 530 1647 532 560 539 1612 557 562 537 1640 539 1611 557 5189 4398 4344 564 1638 530 563 537 1640 539 1639 529 563 537 530 559 1644 535 560 529 535 564 1639 529 537 563 556 533 1644 535 1643 536 557 532 1647 532 559 530 536 564 555 534 1643 536 1616 563 1640 539 1613 555 1624 565 1610 558 1619 560 1617 562 557 532 560 539 554 535 557 532 562 537 553 536 1641 538 555 534 1643 536 530 559 1644 535 558 531 564 536 1639 539 553 536 1641 537 555 534 1617 562 557 532 1645 534 1645 534 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4404 4345 563 1613 566 553 536 1641 538 1614 565 528 561 557 532 1645 534 560 539 551 538 1639 529 563 536 556 533 1618 561 1642 537 556 533 1645 534 557 532 561 538 1638 530 1646 533 1645 554 1623 556 1621 558 1596 562 1613 586 1591 588 531 537 555 534 559 530 562 537 555 534 561 538 552 537 555 534 559 530 562 537 555 534 559 530 562 537 557 532 1643 536 1615 564 1639 560 1591 588 1616 563 1588 580 1623 556 1621 537 5183 4404 4339 579 1597 581 538 530 1646 533 1619 580 539 529 563 536 1615 564 557 532 532 557 1646 533 560 539 553 536 1641 538 1639 539 554 535 1643 536 555 534 559 530 1647 532 1646 533 1644 535 1617 582 1620 559 1621 537 1612 587 1590 589 530 538 554 535 558 531 561 538 554 535 560 539 551 538 554 535 558 531 561 538 554 535 557 532 561 538 556 533 1642 537 1640 538 1613 565 1638 561 1590 589 1588 580 1623 556 1598 560 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4400 4357 561 1616 563 530 559 1619 559 1644 535 557 532 535 564 1613 555 565 534 556 533 1645 534 533 556 536 563 1614 565 1639 529 537 563 1643 536 529 560 558 531 1647 532 1645 533 1618 561 1643 536 1642 537 1642 537 1613 565 1638 530 537 563 556 533 559 530 563 536 556 533 562 538 527 562 1615 563 555 534 1643 536 531 558 561 538 554 535 560 539 1610 558 560 539 1612 556 563 537 1640 538 1639 539 1612 556 1620 558 5189 4398 4345 562 1614 564 529 560 1643 536 1616 563 531 558 534 565 1639 529 565 534 556 533 1619 559 559 530 563 537 1641 538 1640 538 554 535 1645 534 531 558 560 539 1639 529 1622 556 1621 558 1646 532 1619 559 1646 533 1617 562 1642 537 557 532 560 539 554 535 557 532 561 538 556 533 558 531 1620 559 560 539 1638 530 563 536 556 533 560 539 555 534 1642 537 556 533 1619 559 559 530 1622 557 1621 558 1620 558 1647 532 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4402 4346 561 1615 564 555 534 1644 535 1643 536 531 558 561 538 1639 539 555 534 531 558 1645 533 560 529 563 536 1641 537 1640 538 528 561 1645 533 531 558 561 538 1639 539 1638 530 1622 557 1647 532 1646 564 1590 557 1619 559 1618 561 558 531 562 537 555 534 559 530 563 536 558 531 1645 533 559 530 1648 530 1647 531 1646 532 1646 532 560 539 556 533 558 531 1621 557 561 538 555 534 558 531 562 537 1641 537 1639 539 5183 4405 4339 558 1645 533 559 530 1648 530 1647 532 561 538 555 534 1644 535 560 539 551 538 1614 564 555 534 559 530 1647 532 1620 558 561 538 1615 563 553 536 531 558 1620 558 1644 534 1643 536 1642 537 1641 537 1642 536 1613 565 1638 530 563 536 557 532 534 565 554 535 557 532 563 536 1613 565 554 535 1642 537 1641 537 1640 538 1640 538 554 535 560 539 551 538 1614 564 528 561 558 531 562 537 555 534 1644 534 1645 533 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4404 4354 564 1614 564 528 561 1617 562 1642 537 530 559 534 565 1613 555 539 560 556 533 1619 559 534 555 563 536 1642 537 1641 537 555 534 1646 533 532 557 562 537 1640 538 1613 565 1613 555 1648 531 1647 532 1622 557 1619 559 1618 560 559 530 563 536 556 533 560 539 554 535 559 530 561 538 1639 539 554 535 1616 563 1615 564 1615 563 555 534 561 538 1612 556 563 536 1641 538 529 560 533 556 563 537 1641 538 1614 564 5183 4404 4342 555 1622 557 562 537 1641 537 1640 538 529 560 533 556 1648 531 538 562 555 534 1618 560 559 530 562 537 1615 563 1640 538 529 560 1646 532 532 557 562 537 1640 538 1639 539 1639 539 1638 530 1648 530 1649 540 1611 557 1646 532 561 538 554 535 558 531 562 537 555 534 561 538 552 537 1641 537 555 534 1644 534 1643 536 1617 562 557 532 563 536 1639 539 553 536 1642 537 556 533 560 539 554 535 1642 537 1643 535 +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4403 4333 563 1615 563 528 561 1618 560 1618 560 531 558 561 538 1614 564 556 533 558 541 1611 557 562 537 555 534 1619 559 1618 560 558 541 1613 565 551 538 1614 564 1614 564 1614 564 1613 565 528 561 1617 561 1619 559 1617 561 557 532 535 564 528 561 533 566 1611 557 562 537 558 531 1619 559 1619 559 1619 559 559 540 553 536 557 532 561 538 557 532 559 540 553 536 557 532 1620 558 1620 558 1620 558 1620 558 1618 560 5188 4398 4346 561 1618 560 558 531 1621 557 1621 557 561 538 555 534 1619 559 561 538 553 536 1616 562 556 533 560 539 1614 564 1613 565 554 535 1619 559 557 532 1621 557 1620 558 1620 558 1620 558 560 539 1613 565 1615 563 1613 565 553 536 557 532 535 564 554 535 1618 560 558 531 564 535 1615 563 1615 563 1614 564 555 534 559 540 552 537 530 559 536 563 528 561 532 557 562 537 1615 563 1615 563 1614 564 1614 564 1616 562 +# +# Model: Sharp AH-X9VEW. Doesn't have heat function. +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 307 125509 3831 1867 489 482 491 1392 461 468 485 1397 456 489 464 1411 463 466 487 1397 456 488 465 1390 463 482 481 1402 462 1402 483 471 461 1412 462 468 485 1400 464 1401 484 1397 488 1395 458 471 461 485 457 1408 487 1393 460 485 457 473 459 486 456 474 489 1394 459 471 461 485 457 473 459 488 465 466 455 490 463 467 465 480 462 468 464 482 460 470 483 1399 465 464 457 488 465 465 488 1393 460 484 458 471 461 485 457 1415 459 1405 511 408 482 489 464 466 487 1377 456 489 464 481 461 469 463 482 460 470 462 483 459 470 462 484 458 472 460 485 457 473 459 487 455 474 489 1395 458 471 461 485 457 472 460 486 456 473 459 486 456 474 458 487 455 474 458 488 465 466 487 1396 457 487 434 496 457 488 433 496 457 489 432 497 466 479 432 498 465 480 431 499 464 482 439 490 463 482 460 1394 491 1389 485 1395 490 1392 461 468 464 482 460 469 484 1399 465 1399 465 480 462 483 491 77962 300 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 345 2685 478 1170 176 29680 3832 1891 465 481 482 1400 464 465 488 1395 458 470 483 1396 457 487 466 1406 458 471 492 1389 464 481 461 1411 463 1408 456 489 464 1407 457 473 490 1392 482 1383 481 1399 486 1396 458 462 459 497 456 1409 486 1394 459 459 483 473 459 487 455 474 489 1392 461 468 464 481 461 469 463 483 459 471 482 1398 487 1379 464 481 461 485 457 1416 458 487 466 1407 457 472 460 460 482 474 489 1393 460 469 463 483 459 471 461 485 457 1408 456 488 465 481 461 469 484 1381 462 483 459 486 456 473 459 487 455 474 458 487 455 475 457 489 464 467 465 481 461 1410 485 1380 484 1397 488 1392 461 483 459 471 461 485 457 473 459 486 456 474 458 488 454 476 456 490 463 467 465 481 461 1411 464 481 440 491 462 484 437 492 461 485 436 494 459 487 434 495 458 488 433 497 456 489 432 498 486 450 482 1408 467 1389 485 1394 480 1384 459 486 456 489 464 466 487 1393 460 485 457 1415 459 1406 510 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 181 11813 3819 1899 457 488 486 1421 433 472 491 1416 438 481 461 1417 437 482 492 1416 437 467 486 1419 435 485 457 1439 435 1403 461 483 480 1408 435 484 490 1416 438 1401 484 1395 490 1392 461 467 465 480 462 1427 458 1396 457 486 456 473 459 486 456 472 481 1426 438 466 466 480 462 467 465 480 462 467 465 479 463 1426 438 481 461 484 458 1438 436 1409 486 1378 465 480 462 483 459 471 482 1425 428 475 457 487 455 474 458 487 455 1434 430 489 464 481 461 468 485 1404 439 480 462 483 459 470 462 483 459 469 463 482 460 470 462 483 459 470 462 483 459 1437 458 1381 483 1395 490 1390 463 481 461 469 463 482 460 468 464 481 461 468 464 481 461 469 463 482 460 469 463 481 461 1434 430 474 458 486 456 473 459 486 456 473 459 486 456 473 459 486 456 473 459 485 457 472 460 485 489 449 462 1434 461 1378 486 1393 481 1397 519 400 479 476 456 489 464 1431 433 486 435 495 458 487 487 +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3826 1866 490 481 482 1382 461 484 490 1392 461 467 486 1396 457 471 482 1400 464 479 463 1390 463 481 482 1399 465 1398 518 401 510 1379 464 480 483 1398 456 1408 487 1392 482 1399 517 386 483 487 466 1399 486 1392 461 483 459 470 462 482 460 469 484 1397 456 473 459 459 483 472 460 484 458 471 461 483 459 1411 463 480 462 467 486 1377 487 1392 482 1396 457 486 456 472 460 486 456 473 480 1400 464 465 456 488 454 475 488 1393 460 468 464 480 462 467 486 1395 458 470 462 483 459 485 436 493 460 469 463 481 461 486 435 500 463 463 458 486 456 1397 488 1390 484 1394 480 1400 464 465 456 488 465 464 457 487 455 474 458 487 455 473 459 485 457 472 460 485 457 472 481 1399 517 401 458 497 456 473 459 485 457 487 434 495 458 471 461 484 458 488 433 502 430 507 435 500 432 498 455 1409 486 1392 482 1399 454 1409 455 488 465 480 462 467 465 480 462 1408 456 473 459 486 488 +# +# Model: Electrolux ESV09CRO-B21. Doesn't have heat function. +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3092 3057 3092 4438 579 1675 545 534 576 1650 571 535 575 531 569 1656 575 1652 579 527 573 533 577 1648 572 1655 576 1651 580 526 573 532 578 1647 573 532 578 1647 573 1654 577 529 571 535 575 530 570 535 575 529 571 534 576 529 571 534 576 528 571 534 576 528 572 533 577 528 572 533 577 527 573 1651 580 526 573 532 578 527 572 532 568 537 573 531 568 536 574 1650 571 535 575 531 569 536 574 530 569 535 575 529 571 534 576 529 571 533 577 528 572 533 577 527 572 532 568 536 574 531 569 1655 576 530 570 535 575 530 570 535 575 529 571 534 576 528 572 533 577 527 573 532 578 527 572 531 569 536 574 531 569 535 575 530 570 534 576 529 571 534 576 528 571 533 577 527 573 532 567 537 573 531 569 536 574 530 570 535 575 529 571 534 576 528 571 533 577 527 572 532 578 526 573 531 569 536 574 530 570 535 575 529 571 534 576 528 572 533 577 1646 574 532 578 1646 574 1652 579 527 572 533 577 1647 573 1653 578 1675 545 534 576 1649 571 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 315 101050 3094 3056 3093 4437 580 1648 572 534 576 1649 582 525 574 530 580 1646 574 1653 578 529 570 534 576 529 571 534 576 529 570 1655 576 1651 580 527 572 532 578 1647 573 1654 577 1651 580 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 525 574 531 579 525 574 531 579 1646 574 532 578 526 573 531 579 526 573 531 579 526 573 1652 579 527 572 1653 578 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 572 532 578 527 572 532 578 527 572 532 578 526 573 1652 579 527 572 532 578 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 526 573 531 579 525 574 530 580 525 574 530 580 525 574 530 580 524 575 529 581 524 575 529 571 534 576 528 571 533 577 528 571 533 577 528 571 533 577 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 525 574 531 579 525 574 530 580 525 574 1650 581 525 574 1651 580 1647 573 533 577 527 572 1653 578 528 572 1654 577 1650 581 1646 574 71637 254 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 284 19161 3098 3053 3096 4435 572 1656 575 532 578 1648 572 534 576 530 570 1682 549 1652 579 527 572 534 576 1649 571 1656 575 1652 579 1649 571 1656 575 531 579 527 572 1653 578 1649 571 1656 575 531 579 527 572 532 578 527 572 533 577 527 572 533 577 527 573 532 578 527 572 532 578 527 573 532 578 527 572 1652 579 527 572 533 577 528 571 533 577 528 571 533 577 1648 572 533 577 1649 571 535 575 530 569 536 574 531 569 536 574 530 569 536 574 530 570 535 575 530 570 535 575 530 569 535 575 530 569 535 575 1649 571 535 575 531 568 536 574 531 568 536 574 531 568 536 574 531 569 536 574 530 569 536 574 530 569 535 575 530 569 535 575 530 569 535 575 530 570 535 575 529 570 534 576 529 570 534 576 529 570 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 571 533 577 528 572 1652 579 527 572 1653 578 529 570 534 576 529 570 535 575 529 570 1654 577 1677 554 1673 547 +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3093 3058 3090 4441 576 1652 579 528 571 1654 577 531 579 526 573 1652 579 1649 582 525 574 1652 579 528 571 1654 577 1651 580 527 572 533 577 528 571 533 577 1649 582 1646 574 1653 578 529 581 525 574 530 580 525 574 530 580 525 574 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 532 578 1647 573 533 577 1648 572 535 575 530 569 535 575 530 580 525 574 531 579 525 574 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 1651 580 527 572 533 577 528 571 533 577 528 571 533 577 528 571 533 577 528 571 534 576 528 571 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 529 570 534 576 1649 582 525 574 1650 581 1647 573 1654 577 1651 580 1647 573 1654 577 531 579 1646 574 1653 578 +# +# Model: Daikin FTE35KV1. Doesn't have heat function. +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 5045 2158 335 1768 358 690 357 723 335 716 331 1771 355 694 364 686 361 720 327 723 335 1767 359 690 357 1775 362 1770 356 692 366 1767 359 1772 354 1777 360 1771 355 1776 361 687 360 690 357 1776 361 687 360 690 357 693 365 716 331 719 328 692 366 1767 359 1772 354 1777 360 1771 355 1776 361 687 360 1773 364 1767 360 689 358 692 366 685 362 688 359 721 326 724 334 717 330 720 327 723 335 715 332 718 329 721 326 1777 360 1771 355 1776 361 1770 356 692 366 715 332 718 329 721 326 29460 5042 2161 332 1770 356 692 366 685 362 688 359 1774 363 685 362 688 359 721 326 694 364 1769 357 691 367 1767 360 1771 355 693 365 1769 358 1773 364 1768 359 1772 365 1766 361 688 359 691 367 1767 360 689 358 692 366 684 363 717 330 720 327 693 365 686 361 689 358 722 336 684 363 1770 357 1774 363 686 361 689 358 1774 363 686 361 1771 356 1776 361 1770 357 692 366 685 362 688 359 690 357 1776 361 687 360 690 357 1776 361 688 359 691 356 694 364 716 331 689 358 1775 362 686 361 689 358 692 366 685 362 688 359 691 356 724 334 716 331 689 358 722 336 685 362 688 359 721 326 693 365 716 331 689 358 692 366 684 363 718 329 690 357 693 365 716 331 689 358 722 336 1767 360 689 358 1774 363 686 361 1771 356 693 365 686 361 689 358 722 336 684 363 717 330 720 327 1776 361 687 360 690 357 693 365 716 331 1771 355 693 365 686 361 1772 355 1776 361 688 359 1773 364 1768 359 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 5038 2165 328 1772 365 686 361 689 358 692 366 1765 362 719 328 692 366 684 363 717 330 1771 366 684 363 1768 359 1774 363 686 361 1771 355 1776 361 1770 357 1775 362 1769 358 691 356 694 364 1767 360 691 356 694 364 686 361 689 358 692 366 685 362 1769 358 1775 362 1769 358 1774 363 1768 359 690 357 1776 361 1770 357 692 366 684 363 687 360 690 357 693 365 686 361 689 358 692 366 684 363 687 360 690 357 693 365 1766 361 1773 364 1767 360 1772 355 694 364 686 361 689 358 692 366 25151 319 3980 5041 2131 362 1769 358 693 365 686 361 689 358 1772 365 686 361 689 358 692 366 684 363 1768 359 692 366 1765 361 1772 354 694 364 1769 358 1774 363 1768 359 1773 364 1767 359 719 328 692 366 1796 331 719 328 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1768 359 1775 362 686 361 689 358 1773 364 686 361 1770 357 1777 360 1771 355 693 365 686 361 689 358 1772 365 1769 358 690 357 694 364 1767 360 691 356 694 364 686 361 689 358 723 335 1766 361 690 357 693 365 685 362 688 359 691 356 694 364 687 360 690 357 693 365 685 362 688 359 691 356 694 364 687 360 690 357 693 365 685 362 688 359 691 367 684 363 687 360 1771 366 684 363 687 360 690 357 693 365 1767 360 690 357 1804 333 687 360 690 357 693 365 686 361 689 358 692 366 685 362 1768 359 692 366 685 362 688 359 690 357 1774 363 688 359 691 356 1774 363 1770 356 1775 362 1769 358 691 356 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 301 132136 5036 2167 337 1766 361 689 358 692 366 684 363 1770 357 692 366 684 363 718 329 690 357 1776 361 687 360 1773 364 1767 360 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 718 329 1773 364 684 363 718 329 691 356 694 364 716 331 719 328 1775 362 1769 358 1774 363 1768 359 1772 365 714 333 1770 357 1774 363 716 331 719 328 722 336 715 332 718 329 721 326 724 334 716 331 719 328 722 336 715 332 718 329 1773 364 1767 360 1772 354 1777 360 719 328 721 326 725 333 717 330 29455 5036 2139 354 1777 360 688 359 691 367 714 333 1770 356 692 366 684 363 687 360 690 357 1776 361 688 359 1773 364 1768 359 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 687 360 1773 364 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 1777 360 1771 355 693 365 685 362 1771 355 693 365 1768 359 1773 364 1767 360 689 358 692 366 685 362 1771 355 1775 362 687 360 690 357 1775 362 687 360 690 357 693 365 716 331 689 358 1774 363 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1771 355 693 365 1768 358 1773 364 684 363 687 360 690 357 693 365 1768 359 690 357 1776 361 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1770 356 693 365 685 362 688 359 691 356 1777 360 1771 355 693 365 686 361 689 358 692 366 685 362 1770 356 +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 5043 2132 361 1770 356 723 335 715 332 718 329 1774 363 715 332 719 328 722 336 714 333 1770 356 722 336 1767 360 1772 354 724 334 1769 357 1774 363 1768 358 1773 364 1767 359 720 327 723 335 1768 359 720 327 723 335 716 331 719 328 722 336 714 333 1770 356 1774 363 1769 357 1773 364 1767 360 720 327 1775 362 1769 357 721 326 725 333 717 330 720 327 723 335 716 331 719 328 722 336 714 333 717 330 720 327 723 335 1768 359 1773 364 1767 360 1772 354 724 334 717 330 720 327 723 335 29451 5041 2134 359 1772 354 724 334 717 330 720 327 1775 362 717 330 720 327 723 335 715 332 1771 355 723 335 1768 358 1773 364 715 332 1770 357 1775 362 1769 357 1774 363 1768 359 720 327 723 335 1768 359 720 327 724 334 716 331 719 328 722 336 715 332 718 329 720 327 723 335 716 331 1771 355 1776 361 718 329 721 326 1776 361 718 329 1773 364 1767 360 720 327 723 335 715 332 718 329 1774 363 1768 359 720 327 723 335 1768 358 721 326 724 334 716 331 719 328 722 336 1767 360 719 328 722 336 715 332 718 329 721 326 724 334 717 330 720 327 723 335 715 332 719 328 722 325 725 333 717 330 720 327 723 335 716 331 719 328 1774 363 716 331 1771 355 1776 361 718 329 721 326 724 334 717 330 1772 365 714 333 1770 356 722 336 715 332 718 329 721 326 724 334 717 330 719 328 1775 362 717 330 720 327 723 335 715 332 718 329 1774 363 715 332 718 329 721 326 725 333 717 330 1772 365 +# +# Model: Dyson Purifier Hot+Cool +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2309 665 781 672 803 672 803 695 832 643 833 1355 804 694 836 640 781 695 804 1409 778 697 777 702 797 678 798 677 799 678 799 701 803 674 802 1412 801 674 801 674 801 674 802 674 801 51317 2284 670 775 1413 802 51252 2283 670 801 1412 775 51275 2258 673 798 1414 802 51248 2284 670 802 1412 774 51246 2259 695 775 1413 801 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2316 610 806 671 781 695 806 695 781 695 782 1405 782 694 808 694 780 693 808 1381 779 697 802 1412 776 1438 800 1437 776 1438 775 700 775 1412 776 700 801 701 775 700 776 1438 776 700 776 51695 2258 695 776 1437 776 51248 2258 672 798 1439 776 51240 2258 670 801 1436 776 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2342 612 781 695 805 668 810 666 810 665 811 1402 811 666 811 692 781 670 781 1432 781 696 778 1436 802 1412 802 1412 802 1413 801 1412 802 1412 801 1412 777 1411 776 1463 776 1412 800 1414 801 51041 2257 697 802 1411 777 51240 2283 671 776 1437 801 51209 2255 672 799 1412 801 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2317 637 832 644 830 669 805 670 805 672 803 1411 803 673 802 674 802 673 803 1411 803 674 801 1411 802 675 775 1415 800 701 774 1440 801 1412 775 702 799 1414 774 1413 801 701 801 675 800 51681 2257 695 803 1411 801 51226 2283 671 799 1412 803 51246 2257 696 803 1411 775 51255 2282 668 803 1410 802 51243 2258 695 802 1387 798 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2315 618 853 643 832 644 834 641 833 643 833 1356 805 695 835 640 808 667 809 1404 808 668 806 1409 803 674 801 1412 802 1388 799 677 799 701 775 701 801 1389 799 677 799 676 800 1439 802 51426 2283 671 800 1412 802 51251 2258 697 801 1387 800 51248 2283 669 802 1411 802 51230 2258 696 799 1387 801 51225 2283 670 801 1411 801 51200 2280 695 775 1411 802 51227 2258 696 802 1411 775 51204 2281 669 801 1411 800 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2320 634 837 637 838 637 838 640 835 642 832 1378 836 645 826 670 809 667 808 1406 806 672 803 674 802 1412 802 1412 800 676 801 675 802 1412 802 674 802 1413 801 1412 801 1413 802 1412 802 50937 2285 671 801 1411 802 51225 2280 696 775 1412 801 51212 2283 671 775 1412 802 +# +# Model: Daikin FTXM20M. +name: Off +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 +# +name: Dh +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 +# +name: Cool_hi +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 +# +name: Cool_lo +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 +# +name: Heat_hi +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 +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 +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 +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 +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 +# +name: Cool_lo +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 +# +name: Heat_hi +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 +# +name: Heat_lo +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 +# +# Model: Airwell Prime DCI Series +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3078 3852 2004 888 1054 1824 1045 894 1051 865 2062 861 1078 860 1080 865 1046 894 1015 1883 1945 899 1013 901 1013 901 1012 902 1012 900 1042 927 986 902 1012 927 987 901 1012 902 1012 927 986 903 1011 902 1012 931 1011 931 1011 904 1010 933 1009 928 985 928 986 1885 1944 927 3017 3943 1943 927 985 1915 984 929 985 929 1943 957 984 929 985 928 985 929 985 1886 1943 899 984 930 983 930 984 957 986 929 985 929 985 930 984 930 983 930 984 930 1013 930 984 959 983 931 982 931 983 930 984 930 984 930 984 960 1011 931 984 1918 1939 929 3016 3917 1940 930 982 1916 954 959 954 959 1913 957 955 933 981 959 928 1015 954 1916 1913 959 953 960 957 986 927 1015 928 1015 953 961 927 987 927 986 927 987 927 986 927 1016 927 987 927 1016 926 1044 928 987 926 1015 928 988 926 987 926 988 926 1946 1883 987 3974 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3060 3870 1026 888 1984 886 1026 888 1053 888 1026 887 1055 858 1055 858 1052 860 1024 891 1044 1854 1016 897 1975 1853 1975 925 1015 898 1015 898 1016 897 1016 898 1015 898 1016 898 1015 898 1015 898 1015 899 1015 899 1014 899 1014 899 1016 927 1014 900 1014 899 1014 1856 1974 926 3048 3883 1015 898 1975 897 1014 899 1015 899 1014 900 1014 899 1015 900 1013 900 1014 899 1014 1857 1014 900 1973 1855 1973 899 1012 901 1013 902 1011 926 987 927 987 926 987 927 987 955 987 926 988 926 987 927 1015 927 987 927 987 926 988 926 1016 927 1015 1884 1945 925 3020 3911 986 928 1946 925 986 927 986 928 986 928 986 927 987 927 987 928 986 928 986 1884 987 956 1946 1882 1946 925 986 928 986 927 986 928 985 928 986 928 986 928 986 928 985 928 985 929 959 954 984 930 984 987 931 955 959 956 958 955 959 1968 1891 980 3982 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3054 3879 1977 892 1020 1846 1083 838 1073 840 2031 859 1051 864 1047 864 1077 896 1014 927 986 928 986 928 986 1885 1943 927 985 929 1013 928 986 929 985 928 985 928 985 928 985 929 985 929 985 957 985 928 1015 928 1015 928 985 929 984 929 985 929 985 1886 1943 927 3017 3914 1943 928 984 1886 985 958 984 929 1972 957 984 958 984 929 984 930 984 929 984 930 984 930 983 1887 1942 929 983 930 984 930 984 930 983 959 984 930 984 930 983 930 984 930 984 930 984 930 983 931 983 931 983 959 984 931 983 931 983 1888 1941 930 3014 3943 1914 931 981 1915 955 959 955 959 1913 958 955 959 954 959 955 959 955 959 955 988 955 960 953 1917 1941 959 953 960 953 961 953 961 953 961 927 987 927 987 927 987 926 987 927 1017 926 988 926 988 925 988 925 988 926 988 925 1018 925 1972 1857 1014 3946 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3080 3850 2007 863 1049 1851 1048 888 1054 887 2012 886 1026 887 1025 888 1050 865 1045 1854 2029 871 1041 872 1040 873 1042 899 1043 899 1043 872 1015 898 1016 899 1041 872 1042 872 1015 926 1017 898 1016 927 1016 926 1016 899 1014 898 1015 926 1016 899 1015 898 1015 1856 1973 897 3048 3910 1947 898 1015 1884 1015 925 988 899 2003 953 988 900 1014 954 988 926 987 1857 1972 896 988 899 1014 900 1014 900 1013 927 987 929 1014 929 1013 926 987 927 986 927 987 927 987 927 987 927 986 927 987 927 986 985 958 928 986 927 986 927 1012 1860 1943 927 3018 3914 1943 955 986 1914 986 929 984 928 1944 955 957 930 984 957 985 956 958 1913 1915 956 957 957 956 957 931 1012 957 930 983 958 955 931 982 958 956 960 983 958 956 958 955 958 930 984 930 984 930 984 930 984 930 1013 930 984 930 985 928 1942 1887 983 3977 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3083 3873 2012 1843 2013 1873 1054 888 2011 887 1025 887 1025 887 1052 890 1048 1852 1017 896 1018 895 1018 896 1976 895 1017 898 1016 898 1015 898 1016 897 1016 897 1016 897 1017 897 1016 897 1017 900 1043 899 1014 897 1016 897 1016 898 1016 898 1015 898 1015 1857 1972 896 3048 3911 1947 1853 1975 1854 1015 898 1973 897 1016 927 1015 926 987 926 1016 1857 1014 926 987 899 1015 901 1970 926 987 927 987 926 988 955 988 1013 958 900 1014 926 987 900 1014 900 1014 900 1013 927 986 927 987 927 1016 955 987 927 987 955 987 1884 1946 928 3045 3912 1974 1883 1974 1883 987 927 1974 926 986 927 987 928 987 956 987 1942 986 956 986 928 986 928 1944 926 986 928 985 929 984 929 985 928 986 956 987 928 986 958 984 929 984 930 984 930 983 929 985 958 984 930 984 957 957 956 957 1887 1971 956 4003 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3108 3851 2062 1793 2006 1821 1103 839 2031 859 1085 829 1081 833 1079 836 1045 1911 1973 897 1015 898 1016 899 1041 871 1016 899 1014 898 1015 899 1015 899 1014 899 1041 872 1015 899 1041 872 1041 872 1015 899 1015 899 1041 873 1014 899 1041 873 1014 899 1014 1883 1975 900 3045 3886 1997 1856 1945 1857 1012 927 1945 900 1013 901 1012 901 1013 901 1012 1859 1999 901 1012 930 1012 903 1011 903 1010 903 1011 902 1012 960 1011 928 986 932 1010 903 1011 928 1015 928 985 929 985 928 1014 928 985 929 985 929 984 929 985 928 986 1915 1971 928 3017 3915 1942 1885 1943 1885 985 930 1971 929 984 930 983 930 984 930 984 1887 1942 929 983 960 983 931 982 931 983 932 981 958 985 958 956 958 984 959 954 931 983 932 981 959 955 932 982 959 954 960 982 961 983 933 955 988 955 985 929 1943 1915 958 4003 +# +# Model: Danby DAC060EB7WDB +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4402 4442 527 1629 529 549 529 1628 530 550 529 548 531 549 530 550 529 1630 528 549 530 549 529 549 530 1630 527 1629 529 1630 528 548 530 550 529 547 531 1628 530 1629 529 1628 530 1628 529 1628 530 1628 530 549 530 1628 530 1628 530 1627 531 1628 530 1627 531 1631 527 1629 529 1628 530 1627 531 1628 530 1628 530 1629 529 1627 531 1627 531 1628 529 1627 531 1627 531 1629 529 1627 530 549 529 549 530 549 530 1628 529 1627 530 5234 4401 4440 530 550 528 1628 529 549 530 1628 529 1629 528 1626 531 1628 529 550 529 1628 530 1627 531 1629 529 549 529 548 530 549 530 1628 529 1628 530 1627 530 547 532 547 532 547 531 548 531 548 530 548 531 1626 531 549 530 547 531 547 531 548 530 548 530 549 530 548 531 546 532 578 500 547 532 548 531 548 531 548 530 548 531 548 530 548 531 547 531 547 532 547 531 1627 531 1627 530 1626 532 547 531 547 532 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4454 4387 586 1573 585 494 584 1571 586 494 584 493 585 524 554 493 531 1626 586 1573 585 494 585 492 586 492 586 494 585 493 585 493 586 1571 586 493 585 1572 531 1627 530 549 584 494 586 1571 587 1572 529 1628 529 1628 530 1627 530 1626 532 1627 531 1626 532 1628 529 1627 531 1626 531 1627 531 1627 587 1569 532 1625 533 1626 532 1626 532 1627 531 1627 530 548 531 1626 532 1627 531 548 531 1627 531 548 531 546 532 547 531 5233 4401 4443 530 548 530 1627 530 547 531 1627 531 1626 531 1626 531 1627 530 549 530 548 530 1627 531 1627 531 1627 530 1627 531 1627 531 1626 531 548 530 1627 530 547 532 547 532 1627 531 1627 532 546 531 547 531 548 530 548 530 548 531 548 531 548 530 548 530 549 530 548 531 548 531 548 530 547 532 549 529 548 530 548 531 547 532 548 530 548 531 1627 530 548 530 548 531 1626 533 546 531 1627 530 1627 532 1626 530 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4492 4354 619 1537 619 459 621 1537 622 455 623 456 623 457 622 456 623 1535 622 1536 622 458 621 456 623 1536 621 1535 623 456 622 456 623 457 621 456 531 1627 621 1536 622 456 622 457 622 455 623 458 621 458 621 1537 621 1536 620 1539 618 1538 531 1628 531 1627 530 1628 530 1628 530 1627 531 1628 530 1628 530 1628 531 1627 530 1629 529 1628 529 1628 530 549 530 1627 531 1628 530 1628 530 1627 586 494 530 1627 530 549 530 5232 4400 4443 586 492 587 1571 587 493 585 1572 530 1628 586 1572 586 1572 586 492 587 493 586 1572 586 1571 586 492 587 493 585 1572 531 1627 585 1573 585 1572 585 492 586 494 585 1572 586 1571 531 1627 531 1628 530 1627 587 491 531 548 587 492 530 548 530 547 532 548 531 547 532 547 531 548 531 547 532 547 531 548 588 491 530 547 589 490 531 547 532 1626 532 548 531 548 531 547 532 547 532 1627 531 548 531 1626 532 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4401 4441 528 1629 529 550 528 1628 529 551 528 550 528 551 527 551 528 1629 529 1629 529 550 529 1630 528 551 528 549 530 550 529 551 528 549 529 550 529 1629 529 1628 530 549 530 1629 529 550 529 1628 529 1629 529 1631 527 1628 530 1628 529 1629 528 1628 530 1629 529 1629 529 1629 529 1629 528 1629 529 1629 529 1630 528 1629 529 1629 529 1628 529 1629 528 551 528 1629 529 550 529 550 530 548 529 1631 527 551 528 1629 529 5235 4402 4439 530 550 528 1629 529 549 530 1628 529 1629 529 1628 530 1629 529 549 530 550 529 1628 530 552 526 1628 529 1628 530 1628 530 1627 531 1628 529 1629 528 551 528 550 529 1628 530 550 528 1628 529 549 529 550 528 550 529 549 530 548 530 551 528 550 528 578 500 550 529 550 529 551 527 549 530 549 529 549 529 550 528 548 530 550 528 549 529 1629 528 550 529 1630 528 1628 530 1628 530 549 530 1628 529 549 529 +# +# Model: Carrier 42QHB12D8S +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4467 4363 599 1556 599 478 599 1556 599 1558 597 505 572 505 572 1583 571 505 572 506 624 1531 653 423 651 426 624 1530 622 1532 572 505 572 1583 571 506 571 1584 571 1583 571 1584 571 1584 571 507 570 1585 570 1585 570 1585 570 508 569 508 569 508 592 485 570 1585 593 484 570 508 569 1586 569 1586 592 1563 593 485 569 508 592 485 592 485 570 508 569 508 570 508 569 508 569 1586 569 1586 569 1586 569 1586 569 1586 569 5187 4461 4371 568 1586 569 508 593 1562 593 1563 569 509 591 486 591 1563 569 508 592 486 592 1563 592 485 592 486 591 1563 592 1563 591 486 592 1564 591 486 592 1563 593 1563 591 1563 592 1564 592 485 592 1563 592 1563 592 1564 591 486 591 486 592 485 592 485 592 1563 592 486 591 486 592 1564 591 1563 592 1563 592 486 592 485 592 486 591 486 592 485 592 486 591 486 591 486 591 1563 592 1563 593 1563 591 1564 591 1563 591 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4440 4390 571 1583 572 505 572 1583 596 1559 571 505 572 505 572 1583 596 481 596 481 597 1559 625 451 654 423 625 1529 595 1560 596 480 572 1583 572 505 596 482 594 483 571 1583 571 1584 571 1584 571 1584 571 1585 569 1585 593 1562 594 1561 593 485 569 508 593 484 592 485 593 484 592 485 592 1563 569 508 592 1562 592 485 570 1586 592 485 592 486 569 1586 592 485 569 1585 570 508 569 1586 569 508 569 1585 570 1586 569 5186 4438 4393 569 1585 593 485 592 1562 569 1585 569 508 569 508 569 1585 591 486 593 484 591 1563 591 486 591 486 590 1564 569 1585 569 508 593 1562 592 486 592 485 569 508 591 1563 592 1562 569 1586 569 1586 591 1563 592 1563 590 1564 591 1563 592 486 569 508 569 508 569 508 569 508 592 486 592 1563 568 508 593 1563 591 486 592 1563 569 508 592 486 592 1563 591 486 592 1563 592 485 592 1563 592 486 592 1563 591 1563 592 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4465 4365 598 1557 598 479 598 1557 598 1557 598 479 598 479 598 1556 599 479 598 507 593 1562 652 424 624 452 595 1559 595 1560 594 483 571 1584 594 1561 593 484 593 1562 593 1562 593 1562 593 1562 593 1562 593 1563 592 485 592 1562 593 484 593 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 1562 592 1563 592 1562 593 1562 592 1563 592 1563 592 1562 592 1563 592 5165 4439 4394 569 1586 592 485 569 1586 569 1586 569 508 569 508 593 1562 592 485 570 508 592 1563 592 485 594 484 569 1586 569 1586 569 508 569 1586 592 1563 569 508 592 1563 569 1586 592 1563 592 1563 569 1586 569 1585 569 508 569 1586 569 508 569 508 569 508 569 508 592 485 569 508 592 485 569 508 593 485 569 508 569 508 569 508 593 484 570 508 569 1586 593 1562 570 1586 569 1586 592 1563 569 1586 592 1563 592 1563 592 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4465 4364 599 1556 599 479 598 1556 599 1556 599 478 599 479 598 1559 596 505 572 506 571 1584 653 424 624 452 571 1583 572 1583 571 506 571 1583 571 1584 571 506 571 1584 571 1585 570 1585 570 1585 592 1563 592 1562 593 485 592 1563 592 485 593 485 592 485 592 485 592 485 592 485 593 485 592 1563 592 485 592 1563 592 485 592 485 592 485 592 485 592 1563 592 485 592 1563 592 485 592 1563 592 1563 592 1563 592 1563 592 5164 4460 4372 592 1563 592 485 593 1563 592 1563 592 486 591 486 591 1563 592 485 592 485 592 1563 592 485 592 485 592 1563 592 1563 592 485 592 1563 592 1563 592 485 592 1563 592 1563 592 1563 592 1563 592 1563 592 1564 591 486 591 1563 591 485 592 485 592 485 592 485 592 485 592 485 592 485 592 1563 592 485 592 1563 591 486 591 485 592 486 591 485 592 1563 592 485 592 1563 592 485 592 1564 591 1563 592 1563 592 1563 592 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4440 4391 572 1583 572 505 572 1584 571 1583 572 505 572 505 572 1583 596 481 573 505 596 1559 626 451 655 422 625 1529 596 1559 572 505 572 1583 572 1582 572 505 572 1583 572 1583 571 1584 571 1584 571 1585 570 1584 594 484 593 1562 592 485 592 485 592 485 593 485 593 484 593 484 593 1562 593 484 593 1562 593 1563 592 1562 592 1563 592 485 593 484 592 485 593 1562 593 484 593 485 592 485 592 485 592 1562 593 1563 592 5163 4462 4370 592 1563 592 485 592 1563 592 1563 592 485 592 485 592 1563 592 485 592 485 593 1562 593 485 593 485 592 1563 592 1563 592 485 592 1562 593 1563 592 484 593 1563 592 1563 592 1563 592 1563 592 1563 592 1563 592 485 592 1563 592 485 592 485 592 485 592 485 592 485 593 485 592 1563 592 485 592 1563 592 1563 592 1563 592 1563 592 485 592 485 592 485 592 1563 591 485 592 486 591 485 592 485 593 1563 591 1563 593 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4467 4390 571 1583 572 505 595 1560 572 1583 572 505 572 505 596 1559 596 482 596 481 597 1559 626 451 655 422 625 1529 596 1559 596 481 572 1582 573 1583 571 505 572 1583 595 1560 594 1561 592 1562 594 1561 593 1562 593 484 593 1563 592 485 592 485 592 485 592 485 593 484 593 485 592 485 592 1562 593 485 592 1563 592 1562 593 1562 594 483 593 485 593 1562 592 485 593 1561 593 484 593 484 593 484 593 1562 593 1562 592 5163 4462 4370 592 1563 593 484 592 1563 592 1563 592 485 592 485 593 1562 593 484 593 485 592 1562 593 484 593 485 592 1562 593 1562 593 485 592 1563 592 1563 592 485 592 1563 592 1562 593 1562 593 1563 592 1563 592 1562 592 485 593 1562 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 1563 592 485 592 1563 591 1563 592 1563 593 485 592 485 592 1563 592 485 592 1563 591 485 593 485 592 485 592 1563 592 1563 591 +# +# Model: Mitsubishi MSZ-AP25VGK +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3531 1667 500 1225 499 1225 499 376 499 377 498 377 498 1224 500 377 498 377 498 1224 500 1225 499 377 527 1195 557 318 556 318 555 1167 530 1194 529 374 499 1224 499 1225 497 377 497 378 497 1228 496 379 496 380 495 1229 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 496 380 495 380 495 380 495 380 495 9028 3526 1672 495 1229 495 1229 495 380 495 380 495 380 495 1230 494 380 495 380 495 1229 495 1229 495 380 495 1229 495 380 495 380 495 1229 495 1229 495 380 495 1229 495 1229 495 380 495 380 495 1229 495 380 495 380 495 1229 495 380 495 381 494 381 494 380 495 380 495 380 495 380 495 381 494 380 495 381 494 381 494 381 494 381 494 381 494 380 495 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 1230 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 381 494 381 494 381 494 381 494 381 494 1230 494 1230 494 381 494 1230 494 1230 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 381 494 1230 494 381 494 381 494 1230 494 381 494 1230 494 1230 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 1231 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 1231 493 382 493 1231 493 382 493 1231 493 382 493 383 492 382 493 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3561 1666 500 1195 529 1193 531 374 501 375 500 375 500 1196 529 374 501 375 500 1223 501 1223 501 375 529 1194 558 318 557 318 555 1168 530 1193 530 345 529 1194 529 1196 527 348 526 350 525 1200 524 352 522 353 522 1228 496 379 496 379 496 379 496 379 496 379 496 379 497 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 9028 3529 1670 496 1228 496 1228 496 379 496 379 496 379 496 1228 496 379 496 379 496 1228 496 1228 496 379 496 1228 496 379 496 379 496 1228 496 1228 496 379 496 1228 496 1228 496 379 496 379 496 1228 496 379 496 379 496 1228 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 380 495 379 496 379 496 379 496 379 496 380 495 379 496 380 495 379 496 1229 495 380 495 380 495 379 496 380 495 380 495 380 495 1229 495 380 495 380 495 380 495 380 495 380 495 380 495 1229 495 380 495 380 495 380 495 380 495 380 495 1229 495 380 495 380 495 1229 495 1229 495 380 495 380 495 380 495 380 495 380 496 380 495 380 495 380 495 1229 495 380 495 1229 495 380 495 380 495 1229 495 380 495 1229 495 1229 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 381 494 380 495 380 495 380 495 380 495 1230 494 380 495 381 494 381 494 380 495 380 495 380 495 380 495 381 494 381 495 380 495 381 494 381 495 380 495 381 494 381 495 380 495 381 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 381 494 381 494 1230 494 381 494 1230 494 381 494 381 494 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3534 1637 530 1192 533 1195 529 375 500 375 500 375 500 1195 530 375 501 375 500 1224 500 1224 501 376 528 1195 557 319 556 318 555 1168 530 1193 530 345 530 1194 529 1196 527 348 526 350 525 1201 523 353 522 378 497 1228 496 379 497 379 496 379 497 379 496 379 497 379 497 379 497 379 496 379 496 379 496 379 496 379 497 379 497 379 496 379 496 9030 3530 1671 496 1229 496 1229 495 379 496 379 496 379 497 1229 496 379 496 379 497 1229 496 1229 495 380 496 1229 495 379 497 379 496 1229 496 1229 495 379 497 1229 495 1228 497 379 496 380 496 1229 496 379 497 379 496 1229 496 379 496 380 496 380 495 380 496 379 496 380 496 380 495 380 496 380 496 380 496 379 496 380 496 380 495 380 496 380 495 380 496 380 495 380 496 380 495 380 495 1229 496 380 495 380 495 380 495 380 496 380 495 1229 496 1229 496 380 495 380 496 380 495 380 496 380 496 380 495 380 495 380 496 380 496 380 495 380 496 380 495 1229 495 1230 495 380 495 1229 496 1229 496 380 495 381 495 380 495 380 495 380 496 380 496 380 495 380 496 1229 496 380 495 1230 495 380 495 380 495 1230 495 380 495 1230 495 1230 495 380 495 380 496 380 495 380 495 380 495 380 496 380 496 380 495 380 496 380 495 380 495 380 496 380 495 381 495 380 495 380 495 380 495 381 495 380 495 381 495 380 495 381 494 381 495 381 495 380 495 1230 495 381 495 380 495 381 494 381 495 381 494 381 495 381 494 381 495 381 495 381 494 381 495 381 494 381 495 381 495 381 494 381 495 381 494 381 494 381 495 381 494 381 494 381 494 381 495 1230 495 381 495 1230 495 1230 495 381 494 1230 494 381 495 381 494 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3534 1667 500 1224 501 1224 501 376 500 375 500 376 500 1224 501 376 500 376 499 1224 501 1225 500 376 500 1225 556 320 556 318 555 1167 530 1194 530 345 530 1195 529 1196 528 348 527 377 498 1227 498 378 497 379 496 1229 496 379 496 379 497 379 497 379 497 379 497 380 496 379 497 379 496 379 497 380 496 380 496 380 496 380 496 379 497 379 497 9033 3530 1672 496 1229 496 1229 496 380 496 380 496 380 496 1229 496 380 496 380 496 1229 496 1229 496 380 496 1229 496 380 496 380 496 1229 496 1229 496 380 496 1230 495 1229 496 380 495 380 496 1229 496 380 496 380 496 1229 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 495 380 496 380 496 380 496 380 496 380 496 380 496 380 496 1230 495 380 496 380 495 380 496 381 495 380 495 1230 495 1230 495 380 496 380 496 380 496 1230 495 1230 495 1230 495 380 496 380 496 380 496 380 496 380 496 381 495 1230 495 1230 495 381 495 1230 495 1230 495 380 495 381 495 381 495 381 495 381 495 381 495 380 496 381 495 1230 495 381 495 1230 495 381 495 380 496 1230 495 381 495 1230 495 1230 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 380 496 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 1231 494 381 495 381 495 381 495 381 495 381 495 381 494 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 382 494 382 494 1231 494 381 495 1231 494 1231 494 381 495 382 494 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3539 1670 501 1226 502 1226 501 376 501 377 500 376 501 1226 501 376 499 378 500 1226 501 1226 501 377 528 1199 557 320 557 318 529 1197 531 1195 531 346 530 1196 530 1198 528 349 527 352 524 1229 498 379 498 379 498 1230 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 9045 3536 1674 496 1231 497 1231 497 380 497 380 497 380 497 1231 496 380 497 380 497 1231 497 1231 496 380 497 1231 496 380 497 380 497 1231 496 1231 497 380 497 1231 496 1231 496 380 497 380 497 1231 496 381 496 380 497 1231 497 381 496 380 497 380 497 380 497 380 497 381 496 381 496 381 496 380 497 380 497 381 496 381 496 381 496 381 496 380 497 381 496 381 496 381 496 381 496 380 497 1231 496 381 496 381 496 380 497 381 496 381 496 1232 495 381 496 381 496 381 496 381 496 1232 495 1232 495 1232 496 1232 495 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 1232 496 1232 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 1232 496 381 496 1232 495 381 496 1232 495 381 496 1232 495 1232 495 381 496 381 496 382 495 381 496 381 496 381 496 381 496 382 495 381 496 381 496 381 496 381 496 381 496 382 495 381 496 381 496 381 496 381 496 382 495 382 495 382 495 382 495 382 495 382 495 382 495 1232 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 1233 495 1233 494 1233 495 382 495 382 495 1233 495 1233 494 382 495 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3539 1637 533 1225 502 1193 534 375 501 376 501 376 500 1226 501 376 501 376 500 1226 500 1227 501 376 529 1197 558 320 557 319 555 1169 531 1195 531 346 529 1196 530 1198 528 349 526 352 524 1229 497 379 497 379 497 1230 497 380 497 380 497 379 498 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 9042 3535 1674 496 1230 497 1230 497 380 497 380 497 380 497 1230 497 380 497 380 496 1230 497 1230 497 380 497 1230 497 380 497 380 497 1231 496 1230 497 380 497 1231 496 1231 496 380 496 380 497 1231 496 380 497 380 496 1231 496 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 496 380 497 380 497 380 497 380 497 380 496 381 496 380 497 380 497 380 497 381 496 1231 496 381 496 380 497 380 497 381 496 381 496 1231 496 381 496 381 496 380 497 381 495 1231 496 1231 496 1231 496 380 497 381 496 381 496 380 496 381 496 381 496 381 496 381 496 381 496 1231 496 1231 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 1231 496 381 496 381 496 1232 495 381 495 1232 495 381 496 1231 496 1231 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 495 381 496 381 496 381 496 381 496 1232 495 381 496 381 496 381 496 381 496 381 495 381 496 381 495 382 495 381 496 382 495 381 495 382 495 381 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 381 495 1232 495 1232 495 1232 495 1232 495 1232 495 382 495 382 495 382 495 +# +# Model: Hitachi RAK-50PEB +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30683 50966 3411 1600 493 1186 493 347 492 348 491 348 491 349 490 349 490 350 489 351 488 351 488 352 487 352 488 351 488 1192 487 352 487 351 488 352 487 352 487 352 488 352 488 351 488 1192 487 1191 488 352 487 352 487 352 487 352 487 352 487 352 487 352 487 352 487 1192 487 352 487 1192 487 1192 487 1192 487 1192 488 1192 487 1192 488 352 487 1192 487 1192 487 352 487 352 487 352 487 352 488 352 487 352 488 352 487 352 487 1192 487 1192 487 1192 487 1192 487 1192 487 1192 487 1192 487 1192 487 352 487 352 488 352 487 1192 487 352 487 352 487 352 487 352 487 1192 487 352 487 353 486 1192 487 353 486 353 486 353 486 353 486 1193 486 353 487 353 486 353 486 353 486 353 486 353 486 1193 486 1193 486 353 487 353 486 353 486 353 486 353 487 353 486 353 486 353 486 1193 486 353 486 353 486 1193 486 353 487 353 486 353 487 353 486 353 486 353 486 353 486 353 487 353 486 353 486 1193 486 1193 486 353 487 353 486 353 486 353 486 354 485 353 486 354 485 353 486 354 486 353 486 353 486 354 486 353 486 353 487 1193 486 1194 485 353 487 353 486 354 485 354 485 354 486 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 486 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 486 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 355 484 355 484 355 484 354 485 354 485 355 484 355 484 355 484 355 484 354 486 355 484 378 461 356 484 378 461 355 485 355 484 355 484 355 485 355 484 378 461 378 461 355 484 356 483 355 484 378 461 378 462 355 485 378 461 378 462 378 461 379 461 356 483 378 461 1195 484 378 461 379 461 356 484 378 461 379 460 379 461 378 461 378 462 378 461 379 461 378 461 378 461 379 460 379 460 379 461 378 461 378 461 378 461 378 461 378 461 379 460 379 460 379 460 379 461 379 460 1219 460 1219 460 379 461 1219 460 379 461 1219 460 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30684 50965 3412 1599 494 1185 494 346 493 346 493 347 492 348 491 349 490 349 490 350 489 351 489 350 489 350 489 351 489 1191 488 351 488 351 488 351 489 351 488 351 488 351 488 351 488 1191 488 1191 488 351 488 351 488 351 488 351 488 351 488 351 489 351 488 351 488 1191 488 351 489 1191 488 1191 488 1191 488 1191 488 1191 488 1191 488 351 488 1191 488 1192 487 351 488 352 487 351 488 351 488 352 487 352 487 352 487 352 488 1192 487 1192 487 1216 463 1192 487 1192 488 1192 487 1193 486 1192 488 352 487 352 487 352 487 1193 486 376 463 376 463 376 464 352 487 1216 463 376 463 376 463 1216 464 376 463 376 463 376 463 1216 463 376 463 376 463 353 486 353 487 376 463 376 463 376 463 1216 463 376 463 1216 463 376 464 376 463 376 463 376 464 376 463 376 463 376 463 376 463 1216 463 376 463 1216 463 376 463 376 463 376 463 376 463 376 463 376 463 376 463 376 463 376 464 376 463 1216 463 1217 463 376 463 377 462 377 462 377 463 376 463 376 463 377 462 376 463 377 462 377 462 377 463 376 463 377 462 377 462 1217 462 1216 463 377 463 377 462 377 462 377 462 377 463 376 463 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 463 377 462 377 462 1217 462 377 462 377 462 377 463 377 462 377 463 377 462 377 462 377 462 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 1217 462 377 462 377 462 377 462 377 462 378 461 377 462 377 462 378 462 377 462 377 462 377 463 377 462 378 461 378 462 377 462 378 461 377 462 378 461 378 461 378 461 378 462 377 462 378 462 1217 462 1218 461 1218 461 378 461 378 461 1218 462 377 462 378 462 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30747 50897 3484 1554 543 1137 542 310 529 309 530 309 530 309 530 310 529 309 530 310 529 309 530 309 530 309 530 309 530 1138 541 309 531 309 530 309 530 309 530 309 530 309 530 309 530 1138 541 1138 541 310 529 309 530 309 531 309 530 309 530 309 530 309 530 310 529 1139 541 309 530 1138 541 1138 541 1138 541 1138 542 1138 541 1138 541 310 529 1138 541 1138 541 309 530 309 530 309 531 310 529 309 530 309 530 309 530 309 530 1139 541 1139 540 1139 541 1138 541 1139 540 1139 540 1138 541 1139 540 310 529 310 529 309 530 1139 541 310 529 309 530 309 530 309 530 1139 540 309 530 309 530 1139 541 309 530 309 530 309 530 1139 540 309 531 309 530 1139 541 309 530 309 530 309 530 309 530 309 530 309 530 1139 540 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 1139 540 310 529 309 530 309 530 309 530 309 530 309 531 310 529 310 529 309 531 310 529 1140 540 309 530 309 531 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 310 529 1140 539 1140 539 309 530 309 530 309 530 309 530 310 529 309 530 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 1140 540 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 309 530 310 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 530 309 531 309 530 309 530 309 531 309 531 309 530 309 530 309 530 309 530 309 531 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 309 531 309 530 309 530 309 531 309 530 309 530 309 530 309 530 1141 538 309 531 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 309 530 309 530 310 530 309 531 309 530 309 530 309 530 309 530 309 530 309 531 1142 537 309 530 1142 538 309 530 1141 538 309 530 309 530 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30694 50951 3483 1555 542 1137 542 308 531 309 530 308 531 308 531 308 531 308 531 308 531 308 531 308 532 308 531 308 532 1139 541 308 531 308 531 308 531 308 531 308 531 309 531 308 531 1139 541 1139 540 308 531 308 531 308 531 308 531 309 530 308 531 308 531 308 532 1139 541 308 532 1139 540 1139 541 1139 540 1139 540 1139 540 1139 541 309 530 1139 540 1139 540 308 531 308 531 308 531 308 532 308 531 308 531 308 531 308 531 1140 540 1139 541 1139 540 1139 540 1139 540 1139 541 1139 540 1139 540 308 531 308 531 308 531 1140 540 309 530 308 531 308 532 308 531 1140 540 308 531 308 532 1140 539 308 531 308 531 308 531 308 532 308 531 308 531 1140 540 308 531 308 531 308 531 308 531 308 532 308 531 1140 540 308 531 308 531 308 531 308 531 308 531 308 531 1140 540 1140 540 1140 539 308 531 1140 539 308 531 308 531 308 532 308 531 306 533 308 531 306 533 308 531 307 533 308 531 1141 539 308 532 308 531 308 531 306 534 306 533 306 534 306 533 306 533 306 533 306 533 306 534 307 532 307 533 306 533 308 532 1141 539 1141 539 308 531 308 532 308 531 307 533 307 481 352 538 307 533 307 532 307 533 307 481 352 539 307 532 307 533 306 482 352 487 352 537 306 534 307 482 352 538 307 482 352 487 1192 539 309 530 307 531 306 483 352 487 352 538 307 482 352 538 306 483 352 487 352 487 352 487 353 486 352 488 353 486 352 487 352 487 353 487 353 486 353 486 353 487 352 487 353 486 353 486 353 486 353 486 353 487 353 487 353 486 353 486 353 487 353 486 353 486 353 486 353 487 353 486 353 487 352 487 353 486 353 486 353 487 353 486 353 486 353 487 353 486 353 487 353 486 353 487 353 486 1193 537 306 483 353 486 353 487 353 486 353 487 353 486 353 486 353 487 353 486 353 486 353 487 353 486 353 486 353 486 353 486 353 487 353 486 353 486 353 486 353 486 353 486 353 486 353 486 1194 485 353 487 1193 538 1142 486 1193 486 353 486 353 486 353 568 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30703 50953 3432 1606 490 1189 490 349 490 349 490 349 490 350 489 350 489 350 489 350 490 350 489 350 489 350 490 350 489 1190 490 350 489 350 489 350 489 350 489 350 490 350 489 350 490 1190 490 1190 489 350 489 350 489 350 489 350 490 350 490 350 489 350 490 350 490 1190 489 350 490 1190 489 1190 490 1190 489 1190 490 1190 489 1190 489 350 490 1190 489 1190 489 350 490 350 489 350 490 350 489 350 489 350 490 350 489 350 490 1190 489 1190 489 1190 490 1190 489 1190 490 1190 489 1190 489 1191 489 350 489 350 489 350 490 1190 490 350 489 350 490 350 489 350 490 1190 489 350 490 350 489 1190 490 350 489 350 490 350 490 350 489 350 489 350 489 1191 489 350 489 350 490 350 489 350 489 1191 489 1190 489 350 489 350 490 350 489 350 490 350 489 351 489 350 489 350 489 350 489 350 490 350 489 350 489 1191 489 350 489 351 489 351 488 351 489 351 488 351 489 350 489 351 489 351 489 1191 488 351 488 351 488 351 489 350 489 351 488 350 490 350 489 351 488 351 488 351 489 350 489 350 489 351 489 351 489 350 489 1191 488 1191 489 350 489 351 488 351 489 351 488 351 489 351 488 351 488 351 489 351 489 351 489 350 489 351 489 351 488 351 488 351 488 351 489 351 488 351 488 351 488 351 489 351 489 1191 488 351 489 351 488 351 489 351 488 351 489 351 489 351 488 351 488 351 489 351 489 351 488 351 488 351 488 351 489 351 488 351 488 351 489 351 488 351 488 351 489 351 489 351 488 351 488 351 489 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 488 351 489 351 488 351 489 351 489 351 489 351 488 351 489 351 488 351 488 351 489 351 488 1192 488 351 488 351 488 351 489 351 488 351 488 351 489 351 489 351 489 351 488 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 489 1191 488 1191 488 352 488 351 488 352 488 351 489 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30675 50953 3432 1606 490 1190 489 350 489 350 489 350 489 350 489 351 488 350 489 351 489 351 488 351 489 351 488 351 489 1191 488 351 488 351 489 351 488 351 488 351 488 351 489 351 488 1191 489 1191 489 351 488 351 488 351 488 351 488 351 489 351 489 351 488 351 489 1191 488 351 488 1191 489 1191 488 1191 488 1191 488 1191 488 1191 488 351 489 1191 488 1191 489 351 488 351 489 351 488 351 489 351 488 351 488 351 488 351 488 1191 488 1191 488 1191 488 1191 489 1191 488 1191 488 1191 489 1191 488 351 488 351 489 351 488 1191 488 351 489 351 488 351 488 351 489 1191 488 351 489 351 488 1191 488 351 488 351 488 351 489 351 489 351 488 351 489 1191 488 351 488 351 489 351 488 351 488 1191 488 1191 488 351 488 351 489 351 488 351 489 351 488 351 489 351 489 1191 488 1192 488 1191 488 351 489 1191 489 351 488 351 488 351 489 351 489 351 488 351 488 351 489 351 488 351 488 351 488 1192 488 351 489 351 488 351 488 351 489 351 489 351 488 351 488 351 488 352 487 352 488 351 488 352 488 351 488 351 488 351 488 1192 488 1192 487 352 488 352 487 352 487 352 488 352 487 352 488 351 488 352 488 352 488 352 487 352 488 351 488 351 488 352 488 352 487 352 488 352 487 352 488 352 488 352 488 352 487 1192 487 352 487 352 488 352 488 352 488 352 487 352 487 352 488 352 487 352 487 352 487 352 488 352 488 352 487 352 487 352 488 352 487 352 488 352 487 352 487 352 487 352 487 352 487 352 488 352 487 352 487 352 487 352 488 352 487 352 488 352 487 352 488 352 487 352 487 352 488 352 487 352 488 352 487 352 488 352 488 352 487 352 487 352 487 352 487 352 487 352 488 352 487 352 487 352 487 1193 486 352 487 352 488 352 487 352 487 352 488 352 487 352 488 352 488 352 487 352 488 352 487 353 486 353 487 352 487 352 488 352 488 352 487 352 487 352 487 352 487 353 487 352 488 352 488 352 487 1193 486 1193 486 1193 486 1193 487 353 487 352 487 353 486 +# +# Model: LG PC07SQR +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3169 9836 535 1553 511 551 491 543 491 544 490 1586 490 532 510 530 511 543 491 1579 489 1577 490 543 490 543 491 543 491 544 490 544 489 544 490 543 491 544 490 550 492 544 490 543 491 1586 489 542 492 1583 492 552 490 532 510 537 512 1585 491 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3385 9888 512 1544 512 544 489 544 489 544 490 1586 489 545 489 544 489 545 489 531 511 541 508 533 508 542 507 544 489 546 487 544 490 1587 489 1573 510 543 490 543 491 1578 490 545 489 1574 509 545 488 1587 489 1573 510 1586 490 1579 489 1568 507 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3288 9816 598 1504 574 472 569 463 571 463 571 1515 568 472 569 461 573 471 571 459 590 462 571 463 571 447 594 462 572 462 571 464 570 459 590 463 570 464 570 1496 571 1497 570 463 571 1514 569 464 570 1504 572 1514 569 471 571 473 568 462 572 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3205 9865 616 1473 536 518 516 517 517 517 517 1575 517 527 568 464 515 517 571 463 570 462 572 462 572 469 573 461 573 463 570 462 572 469 573 1504 572 451 590 451 591 472 569 463 571 1494 573 461 573 1497 570 1505 570 1503 572 471 571 1491 592 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3363 9846 596 1495 514 518 515 517 517 515 518 1552 516 502 539 517 517 516 518 516 518 517 517 517 571 462 517 518 516 1554 568 461 589 462 572 1505 571 1507 568 1514 514 1568 570 461 573 1504 572 472 570 1507 592 1490 593 460 574 462 572 447 594 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3204 9889 537 1587 491 529 512 544 489 545 489 1573 510 530 511 543 491 552 490 538 511 543 491 543 491 532 509 543 491 1587 489 537 512 543 491 1577 490 543 491 543 491 544 489 543 491 1586 489 544 490 1587 489 539 510 543 491 543 491 1586 490 +# +# Model: Daikin FTXC35DV1B +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 530 315 532 314 532 314 532 314 532 314 506 340 561 24780 3568 1647 507 1213 535 313 534 340 506 340 506 1212 507 339 507 339 506 339 506 339 506 1214 505 341 504 1215 505 1216 504 343 503 1217 503 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 1219 501 345 501 344 502 345 501 1219 501 1219 501 345 501 344 502 344 502 345 501 344 502 345 501 344 502 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 1219 501 1219 501 345 501 1219 501 345 501 1219 501 1219 501 34807 3563 1652 502 1218 502 344 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1219 501 344 502 1218 502 1219 501 344 502 1219 501 1218 502 1219 501 1219 501 1219 501 345 501 344 502 1219 501 345 501 344 502 345 501 344 502 344 502 345 501 345 501 345 501 344 502 345 501 345 501 1219 501 345 501 345 501 345 501 345 501 1219 501 345 501 345 501 1219 501 345 501 345 501 1219 501 1219 501 345 501 1219 501 1219 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 345 501 345 501 1220 500 346 500 345 501 346 500 346 500 34806 3564 1652 502 1218 502 344 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 1218 502 344 502 1218 502 1218 502 1218 502 1219 501 1218 502 344 502 345 501 1219 501 345 501 344 502 345 501 344 502 344 502 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 345 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 1219 501 345 501 345 501 345 501 346 500 345 501 345 501 345 501 345 501 346 500 346 500 346 500 346 500 346 500 1220 499 346 500 1220 499 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 347 499 346 500 1221 499 1221 499 1221 499 347 499 347 499 347 499 347 499 347 499 347 499 347 499 347 499 347 499 1245 475 1222 498 1245 475 371 475 348 498 348 498 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 372 474 372 474 372 474 371 475 371 475 1246 474 371 475 1246 474 372 474 372 474 372 474 1246 474 1246 474 372 474 371 475 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 1246 474 372 474 1246 474 1246 473 372 474 372 474 1246 474 372 474 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 536 339 507 311 535 312 534 312 534 312 534 339 507 24807 3710 1507 594 1156 563 305 543 304 542 304 570 1126 592 304 515 304 542 304 542 304 542 1155 564 304 541 1158 505 1215 505 342 504 1217 503 1217 503 1218 502 1218 502 1218 502 344 502 343 503 1218 502 343 503 343 503 344 502 344 502 344 502 344 502 344 502 343 503 343 503 344 502 1218 502 344 502 1218 502 344 502 344 502 344 502 1218 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 1218 502 1218 501 344 502 1218 502 344 502 1218 502 1218 502 34800 3564 1651 503 1217 503 343 503 344 502 343 503 1217 503 343 503 343 503 344 502 344 502 1218 502 344 502 1217 503 1218 502 344 502 1218 502 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 1218 502 344 502 344 502 1218 502 1218 502 344 502 1218 502 1219 501 1219 501 345 501 344 502 344 502 344 502 344 502 345 501 345 501 1219 501 345 501 1219 501 345 501 345 501 345 501 345 501 34805 3565 1650 503 1216 503 343 503 343 503 344 502 1217 503 343 503 344 502 343 503 343 503 1217 503 344 502 1218 502 1218 502 344 502 1218 502 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 344 502 1218 502 344 502 1218 501 344 502 344 502 344 502 345 501 344 502 345 501 344 502 345 501 1219 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 345 501 1219 501 345 501 345 501 345 501 345 501 346 500 345 501 345 501 346 500 370 476 346 500 346 500 346 500 370 476 346 500 346 500 346 500 346 500 1221 499 1244 476 1244 476 370 476 346 500 370 476 370 476 370 476 370 476 370 476 370 476 370 476 1244 476 1244 476 1244 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 1244 476 370 476 1245 475 370 476 371 475 370 476 1245 475 1245 475 371 475 371 475 370 476 370 476 370 476 370 476 370 476 371 475 370 476 371 475 371 475 371 475 371 475 370 476 371 475 371 475 371 475 1245 475 1245 475 1245 475 371 475 371 475 1245 475 371 475 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 507 340 559 306 487 341 530 315 531 313 561 305 541 24776 3569 1646 508 1212 537 340 506 340 506 340 506 1213 507 339 507 339 506 339 506 340 505 1214 505 341 504 1215 504 1216 503 343 503 1217 502 1218 502 1218 502 1218 502 1218 501 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 344 502 344 502 344 502 1219 501 1218 502 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 1219 501 1219 501 345 501 1219 501 345 501 1219 501 1219 501 34800 3563 1652 502 1217 502 344 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 1218 501 344 502 1218 502 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 502 345 501 345 501 344 502 345 501 344 502 345 501 345 501 345 501 344 502 345 501 345 501 1219 501 345 501 345 501 345 501 345 501 1219 501 345 501 1219 501 1219 501 345 501 345 501 1219 501 1219 501 345 501 1219 500 1219 500 1219 500 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 345 501 1220 500 346 500 346 500 346 500 346 500 34808 3564 1652 503 1218 502 344 502 344 502 344 502 1218 502 344 502 344 502 345 501 344 502 1218 502 345 501 1218 502 1219 501 344 502 1219 501 1219 501 1219 501 1219 501 1219 501 345 501 345 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 500 345 501 345 501 1219 500 1219 501 1219 501 345 501 345 501 345 501 345 501 1219 501 345 501 345 501 1219 501 346 500 346 500 345 501 345 501 346 500 346 500 346 500 345 501 346 500 346 500 346 500 346 500 346 500 346 500 346 500 1220 500 346 500 1220 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 347 499 347 499 346 500 1221 499 1245 475 1221 499 347 499 347 499 347 499 348 498 347 499 348 498 348 498 371 475 348 498 1222 498 1245 475 1245 475 348 498 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 372 474 372 474 372 474 371 475 1245 474 372 474 1246 474 372 474 372 474 372 474 1246 474 1246 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 1246 474 372 474 372 474 372 474 372 474 1247 473 1247 473 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 533 311 535 312 534 312 534 313 533 310 536 312 534 24804 3705 1509 593 1129 590 305 542 305 542 305 569 1127 590 304 514 305 541 304 542 305 541 1155 564 305 540 1157 561 1159 504 341 504 1217 503 1217 503 1217 503 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 344 502 344 502 344 502 1218 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 345 501 1218 502 1218 502 1218 502 344 502 1219 501 345 501 1219 501 1219 501 34806 3565 1650 503 1217 502 343 503 343 503 343 503 1217 503 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 1218 502 344 502 1218 502 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1218 502 344 502 1219 501 344 502 1218 502 344 502 1219 501 1218 501 345 501 1218 501 1219 501 1218 502 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 1219 501 345 501 345 501 345 501 345 501 34805 3565 1651 503 1217 502 343 503 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 1218 502 344 502 1218 502 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 344 502 1218 502 1219 501 1218 502 345 501 344 502 345 501 1219 501 1219 501 1219 501 345 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 345 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 346 500 346 500 345 501 345 501 346 500 346 500 346 500 346 500 346 500 346 500 1220 500 1221 499 1221 498 346 500 346 500 370 476 346 500 347 499 347 499 370 476 370 476 370 476 1244 476 1244 476 1244 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 371 475 370 476 370 476 370 476 371 475 370 476 370 476 370 476 370 476 1244 476 371 475 1245 475 370 476 371 475 371 475 1245 475 1245 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 476 371 475 1245 475 1245 475 371 475 371 475 1246 474 1245 475 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 535 313 533 311 535 311 535 312 534 312 534 313 533 24805 3711 1506 592 1129 592 305 543 305 540 305 569 1126 592 304 514 304 542 304 541 304 485 1213 507 340 506 1214 506 1215 505 342 504 1217 503 1218 502 1218 502 1218 502 1218 502 344 502 344 502 1218 503 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 1218 502 344 502 344 502 344 502 1218 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 503 344 502 1219 501 1219 501 1219 501 345 501 1219 501 345 501 1219 502 1219 502 34818 3566 1651 503 1217 503 343 503 344 502 344 502 1218 502 344 502 344 502 344 502 344 503 1218 502 344 502 1218 503 1218 502 344 502 1218 502 1218 503 1218 503 1218 502 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 1218 502 344 502 345 501 344 502 344 502 1219 501 345 501 345 502 1219 501 345 501 1219 502 1219 502 1219 502 344 502 1219 501 1219 502 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 1219 501 345 501 345 501 345 501 1219 501 345 501 345 501 345 501 34812 3566 1651 503 1217 503 344 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 1218 503 344 502 1218 502 1218 502 344 502 1218 502 1218 502 1218 502 1219 501 1218 502 344 502 344 502 1218 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 344 502 345 501 344 502 345 501 345 501 1219 502 345 501 345 501 1219 502 345 501 345 501 1219 501 345 501 345 501 1219 501 1219 501 1219 501 345 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 502 345 501 345 501 345 501 346 500 345 501 345 501 345 501 345 501 345 501 1220 500 346 500 1220 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 347 499 346 500 347 499 347 499 370 476 1244 476 1221 499 1244 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 370 476 1245 475 1245 476 1245 475 371 475 370 476 370 476 371 475 371 475 370 476 370 476 371 475 371 476 370 476 370 476 371 475 371 475 371 475 371 475 371 475 1245 475 371 475 1245 475 371 475 371 475 371 475 1245 475 1245 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 476 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 371 475 1245 475 1245 475 1245 475 371 475 1245 475 1246 474 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 535 314 532 314 533 313 533 312 534 313 533 312 508 24840 3569 1647 508 1213 536 342 505 342 504 341 505 1214 506 340 506 340 506 340 505 340 506 1214 506 341 504 1216 504 1217 503 343 503 1218 502 1219 501 1219 502 1219 502 1219 502 345 502 344 502 1219 502 345 501 345 502 345 502 345 501 345 501 345 501 345 501 345 501 345 502 345 501 1219 502 345 501 1219 501 345 502 345 501 345 501 1219 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1220 500 1219 501 1220 500 345 501 1220 500 345 501 1220 501 1220 500 34815 3564 1653 502 1218 502 344 502 345 501 344 502 1219 501 345 501 345 501 345 501 345 501 1219 502 345 501 1219 502 1220 501 345 502 1219 501 1219 502 1219 501 1219 502 1219 502 345 501 345 501 1220 501 345 502 345 501 345 501 345 501 345 501 345 501 345 502 345 501 345 502 345 501 345 501 1220 501 345 501 345 501 345 501 345 501 1220 500 345 501 346 501 1220 500 1220 501 345 501 345 501 346 500 1220 500 1220 500 1220 500 1220 500 346 500 346 500 346 500 345 501 346 500 345 501 1220 500 346 500 1220 500 1220 500 1220 500 346 500 346 500 346 500 34816 3565 1653 502 1219 501 344 502 345 501 345 501 1219 502 345 501 345 502 345 501 345 501 1219 501 345 501 1219 502 1219 501 345 501 1219 502 1219 501 1219 501 1219 501 1219 501 345 501 345 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1220 501 346 501 345 501 1220 501 346 500 346 500 1220 500 346 500 345 501 346 500 1220 500 1220 500 1220 501 1220 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 1220 500 346 500 1220 500 347 499 346 500 346 500 346 500 347 499 347 499 346 500 346 500 347 499 347 499 347 499 347 499 347 499 347 499 347 499 347 499 347 499 1222 498 1222 499 1222 498 347 499 348 498 348 498 347 499 371 475 348 498 348 498 348 498 371 475 1222 498 1246 474 1246 474 372 475 371 475 372 474 372 474 348 498 371 475 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 1246 474 372 474 1246 474 372 474 372 474 372 474 1246 474 1246 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 1246 474 372 474 1247 473 372 474 1246 474 1246 474 1246 474 diff --git a/applications/main/infrared/resources/infrared/assets/audio.ir b/applications/main/infrared/resources/infrared/assets/audio.ir new file mode 100644 index 00000000000..d5a0f86dc36 --- /dev/null +++ b/applications/main/infrared/resources/infrared/assets/audio.ir @@ -0,0 +1,428 @@ +Filetype: IR library file +Version: 1 +# +# Model: NoName Unknown Audio remote +name: Play +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 43 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 15 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 07 00 00 00 +# +name: Next +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 40 00 00 00 +# +name: Prev +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 44 00 00 00 +# +# Model: Western Digital Unknown +name: Power +type: parsed +protocol: NECext +address: 84 79 00 00 +command: 12 ED 00 00 +# +name: Play +type: parsed +protocol: NECext +address: 84 79 00 00 +command: 0A F5 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 84 79 00 00 +command: 05 FA 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 84 79 00 00 +command: 00 FF 00 00 +# +name: Next +type: parsed +protocol: NECext +address: 84 79 00 00 +command: 01 FE 00 00 +# +name: Prev +type: parsed +protocol: NECext +address: 84 79 00 00 +command: 02 FD 00 00 +# +# Model: Yamaha RAV15 +name: Play +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 43 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 15 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 07 00 00 00 +# +name: Next +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 40 00 00 00 +# +name: Prev +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 44 00 00 00 +# +# Model: Yamaha RX-V375 +name: Power +type: parsed +protocol: NEC +address: 7E 00 00 00 +command: 2A 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 7A 00 00 00 +command: 1A 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 7A 00 00 00 +command: 1C 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 7A 00 00 00 +command: 1B 00 00 00 +# +# Model: SVEN HT-415 +name: Power +type: parsed +protocol: NEC +address: 41 00 00 00 +command: 42 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 41 00 00 00 +command: 0B 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 41 00 00 00 +command: 1B 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 41 00 00 00 +command: 56 00 00 00 +# +# Model: HUAYU AKB74475490 +name: Power +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 08 00 00 00 +# +name: Play +type: parsed +protocol: NEC +address: 04 00 00 00 +command: B0 00 00 00 +# +name: Pause +type: parsed +protocol: NEC +address: 04 00 00 00 +command: BA 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 02 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 03 00 00 00 +# +name: Prev +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 8F 00 00 00 +# +name: Next +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 8E 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 09 00 00 00 +# +# Model: Samsung HW-K450 Soundbar +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4637 4376 612 419 584 420 584 420 583 421 582 1427 531 1477 531 472 532 472 557 1452 556 1451 557 1451 557 1452 556 447 557 448 556 449 555 449 555 4453 554 450 554 450 554 451 553 450 554 451 553 451 553 451 553 450 554 1455 553 1454 554 1454 554 451 553 1454 554 1454 554 1455 553 1454 554 451 553 450 554 450 554 1455 553 55439 4554 4458 555 449 555 449 555 450 554 450 554 1455 553 1454 554 451 553 450 554 1454 554 1454 554 1454 554 1455 553 450 554 451 553 451 553 451 553 4453 554 451 553 451 552 451 553 451 553 451 553 451 553 451 553 451 553 1455 553 1455 553 1455 553 451 553 1455 553 1455 553 1455 553 1455 553 451 553 451 552 451 553 1455 553 +# +name: Play +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4636 4380 612 392 612 394 610 394 610 419 583 1399 557 1452 556 473 556 448 556 1428 581 1453 555 1453 555 1453 555 449 555 450 554 451 553 452 552 4457 551 452 552 452 552 452 552 452 552 452 552 1457 551 452 552 1457 551 452 552 452 552 453 551 1457 552 1457 551 452 552 1457 551 452 552 1457 551 1457 551 1457 552 452 552 55450 4551 4461 553 451 553 452 552 452 552 452 552 1456 552 1456 552 452 552 452 552 1456 552 1456 552 1456 552 1456 552 452 552 452 552 453 551 453 551 4456 551 453 551 453 551 453 551 453 551 453 551 1457 551 453 551 1457 552 453 551 454 550 454 550 1457 552 1457 551 454 550 1457 551 454 550 1458 551 1457 551 1458 550 454 550 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4640 4405 583 420 583 421 582 421 583 422 581 1427 531 1478 530 473 531 472 557 1452 557 1452 556 1452 556 1452 556 448 556 448 556 449 555 450 554 4454 554 451 553 451 553 451 553 451 553 1455 554 1455 553 1455 553 451 553 1455 553 1456 553 1456 553 451 553 451 553 451 553 451 554 1455 554 451 553 452 553 451 553 1456 553 55447 4556 4458 555 449 555 450 554 450 554 450 554 1455 553 1455 553 451 553 451 553 1455 553 1455 553 1455 553 1455 553 451 553 451 553 451 553 451 553 4454 553 451 553 450 554 451 553 450 554 1455 553 1455 553 1455 553 451 553 1455 553 1455 553 1455 553 451 553 451 553 451 553 450 554 1455 553 451 553 451 553 451 553 1455 553 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4636 4378 613 393 611 392 612 393 611 393 557 1451 558 1450 610 420 583 421 557 1427 581 1452 556 1452 556 1452 556 448 555 449 555 450 554 450 554 4455 553 451 553 451 553 451 553 451 553 451 553 451 553 452 552 1456 553 1456 552 1456 553 1456 552 451 553 1456 553 1456 552 1456 553 451 553 451 553 452 552 451 553 1456 552 55452 4553 4461 553 450 554 451 553 451 553 451 553 1456 553 1456 552 451 553 451 553 1456 552 1455 553 1455 553 1455 553 451 553 451 553 451 553 451 553 4456 552 451 553 451 553 451 553 451 553 451 553 451 553 451 553 1456 552 1455 553 1455 553 1455 553 451 553 1455 553 1456 552 1456 552 451 553 451 553 451 553 451 553 1456 552 +# +name: Prev +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 255 113623 4638 4378 613 391 612 392 559 446 558 446 558 1477 531 1477 532 472 532 472 532 1476 532 1476 532 1476 532 1477 531 473 555 449 555 449 555 450 554 4455 554 450 554 450 554 450 554 450 554 1455 554 1455 554 450 554 1455 554 450 555 450 554 450 554 1455 554 451 553 451 553 1455 554 450 554 1455 554 1456 553 1455 554 450 554 +# +name: Next +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4557 4430 611 392 610 394 559 445 559 446 558 1451 558 1477 531 448 556 472 532 1476 532 1477 532 1477 531 1477 531 473 556 449 555 449 555 450 554 4454 554 450 554 450 554 450 554 450 555 450 554 450 554 1455 554 1455 554 450 554 450 554 450 554 1455 554 1455 554 1455 553 451 553 450 554 1455 554 1455 554 1455 554 450 554 55458 4555 4459 554 450 554 450 554 450 554 450 554 1455 553 1455 553 450 554 450 554 1455 553 1455 553 1455 553 1455 553 450 554 450 554 450 554 450 554 4454 554 450 554 450 554 450 554 451 553 450 554 450 554 1455 553 1455 553 451 553 450 554 450 554 1455 553 1455 553 1455 553 450 554 451 553 1455 554 1455 553 1455 553 450 554 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4639 4406 586 418 585 393 559 447 557 447 557 1477 532 1477 532 472 532 472 532 1476 533 1476 532 1476 532 1476 532 473 555 449 555 449 555 449 555 4455 554 450 554 450 554 450 554 450 554 1455 554 450 554 450 554 450 554 1455 554 1455 553 1455 553 450 554 450 554 1455 554 1455 554 1455 554 450 554 450 554 450 554 1455 554 55454 4557 4458 555 449 555 449 555 450 554 450 554 1455 554 1455 553 450 554 450 554 1455 554 1455 554 1454 554 1455 554 450 554 450 554 450 555 450 554 4455 553 450 554 450 554 450 554 450 554 1455 554 450 554 450 554 450 554 1455 554 1455 554 1455 553 450 554 450 554 1455 554 1455 553 1455 554 450 554 450 554 450 554 1455 554 +# +# Model: Edifier R1850DB +name: Power +type: parsed +protocol: NECext +address: 10 E7 00 00 +command: 46 B9 00 00 +# +name: Play +type: parsed +protocol: NECext +address: 10 E7 00 00 +command: 5E A1 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 10 E7 00 00 +command: 05 FA 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 10 E7 00 00 +command: 49 B6 00 00 +# +name: Next +type: parsed +protocol: NECext +address: 10 E7 00 00 +command: 02 FD 00 00 +# +name: Prev +type: parsed +protocol: NECext +address: 10 E7 00 00 +command: 1E E1 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 10 E7 00 00 +command: 41 BE 00 00 +# +# Model: Grundig CMS 5000 +name: Power +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 10 EF 00 00 +# Also Pause +name: Play +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 02 FD 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 0D F2 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 17 E8 00 00 +# +name: Next +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 13 EC 00 00 +# +name: Prev +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 11 EE 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 30 FC 00 00 +command: 0C F3 00 00 +# +# Model: Panasonic SA-PM193 +name: Power +type: parsed +protocol: Kaseikyo +address: AC 02 20 00 +command: D1 03 00 00 +# CD play/pause, tape play also exists but probably less commonly used +name: Play +type: parsed +protocol: Kaseikyo +address: AA 02 20 00 +command: A0 00 00 00 +# same as above +name: Pause +type: parsed +protocol: Kaseikyo +address: AA 02 20 00 +command: A0 00 00 00 +# +name: Vol_up +type: parsed +protocol: Kaseikyo +address: A0 02 20 00 +command: 00 02 00 00 +# +name: Vol_dn +type: parsed +protocol: Kaseikyo +address: A0 02 20 00 +command: 10 02 00 00 +# +name: Next +type: parsed +protocol: Kaseikyo +address: AC 02 20 01 +command: A1 00 00 00 +# +name: Prev +type: parsed +protocol: Kaseikyo +address: AC 02 20 01 +command: 91 00 00 00 +# +name: Mute +type: parsed +protocol: Kaseikyo +address: A0 02 20 00 +command: 20 03 00 00 +# +# Model: NAD C316BEE +name: Power +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 25 DA 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 94 6B 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 88 77 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 8C 73 00 00 +# +name: Play +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 01 FE 00 00 +# +name: Pause +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 4A B5 00 00 +# +name: Prev +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 05 FA 00 00 +# +name: Next +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 06 F9 00 00 diff --git a/applications/main/infrared/resources/infrared/assets/projector.ir b/applications/main/infrared/resources/infrared/assets/projector.ir new file mode 100644 index 00000000000..e9861de21f6 --- /dev/null +++ b/applications/main/infrared/resources/infrared/assets/projector.ir @@ -0,0 +1,829 @@ +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/applications/main/infrared/resources/infrared/assets/tv.ir b/applications/main/infrared/resources/infrared/assets/tv.ir new file mode 100644 index 00000000000..86b11e8a793 --- /dev/null +++ b/applications/main/infrared/resources/infrared/assets/tv.ir @@ -0,0 +1,1688 @@ +Filetype: IR library file +Version: 1 +# +name: Power +type: parsed +protocol: SIRC +address: 01 00 00 00 +command: 15 00 00 00 +# +name: Power +type: parsed +protocol: SIRC +address: 10 00 00 00 +command: 15 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 05 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 00 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 01 00 00 00 +# +name: Ch_next +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 02 00 00 00 +# +name: Ch_prev +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 03 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 0B 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 1C 00 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 4B 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 4F 00 00 00 +# +name: Ch_next +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 09 00 00 00 +# +name: Ch_prev +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 05 00 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 08 00 00 00 +# +name: Power +type: parsed +protocol: Samsung32 +address: 0E 00 00 00 +command: 0C 00 00 00 +# +name: Mute +type: parsed +protocol: Samsung32 +address: 0E 00 00 00 +command: 0D 00 00 00 +# +name: Vol_up +type: parsed +protocol: Samsung32 +address: 0E 00 00 00 +command: 14 00 00 00 +# +name: Vol_dn +type: parsed +protocol: Samsung32 +address: 0E 00 00 00 +command: 15 00 00 00 +# +name: Ch_next +type: parsed +protocol: Samsung32 +address: 0E 00 00 00 +command: 12 00 00 00 +# +name: Ch_prev +type: parsed +protocol: Samsung32 +address: 0E 00 00 00 +command: 13 00 00 00 +# +name: Power +type: parsed +protocol: RC6 +address: 00 00 00 00 +command: 0C 00 00 00 +# +name: Power +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 02 00 00 00 +# +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: Ch_next +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 12 00 00 00 +# +name: Ch_prev +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 10 00 00 00 +# +name: Mute +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 0F 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 50 00 00 00 +command: 17 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 40 00 00 00 +command: 12 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 31 49 00 00 +command: 63 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: AA 00 00 00 +command: 1C 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 38 00 00 00 +command: 1C 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 83 7A 00 00 +command: 08 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 53 00 00 00 +command: 17 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 18 18 00 00 +command: C0 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 38 00 00 00 +command: 10 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: AA 00 00 00 +command: C5 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 08 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 18 00 00 00 +command: 08 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 71 00 00 00 +command: 08 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 80 6F 00 00 +command: 0A 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 48 00 00 00 +command: 00 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 80 7B 00 00 +command: 13 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 80 7E 00 00 +command: 18 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 50 00 00 00 +command: 08 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 80 75 00 00 +command: 0A 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 80 57 00 00 +command: 0A 00 00 00 +# +name: Power +type: parsed +protocol: Samsung32 +address: 0B 00 00 00 +command: 0A 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: AA 00 00 00 +command: 1B 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 85 46 00 00 +command: 12 00 00 00 +# +name: Power +type: parsed +protocol: Samsung32 +address: 05 00 00 00 +command: 02 00 00 00 +# +name: Power +type: parsed +protocol: Samsung32 +address: 08 00 00 00 +command: 0F 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 01 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 634 2571 505 519 479 519 479 518 480 518 480 518 480 518 480 517 481 547 481 517 481 20040 590 2555 501 1007 999 997 510 548 480 486 512 486 512 486 542 485 513 516 482 116758 593 2552 504 1004 992 1004 514 514 514 483 515 513 485 483 545 482 516 482 516 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 525 1955 449 1999 476 4545 446 4544 478 2032 443 2006 469 2011 444 4577 445 4545 447 4574 448 2002 473 4547 444 34913 447 2032 443 2007 478 4542 449 4541 471 2039 446 2004 471 2008 447 4574 448 4543 448 4572 450 2030 445 4545 446 +# +name: Power +type: parsed +protocol: Kaseikyo +address: 80 02 20 00 +command: D0 03 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 389 1737 280 796 253 744 295 754 274 775 274 776 273 1827 271 1828 270 805 254 1820 278 796 253 797 252 745 294 1806 302 773 245 48942 305 1821 277 798 251 746 303 747 271 778 271 1829 279 796 253 796 253 1821 277 798 251 1823 275 1824 274 1825 273 802 247 1827 271 42824 381 1745 272 804 245 752 297 753 275 773 276 774 275 1825 273 1826 272 803 246 1828 270 805 254 795 244 753 296 1804 294 781 247 48939 379 1746 271 804 245 779 270 753 275 774 275 1825 273 802 247 802 247 1827 271 804 245 1829 279 1820 278 1821 277 798 251 1823 275 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 562 1721 561 594 557 597 564 617 513 615 536 618 543 1715 566 1716 566 1692 559 594 567 588 563 618 543 611 540 615 536 618 543 1715 556 623 538 617 534 621 530 624 516 638 513 642 509 1722 560 620 541 1717 565 1692 559 1724 568 1715 556 1701 560 1723 559 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8436 4189 538 1563 566 1559 539 510 559 543 516 507 542 560 509 540 509 567 512 1586 512 1562 567 1559 539 536 533 1566 542 507 562 513 536 540 509 22102 647 1478 559 1568 540 508 541 535 534 515 534 568 511 538 511 539 540 1585 513 1560 559 1567 541 534 535 1564 534 515 534 568 511 538 511 22125 644 1482 565 1561 537 511 538 564 515 508 541 535 534 541 508 516 563 1588 510 1563 556 1570 538 510 559 1567 541 534 515 535 534 541 508 +# +name: Power +type: parsed +protocol: RC5 +address: 00 00 00 00 +command: 0C 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 448 2031 444 2005 480 4540 452 4539 473 2037 438 2011 474 2006 449 4571 451 4539 453 4568 444 2036 449 4541 451 34906 527 1953 451 1998 477 4543 449 4542 480 2030 445 2004 471 2009 446 4575 447 4543 449 4572 450 1999 476 4545 446 +# +name: Power +type: parsed +protocol: NEC42 +address: 7B 00 00 00 +command: 00 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8041 3979 513 536 482 515 513 1559 509 515 514 1560 508 515 514 509 509 514 484 4000 533 1566 512 537 481 1566 512 537 481 1566 512 537 481 516 513 537 481 24150 8044 3977 505 518 510 539 479 1567 511 512 506 1568 510 539 479 543 485 538 480 3977 536 1564 514 534 484 1563 515 508 510 1563 515 508 510 540 489 534 484 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 383 2027 295 2112 271 2138 266 890 271 887 294 926 235 2114 300 887 274 915 236 2145 269 887 274 884 297 923 238 920 241 917 264 895 266 26573 384 2026 296 2111 273 2136 268 889 272 886 295 924 237 2113 301 886 275 914 237 2144 270 886 275 914 267 921 240 919 242 916 265 924 237 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 177 8474 175 5510 174 8476 173 8477 177 8504 171 5515 175 8476 178 8472 177 8473 176 5541 174 8476 173 45583 171 8481 178 5507 177 8473 176 8474 175 8506 173 5512 172 8478 176 8475 174 8476 178 5538 177 8474 175 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8044 3976 506 517 511 1563 505 517 511 538 480 517 511 538 460 563 455 568 460 3993 530 545 483 1564 514 1559 509 1564 514 509 509 540 488 535 483 513 505 24150 8043 3978 514 509 509 1564 514 509 509 540 478 519 509 540 458 565 464 559 459 3994 529 546 482 1565 513 1560 508 1565 513 510 508 541 487 536 482 541 477 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 558 2942 450 10021 482 2989 484 10015 447 3024 449 10021 472 3100 485 2986 477 2994 500 2999 475 2996 477 2994 479 2992 482 3018 476 2995 479 6464 484 36270 477 3023 450 10020 473 2999 485 10014 448 3022 452 10019 474 3098 478 2994 480 2991 503 2996 477 2994 480 2992 482 2990 484 3015 479 2992 481 6462 485 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 587 2407 476 1062 965 547 482 547 451 577 452 546 452 22108 590 2404 479 1060 967 1028 510 519 509 548 480 487 511 120791 645 2411 472 1066 961 1065 483 515 514 514 504 493 515 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 172 7439 171 7441 169 7443 177 7434 176 7462 178 4887 176 4916 177 4914 169 7469 171 4920 174 4918 175 55174 176 7436 174 7437 173 7439 171 7440 175 7463 172 4894 174 4917 171 4921 172 7465 175 4916 178 4914 169 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 589 2556 500 524 474 554 454 544 454 543 455 543 455 543 455 543 445 583 446 552 446 20046 584 2561 505 1033 485 514 963 518 480 1032 516 512 476 522 506 491 507 522 476 116758 586 2560 506 1033 484 513 964 548 450 1031 507 522 476 521 507 490 508 521 477 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 586 2407 476 1063 964 578 450 548 450 547 481 517 481 577 451 546 452 546 472 556 452 545 453 575 453 514 484 544 484 543 455 14954 510 2483 481 1058 480 548 959 552 446 1067 481 516 512 546 482 515 992 1003 535 493 515 543 486 513 475 522 506 552 446 111671 589 2405 478 1061 477 551 967 514 484 1059 479 549 479 548 480 517 990 1036 512 516 482 546 483 515 503 525 483 544 454 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8444 4180 537 1564 565 1560 538 1561 557 1568 540 1559 539 536 533 542 517 559 510 1563 535 1564 565 1561 537 512 567 1558 540 535 534 1566 542 1557 562 23122 564 1562 557 1569 539 1560 538 1587 542 1558 540 534 535 515 534 541 538 1587 511 1563 566 1560 538 536 533 1567 541 534 515 1585 533 1566 542 23166 561 1565 564 1561 537 1563 535 1590 539 1561 537 538 541 534 515 535 534 1564 534 1566 563 1563 535 540 539 1560 538 511 538 1588 541 1559 539 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 527 1923 481 1998 477 4543 449 4542 470 2040 445 2004 471 2009 446 4575 447 4543 449 4572 450 1999 476 2034 441 34899 524 1956 448 2001 474 4546 446 4545 477 2033 442 2007 478 2002 443 4578 444 4546 445 4575 447 2003 472 2037 448 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 533 1356 437 3474 427 3483 429 3455 436 1454 430 1459 405 28168 510 1379 434 3477 434 3476 425 3459 432 1457 427 1462 402 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 179 7433 177 4915 178 7434 175 7436 174 7464 176 7435 175 4916 177 4915 173 4918 170 4922 171 4920 173 55174 175 7437 173 4919 174 7437 173 7439 171 7467 173 7438 172 4920 173 4919 174 4917 176 4915 178 4914 169 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 169 6731 176 6748 169 6730 177 6748 169 6755 172 4427 177 4447 178 6721 175 6749 178 4446 168 4456 169 54704 176 6723 174 6750 177 6723 173 6750 177 6747 170 4429 175 4449 176 6723 174 6751 176 4448 177 4447 178 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3506 3494 876 830 840 2576 847 2568 844 862 819 2570 842 864 816 863 818 2570 842 836 844 2572 840 866 815 865 815 2573 839 867 813 866 814 2573 850 857 813 2575 847 2568 844 834 847 2569 843 835 845 2571 872 2571 842 32654 3512 3488 872 834 847 2570 842 2573 839 867 814 2574 849 858 822 857 813 2575 848 859 821 2566 846 832 848 860 821 2567 845 833 848 860 820 2568 844 834 847 2569 843 2572 840 838 843 2574 849 829 841 2575 868 2575 837 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 560 2939 453 10018 475 2996 477 10022 450 3021 452 10018 475 3097 478 6464 483 6460 477 6466 502 6469 479 2993 480 2990 484 36274 564 2936 456 10015 478 2993 481 10020 534 2936 456 10014 479 3093 482 6461 476 6466 482 6461 476 6495 473 2999 485 2986 477 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 10726 41047 10727 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1617 4604 1559 1537 1560 1537 1560 4661 1533 33422 1613 4607 1566 1530 1556 1540 1536 4685 1539 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 174 4972 177 4910 173 4944 170 6988 174 6984 177 6951 175 6983 174 14269 177 4969 175 4912 176 4941 178 6979 172 6986 175 6953 178 6980 171 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 174 7067 176 10544 1055 719 1053 739 1054 738 953 822 1052 2566 169 3435 171 3431 175 3446 170 3432 174 3446 170 3432 174 3428 178 3442 174 3429 177 3425 171 39320 2323 4900 178 10543 1056 719 1053 739 1054 738 953 821 1053 2566 169 3435 171 3431 175 3445 171 3432 174 3446 170 3432 174 3428 178 3442 174 3428 178 3425 171 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3506 3492 868 839 841 2575 848 858 822 2566 846 832 848 859 821 858 812 2576 847 860 820 2567 845 833 847 860 821 2568 844 862 818 2570 842 864 817 2571 841 2574 849 2567 845 861 820 2568 845 834 847 2570 873 2570 842 34395 3503 3496 874 833 847 2568 845 834 847 2570 842 864 816 835 846 862 819 2569 843 835 846 2571 841 865 816 864 816 2571 841 837 844 2573 850 857 813 2575 848 2567 845 2570 842 864 816 2572 840 838 842 2574 869 2574 838 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 170 8479 170 5516 178 8472 177 8474 175 8506 173 5513 171 8479 175 8476 178 8472 177 5540 175 8475 174 45584 177 8473 176 5509 175 8476 173 8477 176 8504 170 5516 178 8472 177 8474 175 8476 173 5543 172 8479 170 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 178 4969 170 6958 173 4944 175 6983 173 6956 174 6984 177 6980 171 16308 180 4966 173 6955 176 4941 172 6985 176 6982 169 6960 176 6982 174 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 585 2409 474 550 478 550 448 1033 994 1063 485 512 506 79916 585 2410 473 551 477 550 448 1034 993 1033 515 543 475 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1192 1012 6649 26844 1192 1013 6648 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3134 6105 6263 82963 3134 6105 6263 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 588 1511 567 1533 565 589 531 597 513 589 541 587 533 1565 533 569 541 1533 565 562 568 1531 537 1537 561 593 537 591 539 1507 561 1539 569 22152 586 1513 565 1535 563 590 540 562 538 564 566 588 542 1557 531 597 513 1534 564 590 540 1533 535 1539 559 568 562 592 538 1509 569 1531 567 +# +name: Power +type: parsed +protocol: RC5 +address: 00 00 00 00 +command: 20 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 689 1461 566 1534 564 589 531 597 513 1534 564 564 566 1533 535 620 510 1537 561 592 538 1535 543 1531 567 587 533 595 535 1511 567 1533 565 22161 588 1512 566 1534 564 564 556 598 512 1535 563 565 565 1534 534 594 536 1538 560 593 537 1536 542 1532 566 587 543 585 535 1511 567 1534 564 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 588 2558 498 526 482 515 483 546 452 545 453 545 453 545 453 545 453 544 474 554 444 20047 583 2562 504 519 479 519 479 1003 515 483 1024 548 450 1001 995 1001 506 116771 593 2552 504 520 478 551 447 1004 513 514 993 549 449 1002 994 1002 516 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 587 2559 507 517 481 517 481 547 451 516 482 546 452 546 452 546 452 545 473 525 483 20038 592 2553 503 521 477 552 446 521 477 1004 514 515 992 1034 483 514 484 513 485 116769 593 2552 504 520 478 550 448 520 478 1003 515 514 993 1032 486 513 475 522 476 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4558 4576 558 596 565 97881 4554 4579 565 589 562 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1039 7202 958 4713 981 4713 961 7255 956 4739 955 16077 1038 7204 956 4714 980 4715 959 7256 954 4741 954 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 506 2638 510 516 482 516 482 546 452 546 452 545 453 545 453 545 453 575 443 554 444 20048 592 2554 502 1036 481 517 481 517 511 516 482 516 961 1035 513 515 483 514 484 116757 594 2552 504 1034 484 514 484 514 504 524 484 513 964 1032 506 522 476 492 506 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 170 7441 169 4924 174 7437 193 7444 171 7441 174 4918 170 7441 174 4917 171 7440 195 7443 172 7439 171 55187 174 7437 173 4919 175 7437 173 7438 172 7466 175 4891 172 7439 171 4921 172 7439 171 7441 174 7464 171 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 179 4967 172 4915 178 6980 171 4945 169 4948 176 4912 176 4941 178 16307 176 4969 175 4912 176 6982 174 4942 171 4945 169 4919 174 4943 176 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1042 793 898 883 869 858 924 884 878 877 875 879 873 855 928 1717 901 854 1794 878 894 887 875 1716 902 89114 985 798 903 878 874 880 902 852 900 855 897 857 905 876 896 1722 906 849 1789 883 899 855 897 1721 897 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 170 8480 169 8481 178 8472 177 8474 175 8476 173 5513 176 8474 175 8476 173 8507 178 5509 175 8505 174 45558 175 8476 173 8476 173 8477 172 8478 171 8480 169 5517 177 8473 176 8474 175 8506 174 5512 172 8509 171 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 628 2577 499 528 480 545 453 544 454 544 454 544 454 544 454 543 455 543 475 553 445 20047 593 2553 503 521 477 551 447 1004 514 515 992 519 479 1003 515 513 485 543 455 116768 585 2561 505 519 479 519 479 1002 516 513 994 548 450 1001 506 522 476 521 477 +# +name: Power +type: parsed +protocol: NECext +address: 80 63 00 00 +command: 0F 15 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 80 64 00 00 +command: 49 08 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 298 1828 270 804 245 1829 279 1820 278 797 252 771 278 1822 276 1824 274 800 249 1825 273 802 247 776 252 771 278 1822 276 799 250 48931 388 1737 280 796 253 1821 277 1822 276 798 251 1823 275 800 249 774 275 1825 273 776 273 1801 297 1828 270 1829 279 796 253 1821 277 42813 301 1825 273 801 248 1826 272 1827 271 804 245 805 244 1830 278 1821 277 798 251 1823 275 799 250 774 244 779 280 1820 278 796 253 48926 382 1744 354 696 271 1828 270 1829 279 796 253 1821 277 797 252 746 303 1823 275 774 275 1799 299 1826 272 1827 271 804 245 1829 279 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 177 5508 176 5539 176 8475 174 5542 173 8478 171 5545 170 8481 168 8482 177 8473 176 5541 174 8476 173 45573 169 5517 177 5538 177 8473 176 5541 174 8476 173 5544 171 8479 170 8481 178 8472 177 5540 175 8475 174 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 175 8474 175 8475 174 8477 172 8478 171 8480 169 8481 178 5538 177 5510 174 5542 173 5543 172 5544 171 45575 177 8472 177 8474 175 8476 173 8477 172 8478 171 8481 178 5507 177 5539 176 5540 175 5542 173 5543 172 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8050 3971 511 1562 516 1558 510 539 490 1557 511 1563 515 533 485 538 490 533 485 3983 530 1569 509 1564 514 1559 509 1565 513 1560 508 515 513 536 482 541 488 24152 8042 3979 514 1560 508 1565 513 510 508 1565 513 1560 508 515 513 536 482 541 488 3980 533 1567 511 1562 516 1557 511 1563 515 1558 510 539 489 534 484 539 490 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 175 8475 174 5512 177 8473 176 8475 174 8506 179 5508 176 8474 175 8475 174 8477 172 5544 171 8480 169 45587 176 8475 174 5511 173 8477 177 8473 176 8504 170 5516 178 8472 177 8473 176 8475 174 5542 173 8478 171 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 176 7436 174 7438 172 7439 171 7441 174 7463 172 7440 170 4921 172 4919 174 4917 176 4916 177 4914 174 55176 175 7437 173 7439 191 7446 174 7438 177 7434 176 7435 175 4917 176 4915 179 4914 174 4917 171 4946 178 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 176 7435 175 4917 177 7435 175 7436 189 7449 176 7435 175 7437 173 4918 175 7436 174 4918 175 4916 178 55171 175 7436 174 4918 170 7441 174 7438 177 7460 170 7441 174 7438 177 4914 174 7437 178 4914 169 4922 171 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8049 3973 509 1564 514 509 509 1564 514 535 483 1564 514 535 483 540 489 535 483 3980 533 1566 512 537 481 1566 512 511 507 1566 512 537 481 542 486 511 507 24149 8045 3976 516 1558 510 512 516 1557 511 512 516 1558 510 512 516 507 511 512 506 3984 539 1560 508 515 513 1560 508 541 488 1560 508 541 488 536 482 514 504 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 366 202 867 527 170 130516 343 227 863 529 168 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 176 5992 176 1372 176 1320 177 1345 172 1349 179 1370 178 1317 175 4444 176 1346 171 1351 177 1345 172 1349 179 1344 174 5963 175 65574 172 5995 178 1344 174 1348 175 1347 175 1347 170 1378 170 1325 172 4447 178 1344 173 1349 169 1353 175 1347 170 1351 177 5961 177 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 497 1478 488 511 467 1482 494 531 447 1477 499 501 466 533 445 530 468 507 471 529 438 12857 488 1485 491 509 469 1481 495 529 448 1476 490 510 468 532 445 529 469 480 498 502 465 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 982 6313 961 2662 903 2718 929 2719 907 6363 900 2722 904 6365 909 6361 903 6368 906 2742 905 46364 989 6307 906 2716 911 2712 925 2723 903 6367 907 2715 901 6369 905 6365 909 6361 903 2745 902 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 599 257 975 317 177 130649 308 228 974 319 175 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 176 4969 170 6958 173 6985 177 6981 171 6958 178 6980 177 6981 170 16308 180 4966 173 6955 176 6982 169 6988 174 6956 175 6983 173 6984 172 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 178 5990 173 1349 174 1348 175 1348 174 4444 176 1346 171 1351 177 4416 178 1344 173 1348 169 1353 175 1347 170 1351 177 9048 177 65573 173 5995 173 1348 175 1348 174 1347 176 4444 171 1351 177 1345 173 4421 173 1348 169 1352 176 1347 170 1351 177 1345 172 9053 177 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 174 4972 177 4910 173 6985 177 4940 174 6984 178 6951 175 6983 174 14269 177 4968 176 4912 176 6981 175 4941 173 6985 177 6953 178 6979 172 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1318 225 177 130305 1609 231 171 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 585 2410 473 1065 962 581 447 519 479 580 448 549 449 579 449 518 480 548 480 517 481 517 481 577 451 516 482 576 452 545 453 14955 510 2483 481 1058 480 549 969 543 445 1037 511 547 481 546 482 516 482 545 484 545 483 514 484 544 963 1063 485 543 445 111612 626 2427 557 954 513 512 995 547 451 1031 507 521 508 551 477 520 478 550 478 549 479 488 510 548 969 1057 481 517 481 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 203 272 1215 302 171 130229 1423 275 178 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 176 7436 174 7438 192 7446 174 7437 178 7434 176 7435 175 4917 176 4915 178 4913 175 4917 197 7440 175 55130 286 7377 177 7435 175 7437 173 7438 172 7466 174 7411 178 4913 170 4921 172 4919 174 4918 175 7436 174 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 175 7437 173 4918 170 7441 174 7437 178 7460 170 7441 179 7433 177 4915 178 4913 170 4922 171 4920 173 55124 291 7372 172 4921 172 7439 171 7441 174 7463 172 7440 170 7442 178 4913 170 4922 171 4920 173 4918 175 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 172 8477 172 8478 171 8480 169 5516 179 8502 178 5509 175 8475 174 8476 173 8479 170 5545 170 8480 169 45588 176 8473 176 8474 175 8476 173 5513 177 8504 171 5515 174 8476 178 8472 177 8473 176 5541 174 8476 173 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 177 7436 174 4918 175 7436 174 4918 175 7462 178 4887 176 4916 177 4914 174 4917 171 4921 172 4919 175 55184 175 7435 175 4918 175 7436 174 4918 175 7462 178 4914 174 4917 171 4920 173 4919 174 4917 176 4915 178 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 508 2484 480 1060 967 544 485 544 454 574 454 543 455 573 445 553 445 522 506 552 446 552 446 551 477 551 447 581 447 520 478 550 478 15908 626 2427 476 1062 476 522 995 547 451 1061 477 520 508 550 478 519 479 519 999 1058 959 1037 990 582 446 1035 483 111666 590 2404 479 1059 479 549 968 543 455 1027 511 548 480 517 511 486 512 546 961 1065 962 1034 993 579 449 1032 486 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 175 8475 174 8475 174 8477 172 8478 171 8480 169 5517 178 8473 175 8475 179 8501 173 5513 176 8505 169 45562 179 8472 177 8473 176 8474 175 8476 173 8478 171 5515 174 8476 178 8473 176 8505 174 5512 172 8508 171 120769 178 93377 175 7437 173 4919 174 7437 173 7439 171 7467 173 7439 171 7441 174 4918 170 4921 172 7439 171 7467 173 55167 171 7440 170 4922 171 7440 195 7443 172 7439 171 7441 174 7438 177 4915 173 4918 195 7442 173 7439 170 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 295 1805 273 776 242 1808 270 754 244 1806 272 777 241 758 270 754 264 760 268 756 272 14149 297 1802 266 784 244 1805 263 762 246 1803 265 785 244 780 248 751 267 758 271 753 265 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 535 1723 569 585 566 615 536 619 542 586 565 616 535 1722 539 615 536 1721 561 620 541 587 564 617 534 621 540 588 563 618 543 1714 537 617 534 621 540 615 536 618 543 612 539 615 536 1722 560 594 567 1717 534 1723 569 1714 557 1700 643 1639 643 1641 559 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 502 2521 504 521 997 545 453 575 453 545 453 575 454 22097 591 2433 511 513 994 1062 476 552 476 522 476 552 476 120839 628 2455 509 515 992 1064 484 513 505 493 515 543 475 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 591 2404 479 1060 967 1029 509 519 509 549 479 518 480 79926 585 2408 556 956 989 1033 515 544 484 543 475 522 476 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 586 2560 506 518 480 548 450 548 450 517 481 517 481 517 481 547 451 546 482 516 482 20040 590 2556 500 1038 480 518 969 543 455 543 475 1006 511 517 481 517 511 486 481 116778 584 2561 505 1033 485 513 964 548 450 548 480 1001 506 522 476 522 506 521 446 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 917 206 175 186 170 21561 170 2280 175 2274 502 1929 174 2276 169 5178 170 2261 173 3724 498 1932 171 2279 176 2273 172 3709 172 2277 178 3720 171 17223 177 7619 174 2275 170 2279 176 2256 168 2280 175 5172 176 2256 168 3729 173 2276 179 2253 171 2278 177 3703 178 2271 174 3724 177 17251 170 7627 177 2272 173 2276 169 2263 171 2277 178 5169 169 2262 172 3726 175 2256 168 2280 175 2274 171 3710 171 2278 177 3720 171 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 565 233 653 313 170 130328 752 235 233 107 229 398 177 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 586 2439 505 549 968 1027 511 517 511 517 481 547 481 21583 584 2439 505 520 997 1059 479 549 479 518 480 548 480 120894 593 2432 501 522 995 1061 477 521 507 520 478 550 478 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 558 8032 474 8115 503 8116 482 8108 480 8141 477 5239 476 8114 504 8115 483 8107 481 5236 509 8111 477 45290 554 8036 481 8108 510 8110 478 8112 476 8145 473 5243 482 8107 511 8109 479 8111 477 5240 505 8115 473 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 173 7438 172 4920 173 7438 172 4920 173 7465 175 4890 173 7439 171 4920 173 4919 174 4917 176 4915 178 55179 170 7441 169 4924 174 7437 178 4913 175 7463 172 4893 170 7441 174 4918 170 4922 171 4920 173 4918 175 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 225 745 222 774 193 778 200 797 175 769 193 777 201 771 196 774 224 747 220 776 202 769 198 248 220 252 196 250 218 253 225 746 221 250 198 248 220 252 226 246 222 223 225 248 220 252 216 229 219 253 225 247 221 277 176 243 220 279 189 230 228 244 224 248 220 252 196 250 218 253 215 257 201 770 197 799 189 257 201 271 197 37716 222 749 218 778 200 771 196 801 172 772 200 771 196 774 193 777 221 750 217 780 197 773 194 251 217 255 193 279 199 273 195 749 218 254 194 252 226 245 223 275 178 242 221 277 201 245 193 253 225 247 221 250 218 254 194 252 226 272 196 223 225 248 220 251 217 255 193 253 225 247 221 251 197 773 194 803 174 297 176 244 219 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3503 2655 197 642 876 2568 844 834 846 833 848 860 821 831 839 2576 847 2569 843 2572 841 2574 849 858 823 857 813 866 815 865 815 2572 841 2575 848 2568 844 2570 842 836 844 863 818 834 847 861 820 2568 845 2571 872 2571 842 32651 3505 3495 875 2567 845 861 820 832 849 859 811 840 840 2576 847 2568 844 2571 842 2574 849 830 840 867 814 838 842 865 815 2572 840 2575 848 2568 845 2571 841 865 815 864 817 834 846 861 819 2569 843 2572 871 2572 840 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 347 677 219 252 196 276 202 742 225 798 174 770 192 778 200 771 196 775 223 748 219 777 200 770 197 249 219 253 195 251 227 271 197 747 220 252 216 229 219 253 225 273 195 277 176 244 219 253 215 230 228 244 224 248 220 252 196 250 218 280 198 247 201 245 223 249 219 253 195 251 227 245 223 248 200 797 175 795 198 248 200 246 222 37666 344 678 228 245 223 222 226 771 196 800 198 747 220 750 217 753 224 773 194 776 202 769 198 799 173 246 227 245 223 249 199 247 221 749 218 280 198 247 201 245 223 249 219 253 195 251 227 244 224 248 200 272 196 250 218 254 194 252 226 245 223 249 199 274 194 251 227 245 193 253 225 273 195 251 217 753 225 746 221 251 197 275 193 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 174 7437 173 7440 174 7437 178 7433 177 7461 169 7442 178 4914 174 4917 171 4921 172 4919 174 7463 177 55163 177 7435 174 7437 173 7438 172 7440 175 7463 172 7413 176 4915 178 4913 175 4917 171 4920 174 7438 172 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8042 3979 513 510 508 541 487 1559 509 515 513 1560 508 515 513 510 508 541 477 3981 532 1568 510 1563 515 1558 510 1564 514 1559 509 514 514 535 483 540 488 24151 8042 3979 513 536 482 541 487 1560 508 541 488 1560 508 541 487 536 482 541 477 3980 533 1566 512 1561 507 1566 512 1562 516 1557 511 538 491 533 485 538 490 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8988 4504 640 487 562 592 538 590 510 592 538 590 540 588 532 596 514 614 516 1689 562 1668 563 1695 536 1695 566 1664 567 1690 612 1618 643 1430 801 1613 648 481 558 570 540 589 541 587 533 595 535 592 508 594 536 592 538 1667 564 1693 558 1672 569 1688 563 1668 644 1586 563 1694 567 40630 8994 2265 557 96833 8987 2273 538 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 173 7439 171 4922 172 7439 171 4921 172 7466 174 4917 176 4915 173 4918 170 7441 174 7438 192 7445 175 55181 174 7437 173 4918 175 7437 173 4919 175 7463 177 4888 175 4917 176 4915 178 7460 170 7440 175 7437 178 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1636 4610 1563 1533 1584 7760 1561 28769 1641 4606 1557 1539 1588 7757 1564 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 590 2404 479 1059 968 575 454 544 454 574 454 543 455 543 475 522 476 552 476 552 446 551 447 581 448 520 478 581 447 519 479 549 479 15967 587 2407 476 1062 476 522 995 547 451 1030 508 520 509 550 478 519 479 519 998 1028 999 1057 481 547 960 1066 482 111641 587 2407 476 1063 485 513 994 547 451 1031 507 551 477 551 477 520 478 550 968 1059 968 1027 511 548 959 1036 512 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 588 2406 477 1061 966 577 451 516 482 545 483 545 453 575 443 524 484 514 504 554 454 543 455 543 475 553 445 583 445 521 477 582 446 15969 585 2409 474 1065 483 514 993 549 449 1033 515 543 475 553 475 522 476 552 965 1030 997 576 452 1029 998 1028 479 111668 587 2407 475 1063 485 543 964 517 481 1031 507 552 476 551 477 520 478 550 968 1028 999 574 454 1027 990 1036 481 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 174 7439 196 7441 174 7437 173 7439 171 7441 174 7438 177 7460 170 7442 178 7433 177 4915 173 4918 170 55186 174 7438 172 7440 170 7441 174 7438 177 7461 169 7416 173 7464 176 7435 175 7437 173 4918 175 4917 176 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 173 7438 172 7441 174 7438 177 7434 176 7462 168 7443 177 7435 175 4917 176 4915 173 7438 177 4941 173 55166 174 7438 197 7441 174 7437 173 7439 171 7441 174 7438 177 7460 170 4922 171 4893 195 7443 172 4920 173 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 175 7436 179 4913 175 4917 171 4920 173 4945 169 4896 177 7435 175 7436 174 7464 176 4916 177 4914 169 55180 170 7441 169 4924 169 4922 171 4920 173 4919 174 4917 176 7435 175 7463 177 7434 176 4916 177 4914 174 +# +name: Power +type: parsed +protocol: RC5 +address: 02 00 00 00 +command: 0C 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 176 5991 177 1372 176 1320 177 1344 173 1349 174 1374 169 1327 170 4449 176 1346 171 1351 177 1345 172 1349 168 1353 175 5963 175 65573 173 5995 178 1344 174 1348 175 1348 174 1347 170 1378 170 1325 172 4447 178 1344 174 1349 169 1353 175 1347 170 1352 176 5961 177 +# +name: Power +type: parsed +protocol: NEC +address: 71 00 00 00 +command: 4A 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 60 00 00 00 +command: 03 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 60 00 00 00 +command: 00 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 42 00 00 00 +command: 01 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 50 AD 00 00 +command: 00 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 50 AD 00 00 +command: 02 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 50 00 00 00 +command: 3F 00 00 00 +# +name: Power +type: parsed +protocol: Samsung32 +address: 06 00 00 00 +command: 0F 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 12 00 00 00 +# +name: Power +type: parsed +protocol: Samsung32 +address: 08 00 00 00 +command: 0B 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 83 55 00 00 +command: C2 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 51 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 BD 00 00 +command: 01 00 00 00 +# +name: Power +type: parsed +protocol: Samsung32 +address: 00 00 00 00 +command: 0F 00 00 00 +# +name: Power +type: parsed +protocol: Samsung32 +address: 16 00 00 00 +command: 0F 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 01 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 80 68 00 00 +command: 49 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 86 02 00 00 +command: 49 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 178 7761 176 11308 546 957 540 1958 538 970 537 1955 541 962 545 1953 543 964 543 962 545 957 540 970 548 960 547 1945 541 1950 546 965 542 1953 543 962 545 1945 540 970 537 1958 538 7881 172 7744 172 11318 536 971 536 1956 540 963 534 1964 542 966 541 1951 535 968 539 971 536 971 536 969 538 964 533 1965 541 1954 542 963 534 1956 540 971 536 1959 537 968 539 1951 535 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 278 1845 274 808 271 806 273 812 278 805 275 805 274 1840 279 1844 275 809 281 1836 272 806 274 812 278 805 274 1842 277 802 277 44956 279 1842 277 804 275 802 277 808 271 811 279 1838 281 798 271 814 276 1844 275 806 273 1841 278 1845 274 1846 273 808 271 1843 276 44959 275 1845 274 807 272 805 275 811 279 804 275 805 274 1839 280 1844 275 808 271 1845 274 805 274 811 279 804 275 1841 278 801 278 44955 280 1841 278 802 277 801 278 807 272 810 280 1837 271 807 272 813 277 1843 276 805 274 1839 280 1843 276 1845 274 807 272 1842 277 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 509 1717 504 629 512 631 510 627 504 633 508 633 508 1713 508 1719 512 1713 508 625 505 637 504 633 508 629 512 629 512 623 508 1719 512 626 505 628 513 631 510 627 514 623 507 632 509 1713 508 632 509 1716 505 1715 506 1724 507 1716 505 1719 512 1715 506 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 506 493 505 4059 505 5051 501 506 502 4065 499 511 497 4063 501 5058 504 504 504 4060 504 5052 500 507 501 4066 498 5056 506 5042 500 515 503 119614 504 505 503 4065 499 5051 501 511 497 4069 505 499 499 4072 502 5050 502 506 502 4066 498 5053 499 512 506 4060 504 5044 498 5061 501 508 500 +# +name: Power +type: parsed +protocol: NECext +address: 87 22 00 00 +command: E0 1F 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8313 4161 515 1574 514 1571 507 569 510 562 507 563 506 562 507 568 511 562 507 1580 508 1577 511 1582 506 567 513 1575 513 554 505 571 509 564 505 22604 513 1573 505 1589 509 563 506 564 505 562 507 569 511 562 507 563 506 1579 509 1584 514 1576 512 558 511 1574 514 562 507 565 515 556 513 22593 514 1581 507 1583 505 564 505 563 506 570 510 563 506 564 505 562 507 1586 512 1578 510 1577 511 557 512 1581 507 566 514 556 513 555 514 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8735 4383 558 573 557 550 560 568 562 544 566 1722 560 540 560 1732 560 544 566 1719 563 1701 560 1723 559 1704 557 574 556 1699 562 574 556 1703 559 1727 565 1698 563 1721 561 546 564 1723 559 541 559 577 564 541 559 571 560 548 562 565 566 1697 565 567 564 1693 558 1733 559 1701 560 39926 8754 2247 565 92341 8758 2243 589 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3298 3336 821 2506 825 881 820 2505 826 2529 823 856 825 2524 817 866 825 2528 813 2513 818 888 813 862 819 887 814 865 826 2522 820 864 827 2526 815 861 820 887 814 2510 821 885 816 863 818 2531 821 2512 819 2559 793 32401 3298 3349 818 2507 824 882 819 2509 822 2527 814 868 823 2530 821 855 826 2531 821 2504 827 879 822 857 824 875 816 867 824 2529 823 854 827 2530 821 853 817 889 822 2506 825 873 818 866 825 2527 814 2513 818 2564 788 +# +name: Power +type: parsed +protocol: NEC42 +address: 1C 01 00 00 +command: 12 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 289 2112 261 2109 295 2101 262 918 294 912 259 915 297 2098 265 916 296 909 262 2107 297 905 266 913 289 918 263 910 292 909 262 918 294 24789 263 2106 298 2098 265 2110 294 913 258 916 296 905 266 2108 296 911 260 914 288 2107 266 914 298 908 263 911 291 910 261 919 293 913 258 +# +name: Power +type: parsed +protocol: NEC +address: 38 00 00 00 +command: 12 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3215 1637 410 430 405 439 406 1242 408 435 410 1242 408 428 407 440 405 435 410 1240 410 1243 407 431 404 440 405 437 408 1238 402 1254 406 434 401 439 406 438 407 431 404 440 405 437 408 428 407 439 406 434 401 439 406 438 407 1241 409 434 401 441 404 432 403 444 401 1249 401 439 406 438 407 1241 409 434 401 441 404 433 402 444 401 1249 401 439 406 1248 402 436 409 434 401 441 404 433 402 444 401 439 406 45471 3239 1614 403 435 400 444 401 1250 400 437 408 1248 402 438 407 433 402 442 403 1245 405 1248 423 420 405 431 404 443 402 1248 402 1248 422 421 404 435 400 443 402 440 405 431 404 443 402 438 407 433 402 442 403 435 400 443 402 1250 400 436 399 447 408 432 403 438 407 1246 404 434 401 443 402 1250 400 436 399 447 408 432 403 437 408 1246 404 434 401 1253 407 434 401 436 399 447 408 432 403 437 408 436 399 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 7928 3948 504 515 503 518 500 1583 505 516 502 1584 504 510 508 516 502 516 502 3952 510 1579 509 507 501 1587 501 519 510 1571 507 518 500 517 501 517 501 23073 7931 3943 509 514 504 515 503 1578 500 524 505 1580 508 510 508 514 504 511 507 3951 511 1576 502 513 505 1586 502 515 503 1582 506 515 503 513 505 516 502 +# +name: Power +type: parsed +protocol: NECext +address: 18 18 00 00 +command: C0 3F 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 492 4975 496 4993 498 498 500 4056 498 504 494 4055 499 4963 497 532 496 4031 492 4996 495 502 496 4060 494 4972 499 4990 491 506 492 4063 491 511 497 4052 492 4970 490 539 500 4027 496 103358 500 4961 500 4995 496 505 493 4056 498 499 499 4057 497 4969 491 533 496 4026 497 4997 494 508 490 4059 495 4967 494 5001 490 511 497 4053 491 505 493 4063 491 4975 496 528 490 4032 491 +# +name: Power +type: parsed +protocol: SIRC +address: 01 00 00 00 +command: 2F 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3425 3482 848 2607 846 2639 845 2608 846 2639 845 2612 852 2624 850 2613 851 911 851 885 847 919 843 891 851 915 847 890 852 907 845 897 845 917 845 891 851 915 847 887 845 2639 845 2612 852 2625 849 2613 851 2630 844 34282 3455 3478 852 2601 852 2633 851 2605 848 2629 845 2617 847 2633 851 2604 849 917 845 890 852 913 849 889 843 915 847 896 846 916 846 890 852 914 848 885 847 919 843 895 847 2630 844 2617 847 2634 850 2605 848 2636 848 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 500 500 498 4066 498 5058 504 502 506 4061 503 508 500 4060 504 5055 497 5055 497 511 497 4071 503 5047 505 507 501 4065 499 5049 503 512 496 124314 501 508 500 4067 507 5043 499 513 505 4060 504 501 497 4073 501 5052 500 5052 500 512 496 4066 498 5058 504 506 502 4058 506 5053 499 509 499 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 176 7763 174 8817 169 10842 544 1955 541 967 540 1952 544 959 538 972 546 962 545 1947 538 1952 544 967 540 1955 541 964 543 1947 539 972 546 1949 547 7873 170 7746 170 10350 175 11811 543 960 537 1961 535 972 535 970 537 965 542 1956 540 1956 540 965 542 1947 539 973 534 1960 536 969 538 1952 534 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2570 2682 1189 1208 1186 2665 1186 1217 1187 2668 1183 1215 1179 2671 1190 2695 1186 1187 1187 2692 1179 2671 1190 1213 1181 1193 1191 1207 1187 2663 1188 1215 1179 2676 1185 46941 2563 2685 1186 1191 1183 2698 1184 1188 1186 2691 1180 1197 1187 2694 1187 2666 1185 1210 1184 2674 1187 2695 1186 1185 1189 1206 1188 1189 1185 2696 1186 1186 1187 2689 1182 +# +name: Power +type: parsed +protocol: RC5 +address: 00 00 00 00 +command: 26 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3325 1560 406 444 401 453 402 1226 404 449 406 1226 404 443 402 454 401 449 406 1224 406 1228 402 446 409 444 401 451 404 1223 407 1230 410 440 405 445 410 443 402 446 409 444 401 451 404 442 403 453 402 448 407 443 402 451 404 1224 406 448 407 444 401 445 410 446 409 1221 409 442 403 1231 409 439 406 1227 403 450 405 441 404 452 403 1227 403 448 407 446 409 439 406 447 408 444 401 445 400 456 410 440 405 52348 3320 1553 403 445 400 454 401 1230 400 447 408 1228 402 449 406 444 401 452 403 1225 405 1229 431 421 404 442 403 454 401 1229 401 1229 431 423 402 446 399 455 400 451 404 442 403 453 402 448 407 443 402 451 404 444 401 452 403 1229 401 445 400 457 398 451 404 446 399 1235 405 443 402 1232 408 444 401 1225 405 452 403 447 398 452 403 1231 399 449 406 447 408 443 402 444 401 456 399 450 405 445 400 453 402 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 990 915 980 909 986 924 981 2829 981 934 981 2823 987 923 982 2828 982 933 982 906 989 33895 989 907 988 927 988 900 985 2841 979 915 980 2851 980 909 986 2840 980 914 981 934 981 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 821 5754 848 2490 841 2492 819 2524 817 5726 845 2492 839 5727 844 5757 845 5727 854 2483 848 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 5092 1621 406 2599 406 2598 407 2605 1653 1616 411 2619 1609 1606 1654 1618 409 2623 382 2600 405 +# +name: Power +type: parsed +protocol: NEC +address: 15 00 00 00 +command: 12 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1144 1010 6795 26754 1151 997 6798 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1144 1009 1120 1006 1143 1991 1116 26758 1146 1006 1123 1003 1146 1988 1119 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 170 46238 169 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8906 4165 572 1672 569 1678 563 640 572 637 565 643 569 633 569 643 569 1674 567 639 563 1684 567 637 565 1681 570 1675 566 1673 568 1681 570 636 566 640 572 637 565 639 563 1684 567 640 572 630 572 640 572 634 568 1675 566 1681 570 1670 571 638 564 1681 570 1669 572 1678 563 1680 571 40485 8898 2252 570 85621 8955 2194 567 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 446 1191 449 1194 446 1195 445 1204 446 1200 1316 459 447 1193 447 1202 448 1197 443 1201 449 1191 449 34491 443 1204 446 1197 443 1198 442 1207 443 1202 1314 436 440 1201 449 1199 441 1205 445 1198 442 1199 441 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4268 4327 522 1593 516 1603 516 1597 522 519 520 520 519 515 514 531 518 520 519 519 520 521 518 519 520 1597 522 1595 514 1597 522 1599 520 1595 514 524 515 1604 515 521 518 523 516 524 515 519 520 525 514 524 515 1599 520 522 517 1596 513 1605 514 1602 517 1595 514 1607 522 1592 516 40481 8748 2187 523 93986 8721 2189 521 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 247 3006 244 153 178 1007 251 117 637 597 381 678 218 156 175 529 382 679 217 157 174 24963 247 3038 252 117 219 994 249 119 217 483 250 117 173 531 278 779 173 167 169 569 383 679 217 156 175 126538 246 3039 251 118 218 995 247 120 195 504 249 118 172 532 277 780 172 168 178 560 382 680 216 157 174 +# +name: Power +type: parsed +protocol: NEC +address: 10 00 00 00 +command: EF 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4278 4318 521 517 522 520 519 517 522 520 519 521 518 516 513 531 518 520 519 1595 514 1605 514 1599 520 1598 521 1595 513 1598 521 1600 519 1596 513 1602 517 1601 518 1595 514 1604 515 525 514 520 519 526 513 525 514 524 515 527 522 513 516 526 513 1603 516 1595 514 1607 522 1593 516 40481 8749 2186 524 93985 8722 2189 521 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2746 8423 2744 19607 2746 19601 2742 8431 2745 8424 2742 19608 2745 8419 2747 19608 2745 19607 2746 8422 2744 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 170 6683 174 4889 175 10382 372 849 821 2498 176 10378 264 2479 842 2492 839 2475 846 2483 838 2481 840 2495 836 2477 844 845 846 37009 172 6681 176 4888 175 10381 373 848 924 2395 844 2490 174 10383 248 2492 172 10386 245 2495 169 +# +name: Power +type: parsed +protocol: NEC +address: C4 00 00 00 +command: 18 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 37 00 00 00 +command: 12 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 179 2644 178 8174 170 2646 176 8181 173 2648 174 8177 177 2640 172 5419 174 5413 170 2649 173 2644 178 2646 176 2646 176 5409 174 28831 174 2651 171 8183 171 2648 174 8174 170 2655 177 8177 177 2642 170 5412 171 5420 173 2649 173 2646 176 2640 172 2653 169 5419 174 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 174 2648 174 8178 176 2640 171 8185 169 2653 169 8182 172 2645 177 2647 557 2264 558 5027 174 5410 173 5418 175 5413 170 28837 168 2649 173 8184 170 2652 170 8181 173 2643 169 8188 176 2646 176 2643 168 2648 174 5417 176 5411 172 5413 170 5413 170 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 178 706 266 139 268 137 173 173 198 234 178 126768 175 299 169 86 275 130 267 138 172 230 177 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 554 1922 584 3809 582 3782 578 3821 580 1920 555 1941 544 1922 574 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 476 1470 465 3479 474 3469 474 3477 475 1467 468 1471 474 27473 472 1474 472 3475 467 3478 475 3468 474 1471 475 1468 467 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 301 2188 236 2169 235 2205 230 1009 234 1070 183 1064 179 2198 206 1046 207 1069 173 2207 207 1042 211 1062 201 1043 210 1032 231 1005 237 1039 234 1006 236 +# +name: Power +type: parsed +protocol: NEC +address: 6D 00 00 00 +command: 14 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 11 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: A0 00 00 00 +command: 0B 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 28 00 00 00 +command: 0B 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: FF 00 00 00 +command: 3F 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 176 7764 173 11361 544 1951 545 1948 176 8815 171 2319 177 2322 174 2321 175 2318 178 2313 173 18281 170 7746 170 11369 536 1954 542 1957 539 969 538 1954 542 1949 536 1962 534 1960 174 2320 176 2315 170 2328 178 2317 168 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 172 23035 176 2267 178 2285 170 2270 175 2288 177 11602 222 2218 216 2246 173 183 173 935 221 218 174 864 221 1250 171 1310 223 2219 216 2247 218 1244 223 216 176 869 170 1296 217 2239 216 1254 223 216 176 866 219 9127 169 7642 173 2284 171 2273 172 2290 175 2263 171 10143 178 10623 222 1245 222 217 175 1843 220 2226 219 1264 223 1237 174 183 178 952 219 2222 223 216 176 866 219 1249 172 9190 173 7637 178 2266 169 2286 169 2280 175 2284 171 10125 175 10622 224 215 177 868 216 2228 217 2238 171 1299 224 215 177 865 174 183 173 934 222 216 176 1848 215 1247 220 1265 222 762 170 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 7410 1482 382 2742 375 2747 380 2751 1630 1535 400 2724 1657 1529 1629 1538 407 2720 407 2716 401 2722 4125 1549 376 2751 376 2748 379 2744 1626 1541 405 2723 1658 1530 1628 1531 404 2726 401 2726 401 2723 4124 1542 383 2747 380 2747 380 2745 1626 1534 401 2729 1652 1539 1629 1532 403 2719 408 2722 405 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4277 4319 520 518 521 521 518 518 521 1597 522 1594 515 1597 522 1599 520 1594 515 1600 519 1600 519 1594 515 526 513 527 522 512 517 528 521 517 522 516 513 1605 514 522 517 525 514 525 514 521 518 526 513 525 514 1600 519 523 516 1597 522 1596 513 1603 516 1595 514 1607 522 1593 516 40481 8749 2186 524 93986 8721 2188 522 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1325 431 445 1199 1317 455 441 1207 443 1202 1294 457 449 1190 440 1209 441 1205 445 1198 1318 454 442 93237 1320 434 442 1201 1325 448 448 1200 440 1205 1291 460 446 1193 447 1202 448 1198 442 1201 1325 447 449 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 0B 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 300 1791 297 744 295 743 296 751 298 745 294 747 302 736 293 1837 271 745 294 747 302 1793 295 752 297 1802 296 1801 297 742 297 1806 302 31592 296 1801 297 742 297 749 300 744 295 745 294 745 294 752 297 1829 269 746 293 745 294 1809 300 744 295 1802 296 1799 299 748 301 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 178 2003 177 2899 177 1996 174 2908 168 2910 177 2000 170 2004 176 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8898 4173 564 642 570 1677 564 1677 564 645 567 640 572 631 571 641 571 635 567 639 563 1683 568 1673 568 641 571 636 566 637 565 647 565 641 571 634 568 642 570 1671 570 639 563 1682 569 632 570 643 569 636 566 1677 564 1683 568 636 566 1680 571 636 566 1674 567 1682 569 1674 567 40489 8904 2246 566 85626 8902 2248 564 +# +name: Power +type: parsed +protocol: NEC +address: AA 00 00 00 +command: 80 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 51 00 00 00 +command: 08 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3892 3856 525 978 529 977 530 969 528 977 530 974 523 975 532 1924 531 971 526 1924 531 975 532 1916 529 976 531 1921 524 1947 508 1925 530 1920 525 1926 529 1925 530 970 527 1927 528 976 531 1915 530 978 529 1921 1033 9201 3871 3867 524 977 530 975 522 981 526 972 525 983 524 978 529 1921 524 982 525 1923 532 973 524 1928 527 971 526 1931 524 1950 505 1922 523 1931 524 1924 531 1923 532 972 525 1922 523 985 533 1918 527 975 532 1922 1032 +# +name: Power +type: parsed +protocol: NEC +address: 6E 00 00 00 +command: 14 00 00 00 +# +name: Power +type: parsed +protocol: RC5 +address: 07 00 00 00 +command: 0C 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4758 1543 403 2731 407 2726 402 2739 1601 1514 401 2760 1580 1530 1577 1540 406 2758 400 2708 1602 1535 400 2740 408 2729 409 2726 401 2731 1599 1520 405 2758 1572 1540 1578 1532 403 2737 431 2706 1604 1535 411 2722 405 2734 403 2734 404 2731 1599 1512 403 2764 1576 1539 1578 1533 402 2730 428 2712 1608 1534 402 +# +name: Power +type: parsed +protocol: NEC +address: AA 00 00 00 +command: A7 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 171 313 176 798 337 133 600 232 170 126777 176 65 169 496 176 200 609 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 500 527 491 4033 500 4986 495 510 498 4054 500 499 499 4048 496 4974 497 530 499 4026 497 4989 492 513 495 4057 497 4967 493 4992 499 506 492 4060 494 505 493 4054 500 98563 497 529 500 4025 498 4988 493 512 496 4056 498 501 497 4050 493 4976 495 532 497 4028 495 4991 500 505 493 4059 495 4969 491 4994 497 508 490 4062 492 507 491 4056 498 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 879 901 871 1796 1770 903 869 917 876 916 877 913 880 906 877 918 875 914 879 1788 1768 1784 875 87826 871 921 872 1797 1769 897 875 919 874 915 878 910 873 920 873 914 879 913 870 1800 1776 1767 871 +# +name: Power +type: parsed +protocol: Kaseikyo +address: 90 02 20 00 +command: D0 03 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4411 4332 558 1660 561 596 565 612 559 597 564 617 565 585 566 620 561 591 560 620 561 595 566 611 560 597 564 1653 558 592 559 627 565 589 562 617 564 1630 560 617 564 592 559 22591 4439 4322 558 1639 561 618 563 590 561 622 560 592 559 624 557 597 564 612 559 600 561 618 563 590 561 622 559 1628 562 621 561 594 567 609 562 597 564 1652 559 595 566 617 564 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3444 1767 413 487 419 1274 417 481 415 1277 414 488 418 1267 414 492 414 1276 415 484 412 1282 419 478 418 1275 416 1276 415 480 416 1280 421 479 417 1272 419 1275 416 1272 419 1274 417 484 412 484 412 493 413 1277 414 485 421 1273 418 479 417 486 420 1271 420 476 420 486 420 479 417 482 414 1280 411 1276 415 488 418 1273 418 478 418 488 418 481 415 1275 416 487 419 478 418 485 411 1280 421 475 421 1275 416 1273 418 69071 3439 1759 441 456 440 1253 438 463 443 1243 438 468 438 1251 440 460 446 1247 444 454 442 1251 440 461 445 1241 440 1256 445 455 441 1248 443 461 445 1242 439 1254 447 1245 446 1240 441 465 441 458 438 462 444 1249 442 456 440 1252 439 463 443 452 444 1252 439 461 445 454 442 461 445 452 444 1249 442 1250 441 454 442 1254 437 463 443 456 440 463 443 1245 446 456 440 462 444 451 445 1251 440 460 436 1253 438 1256 445 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8705 4317 583 682 581 688 585 678 585 1721 581 1722 580 681 582 690 583 682 581 1721 581 1725 587 1713 579 689 584 683 580 1718 584 1724 588 1714 588 677 586 683 580 683 580 1726 586 680 583 678 585 687 586 679 584 1718 584 1721 581 1719 583 685 588 1716 586 1713 579 1729 583 1719 583 41145 8706 2217 585 94686 8705 2217 584 +# +name: Power +type: parsed +protocol: NECext +address: 10 2D 00 00 +command: 1F E0 00 00 +# +name: Power +type: parsed +protocol: RC5 +address: 05 00 00 00 +command: 0C 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 178 7753 174 2308 178 2284 171 2316 170 2298 177 10228 174 10724 223 1256 221 217 175 1868 169 2293 172 1327 216 1263 178 1316 217 2245 220 218 174 887 218 1261 170 10707 174 7752 175 2296 169 2314 171 2293 172 2307 179 10216 176 6265 174 10729 219 1258 219 1272 215 1268 173 2310 221 1255 222 217 175 877 172 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 532 1692 559 561 529 574 567 559 531 566 564 555 535 1694 557 568 532 1691 560 560 530 573 557 568 532 565 565 555 535 567 563 1688 533 564 567 554 536 567 563 562 538 558 562 558 532 1697 565 561 539 1683 558 1689 532 1697 564 1687 534 1689 562 1684 537 +# +name: Power +type: parsed +protocol: RC5 +address: 03 00 00 00 +command: 0C 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 12 FF 00 00 +command: 0E F1 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 446 1694 455 1706 453 1705 424 628 452 597 452 622 458 1673 456 625 455 594 455 1706 454 590 459 621 459 591 458 616 453 591 458 622 539 22750 459 1675 454 1733 427 1712 427 622 458 588 451 622 458 1681 458 618 451 595 454 1705 455 598 451 625 454 592 457 616 453 598 451 626 535 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3429 3445 875 2555 868 875 867 871 871 2560 873 868 874 2551 872 874 868 871 871 869 873 870 872 865 867 2565 868 873 869 2556 867 2567 866 874 868 2560 873 870 872 2555 868 2564 869 2586 847 2577 846 2589 844 870 872 32983 3470 3430 869 2559 874 868 874 867 875 2550 873 873 869 2559 874 865 867 877 875 862 870 873 869 872 870 2555 868 877 875 2554 869 2559 874 869 873 2554 869 873 869 2562 871 2553 870 2590 843 2586 847 2581 842 876 866 +# +name: Power +type: parsed +protocol: NEC +address: 83 00 00 00 +command: FF 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4275 4320 519 520 519 523 516 520 519 1599 520 520 519 515 524 521 518 520 519 1595 524 1595 524 1588 520 521 518 1599 520 1591 517 1603 516 1599 520 1595 524 1594 525 1588 521 1597 522 518 521 513 526 519 520 518 521 517 522 520 519 517 522 519 520 1597 522 1589 519 1601 518 1597 522 40475 8724 2186 514 93995 8752 2183 516 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 10380 4892 599 620 592 605 597 620 592 604 598 623 589 2081 598 627 595 2080 599 2101 598 2105 574 2099 590 2088 591 2111 599 591 590 2116 594 599 593 626 596 2082 597 619 593 2086 593 626 596 594 598 627 595 598 594 2106 593 604 598 2100 589 607 595 2107 593 2079 590 2116 594 2082 750 41149 8722 2109 590 94682 8727 2104 596 +# +name: Power +type: parsed +protocol: NEC +address: 83 00 00 00 +command: 08 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4271 4324 515 1600 519 1600 519 1594 515 1603 516 1601 518 1593 516 1605 514 1601 518 520 519 522 517 520 519 522 517 523 516 518 521 523 516 522 517 1598 521 1597 522 1591 518 1600 519 521 518 516 523 522 517 521 518 520 519 523 516 520 519 522 517 1599 520 1591 518 1603 516 1599 520 40477 8753 2183 516 93993 8724 2185 515 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 7847 3931 470 1448 467 495 472 1443 472 490 467 1451 464 491 466 499 468 491 466 4413 467 1455 470 486 471 1449 466 495 472 1441 464 501 466 492 465 494 463 22093 7851 3934 467 1454 471 490 467 1446 469 496 471 1446 469 489 468 494 473 484 473 4410 470 1449 466 489 468 1455 470 489 468 1449 466 496 471 486 471 490 467 +# +name: Power +type: parsed +protocol: RC5 +address: 01 00 00 00 +command: 0C 00 00 00 +# +name: Mute +type: parsed +protocol: RC5 +address: 01 00 00 00 +command: 0D 00 00 00 +# +name: Vol_up +type: parsed +protocol: RC5 +address: 01 00 00 00 +command: 10 00 00 00 +# +name: Vol_dn +type: parsed +protocol: RC5 +address: 01 00 00 00 +command: 11 00 00 00 +# +name: Ch_next +type: parsed +protocol: RC5 +address: 01 00 00 00 +command: 20 00 00 00 +# +name: Ch_prev +type: parsed +protocol: RC5 +address: 01 00 00 00 +command: 21 00 00 00 +# +# Model: VIZIO +# +name: Mute +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 09 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 02 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 03 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3462 1592 490 332 513 1200 489 331 514 1201 489 355 490 1201 489 356 512 1178 489 356 512 1178 512 334 487 1202 488 1202 488 357 512 1178 512 334 486 1203 487 1202 488 1203 487 1204 486 383 461 1228 488 357 488 357 487 357 487 1203 486 1204 486 359 485 1205 485 360 485 361 484 360 485 360 485 361 484 361 484 361 484 1206 484 360 484 361 484 361 484 1206 484 361 484 361 484 361 484 1206 484 361 484 1206 484 361 484 71543 3434 1620 486 359 485 1205 485 360 485 1206 484 360 485 1206 484 360 485 1206 484 360 485 1206 484 360 485 1205 485 1206 484 360 485 1206 484 360 485 1206 484 1206 484 1206 484 1206 484 361 484 1206 484 360 485 360 485 361 484 1206 484 1206 484 360 484 1206 484 360 485 361 484 361 484 360 485 361 484 361 484 361 484 1206 484 361 484 361 484 361 484 1206 484 361 484 361 484 361 484 1207 483 361 484 1206 484 361 484 71543 3435 1619 486 358 486 1204 486 359 486 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 360 484 1205 485 1205 485 360 485 1205 485 360 485 1205 485 1205 485 1205 485 1206 484 360 485 1205 485 360 485 360 485 360 485 1205 485 1206 484 360 485 1206 484 360 485 360 485 360 485 360 485 360 485 360 485 360 485 1206 484 360 485 360 485 360 485 1206 484 360 485 360 485 360 485 1205 485 360 485 1206 484 360 485 71542 3436 1619 486 358 487 1204 486 359 485 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 1205 485 360 485 1205 485 360 485 1206 484 1206 484 1206 484 1206 484 360 485 1206 484 360 485 360 485 361 484 1206 484 1206 484 361 484 1206 484 361 484 361 484 361 484 361 484 361 484 361 484 360 485 1206 484 361 484 361 484 361 484 1206 484 361 484 361 484 360 485 1206 484 361 484 1206 484 361 484 71542 3437 1618 487 358 486 1204 486 359 486 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 1206 484 360 485 1205 485 360 485 1206 484 1205 485 1206 484 1205 485 360 485 1205 485 360 485 360 485 360 485 1205 485 1205 485 360 485 1205 485 360 485 360 485 360 485 360 485 360 485 360 485 360 485 1205 485 360 485 360 485 360 485 1205 485 360 485 360 485 360 485 1205 485 360 485 1205 485 360 485 +# +# Thomson RC3000E02 +# +name: Power +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: 54 00 00 00 +# +name: Vol_up +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: F4 00 00 00 +# +name: Vol_dn +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: 74 00 00 00 +# +name: Ch_next +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: B4 00 00 00 +# +name: Ch_prev +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: 34 00 00 00 +# +name: Mute +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: FC 00 00 00 +# +# VOX Electronics 43ADS316B +# +name: Power +type: parsed +protocol: NEC +address: 40 00 00 00 +command: 0B 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 40 00 00 00 +command: 13 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 40 00 00 00 +command: 17 00 00 00 +# +name: Ch_next +type: parsed +protocol: NEC +address: 40 00 00 00 +command: 11 00 00 00 +# +name: Ch_prev +type: parsed +protocol: NEC +address: 40 00 00 00 +command: 10 00 00 00 diff --git a/applications/infrared/scenes/common/infrared_scene_universal_common.c b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c similarity index 87% rename from applications/infrared/scenes/common/infrared_scene_universal_common.c rename to applications/main/infrared/scenes/common/infrared_scene_universal_common.c index 57ac81168e2..4967d195664 100644 --- a/applications/infrared/scenes/common/infrared_scene_universal_common.c +++ b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c @@ -1,20 +1,21 @@ -#include "../../infrared_i.h" +#include "../../infrared_app_i.h" #include void infrared_scene_universal_common_item_callback(void* context, uint32_t index) { - Infrared* infrared = context; + InfraredApp* infrared = context; uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeButtonSelected, index); view_dispatcher_send_custom_event(infrared->view_dispatcher, event); } static void infrared_scene_universal_common_progress_back_callback(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeBackPressed, -1); view_dispatcher_send_custom_event(infrared->view_dispatcher, event); } -static void infrared_scene_universal_common_show_popup(Infrared* infrared, uint32_t record_count) { +static void + infrared_scene_universal_common_show_popup(InfraredApp* infrared, uint32_t record_count) { ViewStack* view_stack = infrared->view_stack; InfraredProgressView* progress = infrared->progress; infrared_progress_view_set_progress_total(progress, record_count); @@ -24,7 +25,7 @@ static void infrared_scene_universal_common_show_popup(Infrared* infrared, uint3 infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); } -static void infrared_scene_universal_common_hide_popup(Infrared* infrared) { +static void infrared_scene_universal_common_hide_popup(InfraredApp* infrared) { ViewStack* view_stack = infrared->view_stack; InfraredProgressView* progress = infrared->progress; view_stack_remove_view(view_stack, infrared_progress_view_get_view(progress)); @@ -32,12 +33,12 @@ static void infrared_scene_universal_common_hide_popup(Infrared* infrared) { } void infrared_scene_universal_common_on_enter(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; view_stack_add_view(infrared->view_stack, button_panel_get_view(infrared->button_panel)); } bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; + InfraredApp* infrared = context; SceneManager* scene_manager = infrared->scene_manager; InfraredBruteForce* brute_force = infrared->brute_force; bool consumed = false; @@ -70,7 +71,7 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e uint32_t record_count; if(infrared_brute_force_start( brute_force, infrared_custom_event_get_value(event.event), &record_count)) { - DOLPHIN_DEED(DolphinDeedIrBruteForce); + dolphin_deed(DolphinDeedIrSend); infrared_scene_universal_common_show_popup(infrared, record_count); } else { scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases); @@ -84,8 +85,9 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e } void infrared_scene_universal_common_on_exit(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; ButtonPanel* button_panel = infrared->button_panel; view_stack_remove_view(infrared->view_stack, button_panel_get_view(button_panel)); + infrared_brute_force_reset(infrared->brute_force); button_panel_reset(button_panel); } diff --git a/applications/infrared/scenes/common/infrared_scene_universal_common.h b/applications/main/infrared/scenes/common/infrared_scene_universal_common.h similarity index 100% rename from applications/infrared/scenes/common/infrared_scene_universal_common.h rename to applications/main/infrared/scenes/common/infrared_scene_universal_common.h diff --git a/applications/infrared/scenes/infrared_scene.c b/applications/main/infrared/scenes/infrared_scene.c similarity index 100% rename from applications/infrared/scenes/infrared_scene.c rename to applications/main/infrared/scenes/infrared_scene.c diff --git a/applications/infrared/scenes/infrared_scene.h b/applications/main/infrared/scenes/infrared_scene.h similarity index 100% rename from applications/infrared/scenes/infrared_scene.h rename to applications/main/infrared/scenes/infrared_scene.h diff --git a/applications/infrared/scenes/infrared_scene_ask_back.c b/applications/main/infrared/scenes/infrared_scene_ask_back.c similarity index 85% rename from applications/infrared/scenes/infrared_scene_ask_back.c rename to applications/main/infrared/scenes/infrared_scene_ask_back.c index 493458ade32..f97a38bb091 100644 --- a/applications/infrared/scenes/infrared_scene_ask_back.c +++ b/applications/main/infrared/scenes/infrared_scene_ask_back.c @@ -1,22 +1,22 @@ -#include "../infrared_i.h" +#include "../infrared_app_i.h" static void infrared_scene_dialog_result_callback(DialogExResult result, void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; view_dispatcher_send_custom_event(infrared->view_dispatcher, result); } void infrared_scene_ask_back_on_enter(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = 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); @@ -28,7 +28,7 @@ void infrared_scene_ask_back_on_enter(void* context) { } bool infrared_scene_ask_back_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; + InfraredApp* infrared = context; SceneManager* scene_manager = infrared->scene_manager; bool consumed = false; @@ -54,6 +54,6 @@ bool infrared_scene_ask_back_on_event(void* context, SceneManagerEvent event) { } void infrared_scene_ask_back_on_exit(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; dialog_ex_reset(infrared->dialog_ex); } diff --git a/applications/infrared/scenes/infrared_scene_ask_retry.c b/applications/main/infrared/scenes/infrared_scene_ask_retry.c similarity index 80% rename from applications/infrared/scenes/infrared_scene_ask_retry.c rename to applications/main/infrared/scenes/infrared_scene_ask_retry.c index c87d9e6d3f9..365ed5c812f 100644 --- a/applications/infrared/scenes/infrared_scene_ask_retry.c +++ b/applications/main/infrared/scenes/infrared_scene_ask_retry.c @@ -1,17 +1,17 @@ -#include "../infrared_i.h" +#include "../infrared_app_i.h" static void infrared_scene_dialog_result_callback(DialogExResult result, void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; view_dispatcher_send_custom_event(infrared->view_dispatcher, result); } void infrared_scene_ask_retry_on_enter(void* context) { - Infrared* infrared = context; + InfraredApp* 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); @@ -23,7 +23,7 @@ void infrared_scene_ask_retry_on_enter(void* context) { } bool infrared_scene_ask_retry_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; + InfraredApp* infrared = context; SceneManager* scene_manager = infrared->scene_manager; bool consumed = false; @@ -43,6 +43,6 @@ bool infrared_scene_ask_retry_on_event(void* context, SceneManagerEvent event) { } void infrared_scene_ask_retry_on_exit(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; dialog_ex_reset(infrared->dialog_ex); } diff --git a/applications/infrared/scenes/infrared_scene_config.h b/applications/main/infrared/scenes/infrared_scene_config.h similarity index 81% rename from applications/infrared/scenes/infrared_scene_config.h rename to applications/main/infrared/scenes/infrared_scene_config.h index 26a92056deb..27ef2f3b179 100644 --- a/applications/infrared/scenes/infrared_scene_config.h +++ b/applications/main/infrared/scenes/infrared_scene_config.h @@ -7,6 +7,7 @@ ADD_SCENE(infrared, edit_delete_done, EditDeleteDone) ADD_SCENE(infrared, edit_button_select, EditButtonSelect) ADD_SCENE(infrared, edit_rename, EditRename) ADD_SCENE(infrared, edit_rename_done, EditRenameDone) +ADD_SCENE(infrared, edit_move, EditMove) ADD_SCENE(infrared, learn, Learn) ADD_SCENE(infrared, learn_done, LearnDone) ADD_SCENE(infrared, learn_enter_name, LearnEnterName) @@ -15,6 +16,9 @@ ADD_SCENE(infrared, remote, Remote) ADD_SCENE(infrared, remote_list, RemoteList) ADD_SCENE(infrared, universal, Universal) ADD_SCENE(infrared, universal_tv, UniversalTV) +ADD_SCENE(infrared, universal_ac, UniversalAC) +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, rpc, Rpc) diff --git a/applications/infrared/scenes/infrared_scene_debug.c b/applications/main/infrared/scenes/infrared_scene_debug.c similarity index 84% rename from applications/infrared/scenes/infrared_scene_debug.c rename to applications/main/infrared/scenes/infrared_scene_debug.c index dd0609b56c1..adffbc83ace 100644 --- a/applications/infrared/scenes/infrared_scene_debug.c +++ b/applications/main/infrared/scenes/infrared_scene_debug.c @@ -1,7 +1,7 @@ -#include "../infrared_i.h" +#include "../infrared_app_i.h" void infrared_scene_debug_on_enter(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; InfraredWorker* worker = infrared->worker; infrared_worker_rx_set_received_signal_callback( @@ -14,26 +14,26 @@ void infrared_scene_debug_on_enter(void* context) { } bool infrared_scene_debug_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; + InfraredApp* infrared = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == InfraredCustomEventTypeSignalReceived) { InfraredDebugView* debug_view = infrared->debug_view; - InfraredSignal* signal = infrared->received_signal; + InfraredSignal* signal = infrared->current_signal; if(infrared_signal_is_raw(signal)) { - InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); + const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); infrared_debug_view_set_text(debug_view, "RAW\n%d samples\n", raw->timings_size); - printf("RAW, %d samples:\r\n", raw->timings_size); + printf("RAW, %zu samples:\r\n", raw->timings_size); for(size_t i = 0; i < raw->timings_size; ++i) { printf("%lu ", raw->timings[i]); } printf("\r\n"); } else { - InfraredMessage* message = infrared_signal_get_message(signal); + const InfraredMessage* message = infrared_signal_get_message(signal); infrared_debug_view_set_text( debug_view, "%s\nA:0x%0*lX\nC:0x%0*lX\n%s\n", @@ -61,7 +61,7 @@ bool infrared_scene_debug_on_event(void* context, SceneManagerEvent event) { } void infrared_scene_debug_on_exit(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; InfraredWorker* worker = infrared->worker; infrared_worker_rx_stop(worker); infrared_worker_rx_enable_blink_on_receiving(worker, false); diff --git a/applications/infrared/scenes/infrared_scene_edit.c b/applications/main/infrared/scenes/infrared_scene_edit.c similarity index 87% rename from applications/infrared/scenes/infrared_scene_edit.c rename to applications/main/infrared/scenes/infrared_scene_edit.c index 360ed49b351..c22e9539631 100644 --- a/applications/infrared/scenes/infrared_scene_edit.c +++ b/applications/main/infrared/scenes/infrared_scene_edit.c @@ -1,20 +1,21 @@ -#include "../infrared_i.h" +#include "../infrared_app_i.h" typedef enum { SubmenuIndexAddButton, SubmenuIndexRenameButton, + SubmenuIndexMoveButton, SubmenuIndexDeleteButton, SubmenuIndexRenameRemote, SubmenuIndexDeleteRemote, } SubmenuIndex; static void infrared_scene_edit_submenu_callback(void* context, uint32_t index) { - Infrared* infrared = context; + InfraredApp* infrared = context; view_dispatcher_send_custom_event(infrared->view_dispatcher, index); } void infrared_scene_edit_on_enter(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; Submenu* submenu = infrared->submenu; SceneManager* scene_manager = infrared->scene_manager; @@ -30,6 +31,12 @@ void infrared_scene_edit_on_enter(void* context) { SubmenuIndexRenameButton, infrared_scene_edit_submenu_callback, context); + submenu_add_item( + submenu, + "Move Button", + SubmenuIndexMoveButton, + infrared_scene_edit_submenu_callback, + context); submenu_add_item( submenu, "Delete Button", @@ -57,7 +64,7 @@ void infrared_scene_edit_on_enter(void* context) { } bool infrared_scene_edit_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; + InfraredApp* infrared = context; SceneManager* scene_manager = infrared->scene_manager; bool consumed = false; @@ -74,6 +81,9 @@ bool infrared_scene_edit_on_event(void* context, SceneManagerEvent event) { infrared->app_state.edit_mode = InfraredEditModeRename; scene_manager_next_scene(scene_manager, InfraredSceneEditButtonSelect); consumed = true; + } else if(submenu_index == SubmenuIndexMoveButton) { + scene_manager_next_scene(scene_manager, InfraredSceneEditMove); + consumed = true; } else if(submenu_index == SubmenuIndexDeleteButton) { infrared->app_state.edit_target = InfraredEditTargetButton; infrared->app_state.edit_mode = InfraredEditModeDelete; @@ -96,6 +106,6 @@ bool infrared_scene_edit_on_event(void* context, SceneManagerEvent event) { } void infrared_scene_edit_on_exit(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; submenu_reset(infrared->submenu); } diff --git a/applications/infrared/scenes/infrared_scene_edit_button_select.c b/applications/main/infrared/scenes/infrared_scene_edit_button_select.c similarity index 83% rename from applications/infrared/scenes/infrared_scene_edit_button_select.c rename to applications/main/infrared/scenes/infrared_scene_edit_button_select.c index a7f8a2bf7aa..3fd59b5792b 100644 --- a/applications/infrared/scenes/infrared_scene_edit_button_select.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_button_select.c @@ -1,12 +1,12 @@ -#include "../infrared_i.h" +#include "../infrared_app_i.h" static void infrared_scene_edit_button_select_submenu_callback(void* context, uint32_t index) { - Infrared* infrared = context; + InfraredApp* infrared = context; view_dispatcher_send_custom_event(infrared->view_dispatcher, index); } void infrared_scene_edit_button_select_on_enter(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; Submenu* submenu = infrared->submenu; InfraredRemote* remote = infrared->remote; InfraredAppState* app_state = &infrared->app_state; @@ -16,12 +16,11 @@ void infrared_scene_edit_button_select_on_enter(void* context) { "Delete Button:"; submenu_set_header(submenu, header); - const size_t button_count = infrared_remote_get_button_count(remote); + const size_t button_count = infrared_remote_get_signal_count(remote); for(size_t i = 0; i < button_count; ++i) { - InfraredRemoteButton* button = infrared_remote_get_button(remote, i); submenu_add_item( submenu, - infrared_remote_button_get_name(button), + infrared_remote_get_signal_name(remote, i), i, infrared_scene_edit_button_select_submenu_callback, context); @@ -36,7 +35,7 @@ void infrared_scene_edit_button_select_on_enter(void* context) { } bool infrared_scene_edit_button_select_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; + InfraredApp* infrared = context; InfraredAppState* app_state = &infrared->app_state; SceneManager* scene_manager = infrared->scene_manager; bool consumed = false; @@ -49,7 +48,7 @@ bool infrared_scene_edit_button_select_on_event(void* context, SceneManagerEvent } else if(edit_mode == InfraredEditModeDelete) { scene_manager_next_scene(scene_manager, InfraredSceneEditDelete); } else { - furi_assert(0); + furi_crash(); } consumed = true; } @@ -58,6 +57,6 @@ bool infrared_scene_edit_button_select_on_event(void* context, SceneManagerEvent } void infrared_scene_edit_button_select_on_exit(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; submenu_reset(infrared->submenu); } diff --git a/applications/main/infrared/scenes/infrared_scene_edit_delete.c b/applications/main/infrared/scenes/infrared_scene_edit_delete.c new file mode 100644 index 00000000000..0cb88efdb66 --- /dev/null +++ b/applications/main/infrared/scenes/infrared_scene_edit_delete.c @@ -0,0 +1,128 @@ +#include "../infrared_app_i.h" + +static void + infrared_scene_edit_delete_dialog_result_callback(DialogExResult result, void* context) { + InfraredApp* infrared = context; + view_dispatcher_send_custom_event(infrared->view_dispatcher, result); +} + +void infrared_scene_edit_delete_on_enter(void* context) { + InfraredApp* infrared = context; + DialogEx* dialog_ex = infrared->dialog_ex; + InfraredRemote* remote = infrared->remote; + + const InfraredEditTarget edit_target = infrared->app_state.edit_target; + if(edit_target == InfraredEditTargetButton) { + dialog_ex_set_header(dialog_ex, "Delete Button?", 64, 0, AlignCenter, AlignTop); + + const int32_t current_button_index = infrared->app_state.current_button_index; + furi_check(current_button_index != InfraredButtonIndexNone); + + if(!infrared_remote_load_signal(remote, infrared->current_signal, current_button_index)) { + infrared_show_error_message( + infrared, + "Failed to load\n\"%s\"", + infrared_remote_get_signal_name(remote, current_button_index)); + scene_manager_previous_scene(infrared->scene_manager); + return; + } + + if(infrared_signal_is_raw(infrared->current_signal)) { + const InfraredRawSignal* raw = + infrared_signal_get_raw_signal(infrared->current_signal); + infrared_text_store_set( + infrared, + 0, + "%s\nRAW\n%zu samples", + infrared_remote_get_signal_name(remote, current_button_index), + raw->timings_size); + + } else { + const InfraredMessage* message = infrared_signal_get_message(infrared->current_signal); + infrared_text_store_set( + infrared, + 0, + "%s\n%s\nA=0x%0*lX C=0x%0*lX", + infrared_remote_get_signal_name(remote, current_button_index), + infrared_get_protocol_name(message->protocol), + ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4), + message->address, + ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), + message->command); + } + + } else if(edit_target == InfraredEditTargetRemote) { + dialog_ex_set_header(dialog_ex, "Delete Remote?", 64, 0, AlignCenter, AlignTop); + infrared_text_store_set( + infrared, + 0, + "%s\n with %zu buttons", + infrared_remote_get_name(remote), + infrared_remote_get_signal_count(remote)); + } else { + furi_crash(); + } + + dialog_ex_set_text(dialog_ex, infrared->text_store[0], 64, 31, AlignCenter, AlignCenter); + dialog_ex_set_icon(dialog_ex, 0, 0, NULL); + dialog_ex_set_left_button_text(dialog_ex, "Cancel"); + dialog_ex_set_right_button_text(dialog_ex, "Delete"); + dialog_ex_set_result_callback(dialog_ex, infrared_scene_edit_delete_dialog_result_callback); + dialog_ex_set_context(dialog_ex, context); + + view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationHorizontal); + view_stack_add_view(infrared->view_stack, dialog_ex_get_view(infrared->dialog_ex)); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); +} + +bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event) { + InfraredApp* infrared = context; + SceneManager* scene_manager = infrared->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultLeft) { + scene_manager_previous_scene(scene_manager); + consumed = true; + } else if(event.event == DialogExResultRight) { + bool success = false; + InfraredRemote* remote = infrared->remote; + InfraredAppState* app_state = &infrared->app_state; + const InfraredEditTarget edit_target = app_state->edit_target; + + if(edit_target == InfraredEditTargetButton) { + furi_assert(app_state->current_button_index != InfraredButtonIndexNone); + infrared_show_loading_popup(infrared, true); + success = infrared_remote_delete_signal(remote, app_state->current_button_index); + infrared_show_loading_popup(infrared, false); + app_state->current_button_index = InfraredButtonIndexNone; + } else if(edit_target == InfraredEditTargetRemote) { + success = infrared_remote_remove(remote); + app_state->current_button_index = InfraredButtonIndexNone; + } else { + furi_crash(); + } + + if(success) { + scene_manager_next_scene(scene_manager, InfraredSceneEditDeleteDone); + } else { + infrared_show_error_message( + infrared, + "Failed to\ndelete %s", + edit_target == InfraredEditTargetButton ? "button" : "file"); + const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; + scene_manager_search_and_switch_to_previous_scene_one_of( + scene_manager, possible_scenes, COUNT_OF(possible_scenes)); + } + consumed = true; + } + } + + return consumed; +} + +void infrared_scene_edit_delete_on_exit(void* context) { + InfraredApp* infrared = context; + view_stack_remove_view(infrared->view_stack, dialog_ex_get_view(infrared->dialog_ex)); +} diff --git a/applications/infrared/scenes/infrared_scene_edit_delete_done.c b/applications/main/infrared/scenes/infrared_scene_edit_delete_done.c similarity index 90% rename from applications/infrared/scenes/infrared_scene_edit_delete_done.c rename to applications/main/infrared/scenes/infrared_scene_edit_delete_done.c index 49a299d2aa6..9205db4c4e7 100644 --- a/applications/infrared/scenes/infrared_scene_edit_delete_done.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_delete_done.c @@ -1,7 +1,7 @@ -#include "../infrared_i.h" +#include "../infrared_app_i.h" void infrared_scene_edit_delete_done_on_enter(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; Popup* popup = infrared->popup; popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); @@ -16,7 +16,7 @@ void infrared_scene_edit_delete_done_on_enter(void* context) { } bool infrared_scene_edit_delete_done_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; + InfraredApp* infrared = context; SceneManager* scene_manager = infrared->scene_manager; bool consumed = false; @@ -33,7 +33,7 @@ bool infrared_scene_edit_delete_done_on_event(void* context, SceneManagerEvent e view_dispatcher_stop(infrared->view_dispatcher); } } else { - furi_assert(0); + furi_crash(); } consumed = true; } @@ -43,6 +43,6 @@ bool infrared_scene_edit_delete_done_on_event(void* context, SceneManagerEvent e } void infrared_scene_edit_delete_done_on_exit(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; UNUSED(infrared); } diff --git a/applications/main/infrared/scenes/infrared_scene_edit_move.c b/applications/main/infrared/scenes/infrared_scene_edit_move.c new file mode 100644 index 00000000000..4959a831095 --- /dev/null +++ b/applications/main/infrared/scenes/infrared_scene_edit_move.c @@ -0,0 +1,69 @@ +#include "../infrared_app_i.h" + +static void infrared_scene_edit_move_button_callback( + uint32_t index_old, + uint32_t index_new, + void* context) { + InfraredApp* infrared = context; + furi_assert(infrared); + + infrared->app_state.prev_button_index = index_old; + infrared->app_state.current_button_index = index_new; + + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeButtonSelected); +} + +void infrared_scene_edit_move_on_enter(void* context) { + InfraredApp* infrared = context; + InfraredRemote* remote = infrared->remote; + + for(size_t i = 0; i < infrared_remote_get_signal_count(remote); ++i) { + infrared_move_view_add_item( + infrared->move_view, infrared_remote_get_signal_name(remote, i)); + } + + infrared_move_view_set_callback( + infrared->move_view, infrared_scene_edit_move_button_callback, infrared); + + view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationHorizontal); + view_stack_add_view(infrared->view_stack, infrared_move_view_get_view(infrared->move_view)); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); +} + +bool infrared_scene_edit_move_on_event(void* context, SceneManagerEvent event) { + InfraredApp* infrared = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == InfraredCustomEventTypeButtonSelected) { + infrared_show_loading_popup(infrared, true); + const bool button_moved = infrared_remote_move_signal( + infrared->remote, + infrared->app_state.prev_button_index, + infrared->app_state.current_button_index); + infrared_show_loading_popup(infrared, false); + + if(!button_moved) { + infrared_show_error_message( + infrared, + "Failed to move\n\"%s\"", + infrared_remote_get_signal_name( + infrared->remote, infrared->app_state.current_button_index)); + scene_manager_search_and_switch_to_previous_scene( + infrared->scene_manager, InfraredSceneRemoteList); + } + + consumed = true; + } + } + + return consumed; +} + +void infrared_scene_edit_move_on_exit(void* context) { + InfraredApp* infrared = context; + view_stack_remove_view(infrared->view_stack, infrared_move_view_get_view(infrared->move_view)); + infrared_move_view_reset(infrared->move_view); +} diff --git a/applications/main/infrared/scenes/infrared_scene_edit_rename.c b/applications/main/infrared/scenes/infrared_scene_edit_rename.c new file mode 100644 index 00000000000..178690926d4 --- /dev/null +++ b/applications/main/infrared/scenes/infrared_scene_edit_rename.c @@ -0,0 +1,116 @@ +#include "../infrared_app_i.h" + +#include +#include + +void infrared_scene_edit_rename_on_enter(void* context) { + InfraredApp* infrared = context; + InfraredRemote* remote = infrared->remote; + TextInput* text_input = infrared->text_input; + size_t enter_name_length = 0; + + const InfraredEditTarget edit_target = infrared->app_state.edit_target; + if(edit_target == InfraredEditTargetButton) { + text_input_set_header_text(text_input, "Name the button"); + + const int32_t current_button_index = infrared->app_state.current_button_index; + furi_check(current_button_index != InfraredButtonIndexNone); + + enter_name_length = INFRARED_MAX_BUTTON_NAME_LENGTH; + strncpy( + infrared->text_store[0], + infrared_remote_get_signal_name(remote, current_button_index), + enter_name_length); + + } else if(edit_target == InfraredEditTargetRemote) { + text_input_set_header_text(text_input, "Name the remote"); + enter_name_length = INFRARED_MAX_REMOTE_NAME_LENGTH; + strncpy(infrared->text_store[0], infrared_remote_get_name(remote), enter_name_length); + + FuriString* folder_path; + folder_path = furi_string_alloc(); + + if(furi_string_end_with(infrared->file_path, INFRARED_APP_EXTENSION)) { + path_extract_dirname(furi_string_get_cstr(infrared->file_path), folder_path); + } + + ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( + furi_string_get_cstr(folder_path), + INFRARED_APP_EXTENSION, + infrared_remote_get_name(remote)); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + furi_string_free(folder_path); + } else { + furi_crash(); + } + + text_input_set_result_callback( + text_input, + infrared_text_input_callback, + context, + infrared->text_store[0], + enter_name_length, + false); + + view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationHorizontal); + view_stack_add_view(infrared->view_stack, text_input_get_view(infrared->text_input)); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); +} + +bool infrared_scene_edit_rename_on_event(void* context, SceneManagerEvent event) { + InfraredApp* infrared = context; + InfraredRemote* remote = infrared->remote; + SceneManager* scene_manager = infrared->scene_manager; + InfraredAppState* app_state = &infrared->app_state; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == InfraredCustomEventTypeTextEditDone) { + bool success = false; + const InfraredEditTarget edit_target = app_state->edit_target; + if(edit_target == InfraredEditTargetButton) { + const int32_t current_button_index = app_state->current_button_index; + furi_assert(current_button_index != InfraredButtonIndexNone); + infrared_show_loading_popup(infrared, true); + success = infrared_remote_rename_signal( + remote, current_button_index, infrared->text_store[0]); + infrared_show_loading_popup(infrared, false); + app_state->current_button_index = InfraredButtonIndexNone; + } else if(edit_target == InfraredEditTargetRemote) { + success = infrared_rename_current_remote(infrared, infrared->text_store[0]); + } else { + furi_crash(); + } + + if(success) { + scene_manager_next_scene(scene_manager, InfraredSceneEditRenameDone); + } else { + infrared_show_error_message( + infrared, + "Failed to\nrename %s", + edit_target == InfraredEditTargetButton ? "button" : "file"); + scene_manager_search_and_switch_to_previous_scene( + scene_manager, InfraredSceneRemoteList); + } + consumed = true; + } + } + + return consumed; +} + +void infrared_scene_edit_rename_on_exit(void* context) { + InfraredApp* infrared = context; + TextInput* text_input = infrared->text_input; + + view_stack_remove_view(infrared->view_stack, text_input_get_view(text_input)); + + void* validator_context = text_input_get_validator_callback_context(text_input); + text_input_set_validator(text_input, NULL, NULL); + + if(validator_context) { + validator_is_file_free((ValidatorIsFile*)validator_context); + } +} diff --git a/applications/infrared/scenes/infrared_scene_edit_rename_done.c b/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c similarity index 88% rename from applications/infrared/scenes/infrared_scene_edit_rename_done.c rename to applications/main/infrared/scenes/infrared_scene_edit_rename_done.c index 6c7096e17d0..35f51598941 100644 --- a/applications/infrared/scenes/infrared_scene_edit_rename_done.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c @@ -1,7 +1,7 @@ -#include "../infrared_i.h" +#include "../infrared_app_i.h" void infrared_scene_edit_rename_done_on_enter(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; Popup* popup = infrared->popup; popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); @@ -16,7 +16,7 @@ void infrared_scene_edit_rename_done_on_enter(void* context) { } bool infrared_scene_edit_rename_done_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; + InfraredApp* infrared = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { @@ -33,6 +33,6 @@ bool infrared_scene_edit_rename_done_on_event(void* context, SceneManagerEvent e } void infrared_scene_edit_rename_done_on_exit(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; UNUSED(infrared); } diff --git a/applications/infrared/scenes/infrared_scene_error_databases.c b/applications/main/infrared/scenes/infrared_scene_error_databases.c similarity index 89% rename from applications/infrared/scenes/infrared_scene_error_databases.c rename to applications/main/infrared/scenes/infrared_scene_error_databases.c index 4ed4dee583b..f51f83f25f3 100644 --- a/applications/infrared/scenes/infrared_scene_error_databases.c +++ b/applications/main/infrared/scenes/infrared_scene_error_databases.c @@ -1,7 +1,7 @@ -#include "../infrared_i.h" +#include "../infrared_app_i.h" void infrared_scene_error_databases_on_enter(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; Popup* popup = infrared->popup; popup_set_icon(popup, 5, 11, &I_SDQuestion_35x43); @@ -16,7 +16,7 @@ void infrared_scene_error_databases_on_enter(void* context) { } bool infrared_scene_error_databases_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; + InfraredApp* infrared = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { @@ -31,7 +31,7 @@ bool infrared_scene_error_databases_on_event(void* context, SceneManagerEvent ev } void infrared_scene_error_databases_on_exit(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; popup_reset(infrared->popup); infrared_play_notification_message(infrared, InfraredNotificationMessageYellowOff); } diff --git a/applications/infrared/scenes/infrared_scene_learn.c b/applications/main/infrared/scenes/infrared_scene_learn.c similarity index 83% rename from applications/infrared/scenes/infrared_scene_learn.c rename to applications/main/infrared/scenes/infrared_scene_learn.c index 0edb74ca2b4..bcd0a2cd0f8 100644 --- a/applications/infrared/scenes/infrared_scene_learn.c +++ b/applications/main/infrared/scenes/infrared_scene_learn.c @@ -1,7 +1,8 @@ -#include "../infrared_i.h" +#include "../infrared_app_i.h" +#include void infrared_scene_learn_on_enter(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; Popup* popup = infrared->popup; InfraredWorker* worker = infrared->worker; @@ -20,14 +21,14 @@ void infrared_scene_learn_on_enter(void* context) { } bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; + InfraredApp* infrared = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == InfraredCustomEventTypeSignalReceived) { - infrared_worker_rx_set_received_signal_callback(infrared->worker, NULL, NULL); infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess); scene_manager_next_scene(infrared->scene_manager, InfraredSceneLearnSuccess); + dolphin_deed(DolphinDeedIrLearnSuccess); consumed = true; } } @@ -36,8 +37,9 @@ bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) { } void infrared_scene_learn_on_exit(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; Popup* popup = infrared->popup; + infrared_worker_rx_set_received_signal_callback(infrared->worker, NULL, NULL); infrared_worker_rx_stop(infrared->worker); infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop); popup_set_icon(popup, 0, 0, NULL); diff --git a/applications/infrared/scenes/infrared_scene_learn_done.c b/applications/main/infrared/scenes/infrared_scene_learn_done.c similarity index 87% rename from applications/infrared/scenes/infrared_scene_learn_done.c rename to applications/main/infrared/scenes/infrared_scene_learn_done.c index 7d35717155e..b4eb38331d0 100644 --- a/applications/infrared/scenes/infrared_scene_learn_done.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_done.c @@ -1,13 +1,10 @@ -#include "../infrared_i.h" - -#include +#include "../infrared_app_i.h" void infrared_scene_learn_done_on_enter(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; Popup* popup = infrared->popup; popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - DOLPHIN_DEED(DolphinDeedIrSave); if(infrared->app_state.is_learning_new_remote) { popup_set_header(popup, "New remote\ncreated!", 0, 0, AlignLeft, AlignTop); @@ -24,7 +21,7 @@ void infrared_scene_learn_done_on_enter(void* context) { } bool infrared_scene_learn_done_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; + InfraredApp* infrared = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { @@ -41,7 +38,7 @@ bool infrared_scene_learn_done_on_event(void* context, SceneManagerEvent event) } void infrared_scene_learn_done_on_exit(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; infrared->app_state.is_learning_new_remote = false; popup_set_header(infrared->popup, NULL, 0, 0, AlignLeft, AlignTop); } diff --git a/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c b/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c new file mode 100644 index 00000000000..be46a869168 --- /dev/null +++ b/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c @@ -0,0 +1,71 @@ +#include "../infrared_app_i.h" +#include + +void infrared_scene_learn_enter_name_on_enter(void* context) { + InfraredApp* infrared = context; + TextInput* text_input = infrared->text_input; + InfraredSignal* signal = infrared->current_signal; + + if(infrared_signal_is_raw(signal)) { + const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); + infrared_text_store_set(infrared, 0, "RAW_%zu", raw->timings_size); + } else { + const InfraredMessage* message = infrared_signal_get_message(signal); + infrared_text_store_set( + infrared, + 0, + "%.4s_%0*lX", + infrared_get_protocol_name(message->protocol), + ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), + message->command); + } + + text_input_set_header_text(text_input, "Name the button"); + text_input_set_result_callback( + text_input, + infrared_text_input_callback, + context, + infrared->text_store[0], + INFRARED_MAX_BUTTON_NAME_LENGTH, + true); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewTextInput); +} + +bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent event) { + InfraredApp* infrared = context; + InfraredSignal* signal = infrared->current_signal; + SceneManager* scene_manager = infrared->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == InfraredCustomEventTypeTextEditDone) { + const char* signal_name = infrared->text_store[0]; + const bool success = + infrared->app_state.is_learning_new_remote ? + infrared_add_remote_with_button(infrared, signal_name, signal) : + infrared_remote_append_signal(infrared->remote, signal, signal_name); + + if(success) { + scene_manager_next_scene(scene_manager, InfraredSceneLearnDone); + dolphin_deed(DolphinDeedIrSave); + } else { + infrared_show_error_message( + infrared, + "Failed to\n%s", + infrared->app_state.is_learning_new_remote ? "create file" : "add signal"); + const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; + scene_manager_search_and_switch_to_previous_scene_one_of( + scene_manager, possible_scenes, COUNT_OF(possible_scenes)); + } + consumed = true; + } + } + + return consumed; +} + +void infrared_scene_learn_enter_name_on_exit(void* context) { + InfraredApp* infrared = context; + UNUSED(infrared); +} diff --git a/applications/infrared/scenes/infrared_scene_learn_success.c b/applications/main/infrared/scenes/infrared_scene_learn_success.c similarity index 86% rename from applications/infrared/scenes/infrared_scene_learn_success.c rename to applications/main/infrared/scenes/infrared_scene_learn_success.c index 466627144a0..deb54bb5ef2 100644 --- a/applications/infrared/scenes/infrared_scene_learn_success.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_success.c @@ -1,29 +1,26 @@ -#include "../infrared_i.h" - -#include +#include "../infrared_app_i.h" static void infrared_scene_learn_success_dialog_result_callback(DialogExResult result, void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; view_dispatcher_send_custom_event(infrared->view_dispatcher, result); } void infrared_scene_learn_success_on_enter(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; DialogEx* dialog_ex = infrared->dialog_ex; - InfraredSignal* signal = infrared->received_signal; + InfraredSignal* signal = infrared->current_signal; - DOLPHIN_DEED(DolphinDeedIrLearnSuccess); infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOn); if(infrared_signal_is_raw(signal)) { - InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); + const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); dialog_ex_set_header(dialog_ex, "Unknown", 95, 10, AlignCenter, AlignCenter); - infrared_text_store_set(infrared, 0, "%d samples", raw->timings_size); + infrared_text_store_set(infrared, 0, "%zu samples", raw->timings_size); dialog_ex_set_text(dialog_ex, infrared->text_store[0], 75, 23, AlignLeft, AlignTop); } else { - InfraredMessage* message = infrared_signal_get_message(signal); + const InfraredMessage* message = infrared_signal_get_message(signal); uint8_t addr_digits = ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4); uint8_t cmd_digits = @@ -59,7 +56,7 @@ void infrared_scene_learn_success_on_enter(void* context) { } bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; + InfraredApp* infrared = context; SceneManager* scene_manager = infrared->scene_manager; const bool is_transmitter_idle = !infrared->app_state.is_transmitting; bool consumed = false; @@ -87,7 +84,7 @@ bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent even consumed = true; } else if(event.event == DialogExPressCenter) { infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff); - infrared_tx_start_received(infrared); + infrared_tx_start(infrared); consumed = true; } else if(event.event == DialogExReleaseCenter) { infrared_tx_stop(infrared); @@ -99,7 +96,7 @@ bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent even } void infrared_scene_learn_success_on_exit(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; dialog_ex_reset(infrared->dialog_ex); infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff); } diff --git a/applications/infrared/scenes/infrared_scene_remote.c b/applications/main/infrared/scenes/infrared_scene_remote.c similarity index 88% rename from applications/infrared/scenes/infrared_scene_remote.c rename to applications/main/infrared/scenes/infrared_scene_remote.c index c1f5b6627ce..6c1d1ec4e39 100644 --- a/applications/infrared/scenes/infrared_scene_remote.c +++ b/applications/main/infrared/scenes/infrared_scene_remote.c @@ -1,14 +1,14 @@ -#include "../infrared_i.h" +#include "../infrared_app_i.h" typedef enum { - ButtonIndexPlus = -2, + ButtonIndexLearn = -2, ButtonIndexEdit = -1, ButtonIndexNA = 0, } ButtonIndex; static void infrared_scene_remote_button_menu_callback(void* context, int32_t index, InputType type) { - Infrared* infrared = context; + InfraredApp* infrared = context; uint16_t custom_type; if(type == InputTypePress) { @@ -26,17 +26,15 @@ static void } void infrared_scene_remote_on_enter(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; InfraredRemote* remote = infrared->remote; ButtonMenu* button_menu = infrared->button_menu; SceneManager* scene_manager = infrared->scene_manager; - size_t button_count = infrared_remote_get_button_count(remote); - for(size_t i = 0; i < button_count; ++i) { - InfraredRemoteButton* button = infrared_remote_get_button(remote, i); + for(size_t i = 0; i < infrared_remote_get_signal_count(remote); ++i) { button_menu_add_item( button_menu, - infrared_remote_button_get_name(button), + infrared_remote_get_signal_name(remote, i), i, infrared_scene_remote_button_menu_callback, ButtonMenuItemTypeCommon, @@ -46,7 +44,7 @@ void infrared_scene_remote_on_enter(void* context) { button_menu_add_item( button_menu, "+", - ButtonIndexPlus, + ButtonIndexLearn, infrared_scene_remote_button_menu_callback, ButtonMenuItemTypeControl, context); @@ -68,7 +66,7 @@ void infrared_scene_remote_on_enter(void* context) { } bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; + InfraredApp* infrared = context; SceneManager* scene_manager = infrared->scene_manager; const bool is_transmitter_idle = !infrared->app_state.is_transmitting; bool consumed = false; @@ -97,7 +95,7 @@ bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) { if(is_transmitter_idle) { scene_manager_set_scene_state( scene_manager, InfraredSceneRemote, (unsigned)button_index); - if(button_index == ButtonIndexPlus) { + if(button_index == ButtonIndexLearn) { infrared->app_state.is_learning_new_remote = false; scene_manager_next_scene(scene_manager, InfraredSceneLearn); consumed = true; @@ -116,6 +114,6 @@ bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) { } void infrared_scene_remote_on_exit(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; button_menu_reset(infrared->button_menu); } diff --git a/applications/main/infrared/scenes/infrared_scene_remote_list.c b/applications/main/infrared/scenes/infrared_scene_remote_list.c new file mode 100644 index 00000000000..2276e270a0c --- /dev/null +++ b/applications/main/infrared/scenes/infrared_scene_remote_list.c @@ -0,0 +1,44 @@ +#include "../infrared_app_i.h" + +void infrared_scene_remote_list_on_enter(void* context) { + InfraredApp* infrared = context; + SceneManager* scene_manager = infrared->scene_manager; + ViewDispatcher* view_dispatcher = infrared->view_dispatcher; + + view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); + view_dispatcher_switch_to_view(view_dispatcher, InfraredViewStack); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, INFRARED_APP_EXTENSION, &I_ir_10px); + browser_options.base_path = INFRARED_APP_FOLDER; + + while(dialog_file_browser_show( + infrared->dialogs, infrared->file_path, infrared->file_path, &browser_options)) { + const char* file_path = furi_string_get_cstr(infrared->file_path); + + infrared_show_loading_popup(infrared, true); + const bool remote_loaded = infrared_remote_load(infrared->remote, file_path); + infrared_show_loading_popup(infrared, false); + + if(remote_loaded) { + scene_manager_next_scene(scene_manager, InfraredSceneRemote); + return; + } else { + infrared_show_error_message(infrared, "Failed to load\n\"%s\"", file_path); + } + } + + scene_manager_previous_scene(scene_manager); +} + +bool infrared_scene_remote_list_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + bool consumed = false; + + return consumed; +} + +void infrared_scene_remote_list_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/main/infrared/scenes/infrared_scene_rpc.c b/applications/main/infrared/scenes/infrared_scene_rpc.c new file mode 100644 index 00000000000..f3408fba4dd --- /dev/null +++ b/applications/main/infrared/scenes/infrared_scene_rpc.c @@ -0,0 +1,116 @@ +#include "../infrared_app_i.h" +#include + +#define TAG "InfraredApp" + +typedef enum { + InfraredRpcStateIdle, + InfraredRpcStateLoaded, + InfraredRpcStateSending, +} InfraredRpcState; + +void infrared_scene_rpc_on_enter(void* context) { + InfraredApp* infrared = context; + Popup* popup = infrared->popup; + + popup_set_header(popup, "Infrared", 89, 42, AlignCenter, AlignBottom); + popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop); + + popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61); + + popup_set_context(popup, context); + popup_set_callback(popup, infrared_popup_closed_callback); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup); + + scene_manager_set_scene_state(infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateIdle); + + notification_message(infrared->notifications, &sequence_display_backlight_on); +} + +bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { + InfraredApp* infrared = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + InfraredRpcState state = + scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneRpc); + if(event.event == InfraredCustomEventTypeBackPressed) { + view_dispatcher_stop(infrared->view_dispatcher); + } else if(event.event == InfraredCustomEventTypePopupClosed) { + view_dispatcher_stop(infrared->view_dispatcher); + } else if(event.event == InfraredCustomEventTypeRpcLoadFile) { + bool result = false; + if(state == InfraredRpcStateIdle) { + result = infrared_remote_load( + infrared->remote, furi_string_get_cstr(infrared->file_path)); + if(result) { + scene_manager_set_scene_state( + infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded); + } + } + const char* remote_name = infrared_remote_get_name(infrared->remote); + + infrared_text_store_set(infrared, 0, "loaded\n%s", remote_name); + popup_set_text( + infrared->popup, infrared->text_store[0], 89, 44, AlignCenter, AlignTop); + + rpc_system_app_confirm(infrared->rpc_ctx, result); + } else if( + event.event == InfraredCustomEventTypeRpcButtonPressName || + event.event == InfraredCustomEventTypeRpcButtonPressIndex) { + bool result = false; + if(state == InfraredRpcStateLoaded) { + if(event.event == InfraredCustomEventTypeRpcButtonPressName) { + const char* button_name = furi_string_get_cstr(infrared->button_name); + size_t index; + const bool index_found = + infrared_remote_get_signal_index(infrared->remote, button_name, &index); + infrared->app_state.current_button_index = + index_found ? (signed)index : InfraredButtonIndexNone; + FURI_LOG_D(TAG, "Sending signal with name \"%s\"", button_name); + } else { + FURI_LOG_D( + TAG, + "Sending signal with index \"%ld\"", + infrared->app_state.current_button_index); + } + if(infrared->app_state.current_button_index != InfraredButtonIndexNone) { + infrared_tx_start_button_index( + infrared, infrared->app_state.current_button_index); + scene_manager_set_scene_state( + infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateSending); + result = true; + } + } + rpc_system_app_confirm(infrared->rpc_ctx, result); + } else if(event.event == InfraredCustomEventTypeRpcButtonRelease) { + bool result = false; + if(state == InfraredRpcStateSending) { + infrared_tx_stop(infrared); + result = true; + scene_manager_set_scene_state( + infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded); + } + rpc_system_app_confirm(infrared->rpc_ctx, result); + } else if(event.event == InfraredCustomEventTypeRpcExit) { + scene_manager_stop(infrared->scene_manager); + view_dispatcher_stop(infrared->view_dispatcher); + rpc_system_app_confirm(infrared->rpc_ctx, true); + } else if(event.event == InfraredCustomEventTypeRpcSessionClose) { + scene_manager_stop(infrared->scene_manager); + view_dispatcher_stop(infrared->view_dispatcher); + } + } + return consumed; +} + +void infrared_scene_rpc_on_exit(void* context) { + InfraredApp* infrared = context; + if(scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneRpc) == + InfraredRpcStateSending) { + infrared_tx_stop(infrared); + } + popup_reset(infrared->popup); +} diff --git a/applications/infrared/scenes/infrared_scene_start.c b/applications/main/infrared/scenes/infrared_scene_start.c similarity index 91% rename from applications/infrared/scenes/infrared_scene_start.c rename to applications/main/infrared/scenes/infrared_scene_start.c index d188a6c36dc..0e23bb7b8c9 100644 --- a/applications/infrared/scenes/infrared_scene_start.c +++ b/applications/main/infrared/scenes/infrared_scene_start.c @@ -1,4 +1,4 @@ -#include "../infrared_i.h" +#include "../infrared_app_i.h" enum SubmenuIndex { SubmenuIndexUniversalRemotes, @@ -8,12 +8,12 @@ enum SubmenuIndex { }; static void infrared_scene_start_submenu_callback(void* context, uint32_t index) { - Infrared* infrared = context; + InfraredApp* infrared = context; view_dispatcher_send_custom_event(infrared->view_dispatcher, index); } void infrared_scene_start_on_enter(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; Submenu* submenu = infrared->submenu; SceneManager* scene_manager = infrared->scene_manager; @@ -50,7 +50,7 @@ void infrared_scene_start_on_enter(void* context) { } bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; + InfraredApp* infrared = context; SceneManager* scene_manager = infrared->scene_manager; bool consumed = false; @@ -66,7 +66,7 @@ bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(scene_manager, InfraredSceneLearn); consumed = true; } else if(submenu_index == SubmenuIndexSavedRemotes) { - string_set_str(infrared->file_path, INFRARED_APP_FOLDER); + furi_string_set(infrared->file_path, INFRARED_APP_FOLDER); scene_manager_next_scene(scene_manager, InfraredSceneRemoteList); consumed = true; } else if(submenu_index == SubmenuIndexDebug) { @@ -79,6 +79,6 @@ bool infrared_scene_start_on_event(void* context, SceneManagerEvent event) { } void infrared_scene_start_on_exit(void* context) { - Infrared* infrared = context; + InfraredApp* infrared = context; submenu_reset(infrared->submenu); } diff --git a/applications/main/infrared/scenes/infrared_scene_universal.c b/applications/main/infrared/scenes/infrared_scene_universal.c new file mode 100644 index 00000000000..197478e3345 --- /dev/null +++ b/applications/main/infrared/scenes/infrared_scene_universal.c @@ -0,0 +1,77 @@ +#include "../infrared_app_i.h" + +typedef enum { + SubmenuIndexUniversalTV, + SubmenuIndexUniversalAC, + SubmenuIndexUniversalAudio, + SubmenuIndexUniversalProjector, +} SubmenuIndex; + +static void infrared_scene_universal_submenu_callback(void* context, uint32_t index) { + InfraredApp* infrared = context; + view_dispatcher_send_custom_event(infrared->view_dispatcher, index); +} + +void infrared_scene_universal_on_enter(void* context) { + InfraredApp* infrared = context; + Submenu* submenu = infrared->submenu; + + submenu_add_item( + submenu, + "TVs", + SubmenuIndexUniversalTV, + infrared_scene_universal_submenu_callback, + context); + submenu_add_item( + submenu, + "Audio Players", + SubmenuIndexUniversalAudio, + infrared_scene_universal_submenu_callback, + context); + submenu_add_item( + submenu, + "Projectors", + SubmenuIndexUniversalProjector, + infrared_scene_universal_submenu_callback, + context); + submenu_add_item( + submenu, + "Air Conditioners", + SubmenuIndexUniversalAC, + infrared_scene_universal_submenu_callback, + context); + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneUniversal)); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu); +} + +bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) { + InfraredApp* infrared = context; + SceneManager* scene_manager = infrared->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexUniversalTV) { + scene_manager_next_scene(scene_manager, InfraredSceneUniversalTV); + consumed = true; + } else if(event.event == SubmenuIndexUniversalAC) { + scene_manager_next_scene(scene_manager, InfraredSceneUniversalAC); + consumed = true; + } else if(event.event == SubmenuIndexUniversalAudio) { + scene_manager_next_scene(scene_manager, InfraredSceneUniversalAudio); + consumed = true; + } else if(event.event == SubmenuIndexUniversalProjector) { + scene_manager_next_scene(scene_manager, InfraredSceneUniversalProjector); + consumed = true; + } + scene_manager_set_scene_state(scene_manager, InfraredSceneUniversal, event.event); + } + + return consumed; +} + +void infrared_scene_universal_on_exit(void* context) { + InfraredApp* infrared = context; + submenu_reset(infrared->submenu); +} diff --git a/applications/main/infrared/scenes/infrared_scene_universal_ac.c b/applications/main/infrared/scenes/infrared_scene_universal_ac.c new file mode 100644 index 00000000000..764a9518909 --- /dev/null +++ b/applications/main/infrared/scenes/infrared_scene_universal_ac.c @@ -0,0 +1,143 @@ +#include "../infrared_app_i.h" + +#include "common/infrared_scene_universal_common.h" + +void infrared_scene_universal_ac_on_enter(void* context) { + infrared_scene_universal_common_on_enter(context); + + InfraredApp* infrared = context; + ButtonPanel* button_panel = infrared->button_panel; + InfraredBruteForce* brute_force = infrared->brute_force; + + infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/ac.ir")); + + button_panel_reserve(button_panel, 2, 3); + uint32_t i = 0; + button_panel_add_item( + button_panel, + i, + 0, + 0, + 6, + 15, + &I_off_19x20, + &I_off_hover_19x20, + infrared_scene_universal_common_item_callback, + context); + button_panel_add_icon(button_panel, 10, 37, &I_off_text_12x5); + infrared_brute_force_add_record(brute_force, i++, "Off"); + button_panel_add_item( + button_panel, + i, + 1, + 0, + 39, + 15, + &I_dry_19x20, + &I_dry_hover_19x20, + infrared_scene_universal_common_item_callback, + context); + button_panel_add_icon(button_panel, 41, 37, &I_dry_text_15x5); + infrared_brute_force_add_record(brute_force, i++, "Dh"); + button_panel_add_item( + button_panel, + i, + 0, + 1, + 3, + 49, + &I_max_24x23, + &I_max_hover_24x23, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Cool_hi"); + button_panel_add_item( + button_panel, + i, + 1, + 1, + 37, + 49, + &I_max_24x23, + &I_max_hover_24x23, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Heat_hi"); + if(furi_hal_rtc_get_locale_units() == FuriHalRtcLocaleUnitsMetric) { + button_panel_add_item( + button_panel, + i, + 0, + 2, + 3, + 100, + &I_celsius_24x23, + &I_celsius_hover_24x23, + infrared_scene_universal_common_item_callback, + context); + } else { + button_panel_add_item( + button_panel, + i, + 0, + 2, + 3, + 100, + &I_fahren_24x23, + &I_fahren_hover_24x23, + infrared_scene_universal_common_item_callback, + context); + } + infrared_brute_force_add_record(brute_force, i++, "Cool_lo"); + + if(furi_hal_rtc_get_locale_units() == FuriHalRtcLocaleUnitsMetric) { + button_panel_add_item( + button_panel, + i, + 1, + 2, + 37, + 100, + &I_celsius_24x23, + &I_celsius_hover_24x23, + infrared_scene_universal_common_item_callback, + context); + } else { + button_panel_add_item( + button_panel, + i, + 1, + 2, + 37, + 100, + &I_fahren_24x23, + &I_fahren_hover_24x23, + infrared_scene_universal_common_item_callback, + context); + } + infrared_brute_force_add_record(brute_force, i++, "Heat_lo"); + + button_panel_add_icon(button_panel, 0, 60, &I_cool_30x51); + button_panel_add_icon(button_panel, 34, 60, &I_heat_30x51); + + button_panel_add_label(button_panel, 4, 10, FontPrimary, "AC remote"); + + view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); + + infrared_show_loading_popup(infrared, true); + bool success = infrared_brute_force_calculate_messages(brute_force); + infrared_show_loading_popup(infrared, false); + + if(!success) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases); + } +} + +bool infrared_scene_universal_ac_on_event(void* context, SceneManagerEvent event) { + return infrared_scene_universal_common_on_event(context, event); +} + +void infrared_scene_universal_ac_on_exit(void* context) { + infrared_scene_universal_common_on_exit(context); +} diff --git a/applications/main/infrared/scenes/infrared_scene_universal_audio.c b/applications/main/infrared/scenes/infrared_scene_universal_audio.c new file mode 100644 index 00000000000..241a22bcbb7 --- /dev/null +++ b/applications/main/infrared/scenes/infrared_scene_universal_audio.c @@ -0,0 +1,140 @@ +#include "../infrared_app_i.h" + +#include "common/infrared_scene_universal_common.h" + +void infrared_scene_universal_audio_on_enter(void* context) { + infrared_scene_universal_common_on_enter(context); + + InfraredApp* infrared = context; + ButtonPanel* button_panel = infrared->button_panel; + InfraredBruteForce* brute_force = infrared->brute_force; + + infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/audio.ir")); + + button_panel_reserve(button_panel, 2, 4); + uint32_t i = 0; + button_panel_add_item( + button_panel, + i, + 0, + 0, + 6, + 13, + &I_power_19x20, + &I_power_hover_19x20, + infrared_scene_universal_common_item_callback, + context); + button_panel_add_icon(button_panel, 4, 35, &I_power_text_24x5); + infrared_brute_force_add_record(brute_force, i++, "Power"); + button_panel_add_item( + button_panel, + i, + 1, + 0, + 39, + 13, + &I_mute_19x20, + &I_mute_hover_19x20, + infrared_scene_universal_common_item_callback, + context); + button_panel_add_icon(button_panel, 39, 35, &I_mute_text_19x5); + infrared_brute_force_add_record(brute_force, i++, "Mute"); + button_panel_add_item( + button_panel, + i, + 0, + 1, + 6, + 42, + &I_play_19x20, + &I_play_hover_19x20, + infrared_scene_universal_common_item_callback, + context); + button_panel_add_icon(button_panel, 6, 64, &I_play_text_19x5); + infrared_brute_force_add_record(brute_force, i++, "Play"); + button_panel_add_item( + button_panel, + i, + 0, + 2, + 6, + 71, + &I_pause_19x20, + &I_pause_hover_19x20, + infrared_scene_universal_common_item_callback, + context); + button_panel_add_icon(button_panel, 4, 93, &I_pause_text_23x5); + infrared_brute_force_add_record(brute_force, i++, "Pause"); + button_panel_add_item( + button_panel, + i, + 0, + 3, + 6, + 101, + &I_prev_19x20, + &I_prev_hover_19x20, + infrared_scene_universal_common_item_callback, + context); + button_panel_add_icon(button_panel, 6, 123, &I_prev_text_19x5); + infrared_brute_force_add_record(brute_force, i++, "Prev"); + button_panel_add_item( + button_panel, + i, + 1, + 3, + 39, + 101, + &I_next_19x20, + &I_next_hover_19x20, + infrared_scene_universal_common_item_callback, + context); + button_panel_add_icon(button_panel, 39, 123, &I_next_text_19x6); + infrared_brute_force_add_record(brute_force, i++, "Next"); + button_panel_add_item( + button_panel, + i, + 1, + 2, + 37, + 77, + &I_voldown_24x21, + &I_voldown_hover_24x21, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Vol_dn"); + button_panel_add_item( + button_panel, + i, + 1, + 1, + 37, + 43, + &I_volup_24x21, + &I_volup_hover_24x21, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Vol_up"); + + button_panel_add_label(button_panel, 1, 10, FontPrimary, "Mus. remote"); + button_panel_add_icon(button_panel, 34, 56, &I_vol_ac_text_30x30); + + view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); + + infrared_show_loading_popup(infrared, true); + bool success = infrared_brute_force_calculate_messages(brute_force); + infrared_show_loading_popup(infrared, false); + + if(!success) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases); + } +} + +bool infrared_scene_universal_audio_on_event(void* context, SceneManagerEvent event) { + return infrared_scene_universal_common_on_event(context, event); +} + +void infrared_scene_universal_audio_on_exit(void* context) { + infrared_scene_universal_common_on_exit(context); +} diff --git a/applications/main/infrared/scenes/infrared_scene_universal_projector.c b/applications/main/infrared/scenes/infrared_scene_universal_projector.c new file mode 100644 index 00000000000..d8520deb39e --- /dev/null +++ b/applications/main/infrared/scenes/infrared_scene_universal_projector.c @@ -0,0 +1,89 @@ +#include "../infrared_app_i.h" + +#include "common/infrared_scene_universal_common.h" + +void infrared_scene_universal_projector_on_enter(void* context) { + infrared_scene_universal_common_on_enter(context); + + InfraredApp* infrared = context; + ButtonPanel* button_panel = infrared->button_panel; + InfraredBruteForce* brute_force = infrared->brute_force; + + infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/projector.ir")); + + button_panel_reserve(button_panel, 2, 3); + uint32_t i = 0; + button_panel_add_item( + button_panel, + i, + 0, + 0, + 6, + 23, + &I_power_19x20, + &I_power_hover_19x20, + infrared_scene_universal_common_item_callback, + context); + button_panel_add_icon(button_panel, 4, 45, &I_power_text_24x5); + infrared_brute_force_add_record(brute_force, i++, "Power"); + button_panel_add_item( + button_panel, + i, + 1, + 0, + 39, + 23, + &I_mute_19x20, + &I_mute_hover_19x20, + infrared_scene_universal_common_item_callback, + context); + button_panel_add_icon(button_panel, 39, 45, &I_mute_text_19x5); + infrared_brute_force_add_record(brute_force, i++, "Mute"); + button_panel_add_item( + button_panel, + i, + 0, + 1, + 20, + 59, + &I_volup_24x21, + &I_volup_hover_24x21, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Vol_up"); + + button_panel_add_item( + button_panel, + i, + 0, + 2, + 20, + 93, + &I_voldown_24x21, + &I_voldown_hover_24x21, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Vol_dn"); + + button_panel_add_label(button_panel, 3, 11, FontPrimary, "Proj. remote"); + button_panel_add_icon(button_panel, 17, 72, &I_vol_ac_text_30x30); + + view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); + + infrared_show_loading_popup(infrared, true); + bool success = infrared_brute_force_calculate_messages(brute_force); + infrared_show_loading_popup(infrared, false); + + if(!success) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases); + } +} + +bool infrared_scene_universal_projector_on_event(void* context, SceneManagerEvent event) { + return infrared_scene_universal_common_on_event(context, event); +} + +void infrared_scene_universal_projector_on_exit(void* context) { + infrared_scene_universal_common_on_exit(context); +} diff --git a/applications/main/infrared/scenes/infrared_scene_universal_tv.c b/applications/main/infrared/scenes/infrared_scene_universal_tv.c new file mode 100644 index 00000000000..6031205f551 --- /dev/null +++ b/applications/main/infrared/scenes/infrared_scene_universal_tv.c @@ -0,0 +1,116 @@ +#include "../infrared_app_i.h" + +#include "common/infrared_scene_universal_common.h" + +void infrared_scene_universal_tv_on_enter(void* context) { + infrared_scene_universal_common_on_enter(context); + + InfraredApp* infrared = context; + ButtonPanel* button_panel = infrared->button_panel; + InfraredBruteForce* brute_force = infrared->brute_force; + + infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/tv.ir")); + + button_panel_reserve(button_panel, 2, 3); + uint32_t i = 0; + button_panel_add_item( + button_panel, + i, + 0, + 0, + 6, + 16, + &I_power_19x20, + &I_power_hover_19x20, + infrared_scene_universal_common_item_callback, + context); + button_panel_add_icon(button_panel, 4, 38, &I_power_text_24x5); + infrared_brute_force_add_record(brute_force, i++, "Power"); + button_panel_add_item( + button_panel, + i, + 1, + 0, + 39, + 16, + &I_mute_19x20, + &I_mute_hover_19x20, + infrared_scene_universal_common_item_callback, + context); + button_panel_add_icon(button_panel, 39, 38, &I_mute_text_19x5); + + button_panel_add_icon(button_panel, 0, 66, &I_ch_text_31x34); + button_panel_add_icon(button_panel, 35, 66, &I_vol_tv_text_29x34); + + infrared_brute_force_add_record(brute_force, i++, "Mute"); + button_panel_add_item( + button_panel, + i, + 1, + 1, + 38, + 53, + &I_volup_24x21, + &I_volup_hover_24x21, + infrared_scene_universal_common_item_callback, + context); + + infrared_brute_force_add_record(brute_force, i++, "Vol_up"); + button_panel_add_item( + button_panel, + i, + 0, + 1, + 3, + 53, + &I_ch_up_24x21, + &I_ch_up_hover_24x21, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Ch_next"); + button_panel_add_item( + button_panel, + i, + 1, + 2, + 38, + 91, + &I_voldown_24x21, + &I_voldown_hover_24x21, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Vol_dn"); + button_panel_add_item( + button_panel, + i, + 0, + 2, + 3, + 91, + &I_ch_down_24x21, + &I_ch_down_hover_24x21, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Ch_prev"); + + button_panel_add_label(button_panel, 5, 10, FontPrimary, "TV remote"); + + view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); + + infrared_show_loading_popup(infrared, true); + bool success = infrared_brute_force_calculate_messages(brute_force); + infrared_show_loading_popup(infrared, false); + + if(!success) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases); + } +} + +bool infrared_scene_universal_tv_on_event(void* context, SceneManagerEvent event) { + return infrared_scene_universal_common_on_event(context, event); +} + +void infrared_scene_universal_tv_on_exit(void* context) { + infrared_scene_universal_common_on_exit(context); +} diff --git a/applications/infrared/views/infrared_debug_view.c b/applications/main/infrared/views/infrared_debug_view.c similarity index 100% rename from applications/infrared/views/infrared_debug_view.c rename to applications/main/infrared/views/infrared_debug_view.c index ab2c679c4d0..ec08962813e 100644 --- a/applications/infrared/views/infrared_debug_view.c +++ b/applications/main/infrared/views/infrared_debug_view.c @@ -1,11 +1,11 @@ #include "infrared_debug_view.h" -#include -#include - #include #include +#include +#include + #define INFRARED_DEBUG_TEXT_LENGTH 64 struct InfraredDebugView { diff --git a/applications/infrared/views/infrared_debug_view.h b/applications/main/infrared/views/infrared_debug_view.h similarity index 100% rename from applications/infrared/views/infrared_debug_view.h rename to applications/main/infrared/views/infrared_debug_view.h diff --git a/applications/main/infrared/views/infrared_move_view.c b/applications/main/infrared/views/infrared_move_view.c new file mode 100644 index 00000000000..374a65a44e8 --- /dev/null +++ b/applications/main/infrared/views/infrared_move_view.c @@ -0,0 +1,233 @@ +#include "infrared_move_view.h" + +#include + +#include +#include + +#include + +#define LIST_ITEMS 4U +#define LIST_LINE_H 13U +#define HEADER_H 12U +#define MOVE_X_OFFSET 5U + +struct InfraredMoveView { + View* view; + InfraredMoveCallback callback; + void* callback_context; +}; + +ARRAY_DEF(InfraredMoveViewItemArray, const char*, M_CSTR_DUP_OPLIST); //-V575 + +typedef struct { + InfraredMoveViewItemArray_t labels; + int32_t list_offset; + int32_t current_idx; + int32_t start_idx; + bool is_moving; +} InfraredMoveViewModel; + +static void infrared_move_view_draw_callback(Canvas* canvas, void* _model) { + InfraredMoveViewModel* model = _model; + + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned( + canvas, canvas_width(canvas) / 2, 0, AlignCenter, AlignTop, "Select a button to move"); + + const size_t btn_number = InfraredMoveViewItemArray_size(model->labels); + const bool show_scrollbar = btn_number > LIST_ITEMS; + + canvas_set_font(canvas, FontSecondary); + + for(uint32_t i = 0; i < MIN(btn_number, LIST_ITEMS); i++) { + int32_t idx = CLAMP((uint32_t)(i + model->list_offset), btn_number, 0U); + uint8_t x_offset = (model->is_moving && model->current_idx == idx) ? MOVE_X_OFFSET : 0; + uint8_t y_offset = HEADER_H + i * LIST_LINE_H; + uint8_t box_end_x = canvas_width(canvas) - (show_scrollbar ? 6 : 1); + + canvas_set_color(canvas, ColorBlack); + if(model->current_idx == idx) { + canvas_draw_box(canvas, x_offset, y_offset, box_end_x - x_offset, LIST_LINE_H); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, x_offset, y_offset); + canvas_draw_dot(canvas, x_offset + 1, y_offset); + canvas_draw_dot(canvas, x_offset, y_offset + 1); + canvas_draw_dot(canvas, x_offset, y_offset + LIST_LINE_H - 1); + canvas_draw_dot(canvas, box_end_x - 1, y_offset); + canvas_draw_dot(canvas, box_end_x - 1, y_offset + LIST_LINE_H - 1); + } + canvas_draw_str_aligned( + canvas, + x_offset + 3, + y_offset + 3, + AlignLeft, + AlignTop, + *InfraredMoveViewItemArray_cget(model->labels, idx)); + } + + if(show_scrollbar) { + elements_scrollbar_pos( + canvas, + canvas_width(canvas), + HEADER_H, + canvas_height(canvas) - HEADER_H, + model->current_idx, + btn_number); + } +} + +static void update_list_offset(InfraredMoveViewModel* model) { + const size_t btn_number = InfraredMoveViewItemArray_size(model->labels); + const int32_t bounds = btn_number > (LIST_ITEMS - 1) ? 2 : btn_number; + + if((btn_number > (LIST_ITEMS - 1)) && (model->current_idx >= ((int32_t)btn_number - 1))) { + model->list_offset = model->current_idx - (LIST_ITEMS - 1); + } else if(model->list_offset < model->current_idx - bounds) { + model->list_offset = + CLAMP(model->current_idx - (int32_t)(LIST_ITEMS - 2), (int32_t)btn_number - bounds, 0); + } else if(model->list_offset > model->current_idx - bounds) { + model->list_offset = CLAMP(model->current_idx - 1, (int32_t)btn_number - bounds, 0); + } +} + +static bool infrared_move_view_input_callback(InputEvent* event, void* context) { + InfraredMoveView* move_view = context; + + bool consumed = false; + + if(((event->type == InputTypeShort || event->type == InputTypeRepeat)) && + ((event->key == InputKeyUp) || (event->key == InputKeyDown))) { + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { + const size_t btn_number = InfraredMoveViewItemArray_size(model->labels); + const int32_t item_idx_prev = model->current_idx; + + if(event->key == InputKeyUp) { + if(model->current_idx <= 0) { + model->current_idx = btn_number; + } + model->current_idx--; + + } else if(event->key == InputKeyDown) { + model->current_idx++; + if(model->current_idx >= (int32_t)(btn_number)) { + model->current_idx = 0; + } + } + + if(model->is_moving) { + InfraredMoveViewItemArray_swap_at( + model->labels, item_idx_prev, model->current_idx); + } + + update_list_offset(model); + }, + true); + + consumed = true; + + } else if((event->key == InputKeyOk) && (event->type == InputTypeShort)) { + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { + if(!model->is_moving) { + model->start_idx = model->current_idx; + } else if(move_view->callback) { + move_view->callback( + model->start_idx, model->current_idx, move_view->callback_context); + } + model->is_moving = !(model->is_moving); + }, + true); + + consumed = true; + + } else if(event->key == InputKeyBack) { + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { + if(model->is_moving && move_view->callback) { + move_view->callback( + model->start_idx, model->current_idx, move_view->callback_context); + } + model->is_moving = false; + }, + false); + + // Not consuming, Back event is passed thru + } + + return consumed; +} + +void infrared_move_view_set_callback( + InfraredMoveView* move_view, + InfraredMoveCallback callback, + void* context) { + furi_assert(move_view); + move_view->callback = callback; + move_view->callback_context = context; +} + +void infrared_move_view_add_item(InfraredMoveView* move_view, const char* label) { + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { InfraredMoveViewItemArray_push_back(model->labels, label); }, + true); +} + +void infrared_move_view_reset(InfraredMoveView* move_view) { + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { + InfraredMoveViewItemArray_reset(model->labels); + model->list_offset = 0; + model->start_idx = 0; + model->current_idx = 0; + model->is_moving = false; + }, + false); + move_view->callback_context = NULL; +} + +InfraredMoveView* infrared_move_view_alloc(void) { + InfraredMoveView* move_view = malloc(sizeof(InfraredMoveView)); + + move_view->view = view_alloc(); + view_allocate_model(move_view->view, ViewModelTypeLocking, sizeof(InfraredMoveViewModel)); + view_set_draw_callback(move_view->view, infrared_move_view_draw_callback); + view_set_input_callback(move_view->view, infrared_move_view_input_callback); + view_set_context(move_view->view, move_view); + + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { InfraredMoveViewItemArray_init(model->labels); }, + true); + + return move_view; +} + +void infrared_move_view_free(InfraredMoveView* move_view) { + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { InfraredMoveViewItemArray_clear(model->labels); }, + true); + + view_free(move_view->view); + free(move_view); +} + +View* infrared_move_view_get_view(InfraredMoveView* move_view) { + return move_view->view; +} diff --git a/applications/main/infrared/views/infrared_move_view.h b/applications/main/infrared/views/infrared_move_view.h new file mode 100644 index 00000000000..0ab15ce0d1e --- /dev/null +++ b/applications/main/infrared/views/infrared_move_view.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +typedef struct InfraredMoveView InfraredMoveView; + +typedef void (*InfraredMoveCallback)(uint32_t index_old, uint32_t index_new, void* context); + +InfraredMoveView* infrared_move_view_alloc(void); + +void infrared_move_view_free(InfraredMoveView* debug_view); + +View* infrared_move_view_get_view(InfraredMoveView* debug_view); + +void infrared_move_view_set_callback( + InfraredMoveView* move_view, + InfraredMoveCallback callback, + void* context); + +void infrared_move_view_add_item(InfraredMoveView* move_view, const char* label); + +void infrared_move_view_reset(InfraredMoveView* move_view); diff --git a/applications/infrared/views/infrared_progress_view.c b/applications/main/infrared/views/infrared_progress_view.c similarity index 95% rename from applications/infrared/views/infrared_progress_view.c rename to applications/main/infrared/views/infrared_progress_view.c index 5c84ce0d95d..432da7ff1cd 100644 --- a/applications/infrared/views/infrared_progress_view.c +++ b/applications/main/infrared/views/infrared_progress_view.c @@ -1,14 +1,15 @@ -#include -#include "furi_hal_resources.h" -#include "assets_icons.h" -#include "gui/canvas.h" -#include "gui/view.h" -#include "input/input.h" -#include "m-string.h" +#include "infrared_progress_view.h" + +#include +#include +#include #include +#include +#include + #include -#include "infrared_progress_view.h" -#include "gui/modules/button_panel.h" +#include +#include #include struct InfraredProgressView { diff --git a/applications/infrared/views/infrared_progress_view.h b/applications/main/infrared/views/infrared_progress_view.h similarity index 100% rename from applications/infrared/views/infrared_progress_view.h rename to applications/main/infrared/views/infrared_progress_view.h diff --git a/applications/main/lfrfid/application.fam b/applications/main/lfrfid/application.fam new file mode 100644 index 00000000000..c067d786fc4 --- /dev/null +++ b/applications/main/lfrfid/application.fam @@ -0,0 +1,22 @@ +App( + appid="lfrfid", + name="125 kHz RFID", + apptype=FlipperAppType.MENUEXTERNAL, + targets=["f7"], + entry_point="lfrfid_app", + icon="A_125khz_14", + stack_size=2 * 1024, + order=20, + fap_libs=["assets"], + fap_icon="icon.png", + fap_category="RFID", +) + +App( + appid="lfrfid_start", + targets=["f7"], + apptype=FlipperAppType.STARTUP, + entry_point="lfrfid_on_system_start", + sources=["lfrfid_cli.c"], + order=50, +) diff --git a/applications/main/lfrfid/icon.png b/applications/main/lfrfid/icon.png new file mode 100644 index 00000000000..ce01284a2c1 Binary files /dev/null and b/applications/main/lfrfid/icon.png differ diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c new file mode 100644 index 00000000000..cd0613861fe --- /dev/null +++ b/applications/main/lfrfid/lfrfid.c @@ -0,0 +1,326 @@ +#include "lfrfid_i.h" +#include + +static bool lfrfid_debug_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + LfRfid* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool lfrfid_debug_back_event_callback(void* context) { + furi_assert(context); + LfRfid* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void rpc_command_callback(const RpcAppSystemEvent* event, void* context) { + furi_assert(context); + LfRfid* app = (LfRfid*)context; + + if(event->type == RpcAppEventTypeSessionClose) { + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventRpcSessionClose); + // Detach RPC + rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL); + app->rpc_ctx = NULL; + } else if(event->type == RpcAppEventTypeAppExit) { + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventExit); + } else if(event->type == RpcAppEventTypeLoadFile) { + furi_assert(event->data.type == RpcAppSystemEventDataTypeString); + furi_string_set(app->file_path, event->data.string); + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventRpcLoadFile); + } else { + rpc_system_app_confirm(app->rpc_ctx, false); + } +} + +static LfRfid* lfrfid_alloc() { + LfRfid* lfrfid = malloc(sizeof(LfRfid)); + + lfrfid->storage = furi_record_open(RECORD_STORAGE); + lfrfid->dialogs = furi_record_open(RECORD_DIALOGS); + + lfrfid->file_name = furi_string_alloc(); + lfrfid->raw_file_name = furi_string_alloc(); + lfrfid->file_path = furi_string_alloc_set(LFRFID_APP_FOLDER); + + lfrfid->dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + + size_t size = protocol_dict_get_max_data_size(lfrfid->dict); + lfrfid->new_key_data = (uint8_t*)malloc(size); + lfrfid->old_key_data = (uint8_t*)malloc(size); + + lfrfid->lfworker = lfrfid_worker_alloc(lfrfid->dict); + + lfrfid->view_dispatcher = view_dispatcher_alloc(); + lfrfid->scene_manager = scene_manager_alloc(&lfrfid_scene_handlers, lfrfid); + view_dispatcher_enable_queue(lfrfid->view_dispatcher); + view_dispatcher_set_event_callback_context(lfrfid->view_dispatcher, lfrfid); + view_dispatcher_set_custom_event_callback( + lfrfid->view_dispatcher, lfrfid_debug_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + lfrfid->view_dispatcher, lfrfid_debug_back_event_callback); + + // Open GUI record + lfrfid->gui = furi_record_open(RECORD_GUI); + + // Open Notification record + lfrfid->notifications = furi_record_open(RECORD_NOTIFICATION); + + // Submenu + lfrfid->submenu = submenu_alloc(); + view_dispatcher_add_view( + lfrfid->view_dispatcher, LfRfidViewSubmenu, submenu_get_view(lfrfid->submenu)); + + // Dialog + lfrfid->dialog_ex = dialog_ex_alloc(); + view_dispatcher_add_view( + lfrfid->view_dispatcher, LfRfidViewDialogEx, dialog_ex_get_view(lfrfid->dialog_ex)); + + // Popup + lfrfid->popup = popup_alloc(); + view_dispatcher_add_view( + lfrfid->view_dispatcher, LfRfidViewPopup, popup_get_view(lfrfid->popup)); + + // Widget + lfrfid->widget = widget_alloc(); + view_dispatcher_add_view( + lfrfid->view_dispatcher, LfRfidViewWidget, widget_get_view(lfrfid->widget)); + + // Text Input + lfrfid->text_input = text_input_alloc(); + view_dispatcher_add_view( + lfrfid->view_dispatcher, LfRfidViewTextInput, text_input_get_view(lfrfid->text_input)); + + // Byte Input + lfrfid->byte_input = byte_input_alloc(); + view_dispatcher_add_view( + lfrfid->view_dispatcher, LfRfidViewByteInput, byte_input_get_view(lfrfid->byte_input)); + + // Read custom view + lfrfid->read_view = lfrfid_view_read_alloc(); + view_dispatcher_add_view( + lfrfid->view_dispatcher, LfRfidViewRead, lfrfid_view_read_get_view(lfrfid->read_view)); + + return lfrfid; +} //-V773 + +static void lfrfid_free(LfRfid* lfrfid) { + furi_assert(lfrfid); + + furi_string_free(lfrfid->raw_file_name); + furi_string_free(lfrfid->file_name); + furi_string_free(lfrfid->file_path); + protocol_dict_free(lfrfid->dict); + + lfrfid_worker_free(lfrfid->lfworker); + + if(lfrfid->rpc_ctx) { + rpc_system_app_set_callback(lfrfid->rpc_ctx, NULL, NULL); + rpc_system_app_send_exited(lfrfid->rpc_ctx); + } + + free(lfrfid->new_key_data); + free(lfrfid->old_key_data); + + // Submenu + view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewSubmenu); + submenu_free(lfrfid->submenu); + + // DialogEx + view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewDialogEx); + dialog_ex_free(lfrfid->dialog_ex); + + // Popup + view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewPopup); + popup_free(lfrfid->popup); + + // Widget + view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewWidget); + widget_free(lfrfid->widget); + + // TextInput + view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewTextInput); + text_input_free(lfrfid->text_input); + + // ByteInput + view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewByteInput); + byte_input_free(lfrfid->byte_input); + + // Read custom view + view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewRead); + lfrfid_view_read_free(lfrfid->read_view); + + // View Dispatcher + view_dispatcher_free(lfrfid->view_dispatcher); + + // Scene Manager + scene_manager_free(lfrfid->scene_manager); + + // GUI + furi_record_close(RECORD_GUI); + lfrfid->gui = NULL; + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + lfrfid->notifications = NULL; + + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_DIALOGS); + + free(lfrfid); +} + +int32_t lfrfid_app(void* p) { + LfRfid* app = lfrfid_alloc(); + char* args = p; + + lfrfid_make_app_folder(app); + + if(args && strlen(args)) { + uint32_t rpc_ctx_ptr = 0; + if(sscanf(args, "RPC %lX", &rpc_ctx_ptr) == 1) { + app->rpc_ctx = (RpcAppSystem*)rpc_ctx_ptr; + rpc_system_app_set_callback(app->rpc_ctx, rpc_command_callback, app); + rpc_system_app_send_started(app->rpc_ctx); + view_dispatcher_attach_to_gui( + app->view_dispatcher, app->gui, ViewDispatcherTypeDesktop); + scene_manager_next_scene(app->scene_manager, LfRfidSceneRpc); + dolphin_deed(DolphinDeedRfidEmulate); + } else { + furi_string_set(app->file_path, args); + if(lfrfid_load_key_data(app, app->file_path, true)) { + view_dispatcher_attach_to_gui( + app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); + dolphin_deed(DolphinDeedRfidEmulate); + } else { + view_dispatcher_stop(app->view_dispatcher); + } + } + } else { + view_dispatcher_attach_to_gui( + app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + scene_manager_next_scene(app->scene_manager, LfRfidSceneStart); + } + + view_dispatcher_run(app->view_dispatcher); + + lfrfid_free(app); + + return 0; +} + +bool lfrfid_save_key(LfRfid* app) { + furi_assert(app); + + bool result = false; + + lfrfid_make_app_folder(app); + + if(furi_string_end_with(app->file_path, LFRFID_APP_FILENAME_EXTENSION)) { + size_t filename_start = furi_string_search_rchar(app->file_path, '/'); + furi_string_left(app->file_path, filename_start); + } + + furi_string_cat_printf( + app->file_path, + "/%s%s", + furi_string_get_cstr(app->file_name), + LFRFID_APP_FILENAME_EXTENSION); + + result = lfrfid_save_key_data(app, app->file_path); + return result; +} + +bool lfrfid_load_key_from_file_select(LfRfid* app) { + furi_assert(app); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, LFRFID_APP_FILENAME_EXTENSION, &I_125_10px); + browser_options.base_path = LFRFID_APP_FOLDER; + + // Input events and views are managed by file_browser + bool result = + dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options); + + if(result) { + result = lfrfid_load_key_data(app, app->file_path, true); + } + + return result; +} + +bool lfrfid_delete_key(LfRfid* app) { + furi_assert(app); + + return storage_simply_remove(app->storage, furi_string_get_cstr(app->file_path)); +} + +bool lfrfid_load_key_data(LfRfid* app, FuriString* path, bool show_dialog) { + bool result = false; + + do { + app->protocol_id = lfrfid_dict_file_load(app->dict, furi_string_get_cstr(path)); + if(app->protocol_id == PROTOCOL_NO) break; + + path_extract_filename(path, app->file_name, true); + result = true; + } while(0); + + if((!result) && (show_dialog)) { + dialog_message_show_storage_error(app->dialogs, "Cannot load\nkey file"); + } + + return result; +} + +bool lfrfid_save_key_data(LfRfid* app, FuriString* path) { + bool result = lfrfid_dict_file_save(app->dict, app->protocol_id, furi_string_get_cstr(path)); + + if(!result) { + dialog_message_show_storage_error(app->dialogs, "Cannot save\nkey file"); + } + + return result; +} + +void lfrfid_make_app_folder(LfRfid* app) { + furi_assert(app); + + if(!storage_simply_mkdir(app->storage, LFRFID_APP_FOLDER)) { + dialog_message_show_storage_error(app->dialogs, "Cannot create\napp folder"); + } +} + +void lfrfid_text_store_set(LfRfid* app, const char* text, ...) { + furi_assert(app); + va_list args; + va_start(args, text); + + vsnprintf(app->text_store, LFRFID_TEXT_STORE_SIZE, text, args); + + va_end(args); +} + +void lfrfid_text_store_clear(LfRfid* app) { + furi_assert(app); + memset(app->text_store, 0, sizeof(app->text_store)); +} + +void lfrfid_popup_timeout_callback(void* context) { + LfRfid* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventPopupClosed); +} + +void lfrfid_widget_callback(GuiButtonType result, InputType type, void* context) { + LfRfid* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void lfrfid_text_input_callback(void* context) { + LfRfid* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventNext); +} diff --git a/applications/main/lfrfid/lfrfid_cli.c b/applications/main/lfrfid/lfrfid_cli.c new file mode 100644 index 00000000000..ce3e987e80e --- /dev/null +++ b/applications/main/lfrfid/lfrfid_cli.c @@ -0,0 +1,576 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +static void lfrfid_cli(Cli* cli, FuriString* args, void* context); + +// app cli function +void lfrfid_on_system_start() { + Cli* cli = furi_record_open(RECORD_CLI); + cli_add_command(cli, "rfid", CliCommandFlagDefault, lfrfid_cli, NULL); + furi_record_close(RECORD_CLI); +} + +static void lfrfid_cli_print_usage() { + printf("Usage:\r\n"); + printf("rfid read \r\n"); + printf("rfid \r\n"); + printf("rfid raw_read \r\n"); + printf("rfid raw_emulate \r\n"); +}; + +typedef struct { + ProtocolId protocol; + FuriEventFlag* event; +} LFRFIDCliReadContext; + +static void lfrfid_cli_read_callback(LFRFIDWorkerReadResult result, ProtocolId proto, void* ctx) { + furi_assert(ctx); + LFRFIDCliReadContext* context = ctx; + if(result == LFRFIDWorkerReadDone) { + context->protocol = proto; + FURI_SW_MEMBARRIER(); + } + furi_event_flag_set(context->event, 1 << result); +} + +static void lfrfid_cli_read(Cli* cli, FuriString* args) { + FuriString* type_string; + type_string = furi_string_alloc(); + LFRFIDWorkerReadType type = LFRFIDWorkerReadTypeAuto; + + if(args_read_string_and_trim(args, type_string)) { + if(furi_string_cmp_str(type_string, "normal") == 0 || + furi_string_cmp_str(type_string, "ask") == 0) { + // ask + type = LFRFIDWorkerReadTypeASKOnly; + } else if( + furi_string_cmp_str(type_string, "indala") == 0 || + furi_string_cmp_str(type_string, "psk") == 0) { + // psk + type = LFRFIDWorkerReadTypePSKOnly; + } else { + lfrfid_cli_print_usage(); + furi_string_free(type_string); + return; + } + } + furi_string_free(type_string); + + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + LFRFIDWorker* worker = lfrfid_worker_alloc(dict); + LFRFIDCliReadContext context; + context.protocol = PROTOCOL_NO; + context.event = furi_event_flag_alloc(); + + lfrfid_worker_start_thread(worker); + + printf("Reading RFID...\r\nPress Ctrl+C to abort\r\n"); + + const uint32_t available_flags = (1 << LFRFIDWorkerReadDone); + + lfrfid_worker_read_start(worker, type, lfrfid_cli_read_callback, &context); + + while(true) { + uint32_t flags = + furi_event_flag_wait(context.event, available_flags, FuriFlagWaitAny, 100); + + if(flags != (unsigned)FuriFlagErrorTimeout) { + if(FURI_BIT(flags, LFRFIDWorkerReadDone)) { + break; + } + } + + if(cli_cmd_interrupt_received(cli)) break; + } + + lfrfid_worker_stop(worker); + lfrfid_worker_stop_thread(worker); + lfrfid_worker_free(worker); + + if(context.protocol != PROTOCOL_NO) { + printf("%s ", protocol_dict_get_name(dict, context.protocol)); + + size_t size = protocol_dict_get_data_size(dict, context.protocol); + uint8_t* data = malloc(size); + protocol_dict_get_data(dict, context.protocol, data, size); + for(size_t i = 0; i < size; i++) { + printf("%02X", data[i]); + } + printf("\r\n"); + free(data); + + FuriString* info; + info = furi_string_alloc(); + protocol_dict_render_data(dict, info, context.protocol); + if(!furi_string_empty(info)) { + printf("%s\r\n", furi_string_get_cstr(info)); + } + furi_string_free(info); + } + + printf("Reading stopped\r\n"); + protocol_dict_free(dict); + + furi_event_flag_free(context.event); +} + +static bool lfrfid_cli_parse_args(FuriString* args, ProtocolDict* dict, ProtocolId* protocol) { + bool result = false; + FuriString *protocol_name, *data_text; + protocol_name = furi_string_alloc(); + data_text = furi_string_alloc(); + size_t data_size = protocol_dict_get_max_data_size(dict); + uint8_t* data = malloc(data_size); + + do { + // load args + if(!args_read_string_and_trim(args, protocol_name) || + !args_read_string_and_trim(args, data_text)) { + lfrfid_cli_print_usage(); + break; + } + + // check protocol arg + *protocol = protocol_dict_get_protocol_by_name(dict, furi_string_get_cstr(protocol_name)); + if(*protocol == PROTOCOL_NO) { + printf( + "Unknown protocol: %s\r\n" + "Available protocols:\r\n", + furi_string_get_cstr(protocol_name)); + + for(ProtocolId i = 0; i < LFRFIDProtocolMax; i++) { + printf( + "\t%s, %zu bytes long\r\n", + protocol_dict_get_name(dict, i), + protocol_dict_get_data_size(dict, i)); + } + break; + } + + data_size = protocol_dict_get_data_size(dict, *protocol); + + // check data arg + if(!args_read_hex_bytes(data_text, data, data_size)) { + printf( + "%s data needs to be %zu bytes long\r\n", + protocol_dict_get_name(dict, *protocol), + data_size); + break; + } + + // load data to protocol + protocol_dict_set_data(dict, *protocol, data, data_size); + + result = true; + } while(false); + + free(data); + furi_string_free(protocol_name); + furi_string_free(data_text); + return result; +} + +static void lfrfid_cli_write_callback(LFRFIDWorkerWriteResult result, void* ctx) { + furi_assert(ctx); + FuriEventFlag* events = ctx; + furi_event_flag_set(events, 1 << result); +} + +static void lfrfid_cli_write(Cli* cli, FuriString* args) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + ProtocolId protocol; + + if(!lfrfid_cli_parse_args(args, dict, &protocol)) { + protocol_dict_free(dict); + return; + } + + LFRFIDWorker* worker = lfrfid_worker_alloc(dict); + FuriEventFlag* event = furi_event_flag_alloc(); + + lfrfid_worker_start_thread(worker); + lfrfid_worker_write_start(worker, protocol, lfrfid_cli_write_callback, event); + + printf("Writing RFID...\r\nPress Ctrl+C to abort\r\n"); + const uint32_t available_flags = (1 << LFRFIDWorkerWriteOK) | + (1 << LFRFIDWorkerWriteProtocolCannotBeWritten) | + (1 << LFRFIDWorkerWriteFobCannotBeWritten); + + while(!cli_cmd_interrupt_received(cli)) { + uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100); + if(flags != (unsigned)FuriFlagErrorTimeout) { + if(FURI_BIT(flags, LFRFIDWorkerWriteOK)) { + printf("Written!\r\n"); + break; + } + + if(FURI_BIT(flags, LFRFIDWorkerWriteProtocolCannotBeWritten)) { + printf("This protocol cannot be written.\r\n"); + break; + } + + if(FURI_BIT(flags, LFRFIDWorkerWriteFobCannotBeWritten)) { + printf("Seems this fob cannot be written.\r\n"); + } + } + } + printf("Writing stopped\r\n"); + + lfrfid_worker_stop(worker); + lfrfid_worker_stop_thread(worker); + lfrfid_worker_free(worker); + protocol_dict_free(dict); + furi_event_flag_free(event); +} + +static void lfrfid_cli_emulate(Cli* cli, FuriString* args) { + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + ProtocolId protocol; + + if(!lfrfid_cli_parse_args(args, dict, &protocol)) { + protocol_dict_free(dict); + return; + } + + LFRFIDWorker* worker = lfrfid_worker_alloc(dict); + + lfrfid_worker_start_thread(worker); + lfrfid_worker_emulate_start(worker, protocol); + + printf("Emulating RFID...\r\nPress Ctrl+C to abort\r\n"); + while(!cli_cmd_interrupt_received(cli)) { + furi_delay_ms(100); + } + printf("Emulation stopped\r\n"); + + lfrfid_worker_stop(worker); + lfrfid_worker_stop_thread(worker); + lfrfid_worker_free(worker); + protocol_dict_free(dict); +} + +static void lfrfid_cli_raw_analyze(Cli* cli, FuriString* args) { + UNUSED(cli); + FuriString *filepath, *info_string; + filepath = furi_string_alloc(); + info_string = furi_string_alloc(); + Storage* storage = furi_record_open(RECORD_STORAGE); + LFRFIDRawFile* file = lfrfid_raw_file_alloc(storage); + + do { + float frequency = 0; + float duty_cycle = 0; + + if(!args_read_probably_quoted_string_and_trim(args, filepath)) { + lfrfid_cli_print_usage(); + break; + } + + if(!lfrfid_raw_file_open_read(file, furi_string_get_cstr(filepath))) { + printf("Failed to open file\r\n"); + break; + } + + if(!lfrfid_raw_file_read_header(file, &frequency, &duty_cycle)) { + printf("Invalid header\r\n"); + break; + } + + bool file_end = false; + uint32_t total_warns = 0; + uint32_t total_duration = 0; + uint32_t total_pulse = 0; + ProtocolId total_protocol = PROTOCOL_NO; + + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + protocol_dict_decoders_start(dict); + + while(!file_end) { + uint32_t pulse = 0; + uint32_t duration = 0; + if(lfrfid_raw_file_read_pair(file, &duration, &pulse, &file_end)) { + bool warn = false; + + if(pulse > duration || pulse <= 0 || duration <= 0) { + total_warns += 1; + warn = true; + } + + furi_string_printf(info_string, "[%lu %lu]", pulse, duration); + printf("%-16s", furi_string_get_cstr(info_string)); + furi_string_printf(info_string, "[%lu %lu]", pulse, duration - pulse); + printf("%-16s", furi_string_get_cstr(info_string)); + + if(warn) { + printf(" <<----"); + } + + if(total_protocol == PROTOCOL_NO) { + total_protocol = protocol_dict_decoders_feed(dict, true, pulse); + if(total_protocol == PROTOCOL_NO) { + total_protocol = + protocol_dict_decoders_feed(dict, false, duration - pulse); + } + + if(total_protocol != PROTOCOL_NO) { + printf(" ", protocol_dict_get_name(dict, total_protocol)); + } + } + + printf("\r\n"); + + total_pulse += pulse; + total_duration += duration; + + if(total_protocol != PROTOCOL_NO) { //-V1051 + break; + } + } else { + printf("Failed to read pair\r\n"); + break; + } + } + + printf(" Frequency: %f\r\n", (double)frequency); + printf(" Duty Cycle: %f\r\n", (double)duty_cycle); + printf(" Warns: %lu\r\n", total_warns); + printf(" Pulse sum: %lu\r\n", total_pulse); + printf("Duration sum: %lu\r\n", total_duration); + printf(" Average: %f\r\n", (double)((float)total_pulse / (float)total_duration)); + printf(" Protocol: "); + + if(total_protocol != PROTOCOL_NO) { + size_t data_size = protocol_dict_get_data_size(dict, total_protocol); + uint8_t* data = malloc(data_size); + protocol_dict_get_data(dict, total_protocol, data, data_size); + + printf("%s [", protocol_dict_get_name(dict, total_protocol)); + for(size_t i = 0; i < data_size; i++) { + printf("%02X", data[i]); + if(i < data_size - 1) { + printf(" "); + } + } + printf("]\r\n"); + + protocol_dict_render_data(dict, info_string, total_protocol); + printf("%s\r\n", furi_string_get_cstr(info_string)); + + free(data); + } else { + printf("not found\r\n"); + } + + protocol_dict_free(dict); + } while(false); + + furi_string_free(filepath); + furi_string_free(info_string); + lfrfid_raw_file_free(file); + furi_record_close(RECORD_STORAGE); +} + +static void lfrfid_cli_raw_read_callback(LFRFIDWorkerReadRawResult result, void* context) { + furi_assert(context); + FuriEventFlag* event = context; + furi_event_flag_set(event, 1 << result); +} + +static void lfrfid_cli_raw_read(Cli* cli, FuriString* args) { + UNUSED(cli); + + FuriString *filepath, *type_string; + filepath = furi_string_alloc(); + type_string = furi_string_alloc(); + LFRFIDWorkerReadType type = LFRFIDWorkerReadTypeAuto; + + do { + if(args_read_string_and_trim(args, type_string)) { + if(furi_string_cmp_str(type_string, "normal") == 0 || + furi_string_cmp_str(type_string, "ask") == 0) { + // ask + type = LFRFIDWorkerReadTypeASKOnly; + } else if( + furi_string_cmp_str(type_string, "indala") == 0 || + furi_string_cmp_str(type_string, "psk") == 0) { + // psk + type = LFRFIDWorkerReadTypePSKOnly; + } else { + lfrfid_cli_print_usage(); + break; + } + } + + if(!args_read_probably_quoted_string_and_trim(args, filepath)) { + lfrfid_cli_print_usage(); + break; + } + + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + LFRFIDWorker* worker = lfrfid_worker_alloc(dict); + FuriEventFlag* event = furi_event_flag_alloc(); + + lfrfid_worker_start_thread(worker); + + bool overrun = false; + + const uint32_t available_flags = (1 << LFRFIDWorkerReadRawFileError) | + (1 << LFRFIDWorkerReadRawOverrun); + + lfrfid_worker_read_raw_start( + worker, furi_string_get_cstr(filepath), type, lfrfid_cli_raw_read_callback, event); + while(true) { + uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100); + + if(flags != (unsigned)FuriFlagErrorTimeout) { + if(FURI_BIT(flags, LFRFIDWorkerReadRawFileError)) { + printf("File is not RFID raw file\r\n"); + break; + } + + if(FURI_BIT(flags, LFRFIDWorkerReadRawOverrun)) { + if(!overrun) { + printf("Overrun\r\n"); + overrun = true; + } + } + } + + if(cli_cmd_interrupt_received(cli)) break; + } + + if(overrun) { + printf("An overrun occurred during read\r\n"); + } + + lfrfid_worker_stop(worker); + + lfrfid_worker_stop_thread(worker); + lfrfid_worker_free(worker); + protocol_dict_free(dict); + + furi_event_flag_free(event); + + } while(false); + + furi_string_free(filepath); + furi_string_free(type_string); +} + +static void lfrfid_cli_raw_emulate_callback(LFRFIDWorkerEmulateRawResult result, void* context) { + furi_assert(context); + FuriEventFlag* event = context; + furi_event_flag_set(event, 1 << result); +} + +static void lfrfid_cli_raw_emulate(Cli* cli, FuriString* args) { + UNUSED(cli); + + FuriString* filepath; + filepath = furi_string_alloc(); + Storage* storage = furi_record_open(RECORD_STORAGE); + + do { + if(!args_read_probably_quoted_string_and_trim(args, filepath)) { + lfrfid_cli_print_usage(); + break; + } + + if(!storage_file_exists(storage, furi_string_get_cstr(filepath))) { + printf("File not found: \"%s\"\r\n", furi_string_get_cstr(filepath)); + break; + } + + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + LFRFIDWorker* worker = lfrfid_worker_alloc(dict); + FuriEventFlag* event = furi_event_flag_alloc(); + + lfrfid_worker_start_thread(worker); + + bool overrun = false; + + const uint32_t available_flags = (1 << LFRFIDWorkerEmulateRawFileError) | + (1 << LFRFIDWorkerEmulateRawOverrun); + + lfrfid_worker_emulate_raw_start( + worker, furi_string_get_cstr(filepath), lfrfid_cli_raw_emulate_callback, event); + while(true) { + uint32_t flags = furi_event_flag_wait(event, available_flags, FuriFlagWaitAny, 100); + + if(flags != (unsigned)FuriFlagErrorTimeout) { + if(FURI_BIT(flags, LFRFIDWorkerEmulateRawFileError)) { + printf("File is not RFID raw file\r\n"); + break; + } + + if(FURI_BIT(flags, LFRFIDWorkerEmulateRawOverrun)) { + if(!overrun) { + printf("Overrun\r\n"); + overrun = true; + } + } + } + + if(cli_cmd_interrupt_received(cli)) break; + } + + if(overrun) { + printf("An overrun occurred during emulation\r\n"); + } + + lfrfid_worker_stop(worker); + + lfrfid_worker_stop_thread(worker); + lfrfid_worker_free(worker); + protocol_dict_free(dict); + + furi_event_flag_free(event); + + } while(false); + + furi_record_close(RECORD_STORAGE); + furi_string_free(filepath); +} + +static void lfrfid_cli(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + FuriString* cmd; + cmd = furi_string_alloc(); + + if(!args_read_string_and_trim(args, cmd)) { + furi_string_free(cmd); + lfrfid_cli_print_usage(); + return; + } + + if(furi_string_cmp_str(cmd, "read") == 0) { + lfrfid_cli_read(cli, args); + } else if(furi_string_cmp_str(cmd, "write") == 0) { + lfrfid_cli_write(cli, args); + } else if(furi_string_cmp_str(cmd, "emulate") == 0) { + lfrfid_cli_emulate(cli, args); + } else if(furi_string_cmp_str(cmd, "raw_read") == 0) { + lfrfid_cli_raw_read(cli, args); + } else if(furi_string_cmp_str(cmd, "raw_emulate") == 0) { + lfrfid_cli_raw_emulate(cli, args); + } else if(furi_string_cmp_str(cmd, "raw_analyze") == 0) { + lfrfid_cli_raw_analyze(cli, args); + } else { + lfrfid_cli_print_usage(); + } + + furi_string_free(cmd); +} diff --git a/applications/main/lfrfid/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h new file mode 100644 index 00000000000..d94a5f865a8 --- /dev/null +++ b/applications/main/lfrfid/lfrfid_i.h @@ -0,0 +1,145 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#define LFRFID_KEY_NAME_SIZE 22 +#define LFRFID_TEXT_STORE_SIZE 40 + +#define LFRFID_APP_FOLDER ANY_PATH("lfrfid") +#define LFRFID_SD_FOLDER EXT_PATH("lfrfid") +#define LFRFID_APP_FILENAME_PREFIX "RFID" +#define LFRFID_APP_FILENAME_EXTENSION ".rfid" +#define LFRFID_APP_SHADOW_FILENAME_EXTENSION ".shd" + +#define LFRFID_APP_RAW_ASK_EXTENSION ".ask.raw" +#define LFRFID_APP_RAW_PSK_EXTENSION ".psk.raw" + +enum LfRfidCustomEvent { + LfRfidEventNext = 100, + LfRfidEventExit, + LfRfidEventPopupClosed, + LfRfidEventReadSenseStart, + LfRfidEventReadSenseEnd, + LfRfidEventReadSenseCardStart, + LfRfidEventReadSenseCardEnd, + LfRfidEventReadStartASK, + LfRfidEventReadStartPSK, + LfRfidEventReadDone, + LfRfidEventReadOverrun, + LfRfidEventReadError, + LfRfidEventWriteOK, + LfRfidEventWriteProtocolCannotBeWritten, + LfRfidEventWriteFobCannotBeWritten, + LfRfidEventWriteTooLongToWrite, + LfRfidEventRpcLoadFile, + LfRfidEventRpcSessionClose, +}; + +typedef enum { + LfRfidRpcStateIdle, + LfRfidRpcStateEmulating, +} LfRfidRpcState; + +typedef struct LfRfid LfRfid; + +struct LfRfid { + LFRFIDWorker* lfworker; + ViewDispatcher* view_dispatcher; + Gui* gui; + NotificationApp* notifications; + SceneManager* scene_manager; + Storage* storage; + DialogsApp* dialogs; + Widget* widget; + + char text_store[LFRFID_TEXT_STORE_SIZE + 1]; + FuriString* file_path; + FuriString* file_name; + FuriString* raw_file_name; + + ProtocolDict* dict; + ProtocolId protocol_id; + ProtocolId protocol_id_next; + LFRFIDWorkerReadType read_type; + + uint8_t* old_key_data; + uint8_t* new_key_data; + + RpcAppSystem* rpc_ctx; + LfRfidRpcState rpc_state; + + // Common Views + Submenu* submenu; + DialogEx* dialog_ex; + Popup* popup; + TextInput* text_input; + ByteInput* byte_input; + + // Custom views + LfRfidReadView* read_view; +}; + +typedef enum { + LfRfidViewSubmenu, + LfRfidViewDialogEx, + LfRfidViewPopup, + LfRfidViewWidget, + LfRfidViewTextInput, + LfRfidViewByteInput, + LfRfidViewRead, +} LfRfidView; + +bool lfrfid_save_key(LfRfid* app); + +bool lfrfid_load_key_from_file_select(LfRfid* app); + +bool lfrfid_delete_key(LfRfid* app); + +bool lfrfid_load_key_data(LfRfid* app, FuriString* path, bool show_dialog); + +bool lfrfid_save_key_data(LfRfid* app, FuriString* path); + +void lfrfid_make_app_folder(LfRfid* app); + +void lfrfid_text_store_set(LfRfid* app, const char* text, ...); + +void lfrfid_text_store_clear(LfRfid* app); + +void lfrfid_popup_timeout_callback(void* context); + +void lfrfid_widget_callback(GuiButtonType result, InputType type, void* context); + +void lfrfid_text_input_callback(void* context); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene.c b/applications/main/lfrfid/scenes/lfrfid_scene.c new file mode 100644 index 00000000000..0de5ec36b07 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene.c @@ -0,0 +1,30 @@ +#include "lfrfid_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const lfrfid_on_enter_handlers[])(void*) = { +#include "lfrfid_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 lfrfid_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "lfrfid_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 lfrfid_on_exit_handlers[])(void* context) = { +#include "lfrfid_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers lfrfid_scene_handlers = { + .on_enter_handlers = lfrfid_on_enter_handlers, + .on_event_handlers = lfrfid_on_event_handlers, + .on_exit_handlers = lfrfid_on_exit_handlers, + .scene_num = LfRfidSceneNum, +}; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene.h b/applications/main/lfrfid/scenes/lfrfid_scene.h new file mode 100644 index 00000000000..8ce7da09c3f --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) LfRfidScene##id, +typedef enum { +#include "lfrfid_scene_config.h" + LfRfidSceneNum, +} LfRfidScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers lfrfid_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "lfrfid_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 "lfrfid_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 "lfrfid_scene_config.h" +#undef ADD_SCENE diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_config.h b/applications/main/lfrfid/scenes/lfrfid_scene_config.h new file mode 100644 index 00000000000..b77ade82f69 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_config.h @@ -0,0 +1,24 @@ +ADD_SCENE(lfrfid, start, Start) +ADD_SCENE(lfrfid, read, Read) +ADD_SCENE(lfrfid, read_success, ReadSuccess) +ADD_SCENE(lfrfid, retry_confirm, RetryConfirm) +ADD_SCENE(lfrfid, exit_confirm, ExitConfirm) +ADD_SCENE(lfrfid, delete_confirm, DeleteConfirm) +ADD_SCENE(lfrfid, read_key_menu, ReadKeyMenu) +ADD_SCENE(lfrfid, write, Write) +ADD_SCENE(lfrfid, write_success, WriteSuccess) +ADD_SCENE(lfrfid, emulate, Emulate) +ADD_SCENE(lfrfid, save_name, SaveName) +ADD_SCENE(lfrfid, save_success, SaveSuccess) +ADD_SCENE(lfrfid, select_key, SelectKey) +ADD_SCENE(lfrfid, saved_key_menu, SavedKeyMenu) +ADD_SCENE(lfrfid, save_data, SaveData) +ADD_SCENE(lfrfid, save_type, SaveType) +ADD_SCENE(lfrfid, saved_info, SavedInfo) +ADD_SCENE(lfrfid, delete_success, DeleteSuccess) +ADD_SCENE(lfrfid, extra_actions, ExtraActions) +ADD_SCENE(lfrfid, raw_info, RawInfo) +ADD_SCENE(lfrfid, raw_name, RawName) +ADD_SCENE(lfrfid, raw_read, RawRead) +ADD_SCENE(lfrfid, raw_success, RawSuccess) +ADD_SCENE(lfrfid, rpc, Rpc) diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_delete_confirm.c b/applications/main/lfrfid/scenes/lfrfid_scene_delete_confirm.c new file mode 100644 index 00000000000..b7702e26915 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_delete_confirm.c @@ -0,0 +1,68 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_delete_confirm_on_enter(void* context) { + LfRfid* app = context; + Widget* widget = app->widget; + + FuriString* tmp_string; + tmp_string = furi_string_alloc(); + + widget_add_button_element(widget, GuiButtonTypeLeft, "Back", lfrfid_widget_callback, app); + widget_add_button_element(widget, GuiButtonTypeRight, "Delete", lfrfid_widget_callback, app); + + furi_string_printf(tmp_string, "Delete %s?", furi_string_get_cstr(app->file_name)); + widget_add_string_element( + widget, 64, 0, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp_string)); + + furi_string_reset(tmp_string); + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + uint8_t* data = (uint8_t*)malloc(size); + protocol_dict_get_data(app->dict, app->protocol_id, data, size); + for(uint8_t i = 0; i < MIN(size, (size_t)8); i++) { + if(i != 0) { + furi_string_cat_printf(tmp_string, " "); + } + + furi_string_cat_printf(tmp_string, "%02X", data[i]); + } + free(data); + + widget_add_string_element( + widget, 64, 19, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp_string)); + widget_add_string_element( + widget, + 64, + 49, + AlignCenter, + AlignBottom, + FontSecondary, + protocol_dict_get_name(app->dict, app->protocol_id)); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); + furi_string_free(tmp_string); +} + +bool lfrfid_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + consumed = true; // Ignore Back button presses + } else if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeLeft) { + scene_manager_previous_scene(scene_manager); + } else if(event.event == GuiButtonTypeRight) { + lfrfid_delete_key(app); + scene_manager_next_scene(scene_manager, LfRfidSceneDeleteSuccess); + } + } + + return consumed; +} + +void lfrfid_scene_delete_confirm_on_exit(void* context) { + LfRfid* app = context; + widget_reset(app->widget); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c new file mode 100644 index 00000000000..f940b9bd436 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c @@ -0,0 +1,35 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_delete_success_on_enter(void* context) { + LfRfid* app = context; + Popup* popup = app->popup; + + popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); + popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); + popup_set_context(popup, app); + popup_set_callback(popup, lfrfid_popup_timeout_callback); + popup_set_timeout(popup, 1500); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); +} + +bool lfrfid_scene_delete_success_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + bool consumed = false; + + if((event.type == SceneManagerEventTypeBack) || + ((event.type == SceneManagerEventTypeCustom) && (event.event == LfRfidEventPopupClosed))) { + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, LfRfidSceneSelectKey); + consumed = true; + } + + return consumed; +} + +void lfrfid_scene_delete_success_on_exit(void* context) { + LfRfid* app = context; + + popup_reset(app->popup); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c new file mode 100644 index 00000000000..dc39189942f --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c @@ -0,0 +1,41 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_emulate_on_enter(void* context) { + LfRfid* app = context; + Popup* popup = app->popup; + + popup_set_header(popup, "Emulating", 89, 30, AlignCenter, AlignTop); + if(!furi_string_empty(app->file_name)) { + popup_set_text(popup, furi_string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop); + } else { + popup_set_text( + popup, + protocol_dict_get_name(app->dict, app->protocol_id), + 89, + 43, + AlignCenter, + AlignTop); + } + popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); + + lfrfid_worker_start_thread(app->lfworker); + lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id); + notification_message(app->notifications, &sequence_blink_start_magenta); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); +} + +bool lfrfid_scene_emulate_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + bool consumed = false; + return consumed; +} + +void lfrfid_scene_emulate_on_exit(void* context) { + LfRfid* app = context; + notification_message(app->notifications, &sequence_blink_stop); + popup_reset(app->popup); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_exit_confirm.c b/applications/main/lfrfid/scenes/lfrfid_scene_exit_confirm.c new file mode 100644 index 00000000000..e8ab481c6fa --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_exit_confirm.c @@ -0,0 +1,39 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_exit_confirm_on_enter(void* context) { + LfRfid* app = context; + Widget* widget = app->widget; + + 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, "Exit to RFID Menu?"); + widget_add_string_element( + widget, 64, 31, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!"); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); +} + +bool lfrfid_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + consumed = true; // Ignore Back button presses + } else if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeLeft) { + scene_manager_search_and_switch_to_previous_scene(scene_manager, LfRfidSceneStart); + } else if(event.event == GuiButtonTypeRight) { + scene_manager_previous_scene(scene_manager); + } + } + + return consumed; +} + +void lfrfid_scene_exit_confirm_on_exit(void* context) { + LfRfid* app = context; + widget_reset(app->widget); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c new file mode 100644 index 00000000000..1aed9a03c37 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c @@ -0,0 +1,82 @@ +#include "../lfrfid_i.h" +#include + +typedef enum { + SubmenuIndexASK, + SubmenuIndexPSK, + SubmenuIndexRAW, +} SubmenuIndex; + +static void lfrfid_scene_extra_actions_submenu_callback(void* context, uint32_t index) { + LfRfid* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void lfrfid_scene_extra_actions_on_enter(void* context) { + LfRfid* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Read ASK (Animal, Ordinary Card)", + SubmenuIndexASK, + lfrfid_scene_extra_actions_submenu_callback, + app); + submenu_add_item( + submenu, + "Read PSK (Indala)", + SubmenuIndexPSK, + lfrfid_scene_extra_actions_submenu_callback, + app); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + submenu_add_item( + submenu, + "Read RAW RFID data", + SubmenuIndexRAW, + lfrfid_scene_extra_actions_submenu_callback, + app); + } + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneExtraActions)); + + // clear key + furi_string_reset(app->file_name); + app->protocol_id = PROTOCOL_NO; + app->read_type = LFRFIDWorkerReadTypeAuto; + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu); +} + +bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexASK) { + app->read_type = LFRFIDWorkerReadTypeASKOnly; + scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); + dolphin_deed(DolphinDeedRfidRead); + consumed = true; + } else if(event.event == SubmenuIndexPSK) { + app->read_type = LFRFIDWorkerReadTypePSKOnly; + scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); + dolphin_deed(DolphinDeedRfidRead); + consumed = true; + } else if(event.event == SubmenuIndexRAW) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName); + consumed = true; + } + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneExtraActions, event.event); + } + + return consumed; +} + +void lfrfid_scene_extra_actions_on_exit(void* context) { + LfRfid* app = context; + + submenu_reset(app->submenu); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_raw_info.c b/applications/main/lfrfid/scenes/lfrfid_scene_raw_info.c new file mode 100644 index 00000000000..f403c1f38ed --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_raw_info.c @@ -0,0 +1,64 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_raw_info_on_enter(void* context) { + LfRfid* app = context; + Widget* widget = app->widget; + + // FuriString* tmp_string; + // tmp_string = furi_string_alloc(); + + bool sd_exist = storage_sd_status(app->storage) == FSE_OK; + if(!sd_exist) { + widget_add_icon_element(widget, 0, 0, &I_SDQuestion_35x43); + widget_add_string_multiline_element( + widget, + 81, + 4, + AlignCenter, + AlignTop, + FontSecondary, + "No SD card found.\nThis function will not\nwork without\nSD card."); + + widget_add_button_element(widget, GuiButtonTypeLeft, "Back", lfrfid_widget_callback, app); + } else { + widget_add_string_multiline_element( + widget, + 0, + 1, + AlignLeft, + AlignTop, + FontSecondary, + "RAW RFID data reader\n1) Put the Flipper on your card\n2) Press OK\n3) Wait until data is read"); + + widget_add_button_element(widget, GuiButtonTypeCenter, "OK", lfrfid_widget_callback, app); + } + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); + //furi_string_free(tmp_string); +} + +bool lfrfid_scene_raw_info_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + consumed = true; + scene_manager_search_and_switch_to_previous_scene(scene_manager, LfRfidSceneExtraActions); + } else if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeCenter) { + scene_manager_next_scene(scene_manager, LfRfidSceneRawRead); + } else if(event.event == GuiButtonTypeLeft) { + scene_manager_search_and_switch_to_previous_scene( + scene_manager, LfRfidSceneExtraActions); + } + } + + return consumed; +} + +void lfrfid_scene_raw_info_on_exit(void* context) { + LfRfid* app = context; + widget_reset(app->widget); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_raw_name.c b/applications/main/lfrfid/scenes/lfrfid_scene_raw_name.c new file mode 100644 index 00000000000..3e09dbf083c --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_raw_name.c @@ -0,0 +1,58 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_raw_name_on_enter(void* context) { + LfRfid* app = context; + TextInput* text_input = app->text_input; + + const char* key_name = furi_string_get_cstr(app->raw_file_name); + + bool key_name_is_empty = furi_string_empty(app->file_name); + if(key_name_is_empty) { + lfrfid_text_store_set(app, "RfidRecord"); + } else { + lfrfid_text_store_set(app, "%s", key_name); + } + + text_input_set_header_text(text_input, "Name the raw file"); + + text_input_set_result_callback( + text_input, + lfrfid_text_input_callback, + app, + app->text_store, + LFRFID_KEY_NAME_SIZE, + key_name_is_empty); + + ValidatorIsFile* validator_is_file = + validator_is_file_alloc_init(LFRFID_SD_FOLDER, LFRFID_APP_RAW_ASK_EXTENSION, NULL); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewTextInput); +} + +bool lfrfid_scene_raw_name_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == LfRfidEventNext) { + consumed = true; + furi_string_set(app->raw_file_name, app->text_store); + scene_manager_next_scene(scene_manager, LfRfidSceneRawInfo); + } + } + + return consumed; +} + +void lfrfid_scene_raw_name_on_exit(void* context) { + LfRfid* app = context; + TextInput* text_input = app->text_input; + + void* validator_context = text_input_get_validator_callback_context(text_input); + text_input_set_validator(text_input, NULL, NULL); + validator_is_file_free((ValidatorIsFile*)validator_context); + + text_input_reset(text_input); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c b/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c new file mode 100644 index 00000000000..b2c7c364e10 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c @@ -0,0 +1,126 @@ +#include "../lfrfid_i.h" + +#define RAW_READ_TIME 5000 + +typedef struct { + FuriString* string_file_name; + FuriTimer* timer; + bool is_psk; + bool error; +} LfRfidReadRawState; + +static void lfrfid_read_callback(LFRFIDWorkerReadRawResult result, void* context) { + LfRfid* app = context; + + if(result == LFRFIDWorkerReadRawFileError) { + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventReadError); + } else if(result == LFRFIDWorkerReadRawOverrun) { + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventReadOverrun); + } +} + +static void timer_callback(void* context) { + LfRfid* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventReadDone); +} + +void lfrfid_scene_raw_read_on_enter(void* context) { + LfRfid* app = context; + Popup* popup = app->popup; + + LfRfidReadRawState* state = malloc(sizeof(LfRfidReadRawState)); + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneRawRead, (uint32_t)state); + state->string_file_name = furi_string_alloc(); + + popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); + lfrfid_worker_start_thread(app->lfworker); + lfrfid_make_app_folder(app); + + state->timer = furi_timer_alloc(timer_callback, FuriTimerTypeOnce, app); + furi_timer_start(state->timer, RAW_READ_TIME); + furi_string_printf( + state->string_file_name, + "%s/%s%s", + LFRFID_SD_FOLDER, + furi_string_get_cstr(app->raw_file_name), + LFRFID_APP_RAW_ASK_EXTENSION); + popup_set_header(popup, "Reading\nRAW RFID\nASK", 89, 30, AlignCenter, AlignTop); + lfrfid_worker_read_raw_start( + app->lfworker, + furi_string_get_cstr(state->string_file_name), + LFRFIDWorkerReadTypeASKOnly, + lfrfid_read_callback, + app); + + notification_message(app->notifications, &sequence_blink_start_cyan); + + state->is_psk = false; + state->error = false; +} + +bool lfrfid_scene_raw_read_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + Popup* popup = app->popup; + LfRfidReadRawState* state = + (LfRfidReadRawState*)scene_manager_get_scene_state(app->scene_manager, LfRfidSceneRawRead); + bool consumed = false; + + furi_assert(state); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == LfRfidEventReadError) { + consumed = true; + state->error = true; + popup_set_header( + popup, "Reading\nRAW RFID\nFile error", 89, 30, AlignCenter, AlignTop); + notification_message(app->notifications, &sequence_blink_start_red); + furi_timer_stop(state->timer); + } else if(event.event == LfRfidEventReadDone) { + consumed = true; + if(!state->error) { + if(state->is_psk) { + notification_message(app->notifications, &sequence_success); + scene_manager_next_scene(app->scene_manager, LfRfidSceneRawSuccess); + } else { + popup_set_header( + popup, "Reading\nRAW RFID\nPSK", 89, 30, AlignCenter, AlignTop); + notification_message(app->notifications, &sequence_blink_start_yellow); + lfrfid_worker_stop(app->lfworker); + furi_string_printf( + state->string_file_name, + "%s/%s%s", + LFRFID_SD_FOLDER, + furi_string_get_cstr(app->raw_file_name), + LFRFID_APP_RAW_PSK_EXTENSION); + lfrfid_worker_read_raw_start( + app->lfworker, + furi_string_get_cstr(state->string_file_name), + LFRFIDWorkerReadTypePSKOnly, + lfrfid_read_callback, + app); + furi_timer_start(state->timer, RAW_READ_TIME); + state->is_psk = true; + } + } + } + } + + return consumed; +} + +void lfrfid_scene_raw_read_on_exit(void* context) { + LfRfid* app = context; + LfRfidReadRawState* state = + (LfRfidReadRawState*)scene_manager_get_scene_state(app->scene_manager, LfRfidSceneRawRead); + + notification_message(app->notifications, &sequence_blink_stop); + popup_reset(app->popup); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); + furi_timer_free(state->timer); + + furi_string_free(state->string_file_name); + free(state); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_raw_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_raw_success.c new file mode 100644 index 00000000000..09a005298f6 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_raw_success.c @@ -0,0 +1,39 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_raw_success_on_enter(void* context) { + LfRfid* app = context; + Widget* widget = app->widget; + + widget_add_button_element(widget, GuiButtonTypeCenter, "OK", lfrfid_widget_callback, app); + + widget_add_string_multiline_element( + widget, + 0, + 1, + AlignLeft, + AlignTop, + FontSecondary, + "RAW RFID read success!\nNow you can analyze files\nOr send them to developers"); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); +} + +bool lfrfid_scene_raw_success_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeCenter) { + scene_manager_search_and_switch_to_previous_scene( + scene_manager, LfRfidSceneExtraActions); + } + } + return consumed; +} + +void lfrfid_scene_raw_success_on_exit(void* context) { + LfRfid* app = context; + widget_reset(app->widget); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read.c b/applications/main/lfrfid/scenes/lfrfid_scene_read.c new file mode 100644 index 00000000000..d04ce41d4a6 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read.c @@ -0,0 +1,108 @@ +#include "../lfrfid_i.h" +#include + +static const NotificationSequence sequence_blink_set_yellow = { + &message_blink_set_color_yellow, + NULL, +}; + +static const NotificationSequence sequence_blink_set_green = { + &message_blink_set_color_green, + NULL, +}; + +static const NotificationSequence sequence_blink_set_cyan = { + &message_blink_set_color_cyan, + NULL, +}; + +static void + lfrfid_read_callback(LFRFIDWorkerReadResult result, ProtocolId protocol, void* context) { + LfRfid* app = context; + uint32_t event = 0; + + if(result == LFRFIDWorkerReadSenseStart) { + event = LfRfidEventReadSenseStart; + } else if(result == LFRFIDWorkerReadSenseEnd) { + event = LfRfidEventReadSenseEnd; + } else if(result == LFRFIDWorkerReadSenseCardStart) { + event = LfRfidEventReadSenseCardStart; + } else if(result == LFRFIDWorkerReadSenseCardEnd) { + event = LfRfidEventReadSenseCardEnd; + } else if(result == LFRFIDWorkerReadDone) { + event = LfRfidEventReadDone; + app->protocol_id_next = protocol; + } else if(result == LFRFIDWorkerReadStartASK) { + event = LfRfidEventReadStartASK; + } else if(result == LFRFIDWorkerReadStartPSK) { + event = LfRfidEventReadStartPSK; + } else { + return; + } + + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void lfrfid_scene_read_on_enter(void* context) { + LfRfid* app = context; + + if(app->read_type == LFRFIDWorkerReadTypePSKOnly) { + lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadPskOnly); + } else if(app->read_type == LFRFIDWorkerReadTypeASKOnly) { + lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadAskOnly); + } + + lfrfid_worker_start_thread(app->lfworker); + lfrfid_worker_read_start(app->lfworker, app->read_type, lfrfid_read_callback, app); + + notification_message(app->notifications, &sequence_blink_start_cyan); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewRead); +} + +bool lfrfid_scene_read_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == LfRfidEventReadSenseStart) { + notification_message(app->notifications, &sequence_blink_set_yellow); + consumed = true; + } else if(event.event == LfRfidEventReadSenseCardStart) { + notification_message(app->notifications, &sequence_blink_set_green); + consumed = true; + } else if( + (event.event == LfRfidEventReadSenseEnd) || + (event.event == LfRfidEventReadSenseCardEnd)) { + notification_message(app->notifications, &sequence_blink_set_cyan); + consumed = true; + } else if(event.event == LfRfidEventReadDone) { + app->protocol_id = app->protocol_id_next; + notification_message(app->notifications, &sequence_success); + furi_string_reset(app->file_name); + scene_manager_next_scene(app->scene_manager, LfRfidSceneReadSuccess); + dolphin_deed(DolphinDeedRfidReadSuccess); + consumed = true; + } else if(event.event == LfRfidEventReadStartPSK) { + if(app->read_type == LFRFIDWorkerReadTypeAuto) { + lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadPsk); + } + consumed = true; + } else if(event.event == LfRfidEventReadStartASK) { + if(app->read_type == LFRFIDWorkerReadTypeAuto) { + lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadAsk); + } + consumed = true; + } + } + + return consumed; +} + +void lfrfid_scene_read_on_exit(void* context) { + LfRfid* app = context; + notification_message(app->notifications, &sequence_blink_stop); + popup_reset(app->popup); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c new file mode 100644 index 00000000000..36f0d6d93a3 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c @@ -0,0 +1,60 @@ +#include "../lfrfid_i.h" +#include + +typedef enum { + SubmenuIndexSave, + SubmenuIndexEmulate, + SubmenuIndexWrite, +} SubmenuIndex; + +void lfrfid_scene_read_key_menu_submenu_callback(void* context, uint32_t index) { + LfRfid* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void lfrfid_scene_read_key_menu_on_enter(void* context) { + LfRfid* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, "Save", SubmenuIndexSave, lfrfid_scene_read_key_menu_submenu_callback, app); + submenu_add_item( + submenu, "Emulate", SubmenuIndexEmulate, lfrfid_scene_read_key_menu_submenu_callback, app); + submenu_add_item( + submenu, "Write", SubmenuIndexWrite, lfrfid_scene_read_key_menu_submenu_callback, app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu)); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu); +} + +bool lfrfid_scene_read_key_menu_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexWrite) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite); + consumed = true; + } else if(event.event == SubmenuIndexSave) { + furi_string_reset(app->file_name); + scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexEmulate) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); + dolphin_deed(DolphinDeedRfidEmulate); + consumed = true; + } + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu, event.event); + } + + return consumed; +} + +void lfrfid_scene_read_key_menu_on_exit(void* context) { + LfRfid* app = context; + + submenu_reset(app->submenu); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_read_success.c new file mode 100644 index 00000000000..b83ef4a3992 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read_success.c @@ -0,0 +1,78 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_read_success_on_enter(void* context) { + LfRfid* app = context; + Widget* widget = app->widget; + + FuriString* tmp_string; + tmp_string = furi_string_alloc(); + + widget_add_button_element(widget, GuiButtonTypeLeft, "Retry", lfrfid_widget_callback, app); + widget_add_button_element(widget, GuiButtonTypeRight, "More", lfrfid_widget_callback, app); + + furi_string_printf( + tmp_string, + "%s[%s]", + protocol_dict_get_name(app->dict, app->protocol_id), + protocol_dict_get_manufacturer(app->dict, app->protocol_id)); + + widget_add_string_element( + widget, 0, 2, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(tmp_string)); + + furi_string_reset(tmp_string); + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + uint8_t* data = (uint8_t*)malloc(size); + protocol_dict_get_data(app->dict, app->protocol_id, data, size); + for(uint8_t i = 0; i < size; i++) { + if(i >= 9) { + furi_string_cat_printf(tmp_string, ".."); + break; + } else { + if(i != 0) { + furi_string_cat_printf(tmp_string, " "); + } + furi_string_cat_printf(tmp_string, "%02X", data[i]); + } + } + free(data); + + FuriString* render_data; + render_data = furi_string_alloc(); + protocol_dict_render_brief_data(app->dict, render_data, app->protocol_id); + furi_string_cat_printf(tmp_string, "\r\n%s", furi_string_get_cstr(render_data)); + furi_string_free(render_data); + + widget_add_string_multiline_element( + widget, 0, 16, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp_string)); + + notification_message_block(app->notifications, &sequence_set_green_255); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); + furi_string_free(tmp_string); +} + +bool lfrfid_scene_read_success_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(scene_manager, LfRfidSceneExitConfirm); + consumed = true; + } else if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeLeft) { + scene_manager_next_scene(scene_manager, LfRfidSceneRetryConfirm); + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(scene_manager, LfRfidSceneReadKeyMenu); + } + } + + return consumed; +} + +void lfrfid_scene_read_success_on_exit(void* context) { + LfRfid* app = context; + notification_message_block(app->notifications, &sequence_reset_green); + widget_reset(app->widget); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_retry_confirm.c b/applications/main/lfrfid/scenes/lfrfid_scene_retry_confirm.c new file mode 100644 index 00000000000..ddac3e8ba55 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_retry_confirm.c @@ -0,0 +1,39 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_retry_confirm_on_enter(void* context) { + LfRfid* app = context; + Widget* widget = app->widget; + + 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, "Retry Reading?"); + widget_add_string_element( + widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!"); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); +} + +bool lfrfid_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + consumed = true; // Ignore Back button presses + } else if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeLeft) { + scene_manager_search_and_switch_to_previous_scene(scene_manager, LfRfidSceneRead); + } else if(event.event == GuiButtonTypeRight) { + scene_manager_previous_scene(scene_manager); + } + } + + return consumed; +} + +void lfrfid_scene_retry_confirm_on_exit(void* context) { + LfRfid* app = context; + widget_reset(app->widget); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c b/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c new file mode 100644 index 00000000000..906218d74f0 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c @@ -0,0 +1,66 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_rpc_on_enter(void* context) { + LfRfid* app = context; + Popup* popup = app->popup; + + popup_set_header(popup, "LF RFID", 89, 42, AlignCenter, AlignBottom); + popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); + + notification_message(app->notifications, &sequence_display_backlight_on); + + app->rpc_state = LfRfidRpcStateIdle; +} + +bool lfrfid_scene_rpc_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + Popup* popup = app->popup; + UNUSED(event); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == LfRfidEventExit) { + rpc_system_app_confirm(app->rpc_ctx, true); + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } else if(event.event == LfRfidEventRpcSessionClose) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } else if(event.event == LfRfidEventRpcLoadFile) { + bool result = false; + if(app->rpc_state == LfRfidRpcStateIdle) { + if(lfrfid_load_key_data(app, app->file_path, false)) { + lfrfid_worker_start_thread(app->lfworker); + lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id); + app->rpc_state = LfRfidRpcStateEmulating; + + lfrfid_text_store_set( + app, "emulating\n%s", furi_string_get_cstr(app->file_name)); + popup_set_text(popup, app->text_store, 89, 44, AlignCenter, AlignTop); + + notification_message(app->notifications, &sequence_blink_start_magenta); + result = true; + } + } + rpc_system_app_confirm(app->rpc_ctx, result); + } + } + return consumed; +} + +void lfrfid_scene_rpc_on_exit(void* context) { + LfRfid* app = context; + Popup* popup = app->popup; + + if(app->rpc_state == LfRfidRpcStateEmulating) { + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); + notification_message(app->notifications, &sequence_blink_stop); + } + + popup_reset(popup); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_data.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_data.c new file mode 100644 index 00000000000..11a687bdd4d --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_data.c @@ -0,0 +1,48 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_save_data_on_enter(void* context) { + LfRfid* app = context; + ByteInput* byte_input = app->byte_input; + + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + + bool need_restore = scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSaveData); + + if(!need_restore) { + protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size); + protocol_dict_get_data(app->dict, app->protocol_id, app->new_key_data, size); + } + + byte_input_set_header_text(byte_input, "Enter the data in hex"); + + byte_input_set_result_callback( + byte_input, lfrfid_text_input_callback, NULL, app, app->new_key_data, size); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewByteInput); +} + +bool lfrfid_scene_save_data_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == LfRfidEventNext) { + consumed = true; + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + protocol_dict_set_data(app->dict, app->protocol_id, app->new_key_data, size); + scene_manager_next_scene(scene_manager, LfRfidSceneSaveName); + scene_manager_set_scene_state(scene_manager, LfRfidSceneSaveData, 1); + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_set_scene_state(scene_manager, LfRfidSceneSaveData, 0); + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size); + } + + return consumed; +} + +void lfrfid_scene_save_data_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c new file mode 100644 index 00000000000..3a38e213de1 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c @@ -0,0 +1,88 @@ +#include "../lfrfid_i.h" +#include +#include + +void lfrfid_scene_save_name_on_enter(void* context) { + LfRfid* app = context; + TextInput* text_input = app->text_input; + FuriString* folder_path; + folder_path = furi_string_alloc(); + + bool key_name_is_empty = furi_string_empty(app->file_name); + if(key_name_is_empty) { + furi_string_set(app->file_path, LFRFID_APP_FOLDER); + + name_generator_make_auto( + app->text_store, LFRFID_TEXT_STORE_SIZE, LFRFID_APP_FILENAME_PREFIX); + + furi_string_set(folder_path, LFRFID_APP_FOLDER); + } else { + lfrfid_text_store_set(app, "%s", furi_string_get_cstr(app->file_name)); + path_extract_dirname(furi_string_get_cstr(app->file_path), folder_path); + } + + text_input_set_header_text(text_input, "Name the card"); + text_input_set_result_callback( + text_input, + lfrfid_text_input_callback, + app, + app->text_store, + LFRFID_KEY_NAME_SIZE, + key_name_is_empty); + + FURI_LOG_I("", "%s %s", furi_string_get_cstr(folder_path), app->text_store); + + ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( + furi_string_get_cstr(folder_path), + LFRFID_APP_FILENAME_EXTENSION, + furi_string_get_cstr(app->file_name)); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + furi_string_free(folder_path); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewTextInput); +} + +bool lfrfid_scene_save_name_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == LfRfidEventNext) { + consumed = true; + if(!furi_string_empty(app->file_name)) { + lfrfid_delete_key(app); + } + + furi_string_set(app->file_name, app->text_store); + + if(lfrfid_save_key(app)) { + scene_manager_next_scene(scene_manager, LfRfidSceneSaveSuccess); + if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSavedKeyMenu)) { + // Nothing, do not count editing as saving + } else if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSaveType)) { + dolphin_deed(DolphinDeedRfidAdd); + } else { + dolphin_deed(DolphinDeedRfidSave); + } + } else { + scene_manager_search_and_switch_to_previous_scene( + scene_manager, LfRfidSceneReadKeyMenu); + } + } + } + + return consumed; +} + +void lfrfid_scene_save_name_on_exit(void* context) { + LfRfid* app = context; + TextInput* text_input = app->text_input; + + void* validator_context = text_input_get_validator_callback_context(text_input); + text_input_set_validator(text_input, NULL, NULL); + validator_is_file_free((ValidatorIsFile*)validator_context); + + text_input_reset(text_input); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c new file mode 100644 index 00000000000..52aefa84899 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c @@ -0,0 +1,44 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_save_success_on_enter(void* context) { + LfRfid* app = context; + Popup* popup = app->popup; + + // Clear state of data enter scene + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveData, 0); + + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); + popup_set_context(popup, app); + popup_set_callback(popup, lfrfid_popup_timeout_callback); + popup_set_timeout(popup, 1500); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); +} + +bool lfrfid_scene_save_success_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + bool consumed = false; + + const uint32_t prev_scenes[] = {LfRfidSceneReadKeyMenu, LfRfidSceneSelectKey}; + + if((event.type == SceneManagerEventTypeBack) || + ((event.type == SceneManagerEventTypeCustom) && (event.event == LfRfidEventPopupClosed))) { + bool result = scene_manager_search_and_switch_to_previous_scene_one_of( + app->scene_manager, prev_scenes, COUNT_OF(prev_scenes)); + if(!result) { + scene_manager_search_and_switch_to_another_scene( + app->scene_manager, LfRfidSceneSelectKey); + } + consumed = true; + } + + return consumed; +} + +void lfrfid_scene_save_success_on_exit(void* context) { + LfRfid* app = context; + + popup_reset(app->popup); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_type.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_type.c new file mode 100644 index 00000000000..dd20ae4890e --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_type.c @@ -0,0 +1,80 @@ +#include "../lfrfid_i.h" + +typedef struct { + uint32_t line_sel; +} SaveTypeCtx; + +static void lfrfid_scene_save_type_submenu_callback(void* context, uint32_t index) { + LfRfid* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void lfrfid_scene_save_type_on_enter(void* context) { + LfRfid* app = context; + Submenu* submenu = app->submenu; + + SaveTypeCtx* state = malloc(sizeof(SaveTypeCtx)); + FuriString* protocol_string = furi_string_alloc(); + for(uint8_t i = 0; i < LFRFIDProtocolMax; i++) { + if(strcmp( + protocol_dict_get_manufacturer(app->dict, i), + protocol_dict_get_name(app->dict, i)) != 0) { + furi_string_printf( + protocol_string, + "%s %s", + protocol_dict_get_manufacturer(app->dict, i), + protocol_dict_get_name(app->dict, i)); + } else { + furi_string_printf(protocol_string, "%s", protocol_dict_get_name(app->dict, i)); + } + submenu_add_item( + submenu, + furi_string_get_cstr(protocol_string), + i, + lfrfid_scene_save_type_submenu_callback, + app); + } + furi_string_free(protocol_string); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSaveType)); + + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveType, (uint32_t)state); + + // clear key name + furi_string_reset(app->file_name); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu); +} + +bool lfrfid_scene_save_type_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + bool consumed = false; + + SaveTypeCtx* state = + (SaveTypeCtx*)scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSaveType); + furi_check(state); + + if(event.type == SceneManagerEventTypeCustom) { + app->protocol_id = event.event; + state->line_sel = event.event; + scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveData); + consumed = true; + } + + return consumed; +} + +void lfrfid_scene_save_type_on_exit(void* context) { + LfRfid* app = context; + SaveTypeCtx* state = + (SaveTypeCtx*)scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSaveType); + furi_check(state); + + submenu_reset(app->submenu); + + uint32_t line_sel = state->line_sel; + free(state); + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveType, line_sel); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c b/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c new file mode 100644 index 00000000000..3f1c2d400e4 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c @@ -0,0 +1,51 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_saved_info_on_enter(void* context) { + LfRfid* app = context; + Widget* widget = app->widget; + + FuriString* tmp_string; + tmp_string = furi_string_alloc(); + + furi_string_printf( + tmp_string, + "%s [%s]\r\n", + furi_string_get_cstr(app->file_name), + protocol_dict_get_name(app->dict, app->protocol_id)); + + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + uint8_t* data = (uint8_t*)malloc(size); + protocol_dict_get_data(app->dict, app->protocol_id, data, size); + for(uint8_t i = 0; i < size; i++) { + if(i != 0) { + furi_string_cat_printf(tmp_string, " "); + } + + furi_string_cat_printf(tmp_string, "%02X", data[i]); + } + free(data); + + FuriString* render_data; + render_data = furi_string_alloc(); + protocol_dict_render_data(app->dict, render_data, app->protocol_id); + furi_string_cat_printf(tmp_string, "\r\n%s", furi_string_get_cstr(render_data)); + furi_string_free(render_data); + + widget_add_string_multiline_element( + widget, 0, 1, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp_string)); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); + furi_string_free(tmp_string); +} + +bool lfrfid_scene_saved_info_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + bool consumed = false; + return consumed; +} + +void lfrfid_scene_saved_info_on_exit(void* context) { + LfRfid* app = context; + widget_reset(app->widget); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c new file mode 100644 index 00000000000..206074e9b08 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c @@ -0,0 +1,71 @@ +#include "../lfrfid_i.h" +#include + +typedef enum { + SubmenuIndexEmulate, + SubmenuIndexWrite, + SubmenuIndexEdit, + SubmenuIndexDelete, + SubmenuIndexInfo, +} SubmenuIndex; + +static void lfrfid_scene_saved_key_menu_submenu_callback(void* context, uint32_t index) { + LfRfid* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void lfrfid_scene_saved_key_menu_on_enter(void* context) { + LfRfid* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, "Emulate", SubmenuIndexEmulate, lfrfid_scene_saved_key_menu_submenu_callback, app); + submenu_add_item( + submenu, "Write", SubmenuIndexWrite, lfrfid_scene_saved_key_menu_submenu_callback, app); + submenu_add_item( + submenu, "Edit", SubmenuIndexEdit, lfrfid_scene_saved_key_menu_submenu_callback, app); + submenu_add_item( + submenu, "Delete", SubmenuIndexDelete, lfrfid_scene_saved_key_menu_submenu_callback, app); + submenu_add_item( + submenu, "Info", SubmenuIndexInfo, lfrfid_scene_saved_key_menu_submenu_callback, app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSavedKeyMenu)); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu); +} + +bool lfrfid_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexEmulate) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); + dolphin_deed(DolphinDeedRfidEmulate); + consumed = true; + } else if(event.event == SubmenuIndexWrite) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite); + consumed = true; + } else if(event.event == SubmenuIndexEdit) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveData); + consumed = true; + } else if(event.event == SubmenuIndexDelete) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneDeleteConfirm); + consumed = true; + } else if(event.event == SubmenuIndexInfo) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneSavedInfo); + consumed = true; + } + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSavedKeyMenu, event.event); + } + + return consumed; +} + +void lfrfid_scene_saved_key_menu_on_exit(void* context) { + LfRfid* app = context; + + submenu_reset(app->submenu); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_select_key.c b/applications/main/lfrfid/scenes/lfrfid_scene_select_key.c new file mode 100644 index 00000000000..2a9cc1c634a --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_select_key.c @@ -0,0 +1,22 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_select_key_on_enter(void* context) { + LfRfid* app = context; + + if(lfrfid_load_key_from_file_select(app)) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneSavedKeyMenu); + } else { + scene_manager_previous_scene(app->scene_manager); + } +} + +bool lfrfid_scene_select_key_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + bool consumed = false; + return consumed; +} + +void lfrfid_scene_select_key_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_start.c b/applications/main/lfrfid/scenes/lfrfid_scene_start.c new file mode 100644 index 00000000000..8a01fc707b1 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_start.c @@ -0,0 +1,81 @@ +#include "../lfrfid_i.h" +#include + +typedef enum { + SubmenuIndexRead, + SubmenuIndexSaved, + SubmenuIndexAddManually, + SubmenuIndexExtraActions, +} SubmenuIndex; + +static void lfrfid_scene_start_submenu_callback(void* context, uint32_t index) { + LfRfid* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void lfrfid_scene_start_on_enter(void* context) { + LfRfid* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item(submenu, "Read", SubmenuIndexRead, lfrfid_scene_start_submenu_callback, app); + submenu_add_item( + submenu, "Saved", SubmenuIndexSaved, lfrfid_scene_start_submenu_callback, app); + submenu_add_item( + submenu, "Add Manually", SubmenuIndexAddManually, lfrfid_scene_start_submenu_callback, app); + submenu_add_item( + submenu, + "Extra Actions", + SubmenuIndexExtraActions, + lfrfid_scene_start_submenu_callback, + app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneStart)); + + // clear key + furi_string_reset(app->file_name); + app->protocol_id = PROTOCOL_NO; + app->read_type = LFRFIDWorkerReadTypeAuto; + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu); +} + +bool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + bool consumed = false; + + 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; + } + } + + return consumed; +} + +void lfrfid_scene_start_on_exit(void* context) { + LfRfid* app = context; + + submenu_reset(app->submenu); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write.c b/applications/main/lfrfid/scenes/lfrfid_scene_write.c new file mode 100644 index 00000000000..b7faed69ff4 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_write.c @@ -0,0 +1,94 @@ +#include "../lfrfid_i.h" + +static void lfrfid_write_callback(LFRFIDWorkerWriteResult result, void* context) { + LfRfid* app = context; + uint32_t event = 0; + + if(result == LFRFIDWorkerWriteOK) { + event = LfRfidEventWriteOK; + } else if(result == LFRFIDWorkerWriteProtocolCannotBeWritten) { + event = LfRfidEventWriteProtocolCannotBeWritten; + } else if(result == LFRFIDWorkerWriteFobCannotBeWritten) { + event = LfRfidEventWriteFobCannotBeWritten; + } else if(result == LFRFIDWorkerWriteTooLongToWrite) { + event = LfRfidEventWriteTooLongToWrite; + } + + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void lfrfid_scene_write_on_enter(void* context) { + LfRfid* app = context; + Popup* popup = app->popup; + + popup_set_header(popup, "Writing", 89, 30, AlignCenter, AlignTop); + if(!furi_string_empty(app->file_name)) { + popup_set_text(popup, furi_string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop); + } else { + popup_set_text( + popup, + protocol_dict_get_name(app->dict, app->protocol_id), + 89, + 43, + AlignCenter, + AlignTop); + } + popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); + + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size); + + lfrfid_worker_start_thread(app->lfworker); + lfrfid_worker_write_start( + app->lfworker, (LFRFIDProtocol)app->protocol_id, lfrfid_write_callback, app); + notification_message(app->notifications, &sequence_blink_start_magenta); +} + +bool lfrfid_scene_write_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + Popup* popup = app->popup; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == LfRfidEventWriteOK) { + notification_message(app->notifications, &sequence_success); + scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteSuccess); + consumed = true; + } else if(event.event == LfRfidEventWriteProtocolCannotBeWritten) { + popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); + popup_set_header(popup, "Error", 64, 3, AlignCenter, AlignTop); + popup_set_text(popup, "This protocol\ncannot be written", 3, 17, AlignLeft, AlignTop); + notification_message(app->notifications, &sequence_blink_start_red); + consumed = true; + } else if( + (event.event == LfRfidEventWriteFobCannotBeWritten) || + (event.event == LfRfidEventWriteTooLongToWrite)) { + popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); + popup_set_header(popup, "Still trying to write...", 64, 3, AlignCenter, AlignTop); + popup_set_text( + popup, + "Make sure this\ncard is writable\nand not\nprotected.", + 3, + 17, + AlignLeft, + AlignTop); + notification_message(app->notifications, &sequence_blink_start_yellow); + consumed = true; + } + } + + return consumed; +} + +void lfrfid_scene_write_on_exit(void* context) { + LfRfid* app = context; + notification_message(app->notifications, &sequence_blink_stop); + popup_reset(app->popup); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); + + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size); +} diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c new file mode 100644 index 00000000000..52e30d6b666 --- /dev/null +++ b/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c @@ -0,0 +1,38 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_write_success_on_enter(void* context) { + LfRfid* app = context; + Popup* popup = app->popup; + + popup_set_header(popup, "Successfully\nwritten!", 94, 3, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); + popup_set_context(popup, app); + popup_set_callback(popup, lfrfid_popup_timeout_callback); + popup_set_timeout(popup, 1500); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); + notification_message_block(app->notifications, &sequence_set_green_255); +} + +bool lfrfid_scene_write_success_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + bool consumed = false; + + const uint32_t prev_scenes[] = {LfRfidSceneReadKeyMenu, LfRfidSceneSelectKey}; + + if((event.type == SceneManagerEventTypeBack) || + ((event.type == SceneManagerEventTypeCustom) && (event.event == LfRfidEventPopupClosed))) { + scene_manager_search_and_switch_to_previous_scene_one_of( + app->scene_manager, prev_scenes, COUNT_OF(prev_scenes)); + consumed = true; + } + + return consumed; +} + +void lfrfid_scene_write_success_on_exit(void* context) { + LfRfid* app = context; + notification_message_block(app->notifications, &sequence_reset_green); + popup_reset(app->popup); +} diff --git a/applications/main/lfrfid/views/lfrfid_view_read.c b/applications/main/lfrfid/views/lfrfid_view_read.c new file mode 100644 index 00000000000..094afb61758 --- /dev/null +++ b/applications/main/lfrfid/views/lfrfid_view_read.c @@ -0,0 +1,113 @@ +#include "lfrfid_view_read.h" +#include +#include + +#define TEMP_STR_LEN 128 + +struct LfRfidReadView { + View* view; +}; + +typedef struct { + IconAnimation* icon; + LfRfidReadViewMode read_mode; +} LfRfidReadViewModel; + +static void lfrfid_view_read_draw_callback(Canvas* canvas, void* _model) { + LfRfidReadViewModel* model = _model; + canvas_set_color(canvas, ColorBlack); + + canvas_draw_icon(canvas, 0, 8, &I_NFC_manual_60x50); + + canvas_set_font(canvas, FontPrimary); + + if(model->read_mode == LfRfidReadAsk) { + canvas_draw_str(canvas, 70, 16, "Reading 1/2"); + + canvas_draw_str(canvas, 77, 29, "ASK"); + canvas_draw_icon(canvas, 70, 22, &I_ButtonRight_4x7); + canvas_draw_icon_animation(canvas, 102, 21, model->icon); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 77, 43, "PSK"); + } else if(model->read_mode == LfRfidReadPsk) { + canvas_draw_str(canvas, 70, 16, "Reading 2/2"); + + canvas_draw_str(canvas, 77, 43, "PSK"); + canvas_draw_icon(canvas, 70, 36, &I_ButtonRight_4x7); + canvas_draw_icon_animation(canvas, 102, 35, model->icon); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 77, 29, "ASK"); + } else { + canvas_draw_str(canvas, 72, 16, "Reading"); + + if(model->read_mode == LfRfidReadAskOnly) { + canvas_draw_str(canvas, 77, 35, "ASK"); + } else { + canvas_draw_str(canvas, 77, 35, "PSK"); + } + canvas_draw_icon_animation(canvas, 102, 27, model->icon); + } + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 61, 56, "Don't move card"); +} + +void lfrfid_view_read_enter(void* context) { + LfRfidReadView* read_view = context; + with_view_model( + read_view->view, LfRfidReadViewModel * model, { icon_animation_start(model->icon); }, true); +} + +void lfrfid_view_read_exit(void* context) { + LfRfidReadView* read_view = context; + with_view_model( + read_view->view, LfRfidReadViewModel * model, { icon_animation_stop(model->icon); }, false); +} + +LfRfidReadView* lfrfid_view_read_alloc() { + LfRfidReadView* read_view = malloc(sizeof(LfRfidReadView)); + read_view->view = view_alloc(); + view_set_context(read_view->view, read_view); + view_allocate_model(read_view->view, ViewModelTypeLocking, sizeof(LfRfidReadViewModel)); + + with_view_model( + read_view->view, + LfRfidReadViewModel * model, + { + model->icon = icon_animation_alloc(&A_Round_loader_8x8); + view_tie_icon_animation(read_view->view, model->icon); + }, + false); + + view_set_draw_callback(read_view->view, lfrfid_view_read_draw_callback); + view_set_enter_callback(read_view->view, lfrfid_view_read_enter); + view_set_exit_callback(read_view->view, lfrfid_view_read_exit); + + return read_view; +} + +void lfrfid_view_read_free(LfRfidReadView* read_view) { + with_view_model( + read_view->view, LfRfidReadViewModel * model, { icon_animation_free(model->icon); }, false); + + view_free(read_view->view); + free(read_view); +} + +View* lfrfid_view_read_get_view(LfRfidReadView* read_view) { + return read_view->view; +} + +void lfrfid_view_read_set_read_mode(LfRfidReadView* read_view, LfRfidReadViewMode mode) { + with_view_model( + read_view->view, + LfRfidReadViewModel * model, + { + icon_animation_stop(model->icon); + icon_animation_start(model->icon); + model->read_mode = mode; + }, + true); +} diff --git a/applications/main/lfrfid/views/lfrfid_view_read.h b/applications/main/lfrfid/views/lfrfid_view_read.h new file mode 100644 index 00000000000..55bb1f230fa --- /dev/null +++ b/applications/main/lfrfid/views/lfrfid_view_read.h @@ -0,0 +1,19 @@ +#pragma once +#include + +typedef enum { + LfRfidReadAsk, + LfRfidReadPsk, + LfRfidReadAskOnly, + LfRfidReadPskOnly +} LfRfidReadViewMode; + +typedef struct LfRfidReadView LfRfidReadView; + +LfRfidReadView* lfrfid_view_read_alloc(); + +void lfrfid_view_read_free(LfRfidReadView* read_view); + +View* lfrfid_view_read_get_view(LfRfidReadView* read_view); + +void lfrfid_view_read_set_read_mode(LfRfidReadView* read_view, LfRfidReadViewMode mode); diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam new file mode 100644 index 00000000000..9a98b57c8c3 --- /dev/null +++ b/applications/main/nfc/application.fam @@ -0,0 +1,84 @@ +App( + appid="nfc", + name="NFC", + apptype=FlipperAppType.MENUEXTERNAL, + targets=["f7"], + entry_point="nfc_app", + icon="A_NFC_14", + stack_size=5 * 1024, + order=30, + resources="resources", + sources=[ + "*.c", + "!plugins", + "!nfc_cli.c", + ], + fap_libs=["assets", "mbedtls"], + fap_icon="icon.png", + fap_category="NFC", +) + +# Parser plugins + +App( + appid="all_in_one_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="all_in_one_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/all_in_one.c"], +) + +App( + appid="opal_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="opal_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/opal.c"], +) + +App( + appid="myki_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="myki_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/myki.c"], +) + +App( + appid="troika_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="troika_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/troika.c"], +) + +App( + appid="plantain_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="plantain_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/plantain.c"], +) + +App( + appid="two_cities_parser", + apptype=FlipperAppType.PLUGIN, + entry_point="two_cities_plugin_ep", + targets=["f7"], + requires=["nfc"], + sources=["plugins/supported_cards/two_cities.c"], +) + +App( + appid="nfc_start", + targets=["f7"], + apptype=FlipperAppType.STARTUP, + entry_point="nfc_on_system_start", + sources=["nfc_cli.c"], + order=30, +) diff --git a/applications/main/nfc/helpers/mf_classic_key_cache.c b/applications/main/nfc/helpers/mf_classic_key_cache.c new file mode 100644 index 00000000000..5127e645209 --- /dev/null +++ b/applications/main/nfc/helpers/mf_classic_key_cache.c @@ -0,0 +1,212 @@ +#include "mf_classic_key_cache.h" + +#include +#include + +#define NFC_APP_KEYS_EXTENSION ".keys" +#define NFC_APP_KEY_CACHE_FOLDER "/ext/nfc/.cache" + +static const char* mf_classic_key_cache_file_header = "Flipper NFC keys"; +static const uint32_t mf_classic_key_cache_file_version = 1; + +struct MfClassicKeyCache { + MfClassicDeviceKeys keys; + MfClassicKeyType current_key_type; + uint8_t current_sector; +}; + +static void nfc_get_key_cache_file_path(const uint8_t* uid, size_t uid_len, FuriString* path) { + furi_string_printf(path, "%s/", NFC_APP_KEY_CACHE_FOLDER); + for(size_t i = 0; i < uid_len; i++) { + furi_string_cat_printf(path, "%02X", uid[i]); + } + furi_string_cat_printf(path, "%s", NFC_APP_KEYS_EXTENSION); +} + +MfClassicKeyCache* mf_classic_key_cache_alloc() { + MfClassicKeyCache* instance = malloc(sizeof(MfClassicKeyCache)); + + return instance; +} + +void mf_classic_key_cache_free(MfClassicKeyCache* instance) { + furi_assert(instance); + + free(instance); +} + +bool mf_classic_key_cache_save(MfClassicKeyCache* instance, const MfClassicData* data) { + UNUSED(instance); + furi_assert(data); + + size_t uid_len = 0; + const uint8_t* uid = mf_classic_get_uid(data, &uid_len); + FuriString* file_path = furi_string_alloc(); + nfc_get_key_cache_file_path(uid, uid_len, file_path); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + + FuriString* temp_str = furi_string_alloc(); + bool save_success = false; + do { + if(!storage_simply_mkdir(storage, NFC_APP_KEY_CACHE_FOLDER)) break; + if(!storage_simply_remove(storage, furi_string_get_cstr(file_path))) break; + if(!flipper_format_buffered_file_open_always(ff, furi_string_get_cstr(file_path))) break; + + if(!flipper_format_write_header_cstr( + ff, mf_classic_key_cache_file_header, mf_classic_key_cache_file_version)) + break; + if(!flipper_format_write_string_cstr( + ff, "Mifare Classic type", mf_classic_get_device_name(data, NfcDeviceNameTypeShort))) + break; + if(!flipper_format_write_hex_uint64(ff, "Key A map", &data->key_a_mask, 1)) break; + if(!flipper_format_write_hex_uint64(ff, "Key B map", &data->key_b_mask, 1)) break; + + uint8_t sector_num = mf_classic_get_total_sectors_num(data->type); + bool key_save_success = true; + for(size_t i = 0; (i < sector_num) && (key_save_success); i++) { + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); + if(FURI_BIT(data->key_a_mask, i)) { + furi_string_printf(temp_str, "Key A sector %d", i); + key_save_success = flipper_format_write_hex( + ff, furi_string_get_cstr(temp_str), sec_tr->key_a.data, sizeof(MfClassicKey)); + } + if(!key_save_success) break; + if(FURI_BIT(data->key_b_mask, i)) { + furi_string_printf(temp_str, "Key B sector %d", i); + key_save_success = flipper_format_write_hex( + ff, furi_string_get_cstr(temp_str), sec_tr->key_b.data, sizeof(MfClassicKey)); + } + } + save_success = key_save_success; + } while(false); + + flipper_format_free(ff); + furi_string_free(temp_str); + furi_string_free(file_path); + furi_record_close(RECORD_STORAGE); + + return save_success; +} + +bool mf_classic_key_cache_load(MfClassicKeyCache* instance, const uint8_t* uid, size_t uid_len) { + furi_assert(instance); + furi_assert(uid); + + mf_classic_key_cache_reset(instance); + + FuriString* file_path = furi_string_alloc(); + nfc_get_key_cache_file_path(uid, uid_len, file_path); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + + FuriString* temp_str = furi_string_alloc(); + bool load_success = false; + do { + if(!flipper_format_buffered_file_open_existing(ff, furi_string_get_cstr(file_path))) break; + + uint32_t version = 0; + if(!flipper_format_read_header(ff, temp_str, &version)) break; + if(furi_string_cmp_str(temp_str, mf_classic_key_cache_file_header)) break; + if(version != mf_classic_key_cache_file_version) break; + + if(!flipper_format_read_hex_uint64(ff, "Key A map", &instance->keys.key_a_mask, 1)) break; + if(!flipper_format_read_hex_uint64(ff, "Key B map", &instance->keys.key_b_mask, 1)) break; + + bool key_read_success = true; + for(size_t i = 0; (i < MF_CLASSIC_TOTAL_SECTORS_MAX) && (key_read_success); i++) { + if(FURI_BIT(instance->keys.key_a_mask, i)) { + furi_string_printf(temp_str, "Key A sector %d", i); + key_read_success = flipper_format_read_hex( + ff, + furi_string_get_cstr(temp_str), + instance->keys.key_a[i].data, + sizeof(MfClassicKey)); + } + if(!key_read_success) break; + if(FURI_BIT(instance->keys.key_b_mask, i)) { + furi_string_printf(temp_str, "Key B sector %d", i); + key_read_success = flipper_format_read_hex( + ff, + furi_string_get_cstr(temp_str), + instance->keys.key_b[i].data, + sizeof(MfClassicKey)); + } + } + load_success = key_read_success; + } while(false); + + flipper_format_buffered_file_close(ff); + flipper_format_free(ff); + furi_string_free(temp_str); + furi_string_free(file_path); + furi_record_close(RECORD_STORAGE); + + return load_success; +} + +void mf_classic_key_cache_load_from_data(MfClassicKeyCache* instance, const MfClassicData* data) { + furi_assert(instance); + furi_assert(data); + + mf_classic_key_cache_reset(instance); + instance->keys.key_a_mask = data->key_a_mask; + instance->keys.key_b_mask = data->key_b_mask; + for(size_t i = 0; i < MF_CLASSIC_TOTAL_SECTORS_MAX; i++) { + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); + + if(FURI_BIT(data->key_a_mask, i)) { + instance->keys.key_a[i] = sec_tr->key_a; + } + if(FURI_BIT(data->key_b_mask, i)) { + instance->keys.key_b[i] = sec_tr->key_b; + } + } +} + +bool mf_classic_key_cahce_get_next_key( + MfClassicKeyCache* instance, + uint8_t* sector_num, + MfClassicKey* key, + MfClassicKeyType* key_type) { + furi_assert(instance); + furi_assert(sector_num); + furi_assert(key); + furi_assert(key_type); + + bool next_key_found = false; + for(uint8_t i = instance->current_sector; i < MF_CLASSIC_TOTAL_SECTORS_MAX; i++) { + if(FURI_BIT(instance->keys.key_a_mask, i)) { + FURI_BIT_CLEAR(instance->keys.key_a_mask, i); + *key = instance->keys.key_a[i]; + *key_type = MfClassicKeyTypeA; + *sector_num = i; + + next_key_found = true; + break; + } + if(FURI_BIT(instance->keys.key_b_mask, i)) { + FURI_BIT_CLEAR(instance->keys.key_b_mask, i); + *key = instance->keys.key_b[i]; + *key_type = MfClassicKeyTypeB; + *sector_num = i; + + next_key_found = true; + instance->current_sector = i; + break; + } + } + + return next_key_found; +} + +void mf_classic_key_cache_reset(MfClassicKeyCache* instance) { + furi_assert(instance); + + instance->current_key_type = MfClassicKeyTypeA; + instance->current_sector = 0; + instance->keys.key_a_mask = 0; + instance->keys.key_b_mask = 0; +} diff --git a/applications/main/nfc/helpers/mf_classic_key_cache.h b/applications/main/nfc/helpers/mf_classic_key_cache.h new file mode 100644 index 00000000000..407c6e28bd4 --- /dev/null +++ b/applications/main/nfc/helpers/mf_classic_key_cache.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MfClassicKeyCache MfClassicKeyCache; + +MfClassicKeyCache* mf_classic_key_cache_alloc(); + +void mf_classic_key_cache_free(MfClassicKeyCache* instance); + +bool mf_classic_key_cache_load(MfClassicKeyCache* instance, const uint8_t* uid, size_t uid_len); + +void mf_classic_key_cache_load_from_data(MfClassicKeyCache* instance, const MfClassicData* data); + +bool mf_classic_key_cahce_get_next_key( + MfClassicKeyCache* instance, + uint8_t* sector_num, + MfClassicKey* key, + MfClassicKeyType* key_type); + +bool mf_classic_key_cache_save(MfClassicKeyCache* instance, const MfClassicData* data); + +void mf_classic_key_cache_reset(MfClassicKeyCache* instance); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/nfc/helpers/mf_ultralight_auth.c b/applications/main/nfc/helpers/mf_ultralight_auth.c new file mode 100644 index 00000000000..5e0c67887f6 --- /dev/null +++ b/applications/main/nfc/helpers/mf_ultralight_auth.c @@ -0,0 +1,58 @@ +#include "mf_ultralight_auth.h" + +#include +#include + +MfUltralightAuth* mf_ultralight_auth_alloc() { + MfUltralightAuth* instance = malloc(sizeof(MfUltralightAuth)); + + return instance; +} + +void mf_ultralight_auth_free(MfUltralightAuth* instance) { + furi_assert(instance); + + free(instance); +} + +void mf_ultralight_auth_reset(MfUltralightAuth* instance) { + furi_assert(instance); + + instance->type = MfUltralightAuthTypeNone; + memset(&instance->password, 0, sizeof(MfUltralightAuthPassword)); + memset(&instance->pack, 0, sizeof(MfUltralightAuthPack)); +} + +bool mf_ultralight_generate_amiibo_pass(MfUltralightAuth* instance, uint8_t* uid, uint16_t uid_len) { + furi_assert(instance); + furi_assert(uid); + + bool generated = false; + if(uid_len == 7) { + instance->password.data[0] = uid[1] ^ uid[3] ^ 0xAA; + instance->password.data[1] = uid[2] ^ uid[4] ^ 0x55; + instance->password.data[2] = uid[3] ^ uid[5] ^ 0xAA; + instance->password.data[3] = uid[4] ^ uid[6] ^ 0x55; + generated = true; + } + + return generated; +} + +bool mf_ultralight_generate_xiaomi_pass(MfUltralightAuth* instance, uint8_t* uid, uint16_t uid_len) { + furi_assert(instance); + furi_assert(uid); + + uint8_t hash[20]; + bool generated = false; + if(uid_len == 7) { + mbedtls_sha1(uid, uid_len, hash); + instance->password.data[0] = (hash[hash[0] % 20]); + instance->password.data[1] = (hash[(hash[0] + 5) % 20]); + instance->password.data[2] = (hash[(hash[0] + 13) % 20]); + instance->password.data[3] = (hash[(hash[0] + 17) % 20]); + generated = true; + } + + return generated; +} diff --git a/applications/main/nfc/helpers/mf_ultralight_auth.h b/applications/main/nfc/helpers/mf_ultralight_auth.h new file mode 100644 index 00000000000..2ddfcafe12b --- /dev/null +++ b/applications/main/nfc/helpers/mf_ultralight_auth.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + MfUltralightAuthTypeNone, + MfUltralightAuthTypeReader, + MfUltralightAuthTypeManual, + MfUltralightAuthTypeXiaomi, + MfUltralightAuthTypeAmiibo, +} MfUltralightAuthType; + +typedef struct { + MfUltralightAuthType type; + MfUltralightAuthPassword password; + MfUltralightAuthPack pack; +} MfUltralightAuth; + +MfUltralightAuth* mf_ultralight_auth_alloc(); + +void mf_ultralight_auth_free(MfUltralightAuth* instance); + +void mf_ultralight_auth_reset(MfUltralightAuth* instance); + +bool mf_ultralight_generate_amiibo_pass(MfUltralightAuth* instance, uint8_t* uid, uint16_t uid_len); + +bool mf_ultralight_generate_xiaomi_pass(MfUltralightAuth* instance, uint8_t* uid, uint16_t uid_len); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/nfc/helpers/mf_user_dict.c b/applications/main/nfc/helpers/mf_user_dict.c new file mode 100644 index 00000000000..09f0c1506e6 --- /dev/null +++ b/applications/main/nfc/helpers/mf_user_dict.c @@ -0,0 +1,83 @@ +#include "mf_user_dict.h" + +#include +#include +#include + +#define NFC_APP_FOLDER ANY_PATH("nfc") +#define NFC_APP_MF_CLASSIC_DICT_USER_PATH (NFC_APP_FOLDER "/assets/mf_classic_dict_user.nfc") + +struct MfUserDict { + size_t keys_num; + MfClassicKey* keys_arr; +}; + +MfUserDict* mf_user_dict_alloc(size_t max_keys_to_load) { + MfUserDict* instance = malloc(sizeof(MfUserDict)); + + NfcDict* dict = nfc_dict_alloc( + NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); + furi_assert(dict); + + size_t dict_keys_num = nfc_dict_get_total_keys(dict); + instance->keys_num = MIN(max_keys_to_load, dict_keys_num); + + if(instance->keys_num > 0) { + instance->keys_arr = malloc(instance->keys_num * sizeof(MfClassicKey)); + for(size_t i = 0; i < instance->keys_num; i++) { + bool key_loaded = + nfc_dict_get_next_key(dict, instance->keys_arr[i].data, sizeof(MfClassicKey)); + furi_assert(key_loaded); + } + } + nfc_dict_free(dict); + + return instance; +} + +void mf_user_dict_free(MfUserDict* instance) { + furi_assert(instance); + + if(instance->keys_num > 0) { + free(instance->keys_arr); + } + free(instance); +} + +size_t mf_user_dict_get_keys_cnt(MfUserDict* instance) { + furi_assert(instance); + + return instance->keys_num; +} + +void mf_user_dict_get_key_str(MfUserDict* instance, uint32_t index, FuriString* str) { + furi_assert(instance); + furi_assert(str); + furi_assert(index < instance->keys_num); + furi_assert(instance->keys_arr); + + furi_string_reset(str); + for(size_t i = 0; i < sizeof(MfClassicKey); i++) { + furi_string_cat_printf(str, "%02X", instance->keys_arr[index].data[i]); + } +} + +bool mf_user_dict_delete_key(MfUserDict* instance, uint32_t index) { + furi_assert(instance); + furi_assert(index < instance->keys_num); + furi_assert(instance->keys_arr); + + NfcDict* dict = nfc_dict_alloc( + NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); + furi_assert(dict); + + bool key_delete_success = + nfc_dict_delete_key(dict, instance->keys_arr[index].data, sizeof(MfClassicKey)); + nfc_dict_free(dict); + + if(key_delete_success) { + instance->keys_num--; + } + + return key_delete_success; +} diff --git a/applications/main/nfc/helpers/mf_user_dict.h b/applications/main/nfc/helpers/mf_user_dict.h new file mode 100644 index 00000000000..d482d2d5f78 --- /dev/null +++ b/applications/main/nfc/helpers/mf_user_dict.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MfUserDict MfUserDict; + +MfUserDict* mf_user_dict_alloc(size_t max_keys_to_load); + +void mf_user_dict_free(MfUserDict* instance); + +size_t mf_user_dict_get_keys_cnt(MfUserDict* instance); + +void mf_user_dict_get_key_str(MfUserDict* instance, uint32_t index, FuriString* str); + +bool mf_user_dict_delete_key(MfUserDict* instance, uint32_t index); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/nfc/helpers/mfkey32_logger.c b/applications/main/nfc/helpers/mfkey32_logger.c new file mode 100644 index 00000000000..8024179f809 --- /dev/null +++ b/applications/main/nfc/helpers/mfkey32_logger.c @@ -0,0 +1,173 @@ +#include "mfkey32_logger.h" + +#include + +#include +#include +#include + +#define MFKEY32_LOGGER_MAX_NONCES_SAVED (100) + +typedef struct { + bool is_filled; + uint32_t cuid; + uint8_t sector_num; + MfClassicKeyType key_type; + uint32_t nt0; + uint32_t nr0; + uint32_t ar0; + uint32_t nt1; + uint32_t nr1; + uint32_t ar1; +} Mfkey32LoggerParams; + +ARRAY_DEF(Mfkey32LoggerParams, Mfkey32LoggerParams, M_POD_OPLIST); + +struct Mfkey32Logger { + uint32_t cuid; + Mfkey32LoggerParams_t params_arr; + size_t nonces_saves; + size_t params_collected; +}; + +Mfkey32Logger* mfkey32_logger_alloc(uint32_t cuid) { + Mfkey32Logger* instance = malloc(sizeof(Mfkey32Logger)); + instance->cuid = cuid; + Mfkey32LoggerParams_init(instance->params_arr); + + return instance; +} + +void mfkey32_logger_free(Mfkey32Logger* instance) { + furi_assert(instance); + furi_assert(instance->params_arr); + + Mfkey32LoggerParams_clear(instance->params_arr); + free(instance); +} + +static bool mfkey32_logger_add_nonce_to_existing_params( + Mfkey32Logger* instance, + MfClassicAuthContext* auth_context) { + bool nonce_added = false; + do { + if(Mfkey32LoggerParams_size(instance->params_arr) == 0) break; + + Mfkey32LoggerParams_it_t it; + for(Mfkey32LoggerParams_it(it, instance->params_arr); !Mfkey32LoggerParams_end_p(it); + Mfkey32LoggerParams_next(it)) { + Mfkey32LoggerParams* params = Mfkey32LoggerParams_ref(it); + if(params->is_filled) continue; + + uint8_t sector_num = mf_classic_get_sector_by_block(auth_context->block_num); + if(params->sector_num != sector_num) continue; + if(params->key_type != auth_context->key_type) continue; + + params->nt1 = nfc_util_bytes2num(auth_context->nt.data, sizeof(MfClassicNt)); + params->nr1 = nfc_util_bytes2num(auth_context->nr.data, sizeof(MfClassicNr)); + params->ar1 = nfc_util_bytes2num(auth_context->ar.data, sizeof(MfClassicAr)); + params->is_filled = true; + + instance->params_collected++; + nonce_added = true; + break; + } + + } while(false); + + return nonce_added; +} + +void mfkey32_logger_add_nonce(Mfkey32Logger* instance, MfClassicAuthContext* auth_context) { + furi_assert(instance); + furi_assert(auth_context); + + bool nonce_added = mfkey32_logger_add_nonce_to_existing_params(instance, auth_context); + if(!nonce_added && (instance->nonces_saves < MFKEY32_LOGGER_MAX_NONCES_SAVED)) { + uint8_t sector_num = mf_classic_get_sector_by_block(auth_context->block_num); + Mfkey32LoggerParams params = { + .is_filled = false, + .cuid = instance->cuid, + .sector_num = sector_num, + .key_type = auth_context->key_type, + .nt0 = nfc_util_bytes2num(auth_context->nt.data, sizeof(MfClassicNt)), + .nr0 = nfc_util_bytes2num(auth_context->nr.data, sizeof(MfClassicNr)), + .ar0 = nfc_util_bytes2num(auth_context->ar.data, sizeof(MfClassicAr)), + }; + Mfkey32LoggerParams_push_back(instance->params_arr, params); + instance->nonces_saves++; + } +} + +size_t mfkey32_logger_get_params_num(Mfkey32Logger* instance) { + furi_assert(instance); + + return instance->params_collected; +} + +bool mfkey32_logger_save_params(Mfkey32Logger* instance, const char* path) { + furi_assert(instance); + furi_assert(path); + furi_assert(instance->params_collected > 0); + furi_assert(instance->params_arr); + + bool params_saved = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + Stream* stream = buffered_file_stream_alloc(storage); + FuriString* temp_str = furi_string_alloc(); + + do { + if(!buffered_file_stream_open(stream, path, FSAM_WRITE, FSOM_OPEN_APPEND)) break; + + bool params_write_success = true; + Mfkey32LoggerParams_it_t it; + for(Mfkey32LoggerParams_it(it, instance->params_arr); !Mfkey32LoggerParams_end_p(it); + Mfkey32LoggerParams_next(it)) { + Mfkey32LoggerParams* params = Mfkey32LoggerParams_ref(it); + if(!params->is_filled) continue; + furi_string_printf( + temp_str, + "Sec %d key %c cuid %08lx nt0 %08lx nr0 %08lx ar0 %08lx nt1 %08lx nr1 %08lx ar1 %08lx\n", + params->sector_num, + params->key_type == MfClassicKeyTypeA ? 'A' : 'B', + params->cuid, + params->nt0, + params->nr0, + params->ar0, + params->nt1, + params->nr1, + params->ar1); + if(!stream_write_string(stream, temp_str)) { + params_write_success = false; + break; + } + } + if(!params_write_success) break; + + params_saved = true; + } while(false); + + furi_string_free(temp_str); + buffered_file_stream_close(stream); + stream_free(stream); + furi_record_close(RECORD_STORAGE); + + return params_saved; +} + +void mfkey32_logger_get_params_data(Mfkey32Logger* instance, FuriString* str) { + furi_assert(instance); + furi_assert(str); + furi_assert(instance->params_collected > 0); + + furi_string_reset(str); + Mfkey32LoggerParams_it_t it; + for(Mfkey32LoggerParams_it(it, instance->params_arr); !Mfkey32LoggerParams_end_p(it); + Mfkey32LoggerParams_next(it)) { + Mfkey32LoggerParams* params = Mfkey32LoggerParams_ref(it); + if(!params->is_filled) continue; + + char key_char = params->key_type == MfClassicKeyTypeA ? 'A' : 'B'; + furi_string_cat_printf(str, "Sector %d, key %c\n", params->sector_num, key_char); + } +} diff --git a/applications/main/nfc/helpers/mfkey32_logger.h b/applications/main/nfc/helpers/mfkey32_logger.h new file mode 100644 index 00000000000..12b42e1fb51 --- /dev/null +++ b/applications/main/nfc/helpers/mfkey32_logger.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Mfkey32Logger Mfkey32Logger; + +Mfkey32Logger* mfkey32_logger_alloc(uint32_t cuid); + +void mfkey32_logger_free(Mfkey32Logger* instance); + +void mfkey32_logger_add_nonce(Mfkey32Logger* instance, MfClassicAuthContext* auth_context); + +size_t mfkey32_logger_get_params_num(Mfkey32Logger* instance); + +bool mfkey32_logger_save_params(Mfkey32Logger* instance, const char* path); + +void mfkey32_logger_get_params_data(Mfkey32Logger* instance, FuriString* str); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/nfc/helpers/nfc_custom_event.h b/applications/main/nfc/helpers/nfc_custom_event.h new file mode 100644 index 00000000000..16fbc47492b --- /dev/null +++ b/applications/main/nfc/helpers/nfc_custom_event.h @@ -0,0 +1,33 @@ +#pragma once + +typedef enum { + // Reserve first 100 events for button types and indexes, starting from 0 + NfcCustomEventReserved = 100, + + // Mf classic dict attack events + NfcCustomEventDictAttackComplete, + NfcCustomEventDictAttackSkip, + NfcCustomEventDictAttackDataUpdate, + + NfcCustomEventCardDetected, + NfcCustomEventCardLost, + + NfcCustomEventViewExit, + NfcCustomEventWorkerExit, + NfcCustomEventWorkerUpdate, + NfcCustomEventWrongCard, + NfcCustomEventTimerExpired, + NfcCustomEventByteInputDone, + NfcCustomEventTextInputDone, + NfcCustomEventDictAttackDone, + + NfcCustomEventRpcLoadFile, + NfcCustomEventRpcExit, + NfcCustomEventRpcSessionClose, + + NfcCustomEventPollerSuccess, + NfcCustomEventPollerIncomplete, + NfcCustomEventPollerFailure, + + NfcCustomEventListenerUpdate, +} NfcCustomEvent; diff --git a/applications/main/nfc/helpers/nfc_emv_parser.c b/applications/main/nfc/helpers/nfc_emv_parser.c new file mode 100644 index 00000000000..30e102405e0 --- /dev/null +++ b/applications/main/nfc/helpers/nfc_emv_parser.c @@ -0,0 +1,82 @@ +#include "nfc_emv_parser.h" +#include + +static const char* nfc_resources_header = "Flipper EMV resources"; +static const uint32_t nfc_resources_file_version = 1; + +static bool nfc_emv_parser_search_data( + Storage* storage, + const char* file_name, + FuriString* key, + FuriString* data) { + bool parsed = false; + FlipperFormat* file = flipper_format_file_alloc(storage); + FuriString* temp_str; + temp_str = furi_string_alloc(); + + do { + // Open file + if(!flipper_format_file_open_existing(file, file_name)) break; + // Read file header and version + uint32_t version = 0; + if(!flipper_format_read_header(file, temp_str, &version)) break; + if(furi_string_cmp_str(temp_str, nfc_resources_header) || + (version != nfc_resources_file_version)) + break; + if(!flipper_format_read_string(file, furi_string_get_cstr(key), data)) break; + parsed = true; + } while(false); + + furi_string_free(temp_str); + flipper_format_free(file); + return parsed; +} + +bool nfc_emv_parser_get_aid_name( + Storage* storage, + uint8_t* aid, + uint8_t aid_len, + FuriString* aid_name) { + furi_assert(storage); + bool parsed = false; + FuriString* key; + key = furi_string_alloc(); + for(uint8_t i = 0; i < aid_len; i++) { + furi_string_cat_printf(key, "%02X", aid[i]); + } + if(nfc_emv_parser_search_data(storage, EXT_PATH("nfc/assets/aid.nfc"), key, aid_name)) { + parsed = true; + } + furi_string_free(key); + return parsed; +} + +bool nfc_emv_parser_get_country_name( + Storage* storage, + uint16_t country_code, + FuriString* country_name) { + bool parsed = false; + FuriString* key; + key = furi_string_alloc_printf("%04X", country_code); + if(nfc_emv_parser_search_data( + storage, EXT_PATH("nfc/assets/country_code.nfc"), key, country_name)) { + parsed = true; + } + furi_string_free(key); + return parsed; +} + +bool nfc_emv_parser_get_currency_name( + Storage* storage, + uint16_t currency_code, + FuriString* currency_name) { + bool parsed = false; + FuriString* key; + key = furi_string_alloc_printf("%04X", currency_code); + if(nfc_emv_parser_search_data( + storage, EXT_PATH("nfc/assets/currency_code.nfc"), key, currency_name)) { + parsed = true; + } + furi_string_free(key); + return parsed; +} diff --git a/applications/nfc/helpers/nfc_emv_parser.h b/applications/main/nfc/helpers/nfc_emv_parser.h old mode 100755 new mode 100644 similarity index 91% rename from applications/nfc/helpers/nfc_emv_parser.h rename to applications/main/nfc/helpers/nfc_emv_parser.h index 5948ed34066..abe57f470fd --- a/applications/nfc/helpers/nfc_emv_parser.h +++ b/applications/main/nfc/helpers/nfc_emv_parser.h @@ -2,7 +2,6 @@ #include #include -#include #include /** Get EMV application name by number @@ -16,7 +15,7 @@ bool nfc_emv_parser_get_aid_name( Storage* storage, uint8_t* aid, uint8_t aid_len, - string_t aid_name); + FuriString* aid_name); /** Get country name by country code * @param storage Storage instance @@ -27,7 +26,7 @@ bool nfc_emv_parser_get_aid_name( bool nfc_emv_parser_get_country_name( Storage* storage, uint16_t country_code, - string_t country_name); + FuriString* country_name); /** Get currency name by currency code * @param storage Storage instance @@ -38,4 +37,4 @@ bool nfc_emv_parser_get_country_name( bool nfc_emv_parser_get_currency_name( Storage* storage, uint16_t currency_code, - string_t currency_name); + FuriString* currency_name); diff --git a/applications/main/nfc/helpers/nfc_supported_cards.c b/applications/main/nfc/helpers/nfc_supported_cards.c new file mode 100644 index 00000000000..a242ba3ae6d --- /dev/null +++ b/applications/main/nfc/helpers/nfc_supported_cards.c @@ -0,0 +1,147 @@ +#include "nfc_supported_cards.h" +#include "../plugins/supported_cards/nfc_supported_card_plugin.h" + +#include +#include +#include + +#include +#include + +#define TAG "NfcSupportedCards" + +#define NFC_SUPPORTED_CARDS_PLUGINS_PATH APP_DATA_PATH("plugins") +#define NFC_SUPPORTED_CARDS_PLUGIN_SUFFIX "_parser.fal" + +typedef struct { + Storage* storage; + File* directory; + FuriString* file_path; + char file_name[256]; + FlipperApplication* app; +} NfcSupportedCards; + +static NfcSupportedCards* nfc_supported_cards_alloc() { + NfcSupportedCards* instance = malloc(sizeof(NfcSupportedCards)); + + instance->storage = furi_record_open(RECORD_STORAGE); + instance->directory = storage_file_alloc(instance->storage); + instance->file_path = furi_string_alloc(); + + if(!storage_dir_open(instance->directory, NFC_SUPPORTED_CARDS_PLUGINS_PATH)) { + FURI_LOG_D(TAG, "Failed to open directory: %s", NFC_SUPPORTED_CARDS_PLUGINS_PATH); + } + + return instance; +} + +static void nfc_supported_cards_free(NfcSupportedCards* instance) { + if(instance->app) { + flipper_application_free(instance->app); + } + + furi_string_free(instance->file_path); + + storage_dir_close(instance->directory); + storage_file_free(instance->directory); + + furi_record_close(RECORD_STORAGE); + free(instance); +} + +static const NfcSupportedCardsPlugin* + nfc_supported_cards_get_next_plugin(NfcSupportedCards* instance) { + const NfcSupportedCardsPlugin* plugin = NULL; + + do { + if(!storage_file_is_open(instance->directory)) break; + if(!storage_dir_read( + instance->directory, NULL, instance->file_name, sizeof(instance->file_name))) + break; + + furi_string_set(instance->file_path, instance->file_name); + if(!furi_string_end_with_str(instance->file_path, NFC_SUPPORTED_CARDS_PLUGIN_SUFFIX)) + continue; + + path_concat(NFC_SUPPORTED_CARDS_PLUGINS_PATH, instance->file_name, instance->file_path); + + if(instance->app) flipper_application_free(instance->app); + instance->app = flipper_application_alloc(instance->storage, firmware_api_interface); + + if(flipper_application_preload(instance->app, furi_string_get_cstr(instance->file_path)) != + FlipperApplicationPreloadStatusSuccess) + continue; + if(!flipper_application_is_plugin(instance->app)) continue; + + if(flipper_application_map_to_memory(instance->app) != FlipperApplicationLoadStatusSuccess) + continue; + + const FlipperAppPluginDescriptor* descriptor = + flipper_application_plugin_get_descriptor(instance->app); + + if(descriptor == NULL) continue; + + if(strcmp(descriptor->appid, NFC_SUPPORTED_CARD_PLUGIN_APP_ID) != 0) continue; + if(descriptor->ep_api_version != NFC_SUPPORTED_CARD_PLUGIN_API_VERSION) continue; + + plugin = descriptor->entry_point; + } while(plugin == NULL); //-V654 + + return plugin; +} + +bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc) { + furi_assert(device); + furi_assert(nfc); + + bool card_read = false; + + NfcSupportedCards* supported_cards = nfc_supported_cards_alloc(); + + do { + const NfcSupportedCardsPlugin* plugin = + nfc_supported_cards_get_next_plugin(supported_cards); + if(plugin == NULL) break; //-V547 + + const NfcProtocol protocol = nfc_device_get_protocol(device); //-V779 + if(plugin->protocol != protocol) continue; + + if(plugin->verify) { + if(!plugin->verify(nfc)) continue; + } + + if(plugin->read) { + card_read = plugin->read(nfc, device); + } + + } while(!card_read); + + nfc_supported_cards_free(supported_cards); + return card_read; +} + +bool nfc_supported_cards_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + bool parsed = false; + + NfcSupportedCards* supported_cards = nfc_supported_cards_alloc(); + + do { + const NfcSupportedCardsPlugin* plugin = + nfc_supported_cards_get_next_plugin(supported_cards); + if(plugin == NULL) break; //-V547 + + const NfcProtocol protocol = nfc_device_get_protocol(device); //-V779 + if(plugin->protocol != protocol) continue; + + if(plugin->parse) { + parsed = plugin->parse(device, parsed_data); + } + + } while(!parsed); + + nfc_supported_cards_free(supported_cards); + return parsed; +} diff --git a/applications/main/nfc/helpers/nfc_supported_cards.h b/applications/main/nfc/helpers/nfc_supported_cards.h new file mode 100644 index 00000000000..0c25a5b1180 --- /dev/null +++ b/applications/main/nfc/helpers/nfc_supported_cards.h @@ -0,0 +1,50 @@ +/** + * @file nfc_supported_cards.h + * @brief Supported card plugin loader interface. + * + * @see nfc_supported_card_plugin.h for instructions on adding a new plugin. + */ +#pragma once + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Read the card using a custom procedure. + * + * This function will load all suitable supported card plugins one by one and + * try to execute the custom read procedure specified in each. Upon first success, + * no further attempts will be made and the function will return. + * + * @param[in,out] device pointer to a device instance to hold the read data. + * @param[in,out] nfc pointer to an Nfc instance. + * @returns true if the card was successfully read, false otherwise. + * + * @see NfcSupportedCardPluginRead for detailed description. + */ +bool nfc_supported_cards_read(NfcDevice* device, Nfc* nfc); + +/** + * @brief Parse raw data into human-readable representation. + * + * This function will load all suitable supported card plugins one by one and + * try to parse the data according to each implementation. Upon first success, + * no further attempts will be made and the function will return. + * + * @param[in] device pointer to a device instance holding the data is to be parsed. + * @param[out] parsed_data pointer to the string to contain the formatted result. + * @returns true if the card was successfully parsed, false otherwise. + * + * @see NfcSupportedCardPluginParse for detailed description. + */ +bool nfc_supported_cards_parse(const NfcDevice* device, FuriString* parsed_data); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.c b/applications/main/nfc/helpers/protocol_support/felica/felica.c new file mode 100644 index 00000000000..f9c84912161 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.c @@ -0,0 +1,114 @@ +#include "felica.h" +#include "felica_render.h" + +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_common.h" +#include "../nfc_protocol_support_gui_common.h" + +static void nfc_scene_info_on_enter_felica(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_felica_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand nfc_scene_read_poller_callback_felica(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolFelica); + + NfcApp* instance = context; + const FelicaPollerEvent* felica_event = event.event_data; + + if(felica_event->type == FelicaPollerEventTypeReady) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolFelica, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_felica(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_felica, instance); +} + +static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const FelicaData* data = nfc_device_get_data(device, NfcProtocolFelica); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_felica_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static bool nfc_scene_saved_menu_on_event_felica(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexCommonEdit) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); + return true; + } + + return false; +} + +static void nfc_scene_emulate_on_enter_felica(NfcApp* instance) { + const FelicaData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolFelica); + instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolFelica, data); + nfc_listener_start(instance->listener, NULL, NULL); +} + +const NfcProtocolSupportBase nfc_protocol_support_felica = { + .features = NfcProtocolFeatureEmulateUid, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_felica, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_felica, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_felica, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_saved_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_scene_saved_menu_on_event_felica, + }, + .scene_save_name = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_felica, + .on_event = nfc_protocol_support_common_on_event_empty, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.h b/applications/main/nfc/helpers/protocol_support/felica/felica.h new file mode 100644 index 00000000000..e581b6709e7 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_felica; diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica_render.c b/applications/main/nfc/helpers/protocol_support/felica/felica_render.c new file mode 100644 index 00000000000..3142b2c6dbf --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/felica/felica_render.c @@ -0,0 +1,19 @@ +#include "felica_render.h" + +void nfc_render_felica_info( + const FelicaData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + furi_string_cat_printf(str, "IDm:"); + + for(size_t i = 0; i < FELICA_IDM_SIZE; i++) { + furi_string_cat_printf(str, " %02X", data->idm.data[i]); + } + + if(format_type == NfcProtocolFormatTypeFull) { + furi_string_cat_printf(str, "\nPMm:"); + for(size_t i = 0; i < FELICA_PMM_SIZE; ++i) { + furi_string_cat_printf(str, " %02X", data->pmm.data[i]); + } + } +} diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica_render.h b/applications/main/nfc/helpers/protocol_support/felica/felica_render.h new file mode 100644 index 00000000000..6d9816fc663 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/felica/felica_render.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_felica_info( + const FelicaData* data, + NfcProtocolFormatType format_type, + FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c new file mode 100644 index 00000000000..c0d502d0380 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.c @@ -0,0 +1,145 @@ +#include "iso14443_3a.h" +#include "iso14443_3a_render.h" + +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_common.h" +#include "../nfc_protocol_support_gui_common.h" + +static void nfc_scene_info_on_enter_iso14443_3a(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso14443_3aData* data = nfc_device_get_data(device, NfcProtocolIso14443_3a); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_iso14443_3a_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand + nfc_scene_read_poller_callback_iso14443_3a(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_3a); + + NfcApp* instance = context; + const Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data; + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolIso14443_3a, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_iso14443_3a(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_iso14443_3a, instance); +} + +static void nfc_scene_read_success_on_enter_iso14443_3a(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso14443_3aData* data = nfc_device_get_data(device, NfcProtocolIso14443_3a); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_iso14443_3a_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand + nfc_scene_emulate_listener_callback_iso14443_3a(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + furi_assert(event.event_data); + + NfcApp* nfc = context; + Iso14443_3aListenerEvent* iso14443_3a_event = event.event_data; + + if(iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame) { + if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) { + furi_string_cat_printf(nfc->text_box_store, "R:"); + for(size_t i = 0; i < bit_buffer_get_size_bytes(iso14443_3a_event->data->buffer); + i++) { + furi_string_cat_printf( + nfc->text_box_store, + " %02X", + bit_buffer_get_byte(iso14443_3a_event->data->buffer, i)); + } + furi_string_push_back(nfc->text_box_store, '\n'); + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); + } + } + + return NfcCommandContinue; +} + +static void nfc_scene_emulate_on_enter_iso14443_3a(NfcApp* instance) { + const Iso14443_3aData* data = + nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_3a); + + instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_3a, data); + nfc_listener_start( + instance->listener, nfc_scene_emulate_listener_callback_iso14443_3a, instance); +} + +static bool nfc_scene_read_menu_on_event_iso14443_3a(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexCommonEmulate) { + scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); + return true; + } + + return false; +} + +const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a = { + .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEditUid, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_iso14443_3a, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_iso14443_3a, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_scene_read_menu_on_event_iso14443_3a, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_iso14443_3a, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_saved_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_save_name = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_iso14443_3a, + .on_event = nfc_protocol_support_common_on_event_empty, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.h b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.h new file mode 100644 index 00000000000..d085f25c77b --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_iso14443_3a; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c new file mode 100644 index 00000000000..7306f1072d6 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.c @@ -0,0 +1,34 @@ +#include "iso14443_3a_render.h" + +void nfc_render_iso14443_3a_format_bytes(FuriString* str, const uint8_t* const data, size_t size) { + for(size_t i = 0; i < size; i++) { + furi_string_cat_printf(str, " %02X", data[i]); + } +} + +void nfc_render_iso14443_3a_info( + const Iso14443_3aData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + if(format_type == NfcProtocolFormatTypeFull) { + const char iso_type = iso14443_3a_supports_iso14443_4(data) ? '4' : '3'; + furi_string_cat_printf(str, "ISO 14443-%c (NFC-A)\n", iso_type); + } + + nfc_render_iso14443_3a_brief(data, str); + + if(format_type == NfcProtocolFormatTypeFull) { + nfc_render_iso14443_3a_extra(data, str); + } +} + +void nfc_render_iso14443_3a_brief(const Iso14443_3aData* data, FuriString* str) { + furi_string_cat_printf(str, "UID:"); + + nfc_render_iso14443_3a_format_bytes(str, data->uid, data->uid_len); +} + +void nfc_render_iso14443_3a_extra(const Iso14443_3aData* data, FuriString* str) { + furi_string_cat_printf(str, "\nATQA: %02X %02X ", data->atqa[1], data->atqa[0]); + furi_string_cat_printf(str, "SAK: %02X", data->sak); +} diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.h b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.h new file mode 100644 index 00000000000..14b91d221d7 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3a/iso14443_3a_render.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_iso14443_3a_info( + const Iso14443_3aData* data, + NfcProtocolFormatType format_type, + FuriString* str); + +void nfc_render_iso14443_3a_format_bytes(FuriString* str, const uint8_t* const data, size_t size); + +void nfc_render_iso14443_3a_brief(const Iso14443_3aData* data, FuriString* str); + +void nfc_render_iso14443_3a_extra(const Iso14443_3aData* data, FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c new file mode 100644 index 00000000000..fee23184624 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.c @@ -0,0 +1,113 @@ +#include "iso14443_3b.h" +#include "iso14443_3b_render.h" + +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_common.h" +#include "../nfc_protocol_support_gui_common.h" + +static void nfc_scene_info_on_enter_iso14443_3b(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso14443_3bData* data = nfc_device_get_data(device, NfcProtocolIso14443_3b); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_iso14443_3b_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand + nfc_scene_read_poller_callback_iso14443_3b(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_3b); + + NfcApp* instance = context; + const Iso14443_3bPollerEvent* iso14443_3b_event = event.event_data; + + if(iso14443_3b_event->type == Iso14443_3bPollerEventTypeReady) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolIso14443_3b, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_iso14443_3b(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_iso14443_3b, instance); +} + +static void nfc_scene_read_success_on_enter_iso14443_3b(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso14443_3bData* data = nfc_device_get_data(device, NfcProtocolIso14443_3b); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_iso14443_3b_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexCommonEdit) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); + return true; + } + + return false; +} + +static bool nfc_scene_saved_menu_on_event_iso14443_3b(NfcApp* instance, uint32_t event) { + return nfc_scene_saved_menu_on_event_iso14443_3b_common(instance, event); +} + +const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b = { + .features = NfcProtocolFeatureNone, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_iso14443_3b, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_iso14443_3b, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_iso14443_3b, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_saved_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_scene_saved_menu_on_event_iso14443_3b, + }, + .scene_save_name = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_emulate = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.h b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.h new file mode 100644 index 00000000000..bf3caa0c0e3 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_iso14443_3b; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h new file mode 100644 index 00000000000..53fe6b3927a --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_i.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include "iso14443_3b.h" + +bool nfc_scene_saved_menu_on_event_iso14443_3b_common(NfcApp* instance, uint32_t event); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.c b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.c new file mode 100644 index 00000000000..2e81d57a48b --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.c @@ -0,0 +1,78 @@ +#include "iso14443_3b_render.h" + +void nfc_render_iso14443_3b_info( + const Iso14443_3bData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + if(format_type == NfcProtocolFormatTypeFull) { + const char iso_type = iso14443_3b_supports_iso14443_4(data) ? '4' : '3'; + furi_string_cat_printf(str, "ISO 14443-%c (NFC-B)\n", iso_type); + } + + furi_string_cat_printf(str, "UID:"); + + size_t uid_size; + const uint8_t* uid = iso14443_3b_get_uid(data, &uid_size); + + for(size_t i = 0; i < uid_size; i++) { + furi_string_cat_printf(str, " %02X", uid[i]); + } + + if(format_type != NfcProtocolFormatTypeFull) return; + + furi_string_cat_printf(str, "\n\e#Protocol info\n"); + + if(iso14443_3b_supports_bit_rate(data, Iso14443_3bBitRateBoth106Kbit)) { + furi_string_cat(str, "Bit rate PICC <-> PCD:\n 106 kBit/s supported\n"); + } else { + furi_string_cat(str, "Bit rate PICC -> PCD:\n"); + if(iso14443_3b_supports_bit_rate(data, Iso14443_3bBitRatePiccToPcd212Kbit)) { + furi_string_cat(str, " 212 kBit/s supported\n"); + } + if(iso14443_3b_supports_bit_rate(data, Iso14443_3bBitRatePiccToPcd424Kbit)) { + furi_string_cat(str, " 424 kBit/s supported\n"); + } + if(iso14443_3b_supports_bit_rate(data, Iso14443_3bBitRatePiccToPcd848Kbit)) { + furi_string_cat(str, " 848 kBit/s supported\n"); + } + + furi_string_cat(str, "Bit rate PICC <- PCD:\n"); + if(iso14443_3b_supports_bit_rate(data, Iso14443_3bBitRatePcdToPicc212Kbit)) { + furi_string_cat(str, " 212 kBit/s supported\n"); + } + if(iso14443_3b_supports_bit_rate(data, Iso14443_3bBitRatePcdToPicc424Kbit)) { + furi_string_cat(str, " 424 kBit/s supported\n"); + } + if(iso14443_3b_supports_bit_rate(data, Iso14443_3bBitRatePcdToPicc848Kbit)) { + furi_string_cat(str, " 848 kBit/s supported\n"); + } + } + + furi_string_cat(str, "Max frame size: "); + + const uint16_t max_frame_size = iso14443_3b_get_frame_size_max(data); + if(max_frame_size != 0) { + furi_string_cat_printf(str, "%u bytes\n", max_frame_size); + } else { + furi_string_cat(str, "? (RFU)\n"); + } + + const double fwt = iso14443_3b_get_fwt_fc_max(data) / 13.56e6; + furi_string_cat_printf(str, "Max waiting time: %4.2g s\n", fwt); + + const char* nad_support_str = + iso14443_3b_supports_frame_option(data, Iso14443_3bFrameOptionNad) ? "" : "not "; + furi_string_cat_printf(str, "NAD: %ssupported\n", nad_support_str); + + const char* cid_support_str = + iso14443_3b_supports_frame_option(data, Iso14443_3bFrameOptionCid) ? "" : "not "; + furi_string_cat_printf(str, "CID: %ssupported", cid_support_str); + + furi_string_cat_printf(str, "\n\e#Application data\nRaw:"); + + size_t app_data_size; + const uint8_t* app_data = iso14443_3b_get_application_data(data, &app_data_size); + for(size_t i = 0; i < app_data_size; ++i) { + furi_string_cat_printf(str, " %02X", app_data[i]); + } +} diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.h b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.h new file mode 100644 index 00000000000..ee50f6acf5a --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_3b/iso14443_3b_render.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_iso14443_3b_info( + const Iso14443_3bData* data, + NfcProtocolFormatType format_type, + FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c new file mode 100644 index 00000000000..0a3a592e172 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.c @@ -0,0 +1,149 @@ +#include "iso14443_4a.h" +#include "iso14443_4a_render.h" + +#include +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_common.h" +#include "../nfc_protocol_support_gui_common.h" + +static void nfc_scene_info_on_enter_iso14443_4a(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso14443_4aData* data = nfc_device_get_data(device, NfcProtocolIso14443_4a); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_iso14443_4a_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand + nfc_scene_read_poller_callback_iso14443_4a(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_4a); + + NfcApp* instance = context; + const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data; + + if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolIso14443_4a, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_iso14443_4a(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_iso14443_4a, instance); +} + +static void nfc_scene_read_success_on_enter_iso14443_4a(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso14443_4aData* data = nfc_device_get_data(device, NfcProtocolIso14443_4a); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_iso14443_4a_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_saved_menu_on_enter_iso14443_4a(NfcApp* instance) { + UNUSED(instance); +} + +NfcCommand nfc_scene_emulate_listener_callback_iso14443_4a(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolIso14443_4a); + furi_assert(event.event_data); + + NfcApp* nfc = context; + Iso14443_4aListenerEvent* iso14443_4a_event = event.event_data; + + if(iso14443_4a_event->type == Iso14443_4aListenerEventTypeReceivedData) { + if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) { + furi_string_cat_printf(nfc->text_box_store, "R:"); + for(size_t i = 0; i < bit_buffer_get_size_bytes(iso14443_4a_event->data->buffer); + i++) { + furi_string_cat_printf( + nfc->text_box_store, + " %02X", + bit_buffer_get_byte(iso14443_4a_event->data->buffer, i)); + } + furi_string_push_back(nfc->text_box_store, '\n'); + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); + } + } + + return NfcCommandContinue; +} + +static void nfc_scene_emulate_on_enter_iso14443_4a(NfcApp* instance) { + const Iso14443_4aData* data = + nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a); + + instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, data); + nfc_listener_start( + instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance); +} + +static bool nfc_scene_read_menu_on_event_iso14443_4a(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexCommonEmulate) { + scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); + return true; + } + + return false; +} + +const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a = { + .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureEditUid, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_iso14443_4a, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_iso14443_4a, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_scene_read_menu_on_event_iso14443_4a, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_iso14443_4a, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_saved_menu = + { + .on_enter = nfc_scene_saved_menu_on_enter_iso14443_4a, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_save_name = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_iso14443_4a, + .on_event = nfc_protocol_support_common_on_event_empty, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.h b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.h new file mode 100644 index 00000000000..1b173d7b091 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_iso14443_4a; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_i.h b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_i.h new file mode 100644 index 00000000000..7e13403da13 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_i.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include "iso14443_4a.h" + +NfcCommand nfc_scene_emulate_listener_callback_iso14443_4a(NfcGenericEvent event, void* context); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.c b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.c new file mode 100644 index 00000000000..a963e744b9e --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.c @@ -0,0 +1,84 @@ +#include "iso14443_4a_render.h" + +#include "../iso14443_3a/iso14443_3a_render.h" + +void nfc_render_iso14443_4a_info( + const Iso14443_4aData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + nfc_render_iso14443_4a_brief(data, str); + + if(format_type != NfcProtocolFormatTypeFull) return; + + nfc_render_iso14443_4a_extra(data, str); +} + +void nfc_render_iso14443_4a_brief(const Iso14443_4aData* data, FuriString* str) { + nfc_render_iso14443_3a_brief(iso14443_4a_get_base_data(data), str); +} + +void nfc_render_iso14443_4a_extra(const Iso14443_4aData* data, FuriString* str) { + furi_string_cat_printf(str, "\n\e#Protocol info\n"); + + if(iso14443_4a_supports_bit_rate(data, Iso14443_4aBitRateBoth106Kbit)) { + furi_string_cat(str, "Bit rate PICC <-> PCD:\n 106 kBit/s supported\n"); + } else { + furi_string_cat(str, "Bit rate PICC -> PCD:\n"); + if(iso14443_4a_supports_bit_rate(data, Iso14443_4aBitRatePiccToPcd212Kbit)) { + furi_string_cat(str, " 212 kBit/s supported\n"); + } + if(iso14443_4a_supports_bit_rate(data, Iso14443_4aBitRatePiccToPcd424Kbit)) { + furi_string_cat(str, " 424 kBit/s supported\n"); + } + if(iso14443_4a_supports_bit_rate(data, Iso14443_4aBitRatePiccToPcd848Kbit)) { + furi_string_cat(str, " 848 kBit/s supported\n"); + } + + furi_string_cat(str, "Bit rate PICC <- PCD:\n"); + if(iso14443_4a_supports_bit_rate(data, Iso14443_4aBitRatePcdToPicc212Kbit)) { + furi_string_cat(str, " 212 kBit/s supported\n"); + } + if(iso14443_4a_supports_bit_rate(data, Iso14443_4aBitRatePcdToPicc424Kbit)) { + furi_string_cat(str, " 424 kBit/s supported\n"); + } + if(iso14443_4a_supports_bit_rate(data, Iso14443_4aBitRatePcdToPicc848Kbit)) { + furi_string_cat(str, " 848 kBit/s supported\n"); + } + } + + furi_string_cat(str, "Max frame size: "); + + const uint16_t max_frame_size = iso14443_4a_get_frame_size_max(data); + if(max_frame_size != 0) { + furi_string_cat_printf(str, "%u bytes\n", max_frame_size); + } else { + furi_string_cat(str, "? (RFU)\n"); + } + + const uint32_t fwt_fc = iso14443_4a_get_fwt_fc_max(data); + if(fwt_fc != 0) { + furi_string_cat_printf(str, "Max waiting time: %4.2g s\n", (double)(fwt_fc / 13.56e6)); + } + + const char* nad_support_str = + iso14443_4a_supports_frame_option(data, Iso14443_4aFrameOptionNad) ? "" : "not "; + furi_string_cat_printf(str, "NAD: %ssupported\n", nad_support_str); + + const char* cid_support_str = + iso14443_4a_supports_frame_option(data, Iso14443_4aFrameOptionCid) ? "" : "not "; + furi_string_cat_printf(str, "CID: %ssupported", cid_support_str); + + uint32_t hist_bytes_count; + const uint8_t* hist_bytes = iso14443_4a_get_historical_bytes(data, &hist_bytes_count); + + if(hist_bytes_count > 0) { + furi_string_cat_printf(str, "\n\e#Historical bytes\nRaw:"); + + for(size_t i = 0; i < hist_bytes_count; ++i) { + furi_string_cat_printf(str, " %02X", hist_bytes[i]); + } + } + + furi_string_cat(str, "\n\e#ISO14443-3A data"); + nfc_render_iso14443_3a_extra(iso14443_4a_get_base_data(data), str); +} diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.h b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.h new file mode 100644 index 00000000000..fdcd7066eaf --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4a/iso14443_4a_render.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_iso14443_4a_info( + const Iso14443_4aData* data, + NfcProtocolFormatType format_type, + FuriString* str); + +void nfc_render_iso14443_4a_brief(const Iso14443_4aData* data, FuriString* str); + +void nfc_render_iso14443_4a_extra(const Iso14443_4aData* data, FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c new file mode 100644 index 00000000000..a0c70a22e91 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.c @@ -0,0 +1,118 @@ +#include "iso14443_4b.h" +#include "iso14443_4b_render.h" + +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_common.h" +#include "../nfc_protocol_support_gui_common.h" +#include "../iso14443_3b/iso14443_3b_i.h" + +static void nfc_scene_info_on_enter_iso14443_4b(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso14443_4bData* data = nfc_device_get_data(device, NfcProtocolIso14443_4b); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_iso14443_4b_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand + nfc_scene_read_poller_callback_iso14443_4b(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_4b); + + NfcApp* instance = context; + const Iso14443_4bPollerEvent* iso14443_4b_event = event.event_data; + + if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeReady) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolIso14443_4b, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_iso14443_4b(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_iso14443_4b, instance); +} + +static void nfc_scene_read_success_on_enter_iso14443_4b(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso14443_4bData* data = nfc_device_get_data(device, NfcProtocolIso14443_4b); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_iso14443_4b_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_saved_menu_on_enter_iso14443_4b(NfcApp* instance) { + UNUSED(instance); +} + +static bool nfc_scene_read_menu_on_event_iso14443_4b(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexCommonEmulate) { + scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); + return true; + } + + return false; +} + +static bool nfc_scene_saved_menu_on_event_iso14443_4b(NfcApp* instance, uint32_t event) { + return nfc_scene_saved_menu_on_event_iso14443_3b_common(instance, event); +} + +const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b = { + .features = NfcProtocolFeatureNone, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_iso14443_4b, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_iso14443_4b, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_scene_read_menu_on_event_iso14443_4b, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_iso14443_4b, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_saved_menu = + { + .on_enter = nfc_scene_saved_menu_on_enter_iso14443_4b, + .on_event = nfc_scene_saved_menu_on_event_iso14443_4b, + }, + .scene_save_name = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_emulate = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.h b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.h new file mode 100644 index 00000000000..84a3456d4d1 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_iso14443_4b; diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b_render.c b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b_render.c new file mode 100644 index 00000000000..e15b289a083 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b_render.c @@ -0,0 +1,10 @@ +#include "iso14443_4b_render.h" + +#include "../iso14443_3b/iso14443_3b_render.h" + +void nfc_render_iso14443_4b_info( + const Iso14443_4bData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + nfc_render_iso14443_3b_info(iso14443_4b_get_base_data(data), format_type, str); +} diff --git a/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b_render.h b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b_render.h new file mode 100644 index 00000000000..094892f83a4 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso14443_4b/iso14443_4b_render.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_iso14443_4b_info( + const Iso14443_4bData* data, + NfcProtocolFormatType format_type, + FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c new file mode 100644 index 00000000000..7f861a03265 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.c @@ -0,0 +1,144 @@ +#include "iso15693_3.h" +#include "iso15693_3_render.h" + +#include +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_common.h" +#include "../nfc_protocol_support_gui_common.h" + +static void nfc_scene_info_on_enter_iso15693_3(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso15693_3Data* data = nfc_device_get_data(device, NfcProtocolIso15693_3); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_iso15693_3_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand nfc_scene_read_poller_callback_iso15693_3(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso15693_3); + + NfcApp* instance = context; + const Iso15693_3PollerEvent* iso15693_3_event = event.event_data; + + if(iso15693_3_event->type == Iso15693_3PollerEventTypeReady) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolIso15693_3, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_iso15693_3(NfcApp* instance) { + UNUSED(instance); + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_iso15693_3, instance); +} + +static void nfc_scene_read_success_on_enter_iso15693_3(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const Iso15693_3Data* data = nfc_device_get_data(device, NfcProtocolIso15693_3); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_iso15693_3_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand + nfc_scene_emulate_listener_callback_iso15693_3(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolIso15693_3); + furi_assert(event.event_data); + + NfcApp* nfc = context; + Iso15693_3ListenerEvent* iso15693_3_event = event.event_data; + + if(iso15693_3_event->type == Iso15693_3ListenerEventTypeCustomCommand) { + if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) { + furi_string_cat_printf(nfc->text_box_store, "R:"); + for(size_t i = 0; i < bit_buffer_get_size_bytes(iso15693_3_event->data->buffer); i++) { + furi_string_cat_printf( + nfc->text_box_store, + " %02X", + bit_buffer_get_byte(iso15693_3_event->data->buffer, i)); + } + furi_string_push_back(nfc->text_box_store, '\n'); + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); + } + } + + return NfcCommandContinue; +} + +static void nfc_scene_emulate_on_enter_iso15693_3(NfcApp* instance) { + const Iso15693_3Data* data = nfc_device_get_data(instance->nfc_device, NfcProtocolIso15693_3); + + instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolIso15693_3, data); + nfc_listener_start( + instance->listener, nfc_scene_emulate_listener_callback_iso15693_3, instance); +} + +static bool nfc_scene_saved_menu_on_event_iso15693_3(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexCommonEdit) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); + return true; + } + + return false; +} + +const NfcProtocolSupportBase nfc_protocol_support_iso15693_3 = { + .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureEditUid, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_iso15693_3, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_iso15693_3, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_iso15693_3, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_saved_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_scene_saved_menu_on_event_iso15693_3, + }, + .scene_save_name = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_iso15693_3, + .on_event = nfc_protocol_support_common_on_event_empty, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.h b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.h new file mode 100644 index 00000000000..a26633ee610 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_iso15693_3; diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c new file mode 100644 index 00000000000..92bdb22dc97 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.c @@ -0,0 +1,92 @@ +#include "iso15693_3_render.h" + +#define NFC_RENDER_ISO15693_3_MAX_BYTES (128U) + +void nfc_render_iso15693_3_info( + const Iso15693_3Data* data, + NfcProtocolFormatType format_type, + FuriString* str) { + if(format_type == NfcProtocolFormatTypeFull) { + furi_string_cat(str, "ISO15693-3 (NFC-V)\n"); + } + + nfc_render_iso15693_3_brief(data, str); + + if(format_type == NfcProtocolFormatTypeFull) { + nfc_render_iso15693_3_extra(data, str); + } +} + +void nfc_render_iso15693_3_brief(const Iso15693_3Data* data, FuriString* str) { + furi_string_cat_printf(str, "UID:"); + + size_t uid_len; + const uint8_t* uid = iso15693_3_get_uid(data, &uid_len); + + for(size_t i = 0; i < uid_len; i++) { + furi_string_cat_printf(str, " %02X", uid[i]); + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) { + const uint16_t block_count = iso15693_3_get_block_count(data); + const uint8_t block_size = iso15693_3_get_block_size(data); + + furi_string_cat_printf(str, "Memory: %u bytes\n", block_count * block_size); + furi_string_cat_printf(str, "(%u blocks x %u bytes)", block_count, block_size); + } +} + +void nfc_render_iso15693_3_extra(const Iso15693_3Data* data, FuriString* str) { + furi_string_cat(str, "\n\e#General info\n"); + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) { + furi_string_cat_printf(str, "DSFID: %02X\n", data->system_info.ic_ref); + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) { + furi_string_cat_printf(str, "AFI: %02X\n", data->system_info.afi); + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_IC_REF) { + furi_string_cat_printf(str, "IC Reference: %02X\n", data->system_info.ic_ref); + } + + furi_string_cat(str, "\e#Lock bits\n"); + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) { + furi_string_cat_printf( + str, "DSFID: %s locked\n", data->settings.lock_bits.dsfid ? "" : "not"); + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) { + furi_string_cat_printf( + str, "AFI: %s locked\n", data->settings.lock_bits.dsfid ? "" : "not"); + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) { + furi_string_cat(str, "\e#Memory data\n\e*--------------------\n"); + + const uint16_t block_count = iso15693_3_get_block_count(data); + const uint8_t block_size = iso15693_3_get_block_size(data); + const uint16_t display_block_count = + MIN(NFC_RENDER_ISO15693_3_MAX_BYTES / block_size, block_count); + + for(uint32_t i = 0; i < display_block_count; ++i) { + furi_string_cat(str, "\e*"); + + const uint8_t* block_data = iso15693_3_get_block_data(data, i); + for(uint32_t j = 0; j < block_size; ++j) { + furi_string_cat_printf(str, "%02X ", block_data[j]); + } + + const char* lock_str = iso15693_3_is_block_locked(data, i) ? "[LOCK]" : ""; + furi_string_cat_printf(str, "| %s\n", lock_str); + } + + if(block_count != display_block_count) { + furi_string_cat_printf( + str, + "(Data is too big. Showing only the first %u bytes.)", + display_block_count * block_size); + } + } +} diff --git a/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.h b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.h new file mode 100644 index 00000000000..d531fd2eb07 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/iso15693_3/iso15693_3_render.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_iso15693_3_info( + const Iso15693_3Data* data, + NfcProtocolFormatType format_type, + FuriString* str); + +void nfc_render_iso15693_3_brief(const Iso15693_3Data* data, FuriString* str); + +void nfc_render_iso15693_3_extra(const Iso15693_3Data* data, FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c new file mode 100644 index 00000000000..3e0468cd91e --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -0,0 +1,252 @@ +#include "mf_classic.h" +#include "mf_classic_render.h" + +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_common.h" +#include "../nfc_protocol_support_gui_common.h" + +#define TAG "MfClassicApp" + +enum { + SubmenuIndexDetectReader = SubmenuIndexCommonMax, + SubmenuIndexWrite, + SubmenuIndexUpdate, +}; + +static void nfc_scene_info_on_enter_mf_classic(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_mf_classic_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_more_info_on_enter_mf_classic(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfClassicData* mfc_data = nfc_device_get_data(device, NfcProtocolMfClassic); + + furi_string_reset(instance->text_box_store); + nfc_render_mf_classic_dump(mfc_data, instance->text_box_store); + + text_box_set_font(instance->text_box, TextBoxFontHex); + text_box_set_text(instance->text_box, furi_string_get_cstr(instance->text_box_store)); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox); +} + +static NfcCommand nfc_scene_read_poller_callback_mf_classic(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolMfClassic); + + NfcApp* instance = context; + const MfClassicPollerEvent* mfc_event = event.event_data; + NfcCommand command = NfcCommandContinue; + + if(mfc_event->type == MfClassicPollerEventTypeRequestMode) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolMfClassic, nfc_poller_get_data(instance->poller)); + size_t uid_len = 0; + const uint8_t* uid = nfc_device_get_uid(instance->nfc_device, &uid_len); + if(mf_classic_key_cache_load(instance->mfc_key_cache, uid, uid_len)) { + FURI_LOG_I(TAG, "Key cache found"); + mfc_event->data->poller_mode.mode = MfClassicPollerModeRead; + } else { + FURI_LOG_I(TAG, "Key cache not found"); + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventPollerIncomplete); + command = NfcCommandStop; + } + } else if(mfc_event->type == MfClassicPollerEventTypeRequestReadSector) { + uint8_t sector_num = 0; + MfClassicKey key = {}; + MfClassicKeyType key_type = MfClassicKeyTypeA; + if(mf_classic_key_cahce_get_next_key( + instance->mfc_key_cache, §or_num, &key, &key_type)) { + mfc_event->data->read_sector_request_data.sector_num = sector_num; + mfc_event->data->read_sector_request_data.key = key; + mfc_event->data->read_sector_request_data.key_type = key_type; + mfc_event->data->read_sector_request_data.key_provided = true; + } else { + mfc_event->data->read_sector_request_data.key_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolMfClassic, nfc_poller_get_data(instance->poller)); + const MfClassicData* mfc_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + NfcCustomEvent custom_event = mf_classic_is_card_read(mfc_data) ? + NfcCustomEventPollerSuccess : + NfcCustomEventPollerIncomplete; + view_dispatcher_send_custom_event(instance->view_dispatcher, custom_event); + command = NfcCommandStop; + } + + return command; +} + +static void nfc_scene_read_on_enter_mf_classic(NfcApp* instance) { + mf_classic_key_cache_reset(instance->mfc_key_cache); + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_classic, instance); +} + +static bool nfc_scene_read_on_event_mf_classic(NfcApp* instance, uint32_t event) { + if(event == NfcCustomEventPollerIncomplete) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDictAttack); + } + + return true; +} + +static void nfc_scene_read_menu_on_enter_mf_classic(NfcApp* instance) { + Submenu* submenu = instance->submenu; + const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + + if(!mf_classic_is_card_read(data)) { + submenu_add_item( + submenu, + "Detect Reader", + SubmenuIndexDetectReader, + nfc_protocol_support_common_submenu_callback, + instance); + } +} + +static void nfc_scene_read_success_on_enter_mf_classic(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_mf_classic_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_saved_menu_on_enter_mf_classic(NfcApp* instance) { + Submenu* submenu = instance->submenu; + const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + + if(!mf_classic_is_card_read(data)) { + submenu_add_item( + submenu, + "Detect Reader", + SubmenuIndexDetectReader, + nfc_protocol_support_common_submenu_callback, + instance); + } + submenu_add_item( + submenu, + "Write to Initial Card", + SubmenuIndexWrite, + nfc_protocol_support_common_submenu_callback, + instance); + submenu_add_item( + submenu, + "Update from Initial Card", + SubmenuIndexUpdate, + nfc_protocol_support_common_submenu_callback, + instance); +} + +static void nfc_scene_emulate_on_enter_mf_classic(NfcApp* instance) { + const MfClassicData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolMfClassic, data); + nfc_listener_start(instance->listener, NULL, NULL); +} + +static bool nfc_scene_read_menu_on_event_mf_classic(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexDetectReader) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader); + dolphin_deed(DolphinDeedNfcDetectReader); + return true; + } + + return false; +} + +static bool nfc_scene_saved_menu_on_event_mf_classic(NfcApp* instance, uint32_t event) { + bool consumed = false; + + if(event == SubmenuIndexDetectReader) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicDetectReader); + consumed = true; + } else if(event == SubmenuIndexWrite) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitial); + consumed = true; + } else if(event == SubmenuIndexUpdate) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicUpdateInitial); + consumed = true; + } + + return consumed; +} + +static bool nfc_scene_save_name_on_event_mf_classic(NfcApp* instance, uint32_t event) { + bool consumed = false; + + if(event == NfcCustomEventTextInputDone) { + mf_classic_key_cache_save( + instance->mfc_key_cache, + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic)); + consumed = true; + } + + return consumed; +} + +const NfcProtocolSupportBase nfc_protocol_support_mf_classic = { + .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_mf_classic, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_more_info = + { + .on_enter = nfc_scene_more_info_on_enter_mf_classic, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_mf_classic, + .on_event = nfc_scene_read_on_event_mf_classic, + }, + .scene_read_menu = + { + .on_enter = nfc_scene_read_menu_on_enter_mf_classic, + .on_event = nfc_scene_read_menu_on_event_mf_classic, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_mf_classic, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_saved_menu = + { + .on_enter = nfc_scene_saved_menu_on_enter_mf_classic, + .on_event = nfc_scene_saved_menu_on_event_mf_classic, + }, + .scene_save_name = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_scene_save_name_on_event_mf_classic, + }, + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_mf_classic, + .on_event = nfc_protocol_support_common_on_event_empty, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.h b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.h new file mode 100644 index 00000000000..d0f09f5d78e --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_mf_classic; diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c new file mode 100644 index 00000000000..5bd4a6b6ddd --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.c @@ -0,0 +1,30 @@ +#include "mf_classic_render.h" + +#include "../iso14443_3a/iso14443_3a_render.h" + +void nfc_render_mf_classic_info( + const MfClassicData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + nfc_render_iso14443_3a_info(data->iso14443_3a_data, format_type, str); + + uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); + uint8_t keys_total = sectors_total * 2; + uint8_t keys_found = 0; + uint8_t sectors_read = 0; + mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); + + furi_string_cat_printf(str, "\nKeys Found: %u/%u", keys_found, keys_total); + furi_string_cat_printf(str, "\nSectors Read: %u/%u", sectors_read, sectors_total); +} + +void nfc_render_mf_classic_dump(const MfClassicData* data, FuriString* str) { + uint16_t total_blocks = mf_classic_get_total_block_num(data->type); + + for(size_t i = 0; i < total_blocks; i++) { + for(size_t j = 0; j < sizeof(MfClassicBlock); j += 2) { + furi_string_cat_printf( + str, "%02X%02X ", data->block[i].data[j], data->block[i].data[j + 1]); + } + } +} diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.h b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.h new file mode 100644 index 00000000000..7e1619761f4 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic_render.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_mf_classic_info( + const MfClassicData* data, + NfcProtocolFormatType format_type, + FuriString* str); + +void nfc_render_mf_classic_dump(const MfClassicData* data, FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c new file mode 100644 index 00000000000..bc05c2a4c36 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.c @@ -0,0 +1,120 @@ +#include "mf_desfire.h" +#include "mf_desfire_render.h" + +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_common.h" +#include "../nfc_protocol_support_gui_common.h" +#include "../iso14443_4a/iso14443_4a_i.h" + +static void nfc_scene_info_on_enter_mf_desfire(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfDesfireData* data = nfc_device_get_data(device, NfcProtocolMfDesfire); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_mf_desfire_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_more_info_on_enter_mf_desfire(NfcApp* instance) { + // Jump to advanced scene right away + scene_manager_next_scene(instance->scene_manager, NfcSceneMfDesfireMoreInfo); +} + +static NfcCommand nfc_scene_read_poller_callback_mf_desfire(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolMfDesfire); + + NfcApp* instance = context; + const MfDesfirePollerEvent* mf_desfire_event = event.event_data; + + if(mf_desfire_event->type == MfDesfirePollerEventTypeReadSuccess) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolMfDesfire, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_mf_desfire(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_desfire, instance); +} + +static void nfc_scene_read_success_on_enter_mf_desfire(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfDesfireData* data = nfc_device_get_data(device, NfcProtocolMfDesfire); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_mf_desfire_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_emulate_on_enter_mf_desfire(NfcApp* instance) { + const Iso14443_4aData* iso14443_4a_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_4a); + + instance->listener = + nfc_listener_alloc(instance->nfc, NfcProtocolIso14443_4a, iso14443_4a_data); + nfc_listener_start( + instance->listener, nfc_scene_emulate_listener_callback_iso14443_4a, instance); +} + +const NfcProtocolSupportBase nfc_protocol_support_mf_desfire = { + .features = NfcProtocolFeatureEmulateUid | NfcProtocolFeatureMoreInfo, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_mf_desfire, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_more_info = + { + .on_enter = nfc_scene_more_info_on_enter_mf_desfire, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_mf_desfire, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_mf_desfire, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_saved_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_save_name = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_mf_desfire, + .on_event = nfc_protocol_support_common_on_event_empty, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.h b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.h new file mode 100644 index 00000000000..504860f16a4 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_mf_desfire; diff --git a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.c b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.c new file mode 100644 index 00000000000..94b333f5523 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.c @@ -0,0 +1,266 @@ +#include "mf_desfire_render.h" + +#include "../iso14443_4a/iso14443_4a_render.h" + +void nfc_render_mf_desfire_info( + const MfDesfireData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + nfc_render_iso14443_4a_brief(mf_desfire_get_base_data(data), str); + + const uint32_t bytes_total = 1UL << (data->version.sw_storage >> 1); + const uint32_t bytes_free = data->free_memory.is_present ? data->free_memory.bytes_free : 0; + + furi_string_cat_printf(str, "\n%lu", bytes_total); + + if(data->version.sw_storage & 1) { + furi_string_push_back(str, '+'); + } + + furi_string_cat_printf(str, " bytes, %lu bytes free\n", bytes_free); + + const uint32_t app_count = simple_array_get_count(data->applications); + uint32_t file_count = 0; + + for(uint32_t i = 0; i < app_count; ++i) { + const MfDesfireApplication* app = simple_array_cget(data->applications, i); + file_count += simple_array_get_count(app->file_ids); + } + + furi_string_cat_printf(str, "%lu Application%s", app_count, app_count != 1 ? "s" : ""); + furi_string_cat_printf(str, ", %lu File%s", file_count, file_count != 1 ? "s" : ""); + + if(format_type != NfcProtocolFormatTypeFull) return; + + furi_string_cat(str, "\n\e#ISO14443-4 data"); + nfc_render_iso14443_4a_extra(mf_desfire_get_base_data(data), str); +} + +void nfc_render_mf_desfire_data(const MfDesfireData* data, FuriString* str) { + nfc_render_mf_desfire_version(&data->version, str); + nfc_render_mf_desfire_free_memory(&data->free_memory, str); + nfc_render_mf_desfire_key_settings(&data->master_key_settings, str); + + for(uint32_t i = 0; i < simple_array_get_count(data->master_key_versions); ++i) { + nfc_render_mf_desfire_key_version(simple_array_cget(data->master_key_versions, i), i, str); + } +} + +void nfc_render_mf_desfire_version(const MfDesfireVersion* data, FuriString* str) { + furi_string_cat_printf( + str, + "%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + data->uid[0], + data->uid[1], + data->uid[2], + data->uid[3], + data->uid[4], + data->uid[5], + data->uid[6]); + furi_string_cat_printf( + str, + "hw %02x type %02x sub %02x\n" + " maj %02x min %02x\n" + " size %02x proto %02x\n", + data->hw_vendor, + data->hw_type, + data->hw_subtype, + data->hw_major, + data->hw_minor, + data->hw_storage, + data->hw_proto); + furi_string_cat_printf( + str, + "sw %02x type %02x sub %02x\n" + " maj %02x min %02x\n" + " size %02x proto %02x\n", + data->sw_vendor, + data->sw_type, + data->sw_subtype, + data->sw_major, + data->sw_minor, + data->sw_storage, + data->sw_proto); + furi_string_cat_printf( + str, + "batch %02x:%02x:%02x:%02x:%02x\n" + "week %d year %d\n", + data->batch[0], + data->batch[1], + data->batch[2], + data->batch[3], + data->batch[4], + data->prod_week, + data->prod_year); +} + +void nfc_render_mf_desfire_free_memory(const MfDesfireFreeMemory* data, FuriString* str) { + if(data->is_present) { + furi_string_cat_printf(str, "freeMem %lu\n", data->bytes_free); + } +} + +void nfc_render_mf_desfire_key_settings(const MfDesfireKeySettings* data, FuriString* str) { + furi_string_cat_printf(str, "changeKeyID %d\n", data->change_key_id); + furi_string_cat_printf(str, "configChangeable %d\n", data->is_config_changeable); + furi_string_cat_printf(str, "freeCreateDelete %d\n", data->is_free_create_delete); + furi_string_cat_printf(str, "freeDirectoryList %d\n", data->is_free_directory_list); + furi_string_cat_printf(str, "masterChangeable %d\n", data->is_master_key_changeable); + + if(data->flags) { + furi_string_cat_printf(str, "flags %d\n", data->flags); + } + + furi_string_cat_printf(str, "maxKeys %d\n", data->max_keys); +} + +void nfc_render_mf_desfire_key_version( + const MfDesfireKeyVersion* data, + uint32_t index, + FuriString* str) { + furi_string_cat_printf(str, "key %lu version %u\n", index, *data); +} + +void nfc_render_mf_desfire_application_id(const MfDesfireApplicationId* data, FuriString* str) { + const uint8_t* app_id = data->data; + furi_string_cat_printf(str, "Application %02x%02x%02x\n", app_id[0], app_id[1], app_id[2]); +} + +void nfc_render_mf_desfire_application(const MfDesfireApplication* data, FuriString* str) { + nfc_render_mf_desfire_key_settings(&data->key_settings, str); + + for(uint32_t i = 0; i < simple_array_get_count(data->key_versions); ++i) { + nfc_render_mf_desfire_key_version(simple_array_cget(data->key_versions, i), i, str); + } +} + +void nfc_render_mf_desfire_file_id(const MfDesfireFileId* data, FuriString* str) { + furi_string_cat_printf(str, "File %d\n", *data); +} + +void nfc_render_mf_desfire_file_settings_data( + const MfDesfireFileSettings* settings, + const MfDesfireFileData* data, + FuriString* str) { + const char* type; + switch(settings->type) { + case MfDesfireFileTypeStandard: + type = "standard"; + break; + case MfDesfireFileTypeBackup: + type = "backup"; + break; + case MfDesfireFileTypeValue: + type = "value"; + break; + case MfDesfireFileTypeLinearRecord: + type = "linear"; + break; + case MfDesfireFileTypeCyclicRecord: + type = "cyclic"; + break; + default: + type = "unknown"; + } + + const char* comm; + switch(settings->comm) { + case MfDesfireFileCommunicationSettingsPlaintext: + comm = "plain"; + break; + case MfDesfireFileCommunicationSettingsAuthenticated: + comm = "auth"; + break; + case MfDesfireFileCommunicationSettingsEnciphered: + comm = "enciphered"; + break; + default: + comm = "unknown"; + } + + furi_string_cat_printf(str, "%s %s\n", type, comm); + furi_string_cat_printf( + str, + "r %d w %d rw %d c %d\n", + settings->access_rights >> 12 & 0xF, + settings->access_rights >> 8 & 0xF, + settings->access_rights >> 4 & 0xF, + settings->access_rights & 0xF); + + uint32_t record_count = 1; + uint32_t record_size = 0; + + const uint32_t total_size = simple_array_get_count(data->data); + + switch(settings->type) { + case MfDesfireFileTypeStandard: + case MfDesfireFileTypeBackup: + record_size = settings->data.size; + furi_string_cat_printf(str, "size %lu\n", record_size); + break; + case MfDesfireFileTypeValue: + record_size = MF_DESFIRE_VALUE_SIZE; + furi_string_cat_printf( + str, "lo %lu hi %lu\n", settings->value.lo_limit, settings->value.hi_limit); + furi_string_cat_printf( + str, + "limit %lu enabled %d\n", + settings->value.limited_credit_value, + settings->value.limited_credit_enabled); + break; + case MfDesfireFileTypeLinearRecord: + case MfDesfireFileTypeCyclicRecord: + record_count = settings->record.cur; + record_size = settings->record.size; + furi_string_cat_printf(str, "size %lu\n", record_size); + furi_string_cat_printf(str, "num %lu max %lu\n", record_count, settings->record.max); + break; + } + + if(simple_array_get_count(data->data) == 0) { + return; + } + + for(uint32_t rec = 0; rec < record_count; rec++) { + const uint32_t size_offset = rec * record_size; + const uint32_t size_remaining = total_size > size_offset ? total_size - size_offset : 0; + + if(size_remaining < record_size) { + furi_string_cat_printf( + str, "record %lu (partial %lu of %lu)\n", rec, size_remaining, record_size); + record_size = size_remaining; + } else { + furi_string_cat_printf(str, "record %lu\n", rec); + } + + for(uint32_t ch = 0; ch < record_size; ch += 4) { + furi_string_cat_printf(str, "%03lx|", ch); + + for(uint32_t i = 0; i < 4; i++) { + if(ch + i < record_size) { + const uint32_t data_index = rec * record_size + ch + i; + const uint8_t data_byte = + *(const uint8_t*)simple_array_cget(data->data, data_index); + furi_string_cat_printf(str, "%02x ", data_byte); + } else { + furi_string_cat_printf(str, " "); + } + } + + for(uint32_t i = 0; i < 4 && ch + i < record_size; i++) { + const uint32_t data_index = rec * record_size + ch + i; + const uint8_t data_byte = + *(const uint8_t*)simple_array_cget(data->data, data_index); + if(isprint(data_byte)) { + furi_string_cat_printf(str, "%c", data_byte); + } else { + furi_string_cat_printf(str, "."); + } + } + + furi_string_push_back(str, '\n'); + } + + furi_string_push_back(str, '\n'); + } +} diff --git a/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.h b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.h new file mode 100644 index 00000000000..ff5e815bff0 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_desfire/mf_desfire_render.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_mf_desfire_info( + const MfDesfireData* data, + NfcProtocolFormatType format_type, + FuriString* str); + +void nfc_render_mf_desfire_data(const MfDesfireData* data, FuriString* str); + +void nfc_render_mf_desfire_version(const MfDesfireVersion* data, FuriString* str); + +void nfc_render_mf_desfire_free_memory(const MfDesfireFreeMemory* data, FuriString* str); + +void nfc_render_mf_desfire_key_settings(const MfDesfireKeySettings* data, FuriString* str); + +void nfc_render_mf_desfire_key_version( + const MfDesfireKeyVersion* data, + uint32_t index, + FuriString* str); + +void nfc_render_mf_desfire_application_id(const MfDesfireApplicationId* data, FuriString* str); + +void nfc_render_mf_desfire_application(const MfDesfireApplication* data, FuriString* str); + +void nfc_render_mf_desfire_file_id(const MfDesfireFileId* data, FuriString* str); + +void nfc_render_mf_desfire_file_settings_data( + const MfDesfireFileSettings* settings, + const MfDesfireFileData* data, + FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c new file mode 100644 index 00000000000..3e27fc539e6 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -0,0 +1,209 @@ +#include "mf_ultralight.h" +#include "mf_ultralight_render.h" + +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_common.h" +#include "../nfc_protocol_support_gui_common.h" + +enum { + SubmenuIndexUnlock = SubmenuIndexCommonMax, + SubmenuIndexUnlockByReader, + SubmenuIndexUnlockByPassword, + SubmenuIndexWrite, +}; + +static void nfc_scene_info_on_enter_mf_ultralight(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_mf_ultralight_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_more_info_on_enter_mf_ultralight(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfUltralightData* mfu = nfc_device_get_data(device, NfcProtocolMfUltralight); + + furi_string_reset(instance->text_box_store); + nfc_render_mf_ultralight_dump(mfu, instance->text_box_store); + + text_box_set_font(instance->text_box, TextBoxFontHex); + text_box_set_text(instance->text_box, furi_string_get_cstr(instance->text_box_store)); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox); +} + +static NfcCommand + nfc_scene_read_poller_callback_mf_ultralight(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolMfUltralight); + + NfcApp* instance = context; + const MfUltralightPollerEvent* mf_ultralight_event = event.event_data; + + if(mf_ultralight_event->type == MfUltralightPollerEventTypeReadSuccess) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthRequest) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolMfUltralight, nfc_poller_get_data(instance->poller)); + const MfUltralightData* data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); + if(instance->mf_ul_auth->type == MfUltralightAuthTypeXiaomi) { + if(mf_ultralight_generate_xiaomi_pass( + instance->mf_ul_auth, + data->iso14443_3a_data->uid, + data->iso14443_3a_data->uid_len)) { + mf_ultralight_event->data->auth_context.skip_auth = false; + } + } else if(instance->mf_ul_auth->type == MfUltralightAuthTypeAmiibo) { + if(mf_ultralight_generate_amiibo_pass( + instance->mf_ul_auth, + data->iso14443_3a_data->uid, + data->iso14443_3a_data->uid_len)) { + mf_ultralight_event->data->auth_context.skip_auth = false; + } + } else if( + instance->mf_ul_auth->type == MfUltralightAuthTypeManual || + instance->mf_ul_auth->type == MfUltralightAuthTypeReader) { + mf_ultralight_event->data->auth_context.skip_auth = false; + } else { + mf_ultralight_event->data->auth_context.skip_auth = true; + } + if(!mf_ultralight_event->data->auth_context.skip_auth) { + mf_ultralight_event->data->auth_context.password = instance->mf_ul_auth->password; + } + } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthSuccess) { + instance->mf_ul_auth->pack = mf_ultralight_event->data->auth_context.pack; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_mf_ultralight(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_mf_ultralight, instance); +} + +static void nfc_scene_read_and_saved_menu_on_enter_mf_ultralight(NfcApp* instance) { + Submenu* submenu = instance->submenu; + + const MfUltralightData* data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); + + if(!mf_ultralight_is_all_data_read(data)) { + submenu_add_item( + submenu, + "Unlock", + SubmenuIndexUnlock, + nfc_protocol_support_common_submenu_callback, + instance); + } else if( + data->type == MfUltralightTypeNTAG213 || data->type == MfUltralightTypeNTAG215 || + data->type == MfUltralightTypeNTAG216) { + submenu_add_item( + submenu, + "Write", + SubmenuIndexWrite, + nfc_protocol_support_common_submenu_callback, + instance); + } +} + +static void nfc_scene_read_success_on_enter_mf_ultralight(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight); + + FuriString* temp_str = furi_string_alloc(); + + bool unlocked = + scene_manager_has_previous_scene(instance->scene_manager, NfcSceneMfUltralightUnlockWarn); + if(unlocked) { + nfc_render_mf_ultralight_pwd_pack(data, temp_str); + } else { + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + + nfc_render_mf_ultralight_info(data, NfcProtocolFormatTypeShort, temp_str); + } + + mf_ultralight_auth_reset(instance->mf_ul_auth); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static void nfc_scene_emulate_on_enter_mf_ultralight(NfcApp* instance) { + const MfUltralightData* data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); + instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolMfUltralight, data); + nfc_listener_start(instance->listener, NULL, NULL); +} + +static bool + nfc_scene_read_and_saved_menu_on_event_mf_ultralight(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexUnlock) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu); + return true; + } else if(event == SubmenuIndexWrite) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrite); + return true; + } + return false; +} + +const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight = { + .features = NfcProtocolFeatureEmulateFull | NfcProtocolFeatureMoreInfo, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_mf_ultralight, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_more_info = + { + .on_enter = nfc_scene_more_info_on_enter_mf_ultralight, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_mf_ultralight, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_menu = + { + .on_enter = nfc_scene_read_and_saved_menu_on_enter_mf_ultralight, + .on_event = nfc_scene_read_and_saved_menu_on_event_mf_ultralight, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_mf_ultralight, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_saved_menu = + { + .on_enter = nfc_scene_read_and_saved_menu_on_enter_mf_ultralight, + .on_event = nfc_scene_read_and_saved_menu_on_event_mf_ultralight, + }, + .scene_save_name = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_mf_ultralight, + .on_event = nfc_protocol_support_common_on_event_empty, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.h b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.h new file mode 100644 index 00000000000..83e58732e41 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_mf_ultralight; diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.c new file mode 100644 index 00000000000..5296f480712 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.c @@ -0,0 +1,45 @@ +#include "mf_ultralight_render.h" + +#include "../iso14443_3a/iso14443_3a_render.h" + +static void nfc_render_mf_ultralight_pages_count(const MfUltralightData* data, FuriString* str) { + furi_string_cat_printf(str, "\nPages Read: %u/%u", data->pages_read, data->pages_total); + if(data->pages_read != data->pages_total) { + furi_string_cat_printf(str, "\nPassword-protected pages!"); + } +} + +void nfc_render_mf_ultralight_pwd_pack(const MfUltralightData* data, FuriString* str) { + bool all_pages = mf_ultralight_is_all_data_read(data); + furi_string_cat_printf(str, "\e#%s pages unlocked!", all_pages ? "All" : "Not all"); + + MfUltralightConfigPages* config; + mf_ultralight_get_config_page(data, &config); + + furi_string_cat_printf(str, "\nPassword: "); + nfc_render_iso14443_3a_format_bytes( + str, config->password.data, MF_ULTRALIGHT_AUTH_PASSWORD_SIZE); + + furi_string_cat_printf(str, "\nPACK: "); + nfc_render_iso14443_3a_format_bytes(str, config->pack.data, MF_ULTRALIGHT_AUTH_PACK_SIZE); + + nfc_render_mf_ultralight_pages_count(data, str); +} + +void nfc_render_mf_ultralight_info( + const MfUltralightData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + nfc_render_iso14443_3a_info(data->iso14443_3a_data, format_type, str); + + nfc_render_mf_ultralight_pages_count(data, str); +} + +void nfc_render_mf_ultralight_dump(const MfUltralightData* data, FuriString* str) { + for(size_t i = 0; i < data->pages_read; i++) { + const uint8_t* page_data = data->page[i].data; + for(size_t j = 0; j < MF_ULTRALIGHT_PAGE_SIZE; j += 2) { + furi_string_cat_printf(str, "%02X%02X ", page_data[j], page_data[j + 1]); + } + } +} diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.h b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.h new file mode 100644 index 00000000000..5b15bb59110 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight_render.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_mf_ultralight_info( + const MfUltralightData* data, + NfcProtocolFormatType format_type, + FuriString* str); + +void nfc_render_mf_ultralight_dump(const MfUltralightData* data, FuriString* str); + +void nfc_render_mf_ultralight_pwd_pack(const MfUltralightData* data, FuriString* str); \ No newline at end of file diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c new file mode 100644 index 00000000000..87fbe9f0828 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.c @@ -0,0 +1,788 @@ +/** + * @file nfc_protocol_support.c + * @brief Common implementation of application-level protocol support. + * + * @see nfc_protocol_support_base.h + * @see nfc_protocol_support_common.h + */ +#include "nfc_protocol_support.h" + +#include "nfc/nfc_app_i.h" +#include "nfc/helpers/nfc_supported_cards.h" + +#include "nfc_protocol_support_defs.h" +#include "nfc_protocol_support_gui_common.h" + +/** + * @brief Common scene entry handler. + * + * @param[in,out] instance pointer to the NFC application instance. + */ +typedef void (*NfcProtocolSupportCommonOnEnter)(NfcApp* instance); + +/** + * @brief Common scene custom event handler. + * + * @param[in,out] instance pointer to the NFC application instance. + * @param[in] event custom event to be handled. + * @returns true if the event was handled, false otherwise. + */ +typedef bool (*NfcProtocolSupportCommonOnEvent)(NfcApp* instance, SceneManagerEvent event); + +/** + * @brief Common scene exit handler. + * + * @param[in,out] instance pointer to the NFC application instance. + */ +typedef void (*NfcProtocolSupportCommonOnExit)(NfcApp* instance); + +/** + * @brief Structure containing common scene handler pointers. + */ +typedef struct { + NfcProtocolSupportCommonOnEnter on_enter; /**< Pointer to the on_enter() function. */ + NfcProtocolSupportCommonOnEvent on_event; /**< Pointer to the on_event() function. */ + NfcProtocolSupportCommonOnExit on_exit; /**< Pointer to the on_exit() function. */ +} NfcProtocolSupportCommonSceneBase; + +static const NfcProtocolSupportCommonSceneBase nfc_protocol_support_scenes[]; + +// Interface functions +void nfc_protocol_support_on_enter(NfcProtocolSupportScene scene, void* context) { + furi_assert(scene < NfcProtocolSupportSceneCount); + furi_assert(context); + + NfcApp* instance = context; + nfc_protocol_support_scenes[scene].on_enter(instance); +} + +bool nfc_protocol_support_on_event( + NfcProtocolSupportScene scene, + void* context, + SceneManagerEvent event) { + furi_assert(scene < NfcProtocolSupportSceneCount); + furi_assert(context); + + NfcApp* instance = context; + return nfc_protocol_support_scenes[scene].on_event(instance, event); +} + +void nfc_protocol_support_on_exit(NfcProtocolSupportScene scene, void* context) { + furi_assert(scene < NfcProtocolSupportSceneCount); + furi_assert(context); + + NfcApp* instance = context; + nfc_protocol_support_scenes[scene].on_exit(instance); +} + +static bool nfc_protocol_support_has_feature(NfcProtocol protocol, NfcProtocolFeature feature) { + return nfc_protocol_support[protocol]->features & feature; +} + +// Common scene handlers +// SceneInfo +static void nfc_protocol_support_scene_info_on_enter(NfcApp* instance) { + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + nfc_protocol_support[protocol]->scene_info.on_enter(instance); + + if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureMoreInfo)) { + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "More", + nfc_protocol_support_common_widget_callback, + instance); + } + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +static bool nfc_protocol_support_scene_info_on_event(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMoreInfo); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + // If the card could not be parsed, return to the respective menu + if(!scene_manager_get_scene_state(instance->scene_manager, NfcSceneSupportedCard)) { + const uint32_t scenes[] = {NfcSceneSavedMenu, NfcSceneReadMenu}; + scene_manager_search_and_switch_to_previous_scene_one_of( + instance->scene_manager, scenes, COUNT_OF(scenes)); + consumed = true; + } + } + + return consumed; +} + +static void nfc_protocol_support_scene_info_on_exit(NfcApp* instance) { + widget_reset(instance->widget); +} + +// SceneMoreInfo +static void nfc_protocol_support_scene_more_info_on_enter(NfcApp* instance) { + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + nfc_protocol_support[protocol]->scene_more_info.on_enter(instance); +} + +static bool + nfc_protocol_support_scene_more_info_on_event(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + consumed = nfc_protocol_support[protocol]->scene_more_info.on_event(instance, event.event); + } + + return consumed; +} + +static void nfc_protocol_support_scene_more_info_on_exit(NfcApp* instance) { + text_box_reset(instance->text_box); + furi_string_reset(instance->text_box_store); +} + +// SceneRead +static void nfc_protocol_support_scene_read_on_enter(NfcApp* instance) { + popup_set_header( + instance->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop); + popup_set_icon(instance->popup, 12, 23, &A_Loading_24); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); + + const NfcProtocol protocol = + instance->protocols_detected[instance->protocols_detected_selected_idx]; + instance->poller = nfc_poller_alloc(instance->nfc, protocol); + + // Start poller with the appropriate callback + nfc_protocol_support[protocol]->scene_read.on_enter(instance); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); + + nfc_blink_detect_start(instance); +} + +static bool nfc_protocol_support_scene_read_on_event(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventPollerSuccess) { + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + notification_message(instance->notifications, &sequence_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + consumed = true; + } else if(event.event == NfcCustomEventPollerIncomplete) { + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + bool card_read = nfc_supported_cards_read(instance->nfc_device, instance->nfc); + if(card_read) { + notification_message(instance->notifications, &sequence_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + consumed = true; + } else { + const NfcProtocol protocol = + instance->protocols_detected[instance->protocols_detected_selected_idx]; + consumed = + nfc_protocol_support[protocol]->scene_read.on_event(instance, event.event); + } + } else if(event.event == NfcCustomEventPollerFailure) { + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneDetect)) { + scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneDetect); + } + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + static const uint32_t possible_scenes[] = {NfcSceneSelectProtocol, NfcSceneStart}; + scene_manager_search_and_switch_to_previous_scene_one_of( + instance->scene_manager, possible_scenes, COUNT_OF(possible_scenes)); + consumed = true; + } + + return consumed; +} + +static void nfc_protocol_support_scene_read_on_exit(NfcApp* instance) { + popup_reset(instance->popup); + + nfc_blink_stop(instance); +} + +// SceneReadMenu +static void nfc_protocol_support_scene_read_menu_on_enter(NfcApp* instance) { + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + + Submenu* submenu = instance->submenu; + + submenu_add_item( + submenu, + "Save", + SubmenuIndexCommonSave, + nfc_protocol_support_common_submenu_callback, + instance); + + if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) { + submenu_add_item( + submenu, + "Emulate UID", + SubmenuIndexCommonEmulate, + nfc_protocol_support_common_submenu_callback, + instance); + + } else if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateFull)) { + submenu_add_item( + submenu, + "Emulate", + SubmenuIndexCommonEmulate, + nfc_protocol_support_common_submenu_callback, + instance); + } + + nfc_protocol_support[protocol]->scene_read_menu.on_enter(instance); + + submenu_add_item( + submenu, + "Info", + SubmenuIndexCommonInfo, + nfc_protocol_support_common_submenu_callback, + instance); + + submenu_set_selected_item( + instance->submenu, + scene_manager_get_scene_state(instance->scene_manager, NfcSceneReadMenu)); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); +} + +static bool + nfc_protocol_support_scene_read_menu_on_event(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(instance->scene_manager, NfcSceneReadMenu, event.event); + + if(event.event == SubmenuIndexCommonSave) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexCommonInfo) { + scene_manager_next_scene(instance->scene_manager, NfcSceneInfo); + consumed = true; + } else if(event.event == SubmenuIndexCommonEmulate) { + dolphin_deed(DolphinDeedNfcEmulate); + scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); + consumed = true; + } else { + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + consumed = + nfc_protocol_support[protocol]->scene_read_menu.on_event(instance, event.event); + } + + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_set_scene_state(instance->scene_manager, NfcSceneSavedMenu, 0); + } + + return consumed; +} + +// Same for read_menu and saved_menu +static void nfc_protocol_support_scene_read_saved_menu_on_exit(NfcApp* instance) { + submenu_reset(instance->submenu); +} + +// SceneReadSuccess +static void nfc_protocol_support_scene_read_success_on_enter(NfcApp* instance) { + Widget* widget = instance->widget; + + FuriString* temp_str = furi_string_alloc(); + if(nfc_supported_cards_parse(instance->nfc_device, temp_str)) { + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + } else { + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + nfc_protocol_support[protocol]->scene_read_success.on_enter(instance); + } + + furi_string_free(temp_str); + + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", nfc_protocol_support_common_widget_callback, instance); + widget_add_button_element( + widget, GuiButtonTypeRight, "More", nfc_protocol_support_common_widget_callback, instance); + + notification_message_block(instance->notifications, &sequence_set_green_255); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +static bool + nfc_protocol_support_scene_read_success_on_event(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + scene_manager_next_scene(instance->scene_manager, NfcSceneRetryConfirm); + consumed = true; + + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneReadMenu); + consumed = true; + } + + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(instance->scene_manager, NfcSceneExitConfirm); + consumed = true; + } + + return consumed; +} + +static void nfc_protocol_support_scene_read_success_on_exit(NfcApp* instance) { + notification_message_block(instance->notifications, &sequence_reset_green); + widget_reset(instance->widget); +} + +// SceneSavedMenu +static void nfc_protocol_support_scene_saved_menu_on_enter(NfcApp* instance) { + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + + Submenu* submenu = instance->submenu; + + // Header submenu items + if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) { + submenu_add_item( + submenu, + "Emulate UID", + SubmenuIndexCommonEmulate, + nfc_protocol_support_common_submenu_callback, + instance); + + } else if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateFull)) { + submenu_add_item( + submenu, + "Emulate", + SubmenuIndexCommonEmulate, + nfc_protocol_support_common_submenu_callback, + instance); + } + + if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEditUid)) { + submenu_add_item( + submenu, + "Edit UID", + SubmenuIndexCommonEdit, + nfc_protocol_support_common_submenu_callback, + instance); + } + + // Protocol-dependent menu items + nfc_protocol_support[protocol]->scene_saved_menu.on_enter(instance); + + // Trailer submenu items + submenu_add_item( + submenu, + "Info", + SubmenuIndexCommonInfo, + nfc_protocol_support_common_submenu_callback, + instance); + submenu_add_item( + submenu, + "Rename", + SubmenuIndexCommonRename, + nfc_protocol_support_common_submenu_callback, + instance); + submenu_add_item( + submenu, + "Delete", + SubmenuIndexCommonDelete, + nfc_protocol_support_common_submenu_callback, + instance); + + if(nfc_has_shadow_file(instance)) { + submenu_add_item( + submenu, + "Restore Data Changes", + SubmenuIndexCommonRestore, + nfc_protocol_support_common_submenu_callback, + instance); + } + + submenu_set_selected_item( + instance->submenu, + scene_manager_get_scene_state(instance->scene_manager, NfcSceneSavedMenu)); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); +} + +static bool + nfc_protocol_support_scene_saved_menu_on_event(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(instance->scene_manager, NfcSceneSavedMenu, event.event); + + if(event.event == SubmenuIndexCommonRestore) { + scene_manager_next_scene(instance->scene_manager, NfcSceneRestoreOriginalConfirm); + consumed = true; + } else if(event.event == SubmenuIndexCommonInfo) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSupportedCard); + consumed = true; + } else if(event.event == SubmenuIndexCommonRename) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexCommonDelete) { + scene_manager_next_scene(instance->scene_manager, NfcSceneDelete); + consumed = true; + } else if(event.event == SubmenuIndexCommonEmulate) { + const bool is_added = + scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSetType); + dolphin_deed(is_added ? DolphinDeedNfcAddEmulate : DolphinDeedNfcEmulate); + scene_manager_next_scene(instance->scene_manager, NfcSceneEmulate); + consumed = true; + } else if(event.event == SubmenuIndexCommonEdit) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); + consumed = true; + } else { + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + consumed = + nfc_protocol_support[protocol]->scene_saved_menu.on_event(instance, event.event); + } + + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_set_scene_state(instance->scene_manager, NfcSceneSavedMenu, 0); + } + + return consumed; +} + +// SceneSaveName + +static void nfc_protocol_support_scene_save_name_on_enter(NfcApp* instance) { + FuriString* folder_path = furi_string_alloc(); + TextInput* text_input = instance->text_input; + + bool name_is_empty = furi_string_empty(instance->file_name); + if(name_is_empty) { + furi_string_set(instance->file_path, NFC_APP_FOLDER); + name_generator_make_auto( + instance->text_store, NFC_TEXT_STORE_SIZE, NFC_APP_FILENAME_PREFIX); + furi_string_set(folder_path, NFC_APP_FOLDER); + } else { + nfc_text_store_set(instance, "%s", furi_string_get_cstr(instance->file_name)); + path_extract_dirname(furi_string_get_cstr(instance->file_path), folder_path); + } + + text_input_set_header_text(text_input, "Name the card"); + text_input_set_result_callback( + text_input, + nfc_protocol_support_common_text_input_done_callback, + instance, + instance->text_store, + NFC_NAME_SIZE, + name_is_empty); + + ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( + furi_string_get_cstr(folder_path), + NFC_APP_EXTENSION, + furi_string_get_cstr(instance->file_name)); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + furi_string_free(folder_path); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextInput); +} + +static bool + nfc_protocol_support_scene_save_name_on_event(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventTextInputDone) { + if(!furi_string_empty(instance->file_name)) { + nfc_delete(instance); + } + furi_string_set(instance->file_name, instance->text_store); + + if(nfc_save(instance)) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess); + dolphin_deed( + scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSetType) ? + DolphinDeedNfcAddSave : + DolphinDeedNfcSave); + const NfcProtocol protocol = + instance->protocols_detected[instance->protocols_detected_selected_idx]; + consumed = nfc_protocol_support[protocol]->scene_save_name.on_event( + instance, event.event); + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneStart); + } + } + } + + return consumed; +} + +static void nfc_protocol_support_scene_save_name_on_exit(NfcApp* instance) { + 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); +} + +// SceneEmulate +/** + * @brief Current view displayed on the emulation scene. + * + * The emulation scehe has two states: the default one showing information about + * the card being emulated, and the logs which show the raw data received from the reader. + * + * The user has the ability to switch betweeen these two scenes, however the prompt to switch is + * only shown after some information had appered in the log view. + */ +enum { + NfcSceneEmulateStateWidget, /**< Widget view is displayed. */ + NfcSceneEmulateStateTextBox, /**< TextBox view is displayed. */ +}; + +static void nfc_protocol_support_scene_emulate_on_enter(NfcApp* instance) { + Widget* widget = instance->widget; + TextBox* text_box = instance->text_box; + + FuriString* temp_str = furi_string_alloc(); + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + + widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_47x61); + + if(nfc_protocol_support_has_feature(protocol, NfcProtocolFeatureEmulateUid)) { + widget_add_string_element( + widget, 90, 13, AlignCenter, AlignTop, FontPrimary, "Emulating UID"); + + size_t uid_len; + const uint8_t* uid = nfc_device_get_uid(instance->nfc_device, &uid_len); + + for(size_t i = 0; i < uid_len; ++i) { + furi_string_cat_printf(temp_str, "%02X ", uid[i]); + } + + furi_string_trim(temp_str); + + } else { + widget_add_string_element(widget, 90, 13, AlignCenter, AlignTop, FontPrimary, "Emulating"); + furi_string_set( + temp_str, nfc_device_get_name(instance->nfc_device, NfcDeviceNameTypeFull)); + } + + widget_add_text_box_element( + widget, 56, 28, 71, 25, AlignCenter, AlignTop, furi_string_get_cstr(temp_str), false); + + furi_string_free(temp_str); + + text_box_set_font(text_box, TextBoxFontHex); + text_box_set_focus(text_box, TextBoxFocusEnd); + furi_string_reset(instance->text_box_store); + + // instance->listener is allocated in the respective on_enter() handler + nfc_protocol_support[protocol]->scene_emulate.on_enter(instance); + + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneEmulate, NfcSceneEmulateStateWidget); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); + nfc_blink_emulate_start(instance); +} + +static bool + nfc_protocol_support_scene_emulate_on_event(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + const uint32_t state = scene_manager_get_scene_state(instance->scene_manager, NfcSceneEmulate); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventListenerUpdate) { + // Add data button to widget if data is received for the first time + if(furi_string_size(instance->text_box_store)) { + widget_add_button_element( + instance->widget, + GuiButtonTypeCenter, + "Log", + nfc_protocol_support_common_widget_callback, + instance); + } + // Update TextBox data + text_box_set_text(instance->text_box, furi_string_get_cstr(instance->text_box_store)); + consumed = true; + } else if(event.event == GuiButtonTypeCenter) { + if(state == NfcSceneEmulateStateWidget) { + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewTextBox); + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneEmulate, NfcSceneEmulateStateTextBox); + consumed = true; + } + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state == NfcSceneEmulateStateTextBox) { + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneEmulate, NfcSceneEmulateStateWidget); + consumed = true; + } + } + + return consumed; +} + +static void nfc_protocol_support_scene_emulate_stop_listener(NfcApp* instance) { + nfc_listener_stop(instance->listener); + + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + + if(protocol == nfc_listener_get_protocol(instance->listener)) { + const NfcDeviceData* data = nfc_listener_get_data(instance->listener, protocol); + + if(!nfc_device_is_equal_data(instance->nfc_device, protocol, data)) { + nfc_device_set_data(instance->nfc_device, protocol, data); + nfc_save_shadow_file(instance); + } + } + + nfc_listener_free(instance->listener); +} + +static void nfc_protocol_support_scene_emulate_on_exit(NfcApp* instance) { + nfc_protocol_support_scene_emulate_stop_listener(instance); + + // Clear view + widget_reset(instance->widget); + text_box_reset(instance->text_box); + furi_string_reset(instance->text_box_store); + + nfc_blink_stop(instance); +} + +static void nfc_protocol_support_scene_rpc_on_enter(NfcApp* instance) { + UNUSED(instance); +} + +static void nfc_protocol_support_scene_rpc_setup_ui_and_emulate(NfcApp* instance) { + nfc_text_store_set(instance, "emulating\n%s", furi_string_get_cstr(instance->file_name)); + + popup_set_header(instance->popup, "NFC", 89, 42, AlignCenter, AlignBottom); + popup_set_text(instance->popup, instance->text_store, 89, 44, AlignCenter, AlignTop); + popup_set_icon(instance->popup, 0, 12, &I_RFIDDolphinSend_97x61); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); + + notification_message(instance->notifications, &sequence_display_backlight_on); + nfc_blink_emulate_start(instance); + + const NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + nfc_protocol_support[protocol]->scene_emulate.on_enter(instance); + + instance->rpc_state = NfcRpcStateEmulating; +} + +static bool nfc_protocol_support_scene_rpc_on_event(NfcApp* instance, SceneManagerEvent event) { + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventRpcLoadFile) { + bool success = false; + if(instance->rpc_state == NfcRpcStateIdle) { + if(nfc_load_file(instance, instance->file_path, false)) { + nfc_protocol_support_scene_rpc_setup_ui_and_emulate(instance); + success = true; + } + } + rpc_system_app_confirm(instance->rpc_ctx, success); + } else if(event.event == NfcCustomEventRpcExit) { + rpc_system_app_confirm(instance->rpc_ctx, true); + scene_manager_stop(instance->scene_manager); + view_dispatcher_stop(instance->view_dispatcher); + } else if(event.event == NfcCustomEventRpcSessionClose) { + scene_manager_stop(instance->scene_manager); + view_dispatcher_stop(instance->view_dispatcher); + } + consumed = true; + } + + return consumed; +} + +static void nfc_protocol_support_scene_rpc_on_exit(NfcApp* instance) { + if(instance->rpc_state == NfcRpcStateEmulating) { + nfc_protocol_support_scene_emulate_stop_listener(instance); + } + + popup_reset(instance->popup); + text_box_reset(instance->text_box); + furi_string_reset(instance->text_box_store); + + nfc_blink_stop(instance); +} + +static const NfcProtocolSupportCommonSceneBase + nfc_protocol_support_scenes[NfcProtocolSupportSceneCount] = { + [NfcProtocolSupportSceneInfo] = + { + .on_enter = nfc_protocol_support_scene_info_on_enter, + .on_event = nfc_protocol_support_scene_info_on_event, + .on_exit = nfc_protocol_support_scene_info_on_exit, + }, + [NfcProtocolSupportSceneMoreInfo] = + { + .on_enter = nfc_protocol_support_scene_more_info_on_enter, + .on_event = nfc_protocol_support_scene_more_info_on_event, + .on_exit = nfc_protocol_support_scene_more_info_on_exit, + }, + [NfcProtocolSupportSceneRead] = + { + .on_enter = nfc_protocol_support_scene_read_on_enter, + .on_event = nfc_protocol_support_scene_read_on_event, + .on_exit = nfc_protocol_support_scene_read_on_exit, + }, + [NfcProtocolSupportSceneReadMenu] = + { + .on_enter = nfc_protocol_support_scene_read_menu_on_enter, + .on_event = nfc_protocol_support_scene_read_menu_on_event, + .on_exit = nfc_protocol_support_scene_read_saved_menu_on_exit, + }, + [NfcProtocolSupportSceneReadSuccess] = + { + .on_enter = nfc_protocol_support_scene_read_success_on_enter, + .on_event = nfc_protocol_support_scene_read_success_on_event, + .on_exit = nfc_protocol_support_scene_read_success_on_exit, + }, + [NfcProtocolSupportSceneSavedMenu] = + { + .on_enter = nfc_protocol_support_scene_saved_menu_on_enter, + .on_event = nfc_protocol_support_scene_saved_menu_on_event, + .on_exit = nfc_protocol_support_scene_read_saved_menu_on_exit, + }, + [NfcProtocolSupportSceneSaveName] = + { + .on_enter = nfc_protocol_support_scene_save_name_on_enter, + .on_event = nfc_protocol_support_scene_save_name_on_event, + .on_exit = nfc_protocol_support_scene_save_name_on_exit, + }, + [NfcProtocolSupportSceneEmulate] = + { + .on_enter = nfc_protocol_support_scene_emulate_on_enter, + .on_event = nfc_protocol_support_scene_emulate_on_event, + .on_exit = nfc_protocol_support_scene_emulate_on_exit, + }, + [NfcProtocolSupportSceneRpc] = + { + .on_enter = nfc_protocol_support_scene_rpc_on_enter, + .on_event = nfc_protocol_support_scene_rpc_on_event, + .on_exit = nfc_protocol_support_scene_rpc_on_exit, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.h new file mode 100644 index 00000000000..b6bfde45c04 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support.h @@ -0,0 +1,113 @@ +/** + * @file nfc_protocol_support.h + * @brief Interface for application-level protocol support. + * + * NFC protocol support helper abstracts common scenes with a single interface + * and lets each protocol decide on concrete implementation. + * + * # Integrating a new protocol into the application + * + * Most of the scenes in the NFC application work through abstract APIs, so they do not need + * protocol-specific versions of themselves. However, when such a situation + * occurs, the protocol support helper provides another level of abstraction to hide + * the protocol-specific details and isolate them to separate modules. + * + * @see nfc_protocol.h for more information on adding library protocols. + * + * The steps for adding support for a library protocol are described below. + * + * ## 1. Create the files + * + * ### 1.1 Recommended file structure + * + * The recommended file structure for a protocol support is as follows: + * + * ```text + * protocol_support + * | + * +- protocol_name + * | + * +- protocol_name.h + * | + * +- protocol_name.c + * | + * +- protocol_name_render.h + * | + * +- protocol_name_render.c + * | + * ``` + * ### 1.2 File structure explanation + * + * | Filename | Explanation | + * |:-----------------------|:------------| + * | protocol_name.h | Interface structure declaration used in `nfc_protocol_support_defs.c`. | + * | protocol_name.c | Protocol-specific scene implemenatations and definitions. | + * | protocol_name_render.h | Protocol-specific rendering (formatting) functions. Used for converting protocol data into textual descriptions. | + * | protocol_name_render.c | Implementations for functions declared in `protocol_name_render.h`.| + * + * ## 2. Implement the code + * + * ### 2.1 Features + * + * Decide what features the protocol will be providing. The features can be combined using bitwise OR (`"|"`). + * This choice influences which scenes will have to be implemented in step 2.2. + * + * @see NfcProtocolFeature for the enumeration of possible features to implement. + * + * ### 2.2 Scenes + * + * If a particular scene is not implemented, its empty placeholder from nfc_protocol_support_gui_common.h must be used instead. + * + * @see nfc_protocol_support_common.h for the enumeration of all scenes that can be implemented. + * @see nfc_protocol_support_base.h for the scene implementation details. + * + * ### 2.3. Registering the protocol support + * + * After completing the protocol support, it must be registered within the application in order for it to be usable. + * + * In nfc_protocol_support_defs.c, include the `protocol_name.h` file and add a new entry in the `nfc_protocol_support[]` + * array under the appropriate index. + * + * ## Done! + * + * @note It will not always be possible to abstract all of the protocol's functionality using the protocol support helper. + * In such cases, creating separate protocol-specific scenes is okay (as an example, note the `nfc/scenes/nfc_scene_mf_classic_*` scenes which didn't fit this paradigm). + */ +#pragma once + +#include + +#include "nfc_protocol_support_common.h" + +/** + * @brief Abstract interface for on_enter() scene handler. + * + * Is to be called whenever a scene is entered to. + * + * @param[in] scene identifier of the scene associated with the handler. + * @param[in,out] context pointer to a user-specified context (will be passed to concrete handler). + */ +void nfc_protocol_support_on_enter(NfcProtocolSupportScene scene, void* context); + +/** + * @brief Abstract interface for on_event() scene handler. + * + * @param[in] scene identifier of the scene associated with the handler. + * @param[in,out] context pointer to a user-specified context (will be passed to concrete handler). + * @param[in] event SceneManager event to be handled by the scene. + * @returns true if the event was consumed, false otherwise. + */ +bool nfc_protocol_support_on_event( + NfcProtocolSupportScene scene, + void* context, + SceneManagerEvent event); + +/** + * @brief Abstract interface for on_exit() scene handler. + * + * Is to be called whenever a scene is exited from. + * + * @param[in] scene identifier of the scene associated with the handler. + * @param[in,out] context pointer to a user-specified context (will be passed to concrete handler). + */ +void nfc_protocol_support_on_exit(NfcProtocolSupportScene scene, void* context); diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h new file mode 100644 index 00000000000..69a6d34d291 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_base.h @@ -0,0 +1,116 @@ +/** + * @file nfc_protocol_support_base.h + * @brief Abstract interface for application-level protocol support. + */ +#pragma once + +#include + +#include "../../nfc_app.h" + +/** + * @brief Scene entry handler. + * + * @param[in,out] instance pointer to the NFC application instance. + */ +typedef void (*NfcProtocolSupportOnEnter)(NfcApp* instance); + +/** + * @brief Scene event handler. + * + * @param[in,out] instance pointer to the NFC application instance. + * @param[in] event custom event that has occurred. + * @returns true if the event was handled, false otherwise. + */ +typedef bool (*NfcProtocolSupportOnEvent)(NfcApp* instance, uint32_t event); + +/** + * @brief Abstract scene interface. + * + * on_exit() handler is not implemented due to being redundant. + */ +typedef struct { + NfcProtocolSupportOnEnter on_enter; /**< Pointer to the on_enter() function. */ + NfcProtocolSupportOnEvent on_event; /**< Pointer to the on_event() function. */ +} NfcProtocolSupportSceneBase; + +/** + * @brief Abstract protocol support interface. + */ +typedef struct { + const uint32_t features; /**< Feature bitmask supported by the protocol. */ + + /** + * @brief Handlers for protocol-specific info scene. + * + * This scene displays general information about a saved or recently read card. + * It may include a button that will lead to more information being shown. + */ + NfcProtocolSupportSceneBase scene_info; + + /** + * @brief Handlers for protocol-specific extended info scene. + * + * This scene shows more information about a saved or + * recently read card, such as memory dumps. + * + * It may include (a) button(s) and/or menu(s) that will lead to + * protocol-specific scenes not covered in this helper. + */ + NfcProtocolSupportSceneBase scene_more_info; + + /** + * @brief Handlers for protocol-specific read scene. + * + * This scene is activated when a read operation is in progress. + * It is responsible for creating a poller and for handling its events. + */ + NfcProtocolSupportSceneBase scene_read; + + /** + * @brief Handlers for protocol-specific read menu scene. + * + * This scene presents the user with options available for the + * recenly read card. Such options may include: + * * Saving + * * Getting information + * * Emulating etc. + */ + NfcProtocolSupportSceneBase scene_read_menu; + + /** + * @brief Handlers for protocol-specific read success scene. + * + * This scene is activated after a successful read operation. + * It is responsible for displaying a very short summary about + * the card that was just read. + */ + NfcProtocolSupportSceneBase scene_read_success; + + /** + * @brief Handlers for protocol-specific saved file menu scene. + * + * This scene presents the user with options available for a + * card loaded from file. Such options may include: + * * Renaming + * * Deleting + * * Getting information + * * Emulating etc. + */ + NfcProtocolSupportSceneBase scene_saved_menu; + + /** + * @brief Handlers for protocol-specific name entry scene. + * + * This scene is used to enter a file name when saving or renaming a file. + */ + NfcProtocolSupportSceneBase scene_save_name; + + /** + * @brief Handlers for protocol-specific emulate scene. + * + * This scene is activated when an emulation operation is in progress. + * It is responsible for creating a listener and for handling its events. + */ + NfcProtocolSupportSceneBase scene_emulate; +} NfcProtocolSupportBase; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_common.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_common.h new file mode 100644 index 00000000000..6e3214106ad --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_common.h @@ -0,0 +1,36 @@ +/** + * @file nfc_protocol_support_common.h + * @brief Common application-level protocol support definitions. + */ +#pragma once + +/** + * @brief Enumeration of protocol features. + */ +typedef enum { + NfcProtocolFeatureNone = 0, /**< No features are supported. */ + NfcProtocolFeatureEmulateUid = 1UL << 0, /**< Partial emulation is supported. */ + NfcProtocolFeatureEmulateFull = 1UL << 1, /**< Complete emulation is supported. */ + NfcProtocolFeatureEditUid = 1UL << 2, /**< UID editing is supported. */ + NfcProtocolFeatureMoreInfo = 1UL << 3, /**< More information is provided. */ +} NfcProtocolFeature; + +/** + * @brief Enumeration of protocol-aware scenes. + * + * These are the scenes that are common to all protocols, but require + * a protocol-specific implementation. + */ +typedef enum { + NfcProtocolSupportSceneInfo, /**< Display general card information. */ + NfcProtocolSupportSceneMoreInfo, /**< Display more card information. */ + NfcProtocolSupportSceneRead, /**< Shown when reading a card. */ + NfcProtocolSupportSceneReadMenu, /**< Menu with options available for the recently read card. */ + NfcProtocolSupportSceneReadSuccess, /**< Shown after having successfully read a card. */ + NfcProtocolSupportSceneSavedMenu, /**< Menu for the card that was loaded from file. */ + NfcProtocolSupportSceneSaveName, /**< Shown when saving or renaming a file. */ + NfcProtocolSupportSceneEmulate, /**< Shown when emulating a card. */ + NfcProtocolSupportSceneRpc, /**< Shown in remote-controlled (RPC) mode. */ + + NfcProtocolSupportSceneCount, /**< Special value equal to total scene count. Internal use. */ +} NfcProtocolSupportScene; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c new file mode 100644 index 00000000000..215ffc4553a --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.c @@ -0,0 +1,45 @@ +/** + * @file nfc_protocol_support_defs.c + * @brief Application-level protocol support definitions. + * + * This file is to be modified whenever support for + * a new protocol is to be added. + */ +#include "nfc_protocol_support_base.h" + +#include + +#include "iso14443_3a/iso14443_3a.h" +#include "iso14443_3b/iso14443_3b.h" +#include "iso14443_4a/iso14443_4a.h" +#include "iso14443_4b/iso14443_4b.h" +#include "iso15693_3/iso15693_3.h" +#include "felica/felica.h" +#include "mf_ultralight/mf_ultralight.h" +#include "mf_classic/mf_classic.h" +#include "mf_desfire/mf_desfire.h" +#include "slix/slix.h" +#include "st25tb/st25tb.h" + +/** + * @brief Array of pointers to concrete protocol support implementations. + * + * When adding support for a new protocol, add it to the end of this array + * under its respective index. + * + * @see nfc_protocol.h + */ +const NfcProtocolSupportBase* nfc_protocol_support[NfcProtocolNum] = { + [NfcProtocolIso14443_3a] = &nfc_protocol_support_iso14443_3a, + [NfcProtocolIso14443_3b] = &nfc_protocol_support_iso14443_3b, + [NfcProtocolIso14443_4a] = &nfc_protocol_support_iso14443_4a, + [NfcProtocolIso14443_4b] = &nfc_protocol_support_iso14443_4b, + [NfcProtocolIso15693_3] = &nfc_protocol_support_iso15693_3, + [NfcProtocolFelica] = &nfc_protocol_support_felica, + [NfcProtocolMfUltralight] = &nfc_protocol_support_mf_ultralight, + [NfcProtocolMfClassic] = &nfc_protocol_support_mf_classic, + [NfcProtocolMfDesfire] = &nfc_protocol_support_mf_desfire, + [NfcProtocolSlix] = &nfc_protocol_support_slix, + [NfcProtocolSt25tb] = &nfc_protocol_support_st25tb, + /* Add new protocol support implementations here */ +}; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.h new file mode 100644 index 00000000000..7a9d5b6374d --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_defs.h @@ -0,0 +1,12 @@ +/** + * @file nfc_protocol_support_defs.h + * @brief Application-level protocol support declarations. + */ +#pragma once + +#include "nfc_protocol_support_base.h" + +/** + * @brief Declaraion of array of pointers to protocol support implementations. + */ +extern const NfcProtocolSupportBase* nfc_protocol_support[]; diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c new file mode 100644 index 00000000000..f3a85512555 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.c @@ -0,0 +1,42 @@ +#include "nfc_protocol_support_gui_common.h" + +#include "nfc/nfc_app_i.h" + +void nfc_protocol_support_common_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + NfcApp* instance = context; + view_dispatcher_send_custom_event(instance->view_dispatcher, index); +} + +void nfc_protocol_support_common_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + furi_assert(context); + NfcApp* instance = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_protocol_support_common_byte_input_done_callback(void* context) { + furi_assert(context); + NfcApp* instance = context; + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_protocol_support_common_text_input_done_callback(void* context) { + NfcApp* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone); +} + +void nfc_protocol_support_common_on_enter_empty(NfcApp* instance) { + UNUSED(instance); +} + +bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, uint32_t event) { + UNUSED(instance); + UNUSED(event); + return true; +} diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h new file mode 100644 index 00000000000..40ba40c8ec1 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h @@ -0,0 +1,85 @@ +/** + * @file nfc_protocol_support_gui_common.h + * @brief Common GUI functions and definitions. + */ +#pragma once + +#include + +#include "nfc/nfc_app.h" + +/** + * @brief Common submenu indices. + */ +enum { + SubmenuIndexCommonSave, /**< Save menu option. */ + SubmenuIndexCommonEmulate, /**< Emulate menu option. */ + SubmenuIndexCommonEdit, /**< Edit menu option. */ + SubmenuIndexCommonInfo, /**< Info menu option. */ + SubmenuIndexCommonRename, /**< Rename menu option. */ + SubmenuIndexCommonDelete, /**< Delete menu option. */ + SubmenuIndexCommonRestore, /**< Restore menu option. */ + SubmenuIndexCommonMax, /**< Special value, internal use. */ +}; + +/** + * @brief Common submenu callback. + * + * Called each time the user presses on a selected submenu item. + * + * @param[in,out] context pointer to a user-defined context object. + * @param[in] index index of the item that was activated. + */ +void nfc_protocol_support_common_submenu_callback(void* context, uint32_t index); + +/** + * @brief Common widget callback. + * + * Called each time the user presses on a selected widget element. + * + * @param[in] result identifier of the activated button. + * @param[in] type type of press action. + * @param[in,out] context pointer to a user-defined context object. + */ +void nfc_protocol_support_common_widget_callback( + GuiButtonType result, + InputType type, + void* context); + +/** + * @brief Common byte input callback. + * + * Called each time the user accepts the byte input. + * + * @param[in,out] context pointer to a user-defined context object. + */ +void nfc_protocol_support_common_byte_input_done_callback(void* context); + +/** + * @brief Common text input callback. + * + * Called each time the user accepts the text input. + * + * @param[in,out] context pointer to a user-defined context object. + */ +void nfc_protocol_support_common_text_input_done_callback(void* context); + +/** + * @brief Empty on_enter() handler. + * + * Does nothing. + * + * @param[in] instance pointer to the NFC application instance. + */ +void nfc_protocol_support_common_on_enter_empty(NfcApp* instance); + +/** + * @brief Empty on_event() handler. + * + * Does nothing and returns true. + * + * @param[in] instance pointer to the NFC application instance. + * @param[in] event custom event type that has occurred. + * @returns always true. + */ +bool nfc_protocol_support_common_on_event_empty(NfcApp* instance, uint32_t event); diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_render_common.h b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_render_common.h new file mode 100644 index 00000000000..a2e82ea1535 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_render_common.h @@ -0,0 +1,13 @@ +/** + * @file nfc_protocol_support_render_common.h + * @brief Common formatting-related defines. + */ +#pragma once + +/** + * @brief Displayed information verbosity level. + */ +typedef enum { + NfcProtocolFormatTypeShort, /**< Short format, terse info. */ + NfcProtocolFormatTypeFull, /**< Full format, verbose info. */ +} NfcProtocolFormatType; diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix.c b/applications/main/nfc/helpers/protocol_support/slix/slix.c new file mode 100644 index 00000000000..ad858a75fce --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/slix/slix.c @@ -0,0 +1,141 @@ +#include "slix.h" +#include "slix_render.h" + +#include +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_common.h" +#include "../nfc_protocol_support_gui_common.h" + +static void nfc_scene_info_on_enter_slix(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const SlixData* data = nfc_device_get_data(device, NfcProtocolSlix); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_slix_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand nfc_scene_read_poller_callback_slix(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolSlix); + + NfcApp* instance = context; + const SlixPollerEvent* slix_event = event.event_data; + + if(slix_event->type == SlixPollerEventTypeReady) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolSlix, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_slix(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_slix, instance); +} + +static void nfc_scene_read_success_on_enter_slix(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const SlixData* data = nfc_device_get_data(device, NfcProtocolSlix); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_slix_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand nfc_scene_emulate_listener_callback_slix(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolSlix); + furi_assert(event.event_data); + + NfcApp* nfc = context; + SlixListenerEvent* slix_event = event.event_data; + + if(slix_event->type == SlixListenerEventTypeCustomCommand) { + if(furi_string_size(nfc->text_box_store) < NFC_LOG_SIZE_MAX) { + furi_string_cat_printf(nfc->text_box_store, "R:"); + for(size_t i = 0; i < bit_buffer_get_size_bytes(slix_event->data->buffer); i++) { + furi_string_cat_printf( + nfc->text_box_store, + " %02X", + bit_buffer_get_byte(slix_event->data->buffer, i)); + } + furi_string_push_back(nfc->text_box_store, '\n'); + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventListenerUpdate); + } + } + + return NfcCommandContinue; +} + +static void nfc_scene_emulate_on_enter_slix(NfcApp* instance) { + const SlixData* data = nfc_device_get_data(instance->nfc_device, NfcProtocolSlix); + + instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolSlix, data); + nfc_listener_start(instance->listener, nfc_scene_emulate_listener_callback_slix, instance); +} + +static bool nfc_scene_saved_menu_on_event_slix(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexCommonEdit) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); + return true; + } + + return false; +} + +const NfcProtocolSupportBase nfc_protocol_support_slix = { + .features = NfcProtocolFeatureEmulateFull, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_slix, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_slix, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_slix, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_saved_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_scene_saved_menu_on_event_slix, + }, + .scene_save_name = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_emulate = + { + .on_enter = nfc_scene_emulate_on_enter_slix, + .on_event = nfc_protocol_support_common_on_event_empty, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix.h b/applications/main/nfc/helpers/protocol_support/slix/slix.h new file mode 100644 index 00000000000..9c7504ebafa --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/slix/slix.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_slix; diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix_render.c b/applications/main/nfc/helpers/protocol_support/slix/slix_render.c new file mode 100644 index 00000000000..80f953db973 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/slix/slix_render.c @@ -0,0 +1,74 @@ +#include "slix_render.h" + +#include "../iso15693_3/iso15693_3_render.h" + +void nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_type, FuriString* str) { + nfc_render_iso15693_3_brief(slix_get_base_data(data), str); + + if(format_type != NfcProtocolFormatTypeFull) return; + const SlixType slix_type = slix_get_type(data); + + furi_string_cat(str, "\n\e#Passwords\n"); + + static const char* slix_password_names[] = { + "Read", + "Write", + "Privacy", + "Destroy", + "EAS/AFI", + }; + + for(uint32_t i = 0; i < SlixPasswordTypeCount; ++i) { + if(slix_type_supports_password(slix_type, i)) { + furi_string_cat_printf( + str, "%s : %08lX\n", slix_password_names[i], data->passwords[i]); + } + } + + furi_string_cat(str, "\e#Lock bits\n"); + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_EAS)) { + furi_string_cat_printf( + str, "EAS: %s locked\n", data->system_info.lock_bits.eas ? "" : "not"); + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) { + furi_string_cat_printf( + str, "PPL: %s locked\n", data->system_info.lock_bits.ppl ? "" : "not"); + + const SlixProtection protection = data->system_info.protection; + + furi_string_cat(str, "\e#Page protection\n"); + furi_string_cat_printf(str, "Pointer: H >= %02X\n", protection.pointer); + + const char* rh = (protection.condition & SLIX_PP_CONDITION_RH) ? "" : "un"; + const char* rl = (protection.condition & SLIX_PP_CONDITION_RL) ? "" : "un"; + + const char* wh = (protection.condition & SLIX_PP_CONDITION_WH) ? "" : "un"; + const char* wl = (protection.condition & SLIX_PP_CONDITION_WL) ? "" : "un"; + + furi_string_cat_printf(str, "R: H %sprotec. L %sprotec.\n", rh, rl); + furi_string_cat_printf(str, "W: H %sprotec. L %sprotec.\n", wh, wl); + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PRIVACY)) { + furi_string_cat(str, "\e#Privacy\n"); + furi_string_cat_printf(str, "Privacy mode: %sabled\n", data->privacy ? "en" : "dis"); + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_SIGNATURE)) { + furi_string_cat(str, "\e#Signature\n"); + for(uint32_t i = 0; i < 4; ++i) { + furi_string_cat_printf(str, "%02X ", data->signature[i]); + } + + furi_string_cat(str, "[ ... ]"); + + for(uint32_t i = 0; i < 3; ++i) { + furi_string_cat_printf(str, " %02X", data->signature[sizeof(SlixSignature) - i - 1]); + } + } + + furi_string_cat(str, "\n\e#ISO15693-3 data"); + nfc_render_iso15693_3_extra(slix_get_base_data(data), str); +} diff --git a/applications/main/nfc/helpers/protocol_support/slix/slix_render.h b/applications/main/nfc/helpers/protocol_support/slix/slix_render.h new file mode 100644 index 00000000000..98ae6dc97fd --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/slix/slix_render.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_slix_info(const SlixData* data, NfcProtocolFormatType format_type, FuriString* str); diff --git a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c new file mode 100644 index 00000000000..e22af48b34e --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.c @@ -0,0 +1,110 @@ +#include "st25tb.h" +#include "st25tb_render.h" + +#include + +#include "nfc/nfc_app_i.h" + +#include "../nfc_protocol_support_common.h" +#include "../nfc_protocol_support_gui_common.h" + +static void nfc_scene_info_on_enter_st25tb(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const St25tbData* data = nfc_device_get_data(device, NfcProtocolSt25tb); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_st25tb_info(data, NfcProtocolFormatTypeFull, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 64, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static NfcCommand nfc_scene_read_poller_callback_st25tb(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolSt25tb); + + NfcApp* instance = context; + const St25tbPollerEvent* st25tb_event = event.event_data; + + if(st25tb_event->type == St25tbPollerEventTypeRequestMode) { + st25tb_event->data->mode_request.mode = St25tbPollerModeRead; + } else if(st25tb_event->type == St25tbPollerEventTypeSuccess) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolSt25tb, nfc_poller_get_data(instance->poller)); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + return NfcCommandStop; + } + + return NfcCommandContinue; +} + +static void nfc_scene_read_on_enter_st25tb(NfcApp* instance) { + nfc_poller_start(instance->poller, nfc_scene_read_poller_callback_st25tb, instance); +} + +static void nfc_scene_read_success_on_enter_st25tb(NfcApp* instance) { + const NfcDevice* device = instance->nfc_device; + const St25tbData* data = nfc_device_get_data(device, NfcProtocolSt25tb); + + FuriString* temp_str = furi_string_alloc(); + furi_string_cat_printf( + temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + nfc_render_st25tb_info(data, NfcProtocolFormatTypeShort, temp_str); + + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + + furi_string_free(temp_str); +} + +static bool nfc_scene_saved_menu_on_event_st25tb(NfcApp* instance, uint32_t event) { + if(event == SubmenuIndexCommonEdit) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); + return true; + } + + return false; +} + +const NfcProtocolSupportBase nfc_protocol_support_st25tb = { + .features = NfcProtocolFeatureNone, + + .scene_info = + { + .on_enter = nfc_scene_info_on_enter_st25tb, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read = + { + .on_enter = nfc_scene_read_on_enter_st25tb, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_read_success = + { + .on_enter = nfc_scene_read_success_on_enter_st25tb, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_saved_menu = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_scene_saved_menu_on_event_st25tb, + }, + .scene_save_name = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, + .scene_emulate = + { + .on_enter = nfc_protocol_support_common_on_enter_empty, + .on_event = nfc_protocol_support_common_on_event_empty, + }, +}; diff --git a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.h b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.h new file mode 100644 index 00000000000..3b635fdefc3 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../nfc_protocol_support_base.h" + +extern const NfcProtocolSupportBase nfc_protocol_support_st25tb; diff --git a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb_render.c b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb_render.c new file mode 100644 index 00000000000..e3a0f3c50f8 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb_render.c @@ -0,0 +1,22 @@ +#include "st25tb_render.h" +#include + +void nfc_render_st25tb_info( + const St25tbData* data, + NfcProtocolFormatType format_type, + FuriString* str) { + furi_string_cat_printf(str, "UID"); + + for(size_t i = 0; i < ST25TB_UID_SIZE; i++) { + furi_string_cat_printf(str, " %02X", data->uid[i]); + } + + if(format_type == NfcProtocolFormatTypeFull) { + furi_string_cat_printf(str, "\nSys. OTP: %08lX", data->system_otp_block); + furi_string_cat_printf(str, "\nBlocks:"); + for(size_t i = 0; i < st25tb_get_block_count(data->type); i += 2) { + furi_string_cat_printf( + str, "\n %02X %08lX %08lX", i, data->blocks[i], data->blocks[i + 1]); + } + } +} diff --git a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb_render.h b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb_render.h new file mode 100644 index 00000000000..9f7be34e9b0 --- /dev/null +++ b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb_render.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include "../nfc_protocol_support_render_common.h" + +void nfc_render_st25tb_info( + const St25tbData* data, + NfcProtocolFormatType format_type, + FuriString* str); diff --git a/applications/main/nfc/icon.png b/applications/main/nfc/icon.png new file mode 100644 index 00000000000..6bc027111a7 Binary files /dev/null and b/applications/main/nfc/icon.png differ diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c new file mode 100644 index 00000000000..141a67e5cba --- /dev/null +++ b/applications/main/nfc/nfc_app.c @@ -0,0 +1,500 @@ +#include "nfc_app_i.h" + +#include + +bool nfc_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + NfcApp* nfc = context; + return scene_manager_handle_custom_event(nfc->scene_manager, event); +} + +bool nfc_back_event_callback(void* context) { + furi_assert(context); + NfcApp* nfc = context; + return scene_manager_handle_back_event(nfc->scene_manager); +} + +static void nfc_app_rpc_command_callback(const RpcAppSystemEvent* event, void* context) { + furi_assert(context); + NfcApp* nfc = (NfcApp*)context; + + furi_assert(nfc->rpc_ctx); + + if(event->type == RpcAppEventTypeSessionClose) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcSessionClose); + rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); + nfc->rpc_ctx = NULL; + } else if(event->type == RpcAppEventTypeAppExit) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcExit); + } else if(event->type == RpcAppEventTypeLoadFile) { + furi_assert(event->data.type == RpcAppSystemEventDataTypeString); + furi_string_set(nfc->file_path, event->data.string); + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcLoadFile); + } else { + rpc_system_app_confirm(nfc->rpc_ctx, false); + } +} + +NfcApp* nfc_app_alloc() { + NfcApp* instance = malloc(sizeof(NfcApp)); + + instance->view_dispatcher = view_dispatcher_alloc(); + instance->scene_manager = scene_manager_alloc(&nfc_scene_handlers, instance); + 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, nfc_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + instance->view_dispatcher, nfc_back_event_callback); + + instance->nfc = nfc_alloc(); + + instance->mf_ul_auth = mf_ultralight_auth_alloc(); + instance->mfc_key_cache = mf_classic_key_cache_alloc(); + + // Nfc device + instance->nfc_device = nfc_device_alloc(); + nfc_device_set_loading_callback(instance->nfc_device, nfc_show_loading_popup, instance); + + // Open GUI record + instance->gui = furi_record_open(RECORD_GUI); + + // Open Notification record + instance->notifications = furi_record_open(RECORD_NOTIFICATION); + + // Open Storage record + instance->storage = furi_record_open(RECORD_STORAGE); + + // Open Dialogs record + instance->dialogs = furi_record_open(RECORD_DIALOGS); + + // Submenu + instance->submenu = submenu_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcViewMenu, submenu_get_view(instance->submenu)); + + // Dialog + instance->dialog_ex = dialog_ex_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcViewDialogEx, dialog_ex_get_view(instance->dialog_ex)); + + // Popup + instance->popup = popup_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcViewPopup, popup_get_view(instance->popup)); + + // Loading + instance->loading = loading_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcViewLoading, loading_get_view(instance->loading)); + + // Text Input + instance->text_input = text_input_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcViewTextInput, text_input_get_view(instance->text_input)); + + // Byte Input + instance->byte_input = byte_input_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcViewByteInput, byte_input_get_view(instance->byte_input)); + + // TextBox + instance->text_box = text_box_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcViewTextBox, text_box_get_view(instance->text_box)); + instance->text_box_store = furi_string_alloc(); + + // Custom Widget + instance->widget = widget_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcViewWidget, widget_get_view(instance->widget)); + + // Dict attack + + instance->dict_attack = dict_attack_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(instance->dict_attack)); + + // Detect Reader + instance->detect_reader = detect_reader_alloc(); + view_dispatcher_add_view( + instance->view_dispatcher, + NfcViewDetectReader, + detect_reader_get_view(instance->detect_reader)); + + instance->iso14443_3a_edit_data = iso14443_3a_alloc(); + instance->file_path = furi_string_alloc_set(NFC_APP_FOLDER); + instance->file_name = furi_string_alloc(); + + return instance; +} + +void nfc_app_free(NfcApp* instance) { + furi_assert(instance); + + if(instance->rpc_ctx) { + rpc_system_app_send_exited(instance->rpc_ctx); + rpc_system_app_set_callback(instance->rpc_ctx, NULL, NULL); + } + + nfc_free(instance->nfc); + + mf_ultralight_auth_free(instance->mf_ul_auth); + mf_classic_key_cache_free(instance->mfc_key_cache); + + // Nfc device + nfc_device_free(instance->nfc_device); + + // Submenu + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewMenu); + submenu_free(instance->submenu); + + // DialogEx + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewDialogEx); + dialog_ex_free(instance->dialog_ex); + + // Popup + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewPopup); + popup_free(instance->popup); + + // Loading + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewLoading); + loading_free(instance->loading); + + // TextInput + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewTextInput); + text_input_free(instance->text_input); + + // ByteInput + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewByteInput); + byte_input_free(instance->byte_input); + + // TextBox + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewTextBox); + text_box_free(instance->text_box); + furi_string_free(instance->text_box_store); + + // Custom Widget + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewWidget); + widget_free(instance->widget); + + // Dict attack + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewDictAttack); + dict_attack_free(instance->dict_attack); + + // Detect reader + view_dispatcher_remove_view(instance->view_dispatcher, NfcViewDetectReader); + detect_reader_free(instance->detect_reader); + + // View Dispatcher + view_dispatcher_free(instance->view_dispatcher); + + // Scene Manager + scene_manager_free(instance->scene_manager); + + furi_record_close(RECORD_DIALOGS); + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_NOTIFICATION); + // GUI + furi_record_close(RECORD_GUI); + instance->gui = NULL; + + instance->notifications = NULL; + + iso14443_3a_free(instance->iso14443_3a_edit_data); + furi_string_free(instance->file_path); + furi_string_free(instance->file_name); + + free(instance); +} + +void nfc_text_store_set(NfcApp* nfc, const char* text, ...) { + va_list args; + va_start(args, text); + + vsnprintf(nfc->text_store, sizeof(nfc->text_store), text, args); + + va_end(args); +} + +void nfc_text_store_clear(NfcApp* nfc) { + memset(nfc->text_store, 0, sizeof(nfc->text_store)); +} + +void nfc_blink_read_start(NfcApp* nfc) { + notification_message(nfc->notifications, &sequence_blink_start_cyan); +} + +void nfc_blink_emulate_start(NfcApp* nfc) { + notification_message(nfc->notifications, &sequence_blink_start_magenta); +} + +void nfc_blink_detect_start(NfcApp* nfc) { + notification_message(nfc->notifications, &sequence_blink_start_yellow); +} + +void nfc_blink_stop(NfcApp* nfc) { + notification_message(nfc->notifications, &sequence_blink_stop); +} + +void nfc_make_app_folders(NfcApp* instance) { + furi_assert(instance); + + if(!storage_simply_mkdir(instance->storage, NFC_APP_FOLDER)) { + dialog_message_show_storage_error(instance->dialogs, "Cannot create\napp folder"); + } +} + +bool nfc_save_file(NfcApp* instance, FuriString* path) { + furi_assert(instance); + furi_assert(path); + + bool result = nfc_device_save(instance->nfc_device, furi_string_get_cstr(instance->file_path)); + + if(!result) { + dialog_message_show_storage_error(instance->dialogs, "Cannot save\nkey file"); + } + + return result; +} + +static bool nfc_set_shadow_file_path(FuriString* file_path, FuriString* shadow_file_path) { + furi_assert(file_path); + furi_assert(shadow_file_path); + + bool shadow_file_path_set = false; + if(furi_string_end_with(file_path, NFC_APP_SHADOW_EXTENSION)) { + furi_string_set(shadow_file_path, file_path); + shadow_file_path_set = true; + } else if(furi_string_end_with(file_path, NFC_APP_EXTENSION)) { + size_t path_len = furi_string_size(file_path); + // Cut .nfc + furi_string_set_n(shadow_file_path, file_path, 0, path_len - 4); + furi_string_cat_printf(shadow_file_path, "%s", NFC_APP_SHADOW_EXTENSION); + shadow_file_path_set = true; + } + + return shadow_file_path_set; +} + +static bool nfc_has_shadow_file_internal(NfcApp* instance, FuriString* path) { + furi_assert(path); + + bool has_shadow_file = false; + FuriString* shadow_file_path = furi_string_alloc(); + do { + if(furi_string_empty(path)) break; + if(!nfc_set_shadow_file_path(path, shadow_file_path)) break; + has_shadow_file = + storage_common_exists(instance->storage, furi_string_get_cstr(shadow_file_path)); + } while(false); + + furi_string_free(shadow_file_path); + + return has_shadow_file; +} + +bool nfc_has_shadow_file(NfcApp* instance) { + furi_assert(instance); + + return nfc_has_shadow_file_internal(instance, instance->file_path); +} + +static bool nfc_save_internal(NfcApp* instance, const char* extension) { + furi_assert(instance); + furi_assert(extension); + + bool result = false; + + nfc_make_app_folders(instance); + + if(furi_string_end_with(instance->file_path, NFC_APP_EXTENSION) || + (furi_string_end_with(instance->file_path, NFC_APP_SHADOW_EXTENSION))) { + size_t filename_start = furi_string_search_rchar(instance->file_path, '/'); + furi_string_left(instance->file_path, filename_start); + } + + furi_string_cat_printf( + instance->file_path, "/%s%s", furi_string_get_cstr(instance->file_name), extension); + + result = nfc_save_file(instance, instance->file_path); + + return result; +} + +bool nfc_save_shadow_file(NfcApp* instance) { + furi_assert(instance); + + return nfc_save_internal(instance, NFC_APP_SHADOW_EXTENSION); +} + +bool nfc_save(NfcApp* instance) { + furi_assert(instance); + + return nfc_save_internal(instance, NFC_APP_EXTENSION); +} + +bool nfc_load_file(NfcApp* instance, FuriString* path, bool show_dialog) { + furi_assert(instance); + furi_assert(path); + bool result = false; + + FuriString* load_path = furi_string_alloc(); + if(nfc_has_shadow_file_internal(instance, path)) { + nfc_set_shadow_file_path(path, load_path); + } else if(furi_string_end_with(path, NFC_APP_SHADOW_EXTENSION)) { + size_t path_len = furi_string_size(path); + furi_string_set_n(load_path, path, 0, path_len - 4); + furi_string_cat_printf(load_path, "%s", NFC_APP_EXTENSION); + } else { + furi_string_set(load_path, path); + } + + result = nfc_device_load(instance->nfc_device, furi_string_get_cstr(load_path)); + + if(result) { + path_extract_filename(load_path, instance->file_name, true); + } + + if((!result) && (show_dialog)) { + dialog_message_show_storage_error(instance->dialogs, "Cannot load\nkey file"); + } + + furi_string_free(load_path); + + return result; +} + +bool nfc_delete(NfcApp* instance) { + furi_assert(instance); + + if(nfc_has_shadow_file(instance)) { + nfc_delete_shadow_file(instance); + } + + if(furi_string_end_with_str(instance->file_path, NFC_APP_SHADOW_EXTENSION)) { + size_t path_len = furi_string_size(instance->file_path); + furi_string_replace_at(instance->file_path, path_len - 4, 4, NFC_APP_EXTENSION); + } + + return storage_simply_remove(instance->storage, furi_string_get_cstr(instance->file_path)); +} + +bool nfc_delete_shadow_file(NfcApp* instance) { + furi_assert(instance); + + FuriString* shadow_file_path = furi_string_alloc(); + + bool result = nfc_set_shadow_file_path(instance->file_path, shadow_file_path) && + storage_simply_remove(instance->storage, furi_string_get_cstr(shadow_file_path)); + + furi_string_free(shadow_file_path); + return result; +} + +bool nfc_load_from_file_select(NfcApp* instance) { + furi_assert(instance); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, NFC_APP_EXTENSION, &I_Nfc_10px); + browser_options.base_path = NFC_APP_FOLDER; + browser_options.hide_dot_files = true; + + // Input events and views are managed by file_browser + bool result = dialog_file_browser_show( + instance->dialogs, instance->file_path, instance->file_path, &browser_options); + + if(result) { + result = nfc_load_file(instance, instance->file_path, true); + } + + return result; +} + +void nfc_show_loading_popup(void* context, bool show) { + NfcApp* nfc = context; + + if(show) { + // Raise timer priority so that animations can play + furi_timer_set_thread_priority(FuriTimerThreadPriorityElevated); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewLoading); + } else { + // Restore default timer priority + furi_timer_set_thread_priority(FuriTimerThreadPriorityNormal); + } +} + +void nfc_app_set_detected_protocols(NfcApp* instance, const NfcProtocol* types, uint32_t count) { + furi_assert(instance); + furi_assert(types); + furi_assert(count < NfcProtocolNum); + + memcpy(instance->protocols_detected, types, count); + instance->protocols_detected_num = count; + instance->protocols_detected_selected_idx = 0; +} + +void nfc_app_reset_detected_protocols(NfcApp* instance) { + furi_assert(instance); + + instance->protocols_detected_selected_idx = 0; + instance->protocols_detected_num = 0; +} + +static bool nfc_is_hal_ready() { + if(furi_hal_nfc_is_hal_ready() != FuriHalNfcErrorNone) { + // No connection to the chip, show an error screen + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_text( + message, + "Error!\nNFC chip failed to start\n\n\nSend a photo of this to:\nsupport@flipperzero.one", + 0, + 0, + AlignLeft, + AlignTop); + dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + return false; + } else { + return true; + } +} + +int32_t nfc_app(void* p) { + if(!nfc_is_hal_ready()) return 0; + + NfcApp* nfc = nfc_app_alloc(); + const char* args = p; + + if(args && strlen(args)) { + if(sscanf(args, "RPC %p", &nfc->rpc_ctx) == 1) { + rpc_system_app_set_callback(nfc->rpc_ctx, nfc_app_rpc_command_callback, nfc); + rpc_system_app_send_started(nfc->rpc_ctx); + view_dispatcher_attach_to_gui( + nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeDesktop); + scene_manager_next_scene(nfc->scene_manager, NfcSceneRpc); + } else { + view_dispatcher_attach_to_gui( + nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen); + + furi_string_set(nfc->file_path, args); + if(nfc_load_file(nfc, nfc->file_path, false)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulate); + } else { + view_dispatcher_stop(nfc->view_dispatcher); + } + } + } else { + view_dispatcher_attach_to_gui( + nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen); + scene_manager_next_scene(nfc->scene_manager, NfcSceneStart); + } + + view_dispatcher_run(nfc->view_dispatcher); + + nfc_app_free(nfc); + + return 0; +} diff --git a/applications/main/nfc/nfc_app.h b/applications/main/nfc/nfc_app.h new file mode 100644 index 00000000000..c3026d30287 --- /dev/null +++ b/applications/main/nfc/nfc_app.h @@ -0,0 +1,19 @@ +/** + * @file nfc_app.h + * @brief NFC application -- start here. + * + * Application for interfacing with NFC cards and other devices via Flipper's built-in NFC hardware. + * + * Main features: + * * Multiple protocols support + * * Card emulation + * * Shadow file support + * * Dynamically loaded parser plugins + * + * @see nfc_protocol.h for information on adding a new library protocol. + * @see nfc_protocol_support.h for information on integrating a library protocol into the app. + * @see nfc_supported_card_plugin.h for information on adding supported card plugins (parsers). + */ +#pragma once + +typedef struct NfcApp NfcApp; diff --git a/applications/main/nfc/nfc_app_i.h b/applications/main/nfc/nfc_app_i.h new file mode 100644 index 00000000000..3c0092007af --- /dev/null +++ b/applications/main/nfc/nfc_app_i.h @@ -0,0 +1,192 @@ +#pragma once + +#include "nfc_app.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "views/dict_attack.h" +#include "views/detect_reader.h" +#include "views/dict_attack.h" + +#include +#include "helpers/nfc_custom_event.h" +#include "helpers/mf_ultralight_auth.h" +#include "helpers/mf_user_dict.h" +#include "helpers/mfkey32_logger.h" +#include "helpers/mf_classic_key_cache.h" + +#include +#include +#include + +#include "rpc/rpc_app.h" + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#define NFC_NAME_SIZE 22 +#define NFC_TEXT_STORE_SIZE 128 +#define NFC_BYTE_INPUT_STORE_SIZE 10 +#define NFC_LOG_SIZE_MAX (1024) +#define NFC_APP_FOLDER ANY_PATH("nfc") +#define NFC_APP_EXTENSION ".nfc" +#define NFC_APP_SHADOW_EXTENSION ".shd" +#define NFC_APP_FILENAME_PREFIX "NFC" + +#define NFC_APP_MFKEY32_LOGS_FILE_NAME ".mfkey32.log" +#define NFC_APP_MFKEY32_LOGS_FILE_PATH (NFC_APP_FOLDER "/" NFC_APP_MFKEY32_LOGS_FILE_NAME) + +#define NFC_APP_MF_CLASSIC_DICT_USER_PATH (NFC_APP_FOLDER "/assets/mf_classic_dict_user.nfc") +#define NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH (NFC_APP_FOLDER "/assets/mf_classic_dict.nfc") + +typedef enum { + NfcRpcStateIdle, + NfcRpcStateEmulating, +} NfcRpcState; + +typedef struct { + NfcDict* dict; + uint8_t sectors_total; + uint8_t sectors_read; + uint8_t current_sector; + uint8_t keys_found; + size_t dict_keys_total; + size_t dict_keys_current; + bool is_key_attack; + uint8_t key_attack_current_sector; + bool is_card_present; +} NfcMfClassicDictAttackContext; + +struct NfcApp { + DialogsApp* dialogs; + Storage* storage; + Gui* gui; + ViewDispatcher* view_dispatcher; + NotificationApp* notifications; + SceneManager* scene_manager; + + char text_store[NFC_TEXT_STORE_SIZE + 1]; + FuriString* text_box_store; + uint8_t byte_input_store[NFC_BYTE_INPUT_STORE_SIZE]; + + uint32_t protocols_detected_num; + NfcProtocol protocols_detected[NfcProtocolNum]; + uint32_t protocols_detected_selected_idx; + + RpcAppSystem* rpc_ctx; + NfcRpcState rpc_state; + + // Common Views + Submenu* submenu; + DialogEx* dialog_ex; + Popup* popup; + Loading* loading; + TextInput* text_input; + ByteInput* byte_input; + TextBox* text_box; + Widget* widget; + DetectReader* detect_reader; + DictAttack* dict_attack; + + Nfc* nfc; + NfcPoller* poller; + NfcScanner* scanner; + NfcListener* listener; + + MfUltralightAuth* mf_ul_auth; + NfcMfClassicDictAttackContext nfc_dict_context; + Mfkey32Logger* mfkey32_logger; + MfUserDict* mf_user_dict; + MfClassicKeyCache* mfc_key_cache; + + NfcDevice* nfc_device; + Iso14443_3aData* iso14443_3a_edit_data; + FuriString* file_path; + FuriString* file_name; + FuriTimer* timer; +}; + +typedef enum { + NfcViewMenu, + NfcViewDialogEx, + NfcViewPopup, + NfcViewLoading, + NfcViewTextInput, + NfcViewByteInput, + NfcViewTextBox, + NfcViewWidget, + NfcViewDictAttack, + NfcViewDetectReader, +} NfcView; + +int32_t nfc_task(void* p); + +void nfc_text_store_set(NfcApp* nfc, const char* text, ...); + +void nfc_text_store_clear(NfcApp* nfc); + +void nfc_blink_read_start(NfcApp* nfc); + +void nfc_blink_emulate_start(NfcApp* nfc); + +void nfc_blink_detect_start(NfcApp* nfc); + +void nfc_blink_stop(NfcApp* nfc); + +void nfc_show_loading_popup(void* context, bool show); + +bool nfc_has_shadow_file(NfcApp* instance); + +bool nfc_save_shadow_file(NfcApp* instance); + +bool nfc_delete_shadow_file(NfcApp* instance); + +bool nfc_save(NfcApp* instance); + +bool nfc_delete(NfcApp* instance); + +bool nfc_load_from_file_select(NfcApp* instance); + +bool nfc_load_file(NfcApp* instance, FuriString* path, bool show_dialog); + +bool nfc_save_file(NfcApp* instance, FuriString* path); + +void nfc_make_app_folder(NfcApp* instance); + +void nfc_app_set_detected_protocols(NfcApp* instance, const NfcProtocol* types, uint32_t count); + +void nfc_app_reset_detected_protocols(NfcApp* instance); diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c new file mode 100644 index 00000000000..b5a40b122da --- /dev/null +++ b/applications/main/nfc/nfc_cli.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include + +#include + +#define FLAG_EVENT (1 << 10) + +static void nfc_cli_print_usage() { + printf("Usage:\r\n"); + printf("nfc \r\n"); + printf("Cmd list:\r\n"); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + printf("\tfield\t - turn field on\r\n"); + } +} + +static void nfc_cli_field(Cli* cli, FuriString* args) { + UNUSED(args); + // Check if nfc worker is not busy + if(furi_hal_nfc_is_hal_ready() != FuriHalNfcErrorNone) { + printf("NFC chip failed to start\r\n"); + return; + } + + furi_hal_nfc_acquire(); + furi_hal_nfc_low_power_mode_stop(); + furi_hal_nfc_poller_field_on(); + + printf("Field is on. Don't leave device in this mode for too long.\r\n"); + printf("Press Ctrl+C to abort\r\n"); + + while(!cli_cmd_interrupt_received(cli)) { + furi_delay_ms(50); + } + + furi_hal_nfc_low_power_mode_start(); + furi_hal_nfc_release(); +} + +static void nfc_cli(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + FuriString* cmd; + cmd = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, cmd)) { + nfc_cli_print_usage(); + break; + } + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + if(furi_string_cmp_str(cmd, "field") == 0) { + nfc_cli_field(cli, args); + break; + } + } + + nfc_cli_print_usage(); + } while(false); + + furi_string_free(cmd); +} + +void nfc_on_system_start() { +#ifdef SRV_CLI + Cli* cli = furi_record_open(RECORD_CLI); + cli_add_command(cli, "nfc", CliCommandFlagDefault, nfc_cli, NULL); + furi_record_close(RECORD_CLI); +#else + UNUSED(nfc_cli); +#endif +} diff --git a/applications/main/nfc/plugins/supported_cards/all_in_one.c b/applications/main/nfc/plugins/supported_cards/all_in_one.c new file mode 100644 index 00000000000..1be23d1f332 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/all_in_one.c @@ -0,0 +1,107 @@ +#include "nfc_supported_card_plugin.h" + +#include +#include + +#define TAG "AllInOne" + +typedef enum { + AllInOneLayoutTypeA, + AllInOneLayoutTypeD, + AllInOneLayoutTypeE2, + AllInOneLayoutTypeE3, + AllInOneLayoutTypeE5, + AllInOneLayoutType2, + AllInOneLayoutTypeUnknown, +} AllInOneLayoutType; + +static AllInOneLayoutType all_in_one_get_layout(const MfUltralightData* data) { + // Switch on the second half of the third byte of page 5 + const uint8_t layout_byte = data->page[5].data[2]; + const uint8_t layout_half_byte = data->page[5].data[2] & 0x0F; + + FURI_LOG_I(TAG, "Layout byte: %02x", layout_byte); + FURI_LOG_I(TAG, "Layout half-byte: %02x", layout_half_byte); + + switch(layout_half_byte) { + // If it is A, the layout type is a type A layout + case 0x0A: + return AllInOneLayoutTypeA; + case 0x0D: + return AllInOneLayoutTypeD; + case 0x02: + return AllInOneLayoutType2; + default: + FURI_LOG_I(TAG, "Unknown layout type: %d", layout_half_byte); + return AllInOneLayoutTypeUnknown; + } +} + +static bool all_in_one_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + const MfUltralightData* data = nfc_device_get_data(device, NfcProtocolMfUltralight); + + bool parsed = false; + + do { + if(data->page[4].data[0] != 0x45 || data->page[4].data[1] != 0xD9) { + FURI_LOG_I(TAG, "Pass not verified"); + break; + } + + uint8_t ride_count = 0; + uint32_t serial = 0; + + const AllInOneLayoutType layout_type = all_in_one_get_layout(data); + + if(layout_type == AllInOneLayoutTypeA) { + // If the layout is A then the ride count is stored in the first byte of page 8 + ride_count = data->page[8].data[0]; + } else if(layout_type == AllInOneLayoutTypeD) { + // If the layout is D, the ride count is stored in the second byte of page 9 + ride_count = data->page[9].data[1]; + } else { + FURI_LOG_I(TAG, "Unknown layout: %d", layout_type); + ride_count = 137; + } + + // // The number starts at the second half of the third byte on page 4, and is 32 bits long + // // So we get the second half of the third byte, then bytes 4-6, and then the first half of the 7th byte + // // B8 17 A2 A4 BD becomes 81 7A 2A 4B + const uint8_t* serial_data_lo = data->page[4].data; + const uint8_t* serial_data_hi = data->page[5].data; + + serial = (serial_data_lo[2] & 0x0F) << 28 | serial_data_lo[3] << 20 | + serial_data_hi[0] << 12 | serial_data_hi[1] << 4 | serial_data_hi[2] >> 4; + + // Format string for rides count + furi_string_printf( + parsed_data, "\e#All-In-One\nNumber: %lu\nRides left: %u", serial, ride_count); + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin all_in_one_plugin = { + .protocol = NfcProtocolMfUltralight, + .verify = NULL, + .read = NULL, + .parse = all_in_one_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor all_in_one_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &all_in_one_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* all_in_one_plugin_ep() { + return &all_in_one_plugin_descriptor; +} diff --git a/applications/main/nfc/plugins/supported_cards/myki.c b/applications/main/nfc/plugins/supported_cards/myki.c new file mode 100644 index 00000000000..70a6963710f --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/myki.c @@ -0,0 +1,116 @@ +/* myki.c - Parser for myki cards (Melbourne, Australia). + * + * Based on the code by Emily Trau (https://github.com/emilytrau) + * Original pull request URL: https://github.com/flipperdevices/flipperzero-firmware/pull/2326 + * Reference: https://github.com/metrodroid/metrodroid/wiki/Myki + */ +#include "nfc_supported_card_plugin.h" + +#include +#include + +static const MfDesfireApplicationId myki_app_id = {.data = {0x00, 0x11, 0xf2}}; +static const MfDesfireFileId myki_file_id = 0x0f; + +static uint8_t myki_calculate_luhn(uint64_t number) { + // https://en.wikipedia.org/wiki/Luhn_algorithm + // Drop existing check digit to form payload + uint64_t payload = number / 10; + int sum = 0; + int position = 0; + + while(payload > 0) { + int digit = payload % 10; + if(position % 2 == 0) { + digit *= 2; + } + if(digit > 9) { + digit = (digit / 10) + (digit % 10); + } + sum += digit; + payload /= 10; + position++; + } + + return (10 - (sum % 10)) % 10; +} + +static bool myki_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + bool parsed = false; + + do { + const MfDesfireData* data = nfc_device_get_data(device, NfcProtocolMfDesfire); + + const MfDesfireApplication* app = mf_desfire_get_application(data, &myki_app_id); + if(app == NULL) break; + + typedef struct { + uint32_t top; + uint32_t bottom; + } MykiFile; + + const MfDesfireFileSettings* file_settings = + mf_desfire_get_file_settings(app, &myki_file_id); + + if(file_settings == NULL || file_settings->type != MfDesfireFileTypeStandard || + file_settings->data.size < sizeof(MykiFile)) + break; + + const MfDesfireFileData* file_data = mf_desfire_get_file_data(app, &myki_file_id); + if(file_data == NULL) break; + + const MykiFile* myki_file = simple_array_cget_data(file_data->data); + + // All myki card numbers are prefixed with "308425" + if(myki_file->top != 308425UL) break; + // Card numbers are always 15 digits in length + if(myki_file->bottom < 10000000UL || myki_file->bottom >= 100000000UL) break; + + uint64_t card_number = myki_file->top * 1000000000ULL + myki_file->bottom * 10UL; + // Stored card number doesn't include check digit + card_number += myki_calculate_luhn(card_number); + + furi_string_set(parsed_data, "\e#myki\n"); + + // Stylise card number according to the physical card + char card_string[20]; + snprintf(card_string, sizeof(card_string), "%llu", card_number); + + // Digit count in each space-separated group + static const uint8_t digit_count[] = {1, 5, 4, 4, 1}; + + for(uint32_t i = 0, k = 0; i < COUNT_OF(digit_count); k += digit_count[i++]) { + for(uint32_t j = 0; j < digit_count[i]; ++j) { + furi_string_push_back(parsed_data, card_string[j + k]); + } + furi_string_push_back(parsed_data, ' '); + } + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin myki_plugin = { + .protocol = NfcProtocolMfDesfire, + .verify = NULL, + .read = NULL, + .parse = myki_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor myki_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &myki_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* myki_plugin_ep() { + return &myki_plugin_descriptor; +} diff --git a/applications/main/nfc/plugins/supported_cards/nfc_supported_card_plugin.h b/applications/main/nfc/plugins/supported_cards/nfc_supported_card_plugin.h new file mode 100644 index 00000000000..7883aeb6b07 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/nfc_supported_card_plugin.h @@ -0,0 +1,94 @@ +/** + * @file nfc_supported_card_plugin.h + * @brief Supported card plugin abstract interface. + * + * Supported card plugins are dynamically loaded libraries that help making sense of + * a particular card's raw data, if a suitable plugin exists. + * + * For example, if some card serves as a bus ticket, instead of just displaying a data dump, + * a suitable plugin will transform that data into a human-readable format, showing the number + * of rides or balance left. + * Because of the highly specialised nature of application-specific cards, a separate plugin + * for each such card type must be implemented. + * + * To add a new plugin, create a uniquely-named .c file in the `supported_cards` directory + * and implement at least the parse() function in the NfcSupportedCardsPlugin structure. + * Then, register the plugin in the `application.fam` file in the `nfc` directory. Use the existing + * entries as an example. After being registered, the plugin will be automatically deployed with the application. + * + * @note the APPID field MUST end with `_parser` so the applicaton would know that this particular file + * is a supported card plugin. + * + * @see nfc_supported_cards.h + */ +#pragma once + +#include + +#include +#include + +/** + * @brief Unique string identifier for supported card plugins. + */ +#define NFC_SUPPORTED_CARD_PLUGIN_APP_ID "NfcSupportedCardPlugin" + +/** + * @brief Currently supported plugin API version. + */ +#define NFC_SUPPORTED_CARD_PLUGIN_API_VERSION 1 + +/** + * @brief Verify that the card is of a supported type. + * + * This function should be implemented if a quick check exists + * allowing to verify that the plugin is working with the appropriate card type. + * Such checks may include, but are not limited to: reading a specific sector, + * performing a certain read operation, etc. + * + * @param[in,out] nfc pointer to an Nfc instance. + * @returns true if the card was successfully verified, false otherwise. + */ +typedef bool (*NfcSupportedCardPluginVerify)(Nfc* nfc); + +/** + * @brief Read the card using a custom procedure. + * + * This function should be implemented if a card requires some special reading + * procedure not covered in the vanilla poller. Examples include, but are not + * limited to: reading with particular security keys, mandatory order of read + * operations, etc. + * + * @param[in,out] nfc pointer to an Nfc instance. + * @param[in,out] device pointer to a device instance to hold the read data. + * @returns true if the card was successfully read, false otherwise. + */ +typedef bool (*NfcSupportedCardPluginRead)(Nfc* nfc, NfcDevice* device); + +/** + * @brief Parse raw data into human-readable representation. + * + * A supported card plugin may contain only this function, if no special verification + * or reading procedures are not required. In any case, the data must be complete and + * available through the `device` parameter at the time of calling. + * + * The output format is free and application-dependent. Multiple lines should + * be separated by newline character. + * + * @param[in] device pointer to a device instance holding the data is to be parsed. + * @param[out] parsed_data pointer to the string to contain the formatted result. + * @returns true if the card was successfully parsed, false otherwise. + */ +typedef bool (*NfcSupportedCardPluginParse)(const NfcDevice* device, FuriString* parsed_data); + +/** + * @brief Supported card plugin interface. + * + * For a minimally functional plugin, only the parse() function must be implemented. + */ +typedef struct { + NfcProtocol protocol; /**< Identifier of the protocol this card type works on top of. */ + NfcSupportedCardPluginVerify verify; /**< Pointer to the verify() function. */ + NfcSupportedCardPluginRead read; /**< Pointer to the read() function. */ + NfcSupportedCardPluginParse parse; /**< Pointer to the parse() function. */ +} NfcSupportedCardsPlugin; diff --git a/applications/main/nfc/plugins/supported_cards/opal.c b/applications/main/nfc/plugins/supported_cards/opal.c new file mode 100644 index 00000000000..c71635d53eb --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/opal.c @@ -0,0 +1,233 @@ +/* + * opal.c - Parser for Opal card (Sydney, Australia). + * + * Copyright 2023 Michael Farrell + * + * This will only read "standard" MIFARE DESFire-based Opal cards. Free travel + * cards (including School Opal cards, veteran, vision-impaired persons and + * TfNSW employees' cards) and single-trip tickets are MIFARE Ultralight C + * cards and not supported. + * + * Reference: https://github.com/metrodroid/metrodroid/wiki/Opal + * + * Note: The card values are all little-endian (like Flipper), but the above + * reference was originally written based on Java APIs, which are big-endian. + * This implementation presumes a little-endian system. + * + * 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 . + */ + +#include "nfc_supported_card_plugin.h" + +#include +#include +#include + +#include + +static const MfDesfireApplicationId opal_app_id = {.data = {0x31, 0x45, 0x53}}; + +static const MfDesfireFileId opal_file_id = 0x07; + +static const char* opal_modes[5] = + {"Rail / Metro", "Ferry / Light Rail", "Bus", "Unknown mode", "Manly Ferry"}; + +static const char* opal_usages[14] = { + "New / Unused", + "Tap on: new journey", + "Tap on: transfer from same mode", + "Tap on: transfer from other mode", + NULL, // Manly Ferry: new journey + NULL, // Manly Ferry: transfer from ferry + NULL, // Manly Ferry: transfer from other + "Tap off: distance fare", + "Tap off: flat fare", + "Automated tap off: failed to tap off", + "Tap off: end of trip without start", + "Tap off: reversal", + "Tap on: rejected", + "Unknown usage", +}; + +// Opal file 0x7 structure. Assumes a little-endian CPU. +typedef struct __attribute__((__packed__)) { + uint32_t serial : 32; + uint8_t check_digit : 4; + bool blocked : 1; + uint16_t txn_number : 16; + int32_t balance : 21; + uint16_t days : 15; + uint16_t minutes : 11; + uint8_t mode : 3; + uint16_t usage : 4; + bool auto_topup : 1; + uint8_t weekly_journeys : 4; + uint16_t checksum : 16; +} OpalFile; + +static_assert(sizeof(OpalFile) == 16, "OpalFile"); + +// Converts an Opal timestamp to FuriHalRtcDateTime. +// +// Opal measures days since 1980-01-01 and minutes since midnight, and presumes +// all days are 1440 minutes. +static void opal_date_time_to_furi(uint16_t days, uint16_t minutes, FuriHalRtcDateTime* out) { + out->year = 1980; + out->month = 1; + // 1980-01-01 is a Tuesday + out->weekday = ((days + 1) % 7) + 1; + out->hour = minutes / 60; + out->minute = minutes % 60; + out->second = 0; + + // What year is it? + for(;;) { + const uint16_t num_days_in_year = furi_hal_rtc_get_days_per_year(out->year); + if(days < num_days_in_year) break; + days -= num_days_in_year; + out->year++; + } + + // 1-index the day of the year + days++; + + for(;;) { + // What month is it? + const bool is_leap = furi_hal_rtc_is_leap_year(out->year); + const uint8_t num_days_in_month = furi_hal_rtc_get_days_per_month(is_leap, out->month); + if(days <= num_days_in_month) break; + days -= num_days_in_month; + out->month++; + } + + out->day = days; +} + +static bool opal_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + furi_assert(parsed_data); + + const MfDesfireData* data = nfc_device_get_data(device, NfcProtocolMfDesfire); + + bool parsed = false; + + do { + const MfDesfireApplication* app = mf_desfire_get_application(data, &opal_app_id); + if(app == NULL) break; + + const MfDesfireFileSettings* file_settings = + mf_desfire_get_file_settings(app, &opal_file_id); + if(file_settings == NULL || file_settings->type != MfDesfireFileTypeStandard || + file_settings->data.size != sizeof(OpalFile)) + break; + + const MfDesfireFileData* file_data = mf_desfire_get_file_data(app, &opal_file_id); + if(file_data == NULL) break; + + const OpalFile* opal_file = simple_array_cget_data(file_data->data); + + const uint8_t serial2 = opal_file->serial / 10000000; + const uint16_t serial3 = (opal_file->serial / 1000) % 10000; + const uint16_t serial4 = (opal_file->serial % 1000); + + if(opal_file->check_digit > 9) break; + + // Negative balance. Make this a positive value again and record the + // sign separately, because then we can handle balances of -99..-1 + // cents, as the "dollars" division below would result in a positive + // zero value. + const bool is_negative_balance = (opal_file->balance < 0); + const char* sign = is_negative_balance ? "-" : ""; + const int32_t balance = is_negative_balance ? labs(opal_file->balance) : //-V1081 + opal_file->balance; + const uint8_t balance_cents = balance % 100; + const int32_t balance_dollars = balance / 100; + + FuriHalRtcDateTime timestamp; + opal_date_time_to_furi(opal_file->days, opal_file->minutes, ×tamp); + + // Usages 4..6 associated with the Manly Ferry, which correspond to + // usages 1..3 for other modes. + const bool is_manly_ferry = (opal_file->usage >= 4) && (opal_file->usage <= 6); + + // 3..7 are "reserved", but we use 4 to indicate the Manly Ferry. + const uint8_t mode = is_manly_ferry ? 4 : opal_file->mode; + const uint8_t usage = is_manly_ferry ? opal_file->usage - 3 : opal_file->usage; + + const char* mode_str = opal_modes[mode > 4 ? 3 : mode]; + const char* usage_str = opal_usages[usage > 12 ? 13 : usage]; + + furi_string_printf( + parsed_data, + "\e#Opal: $%s%ld.%02hu\n3085 22%02hhu %04hu %03hu%01hhu\n%s, %s\n", + sign, + balance_dollars, + balance_cents, + serial2, + serial3, + serial4, + opal_file->check_digit, + mode_str, + usage_str); + + FuriString* timestamp_str = furi_string_alloc(); + + locale_format_date(timestamp_str, ×tamp, locale_get_date_format(), "-"); + furi_string_cat(parsed_data, timestamp_str); + furi_string_cat(parsed_data, " at "); + + locale_format_time(timestamp_str, ×tamp, locale_get_time_format(), false); + furi_string_cat(parsed_data, timestamp_str); + + furi_string_free(timestamp_str); + + furi_string_cat_printf( + parsed_data, + "\nWeekly journeys: %hhu, Txn #%hu\n", + opal_file->weekly_journeys, + opal_file->txn_number); + + if(opal_file->auto_topup) { + furi_string_cat_str(parsed_data, "Auto-topup enabled\n"); + } + + if(opal_file->blocked) { + furi_string_cat_str(parsed_data, "Card blocked\n"); + } + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin opal_plugin = { + .protocol = NfcProtocolMfDesfire, + .verify = NULL, + .read = NULL, + .parse = opal_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor opal_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &opal_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* opal_plugin_ep() { + return &opal_plugin_descriptor; +} diff --git a/applications/main/nfc/plugins/supported_cards/plantain.c b/applications/main/nfc/plugins/supported_cards/plantain.c new file mode 100644 index 00000000000..a21e1cd415b --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/plantain.c @@ -0,0 +1,219 @@ +#include "nfc_supported_card_plugin.h" + +#include + +#include +#include +#include + +#define TAG "Plantain" + +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +typedef struct { + const MfClassicKeyPair* keys; + uint32_t data_sector; +} PlantainCardConfig; + +static const MfClassicKeyPair plantain_1k_keys[] = { + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xe56ac127dd45, .b = 0x19fc84a3784b}, + {.a = 0x77dabc9825e1, .b = 0x9764fec3154a}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0x26973ea74321, .b = 0xd27058c6e2c7}, + {.a = 0xeb0a8ff88ade, .b = 0x578a9ada41e3}, + {.a = 0xea0fd73cb149, .b = 0x29c35fa068fb}, + {.a = 0xc76bf71a2509, .b = 0x9ba241db3f56}, + {.a = 0xacffffffffff, .b = 0x71f3a315ad26}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, +}; + +static const MfClassicKeyPair plantain_4k_keys[] = { + {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xe56ac127dd45, .b = 0x19fc84a3784b}, {.a = 0x77dabc9825e1, .b = 0x9764fec3154a}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0x26973ea74321, .b = 0xd27058c6e2c7}, {.a = 0xeb0a8ff88ade, .b = 0x578a9ada41e3}, + {.a = 0xea0fd73cb149, .b = 0x29c35fa068fb}, {.a = 0xc76bf71a2509, .b = 0x9ba241db3f56}, + {.a = 0xacffffffffff, .b = 0x71f3a315ad26}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0x72f96bdd3714, .b = 0x462225cd34cf}, {.a = 0x044ce1872bc3, .b = 0x8c90c70cff4a}, + {.a = 0xbc2d1791dec1, .b = 0xca96a487de0b}, {.a = 0x8791b2ccb5c4, .b = 0xc956c3b80da3}, + {.a = 0x8e26e45e7d65, .b = 0x8e65b3af7d22}, {.a = 0x0f318130ed18, .b = 0x0c420a20e056}, + {.a = 0x045ceca15535, .b = 0x31bec3d9e510}, {.a = 0x9d993c5d4ef4, .b = 0x86120e488abf}, + {.a = 0xc65d4eaa645b, .b = 0xb69d40d1a439}, {.a = 0x3a8a139c20b4, .b = 0x8818a9c5d406}, + {.a = 0xbaff3053b496, .b = 0x4b7cb25354d3}, {.a = 0x7413b599c4ea, .b = 0xb0a2AAF3A1BA}, + {.a = 0x0ce7cd2cc72b, .b = 0xfa1fbb3f0f1f}, {.a = 0x0be5fac8b06a, .b = 0x6f95887a4fd3}, + {.a = 0x0eb23cc8110b, .b = 0x04dc35277635}, {.a = 0xbc4580b7f20b, .b = 0xd0a4131fb290}, + {.a = 0x7a396f0d633d, .b = 0xad2bdc097023}, {.a = 0xa3faa6daff67, .b = 0x7600e889adf9}, + {.a = 0xfd8705e721b0, .b = 0x296fc317a513}, {.a = 0x22052b480d11, .b = 0xe19504c39461}, + {.a = 0xa7141147d430, .b = 0xff16014fefc7}, {.a = 0x8a8d88151a00, .b = 0x038b5f9b5a2a}, + {.a = 0xb27addfb64b0, .b = 0x152fd0c420a7}, {.a = 0x7259fa0197c6, .b = 0x5583698df085}, +}; + +static bool plantain_get_card_config(PlantainCardConfig* config, MfClassicType type) { + bool success = true; + + if(type == MfClassicType1k) { + config->data_sector = 8; + config->keys = plantain_1k_keys; + } else if(type == MfClassicType4k) { + config->data_sector = 8; + config->keys = plantain_4k_keys; + } else { + success = false; + } + + return success; +} + +static bool plantain_verify_type(Nfc* nfc, MfClassicType type) { + bool verified = false; + + do { + PlantainCardConfig cfg = {}; + if(!plantain_get_card_config(&cfg, type)) break; + + const uint8_t block_num = mf_classic_get_first_block_num_of_sector(cfg.data_sector); + FURI_LOG_D(TAG, "Verifying sector %lu", cfg.data_sector); + + MfClassicKey key = {0}; + nfc_util_num2bytes(cfg.keys[cfg.data_sector].a, COUNT_OF(key.data), key.data); + + MfClassicAuthContext auth_context; + MfClassicError error = + mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); + break; + } + + verified = true; + } while(false); + + return verified; +} + +static bool plantain_verify(Nfc* nfc) { + return plantain_verify_type(nfc, MfClassicType1k) || + plantain_verify_type(nfc, MfClassicType4k); +} + +static bool plantain_read(Nfc* nfc, NfcDevice* device) { + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + MfClassicType type = MfClassicTypeMini; + MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); + if(error != MfClassicErrorNone) break; + + data->type = type; + PlantainCardConfig cfg = {}; + if(!plantain_get_card_config(&cfg, data->type)) break; + + MfClassicDeviceKeys keys = {}; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + nfc_util_num2bytes(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); + FURI_BIT_SET(keys.key_a_mask, i); + nfc_util_num2bytes(cfg.keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); + FURI_BIT_SET(keys.key_b_mask, i); + } + + error = mf_classic_poller_sync_read(nfc, &keys, data); + if(error != MfClassicErrorNone) { + FURI_LOG_W(TAG, "Failed to read data"); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = true; + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static bool plantain_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // Verify card type + PlantainCardConfig cfg = {}; + if(!plantain_get_card_config(&cfg, data->type)) break; + + // Verify key + const MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(data, cfg.data_sector); + + const uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data)); + if(key != cfg.keys[cfg.data_sector].a) break; + + // Point to block 0 of sector 4, value 0 + const uint8_t* temp_ptr = data->block[16].data; + // Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t + // 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal + uint32_t balance = + ((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100; + // Read card number + // Point to block 0 of sector 0, value 0 + temp_ptr = data->block[0].data; + // Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t + // 04 31 16 8A 23 5C 80 becomes 80 5C 23 8A 16 31 04, and equals to 36130104729284868 decimal + uint8_t card_number_arr[7]; + for(size_t i = 0; i < 7; i++) { + card_number_arr[i] = temp_ptr[6 - i]; + } + // Copy card number to uint64_t + uint64_t card_number = 0; + for(size_t i = 0; i < 7; i++) { + card_number = (card_number << 8) | card_number_arr[i]; + } + + furi_string_printf( + parsed_data, "\e#Plantain\nN:%llu-\nBalance:%lu\n", card_number, balance); + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin plantain_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = plantain_verify, + .read = plantain_read, + .parse = plantain_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor plantain_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &plantain_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* plantain_plugin_ep() { + return &plantain_plugin_descriptor; +} diff --git a/applications/main/nfc/plugins/supported_cards/troika.c b/applications/main/nfc/plugins/supported_cards/troika.c new file mode 100644 index 00000000000..7cf1e4dd8c4 --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/troika.c @@ -0,0 +1,214 @@ +#include "nfc_supported_card_plugin.h" + +#include + +#include +#include +#include + +#define TAG "Troika" + +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +typedef struct { + const MfClassicKeyPair* keys; + uint32_t data_sector; +} TroikaCardConfig; + +static const MfClassicKeyPair troika_1k_keys[] = { + {.a = 0xa0a1a2a3a4a5, .b = 0xfbf225dc5d58}, + {.a = 0xa82607b01c0d, .b = 0x2910989b6880}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, + {.a = 0x73068f118c13, .b = 0x2b7f3253fac5}, + {.a = 0xfbc2793d540b, .b = 0xd3a297dc2698}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, + {.a = 0xae3d65a3dad4, .b = 0x0f1c63013dba}, + {.a = 0xa73f5dc1d333, .b = 0xe35173494a81}, + {.a = 0x69a32f1c2f19, .b = 0x6b8bd9860763}, + {.a = 0x9becdf3d9273, .b = 0xf8493407799d}, + {.a = 0x08b386463229, .b = 0x5efbaecef46b}, + {.a = 0xcd4c61c26e3d, .b = 0x31c7610de3b0}, + {.a = 0xa82607b01c0d, .b = 0x2910989b6880}, + {.a = 0x0e8f64340ba4, .b = 0x4acec1205d75}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, +}; + +static const MfClassicKeyPair troika_4k_keys[] = { + {.a = 0xa0a1a2a3a4a5, .b = 0xfbf225dc5d58}, {.a = 0xa82607b01c0d, .b = 0x2910989b6880}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, + {.a = 0x73068f118c13, .b = 0x2b7f3253fac5}, {.a = 0xfbc2793d540b, .b = 0xd3a297dc2698}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0xae3d65a3dad4, .b = 0x0f1c63013dbb}, + {.a = 0xa73f5dc1d333, .b = 0xe35173494a81}, {.a = 0x69a32f1c2f19, .b = 0x6b8bd9860763}, + {.a = 0x9becdf3d9273, .b = 0xf8493407799d}, {.a = 0x08b386463229, .b = 0x5efbaecef46b}, + {.a = 0xcd4c61c26e3d, .b = 0x31c7610de3b0}, {.a = 0xa82607b01c0d, .b = 0x2910989b6880}, + {.a = 0x0e8f64340ba4, .b = 0x4acec1205d75}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, + {.a = 0x6b02733bb6ec, .b = 0x7038cd25c408}, {.a = 0x403d706ba880, .b = 0xb39d19a280df}, + {.a = 0xc11f4597efb5, .b = 0x70d901648cb9}, {.a = 0x0db520c78c1c, .b = 0x73e5b9d9d3a4}, + {.a = 0x3ebce0925b2f, .b = 0x372cc880f216}, {.a = 0x16a27af45407, .b = 0x9868925175ba}, + {.a = 0xaba208516740, .b = 0xce26ecb95252}, {.a = 0xcd64e567abcd, .b = 0x8f79c4fd8a01}, + {.a = 0x764cd061f1e6, .b = 0xa74332f74994}, {.a = 0x1cc219e9fec1, .b = 0xb90de525ceb6}, + {.a = 0x2fe3cb83ea43, .b = 0xfba88f109b32}, {.a = 0x07894ffec1d6, .b = 0xefcb0e689db3}, + {.a = 0x04c297b91308, .b = 0xc8454c154cb5}, {.a = 0x7a38e3511a38, .b = 0xab16584c972a}, + {.a = 0x7545df809202, .b = 0xecf751084a80}, {.a = 0x5125974cd391, .b = 0xd3eafb5df46d}, + {.a = 0x7a86aa203788, .b = 0xe41242278ca2}, {.a = 0xafcef64c9913, .b = 0x9db96dca4324}, + {.a = 0x04eaa462f70b, .b = 0xac17b93e2fae}, {.a = 0xe734c210f27e, .b = 0x29ba8c3e9fda}, + {.a = 0xd5524f591eed, .b = 0x5daf42861b4d}, {.a = 0xe4821a377b75, .b = 0xe8709e486465}, + {.a = 0x518dc6eea089, .b = 0x97c64ac98ca4}, {.a = 0xbb52f8cce07f, .b = 0x6b6119752c70}, +}; + +static bool troika_get_card_config(TroikaCardConfig* config, MfClassicType type) { + bool success = true; + + if(type == MfClassicType1k) { + config->data_sector = 8; + config->keys = troika_1k_keys; + } else if(type == MfClassicType4k) { + config->data_sector = 4; + config->keys = troika_4k_keys; + } else { + success = false; + } + + return success; +} + +static bool troika_verify_type(Nfc* nfc, MfClassicType type) { + bool verified = false; + + do { + TroikaCardConfig cfg = {}; + if(!troika_get_card_config(&cfg, type)) break; + + const uint8_t block_num = mf_classic_get_first_block_num_of_sector(cfg.data_sector); + FURI_LOG_D(TAG, "Verifying sector %lu", cfg.data_sector); + + MfClassicKey key = {0}; + nfc_util_num2bytes(cfg.keys[cfg.data_sector].a, COUNT_OF(key.data), key.data); + + MfClassicAuthContext auth_context; + MfClassicError error = + mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_context); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); + break; + } + + verified = true; + } while(false); + + return verified; +} + +static bool troika_verify(Nfc* nfc) { + return troika_verify_type(nfc, MfClassicType1k) || troika_verify_type(nfc, MfClassicType4k); +} + +static bool troika_read(Nfc* nfc, NfcDevice* device) { + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + MfClassicType type = MfClassicTypeMini; + MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); + if(error != MfClassicErrorNone) break; + + data->type = type; + TroikaCardConfig cfg = {}; + if(!troika_get_card_config(&cfg, data->type)) break; + + MfClassicDeviceKeys keys = { + .key_a_mask = 0, + .key_b_mask = 0, + }; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + nfc_util_num2bytes(cfg.keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); + FURI_BIT_SET(keys.key_a_mask, i); + nfc_util_num2bytes(cfg.keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); + FURI_BIT_SET(keys.key_b_mask, i); + } + + error = mf_classic_poller_sync_read(nfc, &keys, data); + if(error != MfClassicErrorNone) { + FURI_LOG_W(TAG, "Failed to read data"); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = true; + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static bool troika_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // Verify card type + TroikaCardConfig cfg = {}; + if(!troika_get_card_config(&cfg, data->type)) break; + + // Verify key + const MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(data, cfg.data_sector); + + const uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, COUNT_OF(sec_tr->key_a.data)); + if(key != cfg.keys[cfg.data_sector].a) break; + + // Parse data + const uint8_t start_block_num = mf_classic_get_first_block_num_of_sector(cfg.data_sector); + + const uint8_t* temp_ptr = &data->block[start_block_num + 1].data[5]; + uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25; + temp_ptr = &data->block[start_block_num].data[2]; + + uint32_t number = 0; + for(size_t i = 1; i < 5; i++) { + number <<= 8; + number |= temp_ptr[i]; + } + number >>= 4; + number |= (temp_ptr[0] & 0xf) << 28; + + furi_string_printf(parsed_data, "\e#Troika\nNum: %lu\nBalance: %u RUR", number, balance); + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin troika_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = troika_verify, + .read = troika_read, + .parse = troika_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor troika_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &troika_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* troika_plugin_ep() { + return &troika_plugin_descriptor; +} diff --git a/applications/main/nfc/plugins/supported_cards/two_cities.c b/applications/main/nfc/plugins/supported_cards/two_cities.c new file mode 100644 index 00000000000..1748d372d2a --- /dev/null +++ b/applications/main/nfc/plugins/supported_cards/two_cities.c @@ -0,0 +1,189 @@ +#include "nfc_supported_card_plugin.h" + +#include + +#include +#include +#include + +#define TAG "TwoCities" + +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +static const MfClassicKeyPair two_cities_4k_keys[] = { + {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, + {.a = 0xe56ac127dd45, .b = 0x19fc84a3784b}, {.a = 0x77dabc9825e1, .b = 0x9764fec3154a}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xa73f5dc1d333, .b = 0xe35173494a81}, {.a = 0x69a32f1c2f19, .b = 0x6b8bd9860763}, + {.a = 0xea0fd73cb149, .b = 0x29c35fa068fb}, {.a = 0xc76bf71a2509, .b = 0x9ba241db3f56}, + {.a = 0xacffffffffff, .b = 0x71f3a315ad26}, {.a = 0xffffffffffff, .b = 0xffffffffffff}, + {.a = 0xffffffffffff, .b = 0xffffffffffff}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, + {.a = 0x72f96bdd3714, .b = 0x462225cd34cf}, {.a = 0x044ce1872bc3, .b = 0x8c90c70cff4a}, + {.a = 0xbc2d1791dec1, .b = 0xca96a487de0b}, {.a = 0x8791b2ccb5c4, .b = 0xc956c3b80da3}, + {.a = 0x8e26e45e7d65, .b = 0x8e65b3af7d22}, {.a = 0x0f318130ed18, .b = 0x0c420a20e056}, + {.a = 0x045ceca15535, .b = 0x31bec3d9e510}, {.a = 0x9d993c5d4ef4, .b = 0x86120e488abf}, + {.a = 0xc65d4eaa645b, .b = 0xb69d40d1a439}, {.a = 0x3a8a139c20b4, .b = 0x8818a9c5d406}, + {.a = 0xbaff3053b496, .b = 0x4b7cb25354d3}, {.a = 0x7413b599c4ea, .b = 0xb0a2AAF3A1BA}, + {.a = 0x0ce7cd2cc72b, .b = 0xfa1fbb3f0f1f}, {.a = 0x0be5fac8b06a, .b = 0x6f95887a4fd3}, + {.a = 0x26973ea74321, .b = 0xd27058c6e2c7}, {.a = 0xeb0a8ff88ade, .b = 0x578a9ada41e3}, + {.a = 0x7a396f0d633d, .b = 0xad2bdc097023}, {.a = 0xa3faa6daff67, .b = 0x7600e889adf9}, + {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, {.a = 0x2aa05ed1856f, .b = 0xeaac88e5dc99}, + {.a = 0xa7141147d430, .b = 0xff16014fefc7}, {.a = 0x8a8d88151a00, .b = 0x038b5f9b5a2a}, + {.a = 0xb27addfb64b0, .b = 0x152fd0c420a7}, {.a = 0x7259fa0197c6, .b = 0x5583698df085}, +}; + +bool two_cities_verify(Nfc* nfc) { + bool verified = false; + + do { + const uint8_t verify_sector = 4; + uint8_t block_num = mf_classic_get_first_block_num_of_sector(verify_sector); + FURI_LOG_D(TAG, "Verifying sector %u", verify_sector); + + MfClassicKey key = {}; + nfc_util_num2bytes(two_cities_4k_keys[verify_sector].a, COUNT_OF(key.data), key.data); + + MfClassicAuthContext auth_ctx = {}; + MfClassicError error = + mf_classic_poller_sync_auth(nfc, block_num, &key, MfClassicKeyTypeA, &auth_ctx); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to read block %u: %d", block_num, error); + break; + } + + verified = true; + } while(false); + + return verified; +} + +static bool two_cities_read(Nfc* nfc, NfcDevice* device) { + furi_assert(nfc); + furi_assert(device); + + bool is_read = false; + + MfClassicData* data = mf_classic_alloc(); + nfc_device_copy_data(device, NfcProtocolMfClassic, data); + + do { + MfClassicType type = MfClassicTypeMini; + MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); + if(error != MfClassicErrorNone) break; + + data->type = type; + MfClassicDeviceKeys keys = {}; + for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { + nfc_util_num2bytes(two_cities_4k_keys[i].a, sizeof(MfClassicKey), keys.key_a[i].data); + FURI_BIT_SET(keys.key_a_mask, i); + nfc_util_num2bytes(two_cities_4k_keys[i].b, sizeof(MfClassicKey), keys.key_b[i].data); + FURI_BIT_SET(keys.key_b_mask, i); + } + + error = mf_classic_poller_sync_read(nfc, &keys, data); + if(error != MfClassicErrorNone) { + FURI_LOG_W(TAG, "Failed to read data"); + break; + } + + nfc_device_set_data(device, NfcProtocolMfClassic, data); + + is_read = true; + } while(false); + + mf_classic_free(data); + + return is_read; +} + +static bool two_cities_parse(const NfcDevice* device, FuriString* parsed_data) { + furi_assert(device); + + const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); + + bool parsed = false; + + do { + // Verify key + MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a.data, 6); + if(key != two_cities_4k_keys[4].a) return false; + + // ===== + // PLANTAIN + // ===== + + // Point to block 0 of sector 4, value 0 + const uint8_t* temp_ptr = data->block[16].data; + // Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t + // 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal + uint32_t balance = + ((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100; + // Read card number + // Point to block 0 of sector 0, value 0 + temp_ptr = data->block[0].data; + // Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t + // 04 31 16 8A 23 5C 80 becomes 80 5C 23 8A 16 31 04, and equals to 36130104729284868 decimal + uint8_t card_number_arr[7]; + for(size_t i = 0; i < 7; i++) { + card_number_arr[i] = temp_ptr[6 - i]; + } + // Copy card number to uint64_t + uint64_t card_number = 0; + for(size_t i = 0; i < 7; i++) { + card_number = (card_number << 8) | card_number_arr[i]; + } + + // ===== + // --PLANTAIN-- + // ===== + // TROIKA + // ===== + + const uint8_t* troika_temp_ptr = &data->block[33].data[5]; + uint16_t troika_balance = ((troika_temp_ptr[0] << 8) | troika_temp_ptr[1]) / 25; + troika_temp_ptr = &data->block[32].data[2]; + uint32_t troika_number = 0; + for(size_t i = 0; i < 4; i++) { + troika_number <<= 8; + troika_number |= troika_temp_ptr[i]; + } + troika_number >>= 4; + + furi_string_printf( + parsed_data, + "\e#Troika+Plantain\nPN: %llu-\nPB: %lu rur.\nTN: %lu\nTB: %u rur.\n", + card_number, + balance, + troika_number, + troika_balance); + + parsed = true; + } while(false); + + return parsed; +} + +/* Actual implementation of app<>plugin interface */ +static const NfcSupportedCardsPlugin two_cities_plugin = { + .protocol = NfcProtocolMfClassic, + .verify = two_cities_verify, + .read = two_cities_read, + .parse = two_cities_parse, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor two_cities_plugin_descriptor = { + .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, + .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, + .entry_point = &two_cities_plugin, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* two_cities_plugin_ep() { + return &two_cities_plugin_descriptor; +} diff --git a/assets/resources/nfc/assets/aid.nfc b/applications/main/nfc/resources/nfc/assets/aid.nfc similarity index 100% rename from assets/resources/nfc/assets/aid.nfc rename to applications/main/nfc/resources/nfc/assets/aid.nfc diff --git a/assets/resources/nfc/assets/country_code.nfc b/applications/main/nfc/resources/nfc/assets/country_code.nfc similarity index 100% rename from assets/resources/nfc/assets/country_code.nfc rename to applications/main/nfc/resources/nfc/assets/country_code.nfc diff --git a/assets/resources/nfc/assets/currency_code.nfc b/applications/main/nfc/resources/nfc/assets/currency_code.nfc similarity index 100% rename from assets/resources/nfc/assets/currency_code.nfc rename to applications/main/nfc/resources/nfc/assets/currency_code.nfc diff --git a/assets/resources/nfc/assets/mf_classic_dict.nfc b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc similarity index 99% rename from assets/resources/nfc/assets/mf_classic_dict.nfc rename to applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc index f4cdf595207..0925888f226 100644 --- a/assets/resources/nfc/assets/mf_classic_dict.nfc +++ b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc @@ -244,6 +244,7 @@ FEE470A4CB58 1F1A0A111B5B 1F1FFE000000 2031D1E57A3B +# HID Key B 204752454154 21A600056CB0 22729A9BD40F @@ -292,6 +293,7 @@ FEE470A4CB58 45635EF66EF3 476242304C53 484558414354 +# HID Key A 484944204953 484A57696F4A 48734389EDC3 @@ -1226,6 +1228,7 @@ C41514DEFC07 ABFEDC124578 046154274C11 5429D67E1F57 +# SMARTair Key B E7316853E731 CD7FFFF81C4A F253C30568C4 @@ -1308,4 +1311,4 @@ CE99FBC8BD26 # PIK Comfort Moscow keys (ISBC Mifare Plus SE 1K) 009FB42D98ED -002E626E2820 \ No newline at end of file +002E626E2820 diff --git a/applications/nfc/scenes/nfc_scene.c b/applications/main/nfc/scenes/nfc_scene.c old mode 100755 new mode 100644 similarity index 100% rename from applications/nfc/scenes/nfc_scene.c rename to applications/main/nfc/scenes/nfc_scene.c diff --git a/applications/nfc/scenes/nfc_scene.h b/applications/main/nfc/scenes/nfc_scene.h similarity index 100% rename from applications/nfc/scenes/nfc_scene.h rename to applications/main/nfc/scenes/nfc_scene.h diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h new file mode 100644 index 00000000000..a9887996d6d --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -0,0 +1,61 @@ +ADD_SCENE(nfc, start, Start) +ADD_SCENE(nfc, file_select, FileSelect) +ADD_SCENE(nfc, saved_menu, SavedMenu) +ADD_SCENE(nfc, save_name, SaveName) +ADD_SCENE(nfc, save_success, SaveSuccess) +ADD_SCENE(nfc, delete, Delete) +ADD_SCENE(nfc, delete_success, DeleteSuccess) +ADD_SCENE(nfc, restore_original_confirm, RestoreOriginalConfirm) +ADD_SCENE(nfc, restore_original, RestoreOriginal) + +ADD_SCENE(nfc, detect, Detect) +ADD_SCENE(nfc, read, Read) +ADD_SCENE(nfc, info, Info) +ADD_SCENE(nfc, more_info, MoreInfo) +ADD_SCENE(nfc, supported_card, SupportedCard) +ADD_SCENE(nfc, select_protocol, SelectProtocol) +ADD_SCENE(nfc, extra_actions, ExtraActions) +ADD_SCENE(nfc, read_success, ReadSuccess) +ADD_SCENE(nfc, read_menu, ReadMenu) +ADD_SCENE(nfc, emulate, Emulate) +ADD_SCENE(nfc, rpc, Rpc) +ADD_SCENE(nfc, debug, Debug) +ADD_SCENE(nfc, field, Field) +ADD_SCENE(nfc, retry_confirm, RetryConfirm) +ADD_SCENE(nfc, exit_confirm, ExitConfirm) + +ADD_SCENE(nfc, mf_ultralight_write, MfUltralightWrite) +ADD_SCENE(nfc, mf_ultralight_write_success, MfUltralightWriteSuccess) +ADD_SCENE(nfc, mf_ultralight_write_fail, MfUltralightWriteFail) +ADD_SCENE(nfc, mf_ultralight_wrong_card, MfUltralightWrongCard) +ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu) +ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) +ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) +ADD_SCENE(nfc, mf_ultralight_capture_pass, MfUltralightCapturePass) + +ADD_SCENE(nfc, mf_desfire_more_info, MfDesfireMoreInfo) +ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp) + +ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack) +ADD_SCENE(nfc, mf_classic_detect_reader, MfClassicDetectReader) +ADD_SCENE(nfc, mf_classic_mfkey_nonces_info, MfClassicMfkeyNoncesInfo) +ADD_SCENE(nfc, mf_classic_mfkey_complete, MfClassicMfkeyComplete) +ADD_SCENE(nfc, mf_classic_update_initial, MfClassicUpdateInitial) +ADD_SCENE(nfc, mf_classic_update_initial_success, MfClassicUpdateInitialSuccess) +ADD_SCENE(nfc, mf_classic_write_initial, MfClassicWriteInitial) +ADD_SCENE(nfc, mf_classic_write_initial_success, MfClassicWriteInitialSuccess) +ADD_SCENE(nfc, mf_classic_write_initial_fail, MfClassicWriteInitialFail) +ADD_SCENE(nfc, mf_classic_wrong_card, MfClassicWrongCard) + +ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys) +ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList) +ADD_SCENE(nfc, mf_classic_keys_delete, MfClassicKeysDelete) +ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd) +ADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate) + +ADD_SCENE(nfc, set_type, SetType) +ADD_SCENE(nfc, set_sak, SetSak) +ADD_SCENE(nfc, set_atqa, SetAtqa) +ADD_SCENE(nfc, set_uid, SetUid) + +ADD_SCENE(nfc, generate_info, GenerateInfo) diff --git a/applications/main/nfc/scenes/nfc_scene_debug.c b/applications/main/nfc/scenes/nfc_scene_debug.c new file mode 100644 index 00000000000..97592f2e270 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_debug.c @@ -0,0 +1,46 @@ +#include "../nfc_app_i.h" + +enum SubmenuDebugIndex { + SubmenuDebugIndexField, + SubmenuDebugIndexApdu, +}; + +void nfc_scene_debug_submenu_callback(void* context, uint32_t index) { + NfcApp* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_debug_on_enter(void* context) { + NfcApp* nfc = context; + Submenu* submenu = nfc->submenu; + + submenu_add_item( + submenu, "Field", SubmenuDebugIndexField, nfc_scene_debug_submenu_callback, nfc); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDebug)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_debug_on_event(void* context, SceneManagerEvent event) { + NfcApp* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuDebugIndexField) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneDebug, SubmenuDebugIndexField); + scene_manager_next_scene(nfc->scene_manager, NfcSceneField); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_debug_on_exit(void* context) { + NfcApp* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_delete.c b/applications/main/nfc/scenes/nfc_scene_delete.c new file mode 100644 index 00000000000..c1a676168ad --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_delete.c @@ -0,0 +1,67 @@ +#include "../nfc_app_i.h" + +void nfc_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) { + NfcApp* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_delete_on_enter(void* context) { + NfcApp* nfc = context; + + // Setup Custom Widget view + FuriString* temp_str; + temp_str = furi_string_alloc(); + + furi_string_printf(temp_str, "\e#Delete %s?\e#", furi_string_get_cstr(nfc->file_name)); + widget_add_text_box_element( + nfc->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, furi_string_get_cstr(temp_str), false); + widget_add_button_element( + nfc->widget, GuiButtonTypeLeft, "Cancel", nfc_scene_delete_widget_callback, nfc); + widget_add_button_element( + nfc->widget, GuiButtonTypeRight, "Delete", nfc_scene_delete_widget_callback, nfc); + + size_t uid_len; + const uint8_t* uid = nfc_device_get_uid(nfc->nfc_device, &uid_len); + + furi_string_set(temp_str, "UID:"); + for(size_t i = 0; i < uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", uid[i]); + } + widget_add_string_element( + nfc->widget, 64, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); + + furi_string_set_str(temp_str, nfc_device_get_name(nfc->nfc_device, NfcDeviceNameTypeFull)); + widget_add_string_element( + nfc->widget, 64, 34, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) { + NfcApp* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } else if(event.event == GuiButtonTypeRight) { + if(nfc_delete(nfc)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess); + } else { + scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart); + } + consumed = true; + } + } + return consumed; +} + +void nfc_scene_delete_on_exit(void* context) { + NfcApp* nfc = context; + + widget_reset(nfc->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_delete_success.c b/applications/main/nfc/scenes/nfc_scene_delete_success.c new file mode 100644 index 00000000000..f0c22eec4d5 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_delete_success.c @@ -0,0 +1,45 @@ +#include "../nfc_app_i.h" + +void nfc_scene_delete_success_popup_callback(void* context) { + NfcApp* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_delete_success_on_enter(void* context) { + NfcApp* nfc = context; + + // Setup view + Popup* popup = nfc->popup; + popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); + popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_delete_success_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) { + NfcApp* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfClassicKeys); + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneFileSelect); + } + } + } + return consumed; +} + +void nfc_scene_delete_success_on_exit(void* context) { + NfcApp* nfc = context; + + // Clear view + popup_reset(nfc->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_detect.c b/applications/main/nfc/scenes/nfc_scene_detect.c new file mode 100644 index 00000000000..326b1458c0d --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_detect.c @@ -0,0 +1,59 @@ +#include "../nfc_app_i.h" +#include + +void nfc_scene_detect_scan_callback(NfcScannerEvent event, void* context) { + furi_assert(context); + + NfcApp* instance = context; + + if(event.type == NfcScannerEventTypeDetected) { + nfc_app_set_detected_protocols(instance, event.data.protocols, event.data.protocol_num); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWorkerExit); + } +} + +void nfc_scene_detect_on_enter(void* context) { + NfcApp* instance = context; + + // Setup view + popup_reset(instance->popup); + popup_set_text( + instance->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); + popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); + + nfc_app_reset_detected_protocols(instance); + + instance->scanner = nfc_scanner_alloc(instance->nfc); + nfc_scanner_start(instance->scanner, nfc_scene_detect_scan_callback, instance); + + nfc_blink_detect_start(instance); +} + +bool nfc_scene_detect_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventWorkerExit) { + if(instance->protocols_detected_num > 1) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSelectProtocol); + } else { + scene_manager_next_scene(instance->scene_manager, NfcSceneRead); + } + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_detect_on_exit(void* context) { + NfcApp* instance = context; + + nfc_scanner_stop(instance->scanner); + nfc_scanner_free(instance->scanner); + popup_reset(instance->popup); + + nfc_blink_stop(instance); +} diff --git a/applications/main/nfc/scenes/nfc_scene_emulate.c b/applications/main/nfc/scenes/nfc_scene_emulate.c new file mode 100644 index 00000000000..6f217f31548 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_emulate.c @@ -0,0 +1,13 @@ +#include "../helpers/protocol_support/nfc_protocol_support.h" + +void nfc_scene_emulate_on_enter(void* context) { + nfc_protocol_support_on_enter(NfcProtocolSupportSceneEmulate, context); +} + +bool nfc_scene_emulate_on_event(void* context, SceneManagerEvent event) { + return nfc_protocol_support_on_event(NfcProtocolSupportSceneEmulate, context, event); +} + +void nfc_scene_emulate_on_exit(void* context) { + nfc_protocol_support_on_exit(NfcProtocolSupportSceneEmulate, context); +} diff --git a/applications/main/nfc/scenes/nfc_scene_exit_confirm.c b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c new file mode 100644 index 00000000000..c024d31295a --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_exit_confirm.c @@ -0,0 +1,52 @@ +#include "../nfc_app_i.h" + +void nfc_scene_exit_confirm_dialog_callback(DialogExResult result, void* context) { + NfcApp* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); +} + +void nfc_scene_exit_confirm_on_enter(void* context) { + NfcApp* nfc = context; + DialogEx* dialog_ex = nfc->dialog_ex; + + dialog_ex_set_left_button_text(dialog_ex, "Exit"); + dialog_ex_set_right_button_text(dialog_ex, "Stay"); + dialog_ex_set_header(dialog_ex, "Exit to NFC Menu?", 64, 11, AlignCenter, AlignTop); + dialog_ex_set_text( + 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_exit_confirm_dialog_callback); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); +} + +bool nfc_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { + NfcApp* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } else if(event.event == DialogExResultLeft) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSelectProtocol)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneSelectProtocol); + } else { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart); + } + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = true; + } + + return consumed; +} + +void nfc_scene_exit_confirm_on_exit(void* context) { + NfcApp* nfc = context; + + // Clean view + dialog_ex_reset(nfc->dialog_ex); +} diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c new file mode 100644 index 00000000000..7f51b717415 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -0,0 +1,72 @@ +#include "../nfc_app_i.h" + +enum SubmenuIndex { + SubmenuIndexReadCardType, + SubmenuIndexMfClassicKeys, + SubmenuIndexMfUltralightUnlock, +}; + +void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { + NfcApp* instance = context; + + view_dispatcher_send_custom_event(instance->view_dispatcher, index); +} + +void nfc_scene_extra_actions_on_enter(void* context) { + NfcApp* instance = context; + Submenu* submenu = instance->submenu; + + submenu_add_item( + submenu, + "Read Specific Card Type", + SubmenuIndexReadCardType, + nfc_scene_extra_actions_submenu_callback, + instance); + submenu_add_item( + submenu, + "Mifare Classic Keys", + SubmenuIndexMfClassicKeys, + nfc_scene_extra_actions_submenu_callback, + instance); + submenu_add_item( + submenu, + "Unlock NTAG/Ultralight", + SubmenuIndexMfUltralightUnlock, + nfc_scene_extra_actions_submenu_callback, + instance); + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(instance->scene_manager, NfcSceneExtraActions)); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexMfClassicKeys) { + if(nfc_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_PATH)) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeys); + } else { + scene_manager_previous_scene(instance->scene_manager); + } + consumed = true; + } else if(event.event == SubmenuIndexMfUltralightUnlock) { + mf_ultralight_auth_reset(instance->mf_ul_auth); + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu); + consumed = true; + } else if(event.event == SubmenuIndexReadCardType) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSelectProtocol); + consumed = true; + } + scene_manager_set_scene_state(instance->scene_manager, NfcSceneExtraActions, event.event); + } + + return consumed; +} + +void nfc_scene_extra_actions_on_exit(void* context) { + NfcApp* instance = context; + + submenu_reset(instance->submenu); +} diff --git a/applications/nfc/scenes/nfc_scene_field.c b/applications/main/nfc/scenes/nfc_scene_field.c similarity index 77% rename from applications/nfc/scenes/nfc_scene_field.c rename to applications/main/nfc/scenes/nfc_scene_field.c index e3eb6a7088b..d5eb916a851 100644 --- a/applications/nfc/scenes/nfc_scene_field.c +++ b/applications/main/nfc/scenes/nfc_scene_field.c @@ -1,10 +1,10 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" void nfc_scene_field_on_enter(void* context) { - Nfc* nfc = context; - - furi_hal_nfc_field_on(); + NfcApp* nfc = context; + furi_hal_nfc_low_power_mode_stop(); + furi_hal_nfc_poller_field_on(); Popup* popup = nfc->popup; popup_set_header( popup, @@ -25,9 +25,9 @@ bool nfc_scene_field_on_event(void* context, SceneManagerEvent event) { } void nfc_scene_field_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; - furi_hal_nfc_field_off(); + furi_hal_nfc_low_power_mode_start(); notification_internal_message(nfc->notifications, &sequence_reset_blue); popup_reset(nfc->popup); } diff --git a/applications/main/nfc/scenes/nfc_scene_file_select.c b/applications/main/nfc/scenes/nfc_scene_file_select.c new file mode 100644 index 00000000000..e1edcadb1be --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_file_select.c @@ -0,0 +1,22 @@ +#include "../nfc_app_i.h" + +void nfc_scene_file_select_on_enter(void* context) { + NfcApp* instance = context; + + if(nfc_load_from_file_select(instance)) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSavedMenu); + } else { + scene_manager_previous_scene(instance->scene_manager); + } +} + +bool nfc_scene_file_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + bool consumed = false; + return consumed; +} + +void nfc_scene_file_select_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/main/nfc/scenes/nfc_scene_generate_info.c b/applications/main/nfc/scenes/nfc_scene_generate_info.c new file mode 100644 index 00000000000..c4f86ff4a6d --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_generate_info.c @@ -0,0 +1,65 @@ +#include "../nfc_app_i.h" + +void nfc_scene_generate_info_widget_callback(GuiButtonType result, InputType type, void* context) { + NfcApp* instance = context; + + if(type == InputTypeShort) { + if(result == GuiButtonTypeRight) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } + } +} + +void nfc_scene_generate_info_on_enter(void* context) { + NfcApp* instance = context; + + NfcProtocol protocol = nfc_device_get_protocol(instance->nfc_device); + furi_assert((protocol == NfcProtocolMfUltralight) || (protocol == NfcProtocolMfClassic)); + + const Iso14443_3aData* iso14443_3a_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_3a); + + // Setup dialog view + Widget* widget = instance->widget; + widget_add_button_element( + widget, GuiButtonTypeRight, "More", nfc_scene_generate_info_widget_callback, instance); + + // Create info text + NfcDataGeneratorType type = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneGenerateInfo); + const char* name = nfc_data_generator_get_name(type); + widget_add_string_element(widget, 0, 0, AlignLeft, AlignTop, FontPrimary, name); + widget_add_string_element(widget, 0, 13, AlignLeft, AlignTop, FontSecondary, "NFC-A"); + + FuriString* temp_str = furi_string_alloc_printf("UID:"); + // Append UID + for(int i = 0; i < iso14443_3a_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", iso14443_3a_data->uid[i]); + } + widget_add_string_element( + widget, 0, 25, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_generate_info_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneReadMenu); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_generate_info_on_exit(void* context) { + NfcApp* instance = context; + + // Clean views + widget_reset(instance->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_info.c b/applications/main/nfc/scenes/nfc_scene_info.c new file mode 100644 index 00000000000..6e9d504975f --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_info.c @@ -0,0 +1,13 @@ +#include "../helpers/protocol_support/nfc_protocol_support.h" + +void nfc_scene_info_on_enter(void* context) { + nfc_protocol_support_on_enter(NfcProtocolSupportSceneInfo, context); +} + +bool nfc_scene_info_on_event(void* context, SceneManagerEvent event) { + return nfc_protocol_support_on_event(NfcProtocolSupportSceneInfo, context, event); +} + +void nfc_scene_info_on_exit(void* context) { + nfc_protocol_support_on_exit(NfcProtocolSupportSceneInfo, context); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c new file mode 100644 index 00000000000..987f81837a5 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_detect_reader.c @@ -0,0 +1,154 @@ +#include "../nfc_app_i.h" + +#include + +#define NXP_MANUFACTURER_ID (0x04) + +#define NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX (10U) +#define NFC_SCENE_DETECT_READER_WAIT_NONCES_TIMEOUT_MS (1000) + +static const NotificationSequence sequence_detect_reader = { + &message_green_255, + &message_blue_255, + &message_do_not_reset, + NULL, +}; + +void nfc_scene_mf_classic_detect_reader_view_callback(void* context) { + NfcApp* instance = context; + + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); +} + +NfcCommand nfc_scene_mf_classic_detect_listener_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.protocol == NfcProtocolMfClassic); + + NfcApp* instance = context; + MfClassicListenerEvent* mfc_event = event.event_data; + + if(mfc_event->type == MfClassicListenerEventTypeAuthContextPartCollected) { + MfClassicAuthContext* auth_ctx = &mfc_event->data->auth_context; + mfkey32_logger_add_nonce(instance->mfkey32_logger, auth_ctx); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWorkerUpdate); + } + + return NfcCommandContinue; +} + +void nfc_scene_mf_classic_timer_callback(void* context) { + NfcApp* instance = context; + + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventTimerExpired); +} + +void nfc_scene_mf_classic_detect_reader_on_enter(void* context) { + NfcApp* instance = context; + + if(nfc_device_get_protocol(instance->nfc_device) == NfcProtocolInvalid) { + Iso14443_3aData iso3_data = { + .uid_len = 7, + .uid = {0}, + .atqa = {0x44, 0x00}, + .sak = 0x08, + }; + iso3_data.uid[0] = NXP_MANUFACTURER_ID; + furi_hal_random_fill_buf(&iso3_data.uid[1], iso3_data.uid_len - 1); + MfClassicData* mfc_data = mf_classic_alloc(); + + mfc_data->type = MfClassicType4k; + iso14443_3a_copy(mfc_data->iso14443_3a_data, &iso3_data); + nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, mfc_data); + + mf_classic_free(mfc_data); + } + + const Iso14443_3aData* iso3_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolIso14443_3a); + uint32_t cuid = iso14443_3a_get_cuid(iso3_data); + + instance->mfkey32_logger = mfkey32_logger_alloc(cuid); + instance->timer = + furi_timer_alloc(nfc_scene_mf_classic_timer_callback, FuriTimerTypeOnce, instance); + + detect_reader_set_nonces_max(instance->detect_reader, NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX); + detect_reader_set_callback( + instance->detect_reader, nfc_scene_mf_classic_detect_reader_view_callback, instance); + + notification_message(instance->notifications, &sequence_detect_reader); + + instance->listener = nfc_listener_alloc( + instance->nfc, + NfcProtocolMfClassic, + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic)); + nfc_listener_start( + instance->listener, nfc_scene_mf_classic_detect_listener_callback, instance); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewDetectReader); +} + +bool nfc_scene_mf_classic_detect_reader_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventWorkerUpdate) { + furi_timer_stop(instance->timer); + notification_message(instance->notifications, &sequence_blink_start_cyan); + + size_t nonces_pairs = 2 * mfkey32_logger_get_params_num(instance->mfkey32_logger); + detect_reader_set_state(instance->detect_reader, DetectReaderStateReaderDetected); + detect_reader_set_nonces_collected(instance->detect_reader, nonces_pairs); + if(nonces_pairs >= NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX) { + if(instance->listener) { + nfc_listener_stop(instance->listener); + nfc_listener_free(instance->listener); + instance->listener = NULL; + } + detect_reader_set_state(instance->detect_reader, DetectReaderStateDone); + nfc_blink_stop(instance); + notification_message(instance->notifications, &sequence_single_vibro); + notification_message(instance->notifications, &sequence_set_green_255); + } else { + furi_timer_start(instance->timer, NFC_SCENE_DETECT_READER_WAIT_NONCES_TIMEOUT_MS); + } + consumed = true; + } else if(event.event == NfcCustomEventTimerExpired) { + detect_reader_set_state(instance->detect_reader, DetectReaderStateReaderLost); + nfc_blink_stop(instance); + notification_message(instance->notifications, &sequence_detect_reader); + } else if(event.event == NfcCustomEventViewExit) { + if(instance->listener) { + nfc_listener_stop(instance->listener); + nfc_listener_free(instance->listener); + instance->listener = NULL; + } + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicMfkeyNoncesInfo); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(instance->listener) { + nfc_listener_stop(instance->listener); + nfc_listener_free(instance->listener); + instance->listener = NULL; + } + mfkey32_logger_free(instance->mfkey32_logger); + } + + return consumed; +} + +void nfc_scene_mf_classic_detect_reader_on_exit(void* context) { + NfcApp* instance = context; + + // Clear view + detect_reader_reset(instance->detect_reader); + + furi_timer_stop(instance->timer); + furi_timer_free(instance->timer); + + // Stop notifications + nfc_blink_stop(instance); + notification_message(instance->notifications, &sequence_reset_green); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c new file mode 100644 index 00000000000..ff7af9e1e8b --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -0,0 +1,270 @@ +#include "../nfc_app_i.h" + +#include +#include + +#define TAG "NfcMfClassicDictAttack" + +typedef enum { + DictAttackStateUserDictInProgress, + DictAttackStateSystemDictInProgress, +} DictAttackState; + +NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.instance); + furi_assert(event.protocol == NfcProtocolMfClassic); + + NfcCommand command = NfcCommandContinue; + MfClassicPollerEvent* mfc_event = event.event_data; + + NfcApp* instance = context; + if(mfc_event->type == MfClassicPollerEventTypeCardDetected) { + instance->nfc_dict_context.is_card_present = true; + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); + } else if(mfc_event->type == MfClassicPollerEventTypeCardLost) { + instance->nfc_dict_context.is_card_present = false; + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost); + } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) { + const MfClassicData* mfc_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + mfc_event->data->poller_mode.mode = MfClassicPollerModeDictAttack; + mfc_event->data->poller_mode.data = mfc_data; + instance->nfc_dict_context.sectors_total = + mf_classic_get_total_sectors_num(mfc_data->type); + mf_classic_get_read_sectors_and_keys( + mfc_data, + &instance->nfc_dict_context.sectors_read, + &instance->nfc_dict_context.keys_found); + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); + } else if(mfc_event->type == MfClassicPollerEventTypeRequestKey) { + MfClassicKey key = {}; + if(nfc_dict_get_next_key(instance->nfc_dict_context.dict, key.data, sizeof(MfClassicKey))) { + mfc_event->data->key_request_data.key = key; + mfc_event->data->key_request_data.key_provided = true; + instance->nfc_dict_context.dict_keys_current++; + if(instance->nfc_dict_context.dict_keys_current % 10 == 0) { + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); + } + } else { + mfc_event->data->key_request_data.key_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeDataUpdate) { + MfClassicPollerEventDataUpdate* data_update = &mfc_event->data->data_update; + instance->nfc_dict_context.sectors_read = data_update->sectors_read; + instance->nfc_dict_context.keys_found = data_update->keys_found; + instance->nfc_dict_context.current_sector = data_update->current_sector; + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); + } else if(mfc_event->type == MfClassicPollerEventTypeNextSector) { + nfc_dict_rewind(instance->nfc_dict_context.dict); + instance->nfc_dict_context.dict_keys_current = 0; + instance->nfc_dict_context.current_sector = + mfc_event->data->next_sector_data.current_sector; + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); + } else if(mfc_event->type == MfClassicPollerEventTypeFoundKeyA) { + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); + } else if(mfc_event->type == MfClassicPollerEventTypeFoundKeyB) { + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); + } else if(mfc_event->type == MfClassicPollerEventTypeKeyAttackStart) { + instance->nfc_dict_context.key_attack_current_sector = + mfc_event->data->key_attack_data.current_sector; + instance->nfc_dict_context.is_key_attack = true; + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); + } else if(mfc_event->type == MfClassicPollerEventTypeKeyAttackStop) { + nfc_dict_rewind(instance->nfc_dict_context.dict); + instance->nfc_dict_context.is_key_attack = false; + instance->nfc_dict_context.dict_keys_current = 0; + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate); + } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) { + const MfClassicData* mfc_data = nfc_poller_get_data(instance->poller); + nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, mfc_data); + view_dispatcher_send_custom_event( + instance->view_dispatcher, NfcCustomEventDictAttackComplete); + command = NfcCommandStop; + } + + return command; +} + +void nfc_dict_attack_dict_attack_result_callback(DictAttackEvent event, void* context) { + furi_assert(context); + NfcApp* instance = context; + + if(event == DictAttackEventSkipPressed) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventDictAttackSkip); + } +} + +static void nfc_scene_mf_classic_dict_attack_update_view(NfcApp* instance) { + NfcMfClassicDictAttackContext* mfc_dict = &instance->nfc_dict_context; + + if(mfc_dict->is_key_attack) { + dict_attack_set_key_attack(instance->dict_attack, mfc_dict->key_attack_current_sector); + } else { + dict_attack_reset_key_attack(instance->dict_attack); + dict_attack_set_sectors_total(instance->dict_attack, mfc_dict->sectors_total); + dict_attack_set_sectors_read(instance->dict_attack, mfc_dict->sectors_read); + dict_attack_set_keys_found(instance->dict_attack, mfc_dict->keys_found); + dict_attack_set_current_dict_key(instance->dict_attack, mfc_dict->dict_keys_current); + dict_attack_set_current_sector(instance->dict_attack, mfc_dict->current_sector); + } +} + +static void nfc_scene_mf_classic_dict_attack_prepare_view(NfcApp* instance) { + uint32_t state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack); + if(state == DictAttackStateUserDictInProgress) { + do { + if(!nfc_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_PATH)) { + state = DictAttackStateSystemDictInProgress; + break; + } + + instance->nfc_dict_context.dict = nfc_dict_alloc( + NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); + if(nfc_dict_get_total_keys(instance->nfc_dict_context.dict) == 0) { + nfc_dict_free(instance->nfc_dict_context.dict); + state = DictAttackStateSystemDictInProgress; + break; + } + + dict_attack_set_header(instance->dict_attack, "MF Classic User Dictionary"); + } while(false); + } + if(state == DictAttackStateSystemDictInProgress) { + instance->nfc_dict_context.dict = nfc_dict_alloc( + NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, NfcDictModeOpenExisting, sizeof(MfClassicKey)); + dict_attack_set_header(instance->dict_attack, "MF Classic System Dictionary"); + } + + instance->nfc_dict_context.dict_keys_total = + nfc_dict_get_total_keys(instance->nfc_dict_context.dict); + dict_attack_set_total_dict_keys( + instance->dict_attack, instance->nfc_dict_context.dict_keys_total); + instance->nfc_dict_context.dict_keys_current = 0; + + dict_attack_set_callback( + instance->dict_attack, nfc_dict_attack_dict_attack_result_callback, instance); + nfc_scene_mf_classic_dict_attack_update_view(instance); + + scene_manager_set_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack, state); +} + +void nfc_scene_mf_classic_dict_attack_on_enter(void* context) { + NfcApp* instance = context; + + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateUserDictInProgress); + nfc_scene_mf_classic_dict_attack_prepare_view(instance); + dict_attack_set_card_state(instance->dict_attack, true); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewDictAttack); + nfc_blink_read_start(instance); + notification_message(instance->notifications, &sequence_display_backlight_enforce_on); + + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); + nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); +} + +bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + uint32_t state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack); + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventDictAttackComplete) { + if(state == DictAttackStateUserDictInProgress) { + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + nfc_dict_free(instance->nfc_dict_context.dict); + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicDictAttack, + DictAttackStateSystemDictInProgress); + nfc_scene_mf_classic_dict_attack_prepare_view(instance); + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); + nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); + consumed = true; + } else { + notification_message(instance->notifications, &sequence_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + consumed = true; + } + } else if(event.event == NfcCustomEventCardDetected) { + dict_attack_set_card_state(instance->dict_attack, true); + consumed = true; + } else if(event.event == NfcCustomEventCardLost) { + dict_attack_set_card_state(instance->dict_attack, false); + consumed = true; + } else if(event.event == NfcCustomEventDictAttackDataUpdate) { + nfc_scene_mf_classic_dict_attack_update_view(instance); + } else if(event.event == NfcCustomEventDictAttackSkip) { + const MfClassicData* mfc_data = nfc_poller_get_data(instance->poller); + nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, mfc_data); + if(state == DictAttackStateUserDictInProgress) { + if(instance->nfc_dict_context.is_card_present) { + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + nfc_dict_free(instance->nfc_dict_context.dict); + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicDictAttack, + DictAttackStateSystemDictInProgress); + nfc_scene_mf_classic_dict_attack_prepare_view(instance); + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); + nfc_poller_start(instance->poller, nfc_dict_attack_worker_callback, instance); + } else { + notification_message(instance->notifications, &sequence_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + } + consumed = true; + } else if(state == DictAttackStateSystemDictInProgress) { + notification_message(instance->notifications, &sequence_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); + consumed = true; + } + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(instance->scene_manager, NfcSceneExitConfirm); + consumed = true; + } + return consumed; +} + +void nfc_scene_mf_classic_dict_attack_on_exit(void* context) { + NfcApp* instance = context; + + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + + dict_attack_reset(instance->dict_attack); + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneMfClassicDictAttack, DictAttackStateUserDictInProgress); + + nfc_dict_free(instance->nfc_dict_context.dict); + + instance->nfc_dict_context.current_sector = 0; + instance->nfc_dict_context.sectors_total = 0; + instance->nfc_dict_context.sectors_read = 0; + instance->nfc_dict_context.keys_found = 0; + instance->nfc_dict_context.dict_keys_total = 0; + instance->nfc_dict_context.dict_keys_current = 0; + instance->nfc_dict_context.is_key_attack = false; + instance->nfc_dict_context.key_attack_current_sector = 0; + instance->nfc_dict_context.is_card_present = false; + + nfc_blink_stop(instance); + notification_message(instance->notifications, &sequence_display_backlight_enforce_auto); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c new file mode 100644 index 00000000000..3106c740aea --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys.c @@ -0,0 +1,95 @@ +#include "../nfc_app_i.h" + +#define NFC_SCENE_MF_CLASSIC_KEYS_MAX (100) + +void nfc_scene_mf_classic_keys_widget_callback(GuiButtonType result, InputType type, void* context) { + NfcApp* instance = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_keys_on_enter(void* context) { + NfcApp* instance = context; + + // Load flipper dict keys total + uint32_t flipper_dict_keys_total = 0; + NfcDict* dict = nfc_dict_alloc( + NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH, NfcDictModeOpenExisting, sizeof(MfClassicKey)); + if(dict) { + flipper_dict_keys_total = nfc_dict_get_total_keys(dict); + nfc_dict_free(dict); + } + + // Load user dict keys total + uint32_t user_dict_keys_total = 0; + dict = nfc_dict_alloc( + NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); + if(dict) { + user_dict_keys_total = nfc_dict_get_total_keys(dict); + nfc_dict_free(dict); + } + + FuriString* temp_str = furi_string_alloc(); + widget_add_string_element( + instance->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "MIFARE Classic Keys"); + furi_string_printf(temp_str, "System dict: %lu", flipper_dict_keys_total); + widget_add_string_element( + instance->widget, + 0, + 20, + AlignLeft, + AlignTop, + FontSecondary, + furi_string_get_cstr(temp_str)); + furi_string_printf(temp_str, "User dict: %lu", user_dict_keys_total); + widget_add_string_element( + instance->widget, + 0, + 32, + AlignLeft, + AlignTop, + FontSecondary, + furi_string_get_cstr(temp_str)); + widget_add_icon_element(instance->widget, 87, 13, &I_Keychain_39x36); + widget_add_button_element( + instance->widget, + GuiButtonTypeCenter, + "Add", + nfc_scene_mf_classic_keys_widget_callback, + instance); + if(user_dict_keys_total > 0) { + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "List", + nfc_scene_mf_classic_keys_widget_callback, + instance); + } + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_keys_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeCenter) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeysAdd); + consumed = true; + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeysList); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_classic_keys_on_exit(void* context) { + NfcApp* instance = context; + + widget_reset(instance->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c new file mode 100644 index 00000000000..82400034359 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c @@ -0,0 +1,62 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_keys_add_byte_input_callback(void* context) { + NfcApp* instance = context; + + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_scene_mf_classic_keys_add_on_enter(void* context) { + NfcApp* instance = context; + + // Setup view + ByteInput* byte_input = instance->byte_input; + byte_input_set_header_text(byte_input, "Enter the key in hex"); + byte_input_set_result_callback( + byte_input, + nfc_scene_mf_classic_keys_add_byte_input_callback, + NULL, + instance, + instance->byte_input_store, + sizeof(MfClassicKey)); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + // Add key to dict + NfcDict* dict = nfc_dict_alloc( + NFC_APP_MF_CLASSIC_DICT_USER_PATH, NfcDictModeOpenAlways, sizeof(MfClassicKey)); + furi_assert(dict); + + MfClassicKey key = {}; + memcpy(key.data, instance->byte_input_store, sizeof(MfClassicKey)); + if(nfc_dict_is_key_present(dict, key.data, sizeof(MfClassicKey))) { + scene_manager_next_scene( + instance->scene_manager, NfcSceneMfClassicKeysWarnDuplicate); + } else if(nfc_dict_add_key(dict, key.data, sizeof(MfClassicKey))) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess); + dolphin_deed(DolphinDeedNfcMfcAdd); + } else { + scene_manager_previous_scene(instance->scene_manager); + } + + nfc_dict_free(dict); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_classic_keys_add_on_exit(void* context) { + NfcApp* instance = context; + + // Clear view + byte_input_set_result_callback(instance->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(instance->byte_input, ""); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c new file mode 100644 index 00000000000..b245a2a552d --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_delete.c @@ -0,0 +1,77 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_keys_delete_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcApp* instance = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_keys_delete_on_enter(void* context) { + NfcApp* instance = context; + + uint32_t key_index = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicKeysDelete); + FuriString* key_str = furi_string_alloc(); + + widget_add_string_element( + instance->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Delete this key?"); + widget_add_button_element( + instance->widget, + GuiButtonTypeLeft, + "Cancel", + nfc_scene_mf_classic_keys_delete_widget_callback, + instance); + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "Delete", + nfc_scene_mf_classic_keys_delete_widget_callback, + instance); + + mf_user_dict_get_key_str(instance->mf_user_dict, key_index, key_str); + widget_add_string_element( + instance->widget, + 64, + 32, + AlignCenter, + AlignCenter, + FontSecondary, + furi_string_get_cstr(key_str)); + + furi_string_free(key_str); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_keys_delete_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeRight) { + uint32_t key_index = scene_manager_get_scene_state( + instance->scene_manager, NfcSceneMfClassicKeysDelete); + if(mf_user_dict_delete_key(instance->mf_user_dict, key_index)) { + scene_manager_next_scene(instance->scene_manager, NfcSceneDeleteSuccess); + } else { + scene_manager_previous_scene(instance->scene_manager); + } + } else if(event.event == GuiButtonTypeLeft) { + scene_manager_previous_scene(instance->scene_manager); + } + consumed = true; + } + + return consumed; +} + +void nfc_scene_mf_classic_keys_delete_on_exit(void* context) { + NfcApp* instance = context; + + mf_user_dict_free(instance->mf_user_dict); + widget_reset(instance->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c new file mode 100644 index 00000000000..7370c06840e --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_list.c @@ -0,0 +1,51 @@ +#include "../nfc_app_i.h" + +#define NFC_SCENE_MF_CLASSIC_KEYS_LIST_MAX (100) + +void nfc_scene_mf_classic_keys_list_submenu_callback(void* context, uint32_t index) { + NfcApp* instance = context; + + view_dispatcher_send_custom_event(instance->view_dispatcher, index); +} + +void nfc_scene_mf_classic_keys_list_on_enter(void* context) { + NfcApp* instance = context; + + instance->mf_user_dict = mf_user_dict_alloc(NFC_SCENE_MF_CLASSIC_KEYS_LIST_MAX); + + submenu_set_header(instance->submenu, "Select key to delete:"); + FuriString* temp_str = furi_string_alloc(); + for(size_t i = 0; i < mf_user_dict_get_keys_cnt(instance->mf_user_dict); i++) { + mf_user_dict_get_key_str(instance->mf_user_dict, i, temp_str); + submenu_add_item( + instance->submenu, + furi_string_get_cstr(temp_str), + i, + nfc_scene_mf_classic_keys_list_submenu_callback, + instance); + } + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_mf_classic_keys_list_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneMfClassicKeysDelete, event.event); + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicKeysDelete); + } else if(event.type == SceneManagerEventTypeBack) { + mf_user_dict_free(instance->mf_user_dict); + } + + return consumed; +} + +void nfc_scene_mf_classic_keys_list_on_exit(void* context) { + NfcApp* instance = context; + + submenu_reset(instance->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c new file mode 100644 index 00000000000..991c956c1c0 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_warn_duplicate.c @@ -0,0 +1,49 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_keys_warn_duplicate_popup_callback(void* context) { + NfcApp* instance = context; + + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_keys_warn_duplicate_on_enter(void* context) { + NfcApp* instance = context; + + // Setup view + Popup* popup = instance->popup; + popup_set_icon(popup, 72, 16, &I_DolphinCommon_56x48); + popup_set_header(popup, "Key already exists!", 64, 3, AlignCenter, AlignTop); + popup_set_text( + popup, + "Please enter a\n" + "different key.", + 4, + 24, + AlignLeft, + AlignTop); + popup_set_timeout(popup, 1500); + popup_set_context(popup, instance); + popup_set_callback(popup, nfc_scene_mf_classic_keys_warn_duplicate_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_classic_keys_warn_duplicate_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneMfClassicKeysAdd); + } + } + + return consumed; +} + +void nfc_scene_mf_classic_keys_warn_duplicate_on_exit(void* context) { + NfcApp* instance = context; + + popup_reset(instance->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c new file mode 100644 index 00000000000..8e07043e25b --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_complete.c @@ -0,0 +1,58 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_mfkey_complete_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcApp* instance = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_mfkey_complete_on_enter(void* context) { + NfcApp* instance = context; + + widget_add_string_element( + instance->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Complete!"); + widget_add_string_multiline_element( + instance->widget, + 64, + 32, + AlignCenter, + AlignCenter, + FontSecondary, + "Now use Mfkey32\nto extract keys"); + widget_add_button_element( + instance->widget, + GuiButtonTypeCenter, + "OK", + nfc_scene_mf_classic_mfkey_complete_callback, + instance); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_mfkey_complete_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeCenter) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneStart); + } + } else if(event.type == SceneManagerEventTypeBack) { + const uint32_t prev_scenes[] = {NfcSceneSavedMenu, NfcSceneStart}; + consumed = scene_manager_search_and_switch_to_previous_scene_one_of( + instance->scene_manager, prev_scenes, COUNT_OF(prev_scenes)); + } + + return consumed; +} + +void nfc_scene_mf_classic_mfkey_complete_on_exit(void* context) { + NfcApp* instance = context; + + widget_reset(instance->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_nonces_info.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_nonces_info.c new file mode 100644 index 00000000000..7f968b1dd68 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_mfkey_nonces_info.c @@ -0,0 +1,72 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_mfkey_nonces_info_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcApp* instance = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_mfkey_nonces_info_on_enter(void* context) { + NfcApp* instance = context; + + FuriString* temp_str = furi_string_alloc(); + + size_t mfkey_params_saved = mfkey32_logger_get_params_num(instance->mfkey32_logger); + furi_string_printf(temp_str, "Nonce pairs saved: %zu\n", mfkey_params_saved); + widget_add_string_element( + instance->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(temp_str)); + widget_add_string_element( + instance->widget, 0, 12, AlignLeft, AlignTop, FontSecondary, "Authenticated sectors:"); + + mfkey32_logger_get_params_data(instance->mfkey32_logger, temp_str); + widget_add_text_scroll_element( + instance->widget, 0, 22, 128, 42, furi_string_get_cstr(temp_str)); + widget_add_button_element( + instance->widget, + GuiButtonTypeCenter, + "OK", + nfc_scene_mf_classic_mfkey_nonces_info_callback, + instance); + + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_mfkey_nonces_info_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeCenter) { + if(mfkey32_logger_save_params( + instance->mfkey32_logger, NFC_APP_MFKEY32_LOGS_FILE_PATH)) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicMfkeyComplete); + } else { + scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneStart); + } + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + const uint32_t prev_scenes[] = {NfcSceneSavedMenu, NfcSceneStart}; + consumed = scene_manager_search_and_switch_to_previous_scene_one_of( + instance->scene_manager, prev_scenes, COUNT_OF(prev_scenes)); + } + + return consumed; +} + +void nfc_scene_mf_classic_mfkey_nonces_info_on_exit(void* context) { + NfcApp* instance = context; + + mfkey32_logger_free(instance->mfkey32_logger); + + // Clear view + widget_reset(instance->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial.c new file mode 100644 index 00000000000..961afdf5311 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial.c @@ -0,0 +1,144 @@ +#include "../nfc_app_i.h" + +#include + +enum { + NfcSceneMfClassicUpdateInitialStateCardSearch, + NfcSceneMfClassicUpdateInitialStateCardFound, +}; + +NfcCommand nfc_mf_classic_update_initial_worker_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.protocol == NfcProtocolMfClassic); + + NfcCommand command = NfcCommandContinue; + const MfClassicPollerEvent* mfc_event = event.event_data; + NfcApp* instance = context; + + if(mfc_event->type == MfClassicPollerEventTypeCardDetected) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); + } else if(mfc_event->type == MfClassicPollerEventTypeCardLost) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost); + } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) { + const MfClassicData* updated_data = nfc_poller_get_data(instance->poller); + const MfClassicData* old_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + if(iso14443_3a_is_equal(updated_data->iso14443_3a_data, old_data->iso14443_3a_data)) { + mfc_event->data->poller_mode.mode = MfClassicPollerModeRead; + } else { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard); + command = NfcCommandStop; + } + } else if(mfc_event->type == MfClassicPollerEventTypeRequestReadSector) { + uint8_t sector_num = 0; + MfClassicKey key = {}; + MfClassicKeyType key_type = MfClassicKeyTypeA; + if(mf_classic_key_cahce_get_next_key( + instance->mfc_key_cache, §or_num, &key, &key_type)) { + mfc_event->data->read_sector_request_data.sector_num = sector_num; + mfc_event->data->read_sector_request_data.key = key; + mfc_event->data->read_sector_request_data.key_type = key_type; + mfc_event->data->read_sector_request_data.key_provided = true; + } else { + mfc_event->data->read_sector_request_data.key_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) { + const MfClassicData* updated_data = nfc_poller_get_data(instance->poller); + nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, updated_data); + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWorkerExit); + command = NfcCommandStop; + } + + return command; +} + +static void nfc_scene_mf_classic_update_initial_setup_view(NfcApp* instance) { + Popup* popup = instance->popup; + popup_reset(popup); + uint32_t state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicUpdateInitial); + + if(state == NfcSceneMfClassicUpdateInitialStateCardSearch) { + popup_set_text( + instance->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); + } else { + popup_set_header(popup, "Updating\nDon't move...", 52, 32, AlignLeft, AlignCenter); + popup_set_icon(popup, 12, 23, &A_Loading_24); + } + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +void nfc_scene_mf_classic_update_initial_on_enter(void* context) { + NfcApp* instance = context; + dolphin_deed(DolphinDeedNfcEmulate); + + const MfClassicData* mfc_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + mf_classic_key_cache_load_from_data(instance->mfc_key_cache, mfc_data); + + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicUpdateInitial, + NfcSceneMfClassicUpdateInitialStateCardSearch); + nfc_scene_mf_classic_update_initial_setup_view(instance); + + // Setup and start worker + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); + nfc_poller_start(instance->poller, nfc_mf_classic_update_initial_worker_callback, instance); + nfc_blink_emulate_start(instance); +} + +bool nfc_scene_mf_classic_update_initial_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventCardDetected) { + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicUpdateInitial, + NfcSceneMfClassicUpdateInitialStateCardFound); + nfc_scene_mf_classic_update_initial_setup_view(instance); + consumed = true; + } else if(event.event == NfcCustomEventCardLost) { + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicUpdateInitial, + NfcSceneMfClassicUpdateInitialStateCardSearch); + nfc_scene_mf_classic_update_initial_setup_view(instance); + consumed = true; + } else if(event.event == NfcCustomEventWrongCard) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWrongCard); + consumed = true; + } else if(event.event == NfcCustomEventWorkerExit) { + if(nfc_save_shadow_file(instance)) { + scene_manager_next_scene( + instance->scene_manager, NfcSceneMfClassicUpdateInitialSuccess); + } else { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWrongCard); + consumed = true; + } + } + } + + return consumed; +} + +void nfc_scene_mf_classic_update_initial_on_exit(void* context) { + NfcApp* instance = context; + + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicUpdateInitial, + NfcSceneMfClassicUpdateInitialStateCardSearch); + // Clear view + popup_reset(instance->popup); + + nfc_blink_stop(instance); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_success.c new file mode 100644 index 00000000000..02e307b01ba --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial_success.c @@ -0,0 +1,43 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_update_initial_success_popup_callback(void* context) { + NfcApp* instance = context; + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_update_initial_success_on_enter(void* context) { + NfcApp* instance = context; + dolphin_deed(DolphinDeedNfcSave); + + notification_message(instance->notifications, &sequence_success); + + Popup* popup = instance->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Updated!", 11, 20, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, instance); + popup_set_callback(popup, nfc_scene_mf_classic_update_initial_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_classic_update_initial_success_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneSavedMenu); + } + } + return consumed; +} + +void nfc_scene_mf_classic_update_initial_success_on_exit(void* context) { + NfcApp* instance = context; + + // Clear view + popup_reset(instance->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c new file mode 100644 index 00000000000..79f1def1d12 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial.c @@ -0,0 +1,146 @@ +#include "../nfc_app_i.h" + +#include + +enum { + NfcSceneMfClassicWriteInitialStateCardSearch, + NfcSceneMfClassicWriteInitialStateCardFound, +}; + +NfcCommand + nfc_scene_mf_classic_write_initial_worker_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.protocol == NfcProtocolMfClassic); + + NfcCommand command = NfcCommandContinue; + NfcApp* instance = context; + MfClassicPollerEvent* mfc_event = event.event_data; + const MfClassicData* write_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfClassic); + + if(mfc_event->type == MfClassicPollerEventTypeCardDetected) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); + } else if(mfc_event->type == MfClassicPollerEventTypeCardLost) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardLost); + } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) { + const MfClassicData* tag_data = nfc_poller_get_data(instance->poller); + if(iso14443_3a_is_equal(tag_data->iso14443_3a_data, write_data->iso14443_3a_data)) { + mfc_event->data->poller_mode.mode = MfClassicPollerModeWrite; + } else { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard); + command = NfcCommandStop; + } + } else if(mfc_event->type == MfClassicPollerEventTypeRequestSectorTrailer) { + uint8_t sector = mfc_event->data->sec_tr_data.sector_num; + uint8_t sec_tr = mf_classic_get_sector_trailer_num_by_sector(sector); + if(mf_classic_is_block_read(write_data, sec_tr)) { + mfc_event->data->sec_tr_data.sector_trailer = write_data->block[sec_tr]; + mfc_event->data->sec_tr_data.sector_trailer_provided = true; + } else { + mfc_event->data->sec_tr_data.sector_trailer_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeRequestWriteBlock) { + uint8_t block_num = mfc_event->data->write_block_data.block_num; + if(mf_classic_is_block_read(write_data, block_num)) { + mfc_event->data->write_block_data.write_block = write_data->block[block_num]; + mfc_event->data->write_block_data.write_block_provided = true; + } else { + mfc_event->data->write_block_data.write_block_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + command = NfcCommandStop; + } else if(mfc_event->type == MfClassicPollerEventTypeFail) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure); + command = NfcCommandStop; + } + return command; +} + +static void nfc_scene_mf_classic_write_initial_setup_view(NfcApp* instance) { + Popup* popup = instance->popup; + popup_reset(popup); + uint32_t state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicWriteInitial); + + if(state == NfcSceneMfClassicWriteInitialStateCardSearch) { + popup_set_text( + instance->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); + } else { + popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); + popup_set_icon(popup, 12, 23, &A_Loading_24); + } + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +void nfc_scene_mf_classic_write_initial_on_enter(void* context) { + NfcApp* instance = context; + dolphin_deed(DolphinDeedNfcEmulate); + + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicWriteInitial, + NfcSceneMfClassicWriteInitialStateCardSearch); + nfc_scene_mf_classic_write_initial_setup_view(instance); + + // Setup and start worker + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfClassic); + nfc_poller_start( + instance->poller, nfc_scene_mf_classic_write_initial_worker_callback, instance); + + nfc_blink_emulate_start(instance); +} + +bool nfc_scene_mf_classic_write_initial_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventCardDetected) { + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicWriteInitial, + NfcSceneMfClassicWriteInitialStateCardFound); + nfc_scene_mf_classic_write_initial_setup_view(instance); + consumed = true; + } else if(event.event == NfcCustomEventCardLost) { + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicWriteInitial, + NfcSceneMfClassicWriteInitialStateCardSearch); + nfc_scene_mf_classic_write_initial_setup_view(instance); + consumed = true; + } else if(event.event == NfcCustomEventWrongCard) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWrongCard); + consumed = true; + } else if(event.event == NfcCustomEventPollerSuccess) { + scene_manager_next_scene( + instance->scene_manager, NfcSceneMfClassicWriteInitialSuccess); + consumed = true; + } else if(event.event == NfcCustomEventPollerFailure) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfClassicWriteInitialFail); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_classic_write_initial_on_exit(void* context) { + NfcApp* instance = context; + + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfClassicWriteInitial, + NfcSceneMfClassicWriteInitialStateCardSearch); + // Clear view + popup_reset(instance->popup); + + nfc_blink_stop(instance); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c new file mode 100644 index 00000000000..f85e5a80c3f --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_fail.c @@ -0,0 +1,62 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_write_initial_fail_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcApp* instance = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_write_initial_fail_on_enter(void* context) { + NfcApp* instance = context; + Widget* widget = instance->widget; + + notification_message(instance->notifications, &sequence_error); + + widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); + widget_add_string_multiline_element( + widget, + 7, + 17, + AlignLeft, + AlignTop, + FontSecondary, + "Not all sectors\nwere written\ncorrectly."); + + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Finish", + nfc_scene_mf_classic_write_initial_fail_widget_callback, + instance); + + // Setup and start worker + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_write_initial_fail_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneSavedMenu); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneSavedMenu); + } + return consumed; +} + +void nfc_scene_mf_classic_write_initial_fail_on_exit(void* context) { + NfcApp* instance = context; + + widget_reset(instance->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c new file mode 100644 index 00000000000..acb75cd2e9f --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_initial_success.c @@ -0,0 +1,43 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_write_initial_success_popup_callback(void* context) { + NfcApp* instance = context; + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_classic_write_initial_success_on_enter(void* context) { + NfcApp* instance = context; + dolphin_deed(DolphinDeedNfcSave); + + notification_message(instance->notifications, &sequence_success); + + Popup* popup = instance->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Successfully\nwritten", 13, 22, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, instance); + popup_set_callback(popup, nfc_scene_mf_classic_write_initial_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_classic_write_initial_success_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneSavedMenu); + } + } + return consumed; +} + +void nfc_scene_mf_classic_write_initial_success_on_exit(void* context) { + NfcApp* instance = context; + + // Clear view + popup_reset(instance->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c new file mode 100644 index 00000000000..50025048af4 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_wrong_card.c @@ -0,0 +1,57 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_classic_wrong_card_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcApp* instance = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_wrong_card_on_enter(void* context) { + NfcApp* instance = context; + Widget* widget = instance->widget; + + notification_message(instance->notifications, &sequence_error); + + widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); + widget_add_string_multiline_element( + widget, + 4, + 17, + AlignLeft, + AlignTop, + FontSecondary, + "Data management\nis only possible\nwith initial card"); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + nfc_scene_mf_classic_wrong_card_widget_callback, + instance); + + // Setup and start worker + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_wrong_card_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(instance->scene_manager); + } + } + return consumed; +} + +void nfc_scene_mf_classic_wrong_card_on_exit(void* context) { + NfcApp* instance = context; + + widget_reset(instance->widget); +} \ No newline at end of file diff --git a/applications/main/nfc/scenes/nfc_scene_mf_desfire_app.c b/applications/main/nfc/scenes/nfc_scene_mf_desfire_app.c new file mode 100644 index 00000000000..8d6a92b6c7e --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_desfire_app.c @@ -0,0 +1,109 @@ +#include "../nfc_app_i.h" + +#include "../helpers/protocol_support/mf_desfire/mf_desfire_render.h" + +enum SubmenuIndex { + SubmenuIndexAppInfo, + SubmenuIndexDynamic, // dynamic indexes start here +}; + +static void nfc_scene_mf_desfire_app_submenu_callback(void* context, uint32_t index) { + NfcApp* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_mf_desfire_app_on_enter(void* context) { + NfcApp* nfc = context; + + text_box_set_font(nfc->text_box, TextBoxFontHex); + submenu_add_item( + nfc->submenu, + "App info", + SubmenuIndexAppInfo, + nfc_scene_mf_desfire_app_submenu_callback, + nfc); + + const uint32_t app_idx = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp) >> 1; + + const MfDesfireData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolMfDesfire); + const MfDesfireApplication* app = simple_array_cget(data->applications, app_idx); + + FuriString* label = furi_string_alloc(); + + for(uint32_t i = 0; i < simple_array_get_count(app->file_ids); ++i) { + const MfDesfireFileId file_id = + *(const MfDesfireFileId*)simple_array_cget(app->file_ids, i); + furi_string_printf(label, "File %d", file_id); + submenu_add_item( + nfc->submenu, + furi_string_get_cstr(label), + i + SubmenuIndexDynamic, + nfc_scene_mf_desfire_app_submenu_callback, + nfc); + } + + furi_string_free(label); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_mf_desfire_app_on_event(void* context, SceneManagerEvent event) { + NfcApp* nfc = context; + bool consumed = false; + + const uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } else { + const MfDesfireData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolMfDesfire); + + const uint32_t app_index = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp) >> 1; + const MfDesfireApplication* app = simple_array_cget(data->applications, app_index); + + TextBox* text_box = nfc->text_box; + furi_string_reset(nfc->text_box_store); + if(event.event == SubmenuIndexAppInfo) { + const MfDesfireApplicationId* app_id = + simple_array_cget(data->application_ids, app_index); + nfc_render_mf_desfire_application_id(app_id, nfc->text_box_store); + nfc_render_mf_desfire_application(app, nfc->text_box_store); + } else { + const uint32_t file_index = event.event - SubmenuIndexDynamic; + const MfDesfireFileId* file_id = simple_array_cget(app->file_ids, file_index); + const MfDesfireFileSettings* file_settings = + simple_array_cget(app->file_settings, file_index); + const MfDesfireFileData* file_data = simple_array_cget(app->file_data, file_index); + nfc_render_mf_desfire_file_id(file_id, nfc->text_box_store); + nfc_render_mf_desfire_file_settings_data( + file_settings, file_data, nfc->text_box_store); + } + text_box_set_text(text_box, furi_string_get_cstr(nfc->text_box_store)); + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, state | 1); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + consumed = true; + } + + } else if(event.type == SceneManagerEventTypeBack) { + if(state & 1) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, state & ~1); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_desfire_app_on_exit(void* context) { + NfcApp* nfc = context; + + // Clear views + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_desfire_more_info.c b/applications/main/nfc/scenes/nfc_scene_mf_desfire_more_info.c new file mode 100644 index 00000000000..76834e3f4f7 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_desfire_more_info.c @@ -0,0 +1,112 @@ +#include "../nfc_app_i.h" + +#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h" +#include "../helpers/protocol_support/mf_desfire/mf_desfire_render.h" + +enum { + MifareDesfireMoreInfoStateMenu, + MifareDesfireMoreInfoStateItem, // MUST be last, states >= this correspond with submenu index +}; + +enum SubmenuIndex { + SubmenuIndexCardInfo, + SubmenuIndexDynamic, // dynamic indices start here +}; + +void nfc_scene_mf_desfire_more_info_on_enter(void* context) { + NfcApp* nfc = context; + Submenu* submenu = nfc->submenu; + + const uint32_t state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireMoreInfo); + const MfDesfireData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolMfDesfire); + + text_box_set_font(nfc->text_box, TextBoxFontHex); + + submenu_add_item( + submenu, + "Card info", + SubmenuIndexCardInfo, + nfc_protocol_support_common_submenu_callback, + nfc); + + FuriString* label = furi_string_alloc(); + + for(uint32_t i = 0; i < simple_array_get_count(data->application_ids); ++i) { + const MfDesfireApplicationId* app_id = simple_array_cget(data->application_ids, i); + furi_string_printf( + label, "App %02x%02x%02x", app_id->data[0], app_id->data[1], app_id->data[2]); + submenu_add_item( + submenu, + furi_string_get_cstr(label), + i + SubmenuIndexDynamic, + nfc_protocol_support_common_submenu_callback, + nfc); + } + + furi_string_free(label); + + if(state >= MifareDesfireMoreInfoStateItem) { + submenu_set_selected_item( + nfc->submenu, state - MifareDesfireMoreInfoStateItem + SubmenuIndexDynamic); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfDesfireMoreInfo, MifareDesfireMoreInfoStateMenu); + } + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_mf_desfire_more_info_on_event(void* context, SceneManagerEvent event) { + NfcApp* nfc = context; + bool consumed = false; + + const uint32_t state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireMoreInfo); + const MfDesfireData* data = nfc_device_get_data(nfc->nfc_device, NfcProtocolMfDesfire); + + if(event.type == SceneManagerEventTypeCustom) { + TextBox* text_box = nfc->text_box; + furi_string_reset(nfc->text_box_store); + + if(event.event == SubmenuIndexCardInfo) { + nfc_render_mf_desfire_data(data, nfc->text_box_store); + text_box_set_text(text_box, furi_string_get_cstr(nfc->text_box_store)); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + scene_manager_set_scene_state( + nfc->scene_manager, + NfcSceneMfDesfireMoreInfo, + MifareDesfireMoreInfoStateItem + SubmenuIndexCardInfo); + consumed = true; + } else { + const uint32_t index = event.event - SubmenuIndexDynamic; + scene_manager_set_scene_state( + nfc->scene_manager, + NfcSceneMfDesfireMoreInfo, + MifareDesfireMoreInfoStateItem + index); + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, index << 1); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireApp); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state >= MifareDesfireMoreInfoStateItem) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfDesfireMoreInfo, MifareDesfireMoreInfoStateMenu); + } else { + // Return directly to the Info scene + scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneInfo); + } + consumed = true; + } + + return consumed; +} + +void nfc_scene_mf_desfire_more_info_on_exit(void* context) { + NfcApp* nfc = context; + + // Clear views + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_capture_pass.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_capture_pass.c new file mode 100644 index 00000000000..1c4be463066 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_capture_pass.c @@ -0,0 +1,67 @@ +#include "../nfc_app_i.h" + +NfcCommand + nfc_scene_mf_ultralight_capture_pass_worker_callback(NfcGenericEvent event, void* context) { + NfcApp* instance = context; + MfUltralightListenerEvent* mfu_event = event.event_data; + MfUltralightAuth* mauth = instance->mf_ul_auth; + + if(mfu_event->type == MfUltralightListenerEventTypeAuth) { + mauth->password = mfu_event->data->password; + view_dispatcher_send_custom_event( + instance->view_dispatcher, MfUltralightListenerEventTypeAuth); + } + + return NfcCommandContinue; +} + +void nfc_scene_mf_ultralight_capture_pass_on_enter(void* context) { + NfcApp* instance = context; + + // Setup view + widget_add_string_multiline_element( + instance->widget, + 54, + 30, + AlignLeft, + AlignCenter, + FontPrimary, + "Touch the\nreader to get\npassword..."); + widget_add_icon_element(instance->widget, 0, 15, &I_Modern_reader_18x34); + widget_add_icon_element(instance->widget, 20, 12, &I_Move_flipper_26x39); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); + + // Start worker + const MfUltralightData* data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); + instance->listener = nfc_listener_alloc(instance->nfc, NfcProtocolMfUltralight, data); + nfc_listener_start( + instance->listener, nfc_scene_mf_ultralight_capture_pass_worker_callback, instance); + + nfc_blink_read_start(instance); +} + +bool nfc_scene_mf_ultralight_capture_pass_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == MfUltralightListenerEventTypeAuth) { + notification_message(instance->notifications, &sequence_success); + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_ultralight_capture_pass_on_exit(void* context) { + NfcApp* instance = context; + + // Clear view + nfc_listener_stop(instance->listener); + nfc_listener_free(instance->listener); + widget_reset(instance->widget); + + nfc_blink_stop(instance); +} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_key_input.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_key_input.c similarity index 87% rename from applications/nfc/scenes/nfc_scene_mf_ultralight_key_input.c rename to applications/main/nfc/scenes/nfc_scene_mf_ultralight_key_input.c index 089187d5bc3..6db6023d274 100644 --- a/applications/nfc/scenes/nfc_scene_mf_ultralight_key_input.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_key_input.c @@ -1,13 +1,13 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" void nfc_scene_mf_ultralight_key_input_byte_input_callback(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); } void nfc_scene_mf_ultralight_key_input_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Setup view ByteInput* byte_input = nfc->byte_input; @@ -17,13 +17,13 @@ void nfc_scene_mf_ultralight_key_input_on_enter(void* context) { nfc_scene_mf_ultralight_key_input_byte_input_callback, NULL, nfc, - nfc->byte_input_store, + nfc->mf_ul_auth->password.data, 4); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); } bool nfc_scene_mf_ultralight_key_input_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { @@ -36,7 +36,7 @@ bool nfc_scene_mf_ultralight_key_input_on_event(void* context, SceneManagerEvent } void nfc_scene_mf_ultralight_key_input_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Clear view byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c new file mode 100644 index 00000000000..4d97040ee20 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c @@ -0,0 +1,84 @@ +#include "../nfc_app_i.h" + +enum SubmenuIndex { + SubmenuIndexMfUlUnlockMenuReader, + SubmenuIndexMfUlUnlockMenuAmeebo, + SubmenuIndexMfUlUnlockMenuXiaomi, + SubmenuIndexMfUlUnlockMenuManual, +}; + +void nfc_scene_mf_ultralight_unlock_menu_submenu_callback(void* context, uint32_t index) { + NfcApp* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) { + NfcApp* nfc = context; + Submenu* submenu = nfc->submenu; + + uint32_t state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + if(nfc_device_get_protocol(nfc->nfc_device) == NfcProtocolMfUltralight) { + submenu_add_item( + submenu, + "Unlock With Reader", + SubmenuIndexMfUlUnlockMenuReader, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + } + submenu_add_item( + submenu, + "Auth As Ameebo", + SubmenuIndexMfUlUnlockMenuAmeebo, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Auth As Xiaomi Air Purifier", + SubmenuIndexMfUlUnlockMenuXiaomi, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Enter Password Manually", + SubmenuIndexMfUlUnlockMenuManual, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + submenu_set_selected_item(submenu, state); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_mf_ultralight_unlock_menu_on_event(void* context, SceneManagerEvent event) { + NfcApp* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexMfUlUnlockMenuManual) { + nfc->mf_ul_auth->type = MfUltralightAuthTypeManual; + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightKeyInput); + consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockMenuAmeebo) { + nfc->mf_ul_auth->type = MfUltralightAuthTypeAmiibo; + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockMenuXiaomi) { + nfc->mf_ul_auth->type = MfUltralightAuthTypeXiaomi; + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockMenuReader) { + nfc->mf_ul_auth->type = MfUltralightAuthTypeReader; + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightCapturePass); + consumed = true; + } + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu, event.event); + } + return consumed; +} + +void nfc_scene_mf_ultralight_unlock_menu_on_exit(void* context) { + NfcApp* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c new file mode 100644 index 00000000000..6be051cedd8 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c @@ -0,0 +1,97 @@ +#include "../nfc_app_i.h" +#include + +void nfc_scene_mf_ultralight_unlock_warn_dialog_callback(DialogExResult result, void* context) { + NfcApp* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); +} + +void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) { + NfcApp* nfc = context; + DialogEx* dialog_ex = nfc->dialog_ex; + + dialog_ex_set_context(dialog_ex, nfc); + dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_ultralight_unlock_warn_dialog_callback); + + MfUltralightAuthType type = nfc->mf_ul_auth->type; + if((type == MfUltralightAuthTypeReader) || (type == MfUltralightAuthTypeManual)) { + // Build dialog text + FuriString* password_str = + furi_string_alloc_set_str("Try to unlock the card with\npassword: "); + for(size_t i = 0; i < sizeof(nfc->mf_ul_auth->password.data); i++) { + furi_string_cat_printf(password_str, "%02X ", nfc->mf_ul_auth->password.data[i]); + } + furi_string_cat_str(password_str, "?\nCaution, a wrong password\ncan block the card!"); + nfc_text_store_set(nfc, furi_string_get_cstr(password_str)); + furi_string_free(password_str); + + const char* message = (type == MfUltralightAuthTypeReader) ? "Password captured!" : + "Risky function!"; + dialog_ex_set_header(dialog_ex, message, 64, 0, AlignCenter, AlignTop); + dialog_ex_set_text(dialog_ex, nfc->text_store, 64, 12, AlignCenter, AlignTop); + dialog_ex_set_left_button_text(dialog_ex, "Cancel"); + dialog_ex_set_right_button_text(dialog_ex, "Continue"); + + if(type == MfUltralightAuthTypeReader) { + notification_message(nfc->notifications, &sequence_set_green_255); + } + } else { + dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog_ex, "Wrong password\ncan block your\ncard.", 4, 18, AlignLeft, AlignTop); + dialog_ex_set_icon(dialog_ex, 73, 20, &I_DolphinCommon_56x48); + dialog_ex_set_center_button_text(dialog_ex, "OK"); + } + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); +} + +bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEvent event) { + NfcApp* nfc = context; + + bool consumed = false; + + nfc->protocols_detected[0] = nfc_device_get_protocol(nfc->nfc_device); + MfUltralightAuthType type = nfc->mf_ul_auth->type; + if((type == MfUltralightAuthTypeReader) || (type == MfUltralightAuthTypeManual)) { + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + dolphin_deed(DolphinDeedNfcRead); + consumed = true; + } else if(event.event == DialogExResultLeft) { + if(type == MfUltralightAuthTypeReader) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + } else { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + } + } else if(event.type == SceneManagerEventTypeBack) { + // Cannot press back + consumed = true; + } + } else { + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultCenter) { + const NfcProtocol mfu_protocol[] = {NfcProtocolMfUltralight}; + nfc_app_set_detected_protocols(nfc, mfu_protocol, COUNT_OF(mfu_protocol)); + scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); + dolphin_deed(DolphinDeedNfcRead); + consumed = true; + } + } + } + + return consumed; +} + +void nfc_scene_mf_ultralight_unlock_warn_on_exit(void* context) { + NfcApp* nfc = context; + + dialog_ex_reset(nfc->dialog_ex); + nfc_text_store_clear(nfc); + + notification_message_block(nfc->notifications, &sequence_reset_green); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c new file mode 100644 index 00000000000..b3c1beef5ab --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c @@ -0,0 +1,119 @@ +#include "../nfc_app_i.h" + +#include + +enum { + NfcSceneMfUltralightWriteStateCardSearch, + NfcSceneMfUltralightWriteStateCardFound, +}; + +NfcCommand nfc_scene_mf_ultralight_write_worker_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.protocol == NfcProtocolMfUltralight); + + NfcCommand command = NfcCommandContinue; + NfcApp* instance = context; + MfUltralightPollerEvent* mfu_event = event.event_data; + + if(mfu_event->type == MfUltralightPollerEventTypeRequestMode) { + mfu_event->data->poller_mode = MfUltralightPollerModeWrite; + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected); + } else if(mfu_event->type == MfUltralightPollerEventTypeAuthRequest) { + mfu_event->data->auth_context.skip_auth = true; + } else if(mfu_event->type == MfUltralightPollerEventTypeRequestWriteData) { + mfu_event->data->write_data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); + } else if(mfu_event->type == MfUltralightPollerEventTypeCardMismatch) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventWrongCard); + command = NfcCommandStop; + } else if(mfu_event->type == MfUltralightPollerEventTypeCardLocked) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure); + command = NfcCommandStop; + } else if(mfu_event->type == MfUltralightPollerEventTypeWriteFail) { + command = NfcCommandStop; + } else if(mfu_event->type == MfUltralightPollerEventTypeWriteSuccess) { + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess); + command = NfcCommandStop; + } + return command; +} + +static void nfc_scene_mf_ultralight_write_setup_view(NfcApp* instance) { + Popup* popup = instance->popup; + popup_reset(popup); + uint32_t state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfUltralightWrite); + + if(state == NfcSceneMfUltralightWriteStateCardSearch) { + popup_set_text( + instance->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter); + popup_set_icon(instance->popup, 0, 8, &I_NFC_manual_60x50); + } else { + popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); + popup_set_icon(popup, 12, 23, &A_Loading_24); + } + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +void nfc_scene_mf_ultralight_write_on_enter(void* context) { + NfcApp* instance = context; + dolphin_deed(DolphinDeedNfcEmulate); + + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfUltralightWrite, + NfcSceneMfUltralightWriteStateCardSearch); + nfc_scene_mf_ultralight_write_setup_view(instance); + + // Setup and start worker + FURI_LOG_D("WMFU", "Card searching..."); + instance->poller = nfc_poller_alloc(instance->nfc, NfcProtocolMfUltralight); + nfc_poller_start(instance->poller, nfc_scene_mf_ultralight_write_worker_callback, instance); + + nfc_blink_emulate_start(instance); +} + +bool nfc_scene_mf_ultralight_write_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventCardDetected) { + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfUltralightWrite, + NfcSceneMfUltralightWriteStateCardFound); + nfc_scene_mf_ultralight_write_setup_view(instance); + consumed = true; + } else if(event.event == NfcCustomEventWrongCard) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrongCard); + consumed = true; + } else if(event.event == NfcCustomEventPollerSuccess) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWriteSuccess); + consumed = true; + } else if(event.event == NfcCustomEventPollerFailure) { + scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWriteFail); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_ultralight_write_on_exit(void* context) { + NfcApp* instance = context; + + nfc_poller_stop(instance->poller); + nfc_poller_free(instance->poller); + + scene_manager_set_scene_state( + instance->scene_manager, + NfcSceneMfUltralightWrite, + NfcSceneMfUltralightWriteStateCardSearch); + // Clear view + popup_reset(instance->popup); + + nfc_blink_stop(instance); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c new file mode 100644 index 00000000000..dff5f278153 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_fail.c @@ -0,0 +1,67 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_ultralight_write_fail_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcApp* instance = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_scene_mf_ultralight_write_fail_on_enter(void* context) { + NfcApp* instance = context; + Widget* widget = instance->widget; + + notification_message(instance->notifications, &sequence_error); + + widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); + widget_add_string_multiline_element( + widget, + 7, + 17, + AlignLeft, + AlignTop, + FontSecondary, + "Card protected by\npassword, AUTH0\nor lock bits"); + + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Finish", + nfc_scene_mf_ultralight_write_fail_widget_callback, + instance); + + // Setup and start worker + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +static bool nfc_scene_mf_ultralight_write_fail_move_to_back_scene(const NfcApp* const instance) { + bool was_saved = scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu); + uint32_t scene_id = was_saved ? NfcSceneSavedMenu : NfcSceneReadMenu; + + return scene_manager_search_and_switch_to_previous_scene(instance->scene_manager, scene_id); +} + +bool nfc_scene_mf_ultralight_write_fail_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = nfc_scene_mf_ultralight_write_fail_move_to_back_scene(instance); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = nfc_scene_mf_ultralight_write_fail_move_to_back_scene(instance); + } + return consumed; +} + +void nfc_scene_mf_ultralight_write_fail_on_exit(void* context) { + NfcApp* instance = context; + + widget_reset(instance->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c new file mode 100644 index 00000000000..c1fbc35ee54 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_write_success.c @@ -0,0 +1,43 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_ultralight_write_success_popup_callback(void* context) { + NfcApp* instance = context; + view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_mf_ultralight_write_success_on_enter(void* context) { + NfcApp* instance = context; + dolphin_deed(DolphinDeedNfcSave); + + notification_message(instance->notifications, &sequence_success); + + Popup* popup = instance->popup; + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Successfully\nwritten", 13, 22, AlignLeft, AlignBottom); + popup_set_timeout(popup, 1500); + popup_set_context(popup, instance); + popup_set_callback(popup, nfc_scene_mf_ultralight_write_success_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewPopup); +} + +bool nfc_scene_mf_ultralight_write_success_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventViewExit) { + consumed = scene_manager_search_and_switch_to_previous_scene( + instance->scene_manager, NfcSceneSavedMenu); + } + } + return consumed; +} + +void nfc_scene_mf_ultralight_write_success_on_exit(void* context) { + NfcApp* instance = context; + + // Clear view + popup_reset(instance->popup); +} diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c new file mode 100644 index 00000000000..a225c474db8 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_wrong_card.c @@ -0,0 +1,58 @@ +#include "../nfc_app_i.h" + +void nfc_scene_mf_ultralight_wrong_card_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcApp* instance = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(instance->view_dispatcher, result); + } +} + +void nfc_scene_mf_ultralight_wrong_card_on_enter(void* context) { + NfcApp* instance = context; + Widget* widget = instance->widget; + + notification_message(instance->notifications, &sequence_error); + + widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); + widget_add_string_element( + widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); + widget_add_string_multiline_element( + widget, + 4, + 17, + AlignLeft, + AlignTop, + FontSecondary, + "Card of the same\ntype should be\n presented"); + //"Data management\nis only possible\nwith card of same type"); + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + nfc_scene_mf_ultralight_wrong_card_widget_callback, + instance); + + // Setup and start worker + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_ultralight_wrong_card_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(instance->scene_manager); + } + } + return consumed; +} + +void nfc_scene_mf_ultralight_wrong_card_on_exit(void* context) { + NfcApp* instance = context; + + widget_reset(instance->widget); +} \ No newline at end of file diff --git a/applications/main/nfc/scenes/nfc_scene_more_info.c b/applications/main/nfc/scenes/nfc_scene_more_info.c new file mode 100644 index 00000000000..b74e30295d3 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_more_info.c @@ -0,0 +1,13 @@ +#include "../helpers/protocol_support/nfc_protocol_support.h" + +void nfc_scene_more_info_on_enter(void* context) { + nfc_protocol_support_on_enter(NfcProtocolSupportSceneMoreInfo, context); +} + +bool nfc_scene_more_info_on_event(void* context, SceneManagerEvent event) { + return nfc_protocol_support_on_event(NfcProtocolSupportSceneMoreInfo, context, event); +} + +void nfc_scene_more_info_on_exit(void* context) { + nfc_protocol_support_on_exit(NfcProtocolSupportSceneMoreInfo, context); +} diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c new file mode 100644 index 00000000000..e9603afd1ad --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -0,0 +1,13 @@ +#include "../helpers/protocol_support/nfc_protocol_support.h" + +void nfc_scene_read_on_enter(void* context) { + nfc_protocol_support_on_enter(NfcProtocolSupportSceneRead, context); +} + +bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { + return nfc_protocol_support_on_event(NfcProtocolSupportSceneRead, context, event); +} + +void nfc_scene_read_on_exit(void* context) { + nfc_protocol_support_on_exit(NfcProtocolSupportSceneRead, context); +} diff --git a/applications/main/nfc/scenes/nfc_scene_read_menu.c b/applications/main/nfc/scenes/nfc_scene_read_menu.c new file mode 100644 index 00000000000..cba07a485ae --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_read_menu.c @@ -0,0 +1,13 @@ +#include "../helpers/protocol_support/nfc_protocol_support.h" + +void nfc_scene_read_menu_on_enter(void* context) { + nfc_protocol_support_on_enter(NfcProtocolSupportSceneReadMenu, context); +} + +bool nfc_scene_read_menu_on_event(void* context, SceneManagerEvent event) { + return nfc_protocol_support_on_event(NfcProtocolSupportSceneReadMenu, context, event); +} + +void nfc_scene_read_menu_on_exit(void* context) { + nfc_protocol_support_on_exit(NfcProtocolSupportSceneReadMenu, context); +} diff --git a/applications/main/nfc/scenes/nfc_scene_read_success.c b/applications/main/nfc/scenes/nfc_scene_read_success.c new file mode 100644 index 00000000000..5ceada48b5a --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_read_success.c @@ -0,0 +1,13 @@ +#include "../helpers/protocol_support/nfc_protocol_support.h" + +void nfc_scene_read_success_on_enter(void* context) { + nfc_protocol_support_on_enter(NfcProtocolSupportSceneReadSuccess, context); +} + +bool nfc_scene_read_success_on_event(void* context, SceneManagerEvent event) { + return nfc_protocol_support_on_event(NfcProtocolSupportSceneReadSuccess, context, event); +} + +void nfc_scene_read_success_on_exit(void* context) { + nfc_protocol_support_on_exit(NfcProtocolSupportSceneReadSuccess, context); +} diff --git a/applications/nfc/scenes/nfc_scene_restore_original.c b/applications/main/nfc/scenes/nfc_scene_restore_original.c similarity index 82% rename from applications/nfc/scenes/nfc_scene_restore_original.c rename to applications/main/nfc/scenes/nfc_scene_restore_original.c index 3ecf5c048e4..612e6041e69 100644 --- a/applications/nfc/scenes/nfc_scene_restore_original.c +++ b/applications/main/nfc/scenes/nfc_scene_restore_original.c @@ -1,12 +1,12 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" void nfc_scene_restore_original_popup_callback(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); } void nfc_scene_restore_original_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Setup view Popup* popup = nfc->popup; @@ -20,17 +20,17 @@ void nfc_scene_restore_original_on_enter(void* context) { } bool nfc_scene_restore_original_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventViewExit) { - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { + if(nfc_load_file(nfc, nfc->file_path, false)) { consumed = scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneSavedMenu); } else { consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneStart); + nfc->scene_manager, NfcSceneFileSelect); } } } @@ -38,7 +38,7 @@ bool nfc_scene_restore_original_on_event(void* context, SceneManagerEvent event) } void nfc_scene_restore_original_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Clear view popup_reset(nfc->popup); diff --git a/applications/nfc/scenes/nfc_scene_restore_original_confirm.c b/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c similarity index 87% rename from applications/nfc/scenes/nfc_scene_restore_original_confirm.c rename to applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c index 2c12749dfa1..6e260da2a27 100644 --- a/applications/nfc/scenes/nfc_scene_restore_original_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c @@ -1,17 +1,17 @@ -#include "../nfc_i.h" +#include "../nfc_app_i.h" void nfc_scene_restore_original_confirm_dialog_callback(DialogExResult result, void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, result); } void nfc_scene_restore_original_confirm_on_enter(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; DialogEx* dialog_ex = nfc->dialog_ex; dialog_ex_set_header(dialog_ex, "Restore Card Data?", 64, 0, AlignCenter, AlignTop); - dialog_ex_set_icon(dialog_ex, 5, 15, &I_Restoring); + dialog_ex_set_icon(dialog_ex, 5, 11, &I_ArrowC_1_36x36); dialog_ex_set_text( dialog_ex, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop); dialog_ex_set_left_button_text(dialog_ex, "Cancel"); @@ -23,16 +23,16 @@ void nfc_scene_restore_original_confirm_on_enter(void* context) { } bool nfc_scene_restore_original_confirm_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == DialogExResultRight) { - if(!nfc_device_restore(nfc->dev, true)) { + if(nfc_delete_shadow_file(nfc)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginal); + } else { scene_manager_search_and_switch_to_previous_scene( nfc->scene_manager, NfcSceneStart); - } else { - scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginal); } consumed = true; } else if(event.event == DialogExResultLeft) { @@ -46,7 +46,7 @@ bool nfc_scene_restore_original_confirm_on_event(void* context, SceneManagerEven } void nfc_scene_restore_original_confirm_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Clean view dialog_ex_reset(nfc->dialog_ex); diff --git a/applications/main/nfc/scenes/nfc_scene_retry_confirm.c b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c new file mode 100644 index 00000000000..b80f1bdcc14 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_retry_confirm.c @@ -0,0 +1,52 @@ +#include "../nfc_app_i.h" + +void nfc_scene_retry_confirm_dialog_callback(DialogExResult result, void* context) { + NfcApp* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); +} + +void nfc_scene_retry_confirm_on_enter(void* context) { + NfcApp* nfc = context; + DialogEx* dialog_ex = nfc->dialog_ex; + + dialog_ex_set_left_button_text(dialog_ex, "Retry"); + 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\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); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); +} + +bool nfc_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) { + NfcApp* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultRight) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } else if(event.event == DialogExResultLeft) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneDetect)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneDetect); + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneRead)) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneRead); + } + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = true; + } + + return consumed; +} + +void nfc_scene_retry_confirm_on_exit(void* context) { + NfcApp* nfc = context; + + // Clean view + dialog_ex_reset(nfc->dialog_ex); +} diff --git a/applications/main/nfc/scenes/nfc_scene_rpc.c b/applications/main/nfc/scenes/nfc_scene_rpc.c new file mode 100644 index 00000000000..e12e84605bc --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_rpc.c @@ -0,0 +1,13 @@ +#include "../helpers/protocol_support/nfc_protocol_support.h" + +void nfc_scene_rpc_on_enter(void* context) { + nfc_protocol_support_on_enter(NfcProtocolSupportSceneRpc, context); +} + +bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { + return nfc_protocol_support_on_event(NfcProtocolSupportSceneRpc, context, event); +} + +void nfc_scene_rpc_on_exit(void* context) { + nfc_protocol_support_on_exit(NfcProtocolSupportSceneRpc, context); +} diff --git a/applications/main/nfc/scenes/nfc_scene_save_name.c b/applications/main/nfc/scenes/nfc_scene_save_name.c new file mode 100644 index 00000000000..c23f097e144 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_save_name.c @@ -0,0 +1,13 @@ +#include "../helpers/protocol_support/nfc_protocol_support.h" + +void nfc_scene_save_name_on_enter(void* context) { + nfc_protocol_support_on_enter(NfcProtocolSupportSceneSaveName, context); +} + +bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { + return nfc_protocol_support_on_event(NfcProtocolSupportSceneSaveName, context, event); +} + +void nfc_scene_save_name_on_exit(void* context) { + nfc_protocol_support_on_exit(NfcProtocolSupportSceneSaveName, context); +} diff --git a/applications/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c similarity index 82% rename from applications/nfc/scenes/nfc_scene_save_success.c rename to applications/main/nfc/scenes/nfc_scene_save_success.c index a3b17451f01..0cb26c0d45a 100644 --- a/applications/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -1,14 +1,12 @@ -#include "../nfc_i.h" -#include +#include "../nfc_app_i.h" void nfc_scene_save_success_popup_callback(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); } void nfc_scene_save_success_on_enter(void* context) { - Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcSave); + NfcApp* nfc = context; // Setup view Popup* popup = nfc->popup; @@ -22,14 +20,14 @@ void nfc_scene_save_success_on_enter(void* context) { } bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; + NfcApp* nfc = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventViewExit) { - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneMfClassicKeys)) { consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneSavedMenu); + nfc->scene_manager, NfcSceneMfClassicKeys); } else { consumed = scene_manager_search_and_switch_to_another_scene( nfc->scene_manager, NfcSceneFileSelect); @@ -40,7 +38,7 @@ bool nfc_scene_save_success_on_event(void* context, SceneManagerEvent event) { } void nfc_scene_save_success_on_exit(void* context) { - Nfc* nfc = context; + NfcApp* nfc = context; // Clear view popup_reset(nfc->popup); diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c new file mode 100644 index 00000000000..d367e8ab861 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -0,0 +1,13 @@ +#include "../helpers/protocol_support/nfc_protocol_support.h" + +void nfc_scene_saved_menu_on_enter(void* context) { + nfc_protocol_support_on_enter(NfcProtocolSupportSceneSavedMenu, context); +} + +bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { + return nfc_protocol_support_on_event(NfcProtocolSupportSceneSavedMenu, context, event); +} + +void nfc_scene_saved_menu_on_exit(void* context) { + nfc_protocol_support_on_exit(NfcProtocolSupportSceneSavedMenu, context); +} diff --git a/applications/main/nfc/scenes/nfc_scene_select_protocol.c b/applications/main/nfc/scenes/nfc_scene_select_protocol.c new file mode 100644 index 00000000000..86b9982fc69 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_select_protocol.c @@ -0,0 +1,67 @@ +#include "../nfc_app_i.h" + +void nfc_scene_select_protocol_submenu_callback(void* context, uint32_t index) { + NfcApp* instance = context; + + view_dispatcher_send_custom_event(instance->view_dispatcher, index); +} + +void nfc_scene_select_protocol_on_enter(void* context) { + NfcApp* instance = context; + Submenu* submenu = instance->submenu; + + FuriString* temp_str = furi_string_alloc(); + const char* prefix; + if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneExtraActions)) { + prefix = "Read"; + instance->protocols_detected_num = NfcProtocolNum; + for(uint32_t i = 0; i < NfcProtocolNum; i++) { + instance->protocols_detected[i] = i; + } + } else { + prefix = "Read as"; + submenu_set_header(submenu, "Multi-protocol card"); + notification_message(instance->notifications, &sequence_single_vibro); + } + + for(uint32_t i = 0; i < instance->protocols_detected_num; i++) { + furi_string_printf( + temp_str, + "%s %s", + prefix, + nfc_device_get_protocol_name(instance->protocols_detected[i])); + submenu_add_item( + submenu, + furi_string_get_cstr(temp_str), + i, + nfc_scene_select_protocol_submenu_callback, + instance); + } + furi_string_free(temp_str); + + const uint32_t state = + scene_manager_get_scene_state(instance->scene_manager, NfcSceneSelectProtocol); + submenu_set_selected_item(submenu, state); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_select_protocol_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + instance->protocols_detected_selected_idx = event.event; + scene_manager_next_scene(instance->scene_manager, NfcSceneRead); + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneSelectProtocol, event.event); + consumed = true; + } + return consumed; +} + +void nfc_scene_select_protocol_on_exit(void* context) { + NfcApp* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_set_atqa.c b/applications/main/nfc/scenes/nfc_scene_set_atqa.c new file mode 100644 index 00000000000..17a07b8b537 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_set_atqa.c @@ -0,0 +1,49 @@ +#include "../nfc_app_i.h" + +#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h" + +static void nfc_scene_set_atqa_byte_input_changed_callback(void* context) { + NfcApp* instance = context; + iso14443_3a_set_atqa(instance->iso14443_3a_edit_data, instance->byte_input_store); +} + +void nfc_scene_set_atqa_on_enter(void* context) { + NfcApp* instance = context; + + iso14443_3a_get_atqa(instance->iso14443_3a_edit_data, instance->byte_input_store); + + // Setup view + ByteInput* byte_input = instance->byte_input; + byte_input_set_header_text(byte_input, "Enter ATQA in hex"); + byte_input_set_result_callback( + byte_input, + nfc_protocol_support_common_byte_input_done_callback, + nfc_scene_set_atqa_byte_input_changed_callback, + instance, + instance->byte_input_store, + 2); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_set_atqa_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + nfc_device_set_data( + instance->nfc_device, NfcProtocolIso14443_3a, instance->iso14443_3a_edit_data); + scene_manager_next_scene(instance->scene_manager, NfcSceneSetUid); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_set_atqa_on_exit(void* context) { + NfcApp* instance = context; + + // Clear view + byte_input_set_result_callback(instance->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(instance->byte_input, ""); +} diff --git a/applications/main/nfc/scenes/nfc_scene_set_sak.c b/applications/main/nfc/scenes/nfc_scene_set_sak.c new file mode 100644 index 00000000000..c55cee1c21d --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_set_sak.c @@ -0,0 +1,48 @@ +#include "../nfc_app_i.h" + +#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h" + +static void nfc_scene_set_sak_byte_input_changed_callback(void* context) { + NfcApp* instance = context; + iso14443_3a_set_sak(instance->iso14443_3a_edit_data, instance->byte_input_store[0]); +} + +void nfc_scene_set_sak_on_enter(void* context) { + NfcApp* instance = context; + + instance->byte_input_store[0] = iso14443_3a_get_sak(instance->iso14443_3a_edit_data); + + // Setup view + ByteInput* byte_input = instance->byte_input; + byte_input_set_header_text(byte_input, "Enter SAK in hex"); + byte_input_set_result_callback( + byte_input, + nfc_protocol_support_common_byte_input_done_callback, + nfc_scene_set_sak_byte_input_changed_callback, + instance, + instance->byte_input_store, + 1); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_set_sak_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSetAtqa); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_set_sak_on_exit(void* context) { + NfcApp* instance = context; + + // Clear view + byte_input_set_result_callback(instance->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(instance->byte_input, ""); +} diff --git a/applications/main/nfc/scenes/nfc_scene_set_type.c b/applications/main/nfc/scenes/nfc_scene_set_type.c new file mode 100644 index 00000000000..e3366008078 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_set_type.c @@ -0,0 +1,72 @@ +#include "../nfc_app_i.h" + +#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h" + +enum SubmenuIndex { + SubmenuIndexGeneratorsStart, + SubmenuIndexNFCA4 = NfcDataGeneratorTypeNum, + SubmenuIndexNFCA7, +}; + +static void nfc_scene_set_type_init_edit_data(Iso14443_3aData* data, size_t uid_len) { + // Easiest way to create a zero'd buffer of given length + uint8_t* uid = malloc(uid_len); + iso14443_3a_set_uid(data, uid, uid_len); + free(uid); +} + +void nfc_scene_set_type_on_enter(void* context) { + NfcApp* instance = context; + + Submenu* submenu = instance->submenu; + submenu_add_item( + submenu, + "NFC-A 7-bytes UID", + SubmenuIndexNFCA7, + nfc_protocol_support_common_submenu_callback, + instance); + submenu_add_item( + submenu, + "NFC-A 4-bytes UID", + SubmenuIndexNFCA4, + nfc_protocol_support_common_submenu_callback, + instance); + + for(size_t i = 0; i < NfcDataGeneratorTypeNum; i++) { + const char* name = nfc_data_generator_get_name(i); + submenu_add_item(submenu, name, i, nfc_protocol_support_common_submenu_callback, instance); + } + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexNFCA7) { + nfc_scene_set_type_init_edit_data(instance->iso14443_3a_edit_data, 7); + scene_manager_next_scene(instance->scene_manager, NfcSceneSetSak); + consumed = true; + } else if(event.event == SubmenuIndexNFCA4) { + nfc_scene_set_type_init_edit_data(instance->iso14443_3a_edit_data, 4); + scene_manager_next_scene(instance->scene_manager, NfcSceneSetSak); + consumed = true; + } else { + nfc_data_generator_fill_data(event.event, instance->nfc_device); + scene_manager_set_scene_state( + instance->scene_manager, NfcSceneGenerateInfo, event.event); + scene_manager_next_scene(instance->scene_manager, NfcSceneGenerateInfo); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_set_type_on_exit(void* context) { + NfcApp* instance = context; + + submenu_reset(instance->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_set_uid.c b/applications/main/nfc/scenes/nfc_scene_set_uid.c new file mode 100644 index 00000000000..df8a4dc72cc --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_set_uid.c @@ -0,0 +1,63 @@ +#include "../nfc_app_i.h" + +#include "../helpers/protocol_support/nfc_protocol_support_gui_common.h" + +static void nfc_scene_set_uid_byte_input_changed_callback(void* context) { + NfcApp* instance = context; + // Retrieve previously saved UID length + const size_t uid_len = scene_manager_get_scene_state(instance->scene_manager, NfcSceneSetUid); + nfc_device_set_uid(instance->nfc_device, instance->byte_input_store, uid_len); +} + +void nfc_scene_set_uid_on_enter(void* context) { + NfcApp* instance = context; + + size_t uid_len; + const uint8_t* uid = nfc_device_get_uid(instance->nfc_device, &uid_len); + + memcpy(instance->byte_input_store, uid, uid_len); + // Save UID length for use in callback + scene_manager_set_scene_state(instance->scene_manager, NfcSceneSetUid, uid_len); + + // Setup view + ByteInput* byte_input = instance->byte_input; + byte_input_set_header_text(byte_input, "Enter UID in hex"); + byte_input_set_result_callback( + byte_input, + nfc_protocol_support_common_byte_input_done_callback, + nfc_scene_set_uid_byte_input_changed_callback, + instance, + instance->byte_input_store, + uid_len); + + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + if(scene_manager_has_previous_scene(instance->scene_manager, NfcSceneSavedMenu)) { + if(nfc_save(instance)) { + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveSuccess); + consumed = true; + } + } else { + scene_manager_next_scene(instance->scene_manager, NfcSceneSaveName); + consumed = true; + } + } + } + + return consumed; +} + +void nfc_scene_set_uid_on_exit(void* context) { + NfcApp* instance = context; + + // Clear view + byte_input_set_result_callback(instance->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(instance->byte_input, ""); +} diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c new file mode 100644 index 00000000000..c923226fc7e --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -0,0 +1,93 @@ +#include "../nfc_app_i.h" +#include + +enum SubmenuIndex { + SubmenuIndexRead, + SubmenuIndexDetectReader, + SubmenuIndexSaved, + SubmenuIndexExtraAction, + SubmenuIndexAddManually, + SubmenuIndexDebug, +}; + +void nfc_scene_start_submenu_callback(void* context, uint32_t index) { + NfcApp* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_start_on_enter(void* context) { + NfcApp* nfc = context; + Submenu* submenu = nfc->submenu; + + // Clear file name and device contents + furi_string_reset(nfc->file_name); + nfc_device_clear(nfc->nfc_device); + iso14443_3a_reset(nfc->iso14443_3a_edit_data); + + submenu_add_item(submenu, "Read", SubmenuIndexRead, nfc_scene_start_submenu_callback, nfc); + submenu_add_item( + submenu, "Detect Reader", SubmenuIndexDetectReader, nfc_scene_start_submenu_callback, nfc); + submenu_add_item(submenu, "Saved", SubmenuIndexSaved, nfc_scene_start_submenu_callback, nfc); + submenu_add_item( + submenu, "Extra Actions", SubmenuIndexExtraAction, nfc_scene_start_submenu_callback, nfc); + submenu_add_item( + submenu, "Add Manually", SubmenuIndexAddManually, nfc_scene_start_submenu_callback, nfc); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + submenu_add_item( + submenu, "Debug", SubmenuIndexDebug, nfc_scene_start_submenu_callback, nfc); + } + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneStart)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { + NfcApp* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexRead) { + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead); + scene_manager_next_scene(nfc->scene_manager, NfcSceneDetect); + dolphin_deed(DolphinDeedNfcRead); + consumed = true; + } else if(event.event == SubmenuIndexDetectReader) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneStart, SubmenuIndexDetectReader); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDetectReader); + consumed = true; + } else if(event.event == SubmenuIndexSaved) { + // Save the scene state explicitly in each branch, so that + // if the user cancels loading a file, the Saved menu item + // is properly reselected. + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexSaved); + scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect); + consumed = true; + } else if(event.event == SubmenuIndexExtraAction) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneStart, SubmenuIndexExtraAction); + scene_manager_next_scene(nfc->scene_manager, NfcSceneExtraActions); + consumed = true; + } else if(event.event == SubmenuIndexAddManually) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneStart, SubmenuIndexAddManually); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType); + consumed = true; + } else if(event.event == SubmenuIndexDebug) { + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug); + scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_start_on_exit(void* context) { + NfcApp* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_supported_card.c b/applications/main/nfc/scenes/nfc_scene_supported_card.c new file mode 100644 index 00000000000..cea55b783bc --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_supported_card.c @@ -0,0 +1,50 @@ +#include "nfc/nfc_app_i.h" + +#include "nfc/helpers/nfc_supported_cards.h" +#include "nfc/helpers/protocol_support/nfc_protocol_support_gui_common.h" + +void nfc_scene_supported_card_on_enter(void* context) { + NfcApp* instance = context; + + FuriString* temp_str = furi_string_alloc(); + + if(nfc_supported_cards_parse(instance->nfc_device, temp_str)) { + widget_add_text_scroll_element( + instance->widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + widget_add_button_element( + instance->widget, + GuiButtonTypeRight, + "More", + nfc_protocol_support_common_widget_callback, + instance); + + scene_manager_set_scene_state(instance->scene_manager, NfcSceneSupportedCard, true); + view_dispatcher_switch_to_view(instance->view_dispatcher, NfcViewWidget); + + } else { + scene_manager_set_scene_state(instance->scene_manager, NfcSceneSupportedCard, false); + scene_manager_next_scene(instance->scene_manager, NfcSceneInfo); + } + + furi_string_free(temp_str); +} + +bool nfc_scene_supported_card_on_event(void* context, SceneManagerEvent event) { + NfcApp* instance = context; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(instance->scene_manager, NfcSceneInfo); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_supported_card_on_exit(void* context) { + NfcApp* instance = context; + widget_reset(instance->widget); +} diff --git a/applications/main/nfc/views/detect_reader.c b/applications/main/nfc/views/detect_reader.c new file mode 100644 index 00000000000..ebcda7caf1b --- /dev/null +++ b/applications/main/nfc/views/detect_reader.c @@ -0,0 +1,193 @@ +#include "detect_reader.h" +#include +#include + +#define DETECT_READER_UID_MAX_LEN (10) + +struct DetectReader { + View* view; + DetectReaderDoneCallback callback; + void* context; +}; + +typedef struct { + uint16_t nonces; + uint16_t nonces_max; + DetectReaderState state; + FuriString* uid_str; +} DetectReaderViewModel; + +static void detect_reader_draw_callback(Canvas* canvas, void* model) { + DetectReaderViewModel* m = model; + char text[32] = {}; + + // Draw header and icon + canvas_draw_icon(canvas, 0, 16, &I_Modern_reader_18x34); + if(m->state == DetectReaderStateStart) { + snprintf(text, sizeof(text), "Touch the reader"); + canvas_draw_icon(canvas, 21, 13, &I_Move_flipper_26x39); + if(furi_string_size(m->uid_str)) { + elements_multiline_text_aligned( + canvas, 64, 64, AlignCenter, AlignBottom, furi_string_get_cstr(m->uid_str)); + } + } else if(m->state == DetectReaderStateReaderDetected) { + snprintf(text, sizeof(text), "Move the Flipper away"); + canvas_draw_icon(canvas, 24, 25, &I_Release_arrow_18x15); + } else if(m->state == DetectReaderStateReaderLost) { + snprintf(text, sizeof(text), "Touch the reader again"); + canvas_draw_icon(canvas, 21, 13, &I_Move_flipper_26x39); + } + + canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, text); + + // Draw collected nonces + if(m->state == DetectReaderStateStart) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Emulating..."); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 51, 35, AlignLeft, AlignTop, "MIFARE MFkey32"); + } else { + if(m->state == DetectReaderStateDone) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Completed!"); + } else { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 51, 22, AlignLeft, AlignTop, "Collecting..."); + } + canvas_set_font(canvas, FontSecondary); + snprintf(text, sizeof(text), "Nonce pairs: %d/%d", m->nonces, m->nonces_max); + canvas_draw_str_aligned(canvas, 51, 35, AlignLeft, AlignTop, text); + } + // Draw button + if(m->nonces > 0) { + elements_button_center(canvas, "Done"); + } +} + +static bool detect_reader_input_callback(InputEvent* event, void* context) { + DetectReader* detect_reader = context; + furi_assert(detect_reader->callback); + bool consumed = false; + + uint8_t nonces = 0; + with_view_model( + detect_reader->view, DetectReaderViewModel * model, { nonces = model->nonces; }, false); + + if(event->type == InputTypeShort) { + if(event->key == InputKeyOk) { + if(nonces > 0) { + detect_reader->callback(detect_reader->context); + consumed = true; + } + } + } + + return consumed; +} + +DetectReader* detect_reader_alloc() { + DetectReader* detect_reader = malloc(sizeof(DetectReader)); + detect_reader->view = view_alloc(); + view_allocate_model(detect_reader->view, ViewModelTypeLocking, sizeof(DetectReaderViewModel)); + view_set_draw_callback(detect_reader->view, detect_reader_draw_callback); + view_set_input_callback(detect_reader->view, detect_reader_input_callback); + view_set_context(detect_reader->view, detect_reader); + + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { model->uid_str = furi_string_alloc(); }, + false); + + return detect_reader; +} + +void detect_reader_free(DetectReader* detect_reader) { + furi_assert(detect_reader); + + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { furi_string_free(model->uid_str); }, + false); + + view_free(detect_reader->view); + free(detect_reader); +} + +void detect_reader_reset(DetectReader* detect_reader) { + furi_assert(detect_reader); + + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { + model->nonces = 0; + model->nonces_max = 0; + model->state = DetectReaderStateStart; + furi_string_reset(model->uid_str); + }, + false); +} + +View* detect_reader_get_view(DetectReader* detect_reader) { + furi_assert(detect_reader); + + return detect_reader->view; +} + +void detect_reader_set_callback( + DetectReader* detect_reader, + DetectReaderDoneCallback callback, + void* context) { + furi_assert(detect_reader); + furi_assert(callback); + + detect_reader->callback = callback; + detect_reader->context = context; +} + +void detect_reader_set_nonces_max(DetectReader* detect_reader, uint16_t nonces_max) { + furi_assert(detect_reader); + + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { model->nonces_max = nonces_max; }, + false); +} + +void detect_reader_set_nonces_collected(DetectReader* detect_reader, uint16_t nonces_collected) { + furi_assert(detect_reader); + + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { + model->nonces = nonces_collected; + model->state = DetectReaderStateReaderDetected; + }, + false); +} + +void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState state) { + furi_assert(detect_reader); + with_view_model( + detect_reader->view, DetectReaderViewModel * model, { model->state = state; }, true); +} + +void detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len) { + furi_assert(detect_reader); + furi_assert(uid); + furi_assert(uid_len < DETECT_READER_UID_MAX_LEN); + with_view_model( + detect_reader->view, + DetectReaderViewModel * model, + { + furi_string_set_str(model->uid_str, "UID:"); + for(size_t i = 0; i < uid_len; i++) { + furi_string_cat_printf(model->uid_str, " %02X", uid[i]); + } + }, + true); +} diff --git a/applications/main/nfc/views/detect_reader.h b/applications/main/nfc/views/detect_reader.h new file mode 100644 index 00000000000..6481216b4cf --- /dev/null +++ b/applications/main/nfc/views/detect_reader.h @@ -0,0 +1,36 @@ +#pragma once +#include +#include +#include + +typedef struct DetectReader DetectReader; + +typedef enum { + DetectReaderStateStart, + DetectReaderStateReaderDetected, + DetectReaderStateReaderLost, + DetectReaderStateDone, +} DetectReaderState; + +typedef void (*DetectReaderDoneCallback)(void* context); + +DetectReader* detect_reader_alloc(); + +void detect_reader_free(DetectReader* detect_reader); + +void detect_reader_reset(DetectReader* detect_reader); + +View* detect_reader_get_view(DetectReader* detect_reader); + +void detect_reader_set_callback( + DetectReader* detect_reader, + DetectReaderDoneCallback callback, + void* context); + +void detect_reader_set_nonces_max(DetectReader* detect_reader, uint16_t nonces_max); + +void detect_reader_set_nonces_collected(DetectReader* detect_reader, uint16_t nonces_collected); + +void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState state); + +void detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len); diff --git a/applications/main/nfc/views/dict_attack.c b/applications/main/nfc/views/dict_attack.c new file mode 100644 index 00000000000..b656c2dc5b5 --- /dev/null +++ b/applications/main/nfc/views/dict_attack.c @@ -0,0 +1,245 @@ +#include "dict_attack.h" + +#include + +#define NFC_CLASSIC_KEYS_PER_SECTOR 2 + +struct DictAttack { + View* view; + DictAttackCallback callback; + void* context; +}; + +typedef struct { + FuriString* header; + bool card_detected; + uint8_t sectors_total; + uint8_t sectors_read; + uint8_t current_sector; + uint8_t keys_found; + size_t dict_keys_total; + size_t dict_keys_current; + bool is_key_attack; + uint8_t key_attack_current_sector; +} DictAttackViewModel; + +static void dict_attack_draw_callback(Canvas* canvas, void* model) { + DictAttackViewModel* m = model; + if(!m->card_detected) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Lost the tag!"); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned( + canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly."); + } else { + char draw_str[32] = {}; + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 64, 0, AlignCenter, AlignTop, furi_string_get_cstr(m->header)); + if(m->is_key_attack) { + snprintf( + draw_str, + sizeof(draw_str), + "Reuse key check for sector: %d", + m->key_attack_current_sector); + } else { + snprintf(draw_str, sizeof(draw_str), "Unlocking sector: %d", m->current_sector); + } + canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str); + float dict_progress = m->dict_keys_total == 0 ? + 0 : + (float)(m->dict_keys_current) / (float)(m->dict_keys_total); + float progress = m->sectors_total == 0 ? 0 : + ((float)(m->current_sector) + dict_progress) / + (float)(m->sectors_total); + if(progress > 1.0f) { + progress = 1.0f; + } + if(m->dict_keys_current == 0) { + // Cause when people see 0 they think it's broken + snprintf(draw_str, sizeof(draw_str), "%d/%zu", 1, m->dict_keys_total); + } else { + snprintf( + draw_str, sizeof(draw_str), "%zu/%zu", m->dict_keys_current, m->dict_keys_total); + } + elements_progress_bar_with_text(canvas, 0, 20, 128, dict_progress, draw_str); + canvas_set_font(canvas, FontSecondary); + snprintf( + draw_str, + sizeof(draw_str), + "Keys found: %d/%d", + m->keys_found, + m->sectors_total * NFC_CLASSIC_KEYS_PER_SECTOR); + canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str); + snprintf( + draw_str, sizeof(draw_str), "Sectors Read: %d/%d", m->sectors_read, m->sectors_total); + canvas_draw_str_aligned(canvas, 0, 43, AlignLeft, AlignTop, draw_str); + } + elements_button_center(canvas, "Skip"); +} + +static bool dict_attack_input_callback(InputEvent* event, void* context) { + DictAttack* instance = context; + bool consumed = false; + + if(event->type == InputTypeShort && event->key == InputKeyOk) { + if(instance->callback) { + instance->callback(DictAttackEventSkipPressed, instance->context); + } + consumed = true; + } + + return consumed; +} + +DictAttack* dict_attack_alloc() { + DictAttack* instance = malloc(sizeof(DictAttack)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(DictAttackViewModel)); + view_set_draw_callback(instance->view, dict_attack_draw_callback); + view_set_input_callback(instance->view, dict_attack_input_callback); + view_set_context(instance->view, instance); + with_view_model( + instance->view, + DictAttackViewModel * model, + { model->header = furi_string_alloc(); }, + false); + + return instance; +} + +void dict_attack_free(DictAttack* instance) { + furi_assert(instance); + + with_view_model( + instance->view, DictAttackViewModel * model, { furi_string_free(model->header); }, false); + + view_free(instance->view); + free(instance); +} + +void dict_attack_reset(DictAttack* instance) { + furi_assert(instance); + + with_view_model( + instance->view, + DictAttackViewModel * model, + { + model->card_detected = false; + model->sectors_total = 0; + model->sectors_read = 0; + model->current_sector = 0; + model->keys_found = 0; + model->dict_keys_total = 0; + model->dict_keys_current = 0; + model->is_key_attack = false; + furi_string_reset(model->header); + }, + false); +} + +View* dict_attack_get_view(DictAttack* instance) { + furi_assert(instance); + + return instance->view; +} + +void dict_attack_set_callback(DictAttack* instance, DictAttackCallback callback, void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +void dict_attack_set_header(DictAttack* instance, const char* header) { + furi_assert(instance); + furi_assert(header); + + with_view_model( + instance->view, + DictAttackViewModel * model, + { furi_string_set(model->header, header); }, + true); +} + +void dict_attack_set_card_state(DictAttack* instance, bool detected) { + furi_assert(instance); + + with_view_model( + instance->view, DictAttackViewModel * model, { model->card_detected = detected; }, true); +} + +void dict_attack_set_sectors_total(DictAttack* instance, uint8_t sectors_total) { + furi_assert(instance); + + with_view_model( + instance->view, + DictAttackViewModel * model, + { model->sectors_total = sectors_total; }, + true); +} + +void dict_attack_set_sectors_read(DictAttack* instance, uint8_t sectors_read) { + furi_assert(instance); + + with_view_model( + instance->view, DictAttackViewModel * model, { model->sectors_read = sectors_read; }, true); +} + +void dict_attack_set_keys_found(DictAttack* instance, uint8_t keys_found) { + furi_assert(instance); + + with_view_model( + instance->view, DictAttackViewModel * model, { model->keys_found = keys_found; }, true); +} + +void dict_attack_set_current_sector(DictAttack* instance, uint8_t current_sector) { + furi_assert(instance); + + with_view_model( + instance->view, + DictAttackViewModel * model, + { model->current_sector = current_sector; }, + true); +} + +void dict_attack_set_total_dict_keys(DictAttack* instance, size_t dict_keys_total) { + furi_assert(instance); + + with_view_model( + instance->view, + DictAttackViewModel * model, + { model->dict_keys_total = dict_keys_total; }, + true); +} + +void dict_attack_set_current_dict_key(DictAttack* instance, size_t cur_key_num) { + furi_assert(instance); + + with_view_model( + instance->view, + DictAttackViewModel * model, + { model->dict_keys_current = cur_key_num; }, + true); +} + +void dict_attack_set_key_attack(DictAttack* instance, uint8_t sector) { + furi_assert(instance); + + with_view_model( + instance->view, + DictAttackViewModel * model, + { + model->is_key_attack = true; + model->key_attack_current_sector = sector; + }, + true); +} + +void dict_attack_reset_key_attack(DictAttack* instance) { + furi_assert(instance); + + with_view_model( + instance->view, DictAttackViewModel * model, { model->is_key_attack = false; }, true); +} diff --git a/applications/main/nfc/views/dict_attack.h b/applications/main/nfc/views/dict_attack.h new file mode 100644 index 00000000000..54a0220fe59 --- /dev/null +++ b/applications/main/nfc/views/dict_attack.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DictAttack DictAttack; + +typedef enum { + DictAttackEventSkipPressed, +} DictAttackEvent; + +typedef void (*DictAttackCallback)(DictAttackEvent event, void* context); + +DictAttack* dict_attack_alloc(); + +void dict_attack_free(DictAttack* instance); + +void dict_attack_reset(DictAttack* instance); + +View* dict_attack_get_view(DictAttack* instance); + +void dict_attack_set_callback(DictAttack* instance, DictAttackCallback callback, void* context); + +void dict_attack_set_header(DictAttack* instance, const char* header); + +void dict_attack_set_card_state(DictAttack* instance, bool detected); + +void dict_attack_set_sectors_total(DictAttack* instance, uint8_t sectors_total); + +void dict_attack_set_sectors_read(DictAttack* instance, uint8_t sectors_read); + +void dict_attack_set_keys_found(DictAttack* instance, uint8_t keys_found); + +void dict_attack_set_current_sector(DictAttack* instance, uint8_t curr_sec); + +void dict_attack_set_total_dict_keys(DictAttack* instance, size_t dict_keys_total); + +void dict_attack_set_current_dict_key(DictAttack* instance, size_t cur_key_num); + +void dict_attack_set_key_attack(DictAttack* instance, uint8_t sector); + +void dict_attack_reset_key_attack(DictAttack* instance); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/onewire/application.fam b/applications/main/onewire/application.fam new file mode 100644 index 00000000000..3d35abce948 --- /dev/null +++ b/applications/main/onewire/application.fam @@ -0,0 +1,6 @@ +App( + appid="onewire_start", + apptype=FlipperAppType.STARTUP, + entry_point="onewire_on_system_start", + order=60, +) diff --git a/applications/main/onewire/onewire_cli.c b/applications/main/onewire/onewire_cli.c new file mode 100644 index 00000000000..5f6cdc670bd --- /dev/null +++ b/applications/main/onewire/onewire_cli.c @@ -0,0 +1,72 @@ +#include +#include + +#include +#include + +#include + +static void onewire_cli(Cli* cli, FuriString* args, void* context); + +void onewire_on_system_start() { +#ifdef SRV_CLI + Cli* cli = furi_record_open(RECORD_CLI); + cli_add_command(cli, "onewire", CliCommandFlagDefault, onewire_cli, cli); + furi_record_close(RECORD_CLI); +#else + UNUSED(onewire_cli); +#endif +} + +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(&gpio_ibutton); + uint8_t address[8]; + bool done = false; + + printf("Search started\r\n"); + + onewire_host_start(onewire); + furi_hal_power_enable_otg(); + + while(!done) { + if(onewire_host_search(onewire, address, OneWireHostSearchModeNormal) != 1) { + printf("Search finished\r\n"); + onewire_host_reset_search(onewire); + done = true; + } else { + printf("Found: "); + for(uint8_t i = 0; i < 8; i++) { + printf("%02X", address[i]); + } + printf("\r\n"); + } + furi_delay_ms(100); + } + + furi_hal_power_disable_otg(); + onewire_host_free(onewire); +} + +void onewire_cli(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + FuriString* cmd; + cmd = furi_string_alloc(); + + if(!args_read_string_and_trim(args, cmd)) { + furi_string_free(cmd); + onewire_cli_print_usage(); + return; + } + + if(furi_string_cmp_str(cmd, "search") == 0) { + onewire_cli_search(cli); + } + + furi_string_free(cmd); +} diff --git a/applications/main/subghz/application.fam b/applications/main/subghz/application.fam new file mode 100644 index 00000000000..5f9f24dcd31 --- /dev/null +++ b/applications/main/subghz/application.fam @@ -0,0 +1,28 @@ +App( + appid="subghz", + name="Sub-GHz", + apptype=FlipperAppType.MENUEXTERNAL, + targets=["f7"], + entry_point="subghz_app", + icon="A_Sub1ghz_14", + stack_size=3 * 1024, + order=10, + sources=[ + "*.c", + "!subghz_cli.c", + "!helpers/subghz_chat.c", + ], + resources="resources", + fap_libs=["assets", "hwdrivers"], + fap_icon="icon.png", + fap_category="Sub-GHz", +) + +App( + appid="subghz_start", + targets=["f7"], + apptype=FlipperAppType.STARTUP, + entry_point="subghz_on_system_start", + sources=["subghz_cli.c", "helpers/subghz_chat.c"], + order=40, +) diff --git a/applications/subghz/helpers/subghz_chat.c b/applications/main/subghz/helpers/subghz_chat.c similarity index 90% rename from applications/subghz/helpers/subghz_chat.c rename to applications/main/subghz/helpers/subghz_chat.c index f821feaa9b2..bbe219fd2f7 100644 --- a/applications/subghz/helpers/subghz_chat.c +++ b/applications/main/subghz/helpers/subghz_chat.c @@ -59,11 +59,8 @@ SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli) { instance->cli = cli; - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "SubGhzChat"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, subghz_chat_worker_thread); + instance->thread = + furi_thread_alloc_ex("SubGhzChat", 2048, subghz_chat_worker_thread, instance); instance->subghz_txrx = subghz_tx_rx_worker_alloc(); instance->event_queue = furi_message_queue_alloc(80, sizeof(SubGhzChatEvent)); return instance; @@ -79,12 +76,15 @@ void subghz_chat_worker_free(SubGhzChatWorker* instance) { free(instance); } -bool subghz_chat_worker_start(SubGhzChatWorker* instance, uint32_t frequency) { +bool subghz_chat_worker_start( + SubGhzChatWorker* instance, + const SubGhzDevice* device, + uint32_t frequency) { furi_assert(instance); furi_assert(!instance->worker_running); bool res = false; - if(subghz_tx_rx_worker_start(instance->subghz_txrx, frequency)) { + if(subghz_tx_rx_worker_start(instance->subghz_txrx, device, frequency)) { furi_message_queue_reset(instance->event_queue); subghz_tx_rx_worker_set_callback_have_read( instance->subghz_txrx, subghz_chat_worker_update_rx_event_chat, instance); diff --git a/applications/subghz/helpers/subghz_chat.h b/applications/main/subghz/helpers/subghz_chat.h similarity index 86% rename from applications/subghz/helpers/subghz_chat.h rename to applications/main/subghz/helpers/subghz_chat.h index b418bbdbffc..2c454b75d98 100644 --- a/applications/subghz/helpers/subghz_chat.h +++ b/applications/main/subghz/helpers/subghz_chat.h @@ -1,5 +1,6 @@ #pragma once #include "../subghz_i.h" +#include #include typedef struct SubGhzChatWorker SubGhzChatWorker; @@ -20,7 +21,10 @@ typedef struct { SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli); void subghz_chat_worker_free(SubGhzChatWorker* instance); -bool subghz_chat_worker_start(SubGhzChatWorker* instance, uint32_t frequency); +bool subghz_chat_worker_start( + SubGhzChatWorker* instance, + const SubGhzDevice* device, + uint32_t frequency); void subghz_chat_worker_stop(SubGhzChatWorker* instance); bool subghz_chat_worker_is_running(SubGhzChatWorker* instance); SubGhzChatEvent subghz_chat_worker_get_event_chat(SubGhzChatWorker* instance); diff --git a/applications/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h similarity index 96% rename from applications/subghz/helpers/subghz_custom_event.h rename to applications/main/subghz/helpers/subghz_custom_event.h index 765c9e251c5..285b4a60f91 100644 --- a/applications/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -6,14 +6,13 @@ typedef enum { SubGhzCustomEventManagerSetRAW, //SubmenuIndex - SubmenuIndexPricenton, + SubmenuIndexPricenton_433, + SubmenuIndexPricenton_315, SubmenuIndexNiceFlo12bit, SubmenuIndexNiceFlo24bit, SubmenuIndexCAME12bit, SubmenuIndexCAME24bit, SubmenuIndexCAMETwee, - SubmenuIndexNeroSketch, - SubmenuIndexNeroRadio, SubmenuIndexGateTX, SubmenuIndexDoorHan_315_00, SubmenuIndexDoorHan_433_92, diff --git a/applications/main/subghz/helpers/subghz_error_type.h b/applications/main/subghz/helpers/subghz_error_type.h new file mode 100644 index 00000000000..0f86d6ea7d1 --- /dev/null +++ b/applications/main/subghz/helpers/subghz_error_type.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +/** SubGhzErrorType */ +typedef enum { + SubGhzErrorTypeNoError = 0, /** There are no errors */ + SubGhzErrorTypeParseFile = + 1, /** File parsing error, or wrong file structure, or missing required parameters. more accurate data can be obtained through the debug port */ + SubGhzErrorTypeOnlyRX = + 2, /** Transmission on this frequency is blocked by regional settings */ + SubGhzErrorTypeParserOthers = 3, /** Error in protocol parameters description */ +} SubGhzErrorType; diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.c new file mode 100644 index 00000000000..9b33e92d186 --- /dev/null +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.c @@ -0,0 +1,27 @@ +#include "subghz_frequency_analyzer_log_item_array.h" + +const char* + subghz_frequency_analyzer_log_get_order_name(SubGhzFrequencyAnalyzerLogOrderBy order_by) { + if(order_by == SubGhzFrequencyAnalyzerLogOrderBySeqAsc) { + return "Seq. A"; + } + if(order_by == SubGhzFrequencyAnalyzerLogOrderByCountDesc) { + return "Count D"; + } + if(order_by == SubGhzFrequencyAnalyzerLogOrderByCountAsc) { + return "Count A"; + } + if(order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIDesc) { + return "RSSI D"; + } + if(order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIAsc) { + return "RSSI A"; + } + if(order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyDesc) { + return "Freq. D"; + } + if(order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc) { + return "Freq. A"; + } + return "Seq. D"; +} diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h new file mode 100644 index 00000000000..2fa70284a42 --- /dev/null +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include +#include + +typedef enum { + SubGhzFrequencyAnalyzerLogOrderBySeqDesc, + SubGhzFrequencyAnalyzerLogOrderBySeqAsc, + SubGhzFrequencyAnalyzerLogOrderByCountDesc, + SubGhzFrequencyAnalyzerLogOrderByCountAsc, + SubGhzFrequencyAnalyzerLogOrderByRSSIDesc, + SubGhzFrequencyAnalyzerLogOrderByRSSIAsc, + SubGhzFrequencyAnalyzerLogOrderByFrequencyDesc, + SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc, +} SubGhzFrequencyAnalyzerLogOrderBy; + +const char* + subghz_frequency_analyzer_log_get_order_name(SubGhzFrequencyAnalyzerLogOrderBy order_by); + +TUPLE_DEF2( //-V1048 + SubGhzFrequencyAnalyzerLogItem, + (seq, uint8_t), + (frequency, uint32_t), + (count, uint8_t), + (rssi_max, uint8_t)) +/* Register globally the oplist */ +#define M_OPL_SubGhzFrequencyAnalyzerLogItem_t() \ + TUPLE_OPLIST( \ + SubGhzFrequencyAnalyzerLogItem, \ + M_DEFAULT_OPLIST, \ + M_DEFAULT_OPLIST, \ + M_DEFAULT_OPLIST, \ + M_DEFAULT_OPLIST) + +/* Define the array, register the oplist and define further algorithms on it */ +ARRAY_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItem_t) //-V779 +#define M_OPL_SubGhzFrequencyAnalyzerLogItemArray_t() \ + ARRAY_OPLIST(SubGhzFrequencyAnalyzerLogItemArray, M_OPL_SubGhzFrequencyAnalyzerLogItem_t()) +ALGO_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItemArray_t) + +FUNC_OBJ_INS_DEF( + SubGhzFrequencyAnalyzerLogItemArray_compare_by /* name of the instance */, + SubGhzFrequencyAnalyzerLogItemArray_cmp_obj /* name of the interface */, + (a, + b) /* name of the input parameters of the function like object. The type are inherited from the interface. */ + , + { + /* code of the function object */ + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc) { + return a->frequency < b->frequency ? -1 : a->frequency > b->frequency; + } + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyDesc) { + return a->frequency > b->frequency ? -1 : a->frequency < b->frequency; + } + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIAsc) { + return a->rssi_max < b->rssi_max ? -1 : a->rssi_max > b->rssi_max; + } + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIDesc) { + return a->rssi_max > b->rssi_max ? -1 : a->rssi_max < b->rssi_max; + } + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByCountAsc) { + return a->count < b->count ? -1 : a->count > b->count; + } + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByCountDesc) { + return a->count > b->count ? -1 : a->count < b->count; + } + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderBySeqAsc) { + return a->seq < b->seq ? -1 : a->seq > b->seq; + } + + return a->seq > b->seq ? -1 : a->seq < b->seq; + }, + /* Additional fields stored in the function object */ + (order_by, SubGhzFrequencyAnalyzerLogOrderBy)) +#define M_OPL_SubGhzFrequencyAnalyzerLogItemArray_compare_by_t() \ + FUNC_OBJ_INS_OPLIST(SubGhzFrequencyAnalyzerLogItemArray_compare_by, M_DEFAULT_OPLIST) diff --git a/applications/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c similarity index 87% rename from applications/subghz/helpers/subghz_frequency_analyzer_worker.c rename to applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c index 10c5a9ea81a..4a4445faad8 100644 --- a/applications/subghz/helpers/subghz_frequency_analyzer_worker.c +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c @@ -2,11 +2,10 @@ #include #include +#include #define TAG "SubghzFrequencyAnalyzerWorker" -#define SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD -95.0f - static const uint8_t subghz_preset_ook_58khz[][2] = { {CC1101_MDMCFG4, 0b11110111}, // Rx BW filter is 58.035714kHz /* End */ @@ -71,6 +70,8 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { .frequency_coarse = 0, .rssi_coarse = 0, .frequency_fine = 0, .rssi_fine = 0}; float rssi = 0; uint32_t frequency = 0; + float rssi_temp = -127.0f; + uint32_t frequency_temp = 0; CC1101Status status; //Start CC1101 @@ -147,7 +148,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { FURI_LOG_T( TAG, - "RSSI: avg %f, max %f at %u, min %f", + "RSSI: avg %f, max %f at %lu, min %f", (double)(rssi_avg / rssi_avg_samples), (double)frequency_rssi.rssi_coarse, frequency_rssi.frequency_coarse, @@ -178,7 +179,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { rssi = furi_hal_subghz_get_rssi(); - FURI_LOG_T(TAG, "#:%u:%f", frequency, (double)rssi); + FURI_LOG_T(TAG, "#:%lu:%f", frequency, (double)rssi); if(frequency_rssi.rssi_fine < rssi) { frequency_rssi.rssi_fine = rssi; @@ -191,10 +192,13 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { // Deliver results fine if(frequency_rssi.rssi_fine > SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) { FURI_LOG_D( - TAG, "=:%u:%f", frequency_rssi.frequency_fine, (double)frequency_rssi.rssi_fine); + TAG, "=:%lu:%f", frequency_rssi.frequency_fine, (double)frequency_rssi.rssi_fine); instance->sample_hold_counter = 20; - if(instance->filVal) { + rssi_temp = (rssi_temp + frequency_rssi.rssi_fine) / 2; + frequency_temp = frequency_rssi.frequency_fine; + + if(!float_is_equal(instance->filVal, 0.f)) { frequency_rssi.frequency_fine = subghz_frequency_analyzer_worker_expRunningAverageAdaptive( instance, frequency_rssi.frequency_fine); @@ -202,19 +206,21 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { // Deliver callback if(instance->pair_callback) { instance->pair_callback( - instance->context, frequency_rssi.frequency_fine, frequency_rssi.rssi_fine); + instance->context, frequency_rssi.frequency_fine, rssi_temp, true); } } else if( // Deliver results coarse (frequency_rssi.rssi_coarse > SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) && (instance->sample_hold_counter < 10)) { FURI_LOG_D( TAG, - "~:%u:%f", + "~:%lu:%f", frequency_rssi.frequency_coarse, (double)frequency_rssi.rssi_coarse); instance->sample_hold_counter = 20; - if(instance->filVal) { + rssi_temp = (rssi_temp + frequency_rssi.rssi_coarse) / 2; + frequency_temp = frequency_rssi.frequency_coarse; + if(!float_is_equal(instance->filVal, 0.f)) { frequency_rssi.frequency_coarse = subghz_frequency_analyzer_worker_expRunningAverageAdaptive( instance, frequency_rssi.frequency_coarse); @@ -222,16 +228,21 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { // Deliver callback if(instance->pair_callback) { instance->pair_callback( - instance->context, - frequency_rssi.frequency_coarse, - frequency_rssi.rssi_coarse); + instance->context, frequency_rssi.frequency_coarse, rssi_temp, true); } } else { if(instance->sample_hold_counter > 0) { instance->sample_hold_counter--; + if(instance->sample_hold_counter == 15) { + if(instance->pair_callback) { + instance->pair_callback( + instance->context, frequency_temp, rssi_temp, false); + } + } } else { instance->filVal = 0; - if(instance->pair_callback) instance->pair_callback(instance->context, 0, 0); + rssi_temp = -127.0f; + instance->pair_callback(instance->context, 0, 0, false); } } } @@ -247,14 +258,10 @@ SubGhzFrequencyAnalyzerWorker* subghz_frequency_analyzer_worker_alloc(void* cont furi_assert(context); SubGhzFrequencyAnalyzerWorker* instance = malloc(sizeof(SubGhzFrequencyAnalyzerWorker)); - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "SubGhzFAWorker"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, subghz_frequency_analyzer_worker_thread); - + instance->thread = furi_thread_alloc_ex( + "SubGhzFAWorker", 2048, subghz_frequency_analyzer_worker_thread, instance); SubGhz* subghz = context; - instance->setting = subghz->setting; + instance->setting = subghz_txrx_get_setting(subghz->txrx); return instance; } diff --git a/applications/subghz/helpers/subghz_frequency_analyzer_worker.h b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.h similarity index 89% rename from applications/subghz/helpers/subghz_frequency_analyzer_worker.h rename to applications/main/subghz/helpers/subghz_frequency_analyzer_worker.h index 50687c76da2..ed5bd9644fe 100644 --- a/applications/subghz/helpers/subghz_frequency_analyzer_worker.h +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.h @@ -3,10 +3,15 @@ #include #include "../subghz_i.h" +#define SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD -93.0f + typedef struct SubGhzFrequencyAnalyzerWorker SubGhzFrequencyAnalyzerWorker; -typedef void ( - *SubGhzFrequencyAnalyzerWorkerPairCallback)(void* context, uint32_t frequency, float rssi); +typedef void (*SubGhzFrequencyAnalyzerWorkerPairCallback)( + void* context, + uint32_t frequency, + float rssi, + bool signal); typedef struct { uint32_t frequency_coarse; diff --git a/applications/main/subghz/helpers/subghz_threshold_rssi.c b/applications/main/subghz/helpers/subghz_threshold_rssi.c new file mode 100644 index 00000000000..07d7bccf93d --- /dev/null +++ b/applications/main/subghz/helpers/subghz_threshold_rssi.c @@ -0,0 +1,59 @@ +#include "subghz_threshold_rssi.h" +#include +#include "../subghz_i.h" + +#define TAG "SubGhzThresholdRssi" +#define THRESHOLD_RSSI_LOW_COUNT 10 + +struct SubGhzThresholdRssi { + float threshold_rssi; + uint8_t threshold_rssi_low_count; +}; + +SubGhzThresholdRssi* subghz_threshold_rssi_alloc(void) { + SubGhzThresholdRssi* instance = malloc(sizeof(SubGhzThresholdRssi)); + instance->threshold_rssi = SUBGHZ_RAW_THRESHOLD_MIN; + instance->threshold_rssi_low_count = THRESHOLD_RSSI_LOW_COUNT; + return instance; +} + +void subghz_threshold_rssi_free(SubGhzThresholdRssi* instance) { + furi_assert(instance); + free(instance); +} + +void subghz_threshold_rssi_set(SubGhzThresholdRssi* instance, float rssi) { + furi_assert(instance); + instance->threshold_rssi = rssi; +} + +float subghz_threshold_rssi_get(SubGhzThresholdRssi* instance) { + furi_assert(instance); + return instance->threshold_rssi; +} + +SubGhzThresholdRssiData subghz_threshold_get_rssi_data(SubGhzThresholdRssi* instance, float rssi) { + furi_assert(instance); + SubGhzThresholdRssiData ret = {.rssi = rssi, .is_above = false}; + + if(float_is_equal(instance->threshold_rssi, SUBGHZ_RAW_THRESHOLD_MIN)) { + ret.is_above = true; + } else { + if(rssi < instance->threshold_rssi) { + instance->threshold_rssi_low_count++; + if(instance->threshold_rssi_low_count > THRESHOLD_RSSI_LOW_COUNT) { + instance->threshold_rssi_low_count = THRESHOLD_RSSI_LOW_COUNT; + } + ret.is_above = false; + } else { + instance->threshold_rssi_low_count = 0; + } + + if(instance->threshold_rssi_low_count == THRESHOLD_RSSI_LOW_COUNT) { + ret.is_above = false; + } else { + ret.is_above = true; + } + } + return ret; +} diff --git a/applications/main/subghz/helpers/subghz_threshold_rssi.h b/applications/main/subghz/helpers/subghz_threshold_rssi.h new file mode 100644 index 00000000000..1d588e271b3 --- /dev/null +++ b/applications/main/subghz/helpers/subghz_threshold_rssi.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +typedef struct { + float rssi; /**< Current RSSI */ + bool is_above; /**< Exceeded threshold level */ +} SubGhzThresholdRssiData; + +typedef struct SubGhzThresholdRssi SubGhzThresholdRssi; + +/** Allocate SubGhzThresholdRssi + * + * @return SubGhzThresholdRssi* + */ +SubGhzThresholdRssi* subghz_threshold_rssi_alloc(void); + +/** Free SubGhzThresholdRssi + * + * @param instance Pointer to a SubGhzThresholdRssi + */ +void subghz_threshold_rssi_free(SubGhzThresholdRssi* instance); + +/** Set threshold + * + * @param instance Pointer to a SubGhzThresholdRssi + * @param rssi RSSI threshold + */ +void subghz_threshold_rssi_set(SubGhzThresholdRssi* instance, float rssi); + +/** Get threshold + * + * @param instance Pointer to a SubGhzThresholdRssi + * @return float RSSI threshold + */ +float subghz_threshold_rssi_get(SubGhzThresholdRssi* instance); + +/** Check threshold + * + * @param instance Pointer to a SubGhzThresholdRssi + * @param rssi Current RSSI + * @return SubGhzThresholdRssiData + */ +SubGhzThresholdRssiData subghz_threshold_get_rssi_data(SubGhzThresholdRssi* instance, float rssi); diff --git a/applications/main/subghz/helpers/subghz_txrx.c b/applications/main/subghz/helpers/subghz_txrx.c new file mode 100644 index 00000000000..e3e06695173 --- /dev/null +++ b/applications/main/subghz/helpers/subghz_txrx.c @@ -0,0 +1,612 @@ +#include "subghz_txrx_i.h" + +#include +#include +#include + +#define TAG "SubGhz" + +static void subghz_txrx_radio_device_power_on(SubGhzTxRx* instance) { + UNUSED(instance); + uint8_t attempts = 5; + while(--attempts > 0) { + if(furi_hal_power_enable_otg()) break; + } + if(attempts == 0) { + if(furi_hal_power_get_usb_voltage() < 4.5f) { + FURI_LOG_E( + TAG, + "Error power otg enable. BQ2589 check otg fault = %d", + furi_hal_power_check_otg_fault() ? 1 : 0); + } + } +} + +static void subghz_txrx_radio_device_power_off(SubGhzTxRx* instance) { + UNUSED(instance); + if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); +} + +SubGhzTxRx* subghz_txrx_alloc() { + SubGhzTxRx* instance = malloc(sizeof(SubGhzTxRx)); + instance->setting = subghz_setting_alloc(); + subghz_setting_load(instance->setting, EXT_PATH("subghz/assets/setting_user")); + + instance->preset = malloc(sizeof(SubGhzRadioPreset)); + instance->preset->name = furi_string_alloc(); + subghz_txrx_set_preset( + instance, "AM650", subghz_setting_get_default_frequency(instance->setting), NULL, 0); + + instance->txrx_state = SubGhzTxRxStateSleep; + + subghz_txrx_hopper_set_state(instance, SubGhzHopperStateOFF); + subghz_txrx_speaker_set_state(instance, SubGhzSpeakerStateDisable); + + instance->worker = subghz_worker_alloc(); + instance->fff_data = flipper_format_string_alloc(); + + instance->environment = subghz_environment_alloc(); + instance->is_database_loaded = + subghz_environment_load_keystore(instance->environment, SUBGHZ_KEYSTORE_DIR_NAME); + subghz_environment_load_keystore(instance->environment, SUBGHZ_KEYSTORE_DIR_USER_NAME); + subghz_environment_set_came_atomo_rainbow_table_file_name( + instance->environment, SUBGHZ_CAME_ATOMO_DIR_NAME); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + instance->environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); + subghz_environment_set_nice_flor_s_rainbow_table_file_name( + instance->environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); + subghz_environment_set_protocol_registry( + instance->environment, (void*)&subghz_protocol_registry); + instance->receiver = subghz_receiver_alloc_init(instance->environment); + + subghz_worker_set_overrun_callback( + instance->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset); + subghz_worker_set_pair_callback( + instance->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode); + subghz_worker_set_context(instance->worker, instance->receiver); + + //set default device External + subghz_devices_init(); + instance->radio_device_type = SubGhzRadioDeviceTypeInternal; + instance->radio_device_type = + subghz_txrx_radio_device_set(instance, SubGhzRadioDeviceTypeExternalCC1101); + + return instance; +} + +void subghz_txrx_free(SubGhzTxRx* instance) { + furi_assert(instance); + + if(instance->radio_device_type != SubGhzRadioDeviceTypeInternal) { + subghz_txrx_radio_device_power_off(instance); + subghz_devices_end(instance->radio_device); + } + + subghz_devices_deinit(); + + subghz_worker_free(instance->worker); + subghz_receiver_free(instance->receiver); + subghz_environment_free(instance->environment); + flipper_format_free(instance->fff_data); + furi_string_free(instance->preset->name); + subghz_setting_free(instance->setting); + + free(instance->preset); + free(instance); +} + +bool subghz_txrx_is_database_loaded(SubGhzTxRx* instance) { + furi_assert(instance); + return instance->is_database_loaded; +} + +void subghz_txrx_set_preset( + SubGhzTxRx* instance, + const char* preset_name, + uint32_t frequency, + uint8_t* preset_data, + size_t preset_data_size) { + furi_assert(instance); + furi_string_set(instance->preset->name, preset_name); + SubGhzRadioPreset* preset = instance->preset; + preset->frequency = frequency; + preset->data = preset_data; + preset->data_size = preset_data_size; +} + +const char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset) { + UNUSED(instance); + const char* preset_name = ""; + if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) { + preset_name = "AM270"; + } else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) { + preset_name = "AM650"; + } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) { + preset_name = "FM238"; + } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) { + preset_name = "FM476"; + } else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) { + preset_name = "CUSTOM"; + } else { + FURI_LOG_E(TAG, "Unknown preset"); + } + return preset_name; +} + +SubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance) { + furi_assert(instance); + return *instance->preset; +} + +void subghz_txrx_get_frequency_and_modulation( + SubGhzTxRx* instance, + FuriString* frequency, + FuriString* modulation) { + furi_assert(instance); + SubGhzRadioPreset* preset = instance->preset; + if(frequency != NULL) { + furi_string_printf( + frequency, + "%03ld.%02ld", + preset->frequency / 1000000 % 1000, + preset->frequency / 10000 % 100); + } + if(modulation != NULL) { + furi_string_printf(modulation, "%.2s", furi_string_get_cstr(preset->name)); + } +} + +static void subghz_txrx_begin(SubGhzTxRx* instance, uint8_t* preset_data) { + furi_assert(instance); + subghz_devices_reset(instance->radio_device); + subghz_devices_idle(instance->radio_device); + subghz_devices_load_preset(instance->radio_device, FuriHalSubGhzPresetCustom, preset_data); + instance->txrx_state = SubGhzTxRxStateIDLE; +} + +static uint32_t subghz_txrx_rx(SubGhzTxRx* instance, uint32_t frequency) { + furi_assert(instance); + + furi_assert( + instance->txrx_state != SubGhzTxRxStateRx && instance->txrx_state != SubGhzTxRxStateSleep); + + subghz_devices_idle(instance->radio_device); + + uint32_t value = subghz_devices_set_frequency(instance->radio_device, frequency); + subghz_devices_flush_rx(instance->radio_device); + subghz_txrx_speaker_on(instance); + + subghz_devices_start_async_rx( + instance->radio_device, subghz_worker_rx_callback, instance->worker); + subghz_worker_start(instance->worker); + instance->txrx_state = SubGhzTxRxStateRx; + return value; +} + +static void subghz_txrx_idle(SubGhzTxRx* instance) { + furi_assert(instance); + if(instance->txrx_state != SubGhzTxRxStateSleep) { + subghz_devices_idle(instance->radio_device); + subghz_txrx_speaker_off(instance); + instance->txrx_state = SubGhzTxRxStateIDLE; + } +} + +static void subghz_txrx_rx_end(SubGhzTxRx* instance) { + furi_assert(instance); + furi_assert(instance->txrx_state == SubGhzTxRxStateRx); + + if(subghz_worker_is_running(instance->worker)) { + subghz_worker_stop(instance->worker); + subghz_devices_stop_async_rx(instance->radio_device); + } + subghz_devices_idle(instance->radio_device); + subghz_txrx_speaker_off(instance); + instance->txrx_state = SubGhzTxRxStateIDLE; +} + +void subghz_txrx_sleep(SubGhzTxRx* instance) { + furi_assert(instance); + subghz_devices_sleep(instance->radio_device); + instance->txrx_state = SubGhzTxRxStateSleep; +} + +static bool subghz_txrx_tx(SubGhzTxRx* instance, uint32_t frequency) { + furi_assert(instance); + furi_assert(instance->txrx_state != SubGhzTxRxStateSleep); + subghz_devices_idle(instance->radio_device); + subghz_devices_set_frequency(instance->radio_device, frequency); + + bool ret = subghz_devices_set_tx(instance->radio_device); + if(ret) { + subghz_txrx_speaker_on(instance); + instance->txrx_state = SubGhzTxRxStateTx; + } + + return ret; +} + +SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format) { + furi_assert(instance); + furi_assert(flipper_format); + + subghz_txrx_stop(instance); + + SubGhzTxRxStartTxState ret = SubGhzTxRxStartTxStateErrorParserOthers; + FuriString* temp_str = furi_string_alloc(); + uint32_t repeat = 200; + do { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) { + FURI_LOG_E(TAG, "Missing Protocol"); + break; + } + if(!flipper_format_insert_or_update_uint32(flipper_format, "Repeat", &repeat, 1)) { + FURI_LOG_E(TAG, "Unable Repeat"); + break; + } + ret = SubGhzTxRxStartTxStateOk; + + SubGhzRadioPreset* preset = instance->preset; + instance->transmitter = + subghz_transmitter_alloc_init(instance->environment, furi_string_get_cstr(temp_str)); + + if(instance->transmitter) { + if(subghz_transmitter_deserialize(instance->transmitter, flipper_format) == + SubGhzProtocolStatusOk) { + if(strcmp(furi_string_get_cstr(preset->name), "") != 0) { + subghz_txrx_begin( + instance, + subghz_setting_get_preset_data_by_name( + instance->setting, furi_string_get_cstr(preset->name))); + if(preset->frequency) { + if(!subghz_txrx_tx(instance, preset->frequency)) { + FURI_LOG_E(TAG, "Only Rx"); + ret = SubGhzTxRxStartTxStateErrorOnlyRx; + } + } else { + ret = SubGhzTxRxStartTxStateErrorParserOthers; + } + + } else { + FURI_LOG_E( + TAG, "Unknown name preset \" %s \"", furi_string_get_cstr(preset->name)); + ret = SubGhzTxRxStartTxStateErrorParserOthers; + } + + if(ret == SubGhzTxRxStartTxStateOk) { + //Start TX + subghz_devices_start_async_tx( + instance->radio_device, subghz_transmitter_yield, instance->transmitter); + } + } else { + ret = SubGhzTxRxStartTxStateErrorParserOthers; + } + } else { + ret = SubGhzTxRxStartTxStateErrorParserOthers; + } + if(ret != SubGhzTxRxStartTxStateOk) { + subghz_transmitter_free(instance->transmitter); + if(instance->txrx_state != SubGhzTxRxStateIDLE) { + subghz_txrx_idle(instance); + } + } + + } while(false); + furi_string_free(temp_str); + return ret; +} + +void subghz_txrx_rx_start(SubGhzTxRx* instance) { + furi_assert(instance); + subghz_txrx_stop(instance); + subghz_txrx_begin( + instance, + subghz_setting_get_preset_data_by_name( + subghz_txrx_get_setting(instance), furi_string_get_cstr(instance->preset->name))); + subghz_txrx_rx(instance, instance->preset->frequency); +} + +void subghz_txrx_set_need_save_callback( + SubGhzTxRx* instance, + SubGhzTxRxNeedSaveCallback callback, + void* context) { + furi_assert(instance); + instance->need_save_callback = callback; + instance->need_save_context = context; +} + +static void subghz_txrx_tx_stop(SubGhzTxRx* instance) { + furi_assert(instance); + furi_assert(instance->txrx_state == SubGhzTxRxStateTx); + //Stop TX + subghz_devices_stop_async_tx(instance->radio_device); + subghz_transmitter_stop(instance->transmitter); + subghz_transmitter_free(instance->transmitter); + + //if protocol dynamic then we save the last upload + if(instance->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) { + if(instance->need_save_callback) { + instance->need_save_callback(instance->need_save_context); + } + } + subghz_txrx_idle(instance); + subghz_txrx_speaker_off(instance); +} + +FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance) { + furi_assert(instance); + return instance->fff_data; +} + +SubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance) { + furi_assert(instance); + return instance->setting; +} + +void subghz_txrx_stop(SubGhzTxRx* instance) { + furi_assert(instance); + + switch(instance->txrx_state) { + case SubGhzTxRxStateTx: + subghz_txrx_tx_stop(instance); + subghz_txrx_speaker_unmute(instance); + break; + case SubGhzTxRxStateRx: + subghz_txrx_rx_end(instance); + subghz_txrx_speaker_mute(instance); + break; + + default: + break; + } +} + +void subghz_txrx_hopper_update(SubGhzTxRx* instance) { + furi_assert(instance); + + switch(instance->hopper_state) { + case SubGhzHopperStateOFF: + case SubGhzHopperStatePause: + return; + case SubGhzHopperStateRSSITimeOut: + if(instance->hopper_timeout != 0) { + instance->hopper_timeout--; + return; + } + break; + default: + break; + } + float rssi = -127.0f; + if(instance->hopper_state != SubGhzHopperStateRSSITimeOut) { + // See RSSI Calculation timings in CC1101 17.3 RSSI + rssi = subghz_devices_get_rssi(instance->radio_device); + + // Stay if RSSI is high enough + if(rssi > -90.0f) { + instance->hopper_timeout = 10; + instance->hopper_state = SubGhzHopperStateRSSITimeOut; + return; + } + } else { + instance->hopper_state = SubGhzHopperStateRunnig; + } + // Select next frequency + if(instance->hopper_idx_frequency < + subghz_setting_get_hopper_frequency_count(instance->setting) - 1) { + instance->hopper_idx_frequency++; + } else { + instance->hopper_idx_frequency = 0; + } + + if(instance->txrx_state == SubGhzTxRxStateRx) { + subghz_txrx_rx_end(instance); + }; + if(instance->txrx_state == SubGhzTxRxStateIDLE) { + subghz_receiver_reset(instance->receiver); + instance->preset->frequency = + subghz_setting_get_hopper_frequency(instance->setting, instance->hopper_idx_frequency); + subghz_txrx_rx(instance, instance->preset->frequency); + } +} + +SubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance) { + furi_assert(instance); + return instance->hopper_state; +} + +void subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state) { + furi_assert(instance); + instance->hopper_state = state; +} + +void subghz_txrx_hopper_unpause(SubGhzTxRx* instance) { + furi_assert(instance); + if(instance->hopper_state == SubGhzHopperStatePause) { + instance->hopper_state = SubGhzHopperStateRunnig; + } +} + +void subghz_txrx_hopper_pause(SubGhzTxRx* instance) { + furi_assert(instance); + if(instance->hopper_state == SubGhzHopperStateRunnig) { + instance->hopper_state = SubGhzHopperStatePause; + } +} + +void subghz_txrx_speaker_on(SubGhzTxRx* instance) { + furi_assert(instance); + if(instance->speaker_state == SubGhzSpeakerStateEnable) { + if(furi_hal_speaker_acquire(30)) { + subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_speaker); + } else { + instance->speaker_state = SubGhzSpeakerStateDisable; + } + } +} + +void subghz_txrx_speaker_off(SubGhzTxRx* instance) { + furi_assert(instance); + if(instance->speaker_state != SubGhzSpeakerStateDisable) { + if(furi_hal_speaker_is_mine()) { + subghz_devices_set_async_mirror_pin(instance->radio_device, NULL); + furi_hal_speaker_release(); + if(instance->speaker_state == SubGhzSpeakerStateShutdown) + instance->speaker_state = SubGhzSpeakerStateDisable; + } + } +} + +void subghz_txrx_speaker_mute(SubGhzTxRx* instance) { + furi_assert(instance); + if(instance->speaker_state == SubGhzSpeakerStateEnable) { + if(furi_hal_speaker_is_mine()) { + subghz_devices_set_async_mirror_pin(instance->radio_device, NULL); + } + } +} + +void subghz_txrx_speaker_unmute(SubGhzTxRx* instance) { + furi_assert(instance); + if(instance->speaker_state == SubGhzSpeakerStateEnable) { + if(furi_hal_speaker_is_mine()) { + subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_speaker); + } + } +} + +void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state) { + furi_assert(instance); + instance->speaker_state = state; +} + +SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance) { + furi_assert(instance); + return instance->speaker_state; +} + +bool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol) { + furi_assert(instance); + furi_assert(name_protocol); + bool res = false; + instance->decoder_result = + subghz_receiver_search_decoder_base_by_name(instance->receiver, name_protocol); + if(instance->decoder_result) { + res = true; + } + return res; +} + +SubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance) { + furi_assert(instance); + return instance->decoder_result; +} + +bool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance) { + furi_assert(instance); + return ( + (instance->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) == + SubGhzProtocolFlag_Save); +} + +bool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type) { + furi_assert(instance); + const SubGhzProtocol* protocol = instance->decoder_result->protocol; + if(check_type) { + return ( + ((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) && + protocol->encoder->deserialize && protocol->type == SubGhzProtocolTypeStatic); + } + return ( + ((protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) && + protocol->encoder->deserialize); +} + +void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter) { + furi_assert(instance); + subghz_receiver_set_filter(instance->receiver, filter); +} + +void subghz_txrx_set_rx_calback( + SubGhzTxRx* instance, + SubGhzReceiverCallback callback, + void* context) { + subghz_receiver_set_rx_callback(instance->receiver, callback, context); +} + +void subghz_txrx_set_raw_file_encoder_worker_callback_end( + SubGhzTxRx* instance, + SubGhzProtocolEncoderRAWCallbackEnd callback, + void* context) { + subghz_protocol_raw_file_encoder_worker_set_callback_end( + (SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance(instance->transmitter), + callback, + context); +} + +bool subghz_txrx_radio_device_is_external_connected(SubGhzTxRx* instance, const char* name) { + furi_assert(instance); + + bool is_connect = false; + bool is_otg_enabled = furi_hal_power_is_otg_enabled(); + + if(!is_otg_enabled) { + subghz_txrx_radio_device_power_on(instance); + } + + const SubGhzDevice* device = subghz_devices_get_by_name(name); + if(device) { + is_connect = subghz_devices_is_connect(device); + } + + if(!is_otg_enabled) { + subghz_txrx_radio_device_power_off(instance); + } + return is_connect; +} + +SubGhzRadioDeviceType + subghz_txrx_radio_device_set(SubGhzTxRx* instance, SubGhzRadioDeviceType radio_device_type) { + furi_assert(instance); + + if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 && + subghz_txrx_radio_device_is_external_connected(instance, SUBGHZ_DEVICE_CC1101_EXT_NAME)) { + subghz_txrx_radio_device_power_on(instance); + instance->radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME); + subghz_devices_begin(instance->radio_device); + instance->radio_device_type = SubGhzRadioDeviceTypeExternalCC1101; + } else { + subghz_txrx_radio_device_power_off(instance); + if(instance->radio_device_type != SubGhzRadioDeviceTypeInternal) { + subghz_devices_end(instance->radio_device); + } + instance->radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME); + instance->radio_device_type = SubGhzRadioDeviceTypeInternal; + } + + return instance->radio_device_type; +} + +SubGhzRadioDeviceType subghz_txrx_radio_device_get(SubGhzTxRx* instance) { + furi_assert(instance); + return instance->radio_device_type; +} + +float subghz_txrx_radio_device_get_rssi(SubGhzTxRx* instance) { + furi_assert(instance); + return subghz_devices_get_rssi(instance->radio_device); +} + +const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance) { + furi_assert(instance); + return subghz_devices_get_name(instance->radio_device); +} + +bool subghz_txrx_radio_device_is_frequecy_valid(SubGhzTxRx* instance, uint32_t frequency) { + furi_assert(instance); + return subghz_devices_is_frequency_valid(instance->radio_device, frequency); +} \ No newline at end of file diff --git a/applications/main/subghz/helpers/subghz_txrx.h b/applications/main/subghz/helpers/subghz_txrx.h new file mode 100644 index 00000000000..e49789206f0 --- /dev/null +++ b/applications/main/subghz/helpers/subghz_txrx.h @@ -0,0 +1,336 @@ +#pragma once + +#include "subghz_types.h" + +#include +#include +#include +#include +#include +#include + +typedef struct SubGhzTxRx SubGhzTxRx; + +typedef void (*SubGhzTxRxNeedSaveCallback)(void* context); + +typedef enum { + SubGhzTxRxStartTxStateOk, + SubGhzTxRxStartTxStateErrorOnlyRx, + SubGhzTxRxStartTxStateErrorParserOthers, +} SubGhzTxRxStartTxState; + +/** + * Allocate SubGhzTxRx + * + * @return SubGhzTxRx* pointer to SubGhzTxRx + */ +SubGhzTxRx* subghz_txrx_alloc(); + +/** + * Free SubGhzTxRx + * + * @param instance Pointer to a SubGhzTxRx + */ +void subghz_txrx_free(SubGhzTxRx* instance); + +/** + * Check if the database is loaded + * + * @param instance Pointer to a SubGhzTxRx + * @return bool True if the database is loaded + */ +bool subghz_txrx_is_database_loaded(SubGhzTxRx* instance); + +/** + * Set preset + * + * @param instance Pointer to a SubGhzTxRx + * @param preset_name Name of preset + * @param frequency Frequency in Hz + * @param preset_data Data of preset + * @param preset_data_size Size of preset data + */ +void subghz_txrx_set_preset( + SubGhzTxRx* instance, + const char* preset_name, + uint32_t frequency, + uint8_t* preset_data, + size_t preset_data_size); + +/** + * Get name of preset + * + * @param instance Pointer to a SubGhzTxRx + * @param preset String of preset + * @return const char* Name of preset + */ +const char* subghz_txrx_get_preset_name(SubGhzTxRx* instance, const char* preset); + +/** + * Get of preset + * + * @param instance Pointer to a SubGhzTxRx + * @return SubGhzRadioPreset Preset + */ +SubGhzRadioPreset subghz_txrx_get_preset(SubGhzTxRx* instance); + +/** + * Get string frequency and modulation + * + * @param instance Pointer to a SubGhzTxRx + * @param frequency Pointer to a string frequency + * @param modulation Pointer to a string modulation + */ +void subghz_txrx_get_frequency_and_modulation( + SubGhzTxRx* instance, + FuriString* frequency, + FuriString* modulation); + +/** + * Start TX CC1101 + * + * @param instance Pointer to a SubGhzTxRx + * @param flipper_format Pointer to a FlipperFormat + * @return SubGhzTxRxStartTxState + */ +SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* flipper_format); + +/** + * Start RX CC1101 + * + * @param instance Pointer to a SubGhzTxRx + */ +void subghz_txrx_rx_start(SubGhzTxRx* instance); + +/** + * Stop TX/RX CC1101 + * + * @param instance Pointer to a SubGhzTxRx + */ +void subghz_txrx_stop(SubGhzTxRx* instance); + +/** + * Set sleep mode CC1101 + * + * @param instance Pointer to a SubGhzTxRx + */ +void subghz_txrx_sleep(SubGhzTxRx* instance); + +/** + * Update frequency CC1101 in automatic mode (hopper) + * + * @param instance Pointer to a SubGhzTxRx + */ +void subghz_txrx_hopper_update(SubGhzTxRx* instance); + +/** + * Get state hopper + * + * @param instance Pointer to a SubGhzTxRx + * @return SubGhzHopperState + */ +SubGhzHopperState subghz_txrx_hopper_get_state(SubGhzTxRx* instance); + +/** + * Set state hopper + * + * @param instance Pointer to a SubGhzTxRx + * @param state State hopper + */ +void subghz_txrx_hopper_set_state(SubGhzTxRx* instance, SubGhzHopperState state); + +/** + * Unpause hopper + * + * @param instance Pointer to a SubGhzTxRx + */ +void subghz_txrx_hopper_unpause(SubGhzTxRx* instance); + +/** + * Set pause hopper + * + * @param instance Pointer to a SubGhzTxRx + */ +void subghz_txrx_hopper_pause(SubGhzTxRx* instance); + +/** + * Speaker on + * + * @param instance Pointer to a SubGhzTxRx + */ +void subghz_txrx_speaker_on(SubGhzTxRx* instance); + +/** + * Speaker off + * + * @param instance Pointer to a SubGhzTxRx + */ +void subghz_txrx_speaker_off(SubGhzTxRx* instance); + +/** + * Speaker mute + * + * @param instance Pointer to a SubGhzTxRx + */ +void subghz_txrx_speaker_mute(SubGhzTxRx* instance); + +/** + * Speaker unmute + * + * @param instance Pointer to a SubGhzTxRx + */ +void subghz_txrx_speaker_unmute(SubGhzTxRx* instance); + +/** + * Set state speaker + * + * @param instance Pointer to a SubGhzTxRx + * @param state State speaker + */ +void subghz_txrx_speaker_set_state(SubGhzTxRx* instance, SubGhzSpeakerState state); + +/** + * Get state speaker + * + * @param instance Pointer to a SubGhzTxRx + * @return SubGhzSpeakerState + */ +SubGhzSpeakerState subghz_txrx_speaker_get_state(SubGhzTxRx* instance); + +/** + * load decoder by name protocol + * + * @param instance Pointer to a SubGhzTxRx + * @param name_protocol Name protocol + * @return bool True if the decoder is loaded + */ +bool subghz_txrx_load_decoder_by_name_protocol(SubGhzTxRx* instance, const char* name_protocol); + +/** + * Get decoder + * + * @param instance Pointer to a SubGhzTxRx + * @return SubGhzProtocolDecoderBase* Pointer to a SubGhzProtocolDecoderBase + */ +SubGhzProtocolDecoderBase* subghz_txrx_get_decoder(SubGhzTxRx* instance); + +/** + * Set callback for save data + * + * @param instance Pointer to a SubGhzTxRx + * @param callback Callback for save data + * @param context Context for callback + */ +void subghz_txrx_set_need_save_callback( + SubGhzTxRx* instance, + SubGhzTxRxNeedSaveCallback callback, + void* context); + +/** + * Get pointer to a load data key + * + * @param instance Pointer to a SubGhzTxRx + * @return FlipperFormat* + */ +FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance); + +/** + * Get pointer to a SugGhzSetting + * + * @param instance Pointer to a SubGhzTxRx + * @return SubGhzSetting* + */ +SubGhzSetting* subghz_txrx_get_setting(SubGhzTxRx* instance); + +/** + * Is it possible to save this protocol + * + * @param instance Pointer to a SubGhzTxRx + * @return bool True if it is possible to save this protocol + */ +bool subghz_txrx_protocol_is_serializable(SubGhzTxRx* instance); + +/** + * Is it possible to send this protocol + * + * @param instance Pointer to a SubGhzTxRx + * @return bool True if it is possible to send this protocol + */ +bool subghz_txrx_protocol_is_transmittable(SubGhzTxRx* instance, bool check_type); + +/** + * Set filter, what types of decoder to use + * + * @param instance Pointer to a SubGhzTxRx + * @param filter Filter + */ +void subghz_txrx_receiver_set_filter(SubGhzTxRx* instance, SubGhzProtocolFlag filter); + +/** + * Set callback for receive data + * + * @param instance Pointer to a SubGhzTxRx + * @param callback Callback for receive data + * @param context Context for callback + */ +void subghz_txrx_set_rx_calback( + SubGhzTxRx* instance, + SubGhzReceiverCallback callback, + void* context); + +/** + * Set callback for Raw decoder, end of data transfer + * + * @param instance Pointer to a SubGhzTxRx + * @param callback Callback for Raw decoder, end of data transfer + * @param context Context for callback + */ +void subghz_txrx_set_raw_file_encoder_worker_callback_end( + SubGhzTxRx* instance, + SubGhzProtocolEncoderRAWCallbackEnd callback, + void* context); + +/* Checking if an external radio device is connected +* +* @param instance Pointer to a SubGhzTxRx +* @param name Name of external radio device +* @return bool True if is connected to the external radio device +*/ +bool subghz_txrx_radio_device_is_external_connected(SubGhzTxRx* instance, const char* name); + +/* Set the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @param radio_device_type Radio device type +* @return SubGhzRadioDeviceType Type of installed radio device +*/ +SubGhzRadioDeviceType + subghz_txrx_radio_device_set(SubGhzTxRx* instance, SubGhzRadioDeviceType radio_device_type); + +/* Get the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @return SubGhzRadioDeviceType Type of installed radio device +*/ +SubGhzRadioDeviceType subghz_txrx_radio_device_get(SubGhzTxRx* instance); + +/* Get RSSI the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @return float RSSI +*/ +float subghz_txrx_radio_device_get_rssi(SubGhzTxRx* instance); + +/* Get name the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @return const char* Name of installed radio device +*/ +const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance); + +/* Get get intelligence whether frequency the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @return bool True if the frequency is valid +*/ +bool subghz_txrx_radio_device_is_frequecy_valid(SubGhzTxRx* instance, uint32_t frequency); \ No newline at end of file diff --git a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c new file mode 100644 index 00000000000..0413173e6e8 --- /dev/null +++ b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c @@ -0,0 +1,161 @@ +#include "subghz_txrx_i.h" +#include "subghz_txrx_create_protocol_key.h" +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define TAG "SubGhzCreateProtocolKey" + +SubGhzProtocolStatus subghz_txrx_gen_data_protocol( + void* context, + const char* preset_name, + uint32_t frequency, + const char* protocol_name, + uint64_t key, + uint32_t bit) { + furi_assert(context); + SubGhzTxRx* instance = context; + + SubGhzProtocolStatus ret = SubGhzProtocolStatusOk; + + subghz_txrx_set_preset(instance, preset_name, frequency, NULL, 0); + instance->decoder_result = + subghz_receiver_search_decoder_base_by_name(instance->receiver, protocol_name); + + if(instance->decoder_result == NULL) { + FURI_LOG_E(TAG, "Protocol not found!"); + ret = SubGhzProtocolStatusErrorProtocolNotFound; + return ret; + } + + do { + Stream* fff_data_stream = flipper_format_get_raw_stream(instance->fff_data); + stream_clean(fff_data_stream); + ret = subghz_protocol_decoder_base_serialize( + instance->decoder_result, instance->fff_data, instance->preset); + if(ret != SubGhzProtocolStatusOk) { + FURI_LOG_E(TAG, "Unable to serialize"); + break; + } + if(!flipper_format_update_uint32(instance->fff_data, "Bit", &bit, 1)) { + ret = SubGhzProtocolStatusErrorParserOthers; + FURI_LOG_E(TAG, "Unable to update Bit"); + break; + } + + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (key >> (i * 8)) & 0xFF; + } + if(!flipper_format_update_hex(instance->fff_data, "Key", key_data, sizeof(uint64_t))) { + ret = SubGhzProtocolStatusErrorParserOthers; + FURI_LOG_E(TAG, "Unable to update Key"); + break; + } + } while(false); + return ret; +} + +SubGhzProtocolStatus subghz_txrx_gen_data_protocol_and_te( + SubGhzTxRx* instance, + const char* preset_name, + uint32_t frequency, + const char* protocol_name, + uint64_t key, + uint32_t bit, + uint32_t te) { + furi_assert(instance); + SubGhzProtocolStatus ret = + subghz_txrx_gen_data_protocol(instance, preset_name, frequency, protocol_name, key, bit); + if(ret == SubGhzProtocolStatusOk) { + if(!flipper_format_update_uint32(instance->fff_data, "TE", (uint32_t*)&te, 1)) { + ret = SubGhzProtocolStatusErrorParserOthers; + FURI_LOG_E(TAG, "Unable to update Te"); + } + } + return ret; +} + +SubGhzProtocolStatus subghz_txrx_gen_keeloq_protocol( + SubGhzTxRx* instance, + const char* name_preset, + uint32_t frequency, + const char* name_sysmem, + uint32_t serial, + uint8_t btn, + uint16_t cnt) { + furi_assert(instance); + + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + serial &= 0x0FFFFFFF; + instance->transmitter = + subghz_transmitter_alloc_init(instance->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); + subghz_txrx_set_preset(instance, name_preset, frequency, NULL, 0); + if(instance->transmitter) { + subghz_protocol_keeloq_create_data( + subghz_transmitter_get_protocol_instance(instance->transmitter), + instance->fff_data, + serial, + btn, + cnt, + name_sysmem, + instance->preset); + ret = SubGhzProtocolStatusOk; + } + subghz_transmitter_free(instance->transmitter); + return ret; +} + +SubGhzProtocolStatus subghz_txrx_gen_secplus_v2_protocol( + SubGhzTxRx* instance, + const char* name_preset, + uint32_t frequency, + uint32_t serial, + uint8_t btn, + uint32_t cnt) { + furi_assert(instance); + + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + instance->transmitter = + subghz_transmitter_alloc_init(instance->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); + subghz_txrx_set_preset(instance, name_preset, frequency, NULL, 0); + + if(instance->transmitter) { + subghz_protocol_secplus_v2_create_data( + subghz_transmitter_get_protocol_instance(instance->transmitter), + instance->fff_data, + serial, + btn, + cnt, + instance->preset); + ret = SubGhzProtocolStatusOk; + } + return ret; +} + +SubGhzProtocolStatus subghz_txrx_gen_secplus_v1_protocol( + SubGhzTxRx* instance, + const char* name_preset, + uint32_t frequency) { + furi_assert(instance); + + uint32_t serial = (uint32_t)rand(); + while(!subghz_protocol_secplus_v1_check_fixed(serial)) { + serial = (uint32_t)rand(); + } + + return subghz_txrx_gen_data_protocol( + instance, + name_preset, + frequency, + SUBGHZ_PROTOCOL_SECPLUS_V1_NAME, + (uint64_t)serial << 32 | 0xE6000000, + 42); +} diff --git a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h new file mode 100644 index 00000000000..edc6e541557 --- /dev/null +++ b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h @@ -0,0 +1,96 @@ +#pragma once +#include "subghz_types.h" +#include "subghz_txrx.h" + +/** + * Generate data for protocol + * + * @param instance Pointer to a SubGhzTxRx + * @param preset_name Name of preset + * @param frequency Frequency in Hz + * @param protocol_name Name of protocol + * @param key Key + * @param bit Bit + * @return SubGhzProtocolStatus + */ +SubGhzProtocolStatus subghz_txrx_gen_data_protocol( + void* context, + const char* preset_name, + uint32_t frequency, + const char* protocol_name, + uint64_t key, + uint32_t bit); + +/** + * Generate data for protocol and te + * + * @param instance Pointer to a SubGhzTxRx + * @param preset_name Name of preset + * @param frequency Frequency in Hz + * @param protocol_name Name of protocol + * @param key Key + * @param bit Bit + * @param te Te + * @return SubGhzProtocolStatus + */ +SubGhzProtocolStatus subghz_txrx_gen_data_protocol_and_te( + SubGhzTxRx* instance, + const char* preset_name, + uint32_t frequency, + const char* protocol_name, + uint64_t key, + uint32_t bit, + uint32_t te); + +/** + * Generate data Keeloq protocol + * + * @param instance Pointer to a SubGhzTxRx + * @param name_preset Name of preset + * @param frequency Frequency in Hz + * @param name_sysmem Name of Keeloq sysmem + * @param serial Serial number + * @param btn Button + * @param cnt Counter + * @return SubGhzProtocolStatus + */ +SubGhzProtocolStatus subghz_txrx_gen_keeloq_protocol( + SubGhzTxRx* instance, + const char* name_preset, + uint32_t frequency, + const char* name_sysmem, + uint32_t serial, + uint8_t btn, + uint16_t cnt); + +/** + * Generate data SecPlus v2 protocol + * + * @param instance Pointer to a SubGhzTxRx + * @param name_preset Name of preset + * @param frequency Frequency in Hz + * @param serial Serial number + * @param btn Button + * @param cnt Counter + * @return SubGhzProtocolStatus + */ +SubGhzProtocolStatus subghz_txrx_gen_secplus_v2_protocol( + SubGhzTxRx* instance, + const char* name_preset, + uint32_t frequency, + uint32_t serial, + uint8_t btn, + uint32_t cnt); + +/** + * Generate data SecPlus v1 protocol + * + * @param instance Pointer to a SubGhzTxRx + * @param name_preset Name of preset + * @param frequency Frequency in Hz + * @return SubGhzProtocolStatus + */ +SubGhzProtocolStatus subghz_txrx_gen_secplus_v1_protocol( + SubGhzTxRx* instance, + const char* name_preset, + uint32_t frequency); \ No newline at end of file diff --git a/applications/main/subghz/helpers/subghz_txrx_i.h b/applications/main/subghz/helpers/subghz_txrx_i.h new file mode 100644 index 00000000000..b7d74fd492f --- /dev/null +++ b/applications/main/subghz/helpers/subghz_txrx_i.h @@ -0,0 +1,29 @@ +#pragma once + +#include "subghz_txrx.h" + +struct SubGhzTxRx { + SubGhzWorker* worker; + + SubGhzEnvironment* environment; + SubGhzReceiver* receiver; + SubGhzTransmitter* transmitter; + SubGhzProtocolDecoderBase* decoder_result; + FlipperFormat* fff_data; + + SubGhzRadioPreset* preset; + SubGhzSetting* setting; + + uint8_t hopper_timeout; + uint8_t hopper_idx_frequency; + bool is_database_loaded; + SubGhzHopperState hopper_state; + + SubGhzTxRxState txrx_state; + SubGhzSpeakerState speaker_state; + const SubGhzDevice* radio_device; + SubGhzRadioDeviceType radio_device_type; + + SubGhzTxRxNeedSaveCallback need_save_callback; + void* need_save_context; +}; diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h new file mode 100644 index 00000000000..e71c22dd564 --- /dev/null +++ b/applications/main/subghz/helpers/subghz_types.h @@ -0,0 +1,90 @@ +#pragma once + +#include +#include + +/** SubGhzNotification state */ +typedef enum { + SubGhzNotificationStateStarting, + SubGhzNotificationStateIDLE, + SubGhzNotificationStateTx, + SubGhzNotificationStateRx, + SubGhzNotificationStateRxDone, +} SubGhzNotificationState; + +/** SubGhzTxRx state */ +typedef enum { + SubGhzTxRxStateIDLE, + SubGhzTxRxStateRx, + SubGhzTxRxStateTx, + SubGhzTxRxStateSleep, +} SubGhzTxRxState; + +/** SubGhzHopperState state */ +typedef enum { + SubGhzHopperStateOFF, + SubGhzHopperStateRunnig, + SubGhzHopperStatePause, + SubGhzHopperStateRSSITimeOut, +} SubGhzHopperState; + +/** SubGhzSpeakerState state */ +typedef enum { + SubGhzSpeakerStateDisable, + SubGhzSpeakerStateShutdown, + SubGhzSpeakerStateEnable, +} SubGhzSpeakerState; + +/** SubGhzRadioDeviceType */ +typedef enum { + SubGhzRadioDeviceTypeAuto, + SubGhzRadioDeviceTypeInternal, + SubGhzRadioDeviceTypeExternalCC1101, +} SubGhzRadioDeviceType; + +/** SubGhzRxKeyState state */ +typedef enum { + SubGhzRxKeyStateIDLE, + SubGhzRxKeyStateNoSave, + SubGhzRxKeyStateNeedSave, + SubGhzRxKeyStateBack, + SubGhzRxKeyStateStart, + SubGhzRxKeyStateAddKey, + SubGhzRxKeyStateExit, + SubGhzRxKeyStateRAWLoad, + SubGhzRxKeyStateRAWSave, +} SubGhzRxKeyState; + +/** SubGhzLoadKeyState state */ +typedef enum { + SubGhzLoadKeyStateUnknown, + SubGhzLoadKeyStateOK, + SubGhzLoadKeyStateParseErr, + SubGhzLoadKeyStateProtocolDescriptionErr, +} SubGhzLoadKeyState; + +/** SubGhzLock */ +typedef enum { + SubGhzLockOff, + SubGhzLockOn, +} SubGhzLock; + +typedef enum { + SubGhzViewIdMenu, + SubGhzViewIdReceiver, + SubGhzViewIdPopup, + SubGhzViewIdTextInput, + SubGhzViewIdWidget, + SubGhzViewIdTransmitter, + SubGhzViewIdVariableItemList, + SubGhzViewIdFrequencyAnalyzer, + SubGhzViewIdReadRAW, + +} SubGhzViewId; + +/** SubGhz load type file */ +typedef enum { + SubGhzLoadTypeFileNoLoad, + SubGhzLoadTypeFileKey, + SubGhzLoadTypeFileRaw, +} SubGhzLoadTypeFile; diff --git a/applications/main/subghz/icon.png b/applications/main/subghz/icon.png new file mode 100644 index 00000000000..5a25fdf4ef1 Binary files /dev/null and b/applications/main/subghz/icon.png differ diff --git a/applications/main/subghz/resources/subghz/assets/alutech_at_4n b/applications/main/subghz/resources/subghz/assets/alutech_at_4n new file mode 100644 index 00000000000..5d7beacec42 --- /dev/null +++ b/applications/main/subghz/resources/subghz/assets/alutech_at_4n @@ -0,0 +1,6 @@ +Filetype: Flipper SubGhz Keystore RAW File +Version: 0 +Encryption: 1 +IV: 88 64 A6 A6 44 47 67 8A D6 32 36 F6 B9 06 57 31 +Encrypt_data: RAW +E811BD4F0955D217AE6677906E799D45D8DAAFD1F7923E1660B5E24574631B60 \ No newline at end of file diff --git a/assets/resources/subghz/assets/came_atomo b/applications/main/subghz/resources/subghz/assets/came_atomo similarity index 100% rename from assets/resources/subghz/assets/came_atomo rename to applications/main/subghz/resources/subghz/assets/came_atomo diff --git a/applications/main/subghz/resources/subghz/assets/keeloq_mfcodes b/applications/main/subghz/resources/subghz/assets/keeloq_mfcodes new file mode 100644 index 00000000000..f2e45b491b3 --- /dev/null +++ b/applications/main/subghz/resources/subghz/assets/keeloq_mfcodes @@ -0,0 +1,57 @@ +Filetype: Flipper SubGhz Keystore File +Version: 0 +Encryption: 1 +IV: 23 17 32 54 92 3A 28 13 12 DE 8B A7 24 71 25 74 +B8ACA082EA4B34E8B217A6FE63F3545D160AB7F4833C87E8743BAA7002ACC123 +E779135FE66629CAC4661B1C3D5C9857C9417CAF216241933FF6CE60A74D53C0 +15340C049A444C79ED489619A31E2AFDCFF6E4864CC4D9B1DA8E98D7EAB9C8D5 +8E696E85837B3BA8D6D380E1F36E445D630CF0B7B18A45930A08F832EE634A02 +C85341BE669E509E902FEFCA7D025A20DC80D4F14FE1C3542B1DC3A5C7393A36F901A63C3BC074B058897B12F0F5F0D6 +2834E5D7726670A03ED6C8B27B5863EEE2FD3668795251AB65B9E4FF76ADEC3E +8E71AF7660BCFC545E92674D74F98A35FCF1B54AB5BACCEE182B7F8EDB3FA356 +C20442D506332D1A410AF3187C29BE01729C282A69B85CF8D92D70FAE3407BA8 +00BC449D006566A6549A86F52B98410871B0C14D60C181BFC017446B192C934A +8A6A28DAF1F61867B1209AB9105986A7383ECE72A40F8D93F1D0066251800A3A +4A6AFDFA6130A50180358B111145D7914C720E04D69EE5459C049FCD64249153315F86A5A9F75AEE5CB726847BA64F2A +91F838BC515C8CDD32C5803679B81A24FD5CF0C3A4AEA2C07342ACA1020E05F3B393A410B33847A8C48141D923202CF3 +7C4DEBC03295291EC5B2EBB002670D14E7C972F32DB2B3CD4E61B2BE846345D9 +8FFEB948DA3D1A7FCF620F043B1D35354F83861F22B6D693F2A7119A2E287D01 +309C66AF38E6447CB33FB0967C9365CA36314DBF74695F63CF9E558A4FF5193B +411D32EC6ADED3360DE9E9A0962F97DF2B80A09F2A2AC8D4DB6DB09FAD6EA217 +A5E5550BD59425003F9166237CD4BB6A323885D7916F71F3B2795B8A4C7125ACC1237BBF0840F0E999BD2EC14327654F +4370A828AFED684B96E8EDAB670C46D2D525FCB7E469B63CD1228C902C3ACF4E +88E3853F58129C4D834B18E4D01785591094301148D802A3A21B63521DDDA325 +D5365BF55DFE7107909684F4ECCE052E617F91FAF08CE5254721A30BEF87F368F329C46439BD1EA2819DC416B2CEF247 +BDAB9FFD3A82ECE05E8CA00F11954CDB1C124B558A5D369DFE16D32C3AEF08B2013945CC69212F170F17F62A55D35928 +8E448AA1D39C894F306FE65B0D0DD733717EF81758FFA908AF7583BFD00108E5 +019A1B0AA3492427DF3DB817B438D0302B0B8C77807D19EE45EAC7F697B2EB85 +C678A3F9DA726FE1688F86BB063EA017EDB5389E0F185484D9F5DFD4EBEE0579 +760F77AB9BD99593AC855E4AE00FBAFBB34FA2BFF504BD5CC9557658E850C6D0 +4F3E77342E9A655D4EC167E2B0428833FEE31CF603FA1CE3D1BCD619D5C9D511 +2233EFBEDD4A3A95BA8160EFCED533D9F25B65AAB10F627681D2753871814366 +A1261BE878933B4905956BE7708F7C40E999714503B52B8021D44E131D87FF6C +8F31E76314A46EA9B0CCD3EDA4B2DF603829D8E3AF8CE41AEA6BFFBA2E2990AC +850C08E45CDA4E42BD4F09B956299C0ED58615BE3664791C88D1E2608D46D205 +6283656518C6ADFC3B7D309D2763BD91111720CD51029FAA691211DEFF84CB47 +9FCAF9F77C9D1B9B6934E53DB10DFF88D392D8E7BB4DC28D65F65DC396E2E00A +41D2406BE1887FCC094861E4DF0683B1BE534C5CBF059259E946F9D04222C2AB +1D6F37E645591F0C491312C1E0CB54F1A6B70F94B54D63100C4584AA016DB589338A704B6D8B6B89C2381F660D987A2F +DCEA08D6E5C0D6229EBDE07DBB6CBC0EA8D6E24A3B631CC3775CE23A496CF178 +907051215A741BE55603F27C3FD86561CC6231CEED83AA76665D6A0B6B3FEC88 +36CD5B5BCC63D45B89592B2938D6542B9D82AB47BEB73C0D30D2EC19BD8B35C2 +1FD9651ABAD773D6D22DE677CF7B19615D5B805594DCA117F04AC3255AE8184A +4900723A8F7736C9BEAE938EE021F3E44AD806244010F825D04C59E6C67B5EA2 +0D56A8CE77106DA2CB44F65B8CF5F4A024B9CC774D1C4CB8E862C95BD30B42E7B2E946CAC361F2830361C010DC938833 +11543FD104EA7D68B6B9C3BFE3F25A84F651FCB7FB5423AADDC472A48E12AFB2 +98CD236E8B92D706DA0F92F43AC1D2CAF1CFBCDC7B330A7C9D65CDC4F2E2A677 +753557A9A5075C033206111E3C832044227A32EBF3AB2B08F6FBFCC2109B2F2A +4AA124232EF6C25CB7321F5E5F7BE11114C763F8DDAFB6B05E8412ADF37B5C7B +01514EA408E26BAFD30290429DE1A0211F4FBDB09DE2E66CE92A6F09B998EF29 +4715716D5AFF99A2D8205A46AEEF0BC20D72F3F54991F7DF4142FF187485C178 +281A32B90F8B85F31F54BF0D83B6D03E4E65A72ADB3D5A297C91881E36B45E8F +9C05D3CB9F473D5693697A8567DBA7CF650C0A0D6C1BBE3ACE78ACDE0BD027B6C836B6CB6365D5B5D00F101FA83876B9 +DC88B0237634CB007244A4169A5D9F5F65FC782E3C4388358A7E2019F7B4CC05 +BC7ACAA91D23616A0070DCA328901DDDE3BB5B59260FB45083916B6ED0E6450C +A280434D059D652D77ACB9F44409CDC7393A876239BA894DD4BE843FAB0AC561 +334F1316A35EB8A838F6B209FA4F8148062972E27A296379731B1F728A0BB32E +DDC9883E90EBE73915CFDF8EA7193F2449B66010D45F5EC6F20949D5C49719A9298B95269531A3440577B00EADE1B379 diff --git a/applications/main/subghz/resources/subghz/assets/keeloq_mfcodes_user.example b/applications/main/subghz/resources/subghz/assets/keeloq_mfcodes_user.example new file mode 100644 index 00000000000..0d43c5591e7 --- /dev/null +++ b/applications/main/subghz/resources/subghz/assets/keeloq_mfcodes_user.example @@ -0,0 +1,12 @@ +# to use manual settings and prevent them from being deleted on upgrade, rename *_user.example files to *_user +# for adding manufacture keys +# AABBCCDDEEFFAABB:X:NAME\r\n +# AABBCCDDEEFFAABB - man 64 bit +# X - encryption method 1 - Simple Learning, 2 - Normal_Learning, 3 - Secure_Learning, 4 - Magic_xor_type1 Learning +# 0 - iterates over both previous and man in direct and reverse byte sequence +# NAME - name (string without spaces) max 64 characters long +Filetype: Flipper SubGhz Keystore File +Version: 0 +Encryption: 0 +AABBCCDDEEFFAABB:1:Test1 +AABBCCDDEEFFAABB:1:Test2 diff --git a/assets/resources/subghz/assets/nice_flor_s b/applications/main/subghz/resources/subghz/assets/nice_flor_s similarity index 100% rename from assets/resources/subghz/assets/nice_flor_s rename to applications/main/subghz/resources/subghz/assets/nice_flor_s diff --git a/applications/main/subghz/resources/subghz/assets/setting_user.example b/applications/main/subghz/resources/subghz/assets/setting_user.example new file mode 100644 index 00000000000..a0cbb9a0761 --- /dev/null +++ b/applications/main/subghz/resources/subghz/assets/setting_user.example @@ -0,0 +1,30 @@ +# 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: 310000000 +#Frequency: 320000000 + +# Frequencies used for hopping mode (keep this list small or flipper will miss signal) +#Hopper_frequency: 300000000 +#Hopper_frequency: 310000000 +#Hopper_frequency: 310000000 + +# 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 + +#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/applications/subghz/scenes/subghz_scene.c b/applications/main/subghz/scenes/subghz_scene.c similarity index 100% rename from applications/subghz/scenes/subghz_scene.c rename to applications/main/subghz/scenes/subghz_scene.c diff --git a/applications/subghz/scenes/subghz_scene.h b/applications/main/subghz/scenes/subghz_scene.h similarity index 100% rename from applications/subghz/scenes/subghz_scene.h rename to applications/main/subghz/scenes/subghz_scene.h diff --git a/applications/subghz/scenes/subghz_scene_config.h b/applications/main/subghz/scenes/subghz_scene_config.h similarity index 84% rename from applications/subghz/scenes/subghz_scene_config.h rename to applications/main/subghz/scenes/subghz_scene_config.h index fd1271c8c73..47655958b4b 100644 --- a/applications/subghz/scenes/subghz_scene_config.h +++ b/applications/main/subghz/scenes/subghz_scene_config.h @@ -12,10 +12,6 @@ ADD_SCENE(subghz, show_only_rx, ShowOnlyRx) ADD_SCENE(subghz, saved_menu, SavedMenu) ADD_SCENE(subghz, delete, Delete) ADD_SCENE(subghz, delete_success, DeleteSuccess) -ADD_SCENE(subghz, test, Test) -ADD_SCENE(subghz, test_static, TestStatic) -ADD_SCENE(subghz, test_carrier, TestCarrier) -ADD_SCENE(subghz, test_packet, TestPacket) ADD_SCENE(subghz, set_type, SetType) ADD_SCENE(subghz, frequency_analyzer, FrequencyAnalyzer) ADD_SCENE(subghz, read_raw, ReadRAW) @@ -23,3 +19,5 @@ ADD_SCENE(subghz, more_raw, MoreRAW) ADD_SCENE(subghz, delete_raw, DeleteRAW) ADD_SCENE(subghz, need_saving, NeedSaving) ADD_SCENE(subghz, rpc, Rpc) +ADD_SCENE(subghz, region_info, RegionInfo) +ADD_SCENE(subghz, radio_settings, RadioSettings) diff --git a/applications/main/subghz/scenes/subghz_scene_delete.c b/applications/main/subghz/scenes/subghz_scene_delete.c new file mode 100644 index 00000000000..0d14cd23a3f --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_delete.c @@ -0,0 +1,74 @@ +#include "../subghz_i.h" +#include "../helpers/subghz_custom_event.h" + +void subghz_scene_delete_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + SubGhz* subghz = context; + if((result == GuiButtonTypeRight) && (type == InputTypeShort)) { + view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneDelete); + } +} + +void subghz_scene_delete_on_enter(void* context) { + SubGhz* subghz = context; + FuriString* frequency_str; + FuriString* modulation_str; + FuriString* text; + + frequency_str = furi_string_alloc(); + modulation_str = furi_string_alloc(); + text = furi_string_alloc(); + + subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str); + widget_add_string_element( + subghz->widget, + 78, + 0, + AlignLeft, + AlignTop, + FontSecondary, + furi_string_get_cstr(frequency_str)); + + widget_add_string_element( + subghz->widget, + 113, + 0, + AlignLeft, + AlignTop, + FontSecondary, + furi_string_get_cstr(modulation_str)); + subghz_protocol_decoder_base_get_string(subghz_txrx_get_decoder(subghz->txrx), text); + widget_add_string_multiline_element( + subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(text)); + + furi_string_free(frequency_str); + furi_string_free(modulation_str); + furi_string_free(text); + + widget_add_button_element( + subghz->widget, GuiButtonTypeRight, "Delete", subghz_scene_delete_callback, subghz); + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); +} + +bool subghz_scene_delete_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzCustomEventSceneDelete) { + furi_string_set(subghz->file_path_tmp, subghz->file_path); + if(subghz_delete_file(subghz)) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess); + } else { + scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart); + } + return true; + } + } + return false; +} + +void subghz_scene_delete_on_exit(void* context) { + SubGhz* subghz = context; + widget_reset(subghz->widget); +} diff --git a/applications/subghz/scenes/subghz_scene_delete_raw.c b/applications/main/subghz/scenes/subghz_scene_delete_raw.c similarity index 77% rename from applications/subghz/scenes/subghz_scene_delete_raw.c rename to applications/main/subghz/scenes/subghz_scene_delete_raw.c index a20968d5bb9..8dff442a870 100644 --- a/applications/subghz/scenes/subghz_scene_delete_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_raw.c @@ -15,27 +15,33 @@ void subghz_scene_delete_raw_callback(GuiButtonType result, InputType type, void void subghz_scene_delete_raw_on_enter(void* context) { SubGhz* subghz = context; - string_t frequency_str; - string_t modulation_str; + FuriString* frequency_str; + FuriString* modulation_str; - string_init(frequency_str); - string_init(modulation_str); + frequency_str = furi_string_alloc(); + modulation_str = furi_string_alloc(); char delete_str[SUBGHZ_MAX_LEN_NAME + 16]; - string_t file_name; - string_init(file_name); + FuriString* file_name; + file_name = furi_string_alloc(); path_extract_filename(subghz->file_path, file_name, true); - snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", string_get_cstr(file_name)); - string_clear(file_name); + snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", furi_string_get_cstr(file_name)); + furi_string_free(file_name); widget_add_text_box_element( subghz->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str, false); widget_add_string_element( subghz->widget, 38, 25, AlignLeft, AlignTop, FontSecondary, "RAW signal"); - subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); + subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str); widget_add_string_element( - subghz->widget, 35, 37, AlignLeft, AlignTop, FontSecondary, string_get_cstr(frequency_str)); + subghz->widget, + 35, + 37, + AlignLeft, + AlignTop, + FontSecondary, + furi_string_get_cstr(frequency_str)); widget_add_string_element( subghz->widget, @@ -44,10 +50,10 @@ void subghz_scene_delete_raw_on_enter(void* context) { AlignLeft, AlignTop, FontSecondary, - string_get_cstr(modulation_str)); + furi_string_get_cstr(modulation_str)); - string_clear(frequency_str); - string_clear(modulation_str); + furi_string_free(frequency_str); + furi_string_free(modulation_str); widget_add_button_element( subghz->widget, GuiButtonTypeRight, "Delete", subghz_scene_delete_raw_callback, subghz); @@ -61,7 +67,7 @@ bool subghz_scene_delete_raw_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubGhzCustomEventSceneDeleteRAW) { - string_set(subghz->file_path_tmp, subghz->file_path); + furi_string_set(subghz->file_path_tmp, subghz->file_path); if(subghz_delete_file(subghz)) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess); } else { diff --git a/applications/subghz/scenes/subghz_scene_delete_success.c b/applications/main/subghz/scenes/subghz_scene_delete_success.c similarity index 77% rename from applications/subghz/scenes/subghz_scene_delete_success.c rename to applications/main/subghz/scenes/subghz_scene_delete_success.c index d6e1f8dd42a..4d9f33e37e0 100644 --- a/applications/subghz/scenes/subghz_scene_delete_success.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_success.c @@ -28,10 +28,8 @@ bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event if(event.event == SubGhzCustomEventSceneDeleteSuccess) { if(scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneReadRAW)) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); } else if(scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneSaved)) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); } else { scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneStart); @@ -44,14 +42,7 @@ bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event void subghz_scene_delete_success_on_exit(void* context) { SubGhz* subghz = context; - - // Clear view Popup* popup = subghz->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); + + popup_reset(popup); } diff --git a/applications/subghz/scenes/subghz_scene_frequency_analyzer.c b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c similarity index 88% rename from applications/subghz/scenes/subghz_scene_frequency_analyzer.c rename to applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c index b42acd4d2ca..b48a821da23 100644 --- a/applications/subghz/scenes/subghz_scene_frequency_analyzer.c +++ b/applications/main/subghz/scenes/subghz_scene_frequency_analyzer.c @@ -1,6 +1,5 @@ #include "../subghz_i.h" #include "../views/subghz_frequency_analyzer.h" -#include void subghz_scene_frequency_analyzer_callback(SubGhzCustomEvent event, void* context) { furi_assert(context); @@ -10,7 +9,6 @@ void subghz_scene_frequency_analyzer_callback(SubGhzCustomEvent event, void* con void subghz_scene_frequency_analyzer_on_enter(void* context) { SubGhz* subghz = context; - DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer); subghz_frequency_analyzer_set_callback( subghz->subghz_frequency_analyzer, subghz_scene_frequency_analyzer_callback, subghz); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdFrequencyAnalyzer); @@ -21,6 +19,8 @@ bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent e if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubGhzCustomEventSceneAnalyzerLock) { notification_message(subghz->notifications, &sequence_set_green_255); + notification_message(subghz->notifications, &sequence_single_vibro); + notification_message(subghz->notifications, &sequence_display_backlight_on); return true; } else if(event.event == SubGhzCustomEventSceneAnalyzerUnlock) { notification_message(subghz->notifications, &sequence_reset_rgb); diff --git a/applications/main/subghz/scenes/subghz_scene_more_raw.c b/applications/main/subghz/scenes/subghz_scene_more_raw.c new file mode 100644 index 00000000000..864be1ed1ad --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_more_raw.c @@ -0,0 +1,77 @@ +#include "../subghz_i.h" + +enum SubmenuIndex { + SubmenuIndexEdit, + SubmenuIndexDelete, +}; + +void subghz_scene_more_raw_submenu_callback(void* context, uint32_t index) { + SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, index); +} + +void subghz_scene_more_raw_on_enter(void* context) { + SubGhz* subghz = context; + + submenu_add_item( + subghz->submenu, + "Rename", + SubmenuIndexEdit, + subghz_scene_more_raw_submenu_callback, + subghz); + + submenu_add_item( + subghz->submenu, + "Delete", + SubmenuIndexDelete, + subghz_scene_more_raw_submenu_callback, + subghz); + + submenu_set_selected_item( + subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneMoreRAW)); + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdMenu); +} + +bool subghz_scene_more_raw_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexDelete) { + if(subghz_file_available(subghz)) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexDelete); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteRAW); + return true; + } else { + if(!scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart)) { + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + } + } + } else if(event.event == SubmenuIndexEdit) { + if(subghz_file_available(subghz)) { + furi_string_reset(subghz->file_path_tmp); + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexEdit); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); + return true; + } else { + if(!scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart)) { + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + } + } + } + } + return false; +} + +void subghz_scene_more_raw_on_exit(void* context) { + SubGhz* subghz = context; + submenu_reset(subghz->submenu); +} diff --git a/applications/subghz/scenes/subghz_scene_need_saving.c b/applications/main/subghz/scenes/subghz_scene_need_saving.c similarity index 77% rename from applications/subghz/scenes/subghz_scene_need_saving.c rename to applications/main/subghz/scenes/subghz_scene_need_saving.c index 53bffedc844..f29f26309c6 100644 --- a/applications/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); @@ -37,27 +37,23 @@ void subghz_scene_need_saving_on_enter(void* context) { bool subghz_scene_need_saving_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; if(event.type == SceneManagerEventTypeBack) { - subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack); scene_manager_previous_scene(subghz->scene_manager); return true; } else if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubGhzCustomEventSceneStay) { - subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack); scene_manager_previous_scene(subghz->scene_manager); return true; } else if(event.event == SubGhzCustomEventSceneExit) { - if(subghz->txrx->rx_key_state == SubGhzRxKeyStateExit) { - subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; - subghz_preset_init( - subghz, - "AM650", - subghz_setting_get_default_frequency(subghz->setting), - NULL, - 0); + SubGhzRxKeyState state = subghz_rx_key_state_get(subghz); + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); + + if(state == SubGhzRxKeyStateExit) { + subghz_set_default_preset(subghz); scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneStart); } else { - subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; scene_manager_previous_scene(subghz->scene_manager); } diff --git a/applications/main/subghz/scenes/subghz_scene_radio_setting.c b/applications/main/subghz/scenes/subghz_scene_radio_setting.c new file mode 100644 index 00000000000..1f8e4d83d6a --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_radio_setting.c @@ -0,0 +1,86 @@ +#include "../subghz_i.h" +#include +#include +#include + +enum SubGhzRadioSettingIndex { + SubGhzRadioSettingIndexDevice, +}; + +#define RADIO_DEVICE_COUNT 2 +const char* const radio_device_text[RADIO_DEVICE_COUNT] = { + "Internal", + "External", +}; + +const uint32_t radio_device_value[RADIO_DEVICE_COUNT] = { + SubGhzRadioDeviceTypeInternal, + SubGhzRadioDeviceTypeExternalCC1101, +}; + +const char* const radio_device_name[RADIO_DEVICE_COUNT] = { + SUBGHZ_DEVICE_CC1101_INT_NAME, + SUBGHZ_DEVICE_CC1101_EXT_NAME, +}; + +static uint8_t subghz_scene_radio_settings_next_index_connect_ext_device( + SubGhz* subghz, + uint8_t current_index) { + uint8_t index = 0; + for(index = current_index; index < RADIO_DEVICE_COUNT; index++) { + if(subghz_txrx_radio_device_is_external_connected(subghz->txrx, radio_device_name[index])) { + break; + } + } + if(index == RADIO_DEVICE_COUNT) index = 0; + return index; +} + +static void subghz_scene_radio_settings_set_device(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t current_index = variable_item_get_current_value_index(item); + uint8_t index = + subghz_scene_radio_settings_next_index_connect_ext_device(subghz, current_index); + variable_item_set_current_value_text(item, radio_device_text[index]); + subghz_txrx_radio_device_set(subghz->txrx, radio_device_value[index]); +} + +void subghz_scene_radio_settings_on_enter(void* context) { + SubGhz* subghz = context; + VariableItem* item; + uint8_t value_index; + + uint8_t value_count_device = RADIO_DEVICE_COUNT; + + if(subghz_txrx_radio_device_get(subghz->txrx) == SubGhzRadioDeviceTypeInternal && + !subghz_scene_radio_settings_next_index_connect_ext_device(subghz, 1)) + value_count_device = 1; + + item = variable_item_list_add( + subghz->variable_item_list, + "Module", + value_count_device, + subghz_scene_radio_settings_set_device, + subghz); + value_index = value_index_uint32( + subghz_txrx_radio_device_get(subghz->txrx), radio_device_value, value_count_device); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, radio_device_text[value_index]); + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); +} + +bool subghz_scene_radio_settings_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + bool consumed = false; + UNUSED(subghz); + UNUSED(event); + + return consumed; +} + +void subghz_scene_radio_settings_on_exit(void* context) { + SubGhz* subghz = context; + variable_item_list_set_selected_item(subghz->variable_item_list, 0); + variable_item_list_reset(subghz->variable_item_list); +} diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c new file mode 100644 index 00000000000..f2ab6577026 --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -0,0 +1,337 @@ +#include "../subghz_i.h" +#include "../views/subghz_read_raw.h" +#include +#include +#include + +#define RAW_FILE_NAME "Raw_signal_" +#define TAG "SubGhzSceneReadRaw" + +bool subghz_scene_read_raw_update_filename(SubGhz* subghz) { + bool ret = false; + //set the path to read the file + FuriString* temp_str; + temp_str = furi_string_alloc(); + do { + FlipperFormat* fff_data = subghz_txrx_get_fff_data(subghz->txrx); + if(!flipper_format_rewind(fff_data)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + + if(!flipper_format_read_string(fff_data, "File_name", temp_str)) { + FURI_LOG_E(TAG, "Missing File_name"); + break; + } + + furi_string_set(subghz->file_path, temp_str); + + ret = true; + } while(false); + + furi_string_free(temp_str); + return ret; +} + +static void subghz_scene_read_raw_update_statusbar(void* context) { + furi_assert(context); + SubGhz* subghz = context; + + FuriString* frequency_str = furi_string_alloc(); + FuriString* modulation_str = furi_string_alloc(); + + subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str); + subghz_read_raw_add_data_statusbar( + subghz->subghz_read_raw, + furi_string_get_cstr(frequency_str), + furi_string_get_cstr(modulation_str)); + + furi_string_free(frequency_str); + furi_string_free(modulation_str); + + subghz_read_raw_set_radio_device_type( + subghz->subghz_read_raw, subghz_txrx_radio_device_get(subghz->txrx)); +} + +void subghz_scene_read_raw_callback(SubGhzCustomEvent event, void* context) { + furi_assert(context); + SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, event); +} + +void subghz_scene_read_raw_callback_end_tx(void* context) { + furi_assert(context); + SubGhz* subghz = context; + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventViewReadRAWSendStop); +} + +void subghz_scene_read_raw_on_enter(void* context) { + SubGhz* subghz = context; + FuriString* file_name = furi_string_alloc(); + + float threshold_rssi = subghz_threshold_rssi_get(subghz->threshold_rssi); + switch(subghz_rx_key_state_get(subghz)) { + case SubGhzRxKeyStateBack: + subghz_read_raw_set_status( + subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, "", threshold_rssi); + break; + case SubGhzRxKeyStateRAWLoad: + path_extract_filename(subghz->file_path, file_name, true); + subghz_read_raw_set_status( + subghz->subghz_read_raw, + SubGhzReadRAWStatusLoadKeyTX, + furi_string_get_cstr(file_name), + threshold_rssi); + break; + case SubGhzRxKeyStateRAWSave: + path_extract_filename(subghz->file_path, file_name, true); + subghz_read_raw_set_status( + subghz->subghz_read_raw, + SubGhzReadRAWStatusSaveKey, + furi_string_get_cstr(file_name), + threshold_rssi); + break; + default: + subghz_read_raw_set_status( + subghz->subghz_read_raw, SubGhzReadRAWStatusStart, "", threshold_rssi); + break; + } + + if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateBack) { + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); + } + furi_string_free(file_name); + subghz_scene_read_raw_update_statusbar(subghz); + + //set callback view raw + subghz_read_raw_set_callback(subghz->subghz_read_raw, subghz_scene_read_raw_callback, subghz); + + furi_check(subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, SUBGHZ_PROTOCOL_RAW_NAME)); + + //set filter RAW feed + subghz_txrx_receiver_set_filter(subghz->txrx, SubGhzProtocolFlag_RAW); + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReadRAW); +} + +bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + bool consumed = false; + SubGhzProtocolDecoderRAW* decoder_raw = + (SubGhzProtocolDecoderRAW*)subghz_txrx_get_decoder(subghz->txrx); + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case SubGhzCustomEventViewReadRAWBack: + + subghz_txrx_stop(subghz->txrx); + //Stop save file + subghz_protocol_raw_save_to_file_stop(decoder_raw); + subghz->state_notifications = SubGhzNotificationStateIDLE; + //needed save? + if((subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) || + (subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateBack)) { + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateExit); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); + } else { + //Restore default setting + subghz_set_default_preset(subghz); + if(!scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneSaved)) { + if(!scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart)) { + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + } + } + } + consumed = true; + break; + + case SubGhzCustomEventViewReadRAWTXRXStop: + subghz_txrx_stop(subghz->txrx); + subghz->state_notifications = SubGhzNotificationStateIDLE; + consumed = true; + break; + + case SubGhzCustomEventViewReadRAWConfig: + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig); + consumed = true; + break; + + case SubGhzCustomEventViewReadRAWErase: + if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) { + if(subghz_scene_read_raw_update_filename(subghz)) { + furi_string_set(subghz->file_path_tmp, subghz->file_path); + subghz_delete_file(subghz); + } + } + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); + notification_message(subghz->notifications, &sequence_reset_rgb); + consumed = true; + break; + + case SubGhzCustomEventViewReadRAWMore: + if(subghz_file_available(subghz)) { + if(subghz_scene_read_raw_update_filename(subghz)) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet); + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW); + consumed = true; + } else { + furi_crash("SubGhz: RAW file name update error."); + } + } else { + if(!scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart)) { + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + } + } + break; + + case SubGhzCustomEventViewReadRAWSendStart: + + if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) { + //start send + subghz->state_notifications = SubGhzNotificationStateIDLE; + if(!subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) { + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack); + subghz_read_raw_set_status( + subghz->subghz_read_raw, + SubGhzReadRAWStatusIDLE, + "", + subghz_threshold_rssi_get(subghz->threshold_rssi)); + } else { + if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSaved) || + !scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneStart)) { + dolphin_deed(DolphinDeedSubGhzSend); + } + // set callback end tx + subghz_txrx_set_raw_file_encoder_worker_callback_end( + subghz->txrx, subghz_scene_read_raw_callback_end_tx, subghz); + subghz->state_notifications = SubGhzNotificationStateTx; + } + } else { + if(!scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart)) { + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + } + } + consumed = true; + break; + + case SubGhzCustomEventViewReadRAWSendStop: + subghz->state_notifications = SubGhzNotificationStateIDLE; + subghz_txrx_stop(subghz->txrx); + subghz_read_raw_stop_send(subghz->subghz_read_raw); + consumed = true; + break; + + case SubGhzCustomEventViewReadRAWIDLE: + subghz_txrx_stop(subghz->txrx); + size_t spl_count = subghz_protocol_raw_get_sample_write(decoder_raw); + + subghz_protocol_raw_save_to_file_stop(decoder_raw); + + FuriString* temp_str = furi_string_alloc(); + furi_string_printf( + temp_str, + "%s/%s%s", + SUBGHZ_RAW_FOLDER, + RAW_FILE_NAME, + SUBGHZ_APP_FILENAME_EXTENSION); + subghz_protocol_raw_gen_fff_data( + subghz_txrx_get_fff_data(subghz->txrx), + furi_string_get_cstr(temp_str), + subghz_txrx_radio_device_get_name(subghz->txrx)); + furi_string_free(temp_str); + + if(spl_count > 0) { + notification_message(subghz->notifications, &sequence_set_green_255); + } else { + notification_message(subghz->notifications, &sequence_reset_rgb); + } + + subghz->state_notifications = SubGhzNotificationStateIDLE; + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); + + consumed = true; + break; + + case SubGhzCustomEventViewReadRAWREC: + if(subghz_rx_key_state_get(subghz) != SubGhzRxKeyStateIDLE) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); + } else { + SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); + if(subghz_protocol_raw_save_to_file_init(decoder_raw, RAW_FILE_NAME, &preset)) { + dolphin_deed(DolphinDeedSubGhzRawRec); + subghz_txrx_rx_start(subghz->txrx); + subghz->state_notifications = SubGhzNotificationStateRx; + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); + } else { + furi_string_set(subghz->error_str, "Function requires\nan SD card."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + } + consumed = true; + break; + + case SubGhzCustomEventViewReadRAWSave: + if(subghz_file_available(subghz) && subghz_scene_read_raw_update_filename(subghz)) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSetRAW); + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateBack); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); + } else { + if(!scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart)) { + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + } + } + consumed = true; + break; + + default: + break; + } + } else if(event.type == SceneManagerEventTypeTick) { + switch(subghz->state_notifications) { + case SubGhzNotificationStateRx: + notification_message(subghz->notifications, &sequence_blink_cyan_10); + + subghz_read_raw_update_sample_write( + subghz->subghz_read_raw, subghz_protocol_raw_get_sample_write(decoder_raw)); + + SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data( + subghz->threshold_rssi, subghz_txrx_radio_device_get_rssi(subghz->txrx)); + subghz_read_raw_add_data_rssi( + subghz->subghz_read_raw, ret_rssi.rssi, ret_rssi.is_above); + subghz_protocol_raw_save_to_file_pause(decoder_raw, !ret_rssi.is_above); + break; + case SubGhzNotificationStateTx: + notification_message(subghz->notifications, &sequence_blink_magenta_10); + subghz_read_raw_update_sin(subghz->subghz_read_raw); + break; + default: + break; + } + } + return consumed; +} + +void subghz_scene_read_raw_on_exit(void* context) { + SubGhz* subghz = context; + + //Stop CC1101 + subghz_txrx_stop(subghz->txrx); + subghz->state_notifications = SubGhzNotificationStateIDLE; + notification_message(subghz->notifications, &sequence_reset_rgb); + + //filter restoration + subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); +} diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c new file mode 100644 index 00000000000..497938feebf --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -0,0 +1,225 @@ +#include "../subghz_i.h" +#include "../views/receiver.h" +#include +#include + +static const NotificationSequence subghs_sequence_rx = { + &message_green_255, + + &message_vibro_on, + &message_note_c6, + &message_delay_50, + &message_sound_off, + &message_vibro_off, + + &message_delay_50, + NULL, +}; + +static const NotificationSequence subghs_sequence_rx_locked = { + &message_green_255, + + &message_display_backlight_on, + + &message_vibro_on, + &message_note_c6, + &message_delay_50, + &message_sound_off, + &message_vibro_off, + + &message_delay_500, + + &message_display_backlight_off, + NULL, +}; + +static void subghz_scene_receiver_update_statusbar(void* context) { + SubGhz* subghz = context; + FuriString* history_stat_str = furi_string_alloc(); + if(!subghz_history_get_text_space_left(subghz->history, history_stat_str)) { + FuriString* frequency_str = furi_string_alloc(); + FuriString* modulation_str = furi_string_alloc(); + + subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str); + + subghz_view_receiver_add_data_statusbar( + subghz->subghz_receiver, + furi_string_get_cstr(frequency_str), + furi_string_get_cstr(modulation_str), + furi_string_get_cstr(history_stat_str)); + + furi_string_free(frequency_str); + furi_string_free(modulation_str); + } else { + subghz_view_receiver_add_data_statusbar( + subghz->subghz_receiver, furi_string_get_cstr(history_stat_str), "", ""); + subghz->state_notifications = SubGhzNotificationStateIDLE; + } + furi_string_free(history_stat_str); + + subghz_view_receiver_set_radio_device_type( + subghz->subghz_receiver, subghz_txrx_radio_device_get(subghz->txrx)); +} + +void subghz_scene_receiver_callback(SubGhzCustomEvent event, void* context) { + furi_assert(context); + SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, event); +} + +static void subghz_scene_add_to_history_callback( + SubGhzReceiver* receiver, + SubGhzProtocolDecoderBase* decoder_base, + void* context) { + furi_assert(context); + SubGhz* subghz = context; + SubGhzHistory* history = subghz->history; + FuriString* str_buff = furi_string_alloc(); + + SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); + + if(subghz_history_add_to_history(history, decoder_base, &preset)) { + furi_string_reset(str_buff); + + subghz->state_notifications = SubGhzNotificationStateRxDone; + uint16_t item_history = subghz_history_get_item(history); + subghz_history_get_text_item_menu(history, str_buff, item_history - 1); + subghz_view_receiver_add_item_to_menu( + subghz->subghz_receiver, + furi_string_get_cstr(str_buff), + subghz_history_get_type_protocol(history, item_history - 1)); + + subghz_scene_receiver_update_statusbar(subghz); + } + subghz_receiver_reset(receiver); + furi_string_free(str_buff); + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); +} + +void subghz_scene_receiver_on_enter(void* context) { + SubGhz* subghz = context; + SubGhzHistory* history = subghz->history; + + FuriString* str_buff; + str_buff = furi_string_alloc(); + + if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateIDLE) { + subghz_set_default_preset(subghz); + subghz_history_reset(history); + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); + } + + subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz_is_locked(subghz)); + + //Load history to receiver + subghz_view_receiver_exit(subghz->subghz_receiver); + for(uint8_t i = 0; i < subghz_history_get_item(history); i++) { + furi_string_reset(str_buff); + subghz_history_get_text_item_menu(history, str_buff, i); + subghz_view_receiver_add_item_to_menu( + subghz->subghz_receiver, + furi_string_get_cstr(str_buff), + subghz_history_get_type_protocol(history, i)); + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); + } + furi_string_free(str_buff); + + subghz_view_receiver_set_callback( + subghz->subghz_receiver, subghz_scene_receiver_callback, subghz); + subghz_txrx_set_rx_calback(subghz->txrx, subghz_scene_add_to_history_callback, subghz); + + subghz->state_notifications = SubGhzNotificationStateRx; + subghz_txrx_rx_start(subghz->txrx); + subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen); + + //to use a universal decoder, we are looking for a link to it + furi_check( + subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, SUBGHZ_PROTOCOL_BIN_RAW_NAME)); + + subghz_scene_receiver_update_statusbar(subghz); + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver); +} + +bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case SubGhzCustomEventViewReceiverBack: + // Stop CC1101 Rx + subghz->state_notifications = SubGhzNotificationStateIDLE; + subghz_txrx_stop(subghz->txrx); + subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); + subghz->idx_menu_chosen = 0; + subghz_txrx_set_rx_calback(subghz->txrx, NULL, subghz); + + if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) { + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateExit); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); + } else { + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); + subghz_set_default_preset(subghz); + scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart); + } + consumed = true; + break; + case SubGhzCustomEventViewReceiverOK: + subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); + dolphin_deed(DolphinDeedSubGhzReceiverInfo); + consumed = true; + break; + case SubGhzCustomEventViewReceiverConfig: + subghz->state_notifications = SubGhzNotificationStateIDLE; + subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig); + consumed = true; + break; + case SubGhzCustomEventViewReceiverOffDisplay: + notification_message(subghz->notifications, &sequence_display_backlight_off); + consumed = true; + break; + case SubGhzCustomEventViewReceiverUnlock: + subghz_unlock(subghz); + consumed = true; + break; + default: + break; + } + } else if(event.type == SceneManagerEventTypeTick) { + if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) { + subghz_txrx_hopper_update(subghz->txrx); + subghz_scene_receiver_update_statusbar(subghz); + } + + SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data( + subghz->threshold_rssi, subghz_txrx_radio_device_get_rssi(subghz->txrx)); + + subghz_receiver_rssi(subghz->subghz_receiver, ret_rssi.rssi); + subghz_protocol_decoder_bin_raw_data_input_rssi( + (SubGhzProtocolDecoderBinRAW*)subghz_txrx_get_decoder(subghz->txrx), ret_rssi.rssi); + + switch(subghz->state_notifications) { + case SubGhzNotificationStateRx: + notification_message(subghz->notifications, &sequence_blink_cyan_10); + break; + case SubGhzNotificationStateRxDone: + if(!subghz_is_locked(subghz)) { + notification_message(subghz->notifications, &subghs_sequence_rx); + } else { + notification_message(subghz->notifications, &subghs_sequence_rx_locked); + } + subghz->state_notifications = SubGhzNotificationStateRx; + break; + default: + break; + } + } + return consumed; +} + +void subghz_scene_receiver_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c new file mode 100644 index 00000000000..55a8f6b44a5 --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -0,0 +1,366 @@ +#include "../subghz_i.h" +#include + +enum SubGhzSettingIndex { + SubGhzSettingIndexFrequency, + SubGhzSettingIndexHopping, + SubGhzSettingIndexModulation, + SubGhzSettingIndexBinRAW, + SubGhzSettingIndexSound, + SubGhzSettingIndexLock, + SubGhzSettingIndexRAWThesholdRSSI, +}; + +#define RAW_THRESHOLD_RSSI_COUNT 11 +const char* const raw_theshold_rssi_text[RAW_THRESHOLD_RSSI_COUNT] = { + "-----", + "-85.0", + "-80.0", + "-75.0", + "-70.0", + "-65.0", + "-60.0", + "-55.0", + "-50.0", + "-45.0", + "-40.0", + +}; +const float raw_theshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = { + -90.0f, + -85.0f, + -80.0f, + -75.0f, + -70.0f, + -65.0f, + -60.0f, + -55.0f, + -50.0f, + -45.0f, + -40.0f, +}; + +#define HOPPING_COUNT 2 +const char* const hopping_text[HOPPING_COUNT] = { + "OFF", + "ON", +}; +const uint32_t hopping_value[HOPPING_COUNT] = { + SubGhzHopperStateOFF, + SubGhzHopperStateRunnig, +}; + +#define SPEAKER_COUNT 2 +const char* const speaker_text[SPEAKER_COUNT] = { + "OFF", + "ON", +}; +const uint32_t speaker_value[SPEAKER_COUNT] = { + SubGhzSpeakerStateShutdown, + SubGhzSpeakerStateEnable, +}; +#define BIN_RAW_COUNT 2 +const char* const bin_raw_text[BIN_RAW_COUNT] = { + "OFF", + "ON", +}; +const uint32_t bin_raw_value[BIN_RAW_COUNT] = { + SubGhzProtocolFlag_Decodable, + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW, +}; + +uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) { + furi_assert(context); + SubGhz* subghz = context; + SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); + + uint8_t index = 0; + for(uint8_t i = 0; i < subghz_setting_get_frequency_count(setting); i++) { + if(value == subghz_setting_get_frequency(setting, i)) { + index = i; + break; + } else { + index = subghz_setting_get_frequency_default_index(setting); + } + } + return index; +} + +uint8_t subghz_scene_receiver_config_next_preset(const char* preset_name, void* context) { + furi_assert(context); + SubGhz* subghz = context; + SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); + + uint8_t index = 0; + for(uint8_t i = 0; i < subghz_setting_get_preset_count(setting); i++) { + if(!strcmp(subghz_setting_get_preset_name(setting, i), preset_name)) { + index = i; + break; + } else { + // index = subghz_setting_get_frequency_default_index(subghz_txrx_get_setting(subghz->txrx)); + } + } + return index; +} + +uint8_t subghz_scene_receiver_config_hopper_value_index( + const uint32_t value, + const uint32_t values[], + uint8_t values_count, + void* context) { + furi_assert(context); + UNUSED(values_count); + SubGhz* subghz = context; + + if(value == values[0]) { + return 0; + } else { + variable_item_set_current_value_text( + (VariableItem*)scene_manager_get_scene_state( + subghz->scene_manager, SubGhzSceneReceiverConfig), + " -----"); + return 1; + } +} + +static void subghz_scene_receiver_config_set_frequency(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); + + if(subghz_txrx_hopper_get_state(subghz->txrx) == SubGhzHopperStateOFF) { + char text_buf[10] = {0}; + uint32_t frequency = subghz_setting_get_frequency(setting, index); + SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); + + snprintf( + text_buf, + sizeof(text_buf), + "%lu.%02lu", + frequency / 1000000, + (frequency % 1000000) / 10000); + variable_item_set_current_value_text(item, text_buf); + subghz_txrx_set_preset( + subghz->txrx, + furi_string_get_cstr(preset.name), + frequency, + preset.data, + preset.data_size); + } else { + variable_item_set_current_value_index( + item, subghz_setting_get_frequency_default_index(setting)); + } +} + +static void subghz_scene_receiver_config_set_preset(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); + + variable_item_set_current_value_text(item, subghz_setting_get_preset_name(setting, index)); + + SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); + subghz_txrx_set_preset( + subghz->txrx, + subghz_setting_get_preset_name(setting, index), + preset.frequency, + subghz_setting_get_preset_data(setting, index), + subghz_setting_get_preset_data_size(setting, index)); +} + +static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); + VariableItem* frequency_item = (VariableItem*)scene_manager_get_scene_state( + subghz->scene_manager, SubGhzSceneReceiverConfig); + + variable_item_set_current_value_text(item, hopping_text[index]); + if(hopping_value[index] == SubGhzHopperStateOFF) { + char text_buf[10] = {0}; + uint32_t frequency = subghz_setting_get_default_frequency(setting); + SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); + + snprintf( + text_buf, + sizeof(text_buf), + "%lu.%02lu", + frequency / 1000000, + (frequency % 1000000) / 10000); + variable_item_set_current_value_text(frequency_item, text_buf); + + subghz_txrx_set_preset( + subghz->txrx, + furi_string_get_cstr(preset.name), + frequency, + preset.data, + preset.data_size); + variable_item_set_current_value_index( + frequency_item, subghz_setting_get_frequency_default_index(setting)); + } else { + variable_item_set_current_value_text(frequency_item, " -----"); + variable_item_set_current_value_index( + frequency_item, subghz_setting_get_frequency_default_index(setting)); + } + + subghz_txrx_hopper_set_state(subghz->txrx, hopping_value[index]); +} + +static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, speaker_text[index]); + subghz_txrx_speaker_set_state(subghz->txrx, speaker_value[index]); +} + +static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, bin_raw_text[index]); + subghz->filter = bin_raw_value[index]; + subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); +} + +static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) { + 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_threshold_rssi_set(subghz->threshold_rssi, raw_theshold_rssi_value[index]); +} + +static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { + furi_assert(context); + SubGhz* subghz = context; + if(index == SubGhzSettingIndexLock) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneSettingLock); + } +} + +void subghz_scene_receiver_config_on_enter(void* context) { + SubGhz* subghz = context; + VariableItem* item; + uint8_t value_index; + SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); + SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); + + item = variable_item_list_add( + subghz->variable_item_list, + "Frequency:", + subghz_setting_get_frequency_count(setting), + subghz_scene_receiver_config_set_frequency, + subghz); + value_index = subghz_scene_receiver_config_next_frequency(preset.frequency, subghz); + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReceiverConfig, (uint32_t)item); + variable_item_set_current_value_index(item, value_index); + char text_buf[10] = {0}; + uint32_t frequency = subghz_setting_get_frequency(setting, value_index); + snprintf( + text_buf, + sizeof(text_buf), + "%lu.%02lu", + frequency / 1000000, + (frequency % 1000000) / 10000); + variable_item_set_current_value_text(item, text_buf); + + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubGhzCustomEventManagerSet) { + item = variable_item_list_add( + subghz->variable_item_list, + "Hopping:", + HOPPING_COUNT, + subghz_scene_receiver_config_set_hopping_running, + subghz); + value_index = subghz_scene_receiver_config_hopper_value_index( + subghz_txrx_hopper_get_state(subghz->txrx), hopping_value, HOPPING_COUNT, subghz); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, hopping_text[value_index]); + } + + item = variable_item_list_add( + subghz->variable_item_list, + "Modulation:", + subghz_setting_get_preset_count(setting), + subghz_scene_receiver_config_set_preset, + subghz); + value_index = + subghz_scene_receiver_config_next_preset(furi_string_get_cstr(preset.name), subghz); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text( + item, subghz_setting_get_preset_name(setting, value_index)); + + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubGhzCustomEventManagerSet) { + item = variable_item_list_add( + subghz->variable_item_list, + "Bin_RAW:", + BIN_RAW_COUNT, + subghz_scene_receiver_config_set_bin_raw, + subghz); + value_index = value_index_uint32(subghz->filter, bin_raw_value, BIN_RAW_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, bin_raw_text[value_index]); + } + + item = variable_item_list_add( + subghz->variable_item_list, + "Sound:", + SPEAKER_COUNT, + subghz_scene_receiver_config_set_speaker, + subghz); + value_index = value_index_uint32( + subghz_txrx_speaker_get_state(subghz->txrx), speaker_value, SPEAKER_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, speaker_text[value_index]); + + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubGhzCustomEventManagerSet) { + variable_item_list_add(subghz->variable_item_list, "Lock Keyboard", 1, NULL, NULL); + variable_item_list_set_enter_callback( + subghz->variable_item_list, + subghz_scene_receiver_config_var_list_enter_callback, + subghz); + } + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == + SubGhzCustomEventManagerSet) { + item = variable_item_list_add( + subghz->variable_item_list, + "RSSI Threshold:", + RAW_THRESHOLD_RSSI_COUNT, + subghz_scene_receiver_config_set_raw_threshold_rssi, + subghz); + value_index = value_index_float( + subghz_threshold_rssi_get(subghz->threshold_rssi), + raw_theshold_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]); + } + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); +} + +bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzCustomEventSceneSettingLock) { + subghz_lock(subghz); + scene_manager_previous_scene(subghz->scene_manager); + consumed = true; + } + } + return consumed; +} + +void subghz_scene_receiver_config_on_exit(void* context) { + SubGhz* subghz = context; + variable_item_list_set_selected_item(subghz->variable_item_list, 0); + variable_item_list_reset(subghz->variable_item_list); + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); +} diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c new file mode 100644 index 00000000000..7180bb3a403 --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -0,0 +1,173 @@ +#include "../subghz_i.h" +#include "../helpers/subghz_custom_event.h" + +void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + SubGhz* subghz = context; + + if((result == GuiButtonTypeCenter) && (type == InputTypePress)) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoTxStart); + } else if((result == GuiButtonTypeCenter) && (type == InputTypeRelease)) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoTxStop); + } else if((result == GuiButtonTypeRight) && (type == InputTypeShort)) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoSave); + } +} + +static bool subghz_scene_receiver_info_update_parser(void* context) { + SubGhz* subghz = context; + + if(subghz_txrx_load_decoder_by_name_protocol( + subghz->txrx, + subghz_history_get_protocol_name(subghz->history, subghz->idx_menu_chosen))) { + // 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_get_decoder(subghz->txrx), + subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen)); + + SubGhzRadioPreset* preset = + subghz_history_get_radio_preset(subghz->history, subghz->idx_menu_chosen); + subghz_txrx_set_preset( + subghz->txrx, + furi_string_get_cstr(preset->name), + preset->frequency, + preset->data, + preset->data_size); + + return true; + } + return false; +} + +void subghz_scene_receiver_info_on_enter(void* context) { + SubGhz* subghz = context; + + if(subghz_scene_receiver_info_update_parser(subghz)) { + FuriString* frequency_str = furi_string_alloc(); + FuriString* modulation_str = furi_string_alloc(); + FuriString* text = furi_string_alloc(); + + subghz_txrx_get_frequency_and_modulation(subghz->txrx, frequency_str, modulation_str); + widget_add_string_element( + subghz->widget, + 78, + 0, + AlignLeft, + AlignTop, + FontSecondary, + furi_string_get_cstr(frequency_str)); + + widget_add_string_element( + subghz->widget, + 113, + 0, + AlignLeft, + AlignTop, + FontSecondary, + furi_string_get_cstr(modulation_str)); + subghz_protocol_decoder_base_get_string(subghz_txrx_get_decoder(subghz->txrx), text); + widget_add_string_multiline_element( + subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(text)); + + furi_string_free(frequency_str); + furi_string_free(modulation_str); + furi_string_free(text); + + if(subghz_txrx_protocol_is_serializable(subghz->txrx)) { + widget_add_button_element( + subghz->widget, + GuiButtonTypeRight, + "Save", + subghz_scene_receiver_info_callback, + subghz); + } + if(subghz_txrx_protocol_is_transmittable(subghz->txrx, true)) { + widget_add_button_element( + subghz->widget, + GuiButtonTypeCenter, + "Send", + subghz_scene_receiver_info_callback, + subghz); + } + } else { + widget_add_icon_element(subghz->widget, 37, 15, &I_DolphinCommon_56x48); + widget_add_string_element( + subghz->widget, 13, 8, AlignLeft, AlignBottom, FontSecondary, "Error history parse."); + } + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); +} + +bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzCustomEventSceneReceiverInfoTxStart) { + if(!subghz_scene_receiver_info_update_parser(subghz)) { + return false; + } + //CC1101 Stop RX -> Start TX + subghz_txrx_hopper_pause(subghz->txrx); + if(!subghz_tx_start( + subghz, + subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen))) { + subghz_txrx_rx_start(subghz->txrx); + subghz_txrx_hopper_unpause(subghz->txrx); + subghz->state_notifications = SubGhzNotificationStateRx; + } else { + subghz->state_notifications = SubGhzNotificationStateTx; + } + return true; + } else if(event.event == SubGhzCustomEventSceneReceiverInfoTxStop) { + //CC1101 Stop Tx -> Start RX + subghz->state_notifications = SubGhzNotificationStateIDLE; + + subghz_txrx_rx_start(subghz->txrx); + + subghz_txrx_hopper_unpause(subghz->txrx); + subghz->state_notifications = SubGhzNotificationStateRx; + return true; + } else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) { + //CC1101 Stop RX -> Save + subghz->state_notifications = SubGhzNotificationStateIDLE; + subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); + + subghz_txrx_stop(subghz->txrx); + if(!subghz_scene_receiver_info_update_parser(subghz)) { + return false; + } + + if(subghz_txrx_protocol_is_serializable(subghz->txrx)) { + subghz_file_name_clear(subghz); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); + } + return true; + } + } else if(event.type == SceneManagerEventTypeTick) { + if(subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF) { + subghz_txrx_hopper_update(subghz->txrx); + } + switch(subghz->state_notifications) { + case SubGhzNotificationStateTx: + notification_message(subghz->notifications, &sequence_blink_magenta_10); + break; + case SubGhzNotificationStateRx: + notification_message(subghz->notifications, &sequence_blink_cyan_10); + break; + case SubGhzNotificationStateRxDone: + notification_message(subghz->notifications, &sequence_blink_green_100); + subghz->state_notifications = SubGhzNotificationStateRx; + break; + default: + break; + } + } + return false; +} + +void subghz_scene_receiver_info_on_exit(void* context) { + SubGhz* subghz = context; + widget_reset(subghz->widget); +} diff --git a/applications/main/subghz/scenes/subghz_scene_region_info.c b/applications/main/subghz/scenes/subghz_scene_region_info.c new file mode 100644 index 00000000000..b98394af072 --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_region_info.c @@ -0,0 +1,38 @@ +#include "../subghz_i.h" + +#include + +void subghz_scene_region_info_on_enter(void* context) { + SubGhz* subghz = context; + const FuriHalRegion* const region = furi_hal_region_get(); + FuriString* buffer = furi_string_alloc(); + if(region) { + furi_string_cat_printf(buffer, "Region: %s, bands:\n", region->country_code); + for(uint16_t i = 0; i < region->bands_count; ++i) { + furi_string_cat_printf( + buffer, + " %lu-%lu kHz\n", + region->bands[i].start / 1000, + region->bands[i].end / 1000); + } + } else { + furi_string_cat_printf(buffer, "Region: N/A\n"); + } + + widget_add_string_multiline_element( + subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(buffer)); + + furi_string_free(buffer); + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); +} + +bool subghz_scene_region_info_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void subghz_scene_region_info_on_exit(void* context) { + SubGhz* subghz = context; + widget_reset(subghz->widget); +} diff --git a/applications/main/subghz/scenes/subghz_scene_rpc.c b/applications/main/subghz/scenes/subghz_scene_rpc.c new file mode 100644 index 00000000000..f8bf066d549 --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_rpc.c @@ -0,0 +1,117 @@ +#include "../subghz_i.h" + +typedef enum { + SubGhzRpcStateIdle, + SubGhzRpcStateLoaded, + SubGhzRpcStateTx, +} SubGhzRpcState; + +void subghz_scene_rpc_on_enter(void* context) { + SubGhz* subghz = context; + Popup* popup = subghz->popup; + + popup_set_header(popup, "Sub-GHz", 89, 42, AlignCenter, AlignBottom); + popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop); + + popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61); + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdPopup); + + scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateIdle); + + notification_message(subghz->notifications, &sequence_display_backlight_on); +} + +bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + Popup* popup = subghz->popup; + bool consumed = false; + SubGhzRpcState state = scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneRpc); + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == SubGhzCustomEventSceneExit) { + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + rpc_system_app_confirm(subghz->rpc_ctx, true); + } else if(event.event == SubGhzCustomEventSceneRpcSessionClose) { + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + } else if(event.event == SubGhzCustomEventSceneRpcButtonPress) { + bool result = false; + if(state == SubGhzRpcStateLoaded) { + switch( + subghz_txrx_tx_start(subghz->txrx, subghz_txrx_get_fff_data(subghz->txrx))) { + case SubGhzTxRxStartTxStateErrorOnlyRx: + rpc_system_app_set_error_code(subghz->rpc_ctx, SubGhzErrorTypeOnlyRX); + rpc_system_app_set_error_text( + subghz->rpc_ctx, + "Transmission on this frequency is restricted in your region"); + break; + case SubGhzTxRxStartTxStateErrorParserOthers: + rpc_system_app_set_error_code(subghz->rpc_ctx, SubGhzErrorTypeParserOthers); + rpc_system_app_set_error_text( + subghz->rpc_ctx, "Error in protocol parameters description"); + break; + + default: //if(SubGhzTxRxStartTxStateOk) + result = true; + subghz_blink_start(subghz); + state = SubGhzRpcStateTx; + break; + } + } + rpc_system_app_confirm(subghz->rpc_ctx, result); + } else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) { + bool result = false; + if(state == SubGhzRpcStateTx) { + subghz_txrx_stop(subghz->txrx); + subghz_blink_stop(subghz); + result = true; + } + state = SubGhzRpcStateIdle; + rpc_system_app_confirm(subghz->rpc_ctx, result); + } else if(event.event == SubGhzCustomEventSceneRpcLoad) { + bool result = false; + if(state == SubGhzRpcStateIdle) { + if(subghz_key_load(subghz, furi_string_get_cstr(subghz->file_path), false)) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateLoaded); + result = true; + FuriString* file_name; + file_name = furi_string_alloc(); + path_extract_filename(subghz->file_path, file_name, true); + + snprintf( + subghz->file_name_tmp, + SUBGHZ_MAX_LEN_NAME, + "loaded\n%s", + furi_string_get_cstr(file_name)); + popup_set_text(popup, subghz->file_name_tmp, 89, 44, AlignCenter, AlignTop); + + furi_string_free(file_name); + } else { + rpc_system_app_set_error_code(subghz->rpc_ctx, SubGhzErrorTypeParseFile); + rpc_system_app_set_error_text(subghz->rpc_ctx, "Cannot parse file"); + } + } + rpc_system_app_confirm(subghz->rpc_ctx, result); + } + } + return consumed; +} + +void subghz_scene_rpc_on_exit(void* context) { + SubGhz* subghz = context; + SubGhzRpcState state = scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneRpc); + if(state == SubGhzRpcStateTx) { + subghz_txrx_stop(subghz->txrx); + subghz_blink_stop(subghz); + } + + Popup* popup = subghz->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); +} diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c new file mode 100644 index 00000000000..394dda89e51 --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -0,0 +1,171 @@ +#include "../subghz_i.h" +#include "subghz/types.h" +#include "../helpers/subghz_custom_event.h" +#include +#include +#include +#include + +#define MAX_TEXT_INPUT_LEN 22 + +void subghz_scene_save_name_text_input_callback(void* context) { + furi_assert(context); + SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneSaveName); +} + +void subghz_scene_save_name_get_timefilename(FuriString* name) { + FuriHalRtcDateTime datetime = {0}; + furi_hal_rtc_get_datetime(&datetime); + furi_string_printf( + name, + "RAW-%.4d%.2d%.2d-%.2d%.2d%.2d", + datetime.year, + datetime.month, + datetime.day, + datetime.hour, + datetime.minute, + datetime.second); +} + +void subghz_scene_save_name_on_enter(void* context) { + SubGhz* subghz = context; + + // Setup view + TextInput* text_input = subghz->text_input; + bool dev_name_empty = false; + + FuriString* file_name = furi_string_alloc(); + FuriString* dir_name = furi_string_alloc(); + + if(!subghz_path_is_file(subghz->file_path)) { + char file_name_buf[SUBGHZ_MAX_LEN_NAME] = {0}; + + name_generator_make_auto(file_name_buf, SUBGHZ_MAX_LEN_NAME, SUBGHZ_APP_FILENAME_PREFIX); + + furi_string_set(file_name, file_name_buf); + furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER); + //highlighting the entire filename by default + dev_name_empty = true; + } else { + furi_string_set(subghz->file_path_tmp, subghz->file_path); + path_extract_dirname(furi_string_get_cstr(subghz->file_path), dir_name); + path_extract_filename(subghz->file_path, file_name, true); + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubGhzCustomEventManagerNoSet) { + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == + SubGhzCustomEventManagerSetRAW) { + dev_name_empty = true; + subghz_scene_save_name_get_timefilename(file_name); + } + } + furi_string_set(subghz->file_path, dir_name); + } + + strncpy(subghz->file_name_tmp, furi_string_get_cstr(file_name), SUBGHZ_MAX_LEN_NAME); + text_input_set_header_text(text_input, "Name signal"); + text_input_set_result_callback( + text_input, + subghz_scene_save_name_text_input_callback, + subghz, + subghz->file_name_tmp, + MAX_TEXT_INPUT_LEN, + dev_name_empty); + + ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( + furi_string_get_cstr(subghz->file_path), SUBGHZ_APP_FILENAME_EXTENSION, ""); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + furi_string_free(file_name); + furi_string_free(dir_name); + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTextInput); +} + +bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + if(event.type == SceneManagerEventTypeBack) { + if(!strcmp(subghz->file_name_tmp, "") || + scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubGhzCustomEventManagerNoSet) { + furi_string_set(subghz->file_path, subghz->file_path_tmp); + } + scene_manager_previous_scene(subghz->scene_manager); + return true; + } else if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzCustomEventSceneSaveName) { + if(strcmp(subghz->file_name_tmp, "") != 0) { + furi_string_cat_printf( + subghz->file_path, + "/%s%s", + subghz->file_name_tmp, + SUBGHZ_APP_FILENAME_EXTENSION); + if(subghz_path_is_file(subghz->file_path_tmp)) { + if(!subghz_rename_file(subghz)) { + return false; + } + } else { + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType) != + SubGhzCustomEventManagerNoSet) { + subghz_save_protocol_to_file( + subghz, + subghz_txrx_get_fff_data(subghz->txrx), + furi_string_get_cstr(subghz->file_path)); + scene_manager_set_scene_state( + subghz->scene_manager, + SubGhzSceneSetType, + SubGhzCustomEventManagerNoSet); + } else { + subghz_save_protocol_to_file( + subghz, + subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen), + furi_string_get_cstr(subghz->file_path)); + } + } + + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubGhzCustomEventManagerNoSet) { + subghz_protocol_raw_gen_fff_data( + subghz_txrx_get_fff_data(subghz->txrx), + furi_string_get_cstr(subghz->file_path), + subghz_txrx_radio_device_get_name(subghz->txrx)); + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); + } else { + subghz_file_name_clear(subghz); + } + + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess); + if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSavedMenu)) { + // Nothing, do not count editing as saving + } else if(scene_manager_has_previous_scene( + subghz->scene_manager, SubGhzSceneMoreRAW)) { + // Ditto, for RAW signals + } else if(scene_manager_has_previous_scene( + subghz->scene_manager, SubGhzSceneSetType)) { + dolphin_deed(DolphinDeedSubGhzAddManually); + } else { + dolphin_deed(DolphinDeedSubGhzSave); + } + return true; + } else { + furi_string_set(subghz->error_str, "No name file"); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); + return true; + } + } + } + return false; +} + +void subghz_scene_save_name_on_exit(void* context) { + SubGhz* subghz = context; + + // Clear validator + void* validator_context = text_input_get_validator_callback_context(subghz->text_input); + text_input_set_validator(subghz->text_input, NULL, NULL); + validator_is_file_free(validator_context); + + // Clear view + text_input_reset(subghz->text_input); +} diff --git a/applications/subghz/scenes/subghz_scene_save_success.c b/applications/main/subghz/scenes/subghz_scene_save_success.c similarity index 75% rename from applications/subghz/scenes/subghz_scene_save_success.c rename to applications/main/subghz/scenes/subghz_scene_save_success.c index 3d5c16ca3db..40ade5a5350 100644 --- a/applications/subghz/scenes/subghz_scene_save_success.c +++ b/applications/main/subghz/scenes/subghz_scene_save_success.c @@ -1,7 +1,5 @@ #include "../subghz_i.h" #include "../helpers/subghz_custom_event.h" -#include -#include void subghz_scene_save_success_popup_callback(void* context) { SubGhz* subghz = context; @@ -10,7 +8,6 @@ void subghz_scene_save_success_popup_callback(void* context) { void subghz_scene_save_success_on_enter(void* context) { SubGhz* subghz = context; - DOLPHIN_DEED(DolphinDeedSubGhzSave); // Setup view Popup* popup = subghz->popup; @@ -29,10 +26,10 @@ bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event) if(event.event == SubGhzCustomEventSceneSaveSuccess) { if(!scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneReceiver)) { - subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWSave; + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWSave); if(!scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneReadRAW)) { - subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); if(!scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneSaved)) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); @@ -47,14 +44,7 @@ bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event) void subghz_scene_save_success_on_exit(void* context) { SubGhz* subghz = context; - - // Clear view Popup* popup = subghz->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); + + popup_reset(popup); } diff --git a/applications/subghz/scenes/subghz_scene_saved.c b/applications/main/subghz/scenes/subghz_scene_saved.c similarity index 82% rename from applications/subghz/scenes/subghz_scene_saved.c rename to applications/main/subghz/scenes/subghz_scene_saved.c index 62ade3508e7..8b198e3395e 100644 --- a/applications/subghz/scenes/subghz_scene_saved.c +++ b/applications/main/subghz/scenes/subghz_scene_saved.c @@ -4,8 +4,8 @@ void subghz_scene_saved_on_enter(void* context) { SubGhz* subghz = context; if(subghz_load_protocol_from_file(subghz)) { - if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { - subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; + if(subghz_get_load_type_file(subghz) == SubGhzLoadTypeFileRaw) { + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); } else { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSavedMenu); diff --git a/applications/subghz/scenes/subghz_scene_saved_menu.c b/applications/main/subghz/scenes/subghz_scene_saved_menu.c similarity index 100% rename from applications/subghz/scenes/subghz_scene_saved_menu.c rename to applications/main/subghz/scenes/subghz_scene_saved_menu.c diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c new file mode 100644 index 00000000000..f76bd9e27d2 --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -0,0 +1,235 @@ +#include "../subghz_i.h" +#include "../helpers/subghz_txrx_create_protocol_key.h" +#include +#include + +#define TAG "SubGhzSetType" + +void subghz_scene_set_type_submenu_callback(void* context, uint32_t index) { + SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, index); +} + +void subghz_scene_set_type_on_enter(void* context) { + SubGhz* subghz = context; + + submenu_add_item( + subghz->submenu, + "Princeton_433", + SubmenuIndexPricenton_433, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "Princeton_315", + SubmenuIndexPricenton_315, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "Nice Flo 12bit_433", + SubmenuIndexNiceFlo12bit, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "Nice Flo 24bit_433", + SubmenuIndexNiceFlo24bit, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "CAME 12bit_433", + SubmenuIndexCAME12bit, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "CAME 24bit_433", + SubmenuIndexCAME24bit, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "Linear_300", + SubmenuIndexLinear_300_00, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "CAME TWEE", + SubmenuIndexCAMETwee, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "Gate TX_433", + SubmenuIndexGateTX, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "DoorHan_315", + SubmenuIndexDoorHan_315_00, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "DoorHan_433", + SubmenuIndexDoorHan_433_92, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "LiftMaster_315", + SubmenuIndexLiftMaster_315_00, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "LiftMaster_390", + SubmenuIndexLiftMaster_390_00, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "Security+2.0_310", + SubmenuIndexSecPlus_v2_310_00, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "Security+2.0_315", + SubmenuIndexSecPlus_v2_315_00, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "Security+2.0_390", + SubmenuIndexSecPlus_v2_390_00, + subghz_scene_set_type_submenu_callback, + subghz); + + submenu_set_selected_item( + subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType)); + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdMenu); +} + +bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + SubGhzProtocolStatus generated_protocol = SubGhzProtocolStatusError; + + if(event.type == SceneManagerEventTypeCustom) { + uint32_t key = (uint32_t)rand(); + switch(event.event) { + case SubmenuIndexPricenton_433: + key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 + generated_protocol = subghz_txrx_gen_data_protocol_and_te( + subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_PRINCETON_NAME, key, 24, 400); + break; + case SubmenuIndexPricenton_315: + key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 + generated_protocol = subghz_txrx_gen_data_protocol_and_te( + subghz->txrx, "AM650", 315000000, SUBGHZ_PROTOCOL_PRINCETON_NAME, key, 24, 400); + break; + case SubmenuIndexNiceFlo12bit: + key = (key & 0x00000FF0) | 0x1; //btn 0x1, 0x2, 0x4 + generated_protocol = subghz_txrx_gen_data_protocol( + subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 12); + break; + case SubmenuIndexNiceFlo24bit: + key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 + generated_protocol = subghz_txrx_gen_data_protocol( + subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 24); + break; + case SubmenuIndexCAME12bit: + key = (key & 0x00000FF0) | 0x1; //btn 0x1, 0x2, 0x4 + generated_protocol = subghz_txrx_gen_data_protocol( + subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_CAME_NAME, key, 12); + break; + case SubmenuIndexCAME24bit: + key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 + generated_protocol = subghz_txrx_gen_data_protocol( + subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_CAME_NAME, key, 24); + break; + case SubmenuIndexLinear_300_00: + key = (key & 0x3FF); + generated_protocol = subghz_txrx_gen_data_protocol( + subghz->txrx, "AM650", 300000000, SUBGHZ_PROTOCOL_LINEAR_NAME, key, 10); + break; + case SubmenuIndexCAMETwee: + key = (key & 0x0FFFFFF0); + key = 0x003FFF7200000000 | (key ^ 0xE0E0E0EE); + + generated_protocol = subghz_txrx_gen_data_protocol( + subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_CAME_TWEE_NAME, key, 54); + break; + case SubmenuIndexGateTX: + key = (key & 0x00F0FF00) | 0xF << 16 | 0x40; //btn 0xF, 0xC, 0xA, 0x6 (?) + uint64_t rev_key = subghz_protocol_blocks_reverse_key(key, 24); + generated_protocol = subghz_txrx_gen_data_protocol( + subghz->txrx, "AM650", 433920000, SUBGHZ_PROTOCOL_GATE_TX_NAME, rev_key, 24); + break; + case SubmenuIndexDoorHan_433_92: + generated_protocol = subghz_txrx_gen_keeloq_protocol( + subghz->txrx, "AM650", 433920000, "DoorHan", key, 0x2, 0x0003); + if(generated_protocol != SubGhzProtocolStatusOk) { + 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_315_00: + generated_protocol = subghz_txrx_gen_keeloq_protocol( + subghz->txrx, "AM650", 315000000, "DoorHan", key, 0x2, 0x0003); + if(generated_protocol != SubGhzProtocolStatusOk) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + break; + case SubmenuIndexLiftMaster_315_00: + generated_protocol = + subghz_txrx_gen_secplus_v1_protocol(subghz->txrx, "AM650", 315000000); + break; + case SubmenuIndexLiftMaster_390_00: + generated_protocol = + subghz_txrx_gen_secplus_v1_protocol(subghz->txrx, "AM650", 390000000); + break; + case SubmenuIndexSecPlus_v2_310_00: + key = (key & 0x7FFFF3FC); // 850LM pairing + generated_protocol = subghz_txrx_gen_secplus_v2_protocol( + subghz->txrx, "AM650", 310000000, key, 0x68, 0xE500000); + break; + case SubmenuIndexSecPlus_v2_315_00: + key = (key & 0x7FFFF3FC); // 850LM pairing + generated_protocol = subghz_txrx_gen_secplus_v2_protocol( + subghz->txrx, "AM650", 315000000, key, 0x68, 0xE500000); + break; + case SubmenuIndexSecPlus_v2_390_00: + key = (key & 0x7FFFF3FC); // 850LM pairing + generated_protocol = subghz_txrx_gen_secplus_v2_protocol( + subghz->txrx, "AM650", 390000000, key, 0x68, 0xE500000); + break; + default: + return false; + break; + } + + scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneSetType, event.event); + + if(generated_protocol == SubGhzProtocolStatusOk) { + subghz_file_name_clear(subghz); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); + return true; + } + } + + return false; +} + +void subghz_scene_set_type_on_exit(void* context) { + SubGhz* subghz = context; + submenu_reset(subghz->submenu); +} diff --git a/applications/subghz/scenes/subghz_scene_show_error.c b/applications/main/subghz/scenes/subghz_scene_show_error.c similarity index 86% rename from applications/subghz/scenes/subghz_scene_show_error.c rename to applications/main/subghz/scenes/subghz_scene_show_error.c index 5632a859e8a..d52eca9b6cf 100644 --- a/applications/subghz/scenes/subghz_scene_show_error.c +++ b/applications/main/subghz/scenes/subghz_scene_show_error.c @@ -33,7 +33,7 @@ void subghz_scene_show_error_on_enter(void* context) { AlignCenter, AlignCenter, FontSecondary, - string_get_cstr(subghz->error_str)); + furi_string_get_cstr(subghz->error_str)); if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) == SubGhzCustomEventManagerSet) { widget_add_button_element( @@ -50,9 +50,10 @@ void subghz_scene_show_error_on_enter(void* context) { bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; + SubGhzCustomEvent scene_state = + scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError); if(event.type == SceneManagerEventTypeBack) { - if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) == - SubGhzCustomEventManagerSet) { + if(scene_state == SubGhzCustomEventManagerSet) { return false; } else { scene_manager_search_and_switch_to_previous_scene( @@ -61,14 +62,12 @@ bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) { return true; } else if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubGhzCustomEventSceneShowErrorOk) { - if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) == - SubGhzCustomEventManagerSet) { + if(scene_state == SubGhzCustomEventManagerSet) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart); } return true; } else if(event.event == SubGhzCustomEventSceneShowErrorBack) { - if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) == - SubGhzCustomEventManagerSet) { + if(scene_state == SubGhzCustomEventManagerSet) { //exit app if(!scene_manager_previous_scene(subghz->scene_manager)) { scene_manager_stop(subghz->scene_manager); @@ -89,6 +88,6 @@ void subghz_scene_show_error_on_exit(void* context) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneShowError, SubGhzCustomEventManagerNoSet); widget_reset(subghz->widget); - string_reset(subghz->error_str); + furi_string_reset(subghz->error_str); notification_message(subghz->notifications, &sequence_reset_rgb); } diff --git a/applications/subghz/scenes/subghz_scene_show_error_sub.c b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c similarity index 75% rename from applications/subghz/scenes/subghz_scene_show_error_sub.c rename to applications/main/subghz/scenes/subghz_scene_show_error_sub.c index 74e034323e9..113e7ae7463 100644 --- a/applications/subghz/scenes/subghz_scene_show_error_sub.c +++ b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c @@ -12,7 +12,7 @@ void subghz_scene_show_error_sub_on_enter(void* context) { // Setup view Popup* popup = subghz->popup; popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); - popup_set_header(popup, string_get_cstr(subghz->error_str), 14, 15, AlignLeft, AlignTop); + popup_set_header(popup, furi_string_get_cstr(subghz->error_str), 14, 15, AlignLeft, AlignTop); popup_set_timeout(popup, 1500); popup_set_context(popup, subghz); popup_set_callback(popup, subghz_scene_show_error_sub_popup_callback); @@ -36,17 +36,11 @@ bool subghz_scene_show_error_sub_on_event(void* context, SceneManagerEvent event void subghz_scene_show_error_sub_on_exit(void* context) { SubGhz* subghz = context; - - // Clear view Popup* popup = subghz->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); - string_reset(subghz->error_str); + + popup_reset(popup); + + furi_string_reset(subghz->error_str); notification_message(subghz->notifications, &sequence_reset_rgb); } diff --git a/applications/subghz/scenes/subghz_scene_show_only_rx.c b/applications/main/subghz/scenes/subghz_scene_show_only_rx.c similarity index 83% rename from applications/subghz/scenes/subghz_scene_show_only_rx.c rename to applications/main/subghz/scenes/subghz_scene_show_only_rx.c index 3bc08e5b4da..1907c41926c 100644 --- a/applications/subghz/scenes/subghz_scene_show_only_rx.c +++ b/applications/main/subghz/scenes/subghz_scene_show_only_rx.c @@ -43,14 +43,7 @@ bool subghz_scene_show_only_rx_on_event(void* context, SceneManagerEvent event) void subghz_scene_show_only_rx_on_exit(void* context) { SubGhz* subghz = context; - - // Clear view Popup* popup = subghz->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); + + popup_reset(popup); } diff --git a/applications/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c similarity index 76% rename from applications/subghz/scenes/subghz_scene_start.c rename to applications/main/subghz/scenes/subghz_scene_start.c index f37ccae9e42..951c756d843 100644 --- a/applications/subghz/scenes/subghz_scene_start.c +++ b/applications/main/subghz/scenes/subghz_scene_start.c @@ -1,12 +1,14 @@ #include "../subghz_i.h" +#include enum SubmenuIndex { SubmenuIndexRead = 10, SubmenuIndexSaved, - SubmenuIndexTest, - SubmenuIndexAddManualy, + SubmenuIndexAddManually, SubmenuIndexFrequencyAnalyzer, SubmenuIndexReadRAW, + SubmenuIndexShowRegionInfo, + SubmenuIndexRadioSetting, }; void subghz_scene_start_submenu_callback(void* context, uint32_t index) { @@ -32,7 +34,7 @@ void subghz_scene_start_on_enter(void* context) { submenu_add_item( subghz->submenu, "Add Manually", - SubmenuIndexAddManualy, + SubmenuIndexAddManually, subghz_scene_start_submenu_callback, subghz); submenu_add_item( @@ -41,10 +43,18 @@ void subghz_scene_start_on_enter(void* context) { SubmenuIndexFrequencyAnalyzer, subghz_scene_start_submenu_callback, subghz); - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - submenu_add_item( - subghz->submenu, "Test", SubmenuIndexTest, subghz_scene_start_submenu_callback, subghz); - } + submenu_add_item( + subghz->submenu, + "Region Information", + SubmenuIndexShowRegionInfo, + subghz_scene_start_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "Radio Settings", + SubmenuIndexRadioSetting, + subghz_scene_start_submenu_callback, + subghz); submenu_set_selected_item( subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneStart)); @@ -62,7 +72,7 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.event == SubmenuIndexReadRAW) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW); - subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); return true; } else if(event.event == SubmenuIndexRead) { @@ -75,20 +85,26 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { subghz->scene_manager, SubGhzSceneStart, SubmenuIndexSaved); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); return true; - } else if(event.event == SubmenuIndexAddManualy) { + } else if(event.event == SubmenuIndexAddManually) { scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneStart, SubmenuIndexAddManualy); + subghz->scene_manager, SubGhzSceneStart, SubmenuIndexAddManually); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSetType); return true; } else if(event.event == SubmenuIndexFrequencyAnalyzer) { 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 == SubmenuIndexShowRegionInfo) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneStart, SubmenuIndexShowRegionInfo); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneRegionInfo); return true; - } else if(event.event == SubmenuIndexTest) { + } else if(event.event == SubmenuIndexRadioSetting) { scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneStart, SubmenuIndexTest); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTest); + subghz->scene_manager, SubGhzSceneStart, SubmenuIndexRadioSetting); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneRadioSettings); return true; } } diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c new file mode 100644 index 00000000000..f83e44a0a1d --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -0,0 +1,94 @@ +#include "../subghz_i.h" +#include "../views/transmitter.h" +#include + +void subghz_scene_transmitter_callback(SubGhzCustomEvent event, void* context) { + furi_assert(context); + SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, event); +} + +bool subghz_scene_transmitter_update_data_show(void* context) { + SubGhz* subghz = context; + bool ret = false; + SubGhzProtocolDecoderBase* decoder = subghz_txrx_get_decoder(subghz->txrx); + + if(decoder) { + FuriString* key_str = furi_string_alloc(); + FuriString* frequency_str = furi_string_alloc(); + FuriString* modulation_str = furi_string_alloc(); + + if(subghz_protocol_decoder_base_deserialize( + decoder, subghz_txrx_get_fff_data(subghz->txrx)) == SubGhzProtocolStatusOk) { + subghz_protocol_decoder_base_get_string(decoder, key_str); + + subghz_txrx_get_frequency_and_modulation(subghz->txrx, 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), + subghz_txrx_protocol_is_transmittable(subghz->txrx, false)); + ret = true; + } + furi_string_free(frequency_str); + furi_string_free(modulation_str); + furi_string_free(key_str); + } + subghz_view_transmitter_set_radio_device_type( + subghz->subghz_transmitter, subghz_txrx_radio_device_get(subghz->txrx)); + return ret; +} + +void subghz_scene_transmitter_on_enter(void* context) { + SubGhz* subghz = context; + if(!subghz_scene_transmitter_update_data_show(subghz)) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventViewTransmitterError); + } + + subghz_view_transmitter_set_callback( + subghz->subghz_transmitter, subghz_scene_transmitter_callback, subghz); + + subghz->state_notifications = SubGhzNotificationStateIDLE; + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTransmitter); +} + +bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubGhzCustomEventViewTransmitterSendStart) { + subghz->state_notifications = SubGhzNotificationStateIDLE; + + if(subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) { + subghz->state_notifications = SubGhzNotificationStateTx; + subghz_scene_transmitter_update_data_show(subghz); + dolphin_deed(DolphinDeedSubGhzSend); + } + return true; + } else if(event.event == SubGhzCustomEventViewTransmitterSendStop) { + subghz->state_notifications = SubGhzNotificationStateIDLE; + subghz_txrx_stop(subghz->txrx); + return true; + } else if(event.event == SubGhzCustomEventViewTransmitterBack) { + subghz->state_notifications = SubGhzNotificationStateIDLE; + scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart); + return true; + } else if(event.event == SubGhzCustomEventViewTransmitterError) { + furi_string_set(subghz->error_str, "Protocol not\nfound!"); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); + } + } else if(event.type == SceneManagerEventTypeTick) { + if(subghz->state_notifications == SubGhzNotificationStateTx) { + notification_message(subghz->notifications, &sequence_blink_magenta_10); + } + return true; + } + return false; +} + +void subghz_scene_transmitter_on_exit(void* context) { + SubGhz* subghz = context; + subghz->state_notifications = SubGhzNotificationStateIDLE; +} diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c new file mode 100644 index 00000000000..69a72e95d63 --- /dev/null +++ b/applications/main/subghz/subghz.c @@ -0,0 +1,304 @@ +/* Abandon hope, all ye who enter here. */ + +#include "subghz_i.h" + +bool subghz_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + SubGhz* subghz = context; + return scene_manager_handle_custom_event(subghz->scene_manager, event); +} + +bool subghz_back_event_callback(void* context) { + furi_assert(context); + SubGhz* subghz = context; + return scene_manager_handle_back_event(subghz->scene_manager); +} + +void subghz_tick_event_callback(void* context) { + furi_assert(context); + SubGhz* subghz = context; + scene_manager_handle_tick_event(subghz->scene_manager); +} + +static void subghz_rpc_command_callback(const RpcAppSystemEvent* event, void* context) { + furi_assert(context); + SubGhz* subghz = context; + + furi_assert(subghz->rpc_ctx); + + if(event->type == RpcAppEventTypeSessionClose) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneRpcSessionClose); + rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); + subghz->rpc_ctx = NULL; + } else if(event->type == RpcAppEventTypeAppExit) { + view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); + } else if(event->type == RpcAppEventTypeLoadFile) { + furi_assert(event->data.type == RpcAppSystemEventDataTypeString); + furi_string_set(subghz->file_path, event->data.string); + view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneRpcLoad); + } else if(event->type == RpcAppEventTypeButtonPress) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonPress); + } else if(event->type == RpcAppEventTypeButtonRelease) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonRelease); + } else { + rpc_system_app_confirm(subghz->rpc_ctx, false); + } +} + +SubGhz* subghz_alloc() { + SubGhz* subghz = malloc(sizeof(SubGhz)); + + subghz->file_path = furi_string_alloc(); + subghz->file_path_tmp = furi_string_alloc(); + + // GUI + subghz->gui = furi_record_open(RECORD_GUI); + + // View Dispatcher + subghz->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(subghz->view_dispatcher); + + subghz->scene_manager = scene_manager_alloc(&subghz_scene_handlers, subghz); + view_dispatcher_set_event_callback_context(subghz->view_dispatcher, subghz); + view_dispatcher_set_custom_event_callback( + subghz->view_dispatcher, subghz_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + subghz->view_dispatcher, subghz_back_event_callback); + view_dispatcher_set_tick_event_callback( + subghz->view_dispatcher, subghz_tick_event_callback, 100); + + // Open Notification record + subghz->notifications = furi_record_open(RECORD_NOTIFICATION); + + // SubMenu + subghz->submenu = submenu_alloc(); + view_dispatcher_add_view( + subghz->view_dispatcher, SubGhzViewIdMenu, submenu_get_view(subghz->submenu)); + + // Receiver + subghz->subghz_receiver = subghz_view_receiver_alloc(); + view_dispatcher_add_view( + subghz->view_dispatcher, + SubGhzViewIdReceiver, + subghz_view_receiver_get_view(subghz->subghz_receiver)); + + // Popup + subghz->popup = popup_alloc(); + view_dispatcher_add_view( + subghz->view_dispatcher, SubGhzViewIdPopup, popup_get_view(subghz->popup)); + + // Text Input + subghz->text_input = text_input_alloc(); + view_dispatcher_add_view( + subghz->view_dispatcher, SubGhzViewIdTextInput, text_input_get_view(subghz->text_input)); + + // Custom Widget + subghz->widget = widget_alloc(); + view_dispatcher_add_view( + subghz->view_dispatcher, SubGhzViewIdWidget, widget_get_view(subghz->widget)); + + //Dialog + subghz->dialogs = furi_record_open(RECORD_DIALOGS); + + // Transmitter + subghz->subghz_transmitter = subghz_view_transmitter_alloc(); + view_dispatcher_add_view( + subghz->view_dispatcher, + SubGhzViewIdTransmitter, + subghz_view_transmitter_get_view(subghz->subghz_transmitter)); + + // Variable Item List + subghz->variable_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + subghz->view_dispatcher, + SubGhzViewIdVariableItemList, + variable_item_list_get_view(subghz->variable_item_list)); + + // Frequency Analyzer + subghz->subghz_frequency_analyzer = subghz_frequency_analyzer_alloc(); + view_dispatcher_add_view( + subghz->view_dispatcher, + SubGhzViewIdFrequencyAnalyzer, + subghz_frequency_analyzer_get_view(subghz->subghz_frequency_analyzer)); + + // Read RAW + subghz->subghz_read_raw = subghz_read_raw_alloc(); + view_dispatcher_add_view( + subghz->view_dispatcher, + SubGhzViewIdReadRAW, + subghz_read_raw_get_view(subghz->subghz_read_raw)); + + //init threshold rssi + subghz->threshold_rssi = subghz_threshold_rssi_alloc(); + + subghz_unlock(subghz); + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateIDLE); + subghz->history = subghz_history_alloc(); + subghz->filter = SubGhzProtocolFlag_Decodable; + + //init TxRx & History & KeyBoard + subghz->txrx = subghz_txrx_alloc(); + subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); + subghz_txrx_set_need_save_callback(subghz->txrx, subghz_save_to_file, subghz); + + //Init Error_str + subghz->error_str = furi_string_alloc(); + + return subghz; +} + +void subghz_free(SubGhz* subghz) { + furi_assert(subghz); + + if(subghz->rpc_ctx) { + rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); + rpc_system_app_send_exited(subghz->rpc_ctx); + subghz_blink_stop(subghz); + subghz->rpc_ctx = NULL; + } + + subghz_txrx_speaker_off(subghz->txrx); + subghz_txrx_stop(subghz->txrx); + subghz_txrx_sleep(subghz->txrx); + + // Receiver + view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdReceiver); + subghz_view_receiver_free(subghz->subghz_receiver); + + // TextInput + view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTextInput); + text_input_free(subghz->text_input); + + // Custom Widget + view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdWidget); + widget_free(subghz->widget); + + //Dialog + furi_record_close(RECORD_DIALOGS); + + // Transmitter + view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTransmitter); + subghz_view_transmitter_free(subghz->subghz_transmitter); + + // Variable Item List + view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); + variable_item_list_free(subghz->variable_item_list); + + // Frequency Analyzer + view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdFrequencyAnalyzer); + subghz_frequency_analyzer_free(subghz->subghz_frequency_analyzer); + + // Read RAW + view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdReadRAW); + subghz_read_raw_free(subghz->subghz_read_raw); + + // Submenu + view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdMenu); + submenu_free(subghz->submenu); + + // Popup + view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdPopup); + popup_free(subghz->popup); + + // Scene manager + scene_manager_free(subghz->scene_manager); + + // View Dispatcher + view_dispatcher_free(subghz->view_dispatcher); + + // GUI + furi_record_close(RECORD_GUI); + subghz->gui = NULL; + + // threshold rssi + subghz_threshold_rssi_free(subghz->threshold_rssi); + + //Worker & Protocol & History + subghz_history_free(subghz->history); + + //TxRx + subghz_txrx_free(subghz->txrx); + + //Error string + furi_string_free(subghz->error_str); + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + subghz->notifications = NULL; + + // Path strings + furi_string_free(subghz->file_path); + furi_string_free(subghz->file_path_tmp); + + // The rest + free(subghz); +} + +int32_t subghz_app(void* p) { + SubGhz* subghz = subghz_alloc(); + + if(!furi_hal_region_is_provisioned()) { + subghz_dialog_message_show_only_rx(subghz); + subghz_free(subghz); + return 1; + } + + // Check argument and run corresponding scene + if(p && strlen(p)) { + uint32_t rpc_ctx = 0; + if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { + subghz->rpc_ctx = (void*)rpc_ctx; + rpc_system_app_set_callback(subghz->rpc_ctx, subghz_rpc_command_callback, subghz); + rpc_system_app_send_started(subghz->rpc_ctx); + view_dispatcher_attach_to_gui( + subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeDesktop); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneRpc); + } else { + view_dispatcher_attach_to_gui( + subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen); + if(subghz_key_load(subghz, p, true)) { + furi_string_set(subghz->file_path, (const char*)p); + + if(subghz_get_load_type_file(subghz) == SubGhzLoadTypeFileRaw) { + //Load Raw TX + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateRAWLoad); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); + } else { + //Load transmitter TX + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter); + } + } else { + //exit app + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); + } + } + } else { + view_dispatcher_attach_to_gui( + subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen); + furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER); + if(subghz_txrx_is_database_loaded(subghz->txrx)) { + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart); + } else { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneShowError, SubGhzCustomEventManagerSet); + furi_string_set( + subghz->error_str, + "No SD card or\ndatabase found.\nSome app function\nmay be reduced."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + } + + furi_hal_power_suppress_charge_enter(); + + view_dispatcher_run(subghz->view_dispatcher); + + furi_hal_power_suppress_charge_exit(); + + subghz_free(subghz); + + return 0; +} diff --git a/applications/subghz/subghz.h b/applications/main/subghz/subghz.h similarity index 100% rename from applications/subghz/subghz.h rename to applications/main/subghz/subghz.h diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c new file mode 100644 index 00000000000..e1b5e868414 --- /dev/null +++ b/applications/main/subghz/subghz_cli.c @@ -0,0 +1,1047 @@ +#include "subghz_cli.h" + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "helpers/subghz_chat.h" + +#include +#include + +#include +#include + +#define SUBGHZ_FREQUENCY_RANGE_STR \ + "299999755...348000000 or 386999938...464000000 or 778999847...928000000" + +#define SUBGHZ_REGION_FILENAME "/int/.region_data" + +#define TAG "SubGhzCli" + +static void subghz_cli_radio_device_power_on() { + uint8_t attempts = 5; + while(--attempts > 0) { + if(furi_hal_power_enable_otg()) break; + } + if(attempts == 0) { + if(furi_hal_power_get_usb_voltage() < 4.5f) { + FURI_LOG_E( + "TAG", + "Error power otg enable. BQ2589 check otg fault = %d", + furi_hal_power_check_otg_fault() ? 1 : 0); + } + } +} + +static void subghz_cli_radio_device_power_off() { + if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); +} + +void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + uint32_t frequency = 433920000; + + if(furi_string_size(args)) { + int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency); + if(ret != 1) { + printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); + cli_print_usage("subghz tx_carrier", "", furi_string_get_cstr(args)); + return; + } + if(!furi_hal_subghz_is_frequency_valid(frequency)) { + printf( + "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", + frequency); + return; + } + } + + furi_hal_subghz_reset(); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); + frequency = furi_hal_subghz_set_frequency_and_path(frequency); + + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_cc1101_g0, true); + + furi_hal_power_suppress_charge_enter(); + + if(furi_hal_subghz_tx()) { + printf("Transmitting at frequency %lu Hz\r\n", frequency); + printf("Press CTRL+C to stop\r\n"); + while(!cli_cmd_interrupt_received(cli)) { + furi_delay_ms(250); + } + } else { + printf("This frequency can only be used for RX in your region\r\n"); + } + + furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); + furi_hal_subghz_sleep(); + + furi_hal_power_suppress_charge_exit(); +} + +void subghz_cli_command_rx_carrier(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + uint32_t frequency = 433920000; + + if(furi_string_size(args)) { + int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency); + if(ret != 1) { + printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); + cli_print_usage("subghz rx_carrier", "", furi_string_get_cstr(args)); + return; + } + if(!furi_hal_subghz_is_frequency_valid(frequency)) { + printf( + "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", + frequency); + return; + } + } + + furi_hal_subghz_reset(); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); + frequency = furi_hal_subghz_set_frequency_and_path(frequency); + printf("Receiving at frequency %lu Hz\r\n", frequency); + printf("Press CTRL+C to stop\r\n"); + + furi_hal_power_suppress_charge_enter(); + + furi_hal_subghz_rx(); + + while(!cli_cmd_interrupt_received(cli)) { + furi_delay_ms(250); + printf("RSSI: %03.1fdbm\r", (double)furi_hal_subghz_get_rssi()); + fflush(stdout); + } + + furi_hal_power_suppress_charge_exit(); + + furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); + furi_hal_subghz_sleep(); +} + +static const SubGhzDevice* subghz_cli_command_get_device(uint32_t* device_ind) { + const SubGhzDevice* device = NULL; + switch(*device_ind) { + case 1: + subghz_cli_radio_device_power_on(); + device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME); + break; + + default: + device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME); + break; + } + //check if the device is connected + if(!subghz_devices_is_connect(device)) { + subghz_cli_radio_device_power_off(); + device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME); + *device_ind = 0; + } + return device; +} + +void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + uint32_t frequency = 433920000; + uint32_t key = 0x0074BADE; + uint32_t repeat = 10; + uint32_t te = 403; + uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT + + if(furi_string_size(args)) { + int ret = sscanf( + furi_string_get_cstr(args), + "%lx %lu %lu %lu %lu", + &key, + &frequency, + &te, + &repeat, + &device_ind); + if(ret != 5) { + printf( + "sscanf returned %d, key: %lx, frequency: %lu, te: %lu, repeat: %lu, device: %lu\r\n ", + ret, + key, + frequency, + te, + repeat, + device_ind); + cli_print_usage( + "subghz tx", + "<3 Byte Key: in hex> ", + furi_string_get_cstr(args)); + return; + } + } + subghz_devices_init(); + const SubGhzDevice* device = subghz_cli_command_get_device(&device_ind); + if(!subghz_devices_is_frequency_valid(device, frequency)) { + printf( + "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency); + subghz_devices_deinit(); + subghz_cli_radio_device_power_off(); + return; + } + printf( + "Transmitting at %lu, key %lx, te %lu, repeat %lu device %lu. Press CTRL+C to stop\r\n", + frequency, + key, + te, + repeat, + device_ind); + + FuriString* flipper_format_string = furi_string_alloc_printf( + "Protocol: Princeton\n" + "Bit: 24\n" + "Key: 00 00 00 00 00 %02X %02X %02X\n" + "TE: %lu\n" + "Repeat: %lu\n", + (uint8_t)((key >> 16) & 0xFFU), + (uint8_t)((key >> 8) & 0xFFU), + (uint8_t)(key & 0xFFU), + te, + repeat); + FlipperFormat* flipper_format = flipper_format_string_alloc(); + Stream* stream = flipper_format_get_raw_stream(flipper_format); + stream_clean(stream); + stream_write_cstring(stream, furi_string_get_cstr(flipper_format_string)); + + SubGhzEnvironment* environment = subghz_environment_alloc(); + subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); + + SubGhzTransmitter* transmitter = subghz_transmitter_alloc_init(environment, "Princeton"); + subghz_transmitter_deserialize(transmitter, flipper_format); + + subghz_devices_begin(device); + subghz_devices_reset(device); + subghz_devices_load_preset(device, FuriHalSubGhzPresetOok650Async, NULL); + frequency = subghz_devices_set_frequency(device, frequency); + + furi_hal_power_suppress_charge_enter(); + if(subghz_devices_start_async_tx(device, subghz_transmitter_yield, transmitter)) { + while(!(subghz_devices_is_async_complete_tx(device) || cli_cmd_interrupt_received(cli))) { + printf("."); + fflush(stdout); + furi_delay_ms(333); + } + subghz_devices_stop_async_tx(device); + + } else { + printf("Transmission on this frequency is restricted in your region\r\n"); + } + + subghz_devices_sleep(device); + subghz_devices_end(device); + subghz_devices_deinit(); + subghz_cli_radio_device_power_off(); + + furi_hal_power_suppress_charge_exit(); + + flipper_format_free(flipper_format); + subghz_transmitter_free(transmitter); + subghz_environment_free(environment); +} + +typedef struct { + volatile bool overrun; + FuriStreamBuffer* stream; + size_t packet_count; +} SubGhzCliCommandRx; + +static void subghz_cli_command_rx_capture_callback(bool level, uint32_t duration, void* context) { + SubGhzCliCommandRx* instance = context; + + LevelDuration level_duration = level_duration_make(level, duration); + if(instance->overrun) { + instance->overrun = false; + level_duration = level_duration_reset(); + } + size_t ret = + furi_stream_buffer_send(instance->stream, &level_duration, sizeof(LevelDuration), 0); + if(sizeof(LevelDuration) != ret) instance->overrun = true; +} + +static void subghz_cli_command_rx_callback( + SubGhzReceiver* receiver, + SubGhzProtocolDecoderBase* decoder_base, + void* context) { + SubGhzCliCommandRx* instance = context; + instance->packet_count++; + + FuriString* text; + text = furi_string_alloc(); + subghz_protocol_decoder_base_get_string(decoder_base, text); + subghz_receiver_reset(receiver); + printf("%s", furi_string_get_cstr(text)); + furi_string_free(text); +} + +void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + uint32_t frequency = 433920000; + uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT + + if(furi_string_size(args)) { + int ret = sscanf(furi_string_get_cstr(args), "%lu %lu", &frequency, &device_ind); + if(ret != 2) { + printf( + "sscanf returned %d, frequency: %lu device: %lu\r\n", ret, frequency, device_ind); + cli_print_usage( + "subghz rx", + " ", + furi_string_get_cstr(args)); + return; + } + } + subghz_devices_init(); + const SubGhzDevice* device = subghz_cli_command_get_device(&device_ind); + if(!subghz_devices_is_frequency_valid(device, frequency)) { + printf( + "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency); + subghz_devices_deinit(); + subghz_cli_radio_device_power_off(); + return; + } + + // Allocate context and buffers + SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); + instance->stream = + furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration)); + furi_check(instance->stream); + + SubGhzEnvironment* environment = subghz_environment_alloc(); + subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME); + subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME); + subghz_environment_set_came_atomo_rainbow_table_file_name( + environment, SUBGHZ_CAME_ATOMO_DIR_NAME); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); + subghz_environment_set_nice_flor_s_rainbow_table_file_name( + environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); + subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); + + SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); + subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); + subghz_receiver_set_rx_callback(receiver, subghz_cli_command_rx_callback, instance); + + // Configure radio + subghz_devices_begin(device); + subghz_devices_reset(device); + subghz_devices_load_preset(device, FuriHalSubGhzPresetOok650Async, NULL); + frequency = subghz_devices_set_frequency(device, frequency); + + furi_hal_power_suppress_charge_enter(); + + // Prepare and start RX + subghz_devices_start_async_rx(device, subghz_cli_command_rx_capture_callback, instance); + + // Wait for packets to arrive + printf( + "Listening at frequency: %lu device: %lu. Press CTRL+C to stop\r\n", + frequency, + device_ind); + LevelDuration level_duration; + while(!cli_cmd_interrupt_received(cli)) { + int ret = furi_stream_buffer_receive( + instance->stream, &level_duration, sizeof(LevelDuration), 10); + if(ret == sizeof(LevelDuration)) { + if(level_duration_is_reset(level_duration)) { + printf("."); + subghz_receiver_reset(receiver); + } else { + bool level = level_duration_get_level(level_duration); + uint32_t duration = level_duration_get_duration(level_duration); + subghz_receiver_decode(receiver, level, duration); + } + } + } + + // Shutdown radio + subghz_devices_stop_async_rx(device); + subghz_devices_sleep(device); + subghz_devices_end(device); + subghz_devices_deinit(); + subghz_cli_radio_device_power_off(); + + furi_hal_power_suppress_charge_exit(); + + printf("\r\nPackets received %zu\r\n", instance->packet_count); + + // Cleanup + subghz_receiver_free(receiver); + subghz_environment_free(environment); + furi_stream_buffer_free(instance->stream); + free(instance); +} + +void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + uint32_t frequency = 433920000; + + if(furi_string_size(args)) { + int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency); + if(ret != 1) { + printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); + cli_print_usage("subghz rx", "", furi_string_get_cstr(args)); + return; + } + if(!furi_hal_subghz_is_frequency_valid(frequency)) { + printf( + "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", + frequency); + return; + } + } + + // Allocate context and buffers + SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); + instance->stream = + furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration)); + furi_check(instance->stream); + + // Configure radio + furi_hal_subghz_reset(); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_270khz_async_regs); + frequency = furi_hal_subghz_set_frequency_and_path(frequency); + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + + furi_hal_power_suppress_charge_enter(); + + // Prepare and start RX + furi_hal_subghz_start_async_rx(subghz_cli_command_rx_capture_callback, instance); + + // Wait for packets to arrive + printf("Listening at %lu. Press CTRL+C to stop\r\n", frequency); + LevelDuration level_duration; + size_t counter = 0; + while(!cli_cmd_interrupt_received(cli)) { + int ret = furi_stream_buffer_receive( + instance->stream, &level_duration, sizeof(LevelDuration), 10); + if(ret == 0) { + continue; + } + if(ret != sizeof(LevelDuration)) { + puts("stream corrupt"); + break; + } + if(level_duration_is_reset(level_duration)) { + puts(". "); + } else { + bool level = level_duration_get_level(level_duration); + uint32_t duration = level_duration_get_duration(level_duration); + printf("%c%lu ", level ? '+' : '-', duration); + } + furi_thread_stdout_flush(); + counter++; + if(counter > 255) { + puts("\r\n"); + counter = 0; + } + } + + // Shutdown radio + furi_hal_subghz_stop_async_rx(); + furi_hal_subghz_sleep(); + + furi_hal_power_suppress_charge_exit(); + + // Cleanup + furi_stream_buffer_free(instance->stream); + free(instance); +} + +void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + FuriString* file_name; + file_name = furi_string_alloc(); + furi_string_set(file_name, ANY_PATH("subghz/test.sub")); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + FuriString* temp_str; + temp_str = furi_string_alloc(); + uint32_t temp_data32; + bool check_file = false; + + do { + if(furi_string_size(args)) { + if(!args_read_string_and_trim(args, file_name)) { + cli_print_usage( + "subghz decode_raw", "", furi_string_get_cstr(args)); + break; + } + } + + if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) { + printf( + "subghz decode_raw \033[0;31mError open file\033[0m %s\r\n", + furi_string_get_cstr(file_name)); + break; + } + + if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { + printf("subghz decode_raw \033[0;31mMissing or incorrect header\033[0m\r\n"); + break; + } + + if(!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE) && + temp_data32 == SUBGHZ_KEY_FILE_VERSION) { + } else { + printf("subghz decode_raw \033[0;31mType or version mismatch\033[0m\r\n"); + break; + } + + check_file = true; + } while(false); + + furi_string_free(temp_str); + flipper_format_free(fff_data_file); + furi_record_close(RECORD_STORAGE); + + if(check_file) { + // Allocate context + SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); + + SubGhzEnvironment* environment = subghz_environment_alloc(); + if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME)) { + printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;32mOK\033[0m\r\n"); + } else { + printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;31mERROR\033[0m\r\n"); + } + if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME)) { + printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;32mOK\033[0m\r\n"); + } else { + printf( + "SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;31mERROR\033[0m\r\n"); + } + subghz_environment_set_came_atomo_rainbow_table_file_name( + environment, SUBGHZ_CAME_ATOMO_DIR_NAME); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); + subghz_environment_set_nice_flor_s_rainbow_table_file_name( + environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); + subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); + + SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); + subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); + subghz_receiver_set_rx_callback(receiver, subghz_cli_command_rx_callback, instance); + + SubGhzFileEncoderWorker* file_worker_encoder = subghz_file_encoder_worker_alloc(); + if(subghz_file_encoder_worker_start( + file_worker_encoder, furi_string_get_cstr(file_name), NULL)) { + //the worker needs a file in order to open and read part of the file + furi_delay_ms(100); + } + + printf( + "Listening at \033[0;33m%s\033[0m.\r\n\r\nPress CTRL+C to stop\r\n\r\n", + furi_string_get_cstr(file_name)); + + LevelDuration level_duration; + while(!cli_cmd_interrupt_received(cli)) { + furi_delay_us(500); //you need to have time to read from the file from the SD card + level_duration = subghz_file_encoder_worker_get_level_duration(file_worker_encoder); + if(!level_duration_is_reset(level_duration)) { + bool level = level_duration_get_level(level_duration); + uint32_t duration = level_duration_get_duration(level_duration); + subghz_receiver_decode(receiver, level, duration); + } else { + break; + } + } + + printf("\r\nPackets received \033[0;32m%zu\033[0m\r\n", instance->packet_count); + + // Cleanup + subghz_receiver_free(receiver); + subghz_environment_free(environment); + + if(subghz_file_encoder_worker_is_running(file_worker_encoder)) { + subghz_file_encoder_worker_stop(file_worker_encoder); + } + subghz_file_encoder_worker_free(file_worker_encoder); + free(instance); + } + furi_string_free(file_name); +} + +static void subghz_cli_command_print_usage() { + printf("Usage:\r\n"); + printf("subghz \r\n"); + printf("Cmd list:\r\n"); + + printf( + "\tchat \t - Chat with other Flippers\r\n"); + printf( + "\ttx <3 byte Key: in hex> \t - Transmitting key\r\n"); + printf("\trx \t - Receive\r\n"); + printf("\trx_raw \t - Receive RAW\r\n"); + printf("\tdecode_raw \t - Testing\r\n"); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + printf("\r\n"); + printf(" debug cmd:\r\n"); + printf("\ttx_carrier \t - Transmit carrier\r\n"); + printf("\trx_carrier \t - Receive carrier\r\n"); + printf( + "\tencrypt_keeloq \t - Encrypt keeloq manufacture keys\r\n"); + printf( + "\tencrypt_raw \t - Encrypt RAW data\r\n"); + } +} + +static void subghz_cli_command_encrypt_keeloq(Cli* cli, FuriString* args) { + UNUSED(cli); + uint8_t iv[16]; + + FuriString* source; + FuriString* destination; + source = furi_string_alloc(); + destination = furi_string_alloc(); + + SubGhzKeystore* keystore = subghz_keystore_alloc(); + + do { + if(!args_read_string_and_trim(args, source)) { + subghz_cli_command_print_usage(); + break; + } + + if(!args_read_string_and_trim(args, destination)) { + subghz_cli_command_print_usage(); + break; + } + + if(!args_read_hex_bytes(args, iv, 16)) { + subghz_cli_command_print_usage(); + break; + } + + if(!subghz_keystore_load(keystore, furi_string_get_cstr(source))) { + printf("Failed to load Keystore"); + break; + } + + if(!subghz_keystore_save(keystore, furi_string_get_cstr(destination), iv)) { + printf("Failed to save Keystore"); + break; + } + } while(false); + + subghz_keystore_free(keystore); + furi_string_free(destination); + furi_string_free(source); +} + +static void subghz_cli_command_encrypt_raw(Cli* cli, FuriString* args) { + UNUSED(cli); + uint8_t iv[16]; + + FuriString* source; + FuriString* destination; + source = furi_string_alloc(); + destination = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, source)) { + subghz_cli_command_print_usage(); + break; + } + + if(!args_read_string_and_trim(args, destination)) { + subghz_cli_command_print_usage(); + break; + } + + if(!args_read_hex_bytes(args, iv, 16)) { + subghz_cli_command_print_usage(); + break; + } + + if(!subghz_keystore_raw_encrypted_save( + furi_string_get_cstr(source), furi_string_get_cstr(destination), iv)) { + printf("Failed to save Keystore"); + break; + } + + } while(false); + + furi_string_free(destination); + furi_string_free(source); +} + +static void subghz_cli_command_chat(Cli* cli, FuriString* args) { + uint32_t frequency = 433920000; + uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT + + if(furi_string_size(args)) { + int ret = sscanf(furi_string_get_cstr(args), "%lu %lu", &frequency, &device_ind); + if(ret != 2) { + printf("sscanf returned %d, Frequency: %lu\r\n", ret, frequency); + printf("sscanf returned %d, Device: %lu\r\n", ret, device_ind); + cli_print_usage( + "subghz chat", + " ", + furi_string_get_cstr(args)); + return; + } + } + subghz_devices_init(); + const SubGhzDevice* device = subghz_cli_command_get_device(&device_ind); + if(!subghz_devices_is_frequency_valid(device, frequency)) { + printf( + "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency); + subghz_devices_deinit(); + subghz_cli_radio_device_power_off(); + return; + } + if(!furi_hal_region_is_frequency_allowed(frequency)) { + printf( + "In your region, only reception on this frequency (%lu) is allowed,\r\n" + "the actual operation of the application is not possible\r\n ", + frequency); + return; + } + + SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(cli); + + if(!subghz_chat_worker_start(subghz_chat, device, frequency)) { + printf("Startup error SubGhzChatWorker\r\n"); + + if(subghz_chat_worker_is_running(subghz_chat)) { + subghz_chat_worker_stop(subghz_chat); + subghz_chat_worker_free(subghz_chat); + } + return; + } + + printf("Receiving at frequency %lu Hz\r\n", frequency); + printf("Press CTRL+C to stop\r\n"); + + furi_hal_power_suppress_charge_enter(); + + size_t message_max_len = 64; + uint8_t message[64] = {0}; + FuriString* input; + input = furi_string_alloc(); + FuriString* name; + name = furi_string_alloc(); + FuriString* output; + output = furi_string_alloc(); + FuriString* sysmsg; + sysmsg = furi_string_alloc(); + bool exit = false; + SubGhzChatEvent chat_event; + + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + + furi_string_printf(name, "\033[0;33m%s\033[0m: ", furi_hal_version_get_name_ptr()); + furi_string_set(input, name); + printf("%s", furi_string_get_cstr(input)); + fflush(stdout); + + while(!exit) { + chat_event = subghz_chat_worker_get_event_chat(subghz_chat); + switch(chat_event.event) { + case SubGhzChatEventInputData: + if(chat_event.c == CliSymbolAsciiETX) { + printf("\r\n"); + chat_event.event = SubGhzChatEventUserExit; + subghz_chat_worker_put_event_chat(subghz_chat, &chat_event); + break; + } else if( + (chat_event.c == CliSymbolAsciiBackspace) || (chat_event.c == CliSymbolAsciiDel)) { + size_t len = furi_string_utf8_length(input); + if(len > furi_string_utf8_length(name)) { + printf("%s", "\e[D\e[1P"); + fflush(stdout); + //delete 1 char UTF + const char* str = furi_string_get_cstr(input); + size_t size = 0; + FuriStringUTF8State s = FuriStringUTF8StateStarting; + FuriStringUnicodeValue u = 0; + furi_string_reset(sysmsg); + while(*str) { + furi_string_utf8_decode(*str, &s, &u); + if((s == FuriStringUTF8StateError) || s == FuriStringUTF8StateStarting) { + furi_string_utf8_push(sysmsg, u); + if(++size >= len - 1) break; + s = FuriStringUTF8StateStarting; + } + str++; + } + furi_string_set(input, sysmsg); + } + } else if(chat_event.c == CliSymbolAsciiCR) { + printf("\r\n"); + furi_string_push_back(input, '\r'); + furi_string_push_back(input, '\n'); + while(!subghz_chat_worker_write( + subghz_chat, + (uint8_t*)furi_string_get_cstr(input), + strlen(furi_string_get_cstr(input)))) { + furi_delay_ms(10); + } + + furi_string_printf(input, "%s", furi_string_get_cstr(name)); + printf("%s", furi_string_get_cstr(input)); + fflush(stdout); + } else if(chat_event.c == CliSymbolAsciiLF) { + //cut out the symbol \n + } else { + putc(chat_event.c, stdout); + fflush(stdout); + furi_string_push_back(input, chat_event.c); + break; + case SubGhzChatEventRXData: + do { + memset(message, 0x00, message_max_len); + size_t len = subghz_chat_worker_read(subghz_chat, message, message_max_len); + for(size_t i = 0; i < len; i++) { + furi_string_push_back(output, message[i]); + if(message[i] == '\n') { + printf("\r"); + for(uint8_t i = 0; i < 80; i++) { + printf(" "); + } + printf("\r %s", furi_string_get_cstr(output)); + printf("%s", furi_string_get_cstr(input)); + fflush(stdout); + furi_string_reset(output); + } + } + } while(subghz_chat_worker_available(subghz_chat)); + break; + case SubGhzChatEventNewMessage: + notification_message(notification, &sequence_single_vibro); + break; + case SubGhzChatEventUserEntrance: + furi_string_printf( + sysmsg, + "\033[0;34m%s joined chat.\033[0m\r\n", + furi_hal_version_get_name_ptr()); + subghz_chat_worker_write( + subghz_chat, + (uint8_t*)furi_string_get_cstr(sysmsg), + strlen(furi_string_get_cstr(sysmsg))); + break; + case SubGhzChatEventUserExit: + furi_string_printf( + sysmsg, "\033[0;31m%s left chat.\033[0m\r\n", furi_hal_version_get_name_ptr()); + subghz_chat_worker_write( + subghz_chat, + (uint8_t*)furi_string_get_cstr(sysmsg), + strlen(furi_string_get_cstr(sysmsg))); + furi_delay_ms(10); + exit = true; + break; + default: + FURI_LOG_W("SubGhzChat", "Error event"); + break; + } + } + if(!cli_is_connected(cli)) { + printf("\r\n"); + chat_event.event = SubGhzChatEventUserExit; + subghz_chat_worker_put_event_chat(subghz_chat, &chat_event); + } + } + + furi_string_free(input); + furi_string_free(name); + furi_string_free(output); + furi_string_free(sysmsg); + + subghz_devices_deinit(); + subghz_cli_radio_device_power_off(); + + furi_hal_power_suppress_charge_exit(); + furi_record_close(RECORD_NOTIFICATION); + + if(subghz_chat_worker_is_running(subghz_chat)) { + subghz_chat_worker_stop(subghz_chat); + subghz_chat_worker_free(subghz_chat); + } + printf("\r\nExit chat\r\n"); +} + +static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { + FuriString* cmd; + cmd = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, cmd)) { + subghz_cli_command_print_usage(); + break; + } + + if(furi_string_cmp_str(cmd, "chat") == 0) { + subghz_cli_command_chat(cli, args); + break; + } + + if(furi_string_cmp_str(cmd, "tx") == 0) { + subghz_cli_command_tx(cli, args, context); + break; + } + + if(furi_string_cmp_str(cmd, "rx") == 0) { + subghz_cli_command_rx(cli, args, context); + break; + } + + if(furi_string_cmp_str(cmd, "rx_raw") == 0) { + subghz_cli_command_rx_raw(cli, args, context); + break; + } + + if(furi_string_cmp_str(cmd, "decode_raw") == 0) { + subghz_cli_command_decode_raw(cli, args, context); + break; + } + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + if(furi_string_cmp_str(cmd, "encrypt_keeloq") == 0) { + subghz_cli_command_encrypt_keeloq(cli, args); + break; + } + + if(furi_string_cmp_str(cmd, "encrypt_raw") == 0) { + subghz_cli_command_encrypt_raw(cli, args); + break; + } + + if(furi_string_cmp_str(cmd, "tx_carrier") == 0) { + subghz_cli_command_tx_carrier(cli, args, context); + break; + } + + if(furi_string_cmp_str(cmd, "rx_carrier") == 0) { + subghz_cli_command_rx_carrier(cli, args, context); + break; + } + } + + subghz_cli_command_print_usage(); + } while(false); + + furi_string_free(cmd); +} + +static bool + subghz_on_system_start_istream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) { + File* file = istream->state; + size_t ret = storage_file_read(file, buf, count); + return (count == ret); +} + +static bool subghz_on_system_start_istream_decode_band( + pb_istream_t* stream, + const pb_field_t* field, + void** arg) { + (void)field; + FuriHalRegion* region = *arg; + + PB_Region_Band band = {0}; + if(!pb_decode(stream, PB_Region_Band_fields, &band)) { + FURI_LOG_E("SubGhzOnStart", "PB Region band decode error: %s", PB_GET_ERROR(stream)); + return false; + } + + region->bands_count += 1; + region = realloc( //-V701 + region, + sizeof(FuriHalRegion) + sizeof(FuriHalRegionBand) * region->bands_count); + size_t pos = region->bands_count - 1; + region->bands[pos].start = band.start; + region->bands[pos].end = band.end; + region->bands[pos].power_limit = band.power_limit; + region->bands[pos].duty_cycle = band.duty_cycle; + *arg = region; + + FURI_LOG_I( + "SubGhzOnStart", + "Add allowed band: start %luHz, stop %luHz, power_limit %ddBm, duty_cycle %u%%", + band.start, + band.end, + band.power_limit, + band.duty_cycle); + return true; +} + +void subghz_on_system_start() { +#ifdef SRV_CLI + Cli* cli = furi_record_open(RECORD_CLI); + + cli_add_command(cli, "subghz", CliCommandFlagDefault, subghz_cli_command, NULL); + + furi_record_close(RECORD_CLI); +#else + UNUSED(subghz_cli_command); +#endif + +#ifdef SRV_STORAGE + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + FileInfo fileinfo = {0}; + PB_Region pb_region = {0}; + pb_region.bands.funcs.decode = subghz_on_system_start_istream_decode_band; + + do { + if(storage_common_stat(storage, SUBGHZ_REGION_FILENAME, &fileinfo) != FSE_OK || + fileinfo.size == 0) { + FURI_LOG_W("SubGhzOnStart", "Region data is missing or empty"); + break; + } + + if(!storage_file_open(file, SUBGHZ_REGION_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) { + FURI_LOG_E("SubGhzOnStart", "Unable to open region data"); + break; + } + + pb_istream_t istream = { + .callback = subghz_on_system_start_istream_read, + .state = file, + .errmsg = NULL, + .bytes_left = fileinfo.size, + }; + + pb_region.bands.arg = malloc(sizeof(FuriHalRegion)); + if(!pb_decode(&istream, PB_Region_fields, &pb_region)) { + FURI_LOG_E("SubGhzOnStart", "Invalid region data"); + free(pb_region.bands.arg); + break; + } + + FuriHalRegion* region = pb_region.bands.arg; + memcpy( + region->country_code, + pb_region.country_code->bytes, + pb_region.country_code->size < 4 ? pb_region.country_code->size : 3); + furi_hal_region_set(region); + } while(0); + + pb_release(PB_Region_fields, &pb_region); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +#else + UNUSED(subghz_cli_command); + UNUSED(subghz_on_system_start_istream_decode_band); + UNUSED(subghz_on_system_start_istream_read); +#endif +} diff --git a/applications/subghz/subghz_cli.h b/applications/main/subghz/subghz_cli.h similarity index 100% rename from applications/subghz/subghz_cli.h rename to applications/main/subghz/subghz_cli.h diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c new file mode 100644 index 00000000000..e6c93e05cc0 --- /dev/null +++ b/applications/main/subghz/subghz_history.c @@ -0,0 +1,239 @@ +#include "subghz_history.h" +#include +#include + +#include + +#define SUBGHZ_HISTORY_MAX 50 +#define SUBGHZ_HISTORY_FREE_HEAP 20480 +#define TAG "SubGhzHistory" + +typedef struct { + FuriString* item_str; + FlipperFormat* flipper_string; + uint8_t type; + SubGhzRadioPreset* preset; +} SubGhzHistoryItem; + +ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST) + +#define M_OPL_SubGhzHistoryItemArray_t() ARRAY_OPLIST(SubGhzHistoryItemArray, M_POD_OPLIST) + +typedef struct { + SubGhzHistoryItemArray_t data; +} SubGhzHistoryStruct; + +struct SubGhzHistory { + uint32_t last_update_timestamp; + uint16_t last_index_write; + uint8_t code_last_hash_data; + FuriString* tmp_string; + SubGhzHistoryStruct* history; +}; + +SubGhzHistory* subghz_history_alloc(void) { + SubGhzHistory* instance = malloc(sizeof(SubGhzHistory)); + instance->tmp_string = furi_string_alloc(); + instance->history = malloc(sizeof(SubGhzHistoryStruct)); + SubGhzHistoryItemArray_init(instance->history->data); + return instance; +} + +void subghz_history_free(SubGhzHistory* instance) { + furi_assert(instance); + furi_string_free(instance->tmp_string); + for + M_EACH(item, instance->history->data, SubGhzHistoryItemArray_t) { + 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_clear(instance->history->data); + free(instance->history); + free(instance); +} + +uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx) { + furi_assert(instance); + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + return item->preset->frequency; +} + +SubGhzRadioPreset* subghz_history_get_radio_preset(SubGhzHistory* instance, uint16_t idx) { + furi_assert(instance); + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + return item->preset; +} + +const char* subghz_history_get_preset(SubGhzHistory* instance, uint16_t idx) { + furi_assert(instance); + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + return furi_string_get_cstr(item->preset->name); +} + +void subghz_history_reset(SubGhzHistory* instance) { + furi_assert(instance); + furi_string_reset(instance->tmp_string); + for + M_EACH(item, instance->history->data, SubGhzHistoryItemArray_t) { + 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_reset(instance->history->data); + instance->last_index_write = 0; + instance->code_last_hash_data = 0; +} + +uint16_t subghz_history_get_item(SubGhzHistory* instance) { + furi_assert(instance); + return instance->last_index_write; +} + +uint8_t subghz_history_get_type_protocol(SubGhzHistory* instance, uint16_t idx) { + furi_assert(instance); + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + return item->type; +} + +const char* subghz_history_get_protocol_name(SubGhzHistory* instance, uint16_t idx) { + furi_assert(instance); + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + flipper_format_rewind(item->flipper_string); + if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) { + FURI_LOG_E(TAG, "Missing Protocol"); + furi_string_reset(instance->tmp_string); + } + return furi_string_get_cstr(instance->tmp_string); +} + +FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx) { + furi_assert(instance); + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + if(item->flipper_string) { + return item->flipper_string; + } else { + return NULL; + } +} +bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output) { + furi_assert(instance); + if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) { + if(output != NULL) furi_string_printf(output, " Free heap LOW"); + return true; + } + if(instance->last_index_write == SUBGHZ_HISTORY_MAX) { + if(output != NULL) furi_string_printf(output, " Memory is FULL"); + return true; + } + if(output != NULL) + furi_string_printf(output, "%02u/%02u", instance->last_index_write, SUBGHZ_HISTORY_MAX); + return false; +} + +void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* output, uint16_t idx) { + SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); + furi_string_set(output, item->item_str); +} + +bool subghz_history_add_to_history( + SubGhzHistory* instance, + void* context, + SubGhzRadioPreset* preset) { + furi_assert(instance); + furi_assert(context); + + if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) return false; + if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return false; + + SubGhzProtocolDecoderBase* decoder_base = context; + if((instance->code_last_hash_data == + subghz_protocol_decoder_base_get_hash_data(decoder_base)) && + ((furi_get_tick() - instance->last_update_timestamp) < 500)) { + instance->last_update_timestamp = furi_get_tick(); + return false; + } + + instance->code_last_hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); + instance->last_update_timestamp = furi_get_tick(); + + FuriString* text; + text = furi_string_alloc(); + SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data); + item->preset = malloc(sizeof(SubGhzRadioPreset)); + item->type = decoder_base->protocol->type; + item->preset->frequency = preset->frequency; + item->preset->name = furi_string_alloc(); + furi_string_set(item->preset->name, preset->name); + item->preset->data = preset->data; + item->preset->data_size = preset->data_size; + + item->item_str = furi_string_alloc(); + item->flipper_string = flipper_format_string_alloc(); + subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, preset); + + do { + if(!flipper_format_rewind(item->flipper_string)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) { + FURI_LOG_E(TAG, "Missing Protocol"); + break; + } + if(!strcmp(furi_string_get_cstr(instance->tmp_string), "KeeLoq")) { + furi_string_set(instance->tmp_string, "KL "); + if(!flipper_format_read_string(item->flipper_string, "Manufacture", text)) { + FURI_LOG_E(TAG, "Missing Protocol"); + break; + } + furi_string_cat(instance->tmp_string, text); + } else if(!strcmp(furi_string_get_cstr(instance->tmp_string), "Star Line")) { + furi_string_set(instance->tmp_string, "SL "); + if(!flipper_format_read_string(item->flipper_string, "Manufacture", text)) { + FURI_LOG_E(TAG, "Missing Protocol"); + break; + } + furi_string_cat(instance->tmp_string, text); + } + if(!flipper_format_rewind(item->flipper_string)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex(item->flipper_string, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_D(TAG, "No Key"); + } + uint64_t data = 0; + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + data = (data << 8) | key_data[i]; + } + if(data != 0) { + if(!(uint32_t)(data >> 32)) { + furi_string_printf( + item->item_str, + "%s %lX", + furi_string_get_cstr(instance->tmp_string), + (uint32_t)(data & 0xFFFFFFFF)); + } else { + furi_string_printf( + item->item_str, + "%s %lX%08lX", + furi_string_get_cstr(instance->tmp_string), + (uint32_t)(data >> 32), + (uint32_t)(data & 0xFFFFFFFF)); + } + } else { + furi_string_printf(item->item_str, "%s", furi_string_get_cstr(instance->tmp_string)); + } + + } while(false); + + furi_string_free(text); + instance->last_index_write++; + return true; +} diff --git a/applications/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h similarity index 88% rename from applications/subghz/subghz_history.h rename to applications/main/subghz/subghz_history.h index adbcfc18aaa..5b2b57d3338 100644 --- a/applications/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -5,7 +5,7 @@ #include #include #include -#include "helpers/subghz_types.h" +#include typedef struct SubGhzHistory SubGhzHistory; @@ -35,7 +35,7 @@ void subghz_history_reset(SubGhzHistory* instance); */ uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx); -SubGhzPresetDefinition* subghz_history_get_preset_def(SubGhzHistory* instance, uint16_t idx); +SubGhzRadioPreset* subghz_history_get_radio_preset(SubGhzHistory* instance, uint16_t idx); /** Get preset to history[idx] * @@ -71,30 +71,30 @@ const char* subghz_history_get_protocol_name(SubGhzHistory* instance, uint16_t i /** Get string item menu to history[idx] * * @param instance - SubGhzHistory instance - * @param output - string_t output + * @param output - FuriString* output * @param idx - record index */ -void subghz_history_get_text_item_menu(SubGhzHistory* instance, string_t output, uint16_t idx); +void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* output, uint16_t idx); /** Get string the remaining number of records to history * * @param instance - SubGhzHistory instance - * @param output - string_t output + * @param output - FuriString* output * @return bool - is FUUL */ -bool subghz_history_get_text_space_left(SubGhzHistory* instance, string_t output); +bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output); /** Add protocol to history * * @param instance - SubGhzHistory instance * @param context - SubGhzProtocolCommon context - * @param preset - SubGhzPresetDefinition preset + * @param preset - SubGhzRadioPreset preset * @return bool; */ bool subghz_history_add_to_history( SubGhzHistory* instance, void* context, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data * diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c new file mode 100644 index 00000000000..c03efe5e568 --- /dev/null +++ b/applications/main/subghz/subghz_i.c @@ -0,0 +1,424 @@ +#include "subghz_i.h" + +#include "assets_icons.h" +#include "subghz/types.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "views/receiver.h" + +#include +#include +#include + +#define TAG "SubGhz" + +void subghz_set_default_preset(SubGhz* subghz) { + furi_assert(subghz); + subghz_txrx_set_preset( + subghz->txrx, + "AM650", + subghz_setting_get_default_frequency(subghz_txrx_get_setting(subghz->txrx)), + NULL, + 0); +} + +void subghz_blink_start(SubGhz* subghz) { + furi_assert(subghz); + notification_message(subghz->notifications, &sequence_blink_stop); + notification_message(subghz->notifications, &sequence_blink_start_magenta); +} + +void subghz_blink_stop(SubGhz* subghz) { + furi_assert(subghz); + notification_message(subghz->notifications, &sequence_blink_stop); +} + +bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) { + switch(subghz_txrx_tx_start(subghz->txrx, flipper_format)) { + case SubGhzTxRxStartTxStateErrorParserOthers: + dialog_message_show_storage_error( + subghz->dialogs, "Error in protocol\nparameters\ndescription"); + break; + case SubGhzTxRxStartTxStateErrorOnlyRx: + subghz_dialog_message_show_only_rx(subghz); + break; + + default: + return true; + break; + } + return false; +} + +void subghz_dialog_message_show_only_rx(SubGhz* subghz) { + DialogsApp* dialogs = subghz->dialogs; + DialogMessage* message = dialog_message_alloc(); + + const char* header_text = "Transmission is blocked"; + const char* message_text = "Transmission on\nthis frequency is\nrestricted in\nyour region"; + if(!furi_hal_region_is_provisioned()) { + header_text = "Firmware update needed"; + message_text = "Please update\nfirmware before\nusing this feature\nflipp.dev/upd"; + } + + dialog_message_set_header(message, header_text, 63, 3, AlignCenter, AlignTop); + dialog_message_set_text(message, message_text, 0, 17, AlignLeft, AlignTop); + + dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17); + + dialog_message_show(dialogs, message); + dialog_message_free(message); +} + +bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { + furi_assert(subghz); + furi_assert(file_path); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + Stream* fff_data_stream = + flipper_format_get_raw_stream(subghz_txrx_get_fff_data(subghz->txrx)); + + SubGhzLoadKeyState load_key_state = SubGhzLoadKeyStateParseErr; + FuriString* temp_str = furi_string_alloc(); + uint32_t temp_data32; + + do { + stream_clean(fff_data_stream); + if(!flipper_format_file_open_existing(fff_data_file, file_path)) { + FURI_LOG_E(TAG, "Error open file %s", file_path); + break; + } + + if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { + FURI_LOG_E(TAG, "Missing or incorrect header"); + break; + } + + if(((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_KEY_FILE_TYPE)) || + (!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE))) && + temp_data32 == SUBGHZ_KEY_FILE_VERSION) { + } else { + FURI_LOG_E(TAG, "Type or version mismatch"); + break; + } + + //Load frequency + if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) { + FURI_LOG_E(TAG, "Missing Frequency"); + break; + } + + if(!subghz_txrx_radio_device_is_frequecy_valid(subghz->txrx, temp_data32)) { + FURI_LOG_E(TAG, "Frequency not supported"); + break; + } + + //Load preset + if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { + FURI_LOG_E(TAG, "Missing Preset"); + break; + } + + furi_string_set_str( + temp_str, subghz_txrx_get_preset_name(subghz->txrx, furi_string_get_cstr(temp_str))); + if(!strcmp(furi_string_get_cstr(temp_str), "")) { + break; + } + SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); + + if(!strcmp(furi_string_get_cstr(temp_str), "CUSTOM")) { + //TODO FL-3551: add Custom_preset_module + //delete preset if it already exists + subghz_setting_delete_custom_preset(setting, furi_string_get_cstr(temp_str)); + //load custom preset from file + if(!subghz_setting_load_custom_preset( + setting, furi_string_get_cstr(temp_str), fff_data_file)) { + FURI_LOG_E(TAG, "Missing Custom preset"); + break; + } + } + size_t preset_index = + subghz_setting_get_inx_preset_by_name(setting, furi_string_get_cstr(temp_str)); + subghz_txrx_set_preset( + subghz->txrx, + furi_string_get_cstr(temp_str), + temp_data32, + subghz_setting_get_preset_data(setting, preset_index), + subghz_setting_get_preset_data_size(setting, preset_index)); + + //Load protocol + if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { + FURI_LOG_E(TAG, "Missing Protocol"); + break; + } + + FlipperFormat* fff_data = subghz_txrx_get_fff_data(subghz->txrx); + if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { + //if RAW + subghz->load_type_file = SubGhzLoadTypeFileRaw; + subghz_protocol_raw_gen_fff_data( + fff_data, file_path, subghz_txrx_radio_device_get_name(subghz->txrx)); + } else { + subghz->load_type_file = SubGhzLoadTypeFileKey; + stream_copy_full( + flipper_format_get_raw_stream(fff_data_file), + flipper_format_get_raw_stream(fff_data)); + } + + if(subghz_txrx_load_decoder_by_name_protocol( + subghz->txrx, furi_string_get_cstr(temp_str))) { + SubGhzProtocolStatus status = subghz_protocol_decoder_base_deserialize( + subghz_txrx_get_decoder(subghz->txrx), fff_data); + if(status != SubGhzProtocolStatusOk) { + load_key_state = SubGhzLoadKeyStateProtocolDescriptionErr; + break; + } + } else { + FURI_LOG_E(TAG, "Protocol not found"); + break; + } + + load_key_state = SubGhzLoadKeyStateOK; + } while(0); + + furi_string_free(temp_str); + flipper_format_free(fff_data_file); + furi_record_close(RECORD_STORAGE); + + switch(load_key_state) { + case SubGhzLoadKeyStateParseErr: + if(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 SubGhzLoadKeyStateOK: + return true; + + default: + furi_crash("SubGhz: Unknown load_key_state."); + return false; + } +} + +SubGhzLoadTypeFile subghz_get_load_type_file(SubGhz* subghz) { + furi_assert(subghz); + return subghz->load_type_file; +} + +bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len) { + furi_assert(subghz); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FuriString* temp_str = furi_string_alloc(); + FuriString* file_name = furi_string_alloc(); + FuriString* file_path = furi_string_alloc(); + + bool res = false; + + if(subghz_path_is_file(subghz->file_path)) { + //get the name of the next free file + path_extract_filename(subghz->file_path, file_name, true); + path_extract_dirname(furi_string_get_cstr(subghz->file_path), file_path); + + storage_get_next_filename( + storage, + furi_string_get_cstr(file_path), + furi_string_get_cstr(file_name), + SUBGHZ_APP_FILENAME_EXTENSION, + file_name, + max_len); + + furi_string_printf( + temp_str, + "%s/%s%s", + furi_string_get_cstr(file_path), + furi_string_get_cstr(file_name), + SUBGHZ_APP_FILENAME_EXTENSION); + furi_string_set(subghz->file_path, temp_str); + res = true; + } + + furi_string_free(temp_str); + furi_string_free(file_path); + furi_string_free(file_name); + furi_record_close(RECORD_STORAGE); + + return res; +} + +bool subghz_save_protocol_to_file( + SubGhz* subghz, + FlipperFormat* flipper_format, + const char* dev_file_name) { + furi_assert(subghz); + furi_assert(flipper_format); + furi_assert(dev_file_name); + + Storage* storage = furi_record_open(RECORD_STORAGE); + Stream* flipper_format_stream = flipper_format_get_raw_stream(flipper_format); + + bool saved = false; + FuriString* file_dir = furi_string_alloc(); + + path_extract_dirname(dev_file_name, file_dir); + do { + //removing additional fields + flipper_format_delete_key(flipper_format, "Repeat"); + flipper_format_delete_key(flipper_format, "Manufacture"); + + // Create subghz folder directory if necessary + if(!storage_simply_mkdir(storage, furi_string_get_cstr(file_dir))) { + dialog_message_show_storage_error(subghz->dialogs, "Cannot create\nfolder"); + break; + } + + if(!storage_simply_remove(storage, dev_file_name)) { + break; + } + stream_seek(flipper_format_stream, 0, StreamOffsetFromStart); + stream_save_to_file(flipper_format_stream, storage, dev_file_name, FSOM_CREATE_ALWAYS); + + if(storage_common_stat(storage, dev_file_name, NULL) != FSE_OK) { + break; + } + + saved = true; + } while(0); + furi_string_free(file_dir); + furi_record_close(RECORD_STORAGE); + return saved; +} + +void subghz_save_to_file(void* context) { + furi_assert(context); + SubGhz* subghz = context; + if(subghz_path_is_file(subghz->file_path)) { + subghz_save_protocol_to_file( + subghz, + subghz_txrx_get_fff_data(subghz->txrx), + furi_string_get_cstr(subghz->file_path)); + } +} + +bool subghz_load_protocol_from_file(SubGhz* subghz) { + furi_assert(subghz); + + FuriString* file_path = furi_string_alloc(); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, SUBGHZ_APP_FILENAME_EXTENSION, &I_sub1_10px); + browser_options.base_path = SUBGHZ_APP_FOLDER; + + // Input events and views are managed by file_select + bool res = dialog_file_browser_show( + subghz->dialogs, subghz->file_path, subghz->file_path, &browser_options); + + if(res) { + res = subghz_key_load(subghz, furi_string_get_cstr(subghz->file_path), true); + } + + furi_string_free(file_path); + + return res; +} + +bool subghz_rename_file(SubGhz* subghz) { + furi_assert(subghz); + bool ret = true; + + Storage* storage = furi_record_open(RECORD_STORAGE); + + if(furi_string_cmp(subghz->file_path_tmp, subghz->file_path)) { + FS_Error fs_result = storage_common_rename( + storage, + furi_string_get_cstr(subghz->file_path_tmp), + furi_string_get_cstr(subghz->file_path)); + + if(fs_result != FSE_OK) { + dialog_message_show_storage_error(subghz->dialogs, "Cannot rename\n file/directory"); + ret = false; + } + } + furi_record_close(RECORD_STORAGE); + + return ret; +} + +bool subghz_file_available(SubGhz* subghz) { + furi_assert(subghz); + bool ret = true; + Storage* storage = furi_record_open(RECORD_STORAGE); + + FS_Error fs_result = + storage_common_stat(storage, furi_string_get_cstr(subghz->file_path), NULL); + + if(fs_result != FSE_OK) { + dialog_message_show_storage_error(subghz->dialogs, "File not available\n file/directory"); + ret = false; + } + + furi_record_close(RECORD_STORAGE); + return ret; +} + +bool subghz_delete_file(SubGhz* subghz) { + furi_assert(subghz); + + Storage* storage = furi_record_open(RECORD_STORAGE); + bool result = storage_simply_remove(storage, furi_string_get_cstr(subghz->file_path_tmp)); + furi_record_close(RECORD_STORAGE); + + subghz_file_name_clear(subghz); + + return result; +} + +void subghz_file_name_clear(SubGhz* subghz) { + furi_assert(subghz); + furi_string_set(subghz->file_path, SUBGHZ_APP_FOLDER); + furi_string_reset(subghz->file_path_tmp); +} + +bool subghz_path_is_file(FuriString* path) { + return furi_string_end_with(path, SUBGHZ_APP_FILENAME_EXTENSION); +} + +void subghz_lock(SubGhz* subghz) { + furi_assert(subghz); + subghz->lock = SubGhzLockOn; +} + +void subghz_unlock(SubGhz* subghz) { + furi_assert(subghz); + subghz->lock = SubGhzLockOff; +} + +bool subghz_is_locked(SubGhz* subghz) { + furi_assert(subghz); + return (subghz->lock == SubGhzLockOn); +} + +void subghz_rx_key_state_set(SubGhz* subghz, SubGhzRxKeyState state) { + furi_assert(subghz); + subghz->rx_key_state = state; +} + +SubGhzRxKeyState subghz_rx_key_state_get(SubGhz* subghz) { + furi_assert(subghz); + return subghz->rx_key_state; +} diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h new file mode 100644 index 00000000000..9e58f3947dc --- /dev/null +++ b/applications/main/subghz/subghz_i.h @@ -0,0 +1,102 @@ +#pragma once + +#include "helpers/subghz_types.h" +#include "helpers/subghz_error_type.h" +#include +#include "subghz.h" +#include "views/receiver.h" +#include "views/transmitter.h" +#include "views/subghz_frequency_analyzer.h" +#include "views/subghz_read_raw.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "subghz_history.h" + +#include +#include + +#include "rpc/rpc_app.h" + +#include "helpers/subghz_threshold_rssi.h" + +#include "helpers/subghz_txrx.h" + +#define SUBGHZ_MAX_LEN_NAME 64 + +struct SubGhz { + Gui* gui; + NotificationApp* notifications; + + SubGhzTxRx* txrx; + + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + + Submenu* submenu; + Popup* popup; + TextInput* text_input; + Widget* widget; + DialogsApp* dialogs; + FuriString* file_path; + FuriString* file_path_tmp; + char file_name_tmp[SUBGHZ_MAX_LEN_NAME]; + SubGhzNotificationState state_notifications; + + SubGhzViewReceiver* subghz_receiver; + SubGhzViewTransmitter* subghz_transmitter; + VariableItemList* variable_item_list; + + SubGhzFrequencyAnalyzer* subghz_frequency_analyzer; + SubGhzReadRAW* subghz_read_raw; + + SubGhzProtocolFlag filter; + FuriString* error_str; + SubGhzLock lock; + SubGhzThresholdRssi* threshold_rssi; + SubGhzRxKeyState rx_key_state; + SubGhzHistory* history; + uint16_t idx_menu_chosen; + SubGhzLoadTypeFile load_type_file; + void* rpc_ctx; +}; + +void subghz_set_default_preset(SubGhz* subghz); +void subghz_blink_start(SubGhz* subghz); +void subghz_blink_stop(SubGhz* subghz); + +bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format); +void subghz_dialog_message_show_only_rx(SubGhz* subghz); + +bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog); +bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len); +bool subghz_save_protocol_to_file( + SubGhz* subghz, + FlipperFormat* flipper_format, + const char* dev_file_name); +void subghz_save_to_file(void* context); +bool subghz_load_protocol_from_file(SubGhz* subghz); +bool subghz_rename_file(SubGhz* subghz); +bool subghz_file_available(SubGhz* subghz); +bool subghz_delete_file(SubGhz* subghz); +void subghz_file_name_clear(SubGhz* subghz); +bool subghz_path_is_file(FuriString* path); +SubGhzLoadTypeFile subghz_get_load_type_file(SubGhz* subghz); + +void subghz_lock(SubGhz* subghz); +void subghz_unlock(SubGhz* subghz); +bool subghz_is_locked(SubGhz* subghz); + +void subghz_rx_key_state_set(SubGhz* subghz, SubGhzRxKeyState state); +SubGhzRxKeyState subghz_rx_key_state_get(SubGhz* subghz); diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c new file mode 100644 index 00000000000..23fa26c772c --- /dev/null +++ b/applications/main/subghz/views/receiver.c @@ -0,0 +1,491 @@ +#include "receiver.h" +#include "../subghz_i.h" +#include + +#include +#include +#include +#include + +#define FRAME_HEIGHT 12 +#define MAX_LEN_PX 111 +#define MENU_ITEMS 4u +#define UNLOCK_CNT 3 + +#define SUBGHZ_RAW_THRESHOLD_MIN -90.0f + +typedef struct { + FuriString* item_str; + uint8_t type; +} SubGhzReceiverMenuItem; + +ARRAY_DEF(SubGhzReceiverMenuItemArray, SubGhzReceiverMenuItem, M_POD_OPLIST) + +#define M_OPL_SubGhzReceiverMenuItemArray_t() \ + ARRAY_OPLIST(SubGhzReceiverMenuItemArray, M_POD_OPLIST) + +struct SubGhzReceiverHistory { + SubGhzReceiverMenuItemArray_t data; +}; + +typedef struct SubGhzReceiverHistory SubGhzReceiverHistory; + +static const Icon* ReceiverItemIcons[] = { + [SubGhzProtocolTypeUnknown] = &I_Quest_7x8, + [SubGhzProtocolTypeStatic] = &I_Unlock_7x8, + [SubGhzProtocolTypeDynamic] = &I_Lock_7x8, +}; + +typedef enum { + SubGhzViewReceiverBarShowDefault, + SubGhzViewReceiverBarShowLock, + SubGhzViewReceiverBarShowToUnlockPress, + SubGhzViewReceiverBarShowUnlock, +} SubGhzViewReceiverBarShow; + +struct SubGhzViewReceiver { + bool lock; + uint8_t lock_count; + FuriTimer* timer; + View* view; + SubGhzViewReceiverCallback callback; + void* context; +}; + +typedef struct { + FuriString* frequency_str; + FuriString* preset_str; + FuriString* history_stat_str; + SubGhzReceiverHistory* history; + uint16_t idx; + uint16_t list_offset; + uint16_t history_item; + SubGhzViewReceiverBarShow bar_show; + uint8_t u_rssi; + SubGhzRadioDeviceType device_type; +} SubGhzViewReceiverModel; + +void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) { + furi_assert(instance); + with_view_model( + instance->view, + SubGhzViewReceiverModel * model, + { + if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) { + model->u_rssi = 0; + } else { + model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_THRESHOLD_MIN); + } + }, + true); +} + +void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, bool lock) { + furi_assert(subghz_receiver); + subghz_receiver->lock_count = 0; + + if(lock == true) { + subghz_receiver->lock = true; + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { model->bar_show = SubGhzViewReceiverBarShowLock; }, + true); + furi_timer_start(subghz_receiver->timer, 1000); + } else { + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { model->bar_show = SubGhzViewReceiverBarShowDefault; }, + true); + } +} + +void subghz_view_receiver_set_callback( + SubGhzViewReceiver* subghz_receiver, + SubGhzViewReceiverCallback callback, + void* context) { + furi_assert(subghz_receiver); + furi_assert(callback); + subghz_receiver->callback = callback; + subghz_receiver->context = context; +} + +static void subghz_view_receiver_update_offset(SubGhzViewReceiver* subghz_receiver) { + furi_assert(subghz_receiver); + + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { + size_t history_item = model->history_item; + uint16_t bounds = history_item > 3 ? 2 : history_item; + + if(history_item > 3 && model->idx >= (int16_t)(history_item - 1)) { + model->list_offset = model->idx - 3; + } else if(model->list_offset < model->idx - bounds) { + model->list_offset = + CLAMP(model->list_offset + 1, (int16_t)(history_item - bounds), 0); + } else if(model->list_offset > model->idx - bounds) { + model->list_offset = CLAMP(model->idx - 1, (int16_t)(history_item - bounds), 0); + } + }, + true); +} + +void subghz_view_receiver_add_item_to_menu( + SubGhzViewReceiver* subghz_receiver, + const char* name, + uint8_t type) { + furi_assert(subghz_receiver); + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { + SubGhzReceiverMenuItem* item_menu = + SubGhzReceiverMenuItemArray_push_raw(model->history->data); + item_menu->item_str = furi_string_alloc_set(name); + item_menu->type = type; + if((model->idx == model->history_item - 1)) { + model->history_item++; + model->idx++; + } else { + model->history_item++; + } + }, + true); + subghz_view_receiver_update_offset(subghz_receiver); +} + +void subghz_view_receiver_add_data_statusbar( + SubGhzViewReceiver* subghz_receiver, + const char* frequency_str, + const char* preset_str, + const char* history_stat_str) { + furi_assert(subghz_receiver); + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { + furi_string_set(model->frequency_str, frequency_str); + furi_string_set(model->preset_str, preset_str); + furi_string_set(model->history_stat_str, history_stat_str); + }, + true); +} + +void subghz_view_receiver_set_radio_device_type( + SubGhzViewReceiver* subghz_receiver, + SubGhzRadioDeviceType device_type) { + furi_assert(subghz_receiver); + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { model->device_type = device_type; }, + true); +} + +static void subghz_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, 0 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, 0, 0 + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, 1, 0 + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 1); + + canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 11); + canvas_draw_dot(canvas, scrollbar ? 121 : 126, 0 + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11); +} + +static void subghz_view_rssi_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { + for(uint8_t i = 1; i < model->u_rssi; i++) { + if(i % 5) { + canvas_draw_dot(canvas, 46 + i, 52); + canvas_draw_dot(canvas, 47 + i, 53); + canvas_draw_dot(canvas, 46 + i, 54); + } + } +} + +void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + + elements_button_left(canvas, "Config"); + + bool scrollbar = model->history_item > 4; + FuriString* str_buff; + str_buff = furi_string_alloc(); + + SubGhzReceiverMenuItem* item_menu; + + for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) { + size_t idx = CLAMP((uint16_t)(i + model->list_offset), model->history_item, 0); + item_menu = SubGhzReceiverMenuItemArray_get(model->history->data, idx); + furi_string_set(str_buff, item_menu->item_str); + elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 7 : MAX_LEN_PX); + if(model->idx == idx) { + subghz_view_receiver_draw_frame(canvas, i, scrollbar); + } else { + canvas_set_color(canvas, ColorBlack); + } + canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); + canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff)); + furi_string_reset(str_buff); + } + if(scrollbar) { + elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item); + } + furi_string_free(str_buff); + + canvas_set_color(canvas, ColorBlack); + + if(model->history_item == 0) { + canvas_draw_icon(canvas, 0, 0, &I_Scanning_short_96x52); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 63, 44, "Scanning..."); + canvas_set_font(canvas, FontSecondary); + } + + if(model->device_type == SubGhzRadioDeviceTypeInternal) { + canvas_draw_icon(canvas, 108, 0, &I_Internal_antenna_20x12); + } else { + canvas_draw_icon(canvas, 108, 0, &I_External_antenna_20x12); + } + + subghz_view_rssi_draw(canvas, model); + switch(model->bar_show) { + case SubGhzViewReceiverBarShowLock: + canvas_draw_icon(canvas, 64, 56, &I_Lock_7x8); + canvas_draw_str(canvas, 74, 64, "Locked"); + break; + case SubGhzViewReceiverBarShowToUnlockPress: + canvas_draw_str(canvas, 44, 64, furi_string_get_cstr(model->frequency_str)); + canvas_draw_str(canvas, 79, 64, furi_string_get_cstr(model->preset_str)); + canvas_draw_str(canvas, 97, 64, furi_string_get_cstr(model->history_stat_str)); + canvas_set_font(canvas, FontSecondary); + elements_bold_rounded_frame(canvas, 14, 8, 99, 48); + elements_multiline_text(canvas, 65, 26, "To unlock\npress:"); + canvas_draw_icon(canvas, 65, 42, &I_Pin_back_arrow_10x8); + canvas_draw_icon(canvas, 80, 42, &I_Pin_back_arrow_10x8); + canvas_draw_icon(canvas, 95, 42, &I_Pin_back_arrow_10x8); + canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42); + canvas_draw_dot(canvas, 17, 61); + break; + case SubGhzViewReceiverBarShowUnlock: + canvas_draw_icon(canvas, 64, 56, &I_Unlock_7x8); + canvas_draw_str(canvas, 74, 64, "Unlocked"); + break; + default: + canvas_draw_str(canvas, 44, 64, furi_string_get_cstr(model->frequency_str)); + canvas_draw_str(canvas, 79, 64, furi_string_get_cstr(model->preset_str)); + canvas_draw_str(canvas, 97, 64, furi_string_get_cstr(model->history_stat_str)); + break; + } +} + +static void subghz_view_receiver_timer_callback(void* context) { + furi_assert(context); + SubGhzViewReceiver* subghz_receiver = context; + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { model->bar_show = SubGhzViewReceiverBarShowDefault; }, + true); + if(subghz_receiver->lock_count < UNLOCK_CNT) { + subghz_receiver->callback( + SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context); + } else { + subghz_receiver->lock = false; + subghz_receiver->callback(SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context); + } + subghz_receiver->lock_count = 0; +} + +bool subghz_view_receiver_input(InputEvent* event, void* context) { + furi_assert(context); + SubGhzViewReceiver* subghz_receiver = context; + + if(subghz_receiver->lock == true) { + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { model->bar_show = SubGhzViewReceiverBarShowToUnlockPress; }, + true); + if(subghz_receiver->lock_count == 0) { + furi_timer_start(subghz_receiver->timer, 1000); + } + if(event->key == InputKeyBack && event->type == InputTypeShort) { + subghz_receiver->lock_count++; + } + if(subghz_receiver->lock_count >= UNLOCK_CNT) { + // subghz_receiver->callback( + // SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context); + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { model->bar_show = SubGhzViewReceiverBarShowUnlock; }, + true); + //subghz_receiver->lock = false; + furi_timer_start(subghz_receiver->timer, 650); + } + + return true; + } + + if(event->key == InputKeyBack && event->type == InputTypeShort) { + subghz_receiver->callback(SubGhzCustomEventViewReceiverBack, subghz_receiver->context); + } else if( + event->key == InputKeyUp && + (event->type == InputTypeShort || event->type == InputTypeRepeat)) { + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { + if(model->idx != 0) model->idx--; + }, + true); + } else if( + event->key == InputKeyDown && + (event->type == InputTypeShort || event->type == InputTypeRepeat)) { + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { + if((model->history_item != 0) && (model->idx != model->history_item - 1)) + model->idx++; + }, + true); + } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { + subghz_receiver->callback(SubGhzCustomEventViewReceiverConfig, subghz_receiver->context); + } else if(event->key == InputKeyOk && event->type == InputTypeShort) { + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { + if(model->history_item != 0) { + subghz_receiver->callback( + SubGhzCustomEventViewReceiverOK, subghz_receiver->context); + } + }, + false); + } + + subghz_view_receiver_update_offset(subghz_receiver); + + return true; +} + +void subghz_view_receiver_enter(void* context) { + furi_assert(context); +} + +void subghz_view_receiver_exit(void* context) { + furi_assert(context); + SubGhzViewReceiver* subghz_receiver = context; + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { + furi_string_reset(model->frequency_str); + furi_string_reset(model->preset_str); + furi_string_reset(model->history_stat_str); + for + M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) { + furi_string_free(item_menu->item_str); + item_menu->type = 0; + } + SubGhzReceiverMenuItemArray_reset(model->history->data); + model->idx = 0; + model->list_offset = 0; + model->history_item = 0; + }, + false); + furi_timer_stop(subghz_receiver->timer); +} + +SubGhzViewReceiver* subghz_view_receiver_alloc() { + SubGhzViewReceiver* subghz_receiver = malloc(sizeof(SubGhzViewReceiver)); + + // View allocation and configuration + subghz_receiver->view = view_alloc(); + + subghz_receiver->lock = false; + subghz_receiver->lock_count = 0; + view_allocate_model( + subghz_receiver->view, ViewModelTypeLocking, sizeof(SubGhzViewReceiverModel)); + view_set_context(subghz_receiver->view, subghz_receiver); + view_set_draw_callback(subghz_receiver->view, (ViewDrawCallback)subghz_view_receiver_draw); + view_set_input_callback(subghz_receiver->view, subghz_view_receiver_input); + view_set_enter_callback(subghz_receiver->view, subghz_view_receiver_enter); + view_set_exit_callback(subghz_receiver->view, subghz_view_receiver_exit); + + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { + model->frequency_str = furi_string_alloc(); + model->preset_str = furi_string_alloc(); + model->history_stat_str = furi_string_alloc(); + model->bar_show = SubGhzViewReceiverBarShowDefault; + model->history = malloc(sizeof(SubGhzReceiverHistory)); + SubGhzReceiverMenuItemArray_init(model->history->data); + }, + true); + subghz_receiver->timer = + furi_timer_alloc(subghz_view_receiver_timer_callback, FuriTimerTypeOnce, subghz_receiver); + return subghz_receiver; +} + +void subghz_view_receiver_free(SubGhzViewReceiver* subghz_receiver) { + furi_assert(subghz_receiver); + + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { + furi_string_free(model->frequency_str); + furi_string_free(model->preset_str); + furi_string_free(model->history_stat_str); + for + M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) { + furi_string_free(item_menu->item_str); + item_menu->type = 0; + } + SubGhzReceiverMenuItemArray_clear(model->history->data); + free(model->history); + }, + false); + furi_timer_free(subghz_receiver->timer); + view_free(subghz_receiver->view); + free(subghz_receiver); +} + +View* subghz_view_receiver_get_view(SubGhzViewReceiver* subghz_receiver) { + furi_assert(subghz_receiver); + return subghz_receiver->view; +} + +uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver) { + furi_assert(subghz_receiver); + uint32_t idx = 0; + with_view_model( + subghz_receiver->view, SubGhzViewReceiverModel * model, { idx = model->idx; }, false); + return idx; +} + +void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx) { + furi_assert(subghz_receiver); + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { + model->idx = idx; + if(model->idx > 2) model->list_offset = idx - 2; + }, + true); + subghz_view_receiver_update_offset(subghz_receiver); +} diff --git a/applications/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h similarity index 84% rename from applications/subghz/views/receiver.h rename to applications/main/subghz/views/receiver.h index aab7a76c545..c91c069386c 100644 --- a/applications/subghz/views/receiver.h +++ b/applications/main/subghz/views/receiver.h @@ -8,7 +8,9 @@ typedef struct SubGhzViewReceiver SubGhzViewReceiver; typedef void (*SubGhzViewReceiverCallback)(SubGhzCustomEvent event, void* context); -void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock keyboard); +void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi); + +void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, bool keyboard); void subghz_view_receiver_set_callback( SubGhzViewReceiver* subghz_receiver, @@ -27,6 +29,10 @@ void subghz_view_receiver_add_data_statusbar( const char* preset_str, const char* history_stat_str); +void subghz_view_receiver_set_radio_device_type( + SubGhzViewReceiver* subghz_receiver, + SubGhzRadioDeviceType device_type); + void subghz_view_receiver_add_item_to_menu( SubGhzViewReceiver* subghz_receiver, const char* name, diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c new file mode 100644 index 00000000000..d90401678a8 --- /dev/null +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -0,0 +1,466 @@ +#include "subghz_frequency_analyzer.h" +#include "../subghz_i.h" + +#include +#include +#include +#include +#include +#include +#include "../helpers/subghz_frequency_analyzer_worker.h" +#include "../helpers/subghz_frequency_analyzer_log_item_array.h" + +#include +#include + +#define LOG_FREQUENCY_MAX_ITEMS 60 // uint8_t (limited by 'seq' of SubGhzFrequencyAnalyzerLogItem) + +#define SNPRINTF_FREQUENCY(buff, freq) \ + snprintf(buff, sizeof(buff), "%03ld.%03ld", freq / 1000000 % 1000, freq / 1000 % 1000); + +typedef enum { + SubGhzFrequencyAnalyzerStatusIDLE, +} SubGhzFrequencyAnalyzerStatus; + +typedef enum { + SubGhzFrequencyAnalyzerFragmentBottomTypeMain, + SubGhzFrequencyAnalyzerFragmentBottomTypeLog, +} SubGhzFrequencyAnalyzerFragmentBottomType; + +struct SubGhzFrequencyAnalyzer { + View* view; + SubGhzFrequencyAnalyzerWorker* worker; + SubGhzFrequencyAnalyzerCallback callback; + void* context; + bool locked; + uint32_t last_frequency; +}; + +typedef struct { + uint32_t frequency; + uint8_t rssi; + uint32_t history_frequency[3]; + bool signal; + SubGhzFrequencyAnalyzerLogItemArray_t log_frequency; + SubGhzFrequencyAnalyzerFragmentBottomType fragment_bottom_type; + SubGhzFrequencyAnalyzerLogOrderBy log_frequency_order_by; + uint8_t log_frequency_scroll_offset; +} SubGhzFrequencyAnalyzerModel; + +static inline uint8_t rssi_sanitize(float rssi) { + return ( + !float_is_equal(rssi, 0.f) ? (uint8_t)(rssi - SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) : 0); +} + +void subghz_frequency_analyzer_set_callback( + SubGhzFrequencyAnalyzer* subghz_frequency_analyzer, + SubGhzFrequencyAnalyzerCallback callback, + void* context) { + furi_assert(subghz_frequency_analyzer); + furi_assert(callback); + subghz_frequency_analyzer->callback = callback; + subghz_frequency_analyzer->context = context; +} + +void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) { + uint8_t column_number = 0; + if(rssi) { + rssi = rssi / 3 + 2; + if(rssi > 20) rssi = 20; + for(uint8_t i = 1; i < rssi; i++) { + if(i % 4) { + column_number++; + canvas_draw_box(canvas, x + 2 * i, y - column_number, 2, column_number); + } + } + } +} + +void subghz_frequency_analyzer_draw_log_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) { + uint8_t column_height = 6; + if(rssi) { + if(rssi > 54) rssi = 54; + for(uint8_t i = 1; i < rssi; i++) { + if(i % 5) { + canvas_draw_box(canvas, x + i, y - column_height, 1, column_height); + } + } + } +} + +static void subghz_frequency_analyzer_log_frequency_draw( + Canvas* canvas, + SubGhzFrequencyAnalyzerModel* model) { + char buffer[64]; + const uint8_t offset_x = 0; + const uint8_t offset_y = 43; + canvas_set_font(canvas, FontKeyboard); + + const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency); + if(items_count == 0) { + canvas_draw_rframe(canvas, offset_x + 27, offset_y - 3, 73, 16, 5); + canvas_draw_str_aligned( + canvas, offset_x + 64, offset_y + 8, AlignCenter, AlignBottom, "No records"); + return; + } else if(items_count > 3) { + elements_scrollbar_pos( + canvas, + offset_x + 127, + offset_y - 8, + 29, + model->log_frequency_scroll_offset, + items_count - 2); + } + + SubGhzFrequencyAnalyzerLogItem_t* log_frequency_item; + for(uint8_t i = 0; i < 3; ++i) { + const uint8_t item_pos = model->log_frequency_scroll_offset + i; + if(item_pos >= items_count) { + break; + } + log_frequency_item = + SubGhzFrequencyAnalyzerLogItemArray_get(model->log_frequency, item_pos); + // Frequency + SNPRINTF_FREQUENCY(buffer, (*log_frequency_item)->frequency) + canvas_draw_str(canvas, offset_x, offset_y + i * 10, buffer); + + // Count + snprintf(buffer, sizeof(buffer), "%3d", (*log_frequency_item)->count); + canvas_draw_str(canvas, offset_x + 48, offset_y + i * 10, buffer); + + // Max RSSI + subghz_frequency_analyzer_draw_log_rssi( + canvas, (*log_frequency_item)->rssi_max, offset_x + 69, (offset_y + i * 10)); + } + + canvas_set_font(canvas, FontSecondary); +} + +static void subghz_frequency_analyzer_history_frequency_draw( + Canvas* canvas, + SubGhzFrequencyAnalyzerModel* model) { + char buffer[64]; + uint8_t x = 66; + uint8_t y = 43; + + canvas_set_font(canvas, FontKeyboard); + for(uint8_t i = 0; i < 3; i++) { + if(model->history_frequency[i]) { + SNPRINTF_FREQUENCY(buffer, model->history_frequency[i]) + canvas_draw_str(canvas, x, y + i * 10, buffer); + } else { + canvas_draw_str(canvas, x, y + i * 10, "---.---"); + } + canvas_draw_str(canvas, x + 44, y + i * 10, "MHz"); + } + canvas_set_font(canvas, FontSecondary); +} + +void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel* model) { + furi_assert(canvas); + furi_assert(model); + char buffer[64]; + + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + + if(model->fragment_bottom_type == SubGhzFrequencyAnalyzerFragmentBottomTypeLog) { + const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency); + const char* log_order_by_name = + subghz_frequency_analyzer_log_get_order_name(model->log_frequency_order_by); + if(items_count < LOG_FREQUENCY_MAX_ITEMS) { + snprintf(buffer, sizeof(buffer), "Frequency Analyzer [%s]", log_order_by_name); + canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignBottom, buffer); + } else { + snprintf(buffer, sizeof(buffer), "The log is full! [%s]", log_order_by_name); + canvas_draw_str(canvas, 2, 8, buffer); + } + subghz_frequency_analyzer_log_frequency_draw(canvas, model); + } else { + canvas_draw_str(canvas, 0, 8, "Frequency Analyzer"); + canvas_draw_icon(canvas, 108, 0, &I_Internal_antenna_20x12); + canvas_draw_str(canvas, 0, 64, "RSSI"); + subghz_frequency_analyzer_draw_rssi(canvas, model->rssi, 20, 64); + + subghz_frequency_analyzer_history_frequency_draw(canvas, model); + } + + // Frequency + canvas_set_font(canvas, FontBigNumbers); + SNPRINTF_FREQUENCY(buffer, model->frequency); + if(model->signal) { + canvas_draw_box(canvas, 4, 11, 121, 22); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_str(canvas, 8, 29, buffer); + canvas_draw_icon(canvas, 96, 18, &I_MHz_25x11); +} + +static void subghz_frequency_analyzer_log_frequency_sort(SubGhzFrequencyAnalyzerModel* model) { + furi_assert(model); + M_LET((cmp, model->log_frequency_order_by), SubGhzFrequencyAnalyzerLogItemArray_compare_by_t) + SubGhzFrequencyAnalyzerLogItemArray_sort_fo( + model->log_frequency, SubGhzFrequencyAnalyzerLogItemArray_compare_by_as_interface(cmp)); +} + +bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { + furi_assert(context); + SubGhzFrequencyAnalyzer* instance = context; + + if(event->key == InputKeyBack) { + return false; + } + + if((event->type == InputTypeShort) && + ((event->key == InputKeyLeft) || (event->key == InputKeyRight))) { + with_view_model( + instance->view, + SubGhzFrequencyAnalyzerModel * model, + { + if(event->key == InputKeyLeft) { + if(model->fragment_bottom_type == 0) { + model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeLog; + } else { + --model->fragment_bottom_type; + } + } else if(event->key == InputKeyRight) { + if(model->fragment_bottom_type == + SubGhzFrequencyAnalyzerFragmentBottomTypeLog) { + model->fragment_bottom_type = 0; + } else { + ++model->fragment_bottom_type; + } + } + }, + true); + } else if((event->type == InputTypeShort) && (event->key == InputKeyOk)) { + with_view_model( + instance->view, + SubGhzFrequencyAnalyzerModel * model, + { + if(model->fragment_bottom_type == SubGhzFrequencyAnalyzerFragmentBottomTypeLog) { + ++model->log_frequency_order_by; + if(model->log_frequency_order_by > + SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc) { + model->log_frequency_order_by = 0; + } + subghz_frequency_analyzer_log_frequency_sort(model); + } + }, + true); + } else if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) { + with_view_model( + instance->view, + SubGhzFrequencyAnalyzerModel * model, + { + if(model->fragment_bottom_type == SubGhzFrequencyAnalyzerFragmentBottomTypeLog) { + if(event->key == InputKeyUp) { + if(model->log_frequency_scroll_offset > 0) { + --model->log_frequency_scroll_offset; + } + } else if(event->key == InputKeyDown) { + const size_t items_count = + SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency); + if((model->log_frequency_scroll_offset + 3u) < items_count) { + ++model->log_frequency_scroll_offset; + } + } + } + }, + true); + } + + return true; +} + +static void subghz_frequency_analyzer_log_frequency_search_it( + SubGhzFrequencyAnalyzerLogItemArray_it_t* itref, + SubGhzFrequencyAnalyzerLogItemArray_t* log_frequency, + uint32_t frequency) { + furi_assert(log_frequency); + + SubGhzFrequencyAnalyzerLogItemArray_it(*itref, *log_frequency); + SubGhzFrequencyAnalyzerLogItem_t* item; + while(!SubGhzFrequencyAnalyzerLogItemArray_end_p(*itref)) { + item = SubGhzFrequencyAnalyzerLogItemArray_ref(*itref); + if((*item)->frequency == frequency) { + break; + } + SubGhzFrequencyAnalyzerLogItemArray_next(*itref); + } +} + +static bool subghz_frequency_analyzer_log_frequency_insert(SubGhzFrequencyAnalyzerModel* model) { + furi_assert(model); + const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency); + if(items_count < LOG_FREQUENCY_MAX_ITEMS) { + SubGhzFrequencyAnalyzerLogItem_t* item = + SubGhzFrequencyAnalyzerLogItemArray_push_new(model->log_frequency); + (*item)->frequency = model->frequency; + (*item)->count = 1; + (*item)->rssi_max = model->rssi; + (*item)->seq = items_count; + return true; + } + return false; +} + +static void subghz_frequency_analyzer_log_frequency_update( + SubGhzFrequencyAnalyzerModel* model, + bool need_insert) { + furi_assert(model); + if(!model->frequency) { + return; + } + + SubGhzFrequencyAnalyzerLogItemArray_it_t it; + subghz_frequency_analyzer_log_frequency_search_it( + &it, &model->log_frequency, model->frequency); + if(!SubGhzFrequencyAnalyzerLogItemArray_end_p(it)) { + SubGhzFrequencyAnalyzerLogItem_t* item = SubGhzFrequencyAnalyzerLogItemArray_ref(it); + if((*item)->rssi_max < model->rssi) { + (*item)->rssi_max = model->rssi; + } + + if(need_insert && (*item)->count < UINT8_MAX) { + ++(*item)->count; + subghz_frequency_analyzer_log_frequency_sort(model); + } + } else if(need_insert) { + if(subghz_frequency_analyzer_log_frequency_insert(model)) { + subghz_frequency_analyzer_log_frequency_sort(model); + } + } +} + +void subghz_frequency_analyzer_pair_callback( + void* context, + uint32_t frequency, + float rssi, + bool signal) { + SubGhzFrequencyAnalyzer* instance = context; + if(float_is_equal(rssi, 0.f) && instance->locked) { + if(instance->callback) { + instance->callback(SubGhzCustomEventSceneAnalyzerUnlock, instance->context); + } + instance->last_frequency = 0; + //update history + with_view_model( + instance->view, + SubGhzFrequencyAnalyzerModel * model, + { + model->history_frequency[2] = model->history_frequency[1]; + model->history_frequency[1] = model->history_frequency[0]; + model->history_frequency[0] = model->frequency; + }, + false); + } else if(!float_is_equal(rssi, 0.f) && !instance->locked) { + if(instance->callback) { + instance->callback(SubGhzCustomEventSceneAnalyzerLock, instance->context); + } + } + + instance->locked = !float_is_equal(rssi, 0.f); + with_view_model( + instance->view, + SubGhzFrequencyAnalyzerModel * model, + { + model->rssi = rssi_sanitize(rssi); + model->frequency = frequency; + model->signal = signal; + if(frequency) { + subghz_frequency_analyzer_log_frequency_update( + model, frequency != instance->last_frequency); + instance->last_frequency = frequency; + } + }, + true); +} + +void subghz_frequency_analyzer_enter(void* context) { + furi_assert(context); + SubGhzFrequencyAnalyzer* instance = context; + + //Start worker + instance->worker = subghz_frequency_analyzer_worker_alloc(instance->context); + + subghz_frequency_analyzer_worker_set_pair_callback( + instance->worker, + (SubGhzFrequencyAnalyzerWorkerPairCallback)subghz_frequency_analyzer_pair_callback, + instance); + + subghz_frequency_analyzer_worker_start(instance->worker); + + with_view_model( + instance->view, + SubGhzFrequencyAnalyzerModel * model, + { + model->rssi = 0u; + model->frequency = 0; + model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain; + model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc; + model->log_frequency_scroll_offset = 0; + model->history_frequency[0] = model->history_frequency[1] = + model->history_frequency[2] = 0; + SubGhzFrequencyAnalyzerLogItemArray_init(model->log_frequency); + }, + true); +} + +void subghz_frequency_analyzer_exit(void* context) { + furi_assert(context); + SubGhzFrequencyAnalyzer* instance = context; + + //Stop worker + if(subghz_frequency_analyzer_worker_is_running(instance->worker)) { + subghz_frequency_analyzer_worker_stop(instance->worker); + } + subghz_frequency_analyzer_worker_free(instance->worker); + + with_view_model( + instance->view, + SubGhzFrequencyAnalyzerModel * model, + { + model->rssi = 0; + model->frequency = 0; + model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain; + model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc; + model->log_frequency_scroll_offset = 0; + model->history_frequency[0] = model->history_frequency[1] = + model->history_frequency[2] = 0; + SubGhzFrequencyAnalyzerLogItemArray_clear(model->log_frequency); + }, + true); +} + +SubGhzFrequencyAnalyzer* subghz_frequency_analyzer_alloc() { + SubGhzFrequencyAnalyzer* instance = malloc(sizeof(SubGhzFrequencyAnalyzer)); + + // View allocation and configuration + instance->last_frequency = 0; + instance->view = view_alloc(); + view_allocate_model( + instance->view, ViewModelTypeLocking, sizeof(SubGhzFrequencyAnalyzerModel)); + view_set_context(instance->view, instance); + view_set_draw_callback(instance->view, (ViewDrawCallback)subghz_frequency_analyzer_draw); + view_set_input_callback(instance->view, subghz_frequency_analyzer_input); + view_set_enter_callback(instance->view, subghz_frequency_analyzer_enter); + view_set_exit_callback(instance->view, subghz_frequency_analyzer_exit); + + with_view_model( + instance->view, SubGhzFrequencyAnalyzerModel * model, { model->rssi = 0; }, true); + + return instance; +} + +void subghz_frequency_analyzer_free(SubGhzFrequencyAnalyzer* instance) { + furi_assert(instance); + + view_free(instance->view); + free(instance); +} + +View* subghz_frequency_analyzer_get_view(SubGhzFrequencyAnalyzer* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/applications/subghz/views/subghz_frequency_analyzer.h b/applications/main/subghz/views/subghz_frequency_analyzer.h similarity index 100% rename from applications/subghz/views/subghz_frequency_analyzer.h rename to applications/main/subghz/views/subghz_frequency_analyzer.h diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c new file mode 100644 index 00000000000..b074cdc9ff3 --- /dev/null +++ b/applications/main/subghz/views/subghz_read_raw.c @@ -0,0 +1,639 @@ +#include "subghz_read_raw.h" +#include "../subghz_i.h" + +#include +#include +#include +#include +#include + +#include +#define SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE 100 +#define TAG "SubGhzReadRaw" + +struct SubGhzReadRAW { + View* view; + SubGhzReadRAWCallback callback; + void* context; +}; + +typedef struct { + FuriString* frequency_str; + FuriString* preset_str; + FuriString* sample_write; + FuriString* file_name; + uint8_t* rssi_history; + uint8_t rssi_curret; + bool rssi_history_end; + uint8_t ind_write; + uint8_t ind_sin; + SubGhzReadRAWStatus status; + float raw_threshold_rssi; + SubGhzRadioDeviceType device_type; +} SubGhzReadRAWModel; + +void subghz_read_raw_set_callback( + SubGhzReadRAW* subghz_read_raw, + SubGhzReadRAWCallback callback, + void* context) { + furi_assert(subghz_read_raw); + furi_assert(callback); + subghz_read_raw->callback = callback; + subghz_read_raw->context = context; +} + +void subghz_read_raw_add_data_statusbar( + SubGhzReadRAW* instance, + const char* frequency_str, + const char* preset_str) { + furi_assert(instance); + with_view_model( + instance->view, + SubGhzReadRAWModel * model, + { + furi_string_set(model->frequency_str, frequency_str); + furi_string_set(model->preset_str, preset_str); + }, + true); +} + +void subghz_read_raw_set_radio_device_type( + SubGhzReadRAW* instance, + SubGhzRadioDeviceType device_type) { + furi_assert(instance); + with_view_model( + instance->view, SubGhzReadRAWModel * model, { model->device_type = device_type; }, true); +} + +void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool trace) { + furi_assert(instance); + uint8_t u_rssi = 0; + + if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) { + u_rssi = 0; + } else { + u_rssi = (uint8_t)((rssi - SUBGHZ_RAW_THRESHOLD_MIN) / 2.7); + } + + with_view_model( + instance->view, + SubGhzReadRAWModel * model, + { + model->rssi_curret = u_rssi; + if(trace) { + model->rssi_history[model->ind_write++] = u_rssi; + } else { + model->rssi_history[model->ind_write] = u_rssi; + } + + if(model->ind_write > SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE) { + model->rssi_history_end = true; + model->ind_write = 0; + } + }, + true); +} + +void subghz_read_raw_update_sample_write(SubGhzReadRAW* instance, size_t sample) { + furi_assert(instance); + + with_view_model( + instance->view, + SubGhzReadRAWModel * model, + { furi_string_printf(model->sample_write, "%zu spl.", sample); }, + false); +} + +void subghz_read_raw_stop_send(SubGhzReadRAW* instance) { + furi_assert(instance); + + with_view_model( + instance->view, + SubGhzReadRAWModel * model, + { + switch(model->status) { + case SubGhzReadRAWStatusTXRepeat: + case SubGhzReadRAWStatusLoadKeyTXRepeat: + instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context); + break; + case SubGhzReadRAWStatusTX: + model->status = SubGhzReadRAWStatusIDLE; + break; + case SubGhzReadRAWStatusLoadKeyTX: + model->status = SubGhzReadRAWStatusLoadKeyIDLE; + break; + + default: + FURI_LOG_W(TAG, "unknown status"); + model->status = SubGhzReadRAWStatusIDLE; + break; + } + }, + true); +} + +void subghz_read_raw_update_sin(SubGhzReadRAW* instance) { + furi_assert(instance); + with_view_model( + instance->view, + SubGhzReadRAWModel * model, + { + if(model->ind_sin++ > 62) { + model->ind_sin = 0; + } + }, + true); +} + +static int8_t subghz_read_raw_tab_sin(uint8_t x) { + const uint8_t tab_sin[64] = {0, 3, 6, 9, 12, 16, 19, 22, 25, 28, 31, 34, 37, + 40, 43, 46, 49, 51, 54, 57, 60, 63, 65, 68, 71, 73, + 76, 78, 81, 83, 85, 88, 90, 92, 94, 96, 98, 100, 102, + 104, 106, 107, 109, 111, 112, 113, 115, 116, 117, 118, 120, 121, + 122, 122, 123, 124, 125, 125, 126, 126, 126, 127, 127, 127}; + + int8_t r = tab_sin[((x & 0x40) ? -x - 1 : x) & 0x3f]; + if(x & 0x80) return -r; + return r; +} + +void subghz_read_raw_draw_sin(Canvas* canvas, SubGhzReadRAWModel* model) { +#define SUBGHZ_RAW_SIN_AMPLITUDE 11 + for(int i = 113; i > 0; i--) { + canvas_draw_line( + canvas, + i, + 32 - subghz_read_raw_tab_sin(i + model->ind_sin * 16) / SUBGHZ_RAW_SIN_AMPLITUDE, + i + 1, + 32 + subghz_read_raw_tab_sin((i + model->ind_sin * 16 + 1) * 2) / + SUBGHZ_RAW_SIN_AMPLITUDE); + canvas_draw_line( + canvas, + i + 1, + 32 - subghz_read_raw_tab_sin(i + model->ind_sin * 16) / SUBGHZ_RAW_SIN_AMPLITUDE, + i + 2, + 32 + subghz_read_raw_tab_sin((i + model->ind_sin * 16 + 1) * 2) / + SUBGHZ_RAW_SIN_AMPLITUDE); + } +} + +void subghz_read_raw_draw_scale(Canvas* canvas, SubGhzReadRAWModel* model) { +#define SUBGHZ_RAW_TOP_SCALE 14 +#define SUBGHZ_RAW_END_SCALE 115 + + if(model->rssi_history_end == false) { + for(int i = SUBGHZ_RAW_END_SCALE; i > 0; i -= 15) { + canvas_draw_line(canvas, i, SUBGHZ_RAW_TOP_SCALE, i, SUBGHZ_RAW_TOP_SCALE + 4); + canvas_draw_line(canvas, i - 5, SUBGHZ_RAW_TOP_SCALE, i - 5, SUBGHZ_RAW_TOP_SCALE + 2); + canvas_draw_line( + canvas, i - 10, SUBGHZ_RAW_TOP_SCALE, i - 10, SUBGHZ_RAW_TOP_SCALE + 2); + } + } else { + for(int i = SUBGHZ_RAW_END_SCALE - model->ind_write % 15; i > -15; i -= 15) { + canvas_draw_line(canvas, i, SUBGHZ_RAW_TOP_SCALE, i, SUBGHZ_RAW_TOP_SCALE + 4); + if(SUBGHZ_RAW_END_SCALE > i + 5) + canvas_draw_line( + canvas, i + 5, SUBGHZ_RAW_TOP_SCALE, i + 5, SUBGHZ_RAW_TOP_SCALE + 2); + if(SUBGHZ_RAW_END_SCALE > i + 10) + canvas_draw_line( + canvas, i + 10, SUBGHZ_RAW_TOP_SCALE, i + 10, SUBGHZ_RAW_TOP_SCALE + 2); + } + } +} + +void subghz_read_raw_draw_rssi(Canvas* canvas, SubGhzReadRAWModel* model) { + int ind = 0; + int base = 0; + uint8_t width = 2; + if(model->rssi_history_end == false) { + for(int i = model->ind_write; i >= 0; i--) { + 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); + if(model->ind_write > 3) { + canvas_draw_line( + canvas, model->ind_write - 1, 47, model->ind_write - 1, 47 - model->rssi_curret); + + for(uint8_t i = 13; i < 47; i += width * 2) { + canvas_draw_line(canvas, model->ind_write, i, model->ind_write, i + width); + } + canvas_draw_line(canvas, model->ind_write - 2, 12, model->ind_write + 2, 12); + canvas_draw_line(canvas, model->ind_write - 1, 13, model->ind_write + 1, 13); + } + } else { + int i = 0; + base = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - model->ind_write; + for(i = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE; i > 0; i--) { + ind = i - base; + if(ind < 0) ind += SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE; + canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[ind]); + } + + canvas_draw_line( + canvas, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1, + 47, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1, + 47 - model->rssi_curret); + canvas_draw_line( + canvas, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1, + 47, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1, + 47 - model->rssi_curret); + + for(uint8_t i = 13; i < 47; i += width * 2) { + canvas_draw_line( + canvas, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, + i, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, + i + width); + } + canvas_draw_line( + canvas, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 2, + 12, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 2, + 12); + canvas_draw_line( + canvas, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1, + 13, + SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1, + 13); + } +} + +void subghz_read_raw_draw_threshold_rssi(Canvas* canvas, SubGhzReadRAWModel* model) { + uint8_t x = 118; + uint8_t y = 48; + + if(model->raw_threshold_rssi > SUBGHZ_RAW_THRESHOLD_MIN) { + uint8_t x = 118; + 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) { + canvas_draw_line(canvas, i, y, i + width, y); + } + } + canvas_draw_line(canvas, x, y - 2, x, y + 2); + canvas_draw_line(canvas, x - 1, y - 1, x - 1, y + 1); + canvas_draw_dot(canvas, x - 2, y); +} + +void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) { + uint8_t graphics_mode = 1; + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 9, furi_string_get_cstr(model->frequency_str)); + canvas_draw_str(canvas, 35, 9, furi_string_get_cstr(model->preset_str)); + canvas_draw_str_aligned( + canvas, 106, 2, AlignRight, AlignTop, furi_string_get_cstr(model->sample_write)); + + if(model->device_type == SubGhzRadioDeviceTypeInternal) { + canvas_draw_icon(canvas, 108, 0, &I_Internal_antenna_20x12); + } else { + canvas_draw_icon(canvas, 108, 0, &I_External_antenna_20x12); + } + canvas_draw_line(canvas, 0, 14, 115, 14); + canvas_draw_line(canvas, 0, 48, 115, 48); + canvas_draw_line(canvas, 115, 14, 115, 48); + + switch(model->status) { + case SubGhzReadRAWStatusIDLE: + elements_button_left(canvas, "Erase"); + elements_button_center(canvas, "Send"); + elements_button_right(canvas, "Save"); + break; + case SubGhzReadRAWStatusLoadKeyIDLE: + elements_button_left(canvas, "New"); + elements_button_center(canvas, "Send"); + elements_button_right(canvas, "More"); + elements_text_box( + canvas, + 4, + 20, + 110, + 30, + AlignCenter, + AlignCenter, + furi_string_get_cstr(model->file_name), + true); + break; + + case SubGhzReadRAWStatusTX: + case SubGhzReadRAWStatusTXRepeat: + case SubGhzReadRAWStatusLoadKeyTX: + case SubGhzReadRAWStatusLoadKeyTXRepeat: + graphics_mode = 0; + elements_button_center(canvas, "Send"); + break; + + case SubGhzReadRAWStatusStart: + elements_button_left(canvas, "Config"); + elements_button_center(canvas, "REC"); + break; + + default: + elements_button_center(canvas, "Stop"); + break; + } + + if(graphics_mode == 0) { + subghz_read_raw_draw_sin(canvas, model); + } else { + subghz_read_raw_draw_rssi(canvas, model); + subghz_read_raw_draw_scale(canvas, model); + subghz_read_raw_draw_threshold_rssi(canvas, model); + canvas_set_font_direction(canvas, CanvasDirectionBottomToTop); + canvas_draw_str(canvas, 128, 40, "RSSI"); + canvas_set_font_direction(canvas, CanvasDirectionLeftToRight); + } +} + +bool subghz_read_raw_input(InputEvent* event, void* context) { + furi_assert(context); + SubGhzReadRAW* instance = context; + + if((event->key == InputKeyOk) && + (event->type == InputTypeLong || event->type == InputTypeRepeat)) { + //we check that if we hold the transfer button, + //further check of events is not needed, we exit + return false; + } else if(event->key == InputKeyOk && event->type == InputTypePress) { + uint8_t ret = false; + with_view_model( + instance->view, + SubGhzReadRAWModel * model, + { + switch(model->status) { + case SubGhzReadRAWStatusIDLE: + // Start TX + instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context); + model->status = SubGhzReadRAWStatusTXRepeat; + ret = true; + break; + case SubGhzReadRAWStatusTX: + // Start TXRepeat + model->status = SubGhzReadRAWStatusTXRepeat; + break; + case SubGhzReadRAWStatusLoadKeyIDLE: + // Start Load Key TX + instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context); + model->status = SubGhzReadRAWStatusLoadKeyTXRepeat; + ret = true; + break; + case SubGhzReadRAWStatusLoadKeyTX: + // Start Load Key TXRepeat + model->status = SubGhzReadRAWStatusLoadKeyTXRepeat; + break; + + default: + break; + } + }, + ret); + } else if(event->key == InputKeyOk && event->type == InputTypeRelease) { + with_view_model( + instance->view, + SubGhzReadRAWModel * model, + { + if(model->status == SubGhzReadRAWStatusTXRepeat) { + // Stop repeat TX + model->status = SubGhzReadRAWStatusTX; + } else if(model->status == SubGhzReadRAWStatusLoadKeyTXRepeat) { + // Stop repeat TX + model->status = SubGhzReadRAWStatusLoadKeyTX; + } + }, + false); + } else if(event->key == InputKeyBack && event->type == InputTypeShort) { + with_view_model( + instance->view, + SubGhzReadRAWModel * model, + { + switch(model->status) { + case SubGhzReadRAWStatusREC: + //Stop REC + instance->callback(SubGhzCustomEventViewReadRAWIDLE, instance->context); + model->status = SubGhzReadRAWStatusIDLE; + break; + case SubGhzReadRAWStatusLoadKeyTX: + //Stop TxRx + instance->callback(SubGhzCustomEventViewReadRAWTXRXStop, instance->context); + model->status = SubGhzReadRAWStatusLoadKeyIDLE; + break; + case SubGhzReadRAWStatusTX: + //Stop TxRx + instance->callback(SubGhzCustomEventViewReadRAWTXRXStop, instance->context); + model->status = SubGhzReadRAWStatusIDLE; + break; + case SubGhzReadRAWStatusLoadKeyIDLE: + //Exit + instance->callback(SubGhzCustomEventViewReadRAWBack, instance->context); + break; + + default: + //Exit + instance->callback(SubGhzCustomEventViewReadRAWBack, instance->context); + break; + } + }, + true); + } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { + with_view_model( + instance->view, + SubGhzReadRAWModel * model, + { + if(model->status == SubGhzReadRAWStatusStart) { + //Config + instance->callback(SubGhzCustomEventViewReadRAWConfig, instance->context); + } else if( + (model->status == SubGhzReadRAWStatusIDLE) || + (model->status == SubGhzReadRAWStatusLoadKeyIDLE)) { + //Erase + model->status = SubGhzReadRAWStatusStart; + model->rssi_history_end = false; + model->ind_write = 0; + furi_string_set(model->sample_write, "0 spl."); + furi_string_reset(model->file_name); + instance->callback(SubGhzCustomEventViewReadRAWErase, instance->context); + } + }, + true); + } else if(event->key == InputKeyRight && event->type == InputTypeShort) { + with_view_model( + instance->view, + SubGhzReadRAWModel * model, + { + if(model->status == SubGhzReadRAWStatusIDLE) { + //Save + instance->callback(SubGhzCustomEventViewReadRAWSave, instance->context); + } else if(model->status == SubGhzReadRAWStatusLoadKeyIDLE) { + //More + instance->callback(SubGhzCustomEventViewReadRAWMore, instance->context); + } + }, + true); + } else if(event->key == InputKeyOk && event->type == InputTypeShort) { + with_view_model( + instance->view, + SubGhzReadRAWModel * model, + { + if(model->status == SubGhzReadRAWStatusStart) { + //Record + instance->callback(SubGhzCustomEventViewReadRAWREC, instance->context); + model->status = SubGhzReadRAWStatusREC; + model->ind_write = 0; + model->rssi_history_end = false; + } else if(model->status == SubGhzReadRAWStatusREC) { + //Stop + instance->callback(SubGhzCustomEventViewReadRAWIDLE, instance->context); + model->status = SubGhzReadRAWStatusIDLE; + } + }, + true); + } + return true; +} + +void subghz_read_raw_set_status( + SubGhzReadRAW* instance, + SubGhzReadRAWStatus status, + const char* file_name, + float raw_threshold_rssi) { + furi_assert(instance); + + switch(status) { + case SubGhzReadRAWStatusStart: + with_view_model( + instance->view, + SubGhzReadRAWModel * model, + { + model->status = SubGhzReadRAWStatusStart; + model->rssi_history_end = false; + model->ind_write = 0; + furi_string_reset(model->file_name); + furi_string_set(model->sample_write, "0 spl."); + model->raw_threshold_rssi = raw_threshold_rssi; + }, + true); + break; + case SubGhzReadRAWStatusIDLE: + with_view_model( + instance->view, + SubGhzReadRAWModel * model, + { model->status = SubGhzReadRAWStatusIDLE; }, + true); + break; + case SubGhzReadRAWStatusLoadKeyTX: + with_view_model( + instance->view, + SubGhzReadRAWModel * model, + { + model->status = SubGhzReadRAWStatusLoadKeyIDLE; + model->rssi_history_end = false; + model->ind_write = 0; + furi_string_set(model->file_name, file_name); + furi_string_set(model->sample_write, "RAW"); + }, + true); + break; + case SubGhzReadRAWStatusSaveKey: + with_view_model( + instance->view, + SubGhzReadRAWModel * model, + { + model->status = SubGhzReadRAWStatusLoadKeyIDLE; + if(!model->ind_write) { + furi_string_set(model->file_name, file_name); + furi_string_set(model->sample_write, "RAW"); + } else { + furi_string_reset(model->file_name); + } + }, + true); + break; + + default: + FURI_LOG_W(TAG, "unknown status"); + break; + } +} + +void subghz_read_raw_enter(void* context) { + furi_assert(context); + //SubGhzReadRAW* instance = context; +} + +void subghz_read_raw_exit(void* context) { + furi_assert(context); + SubGhzReadRAW* instance = context; + + with_view_model( + instance->view, + SubGhzReadRAWModel * model, + { + if(model->status != SubGhzReadRAWStatusIDLE && + model->status != SubGhzReadRAWStatusStart && + model->status != SubGhzReadRAWStatusLoadKeyIDLE) { + instance->callback(SubGhzCustomEventViewReadRAWIDLE, instance->context); + model->status = SubGhzReadRAWStatusStart; + } + }, + true); +} + +SubGhzReadRAW* subghz_read_raw_alloc() { + SubGhzReadRAW* instance = malloc(sizeof(SubGhzReadRAW)); + + // View allocation and configuration + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubGhzReadRAWModel)); + view_set_context(instance->view, instance); + view_set_draw_callback(instance->view, (ViewDrawCallback)subghz_read_raw_draw); + view_set_input_callback(instance->view, subghz_read_raw_input); + view_set_enter_callback(instance->view, subghz_read_raw_enter); + view_set_exit_callback(instance->view, subghz_read_raw_exit); + + with_view_model( + instance->view, + SubGhzReadRAWModel * model, + { + model->frequency_str = furi_string_alloc(); + model->preset_str = furi_string_alloc(); + model->sample_write = furi_string_alloc(); + model->file_name = furi_string_alloc(); + model->rssi_history = malloc(SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE * sizeof(uint8_t)); + model->raw_threshold_rssi = -127.0f; + }, + true); + + return instance; +} + +void subghz_read_raw_free(SubGhzReadRAW* instance) { + furi_assert(instance); + + with_view_model( + instance->view, + SubGhzReadRAWModel * model, + { + furi_string_free(model->frequency_str); + furi_string_free(model->preset_str); + furi_string_free(model->sample_write); + furi_string_free(model->file_name); + free(model->rssi_history); + }, + true); + view_free(instance->view); + free(instance); +} + +View* subghz_read_raw_get_view(SubGhzReadRAW* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/applications/subghz/views/subghz_read_raw.h b/applications/main/subghz/views/subghz_read_raw.h similarity index 82% rename from applications/subghz/views/subghz_read_raw.h rename to applications/main/subghz/views/subghz_read_raw.h index 1d4bb7dc0c9..83403e9750c 100644 --- a/applications/subghz/views/subghz_read_raw.h +++ b/applications/main/subghz/views/subghz_read_raw.h @@ -1,8 +1,11 @@ #pragma once #include +#include "../helpers/subghz_types.h" #include "../helpers/subghz_custom_event.h" +#define SUBGHZ_RAW_THRESHOLD_MIN -90.0f + typedef struct SubGhzReadRAW SubGhzReadRAW; typedef void (*SubGhzReadRAWCallback)(SubGhzCustomEvent event, void* context); @@ -34,17 +37,22 @@ void subghz_read_raw_add_data_statusbar( const char* frequency_str, const char* preset_str); +void subghz_read_raw_set_radio_device_type( + SubGhzReadRAW* instance, + SubGhzRadioDeviceType device_type); + void subghz_read_raw_update_sample_write(SubGhzReadRAW* instance, size_t sample); void subghz_read_raw_stop_send(SubGhzReadRAW* instance); void subghz_read_raw_update_sin(SubGhzReadRAW* instance); -void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi); +void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool trace); void subghz_read_raw_set_status( SubGhzReadRAW* instance, SubGhzReadRAWStatus status, - const char* file_name); + const char* file_name, + float raw_threshold_rssi); View* subghz_read_raw_get_view(SubGhzReadRAW* subghz_static); diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c new file mode 100644 index 00000000000..2a876f8c268 --- /dev/null +++ b/applications/main/subghz/views/transmitter.c @@ -0,0 +1,208 @@ +#include "transmitter.h" +#include "../subghz_i.h" + +#include +#include + +struct SubGhzViewTransmitter { + View* view; + SubGhzViewTransmitterCallback callback; + void* context; +}; + +typedef struct { + FuriString* frequency_str; + FuriString* preset_str; + FuriString* key_str; + bool show_button; + SubGhzRadioDeviceType device_type; +} SubGhzViewTransmitterModel; + +void subghz_view_transmitter_set_callback( + SubGhzViewTransmitter* subghz_transmitter, + SubGhzViewTransmitterCallback callback, + void* context) { + furi_assert(subghz_transmitter); + + subghz_transmitter->callback = callback; + subghz_transmitter->context = context; +} + +void subghz_view_transmitter_add_data_to_show( + SubGhzViewTransmitter* subghz_transmitter, + const char* key_str, + const char* frequency_str, + const char* preset_str, + bool show_button) { + furi_assert(subghz_transmitter); + with_view_model( + subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { + furi_string_set(model->key_str, key_str); + furi_string_set(model->frequency_str, frequency_str); + furi_string_set(model->preset_str, preset_str); + model->show_button = show_button; + }, + true); +} + +void subghz_view_transmitter_set_radio_device_type( + SubGhzViewTransmitter* subghz_transmitter, + SubGhzRadioDeviceType device_type) { + furi_assert(subghz_transmitter); + with_view_model( + subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { model->device_type = device_type; }, + true); +} + +static void subghz_view_transmitter_button_right(Canvas* canvas, const char* str) { + const uint8_t button_height = 12; + const uint8_t vertical_offset = 3; + const uint8_t horizontal_offset = 1; + const uint8_t string_width = canvas_string_width(canvas, str); + const Icon* icon = &I_ButtonCenter_7x7; + const uint8_t icon_offset = 3; + const uint8_t icon_width_with_offset = icon_get_width(icon) + icon_offset; + const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; + + const uint8_t x = (canvas_width(canvas) - button_width) / 2 + 44; + const uint8_t y = canvas_height(canvas); + + canvas_draw_box(canvas, x, y - button_height, button_width, button_height); + + canvas_draw_line(canvas, x - 1, y, x - 1, y - button_height + 0); + canvas_draw_line(canvas, x - 2, y, x - 2, y - button_height + 1); + canvas_draw_line(canvas, x - 3, y, x - 3, y - button_height + 2); + + canvas_draw_line(canvas, x + button_width + 0, y, x + button_width + 0, y - button_height + 0); + canvas_draw_line(canvas, x + button_width + 1, y, x + button_width + 1, y - button_height + 1); + canvas_draw_line(canvas, x + button_width + 2, y, x + button_width + 2, y - button_height + 2); + + canvas_invert_color(canvas); + canvas_draw_icon( + canvas, + x + horizontal_offset, + y - button_height + vertical_offset - 1, + &I_ButtonCenter_7x7); + canvas_draw_str( + canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str); + canvas_invert_color(canvas); +} + +void subghz_view_transmitter_draw(Canvas* canvas, SubGhzViewTransmitterModel* model) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned( + canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(model->key_str)); + 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->show_button) { + if(model->device_type == SubGhzRadioDeviceTypeInternal) { + canvas_draw_icon(canvas, 108, 39, &I_Internal_antenna_20x12); + } else { + canvas_draw_icon(canvas, 108, 39, &I_External_antenna_20x12); + } + subghz_view_transmitter_button_right(canvas, "Send"); + } +} + +bool subghz_view_transmitter_input(InputEvent* event, void* context) { + furi_assert(context); + SubGhzViewTransmitter* subghz_transmitter = context; + bool can_be_sent = false; + + if(event->key == InputKeyBack && event->type == InputTypeShort) { + with_view_model( + subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { + furi_string_reset(model->frequency_str); + furi_string_reset(model->preset_str); + furi_string_reset(model->key_str); + model->show_button = false; + }, + false); + return false; + } + + with_view_model( + subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { + if(model->show_button) { + can_be_sent = true; + } + }, + true); + + if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) { + subghz_transmitter->callback( + SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context); + return true; + } else if(can_be_sent && event->key == InputKeyOk && event->type == InputTypeRelease) { + subghz_transmitter->callback( + SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context); + return true; + } + + return true; +} + +void subghz_view_transmitter_enter(void* context) { + furi_assert(context); +} + +void subghz_view_transmitter_exit(void* context) { + furi_assert(context); +} + +SubGhzViewTransmitter* subghz_view_transmitter_alloc() { + SubGhzViewTransmitter* subghz_transmitter = malloc(sizeof(SubGhzViewTransmitter)); + + // View allocation and configuration + subghz_transmitter->view = view_alloc(); + view_allocate_model( + subghz_transmitter->view, ViewModelTypeLocking, sizeof(SubGhzViewTransmitterModel)); + view_set_context(subghz_transmitter->view, subghz_transmitter); + view_set_draw_callback( + subghz_transmitter->view, (ViewDrawCallback)subghz_view_transmitter_draw); + view_set_input_callback(subghz_transmitter->view, subghz_view_transmitter_input); + view_set_enter_callback(subghz_transmitter->view, subghz_view_transmitter_enter); + view_set_exit_callback(subghz_transmitter->view, subghz_view_transmitter_exit); + + with_view_model( + subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { + model->frequency_str = furi_string_alloc(); + model->preset_str = furi_string_alloc(); + model->key_str = furi_string_alloc(); + }, + true); + return subghz_transmitter; +} + +void subghz_view_transmitter_free(SubGhzViewTransmitter* subghz_transmitter) { + furi_assert(subghz_transmitter); + + with_view_model( + subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { + furi_string_free(model->frequency_str); + furi_string_free(model->preset_str); + furi_string_free(model->key_str); + }, + true); + view_free(subghz_transmitter->view); + free(subghz_transmitter); +} + +View* subghz_view_transmitter_get_view(SubGhzViewTransmitter* subghz_transmitter) { + furi_assert(subghz_transmitter); + return subghz_transmitter->view; +} diff --git a/applications/subghz/views/transmitter.h b/applications/main/subghz/views/transmitter.h similarity index 79% rename from applications/subghz/views/transmitter.h rename to applications/main/subghz/views/transmitter.h index 64bcbd1afae..19da3145c95 100644 --- a/applications/subghz/views/transmitter.h +++ b/applications/main/subghz/views/transmitter.h @@ -1,6 +1,7 @@ #pragma once #include +#include "../helpers/subghz_types.h" #include "../helpers/subghz_custom_event.h" typedef struct SubGhzViewTransmitter SubGhzViewTransmitter; @@ -12,6 +13,10 @@ void subghz_view_transmitter_set_callback( SubGhzViewTransmitterCallback callback, void* context); +void subghz_view_transmitter_set_radio_device_type( + SubGhzViewTransmitter* subghz_transmitter, + SubGhzRadioDeviceType device_type); + SubGhzViewTransmitter* subghz_view_transmitter_alloc(); void subghz_view_transmitter_free(SubGhzViewTransmitter* subghz_transmitter); @@ -23,4 +28,4 @@ void subghz_view_transmitter_add_data_to_show( const char* key_str, const char* frequency_str, const char* preset_str, - uint8_t show_button); + bool show_button); diff --git a/applications/main/u2f/application.fam b/applications/main/u2f/application.fam new file mode 100644 index 00000000000..5e0cde736c9 --- /dev/null +++ b/applications/main/u2f/application.fam @@ -0,0 +1,13 @@ +App( + appid="u2f", + name="U2F", + apptype=FlipperAppType.MENUEXTERNAL, + entry_point="u2f_app", + stack_size=2 * 1024, + icon="A_U2F_14", + order=80, + resources="resources", + fap_libs=["assets", "mbedtls"], + fap_category="USB", + fap_icon="icon.png", +) diff --git a/applications/main/u2f/icon.png b/applications/main/u2f/icon.png new file mode 100644 index 00000000000..fcd87a2ef56 Binary files /dev/null and b/applications/main/u2f/icon.png differ diff --git a/assets/resources/u2f/assets/cert.der b/applications/main/u2f/resources/u2f/assets/cert.der similarity index 100% rename from assets/resources/u2f/assets/cert.der rename to applications/main/u2f/resources/u2f/assets/cert.der diff --git a/assets/resources/u2f/assets/cert_key.u2f b/applications/main/u2f/resources/u2f/assets/cert_key.u2f similarity index 100% rename from assets/resources/u2f/assets/cert_key.u2f rename to applications/main/u2f/resources/u2f/assets/cert_key.u2f diff --git a/applications/u2f/scenes/u2f_scene.c b/applications/main/u2f/scenes/u2f_scene.c similarity index 100% rename from applications/u2f/scenes/u2f_scene.c rename to applications/main/u2f/scenes/u2f_scene.c diff --git a/applications/u2f/scenes/u2f_scene.h b/applications/main/u2f/scenes/u2f_scene.h similarity index 100% rename from applications/u2f/scenes/u2f_scene.h rename to applications/main/u2f/scenes/u2f_scene.h diff --git a/applications/u2f/scenes/u2f_scene_config.h b/applications/main/u2f/scenes/u2f_scene_config.h similarity index 100% rename from applications/u2f/scenes/u2f_scene_config.h rename to applications/main/u2f/scenes/u2f_scene_config.h diff --git a/applications/u2f/scenes/u2f_scene_error.c b/applications/main/u2f/scenes/u2f_scene_error.c similarity index 100% rename from applications/u2f/scenes/u2f_scene_error.c rename to applications/main/u2f/scenes/u2f_scene_error.c diff --git a/applications/u2f/scenes/u2f_scene_main.c b/applications/main/u2f/scenes/u2f_scene_main.c similarity index 97% rename from applications/u2f/scenes/u2f_scene_main.c rename to applications/main/u2f/scenes/u2f_scene_main.c index 60ed71c7869..992236e7a8b 100644 --- a/applications/u2f/scenes/u2f_scene_main.c +++ b/applications/main/u2f/scenes/u2f_scene_main.c @@ -1,7 +1,7 @@ #include "../u2f_app_i.h" #include "../views/u2f_view.h" #include -#include "furi_hal.h" +#include #include "../u2f.h" #define U2F_REQUEST_TIMEOUT 500 @@ -58,7 +58,7 @@ bool u2f_scene_main_on_event(void* context, SceneManagerEvent event) { app->event_cur = event.event; if(event.event == U2fCustomEventRegister) u2f_view_set_state(app->u2f_view, U2fMsgRegister); - else if(event.event == U2fCustomEventAuth) + else if(event.event == U2fCustomEventAuth) //-V547 u2f_view_set_state(app->u2f_view, U2fMsgAuth); notification_message(app->notifications, &sequence_display_backlight_on); notification_message(app->notifications, &sequence_single_vibro); @@ -68,7 +68,7 @@ bool u2f_scene_main_on_event(void* context, SceneManagerEvent event) { notification_message(app->notifications, &sequence_blink_magenta_10); } else if(event.event == U2fCustomEventAuthSuccess) { notification_message_block(app->notifications, &sequence_set_green_255); - DOLPHIN_DEED(DolphinDeedU2fAuthorized); + dolphin_deed(DolphinDeedU2fAuthorized); furi_timer_start(app->timer, U2F_SUCCESS_TIMEOUT); app->event_cur = U2fCustomEventNone; u2f_view_set_state(app->u2f_view, U2fMsgSuccess); diff --git a/applications/main/u2f/u2f.c b/applications/main/u2f/u2f.c new file mode 100644 index 00000000000..3bdafe9185e --- /dev/null +++ b/applications/main/u2f/u2f.c @@ -0,0 +1,449 @@ +#include "u2f.h" +#include "u2f_hid.h" +#include "u2f_data.h" + +#include +#include +#include +#include // for lfs_tobe32 + +#include +#include +#include +#include + +#define TAG "U2f" +#define WORKER_TAG TAG "Worker" + +#define MCHECK(expr) furi_check((expr) == 0) + +#define U2F_CMD_REGISTER 0x01 +#define U2F_CMD_AUTHENTICATE 0x02 +#define U2F_CMD_VERSION 0x03 + +typedef enum { + U2fCheckOnly = 0x07, // "check-only" - only check key handle, don't send auth response + U2fEnforce = + 0x03, // "enforce-user-presence-and-sign" - send auth response only if user is present + U2fDontEnforce = + 0x08, // "dont-enforce-user-presence-and-sign" - send auth response even if user is missing +} U2fAuthMode; + +#define U2F_HASH_SIZE 32 +#define U2F_NONCE_SIZE 32 +#define U2F_CHALLENGE_SIZE 32 +#define U2F_APP_ID_SIZE 32 + +#define U2F_EC_KEY_SIZE 32 +#define U2F_EC_BIGNUM_SIZE 32 +#define U2F_EC_POINT_SIZE 65 + +typedef struct { + uint8_t format; + uint8_t xy[64]; +} FURI_PACKED U2fPubKey; +_Static_assert(sizeof(U2fPubKey) == U2F_EC_POINT_SIZE, "U2fPubKey size mismatch"); + +typedef struct { + uint8_t len; + uint8_t hash[U2F_HASH_SIZE]; + uint8_t nonce[U2F_NONCE_SIZE]; +} FURI_PACKED U2fKeyHandle; + +typedef struct { + uint8_t cla; + uint8_t ins; + uint8_t p1; + uint8_t p2; + uint8_t len[3]; + uint8_t challenge[U2F_CHALLENGE_SIZE]; + uint8_t app_id[U2F_APP_ID_SIZE]; +} FURI_PACKED U2fRegisterReq; + +typedef struct { + uint8_t reserved; + U2fPubKey pub_key; + U2fKeyHandle key_handle; + uint8_t cert[]; +} FURI_PACKED U2fRegisterResp; + +typedef struct { + uint8_t cla; + uint8_t ins; + uint8_t p1; + uint8_t p2; + uint8_t len[3]; + uint8_t challenge[U2F_CHALLENGE_SIZE]; + uint8_t app_id[U2F_APP_ID_SIZE]; + U2fKeyHandle key_handle; +} FURI_PACKED U2fAuthReq; + +typedef struct { + uint8_t user_present; + uint32_t counter; + uint8_t signature[]; +} FURI_PACKED U2fAuthResp; + +static const uint8_t ver_str[] = {"U2F_V2"}; + +static const uint8_t state_no_error[] = {0x90, 0x00}; +static const uint8_t state_not_supported[] = {0x6D, 0x00}; +static const uint8_t state_user_missing[] = {0x69, 0x85}; +static const uint8_t state_wrong_data[] = {0x6A, 0x80}; + +struct U2fData { + uint8_t device_key[U2F_EC_KEY_SIZE]; + uint8_t cert_key[U2F_EC_KEY_SIZE]; + uint32_t counter; + bool ready; + bool user_present; + U2fEvtCallback callback; + void* context; + mbedtls_ecp_group group; +}; + +static int u2f_uecc_random_cb(void* context, uint8_t* dest, unsigned size) { + UNUSED(context); + furi_hal_random_fill_buf(dest, size); + return 0; +} + +U2fData* u2f_alloc() { + return malloc(sizeof(U2fData)); +} + +void u2f_free(U2fData* U2F) { + furi_assert(U2F); + mbedtls_ecp_group_free(&U2F->group); + free(U2F); +} + +bool u2f_init(U2fData* U2F) { + furi_assert(U2F); + + if(u2f_data_cert_check() == false) { + FURI_LOG_E(TAG, "Certificate load error"); + return false; + } + if(u2f_data_cert_key_load(U2F->cert_key) == false) { + FURI_LOG_E(TAG, "Certificate key load error"); + return false; + } + if(u2f_data_key_load(U2F->device_key) == false) { + FURI_LOG_W(TAG, "Key loading error, generating new"); + if(u2f_data_key_generate(U2F->device_key) == false) { + FURI_LOG_E(TAG, "Key write failed"); + return false; + } + } + if(u2f_data_cnt_read(&U2F->counter) == false) { + FURI_LOG_W(TAG, "Counter loading error, resetting counter"); + U2F->counter = 0; + if(u2f_data_cnt_write(0) == false) { + FURI_LOG_E(TAG, "Counter write failed"); + return false; + } + } + + mbedtls_ecp_group_init(&U2F->group); + mbedtls_ecp_group_load(&U2F->group, MBEDTLS_ECP_DP_SECP256R1); + + U2F->ready = true; + return true; +} + +void u2f_set_event_callback(U2fData* U2F, U2fEvtCallback callback, void* context) { + furi_assert(U2F); + furi_assert(callback); + U2F->callback = callback; + U2F->context = context; +} + +void u2f_confirm_user_present(U2fData* U2F) { + U2F->user_present = true; +} + +static uint8_t u2f_der_encode_int(uint8_t* der, uint8_t* val, uint8_t val_len) { + der[0] = 0x02; // Integer + + uint8_t len = 2; + // Omit leading zeros + while(val[0] == 0 && val_len > 0) { + ++val; + --val_len; + } + + // Check if integer is negative + if(val[0] > 0x7f) der[len++] = 0; + + memcpy(der + len, val, val_len); + len += val_len; + + der[1] = len - 2; + return len; +} + +static uint8_t u2f_der_encode_signature(uint8_t* der, uint8_t* sig) { + der[0] = 0x30; + + uint8_t len = 2; + len += u2f_der_encode_int(der + len, sig, U2F_HASH_SIZE); + len += u2f_der_encode_int(der + len, sig + U2F_HASH_SIZE, U2F_HASH_SIZE); + + der[1] = len - 2; + return len; +} + +static void + u2f_ecc_sign(mbedtls_ecp_group* grp, const uint8_t* key, uint8_t* hash, uint8_t* signature) { + mbedtls_mpi r, s, d; + + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&s); + mbedtls_mpi_init(&d); + + MCHECK(mbedtls_mpi_read_binary(&d, key, U2F_EC_KEY_SIZE)); + MCHECK(mbedtls_ecdsa_sign(grp, &r, &s, &d, hash, U2F_HASH_SIZE, u2f_uecc_random_cb, NULL)); + MCHECK(mbedtls_mpi_write_binary(&r, signature, U2F_EC_BIGNUM_SIZE)); + MCHECK(mbedtls_mpi_write_binary(&s, signature + U2F_EC_BIGNUM_SIZE, U2F_EC_BIGNUM_SIZE)); + + mbedtls_mpi_free(&r); + mbedtls_mpi_free(&s); + mbedtls_mpi_free(&d); +} + +static void u2f_ecc_compute_public_key( + mbedtls_ecp_group* grp, + const uint8_t* private_key, + U2fPubKey* public_key) { + mbedtls_ecp_point Q; + mbedtls_mpi d; + size_t olen; + + mbedtls_ecp_point_init(&Q); + mbedtls_mpi_init(&d); + + MCHECK(mbedtls_mpi_read_binary(&d, private_key, U2F_EC_KEY_SIZE)); + MCHECK(mbedtls_ecp_mul(grp, &Q, &d, &grp->G, u2f_uecc_random_cb, NULL)); + MCHECK(mbedtls_ecp_check_privkey(grp, &d)); + + MCHECK(mbedtls_ecp_point_write_binary( + grp, &Q, MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, (unsigned char*)public_key, sizeof(U2fPubKey))); + + mbedtls_ecp_point_free(&Q); + mbedtls_mpi_free(&d); +} + +/////////////////////////////////////////// + +static uint16_t u2f_register(U2fData* U2F, uint8_t* buf) { + U2fRegisterReq* req = (U2fRegisterReq*)buf; + U2fRegisterResp* resp = (U2fRegisterResp*)buf; + U2fKeyHandle handle; + uint8_t private[U2F_EC_KEY_SIZE]; + U2fPubKey pub_key; + uint8_t hash[U2F_HASH_SIZE]; + uint8_t signature[U2F_EC_BIGNUM_SIZE * 2]; + + if(u2f_data_check(false) == false) { + U2F->ready = false; + if(U2F->callback != NULL) U2F->callback(U2fNotifyError, U2F->context); + memcpy(&buf[0], state_not_supported, 2); + return 2; + } + + if(U2F->callback != NULL) U2F->callback(U2fNotifyRegister, U2F->context); + if(U2F->user_present == false) { + memcpy(&buf[0], state_user_missing, 2); + return 2; + } + U2F->user_present = false; + + handle.len = U2F_HASH_SIZE * 2; + + // Generate random nonce + furi_hal_random_fill_buf(handle.nonce, 32); + + { + mbedtls_md_context_t hmac_ctx; + mbedtls_md_init(&hmac_ctx); + MCHECK(mbedtls_md_setup(&hmac_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1)); + MCHECK(mbedtls_md_hmac_starts(&hmac_ctx, U2F->device_key, sizeof(U2F->device_key))); + + // Generate private key + MCHECK(mbedtls_md_hmac_update(&hmac_ctx, req->app_id, sizeof(req->app_id))); + MCHECK(mbedtls_md_hmac_update(&hmac_ctx, handle.nonce, sizeof(handle.nonce))); + MCHECK(mbedtls_md_hmac_finish(&hmac_ctx, private)); + + MCHECK(mbedtls_md_hmac_reset(&hmac_ctx)); + + // Generate private key handle + MCHECK(mbedtls_md_hmac_update(&hmac_ctx, private, sizeof(private))); + MCHECK(mbedtls_md_hmac_update(&hmac_ctx, req->app_id, sizeof(req->app_id))); + MCHECK(mbedtls_md_hmac_finish(&hmac_ctx, handle.hash)); + } + + // Generate public key + u2f_ecc_compute_public_key(&U2F->group, private, &pub_key); + + // Generate signature + { + uint8_t reserved_byte = 0; + + mbedtls_sha256_context sha_ctx; + + mbedtls_sha256_init(&sha_ctx); + mbedtls_sha256_starts(&sha_ctx, 0); + + mbedtls_sha256_update(&sha_ctx, &reserved_byte, 1); + mbedtls_sha256_update(&sha_ctx, req->app_id, sizeof(req->app_id)); + mbedtls_sha256_update(&sha_ctx, req->challenge, sizeof(req->challenge)); + mbedtls_sha256_update(&sha_ctx, handle.hash, handle.len); + mbedtls_sha256_update(&sha_ctx, (uint8_t*)&pub_key, sizeof(U2fPubKey)); + + mbedtls_sha256_finish(&sha_ctx, hash); + mbedtls_sha256_free(&sha_ctx); + } + + // Sign hash + u2f_ecc_sign(&U2F->group, U2F->cert_key, hash, signature); + + // Encode response message + resp->reserved = 0x05; + memcpy(&(resp->pub_key), &pub_key, sizeof(U2fPubKey)); + memcpy(&(resp->key_handle), &handle, sizeof(U2fKeyHandle)); + uint32_t cert_len = u2f_data_cert_load(resp->cert); + uint8_t signature_len = u2f_der_encode_signature(resp->cert + cert_len, signature); + memcpy(resp->cert + cert_len + signature_len, state_no_error, 2); + + return (sizeof(U2fRegisterResp) + cert_len + signature_len + 2); +} + +static uint16_t u2f_authenticate(U2fData* U2F, uint8_t* buf) { + U2fAuthReq* req = (U2fAuthReq*)buf; + U2fAuthResp* resp = (U2fAuthResp*)buf; + uint8_t priv_key[U2F_EC_KEY_SIZE]; + uint8_t mac_control[32]; + uint8_t flags = 0; + uint8_t hash[U2F_HASH_SIZE]; + uint8_t signature[U2F_HASH_SIZE * 2]; + uint32_t be_u2f_counter; + + if(u2f_data_check(false) == false) { + U2F->ready = false; + if(U2F->callback != NULL) U2F->callback(U2fNotifyError, U2F->context); + memcpy(&buf[0], state_not_supported, 2); + return 2; + } + + if(U2F->callback != NULL) U2F->callback(U2fNotifyAuth, U2F->context); + if(U2F->user_present == true) { + flags |= 1; + } else { + if(req->p1 == U2fEnforce) { + memcpy(&buf[0], state_user_missing, 2); + return 2; + } + } + U2F->user_present = false; + + // The 4 byte counter is represented in big endian. Increment it before use + be_u2f_counter = lfs_tobe32(U2F->counter + 1); + + // Generate hash + { + mbedtls_sha256_context sha_ctx; + + mbedtls_sha256_init(&sha_ctx); + mbedtls_sha256_starts(&sha_ctx, 0); + + mbedtls_sha256_update(&sha_ctx, req->app_id, sizeof(req->app_id)); + mbedtls_sha256_update(&sha_ctx, &flags, 1); + mbedtls_sha256_update(&sha_ctx, (uint8_t*)&(be_u2f_counter), sizeof(be_u2f_counter)); + mbedtls_sha256_update(&sha_ctx, req->challenge, sizeof(req->challenge)); + + mbedtls_sha256_finish(&sha_ctx, hash); + mbedtls_sha256_free(&sha_ctx); + } + + { + mbedtls_md_context_t hmac_ctx; + mbedtls_md_init(&hmac_ctx); + MCHECK(mbedtls_md_setup(&hmac_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1)); + MCHECK(mbedtls_md_hmac_starts(&hmac_ctx, U2F->device_key, sizeof(U2F->device_key))); + + // Recover private key + MCHECK(mbedtls_md_hmac_update(&hmac_ctx, req->app_id, sizeof(req->app_id))); + MCHECK(mbedtls_md_hmac_update( + &hmac_ctx, req->key_handle.nonce, sizeof(req->key_handle.nonce))); + MCHECK(mbedtls_md_hmac_finish(&hmac_ctx, priv_key)); + + MCHECK(mbedtls_md_hmac_reset(&hmac_ctx)); + + // Generate and verify private key handle + MCHECK(mbedtls_md_hmac_update(&hmac_ctx, priv_key, sizeof(priv_key))); + MCHECK(mbedtls_md_hmac_update(&hmac_ctx, req->app_id, sizeof(req->app_id))); + MCHECK(mbedtls_md_hmac_finish(&hmac_ctx, mac_control)); + } + + if(memcmp(req->key_handle.hash, mac_control, sizeof(mac_control)) != 0) { + FURI_LOG_W(TAG, "Wrong handle!"); + memcpy(&buf[0], state_wrong_data, 2); + return 2; + } + + if(req->p1 == U2fCheckOnly) { // Check-only: don't need to send full response + memcpy(&buf[0], state_user_missing, 2); + return 2; + } + + // Sign hash + u2f_ecc_sign(&U2F->group, priv_key, hash, signature); + + resp->user_present = flags; + resp->counter = be_u2f_counter; + uint8_t signature_len = u2f_der_encode_signature(resp->signature, signature); + memcpy(resp->signature + signature_len, state_no_error, 2); + + U2F->counter++; + FURI_LOG_D(TAG, "Counter: %lu", U2F->counter); + u2f_data_cnt_write(U2F->counter); + + if(U2F->callback != NULL) U2F->callback(U2fNotifyAuthSuccess, U2F->context); + + return (sizeof(U2fAuthResp) + signature_len + 2); +} + +uint16_t u2f_msg_parse(U2fData* U2F, uint8_t* buf, uint16_t len) { + furi_assert(U2F); + if(!U2F->ready) return 0; + if((buf[0] != 0x00) && (len < 5)) return 0; + if(buf[1] == U2F_CMD_REGISTER) { // Register request + return u2f_register(U2F, buf); + + } else if(buf[1] == U2F_CMD_AUTHENTICATE) { // Authenticate request + return u2f_authenticate(U2F, buf); + + } else if(buf[1] == U2F_CMD_VERSION) { // Get U2F version string + memcpy(&buf[0], ver_str, 6); + memcpy(&buf[6], state_no_error, 2); + return 8; + } else { + memcpy(&buf[0], state_not_supported, 2); + return 2; + } + return 0; +} + +void u2f_wink(U2fData* U2F) { + if(U2F->callback != NULL) U2F->callback(U2fNotifyWink, U2F->context); +} + +void u2f_set_state(U2fData* U2F, uint8_t state) { + if(state == 0) { + if(U2F->callback != NULL) U2F->callback(U2fNotifyDisconnect, U2F->context); + } else { + if(U2F->callback != NULL) U2F->callback(U2fNotifyConnect, U2F->context); + } + U2F->user_present = false; +} diff --git a/applications/u2f/u2f.h b/applications/main/u2f/u2f.h similarity index 100% rename from applications/u2f/u2f.h rename to applications/main/u2f/u2f.h diff --git a/applications/u2f/u2f_app.c b/applications/main/u2f/u2f_app.c similarity index 100% rename from applications/u2f/u2f_app.c rename to applications/main/u2f/u2f_app.c diff --git a/applications/u2f/u2f_app.h b/applications/main/u2f/u2f_app.h similarity index 100% rename from applications/u2f/u2f_app.h rename to applications/main/u2f/u2f_app.h diff --git a/applications/u2f/u2f_app_i.h b/applications/main/u2f/u2f_app_i.h similarity index 97% rename from applications/u2f/u2f_app_i.h rename to applications/main/u2f/u2f_app_i.h index 53647859ab8..2896684c373 100644 --- a/applications/u2f/u2f_app_i.h +++ b/applications/main/u2f/u2f_app_i.h @@ -4,6 +4,7 @@ #include "scenes/u2f_scene.h" #include +#include #include #include #include diff --git a/applications/u2f/u2f_data.c b/applications/main/u2f/u2f_data.c similarity index 87% rename from applications/u2f/u2f_data.c rename to applications/main/u2f/u2f_data.c index 117fbdbe36d..cb9c4c2947d 100644 --- a/applications/u2f/u2f_data.c +++ b/applications/main/u2f/u2f_data.c @@ -5,16 +5,16 @@ #include #include -#define TAG "U2F" +#define TAG "U2f" -#define U2F_DATA_FOLDER ANY_PATH("u2f/") +#define U2F_DATA_FOLDER EXT_PATH("u2f/") #define U2F_CERT_FILE U2F_DATA_FOLDER "assets/cert.der" #define U2F_CERT_KEY_FILE U2F_DATA_FOLDER "assets/cert_key.u2f" #define U2F_KEY_FILE U2F_DATA_FOLDER "key.u2f" #define U2F_CNT_FILE U2F_DATA_FOLDER "cnt.u2f" #define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_FACTORY 2 -#define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE 11 +#define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE FURI_HAL_CRYPTO_ENCLAVE_UNIQUE_KEY_SLOT #define U2F_CERT_STOCK 0 // Stock certificate, private key is encrypted with factory key #define U2F_CERT_USER 1 // User certificate, private key is encrypted with unique key @@ -37,7 +37,7 @@ typedef struct { uint32_t counter; uint8_t random_salt[24]; uint32_t control; -} __attribute__((packed)) U2fCounterData; +} FURI_PACKED U2fCounterData; bool u2f_data_check(bool cert_only) { bool state = false; @@ -136,7 +136,7 @@ static bool u2f_data_cert_key_encrypt(uint8_t* cert_key) { // Generate random IV furi_hal_random_fill_buf(iv, 16); - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); return false; } @@ -145,7 +145,7 @@ static bool u2f_data_cert_key_encrypt(uint8_t* cert_key) { FURI_LOG_E(TAG, "Encryption failed"); return false; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); @@ -179,10 +179,10 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) { uint32_t version = 0; // Check if unique key exists in secure eclave and generate it if missing - if(!furi_hal_crypto_verify_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE)) return false; + if(!furi_hal_crypto_enclave_ensure_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE)) return false; - string_t filetype; - string_init(filetype); + FuriString* filetype; + filetype = furi_string_alloc(); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); @@ -194,7 +194,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) { break; } - if(strcmp(string_get_cstr(filetype), U2F_CERT_KEY_FILE_TYPE) != 0 || + if(strcmp(furi_string_get_cstr(filetype), U2F_CERT_KEY_FILE_TYPE) != 0 || version != U2F_CERT_KEY_VERSION) { FURI_LOG_E(TAG, "Type or version mismatch"); break; @@ -226,7 +226,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) { break; } - if(!furi_hal_crypto_store_load_key(key_slot, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -237,7 +237,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) { FURI_LOG_E(TAG, "Decryption failed"); break; } - furi_hal_crypto_store_unload_key(key_slot); + furi_hal_crypto_enclave_unload_key(key_slot); } else { if(!flipper_format_read_hex(flipper_format, "Data", cert_key, 32)) { FURI_LOG_E(TAG, "Missing data"); @@ -250,7 +250,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) { flipper_format_free(flipper_format); furi_record_close(RECORD_STORAGE); - string_clear(filetype); + furi_string_free(filetype); if(cert_type == U2F_CERT_USER_UNENCRYPTED) { return u2f_data_cert_key_encrypt(cert_key); @@ -267,8 +267,8 @@ bool u2f_data_key_load(uint8_t* device_key) { uint8_t key[48]; uint32_t version = 0; - string_t filetype; - string_init(filetype); + FuriString* filetype; + filetype = furi_string_alloc(); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); @@ -279,7 +279,7 @@ bool u2f_data_key_load(uint8_t* device_key) { FURI_LOG_E(TAG, "Missing or incorrect header"); break; } - if(strcmp(string_get_cstr(filetype), U2F_DEVICE_KEY_FILE_TYPE) != 0 || + if(strcmp(furi_string_get_cstr(filetype), U2F_DEVICE_KEY_FILE_TYPE) != 0 || version != U2F_DEVICE_KEY_VERSION) { FURI_LOG_E(TAG, "Type or version mismatch"); break; @@ -292,7 +292,7 @@ bool u2f_data_key_load(uint8_t* device_key) { FURI_LOG_E(TAG, "Missing data"); break; } - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -302,13 +302,13 @@ bool u2f_data_key_load(uint8_t* device_key) { FURI_LOG_E(TAG, "Decryption failed"); break; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); state = true; } while(0); } flipper_format_free(flipper_format); furi_record_close(RECORD_STORAGE); - string_clear(filetype); + furi_string_free(filetype); return state; } @@ -324,7 +324,7 @@ bool u2f_data_key_generate(uint8_t* device_key) { furi_hal_random_fill_buf(iv, 16); furi_hal_random_fill_buf(key, 32); - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); return false; } @@ -333,7 +333,7 @@ bool u2f_data_key_generate(uint8_t* device_key) { FURI_LOG_E(TAG, "Encryption failed"); return false; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); @@ -366,8 +366,8 @@ bool u2f_data_cnt_read(uint32_t* cnt_val) { uint8_t cnt_encr[48]; uint32_t version = 0; - string_t filetype; - string_init(filetype); + FuriString* filetype; + filetype = furi_string_alloc(); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); @@ -378,7 +378,7 @@ bool u2f_data_cnt_read(uint32_t* cnt_val) { FURI_LOG_E(TAG, "Missing or incorrect header"); break; } - if(strcmp(string_get_cstr(filetype), U2F_COUNTER_FILE_TYPE) != 0) { + if(strcmp(furi_string_get_cstr(filetype), U2F_COUNTER_FILE_TYPE) != 0) { FURI_LOG_E(TAG, "Type mismatch"); break; } @@ -398,17 +398,17 @@ bool u2f_data_cnt_read(uint32_t* cnt_val) { FURI_LOG_E(TAG, "Missing data"); break; } - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } - memset(&cnt, 0, 32); - if(!furi_hal_crypto_decrypt(cnt_encr, (uint8_t*)&cnt, 32)) { - memset(&cnt, 0, 32); + memset(&cnt, 0, sizeof(U2fCounterData)); + if(!furi_hal_crypto_decrypt(cnt_encr, (uint8_t*)&cnt, sizeof(U2fCounterData))) { + memset(&cnt, 0, sizeof(U2fCounterData)); FURI_LOG_E(TAG, "Decryption failed"); break; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); if(cnt.control == U2F_COUNTER_CONTROL_VAL) { *cnt_val = cnt.counter; state = true; @@ -417,7 +417,7 @@ bool u2f_data_cnt_read(uint32_t* cnt_val) { } flipper_format_free(flipper_format); furi_record_close(RECORD_STORAGE); - string_clear(filetype); + furi_string_free(filetype); if(old_counter && state) { // Change counter endianness and rewrite counter file @@ -440,7 +440,7 @@ bool u2f_data_cnt_write(uint32_t cnt_val) { cnt.control = U2F_COUNTER_CONTROL_VAL; cnt.counter = cnt_val; - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); return false; } @@ -449,7 +449,7 @@ bool u2f_data_cnt_write(uint32_t cnt_val) { FURI_LOG_E(TAG, "Encryption failed"); return false; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); diff --git a/applications/u2f/u2f_data.h b/applications/main/u2f/u2f_data.h similarity index 100% rename from applications/u2f/u2f_data.h rename to applications/main/u2f/u2f_data.h diff --git a/applications/u2f/u2f_hid.c b/applications/main/u2f/u2f_hid.c similarity index 91% rename from applications/u2f/u2f_hid.c rename to applications/main/u2f/u2f_hid.c index 4922d6a5af7..d7d7e6cf413 100644 --- a/applications/u2f/u2f_hid.c +++ b/applications/main/u2f/u2f_hid.c @@ -10,7 +10,7 @@ #include -#define TAG "U2FHID" +#define TAG "U2fHid" #define WORKER_TAG TAG "Worker" #define U2F_HID_MAX_PAYLOAD_LEN ((HID_U2F_PACKET_LEN - 7) + 128 * (HID_U2F_PACKET_LEN - 5)) @@ -58,13 +58,13 @@ struct U2fHid_packet { struct U2fHid { FuriThread* thread; FuriTimer* lock_timer; - struct U2fHid_packet packet; uint8_t seq_id_last; uint16_t req_buf_ptr; uint32_t req_len_left; uint32_t lock_cid; bool lock; U2fData* u2f_instance; + struct U2fHid_packet packet; }; static void u2f_hid_event_callback(HidU2fEvent ev, void* context) { @@ -94,7 +94,7 @@ static void u2f_hid_send_response(U2fHid* u2f_hid) { uint16_t data_ptr = 0; memset(packet_buf, 0, HID_U2F_PACKET_LEN); - memcpy(packet_buf, &(u2f_hid->packet.cid), 4); + memcpy(packet_buf, &(u2f_hid->packet.cid), sizeof(uint32_t)); //-V1086 // Init packet packet_buf[4] = u2f_hid->packet.cmd; @@ -166,7 +166,7 @@ static bool u2f_hid_parse_request(U2fHid* u2f_hid) { return false; u2f_hid->packet.len = 17; uint32_t random_cid = furi_hal_random_get(); - memcpy(&(u2f_hid->packet.payload[8]), &random_cid, 4); + memcpy(&(u2f_hid->packet.payload[8]), &random_cid, sizeof(uint32_t)); //-V1086 u2f_hid->packet.payload[12] = 2; // Protocol version u2f_hid->packet.payload[13] = 1; // Device version major u2f_hid->packet.payload[14] = 0; // Device version minor @@ -177,7 +177,7 @@ static bool u2f_hid_parse_request(U2fHid* u2f_hid) { } else if(u2f_hid->packet.cmd == U2F_HID_WINK) { // WINK - notify user if(u2f_hid->packet.len != 0) return false; u2f_wink(u2f_hid->u2f_instance); - u2f_hid->packet.len = 0; + u2f_hid->packet.len = 0; //-V1048 u2f_hid_send_response(u2f_hid); } else return false; @@ -203,7 +203,7 @@ static int32_t u2f_hid_worker(void* context) { WorkerEvtStop | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtRequest, FuriFlagWaitAny, FuriWaitForever); - furi_check((flags & FuriFlagError) == 0); + furi_check(!(flags & FuriFlagError)); if(flags & WorkerEvtStop) break; if(flags & WorkerEvtConnect) { u2f_set_state(u2f_hid->u2f_instance, 1); @@ -215,10 +215,21 @@ static int32_t u2f_hid_worker(void* context) { } if(flags & WorkerEvtRequest) { uint32_t len_cur = furi_hal_hid_u2f_get_request(packet_buf); - if(len_cur > 0) { + do { + if(len_cur == 0) { + break; + } if((packet_buf[4] & U2F_HID_TYPE_MASK) == U2F_HID_TYPE_INIT) { + if(len_cur < 7) { + u2f_hid->req_len_left = 0; + break; // Wrong chunk len + } // Init packet u2f_hid->packet.len = (packet_buf[5] << 8) | (packet_buf[6]); + if(u2f_hid->packet.len > U2F_HID_MAX_PAYLOAD_LEN) { + u2f_hid->req_len_left = 0; + break; // Wrong packet len + } if(u2f_hid->packet.len > (len_cur - 7)) { u2f_hid->req_len_left = u2f_hid->packet.len - (len_cur - 7); len_cur = len_cur - 7; @@ -232,6 +243,10 @@ static int32_t u2f_hid_worker(void* context) { u2f_hid->req_buf_ptr = len_cur; if(len_cur > 0) memcpy(u2f_hid->packet.payload, &packet_buf[7], len_cur); } else { + if(len_cur < 5) { + u2f_hid->req_len_left = 0; + break; // Wrong chunk len + } // Continuation packet if(u2f_hid->req_len_left > 0) { uint32_t cid_temp = 0; @@ -260,7 +275,7 @@ static int32_t u2f_hid_worker(void* context) { u2f_hid_send_error(u2f_hid, U2F_HID_ERR_INVALID_CMD); } } - } + } while(0); } if(flags & WorkerEvtUnlock) { u2f_hid->lock = false; @@ -282,11 +297,7 @@ U2fHid* u2f_hid_start(U2fData* u2f_inst) { u2f_hid->u2f_instance = u2f_inst; - u2f_hid->thread = furi_thread_alloc(); - furi_thread_set_name(u2f_hid->thread, "U2fHidWorker"); - furi_thread_set_stack_size(u2f_hid->thread, 2048); - furi_thread_set_context(u2f_hid->thread, u2f_hid); - furi_thread_set_callback(u2f_hid->thread, u2f_hid_worker); + u2f_hid->thread = furi_thread_alloc_ex("U2fHidWorker", 2048, u2f_hid_worker, u2f_hid); furi_thread_start(u2f_hid->thread); return u2f_hid; } diff --git a/applications/u2f/u2f_hid.h b/applications/main/u2f/u2f_hid.h similarity index 100% rename from applications/u2f/u2f_hid.h rename to applications/main/u2f/u2f_hid.h diff --git a/applications/u2f/views/u2f_view.c b/applications/main/u2f/views/u2f_view.c similarity index 92% rename from applications/u2f/views/u2f_view.c rename to applications/main/u2f/views/u2f_view.c index 11e2c9b0462..bf220ca2c3a 100644 --- a/applications/u2f/views/u2f_view.c +++ b/applications/main/u2f/views/u2f_view.c @@ -1,5 +1,6 @@ #include "u2f_view.h" #include +#include struct U2fView { View* view; @@ -36,10 +37,10 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { } else if(model->display_msg == U2fMsgSuccess) { canvas_draw_icon(canvas, 22, 15, &I_Connected_62x31); canvas_draw_str_aligned( - canvas, 128 / 2, 3, AlignCenter, AlignTop, "Authentication successfull!"); + canvas, 128 / 2, 3, AlignCenter, AlignTop, "Authentication successful!"); } else if(model->display_msg == U2fMsgError) { canvas_draw_icon(canvas, 22, 15, &I_Error_62x31); - canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Ceritficate error"); + canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Certificate error"); } } @@ -85,18 +86,17 @@ void u2f_view_set_ok_callback(U2fView* u2f, U2fOkCallback callback, void* contex furi_assert(u2f); furi_assert(callback); with_view_model( - u2f->view, (U2fModel * model) { + u2f->view, + U2fModel * model, + { UNUSED(model); u2f->callback = callback; u2f->context = context; - return false; - }); + }, + false); } void u2f_view_set_state(U2fView* u2f, U2fViewMsg msg) { with_view_model( - u2f->view, (U2fModel * model) { - model->display_msg = msg; - return true; - }); + u2f->view, U2fModel * model, { model->display_msg = msg; }, true); } diff --git a/applications/u2f/views/u2f_view.h b/applications/main/u2f/views/u2f_view.h similarity index 100% rename from applications/u2f/views/u2f_view.h rename to applications/main/u2f/views/u2f_view.h diff --git a/applications/meta/application.fam b/applications/meta/application.fam deleted file mode 100644 index a447b94ae4d..00000000000 --- a/applications/meta/application.fam +++ /dev/null @@ -1,41 +0,0 @@ -App( - appid="basic_services", - name="Basic services", - apptype=FlipperAppType.METAPACKAGE, - provides=[ - "rpc", - "bt", - "desktop", - "loader", - "power", - ], -) - - -App( - appid="basic_apps", - name="Basic applications for main menu", - apptype=FlipperAppType.METAPACKAGE, - provides=[ - "gpio", - "ibutton", - "infrared", - "lfrfid", - "nfc", - "subghz", - "bad_usb", - "u2f", - ], -) - - -App( - appid="basic_plugins", - name="Basic applications for plug-in menu", - apptype=FlipperAppType.METAPACKAGE, - provides=[ - "music_player", - "snake_game", - "bt_hid", - ], -) diff --git a/applications/music_player/application.fam b/applications/music_player/application.fam deleted file mode 100644 index 70e2974323a..00000000000 --- a/applications/music_player/application.fam +++ /dev/null @@ -1,22 +0,0 @@ -App( - appid="music_player", - name="Music Player", - apptype=FlipperAppType.PLUGIN, - entry_point="music_player_app", - cdefines=["APP_MUSIC_PLAYER"], - requires=[ - "gui", - "dialogs", - ], - provides=["music_player_start"], - stack_size=2 * 1024, - order=20, -) - -App( - appid="music_player_start", - apptype=FlipperAppType.STARTUP, - entry_point="music_player_on_system_start", - requires=["music_player"], - order=30, -) diff --git a/applications/music_player/music_player.c b/applications/music_player/music_player.c deleted file mode 100644 index 121efa0f980..00000000000 --- a/applications/music_player/music_player.c +++ /dev/null @@ -1,363 +0,0 @@ -#include "music_player_worker.h" - -#include -#include - -#include -#include -#include -#include - -#include - -#define TAG "MusicPlayer" - -#define MUSIC_PLAYER_APP_PATH_FOLDER ANY_PATH("music_player") -#define MUSIC_PLAYER_APP_EXTENSION "*" - -#define MUSIC_PLAYER_SEMITONE_HISTORY_SIZE 4 - -typedef struct { - uint8_t semitone_history[MUSIC_PLAYER_SEMITONE_HISTORY_SIZE]; - uint8_t duration_history[MUSIC_PLAYER_SEMITONE_HISTORY_SIZE]; - - uint8_t volume; - uint8_t semitone; - uint8_t dots; - uint8_t duration; - float position; -} MusicPlayerModel; - -typedef struct { - MusicPlayerModel* model; - FuriMutex** model_mutex; - - FuriMessageQueue* input_queue; - - ViewPort* view_port; - Gui* gui; - - MusicPlayerWorker* worker; -} MusicPlayer; - -static const float MUSIC_PLAYER_VOLUMES[] = {0, .25, .5, .75, 1}; - -static const char* semitone_to_note(int8_t semitone) { - switch(semitone) { - case 0: - return "C"; - case 1: - return "C#"; - case 2: - return "D"; - case 3: - return "D#"; - case 4: - return "E"; - case 5: - return "F"; - case 6: - return "F#"; - case 7: - return "G"; - case 8: - return "G#"; - case 9: - return "A"; - case 10: - return "A#"; - case 11: - return "B"; - default: - return "--"; - } -} - -static bool is_white_note(uint8_t semitone, uint8_t id) { - switch(semitone) { - case 0: - if(id == 0) return true; - break; - case 2: - if(id == 1) return true; - break; - case 4: - if(id == 2) return true; - break; - case 5: - if(id == 3) return true; - break; - case 7: - if(id == 4) return true; - break; - case 9: - if(id == 5) return true; - break; - case 11: - if(id == 6) return true; - break; - default: - break; - } - - return false; -} - -static bool is_black_note(uint8_t semitone, uint8_t id) { - switch(semitone) { - case 1: - if(id == 0) return true; - break; - case 3: - if(id == 1) return true; - break; - case 6: - if(id == 3) return true; - break; - case 8: - if(id == 4) return true; - break; - case 10: - if(id == 5) return true; - break; - default: - break; - } - - return false; -} - -static void render_callback(Canvas* canvas, void* ctx) { - MusicPlayer* music_player = ctx; - furi_check(furi_mutex_acquire(music_player->model_mutex, FuriWaitForever) == FuriStatusOk); - - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 0, 12, "MusicPlayer"); - - uint8_t x_pos = 0; - uint8_t y_pos = 24; - const uint8_t white_w = 10; - const uint8_t white_h = 40; - - const int8_t black_x = 6; - const int8_t black_y = -5; - const uint8_t black_w = 8; - const uint8_t black_h = 32; - - // white keys - for(size_t i = 0; i < 7; i++) { - if(is_white_note(music_player->model->semitone, i)) { - canvas_draw_box(canvas, x_pos + white_w * i, y_pos, white_w + 1, white_h); - } else { - canvas_draw_frame(canvas, x_pos + white_w * i, y_pos, white_w + 1, white_h); - } - } - - // black keys - for(size_t i = 0; i < 7; i++) { - if(i != 2 && i != 6) { - canvas_set_color(canvas, ColorWhite); - canvas_draw_box( - canvas, x_pos + white_w * i + black_x, y_pos + black_y, black_w + 1, black_h); - canvas_set_color(canvas, ColorBlack); - if(is_black_note(music_player->model->semitone, i)) { - canvas_draw_box( - canvas, x_pos + white_w * i + black_x, y_pos + black_y, black_w + 1, black_h); - } else { - canvas_draw_frame( - canvas, x_pos + white_w * i + black_x, y_pos + black_y, black_w + 1, black_h); - } - } - } - - // volume view_port - x_pos = 124; - y_pos = 0; - const uint8_t volume_h = - (64 / (COUNT_OF(MUSIC_PLAYER_VOLUMES) - 1)) * music_player->model->volume; - canvas_draw_frame(canvas, x_pos, y_pos, 4, 64); - canvas_draw_box(canvas, x_pos, y_pos + (64 - volume_h), 4, volume_h); - - // note stack view_port - x_pos = 73; - y_pos = 0; - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontPrimary); - canvas_draw_frame(canvas, x_pos, y_pos, 49, 64); - canvas_draw_line(canvas, x_pos + 28, 0, x_pos + 28, 64); - - char duration_text[16]; - for(uint8_t i = 0; i < MUSIC_PLAYER_SEMITONE_HISTORY_SIZE; i++) { - if(music_player->model->duration_history[i] == 0xFF) { - snprintf(duration_text, 15, "--"); - } else { - snprintf(duration_text, 15, "%d", music_player->model->duration_history[i]); - } - - if(i == 0) { - canvas_draw_box(canvas, x_pos, y_pos + 48, 49, 16); - canvas_set_color(canvas, ColorWhite); - } else { - canvas_set_color(canvas, ColorBlack); - } - canvas_draw_str( - canvas, - x_pos + 4, - 64 - 16 * i - 3, - semitone_to_note(music_player->model->semitone_history[i])); - canvas_draw_str(canvas, x_pos + 31, 64 - 16 * i - 3, duration_text); - canvas_draw_line(canvas, x_pos, 64 - 16 * i, x_pos + 48, 64 - 16 * i); - } - - furi_mutex_release(music_player->model_mutex); -} - -static void input_callback(InputEvent* input_event, void* ctx) { - MusicPlayer* music_player = ctx; - if(input_event->type == InputTypeShort) { - furi_message_queue_put(music_player->input_queue, input_event, 0); - } -} - -static void music_player_worker_callback( - uint8_t semitone, - uint8_t dots, - uint8_t duration, - float position, - void* context) { - MusicPlayer* music_player = context; - furi_check(furi_mutex_acquire(music_player->model_mutex, FuriWaitForever) == FuriStatusOk); - - for(size_t i = 0; i < MUSIC_PLAYER_SEMITONE_HISTORY_SIZE - 1; i++) { - size_t r = MUSIC_PLAYER_SEMITONE_HISTORY_SIZE - 1 - i; - music_player->model->duration_history[r] = music_player->model->duration_history[r - 1]; - music_player->model->semitone_history[r] = music_player->model->semitone_history[r - 1]; - } - - semitone = (semitone == 0xFF) ? 0xFF : semitone % 12; - - music_player->model->semitone = semitone; - music_player->model->dots = dots; - music_player->model->duration = duration; - music_player->model->position = position; - - music_player->model->semitone_history[0] = semitone; - music_player->model->duration_history[0] = duration; - - furi_mutex_release(music_player->model_mutex); - view_port_update(music_player->view_port); -} - -MusicPlayer* music_player_alloc() { - MusicPlayer* instance = malloc(sizeof(MusicPlayer)); - - instance->model = malloc(sizeof(MusicPlayerModel)); - memset(instance->model->duration_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE); - memset(instance->model->semitone_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE); - instance->model->volume = 3; - - instance->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - - instance->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); - - instance->worker = music_player_worker_alloc(); - music_player_worker_set_volume( - instance->worker, MUSIC_PLAYER_VOLUMES[instance->model->volume]); - music_player_worker_set_callback(instance->worker, music_player_worker_callback, instance); - - instance->view_port = view_port_alloc(); - view_port_draw_callback_set(instance->view_port, render_callback, instance); - view_port_input_callback_set(instance->view_port, input_callback, instance); - - // Open GUI and register view_port - instance->gui = furi_record_open(RECORD_GUI); - gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen); - - return instance; -} - -void music_player_free(MusicPlayer* instance) { - gui_remove_view_port(instance->gui, instance->view_port); - furi_record_close(RECORD_GUI); - view_port_free(instance->view_port); - - music_player_worker_free(instance->worker); - - furi_message_queue_free(instance->input_queue); - - furi_mutex_free(instance->model_mutex); - - free(instance->model); - free(instance); -} - -int32_t music_player_app(void* p) { - MusicPlayer* music_player = music_player_alloc(); - - string_t file_path; - string_init(file_path); - - do { - if(p && strlen(p)) { - string_cat_str(file_path, p); - } else { - string_set_str(file_path, MUSIC_PLAYER_APP_PATH_FOLDER); - - DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); - bool res = dialog_file_browser_show( - dialogs, - file_path, - file_path, - MUSIC_PLAYER_APP_EXTENSION, - true, - &I_music_10px, - false); - - furi_record_close(RECORD_DIALOGS); - if(!res) { - FURI_LOG_E(TAG, "No file selected"); - break; - } - } - - if(!music_player_worker_load(music_player->worker, string_get_cstr(file_path))) { - FURI_LOG_E(TAG, "Unable to load file"); - break; - } - - music_player_worker_start(music_player->worker); - - InputEvent input; - while(furi_message_queue_get(music_player->input_queue, &input, FuriWaitForever) == - FuriStatusOk) { - furi_check( - furi_mutex_acquire(music_player->model_mutex, FuriWaitForever) == FuriStatusOk); - - if(input.key == InputKeyBack) { - furi_mutex_release(music_player->model_mutex); - break; - } else if(input.key == InputKeyUp) { - if(music_player->model->volume < COUNT_OF(MUSIC_PLAYER_VOLUMES) - 1) - music_player->model->volume++; - music_player_worker_set_volume( - music_player->worker, MUSIC_PLAYER_VOLUMES[music_player->model->volume]); - } else if(input.key == InputKeyDown) { - if(music_player->model->volume > 0) music_player->model->volume--; - music_player_worker_set_volume( - music_player->worker, MUSIC_PLAYER_VOLUMES[music_player->model->volume]); - } - - furi_mutex_release(music_player->model_mutex); - view_port_update(music_player->view_port); - } - - music_player_worker_stop(music_player->worker); - } while(0); - - string_clear(file_path); - music_player_free(music_player); - - return 0; -} diff --git a/applications/music_player/music_player_cli.c b/applications/music_player/music_player_cli.c deleted file mode 100644 index 782004439af..00000000000 --- a/applications/music_player/music_player_cli.c +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include -#include -#include "music_player_worker.h" - -static void music_player_cli(Cli* cli, string_t args, void* context) { - UNUSED(context); - MusicPlayerWorker* music_player_worker = music_player_worker_alloc(); - Storage* storage = furi_record_open(RECORD_STORAGE); - - do { - if(storage_common_stat(storage, string_get_cstr(args), NULL) == FSE_OK) { - if(!music_player_worker_load(music_player_worker, string_get_cstr(args))) { - printf("Failed to open file %s\r\n", string_get_cstr(args)); - break; - } - } else { - if(!music_player_worker_load_rtttl_from_string( - music_player_worker, string_get_cstr(args))) { - printf("Argument is not a file or RTTTL\r\n"); - break; - } - } - - printf("Press CTRL+C to stop\r\n"); - music_player_worker_set_volume(music_player_worker, 1.0f); - music_player_worker_start(music_player_worker); - while(!cli_cmd_interrupt_received(cli)) { - furi_delay_ms(50); - } - music_player_worker_stop(music_player_worker); - } while(0); - - furi_record_close(RECORD_STORAGE); - music_player_worker_free(music_player_worker); -} - -void music_player_on_system_start() { -#ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - - cli_add_command(cli, "music_player", CliCommandFlagDefault, music_player_cli, NULL); - - furi_record_close(RECORD_CLI); -#else - UNUSED(music_player_cli); -#endif -} diff --git a/applications/music_player/music_player_worker.c b/applications/music_player/music_player_worker.c deleted file mode 100644 index ca4f1d8c9f2..00000000000 --- a/applications/music_player/music_player_worker.c +++ /dev/null @@ -1,499 +0,0 @@ -#include "music_player_worker.h" - -#include -#include - -#include -#include - -#include - -#define TAG "MusicPlayerWorker" - -#define MUSIC_PLAYER_FILETYPE "Flipper Music Format" -#define MUSIC_PLAYER_VERSION 0 - -#define SEMITONE_PAUSE 0xFF - -#define NOTE_C4 261.63f -#define NOTE_C4_SEMITONE (4.0f * 12.0f) -#define TWO_POW_TWELTH_ROOT 1.059463094359f - -typedef struct { - uint8_t semitone; - uint8_t duration; - uint8_t dots; -} NoteBlock; - -ARRAY_DEF(NoteBlockArray, NoteBlock, M_POD_OPLIST); - -struct MusicPlayerWorker { - FuriThread* thread; - bool should_work; - - MusicPlayerWorkerCallback callback; - void* callback_context; - - float volume; - uint32_t bpm; - uint32_t duration; - uint32_t octave; - NoteBlockArray_t notes; -}; - -static int32_t music_player_worker_thread_callback(void* context) { - furi_assert(context); - MusicPlayerWorker* instance = context; - - NoteBlockArray_it_t it; - NoteBlockArray_it(it, instance->notes); - - while(instance->should_work) { - if(NoteBlockArray_end_p(it)) { - NoteBlockArray_it(it, instance->notes); - furi_delay_ms(10); - } else { - NoteBlock* note_block = NoteBlockArray_ref(it); - - float note_from_a4 = (float)note_block->semitone - NOTE_C4_SEMITONE; - float frequency = NOTE_C4 * powf(TWO_POW_TWELTH_ROOT, note_from_a4); - float duration = - 60.0 * furi_kernel_get_tick_frequency() * 4 / instance->bpm / note_block->duration; - uint32_t dots = note_block->dots; - while(dots > 0) { - duration += duration / 2; - dots--; - } - uint32_t next_tick = furi_get_tick() + duration; - float volume = instance->volume; - - if(instance->callback) { - instance->callback( - note_block->semitone, - note_block->dots, - note_block->duration, - 0.0, - instance->callback_context); - } - - furi_hal_speaker_stop(); - furi_hal_speaker_start(frequency, volume); - while(instance->should_work && furi_get_tick() < next_tick) { - volume *= 0.9945679; - furi_hal_speaker_set_volume(volume); - furi_delay_ms(2); - } - NoteBlockArray_next(it); - } - } - - furi_hal_speaker_stop(); - - return 0; -} - -MusicPlayerWorker* music_player_worker_alloc() { - MusicPlayerWorker* instance = malloc(sizeof(MusicPlayerWorker)); - - NoteBlockArray_init(instance->notes); - - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "MusicPlayerWorker"); - furi_thread_set_stack_size(instance->thread, 1024); - furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, music_player_worker_thread_callback); - - instance->volume = 1.0f; - - return instance; -} - -void music_player_worker_free(MusicPlayerWorker* instance) { - furi_assert(instance); - furi_thread_free(instance->thread); - NoteBlockArray_clear(instance->notes); - free(instance); -} - -static bool is_digit(const char c) { - return isdigit(c) != 0; -} - -static bool is_letter(const char c) { - return islower(c) != 0 || isupper(c) != 0; -} - -static bool is_space(const char c) { - return c == ' ' || c == '\t'; -} - -static size_t extract_number(const char* string, uint32_t* number) { - size_t ret = 0; - while(is_digit(*string)) { - *number *= 10; - *number += (*string - '0'); - string++; - ret++; - } - return ret; -} - -static size_t extract_dots(const char* string, uint32_t* number) { - size_t ret = 0; - while(*string == '.') { - *number += 1; - string++; - ret++; - } - return ret; -} - -static size_t extract_char(const char* string, char* symbol) { - if(is_letter(*string)) { - *symbol = *string; - return 1; - } else { - return 0; - } -} - -static size_t extract_sharp(const char* string, char* symbol) { - if(*string == '#' || *string == '_') { - *symbol = '#'; - return 1; - } else { - return 0; - } -} - -static size_t skip_till(const char* string, const char symbol) { - size_t ret = 0; - while(*string != '\0' && *string != symbol) { - string++; - ret++; - } - if(*string != symbol) { - ret = 0; - } - return ret; -} - -static bool music_player_worker_add_note( - MusicPlayerWorker* instance, - uint8_t semitone, - uint8_t duration, - uint8_t dots) { - NoteBlock note_block; - - note_block.semitone = semitone; - note_block.duration = duration; - note_block.dots = dots; - - NoteBlockArray_push_back(instance->notes, note_block); - - return true; -} - -static int8_t note_to_semitone(const char note) { - switch(note) { - case 'C': - return 0; - // C# - case 'D': - return 2; - // D# - case 'E': - return 4; - case 'F': - return 5; - // F# - case 'G': - return 7; - // G# - case 'A': - return 9; - // A# - case 'B': - return 11; - default: - return 0; - } -} - -static bool music_player_worker_parse_notes(MusicPlayerWorker* instance, const char* string) { - const char* cursor = string; - bool result = true; - - while(*cursor != '\0') { - if(!is_space(*cursor)) { - uint32_t duration = 0; - char note_char = '\0'; - char sharp_char = '\0'; - uint32_t octave = 0; - uint32_t dots = 0; - - // Parsing - cursor += extract_number(cursor, &duration); - cursor += extract_char(cursor, ¬e_char); - cursor += extract_sharp(cursor, &sharp_char); - cursor += extract_number(cursor, &octave); - cursor += extract_dots(cursor, &dots); - - // Post processing - note_char = toupper(note_char); - if(!duration) { - duration = instance->duration; - } - if(!octave) { - octave = instance->octave; - } - - // Validation - bool is_valid = true; - is_valid &= (duration >= 1 && duration <= 128); - is_valid &= ((note_char >= 'A' && note_char <= 'G') || note_char == 'P'); - is_valid &= (sharp_char == '#' || sharp_char == '\0'); - is_valid &= (octave <= 16); - is_valid &= (dots <= 16); - if(!is_valid) { - FURI_LOG_E( - TAG, - "Invalid note: %u%c%c%u.%u", - duration, - note_char == '\0' ? '_' : note_char, - sharp_char == '\0' ? '_' : sharp_char, - octave, - dots); - result = false; - break; - } - - // Note to semitones - uint8_t semitone = 0; - if(note_char == 'P') { - semitone = SEMITONE_PAUSE; - } else { - semitone += octave * 12; - semitone += note_to_semitone(note_char); - semitone += sharp_char == '#' ? 1 : 0; - } - - if(music_player_worker_add_note(instance, semitone, duration, dots)) { - FURI_LOG_D( - TAG, - "Added note: %c%c%u.%u = %u %u", - note_char == '\0' ? '_' : note_char, - sharp_char == '\0' ? '_' : sharp_char, - octave, - dots, - semitone, - duration); - } else { - FURI_LOG_E( - TAG, - "Invalid note: %c%c%u.%u = %u %u", - note_char == '\0' ? '_' : note_char, - sharp_char == '\0' ? '_' : sharp_char, - octave, - dots, - semitone, - duration); - } - cursor += skip_till(cursor, ','); - } - - if(*cursor != '\0') cursor++; - } - - return result; -} - -bool music_player_worker_load(MusicPlayerWorker* instance, const char* file_path) { - furi_assert(instance); - furi_assert(file_path); - - bool ret = false; - if(strcasestr(file_path, ".fmf")) { - ret = music_player_worker_load_fmf_from_file(instance, file_path); - } else { - ret = music_player_worker_load_rtttl_from_file(instance, file_path); - } - return ret; -} - -bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const char* file_path) { - furi_assert(instance); - furi_assert(file_path); - - bool result = false; - string_t temp_str; - string_init(temp_str); - - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* file = flipper_format_file_alloc(storage); - - do { - if(!flipper_format_file_open_existing(file, file_path)) break; - - uint32_t version = 0; - if(!flipper_format_read_header(file, temp_str, &version)) break; - if(string_cmp_str(temp_str, MUSIC_PLAYER_FILETYPE) || (version != MUSIC_PLAYER_VERSION)) { - FURI_LOG_E(TAG, "Incorrect file format or version"); - break; - } - - if(!flipper_format_read_uint32(file, "BPM", &instance->bpm, 1)) { - FURI_LOG_E(TAG, "BPM is missing"); - break; - } - if(!flipper_format_read_uint32(file, "Duration", &instance->duration, 1)) { - FURI_LOG_E(TAG, "Duration is missing"); - break; - } - if(!flipper_format_read_uint32(file, "Octave", &instance->octave, 1)) { - FURI_LOG_E(TAG, "Octave is missing"); - break; - } - - if(!flipper_format_read_string(file, "Notes", temp_str)) { - FURI_LOG_E(TAG, "Notes is missing"); - break; - } - - if(!music_player_worker_parse_notes(instance, string_get_cstr(temp_str))) { - break; - } - - result = true; - } while(false); - - furi_record_close(RECORD_STORAGE); - flipper_format_free(file); - string_clear(temp_str); - - return result; -} - -bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const char* file_path) { - furi_assert(instance); - furi_assert(file_path); - - bool result = false; - string_t content; - string_init(content); - Storage* storage = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(storage); - - do { - if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { - FURI_LOG_E(TAG, "Unable to open file"); - break; - }; - - uint16_t ret = 0; - do { - uint8_t buffer[65] = {0}; - ret = storage_file_read(file, buffer, sizeof(buffer) - 1); - for(size_t i = 0; i < ret; i++) { - string_push_back(content, buffer[i]); - } - } while(ret > 0); - - string_strim(content); - if(!string_size(content)) { - FURI_LOG_E(TAG, "Empty file"); - break; - } - - if(!music_player_worker_load_rtttl_from_string(instance, string_get_cstr(content))) { - FURI_LOG_E(TAG, "Invalid file content"); - break; - } - - result = true; - } while(0); - - storage_file_free(file); - furi_record_close(RECORD_STORAGE); - string_clear(content); - - return result; -} - -bool music_player_worker_load_rtttl_from_string(MusicPlayerWorker* instance, const char* string) { - furi_assert(instance); - - const char* cursor = string; - - // Skip name - cursor += skip_till(cursor, ':'); - if(*cursor != ':') { - return false; - } - - // Duration - cursor += skip_till(cursor, '='); - if(*cursor != '=') { - return false; - } - cursor++; - cursor += extract_number(cursor, &instance->duration); - - // Octave - cursor += skip_till(cursor, '='); - if(*cursor != '=') { - return false; - } - cursor++; - cursor += extract_number(cursor, &instance->octave); - - // BPM - cursor += skip_till(cursor, '='); - if(*cursor != '=') { - return false; - } - cursor++; - cursor += extract_number(cursor, &instance->bpm); - - // Notes - cursor += skip_till(cursor, ':'); - if(*cursor != ':') { - return false; - } - cursor++; - if(!music_player_worker_parse_notes(instance, cursor)) { - return false; - } - - return true; -} - -void music_player_worker_set_callback( - MusicPlayerWorker* instance, - MusicPlayerWorkerCallback callback, - void* context) { - furi_assert(instance); - instance->callback = callback; - instance->callback_context = context; -} - -void music_player_worker_set_volume(MusicPlayerWorker* instance, float volume) { - furi_assert(instance); - instance->volume = volume; -} - -void music_player_worker_start(MusicPlayerWorker* instance) { - furi_assert(instance); - furi_assert(instance->should_work == false); - - instance->should_work = true; - furi_thread_start(instance->thread); -} - -void music_player_worker_stop(MusicPlayerWorker* instance) { - furi_assert(instance); - furi_assert(instance->should_work == true); - - instance->should_work = false; - furi_thread_join(instance->thread); -} diff --git a/applications/music_player/music_player_worker.h b/applications/music_player/music_player_worker.h deleted file mode 100644 index 3aa99ea370e..00000000000 --- a/applications/music_player/music_player_worker.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include - -typedef void (*MusicPlayerWorkerCallback)( - uint8_t semitone, - uint8_t dots, - uint8_t duration, - float position, - void* context); - -typedef struct MusicPlayerWorker MusicPlayerWorker; - -MusicPlayerWorker* music_player_worker_alloc(); - -void music_player_worker_free(MusicPlayerWorker* instance); - -bool music_player_worker_load(MusicPlayerWorker* instance, const char* file_path); - -bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const char* file_path); - -bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const char* file_path); - -bool music_player_worker_load_rtttl_from_string(MusicPlayerWorker* instance, const char* string); - -void music_player_worker_set_callback( - MusicPlayerWorker* instance, - MusicPlayerWorkerCallback callback, - void* context); - -void music_player_worker_set_volume(MusicPlayerWorker* instance, float volume); - -void music_player_worker_start(MusicPlayerWorker* instance); - -void music_player_worker_stop(MusicPlayerWorker* instance); diff --git a/applications/nfc/application.fam b/applications/nfc/application.fam deleted file mode 100644 index ce21bfd2738..00000000000 --- a/applications/nfc/application.fam +++ /dev/null @@ -1,23 +0,0 @@ -App( - appid="nfc", - name="NFC", - apptype=FlipperAppType.APP, - entry_point="nfc_app", - cdefines=["APP_NFC"], - requires=[ - "gui", - "dialogs", - ], - provides=["nfc_start"], - icon="A_NFC_14", - stack_size=5 * 1024, - order=30, -) - -App( - appid="nfc_start", - apptype=FlipperAppType.STARTUP, - entry_point="nfc_on_system_start", - requires=["nfc"], - order=30, -) diff --git a/applications/nfc/helpers/nfc_custom_event.h b/applications/nfc/helpers/nfc_custom_event.h deleted file mode 100644 index 4227a5b14e3..00000000000 --- a/applications/nfc/helpers/nfc_custom_event.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -enum NfcCustomEvent { - // Reserve first 100 events for button types and indexes, starting from 0 - NfcCustomEventReserved = 100, - - NfcCustomEventViewExit, - NfcCustomEventWorkerExit, - NfcCustomEventByteInputDone, - NfcCustomEventTextInputDone, - NfcCustomEventDictAttackDone, - NfcCustomEventDictAttackSkip, - NfcCustomEventRpcLoad, - NfcCustomEventRpcSessionClose, -}; diff --git a/applications/nfc/helpers/nfc_emv_parser.c b/applications/nfc/helpers/nfc_emv_parser.c deleted file mode 100755 index 0d7cb5a3371..00000000000 --- a/applications/nfc/helpers/nfc_emv_parser.c +++ /dev/null @@ -1,82 +0,0 @@ -#include "nfc_emv_parser.h" -#include - -static const char* nfc_resources_header = "Flipper EMV resources"; -static const uint32_t nfc_resources_file_version = 1; - -static bool nfc_emv_parser_search_data( - Storage* storage, - const char* file_name, - string_t key, - string_t data) { - bool parsed = false; - FlipperFormat* file = flipper_format_file_alloc(storage); - string_t temp_str; - string_init(temp_str); - - do { - // Open file - if(!flipper_format_file_open_existing(file, file_name)) break; - // Read file header and version - uint32_t version = 0; - if(!flipper_format_read_header(file, temp_str, &version)) break; - if(string_cmp_str(temp_str, nfc_resources_header) || - (version != nfc_resources_file_version)) - break; - if(!flipper_format_read_string(file, string_get_cstr(key), data)) break; - parsed = true; - } while(false); - - string_clear(temp_str); - flipper_format_free(file); - return parsed; -} - -bool nfc_emv_parser_get_aid_name( - Storage* storage, - uint8_t* aid, - uint8_t aid_len, - string_t aid_name) { - furi_assert(storage); - bool parsed = false; - string_t key; - string_init(key); - for(uint8_t i = 0; i < aid_len; i++) { - string_cat_printf(key, "%02X", aid[i]); - } - if(nfc_emv_parser_search_data(storage, EXT_PATH("nfc/assets/aid.nfc"), key, aid_name)) { - parsed = true; - } - string_clear(key); - return parsed; -} - -bool nfc_emv_parser_get_country_name( - Storage* storage, - uint16_t country_code, - string_t country_name) { - bool parsed = false; - string_t key; - string_init_printf(key, "%04X", country_code); - if(nfc_emv_parser_search_data( - storage, EXT_PATH("nfc/assets/country_code.nfc"), key, country_name)) { - parsed = true; - } - string_clear(key); - return parsed; -} - -bool nfc_emv_parser_get_currency_name( - Storage* storage, - uint16_t currency_code, - string_t currency_name) { - bool parsed = false; - string_t key; - string_init_printf(key, "%04X", currency_code); - if(nfc_emv_parser_search_data( - storage, EXT_PATH("nfc/assets/currency_code.nfc"), key, currency_name)) { - parsed = true; - } - string_clear(key); - return parsed; -} diff --git a/applications/nfc/helpers/nfc_generators.c b/applications/nfc/helpers/nfc_generators.c deleted file mode 100644 index b94adbd7b03..00000000000 --- a/applications/nfc/helpers/nfc_generators.c +++ /dev/null @@ -1,492 +0,0 @@ -#include -#include "nfc_generators.h" - -#define NXP_MANUFACTURER_ID (0x04) - -static const uint8_t version_bytes_mf0ulx1[] = {0x00, 0x04, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03}; -static const uint8_t version_bytes_ntag21x[] = {0x00, 0x04, 0x04, 0x02, 0x01, 0x00, 0x00, 0x03}; -static const uint8_t version_bytes_ntag_i2c[] = {0x00, 0x04, 0x04, 0x05, 0x02, 0x00, 0x00, 0x03}; -static const uint8_t default_data_ntag203[] = - {0xE1, 0x10, 0x12, 0x00, 0x01, 0x03, 0xA0, 0x10, 0x44, 0x03, 0x00, 0xFE}; -static const uint8_t default_data_ntag213[] = {0x01, 0x03, 0xA0, 0x0C, 0x34, 0x03, 0x00, 0xFE}; -static const uint8_t default_data_ntag215_216[] = {0x03, 0x00, 0xFE}; -static const uint8_t default_data_ntag_i2c[] = {0xE1, 0x10, 0x00, 0x00, 0x03, 0x00, 0xFE}; -static const uint8_t default_config_ntag_i2c[] = {0x01, 0x00, 0xF8, 0x48, 0x08, 0x01, 0x00, 0x00}; - -static void nfc_generate_common_start(NfcDeviceData* data) { - nfc_device_data_clear(data); -} - -static void nfc_generate_mf_ul_uid(uint8_t* uid) { - uid[0] = NXP_MANUFACTURER_ID; - furi_hal_random_fill_buf(&uid[1], 6); - // I'm not sure how this is generated, but the upper nybble always seems to be 8 - uid[6] &= 0x0F; - uid[6] |= 0x80; -} - -static void nfc_generate_mf_classic_uid(uint8_t* uid, uint8_t length) { - uid[0] = NXP_MANUFACTURER_ID; - furi_hal_random_fill_buf(&uid[1], length - 1); -} - -static void nfc_generate_mf_classic_block_0(uint8_t* block, uint8_t uid_len) { - // Block length is always 16 bytes, and the UID can be either 4 or 7 bytes - furi_assert(uid_len == 4 || uid_len == 7); - furi_assert(block); - nfc_generate_mf_classic_uid(block, uid_len); - for(int i = uid_len; i < 16; i++) { - block[i] = 0xFF; - } -} - -static void nfc_generate_mf_classic_sector_trailer(MfClassicData* data, uint8_t block) { - // All keys are set to FFFF FFFF FFFFh at chip delivery and the bytes 6, 7 and 8 are set to FF0780h. - MfClassicSectorTrailer* sec_tr = (MfClassicSectorTrailer*)data->block[block].value; - sec_tr->access_bits[0] = 0xFF; - sec_tr->access_bits[1] = 0x07; - sec_tr->access_bits[2] = 0x80; - sec_tr->access_bits[3] = 0x69; // Nice - - memset(sec_tr->key_a, 0xff, sizeof(sec_tr->key_a)); - memset(sec_tr->key_b, 0xff, sizeof(sec_tr->key_b)); - - mf_classic_set_block_read(data, block, &data->block[block]); - mf_classic_set_key_found( - data, mf_classic_get_sector_by_block(block), MfClassicKeyA, 0xFFFFFFFFFFFF); - mf_classic_set_key_found( - data, mf_classic_get_sector_by_block(block), MfClassicKeyB, 0xFFFFFFFFFFFF); -} - -static void nfc_generate_mf_ul_common(NfcDeviceData* data) { - data->nfc_data.type = FuriHalNfcTypeA; - data->nfc_data.interface = FuriHalNfcInterfaceRf; - data->nfc_data.uid_len = 7; - nfc_generate_mf_ul_uid(data->nfc_data.uid); - data->nfc_data.atqa[0] = 0x44; - data->nfc_data.atqa[1] = 0x00; - data->nfc_data.sak = 0x00; - data->protocol = NfcDeviceProtocolMifareUl; -} - -static void - nfc_generate_mf_classic_common(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) { - data->nfc_data.type = FuriHalNfcTypeA; - data->nfc_data.interface = FuriHalNfcInterfaceRf; - data->nfc_data.uid_len = uid_len; - nfc_generate_mf_classic_block_0(data->mf_classic_data.block[0].value, uid_len); - data->nfc_data.atqa[0] = 0x44; - data->nfc_data.atqa[1] = 0x00; - data->nfc_data.sak = 0x08; - data->protocol = NfcDeviceProtocolMifareClassic; - data->mf_classic_data.type = type; -} - -static void nfc_generate_calc_bcc(uint8_t* uid, uint8_t* bcc0, uint8_t* bcc1) { - *bcc0 = 0x88 ^ uid[0] ^ uid[1] ^ uid[2]; - *bcc1 = uid[3] ^ uid[4] ^ uid[5] ^ uid[6]; -} - -static void nfc_generate_mf_ul_copy_uid_with_bcc(NfcDeviceData* data) { - MfUltralightData* mful = &data->mf_ul_data; - memcpy(mful->data, data->nfc_data.uid, 3); - memcpy(&mful->data[4], &data->nfc_data.uid[3], 4); - nfc_generate_calc_bcc(data->nfc_data.uid, &mful->data[3], &mful->data[8]); -} - -static void nfc_generate_mf_ul_orig(NfcDeviceData* data) { - nfc_generate_common_start(data); - nfc_generate_mf_ul_common(data); - - MfUltralightData* mful = &data->mf_ul_data; - mful->type = MfUltralightTypeUnknown; - mful->data_size = 16 * 4; - mful->data_read = mful->data_size; - nfc_generate_mf_ul_copy_uid_with_bcc(data); - // TODO: what's internal byte on page 2? - memset(&mful->data[4 * 4], 0xFF, 4); -} - -static void nfc_generate_mf_ul_ntag203(NfcDeviceData* data) { - nfc_generate_common_start(data); - nfc_generate_mf_ul_common(data); - - MfUltralightData* mful = &data->mf_ul_data; - mful->type = MfUltralightTypeNTAG203; - mful->data_size = 42 * 4; - mful->data_read = mful->data_size; - nfc_generate_mf_ul_copy_uid_with_bcc(data); - mful->data[9] = 0x48; // Internal byte - memcpy(&mful->data[3 * 4], default_data_ntag203, sizeof(default_data_ntag203)); -} - -static void nfc_generate_mf_ul_with_config_common(NfcDeviceData* data, uint8_t num_pages) { - nfc_generate_common_start(data); - nfc_generate_mf_ul_common(data); - - MfUltralightData* mful = &data->mf_ul_data; - mful->data_size = num_pages * 4; - mful->data_read = mful->data_size; - nfc_generate_mf_ul_copy_uid_with_bcc(data); - uint16_t config_index = (num_pages - 4) * 4; - mful->data[config_index] = 0x04; // STRG_MOD_EN - mful->data[config_index + 3] = 0xFF; // AUTH0 - mful->data[config_index + 5] = 0x05; // VCTID - memset(&mful->data[config_index + 8], 0xFF, 4); // Default PWD - if(num_pages > 20) mful->data[config_index - 1] = MF_UL_TEARING_FLAG_DEFAULT; -} - -static void nfc_generate_mf_ul_ev1_common(NfcDeviceData* data, uint8_t num_pages) { - nfc_generate_mf_ul_with_config_common(data, num_pages); - MfUltralightData* mful = &data->mf_ul_data; - memcpy(&mful->version, version_bytes_mf0ulx1, sizeof(version_bytes_mf0ulx1)); - for(size_t i = 0; i < 3; ++i) { - mful->tearing[i] = MF_UL_TEARING_FLAG_DEFAULT; - } - // TODO: what's internal byte on page 2? -} - -static void nfc_generate_mf_ul_11(NfcDeviceData* data) { - nfc_generate_mf_ul_ev1_common(data, 20); - MfUltralightData* mful = &data->mf_ul_data; - mful->type = MfUltralightTypeUL11; - mful->version.prod_subtype = 0x01; - mful->version.storage_size = 0x0B; - mful->data[16 * 4] = 0x00; // Low capacitance version does not have STRG_MOD_EN -} - -static void nfc_generate_mf_ul_h11(NfcDeviceData* data) { - nfc_generate_mf_ul_ev1_common(data, 20); - MfUltralightData* mful = &data->mf_ul_data; - mful->type = MfUltralightTypeUL11; - mful->version.prod_subtype = 0x02; - mful->version.storage_size = 0x0B; -} - -static void nfc_generate_mf_ul_21(NfcDeviceData* data) { - nfc_generate_mf_ul_ev1_common(data, 41); - MfUltralightData* mful = &data->mf_ul_data; - mful->type = MfUltralightTypeUL21; - mful->version.prod_subtype = 0x01; - mful->version.storage_size = 0x0E; - mful->data[37 * 4] = 0x00; // Low capacitance version does not have STRG_MOD_EN -} - -static void nfc_generate_mf_ul_h21(NfcDeviceData* data) { - nfc_generate_mf_ul_ev1_common(data, 41); - MfUltralightData* mful = &data->mf_ul_data; - mful->type = MfUltralightTypeUL21; - mful->version.prod_subtype = 0x02; - mful->version.storage_size = 0x0E; -} - -static void nfc_generate_ntag21x_common(NfcDeviceData* data, uint8_t num_pages) { - nfc_generate_mf_ul_with_config_common(data, num_pages); - MfUltralightData* mful = &data->mf_ul_data; - memcpy(&mful->version, version_bytes_ntag21x, sizeof(version_bytes_mf0ulx1)); - mful->data[9] = 0x48; // Internal byte - // Capability container - mful->data[12] = 0xE1; - mful->data[13] = 0x10; -} - -static void nfc_generate_ntag213(NfcDeviceData* data) { - nfc_generate_ntag21x_common(data, 45); - MfUltralightData* mful = &data->mf_ul_data; - mful->type = MfUltralightTypeNTAG213; - mful->version.storage_size = 0x0F; - mful->data[14] = 0x12; - // Default contents - memcpy(&mful->data[16], default_data_ntag213, sizeof(default_data_ntag213)); -} - -static void nfc_generate_ntag215(NfcDeviceData* data) { - nfc_generate_ntag21x_common(data, 135); - MfUltralightData* mful = &data->mf_ul_data; - mful->type = MfUltralightTypeNTAG215; - mful->version.storage_size = 0x11; - mful->data[14] = 0x3E; - // Default contents - memcpy(&mful->data[16], default_data_ntag215_216, sizeof(default_data_ntag215_216)); -} - -static void nfc_generate_ntag216(NfcDeviceData* data) { - nfc_generate_ntag21x_common(data, 231); - MfUltralightData* mful = &data->mf_ul_data; - mful->type = MfUltralightTypeNTAG216; - mful->version.storage_size = 0x13; - mful->data[14] = 0x6D; - // Default contents - memcpy(&mful->data[16], default_data_ntag215_216, sizeof(default_data_ntag215_216)); -} - -static void - nfc_generate_ntag_i2c_common(NfcDeviceData* data, MfUltralightType type, uint16_t num_pages) { - nfc_generate_common_start(data); - nfc_generate_mf_ul_common(data); - - MfUltralightData* mful = &data->mf_ul_data; - mful->type = type; - memcpy(&mful->version, version_bytes_ntag_i2c, sizeof(version_bytes_ntag_i2c)); - mful->data_size = num_pages * 4; - mful->data_read = mful->data_size; - memcpy(mful->data, data->nfc_data.uid, data->nfc_data.uid_len); - mful->data[7] = data->nfc_data.sak; - mful->data[8] = data->nfc_data.atqa[0]; - mful->data[9] = data->nfc_data.atqa[1]; - - uint16_t config_register_page; - uint16_t session_register_page; - - // Sync with mifare_ultralight.c - switch(type) { - case MfUltralightTypeNTAGI2C1K: - config_register_page = 227; - session_register_page = 229; - break; - case MfUltralightTypeNTAGI2C2K: - config_register_page = 481; - session_register_page = 483; - break; - case MfUltralightTypeNTAGI2CPlus1K: - case MfUltralightTypeNTAGI2CPlus2K: - config_register_page = 232; - session_register_page = 234; - break; - default: - furi_assert(false); - break; - } - - memcpy( - &mful->data[config_register_page * 4], - default_config_ntag_i2c, - sizeof(default_config_ntag_i2c)); - memcpy( - &mful->data[session_register_page * 4], - default_config_ntag_i2c, - sizeof(default_config_ntag_i2c)); -} - -static void nfc_generate_ntag_i2c_1k(NfcDeviceData* data) { - nfc_generate_ntag_i2c_common(data, MfUltralightTypeNTAGI2C1K, 231); - MfUltralightData* mful = &data->mf_ul_data; - mful->version.prod_ver_minor = 0x01; - mful->version.storage_size = 0x13; - - memcpy(&mful->data[12], default_data_ntag_i2c, sizeof(default_data_ntag_i2c)); - mful->data[14] = 0x6D; // Size of tag in CC -} - -static void nfc_generate_ntag_i2c_2k(NfcDeviceData* data) { - nfc_generate_ntag_i2c_common(data, MfUltralightTypeNTAGI2C2K, 485); - MfUltralightData* mful = &data->mf_ul_data; - mful->version.prod_ver_minor = 0x01; - mful->version.storage_size = 0x15; - - memcpy(&mful->data[12], default_data_ntag_i2c, sizeof(default_data_ntag_i2c)); - mful->data[14] = 0xEA; // Size of tag in CC -} - -static void nfc_generate_ntag_i2c_plus_common( - NfcDeviceData* data, - MfUltralightType type, - uint16_t num_pages) { - nfc_generate_ntag_i2c_common(data, type, num_pages); - - MfUltralightData* mful = &data->mf_ul_data; - uint16_t config_index = 227 * 4; - mful->data[config_index + 3] = 0xFF; // AUTH0 - memset(&mful->data[config_index + 8], 0xFF, 4); // Default PWD -} - -static void nfc_generate_ntag_i2c_plus_1k(NfcDeviceData* data) { - nfc_generate_ntag_i2c_plus_common(data, MfUltralightTypeNTAGI2CPlus1K, 236); - MfUltralightData* mful = &data->mf_ul_data; - mful->version.prod_ver_minor = 0x02; - mful->version.storage_size = 0x13; -} - -static void nfc_generate_ntag_i2c_plus_2k(NfcDeviceData* data) { - nfc_generate_ntag_i2c_plus_common(data, MfUltralightTypeNTAGI2CPlus2K, 492); - MfUltralightData* mful = &data->mf_ul_data; - mful->version.prod_ver_minor = 0x02; - mful->version.storage_size = 0x15; -} - -static void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) { - nfc_generate_common_start(data); - nfc_generate_mf_classic_common(data, uid_len, type); - - // Set the UID - data->nfc_data.uid[0] = NXP_MANUFACTURER_ID; - for(int i = 1; i < uid_len; i++) { - data->nfc_data.uid[i] = data->mf_classic_data.block[0].value[i]; - } - - MfClassicData* mfc = &data->mf_classic_data; - mf_classic_set_block_read(mfc, 0, &mfc->block[0]); - - if(type == MfClassicType4k) { - // Set every block to 0xFF - for(uint16_t i = 1; i < 256; i += 1) { - if(mf_classic_is_sector_trailer(i)) { - nfc_generate_mf_classic_sector_trailer(mfc, i); - } else { - memset(&mfc->block[i].value, 0xFF, 16); - } - mf_classic_set_block_read(mfc, i, &mfc->block[i]); - } - } else if(type == MfClassicType1k) { - // Set every block to 0xFF - for(uint16_t i = 1; i < MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4; i += 1) { - if(mf_classic_is_sector_trailer(i)) { - nfc_generate_mf_classic_sector_trailer(mfc, i); - } else { - memset(&mfc->block[i].value, 0xFF, 16); - } - mf_classic_set_block_read(mfc, i, &mfc->block[i]); - } - } - - mfc->type = type; -} - -static void nfc_generate_mf_classic_1k_4b_uid(NfcDeviceData* data) { - nfc_generate_mf_classic(data, 4, MfClassicType1k); -} - -static void nfc_generate_mf_classic_1k_7b_uid(NfcDeviceData* data) { - nfc_generate_mf_classic(data, 7, MfClassicType1k); -} - -static void nfc_generate_mf_classic_4k_4b_uid(NfcDeviceData* data) { - nfc_generate_mf_classic(data, 4, MfClassicType4k); -} - -static void nfc_generate_mf_classic_4k_7b_uid(NfcDeviceData* data) { - nfc_generate_mf_classic(data, 7, MfClassicType4k); -} - -static const NfcGenerator mf_ul_generator = { - .name = "Mifare Ultralight", - .generator_func = nfc_generate_mf_ul_orig, - .next_scene = NfcSceneMfUltralightMenu, -}; - -static const NfcGenerator mf_ul_11_generator = { - .name = "Mifare Ultralight EV1 11", - .generator_func = nfc_generate_mf_ul_11, - .next_scene = NfcSceneMfUltralightMenu, -}; - -static const NfcGenerator mf_ul_h11_generator = { - .name = "Mifare Ultralight EV1 H11", - .generator_func = nfc_generate_mf_ul_h11, - .next_scene = NfcSceneMfUltralightMenu, -}; - -static const NfcGenerator mf_ul_21_generator = { - .name = "Mifare Ultralight EV1 21", - .generator_func = nfc_generate_mf_ul_21, - .next_scene = NfcSceneMfUltralightMenu, -}; - -static const NfcGenerator mf_ul_h21_generator = { - .name = "Mifare Ultralight EV1 H21", - .generator_func = nfc_generate_mf_ul_h21, - .next_scene = NfcSceneMfUltralightMenu, -}; - -static const NfcGenerator ntag203_generator = { - .name = "NTAG203", - .generator_func = nfc_generate_mf_ul_ntag203, - .next_scene = NfcSceneMfUltralightMenu, -}; - -static const NfcGenerator ntag213_generator = { - .name = "NTAG213", - .generator_func = nfc_generate_ntag213, - .next_scene = NfcSceneMfUltralightMenu, -}; - -static const NfcGenerator ntag215_generator = { - .name = "NTAG215", - .generator_func = nfc_generate_ntag215, - .next_scene = NfcSceneMfUltralightMenu, -}; - -static const NfcGenerator ntag216_generator = { - .name = "NTAG216", - .generator_func = nfc_generate_ntag216, - .next_scene = NfcSceneMfUltralightMenu, -}; - -static const NfcGenerator ntag_i2c_1k_generator = { - .name = "NTAG I2C 1k", - .generator_func = nfc_generate_ntag_i2c_1k, - .next_scene = NfcSceneMfUltralightMenu, -}; - -static const NfcGenerator ntag_i2c_2k_generator = { - .name = "NTAG I2C 2k", - .generator_func = nfc_generate_ntag_i2c_2k, - .next_scene = NfcSceneMfUltralightMenu, -}; - -static const NfcGenerator ntag_i2c_plus_1k_generator = { - .name = "NTAG I2C Plus 1k", - .generator_func = nfc_generate_ntag_i2c_plus_1k, - .next_scene = NfcSceneMfUltralightMenu, -}; - -static const NfcGenerator ntag_i2c_plus_2k_generator = { - .name = "NTAG I2C Plus 2k", - .generator_func = nfc_generate_ntag_i2c_plus_2k, - .next_scene = NfcSceneMfUltralightMenu, -}; - -static const NfcGenerator mifare_classic_1k_4b_uid_generator = { - .name = "Mifare Classic 1k 4byte UID", - .generator_func = nfc_generate_mf_classic_1k_4b_uid, - .next_scene = NfcSceneMfClassicMenu, -}; - -static const NfcGenerator mifare_classic_1k_7b_uid_generator = { - .name = "Mifare Classic 1k 7byte UID", - .generator_func = nfc_generate_mf_classic_1k_7b_uid, - .next_scene = NfcSceneMfClassicMenu, -}; - -static const NfcGenerator mifare_classic_4k_4b_uid_generator = { - .name = "Mifare Classic 4k 4byte UID", - .generator_func = nfc_generate_mf_classic_4k_4b_uid, - .next_scene = NfcSceneMfClassicMenu, -}; - -static const NfcGenerator mifare_classic_4k_7b_uid_generator = { - .name = "Mifare Classic 4k 7byte UID", - .generator_func = nfc_generate_mf_classic_4k_7b_uid, - .next_scene = NfcSceneMfClassicMenu, -}; - -const NfcGenerator* const nfc_generators[] = { - &mf_ul_generator, - &mf_ul_11_generator, - &mf_ul_h11_generator, - &mf_ul_21_generator, - &mf_ul_h21_generator, - &ntag203_generator, - &ntag213_generator, - &ntag215_generator, - &ntag216_generator, - &ntag_i2c_1k_generator, - &ntag_i2c_2k_generator, - &ntag_i2c_plus_1k_generator, - &ntag_i2c_plus_2k_generator, - &mifare_classic_1k_4b_uid_generator, - &mifare_classic_1k_7b_uid_generator, - &mifare_classic_4k_4b_uid_generator, - &mifare_classic_4k_7b_uid_generator, - NULL, -}; diff --git a/applications/nfc/helpers/nfc_generators.h b/applications/nfc/helpers/nfc_generators.h deleted file mode 100644 index 10a05591b1b..00000000000 --- a/applications/nfc/helpers/nfc_generators.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "../nfc_i.h" - -typedef void (*NfcGeneratorFunc)(NfcDeviceData* data); - -struct NfcGenerator { - const char* name; - NfcGeneratorFunc generator_func; - NfcScene next_scene; -}; - -extern const NfcGenerator* const nfc_generators[]; diff --git a/applications/nfc/nfc.c b/applications/nfc/nfc.c deleted file mode 100644 index 3422e91af85..00000000000 --- a/applications/nfc/nfc.c +++ /dev/null @@ -1,260 +0,0 @@ -#include "nfc_i.h" -#include "furi_hal_nfc.h" - -bool nfc_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - Nfc* nfc = context; - return scene_manager_handle_custom_event(nfc->scene_manager, event); -} - -bool nfc_back_event_callback(void* context) { - furi_assert(context); - Nfc* nfc = context; - return scene_manager_handle_back_event(nfc->scene_manager); -} - -static void nfc_rpc_command_callback(RpcAppSystemEvent event, void* context) { - furi_assert(context); - Nfc* nfc = context; - - furi_assert(nfc->rpc_ctx); - - if(event == RpcAppEventSessionClose) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcSessionClose); - rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); - nfc->rpc_ctx = NULL; - } else if(event == RpcAppEventAppExit) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); - } else if(event == RpcAppEventLoadFile) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcLoad); - } else { - rpc_system_app_confirm(nfc->rpc_ctx, event, false); - } -} - -Nfc* nfc_alloc() { - Nfc* nfc = malloc(sizeof(Nfc)); - - nfc->worker = nfc_worker_alloc(); - nfc->view_dispatcher = view_dispatcher_alloc(); - nfc->scene_manager = scene_manager_alloc(&nfc_scene_handlers, nfc); - view_dispatcher_enable_queue(nfc->view_dispatcher); - view_dispatcher_set_event_callback_context(nfc->view_dispatcher, nfc); - view_dispatcher_set_custom_event_callback(nfc->view_dispatcher, nfc_custom_event_callback); - view_dispatcher_set_navigation_event_callback(nfc->view_dispatcher, nfc_back_event_callback); - - // Nfc device - nfc->dev = nfc_device_alloc(); - - // Open GUI record - nfc->gui = furi_record_open(RECORD_GUI); - - // Open Notification record - nfc->notifications = furi_record_open(RECORD_NOTIFICATION); - - // Submenu - nfc->submenu = submenu_alloc(); - view_dispatcher_add_view(nfc->view_dispatcher, NfcViewMenu, submenu_get_view(nfc->submenu)); - - // Dialog - nfc->dialog_ex = dialog_ex_alloc(); - view_dispatcher_add_view( - nfc->view_dispatcher, NfcViewDialogEx, dialog_ex_get_view(nfc->dialog_ex)); - - // Popup - nfc->popup = popup_alloc(); - view_dispatcher_add_view(nfc->view_dispatcher, NfcViewPopup, popup_get_view(nfc->popup)); - - // Loading - nfc->loading = loading_alloc(); - view_dispatcher_add_view(nfc->view_dispatcher, NfcViewLoading, loading_get_view(nfc->loading)); - - // Text Input - nfc->text_input = text_input_alloc(); - view_dispatcher_add_view( - nfc->view_dispatcher, NfcViewTextInput, text_input_get_view(nfc->text_input)); - - // Byte Input - nfc->byte_input = byte_input_alloc(); - view_dispatcher_add_view( - nfc->view_dispatcher, NfcViewByteInput, byte_input_get_view(nfc->byte_input)); - - // TextBox - nfc->text_box = text_box_alloc(); - view_dispatcher_add_view( - nfc->view_dispatcher, NfcViewTextBox, text_box_get_view(nfc->text_box)); - string_init(nfc->text_box_store); - - // Custom Widget - nfc->widget = widget_alloc(); - view_dispatcher_add_view(nfc->view_dispatcher, NfcViewWidget, widget_get_view(nfc->widget)); - - // Mifare Classic Dict Attack - nfc->dict_attack = dict_attack_alloc(); - view_dispatcher_add_view( - nfc->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(nfc->dict_attack)); - - // Generator - nfc->generator = NULL; - - return nfc; -} - -void nfc_free(Nfc* nfc) { - furi_assert(nfc); - - if(nfc->rpc_state == NfcRpcStateEmulating) { - // Stop worker - nfc_worker_stop(nfc->worker); - } else if(nfc->rpc_state == NfcRpcStateEmulated) { - // Stop worker - nfc_worker_stop(nfc->worker); - // Save data in shadow file - nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); - } - if(nfc->rpc_ctx) { - rpc_system_app_send_exited(nfc->rpc_ctx); - rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); - nfc->rpc_ctx = NULL; - } - - // Nfc device - nfc_device_free(nfc->dev); - - // Submenu - view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewMenu); - submenu_free(nfc->submenu); - - // DialogEx - view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDialogEx); - dialog_ex_free(nfc->dialog_ex); - - // Popup - view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewPopup); - popup_free(nfc->popup); - - // Loading - view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewLoading); - loading_free(nfc->loading); - - // TextInput - view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewTextInput); - text_input_free(nfc->text_input); - - // ByteInput - view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewByteInput); - byte_input_free(nfc->byte_input); - - // TextBox - view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewTextBox); - text_box_free(nfc->text_box); - string_clear(nfc->text_box_store); - - // Custom Widget - view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewWidget); - widget_free(nfc->widget); - - // Mifare Classic Dict Attack - view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDictAttack); - dict_attack_free(nfc->dict_attack); - - // Worker - nfc_worker_stop(nfc->worker); - nfc_worker_free(nfc->worker); - - // View Dispatcher - view_dispatcher_free(nfc->view_dispatcher); - - // Scene Manager - scene_manager_free(nfc->scene_manager); - - // GUI - furi_record_close(RECORD_GUI); - nfc->gui = NULL; - - // Notifications - furi_record_close(RECORD_NOTIFICATION); - nfc->notifications = NULL; - - free(nfc); -} - -void nfc_text_store_set(Nfc* nfc, const char* text, ...) { - va_list args; - va_start(args, text); - - vsnprintf(nfc->text_store, sizeof(nfc->text_store), text, args); - - va_end(args); -} - -void nfc_text_store_clear(Nfc* nfc) { - memset(nfc->text_store, 0, sizeof(nfc->text_store)); -} - -void nfc_blink_start(Nfc* nfc) { - notification_message(nfc->notifications, &sequence_blink_start_blue); -} - -void nfc_blink_stop(Nfc* nfc) { - notification_message(nfc->notifications, &sequence_blink_stop); -} - -void nfc_show_loading_popup(void* context, bool show) { - Nfc* nfc = context; - TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); - - if(show) { - // Raise timer priority so that animations can play - vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewLoading); - } else { - // Restore default timer priority - vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); - } -} - -int32_t nfc_app(void* p) { - Nfc* nfc = nfc_alloc(); - char* args = p; - - // Check argument and run corresponding scene - if(args && strlen(args)) { - nfc_device_set_loading_callback(nfc->dev, nfc_show_loading_popup, nfc); - uint32_t rpc_ctx = 0; - if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { - nfc->rpc_ctx = (void*)rpc_ctx; - rpc_system_app_set_callback(nfc->rpc_ctx, nfc_rpc_command_callback, nfc); - rpc_system_app_send_started(nfc->rpc_ctx); - view_dispatcher_attach_to_gui( - nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeDesktop); - scene_manager_next_scene(nfc->scene_manager, NfcSceneRpc); - } else { - view_dispatcher_attach_to_gui( - nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen); - if(nfc_device_load(nfc->dev, p, true)) { - if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); - } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); - } else { - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); - } - } else { - // Exit app - view_dispatcher_stop(nfc->view_dispatcher); - } - } - nfc_device_set_loading_callback(nfc->dev, NULL, nfc); - } else { - view_dispatcher_attach_to_gui( - nfc->view_dispatcher, nfc->gui, ViewDispatcherTypeFullscreen); - scene_manager_next_scene(nfc->scene_manager, NfcSceneStart); - } - - view_dispatcher_run(nfc->view_dispatcher); - - nfc_free(nfc); - - return 0; -} diff --git a/applications/nfc/nfc.h b/applications/nfc/nfc.h deleted file mode 100644 index e08be6a3aa9..00000000000 --- a/applications/nfc/nfc.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -typedef struct Nfc Nfc; diff --git a/applications/nfc/nfc_cli.c b/applications/nfc/nfc_cli.c deleted file mode 100755 index 4ac95f0437d..00000000000 --- a/applications/nfc/nfc_cli.c +++ /dev/null @@ -1,141 +0,0 @@ -#include -#include -#include -#include - -#include -#include - -static void nfc_cli_print_usage() { - printf("Usage:\r\n"); - printf("nfc \r\n"); - printf("Cmd list:\r\n"); - printf("\tdetect\t - detect nfc device\r\n"); - printf("\temulate\t - emulate predefined nfca card\r\n"); - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - printf("\tfield\t - turn field on\r\n"); - } -} - -static void nfc_cli_detect(Cli* cli, string_t args) { - UNUSED(args); - // Check if nfc worker is not busy - if(furi_hal_nfc_is_busy()) { - printf("Nfc is busy\r\n"); - return; - } - - FuriHalNfcDevData dev_data = {}; - bool cmd_exit = false; - furi_hal_nfc_exit_sleep(); - printf("Detecting nfc...\r\nPress Ctrl+C to abort\r\n"); - 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("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]); - } - printf("\r\n"); - break; - } - furi_hal_nfc_sleep(); - furi_delay_ms(50); - } - furi_hal_nfc_sleep(); -} - -static void nfc_cli_emulate(Cli* cli, string_t args) { - UNUSED(args); - // Check if nfc worker is not busy - if(furi_hal_nfc_is_busy()) { - printf("Nfc is busy\r\n"); - return; - } - - furi_hal_nfc_exit_sleep(); - printf("Emulating NFC-A Type: T2T UID: 36 9C E7 B1 0A C1 34 SAK: 00 ATQA: 00/44\r\n"); - printf("Press Ctrl+C to abort\r\n"); - - FuriHalNfcDevData params = { - .uid = {0x36, 0x9C, 0xe7, 0xb1, 0x0A, 0xC1, 0x34}, - .uid_len = 7, - .atqa = {0x44, 0x00}, - .sak = 0x00, - .type = FuriHalNfcTypeA, - }; - - while(!cli_cmd_interrupt_received(cli)) { - if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 100)) { - printf("Reader detected\r\n"); - furi_hal_nfc_sleep(); - } - furi_delay_ms(50); - } - furi_hal_nfc_sleep(); -} - -static void nfc_cli_field(Cli* cli, string_t args) { - UNUSED(args); - // Check if nfc worker is not busy - if(furi_hal_nfc_is_busy()) { - printf("Nfc is busy\r\n"); - return; - } - - furi_hal_nfc_exit_sleep(); - furi_hal_nfc_field_on(); - - printf("Field is on. Don't leave device in this mode for too long.\r\n"); - printf("Press Ctrl+C to abort\r\n"); - - while(!cli_cmd_interrupt_received(cli)) { - furi_delay_ms(50); - } - - furi_hal_nfc_field_off(); - furi_hal_nfc_sleep(); -} - -static void nfc_cli(Cli* cli, string_t args, void* context) { - UNUSED(context); - string_t cmd; - string_init(cmd); - - do { - if(!args_read_string_and_trim(args, cmd)) { - nfc_cli_print_usage(); - break; - } - if(string_cmp_str(cmd, "detect") == 0) { - nfc_cli_detect(cli, args); - break; - } - if(string_cmp_str(cmd, "emulate") == 0) { - nfc_cli_emulate(cli, args); - break; - } - - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - if(string_cmp_str(cmd, "field") == 0) { - nfc_cli_field(cli, args); - break; - } - } - - nfc_cli_print_usage(); - } while(false); - - string_clear(cmd); -} - -void nfc_on_system_start() { -#ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - cli_add_command(cli, "nfc", CliCommandFlagDefault, nfc_cli, NULL); - furi_record_close(RECORD_CLI); -#else - UNUSED(nfc_cli); -#endif -} diff --git a/applications/nfc/nfc_i.h b/applications/nfc/nfc_i.h deleted file mode 100644 index bcfe4a21921..00000000000 --- a/applications/nfc/nfc_i.h +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once - -#include "nfc.h" - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "views/dict_attack.h" - -#include -#include - -#include "rpc/rpc_app.h" - -#define NFC_TEXT_STORE_SIZE 128 - -typedef enum { - NfcRpcStateIdle, - NfcRpcStateEmulating, - NfcRpcStateEmulated, -} NfcRpcState; - -// Forward declaration due to circular dependency -typedef struct NfcGenerator NfcGenerator; - -struct Nfc { - NfcWorker* worker; - ViewDispatcher* view_dispatcher; - Gui* gui; - NotificationApp* notifications; - SceneManager* scene_manager; - NfcDevice* dev; - FuriHalNfcDevData dev_edit_data; - - char text_store[NFC_TEXT_STORE_SIZE + 1]; - string_t text_box_store; - uint8_t byte_input_store[6]; - - void* rpc_ctx; - NfcRpcState rpc_state; - - // Common Views - Submenu* submenu; - DialogEx* dialog_ex; - Popup* popup; - Loading* loading; - TextInput* text_input; - ByteInput* byte_input; - TextBox* text_box; - Widget* widget; - DictAttack* dict_attack; - - const NfcGenerator* generator; -}; - -typedef enum { - NfcViewMenu, - NfcViewDialogEx, - NfcViewPopup, - NfcViewLoading, - NfcViewTextInput, - NfcViewByteInput, - NfcViewTextBox, - NfcViewWidget, - NfcViewDictAttack, -} NfcView; - -Nfc* nfc_alloc(); - -int32_t nfc_task(void* p); - -void nfc_text_store_set(Nfc* nfc, const char* text, ...); - -void nfc_text_store_clear(Nfc* nfc); - -void nfc_blink_start(Nfc* nfc); - -void nfc_blink_stop(Nfc* nfc); - -void nfc_show_loading_popup(void* context, bool show); diff --git a/applications/nfc/scenes/nfc_scene_config.h b/applications/nfc/scenes/nfc_scene_config.h deleted file mode 100755 index ff34a11d84c..00000000000 --- a/applications/nfc/scenes/nfc_scene_config.h +++ /dev/null @@ -1,51 +0,0 @@ -ADD_SCENE(nfc, start, Start) -ADD_SCENE(nfc, read, Read) -ADD_SCENE(nfc, saved_menu, SavedMenu) -ADD_SCENE(nfc, extra_actions, ExtraActions) -ADD_SCENE(nfc, set_type, SetType) -ADD_SCENE(nfc, set_sak, SetSak) -ADD_SCENE(nfc, set_atqa, SetAtqua) -ADD_SCENE(nfc, set_uid, SetUid) -ADD_SCENE(nfc, generate_info, GenerateInfo) -ADD_SCENE(nfc, read_card_success, ReadCardSuccess) -ADD_SCENE(nfc, save_name, SaveName) -ADD_SCENE(nfc, save_success, SaveSuccess) -ADD_SCENE(nfc, file_select, FileSelect) -ADD_SCENE(nfc, emulate_uid, EmulateUid) -ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess) -ADD_SCENE(nfc, nfca_menu, NfcaMenu) -ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) -ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData) -ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) -ADD_SCENE(nfc, mf_ultralight_emulate, MfUltralightEmulate) -ADD_SCENE(nfc, mf_ultralight_read_auth, MfUltralightReadAuth) -ADD_SCENE(nfc, mf_ultralight_read_auth_result, MfUltralightReadAuthResult) -ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) -ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu) -ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) -ADD_SCENE(nfc, mf_desfire_read_success, MfDesfireReadSuccess) -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_menu, MfClassicMenu) -ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate) -ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys) -ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd) -ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack) -ADD_SCENE(nfc, emv_read_success, EmvReadSuccess) -ADD_SCENE(nfc, emv_menu, EmvMenu) -ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence) -ADD_SCENE(nfc, device_info, DeviceInfo) -ADD_SCENE(nfc, delete, Delete) -ADD_SCENE(nfc, delete_success, DeleteSuccess) -ADD_SCENE(nfc, restore_original_confirm, RestoreOriginalConfirm) -ADD_SCENE(nfc, restore_original, RestoreOriginal) -ADD_SCENE(nfc, debug, Debug) -ADD_SCENE(nfc, field, Field) -ADD_SCENE(nfc, dict_not_found, DictNotFound) -ADD_SCENE(nfc, rpc, Rpc) -ADD_SCENE(nfc, exit_confirm, ExitConfirm) -ADD_SCENE(nfc, retry_confirm, RetryConfirm) -ADD_SCENE(nfc, detect_reader, DetectReader) -ADD_SCENE(nfc, nfc_data_info, NfcDataInfo) diff --git a/applications/nfc/scenes/nfc_scene_debug.c b/applications/nfc/scenes/nfc_scene_debug.c deleted file mode 100644 index ed079c2ed9a..00000000000 --- a/applications/nfc/scenes/nfc_scene_debug.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "../nfc_i.h" - -enum SubmenuDebugIndex { - SubmenuDebugIndexField, - SubmenuDebugIndexApdu, -}; - -void nfc_scene_debug_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); -} - -void nfc_scene_debug_on_enter(void* context) { - Nfc* nfc = context; - Submenu* submenu = nfc->submenu; - - submenu_add_item( - submenu, "Field", SubmenuDebugIndexField, nfc_scene_debug_submenu_callback, nfc); - submenu_add_item( - submenu, "Apdu", SubmenuDebugIndexApdu, nfc_scene_debug_submenu_callback, nfc); - - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDebug)); - - nfc_device_clear(nfc->dev); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); -} - -bool nfc_scene_debug_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuDebugIndexField) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneDebug, SubmenuDebugIndexField); - scene_manager_next_scene(nfc->scene_manager, NfcSceneField); - consumed = true; - } else if(event.event == SubmenuDebugIndexApdu) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneDebug, SubmenuDebugIndexApdu); - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateApduSequence); - consumed = true; - } - } - return consumed; -} - -void nfc_scene_debug_on_exit(void* context) { - Nfc* nfc = context; - - submenu_reset(nfc->submenu); -} diff --git a/applications/nfc/scenes/nfc_scene_delete.c b/applications/nfc/scenes/nfc_scene_delete.c deleted file mode 100755 index 987927e19c4..00000000000 --- a/applications/nfc/scenes/nfc_scene_delete.c +++ /dev/null @@ -1,77 +0,0 @@ -#include "../nfc_i.h" - -void nfc_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) { - Nfc* nfc = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); - } -} - -void nfc_scene_delete_on_enter(void* context) { - Nfc* nfc = context; - FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; - - // Setup Custom Widget view - string_t temp_str; - string_init(temp_str); - - string_printf(temp_str, "\e#Delete %s?\e#", nfc->dev->dev_name); - widget_add_text_box_element( - nfc->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, string_get_cstr(temp_str), false); - widget_add_button_element( - nfc->widget, GuiButtonTypeLeft, "Cancel", nfc_scene_delete_widget_callback, nfc); - widget_add_button_element( - nfc->widget, GuiButtonTypeRight, "Delete", nfc_scene_delete_widget_callback, nfc); - - string_set_str(temp_str, "UID:"); - for(size_t i = 0; i < nfc_data->uid_len; i++) { - string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); - } - widget_add_string_element( - nfc->widget, 64, 24, AlignCenter, AlignTop, FontSecondary, string_get_cstr(temp_str)); - - NfcProtocol protocol = nfc->dev->dev_data.protocol; - if(protocol == NfcDeviceProtocolEMV) { - string_set_str(temp_str, "EMV bank card"); - } else if(protocol == NfcDeviceProtocolMifareUl) { - string_set_str(temp_str, nfc_mf_ul_type(nfc->dev->dev_data.mf_ul_data.type, true)); - } else if(protocol == NfcDeviceProtocolMifareClassic) { - string_set_str(temp_str, nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type)); - } else if(protocol == NfcDeviceProtocolMifareDesfire) { - string_set_str(temp_str, "MIFARE DESFire"); - } else { - string_set_str(temp_str, "Unknown ISO tag"); - } - widget_add_string_element( - nfc->widget, 64, 34, AlignCenter, AlignTop, FontSecondary, string_get_cstr(temp_str)); - widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, "NFC-A"); - string_clear(temp_str); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); -} - -bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_previous_scene(nfc->scene_manager); - } else if(event.event == GuiButtonTypeRight) { - if(nfc_device_delete(nfc->dev, true)) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess); - } else { - scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneStart); - } - consumed = true; - } - } - return consumed; -} - -void nfc_scene_delete_on_exit(void* context) { - Nfc* nfc = context; - - widget_reset(nfc->widget); -} diff --git a/applications/nfc/scenes/nfc_scene_delete_success.c b/applications/nfc/scenes/nfc_scene_delete_success.c deleted file mode 100755 index 713b99ebf8a..00000000000 --- a/applications/nfc/scenes/nfc_scene_delete_success.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "../nfc_i.h" - -void nfc_scene_delete_success_popup_callback(void* context) { - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); -} - -void nfc_scene_delete_success_on_enter(void* context) { - Nfc* nfc = context; - - // Setup view - Popup* popup = nfc->popup; - popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); - popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); - popup_set_timeout(popup, 1500); - popup_set_context(popup, nfc); - popup_set_callback(popup, nfc_scene_delete_success_popup_callback); - popup_enable_timeout(popup); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); -} - -bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventViewExit) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneFileSelect); - } - } - return consumed; -} - -void nfc_scene_delete_success_on_exit(void* context) { - Nfc* nfc = context; - - // Clear view - popup_reset(nfc->popup); -} diff --git a/applications/nfc/scenes/nfc_scene_detect_reader.c b/applications/nfc/scenes/nfc_scene_detect_reader.c deleted file mode 100644 index f734f04cbd4..00000000000 --- a/applications/nfc/scenes/nfc_scene_detect_reader.c +++ /dev/null @@ -1,143 +0,0 @@ -#include "../nfc_i.h" -#include - -#define NFC_SCENE_DETECT_READER_LOG_SIZE_MAX (200) - -enum { - NfcSceneDetectReaderStateWidget, - NfcSceneDetectReaderStateTextBox, -}; - -bool nfc_detect_reader_worker_callback(NfcWorkerEvent event, void* context) { - UNUSED(event); - furi_assert(context); - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); - return true; -} - -void nfc_scene_detect_reader_widget_callback(GuiButtonType result, InputType type, void* context) { - furi_assert(context); - Nfc* nfc = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); - } -} - -void nfc_detect_reader_textbox_callback(void* context) { - furi_assert(context); - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); -} - -// Add widget with device name or inform that data received -static void nfc_scene_detect_reader_widget_config(Nfc* nfc, bool data_received) { - Widget* widget = nfc->widget; - widget_reset(widget); - - widget_add_icon_element(widget, 0, 14, &I_Reader_detect); - widget_add_string_element( - widget, 64, 3, AlignCenter, AlignTop, FontSecondary, "Hold Near Reader"); - widget_add_string_element(widget, 55, 22, AlignLeft, AlignTop, FontPrimary, "Emulating..."); - - if(data_received) { - widget_add_button_element( - widget, GuiButtonTypeCenter, "Log", nfc_scene_detect_reader_widget_callback, nfc); - } -} - -void nfc_scene_detect_reader_on_enter(void* context) { - Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcEmulate); - FuriHalNfcDevData nfc_params = { - .uid = {0x36, 0x9C, 0xe7, 0xb1, 0x0A, 0xC1, 0x34}, - .uid_len = 7, - .atqa = {0x44, 0x00}, - .sak = 0x08, - .type = FuriHalNfcTypeA, - }; - nfc->dev->dev_data.nfc_data = nfc_params; - - // Setup Widget - nfc_scene_detect_reader_widget_config(nfc, false); - // Setup TextBox - TextBox* text_box = nfc->text_box; - text_box_set_font(text_box, TextBoxFontHex); - text_box_set_focus(text_box, TextBoxFocusEnd); - string_reset(nfc->text_box_store); - - // Set Widget state and view - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); - // Start worker - memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); - nfc_worker_start( - nfc->worker, - NfcWorkerStateUidEmulate, - &nfc->dev->dev_data, - nfc_detect_reader_worker_callback, - nfc); - - nfc_blink_start(nfc); -} - -bool nfc_scene_detect_reader_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - NfcReaderRequestData* reader_data = &nfc->dev->dev_data.reader_data; - uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDetectReader); - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventWorkerExit) { - // Add data button to widget if data is received for the first time - if(!string_size(nfc->text_box_store)) { - nfc_scene_detect_reader_widget_config(nfc, true); - } - // Update TextBox data - if(string_size(nfc->text_box_store) < NFC_SCENE_DETECT_READER_LOG_SIZE_MAX) { - string_cat_printf(nfc->text_box_store, "R:"); - for(uint16_t i = 0; i < reader_data->size; i++) { - string_cat_printf(nfc->text_box_store, " %02X", reader_data->data[i]); - } - string_push_back(nfc->text_box_store, '\n'); - text_box_set_text(nfc->text_box, string_get_cstr(nfc->text_box_store)); - } - memset(reader_data, 0, sizeof(NfcReaderRequestData)); - consumed = true; - } else if(event.event == GuiButtonTypeCenter && state == NfcSceneDetectReaderStateWidget) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateTextBox); - consumed = true; - } else if(event.event == NfcCustomEventViewExit && state == NfcSceneDetectReaderStateTextBox) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - if(state == NfcSceneDetectReaderStateTextBox) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget); - consumed = true; - } - } - - return consumed; -} - -void nfc_scene_detect_reader_on_exit(void* context) { - Nfc* nfc = context; - - // Stop worker - nfc_worker_stop(nfc->worker); - - // Clear view - widget_reset(nfc->widget); - text_box_reset(nfc->text_box); - string_reset(nfc->text_box_store); - - nfc_blink_stop(nfc); -} diff --git a/applications/nfc/scenes/nfc_scene_device_info.c b/applications/nfc/scenes/nfc_scene_device_info.c deleted file mode 100644 index 8228c7ea3b6..00000000000 --- a/applications/nfc/scenes/nfc_scene_device_info.c +++ /dev/null @@ -1,81 +0,0 @@ -#include "../nfc_i.h" -#include "../helpers/nfc_emv_parser.h" - -void nfc_scene_device_info_widget_callback(GuiButtonType result, InputType type, void* context) { - Nfc* nfc = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); - } -} - -void nfc_scene_device_info_on_enter(void* context) { - Nfc* nfc = context; - NfcDeviceData* dev_data = &nfc->dev->dev_data; - - string_t temp_str; - string_init(temp_str); - - if(dev_data->protocol == NfcDeviceProtocolEMV) { - EmvData* emv_data = &dev_data->emv_data; - string_printf(temp_str, "\e#%s\n", emv_data->name); - for(uint8_t i = 0; i < emv_data->number_len; i += 2) { - string_cat_printf(temp_str, "%02X%02X ", emv_data->number[i], emv_data->number[i + 1]); - } - string_strim(temp_str); - - // Add expiration date - if(emv_data->exp_mon) { - string_cat_printf(temp_str, "\nExp: %02X/%02X", emv_data->exp_mon, emv_data->exp_year); - } - // Parse currency code - if((emv_data->currency_code)) { - string_t currency_name; - string_init(currency_name); - if(nfc_emv_parser_get_currency_name( - nfc->dev->storage, emv_data->currency_code, currency_name)) { - string_cat_printf(temp_str, "\nCur: %s ", string_get_cstr(currency_name)); - } - string_clear(currency_name); - } - // Parse country code - if((emv_data->country_code)) { - string_t country_name; - string_init(country_name); - if(nfc_emv_parser_get_country_name( - nfc->dev->storage, emv_data->country_code, country_name)) { - string_cat_printf(temp_str, "Reg: %s", string_get_cstr(country_name)); - } - string_clear(country_name); - } - } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { - string_set(temp_str, nfc->dev->dev_data.parsed_data); - } - - widget_add_text_scroll_element(nfc->widget, 0, 0, 128, 52, string_get_cstr(temp_str)); - string_clear(temp_str); - - widget_add_button_element( - nfc->widget, GuiButtonTypeRight, "More", nfc_scene_device_info_widget_callback, nfc); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); -} - -bool nfc_scene_device_info_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeRight) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); - consumed = true; - } - } - return consumed; -} - -void nfc_scene_device_info_on_exit(void* context) { - Nfc* nfc = context; - - // Clear views - widget_reset(nfc->widget); -} diff --git a/applications/nfc/scenes/nfc_scene_dict_not_found.c b/applications/nfc/scenes/nfc_scene_dict_not_found.c deleted file mode 100644 index dc21b08b1cc..00000000000 --- a/applications/nfc/scenes/nfc_scene_dict_not_found.c +++ /dev/null @@ -1,49 +0,0 @@ -#include "../nfc_i.h" - -void nfc_scene_dict_not_found_popup_callback(void* context) { - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); -} - -void nfc_scene_dict_not_found_on_enter(void* context) { - Nfc* nfc = context; - - // Setup view - Popup* popup = nfc->popup; - popup_set_text( - popup, - "Function requires\nan SD card with\nfresh databases.", - 82, - 24, - AlignCenter, - AlignCenter); - popup_set_icon(popup, 6, 10, &I_SDQuestion_35x43); - popup_set_timeout(popup, 2500); - popup_set_context(popup, nfc); - popup_set_callback(popup, nfc_scene_dict_not_found_popup_callback); - popup_enable_timeout(popup); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); -} - -bool nfc_scene_dict_not_found_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventViewExit) { - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneExtraActions)) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneExtraActions); - } else { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneStart); - } - } - } - return consumed; -} - -void nfc_scene_dict_not_found_on_exit(void* context) { - Nfc* nfc = context; - popup_reset(nfc->popup); -} diff --git a/applications/nfc/scenes/nfc_scene_emulate_apdu_sequence.c b/applications/nfc/scenes/nfc_scene_emulate_apdu_sequence.c deleted file mode 100644 index e6062ba4982..00000000000 --- a/applications/nfc/scenes/nfc_scene_emulate_apdu_sequence.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "../nfc_i.h" -#include - -void nfc_scene_emulate_apdu_sequence_on_enter(void* context) { - Nfc* nfc = context; - - // Setup view - Popup* popup = nfc->popup; - popup_set_header(popup, "Run APDU reader", 64, 31, AlignCenter, AlignTop); - - // Setup and start worker - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - nfc_worker_start(nfc->worker, NfcWorkerStateEmulateApdu, &nfc->dev->dev_data, NULL, nfc); - - nfc_blink_start(nfc); -} - -bool nfc_scene_emulate_apdu_sequence_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - bool consumed = false; - return consumed; -} - -void nfc_scene_emulate_apdu_sequence_on_exit(void* context) { - Nfc* nfc = context; - - // Stop worker - nfc_worker_stop(nfc->worker); - // Clear view - popup_reset(nfc->popup); - - nfc_blink_stop(nfc); -} diff --git a/applications/nfc/scenes/nfc_scene_emulate_uid.c b/applications/nfc/scenes/nfc_scene_emulate_uid.c deleted file mode 100755 index 0d92c9f040a..00000000000 --- a/applications/nfc/scenes/nfc_scene_emulate_uid.c +++ /dev/null @@ -1,146 +0,0 @@ -#include "../nfc_i.h" -#include - -#define NFC_SCENE_EMULATE_UID_LOG_SIZE_MAX (200) - -enum { - NfcSceneEmulateUidStateWidget, - NfcSceneEmulateUidStateTextBox, -}; - -bool nfc_emulate_uid_worker_callback(NfcWorkerEvent event, void* context) { - UNUSED(event); - furi_assert(context); - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); - return true; -} - -void nfc_scene_emulate_uid_widget_callback(GuiButtonType result, InputType type, void* context) { - furi_assert(context); - Nfc* nfc = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); - } -} - -void nfc_emulate_uid_textbox_callback(void* context) { - furi_assert(context); - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); -} - -// Add widget with device name or inform that data received -static void nfc_scene_emulate_uid_widget_config(Nfc* nfc, bool data_received) { - FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; - Widget* widget = nfc->widget; - widget_reset(widget); - string_t info_str; - string_init(info_str); - - widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61); - widget_add_string_element(widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Emulating UID"); - if(strcmp(nfc->dev->dev_name, "")) { - string_printf(info_str, "%s", nfc->dev->dev_name); - } else { - for(uint8_t i = 0; i < data->uid_len; i++) { - string_cat_printf(info_str, "%02X ", data->uid[i]); - } - } - string_strim(info_str); - widget_add_text_box_element( - widget, 56, 43, 70, 21, AlignCenter, AlignTop, string_get_cstr(info_str), true); - string_clear(info_str); - if(data_received) { - widget_add_button_element( - widget, GuiButtonTypeCenter, "Log", nfc_scene_emulate_uid_widget_callback, nfc); - } -} - -void nfc_scene_emulate_uid_on_enter(void* context) { - Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcEmulate); - - // Setup Widget - nfc_scene_emulate_uid_widget_config(nfc, false); - // Setup TextBox - TextBox* text_box = nfc->text_box; - text_box_set_font(text_box, TextBoxFontHex); - text_box_set_focus(text_box, TextBoxFocusEnd); - string_reset(nfc->text_box_store); - - // Set Widget state and view - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneEmulateUid, NfcSceneEmulateUidStateWidget); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); - // Start worker - memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); - nfc_worker_start( - nfc->worker, - NfcWorkerStateUidEmulate, - &nfc->dev->dev_data, - nfc_emulate_uid_worker_callback, - nfc); - - nfc_blink_start(nfc); -} - -bool nfc_scene_emulate_uid_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - NfcReaderRequestData* reader_data = &nfc->dev->dev_data.reader_data; - uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmulateUid); - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventWorkerExit) { - // Add data button to widget if data is received for the first time - if(!string_size(nfc->text_box_store)) { - nfc_scene_emulate_uid_widget_config(nfc, true); - } - // Update TextBox data - if(string_size(nfc->text_box_store) < NFC_SCENE_EMULATE_UID_LOG_SIZE_MAX) { - string_cat_printf(nfc->text_box_store, "R:"); - for(uint16_t i = 0; i < reader_data->size; i++) { - string_cat_printf(nfc->text_box_store, " %02X", reader_data->data[i]); - } - string_push_back(nfc->text_box_store, '\n'); - text_box_set_text(nfc->text_box, string_get_cstr(nfc->text_box_store)); - } - memset(reader_data, 0, sizeof(NfcReaderRequestData)); - consumed = true; - } else if(event.event == GuiButtonTypeCenter && state == NfcSceneEmulateUidStateWidget) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneEmulateUid, NfcSceneEmulateUidStateTextBox); - consumed = true; - } else if(event.event == NfcCustomEventViewExit && state == NfcSceneEmulateUidStateTextBox) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneEmulateUid, NfcSceneEmulateUidStateWidget); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - if(state == NfcSceneEmulateUidStateTextBox) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneEmulateUid, NfcSceneEmulateUidStateWidget); - consumed = true; - } - } - - return consumed; -} - -void nfc_scene_emulate_uid_on_exit(void* context) { - Nfc* nfc = context; - - // Stop worker - nfc_worker_stop(nfc->worker); - - // Clear view - widget_reset(nfc->widget); - text_box_reset(nfc->text_box); - string_reset(nfc->text_box_store); - - nfc_blink_stop(nfc); -} diff --git a/applications/nfc/scenes/nfc_scene_emv_menu.c b/applications/nfc/scenes/nfc_scene_emv_menu.c deleted file mode 100644 index 1da630fcf13..00000000000 --- a/applications/nfc/scenes/nfc_scene_emv_menu.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "../nfc_i.h" - -enum SubmenuIndex { - SubmenuIndexSave, - SubmenuIndexInfo, -}; - -void nfc_scene_emv_menu_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); -} - -void nfc_scene_emv_menu_on_enter(void* context) { - Nfc* nfc = context; - Submenu* submenu = nfc->submenu; - - submenu_add_item(submenu, "Save", SubmenuIndexSave, nfc_scene_emv_menu_submenu_callback, nfc); - submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_emv_menu_submenu_callback, nfc); - submenu_set_selected_item( - nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmvMenu)); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); -} - -bool nfc_scene_emv_menu_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexSave) { - nfc->dev->format = NfcDeviceSaveFormatBankCard; - // Clear device name - nfc_device_set_name(nfc->dev, ""); - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); - consumed = true; - } else if(event.event == SubmenuIndexInfo) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); - consumed = true; - } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneEmvMenu, event.event); - } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_previous_scene(nfc->scene_manager); - } - - return consumed; -} - -void nfc_scene_emv_menu_on_exit(void* context) { - Nfc* nfc = context; - - // Clear view - submenu_reset(nfc->submenu); -} diff --git a/applications/nfc/scenes/nfc_scene_emv_read_success.c b/applications/nfc/scenes/nfc_scene_emv_read_success.c deleted file mode 100644 index 9cf7ff9e903..00000000000 --- a/applications/nfc/scenes/nfc_scene_emv_read_success.c +++ /dev/null @@ -1,88 +0,0 @@ -#include "../nfc_i.h" -#include "../helpers/nfc_emv_parser.h" -#include - -void nfc_scene_emv_read_success_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - Nfc* nfc = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); - } -} - -void nfc_scene_emv_read_success_on_enter(void* context) { - Nfc* nfc = context; - EmvData* emv_data = &nfc->dev->dev_data.emv_data; - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - - // Setup Custom Widget view - widget_add_button_element( - nfc->widget, GuiButtonTypeLeft, "Retry", nfc_scene_emv_read_success_widget_callback, nfc); - widget_add_button_element( - nfc->widget, GuiButtonTypeRight, "More", nfc_scene_emv_read_success_widget_callback, nfc); - - string_t temp_str; - string_init_printf(temp_str, "\e#%s\n", emv_data->name); - for(uint8_t i = 0; i < emv_data->number_len; i += 2) { - string_cat_printf(temp_str, "%02X%02X ", emv_data->number[i], emv_data->number[i + 1]); - } - string_strim(temp_str); - - // Add expiration date - if(emv_data->exp_mon) { - string_cat_printf(temp_str, "\nExp: %02X/%02X", emv_data->exp_mon, emv_data->exp_year); - } - // Parse currency code - if((emv_data->currency_code)) { - string_t currency_name; - string_init(currency_name); - if(nfc_emv_parser_get_currency_name( - nfc->dev->storage, emv_data->currency_code, currency_name)) { - string_cat_printf(temp_str, "\nCur: %s ", string_get_cstr(currency_name)); - } - string_clear(currency_name); - } - // Parse country code - if((emv_data->country_code)) { - string_t country_name; - string_init(country_name); - if(nfc_emv_parser_get_country_name( - nfc->dev->storage, emv_data->country_code, country_name)) { - string_cat_printf(temp_str, "Reg: %s", string_get_cstr(country_name)); - } - string_clear(country_name); - } - - widget_add_text_scroll_element(nfc->widget, 0, 0, 128, 52, string_get_cstr(temp_str)); - string_clear(temp_str); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); -} - -bool nfc_scene_emv_read_success_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); - consumed = true; - } else if(event.event == GuiButtonTypeRight) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmvMenu); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); - consumed = true; - } - return consumed; -} - -void nfc_scene_emv_read_success_on_exit(void* context) { - Nfc* nfc = context; - - // Clear view - widget_reset(nfc->widget); -} diff --git a/applications/nfc/scenes/nfc_scene_exit_confirm.c b/applications/nfc/scenes/nfc_scene_exit_confirm.c deleted file mode 100644 index b22dd42a13a..00000000000 --- a/applications/nfc/scenes/nfc_scene_exit_confirm.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "../nfc_i.h" - -void nfc_scene_exit_confirm_dialog_callback(DialogExResult result, void* context) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); -} - -void nfc_scene_exit_confirm_on_enter(void* context) { - Nfc* nfc = context; - DialogEx* dialog_ex = nfc->dialog_ex; - - dialog_ex_set_left_button_text(dialog_ex, "Exit"); - dialog_ex_set_right_button_text(dialog_ex, "Stay"); - dialog_ex_set_header(dialog_ex, "Exit to NFC Menu?", 64, 11, AlignCenter, AlignTop); - dialog_ex_set_text( - 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_exit_confirm_dialog_callback); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); -} - -bool nfc_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == DialogExResultRight) { - consumed = scene_manager_previous_scene(nfc->scene_manager); - } else if(event.event == DialogExResultLeft) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneStart); - } - } else if(event.type == SceneManagerEventTypeBack) { - consumed = true; - } - - return consumed; -} - -void nfc_scene_exit_confirm_on_exit(void* context) { - Nfc* nfc = context; - - // Clean view - dialog_ex_reset(nfc->dialog_ex); -} diff --git a/applications/nfc/scenes/nfc_scene_extra_actions.c b/applications/nfc/scenes/nfc_scene_extra_actions.c deleted file mode 100644 index 43e49e5a0ff..00000000000 --- a/applications/nfc/scenes/nfc_scene_extra_actions.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "../nfc_i.h" - -enum SubmenuIndex { - SubmenuIndexMfClassicKeys, - SubmenuIndexMfUltralightUnlock, -}; - -void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); -} - -void nfc_scene_extra_actions_on_enter(void* context) { - Nfc* nfc = context; - Submenu* submenu = nfc->submenu; - - submenu_add_item( - submenu, - "Mf Classic Keys", - SubmenuIndexMfClassicKeys, - nfc_scene_extra_actions_submenu_callback, - nfc); - submenu_add_item( - submenu, - "Unlock NTAG/Ultralight", - SubmenuIndexMfUltralightUnlock, - nfc_scene_extra_actions_submenu_callback, - nfc); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); -} - -bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexMfClassicKeys) { - if(mf_classic_dict_check_presence(MfClassicDictTypeFlipper)) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeys); - } else { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); - } - consumed = true; - } else if(event.event == SubmenuIndexMfUltralightUnlock) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); - } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); - } - return consumed; -} - -void nfc_scene_extra_actions_on_exit(void* context) { - Nfc* nfc = context; - - submenu_reset(nfc->submenu); -} diff --git a/applications/nfc/scenes/nfc_scene_file_select.c b/applications/nfc/scenes/nfc_scene_file_select.c deleted file mode 100755 index 693fdec2052..00000000000 --- a/applications/nfc/scenes/nfc_scene_file_select.c +++ /dev/null @@ -1,25 +0,0 @@ -#include "../nfc_i.h" -#include "nfc/nfc_device.h" - -void nfc_scene_file_select_on_enter(void* context) { - Nfc* nfc = context; - // Process file_select return - nfc_device_set_loading_callback(nfc->dev, nfc_show_loading_popup, nfc); - if(nfc_file_select(nfc->dev)) { - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, 0); - scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu); - } else { - scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart); - } - nfc_device_set_loading_callback(nfc->dev, NULL, nfc); -} - -bool nfc_scene_file_select_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void nfc_scene_file_select_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/nfc/scenes/nfc_scene_generate_info.c b/applications/nfc/scenes/nfc_scene_generate_info.c deleted file mode 100644 index 7fb7eb94207..00000000000 --- a/applications/nfc/scenes/nfc_scene_generate_info.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "../nfc_i.h" -#include "../helpers/nfc_generators.h" - -void nfc_scene_generate_info_dialog_callback(DialogExResult result, void* context) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); -} - -void nfc_scene_generate_info_on_enter(void* context) { - Nfc* nfc = context; - - // Setup dialog view - FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; - DialogEx* dialog_ex = nfc->dialog_ex; - dialog_ex_set_right_button_text(dialog_ex, "More"); - - // Create info text - string_t info_str; - string_init_printf( - info_str, "%s\n%s\nUID:", nfc->generator->name, nfc_get_dev_type(data->type)); - // Append UID - for(int i = 0; i < data->uid_len; ++i) { - string_cat_printf(info_str, " %02X", data->uid[i]); - } - nfc_text_store_set(nfc, string_get_cstr(info_str)); - string_clear(info_str); - - dialog_ex_set_text(dialog_ex, nfc->text_store, 0, 0, AlignLeft, AlignTop); - dialog_ex_set_context(dialog_ex, nfc); - dialog_ex_set_result_callback(dialog_ex, nfc_scene_generate_info_dialog_callback); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); -} - -bool nfc_scene_generate_info_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == DialogExResultRight) { - scene_manager_next_scene(nfc->scene_manager, nfc->generator->next_scene); - consumed = true; - } - } - - return consumed; -} - -void nfc_scene_generate_info_on_exit(void* context) { - Nfc* nfc = context; - - // Clean views - dialog_ex_reset(nfc->dialog_ex); -} diff --git a/applications/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/nfc/scenes/nfc_scene_mf_classic_dict_attack.c deleted file mode 100644 index d821c182da6..00000000000 --- a/applications/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ /dev/null @@ -1,170 +0,0 @@ -#include "../nfc_i.h" - -#define TAG "NfcMfClassicDictAttack" - -typedef enum { - DictAttackStateIdle, - DictAttackStateUserDictInProgress, - DictAttackStateFlipperDictInProgress, -} DictAttackState; - -bool nfc_dict_attack_worker_callback(NfcWorkerEvent event, void* context) { - furi_assert(context); - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, event); - return true; -} - -void nfc_dict_attack_dict_attack_result_callback(void* context) { - furi_assert(context); - Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventDictAttackSkip); -} - -static void nfc_scene_mf_classic_dict_attack_update_view(Nfc* nfc) { - MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; - uint8_t sectors_read = 0; - uint8_t keys_found = 0; - - // Calculate found keys and read sectors - mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); - dict_attack_set_keys_found(nfc->dict_attack, keys_found); - dict_attack_set_sector_read(nfc->dict_attack, sectors_read); -} - -static void nfc_scene_mf_classic_dict_attack_prepare_view(Nfc* nfc, DictAttackState state) { - MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; - NfcMfClassicDictAttackData* dict_attack_data = &nfc->dev->dev_data.mf_classic_dict_attack_data; - NfcWorkerState worker_state = NfcWorkerStateReady; - MfClassicDict* dict = NULL; - - // Identify scene state - if(state == DictAttackStateIdle) { - if(mf_classic_dict_check_presence(MfClassicDictTypeUser)) { - state = DictAttackStateUserDictInProgress; - } else { - state = DictAttackStateFlipperDictInProgress; - } - } else if(state == DictAttackStateUserDictInProgress) { - state = DictAttackStateFlipperDictInProgress; - } - - // Setup view - if(state == DictAttackStateUserDictInProgress) { - worker_state = NfcWorkerStateMfClassicDictAttack; - dict_attack_set_header(nfc->dict_attack, "Mf Classic User Dict."); - dict = mf_classic_dict_alloc(MfClassicDictTypeUser); - - // If failed to load user dictionary - try flipper dictionary - if(!dict) { - FURI_LOG_E(TAG, "User dictionary not found"); - state = DictAttackStateFlipperDictInProgress; - } - } - if(state == DictAttackStateFlipperDictInProgress) { - worker_state = NfcWorkerStateMfClassicDictAttack; - dict_attack_set_header(nfc->dict_attack, "Mf Classic Flipper Dict."); - dict = mf_classic_dict_alloc(MfClassicDictTypeFlipper); - if(!dict) { - FURI_LOG_E(TAG, "Flipper dictionary not found"); - // Pass through to let worker handle the failure - } - } - // Free previous dictionary - if(dict_attack_data->dict) { - mf_classic_dict_free(dict_attack_data->dict); - } - dict_attack_data->dict = dict; - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfClassicDictAttack, state); - dict_attack_set_callback(nfc->dict_attack, nfc_dict_attack_dict_attack_result_callback, nfc); - dict_attack_set_current_sector(nfc->dict_attack, 0); - dict_attack_set_card_detected(nfc->dict_attack, data->type); - dict_attack_set_total_dict_keys( - nfc->dict_attack, dict ? mf_classic_dict_get_total_keys(dict) : 0); - nfc_scene_mf_classic_dict_attack_update_view(nfc); - nfc_worker_start( - nfc->worker, worker_state, &nfc->dev->dev_data, nfc_dict_attack_worker_callback, nfc); -} - -void nfc_scene_mf_classic_dict_attack_on_enter(void* context) { - Nfc* nfc = context; - nfc_scene_mf_classic_dict_attack_prepare_view(nfc, DictAttackStateIdle); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDictAttack); - nfc_blink_start(nfc); -} - -bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; - bool consumed = false; - - uint32_t state = - scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicDictAttack); - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcWorkerEventSuccess) { - if(state == DictAttackStateUserDictInProgress) { - nfc_worker_stop(nfc->worker); - nfc_scene_mf_classic_dict_attack_prepare_view(nfc, state); - consumed = true; - } else { - notification_message(nfc->notifications, &sequence_success); - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); - consumed = true; - } - } else if(event.event == NfcWorkerEventAborted) { - if(state == DictAttackStateUserDictInProgress) { - nfc_scene_mf_classic_dict_attack_prepare_view(nfc, state); - consumed = true; - } else { - notification_message(nfc->notifications, &sequence_success); - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); - consumed = true; - } - } else if(event.event == NfcWorkerEventCardDetected) { - dict_attack_set_card_detected(nfc->dict_attack, data->type); - consumed = true; - } else if(event.event == NfcWorkerEventNoCardDetected) { - dict_attack_set_card_removed(nfc->dict_attack); - consumed = true; - } else if(event.event == NfcWorkerEventFoundKeyA) { - dict_attack_inc_keys_found(nfc->dict_attack); - consumed = true; - } else if(event.event == NfcWorkerEventFoundKeyB) { - dict_attack_inc_keys_found(nfc->dict_attack); - consumed = true; - } else if(event.event == NfcWorkerEventNewSector) { - nfc_scene_mf_classic_dict_attack_update_view(nfc); - dict_attack_inc_current_sector(nfc->dict_attack); - consumed = true; - } else if(event.event == NfcWorkerEventNewDictKeyBatch) { - nfc_scene_mf_classic_dict_attack_update_view(nfc); - dict_attack_inc_current_dict_key(nfc->dict_attack, NFC_DICT_KEY_BATCH_SIZE); - consumed = true; - } else if(event.event == NfcCustomEventDictAttackSkip) { - if(state == DictAttackStateUserDictInProgress) { - nfc_worker_stop(nfc->worker); - consumed = true; - } else if(state == DictAttackStateFlipperDictInProgress) { - nfc_worker_stop(nfc->worker); - consumed = true; - } - } - } else if(event.type == SceneManagerEventTypeBack) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); - consumed = true; - } - return consumed; -} - -void nfc_scene_mf_classic_dict_attack_on_exit(void* context) { - Nfc* nfc = context; - NfcMfClassicDictAttackData* dict_attack_data = &nfc->dev->dev_data.mf_classic_dict_attack_data; - // Stop worker - nfc_worker_stop(nfc->worker); - if(dict_attack_data->dict) { - mf_classic_dict_free(dict_attack_data->dict); - dict_attack_data->dict = NULL; - } - dict_attack_reset(nfc->dict_attack); - nfc_blink_stop(nfc); -} diff --git a/applications/nfc/scenes/nfc_scene_mf_classic_emulate.c b/applications/nfc/scenes/nfc_scene_mf_classic_emulate.c deleted file mode 100644 index 044388b8b57..00000000000 --- a/applications/nfc/scenes/nfc_scene_mf_classic_emulate.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "../nfc_i.h" -#include - -#define NFC_MF_CLASSIC_DATA_NOT_CHANGED (0UL) -#define NFC_MF_CLASSIC_DATA_CHANGED (1UL) - -bool nfc_mf_classic_emulate_worker_callback(NfcWorkerEvent event, void* context) { - UNUSED(event); - Nfc* nfc = context; - - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfClassicEmulate, NFC_MF_CLASSIC_DATA_CHANGED); - return true; -} - -void nfc_scene_mf_classic_emulate_on_enter(void* context) { - Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcEmulate); - - // Setup view - Popup* popup = nfc->popup; - if(strcmp(nfc->dev->dev_name, "")) { - nfc_text_store_set(nfc, "Emulating\n%s", nfc->dev->dev_name); - } else { - nfc_text_store_set(nfc, "Emulating\nMf Classic", nfc->dev->dev_name); - } - popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); - popup_set_header(popup, nfc->text_store, 56, 31, AlignLeft, AlignTop); - - // Setup and start worker - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - nfc_worker_start( - nfc->worker, - NfcWorkerStateMfClassicEmulate, - &nfc->dev->dev_data, - nfc_mf_classic_emulate_worker_callback, - nfc); - nfc_blink_start(nfc); -} - -bool nfc_scene_mf_classic_emulate_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeBack) { - // Stop worker - nfc_worker_stop(nfc->worker); - // Check if data changed and save in shadow file - if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicEmulate) == - NFC_MF_CLASSIC_DATA_CHANGED) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfClassicEmulate, NFC_MF_CLASSIC_DATA_NOT_CHANGED); - nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); - } - consumed = false; - } - return consumed; -} - -void nfc_scene_mf_classic_emulate_on_exit(void* context) { - Nfc* nfc = context; - - // Clear view - popup_reset(nfc->popup); - - nfc_blink_stop(nfc); -} diff --git a/applications/nfc/scenes/nfc_scene_mf_classic_keys.c b/applications/nfc/scenes/nfc_scene_mf_classic_keys.c deleted file mode 100644 index fcb8bc189f7..00000000000 --- a/applications/nfc/scenes/nfc_scene_mf_classic_keys.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "../nfc_i.h" - -void nfc_scene_mf_classic_keys_widget_callback(GuiButtonType result, InputType type, void* context) { - Nfc* nfc = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); - } -} - -void nfc_scene_mf_classic_keys_on_enter(void* context) { - Nfc* nfc = context; - - // Load flipper dict keys total - uint32_t flipper_dict_keys_total = 0; - MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeFlipper); - if(dict) { - flipper_dict_keys_total = mf_classic_dict_get_total_keys(dict); - mf_classic_dict_free(dict); - } - // Load user dict keys total - uint32_t user_dict_keys_total = 0; - dict = mf_classic_dict_alloc(MfClassicDictTypeUser); - if(dict) { - user_dict_keys_total = mf_classic_dict_get_total_keys(dict); - mf_classic_dict_free(dict); - } - - widget_add_string_element( - nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, "MF Classic Keys"); - char temp_str[32]; - snprintf(temp_str, sizeof(temp_str), "Flipper dict: %ld", flipper_dict_keys_total); - widget_add_string_element(nfc->widget, 0, 20, AlignLeft, AlignTop, FontSecondary, temp_str); - snprintf(temp_str, sizeof(temp_str), "User dict: %ld", user_dict_keys_total); - widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str); - widget_add_button_element( - nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc); - widget_add_icon_element(nfc->widget, 90, 12, &I_Keychain); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); -} - -bool nfc_scene_mf_classic_keys_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeCenter) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicKeysAdd); - consumed = true; - } - } - - return consumed; -} - -void nfc_scene_mf_classic_keys_on_exit(void* context) { - Nfc* nfc = context; - - widget_reset(nfc->widget); -} diff --git a/applications/nfc/scenes/nfc_scene_mf_classic_keys_add.c b/applications/nfc/scenes/nfc_scene_mf_classic_keys_add.c deleted file mode 100644 index 9f56b0f452e..00000000000 --- a/applications/nfc/scenes/nfc_scene_mf_classic_keys_add.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "../nfc_i.h" - -void nfc_scene_mf_classic_keys_add_byte_input_callback(void* context) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); -} - -void nfc_scene_mf_classic_keys_add_on_enter(void* context) { - Nfc* nfc = context; - - // Setup view - ByteInput* byte_input = nfc->byte_input; - byte_input_set_header_text(byte_input, "Enter the key in hex"); - byte_input_set_result_callback( - byte_input, - nfc_scene_mf_classic_keys_add_byte_input_callback, - NULL, - nfc, - nfc->byte_input_store, - 6); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); -} - -bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventByteInputDone) { - // Add key to dict - bool key_added = false; - MfClassicDict* dict = mf_classic_dict_alloc(MfClassicDictTypeUser); - if(dict) { - if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) { - key_added = true; - } - } - if(key_added) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); - } else { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); - } - mf_classic_dict_free(dict); - consumed = true; - } - } - return consumed; -} - -void nfc_scene_mf_classic_keys_add_on_exit(void* context) { - Nfc* nfc = context; - - // Clear view - byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(nfc->byte_input, ""); -} diff --git a/applications/nfc/scenes/nfc_scene_mf_classic_menu.c b/applications/nfc/scenes/nfc_scene_mf_classic_menu.c deleted file mode 100644 index 76d02e01ea1..00000000000 --- a/applications/nfc/scenes/nfc_scene_mf_classic_menu.c +++ /dev/null @@ -1,68 +0,0 @@ -#include "../nfc_i.h" - -enum SubmenuIndex { - SubmenuIndexSave, - SubmenuIndexEmulate, - SubmenuIndexInfo, -}; - -void nfc_scene_mf_classic_menu_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); -} - -void nfc_scene_mf_classic_menu_on_enter(void* context) { - Nfc* nfc = context; - Submenu* submenu = nfc->submenu; - - submenu_add_item( - submenu, "Save", SubmenuIndexSave, nfc_scene_mf_classic_menu_submenu_callback, nfc); - submenu_add_item( - submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_mf_classic_menu_submenu_callback, nfc); - submenu_add_item( - submenu, "Info", SubmenuIndexInfo, nfc_scene_mf_classic_menu_submenu_callback, nfc); - - submenu_set_selected_item( - nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicMenu)); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); -} - -bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexSave) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexSave); - nfc->dev->format = NfcDeviceSaveFormatMifareClassic; - // Clear device name - nfc_device_set_name(nfc->dev, ""); - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); - consumed = true; - } else if(event.event == SubmenuIndexEmulate) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexEmulate); - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); - consumed = true; - } else if(event.event == SubmenuIndexInfo) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexInfo); - scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_previous_scene(nfc->scene_manager); - } - - return consumed; -} - -void nfc_scene_mf_classic_menu_on_exit(void* context) { - Nfc* nfc = context; - - // Clear view - submenu_reset(nfc->submenu); -} diff --git a/applications/nfc/scenes/nfc_scene_mf_classic_read_success.c b/applications/nfc/scenes/nfc_scene_mf_classic_read_success.c deleted file mode 100644 index efe676706d4..00000000000 --- a/applications/nfc/scenes/nfc_scene_mf_classic_read_success.c +++ /dev/null @@ -1,81 +0,0 @@ -#include "../nfc_i.h" -#include - -void nfc_scene_mf_classic_read_success_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - furi_assert(context); - Nfc* nfc = context; - - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); - } -} - -void nfc_scene_mf_classic_read_success_on_enter(void* context) { - Nfc* nfc = context; - NfcDeviceData* dev_data = &nfc->dev->dev_data; - MfClassicData* mf_data = &dev_data->mf_classic_data; - - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - - // Setup view - Widget* widget = nfc->widget; - widget_add_button_element( - widget, GuiButtonTypeLeft, "Retry", nfc_scene_mf_classic_read_success_widget_callback, nfc); - widget_add_button_element( - widget, GuiButtonTypeRight, "More", nfc_scene_mf_classic_read_success_widget_callback, nfc); - - string_t temp_str; - if(string_size(nfc->dev->dev_data.parsed_data)) { - string_init_set(temp_str, nfc->dev->dev_data.parsed_data); - } else { - string_init_printf(temp_str, "\e#%s\n", nfc_mf_classic_type(mf_data->type)); - string_cat_printf(temp_str, "UID:"); - for(size_t i = 0; i < dev_data->nfc_data.uid_len; i++) { - string_cat_printf(temp_str, " %02X", dev_data->nfc_data.uid[i]); - } - uint8_t sectors_total = mf_classic_get_total_sectors_num(mf_data->type); - uint8_t keys_total = sectors_total * 2; - uint8_t keys_found = 0; - uint8_t sectors_read = 0; - mf_classic_get_read_sectors_and_keys(mf_data, §ors_read, &keys_found); - string_cat_printf(temp_str, "\nKeys Found: %d/%d", keys_found, keys_total); - string_cat_printf(temp_str, "\nSectors Read: %d/%d", sectors_read, sectors_total); - } - - widget_add_text_scroll_element(widget, 0, 0, 128, 52, string_get_cstr(temp_str)); - string_clear(temp_str); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); -} - -bool nfc_scene_mf_classic_read_success_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); - consumed = true; - } else if(event.event == GuiButtonTypeRight) { - // Clear device name - nfc_device_set_name(nfc->dev, ""); - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicMenu); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); - consumed = true; - } - - return consumed; -} - -void nfc_scene_mf_classic_read_success_on_exit(void* context) { - Nfc* nfc = context; - - // Clear view - widget_reset(nfc->widget); -} diff --git a/applications/nfc/scenes/nfc_scene_mf_desfire_app.c b/applications/nfc/scenes/nfc_scene_mf_desfire_app.c deleted file mode 100644 index 7faafdcf0ba..00000000000 --- a/applications/nfc/scenes/nfc_scene_mf_desfire_app.c +++ /dev/null @@ -1,114 +0,0 @@ -#include "../nfc_i.h" - -#define TAG "NfcSceneMfDesfireApp" - -enum SubmenuIndex { - SubmenuIndexAppInfo, - SubmenuIndexDynamic, // dynamic indexes start here -}; - -MifareDesfireApplication* nfc_scene_mf_desfire_app_get_app(Nfc* nfc) { - uint32_t app_idx = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp) >> - 1; - MifareDesfireApplication* app = nfc->dev->dev_data.mf_df_data.app_head; - for(uint32_t i = 0; i < app_idx && app; i++) { - app = app->next; - } - return app; -} - -void nfc_scene_mf_desfire_app_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); -} - -void nfc_scene_mf_desfire_app_on_enter(void* context) { - Nfc* nfc = context; - Submenu* submenu = nfc->submenu; - MifareDesfireApplication* app = nfc_scene_mf_desfire_app_get_app(nfc); - if(!app) { - popup_set_icon(nfc->popup, 5, 5, &I_WarningDolphin_45x42); - popup_set_header(nfc->popup, "Internal Error!", 55, 12, AlignLeft, AlignBottom); - popup_set_text( - nfc->popup, - "No app selected.\nThis should\nnever happen,\nplease file a bug.", - 55, - 15, - AlignLeft, - AlignTop); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - FURI_LOG_E(TAG, "Bad state. No app selected?"); - return; - } - - text_box_set_font(nfc->text_box, TextBoxFontHex); - - submenu_add_item( - submenu, "App info", SubmenuIndexAppInfo, nfc_scene_mf_desfire_app_submenu_callback, nfc); - - uint16_t cap = NFC_TEXT_STORE_SIZE; - char* buf = nfc->text_store; - int idx = SubmenuIndexDynamic; - for(MifareDesfireFile* file = app->file_head; file; file = file->next) { - int size = snprintf(buf, cap, "File %d", file->id); - if(size < 0 || size >= cap) { - FURI_LOG_W( - TAG, - "Exceeded NFC_TEXT_STORE_SIZE when preparing file id strings; menu truncated"); - break; - } - char* label = buf; - cap -= size + 1; - buf += size + 1; - submenu_add_item(submenu, label, idx++, nfc_scene_mf_desfire_app_submenu_callback, nfc); - } - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); -} - -bool nfc_scene_mf_desfire_app_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp); - - if(event.type == SceneManagerEventTypeCustom) { - MifareDesfireApplication* app = nfc_scene_mf_desfire_app_get_app(nfc); - TextBox* text_box = nfc->text_box; - string_reset(nfc->text_box_store); - if(event.event == SubmenuIndexAppInfo) { - mf_df_cat_application_info(app, nfc->text_box_store); - } else { - uint16_t index = event.event - SubmenuIndexDynamic; - MifareDesfireFile* file = app->file_head; - for(int i = 0; file && i < index; i++) { - file = file->next; - } - if(!file) { - return false; - } - mf_df_cat_file(file, nfc->text_box_store); - } - text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, state | 1); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); - consumed = true; - } else if(event.type == SceneManagerEventTypeBack) { - if(state & 1) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, state & ~1); - consumed = true; - } - } - - return consumed; -} - -void nfc_scene_mf_desfire_app_on_exit(void* context) { - Nfc* nfc = context; - - // Clear views - text_box_reset(nfc->text_box); - string_reset(nfc->text_box_store); - submenu_reset(nfc->submenu); -} diff --git a/applications/nfc/scenes/nfc_scene_mf_desfire_data.c b/applications/nfc/scenes/nfc_scene_mf_desfire_data.c deleted file mode 100644 index 0019a0846a4..00000000000 --- a/applications/nfc/scenes/nfc_scene_mf_desfire_data.c +++ /dev/null @@ -1,107 +0,0 @@ -#include "../nfc_i.h" - -#define TAG "NfcSceneMfDesfireData" - -enum { - MifareDesfireDataStateMenu, - MifareDesfireDataStateItem, // MUST be last, states >= this correspond with submenu index -}; - -enum SubmenuIndex { - SubmenuIndexCardInfo, - SubmenuIndexDynamic, // dynamic indexes start here -}; - -void nfc_scene_mf_desfire_data_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = (Nfc*)context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); -} - -void nfc_scene_mf_desfire_data_on_enter(void* context) { - Nfc* nfc = context; - Submenu* submenu = nfc->submenu; - uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireData); - MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; - - text_box_set_font(nfc->text_box, TextBoxFontHex); - - submenu_add_item( - submenu, - "Card info", - SubmenuIndexCardInfo, - nfc_scene_mf_desfire_data_submenu_callback, - nfc); - - uint16_t cap = NFC_TEXT_STORE_SIZE; - char* buf = nfc->text_store; - int idx = SubmenuIndexDynamic; - for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { - int size = snprintf(buf, cap, "App %02x%02x%02x", app->id[0], app->id[1], app->id[2]); - if(size < 0 || size >= cap) { - FURI_LOG_W( - TAG, "Exceeded NFC_TEXT_STORE_SIZE when preparing app id strings; menu truncated"); - break; - } - char* label = buf; - cap -= size + 1; - buf += size + 1; - submenu_add_item(submenu, label, idx++, nfc_scene_mf_desfire_data_submenu_callback, nfc); - } - - if(state >= MifareDesfireDataStateItem) { - submenu_set_selected_item( - nfc->submenu, state - MifareDesfireDataStateItem + SubmenuIndexDynamic); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfDesfireData, MifareDesfireDataStateMenu); - } - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); -} - -bool nfc_scene_mf_desfire_data_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireData); - MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; - - if(event.type == SceneManagerEventTypeCustom) { - TextBox* text_box = nfc->text_box; - string_reset(nfc->text_box_store); - if(event.event == SubmenuIndexCardInfo) { - mf_df_cat_card_info(data, nfc->text_box_store); - text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); - scene_manager_set_scene_state( - nfc->scene_manager, - NfcSceneMfDesfireData, - MifareDesfireDataStateItem + SubmenuIndexCardInfo); - consumed = true; - } else { - uint16_t index = event.event - SubmenuIndexDynamic; - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfDesfireData, MifareDesfireDataStateItem + index); - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfDesfireApp, index << 1); - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireApp); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - if(state >= MifareDesfireDataStateItem) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfDesfireData, MifareDesfireDataStateMenu); - consumed = true; - } - } - - return consumed; -} - -void nfc_scene_mf_desfire_data_on_exit(void* context) { - Nfc* nfc = context; - - // Clear views - text_box_reset(nfc->text_box); - string_reset(nfc->text_box_store); - submenu_reset(nfc->submenu); -} diff --git a/applications/nfc/scenes/nfc_scene_mf_desfire_menu.c b/applications/nfc/scenes/nfc_scene_mf_desfire_menu.c deleted file mode 100644 index 1e2f2d2f29d..00000000000 --- a/applications/nfc/scenes/nfc_scene_mf_desfire_menu.c +++ /dev/null @@ -1,66 +0,0 @@ -#include "../nfc_i.h" - -enum SubmenuIndex { - SubmenuIndexSave, - SubmenuIndexEmulateUid, - SubmenuIndexInfo, -}; - -void nfc_scene_mf_desfire_menu_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); -} - -void nfc_scene_mf_desfire_menu_on_enter(void* context) { - Nfc* nfc = context; - Submenu* submenu = nfc->submenu; - - submenu_add_item( - submenu, "Save", SubmenuIndexSave, nfc_scene_mf_desfire_menu_submenu_callback, nfc); - submenu_add_item( - submenu, - "Emulate UID", - SubmenuIndexEmulateUid, - nfc_scene_mf_desfire_menu_submenu_callback, - nfc); - submenu_add_item( - submenu, "Info", SubmenuIndexInfo, nfc_scene_mf_desfire_menu_submenu_callback, nfc); - - submenu_set_selected_item( - nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireMenu)); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); -} - -bool nfc_scene_mf_desfire_menu_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexSave) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfDesfireMenu, SubmenuIndexSave); - nfc->dev->format = NfcDeviceSaveFormatMifareDesfire; - // Clear device name - nfc_device_set_name(nfc->dev, ""); - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); - consumed = true; - } else if(event.event == SubmenuIndexEmulateUid) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); - consumed = true; - } else if(event.event == SubmenuIndexInfo) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); - consumed = true; - } - } - - return consumed; -} - -void nfc_scene_mf_desfire_menu_on_exit(void* context) { - Nfc* nfc = context; - - // Clear view - submenu_reset(nfc->submenu); -} diff --git a/applications/nfc/scenes/nfc_scene_mf_desfire_read_success.c b/applications/nfc/scenes/nfc_scene_mf_desfire_read_success.c deleted file mode 100644 index 4827c28513d..00000000000 --- a/applications/nfc/scenes/nfc_scene_mf_desfire_read_success.c +++ /dev/null @@ -1,93 +0,0 @@ -#include "../nfc_i.h" -#include - -void nfc_scene_mf_desfire_read_success_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - Nfc* nfc = context; - - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); - } -} - -void nfc_scene_mf_desfire_read_success_on_enter(void* context) { - Nfc* nfc = context; - - FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; - MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data; - Widget* widget = nfc->widget; - - // Prepare string for data display - string_t temp_str; - string_init_printf(temp_str, "\e#MIFARE DESfire\n"); - string_cat_printf(temp_str, "UID:"); - for(size_t i = 0; i < nfc_data->uid_len; i++) { - string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); - } - - uint32_t bytes_total = 1 << (data->version.sw_storage >> 1); - uint32_t bytes_free = data->free_memory ? data->free_memory->bytes : 0; - string_cat_printf(temp_str, "\n%d", bytes_total); - if(data->version.sw_storage & 1) { - string_push_back(temp_str, '+'); - } - string_cat_printf(temp_str, " bytes, %d bytes free\n", bytes_free); - - uint16_t n_apps = 0; - uint16_t n_files = 0; - for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { - n_apps++; - for(MifareDesfireFile* file = app->file_head; file; file = file->next) { - n_files++; - } - } - string_cat_printf(temp_str, "%d Application", n_apps); - if(n_apps != 1) { - string_push_back(temp_str, 's'); - } - string_cat_printf(temp_str, ", %d file", n_files); - if(n_files != 1) { - string_push_back(temp_str, 's'); - } - - // Add text scroll element - widget_add_text_scroll_element(widget, 0, 0, 128, 52, string_get_cstr(temp_str)); - string_clear(temp_str); - - // Add button elements - widget_add_button_element( - widget, GuiButtonTypeLeft, "Retry", nfc_scene_mf_desfire_read_success_widget_callback, nfc); - widget_add_button_element( - widget, GuiButtonTypeRight, "More", nfc_scene_mf_desfire_read_success_widget_callback, nfc); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); -} - -bool nfc_scene_mf_desfire_read_success_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); - consumed = true; - } else if(event.event == GuiButtonTypeRight) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireMenu); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); - consumed = true; - } - - return consumed; -} - -void nfc_scene_mf_desfire_read_success_on_exit(void* context) { - Nfc* nfc = context; - - // Clean dialog - widget_reset(nfc->widget); -} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_data.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_data.c deleted file mode 100644 index d4184a6b41a..00000000000 --- a/applications/nfc/scenes/nfc_scene_mf_ultralight_data.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "../nfc_i.h" - -void nfc_scene_mf_ultralight_data_on_enter(void* context) { - Nfc* nfc = context; - MfUltralightData* data = &nfc->dev->dev_data.mf_ul_data; - TextBox* text_box = nfc->text_box; - - text_box_set_font(text_box, TextBoxFontHex); - for(uint16_t i = 0; i < data->data_size; i += 2) { - if(!(i % 8) && i) { - string_push_back(nfc->text_box_store, '\n'); - } - string_cat_printf(nfc->text_box_store, "%02X%02X ", data->data[i], data->data[i + 1]); - } - text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); -} - -bool nfc_scene_mf_ultralight_data_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void nfc_scene_mf_ultralight_data_on_exit(void* context) { - Nfc* nfc = context; - - // Clean view - text_box_reset(nfc->text_box); - string_reset(nfc->text_box_store); -} \ No newline at end of file diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_emulate.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_emulate.c deleted file mode 100755 index ce375554126..00000000000 --- a/applications/nfc/scenes/nfc_scene_mf_ultralight_emulate.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "../nfc_i.h" -#include - -#define NFC_MF_UL_DATA_NOT_CHANGED (0UL) -#define NFC_MF_UL_DATA_CHANGED (1UL) - -bool nfc_mf_ultralight_emulate_worker_callback(NfcWorkerEvent event, void* context) { - UNUSED(event); - Nfc* nfc = context; - - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfUltralightEmulate, NFC_MF_UL_DATA_CHANGED); - return true; -} - -void nfc_scene_mf_ultralight_emulate_on_enter(void* context) { - Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcEmulate); - - // Setup view - Popup* popup = nfc->popup; - if(strcmp(nfc->dev->dev_name, "")) { - nfc_text_store_set(nfc, "Emulating\n%s", nfc->dev->dev_name); - } else { - nfc_text_store_set(nfc, "Emulating\nMf Ultralight", nfc->dev->dev_name); - } - popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); - popup_set_header(popup, nfc->text_store, 56, 31, AlignLeft, AlignTop); - - // Setup and start worker - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - nfc_worker_start( - nfc->worker, - NfcWorkerStateMfUltralightEmulate, - &nfc->dev->dev_data, - nfc_mf_ultralight_emulate_worker_callback, - nfc); - nfc_blink_start(nfc); -} - -bool nfc_scene_mf_ultralight_emulate_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeBack) { - // Stop worker - nfc_worker_stop(nfc->worker); - // Check if data changed and save in shadow file - if(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightEmulate) == - NFC_MF_UL_DATA_CHANGED) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfUltralightEmulate, NFC_MF_UL_DATA_NOT_CHANGED); - nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); - } - consumed = false; - } - return consumed; -} - -void nfc_scene_mf_ultralight_emulate_on_exit(void* context) { - Nfc* nfc = context; - - // Clear view - popup_reset(nfc->popup); - - nfc_blink_stop(nfc); -} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_menu.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_menu.c deleted file mode 100644 index ba9f2233887..00000000000 --- a/applications/nfc/scenes/nfc_scene_mf_ultralight_menu.c +++ /dev/null @@ -1,81 +0,0 @@ -#include "../nfc_i.h" - -enum SubmenuIndex { - SubmenuIndexUnlock, - SubmenuIndexSave, - SubmenuIndexEmulate, - SubmenuIndexInfo, -}; - -void nfc_scene_mf_ultralight_menu_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); -} - -void nfc_scene_mf_ultralight_menu_on_enter(void* context) { - Nfc* nfc = context; - Submenu* submenu = nfc->submenu; - MfUltralightData* data = &nfc->dev->dev_data.mf_ul_data; - - if(data->data_read != data->data_size) { - submenu_add_item( - submenu, - "Unlock With Password", - SubmenuIndexUnlock, - nfc_scene_mf_ultralight_menu_submenu_callback, - nfc); - } - submenu_add_item( - submenu, "Save", SubmenuIndexSave, nfc_scene_mf_ultralight_menu_submenu_callback, nfc); - submenu_add_item( - submenu, - "Emulate", - SubmenuIndexEmulate, - nfc_scene_mf_ultralight_menu_submenu_callback, - nfc); - submenu_add_item( - submenu, "Info", SubmenuIndexInfo, nfc_scene_mf_ultralight_menu_submenu_callback, nfc); - - submenu_set_selected_item( - nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightMenu)); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); -} - -bool nfc_scene_mf_ultralight_menu_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexSave) { - nfc->dev->format = NfcDeviceSaveFormatMifareUl; - // Clear device name - nfc_device_set_name(nfc->dev, ""); - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); - consumed = true; - } else if(event.event == SubmenuIndexEmulate) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); - consumed = true; - } else if(event.event == SubmenuIndexUnlock) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); - consumed = true; - } else if(event.event == SubmenuIndexInfo) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); - consumed = true; - } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightMenu, event.event); - - } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_previous_scene(nfc->scene_manager); - } - - return consumed; -} - -void nfc_scene_mf_ultralight_menu_on_exit(void* context) { - Nfc* nfc = context; - - // Clear view - submenu_reset(nfc->submenu); -} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c deleted file mode 100644 index 853ccb05570..00000000000 --- a/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c +++ /dev/null @@ -1,107 +0,0 @@ -#include "../nfc_i.h" -#include - -typedef enum { - NfcSceneMfUlReadStateIdle, - NfcSceneMfUlReadStateDetecting, - NfcSceneMfUlReadStateReading, - NfcSceneMfUlReadStateNotSupportedCard, -} NfcSceneMfUlReadState; - -bool nfc_scene_mf_ultralight_read_auth_worker_callback(NfcWorkerEvent event, void* context) { - Nfc* nfc = context; - - if(event == NfcWorkerEventMfUltralightPassKey) { - memcpy(nfc->dev->dev_data.mf_ul_data.auth_key, nfc->byte_input_store, 4); - } else { - view_dispatcher_send_custom_event(nfc->view_dispatcher, event); - } - return true; -} - -void nfc_scene_mf_ultralight_read_auth_set_state(Nfc* nfc, NfcSceneMfUlReadState state) { - uint32_t curr_state = - scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadAuth); - if(curr_state != state) { - if(state == NfcSceneMfUlReadStateDetecting) { - popup_reset(nfc->popup); - popup_set_text( - nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); - popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual); - } else if(state == NfcSceneMfUlReadStateReading) { - popup_reset(nfc->popup); - popup_set_header( - nfc->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop); - popup_set_icon(nfc->popup, 12, 23, &A_Loading_24); - } else if(state == NfcSceneMfUlReadStateNotSupportedCard) { - popup_reset(nfc->popup); - popup_set_header(nfc->popup, "Wrong type of card!", 64, 3, AlignCenter, AlignTop); - popup_set_text( - nfc->popup, - "Only MIFARE\nUltralight & NTAG\n are supported", - 4, - 22, - AlignLeft, - AlignTop); - popup_set_icon(nfc->popup, 73, 20, &I_DolphinCommon_56x48); - } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadAuth, state); - } -} - -void nfc_scene_mf_ultralight_read_auth_on_enter(void* context) { - Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcRead); - - nfc_device_clear(nfc->dev); - // Setup view - nfc_scene_mf_ultralight_read_auth_set_state(nfc, NfcSceneMfUlReadStateDetecting); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - // Start worker - nfc_worker_start( - nfc->worker, - NfcWorkerStateReadMfUltralightReadAuth, - &nfc->dev->dev_data, - nfc_scene_mf_ultralight_read_auth_worker_callback, - nfc); - - nfc_blink_start(nfc); -} - -bool nfc_scene_mf_ultralight_read_auth_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if((event.event == NfcWorkerEventSuccess) || (event.event == NfcWorkerEventFail)) { - notification_message(nfc->notifications, &sequence_success); - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuthResult); - consumed = true; - } else if(event.event == NfcWorkerEventCardDetected) { - nfc_scene_mf_ultralight_read_auth_set_state(nfc, NfcSceneMfUlReadStateReading); - consumed = true; - } else if(event.event == NfcWorkerEventNoCardDetected) { - nfc_scene_mf_ultralight_read_auth_set_state(nfc, NfcSceneMfUlReadStateDetecting); - consumed = true; - } else if(event.event == NfcWorkerEventWrongCardDetected) { - nfc_scene_mf_ultralight_read_auth_set_state( - nfc, NfcSceneMfUlReadStateNotSupportedCard); - } - } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); - } - return consumed; -} - -void nfc_scene_mf_ultralight_read_auth_on_exit(void* context) { - Nfc* nfc = context; - - // Stop worker - nfc_worker_stop(nfc->worker); - // Clear view - popup_reset(nfc->popup); - nfc_blink_stop(nfc); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfUltralightReadAuth, NfcSceneMfUlReadStateIdle); -} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c deleted file mode 100644 index b9421545596..00000000000 --- a/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c +++ /dev/null @@ -1,98 +0,0 @@ -#include "../nfc_i.h" -#include - -void nfc_scene_mf_ultralight_read_auth_result_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - Nfc* nfc = context; - - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); - } -} - -void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) { - Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - - // Setup dialog view - FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; - MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; - MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(mf_ul_data); - Widget* widget = nfc->widget; - string_t temp_str; - string_init(temp_str); - - if((mf_ul_data->data_read == mf_ul_data->data_size) && (mf_ul_data->data_read > 0)) { - widget_add_string_element( - widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "All pages are unlocked!"); - } else { - widget_add_string_element( - widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Not all pages unlocked!"); - } - string_set_str(temp_str, "UID:"); - for(size_t i = 0; i < nfc_data->uid_len; i++) { - string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); - } - widget_add_string_element( - widget, 0, 17, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); - if(mf_ul_data->auth_success) { - string_printf( - temp_str, - "Password: %02X %02X %02X %02X", - config_pages->auth_data.pwd.raw[0], - config_pages->auth_data.pwd.raw[1], - config_pages->auth_data.pwd.raw[2], - config_pages->auth_data.pwd.raw[3]); - widget_add_string_element( - widget, 0, 28, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); - string_printf( - temp_str, - "PACK: %02X %02X", - config_pages->auth_data.pack.raw[0], - config_pages->auth_data.pack.raw[1]); - widget_add_string_element( - widget, 0, 39, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); - } - string_printf( - temp_str, "Pages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4); - widget_add_string_element( - widget, 0, 50, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); - widget_add_button_element( - widget, - GuiButtonTypeRight, - "Save", - nfc_scene_mf_ultralight_read_auth_result_widget_callback, - nfc); - - string_clear(temp_str); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); -} - -bool nfc_scene_mf_ultralight_read_auth_result_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeRight) { - nfc->dev->format = NfcDeviceSaveFormatMifareUl; - // Clear device name - nfc_device_set_name(nfc->dev, ""); - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); - } - - return consumed; -} - -void nfc_scene_mf_ultralight_read_auth_result_on_exit(void* context) { - Nfc* nfc = context; - - // Clean views - widget_reset(nfc->widget); -} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_read_success.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_success.c deleted file mode 100644 index d775bb71d90..00000000000 --- a/applications/nfc/scenes/nfc_scene_mf_ultralight_read_success.c +++ /dev/null @@ -1,78 +0,0 @@ -#include "../nfc_i.h" -#include - -void nfc_scene_mf_ultralight_read_success_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - Nfc* nfc = context; - - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); - } -} - -void nfc_scene_mf_ultralight_read_success_on_enter(void* context) { - Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - - // Setup widget view - FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; - MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; - Widget* widget = nfc->widget; - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Retry", - nfc_scene_mf_ultralight_read_success_widget_callback, - nfc); - widget_add_button_element( - widget, - GuiButtonTypeRight, - "More", - nfc_scene_mf_ultralight_read_success_widget_callback, - nfc); - - string_t temp_str; - string_init_printf(temp_str, "\e#%s\n", nfc_mf_ul_type(mf_ul_data->type, true)); - string_cat_printf(temp_str, "UID:"); - for(size_t i = 0; i < data->uid_len; i++) { - string_cat_printf(temp_str, " %02X", data->uid[i]); - } - string_cat_printf( - temp_str, "\nPages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4); - if(mf_ul_data->data_read != mf_ul_data->data_size) { - string_cat_printf(temp_str, "\nPassword-protected pages!"); - } - widget_add_text_scroll_element(widget, 0, 0, 128, 52, string_get_cstr(temp_str)); - string_clear(temp_str); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); -} - -bool nfc_scene_mf_ultralight_read_success_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); - consumed = true; - } else if(event.event == GuiButtonTypeRight) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightMenu); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); - consumed = true; - } - - return consumed; -} - -void nfc_scene_mf_ultralight_read_success_on_exit(void* context) { - Nfc* nfc = context; - - // Clean view - widget_reset(nfc->widget); -} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c deleted file mode 100644 index 648aa31dcd7..00000000000 --- a/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "../nfc_i.h" - -enum SubmenuIndex { - SubmenuIndexMfUlUnlockMenuManual, - SubmenuIndexMfUlUnlockMenuAmeebo, - SubmenuIndexMfUlUnlockMenuXiaomi, -}; - -void nfc_scene_mf_ultralight_unlock_menu_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); -} - -void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) { - Nfc* nfc = context; - Submenu* submenu = nfc->submenu; - - uint32_t state = - scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); - submenu_add_item( - submenu, - "Enter Password Manually", - SubmenuIndexMfUlUnlockMenuManual, - nfc_scene_mf_ultralight_unlock_menu_submenu_callback, - nfc); - submenu_add_item( - submenu, - "Auth As Ameebo", - SubmenuIndexMfUlUnlockMenuAmeebo, - nfc_scene_mf_ultralight_unlock_menu_submenu_callback, - nfc); - submenu_add_item( - submenu, - "Auth As Xiaomi", - SubmenuIndexMfUlUnlockMenuXiaomi, - nfc_scene_mf_ultralight_unlock_menu_submenu_callback, - nfc); - submenu_set_selected_item(submenu, state); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); -} - -bool nfc_scene_mf_ultralight_unlock_menu_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexMfUlUnlockMenuManual) { - nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodManual; - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightKeyInput); - consumed = true; - } else if(event.event == SubmenuIndexMfUlUnlockMenuAmeebo) { - nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodAmeebo; - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); - consumed = true; - } else if(event.event == SubmenuIndexMfUlUnlockMenuXiaomi) { - nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodXiaomi; - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); - consumed = true; - } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); - } - return consumed; -} - -void nfc_scene_mf_ultralight_unlock_menu_on_exit(void* context) { - Nfc* nfc = context; - - submenu_reset(nfc->submenu); -} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c deleted file mode 100644 index 58e081db94b..00000000000 --- a/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "../nfc_i.h" - -void nfc_scene_mf_ultralight_unlock_warn_dialog_callback(DialogExResult result, void* context) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); -} - -void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) { - Nfc* nfc = context; - DialogEx* dialog_ex = nfc->dialog_ex; - - dialog_ex_set_context(dialog_ex, nfc); - dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_ultralight_unlock_warn_dialog_callback); - - dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop); - dialog_ex_set_text( - dialog_ex, "Wrong password\ncan block your\ncard.", 4, 18, AlignLeft, AlignTop); - dialog_ex_set_icon(dialog_ex, 73, 20, &I_DolphinCommon_56x48); - dialog_ex_set_center_button_text(dialog_ex, "OK"); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); -} - -bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == DialogExResultCenter) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); - consumed = true; - } - } - - return consumed; -} - -void nfc_scene_mf_ultralight_unlock_warn_on_exit(void* context) { - Nfc* nfc = context; - - dialog_ex_reset(nfc->dialog_ex); - submenu_reset(nfc->submenu); -} diff --git a/applications/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/nfc/scenes/nfc_scene_nfc_data_info.c deleted file mode 100644 index 33f5e44af6e..00000000000 --- a/applications/nfc/scenes/nfc_scene_nfc_data_info.c +++ /dev/null @@ -1,133 +0,0 @@ -#include "../nfc_i.h" - -void nfc_scene_nfc_data_info_widget_callback(GuiButtonType result, InputType type, void* context) { - Nfc* nfc = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); - } -} - -void nfc_scene_nfc_data_info_on_enter(void* context) { - Nfc* nfc = context; - Widget* widget = nfc->widget; - FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; - NfcDeviceData* dev_data = &nfc->dev->dev_data; - NfcProtocol protocol = dev_data->protocol; - uint8_t text_scroll_height = 0; - if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl)) { - widget_add_button_element( - widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc); - text_scroll_height = 52; - } else { - text_scroll_height = 64; - } - - string_t temp_str; - string_init(temp_str); - // Set name if present - if(nfc->dev->dev_name[0] != '\0') { - string_printf(temp_str, "\ec%s\n", nfc->dev->dev_name); - } - - // Set tag type - if(protocol == NfcDeviceProtocolEMV) { - string_cat_printf(temp_str, "\e#EMV Bank Card\n"); - } else if(protocol == NfcDeviceProtocolMifareUl) { - string_cat_printf(temp_str, "\e#%s\n", nfc_mf_ul_type(dev_data->mf_ul_data.type, true)); - } else if(protocol == NfcDeviceProtocolMifareClassic) { - string_cat_printf( - temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type)); - } else if(protocol == NfcDeviceProtocolMifareDesfire) { - string_cat_printf(temp_str, "\e#MIFARE DESfire\n"); - } else { - string_cat_printf(temp_str, "\e#Unknown ISO tag\n"); - } - - // Set tag iso data - char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3'; - string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); - string_cat_printf(temp_str, "UID:"); - for(size_t i = 0; i < nfc_data->uid_len; i++) { - string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); - } - string_cat_printf(temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]); - string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak); - - // Set application specific data - if(protocol == NfcDeviceProtocolMifareDesfire) { - MifareDesfireData* data = &dev_data->mf_df_data; - uint32_t bytes_total = 1 << (data->version.sw_storage >> 1); - uint32_t bytes_free = data->free_memory ? data->free_memory->bytes : 0; - string_cat_printf(temp_str, "\n%d", bytes_total); - if(data->version.sw_storage & 1) { - string_push_back(temp_str, '+'); - } - string_cat_printf(temp_str, " bytes, %d bytes free\n", bytes_free); - - uint16_t n_apps = 0; - uint16_t n_files = 0; - for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { - n_apps++; - for(MifareDesfireFile* file = app->file_head; file; file = file->next) { - n_files++; - } - } - string_cat_printf(temp_str, "%d Application", n_apps); - if(n_apps != 1) { - string_push_back(temp_str, 's'); - } - string_cat_printf(temp_str, ", %d file", n_files); - if(n_files != 1) { - string_push_back(temp_str, 's'); - } - } else if(protocol == NfcDeviceProtocolMifareUl) { - MfUltralightData* data = &dev_data->mf_ul_data; - string_cat_printf( - temp_str, "\nPages Read %d/%d", data->data_read / 4, data->data_size / 4); - if(data->data_size > data->data_read) { - string_cat_printf(temp_str, "\nPassword-protected"); - } - } else if(protocol == NfcDeviceProtocolMifareClassic) { - MfClassicData* data = &dev_data->mf_classic_data; - uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); - uint8_t keys_total = sectors_total * 2; - uint8_t keys_found = 0; - uint8_t sectors_read = 0; - mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); - string_cat_printf(temp_str, "\nKeys Found %d/%d", keys_found, keys_total); - string_cat_printf(temp_str, "\nSectors Read %d/%d", sectors_read, sectors_total); - } - - // Add text scroll widget - widget_add_text_scroll_element( - widget, 0, 0, 128, text_scroll_height, string_get_cstr(temp_str)); - string_clear(temp_str); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); -} - -bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - NfcProtocol protocol = nfc->dev->dev_data.protocol; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeRight) { - if(protocol == NfcDeviceProtocolMifareDesfire) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireApp); - consumed = true; - } else if(protocol == NfcDeviceProtocolMifareUl) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightData); - consumed = true; - } - } - } - - return consumed; -} - -void nfc_scene_nfc_data_info_on_exit(void* context) { - Nfc* nfc = context; - - widget_reset(nfc->widget); -} \ No newline at end of file diff --git a/applications/nfc/scenes/nfc_scene_nfca_menu.c b/applications/nfc/scenes/nfc_scene_nfca_menu.c deleted file mode 100644 index 00d0d943dc0..00000000000 --- a/applications/nfc/scenes/nfc_scene_nfca_menu.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "../nfc_i.h" - -enum SubmenuIndex { - SubmenuIndexSaveUid, - SubmenuIndexEmulateUid, - SubmenuIndexInfo, -}; - -void nfc_scene_nfca_menu_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); -} - -void nfc_scene_nfca_menu_on_enter(void* context) { - Nfc* nfc = context; - Submenu* submenu = nfc->submenu; - - submenu_add_item( - submenu, "Save UID", SubmenuIndexSaveUid, nfc_scene_nfca_menu_submenu_callback, nfc); - submenu_add_item( - submenu, "Emulate UID", SubmenuIndexEmulateUid, nfc_scene_nfca_menu_submenu_callback, nfc); - submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_nfca_menu_submenu_callback, nfc); - - submenu_set_selected_item( - nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcaMenu)); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); -} - -bool nfc_scene_nfca_menu_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexSaveUid) { - nfc->dev->format = NfcDeviceSaveFormatUid; - // Clear device name - nfc_device_set_name(nfc->dev, ""); - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); - consumed = true; - } else if(event.event == SubmenuIndexEmulateUid) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); - consumed = true; - } else if(event.event == SubmenuIndexInfo) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); - consumed = true; - } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcaMenu, event.event); - } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_previous_scene(nfc->scene_manager); - } - - return consumed; -} - -void nfc_scene_nfca_menu_on_exit(void* context) { - Nfc* nfc = context; - - // Clear view - submenu_reset(nfc->submenu); -} diff --git a/applications/nfc/scenes/nfc_scene_nfca_read_success.c b/applications/nfc/scenes/nfc_scene_nfca_read_success.c deleted file mode 100644 index 3467a03b634..00000000000 --- a/applications/nfc/scenes/nfc_scene_nfca_read_success.c +++ /dev/null @@ -1,72 +0,0 @@ -#include "../nfc_i.h" -#include - -void nfc_scene_nfca_read_success_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - furi_assert(context); - Nfc* nfc = context; - - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); - } -} - -void nfc_scene_nfca_read_success_on_enter(void* context) { - Nfc* nfc = context; - - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - - // Setup view - FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; - Widget* widget = nfc->widget; - - string_t temp_str; - string_init_set_str(temp_str, "\e#Unknown ISO tag\n"); - - char iso_type = FURI_BIT(data->sak, 5) ? '4' : '3'; - string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); - string_cat_printf(temp_str, "UID:"); - for(size_t i = 0; i < data->uid_len; i++) { - string_cat_printf(temp_str, " %02X", data->uid[i]); - } - string_cat_printf(temp_str, "\nATQA: %02X %02X ", data->atqa[1], data->atqa[0]); - string_cat_printf(temp_str, " SAK: %02X", data->sak); - - widget_add_text_scroll_element(widget, 0, 0, 128, 52, string_get_cstr(temp_str)); - string_clear(temp_str); - - widget_add_button_element( - widget, GuiButtonTypeLeft, "Retry", nfc_scene_nfca_read_success_widget_callback, nfc); - widget_add_button_element( - widget, GuiButtonTypeRight, "More", nfc_scene_nfca_read_success_widget_callback, nfc); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); -} - -bool nfc_scene_nfca_read_success_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); - consumed = true; - } else if(event.event == GuiButtonTypeRight) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaMenu); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); - consumed = true; - } - return consumed; -} - -void nfc_scene_nfca_read_success_on_exit(void* context) { - Nfc* nfc = context; - - // Clear view - widget_reset(nfc->widget); -} diff --git a/applications/nfc/scenes/nfc_scene_read.c b/applications/nfc/scenes/nfc_scene_read.c deleted file mode 100644 index 00b7c8fac76..00000000000 --- a/applications/nfc/scenes/nfc_scene_read.c +++ /dev/null @@ -1,114 +0,0 @@ -#include "../nfc_i.h" -#include - -typedef enum { - NfcSceneReadStateIdle, - NfcSceneReadStateDetecting, - NfcSceneReadStateReading, -} NfcSceneReadState; - -bool nfc_scene_read_worker_callback(NfcWorkerEvent event, void* context) { - Nfc* nfc = context; - bool consumed = false; - if(event == NfcWorkerEventReadMfClassicLoadKeyCache) { - consumed = nfc_device_load_key_cache(nfc->dev); - } else { - view_dispatcher_send_custom_event(nfc->view_dispatcher, event); - consumed = true; - } - return consumed; -} - -void nfc_scene_read_set_state(Nfc* nfc, NfcSceneReadState state) { - uint32_t curr_state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneRead); - if(curr_state != state) { - if(state == NfcSceneReadStateDetecting) { - popup_reset(nfc->popup); - popup_set_text( - nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); - popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual); - } else if(state == NfcSceneReadStateReading) { - popup_reset(nfc->popup); - popup_set_header( - nfc->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop); - popup_set_icon(nfc->popup, 12, 23, &A_Loading_24); - } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneRead, state); - } -} - -void nfc_scene_read_on_enter(void* context) { - Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcRead); - - nfc_device_clear(nfc->dev); - // Setup view - nfc_scene_read_set_state(nfc, NfcSceneReadStateDetecting); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - // Start worker - nfc_worker_start( - nfc->worker, NfcWorkerStateRead, &nfc->dev->dev_data, nfc_scene_read_worker_callback, nfc); - - nfc_blink_start(nfc); -} - -bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if((event.event == NfcWorkerEventReadUidNfcB) || - (event.event == NfcWorkerEventReadUidNfcF) || - (event.event == NfcWorkerEventReadUidNfcV)) { - notification_message(nfc->notifications, &sequence_success); - scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess); - consumed = true; - } else if(event.event == NfcWorkerEventReadUidNfcA) { - notification_message(nfc->notifications, &sequence_success); - scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess); - consumed = true; - } else if(event.event == NfcWorkerEventReadMfUltralight) { - notification_message(nfc->notifications, &sequence_success); - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); - consumed = true; - } else if(event.event == NfcWorkerEventReadMfClassicDone) { - notification_message(nfc->notifications, &sequence_success); - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); - consumed = true; - } else if(event.event == NfcWorkerEventReadMfDesfire) { - notification_message(nfc->notifications, &sequence_success); - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess); - consumed = true; - } else if(event.event == NfcWorkerEventReadBankCard) { - notification_message(nfc->notifications, &sequence_success); - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmvReadSuccess); - consumed = true; - } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { - if(mf_classic_dict_check_presence(MfClassicDictTypeFlipper)) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicDictAttack); - } else { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); - } - consumed = true; - } else if(event.event == NfcWorkerEventCardDetected) { - nfc_scene_read_set_state(nfc, NfcSceneReadStateReading); - consumed = true; - } else if(event.event == NfcWorkerEventNoCardDetected) { - nfc_scene_read_set_state(nfc, NfcSceneReadStateDetecting); - consumed = true; - } - } - return consumed; -} - -void nfc_scene_read_on_exit(void* context) { - Nfc* nfc = context; - - // Stop worker - nfc_worker_stop(nfc->worker); - // Clear view - popup_reset(nfc->popup); - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneRead, NfcSceneReadStateIdle); - - nfc_blink_stop(nfc); -} diff --git a/applications/nfc/scenes/nfc_scene_read_card_success.c b/applications/nfc/scenes/nfc_scene_read_card_success.c deleted file mode 100755 index 0cb38cbdf43..00000000000 --- a/applications/nfc/scenes/nfc_scene_read_card_success.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "../nfc_i.h" -#include - -void nfc_scene_read_card_success_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - furi_assert(context); - Nfc* nfc = context; - - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); - } -} - -void nfc_scene_read_card_success_on_enter(void* context) { - Nfc* nfc = context; - - string_t temp_str; - string_init(temp_str); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - - // Setup view - FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; - Widget* widget = nfc->widget; - string_set_str(temp_str, nfc_get_dev_type(data->type)); - widget_add_string_element( - widget, 64, 12, AlignCenter, AlignBottom, FontPrimary, string_get_cstr(temp_str)); - string_set_str(temp_str, "UID:"); - for(uint8_t i = 0; i < data->uid_len; i++) { - string_cat_printf(temp_str, " %02X", data->uid[i]); - } - widget_add_string_element( - widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(temp_str)); - widget_add_button_element( - widget, GuiButtonTypeLeft, "Retry", nfc_scene_read_card_success_widget_callback, nfc); - - string_clear(temp_str); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); -} - -bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_previous_scene(nfc->scene_manager); - } - } - return consumed; -} - -void nfc_scene_read_card_success_on_exit(void* context) { - Nfc* nfc = context; - - // Clear view - widget_reset(nfc->widget); -} diff --git a/applications/nfc/scenes/nfc_scene_retry_confirm.c b/applications/nfc/scenes/nfc_scene_retry_confirm.c deleted file mode 100644 index 366582ea80c..00000000000 --- a/applications/nfc/scenes/nfc_scene_retry_confirm.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "../nfc_i.h" - -void nfc_scene_retry_confirm_dialog_callback(DialogExResult result, void* context) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); -} - -void nfc_scene_retry_confirm_on_enter(void* context) { - Nfc* nfc = context; - DialogEx* dialog_ex = nfc->dialog_ex; - - dialog_ex_set_left_button_text(dialog_ex, "Retry"); - 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_set_context(dialog_ex, nfc); - dialog_ex_set_result_callback(dialog_ex, nfc_scene_retry_confirm_dialog_callback); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); -} - -bool nfc_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == DialogExResultRight) { - consumed = scene_manager_previous_scene(nfc->scene_manager); - } else if(event.event == DialogExResultLeft) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneRead); - } - } else if(event.type == SceneManagerEventTypeBack) { - consumed = true; - } - - return consumed; -} - -void nfc_scene_retry_confirm_on_exit(void* context) { - Nfc* nfc = context; - - // Clean view - dialog_ex_reset(nfc->dialog_ex); -} diff --git a/applications/nfc/scenes/nfc_scene_rpc.c b/applications/nfc/scenes/nfc_scene_rpc.c deleted file mode 100644 index 7a9eb450339..00000000000 --- a/applications/nfc/scenes/nfc_scene_rpc.c +++ /dev/null @@ -1,86 +0,0 @@ -#include "../nfc_i.h" - -void nfc_scene_rpc_on_enter(void* context) { - Nfc* nfc = context; - Popup* popup = nfc->popup; - - popup_set_header(popup, "NFC", 89, 42, AlignCenter, AlignBottom); - popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop); - - popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - - notification_message(nfc->notifications, &sequence_display_backlight_on); -} - -static bool nfc_scene_rpc_emulate_callback(NfcWorkerEvent event, void* context) { - UNUSED(event); - Nfc* nfc = context; - - nfc->rpc_state = NfcRpcStateEmulated; - return true; -} - -bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - Popup* popup = nfc->popup; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == NfcCustomEventViewExit) { - rpc_system_app_confirm(nfc->rpc_ctx, RpcAppEventAppExit, true); - scene_manager_stop(nfc->scene_manager); - view_dispatcher_stop(nfc->view_dispatcher); - } else if(event.event == NfcCustomEventRpcSessionClose) { - scene_manager_stop(nfc->scene_manager); - view_dispatcher_stop(nfc->view_dispatcher); - } else if(event.event == NfcCustomEventRpcLoad) { - bool result = false; - const char* arg = rpc_system_app_get_data(nfc->rpc_ctx); - if((arg) && (nfc->rpc_state == NfcRpcStateIdle)) { - if(nfc_device_load(nfc->dev, arg, false)) { - if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { - nfc_worker_start( - nfc->worker, - NfcWorkerStateMfUltralightEmulate, - &nfc->dev->dev_data, - nfc_scene_rpc_emulate_callback, - nfc); - } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { - nfc_worker_start( - nfc->worker, - NfcWorkerStateMfClassicEmulate, - &nfc->dev->dev_data, - nfc_scene_rpc_emulate_callback, - nfc); - } else { - nfc_worker_start( - nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc); - } - nfc->rpc_state = NfcRpcStateEmulating; - result = true; - - nfc_blink_start(nfc); - nfc_text_store_set(nfc, "emulating\n%s", nfc->dev->dev_name); - popup_set_text(popup, nfc->text_store, 89, 44, AlignCenter, AlignTop); - } - } - - rpc_system_app_confirm(nfc->rpc_ctx, RpcAppEventLoadFile, result); - } - } - return consumed; -} - -void nfc_scene_rpc_on_exit(void* context) { - Nfc* nfc = context; - Popup* popup = nfc->popup; - - nfc_blink_stop(nfc); - - 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/nfc/scenes/nfc_scene_save_name.c b/applications/nfc/scenes/nfc_scene_save_name.c deleted file mode 100644 index d5e05472ad7..00000000000 --- a/applications/nfc/scenes/nfc_scene_save_name.c +++ /dev/null @@ -1,86 +0,0 @@ -#include "../nfc_i.h" -#include "m-string.h" -#include -#include -#include - -void nfc_scene_save_name_text_input_callback(void* context) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventTextInputDone); -} - -void nfc_scene_save_name_on_enter(void* context) { - Nfc* nfc = context; - - // Setup view - TextInput* text_input = nfc->text_input; - bool dev_name_empty = false; - if(!strcmp(nfc->dev->dev_name, "")) { - set_random_name(nfc->text_store, sizeof(nfc->text_store)); - dev_name_empty = true; - } else { - nfc_text_store_set(nfc, nfc->dev->dev_name); - } - text_input_set_header_text(text_input, "Name the card"); - text_input_set_result_callback( - text_input, - nfc_scene_save_name_text_input_callback, - nfc, - nfc->text_store, - NFC_DEV_NAME_MAX_LEN, - dev_name_empty); - - string_t folder_path; - string_init(folder_path); - - if(string_end_with_str_p(nfc->dev->load_path, NFC_APP_EXTENSION)) { - path_extract_dirname(string_get_cstr(nfc->dev->load_path), folder_path); - } else { - string_set_str(folder_path, NFC_APP_FOLDER); - } - - ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - string_get_cstr(folder_path), NFC_APP_EXTENSION, nfc->dev->dev_name); - text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput); - - string_clear(folder_path); -} - -bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventTextInputDone) { - if(strcmp(nfc->dev->dev_name, "")) { - nfc_device_delete(nfc->dev, true); - } - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetUid)) { - nfc->dev->dev_data.nfc_data = nfc->dev_edit_data; - } - strlcpy(nfc->dev->dev_name, nfc->text_store, strlen(nfc->text_store) + 1); - if(nfc_device_save(nfc->dev, nfc->text_store)) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); - consumed = true; - } else { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneStart); - } - } - } - return consumed; -} - -void nfc_scene_save_name_on_exit(void* context) { - Nfc* nfc = context; - - // Clear view - void* validator_context = text_input_get_validator_callback_context(nfc->text_input); - text_input_set_validator(nfc->text_input, NULL, NULL); - validator_is_file_free(validator_context); - - text_input_reset(nfc->text_input); -} diff --git a/applications/nfc/scenes/nfc_scene_saved_menu.c b/applications/nfc/scenes/nfc_scene_saved_menu.c deleted file mode 100644 index c7aec5d8713..00000000000 --- a/applications/nfc/scenes/nfc_scene_saved_menu.c +++ /dev/null @@ -1,117 +0,0 @@ -#include "../nfc_i.h" - -enum SubmenuIndex { - SubmenuIndexEmulate, - SubmenuIndexEditUid, - SubmenuIndexRename, - SubmenuIndexDelete, - SubmenuIndexInfo, - SubmenuIndexRestoreOriginal, -}; - -void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); -} - -void nfc_scene_saved_menu_on_enter(void* context) { - Nfc* nfc = context; - Submenu* submenu = nfc->submenu; - - if(nfc->dev->format == NfcDeviceSaveFormatUid || - nfc->dev->format == NfcDeviceSaveFormatMifareDesfire || - nfc->dev->format == NfcDeviceSaveFormatBankCard) { - submenu_add_item( - submenu, - "Emulate UID", - SubmenuIndexEmulate, - nfc_scene_saved_menu_submenu_callback, - nfc); - if(nfc->dev->dev_data.protocol == NfcDeviceProtocolUnknown) { - submenu_add_item( - submenu, - "Edit UID", - SubmenuIndexEditUid, - nfc_scene_saved_menu_submenu_callback, - nfc); - } - } else if( - nfc->dev->format == NfcDeviceSaveFormatMifareUl || - nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { - submenu_add_item( - submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); - } - submenu_add_item( - submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc); - if(nfc->dev->shadow_file_exist) { - submenu_add_item( - submenu, - "Restore to original", - SubmenuIndexRestoreOriginal, - nfc_scene_saved_menu_submenu_callback, - nfc); - } - submenu_add_item( - submenu, "Rename", SubmenuIndexRename, nfc_scene_saved_menu_submenu_callback, nfc); - submenu_add_item( - submenu, "Delete", SubmenuIndexDelete, nfc_scene_saved_menu_submenu_callback, nfc); - submenu_set_selected_item( - nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSavedMenu)); - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); -} - -bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - NfcDeviceData* dev_data = &nfc->dev->dev_data; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, event.event); - if(event.event == SubmenuIndexEmulate) { - if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); - } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); - } else { - scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); - } - consumed = true; - } else if(event.event == SubmenuIndexRename) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); - consumed = true; - } else if(event.event == SubmenuIndexEditUid) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); - consumed = true; - } else if(event.event == SubmenuIndexDelete) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDelete); - consumed = true; - } else if(event.event == SubmenuIndexInfo) { - bool application_info_present = false; - if(dev_data->protocol == NfcDeviceProtocolEMV) { - application_info_present = true; - } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { - application_info_present = nfc_supported_card_verify_and_parse(dev_data); - } - - if(application_info_present) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); - } else { - scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); - } - consumed = true; - } else if(event.event == SubmenuIndexRestoreOriginal) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginalConfirm); - consumed = true; - } - } - - return consumed; -} - -void nfc_scene_saved_menu_on_exit(void* context) { - Nfc* nfc = context; - - submenu_reset(nfc->submenu); -} diff --git a/applications/nfc/scenes/nfc_scene_set_atqa.c b/applications/nfc/scenes/nfc_scene_set_atqa.c deleted file mode 100755 index f2100aa196b..00000000000 --- a/applications/nfc/scenes/nfc_scene_set_atqa.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "../nfc_i.h" - -void nfc_scene_set_atqa_byte_input_callback(void* context) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); -} - -void nfc_scene_set_atqa_on_enter(void* context) { - Nfc* nfc = context; - - // Setup view - ByteInput* byte_input = nfc->byte_input; - byte_input_set_header_text(byte_input, "Enter atqa in hex"); - byte_input_set_result_callback( - byte_input, - nfc_scene_set_atqa_byte_input_callback, - NULL, - nfc, - nfc->dev->dev_data.nfc_data.atqa, - 2); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); -} - -bool nfc_scene_set_atqa_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventByteInputDone) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); - consumed = true; - } - } - return consumed; -} - -void nfc_scene_set_atqa_on_exit(void* context) { - Nfc* nfc = context; - - // Clear view - byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(nfc->byte_input, ""); -} diff --git a/applications/nfc/scenes/nfc_scene_set_sak.c b/applications/nfc/scenes/nfc_scene_set_sak.c deleted file mode 100755 index 3c88f350465..00000000000 --- a/applications/nfc/scenes/nfc_scene_set_sak.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "../nfc_i.h" - -void nfc_scene_set_sak_byte_input_callback(void* context) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); -} - -void nfc_scene_set_sak_on_enter(void* context) { - Nfc* nfc = context; - - // Setup view - ByteInput* byte_input = nfc->byte_input; - byte_input_set_header_text(byte_input, "Enter SAK in hex"); - byte_input_set_result_callback( - byte_input, - nfc_scene_set_sak_byte_input_callback, - NULL, - nfc, - &nfc->dev->dev_data.nfc_data.sak, - 1); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); -} - -bool nfc_scene_set_sak_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventByteInputDone) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSetAtqua); - consumed = true; - } - } - return consumed; -} - -void nfc_scene_set_sak_on_exit(void* context) { - Nfc* nfc = context; - - // Clear view - byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(nfc->byte_input, ""); -} diff --git a/applications/nfc/scenes/nfc_scene_set_type.c b/applications/nfc/scenes/nfc_scene_set_type.c deleted file mode 100644 index ec6d11447ad..00000000000 --- a/applications/nfc/scenes/nfc_scene_set_type.c +++ /dev/null @@ -1,69 +0,0 @@ -#include "../nfc_i.h" -#include "m-string.h" -#include "../helpers/nfc_generators.h" - -enum SubmenuIndex { - SubmenuIndexNFCA4, - SubmenuIndexNFCA7, - SubmenuIndexGeneratorsStart, -}; - -void nfc_scene_set_type_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); -} - -void nfc_scene_set_type_on_enter(void* context) { - Nfc* nfc = context; - Submenu* submenu = nfc->submenu; - // Clear device name - nfc_device_set_name(nfc->dev, ""); - string_set_str(nfc->dev->load_path, ""); - submenu_add_item( - submenu, "NFC-A 7-bytes UID", SubmenuIndexNFCA7, nfc_scene_set_type_submenu_callback, nfc); - submenu_add_item( - submenu, "NFC-A 4-bytes UID", SubmenuIndexNFCA4, nfc_scene_set_type_submenu_callback, nfc); - - // Generators - int i = SubmenuIndexGeneratorsStart; - for(const NfcGenerator* const* generator = nfc_generators; *generator != NULL; - ++generator, ++i) { - submenu_add_item(submenu, (*generator)->name, i, nfc_scene_set_type_submenu_callback, nfc); - } - - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); -} - -bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexNFCA7) { - nfc->dev->dev_data.nfc_data.uid_len = 7; - nfc->dev->format = NfcDeviceSaveFormatUid; - scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak); - consumed = true; - } else if(event.event == SubmenuIndexNFCA4) { - nfc->dev->dev_data.nfc_data.uid_len = 4; - nfc->dev->format = NfcDeviceSaveFormatUid; - scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak); - consumed = true; - } else { - nfc_device_clear(nfc->dev); - nfc->generator = nfc_generators[event.event - SubmenuIndexGeneratorsStart]; - nfc->generator->generator_func(&nfc->dev->dev_data); - - scene_manager_next_scene(nfc->scene_manager, NfcSceneGenerateInfo); - consumed = true; - } - } - return consumed; -} - -void nfc_scene_set_type_on_exit(void* context) { - Nfc* nfc = context; - - submenu_reset(nfc->submenu); -} diff --git a/applications/nfc/scenes/nfc_scene_set_uid.c b/applications/nfc/scenes/nfc_scene_set_uid.c deleted file mode 100755 index 0ff28971011..00000000000 --- a/applications/nfc/scenes/nfc_scene_set_uid.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "../nfc_i.h" -#include - -void nfc_scene_set_uid_byte_input_callback(void* context) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); -} - -void nfc_scene_set_uid_on_enter(void* context) { - Nfc* nfc = context; - - // Setup view - ByteInput* byte_input = nfc->byte_input; - byte_input_set_header_text(byte_input, "Enter uid in hex"); - nfc->dev_edit_data = nfc->dev->dev_data.nfc_data; - byte_input_set_result_callback( - byte_input, - nfc_scene_set_uid_byte_input_callback, - NULL, - nfc, - nfc->dev_edit_data.uid, - nfc->dev_edit_data.uid_len); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); -} - -bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = (Nfc*)context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventByteInputDone) { - DOLPHIN_DEED(DolphinDeedNfcAdd); - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { - nfc->dev->dev_data.nfc_data = nfc->dev_edit_data; - if(nfc_device_save(nfc->dev, nfc->dev->dev_name)) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); - consumed = true; - } - } else { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); - consumed = true; - } - } - } - return consumed; -} - -void nfc_scene_set_uid_on_exit(void* context) { - Nfc* nfc = context; - - // Clear view - byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(nfc->byte_input, ""); -} diff --git a/applications/nfc/scenes/nfc_scene_start.c b/applications/nfc/scenes/nfc_scene_start.c deleted file mode 100644 index 01ffb46b82a..00000000000 --- a/applications/nfc/scenes/nfc_scene_start.c +++ /dev/null @@ -1,77 +0,0 @@ -#include "../nfc_i.h" - -enum SubmenuIndex { - SubmenuIndexRead, - SubmenuIndexDetectReader, - SubmenuIndexSaved, - SubmenuIndexExtraAction, - SubmenuIndexAddManualy, - SubmenuIndexDebug, -}; - -void nfc_scene_start_submenu_callback(void* context, uint32_t index) { - Nfc* nfc = context; - - view_dispatcher_send_custom_event(nfc->view_dispatcher, index); -} - -void nfc_scene_start_on_enter(void* context) { - Nfc* nfc = context; - Submenu* submenu = nfc->submenu; - - submenu_add_item(submenu, "Read", SubmenuIndexRead, nfc_scene_start_submenu_callback, nfc); - submenu_add_item( - submenu, "Detect Reader", SubmenuIndexDetectReader, nfc_scene_start_submenu_callback, nfc); - submenu_add_item(submenu, "Saved", SubmenuIndexSaved, nfc_scene_start_submenu_callback, nfc); - submenu_add_item( - submenu, "Extra Actions", SubmenuIndexExtraAction, nfc_scene_start_submenu_callback, nfc); - submenu_add_item( - submenu, "Add Manually", SubmenuIndexAddManualy, nfc_scene_start_submenu_callback, nfc); - - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - submenu_add_item( - submenu, "Debug", SubmenuIndexDebug, nfc_scene_start_submenu_callback, nfc); - } - - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneStart)); - - nfc_device_clear(nfc->dev); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); -} - -bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { - Nfc* nfc = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexRead) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); - consumed = true; - } else if(event.event == SubmenuIndexDetectReader) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); - consumed = true; - } else if(event.event == SubmenuIndexSaved) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect); - consumed = true; - } else if(event.event == SubmenuIndexExtraAction) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneExtraActions); - consumed = true; - } else if(event.event == SubmenuIndexAddManualy) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType); - consumed = true; - } else if(event.event == SubmenuIndexDebug) { - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug); - scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug); - consumed = true; - } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, event.event); - } - return consumed; -} - -void nfc_scene_start_on_exit(void* context) { - Nfc* nfc = context; - - submenu_reset(nfc->submenu); -} diff --git a/applications/nfc/views/dict_attack.c b/applications/nfc/views/dict_attack.c deleted file mode 100644 index b4674fd3117..00000000000 --- a/applications/nfc/views/dict_attack.c +++ /dev/null @@ -1,231 +0,0 @@ -#include "dict_attack.h" - -#include -#include - -typedef enum { - DictAttackStateRead, - DictAttackStateCardRemoved, -} DictAttackState; - -struct DictAttack { - View* view; - DictAttackCallback callback; - void* context; -}; - -typedef struct { - DictAttackState state; - MfClassicType type; - string_t header; - uint8_t sectors_total; - uint8_t sectors_read; - uint8_t sector_current; - uint8_t keys_total; - uint8_t keys_found; - uint16_t dict_keys_total; - uint16_t dict_keys_current; -} DictAttackViewModel; - -static void dict_attack_draw_callback(Canvas* canvas, void* model) { - DictAttackViewModel* m = model; - if(m->state == DictAttackStateCardRemoved) { - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Lost the tag!"); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned( - canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly."); - } else if(m->state == DictAttackStateRead) { - char draw_str[32] = {}; - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, string_get_cstr(m->header)); - canvas_set_font(canvas, FontSecondary); - float dict_progress = m->dict_keys_total == 0 ? - 0 : - (float)(m->dict_keys_current) / (float)(m->dict_keys_total); - float progress = m->sectors_total == 0 ? 0 : - ((float)(m->sector_current) + dict_progress) / - (float)(m->sectors_total); - if(progress > 1.0) { - progress = 1.0; - } - elements_progress_bar(canvas, 5, 15, 120, progress); - canvas_set_font(canvas, FontSecondary); - snprintf(draw_str, sizeof(draw_str), "Keys found: %d/%d", m->keys_found, m->keys_total); - canvas_draw_str_aligned(canvas, 1, 28, AlignLeft, AlignTop, draw_str); - snprintf( - draw_str, sizeof(draw_str), "Sectors Read: %d/%d", m->sectors_read, m->sectors_total); - canvas_draw_str_aligned(canvas, 1, 40, AlignLeft, AlignTop, draw_str); - } - elements_button_center(canvas, "Skip"); -} - -static bool dict_attack_input_callback(InputEvent* event, void* context) { - DictAttack* dict_attack = context; - bool consumed = false; - if(event->type == InputTypeShort && event->key == InputKeyOk) { - if(dict_attack->callback) { - dict_attack->callback(dict_attack->context); - } - consumed = true; - } - return consumed; -} - -DictAttack* dict_attack_alloc() { - DictAttack* dict_attack = malloc(sizeof(DictAttack)); - dict_attack->view = view_alloc(); - view_allocate_model(dict_attack->view, ViewModelTypeLocking, sizeof(DictAttackViewModel)); - view_set_draw_callback(dict_attack->view, dict_attack_draw_callback); - view_set_input_callback(dict_attack->view, dict_attack_input_callback); - view_set_context(dict_attack->view, dict_attack); - with_view_model( - dict_attack->view, (DictAttackViewModel * model) { - string_init(model->header); - return false; - }); - return dict_attack; -} - -void dict_attack_free(DictAttack* dict_attack) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, (DictAttackViewModel * model) { - string_clear(model->header); - return false; - }); - view_free(dict_attack->view); - free(dict_attack); -} - -void dict_attack_reset(DictAttack* dict_attack) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, (DictAttackViewModel * model) { - model->state = DictAttackStateRead; - model->type = MfClassicType1k; - model->sectors_total = 0; - model->sectors_read = 0; - model->sector_current = 0; - model->keys_total = 0; - model->keys_found = 0; - model->dict_keys_total = 0; - model->dict_keys_current = 0; - string_reset(model->header); - return false; - }); -} - -View* dict_attack_get_view(DictAttack* dict_attack) { - furi_assert(dict_attack); - return dict_attack->view; -} - -void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context) { - furi_assert(dict_attack); - furi_assert(callback); - dict_attack->callback = callback; - dict_attack->context = context; -} - -void dict_attack_set_header(DictAttack* dict_attack, const char* header) { - furi_assert(dict_attack); - furi_assert(header); - - with_view_model( - dict_attack->view, (DictAttackViewModel * model) { - string_set_str(model->header, header); - return true; - }); -} - -void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, (DictAttackViewModel * model) { - model->state = DictAttackStateRead; - model->sectors_total = mf_classic_get_total_sectors_num(type); - model->keys_total = model->sectors_total * 2; - return true; - }); -} - -void dict_attack_set_card_removed(DictAttack* dict_attack) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, (DictAttackViewModel * model) { - model->state = DictAttackStateCardRemoved; - return true; - }); -} - -void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, (DictAttackViewModel * model) { - model->sectors_read = sec_read; - return true; - }); -} - -void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, (DictAttackViewModel * model) { - model->keys_found = keys_found; - return true; - }); -} - -void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, (DictAttackViewModel * model) { - model->sector_current = curr_sec; - model->dict_keys_current = 0; - return true; - }); -} - -void dict_attack_inc_current_sector(DictAttack* dict_attack) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, (DictAttackViewModel * model) { - if(model->sector_current < model->sectors_total) { - model->sector_current++; - model->dict_keys_current = 0; - } - return true; - }); -} - -void dict_attack_inc_keys_found(DictAttack* dict_attack) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, (DictAttackViewModel * model) { - if(model->keys_found < model->keys_total) { - model->keys_found++; - } - return true; - }); -} - -void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, (DictAttackViewModel * model) { - model->dict_keys_total = dict_keys_total; - return true; - }); -} - -void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, (DictAttackViewModel * model) { - if(model->dict_keys_current + keys_tried < model->dict_keys_total) { - model->dict_keys_current += keys_tried; - } - return true; - }); -} diff --git a/applications/nfc/views/dict_attack.h b/applications/nfc/views/dict_attack.h deleted file mode 100644 index 684f17f063e..00000000000 --- a/applications/nfc/views/dict_attack.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once -#include -#include -#include - -#include - -typedef struct DictAttack DictAttack; - -typedef void (*DictAttackCallback)(void* context); - -DictAttack* dict_attack_alloc(); - -void dict_attack_free(DictAttack* dict_attack); - -void dict_attack_reset(DictAttack* dict_attack); - -View* dict_attack_get_view(DictAttack* dict_attack); - -void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context); - -void dict_attack_set_header(DictAttack* dict_attack, const char* header); - -void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type); - -void dict_attack_set_card_removed(DictAttack* dict_attack); - -void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read); - -void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found); - -void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec); - -void dict_attack_inc_current_sector(DictAttack* dict_attack); - -void dict_attack_inc_keys_found(DictAttack* dict_attack); - -void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total); - -void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried); diff --git a/applications/notification/application.fam b/applications/notification/application.fam deleted file mode 100644 index be730d51000..00000000000 --- a/applications/notification/application.fam +++ /dev/null @@ -1,21 +0,0 @@ -App( - appid="notification", - name="NotificationSrv", - apptype=FlipperAppType.SERVICE, - entry_point="notification_srv", - cdefines=["SRV_NOTIFICATION"], - requires=["input"], - provides=["notification_settings"], - stack_size=int(1.5 * 1024), - order=100, -) - -App( - appid="notification_settings", - name="LCD and Notifications", - apptype=FlipperAppType.SETTINGS, - entry_point="notification_settings_app", - requires=["notification"], - stack_size=1 * 1024, - order=20, -) diff --git a/applications/notification/notification_settings_app.c b/applications/notification/notification_settings_app.c deleted file mode 100644 index 894938f4c34..00000000000 --- a/applications/notification/notification_settings_app.c +++ /dev/null @@ -1,199 +0,0 @@ -#include -#include "notification_app.h" -#include -#include -#include - -#define MAX_NOTIFICATION_SETTINGS 4 - -typedef struct { - NotificationApp* notification; - Gui* gui; - ViewDispatcher* view_dispatcher; - VariableItemList* variable_item_list; -} NotificationAppSettings; - -static const NotificationSequence sequence_note_c = { - &message_note_c5, - &message_delay_100, - &message_sound_off, - NULL, -}; - -#define BACKLIGHT_COUNT 5 -const char* const backlight_text[BACKLIGHT_COUNT] = { - "0%", - "25%", - "50%", - "75%", - "100%", -}; -const float backlight_value[BACKLIGHT_COUNT] = { - 0.0f, - 0.25f, - 0.5f, - 0.75f, - 1.0f, -}; - -#define VOLUME_COUNT 5 -const char* const volume_text[VOLUME_COUNT] = { - "0%", - "25%", - "50%", - "75%", - "100%", -}; -const float volume_value[VOLUME_COUNT] = {0.0f, 0.25f, 0.5f, 0.75f, 1.0f}; - -#define DELAY_COUNT 6 -const char* const delay_text[DELAY_COUNT] = { - "1s", - "5s", - "15s", - "30s", - "60s", - "120s", -}; -const uint32_t delay_value[DELAY_COUNT] = {1000, 5000, 15000, 30000, 60000, 120000}; - -#define VIBRO_COUNT 2 -const char* const vibro_text[VIBRO_COUNT] = { - "OFF", - "ON", -}; -const bool vibro_value[VIBRO_COUNT] = {false, true}; - -static void backlight_changed(VariableItem* item) { - NotificationAppSettings* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, backlight_text[index]); - app->notification->settings.display_brightness = backlight_value[index]; - notification_message(app->notification, &sequence_display_backlight_on); -} - -static void screen_changed(VariableItem* item) { - NotificationAppSettings* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, delay_text[index]); - app->notification->settings.display_off_delay_ms = delay_value[index]; - notification_message(app->notification, &sequence_display_backlight_on); -} - -const NotificationMessage apply_message = { - .type = NotificationMessageTypeLedBrightnessSettingApply, -}; -const NotificationSequence apply_sequence = { - &apply_message, - NULL, -}; - -static void led_changed(VariableItem* item) { - NotificationAppSettings* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, backlight_text[index]); - app->notification->settings.led_brightness = backlight_value[index]; - notification_message(app->notification, &apply_sequence); - notification_internal_message(app->notification, &apply_sequence); - notification_message(app->notification, &sequence_blink_white_100); -} - -static void volume_changed(VariableItem* item) { - NotificationAppSettings* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, volume_text[index]); - app->notification->settings.speaker_volume = volume_value[index]; - notification_message(app->notification, &sequence_note_c); -} - -static void vibro_changed(VariableItem* item) { - NotificationAppSettings* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, vibro_text[index]); - app->notification->settings.vibro_on = vibro_value[index]; - notification_message(app->notification, &sequence_single_vibro); -} - -static uint32_t notification_app_settings_exit(void* context) { - UNUSED(context); - return VIEW_NONE; -} - -static NotificationAppSettings* alloc_settings() { - NotificationAppSettings* app = malloc(sizeof(NotificationAppSettings)); - app->notification = furi_record_open(RECORD_NOTIFICATION); - app->gui = furi_record_open(RECORD_GUI); - - app->variable_item_list = variable_item_list_alloc(); - View* view = variable_item_list_get_view(app->variable_item_list); - view_set_previous_callback(view, notification_app_settings_exit); - - VariableItem* item; - uint8_t value_index; - - item = variable_item_list_add( - app->variable_item_list, "LCD Backlight", BACKLIGHT_COUNT, backlight_changed, app); - value_index = value_index_float( - app->notification->settings.display_brightness, backlight_value, BACKLIGHT_COUNT); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, backlight_text[value_index]); - - item = variable_item_list_add( - app->variable_item_list, "Backlight Time", DELAY_COUNT, screen_changed, app); - value_index = value_index_uint32( - app->notification->settings.display_off_delay_ms, delay_value, DELAY_COUNT); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, delay_text[value_index]); - - item = variable_item_list_add( - app->variable_item_list, "LED Brightness", BACKLIGHT_COUNT, led_changed, app); - value_index = value_index_float( - app->notification->settings.led_brightness, backlight_value, BACKLIGHT_COUNT); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, backlight_text[value_index]); - - item = variable_item_list_add( - app->variable_item_list, "Volume", VOLUME_COUNT, volume_changed, app); - value_index = - value_index_float(app->notification->settings.speaker_volume, volume_value, VOLUME_COUNT); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, volume_text[value_index]); - - item = - variable_item_list_add(app->variable_item_list, "Vibro", VIBRO_COUNT, vibro_changed, app); - value_index = value_index_bool(app->notification->settings.vibro_on, vibro_value, VIBRO_COUNT); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, vibro_text[value_index]); - - app->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_enable_queue(app->view_dispatcher); - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - view_dispatcher_add_view(app->view_dispatcher, 0, view); - view_dispatcher_switch_to_view(app->view_dispatcher, 0); - - return app; -} - -static void free_settings(NotificationAppSettings* app) { - view_dispatcher_remove_view(app->view_dispatcher, 0); - variable_item_list_free(app->variable_item_list); - view_dispatcher_free(app->view_dispatcher); - - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); - free(app); -} - -int32_t notification_settings_app(void* p) { - UNUSED(p); - NotificationAppSettings* app = alloc_settings(); - view_dispatcher_run(app->view_dispatcher); - notification_message_save_settings(app->notification); - free_settings(app); - return 0; -} diff --git a/applications/picopass/application.fam b/applications/picopass/application.fam deleted file mode 100644 index 223094250db..00000000000 --- a/applications/picopass/application.fam +++ /dev/null @@ -1,11 +0,0 @@ -App( - appid="picopass", - name="PicoPass Reader", - apptype=FlipperAppType.PLUGIN, - entry_point="picopass_app", - cdefines=["APP_PICOPASS"], - requires=["storage", "gui"], - stack_size=1 * 1024, - icon="A_Plugins_14", - order=30, -) diff --git a/applications/picopass/picopass.c b/applications/picopass/picopass.c deleted file mode 100644 index 8c0db4e2a7f..00000000000 --- a/applications/picopass/picopass.c +++ /dev/null @@ -1,162 +0,0 @@ -#include "picopass_i.h" - -#define TAG "PicoPass" - -bool picopass_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - Picopass* picopass = context; - return scene_manager_handle_custom_event(picopass->scene_manager, event); -} - -bool picopass_back_event_callback(void* context) { - furi_assert(context); - Picopass* picopass = context; - return scene_manager_handle_back_event(picopass->scene_manager); -} - -void picopass_tick_event_callback(void* context) { - furi_assert(context); - Picopass* picopass = context; - scene_manager_handle_tick_event(picopass->scene_manager); -} - -Picopass* picopass_alloc() { - Picopass* picopass = malloc(sizeof(Picopass)); - - picopass->worker = picopass_worker_alloc(); - picopass->view_dispatcher = view_dispatcher_alloc(); - picopass->scene_manager = scene_manager_alloc(&picopass_scene_handlers, picopass); - view_dispatcher_enable_queue(picopass->view_dispatcher); - view_dispatcher_set_event_callback_context(picopass->view_dispatcher, picopass); - view_dispatcher_set_custom_event_callback( - picopass->view_dispatcher, picopass_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - picopass->view_dispatcher, picopass_back_event_callback); - view_dispatcher_set_tick_event_callback( - picopass->view_dispatcher, picopass_tick_event_callback, 100); - - // Picopass device - picopass->dev = picopass_device_alloc(); - - // Open GUI record - picopass->gui = furi_record_open(RECORD_GUI); - view_dispatcher_attach_to_gui( - picopass->view_dispatcher, picopass->gui, ViewDispatcherTypeFullscreen); - - // Open Notification record - picopass->notifications = furi_record_open(RECORD_NOTIFICATION); - - // Submenu - picopass->submenu = submenu_alloc(); - view_dispatcher_add_view( - picopass->view_dispatcher, PicopassViewMenu, submenu_get_view(picopass->submenu)); - - // Popup - picopass->popup = popup_alloc(); - view_dispatcher_add_view( - picopass->view_dispatcher, PicopassViewPopup, popup_get_view(picopass->popup)); - - // Text Input - picopass->text_input = text_input_alloc(); - view_dispatcher_add_view( - picopass->view_dispatcher, - PicopassViewTextInput, - text_input_get_view(picopass->text_input)); - - // Custom Widget - picopass->widget = widget_alloc(); - view_dispatcher_add_view( - picopass->view_dispatcher, PicopassViewWidget, widget_get_view(picopass->widget)); - - return picopass; -} - -void picopass_free(Picopass* picopass) { - furi_assert(picopass); - - // Picopass device - picopass_device_free(picopass->dev); - picopass->dev = NULL; - - // Submenu - view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewMenu); - submenu_free(picopass->submenu); - - // Popup - view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewPopup); - popup_free(picopass->popup); - - // TextInput - view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewTextInput); - text_input_free(picopass->text_input); - - // Custom Widget - view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewWidget); - widget_free(picopass->widget); - - // Worker - picopass_worker_stop(picopass->worker); - picopass_worker_free(picopass->worker); - - // View Dispatcher - view_dispatcher_free(picopass->view_dispatcher); - - // Scene Manager - scene_manager_free(picopass->scene_manager); - - // GUI - furi_record_close(RECORD_GUI); - picopass->gui = NULL; - - // Notifications - furi_record_close(RECORD_NOTIFICATION); - picopass->notifications = NULL; - - free(picopass); -} - -void picopass_text_store_set(Picopass* picopass, const char* text, ...) { - va_list args; - va_start(args, text); - - vsnprintf(picopass->text_store, sizeof(picopass->text_store), text, args); - - va_end(args); -} - -void picopass_text_store_clear(Picopass* picopass) { - memset(picopass->text_store, 0, sizeof(picopass->text_store)); -} - -static const NotificationSequence picopass_sequence_blink_start_blue = { - &message_blink_start_10, - &message_blink_set_color_blue, - &message_do_not_reset, - NULL, -}; - -static const NotificationSequence picopass_sequence_blink_stop = { - &message_blink_stop, - NULL, -}; - -void picopass_blink_start(Picopass* picopass) { - notification_message(picopass->notifications, &picopass_sequence_blink_start_blue); -} - -void picopass_blink_stop(Picopass* picopass) { - notification_message(picopass->notifications, &picopass_sequence_blink_stop); -} - -int32_t picopass_app(void* p) { - UNUSED(p); - Picopass* picopass = picopass_alloc(); - - scene_manager_next_scene(picopass->scene_manager, PicopassSceneStart); - - view_dispatcher_run(picopass->view_dispatcher); - - picopass_free(picopass); - - return 0; -} diff --git a/applications/picopass/picopass.h b/applications/picopass/picopass.h deleted file mode 100644 index a1a87d7f869..00000000000 --- a/applications/picopass/picopass.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -typedef struct Picopass Picopass; diff --git a/applications/picopass/picopass_device.c b/applications/picopass/picopass_device.c deleted file mode 100644 index 9b422edd30f..00000000000 --- a/applications/picopass/picopass_device.c +++ /dev/null @@ -1,154 +0,0 @@ -#include "picopass_device.h" - -#include -#include - -#define TAG "PicopassDevice" - -static const char* picopass_file_header = "Flipper Picopass device"; -static const uint32_t picopass_file_version = 1; - -PicopassDevice* picopass_device_alloc() { - PicopassDevice* picopass_dev = malloc(sizeof(PicopassDevice)); - picopass_dev->dev_data.pacs.legacy = false; - picopass_dev->dev_data.pacs.se_enabled = false; - picopass_dev->dev_data.pacs.pin_length = 0; - picopass_dev->storage = furi_record_open(RECORD_STORAGE); - picopass_dev->dialogs = furi_record_open(RECORD_DIALOGS); - return picopass_dev; -} - -void picopass_device_set_name(PicopassDevice* dev, const char* name) { - furi_assert(dev); - - strlcpy(dev->dev_name, name, PICOPASS_DEV_NAME_MAX_LEN); -} - -static bool picopass_device_save_file( - PicopassDevice* dev, - const char* dev_name, - const char* folder, - const char* extension, - bool use_load_path) { - furi_assert(dev); - - bool saved = false; - FlipperFormat* file = flipper_format_file_alloc(dev->storage); - PicopassPacs* pacs = &dev->dev_data.pacs; - PicopassBlock* AA1 = dev->dev_data.AA1; - string_t temp_str; - string_init(temp_str); - - do { - if(use_load_path && !string_empty_p(dev->load_path)) { - // Get directory name - path_extract_dirname(string_get_cstr(dev->load_path), temp_str); - // Create picopass directory if necessary - if(!storage_simply_mkdir(dev->storage, string_get_cstr(temp_str))) break; - // Make path to file to save - 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 - string_printf(temp_str, "%s/%s%s", folder, dev_name, extension); - } - // Open file - if(!flipper_format_file_open_always(file, string_get_cstr(temp_str))) break; - - if(dev->format == PicopassDeviceSaveFormatHF) { - uint32_t fc = pacs->record.FacilityCode; - uint32_t cn = pacs->record.CardNumber; - // Write header - if(!flipper_format_write_header_cstr(file, picopass_file_header, picopass_file_version)) - break; - if(pacs->record.valid) { - if(!flipper_format_write_uint32(file, "Facility Code", &fc, 1)) break; - if(!flipper_format_write_uint32(file, "Card Number", &cn, 1)) break; - if(!flipper_format_write_hex( - file, "Credential", pacs->credential, PICOPASS_BLOCK_LEN)) - break; - if(pacs->pin_length > 0) { - if(!flipper_format_write_hex(file, "PIN\t\t", pacs->pin0, PICOPASS_BLOCK_LEN)) - break; - if(!flipper_format_write_hex( - file, "PIN(cont.)\t", pacs->pin1, PICOPASS_BLOCK_LEN)) - break; - } - } - if(!flipper_format_write_comment_cstr(file, "Picopass blocks")) break; - bool block_saved = true; - - size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] < PICOPASS_MAX_APP_LIMIT ? - AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] : - PICOPASS_MAX_APP_LIMIT; - for(size_t i = 0; i < app_limit; i++) { - string_printf(temp_str, "Block %d", i); - if(!flipper_format_write_hex( - file, string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) { - block_saved = false; - break; - } - } - if(!block_saved) break; - } else if(dev->format == PicopassDeviceSaveFormatLF) { - const char* lf_header = "Flipper RFID key"; - // Write header - if(!flipper_format_write_header_cstr(file, lf_header, 1)) break; - if(!flipper_format_write_comment_cstr( - file, - "This was generated from the Picopass plugin and may not match current lfrfid")) - break; - // When lfrfid supports more formats, update this - if(!flipper_format_write_string_cstr(file, "Key type", "H10301")) break; - uint8_t H10301[3] = {0}; - H10301[0] = pacs->record.FacilityCode; - H10301[1] = pacs->record.CardNumber >> 8; - H10301[2] = pacs->record.CardNumber & 0x00FF; - if(!flipper_format_write_hex(file, "Data", H10301, 3)) break; - } - saved = true; - } while(0); - - if(!saved) { - dialog_message_show_storage_error(dev->dialogs, "Can not save\nkey file"); - } - string_clear(temp_str); - flipper_format_free(file); - return saved; -} - -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); - } else if(dev->format == PicopassDeviceSaveFormatLF) { - return picopass_device_save_file(dev, dev_name, ANY_PATH("lfrfid"), ".rfid", true); - } - return false; -} - -void picopass_device_clear(PicopassDevice* dev) { - furi_assert(dev); - - picopass_device_data_clear(&dev->dev_data); - memset(&dev->dev_data, 0, sizeof(dev->dev_data)); -} - -void picopass_device_free(PicopassDevice* picopass_dev) { - furi_assert(picopass_dev); - picopass_device_clear(picopass_dev); - furi_record_close(RECORD_STORAGE); - furi_record_close(RECORD_DIALOGS); - string_clear(picopass_dev->load_path); - free(picopass_dev); -} - -void picopass_device_data_clear(PicopassDeviceData* dev_data) { - for(size_t i = 0; i < PICOPASS_MAX_APP_LIMIT; i++) { - memset(dev_data->AA1[i].data, 0, sizeof(dev_data->AA1[i].data)); - } - dev_data->pacs.legacy = false; - dev_data->pacs.se_enabled = false; - dev_data->pacs.pin_length = 0; -} diff --git a/applications/picopass/picopass_device.h b/applications/picopass/picopass_device.h deleted file mode 100644 index 89e031ca7d6..00000000000 --- a/applications/picopass/picopass_device.h +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include - -#define PICOPASS_DEV_NAME_MAX_LEN 22 -#define PICOPASS_READER_DATA_MAX_SIZE 64 -#define PICOPASS_BLOCK_LEN 8 -#define PICOPASS_MAX_APP_LIMIT 32 - -#define PICOPASS_CSN_BLOCK_INDEX 0 -#define PICOPASS_CONFIG_BLOCK_INDEX 1 -#define PICOPASS_AIA_BLOCK_INDEX 5 - -#define PICOPASS_APP_FOLDER ANY_PATH("picopass") -#define PICOPASS_APP_EXTENSION ".picopass" -#define PICOPASS_APP_SHADOW_EXTENSION ".pas" - -typedef enum { - PicopassDeviceEncryptionUnknown = 0, - PicopassDeviceEncryptionNone = 0x14, - PicopassDeviceEncryptionDES = 0x15, - PicopassDeviceEncryption3DES = 0x17, -} PicopassEncryption; - -typedef enum { - PicopassDeviceSaveFormatHF, - PicopassDeviceSaveFormatLF, -} PicopassDeviceSaveFormat; - -typedef struct { - bool valid; - uint8_t bitLength; - uint8_t FacilityCode; - uint16_t CardNumber; -} PicopassWiegandRecord; - -typedef struct { - bool legacy; - bool se_enabled; - bool biometrics; - uint8_t pin_length; - PicopassEncryption encryption; - uint8_t credential[8]; - uint8_t pin0[8]; - uint8_t pin1[8]; - PicopassWiegandRecord record; -} PicopassPacs; - -typedef struct { - uint8_t data[PICOPASS_BLOCK_LEN]; -} PicopassBlock; - -typedef struct { - PicopassBlock AA1[PICOPASS_MAX_APP_LIMIT]; - PicopassPacs pacs; -} PicopassDeviceData; - -typedef struct { - Storage* storage; - DialogsApp* dialogs; - PicopassDeviceData dev_data; - char dev_name[PICOPASS_DEV_NAME_MAX_LEN + 1]; - string_t load_path; - PicopassDeviceSaveFormat format; -} PicopassDevice; - -PicopassDevice* picopass_device_alloc(); - -void picopass_device_free(PicopassDevice* picopass_dev); - -void picopass_device_set_name(PicopassDevice* dev, const char* name); - -bool picopass_device_save(PicopassDevice* dev, const char* dev_name); - -void picopass_device_data_clear(PicopassDeviceData* dev_data); - -void picopass_device_clear(PicopassDevice* dev); diff --git a/applications/picopass/picopass_i.h b/applications/picopass/picopass_i.h deleted file mode 100644 index dbf4f8be582..00000000000 --- a/applications/picopass/picopass_i.h +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once - -#include "picopass.h" -#include "picopass_worker.h" -#include "picopass_device.h" - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include - -#include -#include - -#define PICOPASS_TEXT_STORE_SIZE 128 - -enum PicopassCustomEvent { - // Reserve first 100 events for button types and indexes, starting from 0 - PicopassCustomEventReserved = 100, - - PicopassCustomEventViewExit, - PicopassCustomEventWorkerExit, - PicopassCustomEventByteInputDone, - PicopassCustomEventTextInputDone, -}; - -typedef enum { - EventTypeTick, - EventTypeKey, -} EventType; - -struct Picopass { - PicopassWorker* worker; - ViewDispatcher* view_dispatcher; - Gui* gui; - NotificationApp* notifications; - SceneManager* scene_manager; - PicopassDevice* dev; - - char text_store[PICOPASS_TEXT_STORE_SIZE + 1]; - string_t text_box_store; - - // Common Views - Submenu* submenu; - Popup* popup; - TextInput* text_input; - Widget* widget; -}; - -typedef enum { - PicopassViewMenu, - PicopassViewPopup, - PicopassViewTextInput, - PicopassViewWidget, -} PicopassView; - -Picopass* picopass_alloc(); - -void picopass_text_store_set(Picopass* picopass, const char* text, ...); - -void picopass_text_store_clear(Picopass* picopass); - -void picopass_blink_start(Picopass* picopass); - -void picopass_blink_stop(Picopass* picopass); diff --git a/applications/picopass/picopass_worker.c b/applications/picopass/picopass_worker.c deleted file mode 100644 index 3079a98c4d9..00000000000 --- a/applications/picopass/picopass_worker.c +++ /dev/null @@ -1,324 +0,0 @@ -#include "picopass_worker_i.h" -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#define TAG "PicopassWorker" - -const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78}; -const uint8_t picopass_iclass_decryptionkey[] = - {0xb4, 0x21, 0x2c, 0xca, 0xb7, 0xed, 0x21, 0x0f, 0x7b, 0x93, 0xd4, 0x59, 0x39, 0xc7, 0xdd, 0x36}; - -static void picopass_worker_enable_field() { - st25r3916TxRxOn(); - rfalLowPowerModeStop(); - rfalWorker(); -} - -static ReturnCode picopass_worker_disable_field(ReturnCode rc) { - st25r3916TxRxOff(); - rfalLowPowerModeStart(); - return rc; -} - -static ReturnCode picopass_worker_decrypt(uint8_t* enc_data, uint8_t* dec_data) { - uint8_t key[32] = {0}; - memcpy(key, picopass_iclass_decryptionkey, sizeof(picopass_iclass_decryptionkey)); - mbedtls_des3_context ctx; - mbedtls_des3_init(&ctx); - mbedtls_des3_set2key_dec(&ctx, key); - mbedtls_des3_crypt_ecb(&ctx, enc_data, dec_data); - mbedtls_des3_free(&ctx); - return ERR_NONE; -} - -static ReturnCode picopass_worker_parse_wiegand(uint8_t* data, PicopassWiegandRecord* record) { - uint32_t* halves = (uint32_t*)data; - if(halves[0] == 0) { - uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1])); - record->bitLength = 31 - leading0s; - } else { - uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[0])); - record->bitLength = 63 - leading0s; - } - FURI_LOG_D(TAG, "bitLength: %d", record->bitLength); - - if(record->bitLength == 26) { - uint8_t* v4 = data + 4; - uint32_t bot = v4[3] | (v4[2] << 8) | (v4[1] << 16) | (v4[0] << 24); - - record->CardNumber = (bot >> 1) & 0xFFFF; - record->FacilityCode = (bot >> 17) & 0xFF; - FURI_LOG_D(TAG, "FC:%u CN: %u\n", record->FacilityCode, record->CardNumber); - record->valid = true; - } else { - record->CardNumber = 0; - record->FacilityCode = 0; - record->valid = false; - } - return ERR_NONE; -} - -/***************************** Picopass Worker API *******************************/ - -PicopassWorker* picopass_worker_alloc() { - PicopassWorker* picopass_worker = malloc(sizeof(PicopassWorker)); - - // Worker thread attributes - picopass_worker->thread = furi_thread_alloc(); - furi_thread_set_name(picopass_worker->thread, "PicopassWorker"); - furi_thread_set_stack_size(picopass_worker->thread, 8192); - furi_thread_set_callback(picopass_worker->thread, picopass_worker_task); - furi_thread_set_context(picopass_worker->thread, picopass_worker); - - picopass_worker->callback = NULL; - picopass_worker->context = NULL; - picopass_worker->storage = furi_record_open(RECORD_STORAGE); - - picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady); - - return picopass_worker; -} - -void picopass_worker_free(PicopassWorker* picopass_worker) { - furi_assert(picopass_worker); - - furi_thread_free(picopass_worker->thread); - - furi_record_close(RECORD_STORAGE); - - free(picopass_worker); -} - -PicopassWorkerState picopass_worker_get_state(PicopassWorker* picopass_worker) { - return picopass_worker->state; -} - -void picopass_worker_start( - PicopassWorker* picopass_worker, - PicopassWorkerState state, - PicopassDeviceData* dev_data, - PicopassWorkerCallback callback, - void* context) { - furi_assert(picopass_worker); - furi_assert(dev_data); - - picopass_worker->callback = callback; - picopass_worker->context = context; - picopass_worker->dev_data = dev_data; - picopass_worker_change_state(picopass_worker, state); - furi_thread_start(picopass_worker->thread); -} - -void picopass_worker_stop(PicopassWorker* picopass_worker) { - furi_assert(picopass_worker); - if(picopass_worker->state == PicopassWorkerStateBroken || - picopass_worker->state == PicopassWorkerStateReady) { - return; - } - picopass_worker_disable_field(ERR_NONE); - - picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop); - furi_thread_join(picopass_worker->thread); -} - -void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorkerState state) { - picopass_worker->state = state; -} - -/***************************** Picopass Worker Thread *******************************/ - -ReturnCode picopass_detect_card(int timeout) { - UNUSED(timeout); - - ReturnCode err; - - err = rfalPicoPassPollerInitialize(); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerInitialize error %d", err); - return err; - } - - err = rfalFieldOnAndStartGT(); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalFieldOnAndStartGT error %d", err); - return err; - } - - err = rfalPicoPassPollerCheckPresence(); - if(err != ERR_RF_COLLISION) { - FURI_LOG_E(TAG, "rfalPicoPassPollerCheckPresence error %d", err); - return err; - } - - return ERR_NONE; -} - -ReturnCode picopass_read_card(PicopassBlock* AA1) { - rfalPicoPassIdentifyRes idRes; - rfalPicoPassSelectRes selRes; - rfalPicoPassReadCheckRes rcRes; - rfalPicoPassCheckRes chkRes; - - ReturnCode err; - - uint8_t div_key[8] = {0}; - 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 - - loclass_diversifyKey(selRes.CSN, picopass_iclass_key, div_key); - loclass_opt_doReaderMAC(ccnr, div_key, mac); - - err = rfalPicoPassPollerCheck(mac, &chkRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); - return err; - } - - rfalPicoPassReadBlockRes csn; - err = rfalPicoPassPollerReadBlock(PICOPASS_CSN_BLOCK_INDEX, &csn); - memcpy(AA1[PICOPASS_CSN_BLOCK_INDEX].data, csn.data, sizeof(csn.data)); - - rfalPicoPassReadBlockRes cfg; - err = rfalPicoPassPollerReadBlock(PICOPASS_CONFIG_BLOCK_INDEX, &cfg); - memcpy(AA1[PICOPASS_CONFIG_BLOCK_INDEX].data, cfg.data, sizeof(cfg.data)); - - size_t app_limit = cfg.data[0] < PICOPASS_MAX_APP_LIMIT ? cfg.data[0] : PICOPASS_MAX_APP_LIMIT; - - for(size_t i = 2; i < app_limit; i++) { - FURI_LOG_D(TAG, "rfalPicoPassPollerReadBlock block %d", i); - rfalPicoPassReadBlockRes block; - err = rfalPicoPassPollerReadBlock(i, &block); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerReadBlock error %d", err); - return err; - } - - FURI_LOG_D( - TAG, - "rfalPicoPassPollerReadBlock %d %02x%02x%02x%02x%02x%02x%02x%02x", - i, - block.data[0], - block.data[1], - block.data[2], - block.data[3], - block.data[4], - block.data[5], - block.data[6], - block.data[7]); - - memcpy(AA1[i].data, block.data, sizeof(block.data)); - } - - return ERR_NONE; -} - -int32_t picopass_worker_task(void* context) { - PicopassWorker* picopass_worker = context; - - picopass_worker_enable_field(); - if(picopass_worker->state == PicopassWorkerStateDetect) { - picopass_worker_detect(picopass_worker); - } - picopass_worker_disable_field(ERR_NONE); - - picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady); - - return 0; -} - -void picopass_worker_detect(PicopassWorker* picopass_worker) { - picopass_device_data_clear(picopass_worker->dev_data); - PicopassDeviceData* dev_data = picopass_worker->dev_data; - - PicopassBlock* AA1 = dev_data->AA1; - PicopassPacs* pacs = &dev_data->pacs; - ReturnCode err; - - while(picopass_worker->state == PicopassWorkerStateDetect) { - if(picopass_detect_card(1000) == ERR_NONE) { - // Process first found device - err = picopass_read_card(AA1); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_read_card error %d", err); - } - - // Thank you proxmark! - pacs->legacy = (memcmp(AA1[5].data, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); - pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); - - pacs->biometrics = AA1[6].data[4]; - pacs->pin_length = AA1[6].data[6] & 0x0F; - pacs->encryption = AA1[6].data[7]; - - if(pacs->encryption == PicopassDeviceEncryption3DES) { - FURI_LOG_D(TAG, "3DES Encrypted"); - err = picopass_worker_decrypt(AA1[7].data, pacs->credential); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "decrypt error %d", err); - break; - } - - err = picopass_worker_decrypt(AA1[8].data, pacs->pin0); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "decrypt error %d", err); - break; - } - - err = picopass_worker_decrypt(AA1[9].data, pacs->pin1); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "decrypt error %d", err); - break; - } - } else if(pacs->encryption == PicopassDeviceEncryptionNone) { - FURI_LOG_D(TAG, "No Encryption"); - memcpy(pacs->credential, AA1[7].data, PICOPASS_BLOCK_LEN); - memcpy(pacs->pin0, AA1[8].data, PICOPASS_BLOCK_LEN); - memcpy(pacs->pin1, AA1[9].data, PICOPASS_BLOCK_LEN); - } else if(pacs->encryption == PicopassDeviceEncryptionDES) { - FURI_LOG_D(TAG, "DES Encrypted"); - } else { - FURI_LOG_D(TAG, "Unknown encryption"); - break; - } - - picopass_worker_parse_wiegand(pacs->credential, &pacs->record); - - // Notify caller and exit - if(picopass_worker->callback) { - picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context); - } - break; - } - furi_delay_ms(100); - } -} diff --git a/applications/picopass/picopass_worker.h b/applications/picopass/picopass_worker.h deleted file mode 100755 index 9035f1c8951..00000000000 --- a/applications/picopass/picopass_worker.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include "picopass_device.h" - -typedef struct PicopassWorker PicopassWorker; - -typedef enum { - // Init states - PicopassWorkerStateNone, - PicopassWorkerStateBroken, - PicopassWorkerStateReady, - // Main worker states - PicopassWorkerStateDetect, - // Transition - PicopassWorkerStateStop, -} PicopassWorkerState; - -typedef enum { - // Reserve first 50 events for application events - PicopassWorkerEventReserved = 50, - - // Picopass worker common events - PicopassWorkerEventSuccess, - PicopassWorkerEventFail, - PicopassWorkerEventNoCardDetected, - - PicopassWorkerEventStartReading, -} PicopassWorkerEvent; - -typedef void (*PicopassWorkerCallback)(PicopassWorkerEvent event, void* context); - -PicopassWorker* picopass_worker_alloc(); - -PicopassWorkerState picopass_worker_get_state(PicopassWorker* picopass_worker); - -void picopass_worker_free(PicopassWorker* picopass_worker); - -void picopass_worker_start( - PicopassWorker* picopass_worker, - PicopassWorkerState state, - PicopassDeviceData* dev_data, - PicopassWorkerCallback callback, - void* context); - -void picopass_worker_stop(PicopassWorker* picopass_worker); diff --git a/applications/picopass/picopass_worker_i.h b/applications/picopass/picopass_worker_i.h deleted file mode 100644 index 2610d5e7f06..00000000000 --- a/applications/picopass/picopass_worker_i.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "picopass_worker.h" -#include "picopass_i.h" - -#include -#include - -struct PicopassWorker { - FuriThread* thread; - Storage* storage; - - PicopassDeviceData* dev_data; - PicopassWorkerCallback callback; - void* context; - - PicopassWorkerState state; -}; - -void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorkerState state); - -int32_t picopass_worker_task(void* context); - -void picopass_worker_detect(PicopassWorker* picopass_worker); diff --git a/applications/picopass/scenes/picopass_scene.c b/applications/picopass/scenes/picopass_scene.c deleted file mode 100755 index 61bd5e8fea4..00000000000 --- a/applications/picopass/scenes/picopass_scene.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "picopass_scene.h" - -// Generate scene on_enter handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const picopass_on_enter_handlers[])(void*) = { -#include "picopass_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 picopass_on_event_handlers[])(void* context, SceneManagerEvent event) = { -#include "picopass_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 picopass_on_exit_handlers[])(void* context) = { -#include "picopass_scene_config.h" -}; -#undef ADD_SCENE - -// Initialize scene handlers configuration structure -const SceneManagerHandlers picopass_scene_handlers = { - .on_enter_handlers = picopass_on_enter_handlers, - .on_event_handlers = picopass_on_event_handlers, - .on_exit_handlers = picopass_on_exit_handlers, - .scene_num = PicopassSceneNum, -}; diff --git a/applications/picopass/scenes/picopass_scene.h b/applications/picopass/scenes/picopass_scene.h deleted file mode 100644 index 2faa80b122f..00000000000 --- a/applications/picopass/scenes/picopass_scene.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -// Generate scene id and total number -#define ADD_SCENE(prefix, name, id) PicopassScene##id, -typedef enum { -#include "picopass_scene_config.h" - PicopassSceneNum, -} PicopassScene; -#undef ADD_SCENE - -extern const SceneManagerHandlers picopass_scene_handlers; - -// Generate scene on_enter handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "picopass_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 "picopass_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 "picopass_scene_config.h" -#undef ADD_SCENE diff --git a/applications/picopass/scenes/picopass_scene_card_menu.c b/applications/picopass/scenes/picopass_scene_card_menu.c deleted file mode 100644 index a424b919a74..00000000000 --- a/applications/picopass/scenes/picopass_scene_card_menu.c +++ /dev/null @@ -1,65 +0,0 @@ -#include "../picopass_i.h" - -enum SubmenuIndex { - SubmenuIndexSave, - SubmenuIndexSaveAsLF, -}; - -void picopass_scene_card_menu_submenu_callback(void* context, uint32_t index) { - Picopass* picopass = context; - - view_dispatcher_send_custom_event(picopass->view_dispatcher, index); -} - -void picopass_scene_card_menu_on_enter(void* context) { - Picopass* picopass = context; - Submenu* submenu = picopass->submenu; - - submenu_add_item( - submenu, "Save", SubmenuIndexSave, picopass_scene_card_menu_submenu_callback, picopass); - if(picopass->dev->dev_data.pacs.record.valid) { - submenu_add_item( - submenu, - "Save as LF", - SubmenuIndexSaveAsLF, - picopass_scene_card_menu_submenu_callback, - picopass); - } - submenu_set_selected_item( - picopass->submenu, - scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneCardMenu)); - - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewMenu); -} - -bool picopass_scene_card_menu_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexSave) { - scene_manager_set_scene_state( - picopass->scene_manager, PicopassSceneCardMenu, SubmenuIndexSave); - scene_manager_next_scene(picopass->scene_manager, PicopassSceneSaveName); - picopass->dev->format = PicopassDeviceSaveFormatHF; - consumed = true; - } else if(event.event == SubmenuIndexSaveAsLF) { - scene_manager_set_scene_state( - picopass->scene_manager, PicopassSceneCardMenu, SubmenuIndexSaveAsLF); - picopass->dev->format = PicopassDeviceSaveFormatLF; - scene_manager_next_scene(picopass->scene_manager, PicopassSceneSaveName); - 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_card_menu_on_exit(void* context) { - Picopass* picopass = context; - - submenu_reset(picopass->submenu); -} diff --git a/applications/picopass/scenes/picopass_scene_config.h b/applications/picopass/scenes/picopass_scene_config.h deleted file mode 100755 index 0a3e73f2979..00000000000 --- a/applications/picopass/scenes/picopass_scene_config.h +++ /dev/null @@ -1,7 +0,0 @@ -ADD_SCENE(picopass, start, Start) -ADD_SCENE(picopass, read_card, ReadCard) -ADD_SCENE(picopass, read_card_success, ReadCardSuccess) -ADD_SCENE(picopass, card_menu, CardMenu) -ADD_SCENE(picopass, save_name, SaveName) -ADD_SCENE(picopass, save_success, SaveSuccess) -ADD_SCENE(picopass, saved_menu, SavedMenu) diff --git a/applications/picopass/scenes/picopass_scene_read_card.c b/applications/picopass/scenes/picopass_scene_read_card.c deleted file mode 100644 index 0867898a53d..00000000000 --- a/applications/picopass/scenes/picopass_scene_read_card.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "../picopass_i.h" -#include - -void picopass_read_card_worker_callback(PicopassWorkerEvent event, void* context) { - UNUSED(event); - Picopass* picopass = context; - view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventWorkerExit); -} - -void picopass_scene_read_card_on_enter(void* context) { - Picopass* picopass = context; - DOLPHIN_DEED(DolphinDeedNfcRead); - - // Setup view - Popup* popup = picopass->popup; - popup_set_header(popup, "Detecting\npicopass\ncard", 68, 30, AlignLeft, AlignTop); - popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); - - // Start worker - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup); - picopass_worker_start( - picopass->worker, - PicopassWorkerStateDetect, - &picopass->dev->dev_data, - picopass_read_card_worker_callback, - picopass); - - picopass_blink_start(picopass); -} - -bool picopass_scene_read_card_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, PicopassSceneReadCardSuccess); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeTick) { - consumed = true; - } - return consumed; -} - -void picopass_scene_read_card_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/picopass/scenes/picopass_scene_read_card_success.c b/applications/picopass/scenes/picopass_scene_read_card_success.c deleted file mode 100644 index 3866d201c47..00000000000 --- a/applications/picopass/scenes/picopass_scene_read_card_success.c +++ /dev/null @@ -1,92 +0,0 @@ -#include "../picopass_i.h" -#include - -void picopass_scene_read_card_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_card_success_on_enter(void* context) { - Picopass* picopass = context; - string_t credential_str; - string_t wiegand_str; - string_init(credential_str); - string_init(wiegand_str); - - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - - // Send notification - notification_message(picopass->notifications, &sequence_success); - - // Setup view - PicopassPacs* pacs = &picopass->dev->dev_data.pacs; - Widget* widget = picopass->widget; - - size_t bytesLength = 1 + pacs->record.bitLength / 8; - string_set_str(credential_str, ""); - for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) { - string_cat_printf(credential_str, " %02X", pacs->credential[i]); - } - - if(pacs->record.valid) { - string_cat_printf( - wiegand_str, "FC: %u CN: %u", pacs->record.FacilityCode, pacs->record.CardNumber); - } else { - string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength); - } - - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Retry", - picopass_scene_read_card_success_widget_callback, - picopass); - - widget_add_button_element( - widget, - GuiButtonTypeRight, - "More", - picopass_scene_read_card_success_widget_callback, - picopass); - - widget_add_string_element( - widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, string_get_cstr(wiegand_str)); - widget_add_string_element( - widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(credential_str)); - - string_clear(credential_str); - string_clear(wiegand_str); - - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); -} - -bool picopass_scene_read_card_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 == GuiButtonTypeRight) { - // Clear device name - picopass_device_set_name(picopass->dev, ""); - scene_manager_next_scene(picopass->scene_manager, PicopassSceneCardMenu); - consumed = true; - } - } - return consumed; -} - -void picopass_scene_read_card_success_on_exit(void* context) { - Picopass* picopass = context; - - // Clear view - widget_reset(picopass->widget); -} diff --git a/applications/picopass/scenes/picopass_scene_save_name.c b/applications/picopass/scenes/picopass_scene_save_name.c deleted file mode 100644 index c5fa7dd1f1a..00000000000 --- a/applications/picopass/scenes/picopass_scene_save_name.c +++ /dev/null @@ -1,84 +0,0 @@ -#include "../picopass_i.h" -#include "m-string.h" -#include -#include -#include - -void picopass_scene_save_name_text_input_callback(void* context) { - Picopass* picopass = context; - - view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventTextInputDone); -} - -void picopass_scene_save_name_on_enter(void* context) { - Picopass* picopass = context; - - // Setup view - TextInput* text_input = picopass->text_input; - bool dev_name_empty = false; - if(!strcmp(picopass->dev->dev_name, "")) { - set_random_name(picopass->text_store, sizeof(picopass->text_store)); - dev_name_empty = true; - } else { - picopass_text_store_set(picopass, picopass->dev->dev_name); - } - text_input_set_header_text(text_input, "Name the card"); - text_input_set_result_callback( - text_input, - picopass_scene_save_name_text_input_callback, - picopass, - picopass->text_store, - PICOPASS_DEV_NAME_MAX_LEN, - dev_name_empty); - - string_t folder_path; - string_init(folder_path); - - if(string_end_with_str_p(picopass->dev->load_path, PICOPASS_APP_EXTENSION)) { - path_extract_dirname(string_get_cstr(picopass->dev->load_path), folder_path); - } else { - string_set_str(folder_path, PICOPASS_APP_FOLDER); - } - - ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - string_get_cstr(folder_path), PICOPASS_APP_EXTENSION, picopass->dev->dev_name); - text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); - - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewTextInput); - - string_clear(folder_path); -} - -bool picopass_scene_save_name_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == PicopassCustomEventTextInputDone) { - if(strcmp(picopass->dev->dev_name, "")) { - // picopass_device_delete(picopass->dev, true); - } - strlcpy( - picopass->dev->dev_name, picopass->text_store, strlen(picopass->text_store) + 1); - if(picopass_device_save(picopass->dev, picopass->text_store)) { - scene_manager_next_scene(picopass->scene_manager, PicopassSceneSaveSuccess); - consumed = true; - } else { - consumed = scene_manager_search_and_switch_to_previous_scene( - picopass->scene_manager, PicopassSceneStart); - } - } - } - return consumed; -} - -void picopass_scene_save_name_on_exit(void* context) { - Picopass* picopass = context; - - // Clear view - void* validator_context = text_input_get_validator_callback_context(picopass->text_input); - text_input_set_validator(picopass->text_input, NULL, NULL); - validator_is_file_free(validator_context); - - text_input_reset(picopass->text_input); -} diff --git a/applications/picopass/scenes/picopass_scene_save_success.c b/applications/picopass/scenes/picopass_scene_save_success.c deleted file mode 100644 index e92d91fb440..00000000000 --- a/applications/picopass/scenes/picopass_scene_save_success.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "../picopass_i.h" -#include - -void picopass_scene_save_success_popup_callback(void* context) { - Picopass* picopass = context; - view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventViewExit); -} - -void picopass_scene_save_success_on_enter(void* context) { - Picopass* picopass = context; - DOLPHIN_DEED(DolphinDeedNfcSave); - - // Setup view - Popup* popup = picopass->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, picopass); - popup_set_callback(popup, picopass_scene_save_success_popup_callback); - popup_enable_timeout(popup); - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup); -} - -bool picopass_scene_save_success_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == PicopassCustomEventViewExit) { - if(scene_manager_has_previous_scene(picopass->scene_manager, PicopassSceneCardMenu)) { - consumed = scene_manager_search_and_switch_to_previous_scene( - picopass->scene_manager, PicopassSceneCardMenu); - } else { - consumed = scene_manager_search_and_switch_to_previous_scene( - picopass->scene_manager, PicopassSceneStart); - } - } - } - return consumed; -} - -void picopass_scene_save_success_on_exit(void* context) { - Picopass* picopass = context; - - // Clear view - popup_reset(picopass->popup); -} diff --git a/applications/picopass/scenes/picopass_scene_saved_menu.c b/applications/picopass/scenes/picopass_scene_saved_menu.c deleted file mode 100644 index 232cf26a976..00000000000 --- a/applications/picopass/scenes/picopass_scene_saved_menu.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "../picopass_i.h" - -void picopass_scene_saved_menu_submenu_callback(void* context, uint32_t index) { - Picopass* picopass = context; - - view_dispatcher_send_custom_event(picopass->view_dispatcher, index); -} - -void picopass_scene_saved_menu_on_enter(void* context) { - Picopass* picopass = context; - - submenu_set_selected_item( - picopass->submenu, - scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneSavedMenu)); - - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewMenu); -} - -bool picopass_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state( - picopass->scene_manager, PicopassSceneSavedMenu, event.event); - } - - return consumed; -} - -void picopass_scene_saved_menu_on_exit(void* context) { - Picopass* picopass = context; - - submenu_reset(picopass->submenu); -} diff --git a/applications/picopass/scenes/picopass_scene_start.c b/applications/picopass/scenes/picopass_scene_start.c deleted file mode 100644 index 7f42fb13309..00000000000 --- a/applications/picopass/scenes/picopass_scene_start.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "../picopass_i.h" -enum SubmenuIndex { - SubmenuIndexRead, - SubmenuIndexRunScript, - SubmenuIndexSaved, - SubmenuIndexAddManualy, - SubmenuIndexDebug, -}; - -void picopass_scene_start_submenu_callback(void* context, uint32_t index) { - Picopass* picopass = context; - view_dispatcher_send_custom_event(picopass->view_dispatcher, index); -} -void picopass_scene_start_on_enter(void* context) { - Picopass* picopass = context; - - Submenu* submenu = picopass->submenu; - submenu_add_item( - submenu, "Read Card", SubmenuIndexRead, picopass_scene_start_submenu_callback, picopass); - - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneStart)); - picopass_device_clear(picopass->dev); - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewMenu); -} - -bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexRead) { - scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCard); - consumed = true; - } - scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneStart, event.event); - } - - return consumed; -} - -void picopass_scene_start_on_exit(void* context) { - Picopass* picopass = context; - submenu_reset(picopass->submenu); -} diff --git a/applications/power/application.fam b/applications/power/application.fam deleted file mode 100644 index 1e503749a21..00000000000 --- a/applications/power/application.fam +++ /dev/null @@ -1,53 +0,0 @@ -App( - appid="power", - name="PowerSrv", - apptype=FlipperAppType.SERVICE, - entry_point="power_srv", - cdefines=["SRV_POWER"], - requires=[ - "gui", - "cli", - ], - provides=[ - "power_settings", - "power_start", - ], - stack_size=1 * 1024, - order=110, -) - -App( - appid="power_settings", - name="Power", - apptype=FlipperAppType.SETTINGS, - entry_point="power_settings_app", - requires=[ - "gui", - "power", - ], - flags=["InsomniaSafe"], - stack_size=1 * 1024, - order=40, -) - -App( - appid="power_start", - apptype=FlipperAppType.STARTUP, - entry_point="power_on_system_start", - requires=["power"], - order=80, -) - -App( - appid="battery_test", - name="Battery Test", - apptype=FlipperAppType.DEBUG, - entry_point="battery_test_app", - cdefines=["APP_BATTERY_TEST"], - requires=[ - "gui", - "power", - ], - stack_size=1 * 1024, - order=130, -) diff --git a/applications/power/power_cli.c b/applications/power/power_cli.c deleted file mode 100644 index 6af396318bc..00000000000 --- a/applications/power/power_cli.c +++ /dev/null @@ -1,149 +0,0 @@ -#include "power_cli.h" - -#include -#include -#include -#include - -void power_cli_off(Cli* cli, string_t args) { - UNUSED(cli); - UNUSED(args); - Power* power = furi_record_open(RECORD_POWER); - printf("It's now safe to disconnect USB from your flipper\r\n"); - furi_delay_ms(666); - power_off(power); -} - -void power_cli_reboot(Cli* cli, string_t args) { - UNUSED(cli); - UNUSED(args); - power_reboot(PowerBootModeNormal); -} - -void power_cli_reboot2dfu(Cli* cli, string_t args) { - UNUSED(cli); - UNUSED(args); - power_reboot(PowerBootModeDfu); -} - -static void power_cli_info_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, string_t args) { - UNUSED(cli); - UNUSED(args); - furi_hal_power_info_get(power_cli_info_callback, NULL); -} - -void power_cli_debug(Cli* cli, string_t args) { - UNUSED(cli); - UNUSED(args); - furi_hal_power_dump_state(); -} - -void power_cli_5v(Cli* cli, string_t args) { - UNUSED(cli); - if(!string_cmp(args, "0")) { - furi_hal_power_disable_otg(); - } else if(!string_cmp(args, "1")) { - furi_hal_power_enable_otg(); - } else { - cli_print_usage("power_otg", "<1|0>", string_get_cstr(args)); - } -} - -void power_cli_3v3(Cli* cli, string_t args) { - UNUSED(cli); - if(!string_cmp(args, "0")) { - furi_hal_power_disable_external_3_3v(); - } else if(!string_cmp(args, "1")) { - furi_hal_power_enable_external_3_3v(); - } else { - cli_print_usage("power_ext", "<1|0>", string_get_cstr(args)); - } -} - -static void power_cli_command_print_usage() { - printf("Usage:\r\n"); - printf("power \r\n"); - printf("Cmd list:\r\n"); - - 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"); - } -} - -void power_cli(Cli* cli, string_t args, void* context) { - UNUSED(context); - string_t cmd; - string_init(cmd); - - do { - if(!args_read_string_and_trim(args, cmd)) { - power_cli_command_print_usage(); - break; - } - - if(string_cmp_str(cmd, "off") == 0) { - power_cli_off(cli, args); - break; - } - - if(string_cmp_str(cmd, "reboot") == 0) { - power_cli_reboot(cli, args); - break; - } - - if(string_cmp_str(cmd, "reboot2dfu") == 0) { - power_cli_reboot2dfu(cli, args); - break; - } - - if(string_cmp_str(cmd, "info") == 0) { - power_cli_info(cli, args); - break; - } - - if(string_cmp_str(cmd, "debug") == 0) { - power_cli_debug(cli, args); - break; - } - - if(string_cmp_str(cmd, "5v") == 0) { - power_cli_5v(cli, args); - break; - } - - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - if(string_cmp_str(cmd, "3v3") == 0) { - power_cli_3v3(cli, args); - break; - } - } - - power_cli_command_print_usage(); - } while(false); - - string_clear(cmd); -} - -void power_on_system_start() { -#ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - - cli_add_command(cli, "power", CliCommandFlagParallelSafe, power_cli, NULL); - - furi_record_close(RECORD_CLI); -#else - UNUSED(power_cli); -#endif -} diff --git a/applications/power/power_cli.h b/applications/power/power_cli.h deleted file mode 100644 index ef55d869ca1..00000000000 --- a/applications/power/power_cli.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void power_on_system_start(); diff --git a/applications/power/power_settings_app/views/battery_info.c b/applications/power/power_settings_app/views/battery_info.c deleted file mode 100644 index aa3a1e962f5..00000000000 --- a/applications/power/power_settings_app/views/battery_info.c +++ /dev/null @@ -1,126 +0,0 @@ -#include "battery_info.h" -#include -#include - -struct BatteryInfo { - View* view; -}; - -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); - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, x - 4, y + 16, 24, 6); - canvas_set_color(canvas, ColorBlack); - canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val); -}; - -static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { - char emote[20] = {}; - char header[20] = {}; - char value[20] = {}; - - int32_t drain_current = data->gauge_current * (-1000); - uint32_t charge_current = data->gauge_current * 1000; - - // Draw battery - canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28); - if(charge_current > 0) { - canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14); - } else if(drain_current > 100) { - canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14); - } else if(data->charge < 10) { - canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14); - } else { - canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNormal_29x14); - } - - // Draw bubble - elements_bubble(canvas, 53, 0, 71, 39); - - // Set text - if(charge_current > 0) { - snprintf(emote, sizeof(emote), "%s", "Yummy!"); - snprintf(header, sizeof(header), "%s", "Charging at"); - snprintf( - value, - sizeof(value), - "%ld.%ldV %ldmA", - (uint32_t)(data->vbus_voltage), - (uint32_t)(data->vbus_voltage * 10) % 10, - charge_current); - } else if(drain_current > 0) { - snprintf(emote, sizeof(emote), "%s", drain_current > 100 ? "Oh no!" : "Om-nom-nom!"); - snprintf(header, sizeof(header), "%s", "Consumption is"); - snprintf( - value, sizeof(value), "%ld %s", drain_current, drain_current > 100 ? "mA!" : "mA"); - } else if(charge_current != 0 || drain_current != 0) { - snprintf(header, 20, "..."); - } else { - snprintf(header, sizeof(header), "Charged!"); - } - - canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote); - canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header); - canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value); -}; - -static void battery_info_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - BatteryInfoModel* model = context; - - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - draw_battery(canvas, model, 0, 5); - - char batt_level[10]; - char temperature[10]; - char voltage[10]; - char health[10]; - - snprintf(batt_level, sizeof(batt_level), "%ld%%", (uint32_t)model->charge); - snprintf(temperature, sizeof(temperature), "%ld C", (uint32_t)model->gauge_temperature); - snprintf( - voltage, - sizeof(voltage), - "%ld.%01ld V", - (uint32_t)model->gauge_voltage, - (uint32_t)(model->gauge_voltage * 10) % 10); - snprintf(health, sizeof(health), "%d%%", model->health); - - draw_stat(canvas, 8, 42, &I_Battery_16x16, batt_level); - draw_stat(canvas, 40, 42, &I_Temperature_16x16, temperature); - draw_stat(canvas, 72, 42, &I_Voltage_16x16, voltage); - draw_stat(canvas, 104, 42, &I_Health_16x16, health); -} - -BatteryInfo* battery_info_alloc() { - BatteryInfo* battery_info = malloc(sizeof(BatteryInfo)); - battery_info->view = view_alloc(); - view_set_context(battery_info->view, battery_info); - view_allocate_model(battery_info->view, ViewModelTypeLocking, sizeof(BatteryInfoModel)); - view_set_draw_callback(battery_info->view, battery_info_draw_callback); - - return battery_info; -} - -void battery_info_free(BatteryInfo* battery_info) { - furi_assert(battery_info); - view_free(battery_info->view); - free(battery_info); -} - -View* battery_info_get_view(BatteryInfo* battery_info) { - furi_assert(battery_info); - return battery_info->view; -} - -void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data) { - furi_assert(battery_info); - furi_assert(data); - with_view_model( - battery_info->view, (BatteryInfoModel * model) { - memcpy(model, data, sizeof(BatteryInfoModel)); - return true; - }); -} diff --git a/applications/power/power_settings_app/views/battery_info.h b/applications/power/power_settings_app/views/battery_info.h deleted file mode 100644 index e62aa44fabe..00000000000 --- a/applications/power/power_settings_app/views/battery_info.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include - -typedef struct BatteryInfo BatteryInfo; - -typedef struct { - float vbus_voltage; - float gauge_voltage; - float gauge_current; - float gauge_temperature; - uint8_t charge; - uint8_t health; -} BatteryInfoModel; - -BatteryInfo* battery_info_alloc(); - -void battery_info_free(BatteryInfo* battery_info); - -View* battery_info_get_view(BatteryInfo* battery_info); - -void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data); diff --git a/applications/rpc/application.fam b/applications/rpc/application.fam deleted file mode 100644 index 683396e32cb..00000000000 --- a/applications/rpc/application.fam +++ /dev/null @@ -1,12 +0,0 @@ -App( - appid="rpc", - name="RpcSrv", - apptype=FlipperAppType.SERVICE, - entry_point="rpc_srv", - cdefines=["SRV_RPC"], - requires=[ - "cli", - ], - stack_size=4 * 1024, - order=10, -) diff --git a/applications/rpc/rpc.c b/applications/rpc/rpc.c deleted file mode 100644 index f241a67869d..00000000000 --- a/applications/rpc/rpc.c +++ /dev/null @@ -1,719 +0,0 @@ -#include "rpc_i.h" - -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#define TAG "RpcSrv" - -typedef enum { - RpcEvtNewData = (1 << 0), - RpcEvtDisconnect = (1 << 1), -} RpcEvtFlags; - -#define RPC_ALL_EVENTS (RpcEvtNewData | RpcEvtDisconnect) - -DICT_DEF2(RpcHandlerDict, pb_size_t, M_DEFAULT_OPLIST, RpcHandler, M_POD_OPLIST) - -typedef struct { - RpcSystemAlloc alloc; - RpcSystemFree free; - void* context; -} RpcSystemCallbacks; - -static RpcSystemCallbacks rpc_systems[] = { - { - .alloc = rpc_system_system_alloc, - .free = NULL, - }, - { - .alloc = rpc_system_storage_alloc, - .free = rpc_system_storage_free, - }, - { - .alloc = rpc_system_app_alloc, - .free = rpc_system_app_free, - }, - { - .alloc = rpc_system_gui_alloc, - .free = rpc_system_gui_free, - }, - { - .alloc = rpc_system_gpio_alloc, - .free = NULL, - }}; - -struct RpcSession { - Rpc* rpc; - - FuriThread* thread; - - RpcHandlerDict_t handlers; - StreamBufferHandle_t stream; - PB_Main* decoded_message; - bool terminate; - void** system_contexts; - bool decode_error; - - FuriMutex* callbacks_mutex; - RpcSendBytesCallback send_bytes_callback; - RpcBufferIsEmptyCallback buffer_is_empty_callback; - RpcSessionClosedCallback closed_callback; - RpcSessionTerminatedCallback terminated_callback; - void* context; -}; - -struct Rpc { - FuriMutex* busy_mutex; -}; - -static bool content_callback(pb_istream_t* stream, const pb_field_t* field, void** arg); - -static void rpc_close_session_process(const PB_Main* request, void* context) { - furi_assert(request); - - RpcSession* session = (RpcSession*)context; - furi_assert(session); - - rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); - furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); - if(session->closed_callback) { - session->closed_callback(session->context); - } else { - FURI_LOG_W(TAG, "Session stop isn't processed by transport layer"); - } - furi_mutex_release(session->callbacks_mutex); -} - -static size_t rpc_sprintf_msg_file( - string_t str, - const char* prefix, - const PB_Storage_File* msg_file, - size_t msg_files_size) { - size_t cnt = 0; - - for(size_t i = 0; i < msg_files_size; ++i, ++msg_file) { - string_cat_printf( - str, - "%s[%c] size: %5ld", - prefix, - msg_file->type == PB_Storage_File_FileType_DIR ? 'd' : 'f', - msg_file->size); - - if(msg_file->name) { - string_cat_printf(str, " \'%s\'", msg_file->name); - } - - if(msg_file->data && msg_file->data->size) { - string_cat_printf( - str, - " (%d):\'%.*s%s\'", - msg_file->data->size, - MIN(msg_file->data->size, 30), - msg_file->data->bytes, - msg_file->data->size > 30 ? "..." : ""); - } - - string_cat_printf(str, "\r\n"); - } - - return cnt; -} - -void rpc_print_data(const char* prefix, uint8_t* buffer, size_t size) { - string_t str; - string_init(str); - string_reserve(str, 100 + size * 5); - - string_cat_printf(str, "\r\n%s DEC(%d): {", prefix, size); - for(size_t i = 0; i < size; ++i) { - string_cat_printf(str, "%d, ", buffer[i]); - } - string_cat_printf(str, "}\r\n"); - - printf("%s", string_get_cstr(str)); - string_reset(str); - string_reserve(str, 100 + size * 3); - - string_cat_printf(str, "%s HEX(%d): {", prefix, size); - for(size_t i = 0; i < size; ++i) { - string_cat_printf(str, "%02X", buffer[i]); - } - string_cat_printf(str, "}\r\n\r\n"); - - printf("%s", string_get_cstr(str)); - string_clear(str); -} - -void rpc_print_message(const PB_Main* message) { - string_t str; - string_init(str); - - string_cat_printf( - str, - "PB_Main: {\r\n\tresult: %d cmd_id: %ld (%s)\r\n", - message->command_status, - message->command_id, - message->has_next ? "has_next" : "last"); - switch(message->which_content) { - default: - /* not implemented yet */ - string_cat_printf(str, "\tNOT_IMPLEMENTED (%d) {\r\n", message->which_content); - break; - case PB_Main_stop_session_tag: - string_cat_printf(str, "\tstop_session {\r\n"); - break; - case PB_Main_app_start_request_tag: { - string_cat_printf(str, "\tapp_start {\r\n"); - const char* name = message->content.app_start_request.name; - const char* args = message->content.app_start_request.args; - if(name) { - string_cat_printf(str, "\t\tname: %s\r\n", name); - } - if(args) { - string_cat_printf(str, "\t\targs: %s\r\n", args); - } - break; - } - case PB_Main_app_lock_status_request_tag: { - string_cat_printf(str, "\tapp_lock_status_request {\r\n"); - break; - } - case PB_Main_app_lock_status_response_tag: { - string_cat_printf(str, "\tapp_lock_status_response {\r\n"); - bool lock_status = message->content.app_lock_status_response.locked; - string_cat_printf(str, "\t\tlocked: %s\r\n", lock_status ? "true" : "false"); - break; - } - case PB_Main_storage_md5sum_request_tag: { - string_cat_printf(str, "\tmd5sum_request {\r\n"); - const char* path = message->content.storage_md5sum_request.path; - if(path) { - string_cat_printf(str, "\t\tpath: %s\r\n", path); - } - break; - } - case PB_Main_storage_md5sum_response_tag: { - string_cat_printf(str, "\tmd5sum_response {\r\n"); - const char* path = message->content.storage_md5sum_response.md5sum; - if(path) { - string_cat_printf(str, "\t\tmd5sum: %s\r\n", path); - } - break; - } - case PB_Main_system_ping_request_tag: - string_cat_printf(str, "\tping_request {\r\n"); - break; - case PB_Main_system_ping_response_tag: - string_cat_printf(str, "\tping_response {\r\n"); - break; - case PB_Main_system_device_info_request_tag: - string_cat_printf(str, "\tdevice_info_request {\r\n"); - break; - case PB_Main_system_device_info_response_tag: - string_cat_printf(str, "\tdevice_info_response {\r\n"); - string_cat_printf( - str, - "\t\t%s: %s\r\n", - message->content.system_device_info_response.key, - message->content.system_device_info_response.value); - break; - case PB_Main_storage_mkdir_request_tag: - string_cat_printf(str, "\tmkdir {\r\n"); - break; - case PB_Main_storage_delete_request_tag: { - string_cat_printf(str, "\tdelete {\r\n"); - const char* path = message->content.storage_delete_request.path; - if(path) { - string_cat_printf(str, "\t\tpath: %s\r\n", path); - } - break; - } - case PB_Main_empty_tag: - string_cat_printf(str, "\tempty {\r\n"); - break; - case PB_Main_storage_info_request_tag: { - string_cat_printf(str, "\tinfo_request {\r\n"); - const char* path = message->content.storage_info_request.path; - if(path) { - string_cat_printf(str, "\t\tpath: %s\r\n", path); - } - break; - } - case PB_Main_storage_info_response_tag: { - string_cat_printf(str, "\tinfo_response {\r\n"); - string_cat_printf( - str, "\t\ttotal_space: %lu\r\n", message->content.storage_info_response.total_space); - string_cat_printf( - str, "\t\tfree_space: %lu\r\n", message->content.storage_info_response.free_space); - break; - } - case PB_Main_storage_stat_request_tag: { - string_cat_printf(str, "\tstat_request {\r\n"); - const char* path = message->content.storage_stat_request.path; - if(path) { - string_cat_printf(str, "\t\tpath: %s\r\n", path); - } - break; - } - case PB_Main_storage_stat_response_tag: { - string_cat_printf(str, "\tstat_response {\r\n"); - if(message->content.storage_stat_response.has_file) { - const PB_Storage_File* msg_file = &message->content.storage_stat_response.file; - rpc_sprintf_msg_file(str, "\t\t\t", msg_file, 1); - } - break; - } - case PB_Main_storage_list_request_tag: { - string_cat_printf(str, "\tlist_request {\r\n"); - const char* path = message->content.storage_list_request.path; - if(path) { - string_cat_printf(str, "\t\tpath: %s\r\n", path); - } - break; - } - case PB_Main_storage_read_request_tag: { - string_cat_printf(str, "\tread_request {\r\n"); - const char* path = message->content.storage_read_request.path; - if(path) { - string_cat_printf(str, "\t\tpath: %s\r\n", path); - } - break; - } - case PB_Main_storage_write_request_tag: { - string_cat_printf(str, "\twrite_request {\r\n"); - const char* path = message->content.storage_write_request.path; - if(path) { - string_cat_printf(str, "\t\tpath: %s\r\n", path); - } - if(message->content.storage_write_request.has_file) { - const PB_Storage_File* msg_file = &message->content.storage_write_request.file; - rpc_sprintf_msg_file(str, "\t\t\t", msg_file, 1); - } - break; - } - case PB_Main_storage_read_response_tag: - string_cat_printf(str, "\tread_response {\r\n"); - if(message->content.storage_read_response.has_file) { - const PB_Storage_File* msg_file = &message->content.storage_read_response.file; - rpc_sprintf_msg_file(str, "\t\t\t", msg_file, 1); - } - break; - case PB_Main_storage_list_response_tag: { - const PB_Storage_File* msg_file = message->content.storage_list_response.file; - size_t msg_file_count = message->content.storage_list_response.file_count; - string_cat_printf(str, "\tlist_response {\r\n"); - rpc_sprintf_msg_file(str, "\t\t", msg_file, msg_file_count); - break; - } - case PB_Main_storage_rename_request_tag: { - string_cat_printf(str, "\trename_request {\r\n"); - string_cat_printf( - str, "\t\told_path: %s\r\n", message->content.storage_rename_request.old_path); - string_cat_printf( - str, "\t\tnew_path: %s\r\n", message->content.storage_rename_request.new_path); - break; - } - case PB_Main_gui_start_screen_stream_request_tag: - string_cat_printf(str, "\tstart_screen_stream {\r\n"); - break; - case PB_Main_gui_stop_screen_stream_request_tag: - string_cat_printf(str, "\tstop_screen_stream {\r\n"); - break; - case PB_Main_gui_screen_frame_tag: - string_cat_printf(str, "\tscreen_frame {\r\n"); - break; - case PB_Main_gui_send_input_event_request_tag: - string_cat_printf(str, "\tsend_input_event {\r\n"); - string_cat_printf( - str, "\t\tkey: %d\r\n", message->content.gui_send_input_event_request.key); - string_cat_printf( - str, "\t\type: %d\r\n", message->content.gui_send_input_event_request.type); - break; - case PB_Main_gui_start_virtual_display_request_tag: - string_cat_printf(str, "\tstart_virtual_display {\r\n"); - break; - case PB_Main_gui_stop_virtual_display_request_tag: - string_cat_printf(str, "\tstop_virtual_display {\r\n"); - break; - } - string_cat_printf(str, "\t}\r\n}\r\n"); - printf("%s", string_get_cstr(str)); - - string_clear(str); -} - -void rpc_session_set_context(RpcSession* session, void* context) { - furi_assert(session); - - furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); - session->context = context; - furi_mutex_release(session->callbacks_mutex); -} - -void rpc_session_set_close_callback(RpcSession* session, RpcSessionClosedCallback callback) { - furi_assert(session); - - furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); - session->closed_callback = callback; - furi_mutex_release(session->callbacks_mutex); -} - -void rpc_session_set_send_bytes_callback(RpcSession* session, RpcSendBytesCallback callback) { - furi_assert(session); - - furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); - session->send_bytes_callback = callback; - furi_mutex_release(session->callbacks_mutex); -} - -void rpc_session_set_buffer_is_empty_callback( - RpcSession* session, - RpcBufferIsEmptyCallback callback) { - furi_assert(session); - - furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); - session->buffer_is_empty_callback = callback; - furi_mutex_release(session->callbacks_mutex); -} - -void rpc_session_set_terminated_callback( - RpcSession* session, - RpcSessionTerminatedCallback callback) { - furi_assert(session); - - furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); - session->terminated_callback = callback; - furi_mutex_release(session->callbacks_mutex); -} - -/* 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 - * odd: client sends close request and sends command after. - */ -size_t - rpc_session_feed(RpcSession* session, uint8_t* encoded_bytes, size_t size, TickType_t timeout) { - furi_assert(session); - size_t bytes_sent = xStreamBufferSend(session->stream, encoded_bytes, size, timeout); - - furi_thread_flags_set(furi_thread_get_id(session->thread), RpcEvtNewData); - - return bytes_sent; -} - -size_t rpc_session_get_available_size(RpcSession* session) { - furi_assert(session); - return xStreamBufferSpacesAvailable(session->stream); -} - -bool rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) { - RpcSession* session = istream->state; - furi_assert(session); - furi_assert(istream->bytes_left); - - uint32_t flags = 0; - size_t bytes_received = 0; - - while(1) { - bytes_received += - xStreamBufferReceive(session->stream, buf + bytes_received, count - bytes_received, 0); - if(xStreamBufferIsEmpty(session->stream)) { - if(session->buffer_is_empty_callback) { - session->buffer_is_empty_callback(session->context); - } - } - if(session->decode_error) { - /* never go out till RPC_EVENT_DISCONNECT come */ - bytes_received = 0; - } - if(count == bytes_received) { - break; - } else { - flags = furi_thread_flags_wait(RPC_ALL_EVENTS, FuriFlagWaitAny, FuriWaitForever); - if(flags & RpcEvtDisconnect) { - if(xStreamBufferIsEmpty(session->stream)) { - session->terminate = true; - istream->bytes_left = 0; - bytes_received = 0; - break; - } else { - /* Save disconnect flag and continue reading buffer */ - furi_thread_flags_set(furi_thread_get_id(session->thread), RpcEvtDisconnect); - } - } else if(flags & RpcEvtNewData) { - // Just wake thread up - } - } - } - -#if SRV_RPC_DEBUG - rpc_print_data("INPUT", buf, bytes_received); -#endif - - return (count == bytes_received); -} - -static bool content_callback(pb_istream_t* stream, const pb_field_t* field, void** arg) { - furi_assert(stream); - RpcSession* session = stream->state; - furi_assert(session); - - RpcHandler* handler = RpcHandlerDict_get(session->handlers, field->tag); - - if(handler && handler->decode_submessage) { - handler->decode_submessage(stream, field, arg); - } - - return true; -} - -static int32_t rpc_session_worker(void* context) { - furi_assert(context); - RpcSession* session = (RpcSession*)context; - Rpc* rpc = session->rpc; - - FURI_LOG_D(TAG, "Session started"); - - while(1) { - pb_istream_t istream = { - .callback = rpc_pb_stream_read, - .state = session, - .errmsg = NULL, - .bytes_left = RPC_MAX_MESSAGE_SIZE, /* max incoming message size */ - }; - - bool message_decode_failed = false; - - if(pb_decode_ex(&istream, &PB_Main_msg, session->decoded_message, PB_DECODE_DELIMITED)) { -#if SRV_RPC_DEBUG - FURI_LOG_I(TAG, "INPUT:"); - rpc_print_message(session->decoded_message); -#endif - RpcHandler* handler = - RpcHandlerDict_get(session->handlers, session->decoded_message->which_content); - - if(handler && handler->message_handler) { - furi_check(furi_mutex_acquire(rpc->busy_mutex, FuriWaitForever) == FuriStatusOk); - handler->message_handler(session->decoded_message, handler->context); - furi_check(furi_mutex_release(rpc->busy_mutex) == FuriStatusOk); - } else if(session->decoded_message->which_content == 0) { - /* Receiving zeroes means message is 0-length, which - * is valid for proto3: all fields are filled with default values. - * 0 - is default value for which_content field. - * Mark it as decode error, because there is no content message - * in Main message with tag 0. - */ - message_decode_failed = true; - } else if(!handler && !session->terminate) { - FURI_LOG_E( - TAG, - "Message(%d) decoded, but not implemented", - session->decoded_message->which_content); - rpc_send_and_release_empty( - session, - session->decoded_message->command_id, - PB_CommandStatus_ERROR_NOT_IMPLEMENTED); - } - } else { - message_decode_failed = true; - } - - if(message_decode_failed) { - xStreamBufferReset(session->stream); - if(!session->terminate) { - /* Protobuf can't determine start and end of message. - * Handle this by adding varint at beginning - * of a message (PB_ENCODE_DELIMITED). But decoding fail - * means we can't be sure next bytes are varint for next - * message, so the only way to close session. - * RPC itself can't make decision to close session. It has - * to notify: - * 1) down layer (transport) - * 2) other side (companion app) - * Who are responsible to handle RPC session lifecycle. - * Companion receives 2 messages: ERROR_DECODE and session_closed. - */ - FURI_LOG_E(TAG, "Decode failed, error: \'%.128s\'", PB_GET_ERROR(&istream)); - session->decode_error = true; - rpc_send_and_release_empty(session, 0, PB_CommandStatus_ERROR_DECODE); - furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); - if(session->closed_callback) { - session->closed_callback(session->context); - } - furi_mutex_release(session->callbacks_mutex); - } - } - - pb_release(&PB_Main_msg, session->decoded_message); - - if(session->terminate) { - FURI_LOG_D(TAG, "Session terminated"); - break; - } - } - - return 0; -} - -static void rpc_session_free_callback(FuriThreadState thread_state, void* context) { - furi_assert(context); - - RpcSession* session = (RpcSession*)context; - - if(thread_state == FuriThreadStateStopped) { - for(size_t i = 0; i < COUNT_OF(rpc_systems); ++i) { - if(rpc_systems[i].free) { - rpc_systems[i].free(session->system_contexts[i]); - } - } - free(session->system_contexts); - free(session->decoded_message); - RpcHandlerDict_clear(session->handlers); - vStreamBufferDelete(session->stream); - - furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); - if(session->terminated_callback) { - session->terminated_callback(session->context); - } - furi_mutex_release(session->callbacks_mutex); - - furi_mutex_free(session->callbacks_mutex); - furi_thread_free(session->thread); - free(session); - } -} - -RpcSession* rpc_session_open(Rpc* rpc) { - furi_assert(rpc); - - RpcSession* session = malloc(sizeof(RpcSession)); - session->callbacks_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - session->stream = xStreamBufferCreate(RPC_BUFFER_SIZE, 1); - session->rpc = rpc; - session->terminate = false; - session->decode_error = false; - RpcHandlerDict_init(session->handlers); - - session->decoded_message = malloc(sizeof(PB_Main)); - session->decoded_message->cb_content.funcs.decode = content_callback; - session->decoded_message->cb_content.arg = session; - - session->system_contexts = malloc(COUNT_OF(rpc_systems) * sizeof(void*)); - for(size_t i = 0; i < COUNT_OF(rpc_systems); ++i) { - session->system_contexts[i] = rpc_systems[i].alloc(session); - } - - RpcHandler rpc_handler = { - .message_handler = rpc_close_session_process, - .decode_submessage = NULL, - .context = session, - }; - rpc_add_handler(session, PB_Main_stop_session_tag, &rpc_handler); - - session->thread = furi_thread_alloc(); - furi_thread_set_name(session->thread, "RpcSessionWorker"); - furi_thread_set_stack_size(session->thread, 2048); - furi_thread_set_context(session->thread, session); - furi_thread_set_callback(session->thread, rpc_session_worker); - - furi_thread_set_state_context(session->thread, session); - furi_thread_set_state_callback(session->thread, rpc_session_free_callback); - - furi_thread_start(session->thread); - - return session; -} - -void rpc_session_close(RpcSession* session) { - furi_assert(session); - furi_assert(session->rpc); - - rpc_session_set_send_bytes_callback(session, NULL); - rpc_session_set_close_callback(session, NULL); - rpc_session_set_buffer_is_empty_callback(session, NULL); - furi_thread_flags_set(furi_thread_get_id(session->thread), RpcEvtDisconnect); -} - -int32_t rpc_srv(void* p) { - UNUSED(p); - Rpc* rpc = malloc(sizeof(Rpc)); - - rpc->busy_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - - Cli* cli = furi_record_open(RECORD_CLI); - cli_add_command( - cli, "start_rpc_session", CliCommandFlagParallelSafe, rpc_cli_command_start_session, rpc); - - furi_record_create(RECORD_RPC, rpc); - - return 0; -} - -void rpc_add_handler(RpcSession* session, pb_size_t message_tag, RpcHandler* handler) { - furi_assert(RpcHandlerDict_get(session->handlers, message_tag) == NULL); - - RpcHandlerDict_set_at(session->handlers, message_tag, *handler); -} - -void rpc_send(RpcSession* session, PB_Main* message) { - furi_assert(session); - furi_assert(message); - - pb_ostream_t ostream = PB_OSTREAM_SIZING; - -#if SRV_RPC_DEBUG - FURI_LOG_I(TAG, "OUTPUT:"); - rpc_print_message(message); -#endif - - bool result = pb_encode_ex(&ostream, &PB_Main_msg, message, PB_ENCODE_DELIMITED); - furi_check(result && ostream.bytes_written); - - uint8_t* buffer = malloc(ostream.bytes_written); - ostream = pb_ostream_from_buffer(buffer, ostream.bytes_written); - - pb_encode_ex(&ostream, &PB_Main_msg, message, PB_ENCODE_DELIMITED); - -#if SRV_RPC_DEBUG - rpc_print_data("OUTPUT", buffer, ostream.bytes_written); -#endif - - furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); - if(session->send_bytes_callback) { - session->send_bytes_callback(session->context, buffer, ostream.bytes_written); - } - furi_mutex_release(session->callbacks_mutex); - - free(buffer); -} - -void rpc_send_and_release(RpcSession* session, PB_Main* message) { - rpc_send(session, message); - pb_release(&PB_Main_msg, message); -} - -void rpc_send_and_release_empty(RpcSession* session, uint32_t command_id, PB_CommandStatus status) { - PB_Main message = { - .command_id = command_id, - .command_status = status, - .has_next = false, - .which_content = PB_Main_empty_tag, - }; - rpc_send_and_release(session, &message); - pb_release(&PB_Main_msg, &message); -} diff --git a/applications/rpc/rpc_app.c b/applications/rpc/rpc_app.c deleted file mode 100644 index 555cec8cf03..00000000000 --- a/applications/rpc/rpc_app.c +++ /dev/null @@ -1,317 +0,0 @@ -#include "flipper.pb.h" -#include -#include "rpc_i.h" -#include -#include -#include "rpc_app.h" - -#define TAG "RpcSystemApp" - -struct RpcAppSystem { - RpcSession* session; - RpcAppSystemCallback app_callback; - void* app_context; - PB_Main* state_msg; - - uint32_t last_id; - char* last_data; -}; - -#define RPC_SYSTEM_APP_TEMP_ARGS_SIZE 16 - -static void rpc_system_app_start_process(const PB_Main* request, void* context) { - furi_assert(request); - furi_assert(context); - - furi_assert(request->which_content == PB_Main_app_start_request_tag); - RpcAppSystem* rpc_app = context; - RpcSession* session = rpc_app->session; - furi_assert(session); - char args_temp[RPC_SYSTEM_APP_TEMP_ARGS_SIZE]; - - furi_assert(!rpc_app->last_id); - furi_assert(!rpc_app->last_data); - - FURI_LOG_D(TAG, "StartProcess: id %d", request->command_id); - - PB_CommandStatus result = PB_CommandStatus_ERROR_APP_CANT_START; - - Loader* loader = furi_record_open(RECORD_LOADER); - const char* app_name = request->content.app_start_request.name; - if(app_name) { - const char* app_args = request->content.app_start_request.args; - if(app_args && strcmp(app_args, "RPC") == 0) { - // If app is being started in RPC mode - pass RPC context via args string - snprintf(args_temp, RPC_SYSTEM_APP_TEMP_ARGS_SIZE, "RPC %08lX", (uint32_t)rpc_app); - app_args = args_temp; - } - LoaderStatus status = loader_start(loader, app_name, app_args); - if(status == LoaderStatusErrorAppStarted) { - result = PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED; - } else if(status == LoaderStatusErrorInternal) { - result = PB_CommandStatus_ERROR_APP_CANT_START; - } else if(status == LoaderStatusErrorUnknownApp) { - result = PB_CommandStatus_ERROR_INVALID_PARAMETERS; - } else if(status == LoaderStatusOk) { - result = PB_CommandStatus_OK; - } else { - furi_crash("Programming Error"); - } - } else { - result = PB_CommandStatus_ERROR_INVALID_PARAMETERS; - } - - furi_record_close(RECORD_LOADER); - - FURI_LOG_D(TAG, "StartProcess: response id %d, result %d", request->command_id, result); - rpc_send_and_release_empty(session, request->command_id, result); -} - -static void rpc_system_app_lock_status_process(const PB_Main* request, void* context) { - furi_assert(request); - furi_assert(context); - - furi_assert(request->which_content == PB_Main_app_lock_status_request_tag); - RpcAppSystem* rpc_app = context; - RpcSession* session = rpc_app->session; - furi_assert(session); - - FURI_LOG_D(TAG, "LockStatus"); - - Loader* loader = furi_record_open(RECORD_LOADER); - - PB_Main response = { - .has_next = false, - .command_status = PB_CommandStatus_OK, - .command_id = request->command_id, - .which_content = PB_Main_app_lock_status_response_tag, - }; - - response.content.app_lock_status_response.locked = loader_is_locked(loader); - - furi_record_close(RECORD_LOADER); - - FURI_LOG_D(TAG, "LockStatus: response"); - rpc_send_and_release(session, &response); - pb_release(&PB_Main_msg, &response); -} - -static void rpc_system_app_exit_request(const PB_Main* request, void* context) { - furi_assert(request); - furi_assert(context); - - furi_assert(request->which_content == PB_Main_app_exit_request_tag); - RpcAppSystem* rpc_app = context; - RpcSession* session = rpc_app->session; - furi_assert(session); - - PB_CommandStatus status; - - if(rpc_app->app_callback) { - FURI_LOG_D(TAG, "ExitRequest: id %d", request->command_id); - furi_assert(!rpc_app->last_id); - furi_assert(!rpc_app->last_data); - rpc_app->last_id = request->command_id; - rpc_app->app_callback(RpcAppEventAppExit, rpc_app->app_context); - } else { - status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; - FURI_LOG_E( - TAG, "ExitRequest: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status); - rpc_send_and_release_empty(session, request->command_id, status); - } -} - -static void rpc_system_app_load_file(const PB_Main* request, void* context) { - furi_assert(request); - furi_assert(context); - - furi_assert(request->which_content == PB_Main_app_load_file_request_tag); - RpcAppSystem* rpc_app = context; - RpcSession* session = rpc_app->session; - furi_assert(session); - - PB_CommandStatus status; - if(rpc_app->app_callback) { - FURI_LOG_D(TAG, "LoadFile: id %d", request->command_id); - furi_assert(!rpc_app->last_id); - furi_assert(!rpc_app->last_data); - rpc_app->last_id = request->command_id; - rpc_app->last_data = strdup(request->content.app_load_file_request.path); - rpc_app->app_callback(RpcAppEventLoadFile, rpc_app->app_context); - } else { - status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; - FURI_LOG_E( - TAG, "LoadFile: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status); - rpc_send_and_release_empty(session, request->command_id, status); - } -} - -static void rpc_system_app_button_press(const PB_Main* request, void* context) { - furi_assert(request); - furi_assert(context); - - furi_assert(request->which_content == PB_Main_app_button_press_request_tag); - RpcAppSystem* rpc_app = context; - RpcSession* session = rpc_app->session; - furi_assert(session); - - PB_CommandStatus status; - if(rpc_app->app_callback) { - FURI_LOG_D(TAG, "ButtonPress"); - furi_assert(!rpc_app->last_id); - furi_assert(!rpc_app->last_data); - rpc_app->last_id = request->command_id; - rpc_app->last_data = strdup(request->content.app_button_press_request.args); - rpc_app->app_callback(RpcAppEventButtonPress, rpc_app->app_context); - } else { - status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; - FURI_LOG_E( - TAG, "ButtonPress: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status); - rpc_send_and_release_empty(session, request->command_id, status); - } -} - -static void rpc_system_app_button_release(const PB_Main* request, void* context) { - furi_assert(request); - furi_assert(request->which_content == PB_Main_app_button_release_request_tag); - furi_assert(context); - - RpcAppSystem* rpc_app = context; - RpcSession* session = rpc_app->session; - furi_assert(session); - - PB_CommandStatus status; - if(rpc_app->app_callback) { - FURI_LOG_D(TAG, "ButtonRelease"); - furi_assert(!rpc_app->last_id); - furi_assert(!rpc_app->last_data); - rpc_app->last_id = request->command_id; - rpc_app->app_callback(RpcAppEventButtonRelease, rpc_app->app_context); - } else { - status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; - FURI_LOG_E( - TAG, "ButtonRelease: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status); - rpc_send_and_release_empty(session, request->command_id, status); - } -} - -void rpc_system_app_send_started(RpcAppSystem* rpc_app) { - furi_assert(rpc_app); - RpcSession* session = rpc_app->session; - furi_assert(session); - - rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_STARTED; - - FURI_LOG_D(TAG, "SendStarted"); - rpc_send(session, rpc_app->state_msg); -} - -void rpc_system_app_send_exited(RpcAppSystem* rpc_app) { - furi_assert(rpc_app); - RpcSession* session = rpc_app->session; - furi_assert(session); - - rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_CLOSED; - - FURI_LOG_D(TAG, "SendExit"); - rpc_send(session, rpc_app->state_msg); -} - -const char* rpc_system_app_get_data(RpcAppSystem* rpc_app) { - furi_assert(rpc_app); - furi_assert(rpc_app->last_data); - return rpc_app->last_data; -} - -void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result) { - furi_assert(rpc_app); - RpcSession* session = rpc_app->session; - furi_assert(session); - furi_assert(rpc_app->last_id); - - PB_CommandStatus status = result ? PB_CommandStatus_OK : PB_CommandStatus_ERROR_APP_CMD_ERROR; - - uint32_t last_id = 0; - switch(event) { - case RpcAppEventAppExit: - case RpcAppEventLoadFile: - case RpcAppEventButtonPress: - case RpcAppEventButtonRelease: - last_id = rpc_app->last_id; - rpc_app->last_id = 0; - if(rpc_app->last_data) { - free(rpc_app->last_data); - rpc_app->last_data = NULL; - } - FURI_LOG_D(TAG, "AppConfirm: event %d last_id %d status %d", event, last_id, status); - rpc_send_and_release_empty(session, last_id, status); - break; - default: - furi_crash("RPC App state programming Error"); - break; - } -} - -void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx) { - furi_assert(rpc_app); - - rpc_app->app_callback = callback; - rpc_app->app_context = ctx; -} - -void* rpc_system_app_alloc(RpcSession* session) { - furi_assert(session); - - RpcAppSystem* rpc_app = malloc(sizeof(RpcAppSystem)); - rpc_app->session = session; - - // App exit message - rpc_app->state_msg = malloc(sizeof(PB_Main)); - rpc_app->state_msg->which_content = PB_Main_app_state_response_tag; - rpc_app->state_msg->command_status = PB_CommandStatus_OK; - - RpcHandler rpc_handler = { - .message_handler = NULL, - .decode_submessage = NULL, - .context = rpc_app, - }; - - rpc_handler.message_handler = rpc_system_app_start_process; - rpc_add_handler(session, PB_Main_app_start_request_tag, &rpc_handler); - - rpc_handler.message_handler = rpc_system_app_lock_status_process; - rpc_add_handler(session, PB_Main_app_lock_status_request_tag, &rpc_handler); - - rpc_handler.message_handler = rpc_system_app_exit_request; - rpc_add_handler(session, PB_Main_app_exit_request_tag, &rpc_handler); - - rpc_handler.message_handler = rpc_system_app_load_file; - rpc_add_handler(session, PB_Main_app_load_file_request_tag, &rpc_handler); - - rpc_handler.message_handler = rpc_system_app_button_press; - rpc_add_handler(session, PB_Main_app_button_press_request_tag, &rpc_handler); - - rpc_handler.message_handler = rpc_system_app_button_release; - rpc_add_handler(session, PB_Main_app_button_release_request_tag, &rpc_handler); - - return rpc_app; -} - -void rpc_system_app_free(void* context) { - RpcAppSystem* rpc_app = context; - RpcSession* session = rpc_app->session; - furi_assert(session); - - if(rpc_app->app_callback) { - rpc_app->app_callback(RpcAppEventSessionClose, rpc_app->app_context); - } - - while(rpc_app->app_callback) { - furi_delay_tick(1); - } - - if(rpc_app->last_data) free(rpc_app->last_data); - - free(rpc_app->state_msg); - free(rpc_app); -} diff --git a/applications/rpc/rpc_app.h b/applications/rpc/rpc_app.h deleted file mode 100644 index 635c9f8c648..00000000000 --- a/applications/rpc/rpc_app.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -#include "rpc.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - RpcAppEventSessionClose, - RpcAppEventAppExit, - RpcAppEventLoadFile, - RpcAppEventButtonPress, - RpcAppEventButtonRelease, -} RpcAppSystemEvent; - -typedef void (*RpcAppSystemCallback)(RpcAppSystemEvent event, void* context); - -typedef struct RpcAppSystem RpcAppSystem; - -void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx); - -void rpc_system_app_send_started(RpcAppSystem* rpc_app); - -void rpc_system_app_send_exited(RpcAppSystem* rpc_app); - -const char* rpc_system_app_get_data(RpcAppSystem* rpc_app); - -void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result); - -#ifdef __cplusplus -} -#endif diff --git a/applications/rpc/rpc_cli.c b/applications/rpc/rpc_cli.c deleted file mode 100644 index efc672193d6..00000000000 --- a/applications/rpc/rpc_cli.c +++ /dev/null @@ -1,87 +0,0 @@ -#include -#include -#include -#include -#include - -#define TAG "RpcCli" - -typedef struct { - Cli* cli; - bool session_close_request; - FuriSemaphore* terminate_semaphore; -} CliRpc; - -#define CLI_READ_BUFFER_SIZE 64 - -static void rpc_send_bytes_callback(void* context, uint8_t* bytes, size_t bytes_len) { - furi_assert(context); - furi_assert(bytes); - furi_assert(bytes_len); - CliRpc* cli_rpc = context; - - cli_write(cli_rpc->cli, bytes, bytes_len); -} - -static void rpc_session_close_callback(void* context) { - furi_assert(context); - CliRpc* cli_rpc = context; - - cli_rpc->session_close_request = true; -} - -static void rpc_session_terminated_callback(void* context) { - furi_check(context); - CliRpc* cli_rpc = context; - - furi_semaphore_release(cli_rpc->terminate_semaphore); -} - -void rpc_cli_command_start_session(Cli* cli, string_t args, void* context) { - UNUSED(args); - Rpc* rpc = context; - - uint32_t mem_before = memmgr_get_free_heap(); - FURI_LOG_D(TAG, "Free memory %d", mem_before); - - furi_hal_usb_lock(); - RpcSession* rpc_session = rpc_session_open(rpc); - if(rpc_session == NULL) { - printf("Session start error\r\n"); - furi_hal_usb_unlock(); - return; - } - - CliRpc cli_rpc = {.cli = cli, .session_close_request = false}; - cli_rpc.terminate_semaphore = furi_semaphore_alloc(1, 0); - rpc_session_set_context(rpc_session, &cli_rpc); - rpc_session_set_send_bytes_callback(rpc_session, rpc_send_bytes_callback); - rpc_session_set_close_callback(rpc_session, rpc_session_close_callback); - rpc_session_set_terminated_callback(rpc_session, rpc_session_terminated_callback); - - uint8_t* buffer = malloc(CLI_READ_BUFFER_SIZE); - size_t size_received = 0; - - while(1) { - size_received = cli_read_timeout(cli_rpc.cli, buffer, CLI_READ_BUFFER_SIZE, 50); - if(!cli_is_connected(cli_rpc.cli) || cli_rpc.session_close_request) { - break; - } - - if(size_received) { - size_t fed_bytes = rpc_session_feed(rpc_session, buffer, size_received, 3000); - (void)fed_bytes; - furi_assert(fed_bytes == size_received); - } - } - - rpc_session_close(rpc_session); - - furi_check( - furi_semaphore_acquire(cli_rpc.terminate_semaphore, FuriWaitForever) == FuriStatusOk); - - furi_semaphore_free(cli_rpc.terminate_semaphore); - - free(buffer); - furi_hal_usb_unlock(); -} diff --git a/applications/services/application.fam b/applications/services/application.fam new file mode 100644 index 00000000000..aec49b2312e --- /dev/null +++ b/applications/services/application.fam @@ -0,0 +1,13 @@ +App( + appid="basic_services", + name="Basic services", + apptype=FlipperAppType.METAPACKAGE, + provides=[ + "crypto_start", + "rpc_start", + "bt", + "desktop", + "loader", + "power", + ], +) diff --git a/applications/services/applications.h b/applications/services/applications.h new file mode 100644 index 00000000000..7dbb7063f93 --- /dev/null +++ b/applications/services/applications.h @@ -0,0 +1,72 @@ +#pragma once + +#include +#include + +typedef enum { + FlipperInternalApplicationFlagDefault = 0, + FlipperInternalApplicationFlagInsomniaSafe = (1 << 0), +} FlipperInternalApplicationFlag; + +typedef struct { + const FuriThreadCallback app; + const char* name; + const char* appid; + const size_t stack_size; + const Icon* icon; + const FlipperInternalApplicationFlag flags; +} FlipperInternalApplication; + +typedef struct { + const char* name; + const Icon* icon; + const char* path; +} FlipperExternalApplication; + +typedef void (*FlipperInternalOnStartHook)(void); + +extern const char* FLIPPER_AUTORUN_APP_NAME; + +/* Services list + * Spawned on startup + */ +extern const FlipperInternalApplication FLIPPER_SERVICES[]; +extern const size_t FLIPPER_SERVICES_COUNT; + +/* Apps list + * Spawned by loader + */ +extern const FlipperInternalApplication FLIPPER_APPS[]; +extern const size_t FLIPPER_APPS_COUNT; + +/* On system start hooks + * Called by loader, after OS initialization complete + */ +extern const FlipperInternalOnStartHook FLIPPER_ON_SYSTEM_START[]; +extern const size_t FLIPPER_ON_SYSTEM_START_COUNT; + +/* System apps + * Can only be spawned by loader by name + */ +extern const FlipperInternalApplication FLIPPER_SYSTEM_APPS[]; +extern const size_t FLIPPER_SYSTEM_APPS_COUNT; + +/* Debug apps + * Can only be spawned by loader by name + */ +extern const FlipperInternalApplication FLIPPER_DEBUG_APPS[]; +extern const size_t FLIPPER_DEBUG_APPS_COUNT; + +extern const FlipperInternalApplication FLIPPER_ARCHIVE; + +/* Settings list + * Spawned by loader + */ +extern const FlipperInternalApplication FLIPPER_SETTINGS_APPS[]; +extern const size_t FLIPPER_SETTINGS_APPS_COUNT; + +/* External Menu Apps list + * Spawned by loader + */ +extern const FlipperExternalApplication FLIPPER_EXTERNAL_APPS[]; +extern const size_t FLIPPER_EXTERNAL_APPS_COUNT; diff --git a/applications/services/bt/application.fam b/applications/services/bt/application.fam new file mode 100644 index 00000000000..2e97dc1d65d --- /dev/null +++ b/applications/services/bt/application.fam @@ -0,0 +1,25 @@ +App( + appid="bt", + name="BtSrv", + apptype=FlipperAppType.SERVICE, + entry_point="bt_srv", + cdefines=["SRV_BT"], + requires=[ + "cli", + "dialogs", + ], + provides=[ + "bt_start", + "bt_settings", + ], + stack_size=1 * 1024, + order=20, + sdk_headers=["bt_service/bt.h"], +) + +App( + appid="bt_start", + apptype=FlipperAppType.STARTUP, + entry_point="bt_on_system_start", + order=70, +) diff --git a/applications/bt/bt_cli.c b/applications/services/bt/bt_cli.c similarity index 86% rename from applications/bt/bt_cli.c rename to applications/services/bt/bt_cli.c index 3aa1bc75236..02bf6cee4a3 100644 --- a/applications/bt/bt_cli.c +++ b/applications/services/bt/bt_cli.c @@ -1,24 +1,24 @@ #include #include -#include +#include #include -#include "ble.h" +#include #include "bt_settings.h" #include "bt_service/bt.h" -static void bt_cli_command_hci_info(Cli* cli, string_t args, void* context) { +static void bt_cli_command_hci_info(Cli* cli, FuriString* args, void* context) { UNUSED(cli); UNUSED(args); UNUSED(context); - string_t buffer; - string_init(buffer); + FuriString* buffer; + buffer = furi_string_alloc(); furi_hal_bt_dump_state(buffer); - printf("%s", string_get_cstr(buffer)); - string_clear(buffer); + printf("%s", furi_string_get_cstr(buffer)); + furi_string_free(buffer); } -static void bt_cli_command_carrier_tx(Cli* cli, string_t args, void* context) { +static void bt_cli_command_carrier_tx(Cli* cli, FuriString* args, void* context) { UNUSED(context); int channel = 0; int power = 0; @@ -50,7 +50,7 @@ static void bt_cli_command_carrier_tx(Cli* cli, string_t args, void* context) { } while(false); } -static void bt_cli_command_carrier_rx(Cli* cli, string_t args, void* context) { +static void bt_cli_command_carrier_rx(Cli* cli, FuriString* args, void* context) { UNUSED(context); int channel = 0; @@ -81,7 +81,7 @@ static void bt_cli_command_carrier_rx(Cli* cli, string_t args, void* context) { } while(false); } -static void bt_cli_command_packet_tx(Cli* cli, string_t args, void* context) { +static void bt_cli_command_packet_tx(Cli* cli, FuriString* args, void* context) { UNUSED(context); int channel = 0; int pattern = 0; @@ -129,7 +129,7 @@ static void bt_cli_command_packet_tx(Cli* cli, string_t args, void* context) { } while(false); } -static void bt_cli_command_packet_rx(Cli* cli, string_t args, void* context) { +static void bt_cli_command_packet_rx(Cli* cli, FuriString* args, void* context) { UNUSED(context); int channel = 0; int datarate = 1; @@ -178,12 +178,12 @@ static void bt_cli_print_usage() { } } -static void bt_cli(Cli* cli, string_t args, void* context) { +static void bt_cli(Cli* cli, FuriString* args, void* context) { UNUSED(context); furi_record_open(RECORD_BT); - string_t cmd; - string_init(cmd); + FuriString* cmd; + cmd = furi_string_alloc(); BtSettings bt_settings; bt_settings_load(&bt_settings); @@ -192,24 +192,24 @@ static void bt_cli(Cli* cli, string_t args, void* context) { bt_cli_print_usage(); break; } - if(string_cmp_str(cmd, "hci_info") == 0) { + if(furi_string_cmp_str(cmd, "hci_info") == 0) { bt_cli_command_hci_info(cli, args, NULL); break; } if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && furi_hal_bt_is_testing_supported()) { - if(string_cmp_str(cmd, "tx_carrier") == 0) { + if(furi_string_cmp_str(cmd, "tx_carrier") == 0) { bt_cli_command_carrier_tx(cli, args, NULL); break; } - if(string_cmp_str(cmd, "rx_carrier") == 0) { + if(furi_string_cmp_str(cmd, "rx_carrier") == 0) { bt_cli_command_carrier_rx(cli, args, NULL); break; } - if(string_cmp_str(cmd, "tx_packet") == 0) { + if(furi_string_cmp_str(cmd, "tx_packet") == 0) { bt_cli_command_packet_tx(cli, args, NULL); break; } - if(string_cmp_str(cmd, "rx_packet") == 0) { + if(furi_string_cmp_str(cmd, "rx_packet") == 0) { bt_cli_command_packet_rx(cli, args, NULL); break; } @@ -222,7 +222,7 @@ static void bt_cli(Cli* cli, string_t args, void* context) { furi_hal_bt_start_advertising(); } - string_clear(cmd); + furi_string_free(cmd); furi_record_close(RECORD_BT); } diff --git a/applications/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c similarity index 87% rename from applications/bt/bt_service/bt.c rename to applications/services/bt/bt_service/bt.c index bc80acc151a..36409fe5cd6 100644 --- a/applications/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -1,9 +1,10 @@ #include "bt_i.h" -#include "battery_service.h" #include "bt_keys_storage.h" +#include #include #include +#include #define TAG "BtSrv" @@ -35,7 +36,7 @@ static void bt_pin_code_view_port_draw_callback(Canvas* canvas, void* context) { Bt* bt = context; char pin_code_info[24]; canvas_draw_icon(canvas, 0, 0, &I_BLE_Pairing_128x64); - snprintf(pin_code_info, sizeof(pin_code_info), "Pairing code\n%06ld", bt->pin_code); + snprintf(pin_code_info, sizeof(pin_code_info), "Pairing code\n%06lu", bt->pin_code); elements_multiline_text_aligned(canvas, 64, 4, AlignCenter, AlignTop, pin_code_info); elements_button_left(canvas, "Quit"); } @@ -75,14 +76,14 @@ static void bt_pin_code_hide(Bt* bt) { static bool bt_pin_code_verify_event_handler(Bt* bt, uint32_t pin) { furi_assert(bt); notification_message(bt->notification, &sequence_display_backlight_on); - string_t pin_str; + FuriString* pin_str; dialog_message_set_icon(bt->dialog_message, &I_BLE_Pairing_128x64, 0, 0); - string_init_printf(pin_str, "Verify code\n%06d", pin); + pin_str = furi_string_alloc_printf("Verify code\n%06lu", pin); dialog_message_set_text( - bt->dialog_message, string_get_cstr(pin_str), 64, 4, AlignCenter, AlignTop); + bt->dialog_message, furi_string_get_cstr(pin_str), 64, 4, AlignCenter, AlignTop); dialog_message_set_buttons(bt->dialog_message, "Cancel", "OK", NULL); DialogMessageButton button = dialog_message_show(bt->dialogs, bt->dialog_message); - string_clear(pin_str); + furi_string_free(pin_str); return button == DialogMessageButtonCenter; } @@ -116,6 +117,8 @@ Bt* bt_alloc() { if(!bt_settings_load(&bt->bt_settings)) { bt_settings_save(&bt->bt_settings); } + // Keys storage + bt->keys_storage = bt_keys_storage_alloc(BT_KEYS_STORAGE_PATH); // Alloc queue bt->message_queue = furi_message_queue_alloc(8, sizeof(BtMessage)); @@ -160,11 +163,16 @@ static uint16_t bt_serial_event_callback(SerialServiceEvent event, void* context rpc_session_feed(bt->rpc_session, event.data.buffer, event.data.size, 1000); if(bytes_processed != event.data.size) { FURI_LOG_E( - TAG, "Only %d of %d bytes processed by RPC", bytes_processed, event.data.size); + TAG, "Only %zu of %u bytes processed by RPC", bytes_processed, event.data.size); } ret = rpc_session_get_available_size(bt->rpc_session); } else if(event.event == SerialServiceEventTypeDataSent) { furi_event_flag_set(bt->rpc_event, BT_RPC_EVENT_BUFF_SENT); + } else if(event.event == SerialServiceEventTypesBleResetRequest) { + FURI_LOG_I(TAG, "BLE restart request received"); + BtMessage message = {.type = BtMessageTypeSetProfile, .data.profile = BtProfileSerial}; + furi_check( + furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); } return ret; } @@ -217,7 +225,7 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) { furi_event_flag_clear(bt->rpc_event, BT_RPC_EVENT_DISCONNECTED); if(bt->profile == BtProfileSerial) { // Open RPC session - bt->rpc_session = rpc_session_open(bt->rpc); + bt->rpc_session = rpc_session_open(bt->rpc, RpcOwnerBle); if(bt->rpc_session) { FURI_LOG_I(TAG, "Open RPC connection"); rpc_session_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback); @@ -226,6 +234,7 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) { rpc_session_set_context(bt->rpc_session, bt); furi_hal_bt_serial_set_event_callback( RPC_BUFFER_SIZE, bt_serial_event_callback, bt); + furi_hal_bt_serial_set_rpc_status(FuriHalBtSerialRpcStatusActive); } else { FURI_LOG_W(TAG, "RPC is busy, failed to open new session"); } @@ -241,6 +250,7 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) { } else if(event.type == GapEventTypeDisconnected) { if(bt->profile == BtProfileSerial && bt->rpc_session) { FURI_LOG_I(TAG, "Close RPC connection"); + furi_hal_bt_serial_set_rpc_status(FuriHalBtSerialRpcStatusNotActive); furi_event_flag_set(bt->rpc_event, BT_RPC_EVENT_DISCONNECTED); rpc_session_close(bt->rpc_session); furi_hal_bt_serial_set_event_callback(0, NULL, NULL); @@ -277,8 +287,10 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) { static void bt_on_key_storage_change_callback(uint8_t* addr, uint16_t size, void* context) { furi_assert(context); Bt* bt = context; - FURI_LOG_I(TAG, "Changed addr start: %08lX, size changed: %d", addr, size); - BtMessage message = {.type = BtMessageTypeKeysStorageUpdated}; + BtMessage message = { + .type = BtMessageTypeKeysStorageUpdated, + .data.key_storage_data.start_address = addr, + .data.key_storage_data.size = size}; furi_check( furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); } @@ -323,6 +335,8 @@ static void bt_change_profile(Bt* bt, BtMessage* message) { furi_profile = FuriHalBtProfileSerial; } + bt_keys_storage_load(bt->keys_storage); + if(furi_hal_bt_change_app(furi_profile, bt_on_gap_event_callback, bt)) { FURI_LOG_I(TAG, "Bt App started"); if(bt->bt_settings.enabled) { @@ -330,21 +344,28 @@ static void bt_change_profile(Bt* bt, BtMessage* message) { } furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt); bt->profile = message->data.profile; - *message->result = true; + if(message->result) { + *message->result = true; + } } else { FURI_LOG_E(TAG, "Failed to start Bt App"); - *message->result = false; + if(message->result) { + *message->result = false; + } } } else { bt_show_warning(bt, "Radio stack doesn't support this app"); - *message->result = false; + if(message->result) { + *message->result = false; + } } - furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT); + if(message->lock) api_lock_unlock(message->lock); } -static void bt_close_connection(Bt* bt) { +static void bt_close_connection(Bt* bt, BtMessage* message) { bt_close_rpc_connection(bt); - furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT); + furi_hal_bt_stop_advertising(); + if(message->lock) api_lock_unlock(message->lock); } int32_t bt_srv(void* p) { @@ -352,14 +373,14 @@ 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; } - // Read keys - if(!bt_keys_storage_load(bt)) { + // Load keys + if(!bt_keys_storage_load(bt->keys_storage)) { FURI_LOG_W(TAG, "Failed to load bonding keys"); } @@ -404,13 +425,16 @@ int32_t bt_srv(void* p) { // Display PIN code bt_pin_code_show(bt, message.data.pin_code); } else if(message.type == BtMessageTypeKeysStorageUpdated) { - bt_keys_storage_save(bt); + bt_keys_storage_update( + bt->keys_storage, + message.data.key_storage_data.start_address, + message.data.key_storage_data.size); } else if(message.type == BtMessageTypeSetProfile) { bt_change_profile(bt, &message); } else if(message.type == BtMessageTypeDisconnect) { - bt_close_connection(bt); + bt_close_connection(bt, &message); } else if(message.type == BtMessageTypeForgetBondedDevices) { - bt_keys_storage_delete(bt); + bt_keys_storage_delete(bt->keys_storage); } } return 0; diff --git a/applications/bt/bt_service/bt.h b/applications/services/bt/bt_service/bt.h similarity index 77% rename from applications/bt/bt_service/bt.h rename to applications/services/bt/bt_service/bt.h index 6e4e1b824a5..ca47936dbbb 100644 --- a/applications/bt/bt_service/bt.h +++ b/applications/services/bt/bt_service/bt.h @@ -56,6 +56,19 @@ void bt_set_status_changed_callback(Bt* bt, BtStatusChangedCallback callback, vo */ void bt_forget_bonded_devices(Bt* bt); +/** Set keys storage file path + * + * @param bt Bt instance + * @param keys_storage_path Path to file with saved keys + */ +void bt_keys_storage_set_storage_path(Bt* bt, const char* keys_storage_path); + +/** Set default keys storage file path + * + * @param bt Bt instance + */ +void bt_keys_storage_set_default_path(Bt* bt); + #ifdef __cplusplus } #endif diff --git a/applications/services/bt/bt_service/bt_api.c b/applications/services/bt/bt_service/bt_api.c new file mode 100644 index 00000000000..e31031783b6 --- /dev/null +++ b/applications/services/bt/bt_service/bt_api.c @@ -0,0 +1,66 @@ +#include "bt_i.h" + +bool bt_set_profile(Bt* bt, BtProfile profile) { + furi_assert(bt); + + // Send message + bool result = false; + BtMessage message = { + .lock = api_lock_alloc_locked(), + .type = BtMessageTypeSetProfile, + .data.profile = profile, + .result = &result}; + furi_check( + furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); + // Wait for unlock + api_lock_wait_unlock_and_free(message.lock); + + return result; +} + +void bt_disconnect(Bt* bt) { + furi_assert(bt); + + // Send message + BtMessage message = {.lock = api_lock_alloc_locked(), .type = BtMessageTypeDisconnect}; + furi_check( + furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); + // Wait for unlock + api_lock_wait_unlock_and_free(message.lock); +} + +void bt_set_status_changed_callback(Bt* bt, BtStatusChangedCallback callback, void* context) { + furi_assert(bt); + + bt->status_changed_cb = callback; + bt->status_changed_ctx = context; +} + +void bt_forget_bonded_devices(Bt* bt) { + furi_assert(bt); + BtMessage message = {.type = BtMessageTypeForgetBondedDevices}; + furi_check( + furi_message_queue_put(bt->message_queue, &message, FuriWaitForever) == FuriStatusOk); +} + +void bt_keys_storage_set_storage_path(Bt* bt, const char* keys_storage_path) { + furi_assert(bt); + furi_assert(bt->keys_storage); + furi_assert(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) { + furi_assert(bt); + furi_assert(bt->keys_storage); + + bt_keys_storage_set_file_path(bt->keys_storage, BT_KEYS_STORAGE_PATH); +} diff --git a/applications/services/bt/bt_service/bt_i.h b/applications/services/bt/bt_service/bt_i.h new file mode 100644 index 00000000000..55bae76f3bf --- /dev/null +++ b/applications/services/bt/bt_service/bt_i.h @@ -0,0 +1,79 @@ +#pragma once + +#include "bt.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "bt_keys_filename.h" + +#define BT_KEYS_STORAGE_PATH INT_PATH(BT_KEYS_STORAGE_FILE_NAME) + +typedef enum { + BtMessageTypeUpdateStatus, + BtMessageTypeUpdateBatteryLevel, + BtMessageTypeUpdatePowerState, + BtMessageTypePinCodeShow, + BtMessageTypeKeysStorageUpdated, + BtMessageTypeSetProfile, + BtMessageTypeDisconnect, + BtMessageTypeForgetBondedDevices, +} BtMessageType; + +typedef struct { + uint8_t* start_address; + uint16_t size; +} BtKeyStorageUpdateData; + +typedef union { + uint32_t pin_code; + uint8_t battery_level; + BtProfile profile; + BtKeyStorageUpdateData key_storage_data; +} BtMessageData; + +typedef struct { + FuriApiLock lock; + BtMessageType type; + BtMessageData data; + bool* result; +} BtMessage; + +struct Bt { + uint8_t* bt_keys_addr_start; + uint16_t bt_keys_size; + uint16_t max_packet_size; + BtSettings bt_settings; + BtKeysStorage* keys_storage; + BtStatus status; + BtProfile profile; + FuriMessageQueue* message_queue; + NotificationApp* notification; + Gui* gui; + ViewPort* statusbar_view_port; + ViewPort* pin_code_view_port; + uint32_t pin_code; + DialogsApp* dialogs; + DialogMessage* dialog_message; + Power* power; + Rpc* rpc; + RpcSession* rpc_session; + FuriEventFlag* rpc_event; + FuriEventFlag* api_event; + BtStatusChangedCallback status_changed_cb; + void* status_changed_ctx; +}; diff --git a/applications/bt/bt_service/bt_keys_filename.h b/applications/services/bt/bt_service/bt_keys_filename.h similarity index 100% rename from applications/bt/bt_service/bt_keys_filename.h rename to applications/services/bt/bt_service/bt_keys_filename.h diff --git a/applications/services/bt/bt_service/bt_keys_storage.c b/applications/services/bt/bt_service/bt_keys_storage.c new file mode 100644 index 00000000000..215f19a89cb --- /dev/null +++ b/applications/services/bt/bt_service/bt_keys_storage.c @@ -0,0 +1,146 @@ +#include "bt_keys_storage.h" + +#include +#include +#include +#include + +#define BT_KEYS_STORAGE_VERSION (0) +#define BT_KEYS_STORAGE_MAGIC (0x18) + +#define TAG "BtKeyStorage" + +struct BtKeysStorage { + uint8_t* nvm_sram_buff; + uint16_t nvm_sram_buff_size; + FuriString* file_path; +}; + +bool bt_keys_storage_delete(BtKeysStorage* instance) { + furi_assert(instance); + + bool delete_succeed = false; + bool bt_is_active = furi_hal_bt_is_active(); + + furi_hal_bt_stop_advertising(); + delete_succeed = furi_hal_bt_clear_white_list(); + if(bt_is_active) { + furi_hal_bt_start_advertising(); + } + + return delete_succeed; +} + +BtKeysStorage* bt_keys_storage_alloc(const char* keys_storage_path) { + furi_assert(keys_storage_path); + + BtKeysStorage* instance = malloc(sizeof(BtKeysStorage)); + // Set default nvm ram parameters + furi_hal_bt_get_key_storage_buff(&instance->nvm_sram_buff, &instance->nvm_sram_buff_size); + // Set key storage file + instance->file_path = furi_string_alloc(); + furi_string_set_str(instance->file_path, keys_storage_path); + + return instance; +} + +void bt_keys_storage_free(BtKeysStorage* instance) { + furi_assert(instance); + + furi_string_free(instance->file_path); + free(instance); +} + +void bt_keys_storage_set_file_path(BtKeysStorage* instance, const char* path) { + furi_assert(instance); + furi_assert(path); + + furi_string_set_str(instance->file_path, path); +} + +void bt_keys_storage_set_ram_params(BtKeysStorage* instance, uint8_t* buff, uint16_t size) { + furi_assert(instance); + furi_assert(buff); + + instance->nvm_sram_buff = buff; + instance->nvm_sram_buff_size = size; +} + +bool bt_keys_storage_load(BtKeysStorage* instance) { + furi_assert(instance); + + bool loaded = false; + do { + // Get payload size + size_t payload_size = 0; + if(!saved_struct_get_payload_size( + furi_string_get_cstr(instance->file_path), + BT_KEYS_STORAGE_MAGIC, + BT_KEYS_STORAGE_VERSION, + &payload_size)) { + FURI_LOG_E(TAG, "Failed to read payload size"); + break; + } + + if(payload_size > instance->nvm_sram_buff_size) { + FURI_LOG_E(TAG, "Saved data doesn't fit ram buffer"); + break; + } + + // Load saved data to ram + furi_hal_bt_nvm_sram_sem_acquire(); + bool data_loaded = saved_struct_load( + furi_string_get_cstr(instance->file_path), + instance->nvm_sram_buff, + payload_size, + BT_KEYS_STORAGE_MAGIC, + BT_KEYS_STORAGE_VERSION); + furi_hal_bt_nvm_sram_sem_release(); + if(!data_loaded) { + FURI_LOG_E(TAG, "Failed to load struct"); + break; + } + + loaded = true; + } while(false); + + return loaded; +} + +bool bt_keys_storage_update(BtKeysStorage* instance, uint8_t* start_addr, uint32_t size) { + furi_assert(instance); + furi_assert(start_addr); + + bool updated = false; + + FURI_LOG_I( + TAG, + "Base address: %p. Start update address: %p. Size changed: %lu", + (void*)instance->nvm_sram_buff, + start_addr, + size); + + do { + size_t new_size = start_addr - instance->nvm_sram_buff + size; + if(new_size > instance->nvm_sram_buff_size) { + FURI_LOG_E(TAG, "NVM RAM buffer overflow"); + break; + } + + furi_hal_bt_nvm_sram_sem_acquire(); + bool data_updated = saved_struct_save( + furi_string_get_cstr(instance->file_path), + instance->nvm_sram_buff, + new_size, + BT_KEYS_STORAGE_MAGIC, + BT_KEYS_STORAGE_VERSION); + furi_hal_bt_nvm_sram_sem_release(); + if(!data_updated) { + FURI_LOG_E(TAG, "Failed to update key storage"); + break; + } + updated = true; + } while(false); + + return updated; +} diff --git a/applications/services/bt/bt_service/bt_keys_storage.h b/applications/services/bt/bt_service/bt_keys_storage.h new file mode 100644 index 00000000000..cb808ca3047 --- /dev/null +++ b/applications/services/bt/bt_service/bt_keys_storage.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +typedef struct BtKeysStorage BtKeysStorage; + +BtKeysStorage* bt_keys_storage_alloc(const char* keys_storage_path); + +void bt_keys_storage_free(BtKeysStorage* instance); + +void bt_keys_storage_set_file_path(BtKeysStorage* instance, const char* path); + +void bt_keys_storage_set_ram_params(BtKeysStorage* instance, uint8_t* buff, uint16_t size); + +bool bt_keys_storage_load(BtKeysStorage* instance); + +bool bt_keys_storage_update(BtKeysStorage* instance, uint8_t* start_addr, uint32_t size); + +bool bt_keys_storage_delete(BtKeysStorage* instance); diff --git a/applications/bt/bt_settings.c b/applications/services/bt/bt_settings.c similarity index 100% rename from applications/bt/bt_settings.c rename to applications/services/bt/bt_settings.c diff --git a/applications/bt/bt_settings.h b/applications/services/bt/bt_settings.h similarity index 77% rename from applications/bt/bt_settings.h rename to applications/services/bt/bt_settings.h index 260d9c0e0f4..9ed8be89c4a 100644 --- a/applications/bt/bt_settings.h +++ b/applications/services/bt/bt_settings.h @@ -5,6 +5,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { bool enabled; } BtSettings; @@ -12,3 +16,7 @@ typedef struct { bool bt_settings_load(BtSettings* bt_settings); bool bt_settings_save(BtSettings* bt_settings); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/bt/bt_settings_filename.h b/applications/services/bt/bt_settings_filename.h similarity index 100% rename from applications/bt/bt_settings_filename.h rename to applications/services/bt/bt_settings_filename.h diff --git a/applications/services/cli/application.fam b/applications/services/cli/application.fam new file mode 100644 index 00000000000..7a57bb6076b --- /dev/null +++ b/applications/services/cli/application.fam @@ -0,0 +1,10 @@ +App( + appid="cli", + name="CliSrv", + apptype=FlipperAppType.SERVICE, + entry_point="cli_srv", + cdefines=["SRV_CLI"], + stack_size=4 * 1024, + order=30, + sdk_headers=["cli.h", "cli_vcp.h"], +) diff --git a/applications/cli/cli.c b/applications/services/cli/cli.c similarity index 75% rename from applications/cli/cli.c rename to applications/services/cli/cli.c index e554ac898e3..ad3bbd6650d 100644 --- a/applications/cli/cli.c +++ b/applications/services/cli/cli.c @@ -11,8 +11,8 @@ Cli* cli_alloc() { CliCommandTree_init(cli->commands); - string_init(cli->last_line); - string_init(cli->line); + cli->last_line = furi_string_alloc(); + cli->line = furi_string_alloc(); cli->session = NULL; @@ -37,9 +37,11 @@ char cli_getc(Cli* cli) { if(cli->session != NULL) { if(cli->session->rx((uint8_t*)&c, 1, FuriWaitForever) == 0) { cli_reset(cli); + furi_delay_tick(10); } } else { cli_reset(cli); + furi_delay_tick(10); } return c; } @@ -138,34 +140,26 @@ void cli_nl(Cli* cli) { void cli_prompt(Cli* cli) { UNUSED(cli); - printf("\r\n>: %s", string_get_cstr(cli->line)); + printf("\r\n>: %s", furi_string_get_cstr(cli->line)); fflush(stdout); } void cli_reset(Cli* cli) { // cli->last_line is cleared and cli->line's buffer moved to cli->last_line - string_move(cli->last_line, cli->line); + furi_string_move(cli->last_line, cli->line); // Reiniting cli->line - string_init(cli->line); + cli->line = furi_string_alloc(); cli->cursor_position = 0; } static void cli_handle_backspace(Cli* cli) { if(cli->cursor_position > 0) { - furi_assert(string_size(cli->line) > 0); + furi_assert(furi_string_size(cli->line) > 0); // Other side printf("\e[D\e[1P"); fflush(stdout); // Our side - string_t temp; - string_init(temp); - string_reserve(temp, string_size(cli->line) - 1); - string_set_strn(temp, string_get_cstr(cli->line), cli->cursor_position - 1); - string_cat_str(temp, string_get_cstr(cli->line) + cli->cursor_position); - - // cli->line is cleared and temp's buffer moved to cli->line - string_move(cli->line, temp); - // NO MEMORY LEAK, STOP REPORTING IT + furi_string_replace_at(cli->line, cli->cursor_position - 1, 1, ""); cli->cursor_position--; } else { @@ -174,11 +168,11 @@ static void cli_handle_backspace(Cli* cli) { } static void cli_normalize_line(Cli* cli) { - string_strim(cli->line); - cli->cursor_position = string_size(cli->line); + furi_string_trim(cli->line); + cli->cursor_position = furi_string_size(cli->line); } -static void cli_execute_command(Cli* cli, CliCommand* command, string_t args) { +static void cli_execute_command(Cli* cli, CliCommand* command, FuriString* args) { if(!(command->flags & CliCommandFlagInsomniaSafe)) { furi_hal_power_insomnia_enter(); } @@ -208,32 +202,32 @@ static void cli_execute_command(Cli* cli, CliCommand* command, string_t args) { static void cli_handle_enter(Cli* cli) { cli_normalize_line(cli); - if(string_size(cli->line) == 0) { + if(furi_string_size(cli->line) == 0) { cli_prompt(cli); return; } // Command and args container - string_t command; - string_init(command); - string_t args; - string_init(args); + FuriString* command; + command = furi_string_alloc(); + FuriString* args; + args = furi_string_alloc(); // Split command and args - size_t ws = string_search_char(cli->line, ' '); - if(ws == STRING_FAILURE) { - string_set(command, cli->line); + size_t ws = furi_string_search_char(cli->line, ' '); + if(ws == FURI_STRING_FAILURE) { + furi_string_set(command, cli->line); } else { - string_set_n(command, cli->line, 0, ws); - string_set_n(args, cli->line, ws, string_size(cli->line)); - string_strim(args); + furi_string_set_n(command, cli->line, 0, ws); + furi_string_set_n(args, cli->line, ws, furi_string_size(cli->line)); + furi_string_trim(args); } // Search for command furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk); CliCommand* cli_command_ptr = CliCommandTree_get(cli->commands, command); - if(cli_command_ptr) { + if(cli_command_ptr) { //-V547 CliCommand cli_command; memcpy(&cli_command, cli_command_ptr, sizeof(CliCommand)); furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); @@ -244,7 +238,7 @@ static void cli_handle_enter(Cli* cli) { cli_nl(cli); printf( "`%s` command not found, use `help` or `?` to list all available commands", - string_get_cstr(command)); + furi_string_get_cstr(command)); cli_putc(cli, CliSymbolAsciiBell); } @@ -252,59 +246,59 @@ static void cli_handle_enter(Cli* cli) { cli_prompt(cli); // Cleanup command and args - string_clear(command); - string_clear(args); + furi_string_free(command); + furi_string_free(args); } static void cli_handle_autocomplete(Cli* cli) { cli_normalize_line(cli); - if(string_size(cli->line) == 0) { + if(furi_string_size(cli->line) == 0) { return; } cli_nl(cli); // Prepare common base for autocomplete - string_t common; - string_init(common); + FuriString* common; + common = furi_string_alloc(); // Iterate throw commands for M_EACH(cli_command, cli->commands, CliCommandTree_t) { // Process only if starts with line buffer - if(string_start_with_string_p(*cli_command->key_ptr, cli->line)) { + if(furi_string_start_with(*cli_command->key_ptr, cli->line)) { // Show autocomplete option - printf("%s\r\n", string_get_cstr(*cli_command->key_ptr)); + printf("%s\r\n", furi_string_get_cstr(*cli_command->key_ptr)); // Process common base for autocomplete - if(string_size(common) > 0) { + if(furi_string_size(common) > 0) { // Choose shortest string - const size_t key_size = string_size(*cli_command->key_ptr); - const size_t common_size = string_size(common); + const size_t key_size = furi_string_size(*cli_command->key_ptr); + const size_t common_size = furi_string_size(common); const size_t min_size = key_size > common_size ? common_size : key_size; size_t i = 0; while(i < min_size) { // Stop when do not match - if(string_get_char(*cli_command->key_ptr, i) != - string_get_char(common, i)) { + if(furi_string_get_char(*cli_command->key_ptr, i) != + furi_string_get_char(common, i)) { break; } i++; } // Cut right part if any - string_left(common, i); + furi_string_left(common, i); } else { // Start with something - string_set(common, *cli_command->key_ptr); + furi_string_set(common, *cli_command->key_ptr); } } } // Replace line buffer if autocomplete better - if(string_size(common) > string_size(cli->line)) { - string_set(cli->line, common); - cli->cursor_position = string_size(cli->line); + if(furi_string_size(common) > furi_string_size(cli->line)) { + furi_string_set(cli->line, common); + cli->cursor_position = furi_string_size(cli->line); } // Cleanup - string_clear(common); + furi_string_free(common); // Show prompt cli_prompt(cli); } @@ -312,16 +306,16 @@ static void cli_handle_autocomplete(Cli* cli) { static void cli_handle_escape(Cli* cli, char c) { if(c == 'A') { // Use previous command if line buffer is empty - if(string_size(cli->line) == 0 && string_cmp(cli->line, cli->last_line) != 0) { + if(furi_string_size(cli->line) == 0 && furi_string_cmp(cli->line, cli->last_line) != 0) { // Set line buffer and cursor position - string_set(cli->line, cli->last_line); - cli->cursor_position = string_size(cli->line); + furi_string_set(cli->line, cli->last_line); + cli->cursor_position = furi_string_size(cli->line); // Show new line to user - printf("%s", string_get_cstr(cli->line)); + printf("%s", furi_string_get_cstr(cli->line)); } } else if(c == 'B') { } else if(c == 'C') { - if(cli->cursor_position < string_size(cli->line)) { + if(cli->cursor_position < furi_string_size(cli->line)) { cli->cursor_position++; printf("\e[C"); } @@ -361,22 +355,14 @@ void cli_process_input(Cli* cli) { cli_handle_backspace(cli); } else if(in_chr == CliSymbolAsciiCR) { cli_handle_enter(cli); - } else if(in_chr >= 0x20 && in_chr < 0x7F) { - if(cli->cursor_position == string_size(cli->line)) { - string_push_back(cli->line, in_chr); + } else if(in_chr >= 0x20 && in_chr < 0x7F) { //-V560 + if(cli->cursor_position == furi_string_size(cli->line)) { + furi_string_push_back(cli->line, in_chr); cli_putc(cli, in_chr); } else { - // ToDo: better way? - string_t temp; - string_init(temp); - string_reserve(temp, string_size(cli->line) + 1); - string_set_strn(temp, string_get_cstr(cli->line), cli->cursor_position); - string_push_back(temp, in_chr); - string_cat_str(temp, string_get_cstr(cli->line) + cli->cursor_position); - - // cli->line is cleared and temp's buffer moved to cli->line - string_move(cli->line, temp); - // NO MEMORY LEAK, STOP REPORTING IT + // Insert character to line buffer + const char in_str[2] = {in_chr, 0}; + furi_string_replace_at(cli->line, cli->cursor_position, 0, in_str); // Print character in replace mode printf("\e[4h%c\e[4l", in_chr); @@ -394,14 +380,14 @@ void cli_add_command( CliCommandFlag flags, CliCallback callback, void* context) { - string_t name_str; - string_init_set_str(name_str, name); - string_strim(name_str); + FuriString* name_str; + name_str = furi_string_alloc_set(name); + furi_string_trim(name_str); size_t name_replace; do { - name_replace = string_replace_str(name_str, " ", "_"); - } while(name_replace != STRING_FAILURE); + name_replace = furi_string_replace(name_str, " ", "_"); + } while(name_replace != FURI_STRING_FAILURE); CliCommand c; c.callback = callback; @@ -412,24 +398,24 @@ void cli_add_command( CliCommandTree_set_at(cli->commands, name_str, c); furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); - string_clear(name_str); + furi_string_free(name_str); } void cli_delete_command(Cli* cli, const char* name) { - string_t name_str; - string_init_set_str(name_str, name); - string_strim(name_str); + FuriString* name_str; + name_str = furi_string_alloc_set(name); + furi_string_trim(name_str); size_t name_replace; do { - name_replace = string_replace_str(name_str, " ", "_"); - } while(name_replace != STRING_FAILURE); + name_replace = furi_string_replace(name_str, " ", "_"); + } while(name_replace != FURI_STRING_FAILURE); furi_check(furi_mutex_acquire(cli->mutex, FuriWaitForever) == FuriStatusOk); CliCommandTree_erase(cli->commands, name_str); furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); - string_clear(name_str); + furi_string_free(name_str); } void cli_session_open(Cli* cli, void* session) { @@ -477,7 +463,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/cli/cli.h b/applications/services/cli/cli.h similarity index 97% rename from applications/cli/cli.h rename to applications/services/cli/cli.h index 549e72cc523..09f54f056f0 100644 --- a/applications/cli/cli.h +++ b/applications/services/cli/cli.h @@ -4,8 +4,7 @@ */ #pragma once - -#include +#include #ifdef __cplusplus extern "C" { @@ -43,7 +42,7 @@ typedef struct Cli Cli; * @param args string with what was passed after command * @param context pointer to whatever you gave us on cli_add_command */ -typedef void (*CliCallback)(Cli* cli, string_t args, void* context); +typedef void (*CliCallback)(Cli* cli, FuriString* args, void* context); /** Add cli command Registers you command callback * diff --git a/applications/services/cli/cli_command_gpio.c b/applications/services/cli/cli_command_gpio.c new file mode 100644 index 00000000000..d0246273494 --- /dev/null +++ b/applications/services/cli/cli_command_gpio.c @@ -0,0 +1,203 @@ +#include "cli_command_gpio.h" + +#include "core/string.h" +#include +#include +#include + +void cli_command_gpio_print_usage() { + printf("Usage:\r\n"); + printf("gpio \r\n"); + printf("Cmd list:\r\n"); + printf("\tmode <0|1>\t - Set gpio mode: 0 - input, 1 - output\r\n"); + printf("\tset <0|1>\t - Set gpio value\r\n"); + printf("\tread \t - Read gpio value\r\n"); +} + +static bool pin_name_to_int(FuriString* pin_name, size_t* result) { + bool is_debug_mode = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); + for(size_t i = 0; i < gpio_pins_count; i++) { + if(furi_string_equal(pin_name, gpio_pins[i].name)) { + if(!gpio_pins[i].debug || is_debug_mode) { + *result = i; + return true; + } + } + } + + return false; +} + +static void gpio_print_pins(void) { + printf("Wrong pin name. Available pins: "); + bool is_debug_mode = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); + for(size_t i = 0; i < gpio_pins_count; i++) { + if(!gpio_pins[i].debug || is_debug_mode) { + printf("%s ", gpio_pins[i].name); + } + } +} + +typedef enum { + GpioParseReturnOk, + GpioParseReturnCmdSyntaxError, + GpioParseReturnPinError, + GpioParseReturnValueError +} GpioParseReturn; + +static GpioParseReturn gpio_command_parse(FuriString* args, size_t* pin_num, uint8_t* value) { + GpioParseReturn ret = GpioParseReturnOk; + FuriString* pin_name = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, pin_name)) { + ret = GpioParseReturnCmdSyntaxError; + break; + } else if(!pin_name_to_int(pin_name, pin_num)) { + ret = GpioParseReturnPinError; + break; + } + + int pin_mode; //-V779 + if(!args_read_int_and_trim(args, &pin_mode) || pin_mode < 0 || pin_mode > 1) { + ret = GpioParseReturnValueError; + break; + } + + *value = pin_mode; + } while(false); + + furi_string_free(pin_name); + return ret; +} + +void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(context); + + size_t num = 0; + uint8_t value = 255; + + GpioParseReturn err = gpio_command_parse(args, &num, &value); + + if(err == GpioParseReturnCmdSyntaxError) { + cli_print_usage("gpio mode", " <0|1>", furi_string_get_cstr(args)); + return; + } else if(err == GpioParseReturnPinError) { //-V547 + gpio_print_pins(); + return; + } else if(err == GpioParseReturnValueError) { + printf("Value is invalid. Enter 1 for input or 0 for output"); + return; + } + + if(gpio_pins[num].debug) { //-V779 + printf( + "Changing this pin mode may damage hardware. Are you sure you want to continue? (y/n)?\r\n"); + char c = cli_getc(cli); + if(c != 'y' && c != 'Y') { + printf("Cancelled.\r\n"); + return; + } + } + + if(value == 1) { // output + furi_hal_gpio_write(gpio_pins[num].pin, false); + furi_hal_gpio_init_simple(gpio_pins[num].pin, GpioModeOutputPushPull); + printf("Pin %s is now an output (low)", gpio_pins[num].name); + } else { // input + furi_hal_gpio_init_simple(gpio_pins[num].pin, GpioModeInput); + printf("Pin %s is now an input", gpio_pins[num].name); + } +} + +void cli_command_gpio_read(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(context); + + size_t num = 0; + if(!pin_name_to_int(args, &num)) { + gpio_print_pins(); + return; + } + + if(LL_GPIO_MODE_INPUT != //-V779 + LL_GPIO_GetPinMode(gpio_pins[num].pin->port, gpio_pins[num].pin->pin)) { + printf("Err: pin %s is not set as an input.", gpio_pins[num].name); + return; + } + + uint8_t val = !!furi_hal_gpio_read(gpio_pins[num].pin); + + printf("Pin %s <= %u", gpio_pins[num].name, val); +} + +void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + + size_t num = 0; + uint8_t value = 0; + GpioParseReturn err = gpio_command_parse(args, &num, &value); + + if(err == GpioParseReturnCmdSyntaxError) { + cli_print_usage("gpio set", " <0|1>", furi_string_get_cstr(args)); + return; + } else if(err == GpioParseReturnPinError) { //-V547 + gpio_print_pins(); + return; + } else if(err == GpioParseReturnValueError) { + printf("Value is invalid. Enter 1 for high or 0 for low"); + return; + } + + if(LL_GPIO_MODE_OUTPUT != //-V779 + LL_GPIO_GetPinMode(gpio_pins[num].pin->port, gpio_pins[num].pin->pin)) { + printf("Err: pin %s is not set as an output.", gpio_pins[num].name); + return; + } + + // Extra check if debug pins used + if(gpio_pins[num].debug) { + printf( + "Setting this pin may damage hardware. Are you sure you want to continue? (y/n)?\r\n"); + char c = cli_getc(cli); + if(c != 'y' && c != 'Y') { + printf("Cancelled.\r\n"); + return; + } + } + + furi_hal_gpio_write(gpio_pins[num].pin, !!value); + printf("Pin %s => %u", gpio_pins[num].name, !!value); +} + +void cli_command_gpio(Cli* cli, FuriString* args, void* context) { + FuriString* cmd; + cmd = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, cmd)) { + cli_command_gpio_print_usage(); + break; + } + + if(furi_string_cmp_str(cmd, "mode") == 0) { + cli_command_gpio_mode(cli, args, context); + break; + } + + if(furi_string_cmp_str(cmd, "set") == 0) { + cli_command_gpio_set(cli, args, context); + break; + } + + if(furi_string_cmp_str(cmd, "read") == 0) { + cli_command_gpio_read(cli, args, context); + break; + } + + cli_command_gpio_print_usage(); + } while(false); + + furi_string_free(cmd); +} diff --git a/applications/services/cli/cli_command_gpio.h b/applications/services/cli/cli_command_gpio.h new file mode 100644 index 00000000000..7ae5aa62599 --- /dev/null +++ b/applications/services/cli/cli_command_gpio.h @@ -0,0 +1,5 @@ +#pragma once + +#include "cli_i.h" + +void cli_command_gpio(Cli* cli, FuriString* args, void* context); diff --git a/applications/services/cli/cli_commands.c b/applications/services/cli/cli_commands.c new file mode 100644 index 00000000000..467e7c53024 --- /dev/null +++ b/applications/services/cli/cli_commands.c @@ -0,0 +1,474 @@ +#include "cli_commands.h" +#include "cli_command_gpio.h" + +#include +#include +#include +#include +#include +#include +#include + +// 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_info_callback(const char* key, const char* value, bool last, void* context) { + UNUSED(last); + UNUSED(context); + printf("%-30s: %s\r\n", key, value); +} + +/** 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_info(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + + 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 available:"); + + // Command count + const size_t commands_count = CliCommandTree_size(cli->commands); + const size_t commands_count_mid = commands_count / 2 + commands_count % 2; + + // Use 2 iterators from start and middle to show 2 columns + CliCommandTree_it_t it_left; + CliCommandTree_it(it_left, cli->commands); + CliCommandTree_it_t it_right; + CliCommandTree_it(it_right, cli->commands); + for(size_t i = 0; i < commands_count_mid; i++) CliCommandTree_next(it_right); + + // Iterate throw tree + for(size_t i = 0; i < commands_count_mid; i++) { + printf("\r\n"); + // Left Column + if(!CliCommandTree_end_p(it_left)) { + printf("%-30s", furi_string_get_cstr(*CliCommandTree_ref(it_left)->key_ptr)); + CliCommandTree_next(it_left); + } + // Right Column + if(!CliCommandTree_end_p(it_right)) { + printf("%s", furi_string_get_cstr(*CliCommandTree_ref(it_right)->key_ptr)); + CliCommandTree_next(it_right); + } + }; + + if(furi_string_size(args) > 0) { + cli_nl(); + printf("`"); + printf("%s", furi_string_get_cstr(args)); + printf("` command not found"); + } +} + +void cli_command_uptime(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(args); + UNUSED(context); + uint32_t uptime = furi_get_tick() / furi_kernel_get_tick_frequency(); + printf("Uptime: %luh%lum%lus", uptime / 60 / 60, uptime / 60 % 60, uptime % 60); +} + +void cli_command_date(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(context); + + FuriHalRtcDateTime datetime = {0}; + + if(furi_string_size(args) > 0) { + uint16_t hours, minutes, seconds, month, day, year, weekday; + int ret = sscanf( + furi_string_get_cstr(args), + "%hu-%hu-%hu %hu:%hu:%hu %hu", + &year, + &month, + &day, + &hours, + &minutes, + &seconds, + &weekday); + + // Some variables are going to discard upper byte + // There will be some funky behaviour which is not breaking anything + datetime.hour = hours; + datetime.minute = minutes; + datetime.second = seconds; + datetime.weekday = weekday; + datetime.month = month; + datetime.day = day; + datetime.year = year; + + if(ret != 7) { + printf( + "Invalid datetime format, use `%s`. sscanf %d %s", + "%Y-%m-%d %H:%M:%S %u", + ret, + furi_string_get_cstr(args)); + return; + } + + if(!furi_hal_rtc_validate_datetime(&datetime)) { + printf("Invalid datetime data"); + return; + } + + furi_hal_rtc_set_datetime(&datetime); + // Verification + furi_hal_rtc_get_datetime(&datetime); + printf( + "New datetime is: " CLI_DATE_FORMAT, + datetime.year, + datetime.month, + datetime.day, + datetime.hour, + datetime.minute, + datetime.second, + datetime.weekday); + } else { + furi_hal_rtc_get_datetime(&datetime); + printf( + CLI_DATE_FORMAT, + datetime.year, + datetime.month, + datetime.day, + datetime.hour, + datetime.minute, + datetime.second, + datetime.weekday); + } +} + +#define CLI_COMMAND_LOG_RING_SIZE 2048 +#define CLI_COMMAND_LOG_BUFFER_SIZE 64 + +void cli_command_log_tx_callback(const uint8_t* buffer, size_t size, void* context) { + furi_stream_buffer_send(context, buffer, size, 0); +} + +bool cli_command_log_level_set_from_string(FuriString* level) { + FuriLogLevel log_level; + if(furi_log_level_from_string(furi_string_get_cstr(level), &log_level)) { + furi_log_set_level(log_level); + return true; + } else { + printf(" — start logging using the current level from the system settings\r\n"); + printf(" — only critical errors and other important messages\r\n"); + printf(" — non-critical errors and warnings including \r\n"); + printf(" — non-critical information including \r\n"); + printf(" — the default system log level (equivalent to )\r\n"); + printf( + " — debug information including (may impact system performance)\r\n"); + printf( + " — system traces including (may impact system performance)\r\n"); + } + return false; +} + +void cli_command_log(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + FuriStreamBuffer* ring = furi_stream_buffer_alloc(CLI_COMMAND_LOG_RING_SIZE, 1); + uint8_t buffer[CLI_COMMAND_LOG_BUFFER_SIZE]; + FuriLogLevel previous_level = furi_log_get_level(); + bool restore_log_level = false; + + if(furi_string_size(args) > 0) { + if(!cli_command_log_level_set_from_string(args)) { + furi_stream_buffer_free(ring); + return; + } + restore_log_level = true; + } + + const char* current_level; + furi_log_level_to_string(furi_log_get_level(), ¤t_level); + printf("Current log level: %s\r\n", current_level); + + furi_hal_console_set_tx_callback(cli_command_log_tx_callback, ring); + + printf("Use to list available log levels\r\n"); + printf("Press CTRL+C to stop...\r\n"); + while(!cli_cmd_interrupt_received(cli)) { + size_t ret = furi_stream_buffer_receive(ring, buffer, CLI_COMMAND_LOG_BUFFER_SIZE, 50); + cli_write(cli, buffer, ret); + } + + furi_hal_console_set_tx_callback(NULL, NULL); + + if(restore_log_level) { + // There will be strange behaviour if log level is set from settings while log command is running + furi_log_set_level(previous_level); + } + + furi_stream_buffer_free(ring); +} + +void cli_command_sysctl_debug(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(context); + if(!furi_string_cmp(args, "0")) { + furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); + printf("Debug disabled."); + } else if(!furi_string_cmp(args, "1")) { + furi_hal_rtc_set_flag(FuriHalRtcFlagDebug); + printf("Debug enabled."); + } else { + cli_print_usage("sysctl debug", "<1|0>", furi_string_get_cstr(args)); + } +} + +void cli_command_sysctl_heap_track(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(context); + if(!furi_string_cmp(args, "none")) { + furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeNone); + printf("Heap tracking disabled"); + } else if(!furi_string_cmp(args, "main")) { + furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeMain); + printf("Heap tracking enabled for application main thread"); +#if FURI_DEBUG + } else if(!furi_string_cmp(args, "tree")) { + furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeTree); + printf("Heap tracking enabled for application main and child threads"); + } else if(!furi_string_cmp(args, "all")) { + furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackModeAll); + printf("Heap tracking enabled for all threads"); +#endif + } else { + cli_print_usage("sysctl heap_track", "", furi_string_get_cstr(args)); + } +} + +void cli_command_sysctl_print_usage() { + printf("Usage:\r\n"); + printf("sysctl \r\n"); + printf("Cmd list:\r\n"); + + printf("\tdebug <0|1>\t - Enable or disable system debug\r\n"); +#if FURI_DEBUG + printf("\theap_track \t - Set heap allocation tracking mode\r\n"); +#else + printf("\theap_track \t - Set heap allocation tracking mode\r\n"); +#endif +} + +void cli_command_sysctl(Cli* cli, FuriString* args, void* context) { + FuriString* cmd; + cmd = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, cmd)) { + cli_command_sysctl_print_usage(); + break; + } + + if(furi_string_cmp_str(cmd, "debug") == 0) { + cli_command_sysctl_debug(cli, args, context); + break; + } + + if(furi_string_cmp_str(cmd, "heap_track") == 0) { + cli_command_sysctl_heap_track(cli, args, context); + break; + } + + cli_command_sysctl_print_usage(); + } while(false); + + furi_string_free(cmd); +} + +void cli_command_vibro(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(context); + if(!furi_string_cmp(args, "0")) { + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message_block(notification, &sequence_reset_vibro); + furi_record_close(RECORD_NOTIFICATION); + } else if(!furi_string_cmp(args, "1")) { + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message_block(notification, &sequence_set_vibro_on); + furi_record_close(RECORD_NOTIFICATION); + } else { + cli_print_usage("vibro", "<1|0>", furi_string_get_cstr(args)); + } +} + +void cli_command_led(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(context); + // Get first word as light name + NotificationMessage notification_led_message; + FuriString* light_name; + light_name = furi_string_alloc(); + size_t ws = furi_string_search_char(args, ' '); + if(ws == FURI_STRING_FAILURE) { + cli_print_usage("led", " <0-255>", furi_string_get_cstr(args)); + furi_string_free(light_name); + return; + } else { + furi_string_set_n(light_name, args, 0, ws); + furi_string_right(args, ws); + furi_string_trim(args); + } + // Check light name + if(!furi_string_cmp(light_name, "r")) { + notification_led_message.type = NotificationMessageTypeLedRed; + } else if(!furi_string_cmp(light_name, "g")) { + notification_led_message.type = NotificationMessageTypeLedGreen; + } else if(!furi_string_cmp(light_name, "b")) { + notification_led_message.type = NotificationMessageTypeLedBlue; + } else if(!furi_string_cmp(light_name, "bl")) { + notification_led_message.type = NotificationMessageTypeLedDisplayBacklight; + } else { + cli_print_usage("led", " <0-255>", furi_string_get_cstr(args)); + furi_string_free(light_name); + return; + } + furi_string_free(light_name); + // Read light value from the rest of the string + char* end_ptr; + uint32_t value = strtoul(furi_string_get_cstr(args), &end_ptr, 0); + if(!(value < 256 && *end_ptr == '\0')) { + cli_print_usage("led", " <0-255>", furi_string_get_cstr(args)); + return; + } + + // Set led value + notification_led_message.data.led.value = value; + + // Form notification sequence + const NotificationSequence notification_sequence = { + ¬ification_led_message, + NULL, + }; + + // Send notification + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_internal_message_block(notification, ¬ification_sequence); + furi_record_close(RECORD_NOTIFICATION); +} + +void cli_command_ps(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(args); + UNUSED(context); + + const uint8_t threads_num_max = 32; + FuriThreadId threads_ids[threads_num_max]; + uint8_t thread_num = furi_thread_enumerate(threads_ids, threads_num_max); + printf( + "%-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 %-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]), + (uint32_t)(tcb->pxEndOfStack - tcb->pxStack + 1) * sizeof(StackType_t), + furi_thread_get_stack_space(threads_ids[i])); + } + printf("\r\nTotal: %d", thread_num); +} + +void cli_command_free(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(args); + UNUSED(context); + + printf("Free heap size: %zu\r\n", memmgr_get_free_heap()); + printf("Total heap size: %zu\r\n", memmgr_get_total_heap()); + printf("Minimum heap size: %zu\r\n", memmgr_get_minimum_free_heap()); + printf("Maximum heap block: %zu\r\n", memmgr_heap_get_max_free_block()); + + printf("Pool free: %zu\r\n", memmgr_pool_get_free()); + printf("Maximum pool block: %zu\r\n", memmgr_pool_get_max_block()); +} + +void cli_command_free_blocks(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(args); + UNUSED(context); + + memmgr_heap_printf_free_blocks(); +} + +void cli_command_i2c(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(args); + UNUSED(context); + + furi_hal_i2c_acquire(&furi_hal_i2c_handle_external); + printf("Scanning external i2c on PC0(SCL)/PC1(SDA)\r\n" + "Clock: 100khz, 7bit address\r\n" + "\r\n"); + printf(" | 0 1 2 3 4 5 6 7 8 9 A B C D E F\r\n"); + printf("--+--------------------------------\r\n"); + for(uint8_t row = 0; row < 0x8; row++) { + printf("%x | ", row); + for(uint8_t column = 0; column <= 0xF; column++) { + bool ret = furi_hal_i2c_is_device_ready( + &furi_hal_i2c_handle_external, ((row << 4) + column) << 1, 2); + printf("%c ", ret ? '#' : '-'); + } + printf("\r\n"); + } + furi_hal_i2c_release(&furi_hal_i2c_handle_external); +} + +void cli_commands_init(Cli* cli) { + 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, "?", CliCommandFlagParallelSafe, cli_command_help, NULL); + cli_add_command(cli, "help", CliCommandFlagParallelSafe, cli_command_help, NULL); + + cli_add_command(cli, "uptime", CliCommandFlagDefault, cli_command_uptime, NULL); + cli_add_command(cli, "date", CliCommandFlagParallelSafe, cli_command_date, NULL); + cli_add_command(cli, "log", CliCommandFlagParallelSafe, cli_command_log, NULL); + cli_add_command(cli, "sysctl", CliCommandFlagDefault, cli_command_sysctl, NULL); + cli_add_command(cli, "ps", CliCommandFlagParallelSafe, cli_command_ps, NULL); + cli_add_command(cli, "free", CliCommandFlagParallelSafe, cli_command_free, NULL); + cli_add_command(cli, "free_blocks", CliCommandFlagParallelSafe, cli_command_free_blocks, NULL); + + cli_add_command(cli, "vibro", CliCommandFlagDefault, cli_command_vibro, NULL); + cli_add_command(cli, "led", CliCommandFlagDefault, cli_command_led, NULL); + cli_add_command(cli, "gpio", CliCommandFlagDefault, cli_command_gpio, NULL); + cli_add_command(cli, "i2c", CliCommandFlagDefault, cli_command_i2c, NULL); +} diff --git a/applications/cli/cli_commands.h b/applications/services/cli/cli_commands.h similarity index 100% rename from applications/cli/cli_commands.h rename to applications/services/cli/cli_commands.h diff --git a/applications/cli/cli_i.h b/applications/services/cli/cli_i.h old mode 100755 new mode 100644 similarity index 85% rename from applications/cli/cli_i.h rename to applications/services/cli/cli_i.h index 8f0bd85d510..858cd0c8f82 --- a/applications/cli/cli_i.h +++ b/applications/services/cli/cli_i.h @@ -9,17 +9,21 @@ #include #include +#include "cli_vcp.h" + #define CLI_LINE_SIZE_MAX #define CLI_COMMANDS_TREE_RANK 4 +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { CliCallback callback; void* context; uint32_t flags; } CliCommand; -typedef struct CliSession CliSession; - struct CliSession { void (*init)(void); void (*deinit)(void); @@ -32,8 +36,8 @@ struct CliSession { BPTREE_DEF2( CliCommandTree, CLI_COMMANDS_TREE_RANK, - string_t, - STRING_OPLIST, + FuriString*, + FURI_STRING_OPLIST, CliCommand, M_POD_OPLIST) @@ -43,8 +47,8 @@ struct Cli { CliCommandTree_t commands; FuriMutex* mutex; FuriSemaphore* idle_sem; - string_t last_line; - string_t line; + FuriString* last_line; + FuriString* line; CliSession* session; size_t cursor_position; @@ -57,3 +61,7 @@ void cli_reset(Cli* cli); void cli_putc(Cli* cli, char c); void cli_stdout_callback(void* _cookie, const char* data, size_t size); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/services/cli/cli_vcp.c b/applications/services/cli/cli_vcp.c new file mode 100644 index 00000000000..454c99d7a9c --- /dev/null +++ b/applications/services/cli/cli_vcp.c @@ -0,0 +1,316 @@ +#include +#include +#include +#include "cli_i.h" + +#define TAG "CliVcp" + +#define USB_CDC_PKT_LEN CDC_DATA_SZ +#define VCP_RX_BUF_SIZE (USB_CDC_PKT_LEN * 3) +#define VCP_TX_BUF_SIZE (USB_CDC_PKT_LEN * 3) + +#define VCP_IF_NUM 0 + +#ifdef CLI_VCP_DEBUG +#define VCP_DEBUG(...) FURI_LOG_D(TAG, __VA_ARGS__) +#else +#define VCP_DEBUG(...) +#endif + +typedef enum { + VcpEvtStop = (1 << 0), + VcpEvtConnect = (1 << 1), + VcpEvtDisconnect = (1 << 2), + VcpEvtStreamRx = (1 << 3), + VcpEvtRx = (1 << 4), + VcpEvtStreamTx = (1 << 5), + VcpEvtTx = (1 << 6), +} WorkerEvtFlags; + +#define VCP_THREAD_FLAG_ALL \ + (VcpEvtStop | VcpEvtConnect | VcpEvtDisconnect | VcpEvtRx | VcpEvtTx | VcpEvtStreamRx | \ + VcpEvtStreamTx) + +typedef struct { + FuriThread* thread; + + FuriStreamBuffer* tx_stream; + FuriStreamBuffer* rx_stream; + + volatile bool connected; + volatile bool running; + + FuriHalUsbInterface* usb_if_prev; + + uint8_t data_buffer[USB_CDC_PKT_LEN]; +} CliVcp; + +static int32_t vcp_worker(void* context); +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 CdcCallbacks cdc_cb = { + vcp_on_cdc_tx_complete, + vcp_on_cdc_rx, + vcp_state_callback, + vcp_on_cdc_control_line, + NULL, +}; + +static CliVcp* vcp = NULL; + +static const uint8_t ascii_soh = 0x01; +static const uint8_t ascii_eot = 0x04; + +static void cli_vcp_init() { + if(vcp == NULL) { + vcp = malloc(sizeof(CliVcp)); + vcp->tx_stream = furi_stream_buffer_alloc(VCP_TX_BUF_SIZE, 1); + vcp->rx_stream = furi_stream_buffer_alloc(VCP_RX_BUF_SIZE, 1); + } + furi_assert(vcp->thread == NULL); + + vcp->connected = false; + + vcp->thread = furi_thread_alloc_ex("CliVcpWorker", 1024, vcp_worker, NULL); + furi_thread_start(vcp->thread); + + FURI_LOG_I(TAG, "Init OK"); +} + +static void cli_vcp_deinit() { + furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtStop); + furi_thread_join(vcp->thread); + furi_thread_free(vcp->thread); + vcp->thread = NULL; +} + +static int32_t vcp_worker(void* context) { + UNUSED(context); + bool tx_idle = true; + size_t missed_rx = 0; + uint8_t last_tx_pkt_len = 0; + + // Switch USB to VCP mode (if it is not set yet) + vcp->usb_if_prev = furi_hal_usb_get_config(); + if((vcp->usb_if_prev != &usb_cdc_single) && (vcp->usb_if_prev != &usb_cdc_dual)) { + furi_hal_usb_set_config(&usb_cdc_single, NULL); + } + furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL); + + FURI_LOG_D(TAG, "Start"); + vcp->running = true; + + while(1) { + uint32_t flags = + furi_thread_flags_wait(VCP_THREAD_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever); + furi_assert(!(flags & FuriFlagError)); + + // VCP session opened + if(flags & VcpEvtConnect) { + VCP_DEBUG("Connect"); + + if(vcp->connected == false) { + vcp->connected = true; + furi_stream_buffer_send(vcp->rx_stream, &ascii_soh, 1, FuriWaitForever); + } + } + + // VCP session closed + if(flags & VcpEvtDisconnect) { + VCP_DEBUG("Disconnect"); + + if(vcp->connected == true) { + vcp->connected = false; + furi_stream_buffer_receive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); + furi_stream_buffer_send(vcp->rx_stream, &ascii_eot, 1, FuriWaitForever); + } + } + + // Rx buffer was read, maybe there is enough space for new data? + if((flags & VcpEvtStreamRx) && (missed_rx > 0)) { + VCP_DEBUG("StreamRx"); + + if(furi_stream_buffer_spaces_available(vcp->rx_stream) >= USB_CDC_PKT_LEN) { + flags |= VcpEvtRx; + missed_rx--; + } + } + + // New data received + if(flags & VcpEvtRx) { + if(furi_stream_buffer_spaces_available(vcp->rx_stream) >= USB_CDC_PKT_LEN) { + int32_t len = furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN); + VCP_DEBUG("Rx %ld", len); + + if(len > 0) { + furi_check( + furi_stream_buffer_send( + vcp->rx_stream, vcp->data_buffer, len, FuriWaitForever) == + (size_t)len); + } + } else { + VCP_DEBUG("Rx missed"); + missed_rx++; + } + } + + // New data in Tx buffer + if(flags & VcpEvtStreamTx) { + VCP_DEBUG("StreamTx"); + + if(tx_idle) { + flags |= VcpEvtTx; + } + } + + // CDC write transfer done + if(flags & VcpEvtTx) { + size_t len = + furi_stream_buffer_receive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); + + VCP_DEBUG("Tx %d", len); + + if(len > 0) { // Some data left in Tx buffer. Sending it now + tx_idle = false; + furi_hal_cdc_send(VCP_IF_NUM, vcp->data_buffer, len); + last_tx_pkt_len = len; + } else { // There is nothing to send. + if(last_tx_pkt_len == 64) { + // Send extra zero-length packet if last packet len is 64 to indicate transfer end + furi_hal_cdc_send(VCP_IF_NUM, NULL, 0); + } else { + // Set flag to start next transfer instantly + tx_idle = true; + } + last_tx_pkt_len = 0; + } + } + + if(flags & VcpEvtStop) { + vcp->connected = false; + vcp->running = false; + furi_hal_cdc_set_callbacks(VCP_IF_NUM, NULL, NULL); + // Restore previous USB mode (if it was set during init) + if((vcp->usb_if_prev != &usb_cdc_single) && (vcp->usb_if_prev != &usb_cdc_dual)) { + furi_hal_usb_unlock(); + furi_hal_usb_set_config(vcp->usb_if_prev, NULL); + } + furi_stream_buffer_receive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); + furi_stream_buffer_send(vcp->rx_stream, &ascii_eot, 1, FuriWaitForever); + break; + } + } + FURI_LOG_D(TAG, "End"); + return 0; +} + +static size_t cli_vcp_rx(uint8_t* buffer, size_t size, uint32_t timeout) { + furi_assert(vcp); + furi_assert(buffer); + + if(vcp->running == false) { + return 0; + } + + VCP_DEBUG("rx %u start", size); + + size_t rx_cnt = 0; + + while(size > 0) { + size_t batch_size = size; + if(batch_size > VCP_RX_BUF_SIZE) batch_size = VCP_RX_BUF_SIZE; + + size_t len = furi_stream_buffer_receive(vcp->rx_stream, buffer, batch_size, timeout); + VCP_DEBUG("rx %u ", batch_size); + + if(len == 0) break; + if(vcp->running == false) { + // EOT command is received after VCP session close + rx_cnt += len; + break; + } + furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtStreamRx); + size -= len; + buffer += len; + rx_cnt += len; + } + + VCP_DEBUG("rx %u end", size); + return rx_cnt; +} + +static void cli_vcp_tx(const uint8_t* buffer, size_t size) { + furi_assert(vcp); + furi_assert(buffer); + + if(vcp->running == false) { + return; + } + + VCP_DEBUG("tx %u start", size); + + while(size > 0 && vcp->connected) { + size_t batch_size = size; + if(batch_size > USB_CDC_PKT_LEN) batch_size = USB_CDC_PKT_LEN; + + furi_stream_buffer_send(vcp->tx_stream, buffer, batch_size, FuriWaitForever); + furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtStreamTx); + VCP_DEBUG("tx %u", batch_size); + + size -= batch_size; + buffer += batch_size; + } + + VCP_DEBUG("tx %u end", size); +} + +static void cli_vcp_tx_stdout(const char* data, size_t size) { + cli_vcp_tx((const uint8_t*)data, size); +} + +static void vcp_state_callback(void* context, uint8_t state) { + UNUSED(context); + if(state == 0) { + furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtDisconnect); + } +} + +static void vcp_on_cdc_control_line(void* context, uint8_t state) { + UNUSED(context); + // bit 0: DTR state, bit 1: RTS state + bool dtr = state & (1 << 0); + + if(dtr == true) { + furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtConnect); + } else { + furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtDisconnect); + } +} + +static void vcp_on_cdc_rx(void* context) { + UNUSED(context); + uint32_t ret = furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtRx); + furi_check(!(ret & FuriFlagError)); +} + +static void vcp_on_cdc_tx_complete(void* context) { + UNUSED(context); + furi_thread_flags_set(furi_thread_get_id(vcp->thread), VcpEvtTx); +} + +static bool cli_vcp_is_connected(void) { + furi_assert(vcp); + return vcp->connected; +} + +CliSession cli_vcp = { + cli_vcp_init, + cli_vcp_deinit, + cli_vcp_rx, + cli_vcp_tx, + cli_vcp_tx_stdout, + cli_vcp_is_connected, +}; diff --git a/applications/cli/cli_vcp.h b/applications/services/cli/cli_vcp.h similarity index 80% rename from applications/cli/cli_vcp.h rename to applications/services/cli/cli_vcp.h index 2bd47efb28a..3aef2ef7083 100644 --- a/applications/cli/cli_vcp.h +++ b/applications/services/cli/cli_vcp.h @@ -5,12 +5,12 @@ #pragma once -#include "cli_i.h" - #ifdef __cplusplus extern "C" { #endif +typedef struct CliSession CliSession; + extern CliSession cli_vcp; #ifdef __cplusplus diff --git a/applications/crypto/application.fam b/applications/services/crypto/application.fam similarity index 100% rename from applications/crypto/application.fam rename to applications/services/crypto/application.fam diff --git a/applications/crypto/crypto_cli.c b/applications/services/crypto/crypto_cli.c similarity index 77% rename from applications/crypto/crypto_cli.c rename to applications/services/crypto/crypto_cli.c index 26e1fb9c0db..af52b84fae3 100644 --- a/applications/crypto/crypto_cli.c +++ b/applications/services/crypto/crypto_cli.c @@ -17,7 +17,7 @@ void crypto_cli_print_usage() { "\tstore_key \t - Store key in secure enclave. !!! NON-REVERSABLE OPERATION - READ MANUAL FIRST !!!\r\n"); }; -void crypto_cli_encrypt(Cli* cli, string_t args) { +void crypto_cli_encrypt(Cli* cli, FuriString* args) { int key_slot = 0; bool key_loaded = false; uint8_t iv[16]; @@ -33,7 +33,7 @@ void crypto_cli_encrypt(Cli* cli, string_t args) { break; } - if(!furi_hal_crypto_store_load_key(key_slot, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) { printf("Unable to load key from slot %d", key_slot); break; } @@ -41,8 +41,8 @@ void crypto_cli_encrypt(Cli* cli, string_t args) { printf("Enter plain text and press Ctrl+C to complete encryption:\r\n"); - string_t input; - string_init(input); + FuriString* input; + input = furi_string_alloc(); char c; while(cli_read(cli, (uint8_t*)&c, 1) == 1) { if(c == CliSymbolAsciiETX) { @@ -51,14 +51,14 @@ void crypto_cli_encrypt(Cli* cli, string_t args) { } else if(c >= 0x20 && c < 0x7F) { putc(c, stdout); fflush(stdout); - string_push_back(input, c); + furi_string_push_back(input, c); } else if(c == CliSymbolAsciiCR) { printf("\r\n"); - string_cat_str(input, "\r\n"); + furi_string_cat(input, "\r\n"); } } - size_t size = string_size(input); + size_t size = furi_string_size(input); if(size > 0) { // C-string null termination and block alignments size++; @@ -66,9 +66,10 @@ void crypto_cli_encrypt(Cli* cli, string_t args) { if(remain) { size = size - remain + 16; } - string_reserve(input, size); + furi_string_reserve(input, size); uint8_t* output = malloc(size); - if(!furi_hal_crypto_encrypt((const uint8_t*)string_get_cstr(input), output, size)) { + if(!furi_hal_crypto_encrypt( + (const uint8_t*)furi_string_get_cstr(input), output, size)) { printf("Failed to encrypt input"); } else { printf("Hex-encoded encrypted data:\r\n"); @@ -83,15 +84,15 @@ void crypto_cli_encrypt(Cli* cli, string_t args) { printf("No input"); } - string_clear(input); + furi_string_free(input); } while(0); if(key_loaded) { - furi_hal_crypto_store_unload_key(key_slot); + furi_hal_crypto_enclave_unload_key(key_slot); } } -void crypto_cli_decrypt(Cli* cli, string_t args) { +void crypto_cli_decrypt(Cli* cli, FuriString* args) { int key_slot = 0; bool key_loaded = false; uint8_t iv[16]; @@ -107,7 +108,7 @@ void crypto_cli_decrypt(Cli* cli, string_t args) { break; } - if(!furi_hal_crypto_store_load_key(key_slot, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) { printf("Unable to load key from slot %d", key_slot); break; } @@ -115,8 +116,8 @@ void crypto_cli_decrypt(Cli* cli, string_t args) { printf("Enter Hex-encoded data and press Ctrl+C to complete decryption:\r\n"); - string_t hex_input; - string_init(hex_input); + FuriString* hex_input; + hex_input = furi_string_alloc(); char c; while(cli_read(cli, (uint8_t*)&c, 1) == 1) { if(c == CliSymbolAsciiETX) { @@ -125,14 +126,14 @@ void crypto_cli_decrypt(Cli* cli, string_t args) { } else if(c >= 0x20 && c < 0x7F) { putc(c, stdout); fflush(stdout); - string_push_back(hex_input, c); + furi_string_push_back(hex_input, c); } else if(c == CliSymbolAsciiCR) { printf("\r\n"); } } - string_strim(hex_input); - size_t hex_size = string_size(hex_input); + furi_string_trim(hex_input); + size_t hex_size = furi_string_size(hex_input); if(hex_size > 0 && hex_size % 2 == 0) { size_t size = hex_size / 2; uint8_t* input = malloc(size); @@ -141,7 +142,7 @@ void crypto_cli_decrypt(Cli* cli, string_t args) { if(args_read_hex_bytes(hex_input, input, size)) { if(furi_hal_crypto_decrypt(input, output, size)) { printf("Decrypted data:\r\n"); - printf("%s\r\n", output); + printf("%s\r\n", output); //-V576 } else { printf("Failed to decrypt\r\n"); } @@ -155,18 +156,18 @@ void crypto_cli_decrypt(Cli* cli, string_t args) { printf("Invalid or empty input"); } - string_clear(hex_input); + furi_string_free(hex_input); } while(0); if(key_loaded) { - furi_hal_crypto_store_unload_key(key_slot); + furi_hal_crypto_enclave_unload_key(key_slot); } } -void crypto_cli_has_key(Cli* cli, string_t args) { +void crypto_cli_has_key(Cli* cli, FuriString* args) { UNUSED(cli); int key_slot = 0; - uint8_t iv[16]; + uint8_t iv[16] = {0}; do { if(!args_read_int_and_trim(args, &key_slot) || !(key_slot > 0 && key_slot <= 100)) { @@ -174,23 +175,23 @@ void crypto_cli_has_key(Cli* cli, string_t args) { break; } - if(!furi_hal_crypto_store_load_key(key_slot, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) { printf("Unable to load key from slot %d", key_slot); break; } printf("Successfully loaded key from slot %d", key_slot); - furi_hal_crypto_store_unload_key(key_slot); + furi_hal_crypto_enclave_unload_key(key_slot); } while(0); } -void crypto_cli_store_key(Cli* cli, string_t args) { +void crypto_cli_store_key(Cli* cli, FuriString* args) { UNUSED(cli); int key_slot = 0; int key_size = 0; - string_t key_type; - string_init(key_type); + FuriString* key_type; + key_type = furi_string_alloc(); uint8_t data[32 + 12] = {}; FuriHalCryptoKey key; @@ -207,19 +208,19 @@ void crypto_cli_store_key(Cli* cli, string_t args) { break; } - if(string_cmp_str(key_type, "master") == 0) { + if(furi_string_cmp_str(key_type, "master") == 0) { if(key_slot != 0) { printf("Master keyslot must be is 0"); break; } key.type = FuriHalCryptoKeyTypeMaster; - } else if(string_cmp_str(key_type, "simple") == 0) { + } else if(furi_string_cmp_str(key_type, "simple") == 0) { if(key_slot < 1 || key_slot > 99) { printf("Simple keyslot must be in range"); break; } key.type = FuriHalCryptoKeyTypeSimple; - } else if(string_cmp_str(key_type, "encrypted") == 0) { + } else if(furi_string_cmp_str(key_type, "encrypted") == 0) { key.type = FuriHalCryptoKeyTypeEncrypted; data_size += 12; } else { @@ -248,40 +249,40 @@ void crypto_cli_store_key(Cli* cli, string_t args) { } if(key_slot > 0) { - uint8_t iv[16]; + uint8_t iv[16] = {0}; if(key_slot > 1) { - if(!furi_hal_crypto_store_load_key(key_slot - 1, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot - 1, iv)) { printf( "Slot %d before %d is empty, which is not allowed", key_slot - 1, key_slot); break; } - furi_hal_crypto_store_unload_key(key_slot - 1); + furi_hal_crypto_enclave_unload_key(key_slot - 1); } - if(furi_hal_crypto_store_load_key(key_slot, iv)) { - furi_hal_crypto_store_unload_key(key_slot); + if(furi_hal_crypto_enclave_load_key(key_slot, iv)) { + furi_hal_crypto_enclave_unload_key(key_slot); printf("Key slot %d is already used", key_slot); break; } } uint8_t slot; - if(furi_hal_crypto_store_add_key(&key, &slot)) { + if(furi_hal_crypto_enclave_store_key(&key, &slot)) { printf("Success. Stored to slot: %d", slot); } else { printf("Failure"); } } while(0); - string_clear(key_type); + furi_string_free(key_type); } -static void crypto_cli(Cli* cli, string_t args, void* context) { +static void crypto_cli(Cli* cli, FuriString* args, void* context) { UNUSED(context); - string_t cmd; - string_init(cmd); + FuriString* cmd; + cmd = furi_string_alloc(); do { if(!args_read_string_and_trim(args, cmd)) { @@ -289,22 +290,22 @@ static void crypto_cli(Cli* cli, string_t args, void* context) { break; } - if(string_cmp_str(cmd, "encrypt") == 0) { + if(furi_string_cmp_str(cmd, "encrypt") == 0) { crypto_cli_encrypt(cli, args); break; } - if(string_cmp_str(cmd, "decrypt") == 0) { + if(furi_string_cmp_str(cmd, "decrypt") == 0) { crypto_cli_decrypt(cli, args); break; } - if(string_cmp_str(cmd, "has_key") == 0) { + if(furi_string_cmp_str(cmd, "has_key") == 0) { crypto_cli_has_key(cli, args); break; } - if(string_cmp_str(cmd, "store_key") == 0) { + if(furi_string_cmp_str(cmd, "store_key") == 0) { crypto_cli_store_key(cli, args); break; } @@ -312,7 +313,7 @@ static void crypto_cli(Cli* cli, string_t args, void* context) { crypto_cli_print_usage(); } while(false); - string_clear(cmd); + furi_string_free(cmd); } void crypto_on_system_start() { diff --git a/applications/desktop/animations/animation_manager.c b/applications/services/desktop/animations/animation_manager.c similarity index 93% rename from applications/desktop/animations/animation_manager.c rename to applications/services/desktop/animations/animation_manager.c index d755be9c048..44c0c228c4e 100644 --- a/applications/desktop/animations/animation_manager.c +++ b/applications/services/desktop/animations/animation_manager.c @@ -2,8 +2,6 @@ #include #include #include -#include -#include #include #include #include @@ -50,9 +48,10 @@ struct AnimationManager { AnimationManagerSetNewIdleAnimationCallback new_idle_callback; AnimationManagerSetNewIdleAnimationCallback check_blocking_callback; void* context; - string_t freezed_animation_name; + FuriString* freezed_animation_name; int32_t freezed_animation_time_left; ViewStack* view_stack; + bool dummy_mode; }; static StorageAnimation* @@ -94,6 +93,12 @@ void animation_manager_set_interact_callback( animation_manager->interact_callback = callback; } +void animation_manager_set_dummy_mode_state(AnimationManager* animation_manager, bool enabled) { + furi_assert(animation_manager); + animation_manager->dummy_mode = enabled; + animation_manager_start_new_idle(animation_manager); +} + static void animation_manager_check_blocking_callback(const void* message, void* context) { const StorageEvent* storage_event = message; @@ -220,8 +225,7 @@ static bool animation_manager_check_blocking(AnimationManager* animation_manager furi_assert(blocking_animation); animation_manager->sd_shown_sd_ok = true; } else if(!animation_manager->sd_shown_no_db) { - bool db_exists = storage_common_stat(storage, EXT_PATH("Manifest"), NULL) == FSE_OK; - if(!db_exists) { + if(!storage_file_exists(storage, EXT_PATH("Manifest"))) { blocking_animation = animation_storage_find_animation(NO_DB_ANIMATION_NAME); furi_assert(blocking_animation); animation_manager->sd_shown_no_db = true; @@ -239,10 +243,8 @@ static bool animation_manager_check_blocking(AnimationManager* animation_manager furi_record_close(RECORD_DOLPHIN); if(!blocking_animation && stats.level_up_is_pending) { blocking_animation = animation_storage_find_animation(NEW_MAIL_ANIMATION_NAME); - furi_assert(blocking_animation); - if(blocking_animation) { - animation_manager->levelup_pending = true; - } + furi_check(blocking_animation); + animation_manager->levelup_pending = true; } if(blocking_animation) { @@ -280,7 +282,7 @@ AnimationManager* animation_manager_alloc(void) { animation_manager->view_stack = view_stack_alloc(); View* animation_view = bubble_animation_get_view(animation_manager->animation_view); view_stack_add_view(animation_manager->view_stack, animation_view); - string_init(animation_manager->freezed_animation_name); + animation_manager->freezed_animation_name = furi_string_alloc(); animation_manager->idle_animation_timer = furi_timer_alloc(animation_manager_timer_callback, FuriTimerTypeOnce, animation_manager); @@ -318,7 +320,7 @@ void animation_manager_free(AnimationManager* animation_manager) { storage_get_pubsub(storage), animation_manager->pubsub_subscription_storage); furi_record_close(RECORD_STORAGE); - string_clear(animation_manager->freezed_animation_name); + furi_string_free(animation_manager->freezed_animation_name); View* animation_view = bubble_animation_get_view(animation_manager->animation_view); view_stack_remove_view(animation_manager->view_stack, animation_view); bubble_animation_view_free(animation_manager->animation_view); @@ -365,7 +367,9 @@ static bool animation_manager_is_valid_idle_animation( static StorageAnimation* animation_manager_select_idle_animation(AnimationManager* animation_manager) { - UNUSED(animation_manager); + if(animation_manager->dummy_mode) { + return animation_storage_find_animation(HARDCODED_ANIMATION_NAME); + } StorageAnimationList_t animation_list; StorageAnimationList_init(animation_list); animation_storage_fill_animation_list(&animation_list); @@ -434,24 +438,24 @@ bool animation_manager_is_animation_loaded(AnimationManager* animation_manager) void animation_manager_unload_and_stall_animation(AnimationManager* animation_manager) { furi_assert(animation_manager); furi_assert(animation_manager->current_animation); - furi_assert(!string_size(animation_manager->freezed_animation_name)); + furi_assert(!furi_string_size(animation_manager->freezed_animation_name)); furi_assert( (animation_manager->state == AnimationManagerStateIdle) || (animation_manager->state == AnimationManagerStateBlocked)); if(animation_manager->state == AnimationManagerStateBlocked) { animation_manager->state = AnimationManagerStateFreezedBlocked; - } else if(animation_manager->state == AnimationManagerStateIdle) { + } else if(animation_manager->state == AnimationManagerStateIdle) { //-V547 animation_manager->state = AnimationManagerStateFreezedIdle; animation_manager->freezed_animation_time_left = - xTimerGetExpiryTime(animation_manager->idle_animation_timer) - xTaskGetTickCount(); + furi_timer_get_expire_time(animation_manager->idle_animation_timer) - furi_get_tick(); if(animation_manager->freezed_animation_time_left < 0) { animation_manager->freezed_animation_time_left = 0; } furi_timer_stop(animation_manager->idle_animation_timer); } else { - furi_assert(0); + furi_crash(); } FURI_LOG_I( @@ -462,7 +466,7 @@ void animation_manager_unload_and_stall_animation(AnimationManager* animation_ma StorageAnimationManifestInfo* meta = animation_storage_get_meta(animation_manager->current_animation); /* copy str, not move, because it can be internal animation */ - string_set_str(animation_manager->freezed_animation_name, meta->name); + furi_string_set(animation_manager->freezed_animation_name, meta->name); bubble_animation_freeze(animation_manager->animation_view); animation_storage_free_storage_animation(&animation_manager->current_animation); @@ -471,26 +475,26 @@ void animation_manager_unload_and_stall_animation(AnimationManager* animation_ma void animation_manager_load_and_continue_animation(AnimationManager* animation_manager) { furi_assert(animation_manager); furi_assert(!animation_manager->current_animation); - furi_assert(string_size(animation_manager->freezed_animation_name)); + furi_assert(furi_string_size(animation_manager->freezed_animation_name)); furi_assert( (animation_manager->state == AnimationManagerStateFreezedIdle) || (animation_manager->state == AnimationManagerStateFreezedBlocked)); if(animation_manager->state == AnimationManagerStateFreezedBlocked) { StorageAnimation* restore_animation = animation_storage_find_animation( - string_get_cstr(animation_manager->freezed_animation_name)); + furi_string_get_cstr(animation_manager->freezed_animation_name)); /* all blocked animations must be in flipper -> we can * always find blocking animation */ furi_assert(restore_animation); animation_manager_replace_current_animation(animation_manager, restore_animation); animation_manager->state = AnimationManagerStateBlocked; - } else if(animation_manager->state == AnimationManagerStateFreezedIdle) { + } else if(animation_manager->state == AnimationManagerStateFreezedIdle) { //-V547 /* check if we missed some system notifications, and set current_animation */ bool blocked = animation_manager_check_blocking(animation_manager); if(!blocked) { /* if no blocking - try restore last one idle */ StorageAnimation* restore_animation = animation_storage_find_animation( - string_get_cstr(animation_manager->freezed_animation_name)); + furi_string_get_cstr(animation_manager->freezed_animation_name)); if(restore_animation) { Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); DolphinStats stats = dolphin_stats(dolphin); @@ -518,12 +522,12 @@ void animation_manager_load_and_continue_animation(AnimationManager* animation_m FURI_LOG_E( TAG, "Failed to restore \'%s\'", - string_get_cstr(animation_manager->freezed_animation_name)); + furi_string_get_cstr(animation_manager->freezed_animation_name)); } } } else { /* Unknown state is an error. But not in release version.*/ - furi_assert(0); + furi_crash(); } /* if can't restore previous animation - select new */ @@ -536,7 +540,7 @@ void animation_manager_load_and_continue_animation(AnimationManager* animation_m animation_storage_get_meta(animation_manager->current_animation)->name); bubble_animation_unfreeze(animation_manager->animation_view); - string_reset(animation_manager->freezed_animation_name); + furi_string_reset(animation_manager->freezed_animation_name); furi_assert(animation_manager->current_animation); } @@ -559,7 +563,7 @@ static void animation_manager_switch_to_one_shot_view(AnimationManager* animatio } else if(stats.level == 2) { one_shot_view_start_animation(animation_manager->one_shot_view, &A_Levelup2_128x64); } else { - furi_assert(0); + furi_crash(); } } diff --git a/applications/desktop/animations/animation_manager.h b/applications/services/desktop/animations/animation_manager.h similarity index 91% rename from applications/desktop/animations/animation_manager.h rename to applications/services/desktop/animations/animation_manager.h index 9802c4f1f62..a3dcc882794 100644 --- a/applications/desktop/animations/animation_manager.h +++ b/applications/services/desktop/animations/animation_manager.h @@ -69,7 +69,7 @@ View* animation_manager_get_animation_view(AnimationManager* animation_manager); void animation_manager_set_context(AnimationManager* animation_manager, void* context); /** - * Set callback for Animation Manager for defered calls + * Set callback for Animation Manager for deferred calls * for animation_manager_new_idle_process(). * Animation Manager doesn't have it's own thread, so main thread gives * callbacks to A.M. to call when it should perform some inner manipulations. @@ -96,7 +96,7 @@ void animation_manager_set_new_idle_callback( void animation_manager_new_idle_process(AnimationManager* animation_manager); /** - * Set callback for Animation Manager for defered calls + * Set callback for Animation Manager for deferred calls * for animation_manager_check_blocking_process(). * * @animation_manager instance @@ -115,7 +115,7 @@ void animation_manager_set_check_callback( void animation_manager_check_blocking_process(AnimationManager* animation_manager); /** - * Set callback for Animation Manager for defered calls + * Set callback for Animation Manager for deferred calls * for animation_manager_interact_process(). * * @animation_manager instance @@ -157,3 +157,11 @@ void animation_manager_unload_and_stall_animation(AnimationManager* animation_ma * @animation_manager instance */ void animation_manager_load_and_continue_animation(AnimationManager* animation_manager); + +/** + * Enable or disable dummy mode backgrounds of animation manager. + * + * @animation_manager instance + * @enabled bool + */ +void animation_manager_set_dummy_mode_state(AnimationManager* animation_manager, bool enabled); diff --git a/applications/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c similarity index 86% rename from applications/desktop/animations/animation_storage.c rename to applications/services/desktop/animations/animation_storage.c index ba7a7163e59..fb62c36988c 100644 --- a/applications/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -5,7 +5,6 @@ #include #include #include -#include #include "animation_manager.h" #include "animation_storage.h" @@ -35,8 +34,8 @@ static bool animation_storage_load_single_manifest_info( Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* file = flipper_format_file_alloc(storage); flipper_format_set_strict_mode(file, true); - string_t read_string; - string_init(read_string); + FuriString* read_string; + read_string = furi_string_alloc(); do { uint32_t u32value; @@ -44,20 +43,20 @@ static bool animation_storage_load_single_manifest_info( if(!flipper_format_file_open_existing(file, ANIMATION_MANIFEST_FILE)) break; if(!flipper_format_read_header(file, read_string, &u32value)) break; - if(string_cmp_str(read_string, "Flipper Animation Manifest")) break; + if(furi_string_cmp_str(read_string, "Flipper Animation Manifest")) break; manifest_info->name = NULL; /* skip other animation names */ flipper_format_set_strict_mode(file, false); while(flipper_format_read_string(file, "Name", read_string) && - string_cmp_str(read_string, name)) + furi_string_cmp_str(read_string, name)) ; - if(string_cmp_str(read_string, name)) break; + if(furi_string_cmp_str(read_string, name)) break; flipper_format_set_strict_mode(file, true); - manifest_info->name = malloc(string_size(read_string) + 1); - strcpy((char*)manifest_info->name, string_get_cstr(read_string)); + manifest_info->name = malloc(furi_string_size(read_string) + 1); + strcpy((char*)manifest_info->name, furi_string_get_cstr(read_string)); if(!flipper_format_read_uint32(file, "Min butthurt", &u32value, 1)) break; manifest_info->min_butthurt = u32value; @@ -75,7 +74,7 @@ static bool animation_storage_load_single_manifest_info( if(!result && manifest_info->name) { free((void*)manifest_info->name); } - string_clear(read_string); + furi_string_free(read_string); flipper_format_free(file); furi_record_close(RECORD_STORAGE); @@ -91,8 +90,8 @@ void animation_storage_fill_animation_list(StorageAnimationList_t* animation_lis FlipperFormat* file = flipper_format_file_alloc(storage); /* Forbid skipping fields */ flipper_format_set_strict_mode(file, true); - string_t read_string; - string_init(read_string); + FuriString* read_string; + read_string = furi_string_alloc(); do { uint32_t u32value; @@ -101,7 +100,7 @@ void animation_storage_fill_animation_list(StorageAnimationList_t* animation_lis if(FSE_OK != storage_sd_status(storage)) break; if(!flipper_format_file_open_existing(file, ANIMATION_MANIFEST_FILE)) break; if(!flipper_format_read_header(file, read_string, &u32value)) break; - if(string_cmp_str(read_string, "Flipper Animation Manifest")) break; + if(furi_string_cmp_str(read_string, "Flipper Animation Manifest")) break; do { storage_animation = malloc(sizeof(StorageAnimation)); storage_animation->external = true; @@ -109,8 +108,9 @@ void animation_storage_fill_animation_list(StorageAnimationList_t* animation_lis storage_animation->manifest_info.name = NULL; if(!flipper_format_read_string(file, "Name", read_string)) break; - storage_animation->manifest_info.name = malloc(string_size(read_string) + 1); - strcpy((char*)storage_animation->manifest_info.name, string_get_cstr(read_string)); + storage_animation->manifest_info.name = malloc(furi_string_size(read_string) + 1); + strcpy( + (char*)storage_animation->manifest_info.name, furi_string_get_cstr(read_string)); if(!flipper_format_read_uint32(file, "Min butthurt", &u32value, 1)) break; storage_animation->manifest_info.min_butthurt = u32value; @@ -129,7 +129,7 @@ void animation_storage_fill_animation_list(StorageAnimationList_t* animation_lis animation_storage_free_storage_animation(&storage_animation); } while(0); - string_clear(read_string); + furi_string_free(read_string); flipper_format_free(file); // add hard-coded animations @@ -234,16 +234,16 @@ void animation_storage_free_storage_animation(StorageAnimation** storage_animati *storage_animation = NULL; } -static bool animation_storage_cast_align(string_t align_str, Align* align) { - if(!string_cmp_str(align_str, "Bottom")) { +static bool animation_storage_cast_align(FuriString* align_str, Align* align) { + if(!furi_string_cmp(align_str, "Bottom")) { *align = AlignBottom; - } else if(!string_cmp_str(align_str, "Top")) { + } else if(!furi_string_cmp(align_str, "Top")) { *align = AlignTop; - } else if(!string_cmp_str(align_str, "Left")) { + } else if(!furi_string_cmp(align_str, "Left")) { *align = AlignLeft; - } else if(!string_cmp_str(align_str, "Right")) { + } else if(!furi_string_cmp(align_str, "Right")) { *align = AlignRight; - } else if(!string_cmp_str(align_str, "Center")) { + } else if(!furi_string_cmp(align_str, "Center")) { *align = AlignCenter; } else { return false; @@ -294,8 +294,8 @@ static bool animation_storage_load_frames( bool frames_ok = false; File* file = storage_file_alloc(storage); FileInfo file_info; - string_t filename; - string_init(filename); + FuriString* filename; + filename = furi_string_alloc(); size_t max_filesize = ROUND_UP_TO(width, 8) * height + 1; DesktopSettings* desktop_settings = malloc(sizeof(DesktopSettings)); @@ -303,31 +303,37 @@ static bool animation_storage_load_frames( for(int i = 0; i < icon->frame_count; ++i) { frames_ok = false; +<<<<<<< HEAD:applications/desktop/animations/animation_storage.c if(!desktop_settings->is_inverted) { string_printf(filename, ANIMATION_DIR "/%s/frame_%d.bm", name, i); } else { string_printf(filename, ANIMATION_DIR_INVERTED "/%s/frame_%d.bm", name, i); } +======= + furi_string_printf(filename, ANIMATION_DIR "/%s/frame_%d.bm", name, i); +>>>>>>> upstream/dev:applications/services/desktop/animations/animation_storage.c - if(storage_common_stat(storage, string_get_cstr(filename), &file_info) != FSE_OK) break; + 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 %d, max: %d (width %d, height %d)", + "Filesize %llu, max: %zu (width %u, height %u)", file_info.size, max_filesize, width, height); break; } - if(!storage_file_open(file, string_get_cstr(filename), FSAM_READ, FSOM_OPEN_EXISTING)) { - FURI_LOG_E(TAG, "Can't open file \'%s\'", string_get_cstr(filename)); + 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\'", string_get_cstr(filename)); + FURI_LOG_E(TAG, "Read failed: \'%s\'", furi_string_get_cstr(filename)); break; } storage_file_close(file); @@ -337,8 +343,8 @@ static bool animation_storage_load_frames( if(!frames_ok) { FURI_LOG_E( TAG, - "Load \'%s\' failed, %dx%d, size: %d", - string_get_cstr(filename), + "Load \'%s\' failed, %ux%u, size: %llu", + furi_string_get_cstr(filename), width, height, file_info.size); @@ -351,15 +357,15 @@ static bool animation_storage_load_frames( } storage_file_free(file); - string_clear(filename); + furi_string_free(filename); return frames_ok; } static bool animation_storage_load_bubbles(BubbleAnimation* animation, FlipperFormat* ff) { uint32_t u32value; - string_t str; - string_init(str); + FuriString* str; + str = furi_string_alloc(); bool success = false; furi_assert(!animation->frame_bubble_sequences); @@ -368,7 +374,6 @@ static bool animation_storage_load_bubbles(BubbleAnimation* animation, FlipperFo if(u32value > 20) break; animation->frame_bubble_sequences_count = u32value; if(animation->frame_bubble_sequences_count == 0) { - animation->frame_bubble_sequences = NULL; success = true; break; } @@ -406,12 +411,12 @@ static bool animation_storage_load_bubbles(BubbleAnimation* animation, FlipperFo FURI_CONST_ASSIGN(bubble->bubble.y, u32value); if(!flipper_format_read_string(ff, "Text", str)) break; - if(string_size(str) > 100) break; + if(furi_string_size(str) > 100) break; - string_replace_all_str(str, "\\n", "\n"); + furi_string_replace_all(str, "\\n", "\n"); - FURI_CONST_ASSIGN_PTR(bubble->bubble.text, malloc(string_size(str) + 1)); - strcpy((char*)bubble->bubble.text, string_get_cstr(str)); + FURI_CONST_ASSIGN_PTR(bubble->bubble.text, malloc(furi_string_size(str) + 1)); + strcpy((char*)bubble->bubble.text, furi_string_get_cstr(str)); if(!flipper_format_read_string(ff, "AlignH", str)) break; if(!animation_storage_cast_align(str, (Align*)&bubble->bubble.align_h)) break; @@ -433,7 +438,7 @@ static bool animation_storage_load_bubbles(BubbleAnimation* animation, FlipperFo } } - string_clear(str); + furi_string_free(str); return success; } @@ -448,8 +453,8 @@ static BubbleAnimation* animation_storage_load_animation(const char* name) { FlipperFormat* ff = flipper_format_file_alloc(storage); /* Forbid skipping fields */ flipper_format_set_strict_mode(ff, true); - string_t str; - string_init(str); + FuriString* str; + str = furi_string_alloc(); animation->frame_bubble_sequences = NULL; bool success = false; @@ -458,10 +463,10 @@ static BubbleAnimation* animation_storage_load_animation(const char* name) { if(FSE_OK != storage_sd_status(storage)) break; - string_printf(str, ANIMATION_DIR "/%s/" ANIMATION_META_FILE, name); - if(!flipper_format_file_open_existing(ff, string_get_cstr(str))) break; + furi_string_printf(str, ANIMATION_DIR "/%s/" ANIMATION_META_FILE, name); + if(!flipper_format_file_open_existing(ff, furi_string_get_cstr(str))) break; if(!flipper_format_read_header(ff, str, &u32value)) break; - if(string_cmp_str(str, "Flipper Animation")) break; + if(furi_string_cmp_str(str, "Flipper Animation")) break; if(!flipper_format_read_uint32(ff, "Width", &width, 1)) break; if(!flipper_format_read_uint32(ff, "Height", &height, 1)) break; @@ -489,7 +494,7 @@ static BubbleAnimation* animation_storage_load_animation(const char* name) { if(!animation_storage_load_frames(storage, name, animation, u32array, width, height)) break; - if(!flipper_format_read_uint32(ff, "Active cycles", &u32value, 1)) break; + if(!flipper_format_read_uint32(ff, "Active cycles", &u32value, 1)) break; //-V779 animation->active_cycles = u32value; if(!flipper_format_read_uint32(ff, "Frame rate", &u32value, 1)) break; FURI_CONST_ASSIGN(animation->icon_animation.frame_rate, u32value); @@ -502,13 +507,13 @@ static BubbleAnimation* animation_storage_load_animation(const char* name) { success = true; } while(0); - string_clear(str); + furi_string_free(str); flipper_format_free(ff); if(u32array) { free(u32array); } - if(!success) { + if(!success) { //-V547 if(animation->frame_order) { free((void*)animation->frame_order); } diff --git a/applications/desktop/animations/animation_storage.h b/applications/services/desktop/animations/animation_storage.h similarity index 99% rename from applications/desktop/animations/animation_storage.h rename to applications/services/desktop/animations/animation_storage.h index e53c1133fbe..16c0feab4b1 100644 --- a/applications/desktop/animations/animation_storage.h +++ b/applications/services/desktop/animations/animation_storage.h @@ -2,7 +2,6 @@ #include #include #include "views/bubble_animation_view.h" -#include /** Main structure to handle animation data. * Contains all, including animation playing data (BubbleAnimation), diff --git a/applications/desktop/animations/animation_storage_i.h b/applications/services/desktop/animations/animation_storage_i.h similarity index 100% rename from applications/desktop/animations/animation_storage_i.h rename to applications/services/desktop/animations/animation_storage_i.h diff --git a/applications/desktop/animations/views/bubble_animation_view.c b/applications/services/desktop/animations/views/bubble_animation_view.c similarity index 98% rename from applications/desktop/animations/views/bubble_animation_view.c rename to applications/services/desktop/animations/views/bubble_animation_view.c index 607862d1133..9585b2771e0 100644 --- a/applications/desktop/animations/views/bubble_animation_view.c +++ b/applications/services/desktop/animations/views/bubble_animation_view.c @@ -23,7 +23,7 @@ typedef struct { uint8_t active_bubbles; uint8_t passive_bubbles; uint8_t active_shift; - TickType_t active_ended_at; + uint32_t active_ended_at; Icon* freeze_frame; } BubbleAnimationViewModel; @@ -128,8 +128,8 @@ static bool bubble_animation_input_callback(InputEvent* event, void* context) { if(event->key == InputKeyRight) { /* Right button reserved for animation activation, so consume */ - consumed = true; if(event->type == InputTypeShort) { + consumed = true; if(animation_view->interact_callback) { animation_view->interact_callback(animation_view->interact_callback_context); } @@ -154,7 +154,7 @@ static void bubble_animation_activate(BubbleAnimationView* view, bool force) { if(model->current != NULL) { if(!force) { if((model->active_ended_at + model->current->active_cooldown * 1000) > - xTaskGetTickCount()) { + furi_get_tick()) { activate = false; } else if(model->active_shift) { activate = false; @@ -215,7 +215,7 @@ static void bubble_animation_next_frame(BubbleAnimationViewModel* model) { model->active_cycle = 0; model->current_frame = 0; model->current_bubble = bubble_animation_pick_bubble(model, false); - model->active_ended_at = xTaskGetTickCount(); + model->active_ended_at = furi_get_tick(); } if(model->current_bubble) { @@ -355,7 +355,7 @@ void bubble_animation_view_set_animation( furi_assert(model); model->current = new_animation; - model->active_ended_at = xTaskGetTickCount() - (model->current->active_cooldown * 1000); + model->active_ended_at = furi_get_tick() - (model->current->active_cooldown * 1000); model->active_bubbles = 0; model->passive_bubbles = 0; for(int i = 0; i < new_animation->frame_bubble_sequences_count; ++i) { diff --git a/applications/desktop/animations/views/bubble_animation_view.h b/applications/services/desktop/animations/views/bubble_animation_view.h similarity index 100% rename from applications/desktop/animations/views/bubble_animation_view.h rename to applications/services/desktop/animations/views/bubble_animation_view.h diff --git a/applications/desktop/animations/views/one_shot_animation_view.c b/applications/services/desktop/animations/views/one_shot_animation_view.c similarity index 87% rename from applications/desktop/animations/views/one_shot_animation_view.c rename to applications/services/desktop/animations/views/one_shot_animation_view.c index 9a4dff06c59..004fcde7b56 100644 --- a/applications/desktop/animations/views/one_shot_animation_view.c +++ b/applications/services/desktop/animations/views/one_shot_animation_view.c @@ -1,7 +1,6 @@ #include "one_shot_animation_view.h" #include -#include #include #include #include @@ -11,7 +10,7 @@ typedef void (*OneShotInteractCallback)(void*); struct OneShotView { View* view; - TimerHandle_t update_timer; + FuriTimer* update_timer; OneShotInteractCallback interact_callback; void* interact_callback_context; }; @@ -22,8 +21,8 @@ typedef struct { bool block_input; } OneShotViewModel; -static void one_shot_view_update_timer_callback(TimerHandle_t xTimer) { - OneShotView* view = (void*)pvTimerGetTimerID(xTimer); +static void one_shot_view_update_timer_callback(void* context) { + OneShotView* view = context; OneShotViewModel* model = view_get_model(view->view); if((model->index + 1) < model->icon->frame_count) { @@ -65,8 +64,8 @@ static bool one_shot_view_input(InputEvent* event, void* context) { if(!consumed) { if(event->key == InputKeyRight) { /* Right button reserved for animation activation, so consume */ - consumed = true; if(event->type == InputTypeShort) { + consumed = true; if(view->interact_callback) { view->interact_callback(view->interact_callback_context); } @@ -81,7 +80,7 @@ OneShotView* one_shot_view_alloc(void) { OneShotView* view = malloc(sizeof(OneShotView)); view->view = view_alloc(); view->update_timer = - xTimerCreate(NULL, 1000, pdTRUE, view, one_shot_view_update_timer_callback); + furi_timer_alloc(one_shot_view_update_timer_callback, FuriTimerTypePeriodic, view); view_allocate_model(view->view, ViewModelTypeLocking, sizeof(OneShotViewModel)); view_set_context(view->view, view); @@ -94,7 +93,7 @@ OneShotView* one_shot_view_alloc(void) { void one_shot_view_free(OneShotView* view) { furi_assert(view); - xTimerDelete(view->update_timer, portMAX_DELAY); + furi_timer_free(view->update_timer); view_free(view->view); view->view = NULL; free(view); @@ -120,7 +119,7 @@ void one_shot_view_start_animation(OneShotView* view, const Icon* icon) { model->icon = icon; model->block_input = true; view_commit_model(view->view, true); - xTimerChangePeriod(view->update_timer, 1000 / model->icon->frame_rate, portMAX_DELAY); + furi_timer_start(view->update_timer, 1000 / model->icon->frame_rate); } View* one_shot_view_get_view(OneShotView* view) { diff --git a/applications/desktop/animations/views/one_shot_animation_view.h b/applications/services/desktop/animations/views/one_shot_animation_view.h similarity index 100% rename from applications/desktop/animations/views/one_shot_animation_view.h rename to applications/services/desktop/animations/views/one_shot_animation_view.h diff --git a/applications/services/desktop/application.fam b/applications/services/desktop/application.fam new file mode 100644 index 00000000000..da6e2b8020c --- /dev/null +++ b/applications/services/desktop/application.fam @@ -0,0 +1,17 @@ +App( + appid="desktop", + name="DesktopSrv", + apptype=FlipperAppType.SERVICE, + entry_point="desktop_srv", + cdefines=["SRV_DESKTOP"], + requires=[ + "gui", + "dolphin", + "storage", + "input", + ], + provides=["desktop_settings"], + conflicts=["updater"], + stack_size=2 * 1024, + order=60, +) diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c new file mode 100644 index 00000000000..547883e9a74 --- /dev/null +++ b/applications/services/desktop/desktop.c @@ -0,0 +1,482 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "animations/animation_manager.h" +#include "desktop/scenes/desktop_scene.h" +#include "desktop/scenes/desktop_scene_i.h" +#include "desktop/views/desktop_view_locked.h" +#include "desktop/views/desktop_view_pin_input.h" +#include "desktop/views/desktop_view_pin_timeout.h" +#include "desktop_i.h" +#include "helpers/pin.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*); + +static void desktop_loader_callback(const void* message, void* context) { + furi_assert(context); + Desktop* desktop = context; + const LoaderEvent* event = message; + + if(event->type == LoaderEventTypeApplicationStarted) { + view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalBeforeAppStarted); + } else if(event->type == LoaderEventTypeApplicationStopped) { + view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAfterAppFinished); + } +} +static void desktop_lock_icon_draw_callback(Canvas* canvas, void* context) { + UNUSED(context); + furi_assert(canvas); + canvas_draw_icon(canvas, 0, 0, &I_Lock_7x8); +} + +static void desktop_dummy_mode_icon_draw_callback(Canvas* canvas, void* context) { + UNUSED(context); + furi_assert(canvas); + canvas_draw_icon(canvas, 0, 0, &I_GameMode_11x8); +} + +static void desktop_clock_update(Desktop* desktop) { + furi_assert(desktop); + + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + bool time_format_12 = locale_get_time_format() == LocaleTimeFormat12h; + + if(desktop->time_hour != curr_dt.hour || desktop->time_minute != curr_dt.minute || + desktop->time_format_12 != time_format_12) { + desktop->time_format_12 = time_format_12; + desktop->time_hour = curr_dt.hour; + desktop->time_minute = curr_dt.minute; + view_port_update(desktop->clock_viewport); + } +} + +static void desktop_clock_reconfigure(Desktop* desktop) { + furi_assert(desktop); + + desktop_clock_update(desktop); + + if(desktop->settings.display_clock) { + furi_timer_start(desktop->update_clock_timer, furi_ms_to_ticks(1000)); + } else { + furi_timer_stop(desktop->update_clock_timer); + } + + view_port_enabled_set(desktop->clock_viewport, desktop->settings.display_clock); +} + +static void desktop_clock_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + furi_assert(canvas); + + Desktop* desktop = context; + + canvas_set_font(canvas, FontPrimary); + + uint8_t hour = desktop->time_hour; + if(desktop->time_format_12) { + if(hour > 12) { + hour -= 12; + } + if(hour == 0) { + hour = 12; + } + } + + char buffer[20]; + snprintf(buffer, sizeof(buffer), "%02u:%02u", hour, desktop->time_minute); + + view_port_set_width( + desktop->clock_viewport, + canvas_string_width(canvas, buffer) - 1 + (desktop->time_minute % 10 == 1)); + + canvas_draw_str_aligned(canvas, 0, 8, AlignLeft, AlignBottom, buffer); +} + +static void desktop_stealth_mode_icon_draw_callback(Canvas* canvas, void* context) { + UNUSED(context); + furi_assert(canvas); + canvas_draw_icon(canvas, 0, 0, &I_Muted_8x8); +} + +static bool desktop_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + Desktop* desktop = (Desktop*)context; + + switch(event) { + case DesktopGlobalBeforeAppStarted: + animation_manager_unload_and_stall_animation(desktop->animation_manager); + desktop_auto_lock_inhibit(desktop); + return true; + case DesktopGlobalAfterAppFinished: + animation_manager_load_and_continue_animation(desktop->animation_manager); + DESKTOP_SETTINGS_LOAD(&desktop->settings); + + desktop_clock_reconfigure(desktop); + + desktop_auto_lock_arm(desktop); + return true; + case DesktopGlobalAutoLock: + if(!loader_is_locked(desktop->loader)) { + desktop_lock(desktop); + } + return true; + } + + return scene_manager_handle_custom_event(desktop->scene_manager, event); +} + +static bool desktop_back_event_callback(void* context) { + furi_assert(context); + Desktop* desktop = (Desktop*)context; + return scene_manager_handle_back_event(desktop->scene_manager); +} + +static void desktop_tick_event_callback(void* context) { + furi_assert(context); + Desktop* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +static void desktop_input_event_callback(const void* value, void* context) { + furi_assert(value); + furi_assert(context); + const InputEvent* event = value; + Desktop* desktop = context; + if(event->type == InputTypePress) { + desktop_start_auto_lock_timer(desktop); + } +} + +static void desktop_auto_lock_timer_callback(void* context) { + furi_assert(context); + Desktop* desktop = context; + view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAutoLock); +} + +static void desktop_start_auto_lock_timer(Desktop* desktop) { + furi_timer_start( + desktop->auto_lock_timer, furi_ms_to_ticks(desktop->settings.auto_lock_delay_ms)); +} + +static void desktop_stop_auto_lock_timer(Desktop* desktop) { + furi_timer_stop(desktop->auto_lock_timer); +} + +static void desktop_auto_lock_arm(Desktop* desktop) { + if(desktop->settings.auto_lock_delay_ms) { + desktop->input_events_subscription = furi_pubsub_subscribe( + desktop->input_events_pubsub, desktop_input_event_callback, desktop); + desktop_start_auto_lock_timer(desktop); + } +} + +static void desktop_auto_lock_inhibit(Desktop* desktop) { + desktop_stop_auto_lock_timer(desktop); + if(desktop->input_events_subscription) { + furi_pubsub_unsubscribe(desktop->input_events_pubsub, desktop->input_events_subscription); + desktop->input_events_subscription = NULL; + } +} + +static void desktop_clock_timer_callback(void* context) { + furi_assert(context); + Desktop* desktop = context; + + if(gui_active_view_port_count(desktop->gui, GuiLayerStatusBarLeft) < 6) { + desktop_clock_update(desktop); + + view_port_enabled_set(desktop->clock_viewport, true); + } else { + view_port_enabled_set(desktop->clock_viewport, false); + } +} + +void desktop_lock(Desktop* desktop) { + furi_hal_rtc_set_flag(FuriHalRtcFlagLock); + + if(desktop->settings.pin_code.length) { + Cli* cli = furi_record_open(RECORD_CLI); + cli_session_close(cli); + furi_record_close(RECORD_CLI); + } + + desktop_auto_lock_inhibit(desktop); + scene_manager_set_scene_state( + desktop->scene_manager, DesktopSceneLocked, SCENE_LOCKED_FIRST_ENTER); + scene_manager_next_scene(desktop->scene_manager, DesktopSceneLocked); + + DesktopStatus status = {.locked = true}; + furi_pubsub_publish(desktop->status_pubsub, &status); +} + +void desktop_unlock(Desktop* desktop) { + view_port_enabled_set(desktop->lock_icon_viewport, false); + Gui* gui = furi_record_open(RECORD_GUI); + gui_set_lockdown(gui, false); + furi_record_close(RECORD_GUI); + desktop_view_locked_unlock(desktop->locked_view); + scene_manager_search_and_switch_to_previous_scene(desktop->scene_manager, DesktopSceneMain); + desktop_auto_lock_arm(desktop); + furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); + furi_hal_rtc_set_pin_fails(0); + + if(desktop->settings.pin_code.length) { + Cli* cli = furi_record_open(RECORD_CLI); + cli_session_open(cli, &cli_vcp); + furi_record_close(RECORD_CLI); + } + + DesktopStatus status = {.locked = false}; + furi_pubsub_publish(desktop->status_pubsub, &status); +} + +void desktop_set_dummy_mode_state(Desktop* desktop, bool enabled) { + desktop->in_transition = true; + view_port_enabled_set(desktop->dummy_mode_icon_viewport, enabled); + desktop_main_set_dummy_mode_state(desktop->main_view, enabled); + animation_manager_set_dummy_mode_state(desktop->animation_manager, enabled); + desktop->settings.dummy_mode = enabled; + DESKTOP_SETTINGS_SAVE(&desktop->settings); + desktop->in_transition = false; +} + +void desktop_set_stealth_mode_state(Desktop* desktop, bool enabled) { + desktop->in_transition = true; + if(enabled) { + furi_hal_rtc_set_flag(FuriHalRtcFlagStealthMode); + } else { + furi_hal_rtc_reset_flag(FuriHalRtcFlagStealthMode); + } + view_port_enabled_set(desktop->stealth_mode_icon_viewport, enabled); + desktop->in_transition = false; +} + +Desktop* desktop_alloc() { + Desktop* desktop = malloc(sizeof(Desktop)); + + desktop->animation_manager = animation_manager_alloc(); + desktop->gui = furi_record_open(RECORD_GUI); + desktop->scene_thread = furi_thread_alloc(); + desktop->view_dispatcher = view_dispatcher_alloc(); + desktop->scene_manager = scene_manager_alloc(&desktop_scene_handlers, desktop); + + view_dispatcher_enable_queue(desktop->view_dispatcher); + view_dispatcher_attach_to_gui( + desktop->view_dispatcher, desktop->gui, ViewDispatcherTypeDesktop); + view_dispatcher_set_tick_event_callback( + desktop->view_dispatcher, desktop_tick_event_callback, 500); + + view_dispatcher_set_event_callback_context(desktop->view_dispatcher, desktop); + view_dispatcher_set_custom_event_callback( + desktop->view_dispatcher, desktop_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + desktop->view_dispatcher, desktop_back_event_callback); + + desktop->lock_menu = desktop_lock_menu_alloc(); + desktop->debug_view = desktop_debug_alloc(); + desktop->hw_mismatch_popup = popup_alloc(); + desktop->locked_view = desktop_view_locked_alloc(); + desktop->pin_input_view = desktop_view_pin_input_alloc(); + desktop->pin_timeout_view = desktop_view_pin_timeout_alloc(); + desktop->slideshow_view = desktop_view_slideshow_alloc(); + + desktop->main_view_stack = view_stack_alloc(); + desktop->main_view = desktop_main_alloc(); + View* dolphin_view = animation_manager_get_animation_view(desktop->animation_manager); + view_stack_add_view(desktop->main_view_stack, desktop_main_get_view(desktop->main_view)); + view_stack_add_view(desktop->main_view_stack, dolphin_view); + view_stack_add_view( + desktop->main_view_stack, desktop_view_locked_get_view(desktop->locked_view)); + + /* locked view (as animation view) attends in 2 scenes: main & locked, + * because it has to draw "Unlocked" label on main scene */ + desktop->locked_view_stack = view_stack_alloc(); + view_stack_add_view(desktop->locked_view_stack, dolphin_view); + view_stack_add_view( + desktop->locked_view_stack, desktop_view_locked_get_view(desktop->locked_view)); + + view_dispatcher_add_view( + desktop->view_dispatcher, + DesktopViewIdMain, + view_stack_get_view(desktop->main_view_stack)); + view_dispatcher_add_view( + desktop->view_dispatcher, + DesktopViewIdLocked, + view_stack_get_view(desktop->locked_view_stack)); + view_dispatcher_add_view( + desktop->view_dispatcher, + DesktopViewIdLockMenu, + desktop_lock_menu_get_view(desktop->lock_menu)); + view_dispatcher_add_view( + desktop->view_dispatcher, DesktopViewIdDebug, desktop_debug_get_view(desktop->debug_view)); + view_dispatcher_add_view( + desktop->view_dispatcher, + DesktopViewIdHwMismatch, + popup_get_view(desktop->hw_mismatch_popup)); + view_dispatcher_add_view( + desktop->view_dispatcher, + DesktopViewIdPinTimeout, + desktop_view_pin_timeout_get_view(desktop->pin_timeout_view)); + view_dispatcher_add_view( + desktop->view_dispatcher, + DesktopViewIdPinInput, + desktop_view_pin_input_get_view(desktop->pin_input_view)); + view_dispatcher_add_view( + desktop->view_dispatcher, + DesktopViewIdSlideshow, + desktop_view_slideshow_get_view(desktop->slideshow_view)); + + // Lock icon + desktop->lock_icon_viewport = view_port_alloc(); + view_port_set_width(desktop->lock_icon_viewport, icon_get_width(&I_Lock_7x8)); + view_port_draw_callback_set( + desktop->lock_icon_viewport, desktop_lock_icon_draw_callback, desktop); + view_port_enabled_set(desktop->lock_icon_viewport, false); + gui_add_view_port(desktop->gui, desktop->lock_icon_viewport, GuiLayerStatusBarLeft); + + // Dummy mode icon + desktop->dummy_mode_icon_viewport = view_port_alloc(); + view_port_set_width(desktop->dummy_mode_icon_viewport, icon_get_width(&I_GameMode_11x8)); + view_port_draw_callback_set( + desktop->dummy_mode_icon_viewport, desktop_dummy_mode_icon_draw_callback, desktop); + view_port_enabled_set(desktop->dummy_mode_icon_viewport, false); + gui_add_view_port(desktop->gui, desktop->dummy_mode_icon_viewport, GuiLayerStatusBarLeft); + + // Clock + desktop->clock_viewport = view_port_alloc(); + view_port_set_width(desktop->clock_viewport, 25); + view_port_draw_callback_set(desktop->clock_viewport, desktop_clock_draw_callback, desktop); + view_port_enabled_set(desktop->clock_viewport, false); + gui_add_view_port(desktop->gui, desktop->clock_viewport, GuiLayerStatusBarRight); + + // Stealth mode icon + desktop->stealth_mode_icon_viewport = view_port_alloc(); + view_port_set_width(desktop->stealth_mode_icon_viewport, icon_get_width(&I_Muted_8x8)); + view_port_draw_callback_set( + desktop->stealth_mode_icon_viewport, desktop_stealth_mode_icon_draw_callback, desktop); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)) { + view_port_enabled_set(desktop->stealth_mode_icon_viewport, true); + } else { + view_port_enabled_set(desktop->stealth_mode_icon_viewport, false); + } + gui_add_view_port(desktop->gui, desktop->stealth_mode_icon_viewport, GuiLayerStatusBarLeft); + + desktop->loader = furi_record_open(RECORD_LOADER); + + desktop->notification = furi_record_open(RECORD_NOTIFICATION); + desktop->app_start_stop_subscription = furi_pubsub_subscribe( + loader_get_pubsub(desktop->loader), desktop_loader_callback, desktop); + + desktop->input_events_pubsub = furi_record_open(RECORD_INPUT_EVENTS); + desktop->input_events_subscription = NULL; + + desktop->auto_lock_timer = + furi_timer_alloc(desktop_auto_lock_timer_callback, FuriTimerTypeOnce, desktop); + + desktop->status_pubsub = furi_pubsub_alloc(); + + desktop->update_clock_timer = + furi_timer_alloc(desktop_clock_timer_callback, FuriTimerTypePeriodic, desktop); + + furi_record_create(RECORD_DESKTOP, desktop); + + return desktop; +} + +static bool desktop_check_file_flag(const char* flag_path) { + Storage* storage = furi_record_open(RECORD_STORAGE); + bool exists = storage_common_stat(storage, flag_path, NULL) == FSE_OK; + furi_record_close(RECORD_STORAGE); + + return exists; +} + +bool desktop_api_is_locked(Desktop* instance) { + furi_assert(instance); + return furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock); +} + +void desktop_api_unlock(Desktop* instance) { + furi_assert(instance); + view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopLockedEventUnlocked); +} + +FuriPubSub* desktop_api_get_status_pubsub(Desktop* instance) { + furi_assert(instance); + return instance->status_pubsub; +} + +int32_t desktop_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; + } + + 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); + } + + view_port_enabled_set(desktop->dummy_mode_icon_viewport, desktop->settings.dummy_mode); + + desktop_clock_reconfigure(desktop); + + desktop_main_set_dummy_mode_state(desktop->main_view, desktop->settings.dummy_mode); + animation_manager_set_dummy_mode_state( + desktop->animation_manager, desktop->settings.dummy_mode); + + scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) { + desktop_lock(desktop); + } else { + if(!loader_is_locked(desktop->loader)) { + desktop_auto_lock_arm(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); + } + + // Special case: autostart application is already running + if(loader_is_locked(desktop->loader) && + animation_manager_is_animation_loaded(desktop->animation_manager)) { + animation_manager_unload_and_stall_animation(desktop->animation_manager); + } + + view_dispatcher_run(desktop->view_dispatcher); + + furi_crash("That was unexpected"); + + return 0; +} diff --git a/applications/services/desktop/desktop.h b/applications/services/desktop/desktop.h new file mode 100644 index 00000000000..4eab24fcc50 --- /dev/null +++ b/applications/services/desktop/desktop.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +typedef struct Desktop Desktop; + +#define RECORD_DESKTOP "desktop" + +bool desktop_api_is_locked(Desktop* instance); + +void desktop_api_unlock(Desktop* instance); + +typedef struct { + bool locked; +} DesktopStatus; + +FuriPubSub* desktop_api_get_status_pubsub(Desktop* instance); diff --git a/applications/desktop/desktop_i.h b/applications/services/desktop/desktop_i.h similarity index 78% rename from applications/desktop/desktop_i.h rename to applications/services/desktop/desktop_i.h index 0b5d607d194..bb495c92014 100644 --- a/applications/desktop/desktop_i.h +++ b/applications/services/desktop/desktop_i.h @@ -9,7 +9,7 @@ #include "views/desktop_view_lock_menu.h" #include "views/desktop_view_debug.h" #include "views/desktop_view_slideshow.h" -#include "desktop/desktop_settings/desktop_settings.h" +#include #include #include @@ -57,7 +57,10 @@ struct Desktop { DesktopSettings settings; DesktopViewPinInput* pin_input_view; - ViewPort* lock_viewport; + ViewPort* lock_icon_viewport; + ViewPort* dummy_mode_icon_viewport; + ViewPort* clock_viewport; + ViewPort* stealth_mode_icon_viewport; AnimationManager* animation_manager; @@ -68,6 +71,15 @@ struct Desktop { FuriPubSub* input_events_pubsub; FuriPubSubSubscription* input_events_subscription; FuriTimer* auto_lock_timer; + FuriTimer* update_clock_timer; + + FuriPubSub* status_pubsub; + + uint8_t time_hour; + uint8_t time_minute; + bool time_format_12 : 1; // 1 - 12 hour, 0 - 24H + + bool in_transition : 1; }; Desktop* desktop_alloc(); @@ -75,3 +87,5 @@ Desktop* desktop_alloc(); void desktop_free(Desktop* desktop); void desktop_lock(Desktop* desktop); void desktop_unlock(Desktop* desktop); +void desktop_set_dummy_mode_state(Desktop* desktop, bool enabled); +void desktop_set_stealth_mode_state(Desktop* desktop, bool enabled); diff --git a/applications/services/desktop/desktop_settings.h b/applications/services/desktop/desktop_settings.h new file mode 100644 index 00000000000..464245bc887 --- /dev/null +++ b/applications/services/desktop/desktop_settings.h @@ -0,0 +1,75 @@ +#pragma once + +#include "desktop_settings_filename.h" + +#include +#include +#include +#include +#include + +#define DESKTOP_SETTINGS_VER (10) + +#define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME) +#define DESKTOP_SETTINGS_MAGIC (0x17) +#define PIN_MAX_LENGTH 12 + +#define DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG "run_pin_setup" + +#define DESKTOP_SETTINGS_SAVE(x) \ + saved_struct_save( \ + DESKTOP_SETTINGS_PATH, \ + (x), \ + sizeof(DesktopSettings), \ + DESKTOP_SETTINGS_MAGIC, \ + DESKTOP_SETTINGS_VER) + +#define DESKTOP_SETTINGS_LOAD(x) \ + saved_struct_load( \ + DESKTOP_SETTINGS_PATH, \ + (x), \ + sizeof(DesktopSettings), \ + DESKTOP_SETTINGS_MAGIC, \ + DESKTOP_SETTINGS_VER) + +#define MAX_PIN_SIZE 10 +#define MIN_PIN_SIZE 4 +#define MAX_APP_LENGTH 128 + +typedef enum { + FavoriteAppLeftShort = 0, + FavoriteAppLeftLong, + FavoriteAppRightShort, + FavoriteAppRightLong, + FavoriteAppNumber, +} FavoriteAppShortcut; + +typedef enum { + DummyAppLeft = 0, + DummyAppRight, + DummyAppDown, + DummyAppOk, + DummyAppNumber, +} DummyAppShortcut; + +typedef struct { + InputKey data[MAX_PIN_SIZE]; + uint8_t length; +} PinCode; + +typedef struct { + char name_or_path[MAX_APP_LENGTH]; +} FavoriteApp; + +typedef struct { + PinCode pin_code; + uint32_t auto_lock_delay_ms; +<<<<<<< HEAD:applications/desktop/desktop_settings/desktop_settings.h + bool is_inverted; +======= + uint8_t dummy_mode; + uint8_t display_clock; + FavoriteApp favorite_apps[FavoriteAppNumber]; + FavoriteApp dummy_apps[DummyAppNumber]; +>>>>>>> upstream/dev:applications/services/desktop/desktop_settings.h +} DesktopSettings; diff --git a/applications/desktop/desktop_settings/desktop_settings_filename.h b/applications/services/desktop/desktop_settings_filename.h similarity index 100% rename from applications/desktop/desktop_settings/desktop_settings_filename.h rename to applications/services/desktop/desktop_settings_filename.h diff --git a/applications/services/desktop/helpers/pin.c b/applications/services/desktop/helpers/pin.c new file mode 100644 index 00000000000..8a79a1fb8b6 --- /dev/null +++ b/applications/services/desktop/helpers/pin.c @@ -0,0 +1,74 @@ +#include "pin.h" + +#include +#include +#include +#include +#include +#include + +#include "../desktop_i.h" + +static const NotificationSequence sequence_pin_fail = { + &message_display_backlight_on, + + &message_red_255, + &message_vibro_on, + &message_delay_100, + &message_vibro_off, + &message_red_0, + + &message_delay_250, + + &message_red_255, + &message_vibro_on, + &message_delay_100, + &message_vibro_off, + &message_red_0, + NULL, +}; + +static const uint8_t desktop_helpers_fails_timeout[] = { + 0, + 0, + 0, + 0, + 30, + 60, + 90, + 120, + 150, + 180, + /* +60 for every next fail */ +}; + +void desktop_pin_lock_error_notify() { + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_pin_fail); + furi_record_close(RECORD_NOTIFICATION); +} + +uint32_t desktop_pin_lock_get_fail_timeout() { + uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); + uint32_t pin_timeout = 0; + uint32_t max_index = COUNT_OF(desktop_helpers_fails_timeout) - 1; + if(pin_fails <= max_index) { + pin_timeout = desktop_helpers_fails_timeout[pin_fails]; + } else { + pin_timeout = desktop_helpers_fails_timeout[max_index] + (pin_fails - max_index) * 60; + } + + return pin_timeout; +} + +bool desktop_pin_compare(const PinCode* pin_code1, const PinCode* pin_code2) { + furi_assert(pin_code1); + furi_assert(pin_code2); + bool result = false; + + if(pin_code1->length == pin_code2->length) { + result = !memcmp(pin_code1->data, pin_code2->data, pin_code1->length); + } + + return result; +} diff --git a/applications/services/desktop/helpers/pin.h b/applications/services/desktop/helpers/pin.h new file mode 100644 index 00000000000..e5410723e5c --- /dev/null +++ b/applications/services/desktop/helpers/pin.h @@ -0,0 +1,11 @@ +#pragma once +#include +#include +#include "../desktop.h" +#include + +void desktop_pin_lock_error_notify(); + +uint32_t desktop_pin_lock_get_fail_timeout(); + +bool desktop_pin_compare(const PinCode* pin_code1, const PinCode* pin_code2); diff --git a/applications/desktop/helpers/slideshow.c b/applications/services/desktop/helpers/slideshow.c similarity index 99% rename from applications/desktop/helpers/slideshow.c rename to applications/services/desktop/helpers/slideshow.c index b4d85cb9075..a8e132779a4 100644 --- a/applications/desktop/helpers/slideshow.c +++ b/applications/services/desktop/helpers/slideshow.c @@ -41,7 +41,7 @@ Slideshow* slideshow_alloc() { void slideshow_free(Slideshow* slideshow) { Icon* icon = &slideshow->icon; - if(icon) { + if(icon) { //-V547 for(int frame_idx = 0; frame_idx < icon->frame_count; ++frame_idx) { uint8_t* frame_data = (uint8_t*)icon->frames[frame_idx]; free(frame_data); diff --git a/applications/desktop/helpers/slideshow.h b/applications/services/desktop/helpers/slideshow.h similarity index 100% rename from applications/desktop/helpers/slideshow.h rename to applications/services/desktop/helpers/slideshow.h diff --git a/applications/desktop/helpers/slideshow_filename.h b/applications/services/desktop/helpers/slideshow_filename.h similarity index 100% rename from applications/desktop/helpers/slideshow_filename.h rename to applications/services/desktop/helpers/slideshow_filename.h diff --git a/applications/desktop/scenes/desktop_scene.c b/applications/services/desktop/scenes/desktop_scene.c similarity index 100% rename from applications/desktop/scenes/desktop_scene.c rename to applications/services/desktop/scenes/desktop_scene.c diff --git a/applications/desktop/scenes/desktop_scene.h b/applications/services/desktop/scenes/desktop_scene.h similarity index 100% rename from applications/desktop/scenes/desktop_scene.h rename to applications/services/desktop/scenes/desktop_scene.h diff --git a/applications/desktop/scenes/desktop_scene_config.h b/applications/services/desktop/scenes/desktop_scene_config.h similarity index 100% rename from applications/desktop/scenes/desktop_scene_config.h rename to applications/services/desktop/scenes/desktop_scene_config.h diff --git a/applications/services/desktop/scenes/desktop_scene_debug.c b/applications/services/desktop/scenes/desktop_scene_debug.c new file mode 100644 index 00000000000..866c736abad --- /dev/null +++ b/applications/services/desktop/scenes/desktop_scene_debug.c @@ -0,0 +1,44 @@ + +#include +#include + +#include "../desktop_i.h" +#include "../views/desktop_view_debug.h" +#include "desktop_scene.h" + +void desktop_scene_debug_callback(DesktopEvent event, void* context) { + Desktop* desktop = (Desktop*)context; + view_dispatcher_send_custom_event(desktop->view_dispatcher, event); +} + +void desktop_scene_debug_on_enter(void* context) { + Desktop* desktop = (Desktop*)context; + + desktop_debug_set_callback(desktop->debug_view, desktop_scene_debug_callback, desktop); + view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdDebug); +} + +bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) { + Desktop* desktop = (Desktop*)context; + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case DesktopDebugEventExit: + scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); + dolphin_flush(dolphin); + consumed = true; + break; + default: + break; + } + } + + furi_record_close(RECORD_DOLPHIN); + return consumed; +} + +void desktop_scene_debug_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/desktop/scenes/desktop_scene_fault.c b/applications/services/desktop/scenes/desktop_scene_fault.c similarity index 100% rename from applications/desktop/scenes/desktop_scene_fault.c rename to applications/services/desktop/scenes/desktop_scene_fault.c diff --git a/applications/desktop/scenes/desktop_scene_hw_mismatch.c b/applications/services/desktop/scenes/desktop_scene_hw_mismatch.c similarity index 100% rename from applications/desktop/scenes/desktop_scene_hw_mismatch.c rename to applications/services/desktop/scenes/desktop_scene_hw_mismatch.c diff --git a/applications/desktop/scenes/desktop_scene_i.h b/applications/services/desktop/scenes/desktop_scene_i.h similarity index 100% rename from applications/desktop/scenes/desktop_scene_i.h rename to applications/services/desktop/scenes/desktop_scene_i.h diff --git a/applications/services/desktop/scenes/desktop_scene_lock_menu.c b/applications/services/desktop/scenes/desktop_scene_lock_menu.c new file mode 100644 index 00000000000..105b2b37a24 --- /dev/null +++ b/applications/services/desktop/scenes/desktop_scene_lock_menu.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include + +#include "../desktop_i.h" +#include +#include "../views/desktop_view_lock_menu.h" +#include "desktop_scene_i.h" +#include "desktop_scene.h" +#include "../helpers/pin.h" + +#define TAG "DesktopSceneLock" + +void desktop_scene_lock_menu_callback(DesktopEvent event, void* context) { + Desktop* desktop = (Desktop*)context; + view_dispatcher_send_custom_event(desktop->view_dispatcher, event); +} + +void desktop_scene_lock_menu_on_enter(void* context) { + Desktop* desktop = (Desktop*)context; + + DESKTOP_SETTINGS_LOAD(&desktop->settings); + scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); + desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop); + desktop_lock_menu_set_dummy_mode_state(desktop->lock_menu, desktop->settings.dummy_mode); + desktop_lock_menu_set_stealth_mode_state( + desktop->lock_menu, furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)); + desktop_lock_menu_set_idx(desktop->lock_menu, 0); + + view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdLockMenu); +} + +bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { + Desktop* desktop = (Desktop*)context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeTick) { + bool check_pin_changed = + scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLockMenu); + if(check_pin_changed) { + DESKTOP_SETTINGS_LOAD(&desktop->settings); + if(desktop->settings.pin_code.length > 0) { + scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); + } + } + } else if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case DesktopLockMenuEventLock: + scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); + desktop_lock(desktop); + consumed = true; + break; + case DesktopLockMenuEventDummyModeOn: + desktop_set_dummy_mode_state(desktop, true); + scene_manager_search_and_switch_to_previous_scene( + desktop->scene_manager, DesktopSceneMain); + break; + case DesktopLockMenuEventDummyModeOff: + desktop_set_dummy_mode_state(desktop, false); + scene_manager_search_and_switch_to_previous_scene( + desktop->scene_manager, DesktopSceneMain); + break; + case DesktopLockMenuEventStealthModeOn: + desktop_set_stealth_mode_state(desktop, true); + scene_manager_search_and_switch_to_previous_scene( + desktop->scene_manager, DesktopSceneMain); + break; + case DesktopLockMenuEventStealthModeOff: + desktop_set_stealth_mode_state(desktop, false); + scene_manager_search_and_switch_to_previous_scene( + desktop->scene_manager, DesktopSceneMain); + break; + default: + break; + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); + } + return consumed; +} + +void desktop_scene_lock_menu_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/desktop/scenes/desktop_scene_locked.c b/applications/services/desktop/scenes/desktop_scene_locked.c similarity index 90% rename from applications/desktop/scenes/desktop_scene_locked.c rename to applications/services/desktop/scenes/desktop_scene_locked.c index c377d40ab3b..034eedb8ace 100644 --- a/applications/desktop/scenes/desktop_scene_locked.c +++ b/applications/services/desktop/scenes/desktop_scene_locked.c @@ -3,11 +3,10 @@ #include #include #include -#include #include "../desktop.h" #include "../desktop_i.h" -#include "../helpers/pin_lock.h" +#include "../helpers/pin.h" #include "../animations/animation_manager.h" #include "../views/desktop_events.h" #include "../views/desktop_view_pin_input.h" @@ -45,14 +44,14 @@ void desktop_scene_locked_on_enter(void* context) { bool switch_to_timeout_scene = false; uint32_t state = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLocked); if(state == SCENE_LOCKED_FIRST_ENTER) { - bool pin_locked = desktop_pin_lock_is_locked(); - view_port_enabled_set(desktop->lock_viewport, true); + bool pin_locked = desktop->settings.pin_code.length > 0; + view_port_enabled_set(desktop->lock_icon_viewport, true); Gui* gui = furi_record_open(RECORD_GUI); gui_set_lockdown(gui, true); furi_record_close(RECORD_GUI); if(pin_locked) { - LOAD_DESKTOP_SETTINGS(&desktop->settings); + DESKTOP_SETTINGS_LOAD(&desktop->settings); desktop_view_locked_lock(desktop->locked_view, true); uint32_t pin_timeout = desktop_pin_lock_get_fail_timeout(); if(pin_timeout > 0) { @@ -87,6 +86,10 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) { desktop_unlock(desktop); consumed = true; break; + case DesktopLockedEventDoorsClosed: + notification_message(desktop->notification, &sequence_display_backlight_off); + consumed = true; + break; case DesktopLockedEventUpdate: if(desktop_view_locked_is_locked_hint_visible(desktop->locked_view)) { notification_message(desktop->notification, &sequence_display_backlight_off); diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c new file mode 100644 index 00000000000..a659ff4e358 --- /dev/null +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -0,0 +1,229 @@ +#include +#include +#include +#include +#include + +#include "../desktop_i.h" +#include "../views/desktop_events.h" +#include "../views/desktop_view_main.h" +#include "desktop_scene.h" +#include "desktop_scene_i.h" + +#define TAG "DesktopSrv" + +static void desktop_scene_main_new_idle_animation_callback(void* context) { + furi_assert(context); + Desktop* desktop = context; + view_dispatcher_send_custom_event( + desktop->view_dispatcher, DesktopAnimationEventNewIdleAnimation); +} + +static void desktop_scene_main_check_animation_callback(void* context) { + furi_assert(context); + Desktop* desktop = context; + view_dispatcher_send_custom_event( + desktop->view_dispatcher, DesktopAnimationEventCheckAnimation); +} + +static void desktop_scene_main_interact_animation_callback(void* context) { + furi_assert(context); + Desktop* desktop = context; + view_dispatcher_send_custom_event( + desktop->view_dispatcher, DesktopAnimationEventInteractAnimation); +} + +#ifdef APP_ARCHIVE +static void + desktop_switch_to_app(Desktop* desktop, const FlipperInternalApplication* flipper_app) { + furi_assert(desktop); + furi_assert(flipper_app); + furi_assert(flipper_app->app); + furi_assert(flipper_app->name); + + if(furi_thread_get_state(desktop->scene_thread) != FuriThreadStateStopped) { + FURI_LOG_E("Desktop", "Thread is already running"); + return; + } + + FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); + if(mode > FuriHalRtcHeapTrackModeNone) { + furi_thread_enable_heap_trace(desktop->scene_thread); + } else { + furi_thread_disable_heap_trace(desktop->scene_thread); + } + + furi_thread_set_name(desktop->scene_thread, flipper_app->name); + furi_thread_set_stack_size(desktop->scene_thread, flipper_app->stack_size); + furi_thread_set_callback(desktop->scene_thread, flipper_app->app); + + furi_thread_start(desktop->scene_thread); +} +#endif + +static void desktop_scene_main_open_app_or_profile(Desktop* desktop, FavoriteApp* application) { + bool load_ok = false; + if(strlen(application->name_or_path) > 0) { + if(loader_start(desktop->loader, application->name_or_path, NULL, NULL) == + LoaderStatusOk) { + load_ok = true; + } + } + if(!load_ok) { + loader_start(desktop->loader, "Passport", NULL, NULL); + } +} + +static void desktop_scene_main_start_favorite(Desktop* desktop, FavoriteApp* application) { + if(strlen(application->name_or_path) > 0) { + loader_start_with_gui_error(desktop->loader, application->name_or_path, NULL); + } else { + loader_start(desktop->loader, LOADER_APPLICATIONS_NAME, NULL, NULL); + } +} + +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); +} + +void desktop_scene_main_on_enter(void* context) { + Desktop* desktop = (Desktop*)context; + DesktopMainView* main_view = desktop->main_view; + + animation_manager_set_context(desktop->animation_manager, desktop); + animation_manager_set_new_idle_callback( + desktop->animation_manager, desktop_scene_main_new_idle_animation_callback); + animation_manager_set_check_callback( + desktop->animation_manager, desktop_scene_main_check_animation_callback); + animation_manager_set_interact_callback( + desktop->animation_manager, desktop_scene_main_interact_animation_callback); + + desktop_main_set_callback(main_view, desktop_scene_main_callback, desktop); + + view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewIdMain); +} + +bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { + Desktop* desktop = (Desktop*)context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case DesktopMainEventOpenMenu: { + Loader* loader = furi_record_open(RECORD_LOADER); + loader_show_menu(loader); + furi_record_close(RECORD_LOADER); + consumed = true; + } break; + + case DesktopMainEventLock: + desktop_lock(desktop); + consumed = true; + break; + + case DesktopMainEventOpenLockMenu: + scene_manager_next_scene(desktop->scene_manager, DesktopSceneLockMenu); + consumed = true; + break; + + case DesktopMainEventOpenDebug: + scene_manager_next_scene(desktop->scene_manager, DesktopSceneDebug); + consumed = true; + break; + + case DesktopMainEventOpenArchive: +#ifdef APP_ARCHIVE + desktop_switch_to_app(desktop, &FLIPPER_ARCHIVE); +#endif + consumed = true; + break; + + case DesktopMainEventOpenPowerOff: { + loader_start(desktop->loader, "Power", "off", NULL); + consumed = true; + break; + } + + case DesktopMainEventOpenFavoriteLeftShort: + DESKTOP_SETTINGS_LOAD(&desktop->settings); + desktop_scene_main_start_favorite( + desktop, &desktop->settings.favorite_apps[FavoriteAppLeftShort]); + consumed = true; + break; + case DesktopMainEventOpenFavoriteLeftLong: + DESKTOP_SETTINGS_LOAD(&desktop->settings); + desktop_scene_main_start_favorite( + desktop, &desktop->settings.favorite_apps[FavoriteAppLeftLong]); + consumed = true; + break; + case DesktopMainEventOpenFavoriteRightShort: + DESKTOP_SETTINGS_LOAD(&desktop->settings); + desktop_scene_main_start_favorite( + desktop, &desktop->settings.favorite_apps[FavoriteAppRightShort]); + consumed = true; + break; + case DesktopMainEventOpenFavoriteRightLong: + DESKTOP_SETTINGS_LOAD(&desktop->settings); + desktop_scene_main_start_favorite( + desktop, &desktop->settings.favorite_apps[FavoriteAppRightLong]); + consumed = true; + break; + + case DesktopAnimationEventCheckAnimation: + animation_manager_check_blocking_process(desktop->animation_manager); + consumed = true; + break; + case DesktopAnimationEventNewIdleAnimation: + animation_manager_new_idle_process(desktop->animation_manager); + consumed = true; + break; + case DesktopAnimationEventInteractAnimation: + if(!animation_manager_interact_process(desktop->animation_manager)) { + DESKTOP_SETTINGS_LOAD(&desktop->settings); + if(!desktop->settings.dummy_mode) { + desktop_scene_main_open_app_or_profile( + desktop, &desktop->settings.favorite_apps[FavoriteAppRightShort]); + } else { + desktop_scene_main_open_app_or_profile( + desktop, &desktop->settings.dummy_apps[DummyAppRight]); + } + } + consumed = true; + break; + + case DesktopDummyEventOpenLeft: + desktop_scene_main_open_app_or_profile( + desktop, &desktop->settings.dummy_apps[DummyAppLeft]); + break; + case DesktopDummyEventOpenDown: + desktop_scene_main_open_app_or_profile( + desktop, &desktop->settings.dummy_apps[DummyAppDown]); + break; + case DesktopDummyEventOpenOk: + desktop_scene_main_open_app_or_profile( + desktop, &desktop->settings.dummy_apps[DummyAppOk]); + break; + + case DesktopLockedEventUpdate: + desktop_view_locked_update(desktop->locked_view); + consumed = true; + break; + + default: + break; + } + } + + return consumed; +} + +void desktop_scene_main_on_exit(void* context) { + Desktop* desktop = (Desktop*)context; + + animation_manager_set_new_idle_callback(desktop->animation_manager, NULL); + animation_manager_set_check_callback(desktop->animation_manager, NULL); + animation_manager_set_interact_callback(desktop->animation_manager, NULL); + animation_manager_set_context(desktop->animation_manager, desktop); +} diff --git a/applications/desktop/scenes/desktop_scene_pin_input.c b/applications/services/desktop/scenes/desktop_scene_pin_input.c similarity index 86% rename from applications/desktop/scenes/desktop_scene_pin_input.c rename to applications/services/desktop/scenes/desktop_scene_pin_input.c index 9392309e662..0e248def604 100644 --- a/applications/desktop/scenes/desktop_scene_pin_input.c +++ b/applications/services/desktop/scenes/desktop_scene_pin_input.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include @@ -12,7 +11,7 @@ #include "../animations/animation_manager.h" #include "../views/desktop_events.h" #include "../views/desktop_view_pin_input.h" -#include "../helpers/pin_lock.h" +#include "../helpers/pin.h" #include "desktop_scene.h" #include "desktop_scene_i.h" @@ -20,7 +19,7 @@ #define INPUT_PIN_VIEW_TIMEOUT 15000 typedef struct { - TimerHandle_t timer; + FuriTimer* timer; } DesktopScenePinInputState; static void desktop_scene_locked_light_red(bool value) { @@ -33,17 +32,16 @@ static void desktop_scene_locked_light_red(bool value) { furi_record_close(RECORD_NOTIFICATION); } -static void - desktop_scene_pin_input_set_timer(Desktop* desktop, bool enable, TickType_t new_period) { +static void desktop_scene_pin_input_set_timer(Desktop* desktop, bool enable, uint32_t new_period) { furi_assert(desktop); DesktopScenePinInputState* state = (DesktopScenePinInputState*)scene_manager_get_scene_state( desktop->scene_manager, DesktopScenePinInput); furi_assert(state); if(enable) { - xTimerChangePeriod(state->timer, new_period, portMAX_DELAY); + furi_timer_start(state->timer, new_period); } else { - xTimerStop(state->timer, portMAX_DELAY); + furi_timer_stop(state->timer); } } @@ -54,16 +52,18 @@ static void desktop_scene_pin_input_back_callback(void* context) { static void desktop_scene_pin_input_done_callback(const PinCode* pin_code, void* context) { Desktop* desktop = (Desktop*)context; - if(desktop_pin_lock_verify(&desktop->settings.pin_code, pin_code)) { + if(desktop_pin_compare(&desktop->settings.pin_code, pin_code)) { view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopPinInputEventUnlocked); } else { + uint32_t pin_fails = furi_hal_rtc_get_pin_fails(); + furi_hal_rtc_set_pin_fails(pin_fails + 1); view_dispatcher_send_custom_event( desktop->view_dispatcher, DesktopPinInputEventUnlockFailed); } } -static void desktop_scene_pin_input_timer_callback(TimerHandle_t timer) { - Desktop* desktop = pvTimerGetTimerID(timer); +static void desktop_scene_pin_input_timer_callback(void* context) { + Desktop* desktop = context; view_dispatcher_send_custom_event( desktop->view_dispatcher, DesktopPinInputEventResetWrongPinLabel); @@ -82,7 +82,7 @@ void desktop_scene_pin_input_on_enter(void* context) { DesktopScenePinInputState* state = malloc(sizeof(DesktopScenePinInputState)); state->timer = - xTimerCreate(NULL, 10000, pdFALSE, desktop, desktop_scene_pin_input_timer_callback); + furi_timer_alloc(desktop_scene_pin_input_timer_callback, FuriTimerTypeOnce, desktop); scene_manager_set_scene_state(desktop->scene_manager, DesktopScenePinInput, (uint32_t)state); desktop_view_pin_input_hide_pin(desktop->pin_input_view, true); @@ -126,7 +126,6 @@ bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case DesktopPinInputEventUnlocked: - desktop_pin_unlock(&desktop->settings); desktop_unlock(desktop); consumed = true; break; @@ -148,10 +147,7 @@ void desktop_scene_pin_input_on_exit(void* context) { DesktopScenePinInputState* state = (DesktopScenePinInputState*)scene_manager_get_scene_state( desktop->scene_manager, DesktopScenePinInput); - xTimerStop(state->timer, portMAX_DELAY); - while(xTimerIsTimerActive(state->timer)) { - furi_delay_tick(1); - } - xTimerDelete(state->timer, portMAX_DELAY); + + furi_timer_free(state->timer); free(state); } diff --git a/applications/desktop/scenes/desktop_scene_pin_timeout.c b/applications/services/desktop/scenes/desktop_scene_pin_timeout.c similarity index 97% rename from applications/desktop/scenes/desktop_scene_pin_timeout.c rename to applications/services/desktop/scenes/desktop_scene_pin_timeout.c index ebe825e6a91..e3336ad76d6 100644 --- a/applications/desktop/scenes/desktop_scene_pin_timeout.c +++ b/applications/services/desktop/scenes/desktop_scene_pin_timeout.c @@ -1,7 +1,5 @@ #include #include -#include -#include #include #include "../desktop_i.h" diff --git a/applications/desktop/scenes/desktop_scene_slideshow.c b/applications/services/desktop/scenes/desktop_scene_slideshow.c similarity index 88% rename from applications/desktop/scenes/desktop_scene_slideshow.c rename to applications/services/desktop/scenes/desktop_scene_slideshow.c index cab7bf62b18..012aff75196 100644 --- a/applications/desktop/scenes/desktop_scene_slideshow.c +++ b/applications/services/desktop/scenes/desktop_scene_slideshow.c @@ -22,15 +22,11 @@ void desktop_scene_slideshow_on_enter(void* context) { bool desktop_scene_slideshow_on_event(void* context, SceneManagerEvent event) { Desktop* desktop = (Desktop*)context; bool consumed = false; - Storage* storage = NULL; Power* power = NULL; if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case DesktopSlideshowCompleted: - storage = furi_record_open(RECORD_STORAGE); - storage_common_remove(storage, SLIDESHOW_FS_PATH); - furi_record_close(RECORD_STORAGE); scene_manager_previous_scene(desktop->scene_manager); consumed = true; break; @@ -50,4 +46,8 @@ bool desktop_scene_slideshow_on_event(void* context, SceneManagerEvent event) { void desktop_scene_slideshow_on_exit(void* context) { UNUSED(context); + + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_remove(storage, SLIDESHOW_FS_PATH); + furi_record_close(RECORD_STORAGE); } diff --git a/applications/services/desktop/views/desktop_events.h b/applications/services/desktop/views/desktop_events.h new file mode 100644 index 00000000000..5dc51fd85cf --- /dev/null +++ b/applications/services/desktop/views/desktop_events.h @@ -0,0 +1,53 @@ +#pragma once + +typedef enum { + DesktopMainEventLock, + DesktopMainEventOpenLockMenu, + DesktopMainEventOpenArchive, + DesktopMainEventOpenFavoriteLeftShort, + DesktopMainEventOpenFavoriteLeftLong, + DesktopMainEventOpenFavoriteRightShort, + DesktopMainEventOpenFavoriteRightLong, + DesktopMainEventOpenMenu, + DesktopMainEventOpenDebug, + DesktopMainEventOpenPowerOff, + + DesktopDummyEventOpenLeft, + DesktopDummyEventOpenDown, + DesktopDummyEventOpenOk, + + DesktopLockedEventUnlocked, + DesktopLockedEventUpdate, + DesktopLockedEventShowPinInput, + DesktopLockedEventDoorsClosed, + + DesktopPinInputEventResetWrongPinLabel, + DesktopPinInputEventUnlocked, + DesktopPinInputEventUnlockFailed, + DesktopPinInputEventBack, + + DesktopPinTimeoutExit, + + DesktopDebugEventDeed, + DesktopDebugEventWrongDeed, + DesktopDebugEventSaveState, + DesktopDebugEventExit, + + DesktopLockMenuEventLock, + DesktopLockMenuEventDummyModeOn, + DesktopLockMenuEventDummyModeOff, + DesktopLockMenuEventStealthModeOn, + DesktopLockMenuEventStealthModeOff, + + DesktopAnimationEventCheckAnimation, + DesktopAnimationEventNewIdleAnimation, + DesktopAnimationEventInteractAnimation, + + DesktopSlideshowCompleted, + DesktopSlideshowPoweroff, + + // Global events + DesktopGlobalBeforeAppStarted, + DesktopGlobalAfterAppFinished, + DesktopGlobalAutoLock, +} DesktopEvent; diff --git a/applications/services/desktop/views/desktop_view_debug.c b/applications/services/desktop/views/desktop_view_debug.c new file mode 100644 index 00000000000..35c7dc03886 --- /dev/null +++ b/applications/services/desktop/views/desktop_view_debug.c @@ -0,0 +1,140 @@ +#include +#include +#include +#include +#include + +#include "../desktop_i.h" +#include "desktop_view_debug.h" + +void desktop_debug_set_callback( + DesktopDebugView* debug_view, + DesktopDebugViewCallback callback, + void* context) { + furi_assert(debug_view); + furi_assert(callback); + debug_view->callback = callback; + debug_view->context = context; +} + +void desktop_debug_render(Canvas* canvas, void* model) { + UNUSED(model); + canvas_clear(canvas); + const Version* ver; + char buffer[64]; + + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + + uint32_t uptime = furi_get_tick() / furi_kernel_get_tick_frequency(); + snprintf( + buffer, + sizeof(buffer), + "Uptime: %luh%lum%lus", + uptime / 60 / 60, + uptime / 60 % 60, + uptime % 60); + canvas_draw_str_aligned(canvas, 64, 1 + STATUS_BAR_Y_SHIFT, AlignCenter, AlignTop, buffer); + + canvas_set_font(canvas, FontSecondary); + + // Hardware version + const char* my_name = furi_hal_version_get_name_ptr(); + snprintf( + buffer, + sizeof(buffer), + "%d.F%dB%dC%d %s:%s %s", + furi_hal_version_get_hw_version(), + 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_region_get_name(), + my_name ? my_name : "Unknown"); + canvas_draw_str(canvas, 0, 19 + STATUS_BAR_Y_SHIFT, buffer); + + ver = furi_hal_version_get_firmware_version(); + const BleGlueC2Info* c2_ver = NULL; +#ifdef SRV_BT + c2_ver = ble_glue_get_c2_info(); +#endif + if(!ver) { //-V1051 + canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, "No info"); + return; + } + + snprintf( + buffer, sizeof(buffer), "%s [%s]", version_get_version(ver), version_get_builddate(ver)); + canvas_draw_str(canvas, 0, 30 + STATUS_BAR_Y_SHIFT, buffer); + + uint16_t api_major, api_minor; + furi_hal_info_get_api_version(&api_major, &api_minor); + snprintf( + buffer, + sizeof(buffer), + "%s%s [%d.%d] %s", + version_get_dirty_flag(ver) ? "[!] " : "", + version_get_githash(ver), + api_major, + api_minor, + 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), version_get_gitbranch(ver)); + canvas_draw_str(canvas, 0, 50 + STATUS_BAR_Y_SHIFT, buffer); +} + +View* desktop_debug_get_view(DesktopDebugView* debug_view) { + furi_assert(debug_view); + return debug_view->view; +} + +static bool desktop_debug_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + + DesktopDebugView* debug_view = context; + + if(event->key == InputKeyBack && event->type == InputTypeShort) { + debug_view->callback(DesktopDebugEventExit, debug_view->context); + } + + return true; +} + +static void desktop_debug_enter(void* context) { + DesktopDebugView* debug_view = context; + furi_timer_start(debug_view->timer, furi_ms_to_ticks(1000)); +} + +static void desktop_debug_exit(void* context) { + DesktopDebugView* debug_view = context; + furi_timer_stop(debug_view->timer); +} +void desktop_debug_timer(void* context) { + DesktopDebugView* debug_view = context; + view_get_model(debug_view->view); + view_commit_model(debug_view->view, true); +} + +DesktopDebugView* desktop_debug_alloc() { + DesktopDebugView* debug_view = malloc(sizeof(DesktopDebugView)); + debug_view->view = view_alloc(); + debug_view->timer = furi_timer_alloc(desktop_debug_timer, FuriTimerTypePeriodic, debug_view); + view_set_context(debug_view->view, debug_view); + view_set_draw_callback(debug_view->view, (ViewDrawCallback)desktop_debug_render); + view_set_input_callback(debug_view->view, desktop_debug_input); + view_set_enter_callback(debug_view->view, desktop_debug_enter); + view_set_exit_callback(debug_view->view, desktop_debug_exit); + + return debug_view; +} + +void desktop_debug_free(DesktopDebugView* debug_view) { + furi_assert(debug_view); + + furi_timer_free(debug_view->timer); + view_free(debug_view->view); + free(debug_view); +} diff --git a/applications/services/desktop/views/desktop_view_debug.h b/applications/services/desktop/views/desktop_view_debug.h new file mode 100644 index 00000000000..fea0c22a080 --- /dev/null +++ b/applications/services/desktop/views/desktop_view_debug.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include "desktop_events.h" + +typedef struct DesktopDebugView DesktopDebugView; + +typedef void (*DesktopDebugViewCallback)(DesktopEvent event, void* context); + +struct DesktopDebugView { + View* view; + FuriTimer* timer; + DesktopDebugViewCallback callback; + void* context; +}; + +void desktop_debug_set_callback( + DesktopDebugView* debug_view, + DesktopDebugViewCallback callback, + void* context); + +View* desktop_debug_get_view(DesktopDebugView* debug_view); + +DesktopDebugView* desktop_debug_alloc(); + +void desktop_debug_free(DesktopDebugView* debug_view); diff --git a/applications/services/desktop/views/desktop_view_lock_menu.c b/applications/services/desktop/views/desktop_view_lock_menu.c new file mode 100644 index 00000000000..1ba8542b4a6 --- /dev/null +++ b/applications/services/desktop/views/desktop_view_lock_menu.c @@ -0,0 +1,168 @@ +#include +#include +#include + +#include "../desktop_i.h" +#include "desktop_view_lock_menu.h" + +typedef enum { + DesktopLockMenuIndexLock, + DesktopLockMenuIndexStealth, + DesktopLockMenuIndexDummy, + + DesktopLockMenuIndexTotalCount +} DesktopLockMenuIndex; + +void desktop_lock_menu_set_callback( + DesktopLockMenuView* lock_menu, + DesktopLockMenuViewCallback callback, + void* context) { + furi_assert(lock_menu); + furi_assert(callback); + lock_menu->callback = callback; + lock_menu->context = context; +} + +void desktop_lock_menu_set_dummy_mode_state(DesktopLockMenuView* lock_menu, bool dummy_mode) { + with_view_model( + lock_menu->view, + DesktopLockMenuViewModel * model, + { model->dummy_mode = dummy_mode; }, + true); +} + +void desktop_lock_menu_set_stealth_mode_state(DesktopLockMenuView* lock_menu, bool stealth_mode) { + with_view_model( + lock_menu->view, + DesktopLockMenuViewModel * model, + { model->stealth_mode = stealth_mode; }, + true); +} + +void desktop_lock_menu_set_idx(DesktopLockMenuView* lock_menu, uint8_t idx) { + furi_assert(idx < DesktopLockMenuIndexTotalCount); + with_view_model( + lock_menu->view, DesktopLockMenuViewModel * model, { model->idx = idx; }, true); +} + +void desktop_lock_menu_draw_callback(Canvas* canvas, void* model) { + DesktopLockMenuViewModel* m = model; + + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon(canvas, -57, 0 + STATUS_BAR_Y_SHIFT, &I_DoorLeft_70x55); + canvas_draw_icon(canvas, 116, 0 + STATUS_BAR_Y_SHIFT, &I_DoorRight_70x55); + canvas_set_font(canvas, FontSecondary); + + for(size_t i = 0; i < DesktopLockMenuIndexTotalCount; ++i) { + const char* str = NULL; + + if(i == DesktopLockMenuIndexLock) { + str = "Lock"; + } else if(i == DesktopLockMenuIndexStealth) { + if(m->stealth_mode) { + str = "Unmute"; + } else { + str = "Mute"; + } + } else if(i == DesktopLockMenuIndexDummy) { //-V547 + if(m->dummy_mode) { + str = "Default Mode"; + } else { + str = "Dummy Mode"; + } + } + + if(str) //-V547 + canvas_draw_str_aligned( + canvas, 64, 9 + (i * 17) + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter, str); + + if(m->idx == i) elements_frame(canvas, 15, 1 + (i * 17) + STATUS_BAR_Y_SHIFT, 98, 15); + } +} + +View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu) { + furi_assert(lock_menu); + return lock_menu->view; +} + +bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + + DesktopLockMenuView* lock_menu = context; + uint8_t idx = 0; + bool consumed = false; + bool dummy_mode = false; + bool stealth_mode = false; + bool update = false; + + with_view_model( + lock_menu->view, + DesktopLockMenuViewModel * model, + { + if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) { + if(event->key == InputKeyUp) { + if(model->idx == 0) { + model->idx = DesktopLockMenuIndexTotalCount - 1; + } else { + model->idx = CLAMP(model->idx - 1, DesktopLockMenuIndexTotalCount - 1, 0); + } + update = true; + consumed = true; + } else if(event->key == InputKeyDown) { + if(model->idx == DesktopLockMenuIndexTotalCount - 1) { + model->idx = 0; + } else { + model->idx = CLAMP(model->idx + 1, DesktopLockMenuIndexTotalCount - 1, 0); + } + update = true; + consumed = true; + } + } + idx = model->idx; + dummy_mode = model->dummy_mode; + stealth_mode = model->stealth_mode; + }, + update); + + if(event->key == InputKeyOk) { + if((idx == DesktopLockMenuIndexLock)) { + if((event->type == InputTypeShort)) { + lock_menu->callback(DesktopLockMenuEventLock, lock_menu->context); + } + } else if(idx == DesktopLockMenuIndexStealth) { + if((stealth_mode == false) && (event->type == InputTypeShort)) { + lock_menu->callback(DesktopLockMenuEventStealthModeOn, lock_menu->context); + } else if((stealth_mode == true) && (event->type == InputTypeShort)) { + lock_menu->callback(DesktopLockMenuEventStealthModeOff, lock_menu->context); + } + } else if(idx == DesktopLockMenuIndexDummy) { + if((dummy_mode == false) && (event->type == InputTypeShort)) { + lock_menu->callback(DesktopLockMenuEventDummyModeOn, lock_menu->context); + } else if((dummy_mode == true) && (event->type == InputTypeShort)) { + lock_menu->callback(DesktopLockMenuEventDummyModeOff, lock_menu->context); + } + } + consumed = true; + } + + return consumed; +} + +DesktopLockMenuView* desktop_lock_menu_alloc() { + DesktopLockMenuView* lock_menu = malloc(sizeof(DesktopLockMenuView)); + lock_menu->view = view_alloc(); + view_allocate_model(lock_menu->view, ViewModelTypeLocking, sizeof(DesktopLockMenuViewModel)); + view_set_context(lock_menu->view, lock_menu); + view_set_draw_callback(lock_menu->view, (ViewDrawCallback)desktop_lock_menu_draw_callback); + view_set_input_callback(lock_menu->view, desktop_lock_menu_input_callback); + + return lock_menu; +} + +void desktop_lock_menu_free(DesktopLockMenuView* lock_menu_view) { + furi_assert(lock_menu_view); + + view_free(lock_menu_view->view); + free(lock_menu_view); +} diff --git a/applications/desktop/views/desktop_view_lock_menu.h b/applications/services/desktop/views/desktop_view_lock_menu.h similarity index 76% rename from applications/desktop/views/desktop_view_lock_menu.h rename to applications/services/desktop/views/desktop_view_lock_menu.h index e9928a38551..8ac3a727338 100644 --- a/applications/desktop/views/desktop_view_lock_menu.h +++ b/applications/services/desktop/views/desktop_view_lock_menu.h @@ -17,8 +17,8 @@ struct DesktopLockMenuView { typedef struct { uint8_t idx; - uint8_t hint_timeout; - bool pin_set; + bool dummy_mode; + bool stealth_mode; } DesktopLockMenuViewModel; void desktop_lock_menu_set_callback( @@ -27,7 +27,8 @@ void desktop_lock_menu_set_callback( void* context); View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu); -void desktop_lock_menu_pin_set(DesktopLockMenuView* lock_menu, bool pin_is_set); +void desktop_lock_menu_set_dummy_mode_state(DesktopLockMenuView* lock_menu, bool dummy_mode); +void desktop_lock_menu_set_stealth_mode_state(DesktopLockMenuView* lock_menu, bool stealth_mode); void desktop_lock_menu_set_idx(DesktopLockMenuView* lock_menu, uint8_t idx); DesktopLockMenuView* desktop_lock_menu_alloc(); void desktop_lock_menu_free(DesktopLockMenuView* lock_menu); diff --git a/applications/desktop/views/desktop_view_locked.c b/applications/services/desktop/views/desktop_view_locked.c similarity index 91% rename from applications/desktop/views/desktop_view_locked.c rename to applications/services/desktop/views/desktop_view_locked.c index 58ed4face47..8df889dddd3 100644 --- a/applications/desktop/views/desktop_view_locked.c +++ b/applications/services/desktop/views/desktop_view_locked.c @@ -4,9 +4,9 @@ #include #include #include -#include +#include -#include "../desktop_settings/desktop_settings.h" +#include #include "../desktop_i.h" #include "desktop_view_locked.h" @@ -28,7 +28,7 @@ struct DesktopViewLocked { DesktopViewLockedCallback callback; void* context; - TimerHandle_t timer; + FuriTimer* timer; uint8_t lock_count; uint32_t lock_lastpress; }; @@ -57,8 +57,8 @@ void desktop_view_locked_set_callback( locked_view->context = context; } -static void locked_view_timer_callback(TimerHandle_t timer) { - DesktopViewLocked* locked_view = pvTimerGetTimerID(timer); +static void locked_view_timer_callback(void* context) { + DesktopViewLocked* locked_view = context; locked_view->callback(DesktopLockedEventUpdate, locked_view->context); } @@ -89,7 +89,7 @@ static void desktop_view_locked_update_hint_icon_timeout(DesktopViewLocked* lock model->view_state = DesktopViewLockedStateLockedHintShown; } view_commit_model(locked_view->view, change_state); - xTimerChangePeriod(locked_view->timer, pdMS_TO_TICKS(LOCKED_HINT_TIMEOUT_MS), portMAX_DELAY); + furi_timer_start(locked_view->timer, LOCKED_HINT_TIMEOUT_MS); } void desktop_view_locked_update(DesktopViewLocked* locked_view) { @@ -98,6 +98,7 @@ void desktop_view_locked_update(DesktopViewLocked* locked_view) { if(view_state == DesktopViewLockedStateDoorsClosing && !desktop_view_locked_doors_move(model)) { + locked_view->callback(DesktopLockedEventDoorsClosed, locked_view->context); model->view_state = DesktopViewLockedStateLocked; } else if(view_state == DesktopViewLockedStateLockedHintShown) { model->view_state = DesktopViewLockedStateLocked; @@ -108,7 +109,7 @@ void desktop_view_locked_update(DesktopViewLocked* locked_view) { view_commit_model(locked_view->view, true); if(view_state != DesktopViewLockedStateDoorsClosing) { - xTimerStop(locked_view->timer, portMAX_DELAY); + furi_timer_stop(locked_view->timer); } } @@ -146,7 +147,7 @@ static bool desktop_view_locked_input(InputEvent* event, void* context) { furi_assert(context); bool is_changed = false; - const uint32_t press_time = xTaskGetTickCount(); + const uint32_t press_time = furi_get_tick(); DesktopViewLocked* locked_view = context; DesktopViewLockedModel* model = view_get_model(locked_view->view); if(model->view_state == DesktopViewLockedStateUnlockedHintShown && @@ -159,7 +160,7 @@ static bool desktop_view_locked_input(InputEvent* event, void* context) { view_commit_model(locked_view->view, is_changed); if(view_state == DesktopViewLockedStateUnlocked) { - return view_state != DesktopViewLockedStateUnlocked; + return false; } else if(view_state == DesktopViewLockedStateLocked && pin_locked) { locked_view->callback(DesktopLockedEventShowPinInput, locked_view->context); } else if( @@ -194,7 +195,7 @@ DesktopViewLocked* desktop_view_locked_alloc() { DesktopViewLocked* locked_view = malloc(sizeof(DesktopViewLocked)); locked_view->view = view_alloc(); locked_view->timer = - xTimerCreate(NULL, 1000 / 16, pdTRUE, locked_view, locked_view_timer_callback); + furi_timer_alloc(locked_view_timer_callback, FuriTimerTypePeriodic, locked_view); view_allocate_model(locked_view->view, ViewModelTypeLocking, sizeof(DesktopViewLockedModel)); view_set_context(locked_view->view, locked_view); @@ -217,7 +218,7 @@ void desktop_view_locked_close_doors(DesktopViewLocked* locked_view) { model->view_state = DesktopViewLockedStateDoorsClosing; model->door_offset = DOOR_OFFSET_START; view_commit_model(locked_view->view, true); - xTimerChangePeriod(locked_view->timer, pdMS_TO_TICKS(DOOR_MOVING_INTERVAL_MS), portMAX_DELAY); + furi_timer_start(locked_view->timer, DOOR_MOVING_INTERVAL_MS); } void desktop_view_locked_lock(DesktopViewLocked* locked_view, bool pin_locked) { @@ -234,7 +235,7 @@ void desktop_view_locked_unlock(DesktopViewLocked* locked_view) { model->view_state = DesktopViewLockedStateUnlockedHintShown; model->pin_locked = false; view_commit_model(locked_view->view, true); - xTimerChangePeriod(locked_view->timer, pdMS_TO_TICKS(UNLOCKED_HINT_TIMEOUT_MS), portMAX_DELAY); + furi_timer_start(locked_view->timer, UNLOCKED_HINT_TIMEOUT_MS); } bool desktop_view_locked_is_locked_hint_visible(DesktopViewLocked* locked_view) { diff --git a/applications/desktop/views/desktop_view_locked.h b/applications/services/desktop/views/desktop_view_locked.h similarity index 94% rename from applications/desktop/views/desktop_view_locked.h rename to applications/services/desktop/views/desktop_view_locked.h index b0a0aa30301..ea065e39812 100644 --- a/applications/desktop/views/desktop_view_locked.h +++ b/applications/services/desktop/views/desktop_view_locked.h @@ -1,6 +1,6 @@ #pragma once -#include "../desktop_settings/desktop_settings.h" +#include #include "../views/desktop_events.h" #include diff --git a/applications/services/desktop/views/desktop_view_main.c b/applications/services/desktop/views/desktop_view_main.c new file mode 100644 index 00000000000..5e16f60862a --- /dev/null +++ b/applications/services/desktop/views/desktop_view_main.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "../desktop_i.h" +#include "desktop_view_main.h" + +struct DesktopMainView { + View* view; + DesktopMainViewCallback callback; + void* context; + FuriTimer* poweroff_timer; + bool dummy_mode; +}; + +#define DESKTOP_MAIN_VIEW_POWEROFF_TIMEOUT 5000 + +static void desktop_main_poweroff_timer_callback(void* context) { + DesktopMainView* main_view = context; + main_view->callback(DesktopMainEventOpenPowerOff, main_view->context); +} + +void desktop_main_set_callback( + DesktopMainView* main_view, + DesktopMainViewCallback callback, + void* context) { + furi_assert(main_view); + furi_assert(callback); + main_view->callback = callback; + main_view->context = context; +} + +View* desktop_main_get_view(DesktopMainView* main_view) { + furi_assert(main_view); + return main_view->view; +} + +void desktop_main_set_dummy_mode_state(DesktopMainView* main_view, bool dummy_mode) { + furi_assert(main_view); + main_view->dummy_mode = dummy_mode; +} + +bool desktop_main_input_callback(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + + DesktopMainView* main_view = context; + + if(main_view->dummy_mode == false) { + if(event->type == InputTypeShort) { + if(event->key == InputKeyOk) { + main_view->callback(DesktopMainEventOpenMenu, main_view->context); + } else if(event->key == InputKeyUp) { + main_view->callback(DesktopMainEventOpenLockMenu, main_view->context); + } else if(event->key == InputKeyDown) { + main_view->callback(DesktopMainEventOpenArchive, main_view->context); + } else if(event->key == InputKeyLeft) { + main_view->callback(DesktopMainEventOpenFavoriteLeftShort, main_view->context); + } + // Right key short is handled by animation manager + } else if(event->type == InputTypeLong) { + if(event->key == InputKeyUp) { + main_view->callback(DesktopMainEventLock, main_view->context); + } else if(event->key == InputKeyDown) { + main_view->callback(DesktopMainEventOpenDebug, main_view->context); + } else if(event->key == InputKeyLeft) { + main_view->callback(DesktopMainEventOpenFavoriteLeftLong, main_view->context); + } else if(event->key == InputKeyRight) { + main_view->callback(DesktopMainEventOpenFavoriteRightLong, main_view->context); + } + } + } else { + if(event->type == InputTypeShort) { + if(event->key == InputKeyOk) { + main_view->callback(DesktopDummyEventOpenOk, main_view->context); + } else if(event->key == InputKeyUp) { + main_view->callback(DesktopMainEventOpenLockMenu, main_view->context); + } else if(event->key == InputKeyDown) { + main_view->callback(DesktopDummyEventOpenDown, main_view->context); + } else if(event->key == InputKeyLeft) { + main_view->callback(DesktopDummyEventOpenLeft, main_view->context); + } + // Right key short is handled by animation manager + } + } + + if(event->key == InputKeyBack) { + if(event->type == InputTypePress) { + furi_timer_start(main_view->poweroff_timer, DESKTOP_MAIN_VIEW_POWEROFF_TIMEOUT); + } else if(event->type == InputTypeRelease) { + furi_timer_stop(main_view->poweroff_timer); + } + } + + return true; +} + +DesktopMainView* desktop_main_alloc() { + DesktopMainView* main_view = malloc(sizeof(DesktopMainView)); + + main_view->view = view_alloc(); + view_set_context(main_view->view, main_view); + view_set_input_callback(main_view->view, desktop_main_input_callback); + + main_view->poweroff_timer = + furi_timer_alloc(desktop_main_poweroff_timer_callback, FuriTimerTypeOnce, main_view); + + return main_view; +} + +void desktop_main_free(DesktopMainView* main_view) { + furi_assert(main_view); + view_free(main_view->view); + furi_timer_free(main_view->poweroff_timer); + free(main_view); +} diff --git a/applications/desktop/views/desktop_view_main.h b/applications/services/desktop/views/desktop_view_main.h similarity index 84% rename from applications/desktop/views/desktop_view_main.h rename to applications/services/desktop/views/desktop_view_main.h index 329d9548682..b5492b1be98 100644 --- a/applications/desktop/views/desktop_view_main.h +++ b/applications/services/desktop/views/desktop_view_main.h @@ -13,5 +13,6 @@ void desktop_main_set_callback( void* context); View* desktop_main_get_view(DesktopMainView* main_view); +void desktop_main_set_dummy_mode_state(DesktopMainView* main_view, bool dummy_mode); DesktopMainView* desktop_main_alloc(); void desktop_main_free(DesktopMainView* main_view); diff --git a/applications/desktop/views/desktop_view_pin_input.c b/applications/services/desktop/views/desktop_view_pin_input.c similarity index 88% rename from applications/desktop/views/desktop_view_pin_input.c rename to applications/services/desktop/views/desktop_view_pin_input.c index d6ce1a02a45..0894bb776f4 100644 --- a/applications/desktop/views/desktop_view_pin_input.c +++ b/applications/services/desktop/views/desktop_view_pin_input.c @@ -2,11 +2,11 @@ #include #include #include +#include #include -#include #include "desktop_view_pin_input.h" -#include "../desktop_settings/desktop_settings.h" +#include #define NO_ACTIVITY_TIMEOUT 15000 @@ -20,7 +20,7 @@ struct DesktopViewPinInput { DesktopViewPinInputCallback timeout_callback; DesktopViewPinInputDoneCallback done_callback; void* context; - TimerHandle_t timer; + FuriTimer* timer; }; typedef struct { @@ -77,7 +77,7 @@ static bool desktop_view_pin_input_input(InputEvent* event, void* context) { } break; default: - furi_assert(0); + furi_crash(); break; } } @@ -89,7 +89,7 @@ static bool desktop_view_pin_input_input(InputEvent* event, void* context) { pin_input->back_callback(pin_input->context); } - xTimerStart(pin_input->timer, 0); + furi_timer_start(pin_input->timer, NO_ACTIVITY_TIMEOUT); return true; } @@ -114,19 +114,21 @@ static void desktop_view_pin_input_draw_cells(Canvas* canvas, DesktopViewPinInpu } else { switch(model->pin.data[i]) { case InputKeyDown: - canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_down_7x9); + canvas_draw_icon_ex( + canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9, IconRotation180); break; case InputKeyUp: - canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_up7x9); + canvas_draw_icon_ex(canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9, IconRotation0); break; case InputKeyLeft: - canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_left_9x7); + canvas_draw_icon_ex( + canvas, x + 2, y + 3, &I_Pin_arrow_up_7x9, IconRotation270); break; case InputKeyRight: - canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_right_9x7); + canvas_draw_icon_ex(canvas, x + 2, y + 3, &I_Pin_arrow_up_7x9, IconRotation90); break; default: - furi_assert(0); + furi_crash(); break; } } @@ -146,7 +148,8 @@ static void desktop_view_pin_input_draw(Canvas* canvas, void* context) { desktop_view_pin_input_draw_cells(canvas, model); if((model->pin.length > 0) && !model->locked_input) { - canvas_draw_icon(canvas, 4, 53, &I_Pin_back_full_40x8); + canvas_draw_icon(canvas, 4, 53, &I_Pin_back_arrow_10x8); + canvas_draw_str(canvas, 16, 60, "= clear"); } if(model->button_label && ((model->pin.length >= MIN_PIN_SIZE) || model->locked_input)) { @@ -166,8 +169,8 @@ static void desktop_view_pin_input_draw(Canvas* canvas, void* context) { } } -void desktop_view_pin_input_timer_callback(TimerHandle_t timer) { - DesktopViewPinInput* pin_input = pvTimerGetTimerID(timer); +void desktop_view_pin_input_timer_callback(void* context) { + DesktopViewPinInput* pin_input = context; if(pin_input->timeout_callback) { pin_input->timeout_callback(pin_input->context); @@ -176,12 +179,12 @@ void desktop_view_pin_input_timer_callback(TimerHandle_t timer) { static void desktop_view_pin_input_enter(void* context) { DesktopViewPinInput* pin_input = context; - xTimerStart(pin_input->timer, portMAX_DELAY); + furi_timer_start(pin_input->timer, NO_ACTIVITY_TIMEOUT); } static void desktop_view_pin_input_exit(void* context) { DesktopViewPinInput* pin_input = context; - xTimerStop(pin_input->timer, portMAX_DELAY); + furi_timer_stop(pin_input->timer); } DesktopViewPinInput* desktop_view_pin_input_alloc(void) { @@ -191,12 +194,8 @@ DesktopViewPinInput* desktop_view_pin_input_alloc(void) { view_set_context(pin_input->view, pin_input); view_set_draw_callback(pin_input->view, desktop_view_pin_input_draw); view_set_input_callback(pin_input->view, desktop_view_pin_input_input); - pin_input->timer = xTimerCreate( - NULL, - pdMS_TO_TICKS(NO_ACTIVITY_TIMEOUT), - pdFALSE, - pin_input, - desktop_view_pin_input_timer_callback); + pin_input->timer = + furi_timer_alloc(desktop_view_pin_input_timer_callback, FuriTimerTypeOnce, pin_input); view_set_enter_callback(pin_input->view, desktop_view_pin_input_enter); view_set_exit_callback(pin_input->view, desktop_view_pin_input_exit); @@ -212,11 +211,7 @@ DesktopViewPinInput* desktop_view_pin_input_alloc(void) { void desktop_view_pin_input_free(DesktopViewPinInput* pin_input) { furi_assert(pin_input); - xTimerStop(pin_input->timer, portMAX_DELAY); - while(xTimerIsTimerActive(pin_input->timer)) { - furi_delay_tick(1); - } - xTimerDelete(pin_input->timer, portMAX_DELAY); + furi_timer_free(pin_input->timer); view_free(pin_input->view); free(pin_input); diff --git a/applications/desktop/views/desktop_view_pin_input.h b/applications/services/desktop/views/desktop_view_pin_input.h similarity index 96% rename from applications/desktop/views/desktop_view_pin_input.h rename to applications/services/desktop/views/desktop_view_pin_input.h index 3e39fd207e4..40eee4cc9b6 100644 --- a/applications/desktop/views/desktop_view_pin_input.h +++ b/applications/services/desktop/views/desktop_view_pin_input.h @@ -1,7 +1,7 @@ #pragma once #include -#include "desktop/desktop_settings/desktop_settings.h" +#include typedef void (*DesktopViewPinInputCallback)(void*); typedef void (*DesktopViewPinInputDoneCallback)(const PinCode* pin_code, void*); diff --git a/applications/desktop/views/desktop_view_pin_timeout.c b/applications/services/desktop/views/desktop_view_pin_timeout.c similarity index 83% rename from applications/desktop/views/desktop_view_pin_timeout.c rename to applications/services/desktop/views/desktop_view_pin_timeout.c index 6e1e807fdb1..f24ecc8ead3 100644 --- a/applications/desktop/views/desktop_view_pin_timeout.c +++ b/applications/services/desktop/views/desktop_view_pin_timeout.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -13,7 +12,7 @@ struct DesktopViewPinTimeout { View* view; - TimerHandle_t timer; + FuriTimer* timer; DesktopViewPinTimeoutDoneCallback callback; void* context; }; @@ -32,8 +31,8 @@ void desktop_view_pin_timeout_set_callback( instance->context = context; } -static void desktop_view_pin_timeout_timer_callback(TimerHandle_t timer) { - DesktopViewPinTimeout* instance = pvTimerGetTimerID(timer); +static void desktop_view_pin_timeout_timer_callback(void* context) { + DesktopViewPinTimeout* instance = context; bool stop = false; DesktopViewPinTimeoutModel* model = view_get_model(instance->view); @@ -45,7 +44,7 @@ static void desktop_view_pin_timeout_timer_callback(TimerHandle_t timer) { view_commit_model(instance->view, true); if(stop) { - xTimerStop(instance->timer, portMAX_DELAY); + furi_timer_stop(instance->timer); instance->callback(instance->context); } } @@ -67,21 +66,21 @@ static void desktop_view_pin_timeout_draw(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontSecondary); char str[30] = {0}; - snprintf(str, sizeof(str), "Timeout: %lds", model->time_left); + snprintf(str, sizeof(str), "Timeout: %lus", model->time_left); canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignCenter, str); } void desktop_view_pin_timeout_free(DesktopViewPinTimeout* instance) { view_free(instance->view); - xTimerDelete(instance->timer, portMAX_DELAY); + furi_timer_free(instance->timer); free(instance); } DesktopViewPinTimeout* desktop_view_pin_timeout_alloc(void) { DesktopViewPinTimeout* instance = malloc(sizeof(DesktopViewPinTimeout)); - instance->timer = xTimerCreate( - NULL, pdMS_TO_TICKS(1000), pdTRUE, instance, desktop_view_pin_timeout_timer_callback); + instance->timer = + furi_timer_alloc(desktop_view_pin_timeout_timer_callback, FuriTimerTypePeriodic, instance); instance->view = view_alloc(); view_allocate_model(instance->view, ViewModelTypeLockFree, sizeof(DesktopViewPinTimeoutModel)); @@ -101,7 +100,7 @@ void desktop_view_pin_timeout_start(DesktopViewPinTimeout* instance, uint32_t ti model->time_left = time_left; view_commit_model(instance->view, true); - xTimerStart(instance->timer, portMAX_DELAY); + furi_timer_start(instance->timer, 1000); } View* desktop_view_pin_timeout_get_view(DesktopViewPinTimeout* instance) { diff --git a/applications/desktop/views/desktop_view_pin_timeout.h b/applications/services/desktop/views/desktop_view_pin_timeout.h similarity index 100% rename from applications/desktop/views/desktop_view_pin_timeout.h rename to applications/services/desktop/views/desktop_view_pin_timeout.h diff --git a/applications/desktop/views/desktop_view_slideshow.c b/applications/services/desktop/views/desktop_view_slideshow.c similarity index 98% rename from applications/desktop/views/desktop_view_slideshow.c rename to applications/services/desktop/views/desktop_view_slideshow.c index 58a8f6d0c1b..09e155341c5 100644 --- a/applications/desktop/views/desktop_view_slideshow.c +++ b/applications/services/desktop/views/desktop_view_slideshow.c @@ -56,7 +56,7 @@ static bool desktop_view_slideshow_input(InputEvent* event, void* context) { instance->callback(DesktopSlideshowCompleted, instance->context); } update_view = true; - } else if(event->key == InputKeyOk) { + } else if(event->key == InputKeyOk && instance->timer) { if(event->type == InputTypePress) { furi_timer_start(instance->timer, DESKTOP_SLIDESHOW_POWEROFF_SHORT); } else if(event->type == InputTypeRelease) { diff --git a/applications/desktop/views/desktop_view_slideshow.h b/applications/services/desktop/views/desktop_view_slideshow.h similarity index 100% rename from applications/desktop/views/desktop_view_slideshow.h rename to applications/services/desktop/views/desktop_view_slideshow.h diff --git a/applications/services/dialogs/application.fam b/applications/services/dialogs/application.fam new file mode 100644 index 00000000000..eb2b40e28fb --- /dev/null +++ b/applications/services/dialogs/application.fam @@ -0,0 +1,11 @@ +App( + appid="dialogs", + name="DialogsSrv", + apptype=FlipperAppType.SERVICE, + entry_point="dialogs_srv", + cdefines=["SRV_DIALOGS"], + requires=["gui"], + stack_size=1 * 1024, + order=40, + sdk_headers=["dialogs.h"], +) diff --git a/applications/services/dialogs/dialogs.c b/applications/services/dialogs/dialogs.c new file mode 100644 index 00000000000..10c08a991b3 --- /dev/null +++ b/applications/services/dialogs/dialogs.c @@ -0,0 +1,56 @@ +#include "dialogs/dialogs_message.h" +#include "dialogs_i.h" +#include +#include "dialogs_module_file_browser.h" +#include "dialogs_module_message.h" + +void dialog_file_browser_set_basic_options( + DialogsFileBrowserOptions* options, + const char* extension, + const Icon* icon) { + options->extension = extension; + options->base_path = NULL; + options->skip_assets = true; + options->hide_dot_files = true; + options->icon = icon; + options->hide_ext = true; + options->item_loader_callback = NULL; + options->item_loader_context = NULL; +} + +static DialogsApp* dialogs_app_alloc() { + DialogsApp* app = malloc(sizeof(DialogsApp)); + app->message_queue = furi_message_queue_alloc(8, sizeof(DialogsAppMessage)); + + return app; +} + +static void dialogs_app_process_message(DialogsApp* app, DialogsAppMessage* message) { + UNUSED(app); + switch(message->command) { + case DialogsAppCommandFileBrowser: + message->return_data->bool_value = + dialogs_app_process_module_file_browser(&message->data->file_browser); + break; + case DialogsAppCommandDialog: + message->return_data->dialog_value = + dialogs_app_process_module_message(&message->data->dialog); + break; + } + api_lock_unlock(message->lock); +} + +int32_t dialogs_srv(void* p) { + UNUSED(p); + DialogsApp* app = dialogs_app_alloc(); + furi_record_create(RECORD_DIALOGS, app); + + DialogsAppMessage message; + while(1) { + if(furi_message_queue_get(app->message_queue, &message, FuriWaitForever) == FuriStatusOk) { + dialogs_app_process_message(app, &message); + } + } + + return 0; +} diff --git a/applications/services/dialogs/dialogs.h b/applications/services/dialogs/dialogs.h new file mode 100644 index 00000000000..39b15c67c2e --- /dev/null +++ b/applications/services/dialogs/dialogs.h @@ -0,0 +1,171 @@ +#pragma once +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/****************** COMMON ******************/ + +#define RECORD_DIALOGS "dialogs" + +typedef struct DialogsApp DialogsApp; + +/****************** FILE BROWSER ******************/ + +/** + * File browser dialog extra options. + * This can be default-initialized using {@link dialog_file_browser_set_basic_options}. + * @param extension file extension to be offered for selection + * @param base_path root folder path for navigation with back key + * @param skip_assets true - do not show assets folders + * @param hide_dot_files true - hide dot files + * @param icon file icon pointer, NULL for default icon + * @param hide_ext true - hide extensions for files + * @param item_loader_callback callback function for providing custom icon & entry name + * @param hide_ext callback context + */ +typedef struct { + const char* extension; + const char* base_path; + bool skip_assets; + bool hide_dot_files; + const Icon* icon; + bool hide_ext; + FileBrowserLoadItemCallback item_loader_callback; + void* item_loader_context; +} DialogsFileBrowserOptions; + +/** + * Initialize file browser dialog options and set default values. + * This is guaranteed to initialize all fields + * so it is safe to pass pointer to uninitialized {@code options} + * and assume that the data behind it becomes fully initialized after the call. + * @param options pointer to options structure + * @param extension file extension to filter + * @param icon file icon pointer, NULL for default icon + */ +void dialog_file_browser_set_basic_options( + DialogsFileBrowserOptions* options, + const char* extension, + const Icon* icon); + +/** + * Shows and processes the file browser dialog + * @param context api pointer + * @param result_path selected file path string pointer + * @param path preselected file path string pointer + * @param options file browser dialog extra options, may be null + * @return bool whether a file was selected + */ +bool dialog_file_browser_show( + DialogsApp* context, + FuriString* result_path, + FuriString* path, + const DialogsFileBrowserOptions* options); + +/****************** MESSAGE ******************/ + +/** + * Message result type + */ +typedef enum { + DialogMessageButtonBack, + DialogMessageButtonLeft, + DialogMessageButtonCenter, + DialogMessageButtonRight, +} DialogMessageButton; + +/** + * Message struct + */ +typedef struct DialogMessage DialogMessage; + +/** + * Allocate and fill message + * @return DialogMessage* + */ +DialogMessage* dialog_message_alloc(); + +/** + * Free message struct + * @param message message pointer + */ +void dialog_message_free(DialogMessage* message); + +/** + * Set message text + * @param message message pointer + * @param text text, can be NULL if you don't want to display the text + * @param x x position + * @param y y position + * @param horizontal horizontal alignment + * @param vertical vertical alignment + */ +void dialog_message_set_text( + DialogMessage* message, + const char* text, + uint8_t x, + uint8_t y, + Align horizontal, + Align vertical); + +/** + * Set message header + * @param message message pointer + * @param text text, can be NULL if you don't want to display the header + * @param x x position + * @param y y position + * @param horizontal horizontal alignment + * @param vertical vertical alignment + */ +void dialog_message_set_header( + DialogMessage* message, + const char* text, + uint8_t x, + uint8_t y, + Align horizontal, + Align vertical); + +/** + * Set message icon + * @param message message pointer + * @param icon icon pointer, can be NULL if you don't want to display the icon + * @param x x position + * @param y y position + */ +void dialog_message_set_icon(DialogMessage* message, const Icon* icon, uint8_t x, uint8_t y); + +/** + * Set message buttons text, button text can be NULL if you don't want to display and process some buttons + * @param message message pointer + * @param left left button text, can be NULL if you don't want to display the left button + * @param center center button text, can be NULL if you don't want to display the center button + * @param right right button text, can be NULL if you don't want to display the right button + */ +void dialog_message_set_buttons( + DialogMessage* message, + const char* left, + const char* center, + const char* right); + +/** + * Show message from filled struct + * @param context api pointer + * @param message message struct pointer to be shown + * @return DialogMessageButton type + */ +DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* message); + +/** + * Show SD error message (with question sign) + * @param context + * @param error_text + */ +void dialog_message_show_storage_error(DialogsApp* context, const char* error_text); + +#ifdef __cplusplus +} +#endif diff --git a/applications/services/dialogs/dialogs_api.c b/applications/services/dialogs/dialogs_api.c new file mode 100644 index 00000000000..4723a1f91a1 --- /dev/null +++ b/applications/services/dialogs/dialogs_api.c @@ -0,0 +1,100 @@ +#include "dialogs_message.h" +#include "dialogs_i.h" +#include +#include +#include + +/****************** File browser ******************/ + +bool dialog_file_browser_show( + DialogsApp* context, + FuriString* result_path, + FuriString* path, + const DialogsFileBrowserOptions* options) { + 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 : "", + .result_path = result_path, + .file_icon = options ? options->icon : NULL, + .hide_ext = options ? options->hide_ext : true, + .skip_assets = options ? options->skip_assets : true, + .hide_dot_files = options ? options->hide_dot_files : true, + .preselected_filename = path, + .item_callback = options ? options->item_loader_callback : NULL, + .item_callback_context = options ? options->item_loader_context : NULL, + .base_path = furi_string_get_cstr(base_path), + }}; + + DialogsAppReturn return_data; + DialogsAppMessage message = { + .lock = lock, + .command = DialogsAppCommandFileBrowser, + .data = &data, + .return_data = &return_data, + }; + + furi_check( + 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; +} + +/****************** Message ******************/ + +DialogMessageButton dialog_message_show(DialogsApp* context, const DialogMessage* dialog_message) { + FuriApiLock lock = api_lock_alloc_locked(); + furi_check(lock != NULL); + + DialogsAppData data = { + .dialog = { + .message = dialog_message, + }}; + + DialogsAppReturn return_data; + DialogsAppMessage message = { + .lock = lock, + .command = DialogsAppCommandDialog, + .data = &data, + .return_data = &return_data, + }; + + furi_check( + furi_message_queue_put(context->message_queue, &message, FuriWaitForever) == FuriStatusOk); + api_lock_wait_unlock_and_free(lock); + + return return_data.dialog_value; +} + +/****************** Storage error ******************/ + +void dialog_message_show_storage_error(DialogsApp* context, const char* error_text) { + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_text(message, error_text, 88, 32, AlignCenter, AlignCenter); + dialog_message_set_icon(message, &I_SDQuestion_35x43, 5, 6); + dialog_message_set_buttons(message, "Back", NULL, NULL); + dialog_message_show(context, message); + dialog_message_free(message); +} diff --git a/applications/services/dialogs/dialogs_i.h b/applications/services/dialogs/dialogs_i.h new file mode 100644 index 00000000000..29417b41b52 --- /dev/null +++ b/applications/services/dialogs/dialogs_i.h @@ -0,0 +1,16 @@ +#pragma once +#include "dialogs.h" +#include "dialogs_message.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif +struct DialogsApp { + FuriMessageQueue* message_queue; +}; + +#ifdef __cplusplus +} +#endif diff --git a/applications/dialogs/dialogs_message.h b/applications/services/dialogs/dialogs_message.h similarity index 78% rename from applications/dialogs/dialogs_message.h rename to applications/services/dialogs/dialogs_message.h index ccfbdece5f3..1c8c4fb50aa 100644 --- a/applications/dialogs/dialogs_message.h +++ b/applications/services/dialogs/dialogs_message.h @@ -1,8 +1,7 @@ #pragma once #include #include "dialogs_i.h" -#include "dialogs_api_lock.h" -#include "m-string.h" +#include #ifdef __cplusplus extern "C" { @@ -12,9 +11,13 @@ typedef struct { const char* extension; bool skip_assets; bool hide_ext; + bool hide_dot_files; const Icon* file_icon; - string_ptr result_path; - string_ptr preselected_filename; + FuriString* result_path; + FuriString* preselected_filename; + FileBrowserLoadItemCallback item_callback; + void* item_callback_context; + const char* base_path; } DialogsAppMessageDataFileBrowser; typedef struct { diff --git a/applications/services/dialogs/dialogs_module_file_browser.c b/applications/services/dialogs/dialogs_module_file_browser.c new file mode 100644 index 00000000000..79d151c590a --- /dev/null +++ b/applications/services/dialogs/dialogs_module_file_browser.c @@ -0,0 +1,67 @@ +#include "dialogs_i.h" + +#include +#include + +typedef struct { + FuriApiLock lock; + bool result; +} DialogsAppFileBrowserContext; + +static void dialogs_app_file_browser_back_callback(void* context) { + furi_assert(context); + DialogsAppFileBrowserContext* file_browser_context = context; + file_browser_context->result = false; + api_lock_unlock(file_browser_context->lock); +} + +static void dialogs_app_file_browser_callback(void* context) { + furi_assert(context); + DialogsAppFileBrowserContext* file_browser_context = context; + file_browser_context->result = true; + api_lock_unlock(file_browser_context->lock); +} + +bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrowser* data) { + bool ret = false; + Gui* gui = furi_record_open(RECORD_GUI); + + DialogsAppFileBrowserContext* file_browser_context = + malloc(sizeof(DialogsAppFileBrowserContext)); + file_browser_context->lock = api_lock_alloc_locked(); + + ViewHolder* view_holder = view_holder_alloc(); + view_holder_attach_to_gui(view_holder, gui); + view_holder_set_back_callback( + view_holder, dialogs_app_file_browser_back_callback, file_browser_context); + + FileBrowser* file_browser = file_browser_alloc(data->result_path); + file_browser_set_callback( + file_browser, dialogs_app_file_browser_callback, file_browser_context); + file_browser_configure( + file_browser, + data->extension, + data->base_path, + data->skip_assets, + data->hide_dot_files, + data->file_icon, + data->hide_ext); + file_browser_set_item_callback(file_browser, data->item_callback, data->item_callback_context); + file_browser_start(file_browser, data->preselected_filename); + + view_holder_set_view(view_holder, file_browser_get_view(file_browser)); + view_holder_start(view_holder); + api_lock_wait_unlock(file_browser_context->lock); + + ret = file_browser_context->result; + + view_holder_stop(view_holder); + view_holder_free(view_holder); + file_browser_stop(file_browser); + file_browser_free(file_browser); + api_lock_free(file_browser_context->lock); + free(file_browser_context); + furi_record_close(RECORD_GUI); + + return ret; +} diff --git a/applications/dialogs/dialogs_module_file_browser.h b/applications/services/dialogs/dialogs_module_file_browser.h similarity index 100% rename from applications/dialogs/dialogs_module_file_browser.h rename to applications/services/dialogs/dialogs_module_file_browser.h diff --git a/applications/dialogs/dialogs_module_message.c b/applications/services/dialogs/dialogs_module_message.c similarity index 94% rename from applications/dialogs/dialogs_module_message.c rename to applications/services/dialogs/dialogs_module_message.c index 8d1c3ba7504..879ba9baab5 100644 --- a/applications/dialogs/dialogs_module_message.c +++ b/applications/services/dialogs/dialogs_module_message.c @@ -1,5 +1,5 @@ #include "dialogs_i.h" -#include "dialogs_api_lock.h" +#include #include typedef struct { @@ -30,7 +30,7 @@ static void dialogs_app_message_back_callback(void* context) { furi_assert(context); DialogsAppMessageContext* message_context = context; message_context->result = DialogMessageButtonBack; - API_LOCK_UNLOCK(message_context->lock); + api_lock_unlock(message_context->lock); } static void dialogs_app_message_callback(DialogExResult result, void* context) { @@ -49,7 +49,7 @@ static void dialogs_app_message_callback(DialogExResult result, void* context) { default: break; } - API_LOCK_UNLOCK(message_context->lock); + api_lock_unlock(message_context->lock); } DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDataDialog* data) { @@ -57,7 +57,7 @@ DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDa Gui* gui = furi_record_open(RECORD_GUI); const DialogMessage* message = data->message; DialogsAppMessageContext* message_context = malloc(sizeof(DialogsAppMessageContext)); - message_context->lock = API_LOCK_INIT_LOCKED(); + message_context->lock = api_lock_alloc_locked(); ViewHolder* view_holder = view_holder_alloc(); view_holder_attach_to_gui(view_holder, gui); @@ -87,14 +87,14 @@ DialogMessageButton dialogs_app_process_module_message(const DialogsAppMessageDa view_holder_set_view(view_holder, dialog_ex_get_view(dialog_ex)); view_holder_start(view_holder); - API_LOCK_WAIT_UNTIL_UNLOCK(message_context->lock); + api_lock_wait_unlock(message_context->lock); ret = message_context->result; view_holder_stop(view_holder); view_holder_free(view_holder); dialog_ex_free(dialog_ex); - API_LOCK_FREE(message_context->lock); + api_lock_free(message_context->lock); free(message_context); furi_record_close(RECORD_GUI); diff --git a/applications/dialogs/dialogs_module_message.h b/applications/services/dialogs/dialogs_module_message.h similarity index 100% rename from applications/dialogs/dialogs_module_message.h rename to applications/services/dialogs/dialogs_module_message.h diff --git a/applications/services/dolphin/application.fam b/applications/services/dolphin/application.fam new file mode 100644 index 00000000000..78a097e678c --- /dev/null +++ b/applications/services/dolphin/application.fam @@ -0,0 +1,10 @@ +App( + appid="dolphin", + name="DolphinSrv", + apptype=FlipperAppType.SERVICE, + entry_point="dolphin_srv", + cdefines=["SRV_DOLPHIN"], + stack_size=1 * 1024, + order=50, + sdk_headers=["dolphin.h"], +) diff --git a/applications/dolphin/dolphin.c b/applications/services/dolphin/dolphin.c similarity index 75% rename from applications/dolphin/dolphin.c rename to applications/services/dolphin/dolphin.c index 41eeef3b1d0..bef7c4a2853 100644 --- a/applications/dolphin/dolphin.c +++ b/applications/services/dolphin/dolphin.c @@ -1,7 +1,6 @@ #include "dolphin/dolphin.h" #include "dolphin/helpers/dolphin_state.h" #include "dolphin_i.h" -#include "portmacro.h" #include "projdefs.h" #include #include @@ -13,12 +12,13 @@ static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin); -void dolphin_deed(Dolphin* dolphin, DolphinDeed deed) { - furi_assert(dolphin); +void dolphin_deed(DolphinDeed deed) { + Dolphin* dolphin = (Dolphin*)furi_record_open(RECORD_DOLPHIN); DolphinEvent event; event.type = DolphinEventTypeDeed; event.deed = deed; dolphin_event_send_async(dolphin, &event); + furi_record_close(RECORD_DOLPHIN); } DolphinStats dolphin_stats(Dolphin* dolphin) { @@ -44,8 +44,8 @@ void dolphin_flush(Dolphin* dolphin) { dolphin_event_send_wait(dolphin, &event); } -void dolphin_butthurt_timer_callback(TimerHandle_t xTimer) { - Dolphin* dolphin = pvTimerGetTimerID(xTimer); +void dolphin_butthurt_timer_callback(void* context) { + Dolphin* dolphin = context; furi_assert(dolphin); DolphinEvent event; @@ -53,8 +53,8 @@ void dolphin_butthurt_timer_callback(TimerHandle_t xTimer) { dolphin_event_send_async(dolphin, &event); } -void dolphin_flush_timer_callback(TimerHandle_t xTimer) { - Dolphin* dolphin = pvTimerGetTimerID(xTimer); +void dolphin_flush_timer_callback(void* context) { + Dolphin* dolphin = context; furi_assert(dolphin); DolphinEvent event; @@ -62,11 +62,11 @@ void dolphin_flush_timer_callback(TimerHandle_t xTimer) { dolphin_event_send_async(dolphin, &event); } -void dolphin_clear_limits_timer_callback(TimerHandle_t xTimer) { - Dolphin* dolphin = pvTimerGetTimerID(xTimer); +void dolphin_clear_limits_timer_callback(void* context) { + Dolphin* dolphin = context; furi_assert(dolphin); - xTimerChangePeriod(dolphin->clear_limits_timer, HOURS_IN_TICKS(24), portMAX_DELAY); + furi_timer_start(dolphin->clear_limits_timer, HOURS_IN_TICKS(24)); DolphinEvent event; event.type = DolphinEventTypeClearLimits; @@ -79,25 +79,16 @@ Dolphin* dolphin_alloc() { dolphin->state = dolphin_state_alloc(); dolphin->event_queue = furi_message_queue_alloc(8, sizeof(DolphinEvent)); dolphin->pubsub = furi_pubsub_alloc(); - dolphin->butthurt_timer = xTimerCreate( - NULL, HOURS_IN_TICKS(2 * 24), pdTRUE, dolphin, dolphin_butthurt_timer_callback); + dolphin->butthurt_timer = + furi_timer_alloc(dolphin_butthurt_timer_callback, FuriTimerTypePeriodic, dolphin); dolphin->flush_timer = - xTimerCreate(NULL, 30 * 1000, pdFALSE, dolphin, dolphin_flush_timer_callback); - dolphin->clear_limits_timer = xTimerCreate( - NULL, HOURS_IN_TICKS(24), pdTRUE, dolphin, dolphin_clear_limits_timer_callback); + furi_timer_alloc(dolphin_flush_timer_callback, FuriTimerTypeOnce, dolphin); + dolphin->clear_limits_timer = + furi_timer_alloc(dolphin_clear_limits_timer_callback, FuriTimerTypePeriodic, dolphin); return dolphin; } -void dolphin_free(Dolphin* dolphin) { - furi_assert(dolphin); - - dolphin_state_free(dolphin->state); - furi_message_queue_free(dolphin->event_queue); - - free(dolphin); -} - void dolphin_event_send_async(Dolphin* dolphin, DolphinEvent* event) { furi_assert(dolphin); furi_assert(event); @@ -133,14 +124,14 @@ FuriPubSub* dolphin_get_pubsub(Dolphin* dolphin) { static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin) { furi_assert(dolphin); - TickType_t now_ticks = xTaskGetTickCount(); - TickType_t timer_expires_at = xTimerGetExpiryTime(dolphin->clear_limits_timer); + uint32_t now_ticks = furi_get_tick(); + uint32_t timer_expires_at = furi_timer_get_expire_time(dolphin->clear_limits_timer); if((timer_expires_at - now_ticks) > HOURS_IN_TICKS(0.1)) { FuriHalRtcDateTime date; furi_hal_rtc_get_datetime(&date); - TickType_t now_time_in_ms = ((date.hour * 60 + date.minute) * 60 + date.second) * 1000; - TickType_t time_to_clear_limits = 0; + uint32_t now_time_in_ms = ((date.hour * 60 + date.minute) * 60 + date.second) * 1000; + uint32_t time_to_clear_limits = 0; if(date.hour < 5) { time_to_clear_limits = HOURS_IN_TICKS(5) - now_time_in_ms; @@ -148,19 +139,25 @@ static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin) { time_to_clear_limits = HOURS_IN_TICKS(24 + 5) - now_time_in_ms; } - xTimerChangePeriod(dolphin->clear_limits_timer, time_to_clear_limits, portMAX_DELAY); + furi_timer_start(dolphin->clear_limits_timer, time_to_clear_limits); } } 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); dolphin_state_load(dolphin->state); - xTimerReset(dolphin->butthurt_timer, portMAX_DELAY); + furi_timer_restart(dolphin->butthurt_timer, HOURS_IN_TICKS(2 * 24)); dolphin_update_clear_limits_timer_period(dolphin); - xTimerReset(dolphin->clear_limits_timer, portMAX_DELAY); + furi_timer_restart(dolphin->clear_limits_timer, HOURS_IN_TICKS(24)); DolphinEvent event; while(1) { @@ -170,8 +167,8 @@ int32_t dolphin_srv(void* p) { dolphin_state_on_deed(dolphin->state, event.deed); DolphinPubsubEvent event = DolphinPubsubEventUpdate; furi_pubsub_publish(dolphin->pubsub, &event); - xTimerReset(dolphin->butthurt_timer, portMAX_DELAY); - xTimerReset(dolphin->flush_timer, portMAX_DELAY); + furi_timer_restart(dolphin->butthurt_timer, HOURS_IN_TICKS(2 * 24)); + furi_timer_restart(dolphin->flush_timer, 30 * 1000); } else if(event.type == DolphinEventTypeStats) { event.stats->icounter = dolphin->state->data.icounter; event.stats->butthurt = dolphin->state->data.butthurt; @@ -198,7 +195,7 @@ int32_t dolphin_srv(void* p) { } } - dolphin_free(dolphin); + furi_crash("That was unexpected"); return 0; } diff --git a/applications/services/dolphin/dolphin.h b/applications/services/dolphin/dolphin.h new file mode 100644 index 00000000000..1035247e718 --- /dev/null +++ b/applications/services/dolphin/dolphin.h @@ -0,0 +1,51 @@ +#pragma once + +#include "helpers/dolphin_deed.h" + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define RECORD_DOLPHIN "dolphin" + +typedef struct Dolphin Dolphin; + +typedef struct { + uint32_t icounter; + uint32_t butthurt; + uint64_t timestamp; + uint8_t level; + bool level_up_is_pending; +} DolphinStats; + +typedef enum { + DolphinPubsubEventUpdate, +} DolphinPubsubEvent; + +/** Deed complete notification. Call it on deed completion. + * See dolphin_deed.h for available deeds. In futures it will become part of assets. + * Thread safe, async + */ +void dolphin_deed(DolphinDeed deed); + +/** Retrieve dolphin stats + * Thread safe, blocking + */ +DolphinStats dolphin_stats(Dolphin* dolphin); + +/** Flush dolphin queue and save state + * Thread safe, blocking + */ +void dolphin_flush(Dolphin* dolphin); + +void dolphin_upgrade_level(Dolphin* dolphin); + +FuriPubSub* dolphin_get_pubsub(Dolphin* dolphin); + +#ifdef __cplusplus +} +#endif diff --git a/applications/dolphin/dolphin_i.h b/applications/services/dolphin/dolphin_i.h similarity index 85% rename from applications/dolphin/dolphin_i.h rename to applications/services/dolphin/dolphin_i.h index 4bb0df08ee6..2d716c18138 100644 --- a/applications/dolphin/dolphin_i.h +++ b/applications/services/dolphin/dolphin_i.h @@ -30,15 +30,13 @@ struct Dolphin { // Queue FuriMessageQueue* event_queue; FuriPubSub* pubsub; - TimerHandle_t butthurt_timer; - TimerHandle_t flush_timer; - TimerHandle_t clear_limits_timer; + FuriTimer* butthurt_timer; + FuriTimer* flush_timer; + FuriTimer* clear_limits_timer; }; Dolphin* dolphin_alloc(); -void dolphin_free(Dolphin* dolphin); - void dolphin_event_send_async(Dolphin* dolphin, DolphinEvent* event); void dolphin_event_send_wait(Dolphin* dolphin, DolphinEvent* event); diff --git a/applications/dolphin/helpers/dolphin_deed.c b/applications/services/dolphin/helpers/dolphin_deed.c similarity index 75% rename from applications/dolphin/helpers/dolphin_deed.c rename to applications/services/dolphin/helpers/dolphin_deed.c index b112d82a1c9..f1f42b770fd 100644 --- a/applications/dolphin/helpers/dolphin_deed.c +++ b/applications/services/dolphin/helpers/dolphin_deed.c @@ -18,13 +18,15 @@ static const DolphinDeedWeight dolphin_deed_weights[] = { {1, DolphinAppNfc}, // DolphinDeedNfcRead {3, DolphinAppNfc}, // DolphinDeedNfcReadSuccess {3, DolphinAppNfc}, // DolphinDeedNfcSave + {1, DolphinAppNfc}, // DolphinDeedNfcDetectReader {2, DolphinAppNfc}, // DolphinDeedNfcEmulate - {2, DolphinAppNfc}, // DolphinDeedNfcAdd + {2, DolphinAppNfc}, // DolphinDeedNfcMfcAdd + {1, DolphinAppNfc}, // DolphinDeedNfcAddSave + {1, DolphinAppNfc}, // DolphinDeedNfcAddEmulate {1, DolphinAppIr}, // DolphinDeedIrSend {3, DolphinAppIr}, // DolphinDeedIrLearnSuccess {3, DolphinAppIr}, // DolphinDeedIrSave - {2, DolphinAppIr}, // DolphinDeedIrBruteForce {1, DolphinAppIbutton}, // DolphinDeedIbuttonRead {3, DolphinAppIbutton}, // DolphinDeedIbuttonReadSuccess @@ -33,17 +35,23 @@ static const DolphinDeedWeight dolphin_deed_weights[] = { {2, DolphinAppIbutton}, // DolphinDeedIbuttonAdd {3, DolphinAppBadusb}, // DolphinDeedBadUsbPlayScript - {3, DolphinAppU2f}, // DolphinDeedU2fAuthorized + {3, DolphinAppPlugin}, // DolphinDeedU2fAuthorized + + {1, DolphinAppPlugin}, // DolphinDeedGpioUartBridge + + {2, DolphinAppPlugin}, // DolphinDeedPluginStart + {1, DolphinAppPlugin}, // DolphinDeedPluginGameStart + {10, DolphinAppPlugin}, // DolphinDeedPluginGameWin }; static uint8_t dolphin_deed_limits[] = { - 15, // DolphinAppSubGhz - 15, // DolphinAppRfid - 15, // DolphinAppNfc - 15, // DolphinAppIr - 15, // DolphinAppIbutton - 15, // DolphinAppBadusb - 15, // DolphinAppU2f + 20, // DolphinAppSubGhz + 20, // DolphinAppRfid + 20, // DolphinAppNfc + 20, // DolphinAppIr + 20, // DolphinAppIbutton + 20, // DolphinAppBadusb + 20, // DolphinAppPlugin }; _Static_assert(COUNT_OF(dolphin_deed_weights) == DolphinDeedMAX, "dolphin_deed_weights size error"); diff --git a/applications/dolphin/helpers/dolphin_deed.h b/applications/services/dolphin/helpers/dolphin_deed.h similarity index 84% rename from applications/dolphin/helpers/dolphin_deed.h rename to applications/services/dolphin/helpers/dolphin_deed.h index 1f63db3ff8f..c9cd18f316f 100644 --- a/applications/dolphin/helpers/dolphin_deed.h +++ b/applications/services/dolphin/helpers/dolphin_deed.h @@ -13,7 +13,7 @@ typedef enum { DolphinAppIr, DolphinAppIbutton, DolphinAppBadusb, - DolphinAppU2f, + DolphinAppPlugin, DolphinAppMAX, } DolphinApp; @@ -34,13 +34,15 @@ typedef enum { DolphinDeedNfcRead, DolphinDeedNfcReadSuccess, DolphinDeedNfcSave, + DolphinDeedNfcDetectReader, DolphinDeedNfcEmulate, - DolphinDeedNfcAdd, + DolphinDeedNfcMfcAdd, + DolphinDeedNfcAddSave, + DolphinDeedNfcAddEmulate, DolphinDeedIrSend, DolphinDeedIrLearnSuccess, DolphinDeedIrSave, - DolphinDeedIrBruteForce, DolphinDeedIbuttonRead, DolphinDeedIbuttonReadSuccess, @@ -51,6 +53,11 @@ typedef enum { DolphinDeedBadUsbPlayScript, DolphinDeedU2fAuthorized, + DolphinDeedGpioUartBridge, + + DolphinDeedPluginStart, + DolphinDeedPluginGameStart, + DolphinDeedPluginGameWin, DolphinDeedMAX, diff --git a/applications/dolphin/helpers/dolphin_state.c b/applications/services/dolphin/helpers/dolphin_state.c similarity index 98% rename from applications/dolphin/helpers/dolphin_state.c rename to applications/services/dolphin/helpers/dolphin_state.c index 76f38a5fd45..14f080464bf 100644 --- a/applications/dolphin/helpers/dolphin_state.c +++ b/applications/services/dolphin/helpers/dolphin_state.c @@ -14,8 +14,8 @@ #define DOLPHIN_STATE_PATH INT_PATH(DOLPHIN_STATE_FILE_NAME) #define DOLPHIN_STATE_HEADER_MAGIC 0xD0 #define DOLPHIN_STATE_HEADER_VERSION 0x01 -#define LEVEL2_THRESHOLD 735 -#define LEVEL3_THRESHOLD 2940 +#define LEVEL2_THRESHOLD 300 +#define LEVEL3_THRESHOLD 1800 #define BUTTHURT_MAX 14 #define BUTTHURT_MIN 0 @@ -171,7 +171,7 @@ void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed) { FURI_LOG_D( TAG, - "icounter %d, butthurt %d", + "icounter %lu, butthurt %ld", dolphin_state->data.icounter, dolphin_state->data.butthurt); } diff --git a/applications/dolphin/helpers/dolphin_state.h b/applications/services/dolphin/helpers/dolphin_state.h similarity index 100% rename from applications/dolphin/helpers/dolphin_state.h rename to applications/services/dolphin/helpers/dolphin_state.h diff --git a/applications/dolphin/helpers/dolphin_state_filename.h b/applications/services/dolphin/helpers/dolphin_state_filename.h similarity index 100% rename from applications/dolphin/helpers/dolphin_state_filename.h rename to applications/services/dolphin/helpers/dolphin_state_filename.h diff --git a/applications/services/gui/application.fam b/applications/services/gui/application.fam new file mode 100644 index 00000000000..869d964dd01 --- /dev/null +++ b/applications/services/gui/application.fam @@ -0,0 +1,37 @@ +App( + appid="gui", + name="GuiSrv", + apptype=FlipperAppType.SERVICE, + entry_point="gui_srv", + cdefines=["SRV_GUI"], + requires=[ + "input", + "notification", + ], + stack_size=2 * 1024, + order=70, + sdk_headers=[ + "gui.h", + "icon_i.h", + "elements.h", + "view_dispatcher.h", + "view_stack.h", + "modules/button_menu.h", + "modules/byte_input.h", + "modules/popup.h", + "modules/text_input.h", + "modules/widget.h", + "modules/validators.h", + "modules/file_browser.h", + "modules/button_panel.h", + "modules/variable_item_list.h", + "modules/file_browser_worker.h", + "modules/menu.h", + "modules/dialog_ex.h", + "modules/loading.h", + "modules/text_box.h", + "modules/submenu.h", + "modules/widget_elements/widget_element.h", + "modules/empty_screen.h", + ], +) diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c new file mode 100644 index 00000000000..44adcd93951 --- /dev/null +++ b/applications/services/gui/canvas.c @@ -0,0 +1,544 @@ +#include "canvas_i.h" +#include "icon_i.h" +#include "icon_animation_i.h" + +#include +#include +#include +#include + +const CanvasFontParameters canvas_font_params[FontTotalNumber] = { + [FontPrimary] = {.leading_default = 12, .leading_min = 11, .height = 8, .descender = 2}, + [FontSecondary] = {.leading_default = 11, .leading_min = 9, .height = 7, .descender = 2}, + [FontKeyboard] = {.leading_default = 11, .leading_min = 9, .height = 7, .descender = 2}, + [FontBigNumbers] = {.leading_default = 18, .leading_min = 16, .height = 15, .descender = 0}, +}; + +Canvas* canvas_init() { + Canvas* canvas = malloc(sizeof(Canvas)); + canvas->compress_icon = compress_icon_alloc(); + + // Setup u8g2 + u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); + canvas->orientation = CanvasOrientationHorizontal; + // Initialize display + u8g2_InitDisplay(&canvas->fb); + // Wake up display + u8g2_SetPowerSave(&canvas->fb, 0); + + // Clear buffer and send to device + canvas_clear(canvas); + canvas_commit(canvas); + + return canvas; +} + +void canvas_free(Canvas* canvas) { + furi_assert(canvas); + compress_icon_free(canvas->compress_icon); + free(canvas); +} + +void canvas_reset(Canvas* canvas) { + furi_assert(canvas); + + canvas_clear(canvas); + + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + canvas_set_font_direction(canvas, CanvasDirectionLeftToRight); +} + +void canvas_commit(Canvas* canvas) { + furi_assert(canvas); + u8g2_SendBuffer(&canvas->fb); +} + +uint8_t* canvas_get_buffer(Canvas* canvas) { + furi_assert(canvas); + return u8g2_GetBufferPtr(&canvas->fb); +} + +size_t canvas_get_buffer_size(const Canvas* canvas) { + furi_assert(canvas); + return u8g2_GetBufferTileWidth(&canvas->fb) * u8g2_GetBufferTileHeight(&canvas->fb) * 8; +} + +void canvas_frame_set( + Canvas* canvas, + uint8_t offset_x, + uint8_t offset_y, + uint8_t width, + uint8_t height) { + furi_assert(canvas); + canvas->offset_x = offset_x; + canvas->offset_y = offset_y; + canvas->width = width; + canvas->height = height; +} + +uint8_t canvas_width(const Canvas* canvas) { + furi_assert(canvas); + return canvas->width; +} + +uint8_t canvas_height(const Canvas* canvas) { + furi_assert(canvas); + return canvas->height; +} + +uint8_t canvas_current_font_height(const Canvas* canvas) { + furi_assert(canvas); + uint8_t font_height = u8g2_GetMaxCharHeight(&canvas->fb); + + if(canvas->fb.font == u8g2_font_haxrcorp4089_tr) { + font_height += 1; + } + + return font_height; +} + +const CanvasFontParameters* canvas_get_font_params(const Canvas* canvas, Font font) { + furi_assert(canvas); + furi_assert(font < FontTotalNumber); + return &canvas_font_params[font]; +} + +void canvas_clear(Canvas* canvas) { + furi_assert(canvas); + u8g2_ClearBuffer(&canvas->fb); +} + +void canvas_set_color(Canvas* canvas, Color color) { + furi_assert(canvas); + u8g2_SetDrawColor(&canvas->fb, color); +} + +void canvas_set_font_direction(Canvas* canvas, CanvasDirection dir) { + furi_assert(canvas); + u8g2_SetFontDirection(&canvas->fb, dir); +} + +void canvas_invert_color(Canvas* canvas) { + canvas->fb.draw_color = !canvas->fb.draw_color; +} + +void canvas_set_font(Canvas* canvas, Font font) { + furi_assert(canvas); + u8g2_SetFontMode(&canvas->fb, 1); + if(font == FontPrimary) { + u8g2_SetFont(&canvas->fb, u8g2_font_helvB08_tr); + } else if(font == FontSecondary) { + u8g2_SetFont(&canvas->fb, u8g2_font_haxrcorp4089_tr); + } else if(font == FontKeyboard) { + u8g2_SetFont(&canvas->fb, u8g2_font_profont11_mr); + } else if(font == FontBigNumbers) { + u8g2_SetFont(&canvas->fb, u8g2_font_profont22_tn); + } else { + furi_crash(); + } +} + +void canvas_set_custom_u8g2_font(Canvas* canvas, const uint8_t* font) { + furi_assert(canvas); + u8g2_SetFontMode(&canvas->fb, 1); + u8g2_SetFont(&canvas->fb, font); +} + +void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str) { + furi_assert(canvas); + if(!str) return; + x += canvas->offset_x; + y += canvas->offset_y; + u8g2_DrawStr(&canvas->fb, x, y, str); +} + +void canvas_draw_str_aligned( + Canvas* canvas, + uint8_t x, + uint8_t y, + Align horizontal, + Align vertical, + const char* str) { + furi_assert(canvas); + if(!str) return; + x += canvas->offset_x; + y += canvas->offset_y; + + switch(horizontal) { + case AlignLeft: + break; + case AlignRight: + x -= u8g2_GetStrWidth(&canvas->fb, str); + break; + case AlignCenter: + x -= (u8g2_GetStrWidth(&canvas->fb, str) / 2); + break; + default: + furi_crash(); + break; + } + + switch(vertical) { + case AlignTop: + y += u8g2_GetAscent(&canvas->fb); + break; + case AlignBottom: + break; + case AlignCenter: + y += (u8g2_GetAscent(&canvas->fb) / 2); + break; + default: + furi_crash(); + break; + } + + u8g2_DrawStr(&canvas->fb, x, y, str); +} + +uint16_t canvas_string_width(Canvas* canvas, const char* str) { + furi_assert(canvas); + if(!str) return 0; + return u8g2_GetStrWidth(&canvas->fb, str); +} + +uint8_t canvas_glyph_width(Canvas* canvas, uint16_t symbol) { + furi_assert(canvas); + return u8g2_GetGlyphWidth(&canvas->fb, symbol); +} + +void canvas_draw_bitmap( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height, + const uint8_t* compressed_bitmap_data) { + furi_assert(canvas); + + x += canvas->offset_x; + y += canvas->offset_y; + uint8_t* bitmap_data = NULL; + compress_icon_decode(canvas->compress_icon, compressed_bitmap_data, &bitmap_data); + canvas_draw_u8g2_bitmap(&canvas->fb, x, y, width, height, bitmap_data, IconRotation0); +} + +void canvas_draw_icon_animation( + Canvas* canvas, + uint8_t x, + uint8_t y, + IconAnimation* icon_animation) { + furi_assert(canvas); + furi_assert(icon_animation); + + x += canvas->offset_x; + y += canvas->offset_y; + uint8_t* icon_data = NULL; + compress_icon_decode( + canvas->compress_icon, icon_animation_get_data(icon_animation), &icon_data); + canvas_draw_u8g2_bitmap( + &canvas->fb, + x, + y, + icon_animation_get_width(icon_animation), + icon_animation_get_height(icon_animation), + icon_data, + IconRotation0); +} + +static void canvas_draw_u8g2_bitmap_int( + u8g2_t* u8g2, + u8g2_uint_t x, + u8g2_uint_t y, + u8g2_uint_t w, + u8g2_uint_t h, + bool mirror, + bool rotation, + const uint8_t* bitmap) { + u8g2_uint_t blen; + blen = w; + blen += 7; + blen >>= 3; + + if(rotation && !mirror) { + x += w + 1; + } else if(mirror && !rotation) { + y += h - 1; + } + + while(h > 0) { + const uint8_t* b = bitmap; + uint16_t len = w; + uint16_t x0 = x; + uint16_t y0 = y; + uint8_t mask; + uint8_t color = u8g2->draw_color; + uint8_t ncolor = (color == 0 ? 1 : 0); + mask = 1; + + while(len > 0) { + if(u8x8_pgm_read(b) & mask) { + u8g2->draw_color = color; + u8g2_DrawHVLine(u8g2, x0, y0, 1, 0); + } else if(u8g2->bitmap_transparency == 0) { + u8g2->draw_color = ncolor; + u8g2_DrawHVLine(u8g2, x0, y0, 1, 0); + } + + if(rotation) { + y0++; + } else { + x0++; + } + + mask <<= 1; + if(mask == 0) { + mask = 1; + b++; + } + len--; + } + + u8g2->draw_color = color; + bitmap += blen; + + if(mirror) { + if(rotation) { + x++; + } else { + y--; + } + } else { + if(rotation) { + x--; + } else { + y++; + } + } + h--; + } +} + +void canvas_draw_u8g2_bitmap( + u8g2_t* u8g2, + u8g2_uint_t x, + u8g2_uint_t y, + u8g2_uint_t w, + u8g2_uint_t h, + const uint8_t* bitmap, + IconRotation rotation) { + u8g2_uint_t blen; + blen = w; + blen += 7; + blen >>= 3; +#ifdef U8G2_WITH_INTERSECTION + if(u8g2_IsIntersection(u8g2, x, y, x + w, y + h) == 0) return; +#endif /* U8G2_WITH_INTERSECTION */ + + switch(rotation) { + case IconRotation0: + canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 0, 0, bitmap); + break; + case IconRotation90: + canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 0, 1, bitmap); + break; + case IconRotation180: + canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 1, 0, bitmap); + break; + case IconRotation270: + canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 1, 1, bitmap); + break; + default: + break; + } +} + +void canvas_draw_icon_ex( + Canvas* canvas, + uint8_t x, + uint8_t y, + const Icon* icon, + IconRotation rotation) { + furi_assert(canvas); + furi_assert(icon); + + x += canvas->offset_x; + y += canvas->offset_y; + uint8_t* icon_data = NULL; + compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data); + canvas_draw_u8g2_bitmap( + &canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data, rotation); +} + +void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) { + furi_assert(canvas); + furi_assert(icon); + + x += canvas->offset_x; + y += canvas->offset_y; + uint8_t* icon_data = NULL; + compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data); + canvas_draw_u8g2_bitmap( + &canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data, IconRotation0); +} + +void canvas_draw_dot(Canvas* canvas, uint8_t x, uint8_t y) { + furi_assert(canvas); + x += canvas->offset_x; + y += canvas->offset_y; + u8g2_DrawPixel(&canvas->fb, x, y); +} + +void canvas_draw_box(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height) { + furi_assert(canvas); + x += canvas->offset_x; + y += canvas->offset_y; + u8g2_DrawBox(&canvas->fb, x, y, width, height); +} + +void canvas_draw_rbox( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height, + uint8_t radius) { + furi_assert(canvas); + x += canvas->offset_x; + y += canvas->offset_y; + u8g2_DrawRBox(&canvas->fb, x, y, width, height, radius); +} + +void canvas_draw_frame(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, uint8_t height) { + furi_assert(canvas); + x += canvas->offset_x; + y += canvas->offset_y; + u8g2_DrawFrame(&canvas->fb, x, y, width, height); +} + +void canvas_draw_rframe( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height, + uint8_t radius) { + furi_assert(canvas); + x += canvas->offset_x; + y += canvas->offset_y; + u8g2_DrawRFrame(&canvas->fb, x, y, width, height, radius); +} + +void canvas_draw_line(Canvas* canvas, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) { + furi_assert(canvas); + x1 += canvas->offset_x; + y1 += canvas->offset_y; + x2 += canvas->offset_x; + y2 += canvas->offset_y; + u8g2_DrawLine(&canvas->fb, x1, y1, x2, y2); +} + +void canvas_draw_circle(Canvas* canvas, uint8_t x, uint8_t y, uint8_t radius) { + furi_assert(canvas); + x += canvas->offset_x; + y += canvas->offset_y; + u8g2_DrawCircle(&canvas->fb, x, y, radius, U8G2_DRAW_ALL); +} + +void canvas_draw_disc(Canvas* canvas, uint8_t x, uint8_t y, uint8_t radius) { + furi_assert(canvas); + x += canvas->offset_x; + y += canvas->offset_y; + u8g2_DrawDisc(&canvas->fb, x, y, radius, U8G2_DRAW_ALL); +} + +void canvas_draw_triangle( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t base, + uint8_t height, + CanvasDirection dir) { + furi_assert(canvas); + if(dir == CanvasDirectionBottomToTop) { + canvas_draw_line(canvas, x - base / 2, y, x + base / 2, y); + canvas_draw_line(canvas, x - base / 2, y, x, y - height + 1); + canvas_draw_line(canvas, x, y - height + 1, x + base / 2, y); + } else if(dir == CanvasDirectionTopToBottom) { + canvas_draw_line(canvas, x - base / 2, y, x + base / 2, y); + canvas_draw_line(canvas, x - base / 2, y, x, y + height - 1); + canvas_draw_line(canvas, x, y + height - 1, x + base / 2, y); + } else if(dir == CanvasDirectionRightToLeft) { + canvas_draw_line(canvas, x, y - base / 2, x, y + base / 2); + canvas_draw_line(canvas, x, y - base / 2, x - height + 1, y); + canvas_draw_line(canvas, x - height + 1, y, x, y + base / 2); + } else if(dir == CanvasDirectionLeftToRight) { + canvas_draw_line(canvas, x, y - base / 2, x, y + base / 2); + canvas_draw_line(canvas, x, y - base / 2, x + height - 1, y); + canvas_draw_line(canvas, x + height - 1, y, x, y + base / 2); + } +} + +void canvas_draw_xbm( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t w, + uint8_t h, + const uint8_t* bitmap) { + furi_assert(canvas); + x += canvas->offset_x; + y += canvas->offset_y; + canvas_draw_u8g2_bitmap(&canvas->fb, x, y, w, h, bitmap, IconRotation0); +} + +void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch) { + furi_assert(canvas); + x += canvas->offset_x; + y += canvas->offset_y; + u8g2_DrawGlyph(&canvas->fb, x, y, ch); +} + +void canvas_set_bitmap_mode(Canvas* canvas, bool alpha) { + u8g2_SetBitmapMode(&canvas->fb, alpha ? 1 : 0); +} + +void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation) { + furi_assert(canvas); + const u8g2_cb_t* rotate_cb = NULL; + bool need_swap = false; + if(canvas->orientation != orientation) { + switch(orientation) { + case CanvasOrientationHorizontal: + need_swap = canvas->orientation == CanvasOrientationVertical || + canvas->orientation == CanvasOrientationVerticalFlip; + rotate_cb = U8G2_R0; + break; + case CanvasOrientationHorizontalFlip: + need_swap = canvas->orientation == CanvasOrientationVertical || + canvas->orientation == CanvasOrientationVerticalFlip; + rotate_cb = U8G2_R2; + break; + case CanvasOrientationVertical: + need_swap = canvas->orientation == CanvasOrientationHorizontal || + canvas->orientation == CanvasOrientationHorizontalFlip; + rotate_cb = U8G2_R3; + break; + case CanvasOrientationVerticalFlip: + need_swap = canvas->orientation == CanvasOrientationHorizontal || + canvas->orientation == CanvasOrientationHorizontalFlip; + rotate_cb = U8G2_R1; + break; + default: + furi_crash(); + } + + if(need_swap) FURI_SWAP(canvas->width, canvas->height); + u8g2_SetDisplayRotation(&canvas->fb, rotate_cb); + canvas->orientation = orientation; + } +} + +CanvasOrientation canvas_get_orientation(const Canvas* canvas) { + return canvas->orientation; +} diff --git a/applications/gui/canvas.h b/applications/services/gui/canvas.h old mode 100755 new mode 100644 similarity index 83% rename from applications/gui/canvas.h rename to applications/services/gui/canvas.h index 4923e2e6618..a369e213bd9 --- a/applications/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -7,7 +7,7 @@ #include #include -#include +#include #ifdef __cplusplus extern "C" { @@ -17,6 +17,7 @@ extern "C" { typedef enum { ColorWhite = 0x00, ColorBlack = 0x01, + ColorXOR = 0x02, } Color; /** Fonts enumeration */ @@ -42,7 +43,9 @@ typedef enum { /** Canvas Orientation */ typedef enum { CanvasOrientationHorizontal, + CanvasOrientationHorizontalFlip, CanvasOrientationVertical, + CanvasOrientationVerticalFlip, } CanvasOrientation; /** Font Direction */ @@ -61,16 +64,44 @@ typedef struct { uint8_t descender; } CanvasFontParameters; -/** Canvas anonymouse structure */ +/** Icon flip */ +typedef enum { + IconFlipNone, + IconFlipHorizontal, + IconFlipVertical, + IconFlipBoth, +} IconFlip; + +/** Icon rotation */ +typedef enum { + IconRotation0, + IconRotation90, + IconRotation180, + IconRotation270, +} IconRotation; + +/** Canvas anonymous structure */ typedef struct Canvas Canvas; +/** Reset canvas drawing tools configuration + * + * @param canvas Canvas instance + */ +void canvas_reset(Canvas* canvas); + +/** Commit canvas. Send buffer to display + * + * @param canvas Canvas instance + */ +void canvas_commit(Canvas* canvas); + /** Get Canvas width * * @param canvas Canvas instance * * @return width in pixels. */ -uint8_t canvas_width(Canvas* canvas); +uint8_t canvas_width(const Canvas* canvas); /** Get Canvas height * @@ -78,7 +109,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 * @@ -86,7 +117,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 * @@ -95,7 +126,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 * @@ -131,6 +162,13 @@ void canvas_invert_color(Canvas* canvas); */ void canvas_set_font(Canvas* canvas, Font font); +/** Set custom drawing font + * + * @param canvas Canvas instance + * @param font Pointer to u8g2 const uint8_t* font array + */ +void canvas_set_custom_u8g2_font(Canvas* canvas, const uint8_t* font); + /** Draw string at position of baseline defined by x, y. * * @param canvas Canvas instance @@ -176,7 +214,7 @@ uint16_t canvas_string_width(Canvas* canvas, const char* str); * * @return width in pixels */ -uint8_t canvas_glyph_width(Canvas* canvas, char symbol); +uint8_t canvas_glyph_width(Canvas* canvas, uint16_t symbol); /** Draw bitmap picture at position defined by x,y. * @@ -195,6 +233,22 @@ void canvas_draw_bitmap( uint8_t height, const uint8_t* compressed_bitmap_data); +/** Draw icon at position defined by x,y with rotation and flip. + * + * @param canvas Canvas instance + * @param x x coordinate + * @param y y coordinate + * @param icon Icon instance + * @param flip IconFlip + * @param rotation IconRotation + */ +void canvas_draw_icon_ex( + Canvas* canvas, + uint8_t x, + uint8_t y, + const Icon* icon, + IconRotation rotation); + /** Draw animation at position defined by x,y. * * @param canvas Canvas instance @@ -297,7 +351,7 @@ void canvas_draw_disc(Canvas* canvas, uint8_t x, uint8_t y, uint8_t r); * @param y y coordinate of base and height intersection * @param base length of triangle side * @param height length of triangle height - * @param dir CanvasDirection triangle orientaion + * @param dir CanvasDirection triangle orientation */ void canvas_draw_triangle( Canvas* canvas, @@ -323,7 +377,7 @@ void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch); */ void canvas_set_bitmap_mode(Canvas* canvas, bool alpha); -/** Draw rounded-corner frame of width, height at x,y, with round value raduis +/** Draw rounded-corner frame 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 new file mode 100644 index 00000000000..f3b8f17ad79 --- /dev/null +++ b/applications/services/gui/canvas_i.h @@ -0,0 +1,99 @@ +/** + * @file canvas_i.h + * GUI: internal Canvas API + */ + +#pragma once + +#include "canvas.h" +#include +#include + +/** Canvas structure + */ +struct Canvas { + u8g2_t fb; + CanvasOrientation orientation; + uint8_t offset_x; + uint8_t offset_y; + uint8_t width; + uint8_t height; + CompressIcon* compress_icon; +}; + +/** Allocate memory and initialize canvas + * + * @return Canvas instance + */ +Canvas* canvas_init(); + +/** Free canvas memory + * + * @param canvas Canvas instance + */ +void canvas_free(Canvas* canvas); + +/** Get canvas buffer. + * + * @param canvas Canvas instance + * + * @return pointer to buffer + */ +uint8_t* canvas_get_buffer(Canvas* canvas); + +/** Get canvas buffer size. + * + * @param canvas Canvas instance + * + * @return size of canvas in bytes + */ +size_t canvas_get_buffer_size(const Canvas* canvas); + +/** Set drawing region relative to real screen buffer + * + * @param canvas Canvas instance + * @param offset_x x coordinate offset + * @param offset_y y coordinate offset + * @param width width + * @param height height + */ +void canvas_frame_set( + Canvas* canvas, + uint8_t offset_x, + uint8_t offset_y, + uint8_t width, + uint8_t height); + +/** Set canvas orientation + * + * @param canvas Canvas instance + * @param orientation CanvasOrientation + */ +void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation); + +/** Get canvas orientation + * + * @param canvas Canvas instance + * + * @return CanvasOrientation + */ +CanvasOrientation canvas_get_orientation(const Canvas* canvas); + +/** Draw a u8g2 bitmap + * + * @param canvas Canvas instance + * @param x x coordinate + * @param y y coordinate + * @param width width + * @param height height + * @param bitmap bitmap + * @param rotation rotation + */ +void canvas_draw_u8g2_bitmap( + u8g2_t* u8g2, + uint8_t x, + uint8_t y, + uint8_t width, + uint8_t height, + const uint8_t* bitmap, + uint8_t rotation); diff --git a/applications/gui/elements.c b/applications/services/gui/elements.c similarity index 87% rename from applications/gui/elements.c rename to applications/services/gui/elements.c index 58b446038d2..e92c2433cda 100644 --- a/applications/gui/elements.c +++ b/applications/services/gui/elements.c @@ -1,17 +1,17 @@ #include "elements.h" -#include "m-core.h" +#include #include -#include "furi_hal_resources.h" +#include #include -#include "gui/canvas.h" +#include #include #include -#include #include #include "canvas_i.h" +#include #include #include #include @@ -42,6 +42,31 @@ void elements_progress_bar(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, canvas_draw_box(canvas, x + 1, y + 1, progress_length, height - 2); } +void elements_progress_bar_with_text( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + float progress, + const char* text) { + furi_assert(canvas); + furi_assert((progress >= 0.0f) && (progress <= 1.0f)); + uint8_t height = 11; + + uint8_t progress_length = roundf(progress * (width - 2)); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, x + 1, y + 1, width - 2, height - 2); + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe(canvas, x, y, width, height, 3); + + canvas_draw_box(canvas, x + 1, y + 1, progress_length, height - 2); + + canvas_set_color(canvas, ColorXOR); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, x + width / 2, y + 2, AlignCenter, AlignTop, text); +} + void elements_scrollbar_pos( Canvas* canvas, uint8_t x, @@ -189,12 +214,12 @@ static size_t end = text + strlen(text); } size_t text_size = end - text; - string_t str; - string_init_set_str(str, text); - string_left(str, text_size); + FuriString* str; + str = furi_string_alloc_set(text); + furi_string_left(str, text_size); size_t result = 0; - uint16_t len_px = canvas_string_width(canvas, string_get_cstr(str)); + uint16_t len_px = canvas_string_width(canvas, furi_string_get_cstr(str)); uint8_t px_left = 0; if(horizontal == AlignCenter) { if(x > (canvas_width(canvas) / 2)) { @@ -207,15 +232,15 @@ static size_t } else if(horizontal == AlignRight) { px_left = x; } else { - furi_assert(0); + furi_crash(); } if(len_px > px_left) { - uint8_t excess_symbols_approximately = - roundf((float)(len_px - px_left) / ((float)len_px / (float)text_size)); + size_t excess_symbols_approximately = + ceilf((float)(len_px - px_left) / ((float)len_px / (float)text_size)); // reduce to 5 to be sure dash fit, and next line will be at least 5 symbols long if(excess_symbols_approximately > 0) { - excess_symbols_approximately = MAX(excess_symbols_approximately, 5); + excess_symbols_approximately = MAX(excess_symbols_approximately, 5u); result = text_size - excess_symbols_approximately - 1; } else { result = text_size; @@ -224,7 +249,7 @@ static size_t result = text_size; } - string_clear(str); + furi_string_free(str); return result; } @@ -240,7 +265,7 @@ void elements_multiline_text_aligned( uint8_t lines_count = 0; uint8_t font_height = canvas_current_font_height(canvas); - string_t line; + FuriString* line; /* go through text line by line and count lines */ for(const char* start = text; start[0];) { @@ -261,14 +286,15 @@ void elements_multiline_text_aligned( size_t chars_fit = elements_get_max_chars_to_fit(canvas, horizontal, start, x); if((start[chars_fit] == '\n') || (start[chars_fit] == 0)) { - string_init_printf(line, "%.*s", chars_fit, start); + line = furi_string_alloc_printf("%.*s", chars_fit, start); } else if((y + font_height) > canvas_height(canvas)) { - string_init_printf(line, "%.*s...\n", chars_fit, start); + line = furi_string_alloc_printf("%.*s...\n", chars_fit, start); } else { - string_init_printf(line, "%.*s-\n", chars_fit, start); + chars_fit -= 1; // account for the dash + line = furi_string_alloc_printf("%.*s-\n", chars_fit, start); } - canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, string_get_cstr(line)); - string_clear(line); + canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, furi_string_get_cstr(line)); + furi_string_free(line); y += font_height; if(y > canvas_height(canvas)) { break; @@ -284,22 +310,22 @@ void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, const char* t furi_assert(text); uint8_t font_height = canvas_current_font_height(canvas); - string_t str; - string_init(str); + FuriString* str; + str = furi_string_alloc(); const char* start = text; char* end; do { end = strchr(start, '\n'); if(end) { - string_set_strn(str, start, end - start); + furi_string_set_strn(str, start, end - start); + start = end + 1; } else { - string_set_str(str, start); + furi_string_set(str, start); } - canvas_draw_str(canvas, x, y, string_get_cstr(str)); - start = end + 1; + canvas_draw_str(canvas, x, y, furi_string_get_cstr(str)); y += font_height; } while(end && y < 64); - string_clear(str); + furi_string_free(str); } void elements_multiline_text_framed(Canvas* canvas, uint8_t x, uint8_t y, const char* text) { @@ -533,21 +559,68 @@ void elements_bubble_str( canvas_draw_line(canvas, x2, y2, x3, y3); } -void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width) { +void elements_string_fit_width(Canvas* canvas, FuriString* string, uint8_t width) { furi_assert(canvas); furi_assert(string); - uint16_t len_px = canvas_string_width(canvas, string_get_cstr(string)); + uint16_t len_px = canvas_string_width(canvas, furi_string_get_cstr(string)); if(len_px > width) { width -= canvas_string_width(canvas, "..."); do { - string_left(string, string_size(string) - 1); - len_px = canvas_string_width(canvas, string_get_cstr(string)); + furi_string_left(string, furi_string_size(string) - 1); + len_px = canvas_string_width(canvas, furi_string_get_cstr(string)); } while(len_px > width); - string_cat(string, "..."); + furi_string_cat(string, "..."); } } +void elements_scrollable_text_line( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + FuriString* string, + size_t scroll, + bool ellipsis) { + FuriString* line = furi_string_alloc_set(string); + + size_t len_px = canvas_string_width(canvas, furi_string_get_cstr(line)); + if(len_px > width) { + if(ellipsis) { + width -= canvas_string_width(canvas, "..."); + } + + // Calculate scroll size + size_t scroll_size = furi_string_size(line); + size_t right_width = 0; + for(size_t i = scroll_size; i > 0; i--) { + right_width += canvas_glyph_width(canvas, furi_string_get_char(line, i)); + if(right_width > width) break; + scroll_size--; + if(!scroll_size) break; + } + // Ensure that we have something to scroll + if(scroll_size) { + scroll_size += 3; + scroll = scroll % scroll_size; + furi_string_right(line, scroll); + } + + len_px = canvas_string_width(canvas, furi_string_get_cstr(line)); + while(len_px > width) { + furi_string_left(line, furi_string_size(line) - 1); + len_px = canvas_string_width(canvas, furi_string_get_cstr(line)); + } + + if(ellipsis) { + furi_string_cat(line, "..."); + } + } + + canvas_draw_str(canvas, x, y, furi_string_get_cstr(line)); + furi_string_free(line); +} + void elements_text_box( Canvas* canvas, uint8_t x, @@ -567,7 +640,7 @@ void elements_text_box( bool inversed_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; diff --git a/applications/gui/elements.h b/applications/services/gui/elements.h similarity index 82% rename from applications/gui/elements.h rename to applications/services/gui/elements.h index 2329ca27b32..48533513128 100644 --- a/applications/gui/elements.h +++ b/applications/services/gui/elements.h @@ -9,7 +9,7 @@ #pragma once #include -#include +#include #include "canvas.h" #ifdef __cplusplus @@ -31,6 +31,23 @@ extern "C" { */ void elements_progress_bar(Canvas* canvas, uint8_t x, uint8_t y, uint8_t width, float progress); +/** Draw progress bar with text. + * + * @param canvas Canvas instance + * @param x progress bar position on X axis + * @param y progress bar position on Y axis + * @param width progress bar width + * @param progress progress (0.0 - 1.0) + * @param text text to draw + */ +void elements_progress_bar_with_text( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + float progress, + const char* text); + /** Draw scrollbar on canvas at specific position. * * @param canvas Canvas instance @@ -90,7 +107,7 @@ void elements_button_center(Canvas* canvas, const char* str); * * @param canvas Canvas instance * @param x, y coordinates based on align param - * @param horizontal, vertical aligment of multiline text + * @param horizontal, vertical alignment of multiline text * @param text string (possible multiline) */ void elements_multiline_text_aligned( @@ -190,7 +207,26 @@ void elements_bubble_str( * @param string string to trim * @param width max width */ -void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width); +void elements_string_fit_width(Canvas* canvas, FuriString* string, uint8_t width); + +/** Draw scrollable text line + * + * @param canvas The canvas + * @param[in] x X coordinate + * @param[in] y Y coordinate + * @param[in] width The width + * @param string The string + * @param[in] scroll The scroll counter: 0 - no scroll, any other number - scroll. Just count up, everything else will be calculated on the inside. + * @param[in] ellipsis The ellipsis flag: true to add ellipse + */ +void elements_scrollable_text_line( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + FuriString* string, + size_t scroll, + bool ellipsis); /** Draw text box element * diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c new file mode 100644 index 00000000000..0bdc999b774 --- /dev/null +++ b/applications/services/gui/gui.c @@ -0,0 +1,590 @@ +#include "gui_i.h" +#include + +#define TAG "GuiSrv" + +ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) { + // Iterating backward + ViewPortArray_it_t it; + ViewPortArray_it_last(it, array); + while(!ViewPortArray_end_p(it)) { + ViewPort* view_port = *ViewPortArray_ref(it); + if(view_port_is_enabled(view_port)) { + return view_port; + } + ViewPortArray_previous(it); + } + return NULL; +} + +size_t gui_active_view_port_count(Gui* gui, GuiLayer layer) { + furi_assert(gui); + furi_check(layer < GuiLayerMAX); + size_t ret = 0; + + gui_lock(gui); + ViewPortArray_it_t it; + ViewPortArray_it_last(it, gui->layers[layer]); + while(!ViewPortArray_end_p(it)) { + ViewPort* view_port = *ViewPortArray_ref(it); + if(view_port_is_enabled(view_port)) { + ret++; + } + ViewPortArray_previous(it); + } + gui_unlock(gui); + + return ret; +} + +void gui_update(Gui* gui) { + furi_assert(gui); + if(!gui->direct_draw) furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_DRAW); +} + +void gui_input_events_callback(const void* value, void* ctx) { + furi_assert(value); + furi_assert(ctx); + + Gui* gui = ctx; + + furi_message_queue_put(gui->input_queue, value, FuriWaitForever); + furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_INPUT); +} + +// Only Fullscreen supports vertical display for now +static bool gui_redraw_fs(Gui* gui) { + canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal); + canvas_frame_set(gui->canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT); + ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]); + if(view_port) { + view_port_draw(view_port, gui->canvas); + return true; + } else { + return false; + } +} + +static void gui_redraw_status_bar(Gui* gui, bool need_attention) { + ViewPortArray_it_t it; + uint8_t left_used = 0; + uint8_t right_used = 0; + uint8_t width; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient)) { + canvas_set_orientation(gui->canvas, CanvasOrientationHorizontalFlip); + } else { + canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal); + } + + canvas_frame_set( + gui->canvas, GUI_STATUS_BAR_X, GUI_STATUS_BAR_Y, GUI_DISPLAY_WIDTH, GUI_STATUS_BAR_HEIGHT); + + /* for support black theme - paint white area and + * draw icon with transparent white color + */ + canvas_set_color(gui->canvas, ColorWhite); + canvas_draw_box(gui->canvas, 1, 1, 9, 7); + canvas_draw_box(gui->canvas, 7, 3, 58, 6); + canvas_draw_box(gui->canvas, 61, 1, 32, 7); + canvas_draw_box(gui->canvas, 89, 3, 38, 6); + canvas_set_color(gui->canvas, ColorBlack); + canvas_set_bitmap_mode(gui->canvas, 1); + canvas_draw_icon(gui->canvas, 0, 0, &I_Background_128x11); + canvas_set_bitmap_mode(gui->canvas, 0); + + // Right side + uint8_t x = GUI_DISPLAY_WIDTH - 1; + ViewPortArray_it(it, gui->layers[GuiLayerStatusBarRight]); + while(!ViewPortArray_end_p(it) && right_used < GUI_STATUS_BAR_WIDTH) { + ViewPort* view_port = *ViewPortArray_ref(it); + if(view_port_is_enabled(view_port)) { + width = view_port_get_width(view_port); + if(!width) width = 8; + // Recalculate next position + right_used += (width + 2); + x -= (width + 2); + // Prepare work area background + canvas_frame_set( + gui->canvas, + x - 1, + GUI_STATUS_BAR_Y + 1, + width + 2, + GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); + canvas_set_color(gui->canvas, ColorWhite); + canvas_draw_box( + gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas)); + canvas_set_color(gui->canvas, ColorBlack); + // ViewPort draw + canvas_frame_set( + gui->canvas, x, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT); + view_port_draw(view_port, gui->canvas); + } + ViewPortArray_next(it); + } + // Draw frame around icons on the right + if(right_used) { + canvas_frame_set( + gui->canvas, + GUI_DISPLAY_WIDTH - 3 - right_used, + GUI_STATUS_BAR_Y, + right_used + 3, + GUI_STATUS_BAR_HEIGHT); + canvas_set_color(gui->canvas, ColorBlack); + canvas_draw_rframe( + gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1); + canvas_draw_line( + gui->canvas, + canvas_width(gui->canvas) - 2, + 1, + canvas_width(gui->canvas) - 2, + canvas_height(gui->canvas) - 2); + canvas_draw_line( + gui->canvas, + 1, + canvas_height(gui->canvas) - 2, + canvas_width(gui->canvas) - 2, + canvas_height(gui->canvas) - 2); + } + + // Left side + x = 2; + ViewPortArray_it(it, gui->layers[GuiLayerStatusBarLeft]); + while(!ViewPortArray_end_p(it) && (right_used + left_used) < GUI_STATUS_BAR_WIDTH) { + ViewPort* view_port = *ViewPortArray_ref(it); + if(view_port_is_enabled(view_port)) { + width = view_port_get_width(view_port); + if(!width) width = 8; + // Prepare work area background + canvas_frame_set( + gui->canvas, + x - 1, + GUI_STATUS_BAR_Y + 1, + width + 2, + GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); + canvas_set_color(gui->canvas, ColorWhite); + canvas_draw_box( + gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas)); + canvas_set_color(gui->canvas, ColorBlack); + // ViewPort draw + canvas_frame_set( + gui->canvas, x, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT); + view_port_draw(view_port, gui->canvas); + // Recalculate next position + left_used += (width + 2); + x += (width + 2); + } + ViewPortArray_next(it); + } + // Extra notification + if(need_attention) { + width = icon_get_width(&I_Hidden_window_9x8); + // Prepare work area background + canvas_frame_set( + gui->canvas, + x - 1, + GUI_STATUS_BAR_Y + 1, + width + 2, + GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); + canvas_set_color(gui->canvas, ColorWhite); + canvas_draw_box(gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas)); + canvas_set_color(gui->canvas, ColorBlack); + // Draw Icon + canvas_frame_set( + gui->canvas, x, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT); + canvas_draw_icon(gui->canvas, 0, 0, &I_Hidden_window_9x8); + // Recalculate next position + left_used += (width + 2); + x += (width + 2); + } + // Draw frame around icons on the left + if(left_used) { + canvas_frame_set(gui->canvas, 0, 0, left_used + 3, GUI_STATUS_BAR_HEIGHT); + canvas_draw_rframe( + gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1); + canvas_draw_line( + gui->canvas, + canvas_width(gui->canvas) - 2, + 1, + canvas_width(gui->canvas) - 2, + canvas_height(gui->canvas) - 2); + canvas_draw_line( + gui->canvas, + 1, + canvas_height(gui->canvas) - 2, + canvas_width(gui->canvas) - 2, + canvas_height(gui->canvas) - 2); + } +} + +static bool gui_redraw_window(Gui* gui) { + canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal); + canvas_frame_set(gui->canvas, GUI_WINDOW_X, GUI_WINDOW_Y, GUI_WINDOW_WIDTH, GUI_WINDOW_HEIGHT); + ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]); + if(view_port) { + view_port_draw(view_port, gui->canvas); + return true; + } + return false; +} + +static bool gui_redraw_desktop(Gui* gui) { + canvas_set_orientation(gui->canvas, CanvasOrientationHorizontal); + canvas_frame_set(gui->canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT); + ViewPort* view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]); + if(view_port) { + view_port_draw(view_port, gui->canvas); + return true; + } + + return false; +} + +static void gui_redraw(Gui* gui) { + furi_assert(gui); + gui_lock(gui); + + do { + if(gui->direct_draw) break; + + canvas_reset(gui->canvas); + + if(gui->lockdown) { + gui_redraw_desktop(gui); + bool need_attention = + (gui_view_port_find_enabled(gui->layers[GuiLayerWindow]) != 0 || + gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]) != 0); + gui_redraw_status_bar(gui, need_attention); + } else { + if(!gui_redraw_fs(gui)) { + if(!gui_redraw_window(gui)) { + gui_redraw_desktop(gui); + } + gui_redraw_status_bar(gui, false); + } + } + + canvas_commit(gui->canvas); + for + M_EACH(p, gui->canvas_callback_pair, CanvasCallbackPairArray_t) { + p->callback( + canvas_get_buffer(gui->canvas), + canvas_get_buffer_size(gui->canvas), + canvas_get_orientation(gui->canvas), + p->context); + } + } while(false); + + gui_unlock(gui); +} + +static void gui_input(Gui* gui, InputEvent* input_event) { + furi_assert(gui); + furi_assert(input_event); + + // Check input complementarity + uint8_t key_bit = (1 << input_event->key); + if(input_event->type == InputTypeRelease) { + gui->ongoing_input &= ~key_bit; + } else if(input_event->type == InputTypePress) { + gui->ongoing_input |= key_bit; + } else if(!(gui->ongoing_input & key_bit)) { + FURI_LOG_D( + TAG, + "non-complementary input, discarding key: %s type: %s, sequence: %p", + input_get_key_name(input_event->key), + input_get_type_name(input_event->type), + (void*)input_event->sequence); + return; + } + + gui_lock(gui); + + do { + if(gui->direct_draw && !gui->ongoing_input_view_port) { + break; + } + + ViewPort* view_port = NULL; + + if(gui->lockdown) { + view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]); + } else { + view_port = gui_view_port_find_enabled(gui->layers[GuiLayerFullscreen]); + if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerWindow]); + if(!view_port) view_port = gui_view_port_find_enabled(gui->layers[GuiLayerDesktop]); + } + + if(!(gui->ongoing_input & ~key_bit) && input_event->type == InputTypePress) { + gui->ongoing_input_view_port = view_port; + } + + if(view_port && view_port == gui->ongoing_input_view_port) { + view_port_input(view_port, input_event); + } else if(gui->ongoing_input_view_port && input_event->type == InputTypeRelease) { + FURI_LOG_D( + TAG, + "ViewPort changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port", + gui->ongoing_input_view_port, + view_port, + input_get_key_name(input_event->key), + input_get_type_name(input_event->type), + (void*)input_event->sequence); + view_port_input(gui->ongoing_input_view_port, input_event); + } else { + FURI_LOG_D( + TAG, + "ViewPort changed while key press %p -> %p. Discarding key: %s, type: %s, sequence: %p", + gui->ongoing_input_view_port, + view_port, + input_get_key_name(input_event->key), + input_get_type_name(input_event->type), + (void*)input_event->sequence); + } + } while(false); + + gui_unlock(gui); +} + +void gui_lock(Gui* gui) { + furi_assert(gui); + furi_check(furi_mutex_acquire(gui->mutex, FuriWaitForever) == FuriStatusOk); +} + +void gui_unlock(Gui* gui) { + furi_assert(gui); + furi_check(furi_mutex_release(gui->mutex) == FuriStatusOk); +} + +void gui_add_view_port(Gui* gui, ViewPort* view_port, GuiLayer layer) { + furi_assert(gui); + furi_assert(view_port); + furi_check(layer < GuiLayerMAX); + // Only fullscreen supports Vertical orientation for now + ViewPortOrientation view_port_orientation = view_port_get_orientation(view_port); + furi_check( + (layer == GuiLayerFullscreen) || + ((view_port_orientation != ViewPortOrientationVertical) && + (view_port_orientation != ViewPortOrientationVerticalFlip))); + + gui_lock(gui); + // Verify that view port is not yet added + ViewPortArray_it_t it; + for(size_t i = 0; i < GuiLayerMAX; i++) { + ViewPortArray_it(it, gui->layers[i]); + while(!ViewPortArray_end_p(it)) { + furi_assert(*ViewPortArray_ref(it) != view_port); + ViewPortArray_next(it); + } + } + // Add view port and link with gui + ViewPortArray_push_back(gui->layers[layer], view_port); + view_port_gui_set(view_port, gui); + gui_unlock(gui); + + // Request redraw + gui_update(gui); +} + +void gui_remove_view_port(Gui* gui, ViewPort* view_port) { + furi_assert(gui); + furi_assert(view_port); + + gui_lock(gui); + view_port_gui_set(view_port, NULL); + ViewPortArray_it_t it; + for(size_t i = 0; i < GuiLayerMAX; i++) { + ViewPortArray_it(it, gui->layers[i]); + while(!ViewPortArray_end_p(it)) { + if(*ViewPortArray_ref(it) == view_port) { + ViewPortArray_remove(gui->layers[i], it); + } else { + ViewPortArray_next(it); + } + } + } + if(gui->ongoing_input_view_port == view_port) { + gui->ongoing_input_view_port = NULL; + } + gui_unlock(gui); + + // Request redraw + gui_update(gui); +} + +void gui_view_port_send_to_front(Gui* gui, ViewPort* view_port) { + furi_assert(gui); + furi_assert(view_port); + + gui_lock(gui); + // Remove + GuiLayer layer = GuiLayerMAX; + ViewPortArray_it_t it; + for(size_t i = 0; i < GuiLayerMAX; i++) { + ViewPortArray_it(it, gui->layers[i]); + while(!ViewPortArray_end_p(it)) { + if(*ViewPortArray_ref(it) == view_port) { + ViewPortArray_remove(gui->layers[i], it); + furi_assert(layer == GuiLayerMAX); + layer = i; + } else { + ViewPortArray_next(it); + } + } + } + furi_assert(layer != GuiLayerMAX); + // Return to the top + ViewPortArray_push_back(gui->layers[layer], view_port); + gui_unlock(gui); + + // Request redraw + gui_update(gui); +} + +void gui_view_port_send_to_back(Gui* gui, ViewPort* view_port) { + furi_assert(gui); + furi_assert(view_port); + + gui_lock(gui); + // Remove + GuiLayer layer = GuiLayerMAX; + ViewPortArray_it_t it; + for(size_t i = 0; i < GuiLayerMAX; i++) { + ViewPortArray_it(it, gui->layers[i]); + while(!ViewPortArray_end_p(it)) { + if(*ViewPortArray_ref(it) == view_port) { + ViewPortArray_remove(gui->layers[i], it); + furi_assert(layer == GuiLayerMAX); + layer = i; + } else { + ViewPortArray_next(it); + } + } + } + furi_assert(layer != GuiLayerMAX); + // Return to the top + ViewPortArray_push_at(gui->layers[layer], 0, view_port); + gui_unlock(gui); + + // Request redraw + gui_update(gui); +} + +void gui_add_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, void* context) { + furi_assert(gui); + + const CanvasCallbackPair p = {callback, context}; + + gui_lock(gui); + furi_assert(!CanvasCallbackPairArray_count(gui->canvas_callback_pair, p)); + CanvasCallbackPairArray_push_back(gui->canvas_callback_pair, p); + gui_unlock(gui); + + // Request redraw + gui_update(gui); +} + +void gui_remove_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, void* context) { + furi_assert(gui); + + const CanvasCallbackPair p = {callback, context}; + + gui_lock(gui); + furi_assert(CanvasCallbackPairArray_count(gui->canvas_callback_pair, p) == 1); + CanvasCallbackPairArray_remove_val(gui->canvas_callback_pair, p); + gui_unlock(gui); +} + +size_t gui_get_framebuffer_size(const Gui* gui) { + furi_assert(gui); + return canvas_get_buffer_size(gui->canvas); +} + +void gui_set_lockdown(Gui* gui, bool lockdown) { + furi_assert(gui); + + gui_lock(gui); + gui->lockdown = lockdown; + gui_unlock(gui); + + // Request redraw + gui_update(gui); +} + +Canvas* gui_direct_draw_acquire(Gui* gui) { + furi_assert(gui); + gui_lock(gui); + gui->direct_draw = true; + gui_unlock(gui); + + canvas_reset(gui->canvas); + canvas_commit(gui->canvas); + + return gui->canvas; +} + +void gui_direct_draw_release(Gui* gui) { + furi_assert(gui); + + canvas_reset(gui->canvas); + canvas_commit(gui->canvas); + + gui_lock(gui); + gui->direct_draw = false; + gui_unlock(gui); + + gui_update(gui); +} + +Gui* gui_alloc() { + Gui* gui = malloc(sizeof(Gui)); + // Thread ID + gui->thread_id = furi_thread_get_current_id(); + // Allocate mutex + gui->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + furi_check(gui->mutex); + // Layers + for(size_t i = 0; i < GuiLayerMAX; i++) { + ViewPortArray_init(gui->layers[i]); + } + // Drawing canvas + gui->canvas = canvas_init(); + CanvasCallbackPairArray_init(gui->canvas_callback_pair); + + // Input + gui->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + gui->input_events = furi_record_open(RECORD_INPUT_EVENTS); + + furi_check(gui->input_events); + furi_pubsub_subscribe(gui->input_events, gui_input_events_callback, gui); + + return gui; +} + +int32_t gui_srv(void* p) { + UNUSED(p); + Gui* gui = gui_alloc(); + + furi_record_create(RECORD_GUI, gui); + + while(1) { + uint32_t flags = + furi_thread_flags_wait(GUI_THREAD_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever); + // Process and dispatch input + if(flags & GUI_THREAD_FLAG_INPUT) { + // Process till queue become empty + InputEvent input_event; + while(furi_message_queue_get(gui->input_queue, &input_event, 0) == FuriStatusOk) { + gui_input(gui, &input_event); + } + } + // Process and dispatch draw call + if(flags & GUI_THREAD_FLAG_DRAW) { + // Clear flags that arrived on input step + furi_thread_flags_clear(GUI_THREAD_FLAG_DRAW); + gui_redraw(gui); + } + } + + return 0; +} diff --git a/applications/gui/gui.h b/applications/services/gui/gui.h similarity index 76% rename from applications/gui/gui.h rename to applications/services/gui/gui.h index f4886758849..1b5987edace 100644 --- a/applications/gui/gui.h +++ b/applications/services/gui/gui.h @@ -27,7 +27,11 @@ typedef enum { } GuiLayer; /** Gui Canvas Commit Callback */ -typedef void (*GuiCanvasCommitCallback)(uint8_t* data, size_t size, void* context); +typedef void (*GuiCanvasCommitCallback)( + uint8_t* data, + size_t size, + CanvasOrientation orientation, + void* context); #define RECORD_GUI "gui" @@ -94,7 +98,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 * @@ -106,6 +110,28 @@ size_t gui_get_framebuffer_size(Gui* gui); */ void gui_set_lockdown(Gui* gui, bool lockdown); +/** Acquire Direct Draw lock and get Canvas instance + * + * This method return Canvas instance for use in monopoly mode. Direct draw lock + * disables input and draw call dispatch functions in GUI service. No other + * applications or services will be able to draw until gui_direct_draw_release + * call. + * + * @param gui The graphical user interface + * + * @return Canvas instance + */ +Canvas* gui_direct_draw_acquire(Gui* gui); + +/** Release Direct Draw Lock + * + * Release Direct Draw Lock, enables Input and Draw call processing. Canvas + * acquired in gui_direct_draw_acquire will become invalid after this call. + * + * @param gui Gui instance + */ +void gui_direct_draw_release(Gui* gui); + #ifdef __cplusplus } #endif diff --git a/applications/services/gui/gui_i.h b/applications/services/gui/gui_i.h new file mode 100644 index 00000000000..a5e269e034f --- /dev/null +++ b/applications/services/gui/gui_i.h @@ -0,0 +1,118 @@ +/** + * @file gui_i.h + * GUI: main API internals + */ + +#pragma once + +#include "gui.h" + +#include +#include +#include +#include +#include + +#include "canvas.h" +#include "canvas_i.h" +#include "view_port.h" +#include "view_port_i.h" + +#define GUI_DISPLAY_WIDTH 128 +#define GUI_DISPLAY_HEIGHT 64 + +#define GUI_STATUS_BAR_X 0 +#define GUI_STATUS_BAR_Y 0 +#define GUI_STATUS_BAR_WIDTH GUI_DISPLAY_WIDTH +/* 0-1 pixels for upper thin frame + * 2-9 pixels for icons (battery, sd card, etc) + * 10-12 pixels for lower bold line */ +#define GUI_STATUS_BAR_HEIGHT 13 +/* icon itself area (battery, sd card, etc) excluding frame. + * painted 2 pixels below GUI_STATUS_BAR_X. + */ +#define GUI_STATUS_BAR_WORKAREA_HEIGHT 8 + +#define GUI_WINDOW_X 0 +#define GUI_WINDOW_Y GUI_STATUS_BAR_HEIGHT +#define GUI_WINDOW_WIDTH GUI_DISPLAY_WIDTH +#define GUI_WINDOW_HEIGHT (GUI_DISPLAY_HEIGHT - GUI_WINDOW_Y) + +#define GUI_THREAD_FLAG_DRAW (1 << 0) +#define GUI_THREAD_FLAG_INPUT (1 << 1) +#define GUI_THREAD_FLAG_ALL (GUI_THREAD_FLAG_DRAW | GUI_THREAD_FLAG_INPUT) + +ARRAY_DEF(ViewPortArray, ViewPort*, M_PTR_OPLIST); + +typedef struct { + GuiCanvasCommitCallback callback; + void* context; +} CanvasCallbackPair; + +ARRAY_DEF(CanvasCallbackPairArray, CanvasCallbackPair, M_POD_OPLIST); + +#define M_OPL_CanvasCallbackPairArray_t() ARRAY_OPLIST(CanvasCallbackPairArray, M_POD_OPLIST) + +ALGO_DEF(CanvasCallbackPairArray, CanvasCallbackPairArray_t); + +/** Gui structure */ +struct Gui { + // Thread and lock + FuriThreadId thread_id; + FuriMutex* mutex; + + // Layers and Canvas + bool lockdown; + bool direct_draw; + ViewPortArray_t layers[GuiLayerMAX]; + Canvas* canvas; + CanvasCallbackPairArray_t canvas_callback_pair; + + // Input + FuriMessageQueue* input_queue; + FuriPubSub* input_events; + uint8_t ongoing_input; + ViewPort* ongoing_input_view_port; +}; + +/** Find enabled ViewPort in ViewPortArray + * + * @param[in] array The ViewPortArray instance + * + * @return ViewPort instance or NULL + */ +ViewPort* gui_view_port_find_enabled(ViewPortArray_t array); + +/** Update GUI, request redraw + * + * @param gui Gui instance + */ +void gui_update(Gui* gui); + +/** Input event callback + * + * Used to receive input from input service or to inject new input events + * + * @param[in] value The value pointer (InputEvent*) + * @param ctx The context (Gui instance) + */ +void gui_input_events_callback(const void* value, void* ctx); + +/** Get count of view ports in layer + * + * @param gui The Gui instance + * @param[in] layer GuiLayer that we want to get count of view ports + */ +size_t gui_active_view_port_count(Gui* gui, GuiLayer layer); + +/** Lock GUI + * + * @param gui The Gui instance + */ +void gui_lock(Gui* gui); + +/** Unlock GUI + * + * @param gui The Gui instance + */ +void gui_unlock(Gui* gui); diff --git a/applications/gui/icon.c b/applications/services/gui/icon.c similarity index 100% rename from applications/gui/icon.c rename to applications/services/gui/icon.c diff --git a/applications/gui/icon.h b/applications/services/gui/icon.h similarity index 100% rename from applications/gui/icon.h rename to applications/services/gui/icon.h diff --git a/applications/gui/icon_animation.c b/applications/services/gui/icon_animation.c similarity index 80% rename from applications/gui/icon_animation.c rename to applications/services/gui/icon_animation.c index 48c86220886..a39ef2e254e 100644 --- a/applications/gui/icon_animation.c +++ b/applications/services/gui/icon_animation.c @@ -15,7 +15,6 @@ IconAnimation* icon_animation_alloc(const Icon* icon) { void icon_animation_free(IconAnimation* instance) { furi_assert(instance); icon_animation_stop(instance); - while(xTimerIsTimerActive(instance->timer) == pdTRUE) furi_delay_tick(1); furi_timer_free(instance->timer); free(instance); } @@ -29,7 +28,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 +50,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; } @@ -67,10 +66,9 @@ void icon_animation_start(IconAnimation* instance) { instance->animating = true; furi_assert(instance->icon->frame_rate); furi_check( - xTimerChangePeriod( + furi_timer_start( instance->timer, - (furi_kernel_get_tick_frequency() / instance->icon->frame_rate), - portMAX_DELAY) == pdPASS); + (furi_kernel_get_tick_frequency() / instance->icon->frame_rate)) == FuriStatusOk); } } @@ -78,12 +76,12 @@ void icon_animation_stop(IconAnimation* instance) { furi_assert(instance); if(instance->animating) { instance->animating = false; - furi_check(xTimerStop(instance->timer, portMAX_DELAY) == pdPASS); + furi_timer_stop(instance->timer); instance->frame = 0; } } -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/gui/icon_animation.h b/applications/services/gui/icon_animation.h similarity index 89% rename from applications/gui/icon_animation.h rename to applications/services/gui/icon_animation.h index dab9d996dae..6e58df31dee 100644 --- a/applications/gui/icon_animation.h +++ b/applications/services/gui/icon_animation.h @@ -7,7 +7,7 @@ #include #include -#include +#include #ifdef __cplusplus extern "C" { @@ -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/gui/icon_animation_i.h b/applications/services/gui/icon_animation_i.h similarity index 91% rename from applications/gui/icon_animation_i.h rename to applications/services/gui/icon_animation_i.h index 4053a120d3b..62858d288c0 100644 --- a/applications/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/gui/icon_i.h b/applications/services/gui/icon_i.h similarity index 100% rename from applications/gui/icon_i.h rename to applications/services/gui/icon_i.h diff --git a/applications/services/gui/modules/button_menu.c b/applications/services/gui/modules/button_menu.c new file mode 100644 index 00000000000..6ac786c922e --- /dev/null +++ b/applications/services/gui/modules/button_menu.c @@ -0,0 +1,408 @@ +#include "button_menu.h" + +#include +#include +#include + +#include +#include + +#include +#include + +#define ITEM_FIRST_OFFSET 17 +#define ITEM_NEXT_OFFSET 4 +#define ITEM_HEIGHT 14 +#define ITEM_WIDTH 64 +#define BUTTONS_PER_SCREEN 6 + +struct ButtonMenuItem { + const char* label; + int32_t index; + ButtonMenuItemCallback callback; + ButtonMenuItemType type; + void* callback_context; +}; + +ARRAY_DEF(ButtonMenuItemArray, ButtonMenuItem, M_POD_OPLIST); + +struct ButtonMenu { + View* view; + bool freeze_input; +}; + +typedef struct { + ButtonMenuItemArray_t items; + size_t position; + const char* header; +} ButtonMenuModel; + +static void button_menu_draw_control_button( + Canvas* canvas, + uint8_t item_position, + const char* text, + bool selected) { + furi_assert(canvas); + furi_assert(text); + + uint8_t item_x = 0; + uint8_t item_y = ITEM_FIRST_OFFSET + (item_position * (ITEM_HEIGHT + ITEM_NEXT_OFFSET)); + + canvas_set_color(canvas, ColorBlack); + + if(selected) { + elements_slightly_rounded_box(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_str_aligned( + canvas, + item_x + (ITEM_WIDTH / 2), + item_y + (ITEM_HEIGHT / 2), + AlignCenter, + AlignCenter, + text); +} + +static void button_menu_draw_common_button( + Canvas* canvas, + uint8_t item_position, + const char* text, + bool selected) { + furi_assert(canvas); + furi_assert(text); + + uint8_t item_x = 0; + uint8_t item_y = ITEM_FIRST_OFFSET + (item_position * (ITEM_HEIGHT + ITEM_NEXT_OFFSET)); + + canvas_set_color(canvas, ColorBlack); + + if(selected) { + canvas_draw_rbox(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT, 5); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_draw_rframe(canvas, item_x, item_y, ITEM_WIDTH, ITEM_HEIGHT, 5); + } + + FuriString* disp_str; + disp_str = furi_string_alloc_set(text); + elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 6); + + canvas_draw_str_aligned( + canvas, + item_x + (ITEM_WIDTH / 2), + item_y + (ITEM_HEIGHT / 2), + AlignCenter, + AlignCenter, + furi_string_get_cstr(disp_str)); + + furi_string_free(disp_str); +} + +static void button_menu_view_draw_callback(Canvas* canvas, void* _model) { + furi_assert(canvas); + furi_assert(_model); + + ButtonMenuModel* model = (ButtonMenuModel*)_model; + canvas_set_font(canvas, FontSecondary); + + const size_t active_screen = model->position / BUTTONS_PER_SCREEN; + const size_t items_size = ButtonMenuItemArray_size(model->items); + const size_t max_screen = items_size ? (items_size - 1) / BUTTONS_PER_SCREEN : 0; + + if(active_screen > 0) { + canvas_draw_icon(canvas, 28, 1, &I_InfraredArrowUp_4x8); + } + + if(max_screen > active_screen) { + canvas_draw_icon(canvas, 28, 123, &I_InfraredArrowDown_4x8); + } + + if(model->header) { + FuriString* disp_str; + disp_str = furi_string_alloc_set(model->header); + elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 6); + canvas_draw_str_aligned( + canvas, 32, 10, AlignCenter, AlignCenter, furi_string_get_cstr(disp_str)); + furi_string_free(disp_str); + } + + size_t item_position = 0; + ButtonMenuItemArray_it_t it; + + for(ButtonMenuItemArray_it(it, model->items); !ButtonMenuItemArray_end_p(it); + ButtonMenuItemArray_next(it), ++item_position) { + if(active_screen == (item_position / BUTTONS_PER_SCREEN)) { + if(ButtonMenuItemArray_cref(it)->type == ButtonMenuItemTypeControl) { + button_menu_draw_control_button( + canvas, + item_position % BUTTONS_PER_SCREEN, + ButtonMenuItemArray_cref(it)->label, + (item_position == model->position)); + } else if(ButtonMenuItemArray_cref(it)->type == ButtonMenuItemTypeCommon) { + button_menu_draw_common_button( + canvas, + item_position % BUTTONS_PER_SCREEN, + ButtonMenuItemArray_cref(it)->label, + (item_position == model->position)); + } + } + } +} + +static void button_menu_process_up(ButtonMenu* button_menu) { + furi_assert(button_menu); + + with_view_model( + button_menu->view, + ButtonMenuModel * model, + { + if(model->position > 0) { + model->position--; + } else { + model->position = ButtonMenuItemArray_size(model->items) - 1; + } + }, + true); +} + +static void button_menu_process_down(ButtonMenu* button_menu) { + furi_assert(button_menu); + + with_view_model( + button_menu->view, + ButtonMenuModel * model, + { + if(model->position < (ButtonMenuItemArray_size(model->items) - 1)) { + model->position++; + } else { + model->position = 0; + } + }, + true); +} + +static void button_menu_process_right(ButtonMenu* button_menu) { + furi_assert(button_menu); + + with_view_model( + button_menu->view, + ButtonMenuModel * model, + { + if(ButtonMenuItemArray_size(model->items) > BUTTONS_PER_SCREEN) { + size_t position_candidate = model->position + BUTTONS_PER_SCREEN; + position_candidate -= position_candidate % BUTTONS_PER_SCREEN; + if(position_candidate < (ButtonMenuItemArray_size(model->items))) { + model->position = position_candidate; + } else { + model->position = 0; + } + } + }, + true); +} + +static void button_menu_process_left(ButtonMenu* button_menu) { + furi_assert(button_menu); + + with_view_model( + button_menu->view, + ButtonMenuModel * model, + { + if(ButtonMenuItemArray_size(model->items) > BUTTONS_PER_SCREEN) { + size_t position_candidate; + if(model->position < BUTTONS_PER_SCREEN) { + position_candidate = (ButtonMenuItemArray_size(model->items) - 1); + } else { + position_candidate = model->position - BUTTONS_PER_SCREEN; + }; + position_candidate -= position_candidate % BUTTONS_PER_SCREEN; + model->position = position_candidate; + } + }, + true); +} + +static void button_menu_process_ok(ButtonMenu* button_menu, InputType type) { + furi_assert(button_menu); + + ButtonMenuItem* item = NULL; + + with_view_model( + button_menu->view, + ButtonMenuModel * model, + { + if(model->position < (ButtonMenuItemArray_size(model->items))) { + item = ButtonMenuItemArray_get(model->items, model->position); + } + }, + false); + + if(item) { + if(item->type == ButtonMenuItemTypeControl) { + if(type == InputTypeShort) { + if(item->callback) { + item->callback(item->callback_context, item->index, type); + } + } + } + if(item->type == ButtonMenuItemTypeCommon) { + if((type == InputTypePress) || (type == InputTypeRelease)) { + if(item->callback) { + item->callback(item->callback_context, item->index, type); + } + } + } + } +} + +static bool button_menu_view_input_callback(InputEvent* event, void* context) { + furi_assert(event); + + ButtonMenu* button_menu = context; + bool consumed = false; + + if(event->key == InputKeyOk) { + if((event->type == InputTypeRelease) || (event->type == InputTypePress)) { + consumed = true; + button_menu->freeze_input = (event->type == InputTypePress); + button_menu_process_ok(button_menu, event->type); + } else if(event->type == InputTypeShort) { + consumed = true; + button_menu_process_ok(button_menu, event->type); + } + } + + if(!button_menu->freeze_input && + ((event->type == InputTypeRepeat) || (event->type == InputTypeShort))) { + switch(event->key) { + case InputKeyUp: + consumed = true; + button_menu_process_up(button_menu); + break; + case InputKeyDown: + consumed = true; + button_menu_process_down(button_menu); + break; + case InputKeyRight: + consumed = true; + button_menu_process_right(button_menu); + break; + case InputKeyLeft: + consumed = true; + button_menu_process_left(button_menu); + break; + default: + break; + } + } + + return consumed; +} + +View* button_menu_get_view(ButtonMenu* button_menu) { + furi_assert(button_menu); + return button_menu->view; +} + +void button_menu_reset(ButtonMenu* button_menu) { + furi_assert(button_menu); + + with_view_model( + button_menu->view, + ButtonMenuModel * model, + { + ButtonMenuItemArray_reset(model->items); + model->position = 0; + model->header = NULL; + }, + true); +} + +void button_menu_set_header(ButtonMenu* button_menu, const char* header) { + furi_assert(button_menu); + + with_view_model( + button_menu->view, ButtonMenuModel * model, { model->header = header; }, true); +} + +ButtonMenuItem* button_menu_add_item( + ButtonMenu* button_menu, + const char* label, + int32_t index, + ButtonMenuItemCallback callback, + ButtonMenuItemType type, + void* callback_context) { + ButtonMenuItem* item = NULL; + furi_assert(label); + furi_assert(button_menu); + + with_view_model( + button_menu->view, + ButtonMenuModel * model, + { + item = ButtonMenuItemArray_push_new(model->items); + item->label = label; + item->index = index; + item->type = type; + item->callback = callback; + item->callback_context = callback_context; + }, + true); + + return item; +} + +ButtonMenu* button_menu_alloc(void) { + ButtonMenu* button_menu = malloc(sizeof(ButtonMenu)); + button_menu->view = view_alloc(); + view_set_orientation(button_menu->view, ViewOrientationVertical); + view_set_context(button_menu->view, button_menu); + view_allocate_model(button_menu->view, ViewModelTypeLocking, sizeof(ButtonMenuModel)); + view_set_draw_callback(button_menu->view, button_menu_view_draw_callback); + view_set_input_callback(button_menu->view, button_menu_view_input_callback); + + with_view_model( + button_menu->view, + ButtonMenuModel * model, + { + ButtonMenuItemArray_init(model->items); + model->position = 0; + model->header = NULL; + }, + true); + + button_menu->freeze_input = false; + return button_menu; +} + +void button_menu_free(ButtonMenu* button_menu) { + furi_assert(button_menu); + + with_view_model( + button_menu->view, + ButtonMenuModel * model, + { ButtonMenuItemArray_clear(model->items); }, + true); + view_free(button_menu->view); + free(button_menu); +} + +void button_menu_set_selected_item(ButtonMenu* button_menu, uint32_t index) { + furi_assert(button_menu); + + with_view_model( + button_menu->view, + ButtonMenuModel * model, + { + size_t item_position = 0; + ButtonMenuItemArray_it_t it; + for(ButtonMenuItemArray_it(it, model->items); !ButtonMenuItemArray_end_p(it); + ButtonMenuItemArray_next(it), ++item_position) { + if((uint32_t)ButtonMenuItemArray_cref(it)->index == index) { + model->position = item_position; + break; + } + } + }, + true); +} diff --git a/applications/gui/modules/button_menu.h b/applications/services/gui/modules/button_menu.h similarity index 100% rename from applications/gui/modules/button_menu.h rename to applications/services/gui/modules/button_menu.h diff --git a/applications/services/gui/modules/button_panel.c b/applications/services/gui/modules/button_panel.c new file mode 100644 index 00000000000..ded7891e6c5 --- /dev/null +++ b/applications/services/gui/modules/button_panel.c @@ -0,0 +1,442 @@ +#include "button_panel.h" + +#include +#include + +#include +#include +#include + +#include +#include +#include + +typedef struct { + // uint16_t to support multi-screen, wide button panel + uint16_t x; + uint16_t y; + Font font; + const char* str; +} LabelElement; + +LIST_DEF(LabelList, LabelElement, M_POD_OPLIST) +#define M_OPL_LabelList_t() LIST_OPLIST(LabelList) + +typedef struct { + uint16_t x; + uint16_t y; + const Icon* name; + const Icon* name_selected; +} IconElement; + +LIST_DEF(IconList, IconElement, M_POD_OPLIST) +#define M_OPL_IconList_t() LIST_OPLIST(IconList) + +typedef struct ButtonItem { + uint32_t index; + ButtonItemCallback callback; + IconElement icon; + void* callback_context; +} ButtonItem; + +ARRAY_DEF(ButtonArray, ButtonItem*, M_PTR_OPLIST); +#define M_OPL_ButtonArray_t() ARRAY_OPLIST(ButtonArray, M_PTR_OPLIST) +ARRAY_DEF(ButtonMatrix, ButtonArray_t); +#define M_OPL_ButtonMatrix_t() ARRAY_OPLIST(ButtonMatrix, M_OPL_ButtonArray_t()) + +struct ButtonPanel { + View* view; +}; + +typedef struct { + ButtonMatrix_t button_matrix; + IconList_t icons; + LabelList_t labels; + uint16_t reserve_x; + uint16_t reserve_y; + uint16_t selected_item_x; + uint16_t selected_item_y; +} ButtonPanelModel; + +static ButtonItem** button_panel_get_item(ButtonPanelModel* model, size_t x, size_t y); +static void button_panel_process_up(ButtonPanel* button_panel); +static void button_panel_process_down(ButtonPanel* button_panel); +static void button_panel_process_left(ButtonPanel* button_panel); +static void button_panel_process_right(ButtonPanel* button_panel); +static void button_panel_process_ok(ButtonPanel* button_panel); +static void button_panel_view_draw_callback(Canvas* canvas, void* _model); +static bool button_panel_view_input_callback(InputEvent* event, void* context); + +ButtonPanel* button_panel_alloc() { + ButtonPanel* button_panel = malloc(sizeof(ButtonPanel)); + button_panel->view = view_alloc(); + view_set_orientation(button_panel->view, ViewOrientationVertical); + view_set_context(button_panel->view, button_panel); + view_allocate_model(button_panel->view, ViewModelTypeLocking, sizeof(ButtonPanelModel)); + view_set_draw_callback(button_panel->view, button_panel_view_draw_callback); + view_set_input_callback(button_panel->view, button_panel_view_input_callback); + + with_view_model( + button_panel->view, + ButtonPanelModel * model, + { + model->reserve_x = 0; + model->reserve_y = 0; + model->selected_item_x = 0; + model->selected_item_y = 0; + ButtonMatrix_init(model->button_matrix); + LabelList_init(model->labels); + }, + true); + + return button_panel; +} + +void button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t reserve_y) { + furi_check(reserve_x > 0); + furi_check(reserve_y > 0); + + with_view_model( + button_panel->view, + ButtonPanelModel * model, + { + model->reserve_x = reserve_x; + model->reserve_y = reserve_y; + ButtonMatrix_reserve(model->button_matrix, model->reserve_y); + for(size_t i = 0; i > model->reserve_y; ++i) { + ButtonArray_t* array = ButtonMatrix_get(model->button_matrix, i); + ButtonArray_init(*array); + ButtonArray_reserve(*array, reserve_x); + } + LabelList_init(model->labels); + }, + true); +} + +void button_panel_free(ButtonPanel* button_panel) { + furi_assert(button_panel); + + button_panel_reset(button_panel); + + with_view_model( + button_panel->view, + ButtonPanelModel * model, + { + LabelList_clear(model->labels); + ButtonMatrix_clear(model->button_matrix); + }, + true); + + view_free(button_panel->view); + free(button_panel); +} + +void button_panel_reset(ButtonPanel* button_panel) { + furi_assert(button_panel); + + with_view_model( + button_panel->view, + ButtonPanelModel * model, + { + for(size_t x = 0; x < model->reserve_x; ++x) { + for(size_t y = 0; y < model->reserve_y; ++y) { + ButtonItem** button_item = button_panel_get_item(model, x, y); + free(*button_item); + *button_item = NULL; + } + } + model->reserve_x = 0; + model->reserve_y = 0; + model->selected_item_x = 0; + model->selected_item_y = 0; + LabelList_reset(model->labels); + IconList_reset(model->icons); + ButtonMatrix_reset(model->button_matrix); + }, + true); +} + +static ButtonItem** button_panel_get_item(ButtonPanelModel* model, size_t x, size_t y) { + furi_assert(model); + + furi_check(x < model->reserve_x); + furi_check(y < model->reserve_y); + ButtonArray_t* button_array = ButtonMatrix_safe_get(model->button_matrix, x); + ButtonItem** button_item = ButtonArray_safe_get(*button_array, y); + return button_item; +} + +void button_panel_add_item( + ButtonPanel* button_panel, + uint32_t index, + uint16_t matrix_place_x, + uint16_t matrix_place_y, + uint16_t x, + uint16_t y, + const Icon* icon_name, + const Icon* icon_name_selected, + ButtonItemCallback callback, + void* callback_context) { + furi_assert(button_panel); + + with_view_model( //-V773 + button_panel->view, + ButtonPanelModel * model, + { + ButtonItem** button_item_ptr = + button_panel_get_item(model, matrix_place_x, matrix_place_y); + furi_check(*button_item_ptr == NULL); + *button_item_ptr = malloc(sizeof(ButtonItem)); + ButtonItem* button_item = *button_item_ptr; + button_item->callback = callback; + button_item->callback_context = callback_context; + button_item->icon.x = x; + button_item->icon.y = y; + button_item->icon.name = icon_name; + button_item->icon.name_selected = icon_name_selected; + button_item->index = index; + }, + true); +} + +View* button_panel_get_view(ButtonPanel* button_panel) { + furi_assert(button_panel); + return button_panel->view; +} + +static void button_panel_view_draw_callback(Canvas* canvas, void* _model) { + furi_assert(canvas); + furi_assert(_model); + + ButtonPanelModel* model = _model; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + for + M_EACH(icon, model->icons, IconList_t) { + canvas_draw_icon(canvas, icon->x, icon->y, icon->name); + } + + for(size_t x = 0; x < model->reserve_x; ++x) { + for(size_t y = 0; y < model->reserve_y; ++y) { + ButtonItem* button_item = *button_panel_get_item(model, x, y); + if(!button_item) { + continue; + } + const Icon* icon_name = button_item->icon.name; + if((model->selected_item_x == x) && (model->selected_item_y == y)) { + icon_name = button_item->icon.name_selected; + } + canvas_draw_icon(canvas, button_item->icon.x, button_item->icon.y, icon_name); + } + } + + for + M_EACH(label, model->labels, LabelList_t) { + canvas_set_font(canvas, label->font); + canvas_draw_str(canvas, label->x, label->y, label->str); + } +} + +static void button_panel_process_down(ButtonPanel* button_panel) { + with_view_model( + button_panel->view, + ButtonPanelModel * model, + { + uint16_t new_selected_item_x = model->selected_item_x; + uint16_t new_selected_item_y = model->selected_item_y; + size_t i; + + if(new_selected_item_y < (model->reserve_y - 1)) { + ++new_selected_item_y; + + for(i = 0; i < model->reserve_x; ++i) { + new_selected_item_x = (model->selected_item_x + i) % model->reserve_x; + if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) { + break; + } + } + if(i != model->reserve_x) { + model->selected_item_x = new_selected_item_x; + model->selected_item_y = new_selected_item_y; + } + } + }, + true); +} + +static void button_panel_process_up(ButtonPanel* button_panel) { + with_view_model( + button_panel->view, + ButtonPanelModel * model, + { + size_t new_selected_item_x = model->selected_item_x; + size_t new_selected_item_y = model->selected_item_y; + size_t i; + + if(new_selected_item_y > 0) { + --new_selected_item_y; + + for(i = 0; i < model->reserve_x; ++i) { + new_selected_item_x = (model->selected_item_x + i) % model->reserve_x; + if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) { + break; + } + } + if(i != model->reserve_x) { + model->selected_item_x = new_selected_item_x; + model->selected_item_y = new_selected_item_y; + } + } + }, + true); +} + +static void button_panel_process_left(ButtonPanel* button_panel) { + with_view_model( + button_panel->view, + ButtonPanelModel * model, + { + size_t new_selected_item_x = model->selected_item_x; + size_t new_selected_item_y = model->selected_item_y; + size_t i; + + if(new_selected_item_x > 0) { + --new_selected_item_x; + + for(i = 0; i < model->reserve_y; ++i) { + new_selected_item_y = (model->selected_item_y + i) % model->reserve_y; + if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) { + break; + } + } + if(i != model->reserve_y) { + model->selected_item_x = new_selected_item_x; + model->selected_item_y = new_selected_item_y; + } + } + }, + true); +} + +static void button_panel_process_right(ButtonPanel* button_panel) { + with_view_model( + button_panel->view, + ButtonPanelModel * model, + { + uint16_t new_selected_item_x = model->selected_item_x; + uint16_t new_selected_item_y = model->selected_item_y; + size_t i; + + if(new_selected_item_x < (model->reserve_x - 1)) { + ++new_selected_item_x; + + for(i = 0; i < model->reserve_y; ++i) { + new_selected_item_y = (model->selected_item_y + i) % model->reserve_y; + if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) { + break; + } + } + if(i != model->reserve_y) { + model->selected_item_x = new_selected_item_x; + model->selected_item_y = new_selected_item_y; + } + } + }, + true); +} + +void button_panel_process_ok(ButtonPanel* button_panel) { + ButtonItem* button_item = NULL; + + with_view_model( + button_panel->view, + ButtonPanelModel * model, + { + button_item = + *button_panel_get_item(model, model->selected_item_x, model->selected_item_y); + }, + true); + + if(button_item && button_item->callback) { + button_item->callback(button_item->callback_context, button_item->index); + } +} + +static bool button_panel_view_input_callback(InputEvent* event, void* context) { + ButtonPanel* button_panel = context; + furi_assert(button_panel); + bool consumed = false; + + if(event->type == InputTypeShort) { + switch(event->key) { + case InputKeyUp: + consumed = true; + button_panel_process_up(button_panel); + break; + case InputKeyDown: + consumed = true; + button_panel_process_down(button_panel); + break; + case InputKeyLeft: + consumed = true; + button_panel_process_left(button_panel); + break; + case InputKeyRight: + consumed = true; + button_panel_process_right(button_panel); + break; + case InputKeyOk: + consumed = true; + button_panel_process_ok(button_panel); + break; + default: + break; + } + } + + return consumed; +} + +void button_panel_add_label( + ButtonPanel* button_panel, + uint16_t x, + uint16_t y, + Font font, + const char* label_str) { + furi_assert(button_panel); + + with_view_model( + button_panel->view, + ButtonPanelModel * model, + { + LabelElement* label = LabelList_push_raw(model->labels); + label->x = x; + label->y = y; + label->font = font; + label->str = label_str; + }, + true); +} + +// Draw an icon but don't make it a button. +void button_panel_add_icon( + ButtonPanel* button_panel, + uint16_t x, + uint16_t y, + const Icon* icon_name) { + furi_assert(button_panel); + + with_view_model( //-V773 + button_panel->view, + ButtonPanelModel * model, + { + IconElement* icon = IconList_push_raw(model->icons); + icon->x = x; + icon->y = y; + icon->name = icon_name; + icon->name_selected = icon_name; + }, + true); +} \ No newline at end of file diff --git a/applications/gui/modules/button_panel.h b/applications/services/gui/modules/button_panel.h similarity index 87% rename from applications/gui/modules/button_panel.h rename to applications/services/gui/modules/button_panel.h index 0c17e3a7c6a..1218222b881 100644 --- a/applications/gui/modules/button_panel.h +++ b/applications/services/gui/modules/button_panel.h @@ -53,7 +53,7 @@ void button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t re * @param button_panel ButtonPanel instance * @param index value to pass to callback * @param matrix_place_x coordinates by x-axis on virtual grid, it - * is only used for naviagation + * is only used for navigation * @param matrix_place_y coordinates by y-axis on virtual grid, it * is only used for naviagation * @param x x-coordinate to draw icon on @@ -100,6 +100,19 @@ void button_panel_add_label( Font font, const char* label_str); +/** Add a non-button icon to button_panel module. + * + * @param button_panel ButtonPanel instance + * @param x x-coordinate to place icon + * @param y y-coordinate to place icon + * @param icon_name name of the icon to draw + */ +void button_panel_add_icon( + ButtonPanel* button_panel, + uint16_t x, + uint16_t y, + const Icon* icon_name); + #ifdef __cplusplus } #endif diff --git a/applications/services/gui/modules/byte_input.c b/applications/services/gui/modules/byte_input.c new file mode 100644 index 00000000000..e9cd78da02b --- /dev/null +++ b/applications/services/gui/modules/byte_input.c @@ -0,0 +1,869 @@ +#include "byte_input.h" + +#include +#include +#include + +/** ByteInput type */ +struct ByteInput { + View* view; +}; + +typedef struct { + const uint8_t value; + const uint8_t x; + const uint8_t y; +} ByteInputKey; + +typedef struct { + const char* header; + uint8_t* bytes; + uint8_t bytes_count; + + ByteInputCallback input_callback; + ByteChangedCallback changed_callback; + void* callback_context; + + bool selected_high_nibble; + uint8_t selected_byte; + int8_t selected_row; // row -2 - mini_editor, -1 - input, row 0 & 1 - keyboard + uint8_t selected_column; + uint8_t first_visible_byte; +} ByteInputModel; + +static const uint8_t keyboard_origin_x = 7; +static const uint8_t keyboard_origin_y = 31; +static const uint8_t keyboard_row_count = 2; +static const uint8_t enter_symbol = '\r'; +static const uint8_t backspace_symbol = '\b'; +static const uint8_t max_drawable_bytes = 8; + +static const ByteInputKey keyboard_keys_row_1[] = { + {'0', 0, 12}, + {'1', 11, 12}, + {'2', 22, 12}, + {'3', 33, 12}, + {'4', 44, 12}, + {'5', 55, 12}, + {'6', 66, 12}, + {'7', 77, 12}, + {backspace_symbol, 103, 4}, +}; + +static const ByteInputKey keyboard_keys_row_2[] = { + {'8', 0, 26}, + {'9', 11, 26}, + {'A', 22, 26}, + {'B', 33, 26}, + {'C', 44, 26}, + {'D', 55, 26}, + {'E', 66, 26}, + {'F', 77, 26}, + {enter_symbol, 95, 17}, +}; + +/** Get row size + * + * @param row_index Index of row + * + * @return uint8_t Row size + */ +static uint8_t byte_input_get_row_size(uint8_t row_index) { + uint8_t row_size = 0; + + 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; + default: + furi_crash(); + } + + return row_size; +} + +/** Get row pointer + * + * @param row_index Index of row + * + * @return const ByteInputKey* Row pointer + */ +static const ByteInputKey* byte_input_get_row(uint8_t row_index) { + const ByteInputKey* row = NULL; + + switch(row_index + 1) { + case 1: + row = keyboard_keys_row_1; + break; + case 2: + row = keyboard_keys_row_2; + break; + default: + furi_crash(); + } + + return row; +} + +/** Get text from nibble + * + * @param byte byte value + * @param high_nibble Get from high nibble, otherwise low nibble + * + * @return char nibble text + */ +static char byte_input_get_nibble_text(uint8_t byte, bool high_nibble) { + if(high_nibble) { + byte = byte >> 4; + } + byte = byte & 0x0F; + + switch(byte & 0x0F) { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + case 0x4: + case 0x5: + case 0x6: + case 0x7: + case 0x8: + case 0x9: + byte = byte + '0'; + break; + case 0xA: + case 0xB: + case 0xC: + case 0xD: + case 0xE: + case 0xF: + byte = byte - 0xA + 'A'; + break; + default: + byte = '!'; + break; + } + + return byte; +} + +const char num_to_char[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; + +/** Draw input box (common view) + * + * @param canvas The canvas + * @param model The model + */ +static void byte_input_draw_input(Canvas* canvas, ByteInputModel* model) { + const uint8_t text_x = 8; + const uint8_t text_y = 25; + const uint8_t text_y2 = 40; + const bool draw_index_line = + (model->selected_row == -2) && + (model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes + 1) <= 100); + + elements_slightly_rounded_frame(canvas, 6, 14, 116, 15); + + canvas_draw_icon(canvas, 2, 19, &I_ButtonLeftSmall_3x5); + canvas_draw_icon(canvas, 123, 19, &I_ButtonRightSmall_3x5); + + for(uint8_t i = model->first_visible_byte; + i < model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes); + i++) { + uint8_t byte_position = i - model->first_visible_byte; + + if(i == model->selected_byte) { + canvas_draw_frame(canvas, text_x + byte_position * 14, text_y - 9, 15, 11); + if(model->selected_row == -2) { + canvas_draw_icon( + canvas, text_x + 6 + byte_position * 14, text_y - 14, &I_arrow_nano_up); + canvas_draw_icon( + canvas, text_x + 6 + byte_position * 14, text_y + 5, &I_arrow_nano_down); + } + + if(model->selected_high_nibble) { + canvas_draw_glyph( + canvas, + text_x + 8 + byte_position * 14, + text_y, + byte_input_get_nibble_text(model->bytes[i], false)); + canvas_draw_box(canvas, text_x + 1 + byte_position * 14, text_y - 8, 7, 9); + canvas_invert_color(canvas); + canvas_draw_line( + canvas, + text_x + 14 + byte_position * 14, + text_y - 6, + text_x + 14 + byte_position * 14, + text_y - 2); + canvas_draw_glyph( + canvas, + text_x + 2 + byte_position * 14, + text_y, + byte_input_get_nibble_text(model->bytes[i], true)); + canvas_invert_color(canvas); + } else { + canvas_draw_box(canvas, text_x + 7 + byte_position * 14, text_y - 8, 7, 9); + canvas_draw_glyph( + canvas, + text_x + 2 + byte_position * 14, + text_y, + byte_input_get_nibble_text(model->bytes[i], true)); + canvas_invert_color(canvas); + canvas_draw_line( + canvas, + text_x + byte_position * 14, + text_y - 6, + text_x + byte_position * 14, + text_y - 2); + canvas_draw_glyph( + canvas, + text_x + 8 + byte_position * 14, + text_y, + byte_input_get_nibble_text(model->bytes[i], false)); + canvas_invert_color(canvas); + } + } else { + if(model->first_visible_byte > 0 && i == model->first_visible_byte) { + canvas_draw_icon( + canvas, + text_x + 2 + byte_position * 14, + text_y - 7, + &I_More_data_placeholder_5x7); + } else { + canvas_draw_glyph( + canvas, + text_x + 2 + byte_position * 14, + text_y, + byte_input_get_nibble_text(model->bytes[i], true)); + } + if(model->bytes_count - model->first_visible_byte > max_drawable_bytes && + i == model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes) - 1) { + canvas_draw_icon( + canvas, + text_x + 8 + byte_position * 14, + text_y - 7, + &I_More_data_placeholder_5x7); + } else { + canvas_draw_glyph( + canvas, + text_x + 8 + byte_position * 14, + text_y, + byte_input_get_nibble_text(model->bytes[i], false)); + } + } + + if(draw_index_line) { + canvas_draw_icon(canvas, 1, text_y + 8, &I_Hashmark_7x7); + canvas_draw_glyph( + canvas, text_x + 2 + byte_position * 14, text_y2, num_to_char[(i + 1) / 10]); + + canvas_draw_glyph( + canvas, text_x + 8 + byte_position * 14, text_y2, num_to_char[(i + 1) % 10]); + } + } + + if((model->selected_row == -2) && + (model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes + 1) > 100)) { + char str[20]; + + canvas_set_font(canvas, FontSecondary); + snprintf(str, 20, "Selected index"); + canvas_draw_str(canvas, text_x, text_y2, str); + + canvas_set_font(canvas, FontPrimary); + snprintf(str, 20, "%u", (model->selected_byte + 1)); + canvas_draw_str(canvas, text_x + 75, text_y2, str); + } +} + +/** Draw input box (selected view) + * + * @param canvas The canvas + * @param model The model + */ +static void byte_input_draw_input_selected(Canvas* canvas, ByteInputModel* model) { + const uint8_t text_x = 7; + const uint8_t text_y = 25; + + canvas_draw_box(canvas, 0, 12, 127, 19); + canvas_invert_color(canvas); + + elements_slightly_rounded_frame(canvas, 6, 14, 115, 15); + canvas_draw_icon(canvas, 2, 19, &I_ButtonLeftSmall_3x5); + canvas_draw_icon(canvas, 122, 19, &I_ButtonRightSmall_3x5); + + for(uint8_t i = model->first_visible_byte; + i < model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes); + i++) { + uint8_t byte_position = i - model->first_visible_byte; + + if(i == model->selected_byte) { + canvas_draw_box(canvas, text_x + 1 + byte_position * 14, text_y - 9, 13, 11); + canvas_invert_color(canvas); + canvas_draw_glyph( + canvas, + text_x + 2 + byte_position * 14, + text_y, + byte_input_get_nibble_text(model->bytes[i], true)); + canvas_draw_glyph( + canvas, + text_x + 8 + byte_position * 14, + text_y, + byte_input_get_nibble_text(model->bytes[i], false)); + canvas_invert_color(canvas); + } else { + if(model->first_visible_byte > 0 && i == model->first_visible_byte) { + canvas_draw_icon( + canvas, + text_x + 2 + byte_position * 14, + text_y - 7, + &I_More_data_placeholder_5x7); + } else { + canvas_draw_glyph( + canvas, + text_x + 2 + byte_position * 14, + text_y, + byte_input_get_nibble_text(model->bytes[i], true)); + } + if(model->bytes_count - model->first_visible_byte > max_drawable_bytes && + i == model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes) - 1) { + canvas_draw_icon( + canvas, + text_x + 8 + byte_position * 14, + text_y - 7, + &I_More_data_placeholder_5x7); + } else { + canvas_draw_glyph( + canvas, + text_x + 8 + byte_position * 14, + text_y, + byte_input_get_nibble_text(model->bytes[i], false)); + } + } + } + + canvas_invert_color(canvas); +} + +/** Set nibble at position + * + * @param data where to set nibble + * @param position byte position + * @param value char value + * @param high_nibble set high nibble + */ +static void byte_input_set_nibble(uint8_t* data, uint8_t position, char value, bool high_nibble) { + switch(value) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + value = value - '0'; + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + value = value - 'A' + 10; + break; + default: + value = 0; + break; + } + + if(high_nibble) { + data[position] &= 0x0F; + data[position] |= value << 4; + } else { + data[position] &= 0xF0; + data[position] |= value; + } +} + +/** What currently selected + * + * @param model The model + * + * @return true - keyboard selected, false - input selected + */ +static bool byte_input_keyboard_selected(ByteInputModel* model) { + return model->selected_row >= 0; +} + +/** Do transition from keyboard + * + * @param model The model + */ +static void byte_input_transition_from_keyboard(ByteInputModel* model) { + model->selected_row += 1; + model->selected_high_nibble = true; +} + +/** Increase selected byte position + * + * @param model The model + */ +static void byte_input_inc_selected_byte(ByteInputModel* model) { + if(model->selected_byte < model->bytes_count - 1) { + model->selected_byte += 1; + + if(model->bytes_count > max_drawable_bytes) { + if(model->selected_byte - model->first_visible_byte > (max_drawable_bytes - 2)) { + if(model->first_visible_byte < model->bytes_count - max_drawable_bytes) { + model->first_visible_byte++; + } + } + } + } +} + +static void byte_input_inc_selected_byte_mini(ByteInputModel* model) { + if((model->selected_byte < model->bytes_count - 1) || model->selected_high_nibble) { + if(!model->selected_high_nibble) { + model->selected_high_nibble = !model->selected_high_nibble; //-V547 + byte_input_inc_selected_byte(model); + } else { + model->selected_high_nibble = !model->selected_high_nibble; //-V547 + } + } +} + +/** Decrease selected byte position + * + * @param model The model + */ +static void byte_input_dec_selected_byte(ByteInputModel* model) { + if(model->selected_byte > 0) { + model->selected_byte -= 1; + + furi_assert(model->selected_byte >= model->first_visible_byte); + if(model->selected_byte - model->first_visible_byte < 1) { + if(model->first_visible_byte > 0) { + model->first_visible_byte--; + } + } + } +} + +static void byte_input_dec_selected_byte_mini(ByteInputModel* model) { + if(model->selected_byte > 0 || !model->selected_high_nibble) { + if(model->selected_high_nibble) { + model->selected_high_nibble = !model->selected_high_nibble; //-V547 + byte_input_dec_selected_byte(model); + } else { + model->selected_high_nibble = !model->selected_high_nibble; //-V547 + } + } +} + +/** Call input callback + * + * @param model The model + */ +static void byte_input_call_input_callback(ByteInputModel* model) { + if(model->input_callback != NULL) { + model->input_callback(model->callback_context); + } +} + +/** Call changed callback + * + * @param model The model + */ +static void byte_input_call_changed_callback(ByteInputModel* model) { + if(model->changed_callback != NULL) { + model->changed_callback(model->callback_context); + } +} + +/** Clear selected byte + * + * @param model The model + */ + +static void byte_input_clear_selected_byte(ByteInputModel* model) { + model->bytes[model->selected_byte] = 0; + model->selected_high_nibble = true; + byte_input_dec_selected_byte(model); + byte_input_call_changed_callback(model); +} + +/** Handle up button + * + * @param model The model + */ +static void byte_input_handle_up(ByteInputModel* model) { + if(model->selected_row > -2) { + model->selected_row -= 1; + } else if(model->selected_row == -2) { + if(!model->selected_high_nibble) { + model->bytes[model->selected_byte] = (model->bytes[model->selected_byte] & 0xF0) | + ((model->bytes[model->selected_byte] + 1) & 0x0F); + } else { + model->bytes[model->selected_byte] = + ((model->bytes[model->selected_byte] + 0x10) & 0xF0) | + (model->bytes[model->selected_byte] & 0x0F); + } + byte_input_call_changed_callback(model); + } +} + +/** Handle down button + * + * @param model The model + */ +static void byte_input_handle_down(ByteInputModel* model) { + if(model->selected_row != -2) { + if(byte_input_keyboard_selected(model)) { + if(model->selected_row < keyboard_row_count - 1) { + model->selected_row += 1; + } + } else { + byte_input_transition_from_keyboard(model); + } + } else { + if(!model->selected_high_nibble) { + model->bytes[model->selected_byte] = (model->bytes[model->selected_byte] & 0xF0) | + ((model->bytes[model->selected_byte] - 1) & 0x0F); + } else { + model->bytes[model->selected_byte] = + ((model->bytes[model->selected_byte] - 0x10) & 0xF0) | + (model->bytes[model->selected_byte] & 0x0F); + } + byte_input_call_changed_callback(model); + } +} + +/** Handle left button + * + * @param model The model + */ +static void byte_input_handle_left(ByteInputModel* model) { + if(byte_input_keyboard_selected(model)) { + if(model->selected_column > 0) { + model->selected_column -= 1; + } else { + model->selected_column = byte_input_get_row_size(model->selected_row) - 1; + } + } else { + if(model->selected_row != -2) { + byte_input_dec_selected_byte(model); + } else { + byte_input_dec_selected_byte_mini(model); + } + } +} + +/** Handle right button + * + * @param model The model + */ +static void byte_input_handle_right(ByteInputModel* model) { + if(byte_input_keyboard_selected(model)) { + if(model->selected_column < byte_input_get_row_size(model->selected_row) - 1) { + model->selected_column += 1; + } else { + model->selected_column = 0; + } + } else { + if(model->selected_row != -2) { + byte_input_inc_selected_byte(model); + } else { + byte_input_inc_selected_byte_mini(model); + } + } +} + +/** Handle OK button + * + * @param model The model + */ +static void byte_input_handle_ok(ByteInputModel* model) { + if(byte_input_keyboard_selected(model)) { + uint8_t value = byte_input_get_row(model->selected_row)[model->selected_column].value; + + if(value == enter_symbol) { + byte_input_call_input_callback(model); + } else if(value == backspace_symbol) { + byte_input_clear_selected_byte(model); + } else { + byte_input_set_nibble( + model->bytes, model->selected_byte, value, model->selected_high_nibble); + if(model->selected_high_nibble == true) { + model->selected_high_nibble = false; + } else { + byte_input_inc_selected_byte(model); + model->selected_high_nibble = true; + } + byte_input_call_changed_callback(model); + } + } else if(model->selected_row == -2) { + byte_input_call_input_callback(model); + } else { + byte_input_transition_from_keyboard(model); + } +} + +/** Draw callback + * + * @param canvas The canvas + * @param _model The model + */ +static void byte_input_view_draw_callback(Canvas* canvas, void* _model) { + ByteInputModel* model = _model; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontKeyboard); + + if(model->selected_row == -1) { + byte_input_draw_input_selected(canvas, model); + } else { + byte_input_draw_input(canvas, model); + } + + if(model->selected_row == -2) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_icon(canvas, 3, 1, &I_Pin_back_arrow_10x8); + canvas_draw_str_aligned(canvas, 16, 9, AlignLeft, AlignBottom, "back to keyboard"); + elements_button_center(canvas, "Save"); + } else { + // Draw the header + canvas_set_font(canvas, FontSecondary); + if(model->selected_row == -1) { + canvas_draw_str(canvas, 10, 9, "Move up for alternate input"); + canvas_draw_icon(canvas, 3, 4, &I_SmallArrowUp_3x5); + } else { + canvas_draw_str(canvas, 2, 9, model->header); + } + canvas_set_font(canvas, FontKeyboard); + // Draw keyboard + for(uint8_t row = 0; row < keyboard_row_count; row++) { + const uint8_t column_count = byte_input_get_row_size(row); + const ByteInputKey* keys = byte_input_get_row(row); + + for(size_t column = 0; column < column_count; column++) { + if(keys[column].value == enter_symbol) { + 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].value == backspace_symbol) { + 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 - 3, + keyboard_origin_y + keys[column].y - 10, + 11, + 13); + canvas_set_color(canvas, ColorWhite); + } else if( + model->selected_row == -1 && row == 0 && + model->selected_column == column) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame( + canvas, + keyboard_origin_x + keys[column].x - 3, + keyboard_origin_y + keys[column].y - 10, + 11, + 13); + } else { + canvas_set_color(canvas, ColorBlack); + } + + canvas_draw_glyph( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + keys[column].value); + } + } + } + } +} + +/** Input callback + * + * @param event The event + * @param context The context + * + * @return true + * @return false + */ +static bool byte_input_view_input_callback(InputEvent* event, void* context) { + ByteInput* byte_input = context; + furi_assert(byte_input); + bool consumed = false; + + if(event->type == InputTypeShort || event->type == InputTypeRepeat) { + switch(event->key) { + case InputKeyLeft: + with_view_model( + byte_input->view, ByteInputModel * model, { byte_input_handle_left(model); }, true); + consumed = true; + break; + case InputKeyRight: + with_view_model( + byte_input->view, + ByteInputModel * model, + { byte_input_handle_right(model); }, + true); + consumed = true; + break; + case InputKeyUp: + with_view_model( + byte_input->view, ByteInputModel * model, { byte_input_handle_up(model); }, true); + consumed = true; + break; + case InputKeyDown: + with_view_model( + byte_input->view, ByteInputModel * model, { byte_input_handle_down(model); }, true); + consumed = true; + break; + case InputKeyOk: + with_view_model( + byte_input->view, ByteInputModel * model, { byte_input_handle_ok(model); }, true); + consumed = true; + break; + default: + break; + } + } + + if(event->type == InputTypeShort && event->key == InputKeyBack) { + // Back to keyboard + with_view_model( + byte_input->view, + ByteInputModel * model, + { + if(model->selected_row == -2) { + model->selected_row += 1; + consumed = true; + }; + }, + true); + } + + if((event->type == InputTypeLong || event->type == InputTypeRepeat) && + event->key == InputKeyBack) { + with_view_model( + byte_input->view, + ByteInputModel * model, + { byte_input_clear_selected_byte(model); }, + true); + consumed = true; + } + + return consumed; +} + +/** Reset all input-related data in model + * + * @param model The model + */ +static void byte_input_reset_model_input_data(ByteInputModel* model) { + model->bytes = NULL; + model->bytes_count = 0; + model->selected_high_nibble = true; + model->selected_byte = 0; + model->selected_row = 0; + model->selected_column = 0; + model->first_visible_byte = 0; +} + +ByteInput* byte_input_alloc() { + ByteInput* byte_input = malloc(sizeof(ByteInput)); + byte_input->view = view_alloc(); + view_set_context(byte_input->view, byte_input); + view_allocate_model(byte_input->view, ViewModelTypeLocking, sizeof(ByteInputModel)); + view_set_draw_callback(byte_input->view, byte_input_view_draw_callback); + view_set_input_callback(byte_input->view, byte_input_view_input_callback); + + with_view_model( + byte_input->view, + ByteInputModel * model, + { + model->header = ""; + model->input_callback = NULL; + model->changed_callback = NULL; + model->callback_context = NULL; + byte_input_reset_model_input_data(model); + }, + true); + + return byte_input; +} + +void byte_input_free(ByteInput* byte_input) { + furi_assert(byte_input); + view_free(byte_input->view); + free(byte_input); +} + +View* byte_input_get_view(ByteInput* byte_input) { + furi_assert(byte_input); + return byte_input->view; +} + +void byte_input_set_result_callback( + ByteInput* byte_input, + ByteInputCallback input_callback, + ByteChangedCallback changed_callback, + void* callback_context, + uint8_t* bytes, + uint8_t bytes_count) { + with_view_model( + byte_input->view, + ByteInputModel * model, + { + byte_input_reset_model_input_data(model); + model->input_callback = input_callback; + model->changed_callback = changed_callback; + model->callback_context = callback_context; + model->bytes = bytes; + model->bytes_count = bytes_count; + }, + true); +} + +void byte_input_set_header_text(ByteInput* byte_input, const char* text) { + with_view_model( + byte_input->view, ByteInputModel * model, { model->header = text; }, true); +} diff --git a/applications/gui/modules/byte_input.h b/applications/services/gui/modules/byte_input.h similarity index 100% rename from applications/gui/modules/byte_input.h rename to applications/services/gui/modules/byte_input.h diff --git a/applications/gui/modules/dialog_ex.c b/applications/services/gui/modules/dialog_ex.c old mode 100755 new mode 100644 similarity index 89% rename from applications/gui/modules/dialog_ex.c rename to applications/services/gui/modules/dialog_ex.c index dee2a0971c7..7c3ef9b4522 --- a/applications/gui/modules/dialog_ex.c +++ b/applications/services/gui/modules/dialog_ex.c @@ -90,12 +90,14 @@ static bool dialog_ex_view_input_callback(InputEvent* event, void* context) { const char* right_text = NULL; with_view_model( - dialog_ex->view, (DialogExModel * model) { + dialog_ex->view, + DialogExModel * model, + { left_text = model->left_text; center_text = model->center_text; right_text = model->right_text; - return true; - }); + }, + true); if(dialog_ex->callback) { if(event->type == InputTypeShort) { @@ -145,11 +147,13 @@ DialogEx* dialog_ex_alloc() { DialogEx* dialog_ex = malloc(sizeof(DialogEx)); dialog_ex->view = view_alloc(); view_set_context(dialog_ex->view, dialog_ex); - view_allocate_model(dialog_ex->view, ViewModelTypeLockFree, sizeof(DialogExModel)); + view_allocate_model(dialog_ex->view, ViewModelTypeLocking, sizeof(DialogExModel)); view_set_draw_callback(dialog_ex->view, dialog_ex_view_draw_callback); view_set_input_callback(dialog_ex->view, dialog_ex_view_input_callback); with_view_model( - dialog_ex->view, (DialogExModel * model) { + dialog_ex->view, + DialogExModel * model, + { model->header.text = NULL; model->header.x = 0; model->header.y = 0; @@ -169,9 +173,8 @@ DialogEx* dialog_ex_alloc() { model->left_text = NULL; model->center_text = NULL; model->right_text = NULL; - - return true; - }); + }, + true); dialog_ex->enable_extended_events = false; return dialog_ex; } @@ -206,14 +209,16 @@ void dialog_ex_set_header( Align vertical) { furi_assert(dialog_ex); with_view_model( - dialog_ex->view, (DialogExModel * model) { + dialog_ex->view, + DialogExModel * model, + { model->header.text = text; model->header.x = x; model->header.y = y; model->header.horizontal = horizontal; model->header.vertical = vertical; - return true; - }); + }, + true); } void dialog_ex_set_text( @@ -225,52 +230,47 @@ void dialog_ex_set_text( Align vertical) { furi_assert(dialog_ex); with_view_model( - dialog_ex->view, (DialogExModel * model) { + dialog_ex->view, + DialogExModel * model, + { model->text.text = text; model->text.x = x; model->text.y = y; model->text.horizontal = horizontal; model->text.vertical = vertical; - return true; - }); + }, + true); } void dialog_ex_set_icon(DialogEx* dialog_ex, uint8_t x, uint8_t y, const Icon* icon) { furi_assert(dialog_ex); with_view_model( - dialog_ex->view, (DialogExModel * model) { + dialog_ex->view, + DialogExModel * model, + { model->icon.x = x; model->icon.y = y; model->icon.icon = icon; - return true; - }); + }, + true); } void dialog_ex_set_left_button_text(DialogEx* dialog_ex, const char* text) { furi_assert(dialog_ex); with_view_model( - dialog_ex->view, (DialogExModel * model) { - model->left_text = text; - return true; - }); + dialog_ex->view, DialogExModel * model, { model->left_text = text; }, true); } void dialog_ex_set_center_button_text(DialogEx* dialog_ex, const char* text) { furi_assert(dialog_ex); with_view_model( - dialog_ex->view, (DialogExModel * model) { - model->center_text = text; - return true; - }); + dialog_ex->view, DialogExModel * model, { model->center_text = text; }, true); } void dialog_ex_set_right_button_text(DialogEx* dialog_ex, const char* text) { furi_assert(dialog_ex); with_view_model( - dialog_ex->view, (DialogExModel * model) { - model->right_text = text; - return true; - }); + dialog_ex->view, DialogExModel * model, { model->right_text = text; }, true); } void dialog_ex_reset(DialogEx* dialog_ex) { @@ -279,15 +279,17 @@ void dialog_ex_reset(DialogEx* dialog_ex) { .text = NULL, .x = 0, .y = 0, .horizontal = AlignLeft, .vertical = AlignLeft}; IconElement clean_icon_el = {.icon = NULL, .x = 0, .y = 0}; with_view_model( - dialog_ex->view, (DialogExModel * model) { + dialog_ex->view, + DialogExModel * model, + { model->header = clean_text_el; model->text = clean_text_el; model->icon = clean_icon_el; model->left_text = NULL; model->center_text = NULL; model->right_text = NULL; - return true; - }); + }, + true); dialog_ex->context = NULL; dialog_ex->callback = NULL; } diff --git a/applications/gui/modules/dialog_ex.h b/applications/services/gui/modules/dialog_ex.h similarity index 95% rename from applications/gui/modules/dialog_ex.h rename to applications/services/gui/modules/dialog_ex.h index 4c6094239ea..26a46535450 100644 --- a/applications/gui/modules/dialog_ex.h +++ b/applications/services/gui/modules/dialog_ex.h @@ -76,8 +76,8 @@ void dialog_ex_set_context(DialogEx* dialog_ex, void* context); * @param text text to be shown, can be multiline * @param x x position * @param y y position - * @param horizontal horizontal text aligment - * @param vertical vertical text aligment + * @param horizontal horizontal text alignment + * @param vertical vertical text alignment */ void dialog_ex_set_header( DialogEx* dialog_ex, @@ -95,8 +95,8 @@ void dialog_ex_set_header( * @param text text to be shown, can be multiline * @param x x position * @param y y position - * @param horizontal horizontal text aligment - * @param vertical vertical text aligment + * @param horizontal horizontal text alignment + * @param vertical vertical text alignment */ void dialog_ex_set_text( DialogEx* dialog_ex, diff --git a/applications/gui/modules/empty_screen.c b/applications/services/gui/modules/empty_screen.c similarity index 100% rename from applications/gui/modules/empty_screen.c rename to applications/services/gui/modules/empty_screen.c diff --git a/applications/gui/modules/empty_screen.h b/applications/services/gui/modules/empty_screen.h similarity index 100% rename from applications/gui/modules/empty_screen.h rename to applications/services/gui/modules/empty_screen.h diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c new file mode 100644 index 00000000000..91b03ec8aae --- /dev/null +++ b/applications/services/gui/modules/file_browser.c @@ -0,0 +1,735 @@ +#include "file_browser.h" +#include "file_browser_worker.h" + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#define LIST_ITEMS 5u +#define MAX_LEN_PX 110 +#define FRAME_HEIGHT 12 +#define Y_OFFSET 3 + +#define ITEM_LIST_LEN_MAX 50 + +#define CUSTOM_ICON_MAX_SIZE 32 + +#define SCROLL_INTERVAL (333) +#define SCROLL_DELAY (2) + +typedef enum { + BrowserItemTypeLoading, + BrowserItemTypeBack, + BrowserItemTypeFolder, + BrowserItemTypeFile, +} BrowserItemType; + +typedef struct { + FuriString* path; + BrowserItemType type; + uint8_t* custom_icon_data; + FuriString* display_name; +} BrowserItem_t; + +static void BrowserItem_t_init(BrowserItem_t* obj) { + obj->type = BrowserItemTypeLoading; + obj->path = furi_string_alloc(); + obj->display_name = furi_string_alloc(); + obj->custom_icon_data = NULL; +} + +static void BrowserItem_t_init_set(BrowserItem_t* obj, const BrowserItem_t* src) { + obj->type = src->type; + obj->path = furi_string_alloc_set(src->path); + obj->display_name = furi_string_alloc_set(src->display_name); + if(src->custom_icon_data) { + obj->custom_icon_data = malloc(CUSTOM_ICON_MAX_SIZE); + memcpy(obj->custom_icon_data, src->custom_icon_data, CUSTOM_ICON_MAX_SIZE); + } else { + obj->custom_icon_data = NULL; + } +} + +static void BrowserItem_t_set(BrowserItem_t* obj, const BrowserItem_t* src) { + obj->type = src->type; + furi_string_set(obj->path, src->path); + furi_string_set(obj->display_name, src->display_name); + if(src->custom_icon_data) { + memcpy(obj->custom_icon_data, src->custom_icon_data, CUSTOM_ICON_MAX_SIZE); + } else { + obj->custom_icon_data = NULL; + } +} + +static void BrowserItem_t_clear(BrowserItem_t* obj) { + furi_string_free(obj->path); + furi_string_free(obj->display_name); + if(obj->custom_icon_data) { + free(obj->custom_icon_data); + } +} + +ARRAY_DEF( + items_array, + BrowserItem_t, + (INIT(API_2(BrowserItem_t_init)), + SET(API_6(BrowserItem_t_set)), + INIT_SET(API_6(BrowserItem_t_init_set)), + CLEAR(API_2(BrowserItem_t_clear)))) + +struct FileBrowser { + View* view; + BrowserWorker* worker; + const char* ext_filter; + const char* base_path; + bool skip_assets; + bool hide_dot_files; + bool hide_ext; + + FileBrowserCallback callback; + void* context; + + FileBrowserLoadItemCallback item_callback; + void* item_context; + + FuriString* result_path; + FuriTimer* scroll_timer; +}; + +typedef struct { + items_array_t items; + + bool is_root; + bool folder_loading; + bool list_loading; + uint32_t item_cnt; + int32_t item_idx; + int32_t array_offset; + int32_t list_offset; + + const Icon* file_icon; + bool hide_ext; + size_t scroll_counter; + + uint32_t button_held_for_ticks; +} FileBrowserModel; + +static const Icon* BrowserItemIcons[] = { + [BrowserItemTypeLoading] = &I_loading_10px, + [BrowserItemTypeBack] = &I_back_10px, + [BrowserItemTypeFolder] = &I_dir_10px, + [BrowserItemTypeFile] = &I_unknown_10px, +}; + +static void file_browser_view_draw_callback(Canvas* canvas, void* _model); +static bool file_browser_view_input_callback(InputEvent* event, void* context); + +static void + browser_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root); +static void browser_list_load_cb(void* context, uint32_t list_load_offset); +static void + browser_list_item_cb(void* context, FuriString* item_path, bool is_folder, bool is_last); +static void browser_long_load_cb(void* context); + +static void file_browser_scroll_timer_callback(void* context) { + furi_assert(context); + FileBrowser* browser = context; + with_view_model( + browser->view, FileBrowserModel * model, { model->scroll_counter++; }, true); +} + +static void file_browser_view_enter_callback(void* context) { + furi_assert(context); + FileBrowser* browser = context; + with_view_model( + browser->view, FileBrowserModel * model, { model->scroll_counter = 0; }, true); + furi_timer_start(browser->scroll_timer, SCROLL_INTERVAL); +} + +static void file_browser_view_exit_callback(void* context) { + furi_assert(context); + FileBrowser* browser = context; + furi_timer_stop(browser->scroll_timer); +} + +FileBrowser* file_browser_alloc(FuriString* result_path) { + furi_assert(result_path); + FileBrowser* browser = malloc(sizeof(FileBrowser)); + browser->view = view_alloc(); + view_allocate_model(browser->view, ViewModelTypeLocking, sizeof(FileBrowserModel)); + view_set_context(browser->view, browser); + view_set_draw_callback(browser->view, file_browser_view_draw_callback); + view_set_input_callback(browser->view, file_browser_view_input_callback); + view_set_enter_callback(browser->view, file_browser_view_enter_callback); + view_set_exit_callback(browser->view, file_browser_view_exit_callback); + + browser->scroll_timer = + furi_timer_alloc(file_browser_scroll_timer_callback, FuriTimerTypePeriodic, browser); + + browser->result_path = result_path; + + with_view_model( + browser->view, FileBrowserModel * model, { items_array_init(model->items); }, false); + + return browser; +} + +void file_browser_free(FileBrowser* browser) { + furi_assert(browser); + + furi_timer_free(browser->scroll_timer); + + with_view_model( + browser->view, FileBrowserModel * model, { items_array_clear(model->items); }, false); + + view_free(browser->view); + free(browser); +} + +View* file_browser_get_view(FileBrowser* browser) { + furi_assert(browser); + return browser->view; +} + +void file_browser_configure( + FileBrowser* browser, + const char* extension, + const char* base_path, + bool skip_assets, + bool hide_dot_files, + const Icon* file_icon, + bool hide_ext) { + furi_assert(browser); + + browser->ext_filter = extension; + browser->skip_assets = skip_assets; + browser->hide_ext = hide_ext; + browser->base_path = base_path; + browser->hide_dot_files = hide_dot_files; + + with_view_model( + browser->view, + FileBrowserModel * model, + { + model->file_icon = file_icon; + model->hide_ext = hide_ext; + }, + false); +} + +void file_browser_start(FileBrowser* browser, FuriString* path) { + furi_assert(browser); + browser->worker = file_browser_worker_alloc( + path, + browser->base_path, + browser->ext_filter, + browser->skip_assets, + browser->hide_dot_files); + file_browser_worker_set_callback_context(browser->worker, browser); + file_browser_worker_set_folder_callback(browser->worker, browser_folder_open_cb); + file_browser_worker_set_list_callback(browser->worker, browser_list_load_cb); + file_browser_worker_set_item_callback(browser->worker, browser_list_item_cb); + file_browser_worker_set_long_load_callback(browser->worker, browser_long_load_cb); +} + +void file_browser_stop(FileBrowser* browser) { + furi_assert(browser); + file_browser_worker_free(browser->worker); + with_view_model( + browser->view, + FileBrowserModel * model, + { + items_array_reset(model->items); + model->item_cnt = 0; + model->item_idx = 0; + model->array_offset = 0; + model->list_offset = 0; + }, + false); +} + +void file_browser_set_callback(FileBrowser* browser, FileBrowserCallback callback, void* context) { + browser->context = context; + browser->callback = callback; +} + +void file_browser_set_item_callback( + FileBrowser* browser, + FileBrowserLoadItemCallback callback, + void* context) { + browser->item_context = context; + browser->item_callback = callback; +} + +static bool browser_is_item_in_array(FileBrowserModel* model, uint32_t idx) { + size_t array_size = items_array_size(model->items); + + if((idx >= (uint32_t)model->array_offset + array_size) || + (idx < (uint32_t)model->array_offset)) { + return false; + } + return true; +} + +static bool browser_is_list_load_required(FileBrowserModel* model) { + size_t array_size = items_array_size(model->items); + if((array_size > 0) && (!model->is_root) && (model->array_offset == 0)) { + array_size--; + } + uint32_t item_cnt = (model->is_root) ? (model->item_cnt) : (model->item_cnt - 1); + + if((model->list_loading) || (array_size >= item_cnt)) { + return false; + } + + if((model->array_offset > 0) && + (model->item_idx < (model->array_offset + ITEM_LIST_LEN_MAX / 4))) { + return true; + } + + if(((model->array_offset + array_size) < item_cnt) && + (model->item_idx > (int32_t)(model->array_offset + array_size - ITEM_LIST_LEN_MAX / 4))) { + return true; + } + + return false; +} + +static void browser_list_rollover(FileBrowserModel* model) { + if(!model->list_loading && items_array_size(model->items) < model->item_cnt) { + items_array_reset(model->items); + } +} + +static void browser_update_offset(FileBrowser* browser) { + furi_assert(browser); + + with_view_model( + browser->view, + FileBrowserModel * model, + { + uint16_t bounds = model->item_cnt > (LIST_ITEMS - 1) ? 2 : model->item_cnt; + + if((model->item_cnt > (LIST_ITEMS - 1)) && + (model->item_idx >= ((int32_t)model->item_cnt - 1))) { + model->list_offset = model->item_idx - (LIST_ITEMS - 1); + } else if(model->list_offset < model->item_idx - bounds) { + model->list_offset = CLAMP( + model->item_idx - (int32_t)(LIST_ITEMS - 2), + (int32_t)model->item_cnt - bounds, + 0); + } else if(model->list_offset > model->item_idx - bounds) { + model->list_offset = + CLAMP(model->item_idx - 1, (int32_t)model->item_cnt - bounds, 0); + } + }, + false); +} + +static void + browser_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root) { + furi_assert(context); + FileBrowser* browser = (FileBrowser*)context; + + int32_t load_offset = 0; + + with_view_model( + browser->view, + FileBrowserModel * model, + { + items_array_reset(model->items); + if(is_root) { + model->item_cnt = item_cnt; + model->item_idx = (file_idx > 0) ? file_idx : 0; + load_offset = + CLAMP(model->item_idx - ITEM_LIST_LEN_MAX / 2, (int32_t)model->item_cnt, 0); + } else { + model->item_cnt = item_cnt + 1; + model->item_idx = file_idx + 1; + load_offset = CLAMP( + model->item_idx - ITEM_LIST_LEN_MAX / 2 - 1, (int32_t)model->item_cnt - 1, 0); + } + model->array_offset = 0; + model->list_offset = 0; + model->is_root = is_root; + model->list_loading = true; + model->folder_loading = false; + }, + true); + browser_update_offset(browser); + + file_browser_worker_load(browser->worker, load_offset, ITEM_LIST_LEN_MAX); +} + +static void browser_list_load_cb(void* context, uint32_t list_load_offset) { + furi_assert(context); + FileBrowser* browser = (FileBrowser*)context; + + BrowserItem_t back_item; + BrowserItem_t_init(&back_item); + back_item.type = BrowserItemTypeBack; + + with_view_model( + browser->view, + FileBrowserModel * model, + { + items_array_reset(model->items); + model->array_offset = list_load_offset; + if(!model->is_root) { + if(list_load_offset == 0) { + items_array_push_back(model->items, back_item); + } else { + model->array_offset += 1; + } + } + }, + false); + + BrowserItem_t_clear(&back_item); +} + +static void + browser_list_item_cb(void* context, FuriString* item_path, bool is_folder, bool is_last) { + furi_assert(context); + FileBrowser* browser = (FileBrowser*)context; + + BrowserItem_t item; + item.custom_icon_data = NULL; + + if(!is_last) { + item.path = furi_string_alloc_set(item_path); + item.display_name = furi_string_alloc(); + if(is_folder) { + item.type = BrowserItemTypeFolder; + } else { + item.type = BrowserItemTypeFile; + if(browser->item_callback) { + item.custom_icon_data = malloc(CUSTOM_ICON_MAX_SIZE); + if(!browser->item_callback( + item_path, + browser->item_context, + &item.custom_icon_data, + item.display_name)) { + free(item.custom_icon_data); + item.custom_icon_data = NULL; + } + } + } + + if(furi_string_empty(item.display_name)) { + path_extract_filename( + item_path, + item.display_name, + (browser->hide_ext) && (item.type == BrowserItemTypeFile)); + } + + // We shouldn't update screen on each item if custom callback is not set + // Otherwise it will cause screen flickering + bool instant_update = (browser->item_callback != NULL); + with_view_model( + browser->view, + FileBrowserModel * model, + { items_array_push_back(model->items, item); }, + instant_update); + + furi_string_free(item.display_name); + furi_string_free(item.path); + if(item.custom_icon_data) { + free(item.custom_icon_data); + } + } else { + with_view_model( + browser->view, + FileBrowserModel * model, + { + model->list_loading = false; + if(browser_is_list_load_required(model)) { + model->list_loading = true; + int32_t load_offset = CLAMP( + model->item_idx - ITEM_LIST_LEN_MAX / 2, (int32_t)model->item_cnt, 0); + file_browser_worker_load(browser->worker, load_offset, ITEM_LIST_LEN_MAX); + } + }, + true); + } +} + +static void browser_long_load_cb(void* context) { + furi_assert(context); + FileBrowser* browser = (FileBrowser*)context; + + with_view_model( + browser->view, FileBrowserModel * model, { model->folder_loading = true; }, true); +} + +static void browser_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box( + canvas, 0, Y_OFFSET + idx * FRAME_HEIGHT, (scrollbar ? 122 : 127), FRAME_HEIGHT); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, 0, Y_OFFSET + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, 1, Y_OFFSET + idx * FRAME_HEIGHT); + canvas_draw_dot(canvas, 0, (Y_OFFSET + idx * FRAME_HEIGHT) + 1); + + canvas_draw_dot(canvas, 0, (Y_OFFSET + idx * FRAME_HEIGHT) + (FRAME_HEIGHT - 1)); + canvas_draw_dot(canvas, scrollbar ? 121 : 126, Y_OFFSET + idx * FRAME_HEIGHT); + canvas_draw_dot( + canvas, scrollbar ? 121 : 126, (Y_OFFSET + idx * FRAME_HEIGHT) + (FRAME_HEIGHT - 1)); +} + +static void browser_draw_loading(Canvas* canvas, FileBrowserModel* model) { + UNUSED(model); + + uint8_t x = 128 / 2 - 24 / 2; + uint8_t y = 64 / 2 - 24 / 2; + + canvas_draw_icon(canvas, x, y, &A_Loading_24); +} + +static void browser_draw_list(Canvas* canvas, FileBrowserModel* model) { + uint32_t array_size = items_array_size(model->items); + bool show_scrollbar = model->item_cnt > LIST_ITEMS; + + FuriString* filename; + filename = furi_string_alloc(); + + for(uint32_t i = 0; i < MIN(model->item_cnt, LIST_ITEMS); i++) { + int32_t idx = CLAMP((uint32_t)(i + model->list_offset), model->item_cnt, 0u); + + BrowserItemType item_type = BrowserItemTypeLoading; + uint8_t* custom_icon_data = NULL; + + if(browser_is_item_in_array(model, idx)) { + BrowserItem_t* item = items_array_get( + model->items, CLAMP(idx - model->array_offset, (int32_t)(array_size - 1), 0)); + item_type = item->type; + furi_string_set(filename, item->display_name); + if(item_type == BrowserItemTypeFile) { + custom_icon_data = item->custom_icon_data; + } + } else { + furi_string_set(filename, "---"); + } + + if(item_type == BrowserItemTypeBack) { + furi_string_set(filename, ". ."); + } + + size_t scroll_counter = model->scroll_counter; + if(model->item_idx == idx) { + browser_draw_frame(canvas, i, show_scrollbar); + if(scroll_counter < SCROLL_DELAY) { + scroll_counter = 0; + } else { + scroll_counter -= SCROLL_DELAY; + } + } else { + canvas_set_color(canvas, ColorBlack); + scroll_counter = 0; + } + + if(custom_icon_data) { //-V547 + // Currently only 10*10 icons are supported + canvas_draw_bitmap( + canvas, 2, Y_OFFSET + 1 + i * FRAME_HEIGHT, 10, 10, custom_icon_data); + } else if((item_type == BrowserItemTypeFile) && (model->file_icon)) { + canvas_draw_icon(canvas, 2, Y_OFFSET + 1 + i * FRAME_HEIGHT, model->file_icon); + } else if(BrowserItemIcons[item_type] != NULL) { + canvas_draw_icon( + canvas, 2, Y_OFFSET + 1 + i * FRAME_HEIGHT, BrowserItemIcons[item_type]); + } + elements_scrollable_text_line( + canvas, + 15, + Y_OFFSET + 9 + i * FRAME_HEIGHT, + (show_scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX), + filename, + scroll_counter, + (model->item_idx != idx)); + } + + if(show_scrollbar) { + elements_scrollbar_pos( + canvas, + 126, + Y_OFFSET, + canvas_height(canvas) - Y_OFFSET, + model->item_idx, + model->item_cnt); + } + + uint32_t folder_item_cnt = (model->is_root) ? (model->item_cnt) : (model->item_cnt - 1); + if(folder_item_cnt == 0) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned( + canvas, + canvas_width(canvas) / 2, + canvas_height(canvas) / 2, + AlignCenter, + AlignCenter, + ""); + } + + furi_string_free(filename); +} + +static void file_browser_view_draw_callback(Canvas* canvas, void* _model) { + FileBrowserModel* model = _model; + + if(model->folder_loading) { + browser_draw_loading(canvas, model); + } else { + browser_draw_list(canvas, model); + } +} + +static bool file_browser_view_input_callback(InputEvent* event, void* context) { + FileBrowser* browser = context; + furi_assert(browser); + bool consumed = false; + bool is_loading = false; + + with_view_model( + browser->view, FileBrowserModel * model, { is_loading = model->folder_loading; }, false); + + if(is_loading) { + return false; + } else if(event->key == InputKeyUp || event->key == InputKeyDown) { + if(event->type == InputTypeShort || event->type == InputTypeRepeat) { + with_view_model( + browser->view, + FileBrowserModel * model, + { + int32_t scroll_speed = 1; + if(model->button_held_for_ticks > 5) { + if(model->button_held_for_ticks % 2) { + scroll_speed = 0; + } else { + scroll_speed = model->button_held_for_ticks > 9 ? 5 : 3; + } + } + + if(event->key == InputKeyUp) { + if(model->item_idx < scroll_speed) { + model->button_held_for_ticks = 0; + model->item_idx = model->item_cnt - 1; + browser_list_rollover(model); + } else { + model->item_idx = + ((model->item_idx - scroll_speed) + model->item_cnt) % + model->item_cnt; + } + + if(browser_is_list_load_required(model)) { + model->list_loading = true; + int32_t load_offset = CLAMP( + model->item_idx - ITEM_LIST_LEN_MAX / 4 * 3, + (int32_t)model->item_cnt, + 0); + file_browser_worker_load( + browser->worker, load_offset, ITEM_LIST_LEN_MAX); + } + model->scroll_counter = 0; + + model->button_held_for_ticks += 1; + } else if(event->key == InputKeyDown) { + if(model->item_idx + scroll_speed >= (int32_t)model->item_cnt) { + model->button_held_for_ticks = 0; + model->item_idx = 0; + browser_list_rollover(model); + } else { + model->item_idx = (model->item_idx + scroll_speed) % model->item_cnt; + } + + if(browser_is_list_load_required(model)) { + model->list_loading = true; + int32_t load_offset = CLAMP( + model->item_idx - ITEM_LIST_LEN_MAX / 4 * 1, + (int32_t)model->item_cnt, + 0); + file_browser_worker_load( + browser->worker, load_offset, ITEM_LIST_LEN_MAX); + } + model->scroll_counter = 0; + + model->button_held_for_ticks += 1; + } + }, + true); + browser_update_offset(browser); + consumed = true; + } else if(event->type == InputTypeRelease) { + with_view_model( + browser->view, + FileBrowserModel * model, + { model->button_held_for_ticks = 0; }, + true); + } + } else if(event->key == InputKeyOk) { + if(event->type == InputTypeShort) { + BrowserItem_t* selected_item = NULL; + int32_t select_index = 0; + with_view_model( + browser->view, + FileBrowserModel * model, + { + if(browser_is_item_in_array(model, model->item_idx)) { + selected_item = + items_array_get(model->items, model->item_idx - model->array_offset); + select_index = model->item_idx; + if((!model->is_root) && (select_index > 0)) { + select_index -= 1; + } + } + }, + false); + + if(selected_item) { + if(selected_item->type == BrowserItemTypeBack) { + file_browser_worker_folder_exit(browser->worker); + } else if(selected_item->type == BrowserItemTypeFolder) { + file_browser_worker_folder_enter( + browser->worker, selected_item->path, select_index); + } else if(selected_item->type == BrowserItemTypeFile) { + furi_string_set(browser->result_path, selected_item->path); + if(browser->callback) { + browser->callback(browser->context); + } + } + } + consumed = true; + } + } else if(event->key == InputKeyLeft) { + if(event->type == InputTypeShort) { + bool is_root = false; + with_view_model( + browser->view, FileBrowserModel * model, { is_root = model->is_root; }, false); + if(!is_root) { + file_browser_worker_folder_exit(browser->worker); + } + consumed = true; + } + } else if(event->key == InputKeyBack) { + if(event->type == InputTypeShort) { + bool is_root = false; + with_view_model( + browser->view, FileBrowserModel * model, { is_root = model->is_root; }, false); + + if(!is_root && !file_browser_worker_is_in_start_folder(browser->worker)) { + consumed = true; + file_browser_worker_folder_exit(browser->worker); + } + } + } + + return consumed; +} diff --git a/applications/services/gui/modules/file_browser.h b/applications/services/gui/modules/file_browser.h new file mode 100644 index 00000000000..879d62c4e21 --- /dev/null +++ b/applications/services/gui/modules/file_browser.h @@ -0,0 +1,51 @@ +/** + * @file file_browser.h + * GUI: FileBrowser view module API + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct FileBrowser FileBrowser; +typedef void (*FileBrowserCallback)(void* context); + +typedef bool (*FileBrowserLoadItemCallback)( + FuriString* path, + void* context, + uint8_t** icon, + FuriString* item_name); + +FileBrowser* file_browser_alloc(FuriString* result_path); + +void file_browser_free(FileBrowser* browser); + +View* file_browser_get_view(FileBrowser* browser); + +void file_browser_configure( + FileBrowser* browser, + const char* extension, + const char* base_path, + bool skip_assets, + bool hide_dot_files, + const Icon* file_icon, + bool hide_ext); + +void file_browser_start(FileBrowser* browser, FuriString* path); + +void file_browser_stop(FileBrowser* browser); + +void file_browser_set_callback(FileBrowser* browser, FileBrowserCallback callback, void* context); + +void file_browser_set_item_callback( + FileBrowser* browser, + FileBrowserLoadItemCallback callback, + void* context); + +#ifdef __cplusplus +} +#endif diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c new file mode 100644 index 00000000000..857acbbaa41 --- /dev/null +++ b/applications/services/gui/modules/file_browser_worker.c @@ -0,0 +1,495 @@ +#include "file_browser_worker.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#define TAG "BrowserWorker" + +#define ASSETS_DIR "assets" +#define BROWSER_ROOT STORAGE_ANY_PATH_PREFIX +#define FILE_NAME_LEN_MAX 256 +#define LONG_LOAD_THRESHOLD 100 + +typedef enum { + WorkerEvtStop = (1 << 0), + WorkerEvtLoad = (1 << 1), + WorkerEvtFolderEnter = (1 << 2), + WorkerEvtFolderExit = (1 << 3), + WorkerEvtFolderRefresh = (1 << 4), + WorkerEvtConfigChange = (1 << 5), +} WorkerEvtFlags; + +#define WORKER_FLAGS_ALL \ + (WorkerEvtStop | WorkerEvtLoad | WorkerEvtFolderEnter | WorkerEvtFolderExit | \ + WorkerEvtFolderRefresh | WorkerEvtConfigChange) + +ARRAY_DEF(idx_last_array, int32_t) + +struct BrowserWorker { + FuriThread* thread; + + FuriString* filter_extension; + FuriString* path_start; + FuriString* path_current; + FuriString* path_next; + int32_t item_sel_idx; + uint32_t load_offset; + uint32_t load_count; + bool skip_assets; + bool hide_dot_files; + idx_last_array_t idx_last; + + void* cb_ctx; + BrowserWorkerFolderOpenCallback folder_cb; + BrowserWorkerListLoadCallback list_load_cb; + BrowserWorkerListItemCallback list_item_cb; + BrowserWorkerLongLoadCallback long_load_cb; +}; + +static bool browser_path_is_file(FuriString* path) { + bool state = false; + 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_is_dir(&file_info)) { + state = true; + } + } + furi_record_close(RECORD_STORAGE); + return state; +} + +static bool browser_path_trim(FuriString* path) { + bool is_root = false; + size_t filename_start = furi_string_search_rchar(path, '/'); + furi_string_left(path, filename_start); + if((furi_string_empty(path)) || (filename_start == FURI_STRING_FAILURE)) { + furi_string_set(path, BROWSER_ROOT); + is_root = true; + } + return is_root; +} + +static bool browser_filter_by_name(BrowserWorker* browser, FuriString* name, bool is_folder) { + // Skip dot files if enabled + if(browser->hide_dot_files) { + if(furi_string_start_with_str(name, ".")) { + return false; + } + } + + if(is_folder) { + // Skip assets folders (if enabled) + if(browser->skip_assets) { + return ((furi_string_cmp_str(name, ASSETS_DIR) == 0) ? (false) : (true)); + } else { + return true; + } + } else { + // Filter files by extension + if((furi_string_empty(browser->filter_extension)) || + (furi_string_cmp_str(browser->filter_extension, "*") == 0)) { + return true; + } + if(furi_string_end_with(name, browser->filter_extension)) { + return true; + } + } + return false; +} + +static bool browser_folder_check_and_switch(FuriString* path) { + FileInfo file_info; + Storage* storage = furi_record_open(RECORD_STORAGE); + bool is_root = false; + + if(furi_string_search_rchar(path, '/') == 0) { + is_root = true; + } + + 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_is_dir(&file_info)) { + break; + } + } + if(is_root) { + break; + } + is_root = browser_path_trim(path); + } + furi_record_close(RECORD_STORAGE); + return is_root; +} + +static bool browser_folder_init( + BrowserWorker* browser, + FuriString* path, + FuriString* filename, + uint32_t* item_cnt, + int32_t* file_idx) { + bool state = false; + FileInfo file_info; + uint32_t total_files_cnt = 0; + + Storage* storage = furi_record_open(RECORD_STORAGE); + File* directory = storage_file_alloc(storage); + + char name_temp[FILE_NAME_LEN_MAX]; + FuriString* name_str; + name_str = furi_string_alloc(); + + *item_cnt = 0; + *file_idx = -1; + + if(storage_dir_open(directory, furi_string_get_cstr(path))) { + state = true; + while(1) { + if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) { + break; + } + 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_is_dir(&file_info))) { + if(!furi_string_empty(filename)) { + if(furi_string_cmp(name_str, filename) == 0) { + *file_idx = *item_cnt; + } + } + (*item_cnt)++; + } + if(total_files_cnt == LONG_LOAD_THRESHOLD) { + // There are too many files in folder and counting them will take some time - send callback to app + if(browser->long_load_cb) { + browser->long_load_cb(browser->cb_ctx); + } + } + } + } + } + + furi_string_free(name_str); + + storage_dir_close(directory); + storage_file_free(directory); + + furi_record_close(RECORD_STORAGE); + + return state; +} + +static bool + browser_folder_load(BrowserWorker* browser, FuriString* path, uint32_t offset, uint32_t count) { + FileInfo file_info; + + Storage* storage = furi_record_open(RECORD_STORAGE); + File* directory = storage_file_alloc(storage); + + char name_temp[FILE_NAME_LEN_MAX]; + FuriString* name_str; + name_str = furi_string_alloc(); + + uint32_t items_cnt = 0; + + do { + if(!storage_dir_open(directory, furi_string_get_cstr(path))) { + break; + } + + items_cnt = 0; + while(items_cnt < offset) { + if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) { + break; + } + 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_is_dir(&file_info))) { + items_cnt++; + } + } else { + break; + } + } + if(items_cnt != offset) { + break; + } + + if(browser->list_load_cb) { + browser->list_load_cb(browser->cb_ctx, offset); + } + + items_cnt = 0; + while(items_cnt < count) { + if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) { + break; + } + 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_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, file_info_is_dir(&file_info), false); + } + items_cnt++; + } + } else { + break; + } + } + if(browser->list_item_cb) { + browser->list_item_cb(browser->cb_ctx, NULL, false, true); + } + } while(0); + + furi_string_free(name_str); + + storage_dir_close(directory); + storage_file_free(directory); + + furi_record_close(RECORD_STORAGE); + + return (items_cnt == count); +} + +static int32_t browser_worker(void* context) { + BrowserWorker* browser = (BrowserWorker*)context; + furi_assert(browser); + FURI_LOG_D(TAG, "Start"); + + uint32_t items_cnt = 0; + FuriString* path; + path = furi_string_alloc_set(BROWSER_ROOT); + browser->item_sel_idx = -1; + + FuriString* filename; + filename = furi_string_alloc(); + + furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtConfigChange); + + while(1) { + uint32_t flags = + furi_thread_flags_wait(WORKER_FLAGS_ALL, FuriFlagWaitAny, FuriWaitForever); + furi_assert((flags & FuriFlagError) == 0); + + if(flags & WorkerEvtConfigChange) { + // If start path is a path to the file - try finding index of this file in a folder + if(browser_path_is_file(browser->path_next)) { + path_extract_filename(browser->path_next, filename, false); + } + idx_last_array_reset(browser->idx_last); + + furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderEnter); + } + + if(flags & WorkerEvtFolderEnter) { + furi_string_set(path, browser->path_next); + bool is_root = browser_folder_check_and_switch(path); + + // Push previous selected item index to history array + idx_last_array_push_back(browser->idx_last, browser->item_sel_idx); + + int32_t file_idx = 0; + browser_folder_init(browser, path, filename, &items_cnt, &file_idx); + furi_string_set(browser->path_current, path); + FURI_LOG_D( + TAG, + "Enter folder: %s items: %lu idx: %ld", + furi_string_get_cstr(path), + items_cnt, + file_idx); + if(browser->folder_cb) { + browser->folder_cb(browser->cb_ctx, items_cnt, file_idx, is_root); + } + furi_string_reset(filename); + } + + if(flags & WorkerEvtFolderExit) { + browser_path_trim(path); + bool is_root = browser_folder_check_and_switch(path); + + int32_t file_idx = 0; + browser_folder_init(browser, path, filename, &items_cnt, &file_idx); + if(idx_last_array_size(browser->idx_last) > 0) { + // Pop previous selected item index from history array + idx_last_array_pop_back(&file_idx, browser->idx_last); + } + furi_string_set(browser->path_current, path); + FURI_LOG_D( + TAG, + "Exit to: %s items: %lu idx: %ld", + furi_string_get_cstr(path), + items_cnt, + file_idx); + if(browser->folder_cb) { + browser->folder_cb(browser->cb_ctx, items_cnt, file_idx, is_root); + } + } + + if(flags & WorkerEvtFolderRefresh) { + bool is_root = browser_folder_check_and_switch(path); + + int32_t file_idx = 0; + furi_string_reset(filename); + browser_folder_init(browser, path, filename, &items_cnt, &file_idx); + FURI_LOG_D( + TAG, + "Refresh folder: %s items: %lu idx: %ld", + furi_string_get_cstr(path), + items_cnt, + browser->item_sel_idx); + if(browser->folder_cb) { + browser->folder_cb(browser->cb_ctx, items_cnt, browser->item_sel_idx, is_root); + } + } + + if(flags & WorkerEvtLoad) { + FURI_LOG_D( + TAG, "Load offset: %lu cnt: %lu", browser->load_offset, browser->load_count); + browser_folder_load(browser, path, browser->load_offset, browser->load_count); + } + + if(flags & WorkerEvtStop) { + break; + } + } + + furi_string_free(filename); + furi_string_free(path); + + FURI_LOG_D(TAG, "End"); + return 0; +} + +BrowserWorker* file_browser_worker_alloc( + FuriString* path, + const char* base_path, + const char* filter_ext, + bool skip_assets, + bool hide_dot_files) { + BrowserWorker* browser = malloc(sizeof(BrowserWorker)); + + idx_last_array_init(browser->idx_last); + + browser->filter_extension = furi_string_alloc_set(filter_ext); + browser->skip_assets = skip_assets; + browser->hide_dot_files = hide_dot_files; + + browser->path_current = furi_string_alloc_set(path); + browser->path_next = furi_string_alloc_set(path); + + browser->path_start = furi_string_alloc(); + if(base_path) { + furi_string_set_str(browser->path_start, base_path); + } + + browser->thread = furi_thread_alloc_ex("BrowserWorker", 2048, browser_worker, browser); + furi_thread_start(browser->thread); + + return browser; +} //-V773 + +void file_browser_worker_free(BrowserWorker* browser) { + furi_assert(browser); + + furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtStop); + furi_thread_join(browser->thread); + furi_thread_free(browser->thread); + + furi_string_free(browser->filter_extension); + furi_string_free(browser->path_next); + furi_string_free(browser->path_current); + furi_string_free(browser->path_start); + + idx_last_array_clear(browser->idx_last); + + free(browser); +} + +void file_browser_worker_set_callback_context(BrowserWorker* browser, void* context) { + furi_assert(browser); + browser->cb_ctx = context; +} + +void file_browser_worker_set_folder_callback( + BrowserWorker* browser, + BrowserWorkerFolderOpenCallback cb) { + furi_assert(browser); + browser->folder_cb = cb; +} + +void file_browser_worker_set_list_callback( + BrowserWorker* browser, + BrowserWorkerListLoadCallback cb) { + furi_assert(browser); + browser->list_load_cb = cb; +} + +void file_browser_worker_set_item_callback( + BrowserWorker* browser, + BrowserWorkerListItemCallback cb) { + furi_assert(browser); + browser->list_item_cb = cb; +} + +void file_browser_worker_set_long_load_callback( + BrowserWorker* browser, + BrowserWorkerLongLoadCallback cb) { + furi_assert(browser); + browser->long_load_cb = cb; +} + +void file_browser_worker_set_config( + BrowserWorker* browser, + FuriString* path, + const char* filter_ext, + bool skip_assets, + bool hide_dot_files) { + furi_assert(browser); + furi_string_set(browser->path_next, path); + furi_string_set(browser->filter_extension, filter_ext); + browser->skip_assets = skip_assets; + browser->hide_dot_files = hide_dot_files; + furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtConfigChange); +} + +void file_browser_worker_folder_enter(BrowserWorker* browser, FuriString* path, int32_t item_idx) { + furi_assert(browser); + furi_string_set(browser->path_next, path); + browser->item_sel_idx = item_idx; + furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderEnter); +} + +bool file_browser_worker_is_in_start_folder(BrowserWorker* browser) { + furi_assert(browser); + return (furi_string_cmp(browser->path_start, browser->path_current) == 0); +} + +void file_browser_worker_folder_exit(BrowserWorker* browser) { + furi_assert(browser); + furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderExit); +} + +void file_browser_worker_folder_refresh(BrowserWorker* browser, int32_t item_idx) { + furi_assert(browser); + browser->item_sel_idx = item_idx; + furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderRefresh); +} + +void file_browser_worker_load(BrowserWorker* browser, uint32_t offset, uint32_t count) { + furi_assert(browser); + browser->load_offset = offset; + browser->load_count = count; + furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtLoad); +} diff --git a/applications/gui/modules/file_browser_worker.h b/applications/services/gui/modules/file_browser_worker.h similarity index 77% rename from applications/gui/modules/file_browser_worker.h rename to applications/services/gui/modules/file_browser_worker.h index 18c0b48174c..3b4be6aa77b 100644 --- a/applications/gui/modules/file_browser_worker.h +++ b/applications/services/gui/modules/file_browser_worker.h @@ -1,6 +1,5 @@ #pragma once -#include "m-string.h" #include #include @@ -17,12 +16,17 @@ typedef void (*BrowserWorkerFolderOpenCallback)( typedef void (*BrowserWorkerListLoadCallback)(void* context, uint32_t list_load_offset); typedef void (*BrowserWorkerListItemCallback)( void* context, - string_t item_path, + FuriString* item_path, bool is_folder, bool is_last); typedef void (*BrowserWorkerLongLoadCallback)(void* context); -BrowserWorker* file_browser_worker_alloc(string_t path, const char* filter_ext, bool skip_assets); +BrowserWorker* file_browser_worker_alloc( + FuriString* path, + const char* base_path, + const char* filter_ext, + bool skip_assets, + bool hide_dot_files); void file_browser_worker_free(BrowserWorker* browser); @@ -46,11 +50,14 @@ void file_browser_worker_set_long_load_callback( void file_browser_worker_set_config( BrowserWorker* browser, - string_t path, + FuriString* path, const char* filter_ext, - bool skip_assets); + bool skip_assets, + bool hide_dot_files); + +void file_browser_worker_folder_enter(BrowserWorker* browser, FuriString* path, int32_t item_idx); -void file_browser_worker_folder_enter(BrowserWorker* browser, string_t path, int32_t item_idx); +bool file_browser_worker_is_in_start_folder(BrowserWorker* browser); void file_browser_worker_folder_exit(BrowserWorker* browser); diff --git a/applications/gui/modules/loading.c b/applications/services/gui/modules/loading.c similarity index 99% rename from applications/gui/modules/loading.c rename to applications/services/gui/modules/loading.c index 0fa2c128635..e64aeb78fb1 100644 --- a/applications/gui/modules/loading.c +++ b/applications/services/gui/modules/loading.c @@ -1,13 +1,14 @@ -#include -#include -#include +#include "loading.h" + #include #include #include #include #include -#include "loading.h" +#include +#include +#include struct Loading { View* view; diff --git a/applications/gui/modules/loading.h b/applications/services/gui/modules/loading.h similarity index 100% rename from applications/gui/modules/loading.h rename to applications/services/gui/modules/loading.h diff --git a/applications/gui/modules/menu.c b/applications/services/gui/modules/menu.c similarity index 87% rename from applications/gui/modules/menu.c rename to applications/services/gui/modules/menu.c index 67d46d5f3b1..afae8b8fa24 100644 --- a/applications/gui/modules/menu.c +++ b/applications/services/gui/modules/menu.c @@ -1,8 +1,9 @@ #include "menu.h" -#include #include +#include #include +#include struct Menu { View* view; @@ -103,25 +104,29 @@ static bool menu_input_callback(InputEvent* event, void* context) { static void menu_enter(void* context) { Menu* menu = context; with_view_model( - menu->view, (MenuModel * model) { + menu->view, + MenuModel * model, + { MenuItem* item = MenuItemArray_get(model->items, model->position); if(item && item->icon) { icon_animation_start(item->icon); } - return false; - }); + }, + false); } static void menu_exit(void* context) { Menu* menu = context; with_view_model( - menu->view, (MenuModel * model) { + menu->view, + MenuModel * model, + { MenuItem* item = MenuItemArray_get(model->items, model->position); if(item && item->icon) { icon_animation_stop(item->icon); } - return false; - }); + }, + false); } Menu* menu_alloc() { @@ -135,11 +140,13 @@ Menu* menu_alloc() { view_set_exit_callback(menu->view, menu_exit); with_view_model( - menu->view, (MenuModel * model) { + menu->view, + MenuModel * model, + { MenuItemArray_init(model->items); model->position = 0; - return true; - }); + }, + true); return menu; } @@ -147,6 +154,8 @@ Menu* menu_alloc() { void menu_free(Menu* menu) { furi_assert(menu); menu_reset(menu); + with_view_model( + menu->view, MenuModel * model, { MenuItemArray_clear(model->items); }, false); view_free(menu->view); free(menu); } @@ -168,7 +177,9 @@ void menu_add_item( MenuItem* item = NULL; with_view_model( - menu->view, (MenuModel * model) { + menu->view, + MenuModel * model, + { item = MenuItemArray_push_new(model->items); item->label = label; item->icon = icon ? icon_animation_alloc(icon) : icon_animation_alloc(&A_Plugins_14); @@ -176,14 +187,16 @@ void menu_add_item( item->index = index; item->callback = callback; item->callback_context = context; - return true; - }); + }, + true); } void menu_reset(Menu* menu) { furi_assert(menu); with_view_model( - menu->view, (MenuModel * model) { + menu->view, + MenuModel * model, + { for M_EACH(item, model->items, MenuItemArray_t) { icon_animation_stop(item->icon); @@ -192,25 +205,27 @@ void menu_reset(Menu* menu) { MenuItemArray_reset(model->items); model->position = 0; - return true; - }); + }, + true); } void menu_set_selected_item(Menu* menu, uint32_t index) { with_view_model( - menu->view, (MenuModel * model) { - if(index >= MenuItemArray_size(model->items)) { - return false; + menu->view, + MenuModel * model, + { + if(index < MenuItemArray_size(model->items)) { + model->position = index; } - - model->position = index; - return true; - }); + }, + true); } static void menu_process_up(Menu* menu) { with_view_model( - menu->view, (MenuModel * model) { + menu->view, + MenuModel * model, + { MenuItem* item = MenuItemArray_get(model->items, model->position); if(item && item->icon) { icon_animation_stop(item->icon); @@ -226,13 +241,15 @@ static void menu_process_up(Menu* menu) { if(item && item->icon) { icon_animation_start(item->icon); } - return true; - }); + }, + true); } static void menu_process_down(Menu* menu) { with_view_model( - menu->view, (MenuModel * model) { + menu->view, + MenuModel * model, + { MenuItem* item = MenuItemArray_get(model->items, model->position); if(item && item->icon) { icon_animation_stop(item->icon); @@ -248,19 +265,21 @@ static void menu_process_down(Menu* menu) { if(item && item->icon) { icon_animation_start(item->icon); } - return true; - }); + }, + true); } static void menu_process_ok(Menu* menu) { MenuItem* item = NULL; with_view_model( - menu->view, (MenuModel * model) { + menu->view, + MenuModel * model, + { if(model->position < MenuItemArray_size(model->items)) { item = MenuItemArray_get(model->items, model->position); } - return true; - }); + }, + true); if(item && item->callback) { item->callback(item->callback_context, item->index); } diff --git a/applications/gui/modules/menu.h b/applications/services/gui/modules/menu.h old mode 100755 new mode 100644 similarity index 100% rename from applications/gui/modules/menu.h rename to applications/services/gui/modules/menu.h diff --git a/applications/gui/modules/popup.c b/applications/services/gui/modules/popup.c similarity index 91% rename from applications/gui/modules/popup.c rename to applications/services/gui/modules/popup.c index b3cb5e53692..520efceef96 100644 --- a/applications/gui/modules/popup.c +++ b/applications/services/gui/modules/popup.c @@ -98,7 +98,7 @@ void popup_start_timer(void* context) { if(timer_period == 0) timer_period = 1; if(furi_timer_start(popup->timer, timer_period) != FuriStatusOk) { - furi_assert(0); + furi_crash(); }; } } @@ -117,14 +117,16 @@ Popup* popup_alloc() { popup->timer_enabled = false; view_set_context(popup->view, popup); - view_allocate_model(popup->view, ViewModelTypeLockFree, sizeof(PopupModel)); + view_allocate_model(popup->view, ViewModelTypeLocking, sizeof(PopupModel)); view_set_draw_callback(popup->view, popup_view_draw_callback); view_set_input_callback(popup->view, popup_view_input_callback); view_set_enter_callback(popup->view, popup_start_timer); view_set_exit_callback(popup->view, popup_stop_timer); with_view_model( - popup->view, (PopupModel * model) { + popup->view, + PopupModel * model, + { model->header.text = NULL; model->header.x = 0; model->header.y = 0; @@ -140,8 +142,8 @@ Popup* popup_alloc() { model->icon.x = 0; model->icon.y = 0; model->icon.icon = NULL; - return true; - }); + }, + true); return popup; } @@ -176,14 +178,16 @@ void popup_set_header( Align vertical) { furi_assert(popup); with_view_model( - popup->view, (PopupModel * model) { + popup->view, + PopupModel * model, + { model->header.text = text; model->header.x = x; model->header.y = y; model->header.horizontal = horizontal; model->header.vertical = vertical; - return true; - }); + }, + true); } void popup_set_text( @@ -195,25 +199,29 @@ void popup_set_text( Align vertical) { furi_assert(popup); with_view_model( - popup->view, (PopupModel * model) { + popup->view, + PopupModel * model, + { model->text.text = text; model->text.x = x; model->text.y = y; model->text.horizontal = horizontal; model->text.vertical = vertical; - return true; - }); + }, + true); } void popup_set_icon(Popup* popup, uint8_t x, uint8_t y, const Icon* icon) { furi_assert(popup); with_view_model( - popup->view, (PopupModel * model) { + popup->view, + PopupModel * model, + { model->icon.x = x; model->icon.y = y; model->icon.icon = icon; - return true; - }); + }, + true); } void popup_set_timeout(Popup* popup, uint32_t timeout_in_ms) { @@ -233,12 +241,14 @@ void popup_reset(Popup* popup) { furi_assert(popup); with_view_model( - popup->view, (PopupModel * model) { + popup->view, + PopupModel * model, + { memset(&model->header, 0, sizeof(model->header)); memset(&model->text, 0, sizeof(model->text)); memset(&model->icon, 0, sizeof(model->icon)); - return false; - }); + }, + false); popup->callback = NULL; popup->context = NULL; popup->timer_enabled = false; diff --git a/applications/gui/modules/popup.h b/applications/services/gui/modules/popup.h similarity index 96% rename from applications/gui/modules/popup.h rename to applications/services/gui/modules/popup.h index 94f49a2ba67..13371a05d4a 100644 --- a/applications/gui/modules/popup.h +++ b/applications/services/gui/modules/popup.h @@ -64,7 +64,7 @@ void popup_set_context(Popup* popup, void* context); * @param x x position * @param y y position * @param horizontal horizontal alignment - * @param vertical vertical aligment + * @param vertical vertical alignment */ void popup_set_header( Popup* popup, @@ -83,7 +83,7 @@ void popup_set_header( * @param x x position * @param y y position * @param horizontal horizontal alignment - * @param vertical vertical aligment + * @param vertical vertical alignment */ void popup_set_text( Popup* popup, diff --git a/applications/services/gui/modules/submenu.c b/applications/services/gui/modules/submenu.c new file mode 100644 index 00000000000..3ba35edde07 --- /dev/null +++ b/applications/services/gui/modules/submenu.c @@ -0,0 +1,353 @@ +#include "submenu.h" + +#include +#include +#include + +struct Submenu { + View* view; +}; + +typedef struct { + FuriString* label; + uint32_t index; + SubmenuItemCallback callback; + void* callback_context; +} SubmenuItem; + +static void SubmenuItem_init(SubmenuItem* item) { + item->label = furi_string_alloc(); + item->index = 0; + item->callback = NULL; + item->callback_context = NULL; +} + +static void SubmenuItem_init_set(SubmenuItem* item, const SubmenuItem* src) { + item->label = furi_string_alloc_set(src->label); + item->index = src->index; + item->callback = src->callback; + item->callback_context = src->callback_context; +} + +static void SubmenuItem_set(SubmenuItem* item, const SubmenuItem* src) { + furi_string_set(item->label, src->label); + item->index = src->index; + item->callback = src->callback; + item->callback_context = src->callback_context; +} + +static void SubmenuItem_clear(SubmenuItem* item) { + furi_string_free(item->label); +} + +ARRAY_DEF( + SubmenuItemArray, + SubmenuItem, + (INIT(API_2(SubmenuItem_init)), + SET(API_6(SubmenuItem_set)), + INIT_SET(API_6(SubmenuItem_init_set)), + CLEAR(API_2(SubmenuItem_clear)))) + +typedef struct { + SubmenuItemArray_t items; + FuriString* header; + size_t position; + size_t window_position; +} SubmenuModel; + +static void submenu_process_up(Submenu* submenu); +static void submenu_process_down(Submenu* submenu); +static void submenu_process_ok(Submenu* submenu); + +static void submenu_view_draw_callback(Canvas* canvas, void* _model) { + SubmenuModel* model = _model; + + const uint8_t item_height = 16; + uint8_t item_width = canvas_width(canvas) - 5; + + canvas_clear(canvas); + + if(!furi_string_empty(model->header)) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 4, 11, furi_string_get_cstr(model->header)); + } + + canvas_set_font(canvas, FontSecondary); + + size_t position = 0; + SubmenuItemArray_it_t it; + for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it); + SubmenuItemArray_next(it)) { + const size_t item_position = position - model->window_position; + const size_t items_on_screen = furi_string_empty(model->header) ? 4 : 3; + uint8_t y_offset = furi_string_empty(model->header) ? 0 : 16; + + if(item_position < items_on_screen) { + if(position == model->position) { + canvas_set_color(canvas, ColorBlack); + elements_slightly_rounded_box( + canvas, + 0, + y_offset + (item_position * item_height) + 1, + item_width, + item_height - 2); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_set_color(canvas, ColorBlack); + } + + FuriString* disp_str; + disp_str = furi_string_alloc_set(SubmenuItemArray_cref(it)->label); + elements_string_fit_width(canvas, disp_str, item_width - (6 * 2)); + + canvas_draw_str( + canvas, + 6, + y_offset + (item_position * item_height) + item_height - 4, + furi_string_get_cstr(disp_str)); + + furi_string_free(disp_str); + } + + position++; + } + + elements_scrollbar(canvas, model->position, SubmenuItemArray_size(model->items)); +} + +static bool submenu_view_input_callback(InputEvent* event, void* context) { + Submenu* submenu = context; + furi_assert(submenu); + bool consumed = false; + + if(event->type == InputTypeShort) { + switch(event->key) { + case InputKeyUp: + consumed = true; + submenu_process_up(submenu); + break; + case InputKeyDown: + consumed = true; + submenu_process_down(submenu); + break; + case InputKeyOk: + consumed = true; + submenu_process_ok(submenu); + break; + default: + break; + } + } else if(event->type == InputTypeRepeat) { + if(event->key == InputKeyUp) { + consumed = true; + submenu_process_up(submenu); + } else if(event->key == InputKeyDown) { + consumed = true; + submenu_process_down(submenu); + } + } + + return consumed; +} + +Submenu* submenu_alloc() { + Submenu* submenu = malloc(sizeof(Submenu)); + submenu->view = view_alloc(); + view_set_context(submenu->view, submenu); + view_allocate_model(submenu->view, ViewModelTypeLocking, sizeof(SubmenuModel)); + view_set_draw_callback(submenu->view, submenu_view_draw_callback); + view_set_input_callback(submenu->view, submenu_view_input_callback); + + with_view_model( + submenu->view, + SubmenuModel * model, + { + SubmenuItemArray_init(model->items); + model->position = 0; + model->window_position = 0; + model->header = furi_string_alloc(); + }, + true); + + return submenu; +} + +void submenu_free(Submenu* submenu) { + furi_assert(submenu); + + with_view_model( + submenu->view, + SubmenuModel * model, + { + furi_string_free(model->header); + SubmenuItemArray_clear(model->items); + }, + true); + view_free(submenu->view); + free(submenu); +} + +View* submenu_get_view(Submenu* submenu) { + furi_assert(submenu); + return submenu->view; +} + +void submenu_add_item( + Submenu* submenu, + const char* label, + uint32_t index, + SubmenuItemCallback callback, + void* callback_context) { + SubmenuItem* item = NULL; + furi_assert(label); + furi_assert(submenu); + + with_view_model( + submenu->view, + SubmenuModel * model, + { + item = SubmenuItemArray_push_new(model->items); + furi_string_set_str(item->label, label); + item->index = index; + item->callback = callback; + item->callback_context = callback_context; + }, + true); +} + +void submenu_reset(Submenu* submenu) { + furi_assert(submenu); + + with_view_model( + submenu->view, + SubmenuModel * model, + { + SubmenuItemArray_reset(model->items); + model->position = 0; + model->window_position = 0; + furi_string_reset(model->header); + }, + true); +} + +void submenu_set_selected_item(Submenu* submenu, uint32_t index) { + with_view_model( + submenu->view, + SubmenuModel * model, + { + size_t position = 0; + SubmenuItemArray_it_t it; + for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it); + SubmenuItemArray_next(it)) { + if(index == SubmenuItemArray_cref(it)->index) { + break; + } + position++; + } + + const size_t items_size = SubmenuItemArray_size(model->items); + + if(position >= items_size) { + position = 0; + } + + model->position = position; + model->window_position = position; + + if(model->window_position > 0) { + model->window_position -= 1; + } + + const size_t items_on_screen = furi_string_empty(model->header) ? 4 : 3; + + if(items_size <= items_on_screen) { + model->window_position = 0; + } else { + const size_t pos = items_size - items_on_screen; + if(model->window_position > pos) { + model->window_position = pos; + } + } + }, + true); +} + +void submenu_process_up(Submenu* submenu) { + with_view_model( + submenu->view, + SubmenuModel * model, + { + const size_t items_on_screen = furi_string_empty(model->header) ? 4 : 3; + const size_t items_size = SubmenuItemArray_size(model->items); + + if(model->position > 0) { + model->position--; + if((model->position == model->window_position) && (model->window_position > 0)) { + model->window_position--; + } + } else { + model->position = items_size - 1; + if(model->position > items_on_screen - 1) { + model->window_position = model->position - (items_on_screen - 1); + } + } + }, + true); +} + +void submenu_process_down(Submenu* submenu) { + with_view_model( + submenu->view, + SubmenuModel * model, + { + const size_t items_on_screen = furi_string_empty(model->header) ? 4 : 3; + const size_t items_size = SubmenuItemArray_size(model->items); + + if(model->position < items_size - 1) { + model->position++; + if((model->position - model->window_position > items_on_screen - 2) && + (model->window_position < items_size - items_on_screen)) { + model->window_position++; + } + } else { + model->position = 0; + model->window_position = 0; + } + }, + true); +} + +void submenu_process_ok(Submenu* submenu) { + SubmenuItem* item = NULL; + + with_view_model( + submenu->view, + SubmenuModel * model, + { + const size_t items_size = SubmenuItemArray_size(model->items); + if(model->position < items_size) { + item = SubmenuItemArray_get(model->items, model->position); + } + }, + true); + + if(item && item->callback) { + item->callback(item->callback_context, item->index); + } +} + +void submenu_set_header(Submenu* submenu, const char* header) { + furi_assert(submenu); + + with_view_model( + submenu->view, + SubmenuModel * model, + { + if(header == NULL) { + furi_string_reset(model->header); + } else { + furi_string_set_str(model->header, header); + } + }, + true); +} diff --git a/applications/gui/modules/submenu.h b/applications/services/gui/modules/submenu.h similarity index 100% rename from applications/gui/modules/submenu.h rename to applications/services/gui/modules/submenu.h diff --git a/applications/services/gui/modules/text_box.c b/applications/services/gui/modules/text_box.c new file mode 100644 index 00000000000..0e4aae9446b --- /dev/null +++ b/applications/services/gui/modules/text_box.c @@ -0,0 +1,246 @@ +#include "text_box.h" +#include +#include +#include +#include + +struct TextBox { + View* view; + + uint16_t button_held_for_ticks; +}; + +typedef struct { + const char* text; + char* text_pos; + FuriString* text_formatted; + int32_t scroll_pos; + int32_t scroll_num; + TextBoxFont font; + TextBoxFocus focus; + bool formatted; +} TextBoxModel; + +static void text_box_process_down(TextBox* text_box, uint8_t lines) { + with_view_model( + text_box->view, + TextBoxModel * model, + { + if(model->scroll_pos < model->scroll_num - lines) { + model->scroll_pos += lines; + for(uint8_t i = 0; i < lines; i++) { + // Search next line start + while(*model->text_pos++ != '\n') + ; + } + } else if(lines > 1) { + lines = model->scroll_num - model->scroll_pos - 1; + model->scroll_pos = model->scroll_num - 1; + for(uint8_t i = 0; i < lines; i++) { + // Search next line start + while(*model->text_pos++ != '\n') + ; + } + } + }, + true); +} + +static void text_box_process_up(TextBox* text_box, uint8_t lines) { + with_view_model( + text_box->view, + TextBoxModel * model, + { + if(model->scroll_pos > lines - 1) { + model->scroll_pos -= lines; + for(uint8_t i = 0; i < lines; i++) { + // Reach last symbol of previous line + model->text_pos--; + // Search previous line start + while((model->text_pos != model->text) && (*(--model->text_pos) != '\n')) + ; + if(*model->text_pos == '\n') { + model->text_pos++; + } + } + } else if(lines > 1) { + lines = model->scroll_pos; + model->scroll_pos = 0; + model->text_pos = (char*)model->text; + } + }, + true); +} + +static void text_box_insert_endline(Canvas* canvas, TextBoxModel* model) { + size_t i = 0; + size_t line_width = 0; + const char* str = model->text; + size_t line_num = 0; + + const size_t text_width = 120; + + while(str[i] != '\0') { + char symb = str[i++]; + if(symb != '\n') { + size_t glyph_width = canvas_glyph_width(canvas, symb); + if(line_width + glyph_width > text_width) { + line_num++; + line_width = 0; + furi_string_push_back(model->text_formatted, '\n'); + } + line_width += glyph_width; + } else { + line_num++; + line_width = 0; + } + furi_string_push_back(model->text_formatted, symb); + } + line_num++; + model->text = furi_string_get_cstr(model->text_formatted); + model->text_pos = (char*)model->text; + if(model->focus == TextBoxFocusEnd && line_num > 5) { + // Set text position to 5th line from the end + for(uint8_t i = 0; i < line_num - 5; i++) { + while(*model->text_pos++ != '\n') { + }; + } + model->scroll_num = line_num - 4; + model->scroll_pos = line_num - 5; + } else { + model->scroll_num = MAX(line_num - 4, 0u); + model->scroll_pos = 0; + } +} + +static void text_box_view_draw_callback(Canvas* canvas, void* _model) { + TextBoxModel* model = _model; + + canvas_clear(canvas); + if(model->font == TextBoxFontText) { + canvas_set_font(canvas, FontSecondary); + } else if(model->font == TextBoxFontHex) { + canvas_set_font(canvas, FontKeyboard); + } + + if(!model->formatted) { + text_box_insert_endline(canvas, model); + model->formatted = true; + } + + elements_slightly_rounded_frame(canvas, 0, 0, 124, 64); + elements_multiline_text(canvas, 3, 11, model->text_pos); + elements_scrollbar(canvas, model->scroll_pos, model->scroll_num); +} + +static bool text_box_view_input_callback(InputEvent* event, void* context) { + furi_assert(context); + + TextBox* text_box = context; + bool consumed = false; + if(event->type == InputTypeShort || event->type == InputTypeRepeat) { + int32_t scroll_speed = 1; + if(text_box->button_held_for_ticks > 5) { + if(text_box->button_held_for_ticks % 2) { + scroll_speed = 0; + } else { + scroll_speed = text_box->button_held_for_ticks > 9 ? 5 : 3; + } + } + + if(event->key == InputKeyDown) { + text_box_process_down(text_box, scroll_speed); + consumed = true; + } else if(event->key == InputKeyUp) { + text_box_process_up(text_box, scroll_speed); + consumed = true; + } + + text_box->button_held_for_ticks++; + } else if(event->type == InputTypeRelease) { + text_box->button_held_for_ticks = 0; + consumed = true; + } + return consumed; +} + +TextBox* text_box_alloc() { + TextBox* text_box = malloc(sizeof(TextBox)); + text_box->view = view_alloc(); + view_set_context(text_box->view, text_box); + view_allocate_model(text_box->view, ViewModelTypeLocking, sizeof(TextBoxModel)); + view_set_draw_callback(text_box->view, text_box_view_draw_callback); + view_set_input_callback(text_box->view, text_box_view_input_callback); + + with_view_model( + text_box->view, + TextBoxModel * model, + { + model->text = NULL; + model->text_formatted = furi_string_alloc_set(""); + model->formatted = false; + model->font = TextBoxFontText; + }, + true); + + return text_box; +} + +void text_box_free(TextBox* text_box) { + furi_assert(text_box); + + with_view_model( + text_box->view, TextBoxModel * model, { furi_string_free(model->text_formatted); }, true); + view_free(text_box->view); + free(text_box); +} + +View* text_box_get_view(TextBox* text_box) { + furi_assert(text_box); + return text_box->view; +} + +void text_box_reset(TextBox* text_box) { + furi_assert(text_box); + + with_view_model( + text_box->view, + TextBoxModel * model, + { + model->text = NULL; + furi_string_set(model->text_formatted, ""); + model->font = TextBoxFontText; + model->focus = TextBoxFocusStart; + }, + true); +} + +void text_box_set_text(TextBox* text_box, const char* text) { + furi_assert(text_box); + furi_assert(text); + + with_view_model( + text_box->view, + TextBoxModel * model, + { + model->text = text; + furi_string_reset(model->text_formatted); + furi_string_reserve(model->text_formatted, strlen(text)); + model->formatted = false; + }, + true); +} + +void text_box_set_font(TextBox* text_box, TextBoxFont font) { + furi_assert(text_box); + + with_view_model( + text_box->view, TextBoxModel * model, { model->font = font; }, true); +} + +void text_box_set_focus(TextBox* text_box, TextBoxFocus focus) { + furi_assert(text_box); + + with_view_model( + text_box->view, TextBoxModel * model, { model->focus = focus; }, true); +} diff --git a/applications/gui/modules/text_box.h b/applications/services/gui/modules/text_box.h old mode 100755 new mode 100644 similarity index 100% rename from applications/gui/modules/text_box.h rename to applications/services/gui/modules/text_box.h diff --git a/applications/gui/modules/text_input.c b/applications/services/gui/modules/text_input.c similarity index 85% rename from applications/gui/modules/text_input.c rename to applications/services/gui/modules/text_input.c index 26f74e7aca8..50453cf22c4 100644 --- a/applications/gui/modules/text_input.c +++ b/applications/services/gui/modules/text_input.c @@ -1,5 +1,6 @@ #include "text_input.h" #include +#include #include struct TextInput { @@ -27,8 +28,8 @@ typedef struct { TextInputValidatorCallback validator_callback; void* validator_callback_context; - string_t validator_text; - bool valadator_message_visible; + FuriString* validator_text; + bool validator_message_visible; } TextInputModel; static const uint8_t keyboard_origin_x = 1; @@ -91,14 +92,16 @@ static uint8_t get_row_size(uint8_t row_index) { switch(row_index + 1) { case 1: - row_size = sizeof(keyboard_keys_row_1) / sizeof(TextInputKey); + row_size = COUNT_OF(keyboard_keys_row_1); break; case 2: - row_size = sizeof(keyboard_keys_row_2) / sizeof(TextInputKey); + row_size = COUNT_OF(keyboard_keys_row_2); break; case 3: - row_size = sizeof(keyboard_keys_row_3) / sizeof(TextInputKey); + row_size = COUNT_OF(keyboard_keys_row_3); break; + default: + furi_crash(); } return row_size; @@ -117,6 +120,8 @@ static const TextInputKey* get_row(uint8_t row_index) { case 3: row = keyboard_keys_row_3; break; + default: + furi_crash(); } return row; @@ -133,7 +138,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(islower(letter)) { return (letter - 0x20); } else { return letter; @@ -183,7 +188,7 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontKeyboard); - for(uint8_t row = 0; row <= keyboard_row_count; row++) { + for(uint8_t row = 0; row < keyboard_row_count; row++) { const uint8_t column_count = get_row_size(row); const TextInputKey* keys = get_row(row); @@ -232,7 +237,8 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { canvas_set_color(canvas, ColorBlack); } - if(text_length == 0 && char_is_lowercase(keys[column].text)) { + if(model->clear_default_text || + (text_length == 0 && char_is_lowercase(keys[column].text))) { canvas_draw_glyph( canvas, keyboard_origin_x + keys[column].x, @@ -248,7 +254,7 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { } } } - if(model->valadator_message_visible) { + if(model->validator_message_visible) { canvas_set_font(canvas, FontSecondary); canvas_set_color(canvas, ColorWhite); canvas_draw_box(canvas, 8, 10, 110, 48); @@ -256,7 +262,7 @@ static void text_input_view_draw_callback(Canvas* canvas, void* _model) { 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, string_get_cstr(model->validator_text)); + elements_multiline_text(canvas, 62, 20, furi_string_get_cstr(model->validator_text)); canvas_set_font(canvas, FontKeyboard); } } @@ -301,9 +307,11 @@ static void text_input_handle_right(TextInput* text_input, TextInputModel* model static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, bool shift) { char selected = get_selected_char(model); - uint8_t text_length = strlen(model->text_buffer); + size_t text_length = strlen(model->text_buffer); - if(shift) { + bool toggle_case = text_length == 0 || model->clear_default_text; + if(shift) toggle_case = !toggle_case; + if(toggle_case) { selected = char_to_uppercase(selected); } @@ -311,22 +319,21 @@ static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, b if(model->validator_callback && (!model->validator_callback( model->text_buffer, model->validator_text, model->validator_callback_context))) { - model->valadator_message_visible = true; + model->validator_message_visible = true; furi_timer_start(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) { text_input_backspace_cb(model); - } else if(text_length < (model->text_buffer_size - 1)) { + } else { if(model->clear_default_text) { text_length = 0; } - if(text_length == 0 && char_is_lowercase(selected)) { - selected = char_to_uppercase(selected); + if(text_length < (model->text_buffer_size - 1)) { + 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; } @@ -341,8 +348,8 @@ static bool text_input_view_input_callback(InputEvent* event, void* context) { TextInputModel* model = view_get_model(text_input->view); if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && - model->valadator_message_visible) { - model->valadator_message_visible = false; + model->validator_message_visible) { + model->validator_message_visible = false; consumed = true; } else if(event->type == InputTypeShort) { consumed = true; @@ -426,10 +433,10 @@ void text_input_timer_callback(void* context) { TextInput* text_input = context; with_view_model( - text_input->view, (TextInputModel * model) { - model->valadator_message_visible = false; - return true; - }); + text_input->view, + TextInputModel * model, + { model->validator_message_visible = false; }, + true); } TextInput* text_input_alloc() { @@ -443,10 +450,10 @@ TextInput* text_input_alloc() { text_input->timer = furi_timer_alloc(text_input_timer_callback, FuriTimerTypeOnce, text_input); with_view_model( - text_input->view, (TextInputModel * model) { - string_init(model->validator_text); - return false; - }); + text_input->view, + TextInputModel * model, + { model->validator_text = furi_string_alloc(); }, + false); text_input_reset(text_input); @@ -456,15 +463,13 @@ TextInput* text_input_alloc() { void text_input_free(TextInput* text_input) { furi_assert(text_input); with_view_model( - text_input->view, (TextInputModel * model) { - string_clear(model->validator_text); - return false; - }); + text_input->view, + TextInputModel * model, + { furi_string_free(model->validator_text); }, + false); // Send stop command furi_timer_stop(text_input->timer); - // Wait till timer stop - while(furi_timer_is_running(text_input->timer)) furi_delay_tick(1); // Release allocated memory furi_timer_free(text_input->timer); @@ -476,8 +481,9 @@ void text_input_free(TextInput* text_input) { void text_input_reset(TextInput* text_input) { furi_assert(text_input); with_view_model( - text_input->view, (TextInputModel * model) { - model->text_buffer_size = 0; + text_input->view, + TextInputModel * model, + { model->header = ""; model->selected_row = 0; model->selected_column = 0; @@ -488,10 +494,10 @@ void text_input_reset(TextInput* text_input) { model->callback_context = NULL; model->validator_callback = NULL; model->validator_callback_context = NULL; - string_reset(model->validator_text); - model->valadator_message_visible = false; - return true; - }); + furi_string_reset(model->validator_text); + model->validator_message_visible = false; + }, + true); } View* text_input_get_view(TextInput* text_input) { @@ -507,7 +513,9 @@ void text_input_set_result_callback( size_t text_buffer_size, bool clear_default_text) { with_view_model( - text_input->view, (TextInputModel * model) { + text_input->view, + TextInputModel * model, + { model->callback = callback; model->callback_context = callback_context; model->text_buffer = text_buffer; @@ -518,8 +526,8 @@ void text_input_set_result_callback( model->selected_row = 2; model->selected_column = 8; } - return true; - }); + }, + true); } void text_input_set_validator( @@ -527,37 +535,36 @@ void text_input_set_validator( TextInputValidatorCallback callback, void* callback_context) { with_view_model( - text_input->view, (TextInputModel * model) { + text_input->view, + TextInputModel * model, + { model->validator_callback = callback; model->validator_callback_context = callback_context; - return true; - }); + }, + true); } TextInputValidatorCallback text_input_get_validator_callback(TextInput* text_input) { TextInputValidatorCallback validator_callback = NULL; with_view_model( - text_input->view, (TextInputModel * model) { - validator_callback = model->validator_callback; - return false; - }); + text_input->view, + TextInputModel * model, + { validator_callback = model->validator_callback; }, + false); return validator_callback; } void* text_input_get_validator_callback_context(TextInput* text_input) { void* validator_callback_context = NULL; with_view_model( - text_input->view, (TextInputModel * model) { - validator_callback_context = model->validator_callback_context; - return false; - }); + text_input->view, + TextInputModel * model, + { validator_callback_context = model->validator_callback_context; }, + false); return validator_callback_context; } void text_input_set_header_text(TextInput* text_input, const char* text) { with_view_model( - text_input->view, (TextInputModel * model) { - model->header = text; - return true; - }); + text_input->view, TextInputModel * model, { model->header = text; }, true); } diff --git a/applications/gui/modules/text_input.h b/applications/services/gui/modules/text_input.h similarity index 93% rename from applications/gui/modules/text_input.h rename to applications/services/gui/modules/text_input.h index d30fcd4caae..218df3141df 100644 --- a/applications/gui/modules/text_input.h +++ b/applications/services/gui/modules/text_input.h @@ -1,13 +1,12 @@ /** * @file text_input.h - * GUI: TextInput keybord view module API + * GUI: TextInput keyboard view module API */ #pragma once #include #include "validators.h" -#include #ifdef __cplusplus extern "C" { @@ -16,7 +15,7 @@ extern "C" { /** Text input anonymous structure */ typedef struct TextInput TextInput; typedef void (*TextInputCallback)(void* context); -typedef bool (*TextInputValidatorCallback)(const char* text, string_t error, void* context); +typedef bool (*TextInputValidatorCallback)(const char* text, FuriString* error, void* context); /** Allocate and initialize text input * diff --git a/applications/services/gui/modules/validators.c b/applications/services/gui/modules/validators.c new file mode 100644 index 00000000000..a18cb925f14 --- /dev/null +++ b/applications/services/gui/modules/validators.c @@ -0,0 +1,54 @@ +#include "validators.h" +#include +#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; + } + } + + FuriString* path = furi_string_alloc_printf( + "%s/%s%s", instance->app_path_folder, text, instance->app_extension); + Storage* storage = furi_record_open(RECORD_STORAGE); + const bool ret = storage_common_stat(storage, furi_string_get_cstr(path), NULL) != FSE_OK; + if(!ret) { + furi_string_printf(error, "This name\nexists!\nChoose\nanother one."); + } + 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/gui/modules/validators.h b/applications/services/gui/modules/validators.h similarity index 76% rename from applications/gui/modules/validators.h rename to applications/services/gui/modules/validators.h index c4c4ef54c14..e509fd8336c 100644 --- a/applications/gui/modules/validators.h +++ b/applications/services/gui/modules/validators.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #ifdef __cplusplus extern "C" { @@ -15,7 +15,7 @@ ValidatorIsFile* validator_is_file_alloc_init( void validator_is_file_free(ValidatorIsFile* instance); -bool validator_is_file_callback(const char* text, string_t error, void* context); +bool validator_is_file_callback(const char* text, FuriString* error, void* context); #ifdef __cplusplus } diff --git a/applications/gui/modules/variable_item_list.c b/applications/services/gui/modules/variable_item_list.c similarity index 86% rename from applications/gui/modules/variable_item_list.c rename to applications/services/gui/modules/variable_item_list.c index 4e5e4664f2c..cf7f64ca350 100644 --- a/applications/gui/modules/variable_item_list.c +++ b/applications/services/gui/modules/variable_item_list.c @@ -1,14 +1,14 @@ #include "variable_item_list.h" -#include "gui/canvas.h" -#include -#include #include +#include +#include +#include #include struct VariableItem { const char* label; uint8_t current_value_index; - string_t current_value_text; + FuriString* current_value_text; uint8_t values_count; VariableItemChangeCallback change_callback; void* context; @@ -77,7 +77,7 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) { item_text_y, AlignCenter, AlignBottom, - string_get_cstr(item->current_value_text)); + furi_string_get_cstr(item->current_value_text)); if(item->current_value_index < (item->values_count - 1)) { canvas_draw_str(canvas, 115, item_text_y, ">"); @@ -92,7 +92,9 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) { void variable_item_list_set_selected_item(VariableItemList* variable_item_list, uint8_t index) { with_view_model( - variable_item_list->view, (VariableItemListModel * model) { + variable_item_list->view, + VariableItemListModel * model, + { uint8_t position = index; if(position >= VariableItemArray_size(model->items)) { position = 0; @@ -112,9 +114,8 @@ void variable_item_list_set_selected_item(VariableItemList* variable_item_list, model->window_position = (VariableItemArray_size(model->items) - 4); } } - - return true; - }); + }, + true); } uint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_item_list) { @@ -181,12 +182,14 @@ static bool variable_item_list_input_callback(InputEvent* event, void* context) void variable_item_list_process_up(VariableItemList* variable_item_list) { with_view_model( - variable_item_list->view, (VariableItemListModel * model) { + variable_item_list->view, + VariableItemListModel * model, + { uint8_t items_on_screen = 4; if(model->position > 0) { model->position--; - if(((model->position - model->window_position) < 1) && - model->window_position > 0) { + + if((model->position == model->window_position) && (model->window_position > 0)) { model->window_position--; } } else { @@ -195,13 +198,15 @@ void variable_item_list_process_up(VariableItemList* variable_item_list) { model->window_position = model->position - (items_on_screen - 1); } } - return true; - }); + }, + true); } void variable_item_list_process_down(VariableItemList* variable_item_list) { with_view_model( - variable_item_list->view, (VariableItemListModel * model) { + variable_item_list->view, + VariableItemListModel * model, + { uint8_t items_on_screen = 4; if(model->position < (VariableItemArray_size(model->items) - 1)) { model->position++; @@ -214,8 +219,8 @@ void variable_item_list_process_down(VariableItemList* variable_item_list) { model->position = 0; model->window_position = 0; } - return true; - }); + }, + true); } VariableItem* variable_item_list_get_selected_item(VariableItemListModel* model) { @@ -239,7 +244,9 @@ VariableItem* variable_item_list_get_selected_item(VariableItemListModel* model) void variable_item_list_process_left(VariableItemList* variable_item_list) { with_view_model( - variable_item_list->view, (VariableItemListModel * model) { + variable_item_list->view, + VariableItemListModel * model, + { VariableItem* item = variable_item_list_get_selected_item(model); if(item->current_value_index > 0) { item->current_value_index--; @@ -247,13 +254,15 @@ void variable_item_list_process_left(VariableItemList* variable_item_list) { item->change_callback(item); } } - return true; - }); + }, + true); } void variable_item_list_process_right(VariableItemList* variable_item_list) { with_view_model( - variable_item_list->view, (VariableItemListModel * model) { + variable_item_list->view, + VariableItemListModel * model, + { VariableItem* item = variable_item_list_get_selected_item(model); if(item->current_value_index < (item->values_count - 1)) { item->current_value_index++; @@ -261,18 +270,20 @@ void variable_item_list_process_right(VariableItemList* variable_item_list) { item->change_callback(item); } } - return true; - }); + }, + true); } void variable_item_list_process_ok(VariableItemList* variable_item_list) { with_view_model( - variable_item_list->view, (VariableItemListModel * model) { + variable_item_list->view, + VariableItemListModel * model, + { if(variable_item_list->callback) { variable_item_list->callback(variable_item_list->context, model->position); } - return false; - }); + }, + false); } VariableItemList* variable_item_list_alloc() { @@ -285,12 +296,14 @@ VariableItemList* variable_item_list_alloc() { view_set_input_callback(variable_item_list->view, variable_item_list_input_callback); with_view_model( - variable_item_list->view, (VariableItemListModel * model) { + variable_item_list->view, + VariableItemListModel * model, + { VariableItemArray_init(model->items); model->position = 0; model->window_position = 0; - return true; - }); + }, + true); return variable_item_list; } @@ -299,15 +312,17 @@ void variable_item_list_free(VariableItemList* variable_item_list) { furi_assert(variable_item_list); with_view_model( - variable_item_list->view, (VariableItemListModel * model) { + variable_item_list->view, + VariableItemListModel * model, + { VariableItemArray_it_t it; for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it); VariableItemArray_next(it)) { - string_clear(VariableItemArray_ref(it)->current_value_text); + furi_string_free(VariableItemArray_ref(it)->current_value_text); } VariableItemArray_clear(model->items); - return false; - }); + }, + false); view_free(variable_item_list->view); free(variable_item_list); } @@ -316,15 +331,17 @@ void variable_item_list_reset(VariableItemList* variable_item_list) { furi_assert(variable_item_list); with_view_model( - variable_item_list->view, (VariableItemListModel * model) { + variable_item_list->view, + VariableItemListModel * model, + { VariableItemArray_it_t it; for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it); VariableItemArray_next(it)) { - string_clear(VariableItemArray_ref(it)->current_value_text); + furi_string_free(VariableItemArray_ref(it)->current_value_text); } VariableItemArray_reset(model->items); - return false; - }); + }, + false); } View* variable_item_list_get_view(VariableItemList* variable_item_list) { @@ -343,16 +360,18 @@ VariableItem* variable_item_list_add( furi_assert(variable_item_list); with_view_model( - variable_item_list->view, (VariableItemListModel * model) { + variable_item_list->view, + VariableItemListModel * model, + { item = VariableItemArray_push_new(model->items); item->label = label; item->values_count = values_count; item->change_callback = change_callback; item->context = context; item->current_value_index = 0; - string_init(item->current_value_text); - return true; - }); + item->current_value_text = furi_string_alloc(); + }, + true); return item; } @@ -363,20 +382,26 @@ void variable_item_list_set_enter_callback( void* context) { furi_assert(callback); with_view_model( - variable_item_list->view, (VariableItemListModel * model) { + variable_item_list->view, + VariableItemListModel * model, + { UNUSED(model); variable_item_list->callback = callback; variable_item_list->context = context; - return false; - }); + }, + false); } void variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index) { item->current_value_index = current_value_index; } +void variable_item_set_values_count(VariableItem* item, uint8_t values_count) { + item->values_count = values_count; +} + void variable_item_set_current_value_text(VariableItem* item, const char* current_value_text) { - string_set_str(item->current_value_text, current_value_text); + furi_string_set(item->current_value_text, current_value_text); } uint8_t variable_item_get_current_value_index(VariableItem* item) { diff --git a/applications/gui/modules/variable_item_list.h b/applications/services/gui/modules/variable_item_list.h old mode 100755 new mode 100644 similarity index 92% rename from applications/gui/modules/variable_item_list.h rename to applications/services/gui/modules/variable_item_list.h index 78c7769c519..db2a58993d4 --- a/applications/gui/modules/variable_item_list.h +++ b/applications/services/gui/modules/variable_item_list.h @@ -81,6 +81,13 @@ uint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_it */ void variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index); +/** Set number of values for item + * + * @param item VariableItem* instance + * @param values_count The new values count + */ +void variable_item_set_values_count(VariableItem* item, uint8_t values_count); + /** Set item current selected text * * @param item VariableItem* instance diff --git a/applications/gui/modules/widget.c b/applications/services/gui/modules/widget.c similarity index 90% rename from applications/gui/modules/widget.c rename to applications/services/gui/modules/widget.c index b37a64701f7..21b8e561629 100644 --- a/applications/gui/modules/widget.c +++ b/applications/services/gui/modules/widget.c @@ -1,5 +1,6 @@ -#include #include "widget.h" +#include "widget_elements/widget_element_i.h" +#include #include ARRAY_DEF(ElementArray, WidgetElement*, M_PTR_OPLIST); @@ -35,7 +36,9 @@ static bool gui_widget_view_input_callback(InputEvent* event, void* context) { // Call all Widget Elements input handlers with_view_model( - widget->view, (GuiWidgetModel * model) { + widget->view, + GuiWidgetModel * model, + { ElementArray_it_t it; ElementArray_it(it, model->element); while(!ElementArray_end_p(it)) { @@ -45,8 +48,8 @@ static bool gui_widget_view_input_callback(InputEvent* event, void* context) { } ElementArray_next(it); } - return true; - }); + }, + true); return consumed; } @@ -60,10 +63,7 @@ Widget* widget_alloc() { view_set_input_callback(widget->view, gui_widget_view_input_callback); with_view_model( - widget->view, (GuiWidgetModel * model) { - ElementArray_init(model->element); - return true; - }); + widget->view, GuiWidgetModel * model, { ElementArray_init(model->element); }, true); return widget; } @@ -72,7 +72,9 @@ void widget_reset(Widget* widget) { furi_assert(widget); with_view_model( - widget->view, (GuiWidgetModel * model) { + widget->view, + GuiWidgetModel * model, + { ElementArray_it_t it; ElementArray_it(it, model->element); while(!ElementArray_end_p(it)) { @@ -82,8 +84,8 @@ void widget_reset(Widget* widget) { ElementArray_next(it); } ElementArray_reset(model->element); - return true; - }); + }, + true); } void widget_free(Widget* widget) { @@ -92,10 +94,7 @@ void widget_free(Widget* widget) { widget_reset(widget); // Free elements container with_view_model( - widget->view, (GuiWidgetModel * model) { - ElementArray_clear(model->element); - return true; - }); + widget->view, GuiWidgetModel * model, { ElementArray_clear(model->element); }, true); view_free(widget->view); free(widget); @@ -111,11 +110,13 @@ static void widget_add_element(Widget* widget, WidgetElement* element) { furi_assert(element); with_view_model( - widget->view, (GuiWidgetModel * model) { + widget->view, + GuiWidgetModel * model, + { element->parent = widget; ElementArray_push_back(model->element, element); - return true; - }); + }, + true); } void widget_add_string_multiline_element( diff --git a/applications/gui/modules/widget.h b/applications/services/gui/modules/widget.h old mode 100755 new mode 100644 similarity index 95% rename from applications/gui/modules/widget.h rename to applications/services/gui/modules/widget.h index 03586165c2d..d998516781b --- a/applications/gui/modules/widget.h +++ b/applications/services/gui/modules/widget.h @@ -4,8 +4,8 @@ */ #pragma once - -#include "widget_elements/widget_element_i.h" +#include +#include "widget_elements/widget_element.h" #ifdef __cplusplus extern "C" { @@ -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!Inversed 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.h b/applications/services/gui/modules/widget_elements/widget_element.h new file mode 100644 index 00000000000..67990417387 --- /dev/null +++ b/applications/services/gui/modules/widget_elements/widget_element.h @@ -0,0 +1,22 @@ +/** + * @file widget_element_i.h + * GUI: internal Widget Element API + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + GuiButtonTypeLeft, + GuiButtonTypeCenter, + GuiButtonTypeRight, +} GuiButtonType; + +typedef void (*ButtonCallback)(GuiButtonType result, InputType type, void* context); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/gui/modules/widget_elements/widget_element_button.c b/applications/services/gui/modules/widget_elements/widget_element_button.c similarity index 86% rename from applications/gui/modules/widget_elements/widget_element_button.c rename to applications/services/gui/modules/widget_elements/widget_element_button.c index 92be25907d5..3bfae9df392 100644 --- a/applications/gui/modules/widget_elements/widget_element_button.c +++ b/applications/services/gui/modules/widget_elements/widget_element_button.c @@ -1,10 +1,9 @@ #include "widget_element_i.h" #include -#include typedef struct { GuiButtonType button_type; - string_t text; + FuriString* text; ButtonCallback callback; void* context; } GuiButtonModel; @@ -18,11 +17,11 @@ static void gui_button_draw(Canvas* canvas, WidgetElement* element) { canvas_set_font(canvas, FontSecondary); if(model->button_type == GuiButtonTypeLeft) { - elements_button_left(canvas, string_get_cstr(model->text)); + elements_button_left(canvas, furi_string_get_cstr(model->text)); } else if(model->button_type == GuiButtonTypeRight) { - elements_button_right(canvas, string_get_cstr(model->text)); + elements_button_right(canvas, furi_string_get_cstr(model->text)); } else if(model->button_type == GuiButtonTypeCenter) { - elements_button_center(canvas, string_get_cstr(model->text)); + elements_button_center(canvas, furi_string_get_cstr(model->text)); } } @@ -50,7 +49,7 @@ static void gui_button_free(WidgetElement* gui_button) { furi_assert(gui_button); GuiButtonModel* model = gui_button->model; - string_clear(model->text); + furi_string_free(model->text); free(gui_button->model); free(gui_button); } @@ -65,7 +64,7 @@ WidgetElement* widget_element_button_create( model->button_type = button_type; model->callback = callback; model->context = context; - string_init_set_str(model->text, text); + model->text = furi_string_alloc_set(text); // Allocate and init Element WidgetElement* gui_button = malloc(sizeof(WidgetElement)); @@ -76,4 +75,4 @@ WidgetElement* widget_element_button_create( gui_button->model = model; return gui_button; -} +} //-V773 diff --git a/applications/gui/modules/widget_elements/widget_element_frame.c b/applications/services/gui/modules/widget_elements/widget_element_frame.c old mode 100755 new mode 100644 similarity index 100% rename from applications/gui/modules/widget_elements/widget_element_frame.c rename to applications/services/gui/modules/widget_elements/widget_element_frame.c diff --git a/applications/gui/modules/widget_elements/widget_element_i.h b/applications/services/gui/modules/widget_elements/widget_element_i.h old mode 100755 new mode 100644 similarity index 90% rename from applications/gui/modules/widget_elements/widget_element_i.h rename to applications/services/gui/modules/widget_elements/widget_element_i.h index 316ed740069..67dea4b1f69 --- a/applications/gui/modules/widget_elements/widget_element_i.h +++ b/applications/services/gui/modules/widget_elements/widget_element_i.h @@ -7,14 +7,11 @@ #include #include #include +#include "widget_element.h" -typedef enum { - GuiButtonTypeLeft, - GuiButtonTypeCenter, - GuiButtonTypeRight, -} GuiButtonType; - -typedef void (*ButtonCallback)(GuiButtonType result, InputType type, void* context); +#ifdef __cplusplus +extern "C" { +#endif typedef struct WidgetElement WidgetElement; typedef struct Widget Widget; @@ -88,3 +85,7 @@ WidgetElement* widget_element_text_scroll_create( uint8_t width, uint8_t height, const char* text); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/gui/modules/widget_elements/widget_element_icon.c b/applications/services/gui/modules/widget_elements/widget_element_icon.c similarity index 100% rename from applications/gui/modules/widget_elements/widget_element_icon.c rename to applications/services/gui/modules/widget_elements/widget_element_icon.c diff --git a/applications/gui/modules/widget_elements/widget_element_string.c b/applications/services/gui/modules/widget_elements/widget_element_string.c old mode 100755 new mode 100644 similarity index 87% rename from applications/gui/modules/widget_elements/widget_element_string.c rename to applications/services/gui/modules/widget_elements/widget_element_string.c index a03c2b6d864..4bf7dd693f1 --- a/applications/gui/modules/widget_elements/widget_element_string.c +++ b/applications/services/gui/modules/widget_elements/widget_element_string.c @@ -1,5 +1,4 @@ #include "widget_element_i.h" -#include typedef struct { uint8_t x; @@ -7,7 +6,7 @@ typedef struct { Align horizontal; Align vertical; Font font; - string_t text; + FuriString* text; } GuiStringModel; static void gui_string_draw(Canvas* canvas, WidgetElement* element) { @@ -15,7 +14,7 @@ static void gui_string_draw(Canvas* canvas, WidgetElement* element) { furi_assert(element); GuiStringModel* model = element->model; - if(string_size(model->text)) { + if(furi_string_size(model->text)) { canvas_set_font(canvas, model->font); canvas_draw_str_aligned( canvas, @@ -23,7 +22,7 @@ static void gui_string_draw(Canvas* canvas, WidgetElement* element) { model->y, model->horizontal, model->vertical, - string_get_cstr(model->text)); + furi_string_get_cstr(model->text)); } } @@ -31,7 +30,7 @@ static void gui_string_free(WidgetElement* gui_string) { furi_assert(gui_string); GuiStringModel* model = gui_string->model; - string_clear(model->text); + furi_string_free(model->text); free(gui_string->model); free(gui_string); } @@ -52,7 +51,7 @@ WidgetElement* widget_element_string_create( model->horizontal = horizontal; model->vertical = vertical; model->font = font; - string_init_set_str(model->text, text); + model->text = furi_string_alloc_set(text); // Allocate and init Element WidgetElement* gui_string = malloc(sizeof(WidgetElement)); @@ -63,4 +62,4 @@ WidgetElement* widget_element_string_create( gui_string->model = model; return gui_string; -} +} //-V773 diff --git a/applications/gui/modules/widget_elements/widget_element_string_multiline.c b/applications/services/gui/modules/widget_elements/widget_element_string_multiline.c similarity index 88% rename from applications/gui/modules/widget_elements/widget_element_string_multiline.c rename to applications/services/gui/modules/widget_elements/widget_element_string_multiline.c index 01a70a0d087..3fc6b309c07 100644 --- a/applications/gui/modules/widget_elements/widget_element_string_multiline.c +++ b/applications/services/gui/modules/widget_elements/widget_element_string_multiline.c @@ -1,5 +1,4 @@ #include "widget_element_i.h" -#include #include typedef struct { @@ -8,7 +7,7 @@ typedef struct { Align horizontal; Align vertical; Font font; - string_t text; + FuriString* text; } GuiStringMultiLineModel; static void gui_string_multiline_draw(Canvas* canvas, WidgetElement* element) { @@ -16,7 +15,7 @@ static void gui_string_multiline_draw(Canvas* canvas, WidgetElement* element) { furi_assert(element); GuiStringMultiLineModel* model = element->model; - if(string_size(model->text)) { + if(furi_string_size(model->text)) { canvas_set_font(canvas, model->font); elements_multiline_text_aligned( canvas, @@ -24,7 +23,7 @@ static void gui_string_multiline_draw(Canvas* canvas, WidgetElement* element) { model->y, model->horizontal, model->vertical, - string_get_cstr(model->text)); + furi_string_get_cstr(model->text)); } } @@ -32,7 +31,7 @@ static void gui_string_multiline_free(WidgetElement* gui_string) { furi_assert(gui_string); GuiStringMultiLineModel* model = gui_string->model; - string_clear(model->text); + furi_string_free(model->text); free(gui_string->model); free(gui_string); } @@ -53,7 +52,7 @@ WidgetElement* widget_element_string_multiline_create( model->horizontal = horizontal; model->vertical = vertical; model->font = font; - string_init_set_str(model->text, text); + model->text = furi_string_alloc_set(text); // Allocate and init Element WidgetElement* gui_string = malloc(sizeof(WidgetElement)); @@ -64,4 +63,4 @@ WidgetElement* widget_element_string_multiline_create( gui_string->model = model; return gui_string; -} +} //-V773 diff --git a/applications/gui/modules/widget_elements/widget_element_text_box.c b/applications/services/gui/modules/widget_elements/widget_element_text_box.c similarity index 89% rename from applications/gui/modules/widget_elements/widget_element_text_box.c rename to applications/services/gui/modules/widget_elements/widget_element_text_box.c index 4750f8f8fcf..98f8e83d824 100644 --- a/applications/gui/modules/widget_elements/widget_element_text_box.c +++ b/applications/services/gui/modules/widget_elements/widget_element_text_box.c @@ -1,5 +1,4 @@ #include "widget_element_i.h" -#include #include typedef struct { @@ -9,7 +8,7 @@ typedef struct { uint8_t height; Align horizontal; Align vertical; - string_t text; + FuriString* text; bool strip_to_dots; } GuiTextBoxModel; @@ -18,7 +17,7 @@ static void gui_text_box_draw(Canvas* canvas, WidgetElement* element) { furi_assert(element); GuiTextBoxModel* model = element->model; - if(string_size(model->text)) { + if(furi_string_size(model->text)) { elements_text_box( canvas, model->x, @@ -27,7 +26,7 @@ static void gui_text_box_draw(Canvas* canvas, WidgetElement* element) { model->height, model->horizontal, model->vertical, - string_get_cstr(model->text), + furi_string_get_cstr(model->text), model->strip_to_dots); } } @@ -36,7 +35,7 @@ static void gui_text_box_free(WidgetElement* gui_string) { furi_assert(gui_string); GuiTextBoxModel* model = gui_string->model; - string_clear(model->text); + furi_string_free(model->text); free(gui_string->model); free(gui_string); } @@ -60,7 +59,7 @@ WidgetElement* widget_element_text_box_create( model->height = height; model->horizontal = horizontal; model->vertical = vertical; - string_init_set_str(model->text, text); + model->text = furi_string_alloc_set(text); model->strip_to_dots = strip_to_dots; // Allocate and init Element @@ -72,4 +71,4 @@ WidgetElement* widget_element_text_box_create( gui_string->model = model; return gui_string; -} +} //-V773 diff --git a/applications/gui/modules/widget_elements/widget_element_text_scroll.c b/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c similarity index 83% rename from applications/gui/modules/widget_elements/widget_element_text_scroll.c rename to applications/services/gui/modules/widget_elements/widget_element_text_scroll.c index 6682b106a88..4c9c39dffb1 100644 --- a/applications/gui/modules/widget_elements/widget_element_text_scroll.c +++ b/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c @@ -1,5 +1,4 @@ #include "widget_element_i.h" -#include #include #include @@ -8,7 +7,7 @@ typedef struct { Font font; Align horizontal; - string_t text; + FuriString* text; } TextScrollLineArray; ARRAY_DEF(TextScrollLineArray, TextScrollLineArray, M_POD_OPLIST) @@ -19,27 +18,29 @@ typedef struct { uint8_t y; uint8_t width; uint8_t height; - string_t text; + FuriString* text; uint8_t scroll_pos_total; uint8_t scroll_pos_current; bool text_formatted; } WidgetElementTextScrollModel; static bool - widget_element_text_scroll_process_ctrl_symbols(TextScrollLineArray* line, string_t text) { + widget_element_text_scroll_process_ctrl_symbols(TextScrollLineArray* line, FuriString* text) { bool processed = false; do { - if(string_get_char(text, 0) != '\e') break; - char ctrl_symbol = string_get_char(text, 1); + if(furi_string_get_char(text, 0) != '\e') break; + char ctrl_symbol = furi_string_get_char(text, 1); if(ctrl_symbol == 'c') { line->horizontal = AlignCenter; } else if(ctrl_symbol == 'r') { line->horizontal = AlignRight; } else if(ctrl_symbol == '#') { line->font = FontPrimary; + } else if(ctrl_symbol == '*') { + line->font = FontKeyboard; } - string_right(text, 2); + furi_string_right(text, 2); processed = true; } while(false); @@ -51,7 +52,7 @@ void widget_element_text_scroll_add_line(WidgetElement* element, TextScrollLineA TextScrollLineArray new_line; new_line.font = line->font; new_line.horizontal = line->horizontal; - string_init_set(new_line.text, line->text); + new_line.text = furi_string_alloc_set(line->text); TextScrollLineArray_push_back(model->line_array, new_line); } @@ -59,7 +60,7 @@ static void widget_element_text_scroll_fill_lines(Canvas* canvas, WidgetElement* WidgetElementTextScrollModel* model = element->model; TextScrollLineArray line_tmp; bool all_text_processed = false; - string_init(line_tmp.text); + line_tmp.text = furi_string_alloc(); bool reached_new_line = true; uint16_t total_height = 0; @@ -68,14 +69,14 @@ static void widget_element_text_scroll_fill_lines(Canvas* canvas, WidgetElement* // Set default line properties line_tmp.font = FontSecondary; line_tmp.horizontal = AlignLeft; - string_reset(line_tmp.text); + furi_string_reset(line_tmp.text); // Process control symbols while(widget_element_text_scroll_process_ctrl_symbols(&line_tmp, model->text)) ; } // 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++; @@ -84,38 +85,38 @@ static void widget_element_text_scroll_fill_lines(Canvas* canvas, WidgetElement* uint8_t line_width = 0; uint16_t char_i = 0; while(true) { - char next_char = string_get_char(model->text, char_i++); + char next_char = furi_string_get_char(model->text, char_i++); if(next_char == '\0') { - string_push_back(line_tmp.text, '\0'); + furi_string_push_back(line_tmp.text, '\0'); widget_element_text_scroll_add_line(element, &line_tmp); total_height += params->leading_default - params->height; all_text_processed = true; break; } else if(next_char == '\n') { - string_push_back(line_tmp.text, '\0'); + furi_string_push_back(line_tmp.text, '\0'); widget_element_text_scroll_add_line(element, &line_tmp); - string_right(model->text, char_i); + furi_string_right(model->text, char_i); total_height += params->leading_default - params->height; reached_new_line = true; break; } else { line_width += canvas_glyph_width(canvas, next_char); if(line_width > model->width) { - string_push_back(line_tmp.text, '\0'); + furi_string_push_back(line_tmp.text, '\0'); widget_element_text_scroll_add_line(element, &line_tmp); - string_right(model->text, char_i - 1); - string_reset(line_tmp.text); + furi_string_right(model->text, char_i - 1); + furi_string_reset(line_tmp.text); total_height += params->leading_default - params->height; reached_new_line = false; break; } else { - string_push_back(line_tmp.text, next_char); + furi_string_push_back(line_tmp.text, next_char); } } } } - string_clear(line_tmp.text); + furi_string_free(line_tmp.text); } static void widget_element_text_scroll_draw(Canvas* canvas, WidgetElement* element) { @@ -139,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) { @@ -150,7 +151,7 @@ static void widget_element_text_scroll_draw(Canvas* canvas, WidgetElement* eleme x = model->x + model->width; } canvas_draw_str_aligned( - canvas, x, y, line->horizontal, AlignTop, string_get_cstr(line->text)); + canvas, x, y, line->horizontal, AlignTop, furi_string_get_cstr(line->text)); y += params->leading_default; } // Draw scroll bar @@ -205,10 +206,10 @@ static void widget_element_text_scroll_free(WidgetElement* text_scroll) { for(TextScrollLineArray_it(it, model->line_array); !TextScrollLineArray_end_p(it); TextScrollLineArray_next(it)) { TextScrollLineArray* line = TextScrollLineArray_ref(it); - string_clear(line->text); + furi_string_free(line->text); } TextScrollLineArray_clear(model->line_array); - string_clear(model->text); + furi_string_free(model->text); free(text_scroll->model); furi_mutex_free(text_scroll->model_mutex); free(text_scroll); @@ -231,7 +232,7 @@ WidgetElement* widget_element_text_scroll_create( model->scroll_pos_current = 0; model->scroll_pos_total = 1; TextScrollLineArray_init(model->line_array); - string_init_set_str(model->text, text); + model->text = furi_string_alloc_set(text); WidgetElement* text_scroll = malloc(sizeof(WidgetElement)); text_scroll->parent = NULL; @@ -242,4 +243,4 @@ WidgetElement* widget_element_text_scroll_create( text_scroll->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal); return text_scroll; -} +} //-V773 diff --git a/applications/gui/scene_manager.c b/applications/services/gui/scene_manager.c old mode 100755 new mode 100644 similarity index 97% rename from applications/gui/scene_manager.c rename to applications/services/gui/scene_manager.c index 590145e1e7b..4064dafb662 --- a/applications/gui/scene_manager.c +++ b/applications/services/gui/scene_manager.c @@ -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/gui/scene_manager.h b/applications/services/gui/scene_manager.h old mode 100755 new mode 100644 similarity index 96% rename from applications/gui/scene_manager.h rename to applications/services/gui/scene_manager.h index 5b833e5de20..c349a12ceac --- a/applications/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/gui/scene_manager_i.h b/applications/services/gui/scene_manager_i.h old mode 100755 new mode 100644 similarity index 100% rename from applications/gui/scene_manager_i.h rename to applications/services/gui/scene_manager_i.h diff --git a/applications/gui/view.c b/applications/services/gui/view.c similarity index 94% rename from applications/gui/view.c rename to applications/services/gui/view.c index 7ab6d15b753..316f5c612a1 100644 --- a/applications/gui/view.c +++ b/applications/services/gui/view.c @@ -19,19 +19,16 @@ void view_tie_icon_animation(View* view, IconAnimation* icon_animation) { void view_set_draw_callback(View* view, ViewDrawCallback callback) { furi_assert(view); - furi_assert(view->draw_callback == NULL); view->draw_callback = callback; } void view_set_input_callback(View* view, ViewInputCallback callback) { furi_assert(view); - furi_assert(view->input_callback == NULL); view->input_callback = callback; } void view_set_custom_callback(View* view, ViewCustomCallback callback) { furi_assert(view); - furi_assert(callback); view->custom_callback = callback; } @@ -62,7 +59,6 @@ void view_set_update_callback_context(View* view, void* context) { void view_set_context(View* view, void* context) { furi_assert(view); - furi_assert(context); view->context = context; } @@ -81,12 +77,12 @@ void view_allocate_model(View* view, ViewModelType type, size_t size) { view->model = malloc(size); } else if(view->model_type == ViewModelTypeLocking) { ViewModelLocking* model = malloc(sizeof(ViewModelLocking)); - model->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + model->mutex = furi_mutex_alloc(FuriMutexTypeRecursive); furi_check(model->mutex); model->data = malloc(size); view->model = model; } else { - furi_assert(false); + furi_crash(); } } @@ -103,7 +99,7 @@ void view_free_model(View* view) { free(model); view->model = NULL; } else { - furi_assert(false); + furi_crash(); } } diff --git a/applications/gui/view.h b/applications/services/gui/view.h similarity index 87% rename from applications/gui/view.h rename to applications/services/gui/view.h index 5b4bf3609a6..7a2003a63bc 100644 --- a/applications/gui/view.h +++ b/applications/services/gui/view.h @@ -25,7 +25,9 @@ extern "C" { typedef enum { ViewOrientationHorizontal, + ViewOrientationHorizontalFlip, ViewOrientationVertical, + ViewOrientationVerticalFlip, } ViewOrientation; /** View, anonymous type */ @@ -211,25 +213,25 @@ void view_commit_model(View* view, bool update); #endif #ifdef __cplusplus -#define with_view_model_cpp(view, type, var, function_body) \ +#define with_view_model_cpp(view, type, var, code, update) \ { \ - type* p = static_cast(view_get_model(view)); \ - bool update = [&](type * var) function_body(p); \ + type var = static_cast(view_get_model(view)); \ + {code}; \ view_commit_model(view, update); \ } #else /** With clause for view model * * @param view View instance pointer - * @param function_body a (){} lambda declaration, executed within you - * parent function context + * @param type View model type + * @param code Code block that will be executed between model lock and unlock + * @param update Bool flag, if true, view will be updated after code block. Can be variable, so code block can decide if update is needed. * - * @return true if you want to emit view update, false otherwise */ -#define with_view_model(view, function_body) \ - { \ - void* p = view_get_model(view); \ - bool update = ({ bool __fn__ function_body __fn__; })(p); \ - view_commit_model(view, update); \ +#define with_view_model(view, type, code, update) \ + { \ + type = view_get_model(view); \ + {code}; \ + view_commit_model(view, update); \ } #endif diff --git a/applications/gui/view_dispatcher.c b/applications/services/gui/view_dispatcher.c similarity index 92% rename from applications/gui/view_dispatcher.c rename to applications/services/gui/view_dispatcher.c index 307206c14b3..87b07a87c47 100644 --- a/applications/gui/view_dispatcher.c +++ b/applications/services/gui/view_dispatcher.c @@ -23,7 +23,7 @@ void view_dispatcher_free(ViewDispatcher* view_dispatcher) { gui_remove_view_port(view_dispatcher->gui, view_dispatcher->view_port); } // Crash if not all views were freed - furi_assert(ViewDict_size(view_dispatcher->views) == 0); + furi_assert(!ViewDict_size(view_dispatcher->views)); ViewDict_clear(view_dispatcher->views); // Free ViewPort @@ -152,12 +152,12 @@ void view_dispatcher_remove_view(ViewDispatcher* view_dispatcher, uint32_t view_ if(view_dispatcher->current_view == view) { view_dispatcher_set_current_view(view_dispatcher, NULL); } - // Check if view is recieving input + // Check if view is receiving input if(view_dispatcher->ongoing_input_view == view) { view_dispatcher->ongoing_input_view = NULL; } // Remove view - ViewDict_erase(view_dispatcher->views, view_id); + furi_check(ViewDict_erase(view_dispatcher->views, view_id)); view_set_update_callback(view, NULL); view_set_update_callback_context(view, NULL); @@ -207,7 +207,7 @@ void view_dispatcher_attach_to_gui( } else if(type == ViewDispatcherTypeFullscreen) { gui_add_view_port(gui, view_dispatcher->view_port, GuiLayerFullscreen); } else { - furi_check(NULL); + furi_crash(); } view_dispatcher->gui = gui; } @@ -246,7 +246,7 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e "non-complementary input, discarding key: %s, type: %s, sequence: %p", input_get_key_name(event->key), input_get_type_name(event->type), - event->sequence); + (void*)event->sequence); return; } @@ -272,7 +272,6 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e } else if(view_dispatcher->navigation_event_callback) { // Dispatch navigation event if(!view_dispatcher->navigation_event_callback(view_dispatcher->event_context)) { - // TODO: should we allow view_dispatcher to stop without navigation_event_callback? view_dispatcher_stop(view_dispatcher); return; } @@ -286,7 +285,7 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e view_dispatcher->current_view, input_get_key_name(event->key), input_get_type_name(event->type), - event->sequence); + (void*)event->sequence); view_input(view_dispatcher->ongoing_input_view, event); } } @@ -304,8 +303,7 @@ void view_dispatcher_handle_custom_event(ViewDispatcher* view_dispatcher, uint32 } // If custom event is not consumed in View, call callback if(!is_consumed && view_dispatcher->custom_event_callback) { - is_consumed = - view_dispatcher->custom_event_callback(view_dispatcher->event_context, event); + view_dispatcher->custom_event_callback(view_dispatcher->event_context, event); } } @@ -321,6 +319,13 @@ void view_dispatcher_send_custom_event(ViewDispatcher* view_dispatcher, uint32_t furi_message_queue_put(view_dispatcher->queue, &message, FuriWaitForever) == FuriStatusOk); } +static const ViewPortOrientation view_dispatcher_view_port_orientation_table[] = { + [ViewOrientationVertical] = ViewPortOrientationVertical, + [ViewOrientationVerticalFlip] = ViewPortOrientationVerticalFlip, + [ViewOrientationHorizontal] = ViewPortOrientationHorizontal, + [ViewOrientationHorizontalFlip] = ViewPortOrientationHorizontalFlip, +}; + void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* view) { furi_assert(view_dispatcher); // Dispatch view exit event @@ -331,10 +336,13 @@ void view_dispatcher_set_current_view(ViewDispatcher* view_dispatcher, View* vie view_dispatcher->current_view = view; // Dispatch view enter event if(view_dispatcher->current_view) { - if(view->orientation == ViewOrientationVertical) - view_port_set_orientation(view_dispatcher->view_port, ViewPortOrientationVertical); - else if(view->orientation == ViewOrientationHorizontal) - view_port_set_orientation(view_dispatcher->view_port, ViewPortOrientationHorizontal); + ViewPortOrientation orientation = + view_dispatcher_view_port_orientation_table[view->orientation]; + if(view_port_get_orientation(view_dispatcher->view_port) != orientation) { + view_port_set_orientation(view_dispatcher->view_port, orientation); + // we just rotated input keys, now it's time to sacrifice some input + view_dispatcher->ongoing_input = 0; + } view_enter(view_dispatcher->current_view); view_port_enabled_set(view_dispatcher->view_port, true); view_port_update(view_dispatcher->view_port); diff --git a/applications/gui/view_dispatcher.h b/applications/services/gui/view_dispatcher.h old mode 100755 new mode 100644 similarity index 100% rename from applications/gui/view_dispatcher.h rename to applications/services/gui/view_dispatcher.h diff --git a/applications/gui/view_dispatcher_i.h b/applications/services/gui/view_dispatcher_i.h similarity index 100% rename from applications/gui/view_dispatcher_i.h rename to applications/services/gui/view_dispatcher_i.h diff --git a/applications/dialogs/view_holder.c b/applications/services/gui/view_holder.c similarity index 94% rename from applications/dialogs/view_holder.c rename to applications/services/gui/view_holder.c index d2ae7750373..7ab0a8e1a5f 100644 --- a/applications/dialogs/view_holder.c +++ b/applications/services/gui/view_holder.c @@ -50,6 +50,10 @@ void view_holder_free(ViewHolder* view_holder) { void view_holder_set_view(ViewHolder* view_holder, View* view) { furi_assert(view_holder); if(view_holder->view) { + if(view_holder->view->exit_callback) { + view_holder->view->exit_callback(view_holder->view->context); + } + view_set_update_callback(view_holder->view, NULL); view_set_update_callback_context(view_holder->view, NULL); } @@ -59,6 +63,10 @@ void view_holder_set_view(ViewHolder* view_holder, View* view) { if(view_holder->view) { view_set_update_callback(view_holder->view, view_holder_update); view_set_update_callback_context(view_holder->view, view_holder); + + if(view_holder->view->enter_callback) { + view_holder->view->enter_callback(view_holder->view->context); + } } } diff --git a/applications/dialogs/view_holder.h b/applications/services/gui/view_holder.h similarity index 100% rename from applications/dialogs/view_holder.h rename to applications/services/gui/view_holder.h diff --git a/applications/gui/view_i.h b/applications/services/gui/view_i.h similarity index 100% rename from applications/gui/view_i.h rename to applications/services/gui/view_i.h diff --git a/applications/services/gui/view_port.c b/applications/services/gui/view_port.c new file mode 100644 index 00000000000..25f670a7c15 --- /dev/null +++ b/applications/services/gui/view_port.c @@ -0,0 +1,243 @@ +#include "view_port_i.h" + +#include +#include + +#include "gui.h" +#include "gui_i.h" + +#define TAG "ViewPort" + +_Static_assert(ViewPortOrientationMAX == 4, "Incorrect ViewPortOrientation count"); +_Static_assert( + (ViewPortOrientationHorizontal == 0 && ViewPortOrientationHorizontalFlip == 1 && + ViewPortOrientationVertical == 2 && ViewPortOrientationVerticalFlip == 3), + "Incorrect ViewPortOrientation order"); +_Static_assert(InputKeyMAX == 6, "Incorrect InputKey count"); +_Static_assert( + (InputKeyUp == 0 && InputKeyDown == 1 && InputKeyRight == 2 && InputKeyLeft == 3 && + InputKeyOk == 4 && InputKeyBack == 5), + "Incorrect InputKey order"); + +/** InputKey directional keys mappings for different screen orientations +* +*/ +static const InputKey view_port_input_mapping[ViewPortOrientationMAX][InputKeyMAX] = { + {InputKeyUp, + InputKeyDown, + InputKeyRight, + InputKeyLeft, + InputKeyOk, + InputKeyBack}, //ViewPortOrientationHorizontal + {InputKeyDown, + InputKeyUp, + InputKeyLeft, + InputKeyRight, + InputKeyOk, + InputKeyBack}, //ViewPortOrientationHorizontalFlip + {InputKeyRight, + InputKeyLeft, + InputKeyDown, + InputKeyUp, + InputKeyOk, + InputKeyBack}, //ViewPortOrientationVertical + {InputKeyLeft, + InputKeyRight, + InputKeyUp, + InputKeyDown, + InputKeyOk, + InputKeyBack}, //ViewPortOrientationVerticalFlip +}; + +static const InputKey view_port_left_hand_input_mapping[InputKeyMAX] = + {InputKeyDown, InputKeyUp, InputKeyLeft, InputKeyRight, InputKeyOk, InputKeyBack}; + +static const CanvasOrientation view_port_orientation_mapping[ViewPortOrientationMAX] = { + [ViewPortOrientationHorizontal] = CanvasOrientationHorizontal, + [ViewPortOrientationHorizontalFlip] = CanvasOrientationHorizontalFlip, + [ViewPortOrientationVertical] = CanvasOrientationVertical, + [ViewPortOrientationVerticalFlip] = CanvasOrientationVerticalFlip, +}; + +// 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(event->sequence_source != INPUT_SEQUENCE_SOURCE_HARDWARE) { + return; + } + + if(orientation == ViewPortOrientationHorizontal || + orientation == ViewPortOrientationHorizontalFlip) { + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient)) { + event->key = view_port_left_hand_input_mapping[event->key]; + } + } + event->key = view_port_input_mapping[orientation][event->key]; +} + +static void view_port_setup_canvas_orientation(const ViewPort* view_port, Canvas* canvas) { + CanvasOrientation orientation = view_port_orientation_mapping[view_port->orientation]; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient)) { + if(orientation == CanvasOrientationHorizontal) { + orientation = CanvasOrientationHorizontalFlip; + } else if(orientation == CanvasOrientationHorizontalFlip) { + orientation = CanvasOrientationHorizontal; + } + } + + canvas_set_orientation(canvas, orientation); +} + +ViewPort* view_port_alloc() { + ViewPort* view_port = malloc(sizeof(ViewPort)); + view_port->orientation = ViewPortOrientationHorizontal; + view_port->is_enabled = true; + view_port->mutex = furi_mutex_alloc(FuriMutexTypeRecursive); + return view_port; +} + +void view_port_free(ViewPort* view_port) { + furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + furi_check(view_port->gui == NULL); + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); + furi_mutex_free(view_port->mutex); + free(view_port); +} + +void view_port_set_width(ViewPort* view_port, uint8_t width) { + furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + view_port->width = width; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); +} + +uint8_t view_port_get_width(const ViewPort* view_port) { + furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + uint8_t width = view_port->width; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); + return width; +} + +void view_port_set_height(ViewPort* view_port, uint8_t height) { + furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + view_port->height = height; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); +} + +uint8_t view_port_get_height(const ViewPort* view_port) { + furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + uint8_t height = view_port->height; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); + return height; +} + +void view_port_enabled_set(ViewPort* view_port, bool enabled) { + furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + if(view_port->is_enabled != enabled) { + view_port->is_enabled = enabled; + if(view_port->gui) gui_update(view_port->gui); + } + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); +} + +bool view_port_is_enabled(const ViewPort* view_port) { + furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + bool is_enabled = view_port->is_enabled; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); + return is_enabled; +} + +void view_port_draw_callback_set(ViewPort* view_port, ViewPortDrawCallback callback, void* context) { + furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + view_port->draw_callback = callback; + view_port->draw_callback_context = context; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); +} + +void view_port_input_callback_set( + ViewPort* view_port, + ViewPortInputCallback callback, + void* context) { + furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + view_port->input_callback = callback; + view_port->input_callback_context = context; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); +} + +void view_port_update(ViewPort* view_port) { + furi_assert(view_port); + + // We are not going to lockup system, but will notify you instead + // Make sure that you don't call viewport methods inside of another mutex, especially one that is used in draw call + if(furi_mutex_acquire(view_port->mutex, 2) != FuriStatusOk) { + FURI_LOG_W(TAG, "ViewPort lockup: see %s:%d", __FILE__, __LINE__ - 3); + } + + if(view_port->gui && view_port->is_enabled) gui_update(view_port->gui); + furi_mutex_release(view_port->mutex); +} + +void view_port_gui_set(ViewPort* view_port, Gui* gui) { + furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + view_port->gui = gui; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); +} + +void view_port_draw(ViewPort* view_port, Canvas* canvas) { + furi_assert(view_port); + furi_assert(canvas); + + // We are not going to lockup system, but will notify you instead + // Make sure that you don't call viewport methods inside of another mutex, especially one that is used in draw call + if(furi_mutex_acquire(view_port->mutex, 2) != FuriStatusOk) { + FURI_LOG_W(TAG, "ViewPort lockup: see %s:%d", __FILE__, __LINE__ - 3); + } + + furi_check(view_port->gui); + + if(view_port->draw_callback) { + view_port_setup_canvas_orientation(view_port, canvas); + view_port->draw_callback(canvas, view_port->draw_callback_context); + } + + furi_mutex_release(view_port->mutex); +} + +void view_port_input(ViewPort* view_port, InputEvent* event) { + furi_assert(view_port); + furi_assert(event); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + furi_check(view_port->gui); + + if(view_port->input_callback) { + ViewPortOrientation orientation = view_port_get_orientation(view_port); + view_port_map_input(event, orientation); + view_port->input_callback(event, view_port->input_callback_context); + } + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); +} + +void view_port_set_orientation(ViewPort* view_port, ViewPortOrientation orientation) { + furi_assert(view_port); + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + view_port->orientation = orientation; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); +} + +ViewPortOrientation view_port_get_orientation(const ViewPort* view_port) { + furi_check(furi_mutex_acquire(view_port->mutex, FuriWaitForever) == FuriStatusOk); + ViewPortOrientation orientation = view_port->orientation; + furi_check(furi_mutex_release(view_port->mutex) == FuriStatusOk); + return orientation; +} diff --git a/applications/gui/view_port.h b/applications/services/gui/view_port.h similarity index 89% rename from applications/gui/view_port.h rename to applications/services/gui/view_port.h index 96f2798e21b..752fc46ba64 100644 --- a/applications/gui/view_port.h +++ b/applications/services/gui/view_port.h @@ -16,7 +16,10 @@ typedef struct ViewPort ViewPort; typedef enum { ViewPortOrientationHorizontal, + ViewPortOrientationHorizontalFlip, ViewPortOrientationVertical, + ViewPortOrientationVerticalFlip, + ViewPortOrientationMAX, /**< Special value, don't use it */ } ViewPortOrientation; /** ViewPort Draw callback @@ -53,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. * @@ -63,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. * @@ -72,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/gui/view_port_i.h b/applications/services/gui/view_port_i.h similarity index 98% rename from applications/gui/view_port_i.h rename to applications/services/gui/view_port_i.h index 90f48ac930a..444e1a27c18 100644 --- a/applications/gui/view_port_i.h +++ b/applications/services/gui/view_port_i.h @@ -10,6 +10,7 @@ struct ViewPort { Gui* gui; + FuriMutex* mutex; bool is_enabled; ViewPortOrientation orientation; diff --git a/applications/gui/view_stack.c b/applications/services/gui/view_stack.c similarity index 99% rename from applications/gui/view_stack.c rename to applications/services/gui/view_stack.c index 3bb00fbf25a..0a106f1ba57 100644 --- a/applications/gui/view_stack.c +++ b/applications/services/gui/view_stack.c @@ -1,8 +1,9 @@ -#include "gui/view.h" -#include #include "view_stack.h" #include "view_i.h" +#include +#include + #define MAX_VIEWS 3 typedef struct { diff --git a/applications/gui/view_stack.h b/applications/services/gui/view_stack.h similarity index 100% rename from applications/gui/view_stack.h rename to applications/services/gui/view_stack.h diff --git a/applications/services/input/application.fam b/applications/services/input/application.fam new file mode 100644 index 00000000000..d344fc350a8 --- /dev/null +++ b/applications/services/input/application.fam @@ -0,0 +1,10 @@ +App( + appid="input", + name="InputSrv", + apptype=FlipperAppType.SERVICE, + entry_point="input_srv", + cdefines=["SRV_INPUT"], + stack_size=1 * 1024, + order=80, + sdk_headers=["input.h"], +) diff --git a/applications/services/input/input.c b/applications/services/input/input.c new file mode 100644 index 00000000000..216aa39b2e6 --- /dev/null +++ b/applications/services/input/input.c @@ -0,0 +1,145 @@ +#include "input_i.h" + +// #define INPUT_DEBUG + +#define GPIO_Read(input_pin) (furi_hal_gpio_read(input_pin.pin->gpio) ^ (input_pin.pin->inverted)) + +static Input* input = NULL; + +void input_press_timer_callback(void* arg) { + InputPinState* input_pin = arg; + InputEvent event; + event.sequence_source = INPUT_SEQUENCE_SOURCE_HARDWARE; + event.sequence_counter = input_pin->counter; + event.key = input_pin->pin->key; + input_pin->press_counter++; + if(input_pin->press_counter == INPUT_LONG_PRESS_COUNTS) { + event.type = InputTypeLong; + furi_pubsub_publish(input->event_pubsub, &event); + } else if(input_pin->press_counter > INPUT_LONG_PRESS_COUNTS) { + input_pin->press_counter--; + event.type = InputTypeRepeat; + furi_pubsub_publish(input->event_pubsub, &event); + } +} + +void input_isr(void* _ctx) { + UNUSED(_ctx); + furi_thread_flags_set(input->thread_id, INPUT_THREAD_FLAG_ISR); +} + +const char* input_get_key_name(InputKey key) { + for(size_t i = 0; i < input_pins_count; i++) { + if(input_pins[i].key == key) { + return input_pins[i].name; + } + } + return "Unknown"; +} + +const char* input_get_type_name(InputType type) { + switch(type) { + case InputTypePress: + return "Press"; + case InputTypeRelease: + return "Release"; + case InputTypeShort: + return "Short"; + case InputTypeLong: + return "Long"; + case InputTypeRepeat: + return "Repeat"; + default: + return "Unknown"; + } +} + +int32_t input_srv(void* p) { + UNUSED(p); + input = malloc(sizeof(Input)); + input->thread_id = furi_thread_get_current_id(); + input->event_pubsub = furi_pubsub_alloc(); + furi_record_create(RECORD_INPUT_EVENTS, input->event_pubsub); + +#if INPUT_DEBUG + furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull); +#endif + +#ifdef SRV_CLI + input->cli = furi_record_open(RECORD_CLI); + cli_add_command(input->cli, "input", CliCommandFlagParallelSafe, input_cli, input); +#endif + + input->pin_states = malloc(input_pins_count * sizeof(InputPinState)); + + for(size_t i = 0; i < input_pins_count; i++) { + furi_hal_gpio_add_int_callback(input_pins[i].gpio, input_isr, NULL); + input->pin_states[i].pin = &input_pins[i]; + input->pin_states[i].state = GPIO_Read(input->pin_states[i]); + input->pin_states[i].debounce = INPUT_DEBOUNCE_TICKS_HALF; + input->pin_states[i].press_timer = furi_timer_alloc( + input_press_timer_callback, FuriTimerTypePeriodic, &input->pin_states[i]); + input->pin_states[i].press_counter = 0; + } + + while(1) { + bool is_changing = false; + for(size_t i = 0; i < input_pins_count; i++) { + bool state = GPIO_Read(input->pin_states[i]); + if(state) { + if(input->pin_states[i].debounce < INPUT_DEBOUNCE_TICKS) + input->pin_states[i].debounce += 1; + } else { + if(input->pin_states[i].debounce > 0) input->pin_states[i].debounce -= 1; + } + + if(input->pin_states[i].debounce > 0 && + input->pin_states[i].debounce < INPUT_DEBOUNCE_TICKS) { + is_changing = true; + } else if(input->pin_states[i].state != state) { + input->pin_states[i].state = state; + + // Common state info + InputEvent event; + event.sequence_source = INPUT_SEQUENCE_SOURCE_HARDWARE; + event.key = input->pin_states[i].pin->key; + + // Short / Long / Repeat timer routine + if(state) { + input->counter++; + input->pin_states[i].counter = input->counter; + event.sequence_counter = input->pin_states[i].counter; + furi_timer_start(input->pin_states[i].press_timer, INPUT_PRESS_TICKS); + } else { + event.sequence_counter = input->pin_states[i].counter; + furi_timer_stop(input->pin_states[i].press_timer); + while(furi_timer_is_running(input->pin_states[i].press_timer)) + furi_delay_tick(1); + if(input->pin_states[i].press_counter < INPUT_LONG_PRESS_COUNTS) { + event.type = InputTypeShort; + furi_pubsub_publish(input->event_pubsub, &event); + } + input->pin_states[i].press_counter = 0; + } + + // Send Press/Release event + event.type = input->pin_states[i].state ? InputTypePress : InputTypeRelease; + furi_pubsub_publish(input->event_pubsub, &event); + } + } + + if(is_changing) { +#if INPUT_DEBUG + furi_hal_gpio_write(&gpio_ext_pa4, 1); +#endif + furi_delay_tick(1); + } else { +#if INPUT_DEBUG + furi_hal_gpio_write(&gpio_ext_pa4, 0); +#endif + furi_thread_flags_wait(INPUT_THREAD_FLAG_ISR, FuriFlagWaitAny, FuriWaitForever); + } + } + + return 0; +} diff --git a/applications/services/input/input.h b/applications/services/input/input.h new file mode 100644 index 00000000000..a62e84569b6 --- /dev/null +++ b/applications/services/input/input.h @@ -0,0 +1,57 @@ +/** + * @file input.h + * Input: main API + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define RECORD_INPUT_EVENTS "input_events" +#define INPUT_SEQUENCE_SOURCE_HARDWARE (0u) +#define INPUT_SEQUENCE_SOURCE_SOFTWARE (1u) + +/** Input Types + * Some of them are physical events and some logical + */ +typedef enum { + InputTypePress, /**< Press event, emitted after debounce */ + InputTypeRelease, /**< Release event, emitted after debounce */ + InputTypeShort, /**< Short event, emitted after InputTypeRelease done within INPUT_LONG_PRESS interval */ + InputTypeLong, /**< Long event, emitted after INPUT_LONG_PRESS_COUNTS interval, asynchronous to InputTypeRelease */ + InputTypeRepeat, /**< Repeat event, emitted with INPUT_LONG_PRESS_COUNTS period after InputTypeLong event */ + InputTypeMAX, /**< Special value for exceptional */ +} InputType; + +/** Input Event, dispatches with FuriPubSub */ +typedef struct { + union { + uint32_t sequence; + struct { + uint8_t sequence_source : 2; + uint32_t sequence_counter : 30; + }; + }; + InputKey key; + InputType type; +} InputEvent; + +/** Get human readable input key name + * @param key - InputKey + * @return string + */ +const char* input_get_key_name(InputKey key); + +/** Get human readable input type name + * @param type - InputType + * @return string + */ +const char* input_get_type_name(InputType type); + +#ifdef __cplusplus +} +#endif diff --git a/applications/services/input/input_cli.c b/applications/services/input/input_cli.c new file mode 100644 index 00000000000..f7e904b171a --- /dev/null +++ b/applications/services/input/input_cli.c @@ -0,0 +1,125 @@ +#include "input_i.h" + +#include +#include +#include + +static void input_cli_usage() { + printf("Usage:\r\n"); + printf("input \r\n"); + printf("Cmd list:\r\n"); + printf("\tdump\t\t\t - dump input events\r\n"); + printf("\tsend \t - send input event\r\n"); +} + +static void input_cli_dump_events_callback(const void* value, void* ctx) { + furi_assert(value); + furi_assert(ctx); + FuriMessageQueue* input_queue = ctx; + furi_message_queue_put(input_queue, value, FuriWaitForever); +} + +static void input_cli_dump(Cli* cli, FuriString* args, Input* input) { + UNUSED(args); + FuriMessageQueue* input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + FuriPubSubSubscription* input_subscription = + furi_pubsub_subscribe(input->event_pubsub, input_cli_dump_events_callback, input_queue); + + InputEvent input_event; + printf("Press CTRL+C to stop\r\n"); + while(!cli_cmd_interrupt_received(cli)) { + if(furi_message_queue_get(input_queue, &input_event, 100) == FuriStatusOk) { + printf( + "key: %s type: %s\r\n", + input_get_key_name(input_event.key), + input_get_type_name(input_event.type)); + } + } + + furi_pubsub_unsubscribe(input->event_pubsub, input_subscription); + furi_message_queue_free(input_queue); +} + +static void input_cli_send_print_usage() { + printf("Invalid arguments. Usage:\r\n"); + printf("\tinput send \r\n"); + printf("\t\t \t - one of 'up', 'down', 'left', 'right', 'back', 'ok'\r\n"); + printf("\t\t \t - one of 'press', 'release', 'short', 'long'\r\n"); +} + +static void input_cli_send(Cli* cli, FuriString* args, Input* input) { + UNUSED(cli); + InputEvent event; + FuriString* key_str; + key_str = furi_string_alloc(); + bool parsed = false; + + do { + // Parse Key + if(!args_read_string_and_trim(args, key_str)) { + break; + } + if(!furi_string_cmp(key_str, "up")) { + event.key = InputKeyUp; + } else if(!furi_string_cmp(key_str, "down")) { + event.key = InputKeyDown; + } else if(!furi_string_cmp(key_str, "left")) { + event.key = InputKeyLeft; + } else if(!furi_string_cmp(key_str, "right")) { + event.key = InputKeyRight; + } else if(!furi_string_cmp(key_str, "ok")) { + event.key = InputKeyOk; + } else if(!furi_string_cmp(key_str, "back")) { + event.key = InputKeyBack; + } else { + break; + } + // Parse Type + if(!furi_string_cmp(args, "press")) { + event.type = InputTypePress; + } else if(!furi_string_cmp(args, "release")) { + event.type = InputTypeRelease; + } else if(!furi_string_cmp(args, "short")) { + event.type = InputTypeShort; + } else if(!furi_string_cmp(args, "long")) { + event.type = InputTypeLong; + } else { + break; + } + parsed = true; + } while(false); + + if(parsed) { //-V547 + furi_pubsub_publish(input->event_pubsub, &event); + } else { + input_cli_send_print_usage(); + } + furi_string_free(key_str); +} + +void input_cli(Cli* cli, FuriString* args, void* context) { + furi_assert(cli); + furi_assert(context); + Input* input = context; + FuriString* cmd; + cmd = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, cmd)) { + input_cli_usage(); + break; + } + if(furi_string_cmp_str(cmd, "dump") == 0) { + input_cli_dump(cli, args, input); + break; + } + if(furi_string_cmp_str(cmd, "send") == 0) { + input_cli_send(cli, args, input); + break; + } + + input_cli_usage(); + } while(false); + + furi_string_free(cmd); +} diff --git a/applications/input/input_i.h b/applications/services/input/input_i.h similarity index 92% rename from applications/input/input_i.h rename to applications/services/input/input_i.h index f709e0487c9..14d8b0735a5 100644 --- a/applications/input/input_i.h +++ b/applications/services/input/input_i.h @@ -11,7 +11,6 @@ #include #include #include -#include #include #define INPUT_DEBOUNCE_TICKS_HALF (INPUT_DEBOUNCE_TICKS / 2) @@ -46,4 +45,4 @@ void input_press_timer_callback(void* arg); void input_isr(void* _ctx); /** Input CLI command handler */ -void input_cli(Cli* cli, string_t args, void* context); +void input_cli(Cli* cli, FuriString* args, void* context); diff --git a/applications/services/loader/application.fam b/applications/services/loader/application.fam new file mode 100644 index 00000000000..f4d006e076a --- /dev/null +++ b/applications/services/loader/application.fam @@ -0,0 +1,23 @@ +App( + appid="loader", + name="LoaderSrv", + apptype=FlipperAppType.SERVICE, + entry_point="loader_srv", + cdefines=["SRV_LOADER"], + requires=["gui"], + provides=["loader_start"], + stack_size=2 * 1024, + order=90, + sdk_headers=[ + "loader.h", + "firmware_api/firmware_api.h", + ], +) + +App( + appid="loader_start", + apptype=FlipperAppType.STARTUP, + entry_point="loader_on_system_start", + requires=["loader"], + order=90, +) diff --git a/applications/services/loader/firmware_api/firmware_api.cpp b/applications/services/loader/firmware_api/firmware_api.cpp new file mode 100644 index 00000000000..47554f6280c --- /dev/null +++ b/applications/services/loader/firmware_api/firmware_api.cpp @@ -0,0 +1,41 @@ +#include "firmware_api.h" + +#include +#include + +/* Generated table */ +#include + +#include + +static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!"); + +#ifdef APP_UNIT_TESTS +constexpr HashtableApiInterface mock_elf_api_interface{ + { + .api_version_major = 0, + .api_version_minor = 0, + .resolver_callback = &elf_resolve_from_hashtable, + }, + .table_cbegin = nullptr, + .table_cend = nullptr, +}; + +const ElfApiInterface* const firmware_api_interface = &mock_elf_api_interface; +#else +constexpr HashtableApiInterface elf_api_interface{ + { + .api_version_major = (elf_api_version >> 16), + .api_version_minor = (elf_api_version & 0xFFFF), + .resolver_callback = &elf_resolve_from_hashtable, + }, + .table_cbegin = elf_api_table.cbegin(), + .table_cend = elf_api_table.cend(), +}; +const ElfApiInterface* const firmware_api_interface = &elf_api_interface; +#endif + +extern "C" void furi_hal_info_get_api_version(uint16_t* major, uint16_t* minor) { + *major = firmware_api_interface->api_version_major; + *minor = firmware_api_interface->api_version_minor; +} \ No newline at end of file diff --git a/applications/services/loader/firmware_api/firmware_api.h b/applications/services/loader/firmware_api/firmware_api.h new file mode 100644 index 00000000000..c73ae896085 --- /dev/null +++ b/applications/services/loader/firmware_api/firmware_api.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const ElfApiInterface* const firmware_api_interface; diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c new file mode 100644 index 00000000000..29ec86ac669 --- /dev/null +++ b/applications/services/loader/loader.c @@ -0,0 +1,541 @@ +#include "loader.h" +#include "core/core_defines.h" +#include "loader_i.h" +#include +#include +#include +#include + +#include +#include +#include +#include + +#define TAG "Loader" +#define LOADER_MAGIC_THREAD_VALUE 0xDEADBEEF + +// helpers + +static const char* loader_find_external_application_by_name(const char* app_name) { + for(size_t i = 0; i < FLIPPER_EXTERNAL_APPS_COUNT; i++) { + if(strcmp(FLIPPER_EXTERNAL_APPS[i].name, app_name) == 0) { + return FLIPPER_EXTERNAL_APPS[i].path; + } + } + + return NULL; +} + +// API + +LoaderStatus + loader_start(Loader* loader, const char* name, const char* args, FuriString* error_message) { + LoaderMessage message; + LoaderMessageLoaderStatusResult result; + + message.type = LoaderMessageTypeStartByName; + message.start.name = name; + message.start.args = args; + message.start.error_message = error_message; + message.api_lock = api_lock_alloc_locked(); + message.status_value = &result; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); + return result.value; +} + +LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args) { + FuriString* error_message = furi_string_alloc(); + LoaderStatus status = loader_start(loader, name, args, error_message); + + if(status == LoaderStatusErrorUnknownApp && + loader_find_external_application_by_name(name) != NULL) { + // Special case for external apps + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Update needed", 64, 3, AlignCenter, AlignTop); + dialog_message_set_buttons(message, NULL, NULL, NULL); + dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17); + dialog_message_set_text( + message, "Update firmware\nto run this app", 3, 26, AlignLeft, AlignTop); + dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + } else if(status == LoaderStatusErrorUnknownApp || status == LoaderStatusErrorInternal) { + // TODO FL-3522: we have many places where we can emit a double start, ex: desktop, menu + // so i prefer to not show LoaderStatusErrorAppStarted error message for now + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Error", 64, 0, AlignCenter, AlignTop); + dialog_message_set_buttons(message, NULL, NULL, NULL); + + furi_string_replace(error_message, "/ext/apps/", ""); + furi_string_replace(error_message, ", ", "\n"); + furi_string_replace(error_message, ": ", "\n"); + + dialog_message_set_text( + message, furi_string_get_cstr(error_message), 64, 35, AlignCenter, AlignCenter); + + dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + } + + furi_string_free(error_message); + return status; +} + +bool loader_lock(Loader* loader) { + LoaderMessage message; + LoaderMessageBoolResult result; + message.type = LoaderMessageTypeLock; + message.api_lock = api_lock_alloc_locked(); + message.bool_value = &result; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); + return result.value; +} + +void loader_unlock(Loader* loader) { + LoaderMessage message; + message.type = LoaderMessageTypeUnlock; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); +} + +bool loader_is_locked(Loader* loader) { + LoaderMessage message; + LoaderMessageBoolResult result; + message.type = LoaderMessageTypeIsLocked; + message.api_lock = api_lock_alloc_locked(); + message.bool_value = &result; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); + return result.value; +} + +void loader_show_menu(Loader* loader) { + LoaderMessage message; + message.type = LoaderMessageTypeShowMenu; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); +} + +FuriPubSub* loader_get_pubsub(Loader* loader) { + furi_assert(loader); + // it's safe to return pubsub without locking + // because it's never freed and loader is never exited + // also the loader instance cannot be obtained until the pubsub is created + return loader->pubsub; +} + +// callbacks + +static void loader_menu_closed_callback(void* context) { + Loader* loader = context; + LoaderMessage message; + message.type = LoaderMessageTypeMenuClosed; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); +} + +static void loader_applications_closed_callback(void* context) { + Loader* loader = context; + LoaderMessage message; + message.type = LoaderMessageTypeApplicationsClosed; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); +} + +static void loader_thread_state_callback(FuriThreadState thread_state, void* context) { + furi_assert(context); + + Loader* loader = context; + + if(thread_state == FuriThreadStateRunning) { + LoaderEvent event; + event.type = LoaderEventTypeApplicationStarted; + furi_pubsub_publish(loader->pubsub, &event); + } else if(thread_state == FuriThreadStateStopped) { + LoaderMessage message; + message.type = LoaderMessageTypeAppClosed; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); + } +} + +// implementation + +static Loader* loader_alloc() { + Loader* loader = malloc(sizeof(Loader)); + loader->pubsub = furi_pubsub_alloc(); + loader->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage)); + loader->loader_menu = NULL; + loader->loader_applications = NULL; + loader->app.args = NULL; + loader->app.thread = NULL; + loader->app.insomniac = false; + loader->app.fap = NULL; + return loader; +} + +static FlipperInternalApplication const* loader_find_application_by_name_in_list( + const char* name, + const FlipperInternalApplication* list, + const uint32_t n_apps) { + for(size_t i = 0; i < n_apps; i++) { + if((strcmp(name, list[i].name) == 0) || (strcmp(name, list[i].appid) == 0)) { + return &list[i]; + } + } + return NULL; +} + +static const FlipperInternalApplication* loader_find_application_by_name(const char* name) { + const struct { + const FlipperInternalApplication* list; + const uint32_t count; + } lists[] = { + {FLIPPER_SETTINGS_APPS, FLIPPER_SETTINGS_APPS_COUNT}, + {FLIPPER_SYSTEM_APPS, FLIPPER_SYSTEM_APPS_COUNT}, + {FLIPPER_DEBUG_APPS, FLIPPER_DEBUG_APPS_COUNT}, + }; + + for(size_t i = 0; i < COUNT_OF(lists); i++) { + const FlipperInternalApplication* application = + loader_find_application_by_name_in_list(name, lists[i].list, lists[i].count); + if(application) { + return application; + } + } + + return NULL; +} + +static void loader_start_app_thread(Loader* loader, FlipperInternalApplicationFlag flags) { + // setup heap trace + FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); + if(mode > FuriHalRtcHeapTrackModeNone) { + furi_thread_enable_heap_trace(loader->app.thread); + } else { + furi_thread_disable_heap_trace(loader->app.thread); + } + + // setup insomnia + if(!(flags & FlipperInternalApplicationFlagInsomniaSafe)) { + furi_hal_power_insomnia_enter(); + loader->app.insomniac = true; + } else { + loader->app.insomniac = false; + } + + // setup thread state callbacks + furi_thread_set_state_context(loader->app.thread, loader); + furi_thread_set_state_callback(loader->app.thread, loader_thread_state_callback); + + // start app thread + furi_thread_start(loader->app.thread); +} + +static void loader_start_internal_app( + Loader* loader, + const FlipperInternalApplication* app, + const char* args) { + FURI_LOG_I(TAG, "Starting %s", app->name); + + // store args + furi_assert(loader->app.args == NULL); + if(args && strlen(args) > 0) { + loader->app.args = strdup(args); + } + + loader->app.thread = + furi_thread_alloc_ex(app->name, app->stack_size, app->app, loader->app.args); + furi_thread_set_appid(loader->app.thread, app->appid); + + loader_start_app_thread(loader, app->flags); +} + +static void loader_log_status_error( + LoaderStatus status, + FuriString* error_message, + const char* format, + va_list args) { + if(error_message) { + furi_string_vprintf(error_message, format, args); + FURI_LOG_E(TAG, "Status [%d]: %s", status, furi_string_get_cstr(error_message)); + } else { + FURI_LOG_E(TAG, "Status [%d]", status); + } +} + +static LoaderStatus loader_make_status_error( + LoaderStatus status, + FuriString* error_message, + const char* format, + ...) { + va_list args; + va_start(args, format); + loader_log_status_error(status, error_message, format, args); + va_end(args); + return status; +} + +static LoaderStatus loader_make_success_status(FuriString* error_message) { + if(error_message) { + furi_string_set(error_message, "App started"); + } + + return LoaderStatusOk; +} + +static LoaderStatus loader_start_external_app( + Loader* loader, + Storage* storage, + const char* path, + const char* args, + FuriString* error_message) { + LoaderStatus status = loader_make_success_status(error_message); + + do { + loader->app.fap = flipper_application_alloc(storage, firmware_api_interface); + size_t start = furi_get_tick(); + + FURI_LOG_I(TAG, "Loading %s", path); + + FlipperApplicationPreloadStatus preload_res = + flipper_application_preload(loader->app.fap, path); + if(preload_res != FlipperApplicationPreloadStatusSuccess) { + const char* err_msg = flipper_application_preload_status_to_string(preload_res); + status = loader_make_status_error( + LoaderStatusErrorInternal, error_message, "Preload failed, %s: %s", path, err_msg); + break; + } + + FlipperApplicationLoadStatus load_status = + flipper_application_map_to_memory(loader->app.fap); + if(load_status != FlipperApplicationLoadStatusSuccess) { + const char* err_msg = flipper_application_load_status_to_string(load_status); + status = loader_make_status_error( + LoaderStatusErrorInternal, error_message, "Load failed, %s: %s", path, err_msg); + break; + } + + FURI_LOG_I(TAG, "Loaded in %zums", (size_t)(furi_get_tick() - start)); + + loader->app.thread = flipper_application_alloc_thread(loader->app.fap, args); + FuriString* app_name = furi_string_alloc(); + path_extract_filename_no_ext(path, app_name); + furi_thread_set_appid(loader->app.thread, furi_string_get_cstr(app_name)); + furi_string_free(app_name); + + /* This flag is set by the debugger - to break on app start */ + if(furi_hal_debug_is_gdb_session_active()) { + FURI_LOG_W(TAG, "Triggering BP for debugger"); + /* After hitting this, you can set breakpoints in your .fap's code + * Note that you have to toggle breakpoints that were set before */ + __asm volatile("bkpt 0"); + } + + loader_start_app_thread(loader, FlipperInternalApplicationFlagDefault); + } while(0); + + if(status != LoaderStatusOk) { + flipper_application_free(loader->app.fap); + loader->app.fap = NULL; + } + + return status; +} + +// process messages + +static void loader_do_menu_show(Loader* loader) { + if(!loader->loader_menu) { + loader->loader_menu = loader_menu_alloc(loader_menu_closed_callback, loader); + } +} + +static void loader_do_menu_closed(Loader* loader) { + if(loader->loader_menu) { + loader_menu_free(loader->loader_menu); + loader->loader_menu = NULL; + } +} + +static void loader_do_applications_show(Loader* loader) { + if(!loader->loader_applications) { + loader->loader_applications = + loader_applications_alloc(loader_applications_closed_callback, loader); + } +} + +static void loader_do_applications_closed(Loader* loader) { + if(loader->loader_applications) { + loader_applications_free(loader->loader_applications); + loader->loader_applications = NULL; + } +} + +static bool loader_do_is_locked(Loader* loader) { + return loader->app.thread != NULL; +} + +static LoaderStatus loader_do_start_by_name( + Loader* loader, + const char* name, + const char* args, + FuriString* error_message) { + LoaderStatus status; + do { + // check lock + if(loader_do_is_locked(loader)) { + const char* current_thread_name = + furi_thread_get_name(furi_thread_get_id(loader->app.thread)); + status = loader_make_status_error( + LoaderStatusErrorAppStarted, + error_message, + "Loader is locked, please close the \"%s\" first", + current_thread_name); + break; + } + + // check internal apps + { + const FlipperInternalApplication* app = loader_find_application_by_name(name); + if(app) { + loader_start_internal_app(loader, app, args); + status = loader_make_success_status(error_message); + break; + } + } + + // check Applications + if(strcmp(name, LOADER_APPLICATIONS_NAME) == 0) { + loader_do_applications_show(loader); + status = loader_make_success_status(error_message); + break; + } + + // check External Applications + { + const char* path = loader_find_external_application_by_name(name); + if(path) { + name = path; + } + } + + // check Faps + { + Storage* storage = furi_record_open(RECORD_STORAGE); + if(storage_file_exists(storage, name)) { + status = loader_start_external_app(loader, storage, name, args, error_message); + furi_record_close(RECORD_STORAGE); + break; + } + furi_record_close(RECORD_STORAGE); + } + + status = loader_make_status_error( + LoaderStatusErrorUnknownApp, error_message, "Application \"%s\" not found", name); + } while(false); + + return status; +} + +static bool loader_do_lock(Loader* loader) { + if(loader->app.thread) { + return false; + } + + loader->app.thread = (FuriThread*)LOADER_MAGIC_THREAD_VALUE; + return true; +} + +static void loader_do_unlock(Loader* loader) { + furi_check(loader->app.thread == (FuriThread*)LOADER_MAGIC_THREAD_VALUE); + loader->app.thread = NULL; +} + +static void loader_do_app_closed(Loader* loader) { + furi_assert(loader->app.thread); + + furi_thread_join(loader->app.thread); + FURI_LOG_I(TAG, "App returned: %li", furi_thread_get_return_code(loader->app.thread)); + + if(loader->app.args) { + free(loader->app.args); + loader->app.args = NULL; + } + + if(loader->app.insomniac) { + furi_hal_power_insomnia_exit(); + } + + if(loader->app.fap) { + flipper_application_free(loader->app.fap); + loader->app.fap = NULL; + loader->app.thread = NULL; + } else { + furi_thread_free(loader->app.thread); + loader->app.thread = NULL; + } + + FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap()); + + LoaderEvent event; + event.type = LoaderEventTypeApplicationStopped; + furi_pubsub_publish(loader->pubsub, &event); +} + +// app + +int32_t loader_srv(void* p) { + UNUSED(p); + Loader* loader = loader_alloc(); + furi_record_create(RECORD_LOADER, loader); + + FURI_LOG_I(TAG, "Executing system start hooks"); + for(size_t i = 0; i < FLIPPER_ON_SYSTEM_START_COUNT; i++) { + FLIPPER_ON_SYSTEM_START[i](); + } + + if((furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) && FLIPPER_AUTORUN_APP_NAME && + strlen(FLIPPER_AUTORUN_APP_NAME)) { + FURI_LOG_I(TAG, "Starting autorun app: %s", FLIPPER_AUTORUN_APP_NAME); + loader_do_start_by_name(loader, FLIPPER_AUTORUN_APP_NAME, NULL, NULL); + } + + LoaderMessage message; + while(true) { + if(furi_message_queue_get(loader->queue, &message, FuriWaitForever) == FuriStatusOk) { + switch(message.type) { + case LoaderMessageTypeStartByName: + message.status_value->value = loader_do_start_by_name( + loader, message.start.name, message.start.args, message.start.error_message); + api_lock_unlock(message.api_lock); + break; + case LoaderMessageTypeShowMenu: + loader_do_menu_show(loader); + break; + case LoaderMessageTypeMenuClosed: + loader_do_menu_closed(loader); + break; + case LoaderMessageTypeIsLocked: + message.bool_value->value = loader_do_is_locked(loader); + api_lock_unlock(message.api_lock); + break; + case LoaderMessageTypeAppClosed: + loader_do_app_closed(loader); + break; + case LoaderMessageTypeLock: + message.bool_value->value = loader_do_lock(loader); + api_lock_unlock(message.api_lock); + break; + case LoaderMessageTypeUnlock: + loader_do_unlock(loader); + break; + case LoaderMessageTypeApplicationsClosed: + loader_do_applications_closed(loader); + break; + } + } + } + + return 0; +} \ No newline at end of file diff --git a/applications/services/loader/loader.h b/applications/services/loader/loader.h new file mode 100644 index 00000000000..cca65628f88 --- /dev/null +++ b/applications/services/loader/loader.h @@ -0,0 +1,84 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define RECORD_LOADER "loader" +#define LOADER_APPLICATIONS_NAME "Apps" + +typedef struct Loader Loader; + +typedef enum { + LoaderStatusOk, + LoaderStatusErrorAppStarted, + LoaderStatusErrorUnknownApp, + LoaderStatusErrorInternal, +} LoaderStatus; + +typedef enum { + LoaderEventTypeApplicationStarted, + LoaderEventTypeApplicationStopped +} LoaderEventType; + +typedef struct { + LoaderEventType type; +} LoaderEvent; + +/** + * @brief Start application + * @param[in] instance loader instance + * @param[in] name application name or id + * @param[in] args application arguments + * @param[out] error_message detailed error message, can be NULL + * @return LoaderStatus + */ +LoaderStatus + loader_start(Loader* instance, const char* name, const char* args, FuriString* error_message); + +/** + * @brief Start application with GUI error message + * @param[in] instance loader instance + * @param[in] name application name or id + * @param[in] args application arguments + * @return LoaderStatus + */ +LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args); + +/** + * @brief Lock application start + * @param[in] instance loader instance + * @return true on success + */ +bool loader_lock(Loader* instance); + +/** + * @brief Unlock application start + * @param[in] instance loader instance + */ +void loader_unlock(Loader* instance); + +/** + * @brief Check if loader is locked + * @param[in] instance loader instance + * @return true if locked + */ +bool loader_is_locked(Loader* instance); + +/** + * @brief Show loader menu + * @param[in] instance loader instance + */ +void loader_show_menu(Loader* instance); + +/** + * @brief Get loader pubsub + * @param[in] instance loader instance + * @return FuriPubSub* + */ +FuriPubSub* loader_get_pubsub(Loader* instance); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/services/loader/loader_applications.c b/applications/services/loader/loader_applications.c new file mode 100644 index 00000000000..2e1de6134aa --- /dev/null +++ b/applications/services/loader/loader_applications.c @@ -0,0 +1,160 @@ +#include "loader.h" +#include "loader_applications.h" +#include +#include +#include +#include +#include +#include +#include + +#define TAG "LoaderApplications" + +struct LoaderApplications { + FuriThread* thread; + void (*closed_cb)(void*); + void* context; +}; + +static int32_t loader_applications_thread(void* p); + +LoaderApplications* loader_applications_alloc(void (*closed_cb)(void*), void* context) { + LoaderApplications* loader_applications = malloc(sizeof(LoaderApplications)); + loader_applications->thread = + furi_thread_alloc_ex(TAG, 768, loader_applications_thread, (void*)loader_applications); + loader_applications->closed_cb = closed_cb; + loader_applications->context = context; + furi_thread_start(loader_applications->thread); + return loader_applications; +} + +void loader_applications_free(LoaderApplications* loader_applications) { + furi_assert(loader_applications); + furi_thread_join(loader_applications->thread); + furi_thread_free(loader_applications->thread); + free(loader_applications); +} + +typedef struct { + FuriString* fap_path; + DialogsApp* dialogs; + Storage* storage; + Loader* loader; + + Gui* gui; + ViewHolder* view_holder; + Loading* loading; +} LoaderApplicationsApp; + +static LoaderApplicationsApp* loader_applications_app_alloc() { + LoaderApplicationsApp* app = malloc(sizeof(LoaderApplicationsApp)); //-V799 + app->fap_path = furi_string_alloc_set(EXT_PATH("apps")); + app->dialogs = furi_record_open(RECORD_DIALOGS); + app->storage = furi_record_open(RECORD_STORAGE); + app->loader = furi_record_open(RECORD_LOADER); + + app->gui = furi_record_open(RECORD_GUI); + app->view_holder = view_holder_alloc(); + app->loading = loading_alloc(); + + view_holder_attach_to_gui(app->view_holder, app->gui); + view_holder_set_view(app->view_holder, loading_get_view(app->loading)); + + return app; +} //-V773 + +static void loader_applications_app_free(LoaderApplicationsApp* app) { + furi_assert(app); + + view_holder_free(app->view_holder); + loading_free(app->loading); + furi_record_close(RECORD_GUI); + + furi_record_close(RECORD_LOADER); + furi_record_close(RECORD_DIALOGS); + furi_record_close(RECORD_STORAGE); + furi_string_free(app->fap_path); + free(app); +} + +static bool loader_applications_item_callback( + FuriString* path, + void* context, + uint8_t** icon_ptr, + FuriString* item_name) { + LoaderApplicationsApp* loader_applications_app = context; + furi_assert(loader_applications_app); + return flipper_application_load_name_and_icon( + path, loader_applications_app->storage, icon_ptr, item_name); +} + +static bool loader_applications_select_app(LoaderApplicationsApp* loader_applications_app) { + const DialogsFileBrowserOptions browser_options = { + .extension = ".fap", + .skip_assets = true, + .icon = &I_unknown_10px, + .hide_ext = true, + .item_loader_callback = loader_applications_item_callback, + .item_loader_context = loader_applications_app, + .base_path = EXT_PATH("apps"), + }; + + return dialog_file_browser_show( + loader_applications_app->dialogs, + loader_applications_app->fap_path, + loader_applications_app->fap_path, + &browser_options); +} + +#define APPLICATION_STOP_EVENT 1 + +static void loader_pubsub_callback(const void* message, void* context) { + const LoaderEvent* event = message; + const FuriThreadId thread_id = (FuriThreadId)context; + + if(event->type == LoaderEventTypeApplicationStopped) { + furi_thread_flags_set(thread_id, APPLICATION_STOP_EVENT); + } +} + +static void loader_applications_start_app(LoaderApplicationsApp* app) { + const char* name = furi_string_get_cstr(app->fap_path); + + dolphin_deed(DolphinDeedPluginStart); + + // load app + FuriThreadId thread_id = furi_thread_get_current_id(); + FuriPubSubSubscription* subscription = + furi_pubsub_subscribe(loader_get_pubsub(app->loader), loader_pubsub_callback, thread_id); + + LoaderStatus status = loader_start_with_gui_error(app->loader, name, NULL); + + if(status == LoaderStatusOk) { + furi_thread_flags_wait(APPLICATION_STOP_EVENT, FuriFlagWaitAny, FuriWaitForever); + } + + furi_pubsub_unsubscribe(loader_get_pubsub(app->loader), subscription); +} + +static int32_t loader_applications_thread(void* p) { + LoaderApplications* loader_applications = p; + LoaderApplicationsApp* app = loader_applications_app_alloc(); + + // start loading animation + view_holder_start(app->view_holder); + + while(loader_applications_select_app(app)) { + loader_applications_start_app(app); + } + + // stop loading animation + view_holder_stop(app->view_holder); + + loader_applications_app_free(app); + + if(loader_applications->closed_cb) { + loader_applications->closed_cb(loader_applications->context); + } + + return 0; +} \ No newline at end of file diff --git a/applications/services/loader/loader_applications.h b/applications/services/loader/loader_applications.h new file mode 100644 index 00000000000..6b132af0559 --- /dev/null +++ b/applications/services/loader/loader_applications.h @@ -0,0 +1,16 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct LoaderApplications LoaderApplications; + +LoaderApplications* loader_applications_alloc(void (*closed_cb)(void*), void* context); + +void loader_applications_free(LoaderApplications* loader_applications); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/services/loader/loader_cli.c b/applications/services/loader/loader_cli.c new file mode 100644 index 00000000000..489a576e5c0 --- /dev/null +++ b/applications/services/loader/loader_cli.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include "loader.h" + +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_list() { + printf("Apps:\r\n"); + for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { + printf("\t%s\r\n", FLIPPER_APPS[i].name); + } + printf("Settings:\r\n"); + for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { + printf("\t%s\r\n", FLIPPER_SETTINGS_APPS[i].name); + } +} + +static void loader_cli_info(Loader* loader) { + if(!loader_is_locked(loader)) { + printf("No application is running\r\n"); + } else { + // TODO FL-3513: print application name ??? + printf("Application is running\r\n"); + } +} + +static void loader_cli_open(FuriString* args, Loader* loader) { + FuriString* app_name = furi_string_alloc(); + + do { + if(!args_read_probably_quoted_string_and_trim(args, app_name)) { + printf("No application provided\r\n"); + break; + } + furi_string_trim(args); + + const char* args_str = furi_string_get_cstr(args); + if(strlen(args_str) == 0) { + args_str = NULL; + } + + const char* app_name_str = furi_string_get_cstr(app_name); + + FuriString* error_message = furi_string_alloc(); + if(loader_start(loader, app_name_str, args_str, error_message) != LoaderStatusOk) { + printf("%s\r\n", furi_string_get_cstr(error_message)); + } + furi_string_free(error_message); + } while(false); + + furi_string_free(app_name); +} + +static void loader_cli(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(context); + Loader* loader = furi_record_open(RECORD_LOADER); + + FuriString* cmd; + cmd = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, cmd)) { + loader_cli_print_usage(); + break; + } + + if(furi_string_cmp_str(cmd, "list") == 0) { + loader_cli_list(); + break; + } + + if(furi_string_cmp_str(cmd, "open") == 0) { + loader_cli_open(args, loader); + break; + } + + if(furi_string_cmp_str(cmd, "info") == 0) { + loader_cli_info(loader); + break; + } + + loader_cli_print_usage(); + } while(false); + + furi_string_free(cmd); + furi_record_close(RECORD_LOADER); +} + +void loader_on_system_start() { +#ifdef SRV_CLI + Cli* cli = furi_record_open(RECORD_CLI); + cli_add_command(cli, RECORD_LOADER, CliCommandFlagParallelSafe, loader_cli, NULL); + furi_record_close(RECORD_CLI); +#else + UNUSED(loader_cli); +#endif +} \ No newline at end of file diff --git a/applications/services/loader/loader_i.h b/applications/services/loader/loader_i.h new file mode 100644 index 00000000000..688b8fb665c --- /dev/null +++ b/applications/services/loader/loader_i.h @@ -0,0 +1,61 @@ +#pragma once +#include +#include +#include +#include "loader.h" +#include "loader_menu.h" +#include "loader_applications.h" + +typedef struct { + char* args; + FuriThread* thread; + bool insomniac; + FlipperApplication* fap; +} LoaderAppData; + +struct Loader { + FuriPubSub* pubsub; + FuriMessageQueue* queue; + LoaderMenu* loader_menu; + LoaderApplications* loader_applications; + LoaderAppData app; +}; + +typedef enum { + LoaderMessageTypeStartByName, + LoaderMessageTypeAppClosed, + LoaderMessageTypeShowMenu, + LoaderMessageTypeMenuClosed, + LoaderMessageTypeApplicationsClosed, + LoaderMessageTypeLock, + LoaderMessageTypeUnlock, + LoaderMessageTypeIsLocked, +} LoaderMessageType; + +typedef struct { + const char* name; + const char* args; + FuriString* error_message; +} LoaderMessageStartByName; + +typedef struct { + LoaderStatus value; +} LoaderMessageLoaderStatusResult; + +typedef struct { + bool value; +} LoaderMessageBoolResult; + +typedef struct { + FuriApiLock api_lock; + LoaderMessageType type; + + union { + LoaderMessageStartByName start; + }; + + union { + LoaderMessageLoaderStatusResult* status_value; + LoaderMessageBoolResult* bool_value; + }; +} LoaderMessage; diff --git a/applications/services/loader/loader_menu.c b/applications/services/loader/loader_menu.c new file mode 100644 index 00000000000..149fea72c19 --- /dev/null +++ b/applications/services/loader/loader_menu.c @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include + +#include "loader.h" +#include "loader_menu.h" + +#define TAG "LoaderMenu" + +struct LoaderMenu { + FuriThread* thread; + void (*closed_cb)(void*); + void* context; +}; + +static int32_t loader_menu_thread(void* p); + +LoaderMenu* loader_menu_alloc(void (*closed_cb)(void*), void* context) { + LoaderMenu* loader_menu = malloc(sizeof(LoaderMenu)); + loader_menu->closed_cb = closed_cb; + loader_menu->context = context; + loader_menu->thread = furi_thread_alloc_ex(TAG, 1024, loader_menu_thread, loader_menu); + furi_thread_start(loader_menu->thread); + return loader_menu; +} + +void loader_menu_free(LoaderMenu* loader_menu) { + furi_assert(loader_menu); + furi_thread_join(loader_menu->thread); + furi_thread_free(loader_menu->thread); + free(loader_menu); +} + +typedef enum { + LoaderMenuViewPrimary, + LoaderMenuViewSettings, +} LoaderMenuView; + +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + Menu* primary_menu; + Submenu* settings_menu; +} LoaderMenuApp; + +static void loader_menu_start(const char* name) { + Loader* loader = furi_record_open(RECORD_LOADER); + loader_start_with_gui_error(loader, name, NULL); + furi_record_close(RECORD_LOADER); +} + +static void loader_menu_apps_callback(void* context, uint32_t index) { + UNUSED(context); + const char* name = FLIPPER_APPS[index].name; + loader_menu_start(name); +} + +static void loader_menu_external_apps_callback(void* context, uint32_t index) { + UNUSED(context); + const char* path = FLIPPER_EXTERNAL_APPS[index].name; + loader_menu_start(path); +} + +static void loader_menu_applications_callback(void* context, uint32_t index) { + UNUSED(index); + UNUSED(context); + const char* name = LOADER_APPLICATIONS_NAME; + loader_menu_start(name); +} + +static void loader_menu_settings_menu_callback(void* context, uint32_t index) { + UNUSED(context); + const char* name = FLIPPER_SETTINGS_APPS[index].name; + loader_menu_start(name); +} + +static void loader_menu_switch_to_settings(void* context, uint32_t index) { + UNUSED(index); + LoaderMenuApp* app = context; + view_dispatcher_switch_to_view(app->view_dispatcher, LoaderMenuViewSettings); +} + +static uint32_t loader_menu_switch_to_primary(void* context) { + UNUSED(context); + return LoaderMenuViewPrimary; +} + +static uint32_t loader_menu_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +static void loader_menu_build_menu(LoaderMenuApp* app, LoaderMenu* menu) { + size_t i; + + for(i = 0; i < FLIPPER_EXTERNAL_APPS_COUNT; i++) { + menu_add_item( + app->primary_menu, + FLIPPER_EXTERNAL_APPS[i].name, + FLIPPER_EXTERNAL_APPS[i].icon, + i, + loader_menu_external_apps_callback, + (void*)menu); + } + + for(i = 0; i < FLIPPER_APPS_COUNT; i++) { + menu_add_item( + app->primary_menu, + FLIPPER_APPS[i].name, + FLIPPER_APPS[i].icon, + i, + loader_menu_apps_callback, + (void*)menu); + } + menu_add_item( + app->primary_menu, "Settings", &A_Settings_14, i++, loader_menu_switch_to_settings, app); + menu_add_item( + app->primary_menu, + LOADER_APPLICATIONS_NAME, + &A_Plugins_14, + i++, + loader_menu_applications_callback, + (void*)menu); +}; + +static void loader_menu_build_submenu(LoaderMenuApp* app, LoaderMenu* loader_menu) { + for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { + submenu_add_item( + app->settings_menu, + FLIPPER_SETTINGS_APPS[i].name, + i, + loader_menu_settings_menu_callback, + loader_menu); + } +} + +static LoaderMenuApp* loader_menu_app_alloc(LoaderMenu* loader_menu) { + LoaderMenuApp* app = malloc(sizeof(LoaderMenuApp)); + app->gui = furi_record_open(RECORD_GUI); + app->view_dispatcher = view_dispatcher_alloc(); + app->primary_menu = menu_alloc(); + app->settings_menu = submenu_alloc(); + + loader_menu_build_menu(app, loader_menu); + loader_menu_build_submenu(app, loader_menu); + + // Primary menu + View* primary_view = menu_get_view(app->primary_menu); + view_set_context(primary_view, app->primary_menu); + view_set_previous_callback(primary_view, loader_menu_exit); + view_dispatcher_add_view(app->view_dispatcher, LoaderMenuViewPrimary, primary_view); + + // Settings menu + View* settings_view = submenu_get_view(app->settings_menu); + view_set_context(settings_view, app->settings_menu); + view_set_previous_callback(settings_view, loader_menu_switch_to_primary); + view_dispatcher_add_view(app->view_dispatcher, LoaderMenuViewSettings, settings_view); + + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_switch_to_view(app->view_dispatcher, LoaderMenuViewPrimary); + + return app; +} + +static void loader_menu_app_free(LoaderMenuApp* app) { + view_dispatcher_remove_view(app->view_dispatcher, LoaderMenuViewPrimary); + view_dispatcher_remove_view(app->view_dispatcher, LoaderMenuViewSettings); + view_dispatcher_free(app->view_dispatcher); + + menu_free(app->primary_menu); + submenu_free(app->settings_menu); + furi_record_close(RECORD_GUI); + free(app); +} + +static int32_t loader_menu_thread(void* p) { + LoaderMenu* loader_menu = p; + furi_assert(loader_menu); + + LoaderMenuApp* app = loader_menu_app_alloc(loader_menu); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_run(app->view_dispatcher); + + if(loader_menu->closed_cb) { + loader_menu->closed_cb(loader_menu->context); + } + + loader_menu_app_free(app); + + return 0; +} \ No newline at end of file diff --git a/applications/services/loader/loader_menu.h b/applications/services/loader/loader_menu.h new file mode 100644 index 00000000000..528fe7d291c --- /dev/null +++ b/applications/services/loader/loader_menu.h @@ -0,0 +1,16 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct LoaderMenu LoaderMenu; + +LoaderMenu* loader_menu_alloc(void (*closed_cb)(void*), void* context); + +void loader_menu_free(LoaderMenu* loader_menu); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/services/locale/application.fam b/applications/services/locale/application.fam new file mode 100644 index 00000000000..c762d02d67b --- /dev/null +++ b/applications/services/locale/application.fam @@ -0,0 +1,9 @@ +App( + appid="locale", + name="LocaleSrv", + apptype=FlipperAppType.STARTUP, + entry_point="locale_on_system_start", + cdefines=["SRV_LOCALE"], + order=90, + sdk_headers=["locale.h"], +) diff --git a/applications/services/locale/locale.c b/applications/services/locale/locale.c new file mode 100644 index 00000000000..e8b6a9fc533 --- /dev/null +++ b/applications/services/locale/locale.c @@ -0,0 +1,112 @@ +#include "locale.h" + +#define TAG "LocaleSrv" + +LocaleMeasurementUnits locale_get_measurement_unit(void) { + return (LocaleMeasurementUnits)furi_hal_rtc_get_locale_units(); +} + +void locale_set_measurement_unit(LocaleMeasurementUnits format) { + furi_hal_rtc_set_locale_units((FuriHalRtcLocaleUnits)format); +} + +LocaleTimeFormat locale_get_time_format(void) { + return (LocaleTimeFormat)furi_hal_rtc_get_locale_timeformat(); +} + +void locale_set_time_format(LocaleTimeFormat format) { + furi_hal_rtc_set_locale_timeformat((FuriHalRtcLocaleTimeFormat)format); +} + +LocaleDateFormat locale_get_date_format(void) { + return (LocaleDateFormat)furi_hal_rtc_get_locale_dateformat(); +} + +void locale_set_date_format(LocaleDateFormat format) { + furi_hal_rtc_set_locale_dateformat((FuriHalRtcLocaleDateFormat)format); +} + +float locale_fahrenheit_to_celsius(float temp_f) { + return (temp_f - 32.f) / 1.8f; +} + +float locale_celsius_to_fahrenheit(float temp_c) { + return (temp_c * 1.8f + 32.f); +} + +void locale_format_time( + FuriString* out_str, + const FuriHalRtcDateTime* datetime, + const LocaleTimeFormat format, + const bool show_seconds) { + furi_assert(out_str); + furi_assert(datetime); + + uint8_t hours = datetime->hour; + uint8_t am_pm = 0; + if(format == LocaleTimeFormat12h) { + if(hours > 12) { + hours -= 12; + am_pm = 2; + } else { + am_pm = 1; + } + if(hours == 0) { + hours = 12; + } + } + + if(show_seconds) { + furi_string_printf(out_str, "%02u:%02u:%02u", hours, datetime->minute, datetime->second); + } else { + furi_string_printf(out_str, "%02u:%02u", hours, datetime->minute); + } + + if(am_pm > 0) { + furi_string_cat_printf(out_str, " %s", (am_pm == 1) ? ("AM") : ("PM")); + } +} + +void locale_format_date( + FuriString* out_str, + const FuriHalRtcDateTime* datetime, + const LocaleDateFormat format, + const char* separator) { + furi_assert(out_str); + furi_assert(datetime); + furi_assert(separator); + + if(format == LocaleDateFormatDMY) { + furi_string_printf( + out_str, + "%02u%s%02u%s%04u", + datetime->day, + separator, + datetime->month, + separator, + datetime->year); + } else if(format == LocaleDateFormatMDY) { + furi_string_printf( + out_str, + "%02u%s%02u%s%04u", + datetime->month, + separator, + datetime->day, + separator, + datetime->year); + } else { + furi_string_printf( + out_str, + "%04u%s%02u%s%02u", + datetime->year, + separator, + datetime->month, + separator, + datetime->day); + } +} + +int32_t locale_on_system_start(void* p) { + UNUSED(p); + return 0; +} diff --git a/applications/services/locale/locale.h b/applications/services/locale/locale.h new file mode 100644 index 00000000000..61fb4c60558 --- /dev/null +++ b/applications/services/locale/locale.h @@ -0,0 +1,107 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + LocaleMeasurementUnitsMetric = 0, /**< Metric measurement units */ + LocaleMeasurementUnitsImperial = 1, /**< Imperial measurement units */ +} LocaleMeasurementUnits; + +typedef enum { + LocaleTimeFormat24h = 0, /**< 24-hour format */ + LocaleTimeFormat12h = 1, /**< 12-hour format */ +} LocaleTimeFormat; + +typedef enum { + LocaleDateFormatDMY = 0, /**< Day/Month/Year */ + LocaleDateFormatMDY = 1, /**< Month/Day/Year */ + LocaleDateFormatYMD = 2, /**< Year/Month/Day */ +} LocaleDateFormat; + +/** Get Locale measurement units + * + * @return The locale measurement units. + */ +LocaleMeasurementUnits locale_get_measurement_unit(); + +/** Set locale measurement units + * + * @param[in] format The locale measurements units + */ +void locale_set_measurement_unit(LocaleMeasurementUnits format); + +/** Convert Fahrenheit to Celsius + * + * @param[in] temp_f The Temperature in Fahrenheit + * + * @return The Temperature in Celsius + */ +float locale_fahrenheit_to_celsius(float temp_f); + +/** Convert Celsius to Fahrenheit + * + * @param[in] temp_c The Temperature in Celsius + * + * @return The Temperature in Fahrenheit + */ +float locale_celsius_to_fahrenheit(float temp_c); + +/** Get Locale time format + * + * @return The locale time format. + */ +LocaleTimeFormat locale_get_time_format(); + +/** Set Locale Time Format + * + * @param[in] format The Locale Time Format + */ +void locale_set_time_format(LocaleTimeFormat format); + +/** Format time to furi string + * + * @param[out] out_str The FuriString to store formatted time + * @param[in] datetime Pointer to the datetime + * @param[in] format The Locale Time Format + * @param[in] show_seconds The show seconds flag + */ +void locale_format_time( + FuriString* out_str, + const FuriHalRtcDateTime* datetime, + const LocaleTimeFormat format, + const bool show_seconds); + +/** Get Locale DateFormat + * + * @return The Locale DateFormat. + */ +LocaleDateFormat locale_get_date_format(void); + +/** Set Locale DateFormat + * + * @param[in] format The Locale DateFormat + */ +void locale_set_date_format(LocaleDateFormat format); + +/** Format date to furi string + * + * @param[out] out_str The FuriString to store formatted date + * @param[in] datetime Pointer to the datetime + * @param[in] format The format + * @param[in] separator The separator + */ +void locale_format_date( + FuriString* out_str, + const FuriHalRtcDateTime* datetime, + const LocaleDateFormat format, + const char* separator); + +#ifdef __cplusplus +} +#endif diff --git a/applications/services/notification/application.fam b/applications/services/notification/application.fam new file mode 100644 index 00000000000..82f94085a72 --- /dev/null +++ b/applications/services/notification/application.fam @@ -0,0 +1,12 @@ +App( + appid="notification", + name="NotificationSrv", + apptype=FlipperAppType.SERVICE, + entry_point="notification_srv", + cdefines=["SRV_NOTIFICATION"], + requires=["input"], + provides=["notification_settings"], + stack_size=int(1.5 * 1024), + order=100, + sdk_headers=["notification.h", "notification_messages.h"], +) diff --git a/applications/notification/notification.h b/applications/services/notification/notification.h similarity index 98% rename from applications/notification/notification.h rename to applications/services/notification/notification.h index b38620f0f5f..0e1c07e5df6 100644 --- a/applications/notification/notification.h +++ b/applications/services/notification/notification.h @@ -75,6 +75,8 @@ typedef enum { NotificationMessageTypeForceDisplayBrightnessSetting, NotificationMessageTypeLedBrightnessSettingApply, + + NotificationMessageTypeLcdContrastUpdate, } NotificationMessageType; typedef struct { diff --git a/applications/notification/notification_app.c b/applications/services/notification/notification_app.c similarity index 83% rename from applications/notification/notification_app.c rename to applications/services/notification/notification_app.c index 640bd7d71fe..9baa738b79c 100644 --- a/applications/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -1,8 +1,11 @@ -#include "furi_hal_light.h" +#include #include #include #include #include +#include +#include +#include #include "notification.h" #include "notification_messages.h" #include "notification_app.h" @@ -20,14 +23,14 @@ static const uint8_t reset_sound_mask = 1 << 4; static const uint8_t reset_display_mask = 1 << 5; static const uint8_t reset_blink_mask = 1 << 6; -void notification_vibro_on(); -void notification_vibro_off(); -void notification_sound_on(float pwm, float freq); -void notification_sound_off(); +static void notification_vibro_on(bool force); +static void notification_vibro_off(); +static void notification_sound_on(float freq, float volume, bool force); +static void notification_sound_off(); -uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value); -uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value); -uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app); +static uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value); +static uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value); +static uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app); void notification_message_save_settings(NotificationApp* app) { NotificationAppMessage m = { @@ -39,7 +42,8 @@ void notification_message_save_settings(NotificationApp* app) { }; // internal layer -void notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) { +static void + notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) { furi_assert(layer); furi_assert(layer->index < LayerMAX); @@ -52,7 +56,13 @@ void notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t } } -bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) { +static void notification_apply_lcd_contrast(NotificationApp* app) { + Gui* gui = furi_record_open(RECORD_GUI); + u8x8_d_st756x_set_contrast(&gui->canvas->fb.u8x8, app->settings.contrast); + furi_record_close(RECORD_GUI); +} + +static bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) { bool result = false; if((app->led[0].index == LayerInternal) || (app->led[1].index == LayerInternal) || (app->led[2].index == LayerInternal)) { @@ -67,7 +77,7 @@ bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) } // notification layer -void notification_apply_notification_led_layer( +static void notification_apply_notification_led_layer( NotificationLedLayer* layer, const uint8_t layer_value) { furi_assert(layer); @@ -81,7 +91,7 @@ void notification_apply_notification_led_layer( furi_hal_light_set(layer->light, layer->value[LayerNotification]); } -void notification_reset_notification_led_layer(NotificationLedLayer* layer) { +static void notification_reset_notification_led_layer(NotificationLedLayer* layer) { furi_assert(layer); furi_assert(layer->index < LayerMAX); @@ -94,7 +104,10 @@ void notification_reset_notification_led_layer(NotificationLedLayer* layer) { furi_hal_light_set(layer->light, layer->value[LayerInternal]); } -void notification_reset_notification_layer(NotificationApp* app, uint8_t reset_mask) { +static void notification_reset_notification_layer( + NotificationApp* app, + uint8_t reset_mask, + float display_brightness_set) { if(reset_mask & reset_blink_mask) { furi_hal_light_blink_stop(); } @@ -114,6 +127,9 @@ void notification_reset_notification_layer(NotificationApp* app, uint8_t reset_m notification_sound_off(); } if(reset_mask & reset_display_mask) { + if(!float_is_equal(display_brightness_set, app->settings.display_brightness)) { + furi_hal_light_set(LightBacklight, app->settings.display_brightness * 0xFF); + } furi_timer_start(app->display_timer, notification_settings_display_off_delay_ticks(app)); } } @@ -130,31 +146,40 @@ uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8 return (value * app->settings.display_brightness); } -uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value) { +static uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value) { return (value * app->settings.led_brightness); } -uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) { +static uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) { return ( (float)(app->settings.display_off_delay_ms) / (1000.0f / furi_kernel_get_tick_frequency())); } // generics -void notification_vibro_on() { - furi_hal_vibro_on(true); +static void notification_vibro_on(bool force) { + if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { + furi_hal_vibro_on(true); + } } -void notification_vibro_off() { +static void notification_vibro_off() { furi_hal_vibro_on(false); } -void notification_sound_on(float freq, float volume) { - furi_hal_speaker_start(freq, volume); +static void notification_sound_on(float freq, float volume, bool force) { + if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(freq, volume); + } + } } -void notification_sound_off() { - furi_hal_speaker_stop(); +static void notification_sound_off() { + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } } // display timer @@ -165,10 +190,12 @@ static void notification_display_timer(void* ctx) { } // message processing -void notification_process_notification_message( +static void notification_process_notification_message( NotificationApp* app, NotificationAppMessage* message) { uint32_t notification_message_index = 0; + bool force_volume = false; + bool force_vibro = false; const NotificationMessage* notification_message; notification_message = (*message->sequence)[notification_message_index]; @@ -191,16 +218,17 @@ void notification_process_notification_message( notification_apply_notification_led_layer( &app->display, notification_message->data.led.value * display_brightness_setting); + reset_mask |= reset_display_mask; } else { + reset_mask &= ~reset_display_mask; notification_reset_notification_led_layer(&app->display); if(furi_timer_is_running(app->display_timer)) { furi_timer_stop(app->display_timer); } } - reset_mask |= reset_display_mask; break; case NotificationMessageTypeLedDisplayBacklightEnforceOn: - furi_assert(app->display_led_lock < UINT8_MAX); + furi_check(app->display_led_lock < UINT8_MAX); app->display_led_lock++; if(app->display_led_lock == 1) { notification_apply_internal_led_layer( @@ -209,12 +237,15 @@ void notification_process_notification_message( } break; case NotificationMessageTypeLedDisplayBacklightEnforceAuto: - furi_assert(app->display_led_lock > 0); - app->display_led_lock--; - if(app->display_led_lock == 0) { - notification_apply_internal_led_layer( - &app->display, - notification_message->data.led.value * display_brightness_setting); + if(app->display_led_lock > 0) { + app->display_led_lock--; + if(app->display_led_lock == 0) { + notification_apply_internal_led_layer( + &app->display, + notification_message->data.led.value * display_brightness_setting); + } + } else { + FURI_LOG_E(TAG, "Incorrect BacklightEnforce use"); } break; case NotificationMessageTypeLedRed: @@ -264,7 +295,7 @@ void notification_process_notification_message( break; case NotificationMessageTypeVibro: if(notification_message->data.vibro.on) { - if(vibro_setting) notification_vibro_on(); + if(vibro_setting) notification_vibro_on(force_vibro); } else { notification_vibro_off(); } @@ -273,7 +304,8 @@ void notification_process_notification_message( case NotificationMessageTypeSoundOn: notification_sound_on( notification_message->data.sound.frequency, - notification_message->data.sound.volume * speaker_volume_setting); + notification_message->data.sound.volume * speaker_volume_setting, + force_volume); reset_mask |= reset_sound_mask; break; case NotificationMessageTypeSoundOff: @@ -302,9 +334,11 @@ void notification_process_notification_message( break; case NotificationMessageTypeForceSpeakerVolumeSetting: speaker_volume_setting = notification_message->data.forced_settings.speaker_volume; + force_volume = true; break; case NotificationMessageTypeForceVibroSetting: vibro_setting = notification_message->data.forced_settings.vibro; + force_vibro = true; break; case NotificationMessageTypeForceDisplayBrightnessSetting: display_brightness_setting = @@ -319,6 +353,9 @@ void notification_process_notification_message( reset_mask |= reset_green_mask; reset_mask |= reset_blue_mask; break; + case NotificationMessageTypeLcdContrastUpdate: + notification_apply_lcd_contrast(app); + break; } notification_message_index++; notification_message = (*message->sequence)[notification_message_index]; @@ -343,11 +380,12 @@ void notification_process_notification_message( } if(reset_notifications) { - notification_reset_notification_layer(app, reset_mask); + notification_reset_notification_layer(app, reset_mask, display_brightness_setting); } } -void notification_process_internal_message(NotificationApp* app, NotificationAppMessage* message) { +static void + notification_process_internal_message(NotificationApp* app, NotificationAppMessage* message) { uint32_t notification_message_index = 0; const NotificationMessage* notification_message; notification_message = (*message->sequence)[notification_message_index]; @@ -406,7 +444,7 @@ static bool notification_load_settings(NotificationApp* app) { storage_file_open(file, NOTIFICATION_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(fs_result) { - uint16_t bytes_count = storage_file_read(file, &settings, settings_size); + size_t bytes_count = storage_file_read(file, &settings, settings_size); if(bytes_count != settings_size) { fs_result = false; @@ -450,7 +488,7 @@ static bool notification_save_settings(NotificationApp* app) { storage_file_open(file, NOTIFICATION_SETTINGS_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS); if(fs_result) { - uint16_t bytes_count = storage_file_write(file, &settings, settings_size); + size_t bytes_count = storage_file_write(file, &settings, settings_size); if(bytes_count != settings_size) { fs_result = false; @@ -534,6 +572,7 @@ int32_t notification_srv(void* p) { notification_apply_internal_led_layer(&app->led[0], 0x00); notification_apply_internal_led_layer(&app->led[1], 0x00); notification_apply_internal_led_layer(&app->led[2], 0x00); + notification_apply_lcd_contrast(app); furi_record_create(RECORD_NOTIFICATION, app); diff --git a/applications/notification/notification_app.h b/applications/services/notification/notification_app.h similarity index 95% rename from applications/notification/notification_app.h rename to applications/services/notification/notification_app.h index 88194bfbd53..cacc17ffb0c 100644 --- a/applications/notification/notification_app.h +++ b/applications/services/notification/notification_app.h @@ -32,7 +32,7 @@ typedef struct { Light light; } NotificationLedLayer; -#define NOTIFICATION_SETTINGS_VERSION 0x01 +#define NOTIFICATION_SETTINGS_VERSION 0x02 #define NOTIFICATION_SETTINGS_PATH INT_PATH(NOTIFICATION_SETTINGS_FILE_NAME) typedef struct { @@ -41,6 +41,7 @@ typedef struct { float led_brightness; float speaker_volume; uint32_t display_off_delay_ms; + int8_t contrast; bool vibro_on; } NotificationSettings; diff --git a/applications/notification/notification_app_api.c b/applications/services/notification/notification_app_api.c similarity index 100% rename from applications/notification/notification_app_api.c rename to applications/services/notification/notification_app_api.c diff --git a/applications/notification/notification_messages.c b/applications/services/notification/notification_messages.c similarity index 98% rename from applications/notification/notification_messages.c rename to applications/services/notification/notification_messages.c index d795c55d91a..28ec327c6e6 100644 --- a/applications/notification/notification_messages.c +++ b/applications/services/notification/notification_messages.c @@ -1,4 +1,4 @@ -#include "furi_hal_resources.h" +#include #include "notification.h" #include "notification_messages_notes.h" #include @@ -197,6 +197,10 @@ const NotificationMessage message_force_display_brightness_setting_1f = { .data.forced_settings.display_brightness = 1.0f, }; +const NotificationMessage message_lcd_contrast_update = { + .type = NotificationMessageTypeLcdContrastUpdate, +}; + /****************************** Message sequences ******************************/ // Reset @@ -566,3 +570,8 @@ const NotificationSequence sequence_audiovisual_alert = { &message_vibro_off, NULL, }; + +const NotificationSequence sequence_lcd_contrast_update = { + &message_lcd_contrast_update, + NULL, +}; diff --git a/applications/notification/notification_messages.h b/applications/services/notification/notification_messages.h similarity index 97% rename from applications/notification/notification_messages.h rename to applications/services/notification/notification_messages.h index 1007969176c..d87cf74f4ee 100644 --- a/applications/notification/notification_messages.h +++ b/applications/services/notification/notification_messages.h @@ -63,6 +63,9 @@ extern const NotificationMessage message_force_vibro_setting_on; extern const NotificationMessage message_force_vibro_setting_off; extern const NotificationMessage message_force_display_brightness_setting_1f; +// LCD Messages +extern const NotificationMessage message_lcd_contrast_update; + /****************************** Message sequences ******************************/ // Reset @@ -138,6 +141,9 @@ extern const NotificationSequence sequence_success; extern const NotificationSequence sequence_error; extern const NotificationSequence sequence_audiovisual_alert; +// LCD +extern const NotificationSequence sequence_lcd_contrast_update; + #ifdef __cplusplus } #endif diff --git a/applications/notification/notification_messages_notes.c b/applications/services/notification/notification_messages_notes.c similarity index 100% rename from applications/notification/notification_messages_notes.c rename to applications/services/notification/notification_messages_notes.c diff --git a/applications/notification/notification_messages_notes.h b/applications/services/notification/notification_messages_notes.h similarity index 100% rename from applications/notification/notification_messages_notes.h rename to applications/services/notification/notification_messages_notes.h diff --git a/applications/notification/notification_settings_filename.h b/applications/services/notification/notification_settings_filename.h similarity index 100% rename from applications/notification/notification_settings_filename.h rename to applications/services/notification/notification_settings_filename.h diff --git a/applications/services/power/application.fam b/applications/services/power/application.fam new file mode 100644 index 00000000000..f14d88c5426 --- /dev/null +++ b/applications/services/power/application.fam @@ -0,0 +1,26 @@ +App( + appid="power", + name="PowerSrv", + apptype=FlipperAppType.SERVICE, + entry_point="power_srv", + cdefines=["SRV_POWER"], + requires=[ + "gui", + "cli", + ], + provides=[ + "power_settings", + "power_start", + ], + stack_size=1 * 1024, + order=110, + sdk_headers=["power_service/power.h"], +) + +App( + appid="power_start", + apptype=FlipperAppType.STARTUP, + entry_point="power_on_system_start", + requires=["power"], + order=80, +) diff --git a/applications/services/power/power_cli.c b/applications/services/power/power_cli.c new file mode 100644 index 00000000000..021ce35536c --- /dev/null +++ b/applications/services/power/power_cli.c @@ -0,0 +1,119 @@ +#include "power_cli.h" + +#include +#include +#include +#include + +void power_cli_off(Cli* cli, FuriString* args) { + UNUSED(cli); + UNUSED(args); + Power* power = furi_record_open(RECORD_POWER); + printf("It's now safe to disconnect USB from your flipper\r\n"); + furi_delay_ms(666); + power_off(power); +} + +void power_cli_reboot(Cli* cli, FuriString* args) { + UNUSED(cli); + UNUSED(args); + power_reboot(PowerBootModeNormal); +} + +void power_cli_reboot2dfu(Cli* cli, FuriString* args) { + UNUSED(cli); + UNUSED(args); + power_reboot(PowerBootModeDfu); +} + +void power_cli_5v(Cli* cli, FuriString* args) { + UNUSED(cli); + if(!furi_string_cmp(args, "0")) { + furi_hal_power_disable_otg(); + } else if(!furi_string_cmp(args, "1")) { + furi_hal_power_enable_otg(); + } else { + cli_print_usage("power_otg", "<1|0>", furi_string_get_cstr(args)); + } +} + +void power_cli_3v3(Cli* cli, FuriString* args) { + UNUSED(cli); + if(!furi_string_cmp(args, "0")) { + furi_hal_power_disable_external_3_3v(); + } else if(!furi_string_cmp(args, "1")) { + furi_hal_power_enable_external_3_3v(); + } else { + cli_print_usage("power_ext", "<1|0>", furi_string_get_cstr(args)); + } +} + +static void power_cli_command_print_usage() { + printf("Usage:\r\n"); + printf("power \r\n"); + printf("Cmd list:\r\n"); + + printf("\toff\t - shutdown power\r\n"); + printf("\treboot\t - reboot\r\n"); + printf("\treboot2dfu\t - reboot to dfu bootloader\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"); + } +} + +void power_cli(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + FuriString* cmd; + cmd = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, cmd)) { + power_cli_command_print_usage(); + break; + } + + if(furi_string_cmp_str(cmd, "off") == 0) { + power_cli_off(cli, args); + break; + } + + if(furi_string_cmp_str(cmd, "reboot") == 0) { + power_cli_reboot(cli, args); + break; + } + + if(furi_string_cmp_str(cmd, "reboot2dfu") == 0) { + power_cli_reboot2dfu(cli, args); + break; + } + + if(furi_string_cmp_str(cmd, "5v") == 0) { + power_cli_5v(cli, args); + break; + } + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + if(furi_string_cmp_str(cmd, "3v3") == 0) { + power_cli_3v3(cli, args); + break; + } + } + + power_cli_command_print_usage(); + } while(false); + + furi_string_free(cmd); +} + +void power_on_system_start() { +#ifdef SRV_CLI + Cli* cli = furi_record_open(RECORD_CLI); + + cli_add_command(cli, "power", CliCommandFlagParallelSafe, power_cli, NULL); + + furi_record_close(RECORD_CLI); +#else + UNUSED(power_cli); +#endif +} diff --git a/applications/services/power/power_cli.h b/applications/services/power/power_cli.h new file mode 100644 index 00000000000..1517c61409f --- /dev/null +++ b/applications/services/power/power_cli.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void power_on_system_start(); + +#ifdef __cplusplus +} +#endif diff --git a/applications/power/power_service/power.c b/applications/services/power/power_service/power.c similarity index 81% rename from applications/power/power_service/power.c rename to applications/services/power/power_service/power.c index 9036ae1ce39..ad3a5114dca 100644 --- a/applications/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -1,12 +1,10 @@ #include "power_i.h" -#include "views/power_off.h" #include #include -#include -#include #define POWER_OFF_TIMEOUT 90 +#define TAG "Power" void power_draw_battery_callback(Canvas* canvas, void* context) { furi_assert(context); @@ -15,10 +13,24 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { if(power->info.gauge_is_ok) { canvas_draw_box(canvas, 2, 2, (power->info.charge + 4) / 5, 4); + if(power->info.voltage_battery_charge_limit < 4.2) { + // Battery charge voltage limit is modified, indicate with cross pattern + canvas_invert_color(canvas); + uint8_t battery_bar_width = (power->info.charge + 4) / 5; + bool cross_odd = false; + // Start 1 further in from the battery bar's x position + for(uint8_t x = 3; x <= battery_bar_width; x++) { + // Cross pattern is from the center of the battery bar + // y = 2 + 1 (inset) + 1 (for every other) + canvas_draw_dot(canvas, x, 3 + (uint8_t)cross_odd); + cross_odd = !cross_odd; + } + canvas_invert_color(canvas); + } if(power->state == PowerStateCharging) { canvas_set_bitmap_mode(canvas, 1); canvas_set_color(canvas, ColorWhite); - // TODO: replace -1 magic for uint8_t with re-framing + // -1 used here to overflow u8 number and render is outside of the area canvas_draw_icon(canvas, 8, -1, &I_Charging_lightning_mask_9x10); canvas_set_color(canvas, ColorBlack); canvas_draw_icon(canvas, 8, -1, &I_Charging_lightning_9x10); @@ -55,13 +67,14 @@ Power* power_alloc() { // Gui power->view_dispatcher = view_dispatcher_alloc(); - power->popup = popup_alloc(); - popup_set_header( - power->popup, "Disconnect USB for safe\nshutdown", 64, 26, AlignCenter, AlignTop); - view_dispatcher_add_view(power->view_dispatcher, PowerViewPopup, popup_get_view(power->popup)); power->power_off = power_off_alloc(); view_dispatcher_add_view( power->view_dispatcher, PowerViewOff, power_off_get_view(power->power_off)); + power->power_unplug_usb = power_unplug_usb_alloc(); + view_dispatcher_add_view( + power->view_dispatcher, + PowerViewUnplugUsb, + power_unplug_usb_get_view(power->power_unplug_usb)); view_dispatcher_attach_to_gui( power->view_dispatcher, power->gui, ViewDispatcherTypeFullscreen); @@ -72,32 +85,9 @@ Power* power_alloc() { return power; } -void power_free(Power* power) { - furi_assert(power); - - // Gui - view_dispatcher_remove_view(power->view_dispatcher, PowerViewOff); - power_off_free(power->power_off); - view_dispatcher_remove_view(power->view_dispatcher, PowerViewPopup); - popup_free(power->popup); - view_port_free(power->battery_view_port); - - // State - furi_mutex_free(power->api_mtx); - - // FuriPubSub - furi_pubsub_free(power->event_pubsub); - - // Records - furi_record_close(RECORD_NOTIFICATION); - furi_record_close(RECORD_GUI); - - free(power); -} - static void power_check_charging_state(Power* power) { if(furi_hal_power_is_charging()) { - if(power->info.charge == 100) { + if((power->info.charge == 100) || (furi_hal_power_is_charging_done())) { if(power->state != PowerStateCharged) { notification_internal_message(power->notification, &sequence_charged); power->state = PowerStateCharged; @@ -125,13 +115,16 @@ static void power_check_charging_state(Power* power) { static bool power_update_info(Power* power) { PowerInfo info; + info.is_charging = furi_hal_power_is_charging(); info.gauge_is_ok = furi_hal_power_gauge_is_ok(); + info.is_shutdown_requested = furi_hal_power_is_shutdown_requested(); info.charge = furi_hal_power_get_pct(); info.health = furi_hal_power_get_bat_health_pct(); info.capacity_remaining = furi_hal_power_get_battery_remaining_capacity(); 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_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(); @@ -140,6 +133,7 @@ static bool power_update_info(Power* power) { furi_mutex_acquire(power->api_mtx, FuriWaitForever); bool need_refresh = power->info.charge != info.charge; + need_refresh |= power->info.is_charging != info.is_charging; power->info = info; furi_mutex_release(power->api_mtx); @@ -152,7 +146,7 @@ static void power_check_low_battery(Power* power) { } // Check battery charge and vbus voltage - if((power->info.charge == 0) && (power->info.voltage_vbus < 4.0f) && + if((power->info.is_shutdown_requested) && (power->info.voltage_vbus < 4.0f) && power->show_low_bat_level_message) { if(!power->battery_low) { view_dispatcher_send_to_front(power->view_dispatcher); @@ -201,6 +195,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(); power_update_info(power); furi_record_create(RECORD_POWER, power); @@ -229,7 +229,7 @@ int32_t power_srv(void* p) { furi_delay_ms(1000); } - power_free(power); + furi_crash("That was unexpected"); return 0; } diff --git a/applications/power/power_service/power.h b/applications/services/power/power_service/power.h similarity index 88% rename from applications/power/power_service/power.h rename to applications/services/power/power_service/power.h index c516f28f6bf..fdc5b527a60 100644 --- a/applications/power/power_service/power.h +++ b/applications/services/power/power_service/power.h @@ -4,6 +4,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + #define RECORD_POWER "power" typedef struct Power Power; @@ -32,10 +36,13 @@ typedef struct { typedef struct { bool gauge_is_ok; + bool is_charging; + bool is_shutdown_requested; float current_charger; float current_gauge; + float voltage_battery_charge_limit; float voltage_charger; float voltage_gauge; float voltage_vbus; @@ -81,9 +88,13 @@ FuriPubSub* power_get_pubsub(Power* power); */ bool power_is_battery_healthy(Power* power); -/** Enable or disable battery low level notification mesage +/** Enable or disable battery low level notification message * * @param power Power instance * @param enable true - enable, false - disable */ void power_enable_low_battery_level_notification(Power* power, bool enable); + +#ifdef __cplusplus +} +#endif diff --git a/applications/power/power_service/power_api.c b/applications/services/power/power_service/power_api.c similarity index 97% rename from applications/power/power_service/power_api.c rename to applications/services/power/power_service/power_api.c index d26fb3b4f09..8185b7cd528 100644 --- a/applications/power/power_service/power_api.c +++ b/applications/services/power/power_service/power_api.c @@ -8,8 +8,8 @@ void power_off(Power* power) { furi_hal_power_off(); // Notify user if USB is plugged view_dispatcher_send_to_front(power->view_dispatcher); - view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewPopup); - furi_delay_ms(10); + view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewUnplugUsb); + furi_delay_ms(100); furi_halt("Disconnect USB for safe shutdown"); } diff --git a/applications/power/power_service/power_i.h b/applications/services/power/power_service/power_i.h old mode 100755 new mode 100644 similarity index 86% rename from applications/power/power_service/power_i.h rename to applications/services/power/power_service/power_i.h index c7181d0a1ad..8cb5140d77d --- a/applications/power/power_service/power_i.h +++ b/applications/services/power/power_service/power_i.h @@ -5,9 +5,11 @@ #include #include #include +#include #include #include "views/power_off.h" +#include "views/power_unplug_usb.h" #include @@ -21,8 +23,8 @@ typedef enum { struct Power { ViewDispatcher* view_dispatcher; - Popup* popup; PowerOff* power_off; + PowerUnplugUsb* power_unplug_usb; ViewPort* battery_view_port; Gui* gui; @@ -42,6 +44,6 @@ struct Power { }; typedef enum { - PowerViewPopup, PowerViewOff, + PowerViewUnplugUsb, } PowerView; diff --git a/applications/power/power_service/views/power_off.c b/applications/services/power/power_service/views/power_off.c similarity index 90% rename from applications/power/power_service/views/power_off.c rename to applications/services/power/power_service/views/power_off.c index 398ebe4ab9d..3a1addbac70 100644 --- a/applications/power/power_service/views/power_off.c +++ b/applications/services/power/power_service/views/power_off.c @@ -1,6 +1,7 @@ #include "power_off.h" #include #include +#include struct PowerOff { View* view; @@ -25,7 +26,7 @@ static void power_off_draw_callback(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontSecondary); if(model->response == PowerOffResponseDefault) { - snprintf(buff, sizeof(buff), "Charge me!\nOff in %lds!", model->time_left_sec); + snprintf(buff, sizeof(buff), "Charge me!\nOff in %lus!", model->time_left_sec); elements_multiline_text_aligned(canvas, 70, 23, AlignLeft, AlignTop, buff); elements_button_left(canvas, "Cancel"); @@ -87,19 +88,13 @@ View* power_off_get_view(PowerOff* power_off) { void power_off_set_time_left(PowerOff* power_off, uint8_t time_left) { furi_assert(power_off); with_view_model( - power_off->view, (PowerOffModel * model) { - model->time_left_sec = time_left; - return true; - }); + power_off->view, PowerOffModel * model, { model->time_left_sec = time_left; }, true); } PowerOffResponse power_off_get_response(PowerOff* power_off) { furi_assert(power_off); PowerOffResponse response; with_view_model( - power_off->view, (PowerOffModel * model) { - response = model->response; - return false; - }); + power_off->view, PowerOffModel * model, { response = model->response; }, false); return response; } diff --git a/applications/power/power_service/views/power_off.h b/applications/services/power/power_service/views/power_off.h similarity index 100% rename from applications/power/power_service/views/power_off.h rename to applications/services/power/power_service/views/power_off.h diff --git a/applications/services/power/power_service/views/power_unplug_usb.c b/applications/services/power/power_service/views/power_unplug_usb.c new file mode 100644 index 00000000000..c2d61139e4b --- /dev/null +++ b/applications/services/power/power_service/views/power_unplug_usb.c @@ -0,0 +1,44 @@ +#include "power_unplug_usb.h" +#include +#include +#include + +struct PowerUnplugUsb { + View* view; +}; + +static void power_unplug_usb_draw_callback(Canvas* canvas, void* _model) { + UNUSED(_model); + + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon(canvas, 0, 0, &I_Unplug_bg_top_128x14); + canvas_draw_box(canvas, 0, 14, 128, (64 - 10 - 14)); + canvas_draw_icon(canvas, 0, (64 - 10), &I_Unplug_bg_bottom_128x10); + + canvas_set_color(canvas, ColorWhite); + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned( + canvas, 64, 32, AlignCenter, AlignCenter, "It's now safe to unplug\nthe USB cable"); +} + +PowerUnplugUsb* power_unplug_usb_alloc() { + PowerUnplugUsb* power_unplug_usb = malloc(sizeof(PowerUnplugUsb)); + + power_unplug_usb->view = view_alloc(); + view_set_context(power_unplug_usb->view, power_unplug_usb); + view_set_draw_callback(power_unplug_usb->view, power_unplug_usb_draw_callback); + view_set_input_callback(power_unplug_usb->view, NULL); + + return power_unplug_usb; +} + +void power_unplug_usb_free(PowerUnplugUsb* power_unplug_usb) { + furi_assert(power_unplug_usb); + view_free(power_unplug_usb->view); + free(power_unplug_usb); +} + +View* power_unplug_usb_get_view(PowerUnplugUsb* power_unplug_usb) { + furi_assert(power_unplug_usb); + return power_unplug_usb->view; +} diff --git a/applications/services/power/power_service/views/power_unplug_usb.h b/applications/services/power/power_service/views/power_unplug_usb.h new file mode 100644 index 00000000000..e85c6d03b9a --- /dev/null +++ b/applications/services/power/power_service/views/power_unplug_usb.h @@ -0,0 +1,11 @@ +#pragma once + +typedef struct PowerUnplugUsb PowerUnplugUsb; + +#include + +PowerUnplugUsb* power_unplug_usb_alloc(); + +void power_unplug_usb_free(PowerUnplugUsb* power_unplug_usb); + +View* power_unplug_usb_get_view(PowerUnplugUsb* power_unplug_usb); diff --git a/applications/services/rpc/application.fam b/applications/services/rpc/application.fam new file mode 100644 index 00000000000..7c0b6813369 --- /dev/null +++ b/applications/services/rpc/application.fam @@ -0,0 +1,9 @@ +App( + appid="rpc_start", + apptype=FlipperAppType.STARTUP, + entry_point="rpc_on_system_start", + cdefines=["SRV_RPC"], + requires=["cli"], + order=10, + sdk_headers=["rpc_app.h"], +) diff --git a/applications/services/rpc/rpc.c b/applications/services/rpc/rpc.c new file mode 100644 index 00000000000..5880e7d9f96 --- /dev/null +++ b/applications/services/rpc/rpc.c @@ -0,0 +1,491 @@ +#include "rpc_i.h" + +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#define TAG "RpcSrv" + +typedef enum { + RpcEvtNewData = (1 << 0), + RpcEvtDisconnect = (1 << 1), +} RpcEvtFlags; + +#define RPC_ALL_EVENTS (RpcEvtNewData | RpcEvtDisconnect) + +DICT_DEF2(RpcHandlerDict, pb_size_t, M_DEFAULT_OPLIST, RpcHandler, M_POD_OPLIST) + +typedef struct { + RpcSystemAlloc alloc; + RpcSystemFree free; + void* context; +} RpcSystemCallbacks; + +static RpcSystemCallbacks rpc_systems[] = { + { + .alloc = rpc_system_system_alloc, + .free = NULL, + }, + { + .alloc = rpc_system_storage_alloc, + .free = rpc_system_storage_free, + }, + { + .alloc = rpc_system_app_alloc, + .free = rpc_system_app_free, + }, + { + .alloc = rpc_system_gui_alloc, + .free = rpc_system_gui_free, + }, + { + .alloc = rpc_system_gpio_alloc, + .free = NULL, + }, + { + .alloc = rpc_system_property_alloc, + .free = NULL, + }, + { + .alloc = rpc_desktop_alloc, + .free = rpc_desktop_free, + }, +}; + +struct RpcSession { + Rpc* rpc; + + FuriThread* thread; + + RpcHandlerDict_t handlers; + FuriStreamBuffer* stream; + PB_Main* decoded_message; + bool terminate; + void** system_contexts; + bool decode_error; + + FuriMutex* callbacks_mutex; + RpcSendBytesCallback send_bytes_callback; + RpcBufferIsEmptyCallback buffer_is_empty_callback; + RpcSessionClosedCallback closed_callback; + RpcSessionTerminatedCallback terminated_callback; + RpcOwner owner; + void* context; +}; + +struct Rpc { + FuriMutex* busy_mutex; +}; + +RpcOwner rpc_session_get_owner(RpcSession* session) { + furi_assert(session); + return session->owner; +} + +static void rpc_close_session_process(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + + RpcSession* session = (RpcSession*)context; + + rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); + furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); + if(session->closed_callback) { + session->closed_callback(session->context); + } else { + FURI_LOG_W(TAG, "Session stop isn't processed by transport layer"); + } + furi_mutex_release(session->callbacks_mutex); +} + +void rpc_session_set_context(RpcSession* session, void* context) { + furi_assert(session); + + furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); + session->context = context; + furi_mutex_release(session->callbacks_mutex); +} + +void rpc_session_set_close_callback(RpcSession* session, RpcSessionClosedCallback callback) { + furi_assert(session); + + furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); + session->closed_callback = callback; + furi_mutex_release(session->callbacks_mutex); +} + +void rpc_session_set_send_bytes_callback(RpcSession* session, RpcSendBytesCallback callback) { + furi_assert(session); + + furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); + session->send_bytes_callback = callback; + furi_mutex_release(session->callbacks_mutex); +} + +void rpc_session_set_buffer_is_empty_callback( + RpcSession* session, + RpcBufferIsEmptyCallback callback) { + furi_assert(session); + + furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); + session->buffer_is_empty_callback = callback; + furi_mutex_release(session->callbacks_mutex); +} + +void rpc_session_set_terminated_callback( + RpcSession* session, + RpcSessionTerminatedCallback callback) { + furi_assert(session); + + furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); + session->terminated_callback = callback; + furi_mutex_release(session->callbacks_mutex); +} + +/* 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 + * odd: client sends close request and sends command after. + */ +size_t + rpc_session_feed(RpcSession* session, uint8_t* encoded_bytes, size_t size, uint32_t timeout) { + furi_assert(session); + furi_assert(encoded_bytes); + + if(!size) return 0; + + size_t bytes_sent = furi_stream_buffer_send(session->stream, encoded_bytes, size, timeout); + + furi_thread_flags_set(furi_thread_get_id(session->thread), RpcEvtNewData); + + return bytes_sent; +} + +size_t rpc_session_get_available_size(RpcSession* session) { + furi_assert(session); + return furi_stream_buffer_spaces_available(session->stream); +} + +bool rpc_pb_stream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) { + furi_assert(istream); + furi_assert(buf); + RpcSession* session = istream->state; + furi_assert(session); + furi_assert(istream->bytes_left); + + uint32_t flags = 0; + size_t bytes_received = 0; + + while(1) { + bytes_received += furi_stream_buffer_receive( + session->stream, buf + bytes_received, count - bytes_received, 0); + if(furi_stream_buffer_is_empty(session->stream)) { + if(session->buffer_is_empty_callback) { + session->buffer_is_empty_callback(session->context); + } + } + if(session->decode_error) { + /* never go out till RPC_EVENT_DISCONNECT come */ + bytes_received = 0; + } + if(count == bytes_received) { + break; + } else { + flags = furi_thread_flags_wait(RPC_ALL_EVENTS, FuriFlagWaitAny, FuriWaitForever); + if(flags & RpcEvtDisconnect) { + if(furi_stream_buffer_is_empty(session->stream)) { + session->terminate = true; + istream->bytes_left = 0; + bytes_received = 0; + break; + } else { + /* Save disconnect flag and continue reading buffer */ + furi_thread_flags_set(furi_thread_get_id(session->thread), RpcEvtDisconnect); + } + } else if(flags & RpcEvtNewData) { + // Just wake thread up + } + } + } + +#if SRV_RPC_DEBUG + rpc_debug_print_data("INPUT", buf, bytes_received); +#endif + + return (count == bytes_received); +} + +static bool rpc_pb_content_callback(pb_istream_t* stream, const pb_field_t* field, void** arg) { + furi_assert(stream); + RpcSession* session = stream->state; + furi_assert(session); + furi_assert(field); + + RpcHandler* handler = RpcHandlerDict_get(session->handlers, field->tag); + + if(handler && handler->decode_submessage) { + handler->decode_submessage(stream, field, arg); + } + + return true; +} + +static int32_t rpc_session_worker(void* context) { + furi_assert(context); + RpcSession* session = (RpcSession*)context; + Rpc* rpc = session->rpc; + + FURI_LOG_D(TAG, "Session started"); + + while(1) { + pb_istream_t istream = { + .callback = rpc_pb_stream_read, + .state = session, + .errmsg = NULL, + .bytes_left = SIZE_MAX, + }; + + bool message_decode_failed = false; + + if(pb_decode_ex(&istream, &PB_Main_msg, session->decoded_message, PB_DECODE_DELIMITED)) { +#if SRV_RPC_DEBUG + FURI_LOG_I(TAG, "INPUT:"); + rpc_debug_print_message(session->decoded_message); +#endif + RpcHandler* handler = + RpcHandlerDict_get(session->handlers, session->decoded_message->which_content); + + if(handler && handler->message_handler) { + furi_check(furi_mutex_acquire(rpc->busy_mutex, FuriWaitForever) == FuriStatusOk); + handler->message_handler(session->decoded_message, handler->context); + furi_check(furi_mutex_release(rpc->busy_mutex) == FuriStatusOk); + } else if(session->decoded_message->which_content == 0) { + /* Receiving zeroes means message is 0-length, which + * is valid for proto3: all fields are filled with default values. + * 0 - is default value for which_content field. + * Mark it as decode error, because there is no content message + * in Main message with tag 0. + */ + message_decode_failed = true; + } else if(!handler && !session->terminate) { + FURI_LOG_E( + TAG, + "Message(%d) decoded, but not implemented", + session->decoded_message->which_content); + rpc_send_and_release_empty( + session, + session->decoded_message->command_id, + PB_CommandStatus_ERROR_NOT_IMPLEMENTED); + } + } else { + message_decode_failed = true; + } + + if(message_decode_failed) { + furi_stream_buffer_reset(session->stream); + if(!session->terminate) { + /* Protobuf can't determine start and end of message. + * Handle this by adding varint at beginning + * of a message (PB_ENCODE_DELIMITED). But decoding fail + * means we can't be sure next bytes are varint for next + * message, so the only way to close session. + * RPC itself can't make decision to close session. It has + * to notify: + * 1) down layer (transport) + * 2) other side (companion app) + * Who are responsible to handle RPC session lifecycle. + * Companion receives 2 messages: ERROR_DECODE and session_closed. + */ + FURI_LOG_E(TAG, "Decode failed, error: \'%.128s\'", PB_GET_ERROR(&istream)); + session->decode_error = true; + rpc_send_and_release_empty(session, 0, PB_CommandStatus_ERROR_DECODE); + furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); + if(session->closed_callback) { + session->closed_callback(session->context); + } + furi_mutex_release(session->callbacks_mutex); + + if(session->owner == RpcOwnerBle) { + // Disconnect BLE session + FURI_LOG_E("RPC", "BLE session closed due to a decode error"); + Bt* bt = furi_record_open(RECORD_BT); + bt_set_profile(bt, BtProfileSerial); + furi_record_close(RECORD_BT); + FURI_LOG_E("RPC", "Finished disconnecting the BLE session"); + } + } + } + + pb_release(&PB_Main_msg, session->decoded_message); + + if(session->terminate) { + FURI_LOG_D(TAG, "Session terminated"); + break; + } + } + + return 0; +} + +static void rpc_session_thread_pending_callback(void* context, uint32_t arg) { + UNUSED(arg); + RpcSession* session = (RpcSession*)context; + + for(size_t i = 0; i < COUNT_OF(rpc_systems); ++i) { + if(rpc_systems[i].free) { + (rpc_systems[i].free)(session->system_contexts[i]); + } + } + free(session->system_contexts); + free(session->decoded_message); + RpcHandlerDict_clear(session->handlers); + furi_stream_buffer_free(session->stream); + + furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); + if(session->terminated_callback) { + session->terminated_callback(session->context); + } + furi_mutex_release(session->callbacks_mutex); + + furi_mutex_free(session->callbacks_mutex); + furi_thread_join(session->thread); + furi_thread_free(session->thread); + free(session); +} + +static void rpc_session_thread_state_callback(FuriThreadState thread_state, void* context) { + if(thread_state == FuriThreadStateStopped) { + furi_timer_pending_callback(rpc_session_thread_pending_callback, context, 0); + } +} + +RpcSession* rpc_session_open(Rpc* rpc, RpcOwner owner) { + furi_assert(rpc); + + RpcSession* session = malloc(sizeof(RpcSession)); + session->callbacks_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + session->stream = furi_stream_buffer_alloc(RPC_BUFFER_SIZE, 1); + session->rpc = rpc; + session->terminate = false; + session->decode_error = false; + session->owner = owner; + RpcHandlerDict_init(session->handlers); + + session->decoded_message = malloc(sizeof(PB_Main)); + session->decoded_message->cb_content.funcs.decode = rpc_pb_content_callback; + session->decoded_message->cb_content.arg = session; + + session->system_contexts = malloc(COUNT_OF(rpc_systems) * sizeof(void*)); + for(size_t i = 0; i < COUNT_OF(rpc_systems); ++i) { + session->system_contexts[i] = rpc_systems[i].alloc(session); + } + + RpcHandler rpc_handler = { + .message_handler = rpc_close_session_process, + .decode_submessage = NULL, + .context = session, + }; + rpc_add_handler(session, PB_Main_stop_session_tag, &rpc_handler); + + session->thread = furi_thread_alloc_ex("RpcSessionWorker", 3072, rpc_session_worker, session); + + furi_thread_set_state_context(session->thread, session); + furi_thread_set_state_callback(session->thread, rpc_session_thread_state_callback); + + furi_thread_start(session->thread); + + return session; +} + +void rpc_session_close(RpcSession* session) { + furi_assert(session); + furi_assert(session->rpc); + + rpc_session_set_send_bytes_callback(session, NULL); + rpc_session_set_close_callback(session, NULL); + rpc_session_set_buffer_is_empty_callback(session, NULL); + furi_thread_flags_set(furi_thread_get_id(session->thread), RpcEvtDisconnect); +} + +void rpc_on_system_start(void* p) { + UNUSED(p); + Rpc* rpc = malloc(sizeof(Rpc)); + + rpc->busy_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + + Cli* cli = furi_record_open(RECORD_CLI); + cli_add_command( + cli, "start_rpc_session", CliCommandFlagParallelSafe, rpc_cli_command_start_session, rpc); + + furi_record_create(RECORD_RPC, rpc); +} + +void rpc_add_handler(RpcSession* session, pb_size_t message_tag, RpcHandler* handler) { + furi_assert(RpcHandlerDict_get(session->handlers, message_tag) == NULL); + + RpcHandlerDict_set_at(session->handlers, message_tag, *handler); +} + +void rpc_send(RpcSession* session, PB_Main* message) { + furi_assert(session); + furi_assert(message); + + pb_ostream_t ostream = PB_OSTREAM_SIZING; + +#if SRV_RPC_DEBUG + FURI_LOG_I(TAG, "OUTPUT:"); + rpc_debug_print_message(message); +#endif + + bool result = pb_encode_ex(&ostream, &PB_Main_msg, message, PB_ENCODE_DELIMITED); + furi_check(result && ostream.bytes_written); + + uint8_t* buffer = malloc(ostream.bytes_written); + ostream = pb_ostream_from_buffer(buffer, ostream.bytes_written); + + pb_encode_ex(&ostream, &PB_Main_msg, message, PB_ENCODE_DELIMITED); + +#if SRV_RPC_DEBUG + rpc_debug_print_data("OUTPUT", buffer, ostream.bytes_written); +#endif + + furi_mutex_acquire(session->callbacks_mutex, FuriWaitForever); + if(session->send_bytes_callback) { + session->send_bytes_callback(session->context, buffer, ostream.bytes_written); + } + furi_mutex_release(session->callbacks_mutex); + + free(buffer); +} + +void rpc_send_and_release(RpcSession* session, PB_Main* message) { + rpc_send(session, message); + pb_release(&PB_Main_msg, message); +} + +void rpc_send_and_release_empty(RpcSession* session, uint32_t command_id, PB_CommandStatus status) { + furi_assert(session); + + PB_Main message = { + .command_id = command_id, + .command_status = status, + .has_next = false, + .which_content = PB_Main_empty_tag, + }; + + rpc_send_and_release(session, &message); + pb_release(&PB_Main_msg, &message); +} diff --git a/applications/rpc/rpc.h b/applications/services/rpc/rpc.h similarity index 87% rename from applications/rpc/rpc.h rename to applications/services/rpc/rpc.h index dea8b749f10..863bca355b4 100644 --- a/applications/rpc/rpc.h +++ b/applications/services/rpc/rpc.h @@ -5,8 +5,11 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + #define RPC_BUFFER_SIZE (1024) -#define RPC_MAX_MESSAGE_SIZE (1536) #define RECORD_RPC "rpc" @@ -21,12 +24,27 @@ typedef void (*RpcSendBytesCallback)(void* context, uint8_t* bytes, size_t bytes typedef void (*RpcBufferIsEmptyCallback)(void* context); /** Callback to notify transport layer that close_session command * is received. Any other actions lays on transport layer. - * No destruction or session close preformed. */ + * No destruction or session close performed. */ typedef void (*RpcSessionClosedCallback)(void* context); /** Callback to notify transport layer that session was closed * and all operations were finished */ typedef void (*RpcSessionTerminatedCallback)(void* context); +/** RPC owner */ +typedef enum { + RpcOwnerUnknown = 0, + RpcOwnerBle, + RpcOwnerUsb, + RpcOwnerCount, +} RpcOwner; + +/** Get RPC session owner + * + * @param session pointer to RpcSession descriptor + * @return session owner + */ +RpcOwner rpc_session_get_owner(RpcSession* session); + /** Open RPC session * * USAGE: @@ -41,10 +59,11 @@ typedef void (*RpcSessionTerminatedCallback)(void* context); * * * @param rpc instance + * @param owner owner of session * @return pointer to RpcSession descriptor, or * NULL if RPC is busy and can't open session now */ -RpcSession* rpc_session_open(Rpc* rpc); +RpcSession* rpc_session_open(Rpc* rpc, RpcOwner owner); /** Close RPC session * It is guaranteed that no callbacks will be called @@ -105,7 +124,7 @@ void rpc_session_set_terminated_callback( * * @return actually consumed bytes */ -size_t rpc_session_feed(RpcSession* session, uint8_t* buffer, size_t size, TickType_t timeout); +size_t rpc_session_feed(RpcSession* session, uint8_t* buffer, size_t size, uint32_t timeout); /** Get available size of RPC buffer * @@ -114,3 +133,7 @@ size_t rpc_session_feed(RpcSession* session, uint8_t* buffer, size_t size, TickT * @return bytes available in buffer */ size_t rpc_session_get_available_size(RpcSession* session); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/services/rpc/rpc_app.c b/applications/services/rpc/rpc_app.c new file mode 100644 index 00000000000..9af652dae17 --- /dev/null +++ b/applications/services/rpc/rpc_app.c @@ -0,0 +1,467 @@ +#include "flipper.pb.h" +#include +#include "rpc_i.h" +#include +#include +#include "rpc_app.h" + +#define TAG "RpcSystemApp" + +struct RpcAppSystem { + RpcSession* session; + + RpcAppSystemCallback callback; + void* callback_context; + + uint32_t error_code; + char* error_text; + + uint32_t last_command_id; + RpcAppSystemEventType last_event_type; +}; + +#define RPC_SYSTEM_APP_TEMP_ARGS_SIZE 16 + +static void rpc_system_app_send_state_response( + RpcAppSystem* rpc_app, + PB_App_AppState state, + const char* name) { + PB_Main* response = malloc(sizeof(PB_Main)); + + response->which_content = PB_Main_app_state_response_tag; + response->content.app_state_response.state = state; + + FURI_LOG_D(TAG, "%s", name); + rpc_send(rpc_app->session, response); + + free(response); +} + +static void rpc_system_app_send_error_response( + RpcAppSystem* rpc_app, + uint32_t command_id, + PB_CommandStatus status, + const char* name) { + // Not describing all possible errors as only APP_NOT_RUNNING is used + const char* status_str = status == PB_CommandStatus_ERROR_APP_NOT_RUNNING ? "APP_NOT_RUNNING" : + "UNKNOWN"; + FURI_LOG_E(TAG, "%s: %s, id %lu, status: %d", name, status_str, command_id, status); + rpc_send_and_release_empty(rpc_app->session, command_id, status); +} + +static void rpc_system_app_set_last_command( + RpcAppSystem* rpc_app, + uint32_t command_id, + const RpcAppSystemEvent* event) { + furi_assert(rpc_app->last_command_id == 0); + furi_assert(rpc_app->last_event_type == RpcAppEventTypeInvalid); + + rpc_app->last_command_id = command_id; + rpc_app->last_event_type = event->type; +} + +static void rpc_system_app_start_process(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(request->which_content == PB_Main_app_start_request_tag); + + RpcAppSystem* rpc_app = context; + furi_assert(rpc_app); + furi_assert(rpc_app->last_command_id == 0); + furi_assert(rpc_app->last_event_type == RpcAppEventTypeInvalid); + + FURI_LOG_D(TAG, "StartProcess: id %lu", request->command_id); + + Loader* loader = furi_record_open(RECORD_LOADER); + const char* app_name = request->content.app_start_request.name; + + PB_CommandStatus result; + + if(app_name) { + rpc_system_app_error_reset(rpc_app); + + char app_args_temp[RPC_SYSTEM_APP_TEMP_ARGS_SIZE]; + const char* app_args = request->content.app_start_request.args; + + if(app_args && strcmp(app_args, "RPC") == 0) { + // If app is being started in RPC mode - pass RPC context via args string + snprintf(app_args_temp, RPC_SYSTEM_APP_TEMP_ARGS_SIZE, "RPC %08lX", (uint32_t)rpc_app); + app_args = app_args_temp; + } + + const LoaderStatus status = loader_start(loader, app_name, app_args, NULL); + if(status == LoaderStatusErrorAppStarted) { + result = PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED; + } else if(status == LoaderStatusErrorInternal) { + result = PB_CommandStatus_ERROR_APP_CANT_START; + } else if(status == LoaderStatusErrorUnknownApp) { + result = PB_CommandStatus_ERROR_INVALID_PARAMETERS; + } else if(status == LoaderStatusOk) { + result = PB_CommandStatus_OK; + } else { + furi_crash(); + } + } else { + result = PB_CommandStatus_ERROR_INVALID_PARAMETERS; + } + + furi_record_close(RECORD_LOADER); + + FURI_LOG_D(TAG, "StartProcess: response id %lu, result %d", request->command_id, result); + rpc_send_and_release_empty(rpc_app->session, request->command_id, result); +} + +static void rpc_system_app_lock_status_process(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(request->which_content == PB_Main_app_lock_status_request_tag); + + RpcAppSystem* rpc_app = context; + furi_assert(rpc_app); + + rpc_system_app_error_reset(rpc_app); + + FURI_LOG_D(TAG, "LockStatus"); + + PB_Main* response = malloc(sizeof(PB_Main)); + + response->command_id = request->command_id; + response->which_content = PB_Main_app_lock_status_response_tag; + + Loader* loader = furi_record_open(RECORD_LOADER); + response->content.app_lock_status_response.locked = loader_is_locked(loader); + furi_record_close(RECORD_LOADER); + + FURI_LOG_D(TAG, "LockStatus: response"); + rpc_send_and_release(rpc_app->session, response); + + free(response); +} + +static void rpc_system_app_exit_request(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(request->which_content == PB_Main_app_exit_request_tag); + + RpcAppSystem* rpc_app = context; + furi_assert(rpc_app); + + if(rpc_app->callback) { + FURI_LOG_D(TAG, "ExitRequest: id %lu", request->command_id); + + const RpcAppSystemEvent event = { + .type = RpcAppEventTypeAppExit, + .data = + { + .type = RpcAppSystemEventDataTypeNone, + {0}, + }, + }; + + rpc_system_app_error_reset(rpc_app); + rpc_system_app_set_last_command(rpc_app, request->command_id, &event); + + rpc_app->callback(&event, rpc_app->callback_context); + + } else { + rpc_system_app_send_error_response( + rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "ExitRequest"); + } +} + +static void rpc_system_app_load_file(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(request->which_content == PB_Main_app_load_file_request_tag); + + RpcAppSystem* rpc_app = context; + furi_assert(rpc_app); + + if(rpc_app->callback) { + FURI_LOG_D(TAG, "LoadFile: id %lu", request->command_id); + + const RpcAppSystemEvent event = { + .type = RpcAppEventTypeLoadFile, + .data = + { + .type = RpcAppSystemEventDataTypeString, + .string = request->content.app_load_file_request.path, + }, + }; + + rpc_system_app_error_reset(rpc_app); + rpc_system_app_set_last_command(rpc_app, request->command_id, &event); + + rpc_app->callback(&event, rpc_app->callback_context); + + } else { + rpc_system_app_send_error_response( + rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "LoadFile"); + } +} + +static void rpc_system_app_button_press(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(request->which_content == PB_Main_app_button_press_request_tag); + + RpcAppSystem* rpc_app = context; + furi_assert(rpc_app); + + if(rpc_app->callback) { + FURI_LOG_D(TAG, "ButtonPress"); + + RpcAppSystemEvent event; + event.type = RpcAppEventTypeButtonPress; + + if(strlen(request->content.app_button_press_request.args) != 0) { + event.data.type = RpcAppSystemEventDataTypeString; + event.data.string = request->content.app_button_press_request.args; + } else { + event.data.type = RpcAppSystemEventDataTypeInt32; + event.data.i32 = request->content.app_button_press_request.index; + } + + rpc_system_app_error_reset(rpc_app); + rpc_system_app_set_last_command(rpc_app, request->command_id, &event); + + rpc_app->callback(&event, rpc_app->callback_context); + + } else { + rpc_system_app_send_error_response( + rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "ButtonPress"); + } +} + +static void rpc_system_app_button_release(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(request->which_content == PB_Main_app_button_release_request_tag); + + RpcAppSystem* rpc_app = context; + furi_assert(rpc_app); + + if(rpc_app->callback) { + FURI_LOG_D(TAG, "ButtonRelease"); + + const RpcAppSystemEvent event = { + .type = RpcAppEventTypeButtonRelease, + .data = + { + .type = RpcAppSystemEventDataTypeNone, + {0}, + }, + }; + + rpc_system_app_error_reset(rpc_app); + rpc_system_app_set_last_command(rpc_app, request->command_id, &event); + + rpc_app->callback(&event, rpc_app->callback_context); + + } else { + rpc_system_app_send_error_response( + rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "ButtonRelease"); + } +} + +static void rpc_system_app_get_error_process(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(request->which_content == PB_Main_app_get_error_request_tag); + + RpcAppSystem* rpc_app = context; + furi_assert(rpc_app); + + PB_Main* response = malloc(sizeof(PB_Main)); + + response->command_id = request->command_id; + response->which_content = PB_Main_app_get_error_response_tag; + response->content.app_get_error_response.code = rpc_app->error_code; + response->content.app_get_error_response.text = rpc_app->error_text; + + FURI_LOG_D(TAG, "GetError"); + rpc_send(rpc_app->session, response); + + free(response); +} + +static void rpc_system_app_data_exchange_process(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(request->which_content == PB_Main_app_data_exchange_request_tag); + + RpcAppSystem* rpc_app = context; + furi_assert(rpc_app); + + if(rpc_app->callback) { + FURI_LOG_D(TAG, "DataExchange"); + + const pb_bytes_array_t* data = request->content.app_data_exchange_request.data; + + const RpcAppSystemEvent event = { + .type = RpcAppEventTypeDataExchange, + .data = + { + .type = RpcAppSystemEventDataTypeBytes, + .bytes = + { + .ptr = data ? data->bytes : NULL, + .size = data ? data->size : 0, + }, + }, + }; + + rpc_system_app_error_reset(rpc_app); + rpc_system_app_set_last_command(rpc_app, request->command_id, &event); + + rpc_app->callback(&event, rpc_app->callback_context); + } else { + rpc_system_app_send_error_response( + rpc_app, request->command_id, PB_CommandStatus_ERROR_APP_NOT_RUNNING, "DataExchange"); + } +} + +void rpc_system_app_send_started(RpcAppSystem* rpc_app) { + furi_assert(rpc_app); + rpc_system_app_send_state_response(rpc_app, PB_App_AppState_APP_STARTED, "SendStarted"); +} + +void rpc_system_app_send_exited(RpcAppSystem* rpc_app) { + furi_assert(rpc_app); + rpc_system_app_send_state_response(rpc_app, PB_App_AppState_APP_CLOSED, "SendExit"); +} + +void rpc_system_app_confirm(RpcAppSystem* rpc_app, bool result) { + furi_assert(rpc_app); + furi_assert(rpc_app->last_command_id != 0); + /* Ensure that only commands of these types can be confirmed */ + furi_assert( + rpc_app->last_event_type == RpcAppEventTypeAppExit || + rpc_app->last_event_type == RpcAppEventTypeLoadFile || + rpc_app->last_event_type == RpcAppEventTypeButtonPress || + rpc_app->last_event_type == RpcAppEventTypeButtonRelease || + rpc_app->last_event_type == RpcAppEventTypeDataExchange); + + const uint32_t last_command_id = rpc_app->last_command_id; + const RpcAppSystemEventType last_event_type = rpc_app->last_event_type; + + rpc_app->last_command_id = 0; + rpc_app->last_event_type = RpcAppEventTypeInvalid; + + const PB_CommandStatus status = result ? PB_CommandStatus_OK : + PB_CommandStatus_ERROR_APP_CMD_ERROR; + FURI_LOG_D( + TAG, + "AppConfirm: event %d last_id %lu status %d", + last_event_type, + last_command_id, + status); + + rpc_send_and_release_empty(rpc_app->session, last_command_id, status); +} + +void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx) { + furi_assert(rpc_app); + + rpc_app->callback = callback; + rpc_app->callback_context = ctx; +} + +void rpc_system_app_set_error_code(RpcAppSystem* rpc_app, uint32_t error_code) { + furi_assert(rpc_app); + rpc_app->error_code = error_code; +} + +void rpc_system_app_set_error_text(RpcAppSystem* rpc_app, const char* error_text) { + furi_assert(rpc_app); + + if(rpc_app->error_text) { + free(rpc_app->error_text); + } + + rpc_app->error_text = error_text ? strdup(error_text) : NULL; +} + +void rpc_system_app_error_reset(RpcAppSystem* rpc_app) { + furi_assert(rpc_app); + + rpc_system_app_set_error_code(rpc_app, 0); + rpc_system_app_set_error_text(rpc_app, NULL); +} + +void rpc_system_app_exchange_data(RpcAppSystem* rpc_app, const uint8_t* data, size_t data_size) { + furi_assert(rpc_app); + + PB_Main* request = malloc(sizeof(PB_Main)); + + request->which_content = PB_Main_app_data_exchange_request_tag; + PB_App_DataExchangeRequest* content = &request->content.app_data_exchange_request; + + if(data && data_size) { + content->data = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(data_size)); + content->data->size = data_size; + memcpy(content->data->bytes, data, data_size); + } else { + content->data = NULL; + } + + rpc_send_and_release(rpc_app->session, request); + + free(request); +} + +void* rpc_system_app_alloc(RpcSession* session) { + furi_assert(session); + + RpcAppSystem* rpc_app = malloc(sizeof(RpcAppSystem)); + rpc_app->session = session; + + RpcHandler rpc_handler = { + .message_handler = NULL, + .decode_submessage = NULL, + .context = rpc_app, + }; + + rpc_handler.message_handler = rpc_system_app_start_process; + rpc_add_handler(session, PB_Main_app_start_request_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_app_lock_status_process; + rpc_add_handler(session, PB_Main_app_lock_status_request_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_app_exit_request; + rpc_add_handler(session, PB_Main_app_exit_request_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_app_load_file; + rpc_add_handler(session, PB_Main_app_load_file_request_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_app_button_press; + rpc_add_handler(session, PB_Main_app_button_press_request_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_app_button_release; + rpc_add_handler(session, PB_Main_app_button_release_request_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_app_get_error_process; + rpc_add_handler(session, PB_Main_app_get_error_request_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_app_data_exchange_process; + rpc_add_handler(session, PB_Main_app_data_exchange_request_tag, &rpc_handler); + + return rpc_app; +} + +void rpc_system_app_free(void* context) { + RpcAppSystem* rpc_app = context; + furi_assert(rpc_app); + furi_assert(rpc_app->session); + + if(rpc_app->callback) { + const RpcAppSystemEvent event = { + .type = RpcAppEventTypeSessionClose, + .data = + { + .type = RpcAppSystemEventDataTypeNone, + {0}, + }, + }; + + rpc_app->callback(&event, rpc_app->callback_context); + } + + while(rpc_app->callback) { + furi_delay_tick(1); + } + + free(rpc_app); +} diff --git a/applications/services/rpc/rpc_app.h b/applications/services/rpc/rpc_app.h new file mode 100644 index 00000000000..4ee5a24d379 --- /dev/null +++ b/applications/services/rpc/rpc_app.h @@ -0,0 +1,215 @@ +/** + * @file rpc_app.h + * @brief Application RPC subsystem interface. + * + * The application RPC subsystem provides facilities for interacting with applications, + * such as starting/stopping, passing parameters, sending commands and exchanging arbitrary data. + * + * All commands are handled asynchronously via a user-settable callback. + * + * For a complete description of message types handled in this subsystem, + * see https://github.com/flipperdevices/flipperzero-protobuf/blob/dev/application.proto + */ +#pragma once + +#include "rpc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Enumeration of possible event data types. + */ +typedef enum { + RpcAppSystemEventDataTypeNone, /**< No data is provided by the event. */ + RpcAppSystemEventDataTypeString, /**< Event data contains a zero-terminated string. */ + RpcAppSystemEventDataTypeInt32, /**< Event data contains a signed 32-bit integer. */ + RpcAppSystemEventDataTypeBytes, /**< Event data contains zero or more bytes. */ +} RpcAppSystemEventDataType; + +/** + * @brief Event data structure, containing the type and associated data. + * + * All below fields except for type are valid only if the respective type is set. + */ +typedef struct { + RpcAppSystemEventDataType + type; /**< Type of the data. The meaning of other fields depends on this one. */ + union { + const char* string; /**< Pointer to a zero-terminated character string. */ + int32_t i32; /**< Signed 32-bit integer value. */ + struct { + const uint8_t* ptr; /**< Pointer to the byte array data. */ + size_t size; /**< Size of the byte array, in bytes. */ + } bytes; /**< Byte array of arbitrary length. */ + }; +} RpcAppSystemEventData; + +/** + * @brief Enumeration of possible event types. + */ +typedef enum { + /** + * @brief Denotes an invalid state. + * + * An event of this type shall never be passed into the callback. + */ + RpcAppEventTypeInvalid, + /** + * @brief The client side has closed the session. + * + * After receiving this event, the RPC context is no more valid. + */ + RpcAppEventTypeSessionClose, + /** + * @brief The client has requested the application to exit. + * + * The application must exit after receiving this command. + */ + RpcAppEventTypeAppExit, + /** + * @brief The client has requested the application to load a file. + * + * This command's meaning is application-specific, i.e. the application might or + * might not require additional commands after loading a file to do anything useful. + */ + RpcAppEventTypeLoadFile, + /** + * @brief The client has informed the application that a button has been pressed. + * + * This command's meaning is application-specific, e.g. to select a part of the + * previously loaded file or to invoke a particular function within the application. + */ + RpcAppEventTypeButtonPress, + /** + * @brief The client has informed the application that a button has been released. + * + * This command's meaning is application-specific, e.g. to cease + * all activities to be conducted while a button is being pressed. + */ + RpcAppEventTypeButtonRelease, + /** + * @brief The client has sent a byte array of arbitrary size. + * + * This command's purpose is bi-directional exchange of arbitrary raw data. + * Useful for implementing higher-level protocols while using the RPC as a transport layer. + */ + RpcAppEventTypeDataExchange, +} RpcAppSystemEventType; + +/** + * @brief RPC application subsystem event structure. + */ +typedef struct { + RpcAppSystemEventType type; /**< Type of the event. */ + RpcAppSystemEventData data; /**< Data associated with the event. */ +} RpcAppSystemEvent; + +/** + * @brief Callback function type. + * + * A function of this type must be passed to rpc_system_app_set_callback() by the user code. + * + * @warning The event pointer is valid ONLY inside the callback function. + * + * @param[in] event pointer to the event object. Valid only inside the callback function. + * @param[in,out] context pointer to the user-defined context object. + */ +typedef void (*RpcAppSystemCallback)(const RpcAppSystemEvent* event, void* context); + +/** + * @brief RPC application subsystem opaque type declaration. + */ +typedef struct RpcAppSystem RpcAppSystem; + +/** + * @brief Set the callback function for use by an RpcAppSystem instance. + * + * @param[in,out] rpc_app pointer to the instance to be configured. + * @param[in] callback pointer to the function to be called upon message reception. + * @param[in,out] context pointer to the user-defined context object. Will be passed to the callback. + */ +void rpc_system_app_set_callback( + RpcAppSystem* rpc_app, + RpcAppSystemCallback callback, + void* context); + +/** + * @brief Send a notification that an RpcAppSystem instance has been started and is ready. + * + * Call this function once right after acquiring an RPC context and setting the callback. + * + * @param[in,out] rpc_app pointer to the instance to be used. + */ +void rpc_system_app_send_started(RpcAppSystem* rpc_app); + +/** + * @brief Send a notification that the application using an RpcAppSystem instance is about to exit. + * + * Call this function when the application is about to exit (usually in the *_free() function). + * + * @param[in,out] rpc_app pointer to the instance to be used. + */ +void rpc_system_app_send_exited(RpcAppSystem* rpc_app); + +/** + * @brief Send a confirmation that the application using an RpcAppSystem instance has handled the event. + * + * An explicit confirmation is required for the following event types: + * - RpcAppEventTypeAppExit + * - RpcAppEventTypeLoadFile + * - RpcAppEventTypeButtonPress + * - RpcAppEventTypeButtonRelease + * - RpcAppEventTypeDataExchange + * + * Not confirming these events will result in a client-side timeout. + * + * @param[in,out] rpc_app pointer to the instance to be used. + * @param[in] result whether the command was successfully handled or not (true for success). + */ +void rpc_system_app_confirm(RpcAppSystem* rpc_app, bool result); + +/** + * @brief Set the error code stored in an RpcAppSystem instance. + * + * The error code can be retrieved by the client at any time by using the GetError request. + * The error code value has no meaning within the subsystem, i.e. it is only passed through to the client. + * + * @param[in,out] rpc_app pointer to the instance to be modified. + * @param[in] error_code arbitrary error code to be set. + */ +void rpc_system_app_set_error_code(RpcAppSystem* rpc_app, uint32_t error_code); + +/** + * @brief Set the error text stored in an RpcAppSystem instance. + * + * The error text can be retrieved by the client at any time by using the GetError request. + * The text has no meaning within the subsystem, i.e. it is only passed through to the client. + * + * @param[in,out] rpc_app pointer to the instance to be modified. + * @param[in] error_text Pointer to a zero-terminated string containing the error text. + */ +void rpc_system_app_set_error_text(RpcAppSystem* rpc_app, const char* error_text); + +/** + * @brief Reset the error code and text stored in an RpcAppSystem instance. + * + * Resets the error code to 0 and error text to "" (empty string). + * + * @param[in,out] rpc_app pointer to the instance to be reset. + */ +void rpc_system_app_error_reset(RpcAppSystem* rpc_app); + +/** + * @brief Send a byte array of arbitrary data to the client using an RpcAppSystem instance. + * + * @param[in,out] rpc_app pointer to the instance to be used. + * @param[in] data pointer to the data buffer to be sent. + * @param[in] data_size size of the data buffer, in bytes. + */ +void rpc_system_app_exchange_data(RpcAppSystem* rpc_app, const uint8_t* data, size_t data_size); + +#ifdef __cplusplus +} +#endif diff --git a/applications/services/rpc/rpc_cli.c b/applications/services/rpc/rpc_cli.c new file mode 100644 index 00000000000..4612752a83c --- /dev/null +++ b/applications/services/rpc/rpc_cli.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include + +#define TAG "RpcCli" + +typedef struct { + Cli* cli; + bool session_close_request; + FuriSemaphore* terminate_semaphore; +} CliRpc; + +#define CLI_READ_BUFFER_SIZE 64 + +static void rpc_cli_send_bytes_callback(void* context, uint8_t* bytes, size_t bytes_len) { + furi_assert(context); + furi_assert(bytes); + furi_assert(bytes_len > 0); + CliRpc* cli_rpc = context; + + cli_write(cli_rpc->cli, bytes, bytes_len); +} + +static void rpc_cli_session_close_callback(void* context) { + furi_assert(context); + CliRpc* cli_rpc = context; + + cli_rpc->session_close_request = true; +} + +static void rpc_cli_session_terminated_callback(void* context) { + furi_check(context); + CliRpc* cli_rpc = context; + + furi_semaphore_release(cli_rpc->terminate_semaphore); +} + +void rpc_cli_command_start_session(Cli* cli, FuriString* args, void* context) { + UNUSED(args); + furi_assert(cli); + furi_assert(context); + Rpc* rpc = context; + + uint32_t mem_before = memmgr_get_free_heap(); + FURI_LOG_D(TAG, "Free memory %lu", mem_before); + + furi_hal_usb_lock(); + RpcSession* rpc_session = rpc_session_open(rpc, RpcOwnerUsb); + if(rpc_session == NULL) { + printf("Session start error\r\n"); + furi_hal_usb_unlock(); + return; + } + + CliRpc cli_rpc = {.cli = cli, .session_close_request = false}; + cli_rpc.terminate_semaphore = furi_semaphore_alloc(1, 0); + rpc_session_set_context(rpc_session, &cli_rpc); + rpc_session_set_send_bytes_callback(rpc_session, rpc_cli_send_bytes_callback); + rpc_session_set_close_callback(rpc_session, rpc_cli_session_close_callback); + rpc_session_set_terminated_callback(rpc_session, rpc_cli_session_terminated_callback); + + uint8_t* buffer = malloc(CLI_READ_BUFFER_SIZE); + size_t size_received = 0; + + while(1) { + size_received = cli_read_timeout(cli_rpc.cli, buffer, CLI_READ_BUFFER_SIZE, 50); + if(!cli_is_connected(cli_rpc.cli) || cli_rpc.session_close_request) { + break; + } + + if(size_received) { + size_t fed_bytes = rpc_session_feed(rpc_session, buffer, size_received, 3000); + (void)fed_bytes; + furi_assert(fed_bytes == size_received); + } + } + + rpc_session_close(rpc_session); + + furi_check( + furi_semaphore_acquire(cli_rpc.terminate_semaphore, FuriWaitForever) == FuriStatusOk); + + furi_semaphore_free(cli_rpc.terminate_semaphore); + + free(buffer); + furi_hal_usb_unlock(); +} diff --git a/applications/services/rpc/rpc_debug.c b/applications/services/rpc/rpc_debug.c new file mode 100644 index 00000000000..edc2b002513 --- /dev/null +++ b/applications/services/rpc/rpc_debug.c @@ -0,0 +1,259 @@ +#include "rpc_i.h" + +static size_t rpc_debug_print_file_msg( + FuriString* str, + const char* prefix, + const PB_Storage_File* msg_file, + size_t msg_files_size) { + size_t cnt = 0; + + for(size_t i = 0; i < msg_files_size; ++i, ++msg_file) { + furi_string_cat_printf( + str, + "%s[%c] size: %5lu", + prefix, + msg_file->type == PB_Storage_File_FileType_DIR ? 'd' : 'f', + msg_file->size); + + if(msg_file->name) { + furi_string_cat_printf(str, " \'%s\'", msg_file->name); + } + + if(msg_file->data && msg_file->data->size) { + furi_string_cat_printf( + str, + " (%d):\'%.*s%s\'", + msg_file->data->size, + MIN(msg_file->data->size, 30), + msg_file->data->bytes, + msg_file->data->size > 30 ? "..." : ""); + } + + furi_string_cat_printf(str, "\r\n"); + } + + return cnt; +} + +void rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size) { + FuriString* str; + str = furi_string_alloc(); + furi_string_reserve(str, 100 + size * 5); + + furi_string_cat_printf(str, "\r\n%s DEC(%zu): {", prefix, size); + for(size_t i = 0; i < size; ++i) { + furi_string_cat_printf(str, "%d, ", buffer[i]); + } + furi_string_cat_printf(str, "}\r\n"); + + printf("%s", furi_string_get_cstr(str)); + furi_string_reset(str); + furi_string_reserve(str, 100 + size * 3); + + furi_string_cat_printf(str, "%s HEX(%zu): {", prefix, size); + for(size_t i = 0; i < size; ++i) { + furi_string_cat_printf(str, "%02X", buffer[i]); + } + furi_string_cat_printf(str, "}\r\n\r\n"); + + printf("%s", furi_string_get_cstr(str)); + furi_string_free(str); +} + +void rpc_debug_print_message(const PB_Main* message) { + FuriString* str; + str = furi_string_alloc(); + + furi_string_cat_printf( + str, + "PB_Main: {\r\n\tresult: %d cmd_id: %lu (%s)\r\n", + message->command_status, + message->command_id, + message->has_next ? "has_next" : "last"); + switch(message->which_content) { + default: + /* not implemented yet */ + furi_string_cat_printf(str, "\tNOT_IMPLEMENTED (%d) {\r\n", message->which_content); + break; + case PB_Main_stop_session_tag: + furi_string_cat_printf(str, "\tstop_session {\r\n"); + break; + case PB_Main_app_start_request_tag: { + furi_string_cat_printf(str, "\tapp_start {\r\n"); + const char* name = message->content.app_start_request.name; + const char* args = message->content.app_start_request.args; + if(name) { + furi_string_cat_printf(str, "\t\tname: %s\r\n", name); + } + if(args) { + furi_string_cat_printf(str, "\t\targs: %s\r\n", args); + } + break; + } + case PB_Main_app_lock_status_request_tag: { + furi_string_cat_printf(str, "\tapp_lock_status_request {\r\n"); + break; + } + case PB_Main_app_lock_status_response_tag: { + furi_string_cat_printf(str, "\tapp_lock_status_response {\r\n"); + bool lock_status = message->content.app_lock_status_response.locked; + furi_string_cat_printf(str, "\t\tlocked: %s\r\n", lock_status ? "true" : "false"); + break; + } + case PB_Main_storage_md5sum_request_tag: { + furi_string_cat_printf(str, "\tmd5sum_request {\r\n"); + const char* path = message->content.storage_md5sum_request.path; + if(path) { + furi_string_cat_printf(str, "\t\tpath: %s\r\n", path); + } + break; + } + case PB_Main_storage_md5sum_response_tag: { + furi_string_cat_printf(str, "\tmd5sum_response {\r\n"); + const char* md5sum = message->content.storage_md5sum_response.md5sum; + if(md5sum) { //-V547 + furi_string_cat_printf(str, "\t\tmd5sum: %s\r\n", md5sum); + } + break; + } + case PB_Main_system_ping_request_tag: + furi_string_cat_printf(str, "\tping_request {\r\n"); + break; + case PB_Main_system_ping_response_tag: + furi_string_cat_printf(str, "\tping_response {\r\n"); + break; + case PB_Main_system_device_info_request_tag: + furi_string_cat_printf(str, "\tdevice_info_request {\r\n"); + break; + case PB_Main_system_device_info_response_tag: + furi_string_cat_printf(str, "\tdevice_info_response {\r\n"); + furi_string_cat_printf( + str, + "\t\t%s: %s\r\n", + message->content.system_device_info_response.key, + message->content.system_device_info_response.value); + break; + case PB_Main_storage_mkdir_request_tag: + furi_string_cat_printf(str, "\tmkdir {\r\n"); + break; + case PB_Main_storage_delete_request_tag: { + furi_string_cat_printf(str, "\tdelete {\r\n"); + const char* path = message->content.storage_delete_request.path; + if(path) { + furi_string_cat_printf(str, "\t\tpath: %s\r\n", path); + } + break; + } + case PB_Main_empty_tag: + furi_string_cat_printf(str, "\tempty {\r\n"); + break; + case PB_Main_storage_info_request_tag: { + furi_string_cat_printf(str, "\tinfo_request {\r\n"); + const char* path = message->content.storage_info_request.path; + if(path) { + furi_string_cat_printf(str, "\t\tpath: %s\r\n", path); + } + break; + } + case PB_Main_storage_info_response_tag: { + furi_string_cat_printf(str, "\tinfo_response {\r\n"); + furi_string_cat_printf( + str, "\t\ttotal_space: %llu\r\n", message->content.storage_info_response.total_space); + furi_string_cat_printf( + str, "\t\tfree_space: %llu\r\n", message->content.storage_info_response.free_space); + break; + } + case PB_Main_storage_stat_request_tag: { + furi_string_cat_printf(str, "\tstat_request {\r\n"); + const char* path = message->content.storage_stat_request.path; + if(path) { + furi_string_cat_printf(str, "\t\tpath: %s\r\n", path); + } + break; + } + case PB_Main_storage_stat_response_tag: { + furi_string_cat_printf(str, "\tstat_response {\r\n"); + if(message->content.storage_stat_response.has_file) { + const PB_Storage_File* msg_file = &message->content.storage_stat_response.file; + rpc_debug_print_file_msg(str, "\t\t\t", msg_file, 1); + } + break; + } + case PB_Main_storage_list_request_tag: { + furi_string_cat_printf(str, "\tlist_request {\r\n"); + const char* path = message->content.storage_list_request.path; + if(path) { + furi_string_cat_printf(str, "\t\tpath: %s\r\n", path); + } + break; + } + case PB_Main_storage_read_request_tag: { + furi_string_cat_printf(str, "\tread_request {\r\n"); + const char* path = message->content.storage_read_request.path; + if(path) { + furi_string_cat_printf(str, "\t\tpath: %s\r\n", path); + } + break; + } + case PB_Main_storage_write_request_tag: { + furi_string_cat_printf(str, "\twrite_request {\r\n"); + const char* path = message->content.storage_write_request.path; + if(path) { + furi_string_cat_printf(str, "\t\tpath: %s\r\n", path); + } + if(message->content.storage_write_request.has_file) { + const PB_Storage_File* msg_file = &message->content.storage_write_request.file; + rpc_debug_print_file_msg(str, "\t\t\t", msg_file, 1); + } + break; + } + case PB_Main_storage_read_response_tag: + furi_string_cat_printf(str, "\tread_response {\r\n"); + if(message->content.storage_read_response.has_file) { + const PB_Storage_File* msg_file = &message->content.storage_read_response.file; + rpc_debug_print_file_msg(str, "\t\t\t", msg_file, 1); + } + break; + case PB_Main_storage_list_response_tag: { + const PB_Storage_File* msg_file = message->content.storage_list_response.file; + size_t msg_file_count = message->content.storage_list_response.file_count; + furi_string_cat_printf(str, "\tlist_response {\r\n"); + rpc_debug_print_file_msg(str, "\t\t", msg_file, msg_file_count); + break; + } + case PB_Main_storage_rename_request_tag: { + furi_string_cat_printf(str, "\trename_request {\r\n"); + furi_string_cat_printf( + str, "\t\told_path: %s\r\n", message->content.storage_rename_request.old_path); + furi_string_cat_printf( + str, "\t\tnew_path: %s\r\n", message->content.storage_rename_request.new_path); + break; + } + case PB_Main_gui_start_screen_stream_request_tag: + furi_string_cat_printf(str, "\tstart_screen_stream {\r\n"); + break; + case PB_Main_gui_stop_screen_stream_request_tag: + furi_string_cat_printf(str, "\tstop_screen_stream {\r\n"); + break; + case PB_Main_gui_screen_frame_tag: + furi_string_cat_printf(str, "\tscreen_frame {\r\n"); + break; + case PB_Main_gui_send_input_event_request_tag: + furi_string_cat_printf(str, "\tsend_input_event {\r\n"); + furi_string_cat_printf( + str, "\t\tkey: %d\r\n", message->content.gui_send_input_event_request.key); + furi_string_cat_printf( + str, "\t\type: %d\r\n", message->content.gui_send_input_event_request.type); + break; + case PB_Main_gui_start_virtual_display_request_tag: + furi_string_cat_printf(str, "\tstart_virtual_display {\r\n"); + break; + case PB_Main_gui_stop_virtual_display_request_tag: + furi_string_cat_printf(str, "\tstop_virtual_display {\r\n"); + break; + } + furi_string_cat_printf(str, "\t}\r\n}\r\n"); + printf("%s", furi_string_get_cstr(str)); + + furi_string_free(str); +} diff --git a/applications/services/rpc/rpc_desktop.c b/applications/services/rpc/rpc_desktop.c new file mode 100644 index 00000000000..0d72b43d551 --- /dev/null +++ b/applications/services/rpc/rpc_desktop.c @@ -0,0 +1,137 @@ +#include "flipper.pb.h" +#include "rpc_i.h" +#include +#include "desktop.pb.h" + +#define TAG "RpcDesktop" + +typedef struct { + RpcSession* session; + Desktop* desktop; + FuriPubSub* status_pubsub; + FuriPubSubSubscription* status_subscription; +} RpcDesktop; + +static void rpc_desktop_on_is_locked_request(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_desktop_is_locked_request_tag); + + FURI_LOG_D(TAG, "IsLockedRequest"); + RpcDesktop* rpc_desktop = context; + RpcSession* session = rpc_desktop->session; + + PB_CommandStatus ret = desktop_api_is_locked(rpc_desktop->desktop) ? PB_CommandStatus_OK : + PB_CommandStatus_ERROR; + + rpc_send_and_release_empty(session, request->command_id, ret); +} + +static void rpc_desktop_on_unlock_request(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_desktop_unlock_request_tag); + + FURI_LOG_D(TAG, "UnlockRequest"); + RpcDesktop* rpc_desktop = context; + RpcSession* session = rpc_desktop->session; + + desktop_api_unlock(rpc_desktop->desktop); + + rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); +} + +static void rpc_desktop_on_desktop_pubsub(const void* message, void* context) { + RpcDesktop* rpc_desktop = context; + RpcSession* session = rpc_desktop->session; + const DesktopStatus* status = message; + + PB_Main rpc_message = { + .command_id = 0, + .command_status = PB_CommandStatus_OK, + .has_next = false, + .which_content = PB_Main_desktop_status_tag, + .content.desktop_status.locked = status->locked, + }; + rpc_send_and_release(session, &rpc_message); +} + +static void rpc_desktop_on_status_subscribe_request(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_desktop_status_subscribe_request_tag); + + FURI_LOG_D(TAG, "StatusSubscribeRequest"); + RpcDesktop* rpc_desktop = context; + RpcSession* session = rpc_desktop->session; + + if(rpc_desktop->status_subscription) { + rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_ERROR); + } else { + rpc_desktop->status_subscription = furi_pubsub_subscribe( + rpc_desktop->status_pubsub, rpc_desktop_on_desktop_pubsub, rpc_desktop); + rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); + } +} + +static void rpc_desktop_on_status_unsubscribe_request(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_desktop_status_unsubscribe_request_tag); + + FURI_LOG_D(TAG, "StatusUnsubscribeRequest"); + RpcDesktop* rpc_desktop = context; + RpcSession* session = rpc_desktop->session; + + if(rpc_desktop->status_subscription) { + furi_pubsub_unsubscribe(rpc_desktop->status_pubsub, rpc_desktop->status_subscription); + rpc_desktop->status_subscription = NULL; + rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); + } else { + rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_ERROR); + } +} + +void* rpc_desktop_alloc(RpcSession* session) { + furi_assert(session); + + RpcDesktop* rpc_desktop = malloc(sizeof(RpcDesktop)); + rpc_desktop->desktop = furi_record_open(RECORD_DESKTOP); + rpc_desktop->status_pubsub = desktop_api_get_status_pubsub(rpc_desktop->desktop); + rpc_desktop->session = session; + + RpcHandler rpc_handler = { + .message_handler = NULL, + .decode_submessage = NULL, + .context = rpc_desktop, + }; + + rpc_handler.message_handler = rpc_desktop_on_is_locked_request; + rpc_add_handler(session, PB_Main_desktop_is_locked_request_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_desktop_on_unlock_request; + rpc_add_handler(session, PB_Main_desktop_unlock_request_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_desktop_on_status_subscribe_request; + rpc_add_handler(session, PB_Main_desktop_status_subscribe_request_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_desktop_on_status_unsubscribe_request; + rpc_add_handler(session, PB_Main_desktop_status_unsubscribe_request_tag, &rpc_handler); + + return rpc_desktop; +} + +void rpc_desktop_free(void* context) { + furi_assert(context); + RpcDesktop* rpc_desktop = context; + + if(rpc_desktop->status_subscription) { + furi_pubsub_unsubscribe(rpc_desktop->status_pubsub, rpc_desktop->status_subscription); + } + + furi_assert(rpc_desktop->desktop); + furi_record_close(RECORD_DESKTOP); + + rpc_desktop->session = NULL; + free(rpc_desktop); +} \ No newline at end of file diff --git a/applications/rpc/rpc_gpio.c b/applications/services/rpc/rpc_gpio.c similarity index 98% rename from applications/rpc/rpc_gpio.c rename to applications/services/rpc/rpc_gpio.c index 614e775a1c5..09e7385052e 100644 --- a/applications/rpc/rpc_gpio.c +++ b/applications/services/rpc/rpc_gpio.c @@ -57,7 +57,6 @@ static void rpc_system_gpio_set_pin_mode(const PB_Main* request, void* context) furi_assert(request->which_content == PB_Main_gpio_set_pin_mode_tag); RpcSession* session = context; - furi_assert(session); PB_Gpio_SetPinMode cmd = request->content.gpio_set_pin_mode; const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); @@ -77,7 +76,6 @@ static void rpc_system_gpio_write_pin(const PB_Main* request, void* context) { furi_assert(request->which_content == PB_Main_gpio_write_pin_tag); RpcSession* session = context; - furi_assert(session); PB_Gpio_WritePin cmd = request->content.gpio_write_pin; const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); @@ -105,7 +103,6 @@ static void rpc_system_gpio_read_pin(const PB_Main* request, void* context) { furi_assert(request->which_content == PB_Main_gpio_read_pin_tag); RpcSession* session = context; - furi_assert(session); PB_Gpio_ReadPin cmd = request->content.gpio_read_pin; const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); @@ -133,7 +130,6 @@ void rpc_system_gpio_get_pin_mode(const PB_Main* request, void* context) { furi_assert(request->which_content == PB_Main_gpio_get_pin_mode_tag); RpcSession* session = context; - furi_assert(session); PB_Gpio_GetPinMode cmd = request->content.gpio_get_pin_mode; const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); @@ -170,7 +166,6 @@ void rpc_system_gpio_set_input_pull(const PB_Main* request, void* context) { furi_assert(request->which_content == PB_Main_gpio_set_input_pull_tag); RpcSession* session = context; - furi_assert(session); PB_Gpio_SetInputPull cmd = request->content.gpio_set_input_pull; const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); diff --git a/applications/rpc/rpc_gui.c b/applications/services/rpc/rpc_gui.c similarity index 79% rename from applications/rpc/rpc_gui.c rename to applications/services/rpc/rpc_gui.c index 029ed010645..ca8fc61a45f 100644 --- a/applications/rpc/rpc_gui.c +++ b/applications/services/rpc/rpc_gui.c @@ -1,7 +1,9 @@ -#include "flipper.pb.h" #include "rpc_i.h" -#include "gui.pb.h" #include +#include + +#include +#include #define TAG "RpcGui" @@ -12,6 +14,8 @@ typedef enum { #define RpcGuiWorkerFlagAny (RpcGuiWorkerFlagTransmit | RpcGuiWorkerFlagExit) +#define RPC_GUI_INPUT_RESET (0u) + typedef struct { RpcSession* session; Gui* gui; @@ -26,10 +30,25 @@ typedef struct { bool virtual_display_not_empty; bool is_streaming; + + uint32_t input_key_counter[InputKeyMAX]; + uint32_t input_counter; + + ViewPort* rpc_session_active_viewport; } RpcGuiSystem; -static void - rpc_system_gui_screen_stream_frame_callback(uint8_t* data, size_t size, void* context) { +static const PB_Gui_ScreenOrientation rpc_system_gui_screen_orientation_map[] = { + [CanvasOrientationHorizontal] = PB_Gui_ScreenOrientation_HORIZONTAL, + [CanvasOrientationHorizontalFlip] = PB_Gui_ScreenOrientation_HORIZONTAL_FLIP, + [CanvasOrientationVertical] = PB_Gui_ScreenOrientation_VERTICAL, + [CanvasOrientationVerticalFlip] = PB_Gui_ScreenOrientation_VERTICAL_FLIP, +}; + +static void rpc_system_gui_screen_stream_frame_callback( + uint8_t* data, + size_t size, + CanvasOrientation orientation, + void* context) { furi_assert(data); furi_assert(context); @@ -39,6 +58,8 @@ static void furi_assert(size == rpc_gui->transmit_frame->content.gui_screen_frame.data->size); memcpy(buffer, data, size); + rpc_gui->transmit_frame->content.gui_screen_frame.orientation = + rpc_system_gui_screen_orientation_map[orientation]; furi_thread_flags_set(furi_thread_get_id(rpc_gui->transmit_thread), RpcGuiWorkerFlagTransmit); } @@ -48,12 +69,22 @@ static int32_t rpc_system_gui_screen_stream_frame_transmit_thread(void* context) RpcGuiSystem* rpc_gui = (RpcGuiSystem*)context; + uint32_t transmit_time = 0; while(true) { uint32_t flags = furi_thread_flags_wait(RpcGuiWorkerFlagAny, FuriFlagWaitAny, FuriWaitForever); + if(flags & RpcGuiWorkerFlagTransmit) { + transmit_time = furi_get_tick(); rpc_send(rpc_gui->session, rpc_gui->transmit_frame); + transmit_time = furi_get_tick() - transmit_time; + + // Guaranteed bandwidth reserve + uint32_t extra_delay = transmit_time / 20; + if(extra_delay > 500) extra_delay = 500; + if(extra_delay) furi_delay_tick(extra_delay); } + if(flags & RpcGuiWorkerFlagExit) { break; } @@ -88,12 +119,8 @@ static void rpc_system_gui_start_screen_stream_process(const PB_Main* request, v malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(framebuffer_size)); rpc_gui->transmit_frame->content.gui_screen_frame.data->size = framebuffer_size; // Transmission thread for async TX - rpc_gui->transmit_thread = furi_thread_alloc(); - furi_thread_set_name(rpc_gui->transmit_thread, "GuiRpcWorker"); - furi_thread_set_callback( - rpc_gui->transmit_thread, rpc_system_gui_screen_stream_frame_transmit_thread); - furi_thread_set_context(rpc_gui->transmit_thread, rpc_gui); - furi_thread_set_stack_size(rpc_gui->transmit_thread, 1024); + rpc_gui->transmit_thread = furi_thread_alloc_ex( + "GuiRpcWorker", 1024, rpc_system_gui_screen_stream_frame_transmit_thread, rpc_gui); furi_thread_start(rpc_gui->transmit_thread); // GUI framebuffer callback gui_add_framebuffer_callback( @@ -198,6 +225,22 @@ static void return; } + // Event sequence shenanigans + event.sequence_source = INPUT_SEQUENCE_SOURCE_SOFTWARE; + if(event.type == InputTypePress) { + rpc_gui->input_counter++; + if(rpc_gui->input_counter == RPC_GUI_INPUT_RESET) rpc_gui->input_counter++; + rpc_gui->input_key_counter[event.key] = rpc_gui->input_counter; + } + if(rpc_gui->input_key_counter[event.key] == RPC_GUI_INPUT_RESET) { + FURI_LOG_W(TAG, "Out of sequence input event: key %d, type %d,", event.key, event.type); + } + event.sequence_counter = rpc_gui->input_key_counter[event.key]; + if(event.type == InputTypeRelease) { + rpc_gui->input_key_counter[event.key] = RPC_GUI_INPUT_RESET; + } + + // Submit event FuriPubSub* input_events = furi_record_open(RECORD_INPUT_EVENTS); furi_check(input_events); furi_pubsub_publish(input_events, &event); @@ -237,7 +280,7 @@ static void rpc_system_gui_start_virtual_display_process(const PB_Main* request, return; } - // TODO: consider refactoring + // TODO FL-3511: consider refactoring // Using display framebuffer size as an XBM buffer size is like comparing apples and oranges // Glad they both are 1024 for now size_t buffer_size = canvas_get_buffer_size(rpc_gui->gui->canvas); @@ -313,6 +356,12 @@ static void rpc_system_gui_virtual_display_frame_process(const PB_Main* request, (void)session; } +static void rpc_active_session_icon_draw_callback(Canvas* canvas, void* context) { + UNUSED(context); + furi_assert(canvas); + canvas_draw_icon(canvas, 0, 0, &I_Rpc_active_7x8); +} + void* rpc_system_gui_alloc(RpcSession* session) { furi_assert(session); @@ -320,6 +369,18 @@ void* rpc_system_gui_alloc(RpcSession* session) { rpc_gui->gui = furi_record_open(RECORD_GUI); rpc_gui->session = session; + // Active session icon + rpc_gui->rpc_session_active_viewport = view_port_alloc(); + view_port_set_width(rpc_gui->rpc_session_active_viewport, icon_get_width(&I_Rpc_active_7x8)); + view_port_draw_callback_set( + rpc_gui->rpc_session_active_viewport, rpc_active_session_icon_draw_callback, session); + if(rpc_session_get_owner(rpc_gui->session) != RpcOwnerBle) { + view_port_enabled_set(rpc_gui->rpc_session_active_viewport, true); + } else { + view_port_enabled_set(rpc_gui->rpc_session_active_viewport, false); + } + gui_add_view_port(rpc_gui->gui, rpc_gui->rpc_session_active_viewport, GuiLayerStatusBarLeft); + RpcHandler rpc_handler = { .message_handler = NULL, .decode_submessage = NULL, @@ -360,6 +421,9 @@ void rpc_system_gui_free(void* context) { rpc_gui->virtual_display_not_empty = false; } + gui_remove_view_port(rpc_gui->gui, rpc_gui->rpc_session_active_viewport); + view_port_free(rpc_gui->rpc_session_active_viewport); + if(rpc_gui->is_streaming) { rpc_gui->is_streaming = false; // Remove GUI framebuffer callback @@ -376,4 +440,4 @@ void rpc_system_gui_free(void* context) { } furi_record_close(RECORD_GUI); free(rpc_gui); -} +} \ No newline at end of file diff --git a/applications/rpc/rpc_i.h b/applications/services/rpc/rpc_i.h similarity index 76% rename from applications/rpc/rpc_i.h rename to applications/services/rpc/rpc_i.h index e512ad39768..ffca50231c7 100644 --- a/applications/rpc/rpc_i.h +++ b/applications/services/rpc/rpc_i.h @@ -1,6 +1,6 @@ #pragma once #include "rpc.h" -#include "storage/filesystem_api_defines.h" +#include #include #include #include @@ -34,8 +34,14 @@ void* rpc_system_gui_alloc(RpcSession* session); void rpc_system_gui_free(void* ctx); void* rpc_system_gpio_alloc(RpcSession* session); void rpc_system_gpio_free(void* ctx); +void* rpc_system_property_alloc(RpcSession* session); -void rpc_print_message(const PB_Main* message); -void rpc_cli_command_start_session(Cli* cli, string_t args, void* context); +void* rpc_desktop_alloc(RpcSession* session); +void rpc_desktop_free(void* ctx); + +void rpc_debug_print_message(const PB_Main* message); +void rpc_debug_print_data(const char* prefix, uint8_t* buffer, size_t size); + +void rpc_cli_command_start_session(Cli* cli, FuriString* args, void* context); PB_CommandStatus rpc_system_storage_get_error(FS_Error fs_error); diff --git a/applications/services/rpc/rpc_property.c b/applications/services/rpc/rpc_property.c new file mode 100644 index 00000000000..ad15051ebca --- /dev/null +++ b/applications/services/rpc/rpc_property.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include + +#include "rpc_i.h" + +#define TAG "RpcProperty" + +#define PROPERTY_CATEGORY_DEVICE_INFO "devinfo" +#define PROPERTY_CATEGORY_POWER_INFO "pwrinfo" +#define PROPERTY_CATEGORY_POWER_DEBUG "pwrdebug" + +typedef struct { + RpcSession* session; + PB_Main* response; + FuriString* subkey; +} RpcPropertyContext; + +static void + rpc_system_property_get_callback(const char* key, const char* value, bool last, void* context) { + furi_assert(key); + furi_assert(value); + furi_assert(context); + furi_assert(key); + furi_assert(value); + + RpcPropertyContext* ctx = context; + RpcSession* session = ctx->session; + PB_Main* response = ctx->response; + + if(!strncmp(key, furi_string_get_cstr(ctx->subkey), furi_string_size(ctx->subkey))) { + response->content.system_device_info_response.key = strdup(key); + response->content.system_device_info_response.value = strdup(value); + rpc_send_and_release(session, response); + } + + if(last) { + rpc_send_and_release_empty(session, response->command_id, PB_CommandStatus_OK); + } +} + +static void rpc_system_property_get_process(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(request->which_content == PB_Main_property_get_request_tag); + + FURI_LOG_D(TAG, "GetProperty"); + + RpcSession* session = (RpcSession*)context; + furi_assert(session); + + FuriString* topkey = furi_string_alloc(); + FuriString* subkey = furi_string_alloc_set_str(request->content.property_get_request.key); + + const size_t sep_idx = furi_string_search_char(subkey, '.'); + + if(sep_idx == FURI_STRING_FAILURE) { + furi_string_swap(topkey, subkey); + } else { + furi_string_set_n(topkey, subkey, 0, sep_idx); + furi_string_right(subkey, sep_idx + 1); + } + + PB_Main* response = malloc(sizeof(PB_Main)); + + response->command_id = request->command_id; + response->command_status = PB_CommandStatus_OK; + response->has_next = true; + response->which_content = PB_Main_property_get_response_tag; + + RpcPropertyContext property_context = { + .session = session, + .response = response, + .subkey = subkey, + }; + + if(!furi_string_cmp(topkey, PROPERTY_CATEGORY_DEVICE_INFO)) { + furi_hal_info_get(rpc_system_property_get_callback, '.', &property_context); + } else if(!furi_string_cmp(topkey, PROPERTY_CATEGORY_POWER_INFO)) { + furi_hal_power_info_get(rpc_system_property_get_callback, '.', &property_context); + } else if(!furi_string_cmp(topkey, PROPERTY_CATEGORY_POWER_DEBUG)) { + furi_hal_power_debug_get(rpc_system_property_get_callback, &property_context); + } else { + rpc_send_and_release_empty( + session, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS); + } + + furi_string_free(subkey); + furi_string_free(topkey); + + free(response); +} + +void* rpc_system_property_alloc(RpcSession* session) { + furi_assert(session); + + RpcHandler rpc_handler = { + .message_handler = NULL, + .decode_submessage = NULL, + .context = session, + }; + + rpc_handler.message_handler = rpc_system_property_get_process; + rpc_add_handler(session, PB_Main_property_get_request_tag, &rpc_handler); + return NULL; +} diff --git a/applications/rpc/rpc_storage.c b/applications/services/rpc/rpc_storage.c similarity index 84% rename from applications/rpc/rpc_storage.c rename to applications/services/rpc/rpc_storage.c index ad6191b2fd7..a934d1c31a1 100644 --- a/applications/rpc/rpc_storage.c +++ b/applications/services/rpc/rpc_storage.c @@ -1,18 +1,18 @@ -#include "flipper.pb.h" #include #include #include -#include "pb_decode.h" -#include "rpc/rpc.h" -#include "rpc_i.h" -#include "storage.pb.h" -#include "storage/filesystem_api_defines.h" -#include "storage/storage.h" -#include -#include +#include +#include +#include +#include +#include #include #include +#include +#include +#include + #define TAG "RpcStorage" #define MAX_NAME_LENGTH 255 @@ -37,6 +37,7 @@ static void rpc_system_storage_reset_state( RpcSession* session, bool send_error) { furi_assert(rpc_storage); + furi_assert(session); if(rpc_storage->state != RpcStorageStateIdle) { if(send_error) { @@ -137,6 +138,41 @@ static void rpc_system_storage_info_process(const PB_Main* request, void* contex furi_record_close(RECORD_STORAGE); } +static void rpc_system_storage_timestamp_process(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_storage_timestamp_request_tag); + + FURI_LOG_D(TAG, "Timestamp"); + + RpcStorageSystem* rpc_storage = context; + RpcSession* session = rpc_storage->session; + furi_assert(session); + + rpc_system_storage_reset_state(rpc_storage, session, true); + + PB_Main* response = malloc(sizeof(PB_Main)); + response->command_id = request->command_id; + + Storage* fs_api = furi_record_open(RECORD_STORAGE); + + const char* path = request->content.storage_timestamp_request.path; + uint32_t timestamp = 0; + FS_Error error = storage_common_timestamp(fs_api, path, ×tamp); + + response->command_status = rpc_system_storage_get_error(error); + response->which_content = PB_Main_empty_tag; + + if(error == FSE_OK) { + response->which_content = PB_Main_storage_timestamp_response_tag; + response->content.storage_timestamp_response.timestamp = timestamp; + } + + rpc_send_and_release(session, response); + free(response); + furi_record_close(RECORD_STORAGE); +} + static void rpc_system_storage_stat_process(const PB_Main* request, void* context) { furi_assert(request); furi_assert(context); @@ -165,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; @@ -177,6 +213,8 @@ static void rpc_system_storage_stat_process(const PB_Main* request, void* contex } static void rpc_system_storage_list_root(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); RpcStorageSystem* rpc_storage = context; RpcSession* session = rpc_storage->session; furi_assert(session); @@ -204,6 +242,23 @@ static void rpc_system_storage_list_root(const PB_Main* request, void* context) rpc_send_and_release(session, &response); } +static bool rpc_system_storage_list_filter( + const PB_Storage_ListRequest* request, + const FileInfo* fileinfo, + const char* name) { + bool result = false; + + do { + if(!path_contains_only_ascii(name)) break; + if(request->filter_max_size) { + if(fileinfo->size > request->filter_max_size) break; + } + result = true; + } while(false); + + return result; +} + static void rpc_system_storage_list_process(const PB_Main* request, void* context) { furi_assert(request); furi_assert(context); @@ -215,9 +270,11 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex RpcSession* session = rpc_storage->session; furi_assert(session); + const PB_Storage_ListRequest* list_request = &request->content.storage_list_request; + rpc_system_storage_reset_state(rpc_storage, session, true); - if(!strcmp(request->content.storage_list_request.path, "/")) { + if(!strcmp(list_request->path, "/")) { rpc_system_storage_list_root(request, context); return; } @@ -233,10 +290,15 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex }; PB_Storage_ListResponse* list = &response.content.storage_list_response; + bool include_md5 = list_request->include_md5; + FuriString* md5 = furi_string_alloc(); + FuriString* md5_path = furi_string_alloc(); + File* file = storage_file_alloc(fs_api); + bool finish = false; int i = 0; - if(!storage_dir_open(dir, request->content.storage_list_request.path)) { + if(!storage_dir_open(dir, list_request->path)) { response.command_status = rpc_system_storage_get_file_error(dir); response.which_content = PB_Main_empty_tag; finish = true; @@ -246,19 +308,29 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex FileInfo fileinfo; char* name = malloc(MAX_NAME_LENGTH + 1); if(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) { - if(path_contains_only_ascii(name)) { + if(rpc_system_storage_list_filter(list_request, &fileinfo, name)) { if(i == COUNT_OF(list->file)) { list->file_count = i; response.has_next = true; 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; + + if(include_md5 && !file_info_is_dir(&fileinfo)) { + furi_string_printf(md5_path, "%s/%s", list_request->path, name); //-V576 + + if(md5_string_calc_file(file, furi_string_get_cstr(md5_path), md5, NULL)) { + char* md5sum = list->file[i].md5sum; + size_t md5sum_size = sizeof(list->file[i].md5sum); + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); + } + } + ++i; } else { free(name); @@ -273,8 +345,11 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex response.has_next = false; rpc_send_and_release(session, &response); + furi_string_free(md5); + furi_string_free(md5_path); storage_dir_close(dir); storage_file_free(dir); + storage_file_free(file); furi_record_close(RECORD_STORAGE); } @@ -292,7 +367,7 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex rpc_system_storage_reset_state(rpc_storage, session, true); - /* use same message memory to send reponse */ + /* use same message memory to send response */ PB_Main* response = malloc(sizeof(PB_Main)); const char* path = request->content.storage_read_request.path; Storage* fs_api = furi_record_open(RECORD_STORAGE); @@ -391,7 +466,7 @@ static void rpc_system_storage_write_process(const PB_Main* request, void* conte request->content.storage_write_request.file.data->size) { uint8_t* buffer = request->content.storage_write_request.file.data->bytes; size_t buffer_size = request->content.storage_write_request.file.data->size; - uint16_t written_size = storage_file_write(file, buffer, buffer_size); + size_t written_size = storage_file_write(file, buffer, buffer_size); fs_operation_success = (written_size == buffer_size); } @@ -402,6 +477,10 @@ static void rpc_system_storage_write_process(const PB_Main* request, void* conte if(!fs_operation_success) { send_response = true; command_status = rpc_system_storage_get_file_error(file); + if(command_status == PB_CommandStatus_OK) { + // Report errors not handled by underlying APIs + command_status = PB_CommandStatus_ERROR_STORAGE_INTERNAL; + } } if(send_response) { @@ -411,10 +490,12 @@ static void rpc_system_storage_write_process(const PB_Main* request, void* conte } static bool rpc_system_storage_is_dir_is_empty(Storage* fs_api, const char* path) { + furi_assert(fs_api); + furi_assert(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); @@ -526,23 +607,10 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); + FuriString* md5 = furi_string_alloc(); + FS_Error file_error; - if(storage_file_open(file, filename, FSAM_READ, FSOM_OPEN_EXISTING)) { - const uint16_t size_to_read = 512; - const uint8_t hash_size = 16; - uint8_t* data = malloc(size_to_read); - uint8_t* hash = malloc(sizeof(uint8_t) * hash_size); - md5_context* md5_ctx = malloc(sizeof(md5_context)); - - md5_starts(md5_ctx); - while(true) { - uint16_t read_size = storage_file_read(file, data, size_to_read); - if(read_size == 0) break; - md5_update(md5_ctx, data, read_size); - } - md5_finish(md5_ctx, hash); - free(md5_ctx); - + if(md5_string_calc_file(file, filename, md5, &file_error)) { PB_Main response = { .command_id = request->command_id, .command_status = PB_CommandStatus_OK, @@ -552,21 +620,15 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont char* md5sum = response.content.storage_md5sum_response.md5sum; size_t md5sum_size = sizeof(response.content.storage_md5sum_response.md5sum); - (void)md5sum_size; - furi_assert(hash_size <= ((md5sum_size - 1) / 2)); - for(uint8_t i = 0; i < hash_size; i++) { - md5sum += snprintf(md5sum, md5sum_size, "%02x", hash[i]); - } + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); - free(hash); - free(data); - storage_file_close(file); rpc_send_and_release(session, &response); } else { rpc_send_and_release_empty( - session, request->command_id, rpc_system_storage_get_file_error(file)); + session, request->command_id, rpc_system_storage_get_error(file_error)); } + furi_string_free(md5); storage_file_free(file); furi_record_close(RECORD_STORAGE); @@ -605,6 +667,7 @@ static void rpc_system_storage_rename_process(const PB_Main* request, void* cont static void rpc_system_storage_backup_create_process(const PB_Main* request, void* context) { furi_assert(request); furi_assert(request->which_content == PB_Main_storage_backup_create_request_tag); + furi_assert(context); FURI_LOG_D(TAG, "BackupCreate"); @@ -626,6 +689,7 @@ static void rpc_system_storage_backup_create_process(const PB_Main* request, voi static void rpc_system_storage_backup_restore_process(const PB_Main* request, void* context) { furi_assert(request); furi_assert(request->which_content == PB_Main_storage_backup_restore_request_tag); + furi_assert(context); FURI_LOG_D(TAG, "BackupRestore"); @@ -661,6 +725,9 @@ void* rpc_system_storage_alloc(RpcSession* session) { rpc_handler.message_handler = rpc_system_storage_info_process; rpc_add_handler(session, PB_Main_storage_info_request_tag, &rpc_handler); + rpc_handler.message_handler = rpc_system_storage_timestamp_process; + rpc_add_handler(session, PB_Main_storage_timestamp_request_tag, &rpc_handler); + rpc_handler.message_handler = rpc_system_storage_stat_process; rpc_add_handler(session, PB_Main_storage_stat_request_tag, &rpc_handler); @@ -695,6 +762,7 @@ void* rpc_system_storage_alloc(RpcSession* session) { } void rpc_system_storage_free(void* context) { + furi_assert(context); RpcStorageSystem* rpc_storage = context; RpcSession* session = rpc_storage->session; furi_assert(session); diff --git a/applications/rpc/rpc_system.c b/applications/services/rpc/rpc_system.c similarity index 98% rename from applications/rpc/rpc_system.c rename to applications/services/rpc/rpc_system.c index 0538aa64dcd..77dca4a1a60 100644 --- a/applications/rpc/rpc_system.c +++ b/applications/services/rpc/rpc_system.c @@ -30,7 +30,6 @@ static void rpc_system_system_ping_process(const PB_Main* request, void* context } PB_Main response = PB_Main_init_default; - response.has_next = false; response.command_status = PB_CommandStatus_OK; response.command_id = request->command_id; response.which_content = PB_Main_system_ping_response_tag; @@ -77,6 +76,7 @@ static void rpc_system_system_device_info_callback( furi_assert(key); furi_assert(value); RpcSystemContext* ctx = context; + furi_assert(ctx); furi_assert(key); furi_assert(value); @@ -108,7 +108,7 @@ static void rpc_system_system_device_info_process(const PB_Main* request, void* .session = session, .response = response, }; - furi_hal_info_get(rpc_system_system_device_info_callback, &device_info_context); + furi_hal_info_get(rpc_system_system_device_info_callback, '_', &device_info_context); free(response); } @@ -233,6 +233,7 @@ static void rpc_system_system_power_info_callback( furi_assert(key); furi_assert(value); RpcSystemContext* ctx = context; + furi_assert(ctx); furi_assert(key); furi_assert(value); @@ -264,7 +265,7 @@ static void rpc_system_system_get_power_info_process(const PB_Main* request, voi .session = session, .response = response, }; - furi_hal_power_info_get(rpc_system_system_power_info_callback, &power_info_context); + furi_hal_power_info_get(rpc_system_system_power_info_callback, '_', &power_info_context); free(response); } @@ -297,6 +298,8 @@ static void rpc_system_system_update_request_process(const PB_Main* request, voi #endif void* rpc_system_system_alloc(RpcSession* session) { + furi_assert(session); + RpcHandler rpc_handler = { .message_handler = NULL, .decode_submessage = NULL, diff --git a/applications/services/storage/application.fam b/applications/services/storage/application.fam new file mode 100644 index 00000000000..7aa721cc368 --- /dev/null +++ b/applications/services/storage/application.fam @@ -0,0 +1,20 @@ +App( + appid="storage", + name="StorageSrv", + apptype=FlipperAppType.SERVICE, + entry_point="storage_srv", + cdefines=["SRV_STORAGE"], + requires=["storage_settings"], + provides=["storage_start"], + stack_size=3 * 1024, + order=120, + sdk_headers=["storage.h"], +) + +App( + appid="storage_start", + apptype=FlipperAppType.STARTUP, + entry_point="storage_on_system_start", + requires=["storage"], + order=90, +) diff --git a/applications/storage/filesystem_api.c b/applications/services/storage/filesystem_api.c similarity index 90% rename from applications/storage/filesystem_api.c rename to applications/services/storage/filesystem_api.c index b979967acb5..30b20ede4d7 100644 --- a/applications/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/storage/filesystem_api_defines.h b/applications/services/storage/filesystem_api_defines.h similarity index 80% rename from applications/storage/filesystem_api_defines.h rename to applications/services/storage/filesystem_api_defines.h index b6f1d8f1306..cd24b88253f 100644 --- a/applications/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" { @@ -25,13 +26,13 @@ typedef enum { typedef enum { FSE_OK, /**< No error */ FSE_NOT_READY, /**< FS not ready */ - FSE_EXIST, /**< File/Dir alrady exist */ + FSE_EXIST, /**< File/Dir already exist */ FSE_NOT_EXIST, /**< File/Dir does not exist */ FSE_INVALID_PARAMETER, /**< Invalid API parameter */ FSE_DENIED, /**< Access denied */ FSE_INVALID_NAME, /**< Invalid name/path */ FSE_INTERNAL, /**< Internal error */ - FSE_NOT_IMPLEMENTED, /**< Functon not implemented */ + FSE_NOT_IMPLEMENTED, /**< Function not implemented */ FSE_ALREADY_OPEN, /**< File/Dir already opened */ } FS_Error; @@ -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/storage/filesystem_api_internal.h b/applications/services/storage/filesystem_api_internal.h similarity index 92% rename from applications/storage/filesystem_api_internal.h rename to applications/services/storage/filesystem_api_internal.h index bd4bcf823a6..ba98b380e94 100644 --- a/applications/storage/filesystem_api_internal.h +++ b/applications/services/storage/filesystem_api_internal.h @@ -17,7 +17,7 @@ typedef enum { struct File { uint32_t file_id; /**< File ID for internal references */ FileType type; - FS_Error error_id; /**< Standart API error from FS_Error enum */ + FS_Error error_id; /**< Standard API error from FS_Error enum */ int32_t internal_error_id; /**< Internal API error value */ void* storage; }; @@ -165,6 +165,13 @@ typedef struct { * @param total_space pointer to total space value * @param free_space pointer to free space value * @return FS_Error error info + * + * @var FS_Common_Api::equivalent_path + * @brief Test whether two paths are equivalent (e.g differing case on a case-insensitive fs) + * @param path1 first path to be compared + * @param path2 second path to be compared + * @param truncate if set to true, compare only up to the path1's length + * @return true if path1 and path2 are considered equivalent */ typedef struct { FS_Error (*const stat)(void* context, const char* path, FileInfo* fileinfo); @@ -175,6 +182,7 @@ typedef struct { const char* fs_path, uint64_t* total_space, uint64_t* free_space); + bool (*const equivalent_path)(const char* path1, const char* path2); } FS_Common_Api; /** Full filesystem api structure */ diff --git a/applications/storage/storage.c b/applications/services/storage/storage.c similarity index 97% rename from applications/storage/storage.c rename to applications/services/storage/storage.c index 9079a95eda3..a6229af8191 100644 --- a/applications/storage/storage.c +++ b/applications/services/storage/storage.c @@ -5,13 +5,14 @@ #include "storage/storage_glue.h" #include "storages/storage_int.h" #include "storages/storage_ext.h" +#include #define STORAGE_TICK 1000 #define ICON_SD_MOUNTED &I_SDcardMounted_11x8 #define ICON_SD_ERROR &I_SDcardFail_11x8 -#define TAG RECORD_STORAGE +#define TAG "Storage" static void storage_app_sd_icon_draw_callback(Canvas* canvas, void* context) { furi_assert(canvas); @@ -38,6 +39,7 @@ Storage* storage_app_alloc() { for(uint8_t i = 0; i < STORAGE_COUNT; i++) { storage_data_init(&app->storage[i]); + storage_data_timestamp(&app->storage[i]); } #ifndef FURI_RAM_EXEC diff --git a/applications/services/storage/storage.h b/applications/services/storage/storage.h new file mode 100644 index 00000000000..20a371fc081 --- /dev/null +++ b/applications/services/storage/storage.h @@ -0,0 +1,610 @@ +/** + * @file storage.h + * @brief APIs for working with storages, directories and files. + */ +#pragma once + +#include +#include "filesystem_api_defines.h" +#include "storage_sd_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define STORAGE_INT_PATH_PREFIX "/int" +#define STORAGE_EXT_PATH_PREFIX "/ext" +#define STORAGE_ANY_PATH_PREFIX "/any" +#define STORAGE_APP_DATA_PATH_PREFIX "/data" +#define STORAGE_APP_ASSETS_PATH_PREFIX "/assets" + +#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 APP_ASSETS_PATH(path) STORAGE_APP_ASSETS_PATH_PREFIX "/" path + +#define RECORD_STORAGE "storage" + +typedef struct Storage Storage; + +/** + * @brief Allocate and initialize a file instance. + * + * @param storage pointer to a storage API instance. + * @return pointer to the created instance. + */ +File* storage_file_alloc(Storage* storage); + +/** + * @brief Free the file instance. + * + * If the file was open, calling this function will close it automatically. + * @param file pointer to the file instance to be freed. + */ +void storage_file_free(File* file); + +/** + * @brief Enumeration of events emitted by the storage through the PubSub system. + */ +typedef enum { + StorageEventTypeCardMount, /**< SD card was mounted. */ + StorageEventTypeCardUnmount, /**< SD card was unmounted. */ + StorageEventTypeCardMountError, /**< An error occurred during mounting of an SD card. */ + StorageEventTypeFileClose, /**< A file was closed. */ + StorageEventTypeDirClose, /**< A directory was closed. */ +} StorageEventType; + +/** + * @brief Storage event (passed to the PubSub callback). + */ +typedef struct { + StorageEventType type; /**< Type of the event. */ +} StorageEvent; + +/** + * @brief Get the storage pubsub instance. + * + * Storage will send StorageEvent messages. + * + * @param storage pointer to a storage API instance. + * @return pointer to the pubsub instance. + */ +FuriPubSub* storage_get_pubsub(Storage* storage); + +/******************* File Functions *******************/ + +/** + * @brief Open an existing file or create a new one. + * + * @warning The calling code MUST call storage_file_close() even if the open operation had failed. + * + * @param file pointer to the file instance to be opened. + * @param path pointer to a zero-terminated string containing the path to the file to be opened. + * @param access_mode access mode from FS_AccessMode. + * @param open_mode open mode from FS_OpenMode + * @return true if the file was successfully opened, false otherwise. + */ +bool storage_file_open( + File* file, + const char* path, + FS_AccessMode access_mode, + FS_OpenMode open_mode); + +/** + * @brief Close the file. + * + * @param file pointer to the file instance to be closed. + * @return true if the file was successfully closed, false otherwise. + */ +bool storage_file_close(File* file); + +/** + * @brief Check whether the file is open. + * + * @param file pointer to the file instance in question. + * @return true if the file is open, false otherwise. + */ +bool storage_file_is_open(File* file); + +/** + * @brief Check whether a file instance represents a directory. + * + * @param file pointer to the file instance in question. + * @return true if the file instance represents a directory, false otherwise. + */ +bool storage_file_is_dir(File* file); + +/** + * @brief Read bytes from a file into a buffer. + * + * @param file pointer to the file instance to read from. + * @param buff pointer to the buffer to be filled with read data. + * @param bytes_to_read number of bytes to read. Must be less than or equal to the size of the buffer. + * @return actual number of bytes read (may be fewer than requested). + */ +size_t storage_file_read(File* file, void* buff, size_t bytes_to_read); + +/** + * @brief Write bytes from a buffer to a file. + * + * @param file pointer to the file instance to write into. + * @param buff pointer to the buffer containing the data to be written. + * @param bytes_to_write number of bytes to write. Must be less than or equal to the size of the buffer. + * @return actual number of bytes written (may be fewer than requested). + */ +size_t storage_file_write(File* file, const void* buff, size_t bytes_to_write); + +/** + * @brief Change the current access position in a file. + * + * @param file pointer to the file instance in question. + * @param offset access position offset (meaning depends on from_start parameter). + * @param from_start if true, set the access position relative to the file start, otherwise relative to the current position. + * @return success flag + */ +bool storage_file_seek(File* file, uint32_t offset, bool from_start); + +/** + * @brief Get the current access position. + * + * @param file pointer to the file instance in question. + * @return current access position. + */ +uint64_t storage_file_tell(File* file); + +/** + * @brief Truncate the file size to the current access position. + * + * @param file pointer to the file instance to be truncated. + * @return true if the file was successfully truncated, false otherwise. + */ +bool storage_file_truncate(File* file); + +/** + * @brief Get the file size. + * + * @param file pointer to the file instance in question. + * @return size of the file, in bytes. + */ +uint64_t storage_file_size(File* file); + +/** + * @brief Synchronise the file cache with the actual storage. + * + * @param file pointer to the file instance in question. + * @return true if the file was successfully synchronised, false otherwise. + */ +bool storage_file_sync(File* file); + +/** + * @brief Check whether the current access position is at the end of the file. + * + * @param file pointer to a file instance in question. + * @return bool true if the current access position is at the end of the file, false otherwise. + */ +bool storage_file_eof(File* file); + +/** + * @brief Check whether a file exists. + * + * @param storage pointer to a storage API instance. + * @param path pointer to a zero-terminated string containing the path to the file in question. + * @return true if the file exists, false otherwise. + */ +bool storage_file_exists(Storage* storage, const char* path); + +/** + * @brief Copy data from a source file to the destination file. + * + * Both files must be opened prior to calling this function. + * + * The requested amount of bytes will be copied from the current access position + * in the source file to the current access position in the destination file. + * + * @param source pointer to a source file instance. + * @param destination pointer to a destination file instance. + * @param size data size to be copied, in bytes. + * @return true if the data was successfully copied, false otherwise. + */ +bool storage_file_copy_to_file(File* source, File* destination, size_t size); + +/******************* Directory Functions *******************/ + +/** + * @brief Open a directory. + * + * Opening a directory is necessary to be able to read its contents with storage_dir_read(). + * + * @warning The calling code MUST call storage_dir_close() even if the open operation had failed. + * + * @param file pointer to a file instance representing the directory in question. + * @param path pointer to a zero-terminated string containing the path of the directory in question. + * @return true if the directory was successfully opened, false otherwise. + */ +bool storage_dir_open(File* file, const char* path); + +/** + * @brief Close the directory. + * + * @param file pointer to a file instance representing the directory in question. + * @return true if the directory was successfully closed, false otherwise. + */ +bool storage_dir_close(File* file); + +/** + * @brief Get the next item in the directory. + * + * If the next object does not exist, this function returns false as well + * and sets the file error id to FSE_NOT_EXIST. + * + * @param file pointer to a file instance representing the directory in question. + * @param fileinfo pointer to the FileInfo structure to contain the info (may be NULL). + * @param name pointer to the buffer to contain the name (may be NULL). + * @param name_length maximum capacity of the name buffer, in bytes. + * @return true if the next item was successfully read, false otherwise. + */ +bool storage_dir_read(File* file, FileInfo* fileinfo, char* name, uint16_t name_length); + +/** + * @brief Change the access position to first item in the directory. + * + * @param file pointer to a file instance representing the directory in question. + * @return true if the access position was successfully changed, false otherwise. + */ +bool storage_dir_rewind(File* file); + +/** + * @brief Check whether a directory exists. + * + * @param storage pointer to a storage API instance. + * @param path pointer to a zero-terminated string containing the path of the directory in question. + * @return true if the directory exists, false otherwise. + */ +bool storage_dir_exists(Storage* storage, const char* path); + +/******************* Common Functions *******************/ + +/** + * @brief Get the last access time in UNIX format. + * + * @param storage pointer to a storage API instance. + * @param path pointer to a zero-terminated string containing the path of the item in question. + * @param timestamp pointer to a value to contain the timestamp. + * @return FSE_OK if the timestamp has been successfully received, any other error code on failure. + */ +FS_Error storage_common_timestamp(Storage* storage, const char* path, uint32_t* timestamp); + +/** + * @brief Get information about a file or a directory. + * + * @param storage pointer to a storage API instance. + * @param path pointer to a zero-terminated string containing the path of the item in question. + * @param fileinfo pointer to the FileInfo structure to contain the info (may be NULL). + * @return FSE_OK if the info has been successfully received, any other error code on failure. + */ +FS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* fileinfo); + +/** + * @brief Remove a file or a directory. + * + * The directory must be empty. + * The file or the directory must NOT be open. + * + * @param storage pointer to a storage API instance. + * @param path pointer to a zero-terminated string containing the path of the item to be removed. + * @return FSE_OK if the file or directory has been successfully removed, any other error code on failure. + */ +FS_Error storage_common_remove(Storage* storage, const char* path); + +/** + * @brief Rename a file or a directory. + * + * The file or the directory must NOT be open. + * Will overwrite the destination file if it already exists. + * + * Renaming a regular file to itself does nothing and always succeeds. + * Renaming a directory to itself or to a subdirectory of itself always fails. + * + * @param storage pointer to a storage API instance. + * @param old_path pointer to a zero-terminated string containing the source path. + * @param new_path pointer to a zero-terminated string containing the destination path. + * @return FSE_OK if the file or directory has been successfully renamed, any other error code on failure. + */ +FS_Error storage_common_rename(Storage* storage, const char* old_path, const char* new_path); + +/** + * @brief Copy the file to a new location. + * + * The file must NOT be open at the time of calling this function. + * + * @param storage pointer to a storage API instance. + * @param old_path pointer to a zero-terminated string containing the source path. + * @param new_path pointer to a zero-terminated string containing the destination path. + * @return FSE_OK if the file has been successfully copied, any other error code on failure. + */ +FS_Error storage_common_copy(Storage* storage, const char* old_path, const char* new_path); + +/** + * @brief Copy the contents of one directory into another and rename all conflicting files. + * + * @param storage pointer to a storage API instance. + * @param old_path pointer to a zero-terminated string containing the source path. + * @param new_path pointer to a zero-terminated string containing the destination path. + * @return FSE_OK if the directories have been successfully merged, any other error code on failure. + */ +FS_Error storage_common_merge(Storage* storage, const char* old_path, const char* new_path); + +/** + * @brief Create a directory. + * + * @param storage pointer to a storage API instance. + * @param fs_path pointer to a zero-terminated string containing the directory path. + * @return FSE_OK if the directory has been successfully created, any other error code on failure. + */ +FS_Error storage_common_mkdir(Storage* storage, const char* path); + +/** + * @brief Get the general information about the storage. + * + * @param storage pointer to a storage API instance. + * @param fs_path pointer to a zero-terminated string containing the path to the storage question. + * @param total_space pointer to the value to contain the total capacity, in bytes. + * @param free_space pointer to the value to contain the available space, in bytes. + * @return FSE_OK if the information has been successfully received, any other error code on failure. + */ +FS_Error storage_common_fs_info( + Storage* storage, + const char* fs_path, + uint64_t* total_space, + uint64_t* free_space); + +/** + * @brief Parse aliases in a path and replace them with the real path. + * + * Necessary special directories will be created automatically if they did not exist. + * + * @param storage pointer to a storage API instance. + * @param path pointer to a zero-terminated string containing the path in question. + * @return true if the path was successfully resolved, false otherwise. + */ +void storage_common_resolve_path_and_ensure_app_directory(Storage* storage, FuriString* path); + +/** + * @brief Move the contents of source folder to destination one and rename all conflicting files. + * + * Source folder will be deleted if the migration was successful. + * + * @param storage pointer to a storage API instance. + * @param source pointer to a zero-terminated string containing the source path. + * @param dest pointer to a zero-terminated string containing the destination path. + * @return FSE_OK if the migration was successfull completed, any other error code on failure. + */ +FS_Error storage_common_migrate(Storage* storage, const char* source, const char* dest); + +/** + * @brief Check whether a file or a directory exists. + * + * @param storage pointer to a storage API instance. + * @param path pointer to a zero-terminated string containing the path in question. + * @return true if a file or a directory exists, false otherwise. + */ +bool storage_common_exists(Storage* storage, const char* path); + +/** + * @brief Check whether two paths are equivalent. + * + * This function will resolve aliases and apply filesystem-specific + * rules to determine whether the two given paths are equivalent. + * + * Examples: + * - /int/text and /ext/test -> false (Different storages), + * - /int/Test and /int/test -> false (Case-sensitive storage), + * - /ext/Test and /ext/test -> true (Case-insensitive storage). + * + * If the truncate parameter is set to true, the second path will be + * truncated to be no longer than the first one. It is useful to determine + * whether path2 is a subdirectory of path1. + * + * @param storage pointer to a storage API instance. + * @param path1 pointer to a zero-terminated string containing the first path. + * @param path2 pointer to a zero-terminated string containing the second path. + * @param truncate whether to truncate path2 to be no longer than path1. + * @return true if paths are equivalent, false otherwise. + */ +bool storage_common_equivalent_path( + Storage* storage, + const char* path1, + const char* path2, + bool truncate); + +/******************* Error Functions *******************/ + +/** + * @brief Get the textual description of a numeric error identifer. + * + * @param error_id numeric identifier of the error in question. + * @return pointer to a statically allocated zero-terminated string containing the respective error text. + */ +const char* storage_error_get_desc(FS_Error error_id); + +/** + * @brief Get the numeric error identifier from a file instance. + * + * @warning It is not possible to get the error identifier after the file has been closed. + * + * @param file pointer to the file instance in question (must NOT be NULL). + * @return numeric identifier of the last error associated with the file instance. + */ +FS_Error storage_file_get_error(File* file); + +/** + * @brief Get the internal (storage-specific) numeric error identifier from a file instance. + * + * @warning It is not possible to get the internal error identifier after the file has been closed. + * + * @param file pointer to the file instance in question (must NOT be NULL). + * @return numeric identifier of the last internal error associated with the file instance. + */ +int32_t storage_file_get_internal_error(File* file); + +/** + * @brief Get the textual description of a the last error associated with a file instance. + * + * @warning It is not possible to get the error text after the file has been closed. + * + * @param file pointer to the file instance in question (must NOT be NULL). + * @return pointer to a statically allocated zero-terminated string containing the respective error text. + */ +const char* storage_file_get_error_desc(File* file); + +/******************* SD Card Functions *******************/ + +/** + * @brief Format the SD Card. + * + * @param storage pointer to a storage API instance. + * @return FSE_OK if the card was successfully formatted, any other error code on failure. + */ +FS_Error storage_sd_format(Storage* storage); + +/** + * @brief Unmount the SD card. + * + * These return values have special meaning: + * - FSE_NOT_READY if the SD card is not mounted. + * - FSE_DENIED if there are open files on the SD card. + * + * @param storage pointer to a storage API instance. + * @return FSE_OK if the card was successfully formatted, any other error code on failure. + */ +FS_Error storage_sd_unmount(Storage* storage); + +/** + * @brief Mount the SD card. + * + * @param storage pointer to a storage API instance. + * @return FSE_OK if the card was successfully mounted, any other error code on failure. + */ +FS_Error storage_sd_mount(Storage* storage); + +/** + * @brief Get SD card information. + * + * @param storage pointer to a storage API instance. + * @param info pointer to the info object to contain the requested information. + * @return FSE_OK if the info was successfully received, any other error code on failure. + */ +FS_Error storage_sd_info(Storage* storage, SDInfo* info); + +/** + * @brief Get SD card status. + * + * @param storage pointer to a storage API instance. + * @return storage status in the form of a numeric error identifier. + */ +FS_Error storage_sd_status(Storage* storage); + +/******************* Internal LFS Functions *******************/ + +typedef void (*Storage_name_converter)(FuriString*); + +/** + * @brief Back up the internal storage contents to a *.tar archive. + * + * @param storage pointer to a storage API instance. + * @param dstname pointer to a zero-terminated string containing the archive file path. + * @return FSE_OK if the storage was successfully backed up, any other error code on failure. + */ +FS_Error storage_int_backup(Storage* storage, const char* dstname); + +/** + * @brief Restore the internal storage contents from a *.tar archive. + * + * @param storage pointer to a storage API instance. + * @param dstname pointer to a zero-terminated string containing the archive file path. + * @param converter pointer to a filename conversion function (may be NULL). + * @return FSE_OK if the storage was successfully restored, any other error code on failure. + */ +FS_Error storage_int_restore(Storage* api, const char* dstname, Storage_name_converter converter); + +/***************** Simplified Functions ******************/ + +/** + * @brief Remove a file or a directory. + * + * The following conditions must be met: + * - the directory must be empty. + * - the file or the directory must NOT be open. + * + * @param storage pointer to a storage API instance. + * @param path pointer to a zero-terminated string containing the item path. + * @return true on success or if the item does not exist, false otherwise. + */ +bool storage_simply_remove(Storage* storage, const char* path); + +/** + * @brief Recursively remove a file or a directory. + * + * Unlike storage_simply_remove(), the directory does not need to be empty. + * + * @param storage pointer to a storage API instance. + * @param path pointer to a zero-terminated string containing the item path. + * @return true on success or if the item does not exist, false otherwise. + */ +bool storage_simply_remove_recursive(Storage* storage, const char* path); + +/** + * @brief Create a directory. + * + * @param storage pointer to a storage API instance. + * @param path pointer to a zero-terminated string containing the directory path. + * @return true on success or if directory does already exist, false otherwise. + */ +bool storage_simply_mkdir(Storage* storage, const char* path); + +/** + * @brief Get the next free filename in a directory. + * + * Usage example: + * ```c + * FuriString* file_name = furi_string_alloc(); + * Storage* storage = furi_record_open(RECORD_STORAGE); + * + * storage_get_next_filename(storage, + * "/ext/test", + * "cookies", + * ".yum", + * 20); + * + * furi_record_close(RECORD_STORAGE); + * + * use_file_name(file_name); + * + * furi_string_free(file_name); + * ``` + * Possible file_name values after calling storage_get_next_filename(): + * "cookies", "cookies1", "cookies2", ... etc depending on whether any of + * these files have already existed in the directory. + * + * @note If the resulting next file name length is greater than set by the max_len + * parameter, the original filename will be returned instead. + * + * @param storage pointer to a storage API instance. + * @param dirname pointer to a zero-terminated string containing the directory path. + * @param filename pointer to a zero-terminated string containing the file name. + * @param fileextension pointer to a zero-terminated string containing the file extension. + * @param nextfilename pointer to a dynamic string containing the resulting file name. + * @param max_len maximum length of the new name. + */ +void storage_get_next_filename( + Storage* storage, + const char* dirname, + const char* filename, + const char* fileextension, + FuriString* nextfilename, + uint8_t max_len); + +#ifdef __cplusplus +} +#endif diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c new file mode 100644 index 00000000000..2927022a3f4 --- /dev/null +++ b/applications/services/storage/storage_cli.c @@ -0,0 +1,623 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_NAME_LENGTH 255 + +static void storage_cli_print_usage() { + printf("Usage:\r\n"); + printf("storage \r\n"); + printf("The path must start with /int or /ext\r\n"); + printf("Cmd list:\r\n"); + printf("\tinfo\t - get FS info\r\n"); + printf("\tformat\t - format filesystem\r\n"); + printf("\tlist\t - list files and dirs\r\n"); + printf("\ttree\t - list files and dirs, recursive\r\n"); + printf("\tremove\t - delete the file or directory\r\n"); + printf("\tread\t - read text from file and print file size and content to cli\r\n"); + printf( + "\tread_chunks\t - read data from file and print file size and content to cli, should contain how many bytes you want to read in block\r\n"); + printf("\twrite\t - read text from cli and append it to file, stops by ctrl+c\r\n"); + printf( + "\twrite_chunk\t - read data from cli and append it to file, should contain how many bytes you want to write\r\n"); + printf("\tcopy\t - copy file to new file, must contain new path\r\n"); + printf("\trename\t - move file to new file, must contain new path\r\n"); + printf("\tmkdir\t - creates a new directory\r\n"); + printf("\tmd5\t - md5 hash of the file\r\n"); + printf("\tstat\t - info about file or dir\r\n"); + printf("\ttimestamp\t - last modification timestamp\r\n"); +}; + +static void storage_cli_print_error(FS_Error error) { + printf("Storage error: %s\r\n", storage_error_get_desc(error)); +} + +static void storage_cli_info(Cli* cli, FuriString* path) { + UNUSED(cli); + Storage* api = furi_record_open(RECORD_STORAGE); + + if(furi_string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0) { + uint64_t total_space; + uint64_t free_space; + FS_Error error = + storage_common_fs_info(api, STORAGE_INT_PATH_PREFIX, &total_space, &free_space); + + if(error != FSE_OK) { + storage_cli_print_error(error); + } else { + printf( + "Label: %s\r\nType: LittleFS\r\n%luKiB total\r\n%luKiB free\r\n", + furi_hal_version_get_name_ptr() ? furi_hal_version_get_name_ptr() : "Unknown", + (uint32_t)(total_space / 1024), + (uint32_t)(free_space / 1024)); + } + } else if(furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) { + SDInfo sd_info; + FS_Error error = storage_sd_info(api, &sd_info); + + 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%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_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(); + } + + furi_record_close(RECORD_STORAGE); +}; + +static void storage_cli_format(Cli* cli, FuriString* path) { + if(furi_string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0) { + storage_cli_print_error(FSE_NOT_IMPLEMENTED); + } else if(furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) { + printf("Formatting SD card, All data will be lost! Are you sure (y/n)?\r\n"); + char answer = cli_getc(cli); + if(answer == 'y' || answer == 'Y') { + Storage* api = furi_record_open(RECORD_STORAGE); + printf("Formatting, please wait...\r\n"); + + FS_Error error = storage_sd_format(api); + + if(error != FSE_OK) { + storage_cli_print_error(error); + } else { + printf("SD card was successfully formatted.\r\n"); + } + furi_record_close(RECORD_STORAGE); + } else { + printf("Cancelled.\r\n"); + } + } else { + storage_cli_print_usage(); + } +}; + +static void storage_cli_list(Cli* cli, FuriString* path) { + UNUSED(cli); + if(furi_string_cmp_str(path, "/") == 0) { + printf("\t[D] int\r\n"); + printf("\t[D] ext\r\n"); + printf("\t[D] any\r\n"); + } else { + Storage* api = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(api); + + if(storage_dir_open(file, furi_string_get_cstr(path))) { + FileInfo fileinfo; + char name[MAX_NAME_LENGTH]; + bool read_done = false; + + while(storage_dir_read(file, &fileinfo, name, MAX_NAME_LENGTH)) { + read_done = true; + 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)); + } + } + + if(!read_done) { + printf("\tEmpty\r\n"); + } + } else { + storage_cli_print_error(storage_file_get_error(file)); + } + + storage_dir_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + } +} + +static void storage_cli_tree(Cli* cli, FuriString* path) { + if(furi_string_cmp_str(path, "/") == 0) { + furi_string_set(path, STORAGE_INT_PATH_PREFIX); + storage_cli_tree(cli, path); + furi_string_set(path, STORAGE_EXT_PATH_PREFIX); + storage_cli_tree(cli, path); + } else { + Storage* api = furi_record_open(RECORD_STORAGE); + DirWalk* dir_walk = dir_walk_alloc(api); + FuriString* name; + name = furi_string_alloc(); + + if(dir_walk_open(dir_walk, furi_string_get_cstr(path))) { + FileInfo fileinfo; + bool read_done = false; + + while(dir_walk_read(dir_walk, name, &fileinfo) == DirWalkOK) { + read_done = true; + if(file_info_is_dir(&fileinfo)) { + printf("\t[D] %s\r\n", furi_string_get_cstr(name)); + } else { + printf( + "\t[F] %s %lub\r\n", + furi_string_get_cstr(name), + (uint32_t)(fileinfo.size)); + } + } + + if(!read_done) { + printf("\tEmpty\r\n"); + } + } else { + storage_cli_print_error(dir_walk_get_error(dir_walk)); + } + + furi_string_free(name); + dir_walk_free(dir_walk); + furi_record_close(RECORD_STORAGE); + } +} + +static void storage_cli_read(Cli* cli, FuriString* path) { + UNUSED(cli); + Storage* api = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(api); + + if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { + const size_t buffer_size = 128; + size_t read_size = 0; + uint8_t* data = malloc(buffer_size); + + printf("Size: %lu\r\n", (uint32_t)storage_file_size(file)); + + do { + read_size = storage_file_read(file, data, buffer_size); + for(size_t i = 0; i < read_size; i++) { + printf("%c", data[i]); + } + } while(read_size > 0); + printf("\r\n"); + + free(data); + } else { + storage_cli_print_error(storage_file_get_error(file)); + } + + storage_file_close(file); + storage_file_free(file); + + furi_record_close(RECORD_STORAGE); +} + +static void storage_cli_write(Cli* cli, FuriString* path) { + Storage* api = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(api); + + const size_t buffer_size = 512; + uint8_t* buffer = malloc(buffer_size); + + if(storage_file_open(file, furi_string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_APPEND)) { + printf("Just write your text data. New line by Ctrl+Enter, exit by Ctrl+C.\r\n"); + + uint32_t read_index = 0; + + while(true) { + uint8_t symbol = cli_getc(cli); + + if(symbol == CliSymbolAsciiETX) { + size_t write_size = read_index % buffer_size; + + if(write_size > 0) { + size_t written_size = storage_file_write(file, buffer, write_size); + + if(written_size != write_size) { + storage_cli_print_error(storage_file_get_error(file)); + } + break; + } + } + + buffer[read_index % buffer_size] = symbol; + printf("%c", buffer[read_index % buffer_size]); + fflush(stdout); + read_index++; + + if(((read_index % buffer_size) == 0)) { + size_t written_size = storage_file_write(file, buffer, buffer_size); + + if(written_size != buffer_size) { + storage_cli_print_error(storage_file_get_error(file)); + break; + } + } + } + printf("\r\n"); + + } else { + storage_cli_print_error(storage_file_get_error(file)); + } + storage_file_close(file); + + free(buffer); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +} + +static void storage_cli_read_chunks(Cli* cli, FuriString* path, FuriString* args) { + Storage* api = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(api); + + uint32_t buffer_size; + int parsed_count = sscanf(furi_string_get_cstr(args), "%lu", &buffer_size); + + if(parsed_count != 1) { + storage_cli_print_usage(); + } else if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { + uint64_t file_size = storage_file_size(file); + + printf("Size: %llu\r\n", file_size); + + if(buffer_size) { + uint8_t* data = malloc(buffer_size); + while(file_size > 0) { + printf("\r\nReady?\r\n"); + cli_getc(cli); + + size_t read_size = storage_file_read(file, data, buffer_size); + for(size_t i = 0; i < read_size; i++) { + putchar(data[i]); + } + file_size -= read_size; + } + free(data); + } + printf("\r\n"); + + } else { + storage_cli_print_error(storage_file_get_error(file)); + } + + storage_file_close(file); + storage_file_free(file); + + furi_record_close(RECORD_STORAGE); +} + +static void storage_cli_write_chunk(Cli* cli, FuriString* path, FuriString* args) { + Storage* api = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(api); + + uint32_t buffer_size; + int parsed_count = sscanf(furi_string_get_cstr(args), "%lu", &buffer_size); + + if(parsed_count != 1) { + storage_cli_print_usage(); + } else { + if(storage_file_open(file, furi_string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_APPEND)) { + printf("Ready\r\n"); + + if(buffer_size) { + uint8_t* buffer = malloc(buffer_size); + + size_t read_bytes = cli_read(cli, buffer, buffer_size); + + size_t written_size = storage_file_write(file, buffer, read_bytes); + + if(written_size != buffer_size) { + storage_cli_print_error(storage_file_get_error(file)); + } + + free(buffer); + } + } else { + storage_cli_print_error(storage_file_get_error(file)); + } + storage_file_close(file); + } + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +} + +static void storage_cli_stat(Cli* cli, FuriString* path) { + UNUSED(cli); + Storage* api = furi_record_open(RECORD_STORAGE); + + if(furi_string_cmp_str(path, "/") == 0) { + printf("Storage\r\n"); + } else if( + furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0 || + furi_string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0 || + furi_string_cmp_str(path, STORAGE_ANY_PATH_PREFIX) == 0) { + uint64_t total_space; + uint64_t free_space; + FS_Error error = + storage_common_fs_info(api, furi_string_get_cstr(path), &total_space, &free_space); + + if(error != FSE_OK) { + storage_cli_print_error(error); + } else { + printf( + "Storage, %luKiB total, %luKiB free\r\n", + (uint32_t)(total_space / 1024), + (uint32_t)(free_space / 1024)); + } + } else { + FileInfo fileinfo; + FS_Error error = storage_common_stat(api, furi_string_get_cstr(path), &fileinfo); + + if(error == FSE_OK) { + if(file_info_is_dir(&fileinfo)) { + printf("Directory\r\n"); + } else { + printf("File, size: %lub\r\n", (uint32_t)(fileinfo.size)); + } + } else { + storage_cli_print_error(error); + } + } + + furi_record_close(RECORD_STORAGE); +} + +static void storage_cli_timestamp(Cli* cli, FuriString* path) { + UNUSED(cli); + Storage* api = furi_record_open(RECORD_STORAGE); + + uint32_t timestamp = 0; + FS_Error error = storage_common_timestamp(api, furi_string_get_cstr(path), ×tamp); + + if(error != FSE_OK) { + printf("Invalid arguments\r\n"); + } else { + printf("Timestamp %lu\r\n", timestamp); + } + + furi_record_close(RECORD_STORAGE); +} + +static void storage_cli_copy(Cli* cli, FuriString* old_path, FuriString* args) { + UNUSED(cli); + Storage* api = furi_record_open(RECORD_STORAGE); + FuriString* new_path; + new_path = furi_string_alloc(); + + if(!args_read_probably_quoted_string_and_trim(args, new_path)) { + storage_cli_print_usage(); + } else { + FS_Error error = storage_common_copy( + api, furi_string_get_cstr(old_path), furi_string_get_cstr(new_path)); + + if(error != FSE_OK) { + storage_cli_print_error(error); + } + } + + furi_string_free(new_path); + furi_record_close(RECORD_STORAGE); +} + +static void storage_cli_remove(Cli* cli, FuriString* path) { + UNUSED(cli); + Storage* api = furi_record_open(RECORD_STORAGE); + FS_Error error = storage_common_remove(api, furi_string_get_cstr(path)); + + if(error != FSE_OK) { + storage_cli_print_error(error); + } + + furi_record_close(RECORD_STORAGE); +} + +static void storage_cli_rename(Cli* cli, FuriString* old_path, FuriString* args) { + UNUSED(cli); + Storage* api = furi_record_open(RECORD_STORAGE); + FuriString* new_path; + new_path = furi_string_alloc(); + + if(!args_read_probably_quoted_string_and_trim(args, new_path)) { + storage_cli_print_usage(); + } else { + FS_Error error = storage_common_rename( + api, furi_string_get_cstr(old_path), furi_string_get_cstr(new_path)); + + if(error != FSE_OK) { + storage_cli_print_error(error); + } + } + + furi_string_free(new_path); + furi_record_close(RECORD_STORAGE); +} + +static void storage_cli_mkdir(Cli* cli, FuriString* path) { + UNUSED(cli); + Storage* api = furi_record_open(RECORD_STORAGE); + FS_Error error = storage_common_mkdir(api, furi_string_get_cstr(path)); + + if(error != FSE_OK) { + storage_cli_print_error(error); + } + + furi_record_close(RECORD_STORAGE); +} + +static void storage_cli_md5(Cli* cli, FuriString* path) { + UNUSED(cli); + Storage* api = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(api); + FuriString* md5 = furi_string_alloc(); + FS_Error file_error; + + if(md5_string_calc_file(file, furi_string_get_cstr(path), md5, &file_error)) { + printf("%s\r\n", furi_string_get_cstr(md5)); + } else { + storage_cli_print_error(file_error); + } + + furi_string_free(md5); + storage_file_close(file); + storage_file_free(file); + + furi_record_close(RECORD_STORAGE); +} + +void storage_cli(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + FuriString* cmd; + FuriString* path; + cmd = furi_string_alloc(); + path = furi_string_alloc(); + + do { + if(!args_read_string_and_trim(args, cmd)) { + storage_cli_print_usage(); + break; + } + + if(!args_read_probably_quoted_string_and_trim(args, path)) { + storage_cli_print_usage(); + break; + } + + if(furi_string_cmp_str(cmd, "info") == 0) { + storage_cli_info(cli, path); + break; + } + + if(furi_string_cmp_str(cmd, "format") == 0) { + storage_cli_format(cli, path); + break; + } + + if(furi_string_cmp_str(cmd, "list") == 0) { + storage_cli_list(cli, path); + break; + } + + if(furi_string_cmp_str(cmd, "tree") == 0) { + storage_cli_tree(cli, path); + break; + } + + if(furi_string_cmp_str(cmd, "read") == 0) { + storage_cli_read(cli, path); + break; + } + + if(furi_string_cmp_str(cmd, "read_chunks") == 0) { + storage_cli_read_chunks(cli, path, args); + break; + } + + if(furi_string_cmp_str(cmd, "write") == 0) { + storage_cli_write(cli, path); + break; + } + + if(furi_string_cmp_str(cmd, "write_chunk") == 0) { + storage_cli_write_chunk(cli, path, args); + break; + } + + if(furi_string_cmp_str(cmd, "copy") == 0) { + storage_cli_copy(cli, path, args); + break; + } + + if(furi_string_cmp_str(cmd, "remove") == 0) { + storage_cli_remove(cli, path); + break; + } + + if(furi_string_cmp_str(cmd, "rename") == 0) { + storage_cli_rename(cli, path, args); + break; + } + + if(furi_string_cmp_str(cmd, "mkdir") == 0) { + storage_cli_mkdir(cli, path); + break; + } + + if(furi_string_cmp_str(cmd, "md5") == 0) { + storage_cli_md5(cli, path); + break; + } + + if(furi_string_cmp_str(cmd, "stat") == 0) { + storage_cli_stat(cli, path); + break; + } + + if(furi_string_cmp_str(cmd, "timestamp") == 0) { + storage_cli_timestamp(cli, path); + break; + } + + storage_cli_print_usage(); + } while(false); + + furi_string_free(path); + furi_string_free(cmd); +} + +static void storage_cli_factory_reset(Cli* cli, FuriString* args, void* context) { + UNUSED(args); + UNUSED(context); + printf("All data will be lost! Are you sure (y/n)?\r\n"); + char c = cli_getc(cli); + if(c == 'y' || c == 'Y') { + printf("Data will be wiped after reboot.\r\n"); + furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); + power_reboot(PowerBootModeNormal); + } else { + printf("Safe choice.\r\n"); + } +} + +void storage_on_system_start() { +#ifdef SRV_CLI + Cli* cli = furi_record_open(RECORD_CLI); + cli_add_command(cli, RECORD_STORAGE, CliCommandFlagParallelSafe, storage_cli, NULL); + cli_add_command( + cli, "factory_reset", CliCommandFlagParallelSafe, storage_cli_factory_reset, NULL); + furi_record_close(RECORD_CLI); +#else + UNUSED(storage_cli_factory_reset); +#endif +} diff --git a/applications/services/storage/storage_external_api.c b/applications/services/storage/storage_external_api.c new file mode 100644 index 00000000000..666090346a0 --- /dev/null +++ b/applications/services/storage/storage_external_api.c @@ -0,0 +1,1004 @@ +#include +#include +#include "storage.h" +#include "storage_i.h" +#include "storage_message.h" +#include +#include +#include "toolbox/path.h" + +#define MAX_NAME_LENGTH 256 +#define MAX_EXT_LEN 16 +#define FILE_BUFFER_SIZE 512 + +#define TAG "StorageApi" + +#define S_API_PROLOGUE FuriApiLock lock = api_lock_alloc_locked(); + +#define S_FILE_API_PROLOGUE \ + Storage* storage = file->storage; \ + furi_assert(storage); + +#define S_API_EPILOGUE \ + furi_check( \ + furi_message_queue_put(storage->message_queue, &message, FuriWaitForever) == \ + FuriStatusOk); \ + api_lock_wait_unlock_and_free(lock) + +#define S_API_MESSAGE(_command) \ + SAReturn return_data; \ + StorageMessage message = { \ + .lock = lock, \ + .command = _command, \ + .data = &data, \ + .return_data = &return_data, \ + }; + +#define S_API_DATA_FILE \ + SAData data = { \ + .file = { \ + .file = file, \ + }}; + +#define S_RETURN_BOOL (return_data.bool_value); +#define S_RETURN_UINT16 (return_data.uint16_value); +#define S_RETURN_UINT64 (return_data.uint64_value); +#define S_RETURN_ERROR (return_data.error_value); +#define S_RETURN_CSTRING (return_data.cstring_value); + +typedef enum { + StorageEventFlagFileClose = (1 << 0), +} StorageEventFlag; +/****************** FILE ******************/ + +static bool storage_file_open_internal( + File* file, + const char* path, + FS_AccessMode access_mode, + FS_OpenMode open_mode) { + S_FILE_API_PROLOGUE; + S_API_PROLOGUE; + + SAData data = { + .fopen = { + .file = file, + .path = path, + .access_mode = access_mode, + .open_mode = open_mode, + .thread_id = furi_thread_get_current_id(), + }}; + + file->type = FileTypeOpenFile; + + S_API_MESSAGE(StorageCommandFileOpen); + S_API_EPILOGUE; + + return S_RETURN_BOOL; +} + +static void storage_file_close_callback(const void* message, void* context) { + const StorageEvent* storage_event = message; + + if(storage_event->type == StorageEventTypeFileClose || + storage_event->type == StorageEventTypeDirClose) { + furi_assert(context); + FuriEventFlag* event = context; + furi_event_flag_set(event, StorageEventFlagFileClose); + } +} + +bool storage_file_open( + File* file, + const char* path, + FS_AccessMode access_mode, + FS_OpenMode open_mode) { + bool result; + FuriEventFlag* event = furi_event_flag_alloc(); + FuriPubSubSubscription* subscription = furi_pubsub_subscribe( + storage_get_pubsub(file->storage), storage_file_close_callback, event); + + do { + result = storage_file_open_internal(file, path, access_mode, open_mode); + + if(!result && file->error_id == FSE_ALREADY_OPEN) { + furi_event_flag_wait( + event, StorageEventFlagFileClose, FuriFlagWaitAny, FuriWaitForever); + } else { + break; + } + } while(true); + + furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription); + furi_event_flag_free(event); + + FURI_LOG_T( + TAG, + "File %p - %p open (%s)", + (void*)((uint32_t)file - SRAM_BASE), + (void*)(file->file_id - SRAM_BASE), + path); + + return result; +} + +bool storage_file_close(File* file) { + S_FILE_API_PROLOGUE; + S_API_PROLOGUE; + + S_API_DATA_FILE; + S_API_MESSAGE(StorageCommandFileClose); + S_API_EPILOGUE; + + FURI_LOG_T( + TAG, + "File %p - %p closed", + (void*)((uint32_t)file - SRAM_BASE), + (void*)(file->file_id - SRAM_BASE)); + file->type = FileTypeClosed; + + return S_RETURN_BOOL; +} + +static uint16_t storage_file_read_underlying(File* file, void* buff, uint16_t bytes_to_read) { + if(bytes_to_read == 0) { + return 0; + } + + S_FILE_API_PROLOGUE; + S_API_PROLOGUE; + + SAData data = { + .fread = { + .file = file, + .buff = buff, + .bytes_to_read = bytes_to_read, + }}; + + S_API_MESSAGE(StorageCommandFileRead); + S_API_EPILOGUE; + return S_RETURN_UINT16; +} + +static uint16_t + storage_file_write_underlying(File* file, const void* buff, uint16_t bytes_to_write) { + if(bytes_to_write == 0) { + return 0; + } + + S_FILE_API_PROLOGUE; + S_API_PROLOGUE; + + SAData data = { + .fwrite = { + .file = file, + .buff = buff, + .bytes_to_write = bytes_to_write, + }}; + + S_API_MESSAGE(StorageCommandFileWrite); + S_API_EPILOGUE; + return S_RETURN_UINT16; +} + +size_t storage_file_read(File* file, void* buff, size_t to_read) { + size_t total = 0; + + const size_t max_chunk = UINT16_MAX; + do { + const size_t chunk = MIN((to_read - total), max_chunk); + size_t read = storage_file_read_underlying(file, buff + total, chunk); + total += read; + + if(storage_file_get_error(file) != FSE_OK || read != chunk) { + break; + } + } while(total != to_read); + + return total; +} + +size_t storage_file_write(File* file, const void* buff, size_t to_write) { + size_t total = 0; + + const size_t max_chunk = UINT16_MAX; + do { + const size_t chunk = MIN((to_write - total), max_chunk); + size_t written = storage_file_write_underlying(file, buff + total, chunk); + total += written; + + if(storage_file_get_error(file) != FSE_OK || written != chunk) { + break; + } + } while(total != to_write); + + return total; +} + +bool storage_file_seek(File* file, uint32_t offset, bool from_start) { + S_FILE_API_PROLOGUE; + S_API_PROLOGUE; + + SAData data = { + .fseek = { + .file = file, + .offset = offset, + .from_start = from_start, + }}; + + S_API_MESSAGE(StorageCommandFileSeek); + S_API_EPILOGUE; + return S_RETURN_BOOL; +} + +uint64_t storage_file_tell(File* file) { + S_FILE_API_PROLOGUE; + S_API_PROLOGUE; + S_API_DATA_FILE; + S_API_MESSAGE(StorageCommandFileTell); + S_API_EPILOGUE; + return S_RETURN_UINT64; +} + +bool storage_file_truncate(File* file) { + S_FILE_API_PROLOGUE; + S_API_PROLOGUE; + S_API_DATA_FILE; + S_API_MESSAGE(StorageCommandFileTruncate); + S_API_EPILOGUE; + return S_RETURN_BOOL; +} + +uint64_t storage_file_size(File* file) { + S_FILE_API_PROLOGUE; + S_API_PROLOGUE; + S_API_DATA_FILE; + S_API_MESSAGE(StorageCommandFileSize); + S_API_EPILOGUE; + return S_RETURN_UINT64; +} + +bool storage_file_sync(File* file) { + S_FILE_API_PROLOGUE; + S_API_PROLOGUE; + S_API_DATA_FILE; + S_API_MESSAGE(StorageCommandFileSync); + S_API_EPILOGUE; + return S_RETURN_BOOL; +} + +bool storage_file_eof(File* file) { + S_FILE_API_PROLOGUE; + S_API_PROLOGUE; + S_API_DATA_FILE; + S_API_MESSAGE(StorageCommandFileEof); + S_API_EPILOGUE; + return S_RETURN_BOOL; +} + +bool storage_file_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; +} + +bool storage_file_copy_to_file(File* source, File* destination, size_t size) { + uint8_t* buffer = malloc(FILE_BUFFER_SIZE); + + while(size) { + uint32_t read_size = size > FILE_BUFFER_SIZE ? FILE_BUFFER_SIZE : size; + if(storage_file_read(source, buffer, read_size) != read_size) { + break; + } + + if(storage_file_write(destination, buffer, read_size) != read_size) { + break; + } + + size -= read_size; + } + + free(buffer); + return size == 0; +} + +/****************** DIR ******************/ + +static bool storage_dir_open_internal(File* file, const char* path) { + S_FILE_API_PROLOGUE; + S_API_PROLOGUE; + + SAData data = { + .dopen = { + .file = file, + .path = path, + .thread_id = furi_thread_get_current_id(), + }}; + + file->type = FileTypeOpenDir; + + S_API_MESSAGE(StorageCommandDirOpen); + S_API_EPILOGUE; + return S_RETURN_BOOL; +} + +bool storage_dir_open(File* file, const char* path) { + bool result; + FuriEventFlag* event = furi_event_flag_alloc(); + FuriPubSubSubscription* subscription = furi_pubsub_subscribe( + storage_get_pubsub(file->storage), storage_file_close_callback, event); + + do { + result = storage_dir_open_internal(file, path); + + if(!result && file->error_id == FSE_ALREADY_OPEN) { + furi_event_flag_wait( + event, StorageEventFlagFileClose, FuriFlagWaitAny, FuriWaitForever); + } else { + break; + } + } while(true); + + furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription); + furi_event_flag_free(event); + + FURI_LOG_T( + TAG, + "Dir %p - %p open (%s)", + (void*)((uint32_t)file - SRAM_BASE), + (void*)(file->file_id - SRAM_BASE), + path); + + return result; +} + +bool storage_dir_close(File* file) { + S_FILE_API_PROLOGUE; + S_API_PROLOGUE; + S_API_DATA_FILE; + S_API_MESSAGE(StorageCommandDirClose); + S_API_EPILOGUE; + + FURI_LOG_T( + TAG, + "Dir %p - %p closed", + (void*)((uint32_t)file - SRAM_BASE), + (void*)(file->file_id - SRAM_BASE)); + + file->type = FileTypeClosed; + + return S_RETURN_BOOL; +} + +bool storage_dir_read(File* file, FileInfo* fileinfo, char* name, uint16_t name_length) { + S_FILE_API_PROLOGUE; + S_API_PROLOGUE; + + SAData data = { + .dread = { + .file = file, + .fileinfo = fileinfo, + .name = name, + .name_length = name_length, + }}; + + S_API_MESSAGE(StorageCommandDirRead); + S_API_EPILOGUE; + return S_RETURN_BOOL; +} + +bool storage_dir_rewind(File* file) { + S_FILE_API_PROLOGUE; + S_API_PROLOGUE; + S_API_DATA_FILE; + S_API_MESSAGE(StorageCommandDirRewind); + S_API_EPILOGUE; + 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, + .thread_id = furi_thread_get_current_id(), + }}; + + S_API_MESSAGE(StorageCommandCommonTimestamp); + S_API_EPILOGUE; + return S_RETURN_ERROR; +} + +FS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* fileinfo) { + S_API_PROLOGUE; + SAData data = { + .cstat = { + .path = path, + .fileinfo = fileinfo, + .thread_id = furi_thread_get_current_id(), + }}; + + S_API_MESSAGE(StorageCommandCommonStat); + S_API_EPILOGUE; + return S_RETURN_ERROR; +} + +FS_Error storage_common_remove(Storage* storage, const char* path) { + S_API_PROLOGUE; + SAData data = { + .path = { + .path = path, + .thread_id = furi_thread_get_current_id(), + }}; + + S_API_MESSAGE(StorageCommandCommonRemove); + S_API_EPILOGUE; + return S_RETURN_ERROR; +} + +FS_Error storage_common_rename(Storage* storage, const char* old_path, const char* new_path) { + FS_Error error; + + do { + if(!storage_common_exists(storage, old_path)) { + error = FSE_INVALID_NAME; + break; + } + + if(storage_dir_exists(storage, old_path)) { + // Cannot overwrite a file with a directory + if(storage_file_exists(storage, new_path)) { + error = FSE_INVALID_NAME; + break; + } + + // Cannot rename a directory to itself or to a nested directory + if(storage_common_equivalent_path(storage, old_path, new_path, true)) { + error = FSE_INVALID_NAME; + break; + } + + // Renaming a regular file to itself does nothing and always succeeds + } else if(storage_common_equivalent_path(storage, old_path, new_path, false)) { + error = FSE_OK; + break; + } + + if(storage_file_exists(storage, new_path)) { + storage_common_remove(storage, new_path); + } + + error = storage_common_copy(storage, old_path, new_path); + if(error != FSE_OK) { + break; + } + + if(!storage_simply_remove_recursive(storage, old_path)) { + error = FSE_INTERNAL; + } + } while(false); + + return error; +} + +static FS_Error + storage_copy_recursive(Storage* storage, const char* old_path, const char* new_path) { + FS_Error error = storage_common_mkdir(storage, new_path); + DirWalk* dir_walk = dir_walk_alloc(storage); + FuriString* path; + FuriString* tmp_new_path; + FuriString* tmp_old_path; + FileInfo fileinfo; + path = furi_string_alloc(); + tmp_new_path = furi_string_alloc(); + tmp_old_path = furi_string_alloc(); + + do { + if(error != FSE_OK) break; + + if(!dir_walk_open(dir_walk, old_path)) { + error = dir_walk_get_error(dir_walk); + break; + } + + while(1) { + DirWalkResult res = dir_walk_read(dir_walk, path, &fileinfo); + + if(res == DirWalkError) { + error = dir_walk_get_error(dir_walk); + break; + } else if(res == DirWalkLast) { + break; + } else { + furi_string_set(tmp_old_path, path); + furi_string_right(path, strlen(old_path)); + furi_string_printf(tmp_new_path, "%s%s", new_path, furi_string_get_cstr(path)); + + if(file_info_is_dir(&fileinfo)) { + error = storage_common_mkdir(storage, furi_string_get_cstr(tmp_new_path)); + } else { + error = storage_common_copy( + storage, + furi_string_get_cstr(tmp_old_path), + furi_string_get_cstr(tmp_new_path)); + } + + if(error != FSE_OK) break; + } + } + + } while(false); + + furi_string_free(tmp_new_path); + furi_string_free(tmp_old_path); + furi_string_free(path); + dir_walk_free(dir_walk); + return error; +} + +FS_Error storage_common_copy(Storage* storage, const char* old_path, const char* new_path) { + FS_Error error; + + FileInfo fileinfo; + error = storage_common_stat(storage, old_path, &fileinfo); + + if(error == FSE_OK) { + if(file_info_is_dir(&fileinfo)) { + error = storage_copy_recursive(storage, old_path, new_path); + } else { + Stream* stream_from = file_stream_alloc(storage); + Stream* stream_to = file_stream_alloc(storage); + + do { + if(!file_stream_open(stream_from, old_path, FSAM_READ, FSOM_OPEN_EXISTING)) break; + if(!file_stream_open(stream_to, new_path, FSAM_WRITE, FSOM_CREATE_NEW)) break; + stream_copy_full(stream_from, stream_to); + } while(false); + + error = file_stream_get_error(stream_from); + if(error == FSE_OK) { + error = file_stream_get_error(stream_to); + } + + stream_free(stream_from); + stream_free(stream_to); + } + } + + return error; +} + +static FS_Error + storage_merge_recursive(Storage* storage, const char* old_path, const char* new_path) { + FS_Error error = FSE_OK; + DirWalk* dir_walk = dir_walk_alloc(storage); + FuriString *path, *file_basename, *tmp_new_path; + FileInfo fileinfo; + path = furi_string_alloc(); + file_basename = furi_string_alloc(); + tmp_new_path = furi_string_alloc(); + + do { + if(!storage_simply_mkdir(storage, new_path)) break; + + dir_walk_set_recursive(dir_walk, false); + if(!dir_walk_open(dir_walk, old_path)) { + error = dir_walk_get_error(dir_walk); + break; + } + + while(1) { + DirWalkResult res = dir_walk_read(dir_walk, path, &fileinfo); + + if(res == DirWalkError) { + error = dir_walk_get_error(dir_walk); + break; + } else if(res == DirWalkLast) { + break; + } else { + path_extract_basename(furi_string_get_cstr(path), file_basename); + path_concat(new_path, furi_string_get_cstr(file_basename), tmp_new_path); + + if(file_info_is_dir(&fileinfo)) { + if(storage_common_stat( + storage, furi_string_get_cstr(tmp_new_path), &fileinfo) == FSE_OK) { + if(file_info_is_dir(&fileinfo)) { + error = + storage_common_mkdir(storage, furi_string_get_cstr(tmp_new_path)); + if(error != FSE_OK && error != FSE_EXIST) { + break; + } + } + } + } + error = storage_common_merge( + storage, furi_string_get_cstr(path), furi_string_get_cstr(tmp_new_path)); + + if(error != FSE_OK) { + break; + } + } + } + + } while(false); + + furi_string_free(tmp_new_path); + furi_string_free(file_basename); + furi_string_free(path); + dir_walk_free(dir_walk); + return error; +} + +FS_Error storage_common_merge(Storage* storage, const char* old_path, const char* new_path) { + FS_Error error; + const char* new_path_tmp = NULL; + FuriString* new_path_next = NULL; + new_path_next = furi_string_alloc(); + + FileInfo fileinfo; + error = storage_common_stat(storage, old_path, &fileinfo); + + if(error == FSE_OK) { + if(file_info_is_dir(&fileinfo)) { + error = storage_merge_recursive(storage, old_path, new_path); + } else { + error = storage_common_stat(storage, new_path, &fileinfo); + if(error == FSE_OK) { + furi_string_set(new_path_next, new_path); + FuriString* dir_path; + FuriString* filename; + char extension[MAX_EXT_LEN] = {0}; + + dir_path = furi_string_alloc(); + filename = furi_string_alloc(); + + path_extract_filename(new_path_next, filename, true); + path_extract_dirname(new_path, dir_path); + path_extract_extension(new_path_next, extension, MAX_EXT_LEN); + + storage_get_next_filename( + storage, + furi_string_get_cstr(dir_path), + furi_string_get_cstr(filename), + extension, + new_path_next, + 255); + furi_string_cat_printf( + dir_path, "/%s%s", furi_string_get_cstr(new_path_next), extension); + furi_string_set(new_path_next, dir_path); + + furi_string_free(dir_path); + furi_string_free(filename); + new_path_tmp = furi_string_get_cstr(new_path_next); + } else { + new_path_tmp = new_path; + } + Stream* stream_from = file_stream_alloc(storage); + Stream* stream_to = file_stream_alloc(storage); + + do { + if(!file_stream_open(stream_from, old_path, FSAM_READ, FSOM_OPEN_EXISTING)) break; + if(!file_stream_open(stream_to, new_path_tmp, FSAM_WRITE, FSOM_CREATE_NEW)) break; + stream_copy_full(stream_from, stream_to); + } while(false); + + error = file_stream_get_error(stream_from); + if(error == FSE_OK) { + error = file_stream_get_error(stream_to); + } + + stream_free(stream_from); + stream_free(stream_to); + } + } + + furi_string_free(new_path_next); + + return error; +} + +FS_Error storage_common_mkdir(Storage* storage, const char* path) { + S_API_PROLOGUE; + SAData data = { + .path = { + .path = path, + .thread_id = furi_thread_get_current_id(), + }}; + + S_API_MESSAGE(StorageCommandCommonMkDir); + S_API_EPILOGUE; + return S_RETURN_ERROR; +} + +FS_Error storage_common_fs_info( + Storage* storage, + const char* fs_path, + uint64_t* total_space, + uint64_t* free_space) { + S_API_PROLOGUE; + + SAData data = { + .cfsinfo = { + .fs_path = fs_path, + .total_space = total_space, + .free_space = free_space, + .thread_id = furi_thread_get_current_id(), + }}; + + S_API_MESSAGE(StorageCommandCommonFSInfo); + S_API_EPILOGUE; + 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; +} + +bool storage_common_equivalent_path( + Storage* storage, + const char* path1, + const char* path2, + bool truncate) { + S_API_PROLOGUE; + + SAData data = { + .cequivpath = { + .path1 = path1, + .path2 = path2, + .truncate = truncate, + .thread_id = furi_thread_get_current_id(), + }}; + + S_API_MESSAGE(StorageCommandCommonEquivalentPath); + S_API_EPILOGUE; + + return S_RETURN_BOOL; +} + +/****************** ERROR ******************/ + +const char* storage_error_get_desc(FS_Error error_id) { + return filesystem_api_error_get_desc(error_id); +} + +FS_Error storage_file_get_error(File* file) { + furi_check(file != NULL); + return file->error_id; +} + +int32_t storage_file_get_internal_error(File* file) { + furi_check(file != NULL); + return file->internal_error_id; +} + +const char* storage_file_get_error_desc(File* file) { + furi_check(file != NULL); + return filesystem_api_error_get_desc(file->error_id); +} + +/****************** Raw SD API ******************/ + +FS_Error storage_sd_format(Storage* storage) { + S_API_PROLOGUE; + SAData data = {}; + S_API_MESSAGE(StorageCommandSDFormat); + S_API_EPILOGUE; + return S_RETURN_ERROR; +} + +FS_Error storage_sd_unmount(Storage* storage) { + S_API_PROLOGUE; + SAData data = {}; + S_API_MESSAGE(StorageCommandSDUnmount); + S_API_EPILOGUE; + return S_RETURN_ERROR; +} + +FS_Error storage_sd_mount(Storage* storage) { + S_API_PROLOGUE; + SAData data = {}; + S_API_MESSAGE(StorageCommandSDMount); + S_API_EPILOGUE; + return S_RETURN_ERROR; +} + +FS_Error storage_sd_info(Storage* storage, SDInfo* info) { + S_API_PROLOGUE; + SAData data = { + .sdinfo = { + .info = info, + }}; + S_API_MESSAGE(StorageCommandSDInfo); + S_API_EPILOGUE; + return S_RETURN_ERROR; +} + +FS_Error storage_sd_status(Storage* storage) { + S_API_PROLOGUE; + SAData data = {}; + S_API_MESSAGE(StorageCommandSDStatus); + S_API_EPILOGUE; + return S_RETURN_ERROR; +} + +File* storage_file_alloc(Storage* storage) { + File* file = malloc(sizeof(File)); + file->type = FileTypeClosed; + file->storage = storage; + + FURI_LOG_T(TAG, "File/Dir %p alloc", (void*)((uint32_t)file - SRAM_BASE)); + + return file; +} + +bool storage_file_is_open(File* file) { + return (file->type != FileTypeClosed); +} + +bool storage_file_is_dir(File* file) { + return (file->type == FileTypeOpenDir); +} + +void storage_file_free(File* file) { + if(storage_file_is_open(file)) { + if(storage_file_is_dir(file)) { + storage_dir_close(file); + } else { + storage_file_close(file); + } + } + + FURI_LOG_T(TAG, "File/Dir %p free", (void*)((uint32_t)file - SRAM_BASE)); + free(file); +} + +FuriPubSub* storage_get_pubsub(Storage* storage) { + furi_assert(storage); + return storage->pubsub; +} + +bool storage_simply_remove_recursive(Storage* storage, const char* path) { + furi_assert(storage); + furi_assert(path); + FileInfo fileinfo; + bool result = false; + FuriString* fullname; + FuriString* cur_dir; + + if(storage_simply_remove(storage, path)) { + return true; + } + + char* name = malloc(MAX_NAME_LENGTH + 1); //-V799 + File* dir = storage_file_alloc(storage); + cur_dir = furi_string_alloc_set(path); + bool go_deeper = false; + + while(1) { + if(!storage_dir_open(dir, furi_string_get_cstr(cur_dir))) { + storage_dir_close(dir); + break; + } + + while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) { + if(file_info_is_dir(&fileinfo)) { + furi_string_cat_printf(cur_dir, "/%s", name); //-V576 + go_deeper = true; + break; + } + + fullname = furi_string_alloc_printf("%s/%s", furi_string_get_cstr(cur_dir), name); + FS_Error error = storage_common_remove(storage, furi_string_get_cstr(fullname)); + furi_check(error == FSE_OK); + furi_string_free(fullname); + } + storage_dir_close(dir); + + if(go_deeper) { + go_deeper = false; + continue; + } + + FS_Error error = storage_common_remove(storage, furi_string_get_cstr(cur_dir)); + furi_check(error == FSE_OK); + + if(furi_string_cmp(cur_dir, path)) { + size_t last_char = furi_string_search_rchar(cur_dir, '/'); + furi_assert(last_char != FURI_STRING_FAILURE); + furi_string_left(cur_dir, last_char); + } else { + result = true; + break; + } + } + + storage_file_free(dir); + furi_string_free(cur_dir); + free(name); + return result; +} //-V773 + +bool storage_simply_remove(Storage* storage, const char* path) { + FS_Error result; + result = storage_common_remove(storage, path); + return result == FSE_OK || result == FSE_NOT_EXIST; +} + +bool storage_simply_mkdir(Storage* storage, const char* path) { + FS_Error result; + result = storage_common_mkdir(storage, path); + return result == FSE_OK || result == FSE_EXIST; +} + +void storage_get_next_filename( + Storage* storage, + const char* dirname, + const char* filename, + const char* fileextension, + FuriString* nextfilename, + uint8_t max_len) { + FuriString* temp_str; + uint16_t num = 0; + + temp_str = furi_string_alloc_printf("%s/%s%s", dirname, filename, fileextension); + + while(storage_common_stat(storage, furi_string_get_cstr(temp_str), NULL) == FSE_OK) { + num++; + furi_string_printf(temp_str, "%s/%s%d%s", dirname, filename, num, fileextension); + } + if(num && (max_len > strlen(filename))) { + furi_string_printf(nextfilename, "%s%d", filename, num); + } else { + furi_string_printf(nextfilename, "%s", filename); + } + + furi_string_free(temp_str); +} diff --git a/applications/services/storage/storage_glue.c b/applications/services/storage/storage_glue.c new file mode 100644 index 00000000000..41da6c3f487 --- /dev/null +++ b/applications/services/storage/storage_glue.c @@ -0,0 +1,158 @@ +#include "storage_glue.h" +#include + +#define TAG "StorageGlue" + +/****************** storage file ******************/ + +void storage_file_init(StorageFile* obj) { + obj->file = NULL; + obj->file_data = NULL; + obj->path = furi_string_alloc(); +} + +void storage_file_init_set(StorageFile* obj, const StorageFile* src) { + obj->file = src->file; + 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->file_data = src->file_data; + furi_string_set(obj->path, src->path); +} + +void storage_file_clear(StorageFile* obj) { + furi_string_free(obj->path); +} + +/****************** storage data ******************/ + +void storage_data_init(StorageData* storage) { + storage->data = NULL; + storage->status = StorageStatusNotReady; + StorageFileList_init(storage->files); +} + +StorageStatus storage_data_status(StorageData* storage) { + return storage->status; +} + +const char* storage_data_status_text(StorageData* storage) { + const char* result = "unknown"; + switch(storage->status) { + case StorageStatusOK: + result = "ok"; + break; + case StorageStatusNotReady: + result = "not ready"; + break; + case StorageStatusNotMounted: + result = "not mounted"; + break; + case StorageStatusNoFS: + result = "no filesystem"; + break; + case StorageStatusNotAccessible: + result = "not accessible"; + break; + case StorageStatusErrorInternal: + result = "internal"; + break; + } + + return result; +} + +void storage_data_timestamp(StorageData* storage) { + storage->timestamp = furi_hal_rtc_get_timestamp(); +} + +uint32_t storage_data_get_timestamp(StorageData* storage) { + return storage->timestamp; +} + +/****************** storage glue ******************/ + +static StorageFile* storage_get_file(const File* file, StorageData* storage) { + StorageFile* storage_file_ref = NULL; + + StorageFileList_it_t it; + for(StorageFileList_it(it, storage->files); !StorageFileList_end_p(it); + StorageFileList_next(it)) { + StorageFile* storage_file = StorageFileList_ref(it); + + if(storage_file->file->file_id == file->file_id) { + storage_file_ref = storage_file; + break; + } + } + + return storage_file_ref; +} + +bool storage_has_file(const File* file, StorageData* storage) { + return storage_get_file(file, storage) != NULL; +} + +bool storage_path_already_open(FuriString* path, StorageData* storage) { + bool open = false; + + StorageFileList_it_t it; + + for(StorageFileList_it(it, storage->files); !StorageFileList_end_p(it); + StorageFileList_next(it)) { + const StorageFile* storage_file = StorageFileList_cref(it); + + if(furi_string_cmp(storage_file->path, path) == 0) { + open = true; + break; + } + } + + return open; +} + +void storage_set_storage_file_data(const File* file, void* file_data, StorageData* storage) { + StorageFile* storage_file_ref = storage_get_file(file, storage); + furi_check(storage_file_ref != NULL); + storage_file_ref->file_data = file_data; +} + +void* storage_get_storage_file_data(const File* file, StorageData* storage) { + StorageFile* storage_file_ref = storage_get_file(file, storage); + furi_check(storage_file_ref != NULL); + return storage_file_ref->file_data; +} + +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; + furi_string_set(storage_file->path, path); +} + +bool storage_pop_storage_file(File* file, StorageData* storage) { + StorageFileList_it_t it; + bool result = false; + + for(StorageFileList_it(it, storage->files); !StorageFileList_end_p(it); + StorageFileList_next(it)) { + if(StorageFileList_cref(it)->file->file_id == file->file_id) { + result = true; + break; + } + } + + if(result) { + StorageFileList_remove(storage->files, it); + } + + return result; +} + +size_t storage_open_files_count(StorageData* storage) { + size_t count = StorageFileList_size(storage->files); + return count; +} diff --git a/applications/storage/storage_glue.h b/applications/services/storage/storage_glue.h similarity index 84% rename from applications/storage/storage_glue.h rename to applications/services/storage/storage_glue.h index 7cf2e072a9e..4323296cfb3 100644 --- a/applications/storage/storage_glue.h +++ b/applications/services/storage/storage_glue.h @@ -2,7 +2,6 @@ #include #include "filesystem_api_internal.h" -#include #include #ifdef __cplusplus @@ -19,9 +18,8 @@ typedef struct { typedef struct { File* file; - StorageType type; void* file_data; - string_t path; + FuriString* path; } StorageFile; typedef enum { @@ -39,10 +37,10 @@ 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); +uint32_t storage_data_get_timestamp(StorageData* storage); LIST_DEF( StorageFileList, @@ -56,20 +54,22 @@ struct StorageData { const FS_Api* fs_api; StorageApi api; void* data; - FuriMutex* mutex; StorageStatus status; StorageFileList_t files; + uint32_t timestamp; }; bool storage_has_file(const File* file, StorageData* storage_data); -bool storage_path_already_open(string_t path, StorageFileList_t files); +bool storage_path_already_open(FuriString* path, StorageData* storage_data); 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, string_t 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); +size_t storage_open_files_count(StorageData* storage); + #ifdef __cplusplus } #endif diff --git a/applications/storage/storage_i.h b/applications/services/storage/storage_i.h similarity index 80% rename from applications/storage/storage_i.h rename to applications/services/storage/storage_i.h index 5c836ccd295..cb7f16e4709 100644 --- a/applications/storage/storage_i.h +++ b/applications/services/storage/storage_i.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include "storage_glue.h" #include "storage_sd_api.h" @@ -11,6 +12,9 @@ extern "C" { #define STORAGE_COUNT (ST_INT + 1) +#define APPS_DATA_PATH EXT_PATH("apps_data") +#define APPS_ASSETS_PATH EXT_PATH("apps_assets") + typedef struct { ViewPort* view_port; bool enabled; diff --git a/applications/storage/storage_internal_api.c b/applications/services/storage/storage_internal_api.c similarity index 97% rename from applications/storage/storage_internal_api.c rename to applications/services/storage/storage_internal_api.c index 620eae3673e..6d620b9c07b 100644 --- a/applications/storage/storage_internal_api.c +++ b/applications/services/storage/storage_internal_api.c @@ -1,5 +1,4 @@ #include -#include #include "storage.h" #include diff --git a/applications/storage/storage_message.h b/applications/services/storage/storage_message.h similarity index 75% rename from applications/storage/storage_message.h rename to applications/services/storage/storage_message.h index 78cd1e03cc5..cd45906d4ac 100644 --- a/applications/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 { @@ -42,23 +45,44 @@ typedef struct { uint16_t name_length; } SADataDRead; +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 { + const char* path1; + const char* path2; + bool truncate; + FuriThreadId thread_id; +} SADataCEquivPath; + typedef struct { uint32_t id; } SADataError; typedef struct { const char* path; + FuriThreadId thread_id; } SADataPath; typedef struct { @@ -78,8 +102,11 @@ typedef union { SADataDOpen dopen; SADataDRead dread; + SADataCTimestamp ctimestamp; SADataCStat cstat; SADataCFSInfo cfsinfo; + SADataCResolvePath cresolvepath; + SADataCEquivPath cequivpath; SADataError error; @@ -112,6 +139,7 @@ typedef enum { StorageCommandDirClose, StorageCommandDirRead, StorageCommandDirRewind, + StorageCommandCommonTimestamp, StorageCommandCommonStat, StorageCommandCommonRemove, StorageCommandCommonMkDir, @@ -120,10 +148,13 @@ typedef enum { StorageCommandSDUnmount, StorageCommandSDInfo, StorageCommandSDStatus, + StorageCommandCommonResolvePath, + StorageCommandSDMount, + StorageCommandCommonEquivalentPath, } 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 new file mode 100644 index 00000000000..9e96765b624 --- /dev/null +++ b/applications/services/storage/storage_processing.c @@ -0,0 +1,730 @@ +#include "storage_processing.h" +#include +#include + +#define STORAGE_PATH_PREFIX_LEN 4u +_Static_assert( + sizeof(STORAGE_ANY_PATH_PREFIX) == STORAGE_PATH_PREFIX_LEN + 1, + "Any path prefix len mismatch"); +_Static_assert( + sizeof(STORAGE_EXT_PATH_PREFIX) == STORAGE_PATH_PREFIX_LEN + 1, + "Ext path prefix len mismatch"); +_Static_assert( + sizeof(STORAGE_INT_PATH_PREFIX) == STORAGE_PATH_PREFIX_LEN + 1, + "Int path prefix len mismatch"); + +#define FS_CALL(_storage, _fn) ret = _storage->fs_api->_fn; + +static bool storage_type_is_valid(StorageType type) { +#ifdef FURI_RAM_EXEC + return type == ST_EXT; +#else + return type < ST_ERROR; +#endif +} + +static StorageData* get_storage_by_file(File* file, StorageData* storages) { + StorageData* storage_data = NULL; + + for(uint8_t i = 0; i < STORAGE_COUNT; i++) { + if(storage_has_file(file, &storages[i])) { + storage_data = &storages[i]; + } + } + + return storage_data; +} + +static const char* cstr_path_without_vfs_prefix(FuriString* path) { + const char* path_cstr = furi_string_get_cstr(path); + return path_cstr + MIN(STORAGE_PATH_PREFIX_LEN, strlen(path_cstr)); +} + +static StorageType storage_get_type_by_path(FuriString* path) { + StorageType type = ST_ERROR; + const char* path_cstr = furi_string_get_cstr(path); + + if(furi_string_size(path) > STORAGE_PATH_PREFIX_LEN) { + if(path_cstr[STORAGE_PATH_PREFIX_LEN] != '/') { + return ST_ERROR; + } + } + + 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; + } 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(furi_string_search(path, STORAGE_ANY_PATH_PREFIX) == 0) { + switch(real_storage) { + case ST_EXT: + furi_string_replace_at( + path, 0, strlen(STORAGE_EXT_PATH_PREFIX), STORAGE_EXT_PATH_PREFIX); + break; + case ST_INT: + furi_string_replace_at( + path, 0, strlen(STORAGE_INT_PATH_PREFIX), STORAGE_INT_PATH_PREFIX); + break; + default: + break; + } + } +} + +static 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; + } +} + +static void storage_path_trim_trailing_slashes(FuriString* path) { + while(furi_string_end_with(path, "/")) { + furi_string_left(path, furi_string_size(path) - 1); + } +} + +/******************* File Functions *******************/ + +bool storage_process_file_open( + Storage* app, + File* file, + FuriString* path, + FS_AccessMode access_mode, + FS_OpenMode open_mode) { + bool ret = false; + StorageData* storage; + file->error_id = storage_get_data(app, path, &storage); + + if(file->error_id == FSE_OK) { + if(storage_path_already_open(path, storage)) { + file->error_id = FSE_ALREADY_OPEN; + } else { + if(access_mode & FSAM_WRITE) { + storage_data_timestamp(storage); + } + storage_push_storage_file(file, path, storage); + + 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; +} + +bool storage_process_file_close(Storage* app, File* file) { + bool ret = false; + StorageData* storage = get_storage_by_file(file, app->storage); + + if(storage == NULL) { + file->error_id = FSE_INVALID_PARAMETER; + } else { + FS_CALL(storage, file.close(storage, file)); + storage_pop_storage_file(file, storage); + + StorageEvent event = {.type = StorageEventTypeFileClose}; + furi_pubsub_publish(app->pubsub, &event); + } + + return ret; +} + +static uint16_t + storage_process_file_read(Storage* app, File* file, void* buff, uint16_t const bytes_to_read) { + uint16_t ret = 0; + StorageData* storage = get_storage_by_file(file, app->storage); + + if(storage == NULL) { + file->error_id = FSE_INVALID_PARAMETER; + } else { + FS_CALL(storage, file.read(storage, file, buff, bytes_to_read)); + } + + return ret; +} + +static uint16_t storage_process_file_write( + Storage* app, + File* file, + const void* buff, + uint16_t const bytes_to_write) { + uint16_t ret = 0; + StorageData* storage = get_storage_by_file(file, app->storage); + + if(storage == NULL) { + file->error_id = FSE_INVALID_PARAMETER; + } else { + storage_data_timestamp(storage); + FS_CALL(storage, file.write(storage, file, buff, bytes_to_write)); + } + + return ret; +} + +static bool storage_process_file_seek( + Storage* app, + File* file, + const uint32_t offset, + const bool from_start) { + bool ret = false; + StorageData* storage = get_storage_by_file(file, app->storage); + + if(storage == NULL) { + file->error_id = FSE_INVALID_PARAMETER; + } else { + FS_CALL(storage, file.seek(storage, file, offset, from_start)); + } + + return ret; +} + +static uint64_t storage_process_file_tell(Storage* app, File* file) { + uint64_t ret = 0; + StorageData* storage = get_storage_by_file(file, app->storage); + + if(storage == NULL) { + file->error_id = FSE_INVALID_PARAMETER; + } else { + FS_CALL(storage, file.tell(storage, file)); + } + + return ret; +} + +static bool storage_process_file_truncate(Storage* app, File* file) { + bool ret = false; + StorageData* storage = get_storage_by_file(file, app->storage); + + if(storage == NULL) { + file->error_id = FSE_INVALID_PARAMETER; + } else { + storage_data_timestamp(storage); + FS_CALL(storage, file.truncate(storage, file)); + } + + return ret; +} + +static bool storage_process_file_sync(Storage* app, File* file) { + bool ret = false; + StorageData* storage = get_storage_by_file(file, app->storage); + + if(storage == NULL) { + file->error_id = FSE_INVALID_PARAMETER; + } else { + storage_data_timestamp(storage); + FS_CALL(storage, file.sync(storage, file)); + } + + return ret; +} + +static uint64_t storage_process_file_size(Storage* app, File* file) { + uint64_t ret = 0; + StorageData* storage = get_storage_by_file(file, app->storage); + + if(storage == NULL) { + file->error_id = FSE_INVALID_PARAMETER; + } else { + FS_CALL(storage, file.size(storage, file)); + } + + return ret; +} + +static bool storage_process_file_eof(Storage* app, File* file) { + bool ret = false; + StorageData* storage = get_storage_by_file(file, app->storage); + + if(storage == NULL) { + file->error_id = FSE_INVALID_PARAMETER; + } else { + FS_CALL(storage, file.eof(storage, file)); + } + + return ret; +} + +/******************* Dir Functions *******************/ + +bool storage_process_dir_open(Storage* app, File* file, FuriString* path) { + bool ret = false; + StorageData* storage; + file->error_id = storage_get_data(app, path, &storage); + + if(file->error_id == FSE_OK) { + if(storage_path_already_open(path, storage)) { + file->error_id = FSE_ALREADY_OPEN; + } else { + storage_push_storage_file(file, path, storage); + FS_CALL(storage, dir.open(storage, file, cstr_path_without_vfs_prefix(path))); + } + } + + return ret; +} + +bool storage_process_dir_close(Storage* app, File* file) { + bool ret = false; + StorageData* storage = get_storage_by_file(file, app->storage); + + if(storage == NULL) { + file->error_id = FSE_INVALID_PARAMETER; + } else { + FS_CALL(storage, dir.close(storage, file)); + storage_pop_storage_file(file, storage); + + StorageEvent event = {.type = StorageEventTypeDirClose}; + furi_pubsub_publish(app->pubsub, &event); + } + + return ret; +} + +bool storage_process_dir_read( + Storage* app, + File* file, + FileInfo* fileinfo, + char* name, + const uint16_t name_length) { + bool ret = false; + StorageData* storage = get_storage_by_file(file, app->storage); + + if(storage == NULL) { + file->error_id = FSE_INVALID_PARAMETER; + } else { + FS_CALL(storage, dir.read(storage, file, fileinfo, name, name_length)); + } + + return ret; +} + +bool storage_process_dir_rewind(Storage* app, File* file) { + bool ret = false; + StorageData* storage = get_storage_by_file(file, app->storage); + + if(storage == NULL) { + file->error_id = FSE_INVALID_PARAMETER; + } else { + FS_CALL(storage, dir.rewind(storage, file)); + } + + return ret; +} + +/******************* Common FS Functions *******************/ + +static FS_Error + storage_process_common_timestamp(Storage* app, FuriString* path, uint32_t* timestamp) { + StorageData* storage; + FS_Error ret = storage_get_data(app, path, &storage); + + if(ret == FSE_OK) { + *timestamp = storage_data_get_timestamp(storage); + } + + return ret; +} + +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(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, FuriString* path) { + StorageData* storage; + FS_Error ret = storage_get_data(app, path, &storage); + + do { + if(ret != FSE_OK) break; + + if(storage_path_already_open(path, storage)) { + ret = FSE_ALREADY_OPEN; + break; + } + + storage_data_timestamp(storage); + FS_CALL(storage, common.remove(storage, cstr_path_without_vfs_prefix(path))); + } while(false); + + return ret; +} + +static FS_Error storage_process_common_mkdir(Storage* app, FuriString* path) { + StorageData* storage; + FS_Error ret = storage_get_data(app, path, &storage); + + if(ret == FSE_OK) { + storage_data_timestamp(storage); + FS_CALL(storage, common.mkdir(storage, cstr_path_without_vfs_prefix(path))); + } + + return ret; +} + +static FS_Error storage_process_common_fs_info( + Storage* app, + FuriString* path, + uint64_t* total_space, + uint64_t* free_space) { + StorageData* storage; + FS_Error ret = storage_get_data(app, path, &storage); + + if(ret == FSE_OK) { + FS_CALL( + storage, + common.fs_info(storage, cstr_path_without_vfs_prefix(path), total_space, free_space)); + } + + return ret; +} + +static bool + storage_process_common_equivalent_path(Storage* app, FuriString* path1, FuriString* path2) { + bool ret = false; + + do { + const StorageType storage_type1 = storage_get_type_by_path(path1); + const StorageType storage_type2 = storage_get_type_by_path(path2); + + // Paths on different storages are of course not equal + if(storage_type1 != storage_type2) break; + + StorageData* storage; + const FS_Error status = storage_get_data(app, path1, &storage); + + if(status != FSE_OK) break; + + FS_CALL( + storage, + common.equivalent_path(furi_string_get_cstr(path1), furi_string_get_cstr(path2))); + + } while(false); + + return ret; +} + +/****************** Raw SD API ******************/ +// TODO FL-3521: think about implementing a custom storage API to split that kind of api linkage +#include "storages/storage_ext.h" + +static FS_Error storage_process_sd_format(Storage* app) { + FS_Error ret = FSE_OK; + + if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusNotReady) { + ret = FSE_NOT_READY; + } else { + ret = sd_format_card(&app->storage[ST_EXT]); + storage_data_timestamp(&app->storage[ST_EXT]); + } + + return ret; +} + +static FS_Error storage_process_sd_unmount(Storage* app) { + FS_Error ret = FSE_OK; + + do { + StorageData* storage = &app->storage[ST_EXT]; + if(storage_data_status(storage) == StorageStatusNotReady) { + ret = FSE_NOT_READY; + break; + } + + if(storage_open_files_count(storage)) { + ret = FSE_DENIED; + break; + } + + sd_unmount_card(storage); + storage_data_timestamp(storage); + } while(false); + + return ret; +} + +static FS_Error storage_process_sd_mount(Storage* app) { + FS_Error ret = FSE_OK; + + do { + StorageData* storage = &app->storage[ST_EXT]; + if(storage_data_status(storage) != StorageStatusNotReady) { + ret = FSE_NOT_READY; + break; + } + + ret = sd_mount_card(storage, true); + storage_data_timestamp(storage); + } while(false); + + return ret; +} + +static FS_Error storage_process_sd_info(Storage* app, SDInfo* info) { + FS_Error ret = FSE_OK; + + if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusNotReady) { + ret = FSE_NOT_READY; + } else { + ret = sd_card_info(&app->storage[ST_EXT], info); + } + + return ret; +} + +static FS_Error storage_process_sd_status(Storage* app) { + FS_Error ret; + StorageStatus status = storage_data_status(&app->storage[ST_EXT]); + + switch(status) { + case StorageStatusOK: + ret = FSE_OK; + break; + case StorageStatusNotReady: + ret = FSE_NOT_READY; + break; + default: + ret = FSE_INTERNAL; + break; + } + + 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)); + + // "/data" -> "/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); + } else if(furi_string_start_with(path, STORAGE_APP_ASSETS_PATH_PREFIX)) { + FuriString* apps_assets_path_with_appsid = furi_string_alloc_set(APPS_ASSETS_PATH "/"); + furi_string_cat(apps_assets_path_with_appsid, furi_thread_get_appid(thread_id)); + + // "/assets" -> "/ext/apps_assets/appsid" + furi_string_replace_at( + path, + 0, + strlen(STORAGE_APP_ASSETS_PATH_PREFIX), + furi_string_get_cstr(apps_assets_path_with_appsid)); + + furi_string_free(apps_assets_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, + path, + message->data->fopen.access_mode, + message->data->fopen.open_mode); + break; + case StorageCommandFileClose: + message->return_data->bool_value = + storage_process_file_close(app, message->data->fopen.file); + break; + case StorageCommandFileRead: + message->return_data->uint16_value = storage_process_file_read( + app, + message->data->fread.file, + message->data->fread.buff, + message->data->fread.bytes_to_read); + break; + case StorageCommandFileWrite: + message->return_data->uint16_value = storage_process_file_write( + app, + message->data->fwrite.file, + message->data->fwrite.buff, + message->data->fwrite.bytes_to_write); + break; + case StorageCommandFileSeek: + message->return_data->bool_value = storage_process_file_seek( + app, + message->data->fseek.file, + message->data->fseek.offset, + message->data->fseek.from_start); + break; + case StorageCommandFileTell: + message->return_data->uint64_value = + storage_process_file_tell(app, message->data->file.file); + break; + case StorageCommandFileTruncate: + message->return_data->bool_value = + storage_process_file_truncate(app, message->data->file.file); + break; + case StorageCommandFileSync: + message->return_data->bool_value = + storage_process_file_sync(app, message->data->file.file); + break; + case StorageCommandFileSize: + message->return_data->uint64_value = + storage_process_file_size(app, message->data->file.file); + break; + case StorageCommandFileEof: + 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, path); + break; + case StorageCommandDirClose: + message->return_data->bool_value = + storage_process_dir_close(app, message->data->file.file); + break; + case StorageCommandDirRead: + message->return_data->bool_value = storage_process_dir_read( + app, + message->data->dread.file, + message->data->dread.fileinfo, + message->data->dread.name, + message->data->dread.name_length); + break; + case StorageCommandDirRewind: + message->return_data->bool_value = + storage_process_dir_rewind(app, message->data->file.file); + break; + + // Common operations + case StorageCommandCommonTimestamp: + 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: + 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: + 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: + 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, 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; + + case StorageCommandCommonEquivalentPath: { + FuriString* path1 = furi_string_alloc_set(message->data->cequivpath.path1); + FuriString* path2 = furi_string_alloc_set(message->data->cequivpath.path2); + storage_path_trim_trailing_slashes(path1); + storage_path_trim_trailing_slashes(path2); + storage_process_alias(app, path1, message->data->cequivpath.thread_id, false); + storage_process_alias(app, path2, message->data->cequivpath.thread_id, false); + if(message->data->cequivpath.truncate) { + furi_string_left(path2, furi_string_size(path1)); + } + message->return_data->bool_value = + storage_process_common_equivalent_path(app, path1, path2); + furi_string_free(path1); + furi_string_free(path2); + break; + } + + // SD operations + case StorageCommandSDFormat: + message->return_data->error_value = storage_process_sd_format(app); + break; + case StorageCommandSDUnmount: + message->return_data->error_value = storage_process_sd_unmount(app); + break; + case StorageCommandSDMount: + message->return_data->error_value = storage_process_sd_mount(app); + break; + case StorageCommandSDInfo: + message->return_data->error_value = + storage_process_sd_info(app, message->data->sdinfo.info); + break; + case StorageCommandSDStatus: + message->return_data->error_value = storage_process_sd_status(app); + break; + } + + if(path != NULL) { //-V547 + furi_string_free(path); + } + + api_lock_unlock(message->lock); +} + +void storage_process_message(Storage* app, StorageMessage* message) { + storage_process_message_internal(app, message); +} diff --git a/applications/storage/storage_processing.h b/applications/services/storage/storage_processing.h similarity index 100% rename from applications/storage/storage_processing.h rename to applications/services/storage/storage_processing.h diff --git a/applications/storage/storage_sd_api.c b/applications/services/storage/storage_sd_api.c similarity index 100% rename from applications/storage/storage_sd_api.c rename to applications/services/storage/storage_sd_api.c diff --git a/applications/services/storage/storage_sd_api.h b/applications/services/storage/storage_sd_api.h new file mode 100644 index 00000000000..842334d50db --- /dev/null +++ b/applications/services/storage/storage_sd_api.h @@ -0,0 +1,41 @@ +#pragma once +#include +#include "filesystem_api_defines.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SD_LABEL_LENGTH 34 + +typedef enum { + FST_UNKNOWN, + FST_FAT12, + FST_FAT16, + FST_FAT32, + FST_EXFAT, +} SDFsType; + +typedef struct { + SDFsType fs_type; + uint32_t kb_total; + uint32_t kb_free; + 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; +} SDInfo; + +const char* sd_api_get_fs_type_text(SDFsType fs_type); + +#ifdef __cplusplus +} +#endif diff --git a/applications/storage/storages/sd_notify.c b/applications/services/storage/storages/sd_notify.c similarity index 100% rename from applications/storage/storages/sd_notify.c rename to applications/services/storage/storages/sd_notify.c diff --git a/applications/storage/storages/sd_notify.h b/applications/services/storage/storages/sd_notify.h similarity index 100% rename from applications/storage/storages/sd_notify.h rename to applications/services/storage/storages/sd_notify.h diff --git a/applications/storage/storages/storage_ext.c b/applications/services/storage/storages/storage_ext.c similarity index 87% rename from applications/storage/storages/storage_ext.c rename to applications/services/storage/storages/storage_ext.c index 7341a6ec86e..7e617c0ff20 100644 --- a/applications/storage/storages/storage_ext.c +++ b/applications/services/storage/storages/storage_ext.c @@ -24,15 +24,13 @@ static FS_Error storage_ext_parse_error(SDError error); /******************* Core Functions *******************/ -static bool sd_mount_card(StorageData* storage, bool notify) { +static bool sd_mount_card_internal(StorageData* storage, bool notify) { bool result = false; - uint8_t counter = BSP_SD_MaxMountRetryCount(); + uint8_t counter = furi_hal_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()) { + while(result == false && counter > 0 && furi_hal_sd_is_present()) { if(notify) { NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); sd_notify_wait(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 = furi_hal_sd_init(true); } else { - bsp_result = BSP_SD_Init(false); + bsp_result = furi_hal_sd_init(false); } if(bsp_result) { @@ -90,7 +88,7 @@ static bool sd_mount_card(StorageData* storage, bool notify) { } } - storage_data_unlock(storage); + storage_data_timestamp(storage); return result; } @@ -99,17 +97,41 @@ 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? - + // TODO FL-3522: do i need to close the files? f_mount(0, sd_data->path, 0); - storage_data_unlock(storage); + return storage_ext_parse_error(error); } +FS_Error sd_mount_card(StorageData* storage, bool notify) { + sd_mount_card_internal(storage, notify); + FS_Error error; + + if(storage->status != StorageStatusOK) { + FURI_LOG_E(TAG, "sd init error: %s", storage_data_status_text(storage)); + if(notify) { + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + sd_notify_error(notification); + furi_record_close(RECORD_NOTIFICATION); + } + error = FSE_INTERNAL; + } else { + FURI_LOG_I(TAG, "card mounted"); + if(notify) { + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + sd_notify_success(notification); + furi_record_close(RECORD_NOTIFICATION); + } + + error = FSE_OK; + } + + return error; +} + FS_Error sd_format_card(StorageData* storage) { #ifdef FURI_RAM_EXEC UNUSED(storage); @@ -119,8 +141,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); @@ -137,8 +157,6 @@ FS_Error sd_format_card(StorageData* storage) { storage->status = StorageStatusOK; } while(false); - storage_data_unlock(storage); - return storage_ext_parse_error(error); #endif } @@ -155,14 +173,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 @@ -209,6 +225,20 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) { #endif } + FuriHalSdInfo info; + FuriStatus status = furi_hal_sd_info(&info); + + if(status == FuriStatusOk) { + sd_info->manufacturer_id = info.manufacturer_id; + memcpy(sd_info->oem_id, info.oem_id, sizeof(info.oem_id)); + memcpy(sd_info->product_name, info.product_name, sizeof(info.product_name)); + sd_info->product_revision_major = info.product_revision_major; + sd_info->product_revision_minor = info.product_revision_minor; + sd_info->product_serial_number = info.product_serial_number; + sd_info->manufacturing_year = info.manufacturing_year; + sd_info->manufacturing_month = info.manufacturing_month; + } + return storage_ext_parse_error(error); } @@ -216,36 +246,19 @@ static void storage_ext_tick_internal(StorageData* storage, bool notify) { SDData* sd_data = storage->data; if(sd_data->sd_was_present) { - if(hal_sd_detect()) { + if(furi_hal_sd_is_present()) { FURI_LOG_I(TAG, "card detected"); - sd_mount_card(storage, notify); - - if(storage->status != StorageStatusOK) { - FURI_LOG_E(TAG, "sd init error: %s", storage_data_status_text(storage)); - if(notify) { - NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); - sd_notify_error(notification); - furi_record_close(RECORD_NOTIFICATION); - } - } else { - FURI_LOG_I(TAG, "card mounted"); - if(notify) { - NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); - sd_notify_success(notification); - furi_record_close(RECORD_NOTIFICATION); - } - } - sd_data->sd_was_present = false; + sd_mount_card(storage, notify); - if(!hal_sd_detect()) { + if(!furi_hal_sd_is_present()) { FURI_LOG_I(TAG, "card removed while mounting"); sd_unmount_card(storage); sd_data->sd_was_present = true; } } } else { - if(!hal_sd_detect()) { + if(!furi_hal_sd_is_present()) { FURI_LOG_I(TAG, "card removed"); sd_data->sd_was_present = true; @@ -333,6 +346,7 @@ static bool storage_ext_file_close(void* ctx, File* file) { file->internal_error_id = f_close(file_data); file->error_id = storage_ext_parse_error(file->internal_error_id); free(file_data); + storage_set_storage_file_data(file, NULL, storage); return (file->error_id == FSE_OK); } @@ -582,6 +596,16 @@ static FS_Error storage_ext_common_fs_info( #endif } +static bool storage_ext_common_equivalent_path(const char* path1, const char* path2) { +#ifdef FURI_RAM_EXEC + UNUSED(path1); + UNUSED(path2); + return false; +#else + return strcasecmp(path1, path2) == 0; +#endif +} + /******************* Init Storage *******************/ static const FS_Api fs_api = { .file = @@ -610,12 +634,15 @@ static const FS_Api fs_api = { .mkdir = storage_ext_common_mkdir, .remove = storage_ext_common_remove, .fs_info = storage_ext_common_fs_info, + .equivalent_path = storage_ext_common_equivalent_path, }, }; void storage_ext_init(StorageData* storage) { + fatfs_init(); + SDData* sd_data = malloc(sizeof(SDData)); - sd_data->fs = &USERFatFS; + sd_data->fs = &fatfs_object; sd_data->path = "0:/"; sd_data->sd_was_present = true; @@ -623,7 +650,7 @@ void storage_ext_init(StorageData* storage) { storage->api.tick = storage_ext_tick; storage->fs_api = &fs_api; - hal_sd_detect_init(); + furi_hal_sd_presence_init(); // do not notify on first launch, notifications app is waiting for our thread to read settings storage_ext_tick_internal(storage, false); diff --git a/applications/storage/storages/storage_ext.h b/applications/services/storage/storages/storage_ext.h similarity index 85% rename from applications/storage/storages/storage_ext.h rename to applications/services/storage/storages/storage_ext.h index 07ddbcf2f4e..18d1f714373 100644 --- a/applications/storage/storages/storage_ext.h +++ b/applications/services/storage/storages/storage_ext.h @@ -8,6 +8,7 @@ extern "C" { #endif void storage_ext_init(StorageData* storage); +FS_Error sd_mount_card(StorageData* storage, bool notify); FS_Error sd_unmount_card(StorageData* storage); FS_Error sd_format_card(StorageData* storage); FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info); diff --git a/applications/storage/storages/storage_int.c b/applications/services/storage/storages/storage_int.c similarity index 94% rename from applications/storage/storages/storage_int.c rename to applications/services/storage/storages/storage_int.c index 0765a92dc5c..39b092c1d19 100644 --- a/applications/storage/storages/storage_int.c +++ b/applications/services/storage/storages/storage_int.c @@ -9,7 +9,7 @@ /* When less than LFS_RESERVED_PAGES_COUNT are left free, creation & * modification of non-dot files is restricted */ -#define LFS_RESERVED_PAGES_COUNT 5 +#define LFS_RESERVED_PAGES_COUNT 3 typedef struct { const size_t start_address; @@ -77,12 +77,12 @@ static int storage_int_device_read( FURI_LOG_T( TAG, - "Device read: block %d, off %d, buffer: %p, size %d, translated address: %p", + "Device read: block %lu, off %lu, buffer: %p, size %lu, translated address: %p", block, off, buffer, size, - address); + (void*)address); memcpy(buffer, (void*)address, size); @@ -100,19 +100,16 @@ static int storage_int_device_prog( FURI_LOG_T( TAG, - "Device prog: block %d, off %d, buffer: %p, size %d, translated address: %p", + "Device prog: block %lu, off %lu, buffer: %p, size %lu, translated address: %p", block, off, buffer, size, - address); + (void*)address); int ret = 0; while(size > 0) { - if(!furi_hal_flash_write_dword(address, *(uint64_t*)buffer)) { - ret = -1; - break; - } + furi_hal_flash_write_dword(address, *(uint64_t*)buffer); address += c->prog_size; buffer += c->prog_size; size -= c->prog_size; @@ -125,18 +122,15 @@ static int storage_int_device_erase(const struct lfs_config* c, lfs_block_t bloc LFSData* lfs_data = c->context; size_t page = lfs_data->start_page + block; - FURI_LOG_D(TAG, "Device erase: page %d, translated page: %x", block, page); + FURI_LOG_D(TAG, "Device erase: page %lu, translated page: %zx", block, page); - if(furi_hal_flash_erase(page)) { - return 0; - } else { - return -1; - } + furi_hal_flash_erase(page); + return 0; } static int storage_int_device_sync(const struct lfs_config* c) { UNUSED(c); - FURI_LOG_D(TAG, "Device sync: skipping, cause "); + FURI_LOG_D(TAG, "Device sync: skipping"); return 0; } @@ -246,56 +240,38 @@ static void storage_int_lfs_mount(LFSData* lfs_data, StorageData* storage) { /****************** Common Functions ******************/ static FS_Error storage_int_parse_error(int error) { - FS_Error result = FSE_INTERNAL; + FS_Error result; if(error >= LFS_ERR_OK) { result = FSE_OK; } else { switch(error) { - case LFS_ERR_IO: - result = FSE_INTERNAL; - break; - case LFS_ERR_CORRUPT: - result = FSE_INTERNAL; - break; case LFS_ERR_NOENT: result = FSE_NOT_EXIST; break; case LFS_ERR_EXIST: result = FSE_EXIST; break; - case LFS_ERR_NOTDIR: - result = FSE_INVALID_NAME; - break; - case LFS_ERR_ISDIR: - result = FSE_INVALID_NAME; - break; case LFS_ERR_NOTEMPTY: result = FSE_DENIED; break; - case LFS_ERR_BADF: - result = FSE_INVALID_NAME; - break; - case LFS_ERR_FBIG: - result = FSE_INTERNAL; - break; case LFS_ERR_INVAL: - result = FSE_INVALID_PARAMETER; - break; - case LFS_ERR_NOSPC: - result = FSE_INTERNAL; - break; - case LFS_ERR_NOMEM: - result = FSE_INTERNAL; - break; case LFS_ERR_NOATTR: result = FSE_INVALID_PARAMETER; break; + case LFS_ERR_BADF: + case LFS_ERR_ISDIR: + case LFS_ERR_NOTDIR: case LFS_ERR_NAMETOOLONG: result = FSE_INVALID_NAME; break; + case LFS_ERR_IO: + case LFS_ERR_FBIG: + case LFS_ERR_NOSPC: + case LFS_ERR_NOMEM: + case LFS_ERR_CORRUPT: default: - break; + result = FSE_INTERNAL; } } @@ -344,11 +320,12 @@ static bool storage_int_file_open( storage_set_storage_file_data(file, handle, storage); if(!enough_free_space) { - string_t filename; - string_init(filename); + FuriString* filename; + filename = furi_string_alloc(); path_extract_basename(path, filename); - bool is_dot_file = (!string_empty_p(filename) && (string_get_char(filename, 0) == '.')); - string_clear(filename); + bool is_dot_file = + (!furi_string_empty(filename) && (furi_string_get_char(filename, 0) == '.')); + furi_string_free(filename); /* Restrict write & creation access to all non-dot files */ if(!is_dot_file && (flags & (LFS_O_CREAT | LFS_O_WRONLY))) { @@ -709,6 +686,10 @@ static FS_Error storage_int_common_fs_info( return storage_int_parse_error(result); } +static bool storage_int_common_equivalent_path(const char* path1, const char* path2) { + return strcmp(path1, path2) == 0; +} + /******************* Init Storage *******************/ static const FS_Api fs_api = { .file = @@ -737,6 +718,7 @@ static const FS_Api fs_api = { .mkdir = storage_int_common_mkdir, .remove = storage_int_common_remove, .fs_info = storage_int_common_fs_info, + .equivalent_path = storage_int_common_equivalent_path, }, }; @@ -745,8 +727,8 @@ void storage_int_init(StorageData* storage) { LFSData* lfs_data = storage_int_lfs_data_alloc(); FURI_LOG_I( TAG, - "Config: start %p, read %d, write %d, page size: %d, page count: %d, cycles: %d", - lfs_data->start_address, + "Config: start %p, read %lu, write %lu, page size: %lu, page count: %lu, cycles: %ld", + (void*)lfs_data->start_address, lfs_data->config.read_size, lfs_data->config.prog_size, lfs_data->config.block_size, diff --git a/applications/storage/storages/storage_int.h b/applications/services/storage/storages/storage_int.h similarity index 100% rename from applications/storage/storages/storage_int.h rename to applications/services/storage/storages/storage_int.h diff --git a/applications/about/about.c b/applications/settings/about/about.c similarity index 76% rename from applications/about/about.c rename to applications/settings/about/about.c index 1ce143f69b7..a40caea6f7d 100644 --- a/applications/about/about.c +++ b/applications/settings/about/about.c @@ -3,27 +3,47 @@ #include #include #include -#include +#include #include #include #include +#include typedef DialogMessageButton (*AboutDialogScreen)(DialogsApp* dialogs, DialogMessage* message); static DialogMessageButton product_screen(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; +<<<<<<< HEAD:applications/about/about.c const char* screen_header = "Product: Flipper Zero\n" "Model: FZ.1 rev. F7\n"; const char* screen_text = "FCC ID: 2A2V6-FZ\n" "IC: 27624-FZ"; - - dialog_message_set_header(message, screen_header, 0, 0, AlignLeft, AlignTop); - dialog_message_set_text(message, screen_text, 0, 26, AlignLeft, AlignTop); +======= + FuriString* screen_header = furi_string_alloc_printf( + "Product: %s\n" + "Model: %s", + furi_hal_version_get_model_name(), + furi_hal_version_get_model_code()); +>>>>>>> upstream/dev:applications/settings/about/about.c + + FuriString* screen_text = furi_string_alloc_printf( + "FCC ID: %s\n" + "IC: %s", + furi_hal_version_get_fcc_id(), + furi_hal_version_get_ic_id()); + + dialog_message_set_header( + message, furi_string_get_cstr(screen_header), 0, 0, AlignLeft, AlignTop); + dialog_message_set_text( + message, furi_string_get_cstr(screen_text), 0, 26, AlignLeft, AlignTop); result = dialog_message_show(dialogs, message); dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop); dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); + furi_string_free(screen_header); + furi_string_free(screen_text); + return result; } @@ -69,9 +89,12 @@ static DialogMessageButton icon1_screen(DialogsApp* dialogs, DialogMessage* mess static DialogMessageButton icon2_screen(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; - dialog_message_set_icon(message, &I_Certification2_98x33, 15, 10); + dialog_message_set_icon(message, &I_Certification2_46x33, 15, 10); + dialog_message_set_text( + message, furi_hal_version_get_mic_id(), 63, 27, AlignLeft, AlignCenter); result = dialog_message_show(dialogs, message); dialog_message_set_icon(message, NULL, 0, 0); + dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); return result; } @@ -79,11 +102,11 @@ static DialogMessageButton icon2_screen(DialogsApp* dialogs, DialogMessage* mess static DialogMessageButton hw_version_screen(DialogsApp* dialogs, DialogMessage* message) { furi_hal_version_load_custom_otp(); DialogMessageButton result; - string_t buffer; - string_init(buffer); + FuriString* buffer; + buffer = furi_string_alloc(); const char* my_name = furi_hal_version_get_name_ptr(); - string_cat_printf( + furi_string_cat_printf( buffer, "%d.F%dB%dC%d %s:%s %s\n", furi_hal_version_get_hw_version(), @@ -94,54 +117,57 @@ static DialogMessageButton hw_version_screen(DialogsApp* dialogs, DialogMessage* furi_hal_region_get_name(), my_name ? my_name : "Unknown"); - string_cat_printf(buffer, "Serial Number:\n"); + furi_string_cat_printf(buffer, "Serial Number:\n"); const uint8_t* uid = furi_hal_version_uid(); for(size_t i = 0; i < furi_hal_version_uid_size(); i++) { - string_cat_printf(buffer, "%02X", uid[i]); + furi_string_cat_printf(buffer, "%02X", uid[i]); } dialog_message_set_header(message, "HW Version Info:", 0, 0, AlignLeft, AlignTop); - dialog_message_set_text(message, string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop); + dialog_message_set_text(message, furi_string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop); result = dialog_message_show(dialogs, message); dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop); - string_clear(buffer); + furi_string_free(buffer); return result; } static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; - string_t buffer; - string_init(buffer); + FuriString* buffer; + buffer = furi_string_alloc(); const Version* ver = furi_hal_version_get_firmware_version(); const BleGlueC2Info* c2_ver = NULL; #ifdef SRV_BT c2_ver = ble_glue_get_c2_info(); #endif - if(!ver) { - string_cat_printf(buffer, "No info\n"); + if(!ver) { //-V1051 + furi_string_cat_printf(buffer, "No info\n"); } else { - string_cat_printf( + uint16_t api_major, api_minor; + furi_hal_info_get_api_version(&api_major, &api_minor); + furi_string_cat_printf( buffer, - "%s [%s]\n%s%s [%s] %s\n[%d] %s", + "%s [%s]\n%s%s [%d.%d] %s\n[%d] %s", version_get_version(ver), version_get_builddate(ver), version_get_dirty_flag(ver) ? "[!] " : "", version_get_githash(ver), - version_get_gitbranchnum(ver), + api_major, + api_minor, c2_ver ? c2_ver->StackTypeString : "", version_get_target(ver), version_get_gitbranch(ver)); } dialog_message_set_header(message, "FW Version Info:", 0, 0, AlignLeft, AlignTop); - dialog_message_set_text(message, string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop); + dialog_message_set_text(message, furi_string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop); result = dialog_message_show(dialogs, message); dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop); - string_clear(buffer); + furi_string_free(buffer); return result; } @@ -155,8 +181,6 @@ const AboutDialogScreen about_screens[] = { hw_version_screen, fw_version_screen}; -const size_t about_screens_count = sizeof(about_screens) / sizeof(AboutDialogScreen); - int32_t about_settings_app(void* p) { UNUSED(p); DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); @@ -177,7 +201,7 @@ int32_t about_settings_app(void* p) { view_dispatcher_switch_to_view(view_dispatcher, empty_screen_index); while(1) { - if(screen_index >= about_screens_count - 1) { + if(screen_index >= COUNT_OF(about_screens) - 1) { dialog_message_set_buttons(message, "Back", NULL, NULL); } else { dialog_message_set_buttons(message, "Back", NULL, "Next"); @@ -192,7 +216,7 @@ int32_t about_settings_app(void* p) { screen_index--; } } else if(screen_result == DialogMessageButtonRight) { - if(screen_index < about_screens_count) { + if(screen_index < COUNT_OF(about_screens) - 1) { screen_index++; } } else if(screen_result == DialogMessageButtonBack) { @@ -209,4 +233,4 @@ int32_t about_settings_app(void* p) { furi_record_close(RECORD_GUI); return 0; -} \ No newline at end of file +} diff --git a/applications/about/application.fam b/applications/settings/about/application.fam similarity index 100% rename from applications/about/application.fam rename to applications/settings/about/application.fam diff --git a/applications/settings/application.fam b/applications/settings/application.fam new file mode 100644 index 00000000000..cc4b9703dcb --- /dev/null +++ b/applications/settings/application.fam @@ -0,0 +1,10 @@ +App( + appid="settings_apps", + name="Basic settings apps bundle", + apptype=FlipperAppType.METAPACKAGE, + provides=[ + "passport", + "system_settings", + "about", + ], +) diff --git a/applications/settings/bt_settings_app/application.fam b/applications/settings/bt_settings_app/application.fam new file mode 100644 index 00000000000..7a985046b9e --- /dev/null +++ b/applications/settings/bt_settings_app/application.fam @@ -0,0 +1,12 @@ +App( + appid="bt_settings", + name="Bluetooth", + apptype=FlipperAppType.SETTINGS, + entry_point="bt_settings_app", + stack_size=1 * 1024, + requires=[ + "bt", + "gui", + ], + order=10, +) diff --git a/applications/bt/bt_settings_app/bt_settings_app.c b/applications/settings/bt_settings_app/bt_settings_app.c old mode 100755 new mode 100644 similarity index 100% rename from applications/bt/bt_settings_app/bt_settings_app.c rename to applications/settings/bt_settings_app/bt_settings_app.c diff --git a/applications/bt/bt_settings_app/bt_settings_app.h b/applications/settings/bt_settings_app/bt_settings_app.h old mode 100755 new mode 100644 similarity index 94% rename from applications/bt/bt_settings_app/bt_settings_app.h rename to applications/settings/bt_settings_app/bt_settings_app.h index 5b4a8d13928..b79e3695115 --- a/applications/bt/bt_settings_app/bt_settings_app.h +++ b/applications/settings/bt_settings_app/bt_settings_app.h @@ -6,12 +6,13 @@ #include #include #include +#include #include #include #include -#include "../bt_settings.h" +#include #include "scenes/bt_settings_scene.h" enum BtSettingsCustomEvent { diff --git a/applications/bt/bt_settings_app/scenes/bt_settings_scene.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene.c similarity index 100% rename from applications/bt/bt_settings_app/scenes/bt_settings_scene.c rename to applications/settings/bt_settings_app/scenes/bt_settings_scene.c diff --git a/applications/bt/bt_settings_app/scenes/bt_settings_scene.h b/applications/settings/bt_settings_app/scenes/bt_settings_scene.h similarity index 100% rename from applications/bt/bt_settings_app/scenes/bt_settings_scene.h rename to applications/settings/bt_settings_app/scenes/bt_settings_scene.h diff --git a/applications/bt/bt_settings_app/scenes/bt_settings_scene_config.h b/applications/settings/bt_settings_app/scenes/bt_settings_scene_config.h old mode 100755 new mode 100644 similarity index 100% rename from applications/bt/bt_settings_app/scenes/bt_settings_scene_config.h rename to applications/settings/bt_settings_app/scenes/bt_settings_scene_config.h diff --git a/applications/bt/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 old mode 100755 new mode 100644 similarity index 98% rename from applications/bt/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c rename to applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c index 964736b6695..31921b9f339 --- a/applications/bt/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,5 @@ #include "../bt_settings_app.h" -#include "furi_hal_bt.h" +#include void bt_settings_scene_forget_dev_confirm_dialog_callback(DialogExResult result, void* context) { furi_assert(context); diff --git a/applications/bt/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 old mode 100755 new mode 100644 similarity index 98% rename from applications/bt/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c rename to applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c index c0b0c80a917..481ba6d5c85 --- a/applications/bt/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,5 @@ #include "../bt_settings_app.h" -#include "furi_hal_bt.h" +#include void bt_settings_app_scene_forget_dev_success_popup_callback(void* context) { BtSettingsApp* app = context; diff --git a/applications/bt/bt_settings_app/scenes/bt_settings_scene_start.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c old mode 100755 new mode 100644 similarity index 99% rename from applications/bt/bt_settings_app/scenes/bt_settings_scene_start.c rename to applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c index bcbc902b248..5db98e9dec3 --- a/applications/bt/bt_settings_app/scenes/bt_settings_scene_start.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c @@ -1,5 +1,5 @@ #include "../bt_settings_app.h" -#include "furi_hal_bt.h" +#include enum BtSetting { BtSettingOff, diff --git a/applications/settings/desktop_settings/application.fam b/applications/settings/desktop_settings/application.fam new file mode 100644 index 00000000000..d01a28d36f3 --- /dev/null +++ b/applications/settings/desktop_settings/application.fam @@ -0,0 +1,12 @@ +App( + appid="desktop_settings", + name="Desktop", + apptype=FlipperAppType.SETTINGS, + entry_point="desktop_settings_app", + requires=[ + "desktop", + "gui", + ], + stack_size=1 * 1024, + order=50, +) diff --git a/applications/desktop/desktop_settings/desktop_settings_app.c b/applications/settings/desktop_settings/desktop_settings_app.c similarity index 95% rename from applications/desktop/desktop_settings/desktop_settings_app.c rename to applications/settings/desktop_settings/desktop_settings_app.c index 89513a8b867..afb5d59ec69 100644 --- a/applications/desktop/desktop_settings/desktop_settings_app.c +++ b/applications/settings/desktop_settings/desktop_settings_app.c @@ -4,7 +4,7 @@ #include "desktop_settings_app.h" #include "scenes/desktop_settings_scene.h" -#include "../views/desktop_view_pin_input.h" +#include static bool desktop_settings_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -22,6 +22,7 @@ DesktopSettingsApp* desktop_settings_app_alloc() { DesktopSettingsApp* app = malloc(sizeof(DesktopSettingsApp)); app->gui = furi_record_open(RECORD_GUI); + app->dialogs = furi_record_open(RECORD_DIALOGS); app->view_dispatcher = view_dispatcher_alloc(); app->scene_manager = scene_manager_alloc(&desktop_settings_scene_handlers, app); view_dispatcher_enable_queue(app->view_dispatcher); @@ -83,13 +84,14 @@ void desktop_settings_app_free(DesktopSettingsApp* app) { view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); // Records + furi_record_close(RECORD_DIALOGS); furi_record_close(RECORD_GUI); free(app); } extern int32_t desktop_settings_app(void* p) { DesktopSettingsApp* app = desktop_settings_app_alloc(); - LOAD_DESKTOP_SETTINGS(&app->settings); + DESKTOP_SETTINGS_LOAD(&app->settings); if(p && (strcmp(p, DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG) == 0)) { scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto); } else { diff --git a/applications/desktop/desktop_settings/desktop_settings_app.h b/applications/settings/desktop_settings/desktop_settings_app.h similarity index 86% rename from applications/desktop/desktop_settings/desktop_settings_app.h rename to applications/settings/desktop_settings/desktop_settings_app.h index 93ca7b35c75..6f97564c94d 100644 --- a/applications/desktop/desktop_settings/desktop_settings_app.h +++ b/applications/settings/desktop_settings/desktop_settings_app.h @@ -6,9 +6,11 @@ #include #include #include +#include +#include -#include "desktop_settings.h" -#include "desktop/views/desktop_view_pin_input.h" +#include +#include #include "views/desktop_settings_view_pin_setup_howto.h" #include "views/desktop_settings_view_pin_setup_howto2.h" @@ -25,6 +27,7 @@ typedef struct { DesktopSettings settings; Gui* gui; + DialogsApp* dialogs; SceneManager* scene_manager; ViewDispatcher* view_dispatcher; VariableItemList* variable_item_list; diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene.c similarity index 100% rename from applications/desktop/desktop_settings/scenes/desktop_settings_scene.c rename to applications/settings/desktop_settings/scenes/desktop_settings_scene.c diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene.h b/applications/settings/desktop_settings/scenes/desktop_settings_scene.h similarity index 100% rename from applications/desktop/desktop_settings/scenes/desktop_settings_scene.h rename to applications/settings/desktop_settings/scenes/desktop_settings_scene.h diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_config.h b/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h similarity index 100% rename from applications/desktop/desktop_settings/scenes/desktop_settings_scene_config.h rename to applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c new file mode 100644 index 00000000000..31826cae074 --- /dev/null +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c @@ -0,0 +1,190 @@ +#include "../desktop_settings_app.h" +#include "applications.h" +#include "desktop_settings_scene.h" +#include "desktop_settings_scene_i.h" +#include +#include +#include + +#define APPS_COUNT (FLIPPER_APPS_COUNT + FLIPPER_EXTERNAL_APPS_COUNT) + +#define DEFAULT_INDEX (0) +#define EXTERNAL_BROWSER_NAME ("Apps Menu (Default)") +#define PASSPORT_NAME ("Passport (Default)") + +#define EXTERNAL_APPLICATION_INDEX (1) +#define EXTERNAL_APPLICATION_NAME ("[Select App]") + +#define PRESELECTED_SPECIAL 0xffffffff + +static const char* favorite_fap_get_app_name(size_t i) { + const char* name; + if(i < FLIPPER_APPS_COUNT) { + name = FLIPPER_APPS[i].name; + } else { + name = FLIPPER_EXTERNAL_APPS[i - FLIPPER_APPS_COUNT].name; + } + + return name; +} + +static bool favorite_fap_selector_item_callback( + FuriString* file_path, + void* context, + uint8_t** icon_ptr, + FuriString* item_name) { + UNUSED(context); + Storage* storage = furi_record_open(RECORD_STORAGE); + bool success = flipper_application_load_name_and_icon(file_path, storage, icon_ptr, item_name); + furi_record_close(RECORD_STORAGE); + return success; +} + +static bool favorite_fap_selector_file_exists(char* file_path) { + Storage* storage = furi_record_open(RECORD_STORAGE); + bool exists = storage_file_exists(storage, file_path); + furi_record_close(RECORD_STORAGE); + return exists; +} + +static void desktop_settings_scene_favorite_submenu_callback(void* context, uint32_t index) { + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void desktop_settings_scene_favorite_on_enter(void* context) { + DesktopSettingsApp* app = context; + Submenu* submenu = app->submenu; + submenu_reset(submenu); + + uint32_t favorite_id = + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); + uint32_t pre_select_item = PRESELECTED_SPECIAL; + FavoriteApp* curr_favorite_app = NULL; + bool is_dummy_app = false; + bool default_passport = false; + + if((favorite_id & SCENE_STATE_SET_DUMMY_APP) == 0) { + furi_assert(favorite_id < FavoriteAppNumber); + curr_favorite_app = &app->settings.favorite_apps[favorite_id]; + if(favorite_id == FavoriteAppRightShort) { + default_passport = true; + } + } else { + favorite_id &= ~(SCENE_STATE_SET_DUMMY_APP); + furi_assert(favorite_id < DummyAppNumber); + curr_favorite_app = &app->settings.dummy_apps[favorite_id]; + is_dummy_app = true; + default_passport = true; + } + + // Special case: Application browser + submenu_add_item( + submenu, + default_passport ? (PASSPORT_NAME) : (EXTERNAL_BROWSER_NAME), + DEFAULT_INDEX, + desktop_settings_scene_favorite_submenu_callback, + app); + + // Special case: Specific application + submenu_add_item( + submenu, + EXTERNAL_APPLICATION_NAME, + EXTERNAL_APPLICATION_INDEX, + desktop_settings_scene_favorite_submenu_callback, + app); + + if(!is_dummy_app) { + for(size_t i = 0; i < APPS_COUNT; i++) { + const char* name = favorite_fap_get_app_name(i); + + submenu_add_item( + submenu, name, i + 2, desktop_settings_scene_favorite_submenu_callback, app); + + // Select favorite item in submenu + if(!strcmp(name, curr_favorite_app->name_or_path)) { + pre_select_item = i + 2; + } + } + } + + if(pre_select_item == PRESELECTED_SPECIAL) { + if(curr_favorite_app->name_or_path[0] == '\0') { + pre_select_item = DEFAULT_INDEX; + } else { + pre_select_item = EXTERNAL_APPLICATION_INDEX; + } + } + + submenu_set_header(submenu, is_dummy_app ? ("Dummy Mode app:") : ("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); +} + +bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + FuriString* temp_path = furi_string_alloc_set_str(EXT_PATH("apps")); + + uint32_t favorite_id = + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); + FavoriteApp* curr_favorite_app = NULL; + if((favorite_id & SCENE_STATE_SET_DUMMY_APP) == 0) { + furi_assert(favorite_id < FavoriteAppNumber); + curr_favorite_app = &app->settings.favorite_apps[favorite_id]; + } else { + favorite_id &= ~(SCENE_STATE_SET_DUMMY_APP); + furi_assert(favorite_id < DummyAppNumber); + curr_favorite_app = &app->settings.dummy_apps[favorite_id]; + } + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DEFAULT_INDEX) { + curr_favorite_app->name_or_path[0] = '\0'; + consumed = true; + } else if(event.event == EXTERNAL_APPLICATION_INDEX) { + const DialogsFileBrowserOptions browser_options = { + .extension = ".fap", + .icon = &I_unknown_10px, + .skip_assets = true, + .hide_ext = true, + .item_loader_callback = favorite_fap_selector_item_callback, + .item_loader_context = app, + .base_path = EXT_PATH("apps"), + }; + + // Select favorite fap in file browser + if(favorite_fap_selector_file_exists(curr_favorite_app->name_or_path)) { + furi_string_set_str(temp_path, curr_favorite_app->name_or_path); + } + + if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) { + submenu_reset(app->submenu); // Prevent menu from being shown when we exiting scene + strncpy( + curr_favorite_app->name_or_path, + furi_string_get_cstr(temp_path), + MAX_APP_LENGTH); + consumed = true; + } + } else { + size_t app_index = event.event - 2; + const char* name = favorite_fap_get_app_name(app_index); + if(name) strncpy(curr_favorite_app->name_or_path, name, MAX_APP_LENGTH); + consumed = true; + } + if(consumed) { + scene_manager_previous_scene(app->scene_manager); + }; + consumed = true; + } + + furi_string_free(temp_path); + return consumed; +} + +void desktop_settings_scene_favorite_on_exit(void* context) { + DesktopSettingsApp* app = context; + DESKTOP_SETTINGS_SAVE(&app->settings); + submenu_reset(app->submenu); +} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_i.h b/applications/settings/desktop_settings/scenes/desktop_settings_scene_i.h new file mode 100644 index 00000000000..657680bc351 --- /dev/null +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_i.h @@ -0,0 +1,10 @@ +#pragma once + +#define SCENE_STATE_PIN_AUTH_DISABLE (0) +#define SCENE_STATE_PIN_AUTH_CHANGE_PIN (1) + +#define SCENE_STATE_PIN_ERROR_MISMATCH (0) +#define SCENE_STATE_PIN_ERROR_WRONG (1) + +#define SCENE_STATE_SET_FAVORITE_APP (0) +#define SCENE_STATE_SET_DUMMY_APP (1 << 8) diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_auth.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c similarity index 92% rename from applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_auth.c rename to applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c index c43fff6ad55..b73fe347b2d 100644 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_auth.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c @@ -1,10 +1,10 @@ #include #include #include -#include "../../helpers/pin_lock.h" +#include #include "../desktop_settings_app.h" -#include "desktop/desktop_settings/desktop_settings.h" -#include "desktop/views/desktop_view_pin_input.h" +#include +#include #include "desktop_settings_scene.h" #include "desktop_settings_scene_i.h" @@ -18,7 +18,7 @@ static void pin_auth_done_callback(const PinCode* pin_code, void* context) { DesktopSettingsApp* app = context; app->pincode_buffer = *pin_code; - if(desktop_pins_are_equal(&app->settings.pin_code, pin_code)) { + if(desktop_pin_compare(&app->settings.pin_code, pin_code)) { view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL); } else { view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT); @@ -33,7 +33,7 @@ static void pin_auth_back_callback(void* context) { void desktop_settings_scene_pin_auth_on_enter(void* context) { DesktopSettingsApp* app = context; - LOAD_DESKTOP_SETTINGS(&app->settings); + DESKTOP_SETTINGS_LOAD(&app->settings); furi_assert(app->settings.pin_code.length > 0); desktop_view_pin_input_set_context(app->pin_input_view, app); @@ -68,7 +68,7 @@ bool desktop_settings_scene_pin_auth_on_event(void* context, SceneManagerEvent e } else if(state == SCENE_STATE_PIN_AUTH_DISABLE) { scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinDisable); } else { - furi_assert(0); + furi_crash(); } consumed = true; break; diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_disable.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c similarity index 92% rename from applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_disable.c rename to applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c index f6f6b2562c4..7fbcc325219 100644 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_disable.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c @@ -4,8 +4,7 @@ #include #include "../desktop_settings_app.h" -#include "../desktop_settings.h" -#include "desktop/desktop_settings/desktop_settings.h" +#include #include "desktop_settings_scene.h" #define SCENE_EVENT_EXIT (0U) @@ -21,7 +20,7 @@ void desktop_settings_scene_pin_disable_on_enter(void* context) { DesktopSettingsApp* app = context; app->settings.pin_code.length = 0; memset(app->settings.pin_code.data, '0', sizeof(app->settings.pin_code.data)); - SAVE_DESKTOP_SETTINGS(&app->settings); + DESKTOP_SETTINGS_SAVE(&app->settings); popup_set_context(app->popup, app); popup_set_callback(app->popup, pin_disable_back_callback); diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_error.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_error.c similarity index 94% rename from applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_error.c rename to applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_error.c index 38852dd8bd8..1ba3c1b2daf 100644 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_error.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_error.c @@ -2,11 +2,11 @@ #include #include -#include "desktop/desktop_settings/desktop_settings.h" -#include "desktop/views/desktop_view_pin_input.h" +#include +#include #include "desktop_settings_scene.h" #include "desktop_settings_scene_i.h" -#include "../../helpers/pin_lock.h" +#include #include "../desktop_settings_app.h" #define SCENE_EVENT_EXIT (0U) @@ -39,7 +39,7 @@ void desktop_settings_scene_pin_error_on_enter(void* context) { } else if(state == SCENE_STATE_PIN_ERROR_WRONG) { desktop_view_pin_input_set_label_primary(app->pin_input_view, 35, 8, "Wrong PIN!"); } else { - furi_assert(0); + furi_crash(); } desktop_view_pin_input_set_label_secondary(app->pin_input_view, 0, 8, NULL); desktop_view_pin_input_set_label_button(app->pin_input_view, "Retry"); diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_menu.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c similarity index 100% rename from applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_menu.c rename to applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c similarity index 95% rename from applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup.c rename to applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c index b536a343a8f..1603aa33722 100644 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup.c @@ -3,11 +3,11 @@ #include #include "../desktop_settings_app.h" -#include "desktop/desktop_settings/desktop_settings.h" -#include "desktop/views/desktop_view_pin_input.h" +#include +#include #include "desktop_settings_scene.h" #include "desktop_settings_scene_i.h" -#include "../../helpers/pin_lock.h" +#include #define SCENE_EVENT_EXIT (0U) #define SCENE_EVENT_1ST_PIN_ENTERED (1U) @@ -25,7 +25,7 @@ static void pin_setup_done_callback(const PinCode* pin_code, void* context) { view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_1ST_PIN_ENTERED); } else { app->pincode_buffer_filled = false; - if(desktop_pins_are_equal(&app->pincode_buffer, pin_code)) { + if(desktop_pin_compare(&app->pincode_buffer, pin_code)) { view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL); } else { view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT); diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c similarity index 95% rename from applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c rename to applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c index 424084288dc..f6ceb96de99 100644 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c @@ -6,8 +6,8 @@ #include #include "../desktop_settings_app.h" -#include "desktop/desktop_settings/desktop_settings.h" -#include "desktop/views/desktop_view_pin_input.h" +#include +#include #include "desktop_settings_scene.h" #define SCENE_EVENT_DONE (0U) @@ -24,7 +24,7 @@ void desktop_settings_scene_pin_setup_done_on_enter(void* context) { DesktopSettingsApp* app = context; app->settings.pin_code = app->pincode_buffer; - SAVE_DESKTOP_SETTINGS(&app->settings); + DESKTOP_SETTINGS_SAVE(&app->settings); NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); notification_message(notification, &sequence_single_vibro); furi_record_close(RECORD_NOTIFICATION); diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto.c similarity index 94% rename from applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto.c rename to applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto.c index b8d630f2eed..31eec3871ad 100644 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto.c @@ -32,9 +32,7 @@ bool desktop_settings_scene_pin_setup_howto_on_event(void* context, SceneManager consumed = true; break; default: - furi_assert(0); - consumed = true; - break; + furi_crash(); } } return consumed; diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c similarity index 96% rename from applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c rename to applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c index 477d1f27a73..0ebf85c64b7 100644 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_howto2.c @@ -52,9 +52,7 @@ bool desktop_settings_scene_pin_setup_howto2_on_event(void* context, SceneManage break; } default: - furi_assert(0); - consumed = true; - break; + furi_crash(); } } return consumed; diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c new file mode 100644 index 00000000000..3e77bd8a164 --- /dev/null +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c @@ -0,0 +1,192 @@ +#include +#include + +#include "../desktop_settings_app.h" +#include "desktop_settings_scene.h" +#include "desktop_settings_scene_i.h" + +typedef enum { + DesktopSettingsPinSetup = 0, + DesktopSettingsAutoLockDelay, + DesktopSettingsClockDisplay, + DesktopSettingsFavoriteLeftShort, + DesktopSettingsFavoriteLeftLong, + DesktopSettingsFavoriteRightShort, + DesktopSettingsFavoriteRightLong, + DesktopSettingsDummyLeft, + DesktopSettingsDummyRight, + DesktopSettingsDummyDown, + DesktopSettingsDummyOk, +} DesktopSettingsEntry; + +#define AUTO_LOCK_DELAY_COUNT 6 +static const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = { + "OFF", + "30s", + "60s", + "2min", + "5min", + "10min", +}; +static const uint32_t auto_lock_delay_value[AUTO_LOCK_DELAY_COUNT] = + {0, 30000, 60000, 120000, 300000, 600000}; + +#define CLOCK_ENABLE_COUNT 2 +const char* const clock_enable_text[CLOCK_ENABLE_COUNT] = { + "OFF", + "ON", +}; + +const uint32_t clock_enable_value[CLOCK_ENABLE_COUNT] = {0, 1}; + +static void desktop_settings_scene_start_var_list_enter_callback(void* context, uint32_t index) { + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +static void desktop_settings_scene_start_clock_enable_changed(VariableItem* item) { + DesktopSettingsApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, clock_enable_text[index]); + app->settings.display_clock = index; +} + +static void desktop_settings_scene_start_auto_lock_delay_changed(VariableItem* item) { + DesktopSettingsApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, auto_lock_delay_text[index]); + app->settings.auto_lock_delay_ms = auto_lock_delay_value[index]; +} + +void desktop_settings_scene_start_on_enter(void* context) { + DesktopSettingsApp* app = context; + VariableItemList* variable_item_list = app->variable_item_list; + + VariableItem* item; + uint8_t value_index; + + variable_item_list_add(variable_item_list, "PIN Setup", 1, NULL, NULL); + + item = variable_item_list_add( + variable_item_list, + "Auto Lock Time", + AUTO_LOCK_DELAY_COUNT, + desktop_settings_scene_start_auto_lock_delay_changed, + app); + + value_index = value_index_uint32( + app->settings.auto_lock_delay_ms, auto_lock_delay_value, AUTO_LOCK_DELAY_COUNT); + 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, + "Show Clock", + CLOCK_ENABLE_COUNT, + desktop_settings_scene_start_clock_enable_changed, // + app); + + value_index = + value_index_uint32(app->settings.display_clock, clock_enable_value, CLOCK_ENABLE_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, clock_enable_text[value_index]); + + variable_item_list_add(variable_item_list, "Favorite App - Left Short", 1, NULL, NULL); + variable_item_list_add(variable_item_list, "Favorite App - Left Long", 1, NULL, NULL); + variable_item_list_add(variable_item_list, "Favorite App - Right Short", 1, NULL, NULL); + variable_item_list_add(variable_item_list, "Favorite App - Right Long", 1, NULL, NULL); + + variable_item_list_add(variable_item_list, "Dummy Mode App - Left", 1, NULL, NULL); + variable_item_list_add(variable_item_list, "Dummy Mode App - Right", 1, NULL, NULL); + variable_item_list_add(variable_item_list, "Dummy Mode App - Down", 1, NULL, NULL); + variable_item_list_add(variable_item_list, "Dummy Mode App - Ok", 1, NULL, NULL); + + 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); +} + +bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case DesktopSettingsPinSetup: + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinMenu); + break; + + case DesktopSettingsFavoriteLeftShort: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftShort); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + break; + case DesktopSettingsFavoriteLeftLong: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_FAVORITE_APP | FavoriteAppLeftLong); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + break; + case DesktopSettingsFavoriteRightShort: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightShort); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + break; + case DesktopSettingsFavoriteRightLong: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_FAVORITE_APP | FavoriteAppRightLong); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + break; + + case DesktopSettingsDummyLeft: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_DUMMY_APP | DummyAppLeft); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + break; + case DesktopSettingsDummyRight: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_DUMMY_APP | DummyAppRight); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + break; + case DesktopSettingsDummyDown: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_DUMMY_APP | DummyAppDown); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + break; + case DesktopSettingsDummyOk: + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneFavorite, + SCENE_STATE_SET_DUMMY_APP | DummyAppOk); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + break; + + default: + break; + } + consumed = true; + } + return consumed; +} + +void desktop_settings_scene_start_on_exit(void* context) { + DesktopSettingsApp* app = context; + variable_item_list_reset(app->variable_item_list); + DESKTOP_SETTINGS_SAVE(&app->settings); +} diff --git a/applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto.c b/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto.c similarity index 96% rename from applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto.c rename to applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto.c index 3831be8c404..26aa7c3e15a 100644 --- a/applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto.c +++ b/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto.c @@ -17,7 +17,7 @@ struct DesktopSettingsViewPinSetupHowto { static void desktop_settings_view_pin_setup_howto_draw(Canvas* canvas, void* model) { furi_assert(canvas); - furi_assert(model); + UNUSED(model); canvas_draw_icon(canvas, 16, 18, &I_Pin_attention_dpad_29x29); elements_button_right(canvas, "Next"); @@ -57,7 +57,6 @@ void desktop_settings_view_pin_setup_howto_set_callback( DesktopSettingsViewPinSetupHowto* desktop_settings_view_pin_setup_howto_alloc() { DesktopSettingsViewPinSetupHowto* view = malloc(sizeof(DesktopSettingsViewPinSetupHowto)); view->view = view_alloc(); - view_allocate_model(view->view, ViewModelTypeLockFree, 1); view_set_context(view->view, view); view_set_draw_callback(view->view, desktop_settings_view_pin_setup_howto_draw); view_set_input_callback(view->view, desktop_settings_view_pin_setup_howto_input); diff --git a/applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto.h b/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto.h similarity index 100% rename from applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto.h rename to applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto.h diff --git a/applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c b/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c similarity index 97% rename from applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c rename to applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c index ab1fa2383b8..c28826e628a 100644 --- a/applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c +++ b/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto2.c @@ -18,7 +18,7 @@ struct DesktopSettingsViewPinSetupHowto2 { static void desktop_settings_view_pin_setup_howto2_draw(Canvas* canvas, void* model) { furi_assert(canvas); - furi_assert(model); + UNUSED(model); canvas_set_font(canvas, FontSecondary); elements_multiline_text_aligned( @@ -79,7 +79,6 @@ void desktop_settings_view_pin_setup_howto2_set_ok_callback( DesktopSettingsViewPinSetupHowto2* desktop_settings_view_pin_setup_howto2_alloc() { DesktopSettingsViewPinSetupHowto2* view = malloc(sizeof(DesktopSettingsViewPinSetupHowto2)); view->view = view_alloc(); - view_allocate_model(view->view, ViewModelTypeLockFree, 1); view_set_context(view->view, view); view_set_draw_callback(view->view, desktop_settings_view_pin_setup_howto2_draw); view_set_input_callback(view->view, desktop_settings_view_pin_setup_howto2_input); diff --git a/applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto2.h b/applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto2.h similarity index 100% rename from applications/desktop/desktop_settings/views/desktop_settings_view_pin_setup_howto2.h rename to applications/settings/desktop_settings/views/desktop_settings_view_pin_setup_howto2.h diff --git a/applications/settings/dolphin_passport/application.fam b/applications/settings/dolphin_passport/application.fam new file mode 100644 index 00000000000..4167e9dcd59 --- /dev/null +++ b/applications/settings/dolphin_passport/application.fam @@ -0,0 +1,13 @@ +App( + appid="passport", + name="Passport", + apptype=FlipperAppType.SETTINGS, + entry_point="passport_app", + cdefines=["APP_PASSPORT"], + requires=[ + "gui", + "dolphin", + ], + stack_size=1 * 1024, + order=60, +) diff --git a/applications/dolphin/passport/passport.c b/applications/settings/dolphin_passport/passport.c similarity index 100% rename from applications/dolphin/passport/passport.c rename to applications/settings/dolphin_passport/passport.c diff --git a/applications/settings/notification_settings/application.fam b/applications/settings/notification_settings/application.fam new file mode 100644 index 00000000000..117a83870ed --- /dev/null +++ b/applications/settings/notification_settings/application.fam @@ -0,0 +1,9 @@ +App( + appid="notification_settings", + name="LCD and Notifications", + apptype=FlipperAppType.SETTINGS, + entry_point="notification_settings_app", + requires=["notification"], + stack_size=1 * 1024, + order=20, +) diff --git a/applications/settings/notification_settings/notification_settings_app.c b/applications/settings/notification_settings/notification_settings_app.c new file mode 100644 index 00000000000..450aaee144f --- /dev/null +++ b/applications/settings/notification_settings/notification_settings_app.c @@ -0,0 +1,258 @@ +#include +#include +#include +#include +#include + +#define MAX_NOTIFICATION_SETTINGS 4 + +typedef struct { + NotificationApp* notification; + Gui* gui; + ViewDispatcher* view_dispatcher; + VariableItemList* variable_item_list; +} NotificationAppSettings; + +static const NotificationSequence sequence_note_c = { + &message_note_c5, + &message_delay_100, + &message_sound_off, + NULL, +}; + +#define CONTRAST_COUNT 11 +const char* const contrast_text[CONTRAST_COUNT] = { + "-5", + "-4", + "-3", + "-2", + "-1", + "0", + "+1", + "+2", + "+3", + "+4", + "+5", +}; +const int32_t contrast_value[CONTRAST_COUNT] = { + -5, + -4, + -3, + -2, + -1, + 0, + 1, + 2, + 3, + 4, + 5, +}; + +#define BACKLIGHT_COUNT 5 +const char* const backlight_text[BACKLIGHT_COUNT] = { + "0%", + "25%", + "50%", + "75%", + "100%", +}; +const float backlight_value[BACKLIGHT_COUNT] = { + 0.0f, + 0.25f, + 0.5f, + 0.75f, + 1.0f, +}; + +#define VOLUME_COUNT 5 +const char* const volume_text[VOLUME_COUNT] = { + "0%", + "25%", + "50%", + "75%", + "100%", +}; +const float volume_value[VOLUME_COUNT] = {0.0f, 0.25f, 0.5f, 0.75f, 1.0f}; + +#define DELAY_COUNT 6 +const char* const delay_text[DELAY_COUNT] = { + "1s", + "5s", + "15s", + "30s", + "60s", + "120s", +}; +const uint32_t delay_value[DELAY_COUNT] = {1000, 5000, 15000, 30000, 60000, 120000}; + +#define VIBRO_COUNT 2 +const char* const vibro_text[VIBRO_COUNT] = { + "OFF", + "ON", +}; +const bool vibro_value[VIBRO_COUNT] = {false, true}; + +static void contrast_changed(VariableItem* item) { + NotificationAppSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, contrast_text[index]); + app->notification->settings.contrast = contrast_value[index]; + notification_message(app->notification, &sequence_lcd_contrast_update); +} + +static void backlight_changed(VariableItem* item) { + NotificationAppSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, backlight_text[index]); + app->notification->settings.display_brightness = backlight_value[index]; + notification_message(app->notification, &sequence_display_backlight_on); +} + +static void screen_changed(VariableItem* item) { + NotificationAppSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, delay_text[index]); + app->notification->settings.display_off_delay_ms = delay_value[index]; + notification_message(app->notification, &sequence_display_backlight_on); +} + +const NotificationMessage apply_message = { + .type = NotificationMessageTypeLedBrightnessSettingApply, +}; +const NotificationSequence apply_sequence = { + &apply_message, + NULL, +}; + +static void led_changed(VariableItem* item) { + NotificationAppSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, backlight_text[index]); + app->notification->settings.led_brightness = backlight_value[index]; + notification_message(app->notification, &apply_sequence); + notification_internal_message(app->notification, &apply_sequence); + notification_message(app->notification, &sequence_blink_white_100); +} + +static void volume_changed(VariableItem* item) { + NotificationAppSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, volume_text[index]); + app->notification->settings.speaker_volume = volume_value[index]; + notification_message(app->notification, &sequence_note_c); +} + +static void vibro_changed(VariableItem* item) { + NotificationAppSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, vibro_text[index]); + app->notification->settings.vibro_on = vibro_value[index]; + notification_message(app->notification, &sequence_single_vibro); +} + +static uint32_t notification_app_settings_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +static NotificationAppSettings* alloc_settings() { + NotificationAppSettings* app = malloc(sizeof(NotificationAppSettings)); + app->notification = furi_record_open(RECORD_NOTIFICATION); + app->gui = furi_record_open(RECORD_GUI); + + app->variable_item_list = variable_item_list_alloc(); + View* view = variable_item_list_get_view(app->variable_item_list); + view_set_previous_callback(view, notification_app_settings_exit); + + VariableItem* item; + uint8_t value_index; + + item = variable_item_list_add( + app->variable_item_list, "LCD Contrast", CONTRAST_COUNT, contrast_changed, app); + value_index = + value_index_int32(app->notification->settings.contrast, contrast_value, CONTRAST_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, contrast_text[value_index]); + + item = variable_item_list_add( + app->variable_item_list, "LCD Backlight", BACKLIGHT_COUNT, backlight_changed, app); + value_index = value_index_float( + app->notification->settings.display_brightness, backlight_value, BACKLIGHT_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, backlight_text[value_index]); + + item = variable_item_list_add( + app->variable_item_list, "Backlight Time", DELAY_COUNT, screen_changed, app); + value_index = value_index_uint32( + app->notification->settings.display_off_delay_ms, delay_value, DELAY_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, delay_text[value_index]); + + item = variable_item_list_add( + app->variable_item_list, "LED Brightness", BACKLIGHT_COUNT, led_changed, app); + value_index = value_index_float( + app->notification->settings.led_brightness, backlight_value, BACKLIGHT_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, backlight_text[value_index]); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)) { + item = variable_item_list_add(app->variable_item_list, "Volume", 1, NULL, app); + value_index = 0; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, "Stealth"); + } else { + item = variable_item_list_add( + app->variable_item_list, "Volume", VOLUME_COUNT, volume_changed, app); + value_index = value_index_float( + app->notification->settings.speaker_volume, volume_value, VOLUME_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, volume_text[value_index]); + } + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)) { + item = variable_item_list_add(app->variable_item_list, "Vibro", 1, NULL, app); + value_index = 0; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, "Stealth"); + } else { + item = variable_item_list_add( + app->variable_item_list, "Vibro", VIBRO_COUNT, vibro_changed, app); + value_index = + value_index_bool(app->notification->settings.vibro_on, vibro_value, VIBRO_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, vibro_text[value_index]); + } + + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_add_view(app->view_dispatcher, 0, view); + view_dispatcher_switch_to_view(app->view_dispatcher, 0); + + return app; +} + +static void free_settings(NotificationAppSettings* app) { + view_dispatcher_remove_view(app->view_dispatcher, 0); + variable_item_list_free(app->variable_item_list); + view_dispatcher_free(app->view_dispatcher); + + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + free(app); +} + +int32_t notification_settings_app(void* p) { + UNUSED(p); + NotificationAppSettings* app = alloc_settings(); + view_dispatcher_run(app->view_dispatcher); + notification_message_save_settings(app->notification); + free_settings(app); + return 0; +} diff --git a/applications/settings/power_settings_app/application.fam b/applications/settings/power_settings_app/application.fam new file mode 100644 index 00000000000..6f3589e1a84 --- /dev/null +++ b/applications/settings/power_settings_app/application.fam @@ -0,0 +1,14 @@ +App( + appid="power_settings", + name="Power", + apptype=FlipperAppType.SETTINGS, + entry_point="power_settings_app", + requires=[ + "gui", + "power", + "locale", + ], + flags=["InsomniaSafe"], + stack_size=1 * 1024, + order=40, +) diff --git a/applications/power/power_settings_app/power_settings_app.c b/applications/settings/power_settings_app/power_settings_app.c similarity index 100% rename from applications/power/power_settings_app/power_settings_app.c rename to applications/settings/power_settings_app/power_settings_app.c diff --git a/applications/power/power_settings_app/power_settings_app.h b/applications/settings/power_settings_app/power_settings_app.h similarity index 96% rename from applications/power/power_settings_app/power_settings_app.h rename to applications/settings/power_settings_app/power_settings_app.h index 8429b54b4bb..cd05846c055 100644 --- a/applications/power/power_settings_app/power_settings_app.h +++ b/applications/settings/power_settings_app/power_settings_app.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "views/battery_info.h" #include diff --git a/applications/power/power_settings_app/scenes/power_settings_scene.c b/applications/settings/power_settings_app/scenes/power_settings_scene.c similarity index 100% rename from applications/power/power_settings_app/scenes/power_settings_scene.c rename to applications/settings/power_settings_app/scenes/power_settings_scene.c diff --git a/applications/power/power_settings_app/scenes/power_settings_scene.h b/applications/settings/power_settings_app/scenes/power_settings_scene.h similarity index 100% rename from applications/power/power_settings_app/scenes/power_settings_scene.h rename to applications/settings/power_settings_app/scenes/power_settings_scene.h diff --git a/applications/power/power_settings_app/scenes/power_settings_scene_battery_info.c b/applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c old mode 100755 new mode 100644 similarity index 94% rename from applications/power/power_settings_app/scenes/power_settings_scene_battery_info.c rename to applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c index 0085c31dce2..5181c93f7bc --- a/applications/power/power_settings_app/scenes/power_settings_scene_battery_info.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_battery_info.c @@ -7,6 +7,7 @@ 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, + .charge_voltage_limit = app->info.voltage_battery_charge_limit, .charge = app->info.charge, .health = app->info.health, }; diff --git a/applications/power/power_settings_app/scenes/power_settings_scene_config.h b/applications/settings/power_settings_app/scenes/power_settings_scene_config.h old mode 100755 new mode 100644 similarity index 100% rename from applications/power/power_settings_app/scenes/power_settings_scene_config.h rename to applications/settings/power_settings_app/scenes/power_settings_scene_config.h diff --git a/applications/power/power_settings_app/scenes/power_settings_scene_power_off.c b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c similarity index 100% rename from applications/power/power_settings_app/scenes/power_settings_scene_power_off.c rename to applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c diff --git a/applications/power/power_settings_app/scenes/power_settings_scene_reboot.c b/applications/settings/power_settings_app/scenes/power_settings_scene_reboot.c old mode 100755 new mode 100644 similarity index 100% rename from applications/power/power_settings_app/scenes/power_settings_scene_reboot.c rename to applications/settings/power_settings_app/scenes/power_settings_scene_reboot.c diff --git a/applications/power/power_settings_app/scenes/power_settings_scene_start.c b/applications/settings/power_settings_app/scenes/power_settings_scene_start.c old mode 100755 new mode 100644 similarity index 100% rename from applications/power/power_settings_app/scenes/power_settings_scene_start.c rename to applications/settings/power_settings_app/scenes/power_settings_scene_start.c diff --git a/applications/settings/power_settings_app/views/battery_info.c b/applications/settings/power_settings_app/views/battery_info.c new file mode 100644 index 00000000000..8add60db5ab --- /dev/null +++ b/applications/settings/power_settings_app/views/battery_info.c @@ -0,0 +1,159 @@ +#include "battery_info.h" +#include +#include +#include +#include + +#define LOW_CHARGE_THRESHOLD (10) +#define HIGH_DRAIN_CURRENT_THRESHOLD (-100) + +struct BatteryInfo { + View* view; +}; + +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); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, x - 4, y + 16, 24, 6); + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val); +}; + +static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { + char emote[20] = {}; + char header[20] = {}; + char value[20] = {}; + + int32_t current = 1000.0f * data->gauge_current; + + // Draw battery + canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28); + if(current > 0) { + canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14); + } else if(current < HIGH_DRAIN_CURRENT_THRESHOLD) { + canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14); + } else if(data->charge < LOW_CHARGE_THRESHOLD) { + canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14); + } else { + canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNormal_29x14); + } + + // Draw bubble + elements_bubble(canvas, 53, 0, 71, 39); + + // Set text + if(current > 0) { + snprintf(emote, sizeof(emote), "%s", "Yummy!"); + snprintf(header, sizeof(header), "%s", "Charging at"); + snprintf( + value, + sizeof(value), + "%lu.%luV %lumA", + (uint32_t)(data->vbus_voltage), + (uint32_t)(data->vbus_voltage * 10) % 10, + current); + } else if(current < -5) { + // 0-5ma deadband + snprintf( + emote, + sizeof(emote), + "%s", + current < HIGH_DRAIN_CURRENT_THRESHOLD ? "Oh no!" : "Om-nom-nom!"); + snprintf(header, sizeof(header), "%s", "Consumption is"); + snprintf( + value, + sizeof(value), + "%ld %s", + ABS(current), + current < HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); + } else if(data->vbus_voltage > 0) { + 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"); + snprintf( + value, + sizeof(value), + "%lu.%luV", + (uint32_t)(data->charge_voltage_limit), + (uint32_t)(data->charge_voltage_limit * 10) % 10); + } else { + snprintf(header, sizeof(header), "Charged!"); + } + } else { + snprintf(header, sizeof(header), "Napping..."); + } + + canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote); + canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header); + canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value); +}; + +static void battery_info_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + BatteryInfoModel* model = context; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + draw_battery(canvas, model, 0, 5); + + char batt_level[10]; + char temperature[10]; + char voltage[10]; + char health[10]; + + snprintf(batt_level, sizeof(batt_level), "%lu%%", (uint32_t)model->charge); + if(locale_get_measurement_unit() == LocaleMeasurementUnitsMetric) { + snprintf(temperature, sizeof(temperature), "%lu C", (uint32_t)model->gauge_temperature); + } else { + snprintf( + temperature, + sizeof(temperature), + "%lu F", + (uint32_t)locale_celsius_to_fahrenheit(model->gauge_temperature)); + } + snprintf( + voltage, + sizeof(voltage), + "%lu.%01lu V", + (uint32_t)model->gauge_voltage, + (uint32_t)(model->gauge_voltage * 10) % 10UL); + snprintf(health, sizeof(health), "%d%%", model->health); + + draw_stat(canvas, 8, 42, &I_Battery_16x16, batt_level); + draw_stat(canvas, 40, 42, &I_Temperature_16x16, temperature); + draw_stat(canvas, 72, 42, &I_Voltage_16x16, voltage); + draw_stat(canvas, 104, 42, &I_Health_16x16, health); +} + +BatteryInfo* battery_info_alloc() { + BatteryInfo* battery_info = malloc(sizeof(BatteryInfo)); + battery_info->view = view_alloc(); + view_set_context(battery_info->view, battery_info); + view_allocate_model(battery_info->view, ViewModelTypeLocking, sizeof(BatteryInfoModel)); + view_set_draw_callback(battery_info->view, battery_info_draw_callback); + + return battery_info; +} + +void battery_info_free(BatteryInfo* battery_info) { + furi_assert(battery_info); + view_free(battery_info->view); + free(battery_info); +} + +View* battery_info_get_view(BatteryInfo* battery_info) { + furi_assert(battery_info); + return battery_info->view; +} + +void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data) { + furi_assert(battery_info); + furi_assert(data); + with_view_model( + battery_info->view, + BatteryInfoModel * model, + { memcpy(model, data, sizeof(BatteryInfoModel)); }, + true); +} diff --git a/applications/settings/power_settings_app/views/battery_info.h b/applications/settings/power_settings_app/views/battery_info.h new file mode 100644 index 00000000000..e52d1844c8d --- /dev/null +++ b/applications/settings/power_settings_app/views/battery_info.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +typedef struct BatteryInfo BatteryInfo; + +typedef struct { + float vbus_voltage; + float gauge_voltage; + float gauge_current; + float gauge_temperature; + float charge_voltage_limit; + uint8_t charge; + uint8_t health; +} BatteryInfoModel; + +BatteryInfo* battery_info_alloc(); + +void battery_info_free(BatteryInfo* battery_info); + +View* battery_info_get_view(BatteryInfo* battery_info); + +void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data); diff --git a/applications/storage_settings/application.fam b/applications/settings/storage_settings/application.fam similarity index 100% rename from applications/storage_settings/application.fam rename to applications/settings/storage_settings/application.fam diff --git a/applications/storage_settings/scenes/storage_settings_scene.c b/applications/settings/storage_settings/scenes/storage_settings_scene.c similarity index 100% rename from applications/storage_settings/scenes/storage_settings_scene.c rename to applications/settings/storage_settings/scenes/storage_settings_scene.c diff --git a/applications/storage_settings/scenes/storage_settings_scene.h b/applications/settings/storage_settings/scenes/storage_settings_scene.h similarity index 100% rename from applications/storage_settings/scenes/storage_settings_scene.h rename to applications/settings/storage_settings/scenes/storage_settings_scene.h diff --git a/applications/storage_settings/scenes/storage_settings_scene_benchmark.c b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c similarity index 87% rename from applications/storage_settings/scenes/storage_settings_scene_benchmark.c rename to applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c index ddeea4eba02..a5bf1b9d376 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_benchmark.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_benchmark.c @@ -44,7 +44,7 @@ static bool storage_settings_scene_bench_write( } static bool - storage_settings_scene_bench_read(Storage* api, uint16_t size, uint8_t* data, uint32_t* speed) { + storage_settings_scene_bench_read(Storage* api, size_t size, uint8_t* data, uint32_t* speed) { File* file = storage_file_alloc(api); bool result = true; *speed = -1; @@ -82,7 +82,7 @@ static void storage_settings_scene_benchmark(StorageSettings* app) { bench_data[i] = (uint8_t)i; } - uint16_t bench_size[BENCH_COUNT] = {1, 8, 32, 256, 512, 1024}; + size_t bench_size[BENCH_COUNT] = {1, 8, 32, 256, 512, 1024}; uint32_t bench_w_speed[BENCH_COUNT] = {0, 0, 0, 0, 0, 0}; uint32_t bench_r_speed[BENCH_COUNT] = {0, 0, 0, 0, 0, 0}; @@ -92,19 +92,22 @@ static void storage_settings_scene_benchmark(StorageSettings* app) { app->fs_api, bench_size[i], bench_data, &bench_w_speed[i])) break; - if(i > 0) string_cat_printf(app->text_string, "\n"); - string_cat_printf(app->text_string, "%ub : W %luK ", bench_size[i], bench_w_speed[i]); + if(i > 0) furi_string_cat_printf(app->text_string, "\n"); + furi_string_cat_printf(app->text_string, "%ub : W %luK ", bench_size[i], bench_w_speed[i]); dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter); dialog_ex_set_text( - dialog_ex, string_get_cstr(app->text_string), 0, 32, AlignLeft, AlignCenter); + dialog_ex, furi_string_get_cstr(app->text_string), 0, 32, AlignLeft, AlignCenter); if(!storage_settings_scene_bench_read( app->fs_api, bench_size[i], bench_data, &bench_r_speed[i])) break; - string_cat_printf(app->text_string, "R %luK", bench_r_speed[i]); + 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, string_get_cstr(app->text_string), 0, 32, AlignLeft, AlignCenter); + dialog_ex, furi_string_get_cstr(app->text_string), 0, 32, AlignLeft, AlignCenter); } free(bench_data); @@ -159,5 +162,5 @@ void storage_settings_scene_benchmark_on_exit(void* context) { dialog_ex_reset(dialog_ex); - string_reset(app->text_string); + furi_string_reset(app->text_string); } diff --git a/applications/storage_settings/scenes/storage_settings_scene_config.h b/applications/settings/storage_settings/scenes/storage_settings_scene_config.h similarity index 100% rename from applications/storage_settings/scenes/storage_settings_scene_config.h rename to applications/settings/storage_settings/scenes/storage_settings_scene_config.h diff --git a/applications/storage_settings/scenes/storage_settings_scene_factory_reset.c b/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c similarity index 92% rename from applications/storage_settings/scenes/storage_settings_scene_factory_reset.c rename to applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c index a69479681ea..865ee48d4b5 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_factory_reset.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_factory_reset.c @@ -24,7 +24,7 @@ void storage_settings_scene_factory_reset_on_enter(void* context) { dialog_ex_set_header(dialog_ex, "Confirm Factory Reset", 64, 10, AlignCenter, AlignCenter); dialog_ex_set_text( dialog_ex, - "Internal storage will be erased\r\nData and setting will be lost!", + "Internal storage will be erased\r\nData and settings will be lost!", 64, 32, AlignCenter, @@ -49,13 +49,13 @@ bool storage_settings_scene_factory_reset_on_event(void* context, SceneManagerEv case DialogExResultRight: counter++; if(counter < STORAGE_SETTINGS_SCENE_FACTORY_RESET_CONFIRM_COUNT) { - string_printf( + furi_string_printf( app->text_string, "%ld presses left", STORAGE_SETTINGS_SCENE_FACTORY_RESET_CONFIRM_COUNT - counter); dialog_ex_set_text( app->dialog_ex, - string_get_cstr(app->text_string), + furi_string_get_cstr(app->text_string), 64, 32, AlignCenter, @@ -83,5 +83,5 @@ void storage_settings_scene_factory_reset_on_exit(void* context) { dialog_ex_reset(dialog_ex); - string_reset(app->text_string); + furi_string_reset(app->text_string); } diff --git a/applications/storage_settings/scenes/storage_settings_scene_format_confirm.c b/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c similarity index 96% rename from applications/storage_settings/scenes/storage_settings_scene_format_confirm.c rename to applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c index 261ef1997d0..8af065bf8bb 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_format_confirm.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_format_confirm.c @@ -40,8 +40,6 @@ bool storage_settings_scene_format_confirm_on_event(void* context, SceneManagerE if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case DialogExResultLeft: - consumed = scene_manager_previous_scene(app->scene_manager); - break; case DialogExResultCenter: consumed = scene_manager_previous_scene(app->scene_manager); break; diff --git a/applications/storage_settings/scenes/storage_settings_scene_formatting.c b/applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c old mode 100755 new mode 100644 similarity index 100% rename from applications/storage_settings/scenes/storage_settings_scene_formatting.c rename to applications/settings/storage_settings/scenes/storage_settings_scene_formatting.c diff --git a/applications/storage_settings/scenes/storage_settings_scene_internal_info.c b/applications/settings/storage_settings/scenes/storage_settings_scene_internal_info.c similarity index 89% rename from applications/storage_settings/scenes/storage_settings_scene_internal_info.c rename to applications/settings/storage_settings/scenes/storage_settings_scene_internal_info.c index 76c7fd0eca9..f205efc0aee 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_internal_info.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_internal_info.c @@ -25,14 +25,14 @@ void storage_settings_scene_internal_info_on_enter(void* context) { dialog_ex_set_text( dialog_ex, storage_error_get_desc(error), 64, 32, AlignCenter, AlignCenter); } else { - string_printf( + furi_string_printf( app->text_string, - "Label: %s\nType: LittleFS\n%lu KB total\n%lu KB free", + "Label: %s\nType: LittleFS\n%lu KiB total\n%lu KiB free", furi_hal_version_get_name_ptr() ? furi_hal_version_get_name_ptr() : "Unknown", (uint32_t)(total_space / 1024), (uint32_t)(free_space / 1024)); dialog_ex_set_text( - dialog_ex, string_get_cstr(app->text_string), 4, 4, AlignLeft, AlignTop); + dialog_ex, furi_string_get_cstr(app->text_string), 4, 4, AlignLeft, AlignTop); } view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx); @@ -58,5 +58,5 @@ void storage_settings_scene_internal_info_on_exit(void* context) { dialog_ex_reset(dialog_ex); - string_reset(app->text_string); + furi_string_reset(app->text_string); } diff --git a/applications/storage_settings/scenes/storage_settings_scene_sd_info.c b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c similarity index 79% rename from applications/storage_settings/scenes/storage_settings_scene_sd_info.c rename to applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c index cfb4f310dc5..81c786d0cb0 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_sd_info.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c @@ -12,6 +12,7 @@ void storage_settings_scene_sd_info_on_enter(void* context) { SDInfo sd_info; FS_Error sd_status = storage_sd_info(app->fs_api, &sd_info); + scene_manager_set_scene_state(app->scene_manager, StorageSettingsSDInfo, sd_status); dialog_ex_set_context(dialog_ex, app); @@ -24,15 +25,24 @@ 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 { - string_printf( + furi_string_printf( app->text_string, - "Label: %s\nType: %s\n%lu KB total\n%lu KB free", + "Label: %s\nType: %s\n%lu KiB total\n%lu KiB free\n" + "%02X%s %s v%i.%i\nSN:%04lX %02i/%i", sd_info.label, sd_api_get_fs_type_text(sd_info.fs_type), sd_info.kb_total, - sd_info.kb_free); + sd_info.kb_free, + 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, string_get_cstr(app->text_string), 4, 4, AlignLeft, AlignTop); + dialog_ex, furi_string_get_cstr(app->text_string), 4, 1, AlignLeft, AlignTop); } view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx); @@ -47,8 +57,6 @@ bool storage_settings_scene_sd_info_on_event(void* context, SceneManagerEvent ev if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case DialogExResultLeft: - consumed = scene_manager_previous_scene(app->scene_manager); - break; case DialogExResultCenter: consumed = scene_manager_previous_scene(app->scene_manager); break; @@ -70,5 +78,5 @@ void storage_settings_scene_sd_info_on_exit(void* context) { dialog_ex_reset(dialog_ex); - string_reset(app->text_string); + furi_string_reset(app->text_string); } diff --git a/applications/storage_settings/scenes/storage_settings_scene_start.c b/applications/settings/storage_settings/scenes/storage_settings_scene_start.c similarity index 89% rename from applications/storage_settings/scenes/storage_settings_scene_start.c rename to applications/settings/storage_settings/scenes/storage_settings_scene_start.c index 9f41061b893..0e667024f17 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_start.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_start.c @@ -31,12 +31,24 @@ void storage_settings_scene_start_on_enter(void* context) { StorageSettingsStartSubmenuIndexSDInfo, storage_settings_scene_start_submenu_callback, app); - submenu_add_item( - submenu, - "Unmount SD Card", - StorageSettingsStartSubmenuIndexUnmount, - storage_settings_scene_start_submenu_callback, - app); + + FS_Error sd_status = storage_sd_status(app->fs_api); + if(sd_status != FSE_OK) { + submenu_add_item( + submenu, + "Mount SD Card", + StorageSettingsStartSubmenuIndexUnmount, + storage_settings_scene_start_submenu_callback, + app); + } else { + submenu_add_item( + submenu, + "Unmount SD Card", + StorageSettingsStartSubmenuIndexUnmount, + storage_settings_scene_start_submenu_callback, + app); + } + submenu_add_item( submenu, "Format SD Card", diff --git a/applications/storage_settings/scenes/storage_settings_scene_unmount_confirm.c b/applications/settings/storage_settings/scenes/storage_settings_scene_unmount_confirm.c similarity index 83% rename from applications/storage_settings/scenes/storage_settings_scene_unmount_confirm.c rename to applications/settings/storage_settings/scenes/storage_settings_scene_unmount_confirm.c index 2b485b7f719..1b9970f9f60 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_unmount_confirm.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_unmount_confirm.c @@ -12,13 +12,17 @@ void storage_settings_scene_unmount_confirm_on_enter(void* context) { DialogEx* dialog_ex = app->dialog_ex; FS_Error sd_status = storage_sd_status(app->fs_api); - if(sd_status == FSE_NOT_READY) { - dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48); - dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Mount SD Card?", 64, 10, AlignCenter, AlignCenter); dialog_ex_set_text( - dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); - dialog_ex_set_center_button_text(dialog_ex, "Ok"); + dialog_ex, + "This may turn off power\nfor external modules", + 64, + 32, + AlignCenter, + AlignCenter); + dialog_ex_set_left_button_text(dialog_ex, "Cancel"); + dialog_ex_set_right_button_text(dialog_ex, "Mount"); } else { dialog_ex_set_header(dialog_ex, "Unmount SD Card?", 64, 10, AlignCenter, AlignCenter); dialog_ex_set_text( @@ -41,8 +45,6 @@ bool storage_settings_scene_unmount_confirm_on_event(void* context, SceneManager if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case DialogExResultCenter: - consumed = scene_manager_previous_scene(app->scene_manager); - break; case DialogExResultLeft: consumed = scene_manager_previous_scene(app->scene_manager); break; diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c b/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c new file mode 100644 index 00000000000..33bb9552297 --- /dev/null +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c @@ -0,0 +1,76 @@ +#include "../storage_settings.h" + +static void + storage_settings_scene_unmounted_dialog_callback(DialogExResult result, void* context) { + StorageSettings* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, result); +} + +void storage_settings_scene_unmounted_on_enter(void* context) { + StorageSettings* app = context; + DialogEx* dialog_ex = app->dialog_ex; + + FS_Error sd_status = storage_sd_status(app->fs_api); + if(sd_status == FSE_NOT_READY) { + FS_Error error = storage_sd_mount(app->fs_api); + if(error == FSE_OK) { + dialog_ex_set_header(dialog_ex, "SD Card Mounted", 64, 3, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog_ex, "Flipper can use\nSD card now.", 3, 22, AlignLeft, AlignTop); + notification_message(app->notification, &sequence_blink_green_100); + } else { + dialog_ex_set_header(dialog_ex, "Cannot Mount SD Card", 64, 3, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog_ex, storage_error_get_desc(error), 3, 22, AlignLeft, AlignTop); + notification_message(app->notification, &sequence_blink_red_100); + } + } else { + FS_Error error = storage_sd_unmount(app->fs_api); + if(error == FSE_OK) { + dialog_ex_set_header(dialog_ex, "SD Card Unmounted", 64, 3, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog_ex, "You can remove\nSD card now.", 3, 22, AlignLeft, AlignTop); + notification_message(app->notification, &sequence_blink_green_100); + } else { + dialog_ex_set_header( + dialog_ex, "Cannot Unmount SD Card", 64, 3, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog_ex, storage_error_get_desc(error), 3, 22, AlignLeft, AlignTop); + notification_message(app->notification, &sequence_blink_red_100); + } + } + + dialog_ex_set_center_button_text(dialog_ex, "OK"); + dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48); + + dialog_ex_set_context(dialog_ex, app); + dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_unmounted_dialog_callback); + + view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx); +} + +bool storage_settings_scene_unmounted_on_event(void* context, SceneManagerEvent event) { + StorageSettings* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case DialogExResultCenter: + consumed = scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, StorageSettingsStart); + break; + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = true; + } + + return consumed; +} + +void storage_settings_scene_unmounted_on_exit(void* context) { + StorageSettings* app = context; + DialogEx* dialog_ex = app->dialog_ex; + + dialog_ex_reset(dialog_ex); +} diff --git a/applications/storage_settings/storage_settings.c b/applications/settings/storage_settings/storage_settings.c similarity index 96% rename from applications/storage_settings/storage_settings.c rename to applications/settings/storage_settings/storage_settings.c index f580e636917..77a8f0f2269 100644 --- a/applications/storage_settings/storage_settings.c +++ b/applications/settings/storage_settings/storage_settings.c @@ -21,7 +21,7 @@ static StorageSettings* storage_settings_alloc() { app->view_dispatcher = view_dispatcher_alloc(); app->scene_manager = scene_manager_alloc(&storage_settings_scene_handlers, app); - string_init(app->text_string); + app->text_string = furi_string_alloc(); view_dispatcher_enable_queue(app->view_dispatcher); view_dispatcher_set_event_callback_context(app->view_dispatcher, app); @@ -60,7 +60,7 @@ static void storage_settings_free(StorageSettings* app) { furi_record_close(RECORD_STORAGE); furi_record_close(RECORD_NOTIFICATION); - string_clear(app->text_string); + furi_string_free(app->text_string); free(app); } diff --git a/applications/storage_settings/storage_settings.h b/applications/settings/storage_settings/storage_settings.h similarity index 91% rename from applications/storage_settings/storage_settings.h rename to applications/settings/storage_settings/storage_settings.h index f2d071c47c5..fd841623e68 100644 --- a/applications/storage_settings/storage_settings.h +++ b/applications/settings/storage_settings/storage_settings.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -25,7 +26,7 @@ typedef struct { NotificationApp* notification; Storage* fs_api; - // view managment + // view management SceneManager* scene_manager; ViewDispatcher* view_dispatcher; @@ -34,7 +35,7 @@ typedef struct { DialogEx* dialog_ex; // text - string_t text_string; + FuriString* text_string; } StorageSettings; typedef enum { diff --git a/applications/settings/system/application.fam b/applications/settings/system/application.fam new file mode 100644 index 00000000000..69a8f1239f9 --- /dev/null +++ b/applications/settings/system/application.fam @@ -0,0 +1,9 @@ +App( + appid="system_settings", + name="System", + apptype=FlipperAppType.SETTINGS, + entry_point="system_settings_app", + requires=["gui", "locale"], + stack_size=1 * 1024, + order=70, +) diff --git a/applications/settings/system/system_settings.c b/applications/settings/system/system_settings.c new file mode 100644 index 00000000000..d19b4747b9c --- /dev/null +++ b/applications/settings/system/system_settings.c @@ -0,0 +1,290 @@ +#include "system_settings.h" +#include +#include +#include + +const char* const log_level_text[] = { + "Default", + "None", + "Error", + "Warning", + "Info", + "Debug", + "Trace", +}; + +const uint32_t log_level_value[] = { + FuriLogLevelDefault, + FuriLogLevelNone, + FuriLogLevelError, + FuriLogLevelWarn, + FuriLogLevelInfo, + FuriLogLevelDebug, + FuriLogLevelTrace, +}; + +static void log_level_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, log_level_text[index]); + furi_hal_rtc_set_log_level(log_level_value[index]); +} + +const char* const debug_text[] = { + "OFF", + "ON", +}; + +static void debug_changed(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, debug_text[index]); + if(index) { + furi_hal_rtc_set_flag(FuriHalRtcFlagDebug); + } else { + furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); + } +} + +const char* const heap_trace_mode_text[] = { + "None", + "Main", +#if FURI_DEBUG + "Tree", + "All", +#endif +}; + +const uint32_t heap_trace_mode_value[] = { + FuriHalRtcHeapTrackModeNone, + FuriHalRtcHeapTrackModeMain, +#if FURI_DEBUG + FuriHalRtcHeapTrackModeTree, + FuriHalRtcHeapTrackModeAll, +#endif +}; + +static void heap_trace_mode_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, heap_trace_mode_text[index]); + furi_hal_rtc_set_heap_track_mode(heap_trace_mode_value[index]); +} + +const char* const mesurement_units_text[] = { + "Metric", + "Imperial", +}; + +const uint32_t mesurement_units_value[] = { + LocaleMeasurementUnitsMetric, + LocaleMeasurementUnitsImperial, +}; + +static void mesurement_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]); +} + +const char* const time_format_text[] = { + "24h", + "12h", +}; + +const uint32_t time_format_value[] = { + LocaleTimeFormat24h, + LocaleTimeFormat12h, +}; + +static void time_format_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, time_format_text[index]); + locale_set_time_format(time_format_value[index]); +} + +const char* const date_format_text[] = { + "D/M/Y", + "M/D/Y", + "Y/M/D", +}; + +const uint32_t date_format_value[] = { + LocaleDateFormatDMY, + LocaleDateFormatMDY, + LocaleDateFormatYMD, +}; + +static void date_format_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, date_format_text[index]); + locale_set_date_format(date_format_value[index]); +} + +const char* const hand_mode[] = { + "Righty", + "Lefty", +}; + +static void hand_orient_changed(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, hand_mode[index]); + if(index) { + furi_hal_rtc_set_flag(FuriHalRtcFlagHandOrient); + } else { + furi_hal_rtc_reset_flag(FuriHalRtcFlagHandOrient); + } +} + +const char* const sleep_method[] = { + "Default", + "Legacy", +}; + +static void sleep_method_changed(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, sleep_method[index]); + if(index) { + furi_hal_rtc_set_flag(FuriHalRtcFlagLegacySleep); + } else { + furi_hal_rtc_reset_flag(FuriHalRtcFlagLegacySleep); + } +} + +const char* const filename_scheme[] = { + "Default", + "Detailed", +}; + +static void filename_scheme_changed(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, filename_scheme[index]); + if(index) { + furi_hal_rtc_set_flag(FuriHalRtcFlagDetailedFilename); + } else { + furi_hal_rtc_reset_flag(FuriHalRtcFlagDetailedFilename); + } +} + +static uint32_t system_settings_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +SystemSettings* system_settings_alloc() { + SystemSettings* app = malloc(sizeof(SystemSettings)); + + // Load settings + app->gui = furi_record_open(RECORD_GUI); + + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + VariableItem* item; + uint8_t value_index; + app->var_item_list = variable_item_list_alloc(); + + item = variable_item_list_add( + app->var_item_list, "Hand Orient", COUNT_OF(hand_mode), hand_orient_changed, app); + value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient) ? 1 : 0; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, hand_mode[value_index]); + + item = variable_item_list_add( + app->var_item_list, + "Units", + COUNT_OF(mesurement_units_text), + mesurement_units_changed, + app); + value_index = value_index_uint32( + locale_get_measurement_unit(), mesurement_units_value, COUNT_OF(mesurement_units_value)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, mesurement_units_text[value_index]); + + item = variable_item_list_add( + app->var_item_list, "Time Format", COUNT_OF(time_format_text), time_format_changed, app); + value_index = value_index_uint32( + locale_get_time_format(), time_format_value, COUNT_OF(time_format_value)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, time_format_text[value_index]); + + item = variable_item_list_add( + app->var_item_list, "Date Format", COUNT_OF(date_format_text), date_format_changed, app); + value_index = value_index_uint32( + locale_get_date_format(), date_format_value, COUNT_OF(date_format_value)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, date_format_text[value_index]); + + item = variable_item_list_add( + app->var_item_list, "Log Level", COUNT_OF(log_level_text), log_level_changed, app); + value_index = value_index_uint32( + furi_hal_rtc_get_log_level(), log_level_value, COUNT_OF(log_level_text)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, log_level_text[value_index]); + + item = variable_item_list_add( + app->var_item_list, "Debug", COUNT_OF(debug_text), debug_changed, app); + value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) ? 1 : 0; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, debug_text[value_index]); + + item = variable_item_list_add( + app->var_item_list, + "Heap Trace", + COUNT_OF(heap_trace_mode_text), + heap_trace_mode_changed, + app); + value_index = value_index_uint32( + furi_hal_rtc_get_heap_track_mode(), heap_trace_mode_value, COUNT_OF(heap_trace_mode_text)); + furi_hal_rtc_set_heap_track_mode(heap_trace_mode_value[value_index]); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, heap_trace_mode_text[value_index]); + + item = variable_item_list_add( + app->var_item_list, "Sleep Method", COUNT_OF(sleep_method), sleep_method_changed, app); + value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagLegacySleep) ? 1 : 0; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, sleep_method[value_index]); + + item = variable_item_list_add( + app->var_item_list, "File Naming", COUNT_OF(filename_scheme), filename_scheme_changed, app); + value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDetailedFilename) ? 1 : 0; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, filename_scheme[value_index]); + + view_set_previous_callback( + variable_item_list_get_view(app->var_item_list), system_settings_exit); + view_dispatcher_add_view( + app->view_dispatcher, + SystemSettingsViewVarItemList, + variable_item_list_get_view(app->var_item_list)); + + view_dispatcher_switch_to_view(app->view_dispatcher, SystemSettingsViewVarItemList); + + return app; +} + +void system_settings_free(SystemSettings* app) { + furi_assert(app); + // Variable item list + view_dispatcher_remove_view(app->view_dispatcher, SystemSettingsViewVarItemList); + variable_item_list_free(app->var_item_list); + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + // Records + furi_record_close(RECORD_GUI); + free(app); +} + +int32_t system_settings_app(void* p) { + UNUSED(p); + SystemSettings* app = system_settings_alloc(); + view_dispatcher_run(app->view_dispatcher); + system_settings_free(app); + return 0; +} diff --git a/applications/system/system_settings.h b/applications/settings/system/system_settings.h old mode 100755 new mode 100644 similarity index 100% rename from applications/system/system_settings.h rename to applications/settings/system/system_settings.h diff --git a/applications/snake_game/application.fam b/applications/snake_game/application.fam deleted file mode 100644 index 5d6ad527631..00000000000 --- a/applications/snake_game/application.fam +++ /dev/null @@ -1,11 +0,0 @@ -App( - appid="snake_game", - name="Snake Game", - apptype=FlipperAppType.PLUGIN, - entry_point="snake_game_app", - cdefines=["APP_SNAKE_GAME"], - requires=["gui"], - stack_size=1 * 1024, - icon="A_Plugins_14", - order=30, -) diff --git a/applications/storage/application.fam b/applications/storage/application.fam deleted file mode 100644 index 21089635bd8..00000000000 --- a/applications/storage/application.fam +++ /dev/null @@ -1,19 +0,0 @@ -App( - appid="storage", - name="StorageSrv", - apptype=FlipperAppType.SERVICE, - entry_point="storage_srv", - cdefines=["SRV_STORAGE"], - requires=["storage_settings"], - provides=["storage_start"], - stack_size=3 * 1024, - order=120, -) - -App( - appid="storage_start", - apptype=FlipperAppType.STARTUP, - entry_point="storage_on_system_start", - requires=["storage"], - order=90, -) diff --git a/applications/storage/storage.h b/applications/storage/storage.h deleted file mode 100644 index 55a951d128c..00000000000 --- a/applications/storage/storage.h +++ /dev/null @@ -1,349 +0,0 @@ -#pragma once -#include -#include -#include "filesystem_api_defines.h" -#include "storage_sd_api.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define STORAGE_INT_PATH_PREFIX "/int" -#define STORAGE_EXT_PATH_PREFIX "/ext" -#define STORAGE_ANY_PATH_PREFIX "/any" - -#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 RECORD_STORAGE "storage" - -typedef struct Storage Storage; - -/** Allocates and initializes a file descriptor - * @return File* - */ -File* storage_file_alloc(Storage* storage); - -/** Frees the file descriptor. Closes the file if it was open. - */ -void storage_file_free(File* file); - -typedef enum { - StorageEventTypeCardMount, - StorageEventTypeCardUnmount, - StorageEventTypeCardMountError, - StorageEventTypeFileClose, - StorageEventTypeDirClose, -} StorageEventType; - -typedef struct { - StorageEventType type; -} StorageEvent; - -/** - * Get storage pubsub. - * Storage will send StorageEvent messages. - * @param storage - * @return FuriPubSub* - */ -FuriPubSub* storage_get_pubsub(Storage* storage); - -/******************* File Functions *******************/ - -/** Opens an existing file or create a new one. - * @param file pointer to file object. - * @param path path to file - * @param access_mode access mode from FS_AccessMode - * @param open_mode open mode from FS_OpenMode - * @return success flag. You need to close the file even if the open operation failed. - */ -bool storage_file_open( - File* file, - const char* path, - FS_AccessMode access_mode, - FS_OpenMode open_mode); - -/** Close the file. - * @param file pointer to a file object, the file object will be freed. - * @return success flag - */ -bool storage_file_close(File* file); - -/** Tells if the file is open - * @param file pointer to a file object - * @return bool true if file is open - */ -bool storage_file_is_open(File* file); - -/** Tells if the file is a directory - * @param file pointer to a file object - * @return bool true if file is a directory - */ -bool storage_file_is_dir(File* file); - -/** Reads bytes from a file into a buffer - * @param file pointer to file object. - * @param buff pointer to a buffer, for reading - * @param bytes_to_read how many bytes to read. Must be less than or equal to the size of the buffer. - * @return uint16_t how many bytes were actually read - */ -uint16_t storage_file_read(File* file, void* buff, uint16_t bytes_to_read); - -/** Writes bytes from a buffer to a file - * @param file pointer to file object. - * @param buff pointer to buffer, for writing - * @param bytes_to_write how many bytes to write. Must be less than or equal to the size of the buffer. - * @return uint16_t how many bytes were actually written - */ -uint16_t storage_file_write(File* file, const void* buff, uint16_t bytes_to_write); - -/** Moves the r/w pointer - * @param file pointer to file object. - * @param offset offset to move the r/w pointer - * @param from_start set an offset from the start or from the current position - * @return success flag - */ -bool storage_file_seek(File* file, uint32_t offset, bool from_start); - -/** Gets the position of the r/w pointer - * @param file pointer to file object. - * @return uint64_t position of the r/w pointer - */ -uint64_t storage_file_tell(File* file); - -/** Truncates the file size to the current position of the r/w pointer - * @param file pointer to file object. - * @return bool success flag - */ -bool storage_file_truncate(File* file); - -/** Gets the size of the file - * @param file pointer to file object. - * @return uint64_t size of the file - */ -uint64_t storage_file_size(File* file); - -/** Writes file cache to storage - * @param file pointer to file object. - * @return bool success flag - */ -bool storage_file_sync(File* file); - -/** Checks that the r/w pointer is at the end of the file - * @param file pointer to file object. - * @return bool success flag - */ -bool storage_file_eof(File* file); - -/******************* Dir Functions *******************/ - -/** Opens a directory to get objects from it - * @param app pointer to the api - * @param file pointer to file object. - * @param path path to directory - * @return bool success flag. You need to close the directory even if the open operation failed. - */ -bool storage_dir_open(File* file, const char* path); - -/** Close the directory. Also free file handle structure and point it to the NULL. - * @param file pointer to a file object. - * @return bool success flag - */ -bool storage_dir_close(File* file); - -/** Reads the next object in the directory - * @param file pointer to file object. - * @param fileinfo pointer to the read FileInfo, may be NULL - * @param name pointer to name buffer, may be NULL - * @param name_length name buffer length - * @return success flag (if the next object does not exist, it also returns false and sets the file error id to FSE_NOT_EXIST) - */ -bool storage_dir_read(File* file, FileInfo* fileinfo, char* name, uint16_t name_length); - -/** Rewinds the read pointer to first item in the directory - * @param file pointer to file object. - * @return bool success flag - */ -bool storage_dir_rewind(File* file); - -/******************* Common Functions *******************/ - -/** Retrieves information about a file/directory - * @param app pointer to the api - * @param path path to file/directory - * @param fileinfo pointer to the read FileInfo, may be NULL - * @return FS_Error operation result - */ -FS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* fileinfo); - -/** Removes a file/directory from the repository, the directory must be empty and the file/directory must not be open - * @param app pointer to the api - * @param path - * @return FS_Error operation result - */ -FS_Error storage_common_remove(Storage* storage, const char* path); - -/** Renames file/directory, file/directory must not be open - * @param app pointer to the api - * @param old_path old path - * @param new_path new path - * @return FS_Error operation result - */ -FS_Error storage_common_rename(Storage* storage, const char* old_path, const char* new_path); - -/** Copy file, file must not be open - * @param app pointer to the api - * @param old_path old path - * @param new_path new path - * @return FS_Error operation result - */ -FS_Error storage_common_copy(Storage* storage, const char* old_path, const char* new_path); - -/** Copy one folder contents into another with rename of all conflicting files - * @param app pointer to the api - * @param old_path old path - * @param new_path new path - * @return FS_Error operation result - */ -FS_Error storage_common_merge(Storage* storage, const char* old_path, const char* new_path); - -/** Creates a directory - * @param app pointer to the api - * @param path directory path - * @return FS_Error operation result - */ -FS_Error storage_common_mkdir(Storage* storage, const char* path); - -/** Gets general information about the storage - * @param app pointer to the api - * @param fs_path the path to the storage of interest - * @param total_space pointer to total space record, will be filled - * @param free_space pointer to free space record, will be filled - * @return FS_Error operation result - */ -FS_Error storage_common_fs_info( - Storage* storage, - const char* fs_path, - uint64_t* total_space, - uint64_t* free_space); - -/******************* Error Functions *******************/ - -/** Retrieves the error text from the error id - * @param error_id error id - * @return const char* error text - */ -const char* storage_error_get_desc(FS_Error error_id); - -/** Retrieves the error id from the file object - * @param file pointer to file object. Pointer must not point to NULL. YOU CANNOT RETREIVE THE ERROR ID IF THE FILE HAS BEEN CLOSED - * @return FS_Error error id - */ -FS_Error storage_file_get_error(File* file); - -/** Retrieves the internal (storage-specific) error id from the file object - * @param file pointer to file object. Pointer must not point to NULL. YOU CANNOT RETREIVE THE INTERNAL ERROR ID IF THE FILE HAS BEEN CLOSED - * @return FS_Error error id - */ -int32_t storage_file_get_internal_error(File* file); - -/** Retrieves the error text from the file object - * @param file pointer to file object. Pointer must not point to NULL. YOU CANNOT RETREIVE THE ERROR TEXT IF THE FILE HAS BEEN CLOSED - * @return const char* error text - */ -const char* storage_file_get_error_desc(File* file); - -/******************* SD Card Functions *******************/ - -/** Formats SD Card - * @param api pointer to the api - * @return FS_Error operation result - */ -FS_Error storage_sd_format(Storage* api); - -/** Will unmount the SD card - * @param api pointer to the api - * @return FS_Error operation result - */ -FS_Error storage_sd_unmount(Storage* api); - -/** Retrieves SD card information - * @param api pointer to the api - * @param info pointer to the info - * @return FS_Error operation result - */ -FS_Error storage_sd_info(Storage* api, SDInfo* info); - -/** Retrieves SD card status - * @param api pointer to the api - * @return FS_Error operation result - */ -FS_Error storage_sd_status(Storage* api); - -/******************* Internal LFS Functions *******************/ - -typedef void (*Storage_name_converter)(string_t); - -/** Backs up internal storage to a tar archive - * @param api pointer to the api - * @param dstmane destination archive path - * @return FS_Error operation result - */ -FS_Error storage_int_backup(Storage* api, const char* dstname); - -/** Restores internal storage from a tar archive - * @param api pointer to the api - * @param dstmane archive path - * @param converter pointer to filename conversion function, may be NULL - * @return FS_Error operation result - */ -FS_Error storage_int_restore(Storage* api, const char* dstname, Storage_name_converter converter); - -/***************** Simplified Functions ******************/ - -/** - * Removes a file/directory, the directory must be empty and the file/directory must not be open - * @param storage pointer to the api - * @param path - * @return true on success or if file/dir is not exist - */ -bool storage_simply_remove(Storage* storage, const char* path); - -/** - * Recursively removes a file/directory, the directory can be not empty - * @param storage pointer to the api - * @param path - * @return true on success or if file/dir is not exist - */ -bool storage_simply_remove_recursive(Storage* storage, const char* path); - -/** - * Creates a directory - * @param storage - * @param path - * @return true on success or if directory is already exist - */ -bool storage_simply_mkdir(Storage* storage, const char* path); - -/** - * @brief Get next free filename. - * - * @param storage - * @param dirname - * @param filename - * @param fileextension - * @param nextfilename return name - * @param max_len max len name - */ -void storage_get_next_filename( - Storage* storage, - const char* dirname, - const char* filename, - const char* fileextension, - string_t nextfilename, - uint8_t max_len); - -#ifdef __cplusplus -} -#endif diff --git a/applications/storage/storage_cli.c b/applications/storage/storage_cli.c deleted file mode 100644 index 802ebd54805..00000000000 --- a/applications/storage/storage_cli.c +++ /dev/null @@ -1,609 +0,0 @@ -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#define MAX_NAME_LENGTH 255 - -static void storage_cli_print_usage() { - printf("Usage:\r\n"); - printf("storage \r\n"); - printf("The path must start with /int or /ext\r\n"); - printf("Cmd list:\r\n"); - printf("\tinfo\t - get FS info\r\n"); - printf("\tformat\t - format filesystem\r\n"); - printf("\tlist\t - list files and dirs\r\n"); - printf("\ttree\t - list files and dirs, recursive\r\n"); - printf("\tremove\t - delete the file or directory\r\n"); - printf("\tread\t - read text from file and print file size and content to cli\r\n"); - printf( - "\tread_chunks\t - read data from file and print file size and content to cli, should contain how many bytes you want to read in block\r\n"); - printf("\twrite\t - read text from cli and append it to file, stops by ctrl+c\r\n"); - printf( - "\twrite_chunk\t - read data from cli and append it to file, should contain how many bytes you want to write\r\n"); - printf("\tcopy\t - copy file to new file, must contain new path\r\n"); - printf("\trename\t - move file to new file, must contain new path\r\n"); - printf("\tmkdir\t - creates a new directory\r\n"); - printf("\tmd5\t - md5 hash of the file\r\n"); - printf("\tstat\t - info about file or dir\r\n"); -}; - -static void storage_cli_print_error(FS_Error error) { - printf("Storage error: %s\r\n", storage_error_get_desc(error)); -} - -static void storage_cli_info(Cli* cli, string_t path) { - UNUSED(cli); - Storage* api = furi_record_open(RECORD_STORAGE); - - if(string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0) { - uint64_t total_space; - uint64_t free_space; - FS_Error error = - storage_common_fs_info(api, STORAGE_INT_PATH_PREFIX, &total_space, &free_space); - - if(error != FSE_OK) { - storage_cli_print_error(error); - } else { - printf( - "Label: %s\r\nType: LittleFS\r\n%luKB total\r\n%luKB free\r\n", - furi_hal_version_get_name_ptr() ? furi_hal_version_get_name_ptr() : "Unknown", - (uint32_t)(total_space / 1024), - (uint32_t)(free_space / 1024)); - } - } else if(string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) { - SDInfo sd_info; - FS_Error error = storage_sd_info(api, &sd_info); - - if(error != FSE_OK) { - storage_cli_print_error(error); - } else { - printf( - "Label: %s\r\nType: %s\r\n%luKB total\r\n%luKB free\r\n", - sd_info.label, - sd_api_get_fs_type_text(sd_info.fs_type), - sd_info.kb_total, - sd_info.kb_free); - } - } else { - storage_cli_print_usage(); - } - - furi_record_close(RECORD_STORAGE); -}; - -static void storage_cli_format(Cli* cli, string_t path) { - if(string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0) { - storage_cli_print_error(FSE_NOT_IMPLEMENTED); - } else if(string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) { - printf("Formatting SD card, All data will be lost! Are you sure (y/n)?\r\n"); - char answer = cli_getc(cli); - if(answer == 'y' || answer == 'Y') { - Storage* api = furi_record_open(RECORD_STORAGE); - printf("Formatting, please wait...\r\n"); - - FS_Error error = storage_sd_format(api); - - if(error != FSE_OK) { - storage_cli_print_error(error); - } else { - printf("SD card was successfully formatted.\r\n"); - } - furi_record_close(RECORD_STORAGE); - } else { - printf("Cancelled.\r\n"); - } - } else { - storage_cli_print_usage(); - } -}; - -static void storage_cli_list(Cli* cli, string_t path) { - UNUSED(cli); - if(string_cmp_str(path, "/") == 0) { - printf("\t[D] int\r\n"); - printf("\t[D] ext\r\n"); - printf("\t[D] any\r\n"); - } else { - Storage* api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(api); - - if(storage_dir_open(file, string_get_cstr(path))) { - FileInfo fileinfo; - char name[MAX_NAME_LENGTH]; - bool read_done = false; - - while(storage_dir_read(file, &fileinfo, name, MAX_NAME_LENGTH)) { - read_done = true; - if(fileinfo.flags & FSF_DIRECTORY) { - printf("\t[D] %s\r\n", name); - } else { - printf("\t[F] %s %lub\r\n", name, (uint32_t)(fileinfo.size)); - } - } - - if(!read_done) { - printf("\tEmpty\r\n"); - } - } else { - storage_cli_print_error(storage_file_get_error(file)); - } - - storage_dir_close(file); - storage_file_free(file); - furi_record_close(RECORD_STORAGE); - } -} - -static void storage_cli_tree(Cli* cli, string_t path) { - if(string_cmp_str(path, "/") == 0) { - string_set(path, STORAGE_INT_PATH_PREFIX); - storage_cli_tree(cli, path); - string_set(path, STORAGE_EXT_PATH_PREFIX); - storage_cli_tree(cli, path); - } else { - Storage* api = furi_record_open(RECORD_STORAGE); - DirWalk* dir_walk = dir_walk_alloc(api); - string_t name; - string_init(name); - - if(dir_walk_open(dir_walk, string_get_cstr(path))) { - FileInfo fileinfo; - bool read_done = false; - - while(dir_walk_read(dir_walk, name, &fileinfo) == DirWalkOK) { - read_done = true; - if(fileinfo.flags & FSF_DIRECTORY) { - printf("\t[D] %s\r\n", string_get_cstr(name)); - } else { - printf("\t[F] %s %lub\r\n", string_get_cstr(name), (uint32_t)(fileinfo.size)); - } - } - - if(!read_done) { - printf("\tEmpty\r\n"); - } - } else { - storage_cli_print_error(dir_walk_get_error(dir_walk)); - } - - string_clear(name); - dir_walk_free(dir_walk); - furi_record_close(RECORD_STORAGE); - } -} - -static void storage_cli_read(Cli* cli, string_t path) { - UNUSED(cli); - Storage* api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(api); - - if(storage_file_open(file, string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { - const uint16_t buffer_size = 128; - uint16_t read_size = 0; - uint8_t* data = malloc(buffer_size); - - printf("Size: %lu\r\n", (uint32_t)storage_file_size(file)); - - do { - read_size = storage_file_read(file, data, buffer_size); - for(uint16_t i = 0; i < read_size; i++) { - printf("%c", data[i]); - } - } while(read_size > 0); - printf("\r\n"); - - free(data); - } else { - storage_cli_print_error(storage_file_get_error(file)); - } - - storage_file_close(file); - storage_file_free(file); - - furi_record_close(RECORD_STORAGE); -} - -static void storage_cli_write(Cli* cli, string_t path) { - Storage* api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(api); - - const uint16_t buffer_size = 512; - uint8_t* buffer = malloc(buffer_size); - - if(storage_file_open(file, string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_APPEND)) { - printf("Just write your text data. New line by Ctrl+Enter, exit by Ctrl+C.\r\n"); - - uint32_t read_index = 0; - - while(true) { - uint8_t symbol = cli_getc(cli); - - if(symbol == CliSymbolAsciiETX) { - uint16_t write_size = read_index % buffer_size; - - if(write_size > 0) { - uint16_t written_size = storage_file_write(file, buffer, write_size); - - if(written_size != write_size) { - storage_cli_print_error(storage_file_get_error(file)); - } - break; - } - } - - buffer[read_index % buffer_size] = symbol; - printf("%c", buffer[read_index % buffer_size]); - fflush(stdout); - read_index++; - - if(((read_index % buffer_size) == 0)) { - uint16_t written_size = storage_file_write(file, buffer, buffer_size); - - if(written_size != buffer_size) { - storage_cli_print_error(storage_file_get_error(file)); - break; - } - } - } - printf("\r\n"); - - } else { - storage_cli_print_error(storage_file_get_error(file)); - } - storage_file_close(file); - - free(buffer); - storage_file_free(file); - furi_record_close(RECORD_STORAGE); -} - -static void storage_cli_read_chunks(Cli* cli, string_t path, string_t args) { - Storage* api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(api); - - uint32_t buffer_size; - int parsed_count = sscanf(string_get_cstr(args), "%lu", &buffer_size); - - if(parsed_count == EOF || parsed_count != 1) { - storage_cli_print_usage(); - } else if(storage_file_open(file, string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { - uint64_t file_size = storage_file_size(file); - - printf("Size: %lu\r\n", (uint32_t)file_size); - - if(buffer_size) { - uint8_t* data = malloc(buffer_size); - while(file_size > 0) { - printf("\r\nReady?\r\n"); - cli_getc(cli); - - uint16_t read_size = storage_file_read(file, data, buffer_size); - for(uint16_t i = 0; i < read_size; i++) { - putchar(data[i]); - } - file_size -= read_size; - } - free(data); - } - printf("\r\n"); - - } else { - storage_cli_print_error(storage_file_get_error(file)); - } - - storage_file_close(file); - storage_file_free(file); - - furi_record_close(RECORD_STORAGE); -} - -static void storage_cli_write_chunk(Cli* cli, string_t path, string_t args) { - Storage* api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(api); - - uint32_t buffer_size; - int parsed_count = sscanf(string_get_cstr(args), "%lu", &buffer_size); - - if(parsed_count == EOF || parsed_count != 1) { - storage_cli_print_usage(); - } else { - if(storage_file_open(file, string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_APPEND)) { - printf("Ready\r\n"); - - if(buffer_size) { - uint8_t* buffer = malloc(buffer_size); - - for(uint32_t i = 0; i < buffer_size; i++) { - buffer[i] = cli_getc(cli); - } - - uint16_t written_size = storage_file_write(file, buffer, buffer_size); - - if(written_size != buffer_size) { - storage_cli_print_error(storage_file_get_error(file)); - } - - free(buffer); - } - } else { - storage_cli_print_error(storage_file_get_error(file)); - } - storage_file_close(file); - } - - storage_file_free(file); - furi_record_close(RECORD_STORAGE); -} - -static void storage_cli_stat(Cli* cli, string_t path) { - UNUSED(cli); - Storage* api = furi_record_open(RECORD_STORAGE); - - if(string_cmp_str(path, "/") == 0) { - printf("Storage\r\n"); - } else if( - string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0 || - string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0 || - string_cmp_str(path, STORAGE_ANY_PATH_PREFIX) == 0) { - uint64_t total_space; - uint64_t free_space; - FS_Error error = - storage_common_fs_info(api, string_get_cstr(path), &total_space, &free_space); - - if(error != FSE_OK) { - storage_cli_print_error(error); - } else { - printf( - "Storage, %luKB total, %luKB free\r\n", - (uint32_t)(total_space / 1024), - (uint32_t)(free_space / 1024)); - } - } else { - FileInfo fileinfo; - FS_Error error = storage_common_stat(api, string_get_cstr(path), &fileinfo); - - if(error == FSE_OK) { - if(fileinfo.flags & FSF_DIRECTORY) { - printf("Directory\r\n"); - } else { - printf("File, size: %lub\r\n", (uint32_t)(fileinfo.size)); - } - } else { - storage_cli_print_error(error); - } - } - - furi_record_close(RECORD_STORAGE); -} - -static void storage_cli_copy(Cli* cli, string_t old_path, string_t args) { - UNUSED(cli); - Storage* api = furi_record_open(RECORD_STORAGE); - string_t new_path; - string_init(new_path); - - if(!args_read_probably_quoted_string_and_trim(args, new_path)) { - storage_cli_print_usage(); - } else { - FS_Error error = - storage_common_copy(api, string_get_cstr(old_path), string_get_cstr(new_path)); - - if(error != FSE_OK) { - storage_cli_print_error(error); - } - } - - string_clear(new_path); - furi_record_close(RECORD_STORAGE); -} - -static void storage_cli_remove(Cli* cli, string_t path) { - UNUSED(cli); - Storage* api = furi_record_open(RECORD_STORAGE); - FS_Error error = storage_common_remove(api, string_get_cstr(path)); - - if(error != FSE_OK) { - storage_cli_print_error(error); - } - - furi_record_close(RECORD_STORAGE); -} - -static void storage_cli_rename(Cli* cli, string_t old_path, string_t args) { - UNUSED(cli); - Storage* api = furi_record_open(RECORD_STORAGE); - string_t new_path; - string_init(new_path); - - if(!args_read_probably_quoted_string_and_trim(args, new_path)) { - storage_cli_print_usage(); - } else { - FS_Error error = - storage_common_rename(api, string_get_cstr(old_path), string_get_cstr(new_path)); - - if(error != FSE_OK) { - storage_cli_print_error(error); - } - } - - string_clear(new_path); - furi_record_close(RECORD_STORAGE); -} - -static void storage_cli_mkdir(Cli* cli, string_t path) { - UNUSED(cli); - Storage* api = furi_record_open(RECORD_STORAGE); - FS_Error error = storage_common_mkdir(api, string_get_cstr(path)); - - if(error != FSE_OK) { - storage_cli_print_error(error); - } - - furi_record_close(RECORD_STORAGE); -} - -static void storage_cli_md5(Cli* cli, string_t path) { - UNUSED(cli); - Storage* api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(api); - - if(storage_file_open(file, string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { - const uint16_t buffer_size = 512; - const uint8_t hash_size = 16; - uint8_t* data = malloc(buffer_size); - uint8_t* hash = malloc(sizeof(uint8_t) * hash_size); - md5_context* md5_ctx = malloc(sizeof(md5_context)); - - md5_starts(md5_ctx); - while(true) { - uint16_t read_size = storage_file_read(file, data, buffer_size); - if(read_size == 0) break; - md5_update(md5_ctx, data, read_size); - } - md5_finish(md5_ctx, hash); - free(md5_ctx); - - for(uint8_t i = 0; i < hash_size; i++) { - printf("%02x", hash[i]); - } - printf("\r\n"); - - free(hash); - free(data); - } else { - storage_cli_print_error(storage_file_get_error(file)); - } - - storage_file_close(file); - storage_file_free(file); - - furi_record_close(RECORD_STORAGE); -} - -void storage_cli(Cli* cli, string_t args, void* context) { - UNUSED(context); - string_t cmd; - string_t path; - string_init(cmd); - string_init(path); - - do { - if(!args_read_string_and_trim(args, cmd)) { - storage_cli_print_usage(); - break; - } - - if(!args_read_probably_quoted_string_and_trim(args, path)) { - storage_cli_print_usage(); - break; - } - - if(string_cmp_str(cmd, "info") == 0) { - storage_cli_info(cli, path); - break; - } - - if(string_cmp_str(cmd, "format") == 0) { - storage_cli_format(cli, path); - break; - } - - if(string_cmp_str(cmd, "list") == 0) { - storage_cli_list(cli, path); - break; - } - - if(string_cmp_str(cmd, "tree") == 0) { - storage_cli_tree(cli, path); - break; - } - - if(string_cmp_str(cmd, "read") == 0) { - storage_cli_read(cli, path); - break; - } - - if(string_cmp_str(cmd, "read_chunks") == 0) { - storage_cli_read_chunks(cli, path, args); - break; - } - - if(string_cmp_str(cmd, "write") == 0) { - storage_cli_write(cli, path); - break; - } - - if(string_cmp_str(cmd, "write_chunk") == 0) { - storage_cli_write_chunk(cli, path, args); - break; - } - - if(string_cmp_str(cmd, "copy") == 0) { - storage_cli_copy(cli, path, args); - break; - } - - if(string_cmp_str(cmd, "remove") == 0) { - storage_cli_remove(cli, path); - break; - } - - if(string_cmp_str(cmd, "rename") == 0) { - storage_cli_rename(cli, path, args); - break; - } - - if(string_cmp_str(cmd, "mkdir") == 0) { - storage_cli_mkdir(cli, path); - break; - } - - if(string_cmp_str(cmd, "md5") == 0) { - storage_cli_md5(cli, path); - break; - } - - if(string_cmp_str(cmd, "stat") == 0) { - storage_cli_stat(cli, path); - break; - } - - storage_cli_print_usage(); - } while(false); - - string_clear(path); - string_clear(cmd); -} - -static void storage_cli_factory_reset(Cli* cli, string_t args, void* context) { - UNUSED(args); - UNUSED(context); - printf("All data will be lost! Are you sure (y/n)?\r\n"); - char c = cli_getc(cli); - if(c == 'y' || c == 'Y') { - printf("Data will be wiped after reboot.\r\n"); - furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); - power_reboot(PowerBootModeNormal); - } else { - printf("Safe choice.\r\n"); - } -} - -void storage_on_system_start() { -#ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - cli_add_command(cli, RECORD_STORAGE, CliCommandFlagParallelSafe, storage_cli, NULL); - cli_add_command( - cli, "factory_reset", CliCommandFlagParallelSafe, storage_cli_factory_reset, NULL); - furi_record_close(RECORD_CLI); -#else - UNUSED(storage_cli_factory_reset); -#endif -} diff --git a/applications/storage/storage_external_api.c b/applications/storage/storage_external_api.c deleted file mode 100644 index b32080dfcc3..00000000000 --- a/applications/storage/storage_external_api.c +++ /dev/null @@ -1,789 +0,0 @@ -#include -#include -#include -#include "storage.h" -#include "storage_i.h" -#include "storage_message.h" -#include -#include -#include "toolbox/path.h" - -#define MAX_NAME_LENGTH 256 -#define MAX_EXT_LEN 16 - -#define TAG "StorageAPI" - -#define S_API_PROLOGUE \ - FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0); \ - furi_check(semaphore != NULL); - -#define S_FILE_API_PROLOGUE \ - Storage* storage = file->storage; \ - furi_assert(storage); - -#define S_API_EPILOGUE \ - furi_check( \ - furi_message_queue_put(storage->message_queue, &message, FuriWaitForever) == \ - FuriStatusOk); \ - furi_semaphore_acquire(semaphore, FuriWaitForever); \ - furi_semaphore_free(semaphore); - -#define S_API_MESSAGE(_command) \ - SAReturn return_data; \ - StorageMessage message = { \ - .semaphore = semaphore, \ - .command = _command, \ - .data = &data, \ - .return_data = &return_data, \ - }; - -#define S_API_DATA_FILE \ - SAData data = { \ - .file = { \ - .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); -#define S_RETURN_ERROR (return_data.error_value); -#define S_RETURN_CSTRING (return_data.cstring_value); - -typedef enum { - StorageEventFlagFileClose = (1 << 0), -} StorageEventFlag; -/****************** FILE ******************/ - -static bool storage_file_open_internal( - File* file, - const char* path, - FS_AccessMode access_mode, - FS_OpenMode open_mode) { - S_FILE_API_PROLOGUE; - S_API_PROLOGUE; - - SAData data = { - .fopen = { - .file = file, - .path = path, - .access_mode = access_mode, - .open_mode = open_mode, - }}; - - file->type = FileTypeOpenFile; - - S_API_MESSAGE(StorageCommandFileOpen); - S_API_EPILOGUE; - - return S_RETURN_BOOL; -} - -static void storage_file_close_callback(const void* message, void* context) { - const StorageEvent* storage_event = message; - - if(storage_event->type == StorageEventTypeFileClose || - storage_event->type == StorageEventTypeDirClose) { - furi_assert(context); - FuriEventFlag* event = context; - furi_event_flag_set(event, StorageEventFlagFileClose); - } -} - -bool storage_file_open( - File* file, - const char* path, - FS_AccessMode access_mode, - FS_OpenMode open_mode) { - bool result; - FuriEventFlag* event = furi_event_flag_alloc(); - FuriPubSubSubscription* subscription = furi_pubsub_subscribe( - storage_get_pubsub(file->storage), storage_file_close_callback, event); - - do { - result = storage_file_open_internal(file, path, access_mode, open_mode); - - if(!result && file->error_id == FSE_ALREADY_OPEN) { - furi_event_flag_wait( - event, StorageEventFlagFileClose, FuriFlagWaitAny, FuriWaitForever); - } else { - break; - } - } while(true); - - furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription); - furi_event_flag_free(event); - - FURI_LOG_T( - TAG, "File %p - %p open (%s)", (uint32_t)file - SRAM_BASE, file->file_id - SRAM_BASE, path); - - return result; -} - -bool storage_file_close(File* file) { - S_FILE_API_PROLOGUE; - S_API_PROLOGUE; - - S_API_DATA_FILE; - S_API_MESSAGE(StorageCommandFileClose); - S_API_EPILOGUE; - - FURI_LOG_T(TAG, "File %p - %p closed", (uint32_t)file - SRAM_BASE, file->file_id - SRAM_BASE); - file->type = FileTypeClosed; - - return S_RETURN_BOOL; -} - -uint16_t storage_file_read(File* file, void* buff, uint16_t bytes_to_read) { - if(bytes_to_read == 0) { - return 0; - } - - S_FILE_API_PROLOGUE; - S_API_PROLOGUE; - - SAData data = { - .fread = { - .file = file, - .buff = buff, - .bytes_to_read = bytes_to_read, - }}; - - S_API_MESSAGE(StorageCommandFileRead); - S_API_EPILOGUE; - return S_RETURN_UINT16; -} - -uint16_t storage_file_write(File* file, const void* buff, uint16_t bytes_to_write) { - if(bytes_to_write == 0) { - return 0; - } - - S_FILE_API_PROLOGUE; - S_API_PROLOGUE; - - SAData data = { - .fwrite = { - .file = file, - .buff = buff, - .bytes_to_write = bytes_to_write, - }}; - - S_API_MESSAGE(StorageCommandFileWrite); - S_API_EPILOGUE; - return S_RETURN_UINT16; -} - -bool storage_file_seek(File* file, uint32_t offset, bool from_start) { - S_FILE_API_PROLOGUE; - S_API_PROLOGUE; - - SAData data = { - .fseek = { - .file = file, - .offset = offset, - .from_start = from_start, - }}; - - S_API_MESSAGE(StorageCommandFileSeek); - S_API_EPILOGUE; - return S_RETURN_BOOL; -} - -uint64_t storage_file_tell(File* file) { - S_FILE_API_PROLOGUE; - S_API_PROLOGUE; - S_API_DATA_FILE; - S_API_MESSAGE(StorageCommandFileTell); - S_API_EPILOGUE; - return S_RETURN_UINT64; -} - -bool storage_file_truncate(File* file) { - S_FILE_API_PROLOGUE; - S_API_PROLOGUE; - S_API_DATA_FILE; - S_API_MESSAGE(StorageCommandFileTruncate); - S_API_EPILOGUE; - return S_RETURN_BOOL; -} - -uint64_t storage_file_size(File* file) { - S_FILE_API_PROLOGUE; - S_API_PROLOGUE; - S_API_DATA_FILE; - S_API_MESSAGE(StorageCommandFileSize); - S_API_EPILOGUE; - return S_RETURN_UINT64; -} - -bool storage_file_sync(File* file) { - S_FILE_API_PROLOGUE; - S_API_PROLOGUE; - S_API_DATA_FILE; - S_API_MESSAGE(StorageCommandFileSync); - S_API_EPILOGUE; - return S_RETURN_BOOL; -} - -bool storage_file_eof(File* file) { - S_FILE_API_PROLOGUE; - S_API_PROLOGUE; - S_API_DATA_FILE; - S_API_MESSAGE(StorageCommandFileEof); - S_API_EPILOGUE; - return S_RETURN_BOOL; -} - -/****************** DIR ******************/ - -static bool storage_dir_open_internal(File* file, const char* path) { - S_FILE_API_PROLOGUE; - S_API_PROLOGUE; - - SAData data = { - .dopen = { - .file = file, - .path = path, - }}; - - file->type = FileTypeOpenDir; - - S_API_MESSAGE(StorageCommandDirOpen); - S_API_EPILOGUE; - return S_RETURN_BOOL; -} - -bool storage_dir_open(File* file, const char* path) { - bool result; - FuriEventFlag* event = furi_event_flag_alloc(); - FuriPubSubSubscription* subscription = furi_pubsub_subscribe( - storage_get_pubsub(file->storage), storage_file_close_callback, event); - - do { - result = storage_dir_open_internal(file, path); - - if(!result && file->error_id == FSE_ALREADY_OPEN) { - furi_event_flag_wait( - event, StorageEventFlagFileClose, FuriFlagWaitAny, FuriWaitForever); - } else { - break; - } - } while(true); - - furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription); - furi_event_flag_free(event); - - FURI_LOG_T( - TAG, "Dir %p - %p open (%s)", (uint32_t)file - SRAM_BASE, file->file_id - SRAM_BASE, path); - - return result; -} - -bool storage_dir_close(File* file) { - S_FILE_API_PROLOGUE; - S_API_PROLOGUE; - S_API_DATA_FILE; - S_API_MESSAGE(StorageCommandDirClose); - S_API_EPILOGUE; - - FURI_LOG_T(TAG, "Dir %p - %p closed", (uint32_t)file - SRAM_BASE, file->file_id - SRAM_BASE); - - file->type = FileTypeClosed; - - return S_RETURN_BOOL; -} - -bool storage_dir_read(File* file, FileInfo* fileinfo, char* name, uint16_t name_length) { - S_FILE_API_PROLOGUE; - S_API_PROLOGUE; - - SAData data = { - .dread = { - .file = file, - .fileinfo = fileinfo, - .name = name, - .name_length = name_length, - }}; - - S_API_MESSAGE(StorageCommandDirRead); - S_API_EPILOGUE; - return S_RETURN_BOOL; -} - -bool storage_dir_rewind(File* file) { - S_FILE_API_PROLOGUE; - S_API_PROLOGUE; - S_API_DATA_FILE; - S_API_MESSAGE(StorageCommandDirRewind); - S_API_EPILOGUE; - return S_RETURN_BOOL; -} - -/****************** COMMON ******************/ - -FS_Error storage_common_stat(Storage* storage, const char* path, FileInfo* fileinfo) { - S_API_PROLOGUE; - - SAData data = {.cstat = {.path = path, .fileinfo = fileinfo}}; - - S_API_MESSAGE(StorageCommandCommonStat); - S_API_EPILOGUE; - return S_RETURN_ERROR; -} - -FS_Error storage_common_remove(Storage* storage, const char* path) { - S_API_PROLOGUE; - S_API_DATA_PATH; - S_API_MESSAGE(StorageCommandCommonRemove); - S_API_EPILOGUE; - return S_RETURN_ERROR; -} - -FS_Error storage_common_rename(Storage* storage, const char* old_path, const char* new_path) { - FS_Error error = storage_common_copy(storage, old_path, new_path); - if(error == FSE_OK) { - if(storage_simply_remove_recursive(storage, old_path)) { - error = FSE_OK; - } else { - error = FSE_INTERNAL; - } - } - - return error; -} - -static FS_Error - storage_copy_recursive(Storage* storage, const char* old_path, const char* new_path) { - FS_Error error = storage_common_mkdir(storage, new_path); - DirWalk* dir_walk = dir_walk_alloc(storage); - string_t path; - string_t tmp_new_path; - string_t tmp_old_path; - FileInfo fileinfo; - string_init(path); - string_init(tmp_new_path); - string_init(tmp_old_path); - - do { - if(error != FSE_OK) break; - - if(!dir_walk_open(dir_walk, old_path)) { - error = dir_walk_get_error(dir_walk); - break; - } - - while(1) { - DirWalkResult res = dir_walk_read(dir_walk, path, &fileinfo); - - if(res == DirWalkError) { - error = dir_walk_get_error(dir_walk); - break; - } else if(res == DirWalkLast) { - break; - } else { - string_set(tmp_old_path, path); - string_right(path, strlen(old_path)); - string_printf(tmp_new_path, "%s%s", new_path, string_get_cstr(path)); - - if(fileinfo.flags & FSF_DIRECTORY) { - error = storage_common_mkdir(storage, string_get_cstr(tmp_new_path)); - } else { - error = storage_common_copy( - storage, string_get_cstr(tmp_old_path), string_get_cstr(tmp_new_path)); - } - - if(error != FSE_OK) break; - } - } - - } while(false); - - string_clear(tmp_new_path); - string_clear(tmp_old_path); - string_clear(path); - dir_walk_free(dir_walk); - return error; -} - -FS_Error storage_common_copy(Storage* storage, const char* old_path, const char* new_path) { - FS_Error error; - - FileInfo fileinfo; - error = storage_common_stat(storage, old_path, &fileinfo); - - if(error == FSE_OK) { - if(fileinfo.flags & FSF_DIRECTORY) { - error = storage_copy_recursive(storage, old_path, new_path); - } else { - Stream* stream_from = file_stream_alloc(storage); - Stream* stream_to = file_stream_alloc(storage); - - do { - if(!file_stream_open(stream_from, old_path, FSAM_READ, FSOM_OPEN_EXISTING)) break; - if(!file_stream_open(stream_to, new_path, FSAM_WRITE, FSOM_CREATE_NEW)) break; - stream_copy_full(stream_from, stream_to); - } while(false); - - error = file_stream_get_error(stream_from); - if(error == FSE_OK) { - error = file_stream_get_error(stream_to); - } - - stream_free(stream_from); - stream_free(stream_to); - } - } - - return error; -} - -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); - DirWalk* dir_walk = dir_walk_alloc(storage); - string_t path, file_basename, tmp_new_path; - FileInfo fileinfo; - string_init(path); - string_init(file_basename); - string_init(tmp_new_path); - - do { - if((error != FSE_OK) && (error != FSE_EXIST)) break; - - dir_walk_set_recursive(dir_walk, false); - if(!dir_walk_open(dir_walk, old_path)) { - error = dir_walk_get_error(dir_walk); - break; - } - - while(1) { - DirWalkResult res = dir_walk_read(dir_walk, path, &fileinfo); - - if(res == DirWalkError) { - error = dir_walk_get_error(dir_walk); - break; - } else if(res == DirWalkLast) { - break; - } else { - path_extract_basename(string_get_cstr(path), file_basename); - path_concat(new_path, string_get_cstr(file_basename), tmp_new_path); - - if(fileinfo.flags & FSF_DIRECTORY) { - if(storage_common_stat(storage, string_get_cstr(tmp_new_path), &fileinfo) == - FSE_OK) { - if(fileinfo.flags & FSF_DIRECTORY) { - error = storage_common_mkdir(storage, string_get_cstr(tmp_new_path)); - if(error != FSE_OK) { - break; - } - } - } - } - error = storage_common_merge( - storage, string_get_cstr(path), string_get_cstr(tmp_new_path)); - - if(error != FSE_OK) { - break; - } - } - } - - } while(false); - - string_clear(tmp_new_path); - string_clear(file_basename); - string_clear(path); - dir_walk_free(dir_walk); - return error; -} - -FS_Error storage_common_merge(Storage* storage, const char* old_path, const char* new_path) { - FS_Error error; - const char* new_path_tmp; - string_t new_path_next; - string_init(new_path_next); - - FileInfo fileinfo; - error = storage_common_stat(storage, old_path, &fileinfo); - - if(error == FSE_OK) { - if(fileinfo.flags & FSF_DIRECTORY) { - error = storage_merge_recursive(storage, old_path, new_path); - } else { - error = storage_common_stat(storage, new_path, &fileinfo); - if(error == FSE_OK) { - string_set_str(new_path_next, new_path); - string_t dir_path; - string_t filename; - char extension[MAX_EXT_LEN]; - - string_init(dir_path); - string_init(filename); - - path_extract_filename(new_path_next, filename, true); - path_extract_dirname(new_path, dir_path); - path_extract_extension(new_path_next, extension, MAX_EXT_LEN); - - storage_get_next_filename( - storage, - string_get_cstr(dir_path), - string_get_cstr(filename), - extension, - new_path_next, - 255); - string_cat_printf(dir_path, "/%s%s", string_get_cstr(new_path_next), extension); - string_set(new_path_next, dir_path); - - string_clear(dir_path); - string_clear(filename); - new_path_tmp = string_get_cstr(new_path_next); - } else { - new_path_tmp = new_path; - } - Stream* stream_from = file_stream_alloc(storage); - Stream* stream_to = file_stream_alloc(storage); - - do { - if(!file_stream_open(stream_from, old_path, FSAM_READ, FSOM_OPEN_EXISTING)) break; - if(!file_stream_open(stream_to, new_path_tmp, FSAM_WRITE, FSOM_CREATE_NEW)) break; - stream_copy_full(stream_from, stream_to); - } while(false); - - error = file_stream_get_error(stream_from); - if(error == FSE_OK) { - error = file_stream_get_error(stream_to); - } - - stream_free(stream_from); - stream_free(stream_to); - } - } - - string_clear(new_path_next); - - return error; -} - -FS_Error storage_common_mkdir(Storage* storage, const char* path) { - S_API_PROLOGUE; - S_API_DATA_PATH; - S_API_MESSAGE(StorageCommandCommonMkDir); - S_API_EPILOGUE; - return S_RETURN_ERROR; -} - -FS_Error storage_common_fs_info( - Storage* storage, - const char* fs_path, - uint64_t* total_space, - uint64_t* free_space) { - S_API_PROLOGUE; - - SAData data = { - .cfsinfo = { - .fs_path = fs_path, - .total_space = total_space, - .free_space = free_space, - }}; - - S_API_MESSAGE(StorageCommandCommonFSInfo); - S_API_EPILOGUE; - return S_RETURN_ERROR; -} - -/****************** ERROR ******************/ - -const char* storage_error_get_desc(FS_Error error_id) { - return filesystem_api_error_get_desc(error_id); -} - -FS_Error storage_file_get_error(File* file) { - furi_check(file != NULL); - return file->error_id; -} - -int32_t storage_file_get_internal_error(File* file) { - furi_check(file != NULL); - return file->internal_error_id; -} - -const char* storage_file_get_error_desc(File* file) { - furi_check(file != NULL); - return filesystem_api_error_get_desc(file->error_id); -} - -/****************** Raw SD API ******************/ - -FS_Error storage_sd_format(Storage* storage) { - S_API_PROLOGUE; - SAData data = {}; - S_API_MESSAGE(StorageCommandSDFormat); - S_API_EPILOGUE; - return S_RETURN_ERROR; -} - -FS_Error storage_sd_unmount(Storage* storage) { - S_API_PROLOGUE; - SAData data = {}; - S_API_MESSAGE(StorageCommandSDUnmount); - S_API_EPILOGUE; - return S_RETURN_ERROR; -} - -FS_Error storage_sd_info(Storage* storage, SDInfo* info) { - S_API_PROLOGUE; - SAData data = { - .sdinfo = { - .info = info, - }}; - S_API_MESSAGE(StorageCommandSDInfo); - S_API_EPILOGUE; - return S_RETURN_ERROR; -} - -FS_Error storage_sd_status(Storage* storage) { - S_API_PROLOGUE; - SAData data = {}; - S_API_MESSAGE(StorageCommandSDStatus); - S_API_EPILOGUE; - return S_RETURN_ERROR; -} - -File* storage_file_alloc(Storage* storage) { - File* file = malloc(sizeof(File)); - file->type = FileTypeClosed; - file->storage = storage; - - FURI_LOG_T(TAG, "File/Dir %p alloc", (uint32_t)file - SRAM_BASE); - - return file; -} - -bool storage_file_is_open(File* file) { - return (file->type != FileTypeClosed); -} - -bool storage_file_is_dir(File* file) { - return (file->type == FileTypeOpenDir); -} - -void storage_file_free(File* file) { - if(storage_file_is_open(file)) { - if(storage_file_is_dir(file)) { - storage_dir_close(file); - } else { - storage_file_close(file); - } - } - - FURI_LOG_T(TAG, "File/Dir %p free", (uint32_t)file - SRAM_BASE); - free(file); -} - -FuriPubSub* storage_get_pubsub(Storage* storage) { - return storage->pubsub; -} - -bool storage_simply_remove_recursive(Storage* storage, const char* path) { - furi_assert(storage); - furi_assert(path); - FileInfo fileinfo; - bool result = false; - string_t fullname; - string_t cur_dir; - - if(storage_simply_remove(storage, path)) { - return true; - } - - char* name = malloc(MAX_NAME_LENGTH + 1); - File* dir = storage_file_alloc(storage); - string_init_set_str(cur_dir, path); - bool go_deeper = false; - - while(1) { - if(!storage_dir_open(dir, string_get_cstr(cur_dir))) { - storage_dir_close(dir); - break; - } - - while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) { - if(fileinfo.flags & FSF_DIRECTORY) { - string_cat_printf(cur_dir, "/%s", name); - go_deeper = true; - break; - } - - string_init_printf(fullname, "%s/%s", string_get_cstr(cur_dir), name); - FS_Error error = storage_common_remove(storage, string_get_cstr(fullname)); - furi_check(error == FSE_OK); - string_clear(fullname); - } - storage_dir_close(dir); - - if(go_deeper) { - go_deeper = false; - continue; - } - - FS_Error error = storage_common_remove(storage, string_get_cstr(cur_dir)); - furi_check(error == FSE_OK); - - if(string_cmp(cur_dir, path)) { - size_t last_char = string_search_rchar(cur_dir, '/'); - furi_assert(last_char != STRING_FAILURE); - string_left(cur_dir, last_char); - } else { - result = true; - break; - } - } - - storage_file_free(dir); - string_clear(cur_dir); - free(name); - return result; -} - -bool storage_simply_remove(Storage* storage, const char* path) { - FS_Error result; - result = storage_common_remove(storage, path); - return result == FSE_OK || result == FSE_NOT_EXIST; -} - -bool storage_simply_mkdir(Storage* storage, const char* path) { - FS_Error result; - result = storage_common_mkdir(storage, path); - return result == FSE_OK || result == FSE_EXIST; -} - -void storage_get_next_filename( - Storage* storage, - const char* dirname, - const char* filename, - const char* fileextension, - string_t nextfilename, - uint8_t max_len) { - string_t temp_str; - uint16_t num = 0; - - string_init_printf(temp_str, "%s/%s%s", dirname, filename, fileextension); - - while(storage_common_stat(storage, string_get_cstr(temp_str), NULL) == FSE_OK) { - num++; - string_printf(temp_str, "%s/%s%d%s", dirname, filename, num, fileextension); - } - if(num && (max_len > strlen(filename))) { - string_printf(nextfilename, "%s%d", filename, num); - } else { - string_printf(nextfilename, "%s", filename); - } - - string_clear(temp_str); -} diff --git a/applications/storage/storage_glue.c b/applications/storage/storage_glue.c deleted file mode 100644 index d9d599c5c9e..00000000000 --- a/applications/storage/storage_glue.c +++ /dev/null @@ -1,188 +0,0 @@ -#include "storage_glue.h" -#include - -/****************** storage file ******************/ - -void storage_file_init(StorageFile* obj) { - obj->file = NULL; - obj->type = ST_ERROR; - obj->file_data = NULL; - string_init(obj->path); -} - -void storage_file_init_set(StorageFile* obj, const StorageFile* src) { - obj->file = src->file; - obj->type = src->type; - obj->file_data = src->file_data; - string_init_set(obj->path, src->path); -} - -void storage_file_set(StorageFile* obj, const StorageFile* src) { - obj->file = src->file; - obj->type = src->type; - obj->file_data = src->file_data; - string_set(obj->path, src->path); -} - -void storage_file_clear(StorageFile* obj) { - string_clear(obj->path); -} - -/****************** 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; -} - -const char* storage_data_status_text(StorageData* storage) { - const char* result = "unknown"; - switch(storage->status) { - case StorageStatusOK: - result = "ok"; - break; - case StorageStatusNotReady: - result = "not ready"; - break; - case StorageStatusNotMounted: - result = "not mounted"; - break; - case StorageStatusNoFS: - result = "no filesystem"; - break; - case StorageStatusNotAccessible: - result = "not accessible"; - break; - case StorageStatusErrorInternal: - result = "internal"; - break; - } - - return result; -} - -/****************** storage glue ******************/ - -bool storage_has_file(const File* file, StorageData* storage_data) { - bool result = false; - - StorageFileList_it_t it; - for(StorageFileList_it(it, storage_data->files); !StorageFileList_end_p(it); - StorageFileList_next(it)) { - const StorageFile* storage_file = StorageFileList_cref(it); - - if(storage_file->file->file_id == file->file_id) { - result = true; - break; - } - } - - return result; -} - -bool storage_path_already_open(string_t path, StorageFileList_t array) { - bool open = false; - - StorageFileList_it_t it; - - for(StorageFileList_it(it, array); !StorageFileList_end_p(it); StorageFileList_next(it)) { - const StorageFile* storage_file = StorageFileList_cref(it); - - if(string_cmp(storage_file->path, path) == 0) { - open = true; - break; - } - } - - return open; -} - -void storage_set_storage_file_data(const File* file, void* file_data, StorageData* storage) { - StorageFile* founded_file = NULL; - - StorageFileList_it_t it; - - for(StorageFileList_it(it, storage->files); !StorageFileList_end_p(it); - StorageFileList_next(it)) { - StorageFile* storage_file = StorageFileList_ref(it); - - if(storage_file->file->file_id == file->file_id) { - founded_file = storage_file; - break; - } - } - - furi_check(founded_file != NULL); - - founded_file->file_data = file_data; -} - -void* storage_get_storage_file_data(const File* file, StorageData* storage) { - const StorageFile* founded_file = NULL; - - StorageFileList_it_t it; - - for(StorageFileList_it(it, storage->files); !StorageFileList_end_p(it); - StorageFileList_next(it)) { - const StorageFile* storage_file = StorageFileList_cref(it); - - if(storage_file->file->file_id == file->file_id) { - founded_file = storage_file; - break; - } - } - - furi_check(founded_file != NULL); - - return founded_file->file_data; -} - -void storage_push_storage_file(File* file, string_t path, StorageType type, StorageData* storage) { - StorageFile* storage_file = StorageFileList_push_new(storage->files); - furi_check(storage_file != NULL); - - file->file_id = (uint32_t)storage_file; - storage_file->file = file; - storage_file->type = type; - string_set(storage_file->path, path); -} - -bool storage_pop_storage_file(File* file, StorageData* storage) { - StorageFileList_it_t it; - bool result = false; - - for(StorageFileList_it(it, storage->files); !StorageFileList_end_p(it); - StorageFileList_next(it)) { - if(StorageFileList_cref(it)->file->file_id == file->file_id) { - result = true; - break; - } - } - - if(result) { - StorageFileList_remove(storage->files, it); - } - - return result; -} diff --git a/applications/storage/storage_processing.c b/applications/storage/storage_processing.c deleted file mode 100644 index 46ca4e16582..00000000000 --- a/applications/storage/storage_processing.c +++ /dev/null @@ -1,583 +0,0 @@ -#include "storage_processing.h" -#include -#include -#include - -#define FS_CALL(_storage, _fn) \ - storage_data_lock(_storage); \ - ret = _storage->fs_api->_fn; \ - storage_data_unlock(_storage); - -#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) { -#ifdef FURI_RAM_EXEC - return type != ST_EXT; -#else - return type >= ST_ERROR; -#endif -} - -static StorageData* get_storage_by_file(File* file, StorageData* storages) { - StorageData* storage_data = NULL; - - for(uint8_t i = 0; i < STORAGE_COUNT; i++) { - if(storage_has_file(file, &storages[i])) { - storage_data = &storages[i]; - } - } - - return storage_data; -} - -static const char* remove_vfs(const char* path) { - return path + MIN(4u, strlen(path)); -} - -static StorageType storage_get_type_by_path(Storage* app, const char* 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; - } - - if(type == ST_ANY) { - type = ST_INT; - if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusOK) { - type = ST_EXT; - } - } - - return type; -} - -static void storage_path_change_to_real_storage(string_t path, StorageType real_storage) { - if(memcmp(string_get_cstr(path), STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == - 0) { - switch(real_storage) { - case ST_EXT: - string_set_char(path, 0, STORAGE_EXT_PATH_PREFIX[0]); - string_set_char(path, 1, STORAGE_EXT_PATH_PREFIX[1]); - string_set_char(path, 2, STORAGE_EXT_PATH_PREFIX[2]); - string_set_char(path, 3, STORAGE_EXT_PATH_PREFIX[3]); - break; - case ST_INT: - string_set_char(path, 0, STORAGE_INT_PATH_PREFIX[0]); - string_set_char(path, 1, STORAGE_INT_PATH_PREFIX[1]); - string_set_char(path, 2, STORAGE_INT_PATH_PREFIX[2]); - string_set_char(path, 3, STORAGE_INT_PATH_PREFIX[3]); - break; - default: - break; - } - } -} - -/******************* File Functions *******************/ - -bool storage_process_file_open( - Storage* app, - File* file, - const char* 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; - - if(storage_type_is_not_valid(type)) { - file->error_id = FSE_INVALID_NAME; - } else { - storage = storage_get_storage_by_type(app, type); - string_t real_path; - string_init_set(real_path, path); - storage_path_change_to_real_storage(real_path, type); - - if(storage_path_already_open(real_path, storage->files)) { - file->error_id = FSE_ALREADY_OPEN; - } else { - storage_push_storage_file(file, real_path, type, storage); - FS_CALL(storage, file.open(storage, file, remove_vfs(path), access_mode, open_mode)); - } - - string_clear(real_path); - } - - return ret; -} - -bool storage_process_file_close(Storage* app, File* file) { - bool ret = false; - StorageData* storage = get_storage_by_file(file, app->storage); - - if(storage == NULL) { - file->error_id = FSE_INVALID_PARAMETER; - } else { - FS_CALL(storage, file.close(storage, file)); - storage_pop_storage_file(file, storage); - - StorageEvent event = {.type = StorageEventTypeFileClose}; - furi_pubsub_publish(app->pubsub, &event); - } - - return ret; -} - -static uint16_t - storage_process_file_read(Storage* app, File* file, void* buff, uint16_t const bytes_to_read) { - uint16_t ret = 0; - StorageData* storage = get_storage_by_file(file, app->storage); - - if(storage == NULL) { - file->error_id = FSE_INVALID_PARAMETER; - } else { - FS_CALL(storage, file.read(storage, file, buff, bytes_to_read)); - } - - return ret; -} - -static uint16_t storage_process_file_write( - Storage* app, - File* file, - const void* buff, - uint16_t const bytes_to_write) { - uint16_t ret = 0; - StorageData* storage = get_storage_by_file(file, app->storage); - - if(storage == NULL) { - file->error_id = FSE_INVALID_PARAMETER; - } else { - FS_CALL(storage, file.write(storage, file, buff, bytes_to_write)); - } - - return ret; -} - -static bool storage_process_file_seek( - Storage* app, - File* file, - const uint32_t offset, - const bool from_start) { - bool ret = false; - StorageData* storage = get_storage_by_file(file, app->storage); - - if(storage == NULL) { - file->error_id = FSE_INVALID_PARAMETER; - } else { - FS_CALL(storage, file.seek(storage, file, offset, from_start)); - } - - return ret; -} - -static uint64_t storage_process_file_tell(Storage* app, File* file) { - uint64_t ret = 0; - StorageData* storage = get_storage_by_file(file, app->storage); - - if(storage == NULL) { - file->error_id = FSE_INVALID_PARAMETER; - } else { - FS_CALL(storage, file.tell(storage, file)); - } - - return ret; -} - -static bool storage_process_file_truncate(Storage* app, File* file) { - bool ret = false; - StorageData* storage = get_storage_by_file(file, app->storage); - - if(storage == NULL) { - file->error_id = FSE_INVALID_PARAMETER; - } else { - FS_CALL(storage, file.truncate(storage, file)); - } - - return ret; -} - -static bool storage_process_file_sync(Storage* app, File* file) { - bool ret = false; - StorageData* storage = get_storage_by_file(file, app->storage); - - if(storage == NULL) { - file->error_id = FSE_INVALID_PARAMETER; - } else { - FS_CALL(storage, file.sync(storage, file)); - } - - return ret; -} - -static uint64_t storage_process_file_size(Storage* app, File* file) { - uint64_t ret = 0; - StorageData* storage = get_storage_by_file(file, app->storage); - - if(storage == NULL) { - file->error_id = FSE_INVALID_PARAMETER; - } else { - FS_CALL(storage, file.size(storage, file)); - } - - return ret; -} - -static bool storage_process_file_eof(Storage* app, File* file) { - bool ret = false; - StorageData* storage = get_storage_by_file(file, app->storage); - - if(storage == NULL) { - file->error_id = FSE_INVALID_PARAMETER; - } else { - FS_CALL(storage, file.eof(storage, file)); - } - - return ret; -} - -/******************* Dir Functions *******************/ - -bool storage_process_dir_open(Storage* app, File* file, const char* path) { - bool ret = false; - StorageType type = storage_get_type_by_path(app, path); - StorageData* storage; - file->error_id = FSE_OK; - - if(storage_type_is_not_valid(type)) { - file->error_id = FSE_INVALID_NAME; - } else { - storage = storage_get_storage_by_type(app, type); - string_t real_path; - string_init_set(real_path, path); - storage_path_change_to_real_storage(real_path, type); - - if(storage_path_already_open(real_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))); - } - string_clear(real_path); - } - - return ret; -} - -bool storage_process_dir_close(Storage* app, File* file) { - bool ret = false; - StorageData* storage = get_storage_by_file(file, app->storage); - - if(storage == NULL) { - file->error_id = FSE_INVALID_PARAMETER; - } else { - FS_CALL(storage, dir.close(storage, file)); - storage_pop_storage_file(file, storage); - - StorageEvent event = {.type = StorageEventTypeDirClose}; - furi_pubsub_publish(app->pubsub, &event); - } - - return ret; -} - -bool storage_process_dir_read( - Storage* app, - File* file, - FileInfo* fileinfo, - char* name, - const uint16_t name_length) { - bool ret = false; - StorageData* storage = get_storage_by_file(file, app->storage); - - if(storage == NULL) { - file->error_id = FSE_INVALID_PARAMETER; - } else { - FS_CALL(storage, dir.read(storage, file, fileinfo, name, name_length)); - } - - return ret; -} - -bool storage_process_dir_rewind(Storage* app, File* file) { - bool ret = false; - StorageData* storage = get_storage_by_file(file, app->storage); - - if(storage == NULL) { - file->error_id = FSE_INVALID_PARAMETER; - } else { - FS_CALL(storage, dir.rewind(storage, file)); - } - - return ret; -} - -/******************* Common FS Functions *******************/ - -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); - - 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)); - } - - 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); - - string_t real_path; - string_init_set(real_path, path); - storage_path_change_to_real_storage(real_path, type); - - 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)) { - ret = FSE_ALREADY_OPEN; - break; - } - - FS_CALL(storage, common.remove(storage, remove_vfs(path))); - } while(false); - - string_clear(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); - - 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.mkdir(storage, remove_vfs(path))); - } - - return ret; -} - -static FS_Error storage_process_common_fs_info( - Storage* app, - const char* fs_path, - uint64_t* total_space, - uint64_t* free_space) { - FS_Error ret = FSE_OK; - StorageType type = storage_get_type_by_path(app, fs_path); - - 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)); - } - - return ret; -} - -/****************** Raw SD API ******************/ -// TODO think about implementing a custom storage API to split that kind of api linkage -#include "storages/storage_ext.h" - -static FS_Error storage_process_sd_format(Storage* app) { - FS_Error ret = FSE_OK; - - if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusNotReady) { - ret = FSE_NOT_READY; - } else { - ret = sd_format_card(&app->storage[ST_EXT]); - } - - return ret; -} - -static FS_Error storage_process_sd_unmount(Storage* app) { - FS_Error ret = FSE_OK; - - if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusNotReady) { - ret = FSE_NOT_READY; - } else { - sd_unmount_card(&app->storage[ST_EXT]); - } - - return ret; -} - -static FS_Error storage_process_sd_info(Storage* app, SDInfo* info) { - FS_Error ret = FSE_OK; - - if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusNotReady) { - ret = FSE_NOT_READY; - } else { - ret = sd_card_info(&app->storage[ST_EXT], info); - } - - return ret; -} - -static FS_Error storage_process_sd_status(Storage* app) { - FS_Error ret; - StorageStatus status = storage_data_status(&app->storage[ST_EXT]); - - switch(status) { - case StorageStatusOK: - ret = FSE_OK; - break; - case StorageStatusNotReady: - ret = FSE_NOT_READY; - break; - default: - ret = FSE_INTERNAL; - break; - } - - return ret; -} - -/****************** API calls processing ******************/ -void storage_process_message_internal(Storage* app, StorageMessage* message) { - switch(message->command) { - case StorageCommandFileOpen: - message->return_data->bool_value = storage_process_file_open( - app, - message->data->fopen.file, - message->data->fopen.path, - message->data->fopen.access_mode, - message->data->fopen.open_mode); - break; - case StorageCommandFileClose: - message->return_data->bool_value = - storage_process_file_close(app, message->data->fopen.file); - break; - case StorageCommandFileRead: - message->return_data->uint16_value = storage_process_file_read( - app, - message->data->fread.file, - message->data->fread.buff, - message->data->fread.bytes_to_read); - break; - case StorageCommandFileWrite: - message->return_data->uint16_value = storage_process_file_write( - app, - message->data->fwrite.file, - message->data->fwrite.buff, - message->data->fwrite.bytes_to_write); - break; - case StorageCommandFileSeek: - message->return_data->bool_value = storage_process_file_seek( - app, - message->data->fseek.file, - message->data->fseek.offset, - message->data->fseek.from_start); - break; - case StorageCommandFileTell: - message->return_data->uint64_value = - storage_process_file_tell(app, message->data->file.file); - break; - case StorageCommandFileTruncate: - message->return_data->bool_value = - storage_process_file_truncate(app, message->data->file.file); - break; - case StorageCommandFileSync: - message->return_data->bool_value = - storage_process_file_sync(app, message->data->file.file); - break; - case StorageCommandFileSize: - message->return_data->uint64_value = - storage_process_file_size(app, message->data->file.file); - break; - case StorageCommandFileEof: - message->return_data->bool_value = storage_process_file_eof(app, message->data->file.file); - break; - - case StorageCommandDirOpen: - message->return_data->bool_value = - storage_process_dir_open(app, message->data->dopen.file, message->data->dopen.path); - break; - case StorageCommandDirClose: - message->return_data->bool_value = - storage_process_dir_close(app, message->data->file.file); - break; - case StorageCommandDirRead: - message->return_data->bool_value = storage_process_dir_read( - app, - message->data->dread.file, - message->data->dread.fileinfo, - message->data->dread.name, - message->data->dread.name_length); - break; - case StorageCommandDirRewind: - message->return_data->bool_value = - storage_process_dir_rewind(app, message->data->file.file); - break; - case StorageCommandCommonStat: - message->return_data->error_value = storage_process_common_stat( - app, message->data->cstat.path, message->data->cstat.fileinfo); - break; - case StorageCommandCommonRemove: - message->return_data->error_value = - storage_process_common_remove(app, message->data->path.path); - break; - case StorageCommandCommonMkDir: - message->return_data->error_value = - storage_process_common_mkdir(app, message->data->path.path); - break; - case StorageCommandCommonFSInfo: - 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); - break; - case StorageCommandSDFormat: - message->return_data->error_value = storage_process_sd_format(app); - break; - case StorageCommandSDUnmount: - message->return_data->error_value = storage_process_sd_unmount(app); - break; - case StorageCommandSDInfo: - message->return_data->error_value = - storage_process_sd_info(app, message->data->sdinfo.info); - break; - case StorageCommandSDStatus: - message->return_data->error_value = storage_process_sd_status(app); - break; - } - - furi_semaphore_release(message->semaphore); -} - -void storage_process_message(Storage* app, StorageMessage* message) { - storage_process_message_internal(app, message); -} diff --git a/applications/storage/storage_sd_api.h b/applications/storage/storage_sd_api.h deleted file mode 100644 index f83360955e9..00000000000 --- a/applications/storage/storage_sd_api.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once -#include -#include "filesystem_api_defines.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define SD_LABEL_LENGTH 34 - -typedef enum { - FST_UNKNOWN, - FST_FAT12, - FST_FAT16, - FST_FAT32, - FST_EXFAT, -} SDFsType; - -typedef struct { - SDFsType fs_type; - uint32_t kb_total; - uint32_t kb_free; - uint16_t cluster_size; - uint16_t sector_size; - char label[SD_LABEL_LENGTH]; - FS_Error error; -} SDInfo; - -const char* sd_api_get_fs_type_text(SDFsType fs_type); - -#ifdef __cplusplus -} -#endif diff --git a/applications/storage/storage_test_app.c b/applications/storage/storage_test_app.c deleted file mode 100644 index 8bfa9826c6e..00000000000 --- a/applications/storage/storage_test_app.c +++ /dev/null @@ -1,342 +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) { - string_t str_path; - string_init_printf(str_path, "%s/test-folder", path); - - FURI_LOG_I(TAG, "--------- START \"%s\" ---------", path); - - // mkdir - FS_Error result = storage_common_mkdir(api, 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, 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)); - } - - string_clear(str_path); -} - -static void do_test_end(Storage* api, const char* path) { - uint64_t total_space; - uint64_t free_space; - string_t str_path_1; - string_t str_path_2; - string_init_printf(str_path_1, "%s/test-folder", path); - string_init_printf(str_path_2, "%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, string_get_cstr(str_path_1), 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, 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 - string_printf(str_path_1, "%s/test.txt", path); - string_printf(str_path_2, "%s/test2.txt", path); - - result = storage_common_rename(api, string_get_cstr(str_path_1), 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, 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)); - } - - string_clear(str_path_1); - string_clear(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/storage_settings/scenes/storage_settings_scene_unmounted.c b/applications/storage_settings/scenes/storage_settings_scene_unmounted.c deleted file mode 100644 index 486f07603ad..00000000000 --- a/applications/storage_settings/scenes/storage_settings_scene_unmounted.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "../storage_settings.h" - -static void - storage_settings_scene_unmounted_dialog_callback(DialogExResult result, void* context) { - StorageSettings* app = context; - - view_dispatcher_send_custom_event(app->view_dispatcher, result); -} - -void storage_settings_scene_unmounted_on_enter(void* context) { - StorageSettings* app = context; - FS_Error error = storage_sd_unmount(app->fs_api); - DialogEx* dialog_ex = app->dialog_ex; - - dialog_ex_set_center_button_text(dialog_ex, "OK"); - dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48); - - if(error == FSE_OK) { - dialog_ex_set_header(dialog_ex, "SD Card Unmounted", 64, 3, AlignCenter, AlignTop); - dialog_ex_set_text(dialog_ex, "You can remove\nSD card now.", 3, 22, AlignLeft, AlignTop); - notification_message(app->notification, &sequence_blink_green_100); - } else { - dialog_ex_set_header(dialog_ex, "Cannot Unmount SD Card", 64, 3, AlignCenter, AlignTop); - dialog_ex_set_text(dialog_ex, storage_error_get_desc(error), 3, 22, AlignLeft, AlignTop); - notification_message(app->notification, &sequence_blink_red_100); - } - - dialog_ex_set_context(dialog_ex, app); - dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_unmounted_dialog_callback); - - view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx); -} - -bool storage_settings_scene_unmounted_on_event(void* context, SceneManagerEvent event) { - StorageSettings* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case DialogExResultCenter: - consumed = scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, StorageSettingsStart); - break; - } - } else if(event.type == SceneManagerEventTypeBack) { - consumed = true; - } - - return consumed; -} - -void storage_settings_scene_unmounted_on_exit(void* context) { - StorageSettings* app = context; - DialogEx* dialog_ex = app->dialog_ex; - - dialog_ex_reset(dialog_ex); -} diff --git a/applications/subghz/application.fam b/applications/subghz/application.fam deleted file mode 100644 index 7d31ecae50e..00000000000 --- a/applications/subghz/application.fam +++ /dev/null @@ -1,24 +0,0 @@ -App( - appid="subghz", - name="Sub-GHz", - apptype=FlipperAppType.APP, - entry_point="subghz_app", - cdefines=["APP_SUBGHZ"], - requires=[ - "gui", - "cli", - "dialogs", - ], - provides=["subghz_start"], - icon="A_Sub1ghz_14", - stack_size=2 * 1024, - order=10, -) - -App( - appid="subghz_start", - apptype=FlipperAppType.STARTUP, - entry_point="subghz_on_system_start", - requires=["subghz"], - order=40, -) diff --git a/applications/subghz/helpers/subghz_testing.c b/applications/subghz/helpers/subghz_testing.c deleted file mode 100644 index 8afa868e012..00000000000 --- a/applications/subghz/helpers/subghz_testing.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "subghz_testing.h" - -const uint32_t subghz_frequencies_testing[] = { - /* 300 - 348 */ - 300000000, - 304500000, - 310000000, - 312025000, - 313250000, - 313625000, - 315000000, - 315225000, - 321950000, - 348000000, - /* 387 - 464 */ - 387000000, - 433075000, /* LPD433 first */ - 433825000, - 433920000, /* LPD433 mid */ - 434420000, - 434775000, /* LPD433 last channels */ - 438900000, - 464000000, - /* 779 - 928 */ - 779000000, - 868150000, - 868350000, - 868550000, - 915000000, - 925000000, - 926500000, - 927950000, - 928000000, -}; - -const uint32_t subghz_frequencies_count_testing = - sizeof(subghz_frequencies_testing) / sizeof(uint32_t); -const uint32_t subghz_frequencies_433_92_testing = 13; diff --git a/applications/subghz/helpers/subghz_testing.h b/applications/subghz/helpers/subghz_testing.h deleted file mode 100644 index 29ce578a010..00000000000 --- a/applications/subghz/helpers/subghz_testing.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once -#include "../subghz_i.h" - -extern const uint32_t subghz_frequencies_testing[]; -extern const uint32_t subghz_frequencies_count_testing; -extern const uint32_t subghz_frequencies_433_92_testing; diff --git a/applications/subghz/helpers/subghz_types.h b/applications/subghz/helpers/subghz_types.h deleted file mode 100644 index 8da4c8f53fe..00000000000 --- a/applications/subghz/helpers/subghz_types.h +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include "m-string.h" -#include -#include - -/** SubGhzNotification state */ -typedef enum { - SubGhzNotificationStateStarting, - SubGhzNotificationStateIDLE, - SubGhzNotificationStateTx, - SubGhzNotificationStateRx, - SubGhzNotificationStateRxDone, -} SubGhzNotificationState; - -/** SubGhzTxRx state */ -typedef enum { - SubGhzTxRxStateIDLE, - SubGhzTxRxStateRx, - SubGhzTxRxStateTx, - SubGhzTxRxStateSleep, -} SubGhzTxRxState; - -/** SubGhzHopperState state */ -typedef enum { - SubGhzHopperStateOFF, - SubGhzHopperStateRunnig, - SubGhzHopperStatePause, - SubGhzHopperStateRSSITimeOut, -} SubGhzHopperState; - -/** SubGhzRxKeyState state */ -typedef enum { - SubGhzRxKeyStateIDLE, - SubGhzRxKeyStateNoSave, - SubGhzRxKeyStateNeedSave, - SubGhzRxKeyStateBack, - SubGhzRxKeyStateStart, - SubGhzRxKeyStateAddKey, - SubGhzRxKeyStateExit, - SubGhzRxKeyStateRAWLoad, - SubGhzRxKeyStateRAWSave, -} SubGhzRxKeyState; - -/** SubGhzLoadKeyState state */ -typedef enum { - SubGhzLoadKeyStateUnknown, - SubGhzLoadKeyStateOK, - SubGhzLoadKeyStateParseErr, - SubGhzLoadKeyStateOnlyRx, -} SubGhzLoadKeyState; - -/** SubGhzLock */ -typedef enum { - SubGhzLockOff, - SubGhzLockOn, -} SubGhzLock; - -typedef enum { - SubGhzViewIdMenu, - SubGhzViewIdReceiver, - SubGhzViewIdPopup, - SubGhzViewIdTextInput, - SubGhzViewIdWidget, - SubGhzViewIdTransmitter, - SubGhzViewIdVariableItemList, - SubGhzViewIdFrequencyAnalyzer, - SubGhzViewIdReadRAW, - - SubGhzViewIdStatic, - SubGhzViewIdTestCarrier, - SubGhzViewIdTestPacket, -} SubGhzViewId; - -struct SubGhzPresetDefinition { - string_t name; - uint32_t frequency; - uint8_t* data; - size_t data_size; -}; - -typedef struct SubGhzPresetDefinition SubGhzPresetDefinition; diff --git a/applications/subghz/scenes/subghz_scene_delete.c b/applications/subghz/scenes/subghz_scene_delete.c deleted file mode 100644 index 43151de209c..00000000000 --- a/applications/subghz/scenes/subghz_scene_delete.c +++ /dev/null @@ -1,68 +0,0 @@ -#include "../subghz_i.h" -#include "../helpers/subghz_custom_event.h" - -void subghz_scene_delete_callback(GuiButtonType result, InputType type, void* context) { - furi_assert(context); - SubGhz* subghz = context; - if((result == GuiButtonTypeRight) && (type == InputTypeShort)) { - view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneDelete); - } -} - -void subghz_scene_delete_on_enter(void* context) { - SubGhz* subghz = context; - string_t frequency_str; - string_t modulation_str; - string_t text; - - string_init(frequency_str); - string_init(modulation_str); - string_init(text); - - subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); - widget_add_string_element( - subghz->widget, 78, 0, AlignLeft, AlignTop, FontSecondary, string_get_cstr(frequency_str)); - - widget_add_string_element( - subghz->widget, - 113, - 0, - AlignLeft, - AlignTop, - FontSecondary, - string_get_cstr(modulation_str)); - subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, text); - widget_add_string_multiline_element( - subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, string_get_cstr(text)); - - string_clear(frequency_str); - string_clear(modulation_str); - string_clear(text); - - widget_add_button_element( - subghz->widget, GuiButtonTypeRight, "Delete", subghz_scene_delete_callback, subghz); - - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); -} - -bool subghz_scene_delete_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventSceneDelete) { - string_set(subghz->file_path_tmp, subghz->file_path); - if(subghz_delete_file(subghz)) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess); - } else { - scene_manager_search_and_switch_to_previous_scene( - subghz->scene_manager, SubGhzSceneStart); - } - return true; - } - } - return false; -} - -void subghz_scene_delete_on_exit(void* context) { - SubGhz* subghz = context; - widget_reset(subghz->widget); -} diff --git a/applications/subghz/scenes/subghz_scene_more_raw.c b/applications/subghz/scenes/subghz_scene_more_raw.c deleted file mode 100644 index a5bade927ff..00000000000 --- a/applications/subghz/scenes/subghz_scene_more_raw.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "../subghz_i.h" - -enum SubmenuIndex { - SubmenuIndexEdit, - SubmenuIndexDelete, -}; - -void subghz_scene_more_raw_submenu_callback(void* context, uint32_t index) { - SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, index); -} - -void subghz_scene_more_raw_on_enter(void* context) { - SubGhz* subghz = context; - - submenu_add_item( - subghz->submenu, - "Rename", - SubmenuIndexEdit, - subghz_scene_more_raw_submenu_callback, - subghz); - - submenu_add_item( - subghz->submenu, - "Delete", - SubmenuIndexDelete, - subghz_scene_more_raw_submenu_callback, - subghz); - - submenu_set_selected_item( - subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneMoreRAW)); - - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdMenu); -} - -bool subghz_scene_more_raw_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexDelete) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexDelete); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteRAW); - return true; - } else if(event.event == SubmenuIndexEdit) { - string_reset(subghz->file_path_tmp); - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexEdit); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); - return true; - } - } - return false; -} - -void subghz_scene_more_raw_on_exit(void* context) { - SubGhz* subghz = context; - submenu_reset(subghz->submenu); -} diff --git a/applications/subghz/scenes/subghz_scene_read_raw.c b/applications/subghz/scenes/subghz_scene_read_raw.c deleted file mode 100644 index 38b73e07d03..00000000000 --- a/applications/subghz/scenes/subghz_scene_read_raw.c +++ /dev/null @@ -1,342 +0,0 @@ -#include "../subghz_i.h" -#include "../views/subghz_read_raw.h" -#include -#include -#include - -#define RAW_FILE_NAME "Raw_signal_" -#define TAG "SubGhzSceneReadRAW" - -bool subghz_scene_read_raw_update_filename(SubGhz* subghz) { - bool ret = false; - //set the path to read the file - string_t temp_str; - string_init(temp_str); - do { - if(!flipper_format_rewind(subghz->txrx->fff_data)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - - if(!flipper_format_read_string(subghz->txrx->fff_data, "File_name", temp_str)) { - FURI_LOG_E(TAG, "Missing File_name"); - break; - } - - string_set(subghz->file_path, temp_str); - - ret = true; - } while(false); - - string_clear(temp_str); - return ret; -} - -static void subghz_scene_read_raw_update_statusbar(void* context) { - furi_assert(context); - SubGhz* subghz = context; - - string_t frequency_str; - string_t modulation_str; - - string_init(frequency_str); - string_init(modulation_str); - - subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); - subghz_read_raw_add_data_statusbar( - subghz->subghz_read_raw, string_get_cstr(frequency_str), string_get_cstr(modulation_str)); - - string_clear(frequency_str); - string_clear(modulation_str); -} - -void subghz_scene_read_raw_callback(SubGhzCustomEvent event, void* context) { - furi_assert(context); - SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, event); -} - -void subghz_scene_read_raw_callback_end_tx(void* context) { - furi_assert(context); - SubGhz* subghz = context; - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzCustomEventViewReadRAWSendStop); -} - -void subghz_scene_read_raw_on_enter(void* context) { - SubGhz* subghz = context; - string_t file_name; - string_init(file_name); - - switch(subghz->txrx->rx_key_state) { - case SubGhzRxKeyStateBack: - subghz_read_raw_set_status(subghz->subghz_read_raw, SubGhzReadRAWStatusIDLE, ""); - break; - case SubGhzRxKeyStateRAWLoad: - path_extract_filename(subghz->file_path, file_name, true); - subghz_read_raw_set_status( - subghz->subghz_read_raw, SubGhzReadRAWStatusLoadKeyTX, string_get_cstr(file_name)); - subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; - break; - case SubGhzRxKeyStateRAWSave: - path_extract_filename(subghz->file_path, file_name, true); - subghz_read_raw_set_status( - subghz->subghz_read_raw, SubGhzReadRAWStatusSaveKey, string_get_cstr(file_name)); - subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; - break; - default: - subghz_read_raw_set_status(subghz->subghz_read_raw, SubGhzReadRAWStatusStart, ""); - subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; - break; - } - string_clear(file_name); - subghz_scene_read_raw_update_statusbar(subghz); - - //set callback view raw - subghz_read_raw_set_callback(subghz->subghz_read_raw, subghz_scene_read_raw_callback, subghz); - - subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( - subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME); - furi_assert(subghz->txrx->decoder_result); - - //set filter RAW feed - subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_RAW); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReadRAW); -} - -bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case SubGhzCustomEventViewReadRAWBack: - //Stop TX - if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { - subghz_tx_stop(subghz); - subghz_sleep(subghz); - } - //Stop RX - if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { - subghz_rx_end(subghz); - subghz_sleep(subghz); - }; - //Stop save file - subghz_protocol_raw_save_to_file_stop( - (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result); - subghz->state_notifications = SubGhzNotificationStateIDLE; - //needed save? - if((subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) || - (subghz->txrx->rx_key_state == SubGhzRxKeyStateBack)) { - subghz->txrx->rx_key_state = SubGhzRxKeyStateExit; - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); - } else { - //Restore default setting - subghz_preset_init( - subghz, - "AM650", - subghz_setting_get_default_frequency(subghz->setting), - NULL, - 0); - if(!scene_manager_search_and_switch_to_previous_scene( - subghz->scene_manager, SubGhzSceneSaved)) { - if(!scene_manager_search_and_switch_to_previous_scene( - subghz->scene_manager, SubGhzSceneStart)) { - scene_manager_stop(subghz->scene_manager); - view_dispatcher_stop(subghz->view_dispatcher); - } - } - } - consumed = true; - break; - - case SubGhzCustomEventViewReadRAWTXRXStop: - //Stop TX - if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { - subghz_tx_stop(subghz); - subghz_sleep(subghz); - } - //Stop RX - if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { - subghz_rx_end(subghz); - subghz_sleep(subghz); - }; - subghz->state_notifications = SubGhzNotificationStateIDLE; - consumed = true; - break; - - case SubGhzCustomEventViewReadRAWConfig: - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig); - consumed = true; - break; - - case SubGhzCustomEventViewReadRAWErase: - if(subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) { - if(subghz_scene_read_raw_update_filename(subghz)) { - string_set(subghz->file_path_tmp, subghz->file_path); - subghz_delete_file(subghz); - } - } - subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; - notification_message(subghz->notifications, &sequence_reset_rgb); - consumed = true; - break; - - case SubGhzCustomEventViewReadRAWMore: - if(subghz_scene_read_raw_update_filename(subghz)) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet); - subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW); - consumed = true; - } else { - furi_crash("SubGhz: RAW file name update error."); - } - break; - - case SubGhzCustomEventViewReadRAWSendStart: - - if(subghz_scene_read_raw_update_filename(subghz)) { - //start send - subghz->state_notifications = SubGhzNotificationStateIDLE; - 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)) { - subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); - } else { - DOLPHIN_DEED(DolphinDeedSubGhzSend); - // set callback end tx - subghz_protocol_raw_file_encoder_worker_set_callback_end( - (SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance( - subghz->txrx->transmitter), - subghz_scene_read_raw_callback_end_tx, - subghz); - subghz->state_notifications = SubGhzNotificationStateTx; - } - } - } - consumed = true; - break; - - case SubGhzCustomEventViewReadRAWSendStop: - subghz->state_notifications = SubGhzNotificationStateIDLE; - if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { - subghz_tx_stop(subghz); - subghz_sleep(subghz); - } - subghz_read_raw_stop_send(subghz->subghz_read_raw); - consumed = true; - break; - - case SubGhzCustomEventViewReadRAWIDLE: - if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { - subghz_rx_end(subghz); - subghz_sleep(subghz); - }; - - size_t spl_count = subghz_protocol_raw_get_sample_write( - (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result); - - subghz_protocol_raw_save_to_file_stop( - (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result); - - string_t temp_str; - string_init(temp_str); - string_printf( - temp_str, "%s/%s%s", SUBGHZ_RAW_FOLDER, RAW_FILE_NAME, SUBGHZ_APP_EXTENSION); - subghz_protocol_raw_gen_fff_data(subghz->txrx->fff_data, string_get_cstr(temp_str)); - string_clear(temp_str); - - if(spl_count > 0) { - notification_message(subghz->notifications, &sequence_set_green_255); - } else { - notification_message(subghz->notifications, &sequence_reset_rgb); - } - - subghz->state_notifications = SubGhzNotificationStateIDLE; - subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; - - consumed = true; - break; - - case SubGhzCustomEventViewReadRAWREC: - if(subghz->txrx->rx_key_state != SubGhzRxKeyStateIDLE) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); - } else { - //subghz_get_preset_name(subghz, subghz->error_str); - if(subghz_protocol_raw_save_to_file_init( - (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result, - RAW_FILE_NAME, - subghz->txrx->preset)) { - DOLPHIN_DEED(DolphinDeedSubGhzRawRec); - if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || - (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { - subghz_begin( - subghz, - subghz_setting_get_preset_data_by_name( - subghz->setting, string_get_cstr(subghz->txrx->preset->name))); - subghz_rx(subghz, subghz->txrx->preset->frequency); - } - subghz->state_notifications = SubGhzNotificationStateRx; - subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; - } else { - string_set_str(subghz->error_str, "Function requires\nan SD card."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); - } - } - consumed = true; - break; - - case SubGhzCustomEventViewReadRAWSave: - if(subghz_scene_read_raw_update_filename(subghz)) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSetRAW); - subghz->txrx->rx_key_state = SubGhzRxKeyStateBack; - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); - } - consumed = true; - break; - - default: - break; - } - } else if(event.type == SceneManagerEventTypeTick) { - switch(subghz->state_notifications) { - case SubGhzNotificationStateRx: - notification_message(subghz->notifications, &sequence_blink_cyan_10); - subghz_read_raw_update_sample_write( - subghz->subghz_read_raw, - subghz_protocol_raw_get_sample_write( - (SubGhzProtocolDecoderRAW*)subghz->txrx->decoder_result)); - subghz_read_raw_add_data_rssi(subghz->subghz_read_raw, furi_hal_subghz_get_rssi()); - break; - case SubGhzNotificationStateTx: - notification_message(subghz->notifications, &sequence_blink_magenta_10); - subghz_read_raw_update_sin(subghz->subghz_read_raw); - break; - default: - break; - } - } - return consumed; -} - -void subghz_scene_read_raw_on_exit(void* context) { - SubGhz* subghz = context; - - //Stop CC1101 - if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { - subghz_rx_end(subghz); - subghz_sleep(subghz); - }; - subghz->state_notifications = SubGhzNotificationStateIDLE; - notification_message(subghz->notifications, &sequence_reset_rgb); - - //filter restoration - subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); -} diff --git a/applications/subghz/scenes/subghz_scene_receiver.c b/applications/subghz/scenes/subghz_scene_receiver.c deleted file mode 100644 index 7c1f016d09a..00000000000 --- a/applications/subghz/scenes/subghz_scene_receiver.c +++ /dev/null @@ -1,230 +0,0 @@ -#include "../subghz_i.h" -#include "../views/receiver.h" - -static const NotificationSequence subghs_sequence_rx = { - &message_green_255, - - &message_vibro_on, - &message_note_c6, - &message_delay_50, - &message_sound_off, - &message_vibro_off, - - &message_delay_50, - NULL, -}; - -static const NotificationSequence subghs_sequence_rx_locked = { - &message_green_255, - - &message_display_backlight_on, - - &message_vibro_on, - &message_note_c6, - &message_delay_50, - &message_sound_off, - &message_vibro_off, - - &message_delay_500, - - &message_display_backlight_off, - NULL, -}; - -static void subghz_scene_receiver_update_statusbar(void* context) { - SubGhz* subghz = context; - string_t history_stat_str; - string_init(history_stat_str); - if(!subghz_history_get_text_space_left(subghz->txrx->history, history_stat_str)) { - string_t frequency_str; - string_t modulation_str; - - string_init(frequency_str); - string_init(modulation_str); - - subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); - - subghz_view_receiver_add_data_statusbar( - subghz->subghz_receiver, - string_get_cstr(frequency_str), - string_get_cstr(modulation_str), - string_get_cstr(history_stat_str)); - - string_clear(frequency_str); - string_clear(modulation_str); - } else { - subghz_view_receiver_add_data_statusbar( - subghz->subghz_receiver, string_get_cstr(history_stat_str), "", ""); - subghz->state_notifications = SubGhzNotificationStateIDLE; - } - string_clear(history_stat_str); -} - -void subghz_scene_receiver_callback(SubGhzCustomEvent event, void* context) { - furi_assert(context); - SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, event); -} - -static void subghz_scene_add_to_history_callback( - SubGhzReceiver* receiver, - SubGhzProtocolDecoderBase* decoder_base, - void* context) { - furi_assert(context); - SubGhz* subghz = context; - string_t str_buff; - string_init(str_buff); - - if(subghz_history_add_to_history(subghz->txrx->history, decoder_base, subghz->txrx->preset)) { - string_reset(str_buff); - - subghz->state_notifications = SubGhzNotificationStateRxDone; - - subghz_history_get_text_item_menu( - subghz->txrx->history, str_buff, subghz_history_get_item(subghz->txrx->history) - 1); - subghz_view_receiver_add_item_to_menu( - subghz->subghz_receiver, - string_get_cstr(str_buff), - subghz_history_get_type_protocol( - subghz->txrx->history, subghz_history_get_item(subghz->txrx->history) - 1)); - - subghz_scene_receiver_update_statusbar(subghz); - } - subghz_receiver_reset(receiver); - string_clear(str_buff); - subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; -} - -void subghz_scene_receiver_on_enter(void* context) { - SubGhz* subghz = context; - - string_t str_buff; - string_init(str_buff); - - if(subghz->txrx->rx_key_state == SubGhzRxKeyStateIDLE) { - subghz_preset_init( - subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); - subghz_history_reset(subghz->txrx->history); - subghz->txrx->rx_key_state = SubGhzRxKeyStateStart; - } - - subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz->lock); - - //Load history to receiver - subghz_view_receiver_exit(subghz->subghz_receiver); - for(uint8_t i = 0; i < subghz_history_get_item(subghz->txrx->history); i++) { - string_reset(str_buff); - subghz_history_get_text_item_menu(subghz->txrx->history, str_buff, i); - subghz_view_receiver_add_item_to_menu( - subghz->subghz_receiver, - string_get_cstr(str_buff), - subghz_history_get_type_protocol(subghz->txrx->history, i)); - subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey; - } - string_clear(str_buff); - subghz_scene_receiver_update_statusbar(subghz); - subghz_view_receiver_set_callback( - subghz->subghz_receiver, subghz_scene_receiver_callback, subghz); - subghz_receiver_set_rx_callback( - subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz); - - subghz->state_notifications = SubGhzNotificationStateRx; - if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { - subghz_rx_end(subghz); - }; - if((subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) || - (subghz->txrx->txrx_state == SubGhzTxRxStateSleep)) { - subghz_begin( - subghz, - subghz_setting_get_preset_data_by_name( - subghz->setting, string_get_cstr(subghz->txrx->preset->name))); - subghz_rx(subghz, subghz->txrx->preset->frequency); - } - subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen); - - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver); -} - -bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case SubGhzCustomEventViewReceiverBack: - // Stop CC1101 Rx - subghz->state_notifications = SubGhzNotificationStateIDLE; - if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { - subghz_rx_end(subghz); - subghz_sleep(subghz); - }; - subghz->txrx->hopper_state = SubGhzHopperStateOFF; - subghz->txrx->idx_menu_chosen = 0; - subghz_receiver_set_rx_callback(subghz->txrx->receiver, NULL, subghz); - - if(subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) { - subghz->txrx->rx_key_state = SubGhzRxKeyStateExit; - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving); - } else { - subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; - subghz_preset_init( - subghz, - "AM650", - subghz_setting_get_default_frequency(subghz->setting), - NULL, - 0); - scene_manager_search_and_switch_to_previous_scene( - subghz->scene_manager, SubGhzSceneStart); - } - consumed = true; - break; - case SubGhzCustomEventViewReceiverOK: - subghz->txrx->idx_menu_chosen = - subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); - consumed = true; - break; - case SubGhzCustomEventViewReceiverConfig: - subghz->state_notifications = SubGhzNotificationStateIDLE; - subghz->txrx->idx_menu_chosen = - subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig); - consumed = true; - break; - case SubGhzCustomEventViewReceiverOffDisplay: - notification_message(subghz->notifications, &sequence_display_backlight_off); - consumed = true; - break; - case SubGhzCustomEventViewReceiverUnlock: - subghz->lock = SubGhzLockOff; - consumed = true; - break; - default: - break; - } - } else if(event.type == SceneManagerEventTypeTick) { - if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { - subghz_hopper_update(subghz); - subghz_scene_receiver_update_statusbar(subghz); - } - switch(subghz->state_notifications) { - case SubGhzNotificationStateRx: - notification_message(subghz->notifications, &sequence_blink_cyan_10); - break; - case SubGhzNotificationStateRxDone: - if(subghz->lock != SubGhzLockOn) { - notification_message(subghz->notifications, &subghs_sequence_rx); - } else { - notification_message(subghz->notifications, &subghs_sequence_rx_locked); - } - subghz->state_notifications = SubGhzNotificationStateRx; - break; - default: - break; - } - } - return consumed; -} - -void subghz_scene_receiver_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/subghz/scenes/subghz_scene_receiver_config.c b/applications/subghz/scenes/subghz_scene_receiver_config.c deleted file mode 100644 index c59630f7ea4..00000000000 --- a/applications/subghz/scenes/subghz_scene_receiver_config.c +++ /dev/null @@ -1,229 +0,0 @@ -#include "../subghz_i.h" - -enum SubGhzSettingIndex { - SubGhzSettingIndexFrequency, - SubGhzSettingIndexHopping, - SubGhzSettingIndexModulation, - SubGhzSettingIndexLock, -}; - -#define HOPPING_COUNT 2 -const char* const hopping_text[HOPPING_COUNT] = { - "OFF", - "ON", -}; -const uint32_t hopping_value[HOPPING_COUNT] = { - SubGhzHopperStateOFF, - SubGhzHopperStateRunnig, -}; - -uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) { - furi_assert(context); - SubGhz* subghz = context; - uint8_t index = 0; - for(uint8_t i = 0; i < subghz_setting_get_frequency_count(subghz->setting); i++) { - if(value == subghz_setting_get_frequency(subghz->setting, i)) { - index = i; - break; - } else { - index = subghz_setting_get_frequency_default_index(subghz->setting); - } - } - return index; -} - -uint8_t subghz_scene_receiver_config_next_preset(const char* preset_name, void* context) { - furi_assert(context); - SubGhz* subghz = context; - uint8_t index = 0; - for(uint8_t i = 0; i < subghz_setting_get_preset_count(subghz->setting); i++) { - if(!strcmp(subghz_setting_get_preset_name(subghz->setting, i), preset_name)) { - index = i; - break; - } else { - // index = subghz_setting_get_frequency_default_index(subghz->setting); - } - } - return index; -} - -uint8_t subghz_scene_receiver_config_hopper_value_index( - const uint32_t value, - const uint32_t values[], - uint8_t values_count, - void* context) { - furi_assert(context); - UNUSED(values_count); - SubGhz* subghz = context; - - if(value == values[0]) { - return 0; - } else { - variable_item_set_current_value_text( - (VariableItem*)scene_manager_get_scene_state( - subghz->scene_manager, SubGhzSceneReceiverConfig), - " -----"); - return 1; - } -} - -static void subghz_scene_receiver_config_set_frequency(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - if(subghz->txrx->hopper_state == SubGhzHopperStateOFF) { - char text_buf[10] = {0}; - snprintf( - text_buf, - sizeof(text_buf), - "%lu.%02lu", - subghz_setting_get_frequency(subghz->setting, index) / 1000000, - (subghz_setting_get_frequency(subghz->setting, index) % 1000000) / 10000); - variable_item_set_current_value_text(item, text_buf); - subghz->txrx->preset->frequency = subghz_setting_get_frequency(subghz->setting, index); - } else { - variable_item_set_current_value_index( - item, subghz_setting_get_frequency_default_index(subghz->setting)); - } -} - -static void subghz_scene_receiver_config_set_preset(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text( - item, subghz_setting_get_preset_name(subghz->setting, index)); - subghz_preset_init( - subghz, - subghz_setting_get_preset_name(subghz->setting, index), - subghz->txrx->preset->frequency, - subghz_setting_get_preset_data(subghz->setting, index), - subghz_setting_get_preset_data_size(subghz->setting, index)); -} - -static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, hopping_text[index]); - if(hopping_value[index] == SubGhzHopperStateOFF) { - char text_buf[10] = {0}; - snprintf( - text_buf, - sizeof(text_buf), - "%lu.%02lu", - subghz_setting_get_default_frequency(subghz->setting) / 1000000, - (subghz_setting_get_default_frequency(subghz->setting) % 1000000) / 10000); - variable_item_set_current_value_text( - (VariableItem*)scene_manager_get_scene_state( - subghz->scene_manager, SubGhzSceneReceiverConfig), - text_buf); - subghz->txrx->preset->frequency = subghz_setting_get_default_frequency(subghz->setting); - variable_item_set_current_value_index( - (VariableItem*)scene_manager_get_scene_state( - subghz->scene_manager, SubGhzSceneReceiverConfig), - subghz_setting_get_frequency_default_index(subghz->setting)); - } else { - variable_item_set_current_value_text( - (VariableItem*)scene_manager_get_scene_state( - subghz->scene_manager, SubGhzSceneReceiverConfig), - " -----"); - variable_item_set_current_value_index( - (VariableItem*)scene_manager_get_scene_state( - subghz->scene_manager, SubGhzSceneReceiverConfig), - subghz_setting_get_frequency_default_index(subghz->setting)); - } - - subghz->txrx->hopper_state = hopping_value[index]; -} - -static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { - furi_assert(context); - SubGhz* subghz = context; - if(index == SubGhzSettingIndexLock) { - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzCustomEventSceneSettingLock); - } -} - -void subghz_scene_receiver_config_on_enter(void* context) { - SubGhz* subghz = context; - VariableItem* item; - uint8_t value_index; - - item = variable_item_list_add( - subghz->variable_item_list, - "Frequency:", - subghz_setting_get_frequency_count(subghz->setting), - subghz_scene_receiver_config_set_frequency, - subghz); - value_index = - subghz_scene_receiver_config_next_frequency(subghz->txrx->preset->frequency, subghz); - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneReceiverConfig, (uint32_t)item); - variable_item_set_current_value_index(item, value_index); - char text_buf[10] = {0}; - snprintf( - text_buf, - sizeof(text_buf), - "%lu.%02lu", - subghz_setting_get_frequency(subghz->setting, value_index) / 1000000, - (subghz_setting_get_frequency(subghz->setting, value_index) % 1000000) / 10000); - variable_item_set_current_value_text(item, text_buf); - - if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != - SubGhzCustomEventManagerSet) { - item = variable_item_list_add( - subghz->variable_item_list, - "Hopping:", - HOPPING_COUNT, - subghz_scene_receiver_config_set_hopping_running, - subghz); - value_index = subghz_scene_receiver_config_hopper_value_index( - subghz->txrx->hopper_state, hopping_value, HOPPING_COUNT, subghz); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, hopping_text[value_index]); - } - - item = variable_item_list_add( - subghz->variable_item_list, - "Modulation:", - subghz_setting_get_preset_count(subghz->setting), - subghz_scene_receiver_config_set_preset, - subghz); - value_index = subghz_scene_receiver_config_next_preset( - string_get_cstr(subghz->txrx->preset->name), subghz); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text( - item, subghz_setting_get_preset_name(subghz->setting, value_index)); - - if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != - SubGhzCustomEventManagerSet) { - variable_item_list_add(subghz->variable_item_list, "Lock Keyboard", 1, NULL, NULL); - variable_item_list_set_enter_callback( - subghz->variable_item_list, - subghz_scene_receiver_config_var_list_enter_callback, - subghz); - } - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); -} - -bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventSceneSettingLock) { - subghz->lock = SubGhzLockOn; - scene_manager_previous_scene(subghz->scene_manager); - consumed = true; - } - } - return consumed; -} - -void subghz_scene_receiver_config_on_exit(void* context) { - SubGhz* subghz = context; - variable_item_list_reset(subghz->variable_item_list); - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); -} diff --git a/applications/subghz/scenes/subghz_scene_receiver_info.c b/applications/subghz/scenes/subghz_scene_receiver_info.c deleted file mode 100644 index 6f3e6fd1f9c..00000000000 --- a/applications/subghz/scenes/subghz_scene_receiver_info.c +++ /dev/null @@ -1,203 +0,0 @@ -#include "../subghz_i.h" -#include "../helpers/subghz_custom_event.h" -#include - -void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, void* context) { - furi_assert(context); - SubGhz* subghz = context; - - if((result == GuiButtonTypeCenter) && (type == InputTypePress)) { - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoTxStart); - } else if((result == GuiButtonTypeCenter) && (type == InputTypeRelease)) { - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoTxStop); - } else if((result == GuiButtonTypeRight) && (type == InputTypeShort)) { - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzCustomEventSceneReceiverInfoSave); - } -} - -static bool subghz_scene_receiver_info_update_parser(void* context) { - SubGhz* subghz = 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) { - subghz_protocol_decoder_base_deserialize( - subghz->txrx->decoder_result, - subghz_history_get_raw_data(subghz->txrx->history, subghz->txrx->idx_menu_chosen)); - - SubGhzPresetDefinition* preset = - subghz_history_get_preset_def(subghz->txrx->history, subghz->txrx->idx_menu_chosen); - subghz_preset_init( - subghz, - string_get_cstr(preset->name), - preset->frequency, - preset->data, - preset->data_size); - - return true; - } - return false; -} - -void subghz_scene_receiver_info_on_enter(void* context) { - SubGhz* subghz = context; - - DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo); - if(subghz_scene_receiver_info_update_parser(subghz)) { - string_t frequency_str; - string_t modulation_str; - string_t text; - - string_init(frequency_str); - string_init(modulation_str); - string_init(text); - - subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); - widget_add_string_element( - subghz->widget, - 78, - 0, - AlignLeft, - AlignTop, - FontSecondary, - string_get_cstr(frequency_str)); - - widget_add_string_element( - subghz->widget, - 113, - 0, - AlignLeft, - AlignTop, - FontSecondary, - string_get_cstr(modulation_str)); - subghz_protocol_decoder_base_get_string(subghz->txrx->decoder_result, text); - widget_add_string_multiline_element( - subghz->widget, 0, 0, AlignLeft, AlignTop, FontSecondary, string_get_cstr(text)); - - string_clear(frequency_str); - string_clear(modulation_str); - string_clear(text); - - if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) == - SubGhzProtocolFlag_Save) { - widget_add_button_element( - subghz->widget, - GuiButtonTypeRight, - "Save", - subghz_scene_receiver_info_callback, - subghz); - } - if(((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) == - SubGhzProtocolFlag_Send) && - subghz->txrx->decoder_result->protocol->encoder->deserialize && - subghz->txrx->decoder_result->protocol->type == SubGhzProtocolTypeStatic) { - widget_add_button_element( - subghz->widget, - GuiButtonTypeCenter, - "Send", - subghz_scene_receiver_info_callback, - subghz); - } - } else { - widget_add_icon_element(subghz->widget, 37, 15, &I_DolphinCommon_56x48); - widget_add_string_element( - subghz->widget, 13, 8, AlignLeft, AlignBottom, FontSecondary, "Error history parse."); - } - - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); -} - -bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventSceneReceiverInfoTxStart) { - //CC1101 Stop RX -> Start TX - if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { - subghz->txrx->hopper_state = SubGhzHopperStatePause; - } - if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { - subghz_rx_end(subghz); - } - if(!subghz_scene_receiver_info_update_parser(subghz)) { - return false; - } - if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE || - subghz->txrx->txrx_state == SubGhzTxRxStateSleep) { - if(!subghz_tx_start( - subghz, - subghz_history_get_raw_data( - subghz->txrx->history, subghz->txrx->idx_menu_chosen))) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); - } else { - subghz->state_notifications = SubGhzNotificationStateTx; - } - } - return true; - } else if(event.event == SubGhzCustomEventSceneReceiverInfoTxStop) { - //CC1101 Stop Tx -> Start RX - subghz->state_notifications = SubGhzNotificationStateIDLE; - if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { - subghz_tx_stop(subghz); - } - if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { - subghz_begin( - subghz, - subghz_setting_get_preset_data_by_name( - subghz->setting, string_get_cstr(subghz->txrx->preset->name))); - subghz_rx(subghz, subghz->txrx->preset->frequency); - } - if(subghz->txrx->hopper_state == SubGhzHopperStatePause) { - subghz->txrx->hopper_state = SubGhzHopperStateRunnig; - } - subghz->state_notifications = SubGhzNotificationStateRx; - return true; - } else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) { - //CC1101 Stop RX -> Save - subghz->state_notifications = SubGhzNotificationStateIDLE; - if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { - subghz->txrx->hopper_state = SubGhzHopperStateOFF; - } - if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { - subghz_rx_end(subghz); - subghz_sleep(subghz); - } - if(!subghz_scene_receiver_info_update_parser(subghz)) { - return false; - } - - if((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Save) == - SubGhzProtocolFlag_Save) { - subghz_file_name_clear(subghz); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); - } - return true; - } - } else if(event.type == SceneManagerEventTypeTick) { - if(subghz->txrx->hopper_state != SubGhzHopperStateOFF) { - subghz_hopper_update(subghz); - } - switch(subghz->state_notifications) { - case SubGhzNotificationStateTx: - notification_message(subghz->notifications, &sequence_blink_magenta_10); - break; - case SubGhzNotificationStateRx: - notification_message(subghz->notifications, &sequence_blink_cyan_10); - break; - case SubGhzNotificationStateRxDone: - notification_message(subghz->notifications, &sequence_blink_green_100); - subghz->state_notifications = SubGhzNotificationStateRx; - break; - default: - break; - } - } - return false; -} - -void subghz_scene_receiver_info_on_exit(void* context) { - SubGhz* subghz = context; - widget_reset(subghz->widget); -} diff --git a/applications/subghz/scenes/subghz_scene_rpc.c b/applications/subghz/scenes/subghz_scene_rpc.c deleted file mode 100644 index 652499d0576..00000000000 --- a/applications/subghz/scenes/subghz_scene_rpc.c +++ /dev/null @@ -1,100 +0,0 @@ -#include "../subghz_i.h" - -typedef enum { - SubGhzRpcStateIdle, - SubGhzRpcStateLoaded, -} SubGhzRpcState; - -void subghz_scene_rpc_on_enter(void* context) { - SubGhz* subghz = context; - Popup* popup = subghz->popup; - - popup_set_header(popup, "Sub-GHz", 89, 42, AlignCenter, AlignBottom); - popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop); - - popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61); - - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdPopup); - - scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateIdle); - - notification_message(subghz->notifications, &sequence_display_backlight_on); -} - -bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - Popup* popup = subghz->popup; - bool consumed = false; - SubGhzRpcState state = scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneRpc); - - if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == SubGhzCustomEventSceneExit) { - scene_manager_stop(subghz->scene_manager); - view_dispatcher_stop(subghz->view_dispatcher); - rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventAppExit, true); - } else if(event.event == SubGhzCustomEventSceneRpcSessionClose) { - scene_manager_stop(subghz->scene_manager); - view_dispatcher_stop(subghz->view_dispatcher); - } else if(event.event == SubGhzCustomEventSceneRpcButtonPress) { - bool result = false; - if((subghz->txrx->txrx_state == SubGhzTxRxStateSleep) && - (state == SubGhzRpcStateLoaded)) { - subghz_blink_start(subghz); - result = subghz_tx_start(subghz, subghz->txrx->fff_data); - result = true; - } - rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result); - } else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) { - bool result = false; - if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { - subghz_blink_stop(subghz); - subghz_tx_stop(subghz); - subghz_sleep(subghz); - result = true; - } - rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonRelease, result); - } else if(event.event == SubGhzCustomEventSceneRpcLoad) { - bool result = false; - const char* arg = rpc_system_app_get_data(subghz->rpc_ctx); - if(arg && (state == SubGhzRpcStateIdle)) { - if(subghz_key_load(subghz, arg, false)) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateLoaded); - string_set_str(subghz->file_path, arg); - result = true; - string_t file_name; - string_init(file_name); - path_extract_filename(subghz->file_path, file_name, true); - - snprintf( - subghz->file_name_tmp, - SUBGHZ_MAX_LEN_NAME, - "loaded\n%s", - string_get_cstr(file_name)); - popup_set_text(popup, subghz->file_name_tmp, 89, 44, AlignCenter, AlignTop); - - string_clear(file_name); - } - } - rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventLoadFile, result); - } - } - return consumed; -} - -void subghz_scene_rpc_on_exit(void* context) { - SubGhz* subghz = context; - - if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { - subghz_tx_stop(subghz); - subghz_sleep(subghz); - subghz_blink_stop(subghz); - } - - Popup* popup = subghz->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); -} diff --git a/applications/subghz/scenes/subghz_scene_save_name.c b/applications/subghz/scenes/subghz_scene_save_name.c deleted file mode 100644 index 12ec9868187..00000000000 --- a/applications/subghz/scenes/subghz_scene_save_name.c +++ /dev/null @@ -1,141 +0,0 @@ -#include "../subghz_i.h" -#include "m-string.h" -#include "subghz/types.h" -#include -#include "../helpers/subghz_custom_event.h" -#include -#include - -#define MAX_TEXT_INPUT_LEN 22 - -void subghz_scene_save_name_text_input_callback(void* context) { - furi_assert(context); - SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneSaveName); -} - -void subghz_scene_save_name_on_enter(void* context) { - SubGhz* subghz = context; - - // Setup view - TextInput* text_input = subghz->text_input; - bool dev_name_empty = false; - - string_t file_name; - string_t dir_name; - string_init(file_name); - string_init(dir_name); - - if(!subghz_path_is_file(subghz->file_path)) { - char file_name_buf[SUBGHZ_MAX_LEN_NAME] = {0}; - set_random_name(file_name_buf, SUBGHZ_MAX_LEN_NAME); - string_set_str(file_name, file_name_buf); - string_set_str(subghz->file_path, SUBGHZ_APP_FOLDER); - //highlighting the entire filename by default - dev_name_empty = true; - } else { - string_set(subghz->file_path_tmp, subghz->file_path); - path_extract_dirname(string_get_cstr(subghz->file_path), dir_name); - path_extract_filename(subghz->file_path, file_name, true); - if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != - SubGhzCustomEventManagerNoSet) { - if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == - SubGhzCustomEventManagerSetRAW) { - dev_name_empty = true; - subghz_get_next_name_file(subghz, SUBGHZ_MAX_LEN_NAME); - } - path_extract_filename(subghz->file_path, file_name, true); - } - string_set(subghz->file_path, dir_name); - } - - strncpy(subghz->file_name_tmp, string_get_cstr(file_name), SUBGHZ_MAX_LEN_NAME); - text_input_set_header_text(text_input, "Name signal"); - text_input_set_result_callback( - text_input, - subghz_scene_save_name_text_input_callback, - subghz, - subghz->file_name_tmp, - MAX_TEXT_INPUT_LEN, // buffer size - dev_name_empty); - - ValidatorIsFile* validator_is_file = - validator_is_file_alloc_init(string_get_cstr(subghz->file_path), SUBGHZ_APP_EXTENSION, ""); - text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); - - string_clear(file_name); - string_clear(dir_name); - - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTextInput); -} - -bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - if(event.type == SceneManagerEventTypeBack) { - if(!strcmp(subghz->file_name_tmp, "") || - scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != - SubGhzCustomEventManagerNoSet) { - string_set(subghz->file_path, subghz->file_path_tmp); - } - scene_manager_previous_scene(subghz->scene_manager); - return true; - } else if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventSceneSaveName) { - if(strcmp(subghz->file_name_tmp, "")) { - string_cat_printf( - subghz->file_path, "/%s%s", subghz->file_name_tmp, SUBGHZ_APP_EXTENSION); - if(subghz_path_is_file(subghz->file_path_tmp)) { - if(!subghz_rename_file(subghz)) { - return false; - } - } else { - if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType) != - SubGhzCustomEventManagerNoSet) { - subghz_save_protocol_to_file( - subghz, subghz->txrx->fff_data, string_get_cstr(subghz->file_path)); - scene_manager_set_scene_state( - subghz->scene_manager, - SubGhzSceneSetType, - SubGhzCustomEventManagerNoSet); - } else { - subghz_save_protocol_to_file( - subghz, - subghz_history_get_raw_data( - subghz->txrx->history, subghz->txrx->idx_menu_chosen), - string_get_cstr(subghz->file_path)); - } - } - - if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != - SubGhzCustomEventManagerNoSet) { - subghz_protocol_raw_gen_fff_data( - subghz->txrx->fff_data, string_get_cstr(subghz->file_path)); - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); - } else { - subghz_file_name_clear(subghz); - } - - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess); - return true; - } else { - string_set_str(subghz->error_str, "No name file"); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); - return true; - } - } - } - return false; -} - -void subghz_scene_save_name_on_exit(void* context) { - SubGhz* subghz = context; - - // Clear validator - void* validator_context = text_input_get_validator_callback_context(subghz->text_input); - text_input_set_validator(subghz->text_input, NULL, NULL); - validator_is_file_free(validator_context); - - // Clear view - text_input_reset(subghz->text_input); -} diff --git a/applications/subghz/scenes/subghz_scene_set_type.c b/applications/subghz/scenes/subghz_scene_set_type.c deleted file mode 100644 index 1a359542ef3..00000000000 --- a/applications/subghz/scenes/subghz_scene_set_type.c +++ /dev/null @@ -1,396 +0,0 @@ -#include "../subghz_i.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#define TAG "SubGhzSetType" - -bool subghz_scene_set_type_submenu_gen_data_protocol( - void* context, - const char* protocol_name, - uint64_t key, - uint32_t bit, - uint32_t frequency, - const char* preset_name) { - furi_assert(context); - SubGhz* subghz = context; - - bool res = false; - - subghz_preset_init(subghz, preset_name, frequency, NULL, 0); - subghz->txrx->decoder_result = - subghz_receiver_search_decoder_base_by_name(subghz->txrx->receiver, protocol_name); - - if(subghz->txrx->decoder_result == NULL) { - string_set_str(subghz->error_str, "Protocol not\nfound!"); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); - return false; - } - - 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)) { - FURI_LOG_E(TAG, "Unable to serialize"); - break; - } - if(!flipper_format_update_uint32(subghz->txrx->fff_data, "Bit", &bit, 1)) { - FURI_LOG_E(TAG, "Unable to update Bit"); - break; - } - - uint8_t key_data[sizeof(uint64_t)] = {0}; - for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (key >> i * 8) & 0xFF; - } - if(!flipper_format_update_hex(subghz->txrx->fff_data, "Key", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Unable to update Key"); - break; - } - res = true; - } while(false); - return res; -} - -void subghz_scene_set_type_submenu_callback(void* context, uint32_t index) { - SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, index); -} - -void subghz_scene_set_type_on_enter(void* context) { - SubGhz* subghz = context; - - submenu_add_item( - subghz->submenu, - "Princeton_433", - SubmenuIndexPricenton, - subghz_scene_set_type_submenu_callback, - subghz); - submenu_add_item( - subghz->submenu, - "Nice Flo 12bit_433", - SubmenuIndexNiceFlo12bit, - subghz_scene_set_type_submenu_callback, - subghz); - submenu_add_item( - subghz->submenu, - "Nice Flo 24bit_433", - SubmenuIndexNiceFlo24bit, - subghz_scene_set_type_submenu_callback, - subghz); - submenu_add_item( - subghz->submenu, - "CAME 12bit_433", - SubmenuIndexCAME12bit, - subghz_scene_set_type_submenu_callback, - subghz); - submenu_add_item( - subghz->submenu, - "CAME 24bit_433", - SubmenuIndexCAME24bit, - subghz_scene_set_type_submenu_callback, - subghz); - submenu_add_item( - subghz->submenu, - "Linear_300", - SubmenuIndexLinear_300_00, - subghz_scene_set_type_submenu_callback, - subghz); - submenu_add_item( - subghz->submenu, - "CAME TWEE", - SubmenuIndexCAMETwee, - subghz_scene_set_type_submenu_callback, - subghz); - // submenu_add_item( - // subghz->submenu, "Nero Sketch", SubmenuIndexNeroSketch, subghz_scene_set_type_submenu_callback, subghz); - // submenu_add_item( - // subghz->submenu, "Nero Radio", SubmenuIndexNeroRadio, subghz_scene_set_type_submenu_callback, subghz); - submenu_add_item( - subghz->submenu, - "Gate TX_433", - SubmenuIndexGateTX, - subghz_scene_set_type_submenu_callback, - subghz); - submenu_add_item( - subghz->submenu, - "DoorHan_315", - SubmenuIndexDoorHan_315_00, - subghz_scene_set_type_submenu_callback, - subghz); - submenu_add_item( - subghz->submenu, - "DoorHan_433", - SubmenuIndexDoorHan_433_92, - subghz_scene_set_type_submenu_callback, - subghz); - submenu_add_item( - subghz->submenu, - "LiftMaster_315", - SubmenuIndexLiftMaster_315_00, - subghz_scene_set_type_submenu_callback, - subghz); - submenu_add_item( - subghz->submenu, - "LiftMaster_390", - SubmenuIndexLiftMaster_390_00, - subghz_scene_set_type_submenu_callback, - subghz); - submenu_add_item( - subghz->submenu, - "Security+2.0_310", - SubmenuIndexSecPlus_v2_310_00, - subghz_scene_set_type_submenu_callback, - subghz); - submenu_add_item( - subghz->submenu, - "Security+2.0_315", - SubmenuIndexSecPlus_v2_315_00, - subghz_scene_set_type_submenu_callback, - subghz); - submenu_add_item( - subghz->submenu, - "Security+2.0_390", - SubmenuIndexSecPlus_v2_390_00, - subghz_scene_set_type_submenu_callback, - subghz); - - submenu_set_selected_item( - subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType)); - - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdMenu); -} - -bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - bool generated_protocol = false; - - if(event.type == SceneManagerEventTypeCustom) { - //ToDo Fix - uint32_t key = subghz_random_serial(); - switch(event.event) { - case SubmenuIndexPricenton: - key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 - if(subghz_scene_set_type_submenu_gen_data_protocol( - subghz, SUBGHZ_PROTOCOL_PRINCETON_NAME, key, 24, 433920000, "AM650")) { - uint32_t te = 400; - flipper_format_update_uint32(subghz->txrx->fff_data, "TE", (uint32_t*)&te, 1); - generated_protocol = true; - } - break; - case SubmenuIndexNiceFlo12bit: - key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4 - if(subghz_scene_set_type_submenu_gen_data_protocol( - subghz, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 12, 433920000, "AM650")) { - generated_protocol = true; - } - break; - case SubmenuIndexNiceFlo24bit: - key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 - if(subghz_scene_set_type_submenu_gen_data_protocol( - subghz, SUBGHZ_PROTOCOL_NICE_FLO_NAME, key, 24, 433920000, "AM650")) { - generated_protocol = true; - } - break; - case SubmenuIndexCAME12bit: - key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4 - if(subghz_scene_set_type_submenu_gen_data_protocol( - subghz, SUBGHZ_PROTOCOL_CAME_NAME, key, 12, 433920000, "AM650")) { - generated_protocol = true; - } - break; - case SubmenuIndexCAME24bit: - key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 - if(subghz_scene_set_type_submenu_gen_data_protocol( - subghz, SUBGHZ_PROTOCOL_CAME_NAME, key, 24, 433920000, "AM650")) { - generated_protocol = true; - } - break; - case SubmenuIndexLinear_300_00: - key = (key & 0x3FF); - if(subghz_scene_set_type_submenu_gen_data_protocol( - subghz, SUBGHZ_PROTOCOL_LINEAR_NAME, key, 10, 300000000, "AM650")) { - generated_protocol = true; - } - break; - case SubmenuIndexCAMETwee: - key = (key & 0x0FFFFFF0); - key = 0x003FFF7200000000 | (key ^ 0xE0E0E0EE); - if(subghz_scene_set_type_submenu_gen_data_protocol( - subghz, SUBGHZ_PROTOCOL_CAME_TWEE_NAME, key, 54, 433920000, "AM650")) { - generated_protocol = true; - } - break; - // case SubmenuIndexNeroSketch: - // /* code */ - // break; - // case SubmenuIndexNeroRadio: - // /* code */ - // break; - case SubmenuIndexGateTX: - key = (key & 0x00F0FF00) | 0xF << 16 | 0x40; //btn 0xF, 0xC, 0xA, 0x6 (?) - uint64_t rev_key = subghz_protocol_blocks_reverse_key(key, 24); - if(subghz_scene_set_type_submenu_gen_data_protocol( - subghz, SUBGHZ_PROTOCOL_GATE_TX_NAME, rev_key, 24, 433920000, "AM650")) { - generated_protocol = true; - } - 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); - if(subghz->txrx->transmitter) { - subghz_protocol_keeloq_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - key & 0x0FFFFFFF, - 0x2, - 0x0003, - "DoorHan", - subghz->txrx->preset); - generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - if(!generated_protocol) { - string_set_str( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); - } - break; - case SubmenuIndexDoorHan_315_00: - subghz->txrx->transmitter = subghz_transmitter_alloc_init( - subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); - subghz_preset_init(subghz, "AM650", 315000000, NULL, 0); - if(subghz->txrx->transmitter) { - subghz_protocol_keeloq_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - key & 0x0FFFFFFF, - 0x2, - 0x0003, - "DoorHan", - subghz->txrx->preset); - generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - if(!generated_protocol) { - string_set_str( - subghz->error_str, "Function requires\nan SD card with\nfresh databases."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); - } - break; - case SubmenuIndexLiftMaster_315_00: - while(!subghz_protocol_secplus_v1_check_fixed(key)) { - key = subghz_random_serial(); - } - if(subghz_scene_set_type_submenu_gen_data_protocol( - subghz, - SUBGHZ_PROTOCOL_SECPLUS_V1_NAME, - (uint64_t)key << 32 | 0xE6000000, - 42, - 315000000, - "AM650")) { - generated_protocol = true; - } - break; - case SubmenuIndexLiftMaster_390_00: - while(!subghz_protocol_secplus_v1_check_fixed(key)) { - key = subghz_random_serial(); - } - if(subghz_scene_set_type_submenu_gen_data_protocol( - subghz, - SUBGHZ_PROTOCOL_SECPLUS_V1_NAME, - (uint64_t)key << 32 | 0xE6000000, - 42, - 390000000, - "AM650")) { - generated_protocol = true; - } - break; - case SubmenuIndexSecPlus_v2_310_00: - subghz->txrx->transmitter = subghz_transmitter_alloc_init( - subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); - subghz_preset_init(subghz, "AM650", 310000000, NULL, 0); - if(subghz->txrx->transmitter) { - subghz_protocol_secplus_v2_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - key, - 0x68, - 0xE500000, - subghz->txrx->preset); - generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - break; - case SubmenuIndexSecPlus_v2_315_00: - subghz->txrx->transmitter = subghz_transmitter_alloc_init( - subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); - subghz_preset_init(subghz, "AM650", 315000000, NULL, 0); - if(subghz->txrx->transmitter) { - subghz_protocol_secplus_v2_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - key, - 0x68, - 0xE500000, - subghz->txrx->preset); - generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - break; - case SubmenuIndexSecPlus_v2_390_00: - subghz->txrx->transmitter = subghz_transmitter_alloc_init( - subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); - subghz_preset_init(subghz, "AM650", 390000000, NULL, 0); - if(subghz->txrx->transmitter) { - subghz_protocol_secplus_v2_create_data( - subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), - subghz->txrx->fff_data, - key, - 0x68, - 0xE500000, - subghz->txrx->preset); - generated_protocol = true; - } else { - generated_protocol = false; - } - subghz_transmitter_free(subghz->txrx->transmitter); - break; - default: - return false; - break; - } - - scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneSetType, event.event); - - if(generated_protocol) { - subghz_file_name_clear(subghz); - DOLPHIN_DEED(DolphinDeedSubGhzAddManually); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); - return true; - } - } - - return false; -} - -void subghz_scene_set_type_on_exit(void* context) { - SubGhz* subghz = context; - submenu_reset(subghz->submenu); -} diff --git a/applications/subghz/scenes/subghz_scene_test.c b/applications/subghz/scenes/subghz_scene_test.c deleted file mode 100644 index 65f9bbdefad..00000000000 --- a/applications/subghz/scenes/subghz_scene_test.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "../subghz_i.h" - -enum SubmenuIndex { - SubmenuIndexCarrier, - SubmenuIndexPacket, - SubmenuIndexStatic, -}; - -void subghz_scene_test_submenu_callback(void* context, uint32_t index) { - SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, index); -} - -void subghz_scene_test_on_enter(void* context) { - SubGhz* subghz = context; - - submenu_add_item( - subghz->submenu, - "Carrier", - SubmenuIndexCarrier, - subghz_scene_test_submenu_callback, - subghz); - submenu_add_item( - subghz->submenu, "Packet", SubmenuIndexPacket, subghz_scene_test_submenu_callback, subghz); - submenu_add_item( - subghz->submenu, "Static", SubmenuIndexStatic, subghz_scene_test_submenu_callback, subghz); - - submenu_set_selected_item( - subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneTest)); - - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdMenu); -} - -bool subghz_scene_test_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexCarrier) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneTest, SubmenuIndexCarrier); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTestCarrier); - return true; - } else if(event.event == SubmenuIndexPacket) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneTest, SubmenuIndexPacket); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTestPacket); - return true; - } else if(event.event == SubmenuIndexStatic) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneTest, SubmenuIndexStatic); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTestStatic); - return true; - } - } - return false; -} - -void subghz_scene_test_on_exit(void* context) { - SubGhz* subghz = context; - submenu_reset(subghz->submenu); -} diff --git a/applications/subghz/scenes/subghz_scene_test_carrier.c b/applications/subghz/scenes/subghz_scene_test_carrier.c deleted file mode 100644 index 9677792ba31..00000000000 --- a/applications/subghz/scenes/subghz_scene_test_carrier.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "../subghz_i.h" -#include "../views/subghz_test_carrier.h" - -void subghz_scene_test_carrier_callback(SubGhzTestCarrierEvent event, void* context) { - furi_assert(context); - SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, event); -} - -void subghz_scene_test_carrier_on_enter(void* context) { - SubGhz* subghz = context; - subghz_test_carrier_set_callback( - subghz->subghz_test_carrier, subghz_scene_test_carrier_callback, subghz); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTestCarrier); -} - -bool subghz_scene_test_carrier_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzTestCarrierEventOnlyRx) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); - return true; - } - } - return false; -} - -void subghz_scene_test_carrier_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/subghz/scenes/subghz_scene_test_packet.c b/applications/subghz/scenes/subghz_scene_test_packet.c deleted file mode 100644 index 99f0ab1791f..00000000000 --- a/applications/subghz/scenes/subghz_scene_test_packet.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "../subghz_i.h" -#include "../views/subghz_test_packet.h" - -void subghz_scene_test_packet_callback(SubGhzTestPacketEvent event, void* context) { - furi_assert(context); - SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, event); -} - -void subghz_scene_test_packet_on_enter(void* context) { - SubGhz* subghz = context; - subghz_test_packet_set_callback( - subghz->subghz_test_packet, subghz_scene_test_packet_callback, subghz); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTestPacket); -} - -bool subghz_scene_test_packet_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzTestPacketEventOnlyRx) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); - return true; - } - } - return false; -} - -void subghz_scene_test_packet_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/subghz/scenes/subghz_scene_test_static.c b/applications/subghz/scenes/subghz_scene_test_static.c deleted file mode 100644 index 10e6d02a1da..00000000000 --- a/applications/subghz/scenes/subghz_scene_test_static.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "../subghz_i.h" -#include "../views/subghz_test_static.h" - -void subghz_scene_test_static_callback(SubGhzTestStaticEvent event, void* context) { - furi_assert(context); - SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, event); -} - -void subghz_scene_test_static_on_enter(void* context) { - SubGhz* subghz = context; - subghz_test_static_set_callback( - subghz->subghz_test_static, subghz_scene_test_static_callback, subghz); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdStatic); -} - -bool subghz_scene_test_static_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzTestStaticEventOnlyRx) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); - return true; - } - } - return false; -} - -void subghz_scene_test_static_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/subghz/scenes/subghz_scene_transmitter.c b/applications/subghz/scenes/subghz_scene_transmitter.c deleted file mode 100644 index b3f8d079911..00000000000 --- a/applications/subghz/scenes/subghz_scene_transmitter.c +++ /dev/null @@ -1,112 +0,0 @@ -#include "../subghz_i.h" -#include "../views/transmitter.h" -#include - -void subghz_scene_transmitter_callback(SubGhzCustomEvent event, void* context) { - furi_assert(context); - SubGhz* subghz = context; - view_dispatcher_send_custom_event(subghz->view_dispatcher, event); -} - -bool subghz_scene_transmitter_update_data_show(void* context) { - //ToDo Fix - SubGhz* subghz = context; - - if(subghz->txrx->decoder_result) { - string_t key_str; - string_t frequency_str; - string_t modulation_str; - - string_init(key_str); - string_init(frequency_str); - string_init(modulation_str); - 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->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, - string_get_cstr(key_str), - string_get_cstr(frequency_str), - string_get_cstr(modulation_str), - show_button); - - string_clear(frequency_str); - string_clear(modulation_str); - string_clear(key_str); - - return true; - } - return false; -} - -void subghz_scene_transmitter_on_enter(void* context) { - SubGhz* subghz = context; - DOLPHIN_DEED(DolphinDeedSubGhzSend); - if(!subghz_scene_transmitter_update_data_show(subghz)) { - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzCustomEventViewTransmitterError); - } - - subghz_view_transmitter_set_callback( - subghz->subghz_transmitter, subghz_scene_transmitter_callback, subghz); - - subghz->state_notifications = SubGhzNotificationStateIDLE; - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTransmitter); -} - -bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventViewTransmitterSendStart) { - subghz->state_notifications = SubGhzNotificationStateIDLE; - 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); - } else { - subghz->state_notifications = SubGhzNotificationStateTx; - subghz_scene_transmitter_update_data_show(subghz); - } - } - return true; - } else if(event.event == SubGhzCustomEventViewTransmitterSendStop) { - subghz->state_notifications = SubGhzNotificationStateIDLE; - if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { - subghz_tx_stop(subghz); - subghz_sleep(subghz); - } - return true; - } else if(event.event == SubGhzCustomEventViewTransmitterBack) { - subghz->state_notifications = SubGhzNotificationStateIDLE; - scene_manager_search_and_switch_to_previous_scene( - subghz->scene_manager, SubGhzSceneStart); - return true; - } else if(event.event == SubGhzCustomEventViewTransmitterError) { - string_set_str(subghz->error_str, "Protocol not\nfound!"); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); - } - } else if(event.type == SceneManagerEventTypeTick) { - if(subghz->state_notifications == SubGhzNotificationStateTx) { - notification_message(subghz->notifications, &sequence_blink_magenta_10); - } - return true; - } - return false; -} - -void subghz_scene_transmitter_on_exit(void* context) { - SubGhz* subghz = context; - subghz->state_notifications = SubGhzNotificationStateIDLE; -} diff --git a/applications/subghz/subghz.c b/applications/subghz/subghz.c deleted file mode 100644 index 61960218b32..00000000000 --- a/applications/subghz/subghz.c +++ /dev/null @@ -1,373 +0,0 @@ -/* Abandon hope, all ye who enter here. */ - -#include "m-string.h" -#include "subghz/types.h" -#include "subghz_i.h" -#include - -bool subghz_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - SubGhz* subghz = context; - return scene_manager_handle_custom_event(subghz->scene_manager, event); -} - -bool subghz_back_event_callback(void* context) { - furi_assert(context); - SubGhz* subghz = context; - return scene_manager_handle_back_event(subghz->scene_manager); -} - -void subghz_tick_event_callback(void* context) { - furi_assert(context); - SubGhz* subghz = context; - scene_manager_handle_tick_event(subghz->scene_manager); -} - -static void subghz_rpc_command_callback(RpcAppSystemEvent event, void* context) { - furi_assert(context); - SubGhz* subghz = context; - - furi_assert(subghz->rpc_ctx); - - if(event == RpcAppEventSessionClose) { - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzCustomEventSceneRpcSessionClose); - rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); - subghz->rpc_ctx = NULL; - } else if(event == RpcAppEventAppExit) { - view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); - } else if(event == RpcAppEventLoadFile) { - view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneRpcLoad); - } else if(event == RpcAppEventButtonPress) { - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonPress); - } else if(event == RpcAppEventButtonRelease) { - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonRelease); - } else { - rpc_system_app_confirm(subghz->rpc_ctx, event, false); - } -} - -void subghz_blink_start(SubGhz* instance) { - furi_assert(instance); - notification_message(instance->notifications, &sequence_blink_start_magenta); -} - -void subghz_blink_stop(SubGhz* instance) { - furi_assert(instance); - notification_message(instance->notifications, &sequence_blink_stop); -} - -SubGhz* subghz_alloc() { - SubGhz* subghz = malloc(sizeof(SubGhz)); - - string_init(subghz->file_path); - string_init(subghz->file_path_tmp); - - // GUI - subghz->gui = furi_record_open(RECORD_GUI); - - // View Dispatcher - subghz->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_enable_queue(subghz->view_dispatcher); - - subghz->scene_manager = scene_manager_alloc(&subghz_scene_handlers, subghz); - view_dispatcher_set_event_callback_context(subghz->view_dispatcher, subghz); - view_dispatcher_set_custom_event_callback( - subghz->view_dispatcher, subghz_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - subghz->view_dispatcher, subghz_back_event_callback); - view_dispatcher_set_tick_event_callback( - subghz->view_dispatcher, subghz_tick_event_callback, 100); - - // Open Notification record - subghz->notifications = furi_record_open(RECORD_NOTIFICATION); - - // SubMenu - subghz->submenu = submenu_alloc(); - view_dispatcher_add_view( - subghz->view_dispatcher, SubGhzViewIdMenu, submenu_get_view(subghz->submenu)); - - // Receiver - subghz->subghz_receiver = subghz_view_receiver_alloc(); - view_dispatcher_add_view( - subghz->view_dispatcher, - SubGhzViewIdReceiver, - subghz_view_receiver_get_view(subghz->subghz_receiver)); - - // Popup - subghz->popup = popup_alloc(); - view_dispatcher_add_view( - subghz->view_dispatcher, SubGhzViewIdPopup, popup_get_view(subghz->popup)); - - // Text Input - subghz->text_input = text_input_alloc(); - view_dispatcher_add_view( - subghz->view_dispatcher, SubGhzViewIdTextInput, text_input_get_view(subghz->text_input)); - - // Custom Widget - subghz->widget = widget_alloc(); - view_dispatcher_add_view( - subghz->view_dispatcher, SubGhzViewIdWidget, widget_get_view(subghz->widget)); - - //Dialog - subghz->dialogs = furi_record_open(RECORD_DIALOGS); - - // Transmitter - subghz->subghz_transmitter = subghz_view_transmitter_alloc(); - view_dispatcher_add_view( - subghz->view_dispatcher, - SubGhzViewIdTransmitter, - subghz_view_transmitter_get_view(subghz->subghz_transmitter)); - - // Variable Item List - subghz->variable_item_list = variable_item_list_alloc(); - view_dispatcher_add_view( - subghz->view_dispatcher, - SubGhzViewIdVariableItemList, - variable_item_list_get_view(subghz->variable_item_list)); - - // Frequency Analyzer - subghz->subghz_frequency_analyzer = subghz_frequency_analyzer_alloc(); - view_dispatcher_add_view( - subghz->view_dispatcher, - SubGhzViewIdFrequencyAnalyzer, - subghz_frequency_analyzer_get_view(subghz->subghz_frequency_analyzer)); - - // Read RAW - subghz->subghz_read_raw = subghz_read_raw_alloc(); - view_dispatcher_add_view( - subghz->view_dispatcher, - SubGhzViewIdReadRAW, - subghz_read_raw_get_view(subghz->subghz_read_raw)); - - // Carrier Test Module - subghz->subghz_test_carrier = subghz_test_carrier_alloc(); - view_dispatcher_add_view( - subghz->view_dispatcher, - SubGhzViewIdTestCarrier, - subghz_test_carrier_get_view(subghz->subghz_test_carrier)); - - // Packet Test - subghz->subghz_test_packet = subghz_test_packet_alloc(); - view_dispatcher_add_view( - subghz->view_dispatcher, - SubGhzViewIdTestPacket, - subghz_test_packet_get_view(subghz->subghz_test_packet)); - - // Static send - subghz->subghz_test_static = subghz_test_static_alloc(); - view_dispatcher_add_view( - subghz->view_dispatcher, - SubGhzViewIdStatic, - subghz_test_static_get_view(subghz->subghz_test_static)); - - //init setting - subghz->setting = subghz_setting_alloc(); - subghz_setting_load(subghz->setting, EXT_PATH("subghz/assets/setting_user")); - - //init Worker & Protocol & History & KeyBoard - subghz->lock = SubGhzLockOff; - subghz->txrx = malloc(sizeof(SubGhzTxRx)); - subghz->txrx->preset = malloc(sizeof(SubGhzPresetDefinition)); - string_init(subghz->txrx->preset->name); - subghz_preset_init( - subghz, "AM650", subghz_setting_get_default_frequency(subghz->setting), NULL, 0); - - subghz->txrx->txrx_state = SubGhzTxRxStateSleep; - subghz->txrx->hopper_state = SubGhzHopperStateOFF; - subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; - subghz->txrx->history = subghz_history_alloc(); - subghz->txrx->worker = subghz_worker_alloc(); - subghz->txrx->fff_data = flipper_format_string_alloc(); - - subghz->txrx->environment = subghz_environment_alloc(); - subghz_environment_set_came_atomo_rainbow_table_file_name( - subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo")); - subghz_environment_set_nice_flor_s_rainbow_table_file_name( - subghz->txrx->environment, EXT_PATH("subghz/assets/nice_flor_s")); - subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment); - subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); - - subghz_worker_set_overrun_callback( - subghz->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset); - subghz_worker_set_pair_callback( - subghz->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode); - subghz_worker_set_context(subghz->txrx->worker, subghz->txrx->receiver); - - //Init Error_str - string_init(subghz->error_str); - - return subghz; -} - -void subghz_free(SubGhz* subghz) { - furi_assert(subghz); - - if(subghz->rpc_ctx) { - rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); - rpc_system_app_send_exited(subghz->rpc_ctx); - subghz_blink_stop(subghz); - subghz->rpc_ctx = NULL; - } - - // Packet Test - view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket); - subghz_test_packet_free(subghz->subghz_test_packet); - - // Carrier Test - view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestCarrier); - subghz_test_carrier_free(subghz->subghz_test_carrier); - - // Static - view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdStatic); - subghz_test_static_free(subghz->subghz_test_static); - - // Receiver - view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdReceiver); - subghz_view_receiver_free(subghz->subghz_receiver); - - // TextInput - view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTextInput); - text_input_free(subghz->text_input); - - // Custom Widget - view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdWidget); - widget_free(subghz->widget); - - //Dialog - furi_record_close(RECORD_DIALOGS); - - // Transmitter - view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTransmitter); - subghz_view_transmitter_free(subghz->subghz_transmitter); - - // Variable Item List - view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); - variable_item_list_free(subghz->variable_item_list); - - // Frequency Analyzer - view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdFrequencyAnalyzer); - subghz_frequency_analyzer_free(subghz->subghz_frequency_analyzer); - - // Read RAW - view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdReadRAW); - subghz_read_raw_free(subghz->subghz_read_raw); - - // Submenu - view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdMenu); - submenu_free(subghz->submenu); - - // Popup - view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdPopup); - popup_free(subghz->popup); - - // Scene manager - scene_manager_free(subghz->scene_manager); - - // View Dispatcher - view_dispatcher_free(subghz->view_dispatcher); - - // GUI - furi_record_close(RECORD_GUI); - subghz->gui = NULL; - - //setting - subghz_setting_free(subghz->setting); - - //Worker & Protocol & History - subghz_receiver_free(subghz->txrx->receiver); - subghz_environment_free(subghz->txrx->environment); - subghz_worker_free(subghz->txrx->worker); - flipper_format_free(subghz->txrx->fff_data); - subghz_history_free(subghz->txrx->history); - string_clear(subghz->txrx->preset->name); - free(subghz->txrx->preset); - free(subghz->txrx); - - //Error string - string_clear(subghz->error_str); - - // Notifications - furi_record_close(RECORD_NOTIFICATION); - subghz->notifications = NULL; - - // Path strings - string_clear(subghz->file_path); - string_clear(subghz->file_path_tmp); - - // The rest - free(subghz); -} - -int32_t subghz_app(void* p) { - SubGhz* subghz = subghz_alloc(); - - if(!furi_hal_region_is_provisioned()) { - subghz_dialog_message_show_only_rx(subghz); - subghz_free(subghz); - return 1; - } - - //Load database - bool load_database = subghz_environment_load_keystore( - subghz->txrx->environment, EXT_PATH("subghz/assets/keeloq_mfcodes")); - subghz_environment_load_keystore( - subghz->txrx->environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); - // Check argument and run corresponding scene - if(p && strlen(p)) { - uint32_t rpc_ctx = 0; - if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { - subghz->rpc_ctx = (void*)rpc_ctx; - rpc_system_app_set_callback(subghz->rpc_ctx, subghz_rpc_command_callback, subghz); - rpc_system_app_send_started(subghz->rpc_ctx); - view_dispatcher_attach_to_gui( - subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeDesktop); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneRpc); - } else { - view_dispatcher_attach_to_gui( - subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen); - if(subghz_key_load(subghz, p, true)) { - string_set_str(subghz->file_path, p); - - if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { - //Load Raw TX - subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); - } else { - //Load transmitter TX - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter); - } - } else { - //exit app - scene_manager_stop(subghz->scene_manager); - view_dispatcher_stop(subghz->view_dispatcher); - } - } - } else { - view_dispatcher_attach_to_gui( - subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen); - string_set_str(subghz->file_path, SUBGHZ_APP_FOLDER); - if(load_database) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart); - } else { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneShowError, SubGhzCustomEventManagerSet); - string_set_str( - subghz->error_str, - "No SD card or\ndatabase found.\nSome app function\nmay be reduced."); - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); - } - } - - furi_hal_power_suppress_charge_enter(); - - view_dispatcher_run(subghz->view_dispatcher); - - furi_hal_power_suppress_charge_exit(); - - subghz_free(subghz); - - return 0; -} diff --git a/applications/subghz/subghz_cli.c b/applications/subghz/subghz_cli.c deleted file mode 100644 index 09dae0481fa..00000000000 --- a/applications/subghz/subghz_cli.c +++ /dev/null @@ -1,862 +0,0 @@ -#include "subghz_cli.h" - -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include "helpers/subghz_chat.h" - -#include -#include - -#include -#include - -#define SUBGHZ_FREQUENCY_RANGE_STR \ - "299999755...348000000 or 386999938...464000000 or 778999847...928000000" - -#define SUBGHZ_REGION_FILENAME "/int/.region_data" - -void subghz_cli_command_tx_carrier(Cli* cli, string_t args, void* context) { - UNUSED(context); - uint32_t frequency = 433920000; - - if(string_size(args)) { - int ret = sscanf(string_get_cstr(args), "%lu", &frequency); - if(ret != 1) { - printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); - cli_print_usage("subghz tx_carrier", "", string_get_cstr(args)); - return; - } - if(!furi_hal_subghz_is_frequency_valid(frequency)) { - printf( - "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", - frequency); - return; - } - } - - furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); - frequency = furi_hal_subghz_set_frequency_and_path(frequency); - - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_cc1101_g0, true); - - furi_hal_power_suppress_charge_enter(); - - if(furi_hal_subghz_tx()) { - printf("Transmitting at frequency %lu Hz\r\n", frequency); - printf("Press CTRL+C to stop\r\n"); - while(!cli_cmd_interrupt_received(cli)) { - furi_delay_ms(250); - } - } else { - printf("This frequency can only be used for RX in your region\r\n"); - } - - furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); - furi_hal_subghz_sleep(); - - furi_hal_power_suppress_charge_exit(); -} - -void subghz_cli_command_rx_carrier(Cli* cli, string_t args, void* context) { - UNUSED(context); - uint32_t frequency = 433920000; - - if(string_size(args)) { - int ret = sscanf(string_get_cstr(args), "%lu", &frequency); - if(ret != 1) { - printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); - cli_print_usage("subghz rx_carrier", "", string_get_cstr(args)); - return; - } - if(!furi_hal_subghz_is_frequency_valid(frequency)) { - printf( - "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", - frequency); - return; - } - } - - furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); - frequency = furi_hal_subghz_set_frequency_and_path(frequency); - printf("Receiving at frequency %lu Hz\r\n", frequency); - printf("Press CTRL+C to stop\r\n"); - - furi_hal_power_suppress_charge_enter(); - - furi_hal_subghz_rx(); - - while(!cli_cmd_interrupt_received(cli)) { - furi_delay_ms(250); - printf("RSSI: %03.1fdbm\r", (double)furi_hal_subghz_get_rssi()); - fflush(stdout); - } - - furi_hal_power_suppress_charge_exit(); - - furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); - furi_hal_subghz_sleep(); -} - -void subghz_cli_command_tx(Cli* cli, string_t args, void* context) { - UNUSED(context); - uint32_t frequency = 433920000; - uint32_t key = 0x0074BADE; - uint32_t repeat = 10; - - if(string_size(args)) { - int ret = sscanf(string_get_cstr(args), "%lx %lu %lu", &key, &frequency, &repeat); - if(ret != 3) { - printf( - "sscanf returned %d, key: %lx, frequency: %lu, repeat: %lu\r\n", - ret, - key, - frequency, - repeat); - cli_print_usage( - "subghz tx", - "<3 Byte Key: in hex> ", - string_get_cstr(args)); - return; - } - if(!furi_hal_subghz_is_frequency_valid(frequency)) { - printf( - "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", - frequency); - return; - } - } - - printf( - "Transmitting at %lu, key %lx, repeat %lu. Press CTRL+C to stop\r\n", - frequency, - key, - repeat); - - string_t flipper_format_string; - string_init_printf( - flipper_format_string, - "Protocol: Princeton\n" - "Bit: 24\n" - "Key: 00 00 00 00 00 %X %X %X\n" - "TE: 403\n" - "Repeat: %d\n", - (uint8_t)((key >> 16) & 0xFF), - (uint8_t)((key >> 8) & 0xFF), - (uint8_t)(key & 0xFF), - repeat); - FlipperFormat* flipper_format = flipper_format_string_alloc(); - Stream* stream = flipper_format_get_raw_stream(flipper_format); - stream_clean(stream); - stream_write_cstring(stream, string_get_cstr(flipper_format_string)); - - SubGhzEnvironment* environment = subghz_environment_alloc(); - - SubGhzTransmitter* transmitter = subghz_transmitter_alloc_init(environment, "Princeton"); - subghz_transmitter_deserialize(transmitter, flipper_format); - - furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); - frequency = furi_hal_subghz_set_frequency_and_path(frequency); - - furi_hal_power_suppress_charge_enter(); - - furi_hal_subghz_start_async_tx(subghz_transmitter_yield, transmitter); - - while(!(furi_hal_subghz_is_async_tx_complete() || cli_cmd_interrupt_received(cli))) { - printf("."); - fflush(stdout); - furi_delay_ms(333); - } - furi_hal_subghz_stop_async_tx(); - furi_hal_subghz_sleep(); - - furi_hal_power_suppress_charge_exit(); - - flipper_format_free(flipper_format); - subghz_transmitter_free(transmitter); - subghz_environment_free(environment); -} - -typedef struct { - volatile bool overrun; - StreamBufferHandle_t stream; - size_t packet_count; -} SubGhzCliCommandRx; - -static void subghz_cli_command_rx_capture_callback(bool level, uint32_t duration, void* context) { - SubGhzCliCommandRx* instance = context; - - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - LevelDuration level_duration = level_duration_make(level, duration); - if(instance->overrun) { - instance->overrun = false; - level_duration = level_duration_reset(); - } - size_t ret = xStreamBufferSendFromISR( - instance->stream, &level_duration, sizeof(LevelDuration), &xHigherPriorityTaskWoken); - if(sizeof(LevelDuration) != ret) instance->overrun = true; - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); -} - -static void subghz_cli_command_rx_callback( - SubGhzReceiver* receiver, - SubGhzProtocolDecoderBase* decoder_base, - void* context) { - SubGhzCliCommandRx* instance = context; - instance->packet_count++; - - string_t text; - string_init(text); - subghz_protocol_decoder_base_get_string(decoder_base, text); - subghz_receiver_reset(receiver); - printf("%s", string_get_cstr(text)); - string_clear(text); -} - -void subghz_cli_command_rx(Cli* cli, string_t args, void* context) { - UNUSED(context); - uint32_t frequency = 433920000; - - if(string_size(args)) { - int ret = sscanf(string_get_cstr(args), "%lu", &frequency); - if(ret != 1) { - printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); - cli_print_usage("subghz rx", "", string_get_cstr(args)); - return; - } - if(!furi_hal_subghz_is_frequency_valid(frequency)) { - printf( - "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", - frequency); - return; - } - } - - // Allocate context and buffers - SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); - instance->stream = xStreamBufferCreate(sizeof(LevelDuration) * 1024, sizeof(LevelDuration)); - furi_check(instance->stream); - - SubGhzEnvironment* environment = subghz_environment_alloc(); - subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes")); - subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); - subghz_environment_set_came_atomo_rainbow_table_file_name( - environment, EXT_PATH("subghz/assets/came_atomo")); - subghz_environment_set_nice_flor_s_rainbow_table_file_name( - environment, EXT_PATH("subghz/assets/nice_flor_s")); - - SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); - subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); - subghz_receiver_set_rx_callback(receiver, subghz_cli_command_rx_callback, instance); - - // Configure radio - furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); - frequency = furi_hal_subghz_set_frequency_and_path(frequency); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); - - furi_hal_power_suppress_charge_enter(); - - // Prepare and start RX - furi_hal_subghz_start_async_rx(subghz_cli_command_rx_capture_callback, instance); - - // Wait for packets to arrive - printf("Listening at %lu. Press CTRL+C to stop\r\n", frequency); - LevelDuration level_duration; - while(!cli_cmd_interrupt_received(cli)) { - int ret = - xStreamBufferReceive(instance->stream, &level_duration, sizeof(LevelDuration), 10); - if(ret == sizeof(LevelDuration)) { - if(level_duration_is_reset(level_duration)) { - printf("."); - subghz_receiver_reset(receiver); - } else { - bool level = level_duration_get_level(level_duration); - uint32_t duration = level_duration_get_duration(level_duration); - subghz_receiver_decode(receiver, level, duration); - } - } - } - - // Shutdown radio - furi_hal_subghz_stop_async_rx(); - furi_hal_subghz_sleep(); - - furi_hal_power_suppress_charge_exit(); - - printf("\r\nPackets recieved %u\r\n", instance->packet_count); - - // Cleanup - subghz_receiver_free(receiver); - subghz_environment_free(environment); - vStreamBufferDelete(instance->stream); - free(instance); -} - -void subghz_cli_command_decode_raw(Cli* cli, string_t args, void* context) { - UNUSED(context); - string_t file_name; - string_init(file_name); - string_set_str(file_name, ANY_PATH("subghz/test.sub")); - - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - string_t temp_str; - string_init(temp_str); - uint32_t temp_data32; - bool check_file = false; - - do { - if(string_size(args)) { - if(!args_read_string_and_trim(args, file_name)) { - cli_print_usage( - "subghz decode_raw", "", string_get_cstr(args)); - break; - } - } - - if(!flipper_format_file_open_existing(fff_data_file, string_get_cstr(file_name))) { - printf( - "subghz decode_raw \033[0;31mError open file\033[0m %s\r\n", - string_get_cstr(file_name)); - break; - } - - if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { - printf("subghz decode_raw \033[0;31mMissing or incorrect header\033[0m\r\n"); - break; - } - - if(!strcmp(string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE) && - temp_data32 == SUBGHZ_KEY_FILE_VERSION) { - } else { - printf("subghz decode_raw \033[0;31mType or version mismatch\033[0m\r\n"); - break; - } - - check_file = true; - } while(false); - - string_clear(temp_str); - flipper_format_free(fff_data_file); - furi_record_close(RECORD_STORAGE); - - if(check_file) { - // Allocate context - SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); - - SubGhzEnvironment* environment = subghz_environment_alloc(); - if(subghz_environment_load_keystore( - environment, EXT_PATH("subghz/assets/keeloq_mfcodes"))) { - printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;32mOK\033[0m\r\n"); - } else { - printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;31mERROR\033[0m\r\n"); - } - if(subghz_environment_load_keystore( - environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user"))) { - printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;32mOK\033[0m\r\n"); - } else { - printf( - "SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;31mERROR\033[0m\r\n"); - } - subghz_environment_set_came_atomo_rainbow_table_file_name( - environment, EXT_PATH("subghz/assets/came_atomo")); - subghz_environment_set_nice_flor_s_rainbow_table_file_name( - environment, EXT_PATH("subghz/assets/nice_flor_s")); - - SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); - subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable); - subghz_receiver_set_rx_callback(receiver, subghz_cli_command_rx_callback, instance); - - SubGhzFileEncoderWorker* file_worker_encoder = subghz_file_encoder_worker_alloc(); - if(subghz_file_encoder_worker_start(file_worker_encoder, string_get_cstr(file_name))) { - //the worker needs a file in order to open and read part of the file - furi_delay_ms(100); - } - - printf( - "Listening at \033[0;33m%s\033[0m.\r\n\r\nPress CTRL+C to stop\r\n\r\n", - string_get_cstr(file_name)); - - LevelDuration level_duration; - while(!cli_cmd_interrupt_received(cli)) { - furi_delay_us(500); //you need to have time to read from the file from the SD card - level_duration = subghz_file_encoder_worker_get_level_duration(file_worker_encoder); - if(!level_duration_is_reset(level_duration)) { - bool level = level_duration_get_level(level_duration); - uint32_t duration = level_duration_get_duration(level_duration); - subghz_receiver_decode(receiver, level, duration); - } else { - break; - } - } - - printf("\r\nPackets recieved \033[0;32m%u\033[0m\r\n", instance->packet_count); - - // Cleanup - subghz_receiver_free(receiver); - subghz_environment_free(environment); - - if(subghz_file_encoder_worker_is_running(file_worker_encoder)) { - subghz_file_encoder_worker_stop(file_worker_encoder); - } - subghz_file_encoder_worker_free(file_worker_encoder); - free(instance); - } - string_clear(file_name); -} - -static void subghz_cli_command_print_usage() { - printf("Usage:\r\n"); - printf("subghz \r\n"); - printf("Cmd list:\r\n"); - - printf("\tchat \t - Chat with other Flippers\r\n"); - printf( - "\ttx <3 byte Key: in hex> \t - Transmitting key\r\n"); - printf("\trx \t - Reception key\r\n"); - printf("\tdecode_raw \t - Testing\r\n"); - - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - printf("\r\n"); - printf(" debug cmd:\r\n"); - printf("\ttx_carrier \t - Transmit carrier\r\n"); - printf("\trx_carrier \t - Receiv carrier\r\n"); - printf( - "\tencrypt_keeloq \t - Encrypt keeloq manufacture keys\r\n"); - printf( - "\tencrypt_raw \t - Encrypt RAW data\r\n"); - } -} - -static void subghz_cli_command_encrypt_keeloq(Cli* cli, string_t args) { - UNUSED(cli); - uint8_t iv[16]; - - string_t source; - string_t destination; - string_init(source); - string_init(destination); - - SubGhzKeystore* keystore = subghz_keystore_alloc(); - - do { - if(!args_read_string_and_trim(args, source)) { - subghz_cli_command_print_usage(); - break; - } - - if(!args_read_string_and_trim(args, destination)) { - subghz_cli_command_print_usage(); - break; - } - - if(!args_read_hex_bytes(args, iv, 16)) { - subghz_cli_command_print_usage(); - break; - } - - if(!subghz_keystore_load(keystore, string_get_cstr(source))) { - printf("Failed to load Keystore"); - break; - } - - if(!subghz_keystore_save(keystore, string_get_cstr(destination), iv)) { - printf("Failed to save Keystore"); - break; - } - } while(false); - - subghz_keystore_free(keystore); - string_clear(destination); - string_clear(source); -} - -static void subghz_cli_command_encrypt_raw(Cli* cli, string_t args) { - UNUSED(cli); - uint8_t iv[16]; - - string_t source; - string_t destination; - string_init(source); - string_init(destination); - - do { - if(!args_read_string_and_trim(args, source)) { - subghz_cli_command_print_usage(); - break; - } - - if(!args_read_string_and_trim(args, destination)) { - subghz_cli_command_print_usage(); - break; - } - - if(!args_read_hex_bytes(args, iv, 16)) { - subghz_cli_command_print_usage(); - break; - } - - if(!subghz_keystore_raw_encrypted_save( - string_get_cstr(source), string_get_cstr(destination), iv)) { - printf("Failed to save Keystore"); - break; - } - - } while(false); - - string_clear(destination); - string_clear(source); -} - -static void subghz_cli_command_chat(Cli* cli, string_t args) { - uint32_t frequency = 433920000; - - if(string_size(args)) { - int ret = sscanf(string_get_cstr(args), "%lu", &frequency); - if(ret != 1) { - printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); - cli_print_usage("subghz chat", "", string_get_cstr(args)); - return; - } - if(!furi_hal_subghz_is_frequency_valid(frequency)) { - printf( - "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", - frequency); - return; - } - } - if(!furi_hal_region_is_frequency_allowed(frequency)) { - printf( - "In your region, only reception on this frequency (%lu) is allowed,\r\n" - "the actual operation of the application is not possible\r\n ", - frequency); - return; - } - - SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(cli); - if(!subghz_chat_worker_start(subghz_chat, frequency)) { - printf("Startup error SubGhzChatWorker\r\n"); - - if(subghz_chat_worker_is_running(subghz_chat)) { - subghz_chat_worker_stop(subghz_chat); - subghz_chat_worker_free(subghz_chat); - } - return; - } - - printf("Receiving at frequency %lu Hz\r\n", frequency); - printf("Press CTRL+C to stop\r\n"); - - furi_hal_power_suppress_charge_enter(); - - size_t message_max_len = 64; - uint8_t message[64] = {0}; - string_t input; - string_init(input); - string_t name; - string_init(name); - string_t output; - string_init(output); - string_t sysmsg; - string_init(sysmsg); - bool exit = false; - SubGhzChatEvent chat_event; - - NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); - - string_printf(name, "\033[0;33m%s\033[0m: ", furi_hal_version_get_name_ptr()); - string_set(input, name); - printf("%s", string_get_cstr(input)); - fflush(stdout); - - while(!exit) { - chat_event = subghz_chat_worker_get_event_chat(subghz_chat); - switch(chat_event.event) { - case SubGhzChatEventInputData: - if(chat_event.c == CliSymbolAsciiETX) { - printf("\r\n"); - chat_event.event = SubGhzChatEventUserExit; - subghz_chat_worker_put_event_chat(subghz_chat, &chat_event); - break; - } else if( - (chat_event.c == CliSymbolAsciiBackspace) || (chat_event.c == CliSymbolAsciiDel)) { - size_t len = string_length_u(input); - if(len > string_length_u(name)) { - printf("%s", "\e[D\e[1P"); - fflush(stdout); - //delete 1 char UTF - const char* str = string_get_cstr(input); - size_t size = 0; - m_str1ng_utf8_state_e s = M_STRING_UTF8_STARTING; - string_unicode_t u = 0; - string_reset(sysmsg); - while(*str) { - m_str1ng_utf8_decode(*str, &s, &u); - if((s == M_STRING_UTF8_ERROR) || s == M_STRING_UTF8_STARTING) { - string_push_u(sysmsg, u); - if(++size >= len - 1) break; - s = M_STRING_UTF8_STARTING; - } - str++; - } - string_set(input, sysmsg); - } - } else if(chat_event.c == CliSymbolAsciiCR) { - printf("\r\n"); - string_push_back(input, '\r'); - string_push_back(input, '\n'); - while(!subghz_chat_worker_write( - subghz_chat, - (uint8_t*)string_get_cstr(input), - strlen(string_get_cstr(input)))) { - furi_delay_ms(10); - } - - string_printf(input, "%s", string_get_cstr(name)); - printf("%s", string_get_cstr(input)); - fflush(stdout); - } else if(chat_event.c == CliSymbolAsciiLF) { - //cut out the symbol \n - } else { - putc(chat_event.c, stdout); - fflush(stdout); - string_push_back(input, chat_event.c); - break; - case SubGhzChatEventRXData: - do { - memset(message, 0x00, message_max_len); - size_t len = subghz_chat_worker_read(subghz_chat, message, message_max_len); - for(size_t i = 0; i < len; i++) { - string_push_back(output, message[i]); - if(message[i] == '\n') { - printf("\r"); - for(uint8_t i = 0; i < 80; i++) { - printf(" "); - } - printf("\r %s", string_get_cstr(output)); - printf("%s", string_get_cstr(input)); - fflush(stdout); - string_reset(output); - } - } - } while(subghz_chat_worker_available(subghz_chat)); - break; - case SubGhzChatEventNewMessage: - notification_message(notification, &sequence_single_vibro); - break; - case SubGhzChatEventUserEntrance: - string_printf( - sysmsg, - "\033[0;34m%s joined chat.\033[0m\r\n", - furi_hal_version_get_name_ptr()); - subghz_chat_worker_write( - subghz_chat, - (uint8_t*)string_get_cstr(sysmsg), - strlen(string_get_cstr(sysmsg))); - break; - case SubGhzChatEventUserExit: - string_printf( - sysmsg, "\033[0;31m%s left chat.\033[0m\r\n", furi_hal_version_get_name_ptr()); - subghz_chat_worker_write( - subghz_chat, - (uint8_t*)string_get_cstr(sysmsg), - strlen(string_get_cstr(sysmsg))); - furi_delay_ms(10); - exit = true; - break; - default: - FURI_LOG_W("SubGhzChat", "Error event"); - break; - } - } - if(!cli_is_connected(cli)) { - printf("\r\n"); - chat_event.event = SubGhzChatEventUserExit; - subghz_chat_worker_put_event_chat(subghz_chat, &chat_event); - } - } - - string_clear(input); - string_clear(name); - string_clear(output); - string_clear(sysmsg); - furi_hal_power_suppress_charge_exit(); - furi_record_close(RECORD_NOTIFICATION); - - if(subghz_chat_worker_is_running(subghz_chat)) { - subghz_chat_worker_stop(subghz_chat); - subghz_chat_worker_free(subghz_chat); - } - printf("\r\nExit chat\r\n"); -} - -static void subghz_cli_command(Cli* cli, string_t args, void* context) { - string_t cmd; - string_init(cmd); - - do { - if(!args_read_string_and_trim(args, cmd)) { - subghz_cli_command_print_usage(); - break; - } - - if(string_cmp_str(cmd, "chat") == 0) { - subghz_cli_command_chat(cli, args); - break; - } - - if(string_cmp_str(cmd, "tx") == 0) { - subghz_cli_command_tx(cli, args, context); - break; - } - - if(string_cmp_str(cmd, "rx") == 0) { - subghz_cli_command_rx(cli, args, context); - break; - } - - if(string_cmp_str(cmd, "decode_raw") == 0) { - subghz_cli_command_decode_raw(cli, args, context); - break; - } - - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - if(string_cmp_str(cmd, "encrypt_keeloq") == 0) { - subghz_cli_command_encrypt_keeloq(cli, args); - break; - } - - if(string_cmp_str(cmd, "encrypt_raw") == 0) { - subghz_cli_command_encrypt_raw(cli, args); - break; - } - - if(string_cmp_str(cmd, "tx_carrier") == 0) { - subghz_cli_command_tx_carrier(cli, args, context); - break; - } - - if(string_cmp_str(cmd, "rx_carrier") == 0) { - subghz_cli_command_rx_carrier(cli, args, context); - break; - } - } - - subghz_cli_command_print_usage(); - } while(false); - - string_clear(cmd); -} - -static bool - subghz_on_system_start_istream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) { - File* file = istream->state; - uint16_t ret = storage_file_read(file, buf, count); - return (count == ret); -} - -static bool subghz_on_system_start_istream_decode_band( - pb_istream_t* stream, - const pb_field_t* field, - void** arg) { - (void)field; - FuriHalRegion* region = *arg; - - PB_Region_Band band = {0}; - if(!pb_decode(stream, PB_Region_Band_fields, &band)) { - FURI_LOG_E("SubGhzOnStart", "PB Region band decode error: %s", PB_GET_ERROR(stream)); - return false; - } - - region->bands_count += 1; - region = - realloc(region, sizeof(FuriHalRegion) + sizeof(FuriHalRegionBand) * region->bands_count); - size_t pos = region->bands_count - 1; - region->bands[pos].start = band.start; - region->bands[pos].end = band.end; - region->bands[pos].power_limit = band.power_limit; - region->bands[pos].duty_cycle = band.duty_cycle; - *arg = region; - - FURI_LOG_I( - "SubGhzOnStart", - "Add allowed band: start %dHz, stop %dHz, power_limit %ddBm, duty_cycle %d%%", - band.start, - band.end, - band.power_limit, - band.duty_cycle); - return true; -} - -void subghz_on_system_start() { -#ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - - cli_add_command(cli, "subghz", CliCommandFlagDefault, subghz_cli_command, NULL); - - furi_record_close(RECORD_CLI); -#else - UNUSED(subghz_cli_command); -#endif - -#ifdef SRV_STORAGE - Storage* storage = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(storage); - FileInfo fileinfo = {0}; - PB_Region pb_region = {0}; - pb_region.bands.funcs.decode = subghz_on_system_start_istream_decode_band; - - do { - if(storage_common_stat(storage, SUBGHZ_REGION_FILENAME, &fileinfo) != FSE_OK || - fileinfo.size == 0) { - FURI_LOG_W("SubGhzOnStart", "Region data is missing or empty"); - break; - } - - if(!storage_file_open(file, SUBGHZ_REGION_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) { - FURI_LOG_E("SubGhzOnStart", "Unable to open region data"); - break; - } - - pb_istream_t istream = { - .callback = subghz_on_system_start_istream_read, - .state = file, - .errmsg = NULL, - .bytes_left = fileinfo.size, - }; - - pb_region.bands.arg = malloc(sizeof(FuriHalRegion)); - if(!pb_decode(&istream, PB_Region_fields, &pb_region)) { - FURI_LOG_E("SubGhzOnStart", "Invalid region data"); - free(pb_region.bands.arg); - break; - } - - FuriHalRegion* region = pb_region.bands.arg; - memcpy( - region->country_code, - pb_region.country_code->bytes, - pb_region.country_code->size < 4 ? pb_region.country_code->size : 3); - furi_hal_region_set(region); - } while(0); - - pb_release(PB_Region_fields, &pb_region); - storage_file_free(file); - furi_record_close(RECORD_STORAGE); -#else - UNUSED(subghz_cli_command); -#endif -} diff --git a/applications/subghz/subghz_history.c b/applications/subghz/subghz_history.c deleted file mode 100644 index 820a13d1442..00000000000 --- a/applications/subghz/subghz_history.c +++ /dev/null @@ -1,230 +0,0 @@ -#include "subghz_history.h" -#include -#include - -#include -#include - -#define SUBGHZ_HISTORY_MAX 50 -#define TAG "SubGhzHistory" - -typedef struct { - string_t item_str; - FlipperFormat* flipper_string; - uint8_t type; - SubGhzPresetDefinition* preset; -} SubGhzHistoryItem; - -ARRAY_DEF(SubGhzHistoryItemArray, SubGhzHistoryItem, M_POD_OPLIST) - -#define M_OPL_SubGhzHistoryItemArray_t() ARRAY_OPLIST(SubGhzHistoryItemArray, M_POD_OPLIST) - -typedef struct { - SubGhzHistoryItemArray_t data; -} SubGhzHistoryStruct; - -struct SubGhzHistory { - uint32_t last_update_timestamp; - uint16_t last_index_write; - uint8_t code_last_hash_data; - string_t tmp_string; - SubGhzHistoryStruct* history; -}; - -SubGhzHistory* subghz_history_alloc(void) { - SubGhzHistory* instance = malloc(sizeof(SubGhzHistory)); - string_init(instance->tmp_string); - instance->history = malloc(sizeof(SubGhzHistoryStruct)); - SubGhzHistoryItemArray_init(instance->history->data); - return instance; -} - -void subghz_history_free(SubGhzHistory* instance) { - furi_assert(instance); - string_clear(instance->tmp_string); - for - M_EACH(item, instance->history->data, SubGhzHistoryItemArray_t) { - string_clear(item->item_str); - string_clear(item->preset->name); - free(item->preset); - flipper_format_free(item->flipper_string); - item->type = 0; - } - SubGhzHistoryItemArray_clear(instance->history->data); - free(instance->history); - free(instance); -} - -uint32_t subghz_history_get_frequency(SubGhzHistory* instance, uint16_t idx) { - furi_assert(instance); - SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); - return item->preset->frequency; -} - -SubGhzPresetDefinition* subghz_history_get_preset_def(SubGhzHistory* instance, uint16_t idx) { - furi_assert(instance); - SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); - return item->preset; -} - -const char* subghz_history_get_preset(SubGhzHistory* instance, uint16_t idx) { - furi_assert(instance); - SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); - return string_get_cstr(item->preset->name); -} - -void subghz_history_reset(SubGhzHistory* instance) { - furi_assert(instance); - string_reset(instance->tmp_string); - for - M_EACH(item, instance->history->data, SubGhzHistoryItemArray_t) { - string_clear(item->item_str); - string_clear(item->preset->name); - free(item->preset); - flipper_format_free(item->flipper_string); - item->type = 0; - } - SubGhzHistoryItemArray_reset(instance->history->data); - instance->last_index_write = 0; - instance->code_last_hash_data = 0; -} - -uint16_t subghz_history_get_item(SubGhzHistory* instance) { - furi_assert(instance); - return instance->last_index_write; -} - -uint8_t subghz_history_get_type_protocol(SubGhzHistory* instance, uint16_t idx) { - furi_assert(instance); - SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); - return item->type; -} - -const char* subghz_history_get_protocol_name(SubGhzHistory* instance, uint16_t idx) { - furi_assert(instance); - SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); - flipper_format_rewind(item->flipper_string); - if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) { - FURI_LOG_E(TAG, "Missing Protocol"); - string_reset(instance->tmp_string); - } - return string_get_cstr(instance->tmp_string); -} - -FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx) { - furi_assert(instance); - SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); - if(item->flipper_string) { - return item->flipper_string; - } else { - return NULL; - } -} -bool subghz_history_get_text_space_left(SubGhzHistory* instance, string_t output) { - furi_assert(instance); - if(instance->last_index_write == SUBGHZ_HISTORY_MAX) { - if(output != NULL) string_printf(output, "Memory is FULL"); - return true; - } - if(output != NULL) - string_printf(output, "%02u/%02u", instance->last_index_write, SUBGHZ_HISTORY_MAX); - return false; -} - -void subghz_history_get_text_item_menu(SubGhzHistory* instance, string_t output, uint16_t idx) { - SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); - string_set(output, item->item_str); -} - -bool subghz_history_add_to_history( - SubGhzHistory* instance, - void* context, - SubGhzPresetDefinition* preset) { - furi_assert(instance); - furi_assert(context); - - if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return false; - - SubGhzProtocolDecoderBase* decoder_base = context; - if((instance->code_last_hash_data == - subghz_protocol_decoder_base_get_hash_data(decoder_base)) && - ((furi_get_tick() - instance->last_update_timestamp) < 500)) { - instance->last_update_timestamp = furi_get_tick(); - return false; - } - - instance->code_last_hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); - instance->last_update_timestamp = furi_get_tick(); - - string_t text; - string_init(text); - SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data); - item->preset = malloc(sizeof(SubGhzPresetDefinition)); - item->type = decoder_base->protocol->type; - item->preset->frequency = preset->frequency; - string_init(item->preset->name); - string_set(item->preset->name, preset->name); - item->preset->data = preset->data; - item->preset->data_size = preset->data_size; - - string_init(item->item_str); - item->flipper_string = flipper_format_string_alloc(); - subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, preset); - - do { - if(!flipper_format_rewind(item->flipper_string)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) { - FURI_LOG_E(TAG, "Missing Protocol"); - break; - } - if(!strcmp(string_get_cstr(instance->tmp_string), "KeeLoq")) { - string_set_str(instance->tmp_string, "KL "); - if(!flipper_format_read_string(item->flipper_string, "Manufacture", text)) { - FURI_LOG_E(TAG, "Missing Protocol"); - break; - } - string_cat(instance->tmp_string, text); - } else if(!strcmp(string_get_cstr(instance->tmp_string), "Star Line")) { - string_set_str(instance->tmp_string, "SL "); - if(!flipper_format_read_string(item->flipper_string, "Manufacture", text)) { - FURI_LOG_E(TAG, "Missing Protocol"); - break; - } - string_cat(instance->tmp_string, text); - } - if(!flipper_format_rewind(item->flipper_string)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - uint8_t key_data[sizeof(uint64_t)] = {0}; - if(!flipper_format_read_hex(item->flipper_string, "Key", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Missing Key"); - break; - } - uint64_t data = 0; - for(uint8_t i = 0; i < sizeof(uint64_t); i++) { - data = (data << 8) | key_data[i]; - } - if(!(uint32_t)(data >> 32)) { - string_printf( - item->item_str, - "%s %lX", - string_get_cstr(instance->tmp_string), - (uint32_t)(data & 0xFFFFFFFF)); - } else { - string_printf( - item->item_str, - "%s %lX%08lX", - string_get_cstr(instance->tmp_string), - (uint32_t)(data >> 32), - (uint32_t)(data & 0xFFFFFFFF)); - } - } while(false); - - string_clear(text); - instance->last_index_write++; - return true; -} diff --git a/applications/subghz/subghz_i.c b/applications/subghz/subghz_i.c deleted file mode 100644 index dc0d71e56cb..00000000000 --- a/applications/subghz/subghz_i.c +++ /dev/null @@ -1,585 +0,0 @@ -#include "subghz_i.h" - -#include "assets_icons.h" -#include "subghz/types.h" -#include -#include -#include -#include -#include -#include -#include -#include "../notification/notification.h" -#include "views/receiver.h" - -#include -#include -#include - -#define TAG "SubGhz" - -void subghz_preset_init( - void* context, - const char* preset_name, - uint32_t frequency, - uint8_t* preset_data, - size_t preset_data_size) { - furi_assert(context); - SubGhz* subghz = context; - string_set(subghz->txrx->preset->name, preset_name); - subghz->txrx->preset->frequency = frequency; - subghz->txrx->preset->data = preset_data; - subghz->txrx->preset->data_size = preset_data_size; -} - -bool subghz_set_preset(SubGhz* subghz, const char* preset) { - if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) { - string_set(subghz->txrx->preset->name, "AM270"); - } else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) { - string_set(subghz->txrx->preset->name, "AM650"); - } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) { - string_set(subghz->txrx->preset->name, "FM238"); - } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) { - string_set(subghz->txrx->preset->name, "FM476"); - } else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) { - string_set(subghz->txrx->preset->name, "CUSTOM"); - } else { - FURI_LOG_E(TAG, "Unknown preset"); - return false; - } - return true; -} - -void subghz_get_frequency_modulation(SubGhz* subghz, string_t frequency, string_t modulation) { - furi_assert(subghz); - if(frequency != NULL) { - string_printf( - frequency, - "%03ld.%02ld", - subghz->txrx->preset->frequency / 1000000 % 1000, - subghz->txrx->preset->frequency / 10000 % 100); - } - if(modulation != NULL) { - string_printf(modulation, "%0.2s", string_get_cstr(subghz->txrx->preset->name)); - } -} - -void subghz_begin(SubGhz* subghz, uint8_t* preset_data) { - furi_assert(subghz); - 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); - subghz->txrx->txrx_state = SubGhzTxRxStateIDLE; -} - -uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency) { - furi_assert(subghz); - if(!furi_hal_subghz_is_frequency_valid(frequency)) { - furi_crash("SubGhz: Incorrect RX frequency."); - } - furi_assert( - subghz->txrx->txrx_state != SubGhzTxRxStateRx && - subghz->txrx->txrx_state != SubGhzTxRxStateSleep); - - 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_subghz_flush_rx(); - furi_hal_subghz_rx(); - - furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, subghz->txrx->worker); - subghz_worker_start(subghz->txrx->worker); - subghz->txrx->txrx_state = SubGhzTxRxStateRx; - return value; -} - -static bool subghz_tx(SubGhz* subghz, uint32_t frequency) { - furi_assert(subghz); - if(!furi_hal_subghz_is_frequency_valid(frequency)) { - furi_crash("SubGhz: Incorrect TX frequency."); - } - furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep); - furi_hal_subghz_idle(); - furi_hal_subghz_set_frequency_and_path(frequency); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_cc1101_g0, true); - bool ret = furi_hal_subghz_tx(); - subghz->txrx->txrx_state = SubGhzTxRxStateTx; - return ret; -} - -void subghz_idle(SubGhz* subghz) { - furi_assert(subghz); - furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep); - furi_hal_subghz_idle(); - subghz->txrx->txrx_state = SubGhzTxRxStateIDLE; -} - -void subghz_rx_end(SubGhz* subghz) { - furi_assert(subghz); - furi_assert(subghz->txrx->txrx_state == SubGhzTxRxStateRx); - if(subghz_worker_is_running(subghz->txrx->worker)) { - subghz_worker_stop(subghz->txrx->worker); - furi_hal_subghz_stop_async_rx(); - } - furi_hal_subghz_idle(); - subghz->txrx->txrx_state = SubGhzTxRxStateIDLE; -} - -void subghz_sleep(SubGhz* subghz) { - furi_assert(subghz); - furi_hal_subghz_sleep(); - subghz->txrx->txrx_state = SubGhzTxRxStateSleep; -} - -bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format) { - furi_assert(subghz); - - bool ret = false; - string_t temp_str; - string_init(temp_str); - uint32_t repeat = 200; - do { - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) { - 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; - } - - subghz->txrx->transmitter = - subghz_transmitter_alloc_init(subghz->txrx->environment, string_get_cstr(temp_str)); - - if(subghz->txrx->transmitter) { - if(subghz_transmitter_deserialize(subghz->txrx->transmitter, flipper_format)) { - if(strcmp(string_get_cstr(subghz->txrx->preset->name), "")) { - subghz_begin( - subghz, - subghz_setting_get_preset_data_by_name( - subghz->setting, string_get_cstr(subghz->txrx->preset->name))); - } else { - FURI_LOG_E( - TAG, - "Unknown name preset \" %s \"", - string_get_cstr(subghz->txrx->preset->name)); - subghz_begin( - subghz, subghz_setting_get_preset_data_by_name(subghz->setting, "AM650")); - } - if(subghz->txrx->preset->frequency) { - ret = subghz_tx(subghz, subghz->txrx->preset->frequency); - } else { - ret = subghz_tx(subghz, 433920000); - } - if(ret) { - //Start TX - furi_hal_subghz_start_async_tx( - subghz_transmitter_yield, subghz->txrx->transmitter); - } - } - } - if(!ret) { - subghz_transmitter_free(subghz->txrx->transmitter); - if(subghz->txrx->txrx_state != SubGhzTxRxStateSleep) { - subghz_idle(subghz); - } - } - - } while(false); - string_clear(temp_str); - return ret; -} - -void subghz_tx_stop(SubGhz* subghz) { - furi_assert(subghz); - furi_assert(subghz->txrx->txrx_state == SubGhzTxRxStateTx); - //Stop TX - furi_hal_subghz_stop_async_tx(); - subghz_transmitter_stop(subghz->txrx->transmitter); - subghz_transmitter_free(subghz->txrx->transmitter); - - //if protocol dynamic then we save the last upload - if((subghz->txrx->decoder_result->protocol->type == SubGhzProtocolTypeDynamic) && - (subghz_path_is_file(subghz->file_path))) { - subghz_save_protocol_to_file( - subghz, subghz->txrx->fff_data, string_get_cstr(subghz->file_path)); - } - subghz_idle(subghz); - notification_message(subghz->notifications, &sequence_reset_red); -} - -void subghz_dialog_message_show_only_rx(SubGhz* subghz) { - DialogsApp* dialogs = subghz->dialogs; - DialogMessage* message = dialog_message_alloc(); - - const char* header_text = "Transmission is blocked"; - const char* message_text = "Transmission on\nthis frequency is\nrestricted in\nyour region"; - if(!furi_hal_region_is_provisioned()) { - header_text = "Firmware update needed"; - message_text = "Please update\nfirmware before\nusing this feature\nflipp.dev/upd"; - } - - dialog_message_set_header(message, header_text, 63, 3, AlignCenter, AlignTop); - dialog_message_set_text(message, message_text, 0, 17, AlignLeft, AlignTop); - - dialog_message_set_icon(message, &I_DolphinCommon_56x48, 72, 17); - - dialog_message_show(dialogs, message); - dialog_message_free(message); -} - -bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { - furi_assert(subghz); - furi_assert(file_path); - - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - Stream* fff_data_stream = flipper_format_get_raw_stream(subghz->txrx->fff_data); - - SubGhzLoadKeyState load_key_state = SubGhzLoadKeyStateParseErr; - string_t temp_str; - string_init(temp_str); - uint32_t temp_data32; - - do { - stream_clean(fff_data_stream); - if(!flipper_format_file_open_existing(fff_data_file, file_path)) { - FURI_LOG_E(TAG, "Error open file %s", file_path); - break; - } - - if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { - FURI_LOG_E(TAG, "Missing or incorrect header"); - break; - } - - if(((!strcmp(string_get_cstr(temp_str), SUBGHZ_KEY_FILE_TYPE)) || - (!strcmp(string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE))) && - temp_data32 == SUBGHZ_KEY_FILE_VERSION) { - } else { - FURI_LOG_E(TAG, "Type or version mismatch"); - break; - } - - if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) { - FURI_LOG_E(TAG, "Missing Frequency"); - break; - } - - if(!furi_hal_subghz_is_frequency_valid(temp_data32)) { - FURI_LOG_E(TAG, "Frequency not supported"); - break; - } - - if(!furi_hal_region_is_frequency_allowed(temp_data32)) { - FURI_LOG_E(TAG, "This frequency can only be used for RX in your region"); - load_key_state = SubGhzLoadKeyStateOnlyRx; - break; - } - subghz->txrx->preset->frequency = temp_data32; - - if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { - FURI_LOG_E(TAG, "Missing Preset"); - break; - } - - if(!subghz_set_preset(subghz, string_get_cstr(temp_str))) { - break; - } - - if(!strcmp(string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { - //Todo add Custom_preset_module - //delete preset if it already exists - subghz_setting_delete_custom_preset( - subghz->setting, string_get_cstr(subghz->txrx->preset->name)); - //load custom preset from file - if(!subghz_setting_load_custom_preset( - subghz->setting, string_get_cstr(subghz->txrx->preset->name), fff_data_file)) { - FURI_LOG_E(TAG, "Missing Custom preset"); - break; - } - } - size_t preset_index = subghz_setting_get_inx_preset_by_name( - subghz->setting, string_get_cstr(subghz->txrx->preset->name)); - subghz_preset_init( - subghz, - string_get_cstr(subghz->txrx->preset->name), - subghz->txrx->preset->frequency, - subghz_setting_get_preset_data(subghz->setting, preset_index), - subghz_setting_get_preset_data_size(subghz->setting, preset_index)); - - if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { - FURI_LOG_E(TAG, "Missing Protocol"); - break; - } - if(!strcmp(string_get_cstr(temp_str), "RAW")) { - //if RAW - subghz_protocol_raw_gen_fff_data(subghz->txrx->fff_data, file_path); - } else { - stream_copy_full( - flipper_format_get_raw_stream(fff_data_file), - flipper_format_get_raw_stream(subghz->txrx->fff_data)); - } - - subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( - subghz->txrx->receiver, string_get_cstr(temp_str)); - if(subghz->txrx->decoder_result) { - if(!subghz_protocol_decoder_base_deserialize( - subghz->txrx->decoder_result, subghz->txrx->fff_data)) { - break; - } - } else { - FURI_LOG_E(TAG, "Protocol not found"); - break; - } - - load_key_state = SubGhzLoadKeyStateOK; - } while(0); - - string_clear(temp_str); - flipper_format_free(fff_data_file); - furi_record_close(RECORD_STORAGE); - - switch(load_key_state) { - case SubGhzLoadKeyStateParseErr: - if(show_dialog) { - dialog_message_show_storage_error(subghz->dialogs, "Cannot parse\nfile"); - } - return false; - - case SubGhzLoadKeyStateOnlyRx: - if(show_dialog) { - subghz_dialog_message_show_only_rx(subghz); - } - return false; - - case SubGhzLoadKeyStateOK: - return true; - - default: - furi_crash("SubGhz: Unknown load_key_state."); - return false; - } -} - -bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len) { - furi_assert(subghz); - - Storage* storage = furi_record_open(RECORD_STORAGE); - string_t temp_str; - string_t file_name; - string_t file_path; - - string_init(temp_str); - string_init(file_name); - string_init(file_path); - - bool res = false; - - if(subghz_path_is_file(subghz->file_path)) { - //get the name of the next free file - path_extract_filename(subghz->file_path, file_name, true); - path_extract_dirname(string_get_cstr(subghz->file_path), file_path); - - storage_get_next_filename( - storage, - string_get_cstr(file_path), - string_get_cstr(file_name), - SUBGHZ_APP_EXTENSION, - file_name, - max_len); - - string_printf( - temp_str, - "%s/%s%s", - string_get_cstr(file_path), - string_get_cstr(file_name), - SUBGHZ_APP_EXTENSION); - string_set(subghz->file_path, temp_str); - res = true; - } - - string_clear(temp_str); - string_clear(file_path); - string_clear(file_name); - furi_record_close(RECORD_STORAGE); - - return res; -} - -bool subghz_save_protocol_to_file( - SubGhz* subghz, - FlipperFormat* flipper_format, - const char* dev_file_name) { - furi_assert(subghz); - furi_assert(flipper_format); - furi_assert(dev_file_name); - - Storage* storage = furi_record_open(RECORD_STORAGE); - Stream* flipper_format_stream = flipper_format_get_raw_stream(flipper_format); - - bool saved = false; - string_t file_dir; - string_init(file_dir); - - path_extract_dirname(dev_file_name, file_dir); - do { - //removing additional fields - flipper_format_delete_key(flipper_format, "Repeat"); - flipper_format_delete_key(flipper_format, "Manufacture"); - - // Create subghz folder directory if necessary - if(!storage_simply_mkdir(storage, string_get_cstr(file_dir))) { - dialog_message_show_storage_error(subghz->dialogs, "Cannot create\nfolder"); - break; - } - - if(!storage_simply_remove(storage, dev_file_name)) { - break; - } - //ToDo check Write - stream_seek(flipper_format_stream, 0, StreamOffsetFromStart); - stream_save_to_file(flipper_format_stream, storage, dev_file_name, FSOM_CREATE_ALWAYS); - - saved = true; - } while(0); - string_clear(file_dir); - furi_record_close(RECORD_STORAGE); - return saved; -} - -bool subghz_load_protocol_from_file(SubGhz* subghz) { - furi_assert(subghz); - - string_t file_path; - string_init(file_path); - - // Input events and views are managed by file_select - bool res = dialog_file_browser_show( - subghz->dialogs, - subghz->file_path, - subghz->file_path, - SUBGHZ_APP_EXTENSION, - true, - &I_sub1_10px, - true); - - if(res) { - res = subghz_key_load(subghz, string_get_cstr(subghz->file_path), true); - } - - string_clear(file_path); - - return res; -} - -bool subghz_rename_file(SubGhz* subghz) { - furi_assert(subghz); - bool ret = true; - - Storage* storage = furi_record_open(RECORD_STORAGE); - - if(string_cmp(subghz->file_path_tmp, subghz->file_path)) { - FS_Error fs_result = storage_common_rename( - storage, string_get_cstr(subghz->file_path_tmp), string_get_cstr(subghz->file_path)); - - if(fs_result != FSE_OK) { - dialog_message_show_storage_error(subghz->dialogs, "Cannot rename\n file/directory"); - ret = false; - } - } - furi_record_close(RECORD_STORAGE); - - return ret; -} - -bool subghz_delete_file(SubGhz* subghz) { - furi_assert(subghz); - - Storage* storage = furi_record_open(RECORD_STORAGE); - bool result = storage_simply_remove(storage, string_get_cstr(subghz->file_path_tmp)); - furi_record_close(RECORD_STORAGE); - - subghz_file_name_clear(subghz); - - return result; -} - -void subghz_file_name_clear(SubGhz* subghz) { - furi_assert(subghz); - string_set_str(subghz->file_path, SUBGHZ_APP_FOLDER); - string_reset(subghz->file_path_tmp); -} - -bool subghz_path_is_file(string_t path) { - return string_end_with_str_p(path, SUBGHZ_APP_EXTENSION); -} - -uint32_t subghz_random_serial(void) { - static bool rand_generator_inited = false; - - if(!rand_generator_inited) { - srand(DWT->CYCCNT); - rand_generator_inited = true; - } - return (uint32_t)rand(); -} - -void subghz_hopper_update(SubGhz* subghz) { - furi_assert(subghz); - - switch(subghz->txrx->hopper_state) { - case SubGhzHopperStateOFF: - return; - break; - case SubGhzHopperStatePause: - return; - break; - case SubGhzHopperStateRSSITimeOut: - if(subghz->txrx->hopper_timeout != 0) { - subghz->txrx->hopper_timeout--; - return; - } - break; - default: - break; - } - float rssi = -127.0f; - if(subghz->txrx->hopper_state != SubGhzHopperStateRSSITimeOut) { - // See RSSI Calculation timings in CC1101 17.3 RSSI - rssi = furi_hal_subghz_get_rssi(); - - // Stay if RSSI is high enough - if(rssi > -90.0f) { - subghz->txrx->hopper_timeout = 10; - subghz->txrx->hopper_state = SubGhzHopperStateRSSITimeOut; - return; - } - } else { - subghz->txrx->hopper_state = SubGhzHopperStateRunnig; - } - // Select next frequency - if(subghz->txrx->hopper_idx_frequency < - subghz_setting_get_hopper_frequency_count(subghz->setting) - 1) { - subghz->txrx->hopper_idx_frequency++; - } else { - subghz->txrx->hopper_idx_frequency = 0; - } - - if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) { - subghz_rx_end(subghz); - }; - if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { - subghz_receiver_reset(subghz->txrx->receiver); - subghz->txrx->preset->frequency = subghz_setting_get_hopper_frequency( - subghz->setting, subghz->txrx->hopper_idx_frequency); - subghz_rx(subghz, subghz->txrx->preset->frequency); - } -} diff --git a/applications/subghz/subghz_i.h b/applications/subghz/subghz_i.h deleted file mode 100644 index 99a0f8a28e0..00000000000 --- a/applications/subghz/subghz_i.h +++ /dev/null @@ -1,130 +0,0 @@ -#pragma once - -#include "helpers/subghz_types.h" -#include "subghz.h" -#include "views/receiver.h" -#include "views/transmitter.h" -#include "views/subghz_frequency_analyzer.h" -#include "views/subghz_read_raw.h" - -#include "views/subghz_test_static.h" -#include "views/subghz_test_carrier.h" -#include "views/subghz_test_packet.h" - -// #include -// #include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include - -#include "subghz_history.h" -#include "subghz_setting.h" - -#include -#include - -#include "rpc/rpc_app.h" - -#define SUBGHZ_MAX_LEN_NAME 64 - -struct SubGhzTxRx { - SubGhzWorker* worker; - - SubGhzEnvironment* environment; - SubGhzReceiver* receiver; - SubGhzTransmitter* transmitter; - SubGhzProtocolDecoderBase* decoder_result; - FlipperFormat* fff_data; - - SubGhzPresetDefinition* preset; - SubGhzHistory* history; - uint16_t idx_menu_chosen; - SubGhzTxRxState txrx_state; - SubGhzHopperState hopper_state; - uint8_t hopper_timeout; - uint8_t hopper_idx_frequency; - SubGhzRxKeyState rx_key_state; -}; - -typedef struct SubGhzTxRx SubGhzTxRx; - -struct SubGhz { - Gui* gui; - NotificationApp* notifications; - - SubGhzTxRx* txrx; - - SceneManager* scene_manager; - ViewDispatcher* view_dispatcher; - - Submenu* submenu; - Popup* popup; - TextInput* text_input; - Widget* widget; - DialogsApp* dialogs; - string_t file_path; - string_t file_path_tmp; - char file_name_tmp[SUBGHZ_MAX_LEN_NAME]; - SubGhzNotificationState state_notifications; - - SubGhzViewReceiver* subghz_receiver; - SubGhzViewTransmitter* subghz_transmitter; - VariableItemList* variable_item_list; - - SubGhzFrequencyAnalyzer* subghz_frequency_analyzer; - SubGhzReadRAW* subghz_read_raw; - SubGhzTestStatic* subghz_test_static; - SubGhzTestCarrier* subghz_test_carrier; - SubGhzTestPacket* subghz_test_packet; - string_t error_str; - SubGhzSetting* setting; - SubGhzLock lock; - - void* rpc_ctx; -}; - -void subghz_preset_init( - void* context, - const char* preset_name, - uint32_t frequency, - uint8_t* preset_data, - size_t preset_data_size); -bool subghz_set_preset(SubGhz* subghz, const char* preset); -void subghz_get_frequency_modulation(SubGhz* subghz, string_t frequency, string_t modulation); -void subghz_begin(SubGhz* subghz, uint8_t* preset_data); -uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency); -void subghz_rx_end(SubGhz* subghz); -void subghz_sleep(SubGhz* subghz); - -void subghz_blink_start(SubGhz* instance); -void subghz_blink_stop(SubGhz* instance); - -bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format); -void subghz_tx_stop(SubGhz* subghz); -void subghz_dialog_message_show_only_rx(SubGhz* subghz); -bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog); -bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len); -bool subghz_save_protocol_to_file( - SubGhz* subghz, - FlipperFormat* flipper_format, - const char* dev_file_name); -bool subghz_load_protocol_from_file(SubGhz* subghz); -bool subghz_rename_file(SubGhz* subghz); -bool subghz_delete_file(SubGhz* subghz); -void subghz_file_name_clear(SubGhz* subghz); -bool subghz_path_is_file(string_t path); -uint32_t subghz_random_serial(void); -void subghz_hopper_update(SubGhz* subghz); diff --git a/applications/subghz/views/receiver.c b/applications/subghz/views/receiver.c deleted file mode 100644 index c28c3363613..00000000000 --- a/applications/subghz/views/receiver.c +++ /dev/null @@ -1,429 +0,0 @@ -#include "receiver.h" -#include "../subghz_i.h" -#include - -#include -#include -#include -#include -#include - -#define FRAME_HEIGHT 12 -#define MAX_LEN_PX 100 -#define MENU_ITEMS 4u -#define UNLOCK_CNT 3 - -typedef struct { - string_t item_str; - uint8_t type; -} SubGhzReceiverMenuItem; - -ARRAY_DEF(SubGhzReceiverMenuItemArray, SubGhzReceiverMenuItem, M_POD_OPLIST) - -#define M_OPL_SubGhzReceiverMenuItemArray_t() \ - ARRAY_OPLIST(SubGhzReceiverMenuItemArray, M_POD_OPLIST) - -struct SubGhzReceiverHistory { - SubGhzReceiverMenuItemArray_t data; -}; - -typedef struct SubGhzReceiverHistory SubGhzReceiverHistory; - -static const Icon* ReceiverItemIcons[] = { - [SubGhzProtocolTypeUnknown] = &I_Quest_7x8, - [SubGhzProtocolTypeStatic] = &I_Unlock_7x8, - [SubGhzProtocolTypeDynamic] = &I_Lock_7x8, -}; - -typedef enum { - SubGhzViewReceiverBarShowDefault, - SubGhzViewReceiverBarShowLock, - SubGhzViewReceiverBarShowToUnlockPress, - SubGhzViewReceiverBarShowUnlock, -} SubGhzViewReceiverBarShow; - -struct SubGhzViewReceiver { - SubGhzLock lock; - uint8_t lock_count; - FuriTimer* timer; - View* view; - SubGhzViewReceiverCallback callback; - void* context; -}; - -typedef struct { - string_t frequency_str; - string_t preset_str; - string_t history_stat_str; - SubGhzReceiverHistory* history; - uint16_t idx; - uint16_t list_offset; - uint16_t history_item; - SubGhzViewReceiverBarShow bar_show; -} SubGhzViewReceiverModel; - -void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock lock) { - furi_assert(subghz_receiver); - subghz_receiver->lock_count = 0; - if(lock == SubGhzLockOn) { - subghz_receiver->lock = lock; - with_view_model( - subghz_receiver->view, (SubGhzViewReceiverModel * model) { - model->bar_show = SubGhzViewReceiverBarShowLock; - return true; - }); - furi_timer_start(subghz_receiver->timer, pdMS_TO_TICKS(1000)); - } else { - with_view_model( - subghz_receiver->view, (SubGhzViewReceiverModel * model) { - model->bar_show = SubGhzViewReceiverBarShowDefault; - return true; - }); - } -} - -void subghz_view_receiver_set_callback( - SubGhzViewReceiver* subghz_receiver, - SubGhzViewReceiverCallback callback, - void* context) { - furi_assert(subghz_receiver); - furi_assert(callback); - subghz_receiver->callback = callback; - subghz_receiver->context = context; -} - -static void subghz_view_receiver_update_offset(SubGhzViewReceiver* subghz_receiver) { - furi_assert(subghz_receiver); - - with_view_model( - subghz_receiver->view, (SubGhzViewReceiverModel * model) { - size_t history_item = model->history_item; - uint16_t bounds = history_item > 3 ? 2 : history_item; - - if(history_item > 3 && model->idx >= (int16_t)(history_item - 1)) { - model->list_offset = model->idx - 3; - } else if(model->list_offset < model->idx - bounds) { - model->list_offset = - CLAMP(model->list_offset + 1, (int16_t)(history_item - bounds), 0); - } else if(model->list_offset > model->idx - bounds) { - model->list_offset = CLAMP(model->idx - 1, (int16_t)(history_item - bounds), 0); - } - return true; - }); -} - -void subghz_view_receiver_add_item_to_menu( - SubGhzViewReceiver* subghz_receiver, - const char* name, - uint8_t type) { - furi_assert(subghz_receiver); - with_view_model( - subghz_receiver->view, (SubGhzViewReceiverModel * model) { - SubGhzReceiverMenuItem* item_menu = - SubGhzReceiverMenuItemArray_push_raw(model->history->data); - string_init_set_str(item_menu->item_str, name); - item_menu->type = type; - if((model->idx == model->history_item - 1)) { - model->history_item++; - model->idx++; - } else { - model->history_item++; - } - - return true; - }); - subghz_view_receiver_update_offset(subghz_receiver); -} - -void subghz_view_receiver_add_data_statusbar( - SubGhzViewReceiver* subghz_receiver, - const char* frequency_str, - const char* preset_str, - const char* history_stat_str) { - furi_assert(subghz_receiver); - with_view_model( - subghz_receiver->view, (SubGhzViewReceiverModel * model) { - string_set_str(model->frequency_str, frequency_str); - string_set_str(model->preset_str, preset_str); - string_set_str(model->history_stat_str, history_stat_str); - return true; - }); -} - -static void subghz_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { - canvas_set_color(canvas, ColorBlack); - canvas_draw_box(canvas, 0, 0 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT); - - canvas_set_color(canvas, ColorWhite); - canvas_draw_dot(canvas, 0, 0 + idx * FRAME_HEIGHT); - canvas_draw_dot(canvas, 1, 0 + idx * FRAME_HEIGHT); - canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 1); - - canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 11); - canvas_draw_dot(canvas, scrollbar ? 121 : 126, 0 + idx * FRAME_HEIGHT); - canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11); -} - -void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* 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; - string_t str_buff; - string_init(str_buff); - - SubGhzReceiverMenuItem* item_menu; - - for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) { - size_t idx = CLAMP((uint16_t)(i + model->list_offset), model->history_item, 0); - item_menu = SubGhzReceiverMenuItemArray_get(model->history->data, idx); - string_set(str_buff, item_menu->item_str); - elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX); - if(model->idx == idx) { - subghz_view_receiver_draw_frame(canvas, i, scrollbar); - } else { - canvas_set_color(canvas, ColorBlack); - } - canvas_draw_icon(canvas, 1, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); - canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, string_get_cstr(str_buff)); - string_reset(str_buff); - } - if(scrollbar) { - elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item); - } - string_clear(str_buff); - - canvas_set_color(canvas, ColorBlack); - - if(model->history_item == 0) { - 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); - } - - switch(model->bar_show) { - case SubGhzViewReceiverBarShowLock: - canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8); - canvas_draw_str(canvas, 74, 62, "Locked"); - break; - case SubGhzViewReceiverBarShowToUnlockPress: - canvas_draw_str(canvas, 44, 62, string_get_cstr(model->frequency_str)); - canvas_draw_str(canvas, 79, 62, string_get_cstr(model->preset_str)); - canvas_draw_str(canvas, 96, 62, string_get_cstr(model->history_stat_str)); - canvas_set_font(canvas, FontSecondary); - elements_bold_rounded_frame(canvas, 14, 8, 99, 48); - elements_multiline_text(canvas, 65, 26, "To unlock\npress:"); - canvas_draw_icon(canvas, 65, 42, &I_Pin_back_arrow_10x8); - canvas_draw_icon(canvas, 80, 42, &I_Pin_back_arrow_10x8); - canvas_draw_icon(canvas, 95, 42, &I_Pin_back_arrow_10x8); - canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42); - canvas_draw_dot(canvas, 17, 61); - break; - case SubGhzViewReceiverBarShowUnlock: - canvas_draw_icon(canvas, 64, 55, &I_Unlock_7x8); - canvas_draw_str(canvas, 74, 62, "Unlocked"); - break; - default: - canvas_draw_str(canvas, 44, 62, string_get_cstr(model->frequency_str)); - canvas_draw_str(canvas, 79, 62, string_get_cstr(model->preset_str)); - canvas_draw_str(canvas, 96, 62, string_get_cstr(model->history_stat_str)); - break; - } -} - -static void subghz_view_receiver_timer_callback(void* context) { - furi_assert(context); - SubGhzViewReceiver* subghz_receiver = context; - with_view_model( - subghz_receiver->view, (SubGhzViewReceiverModel * model) { - model->bar_show = SubGhzViewReceiverBarShowDefault; - return true; - }); - if(subghz_receiver->lock_count < UNLOCK_CNT) { - subghz_receiver->callback( - SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context); - } else { - subghz_receiver->lock = SubGhzLockOff; - subghz_receiver->callback(SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context); - } - subghz_receiver->lock_count = 0; -} - -bool subghz_view_receiver_input(InputEvent* event, void* context) { - furi_assert(context); - SubGhzViewReceiver* subghz_receiver = context; - - if(subghz_receiver->lock == SubGhzLockOn) { - with_view_model( - subghz_receiver->view, (SubGhzViewReceiverModel * model) { - model->bar_show = SubGhzViewReceiverBarShowToUnlockPress; - return true; - }); - if(subghz_receiver->lock_count == 0) { - furi_timer_start(subghz_receiver->timer, pdMS_TO_TICKS(1000)); - } - if(event->key == InputKeyBack && event->type == InputTypeShort) { - subghz_receiver->lock_count++; - } - if(subghz_receiver->lock_count >= UNLOCK_CNT) { - // subghz_receiver->callback( - // SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context); - with_view_model( - subghz_receiver->view, (SubGhzViewReceiverModel * model) { - model->bar_show = SubGhzViewReceiverBarShowUnlock; - return true; - }); - //subghz_receiver->lock = SubGhzLockOff; - furi_timer_start(subghz_receiver->timer, pdMS_TO_TICKS(650)); - } - - return true; - } - - if(event->key == InputKeyBack && event->type == InputTypeShort) { - subghz_receiver->callback(SubGhzCustomEventViewReceiverBack, subghz_receiver->context); - } else if( - event->key == InputKeyUp && - (event->type == InputTypeShort || event->type == InputTypeRepeat)) { - with_view_model( - subghz_receiver->view, (SubGhzViewReceiverModel * model) { - if(model->idx != 0) model->idx--; - return true; - }); - } else if( - event->key == InputKeyDown && - (event->type == InputTypeShort || event->type == InputTypeRepeat)) { - with_view_model( - subghz_receiver->view, (SubGhzViewReceiverModel * model) { - if(model->idx != model->history_item - 1) model->idx++; - return true; - }); - } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { - subghz_receiver->callback(SubGhzCustomEventViewReceiverConfig, subghz_receiver->context); - } else if(event->key == InputKeyOk && event->type == InputTypeShort) { - with_view_model( - subghz_receiver->view, (SubGhzViewReceiverModel * model) { - if(model->history_item != 0) { - subghz_receiver->callback( - SubGhzCustomEventViewReceiverOK, subghz_receiver->context); - } - return false; - }); - } - - subghz_view_receiver_update_offset(subghz_receiver); - - return true; -} - -void subghz_view_receiver_enter(void* context) { - furi_assert(context); -} - -void subghz_view_receiver_exit(void* context) { - furi_assert(context); - SubGhzViewReceiver* subghz_receiver = context; - with_view_model( - subghz_receiver->view, (SubGhzViewReceiverModel * model) { - string_reset(model->frequency_str); - string_reset(model->preset_str); - string_reset(model->history_stat_str); - for - M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) { - string_clear(item_menu->item_str); - item_menu->type = 0; - } - SubGhzReceiverMenuItemArray_reset(model->history->data); - model->idx = 0; - model->list_offset = 0; - model->history_item = 0; - return false; - }); - furi_timer_stop(subghz_receiver->timer); -} - -SubGhzViewReceiver* subghz_view_receiver_alloc() { - SubGhzViewReceiver* subghz_receiver = malloc(sizeof(SubGhzViewReceiver)); - - // View allocation and configuration - subghz_receiver->view = view_alloc(); - - subghz_receiver->lock = SubGhzLockOff; - subghz_receiver->lock_count = 0; - view_allocate_model( - subghz_receiver->view, ViewModelTypeLocking, sizeof(SubGhzViewReceiverModel)); - view_set_context(subghz_receiver->view, subghz_receiver); - view_set_draw_callback(subghz_receiver->view, (ViewDrawCallback)subghz_view_receiver_draw); - view_set_input_callback(subghz_receiver->view, subghz_view_receiver_input); - view_set_enter_callback(subghz_receiver->view, subghz_view_receiver_enter); - view_set_exit_callback(subghz_receiver->view, subghz_view_receiver_exit); - - with_view_model( - subghz_receiver->view, (SubGhzViewReceiverModel * model) { - string_init(model->frequency_str); - string_init(model->preset_str); - string_init(model->history_stat_str); - model->bar_show = SubGhzViewReceiverBarShowDefault; - model->history = malloc(sizeof(SubGhzReceiverHistory)); - SubGhzReceiverMenuItemArray_init(model->history->data); - return true; - }); - subghz_receiver->timer = - furi_timer_alloc(subghz_view_receiver_timer_callback, FuriTimerTypeOnce, subghz_receiver); - return subghz_receiver; -} - -void subghz_view_receiver_free(SubGhzViewReceiver* subghz_receiver) { - furi_assert(subghz_receiver); - - with_view_model( - subghz_receiver->view, (SubGhzViewReceiverModel * model) { - string_clear(model->frequency_str); - string_clear(model->preset_str); - string_clear(model->history_stat_str); - for - M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) { - string_clear(item_menu->item_str); - item_menu->type = 0; - } - SubGhzReceiverMenuItemArray_clear(model->history->data); - free(model->history); - return false; - }); - furi_timer_free(subghz_receiver->timer); - view_free(subghz_receiver->view); - free(subghz_receiver); -} - -View* subghz_view_receiver_get_view(SubGhzViewReceiver* subghz_receiver) { - furi_assert(subghz_receiver); - return subghz_receiver->view; -} - -uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver) { - furi_assert(subghz_receiver); - uint32_t idx = 0; - with_view_model( - subghz_receiver->view, (SubGhzViewReceiverModel * model) { - idx = model->idx; - return false; - }); - return idx; -} - -void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx) { - furi_assert(subghz_receiver); - with_view_model( - subghz_receiver->view, (SubGhzViewReceiverModel * model) { - model->idx = idx; - if(model->idx > 2) model->list_offset = idx - 2; - return true; - }); - subghz_view_receiver_update_offset(subghz_receiver); -} diff --git a/applications/subghz/views/subghz_frequency_analyzer.c b/applications/subghz/views/subghz_frequency_analyzer.c deleted file mode 100644 index d3f773159eb..00000000000 --- a/applications/subghz/views/subghz_frequency_analyzer.c +++ /dev/null @@ -1,180 +0,0 @@ -#include "subghz_frequency_analyzer.h" -#include "../subghz_i.h" - -#include -#include -#include -#include -#include -#include "../helpers/subghz_frequency_analyzer_worker.h" - -#include - -typedef enum { - SubGhzFrequencyAnalyzerStatusIDLE, -} SubGhzFrequencyAnalyzerStatus; - -struct SubGhzFrequencyAnalyzer { - View* view; - SubGhzFrequencyAnalyzerWorker* worker; - SubGhzFrequencyAnalyzerCallback callback; - void* context; - bool locked; -}; - -typedef struct { - uint32_t frequency; - float rssi; -} SubGhzFrequencyAnalyzerModel; - -void subghz_frequency_analyzer_set_callback( - SubGhzFrequencyAnalyzer* subghz_frequency_analyzer, - SubGhzFrequencyAnalyzerCallback callback, - void* context) { - furi_assert(subghz_frequency_analyzer); - furi_assert(callback); - subghz_frequency_analyzer->callback = callback; - subghz_frequency_analyzer->context = context; -} - -void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, float rssi) { - uint8_t x = 48; - uint8_t y = 56; - uint8_t column_number = 0; - if(rssi) { - rssi = (rssi + 90) / 3; - for(size_t i = 1; i < (uint8_t)rssi; i++) { - if(i > 20) break; - if(i % 4) { - column_number++; - canvas_draw_box(canvas, x + 2 * i, y - column_number, 2, 4 + column_number); - } - } - } -} - -void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel* model) { - char buffer[64]; - - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 20, 8, "Frequency Analyzer"); - - canvas_draw_str(canvas, 28, 60, "RSSI"); - subghz_frequency_analyzer_draw_rssi(canvas, model->rssi); - - //Frequency - canvas_set_font(canvas, FontBigNumbers); - snprintf( - buffer, - sizeof(buffer), - "%03ld.%03ld", - model->frequency / 1000000 % 1000, - model->frequency / 1000 % 1000); - canvas_draw_str(canvas, 8, 35, buffer); - canvas_draw_icon(canvas, 96, 24, &I_MHz_25x11); -} - -bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { - furi_assert(context); - - if(event->key == InputKeyBack) { - return false; - } - - return true; -} - -void subghz_frequency_analyzer_pair_callback(void* context, uint32_t frequency, float rssi) { - SubGhzFrequencyAnalyzer* instance = context; - if((rssi == 0.f) && (instance->locked)) { - if(instance->callback) { - instance->callback(SubGhzCustomEventSceneAnalyzerUnlock, instance->context); - } - } else if((rssi != 0.f) && (!instance->locked)) { - if(instance->callback) { - instance->callback(SubGhzCustomEventSceneAnalyzerLock, instance->context); - } - } - - instance->locked = (rssi != 0.f); - with_view_model( - instance->view, (SubGhzFrequencyAnalyzerModel * model) { - model->rssi = rssi; - model->frequency = frequency; - return true; - }); -} - -void subghz_frequency_analyzer_enter(void* context) { - furi_assert(context); - SubGhzFrequencyAnalyzer* instance = context; - - //Start worker - instance->worker = subghz_frequency_analyzer_worker_alloc(instance->context); - - subghz_frequency_analyzer_worker_set_pair_callback( - instance->worker, - (SubGhzFrequencyAnalyzerWorkerPairCallback)subghz_frequency_analyzer_pair_callback, - instance); - - subghz_frequency_analyzer_worker_start(instance->worker); - - with_view_model( - instance->view, (SubGhzFrequencyAnalyzerModel * model) { - model->rssi = 0; - model->frequency = 0; - return true; - }); -} - -void subghz_frequency_analyzer_exit(void* context) { - furi_assert(context); - SubGhzFrequencyAnalyzer* instance = context; - - //Stop worker - if(subghz_frequency_analyzer_worker_is_running(instance->worker)) { - subghz_frequency_analyzer_worker_stop(instance->worker); - } - subghz_frequency_analyzer_worker_free(instance->worker); - - with_view_model( - instance->view, (SubGhzFrequencyAnalyzerModel * model) { - model->rssi = 0; - return true; - }); -} - -SubGhzFrequencyAnalyzer* subghz_frequency_analyzer_alloc() { - SubGhzFrequencyAnalyzer* instance = malloc(sizeof(SubGhzFrequencyAnalyzer)); - - // View allocation and configuration - instance->view = view_alloc(); - view_allocate_model( - instance->view, ViewModelTypeLocking, sizeof(SubGhzFrequencyAnalyzerModel)); - view_set_context(instance->view, instance); - view_set_draw_callback(instance->view, (ViewDrawCallback)subghz_frequency_analyzer_draw); - view_set_input_callback(instance->view, subghz_frequency_analyzer_input); - view_set_enter_callback(instance->view, subghz_frequency_analyzer_enter); - view_set_exit_callback(instance->view, subghz_frequency_analyzer_exit); - - with_view_model( - instance->view, (SubGhzFrequencyAnalyzerModel * model) { - model->rssi = 0; - return true; - }); - - return instance; -} - -void subghz_frequency_analyzer_free(SubGhzFrequencyAnalyzer* instance) { - furi_assert(instance); - - view_free(instance->view); - free(instance); -} - -View* subghz_frequency_analyzer_get_view(SubGhzFrequencyAnalyzer* instance) { - furi_assert(instance); - return instance->view; -} diff --git a/applications/subghz/views/subghz_read_raw.c b/applications/subghz/views/subghz_read_raw.c deleted file mode 100644 index ccffaf42f7f..00000000000 --- a/applications/subghz/views/subghz_read_raw.c +++ /dev/null @@ -1,534 +0,0 @@ -#include "subghz_read_raw.h" -#include "../subghz_i.h" - -#include -#include -#include -#include -#include - -#include -#define SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE 100 -#define TAG "SubGhzReadRAW" - -struct SubGhzReadRAW { - View* view; - SubGhzReadRAWCallback callback; - void* context; -}; - -typedef struct { - string_t frequency_str; - string_t preset_str; - string_t sample_write; - string_t file_name; - uint8_t* rssi_history; - bool rssi_history_end; - uint8_t ind_write; - uint8_t ind_sin; - SubGhzReadRAWStatus status; -} SubGhzReadRAWModel; - -void subghz_read_raw_set_callback( - SubGhzReadRAW* subghz_read_raw, - SubGhzReadRAWCallback callback, - void* context) { - furi_assert(subghz_read_raw); - furi_assert(callback); - subghz_read_raw->callback = callback; - subghz_read_raw->context = context; -} - -void subghz_read_raw_add_data_statusbar( - SubGhzReadRAW* instance, - const char* frequency_str, - const char* preset_str) { - furi_assert(instance); - with_view_model( - instance->view, (SubGhzReadRAWModel * model) { - string_set_str(model->frequency_str, frequency_str); - string_set_str(model->preset_str, preset_str); - return true; - }); -} - -void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi) { - furi_assert(instance); - uint8_t u_rssi = 0; - - if(rssi < -90) { - u_rssi = 0; - } else { - u_rssi = (uint8_t)((rssi + 90) / 2.7); - } - - with_view_model( - instance->view, (SubGhzReadRAWModel * model) { - model->rssi_history[model->ind_write++] = u_rssi; - if(model->ind_write > SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE) { - model->rssi_history_end = true; - model->ind_write = 0; - } - return true; - }); -} - -void subghz_read_raw_update_sample_write(SubGhzReadRAW* instance, size_t sample) { - furi_assert(instance); - - with_view_model( - instance->view, (SubGhzReadRAWModel * model) { - string_printf(model->sample_write, "%d spl.", sample); - return false; - }); -} - -void subghz_read_raw_stop_send(SubGhzReadRAW* instance) { - furi_assert(instance); - - with_view_model( - instance->view, (SubGhzReadRAWModel * model) { - switch(model->status) { - case SubGhzReadRAWStatusTXRepeat: - case SubGhzReadRAWStatusLoadKeyTXRepeat: - instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context); - break; - case SubGhzReadRAWStatusTX: - model->status = SubGhzReadRAWStatusIDLE; - break; - case SubGhzReadRAWStatusLoadKeyTX: - model->status = SubGhzReadRAWStatusLoadKeyIDLE; - break; - - default: - FURI_LOG_W(TAG, "unknown status"); - model->status = SubGhzReadRAWStatusIDLE; - break; - } - return true; - }); -} - -void subghz_read_raw_update_sin(SubGhzReadRAW* instance) { - furi_assert(instance); - with_view_model( - instance->view, (SubGhzReadRAWModel * model) { - if(model->ind_sin++ > 62) { - model->ind_sin = 0; - } - return true; - }); -} - -static int8_t subghz_read_raw_tab_sin(uint8_t x) { - const uint8_t tab_sin[64] = {0, 3, 6, 9, 12, 16, 19, 22, 25, 28, 31, 34, 37, - 40, 43, 46, 49, 51, 54, 57, 60, 63, 65, 68, 71, 73, - 76, 78, 81, 83, 85, 88, 90, 92, 94, 96, 98, 100, 102, - 104, 106, 107, 109, 111, 112, 113, 115, 116, 117, 118, 120, 121, - 122, 122, 123, 124, 125, 125, 126, 126, 126, 127, 127, 127}; - - int8_t r = tab_sin[((x & 0x40) ? -x - 1 : x) & 0x3f]; - if(x & 0x80) return -r; - return r; -} - -void subghz_read_raw_draw_sin(Canvas* canvas, SubGhzReadRAWModel* model) { -#define SUBGHZ_RAW_SIN_AMPLITUDE 11 - for(int i = 113; i > 0; i--) { - canvas_draw_line( - canvas, - i, - 32 - subghz_read_raw_tab_sin(i + model->ind_sin * 16) / SUBGHZ_RAW_SIN_AMPLITUDE, - i + 1, - 32 + subghz_read_raw_tab_sin((i + model->ind_sin * 16 + 1) * 2) / - SUBGHZ_RAW_SIN_AMPLITUDE); - canvas_draw_line( - canvas, - i + 1, - 32 - subghz_read_raw_tab_sin((i + model->ind_sin * 16)) / SUBGHZ_RAW_SIN_AMPLITUDE, - i + 2, - 32 + subghz_read_raw_tab_sin((i + model->ind_sin * 16 + 1) * 2) / - SUBGHZ_RAW_SIN_AMPLITUDE); - } -} - -void subghz_read_raw_draw_scale(Canvas* canvas, SubGhzReadRAWModel* model) { -#define SUBGHZ_RAW_TOP_SCALE 14 -#define SUBGHZ_RAW_END_SCALE 115 - - if(model->rssi_history_end == false) { - for(int i = SUBGHZ_RAW_END_SCALE; i > 0; i -= 15) { - canvas_draw_line(canvas, i, SUBGHZ_RAW_TOP_SCALE, i, SUBGHZ_RAW_TOP_SCALE + 4); - canvas_draw_line(canvas, i - 5, SUBGHZ_RAW_TOP_SCALE, i - 5, SUBGHZ_RAW_TOP_SCALE + 2); - canvas_draw_line( - canvas, i - 10, SUBGHZ_RAW_TOP_SCALE, i - 10, SUBGHZ_RAW_TOP_SCALE + 2); - } - } else { - for(int i = SUBGHZ_RAW_END_SCALE - model->ind_write % 15; i > -15; i -= 15) { - canvas_draw_line(canvas, i, SUBGHZ_RAW_TOP_SCALE, i, SUBGHZ_RAW_TOP_SCALE + 4); - if(SUBGHZ_RAW_END_SCALE > i + 5) - canvas_draw_line( - canvas, i + 5, SUBGHZ_RAW_TOP_SCALE, i + 5, SUBGHZ_RAW_TOP_SCALE + 2); - if(SUBGHZ_RAW_END_SCALE > i + 10) - canvas_draw_line( - canvas, i + 10, SUBGHZ_RAW_TOP_SCALE, i + 10, SUBGHZ_RAW_TOP_SCALE + 2); - } - } -} - -void subghz_read_raw_draw_rssi(Canvas* canvas, SubGhzReadRAWModel* model) { - int ind = 0; - int base = 0; - if(model->rssi_history_end == false) { - for(int i = model->ind_write; i >= 0; i--) { - canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[i]); - } - if(model->ind_write > 3) { - canvas_draw_line(canvas, model->ind_write, 47, model->ind_write, 13); - canvas_draw_line(canvas, model->ind_write - 2, 12, model->ind_write + 2, 12); - canvas_draw_line(canvas, model->ind_write - 1, 13, model->ind_write + 1, 13); - } - } else { - base = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - model->ind_write; - for(int i = SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE; i >= 0; i--) { - ind = i - base; - if(ind < 0) ind += SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE; - canvas_draw_line(canvas, i, 47, i, 47 - model->rssi_history[ind]); - } - canvas_draw_line( - canvas, SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, 47, SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE, 13); - canvas_draw_line( - canvas, - SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 2, - 12, - SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 2, - 12); - canvas_draw_line( - canvas, - SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE - 1, - 13, - SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE + 1, - 13); - } -} - -void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) { - uint8_t graphics_mode = 1; - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 5, 7, string_get_cstr(model->frequency_str)); - canvas_draw_str(canvas, 40, 7, string_get_cstr(model->preset_str)); - canvas_draw_str_aligned( - canvas, 126, 0, AlignRight, AlignTop, string_get_cstr(model->sample_write)); - - canvas_draw_line(canvas, 0, 14, 115, 14); - canvas_draw_line(canvas, 0, 48, 115, 48); - canvas_draw_line(canvas, 115, 14, 115, 48); - - switch(model->status) { - case SubGhzReadRAWStatusIDLE: - elements_button_left(canvas, "Erase"); - elements_button_center(canvas, "Send"); - elements_button_right(canvas, "Save"); - break; - case SubGhzReadRAWStatusLoadKeyIDLE: - elements_button_left(canvas, "New"); - elements_button_center(canvas, "Send"); - elements_button_right(canvas, "More"); - elements_text_box( - canvas, - 4, - 20, - 110, - 30, - AlignCenter, - AlignCenter, - string_get_cstr(model->file_name), - true); - break; - - case SubGhzReadRAWStatusTX: - case SubGhzReadRAWStatusTXRepeat: - case SubGhzReadRAWStatusLoadKeyTX: - case SubGhzReadRAWStatusLoadKeyTXRepeat: - graphics_mode = 0; - elements_button_center(canvas, "Send"); - break; - - case SubGhzReadRAWStatusStart: - elements_button_left(canvas, "Config"); - elements_button_center(canvas, "REC"); - break; - - default: - elements_button_center(canvas, "Stop"); - break; - } - - if(graphics_mode == 0) { - subghz_read_raw_draw_sin(canvas, model); - } else { - subghz_read_raw_draw_rssi(canvas, model); - subghz_read_raw_draw_scale(canvas, model); - canvas_set_font_direction(canvas, CanvasDirectionBottomToTop); - canvas_draw_str(canvas, 126, 40, "RSSI"); - canvas_set_font_direction(canvas, CanvasDirectionLeftToRight); - } -} - -bool subghz_read_raw_input(InputEvent* event, void* context) { - furi_assert(context); - SubGhzReadRAW* instance = context; - - if((event->key == InputKeyOk) && - (event->type == InputTypeLong || event->type == InputTypeRepeat)) { - //we check that if we hold the transfer button, - //further check of events is not needed, we exit - return false; - } else if(event->key == InputKeyOk && event->type == InputTypePress) { - with_view_model( - instance->view, (SubGhzReadRAWModel * model) { - uint8_t ret = false; - switch(model->status) { - case SubGhzReadRAWStatusIDLE: - // Start TX - instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context); - model->status = SubGhzReadRAWStatusTXRepeat; - ret = true; - break; - case SubGhzReadRAWStatusTX: - // Start TXRepeat - model->status = SubGhzReadRAWStatusTXRepeat; - break; - case SubGhzReadRAWStatusLoadKeyIDLE: - // Start Load Key TX - instance->callback(SubGhzCustomEventViewReadRAWSendStart, instance->context); - model->status = SubGhzReadRAWStatusLoadKeyTXRepeat; - ret = true; - break; - case SubGhzReadRAWStatusLoadKeyTX: - // Start Load Key TXRepeat - model->status = SubGhzReadRAWStatusLoadKeyTXRepeat; - break; - - default: - break; - } - return ret; - }); - } else if(event->key == InputKeyOk && event->type == InputTypeRelease) { - with_view_model( - instance->view, (SubGhzReadRAWModel * model) { - if(model->status == SubGhzReadRAWStatusTXRepeat) { - // Stop repeat TX - model->status = SubGhzReadRAWStatusTX; - } else if(model->status == SubGhzReadRAWStatusLoadKeyTXRepeat) { - // Stop repeat TX - model->status = SubGhzReadRAWStatusLoadKeyTX; - } - return false; - }); - } else if(event->key == InputKeyBack && event->type == InputTypeShort) { - with_view_model( - instance->view, (SubGhzReadRAWModel * model) { - switch(model->status) { - case SubGhzReadRAWStatusREC: - //Stop REC - instance->callback(SubGhzCustomEventViewReadRAWIDLE, instance->context); - model->status = SubGhzReadRAWStatusIDLE; - break; - case SubGhzReadRAWStatusLoadKeyTX: - //Stop TxRx - instance->callback(SubGhzCustomEventViewReadRAWTXRXStop, instance->context); - model->status = SubGhzReadRAWStatusLoadKeyIDLE; - break; - case SubGhzReadRAWStatusTX: - //Stop TxRx - instance->callback(SubGhzCustomEventViewReadRAWTXRXStop, instance->context); - model->status = SubGhzReadRAWStatusIDLE; - break; - case SubGhzReadRAWStatusLoadKeyIDLE: - //Exit - instance->callback(SubGhzCustomEventViewReadRAWBack, instance->context); - break; - - default: - //Exit - instance->callback(SubGhzCustomEventViewReadRAWBack, instance->context); - break; - } - return true; - }); - } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { - with_view_model( - instance->view, (SubGhzReadRAWModel * model) { - if(model->status == SubGhzReadRAWStatusStart) { - //Config - instance->callback(SubGhzCustomEventViewReadRAWConfig, instance->context); - } else if( - (model->status == SubGhzReadRAWStatusIDLE) || - (model->status == SubGhzReadRAWStatusLoadKeyIDLE)) { - //Erase - model->status = SubGhzReadRAWStatusStart; - model->rssi_history_end = false; - model->ind_write = 0; - string_set_str(model->sample_write, "0 spl."); - string_reset(model->file_name); - instance->callback(SubGhzCustomEventViewReadRAWErase, instance->context); - } - return true; - }); - } else if(event->key == InputKeyRight && event->type == InputTypeShort) { - with_view_model( - instance->view, (SubGhzReadRAWModel * model) { - if(model->status == SubGhzReadRAWStatusIDLE) { - //Save - instance->callback(SubGhzCustomEventViewReadRAWSave, instance->context); - } else if(model->status == SubGhzReadRAWStatusLoadKeyIDLE) { - //More - instance->callback(SubGhzCustomEventViewReadRAWMore, instance->context); - } - return true; - }); - } else if(event->key == InputKeyOk && event->type == InputTypeShort) { - with_view_model( - instance->view, (SubGhzReadRAWModel * model) { - if(model->status == SubGhzReadRAWStatusStart) { - //Record - instance->callback(SubGhzCustomEventViewReadRAWREC, instance->context); - model->status = SubGhzReadRAWStatusREC; - model->ind_write = 0; - model->rssi_history_end = false; - } else if(model->status == SubGhzReadRAWStatusREC) { - //Stop - instance->callback(SubGhzCustomEventViewReadRAWIDLE, instance->context); - model->status = SubGhzReadRAWStatusIDLE; - } - return true; - }); - } - return true; -} - -void subghz_read_raw_set_status( - SubGhzReadRAW* instance, - SubGhzReadRAWStatus status, - const char* file_name) { - furi_assert(instance); - - switch(status) { - case SubGhzReadRAWStatusStart: - with_view_model( - instance->view, (SubGhzReadRAWModel * model) { - model->status = SubGhzReadRAWStatusStart; - model->rssi_history_end = false; - model->ind_write = 0; - string_reset(model->file_name); - string_set_str(model->sample_write, "0 spl."); - return true; - }); - break; - case SubGhzReadRAWStatusIDLE: - with_view_model( - instance->view, (SubGhzReadRAWModel * model) { - model->status = SubGhzReadRAWStatusIDLE; - return true; - }); - break; - case SubGhzReadRAWStatusLoadKeyTX: - with_view_model( - instance->view, (SubGhzReadRAWModel * model) { - model->status = SubGhzReadRAWStatusLoadKeyIDLE; - model->rssi_history_end = false; - model->ind_write = 0; - string_set_str(model->file_name, file_name); - string_set_str(model->sample_write, "RAW"); - return true; - }); - break; - case SubGhzReadRAWStatusSaveKey: - with_view_model( - instance->view, (SubGhzReadRAWModel * model) { - model->status = SubGhzReadRAWStatusLoadKeyIDLE; - if(!model->ind_write) { - string_set_str(model->file_name, file_name); - string_set_str(model->sample_write, "RAW"); - } else { - string_reset(model->file_name); - } - return true; - }); - break; - - default: - FURI_LOG_W(TAG, "unknown status"); - break; - } -} - -void subghz_read_raw_enter(void* context) { - furi_assert(context); - //SubGhzReadRAW* instance = context; -} - -void subghz_read_raw_exit(void* context) { - furi_assert(context); - SubGhzReadRAW* instance = context; - - with_view_model( - instance->view, (SubGhzReadRAWModel * model) { - if(model->status != SubGhzReadRAWStatusIDLE && - model->status != SubGhzReadRAWStatusStart && - model->status != SubGhzReadRAWStatusLoadKeyIDLE) { - instance->callback(SubGhzCustomEventViewReadRAWIDLE, instance->context); - model->status = SubGhzReadRAWStatusStart; - } - return true; - }); -} - -SubGhzReadRAW* subghz_read_raw_alloc() { - SubGhzReadRAW* instance = malloc(sizeof(SubGhzReadRAW)); - - // View allocation and configuration - instance->view = view_alloc(); - view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(SubGhzReadRAWModel)); - view_set_context(instance->view, instance); - view_set_draw_callback(instance->view, (ViewDrawCallback)subghz_read_raw_draw); - view_set_input_callback(instance->view, subghz_read_raw_input); - view_set_enter_callback(instance->view, subghz_read_raw_enter); - view_set_exit_callback(instance->view, subghz_read_raw_exit); - - with_view_model( - instance->view, (SubGhzReadRAWModel * model) { - string_init(model->frequency_str); - string_init(model->preset_str); - string_init(model->sample_write); - string_init(model->file_name); - model->rssi_history = malloc(SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE * sizeof(uint8_t)); - return true; - }); - - return instance; -} - -void subghz_read_raw_free(SubGhzReadRAW* instance) { - furi_assert(instance); - - with_view_model( - instance->view, (SubGhzReadRAWModel * model) { - string_clear(model->frequency_str); - string_clear(model->preset_str); - string_clear(model->sample_write); - string_clear(model->file_name); - free(model->rssi_history); - return true; - }); - view_free(instance->view); - free(instance); -} - -View* subghz_read_raw_get_view(SubGhzReadRAW* instance) { - furi_assert(instance); - return instance->view; -} diff --git a/applications/subghz/views/transmitter.c b/applications/subghz/views/transmitter.c deleted file mode 100644 index 3cbcf098a85..00000000000 --- a/applications/subghz/views/transmitter.c +++ /dev/null @@ -1,175 +0,0 @@ -#include "transmitter.h" -#include "../subghz_i.h" - -#include -#include - -struct SubGhzViewTransmitter { - View* view; - SubGhzViewTransmitterCallback callback; - void* context; -}; - -typedef struct { - string_t frequency_str; - string_t preset_str; - string_t key_str; - uint8_t show_button; -} SubGhzViewTransmitterModel; - -void subghz_view_transmitter_set_callback( - SubGhzViewTransmitter* subghz_transmitter, - SubGhzViewTransmitterCallback callback, - void* context) { - furi_assert(subghz_transmitter); - - subghz_transmitter->callback = callback; - subghz_transmitter->context = context; -} - -void subghz_view_transmitter_add_data_to_show( - SubGhzViewTransmitter* subghz_transmitter, - const char* key_str, - const char* frequency_str, - const char* preset_str, - uint8_t show_button) { - furi_assert(subghz_transmitter); - with_view_model( - subghz_transmitter->view, (SubGhzViewTransmitterModel * model) { - string_set_str(model->key_str, key_str); - string_set_str(model->frequency_str, frequency_str); - string_set_str(model->preset_str, preset_str); - model->show_button = show_button; - return true; - }); -} - -static void subghz_view_transmitter_button_right(Canvas* canvas, const char* str) { - const uint8_t button_height = 13; - const uint8_t vertical_offset = 3; - const uint8_t horizontal_offset = 1; - const uint8_t string_width = canvas_string_width(canvas, str); - const Icon* icon = &I_ButtonCenter_7x7; - const uint8_t icon_offset = 3; - const uint8_t icon_width_with_offset = icon_get_width(icon) + icon_offset; - const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; - - const uint8_t x = (canvas_width(canvas) - button_width) / 2 + 40; - const uint8_t y = canvas_height(canvas); - - canvas_draw_box(canvas, x, y - button_height, button_width, button_height); - - canvas_draw_line(canvas, x - 1, y, x - 1, y - button_height + 0); - canvas_draw_line(canvas, x - 2, y, x - 2, y - button_height + 1); - canvas_draw_line(canvas, x - 3, y, x - 3, y - button_height + 2); - - canvas_draw_line(canvas, x + button_width + 0, y, x + button_width + 0, y - button_height + 0); - canvas_draw_line(canvas, x + button_width + 1, y, x + button_width + 1, y - button_height + 1); - canvas_draw_line(canvas, x + button_width + 2, y, x + button_width + 2, y - button_height + 2); - - canvas_invert_color(canvas); - canvas_draw_icon( - canvas, x + horizontal_offset, y - button_height + vertical_offset, &I_ButtonCenter_7x7); - canvas_draw_str( - canvas, x + horizontal_offset + icon_width_with_offset, y - vertical_offset, str); - canvas_invert_color(canvas); -} - -void subghz_view_transmitter_draw(Canvas* canvas, SubGhzViewTransmitterModel* model) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text(canvas, 0, 8, string_get_cstr(model->key_str)); - canvas_draw_str(canvas, 78, 8, string_get_cstr(model->frequency_str)); - canvas_draw_str(canvas, 113, 8, string_get_cstr(model->preset_str)); - if(model->show_button) subghz_view_transmitter_button_right(canvas, "Send"); -} - -bool subghz_view_transmitter_input(InputEvent* event, void* context) { - furi_assert(context); - SubGhzViewTransmitter* subghz_transmitter = context; - bool can_be_sent = false; - - if(event->key == InputKeyBack && event->type == InputTypeShort) { - with_view_model( - subghz_transmitter->view, (SubGhzViewTransmitterModel * model) { - string_reset(model->frequency_str); - string_reset(model->preset_str); - string_reset(model->key_str); - model->show_button = 0; - return false; - }); - return false; - } - - with_view_model( - subghz_transmitter->view, (SubGhzViewTransmitterModel * model) { - if(model->show_button) { - can_be_sent = true; - } - return true; - }); - - if(can_be_sent && event->key == InputKeyOk && event->type == InputTypePress) { - subghz_transmitter->callback( - SubGhzCustomEventViewTransmitterSendStart, subghz_transmitter->context); - return true; - } else if(can_be_sent && event->key == InputKeyOk && event->type == InputTypeRelease) { - subghz_transmitter->callback( - SubGhzCustomEventViewTransmitterSendStop, subghz_transmitter->context); - return true; - } - - return true; -} - -void subghz_view_transmitter_enter(void* context) { - furi_assert(context); -} - -void subghz_view_transmitter_exit(void* context) { - furi_assert(context); -} - -SubGhzViewTransmitter* subghz_view_transmitter_alloc() { - SubGhzViewTransmitter* subghz_transmitter = malloc(sizeof(SubGhzViewTransmitter)); - - // View allocation and configuration - subghz_transmitter->view = view_alloc(); - view_allocate_model( - subghz_transmitter->view, ViewModelTypeLocking, sizeof(SubGhzViewTransmitterModel)); - view_set_context(subghz_transmitter->view, subghz_transmitter); - view_set_draw_callback( - subghz_transmitter->view, (ViewDrawCallback)subghz_view_transmitter_draw); - view_set_input_callback(subghz_transmitter->view, subghz_view_transmitter_input); - view_set_enter_callback(subghz_transmitter->view, subghz_view_transmitter_enter); - view_set_exit_callback(subghz_transmitter->view, subghz_view_transmitter_exit); - - with_view_model( - subghz_transmitter->view, (SubGhzViewTransmitterModel * model) { - string_init(model->frequency_str); - string_init(model->preset_str); - string_init(model->key_str); - return true; - }); - return subghz_transmitter; -} - -void subghz_view_transmitter_free(SubGhzViewTransmitter* subghz_transmitter) { - furi_assert(subghz_transmitter); - - with_view_model( - subghz_transmitter->view, (SubGhzViewTransmitterModel * model) { - string_clear(model->frequency_str); - string_clear(model->preset_str); - string_clear(model->key_str); - return true; - }); - view_free(subghz_transmitter->view); - free(subghz_transmitter); -} - -View* subghz_view_transmitter_get_view(SubGhzViewTransmitter* subghz_transmitter) { - furi_assert(subghz_transmitter); - return subghz_transmitter->view; -} diff --git a/applications/system/application.fam b/applications/system/application.fam index 0fc456b2f88..a59f840e456 100644 --- a/applications/system/application.fam +++ b/applications/system/application.fam @@ -1,9 +1,10 @@ App( - appid="system_settings", - name="System", - apptype=FlipperAppType.SETTINGS, - entry_point="system_settings_app", - requires=["gui"], - stack_size=1 * 1024, - order=70, + appid="system_apps", + name="Applications not shown in menus", + apptype=FlipperAppType.METAPACKAGE, + provides=[ + "updater_app", + "storage_move_to_sd", + # "archive", + ], ) diff --git a/applications/system/hid_app/application.fam b/applications/system/hid_app/application.fam new file mode 100644 index 00000000000..a1fb314b82c --- /dev/null +++ b/applications/system/hid_app/application.fam @@ -0,0 +1,28 @@ +App( + appid="hid_usb", + name="Remote", + apptype=FlipperAppType.EXTERNAL, + entry_point="hid_usb_app", + stack_size=1 * 1024, + fap_description="Use Flipper as a HID remote control over USB", + fap_version="1.0", + fap_category="USB", + fap_icon="hid_usb_10px.png", + fap_icon_assets="assets", + fap_icon_assets_symbol="hid", +) + + +App( + appid="hid_ble", + name="Remote", + apptype=FlipperAppType.EXTERNAL, + entry_point="hid_ble_app", + stack_size=1 * 1024, + fap_description="Use Flipper as a HID remote control over Bluetooth", + fap_version="1.0", + fap_category="Bluetooth", + fap_icon="hid_ble_10px.png", + fap_icon_assets="assets", + fap_icon_assets_symbol="hid", +) diff --git a/applications/system/hid_app/assets/Alt_17x10.png b/applications/system/hid_app/assets/Alt_17x10.png new file mode 100644 index 00000000000..93041d25039 Binary files /dev/null and b/applications/system/hid_app/assets/Alt_17x10.png differ diff --git a/applications/system/hid_app/assets/Arr_dwn_7x9.png b/applications/system/hid_app/assets/Arr_dwn_7x9.png new file mode 100644 index 00000000000..d4034efc432 Binary files /dev/null and b/applications/system/hid_app/assets/Arr_dwn_7x9.png differ diff --git a/applications/system/hid_app/assets/Arr_up_7x9.png b/applications/system/hid_app/assets/Arr_up_7x9.png new file mode 100644 index 00000000000..28b4236a292 Binary files /dev/null and b/applications/system/hid_app/assets/Arr_up_7x9.png differ diff --git a/applications/system/hid_app/assets/Ble_connected_15x15.png b/applications/system/hid_app/assets/Ble_connected_15x15.png new file mode 100644 index 00000000000..64dab9b5307 Binary files /dev/null and b/applications/system/hid_app/assets/Ble_connected_15x15.png differ diff --git a/applications/system/hid_app/assets/Ble_disconnected_15x15.png b/applications/system/hid_app/assets/Ble_disconnected_15x15.png new file mode 100644 index 00000000000..bd54646d891 Binary files /dev/null and b/applications/system/hid_app/assets/Ble_disconnected_15x15.png differ diff --git a/applications/system/hid_app/assets/ButtonDown_7x4.png b/applications/system/hid_app/assets/ButtonDown_7x4.png new file mode 100644 index 00000000000..2954bb6a67d Binary files /dev/null and b/applications/system/hid_app/assets/ButtonDown_7x4.png differ diff --git a/applications/system/hid_app/assets/ButtonF10_5x8.png b/applications/system/hid_app/assets/ButtonF10_5x8.png new file mode 100644 index 00000000000..d1a7a04f06d Binary files /dev/null and b/applications/system/hid_app/assets/ButtonF10_5x8.png differ diff --git a/applications/system/hid_app/assets/ButtonF11_5x8.png b/applications/system/hid_app/assets/ButtonF11_5x8.png new file mode 100644 index 00000000000..7e177358e81 Binary files /dev/null and b/applications/system/hid_app/assets/ButtonF11_5x8.png differ diff --git a/applications/system/hid_app/assets/ButtonF12_5x8.png b/applications/system/hid_app/assets/ButtonF12_5x8.png new file mode 100644 index 00000000000..50d2a7dc63b Binary files /dev/null and b/applications/system/hid_app/assets/ButtonF12_5x8.png differ diff --git a/applications/system/hid_app/assets/ButtonF1_5x8.png b/applications/system/hid_app/assets/ButtonF1_5x8.png new file mode 100644 index 00000000000..7394d27105f Binary files /dev/null and b/applications/system/hid_app/assets/ButtonF1_5x8.png differ diff --git a/applications/system/hid_app/assets/ButtonF2_5x8.png b/applications/system/hid_app/assets/ButtonF2_5x8.png new file mode 100644 index 00000000000..9d922a38581 Binary files /dev/null and b/applications/system/hid_app/assets/ButtonF2_5x8.png differ diff --git a/applications/system/hid_app/assets/ButtonF3_5x8.png b/applications/system/hid_app/assets/ButtonF3_5x8.png new file mode 100644 index 00000000000..95c2dd4f419 Binary files /dev/null and b/applications/system/hid_app/assets/ButtonF3_5x8.png differ diff --git a/applications/system/hid_app/assets/ButtonF4_5x8.png b/applications/system/hid_app/assets/ButtonF4_5x8.png new file mode 100644 index 00000000000..602466f4b66 Binary files /dev/null and b/applications/system/hid_app/assets/ButtonF4_5x8.png differ diff --git a/applications/system/hid_app/assets/ButtonF5_5x8.png b/applications/system/hid_app/assets/ButtonF5_5x8.png new file mode 100644 index 00000000000..d73b5405275 Binary files /dev/null and b/applications/system/hid_app/assets/ButtonF5_5x8.png differ diff --git a/applications/system/hid_app/assets/ButtonF6_5x8.png b/applications/system/hid_app/assets/ButtonF6_5x8.png new file mode 100644 index 00000000000..c50748257ab Binary files /dev/null and b/applications/system/hid_app/assets/ButtonF6_5x8.png differ diff --git a/applications/system/hid_app/assets/ButtonF7_5x8.png b/applications/system/hid_app/assets/ButtonF7_5x8.png new file mode 100644 index 00000000000..396c98f5104 Binary files /dev/null and b/applications/system/hid_app/assets/ButtonF7_5x8.png differ diff --git a/applications/system/hid_app/assets/ButtonF8_5x8.png b/applications/system/hid_app/assets/ButtonF8_5x8.png new file mode 100644 index 00000000000..6304d7fb888 Binary files /dev/null and b/applications/system/hid_app/assets/ButtonF8_5x8.png differ diff --git a/applications/system/hid_app/assets/ButtonF9_5x8.png b/applications/system/hid_app/assets/ButtonF9_5x8.png new file mode 100644 index 00000000000..148e69580f1 Binary files /dev/null and b/applications/system/hid_app/assets/ButtonF9_5x8.png differ diff --git a/applications/system/hid_app/assets/ButtonLeft_4x7.png b/applications/system/hid_app/assets/ButtonLeft_4x7.png new file mode 100644 index 00000000000..0b4655d4324 Binary files /dev/null and b/applications/system/hid_app/assets/ButtonLeft_4x7.png differ diff --git a/applications/system/hid_app/assets/ButtonRight_4x7.png b/applications/system/hid_app/assets/ButtonRight_4x7.png new file mode 100644 index 00000000000..8e1c74c1c00 Binary files /dev/null and b/applications/system/hid_app/assets/ButtonRight_4x7.png differ diff --git a/applications/system/hid_app/assets/ButtonUp_7x4.png b/applications/system/hid_app/assets/ButtonUp_7x4.png new file mode 100644 index 00000000000..1be79328b40 Binary files /dev/null and b/applications/system/hid_app/assets/ButtonUp_7x4.png differ diff --git a/applications/system/hid_app/assets/Button_18x18.png b/applications/system/hid_app/assets/Button_18x18.png new file mode 100644 index 00000000000..30a5b4fab23 Binary files /dev/null and b/applications/system/hid_app/assets/Button_18x18.png differ diff --git a/applications/system/hid_app/assets/Circles_47x47.png b/applications/system/hid_app/assets/Circles_47x47.png new file mode 100644 index 00000000000..6a16ebf7bbe Binary files /dev/null and b/applications/system/hid_app/assets/Circles_47x47.png differ diff --git a/applications/system/hid_app/assets/Cmd_17x10.png b/applications/system/hid_app/assets/Cmd_17x10.png new file mode 100644 index 00000000000..26ca3395c69 Binary files /dev/null and b/applications/system/hid_app/assets/Cmd_17x10.png differ diff --git a/applications/system/hid_app/assets/Ctrl_17x10.png b/applications/system/hid_app/assets/Ctrl_17x10.png new file mode 100644 index 00000000000..0eda7216000 Binary files /dev/null and b/applications/system/hid_app/assets/Ctrl_17x10.png differ diff --git a/applications/system/hid_app/assets/Del_17x10.png b/applications/system/hid_app/assets/Del_17x10.png new file mode 100644 index 00000000000..13d73698307 Binary files /dev/null and b/applications/system/hid_app/assets/Del_17x10.png differ diff --git a/applications/system/hid_app/assets/Esc_17x10.png b/applications/system/hid_app/assets/Esc_17x10.png new file mode 100644 index 00000000000..6a011e97a8a Binary files /dev/null and b/applications/system/hid_app/assets/Esc_17x10.png differ diff --git a/applications/system/hid_app/assets/Left_mouse_icon_9x9.png b/applications/system/hid_app/assets/Left_mouse_icon_9x9.png new file mode 100644 index 00000000000..c533d85729f Binary files /dev/null and b/applications/system/hid_app/assets/Left_mouse_icon_9x9.png differ diff --git a/applications/system/hid_app/assets/Like_def_11x9.png b/applications/system/hid_app/assets/Like_def_11x9.png new file mode 100644 index 00000000000..555bea3d4b0 Binary files /dev/null and b/applications/system/hid_app/assets/Like_def_11x9.png differ diff --git a/applications/system/hid_app/assets/Like_pressed_17x17.png b/applications/system/hid_app/assets/Like_pressed_17x17.png new file mode 100644 index 00000000000..f5bf276f31a Binary files /dev/null and b/applications/system/hid_app/assets/Like_pressed_17x17.png differ diff --git a/applications/system/hid_app/assets/Ok_btn_9x9.png b/applications/system/hid_app/assets/Ok_btn_9x9.png new file mode 100644 index 00000000000..9a1539da204 Binary files /dev/null and b/applications/system/hid_app/assets/Ok_btn_9x9.png differ diff --git a/applications/system/hid_app/assets/Ok_btn_pressed_13x13.png b/applications/system/hid_app/assets/Ok_btn_pressed_13x13.png new file mode 100644 index 00000000000..6b46ba3a824 Binary files /dev/null and b/applications/system/hid_app/assets/Ok_btn_pressed_13x13.png differ diff --git a/assets/icons/PIN/Pin_arrow_down_7x9.png b/applications/system/hid_app/assets/Pin_arrow_down_7x9.png similarity index 100% rename from assets/icons/PIN/Pin_arrow_down_7x9.png rename to applications/system/hid_app/assets/Pin_arrow_down_7x9.png diff --git a/assets/icons/PIN/Pin_arrow_left_9x7.png b/applications/system/hid_app/assets/Pin_arrow_left_9x7.png similarity index 100% rename from assets/icons/PIN/Pin_arrow_left_9x7.png rename to applications/system/hid_app/assets/Pin_arrow_left_9x7.png diff --git a/assets/icons/PIN/Pin_arrow_right_9x7.png b/applications/system/hid_app/assets/Pin_arrow_right_9x7.png similarity index 100% rename from assets/icons/PIN/Pin_arrow_right_9x7.png rename to applications/system/hid_app/assets/Pin_arrow_right_9x7.png diff --git a/assets/icons/PIN/Pin_arrow_up7x9.png b/applications/system/hid_app/assets/Pin_arrow_up_7x9.png similarity index 100% rename from assets/icons/PIN/Pin_arrow_up7x9.png rename to applications/system/hid_app/assets/Pin_arrow_up_7x9.png diff --git a/applications/system/hid_app/assets/Pin_back_arrow_10x8.png b/applications/system/hid_app/assets/Pin_back_arrow_10x8.png new file mode 100644 index 00000000000..3bafabd1448 Binary files /dev/null and b/applications/system/hid_app/assets/Pin_back_arrow_10x8.png differ diff --git a/applications/system/hid_app/assets/Pressed_Button_13x13.png b/applications/system/hid_app/assets/Pressed_Button_13x13.png new file mode 100644 index 00000000000..823926b842b Binary files /dev/null and b/applications/system/hid_app/assets/Pressed_Button_13x13.png differ diff --git a/applications/system/hid_app/assets/Right_mouse_icon_9x9.png b/applications/system/hid_app/assets/Right_mouse_icon_9x9.png new file mode 100644 index 00000000000..446d7176c8d Binary files /dev/null and b/applications/system/hid_app/assets/Right_mouse_icon_9x9.png differ diff --git a/applications/system/hid_app/assets/Space_60x18.png b/applications/system/hid_app/assets/Space_60x18.png new file mode 100644 index 00000000000..e29f50ae922 Binary files /dev/null and b/applications/system/hid_app/assets/Space_60x18.png differ diff --git a/applications/system/hid_app/assets/Space_65x18.png b/applications/system/hid_app/assets/Space_65x18.png new file mode 100644 index 00000000000..b60ae50970b Binary files /dev/null and b/applications/system/hid_app/assets/Space_65x18.png differ diff --git a/applications/system/hid_app/assets/Tab_17x10.png b/applications/system/hid_app/assets/Tab_17x10.png new file mode 100644 index 00000000000..c62d75c0db5 Binary files /dev/null and b/applications/system/hid_app/assets/Tab_17x10.png differ diff --git a/applications/system/hid_app/assets/Voldwn_6x6.png b/applications/system/hid_app/assets/Voldwn_6x6.png new file mode 100644 index 00000000000..d7a82a2df82 Binary files /dev/null and b/applications/system/hid_app/assets/Voldwn_6x6.png differ diff --git a/applications/system/hid_app/assets/Volup_8x6.png b/applications/system/hid_app/assets/Volup_8x6.png new file mode 100644 index 00000000000..4b7ec66d651 Binary files /dev/null and b/applications/system/hid_app/assets/Volup_8x6.png differ diff --git a/applications/system/hid_app/hid.c b/applications/system/hid_app/hid.c new file mode 100644 index 00000000000..88a68f09d0f --- /dev/null +++ b/applications/system/hid_app/hid.c @@ -0,0 +1,472 @@ +#include "hid.h" +#include "views.h" +#include +#include + +#define TAG "HidApp" + +enum HidDebugSubmenuIndex { + HidSubmenuIndexKeynote, + HidSubmenuIndexKeynoteVertical, + HidSubmenuIndexKeyboard, + HidSubmenuIndexMedia, + HidSubmenuIndexTikTok, + HidSubmenuIndexMouse, + HidSubmenuIndexMouseClicker, + HidSubmenuIndexMouseJiggler, + HidSubmenuIndexRemovePairing, +}; + +static void bt_hid_remove_pairing(Bt* bt) { + bt_disconnect(bt); + + // Wait 2nd core to update nvm storage + furi_delay_ms(200); + + furi_hal_bt_stop_advertising(); + + bt_forget_bonded_devices(bt); + + furi_hal_bt_start_advertising(); +} + +static void hid_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + Hid* app = context; + if(index == HidSubmenuIndexKeynote) { + app->view_id = HidViewKeynote; + hid_keynote_set_orientation(app->hid_keynote, false); + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote); + } else if(index == HidSubmenuIndexKeynoteVertical) { + app->view_id = HidViewKeynote; + hid_keynote_set_orientation(app->hid_keynote, true); + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote); + } else if(index == HidSubmenuIndexKeyboard) { + app->view_id = HidViewKeyboard; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeyboard); + } else if(index == HidSubmenuIndexMedia) { + app->view_id = HidViewMedia; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMedia); + } else if(index == HidSubmenuIndexMouse) { + app->view_id = HidViewMouse; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouse); + } else if(index == HidSubmenuIndexTikTok) { + app->view_id = BtHidViewTikTok; + view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok); + } else if(index == HidSubmenuIndexMouseClicker) { + app->view_id = HidViewMouseClicker; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseClicker); + } else if(index == HidSubmenuIndexMouseJiggler) { + app->view_id = HidViewMouseJiggler; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler); + } else if(index == HidSubmenuIndexRemovePairing) { + bt_hid_remove_pairing(app->bt); + } +} + +static void bt_hid_connection_status_changed_callback(BtStatus status, void* context) { + furi_assert(context); + Hid* hid = context; + bool connected = (status == BtStatusConnected); + if(hid->transport == HidTransportBle) { + if(connected) { + notification_internal_message(hid->notifications, &sequence_set_blue_255); + } else { + notification_internal_message(hid->notifications, &sequence_reset_blue); + } + } + hid_keynote_set_connected_status(hid->hid_keynote, connected); + hid_keyboard_set_connected_status(hid->hid_keyboard, connected); + hid_media_set_connected_status(hid->hid_media, connected); + hid_mouse_set_connected_status(hid->hid_mouse, connected); + hid_mouse_clicker_set_connected_status(hid->hid_mouse_clicker, connected); + hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected); + hid_tiktok_set_connected_status(hid->hid_tiktok, connected); +} + +static void hid_dialog_callback(DialogExResult result, void* context) { + furi_assert(context); + Hid* 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, HidViewSubmenu); + } +} + +static uint32_t hid_exit_confirm_view(void* context) { + UNUSED(context); + return HidViewExitConfirm; +} + +static uint32_t hid_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +Hid* hid_alloc(HidTransport transport) { + Hid* app = malloc(sizeof(Hid)); + app->transport = transport; + + // Gui + app->gui = furi_record_open(RECORD_GUI); + + // Bt + app->bt = furi_record_open(RECORD_BT); + + // 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); + // Device Type Submenu view + app->device_type_submenu = submenu_alloc(); + submenu_add_item( + app->device_type_submenu, "Keynote", HidSubmenuIndexKeynote, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, + "Keynote Vertical", + HidSubmenuIndexKeynoteVertical, + hid_submenu_callback, + app); + submenu_add_item( + app->device_type_submenu, "Keyboard", HidSubmenuIndexKeyboard, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, "Media", HidSubmenuIndexMedia, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, "Mouse", HidSubmenuIndexMouse, hid_submenu_callback, app); + if(app->transport == HidTransportBle) { + submenu_add_item( + app->device_type_submenu, + "TikTok Controller", + HidSubmenuIndexTikTok, + hid_submenu_callback, + app); + } + submenu_add_item( + app->device_type_submenu, + "Mouse Clicker", + HidSubmenuIndexMouseClicker, + hid_submenu_callback, + app); + submenu_add_item( + app->device_type_submenu, + "Mouse Jiggler", + HidSubmenuIndexMouseJiggler, + hid_submenu_callback, + app); + if(transport == HidTransportBle) { + submenu_add_item( + app->device_type_submenu, + "Remove Pairing", + HidSubmenuIndexRemovePairing, + hid_submenu_callback, + app); + } + view_set_previous_callback(submenu_get_view(app->device_type_submenu), hid_exit); + view_dispatcher_add_view( + app->view_dispatcher, HidViewSubmenu, submenu_get_view(app->device_type_submenu)); + app->view_id = HidViewSubmenu; + view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); + return app; +} + +Hid* hid_app_alloc_view(void* context) { + furi_assert(context); + Hid* app = context; + // Dialog view + app->dialog = dialog_ex_alloc(); + dialog_ex_set_result_callback(app->dialog, 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, HidViewExitConfirm, dialog_ex_get_view(app->dialog)); + + // Keynote view + app->hid_keynote = hid_keynote_alloc(app); + view_set_previous_callback(hid_keynote_get_view(app->hid_keynote), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewKeynote, hid_keynote_get_view(app->hid_keynote)); + + // Keyboard view + app->hid_keyboard = hid_keyboard_alloc(app); + view_set_previous_callback(hid_keyboard_get_view(app->hid_keyboard), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewKeyboard, hid_keyboard_get_view(app->hid_keyboard)); + + // Media view + app->hid_media = hid_media_alloc(app); + view_set_previous_callback(hid_media_get_view(app->hid_media), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewMedia, hid_media_get_view(app->hid_media)); + + // TikTok view + app->hid_tiktok = hid_tiktok_alloc(app); + view_set_previous_callback(hid_tiktok_get_view(app->hid_tiktok), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, BtHidViewTikTok, hid_tiktok_get_view(app->hid_tiktok)); + + // Mouse view + app->hid_mouse = hid_mouse_alloc(app); + view_set_previous_callback(hid_mouse_get_view(app->hid_mouse), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse)); + + // Mouse clicker view + app->hid_mouse_clicker = hid_mouse_clicker_alloc(app); + view_set_previous_callback( + hid_mouse_clicker_get_view(app->hid_mouse_clicker), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, + HidViewMouseClicker, + hid_mouse_clicker_get_view(app->hid_mouse_clicker)); + + // 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), hid_exit_confirm_view); + view_dispatcher_add_view( + app->view_dispatcher, + HidViewMouseJiggler, + hid_mouse_jiggler_get_view(app->hid_mouse_jiggler)); + + return app; +} + +void hid_free(Hid* app) { + furi_assert(app); + + // Reset notification + if(app->transport == HidTransportBle) { + notification_internal_message(app->notifications, &sequence_reset_blue); + } + + // Free views + view_dispatcher_remove_view(app->view_dispatcher, HidViewSubmenu); + submenu_free(app->device_type_submenu); + view_dispatcher_remove_view(app->view_dispatcher, HidViewExitConfirm); + dialog_ex_free(app->dialog); + view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynote); + hid_keynote_free(app->hid_keynote); + view_dispatcher_remove_view(app->view_dispatcher, HidViewKeyboard); + hid_keyboard_free(app->hid_keyboard); + view_dispatcher_remove_view(app->view_dispatcher, HidViewMedia); + hid_media_free(app->hid_media); + view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse); + hid_mouse_free(app->hid_mouse); + view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseClicker); + hid_mouse_clicker_free(app->hid_mouse_clicker); + view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseJiggler); + hid_mouse_jiggler_free(app->hid_mouse_jiggler); + view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); + hid_tiktok_free(app->hid_tiktok); + view_dispatcher_free(app->view_dispatcher); + + // Close records + furi_record_close(RECORD_GUI); + app->gui = NULL; + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + furi_record_close(RECORD_BT); + app->bt = NULL; + + // Free rest + free(app); +} + +void hid_hal_keyboard_press(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_kb_press(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_kb_press(event); + } else { + furi_crash(); + } +} + +void hid_hal_keyboard_release(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_kb_release(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_kb_release(event); + } else { + furi_crash(); + } +} + +void hid_hal_keyboard_release_all(Hid* instance) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_kb_release_all(); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_kb_release_all(); + } else { + furi_crash(); + } +} + +void hid_hal_consumer_key_press(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_consumer_key_press(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_consumer_key_press(event); + } else { + furi_crash(); + } +} + +void hid_hal_consumer_key_release(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_consumer_key_release(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_consumer_key_release(event); + } else { + furi_crash(); + } +} + +void hid_hal_consumer_key_release_all(Hid* instance) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_consumer_key_release_all(); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_kb_release_all(); + } else { + furi_crash(); + } +} + +void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_move(dx, dy); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_move(dx, dy); + } else { + furi_crash(); + } +} + +void hid_hal_mouse_scroll(Hid* instance, int8_t delta) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_scroll(delta); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_scroll(delta); + } else { + furi_crash(); + } +} + +void hid_hal_mouse_press(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_press(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_press(event); + } else { + furi_crash(); + } +} + +void hid_hal_mouse_release(Hid* instance, uint16_t event) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_release(event); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_release(event); + } else { + furi_crash(); + } +} + +void hid_hal_mouse_release_all(Hid* instance) { + furi_assert(instance); + if(instance->transport == HidTransportBle) { + furi_hal_bt_hid_mouse_release_all(); + } else if(instance->transport == HidTransportUsb) { + furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT); + furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT); + } else { + furi_crash(); + } +} + +int32_t hid_usb_app(void* p) { + UNUSED(p); + Hid* app = hid_alloc(HidTransportUsb); + app = hid_app_alloc_view(app); + FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); + furi_hal_usb_unlock(); + furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); + + bt_hid_connection_status_changed_callback(BtStatusConnected, app); + + dolphin_deed(DolphinDeedPluginStart); + + view_dispatcher_run(app->view_dispatcher); + + furi_hal_usb_set_config(usb_mode_prev, NULL); + + hid_free(app); + + return 0; +} + +int32_t hid_ble_app(void* p) { + UNUSED(p); + Hid* app = hid_alloc(HidTransportBle); + app = hid_app_alloc_view(app); + + bt_disconnect(app->bt); + + // Wait 2nd core to update nvm storage + furi_delay_ms(200); + + // 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); + + furi_check(bt_set_profile(app->bt, BtProfileHidKeyboard)); + + furi_hal_bt_start_advertising(); + bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); + + dolphin_deed(DolphinDeedPluginStart); + + view_dispatcher_run(app->view_dispatcher); + + bt_set_status_changed_callback(app->bt, NULL, NULL); + + bt_disconnect(app->bt); + + // Wait 2nd core to update nvm storage + furi_delay_ms(200); + + bt_keys_storage_set_default_path(app->bt); + + furi_check(bt_set_profile(app->bt, BtProfileSerial)); + + hid_free(app); + + return 0; +} diff --git a/applications/system/hid_app/hid.h b/applications/system/hid_app/hid.h new file mode 100644 index 00000000000..49d8b4e045d --- /dev/null +++ b/applications/system/hid_app/hid.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "views/hid_keynote.h" +#include "views/hid_keyboard.h" +#include "views/hid_media.h" +#include "views/hid_mouse.h" +#include "views/hid_mouse_clicker.h" +#include "views/hid_mouse_jiggler.h" +#include "views/hid_tiktok.h" + +#define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys" + +typedef enum { + HidTransportUsb, + HidTransportBle, +} HidTransport; + +typedef struct Hid Hid; + +struct Hid { + Bt* bt; + Gui* gui; + NotificationApp* notifications; + ViewDispatcher* view_dispatcher; + Submenu* device_type_submenu; + DialogEx* dialog; + HidKeynote* hid_keynote; + HidKeyboard* hid_keyboard; + HidMedia* hid_media; + HidMouse* hid_mouse; + HidMouseClicker* hid_mouse_clicker; + HidMouseJiggler* hid_mouse_jiggler; + HidTikTok* hid_tiktok; + + HidTransport transport; + uint32_t view_id; +}; + +void hid_hal_keyboard_press(Hid* instance, uint16_t event); +void hid_hal_keyboard_release(Hid* instance, uint16_t event); +void hid_hal_keyboard_release_all(Hid* instance); + +void hid_hal_consumer_key_press(Hid* instance, uint16_t event); +void hid_hal_consumer_key_release(Hid* instance, uint16_t event); +void hid_hal_consumer_key_release_all(Hid* instance); + +void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy); +void hid_hal_mouse_scroll(Hid* instance, int8_t delta); +void hid_hal_mouse_press(Hid* instance, uint16_t event); +void hid_hal_mouse_release(Hid* instance, uint16_t event); +void hid_hal_mouse_release_all(Hid* instance); \ No newline at end of file diff --git a/applications/system/hid_app/hid_ble_10px.png b/applications/system/hid_app/hid_ble_10px.png new file mode 100644 index 00000000000..d4d30afe046 Binary files /dev/null and b/applications/system/hid_app/hid_ble_10px.png differ diff --git a/applications/system/hid_app/hid_usb_10px.png b/applications/system/hid_app/hid_usb_10px.png new file mode 100644 index 00000000000..415de7d2304 Binary files /dev/null and b/applications/system/hid_app/hid_usb_10px.png differ diff --git a/applications/system/hid_app/views.h b/applications/system/hid_app/views.h new file mode 100644 index 00000000000..1bea3355e0d --- /dev/null +++ b/applications/system/hid_app/views.h @@ -0,0 +1,11 @@ +typedef enum { + HidViewSubmenu, + HidViewKeynote, + HidViewKeyboard, + HidViewMedia, + HidViewMouse, + HidViewMouseClicker, + HidViewMouseJiggler, + BtHidViewTikTok, + HidViewExitConfirm, +} HidView; \ No newline at end of file diff --git a/applications/system/hid_app/views/hid_keyboard.c b/applications/system/hid_app/views/hid_keyboard.c new file mode 100644 index 00000000000..9060c1d6a64 --- /dev/null +++ b/applications/system/hid_app/views/hid_keyboard.c @@ -0,0 +1,411 @@ +#include "hid_keyboard.h" +#include +#include +#include +#include "../hid.h" +#include "hid_icons.h" + +#define TAG "HidKeyboard" + +struct HidKeyboard { + View* view; + Hid* hid; +}; + +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]; + HidTransport transport; +} HidKeyboardModel; + +typedef struct { + uint8_t width; + char* key; + const Icon* icon; + char* shift_key; + uint8_t value; +} HidKeyboardKey; + +typedef struct { + int8_t x; + int8_t y; +} HidKeyboardPoint; +// 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 their value is used +const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { + { + {.width = 1, .icon = &I_ButtonF1_5x8, .value = HID_KEYBOARD_F1}, + {.width = 1, .icon = &I_ButtonF2_5x8, .value = HID_KEYBOARD_F2}, + {.width = 1, .icon = &I_ButtonF3_5x8, .value = HID_KEYBOARD_F3}, + {.width = 1, .icon = &I_ButtonF4_5x8, .value = HID_KEYBOARD_F4}, + {.width = 1, .icon = &I_ButtonF5_5x8, .value = HID_KEYBOARD_F5}, + {.width = 1, .icon = &I_ButtonF6_5x8, .value = HID_KEYBOARD_F6}, + {.width = 1, .icon = &I_ButtonF7_5x8, .value = HID_KEYBOARD_F7}, + {.width = 1, .icon = &I_ButtonF8_5x8, .value = HID_KEYBOARD_F8}, + {.width = 1, .icon = &I_ButtonF9_5x8, .value = HID_KEYBOARD_F9}, + {.width = 1, .icon = &I_ButtonF10_5x8, .value = HID_KEYBOARD_F10}, + {.width = 1, .icon = &I_ButtonF11_5x8, .value = HID_KEYBOARD_F11}, + {.width = 1, .icon = &I_ButtonF12_5x8, .value = HID_KEYBOARD_F12}, + }, + { + {.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_KEYBOARD_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 = &I_Ctrl_17x10, .value = HID_KEYBOARD_L_CTRL}, + {.width = 0, .value = HID_KEYBOARD_L_CTRL}, + {.width = 2, .icon = &I_Alt_17x10, .value = HID_KEYBOARD_L_ALT}, + {.width = 0, .value = HID_KEYBOARD_L_ALT}, + {.width = 2, .icon = &I_Cmd_17x10, .value = HID_KEYBOARD_L_GUI}, + {.width = 0, .value = HID_KEYBOARD_L_GUI}, + {.width = 2, .icon = &I_Tab_17x10, .value = HID_KEYBOARD_TAB}, + {.width = 0, .value = HID_KEYBOARD_TAB}, + {.width = 2, .icon = &I_Esc_17x10, .value = HID_KEYBOARD_ESCAPE}, + {.width = 0, .value = HID_KEYBOARD_ESCAPE}, + {.width = 2, .icon = &I_Del_17x10, .value = HID_KEYBOARD_DELETE_FORWARD}, + {.width = 0, .value = HID_KEYBOARD_DELETE_FORWARD}, + }, +}; + +static void hid_keyboard_to_upper(char* str) { + while(*str) { + *str = toupper((unsigned char)*str); + str++; + } +} + +static void hid_keyboard_draw_key( + Canvas* canvas, + HidKeyboardModel* model, + uint8_t x, + uint8_t y, + HidKeyboardKey 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)) { + 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 hid_keyboard_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidKeyboardModel* model = context; + + // Header + if((!model->connected) && (model->transport == HidTransportBle)) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keyboard"); + + canvas_draw_icon(canvas, 68, 3, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 127, 4, AlignRight, AlignTop, "Hold to exit"); + + elements_multiline_text_aligned( + canvas, 4, 60, AlignLeft, AlignBottom, "Waiting for Connection..."); + return; // Dont render the keyboard if we are not yet connected + } + + canvas_set_font(canvas, FontKeyboard); + // Start shifting the all keys up if on the next row (Scrolling) + uint8_t initY = model->y == 0 ? 0 : 1; + + if(model->y > 5) { + initY = model->y - 4; + } + + for(uint8_t y = initY; y < ROW_COUNT; y++) { + const HidKeyboardKey* keyboardKeyRow = hid_keyboard_keyset[y]; + uint8_t x = 0; + for(uint8_t i = 0; i < COLUMN_COUNT; i++) { + HidKeyboardKey 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; + bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE; + hid_keyboard_draw_key( + canvas, + model, + x, + y - initY, + key, + (!model->ok_pressed && keySelected) || backSelected); + x += key.width; + } + } +} + +static uint8_t hid_keyboard_get_selected_key(HidKeyboardModel* model) { + HidKeyboardKey key = hid_keyboard_keyset[model->y][model->x]; + return key.value; +} + +static void hid_keyboard_get_select_key(HidKeyboardModel* model, HidKeyboardPoint delta) { + // Keep going until a valid spot is found, this allows for nulls and zero width keys in the map + do { + const int delta_sum = model->y + delta.y; + model->y = delta_sum < 0 ? ROW_COUNT - 1 : delta_sum % ROW_COUNT; + } while(delta.y != 0 && hid_keyboard_keyset[model->y][model->x].value == 0); + + do { + const int delta_sum = model->x + delta.x; + model->x = delta_sum < 0 ? COLUMN_COUNT - 1 : delta_sum % COLUMN_COUNT; + } while(delta.x != 0 && hid_keyboard_keyset[model->y][model->x].width == + 0); // Skip zero width keys, pretend they are one key +} + +static void hid_keyboard_process(HidKeyboard* hid_keyboard, InputEvent* event) { + with_view_model( + hid_keyboard->view, + HidKeyboardModel * 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 = 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; + } + hid_hal_keyboard_press( + hid_keyboard->hid, model->modifier_code | model->last_key_code); + } else if(event->type == InputTypeRelease) { + // Release happens after short and long presses + hid_hal_keyboard_release( + hid_keyboard->hid, 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) { + hid_hal_keyboard_press(hid_keyboard->hid, HID_KEYBOARD_DELETE); + hid_hal_keyboard_release(hid_keyboard->hid, 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) { + hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 0, .y = -1}); + } else if(event->key == InputKeyDown) { + hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 0, .y = 1}); + } else if(event->key == InputKeyLeft) { + hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = -1, .y = 0}); + } else if(event->key == InputKeyRight) { + hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 1, .y = 0}); + } + } + }, + true); +} + +static bool hid_keyboard_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidKeyboard* hid_keyboard = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + hid_hal_keyboard_release_all(hid_keyboard->hid); + } else { + hid_keyboard_process(hid_keyboard, event); + consumed = true; + } + + return consumed; +} + +HidKeyboard* hid_keyboard_alloc(Hid* bt_hid) { + HidKeyboard* hid_keyboard = malloc(sizeof(HidKeyboard)); + hid_keyboard->view = view_alloc(); + hid_keyboard->hid = bt_hid; + view_set_context(hid_keyboard->view, hid_keyboard); + view_allocate_model(hid_keyboard->view, ViewModelTypeLocking, sizeof(HidKeyboardModel)); + view_set_draw_callback(hid_keyboard->view, hid_keyboard_draw_callback); + view_set_input_callback(hid_keyboard->view, hid_keyboard_input_callback); + + with_view_model( + hid_keyboard->view, + HidKeyboardModel * model, + { + model->transport = bt_hid->transport; + model->y = 1; + }, + true); + + return hid_keyboard; +} + +void hid_keyboard_free(HidKeyboard* hid_keyboard) { + furi_assert(hid_keyboard); + view_free(hid_keyboard->view); + free(hid_keyboard); +} + +View* hid_keyboard_get_view(HidKeyboard* hid_keyboard) { + furi_assert(hid_keyboard); + return hid_keyboard->view; +} + +void hid_keyboard_set_connected_status(HidKeyboard* hid_keyboard, bool connected) { + furi_assert(hid_keyboard); + with_view_model( + hid_keyboard->view, HidKeyboardModel * model, { model->connected = connected; }, true); +} diff --git a/applications/system/hid_app/views/hid_keyboard.h b/applications/system/hid_app/views/hid_keyboard.h new file mode 100644 index 00000000000..7127713643b --- /dev/null +++ b/applications/system/hid_app/views/hid_keyboard.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidKeyboard HidKeyboard; + +HidKeyboard* hid_keyboard_alloc(Hid* bt_hid); + +void hid_keyboard_free(HidKeyboard* hid_keyboard); + +View* hid_keyboard_get_view(HidKeyboard* hid_keyboard); + +void hid_keyboard_set_connected_status(HidKeyboard* hid_keyboard, bool connected); diff --git a/applications/system/hid_app/views/hid_keynote.c b/applications/system/hid_app/views/hid_keynote.c new file mode 100644 index 00000000000..543363bf67b --- /dev/null +++ b/applications/system/hid_app/views/hid_keynote.c @@ -0,0 +1,312 @@ +#include "hid_keynote.h" +#include +#include "../hid.h" + +#include "hid_icons.h" + +#define TAG "HidKeynote" + +struct HidKeynote { + View* view; + Hid* hid; +}; + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool ok_pressed; + bool back_pressed; + bool connected; + HidTransport transport; +} HidKeynoteModel; + +static void hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { + 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 hid_keynote_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidKeynoteModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keynote"); + + 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); + } + hid_keynote_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); + } + hid_keynote_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); + } + hid_keynote_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); + } + hid_keynote_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 hid_keynote_draw_vertical_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidKeynoteModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Keynote"); + } else { + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Keynote"); + } + + canvas_draw_icon(canvas, 2, 18, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, "Hold to exit"); + + const uint8_t x_2 = 23; + const uint8_t x_1 = 2; + const uint8_t x_3 = 44; + + const uint8_t y_1 = 44; + const uint8_t y_2 = 65; + + // Up + canvas_draw_icon(canvas, x_2, y_1, &I_Button_18x18); + if(model->up_pressed) { + elements_slightly_rounded_box(canvas, x_2 + 3, y_1 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_2 + 9, y_1 + 6, CanvasDirectionBottomToTop); + canvas_set_color(canvas, ColorBlack); + + // Down + canvas_draw_icon(canvas, x_2, y_2, &I_Button_18x18); + if(model->down_pressed) { + elements_slightly_rounded_box(canvas, x_2 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_2 + 9, y_2 + 10, CanvasDirectionTopToBottom); + canvas_set_color(canvas, ColorBlack); + + // Left + canvas_draw_icon(canvas, x_1, y_2, &I_Button_18x18); + if(model->left_pressed) { + elements_slightly_rounded_box(canvas, x_1 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_1 + 7, y_2 + 8, CanvasDirectionRightToLeft); + canvas_set_color(canvas, ColorBlack); + + // Right + canvas_draw_icon(canvas, x_3, y_2, &I_Button_18x18); + if(model->right_pressed) { + elements_slightly_rounded_box(canvas, x_3 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_3 + 11, y_2 + 8, CanvasDirectionLeftToRight); + canvas_set_color(canvas, ColorBlack); + + // Ok + canvas_draw_icon(canvas, 2, 86, &I_Space_60x18); + if(model->ok_pressed) { + elements_slightly_rounded_box(canvas, 5, 88, 55, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 11, 90, &I_Ok_btn_9x9); + elements_multiline_text_aligned(canvas, 26, 98, AlignLeft, AlignBottom, "Space"); + canvas_set_color(canvas, ColorBlack); + + // Back + canvas_draw_icon(canvas, 2, 107, &I_Space_60x18); + if(model->back_pressed) { + elements_slightly_rounded_box(canvas, 5, 109, 55, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 11, 111, &I_Pin_back_arrow_10x8); + elements_multiline_text_aligned(canvas, 26, 119, AlignLeft, AlignBottom, "Back"); +} + +static void hid_keynote_process(HidKeynote* hid_keynote, InputEvent* event) { + with_view_model( + hid_keynote->view, + HidKeynoteModel * model, + { + if(event->type == InputTypePress) { + if(event->key == InputKeyUp) { + model->up_pressed = true; + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_UP_ARROW); + } else if(event->key == InputKeyDown) { + model->down_pressed = true; + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_DOWN_ARROW); + } else if(event->key == InputKeyLeft) { + model->left_pressed = true; + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_LEFT_ARROW); + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_RIGHT_ARROW); + } else if(event->key == InputKeyOk) { + model->ok_pressed = true; + hid_hal_keyboard_press(hid_keynote->hid, 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; + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_UP_ARROW); + } else if(event->key == InputKeyDown) { + model->down_pressed = false; + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_DOWN_ARROW); + } else if(event->key == InputKeyLeft) { + model->left_pressed = false; + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_LEFT_ARROW); + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_RIGHT_ARROW); + } else if(event->key == InputKeyOk) { + model->ok_pressed = false; + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_SPACEBAR); + } else if(event->key == InputKeyBack) { + model->back_pressed = false; + } + } else if(event->type == InputTypeShort) { + if(event->key == InputKeyBack) { + hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_DELETE); + hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_DELETE); + hid_hal_consumer_key_press(hid_keynote->hid, HID_CONSUMER_AC_BACK); + hid_hal_consumer_key_release(hid_keynote->hid, HID_CONSUMER_AC_BACK); + } + } + }, + true); +} + +static bool hid_keynote_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidKeynote* hid_keynote = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + hid_hal_keyboard_release_all(hid_keynote->hid); + } else { + hid_keynote_process(hid_keynote, event); + consumed = true; + } + + return consumed; +} + +HidKeynote* hid_keynote_alloc(Hid* hid) { + HidKeynote* hid_keynote = malloc(sizeof(HidKeynote)); + hid_keynote->view = view_alloc(); + hid_keynote->hid = hid; + view_set_context(hid_keynote->view, hid_keynote); + view_allocate_model(hid_keynote->view, ViewModelTypeLocking, sizeof(HidKeynoteModel)); + view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback); + view_set_input_callback(hid_keynote->view, hid_keynote_input_callback); + + with_view_model( + hid_keynote->view, HidKeynoteModel * model, { model->transport = hid->transport; }, true); + + return hid_keynote; +} + +void hid_keynote_free(HidKeynote* hid_keynote) { + furi_assert(hid_keynote); + view_free(hid_keynote->view); + free(hid_keynote); +} + +View* hid_keynote_get_view(HidKeynote* hid_keynote) { + furi_assert(hid_keynote); + return hid_keynote->view; +} + +void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected) { + furi_assert(hid_keynote); + with_view_model( + hid_keynote->view, HidKeynoteModel * model, { model->connected = connected; }, true); +} + +void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical) { + furi_assert(hid_keynote); + + if(vertical) { + view_set_draw_callback(hid_keynote->view, hid_keynote_draw_vertical_callback); + view_set_orientation(hid_keynote->view, ViewOrientationVerticalFlip); + + } else { + view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback); + view_set_orientation(hid_keynote->view, ViewOrientationHorizontal); + } +} diff --git a/applications/system/hid_app/views/hid_keynote.h b/applications/system/hid_app/views/hid_keynote.h new file mode 100644 index 00000000000..84bfed4ce41 --- /dev/null +++ b/applications/system/hid_app/views/hid_keynote.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidKeynote HidKeynote; + +HidKeynote* hid_keynote_alloc(Hid* bt_hid); + +void hid_keynote_free(HidKeynote* hid_keynote); + +View* hid_keynote_get_view(HidKeynote* hid_keynote); + +void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected); + +void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical); diff --git a/applications/system/hid_app/views/hid_media.c b/applications/system/hid_app/views/hid_media.c new file mode 100644 index 00000000000..468529d56a8 --- /dev/null +++ b/applications/system/hid_app/views/hid_media.c @@ -0,0 +1,218 @@ +#include "hid_media.h" +#include +#include +#include +#include +#include "../hid.h" + +#include "hid_icons.h" + +#define TAG "HidMedia" + +struct HidMedia { + View* view; + Hid* hid; +}; + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool ok_pressed; + bool connected; + HidTransport transport; +} HidMediaModel; + +static void hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { + 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 hid_media_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidMediaModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Media"); + canvas_set_font(canvas, FontSecondary); + + // 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); + } + hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft); + 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); + } + hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight); + 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); + } + 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 hid_media_process_press(HidMedia* hid_media, InputEvent* event) { + with_view_model( + hid_media->view, + HidMediaModel * model, + { + if(event->key == InputKeyUp) { + model->up_pressed = true; + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyDown) { + model->down_pressed = true; + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyLeft) { + model->left_pressed = true; + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK); + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_SCAN_NEXT_TRACK); + } else if(event->key == InputKeyOk) { + model->ok_pressed = true; + hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_PLAY_PAUSE); + } + }, + true); +} + +static void hid_media_process_release(HidMedia* hid_media, InputEvent* event) { + with_view_model( + hid_media->view, + HidMediaModel * model, + { + if(event->key == InputKeyUp) { + model->up_pressed = false; + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyDown) { + model->down_pressed = false; + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyLeft) { + model->left_pressed = false; + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK); + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_SCAN_NEXT_TRACK); + } else if(event->key == InputKeyOk) { + model->ok_pressed = false; + hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_PLAY_PAUSE); + } + }, + true); +} + +static bool hid_media_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidMedia* hid_media = context; + bool consumed = false; + + if(event->type == InputTypePress) { + hid_media_process_press(hid_media, event); + consumed = true; + } else if(event->type == InputTypeRelease) { + hid_media_process_release(hid_media, event); + consumed = true; + } else if(event->type == InputTypeShort) { + if(event->key == InputKeyBack) { + hid_hal_consumer_key_release_all(hid_media->hid); + } + } + + return consumed; +} + +HidMedia* hid_media_alloc(Hid* hid) { + HidMedia* hid_media = malloc(sizeof(HidMedia)); + hid_media->view = view_alloc(); + hid_media->hid = hid; + view_set_context(hid_media->view, hid_media); + view_allocate_model(hid_media->view, ViewModelTypeLocking, sizeof(HidMediaModel)); + view_set_draw_callback(hid_media->view, hid_media_draw_callback); + view_set_input_callback(hid_media->view, hid_media_input_callback); + + with_view_model( + hid_media->view, HidMediaModel * model, { model->transport = hid->transport; }, true); + + return hid_media; +} + +void hid_media_free(HidMedia* hid_media) { + furi_assert(hid_media); + view_free(hid_media->view); + free(hid_media); +} + +View* hid_media_get_view(HidMedia* hid_media) { + furi_assert(hid_media); + return hid_media->view; +} + +void hid_media_set_connected_status(HidMedia* hid_media, bool connected) { + furi_assert(hid_media); + with_view_model( + hid_media->view, HidMediaModel * model, { model->connected = connected; }, true); +} diff --git a/applications/system/hid_app/views/hid_media.h b/applications/system/hid_app/views/hid_media.h new file mode 100644 index 00000000000..4aa51dc173b --- /dev/null +++ b/applications/system/hid_app/views/hid_media.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +typedef struct HidMedia HidMedia; + +HidMedia* hid_media_alloc(); + +void hid_media_free(HidMedia* hid_media); + +View* hid_media_get_view(HidMedia* hid_media); + +void hid_media_set_connected_status(HidMedia* hid_media, bool connected); diff --git a/applications/system/hid_app/views/hid_mouse.c b/applications/system/hid_app/views/hid_mouse.c new file mode 100644 index 00000000000..30a9d9d0635 --- /dev/null +++ b/applications/system/hid_app/views/hid_mouse.c @@ -0,0 +1,226 @@ +#include "hid_mouse.h" +#include +#include "../hid.h" + +#include "hid_icons.h" + +#define TAG "HidMouse" + +struct HidMouse { + View* view; + Hid* hid; +}; + +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; + HidTransport transport; +} HidMouseModel; + +static void hid_mouse_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidMouseModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse"); + canvas_set_font(canvas, FontSecondary); + + 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 hid_mouse_process(HidMouse* hid_mouse, InputEvent* event) { + with_view_model( + hid_mouse->view, + HidMouseModel * model, + { + if(event->key == InputKeyBack) { + if(event->type == InputTypeShort) { + hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_RIGHT); + hid_hal_mouse_release(hid_mouse->hid, 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) + hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_LEFT); + hid_hal_mouse_release(hid_mouse->hid, HID_MOUSE_BTN_LEFT); + model->left_mouse_held = false; + } else if(event->type == InputTypeLong) { + hid_hal_mouse_press(hid_mouse->hid, 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; + hid_hal_mouse_move(hid_mouse->hid, MOUSE_MOVE_SHORT, 0); + } else if(event->type == InputTypeRepeat) { + hid_hal_mouse_move(hid_mouse->hid, 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; + hid_hal_mouse_move(hid_mouse->hid, -MOUSE_MOVE_SHORT, 0); + } else if(event->type == InputTypeRepeat) { + hid_hal_mouse_move(hid_mouse->hid, -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; + hid_hal_mouse_move(hid_mouse->hid, 0, MOUSE_MOVE_SHORT); + } else if(event->type == InputTypeRepeat) { + hid_hal_mouse_move(hid_mouse->hid, 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; + hid_hal_mouse_move(hid_mouse->hid, 0, -MOUSE_MOVE_SHORT); + } else if(event->type == InputTypeRepeat) { + hid_hal_mouse_move(hid_mouse->hid, 0, -MOUSE_MOVE_LONG); + } else if(event->type == InputTypeRelease) { + model->up_pressed = false; + } + } + }, + true); +} + +static bool hid_mouse_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidMouse* hid_mouse = context; + bool consumed = false; + + if(event->type == InputTypeLong && event->key == InputKeyBack) { + hid_hal_mouse_release_all(hid_mouse->hid); + } else { + hid_mouse_process(hid_mouse, event); + consumed = true; + } + + return consumed; +} + +HidMouse* hid_mouse_alloc(Hid* hid) { + HidMouse* hid_mouse = malloc(sizeof(HidMouse)); + hid_mouse->view = view_alloc(); + hid_mouse->hid = hid; + view_set_context(hid_mouse->view, hid_mouse); + view_allocate_model(hid_mouse->view, ViewModelTypeLocking, sizeof(HidMouseModel)); + view_set_draw_callback(hid_mouse->view, hid_mouse_draw_callback); + view_set_input_callback(hid_mouse->view, hid_mouse_input_callback); + + with_view_model( + hid_mouse->view, HidMouseModel * model, { model->transport = hid->transport; }, true); + + return hid_mouse; +} + +void hid_mouse_free(HidMouse* hid_mouse) { + furi_assert(hid_mouse); + view_free(hid_mouse->view); + free(hid_mouse); +} + +View* hid_mouse_get_view(HidMouse* hid_mouse) { + furi_assert(hid_mouse); + return hid_mouse->view; +} + +void hid_mouse_set_connected_status(HidMouse* hid_mouse, bool connected) { + furi_assert(hid_mouse); + with_view_model( + hid_mouse->view, HidMouseModel * model, { model->connected = connected; }, true); +} diff --git a/applications/system/hid_app/views/hid_mouse.h b/applications/system/hid_app/views/hid_mouse.h new file mode 100644 index 00000000000..d9fb2fd88a7 --- /dev/null +++ b/applications/system/hid_app/views/hid_mouse.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#define MOUSE_MOVE_SHORT 5 +#define MOUSE_MOVE_LONG 20 + +typedef struct Hid Hid; +typedef struct HidMouse HidMouse; + +HidMouse* hid_mouse_alloc(Hid* bt_hid); + +void hid_mouse_free(HidMouse* hid_mouse); + +View* hid_mouse_get_view(HidMouse* hid_mouse); + +void hid_mouse_set_connected_status(HidMouse* hid_mouse, bool connected); diff --git a/applications/system/hid_app/views/hid_mouse_clicker.c b/applications/system/hid_app/views/hid_mouse_clicker.c new file mode 100644 index 00000000000..d85affc4338 --- /dev/null +++ b/applications/system/hid_app/views/hid_mouse_clicker.c @@ -0,0 +1,214 @@ +#include "hid_mouse_clicker.h" +#include +#include "../hid.h" + +#include "hid_icons.h" + +#define TAG "HidMouseClicker" +#define DEFAULT_CLICK_RATE 1 +#define MAXIMUM_CLICK_RATE 60 + +struct HidMouseClicker { + View* view; + Hid* hid; + FuriTimer* timer; +}; + +typedef struct { + bool connected; + bool running; + int rate; + HidTransport transport; +} HidMouseClickerModel; + +static void hid_mouse_clicker_start_or_restart_timer(void* context) { + furi_assert(context); + HidMouseClicker* hid_mouse_clicker = context; + + if(furi_timer_is_running(hid_mouse_clicker->timer)) { + furi_timer_stop(hid_mouse_clicker->timer); + } + + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { + furi_timer_start( + hid_mouse_clicker->timer, furi_kernel_get_tick_frequency() / model->rate); + }, + true); +} + +static void hid_mouse_clicker_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidMouseClickerModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Clicker"); + + // Ok + canvas_draw_icon(canvas, 63, 25, &I_Space_65x18); + if(model->running) { + canvas_set_font(canvas, FontPrimary); + + FuriString* rate_label = furi_string_alloc(); + furi_string_printf(rate_label, "%d clicks/s\n\nUp / Down", model->rate); + elements_multiline_text(canvas, AlignLeft, 35, furi_string_get_cstr(rate_label)); + canvas_set_font(canvas, FontSecondary); + furi_string_free(rate_label); + + elements_slightly_rounded_box(canvas, 66, 27, 60, 13); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_set_font(canvas, FontPrimary); + elements_multiline_text(canvas, AlignLeft, 35, "Press Start\nto start\nclicking"); + canvas_set_font(canvas, FontSecondary); + } + 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_clicker_timer_callback(void* context) { + furi_assert(context); + HidMouseClicker* hid_mouse_clicker = context; + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { + if(model->running) { + hid_hal_mouse_press(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT); + hid_hal_mouse_release(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT); + } + }, + false); +} + +static void hid_mouse_clicker_enter_callback(void* context) { + hid_mouse_clicker_start_or_restart_timer(context); +} + +static void hid_mouse_clicker_exit_callback(void* context) { + furi_assert(context); + HidMouseClicker* hid_mouse_clicker = context; + furi_timer_stop(hid_mouse_clicker->timer); +} + +static bool hid_mouse_clicker_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidMouseClicker* hid_mouse_clicker = context; + + bool consumed = false; + bool rate_changed = false; + + if(event->type != InputTypeShort && event->type != InputTypeRepeat) { + return false; + } + + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { + switch(event->key) { + case InputKeyOk: + model->running = !model->running; + consumed = true; + break; + case InputKeyUp: + if(model->rate < MAXIMUM_CLICK_RATE) { + model->rate++; + } + rate_changed = true; + consumed = true; + break; + case InputKeyDown: + if(model->rate > 1) { + model->rate--; + } + rate_changed = true; + consumed = true; + break; + default: + consumed = true; + break; + } + }, + true); + + if(rate_changed) { + hid_mouse_clicker_start_or_restart_timer(context); + } + + return consumed; +} + +HidMouseClicker* hid_mouse_clicker_alloc(Hid* hid) { + HidMouseClicker* hid_mouse_clicker = malloc(sizeof(HidMouseClicker)); + + hid_mouse_clicker->view = view_alloc(); + view_set_context(hid_mouse_clicker->view, hid_mouse_clicker); + view_allocate_model( + hid_mouse_clicker->view, ViewModelTypeLocking, sizeof(HidMouseClickerModel)); + view_set_draw_callback(hid_mouse_clicker->view, hid_mouse_clicker_draw_callback); + view_set_input_callback(hid_mouse_clicker->view, hid_mouse_clicker_input_callback); + view_set_enter_callback(hid_mouse_clicker->view, hid_mouse_clicker_enter_callback); + view_set_exit_callback(hid_mouse_clicker->view, hid_mouse_clicker_exit_callback); + + hid_mouse_clicker->hid = hid; + + hid_mouse_clicker->timer = furi_timer_alloc( + hid_mouse_clicker_timer_callback, FuriTimerTypePeriodic, hid_mouse_clicker); + + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { + model->transport = hid->transport; + model->rate = DEFAULT_CLICK_RATE; + }, + true); + + return hid_mouse_clicker; +} + +void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker) { + furi_assert(hid_mouse_clicker); + + furi_timer_stop(hid_mouse_clicker->timer); + furi_timer_free(hid_mouse_clicker->timer); + + view_free(hid_mouse_clicker->view); + + free(hid_mouse_clicker); +} + +View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker) { + furi_assert(hid_mouse_clicker); + return hid_mouse_clicker->view; +} + +void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected) { + furi_assert(hid_mouse_clicker); + with_view_model( + hid_mouse_clicker->view, + HidMouseClickerModel * model, + { model->connected = connected; }, + true); +} diff --git a/applications/system/hid_app/views/hid_mouse_clicker.h b/applications/system/hid_app/views/hid_mouse_clicker.h new file mode 100644 index 00000000000..d72847baa70 --- /dev/null +++ b/applications/system/hid_app/views/hid_mouse_clicker.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidMouseClicker HidMouseClicker; + +HidMouseClicker* hid_mouse_clicker_alloc(Hid* bt_hid); + +void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker); + +View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker); + +void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected); diff --git a/applications/system/hid_app/views/hid_mouse_jiggler.c b/applications/system/hid_app/views/hid_mouse_jiggler.c new file mode 100644 index 00000000000..15547eb26b5 --- /dev/null +++ b/applications/system/hid_app/views/hid_mouse_jiggler.c @@ -0,0 +1,159 @@ +#include "hid_mouse_jiggler.h" +#include +#include "../hid.h" + +#include "hid_icons.h" + +#define TAG "HidMouseJiggler" + +struct HidMouseJiggler { + View* view; + Hid* hid; + FuriTimer* timer; +}; + +typedef struct { + bool connected; + bool running; + uint8_t counter; + HidTransport transport; +} HidMouseJigglerModel; + +static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidMouseJigglerModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Jiggler"); + + 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++; + hid_hal_mouse_move( + hid_mouse_jiggler->hid, + (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->type == InputTypeShort && 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(Hid* hid) { + 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->hid = hid; + + hid_mouse_jiggler->timer = furi_timer_alloc( + hid_mouse_jiggler_timer_callback, FuriTimerTypePeriodic, hid_mouse_jiggler); + + with_view_model( + hid_mouse_jiggler->view, + HidMouseJigglerModel * model, + { model->transport = hid->transport; }, + true); + + return hid_mouse_jiggler; +} + +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; +} + +void hid_mouse_jiggler_set_connected_status(HidMouseJiggler* hid_mouse_jiggler, bool connected) { + furi_assert(hid_mouse_jiggler); + with_view_model( + hid_mouse_jiggler->view, + HidMouseJigglerModel * model, + { model->connected = connected; }, + true); +} diff --git a/applications/system/hid_app/views/hid_mouse_jiggler.h b/applications/system/hid_app/views/hid_mouse_jiggler.h new file mode 100644 index 00000000000..0813b4351e1 --- /dev/null +++ b/applications/system/hid_app/views/hid_mouse_jiggler.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#define MOUSE_MOVE_SHORT 5 +#define MOUSE_MOVE_LONG 20 + +typedef struct Hid Hid; +typedef struct HidMouseJiggler HidMouseJiggler; + +HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* bt_hid); + +void hid_mouse_jiggler_free(HidMouseJiggler* hid_mouse_jiggler); + +View* hid_mouse_jiggler_get_view(HidMouseJiggler* hid_mouse_jiggler); + +void hid_mouse_jiggler_set_connected_status(HidMouseJiggler* hid_mouse_jiggler, bool connected); diff --git a/applications/system/hid_app/views/hid_tiktok.c b/applications/system/hid_app/views/hid_tiktok.c new file mode 100644 index 00000000000..e1f9f4bed4c --- /dev/null +++ b/applications/system/hid_app/views/hid_tiktok.c @@ -0,0 +1,241 @@ +#include "hid_tiktok.h" +#include "../hid.h" +#include + +#include "hid_icons.h" + +#define TAG "HidTikTok" + +struct HidTikTok { + View* view; + Hid* hid; +}; + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool ok_pressed; + bool connected; + bool is_cursor_set; + HidTransport transport; +} HidTikTokModel; + +static void hid_tiktok_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidTikTokModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "TikTok"); + canvas_set_font(canvas, FontSecondary); + + // 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, 11, &I_Arr_up_7x9); + 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, 44, &I_Arr_dwn_7x9); + 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); + } + canvas_draw_icon(canvas, 81, 29, &I_Voldwn_6x6); + 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); + } + canvas_draw_icon(canvas, 111, 29, &I_Volup_8x6); + canvas_set_color(canvas, ColorBlack); + + // Ok + if(model->ok_pressed) { + canvas_draw_icon(canvas, 91, 23, &I_Like_pressed_17x17); + } else { + canvas_draw_icon(canvas, 94, 27, &I_Like_def_11x9); + } + // 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 hid_tiktok_reset_cursor(HidTikTok* hid_tiktok) { + // Set cursor to the phone's left up corner + // Delays to guarantee one packet per connection interval + for(size_t i = 0; i < 8; i++) { + hid_hal_mouse_move(hid_tiktok->hid, -127, -127); + furi_delay_ms(50); + } + // Move cursor from the corner + hid_hal_mouse_move(hid_tiktok->hid, 20, 120); + furi_delay_ms(50); +} + +static void + hid_tiktok_process_press(HidTikTok* hid_tiktok, HidTikTokModel* model, InputEvent* event) { + if(event->key == InputKeyUp) { + model->up_pressed = true; + } else if(event->key == InputKeyDown) { + model->down_pressed = true; + } else if(event->key == InputKeyLeft) { + model->left_pressed = true; + hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyOk) { + model->ok_pressed = true; + } +} + +static void + hid_tiktok_process_release(HidTikTok* hid_tiktok, HidTikTokModel* model, InputEvent* event) { + if(event->key == InputKeyUp) { + model->up_pressed = false; + } else if(event->key == InputKeyDown) { + model->down_pressed = false; + } else if(event->key == InputKeyLeft) { + model->left_pressed = false; + hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyOk) { + model->ok_pressed = false; + } +} + +static bool hid_tiktok_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidTikTok* hid_tiktok = context; + bool consumed = false; + + with_view_model( + hid_tiktok->view, + HidTikTokModel * model, + { + if(event->type == InputTypePress) { + hid_tiktok_process_press(hid_tiktok, model, event); + if(model->connected && !model->is_cursor_set) { + hid_tiktok_reset_cursor(hid_tiktok); + model->is_cursor_set = true; + } + consumed = true; + } else if(event->type == InputTypeRelease) { + hid_tiktok_process_release(hid_tiktok, model, event); + consumed = true; + } else if(event->type == InputTypeShort) { + if(event->key == InputKeyOk) { + hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); + furi_delay_ms(50); + hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); + consumed = true; + } else if(event->key == InputKeyUp) { + // Emulate up swipe + hid_hal_mouse_scroll(hid_tiktok->hid, -6); + hid_hal_mouse_scroll(hid_tiktok->hid, -12); + hid_hal_mouse_scroll(hid_tiktok->hid, -19); + hid_hal_mouse_scroll(hid_tiktok->hid, -12); + hid_hal_mouse_scroll(hid_tiktok->hid, -6); + consumed = true; + } else if(event->key == InputKeyDown) { + // Emulate down swipe + hid_hal_mouse_scroll(hid_tiktok->hid, 6); + hid_hal_mouse_scroll(hid_tiktok->hid, 12); + hid_hal_mouse_scroll(hid_tiktok->hid, 19); + hid_hal_mouse_scroll(hid_tiktok->hid, 12); + hid_hal_mouse_scroll(hid_tiktok->hid, 6); + consumed = true; + } else if(event->key == InputKeyBack) { + hid_hal_consumer_key_release_all(hid_tiktok->hid); + consumed = true; + } + } else if(event->type == InputTypeLong) { + if(event->key == InputKeyBack) { + hid_hal_consumer_key_release_all(hid_tiktok->hid); + model->is_cursor_set = false; + consumed = false; + } + } + }, + true); + + return consumed; +} + +HidTikTok* hid_tiktok_alloc(Hid* bt_hid) { + HidTikTok* hid_tiktok = malloc(sizeof(HidTikTok)); + hid_tiktok->hid = bt_hid; + hid_tiktok->view = view_alloc(); + view_set_context(hid_tiktok->view, hid_tiktok); + view_allocate_model(hid_tiktok->view, ViewModelTypeLocking, sizeof(HidTikTokModel)); + view_set_draw_callback(hid_tiktok->view, hid_tiktok_draw_callback); + view_set_input_callback(hid_tiktok->view, hid_tiktok_input_callback); + + with_view_model( + hid_tiktok->view, HidTikTokModel * model, { model->transport = bt_hid->transport; }, true); + + return hid_tiktok; +} + +void hid_tiktok_free(HidTikTok* hid_tiktok) { + furi_assert(hid_tiktok); + view_free(hid_tiktok->view); + free(hid_tiktok); +} + +View* hid_tiktok_get_view(HidTikTok* hid_tiktok) { + furi_assert(hid_tiktok); + return hid_tiktok->view; +} + +void hid_tiktok_set_connected_status(HidTikTok* hid_tiktok, bool connected) { + furi_assert(hid_tiktok); + with_view_model( + hid_tiktok->view, + HidTikTokModel * model, + { + model->connected = connected; + model->is_cursor_set = false; + }, + true); +} diff --git a/applications/system/hid_app/views/hid_tiktok.h b/applications/system/hid_app/views/hid_tiktok.h new file mode 100644 index 00000000000..b2efc3692d3 --- /dev/null +++ b/applications/system/hid_app/views/hid_tiktok.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidTikTok HidTikTok; + +HidTikTok* hid_tiktok_alloc(Hid* bt_hid); + +void hid_tiktok_free(HidTikTok* hid_tiktok); + +View* hid_tiktok_get_view(HidTikTok* hid_tiktok); + +void hid_tiktok_set_connected_status(HidTikTok* hid_tiktok, bool connected); diff --git a/applications/system/snake_game/application.fam b/applications/system/snake_game/application.fam new file mode 100644 index 00000000000..9e803f65db5 --- /dev/null +++ b/applications/system/snake_game/application.fam @@ -0,0 +1,12 @@ +App( + appid="snake_game", + name="Snake Game", + apptype=FlipperAppType.EXTERNAL, + entry_point="snake_game_app", + requires=["gui"], + stack_size=1 * 1024, + fap_version="1.0", + fap_description="Classic Snake Game", + fap_icon="snake_10px.png", + fap_category="Games", +) diff --git a/applications/system/snake_game/snake_10px.png b/applications/system/snake_game/snake_10px.png new file mode 100644 index 00000000000..52d9fa7e0e1 Binary files /dev/null and b/applications/system/snake_game/snake_10px.png differ diff --git a/applications/snake_game/snake_game.c b/applications/system/snake_game/snake_game.c similarity index 86% rename from applications/snake_game/snake_game.c rename to applications/system/snake_game/snake_game.c index b7aabb17cba..bd7f1ce1647 100644 --- a/applications/snake_game/snake_game.c +++ b/applications/system/snake_game/snake_game.c @@ -2,6 +2,9 @@ #include #include #include +#include +#include +#include typedef struct { // +-----x @@ -47,6 +50,7 @@ typedef struct { Direction nextMovement; // if backward of currentMovement, ignore Point fruit; GameState state; + FuriMutex* mutex; } SnakeState; typedef enum { @@ -59,13 +63,40 @@ typedef struct { InputEvent input; } SnakeEvent; +const NotificationSequence sequence_fail = { + &message_vibro_on, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_vibro_off, + NULL, +}; + +const NotificationSequence sequence_eat = { + &message_note_c7, + &message_delay_50, + &message_sound_off, + NULL, +}; + 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); @@ -98,11 +129,11 @@ static void snake_game_render_callback(Canvas* const canvas, void* ctx) { canvas_set_font(canvas, FontSecondary); char buffer[12]; - snprintf(buffer, sizeof(buffer), "Score: %u", snake_state->len - 7); + snprintf(buffer, sizeof(buffer), "Score: %u", snake_state->len - 7U); canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignBottom, buffer); } - release_mutex((ValueMutex*)ctx, snake_state); + furi_mutex_release(snake_state->mutex); } static void snake_game_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { @@ -121,7 +152,7 @@ static void snake_game_update_timer_callback(FuriMessageQueue* event_queue) { static void snake_game_init_game(SnakeState* const snake_state) { Point p[] = {{8, 6}, {7, 6}, {6, 6}, {5, 6}, {4, 6}, {3, 6}, {2, 6}}; - memcpy(snake_state->points, p, sizeof(p)); + memcpy(snake_state->points, p, sizeof(p)); //-V1086 snake_state->len = 7; @@ -230,7 +261,8 @@ static void snake_game_move_snake(SnakeState* const snake_state, Point const nex snake_state->points[0] = next_step; } -static void snake_game_process_game_step(SnakeState* const snake_state) { +static void + snake_game_process_game_step(SnakeState* const snake_state, NotificationApp* notification) { if(snake_state->state == GameStateGameOver) { return; } @@ -249,6 +281,7 @@ static void snake_game_process_game_step(SnakeState* const snake_state) { return; } else if(snake_state->state == GameStateLastChance) { snake_state->state = GameStateGameOver; + notification_message_block(notification, &sequence_fail); return; } } else { @@ -260,6 +293,7 @@ static void snake_game_process_game_step(SnakeState* const snake_state) { crush = snake_game_collision_with_tail(snake_state, next_step); if(crush) { snake_state->state = GameStateGameOver; + notification_message_block(notification, &sequence_fail); return; } @@ -268,6 +302,7 @@ static void snake_game_process_game_step(SnakeState* const snake_state) { snake_state->len++; if(snake_state->len >= MAX_SNAKE_LEN) { snake_state->state = GameStateGameOver; + notification_message_block(notification, &sequence_fail); return; } } @@ -276,27 +311,28 @@ static void snake_game_process_game_step(SnakeState* const snake_state) { if(eatFruit) { snake_state->fruit = snake_game_get_new_fruit(snake_state); + notification_message(notification, &sequence_eat); } } int32_t snake_game_app(void* p) { UNUSED(p); - srand(DWT->CYCCNT); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SnakeEvent)); 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"); 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 = @@ -306,12 +342,17 @@ int32_t snake_game_app(void* p) { // Open GUI and register view_port Gui* gui = furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + + 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 @@ -338,26 +379,32 @@ int32_t snake_game_app(void* p) { case InputKeyBack: processing = false; break; + default: + break; } } } else if(event.type == EventTypeTick) { - snake_game_process_game_step(snake_state); + snake_game_process_game_step(snake_state, notification); } } else { // event timeout } + furi_mutex_release(snake_state->mutex); view_port_update(view_port); - release_mutex(&state_mutex, snake_state); } + // Return backlight to normal state + notification_message(notification, &sequence_display_backlight_enforce_auto); + furi_timer_free(timer); view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); furi_record_close(RECORD_GUI); + 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; diff --git a/applications/storage_move_to_sd/application.fam b/applications/system/storage_move_to_sd/application.fam similarity index 100% rename from applications/storage_move_to_sd/application.fam rename to applications/system/storage_move_to_sd/application.fam diff --git a/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene.c b/applications/system/storage_move_to_sd/scenes/storage_move_to_sd_scene.c similarity index 100% rename from applications/storage_move_to_sd/scenes/storage_move_to_sd_scene.c rename to applications/system/storage_move_to_sd/scenes/storage_move_to_sd_scene.c diff --git a/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene.h b/applications/system/storage_move_to_sd/scenes/storage_move_to_sd_scene.h similarity index 100% rename from applications/storage_move_to_sd/scenes/storage_move_to_sd_scene.h rename to applications/system/storage_move_to_sd/scenes/storage_move_to_sd_scene.h diff --git a/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene_config.h b/applications/system/storage_move_to_sd/scenes/storage_move_to_sd_scene_config.h similarity index 100% rename from applications/storage_move_to_sd/scenes/storage_move_to_sd_scene_config.h rename to applications/system/storage_move_to_sd/scenes/storage_move_to_sd_scene_config.h diff --git a/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene_confirm.c b/applications/system/storage_move_to_sd/scenes/storage_move_to_sd_scene_confirm.c similarity index 94% rename from applications/storage_move_to_sd/scenes/storage_move_to_sd_scene_confirm.c rename to applications/system/storage_move_to_sd/scenes/storage_move_to_sd_scene_confirm.c index 71ce5a7c3fc..08c9e2d7fc2 100644 --- a/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene_confirm.c +++ b/applications/system/storage_move_to_sd/scenes/storage_move_to_sd_scene_confirm.c @@ -1,7 +1,7 @@ #include "../storage_move_to_sd.h" -#include "gui/canvas.h" -#include "gui/modules/widget_elements/widget_element_i.h" -#include "storage/storage.h" +#include +#include +#include static void storage_move_to_sd_scene_confirm_widget_callback( GuiButtonType result, diff --git a/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene_progress.c b/applications/system/storage_move_to_sd/scenes/storage_move_to_sd_scene_progress.c similarity index 100% rename from applications/storage_move_to_sd/scenes/storage_move_to_sd_scene_progress.c rename to applications/system/storage_move_to_sd/scenes/storage_move_to_sd_scene_progress.c diff --git a/applications/storage_move_to_sd/storage_move_to_sd.c b/applications/system/storage_move_to_sd/storage_move_to_sd.c similarity index 88% rename from applications/storage_move_to_sd/storage_move_to_sd.c rename to applications/system/storage_move_to_sd/storage_move_to_sd.c index e5b195d55d8..5d1e694bca4 100644 --- a/applications/storage_move_to_sd/storage_move_to_sd.c +++ b/applications/system/storage_move_to_sd/storage_move_to_sd.c @@ -1,9 +1,8 @@ #include "storage_move_to_sd.h" + #include #include -#include "loader/loader.h" -#include "m-string.h" -#include +#include #include #include @@ -14,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; } @@ -28,25 +27,26 @@ bool storage_move_to_sd_perform(void) { dir_walk_set_recursive(dir_walk, false); dir_walk_set_filter_cb(dir_walk, storage_move_to_sd_check_entry, NULL); - string_t path_src, path_dst; + FuriString *path_src, *path_dst; - string_init(path_dst); - string_init(path_src); + path_dst = furi_string_alloc(); + path_src = furi_string_alloc(); if(dir_walk_open(dir_walk, STORAGE_INT_PATH_PREFIX)) { while(dir_walk_read(dir_walk, path_src, NULL) == DirWalkOK) { - string_set(path_dst, path_src); - string_replace_at( + furi_string_set(path_dst, path_src); + furi_string_replace_at( path_dst, 0, strlen(STORAGE_INT_PATH_PREFIX), STORAGE_EXT_PATH_PREFIX); - storage_common_merge(storage, string_get_cstr(path_src), string_get_cstr(path_dst)); - storage_simply_remove_recursive(storage, string_get_cstr(path_src)); + storage_common_merge( + storage, furi_string_get_cstr(path_src), furi_string_get_cstr(path_dst)); + storage_simply_remove_recursive(storage, furi_string_get_cstr(path_src)); } } dir_walk_free(dir_walk); - string_clear(path_dst); - string_clear(path_src); + furi_string_free(path_dst); + furi_string_free(path_src); furi_record_close(RECORD_STORAGE); @@ -62,8 +62,8 @@ static bool storage_move_to_sd_check(void) { dir_walk_set_recursive(dir_walk, false); dir_walk_set_filter_cb(dir_walk, storage_move_to_sd_check_entry, NULL); - string_t name; - string_init(name); + FuriString* name; + name = furi_string_alloc(); if(dir_walk_open(dir_walk, STORAGE_INT_PATH_PREFIX)) { // if at least 1 entry is present, we should migrate @@ -71,7 +71,7 @@ static bool storage_move_to_sd_check(void) { } dir_walk_free(dir_walk); - string_clear(name); + furi_string_free(name); furi_record_close(RECORD_STORAGE); @@ -172,7 +172,7 @@ static void storage_move_to_sd_mount_callback(const void* message, void* context if(storage_event->type == StorageEventTypeCardMount) { Loader* loader = furi_record_open(RECORD_LOADER); - loader_start(loader, "StorageMoveToSd", NULL); + loader_start(loader, "StorageMoveToSd", NULL, NULL); furi_record_close(RECORD_LOADER); } } diff --git a/applications/storage_move_to_sd/storage_move_to_sd.h b/applications/system/storage_move_to_sd/storage_move_to_sd.h similarity index 91% rename from applications/storage_move_to_sd/storage_move_to_sd.h rename to applications/system/storage_move_to_sd/storage_move_to_sd.h index dc1d669b519..135f3e9b0be 100644 --- a/applications/storage_move_to_sd/storage_move_to_sd.h +++ b/applications/system/storage_move_to_sd/storage_move_to_sd.h @@ -1,17 +1,16 @@ #pragma once -#include "gui/modules/widget_elements/widget_element_i.h" -#include #include #include #include #include -#include - #include #include +#include +#include #include #include +#include #include "scenes/storage_move_to_sd_scene.h" @@ -30,7 +29,7 @@ typedef struct { Widget* widget; NotificationApp* notifications; - // view managment + // view management SceneManager* scene_manager; ViewDispatcher* view_dispatcher; diff --git a/applications/system/system_settings.c b/applications/system/system_settings.c deleted file mode 100644 index 7661413d714..00000000000 --- a/applications/system/system_settings.c +++ /dev/null @@ -1,112 +0,0 @@ -#include "system_settings.h" -#include -#include - -const char* const log_level_text[] = { - "Default", - "None", - "Error", - "Warning", - "Info", - "Debug", - "Trace", -}; - -const uint32_t log_level_value[] = { - FuriLogLevelDefault, - FuriLogLevelNone, - FuriLogLevelError, - FuriLogLevelWarn, - FuriLogLevelInfo, - FuriLogLevelDebug, - FuriLogLevelTrace, -}; - -static void log_level_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, log_level_text[index]); - furi_hal_rtc_set_log_level(log_level_value[index]); -} - -const char* const debug_text[] = { - "OFF", - "ON", -}; - -static void debug_changed(VariableItem* item) { - uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, debug_text[index]); - if(index) { - furi_hal_rtc_set_flag(FuriHalRtcFlagDebug); - } else { - furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); - } - loader_update_menu(); -} - -static uint32_t system_settings_exit(void* context) { - UNUSED(context); - return VIEW_NONE; -} - -SystemSettings* system_settings_alloc() { - SystemSettings* app = malloc(sizeof(SystemSettings)); - - // Load settings - app->gui = furi_record_open(RECORD_GUI); - - app->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_enable_queue(app->view_dispatcher); - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - VariableItem* item; - uint8_t value_index; - app->var_item_list = variable_item_list_alloc(); - - item = variable_item_list_add( - app->var_item_list, "Log Level", COUNT_OF(log_level_text), log_level_changed, app); - value_index = value_index_uint32( - furi_hal_rtc_get_log_level(), log_level_value, COUNT_OF(log_level_text)); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, log_level_text[value_index]); - - item = variable_item_list_add( - app->var_item_list, "Debug", COUNT_OF(debug_text), debug_changed, app); - value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) ? 1 : 0; - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, debug_text[value_index]); - - view_set_previous_callback( - variable_item_list_get_view(app->var_item_list), system_settings_exit); - view_dispatcher_add_view( - app->view_dispatcher, - SystemSettingsViewVarItemList, - variable_item_list_get_view(app->var_item_list)); - - view_dispatcher_switch_to_view(app->view_dispatcher, SystemSettingsViewVarItemList); - - return app; -} - -void system_settings_free(SystemSettings* app) { - furi_assert(app); - // Variable item list - view_dispatcher_remove_view(app->view_dispatcher, SystemSettingsViewVarItemList); - variable_item_list_free(app->var_item_list); - // View dispatcher - view_dispatcher_free(app->view_dispatcher); - // Records - furi_record_close(RECORD_GUI); - free(app); -} - -int32_t system_settings_app(void* p) { - UNUSED(p); - SystemSettings* app = system_settings_alloc(); - view_dispatcher_run(app->view_dispatcher); - system_settings_free(app); - return 0; -} diff --git a/applications/updater/application.fam b/applications/system/updater/application.fam similarity index 100% rename from applications/updater/application.fam rename to applications/system/updater/application.fam diff --git a/applications/system/updater/cli/updater_cli.c b/applications/system/updater/cli/updater_cli.c new file mode 100644 index 00000000000..cebdc4d7cab --- /dev/null +++ b/applications/system/updater/cli/updater_cli.c @@ -0,0 +1,119 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef void (*cmd_handler)(FuriString* args); +typedef struct { + const char* command; + const cmd_handler handler; +} CliSubcommand; + +static void updater_cli_install(FuriString* manifest_path) { + printf("Verifying update package at '%s'\r\n", furi_string_get_cstr(manifest_path)); + + UpdatePrepareResult result = update_operation_prepare(furi_string_get_cstr(manifest_path)); + if(result != UpdatePrepareResultOK) { + printf( + "Error: %s. Stopping update.\r\n", + update_operation_describe_preparation_result(result)); + return; + } + printf("OK.\r\nRestarting to apply update. BRB\r\n"); + furi_delay_ms(100); + furi_hal_power_reset(); +} + +static void updater_cli_backup(FuriString* args) { + printf("Backup /int to '%s'\r\n", furi_string_get_cstr(args)); + Storage* storage = furi_record_open(RECORD_STORAGE); + bool success = lfs_backup_create(storage, furi_string_get_cstr(args)); + furi_record_close(RECORD_STORAGE); + printf("Result: %s\r\n", success ? "OK" : "FAIL"); +} + +static void updater_cli_restore(FuriString* args) { + printf("Restore /int from '%s'\r\n", furi_string_get_cstr(args)); + Storage* storage = furi_record_open(RECORD_STORAGE); + bool success = lfs_backup_unpack(storage, furi_string_get_cstr(args)); + furi_record_close(RECORD_STORAGE); + printf("Result: %s\r\n", success ? "OK" : "FAIL"); +} + +static void updater_cli_help(FuriString* args) { + UNUSED(args); + printf("Commands:\r\n" + "\tinstall /ext/path/to/update.fuf - verify & apply update package\r\n" + "\tbackup /ext/path/to/backup.tar - create internal storage backup\r\n" + "\trestore /ext/path/to/backup.tar - restore internal storage backup\r\n"); +} + +static const CliSubcommand update_cli_subcommands[] = { + {.command = "install", .handler = updater_cli_install}, + {.command = "backup", .handler = updater_cli_backup}, + {.command = "restore", .handler = updater_cli_restore}, + {.command = "help", .handler = updater_cli_help}, +}; + +static void updater_cli_ep(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + UNUSED(context); + FuriString* subcommand; + subcommand = furi_string_alloc(); + if(!args_read_string_and_trim(args, subcommand) || furi_string_empty(args)) { + updater_cli_help(args); + furi_string_free(subcommand); + return; + } + for(size_t idx = 0; idx < COUNT_OF(update_cli_subcommands); ++idx) { + const CliSubcommand* subcmd_def = &update_cli_subcommands[idx]; + if(furi_string_cmp_str(subcommand, subcmd_def->command) == 0) { + subcmd_def->handler(args); + furi_string_free(subcommand); + return; + } + } + furi_string_free(subcommand); + updater_cli_help(args); +} + +static void updater_start_app(void* context, uint32_t arg) { + UNUSED(context); + UNUSED(arg); + + FuriHalRtcBootMode mode = furi_hal_rtc_get_boot_mode(); + if((mode != FuriHalRtcBootModePreUpdate) && (mode != FuriHalRtcBootModePostUpdate)) { + return; + } + + /* We need to spawn a separate thread, because these callbacks are executed + * inside loader process, at startup. + * So, accessing its record would cause a deadlock + */ + Loader* loader = furi_record_open(RECORD_LOADER); + loader_start(loader, "UpdaterApp", NULL, NULL); + furi_record_close(RECORD_LOADER); +} + +void updater_on_system_start() { +#ifdef SRV_CLI + Cli* cli = (Cli*)furi_record_open(RECORD_CLI); + cli_add_command(cli, "update", CliCommandFlagDefault, updater_cli_ep, NULL); + furi_record_close(RECORD_CLI); +#else + UNUSED(updater_cli_ep); +#endif +#ifndef FURI_RAM_EXEC + furi_timer_pending_callback(updater_start_app, NULL, 0); +#else + UNUSED(updater_start_app); +#endif +} diff --git a/applications/updater/scenes/updater_scene.c b/applications/system/updater/scenes/updater_scene.c similarity index 100% rename from applications/updater/scenes/updater_scene.c rename to applications/system/updater/scenes/updater_scene.c diff --git a/applications/updater/scenes/updater_scene.h b/applications/system/updater/scenes/updater_scene.h similarity index 100% rename from applications/updater/scenes/updater_scene.h rename to applications/system/updater/scenes/updater_scene.h diff --git a/applications/updater/scenes/updater_scene_config.h b/applications/system/updater/scenes/updater_scene_config.h similarity index 100% rename from applications/updater/scenes/updater_scene_config.h rename to applications/system/updater/scenes/updater_scene_config.h diff --git a/applications/updater/scenes/updater_scene_error.c b/applications/system/updater/scenes/updater_scene_error.c similarity index 92% rename from applications/updater/scenes/updater_scene_error.c rename to applications/system/updater/scenes/updater_scene_error.c index 362c471a681..dbe97c96b01 100644 --- a/applications/updater/scenes/updater_scene_error.c +++ b/applications/system/updater/scenes/updater_scene_error.c @@ -1,4 +1,4 @@ -#include "updater/updater_i.h" +#include "../updater_i.h" #include "updater_scene.h" #include @@ -58,8 +58,12 @@ bool updater_scene_error_on_event(void* context, SceneManagerEvent event) { } void updater_scene_error_on_exit(void* context) { + furi_assert(context); Updater* updater = (Updater*)context; widget_reset(updater->widget); - free(updater->pending_update); + + if(updater->loaded_manifest) { + update_manifest_free(updater->loaded_manifest); + } } diff --git a/applications/updater/scenes/updater_scene_loadcfg.c b/applications/system/updater/scenes/updater_scene_loadcfg.c similarity index 81% rename from applications/updater/scenes/updater_scene_loadcfg.c rename to applications/system/updater/scenes/updater_scene_loadcfg.c index 1fd87d002a9..99866a6dfdd 100644 --- a/applications/updater/scenes/updater_scene_loadcfg.c +++ b/applications/system/updater/scenes/updater_scene_loadcfg.c @@ -1,4 +1,4 @@ -#include "updater/updater_i.h" +#include "../updater_i.h" #include "updater_scene.h" #include @@ -21,11 +21,9 @@ void updater_scene_loadcfg_apply_callback(GuiButtonType result, InputType type, void updater_scene_loadcfg_on_enter(void* context) { Updater* updater = (Updater*)context; - UpdaterManifestProcessingState* pending_upd = updater->pending_update = - malloc(sizeof(UpdaterManifestProcessingState)); - pending_upd->manifest = update_manifest_alloc(); + UpdateManifest* loaded_manifest = updater->loaded_manifest = update_manifest_alloc(); - if(update_manifest_init(pending_upd->manifest, string_get_cstr(updater->startup_arg))) { + if(update_manifest_init(loaded_manifest, furi_string_get_cstr(updater->startup_arg))) { widget_add_string_element( updater->widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, "Update"); @@ -37,7 +35,7 @@ void updater_scene_loadcfg_on_enter(void* context) { 32, AlignCenter, AlignCenter, - string_get_cstr(pending_upd->manifest->version), + furi_string_get_cstr(loaded_manifest->version), true); widget_add_button_element( @@ -72,7 +70,7 @@ bool updater_scene_loadcfg_on_event(void* context, SceneManagerEvent event) { switch(event.event) { case UpdaterCustomEventStartUpdate: updater->preparation_result = - update_operation_prepare(string_get_cstr(updater->startup_arg)); + update_operation_prepare(furi_string_get_cstr(updater->startup_arg)); if(updater->preparation_result == UpdatePrepareResultOK) { furi_hal_power_reset(); } else { @@ -95,13 +93,12 @@ bool updater_scene_loadcfg_on_event(void* context, SceneManagerEvent event) { } void updater_scene_loadcfg_on_exit(void* context) { + furi_assert(context); Updater* updater = (Updater*)context; - if(updater->pending_update) { - update_manifest_free(updater->pending_update->manifest); - string_clear(updater->pending_update->message); - } - widget_reset(updater->widget); - free(updater->pending_update); + + if(updater->loaded_manifest) { + update_manifest_free(updater->loaded_manifest); + } } diff --git a/applications/updater/scenes/updater_scene_main.c b/applications/system/updater/scenes/updater_scene_main.c similarity index 96% rename from applications/updater/scenes/updater_scene_main.c rename to applications/system/updater/scenes/updater_scene_main.c index 5f7aeaca4b6..9fd68161f50 100644 --- a/applications/updater/scenes/updater_scene_main.c +++ b/applications/system/updater/scenes/updater_scene_main.c @@ -3,8 +3,8 @@ #include #include -#include "updater/updater_i.h" -#include "updater/views/updater_main.h" +#include "../updater_i.h" +#include "../views/updater_main.h" #include "updater_scene.h" static void sd_mount_callback(const void* message, void* context) { @@ -80,7 +80,7 @@ bool updater_scene_main_on_event(void* context, SceneManagerEvent event) { break; case UpdaterCustomEventSdUnmounted: - // TODO: error out, stop worker (it's probably dead actually) + // TODO FL-3499: error out, stop worker (it's probably dead actually) break; default: break; diff --git a/applications/updater/updater.c b/applications/system/updater/updater.c similarity index 94% rename from applications/updater/updater.c rename to applications/system/updater/updater.c index e9bedc72e1c..4c7fd29e9cf 100644 --- a/applications/updater/updater.c +++ b/applications/system/updater/updater.c @@ -5,7 +5,6 @@ #include #include #include -#include #include static bool updater_custom_event_callback(void* context, uint32_t event) { @@ -35,10 +34,10 @@ static void Updater* updater_alloc(const char* arg) { Updater* updater = malloc(sizeof(Updater)); if(arg && strlen(arg)) { - string_init_set_str(updater->startup_arg, arg); - string_replace_str(updater->startup_arg, ANY_PATH(""), EXT_PATH("")); + updater->startup_arg = furi_string_alloc_set(arg); + furi_string_replace(updater->startup_arg, ANY_PATH(""), EXT_PATH("")); } else { - string_init(updater->startup_arg); + updater->startup_arg = furi_string_alloc(); } updater->storage = furi_record_open(RECORD_STORAGE); @@ -94,7 +93,7 @@ Updater* updater_alloc(const char* arg) { void updater_free(Updater* updater) { furi_assert(updater); - string_clear(updater->startup_arg); + furi_string_free(updater->startup_arg); if(updater->update_task) { update_task_set_progress_cb(updater->update_task, NULL, NULL); update_task_free(updater->update_task); diff --git a/applications/updater/updater_i.h b/applications/system/updater/updater_i.h similarity index 82% rename from applications/updater/updater_i.h rename to applications/system/updater/updater_i.h index 8a021a08df9..4e3c704d21a 100644 --- a/applications/updater/updater_i.h +++ b/applications/system/updater/updater_i.h @@ -33,12 +33,6 @@ typedef enum { UpdaterCustomEventSdUnmounted, } UpdaterCustomEvent; -typedef struct UpdaterManifestProcessingState { - UpdateManifest* manifest; - string_t message; - bool ready_to_be_applied; -} UpdaterManifestProcessingState; - typedef struct { // GUI Gui* gui; @@ -49,12 +43,12 @@ typedef struct { UpdaterMainView* main_view; - UpdaterManifestProcessingState* pending_update; + UpdateManifest* loaded_manifest; UpdatePrepareResult preparation_result; UpdateTask* update_task; Widget* widget; - string_t startup_arg; + FuriString* startup_arg; int32_t idle_ticks; } Updater; diff --git a/applications/system/updater/util/update_task.c b/applications/system/updater/util/update_task.c new file mode 100644 index 00000000000..df1793a8009 --- /dev/null +++ b/applications/system/updater/util/update_task.c @@ -0,0 +1,535 @@ +#include "update_task.h" +#include "update_task_i.h" + +#include +#include +#include +#include +#include +#include +#include + +static const char* update_task_stage_descr[] = { + [UpdateTaskStageProgress] = "...", + [UpdateTaskStageReadManifest] = "Loading update manifest", + [UpdateTaskStageValidateDFUImage] = "Checking DFU file", + [UpdateTaskStageFlashWrite] = "Writing flash", + [UpdateTaskStageFlashValidate] = "Validating flash", + [UpdateTaskStageRadioImageValidate] = "Checking radio FW", + [UpdateTaskStageRadioErase] = "Uninstalling radio FW", + [UpdateTaskStageRadioWrite] = "Writing radio FW", + [UpdateTaskStageRadioInstall] = "Installing radio FW", + [UpdateTaskStageRadioBusy] = "Core 2 busy", + [UpdateTaskStageOBValidation] = "Validating opt. bytes", + [UpdateTaskStageLfsBackup] = "Backing up LFS", + [UpdateTaskStageLfsRestore] = "Restoring LFS", + [UpdateTaskStageResourcesUpdate] = "Updating resources", + [UpdateTaskStageSplashscreenInstall] = "Installing splashscreen", + [UpdateTaskStageCompleted] = "Restarting...", + [UpdateTaskStageError] = "Error", + [UpdateTaskStageOBError] = "OB, report", +}; + +static const struct { + UpdateTaskStage stage; + uint8_t percent_min, percent_max; + const char* descr; +} update_task_error_detail[] = { + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 0, + .percent_max = 13, + .descr = "Wrong Updater HW", + }, + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 14, + .percent_max = 20, + .descr = "Manifest pointer error", + }, + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 21, + .percent_max = 30, + .descr = "Manifest load error", + }, + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 31, + .percent_max = 40, + .descr = "Wrong package version", + }, + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 41, + .percent_max = 50, + .descr = "HW Target mismatch", + }, + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 51, + .percent_max = 60, + .descr = "No DFU file", + }, + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 61, + .percent_max = 80, + .descr = "No Radio file", + }, +#ifndef FURI_RAM_EXEC + { + .stage = UpdateTaskStageLfsBackup, + .percent_min = 0, + .percent_max = 100, + .descr = "FS R/W error", + }, +#else + { + .stage = UpdateTaskStageRadioImageValidate, + .percent_min = 0, + .percent_max = 98, + .descr = "FS Read error", + }, + { + .stage = UpdateTaskStageRadioImageValidate, + .percent_min = 99, + .percent_max = 100, + .descr = "CRC mismatch", + }, + { + .stage = UpdateTaskStageRadioErase, + .percent_min = 0, + .percent_max = 30, + .descr = "Stack remove: cmd error", + }, + { + .stage = UpdateTaskStageRadioErase, + .percent_min = 31, + .percent_max = 100, + .descr = "Stack remove: wait failed", + }, + { + .stage = UpdateTaskStageRadioWrite, + .percent_min = 0, + .percent_max = 100, + .descr = "Stack write: error", + }, + { + .stage = UpdateTaskStageRadioInstall, + .percent_min = 0, + .percent_max = 10, + .descr = "Stack install: cmd error", + }, + { + .stage = UpdateTaskStageRadioInstall, + .percent_min = 11, + .percent_max = 100, + .descr = "Stack install: wait failed", + }, + { + .stage = UpdateTaskStageRadioBusy, + .percent_min = 0, + .percent_max = 10, + .descr = "Failed to start C2", + }, + { + .stage = UpdateTaskStageRadioBusy, + .percent_min = 11, + .percent_max = 20, + .descr = "C2 FUS switch failed", + }, + { + .stage = UpdateTaskStageRadioBusy, + .percent_min = 21, + .percent_max = 30, + .descr = "FUS operation failed", + }, + { + .stage = UpdateTaskStageRadioBusy, + .percent_min = 31, + .percent_max = 100, + .descr = "C2 Stach switch failed", + }, + { + .stage = UpdateTaskStageOBValidation, + .percent_min = 0, + .percent_max = 100, + .descr = "Uncorr. value mismatch", + }, + { + .stage = UpdateTaskStageValidateDFUImage, + .percent_min = 0, + .percent_max = 1, + .descr = "Failed to open DFU file", + }, + { + .stage = UpdateTaskStageValidateDFUImage, + .percent_min = 1, + .percent_max = 97, + .descr = "DFU file read error", + }, + { + .stage = UpdateTaskStageValidateDFUImage, + .percent_min = 98, + .percent_max = 100, + .descr = "DFU file CRC mismatch", + }, + { + .stage = UpdateTaskStageFlashWrite, + .percent_min = 0, + .percent_max = 100, + .descr = "Flash write error", + }, + { + .stage = UpdateTaskStageFlashValidate, + .percent_min = 0, + .percent_max = 100, + .descr = "Flash compare error", + }, +#endif +#ifndef FURI_RAM_EXEC + { + .stage = UpdateTaskStageLfsRestore, + .percent_min = 0, + .percent_max = 100, + .descr = "LFS I/O error", + }, + { + .stage = UpdateTaskStageResourcesUpdate, + .percent_min = 0, + .percent_max = 100, + .descr = "SD card I/O error", + }, +#endif +}; + +static const char* update_task_get_error_message(UpdateTaskStage stage, uint8_t percent) { + for(size_t i = 0; i < COUNT_OF(update_task_error_detail); i++) { + if(update_task_error_detail[i].stage == stage && + percent >= update_task_error_detail[i].percent_min && + percent <= update_task_error_detail[i].percent_max) { + return update_task_error_detail[i].descr; + } + } + return "Unknown error"; +} + +typedef struct { + UpdateTaskStageGroup group; + uint8_t weight; +} UpdateTaskStageGroupMap; + +#define STAGE_DEF(GROUP, WEIGHT) \ + { .group = (GROUP), .weight = (WEIGHT), } + +static const UpdateTaskStageGroupMap update_task_stage_progress[] = { + [UpdateTaskStageProgress] = STAGE_DEF(UpdateTaskStageGroupMisc, 0), + + [UpdateTaskStageReadManifest] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 45), + [UpdateTaskStageLfsBackup] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 5), + + [UpdateTaskStageRadioImageValidate] = STAGE_DEF(UpdateTaskStageGroupRadio, 15), + [UpdateTaskStageRadioErase] = STAGE_DEF(UpdateTaskStageGroupRadio, 35), + [UpdateTaskStageRadioWrite] = STAGE_DEF(UpdateTaskStageGroupRadio, 60), + [UpdateTaskStageRadioInstall] = STAGE_DEF(UpdateTaskStageGroupRadio, 30), + [UpdateTaskStageRadioBusy] = STAGE_DEF(UpdateTaskStageGroupRadio, 5), + + [UpdateTaskStageOBValidation] = STAGE_DEF(UpdateTaskStageGroupOptionBytes, 2), + + [UpdateTaskStageValidateDFUImage] = STAGE_DEF(UpdateTaskStageGroupFirmware, 30), + [UpdateTaskStageFlashWrite] = STAGE_DEF(UpdateTaskStageGroupFirmware, 150), + [UpdateTaskStageFlashValidate] = STAGE_DEF(UpdateTaskStageGroupFirmware, 15), + + [UpdateTaskStageLfsRestore] = STAGE_DEF(UpdateTaskStageGroupPostUpdate, 5), + + [UpdateTaskStageResourcesUpdate] = STAGE_DEF(UpdateTaskStageGroupResources, 255), + [UpdateTaskStageSplashscreenInstall] = STAGE_DEF(UpdateTaskStageGroupSplashscreen, 5), + + [UpdateTaskStageCompleted] = STAGE_DEF(UpdateTaskStageGroupMisc, 1), + [UpdateTaskStageError] = STAGE_DEF(UpdateTaskStageGroupMisc, 1), + [UpdateTaskStageOBError] = STAGE_DEF(UpdateTaskStageGroupMisc, 1), +}; + +static UpdateTaskStageGroup update_task_get_task_groups(UpdateTask* update_task) { + UpdateTaskStageGroup ret = UpdateTaskStageGroupPreUpdate | UpdateTaskStageGroupPostUpdate; + UpdateManifest* manifest = update_task->manifest; + if(!furi_string_empty(manifest->radio_image)) { + ret |= UpdateTaskStageGroupRadio; + } + if(update_manifest_has_obdata(manifest)) { + ret |= UpdateTaskStageGroupOptionBytes; + } + if(!furi_string_empty(manifest->firmware_dfu_image)) { + ret |= UpdateTaskStageGroupFirmware; + } + if(!furi_string_empty(manifest->resource_bundle)) { + ret |= UpdateTaskStageGroupResources; + } + if(!furi_string_empty(manifest->splash_file)) { + ret |= UpdateTaskStageGroupSplashscreen; + } + return ret; +} + +static void update_task_calc_completed_stages(UpdateTask* update_task) { + uint32_t completed_stages_points = 0; + for(UpdateTaskStage past_stage = UpdateTaskStageProgress; + past_stage < update_task->state.stage; + ++past_stage) { + const UpdateTaskStageGroupMap* grp_descr = &update_task_stage_progress[past_stage]; + if((grp_descr->group & update_task->state.groups) == 0) { + continue; + } + completed_stages_points += grp_descr->weight; + } + update_task->state.completed_stages_points = completed_stages_points; +} + +void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, uint8_t progress) { + if(stage != UpdateTaskStageProgress) { + /* do not override more specific error states */ + if((stage >= UpdateTaskStageError) && (update_task->state.stage >= UpdateTaskStageError)) { + return; + } + /* Build error message with code "[stage_idx-stage_percent]" */ + if(stage >= UpdateTaskStageError) { + furi_string_printf( + update_task->state.status, + "%s\n#[%d-%d]", + update_task_get_error_message( + update_task->state.stage, update_task->state.stage_progress), + update_task->state.stage, + update_task->state.stage_progress); + } else { + furi_string_set(update_task->state.status, update_task_stage_descr[stage]); + } + /* Store stage update */ + update_task->state.stage = stage; + /* If we are still alive, sum completed stages weights */ + if((stage > UpdateTaskStageProgress) && (stage < UpdateTaskStageCompleted)) { + update_task_calc_completed_stages(update_task); + } + } + + /* Store stage progress for all non-error updates - to provide details on error state */ + if(!update_stage_is_error(stage)) { + update_task->state.stage_progress = progress; + } + + /* Calculate "overall" progress, based on stage weights */ + uint32_t adapted_progress = 1; + if(update_task->state.total_progress_points != 0) { + if(stage < UpdateTaskStageCompleted) { + adapted_progress = MIN( + (update_task->state.completed_stages_points + + (update_task_stage_progress[update_task->state.stage].weight * progress / 100)) * + 100 / (update_task->state.total_progress_points), + 100u); + + } else { + adapted_progress = update_task->state.overall_progress; + } + } + update_task->state.overall_progress = adapted_progress; + + if(update_task->status_change_cb) { + (update_task->status_change_cb)( + furi_string_get_cstr(update_task->state.status), + adapted_progress, + update_stage_is_error(update_task->state.stage), + update_task->status_change_cb_state); + } +} + +static void update_task_close_file(UpdateTask* update_task) { + furi_assert(update_task); + if(!storage_file_is_open(update_task->file)) { + return; + } + + storage_file_close(update_task->file); +} + +static bool update_task_check_file_exists(UpdateTask* update_task, FuriString* filename) { + furi_assert(update_task); + FuriString* tmp_path; + tmp_path = furi_string_alloc_set(update_task->update_path); + path_append(tmp_path, furi_string_get_cstr(filename)); + bool exists = storage_file_exists(update_task->storage, furi_string_get_cstr(tmp_path)); + furi_string_free(tmp_path); + return exists; +} + +bool update_task_open_file(UpdateTask* update_task, FuriString* filename) { + furi_assert(update_task); + update_task_close_file(update_task); + + FuriString* tmp_path; + tmp_path = furi_string_alloc_set(update_task->update_path); + path_append(tmp_path, furi_string_get_cstr(filename)); + bool open_success = storage_file_open( + update_task->file, furi_string_get_cstr(tmp_path), FSAM_READ, FSOM_OPEN_EXISTING); + furi_string_free(tmp_path); + return open_success; +} + +static void update_task_worker_thread_cb(FuriThreadState state, void* context) { + UpdateTask* update_task = context; + + if(state != FuriThreadStateStopped) { + return; + } + + if(furi_thread_get_return_code(update_task->thread) == UPDATE_TASK_NOERR) { + furi_delay_ms(UPDATE_DELAY_OPERATION_OK); + furi_hal_power_reset(); + } +} + +UpdateTask* update_task_alloc() { + UpdateTask* update_task = malloc(sizeof(UpdateTask)); + + update_task->state.stage = UpdateTaskStageProgress; + update_task->state.stage_progress = 0; + update_task->state.overall_progress = 0; + update_task->state.status = furi_string_alloc(); + + update_task->manifest = update_manifest_alloc(); + update_task->storage = furi_record_open(RECORD_STORAGE); + update_task->file = storage_file_alloc(update_task->storage); + update_task->status_change_cb = NULL; + update_task->boot_mode = furi_hal_rtc_get_boot_mode(); + update_task->update_path = furi_string_alloc(); + + FuriThread* thread = update_task->thread = + furi_thread_alloc_ex("UpdateWorker", 5120, NULL, update_task); + + furi_thread_set_state_callback(thread, update_task_worker_thread_cb); + furi_thread_set_state_context(thread, update_task); +#ifdef FURI_RAM_EXEC + UNUSED(update_task_worker_backup_restore); + furi_thread_set_callback(thread, update_task_worker_flash_writer); +#else + UNUSED(update_task_worker_flash_writer); + furi_thread_set_callback(thread, update_task_worker_backup_restore); +#endif + + return update_task; +} + +void update_task_free(UpdateTask* update_task) { + furi_assert(update_task); + + furi_thread_join(update_task->thread); + + furi_thread_free(update_task->thread); + update_task_close_file(update_task); + storage_file_free(update_task->file); + update_manifest_free(update_task->manifest); + + furi_record_close(RECORD_STORAGE); + furi_string_free(update_task->update_path); + + free(update_task); +} + +bool update_task_parse_manifest(UpdateTask* update_task) { + furi_assert(update_task); + update_task->state.stage_progress = 0; + update_task->state.overall_progress = 0; + update_task->state.total_progress_points = 0; + update_task->state.completed_stages_points = 0; + update_task->state.groups = 0; + + update_task_set_progress(update_task, UpdateTaskStageReadManifest, 0); + bool result = false; + FuriString* manifest_path; + manifest_path = furi_string_alloc(); + + do { + update_task_set_progress(update_task, UpdateTaskStageProgress, 13); + if(!furi_hal_version_do_i_belong_here()) { + break; + } + + update_task_set_progress(update_task, UpdateTaskStageProgress, 20); + if(!update_operation_get_current_package_manifest_path( + update_task->storage, manifest_path)) { + break; + } + + path_extract_dirname(furi_string_get_cstr(manifest_path), update_task->update_path); + update_task_set_progress(update_task, UpdateTaskStageProgress, 30); + + UpdateManifest* manifest = update_task->manifest; + if(!update_manifest_init(manifest, furi_string_get_cstr(manifest_path))) { + break; + } + + update_task_set_progress(update_task, UpdateTaskStageProgress, 40); + if(manifest->manifest_version < UPDATE_OPERATION_MIN_MANIFEST_VERSION) { + break; + } + + update_task_set_progress(update_task, UpdateTaskStageProgress, 50); + /* Check target only if it's set - skipped for pre-production samples */ + if(furi_hal_version_get_hw_target() && + (manifest->target != furi_hal_version_get_hw_target())) { + break; + } + + update_task->state.groups = update_task_get_task_groups(update_task); + for(size_t stage_counter = 0; stage_counter < COUNT_OF(update_task_stage_progress); + ++stage_counter) { + const UpdateTaskStageGroupMap* grp_descr = &update_task_stage_progress[stage_counter]; + if((grp_descr->group & update_task->state.groups) != 0) { + update_task->state.total_progress_points += grp_descr->weight; + } + } + + update_task_set_progress(update_task, UpdateTaskStageProgress, 60); + if((update_task->state.groups & UpdateTaskStageGroupFirmware) && + !update_task_check_file_exists(update_task, manifest->firmware_dfu_image)) { + break; + } + + update_task_set_progress(update_task, UpdateTaskStageProgress, 80); + if((update_task->state.groups & UpdateTaskStageGroupRadio) && + (!update_task_check_file_exists(update_task, manifest->radio_image) || + (manifest->radio_version.version.type == 0))) { + break; + } + + update_task_set_progress(update_task, UpdateTaskStageProgress, 100); + result = true; + } while(false); + + furi_string_free(manifest_path); + return result; +} + +void update_task_set_progress_cb(UpdateTask* update_task, updateProgressCb cb, void* state) { + update_task->status_change_cb = cb; + update_task->status_change_cb_state = state; +} + +void update_task_start(UpdateTask* update_task) { + furi_assert(update_task); + furi_thread_start(update_task->thread); +} + +bool update_task_is_running(UpdateTask* update_task) { + furi_assert(update_task); + return furi_thread_get_state(update_task->thread) == FuriThreadStateRunning; +} + +UpdateTaskState const* update_task_get_state(UpdateTask* update_task) { + furi_assert(update_task); + return &update_task->state; +} + +UpdateManifest const* update_task_get_manifest(UpdateTask* update_task) { + furi_assert(update_task); + return update_task->manifest; +} diff --git a/applications/updater/util/update_task.h b/applications/system/updater/util/update_task.h similarity index 96% rename from applications/updater/util/update_task.h rename to applications/system/updater/util/update_task.h index 1f291556852..b3ac3f2f825 100644 --- a/applications/updater/util/update_task.h +++ b/applications/system/updater/util/update_task.h @@ -8,9 +8,8 @@ extern "C" { #include #include -#include -#define UPDATE_DELAY_OPERATION_OK 300 +#define UPDATE_DELAY_OPERATION_OK 10 #define UPDATE_DELAY_OPERATION_ERROR INT_MAX typedef enum { @@ -59,7 +58,7 @@ typedef enum { typedef struct { UpdateTaskStage stage; uint8_t overall_progress, stage_progress; - string_t status; + FuriString* status; UpdateTaskStageGroup groups; uint32_t total_progress_points; uint32_t completed_stages_points; diff --git a/applications/updater/util/update_task_i.h b/applications/system/updater/util/update_task_i.h similarity index 77% rename from applications/updater/util/update_task_i.h rename to applications/system/updater/util/update_task_i.h index 95c63f64432..1b664e57e4c 100644 --- a/applications/updater/util/update_task_i.h +++ b/applications/system/updater/util/update_task_i.h @@ -8,7 +8,7 @@ typedef struct UpdateTask { UpdateTaskState state; - string_t update_path; + FuriString* update_path; UpdateManifest* manifest; FuriThread* thread; Storage* storage; @@ -20,7 +20,12 @@ typedef struct UpdateTask { void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, uint8_t progress); bool update_task_parse_manifest(UpdateTask* update_task); -bool update_task_open_file(UpdateTask* update_task, string_t filename); +bool update_task_open_file(UpdateTask* update_task, FuriString* filename); int32_t update_task_worker_flash_writer(void* context); int32_t update_task_worker_backup_restore(void* context); + +#define CHECK_RESULT(x) \ + if(!(x)) { \ + break; \ + } diff --git a/applications/system/updater/util/update_task_worker_backup.c b/applications/system/updater/util/update_task_worker_backup.c new file mode 100644 index 00000000000..ef4276fac5e --- /dev/null +++ b/applications/system/updater/util/update_task_worker_backup.c @@ -0,0 +1,246 @@ +#include "update_task.h" +#include "update_task_i.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TAG "UpdWorkerBackup" + +static bool update_task_pre_update(UpdateTask* update_task) { + bool success = false; + FuriString* backup_file_path; + backup_file_path = furi_string_alloc(); + path_concat( + furi_string_get_cstr(update_task->update_path), + LFS_BACKUP_DEFAULT_FILENAME, + backup_file_path); + + update_task_set_progress(update_task, UpdateTaskStageLfsBackup, 0); + /* to avoid bootloops */ + furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); + if((success = + lfs_backup_create(update_task->storage, furi_string_get_cstr(backup_file_path)))) { + furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeUpdate); + } + + furi_string_free(backup_file_path); + return success; +} + +typedef enum { + UpdateTaskResourcesWeightsFileCleanup = 20, + UpdateTaskResourcesWeightsDirCleanup = 20, + UpdateTaskResourcesWeightsFileUnpack = 60, +} UpdateTaskResourcesWeights; + +#define UPDATE_TASK_RESOURCES_FILE_TO_TOTAL_PERCENT 90 + +typedef struct { + UpdateTask* update_task; + int32_t total_files, processed_files; +} TarUnpackProgress; + +static bool update_task_resource_unpack_cb(const char* name, bool is_directory, void* context) { + UNUSED(name); + UNUSED(is_directory); + TarUnpackProgress* unpack_progress = context; + unpack_progress->processed_files++; + update_task_set_progress( + unpack_progress->update_task, + UpdateTaskStageProgress, + /* For this stage, last progress segment = extraction */ + (UpdateTaskResourcesWeightsFileCleanup + UpdateTaskResourcesWeightsDirCleanup) + + (unpack_progress->processed_files * UpdateTaskResourcesWeightsFileUnpack) / + (unpack_progress->total_files + 1)); + return true; +} + +static void update_task_cleanup_resources(UpdateTask* update_task, const uint32_t n_tar_entries) { + ResourceManifestReader* manifest_reader = resource_manifest_reader_alloc(update_task->storage); + do { + FURI_LOG_D(TAG, "Cleaning up old manifest"); + if(!resource_manifest_reader_open(manifest_reader, EXT_PATH("Manifest"))) { + FURI_LOG_W(TAG, "No existing manifest"); + break; + } + + const uint32_t n_approx_file_entries = + n_tar_entries * UPDATE_TASK_RESOURCES_FILE_TO_TOTAL_PERCENT / 100 + 1; + uint32_t n_dir_entries = 1; + + ResourceManifestEntry* entry_ptr = NULL; + uint32_t n_processed_entries = 0; + while((entry_ptr = resource_manifest_reader_next(manifest_reader))) { + if(entry_ptr->type == ResourceManifestEntryTypeFile) { + update_task_set_progress( + update_task, + UpdateTaskStageProgress, + /* For this stage, first pass = old manifest's file cleanup */ + (n_processed_entries++ * UpdateTaskResourcesWeightsFileCleanup) / + n_approx_file_entries); + + FuriString* file_path = furi_string_alloc(); + 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)); + + 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++; + } + } + + n_processed_entries = 0; + while((entry_ptr = resource_manifest_reader_previous(manifest_reader))) { + if(entry_ptr->type == ResourceManifestEntryTypeDirectory) { + update_task_set_progress( + update_task, + UpdateTaskStageProgress, + /* For this stage, second 10% of progress = cleanup directories */ + UpdateTaskResourcesWeightsFileCleanup + + (n_processed_entries++ * UpdateTaskResourcesWeightsDirCleanup) / + n_dir_entries); + + FuriString* folder_path = furi_string_alloc(); + + do { + path_concat( + STORAGE_EXT_PATH_PREFIX, + furi_string_get_cstr(entry_ptr->name), + folder_path); + + FURI_LOG_D(TAG, "Removing folder %s", furi_string_get_cstr(folder_path)); + 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 remove failed, cause %s", + furi_string_get_cstr(folder_path), + storage_error_get_desc(result)); + } + } while(false); + + furi_string_free(folder_path); + } + } + } while(false); + resource_manifest_reader_free(manifest_reader); +} + +static bool update_task_post_update(UpdateTask* update_task) { + bool success = false; + + FuriString* file_path; + file_path = furi_string_alloc(); + + TarArchive* archive = tar_archive_alloc(update_task->storage); + do { + path_concat( + furi_string_get_cstr(update_task->update_path), + LFS_BACKUP_DEFAULT_FILENAME, + file_path); + + update_task_set_progress(update_task, UpdateTaskStageLfsRestore, 0); + + CHECK_RESULT(lfs_backup_unpack(update_task->storage, furi_string_get_cstr(file_path))); + + if(update_task->state.groups & UpdateTaskStageGroupResources) { + TarUnpackProgress progress = { + .update_task = update_task, + .total_files = 0, + .processed_files = 0, + }; + update_task_set_progress(update_task, UpdateTaskStageResourcesUpdate, 0); + + path_concat( + furi_string_get_cstr(update_task->update_path), + furi_string_get_cstr(update_task->manifest->resource_bundle), + file_path); + + tar_archive_set_file_callback(archive, update_task_resource_unpack_cb, &progress); + CHECK_RESULT( + tar_archive_open(archive, furi_string_get_cstr(file_path), TAR_OPEN_MODE_READ)); + + progress.total_files = tar_archive_get_entries_count(archive); + if(progress.total_files > 0) { + update_task_cleanup_resources(update_task, progress.total_files); + + CHECK_RESULT(tar_archive_unpack_to(archive, STORAGE_EXT_PATH_PREFIX, NULL)); + } + } + + if(update_task->state.groups & UpdateTaskStageGroupSplashscreen) { + update_task_set_progress(update_task, UpdateTaskStageSplashscreenInstall, 0); + FuriString* tmp_path; + tmp_path = furi_string_alloc_set(update_task->update_path); + path_append(tmp_path, furi_string_get_cstr(update_task->manifest->splash_file)); + if(storage_common_copy( + update_task->storage, + furi_string_get_cstr(tmp_path), + INT_PATH(SLIDESHOW_FILE_NAME)) != FSE_OK) { + // actually, not critical + } + furi_string_free(tmp_path); + update_task_set_progress(update_task, UpdateTaskStageSplashscreenInstall, 100); + } + success = true; + } while(false); + + tar_archive_free(archive); + furi_string_free(file_path); + return success; +} + +int32_t update_task_worker_backup_restore(void* context) { + furi_assert(context); + UpdateTask* update_task = context; + + FuriHalRtcBootMode boot_mode = update_task->boot_mode; + if((boot_mode != FuriHalRtcBootModePreUpdate) && (boot_mode != FuriHalRtcBootModePostUpdate)) { + /* no idea how we got here. Do nothing */ + return UPDATE_TASK_NOERR; + } + + bool success = false; + do { + if(!update_task_parse_manifest(update_task)) { + break; + } + + if(boot_mode == FuriHalRtcBootModePreUpdate) { + success = update_task_pre_update(update_task); + } else if(boot_mode == FuriHalRtcBootModePostUpdate) { //-V547 + success = update_task_post_update(update_task); + if(success) { + update_operation_disarm(); + } + } + } while(false); + + if(!success) { + update_task_set_progress(update_task, UpdateTaskStageError, 0); + return UPDATE_TASK_FAILED; + } + + update_task_set_progress(update_task, UpdateTaskStageCompleted, 100); + return UPDATE_TASK_NOERR; +} diff --git a/applications/updater/util/update_task_worker_flasher.c b/applications/system/updater/util/update_task_worker_flasher.c similarity index 89% rename from applications/updater/util/update_task_worker_flasher.c rename to applications/system/updater/util/update_task_worker_flasher.c index d56b4ae0a9f..1b4b0790032 100644 --- a/applications/updater/util/update_task_worker_flasher.c +++ b/applications/system/updater/util/update_task_worker_flasher.c @@ -11,12 +11,7 @@ #include #include -#define TAG "UpdWorkerRAM" - -#define CHECK_RESULT(x) \ - if(!(x)) { \ - break; \ - } +#define TAG "UpdWorkerRam" #define STM_DFU_VENDOR_ID 0x0483 #define STM_DFU_PRODUCT_ID 0xDF11 @@ -52,11 +47,19 @@ static bool check_address_boundaries(const size_t address) { return ((address >= min_allowed_address) && (address < max_allowed_address)); } +static bool update_task_flash_program_page( + const uint8_t i_page, + const uint8_t* update_block, + uint16_t update_block_len) { + furi_hal_flash_program_page(i_page, update_block, update_block_len); + return true; +} + static bool update_task_write_dfu(UpdateTask* update_task) { DfuUpdateTask page_task = { .address_cb = &check_address_boundaries, .progress_cb = &update_task_file_progress, - .task_cb = &furi_hal_flash_program_page, + .task_cb = &update_task_flash_program_page, .context = update_task, }; @@ -101,7 +104,7 @@ static bool update_task_write_stack_data(UpdateTask* update_task) { update_task_set_progress(update_task, UpdateTaskStageRadioWrite, 0); uint8_t* fw_block = malloc(FLASH_PAGE_SIZE); - uint16_t bytes_read = 0; + size_t bytes_read = 0; uint32_t element_offs = 0; while(element_offs < stack_size) { @@ -117,7 +120,7 @@ static bool update_task_write_stack_data(UpdateTask* update_task) { furi_hal_flash_get_page_number(update_task->manifest->radio_address + element_offs); CHECK_RESULT(i_page >= 0); - CHECK_RESULT(furi_hal_flash_program_page(i_page, fw_block, bytes_read)); + furi_hal_flash_program_page(i_page, fw_block, bytes_read); element_offs += bytes_read; update_task_set_progress( @@ -129,13 +132,12 @@ static bool update_task_write_stack_data(UpdateTask* update_task) { } static void update_task_wait_for_restart(UpdateTask* update_task) { - update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 10); + update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 70); furi_delay_ms(C2_MODE_SWITCH_TIMEOUT); furi_crash("C2 timeout"); } static bool update_task_write_stack(UpdateTask* update_task) { - bool success = false; UpdateManifest* manifest = update_task->manifest; do { FURI_LOG_W(TAG, "Writing stack"); @@ -146,36 +148,34 @@ static bool update_task_write_stack(UpdateTask* update_task) { manifest->radio_crc); CHECK_RESULT(update_task_write_stack_data(update_task)); - update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 0); + update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 10); CHECK_RESULT( ble_glue_fus_stack_install(manifest->radio_address, 0) != BleGlueCommandResultError); - update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 80); + update_task_set_progress(update_task, UpdateTaskStageProgress, 80); CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK); - update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 100); + update_task_set_progress(update_task, UpdateTaskStageProgress, 100); /* ...system will restart here. */ update_task_wait_for_restart(update_task); - success = true; } while(false); - return success; + return false; /* will return only in the case of failure */ } static bool update_task_remove_stack(UpdateTask* update_task) { - bool success = false; do { FURI_LOG_W(TAG, "Removing stack"); update_task_set_progress(update_task, UpdateTaskStageRadioErase, 30); CHECK_RESULT(ble_glue_fus_stack_delete() != BleGlueCommandResultError); - update_task_set_progress(update_task, UpdateTaskStageRadioErase, 80); + update_task_set_progress(update_task, UpdateTaskStageProgress, 80); CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK); - update_task_set_progress(update_task, UpdateTaskStageRadioErase, 100); + update_task_set_progress(update_task, UpdateTaskStageProgress, 100); /* ...system will restart here. */ update_task_wait_for_restart(update_task); - success = true; } while(false); - return success; + return false; /* will return only in the case of failure */ } static bool update_task_manage_radiostack(UpdateTask* update_task) { + update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 10); bool success = false; do { CHECK_RESULT(ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT)); @@ -204,15 +204,17 @@ static bool update_task_manage_radiostack(UpdateTask* update_task) { /* Version or type mismatch. Let's boot to FUS and start updating. */ FURI_LOG_W(TAG, "Restarting to FUS"); furi_hal_rtc_set_flag(FuriHalRtcFlagC2Update); + update_task_set_progress(update_task, UpdateTaskStageProgress, 20); + CHECK_RESULT(furi_hal_bt_ensure_c2_mode(BleGlueC2ModeFUS)); /* ...system will restart here. */ update_task_wait_for_restart(update_task); } } else if(c2_state->mode == BleGlueC2ModeFUS) { /* OK, we're in FUS mode. */ - update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 10); FURI_LOG_W(TAG, "Waiting for FUS to settle"); - ble_glue_fus_wait_operation(); + update_task_set_progress(update_task, UpdateTaskStageProgress, 30); + CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK); if(stack_version_match) { /* We can't check StackType with FUS, but partial version matches */ if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagC2Update)) { @@ -226,7 +228,7 @@ static bool update_task_manage_radiostack(UpdateTask* update_task) { /* We might just had the stack installed. * Let's start it up to check its version */ FURI_LOG_W(TAG, "Starting stack to check full version"); - update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 40); + update_task_set_progress(update_task, UpdateTaskStageProgress, 50); CHECK_RESULT(furi_hal_bt_ensure_c2_mode(BleGlueC2ModeStack)); /* ...system will restart here. */ update_task_wait_for_restart(update_task); @@ -263,7 +265,7 @@ bool update_task_validate_optionbytes(UpdateTask* update_task) { match = false; FURI_LOG_E( TAG, - "OB MISMATCH: #%d: real %08X != %08X (exp.), full %08X", + "OB MISMATCH: #%d: real %08lX != %08lX (exp.), full %08lX", idx, device_ob_value_masked, ref_value, @@ -281,7 +283,7 @@ bool update_task_validate_optionbytes(UpdateTask* update_task) { (manifest->ob_reference.obs[idx].values.base & manifest->ob_write_mask.obs[idx].values.base); - FURI_LOG_W(TAG, "Fixing up OB byte #%d to %08X", idx, patched_value); + FURI_LOG_W(TAG, "Fixing up OB byte #%d to %08lX", idx, patched_value); ob_dirty = true; bool is_fixed = furi_hal_flash_ob_set_word(idx, patched_value) && @@ -293,16 +295,16 @@ bool update_task_validate_optionbytes(UpdateTask* update_task) { * reference value */ FURI_LOG_W( TAG, - "OB #%d is FUBAR (fixed&masked %08X, not %08X)", + "OB #%d is FUBAR (fixed&masked %08lX, not %08lX)", idx, patched_value, ref_value); } } } else { - FURI_LOG_I( + FURI_LOG_D( TAG, - "OB MATCH: #%d: real %08X == %08X (exp.)", + "OB MATCH: #%d: real %08lX == %08lX (exp.)", idx, device_ob_value_masked, ref_value); @@ -342,7 +344,11 @@ int32_t update_task_worker_flash_writer(void* context) { furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePostUpdate); // Format LFS before restoring backup on next boot furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); - +#ifdef FURI_NDEBUG + // Production + furi_hal_rtc_set_log_level(FuriLogLevelDefault); + furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug); +#endif update_task_set_progress(update_task, UpdateTaskStageCompleted, 100); success = true; } while(false); diff --git a/applications/system/updater/views/updater_main.c b/applications/system/updater/views/updater_main.c new file mode 100644 index 00000000000..d32d51b7c0b --- /dev/null +++ b/applications/system/updater/views/updater_main.c @@ -0,0 +1,142 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "../updater_i.h" +#include "updater_main.h" + +struct UpdaterMainView { + View* view; + ViewDispatcher* view_dispatcher; + FuriPubSubSubscription* subscription; + void* context; +}; + +static const uint8_t PROGRESS_RENDER_STEP = 1; /* percent, to limit rendering rate */ + +typedef struct { + FuriString* status; + uint8_t progress, rendered_progress; + bool failed; +} UpdaterProgressModel; + +void updater_main_model_set_state( + UpdaterMainView* main_view, + const char* message, + uint8_t progress, + bool failed) { + bool update = false; + with_view_model( + main_view->view, + UpdaterProgressModel * model, + { + model->failed = failed; + model->progress = progress; + if(furi_string_cmp_str(model->status, message)) { + furi_string_set(model->status, message); + model->rendered_progress = progress; + update = true; + } else if( + (model->rendered_progress > progress) || + ((progress - model->rendered_progress) > PROGRESS_RENDER_STEP)) { + model->rendered_progress = progress; + update = true; + } + }, + update); +} + +View* updater_main_get_view(UpdaterMainView* main_view) { + furi_assert(main_view); + return main_view->view; +} + +bool updater_main_input(InputEvent* event, void* context) { + furi_assert(event); + furi_assert(context); + + UpdaterMainView* main_view = context; + if(!main_view->view_dispatcher) { + return true; + } + + if((event->type == InputTypeShort) && (event->key == InputKeyOk)) { + view_dispatcher_send_custom_event( + main_view->view_dispatcher, UpdaterCustomEventRetryUpdate); + } else if((event->type == InputTypeLong) && (event->key == InputKeyBack)) { + view_dispatcher_send_custom_event( + main_view->view_dispatcher, UpdaterCustomEventCancelUpdate); + } + + return true; +} + +static void updater_main_draw_callback(Canvas* canvas, void* _model) { + UpdaterProgressModel* model = _model; + + canvas_set_font(canvas, FontPrimary); + + if(model->failed) { + canvas_draw_icon(canvas, 2, 22, &I_Warning_30x23); + canvas_draw_str_aligned(canvas, 40, 9, AlignLeft, AlignTop, "Update Failed!"); + canvas_set_font(canvas, FontSecondary); + + elements_multiline_text_aligned( + canvas, 75, 26, AlignCenter, AlignTop, furi_string_get_cstr(model->status)); + + canvas_draw_str_aligned( + canvas, 18, 55, AlignLeft, AlignTop, "to retry, hold to abort"); + canvas_draw_icon(canvas, 7, 54, &I_Ok_btn_9x9); + canvas_draw_icon(canvas, 75, 55, &I_Pin_back_arrow_10x8); + } else { + canvas_draw_str_aligned(canvas, 55, 14, AlignLeft, AlignTop, "UPDATING"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 64, 51, AlignCenter, AlignTop, furi_string_get_cstr(model->status)); + canvas_draw_icon(canvas, 4, 5, &I_Updating_32x40); + elements_progress_bar(canvas, 42, 29, 80, (float)model->progress / 100); + } +} + +UpdaterMainView* updater_main_alloc() { + UpdaterMainView* main_view = malloc(sizeof(UpdaterMainView)); + + main_view->view = view_alloc(); + view_allocate_model(main_view->view, ViewModelTypeLocking, sizeof(UpdaterProgressModel)); + + with_view_model( + main_view->view, + UpdaterProgressModel * model, + { model->status = furi_string_alloc_set("Waiting for SD card"); }, + true); + + view_set_context(main_view->view, main_view); + view_set_input_callback(main_view->view, updater_main_input); + view_set_draw_callback(main_view->view, updater_main_draw_callback); + + return main_view; +} + +void updater_main_free(UpdaterMainView* main_view) { + furi_assert(main_view); + with_view_model( + main_view->view, UpdaterProgressModel * model, { furi_string_free(model->status); }, false); + view_free(main_view->view); + free(main_view); +} + +void updater_main_set_storage_pubsub(UpdaterMainView* main_view, FuriPubSubSubscription* sub) { + main_view->subscription = sub; +} + +FuriPubSubSubscription* updater_main_get_storage_pubsub(UpdaterMainView* main_view) { + return main_view->subscription; +} + +void updater_main_set_view_dispatcher(UpdaterMainView* main_view, ViewDispatcher* view_dispatcher) { + main_view->view_dispatcher = view_dispatcher; +} diff --git a/applications/updater/views/updater_main.h b/applications/system/updater/views/updater_main.h similarity index 100% rename from applications/updater/views/updater_main.h rename to applications/system/updater/views/updater_main.h diff --git a/applications/u2f/application.fam b/applications/u2f/application.fam deleted file mode 100644 index 6b32e022530..00000000000 --- a/applications/u2f/application.fam +++ /dev/null @@ -1,14 +0,0 @@ -App( - appid="u2f", - name="U2F", - apptype=FlipperAppType.APP, - entry_point="u2f_app", - cdefines=["APP_U2F"], - requires=[ - "gui", - "dialogs", - ], - stack_size=2 * 1024, - icon="A_U2F_14", - order=80, -) diff --git a/applications/u2f/u2f.c b/applications/u2f/u2f.c deleted file mode 100644 index 767733ce65b..00000000000 --- a/applications/u2f/u2f.c +++ /dev/null @@ -1,362 +0,0 @@ -#include -#include "u2f.h" -#include "u2f_hid.h" -#include "u2f_data.h" -#include -#include -#include // for lfs_tobe32 - -#include "toolbox/sha256.h" -#include "toolbox/hmac_sha256.h" -#include "micro-ecc/uECC.h" - -#define TAG "U2F" -#define WORKER_TAG TAG "Worker" - -#define U2F_CMD_REGISTER 0x01 -#define U2F_CMD_AUTHENTICATE 0x02 -#define U2F_CMD_VERSION 0x03 - -typedef enum { - U2fCheckOnly = 0x07, // "check-only" - only check key handle, don't send auth response - U2fEnforce = - 0x03, // "enforce-user-presence-and-sign" - send auth response only if user is present - U2fDontEnforce = - 0x08, // "dont-enforce-user-presence-and-sign" - send auth response even if user is missing -} U2fAuthMode; - -typedef struct { - uint8_t format; - uint8_t xy[64]; -} __attribute__((packed)) U2fPubKey; - -typedef struct { - uint8_t len; - uint8_t hash[32]; - uint8_t nonce[32]; -} __attribute__((packed)) U2fKeyHandle; - -typedef struct { - uint8_t cla; - uint8_t ins; - uint8_t p1; - uint8_t p2; - uint8_t len[3]; - uint8_t challenge[32]; - uint8_t app_id[32]; -} __attribute__((packed)) U2fRegisterReq; - -typedef struct { - uint8_t reserved; - U2fPubKey pub_key; - U2fKeyHandle key_handle; - uint8_t cert[]; -} __attribute__((packed)) U2fRegisterResp; - -typedef struct { - uint8_t cla; - uint8_t ins; - uint8_t p1; - uint8_t p2; - uint8_t len[3]; - uint8_t challenge[32]; - uint8_t app_id[32]; - U2fKeyHandle key_handle; -} __attribute__((packed)) U2fAuthReq; - -typedef struct { - uint8_t user_present; - uint32_t counter; - uint8_t signature[]; -} __attribute__((packed)) U2fAuthResp; - -static const uint8_t ver_str[] = {"U2F_V2"}; - -static const uint8_t state_no_error[] = {0x90, 0x00}; -static const uint8_t state_not_supported[] = {0x6D, 0x00}; -static const uint8_t state_user_missing[] = {0x69, 0x85}; -static const uint8_t state_wrong_data[] = {0x6A, 0x80}; - -struct U2fData { - uint8_t device_key[32]; - uint8_t cert_key[32]; - uint32_t counter; - const struct uECC_Curve_t* p_curve; - bool ready; - bool user_present; - U2fEvtCallback callback; - void* context; -}; - -static int u2f_uecc_random(uint8_t* dest, unsigned size) { - furi_hal_random_fill_buf(dest, size); - return 1; -} - -U2fData* u2f_alloc() { - return malloc(sizeof(U2fData)); -} - -void u2f_free(U2fData* U2F) { - furi_assert(U2F); - free(U2F); -} - -bool u2f_init(U2fData* U2F) { - furi_assert(U2F); - - if(u2f_data_cert_check() == false) { - FURI_LOG_E(TAG, "Certificate load error"); - return false; - } - if(u2f_data_cert_key_load(U2F->cert_key) == false) { - FURI_LOG_E(TAG, "Certificate key load error"); - return false; - } - if(u2f_data_key_load(U2F->device_key) == false) { - FURI_LOG_W(TAG, "Key loading error, generating new"); - if(u2f_data_key_generate(U2F->device_key) == false) { - FURI_LOG_E(TAG, "Key write failed"); - return false; - } - } - if(u2f_data_cnt_read(&U2F->counter) == false) { - FURI_LOG_W(TAG, "Counter loading error, resetting counter"); - U2F->counter = 0; - if(u2f_data_cnt_write(0) == false) { - FURI_LOG_E(TAG, "Counter write failed"); - return false; - } - } - - U2F->p_curve = uECC_secp256r1(); - uECC_set_rng(u2f_uecc_random); - - U2F->ready = true; - return true; -} - -void u2f_set_event_callback(U2fData* U2F, U2fEvtCallback callback, void* context) { - furi_assert(U2F); - furi_assert(callback); - U2F->callback = callback; - U2F->context = context; -} - -void u2f_confirm_user_present(U2fData* U2F) { - U2F->user_present = true; -} - -static uint8_t u2f_der_encode_int(uint8_t* der, uint8_t* val, uint8_t val_len) { - der[0] = 0x02; // Integer - - uint8_t len = 2; - // Omit leading zeros - while(val[0] == 0 && val_len > 0) { - ++val; - --val_len; - } - - // Check if integer is negative - if(val[0] > 0x7f) der[len++] = 0; - - memcpy(der + len, val, val_len); - len += val_len; - - der[1] = len - 2; - return len; -} - -static uint8_t u2f_der_encode_signature(uint8_t* der, uint8_t* sig) { - der[0] = 0x30; - - uint8_t len = 2; - len += u2f_der_encode_int(der + len, sig, 32); - len += u2f_der_encode_int(der + len, sig + 32, 32); - - der[1] = len - 2; - return len; -} - -static uint16_t u2f_register(U2fData* U2F, uint8_t* buf) { - U2fRegisterReq* req = (U2fRegisterReq*)buf; - U2fRegisterResp* resp = (U2fRegisterResp*)buf; - U2fKeyHandle handle; - uint8_t private[32]; - U2fPubKey pub_key; - uint8_t hash[32]; - uint8_t signature[64]; - - if(u2f_data_check(false) == false) { - U2F->ready = false; - if(U2F->callback != NULL) U2F->callback(U2fNotifyError, U2F->context); - memcpy(&buf[0], state_not_supported, 2); - return 2; - } - - if(U2F->callback != NULL) U2F->callback(U2fNotifyRegister, U2F->context); - if(U2F->user_present == false) { - memcpy(&buf[0], state_user_missing, 2); - return 2; - } - U2F->user_present = false; - - hmac_sha256_context hmac_ctx; - sha256_context sha_ctx; - - handle.len = 32 * 2; - // Generate random nonce - furi_hal_random_fill_buf(handle.nonce, 32); - - // Generate private key - hmac_sha256_init(&hmac_ctx, U2F->device_key); - hmac_sha256_update(&hmac_ctx, req->app_id, 32); - hmac_sha256_update(&hmac_ctx, handle.nonce, 32); - hmac_sha256_finish(&hmac_ctx, U2F->device_key, private); - - // Generate private key handle - hmac_sha256_init(&hmac_ctx, U2F->device_key); - hmac_sha256_update(&hmac_ctx, private, 32); - hmac_sha256_update(&hmac_ctx, req->app_id, 32); - hmac_sha256_finish(&hmac_ctx, U2F->device_key, handle.hash); - - // Generate public key - pub_key.format = 0x04; // Uncompressed point - uECC_compute_public_key(private, pub_key.xy, U2F->p_curve); - - // Generate signature - uint8_t reserved_byte = 0; - sha256_start(&sha_ctx); - sha256_update(&sha_ctx, &reserved_byte, 1); - sha256_update(&sha_ctx, req->app_id, 32); - sha256_update(&sha_ctx, req->challenge, 32); - sha256_update(&sha_ctx, handle.hash, handle.len); - sha256_update(&sha_ctx, (uint8_t*)&pub_key, 65); - sha256_finish(&sha_ctx, hash); - - uECC_sign(U2F->cert_key, hash, 32, signature, U2F->p_curve); - - // Encode response message - resp->reserved = 0x05; - memcpy(&(resp->pub_key), &pub_key, sizeof(U2fPubKey)); - memcpy(&(resp->key_handle), &handle, sizeof(U2fKeyHandle)); - uint32_t cert_len = u2f_data_cert_load(resp->cert); - uint8_t signature_len = u2f_der_encode_signature(resp->cert + cert_len, signature); - memcpy(resp->cert + cert_len + signature_len, state_no_error, 2); - - return (sizeof(U2fRegisterResp) + cert_len + signature_len + 2); -} - -static uint16_t u2f_authenticate(U2fData* U2F, uint8_t* buf) { - U2fAuthReq* req = (U2fAuthReq*)buf; - U2fAuthResp* resp = (U2fAuthResp*)buf; - uint8_t priv_key[32]; - uint8_t mac_control[32]; - hmac_sha256_context hmac_ctx; - sha256_context sha_ctx; - uint8_t flags = 0; - uint8_t hash[32]; - uint8_t signature[64]; - uint32_t be_u2f_counter; - - if(u2f_data_check(false) == false) { - U2F->ready = false; - if(U2F->callback != NULL) U2F->callback(U2fNotifyError, U2F->context); - memcpy(&buf[0], state_not_supported, 2); - return 2; - } - - if(U2F->callback != NULL) U2F->callback(U2fNotifyAuth, U2F->context); - if(U2F->user_present == true) { - flags |= 1; - } else { - if(req->p1 == U2fEnforce) { - memcpy(&buf[0], state_user_missing, 2); - return 2; - } - } - U2F->user_present = false; - - // The 4 byte counter is represented in big endian. Increment it before use - be_u2f_counter = lfs_tobe32(U2F->counter + 1); - - // Generate hash - sha256_start(&sha_ctx); - sha256_update(&sha_ctx, req->app_id, 32); - sha256_update(&sha_ctx, &flags, 1); - sha256_update(&sha_ctx, (uint8_t*)&(be_u2f_counter), 4); - sha256_update(&sha_ctx, req->challenge, 32); - sha256_finish(&sha_ctx, hash); - - // Recover private key - hmac_sha256_init(&hmac_ctx, U2F->device_key); - hmac_sha256_update(&hmac_ctx, req->app_id, 32); - hmac_sha256_update(&hmac_ctx, req->key_handle.nonce, 32); - hmac_sha256_finish(&hmac_ctx, U2F->device_key, priv_key); - - // Generate and verify private key handle - hmac_sha256_init(&hmac_ctx, U2F->device_key); - hmac_sha256_update(&hmac_ctx, priv_key, 32); - hmac_sha256_update(&hmac_ctx, req->app_id, 32); - hmac_sha256_finish(&hmac_ctx, U2F->device_key, mac_control); - - if(memcmp(req->key_handle.hash, mac_control, 32) != 0) { - FURI_LOG_W(TAG, "Wrong handle!"); - memcpy(&buf[0], state_wrong_data, 2); - return 2; - } - - if(req->p1 == U2fCheckOnly) { // Check-only: don't need to send full response - memcpy(&buf[0], state_user_missing, 2); - return 2; - } - - uECC_sign(priv_key, hash, 32, signature, U2F->p_curve); - - resp->user_present = flags; - resp->counter = be_u2f_counter; - uint8_t signature_len = u2f_der_encode_signature(resp->signature, signature); - memcpy(resp->signature + signature_len, state_no_error, 2); - - U2F->counter++; - FURI_LOG_D(TAG, "Counter: %lu", U2F->counter); - u2f_data_cnt_write(U2F->counter); - - if(U2F->callback != NULL) U2F->callback(U2fNotifyAuthSuccess, U2F->context); - - return (sizeof(U2fAuthResp) + signature_len + 2); -} - -uint16_t u2f_msg_parse(U2fData* U2F, uint8_t* buf, uint16_t len) { - furi_assert(U2F); - if(!U2F->ready) return 0; - if((buf[0] != 0x00) && (len < 5)) return 0; - if(buf[1] == U2F_CMD_REGISTER) { // Register request - return u2f_register(U2F, buf); - - } else if(buf[1] == U2F_CMD_AUTHENTICATE) { // Authenticate request - return u2f_authenticate(U2F, buf); - - } else if(buf[1] == U2F_CMD_VERSION) { // Get U2F version string - memcpy(&buf[0], ver_str, 6); - memcpy(&buf[6], state_no_error, 2); - return 8; - } else { - memcpy(&buf[0], state_not_supported, 2); - return 2; - } - return 0; -} - -void u2f_wink(U2fData* U2F) { - if(U2F->callback != NULL) U2F->callback(U2fNotifyWink, U2F->context); -} - -void u2f_set_state(U2fData* U2F, uint8_t state) { - if(state == 0) { - if(U2F->callback != NULL) U2F->callback(U2fNotifyDisconnect, U2F->context); - } else { - if(U2F->callback != NULL) U2F->callback(U2fNotifyConnect, U2F->context); - } - U2F->user_present = false; -} diff --git a/applications/unit_tests/application.fam b/applications/unit_tests/application.fam deleted file mode 100644 index 54e6feaee08..00000000000 --- a/applications/unit_tests/application.fam +++ /dev/null @@ -1,18 +0,0 @@ -App( - appid="unit_tests", - apptype=FlipperAppType.STARTUP, - entry_point="unit_tests_on_system_start", - cdefines=["APP_UNIT_TESTS"], - provides=["delay_test"], - order=100, -) - -App( - appid="delay_test", - name="Delay Test", - apptype=FlipperAppType.DEBUG, - entry_point="delay_test_app", - stack_size=1 * 1024, - requires=["unit_tests"], - order=110, -) diff --git a/applications/unit_tests/furi/furi_memmgr_test.c b/applications/unit_tests/furi/furi_memmgr_test.c deleted file mode 100644 index b0fd060cf74..00000000000 --- a/applications/unit_tests/furi/furi_memmgr_test.c +++ /dev/null @@ -1,100 +0,0 @@ -#include "../minunit.h" -#include -#include -#include - -// this test is not accurate, but gives a basic understanding -// that memory management is working fine - -// do not include memmgr.h here -// we also test that we are linking against stdlib -extern size_t memmgr_get_free_heap(void); -extern size_t memmgr_get_minimum_free_heap(void); - -// current heap managment realization consume: -// X bytes after allocate and 0 bytes after allocate and free, -// where X = sizeof(void*) + sizeof(size_t), look to BlockLink_t -const size_t heap_overhead_max_size = sizeof(void*) + sizeof(size_t); - -bool heap_equal(size_t heap_size, size_t heap_size_old) { - // heap borders with overhead - const size_t heap_low = heap_size_old - heap_overhead_max_size; - const size_t heap_high = heap_size_old + heap_overhead_max_size; - - // not extact, so we must test it against bigger numbers than "overhead size" - const bool result = ((heap_size >= heap_low) && (heap_size <= heap_high)); - - // debug allocation info - if(!result) { - printf("\n(hl: %zu) <= (p: %zu) <= (hh: %zu)\n", heap_low, heap_size, heap_high); - } - - return result; -} - -void test_furi_memmgr() { - size_t heap_size = 0; - size_t heap_size_old = 0; - const int alloc_size = 128; - - void* ptr = NULL; - void* original_ptr = NULL; - - // do not include furi memmgr.h case -#ifdef FURI_MEMMGR_GUARD - mu_fail("do not link against furi memmgr.h"); -#endif - - // allocate memory case - heap_size_old = memmgr_get_free_heap(); - ptr = malloc(alloc_size); - heap_size = memmgr_get_free_heap(); - mu_assert_pointers_not_eq(ptr, NULL); - mu_assert(heap_equal(heap_size, heap_size_old - alloc_size), "allocate failed"); - - // free memory case - heap_size_old = memmgr_get_free_heap(); - free(ptr); - ptr = NULL; - heap_size = memmgr_get_free_heap(); - mu_assert(heap_equal(heap_size, heap_size_old + alloc_size), "free failed"); - - // reallocate memory case - - // get filled array with some data - original_ptr = malloc(alloc_size); - mu_assert_pointers_not_eq(original_ptr, NULL); - for(int i = 0; i < alloc_size; i++) { - *(unsigned char*)(original_ptr + i) = i; - } - - // malloc array and copy data - ptr = malloc(alloc_size); - mu_assert_pointers_not_eq(ptr, NULL); - memcpy(ptr, original_ptr, alloc_size); - - // reallocate array - heap_size_old = memmgr_get_free_heap(); - ptr = realloc(ptr, alloc_size * 2); - heap_size = memmgr_get_free_heap(); - mu_assert(heap_equal(heap_size, heap_size_old - alloc_size), "reallocate failed"); - mu_assert_int_eq(memcmp(original_ptr, ptr, alloc_size), 0); - free(original_ptr); - free(ptr); - - // allocate and zero-initialize array (calloc) - original_ptr = malloc(alloc_size); - mu_assert_pointers_not_eq(original_ptr, NULL); - - for(int i = 0; i < alloc_size; i++) { - *(unsigned char*)(original_ptr + i) = 0; - } - heap_size_old = memmgr_get_free_heap(); - ptr = calloc(1, alloc_size); - heap_size = memmgr_get_free_heap(); - mu_assert(heap_equal(heap_size, heap_size_old - alloc_size), "callocate failed"); - mu_assert_int_eq(memcmp(original_ptr, ptr, alloc_size), 0); - - free(original_ptr); - free(ptr); -} diff --git a/applications/unit_tests/furi/furi_record_test.c b/applications/unit_tests/furi/furi_record_test.c deleted file mode 100644 index 512ddfdc4a2..00000000000 --- a/applications/unit_tests/furi/furi_record_test.c +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include -#include -#include "../minunit.h" - -void test_furi_create_open() { - // 1. Create record - uint8_t test_data = 0; - furi_record_create("test/holding", (void*)&test_data); - - // 2. Open it - void* record = furi_record_open("test/holding"); - mu_assert_pointers_eq(record, &test_data); - - // 3. Close it - furi_record_close("test/holding"); - - // 4. Clean up - furi_record_destroy("test/holding"); -} diff --git a/applications/unit_tests/furi/furi_valuemutex_test.c b/applications/unit_tests/furi/furi_valuemutex_test.c deleted file mode 100644 index 02fd47eeb31..00000000000 --- a/applications/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/unit_tests/nfc/nfc_test.c b/applications/unit_tests/nfc/nfc_test.c deleted file mode 100644 index dcd162d139a..00000000000 --- a/applications/unit_tests/nfc/nfc_test.c +++ /dev/null @@ -1,184 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "../minunit.h" - -#define TAG "NfcTest" - -#define NFC_TEST_RESOURCES_DIR EXT_PATH("unit_tests/nfc/") -#define NFC_TEST_SIGNAL_SHORT_FILE "nfc_nfca_signal_short.nfc" -#define NFC_TEST_SIGNAL_LONG_FILE "nfc_nfca_signal_long.nfc" - -static const char* nfc_test_file_type = "Flipper NFC test"; -static const uint32_t nfc_test_file_version = 1; - -#define NFC_TEST_DATA_MAX_LEN 18 -#define NFC_TETS_TIMINGS_MAX_LEN 1350 - -typedef struct { - Storage* storage; - NfcaSignal* signal; - uint32_t test_data_len; - uint8_t test_data[NFC_TEST_DATA_MAX_LEN]; - uint32_t test_timings_len; - uint32_t test_timings[NFC_TETS_TIMINGS_MAX_LEN]; -} NfcTest; - -static NfcTest* nfc_test = NULL; - -static void nfc_test_alloc() { - nfc_test = malloc(sizeof(NfcTest)); - nfc_test->signal = nfca_signal_alloc(); - nfc_test->storage = furi_record_open(RECORD_STORAGE); -} - -static void nfc_test_free() { - furi_assert(nfc_test); - - furi_record_close(RECORD_STORAGE); - nfca_signal_free(nfc_test->signal); - free(nfc_test); - nfc_test = NULL; -} - -static bool nfc_test_read_signal_from_file(const char* file_name) { - bool success = false; - - FlipperFormat* file = flipper_format_file_alloc(nfc_test->storage); - string_t file_type; - string_init(file_type); - uint32_t file_version = 0; - - do { - if(!flipper_format_file_open_existing(file, file_name)) break; - if(!flipper_format_read_header(file, file_type, &file_version)) break; - if(string_cmp_str(file_type, nfc_test_file_type) || file_version != nfc_test_file_version) - break; - if(!flipper_format_read_uint32(file, "Data length", &nfc_test->test_data_len, 1)) break; - if(nfc_test->test_data_len > NFC_TEST_DATA_MAX_LEN) break; - if(!flipper_format_read_hex( - file, "Plain data", nfc_test->test_data, nfc_test->test_data_len)) - break; - if(!flipper_format_read_uint32(file, "Timings length", &nfc_test->test_timings_len, 1)) - break; - if(nfc_test->test_timings_len > NFC_TETS_TIMINGS_MAX_LEN) break; - if(!flipper_format_read_uint32( - file, "Timings", nfc_test->test_timings, nfc_test->test_timings_len)) - break; - success = true; - } while(false); - - string_clear(file_type); - flipper_format_free(file); - - return success; -} - -static bool nfc_test_digital_signal_test_encode( - const char* file_name, - uint32_t encode_max_time, - uint32_t timing_tolerance, - uint32_t timings_sum_tolerance) { - furi_assert(nfc_test); - - bool success = false; - uint32_t time = 0; - uint32_t dut_timings_sum = 0; - uint32_t ref_timings_sum = 0; - uint8_t parity[10] = {}; - - do { - // Read test data - if(!nfc_test_read_signal_from_file(file_name)) break; - - // Encode signal - FURI_CRITICAL_ENTER(); - time = DWT->CYCCNT; - nfca_signal_encode( - nfc_test->signal, nfc_test->test_data, nfc_test->test_data_len * 8, parity); - digital_signal_prepare_arr(nfc_test->signal->tx_signal); - time = (DWT->CYCCNT - time) / furi_hal_cortex_instructions_per_microsecond(); - FURI_CRITICAL_EXIT(); - - // Check timings - if(time > encode_max_time) { - FURI_LOG_E( - TAG, "Encoding time: %d us while accepted value: %d us", time, encode_max_time); - break; - } - - // Check data - if(nfc_test->signal->tx_signal->edge_cnt != nfc_test->test_timings_len) { - FURI_LOG_E(TAG, "Not equal timings buffers length"); - break; - } - - uint32_t timings_diff = 0; - uint32_t* ref = nfc_test->test_timings; - uint32_t* dut = nfc_test->signal->tx_signal->reload_reg_buff; - bool timing_check_success = true; - for(size_t i = 0; i < nfc_test->test_timings_len; i++) { - timings_diff = dut[i] > ref[i] ? dut[i] - ref[i] : ref[i] - dut[i]; - dut_timings_sum += dut[i]; - ref_timings_sum += ref[i]; - if(timings_diff > timing_tolerance) { - FURI_LOG_E( - TAG, "Too big differece in %d timings. Ref: %d, DUT: %d", i, ref[i], dut[i]); - timing_check_success = false; - break; - } - } - if(!timing_check_success) break; - uint32_t sum_diff = dut_timings_sum > ref_timings_sum ? dut_timings_sum - ref_timings_sum : - ref_timings_sum - dut_timings_sum; - if(sum_diff > timings_sum_tolerance) { - FURI_LOG_E( - TAG, - "Too big difference in timings sum. Ref: %d, DUT: %d", - ref_timings_sum, - dut_timings_sum); - break; - } - - FURI_LOG_I(TAG, "Encoding time: %d us. Acceptable time: %d us", time, encode_max_time); - FURI_LOG_I( - TAG, - "Timings sum difference: %d [1/64MHZ]. Acceptable difference: %d [1/64MHz]", - sum_diff, - timings_sum_tolerance); - success = true; - } while(false); - - return success; -} - -MU_TEST(nfc_digital_signal_test) { - mu_assert( - nfc_test_digital_signal_test_encode( - NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_SHORT_FILE, 500, 1, 37), - "NFC short digital signal test failed\r\n"); - mu_assert( - nfc_test_digital_signal_test_encode( - NFC_TEST_RESOURCES_DIR NFC_TEST_SIGNAL_LONG_FILE, 2000, 1, 37), - "NFC long digital signal test failed\r\n"); -} - -MU_TEST_SUITE(nfc) { - nfc_test_alloc(); - - MU_RUN_TEST(nfc_digital_signal_test); - - nfc_test_free(); -} - -int run_minunit_test_nfc() { - MU_RUN_SUITE(nfc); - return MU_EXIT_CODE; -} diff --git a/applications/unit_tests/storage/storage_test.c b/applications/unit_tests/storage/storage_test.c deleted file mode 100644 index c3628a4f9da..00000000000 --- a/applications/unit_tests/storage/storage_test.c +++ /dev/null @@ -1,317 +0,0 @@ -#include "../minunit.h" -#include -#include - -#define STORAGE_LOCKED_FILE EXT_PATH("locked_file.test") -#define STORAGE_LOCKED_DIR STORAGE_INT_PATH_PREFIX - -static void storage_file_open_lock_setup() { - Storage* storage = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(storage); - storage_simply_remove(storage, STORAGE_LOCKED_FILE); - mu_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_WRITE, FSOM_CREATE_NEW)); - mu_check(storage_file_write(file, "0123", 4) == 4); - mu_check(storage_file_close(file)); - storage_file_free(file); - furi_record_close(RECORD_STORAGE); -} - -static void storage_file_open_lock_teardown() { - Storage* storage = furi_record_open(RECORD_STORAGE); - mu_check(storage_simply_remove(storage, STORAGE_LOCKED_FILE)); - furi_record_close(RECORD_STORAGE); -} - -static int32_t storage_file_locker(void* ctx) { - Storage* storage = furi_record_open(RECORD_STORAGE); - FuriSemaphore* semaphore = ctx; - File* file = storage_file_alloc(storage); - furi_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)); - furi_semaphore_release(semaphore); - furi_delay_ms(1000); - - furi_check(storage_file_close(file)); - furi_record_close(RECORD_STORAGE); - storage_file_free(file); - return 0; -} - -MU_TEST(storage_file_open_lock) { - Storage* storage = furi_record_open(RECORD_STORAGE); - bool result = false; - FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0); - File* file = storage_file_alloc(storage); - - // file_locker thread start - FuriThread* locker_thread = furi_thread_alloc(); - furi_thread_set_name(locker_thread, "StorageFileLocker"); - furi_thread_set_stack_size(locker_thread, 2048); - furi_thread_set_context(locker_thread, semaphore); - furi_thread_set_callback(locker_thread, storage_file_locker); - furi_thread_start(locker_thread); - - // wait for file lock - furi_semaphore_acquire(semaphore, FuriWaitForever); - furi_semaphore_free(semaphore); - - result = storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING); - storage_file_close(file); - - // file_locker thread stop - mu_check(furi_thread_join(locker_thread) == FuriStatusOk); - furi_thread_free(locker_thread); - - // clean data - storage_file_free(file); - furi_record_close(RECORD_STORAGE); - - mu_assert(result, "cannot open locked file"); -} - -MU_TEST(storage_file_open_close) { - Storage* storage = furi_record_open(RECORD_STORAGE); - File* file; - - file = storage_file_alloc(storage); - mu_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)); - storage_file_close(file); - storage_file_free(file); - - for(size_t i = 0; i < 10; i++) { - file = storage_file_alloc(storage); - mu_check( - storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)); - storage_file_free(file); - } - - furi_record_close(RECORD_STORAGE); -} - -MU_TEST_SUITE(storage_file) { - storage_file_open_lock_setup(); - MU_RUN_TEST(storage_file_open_close); - MU_RUN_TEST(storage_file_open_lock); - storage_file_open_lock_teardown(); -} - -MU_TEST(storage_dir_open_close) { - Storage* storage = furi_record_open(RECORD_STORAGE); - File* file; - - file = storage_file_alloc(storage); - mu_check(storage_dir_open(file, STORAGE_LOCKED_DIR)); - storage_dir_close(file); - storage_file_free(file); - - for(size_t i = 0; i < 10; i++) { - file = storage_file_alloc(storage); - mu_check(storage_dir_open(file, STORAGE_LOCKED_DIR)); - storage_file_free(file); - } - - furi_record_close(RECORD_STORAGE); -} - -static int32_t storage_dir_locker(void* ctx) { - Storage* storage = furi_record_open(RECORD_STORAGE); - FuriSemaphore* semaphore = 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_check(storage_dir_close(file)); - furi_record_close(RECORD_STORAGE); - storage_file_free(file); - return 0; -} - -MU_TEST(storage_dir_open_lock) { - Storage* storage = furi_record_open(RECORD_STORAGE); - bool result = false; - FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0); - File* file = storage_file_alloc(storage); - - // file_locker thread start - FuriThread* locker_thread = furi_thread_alloc(); - furi_thread_set_name(locker_thread, "StorageDirLocker"); - furi_thread_set_stack_size(locker_thread, 2048); - furi_thread_set_context(locker_thread, semaphore); - furi_thread_set_callback(locker_thread, storage_dir_locker); - furi_thread_start(locker_thread); - - // wait for dir lock - furi_semaphore_acquire(semaphore, FuriWaitForever); - furi_semaphore_free(semaphore); - - result = storage_dir_open(file, STORAGE_LOCKED_DIR); - storage_dir_close(file); - - // file_locker thread stop - mu_check(furi_thread_join(locker_thread) == FuriStatusOk); - furi_thread_free(locker_thread); - - // clean data - storage_file_free(file); - furi_record_close(RECORD_STORAGE); - - mu_assert(result, "cannot open locked dir"); -} - -MU_TEST_SUITE(storage_dir) { - MU_RUN_TEST(storage_dir_open_close); - MU_RUN_TEST(storage_dir_open_lock); -} - -static const char* const storage_copy_test_paths[] = { - "1", - "11", - "111", - "1/2", - "1/22", - "1/222", - "11/1", - "111/2", - "111/22", - "111/22/33", -}; - -static const char* const storage_copy_test_files[] = { - "file.test", - "1/file.test", - "111/22/33/file.test", -}; - -static bool write_file_13DA(Storage* storage, const char* path) { - File* file = storage_file_alloc(storage); - bool result = false; - if(storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { - result = storage_file_write(file, "13DA", 4) == 4; - } - storage_file_close(file); - storage_file_free(file); - - return result; -} - -static bool check_file_13DA(Storage* storage, const char* path) { - File* file = storage_file_alloc(storage); - bool result = false; - if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) { - char data[10] = {0}; - result = storage_file_read(file, data, 4) == 4; - if(result) { - result = memcmp(data, "13DA", 4) == 0; - } - } - storage_file_close(file); - storage_file_free(file); - - return result; -} - -static void storage_dir_create(Storage* storage, const char* base) { - string_t path; - string_init(path); - - storage_common_mkdir(storage, base); - - for(size_t i = 0; i < COUNT_OF(storage_copy_test_paths); i++) { - string_printf(path, "%s/%s", base, storage_copy_test_paths[i]); - storage_common_mkdir(storage, string_get_cstr(path)); - } - - for(size_t i = 0; i < COUNT_OF(storage_copy_test_files); i++) { - string_printf(path, "%s/%s", base, storage_copy_test_files[i]); - write_file_13DA(storage, string_get_cstr(path)); - } - - string_clear(path); -} - -static void storage_dir_remove(Storage* storage, const char* base) { - storage_simply_remove_recursive(storage, base); -} - -static bool storage_dir_rename_check(Storage* storage, const char* base) { - bool result = false; - string_t path; - string_init(path); - - result = (storage_common_stat(storage, base, NULL) == FSE_OK); - - if(result) { - for(size_t i = 0; i < COUNT_OF(storage_copy_test_paths); i++) { - string_printf(path, "%s/%s", base, storage_copy_test_paths[i]); - result = (storage_common_stat(storage, string_get_cstr(path), NULL) == FSE_OK); - if(!result) { - break; - } - } - } - - if(result) { - for(size_t i = 0; i < COUNT_OF(storage_copy_test_files); i++) { - string_printf(path, "%s/%s", base, storage_copy_test_files[i]); - result = check_file_13DA(storage, string_get_cstr(path)); - if(!result) { - break; - } - } - } - - string_clear(path); - return result; -} - -MU_TEST(storage_file_rename) { - Storage* storage = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(storage); - - mu_check(write_file_13DA(storage, EXT_PATH("file.old"))); - mu_check(check_file_13DA(storage, EXT_PATH("file.old"))); - mu_assert_int_eq( - FSE_OK, storage_common_rename(storage, EXT_PATH("file.old"), EXT_PATH("file.new"))); - mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("file.old"), NULL)); - mu_assert_int_eq(FSE_OK, storage_common_stat(storage, EXT_PATH("file.new"), NULL)); - mu_check(check_file_13DA(storage, EXT_PATH("file.new"))); - mu_assert_int_eq(FSE_OK, storage_common_remove(storage, EXT_PATH("file.new"))); - - storage_file_free(file); - furi_record_close(RECORD_STORAGE); -} - -MU_TEST(storage_dir_rename) { - Storage* storage = furi_record_open(RECORD_STORAGE); - - storage_dir_create(storage, EXT_PATH("dir.old")); - - mu_check(storage_dir_rename_check(storage, EXT_PATH("dir.old"))); - - mu_assert_int_eq( - FSE_OK, storage_common_rename(storage, EXT_PATH("dir.old"), EXT_PATH("dir.new"))); - mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("dir.old"), NULL)); - mu_check(storage_dir_rename_check(storage, EXT_PATH("dir.new"))); - - storage_dir_remove(storage, EXT_PATH("dir.new")); - mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, EXT_PATH("dir.new"), NULL)); - - furi_record_close(RECORD_STORAGE); -} - -MU_TEST_SUITE(storage_rename) { - MU_RUN_TEST(storage_file_rename); - MU_RUN_TEST(storage_dir_rename); - - Storage* storage = furi_record_open(RECORD_STORAGE); - storage_dir_remove(storage, EXT_PATH("dir.old")); - storage_dir_remove(storage, EXT_PATH("dir.new")); - furi_record_close(RECORD_STORAGE); -} - -int run_minunit_test_storage() { - MU_RUN_SUITE(storage_file); - MU_RUN_SUITE(storage_dir); - MU_RUN_SUITE(storage_rename); - return MU_EXIT_CODE; -} diff --git a/applications/unit_tests/subghz/subghz_test.c b/applications/unit_tests/subghz/subghz_test.c deleted file mode 100644 index f91d27234a9..00000000000 --- a/applications/unit_tests/subghz/subghz_test.c +++ /dev/null @@ -1,581 +0,0 @@ -#include -#include -#include "../minunit.h" -#include -#include -#include -#include -#include -#include - -#define TAG "SubGhz TEST" -#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 TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") -#define TEST_RANDOM_COUNT_PARSE 188 -#define TEST_TIMEOUT 10000 - -static SubGhzEnvironment* environment_handler; -static SubGhzReceiver* receiver_handler; -//static SubGhzTransmitter* transmitter_handler; -static SubGhzFileEncoderWorker* file_worker_encoder_handler; -static uint16_t subghz_test_decoder_count = 0; - -static void subghz_test_rx_callback( - SubGhzReceiver* receiver, - SubGhzProtocolDecoderBase* decoder_base, - void* context) { - UNUSED(receiver); - UNUSED(context); - string_t text; - string_init(text); - subghz_protocol_decoder_base_get_string(decoder_base, text); - subghz_receiver_reset(receiver_handler); - FURI_LOG_T(TAG, "\r\n%s", string_get_cstr(text)); - string_clear(text); - subghz_test_decoder_count++; -} - -static void subghz_test_init(void) { - environment_handler = subghz_environment_alloc(); - subghz_environment_set_came_atomo_rainbow_table_file_name( - environment_handler, CAME_ATOMO_DIR_NAME); - subghz_environment_set_nice_flor_s_rainbow_table_file_name( - environment_handler, NICE_FLOR_S_DIR_NAME); - - receiver_handler = subghz_receiver_alloc_init(environment_handler); - subghz_receiver_set_filter(receiver_handler, SubGhzProtocolFlag_Decodable); - subghz_receiver_set_rx_callback(receiver_handler, subghz_test_rx_callback, NULL); -} - -static void subghz_test_deinit(void) { - subghz_receiver_free(receiver_handler); - subghz_environment_free(environment_handler); -} - -static bool subghz_decoder_test(const char* path, const char* name_decoder) { - subghz_test_decoder_count = 0; - uint32_t test_start = furi_get_tick(); - - SubGhzProtocolDecoderBase* decoder = - subghz_receiver_search_decoder_base_by_name(receiver_handler, name_decoder); - - if(decoder) { - file_worker_encoder_handler = subghz_file_encoder_worker_alloc(); - if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path)) { - // the worker needs a file in order to open and read part of the file - furi_delay_ms(100); - - LevelDuration level_duration; - while(furi_get_tick() - test_start < TEST_TIMEOUT) { - level_duration = - subghz_file_encoder_worker_get_level_duration(file_worker_encoder_handler); - if(!level_duration_is_reset(level_duration)) { - bool level = level_duration_get_level(level_duration); - uint32_t duration = level_duration_get_duration(level_duration); - // Yield, to load data inside the worker - furi_thread_yield(); - decoder->protocol->decoder->feed(decoder, level, duration); - } else { - break; - } - } - furi_delay_ms(10); - } - if(subghz_file_encoder_worker_is_running(file_worker_encoder_handler)) { - subghz_file_encoder_worker_stop(file_worker_encoder_handler); - } - subghz_file_encoder_worker_free(file_worker_encoder_handler); - } - FURI_LOG_T(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count); - if(furi_get_tick() - test_start > TEST_TIMEOUT) { - printf("\033[0;31mTest decoder %s ERROR TimeOut\033[0m\r\n", name_decoder); - return false; - } else { - return subghz_test_decoder_count ? true : false; - } -} - -static bool subghz_decode_random_test(const char* path) { - subghz_test_decoder_count = 0; - subghz_receiver_reset(receiver_handler); - uint32_t test_start = furi_get_tick(); - - file_worker_encoder_handler = subghz_file_encoder_worker_alloc(); - if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path)) { - // the worker needs a file in order to open and read part of the file - furi_delay_ms(100); - - LevelDuration level_duration; - while(furi_get_tick() - test_start < TEST_TIMEOUT * 10) { - level_duration = - subghz_file_encoder_worker_get_level_duration(file_worker_encoder_handler); - if(!level_duration_is_reset(level_duration)) { - bool level = level_duration_get_level(level_duration); - uint32_t duration = level_duration_get_duration(level_duration); - // Yield, to load data inside the worker - furi_thread_yield(); - subghz_receiver_decode(receiver_handler, level, duration); - } else { - break; - } - } - furi_delay_ms(10); - if(subghz_file_encoder_worker_is_running(file_worker_encoder_handler)) { - subghz_file_encoder_worker_stop(file_worker_encoder_handler); - } - subghz_file_encoder_worker_free(file_worker_encoder_handler); - } - FURI_LOG_T(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count); - if(furi_get_tick() - test_start > TEST_TIMEOUT * 10) { - printf("\033[0;31mRandom test ERROR TimeOut\033[0m\r\n"); - return false; - } else if(subghz_test_decoder_count == TEST_RANDOM_COUNT_PARSE) { - return true; - } else { - return false; - } -} - -static bool subghz_encoder_test(const char* path) { - subghz_test_decoder_count = 0; - uint32_t test_start = furi_get_tick(); - string_t temp_str; - string_init(temp_str); - bool file_load = false; - - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - - do { - if(!flipper_format_file_open_existing(fff_data_file, path)) { - FURI_LOG_E(TAG, "Error open file %s", path); - break; - } - - if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { - FURI_LOG_E(TAG, "Missing Preset"); - break; - } - - if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { - FURI_LOG_E(TAG, "Missing Protocol"); - break; - } - file_load = true; - } while(false); - if(file_load) { - SubGhzTransmitter* transmitter = - subghz_transmitter_alloc_init(environment_handler, string_get_cstr(temp_str)); - subghz_transmitter_deserialize(transmitter, fff_data_file); - - SubGhzProtocolDecoderBase* decoder = subghz_receiver_search_decoder_base_by_name( - receiver_handler, string_get_cstr(temp_str)); - - if(decoder) { - LevelDuration level_duration; - while(furi_get_tick() - test_start < TEST_TIMEOUT) { - level_duration = subghz_transmitter_yield(transmitter); - if(!level_duration_is_reset(level_duration)) { - bool level = level_duration_get_level(level_duration); - uint32_t duration = level_duration_get_duration(level_duration); - decoder->protocol->decoder->feed(decoder, level, duration); - } else { - break; - } - } - furi_delay_ms(10); - } - subghz_transmitter_free(transmitter); - } - flipper_format_free(fff_data_file); - FURI_LOG_T(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count); - if(furi_get_tick() - test_start > TEST_TIMEOUT) { - printf("\033[0;31mTest encoder %s ERROR TimeOut\033[0m\r\n", string_get_cstr(temp_str)); - subghz_test_decoder_count = 0; - } - string_clear(temp_str); - - return subghz_test_decoder_count ? true : false; -} - -MU_TEST(subghz_keystore_test) { - mu_assert( - subghz_environment_load_keystore(environment_handler, KEYSTORE_DIR_NAME), - "Test keystore error"); -} - -//test decoders -MU_TEST(subghz_decoder_came_atomo_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/came_atomo_raw.sub"), SUBGHZ_PROTOCOL_CAME_ATOMO_NAME), - "Test decoder " SUBGHZ_PROTOCOL_CAME_ATOMO_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_came_test) { - mu_assert( - subghz_decoder_test(EXT_PATH("unit_tests/subghz/came_raw.sub"), SUBGHZ_PROTOCOL_CAME_NAME), - "Test decoder " SUBGHZ_PROTOCOL_CAME_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_came_twee_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/came_twee_raw.sub"), SUBGHZ_PROTOCOL_CAME_TWEE_NAME), - "Test decoder " SUBGHZ_PROTOCOL_CAME_TWEE_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_faac_slh_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/faac_slh_raw.sub"), SUBGHZ_PROTOCOL_FAAC_SLH_NAME), - "Test decoder " SUBGHZ_PROTOCOL_FAAC_SLH_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_gate_tx_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/gate_tx_raw.sub"), SUBGHZ_PROTOCOL_GATE_TX_NAME), - "Test decoder " SUBGHZ_PROTOCOL_GATE_TX_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_hormann_hsm_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/hormann_hsm_raw.sub"), SUBGHZ_PROTOCOL_HORMANN_HSM_NAME), - "Test decoder " SUBGHZ_PROTOCOL_HORMANN_HSM_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_ido_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/ido_117_111_raw.sub"), SUBGHZ_PROTOCOL_IDO_NAME), - "Test decoder " SUBGHZ_PROTOCOL_IDO_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_keelog_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/doorhan_raw.sub"), SUBGHZ_PROTOCOL_KEELOQ_NAME), - "Test decoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_kia_seed_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/kia_seed_raw.sub"), SUBGHZ_PROTOCOL_KIA_NAME), - "Test decoder " SUBGHZ_PROTOCOL_KIA_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_nero_radio_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/nero_radio_raw.sub"), SUBGHZ_PROTOCOL_NERO_RADIO_NAME), - "Test decoder " SUBGHZ_PROTOCOL_NERO_RADIO_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_nero_sketch_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/nero_sketch_raw.sub"), SUBGHZ_PROTOCOL_NERO_SKETCH_NAME), - "Test decoder " SUBGHZ_PROTOCOL_NERO_SKETCH_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_nice_flo_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/nice_flo_raw.sub"), SUBGHZ_PROTOCOL_NICE_FLO_NAME), - "Test decoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_nice_flor_s_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/nice_flor_s_raw.sub"), SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME), - "Test decoder " SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_princeton_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/Princeton_raw.sub"), SUBGHZ_PROTOCOL_PRINCETON_NAME), - "Test decoder " SUBGHZ_PROTOCOL_PRINCETON_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_scher_khan_magic_code_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/scher_khan_magic_code.sub"), - SUBGHZ_PROTOCOL_SCHER_KHAN_NAME), - "Test decoder " SUBGHZ_PROTOCOL_SCHER_KHAN_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_somfy_keytis_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/Somfy_keytis_raw.sub"), SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME), - "Test decoder " SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_somfy_telis_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/somfy_telis_raw.sub"), SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME), - "Test decoder " SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_star_line_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/cenmax_raw.sub"), SUBGHZ_PROTOCOL_STAR_LINE_NAME), - "Test decoder " SUBGHZ_PROTOCOL_STAR_LINE_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_linear_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/linear_raw.sub"), SUBGHZ_PROTOCOL_LINEAR_NAME), - "Test decoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_megacode_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/megacode_raw.sub"), SUBGHZ_PROTOCOL_MEGACODE_NAME), - "Test decoder " SUBGHZ_PROTOCOL_MEGACODE_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_secplus_v1_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/security_pls_1_0_raw.sub"), - SUBGHZ_PROTOCOL_SECPLUS_V1_NAME), - "Test decoder " SUBGHZ_PROTOCOL_SECPLUS_V1_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_secplus_v2_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/security_pls_2_0_raw.sub"), - SUBGHZ_PROTOCOL_SECPLUS_V2_NAME), - "Test decoder " SUBGHZ_PROTOCOL_SECPLUS_V2_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_holtek_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/holtek_raw.sub"), SUBGHZ_PROTOCOL_HOLTEK_NAME), - "Test decoder " SUBGHZ_PROTOCOL_HOLTEK_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_power_smart_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/power_smart_raw.sub"), SUBGHZ_PROTOCOL_POWER_SMART_NAME), - "Test decoder " SUBGHZ_PROTOCOL_POWER_SMART_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_marantec_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/marantec_raw.sub"), SUBGHZ_PROTOCOL_MARANTEC_NAME), - "Test decoder " SUBGHZ_PROTOCOL_MARANTEC_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_bett_test) { - mu_assert( - subghz_decoder_test(EXT_PATH("unit_tests/subghz/bett_raw.sub"), SUBGHZ_PROTOCOL_BETT_NAME), - "Test decoder " SUBGHZ_PROTOCOL_BETT_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_doitrand_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/doitrand_raw.sub"), SUBGHZ_PROTOCOL_DOITRAND_NAME), - "Test decoder " SUBGHZ_PROTOCOL_DOITRAND_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_phoenix_v2_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/phoenix_v2_raw.sub"), SUBGHZ_PROTOCOL_PHOENIX_V2_NAME), - "Test decoder " SUBGHZ_PROTOCOL_PHOENIX_V2_NAME " error\r\n"); -} - -MU_TEST(subghz_decoder_honeywell_wdb_test) { - mu_assert( - subghz_decoder_test( - EXT_PATH("unit_tests/subghz/honeywell_wdb_raw.sub"), - SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME), - "Test decoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n"); -} - -//test encoders -MU_TEST(subghz_encoder_princeton_test) { - mu_assert( - subghz_encoder_test(EXT_PATH("unit_tests/subghz/princeton.sub")), - "Test encoder " SUBGHZ_PROTOCOL_PRINCETON_NAME " error\r\n"); -} - -MU_TEST(subghz_encoder_came_test) { - mu_assert( - subghz_encoder_test(EXT_PATH("unit_tests/subghz/came.sub")), - "Test encoder " SUBGHZ_PROTOCOL_CAME_NAME " error\r\n"); -} - -MU_TEST(subghz_encoder_came_twee_test) { - mu_assert( - subghz_encoder_test(EXT_PATH("unit_tests/subghz/came_twee.sub")), - "Test encoder " SUBGHZ_PROTOCOL_CAME_TWEE_NAME " error\r\n"); -} - -MU_TEST(subghz_encoder_gate_tx_test) { - mu_assert( - subghz_encoder_test(EXT_PATH("unit_tests/subghz/gate_tx.sub")), - "Test encoder " SUBGHZ_PROTOCOL_GATE_TX_NAME " error\r\n"); -} - -MU_TEST(subghz_encoder_nice_flo_test) { - mu_assert( - subghz_encoder_test(EXT_PATH("unit_tests/subghz/nice_flo.sub")), - "Test encoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n"); -} - -MU_TEST(subghz_encoder_keelog_test) { - mu_assert( - subghz_encoder_test(EXT_PATH("unit_tests/subghz/doorhan.sub")), - "Test encoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n"); -} - -MU_TEST(subghz_encoder_linear_test) { - mu_assert( - subghz_encoder_test(EXT_PATH("unit_tests/subghz/linear.sub")), - "Test encoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n"); -} - -MU_TEST(subghz_encoder_megacode_test) { - mu_assert( - subghz_encoder_test(EXT_PATH("unit_tests/subghz/megacode.sub")), - "Test encoder " SUBGHZ_PROTOCOL_MEGACODE_NAME " error\r\n"); -} - -MU_TEST(subghz_encoder_holtek_test) { - mu_assert( - subghz_encoder_test(EXT_PATH("unit_tests/subghz/holtek.sub")), - "Test encoder " SUBGHZ_PROTOCOL_HOLTEK_NAME " error\r\n"); -} - -MU_TEST(subghz_encoder_secplus_v1_test) { - mu_assert( - subghz_encoder_test(EXT_PATH("unit_tests/subghz/security_pls_1_0.sub")), - "Test encoder " SUBGHZ_PROTOCOL_SECPLUS_V1_NAME " error\r\n"); -} - -MU_TEST(subghz_encoder_secplus_v2_test) { - mu_assert( - subghz_encoder_test(EXT_PATH("unit_tests/subghz/security_pls_2_0.sub")), - "Test encoder " SUBGHZ_PROTOCOL_SECPLUS_V2_NAME " error\r\n"); -} - -MU_TEST(subghz_encoder_power_smart_test) { - mu_assert( - subghz_encoder_test(EXT_PATH("unit_tests/subghz/power_smart.sub")), - "Test encoder " SUBGHZ_PROTOCOL_POWER_SMART_NAME " error\r\n"); -} - -MU_TEST(subghz_encoder_marantec_test) { - mu_assert( - subghz_encoder_test(EXT_PATH("unit_tests/subghz/marantec.sub")), - "Test encoder " SUBGHZ_PROTOCOL_MARANTEC_NAME " error\r\n"); -} - -MU_TEST(subghz_encoder_bett_test) { - mu_assert( - subghz_encoder_test(EXT_PATH("unit_tests/subghz/bett.sub")), - "Test encoder " SUBGHZ_PROTOCOL_BETT_NAME " error\r\n"); -} - -MU_TEST(subghz_encoder_doitrand_test) { - mu_assert( - subghz_encoder_test(EXT_PATH("unit_tests/subghz/doitrand.sub")), - "Test encoder " SUBGHZ_PROTOCOL_DOITRAND_NAME " error\r\n"); -} - -MU_TEST(subghz_encoder_phoenix_v2_test) { - mu_assert( - subghz_encoder_test(EXT_PATH("unit_tests/subghz/phoenix_v2.sub")), - "Test encoder " SUBGHZ_PROTOCOL_PHOENIX_V2_NAME " error\r\n"); -} - -MU_TEST(subghz_encoder_honeywell_wdb_test) { - mu_assert( - subghz_encoder_test(EXT_PATH("unit_tests/subghz/honeywell_wdb.sub")), - "Test encoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n"); -} - -MU_TEST(subghz_random_test) { - mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); -} - -MU_TEST_SUITE(subghz) { - subghz_test_init(); - MU_RUN_TEST(subghz_keystore_test); - - MU_RUN_TEST(subghz_decoder_came_atomo_test); - MU_RUN_TEST(subghz_decoder_came_test); - MU_RUN_TEST(subghz_decoder_came_twee_test); - MU_RUN_TEST(subghz_decoder_faac_slh_test); - MU_RUN_TEST(subghz_decoder_gate_tx_test); - MU_RUN_TEST(subghz_decoder_hormann_hsm_test); - MU_RUN_TEST(subghz_decoder_ido_test); - MU_RUN_TEST(subghz_decoder_keelog_test); - MU_RUN_TEST(subghz_decoder_kia_seed_test); - MU_RUN_TEST(subghz_decoder_nero_radio_test); - MU_RUN_TEST(subghz_decoder_nero_sketch_test); - MU_RUN_TEST(subghz_decoder_nice_flo_test); - MU_RUN_TEST(subghz_decoder_nice_flor_s_test); - MU_RUN_TEST(subghz_decoder_princeton_test); - MU_RUN_TEST(subghz_decoder_scher_khan_magic_code_test); - MU_RUN_TEST(subghz_decoder_somfy_keytis_test); - 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_megacode_test); - MU_RUN_TEST(subghz_decoder_secplus_v1_test); - MU_RUN_TEST(subghz_decoder_secplus_v2_test); - MU_RUN_TEST(subghz_decoder_holtek_test); - MU_RUN_TEST(subghz_decoder_power_smart_test); - MU_RUN_TEST(subghz_decoder_marantec_test); - MU_RUN_TEST(subghz_decoder_bett_test); - MU_RUN_TEST(subghz_decoder_doitrand_test); - MU_RUN_TEST(subghz_decoder_phoenix_v2_test); - MU_RUN_TEST(subghz_decoder_honeywell_wdb_test); - - MU_RUN_TEST(subghz_encoder_princeton_test); - MU_RUN_TEST(subghz_encoder_came_test); - MU_RUN_TEST(subghz_encoder_came_twee_test); - MU_RUN_TEST(subghz_encoder_gate_tx_test); - 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_megacode_test); - MU_RUN_TEST(subghz_encoder_holtek_test); - MU_RUN_TEST(subghz_encoder_secplus_v1_test); - MU_RUN_TEST(subghz_encoder_secplus_v2_test); - MU_RUN_TEST(subghz_encoder_power_smart_test); - MU_RUN_TEST(subghz_encoder_marantec_test); - MU_RUN_TEST(subghz_encoder_bett_test); - MU_RUN_TEST(subghz_encoder_doitrand_test); - MU_RUN_TEST(subghz_encoder_phoenix_v2_test); - MU_RUN_TEST(subghz_encoder_honeywell_wdb_test); - - MU_RUN_TEST(subghz_random_test); - subghz_test_deinit(); -} - -int run_minunit_test_subghz() { - MU_RUN_SUITE(subghz); - return MU_EXIT_CODE; -} diff --git a/applications/unit_tests/test_index.c b/applications/unit_tests/test_index.c deleted file mode 100644 index e528224651d..00000000000 --- a/applications/unit_tests/test_index.c +++ /dev/null @@ -1,131 +0,0 @@ -#include "m-string.h" - -#include -#include -#include -#include "minunit_vars.h" -#include -#include -#include - -#define TAG "UnitTests" - -int run_minunit_test_furi(); -int run_minunit_test_infrared(); -int run_minunit_test_rpc(); -int run_minunit_test_flipper_format(); -int run_minunit_test_flipper_format_string(); -int run_minunit_test_stream(); -int run_minunit_test_storage(); -int run_minunit_test_subghz(); -int run_minunit_test_dirwalk(); -int run_minunit_test_nfc(); - -typedef int (*UnitTestEntry)(); - -typedef struct { - const char* name; - const UnitTestEntry entry; -} UnitTest; - -const UnitTest unit_tests[] = { - {.name = "furi", .entry = run_minunit_test_furi}, - {.name = "storage", .entry = run_minunit_test_storage}, - {.name = "stream", .entry = run_minunit_test_stream}, - {.name = "dirwalk", .entry = run_minunit_test_dirwalk}, - {.name = "flipper_format", .entry = run_minunit_test_flipper_format}, - {.name = "flipper_format_string", .entry = run_minunit_test_flipper_format_string}, - {.name = "rpc", .entry = run_minunit_test_rpc}, - {.name = "subghz", .entry = run_minunit_test_subghz}, - {.name = "infrared", .entry = run_minunit_test_infrared}, - {.name = "nfc", .entry = run_minunit_test_nfc}, -}; - -void minunit_print_progress() { - static const char progress[] = {'\\', '|', '/', '-'}; - static uint8_t progress_counter = 0; - static TickType_t last_tick = 0; - TickType_t current_tick = xTaskGetTickCount(); - if(current_tick - last_tick > 20) { - last_tick = current_tick; - printf("[%c]\033[3D", progress[++progress_counter % COUNT_OF(progress)]); - fflush(stdout); - } -} - -void minunit_print_fail(const char* str) { - printf(FURI_LOG_CLR_E "%s\r\n" FURI_LOG_CLR_RESET, str); -} - -void unit_tests_cli(Cli* cli, string_t args, void* context) { - UNUSED(cli); - UNUSED(args); - UNUSED(context); - uint32_t failed_tests = 0; - minunit_run = 0; - minunit_assert = 0; - minunit_fail = 0; - minunit_status = 0; - - Loader* loader = furi_record_open(RECORD_LOADER); - NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); - - // TODO: lock device while test running - if(loader_is_locked(loader)) { - printf("RPC: stop all applications to run tests\r\n"); - notification_message(notification, &sequence_blink_magenta_100); - } else { - notification_message_block(notification, &sequence_set_only_blue_255); - - uint32_t heap_before = memmgr_get_free_heap(); - uint32_t cycle_counter = furi_get_tick(); - - for(size_t i = 0; i < COUNT_OF(unit_tests); i++) { - if(cli_cmd_interrupt_received(cli)) { - break; - } - - if(string_size(args)) { - if(string_cmp_str(args, unit_tests[i].name) == 0) { - failed_tests += unit_tests[i].entry(); - } else { - printf("Skipping %s\r\n", unit_tests[i].name); - } - } else { - failed_tests += unit_tests[i].entry(); - } - } - printf("\r\nFailed tests: %lu\r\n", failed_tests); - - // Time report - cycle_counter = (furi_get_tick() - cycle_counter); - printf("Consumed: %lu ms\r\n", cycle_counter); - - // Wait for tested services and apps to deallocate memory - furi_delay_ms(200); - uint32_t heap_after = memmgr_get_free_heap(); - printf("Leaked: %ld\r\n", heap_before - heap_after); - - // Final Report - if(failed_tests == 0) { - notification_message(notification, &sequence_success); - printf("Status: PASSED\r\n"); - } else { - notification_message(notification, &sequence_error); - printf("Status: FAILED\r\n"); - } - } - - furi_record_close(RECORD_NOTIFICATION); - furi_record_close(RECORD_LOADER); -} - -void unit_tests_on_system_start() { -#ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - - // We need to launch apps from tests, so we cannot lock loader - cli_add_command(cli, "unit_tests", CliCommandFlagParallelSafe, unit_tests_cli, NULL); - furi_record_close(RECORD_CLI); -#endif -} diff --git a/applications/updater/cli/updater_cli.c b/applications/updater/cli/updater_cli.c deleted file mode 100644 index ec209bd1df0..00000000000 --- a/applications/updater/cli/updater_cli.c +++ /dev/null @@ -1,137 +0,0 @@ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -typedef void (*cmd_handler)(string_t args); -typedef struct { - const char* command; - const cmd_handler handler; -} CliSubcommand; - -static void updater_cli_install(string_t manifest_path) { - printf("Verifying update package at '%s'\r\n", string_get_cstr(manifest_path)); - - UpdatePrepareResult result = update_operation_prepare(string_get_cstr(manifest_path)); - if(result != UpdatePrepareResultOK) { - printf( - "Error: %s. Stopping update.\r\n", - update_operation_describe_preparation_result(result)); - return; - } - printf("OK.\r\nRestarting to apply update. BRB\r\n"); - furi_delay_ms(100); - furi_hal_power_reset(); -} - -static void updater_cli_backup(string_t args) { - printf("Backup /int to '%s'\r\n", string_get_cstr(args)); - Storage* storage = furi_record_open(RECORD_STORAGE); - bool success = lfs_backup_create(storage, string_get_cstr(args)); - furi_record_close(RECORD_STORAGE); - printf("Result: %s\r\n", success ? "OK" : "FAIL"); -} - -static void updater_cli_restore(string_t args) { - printf("Restore /int from '%s'\r\n", string_get_cstr(args)); - Storage* storage = furi_record_open(RECORD_STORAGE); - bool success = lfs_backup_unpack(storage, string_get_cstr(args)); - furi_record_close(RECORD_STORAGE); - printf("Result: %s\r\n", success ? "OK" : "FAIL"); -} - -static void updater_cli_help(string_t args) { - UNUSED(args); - printf("Commands:\r\n" - "\tinstall /ext/path/to/update.fuf - verify & apply update package\r\n" - "\tbackup /ext/path/to/backup.tar - create internal storage backup\r\n" - "\trestore /ext/path/to/backup.tar - restore internal storage backup\r\n"); -} - -static const CliSubcommand update_cli_subcommands[] = { - {.command = "install", .handler = updater_cli_install}, - {.command = "backup", .handler = updater_cli_backup}, - {.command = "restore", .handler = updater_cli_restore}, - {.command = "help", .handler = updater_cli_help}, -}; - -static void updater_cli_ep(Cli* cli, string_t args, void* context) { - UNUSED(cli); - UNUSED(context); - string_t subcommand; - string_init(subcommand); - if(!args_read_string_and_trim(args, subcommand) || string_empty_p(args)) { - updater_cli_help(args); - string_clear(subcommand); - return; - } - for(size_t idx = 0; idx < COUNT_OF(update_cli_subcommands); ++idx) { - const CliSubcommand* subcmd_def = &update_cli_subcommands[idx]; - if(string_cmp_str(subcommand, subcmd_def->command) == 0) { - string_clear(subcommand); - subcmd_def->handler(args); - return; - } - } - string_clear(subcommand); - updater_cli_help(args); -} - -static int32_t updater_spawner_thread_worker(void* arg) { - UNUSED(arg); - Loader* loader = furi_record_open(RECORD_LOADER); - loader_start(loader, "UpdaterApp", NULL); - furi_record_close(RECORD_LOADER); - return 0; -} - -static void updater_spawner_thread_cleanup(FuriThreadState state, void* context) { - FuriThread* thread = context; - if(state == FuriThreadStateStopped) { - furi_thread_free(thread); - } -} - -static void updater_start_app() { - FuriHalRtcBootMode mode = furi_hal_rtc_get_boot_mode(); - if((mode != FuriHalRtcBootModePreUpdate) && (mode != FuriHalRtcBootModePostUpdate)) { - return; - } - - /* We need to spawn a separate thread, because these callbacks are executed - * inside loader process, at startup. - * So, accessing its record would cause a deadlock - */ - FuriThread* thread = furi_thread_alloc(); - - furi_thread_set_name(thread, "UpdateAppSpawner"); - furi_thread_set_stack_size(thread, 768); - furi_thread_set_callback(thread, updater_spawner_thread_worker); - furi_thread_set_state_callback(thread, updater_spawner_thread_cleanup); - furi_thread_set_state_context(thread, thread); - furi_thread_start(thread); -} - -void updater_on_system_start() { -#ifdef SRV_CLI - Cli* cli = (Cli*)furi_record_open(RECORD_CLI); - cli_add_command(cli, "update", CliCommandFlagDefault, updater_cli_ep, NULL); - furi_record_close(RECORD_CLI); -#else - UNUSED(updater_cli_ep); -#endif -#ifndef FURI_RAM_EXEC - updater_start_app(); -#else - UNUSED(updater_start_app); -#endif -} diff --git a/applications/updater/util/update_task.c b/applications/updater/util/update_task.c deleted file mode 100644 index 6864076d688..00000000000 --- a/applications/updater/util/update_task.c +++ /dev/null @@ -1,351 +0,0 @@ -#include "update_task.h" -#include "update_task_i.h" - -#include -#include -#include -#include -#include -#include -#include - -static const char* update_task_stage_descr[] = { - [UpdateTaskStageProgress] = "...", - [UpdateTaskStageReadManifest] = "Loading update manifest", - [UpdateTaskStageValidateDFUImage] = "Checking DFU file", - [UpdateTaskStageFlashWrite] = "Writing flash", - [UpdateTaskStageFlashValidate] = "Validating flash", - [UpdateTaskStageRadioImageValidate] = "Checking radio FW", - [UpdateTaskStageRadioErase] = "Uninstalling radio FW", - [UpdateTaskStageRadioWrite] = "Writing radio FW", - [UpdateTaskStageRadioInstall] = "Installing radio FW", - [UpdateTaskStageRadioBusy] = "Radio is updating", - [UpdateTaskStageOBValidation] = "Validating opt. bytes", - [UpdateTaskStageLfsBackup] = "Backing up LFS", - [UpdateTaskStageLfsRestore] = "Restoring LFS", - [UpdateTaskStageResourcesUpdate] = "Updating resources", - [UpdateTaskStageSplashscreenInstall] = "Installing splashscreen", - [UpdateTaskStageCompleted] = "Restarting...", - [UpdateTaskStageError] = "Error", - [UpdateTaskStageOBError] = "OB, report", -}; - -typedef struct { - UpdateTaskStageGroup group; - uint8_t weight; -} UpdateTaskStageGroupMap; - -#define STAGE_DEF(GROUP, WEIGHT) \ - { .group = (GROUP), .weight = (WEIGHT), } - -static const UpdateTaskStageGroupMap update_task_stage_progress[] = { - [UpdateTaskStageProgress] = STAGE_DEF(UpdateTaskStageGroupMisc, 0), - - [UpdateTaskStageReadManifest] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 5), - [UpdateTaskStageLfsBackup] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 15), - - [UpdateTaskStageRadioImageValidate] = STAGE_DEF(UpdateTaskStageGroupRadio, 15), - [UpdateTaskStageRadioErase] = STAGE_DEF(UpdateTaskStageGroupRadio, 60), - [UpdateTaskStageRadioWrite] = STAGE_DEF(UpdateTaskStageGroupRadio, 80), - [UpdateTaskStageRadioInstall] = STAGE_DEF(UpdateTaskStageGroupRadio, 60), - [UpdateTaskStageRadioBusy] = STAGE_DEF(UpdateTaskStageGroupRadio, 80), - - [UpdateTaskStageOBValidation] = STAGE_DEF(UpdateTaskStageGroupOptionBytes, 10), - - [UpdateTaskStageValidateDFUImage] = STAGE_DEF(UpdateTaskStageGroupFirmware, 50), - [UpdateTaskStageFlashWrite] = STAGE_DEF(UpdateTaskStageGroupFirmware, 200), - [UpdateTaskStageFlashValidate] = STAGE_DEF(UpdateTaskStageGroupFirmware, 30), - - [UpdateTaskStageLfsRestore] = STAGE_DEF(UpdateTaskStageGroupPostUpdate, 30), - - [UpdateTaskStageResourcesUpdate] = STAGE_DEF(UpdateTaskStageGroupResources, 255), - [UpdateTaskStageSplashscreenInstall] = STAGE_DEF(UpdateTaskStageGroupSplashscreen, 5), - - [UpdateTaskStageCompleted] = STAGE_DEF(UpdateTaskStageGroupMisc, 1), - [UpdateTaskStageError] = STAGE_DEF(UpdateTaskStageGroupMisc, 1), - [UpdateTaskStageOBError] = STAGE_DEF(UpdateTaskStageGroupMisc, 1), -}; - -static UpdateTaskStageGroup update_task_get_task_groups(UpdateTask* update_task) { - UpdateTaskStageGroup ret = UpdateTaskStageGroupPreUpdate | UpdateTaskStageGroupPostUpdate; - UpdateManifest* manifest = update_task->manifest; - if(!string_empty_p(manifest->radio_image)) { - ret |= UpdateTaskStageGroupRadio; - } - if(update_manifest_has_obdata(manifest)) { - ret |= UpdateTaskStageGroupOptionBytes; - } - if(!string_empty_p(manifest->firmware_dfu_image)) { - ret |= UpdateTaskStageGroupFirmware; - } - if(!string_empty_p(manifest->resource_bundle)) { - ret |= UpdateTaskStageGroupResources; - } - if(!string_empty_p(manifest->splash_file)) { - ret |= UpdateTaskStageGroupSplashscreen; - } - return ret; -} - -static void update_task_calc_completed_stages(UpdateTask* update_task) { - uint32_t completed_stages_points = 0; - for(UpdateTaskStage past_stage = UpdateTaskStageProgress; - past_stage < update_task->state.stage; - ++past_stage) { - const UpdateTaskStageGroupMap* grp_descr = &update_task_stage_progress[past_stage]; - if((grp_descr->group & update_task->state.groups) == 0) { - continue; - } - completed_stages_points += grp_descr->weight; - } - update_task->state.completed_stages_points = completed_stages_points; -} - -void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, uint8_t progress) { - if(stage != UpdateTaskStageProgress) { - /* do not override more specific error states */ - if((stage >= UpdateTaskStageError) && (update_task->state.stage >= UpdateTaskStageError)) { - return; - } - /* Build error message with code "[stage_idx-stage_percent]" */ - if(stage >= UpdateTaskStageError) { - string_printf( - update_task->state.status, - "%s #[%d-%d]", - update_task_stage_descr[stage], - update_task->state.stage, - update_task->state.stage_progress); - } else { - string_set_str(update_task->state.status, update_task_stage_descr[stage]); - } - /* Store stage update */ - update_task->state.stage = stage; - /* If we are still alive, sum completed stages weights */ - if((stage > UpdateTaskStageProgress) && (stage < UpdateTaskStageCompleted)) { - update_task_calc_completed_stages(update_task); - } - } - - /* Store stage progress for all non-error updates - to provide details on error state */ - if(!update_stage_is_error(stage)) { - update_task->state.stage_progress = progress; - } - - /* Calculate "overall" progress, based on stage weights */ - uint32_t adapted_progress = 1; - if(update_task->state.total_progress_points != 0) { - if(stage < UpdateTaskStageCompleted) { - adapted_progress = MIN( - (update_task->state.completed_stages_points + - (update_task_stage_progress[update_task->state.stage].weight * progress / 100)) * - 100 / (update_task->state.total_progress_points), - 100u); - - } else { - adapted_progress = update_task->state.overall_progress; - } - } - update_task->state.overall_progress = adapted_progress; - - if(update_task->status_change_cb) { - (update_task->status_change_cb)( - string_get_cstr(update_task->state.status), - adapted_progress, - update_stage_is_error(update_task->state.stage), - update_task->status_change_cb_state); - } -} - -static void update_task_close_file(UpdateTask* update_task) { - furi_assert(update_task); - if(!storage_file_is_open(update_task->file)) { - return; - } - - storage_file_close(update_task->file); -} - -static bool update_task_check_file_exists(UpdateTask* update_task, string_t filename) { - furi_assert(update_task); - string_t tmp_path; - string_init_set(tmp_path, update_task->update_path); - path_append(tmp_path, string_get_cstr(filename)); - bool exists = - (storage_common_stat(update_task->storage, string_get_cstr(tmp_path), NULL) == FSE_OK); - string_clear(tmp_path); - return exists; -} - -bool update_task_open_file(UpdateTask* update_task, string_t filename) { - furi_assert(update_task); - update_task_close_file(update_task); - - string_t tmp_path; - string_init_set(tmp_path, update_task->update_path); - path_append(tmp_path, string_get_cstr(filename)); - bool open_success = storage_file_open( - update_task->file, string_get_cstr(tmp_path), FSAM_READ, FSOM_OPEN_EXISTING); - string_clear(tmp_path); - return open_success; -} - -static void update_task_worker_thread_cb(FuriThreadState state, void* context) { - UpdateTask* update_task = context; - - if(state != FuriThreadStateStopped) { - return; - } - - if(furi_thread_get_return_code(update_task->thread) == UPDATE_TASK_NOERR) { - furi_delay_ms(UPDATE_DELAY_OPERATION_OK); - furi_hal_power_reset(); - } -} - -UpdateTask* update_task_alloc() { - UpdateTask* update_task = malloc(sizeof(UpdateTask)); - - update_task->state.stage = UpdateTaskStageProgress; - update_task->state.stage_progress = 0; - update_task->state.overall_progress = 0; - string_init(update_task->state.status); - - update_task->manifest = update_manifest_alloc(); - update_task->storage = furi_record_open(RECORD_STORAGE); - update_task->file = storage_file_alloc(update_task->storage); - update_task->status_change_cb = NULL; - update_task->boot_mode = furi_hal_rtc_get_boot_mode(); - string_init(update_task->update_path); - - FuriThread* thread = update_task->thread = furi_thread_alloc(); - - furi_thread_set_name(thread, "UpdateWorker"); - furi_thread_set_stack_size(thread, 5120); - furi_thread_set_context(thread, update_task); - - furi_thread_set_state_callback(thread, update_task_worker_thread_cb); - furi_thread_set_state_context(thread, update_task); -#ifdef FURI_RAM_EXEC - UNUSED(update_task_worker_backup_restore); - furi_thread_set_callback(thread, update_task_worker_flash_writer); -#else - UNUSED(update_task_worker_flash_writer); - furi_thread_set_callback(thread, update_task_worker_backup_restore); -#endif - - return update_task; -} - -void update_task_free(UpdateTask* update_task) { - furi_assert(update_task); - - furi_thread_join(update_task->thread); - - furi_thread_free(update_task->thread); - update_task_close_file(update_task); - storage_file_free(update_task->file); - update_manifest_free(update_task->manifest); - - furi_record_close(RECORD_STORAGE); - string_clear(update_task->update_path); - - free(update_task); -} - -bool update_task_parse_manifest(UpdateTask* update_task) { - furi_assert(update_task); - update_task->state.stage_progress = 0; - update_task->state.overall_progress = 0; - update_task->state.total_progress_points = 0; - update_task->state.completed_stages_points = 0; - update_task->state.groups = 0; - - update_task_set_progress(update_task, UpdateTaskStageReadManifest, 0); - bool result = false; - string_t manifest_path; - string_init(manifest_path); - - do { - update_task_set_progress(update_task, UpdateTaskStageProgress, 13); - if(!furi_hal_version_do_i_belong_here()) { - break; - } - - update_task_set_progress(update_task, UpdateTaskStageProgress, 20); - if(!update_operation_get_current_package_manifest_path( - update_task->storage, manifest_path)) { - break; - } - - path_extract_dirname(string_get_cstr(manifest_path), update_task->update_path); - update_task_set_progress(update_task, UpdateTaskStageProgress, 30); - - UpdateManifest* manifest = update_task->manifest; - if(!update_manifest_init(manifest, string_get_cstr(manifest_path))) { - break; - } - - update_task_set_progress(update_task, UpdateTaskStageProgress, 40); - if(manifest->manifest_version < UPDATE_OPERATION_MIN_MANIFEST_VERSION) { - break; - } - - update_task_set_progress(update_task, UpdateTaskStageProgress, 50); - if(manifest->target != furi_hal_version_get_hw_target()) { - break; - } - - update_task->state.groups = update_task_get_task_groups(update_task); - for(size_t stage_counter = 0; stage_counter < COUNT_OF(update_task_stage_progress); - ++stage_counter) { - const UpdateTaskStageGroupMap* grp_descr = &update_task_stage_progress[stage_counter]; - if((grp_descr->group & update_task->state.groups) != 0) { - update_task->state.total_progress_points += grp_descr->weight; - } - } - - update_task_set_progress(update_task, UpdateTaskStageProgress, 60); - if((update_task->state.groups & UpdateTaskStageGroupFirmware) && - !update_task_check_file_exists(update_task, manifest->firmware_dfu_image)) { - break; - } - - update_task_set_progress(update_task, UpdateTaskStageProgress, 80); - if((update_task->state.groups & UpdateTaskStageGroupRadio) && - (!update_task_check_file_exists(update_task, manifest->radio_image) || - (manifest->radio_version.version.type == 0))) { - break; - } - - update_task_set_progress(update_task, UpdateTaskStageProgress, 100); - result = true; - } while(false); - - string_clear(manifest_path); - return result; -} - -void update_task_set_progress_cb(UpdateTask* update_task, updateProgressCb cb, void* state) { - update_task->status_change_cb = cb; - update_task->status_change_cb_state = state; -} - -void update_task_start(UpdateTask* update_task) { - furi_assert(update_task); - furi_thread_start(update_task->thread); -} - -bool update_task_is_running(UpdateTask* update_task) { - furi_assert(update_task); - return furi_thread_get_state(update_task->thread) == FuriThreadStateRunning; -} - -UpdateTaskState const* update_task_get_state(UpdateTask* update_task) { - furi_assert(update_task); - return &update_task->state; -} - -UpdateManifest const* update_task_get_manifest(UpdateTask* update_task) { - furi_assert(update_task); - return update_task->manifest; -} diff --git a/applications/updater/util/update_task_worker_backup.c b/applications/updater/util/update_task_worker_backup.c deleted file mode 100644 index 09e4595333c..00000000000 --- a/applications/updater/util/update_task_worker_backup.c +++ /dev/null @@ -1,150 +0,0 @@ -#include "update_task.h" -#include "update_task_i.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define TAG "UpdWorkerBackup" - -#define CHECK_RESULT(x) \ - if(!(x)) { \ - break; \ - } - -static bool update_task_pre_update(UpdateTask* update_task) { - bool success = false; - string_t backup_file_path; - string_init(backup_file_path); - path_concat( - string_get_cstr(update_task->update_path), LFS_BACKUP_DEFAULT_FILENAME, backup_file_path); - - update_task_set_progress(update_task, UpdateTaskStageLfsBackup, 0); - /* to avoid bootloops */ - furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); - if((success = lfs_backup_create(update_task->storage, string_get_cstr(backup_file_path)))) { - furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeUpdate); - } - - string_clear(backup_file_path); - return success; -} - -typedef struct { - UpdateTask* update_task; - int32_t total_files, processed_files; -} TarUnpackProgress; - -static bool update_task_resource_unpack_cb(const char* name, bool is_directory, void* context) { - UNUSED(name); - UNUSED(is_directory); - TarUnpackProgress* unpack_progress = context; - unpack_progress->processed_files++; - update_task_set_progress( - unpack_progress->update_task, - UpdateTaskStageProgress, - unpack_progress->processed_files * 100 / (unpack_progress->total_files + 1)); - return true; -} - -static bool update_task_post_update(UpdateTask* update_task) { - bool success = false; - - string_t file_path; - string_init(file_path); - - TarArchive* archive = tar_archive_alloc(update_task->storage); - do { - path_concat( - string_get_cstr(update_task->update_path), LFS_BACKUP_DEFAULT_FILENAME, file_path); - - update_task_set_progress(update_task, UpdateTaskStageLfsRestore, 0); - - CHECK_RESULT(lfs_backup_unpack(update_task->storage, string_get_cstr(file_path))); - - if(update_task->state.groups & UpdateTaskStageGroupResources) { - TarUnpackProgress progress = { - .update_task = update_task, - .total_files = 0, - .processed_files = 0, - }; - update_task_set_progress(update_task, UpdateTaskStageResourcesUpdate, 0); - - path_concat( - string_get_cstr(update_task->update_path), - string_get_cstr(update_task->manifest->resource_bundle), - file_path); - - tar_archive_set_file_callback(archive, update_task_resource_unpack_cb, &progress); - CHECK_RESULT( - tar_archive_open(archive, string_get_cstr(file_path), TAR_OPEN_MODE_READ)); - - progress.total_files = tar_archive_get_entries_count(archive); - if(progress.total_files > 0) { - CHECK_RESULT(tar_archive_unpack_to(archive, STORAGE_EXT_PATH_PREFIX, NULL)); - } - } - - if(update_task->state.groups & UpdateTaskStageGroupSplashscreen) { - update_task_set_progress(update_task, UpdateTaskStageSplashscreenInstall, 0); - string_t tmp_path; - string_init_set(tmp_path, update_task->update_path); - path_append(tmp_path, string_get_cstr(update_task->manifest->splash_file)); - if(storage_common_copy( - update_task->storage, - string_get_cstr(tmp_path), - INT_PATH(SLIDESHOW_FILE_NAME)) != FSE_OK) { - // actually, not critical - } - string_clear(tmp_path); - update_task_set_progress(update_task, UpdateTaskStageSplashscreenInstall, 100); - } - success = true; - } while(false); - - tar_archive_free(archive); - string_clear(file_path); - return success; -} - -int32_t update_task_worker_backup_restore(void* context) { - furi_assert(context); - UpdateTask* update_task = context; - - FuriHalRtcBootMode boot_mode = update_task->boot_mode; - if((boot_mode != FuriHalRtcBootModePreUpdate) && (boot_mode != FuriHalRtcBootModePostUpdate)) { - /* no idea how we got here. Do nothing */ - return UPDATE_TASK_NOERR; - } - - bool success = false; - do { - if(!update_task_parse_manifest(update_task)) { - break; - } - - if(boot_mode == FuriHalRtcBootModePreUpdate) { - success = update_task_pre_update(update_task); - } else if(boot_mode == FuriHalRtcBootModePostUpdate) { - success = update_task_post_update(update_task); - if(success) { - update_operation_disarm(); - } - } - } while(false); - - if(!success) { - update_task_set_progress(update_task, UpdateTaskStageError, 0); - return UPDATE_TASK_FAILED; - } - - update_task_set_progress(update_task, UpdateTaskStageCompleted, 100); - return UPDATE_TASK_NOERR; -} diff --git a/applications/updater/views/updater_main.c b/applications/updater/views/updater_main.c deleted file mode 100644 index 72541b9abd6..00000000000 --- a/applications/updater/views/updater_main.c +++ /dev/null @@ -1,140 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "../updater_i.h" -#include "updater_main.h" - -struct UpdaterMainView { - View* view; - ViewDispatcher* view_dispatcher; - FuriPubSubSubscription* subscription; - void* context; -}; - -static const uint8_t PROGRESS_RENDER_STEP = 1; /* percent, to limit rendering rate */ - -typedef struct { - string_t status; - uint8_t progress, rendered_progress; - bool failed; -} UpdaterProgressModel; - -void updater_main_model_set_state( - UpdaterMainView* main_view, - const char* message, - uint8_t progress, - bool failed) { - with_view_model( - main_view->view, (UpdaterProgressModel * model) { - model->failed = failed; - model->progress = progress; - if(string_cmp_str(model->status, message)) { - string_set(model->status, message); - model->rendered_progress = progress; - return true; - } - if((model->rendered_progress > progress) || - ((progress - model->rendered_progress) > PROGRESS_RENDER_STEP)) { - model->rendered_progress = progress; - return true; - } - return false; - }); -} - -View* updater_main_get_view(UpdaterMainView* main_view) { - furi_assert(main_view); - return main_view->view; -} - -bool updater_main_input(InputEvent* event, void* context) { - furi_assert(event); - furi_assert(context); - - UpdaterMainView* main_view = context; - if(!main_view->view_dispatcher) { - return true; - } - - if((event->type == InputTypeShort) && (event->key == InputKeyOk)) { - view_dispatcher_send_custom_event( - main_view->view_dispatcher, UpdaterCustomEventRetryUpdate); - } else if((event->type == InputTypeLong) && (event->key == InputKeyBack)) { - view_dispatcher_send_custom_event( - main_view->view_dispatcher, UpdaterCustomEventCancelUpdate); - } - - return true; -} - -static void updater_main_draw_callback(Canvas* canvas, void* _model) { - UpdaterProgressModel* model = _model; - - canvas_set_font(canvas, FontPrimary); - - if(model->failed) { - canvas_draw_str_aligned(canvas, 42, 16, AlignLeft, AlignTop, "Update Failed!"); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, 42, 32, AlignLeft, AlignTop, string_get_cstr(model->status)); - - canvas_draw_icon(canvas, 7, 16, &I_Warning_30x23); - canvas_draw_str_aligned( - canvas, 18, 51, AlignLeft, AlignTop, "to retry, hold to abort"); - canvas_draw_icon(canvas, 7, 50, &I_Ok_btn_9x9); - canvas_draw_icon(canvas, 75, 51, &I_Pin_back_arrow_10x8); - } else { - canvas_draw_str_aligned(canvas, 55, 14, AlignLeft, AlignTop, "UPDATING"); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, 64, 51, AlignCenter, AlignTop, string_get_cstr(model->status)); - canvas_draw_icon(canvas, 4, 5, &I_Updating_32x40); - elements_progress_bar(canvas, 42, 29, 80, (float)model->progress / 100); - } -} - -UpdaterMainView* updater_main_alloc() { - UpdaterMainView* main_view = malloc(sizeof(UpdaterMainView)); - - main_view->view = view_alloc(); - view_allocate_model(main_view->view, ViewModelTypeLocking, sizeof(UpdaterProgressModel)); - - with_view_model( - main_view->view, (UpdaterProgressModel * model) { - string_init_set(model->status, "Waiting for SD card"); - return true; - }); - - view_set_context(main_view->view, main_view); - view_set_input_callback(main_view->view, updater_main_input); - view_set_draw_callback(main_view->view, updater_main_draw_callback); - - return main_view; -} - -void updater_main_free(UpdaterMainView* main_view) { - furi_assert(main_view); - with_view_model( - main_view->view, (UpdaterProgressModel * model) { - string_clear(model->status); - return false; - }); - view_free(main_view->view); - free(main_view); -} - -void updater_main_set_storage_pubsub(UpdaterMainView* main_view, FuriPubSubSubscription* sub) { - main_view->subscription = sub; -} - -FuriPubSubSubscription* updater_main_get_storage_pubsub(UpdaterMainView* main_view) { - return main_view->subscription; -} - -void updater_main_set_view_dispatcher(UpdaterMainView* main_view, ViewDispatcher* view_dispatcher) { - main_view->view_dispatcher = view_dispatcher; -} diff --git a/applications_user/.gitignore b/applications_user/.gitignore new file mode 100644 index 00000000000..72e8ffc0db8 --- /dev/null +++ b/applications_user/.gitignore @@ -0,0 +1 @@ +* diff --git a/applications_user/README.md b/applications_user/README.md new file mode 100644 index 00000000000..8bb7823c1a5 --- /dev/null +++ b/applications_user/README.md @@ -0,0 +1 @@ +Put your custom applications in this folder. \ No newline at end of file diff --git a/assets/.gitignore b/assets/.gitignore index eb20456f178..ca338d63336 100644 --- a/assets/.gitignore +++ b/assets/.gitignore @@ -1,2 +1 @@ -/headers /core2_firmware diff --git a/assets/ReadMe.md b/assets/ReadMe.md index 2cd99d56b64..84310e731f6 100644 --- a/assets/ReadMe.md +++ b/assets/ReadMe.md @@ -9,12 +9,6 @@ ./fbt icons proto dolphin_internal dolphin_blocking dolphin_ext resources ``` -# Compiling with Docker-Compose - -```bash -docker-compose exec dev ./fbt icons proto dolphin_internal dolphin_blocking dolphin_ext resources -``` - # Asset naming rules ## Images and Animations @@ -38,10 +32,7 @@ Good starting point: https://docs.unrealengine.com/4.27/en-US/ProductionPipeline Don't include assets that you are not using, compiler is not going to strip unused assets. # Structure -- `compiled` - Output folder made for compiled assets, after building project, in `build` directory. - `dolphin` - Dolphin game assets sources. Goes to `compiled` and `resources` folders in `build` directory. - `icons` - Icons sources. Goes to `compiled` folder in `build` directory. - `protobuf` - Protobuf sources. Goes to `compiled` folder in `build` directory. -- `resources` - Assets that is going to be provisioned to SD card. - `slideshow` - One-time slideshows for desktop -- `unit_tests` - Some pre-defined signals for testing purposes. diff --git a/assets/SConscript b/assets/SConscript index aa5a5de0dc4..a2550a3a011 100644 --- a/assets/SConscript +++ b/assets/SConscript @@ -1,42 +1,24 @@ Import("env") -# HACHHACK -# Currently injected to CPPPATH by libs - since they are built earlier and depend on assets -# env.Append( -# CPPPATH=[ -# Dir("./compiled"), -# ] -# ) - assetsenv = env.Clone( tools=["fbt_assets"], FW_LIB_NAME="assets", + ASSETS_WORK_DIR=env.Dir("compiled"), + ASSETS_SRC_DIR=env.Dir("#/assets"), ) assetsenv.ApplyLibFlags() -if not assetsenv["VERBOSE"]: - assetsenv.SetDefault( - ICONSCOMSTR="\tICONS\t${TARGET}", - PROTOCOMSTR="\tPROTO\t${SOURCE}", - DOLPHINCOMSTR="\tDOLPHIN\t${DOLPHIN_RES_TYPE}", - RESMANIFESTCOMSTR="\tMANIFEST\t${TARGET}", - PBVERCOMSTR="\tPBVER\t${TARGET}", - ) - -# Gathering icons sources -icons_src = assetsenv.GlobRecursive("*.png", "icons") -icons_src += assetsenv.GlobRecursive("frame_rate", "icons") - -icons = assetsenv.IconBuilder(Dir("compiled"), Dir("#/assets/icons")) -assetsenv.Depends(icons, icons_src) +icons = assetsenv.CompileIcons( + assetsenv["ASSETS_WORK_DIR"], + assetsenv["ASSETS_SRC_DIR"].Dir("icons"), +) assetsenv.Alias("icons", icons) # Protobuf .proto -> .c + .h - proto_src = assetsenv.Glob("protobuf/*.proto", source=True) proto_options = assetsenv.Glob("protobuf/*.options", source=True) -proto = assetsenv.ProtoBuilder(assetsenv.Dir("compiled"), proto_src) +proto = assetsenv.ProtoBuilder(assetsenv["ASSETS_WORK_DIR"], proto_src) assetsenv.Depends(proto, proto_options) # Precious(proto) assetsenv.Alias("proto", proto) @@ -45,8 +27,8 @@ assetsenv.Alias("proto", proto) # Internal animations dolphin_internal = assetsenv.DolphinSymBuilder( - assetsenv.Dir("compiled"), - assetsenv.Dir("#/assets/dolphin"), + assetsenv["ASSETS_WORK_DIR"], + assetsenv["ASSETS_SRC_DIR"].Dir("dolphin"), DOLPHIN_RES_TYPE="internal", ) assetsenv.Alias("dolphin_internal", dolphin_internal) @@ -55,8 +37,8 @@ assetsenv.Alias("dolphin_internal", dolphin_internal) # Blocking animations dolphin_blocking = assetsenv.DolphinSymBuilder( - assetsenv.Dir("compiled"), - assetsenv.Dir("#/assets/dolphin"), + assetsenv["ASSETS_WORK_DIR"], + assetsenv["ASSETS_SRC_DIR"].Dir("dolphin"), DOLPHIN_RES_TYPE="blocking", ) assetsenv.Alias("dolphin_blocking", dolphin_blocking) @@ -64,33 +46,35 @@ assetsenv.Alias("dolphin_blocking", dolphin_blocking) # Protobuf version meta proto_ver = assetsenv.ProtoVerBuilder( - "compiled/protobuf_version.h", - "#/assets/protobuf/Changelog", + "${ASSETS_WORK_DIR}/protobuf_version.h", + assetsenv["ASSETS_SRC_DIR"].File("protobuf/Changelog"), ) assetsenv.Depends(proto_ver, proto) assetsenv.Alias("proto_ver", proto_ver) # Gather everything into a static lib assets_parts = (icons, proto, dolphin_blocking, dolphin_internal, proto_ver) +env.Replace(FW_ASSETS_HEADERS=assets_parts) assetslib = assetsenv.Library("${FW_LIB_NAME}", assets_parts) assetsenv.Install("${LIB_DIST_DIR}", assetslib) # Resources for SD card - if assetsenv["IS_BASE_FIRMWARE"]: + dolphin_external_out_dir = assetsenv["ASSETS_WORK_DIR"].Dir("dolphin") # External dolphin animations dolphin_external = assetsenv.DolphinExtBuilder( - assetsenv.Dir("#/assets/resources/dolphin"), - assetsenv.Dir("#/assets/dolphin"), + dolphin_external_out_dir, + assetsenv["ASSETS_SRC_DIR"].Dir("dolphin"), DOLPHIN_RES_TYPE="external", ) - assetsenv.NoClean(dolphin_external) if assetsenv["FORCE"]: assetsenv.AlwaysBuild(dolphin_external) assetsenv.Alias("dolphin_ext", dolphin_external) + assetsenv.Clean(dolphin_external, dolphin_external_out_dir) +<<<<<<< HEAD # Inverted dolphin animations dolphin_external_inv = assetsenv.DolphinExtBuilder( assetsenv.Dir("#/assets/resources/dolphin_inverted"), @@ -119,5 +103,8 @@ if assetsenv["IS_BASE_FIRMWARE"]: # Exporting resources node to external environment env["FW_RESOURCES"] = resources assetsenv.Alias("resources", resources) +======= + env.Replace(DOLPHIN_EXTERNAL_OUT_DIR=dolphin_external_out_dir) +>>>>>>> upstream/dev Return("assetslib") diff --git a/assets/dolphin/ReadMe.md b/assets/dolphin/ReadMe.md index 6b59d231f0b..e7572571ca0 100644 --- a/assets/dolphin/ReadMe.md +++ b/assets/dolphin/ReadMe.md @@ -26,7 +26,7 @@ Version: 1 - `Name` - name of animation. Must be exact animation directory name. - `Min butthurt`, `Max butthurt` - range of dolphin's butthurt for this animation. - `Min level`, `Max level` - range of dolphin's level for this animation. If 0, this animation doesn't participate in random idle animation selection and can only be selected by exact name. -- `Weight` - chance of this animation to be choosen at random animation selection. +- `Weight` - chance of this animation to be chosen at random animation selection. Some animations can be excluded from participation in random animation selection, such as `L1_NoSd_128x49`. @@ -52,7 +52,7 @@ Version: 1 - `Active cooldown` - amount of seconds (after passive mode) to pass before entering next active mode. - `Bubble slots` - amount of bubble sequences. -- Any bubble sequence plays whole sequence during active mode. There can be many bubble sequences and bubbles inside it. Bubbles in 1 bubble sequence have to reside in 1 slot. Bubbles order in 1 bubble sequence is determined by occurance in file. As soon as frame index goes out of EndFrame index of bubble - next animation bubble is choosen. There can also be free of bubbles frames between 2 bubbles. +- Any bubble sequence plays whole sequence during active mode. There can be many bubble sequences and bubbles inside it. Bubbles in 1 bubble sequence have to reside in 1 slot. Bubbles order in 1 bubble sequence is determined by occurrence in file. As soon as frame index goes out of EndFrame index of bubble - next animation bubble is chosen. There can also be free of bubbles frames between 2 bubbles. - `Slot` - number to unite bubbles for same sequence. - `X`, `Y` - are coordinates of left top corner of bubble. diff --git a/assets/dolphin/blocking/L0_NoDb_128x51/frame_0.png b/assets/dolphin/blocking/L0_NoDb_128x51/frame_0.png index e82c6f2e9c6..759007623af 100644 Binary files a/assets/dolphin/blocking/L0_NoDb_128x51/frame_0.png and b/assets/dolphin/blocking/L0_NoDb_128x51/frame_0.png differ diff --git a/assets/dolphin/blocking/L0_NoDb_128x51/frame_1.png b/assets/dolphin/blocking/L0_NoDb_128x51/frame_1.png index 58dab74d4a6..c9810b61e34 100644 Binary files a/assets/dolphin/blocking/L0_NoDb_128x51/frame_1.png and b/assets/dolphin/blocking/L0_NoDb_128x51/frame_1.png differ diff --git a/assets/dolphin/blocking/L0_NoDb_128x51/frame_2.png b/assets/dolphin/blocking/L0_NoDb_128x51/frame_2.png index 83ac2d5510f..e4d381b0ae4 100644 Binary files a/assets/dolphin/blocking/L0_NoDb_128x51/frame_2.png and b/assets/dolphin/blocking/L0_NoDb_128x51/frame_2.png differ diff --git a/assets/dolphin/blocking/L0_NoDb_128x51/frame_3.png b/assets/dolphin/blocking/L0_NoDb_128x51/frame_3.png index 789dc8822e8..b48aef97839 100644 Binary files a/assets/dolphin/blocking/L0_NoDb_128x51/frame_3.png and b/assets/dolphin/blocking/L0_NoDb_128x51/frame_3.png differ diff --git a/assets/dolphin/blocking/L0_SdBad_128x51/frame_0.png b/assets/dolphin/blocking/L0_SdBad_128x51/frame_0.png index 05f5241c80f..f9a7e073a65 100644 Binary files a/assets/dolphin/blocking/L0_SdBad_128x51/frame_0.png and b/assets/dolphin/blocking/L0_SdBad_128x51/frame_0.png differ diff --git a/assets/dolphin/blocking/L0_SdBad_128x51/frame_1.png b/assets/dolphin/blocking/L0_SdBad_128x51/frame_1.png index bd0f2b93382..147561f0a40 100644 Binary files a/assets/dolphin/blocking/L0_SdBad_128x51/frame_1.png and b/assets/dolphin/blocking/L0_SdBad_128x51/frame_1.png differ diff --git a/assets/dolphin/blocking/L0_SdOk_128x51/frame_0.png b/assets/dolphin/blocking/L0_SdOk_128x51/frame_0.png index ed3aa717d16..6ebbc111108 100644 Binary files a/assets/dolphin/blocking/L0_SdOk_128x51/frame_0.png and b/assets/dolphin/blocking/L0_SdOk_128x51/frame_0.png differ diff --git a/assets/dolphin/blocking/L0_SdOk_128x51/frame_1.png b/assets/dolphin/blocking/L0_SdOk_128x51/frame_1.png index 58add1b9822..5f7a5d2a508 100644 Binary files a/assets/dolphin/blocking/L0_SdOk_128x51/frame_1.png and b/assets/dolphin/blocking/L0_SdOk_128x51/frame_1.png differ diff --git a/assets/dolphin/blocking/L0_SdOk_128x51/frame_2.png b/assets/dolphin/blocking/L0_SdOk_128x51/frame_2.png index a47aebec7bc..a3450ae05db 100644 Binary files a/assets/dolphin/blocking/L0_SdOk_128x51/frame_2.png and b/assets/dolphin/blocking/L0_SdOk_128x51/frame_2.png differ diff --git a/assets/dolphin/blocking/L0_SdOk_128x51/frame_3.png b/assets/dolphin/blocking/L0_SdOk_128x51/frame_3.png index 614ef9c5aee..1e52f151374 100644 Binary files a/assets/dolphin/blocking/L0_SdOk_128x51/frame_3.png and b/assets/dolphin/blocking/L0_SdOk_128x51/frame_3.png differ diff --git a/assets/dolphin/blocking/L0_Url_128x51/frame_0.png b/assets/dolphin/blocking/L0_Url_128x51/frame_0.png index 5d4b5cf2e22..387c85ea2e6 100644 Binary files a/assets/dolphin/blocking/L0_Url_128x51/frame_0.png and b/assets/dolphin/blocking/L0_Url_128x51/frame_0.png differ diff --git a/assets/dolphin/blocking/L0_Url_128x51/frame_1.png b/assets/dolphin/blocking/L0_Url_128x51/frame_1.png index 69f1fb36598..9975ca3f042 100644 Binary files a/assets/dolphin/blocking/L0_Url_128x51/frame_1.png and b/assets/dolphin/blocking/L0_Url_128x51/frame_1.png differ diff --git a/assets/dolphin/blocking/L0_Url_128x51/frame_2.png b/assets/dolphin/blocking/L0_Url_128x51/frame_2.png index 855e7450e53..84241c3f16a 100644 Binary files a/assets/dolphin/blocking/L0_Url_128x51/frame_2.png and b/assets/dolphin/blocking/L0_Url_128x51/frame_2.png differ diff --git a/assets/dolphin/blocking/L0_Url_128x51/frame_3.png b/assets/dolphin/blocking/L0_Url_128x51/frame_3.png index 9e9a7940581..c44b171bfaa 100644 Binary files a/assets/dolphin/blocking/L0_Url_128x51/frame_3.png and b/assets/dolphin/blocking/L0_Url_128x51/frame_3.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_0.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_0.png new file mode 100644 index 00000000000..8b8dc80bce0 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_1.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_1.png new file mode 100644 index 00000000000..956de617070 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_10.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_10.png new file mode 100644 index 00000000000..93fdaa07674 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_11.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_11.png new file mode 100644 index 00000000000..a4e194825e1 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_12.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_12.png new file mode 100644 index 00000000000..9cbcbd070eb Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_13.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_13.png new file mode 100644 index 00000000000..a745cdb03f1 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_14.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_14.png new file mode 100644 index 00000000000..768f471ec1f Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_15.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_15.png new file mode 100644 index 00000000000..2f50fb8c949 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_16.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_16.png new file mode 100644 index 00000000000..fc7b76696c1 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_17.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_17.png new file mode 100644 index 00000000000..013e2008a2f Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_18.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_18.png new file mode 100644 index 00000000000..795120e772f Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_19.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_19.png new file mode 100644 index 00000000000..52061a5a260 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_2.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_2.png new file mode 100644 index 00000000000..10afed391e4 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_20.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_20.png new file mode 100644 index 00000000000..52f87f3a88e Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_21.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_21.png new file mode 100644 index 00000000000..9696e0b5ccf Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_21.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_22.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_22.png new file mode 100644 index 00000000000..d23ee492b07 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_22.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_23.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_23.png new file mode 100644 index 00000000000..9d368900c94 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_23.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_24.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_24.png new file mode 100644 index 00000000000..daf0788ad75 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_24.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_25.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_25.png new file mode 100644 index 00000000000..b40333654f8 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_25.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_26.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_26.png new file mode 100644 index 00000000000..9d499f23d22 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_26.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_27.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_27.png new file mode 100644 index 00000000000..0291dfb5830 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_27.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_28.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_28.png new file mode 100644 index 00000000000..54a889d8151 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_28.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_29.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_29.png new file mode 100644 index 00000000000..ba79b3b88ff Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_29.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_3.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_3.png new file mode 100644 index 00000000000..bedf366c6cc Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_30.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_30.png new file mode 100644 index 00000000000..0731760dffb Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_30.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_31.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_31.png new file mode 100644 index 00000000000..898efdc4b8f Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_31.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_32.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_32.png new file mode 100644 index 00000000000..39f5db8a013 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_32.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_33.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_33.png new file mode 100644 index 00000000000..bee4cff0872 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_33.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_34.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_34.png new file mode 100644 index 00000000000..969b91193d4 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_34.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_35.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_35.png new file mode 100644 index 00000000000..a72cf1823ee Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_35.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_36.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_36.png new file mode 100644 index 00000000000..9a13e7c67db Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_36.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_37.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_37.png new file mode 100644 index 00000000000..da3ee77f30b Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_37.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_38.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_38.png new file mode 100644 index 00000000000..93da7f4f94b Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_38.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_39.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_39.png new file mode 100644 index 00000000000..7510931b44b Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_39.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_4.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_4.png new file mode 100644 index 00000000000..f99454b164c Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_40.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_40.png new file mode 100644 index 00000000000..a17a14044a9 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_40.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_41.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_41.png new file mode 100644 index 00000000000..f763540c917 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_41.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_42.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_42.png new file mode 100644 index 00000000000..97a829e9b7a Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_42.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_43.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_43.png new file mode 100644 index 00000000000..7eadf75185e Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_43.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_44.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_44.png new file mode 100644 index 00000000000..5241195d32b Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_44.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_45.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_45.png new file mode 100644 index 00000000000..2a3ea8e23af Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_45.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_46.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_46.png new file mode 100644 index 00000000000..f4b263b199c Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_46.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_47.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_47.png new file mode 100644 index 00000000000..1563ff39b27 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_47.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_5.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_5.png new file mode 100644 index 00000000000..394bb53df4a Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_6.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_6.png new file mode 100644 index 00000000000..3f84ad47e3d Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_7.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_7.png new file mode 100644 index 00000000000..8ea650ad075 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_8.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_8.png new file mode 100644 index 00000000000..2c8cf3f5d7c Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_9.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_9.png new file mode 100644 index 00000000000..caf6a90ef61 Binary files /dev/null and b/assets/dolphin/external/L1_Kaiju_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L1_Kaiju_128x64/meta.txt b/assets/dolphin/external/L1_Kaiju_128x64/meta.txt new file mode 100644 index 00000000000..597f4b692a9 --- /dev/null +++ b/assets/dolphin/external/L1_Kaiju_128x64/meta.txt @@ -0,0 +1,50 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 16 +Active frames: 60 +Frames order: 0 1 2 1 0 3 4 5 6 3 7 1 8 1 7 9 0 10 11 12 13 14 15 16 14 12 17 18 19 20 21 22 22 23 22 24 25 26 27 26 25 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 19 18 14 45 14 45 14 45 14 45 14 16 14 14 11 46 47 1 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 1 + +Slot: 0 +X: 83 +Y: 42 +Text: Sup +AlignH: Left +AlignV: Top +StartFrame: 38 +EndFrame: 40 + +Slot: 0 +X: 66 +Y: 35 +Text: What just +AlignH: Left +AlignV: Center +StartFrame: 62 +EndFrame: 65 + +Slot: 0 +X: 66 +Y: 35 +Text: happened? +AlignH: Left +AlignV: Center +StartFrame: 66 +EndFrame: 68 + +Slot: 0 +X: 87 +Y: 38 +Text: Idk +AlignH: Left +AlignV: Top +StartFrame: 70 +EndFrame: 70 diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_0.png b/assets/dolphin/external/L1_Mods_128x64/frame_0.png new file mode 100644 index 00000000000..220908495ad Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_1.png b/assets/dolphin/external/L1_Mods_128x64/frame_1.png new file mode 100644 index 00000000000..9123906fbb9 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_10.png b/assets/dolphin/external/L1_Mods_128x64/frame_10.png new file mode 100644 index 00000000000..e90ad5e90d9 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_11.png b/assets/dolphin/external/L1_Mods_128x64/frame_11.png new file mode 100644 index 00000000000..031c0ad81a1 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_12.png b/assets/dolphin/external/L1_Mods_128x64/frame_12.png new file mode 100644 index 00000000000..856e068fd00 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_13.png b/assets/dolphin/external/L1_Mods_128x64/frame_13.png new file mode 100644 index 00000000000..a0366b2cfb4 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_14.png b/assets/dolphin/external/L1_Mods_128x64/frame_14.png new file mode 100644 index 00000000000..24fd557abd9 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_15.png b/assets/dolphin/external/L1_Mods_128x64/frame_15.png new file mode 100644 index 00000000000..3bf1d3ed2b4 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_16.png b/assets/dolphin/external/L1_Mods_128x64/frame_16.png new file mode 100644 index 00000000000..f0b44898fb7 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_17.png b/assets/dolphin/external/L1_Mods_128x64/frame_17.png new file mode 100644 index 00000000000..c98c70c9126 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_18.png b/assets/dolphin/external/L1_Mods_128x64/frame_18.png new file mode 100644 index 00000000000..4f7b7ae82eb Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_19.png b/assets/dolphin/external/L1_Mods_128x64/frame_19.png new file mode 100644 index 00000000000..b3ad6700cec Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_2.png b/assets/dolphin/external/L1_Mods_128x64/frame_2.png new file mode 100644 index 00000000000..c4aac4b916b Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_20.png b/assets/dolphin/external/L1_Mods_128x64/frame_20.png new file mode 100644 index 00000000000..ea2eae4d7cb Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_21.png b/assets/dolphin/external/L1_Mods_128x64/frame_21.png new file mode 100644 index 00000000000..900cc7d1209 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_21.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_22.png b/assets/dolphin/external/L1_Mods_128x64/frame_22.png new file mode 100644 index 00000000000..de6c511e4f4 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_22.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_23.png b/assets/dolphin/external/L1_Mods_128x64/frame_23.png new file mode 100644 index 00000000000..4f82f63bc9e Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_23.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_24.png b/assets/dolphin/external/L1_Mods_128x64/frame_24.png new file mode 100644 index 00000000000..d7c614902b6 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_24.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_25.png b/assets/dolphin/external/L1_Mods_128x64/frame_25.png new file mode 100644 index 00000000000..768030b3c56 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_25.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_26.png b/assets/dolphin/external/L1_Mods_128x64/frame_26.png new file mode 100644 index 00000000000..12f22abdb99 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_26.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_27.png b/assets/dolphin/external/L1_Mods_128x64/frame_27.png new file mode 100644 index 00000000000..9fca976de90 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_27.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_28.png b/assets/dolphin/external/L1_Mods_128x64/frame_28.png new file mode 100644 index 00000000000..4b2ab5863a5 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_28.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_29.png b/assets/dolphin/external/L1_Mods_128x64/frame_29.png new file mode 100644 index 00000000000..69c709adce4 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_29.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_3.png b/assets/dolphin/external/L1_Mods_128x64/frame_3.png new file mode 100644 index 00000000000..1b0e77426c5 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_30.png b/assets/dolphin/external/L1_Mods_128x64/frame_30.png new file mode 100644 index 00000000000..13caae7ca2e Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_30.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_31.png b/assets/dolphin/external/L1_Mods_128x64/frame_31.png new file mode 100644 index 00000000000..b1d1e8bfe4f Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_31.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_32.png b/assets/dolphin/external/L1_Mods_128x64/frame_32.png new file mode 100644 index 00000000000..acf000827ff Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_32.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_33.png b/assets/dolphin/external/L1_Mods_128x64/frame_33.png new file mode 100644 index 00000000000..b6c6fbb1916 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_33.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_34.png b/assets/dolphin/external/L1_Mods_128x64/frame_34.png new file mode 100644 index 00000000000..7d2dcda5d98 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_34.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_35.png b/assets/dolphin/external/L1_Mods_128x64/frame_35.png new file mode 100644 index 00000000000..461270ba4b6 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_35.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_36.png b/assets/dolphin/external/L1_Mods_128x64/frame_36.png new file mode 100644 index 00000000000..b018a94c19c Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_36.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_37.png b/assets/dolphin/external/L1_Mods_128x64/frame_37.png new file mode 100644 index 00000000000..fa2b303cca2 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_37.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_38.png b/assets/dolphin/external/L1_Mods_128x64/frame_38.png new file mode 100644 index 00000000000..ed38122f52a Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_38.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_39.png b/assets/dolphin/external/L1_Mods_128x64/frame_39.png new file mode 100644 index 00000000000..38610bb4b4a Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_39.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_4.png b/assets/dolphin/external/L1_Mods_128x64/frame_4.png new file mode 100644 index 00000000000..45e47de12ad Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_40.png b/assets/dolphin/external/L1_Mods_128x64/frame_40.png new file mode 100644 index 00000000000..7f6b4b29a38 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_40.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_5.png b/assets/dolphin/external/L1_Mods_128x64/frame_5.png new file mode 100644 index 00000000000..7c293b4859a Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_6.png b/assets/dolphin/external/L1_Mods_128x64/frame_6.png new file mode 100644 index 00000000000..e72e7a30ee0 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_7.png b/assets/dolphin/external/L1_Mods_128x64/frame_7.png new file mode 100644 index 00000000000..5c840d6f642 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_8.png b/assets/dolphin/external/L1_Mods_128x64/frame_8.png new file mode 100644 index 00000000000..f689f190cc0 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_9.png b/assets/dolphin/external/L1_Mods_128x64/frame_9.png new file mode 100644 index 00000000000..628394e57a6 Binary files /dev/null and b/assets/dolphin/external/L1_Mods_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L1_Mods_128x64/meta.txt b/assets/dolphin/external/L1_Mods_128x64/meta.txt new file mode 100644 index 00000000000..0225c7e5593 --- /dev/null +++ b/assets/dolphin/external/L1_Mods_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 23 +Active frames: 18 +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 37 38 39 40 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 0 \ No newline at end of file diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_0.png b/assets/dolphin/external/L1_My_dude_128x64/frame_0.png new file mode 100644 index 00000000000..bf07d03d6e6 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_1.png b/assets/dolphin/external/L1_My_dude_128x64/frame_1.png new file mode 100644 index 00000000000..4402654c776 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_10.png b/assets/dolphin/external/L1_My_dude_128x64/frame_10.png new file mode 100644 index 00000000000..10dabe4c5c5 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_11.png b/assets/dolphin/external/L1_My_dude_128x64/frame_11.png new file mode 100644 index 00000000000..878712fe23a Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_12.png b/assets/dolphin/external/L1_My_dude_128x64/frame_12.png new file mode 100644 index 00000000000..19fc985ac86 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_13.png b/assets/dolphin/external/L1_My_dude_128x64/frame_13.png new file mode 100644 index 00000000000..39172f26f90 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_14.png b/assets/dolphin/external/L1_My_dude_128x64/frame_14.png new file mode 100644 index 00000000000..9a3a84fff83 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_15.png b/assets/dolphin/external/L1_My_dude_128x64/frame_15.png new file mode 100644 index 00000000000..2472c2729f6 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_16.png b/assets/dolphin/external/L1_My_dude_128x64/frame_16.png new file mode 100644 index 00000000000..4940aef672f Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_17.png b/assets/dolphin/external/L1_My_dude_128x64/frame_17.png new file mode 100644 index 00000000000..fd910ce5a61 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_18.png b/assets/dolphin/external/L1_My_dude_128x64/frame_18.png new file mode 100644 index 00000000000..ed33f18a707 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_19.png b/assets/dolphin/external/L1_My_dude_128x64/frame_19.png new file mode 100644 index 00000000000..d602a01d5b1 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_2.png b/assets/dolphin/external/L1_My_dude_128x64/frame_2.png new file mode 100644 index 00000000000..b680b4ae056 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_20.png b/assets/dolphin/external/L1_My_dude_128x64/frame_20.png new file mode 100644 index 00000000000..2dfa931f259 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_21.png b/assets/dolphin/external/L1_My_dude_128x64/frame_21.png new file mode 100644 index 00000000000..272064d90de Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_21.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_22.png b/assets/dolphin/external/L1_My_dude_128x64/frame_22.png new file mode 100644 index 00000000000..35d0149ad97 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_22.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_23.png b/assets/dolphin/external/L1_My_dude_128x64/frame_23.png new file mode 100644 index 00000000000..83d02ef88a8 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_23.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_24.png b/assets/dolphin/external/L1_My_dude_128x64/frame_24.png new file mode 100644 index 00000000000..f3ac4417819 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_24.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_25.png b/assets/dolphin/external/L1_My_dude_128x64/frame_25.png new file mode 100644 index 00000000000..832c2bde9f9 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_25.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_26.png b/assets/dolphin/external/L1_My_dude_128x64/frame_26.png new file mode 100644 index 00000000000..3836a3b01b8 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_26.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_27.png b/assets/dolphin/external/L1_My_dude_128x64/frame_27.png new file mode 100644 index 00000000000..ff621b73eea Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_27.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_28.png b/assets/dolphin/external/L1_My_dude_128x64/frame_28.png new file mode 100644 index 00000000000..94e05f94d9d Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_28.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_29.png b/assets/dolphin/external/L1_My_dude_128x64/frame_29.png new file mode 100644 index 00000000000..1ce384b165c Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_29.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_3.png b/assets/dolphin/external/L1_My_dude_128x64/frame_3.png new file mode 100644 index 00000000000..a5056eb4bcf Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_30.png b/assets/dolphin/external/L1_My_dude_128x64/frame_30.png new file mode 100644 index 00000000000..8d42b8b482b Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_30.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_31.png b/assets/dolphin/external/L1_My_dude_128x64/frame_31.png new file mode 100644 index 00000000000..ac926d7be31 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_31.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_32.png b/assets/dolphin/external/L1_My_dude_128x64/frame_32.png new file mode 100644 index 00000000000..35070eb6b4a Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_32.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_33.png b/assets/dolphin/external/L1_My_dude_128x64/frame_33.png new file mode 100644 index 00000000000..a6c973f6794 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_33.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_34.png b/assets/dolphin/external/L1_My_dude_128x64/frame_34.png new file mode 100644 index 00000000000..3f9407f385c Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_34.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_35.png b/assets/dolphin/external/L1_My_dude_128x64/frame_35.png new file mode 100644 index 00000000000..6059e00d545 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_35.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_36.png b/assets/dolphin/external/L1_My_dude_128x64/frame_36.png new file mode 100644 index 00000000000..d2cd0c970c6 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_36.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_37.png b/assets/dolphin/external/L1_My_dude_128x64/frame_37.png new file mode 100644 index 00000000000..e60fd08dee1 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_37.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_38.png b/assets/dolphin/external/L1_My_dude_128x64/frame_38.png new file mode 100644 index 00000000000..70e56b168bf Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_38.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_39.png b/assets/dolphin/external/L1_My_dude_128x64/frame_39.png new file mode 100644 index 00000000000..450b4d4f63d Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_39.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_4.png b/assets/dolphin/external/L1_My_dude_128x64/frame_4.png new file mode 100644 index 00000000000..2d9f4e96334 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_40.png b/assets/dolphin/external/L1_My_dude_128x64/frame_40.png new file mode 100644 index 00000000000..369200345db Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_40.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_41.png b/assets/dolphin/external/L1_My_dude_128x64/frame_41.png new file mode 100644 index 00000000000..e0f88226827 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_41.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_42.png b/assets/dolphin/external/L1_My_dude_128x64/frame_42.png new file mode 100644 index 00000000000..a8a23536a43 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_42.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_43.png b/assets/dolphin/external/L1_My_dude_128x64/frame_43.png new file mode 100644 index 00000000000..6a402b350b0 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_43.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_44.png b/assets/dolphin/external/L1_My_dude_128x64/frame_44.png new file mode 100644 index 00000000000..f425bcc1794 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_44.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_45.png b/assets/dolphin/external/L1_My_dude_128x64/frame_45.png new file mode 100644 index 00000000000..b0ea1a7e78c Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_45.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_46.png b/assets/dolphin/external/L1_My_dude_128x64/frame_46.png new file mode 100644 index 00000000000..3113ff2e623 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_46.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_47.png b/assets/dolphin/external/L1_My_dude_128x64/frame_47.png new file mode 100644 index 00000000000..87403c610ac Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_47.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_48.png b/assets/dolphin/external/L1_My_dude_128x64/frame_48.png new file mode 100644 index 00000000000..2734e2fcd57 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_48.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_5.png b/assets/dolphin/external/L1_My_dude_128x64/frame_5.png new file mode 100644 index 00000000000..df225594910 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_6.png b/assets/dolphin/external/L1_My_dude_128x64/frame_6.png new file mode 100644 index 00000000000..4c00552ea2b Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_7.png b/assets/dolphin/external/L1_My_dude_128x64/frame_7.png new file mode 100644 index 00000000000..9703809405f Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_8.png b/assets/dolphin/external/L1_My_dude_128x64/frame_8.png new file mode 100644 index 00000000000..86e41e913b7 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/frame_9.png b/assets/dolphin/external/L1_My_dude_128x64/frame_9.png new file mode 100644 index 00000000000..4334eefafb9 Binary files /dev/null and b/assets/dolphin/external/L1_My_dude_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L1_My_dude_128x64/meta.txt b/assets/dolphin/external/L1_My_dude_128x64/meta.txt new file mode 100644 index 00000000000..8c326cf42de --- /dev/null +++ b/assets/dolphin/external/L1_My_dude_128x64/meta.txt @@ -0,0 +1,32 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 19 +Active frames: 51 +Frames order: 0 1 2 3 4 5 6 0 1 2 7 8 9 10 11 12 7 8 9 13 14 15 14 13 14 15 7 8 9 16 17 18 13 14 19 20 21 22 23 24 21 25 26 27 28 29 30 31 32 33 32 34 35 36 35 34 37 38 39 40 41 42 43 44 45 46 17 47 48 7 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 1 + +Slot: 0 +X: 41 +Y: 43 +Text: My dude +AlignH: Right +AlignV: Top +StartFrame: 50 +EndFrame: 50 + +Slot: 0 +X: 59 +Y: 43 +Text: My dude +AlignH: Left +AlignV: Top +StartFrame: 54 +EndFrame: 54 \ No newline at end of file diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_0.png b/assets/dolphin/external/L1_Painting_128x64/frame_0.png new file mode 100644 index 00000000000..b2f9bc775cc Binary files /dev/null and b/assets/dolphin/external/L1_Painting_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_1.png b/assets/dolphin/external/L1_Painting_128x64/frame_1.png new file mode 100644 index 00000000000..02ac533ce76 Binary files /dev/null and b/assets/dolphin/external/L1_Painting_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_10.png b/assets/dolphin/external/L1_Painting_128x64/frame_10.png new file mode 100644 index 00000000000..ae3148c323e Binary files /dev/null and b/assets/dolphin/external/L1_Painting_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_11.png b/assets/dolphin/external/L1_Painting_128x64/frame_11.png new file mode 100644 index 00000000000..89d003d071e Binary files /dev/null and b/assets/dolphin/external/L1_Painting_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_2.png b/assets/dolphin/external/L1_Painting_128x64/frame_2.png new file mode 100644 index 00000000000..8bfe6b33c57 Binary files /dev/null and b/assets/dolphin/external/L1_Painting_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_3.png b/assets/dolphin/external/L1_Painting_128x64/frame_3.png new file mode 100644 index 00000000000..1c6fc2144ba Binary files /dev/null and b/assets/dolphin/external/L1_Painting_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_4.png b/assets/dolphin/external/L1_Painting_128x64/frame_4.png new file mode 100644 index 00000000000..d39cddea149 Binary files /dev/null and b/assets/dolphin/external/L1_Painting_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_5.png b/assets/dolphin/external/L1_Painting_128x64/frame_5.png new file mode 100644 index 00000000000..4f21a268ac0 Binary files /dev/null and b/assets/dolphin/external/L1_Painting_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_6.png b/assets/dolphin/external/L1_Painting_128x64/frame_6.png new file mode 100644 index 00000000000..3f492eab5e1 Binary files /dev/null and b/assets/dolphin/external/L1_Painting_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_7.png b/assets/dolphin/external/L1_Painting_128x64/frame_7.png new file mode 100644 index 00000000000..336cffcb4c1 Binary files /dev/null and b/assets/dolphin/external/L1_Painting_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_8.png b/assets/dolphin/external/L1_Painting_128x64/frame_8.png new file mode 100644 index 00000000000..a44a7315da3 Binary files /dev/null and b/assets/dolphin/external/L1_Painting_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_9.png b/assets/dolphin/external/L1_Painting_128x64/frame_9.png new file mode 100644 index 00000000000..7cd4252910d Binary files /dev/null and b/assets/dolphin/external/L1_Painting_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L1_Painting_128x64/meta.txt b/assets/dolphin/external/L1_Painting_128x64/meta.txt new file mode 100644 index 00000000000..6964b479bd6 --- /dev/null +++ b/assets/dolphin/external/L1_Painting_128x64/meta.txt @@ -0,0 +1,32 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 9 +Active frames: 13 +Frames order: 0 1 2 3 4 5 2 3 4 10 6 7 8 7 8 7 8 7 8 9 10 11 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 1 + +Slot: 0 +X: 57 +Y: 24 +Text: No mistakes, +AlignH: Left +AlignV: Center +StartFrame: 11 +EndFrame: 14 + +Slot: 0 +X: 57 +Y: 21 +Text: only happy\n accidents +AlignH: Left +AlignV: Center +StartFrame: 15 +EndFrame: 18 \ No newline at end of file diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_0.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_0.png new file mode 100644 index 00000000000..71e85fe8f97 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_1.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_1.png new file mode 100644 index 00000000000..31ab932b965 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_10.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_10.png new file mode 100644 index 00000000000..da8f13680c0 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_11.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_11.png new file mode 100644 index 00000000000..9c87945b53d Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_12.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_12.png new file mode 100644 index 00000000000..52ecb01c1ff Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_13.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_13.png new file mode 100644 index 00000000000..165b0635a7c Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_14.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_14.png new file mode 100644 index 00000000000..3ad1f1c2d64 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_15.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_15.png new file mode 100644 index 00000000000..dace07e8370 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_16.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_16.png new file mode 100644 index 00000000000..2f232188897 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_17.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_17.png new file mode 100644 index 00000000000..ea67b364570 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_18.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_18.png new file mode 100644 index 00000000000..e4526da940c Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_19.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_19.png new file mode 100644 index 00000000000..b6e3de1acd2 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_2.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_2.png new file mode 100644 index 00000000000..a76a000224f Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_20.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_20.png new file mode 100644 index 00000000000..b33656867fe Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_21.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_21.png new file mode 100644 index 00000000000..6048810f0b3 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_21.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_22.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_22.png new file mode 100644 index 00000000000..657788f2b0c Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_22.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_23.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_23.png new file mode 100644 index 00000000000..852e778bfad Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_23.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_24.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_24.png new file mode 100644 index 00000000000..d8497ee6aff Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_24.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_25.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_25.png new file mode 100644 index 00000000000..d647aae1bf1 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_25.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_26.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_26.png new file mode 100644 index 00000000000..83bc94316b1 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_26.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_27.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_27.png new file mode 100644 index 00000000000..9f4e0ce7b95 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_27.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_28.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_28.png new file mode 100644 index 00000000000..894394ab37e Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_28.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_29.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_29.png new file mode 100644 index 00000000000..63babe79374 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_29.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_3.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_3.png new file mode 100644 index 00000000000..630f42f2ec6 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_30.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_30.png new file mode 100644 index 00000000000..d062254d931 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_30.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_31.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_31.png new file mode 100644 index 00000000000..01ebecda7c5 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_31.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_32.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_32.png new file mode 100644 index 00000000000..a66d6a42d9e Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_32.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_33.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_33.png new file mode 100644 index 00000000000..e80a66743e0 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_33.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_34.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_34.png new file mode 100644 index 00000000000..cea25e209dd Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_34.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_35.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_35.png new file mode 100644 index 00000000000..56e05f8f9af Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_35.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_36.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_36.png new file mode 100644 index 00000000000..8e72a7c390f Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_36.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_37.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_37.png new file mode 100644 index 00000000000..9f20081e53b Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_37.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_38.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_38.png new file mode 100644 index 00000000000..39e62095499 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_38.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_39.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_39.png new file mode 100644 index 00000000000..febe3bf0b05 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_39.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_4.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_4.png new file mode 100644 index 00000000000..7377c608036 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_40.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_40.png new file mode 100644 index 00000000000..2540fd3831d Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_40.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_41.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_41.png new file mode 100644 index 00000000000..63106c87ae7 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_41.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_42.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_42.png new file mode 100644 index 00000000000..21038b47a66 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_42.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_43.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_43.png new file mode 100644 index 00000000000..09309607ca2 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_43.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_44.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_44.png new file mode 100644 index 00000000000..2d4b512253c Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_44.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_45.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_45.png new file mode 100644 index 00000000000..0501b735ac7 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_45.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_46.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_46.png new file mode 100644 index 00000000000..1edca6aed37 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_46.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_47.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_47.png new file mode 100644 index 00000000000..467fe93e0c9 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_47.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_48.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_48.png new file mode 100644 index 00000000000..fb2c54aa933 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_48.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_49.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_49.png new file mode 100644 index 00000000000..74cd345fa09 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_49.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_5.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_5.png new file mode 100644 index 00000000000..70ed4f55900 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_50.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_50.png new file mode 100644 index 00000000000..32221d3476f Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_50.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_51.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_51.png new file mode 100644 index 00000000000..a81457ddc24 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_51.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_52.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_52.png new file mode 100644 index 00000000000..3fc75d8706b Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_52.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_53.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_53.png new file mode 100644 index 00000000000..2e4528c7318 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_53.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_54.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_54.png new file mode 100644 index 00000000000..abab3196435 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_54.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_55.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_55.png new file mode 100644 index 00000000000..dfa5312f556 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_55.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_56.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_56.png new file mode 100644 index 00000000000..59a194b1435 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_56.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_57.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_57.png new file mode 100644 index 00000000000..d142a38db7a Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_57.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_58.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_58.png new file mode 100644 index 00000000000..d6a66da8b87 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_58.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_59.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_59.png new file mode 100644 index 00000000000..4ab56d31ed0 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_59.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_6.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_6.png new file mode 100644 index 00000000000..7d4f0684e24 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_60.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_60.png new file mode 100644 index 00000000000..0cb8722e3ff Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_60.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_61.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_61.png new file mode 100644 index 00000000000..7da7d1adffe Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_61.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_62.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_62.png new file mode 100644 index 00000000000..1bfd8d30301 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_62.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_7.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_7.png new file mode 100644 index 00000000000..0ff0f5a3f07 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_8.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_8.png new file mode 100644 index 00000000000..69877673730 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_9.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_9.png new file mode 100644 index 00000000000..60627211940 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/meta.txt b/assets/dolphin/external/L1_Sad_song_128x64/meta.txt new file mode 100644 index 00000000000..683e690043d --- /dev/null +++ b/assets/dolphin/external/L1_Sad_song_128x64/meta.txt @@ -0,0 +1,284 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 31 +Active frames: 55 +Frames order: 0 1 2 3 4 5 0 1 6 7 8 5 9 10 11 12 13 14 15 0 1 2 3 4 5 0 1 6 7 8 5 9 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 53 54 53 55 56 57 58 59 60 61 62 4 5 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 6 + +Bubble slots: 4 + +Slot: 0 +X: 65 +Y: 14 +Text: All by myself +AlignH: Left +AlignV: Bottom +StartFrame: 45 +EndFrame: 51 + +Slot: 0 +X: 5 +Y: 16 +Text: Don't want +AlignH: Right +AlignV: Bottom +StartFrame: 56 +EndFrame: 58 + +Slot: 0 +X: 15 +Y: 15 +Text: to be +AlignH: Right +AlignV: Bottom +StartFrame: 59 +EndFrame: 60 + +Slot: 0 +X: 14 +Y: 14 +Text: All by myself +AlignH: Right +AlignV: Bottom +StartFrame: 63 +EndFrame: 69 + +Slot: 0 +X: 81 +Y: 25 +Text: anymore +AlignH: Left +AlignV: Bottom +StartFrame: 72 +EndFrame: 74 + +Slot: 1 +X: 65 +Y: 14 +Text: Nevermind +AlignH: Left +AlignV: Bottom +StartFrame: 45 +EndFrame: 48 + +Slot: 1 +X: 65 +Y: 14 +Text: I'll find +AlignH: Left +AlignV: Bottom +StartFrame: 49 +EndFrame: 52 + +Slot: 1 +X: 2 +Y: 16 +Text: Someone like +AlignH: Right +AlignV: Bottom +StartFrame: 56 +EndFrame: 58 + +Slot: 1 +X: 11 +Y: 16 +Text: youuuuu +AlignH: Right +AlignV: Bottom +StartFrame: 59 +EndFrame: 60 + +Slot: 1 +X: 3 +Y: 14 +Text: I wish nothing +AlignH: Right +AlignV: Bottom +StartFrame: 64 +EndFrame: 66 + +Slot: 1 +X: 6 +Y: 14 +Text: but the best +AlignH: Right +AlignV: Bottom +StartFrame: 67 +EndFrame: 70 + +Slot: 1 +X: 81 +Y: 25 +Text: for you +AlignH: Left +AlignV: Bottom +StartFrame: 72 +EndFrame: 74 + +Slot: 2 +X: 65 +Y: 14 +Text: What have I +AlignH: Left +AlignV: Bottom +StartFrame: 45 +EndFrame: 48 + +Slot: 2 +X: 65 +Y: 14 +Text: become +AlignH: Left +AlignV: Bottom +StartFrame: 47 +EndFrame: 51 + +Slot: 2 +X: 6 +Y: 16 +Text: My dearest +AlignH: Right +AlignV: Bottom +StartFrame: 56 +EndFrame: 58 + +Slot: 2 +X: 14 +Y: 16 +Text: friend +AlignH: Right +AlignV: Bottom +StartFrame: 59 +EndFrame: 60 + +Slot: 2 +X: 17 +Y: 14 +Text: Everyone +AlignH: Right +AlignV: Bottom +StartFrame: 63 +EndFrame: 64 + +Slot: 2 +X: 17 +Y: 14 +Text: I know +AlignH: Right +AlignV: Bottom +StartFrame: 65 +EndFrame: 67 + +Slot: 2 +X: 17 +Y: 14 +Text: goes away +AlignH: Right +AlignV: Bottom +StartFrame: 68 +EndFrame: 70 + +Slot: 2 +X: 81 +Y: 25 +Text: in the\n end +AlignH: Left +AlignV: Bottom +StartFrame: 72 +EndFrame: 74 + +Slot: 3 +X: 73 +Y: 14 +Text: We could\n have been +AlignH: Left +AlignV: Bottom +StartFrame: 45 +EndFrame: 48 + +Slot: 3 +X: 73 +Y: 14 +Text: so good\n together +AlignH: Left +AlignV: Bottom +StartFrame: 49 +EndFrame: 51 + +Slot: 3 +X: 7 +Y: 17 +Text: We could\n have lived +AlignH: Right +AlignV: Bottom +StartFrame: 55 +EndFrame: 57 + +Slot: 3 +X: 7 +Y: 17 +Text: this dance\n forever +AlignH: Right +AlignV: Bottom +StartFrame: 58 +EndFrame: 60 + +Slot: 3 +X: 12 +Y: 14 +Text: But now +AlignH: Right +AlignV: Bottom +StartFrame: 64 +EndFrame: 65 + +Slot: 3 +X: 5 +Y: 14 +Text: who's gonna +AlignH: Right +AlignV: Bottom +StartFrame: 66 +EndFrame: 67 + +Slot: 3 +X: 7 +Y: 14 +Text: dance with +AlignH: Right +AlignV: Bottom +StartFrame: 68 +EndFrame: 69 + +Slot: 3 +X: 26 +Y: 14 +Text: me? +AlignH: Right +AlignV: Bottom +StartFrame: 70 +EndFrame: 70 + +Slot: 3 +X: 81 +Y: 25 +Text: Please +AlignH: Left +AlignV: Bottom +StartFrame: 72 +EndFrame: 74 + +Slot: 3 +X: 81 +Y: 25 +Text: stay +AlignH: Left +AlignV: Bottom +StartFrame: 74 +EndFrame: 75 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_0.png b/assets/dolphin/external/L1_Senpai_128x64/frame_0.png new file mode 100644 index 00000000000..ed37723ac24 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_1.png b/assets/dolphin/external/L1_Senpai_128x64/frame_1.png new file mode 100644 index 00000000000..ad708ee431e Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_10.png b/assets/dolphin/external/L1_Senpai_128x64/frame_10.png new file mode 100644 index 00000000000..e385018bf65 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_11.png b/assets/dolphin/external/L1_Senpai_128x64/frame_11.png new file mode 100644 index 00000000000..553a979be10 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_12.png b/assets/dolphin/external/L1_Senpai_128x64/frame_12.png new file mode 100644 index 00000000000..9f8ca7e9b8b Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_13.png b/assets/dolphin/external/L1_Senpai_128x64/frame_13.png new file mode 100644 index 00000000000..a996443fe46 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_14.png b/assets/dolphin/external/L1_Senpai_128x64/frame_14.png new file mode 100644 index 00000000000..628d58b93a3 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_15.png b/assets/dolphin/external/L1_Senpai_128x64/frame_15.png new file mode 100644 index 00000000000..cc8431ade89 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_16.png b/assets/dolphin/external/L1_Senpai_128x64/frame_16.png new file mode 100644 index 00000000000..3ec3727989d Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_17.png b/assets/dolphin/external/L1_Senpai_128x64/frame_17.png new file mode 100644 index 00000000000..11b247eca27 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_18.png b/assets/dolphin/external/L1_Senpai_128x64/frame_18.png new file mode 100644 index 00000000000..bb15041331a Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_19.png b/assets/dolphin/external/L1_Senpai_128x64/frame_19.png new file mode 100644 index 00000000000..f953c8ef195 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_2.png b/assets/dolphin/external/L1_Senpai_128x64/frame_2.png new file mode 100644 index 00000000000..36c3b4abe17 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_20.png b/assets/dolphin/external/L1_Senpai_128x64/frame_20.png new file mode 100644 index 00000000000..d683b9f625f Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_21.png b/assets/dolphin/external/L1_Senpai_128x64/frame_21.png new file mode 100644 index 00000000000..66cbfe1d8ff Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_21.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_22.png b/assets/dolphin/external/L1_Senpai_128x64/frame_22.png new file mode 100644 index 00000000000..dd241d24ae5 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_22.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_23.png b/assets/dolphin/external/L1_Senpai_128x64/frame_23.png new file mode 100644 index 00000000000..944bdc74e99 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_23.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_24.png b/assets/dolphin/external/L1_Senpai_128x64/frame_24.png new file mode 100644 index 00000000000..3f445593af7 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_24.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_25.png b/assets/dolphin/external/L1_Senpai_128x64/frame_25.png new file mode 100644 index 00000000000..ea7823bd740 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_25.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_26.png b/assets/dolphin/external/L1_Senpai_128x64/frame_26.png new file mode 100644 index 00000000000..0b378fbccd6 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_26.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_27.png b/assets/dolphin/external/L1_Senpai_128x64/frame_27.png new file mode 100644 index 00000000000..66eec542a91 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_27.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_28.png b/assets/dolphin/external/L1_Senpai_128x64/frame_28.png new file mode 100644 index 00000000000..1e232ba91bc Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_28.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_29.png b/assets/dolphin/external/L1_Senpai_128x64/frame_29.png new file mode 100644 index 00000000000..e2767bd6554 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_29.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_3.png b/assets/dolphin/external/L1_Senpai_128x64/frame_3.png new file mode 100644 index 00000000000..9a3c13f6621 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_30.png b/assets/dolphin/external/L1_Senpai_128x64/frame_30.png new file mode 100644 index 00000000000..36d1212bec6 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_30.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_31.png b/assets/dolphin/external/L1_Senpai_128x64/frame_31.png new file mode 100644 index 00000000000..037bdc8ed6e Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_31.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_32.png b/assets/dolphin/external/L1_Senpai_128x64/frame_32.png new file mode 100644 index 00000000000..91ce1886940 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_32.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_33.png b/assets/dolphin/external/L1_Senpai_128x64/frame_33.png new file mode 100644 index 00000000000..e3e7799db4d Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_33.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_34.png b/assets/dolphin/external/L1_Senpai_128x64/frame_34.png new file mode 100644 index 00000000000..a28aac4e0f1 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_34.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_35.png b/assets/dolphin/external/L1_Senpai_128x64/frame_35.png new file mode 100644 index 00000000000..04f8c1a7f30 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_35.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_4.png b/assets/dolphin/external/L1_Senpai_128x64/frame_4.png new file mode 100644 index 00000000000..d065b77a12f Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_5.png b/assets/dolphin/external/L1_Senpai_128x64/frame_5.png new file mode 100644 index 00000000000..7a111afd03b Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_6.png b/assets/dolphin/external/L1_Senpai_128x64/frame_6.png new file mode 100644 index 00000000000..318c7eca0a6 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_7.png b/assets/dolphin/external/L1_Senpai_128x64/frame_7.png new file mode 100644 index 00000000000..b56b995dc61 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_8.png b/assets/dolphin/external/L1_Senpai_128x64/frame_8.png new file mode 100644 index 00000000000..6c4b8757060 Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_9.png b/assets/dolphin/external/L1_Senpai_128x64/frame_9.png new file mode 100644 index 00000000000..00b02330e1b Binary files /dev/null and b/assets/dolphin/external/L1_Senpai_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L1_Senpai_128x64/meta.txt b/assets/dolphin/external/L1_Senpai_128x64/meta.txt new file mode 100644 index 00000000000..f68f0b563ba --- /dev/null +++ b/assets/dolphin/external/L1_Senpai_128x64/meta.txt @@ -0,0 +1,23 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 16 +Active frames: 22 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 0 12 13 14 0 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 1 + +Slot: 0 +X: 5 +Y: 29 +Text: SENPAI !!! +AlignH: Right +AlignV: Center +StartFrame: 28 +EndFrame: 31 diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_0.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_0.png new file mode 100644 index 00000000000..e34e7969077 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_1.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_1.png new file mode 100644 index 00000000000..cc6032ad338 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_10.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_10.png new file mode 100644 index 00000000000..a28a21225a8 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_11.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_11.png new file mode 100644 index 00000000000..3d94c89103c Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_12.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_12.png new file mode 100644 index 00000000000..74e0b962c55 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_13.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_13.png new file mode 100644 index 00000000000..3269169e42a Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_14.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_14.png new file mode 100644 index 00000000000..42f844d3d73 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_15.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_15.png new file mode 100644 index 00000000000..861b16c65ef Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_16.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_16.png new file mode 100644 index 00000000000..2f4b3b839c5 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_17.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_17.png new file mode 100644 index 00000000000..7cba6f7953b Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_18.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_18.png new file mode 100644 index 00000000000..0b8fe650ea9 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_19.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_19.png new file mode 100644 index 00000000000..e3c50e3088f Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_2.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_2.png new file mode 100644 index 00000000000..c259b5a5ad1 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_20.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_20.png new file mode 100644 index 00000000000..ae6e7653204 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_21.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_21.png new file mode 100644 index 00000000000..e97affd7e33 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_21.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_22.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_22.png new file mode 100644 index 00000000000..b5c61592432 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_22.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_23.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_23.png new file mode 100644 index 00000000000..ef4876275d1 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_23.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_24.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_24.png new file mode 100644 index 00000000000..4dfe3a02906 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_24.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_25.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_25.png new file mode 100644 index 00000000000..1f9d6ac5477 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_25.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_26.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_26.png new file mode 100644 index 00000000000..379e29b50da Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_26.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_27.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_27.png new file mode 100644 index 00000000000..16210a792bb Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_27.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_28.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_28.png new file mode 100644 index 00000000000..7685c3bc3f7 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_28.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_29.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_29.png new file mode 100644 index 00000000000..5f114a47924 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_29.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_3.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_3.png new file mode 100644 index 00000000000..3f5c523acb2 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_30.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_30.png new file mode 100644 index 00000000000..645ffa6692b Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_30.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_31.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_31.png new file mode 100644 index 00000000000..a086ba60c4f Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_31.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_32.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_32.png new file mode 100644 index 00000000000..4fdc011d11f Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_32.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_33.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_33.png new file mode 100644 index 00000000000..f9789d8b8f6 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_33.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_34.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_34.png new file mode 100644 index 00000000000..e13f825fa75 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_34.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_35.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_35.png new file mode 100644 index 00000000000..05f9639b958 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_35.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_36.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_36.png new file mode 100644 index 00000000000..a968fd015cc Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_36.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_37.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_37.png new file mode 100644 index 00000000000..8393e3ce8c0 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_37.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_38.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_38.png new file mode 100644 index 00000000000..5e6c324997d Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_38.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_39.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_39.png new file mode 100644 index 00000000000..7ca97b4a7de Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_39.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_4.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_4.png new file mode 100644 index 00000000000..11253dd628b Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_40.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_40.png new file mode 100644 index 00000000000..a1ac9f6f327 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_40.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_41.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_41.png new file mode 100644 index 00000000000..c33f03e927a Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_41.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_42.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_42.png new file mode 100644 index 00000000000..dc51592c780 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_42.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_43.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_43.png new file mode 100644 index 00000000000..ff83fe77130 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_43.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_44.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_44.png new file mode 100644 index 00000000000..a5488fcb19b Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_44.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_45.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_45.png new file mode 100644 index 00000000000..86630e83aa5 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_45.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_46.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_46.png new file mode 100644 index 00000000000..a9147ae3c1c Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_46.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_47.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_47.png new file mode 100644 index 00000000000..f5b4529e448 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_47.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_48.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_48.png new file mode 100644 index 00000000000..1f27241b73e Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_48.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_49.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_49.png new file mode 100644 index 00000000000..f5656a7508c Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_49.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_5.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_5.png new file mode 100644 index 00000000000..1545c0ee718 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_50.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_50.png new file mode 100644 index 00000000000..1ff2a8874df Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_50.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_51.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_51.png new file mode 100644 index 00000000000..3608e114f1f Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_51.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_52.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_52.png new file mode 100644 index 00000000000..f109b562b82 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_52.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_53.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_53.png new file mode 100644 index 00000000000..a91b863c702 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_53.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_54.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_54.png new file mode 100644 index 00000000000..052196298a4 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_54.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_55.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_55.png new file mode 100644 index 00000000000..b8a8c651257 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_55.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_56.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_56.png new file mode 100644 index 00000000000..44f5bf6d25f Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_56.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_57.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_57.png new file mode 100644 index 00000000000..f7c1e8023e5 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_57.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_58.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_58.png new file mode 100644 index 00000000000..61efe9f450d Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_58.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_59.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_59.png new file mode 100644 index 00000000000..d0bfc886cc9 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_59.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_6.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_6.png new file mode 100644 index 00000000000..f99662e70f0 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_60.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_60.png new file mode 100644 index 00000000000..950152b7379 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_60.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_61.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_61.png new file mode 100644 index 00000000000..4e430d5c503 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_61.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_7.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_7.png new file mode 100644 index 00000000000..cf09a7842a0 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_8.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_8.png new file mode 100644 index 00000000000..d12fa6abaeb Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_9.png b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_9.png new file mode 100644 index 00000000000..ace68d642e2 Binary files /dev/null and b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L2_Coding_in_the_shell_128x64/meta.txt b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/meta.txt new file mode 100644 index 00000000000..0378846c4d4 --- /dev/null +++ b/assets/dolphin/external/L2_Coding_in_the_shell_128x64/meta.txt @@ -0,0 +1,23 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 21 +Active frames: 44 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 17 19 20 21 22 23 24 24 25 26 27 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 1 + +Slot: 0 +X: 7 +Y: 46 +Text: GOOD JOB! +AlignH: Center +AlignV: Top +StartFrame: 54 +EndFrame: 57 \ No newline at end of file diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_0.png b/assets/dolphin/external/L2_Dj_128x64/frame_0.png new file mode 100644 index 00000000000..95f72f901a5 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_1.png b/assets/dolphin/external/L2_Dj_128x64/frame_1.png new file mode 100644 index 00000000000..32e13541d83 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_10.png b/assets/dolphin/external/L2_Dj_128x64/frame_10.png new file mode 100644 index 00000000000..3cce11f998e Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_11.png b/assets/dolphin/external/L2_Dj_128x64/frame_11.png new file mode 100644 index 00000000000..eca4a1296ee Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_12.png b/assets/dolphin/external/L2_Dj_128x64/frame_12.png new file mode 100644 index 00000000000..5f92e47fdd7 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_13.png b/assets/dolphin/external/L2_Dj_128x64/frame_13.png new file mode 100644 index 00000000000..1b1017ce259 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_14.png b/assets/dolphin/external/L2_Dj_128x64/frame_14.png new file mode 100644 index 00000000000..2cf408c979c Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_15.png b/assets/dolphin/external/L2_Dj_128x64/frame_15.png new file mode 100644 index 00000000000..9b796498c0c Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_16.png b/assets/dolphin/external/L2_Dj_128x64/frame_16.png new file mode 100644 index 00000000000..c1545500cbc Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_17.png b/assets/dolphin/external/L2_Dj_128x64/frame_17.png new file mode 100644 index 00000000000..80863f0b693 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_18.png b/assets/dolphin/external/L2_Dj_128x64/frame_18.png new file mode 100644 index 00000000000..b4527bc8330 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_19.png b/assets/dolphin/external/L2_Dj_128x64/frame_19.png new file mode 100644 index 00000000000..f1531da4ef7 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_2.png b/assets/dolphin/external/L2_Dj_128x64/frame_2.png new file mode 100644 index 00000000000..391cfe1e103 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_20.png b/assets/dolphin/external/L2_Dj_128x64/frame_20.png new file mode 100644 index 00000000000..f63904f29b2 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_21.png b/assets/dolphin/external/L2_Dj_128x64/frame_21.png new file mode 100644 index 00000000000..076448fa940 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_21.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_22.png b/assets/dolphin/external/L2_Dj_128x64/frame_22.png new file mode 100644 index 00000000000..8651f12f8bd Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_22.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_23.png b/assets/dolphin/external/L2_Dj_128x64/frame_23.png new file mode 100644 index 00000000000..d2d8e7e5144 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_23.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_24.png b/assets/dolphin/external/L2_Dj_128x64/frame_24.png new file mode 100644 index 00000000000..6080e7aa3f9 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_24.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_25.png b/assets/dolphin/external/L2_Dj_128x64/frame_25.png new file mode 100644 index 00000000000..ec483addf8c Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_25.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_26.png b/assets/dolphin/external/L2_Dj_128x64/frame_26.png new file mode 100644 index 00000000000..90fc3b1380c Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_26.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_27.png b/assets/dolphin/external/L2_Dj_128x64/frame_27.png new file mode 100644 index 00000000000..39ddf46ab3b Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_27.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_28.png b/assets/dolphin/external/L2_Dj_128x64/frame_28.png new file mode 100644 index 00000000000..f433c969c61 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_28.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_29.png b/assets/dolphin/external/L2_Dj_128x64/frame_29.png new file mode 100644 index 00000000000..263ffe15a83 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_29.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_3.png b/assets/dolphin/external/L2_Dj_128x64/frame_3.png new file mode 100644 index 00000000000..40d1314c95f Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_30.png b/assets/dolphin/external/L2_Dj_128x64/frame_30.png new file mode 100644 index 00000000000..27c297e8d59 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_30.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_31.png b/assets/dolphin/external/L2_Dj_128x64/frame_31.png new file mode 100644 index 00000000000..f2aefbfae2f Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_31.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_32.png b/assets/dolphin/external/L2_Dj_128x64/frame_32.png new file mode 100644 index 00000000000..852c24233ff Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_32.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_33.png b/assets/dolphin/external/L2_Dj_128x64/frame_33.png new file mode 100644 index 00000000000..665ac5eafd8 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_33.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_34.png b/assets/dolphin/external/L2_Dj_128x64/frame_34.png new file mode 100644 index 00000000000..81f133ac559 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_34.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_35.png b/assets/dolphin/external/L2_Dj_128x64/frame_35.png new file mode 100644 index 00000000000..c828207d33e Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_35.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_36.png b/assets/dolphin/external/L2_Dj_128x64/frame_36.png new file mode 100644 index 00000000000..fc923b40236 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_36.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_4.png b/assets/dolphin/external/L2_Dj_128x64/frame_4.png new file mode 100644 index 00000000000..d372ff643b4 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_5.png b/assets/dolphin/external/L2_Dj_128x64/frame_5.png new file mode 100644 index 00000000000..5b52f95175b Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_6.png b/assets/dolphin/external/L2_Dj_128x64/frame_6.png new file mode 100644 index 00000000000..8a1e84a11ee Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_7.png b/assets/dolphin/external/L2_Dj_128x64/frame_7.png new file mode 100644 index 00000000000..1fddffaa495 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_8.png b/assets/dolphin/external/L2_Dj_128x64/frame_8.png new file mode 100644 index 00000000000..14ef1aded64 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_9.png b/assets/dolphin/external/L2_Dj_128x64/frame_9.png new file mode 100644 index 00000000000..05de5d5c698 Binary files /dev/null and b/assets/dolphin/external/L2_Dj_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L2_Dj_128x64/meta.txt b/assets/dolphin/external/L2_Dj_128x64/meta.txt new file mode 100644 index 00000000000..2d0693ef2cf --- /dev/null +++ b/assets/dolphin/external/L2_Dj_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 15 +Active frames: 23 +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 26 28 29 30 31 32 33 34 35 36 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 5 + +Bubble slots: 0 \ No newline at end of file diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_0.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_0.png new file mode 100644 index 00000000000..8f29d5e2c70 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_1.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_1.png new file mode 100644 index 00000000000..f0756ff855c Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_10.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_10.png new file mode 100644 index 00000000000..03d95e2af9a Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_11.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_11.png new file mode 100644 index 00000000000..e4cf6ea8d55 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_12.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_12.png new file mode 100644 index 00000000000..6a7fe1bbba1 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_13.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_13.png new file mode 100644 index 00000000000..38171c273ca Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_14.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_14.png new file mode 100644 index 00000000000..3eb9a1f6327 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_15.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_15.png new file mode 100644 index 00000000000..6244074889e Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_16.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_16.png new file mode 100644 index 00000000000..f1306c2eb4a Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_17.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_17.png new file mode 100644 index 00000000000..ed8d96bab11 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_18.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_18.png new file mode 100644 index 00000000000..e1df3ea495e Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_19.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_19.png new file mode 100644 index 00000000000..d231a656181 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_2.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_2.png new file mode 100644 index 00000000000..e2a864c7625 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_20.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_20.png new file mode 100644 index 00000000000..ddf9d493102 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_21.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_21.png new file mode 100644 index 00000000000..0d407de64bf Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_21.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_22.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_22.png new file mode 100644 index 00000000000..f53ee058835 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_22.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_23.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_23.png new file mode 100644 index 00000000000..83be68d8467 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_23.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_24.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_24.png new file mode 100644 index 00000000000..ae61e5831c4 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_24.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_25.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_25.png new file mode 100644 index 00000000000..a2608a0e1b7 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_25.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_26.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_26.png new file mode 100644 index 00000000000..6602e3f5788 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_26.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_27.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_27.png new file mode 100644 index 00000000000..e82d7fcd14f Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_27.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_28.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_28.png new file mode 100644 index 00000000000..b16dda91d58 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_28.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_29.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_29.png new file mode 100644 index 00000000000..cf91009bf35 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_29.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_3.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_3.png new file mode 100644 index 00000000000..8bfdcabda93 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_30.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_30.png new file mode 100644 index 00000000000..08074849841 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_30.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_31.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_31.png new file mode 100644 index 00000000000..d4dfbce33f3 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_31.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_32.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_32.png new file mode 100644 index 00000000000..c9c169f2eb4 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_32.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_33.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_33.png new file mode 100644 index 00000000000..0e939aa2d4e Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_33.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_34.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_34.png new file mode 100644 index 00000000000..d0f3a5383d8 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_34.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_35.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_35.png new file mode 100644 index 00000000000..2e3a3c64fb1 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_35.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_36.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_36.png new file mode 100644 index 00000000000..4b34fe377c1 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_36.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_37.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_37.png new file mode 100644 index 00000000000..4935b75a6f3 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_37.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_38.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_38.png new file mode 100644 index 00000000000..ccf5bdf09e3 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_38.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_39.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_39.png new file mode 100644 index 00000000000..d276c21233c Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_39.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_4.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_4.png new file mode 100644 index 00000000000..354a96b6c5b Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_40.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_40.png new file mode 100644 index 00000000000..873386a57f9 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_40.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_41.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_41.png new file mode 100644 index 00000000000..529f6cdd455 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_41.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_42.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_42.png new file mode 100644 index 00000000000..15168cd1112 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_42.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_43.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_43.png new file mode 100644 index 00000000000..16196a7062f Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_43.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_44.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_44.png new file mode 100644 index 00000000000..06fb2790559 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_44.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_45.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_45.png new file mode 100644 index 00000000000..5a2221cb928 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_45.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_46.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_46.png new file mode 100644 index 00000000000..7db77e935fe Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_46.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_47.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_47.png new file mode 100644 index 00000000000..a6d5407ad0f Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_47.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_48.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_48.png new file mode 100644 index 00000000000..c6a17fda8a4 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_48.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_49.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_49.png new file mode 100644 index 00000000000..0e6845ac244 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_49.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_5.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_5.png new file mode 100644 index 00000000000..45decf285fb Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_50.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_50.png new file mode 100644 index 00000000000..aa121a538c3 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_50.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_51.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_51.png new file mode 100644 index 00000000000..38184aa75e3 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_51.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_52.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_52.png new file mode 100644 index 00000000000..1259a591a4b Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_52.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_6.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_6.png new file mode 100644 index 00000000000..cd916c40406 Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_7.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_7.png new file mode 100644 index 00000000000..d28bb5454dc Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_8.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_8.png new file mode 100644 index 00000000000..3621243f71f Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/frame_9.png b/assets/dolphin/external/L2_Secret_door_128x64/frame_9.png new file mode 100644 index 00000000000..4d3d537162c Binary files /dev/null and b/assets/dolphin/external/L2_Secret_door_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L2_Secret_door_128x64/meta.txt b/assets/dolphin/external/L2_Secret_door_128x64/meta.txt new file mode 100644 index 00000000000..4091b12769a --- /dev/null +++ b/assets/dolphin/external/L2_Secret_door_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 29 +Active frames: 24 +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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 0 diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_0.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_0.png new file mode 100644 index 00000000000..91209003253 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_1.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_1.png new file mode 100644 index 00000000000..0b99a32ff9c Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_10.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_10.png new file mode 100644 index 00000000000..9da72ac1dab Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_11.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_11.png new file mode 100644 index 00000000000..8d54da6c61b Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_12.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_12.png new file mode 100644 index 00000000000..84046a46c8f Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_13.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_13.png new file mode 100644 index 00000000000..3e1c9c329ab Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_14.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_14.png new file mode 100644 index 00000000000..f4f6ccd661e Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_15.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_15.png new file mode 100644 index 00000000000..5dc1a652563 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_16.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_16.png new file mode 100644 index 00000000000..bec472921c8 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_17.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_17.png new file mode 100644 index 00000000000..82e5176c891 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_18.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_18.png new file mode 100644 index 00000000000..3b5e60dfdb9 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_19.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_19.png new file mode 100644 index 00000000000..5f76c7d2374 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_2.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_2.png new file mode 100644 index 00000000000..84d6aaf3533 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_20.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_20.png new file mode 100644 index 00000000000..2f8394fd518 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_3.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_3.png new file mode 100644 index 00000000000..48adde113a3 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_4.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_4.png new file mode 100644 index 00000000000..5889835b7b1 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_5.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_5.png new file mode 100644 index 00000000000..7f980f57f8a Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_6.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_6.png new file mode 100644 index 00000000000..49713600085 Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_7.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_7.png new file mode 100644 index 00000000000..03d67134aac Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_8.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_8.png new file mode 100644 index 00000000000..9f523caceed Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_9.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_9.png new file mode 100644 index 00000000000..5a565b1af9c Binary files /dev/null and b/assets/dolphin/external/L2_Wake_up_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L2_Wake_up_128x64/meta.txt b/assets/dolphin/external/L2_Wake_up_128x64/meta.txt new file mode 100644 index 00000000000..06c710f0390 --- /dev/null +++ b/assets/dolphin/external/L2_Wake_up_128x64/meta.txt @@ -0,0 +1,14 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 10 +Active frames: 18 +Frames order: 0 1 0 1 0 1 0 2 3 4 0 5 6 7 8 9 10 11 10 12 13 14 15 16 17 18 19 20 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 0 \ No newline at end of file diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt index 197060672e2..d7348b25cdd 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -36,20 +36,6 @@ Min level: 1 Max level: 1 Weight: 3 -Name: L2_Furippa2_128x64 -Min butthurt: 0 -Max butthurt: 6 -Min level: 2 -Max level: 2 -Weight: 3 - -Name: L3_Furippa3_128x64 -Min butthurt: 0 -Max butthurt: 6 -Min level: 3 -Max level: 3 -Weight: 3 - Name: L1_Read_books_128x64 Min butthurt: 0 Max butthurt: 8 @@ -57,13 +43,6 @@ Min level: 1 Max level: 1 Weight: 3 -Name: L2_Hacking_pc_128x64 -Min butthurt: 0 -Max butthurt: 8 -Min level: 2 -Max level: 2 -Weight: 3 - Name: L1_Cry_128x64 Min butthurt: 8 Max butthurt: 13 @@ -85,20 +64,69 @@ Min level: 1 Max level: 3 Weight: 3 -Name: L3_Hijack_radio_128x64 +Name: L1_Mods_128x64 Min butthurt: 0 -Max butthurt: 8 -Min level: 3 +Max butthurt: 9 +Min level: 1 Max level: 3 Weight: 3 -Name: L3_Lab_research_128x54 +Name: L1_Painting_128x64 +Min butthurt: 0 +Max butthurt: 7 +Min level: 1 +Max level: 3 +Weight: 3 + +Name: L1_Leaving_sad_128x64 +Min butthurt: 14 +Max butthurt: 14 +Min level: 1 +Max level: 3 +Weight: 3 + +Name: L1_Senpai_128x64 +Min butthurt: 0 +Max butthurt: 5 +Min level: 1 +Max level: 3 +Weight: 3 + +Name: L1_Kaiju_128x64 Min butthurt: 0 Max butthurt: 10 -Min level: 3 +Min level: 1 Max level: 3 Weight: 3 +Name: L1_My_dude_128x64 +Min butthurt: 0 +Max butthurt: 8 +Min level: 1 +Max level: 3 +Weight: 4 + +Name: L2_Wake_up_128x64 +Min butthurt: 0 +Max butthurt: 12 +Min level: 2 +Max level: 3 +Weight: 3 + +Name: L2_Furippa2_128x64 +Min butthurt: 0 +Max butthurt: 6 +Min level: 2 +Max level: 2 +Weight: 3 + +Name: L2_Hacking_pc_128x64 +Min butthurt: 0 +Max butthurt: 8 +Min level: 2 +Max level: 2 +Weight: 3 + Name: L2_Soldering_128x64 Min butthurt: 0 Max butthurt: 10 @@ -106,9 +134,51 @@ Min level: 2 Max level: 2 Weight: 3 -Name: L1_Leaving_sad_128x64 -Min butthurt: 14 -Max butthurt: 14 -Min level: 1 +Name: L2_Dj_128x64 +Min butthurt: 0 +Max butthurt: 8 +Min level: 2 +Max level: 3 +Weight: 4 + +Name: L3_Furippa3_128x64 +Min butthurt: 0 +Max butthurt: 6 +Min level: 3 Max level: 3 Weight: 3 + +Name: L3_Hijack_radio_128x64 +Min butthurt: 0 +Max butthurt: 8 +Min level: 3 +Max level: 3 +Weight: 3 + +Name: L3_Lab_research_128x54 +Min butthurt: 0 +Max butthurt: 10 +Min level: 3 +Max level: 3 +Weight: 3 + +Name: L1_Sad_song_128x64 +Min butthurt: 8 +Max butthurt: 13 +Min level: 1 +Max level: 3 +Weight: 4 + +Name: L2_Coding_in_the_shell_128x64 +Min butthurt: 0 +Max butthurt: 12 +Min level: 2 +Max level: 3 +Weight: 4 + +Name: L2_Secret_door_128x64 +Min butthurt: 0 +Max butthurt: 12 +Min level: 2 +Max level: 3 +Weight: 4 diff --git a/assets/dolphin/internal/L1_NoSd_128x49/frame_0.png b/assets/dolphin/internal/L1_NoSd_128x49/frame_0.png index 5104afb85f5..9a48d15f7a6 100644 Binary files a/assets/dolphin/internal/L1_NoSd_128x49/frame_0.png and b/assets/dolphin/internal/L1_NoSd_128x49/frame_0.png differ diff --git a/assets/dolphin/internal/L1_NoSd_128x49/frame_1.png b/assets/dolphin/internal/L1_NoSd_128x49/frame_1.png index 2b08366d6c4..b58936d817d 100644 Binary files a/assets/dolphin/internal/L1_NoSd_128x49/frame_1.png and b/assets/dolphin/internal/L1_NoSd_128x49/frame_1.png differ diff --git a/assets/dolphin/internal/L1_NoSd_128x49/frame_2.png b/assets/dolphin/internal/L1_NoSd_128x49/frame_2.png index 12dc1343090..5b474fff262 100644 Binary files a/assets/dolphin/internal/L1_NoSd_128x49/frame_2.png and b/assets/dolphin/internal/L1_NoSd_128x49/frame_2.png differ diff --git a/assets/dolphin/internal/L1_NoSd_128x49/frame_3.png b/assets/dolphin/internal/L1_NoSd_128x49/frame_3.png index f17f8713d34..952f968fb67 100644 Binary files a/assets/dolphin/internal/L1_NoSd_128x49/frame_3.png and b/assets/dolphin/internal/L1_NoSd_128x49/frame_3.png differ diff --git a/assets/dolphin/internal/L1_NoSd_128x49/frame_4.png b/assets/dolphin/internal/L1_NoSd_128x49/frame_4.png index 7a6992a2b04..2bb43b306f3 100644 Binary files a/assets/dolphin/internal/L1_NoSd_128x49/frame_4.png and b/assets/dolphin/internal/L1_NoSd_128x49/frame_4.png differ diff --git a/assets/dolphin/internal/L1_NoSd_128x49/frame_5.png b/assets/dolphin/internal/L1_NoSd_128x49/frame_5.png index 00d9843007e..d7f8c6402a2 100644 Binary files a/assets/dolphin/internal/L1_NoSd_128x49/frame_5.png and b/assets/dolphin/internal/L1_NoSd_128x49/frame_5.png differ diff --git a/assets/icons/About/Certification2_46x33.png b/assets/icons/About/Certification2_46x33.png new file mode 100644 index 00000000000..d421b829149 Binary files /dev/null and b/assets/icons/About/Certification2_46x33.png differ diff --git a/assets/icons/About/Certification2_98x33.png b/assets/icons/About/Certification2_98x33.png deleted file mode 100644 index 49c5581c752..00000000000 Binary files a/assets/icons/About/Certification2_98x33.png and /dev/null differ diff --git a/assets/icons/Archive/keyboard_10px.png b/assets/icons/Archive/keyboard_10px.png new file mode 100644 index 00000000000..74a10e6db2e Binary files /dev/null and b/assets/icons/Archive/keyboard_10px.png differ diff --git a/assets/icons/BLE/BLE_HID/Ble_connected_15x15.png b/assets/icons/BLE/BLE_HID/Ble_connected_15x15.png index 58776828ed4..64dab9b5307 100644 Binary files a/assets/icons/BLE/BLE_HID/Ble_connected_15x15.png and b/assets/icons/BLE/BLE_HID/Ble_connected_15x15.png differ diff --git a/assets/icons/BLE/BLE_HID/Ble_disconnected_15x15.png b/assets/icons/BLE/BLE_HID/Ble_disconnected_15x15.png index bfc1e7d7f18..0858bb93f43 100644 Binary files a/assets/icons/BLE/BLE_HID/Ble_disconnected_15x15.png and b/assets/icons/BLE/BLE_HID/Ble_disconnected_15x15.png differ diff --git a/assets/icons/BLE/BLE_HID/Left_mouse_icon_9x9.png b/assets/icons/BLE/BLE_HID/Left_mouse_icon_9x9.png new file mode 100644 index 00000000000..c533d85729f Binary files /dev/null and b/assets/icons/BLE/BLE_HID/Left_mouse_icon_9x9.png differ diff --git a/assets/icons/BLE/BLE_HID/Ok_btn_pressed_13x13.png b/assets/icons/BLE/BLE_HID/Ok_btn_pressed_13x13.png new file mode 100644 index 00000000000..6b46ba3a824 Binary files /dev/null and b/assets/icons/BLE/BLE_HID/Ok_btn_pressed_13x13.png differ diff --git a/assets/icons/BLE/BLE_HID/Right_mouse_icon_9x9.png b/assets/icons/BLE/BLE_HID/Right_mouse_icon_9x9.png new file mode 100644 index 00000000000..446d7176c8d Binary files /dev/null and b/assets/icons/BLE/BLE_HID/Right_mouse_icon_9x9.png differ diff --git a/assets/icons/Common/Hashmark_7x7.png b/assets/icons/Common/Hashmark_7x7.png new file mode 100644 index 00000000000..93fb147be23 Binary files /dev/null and b/assets/icons/Common/Hashmark_7x7.png differ diff --git a/assets/icons/Common/More_data_placeholder_5x7.png b/assets/icons/Common/More_data_placeholder_5x7.png new file mode 100644 index 00000000000..85025d9f0ac Binary files /dev/null and b/assets/icons/Common/More_data_placeholder_5x7.png differ diff --git a/assets/icons/Common/Round_loader_8x8/frame_01.png b/assets/icons/Common/Round_loader_8x8/frame_01.png new file mode 100644 index 00000000000..a5dc239d85e Binary files /dev/null and b/assets/icons/Common/Round_loader_8x8/frame_01.png differ diff --git a/assets/icons/Common/Round_loader_8x8/frame_02.png b/assets/icons/Common/Round_loader_8x8/frame_02.png new file mode 100644 index 00000000000..162d8a8f42a Binary files /dev/null and b/assets/icons/Common/Round_loader_8x8/frame_02.png differ diff --git a/assets/icons/Common/Round_loader_8x8/frame_03.png b/assets/icons/Common/Round_loader_8x8/frame_03.png new file mode 100644 index 00000000000..5483e473456 Binary files /dev/null and b/assets/icons/Common/Round_loader_8x8/frame_03.png differ diff --git a/assets/icons/Common/Round_loader_8x8/frame_04.png b/assets/icons/Common/Round_loader_8x8/frame_04.png new file mode 100644 index 00000000000..ce2fbbd476d Binary files /dev/null and b/assets/icons/Common/Round_loader_8x8/frame_04.png differ diff --git a/assets/icons/Common/Round_loader_8x8/frame_05.png b/assets/icons/Common/Round_loader_8x8/frame_05.png new file mode 100644 index 00000000000..8b786c0293a Binary files /dev/null and b/assets/icons/Common/Round_loader_8x8/frame_05.png differ diff --git a/assets/icons/Common/Round_loader_8x8/frame_rate b/assets/icons/Common/Round_loader_8x8/frame_rate new file mode 100644 index 00000000000..d8263ee9860 --- /dev/null +++ b/assets/icons/Common/Round_loader_8x8/frame_rate @@ -0,0 +1 @@ +2 \ No newline at end of file diff --git a/assets/icons/Common/arrow_nano_down.png b/assets/icons/Common/arrow_nano_down.png new file mode 100644 index 00000000000..da66350bd1c Binary files /dev/null and b/assets/icons/Common/arrow_nano_down.png differ diff --git a/assets/icons/Common/arrow_nano_up.png b/assets/icons/Common/arrow_nano_up.png new file mode 100644 index 00000000000..4a1d5be85cf Binary files /dev/null and b/assets/icons/Common/arrow_nano_up.png differ diff --git a/assets/icons/ErasePin/Erase_pin_128x64.png b/assets/icons/ErasePin/Erase_pin_128x64.png new file mode 100644 index 00000000000..92ca5f91cbb Binary files /dev/null and b/assets/icons/ErasePin/Erase_pin_128x64.png differ diff --git a/assets/icons/GPIO/ArrowDownEmpty_14x15.png b/assets/icons/GPIO/ArrowDownEmpty_14x15.png deleted file mode 100644 index 8c6d54f9cbe..00000000000 Binary files a/assets/icons/GPIO/ArrowDownEmpty_14x15.png and /dev/null differ diff --git a/assets/icons/GPIO/ArrowDownFilled_14x15.png b/assets/icons/GPIO/ArrowDownFilled_14x15.png deleted file mode 100644 index 6cef0f4a79a..00000000000 Binary files a/assets/icons/GPIO/ArrowDownFilled_14x15.png and /dev/null differ diff --git a/assets/icons/Infrared/Down_25x27.png b/assets/icons/Infrared/Down_25x27.png deleted file mode 100644 index c1309777866..00000000000 Binary files a/assets/icons/Infrared/Down_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Down_hvr_25x27.png b/assets/icons/Infrared/Down_hvr_25x27.png deleted file mode 100644 index 76d18192414..00000000000 Binary files a/assets/icons/Infrared/Down_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Mute_25x27.png b/assets/icons/Infrared/Mute_25x27.png deleted file mode 100644 index d8812dd4fba..00000000000 Binary files a/assets/icons/Infrared/Mute_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Mute_hvr_25x27.png b/assets/icons/Infrared/Mute_hvr_25x27.png deleted file mode 100644 index 155bd900438..00000000000 Binary files a/assets/icons/Infrared/Mute_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Power_25x27.png b/assets/icons/Infrared/Power_25x27.png deleted file mode 100644 index 5ae493fbedf..00000000000 Binary files a/assets/icons/Infrared/Power_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Power_hvr_25x27.png b/assets/icons/Infrared/Power_hvr_25x27.png deleted file mode 100644 index 9425072c0c3..00000000000 Binary files a/assets/icons/Infrared/Power_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Up_25x27.png b/assets/icons/Infrared/Up_25x27.png deleted file mode 100644 index b81a02e8adb..00000000000 Binary files a/assets/icons/Infrared/Up_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Up_hvr_25x27.png b/assets/icons/Infrared/Up_hvr_25x27.png deleted file mode 100644 index cf71e59655a..00000000000 Binary files a/assets/icons/Infrared/Up_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Vol_down_25x27.png b/assets/icons/Infrared/Vol_down_25x27.png deleted file mode 100644 index d7ae4455822..00000000000 Binary files a/assets/icons/Infrared/Vol_down_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Vol_down_hvr_25x27.png b/assets/icons/Infrared/Vol_down_hvr_25x27.png deleted file mode 100644 index c556a037af9..00000000000 Binary files a/assets/icons/Infrared/Vol_down_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Vol_up_25x27.png b/assets/icons/Infrared/Vol_up_25x27.png deleted file mode 100644 index c4d9e87a065..00000000000 Binary files a/assets/icons/Infrared/Vol_up_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Vol_up_hvr_25x27.png b/assets/icons/Infrared/Vol_up_hvr_25x27.png deleted file mode 100644 index 90c2df47d25..00000000000 Binary files a/assets/icons/Infrared/Vol_up_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/back_btn_10x8.png b/assets/icons/Infrared/back_btn_10x8.png new file mode 100644 index 00000000000..10d7beb3083 Binary files /dev/null and b/assets/icons/Infrared/back_btn_10x8.png differ diff --git a/assets/icons/Infrared/celsius_24x23.png b/assets/icons/Infrared/celsius_24x23.png new file mode 100644 index 00000000000..64d7a1db15f Binary files /dev/null and b/assets/icons/Infrared/celsius_24x23.png differ diff --git a/assets/icons/Infrared/celsius_hover_24x23.png b/assets/icons/Infrared/celsius_hover_24x23.png new file mode 100644 index 00000000000..0488b40f571 Binary files /dev/null and b/assets/icons/Infrared/celsius_hover_24x23.png differ diff --git a/assets/icons/Infrared/ch_down_24x21.png b/assets/icons/Infrared/ch_down_24x21.png new file mode 100644 index 00000000000..8c3f81c3d09 Binary files /dev/null and b/assets/icons/Infrared/ch_down_24x21.png differ diff --git a/assets/icons/Infrared/ch_down_hover_24x21.png b/assets/icons/Infrared/ch_down_hover_24x21.png new file mode 100644 index 00000000000..9b840f9ce30 Binary files /dev/null and b/assets/icons/Infrared/ch_down_hover_24x21.png differ diff --git a/assets/icons/Infrared/ch_text_31x34.png b/assets/icons/Infrared/ch_text_31x34.png new file mode 100644 index 00000000000..30e0f584c2e Binary files /dev/null and b/assets/icons/Infrared/ch_text_31x34.png differ diff --git a/assets/icons/Infrared/ch_up_24x21.png b/assets/icons/Infrared/ch_up_24x21.png new file mode 100644 index 00000000000..fa4074d12b4 Binary files /dev/null and b/assets/icons/Infrared/ch_up_24x21.png differ diff --git a/assets/icons/Infrared/ch_up_hover_24x21.png b/assets/icons/Infrared/ch_up_hover_24x21.png new file mode 100644 index 00000000000..944a973f4a4 Binary files /dev/null and b/assets/icons/Infrared/ch_up_hover_24x21.png differ diff --git a/assets/icons/Infrared/cool_30x51.png b/assets/icons/Infrared/cool_30x51.png new file mode 100644 index 00000000000..38a8014bd6a Binary files /dev/null and b/assets/icons/Infrared/cool_30x51.png differ diff --git a/assets/icons/Infrared/dry_19x20.png b/assets/icons/Infrared/dry_19x20.png new file mode 100644 index 00000000000..c689c067585 Binary files /dev/null and b/assets/icons/Infrared/dry_19x20.png differ diff --git a/assets/icons/Infrared/dry_hover_19x20.png b/assets/icons/Infrared/dry_hover_19x20.png new file mode 100644 index 00000000000..5b7196ae221 Binary files /dev/null and b/assets/icons/Infrared/dry_hover_19x20.png differ diff --git a/assets/icons/Infrared/dry_text_15x5.png b/assets/icons/Infrared/dry_text_15x5.png new file mode 100644 index 00000000000..7696e1fc8a4 Binary files /dev/null and b/assets/icons/Infrared/dry_text_15x5.png differ diff --git a/assets/icons/Infrared/fahren_24x23.png b/assets/icons/Infrared/fahren_24x23.png new file mode 100644 index 00000000000..d6f55e806ee Binary files /dev/null and b/assets/icons/Infrared/fahren_24x23.png differ diff --git a/assets/icons/Infrared/fahren_hover_24x23.png b/assets/icons/Infrared/fahren_hover_24x23.png new file mode 100644 index 00000000000..db922c5576f Binary files /dev/null and b/assets/icons/Infrared/fahren_hover_24x23.png differ diff --git a/assets/icons/Infrared/heat_30x51.png b/assets/icons/Infrared/heat_30x51.png new file mode 100644 index 00000000000..aca27c7c896 Binary files /dev/null and b/assets/icons/Infrared/heat_30x51.png differ diff --git a/assets/icons/Infrared/hourglass0_24x24.png b/assets/icons/Infrared/hourglass0_24x24.png new file mode 100644 index 00000000000..a382d84e215 Binary files /dev/null and b/assets/icons/Infrared/hourglass0_24x24.png differ diff --git a/assets/icons/Infrared/hourglass1_24x24.png b/assets/icons/Infrared/hourglass1_24x24.png new file mode 100644 index 00000000000..b4cc7b4623c Binary files /dev/null and b/assets/icons/Infrared/hourglass1_24x24.png differ diff --git a/assets/icons/Infrared/hourglass2_24x24.png b/assets/icons/Infrared/hourglass2_24x24.png new file mode 100644 index 00000000000..d2c3709f704 Binary files /dev/null and b/assets/icons/Infrared/hourglass2_24x24.png differ diff --git a/assets/icons/Infrared/hourglass3_24x24.png b/assets/icons/Infrared/hourglass3_24x24.png new file mode 100644 index 00000000000..e7be1e99557 Binary files /dev/null and b/assets/icons/Infrared/hourglass3_24x24.png differ diff --git a/assets/icons/Infrared/hourglass4_24x24.png b/assets/icons/Infrared/hourglass4_24x24.png new file mode 100644 index 00000000000..49eee2f53a2 Binary files /dev/null and b/assets/icons/Infrared/hourglass4_24x24.png differ diff --git a/assets/icons/Infrared/hourglass5_24x24.png b/assets/icons/Infrared/hourglass5_24x24.png new file mode 100644 index 00000000000..90e1d4b4e7c Binary files /dev/null and b/assets/icons/Infrared/hourglass5_24x24.png differ diff --git a/assets/icons/Infrared/hourglass6_24x24.png b/assets/icons/Infrared/hourglass6_24x24.png new file mode 100644 index 00000000000..e68c744f0c4 Binary files /dev/null and b/assets/icons/Infrared/hourglass6_24x24.png differ diff --git a/assets/icons/Infrared/max_24x23.png b/assets/icons/Infrared/max_24x23.png new file mode 100644 index 00000000000..d4163a65f88 Binary files /dev/null and b/assets/icons/Infrared/max_24x23.png differ diff --git a/assets/icons/Infrared/max_hover_24x23.png b/assets/icons/Infrared/max_hover_24x23.png new file mode 100644 index 00000000000..65f97b0ce3a Binary files /dev/null and b/assets/icons/Infrared/max_hover_24x23.png differ diff --git a/assets/icons/Infrared/mute_19x20.png b/assets/icons/Infrared/mute_19x20.png new file mode 100644 index 00000000000..410e88ac2ed Binary files /dev/null and b/assets/icons/Infrared/mute_19x20.png differ diff --git a/assets/icons/Infrared/mute_hover_19x20.png b/assets/icons/Infrared/mute_hover_19x20.png new file mode 100644 index 00000000000..e9a5b351027 Binary files /dev/null and b/assets/icons/Infrared/mute_hover_19x20.png differ diff --git a/assets/icons/Infrared/mute_text_19x5.png b/assets/icons/Infrared/mute_text_19x5.png new file mode 100644 index 00000000000..fa2d042a676 Binary files /dev/null and b/assets/icons/Infrared/mute_text_19x5.png differ diff --git a/assets/icons/Infrared/next_19x20.png b/assets/icons/Infrared/next_19x20.png new file mode 100644 index 00000000000..512b68745ac Binary files /dev/null and b/assets/icons/Infrared/next_19x20.png differ diff --git a/assets/icons/Infrared/next_hover_19x20.png b/assets/icons/Infrared/next_hover_19x20.png new file mode 100644 index 00000000000..c84bfdb901b Binary files /dev/null and b/assets/icons/Infrared/next_hover_19x20.png differ diff --git a/assets/icons/Infrared/next_text_19x6.png b/assets/icons/Infrared/next_text_19x6.png new file mode 100644 index 00000000000..74d53171f56 Binary files /dev/null and b/assets/icons/Infrared/next_text_19x6.png differ diff --git a/assets/icons/Infrared/off_19x20.png b/assets/icons/Infrared/off_19x20.png new file mode 100644 index 00000000000..6d68d7e6e15 Binary files /dev/null and b/assets/icons/Infrared/off_19x20.png differ diff --git a/assets/icons/Infrared/off_hover_19x20.png b/assets/icons/Infrared/off_hover_19x20.png new file mode 100644 index 00000000000..fddd3f91723 Binary files /dev/null and b/assets/icons/Infrared/off_hover_19x20.png differ diff --git a/assets/icons/Infrared/off_text_12x5.png b/assets/icons/Infrared/off_text_12x5.png new file mode 100644 index 00000000000..500adbf27e7 Binary files /dev/null and b/assets/icons/Infrared/off_text_12x5.png differ diff --git a/assets/icons/Infrared/pause_19x20.png b/assets/icons/Infrared/pause_19x20.png new file mode 100644 index 00000000000..99196d23b5a Binary files /dev/null and b/assets/icons/Infrared/pause_19x20.png differ diff --git a/assets/icons/Infrared/pause_hover_19x20.png b/assets/icons/Infrared/pause_hover_19x20.png new file mode 100644 index 00000000000..33e7d8eb21c Binary files /dev/null and b/assets/icons/Infrared/pause_hover_19x20.png differ diff --git a/assets/icons/Infrared/pause_text_23x5.png b/assets/icons/Infrared/pause_text_23x5.png new file mode 100644 index 00000000000..72c7b04036f Binary files /dev/null and b/assets/icons/Infrared/pause_text_23x5.png differ diff --git a/assets/icons/Infrared/play_19x20.png b/assets/icons/Infrared/play_19x20.png new file mode 100644 index 00000000000..880e977d2e7 Binary files /dev/null and b/assets/icons/Infrared/play_19x20.png differ diff --git a/assets/icons/Infrared/play_hover_19x20.png b/assets/icons/Infrared/play_hover_19x20.png new file mode 100644 index 00000000000..4c837a1445f Binary files /dev/null and b/assets/icons/Infrared/play_hover_19x20.png differ diff --git a/assets/icons/Infrared/play_text_19x5.png b/assets/icons/Infrared/play_text_19x5.png new file mode 100644 index 00000000000..c5f067bcf4f Binary files /dev/null and b/assets/icons/Infrared/play_text_19x5.png differ diff --git a/assets/icons/Infrared/power_19x20.png b/assets/icons/Infrared/power_19x20.png new file mode 100644 index 00000000000..12b92797397 Binary files /dev/null and b/assets/icons/Infrared/power_19x20.png differ diff --git a/assets/icons/Infrared/power_hover_19x20.png b/assets/icons/Infrared/power_hover_19x20.png new file mode 100644 index 00000000000..3a41249ff30 Binary files /dev/null and b/assets/icons/Infrared/power_hover_19x20.png differ diff --git a/assets/icons/Infrared/power_text_24x5.png b/assets/icons/Infrared/power_text_24x5.png new file mode 100644 index 00000000000..88fff8e33d2 Binary files /dev/null and b/assets/icons/Infrared/power_text_24x5.png differ diff --git a/assets/icons/Infrared/prev_19x20.png b/assets/icons/Infrared/prev_19x20.png new file mode 100644 index 00000000000..8d17cec57c5 Binary files /dev/null and b/assets/icons/Infrared/prev_19x20.png differ diff --git a/assets/icons/Infrared/prev_hover_19x20.png b/assets/icons/Infrared/prev_hover_19x20.png new file mode 100644 index 00000000000..be9dce7004e Binary files /dev/null and b/assets/icons/Infrared/prev_hover_19x20.png differ diff --git a/assets/icons/Infrared/prev_text_19x5.png b/assets/icons/Infrared/prev_text_19x5.png new file mode 100644 index 00000000000..473b8974560 Binary files /dev/null and b/assets/icons/Infrared/prev_text_19x5.png differ diff --git a/assets/icons/Infrared/vol_ac_text_30x30.png b/assets/icons/Infrared/vol_ac_text_30x30.png new file mode 100644 index 00000000000..068266d6245 Binary files /dev/null and b/assets/icons/Infrared/vol_ac_text_30x30.png differ diff --git a/assets/icons/Infrared/vol_tv_text_29x34.png b/assets/icons/Infrared/vol_tv_text_29x34.png new file mode 100644 index 00000000000..caef54c2584 Binary files /dev/null and b/assets/icons/Infrared/vol_tv_text_29x34.png differ diff --git a/assets/icons/Infrared/voldown_24x21.png b/assets/icons/Infrared/voldown_24x21.png new file mode 100644 index 00000000000..a80c59594af Binary files /dev/null and b/assets/icons/Infrared/voldown_24x21.png differ diff --git a/assets/icons/Infrared/voldown_hover_24x21.png b/assets/icons/Infrared/voldown_hover_24x21.png new file mode 100644 index 00000000000..6bc57c70eb5 Binary files /dev/null and b/assets/icons/Infrared/voldown_hover_24x21.png differ diff --git a/assets/icons/Infrared/volup_24x21.png b/assets/icons/Infrared/volup_24x21.png new file mode 100644 index 00000000000..688552751ce Binary files /dev/null and b/assets/icons/Infrared/volup_24x21.png differ diff --git a/assets/icons/Infrared/volup_hover_24x21.png b/assets/icons/Infrared/volup_hover_24x21.png new file mode 100644 index 00000000000..5d790e7966f Binary files /dev/null and b/assets/icons/Infrared/volup_hover_24x21.png differ diff --git a/assets/icons/Interface/SmallArrowDown_3x5.png b/assets/icons/Interface/SmallArrowDown_3x5.png new file mode 100644 index 00000000000..1912e5d2462 Binary files /dev/null and b/assets/icons/Interface/SmallArrowDown_3x5.png differ diff --git a/assets/icons/Interface/SmallArrowDown_4x7.png b/assets/icons/Interface/SmallArrowDown_4x7.png new file mode 100644 index 00000000000..5c5252b167d Binary files /dev/null and b/assets/icons/Interface/SmallArrowDown_4x7.png differ diff --git a/assets/icons/Interface/SmallArrowUp_3x5.png b/assets/icons/Interface/SmallArrowUp_3x5.png new file mode 100644 index 00000000000..9c6242078d3 Binary files /dev/null and b/assets/icons/Interface/SmallArrowUp_3x5.png differ diff --git a/assets/icons/Interface/SmallArrowUp_4x7.png b/assets/icons/Interface/SmallArrowUp_4x7.png new file mode 100644 index 00000000000..886369abc6f Binary files /dev/null and b/assets/icons/Interface/SmallArrowUp_4x7.png differ diff --git a/assets/icons/NFC/ArrowC_1_36x36.png b/assets/icons/NFC/ArrowC_1_36x36.png new file mode 100644 index 00000000000..3a0c6dd0cb2 Binary files /dev/null and b/assets/icons/NFC/ArrowC_1_36x36.png differ diff --git a/assets/icons/NFC/Keychain.png b/assets/icons/NFC/Keychain.png deleted file mode 100644 index 7ba1b11da6f..00000000000 Binary files a/assets/icons/NFC/Keychain.png and /dev/null differ diff --git a/assets/icons/NFC/Keychain_39x36.png b/assets/icons/NFC/Keychain_39x36.png new file mode 100644 index 00000000000..d15850b5b7f Binary files /dev/null and b/assets/icons/NFC/Keychain_39x36.png differ diff --git a/assets/icons/NFC/Modern_reader_18x34.png b/assets/icons/NFC/Modern_reader_18x34.png new file mode 100644 index 00000000000..b19c0f30c9f Binary files /dev/null and b/assets/icons/NFC/Modern_reader_18x34.png differ diff --git a/assets/icons/NFC/Move_flipper_26x39.png b/assets/icons/NFC/Move_flipper_26x39.png new file mode 100644 index 00000000000..ff4af9ff059 Binary files /dev/null and b/assets/icons/NFC/Move_flipper_26x39.png differ diff --git a/assets/icons/NFC/NFC_dolphin_emulation_47x61.png b/assets/icons/NFC/NFC_dolphin_emulation_47x61.png new file mode 100644 index 00000000000..1783531285b Binary files /dev/null and b/assets/icons/NFC/NFC_dolphin_emulation_47x61.png differ diff --git a/assets/icons/NFC/NFC_manual.png b/assets/icons/NFC/NFC_manual_60x50.png similarity index 100% rename from assets/icons/NFC/NFC_manual.png rename to assets/icons/NFC/NFC_manual_60x50.png diff --git a/assets/icons/NFC/Reader_detect.png b/assets/icons/NFC/Reader_detect.png deleted file mode 100644 index 56d3663eaa2..00000000000 Binary files a/assets/icons/NFC/Reader_detect.png and /dev/null differ diff --git a/assets/icons/NFC/Release_arrow_18x15.png b/assets/icons/NFC/Release_arrow_18x15.png new file mode 100644 index 00000000000..187a9034542 Binary files /dev/null and b/assets/icons/NFC/Release_arrow_18x15.png differ diff --git a/assets/icons/NFC/Restoring.png b/assets/icons/NFC/Restoring.png deleted file mode 100644 index 9e058869f10..00000000000 Binary files a/assets/icons/NFC/Restoring.png and /dev/null differ diff --git a/assets/icons/PIN/Pin_arrow_up_7x9.png b/assets/icons/PIN/Pin_arrow_up_7x9.png new file mode 100644 index 00000000000..a91a6fd5e99 Binary files /dev/null and b/assets/icons/PIN/Pin_arrow_up_7x9.png differ diff --git a/assets/icons/PIN/Pin_back_full_40x8.png b/assets/icons/PIN/Pin_back_full_40x8.png deleted file mode 100644 index cd1301512db..00000000000 Binary files a/assets/icons/PIN/Pin_back_full_40x8.png and /dev/null differ diff --git a/assets/icons/Power/Unplug_bg_bottom_128x10.png b/assets/icons/Power/Unplug_bg_bottom_128x10.png new file mode 100644 index 00000000000..35d73ba76ea Binary files /dev/null and b/assets/icons/Power/Unplug_bg_bottom_128x10.png differ diff --git a/assets/icons/Power/Unplug_bg_top_128x14.png b/assets/icons/Power/Unplug_bg_top_128x14.png new file mode 100644 index 00000000000..bafa2c49470 Binary files /dev/null and b/assets/icons/Power/Unplug_bg_top_128x14.png differ diff --git a/assets/icons/SDCard/SDQuestion_35x43.png b/assets/icons/SDCard/SDQuestion_35x43.png index 9b9c9a58e32..257ab1d8524 100644 Binary files a/assets/icons/SDCard/SDQuestion_35x43.png and b/assets/icons/SDCard/SDQuestion_35x43.png differ diff --git a/assets/icons/StatusBar/Alert_9x8.png b/assets/icons/StatusBar/Alert_9x8.png new file mode 100644 index 00000000000..d03f107ef1e Binary files /dev/null and b/assets/icons/StatusBar/Alert_9x8.png differ diff --git a/assets/icons/StatusBar/GameMode_11x8.png b/assets/icons/StatusBar/GameMode_11x8.png new file mode 100644 index 00000000000..49f2e25bf32 Binary files /dev/null and b/assets/icons/StatusBar/GameMode_11x8.png differ diff --git a/assets/icons/StatusBar/Hidden_window_9x8.png b/assets/icons/StatusBar/Hidden_window_9x8.png new file mode 100644 index 00000000000..d6fc2b326d0 Binary files /dev/null and b/assets/icons/StatusBar/Hidden_window_9x8.png differ diff --git a/assets/icons/StatusBar/Lock_8x8.png b/assets/icons/StatusBar/Lock_8x8.png deleted file mode 100644 index 01fb0eb6bf7..00000000000 Binary files a/assets/icons/StatusBar/Lock_8x8.png and /dev/null differ diff --git a/assets/icons/StatusBar/Muted_8x8.png b/assets/icons/StatusBar/Muted_8x8.png new file mode 100644 index 00000000000..fee4e09f5e6 Binary files /dev/null and b/assets/icons/StatusBar/Muted_8x8.png differ diff --git a/assets/icons/StatusBar/Rpc_active_7x8.png b/assets/icons/StatusBar/Rpc_active_7x8.png new file mode 100644 index 00000000000..f643a82aa1d Binary files /dev/null and b/assets/icons/StatusBar/Rpc_active_7x8.png differ diff --git a/assets/icons/SubGhz/External_antenna_20x12.png b/assets/icons/SubGhz/External_antenna_20x12.png new file mode 100644 index 00000000000..940087071a1 Binary files /dev/null and b/assets/icons/SubGhz/External_antenna_20x12.png differ diff --git a/assets/icons/SubGhz/Internal_antenna_20x12.png b/assets/icons/SubGhz/Internal_antenna_20x12.png new file mode 100644 index 00000000000..a8a5be09fb8 Binary files /dev/null and b/assets/icons/SubGhz/Internal_antenna_20x12.png differ diff --git a/assets/icons/SubGhz/Scanning_123x52.png b/assets/icons/SubGhz/Scanning_123x52.png deleted file mode 100644 index ec785948d03..00000000000 Binary files a/assets/icons/SubGhz/Scanning_123x52.png and /dev/null differ diff --git a/assets/icons/SubGhz/Scanning_short_96x52.png b/assets/icons/SubGhz/Scanning_short_96x52.png new file mode 100644 index 00000000000..718d0e695a2 Binary files /dev/null and b/assets/icons/SubGhz/Scanning_short_96x52.png differ diff --git a/assets/protobuf b/assets/protobuf index 6727eaf287d..23ad19a7566 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 6727eaf287db077dcd28719cd764f5804712223e +Subproject commit 23ad19a756649ed9f6677b598e5361c5cce6847b diff --git a/assets/resources/Manifest b/assets/resources/Manifest deleted file mode 100644 index 8feab7d4d8b..00000000000 --- a/assets/resources/Manifest +++ /dev/null @@ -1,458 +0,0 @@ -V:0 -T:1660218073 -D:badusb -D:dolphin -D:dolphin_inverted -D:infrared -D:music_player -D:nfc -D:subghz -D:u2f -F:ea60cc48ee07c255248565adaeada0ef:10244:.DS_Store -F:0e41ba26498b7511d7c9e6e6b5e3b149:1592:badusb/demo_macos.txt -F:46a332993ca94b9aa692030ebaa19c70:1552:badusb/demo_windows.txt -D:dolphin/L1_Boxing_128x64 -D:dolphin/L1_Cry_128x64 -D:dolphin/L1_Furippa1_128x64 -D:dolphin/L1_Laptop_128x51 -D:dolphin/L1_Leaving_sad_128x64 -D:dolphin/L1_Mad_fist_128x64 -D:dolphin/L1_Read_books_128x64 -D:dolphin/L1_Recording_128x51 -D:dolphin/L1_Sleep_128x64 -D:dolphin/L1_Waves_128x50 -D:dolphin/L2_Furippa2_128x64 -D:dolphin/L2_Hacking_pc_128x64 -D:dolphin/L2_Soldering_128x64 -D:dolphin/L3_Furippa3_128x64 -D:dolphin/L3_Hijack_radio_128x64 -D:dolphin/L3_Lab_research_128x54 -F:5600cadd49299aa0adaadf611ec53744:10244:dolphin/.DS_Store -F:d1148ab5354eaf4fa7f959589d840932:1563:dolphin/manifest.txt -F:d37be8444102ec5cde5fe3a85d55b57d:481:dolphin/L1_Boxing_128x64/frame_0.bm -F:54fb07443bc153ded9589b74d23b4263:461:dolphin/L1_Boxing_128x64/frame_1.bm -F:e007afe130d699c715b99ce8e5b407bd:531:dolphin/L1_Boxing_128x64/frame_2.bm -F:a999a9a6c76c66158f1aa5ccb56de7c9:437:dolphin/L1_Boxing_128x64/frame_3.bm -F:ec6af9cb451ab16c0fa62e95e8134b49:459:dolphin/L1_Boxing_128x64/frame_4.bm -F:2aa0c1e7bf1131b9dc172aa595ec01f2:450:dolphin/L1_Boxing_128x64/frame_5.bm -F:bbc8f750d17d156438c5cfe1122ec7f4:442:dolphin/L1_Boxing_128x64/frame_6.bm -F:f6e51ada3e3285e330714dab5b4277dd:418:dolphin/L1_Boxing_128x64/meta.txt -F:ab33a6f37209541f3db938d1cfe1706f:889:dolphin/L1_Cry_128x64/frame_0.bm -F:1b3fdeb404af0f7402caa5a5e091a8f8:911:dolphin/L1_Cry_128x64/frame_1.bm -F:4db644b173af72f3d371d2bd81f76b05:910:dolphin/L1_Cry_128x64/frame_2.bm -F:cd4c0ef67a8e514edecd9600242db068:923:dolphin/L1_Cry_128x64/frame_3.bm -F:ee02e9589e0714d3e2bc0d93aa294ccb:894:dolphin/L1_Cry_128x64/frame_4.bm -F:7703a7d9745d13b45d73ce4b86b4cdc8:940:dolphin/L1_Cry_128x64/frame_5.bm -F:ee6de6a0ed903317c4948cb445e0a9a8:915:dolphin/L1_Cry_128x64/frame_6.bm -F:a3892e45826c66f48d3d64fb81521446:934:dolphin/L1_Cry_128x64/frame_7.bm -F:680b12cc4dad722d6583b7e710bfc297:516:dolphin/L1_Cry_128x64/meta.txt -F:4911eaa7cb84ced19e5dea2af51b91a5:294:dolphin/L1_Furippa1_128x64/frame_0.bm -F:5669bee57c7b3d93a1665dd87fd5372a:325:dolphin/L1_Furippa1_128x64/frame_1.bm -F:80b48a77682b853e6236cd1c89083e6f:465:dolphin/L1_Furippa1_128x64/frame_10.bm -F:9d8ea10bf3d3831cb4a94957dc0b41c6:698:dolphin/L1_Furippa1_128x64/frame_11.bm -F:2dbb3125ea63550906fba8f7ec7b1da3:541:dolphin/L1_Furippa1_128x64/frame_12.bm -F:6a06b718957dca9caa63a4f3baa73abb:584:dolphin/L1_Furippa1_128x64/frame_13.bm -F:5450bf16c3d2fceaf5e6ea585b7ef7c1:610:dolphin/L1_Furippa1_128x64/frame_14.bm -F:535c0eca62703eb7df36f17334a6191b:719:dolphin/L1_Furippa1_128x64/frame_15.bm -F:7c03af85ade9b791755f3a4d106c2b7c:458:dolphin/L1_Furippa1_128x64/frame_16.bm -F:41b8fea16ad8705f4594e6119eade395:400:dolphin/L1_Furippa1_128x64/frame_17.bm -F:2db7fd3da5208a8e41902ae27cf41702:333:dolphin/L1_Furippa1_128x64/frame_18.bm -F:7e47428442e0f04959fc6afde979936e:351:dolphin/L1_Furippa1_128x64/frame_2.bm -F:0eb187078f169d7a852e97ecf430aea0:324:dolphin/L1_Furippa1_128x64/frame_3.bm -F:967c402971a442a5bf28eba804bb3ff4:387:dolphin/L1_Furippa1_128x64/frame_4.bm -F:175cb930fba0fc86f54a3a109b741708:390:dolphin/L1_Furippa1_128x64/frame_5.bm -F:f8c3ee1ab657549d1d00c1c72d8d2ff5:407:dolphin/L1_Furippa1_128x64/frame_6.bm -F:4911eaa7cb84ced19e5dea2af51b91a5:294:dolphin/L1_Furippa1_128x64/frame_7.bm -F:8f649ff34b224f4e564644a4494c54ed:283:dolphin/L1_Furippa1_128x64/frame_8.bm -F:3ec3c40d26bf8d3e691b1335d20d4ec0:312:dolphin/L1_Furippa1_128x64/frame_9.bm -F:ebe088426d184cf6651288accd21add6:241:dolphin/L1_Furippa1_128x64/meta.txt -F:d02fdfd1a3b89da00d2acf32bd09da80:555:dolphin/L1_Laptop_128x51/frame_0.bm -F:7e29ea503d41023fa3895d15458f106d:557:dolphin/L1_Laptop_128x51/frame_1.bm -F:eb55e0629de873f537d8412ced528eb4:560:dolphin/L1_Laptop_128x51/frame_2.bm -F:1516472ab3c140dd5bd4d089caa44747:556:dolphin/L1_Laptop_128x51/frame_3.bm -F:61172f89cf0a17bd7f978edccdeed166:560:dolphin/L1_Laptop_128x51/frame_4.bm -F:9d54913928c7e9477b6b8a43f3767621:554:dolphin/L1_Laptop_128x51/frame_5.bm -F:5243d6272bbb213e9c17af07ee011402:553:dolphin/L1_Laptop_128x51/frame_6.bm -F:aa68e0f28f117891ba0f4d7613224fc6:560:dolphin/L1_Laptop_128x51/frame_7.bm -F:9ef1935ab29fe70bbc517f4b602547d7:403:dolphin/L1_Laptop_128x51/meta.txt -F:6ce34e62c5bf4764a4163101afe63e60:514:dolphin/L1_Leaving_sad_128x64/frame_0.bm -F:19a0e0c518d222d91d24b8712ab6bb80:526:dolphin/L1_Leaving_sad_128x64/frame_1.bm -F:837bfb424c8d8a3bfbda7d6a28ba5a5c:316:dolphin/L1_Leaving_sad_128x64/frame_10.bm -F:1a69b6f63a96e0958837ea8b21db3966:294:dolphin/L1_Leaving_sad_128x64/frame_11.bm -F:c3ea827593a4563d544dfb7e99d73885:322:dolphin/L1_Leaving_sad_128x64/frame_12.bm -F:1e3842669191fe9599f830ac133e0751:542:dolphin/L1_Leaving_sad_128x64/frame_2.bm -F:9161660e6827bd776a15eefa2a8add19:557:dolphin/L1_Leaving_sad_128x64/frame_3.bm -F:d01a79fdb4f84397d82bf9927aeb71e0:488:dolphin/L1_Leaving_sad_128x64/frame_4.bm -F:316e30ef319c080fab2a79c21e526319:469:dolphin/L1_Leaving_sad_128x64/frame_5.bm -F:09a812d59b60b5fe7724057daa14ad60:499:dolphin/L1_Leaving_sad_128x64/frame_6.bm -F:9eb07b76cc864a0ce2918d68e41d4500:486:dolphin/L1_Leaving_sad_128x64/frame_7.bm -F:cf8c4cc4abbd700b096037b7ebfd0e31:403:dolphin/L1_Leaving_sad_128x64/frame_8.bm -F:889728ded689203aa82193e573912d18:317:dolphin/L1_Leaving_sad_128x64/frame_9.bm -F:2bff1f09ad1e9059a60e08990ca1d414:477:dolphin/L1_Leaving_sad_128x64/meta.txt -F:c31a882e95ed5c69fd63226db2188710:520:dolphin/L1_Mad_fist_128x64/frame_0.bm -F:740326828f6ba6e29373943ba835e77f:540:dolphin/L1_Mad_fist_128x64/frame_1.bm -F:0c9693dda040fd73ca6d773a10924bd8:542:dolphin/L1_Mad_fist_128x64/frame_10.bm -F:425c1d101debd1e9502db2628640b704:505:dolphin/L1_Mad_fist_128x64/frame_11.bm -F:aa576f7dbd14ec682f6c50314165fb14:501:dolphin/L1_Mad_fist_128x64/frame_12.bm -F:712335eabefb8c7bb7fb2f4301419c10:500:dolphin/L1_Mad_fist_128x64/frame_13.bm -F:b6e11711ea4dcc2e64f267d888f91baf:515:dolphin/L1_Mad_fist_128x64/frame_2.bm -F:61bdd22a2b1e67efe093b6acf7dfadce:538:dolphin/L1_Mad_fist_128x64/frame_3.bm -F:20ae06a3ce7a07656e578edb024e2b3f:512:dolphin/L1_Mad_fist_128x64/frame_4.bm -F:45cf2bd55365a7328df39fe98a496cc9:519:dolphin/L1_Mad_fist_128x64/frame_5.bm -F:4b8840eebb3a4a1ead69a7130816047e:524:dolphin/L1_Mad_fist_128x64/frame_6.bm -F:0de4497a5fbf80cc93e523465c5e3122:515:dolphin/L1_Mad_fist_128x64/frame_7.bm -F:32d8ddeb19bfa415fe283666b1e323a2:517:dolphin/L1_Mad_fist_128x64/frame_8.bm -F:a42a0578c2d0411500fb3485a3beb536:526:dolphin/L1_Mad_fist_128x64/frame_9.bm -F:10a521c78168a5928c859494e2a61cd2:349:dolphin/L1_Mad_fist_128x64/meta.txt -F:61565b7be9a69a60ce2dbae0273df347:653:dolphin/L1_Read_books_128x64/frame_0.bm -F:cf5a2d423540e3af37e789d70c9c1fbf:653:dolphin/L1_Read_books_128x64/frame_1.bm -F:c91935861979d024e6637b8810889878:650:dolphin/L1_Read_books_128x64/frame_2.bm -F:0c007a30f396f3e7a0ded2b24080357d:646:dolphin/L1_Read_books_128x64/frame_3.bm -F:323a52816dd79d6d3186f451e26e06ad:650:dolphin/L1_Read_books_128x64/frame_4.bm -F:494f27958f4cea9b94d09cf27725c5cd:652:dolphin/L1_Read_books_128x64/frame_5.bm -F:a6a7491fe80255e1745c9f293da52805:646:dolphin/L1_Read_books_128x64/frame_6.bm -F:238497e6643fd491cd6002e98c615c05:647:dolphin/L1_Read_books_128x64/frame_7.bm -F:300651e8f53d9a29ae38d4b9292c73cf:643:dolphin/L1_Read_books_128x64/frame_8.bm -F:3d9568deeff646b677092902a98f9ceb:325:dolphin/L1_Read_books_128x64/meta.txt -F:2aba555567ab70cff003ded4138c6721:663:dolphin/L1_Recording_128x51/frame_0.bm -F:8456c6e86825957e5662e2f08eb6c116:657:dolphin/L1_Recording_128x51/frame_1.bm -F:2e4a1aca5afa5a6ab254884210875eb4:629:dolphin/L1_Recording_128x51/frame_10.bm -F:9f1cf96598e3d935879b1d0c97705778:659:dolphin/L1_Recording_128x51/frame_11.bm -F:409abfeca974e5649affcd1faafea988:628:dolphin/L1_Recording_128x51/frame_2.bm -F:66b2a5abf05acbf79f9943e01b8b8cec:654:dolphin/L1_Recording_128x51/frame_3.bm -F:d55c5ed28c2ff48f42ab30b420d64fa3:662:dolphin/L1_Recording_128x51/frame_4.bm -F:2ce12d8cfdd953c9dadb9459c580a320:622:dolphin/L1_Recording_128x51/frame_5.bm -F:da631e3837fcdf3ee9e6abdf17fb764b:664:dolphin/L1_Recording_128x51/frame_6.bm -F:604a7cdac2491c9bc2e88b9e91c99dcc:626:dolphin/L1_Recording_128x51/frame_7.bm -F:fc94649dc98244dd9a0ab7fe62721d3c:663:dolphin/L1_Recording_128x51/frame_8.bm -F:b2475ab8ee26cbd9a403ee603520bd35:661:dolphin/L1_Recording_128x51/frame_9.bm -F:a7c2b3b420706712149cc2426c68df4f:219:dolphin/L1_Recording_128x51/meta.txt -F:9858fd34b55cebcb9be50c5710212a13:580:dolphin/L1_Sleep_128x64/frame_0.bm -F:e47ef8c846083b8fde028b1724861444:589:dolphin/L1_Sleep_128x64/frame_1.bm -F:9749bd05b47fd07cc3a41ab201f86bf4:582:dolphin/L1_Sleep_128x64/frame_2.bm -F:edf11266b20b846ace622e41cd36906b:597:dolphin/L1_Sleep_128x64/frame_3.bm -F:8fbb96a9d809d85fa6bad931fe4e6fe2:510:dolphin/L1_Sleep_128x64/meta.txt -F:283b41f1b2c581c510ff176293b7288a:443:dolphin/L1_Waves_128x50/frame_0.bm -F:c9fc5127e1d8a4217b6b177716725ba0:448:dolphin/L1_Waves_128x50/frame_1.bm -F:8e0797bf26d5d8d3cbeb99798c222b80:463:dolphin/L1_Waves_128x50/frame_2.bm -F:da02b1deb3119b31f2b8f182d5bf3242:472:dolphin/L1_Waves_128x50/frame_3.bm -F:8e6fb4133acbda7e5bb9adad0aed306c:620:dolphin/L1_Waves_128x50/meta.txt -F:be80d2fa903e3250b69c063a1eef0621:350:dolphin/L2_Furippa2_128x64/frame_0.bm -F:9e628f5e154f12d6c57b13befed1f5f6:385:dolphin/L2_Furippa2_128x64/frame_1.bm -F:80b48a77682b853e6236cd1c89083e6f:465:dolphin/L2_Furippa2_128x64/frame_10.bm -F:9d8ea10bf3d3831cb4a94957dc0b41c6:698:dolphin/L2_Furippa2_128x64/frame_11.bm -F:2dbb3125ea63550906fba8f7ec7b1da3:541:dolphin/L2_Furippa2_128x64/frame_12.bm -F:6a06b718957dca9caa63a4f3baa73abb:584:dolphin/L2_Furippa2_128x64/frame_13.bm -F:5450bf16c3d2fceaf5e6ea585b7ef7c1:610:dolphin/L2_Furippa2_128x64/frame_14.bm -F:e3c92103f403857b502081d3b058e53a:740:dolphin/L2_Furippa2_128x64/frame_15.bm -F:432669d796bbf7be1d14f5b7db036a92:533:dolphin/L2_Furippa2_128x64/frame_16.bm -F:53485c6b465c80a1ce8ddf03c4976039:451:dolphin/L2_Furippa2_128x64/frame_17.bm -F:333b75b16c088428a28259c931630fb9:397:dolphin/L2_Furippa2_128x64/frame_18.bm -F:ed02d68380382361f3f01cbf01d13b0c:402:dolphin/L2_Furippa2_128x64/frame_2.bm -F:b0ba042d7b60dc5681182b1d4005f0a2:374:dolphin/L2_Furippa2_128x64/frame_3.bm -F:518a84fa5a4e9e7f84246d5d82e87f15:440:dolphin/L2_Furippa2_128x64/frame_4.bm -F:9b7b0ae6f4f55d30cb43b0465216aa25:449:dolphin/L2_Furippa2_128x64/frame_5.bm -F:03b153949b0dae2efe1fc5f0dc57a0ef:466:dolphin/L2_Furippa2_128x64/frame_6.bm -F:be80d2fa903e3250b69c063a1eef0621:350:dolphin/L2_Furippa2_128x64/frame_7.bm -F:a8433f451cf3efc4ce2fb04a38c1f84f:319:dolphin/L2_Furippa2_128x64/frame_8.bm -F:d32a11bf9779d57191c1e59fe69cf83d:317:dolphin/L2_Furippa2_128x64/frame_9.bm -F:ebe088426d184cf6651288accd21add6:241:dolphin/L2_Furippa2_128x64/meta.txt -F:af4ec0085c29732085c51b18dc97bc27:543:dolphin/L2_Hacking_pc_128x64/frame_0.bm -F:eb141965fb6fb9f8b28766bac92abe1a:545:dolphin/L2_Hacking_pc_128x64/frame_1.bm -F:f7b33d3541dab08aaf4e8375e262b982:548:dolphin/L2_Hacking_pc_128x64/frame_2.bm -F:03634d90c54fd235aa76c0f9f794c80b:608:dolphin/L2_Hacking_pc_128x64/frame_3.bm -F:4c77406302f3fb74f8bdba568097082a:609:dolphin/L2_Hacking_pc_128x64/frame_4.bm -F:b0d1783358094534ac95b3455124d5fe:409:dolphin/L2_Hacking_pc_128x64/meta.txt -F:584c92e6fb15e99389b84d567e6d4d02:699:dolphin/L2_Soldering_128x64/frame_0.bm -F:3fa01b93460379204b6d14f43573b4f3:688:dolphin/L2_Soldering_128x64/frame_1.bm -F:6fad29757d4b7231b1d0ec53d0529b45:699:dolphin/L2_Soldering_128x64/frame_10.bm -F:e82c83e5a03abf4f6a1efd0a0f1ca33a:689:dolphin/L2_Soldering_128x64/frame_2.bm -F:7f9f310e22ef85af225dd1aefa2c47ba:689:dolphin/L2_Soldering_128x64/frame_3.bm -F:1ff31af6f90f07c0cdfa3283f52a5adc:693:dolphin/L2_Soldering_128x64/frame_4.bm -F:1a8f25aff949860cc6ffc79b4f48d5dd:696:dolphin/L2_Soldering_128x64/frame_5.bm -F:dbaa75feb8aebaf9b1cc5201c29952b8:712:dolphin/L2_Soldering_128x64/frame_6.bm -F:ee356bd981fba90c402d8e08d3015792:732:dolphin/L2_Soldering_128x64/frame_7.bm -F:09d5c5a685df606562d407bb9dac798e:705:dolphin/L2_Soldering_128x64/frame_8.bm -F:5451816e73bad029b3b9f3f55d294582:698:dolphin/L2_Soldering_128x64/frame_9.bm -F:c38ffad11987faf5ba6e363ead705e78:319:dolphin/L2_Soldering_128x64/meta.txt -F:2e083023ab65d1f99bba71f9aae6db9a:398:dolphin/L3_Furippa3_128x64/frame_0.bm -F:8cf20e07d84fd6a1157ba932beca70ea:438:dolphin/L3_Furippa3_128x64/frame_1.bm -F:018344c951691b7b1d77c1c6729d3e42:559:dolphin/L3_Furippa3_128x64/frame_10.bm -F:07008e2508064ab7a8467802472a9803:728:dolphin/L3_Furippa3_128x64/frame_11.bm -F:2dbb3125ea63550906fba8f7ec7b1da3:541:dolphin/L3_Furippa3_128x64/frame_12.bm -F:6a06b718957dca9caa63a4f3baa73abb:584:dolphin/L3_Furippa3_128x64/frame_13.bm -F:5450bf16c3d2fceaf5e6ea585b7ef7c1:610:dolphin/L3_Furippa3_128x64/frame_14.bm -F:e333224a4bed87b606df57a252ed4887:741:dolphin/L3_Furippa3_128x64/frame_15.bm -F:a20a6abfbd66fc3f92c66adacc4444a3:559:dolphin/L3_Furippa3_128x64/frame_16.bm -F:c1e051dce6b90e4f69b4792d0356a6b3:492:dolphin/L3_Furippa3_128x64/frame_17.bm -F:377f3621507c6590120cbc1c8ca92999:445:dolphin/L3_Furippa3_128x64/frame_18.bm -F:81f09c0fcd2bddb8a107a199e7149230:463:dolphin/L3_Furippa3_128x64/frame_2.bm -F:ed7fd1ada1070493462c1899f7372baf:424:dolphin/L3_Furippa3_128x64/frame_3.bm -F:e5fb2cdc4e08d6abff3191d37a1007ed:499:dolphin/L3_Furippa3_128x64/frame_4.bm -F:923a05250e5a93c7db7bbbf48448d164:504:dolphin/L3_Furippa3_128x64/frame_5.bm -F:1e9628db28a9a908c4a4b24cb16c5d20:521:dolphin/L3_Furippa3_128x64/frame_6.bm -F:2e083023ab65d1f99bba71f9aae6db9a:398:dolphin/L3_Furippa3_128x64/frame_7.bm -F:f1ec6e12daba9490f9e2e0e308ae3f83:419:dolphin/L3_Furippa3_128x64/frame_8.bm -F:106997120ad4cd23bd51e6f26bd7d74d:435:dolphin/L3_Furippa3_128x64/frame_9.bm -F:ebe088426d184cf6651288accd21add6:241:dolphin/L3_Furippa3_128x64/meta.txt -F:42970030123b2468984785fea7c60318:524:dolphin/L3_Hijack_radio_128x64/frame_0.bm -F:491c6d8ef21e48ca0f6b5fbd269c820b:527:dolphin/L3_Hijack_radio_128x64/frame_1.bm -F:ff499c8716c5f7fc1110a5ee82bda20c:550:dolphin/L3_Hijack_radio_128x64/frame_10.bm -F:ee39d82d6efc6a6992d19b6d75a6c509:572:dolphin/L3_Hijack_radio_128x64/frame_11.bm -F:5d14e8cb9d67bf8f597d6c749d07a135:539:dolphin/L3_Hijack_radio_128x64/frame_12.bm -F:9461004b75a34a36097668159c4cabe6:579:dolphin/L3_Hijack_radio_128x64/frame_13.bm -F:c925d4b1dff9c81463944cf930d7da8d:526:dolphin/L3_Hijack_radio_128x64/frame_2.bm -F:f98ed80cfab3a94b580be81654401c89:529:dolphin/L3_Hijack_radio_128x64/frame_3.bm -F:97ba548c27732be9e05fb8f7be8204ce:571:dolphin/L3_Hijack_radio_128x64/frame_4.bm -F:524932eb2391057fc1dea7237c7086e3:574:dolphin/L3_Hijack_radio_128x64/frame_5.bm -F:8eb9672f719926ac9c4c158575f388cd:524:dolphin/L3_Hijack_radio_128x64/frame_6.bm -F:7ca93fbab93bc278d4a11089d624a07b:655:dolphin/L3_Hijack_radio_128x64/frame_7.bm -F:37b4368f0b7235f3a7347bf499541666:645:dolphin/L3_Hijack_radio_128x64/frame_8.bm -F:ea9c3d7bab4756c2916369d5e130fa71:611:dolphin/L3_Hijack_radio_128x64/frame_9.bm -F:8583743f18a12ff647d3478e7aebdad6:230:dolphin/L3_Hijack_radio_128x64/meta.txt -F:f5f02a9df03bba734bdb7ed3297795f0:611:dolphin/L3_Lab_research_128x54/frame_0.bm -F:8f9655ad286464159443922d00e45620:614:dolphin/L3_Lab_research_128x54/frame_1.bm -F:7793b1bc107d4ea2e311e92dc16bf946:576:dolphin/L3_Lab_research_128x54/frame_10.bm -F:f24b8409f9dc770f3845424fe0ab489e:585:dolphin/L3_Lab_research_128x54/frame_11.bm -F:4ea93c4482dac43f40b67cc308f21e6d:571:dolphin/L3_Lab_research_128x54/frame_12.bm -F:cf3bb68dc78c568db22f37057a9fdd66:615:dolphin/L3_Lab_research_128x54/frame_13.bm -F:79719219aaebc95ea525def9173cabf5:618:dolphin/L3_Lab_research_128x54/frame_2.bm -F:05572cfd756704acd6ce9d6c15d03fc0:608:dolphin/L3_Lab_research_128x54/frame_3.bm -F:a26604a0d5427d5cf62a7a911a68b16c:615:dolphin/L3_Lab_research_128x54/frame_4.bm -F:9edc345fe53017970f93dc680818e63e:618:dolphin/L3_Lab_research_128x54/frame_5.bm -F:cf3bb68dc78c568db22f37057a9fdd66:615:dolphin/L3_Lab_research_128x54/frame_6.bm -F:5442895c85f769349288aa3df0990f9d:585:dolphin/L3_Lab_research_128x54/frame_7.bm -F:33b8fde22f34ef556b64b77164bc19b0:578:dolphin/L3_Lab_research_128x54/frame_8.bm -F:f267f0654781049ca323b11bb4375519:581:dolphin/L3_Lab_research_128x54/frame_9.bm -F:41106c0cbc5144f151b2b2d3daaa0527:727:dolphin/L3_Lab_research_128x54/meta.txt -D:dolphin_inverted/L1_Boxing_128x64 -D:dolphin_inverted/L1_Cry_128x64 -D:dolphin_inverted/L1_Furippa1_128x64 -D:dolphin_inverted/L1_Laptop_128x51 -D:dolphin_inverted/L1_Leaving_sad_128x64 -D:dolphin_inverted/L1_Mad_fist_128x64 -D:dolphin_inverted/L1_Read_books_128x64 -D:dolphin_inverted/L1_Recording_128x51 -D:dolphin_inverted/L1_Sleep_128x64 -D:dolphin_inverted/L1_Waves_128x50 -D:dolphin_inverted/L2_Furippa2_128x64 -D:dolphin_inverted/L2_Hacking_pc_128x64 -D:dolphin_inverted/L2_Soldering_128x64 -D:dolphin_inverted/L3_Furippa3_128x64 -D:dolphin_inverted/L3_Hijack_radio_128x64 -D:dolphin_inverted/L3_Lab_research_128x54 -F:adc19514130fa5ffc2b1e51bdc86f960:10244:dolphin_inverted/.DS_Store -F:d1148ab5354eaf4fa7f959589d840932:1563:dolphin_inverted/manifest.txt -F:d37be8444102ec5cde5fe3a85d55b57d:481:dolphin_inverted/L1_Boxing_128x64/frame_0.bm -F:54fb07443bc153ded9589b74d23b4263:461:dolphin_inverted/L1_Boxing_128x64/frame_1.bm -F:e007afe130d699c715b99ce8e5b407bd:531:dolphin_inverted/L1_Boxing_128x64/frame_2.bm -F:a999a9a6c76c66158f1aa5ccb56de7c9:437:dolphin_inverted/L1_Boxing_128x64/frame_3.bm -F:ec6af9cb451ab16c0fa62e95e8134b49:459:dolphin_inverted/L1_Boxing_128x64/frame_4.bm -F:2aa0c1e7bf1131b9dc172aa595ec01f2:450:dolphin_inverted/L1_Boxing_128x64/frame_5.bm -F:bbc8f750d17d156438c5cfe1122ec7f4:442:dolphin_inverted/L1_Boxing_128x64/frame_6.bm -F:f6e51ada3e3285e330714dab5b4277dd:418:dolphin_inverted/L1_Boxing_128x64/meta.txt -F:ab33a6f37209541f3db938d1cfe1706f:889:dolphin_inverted/L1_Cry_128x64/frame_0.bm -F:1b3fdeb404af0f7402caa5a5e091a8f8:911:dolphin_inverted/L1_Cry_128x64/frame_1.bm -F:4db644b173af72f3d371d2bd81f76b05:910:dolphin_inverted/L1_Cry_128x64/frame_2.bm -F:cd4c0ef67a8e514edecd9600242db068:923:dolphin_inverted/L1_Cry_128x64/frame_3.bm -F:ee02e9589e0714d3e2bc0d93aa294ccb:894:dolphin_inverted/L1_Cry_128x64/frame_4.bm -F:7703a7d9745d13b45d73ce4b86b4cdc8:940:dolphin_inverted/L1_Cry_128x64/frame_5.bm -F:ee6de6a0ed903317c4948cb445e0a9a8:915:dolphin_inverted/L1_Cry_128x64/frame_6.bm -F:a3892e45826c66f48d3d64fb81521446:934:dolphin_inverted/L1_Cry_128x64/frame_7.bm -F:680b12cc4dad722d6583b7e710bfc297:516:dolphin_inverted/L1_Cry_128x64/meta.txt -F:4911eaa7cb84ced19e5dea2af51b91a5:294:dolphin_inverted/L1_Furippa1_128x64/frame_0.bm -F:5669bee57c7b3d93a1665dd87fd5372a:325:dolphin_inverted/L1_Furippa1_128x64/frame_1.bm -F:80b48a77682b853e6236cd1c89083e6f:465:dolphin_inverted/L1_Furippa1_128x64/frame_10.bm -F:9d8ea10bf3d3831cb4a94957dc0b41c6:698:dolphin_inverted/L1_Furippa1_128x64/frame_11.bm -F:2dbb3125ea63550906fba8f7ec7b1da3:541:dolphin_inverted/L1_Furippa1_128x64/frame_12.bm -F:6a06b718957dca9caa63a4f3baa73abb:584:dolphin_inverted/L1_Furippa1_128x64/frame_13.bm -F:5450bf16c3d2fceaf5e6ea585b7ef7c1:610:dolphin_inverted/L1_Furippa1_128x64/frame_14.bm -F:535c0eca62703eb7df36f17334a6191b:719:dolphin_inverted/L1_Furippa1_128x64/frame_15.bm -F:7c03af85ade9b791755f3a4d106c2b7c:458:dolphin_inverted/L1_Furippa1_128x64/frame_16.bm -F:41b8fea16ad8705f4594e6119eade395:400:dolphin_inverted/L1_Furippa1_128x64/frame_17.bm -F:2db7fd3da5208a8e41902ae27cf41702:333:dolphin_inverted/L1_Furippa1_128x64/frame_18.bm -F:7e47428442e0f04959fc6afde979936e:351:dolphin_inverted/L1_Furippa1_128x64/frame_2.bm -F:0eb187078f169d7a852e97ecf430aea0:324:dolphin_inverted/L1_Furippa1_128x64/frame_3.bm -F:967c402971a442a5bf28eba804bb3ff4:387:dolphin_inverted/L1_Furippa1_128x64/frame_4.bm -F:175cb930fba0fc86f54a3a109b741708:390:dolphin_inverted/L1_Furippa1_128x64/frame_5.bm -F:f8c3ee1ab657549d1d00c1c72d8d2ff5:407:dolphin_inverted/L1_Furippa1_128x64/frame_6.bm -F:4911eaa7cb84ced19e5dea2af51b91a5:294:dolphin_inverted/L1_Furippa1_128x64/frame_7.bm -F:8f649ff34b224f4e564644a4494c54ed:283:dolphin_inverted/L1_Furippa1_128x64/frame_8.bm -F:3ec3c40d26bf8d3e691b1335d20d4ec0:312:dolphin_inverted/L1_Furippa1_128x64/frame_9.bm -F:ebe088426d184cf6651288accd21add6:241:dolphin_inverted/L1_Furippa1_128x64/meta.txt -F:d02fdfd1a3b89da00d2acf32bd09da80:555:dolphin_inverted/L1_Laptop_128x51/frame_0.bm -F:7e29ea503d41023fa3895d15458f106d:557:dolphin_inverted/L1_Laptop_128x51/frame_1.bm -F:eb55e0629de873f537d8412ced528eb4:560:dolphin_inverted/L1_Laptop_128x51/frame_2.bm -F:1516472ab3c140dd5bd4d089caa44747:556:dolphin_inverted/L1_Laptop_128x51/frame_3.bm -F:61172f89cf0a17bd7f978edccdeed166:560:dolphin_inverted/L1_Laptop_128x51/frame_4.bm -F:9d54913928c7e9477b6b8a43f3767621:554:dolphin_inverted/L1_Laptop_128x51/frame_5.bm -F:5243d6272bbb213e9c17af07ee011402:553:dolphin_inverted/L1_Laptop_128x51/frame_6.bm -F:aa68e0f28f117891ba0f4d7613224fc6:560:dolphin_inverted/L1_Laptop_128x51/frame_7.bm -F:9ef1935ab29fe70bbc517f4b602547d7:403:dolphin_inverted/L1_Laptop_128x51/meta.txt -F:6ce34e62c5bf4764a4163101afe63e60:514:dolphin_inverted/L1_Leaving_sad_128x64/frame_0.bm -F:19a0e0c518d222d91d24b8712ab6bb80:526:dolphin_inverted/L1_Leaving_sad_128x64/frame_1.bm -F:837bfb424c8d8a3bfbda7d6a28ba5a5c:316:dolphin_inverted/L1_Leaving_sad_128x64/frame_10.bm -F:1a69b6f63a96e0958837ea8b21db3966:294:dolphin_inverted/L1_Leaving_sad_128x64/frame_11.bm -F:c3ea827593a4563d544dfb7e99d73885:322:dolphin_inverted/L1_Leaving_sad_128x64/frame_12.bm -F:1e3842669191fe9599f830ac133e0751:542:dolphin_inverted/L1_Leaving_sad_128x64/frame_2.bm -F:9161660e6827bd776a15eefa2a8add19:557:dolphin_inverted/L1_Leaving_sad_128x64/frame_3.bm -F:d01a79fdb4f84397d82bf9927aeb71e0:488:dolphin_inverted/L1_Leaving_sad_128x64/frame_4.bm -F:316e30ef319c080fab2a79c21e526319:469:dolphin_inverted/L1_Leaving_sad_128x64/frame_5.bm -F:09a812d59b60b5fe7724057daa14ad60:499:dolphin_inverted/L1_Leaving_sad_128x64/frame_6.bm -F:9eb07b76cc864a0ce2918d68e41d4500:486:dolphin_inverted/L1_Leaving_sad_128x64/frame_7.bm -F:cf8c4cc4abbd700b096037b7ebfd0e31:403:dolphin_inverted/L1_Leaving_sad_128x64/frame_8.bm -F:889728ded689203aa82193e573912d18:317:dolphin_inverted/L1_Leaving_sad_128x64/frame_9.bm -F:2bff1f09ad1e9059a60e08990ca1d414:477:dolphin_inverted/L1_Leaving_sad_128x64/meta.txt -F:c31a882e95ed5c69fd63226db2188710:520:dolphin_inverted/L1_Mad_fist_128x64/frame_0.bm -F:740326828f6ba6e29373943ba835e77f:540:dolphin_inverted/L1_Mad_fist_128x64/frame_1.bm -F:0c9693dda040fd73ca6d773a10924bd8:542:dolphin_inverted/L1_Mad_fist_128x64/frame_10.bm -F:425c1d101debd1e9502db2628640b704:505:dolphin_inverted/L1_Mad_fist_128x64/frame_11.bm -F:aa576f7dbd14ec682f6c50314165fb14:501:dolphin_inverted/L1_Mad_fist_128x64/frame_12.bm -F:712335eabefb8c7bb7fb2f4301419c10:500:dolphin_inverted/L1_Mad_fist_128x64/frame_13.bm -F:b6e11711ea4dcc2e64f267d888f91baf:515:dolphin_inverted/L1_Mad_fist_128x64/frame_2.bm -F:61bdd22a2b1e67efe093b6acf7dfadce:538:dolphin_inverted/L1_Mad_fist_128x64/frame_3.bm -F:20ae06a3ce7a07656e578edb024e2b3f:512:dolphin_inverted/L1_Mad_fist_128x64/frame_4.bm -F:45cf2bd55365a7328df39fe98a496cc9:519:dolphin_inverted/L1_Mad_fist_128x64/frame_5.bm -F:4b8840eebb3a4a1ead69a7130816047e:524:dolphin_inverted/L1_Mad_fist_128x64/frame_6.bm -F:0de4497a5fbf80cc93e523465c5e3122:515:dolphin_inverted/L1_Mad_fist_128x64/frame_7.bm -F:32d8ddeb19bfa415fe283666b1e323a2:517:dolphin_inverted/L1_Mad_fist_128x64/frame_8.bm -F:a42a0578c2d0411500fb3485a3beb536:526:dolphin_inverted/L1_Mad_fist_128x64/frame_9.bm -F:10a521c78168a5928c859494e2a61cd2:349:dolphin_inverted/L1_Mad_fist_128x64/meta.txt -F:aa46cfa5a3e6d238c453d2df4b2a8127:653:dolphin_inverted/L1_Read_books_128x64/frame_0.bm -F:c8c5bd480786359c54d196e355912e5f:653:dolphin_inverted/L1_Read_books_128x64/frame_1.bm -F:efb7141e1e516e29479e567e64ec00e9:650:dolphin_inverted/L1_Read_books_128x64/frame_2.bm -F:b20f88080f60614551ad611f3e83531a:646:dolphin_inverted/L1_Read_books_128x64/frame_3.bm -F:b1ea69307675d1d249420ae6819a3cbb:650:dolphin_inverted/L1_Read_books_128x64/frame_4.bm -F:3aea9f844de6299056c17e9cf7c0f57a:652:dolphin_inverted/L1_Read_books_128x64/frame_5.bm -F:6268b88c022bd56548d63f35c69e089d:646:dolphin_inverted/L1_Read_books_128x64/frame_6.bm -F:25584ab152dbec721904101007c8a2b6:647:dolphin_inverted/L1_Read_books_128x64/frame_7.bm -F:df8988937c9c5d67d6878d3dbafdf354:643:dolphin_inverted/L1_Read_books_128x64/frame_8.bm -F:3d9568deeff646b677092902a98f9ceb:325:dolphin_inverted/L1_Read_books_128x64/meta.txt -F:2aba555567ab70cff003ded4138c6721:663:dolphin_inverted/L1_Recording_128x51/frame_0.bm -F:8456c6e86825957e5662e2f08eb6c116:657:dolphin_inverted/L1_Recording_128x51/frame_1.bm -F:2e4a1aca5afa5a6ab254884210875eb4:629:dolphin_inverted/L1_Recording_128x51/frame_10.bm -F:9f1cf96598e3d935879b1d0c97705778:659:dolphin_inverted/L1_Recording_128x51/frame_11.bm -F:409abfeca974e5649affcd1faafea988:628:dolphin_inverted/L1_Recording_128x51/frame_2.bm -F:66b2a5abf05acbf79f9943e01b8b8cec:654:dolphin_inverted/L1_Recording_128x51/frame_3.bm -F:d55c5ed28c2ff48f42ab30b420d64fa3:662:dolphin_inverted/L1_Recording_128x51/frame_4.bm -F:2ce12d8cfdd953c9dadb9459c580a320:622:dolphin_inverted/L1_Recording_128x51/frame_5.bm -F:da631e3837fcdf3ee9e6abdf17fb764b:664:dolphin_inverted/L1_Recording_128x51/frame_6.bm -F:604a7cdac2491c9bc2e88b9e91c99dcc:626:dolphin_inverted/L1_Recording_128x51/frame_7.bm -F:fc94649dc98244dd9a0ab7fe62721d3c:663:dolphin_inverted/L1_Recording_128x51/frame_8.bm -F:b2475ab8ee26cbd9a403ee603520bd35:661:dolphin_inverted/L1_Recording_128x51/frame_9.bm -F:a7c2b3b420706712149cc2426c68df4f:219:dolphin_inverted/L1_Recording_128x51/meta.txt -F:b6af446e6a9ec51a86785500cb58d4f9:579:dolphin_inverted/L1_Sleep_128x64/frame_0.bm -F:b2a2801f470864fdd3958365328690ea:588:dolphin_inverted/L1_Sleep_128x64/frame_1.bm -F:6bea5148fd4cadadce9c7e572c9460c2:581:dolphin_inverted/L1_Sleep_128x64/frame_2.bm -F:b6c04232c3a833020ce8dba0b2002062:595:dolphin_inverted/L1_Sleep_128x64/frame_3.bm -F:8fbb96a9d809d85fa6bad931fe4e6fe2:510:dolphin_inverted/L1_Sleep_128x64/meta.txt -F:283b41f1b2c581c510ff176293b7288a:443:dolphin_inverted/L1_Waves_128x50/frame_0.bm -F:c9fc5127e1d8a4217b6b177716725ba0:448:dolphin_inverted/L1_Waves_128x50/frame_1.bm -F:8e0797bf26d5d8d3cbeb99798c222b80:463:dolphin_inverted/L1_Waves_128x50/frame_2.bm -F:da02b1deb3119b31f2b8f182d5bf3242:472:dolphin_inverted/L1_Waves_128x50/frame_3.bm -F:8e6fb4133acbda7e5bb9adad0aed306c:620:dolphin_inverted/L1_Waves_128x50/meta.txt -F:be80d2fa903e3250b69c063a1eef0621:350:dolphin_inverted/L2_Furippa2_128x64/frame_0.bm -F:9e628f5e154f12d6c57b13befed1f5f6:385:dolphin_inverted/L2_Furippa2_128x64/frame_1.bm -F:80b48a77682b853e6236cd1c89083e6f:465:dolphin_inverted/L2_Furippa2_128x64/frame_10.bm -F:9d8ea10bf3d3831cb4a94957dc0b41c6:698:dolphin_inverted/L2_Furippa2_128x64/frame_11.bm -F:2dbb3125ea63550906fba8f7ec7b1da3:541:dolphin_inverted/L2_Furippa2_128x64/frame_12.bm -F:6a06b718957dca9caa63a4f3baa73abb:584:dolphin_inverted/L2_Furippa2_128x64/frame_13.bm -F:5450bf16c3d2fceaf5e6ea585b7ef7c1:610:dolphin_inverted/L2_Furippa2_128x64/frame_14.bm -F:e3c92103f403857b502081d3b058e53a:740:dolphin_inverted/L2_Furippa2_128x64/frame_15.bm -F:432669d796bbf7be1d14f5b7db036a92:533:dolphin_inverted/L2_Furippa2_128x64/frame_16.bm -F:53485c6b465c80a1ce8ddf03c4976039:451:dolphin_inverted/L2_Furippa2_128x64/frame_17.bm -F:333b75b16c088428a28259c931630fb9:397:dolphin_inverted/L2_Furippa2_128x64/frame_18.bm -F:ed02d68380382361f3f01cbf01d13b0c:402:dolphin_inverted/L2_Furippa2_128x64/frame_2.bm -F:b0ba042d7b60dc5681182b1d4005f0a2:374:dolphin_inverted/L2_Furippa2_128x64/frame_3.bm -F:518a84fa5a4e9e7f84246d5d82e87f15:440:dolphin_inverted/L2_Furippa2_128x64/frame_4.bm -F:9b7b0ae6f4f55d30cb43b0465216aa25:449:dolphin_inverted/L2_Furippa2_128x64/frame_5.bm -F:03b153949b0dae2efe1fc5f0dc57a0ef:466:dolphin_inverted/L2_Furippa2_128x64/frame_6.bm -F:be80d2fa903e3250b69c063a1eef0621:350:dolphin_inverted/L2_Furippa2_128x64/frame_7.bm -F:a8433f451cf3efc4ce2fb04a38c1f84f:319:dolphin_inverted/L2_Furippa2_128x64/frame_8.bm -F:d32a11bf9779d57191c1e59fe69cf83d:317:dolphin_inverted/L2_Furippa2_128x64/frame_9.bm -F:ebe088426d184cf6651288accd21add6:241:dolphin_inverted/L2_Furippa2_128x64/meta.txt -F:f6f57f8d4d5264bd69481e93328ecde6:543:dolphin_inverted/L2_Hacking_pc_128x64/frame_0.bm -F:bf2dbc7fd78011c6a625ffbfda998703:545:dolphin_inverted/L2_Hacking_pc_128x64/frame_1.bm -F:2ea685cc7f86b505b4c5abed8c06cc0a:548:dolphin_inverted/L2_Hacking_pc_128x64/frame_2.bm -F:e80441d10d73f5040bc09c62c515d49a:608:dolphin_inverted/L2_Hacking_pc_128x64/frame_3.bm -F:9cf489b603c88f8773e29ad17cb0a3e7:609:dolphin_inverted/L2_Hacking_pc_128x64/frame_4.bm -F:b0d1783358094534ac95b3455124d5fe:409:dolphin_inverted/L2_Hacking_pc_128x64/meta.txt -F:584c92e6fb15e99389b84d567e6d4d02:699:dolphin_inverted/L2_Soldering_128x64/frame_0.bm -F:3fa01b93460379204b6d14f43573b4f3:688:dolphin_inverted/L2_Soldering_128x64/frame_1.bm -F:6fad29757d4b7231b1d0ec53d0529b45:699:dolphin_inverted/L2_Soldering_128x64/frame_10.bm -F:e82c83e5a03abf4f6a1efd0a0f1ca33a:689:dolphin_inverted/L2_Soldering_128x64/frame_2.bm -F:7f9f310e22ef85af225dd1aefa2c47ba:689:dolphin_inverted/L2_Soldering_128x64/frame_3.bm -F:1ff31af6f90f07c0cdfa3283f52a5adc:693:dolphin_inverted/L2_Soldering_128x64/frame_4.bm -F:1a8f25aff949860cc6ffc79b4f48d5dd:696:dolphin_inverted/L2_Soldering_128x64/frame_5.bm -F:dbaa75feb8aebaf9b1cc5201c29952b8:712:dolphin_inverted/L2_Soldering_128x64/frame_6.bm -F:ee356bd981fba90c402d8e08d3015792:732:dolphin_inverted/L2_Soldering_128x64/frame_7.bm -F:09d5c5a685df606562d407bb9dac798e:705:dolphin_inverted/L2_Soldering_128x64/frame_8.bm -F:5451816e73bad029b3b9f3f55d294582:698:dolphin_inverted/L2_Soldering_128x64/frame_9.bm -F:c38ffad11987faf5ba6e363ead705e78:319:dolphin_inverted/L2_Soldering_128x64/meta.txt -F:2e083023ab65d1f99bba71f9aae6db9a:398:dolphin_inverted/L3_Furippa3_128x64/frame_0.bm -F:8cf20e07d84fd6a1157ba932beca70ea:438:dolphin_inverted/L3_Furippa3_128x64/frame_1.bm -F:018344c951691b7b1d77c1c6729d3e42:559:dolphin_inverted/L3_Furippa3_128x64/frame_10.bm -F:07008e2508064ab7a8467802472a9803:728:dolphin_inverted/L3_Furippa3_128x64/frame_11.bm -F:2dbb3125ea63550906fba8f7ec7b1da3:541:dolphin_inverted/L3_Furippa3_128x64/frame_12.bm -F:6a06b718957dca9caa63a4f3baa73abb:584:dolphin_inverted/L3_Furippa3_128x64/frame_13.bm -F:5450bf16c3d2fceaf5e6ea585b7ef7c1:610:dolphin_inverted/L3_Furippa3_128x64/frame_14.bm -F:e333224a4bed87b606df57a252ed4887:741:dolphin_inverted/L3_Furippa3_128x64/frame_15.bm -F:a20a6abfbd66fc3f92c66adacc4444a3:559:dolphin_inverted/L3_Furippa3_128x64/frame_16.bm -F:c1e051dce6b90e4f69b4792d0356a6b3:492:dolphin_inverted/L3_Furippa3_128x64/frame_17.bm -F:377f3621507c6590120cbc1c8ca92999:445:dolphin_inverted/L3_Furippa3_128x64/frame_18.bm -F:81f09c0fcd2bddb8a107a199e7149230:463:dolphin_inverted/L3_Furippa3_128x64/frame_2.bm -F:ed7fd1ada1070493462c1899f7372baf:424:dolphin_inverted/L3_Furippa3_128x64/frame_3.bm -F:e5fb2cdc4e08d6abff3191d37a1007ed:499:dolphin_inverted/L3_Furippa3_128x64/frame_4.bm -F:923a05250e5a93c7db7bbbf48448d164:504:dolphin_inverted/L3_Furippa3_128x64/frame_5.bm -F:1e9628db28a9a908c4a4b24cb16c5d20:521:dolphin_inverted/L3_Furippa3_128x64/frame_6.bm -F:2e083023ab65d1f99bba71f9aae6db9a:398:dolphin_inverted/L3_Furippa3_128x64/frame_7.bm -F:f1ec6e12daba9490f9e2e0e308ae3f83:419:dolphin_inverted/L3_Furippa3_128x64/frame_8.bm -F:106997120ad4cd23bd51e6f26bd7d74d:435:dolphin_inverted/L3_Furippa3_128x64/frame_9.bm -F:ebe088426d184cf6651288accd21add6:241:dolphin_inverted/L3_Furippa3_128x64/meta.txt -F:d374d3db00ca0978c668136cb2379ebf:524:dolphin_inverted/L3_Hijack_radio_128x64/frame_0.bm -F:16a19544989e0843488be14e497302cf:527:dolphin_inverted/L3_Hijack_radio_128x64/frame_1.bm -F:dee4bb7146230eb73cbb015c42dc39e5:550:dolphin_inverted/L3_Hijack_radio_128x64/frame_10.bm -F:1ba01cf2280308e270ba766924235085:572:dolphin_inverted/L3_Hijack_radio_128x64/frame_11.bm -F:74db94fb73a44c1f9f9515155d50c3ac:539:dolphin_inverted/L3_Hijack_radio_128x64/frame_12.bm -F:4ba8d4b217fb37ac55891d5f7f3774f9:579:dolphin_inverted/L3_Hijack_radio_128x64/frame_13.bm -F:83b8d6c5434286affee629acc159de0b:526:dolphin_inverted/L3_Hijack_radio_128x64/frame_2.bm -F:1bac7bdea6a3ec80b298b1b96f5413ec:529:dolphin_inverted/L3_Hijack_radio_128x64/frame_3.bm -F:ffc79584acaac23aee3b188c7706fc22:571:dolphin_inverted/L3_Hijack_radio_128x64/frame_4.bm -F:c9124f0f04071a6a0143dcbc81aec9ae:574:dolphin_inverted/L3_Hijack_radio_128x64/frame_5.bm -F:74cd5d51a5d1879398edbbe6039add1a:524:dolphin_inverted/L3_Hijack_radio_128x64/frame_6.bm -F:55cbc8656a4e321318442133c3b23828:655:dolphin_inverted/L3_Hijack_radio_128x64/frame_7.bm -F:0b487bd97bb0597e3379e4de4bf7299f:644:dolphin_inverted/L3_Hijack_radio_128x64/frame_8.bm -F:6d52ae12ef2b927a8eaceee964edac30:606:dolphin_inverted/L3_Hijack_radio_128x64/frame_9.bm -F:8583743f18a12ff647d3478e7aebdad6:230:dolphin_inverted/L3_Hijack_radio_128x64/meta.txt -F:f5f02a9df03bba734bdb7ed3297795f0:611:dolphin_inverted/L3_Lab_research_128x54/frame_0.bm -F:8f9655ad286464159443922d00e45620:614:dolphin_inverted/L3_Lab_research_128x54/frame_1.bm -F:7793b1bc107d4ea2e311e92dc16bf946:576:dolphin_inverted/L3_Lab_research_128x54/frame_10.bm -F:f24b8409f9dc770f3845424fe0ab489e:585:dolphin_inverted/L3_Lab_research_128x54/frame_11.bm -F:4ea93c4482dac43f40b67cc308f21e6d:571:dolphin_inverted/L3_Lab_research_128x54/frame_12.bm -F:cf3bb68dc78c568db22f37057a9fdd66:615:dolphin_inverted/L3_Lab_research_128x54/frame_13.bm -F:79719219aaebc95ea525def9173cabf5:618:dolphin_inverted/L3_Lab_research_128x54/frame_2.bm -F:05572cfd756704acd6ce9d6c15d03fc0:608:dolphin_inverted/L3_Lab_research_128x54/frame_3.bm -F:a26604a0d5427d5cf62a7a911a68b16c:615:dolphin_inverted/L3_Lab_research_128x54/frame_4.bm -F:9edc345fe53017970f93dc680818e63e:618:dolphin_inverted/L3_Lab_research_128x54/frame_5.bm -F:cf3bb68dc78c568db22f37057a9fdd66:615:dolphin_inverted/L3_Lab_research_128x54/frame_6.bm -F:5442895c85f769349288aa3df0990f9d:585:dolphin_inverted/L3_Lab_research_128x54/frame_7.bm -F:33b8fde22f34ef556b64b77164bc19b0:578:dolphin_inverted/L3_Lab_research_128x54/frame_8.bm -F:f267f0654781049ca323b11bb4375519:581:dolphin_inverted/L3_Lab_research_128x54/frame_9.bm -F:41106c0cbc5144f151b2b2d3daaa0527:727:dolphin_inverted/L3_Lab_research_128x54/meta.txt -D:infrared/assets -F:a565c3a381695a5f2ba7a0698460238c:74833:infrared/assets/tv.ir -F:a157a80f5a668700403d870c23b9567d:470:music_player/Marble_Machine.fmf -D:nfc/assets -F:81dc04c7b181f94b644079a71476dff4:4742:nfc/assets/aid.nfc -F:86efbebdf41bb6bf15cc51ef88f069d5:2565:nfc/assets/country_code.nfc -F:41b4f08774249014cb8d3dffa5f5c07d:1757:nfc/assets/currency_code.nfc -F:c60e862919731b0bd538a1001bbc1098:17453:nfc/assets/mf_classic_dict.nfc -D:subghz/assets -F:dda1ef895b8a25fde57c874feaaef997:650:subghz/assets/came_atomo -F:788eef2cc74e29f3388463d6607dab0d:3264:subghz/assets/keeloq_mfcodes -F:9214f9c10463b746a27e82ce0b96e040:465:subghz/assets/keeloq_mfcodes_user -F:653bd8d349055a41e1152e557d4a52d3:202:subghz/assets/nice_flor_s -F:c1c63fbd5f5aa3ea504027014652191f:1150:subghz/assets/setting_user -D:u2f/assets -F:7e11e688e39034bbb9d88410044795e1:365:u2f/assets/cert.der -F:f60b88c20ed479ed9684e249f7134618:264:u2f/assets/cert_key.u2f diff --git a/assets/resources/badusb/demo_macos.txt b/assets/resources/badusb/demo_macos.txt deleted file mode 100644 index 6da305905a2..00000000000 --- a/assets/resources/badusb/demo_macos.txt +++ /dev/null @@ -1,86 +0,0 @@ -ID 1234:5678 Apple:Keyboard -REM You can change these values to VID/PID of original Apple keyboard -REM to bypass Keyboard Setup Assistant - -REM This is BadUSB demo script for macOS - -REM Open terminal window -DELAY 1000 -GUI SPACE -DELAY 500 -STRING terminal -DELAY 500 -ENTER -DELAY 750 - -REM Copy-Paste previous string -UP -CTRL c - -REM Bigger shell script example -STRING cat > /dev/null << EOF -ENTER - -STRING Hello World! -ENTER - -DEFAULT_DELAY 50 - -STRING = -REPEAT 59 -ENTER -ENTER - -STRING _.-------.._ -, -ENTER -HOME -STRING .-"```"--..,,_/ /`-, -, \ -ENTER -HOME -STRING .:" /:/ /'\ \ ,_..., `. | | -ENTER -HOME -STRING / ,----/:/ /`\ _\~`_-"` _; -ENTER -HOME -STRING ' / /`"""'\ \ \.~`_-' ,-"'/ -ENTER -HOME -STRING | | | 0 | | .-' ,/` / -ENTER -HOME -STRING | ,..\ \ ,.-"` ,/` / -ENTER -HOME -STRING ; : `/`""\` ,/--==,/-----, -ENTER -HOME -STRING | `-...| -.___-Z:_______J...---; -ENTER -HOME -STRING : ` _-' -ENTER -HOME -STRING _L_ _ ___ ___ ___ ___ ____--"` -ENTER -HOME -STRING | __|| | |_ _|| _ \| _ \| __|| _ \ -ENTER -HOME -STRING | _| | |__ | | | _/| _/| _| | / -ENTER -HOME -STRING |_| |____||___||_| |_| |___||_|_\ -ENTER -HOME -ENTER - -STRING Flipper Zero BadUSB feature is compatible with USB Rubber Ducky script format -ENTER -STRING More information about script syntax can be found here: -ENTER -STRING https://github.com/hak5darren/USB-Rubber-Ducky/wiki/Duckyscript -ENTER - -STRING EOF -ENTER diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_0.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_0.bm deleted file mode 100644 index 46079c3728b..00000000000 Binary files a/assets/resources/dolphin/L1_Boxing_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_1.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_1.bm deleted file mode 100644 index e12d7079629..00000000000 Binary files a/assets/resources/dolphin/L1_Boxing_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_2.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_2.bm deleted file mode 100644 index b416740f277..00000000000 Binary files a/assets/resources/dolphin/L1_Boxing_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_3.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_3.bm deleted file mode 100644 index b7e15ddeec9..00000000000 Binary files a/assets/resources/dolphin/L1_Boxing_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_4.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_4.bm deleted file mode 100644 index 202ad6e37bf..00000000000 Binary files a/assets/resources/dolphin/L1_Boxing_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_5.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_5.bm deleted file mode 100644 index cdc0a2a3431..00000000000 Binary files a/assets/resources/dolphin/L1_Boxing_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Boxing_128x64/frame_6.bm b/assets/resources/dolphin/L1_Boxing_128x64/frame_6.bm deleted file mode 100644 index e8ea3aa6836..00000000000 Binary files a/assets/resources/dolphin/L1_Boxing_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Boxing_128x64/meta.txt b/assets/resources/dolphin/L1_Boxing_128x64/meta.txt deleted file mode 100644 index c66998e7d85..00000000000 --- a/assets/resources/dolphin/L1_Boxing_128x64/meta.txt +++ /dev/null @@ -1,32 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 9 -Active frames: 7 -Frames order: 0 1 2 1 3 1 2 3 1 4 5 6 5 6 5 4 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 2 - -Slot: 0 -X: 78 -Y: 16 -Text: F*&K!!! -AlignH: Left -AlignV: Bottom -StartFrame: 2 -EndFrame: 4 - -Slot: 1 -X: 78 -Y: 16 -Text: What ya\nlookin at?! -AlignH: Left -AlignV: Center -StartFrame: 10 -EndFrame: 15 diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_0.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_0.bm deleted file mode 100644 index 9147714c1e2..00000000000 Binary files a/assets/resources/dolphin/L1_Cry_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_1.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_1.bm deleted file mode 100644 index 789273d9da3..00000000000 Binary files a/assets/resources/dolphin/L1_Cry_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_2.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_2.bm deleted file mode 100644 index a3c87e0a6de..00000000000 Binary files a/assets/resources/dolphin/L1_Cry_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_3.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_3.bm deleted file mode 100644 index ba3012b7e5a..00000000000 Binary files a/assets/resources/dolphin/L1_Cry_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_4.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_4.bm deleted file mode 100644 index 1ce28c7adb7..00000000000 Binary files a/assets/resources/dolphin/L1_Cry_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_5.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_5.bm deleted file mode 100644 index 4cbc4968951..00000000000 Binary files a/assets/resources/dolphin/L1_Cry_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_6.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_6.bm deleted file mode 100644 index cba81ef35db..00000000000 Binary files a/assets/resources/dolphin/L1_Cry_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Cry_128x64/frame_7.bm b/assets/resources/dolphin/L1_Cry_128x64/frame_7.bm deleted file mode 100644 index da28419fd27..00000000000 Binary files a/assets/resources/dolphin/L1_Cry_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Cry_128x64/meta.txt b/assets/resources/dolphin/L1_Cry_128x64/meta.txt deleted file mode 100644 index 1b7d13dd88e..00000000000 --- a/assets/resources/dolphin/L1_Cry_128x64/meta.txt +++ /dev/null @@ -1,41 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 8 -Active frames: 4 -Frames order: 0 1 2 3 4 2 3 4 5 6 7 6 -Active cycles: 2 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 2 - -Slot: 0 -X: 22 -Y: 40 -Text: I miss you -AlignH: Right -AlignV: Bottom -StartFrame: 9 -EndFrame: 11 - -Slot: 0 -X: 17 -Y: 40 -Text: my friend... -AlignH: Right -AlignV: Bottom -StartFrame: 12 -EndFrame: 15 - -Slot: 1 -X: 2 -Y: 29 -Text: Why are you\nalways away? -AlignH: Right -AlignV: Bottom -StartFrame: 9 -EndFrame: 15 diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_0.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_0.bm deleted file mode 100644 index 8558f0f5bd2..00000000000 Binary files a/assets/resources/dolphin/L1_Furippa1_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_1.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_1.bm deleted file mode 100644 index ac7a126ea47..00000000000 Binary files a/assets/resources/dolphin/L1_Furippa1_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_10.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_10.bm deleted file mode 100644 index c5312e5e5a8..00000000000 Binary files a/assets/resources/dolphin/L1_Furippa1_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_11.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_11.bm deleted file mode 100644 index c91ed2fd23e..00000000000 Binary files a/assets/resources/dolphin/L1_Furippa1_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_12.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_12.bm deleted file mode 100644 index 392905a559e..00000000000 Binary files a/assets/resources/dolphin/L1_Furippa1_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_13.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_13.bm deleted file mode 100644 index aa5353e988d..00000000000 Binary files a/assets/resources/dolphin/L1_Furippa1_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_14.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_14.bm deleted file mode 100644 index 837c6c71def..00000000000 Binary files a/assets/resources/dolphin/L1_Furippa1_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_15.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_15.bm deleted file mode 100644 index 4cb6e53325c..00000000000 Binary files a/assets/resources/dolphin/L1_Furippa1_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_16.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_16.bm deleted file mode 100644 index ce3ccdecefb..00000000000 Binary files a/assets/resources/dolphin/L1_Furippa1_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_17.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_17.bm deleted file mode 100644 index 02af0bcf325..00000000000 Binary files a/assets/resources/dolphin/L1_Furippa1_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_18.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_18.bm deleted file mode 100644 index 72620a7c927..00000000000 Binary files a/assets/resources/dolphin/L1_Furippa1_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_2.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_2.bm deleted file mode 100644 index 94357802fc8..00000000000 Binary files a/assets/resources/dolphin/L1_Furippa1_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_3.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_3.bm deleted file mode 100644 index b0d0e691407..00000000000 Binary files a/assets/resources/dolphin/L1_Furippa1_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_4.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_4.bm deleted file mode 100644 index 3413e507294..00000000000 Binary files a/assets/resources/dolphin/L1_Furippa1_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_5.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_5.bm deleted file mode 100644 index fd83dd1c654..00000000000 Binary files a/assets/resources/dolphin/L1_Furippa1_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_6.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_6.bm deleted file mode 100644 index 8c946d1848b..00000000000 Binary files a/assets/resources/dolphin/L1_Furippa1_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_7.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_7.bm deleted file mode 100644 index 8558f0f5bd2..00000000000 Binary files a/assets/resources/dolphin/L1_Furippa1_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_8.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_8.bm deleted file mode 100644 index a57e4bac7dc..00000000000 Binary files a/assets/resources/dolphin/L1_Furippa1_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/frame_9.bm b/assets/resources/dolphin/L1_Furippa1_128x64/frame_9.bm deleted file mode 100644 index 114b26391f8..00000000000 Binary files a/assets/resources/dolphin/L1_Furippa1_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Furippa1_128x64/meta.txt b/assets/resources/dolphin/L1_Furippa1_128x64/meta.txt deleted file mode 100644 index c21027e49cb..00000000000 --- a/assets/resources/dolphin/L1_Furippa1_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 8 -Active frames: 11 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L1_Laptop_128x51/frame_0.bm b/assets/resources/dolphin/L1_Laptop_128x51/frame_0.bm deleted file mode 100644 index 5eb2bdd05d2..00000000000 Binary files a/assets/resources/dolphin/L1_Laptop_128x51/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Laptop_128x51/frame_1.bm b/assets/resources/dolphin/L1_Laptop_128x51/frame_1.bm deleted file mode 100644 index 210f0c918ce..00000000000 Binary files a/assets/resources/dolphin/L1_Laptop_128x51/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Laptop_128x51/frame_2.bm b/assets/resources/dolphin/L1_Laptop_128x51/frame_2.bm deleted file mode 100644 index ff2851c2808..00000000000 Binary files a/assets/resources/dolphin/L1_Laptop_128x51/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Laptop_128x51/frame_3.bm b/assets/resources/dolphin/L1_Laptop_128x51/frame_3.bm deleted file mode 100644 index 360d405abc2..00000000000 Binary files a/assets/resources/dolphin/L1_Laptop_128x51/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Laptop_128x51/frame_4.bm b/assets/resources/dolphin/L1_Laptop_128x51/frame_4.bm deleted file mode 100644 index 9dd5111f8f3..00000000000 Binary files a/assets/resources/dolphin/L1_Laptop_128x51/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Laptop_128x51/frame_5.bm b/assets/resources/dolphin/L1_Laptop_128x51/frame_5.bm deleted file mode 100644 index e56c3f1a8a9..00000000000 Binary files a/assets/resources/dolphin/L1_Laptop_128x51/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Laptop_128x51/frame_6.bm b/assets/resources/dolphin/L1_Laptop_128x51/frame_6.bm deleted file mode 100644 index e9b0c0dffa8..00000000000 Binary files a/assets/resources/dolphin/L1_Laptop_128x51/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Laptop_128x51/frame_7.bm b/assets/resources/dolphin/L1_Laptop_128x51/frame_7.bm deleted file mode 100644 index 4663c6b0371..00000000000 Binary files a/assets/resources/dolphin/L1_Laptop_128x51/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Laptop_128x51/meta.txt b/assets/resources/dolphin/L1_Laptop_128x51/meta.txt deleted file mode 100644 index 90cdc5ce9d8..00000000000 --- a/assets/resources/dolphin/L1_Laptop_128x51/meta.txt +++ /dev/null @@ -1,32 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 51 -Passive frames: 6 -Active frames: 2 -Frames order: 0 1 2 3 4 5 6 7 -Active cycles: 4 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 60 -Y: 23 -Text: I have to rest -AlignH: Left -AlignV: Bottom -StartFrame: 7 -EndFrame: 10 - -Slot: 0 -X: 60 -Y: 23 -Text: but not today -AlignH: Left -AlignV: Bottom -StartFrame: 11 -EndFrame: 13 diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_0.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_0.bm deleted file mode 100644 index d4cf85bada7..00000000000 Binary files a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_1.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_1.bm deleted file mode 100644 index f1f0e89f0bf..00000000000 Binary files a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_10.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_10.bm deleted file mode 100644 index b91030b12f5..00000000000 Binary files a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_11.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_11.bm deleted file mode 100644 index fe93787f2db..00000000000 Binary files a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_12.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_12.bm deleted file mode 100644 index 13fb8e98514..00000000000 Binary files a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_2.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_2.bm deleted file mode 100644 index 3050ba38f22..00000000000 Binary files a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_3.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_3.bm deleted file mode 100644 index 0c0c832385c..00000000000 Binary files a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_4.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_4.bm deleted file mode 100644 index 5e74ea12ae2..00000000000 Binary files a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_5.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_5.bm deleted file mode 100644 index 5c556cfdfd3..00000000000 Binary files a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_6.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_6.bm deleted file mode 100644 index 1dc1567649a..00000000000 Binary files a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_7.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_7.bm deleted file mode 100644 index e992d75c7a0..00000000000 Binary files a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_8.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_8.bm deleted file mode 100644 index 49266d7c225..00000000000 Binary files a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_9.bm b/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_9.bm deleted file mode 100644 index 6ebc55c16ed..00000000000 Binary files a/assets/resources/dolphin/L1_Leaving_sad_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Leaving_sad_128x64/meta.txt b/assets/resources/dolphin/L1_Leaving_sad_128x64/meta.txt deleted file mode 100644 index 87600307954..00000000000 --- a/assets/resources/dolphin/L1_Leaving_sad_128x64/meta.txt +++ /dev/null @@ -1,32 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 4 -Active frames: 42 -Frames order: 0 1 2 1 3 4 5 6 7 8 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 10 11 12 4 3 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 51 -Y: 49 -Text: Adios. -AlignH: Center -AlignV: Top -StartFrame: 6 -EndFrame: 9 - -Slot: 0 -X: 1 -Y: 49 -Text: Forgot something... -AlignH: Center -AlignV: Top -StartFrame: 42 -EndFrame: 45 diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_0.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_0.bm deleted file mode 100644 index d200bb2af70..00000000000 Binary files a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_1.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_1.bm deleted file mode 100644 index 0ec761cbfd5..00000000000 Binary files a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_10.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_10.bm deleted file mode 100644 index d4207c95d87..00000000000 Binary files a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_11.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_11.bm deleted file mode 100644 index 35955bc2021..00000000000 Binary files a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_12.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_12.bm deleted file mode 100644 index 80adf867129..00000000000 Binary files a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_13.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_13.bm deleted file mode 100644 index 1994c0966a4..00000000000 Binary files a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_2.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_2.bm deleted file mode 100644 index d3f19290d29..00000000000 Binary files a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_3.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_3.bm deleted file mode 100644 index 1fc2e2b267a..00000000000 Binary files a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_4.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_4.bm deleted file mode 100644 index 6152a83f8e6..00000000000 Binary files a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_5.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_5.bm deleted file mode 100644 index 842600e6d7b..00000000000 Binary files a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_6.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_6.bm deleted file mode 100644 index 887a4b86641..00000000000 Binary files a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_7.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_7.bm deleted file mode 100644 index 8e9a34e971b..00000000000 Binary files a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_8.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_8.bm deleted file mode 100644 index a430e480a6f..00000000000 Binary files a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_9.bm b/assets/resources/dolphin/L1_Mad_fist_128x64/frame_9.bm deleted file mode 100644 index 487f50b354b..00000000000 Binary files a/assets/resources/dolphin/L1_Mad_fist_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Mad_fist_128x64/meta.txt b/assets/resources/dolphin/L1_Mad_fist_128x64/meta.txt deleted file mode 100644 index 93e59e49b43..00000000000 --- a/assets/resources/dolphin/L1_Mad_fist_128x64/meta.txt +++ /dev/null @@ -1,23 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 7 -Active frames: 13 -Frames order: 0 1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 12 13 12 13 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 67 -Y: 24 -Text: Am I a joke\nto you?!?! -AlignH: Left -AlignV: Center -StartFrame: 15 -EndFrame: 19 diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_0.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_0.bm deleted file mode 100644 index 1169e42d690..00000000000 Binary files a/assets/resources/dolphin/L1_Read_books_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_1.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_1.bm deleted file mode 100644 index 80e2f39bb69..00000000000 Binary files a/assets/resources/dolphin/L1_Read_books_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_2.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_2.bm deleted file mode 100644 index 959b02556d6..00000000000 Binary files a/assets/resources/dolphin/L1_Read_books_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_3.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_3.bm deleted file mode 100644 index 8c106d906a4..00000000000 Binary files a/assets/resources/dolphin/L1_Read_books_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_4.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_4.bm deleted file mode 100644 index 389d2a8ef52..00000000000 Binary files a/assets/resources/dolphin/L1_Read_books_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_5.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_5.bm deleted file mode 100644 index 4b65b6d9b0f..00000000000 Binary files a/assets/resources/dolphin/L1_Read_books_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_6.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_6.bm deleted file mode 100644 index 451c80a26af..00000000000 Binary files a/assets/resources/dolphin/L1_Read_books_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_7.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_7.bm deleted file mode 100644 index 417809078da..00000000000 Binary files a/assets/resources/dolphin/L1_Read_books_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Read_books_128x64/frame_8.bm b/assets/resources/dolphin/L1_Read_books_128x64/frame_8.bm deleted file mode 100644 index 4370393dc8e..00000000000 Binary files a/assets/resources/dolphin/L1_Read_books_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Read_books_128x64/meta.txt b/assets/resources/dolphin/L1_Read_books_128x64/meta.txt deleted file mode 100644 index 7432507ce29..00000000000 --- a/assets/resources/dolphin/L1_Read_books_128x64/meta.txt +++ /dev/null @@ -1,23 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 13 -Active frames: 2 -Frames order: 0 1 0 2 3 3 4 0 1 5 6 1 1 7 8 -Active cycles: 2 -Frame rate: 2 -Duration: 3600 -Active cooldown: 5 - -Bubble slots: 1 - -Slot: 0 -X: 5 -Y: 28 -Text: Predictable twist -AlignH: Right -AlignV: Bottom -StartFrame: 14 -EndFrame: 16 diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_0.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_0.bm deleted file mode 100644 index a278e3a9d69..00000000000 Binary files a/assets/resources/dolphin/L1_Recording_128x51/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_1.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_1.bm deleted file mode 100644 index 403a3ccd751..00000000000 Binary files a/assets/resources/dolphin/L1_Recording_128x51/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_10.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_10.bm deleted file mode 100644 index 7684384fdfb..00000000000 Binary files a/assets/resources/dolphin/L1_Recording_128x51/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_11.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_11.bm deleted file mode 100644 index 478c1e8b5cc..00000000000 Binary files a/assets/resources/dolphin/L1_Recording_128x51/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_2.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_2.bm deleted file mode 100644 index f5dbbc71c2d..00000000000 Binary files a/assets/resources/dolphin/L1_Recording_128x51/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_3.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_3.bm deleted file mode 100644 index eb4b3644b23..00000000000 Binary files a/assets/resources/dolphin/L1_Recording_128x51/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_4.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_4.bm deleted file mode 100644 index e0db66ffe30..00000000000 Binary files a/assets/resources/dolphin/L1_Recording_128x51/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_5.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_5.bm deleted file mode 100644 index 9265ef6d6c2..00000000000 Binary files a/assets/resources/dolphin/L1_Recording_128x51/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_6.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_6.bm deleted file mode 100644 index b2676b7ce2f..00000000000 Binary files a/assets/resources/dolphin/L1_Recording_128x51/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_7.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_7.bm deleted file mode 100644 index 70a1f2d1379..00000000000 Binary files a/assets/resources/dolphin/L1_Recording_128x51/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_8.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_8.bm deleted file mode 100644 index 05f98d639bb..00000000000 Binary files a/assets/resources/dolphin/L1_Recording_128x51/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Recording_128x51/frame_9.bm b/assets/resources/dolphin/L1_Recording_128x51/frame_9.bm deleted file mode 100644 index 65b723203c3..00000000000 Binary files a/assets/resources/dolphin/L1_Recording_128x51/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Recording_128x51/meta.txt b/assets/resources/dolphin/L1_Recording_128x51/meta.txt deleted file mode 100644 index de37d5b2e4e..00000000000 --- a/assets/resources/dolphin/L1_Recording_128x51/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 51 -Passive frames: 6 -Active frames: 6 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 5 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L1_Sleep_128x64/frame_0.bm b/assets/resources/dolphin/L1_Sleep_128x64/frame_0.bm deleted file mode 100644 index 9560e1f4155..00000000000 Binary files a/assets/resources/dolphin/L1_Sleep_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Sleep_128x64/frame_1.bm b/assets/resources/dolphin/L1_Sleep_128x64/frame_1.bm deleted file mode 100644 index 238b50a2e7b..00000000000 Binary files a/assets/resources/dolphin/L1_Sleep_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Sleep_128x64/frame_2.bm b/assets/resources/dolphin/L1_Sleep_128x64/frame_2.bm deleted file mode 100644 index b18e615fe3e..00000000000 Binary files a/assets/resources/dolphin/L1_Sleep_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Sleep_128x64/frame_3.bm b/assets/resources/dolphin/L1_Sleep_128x64/frame_3.bm deleted file mode 100644 index 72fedc5e13c..00000000000 Binary files a/assets/resources/dolphin/L1_Sleep_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Sleep_128x64/meta.txt b/assets/resources/dolphin/L1_Sleep_128x64/meta.txt deleted file mode 100644 index ffd845e8ce4..00000000000 --- a/assets/resources/dolphin/L1_Sleep_128x64/meta.txt +++ /dev/null @@ -1,41 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 2 -Active frames: 4 -Frames order: 0 1 2 3 2 3 -Active cycles: 2 -Frame rate: 2 -Duration: 3600 -Active cooldown: 5 - -Bubble slots: 2 - -Slot: 0 -X: 53 -Y: 20 -Text: In a lucid dream,\nI could walk... -AlignH: Left -AlignV: Bottom -StartFrame: 3 -EndFrame: 9 - -Slot: 1 -X: 53 -Y: 20 -Text: OH MY GOD! -AlignH: Left -AlignV: Bottom -StartFrame: 3 -EndFrame: 5 - -Slot: 1 -X: 53 -Y: 31 -Text: Just a dream... -AlignH: Left -AlignV: Bottom -StartFrame: 6 -EndFrame: 9 diff --git a/assets/resources/dolphin/L1_Waves_128x50/frame_0.bm b/assets/resources/dolphin/L1_Waves_128x50/frame_0.bm deleted file mode 100644 index aa745466624..00000000000 Binary files a/assets/resources/dolphin/L1_Waves_128x50/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Waves_128x50/frame_1.bm b/assets/resources/dolphin/L1_Waves_128x50/frame_1.bm deleted file mode 100644 index a23d250b969..00000000000 Binary files a/assets/resources/dolphin/L1_Waves_128x50/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Waves_128x50/frame_2.bm b/assets/resources/dolphin/L1_Waves_128x50/frame_2.bm deleted file mode 100644 index cd39b17ec89..00000000000 Binary files a/assets/resources/dolphin/L1_Waves_128x50/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Waves_128x50/frame_3.bm b/assets/resources/dolphin/L1_Waves_128x50/frame_3.bm deleted file mode 100644 index 2d5452d7c93..00000000000 Binary files a/assets/resources/dolphin/L1_Waves_128x50/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L1_Waves_128x50/meta.txt b/assets/resources/dolphin/L1_Waves_128x50/meta.txt deleted file mode 100644 index 376447af786..00000000000 --- a/assets/resources/dolphin/L1_Waves_128x50/meta.txt +++ /dev/null @@ -1,50 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 50 -Passive frames: 2 -Active frames: 4 -Frames order: 0 1 2 3 2 3 -Active cycles: 2 -Frame rate: 2 -Duration: 3600 -Active cooldown: 5 - -Bubble slots: 3 - -Slot: 0 -X: 1 -Y: 17 -Text: I am happy,\nmy friend! -AlignH: Right -AlignV: Bottom -StartFrame: 3 -EndFrame: 9 - -Slot: 1 -X: 1 -Y: 17 -Text: So long and\nthanks for\nall the fish! -AlignH: Right -AlignV: Center -StartFrame: 3 -EndFrame: 9 - -Slot: 2 -X: 1 -Y: 25 -Text: I wish I could -AlignH: Right -AlignV: Bottom -StartFrame: 3 -EndFrame: 5 - -Slot: 2 -X: 1 -Y: 25 -Text: swim all day -AlignH: Right -AlignV: Bottom -StartFrame: 6 -EndFrame: 9 diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_0.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_0.bm deleted file mode 100644 index 7e83e14a5ae..00000000000 Binary files a/assets/resources/dolphin/L2_Furippa2_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_1.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_1.bm deleted file mode 100644 index f149f01e9ea..00000000000 Binary files a/assets/resources/dolphin/L2_Furippa2_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_10.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_10.bm deleted file mode 100644 index c5312e5e5a8..00000000000 Binary files a/assets/resources/dolphin/L2_Furippa2_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_11.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_11.bm deleted file mode 100644 index c91ed2fd23e..00000000000 Binary files a/assets/resources/dolphin/L2_Furippa2_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_12.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_12.bm deleted file mode 100644 index 392905a559e..00000000000 Binary files a/assets/resources/dolphin/L2_Furippa2_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_13.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_13.bm deleted file mode 100644 index aa5353e988d..00000000000 Binary files a/assets/resources/dolphin/L2_Furippa2_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_14.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_14.bm deleted file mode 100644 index 837c6c71def..00000000000 Binary files a/assets/resources/dolphin/L2_Furippa2_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_15.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_15.bm deleted file mode 100644 index 0000a886313..00000000000 Binary files a/assets/resources/dolphin/L2_Furippa2_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_16.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_16.bm deleted file mode 100644 index 46a96ff4617..00000000000 Binary files a/assets/resources/dolphin/L2_Furippa2_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_17.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_17.bm deleted file mode 100644 index edccc7396ec..00000000000 Binary files a/assets/resources/dolphin/L2_Furippa2_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_18.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_18.bm deleted file mode 100644 index 84f60c81ef6..00000000000 Binary files a/assets/resources/dolphin/L2_Furippa2_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_2.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_2.bm deleted file mode 100644 index ade67d1016a..00000000000 Binary files a/assets/resources/dolphin/L2_Furippa2_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_3.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_3.bm deleted file mode 100644 index d05e8ae2b47..00000000000 Binary files a/assets/resources/dolphin/L2_Furippa2_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_4.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_4.bm deleted file mode 100644 index 5aef127622c..00000000000 Binary files a/assets/resources/dolphin/L2_Furippa2_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_5.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_5.bm deleted file mode 100644 index 3be1790d74c..00000000000 Binary files a/assets/resources/dolphin/L2_Furippa2_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_6.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_6.bm deleted file mode 100644 index c457e787714..00000000000 Binary files a/assets/resources/dolphin/L2_Furippa2_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_7.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_7.bm deleted file mode 100644 index 7e83e14a5ae..00000000000 Binary files a/assets/resources/dolphin/L2_Furippa2_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_8.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_8.bm deleted file mode 100644 index b7b871d1378..00000000000 Binary files a/assets/resources/dolphin/L2_Furippa2_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/frame_9.bm b/assets/resources/dolphin/L2_Furippa2_128x64/frame_9.bm deleted file mode 100644 index 269e5b1d860..00000000000 Binary files a/assets/resources/dolphin/L2_Furippa2_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Furippa2_128x64/meta.txt b/assets/resources/dolphin/L2_Furippa2_128x64/meta.txt deleted file mode 100644 index c21027e49cb..00000000000 --- a/assets/resources/dolphin/L2_Furippa2_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 8 -Active frames: 11 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_0.bm b/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_0.bm deleted file mode 100644 index 3ff70a91699..00000000000 Binary files a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_1.bm b/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_1.bm deleted file mode 100644 index ed11583f8f8..00000000000 Binary files a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_2.bm b/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_2.bm deleted file mode 100644 index 41850505b54..00000000000 Binary files a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_3.bm b/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_3.bm deleted file mode 100644 index d4b47960a93..00000000000 Binary files a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_4.bm b/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_4.bm deleted file mode 100644 index ee3a5f188d6..00000000000 Binary files a/assets/resources/dolphin/L2_Hacking_pc_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Hacking_pc_128x64/meta.txt b/assets/resources/dolphin/L2_Hacking_pc_128x64/meta.txt deleted file mode 100644 index 8ad8d42a345..00000000000 --- a/assets/resources/dolphin/L2_Hacking_pc_128x64/meta.txt +++ /dev/null @@ -1,32 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 3 -Active frames: 2 -Frames order: 0 1 2 3 4 -Active cycles: 4 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 22 -Y: 25 -Text: Mess with\nthe best, -AlignH: Right -AlignV: Center -StartFrame: 4 -EndFrame: 7 - -Slot: 0 -X: 31 -Y: 25 -Text: die like\nthe rest. -AlignH: Right -AlignV: Center -StartFrame: 8 -EndFrame: 10 diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_0.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_0.bm deleted file mode 100644 index 3fc3644065c..00000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_1.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_1.bm deleted file mode 100644 index 60a2e700a3d..00000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_10.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_10.bm deleted file mode 100644 index f8081935808..00000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_2.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_2.bm deleted file mode 100644 index 1d981e7d51a..00000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_3.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_3.bm deleted file mode 100644 index 48d0aa85d0e..00000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_4.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_4.bm deleted file mode 100644 index a961f4c0a17..00000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_5.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_5.bm deleted file mode 100644 index 81b5693390a..00000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_6.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_6.bm deleted file mode 100644 index 2f030833a27..00000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_7.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_7.bm deleted file mode 100644 index 4519819ea5c..00000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_8.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_8.bm deleted file mode 100644 index 4bb3b79839d..00000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Soldering_128x64/frame_9.bm b/assets/resources/dolphin/L2_Soldering_128x64/frame_9.bm deleted file mode 100644 index 1339c607e63..00000000000 Binary files a/assets/resources/dolphin/L2_Soldering_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L2_Soldering_128x64/meta.txt b/assets/resources/dolphin/L2_Soldering_128x64/meta.txt deleted file mode 100644 index b705bf62361..00000000000 --- a/assets/resources/dolphin/L2_Soldering_128x64/meta.txt +++ /dev/null @@ -1,23 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 9 -Active frames: 5 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 9 10 9 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 71 -Y: 28 -Text: I am busy rn -AlignH: Left -AlignV: Center -StartFrame: 10 -EndFrame: 13 diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_0.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_0.bm deleted file mode 100644 index 07a63d6424e..00000000000 Binary files a/assets/resources/dolphin/L3_Furippa3_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_1.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_1.bm deleted file mode 100644 index 6d118f28a9c..00000000000 Binary files a/assets/resources/dolphin/L3_Furippa3_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_10.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_10.bm deleted file mode 100644 index 8010501d0e5..00000000000 Binary files a/assets/resources/dolphin/L3_Furippa3_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_11.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_11.bm deleted file mode 100644 index 4d650bff0ac..00000000000 Binary files a/assets/resources/dolphin/L3_Furippa3_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_12.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_12.bm deleted file mode 100644 index 392905a559e..00000000000 Binary files a/assets/resources/dolphin/L3_Furippa3_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_13.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_13.bm deleted file mode 100644 index aa5353e988d..00000000000 Binary files a/assets/resources/dolphin/L3_Furippa3_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_14.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_14.bm deleted file mode 100644 index 837c6c71def..00000000000 Binary files a/assets/resources/dolphin/L3_Furippa3_128x64/frame_14.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_15.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_15.bm deleted file mode 100644 index 9ff56a5b6bd..00000000000 Binary files a/assets/resources/dolphin/L3_Furippa3_128x64/frame_15.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_16.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_16.bm deleted file mode 100644 index 34cc0b5122d..00000000000 Binary files a/assets/resources/dolphin/L3_Furippa3_128x64/frame_16.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_17.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_17.bm deleted file mode 100644 index 80cb06fd3cb..00000000000 Binary files a/assets/resources/dolphin/L3_Furippa3_128x64/frame_17.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_18.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_18.bm deleted file mode 100644 index 719a80f0e03..00000000000 Binary files a/assets/resources/dolphin/L3_Furippa3_128x64/frame_18.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_2.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_2.bm deleted file mode 100644 index c014858502e..00000000000 Binary files a/assets/resources/dolphin/L3_Furippa3_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_3.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_3.bm deleted file mode 100644 index f70c33cadb0..00000000000 Binary files a/assets/resources/dolphin/L3_Furippa3_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_4.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_4.bm deleted file mode 100644 index 3fee74b775d..00000000000 Binary files a/assets/resources/dolphin/L3_Furippa3_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_5.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_5.bm deleted file mode 100644 index e363bf7d572..00000000000 Binary files a/assets/resources/dolphin/L3_Furippa3_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_6.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_6.bm deleted file mode 100644 index f46aabadf6b..00000000000 Binary files a/assets/resources/dolphin/L3_Furippa3_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_7.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_7.bm deleted file mode 100644 index 07a63d6424e..00000000000 Binary files a/assets/resources/dolphin/L3_Furippa3_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_8.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_8.bm deleted file mode 100644 index f8bae3a6256..00000000000 Binary files a/assets/resources/dolphin/L3_Furippa3_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/frame_9.bm b/assets/resources/dolphin/L3_Furippa3_128x64/frame_9.bm deleted file mode 100644 index 6b5810dc991..00000000000 Binary files a/assets/resources/dolphin/L3_Furippa3_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Furippa3_128x64/meta.txt b/assets/resources/dolphin/L3_Furippa3_128x64/meta.txt deleted file mode 100644 index c21027e49cb..00000000000 --- a/assets/resources/dolphin/L3_Furippa3_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 8 -Active frames: 11 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_0.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_0.bm deleted file mode 100644 index cf2120ff470..00000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_1.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_1.bm deleted file mode 100644 index 24a49213232..00000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_10.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_10.bm deleted file mode 100644 index 1354c78f2ce..00000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_11.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_11.bm deleted file mode 100644 index c15289b5ec4..00000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_12.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_12.bm deleted file mode 100644 index ac9f083347c..00000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_13.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_13.bm deleted file mode 100644 index 9ad7b9cf364..00000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_2.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_2.bm deleted file mode 100644 index 30c4bedcda5..00000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_3.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_3.bm deleted file mode 100644 index dc0fb9b79cc..00000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_4.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_4.bm deleted file mode 100644 index 025477a7a94..00000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_5.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_5.bm deleted file mode 100644 index 89a4cd6acf8..00000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_6.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_6.bm deleted file mode 100644 index c93ff6acdd0..00000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_7.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_7.bm deleted file mode 100644 index fb6d9bd292c..00000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_8.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_8.bm deleted file mode 100644 index a0377f635fe..00000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_9.bm b/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_9.bm deleted file mode 100644 index 06f66ab3a6b..00000000000 Binary files a/assets/resources/dolphin/L3_Hijack_radio_128x64/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Hijack_radio_128x64/meta.txt b/assets/resources/dolphin/L3_Hijack_radio_128x64/meta.txt deleted file mode 100644 index 1d415b4b85e..00000000000 --- a/assets/resources/dolphin/L3_Hijack_radio_128x64/meta.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 64 -Passive frames: 8 -Active frames: 8 -Frames order: 0 1 2 3 4 5 4 6 7 8 9 10 11 12 11 13 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 0 diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_0.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_0.bm deleted file mode 100644 index db283e81fbb..00000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_0.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_1.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_1.bm deleted file mode 100644 index c600743700f..00000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_1.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_10.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_10.bm deleted file mode 100644 index 694302a9de7..00000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_10.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_11.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_11.bm deleted file mode 100644 index 246b955cf93..00000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_11.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_12.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_12.bm deleted file mode 100644 index b6fb1130bb8..00000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_12.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_13.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_13.bm deleted file mode 100644 index 561335413dc..00000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_13.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_2.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_2.bm deleted file mode 100644 index 1025137e462..00000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_2.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_3.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_3.bm deleted file mode 100644 index e623a1c0fa5..00000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_3.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_4.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_4.bm deleted file mode 100644 index 654a68e8760..00000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_4.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_5.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_5.bm deleted file mode 100644 index 14eae4c1c16..00000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_5.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_6.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_6.bm deleted file mode 100644 index 561335413dc..00000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_6.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_7.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_7.bm deleted file mode 100644 index c9b99a01448..00000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_7.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_8.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_8.bm deleted file mode 100644 index 812db0d466f..00000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_8.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/frame_9.bm b/assets/resources/dolphin/L3_Lab_research_128x54/frame_9.bm deleted file mode 100644 index 0cad9cc2655..00000000000 Binary files a/assets/resources/dolphin/L3_Lab_research_128x54/frame_9.bm and /dev/null differ diff --git a/assets/resources/dolphin/L3_Lab_research_128x54/meta.txt b/assets/resources/dolphin/L3_Lab_research_128x54/meta.txt deleted file mode 100644 index e83a8b43684..00000000000 --- a/assets/resources/dolphin/L3_Lab_research_128x54/meta.txt +++ /dev/null @@ -1,59 +0,0 @@ -Filetype: Flipper Animation -Version: 1 - -Width: 128 -Height: 54 -Passive frames: 6 -Active frames: 8 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 -Active cycles: 1 -Frame rate: 2 -Duration: 3600 -Active cooldown: 7 - -Bubble slots: 1 - -Slot: 0 -X: 71 -Y: 23 -Text: 7em!g!7j!\nVyP5?T- -AlignH: Left -AlignV: Center -StartFrame: 8 -EndFrame: 8 - -Slot: 0 -X: 71 -Y: 23 -Text: aUqF7sz!\n%9.mP5H -AlignH: Left -AlignV: Center -StartFrame: 9 -EndFrame: 9 - -Slot: 0 -X: 71 -Y: 23 -Text: 2%Kx2mV\nL8EyA84 -AlignH: Left -AlignV: Center -StartFrame: 10 -EndFrame: 10 - -Slot: 0 -X: 71 -Y: 23 -Text: U7%cRXr\nvbBa!_W1 -AlignH: Left -AlignV: Center -StartFrame: 11 -EndFrame: 11 - -Slot: 0 -X: 71 -Y: 23 -Text: 5rm_[K%\n!!(U9d$tE -AlignH: Left -AlignV: Center -StartFrame: 12 -EndFrame: 12 diff --git a/assets/resources/dolphin/manifest.txt b/assets/resources/dolphin/manifest.txt deleted file mode 100644 index 197060672e2..00000000000 --- a/assets/resources/dolphin/manifest.txt +++ /dev/null @@ -1,114 +0,0 @@ -Filetype: Flipper Animation Manifest -Version: 1 - -Name: L1_Waves_128x50 -Min butthurt: 0 -Max butthurt: 5 -Min level: 1 -Max level: 3 -Weight: 3 - -Name: L1_Laptop_128x51 -Min butthurt: 0 -Max butthurt: 7 -Min level: 1 -Max level: 1 -Weight: 3 - -Name: L1_Sleep_128x64 -Min butthurt: 0 -Max butthurt: 10 -Min level: 1 -Max level: 3 -Weight: 3 - -Name: L1_Recording_128x51 -Min butthurt: 0 -Max butthurt: 8 -Min level: 1 -Max level: 1 -Weight: 3 - -Name: L1_Furippa1_128x64 -Min butthurt: 0 -Max butthurt: 6 -Min level: 1 -Max level: 1 -Weight: 3 - -Name: L2_Furippa2_128x64 -Min butthurt: 0 -Max butthurt: 6 -Min level: 2 -Max level: 2 -Weight: 3 - -Name: L3_Furippa3_128x64 -Min butthurt: 0 -Max butthurt: 6 -Min level: 3 -Max level: 3 -Weight: 3 - -Name: L1_Read_books_128x64 -Min butthurt: 0 -Max butthurt: 8 -Min level: 1 -Max level: 1 -Weight: 3 - -Name: L2_Hacking_pc_128x64 -Min butthurt: 0 -Max butthurt: 8 -Min level: 2 -Max level: 2 -Weight: 3 - -Name: L1_Cry_128x64 -Min butthurt: 8 -Max butthurt: 13 -Min level: 1 -Max level: 3 -Weight: 3 - -Name: L1_Boxing_128x64 -Min butthurt: 10 -Max butthurt: 13 -Min level: 1 -Max level: 3 -Weight: 3 - -Name: L1_Mad_fist_128x64 -Min butthurt: 9 -Max butthurt: 13 -Min level: 1 -Max level: 3 -Weight: 3 - -Name: L3_Hijack_radio_128x64 -Min butthurt: 0 -Max butthurt: 8 -Min level: 3 -Max level: 3 -Weight: 3 - -Name: L3_Lab_research_128x54 -Min butthurt: 0 -Max butthurt: 10 -Min level: 3 -Max level: 3 -Weight: 3 - -Name: L2_Soldering_128x64 -Min butthurt: 0 -Max butthurt: 10 -Min level: 2 -Max level: 2 -Weight: 3 - -Name: L1_Leaving_sad_128x64 -Min butthurt: 14 -Max butthurt: 14 -Min level: 1 -Max level: 3 -Weight: 3 diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir deleted file mode 100755 index 79478e7eb0a..00000000000 --- a/assets/resources/infrared/assets/tv.ir +++ /dev/null @@ -1,1622 +0,0 @@ -Filetype: IR library file -Version: 1 -# -name: POWER -type: parsed -protocol: SIRC -address: 01 00 00 00 -command: 15 00 00 00 -# -name: POWER -type: parsed -protocol: SIRC -address: 10 00 00 00 -command: 15 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 08 00 00 00 -command: 05 00 00 00 -# -name: VOL+ -type: parsed -protocol: NEC -address: 08 00 00 00 -command: 00 00 00 00 -# -name: VOL- -type: parsed -protocol: NEC -address: 08 00 00 00 -command: 01 00 00 00 -# -name: CH+ -type: parsed -protocol: NEC -address: 08 00 00 00 -command: 02 00 00 00 -# -name: CH- -type: parsed -protocol: NEC -address: 08 00 00 00 -command: 03 00 00 00 -# -name: MUTE -type: parsed -protocol: NEC -address: 08 00 00 00 -command: 0b 00 00 00 -# -name: POWER -type: parsed -protocol: NECext -address: 00 df 00 00 -command: 1c 00 00 00 -# -name: VOL+ -type: parsed -protocol: NECext -address: 00 df 00 00 -command: 4b 00 00 00 -# -name: VOL- -type: parsed -protocol: NECext -address: 00 df 00 00 -command: 4f 00 00 00 -# -name: CH+ -type: parsed -protocol: NECext -address: 00 df 00 00 -command: 09 00 00 00 -# -name: CH- -type: parsed -protocol: NECext -address: 00 df 00 00 -command: 05 00 00 00 -# -name: MUTE -type: parsed -protocol: NECext -address: 00 df 00 00 -command: 08 00 00 00 -# -name: POWER -type: parsed -protocol: Samsung32 -address: 0e 00 00 00 -command: 0c 00 00 00 -# -name: MUTE -type: parsed -protocol: Samsung32 -address: 0e 00 00 00 -command: 0d 00 00 00 -# -name: VOL+ -type: parsed -protocol: Samsung32 -address: 0e 00 00 00 -command: 14 00 00 00 -# -name: VOL- -type: parsed -protocol: Samsung32 -address: 0e 00 00 00 -command: 15 00 00 00 -# -name: CH+ -type: parsed -protocol: Samsung32 -address: 0e 00 00 00 -command: 12 00 00 00 -# -name: CH- -type: parsed -protocol: Samsung32 -address: 0e 00 00 00 -command: 13 00 00 00 -# -name: POWER -type: parsed -protocol: RC6 -address: 00 00 00 00 -command: 0c 00 00 00 -# -name: POWER -type: parsed -protocol: Samsung32 -address: 07 00 00 00 -command: 02 00 00 00 -# -name: POWER -type: parsed -protocol: Samsung32 -address: 07 00 00 00 -command: E6 00 00 00 -# -name: VOL+ -type: parsed -protocol: Samsung32 -address: 07 00 00 00 -command: 07 00 00 00 -# -name: VOL- -type: parsed -protocol: Samsung32 -address: 07 00 00 00 -command: 0B 00 00 00 -# -name: CH+ -type: parsed -protocol: Samsung32 -address: 07 00 00 00 -command: 12 00 00 00 -# -name: CH- -type: parsed -protocol: Samsung32 -address: 07 00 00 00 -command: 10 00 00 00 -# -name: MUTE -type: parsed -protocol: Samsung32 -address: 07 00 00 00 -command: 0F 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 50 00 00 00 -command: 17 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 40 00 00 00 -command: 12 00 00 00 -# -name: POWER -type: parsed -protocol: NECext -address: 31 49 00 00 -command: 63 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: aa 00 00 00 -command: 1c 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 38 00 00 00 -command: 1c 00 00 00 -# -name: POWER -type: parsed -protocol: NECext -address: 83 7a 00 00 -command: 08 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 53 00 00 00 -command: 17 00 00 00 -# -name: POWER -type: parsed -protocol: NECext -address: 18 18 00 00 -command: c0 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 38 00 00 00 -command: 10 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: aa 00 00 00 -command: c5 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 04 00 00 00 -command: 08 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 18 00 00 00 -command: 08 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 71 00 00 00 -command: 08 00 00 00 -# -name: POWER -type: parsed -protocol: NECext -address: 80 6f 00 00 -command: 0a 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 48 00 00 00 -command: 00 00 00 00 -# -name: POWER -type: parsed -protocol: NECext -address: 80 7b 00 00 -command: 13 00 00 00 -# -name: POWER -type: parsed -protocol: Samsung32 -address: 0e 00 00 00 -command: 14 00 00 00 -# -name: POWER -type: parsed -protocol: NECext -address: 80 7e 00 00 -command: 18 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 50 00 00 00 -command: 08 00 00 00 -# -name: POWER -type: parsed -protocol: NECext -address: 80 75 00 00 -command: 0a 00 00 00 -# -name: POWER -type: parsed -protocol: NECext -address: 80 57 00 00 -command: 0a 00 00 00 -# -name: POWER -type: parsed -protocol: Samsung32 -address: 0b 00 00 00 -command: 0a 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: aa 00 00 00 -command: 1b 00 00 00 -# -name: POWER -type: parsed -protocol: NECext -address: 85 46 00 00 -command: 12 00 00 00 -# -name: POWER -type: parsed -protocol: Samsung32 -address: 05 00 00 00 -command: 02 00 00 00 -# -name: POWER -type: parsed -protocol: Samsung32 -address: 08 00 00 00 -command: 0f 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 00 00 00 00 -command: 01 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 00 00 00 00 -command: 01 00 00 00 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 634 2571 505 519 479 519 479 518 480 518 480 518 480 518 480 517 481 547 481 517 481 20040 590 2555 501 1007 999 997 510 548 480 486 512 486 512 486 542 485 513 516 482 116758 593 2552 504 1004 992 1004 514 514 514 483 515 513 485 483 545 482 516 482 516 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 525 1955 449 1999 476 4545 446 4544 478 2032 443 2006 469 2011 444 4577 445 4545 447 4574 448 2002 473 4547 444 34913 447 2032 443 2007 478 4542 449 4541 471 2039 446 2004 471 2008 447 4574 448 4543 448 4572 450 2030 445 4545 446 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 2445 582 1221 603 548 602 1191 609 572 602 1191 609 542 607 544 631 1172 603 568 606 545 605 566 608 543 26263 2414 611 1192 607 544 606 1197 602 569 606 1197 602 539 611 540 635 1168 606 565 610 541 608 563 587 564 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 3461 1802 439 452 444 1283 469 448 438 453 443 447 439 452 444 419 497 448 438 425 471 419 467 451 445 445 441 450 446 1281 471 420 466 425 471 446 440 424 472 445 461 456 440 451 445 445 441 450 446 1281 471 447 439 451 445 419 467 423 473 445 441 422 494 450 436 428 468 1259 493 425 471 1256 496 1259 472 1281 471 1285 467 423 473 417 469 1286 466 452 444 1283 469 1285 467 1261 491 1263 468 423 493 1260 471 74142 3578 1713 467 423 473 1281 471 420 466 452 444 419 467 424 472 418 488 429 467 451 445 445 441 450 446 444 442 449 437 1290 472 446 440 423 473 445 441 449 467 396 490 428 468 449 447 444 442 448 438 1289 473 445 441 450 446 444 442 449 437 426 470 421 495 422 464 426 470 1257 495 450 446 1254 488 1267 464 1290 472 1282 470 421 465 453 443 1284 468 450 446 1281 471 1283 469 1259 493 1261 470 448 468 1258 473 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 389 1737 280 796 253 744 295 754 274 775 274 776 273 1827 271 1828 270 805 254 1820 278 796 253 797 252 745 294 1806 302 773 245 48942 305 1821 277 798 251 746 303 747 271 778 271 1829 279 796 253 796 253 1821 277 798 251 1823 275 1824 274 1825 273 802 247 1827 271 42824 381 1745 272 804 245 752 297 753 275 773 276 774 275 1825 273 1826 272 803 246 1828 270 805 254 795 244 753 296 1804 294 781 247 48939 379 1746 271 804 245 779 270 753 275 774 275 1825 273 802 247 802 247 1827 271 804 245 1829 279 1820 278 1821 277 798 251 1823 275 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 562 1721 561 594 557 597 564 617 513 615 536 618 543 1715 566 1716 566 1692 559 594 567 588 563 618 543 611 540 615 536 618 543 1715 556 623 538 617 534 621 530 624 516 638 513 642 509 1722 560 620 541 1717 565 1692 559 1724 568 1715 556 1701 560 1723 559 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8436 4189 538 1563 566 1559 539 510 559 543 516 507 542 560 509 540 509 567 512 1586 512 1562 567 1559 539 536 533 1566 542 507 562 513 536 540 509 22102 647 1478 559 1568 540 508 541 535 534 515 534 568 511 538 511 539 540 1585 513 1560 559 1567 541 534 535 1564 534 515 534 568 511 538 511 22125 644 1482 565 1561 537 511 538 564 515 508 541 535 534 541 508 516 563 1588 510 1563 556 1570 538 510 559 1567 541 534 515 535 534 541 508 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 929 825 1711 934 797 930 791 909 822 932 789 938 793 934 797 930 791 1719 899 856 1711 907 824 90848 923 830 1706 912 819 908 823 931 790 910 822 933 788 912 819 935 796 1714 904 850 1707 939 792 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 448 2031 444 2005 480 4540 452 4539 473 2037 438 2011 474 2006 449 4571 451 4539 453 4568 444 2036 449 4541 451 34906 527 1953 451 1998 477 4543 449 4542 480 2030 445 2004 471 2009 446 4575 447 4543 449 4572 450 1999 476 4545 446 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 9021 4496 567 1664 567 1690 561 567 533 1698 563 1667 564 1693 568 1663 568 586 534 567 563 565 535 594 536 591 539 589 531 571 539 563 567 1690 541 560 560 594 536 592 538 564 536 1695 566 1664 567 1690 561 1670 561 1696 555 1675 566 562 558 596 514 562 568 559 561 594 536 565 535 593 537 591 539 1665 566 1692 559 1671 560 1697 564 1666 565 1666 565 1693 558 1672 569 23181 9013 4504 569 1689 542 1689 562 565 535 1697 564 1666 646 1610 560 1672 559 595 535 593 537 590 510 566 564 590 540 588 532 596 514 588 542 1689 542 560 560 594 536 592 538 590 510 1694 567 1664 567 1690 561 1669 562 1695 556 1675 566 561 559 596 514 588 542 585 535 593 537 591 509 593 537 591 539 1665 566 1692 559 1671 560 1697 564 1667 564 1667 564 1693 558 1672 569 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8041 3979 513 536 482 515 513 1559 509 515 514 1560 508 515 514 509 509 514 484 4000 533 1566 512 537 481 1566 512 537 481 1566 512 537 481 516 513 537 481 24150 8044 3977 505 518 510 539 479 1567 511 512 506 1568 510 539 479 543 485 538 480 3977 536 1564 514 534 484 1563 515 508 510 1563 515 508 510 540 489 534 484 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 383 2027 295 2112 271 2138 266 890 271 887 294 926 235 2114 300 887 274 915 236 2145 269 887 274 884 297 923 238 920 241 917 264 895 266 26573 384 2026 296 2111 273 2136 268 889 272 886 295 924 237 2113 301 886 275 914 237 2144 270 886 275 914 267 921 240 919 242 916 265 924 237 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 177 8474 175 5510 174 8476 173 8477 177 8504 171 5515 175 8476 178 8472 177 8473 176 5541 174 8476 173 45583 171 8481 178 5507 177 8473 176 8474 175 8506 173 5512 172 8478 176 8475 174 8476 178 5538 177 8474 175 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8044 3976 506 517 511 1563 505 517 511 538 480 517 511 538 460 563 455 568 460 3993 530 545 483 1564 514 1559 509 1564 514 509 509 540 488 535 483 513 505 24150 8043 3978 514 509 509 1564 514 509 509 540 478 519 509 540 458 565 464 559 459 3994 529 546 482 1565 513 1560 508 1565 513 510 508 541 487 536 482 541 477 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 558 2942 450 10021 482 2989 484 10015 447 3024 449 10021 472 3100 485 2986 477 2994 500 2999 475 2996 477 2994 479 2992 482 3018 476 2995 479 6464 484 36270 477 3023 450 10020 473 2999 485 10014 448 3022 452 10019 474 3098 478 2994 480 2991 503 2996 477 2994 480 2992 482 2990 484 3015 479 2992 481 6462 485 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 587 2407 476 1062 965 547 482 547 451 577 452 546 452 22108 590 2404 479 1060 967 1028 510 519 509 548 480 487 511 120791 645 2411 472 1066 961 1065 483 515 514 514 504 493 515 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 172 7439 171 7441 169 7443 177 7434 176 7462 178 4887 176 4916 177 4914 169 7469 171 4920 174 4918 175 55174 176 7436 174 7437 173 7439 171 7440 175 7463 172 4894 174 4917 171 4921 172 7465 175 4916 178 4914 169 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 589 2556 500 524 474 554 454 544 454 543 455 543 455 543 455 543 445 583 446 552 446 20046 584 2561 505 1033 485 514 963 518 480 1032 516 512 476 522 506 491 507 522 476 116758 586 2560 506 1033 484 513 964 548 450 1031 507 522 476 521 507 490 508 521 477 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 586 2407 476 1063 964 578 450 548 450 547 481 517 481 577 451 546 452 546 472 556 452 545 453 575 453 514 484 544 484 543 455 14954 510 2483 481 1058 480 548 959 552 446 1067 481 516 512 546 482 515 992 1003 535 493 515 543 486 513 475 522 506 552 446 111671 589 2405 478 1061 477 551 967 514 484 1059 479 549 479 548 480 517 990 1036 512 516 482 546 483 515 503 525 483 544 454 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8444 4180 537 1564 565 1560 538 1561 557 1568 540 1559 539 536 533 542 517 559 510 1563 535 1564 565 1561 537 512 567 1558 540 535 534 1566 542 1557 562 23122 564 1562 557 1569 539 1560 538 1587 542 1558 540 534 535 515 534 541 538 1587 511 1563 566 1560 538 536 533 1567 541 534 515 1585 533 1566 542 23166 561 1565 564 1561 537 1563 535 1590 539 1561 537 538 541 534 515 535 534 1564 534 1566 563 1563 535 540 539 1560 538 511 538 1588 541 1559 539 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 527 1923 481 1998 477 4543 449 4542 470 2040 445 2004 471 2009 446 4575 447 4543 449 4572 450 1999 476 2034 441 34899 524 1956 448 2001 474 4546 446 4545 477 2033 442 2007 478 2002 443 4578 444 4546 445 4575 447 2003 472 2037 448 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 533 1356 437 3474 427 3483 429 3455 436 1454 430 1459 405 28168 510 1379 434 3477 434 3476 425 3459 432 1457 427 1462 402 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 921 833 1714 932 789 938 793 934 797 903 818 909 822 905 816 938 793 1716 902 853 1714 905 816 90856 922 805 1742 931 790 937 794 933 788 939 792 935 796 930 791 937 794 1715 903 825 1742 904 817 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 179 7433 177 4915 178 7434 175 7436 174 7464 176 7435 175 4916 177 4915 173 4918 170 4922 171 4920 173 55174 175 7437 173 4919 174 7437 173 7439 171 7467 173 7438 172 4920 173 4919 174 4917 176 4915 178 4914 169 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 169 6731 176 6748 169 6730 177 6748 169 6755 172 4427 177 4447 178 6721 175 6749 178 4446 168 4456 169 54704 176 6723 174 6750 177 6723 173 6750 177 6747 170 4429 175 4449 176 6723 174 6751 176 4448 177 4447 178 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 3506 3494 876 830 840 2576 847 2568 844 862 819 2570 842 864 816 863 818 2570 842 836 844 2572 840 866 815 865 815 2573 839 867 813 866 814 2573 850 857 813 2575 847 2568 844 834 847 2569 843 835 845 2571 872 2571 842 32654 3512 3488 872 834 847 2570 842 2573 839 867 814 2574 849 858 822 857 813 2575 848 859 821 2566 846 832 848 860 821 2567 845 833 848 860 820 2568 844 834 847 2569 843 2572 840 838 843 2574 849 829 841 2575 868 2575 837 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 560 2939 453 10018 475 2996 477 10022 450 3021 452 10018 475 3097 478 6464 483 6460 477 6466 502 6469 479 2993 480 2990 484 36274 564 2936 456 10015 478 2993 481 10020 534 2936 456 10014 479 3093 482 6461 476 6466 482 6461 476 6495 473 2999 485 2986 477 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 10726 41047 10727 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 1617 4604 1559 1537 1560 1537 1560 4661 1533 33422 1613 4607 1566 1530 1556 1540 1536 4685 1539 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 174 4972 177 4910 173 4944 170 6988 174 6984 177 6951 175 6983 174 14269 177 4969 175 4912 176 4941 178 6979 172 6986 175 6953 178 6980 171 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 174 7067 176 10544 1055 719 1053 739 1054 738 953 822 1052 2566 169 3435 171 3431 175 3446 170 3432 174 3446 170 3432 174 3428 178 3442 174 3429 177 3425 171 39320 2323 4900 178 10543 1056 719 1053 739 1054 738 953 821 1053 2566 169 3435 171 3431 175 3445 171 3432 174 3446 170 3432 174 3428 178 3442 174 3428 178 3425 171 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 3506 3492 868 839 841 2575 848 858 822 2566 846 832 848 859 821 858 812 2576 847 860 820 2567 845 833 847 860 821 2568 844 862 818 2570 842 864 817 2571 841 2574 849 2567 845 861 820 2568 845 834 847 2570 873 2570 842 34395 3503 3496 874 833 847 2568 845 834 847 2570 842 864 816 835 846 862 819 2569 843 835 846 2571 841 865 816 864 816 2571 841 837 844 2573 850 857 813 2575 848 2567 845 2570 842 864 816 2572 840 838 842 2574 869 2574 838 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 170 8479 170 5516 178 8472 177 8474 175 8506 173 5513 171 8479 175 8476 178 8472 177 5540 175 8475 174 45584 177 8473 176 5509 175 8476 173 8477 176 8504 170 5516 178 8472 177 8474 175 8476 173 5543 172 8479 170 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 178 4969 170 6958 173 4944 175 6983 173 6956 174 6984 177 6980 171 16308 180 4966 173 6955 176 4941 172 6985 176 6982 169 6960 176 6982 174 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 585 2409 474 550 478 550 448 1033 994 1063 485 512 506 79916 585 2410 473 551 477 550 448 1034 993 1033 515 543 475 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 1192 1012 6649 26844 1192 1013 6648 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 3134 6105 6263 82963 3134 6105 6263 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 588 1511 567 1533 565 589 531 597 513 589 541 587 533 1565 533 569 541 1533 565 562 568 1531 537 1537 561 593 537 591 539 1507 561 1539 569 22152 586 1513 565 1535 563 590 540 562 538 564 566 588 542 1557 531 597 513 1534 564 590 540 1533 535 1539 559 568 562 592 538 1509 569 1531 567 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 923 831 1715 903 818 936 795 932 789 938 793 934 797 1713 1740 932 789 911 821 934 798 930 791 90853 928 799 1737 935 796 931 790 937 795 932 789 938 793 1717 1736 936 796 932 789 911 820 934 798 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 689 1461 566 1534 564 589 531 597 513 1534 564 564 566 1533 535 620 510 1537 561 592 538 1535 543 1531 567 587 533 595 535 1511 567 1533 565 22161 588 1512 566 1534 564 564 556 598 512 1535 563 565 565 1534 534 594 536 1538 560 593 537 1536 542 1532 566 587 543 585 535 1511 567 1534 564 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 588 2558 498 526 482 515 483 546 452 545 453 545 453 545 453 545 453 544 474 554 444 20047 583 2562 504 519 479 519 479 1003 515 483 1024 548 450 1001 995 1001 506 116771 593 2552 504 520 478 551 447 1004 513 514 993 549 449 1002 994 1002 516 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 587 2559 507 517 481 517 481 547 451 516 482 546 452 546 452 546 452 545 473 525 483 20038 592 2553 503 521 477 552 446 521 477 1004 514 515 992 1034 483 514 484 513 485 116769 593 2552 504 520 478 550 448 520 478 1003 515 514 993 1032 486 513 475 522 476 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4558 4576 558 596 565 97881 4554 4579 565 589 562 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 1039 7202 958 4713 981 4713 961 7255 956 4739 955 16077 1038 7204 956 4714 980 4715 959 7256 954 4741 954 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 506 2638 510 516 482 516 482 546 452 546 452 545 453 545 453 545 453 575 443 554 444 20048 592 2554 502 1036 481 517 481 517 511 516 482 516 961 1035 513 515 483 514 484 116757 594 2552 504 1034 484 514 484 514 504 524 484 513 964 1032 506 522 476 492 506 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 170 7441 169 4924 174 7437 193 7444 171 7441 174 4918 170 7441 174 4917 171 7440 195 7443 172 7439 171 55187 174 7437 173 4919 175 7437 173 7438 172 7466 175 4891 172 7439 171 4921 172 7439 171 7441 174 7464 171 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 179 4967 172 4915 178 6980 171 4945 169 4948 176 4912 176 4941 178 16307 176 4969 175 4912 176 6982 174 4942 171 4945 169 4919 174 4943 176 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 1042 793 898 883 869 858 924 884 878 877 875 879 873 855 928 1717 901 854 1794 878 894 887 875 1716 902 89114 985 798 903 878 874 880 902 852 900 855 897 857 905 876 896 1722 906 849 1789 883 899 855 897 1721 897 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 170 8480 169 8481 178 8472 177 8474 175 8476 173 5513 176 8474 175 8476 173 8507 178 5509 175 8505 174 45558 175 8476 173 8476 173 8477 172 8478 171 8480 169 5517 177 8473 176 8474 175 8506 174 5512 172 8509 171 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 628 2577 499 528 480 545 453 544 454 544 454 544 454 544 454 543 455 543 475 553 445 20047 593 2553 503 521 477 551 447 1004 514 515 992 519 479 1003 515 513 485 543 455 116768 585 2561 505 519 479 519 479 1002 516 513 994 548 450 1001 506 522 476 521 477 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8987 4505 568 586 534 594 536 591 509 567 563 591 539 563 567 587 513 1692 559 1672 559 1698 563 590 510 592 538 590 540 1664 567 1690 561 593 507 1699 562 1668 563 1694 567 1663 568 586 534 568 562 592 508 595 535 1695 536 592 538 1693 558 595 515 1690 561 593 517 585 535 567 563 39551 8994 4497 566 589 541 560 560 594 516 586 534 594 536 592 538 590 510 1695 566 1664 567 1690 561 593 507 595 535 566 564 1667 564 1693 558 596 534 1670 561 1670 561 1696 565 1666 565 589 541 587 533 595 515 587 533 1697 534 568 562 1695 556 572 538 1693 568 559 541 588 542 585 535 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8987 4504 569 559 561 593 537 565 535 567 563 591 539 588 532 597 513 1691 560 568 562 592 508 1697 564 589 511 565 565 1692 559 1672 559 569 561 1669 562 566 564 564 566 1665 566 588 542 586 534 1670 561 567 563 565 535 593 537 591 539 1692 539 589 541 586 534 594 536 592 508 40679 8987 4504 569 585 535 593 537 591 509 566 564 591 539 588 532 596 514 1691 560 594 536 592 508 1697 564 589 511 591 539 1692 559 1671 560 594 536 1669 562 592 538 590 540 1664 567 587 543 585 535 1670 561 593 537 565 535 593 537 591 539 1691 540 588 542 586 534 594 536 592 508 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 298 1828 270 804 245 1829 279 1820 278 797 252 771 278 1822 276 1824 274 800 249 1825 273 802 247 776 252 771 278 1822 276 799 250 48931 388 1737 280 796 253 1821 277 1822 276 798 251 1823 275 800 249 774 275 1825 273 776 273 1801 297 1828 270 1829 279 796 253 1821 277 42813 301 1825 273 801 248 1826 272 1827 271 804 245 805 244 1830 278 1821 277 798 251 1823 275 799 250 774 244 779 280 1820 278 796 253 48926 382 1744 354 696 271 1828 270 1829 279 796 253 1821 277 797 252 746 303 1823 275 774 275 1799 299 1826 272 1827 271 804 245 1829 279 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 177 5508 176 5539 176 8475 174 5542 173 8478 171 5545 170 8481 168 8482 177 8473 176 5541 174 8476 173 45573 169 5517 177 5538 177 8473 176 5541 174 8476 173 5544 171 8479 170 8481 178 8472 177 5540 175 8475 174 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 175 8474 175 8475 174 8477 172 8478 171 8480 169 8481 178 5538 177 5510 174 5542 173 5543 172 5544 171 45575 177 8472 177 8474 175 8476 173 8477 172 8478 171 8481 178 5507 177 5539 176 5540 175 5542 173 5543 172 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8050 3971 511 1562 516 1558 510 539 490 1557 511 1563 515 533 485 538 490 533 485 3983 530 1569 509 1564 514 1559 509 1565 513 1560 508 515 513 536 482 541 488 24152 8042 3979 514 1560 508 1565 513 510 508 1565 513 1560 508 515 513 536 482 541 488 3980 533 1567 511 1562 516 1557 511 1563 515 1558 510 539 489 534 484 539 490 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 175 8475 174 5512 177 8473 176 8475 174 8506 179 5508 176 8474 175 8475 174 8477 172 5544 171 8480 169 45587 176 8475 174 5511 173 8477 177 8473 176 8504 170 5516 178 8472 177 8473 176 8475 174 5542 173 8478 171 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 176 7436 174 7438 172 7439 171 7441 174 7463 172 7440 170 4921 172 4919 174 4917 176 4916 177 4914 174 55176 175 7437 173 7439 191 7446 174 7438 177 7434 176 7435 175 4917 176 4915 179 4914 174 4917 171 4946 178 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 176 7435 175 4917 177 7435 175 7436 189 7449 176 7435 175 7437 173 4918 175 7436 174 4918 175 4916 178 55171 175 7436 174 4918 170 7441 174 7438 177 7460 170 7441 174 7438 177 4914 174 7437 178 4914 169 4922 171 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8049 3973 509 1564 514 509 509 1564 514 535 483 1564 514 535 483 540 489 535 483 3980 533 1566 512 537 481 1566 512 511 507 1566 512 537 481 542 486 511 507 24149 8045 3976 516 1558 510 512 516 1557 511 512 516 1558 510 512 516 507 511 512 506 3984 539 1560 508 515 513 1560 508 541 488 1560 508 541 488 536 482 514 504 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 366 202 867 527 170 130516 343 227 863 529 168 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 176 5992 176 1372 176 1320 177 1345 172 1349 179 1370 178 1317 175 4444 176 1346 171 1351 177 1345 172 1349 179 1344 174 5963 175 65574 172 5995 178 1344 174 1348 175 1347 175 1347 170 1378 170 1325 172 4447 178 1344 173 1349 169 1353 175 1347 170 1351 177 5961 177 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 497 1478 488 511 467 1482 494 531 447 1477 499 501 466 533 445 530 468 507 471 529 438 12857 488 1485 491 509 469 1481 495 529 448 1476 490 510 468 532 445 529 469 480 498 502 465 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 982 6313 961 2662 903 2718 929 2719 907 6363 900 2722 904 6365 909 6361 903 6368 906 2742 905 46364 989 6307 906 2716 911 2712 925 2723 903 6367 907 2715 901 6369 905 6365 909 6361 903 2745 902 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 599 257 975 317 177 130649 308 228 974 319 175 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 176 4969 170 6958 173 6985 177 6981 171 6958 178 6980 177 6981 170 16308 180 4966 173 6955 176 6982 169 6988 174 6956 175 6983 173 6984 172 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 178 5990 173 1349 174 1348 175 1348 174 4444 176 1346 171 1351 177 4416 178 1344 173 1348 169 1353 175 1347 170 1351 177 9048 177 65573 173 5995 173 1348 175 1348 174 1347 176 4444 171 1351 177 1345 173 4421 173 1348 169 1352 176 1347 170 1351 177 1345 172 9053 177 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 174 4972 177 4910 173 6985 177 4940 174 6984 178 6951 175 6983 174 14269 177 4968 176 4912 176 6981 175 4941 173 6985 177 6953 178 6979 172 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 1318 225 177 130305 1609 231 171 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 585 2410 473 1065 962 581 447 519 479 580 448 549 449 579 449 518 480 548 480 517 481 517 481 577 451 516 482 576 452 545 453 14955 510 2483 481 1058 480 549 969 543 445 1037 511 547 481 546 482 516 482 545 484 545 483 514 484 544 963 1063 485 543 445 111612 626 2427 557 954 513 512 995 547 451 1031 507 521 508 551 477 520 478 550 478 549 479 488 510 548 969 1057 481 517 481 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 203 272 1215 302 171 130229 1423 275 178 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 176 7436 174 7438 192 7446 174 7437 178 7434 176 7435 175 4917 176 4915 178 4913 175 4917 197 7440 175 55130 286 7377 177 7435 175 7437 173 7438 172 7466 174 7411 178 4913 170 4921 172 4919 174 4918 175 7436 174 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 175 7437 173 4918 170 7441 174 7437 178 7460 170 7441 179 7433 177 4915 178 4913 170 4922 171 4920 173 55124 291 7372 172 4921 172 7439 171 7441 174 7463 172 7440 170 7442 178 4913 170 4922 171 4920 173 4918 175 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 172 8477 172 8478 171 8480 169 5516 179 8502 178 5509 175 8475 174 8476 173 8479 170 5545 170 8480 169 45588 176 8473 176 8474 175 8476 173 5513 177 8504 171 5515 174 8476 178 8472 177 8473 176 5541 174 8476 173 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 177 7436 174 4918 175 7436 174 4918 175 7462 178 4887 176 4916 177 4914 174 4917 171 4921 172 4919 175 55184 175 7435 175 4918 175 7436 174 4918 175 7462 178 4914 174 4917 171 4920 173 4919 174 4917 176 4915 178 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 508 2484 480 1060 967 544 485 544 454 574 454 543 455 573 445 553 445 522 506 552 446 552 446 551 477 551 447 581 447 520 478 550 478 15908 626 2427 476 1062 476 522 995 547 451 1061 477 520 508 550 478 519 479 519 999 1058 959 1037 990 582 446 1035 483 111666 590 2404 479 1059 479 549 968 543 455 1027 511 548 480 517 511 486 512 546 961 1065 962 1034 993 579 449 1032 486 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 175 8475 174 8475 174 8477 172 8478 171 8480 169 5517 178 8473 175 8475 179 8501 173 5513 176 8505 169 45562 179 8472 177 8473 176 8474 175 8476 173 8478 171 5515 174 8476 178 8473 176 8505 174 5512 172 8508 171 120769 178 93377 175 7437 173 4919 174 7437 173 7439 171 7467 173 7439 171 7441 174 4918 170 4921 172 7439 171 7467 173 55167 171 7440 170 4922 171 7440 195 7443 172 7439 171 7441 174 7438 177 4915 173 4918 195 7442 173 7439 170 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 295 1805 273 776 242 1808 270 754 244 1806 272 777 241 758 270 754 264 760 268 756 272 14149 297 1802 266 784 244 1805 263 762 246 1803 265 785 244 780 248 751 267 758 271 753 265 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 535 1723 569 585 566 615 536 619 542 586 565 616 535 1722 539 615 536 1721 561 620 541 587 564 617 534 621 540 588 563 618 543 1714 537 617 534 621 540 615 536 618 543 612 539 615 536 1722 560 594 567 1717 534 1723 569 1714 557 1700 643 1639 643 1641 559 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 502 2521 504 521 997 545 453 575 453 545 453 575 454 22097 591 2433 511 513 994 1062 476 552 476 522 476 552 476 120839 628 2455 509 515 992 1064 484 513 505 493 515 543 475 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 591 2404 479 1060 967 1029 509 519 509 549 479 518 480 79926 585 2408 556 956 989 1033 515 544 484 543 475 522 476 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 586 2560 506 518 480 548 450 548 450 517 481 517 481 517 481 547 451 546 482 516 482 20040 590 2556 500 1038 480 518 969 543 455 543 475 1006 511 517 481 517 511 486 481 116778 584 2561 505 1033 485 513 964 548 450 548 480 1001 506 522 476 522 506 521 446 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 917 206 175 186 170 21561 170 2280 175 2274 502 1929 174 2276 169 5178 170 2261 173 3724 498 1932 171 2279 176 2273 172 3709 172 2277 178 3720 171 17223 177 7619 174 2275 170 2279 176 2256 168 2280 175 5172 176 2256 168 3729 173 2276 179 2253 171 2278 177 3703 178 2271 174 3724 177 17251 170 7627 177 2272 173 2276 169 2263 171 2277 178 5169 169 2262 172 3726 175 2256 168 2280 175 2274 171 3710 171 2278 177 3720 171 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 565 233 653 313 170 130328 752 235 233 107 229 398 177 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 586 2439 505 549 968 1027 511 517 511 517 481 547 481 21583 584 2439 505 520 997 1059 479 549 479 518 480 548 480 120894 593 2432 501 522 995 1061 477 521 507 520 478 550 478 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 558 8032 474 8115 503 8116 482 8108 480 8141 477 5239 476 8114 504 8115 483 8107 481 5236 509 8111 477 45290 554 8036 481 8108 510 8110 478 8112 476 8145 473 5243 482 8107 511 8109 479 8111 477 5240 505 8115 473 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 173 7438 172 4920 173 7438 172 4920 173 7465 175 4890 173 7439 171 4920 173 4919 174 4917 176 4915 178 55179 170 7441 169 4924 174 7437 178 4913 175 7463 172 4893 170 7441 174 4918 170 4922 171 4920 173 4918 175 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 225 745 222 774 193 778 200 797 175 769 193 777 201 771 196 774 224 747 220 776 202 769 198 248 220 252 196 250 218 253 225 746 221 250 198 248 220 252 226 246 222 223 225 248 220 252 216 229 219 253 225 247 221 277 176 243 220 279 189 230 228 244 224 248 220 252 196 250 218 253 215 257 201 770 197 799 189 257 201 271 197 37716 222 749 218 778 200 771 196 801 172 772 200 771 196 774 193 777 221 750 217 780 197 773 194 251 217 255 193 279 199 273 195 749 218 254 194 252 226 245 223 275 178 242 221 277 201 245 193 253 225 247 221 250 218 254 194 252 226 272 196 223 225 248 220 251 217 255 193 253 225 247 221 251 197 773 194 803 174 297 176 244 219 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 3503 2655 197 642 876 2568 844 834 846 833 848 860 821 831 839 2576 847 2569 843 2572 841 2574 849 858 823 857 813 866 815 865 815 2572 841 2575 848 2568 844 2570 842 836 844 863 818 834 847 861 820 2568 845 2571 872 2571 842 32651 3505 3495 875 2567 845 861 820 832 849 859 811 840 840 2576 847 2568 844 2571 842 2574 849 830 840 867 814 838 842 865 815 2572 840 2575 848 2568 845 2571 841 865 815 864 817 834 846 861 819 2569 843 2572 871 2572 840 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 347 677 219 252 196 276 202 742 225 798 174 770 192 778 200 771 196 775 223 748 219 777 200 770 197 249 219 253 195 251 227 271 197 747 220 252 216 229 219 253 225 273 195 277 176 244 219 253 215 230 228 244 224 248 220 252 196 250 218 280 198 247 201 245 223 249 219 253 195 251 227 245 223 248 200 797 175 795 198 248 200 246 222 37666 344 678 228 245 223 222 226 771 196 800 198 747 220 750 217 753 224 773 194 776 202 769 198 799 173 246 227 245 223 249 199 247 221 749 218 280 198 247 201 245 223 249 219 253 195 251 227 244 224 248 200 272 196 250 218 254 194 252 226 245 223 249 199 274 194 251 227 245 193 253 225 273 195 251 217 753 225 746 221 251 197 275 193 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 174 7437 173 7440 174 7437 178 7433 177 7461 169 7442 178 4914 174 4917 171 4921 172 4919 174 7463 177 55163 177 7435 174 7437 173 7438 172 7440 175 7463 172 7413 176 4915 178 4913 175 4917 171 4920 174 7438 172 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8042 3979 513 510 508 541 487 1559 509 515 513 1560 508 515 513 510 508 541 477 3981 532 1568 510 1563 515 1558 510 1564 514 1559 509 514 514 535 483 540 488 24151 8042 3979 513 536 482 541 487 1560 508 541 488 1560 508 541 487 536 482 541 477 3980 533 1566 512 1561 507 1566 512 1562 516 1557 511 538 491 533 485 538 490 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8989 4501 562 1696 565 1665 566 562 568 586 514 588 542 586 534 594 536 1667 564 591 539 1692 539 563 567 1690 561 1669 562 1669 562 1696 565 562 538 564 566 588 542 586 534 1670 561 568 562 592 538 590 510 592 538 590 540 561 539 563 567 561 569 585 535 593 507 595 535 593 537 39547 8987 4504 569 1689 562 1668 563 591 539 589 511 591 539 589 541 561 559 1671 560 568 562 1695 536 592 538 1693 558 1673 568 1663 568 1689 562 592 508 594 536 592 538 590 540 1664 567 561 569 559 561 567 543 585 535 593 537 591 509 593 537 591 539 589 541 587 513 589 541 587 533 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8988 4504 640 487 562 592 538 590 510 592 538 590 540 588 532 596 514 614 516 1689 562 1668 563 1695 536 1695 566 1664 567 1690 612 1618 643 1430 801 1613 648 481 558 570 540 589 541 587 533 595 535 592 508 594 536 592 538 1667 564 1693 558 1672 569 1688 563 1668 644 1586 563 1694 567 40630 8994 2265 557 96833 8987 2273 538 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 173 7439 171 4922 172 7439 171 4921 172 7466 174 4917 176 4915 173 4918 170 7441 174 7438 192 7445 175 55181 174 7437 173 4918 175 7437 173 4919 175 7463 177 4888 175 4917 176 4915 178 7460 170 7440 175 7437 178 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 1636 4610 1563 1533 1584 7760 1561 28769 1641 4606 1557 1539 1588 7757 1564 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 590 2404 479 1059 968 575 454 544 454 574 454 543 455 543 475 522 476 552 476 552 446 551 447 581 448 520 478 581 447 519 479 549 479 15967 587 2407 476 1062 476 522 995 547 451 1030 508 520 509 550 478 519 479 519 998 1028 999 1057 481 547 960 1066 482 111641 587 2407 476 1063 485 513 994 547 451 1031 507 551 477 551 477 520 478 550 968 1059 968 1027 511 548 959 1036 512 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 588 2406 477 1061 966 577 451 516 482 545 483 545 453 575 443 524 484 514 504 554 454 543 455 543 475 553 445 583 445 521 477 582 446 15969 585 2409 474 1065 483 514 993 549 449 1033 515 543 475 553 475 522 476 552 965 1030 997 576 452 1029 998 1028 479 111668 587 2407 475 1063 485 543 964 517 481 1031 507 552 476 551 477 520 478 550 968 1028 999 574 454 1027 990 1036 481 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 174 7439 196 7441 174 7437 173 7439 171 7441 174 7438 177 7460 170 7442 178 7433 177 4915 173 4918 170 55186 174 7438 172 7440 170 7441 174 7438 177 7461 169 7416 173 7464 176 7435 175 7437 173 4918 175 4917 176 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 173 7438 172 7441 174 7438 177 7434 176 7462 168 7443 177 7435 175 4917 176 4915 173 7438 177 4941 173 55166 174 7438 197 7441 174 7437 173 7439 171 7441 174 7438 177 7460 170 4922 171 4893 195 7443 172 4920 173 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 175 7436 179 4913 175 4917 171 4920 173 4945 169 4896 177 7435 175 7436 174 7464 176 4916 177 4914 169 55180 170 7441 169 4924 169 4922 171 4920 173 4919 174 4917 176 7435 175 7463 177 7434 176 4916 177 4914 174 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 927 827 1709 936 796 932 789 938 793 1717 1736 936 795 932 789 1721 927 827 1709 909 822 90851 898 829 1738 934 798 930 791 936 796 1715 1738 934 797 930 791 1719 899 828 1739 934 797 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 176 5991 177 1372 176 1320 177 1344 173 1349 174 1374 169 1327 170 4449 176 1346 171 1351 177 1345 172 1349 168 1353 175 5963 175 65573 173 5995 178 1344 174 1348 175 1348 174 1347 170 1378 170 1325 172 4447 178 1344 174 1349 169 1353 175 1347 170 1352 176 5961 177 -# -name: POWER -type: parsed -protocol: NEC -address: 71 00 00 00 -command: 4a 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 60 00 00 00 -command: 03 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 60 00 00 00 -command: 00 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 42 00 00 00 -command: 01 00 00 00 -# -name: POWER -type: parsed -protocol: NECext -address: 50 ad 00 00 -command: 00 00 00 00 -# -name: POWER -type: parsed -protocol: NECext -address: 50 ad 00 00 -command: 02 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 50 00 00 00 -command: 3f 00 00 00 -# -name: POWER -type: parsed -protocol: Samsung32 -address: 06 00 00 00 -command: 0f 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 08 00 00 00 -command: 12 00 00 00 -# -name: POWER -type: parsed -protocol: Samsung32 -address: 08 00 00 00 -command: 0b 00 00 00 -# -name: POWER -type: parsed -protocol: NECext -address: 83 55 00 00 -command: c2 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 00 00 00 00 -command: 51 00 00 00 -# -name: POWER -type: parsed -protocol: NECext -address: 00 bd 00 00 -command: 01 00 00 00 -# -name: POWER -type: parsed -protocol: Samsung32 -address: 00 00 00 00 -command: 0f 00 00 00 -# -name: POWER -type: parsed -protocol: Samsung32 -address: 16 00 00 00 -command: 0f 00 00 00 -# -name: POWER -type: parsed -protocol: NEC -address: 01 00 00 00 -command: 01 00 00 00 -# -name: POWER -type: parsed -protocol: NECext -address: 80 68 00 00 -command: 49 00 00 00 -# -name: POWER -type: parsed -protocol: NECext -address: 86 02 00 00 -command: 49 00 00 00 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 2383 609 1214 600 612 580 1213 608 614 583 1210 605 607 586 616 611 1192 599 613 608 614 579 613 615 607 26670 2387 601 1212 600 612 589 1214 603 609 586 1217 595 607 594 618 606 1187 602 610 609 613 588 614 610 612 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 178 7761 176 11308 546 957 540 1958 538 970 537 1955 541 962 545 1953 543 964 543 962 545 957 540 970 548 960 547 1945 541 1950 546 965 542 1953 543 962 545 1945 540 970 537 1958 538 7881 172 7744 172 11318 536 971 536 1956 540 963 534 1964 542 966 541 1951 535 968 539 971 536 971 536 969 538 964 533 1965 541 1954 542 963 534 1956 540 971 536 1959 537 968 539 1951 535 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 3444 1767 413 486 420 1343 419 478 418 485 411 490 416 479 417 489 417 482 414 485 421 482 414 483 413 490 416 485 411 1343 419 487 419 480 416 483 413 490 416 482 414 488 418 483 413 483 413 492 414 1345 417 482 414 489 417 480 416 487 419 482 414 481 415 491 415 484 412 1347 415 488 418 1338 414 1348 414 1347 415 1340 412 494 412 487 419 1340 412 491 415 1341 421 1341 421 1340 412 1343 419 487 419 1339 413 74311 3445 1753 437 461 445 1316 446 456 440 455 441 465 441 458 438 461 445 458 438 460 436 466 440 461 445 451 445 460 446 1313 439 460 446 457 439 459 437 465 441 460 446 450 436 469 437 462 444 456 440 1322 440 457 439 464 442 459 437 459 437 468 438 461 445 455 441 462 444 1312 440 463 443 1317 445 1310 442 1323 439 1320 442 457 439 464 442 1315 437 466 440 1320 442 1313 439 1326 446 1313 439 460 446 1317 445 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 278 1845 274 808 271 806 273 812 278 805 275 805 274 1840 279 1844 275 809 281 1836 272 806 274 812 278 805 274 1842 277 802 277 44956 279 1842 277 804 275 802 277 808 271 811 279 1838 281 798 271 814 276 1844 275 806 273 1841 278 1845 274 1846 273 808 271 1843 276 44959 275 1845 274 807 272 805 275 811 279 804 275 805 274 1839 280 1844 275 808 271 1845 274 805 274 811 279 804 275 1841 278 801 278 44955 280 1841 278 802 277 801 278 807 272 810 280 1837 271 807 272 813 277 1843 276 805 274 1839 280 1843 276 1845 274 807 272 1842 277 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 881 909 1750 928 875 927 876 921 872 929 874 927 876 918 875 930 873 1815 874 924 1745 937 876 88694 880 922 1747 933 880 914 879 926 877 922 871 927 876 927 876 920 873 1818 871 929 1740 934 879 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 509 1717 504 629 512 631 510 627 504 633 508 633 508 1713 508 1719 512 1713 508 625 505 637 504 633 508 629 512 629 512 623 508 1719 512 626 505 628 513 631 510 627 514 623 507 632 509 1713 508 632 509 1716 505 1715 506 1724 507 1716 505 1719 512 1715 506 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 506 493 505 4059 505 5051 501 506 502 4065 499 511 497 4063 501 5058 504 504 504 4060 504 5052 500 507 501 4066 498 5056 506 5042 500 515 503 119614 504 505 503 4065 499 5051 501 511 497 4069 505 499 499 4072 502 5050 502 506 502 4066 498 5053 499 512 506 4060 504 5044 498 5061 501 508 500 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8887 4470 532 1738 513 1708 533 1708 533 577 533 576 534 569 531 582 538 1705 536 571 539 1707 534 571 539 571 539 570 540 1699 532 581 539 568 532 575 535 576 534 571 539 571 539 569 541 1698 533 1717 534 1708 533 1710 531 1716 535 1706 535 1711 540 1705 536 567 533 580 540 567 533 39042 8915 2231 530 94849 8917 2256 535 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8313 4161 515 1574 514 1571 507 569 510 562 507 563 506 562 507 568 511 562 507 1580 508 1577 511 1582 506 567 513 1575 513 554 505 571 509 564 505 22604 513 1573 505 1589 509 563 506 564 505 562 507 569 511 562 507 563 506 1579 509 1584 514 1576 512 558 511 1574 514 562 507 565 515 556 513 22593 514 1581 507 1583 505 564 505 563 506 570 510 563 506 564 505 562 507 1586 512 1578 510 1577 511 557 512 1581 507 566 514 556 513 555 514 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8735 4383 558 573 557 550 560 568 562 544 566 1722 560 540 560 1732 560 544 566 1719 563 1701 560 1723 559 1704 557 574 556 1699 562 574 556 1703 559 1727 565 1698 563 1721 561 546 564 1723 559 541 559 577 564 541 559 571 560 548 562 565 566 1697 565 567 564 1693 558 1733 559 1701 560 39926 8754 2247 565 92341 8758 2243 589 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 3298 3336 821 2506 825 881 820 2505 826 2529 823 856 825 2524 817 866 825 2528 813 2513 818 888 813 862 819 887 814 865 826 2522 820 864 827 2526 815 861 820 887 814 2510 821 885 816 863 818 2531 821 2512 819 2559 793 32401 3298 3349 818 2507 824 882 819 2509 822 2527 814 868 823 2530 821 855 826 2531 821 2504 827 879 822 857 824 875 816 867 824 2529 823 854 827 2530 821 853 817 889 822 2506 825 873 818 866 825 2527 814 2513 818 2564 788 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8840 4439 532 566 534 566 534 1668 532 1674 536 1669 531 562 538 566 534 563 537 1667 533 567 533 563 537 563 537 562 538 1662 538 1671 529 568 532 565 535 566 534 1668 532 1674 536 1669 531 562 538 1672 528 1675 536 1668 532 1675 535 559 531 1676 534 565 535 558 532 1678 532 565 535 562 538 563 537 1665 535 565 535 1670 530 1669 531 572 538 1666 534 1669 531 1676 534 22777 8858 4447 535 92577 8858 4413 538 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 289 2112 261 2109 295 2101 262 918 294 912 259 915 297 2098 265 916 296 909 262 2107 297 905 266 913 289 918 263 910 292 909 262 918 294 24789 263 2106 298 2098 265 2110 294 913 258 916 296 905 266 2108 296 911 260 914 288 2107 266 914 298 908 263 911 291 910 261 919 293 913 258 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8898 4453 569 578 573 577 574 571 570 1745 567 1747 565 1743 569 583 568 579 572 1740 572 1744 568 1742 570 579 572 576 575 568 573 1746 566 1746 566 580 571 1745 567 577 574 576 575 1739 573 569 572 581 570 577 574 1738 574 576 575 1735 567 1748 574 574 567 1742 570 1748 574 1737 575 41005 8876 2261 571 93850 8898 2264 568 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 3215 1637 410 430 405 439 406 1242 408 435 410 1242 408 428 407 440 405 435 410 1240 410 1243 407 431 404 440 405 437 408 1238 402 1254 406 434 401 439 406 438 407 431 404 440 405 437 408 428 407 439 406 434 401 439 406 438 407 1241 409 434 401 441 404 432 403 444 401 1249 401 439 406 438 407 1241 409 434 401 441 404 433 402 444 401 1249 401 439 406 1248 402 436 409 434 401 441 404 433 402 444 401 439 406 45471 3239 1614 403 435 400 444 401 1250 400 437 408 1248 402 438 407 433 402 442 403 1245 405 1248 423 420 405 431 404 443 402 1248 402 1248 422 421 404 435 400 443 402 440 405 431 404 443 402 438 407 433 402 442 403 435 400 443 402 1250 400 436 399 447 408 432 403 438 407 1246 404 434 401 443 402 1250 400 436 399 447 408 432 403 437 408 1246 404 434 401 1253 407 434 401 436 399 447 408 432 403 437 408 436 399 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 7928 3948 504 515 503 518 500 1583 505 516 502 1584 504 510 508 516 502 516 502 3952 510 1579 509 507 501 1587 501 519 510 1571 507 518 500 517 501 517 501 23073 7931 3943 509 514 504 515 503 1578 500 524 505 1580 508 510 508 514 504 511 507 3951 511 1576 502 513 505 1586 502 515 503 1582 506 515 503 513 505 516 502 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8837 4464 538 610 531 619 532 613 538 1748 534 1750 532 611 540 613 538 609 532 615 536 614 537 608 533 1753 539 1745 537 606 535 618 533 614 537 609 532 619 532 613 538 611 540 609 532 611 540 1748 534 1749 533 1750 532 1754 538 1743 539 1747 535 1750 532 1747 535 618 533 613 538 44105 8864 2224 537 93399 8912 2202 538 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 492 4975 496 4993 498 498 500 4056 498 504 494 4055 499 4963 497 532 496 4031 492 4996 495 502 496 4060 494 4972 499 4990 491 506 492 4063 491 511 497 4052 492 4970 490 539 500 4027 496 103358 500 4961 500 4995 496 505 493 4056 498 499 499 4057 497 4969 491 533 496 4026 497 4997 494 508 490 4059 495 4967 494 5001 490 511 497 4053 491 505 493 4063 491 4975 496 528 490 4032 491 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 2357 610 1183 592 1191 614 1179 595 1188 618 584 613 1180 593 588 613 1190 612 590 605 587 613 589 605 587 25398 2355 623 1180 591 1181 627 1186 589 1183 618 584 616 1187 587 584 614 1189 615 587 605 587 615 587 609 593 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 3425 3482 848 2607 846 2639 845 2608 846 2639 845 2612 852 2624 850 2613 851 911 851 885 847 919 843 891 851 915 847 890 852 907 845 897 845 917 845 891 851 915 847 887 845 2639 845 2612 852 2625 849 2613 851 2630 844 34282 3455 3478 852 2601 852 2633 851 2605 848 2629 845 2617 847 2633 851 2604 849 917 845 890 852 913 849 889 843 915 847 896 846 916 846 890 852 914 848 885 847 919 843 895 847 2630 844 2617 847 2634 850 2605 848 2636 848 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 500 500 498 4066 498 5058 504 502 506 4061 503 508 500 4060 504 5055 497 5055 497 511 497 4071 503 5047 505 507 501 4065 499 5049 503 512 496 124314 501 508 500 4067 507 5043 499 513 505 4060 504 501 497 4073 501 5052 500 5052 500 512 496 4066 498 5058 504 506 502 4058 506 5053 499 509 499 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 176 7763 174 8817 169 10842 544 1955 541 967 540 1952 544 959 538 972 546 962 545 1947 538 1952 544 967 540 1955 541 964 543 1947 539 972 546 1949 547 7873 170 7746 170 10350 175 11811 543 960 537 1961 535 972 535 970 537 965 542 1956 540 1956 540 965 542 1947 539 973 534 1960 536 969 538 1952 534 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 2570 2682 1189 1208 1186 2665 1186 1217 1187 2668 1183 1215 1179 2671 1190 2695 1186 1187 1187 2692 1179 2671 1190 1213 1181 1193 1191 1207 1187 2663 1188 1215 1179 2676 1185 46941 2563 2685 1186 1191 1183 2698 1184 1188 1186 2691 1180 1197 1187 2694 1187 2666 1185 1210 1184 2674 1187 2695 1186 1185 1189 1206 1188 1189 1185 2696 1186 1186 1187 2689 1182 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 873 916 1773 906 877 925 878 919 874 928 875 925 878 1806 1770 914 879 1809 870 928 1772 88684 870 926 1774 908 875 926 877 917 876 929 874 925 878 1809 1777 905 877 1808 871 930 1770 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 874 906 877 912 1767 904 879 908 874 918 875 915 878 907 875 920 873 1795 874 914 1775 897 875 88704 879 914 868 922 1767 897 875 919 874 915 878 911 872 920 873 914 879 1792 877 914 1775 889 873 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 3325 1560 406 444 401 453 402 1226 404 449 406 1226 404 443 402 454 401 449 406 1224 406 1228 402 446 409 444 401 451 404 1223 407 1230 410 440 405 445 410 443 402 446 409 444 401 451 404 442 403 453 402 448 407 443 402 451 404 1224 406 448 407 444 401 445 410 446 409 1221 409 442 403 1231 409 439 406 1227 403 450 405 441 404 452 403 1227 403 448 407 446 409 439 406 447 408 444 401 445 400 456 410 440 405 52348 3320 1553 403 445 400 454 401 1230 400 447 408 1228 402 449 406 444 401 452 403 1225 405 1229 431 421 404 442 403 454 401 1229 401 1229 431 423 402 446 399 455 400 451 404 442 403 453 402 448 407 443 402 451 404 444 401 452 403 1229 401 445 400 457 398 451 404 446 399 1235 405 443 402 1232 408 444 401 1225 405 452 403 447 398 452 403 1231 399 449 406 447 408 443 402 444 401 456 399 450 405 445 400 453 402 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 990 915 980 909 986 924 981 2829 981 934 981 2823 987 923 982 2828 982 933 982 906 989 33895 989 907 988 927 988 900 985 2841 979 915 980 2851 980 909 986 2840 980 914 981 934 981 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 821 5754 848 2490 841 2492 819 2524 817 5726 845 2492 839 5727 844 5757 845 5727 854 2483 848 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 5092 1621 406 2599 406 2598 407 2605 1653 1616 411 2619 1609 1606 1654 1618 409 2623 382 2600 405 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8879 4446 566 1747 565 584 567 1744 568 581 570 1744 568 574 567 586 565 582 569 578 563 1753 570 575 566 1749 563 585 566 1743 569 1749 563 1748 564 582 569 1747 565 580 571 578 573 1741 571 572 569 584 567 580 571 1741 571 578 573 1738 564 1751 572 577 564 1744 568 1750 572 1740 572 41007 8906 2257 565 93855 8872 2265 567 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 1144 1010 6795 26754 1151 997 6798 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 1144 1009 1120 1006 1143 1991 1116 26758 1146 1006 1123 1003 1146 1988 1119 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 170 46238 169 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8906 4165 572 1672 569 1678 563 640 572 637 565 643 569 633 569 643 569 1674 567 639 563 1684 567 637 565 1681 570 1675 566 1673 568 1681 570 636 566 640 572 637 565 639 563 1684 567 640 572 630 572 640 572 634 568 1675 566 1681 570 1670 571 638 564 1681 570 1669 572 1678 563 1680 571 40485 8898 2252 570 85621 8955 2194 567 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 446 1191 449 1194 446 1195 445 1204 446 1200 1316 459 447 1193 447 1202 448 1197 443 1201 449 1191 449 34491 443 1204 446 1197 443 1198 442 1207 443 1202 1314 436 440 1201 449 1199 441 1205 445 1198 442 1199 441 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4268 4327 522 1593 516 1603 516 1597 522 519 520 520 519 515 514 531 518 520 519 519 520 521 518 519 520 1597 522 1595 514 1597 522 1599 520 1595 514 524 515 1604 515 521 518 523 516 524 515 519 520 525 514 524 515 1599 520 522 517 1596 513 1605 514 1602 517 1595 514 1607 522 1592 516 40481 8748 2187 523 93986 8721 2189 521 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 247 3006 244 153 178 1007 251 117 637 597 381 678 218 156 175 529 382 679 217 157 174 24963 247 3038 252 117 219 994 249 119 217 483 250 117 173 531 278 779 173 167 169 569 383 679 217 156 175 126538 246 3039 251 118 218 995 247 120 195 504 249 118 172 532 277 780 172 168 178 560 382 680 216 157 174 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8872 4454 568 579 572 578 573 572 569 581 570 1744 568 575 566 587 564 582 569 1743 569 1747 565 1745 567 1748 564 584 567 1741 571 1747 565 1747 565 1747 565 1751 571 1739 563 1752 570 578 563 1745 567 1751 572 1741 571 575 566 585 566 578 573 577 564 1750 573 571 570 583 568 579 572 41007 8905 2258 574 93846 8871 2266 566 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4278 4318 521 517 522 520 519 517 522 520 519 521 518 516 513 531 518 520 519 1595 514 1605 514 1599 520 1598 521 1595 513 1598 521 1600 519 1596 513 1602 517 1601 518 1595 514 1604 515 525 514 520 519 526 513 525 514 524 515 527 522 513 516 526 513 1603 516 1595 514 1607 522 1593 516 40481 8749 2186 524 93985 8722 2189 521 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 2746 8423 2744 19607 2746 19601 2742 8431 2745 8424 2742 19608 2745 8419 2747 19608 2745 19607 2746 8422 2744 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 170 6683 174 4889 175 10382 372 849 821 2498 176 10378 264 2479 842 2492 839 2475 846 2483 838 2481 840 2495 836 2477 844 845 846 37009 172 6681 176 4888 175 10381 373 848 924 2395 844 2490 174 10383 248 2492 172 10386 245 2495 169 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8898 4453 569 578 573 577 574 1737 565 584 567 582 569 574 567 1751 572 1741 571 1741 571 1744 568 576 575 1741 571 1743 569 1739 573 579 572 575 566 581 570 580 571 574 567 1748 575 1739 573 570 571 582 569 578 573 1739 573 1742 570 1740 572 577 574 575 566 1742 570 1749 574 1738 574 41006 8875 2262 570 93850 8907 2256 566 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8872 4454 568 1745 567 1749 563 1746 566 584 567 1747 565 1743 569 583 568 579 572 575 566 584 567 578 563 1752 571 578 563 580 571 1747 565 1747 565 581 570 1746 566 578 573 577 564 1751 572 571 570 583 568 579 572 1740 572 578 563 1747 565 1750 573 576 565 1743 569 1749 563 1749 563 41017 8906 2257 565 93855 8872 2265 567 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 179 2644 178 8174 170 2646 176 8181 173 2648 174 8177 177 2640 172 5419 174 5413 170 2649 173 2644 178 2646 176 2646 176 5409 174 28831 174 2651 171 8183 171 2648 174 8174 170 2655 177 8177 177 2642 170 5412 171 5420 173 2649 173 2646 176 2640 172 2653 169 5419 174 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 174 2648 174 8178 176 2640 171 8185 169 2653 169 8182 172 2645 177 2647 557 2264 558 5027 174 5410 173 5418 175 5413 170 28837 168 2649 173 8184 170 2652 170 8181 173 2643 169 8188 176 2646 176 2643 168 2648 174 5417 176 5411 172 5413 170 5413 170 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 178 706 266 139 268 137 173 173 198 234 178 126768 175 299 169 86 275 130 267 138 172 230 177 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 554 1922 584 3809 582 3782 578 3821 580 1920 555 1941 544 1922 574 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 476 1470 465 3479 474 3469 474 3477 475 1467 468 1471 474 27473 472 1474 472 3475 467 3478 475 3468 474 1471 475 1468 467 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 301 2188 236 2169 235 2205 230 1009 234 1070 183 1064 179 2198 206 1046 207 1069 173 2207 207 1042 211 1062 201 1043 210 1032 231 1005 237 1039 234 1006 236 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8872 4453 570 1744 568 582 569 1741 571 1744 568 580 571 1738 564 1754 569 578 563 584 567 1749 563 581 570 580 571 1743 569 573 568 585 566 1746 566 580 571 580 571 1739 563 586 565 1749 563 579 572 582 569 577 564 1748 564 1752 571 574 567 1748 564 584 567 1742 570 1748 564 1747 565 41015 8898 2265 567 93853 8875 2262 570 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8904 4446 576 572 569 581 570 575 566 584 567 582 569 1739 573 579 572 575 566 1746 566 1750 572 1738 574 1741 571 1742 570 573 568 1750 572 1740 572 1740 572 578 573 572 569 581 570 1744 568 574 567 586 575 572 569 578 573 1742 570 1740 572 1743 569 579 572 1737 565 1753 570 1743 569 41010 8881 2257 565 93855 8903 2259 573 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8878 4448 564 584 567 583 568 577 564 586 565 584 567 1741 571 582 569 1743 569 1743 569 1746 566 1744 568 1747 565 1749 563 579 572 1747 565 581 570 1743 569 1746 566 578 573 1743 569 579 572 571 570 583 568 579 572 575 566 584 567 1743 569 581 570 1744 568 1740 572 1746 566 1746 566 41013 8898 2264 568 93853 8872 2265 567 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8900 4450 572 575 566 585 566 578 573 1743 569 579 572 1736 566 587 574 572 569 1743 569 1747 565 1745 567 582 569 1745 567 575 566 1753 570 1742 570 1742 570 1746 566 578 573 1742 570 578 573 570 571 582 569 578 573 574 567 583 568 1742 570 579 572 1742 570 1738 574 1744 568 1744 568 41012 8871 2266 566 93855 8903 2258 574 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8872 4454 568 1745 567 1749 563 1747 565 1750 562 1752 571 1737 565 1754 568 1743 569 578 563 587 564 581 570 580 571 577 564 579 572 581 570 576 565 1748 564 1751 572 1739 563 1752 571 1743 569 1739 563 590 571 575 566 581 570 580 571 574 567 583 568 580 571 572 569 1750 562 1749 563 41017 8905 2257 575 93846 8871 2266 566 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 176 7764 173 11361 544 1951 545 1948 176 8815 171 2319 177 2322 174 2321 175 2318 178 2313 173 18281 170 7746 170 11369 536 1954 542 1957 539 969 538 1954 542 1949 536 1962 534 1960 174 2320 176 2315 170 2328 178 2317 168 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 172 23035 176 2267 178 2285 170 2270 175 2288 177 11602 222 2218 216 2246 173 183 173 935 221 218 174 864 221 1250 171 1310 223 2219 216 2247 218 1244 223 216 176 869 170 1296 217 2239 216 1254 223 216 176 866 219 9127 169 7642 173 2284 171 2273 172 2290 175 2263 171 10143 178 10623 222 1245 222 217 175 1843 220 2226 219 1264 223 1237 174 183 178 952 219 2222 223 216 176 866 219 1249 172 9190 173 7637 178 2266 169 2286 169 2280 175 2284 171 10125 175 10622 224 215 177 868 216 2228 217 2238 171 1299 224 215 177 865 174 183 173 934 222 216 176 1848 215 1247 220 1265 222 762 170 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 7410 1482 382 2742 375 2747 380 2751 1630 1535 400 2724 1657 1529 1629 1538 407 2720 407 2716 401 2722 4125 1549 376 2751 376 2748 379 2744 1626 1541 405 2723 1658 1530 1628 1531 404 2726 401 2726 401 2723 4124 1542 383 2747 380 2747 380 2745 1626 1534 401 2729 1652 1539 1629 1532 403 2719 408 2722 405 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4277 4319 520 518 521 521 518 518 521 1597 522 1594 515 1597 522 1599 520 1594 515 1600 519 1600 519 1594 515 526 513 527 522 512 517 528 521 517 522 516 513 1605 514 522 517 525 514 525 514 521 518 526 513 525 514 1600 519 523 516 1597 522 1596 513 1603 516 1595 514 1607 522 1593 516 40481 8749 2186 524 93986 8721 2188 522 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 1325 431 445 1199 1317 455 441 1207 443 1202 1294 457 449 1190 440 1209 441 1205 445 1198 1318 454 442 93237 1320 434 442 1201 1325 448 448 1200 440 1205 1291 460 446 1193 447 1202 448 1198 442 1201 1325 447 449 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8876 4450 572 575 566 585 566 578 573 577 564 585 566 577 564 589 572 574 567 1745 567 1749 563 1747 565 1750 562 1751 571 1737 565 1754 569 1743 569 1743 569 1747 565 579 572 1743 569 579 572 571 570 583 568 579 572 575 566 584 567 1743 569 581 570 1744 568 1740 572 1746 566 1746 566 41013 8898 2265 567 93853 8873 2264 568 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 300 1791 297 744 295 743 296 751 298 745 294 747 302 736 293 1837 271 745 294 747 302 1793 295 752 297 1802 296 1801 297 742 297 1806 302 31592 296 1801 297 742 297 749 300 744 295 745 294 745 294 752 297 1829 269 746 293 745 294 1809 300 744 295 1802 296 1799 299 748 301 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 178 2003 177 2899 177 1996 174 2908 168 2910 177 2000 170 2004 176 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8898 4173 564 642 570 1677 564 1677 564 645 567 640 572 631 571 641 571 635 567 639 563 1683 568 1673 568 641 571 636 566 637 565 647 565 641 571 634 568 642 570 1671 570 639 563 1682 569 632 570 643 569 636 566 1677 564 1683 568 636 566 1680 571 636 566 1674 567 1682 569 1674 567 40489 8904 2246 566 85626 8902 2248 564 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8897 4454 569 578 573 1743 569 576 565 1750 572 576 575 1733 569 584 567 1745 567 1745 567 583 568 1742 570 579 572 1742 570 573 568 1750 573 574 567 580 571 580 571 573 568 582 569 580 571 572 569 584 567 1744 568 1744 568 1748 575 1735 567 1749 574 1740 572 1736 566 1752 571 576 575 41005 8878 2259 563 93858 8901 2260 572 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8880 4446 566 1747 565 585 566 578 573 577 564 1750 573 571 570 1748 564 582 569 578 573 1743 569 1741 571 1744 568 580 571 1737 565 588 563 1749 563 583 568 582 569 576 565 1750 573 576 565 578 573 580 571 576 565 1747 565 1751 571 1738 564 586 565 1749 563 1745 567 1751 572 1741 571 41008 8905 2258 574 93846 8871 2266 566 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 3892 3856 525 978 529 977 530 969 528 977 530 974 523 975 532 1924 531 971 526 1924 531 975 532 1916 529 976 531 1921 524 1947 508 1925 530 1920 525 1926 529 1925 530 970 527 1927 528 976 531 1915 530 978 529 1921 1033 9201 3871 3867 524 977 530 975 522 981 526 972 525 983 524 978 529 1921 524 982 525 1923 532 973 524 1928 527 971 526 1931 524 1950 505 1922 523 1931 524 1924 531 1923 532 972 525 1922 523 985 533 1918 527 975 532 1922 1032 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8900 4451 571 576 575 1742 570 1739 573 1742 570 578 573 1736 566 1752 570 576 575 1737 575 575 566 579 572 578 573 1741 571 571 570 583 568 1745 567 579 572 578 573 1738 574 575 566 1748 575 568 573 581 570 576 575 1737 565 1751 572 573 568 1747 576 573 568 1741 571 1747 565 1747 565 41014 8878 2260 562 93858 8901 2261 571 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 875 904 1745 924 879 913 880 1786 873 919 874 917 1742 922 871 1802 877 912 1747 921 872 88714 880 907 1742 930 873 917 876 1788 871 924 879 910 1749 919 874 1798 871 916 1743 928 875 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4758 1543 403 2731 407 2726 402 2739 1601 1514 401 2760 1580 1530 1577 1540 406 2758 400 2708 1602 1535 400 2740 408 2729 409 2726 401 2731 1599 1520 405 2758 1572 1540 1578 1532 403 2737 431 2706 1604 1535 411 2722 405 2734 403 2734 404 2731 1599 1512 403 2764 1576 1539 1578 1533 402 2730 428 2712 1608 1534 402 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8873 4453 570 578 563 1753 570 575 566 1749 563 585 566 1742 570 583 568 1744 568 1744 568 582 569 1741 571 578 573 1741 571 572 569 1749 563 583 568 1745 567 1748 564 1746 566 583 568 581 570 1738 564 589 572 1740 572 574 567 584 567 577 564 1752 571 1743 569 573 568 1751 572 575 566 41014 8900 2263 569 93851 8878 2259 563 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 171 313 176 798 337 133 600 232 170 126777 176 65 169 496 176 200 609 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 500 527 491 4033 500 4986 495 510 498 4054 500 499 499 4048 496 4974 497 530 499 4026 497 4989 492 513 495 4057 497 4967 493 4992 499 506 492 4060 494 505 493 4054 500 98563 497 529 500 4025 498 4988 493 512 496 4056 498 501 497 4050 493 4976 495 532 497 4028 495 4991 500 505 493 4059 495 4969 491 4994 497 508 490 4062 492 507 491 4056 498 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 879 901 871 1796 1770 903 869 917 876 916 877 913 880 906 877 918 875 914 879 1788 1768 1784 875 87826 871 921 872 1797 1769 897 875 919 874 915 878 910 873 920 873 914 879 913 870 1800 1776 1767 871 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 874 905 1774 894 878 914 879 908 875 917 876 915 878 907 876 919 874 1793 876 913 1777 895 878 88703 872 920 1769 901 872 913 870 925 878 910 873 916 877 916 877 910 873 1798 871 919 1770 894 878 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 3339 1714 415 445 420 1283 418 440 415 448 418 444 422 435 420 446 420 440 415 445 421 443 412 445 421 443 412 449 416 1279 412 455 421 439 416 443 412 452 414 444 411 452 414 1287 414 442 413 453 413 1287 414 446 419 444 422 437 418 445 421 441 414 442 413 452 414 447 419 1281 420 443 412 1285 416 1287 414 1287 414 1282 419 447 419 441 414 1286 415 448 418 1280 421 1282 419 443 412 1283 418 448 418 1283 418 73871 3336 1694 414 444 411 1291 410 452 414 442 413 453 413 448 418 442 413 450 416 443 412 450 416 447 419 437 418 448 418 1282 419 441 414 449 417 442 413 449 417 446 420 436 419 1287 414 446 420 440 415 1288 413 445 410 453 413 449 417 439 416 450 416 445 421 439 416 447 419 1279 412 451 415 1287 414 1282 419 1287 414 1285 416 444 411 453 413 1285 416 447 419 1283 418 1278 413 453 413 1287 414 446 419 1284 417 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4411 4332 558 1660 561 596 565 612 559 597 564 617 565 585 566 620 561 591 560 620 561 595 566 611 560 597 564 1653 558 592 559 627 565 589 562 617 564 1630 560 617 564 592 559 22591 4439 4322 558 1639 561 618 563 590 561 622 560 592 559 624 557 597 564 612 559 600 561 618 563 590 561 622 559 1628 562 621 561 594 567 609 562 597 564 1652 559 595 566 617 564 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 3444 1767 413 487 419 1274 417 481 415 1277 414 488 418 1267 414 492 414 1276 415 484 412 1282 419 478 418 1275 416 1276 415 480 416 1280 421 479 417 1272 419 1275 416 1272 419 1274 417 484 412 484 412 493 413 1277 414 485 421 1273 418 479 417 486 420 1271 420 476 420 486 420 479 417 482 414 1280 411 1276 415 488 418 1273 418 478 418 488 418 481 415 1275 416 487 419 478 418 485 411 1280 421 475 421 1275 416 1273 418 69071 3439 1759 441 456 440 1253 438 463 443 1243 438 468 438 1251 440 460 446 1247 444 454 442 1251 440 461 445 1241 440 1256 445 455 441 1248 443 461 445 1242 439 1254 447 1245 446 1240 441 465 441 458 438 462 444 1249 442 456 440 1252 439 463 443 452 444 1252 439 461 445 454 442 461 445 452 444 1249 442 1250 441 454 442 1254 437 463 443 456 440 463 443 1245 446 456 440 462 444 451 445 1251 440 460 436 1253 438 1256 445 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8705 4317 583 682 581 688 585 678 585 1721 581 1722 580 681 582 690 583 682 581 1721 581 1725 587 1713 579 689 584 683 580 1718 584 1724 588 1714 588 677 586 683 580 683 580 1726 586 680 583 678 585 687 586 679 584 1718 584 1721 581 1719 583 685 588 1716 586 1713 579 1729 583 1719 583 41145 8706 2217 585 94686 8705 2217 584 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8888 4470 532 576 534 576 534 571 539 572 538 1706 535 568 532 581 529 578 532 1711 530 581 529 1711 530 1717 534 574 536 1703 538 575 535 572 538 1705 536 1711 530 1711 530 1716 535 1709 532 571 529 585 535 571 529 579 531 579 531 574 536 574 536 573 537 1701 530 1720 531 1712 529 39045 8912 2261 540 94838 8911 2235 536 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 874 915 1774 904 879 923 870 1816 1770 1800 1776 904 879 1805 874 931 1769 909 874 88698 876 927 1773 904 879 922 871 1819 1767 1796 1770 914 879 1809 870 928 1772 910 873 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 178 7753 174 2308 178 2284 171 2316 170 2298 177 10228 174 10724 223 1256 221 217 175 1868 169 2293 172 1327 216 1263 178 1316 217 2245 220 218 174 887 218 1261 170 10707 174 7752 175 2296 169 2314 171 2293 172 2307 179 10216 176 6265 174 10729 219 1258 219 1272 215 1268 173 2310 221 1255 222 217 175 877 172 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 532 1692 559 561 529 574 567 559 531 566 564 555 535 1694 557 568 532 1691 560 560 530 573 557 568 532 565 565 555 535 567 563 1688 533 564 567 554 536 567 563 562 538 558 562 558 532 1697 565 561 539 1683 558 1689 532 1697 564 1687 534 1689 562 1684 537 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 874 915 1774 904 879 923 870 927 876 1814 876 925 1775 900 872 1821 879 920 1769 909 874 88702 871 926 1774 907 876 925 878 917 876 1817 873 927 1773 905 878 1813 876 921 1769 912 871 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8884 4474 538 569 531 1716 535 570 530 580 530 1715 536 567 533 580 530 577 533 1710 531 1715 536 1705 536 1710 531 1714 537 1702 529 1720 531 1712 529 578 532 1715 536 1705 536 1710 531 577 533 570 530 584 536 571 529 1714 537 573 537 568 532 578 532 1713 538 1701 530 1719 532 1711 530 39044 8913 2260 531 94848 8907 2239 532 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 446 1694 455 1706 453 1705 424 628 452 597 452 622 458 1673 456 625 455 594 455 1706 454 590 459 621 459 591 458 616 453 591 458 622 539 22750 459 1675 454 1733 427 1712 427 622 458 588 451 622 458 1681 458 618 451 595 454 1705 455 598 451 625 454 592 457 616 453 598 451 626 535 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 3429 3445 875 2555 868 875 867 871 871 2560 873 868 874 2551 872 874 868 871 871 869 873 870 872 865 867 2565 868 873 869 2556 867 2567 866 874 868 2560 873 870 872 2555 868 2564 869 2586 847 2577 846 2589 844 870 872 32983 3470 3430 869 2559 874 868 874 867 875 2550 873 873 869 2559 874 865 867 877 875 862 870 873 869 872 870 2555 868 877 875 2554 869 2559 874 869 873 2554 869 873 869 2562 871 2553 870 2590 843 2586 847 2581 842 876 866 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8873 4452 571 1743 569 580 571 574 567 583 568 1746 566 1742 570 1748 564 582 569 578 563 1753 570 1740 572 1743 569 579 572 571 570 583 568 1744 568 579 572 578 573 572 569 1746 566 582 569 574 567 586 565 582 569 1743 569 1746 566 1744 568 581 570 1745 567 1741 571 1747 565 1746 566 41014 8898 2264 568 93853 8874 2263 569 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8902 4448 574 1739 573 1742 570 575 566 584 567 581 570 573 568 585 566 1746 566 580 571 580 571 1739 573 1742 570 1743 569 1739 573 1745 567 579 572 1741 571 1744 568 1742 570 1745 567 1747 565 1743 569 1749 573 1739 573 573 568 583 568 576 575 575 566 583 568 575 566 587 574 572 569 41011 8871 2266 566 93854 8902 2260 572 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4275 4320 519 520 519 523 516 520 519 1599 520 520 519 515 524 521 518 520 519 1595 524 1595 524 1588 520 521 518 1599 520 1591 517 1603 516 1599 520 1595 524 1594 525 1588 521 1597 522 518 521 513 526 519 520 518 521 517 522 520 519 517 522 519 520 1597 522 1589 519 1601 518 1597 522 40475 8724 2186 514 93995 8752 2183 516 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 10380 4892 599 620 592 605 597 620 592 604 598 623 589 2081 598 627 595 2080 599 2101 598 2105 574 2099 590 2088 591 2111 599 591 590 2116 594 599 593 626 596 2082 597 619 593 2086 593 626 596 594 598 627 595 598 594 2106 593 604 598 2100 589 607 595 2107 593 2079 590 2116 594 2082 750 41149 8722 2109 590 94682 8727 2104 596 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8879 4446 566 1747 565 1751 571 573 568 582 569 580 571 571 570 584 567 1744 568 579 572 578 573 1737 565 1751 572 1742 570 1738 564 1754 568 578 573 574 567 584 567 577 564 1752 571 577 564 580 571 582 569 577 564 1748 564 1752 571 1739 563 587 564 1750 572 1736 566 1752 571 1742 570 41009 8903 2260 572 93848 8880 2257 565 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4271 4324 515 1600 519 1600 519 1594 515 1603 516 1601 518 1593 516 1605 514 1601 518 520 519 522 517 520 519 522 517 523 516 518 521 523 516 522 517 1598 521 1597 522 1591 518 1600 519 521 518 516 523 522 517 521 518 520 519 523 516 520 519 522 517 1599 520 1591 518 1603 516 1599 520 40477 8753 2183 516 93993 8724 2185 515 -# -name: POWER -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 7847 3931 470 1448 467 495 472 1443 472 490 467 1451 464 491 466 499 468 491 466 4413 467 1455 470 486 471 1449 466 495 472 1441 464 501 466 492 465 494 463 22093 7851 3934 467 1454 471 490 467 1446 469 496 471 1446 469 489 468 494 473 484 473 4410 470 1449 466 489 468 1455 470 489 468 1449 466 496 471 486 471 490 467 diff --git a/assets/resources/music_player/Marble_Machine.fmf b/assets/resources/music_player/Marble_Machine.fmf deleted file mode 100644 index 7403c9a0f15..00000000000 --- a/assets/resources/music_player/Marble_Machine.fmf +++ /dev/null @@ -1,6 +0,0 @@ -Filetype: Flipper Music Format -Version: 0 -BPM: 130 -Duration: 8 -Octave: 5 -Notes: E6, P, E, B, 4P, E, A, G, A, E, B, P, G, A, D6, 4P, D, B, 4P, D, A, G, A, D, F#, P, G, A, D6, 4P, F#, B, 4P, F#, D6, C6, B, F#, A, P, G, F#, E, P, C, E, B, B4, C, D, D6, C6, B, F#, A, P, G, A, E6, 4P, E, B, 4P, E, A, G, A, E, B, P, G, A, D6, 4P, D, B, 4P, D, A, G, A, D, F#, P, G, A, D6, 4P, F#, B, 4P, F#, D6, C6, B, F#, A, P, G, F#, E, P, C, E, B, B4, C, D, D6, C6, B, F#, A, P, G, A, E6 diff --git a/assets/resources/subghz/assets/keeloq_mfcodes b/assets/resources/subghz/assets/keeloq_mfcodes deleted file mode 100644 index f9771285edc..00000000000 --- a/assets/resources/subghz/assets/keeloq_mfcodes +++ /dev/null @@ -1,48 +0,0 @@ -Filetype: Flipper SubGhz Keystore File -Version: 0 -Encryption: 1 -IV: F2 D4 F5 5A B3 CC 3F 21 28 3A AF ED D1 EB 73 DF -BBFA4D79A73C384D6E07E717F761F32A625F28AA1DB2261B8B19A18261E30AB6 -CE4004AB56111B0B3D486770705FAD8BD616A80957EA2C537BAF1FD09E552DA3 -F974561612C9C751237C64D978F706B41873FDBE38851306574F436CB02D9ECA -E29CAB7C2C2D9853D0F4DF69F582562E8182234E78B355540F6FE3F78C73D518 -97ABE18993A700A607E37DC88E1434F84DDD1C2771693978C9D2FA4CE4F2AB7BBC7C3EB3E8545B37FBBE1C1F1CA03543 -E86ABD0AAE5A4B4A9414C9CB6112CA49B3A8EC29723B14DCA85902F41B05ADDC -C1FBE921035F408C59DA6AD5E76E3887AC9BC90146619B3CAE445BED556E96AC -232C9F86915B927888352797B45F159268FE78956CF09B8D241CDC393D3B0225 -3D9E2A3C701C9D4DD4D72038D4536CA6F515C547CAB0AD18BA71204BD2ABFB74 -4D69A4506D2C97EF8EC68F90CF1AD1065A1EB909793EEB3AF71B0D75E55B9E76 -5A7F4595DFA181C3E946EBEE4974DBD6DA85AF6FCAD0B3725FDD28667175A421D69A2122853E57927C38CCF368732476 -6A946FAEDE134155B5A88EC01AA535E7A778947D360218B560381A64CAF9ACE896079D04C14718D5AD5C0D4EE3005F52 -88AC0C723AAA875A1885C8392A616FA43B205119B0E8D299193979A1921FC8B3 -40588AADA5E1A8BE214B2CCF32D268B48C6B783AE0DD10D88BDF3FF88E921E09 -A7BE05D05DEC9B9A3AE1575D411BF7B12366AD78B726F3E3E843E7BF199961A4 -79F973A155A4367F0EAA078AA0857A2A2A82FC4C8A5AE9E567E7CBF62C2A5CE2 -C38296EEABDA1F95D0C401CC6DDC8656476DC19248588EEF1CB93773D94CDB02A40C902970C4FCB14FABEFFB4F8BC208 -B0B7699B3C3573EE4D88D8CE65FAF3532B5A741D1F20892C0F38BAA2BCE98F2D -6E401D6BDB1B33A404DEB668F3FB353166475487BAADE4A348E3CFDEB3B1B54B -0E44B87878617559783CC6A7C65BE9F99950FE8956ED4BB04894BC53085E3A09CA19915B1E8C143A68D1B7A97F5D1ECB -AC19E55638429C65E6E567C0E96DA9648F8FB80215CF693D7FD5DD86FE7989AC7AC7BAE86BBD4FFF7161AFFB405FFA98 -BCE70C69D90AD639A737813FC8FD26F40F803137BD36E47651C266A671428D6F -F053CF5255AD2E1875A5C38635F7BF203B1DAE1433B162C30AE8695AC8A5589D -B7EFC77FFA98B173E429B3566A27842C4DC5E91B0BC01F07A6A98332C4E1F42A -D7C7950FFB2C5E7D9BCDBC230BF5F1BFFC0FE6F1CF5C8C6013DD90E41AE403FE -50667B2E5909FD5F9D6385788A81DE5F72E56512EAD6BF5EACCA959CB6AF0DEF -6435E07E5E952124B0F80F76E0F68265B8289087387E35C6D51831B299335480 -D7DE1F7748FB8BF90561151CC6AEADC160CA883FE5228768A3737A89F358AF58 -FA206F860C6F981FD4A358FDEA5E1860353406D8416FF2A811D17EBA09C803EA -F2F7B2C6705D1457315F2AAA859AB53592241D63B84C045BC742D220BA110144 -3F0E05E572D1DF5E2B0BBB20EF8F3EB4D198CDF2794F86089E1DB0EF975E9337 -7D54D088C22AA3BA9A97FAB64371B8D512CDEC2A4355116BE2B74BCEC7FEC852 -0FD951F13E19F0FC1A25655DA430640034BE34659C526238E62B6042691998CB -FCA04B0BF98FA89AAEF41A78AE7141EF7783E0D0CBAAB1B6F00C0AD3EAA84A54759D46E1A9BEEDCCE68BA12902802111 -6AD801CE08D58A380B689574BD7FCACC5DF768BDD93AD7EE1AA514A2351EF13A -0A820F47699AFC4A5E3285BF521771FC5B6C5FB7C6C08A1990DA3B3A6766E860 -A7AAC90972DB24D20B57DDD46DC2624FC6169D529426E64B0544AC383799BB2A -AF6088873BC71ED672FA39D50B386523825218C43CDB35D691B0C5895B7EF5C2 -774DFAC8D285241368CB377DA947D7A94951A1520017DF77FE2E6A517D5C6A1FC768BB1E2398F5AF71B10D1806C04CCD -AA788A707E64C40E2A0EB8154FE795EAC68B936FD6BAC5DEF7677A4D5FE344DD -A193EF5D1B223B0FA3C231052EDBDD7A31B0C192BCD8E7E37E11D4D899476ACD -F6986E08949122D46BFA7F218B089E8DB00DCFA6971C5F2468CDDD179E5BBC40 -EDC23A07689EF6229081D1AB9E249E68527BD33EB72C242BA97727E64AF15BCC -70CC64359A2A5DE40D5A30E916DE6532BCC511E7489CD3A2E5DEC269D303FDBD83B7EA14BF13B40E3C960C6D3D12774B diff --git a/assets/resources/subghz/assets/keeloq_mfcodes_user b/assets/resources/subghz/assets/keeloq_mfcodes_user deleted file mode 100644 index f013afa2db4..00000000000 --- a/assets/resources/subghz/assets/keeloq_mfcodes_user +++ /dev/null @@ -1,11 +0,0 @@ -# for adding manufacture keys -# AABBCCDDEEFFAABB:X:NAME\r\n -# AABBCCDDEEFFAABB - man 64 bit -# X - encryption method 1 - Simple Learning, 2 - Normal_Learning, 3 - Secure_Learning, 4 - Magic_xor_type1 Learning -# 0 - iterates over both previous and man in direct and reverse byte sequence -# NAME - name (string without spaces) max 64 characters long -Filetype: Flipper SubGhz Keystore File -Version: 0 -Encryption: 0 -AABBCCDDEEFFAABB:1:Test1 -AABBCCDDEEFFAABB:1:Test2 diff --git a/assets/resources/subghz/assets/setting_user b/assets/resources/subghz/assets/setting_user deleted file mode 100644 index 1f37a2eb5de..00000000000 --- a/assets/resources/subghz/assets/setting_user +++ /dev/null @@ -1,29 +0,0 @@ -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: 310000000 -#Frequency: 320000000 - -# Frequencies used for hopping mode (keep this list small or flipper will miss signal) -#Hopper_frequency: 300000000 -#Hopper_frequency: 310000000 -#Hopper_frequency: 310000000 - -# 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 - -#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/slideshow/first_start/frame_02.png b/assets/slideshow/first_start/frame_02.png index dc1080abaab..adff6af6672 100644 Binary files a/assets/slideshow/first_start/frame_02.png and b/assets/slideshow/first_start/frame_02.png differ diff --git a/assets/unit_tests/subghz/honeywell_wdb.sub b/assets/unit_tests/subghz/honeywell_wdb.sub deleted file mode 100644 index bce11cd0d63..00000000000 --- a/assets/unit_tests/subghz/honeywell_wdb.sub +++ /dev/null @@ -1,7 +0,0 @@ -Filetype: Flipper SubGhz Key File -Version: 1 -Frequency: 868350000 -Preset: FuriHalSubGhzPreset2FSKDev476Async -Protocol: Honeywell -Bit: 48 -Key: 00 00 0E DB 70 20 00 01 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub deleted file mode 100644 index 0a7d529ce64..00000000000 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ /dev/null @@ -1,130 +0,0 @@ -Filetype: Flipper SubGhz RAW File -Version: 1 -Frequency: 433920000 -Preset: FuriHalSubGhzPresetOok650Async -Protocol: RAW -RAW_Data: 1160 -296 263 -166 65 -66 133 -98 133 -494 97 -226 361 -100 295 -66 131 -164 397 -66 431 -198 989 -100 431 -66 131 -66 197 -1052 231 -400 165 -102 199 -100 199 -100 299 -68 229 -234 167 -98 201 -136 267 -132 165 -100 131 -66 67 -66 131 -68 231 -100 99 -134 165 -132 665 -66 231 -100 267 -66 197 -100 659 -66 231 -266 197 -98 229 -990 297 -98 67 -796 99 -562 231 -300 65 -100 363 -1094 163 -336 331 -132 327 -98 261 -98 293 -100 265 -100 1265 -66 335 -134 267 -98 333 -134 99 -66 331 -66 195 -66 227 -164 263 -100 65 -692 163 -1978 97 -232 63 -132 131 -66 263 -66 293 -98 727 -132 65 -66 97 -66 295 -132 97 -66 263 -100 365 -100 165 -268 463 -66 65 -66 265 -1326 97 -100 197 -888 297 -130 327 -98 229 -264 65 -496 131 -266 195 -132 259 -132 361 -132 165 -98 927 -332 99 -66 99 -100 197 -658 65 -100 197 -164 133 -766 197 -236 199 -168 65 -98 233 -198 65 -100 129 -166 197 -790 97 -394 393 -130 557 -100 65 -230 333 -132 1389 -134 65 -66 99 -100 265 -134 99 -100 97 -460 327 -166 233 -266 199 -562 67 -898 197 -100 165 -1100 231 -100 263 -100 263 -132 1161 -66 463 -98 133 -98 165 -130 131 -66 65 -264 97 -266 97 -66 163 -166 97 -66 229 -296 99 -132 391 -98 327 -98 163 -200 631 -196 169 -102 131 -166 1327 -134 1361 -132 331 -134 99 -396 195 -364 97 -132 163 -66 99 -264 133 -200 165 -334 99 -134 65 -1332 165 -132 133 -298 367 -1124 295 -66 129 -66 593 -66 329 -264 397 -66 533 -66 461 -66 133 -132 65 -266 65 -132 99 -232 131 -2186 297 -98 165 -100 65 -66 97 -100 63 -566 99 -100 133 -66 333 -332 231 -364 231 -68 165 -300 497 -100 763 -100 365 -98 165 -200 65 -100 65 -696 65 -134 65 -262 65 -430 97 -66 131 -98 131 -330 293 -198 97 -132 163 -628 133 -98 131 -198 559 -98 459 -64 753 -98 361 -100 165 -132 133 -364 99 -66 197 -100 65 -132 197 -896 199 -66 65 -432 231 -168 231 -100 299 -528 131 -232 99 -134 97 -66 299 -66 197 -68 495 -100 65 -98 65 -100 563 -828 197 -732 267 -1260 65 -100 165 -898 65 -430 165 -496 199 -66 231 -66 131 -132 195 -66 193 -294 1259 -134 165 -100 199 -66 199 -166 233 -430 97 -366 97 -166 65 -1196 97 -132 97 -64 131 -196 97 -66 131 -960 229 -98 99 -264 233 -134 297 -98 467 -100 165 -98 365 -98 735 -98 197 -1464 233 -626 301 -432 97 -132 -RAW_Data: 231 -100 163 -362 163 -132 261 -298 65 -232 67 -132 297 -100 229 -228 497 -66 395 -98 593 -132 165 -132 67 -100 397 -132 99 -600 297 -66 231 -132 495 -136 65 -860 197 -98 129 -260 129 -100 131 -134 331 -268 165 -98 167 -66 231 -68 131 -232 65 -396 99 -100 663 -200 231 -66 1067 -66 99 -166 331 -132 65 -196 65 -134 133 -66 795 -66 397 -100 233 -132 199 -66 233 -200 433 -362 67 -298 327 -100 397 -66 165 -66 233 -232 529 -66 165 -100 67 -66 397 -66 689 -100 195 -462 325 -164 227 -132 261 -426 331 -98 133 -66 97 -296 97 -98 197 -66 97 -428 163 -526 359 -132 327 -164 99 -66 231 -396 329 -64 525 -66 593 -856 195 -100 129 -132 227 -166 229 -130 129 -98 293 -132 163 -98 195 -130 195 -98 97 -66 165 -100 99 -66 199 -466 265 -66 131 -132 197 -198 163 -232 1857 -100 265 -264 67 -264 265 -368 297 -102 133 -66 233 -98 167 -66 201 -66 133 -134 167 -166 65 -168 99 -400 165 -498 97 -298 131 -198 227 -132 327 -98 97 -66 297 -230 593 -132 67 -132 1227 -68 199 -134 197 -102 131 -232 365 -962 133 -298 297 -66 265 -266 333 -230 165 -102 165 -234 65 -98 131 -98 133 -66 167 -134 1357 -134 599 -532 265 -268 99 -298 199 -100 165 -166 131 -100 231 -266 65 -68 231 -334 67 -266 97 -134 65 -198 133 -100 163 -336 165 -234 65 -332 163 -690 97 -564 263 -64 165 -66 499 -100 563 -230 229 -132 99 -166 165 -132 199 -66 497 -66 659 -166 233 -68 331 -100 331 -66 165 -202 133 -896 133 -228 163 -196 131 -66 229 -130 263 -164 65 -98 263 -164 889 -66 261 -164 99 -494 97 -132 131 -64 65 -462 65 -262 99 -332 65 -102 65 -496 99 -132 197 -230 299 -134 65 -166 99 -430 99 -100 563 -66 165 -330 199 -264 197 -68 231 -100 367 -134 365 -68 299 -68 495 -68 165 -100 65 -498 397 -66 327 -100 195 -98 163 -132 163 -360 197 -98 65 -462 97 -526 427 -100 1419 -66 265 -66 333 -98 299 -98 99 -132 97 -100 197 -134 97 -100 231 -68 133 -100 165 -100 97 -100 133 -98 133 -66 165 -166 267 -132 229 -68 299 -66 97 -964 131 -66 133 -98 99 -66 297 -132 131 -68 167 -134 201 -68 167 -66 301 -166 133 -68 365 -100 527 -130 165 -130 233 -262 95 -628 97 -66 231 -168 99 -498 99 -100 229 -100 99 -166 299 -232 65 -100 99 -468 131 -68 197 -100 99 -66 297 -330 99 -100 463 -100 231 -68 133 -234 131 -134 231 -98 -RAW_Data: 561 -264 131 -100 65 -98 99 -66 133 -134 165 -132 65 -68 97 -68 131 -896 329 -198 65 -100 295 -230 199 -264 97 -196 327 -132 131 -100 131 -100 1293 -66 131 -202 463 -198 65 -68 99 -298 65 -232 333 -566 199 -168 99 -168 65 -498 295 -134 167 -134 199 -100 431 -100 65 -530 233 -164 65 -98 297 -134 363 -100 557 -100 163 -100 261 -66 619 -132 231 -296 97 -596 97 -228 131 -66 97 -66 197 -66 97 -360 229 -98 129 -330 99 -130 99 -130 65 -100 65 -132 97 -460 63 -100 265 -330 331 -98 197 -66 855 -464 165 -66 99 -198 99 -868 231 -66 167 -636 329 -530 165 -130 131 -132 99 -528 165 -64 65 -164 65 -166 129 -162 425 -66 231 -100 133 -98 231 -68 333 -98 165 -200 361 -98 99 -66 297 -68 163 -168 65 -498 99 -232 295 -696 99 -266 131 -100 65 -100 99 -628 133 -132 65 -366 265 -398 261 -100 529 -100 297 -132 2535 -596 65 -932 199 -66 199 -866 65 -298 165 -66 67 -132 65 -100 165 -200 131 -166 299 -200 167 -166 199 -334 65 -694 365 -132 727 -232 265 -66 133 -102 65 -100 531 -232 65 -132 65 -100 131 -562 197 -66 297 -66 265 -100 433 -432 495 -332 197 -98 167 -332 97 -66 297 -758 65 -228 99 -194 295 -130 99 -98 261 -98 163 -66 231 -134 1029 -68 399 -200 133 -132 99 -266 163 -264 163 -130 229 -66 689 -166 131 -988 65 -592 97 -132 129 -164 197 -432 65 -100 165 -134 65 -166 133 -232 563 -66 231 -66 599 -66 265 -100 199 -498 231 -464 327 -262 131 -100 327 -430 197 -266 99 -132 65 -66 133 -132 101 -200 65 -100 433 -166 99 -266 265 -98 97 -66 229 -100 263 -200 631 -100 199 -66 167 -300 753 -262 457 -132 231 -98 163 -164 227 -164 65 -262 129 -594 229 -790 131 -66 129 -428 197 -164 197 -200 133 -100 131 -564 65 -134 661 -134 2925 -68 231 -134 167 -132 99 -98 133 -98 167 -98 197 -1086 329 -296 65 -130 65 -264 197 -132 163 -98 165 -66 97 -132 129 -100 261 -66 97 -66 197 -326 95 -134 629 -98 65 -262 65 -132 197 -264 129 -98 163 -230 131 -528 261 -298 197 -64 131 -394 131 -764 99 -132 99 -66 99 -464 367 -98 165 -98 1159 -134 963 -66 633 -562 133 -132 99 -66 131 -564 197 -134 263 -132 195 -66 131 -262 459 -332 197 -66 133 -1060 133 -66 331 -662 197 -134 231 -100 299 -232 1087 -232 131 -100 265 -462 131 -200 433 -168 101 -432 97 -196 65 -132 327 -100 129 -226 163 -100 163 -132 -RAW_Data: 227 -330 129 -466 165 -200 163 -66 97 -130 97 -66 197 -164 457 -166 463 -166 361 -66 163 -66 97 -198 65 -64 99 -300 65 -68 263 -98 99 -134 197 -100 165 -134 231 -66 297 -98 67 -132 365 -66 197 -228 99 -428 195 -132 65 -330 265 -628 63 -130 97 -132 195 -232 195 -130 263 -132 167 -100 133 -166 1229 -264 165 -332 233 -1230 199 -102 167 -100 401 -166 229 -852 197 -264 229 -130 163 -198 65 -98 229 -66 199 -100 335 -98 899 -134 393 -528 97 -66 97 -300 99 -300 99 -664 329 -198 99 -400 131 -198 299 -330 99 -66 65 -132 67 -298 527 -66 493 -132 1085 -198 425 -232 197 -66 131 -164 165 -66 197 -1480 197 -330 65 -494 65 -330 197 -296 99 -66 97 -592 99 -164 97 -426 329 -66 131 -98 99 -66 265 -98 433 -66 131 -234 267 -234 65 -266 99 -98 65 -166 97 -164 197 -198 65 -866 67 -66 231 -430 131 -200 65 -100 631 -198 265 -526 99 -230 129 -68 165 -232 229 -98 427 -328 1053 -134 431 -198 365 -430 131 -100 131 -398 231 -134 1195 -132 131 -134 661 -100 333 -398 97 -400 97 -234 165 -468 65 -100 99 -66 299 -266 67 -98 397 -166 987 -132 97 -64 65 -266 97 -852 297 -132 163 -428 65 -298 99 -1124 97 -266 233 -66 165 -332 65 -800 297 -98 131 -164 469 -100 97 -658 165 -66 133 -132 531 -134 131 -98 297 -722 65 -132 821 -100 229 -132 459 -66 197 -98 985 -330 131 -100 133 -100 101 -198 299 -100 65 -134 363 -100 67 -830 131 -132 99 -100 299 -66 297 -366 99 -166 197 -66 131 -364 131 -200 331 -134 265 -530 99 -564 65 -68 231 -132 233 -134 165 -66 165 -66 431 -102 465 -132 99 -132 99 -364 133 -98 67 -198 133 -66 65 -264 99 -298 231 -100 199 -728 297 -132 65 -134 433 -926 263 -66 227 -528 65 -130 263 -100 97 -98 723 -100 131 -134 531 -66 267 -400 199 -66 65 -100 99 -498 99 -332 529 -98 197 -460 131 -296 231 -296 97 -66 65 -426 131 -132 133 -100 99 -66 99 -964 463 -68 631 -68 1325 -232 365 -68 163 -432 131 -432 301 -234 165 -432 99 -398 65 -100 133 -166 433 -230 163 -236 67 -132 97 -300 65 -66 199 -132 2355 -296 327 -98 65 -66 99 -294 197 -232 133 -100 67 -134 97 -66 65 -760 65 -98 131 -166 197 -132 63 -132 163 -98 163 -754 429 -230 163 -66 695 -132 199 -98 433 -100 1197 -394 491 -460 65 -460 65 -166 97 -68 131 -134 233 -66 299 -762 231 -200 265 -198 499 -132 167 -766 -RAW_Data: 265 -66 165 -134 165 -66 365 -132 2091 -134 231 -632 297 -630 195 -264 65 -296 65 -64 131 -298 165 -100 99 -100 99 -562 165 -398 99 -566 99 -166 133 -334 99 -134 357 -68 97 -332 459 -130 161 -230 163 -132 163 -98 195 -164 97 -130 65 -1262 265 -166 563 -964 133 -100 99 -796 199 -868 67 -134 65 -232 67 -66 199 -132 295 -98 821 -332 1097 -296 129 -758 65 -200 67 -98 531 -198 265 -66 863 -66 65 -300 363 -100 99 -132 165 -100 267 -134 165 -234 231 -100 533 -198 1057 -200 497 -68 531 -100 63 -66 133 -100 163 -330 63 -230 161 -100 97 -66 131 -298 165 -68 167 -732 265 -396 265 -100 99 -166 99 -200 65 -596 131 -696 263 -200 135 -100 133 -66 65 -98 131 -66 399 -66 367 -130 765 -298 97 -98 231 -130 197 -130 65 -294 131 -98 195 -66 129 -66 131 -428 131 -66 197 -660 133 -66 131 -332 165 -628 65 -724 99 -164 131 -98 65 -166 395 -68 435 -68 299 -66 1323 -400 231 -298 99 -264 199 -428 131 -230 65 -1188 99 -98 163 -526 327 -66 197 -394 165 -66 65 -596 857 -66 163 -66 591 -166 129 -234 167 -624 265 -294 263 -500 131 -164 65 -98 195 -166 97 -100 195 -198 131 -1214 1017 -166 97 -428 425 -66 1183 -430 261 -230 65 -196 97 -100 195 -364 65 -328 65 -560 97 -1084 197 -694 99 -132 65 -132 97 -66 229 -332 163 -330 197 -66 327 -100 195 -98 327 -98 993 -132 697 -332 665 -166 199 -234 99 -168 199 -266 201 -66 199 -462 231 -298 195 -66 165 -884 165 -196 131 -66 365 -98 231 -98 329 -430 165 -66 1195 -300 133 -100 163 -132 65 -462 131 -196 163 -228 197 -992 163 -162 95 -824 131 -98 263 -98 97 -166 65 -98 131 -100 65 -496 131 -100 265 -134 199 -68 199 -98 329 -132 131 -66 983 -198 99 -132 295 -66 261 -460 329 -164 131 -426 65 -268 65 -466 131 -66 131 -230 263 -230 297 -200 895 -66 231 -460 1055 -134 433 -100 65 -66 231 -398 131 -400 97 -68 165 -330 297 -330 133 -396 99 -66 263 -262 97 -1644 295 -66 297 -100 729 -132 335 -68 491 -66 231 -326 393 -68 329 -166 65 -922 99 -1652 197 -266 265 -132 99 -432 265 -134 293 -134 65 -166 231 -232 199 -64 631 -400 163 -400 99 -66 131 -730 265 -100 195 -66 195 -330 163 -132 131 -166 67 -830 99 -66 297 -664 97 -134 65 -100 295 -66 133 -166 997 -66 299 -66 795 -98 493 -68 131 -166 131 -100 165 -198 67 -98 99 -598 65 -266 97 -268 199 -132 -RAW_Data: 65 -364 97 -298 97 -692 129 -264 261 -66 263 -564 297 -298 97 -232 131 -66 97 -66 361 -100 199 -68 433 -66 331 -68 333 -438 165 -598 99 -134 65 -100 65 -264 399 -266 67 -764 197 -232 297 -498 199 -858 65 -100 97 -130 131 -230 231 -230 97 -362 229 -66 497 -66 329 -132 65 -64 423 -98 393 -132 65 -664 65 -198 99 -68 331 -100 97 -332 133 -166 199 -98 693 -332 429 -100 265 -166 363 -100 167 -66 497 -100 1365 -202 333 -200 65 -132 199 -100 99 -330 131 -300 229 -832 65 -764 65 -132 263 -200 131 -1026 97 -196 97 -132 165 -98 525 -232 431 -66 659 -66 461 -98 97 -132 65 -100 261 -66 129 -66 163 -100 229 -364 131 -1560 165 -364 133 -268 165 -132 99 -200 65 -434 97 -68 263 -298 131 -230 199 -132 199 -100 165 -68 1829 -134 463 -100 265 -890 395 -364 131 -362 97 -260 97 -462 65 -66 457 -296 197 -268 263 -66 363 -132 895 -66 435 -164 403 -464 165 -928 163 -134 167 -400 263 -660 131 -100 63 -428 199 -132 199 -700 397 -232 335 -66 497 -166 1425 -66 231 -102 231 -134 99 -132 133 -134 99 -530 133 -66 131 -66 165 -166 199 -266 65 -100 131 -100 527 -300 925 -266 363 -530 131 -898 165 -66 199 -134 165 -232 525 -100 1509 -100 99 -200 99 -168 67 -300 163 -404 263 -132 67 -66 263 -664 65 -494 199 -100 165 -100 263 -264 131 -264 161 -66 65 -428 131 -296 163 -264 559 -166 597 -132 199 -498 65 -66 263 -162 163 -66 65 -66 131 -762 197 -162 65 -98 297 -262 163 -66 131 -130 97 -100 131 -564 199 -100 333 -166 165 -66 1653 -100 689 -296 131 -230 229 -526 65 -462 131 -100 197 -398 99 -264 133 -428 233 -134 265 -132 65 -200 97 -100 131 -100 265 -232 99 -232 429 -196 263 -166 669 -230 293 -298 561 -100 65 -100 229 -100 99 -830 99 -132 65 -1058 133 -564 99 -100 131 -826 165 -100 101 -100 65 -100 99 -398 329 -266 65 -66 557 -132 67 -132 627 -464 99 -66 163 -200 99 -66 99 -232 65 -66 165 -132 167 -266 99 -598 65 -432 65 -134 99 -330 197 -202 65 -266 133 -598 99 -68 163 -66 165 -66 1957 -430 393 -134 97 -134 97 -530 65 -428 161 -1150 65 -64 131 -594 229 -326 131 -196 197 -132 65 -566 297 -68 367 -64 1119 -464 163 -334 165 -134 163 -102 99 -66 199 -460 99 -100 99 -132 67 -266 99 -98 99 -532 197 -992 129 -362 231 -166 65 -132 133 -98 599 -98 795 -66 235 -100 333 -232 331 -98 99 -430 -RAW_Data: 131 -266 297 -232 231 -100 267 -266 397 -232 131 -132 133 -668 65 -98 99 -68 131 -398 1325 -66 333 -166 99 -100 199 -960 99 -66 231 -132 167 -134 199 -100 165 -100 495 -132 367 -332 99 -266 495 -66 65 -66 99 -66 65 -134 163 -134 199 -164 199 -98 531 -132 497 -100 65 -100 97 -530 99 -100 131 -68 131 -200 265 -102 97 -332 365 -66 265 -434 163 -68 97 -232 265 -132 65 -264 133 -198 497 -100 197 -68 729 -66 165 -330 165 -762 265 -430 397 -3844 67 -432 1197 -66 631 -66 521 -458 131 -132 197 -264 65 -922 163 -1586 99 -492 229 -530 65 -634 131 -134 65 -398 99 -398 363 -366 295 -1158 99 -134 65 -298 431 -432 97 -634 131 -66 133 -298 97 -466 99 -66 65 -332 65 -498 65 -962 133 -132 99 -464 233 -598 165 -196 199 -68 331 -66 231 -166 65 -200 133 -100 197 -162 393 -264 327 -1052 65 -164 163 -560 99 -98 63 -394 165 -132 99 -166 99 -100 97 -234 131 -1028 131 -66 99 -394 99 -164 97 -298 163 -66 359 -296 197 -66 499 -98 167 -462 695 -132 131 -134 97 -264 65 -100 265 -132 197 -896 195 -166 65 -262 99 -492 97 -196 99 -66 163 -164 97 -1414 133 -364 65 -132 99 -362 131 -164 195 -296 67 -334 197 -98 227 -66 265 -666 165 -430 167 -364 65 -98 97 -230 67 -364 133 -132 165 -134 65 -100 197 -230 97 -462 131 -296 195 -262 99 -98 327 -530 97 -464 99 -198 167 -234 97 -628 99 -266 131 -134 165 -166 65 -630 393 -100 197 -964 99 -400 163 -230 129 -264 97 -100 333 -332 165 -100 67 -66 199 -200 99 -166 133 -992 99 -66 163 -1590 65 -134 65 -366 331 -68 131 -1692 197 -166 99 -132 99 -100 429 -134 65 -100 367 -164 65 -166 329 -166 131 -234 99 -564 99 -100 99 -66 99 -100 263 -164 201 -436 131 -100 133 -268 231 -232 99 -366 65 -498 131 -100 131 -300 265 -432 199 -132 67 -100 163 -300 331 -100 263 -296 129 -68 97 -100 165 -234 99 -234 101 -66 67 -100 97 -1328 97 -264 99 -3316 261 -134 65 -300 65 -102 199 -98 99 -100 231 -1424 165 -164 195 -130 99 -396 129 -598 199 -264 97 -566 65 -762 99 -132 197 -464 131 -66 65 -494 97 -66 229 -66 525 -428 65 -428 229 -920 459 -556 97 -300 199 -100 99 -466 67 -1362 233 -430 131 -132 131 -294 197 -130 163 -98 163 -132 131 -1020 165 -132 165 -98 97 -232 329 -132 165 -130 129 -132 193 -592 197 -130 97 -2076 133 -164 163 -162 97 -98 63 -760 131 -100 -RAW_Data: 659 -560 97 -98 165 -66 65 -396 163 -662 97 -230 133 -598 97 -796 399 -132 165 -132 133 -66 197 -66 131 -398 297 -232 231 -364 199 -430 65 -100 165 -1096 231 -98 165 -98 197 -560 297 -132 65 -1260 429 -824 97 -68 97 -664 199 -296 495 -630 263 -966 99 -234 167 -66 65 -264 231 -68 197 -68 367 -66 163 -132 163 -430 233 -100 399 -134 167 -98 165 -98 99 -132 231 -230 193 -98 161 -396 261 -494 163 -1290 265 -664 65 -66 461 -100 563 -66 431 -100 1087 -198 165 -166 165 -100 899 -102 169 -200 99 -200 265 -66 231 -68 165 -100 133 -396 231 -366 99 -466 65 -234 497 -66 133 -200 133 -100 99 -100 65 -398 199 -264 101 -132 201 -366 195 -228 229 -134 625 -66 163 -66 293 -164 523 -100 97 -196 163 -396 229 -330 99 -98 65 -100 359 -562 195 -98 131 -98 229 -132 63 -822 65 -100 99 -432 229 -100 261 -66 327 -100 325 -198 2007 -132 261 -98 65 -100 397 -164 133 -66 99 -332 65 -98 65 -696 99 -298 231 -260 97 -296 199 -98 229 -396 163 -398 65 -398 197 -100 363 -66 797 -66 199 -100 1259 -132 331 -330 265 -800 197 -200 97 -166 99 -730 133 -432 99 -168 265 -1060 165 -96 129 -164 163 -66 97 -64 295 -68 397 -100 1091 -98 533 -364 261 -64 163 -920 99 -98 65 -164 195 -462 99 -98 97 -198 97 -100 261 -230 131 -594 263 -100 163 -196 129 -328 97 -98 297 -66 129 -66 131 -66 229 -228 461 -100 1127 -64 359 -1052 231 -298 131 -232 163 -428 65 -230 261 -66 97 -366 131 -68 197 -332 231 -460 229 -262 461 -166 629 -200 367 -198 1745 -528 65 -266 67 -166 131 -564 165 -134 135 -1358 197 -198 229 -98 165 -690 131 -196 197 -66 261 -164 263 -98 63 -132 65 -100 229 -100 197 -100 299 -66 465 -66 165 -68 165 -432 131 -530 63 -330 65 -198 97 -326 97 -66 65 -132 195 -498 65 -98 165 -260 95 -264 295 -130 131 -98 197 -526 65 -896 65 -100 429 -98 163 -132 527 -132 1189 -330 299 -166 165 -232 97 -66 99 -198 67 -168 133 -100 167 -200 165 -100 101 -630 199 -596 199 -1354 99 -198 163 -64 227 -132 163 -98 163 -132 425 -100 461 -262 259 -130 195 -98 65 -130 97 -166 129 -66 165 -98 97 -658 99 -1374 163 -132 165 -328 195 -98 99 -496 63 -132 67 -98 197 -130 99 -66 129 -98 1147 -66 1219 -1120 165 -166 263 -66 65 -100 197 -130 99 -164 99 -100 99 -262 199 -332 97 -66 233 -760 65 -464 363 -696 131 -68 97 -68 -RAW_Data: 329 -164 197 -302 429 -296 229 -232 99 -364 97 -98 131 -98 293 -164 133 -200 165 -270 233 -366 131 -400 399 -100 99 -464 133 -66 299 -198 297 -100 199 -66 365 -66 563 -464 425 -166 365 -166 101 -298 165 -166 99 -300 331 -264 329 -298 65 -200 99 -66 165 -1290 129 -594 99 -66 197 -262 197 -164 163 -100 229 -98 163 -96 1709 -100 65 -66 363 -132 99 -824 361 -1388 165 -98 99 -1952 99 -398 263 -200 201 -300 231 -134 265 -166 133 -66 399 -98 233 -232 461 -66 99 -132 165 -100 65 -298 135 -498 99 -394 297 -296 67 -66 165 -432 165 -98 97 -66 265 -66 563 -98 199 -66 265 -232 597 -100 691 -98 631 -132 99 -632 199 -730 97 -132 99 -528 361 -492 131 -296 65 -460 65 -66 97 -164 99 -262 263 -66 97 -98 263 -64 491 -66 851 -100 161 -132 97 -926 99 -100 99 -98 97 -632 163 -326 231 -290 99 -132 199 -66 199 -166 297 -68 167 -564 1059 -1018 1009 -1016 969 -1042 987 -1008 1005 -988 1013 -1032 483 -550 483 -550 225 -282 517 -522 261 -294 499 -560 495 -554 231 -298 269 -266 233 -298 495 -524 521 -520 263 -290 263 -268 271 -308 241 -274 507 -524 259 -260 521 -522 521 -520 523 -518 523 -518 527 -552 231 -298 265 -266 497 -522 263 -258 521 -524 261 -294 499 -558 233 -298 269 -274 271 -270 499 -520 261 -290 229 -296 267 -268 505 -560 233 -298 271 -272 507 -524 487 -550 487 -550 259 -288 225 -298 269 -306 239 -308 241 -276 507 -522 261 -258 259 -294 265 -268 269 -270 271 -304 239 -308 241 -274 273 -270 499 -520 489 -550 259 -288 229 -298 503 -558 231 -334 237 -274 997 -1008 1003 -984 1003 -1034 977 -1006 1013 -986 999 -1046 513 -514 511 -514 257 -280 513 -520 261 -292 497 -556 497 -554 233 -298 267 -268 233 -298 495 -522 527 -518 261 -290 263 -266 273 -306 243 -274 507 -524 261 -258 521 -522 523 -518 523 -516 523 -552 493 -552 231 -298 267 -266 497 -522 263 -256 523 -522 263 -294 497 -558 231 -300 269 -274 271 -272 497 -522 259 -290 227 -298 267 -270 505 -560 231 -300 271 -272 507 -522 521 -518 489 -548 259 -288 225 -298 269 -304 241 -308 241 -276 505 -524 259 -258 259 -294 265 -268 269 -270 271 -306 239 -308 241 -274 271 -270 499 -520 519 -518 261 -288 229 -298 503 -560 231 -300 269 -272 999 -1010 1013 -982 1007 -1018 973 -1016 999 -1010 987 -1044 481 -554 483 -520 273 -292 473 -552 245 -324 487 -532 501 -558 267 -268 269 -268 231 -298 495 -522 523 -520 259 -292 -RAW_Data: 263 -268 271 -308 241 -276 507 -522 261 -256 523 -522 523 -518 521 -518 525 -518 525 -554 229 -298 267 -268 495 -524 261 -258 521 -522 263 -294 499 -556 231 -300 271 -272 271 -270 499 -522 261 -290 227 -296 269 -270 505 -556 233 -298 271 -274 505 -524 521 -516 487 -548 259 -288 227 -298 271 -304 241 -308 241 -274 507 -524 259 -258 259 -294 265 -268 267 -270 271 -272 273 -308 241 -276 271 -270 499 -520 489 -550 259 -288 261 -264 505 -558 233 -300 269 -274 997 -1008 1003 -986 1005 -1002 1011 -1010 987 -1006 997 -1044 479 -546 513 -512 259 -280 511 -518 263 -292 497 -556 497 -556 231 -300 267 -266 233 -298 497 -522 523 -518 263 -290 263 -268 273 -306 241 -276 507 -522 261 -258 521 -522 523 -518 521 -518 525 -518 525 -554 231 -298 265 -266 499 -522 261 -258 521 -522 263 -294 499 -556 231 -300 271 -272 273 -270 499 -520 261 -288 229 -296 267 -270 505 -560 231 -300 271 -274 505 -524 489 -548 487 -548 261 -286 225 -298 271 -306 239 -308 241 -274 507 -524 259 -258 257 -296 267 -266 269 -270 271 -272 273 -308 239 -276 273 -268 497 -520 491 -550 261 -288 227 -296 505 -560 233 -298 269 -274 997 -1012 1009 -988 1007 -996 1009 -1008 1003 -982 999 -1044 513 -512 511 -514 257 -282 511 -520 261 -294 497 -556 497 -556 231 -300 265 -268 233 -298 495 -522 525 -518 263 -290 263 -268 271 -306 243 -274 509 -522 261 -256 523 -522 523 -518 491 -548 523 -552 493 -552 231 -298 265 -268 497 -520 261 -290 493 -522 263 -292 497 -558 231 -300 271 -272 271 -270 501 -520 261 -290 227 -296 267 -270 505 -558 233 -298 271 -274 507 -522 489 -550 487 -550 227 -318 225 -298 269 -304 241 -308 239 -276 507 -522 261 -258 259 -296 265 -266 269 -270 271 -304 239 -308 241 -274 273 -270 497 -520 491 -550 259 -288 227 -298 505 -558 233 -330 237 -274 999 -1012 979 -1016 1005 -1018 977 -1016 983 -1038 977 -1036 475 -550 509 -514 257 -276 503 -548 259 -288 491 -556 497 -556 231 -298 265 -268 233 -298 497 -522 491 -550 261 -292 263 -300 237 -308 241 -276 505 -524 261 -290 491 -522 521 -518 523 -516 525 -550 495 -554 231 -296 231 -300 497 -522 261 -290 491 -522 261 -294 497 -558 231 -334 237 -274 271 -270 499 -520 261 -290 229 -298 267 -268 505 -558 231 -300 269 -274 505 -524 489 -550 487 -548 259 -288 227 -298 269 -306 239 -308 241 -274 509 -524 259 -256 259 -296 265 -268 269 -268 271 -304 239 -308 241 -276 271 -270 497 -522 489 -550 -RAW_Data: 261 -288 227 -296 505 -558 231 -300 271 -272 1001 -1012 977 -1026 1007 -992 1011 -1008 1001 -1002 1001 -1014 513 -512 513 -514 257 -282 513 -520 263 -292 497 -556 497 -556 231 -300 267 -268 231 -298 495 -522 525 -518 261 -292 263 -266 271 -308 241 -276 507 -522 261 -288 491 -522 523 -518 523 -516 523 -520 525 -554 231 -296 265 -268 497 -522 259 -290 491 -522 263 -294 499 -556 265 -266 271 -274 271 -270 499 -520 261 -290 227 -298 267 -270 505 -558 233 -300 269 -274 505 -524 519 -516 487 -550 259 -286 259 -266 269 -306 241 -308 239 -276 507 -522 261 -256 259 -296 267 -266 269 -270 269 -272 273 -308 241 -276 271 -270 497 -520 521 -518 259 -290 259 -264 503 -560 265 -266 271 -274 999 -1014 977 -1016 1007 -984 1009 -1014 999 -1010 985 -1044 483 -520 519 -522 253 -276 529 -512 257 -282 517 -556 497 -554 231 -298 267 -268 233 -298 497 -522 521 -518 261 -290 265 -268 273 -308 241 -276 505 -524 259 -258 521 -520 525 -518 521 -518 525 -518 525 -552 231 -296 267 -268 497 -522 261 -256 523 -522 263 -294 499 -560 231 -298 269 -274 271 -270 497 -522 259 -292 227 -298 267 -268 505 -558 233 -300 271 -272 507 -522 521 -516 487 -550 259 -286 227 -298 271 -304 239 -308 241 -276 507 -520 261 -256 259 -296 265 -268 269 -268 273 -272 273 -306 241 -276 273 -268 497 -520 521 -516 261 -290 227 -298 505 -556 265 -266 271 -274 999 -1008 1005 -986 1005 -1000 1007 -1008 1001 -996 1009 -1016 515 -514 513 -514 259 -282 515 -520 263 -292 497 -556 499 -554 231 -300 265 -268 233 -298 497 -522 523 -518 261 -290 263 -302 239 -308 241 -276 505 -524 259 -258 521 -522 523 -518 523 -518 523 -518 525 -552 231 -298 265 -268 497 -524 259 -258 521 -524 261 -294 499 -556 233 -298 271 -272 273 -270 497 -522 259 -290 229 -296 267 -270 505 -558 231 -124104 99 -334 97 -794 165 -232 99 -136 595 -260 65 -66 163 -100 1353 -100 231 -134 233 -100 231 -962 99 -432 131 -134 231 -234 133 -98 99 -430 65 -100 99 -396 99 -1228 197 -66 97 -200 299 -100 265 -66 99 -262 99 -64 1447 -66 163 -98 65 -562 197 -66 129 -100 197 -164 65 -262 65 -264 65 -164 65 -1490 99 -66 99 -66 65 -166 197 -134 99 -66 131 -100 99 -66 97 -66 231 -266 465 -66 299 -66 461 -98 295 -132 65 -528 229 -164 129 -396 65 -962 133 -566 99 -198 163 -830 197 -100 201 -400 67 -66 99 -132 299 -66 463 -168 231 -66 433 -66 895 -956 65 -828 229 -468 -RAW_Data: 197 -100 131 -130 97 -132 295 -66 97 -430 133 -198 65 -526 727 -66 229 -262 163 -98 525 -66 853 -132 165 -66 399 -166 99 -268 397 -134 233 -66 67 -298 131 -1130 133 -100 65 -134 297 -198 329 -134 197 -66 129 -590 133 -164 131 -132 131 -132 131 -98 2973 -230 65 -664 165 -134 165 -432 99 -166 165 -496 201 -132 99 -100 197 -68 131 -632 67 -98 133 -132 695 -100 293 -100 131 -98 199 -66 297 -100 199 -100 335 -100 265 -430 165 -66 297 -664 99 -264 199 -98 165 -134 167 -200 99 -100 65 -98 99 -296 65 -164 163 -494 99 -100 267 -198 99 -426 199 -134 99 -232 131 -330 65 -134 131 -134 201 -100 463 -232 97 -296 1087 -100 363 -134 97 -132 197 -300 131 -234 233 -990 133 -232 65 -266 65 -264 199 -200 265 -396 65 -134 165 -66 165 -66 65 -330 231 -300 431 -198 331 -266 99 -232 131 -432 65 -266 131 -100 265 -66 599 -498 97 -98 65 -298 197 -98 227 -328 231 -232 163 -660 197 -132 65 -134 363 -66 197 -166 631 -66 361 -132 231 -132 427 -328 99 -898 199 -332 365 -98 199 -162 197 -230 97 -166 561 -198 263 -196 295 -100 65 -428 65 -98 99 -166 199 -132 131 -100 165 -98 131 -66 1161 -66 167 -166 529 -364 163 -460 231 -196 291 -296 65 -66 97 -398 65 -100 133 -658 165 -164 195 -230 99 -132 229 -98 129 -430 691 -68 165 -100 167 -66 435 -100 597 -66 331 -98 99 -298 263 -262 131 -492 129 -788 97 -428 133 -100 65 -100 65 -98 131 -998 199 -98 235 -100 965 -232 793 -98 861 -100 199 -68 529 -398 195 -960 397 -100 131 -266 201 -66 65 -98 99 -132 233 -168 261 -66 131 -428 165 -66 261 -528 65 -66 195 -166 261 -66 361 -164 525 -200 697 -364 97 -66 131 -558 163 -266 65 -298 331 -464 199 -100 165 -100 97 -66 65 -994 331 -166 131 -534 199 -300 67 -98 329 -266 163 -66 227 -66 133 -66 261 -100 559 -466 231 -260 197 -68 227 -98 97 -96 65 -296 163 -296 131 -164 195 -230 263 -66 393 -230 457 -134 131 -534 133 -630 99 -98 399 -100 265 -266 431 -134 67 -100 789 -1584 165 -624 131 -134 263 -396 99 -692 165 -98 165 -398 295 -66 99 -592 163 -132 131 -328 265 -66 231 -100 363 -98 335 -66 167 -98 499 -100 397 -68 131 -100 231 -100 165 -134 165 -1228 99 -66 233 -1386 63 -924 99 -130 131 -392 231 -166 133 -166 165 -198 501 -66 265 -66 299 -98 299 -100 299 -134 231 -68 165 -232 99 -66 65 -200 165 -100 -RAW_Data: 67 -132 199 -302 65 -1058 199 -798 133 -334 165 -894 65 -132 233 -164 329 -130 493 -66 893 -200 631 -100 1207 -984 1013 -1010 1017 -988 997 -1010 1013 -986 1007 -1032 257 -254 253 -292 265 -266 501 -524 525 -520 261 -294 265 -306 241 -276 507 -522 261 -256 521 -524 521 -518 261 -290 495 -558 497 -556 265 -264 267 -268 265 -264 497 -522 261 -292 261 -268 269 -274 273 -274 275 -276 273 -270 231 -296 265 -264 497 -522 527 -520 261 -294 267 -304 241 -276 273 -270 495 -520 261 -256 293 -264 269 -270 507 -556 265 -266 271 -274 507 -522 521 -516 517 -516 259 -256 291 -266 271 -272 273 -274 275 -276 507 -522 259 -256 259 -296 265 -268 269 -270 271 -270 273 -274 275 -276 507 -522 521 -514 519 -516 259 -254 291 -266 269 -274 511 -558 267 -266 991 -1010 1009 -1000 999 -974 1023 -1012 1009 -990 1007 -1032 225 -284 253 -292 263 -264 501 -524 525 -518 263 -296 265 -306 241 -276 507 -522 261 -256 521 -520 523 -518 261 -290 497 -556 499 -554 265 -266 1535 -556 1321 -340 201 -168 1009 -246 209 -304 1249 -320 489 -196 1129 -338 209 -316 1011 -562 197 -260 539 -66 219 -194 815 -260 515 -132 247 -144 825 -260 815 -272 1301 -262 519 -66 215 -230 533 -132 217 -178 1049 -134 213 -178 347 -208 507 -134 247 -142 511 -192 213 -94 519 -274 521 -278 793 -278 815 -278 521 -132 229 -154 503 -198 839 -228 1597 -262 259 -292 1553 -526 1593 -458 1103 -528 1345 -268 235 -298 1293 -260 523 -194 189 -122 525 -196 221 -94 805 -306 1041 -166 233 -144 493 -228 221 -72 1041 -238 253 -342 791 -314 375 -162 823 -256 1019 -166 213 -154 507 -194 213 -124 815 -266 505 -196 221 -122 407 -166 391 -134 983 -262 219 -312 1213 -360 479 -166 1191 -322 223 -262 1043 -560 229 -166 1035 -206 455 -330 1007 -332 469 -198 837 -196 251 -92 471 -264 519 -330 439 -100 419 -132 421 -132 439 -100 453 -230 549 -330 739 -332 769 -302 347 -210 1025 -98 285 -180 603 -416 619 -404 757 -340 305 -226 463 -134 267 -196 313 -192 1175 -512 1563 -522 1279 -288 253 -290 1553 -524 1579 -492 1075 -528 655 -306 343 -188 373 -164 647 -410 457 -170 615 -344 767 -308 469 -100 275 -178 361 -174 621 -416 625 -418 355 -204 593 -446 583 -466 289 -224 355 -202 327 -206 463 -168 287 -106 399 -134 753 -338 757 -326 283 -230 331 -204 1027 -66 277 -210 487 -100 1219 -324 249 -220 1093 -558 229 -132 995 -282 449 -264 1089 -304 475 -132 1017 -446 483 -68 1235 -338 211 -252 515 -102 243 -176 -RAW_Data: 759 -296 497 -132 213 -214 333 -202 329 -204 331 -240 301 -206 369 -170 595 -442 609 -406 605 -440 347 -194 515 -164 211 -154 1041 -198 193 -180 1503 -516 1361 -258 291 -232 1083 -540 1557 -488 1303 -294 253 -288 1549 -524 393 -172 343 -174 533 -132 617 -264 629 -444 479 -200 217 -144 421 -106 1049 -174 243 -140 1025 -172 855 -166 229 -122 801 -314 1045 -134 239 -178 493 -164 217 -124 1047 -164 221 -122 525 -228 225 -100 457 -66 1041 -226 833 -196 1891 -298 523 -302 411 -100 1011 -290 477 -238 1123 -298 491 -66 1033 -478 1797 -320 225 -162 1001 -242 455 -300 1035 -296 231 -332 767 -302 345 -188 369 -194 567 -452 473 -172 233 -160 471 -208 279 -70 483 -342 605 -432 1511 -172 501 -352 1255 -526 1545 -514 1329 -298 263 -266 1063 -538 1553 -488 615 -310 385 -170 391 -136 621 -404 647 -410 389 -160 405 -132 997 -134 279 -178 987 -134 893 -166 241 -154 409 -130 667 -384 477 -200 613 -308 747 -306 483 -134 283 -146 377 -156 779 -298 795 -304 311 -188 797 -302 505 -502 815 -268 301 -240 305 -240 305 -242 271 -236 561 -470 777 -292 283 -260 1285 -298 267 -274 1573 -520 1483 -70 237 -322 219 -126 991 -314 449 -264 1045 -330 235 -304 1307 -340 209 -244 985 -100 475 -430 603 -426 347 -188 375 -176 513 -168 605 -288 367 -170 1365 -268 1527 -516 1471 -138 497 -386 1233 -486 1571 -514 1339 -262 291 -264 303 -244 821 -208 555 -496 543 -464 545 -98 249 -142 417 -140 1043 -140 241 -166 597 -424 813 -280 519 -100 675 -268 601 -442 515 -132 251 -144 367 -168 1053 -132 215 -178 387 -140 513 -206 211 -108 513 -238 1305 -188 867 -190 213 -94 1043 -280 1013 -276 2453 -282 1009 -262 787 -260 515 -260 1055 -232 225 -62 471 -100 489 -292 491 -196 1095 -298 233 -302 1329 -340 245 -176 1017 -136 459 -356 1043 -198 235 -320 1559 -550 1509 -206 253 -284 1277 -296 231 -298 1365 -276 1527 -528 1473 -100 505 -416 1195 -524 553 -302 739 -334 469 -196 251 -92 499 -234 263 -310 677 -324 1031 -264 165 -132 471 -568 201 -338 473 -558 499 -554 231 -300 233 -302 231 -300 497 -522 261 -292 229 -298 271 -306 239 -306 241 -274 273 -270 233 -298 231 -298 497 -556 493 -518 263 -294 265 -304 241 -276 273 -270 497 -520 261 -290 225 -298 267 -270 505 -558 233 -298 271 -274 505 -522 491 -550 487 -546 227 -286 259 -296 301 -114218 231 -100 97 -402 397 -298 99 -100 363 -164 201 -132 699 -100 1193 -100 199 -100 167 -66 429 -200 99 -1060 99 -400 99 -332 65 -168 -RAW_Data: 199 -232 199 -100 99 -630 67 -962 231 -198 65 -366 427 -100 231 -102 299 -100 299 -66 593 -132 63 -98 457 -426 165 -724 261 -98 163 -164 263 -98 99 -230 195 -862 65 -132 133 -498 131 -266 65 -66 197 -298 663 -298 829 -132 299 -460 197 -930 99 -66 399 -132 99 -960 165 -134 231 -132 99 -66 395 -726 67 -132 99 -132 231 -132 597 -98 593 -66 133 -198 1131 -98 267 -298 97 -724 525 -196 623 -132 199 -98 295 -98 65 -98 97 -66 197 -264 497 -98 163 -134 233 -100 297 -198 131 -568 297 -100 99 -66 99 -98 465 -100 97 -100 199 -398 1051 -1046 951 -1046 967 -1018 1009 -1004 1003 -1012 993 -1046 237 -288 465 -554 245 -290 253 -286 247 -276 509 -558 497 -554 497 -522 523 -518 259 -254 519 -520 263 -292 263 -266 269 -306 239 -308 241 -274 273 -270 499 -520 487 -550 261 -288 493 -554 231 -296 267 -306 241 -276 271 -270 233 -298 495 -522 261 -294 493 -556 497 -554 495 -554 231 -296 265 -266 497 -524 261 -290 227 -298 267 -268 505 -558 231 -332 239 -272 507 -522 521 -518 487 -550 259 -286 227 -296 271 -304 241 -308 241 -274 507 -522 261 -258 259 -296 265 -266 269 -270 271 -272 271 -308 241 -274 507 -522 489 -548 259 -256 259 -296 499 -558 229 -298 269 -306 241 -274 999 -1012 981 -1026 975 -1024 1011 -980 995 -1012 1019 -1012 261 -274 501 -514 259 -282 253 -292 265 -268 507 -556 497 -556 497 -522 523 -520 227 -286 519 -520 261 -292 261 -266 271 -308 239 -306 241 -274 273 -270 497 -520 489 -550 261 -288 493 -556 231 -294 267 -304 241 -274 273 -272 233 -298 495 -520 261 -292 495 -558 493 -556 493 -552 231 -298 265 -268 499 -522 261 -290 227 -296 267 -270 505 -560 231 -298 269 -274 505 -524 489 -552 487 -550 227 -318 225 -298 269 -304 241 -306 241 -276 507 -522 261 -256 259 -296 265 -268 269 -270 271 -304 239 -308 241 -274 507 -524 487 -550 259 -254 259 -296 499 -558 231 -298 267 -304 241 -276 999 -1014 979 -1016 1005 -988 1007 -1014 1001 -1014 993 -1012 273 -288 465 -552 247 -290 251 -286 247 -278 507 -556 495 -556 497 -522 527 -518 227 -286 517 -522 261 -292 229 -300 269 -304 241 -306 241 -276 273 -270 497 -520 487 -550 261 -290 491 -556 231 -296 267 -304 241 -274 273 -270 233 -298 495 -522 261 -292 495 -556 497 -552 495 -554 231 -296 265 -268 499 -522 259 -292 227 -296 267 -268 505 -560 231 -300 271 -274 505 -524 489 -550 485 -550 227 -318 225 -298 269 -304 241 -308 241 -274 -RAW_Data: 507 -522 261 -258 259 -296 231 -302 267 -270 271 -304 239 -308 241 -276 505 -524 487 -550 227 -288 257 -296 499 -558 229 -298 269 -304 241 -276 997 -1012 981 -1026 977 -1026 977 -1014 1001 -1014 993 -1046 235 -288 495 -528 239 -326 219 -290 253 -288 485 -564 501 -556 497 -522 493 -552 227 -286 515 -522 261 -292 231 -300 269 -304 239 -308 241 -274 273 -270 499 -520 489 -550 259 -290 489 -556 231 -296 267 -306 239 -276 273 -270 233 -298 497 -520 261 -292 493 -558 495 -554 495 -552 231 -298 233 -300 497 -522 261 -290 227 -296 267 -270 505 -558 231 -334 237 -272 507 -522 491 -552 485 -552 227 -318 225 -296 269 -304 239 -308 241 -276 505 -524 261 -290 227 -294 231 -302 269 -268 271 -304 239 -308 241 -276 507 -524 487 -552 227 -286 259 -294 501 -556 229 -298 269 -306 241 -276 997 -1010 1003 -986 1007 -998 1009 -1008 1001 -986 1001 -1048 227 -276 503 -550 259 -254 257 -298 267 -270 505 -558 497 -554 497 -524 521 -518 229 -286 517 -522 263 -290 263 -266 269 -306 241 -306 241 -274 273 -270 497 -520 521 -516 259 -290 493 -556 231 -296 269 -304 241 -274 273 -270 233 -298 495 -524 261 -290 493 -558 495 -552 497 -552 231 -298 265 -266 497 -522 259 -292 227 -298 267 -270 505 -558 231 -300 269 -274 505 -524 489 -550 487 -550 229 -286 257 -296 269 -304 241 -308 241 -276 507 -520 261 -258 259 -294 267 -266 269 -270 269 -306 239 -306 241 -276 507 -524 489 -548 259 -256 257 -294 501 -558 231 -296 269 -306 239 -276 999 -1012 979 -1028 975 -1024 1007 -1000 1001 -1000 1007 -1012 259 -280 507 -518 259 -290 225 -298 267 -270 503 -558 497 -554 497 -524 525 -518 227 -286 517 -522 261 -292 263 -266 269 -306 239 -308 241 -276 271 -270 499 -520 487 -550 259 -288 493 -556 229 -298 267 -304 241 -274 273 -270 235 -298 495 -520 261 -292 495 -556 495 -556 493 -554 231 -298 233 -300 497 -520 261 -290 227 -296 267 -270 505 -558 233 -298 271 -272 507 -522 491 -550 485 -550 227 -318 227 -296 271 -304 239 -308 241 -276 507 -522 261 -258 259 -294 265 -268 267 -270 271 -304 239 -308 241 -274 507 -524 489 -550 227 -286 257 -296 499 -558 231 -298 267 -304 241 -274 999 -1010 1001 -1004 983 -1004 1011 -1008 1003 -998 1003 -1018 257 -282 509 -518 259 -290 225 -298 267 -270 503 -560 497 -558 497 -520 521 -520 227 -286 517 -522 259 -292 263 -266 271 -304 239 -308 241 -276 273 -268 499 -522 487 -550 259 -288 491 -556 229 -298 267 -304 241 -274 -RAW_Data: 273 -272 233 -296 495 -522 261 -294 493 -556 493 -554 495 -556 229 -298 265 -266 499 -520 261 -290 227 -296 267 -270 505 -558 233 -298 271 -272 507 -524 489 -548 489 -550 227 -316 227 -296 269 -306 239 -308 241 -274 507 -524 259 -258 259 -294 265 -266 269 -270 271 -304 239 -308 239 -276 507 -524 489 -550 227 -286 257 -294 501 -556 231 -296 269 -306 239 -276 999 -1012 981 -1018 1005 -980 1009 -1018 1003 -1008 977 -1038 255 -276 499 -516 259 -282 253 -290 265 -268 505 -556 499 -554 497 -520 525 -518 227 -288 517 -522 261 -292 229 -300 269 -304 239 -308 241 -276 273 -268 497 -522 489 -550 261 -288 489 -556 229 -296 267 -304 241 -276 271 -270 233 -300 495 -522 261 -292 495 -556 495 -552 495 -554 229 -298 231 -300 499 -522 261 -290 227 -296 267 -268 507 -558 231 -298 271 -272 505 -524 489 -552 487 -550 227 -318 225 -296 267 -304 239 -308 241 -274 507 -524 261 -290 227 -296 231 -300 269 -270 269 -306 239 -306 241 -274 505 -524 489 -550 227 -320 225 -296 499 -556 231 -298 267 -306 241 -274 999 -1012 979 -1016 1005 -982 1009 -1014 1003 -1010 979 -1044 237 -292 473 -552 247 -284 245 -276 277 -276 501 -546 515 -548 485 -550 487 -548 225 -286 515 -520 263 -290 263 -266 269 -306 239 -306 243 -274 273 -270 495 -520 491 -548 261 -288 491 -554 231 -296 267 -304 241 -276 273 -270 233 -298 495 -522 261 -290 495 -556 495 -520 527 -554 229 -298 267 -268 497 -522 259 -258 259 -296 269 -270 505 -558 231 -300 269 -274 507 -522 521 -514 489 -548 259 -256 289 -264 271 -272 273 -308 241 -276 505 -522 259 -258 257 -296 267 -266 271 -270 269 -272 273 -306 241 -274 507 -522 521 -516 259 -254 259 -296 501 -556 231 -296 269 -306 241 -276 1001 -1010 979 -1016 1003 -986 1009 -1014 1001 -1006 1011 -1006 253 -276 497 -516 259 -282 255 -292 267 -270 507 -556 497 -554 499 -522 523 -518 259 -254 517 -522 261 -292 265 -266 269 -272 273 -308 243 -274 273 -268 497 -522 521 -516 259 -288 491 -554 231 -298 267 -306 241 -276 273 -270 231 -298 493 -522 261 -292 495 -556 495 -520 527 -554 231 -296 267 -268 497 -520 261 -290 229 -296 267 -270 505 -558 233 -298 271 -272 507 -524 489 -550 487 -548 259 -252 291 -264 271 -272 273 -308 241 -274 507 -520 261 -258 259 -296 265 -266 269 -270 271 -270 273 -308 241 -276 505 -524 487 -548 259 -256 255 -298 501 -556 231 -298 267 -306 241 -276 999 -1004 1007 -988 1009 -998 1011 -1012 991 -982 1025 -1010 -RAW_Data: 269 -290 465 -554 247 -290 251 -286 247 -278 505 -556 497 -554 497 -522 525 -516 227 -286 519 -522 261 -292 261 -266 271 -304 239 -308 241 -276 273 -270 497 -520 489 -550 259 -256 521 -556 231 -296 269 -306 241 -274 273 -270 233 -296 495 -522 261 -292 495 -556 495 -520 527 -554 231 -296 267 -266 499 -518 261 -290 229 -298 267 -268 507 -556 233 -298 271 -272 507 -526 487 -550 485 -548 259 -254 291 -264 271 -272 275 -308 241 -274 505 -522 259 -258 259 -296 265 -268 267 -270 271 -272 273 -274 273 -276 507 -520 521 -516 259 -254 291 -262 501 -556 231 -298 269 -306 241 -276 1001 -1010 979 -1018 1007 -982 1009 -1016 999 -984 1019 -1008 257 -274 499 -548 257 -254 257 -296 267 -270 505 -558 497 -554 499 -520 523 -516 259 -256 517 -524 261 -290 263 -266 271 -270 275 -274 275 -274 273 -268 499 -518 519 -516 261 -288 493 -524 263 -296 269 -270 275 -276 271 -270 233 -298 495 -518 263 -292 493 -524 529 -522 527 -522 265 -294 265 -266 497 -520 261 -258 293 -264 267 -270 507 -556 267 -264 271 -274 507 -522 523 -516 485 -548 259 -254 291 -264 271 -272 275 -274 275 -276 507 -520 259 -256 257 -296 265 -268 271 -270 271 -272 273 -272 275 -276 505 -520 523 -514 257 -256 289 -264 503 -524 263 -296 269 -272 275 -276 999 -1004 1001 -998 1019 -972 1007 -1006 1001 -996 1009 -1020 257 -280 511 -520 259 -256 293 -262 269 -272 505 -556 497 -554 497 -522 523 -518 259 -252 517 -522 261 -292 263 -266 273 -272 275 -272 277 -274 271 -236 529 -522 521 -514 261 -256 523 -522 297 -264 269 -270 275 -276 273 -236 265 -264 529 -520 261 -290 497 -522 527 -520 529 -552 265 -264 267 -234 531 -520 259 -258 291 -264 269 -272 507 -554 265 -266 271 -274 507 -522 519 -514 521 -514 259 -256 291 -266 271 -272 275 -274 275 -274 507 -522 259 -256 289 -264 265 -270 269 -270 269 -272 273 -274 275 -276 505 -522 519 -514 259 -254 291 -264 501 -522 299 -264 269 -272 277 -276 999 -978 1045 -958 1011 -1024 1007 -978 1019 -978 1013 -1008 271 -292 503 -490 283 -288 249 -278 273 -268 501 -522 529 -520 531 -520 525 -516 257 -254 519 -520 263 -292 265 -266 271 -274 273 -274 275 -276 269 -236 529 -520 521 -516 259 -256 523 -522 297 -264 269 -272 277 -274 273 -236 265 -262 527 -522 259 -292 495 -524 529 -520 529 -518 297 -264 267 -234 531 -520 259 -256 293 -264 269 -270 505 -556 267 -264 271 -274 507 -520 521 -518 517 -514 259 -256 293 -266 271 -270 -RAW_Data: 275 -306 241 -276 507 -520 259 -258 257 -296 267 -266 269 -268 273 -272 273 -274 275 -274 505 -524 521 -514 259 -254 289 -264 501 -556 231 -298 269 -270 277 -276 997 -980 1011 -996 1009 -990 1045 -980 999 -1010 1015 -1008 271 -256 501 -536 251 -272 245 -276 277 -286 499 -558 499 -554 497 -522 523 -516 259 -254 517 -522 261 -292 265 -266 271 -272 273 -274 275 -274 271 -236 531 -520 521 -516 259 -256 523 -524 263 -296 269 -270 277 -276 273 -236 265 -262 527 -520 263 -292 495 -556 497 -518 527 -552 263 -266 267 -268 497 -520 261 -258 291 -264 269 -268 507 -554 267 -264 271 -276 507 -522 521 -514 519 -516 259 -254 293 -264 271 -272 273 -274 275 -274 507 -522 259 -256 291 -262 265 -268 271 -270 271 -272 273 -272 275 -276 505 -520 521 -516 259 -254 289 -264 501 -526 263 -298 267 -272 275 -276 999 -1004 1003 -986 1007 -1000 1013 -1010 985 -1014 1015 -1006 275 -254 501 -524 283 -256 251 -284 275 -272 501 -554 499 -554 497 -520 525 -516 259 -254 517 -520 261 -292 263 -268 271 -274 273 -274 275 -276 271 -236 529 -520 521 -516 259 -256 523 -524 295 -264 269 -272 277 -274 273 -270 231 -264 527 -522 263 -258 527 -522 527 -520 529 -552 265 -266 265 -268 497 -520 259 -258 293 -262 269 -272 507 -556 265 -264 271 -276 507 -520 521 -516 519 -514 259 -254 293 -264 271 -272 277 -274 275 -274 507 -522 259 -256 257 -294 267 -268 269 -270 271 -272 273 -274 275 -274 507 -522 519 -516 259 -254 289 -264 503 -522 263 -298 267 -272 277 -276 999 -1008 1009 -984 1009 -984 1005 -1012 999 -1012 1013 -1004 255 -274 499 -544 257 -252 289 -262 269 -270 507 -556 497 -552 497 -520 525 -518 259 -254 519 -520 261 -294 263 -266 271 -272 275 -274 275 -274 273 -234 531 -520 519 -516 261 -256 523 -522 263 -298 269 -272 275 -276 273 -270 231 -264 527 -522 261 -292 493 -524 359 -91592 331 -232 65 -2392 131 -496 329 -2040 231 -66 201 -200 133 -920 65 -232 97 -100 491 -100 195 -198 131 -196 295 -98 197 -66 655 -166 131 -428 99 -296 65 -330 163 -66 163 -98 97 -66 99 -1778 165 -66 195 -854 129 -494 297 -98 97 -66 197 -260 261 -262 165 -132 293 -66 423 -166 9931 -462 67 -762 99 -268 199 -862 65 -760 165 -232 131 -434 133 -1126 363 -132 165 -134 67 -332 97 -466 97 -66 265 -100 133 -100 231 -1590 65 -334 199 -98 67 -1592 201 -100 131 -66 165 -164 97 -1714 327 -66 329 -98 595 -100 993 -132 65 -100 165 -232 65 -100 -RAW_Data: 97 -100 293 -330 99 -98 163 -458 99 -196 131 -66 163 -426 167 -298 99 -462 99 -66 67 -462 229 -66 195 -100 757 -66 425 -66 427 -100 529 -134 265 -1430 131 -302 97 -66 97 -132 65 -66 229 -100 97 -1530 133 -264 563 -66 1165 -200 1025 -198 167 -1294 265 -132 197 -962 131 -300 131 -66 99 -366 165 -198 167 -830 197 -230 195 -434 299 -98 965 -132 265 -894 99 -98 65 -860 461 -66 97 -398 67 -928 165 -200 199 -66 199 -1456 197 -334 133 -132 501 -198 1579 -66 1151 -132 463 -98 10275 -528 97 -966 263 -760 65 -264 197 -134 65 -512 479 -526 455 -572 443 -572 437 -550 451 -548 447 -552 481 -526 457 -542 483 -538 469 -548 449 -548 447 -554 449 -1048 975 -558 463 -548 451 -1046 449 -554 953 -1054 981 -540 443 -550 475 -1014 985 -1052 981 -516 475 -1048 473 -520 487 -534 463 -548 943 -1044 969 -554 449 -558 465 -1042 979 -520 485 -514 473 -548 453 -1042 973 -1048 981 -1014 483 -546 971 -522 473 -556 445 -1032 493 -51306 533 -514 457 -546 451 -550 477 -522 475 -542 471 -554 453 -538 467 -548 451 -546 447 -550 477 -524 473 -554 451 -540 469 -1044 977 -554 451 -546 441 -1042 483 -514 975 -1048 977 -522 493 -516 481 -1046 977 -1018 979 -552 453 -1042 481 -520 485 -516 471 -550 983 -1014 981 -546 477 -518 489 -1012 975 -554 451 -544 467 -550 451 -1042 975 -1050 949 -1046 483 -548 941 -554 483 -514 469 -1046 447 -51364 513 -518 451 -554 451 -544 469 -548 451 -546 481 -514 475 -522 475 -554 443 -572 443 -548 453 -546 479 -514 477 -554 447 -1050 975 -522 493 -512 483 -1040 471 -538 969 -1032 985 -534 467 -1046 977 -528 485 -1016 965 -1042 481 -524 489 -522 973 -524 493 -514 481 -1040 969 -1050 979 -1016 481 -514 475 -552 977 -522 479 -1028 493 -512 481 -546 975 -520 485 -514 473 -548 453 -544 481 -1016 981 -51834 519 -526 465 -514 481 -546 479 -514 473 -522 471 -558 487 -514 469 -548 451 -546 479 -514 475 -522 471 -538 471 -536 485 -1048 961 -546 445 -552 483 -1016 479 -536 965 -1042 977 -534 475 -1042 465 -548 979 -1014 485 -516 977 -1042 479 -514 1005 -1018 975 -1018 483 -544 975 -520 473 -1058 471 -516 975 -548 453 -546 479 -516 479 -522 471 -1052 985 -520 489 -1006 1005 -528 459 -1050 479 -518 489 -51332 483 -542 453 -546 445 -550 477 -522 483 -554 449 -554 461 -514 483 -548 445 -552 441 -588 447 -548 441 -550 455 -548 481 -1042 967 -540 441 -552 481 -1024 489 -514 975 -1034 971 -1050 973 -548 451 -548 447 -1048 485 -540 -RAW_Data: 453 -542 467 -548 945 -1048 981 -544 449 -1056 453 -546 481 -516 973 -554 449 -544 469 -1046 975 -526 487 -508 485 -1038 489 -512 483 -516 479 -522 1001 -508 483 -1036 983 -514 475 -51832 491 -574 411 -604 399 -618 389 -618 385 -622 383 -610 415 -578 411 -602 437 -552 455 -550 449 -554 445 -584 445 -546 441 -1046 981 -522 485 -546 447 -1054 451 -546 973 -1052 953 -1046 965 -1042 977 -556 451 -542 467 -514 483 -1040 969 -1050 479 -532 961 -546 445 -552 481 -520 485 -516 473 -548 453 -546 481 -516 481 -522 475 -1046 481 -532 961 -1044 481 -530 461 -554 449 -524 987 -1040 969 -1052 449 -51326 525 -532 465 -548 451 -548 445 -552 443 -586 431 -546 477 -524 491 -514 481 -546 445 -552 441 -554 485 -516 473 -548 487 -1010 977 -554 451 -540 469 -1042 475 -508 1007 -1034 967 -1042 477 -518 983 -516 479 -524 487 -536 473 -1018 481 -526 987 -540 479 -516 475 -524 485 -540 453 -544 469 -1044 975 -526 483 -1014 505 -518 981 -1010 473 -556 485 -514 469 -548 451 -546 971 -542 483 -520 485 -1010 493 -512 975 -51842 491 -606 339 -662 333 -652 357 -654 385 -628 357 -642 377 -604 403 -622 391 -584 417 -590 421 -614 409 -574 439 -554 421 -1114 909 -578 415 -578 449 -1094 423 -550 975 -1034 969 -1038 477 -522 493 -514 975 -554 483 -1012 967 -1048 977 -518 481 -1028 987 -510 479 -1034 1005 -520 485 -534 465 -1014 1007 -492 517 -1012 999 -514 483 -1016 487 -520 975 -1060 949 -1050 457 -550 479 -530 467 -51296 517 -552 451 -578 421 -570 447 -572 407 -592 427 -584 417 -586 417 -578 415 -576 445 -576 447 -546 447 -568 465 -550 453 -1046 977 -510 467 -542 479 -1050 449 -542 973 -1052 483 -518 973 -514 483 -546 447 -552 483 -520 471 -1036 471 -516 1011 -1012 487 -506 1017 -510 471 -548 451 -1042 475 -510 483 -542 487 -506 983 -1036 987 -1046 975 -1020 979 -516 483 -1042 973 -1028 997 -512 471 -550 487 -51298 65 -1910 229 -230 99 -66 165 -922 97 -164 197 -66 131 -426 65 -68 231 -264 95 -66 361 -196 1023 -66 227 -98 195 -66 229 -98 4711 -100 233 -98 99 -66 1025 -66 10443 -130 233 -168 465 -66 2057 -100 6537 -66 821 -132 1841 -98 165 -98 7675 -66 1343 -232 67 -132 131 -134 329 -100 365 -132 3561 -66 297 -134 1691 -100 861 -66 497 -66 1359 -66 197 -198 429 -66 929 -66 2675 -100 261 -98 99 -98 459 -166 197 -66 195 -130 625 -98 295 -132 455 -66 391 -130 197 -132 5171 -66 1597 -66 1085 -68 1025 -66 565 -100 131 -132 4741 -66 559 -66 2343 -98 -RAW_Data: 4573 -66 2455 -98 2333 -130 5301 -66 531 -66 1895 -66 163 -68 5167 -98 591 -100 1521 -166 7219 -100 2149 -100 1443 -98 1149 -66 9647 -66 5157 -100 1321 -164 917 -100 2083 -132 7551 -134 165 -68 1169 -100 1263 -68 3619 -166 963 -100 1229 -198 2393 -68 1661 -98 2109 -66 131 -68 299 -100 5029 -66 957 -100 1185 -66 1653 -98 4925 -66 1201 -66 1827 -96 163 -132 501 -66 165 -66 763 -100 163 -98 821 -66 4555 -264 1353 -132 499 -100 163 -66 1749 -100 1063 -132 1577 -100 763 -68 331 -102 299 -100 429 -66 265 -234 331 -66 1963 -68 627 -132 231 -102 763 -66 497 -66 265 -68 2061 -68 463 -66 297 -100 1393 -430 295 -100 823 -98 97 -100 631 -102 529 -66 1251 -68 1195 -66 361 -66 195 -100 4929 -134 231 -100 561 -66 829 -68 893 -66 6729 -66 901 -68 1259 -66 65 -166 131 -232 65 -100 131 -132 131 -100 131 -564 661 -98 329 -66 199 -66 263 -66 165 -68 97 -68 363 -566 197 -1064 395 -862 133 -200 99 -198 199 -596 229 -994 295 -928 163 -130 395 -98 229 -66 99 -98 997 -66 133 -566 165 -232 99 -100 99 -100 99 -530 99 -66 133 -66 165 -764 99 -398 199 -296 165 -96 129 -328 131 -560 227 -298 99 -100 465 -100 397 -66 67 -98 331 -132 199 -232 201 -438 263 -166 329 -166 463 -632 131 -398 99 -134 131 -1590 263 -66 67 -196 163 -298 195 -1220 227 -292 297 -132 591 -298 295 -100 493 -66 163 -162 325 -134 197 -64 197 -164 97 -460 65 -166 195 -266 97 -164 327 -264 229 -234 165 -66 163 -630 299 -532 197 -796 131 -100 65 -466 65 -98 99 -328 361 -66 197 -130 295 -66 197 -692 99 -166 99 -166 99 -66 131 -100 99 -66 165 -134 233 -130 267 -498 265 -66 167 -1192 67 -364 463 -132 133 -300 197 -100 99 -298 629 -66 331 -132 629 -134 65 -132 231 -100 65 -1392 65 -166 131 -230 199 -466 97 -100 233 -730 263 -296 99 -98 133 -100 163 -134 67 -100 131 -98 229 -100 231 -132 497 -264 989 -164 233 -98 65 -102 495 -264 197 -826 99 -496 133 -132 365 -64 131 -66 99 -98 65 -132 65 -100 329 -66 165 -100 131 -700 165 -492 231 -130 195 -130 131 -230 197 -200 691 -132 99 -66 331 -264 165 -100 131 -730 131 -200 131 -498 99 -100 197 -100 131 -66 165 -68 165 -534 65 -498 401 -166 65 -592 65 -98 65 -100 131 -66 231 -132 133 -100 299 -198 133 -164 265 -100 433 -66 429 -330 631 -98 163 -66 265 -232 65 -66 199 -100 165 -198 233 -66 131 -234 67 -564 -RAW_Data: 163 -400 165 -464 131 -362 561 -332 263 -98 133 -132 165 -100 661 -232 591 -328 65 -98 133 -368 131 -560 97 -100 165 -464 131 -296 129 -558 131 -98 99 -196 65 -98 163 -100 97 -394 261 -558 259 -98 97 -426 299 -364 365 -66 599 -166 363 -428 231 -134 625 -98 359 -394 195 -164 197 -66 97 -98 263 -490 195 -98 261 -460 163 -100 199 -330 131 -198 163 -66 293 -66 885 -100 1315 -234 333 -234 365 -200 133 -596 197 -166 295 -100 197 -232 165 -66 131 -336 197 -268 231 -100 99 -696 163 -262 331 -132 197 -132 131 -132 361 -100 525 -66 195 -132 227 -232 163 -262 1233 -68 797 -98 427 -98 231 -200 331 -132 129 -98 163 -66 129 -100 97 -100 5243 -198 1295 -66 197 -66 165 -66 499 -23298 65 -68 131 -564 329 -100 133 -298 101 -166 199 -300 65 -764 133 -266 297 -130 165 -132 265 -66 2793 -198 4161 -66 1747 -100 1729 -98 603 -132 363 -530 99 -266 129 -100 231 -396 129 -198 133 -98 991 -132 397 -462 131 -330 295 -132 197 -134 99 -262 99 -232 229 -268 263 -1448 229 -68 65 -198 99 -134 165 -996 429 -396 631 -632 727 -98 297 -100 199 -134 65 -332 65 -234 165 -362 99 -428 263 -232 67 -1158 165 -196 129 -228 131 -132 195 -98 163 -698 65 -234 531 -728 295 -130 97 -98 229 -366 499 -66 99 -66 131 -66 297 -232 165 -100 65 -400 233 -66 265 -134 131 -134 165 -132 233 -134 97 -300 397 -102 65 -98 99 -98 6595 -132 65 -164 131 -100 529 -66 2157 -66 163 -100 99 -466 99 -530 163 -166 195 -264 197 -66 199 -66 199 -100 11433 -164 1055 -562 497 -134 233 -100 399 -100 131 -66 163 -234 733 -264 163 -236 65 -430 67 -332 133 -132 133 -66 465 -66 603 -98 265 -98 199 -66 3567 -100 391 -166 525 -132 1055 -98 627 -264 333 -100 795 -66 165 -66 297 -66 393 -134 263 -100 1317 -98 131 -98 529 -98 293 -66 99 -66 163 -100 493 -64 265 -164 231 -100 199 -132 265 -66 265 -100 99 -134 199 -132 431 -100 365 -132 231 -68 201 -132 891 -100 831 -98 167 -100 1191 -64 329 -66 393 -98 131 -64 131 -98 229 -132 2693 -132 10455 -66 297 -98 631 -100 431 -100 433 -98 165 -134 65 -100 65 -68 863 -132 499 -100 267 -134 99 -66 989 -66 657 -100 757 -594 395 -66 199 -66 463 -330 663 -264 4219 -66 851 -98 129 -132 915 -66 129 -66 363 -98 99 -100 699 -66 4847 -66 2153 -134 1993 -66 5935 -66 1435 -100 2119 -66 9535 -296 197 -624 131 -1446 129 -362 65 -198 -RAW_Data: 67 -298 197 -226 131 -164 595 -66 463 -98 131 -198 395 -262 131 -66 1773 -516 455 -548 481 -516 479 -556 451 -554 451 -542 469 -548 451 -546 447 -552 449 -554 471 -542 477 -518 489 -512 481 -1044 977 -506 485 -540 469 -1044 481 -518 975 -1050 947 -548 489 -512 481 -1042 967 -1020 979 -550 455 -1038 477 -520 471 -556 483 -514 969 -1044 977 -536 473 -544 449 -1054 955 -552 447 -554 485 -514 471 -1046 975 -1020 485 -518 1003 -512 483 -548 447 -554 447 -556 485 -1010 491 -51312 527 -512 453 -544 477 -540 477 -514 473 -538 473 -524 471 -558 453 -542 467 -514 485 -546 479 -514 473 -522 471 -534 501 -1014 1001 -512 481 -514 479 -1052 459 -544 979 -1016 1009 -516 473 -522 469 -1044 995 -1010 1007 -524 475 -1004 495 -512 483 -544 479 -514 1003 -1018 977 -522 489 -512 479 -1034 1003 -522 487 -512 467 -514 483 -1044 975 -1020 485 -524 991 -514 477 -550 473 -524 473 -536 471 -1030 489 -51306 517 -520 493 -512 483 -546 445 -550 477 -520 469 -544 479 -518 495 -512 481 -514 475 -550 477 -526 471 -538 473 -538 471 -1018 979 -548 451 -546 479 -1010 473 -554 965 -1034 989 -516 479 -1048 967 -554 451 -1022 977 -1034 475 -554 473 -510 1003 -512 481 -546 443 -1064 969 -1034 977 -1020 483 -546 477 -516 967 -538 473 -1052 449 -562 461 -514 481 -546 477 -514 477 -522 999 -510 485 -1038 985 -51816 479 -3892 63 -722 289 -742 265 -734 271 -762 231 -722 291 -720 321 -644 373 -642 339 -626 395 -1114 911 -610 415 -580 413 -1098 425 -548 979 -1054 955 -544 443 -1080 449 -548 939 -1054 473 -550 949 -1048 481 -514 973 -1050 975 -1050 453 -544 971 -538 473 -1032 471 -538 967 -554 449 -524 495 -512 481 -546 479 -1008 473 -556 487 -512 965 -546 481 -516 473 -1036 487 -522 483 -97226 691 -134 1391 -396 231 -166 1025 -98 199 -132 99 -266 395 -100 165 -132 231 -98 689 -66 493 -98 65 -98 557 -134 365 -68 131 -164 425 -132 259 -98 1255 -232 195 -66 495 -64 1651 -100 199 -100 1099 -100 331 -132 265 -168 6433 -66 28525 -100 395 -66 395 -100 259 -164 559 -100 463 -66 327 -66 131 -100 685 -66 395 -98 199 -100 431 -66 101 -100 231 -66 333 -66 399 -266 165 -68 99 -100 165 -398 299 -98 197 -824 97 -132 229 -66 295 -264 97 -230 131 -394 469 -266 397 -198 165 -270 133 -562 165 -66 229 -266 99 -232 363 -530 427 -730 99 -166 97 -534 297 -100 297 -134 133 -132 67 -100 67 -66 361 -430 397 -330 65 -198 165 -200 167 -68 131 -132 199 -68 233 -100 -RAW_Data: 99 -234 65 -300 263 -562 163 -66 199 -166 165 -132 99 -264 131 -262 261 -64 459 -66 427 -132 65 -132 131 -100 97 -228 163 -164 523 -328 297 -66 131 -332 199 -266 65 -134 165 -66 197 -68 697 -98 165 -134 233 -496 231 -164 63 -66 197 -264 297 -166 99 -198 131 -100 365 -266 299 -232 99 -66 627 -132 1415 -100 195 -198 165 -98 327 -98 523 -98 131 -228 229 -230 295 -694 499 -100 531 -66 131 -134 165 -300 165 -132 265 -300 997 -102 1921 -134 723 -98 195 -132 99 -164 163 -68 397 -100 331 -132 165 -266 467 -232 131 -66 465 -198 463 -132 165 -66 329 -100 2691 -100 431 -166 397 -100 199 -134 529 -100 1053 -66 18073 -98 1251 -130 1425 -66 497 -98 6625 -10518 1205 -578 607 -588 599 -612 597 -586 605 -582 587 -1214 1171 -1180 1207 -582 609 -588 613 -578 613 -592 611 -576 613 -1184 583 -580 1205 -1206 1169 -614 577 -590 629 -576 601 -1182 609 -598 1177 -578 627 -588 603 -1178 1207 -582 589 -1178 1209 -1178 613 -588 613 -576 1215 -1174 1181 -1204 1183 -604 579 -1218 1175 -604 579 -1186 1225 -562 615 -1184 611 -578 1207 -578 605 -1184 609 -568 1209 -580 629 -582 603 -574 605 -580 603 -612 577 -610 603 -77146 1139 -774 423 -1374 419 -716 495 -708 493 -682 1089 -1308 1095 -688 519 -638 539 -656 551 -638 539 -656 549 -638 575 -622 551 -1206 1179 -1204 585 -616 577 -620 1189 -594 581 -602 581 -1186 603 -614 1169 -1200 1207 -1176 613 -592 1181 -600 581 -1214 583 -580 611 -588 615 -576 615 -588 1173 -1212 579 -590 613 -590 587 -614 1169 -1212 579 -612 591 -580 1201 -588 615 -578 613 -1180 587 -616 1169 -1210 1201 -580 579 -622 591 -1170 1203 -612 575 -614 593 -77174 1201 -608 589 -1210 583 -622 549 -606 585 -626 1169 -1204 579 -608 1205 -578 601 -586 621 -582 589 -614 575 -1214 575 -620 563 -612 1193 -592 579 -1204 1183 -606 577 -1224 559 -614 1195 -588 611 -1182 581 -606 1205 -1176 611 -586 579 -628 1173 -1208 581 -604 1167 -616 571 -1216 1171 -618 585 -1210 1177 -1208 581 -586 1205 -580 589 -616 571 -1212 1171 -616 587 -1206 573 -594 615 -588 589 -614 1169 -618 583 -1212 1177 -578 609 -590 613 -578 611 -77168 1211 -612 565 -1242 549 -628 577 -620 583 -586 1173 -616 587 -612 587 -588 615 -578 611 -586 579 -628 579 -620 585 -1180 1197 -1194 1183 -600 583 -622 583 -1184 1193 -1180 1197 -604 577 -1214 585 -614 1169 -1210 1165 -614 577 -622 591 -580 603 -1182 609 -602 1173 -1204 577 -598 1203 -610 571 -616 575 -1202 581 -608 611 -592 577 -602 1203 -1172 611 -588 611 -578 611 -592 -RAW_Data: 1171 -622 591 -580 603 -1180 1207 -580 599 -1208 579 -622 1173 -612 575 -590 613 -123688 65 -3788 133 -896 65 -168 65 -926 297 -168 65 -100 365 -66 297 -98 201 -1162 131 -428 195 -66 297 -166 99 -760 99 -196 131 -132 65 -98 197 -66 493 -264 99 -264 821 -66 793 -100 3007 -98 2067 -66 2411 -430 97 -98 197 -130 297 -230 465 -98 4123 -100 391 -132 163 -66 751 -164 161 -990 759 -64 1055 -264 129 -166 429 -66 3943 -66 399 -100 665 -432 431 -132 99 -132 97 -68 97 -330 295 -164 131 -66 291 -66 589 -264 563 -100 1227 -166 233 -298 699 -100 863 -534 131 -332 1619 -100 1595 -132 2191 -164 465 -66 233 -68 299 -66 561 -134 369 -68 233 -134 297 -168 463 -166 795 -100 265 -68 131 -100 3629 -66 227 -66 853 -66 529 -100 1191 -98 501 -132 1417 -64 229 -132 1115 -66 463 -66 231 -64 361 -66 231 -98 989 -98 265 -300 1125 -66 897 -68 197 -100 529 -66 787 -164 163 -66 2705 -66 1899 -132 929 -64 1197 -132 4387 -98 263 -96 261 -100 751 -66 229 -10520 1245 -566 609 -564 615 -572 613 -590 615 -594 589 -1174 1203 -1204 1195 -564 615 -592 617 -580 611 -560 627 -576 603 -1186 609 -564 1213 -1176 1211 -578 623 -588 611 -1176 1205 -582 583 -582 613 -588 611 -578 625 -1152 1205 -592 615 -1176 1217 -1172 601 -576 605 -590 1193 -1186 1191 -1184 1227 -576 615 -1150 1209 -604 613 -1152 1227 -576 591 -1188 613 -578 1207 -576 607 -1184 607 -600 1179 -578 629 -588 607 -582 587 -1182 613 -586 1209 -77154 1187 -666 541 -1242 523 -676 535 -618 553 -644 1163 -1212 1169 -628 577 -620 551 -616 609 -588 589 -614 573 -586 597 -610 599 -1182 1209 -1182 581 -592 613 -604 1169 -1216 577 -604 1175 -1206 1175 -1208 1173 -1206 579 -596 1201 -610 571 -1212 605 -584 587 -610 575 -618 583 -612 1171 -1210 577 -588 613 -580 611 -590 1205 -1170 611 -578 611 -592 1171 -622 591 -578 603 -1182 611 -600 1177 -1206 1177 -610 597 -586 601 -1178 615 -590 589 -578 1205 -77194 1193 -618 583 -1208 575 -624 577 -606 583 -588 1205 -1178 611 -572 1201 -612 575 -588 615 -576 611 -590 579 -1206 585 -616 575 -622 1183 -600 577 -1216 1177 -604 581 -592 611 -608 583 -1178 1197 -1196 573 -616 1199 -1174 611 -586 589 -614 1167 -1214 575 -620 1185 -600 577 -1216 1177 -604 575 -1220 1159 -1222 585 -582 1181 -622 579 -592 611 -1200 1175 -588 585 -1208 605 -576 605 -620 561 -612 1189 -606 581 -1212 1177 -1208 581 -586 1207 -77162 599 -4312 397 -788 425 -772 1027 -722 471 -708 487 -686 503 -678 521 -686 503 -680 523 -652 -RAW_Data: 535 -1274 1109 -1240 1175 -614 559 -650 533 -1240 1177 -616 559 -1208 575 -628 577 -604 577 -620 1175 -1208 1171 -614 605 -578 601 -588 599 -1178 615 -588 1185 -1202 589 -614 1167 -618 583 -612 591 -1186 599 -584 601 -582 591 -618 1199 -1176 603 -580 591 -616 573 -616 1179 -624 581 -584 613 -1184 1173 -622 591 -1172 611 -592 579 -610 613 -588 1173 -122626 229 -330 197 -98 65 -98 97 -100 229 -166 129 -232 65 -66 131 -394 97 -66 97 -66 395 -66 557 -66 657 -64 489 -66 327 -100 65 -98 461 -64 5371 -64 691 -98 859 -66 631 -166 797 -66 299 -132 65 -66 4463 -134 331 -134 233 -132 1495 -264 699 -98 263 -134 1031 -692514 65 -100 97 -1392 131 -398 65 -134 201 -832 4061 -132 523 -66 431 -66 99 -132 99 -100 831 -66 165 -66 1969 -164 8553 -100 297 -68 523 -100 197 -130 957 -98 361 -98 2015 -98 263 -130 195 -130 4497 -66 999 -66 1357 -66 1189 -132 929 -66 4649 -66 263 -66 131 -64 657 -100 195 -68 231 -100 1025 -100 565 -68 931 -98 985 -98 661 -164 363 -100 501 -66 3719 -66 2015 -66 663 -132 299 -330 823 -66 1661 -66 4179 -66 433 -66 1523 -68 697 -68 197 -100 199 -66 1265 -98 629 -68 299 -168 629 -100 3539 -166 331 -66 495 -98 327 -132 589 -166 499 -66 463 -100 331 -66 627 -98 489 -64 129 -130 361 -98 195 -130 65 -100 793 -98 391 -66 1515 -66 129 -66 757 -132 561 -98 653 -64 197 -98 1577 -296 163 -100 789 -66 689 -66 3829 -100 533 -398 467 -166 531 -66 331 -198 753 -64 557 -98 693 -66 165 -100 4907 -134 199 -100 131 -66 333 -198 1345 -66 3781 -98 267 -100 597 -232 1449 -66 723 -330 197 -164 1021 -132 561 -98 591 -100 755 -66 559 -100 229 -228 131 -66 163 -198 689 -100 3365 -100 99 -68 431 -100 797 -66 431 -100 401 -68 397 -134 295 -66 297 -64 65 -100 1085 -132 3135 -66 497 -66 201 -98 99 -168 299 -66 165 -68 267 -366 231 -66 463 -266 733 -132 397 -66 297 -100 4563 -66 163 -66 293 -100 755 -100 697 -100 563 -100 4661 -132 65 -100 97 -494 691 -98 63 -66 459 -232 295 -230 229 -198 495 -66 457 -266 425 -685602 231 -202 133 -66 331 -66 167 -132 699 -98 231 -66 301 -66 299 -596 399 -66 557 -230 7389 -66 733 -98 657 -66 723 -100 789 -68 1661 -132 627 -66 1087 -134 97 -66 5173 -134 693 -134 1261 -132 4435 -66 131 -134 199 -66 891 -66 493 -100 397 -66 365 -100 297 -134 4287 -66 501 -166 1281 -100 195 -100 1151 -100 467 -266 265 -98 -RAW_Data: 2027 -68 131 -66 5771 -98 2171 -130 163 -66 7573 -66 661 -66 653 -66 357 -130 7217 -98 229 -132 393 -66 997 -66 4689 -100 97 -134 9081 -66 163 -132 493 -100 1217 -132 529 -230 131 -66 235 -100 133 -134 1359 -66 5243 -98 329 -66 167 -100 299 -100 431 -66 7349 -132 1059 -66 1393 -100 1317 -66 629 -98 429 -100 495 -100 365 -66 397 -234 1489 -100 4599 -100 399 -100 1855 -68 433 -98 1393 -132 7409 -100 5607 -66 463 -66 665 -68 2553 -134 167 -102 2263 -686936 99 -800 67 -98 99 -66 133 -230 265 -66 3631 -100 229 -130 99 -66 825 -100 99 -232 231 -66 521 -66 593 -98 1413 -130 359 -66 4639 -66 131 -68 97 -202 799 -166 99 -68 365 -66 1595 -66 2987 -66 423 -100 131 -98 791 -132 433 -100 893 -66 4831 -100 101 -98 629 -66 1221 -98 197 -66 625 -98 261 -66 559 -66 665 -66 4409 -66 331 -268 2017 -166 199 -68 333 -98 1553 -68 401 -166 563 -66 393 -100 195 -66 1485 -230 265 -100 4353 -68 431 -100 2383 -66 6737 -98 493 -66 1017 -68 5915 -66 1085 -198 527 -66 4223 -100 163 -64 131 -66 1187 -98 393 -66 1747 -130 263 -66 597 -100 501 -100 431 -132 1491 -100 131 -66 599 -68 2595 -100 497 -68 265 -100 763 -100 265 -100 165 -66 595 -68 331 -100 1587 -66 163 -66 99 -164 559 -130 457 -66 1611 -100 991 -66 4403 -100 1427 -164 565 -132 499 -66 827 -298 299 -100 4637 -166 263 -100 995 -66 1083 -428 365 -424 369 -454 377 -420 377 -418 409 -418 377 -424 381 -436 357 -438 387 -444 383 -414 385 -4058 783 -452 797 -434 811 -412 417 -862 773 -474 779 -484 387 -874 383 -850 403 -822 809 -450 415 -834 807 -444 773 -482 381 -870 787 -478 791 -436 805 -448 803 -420 411 -842 403 -836 415 -838 391 -864 807 -480 395 -850 387 -840 385 -844 391 -858 381 -878 783 -452 795 -464 807 -450 817 -448 377 -858 807 -446 777 -450 789 -454 813 -448 819 -430 809 -448 411 -838 807 -446 385 -862 379 -870 769 -468 801 -446 809 -456 379 -872 409 -858 781 -448 383 -846 409 -846 775 -478 381 -848 415 -858 809 -444 813 -422 393 -868 781 -448 385 -850 409 -860 777 -480 385 -866 805 -444 805 -418 411 -808 391 -15820 421 -372 417 -388 419 -386 437 -394 395 -400 421 -408 385 -412 415 -384 415 -418 383 -420 383 -420 385 -4030 825 -410 845 -420 811 -418 423 -838 821 -420 815 -456 385 -878 419 -816 407 -816 835 -416 409 -842 821 -422 811 -456 387 -876 789 -454 825 -428 801 -414 821 -442 399 -850 381 -852 -RAW_Data: 417 -824 447 -812 809 -462 413 -848 421 -816 399 -822 413 -850 419 -816 803 -440 809 -450 815 -446 821 -456 403 -820 821 -420 819 -418 807 -456 811 -412 821 -454 807 -470 379 -850 823 -416 423 -824 409 -844 789 -450 815 -432 807 -446 409 -838 439 -822 811 -414 409 -844 419 -822 803 -442 413 -848 409 -844 815 -448 811 -424 421 -838 787 -450 411 -810 407 -864 811 -418 415 -866 803 -448 817 -418 419 -776 417 -15782 437 -396 427 -378 407 -410 415 -376 415 -410 419 -388 419 -390 419 -388 411 -382 417 -400 409 -398 423 -4008 821 -420 815 -422 815 -446 405 -830 813 -450 811 -452 379 -876 407 -828 419 -810 813 -458 377 -842 823 -454 783 -458 385 -876 789 -456 827 -432 811 -412 821 -420 435 -820 393 -862 421 -810 429 -852 809 -444 409 -836 421 -812 425 -820 393 -828 421 -846 821 -432 809 -446 821 -448 821 -422 393 -854 787 -454 801 -432 803 -446 803 -446 821 -446 793 -472 381 -840 835 -418 415 -838 399 -820 837 -414 835 -410 835 -418 437 -858 389 -846 817 -432 385 -840 421 -812 827 -432 415 -848 421 -816 809 -472 811 -418 393 -866 785 -450 381 -852 419 -820 807 -440 411 -874 801 -448 803 -426 407 -804 413 -15800 397 -394 435 -388 409 -388 411 -386 447 -382 417 -384 417 -386 419 -388 415 -390 417 -420 385 -420 385 -4032 805 -440 803 -446 801 -416 437 -826 811 -448 809 -454 409 -844 407 -828 419 -834 817 -432 385 -842 819 -418 845 -428 415 -846 787 -456 831 -430 805 -414 821 -454 401 -834 381 -840 409 -866 415 -834 801 -472 379 -842 407 -832 419 -842 387 -832 409 -842 823 -454 785 -462 809 -448 817 -418 419 -846 795 -436 809 -414 829 -428 811 -450 811 -456 809 -444 409 -840 819 -420 383 -852 419 -822 799 -442 807 -452 815 -448 419 -854 413 -814 809 -422 409 -838 421 -844 795 -434 415 -848 419 -818 807 -476 809 -420 393 -834 813 -450 383 -852 417 -824 805 -474 381 -850 821 -454 805 -432 379 -808 417 -119860 67 -134 67 -98 133 -764 65 -398 133 -132 665 -200 597 -64 461 -66 4203 -96 227 -98 655 -98 327 -66 819 -66 589 -132 5777 -66 1495 -328 231 -130 131 -66 463 -66 265 -68 131 -68 429 -66 195 -98 5117 -100 797 -100 299 -166 459 -100 1731 -66 1759 -66 563 -66 825 -98 1081 -68 5321 -232 1291 -134 1955 -100 523 -68 795 -66 767 -66 3589 -66 827 -66 267 -100 2395 -100 4745 -66 2343 -66 1057 -100 1363 -100 6775 -166 329 -66 593 -66 1719 -98 6079 -100 427 -66 523 -66 525 -98 -RAW_Data: 1195 -66 1157 -68 333 -100 533 -66 4957 -134 731 -66 297 -66 97 -98 2989 -66 6633 -66 981 -98 983 -66 993 -98 2015 -132 65 -68 857 -232 723 -66 165 -66 1283 -100 853 -66 5575 -102 627 -98 367 -100 265 -66 853 -66 261 -66 195 -66 751 -66 195 -98 657 -132 953 -66 163 -296 429 -264 263 -66 1511 -98 263 -262 297 -68 263 -66 2523 -66 163 -132 295 -96 1447 -66 199 -66 463 -68 795 -66 1795 -100 701 -66 1333 -66 99 -66 131 -264 1293 -100 99 -66 299 -68 397 -66 399 -66 329 -100 595 -100 795 -66 131 -66 2015 -100 295 -98 2673 -130 1571 -66 753 -66 887 -98 723 -98 821 -100 131 -66 889 -66 793 -66 1057 -132 399 -100 397 -298 165 -66 299 -100 465 -100 233 -132 763 -66 1391 -130 2395 -66 895 -132 99 -134 2029 -166 799 -100 593 -98 195 -64 131 -66 1545 -66 785 -100 97 -98 131 -98 1753 -130 295 -66 619 -198 853 -166 233 -66 1393 -98 1783 -66 291 -132 691 -100 997 -66 133 -332 231 -66 131 -66 525 -66 263 -64 2435 -68 2967 -68 1101 -100 165 -68 599 -66 167 -134 235 -134 297 -68 497 -384 377 -448 377 -418 375 -454 377 -432 369 -424 377 -438 377 -436 377 -430 381 -434 359 -436 387 -4046 383 -852 797 -436 387 -870 385 -848 403 -850 809 -454 379 -876 415 -822 405 -852 381 -846 417 -822 801 -440 415 -846 415 -856 805 -446 383 -874 379 -834 405 -838 813 -460 783 -446 817 -444 793 -472 383 -880 381 -862 773 -442 411 -842 377 -858 413 -846 387 -870 775 -484 771 -480 805 -420 415 -848 795 -438 811 -414 817 -452 803 -470 777 -450 819 -450 417 -822 801 -442 385 -876 385 -848 795 -438 809 -452 819 -450 385 -882 375 -848 811 -422 413 -840 383 -846 823 -434 385 -874 381 -886 771 -476 811 -420 411 -840 809 -450 385 -850 413 -832 809 -444 383 -880 771 -486 803 -430 403 -806 383 -15810 425 -384 401 -420 393 -420 391 -386 421 -422 389 -386 423 -386 423 -388 419 -388 421 -388 421 -388 421 -4026 407 -840 813 -418 411 -842 397 -113844 97 -66 97 -164 99 -328 99 -232 299 -366 99 -1066 165 -66 623 -134 1197 -68 931 -66 599 -66 99 -234 2323 -132 1281 -66 495 -198 555 -414 383 -418 383 -418 417 -386 417 -418 383 -422 375 -422 379 -436 385 -444 351 -446 383 -416 385 -4076 379 -844 421 -812 395 -854 377 -862 813 -452 385 -840 815 -450 799 -488 383 -822 807 -446 803 -420 419 -842 403 -852 779 -450 407 -856 387 -880 805 -430 385 -840 809 -454 385 -856 377 -856 -RAW_Data: 777 -484 783 -454 807 -476 807 -410 797 -454 773 -462 383 -872 789 -454 787 -454 383 -870 797 -474 407 -822 813 -446 775 -448 785 -456 785 -484 783 -452 385 -872 385 -882 777 -442 809 -444 375 -864 777 -450 385 -886 789 -452 783 -470 383 -884 391 -858 779 -448 383 -848 385 -852 411 -856 379 -848 419 -852 777 -504 783 -418 801 -448 375 -852 811 -446 777 -450 813 -448 801 -458 419 -810 783 -424 379 -16422 413 -400 399 -396 405 -426 375 -424 411 -390 411 -390 413 -420 383 -420 383 -418 383 -418 415 -418 385 -4062 419 -816 383 -850 383 -852 409 -856 781 -448 385 -852 805 -468 779 -480 383 -844 819 -420 815 -418 391 -860 387 -880 763 -462 383 -876 417 -852 813 -446 381 -848 769 -456 397 -858 363 -882 779 -450 811 -454 807 -480 777 -446 803 -452 779 -432 383 -874 771 -488 765 -456 383 -878 795 -488 385 -854 773 -448 785 -456 777 -484 783 -450 771 -478 377 -860 407 -880 809 -418 813 -416 391 -862 779 -450 385 -888 791 -452 781 -468 381 -876 409 -834 777 -452 383 -872 377 -856 385 -866 405 -854 355 -898 787 -488 765 -460 777 -446 383 -852 807 -432 809 -446 783 -458 817 -454 391 -822 799 -418 399 -16448 421 -388 381 -424 417 -390 377 -456 379 -406 385 -442 383 -416 385 -418 387 -418 387 -452 355 -450 357 -4096 383 -840 387 -846 389 -858 365 -882 785 -444 385 -886 793 -452 781 -470 415 -844 791 -452 779 -432 379 -872 377 -884 779 -448 383 -872 379 -896 799 -430 411 -834 789 -456 383 -838 421 -846 799 -428 807 -444 803 -480 803 -446 767 -472 777 -448 383 -852 791 -488 775 -466 381 -848 793 -486 389 -852 775 -448 803 -446 801 -454 779 -468 775 -448 389 -896 387 -878 775 -438 807 -448 367 -848 813 -444 385 -848 789 -490 767 -458 383 -912 363 -850 813 -444 381 -848 385 -850 409 -854 385 -870 387 -846 803 -464 811 -418 801 -446 375 -884 779 -444 779 -484 779 -446 797 -472 381 -850 773 -416 407 -16410 409 -422 369 -410 403 -410 409 -410 379 -412 411 -408 377 -442 377 -428 379 -440 347 -444 381 -416 381 -4076 411 -844 375 -848 383 -850 385 -884 757 -488 385 -856 775 -480 769 -478 409 -840 787 -448 777 -450 391 -860 387 -848 793 -458 383 -878 417 -852 813 -412 383 -874 787 -454 389 -844 393 -854 785 -482 781 -454 807 -480 777 -446 801 -452 779 -434 381 -876 801 -454 765 -460 381 -878 787 -488 411 -826 775 -446 801 -458 779 -450 809 -450 803 -444 379 -862 419 -878 777 -438 807 -446 367 -878 -RAW_Data: 777 -446 383 -850 791 -486 783 -468 381 -882 393 -856 777 -446 383 -848 383 -854 411 -858 379 -848 419 -850 775 -506 783 -450 769 -446 377 -852 813 -446 777 -484 779 -448 795 -454 417 -824 777 -414 405 -122598 99 -134 65 -100 199 -832 99 -200 829 -98 163 -100 165 -66 197 -100 361 -98 723 -66 2987 -100 561 -460 1415 -100 527 -164 621 -98 459 -100 797 -66 497 -66 365 -132 401 -134 131 -66 699 -100 231 -66 327 -164 197 -66 299 -66 359 -66 395 -66 1773 -98 591 -66 295 -64 261 -98 833 -132 4215 -66 735 -98 331 -100 431 -66 761 -266 331 -102 497 -200 5637 -66 97 -198 921 -100 531 -66 299 -100 1559 -98 99 -100 233 -100 4173 -68 431 -66 599 -100 231 -66 429 -166 331 -132 431 -100 489 -66 359 -98 131 -66 261 -134 5083 -134 299 -100 1921 -68 197 -136 5299 -100 263 -64 721 -100 399 -130 429 -100 99 -98 1351 -100 295 -66 1315 -164 129 -98 393 -164 925 -66 663 -68 131 -66 229 -100 231 -100 99 -98 427 -66 325 -130 165 -98 197 -130 197 -128 327 -100 429 -66 1227 -100 893 -98 3491 -68 699 -66 365 -100 529 -98 229 -396 261 -66 561 -98 227 -98 329 -426 393 -130 363 -66 133 -166 1353 -100 393 -200 197 -100 199 -100 333 -66 961 -66 199 -66 197 -64 593 -66 627 -66 1317 -66 165 -198 199 -98 99 -66 3621 -98 597 -98 165 -66 4527 -200 263 -66 2751 -100 2131 -66 229 -64 721 -132 1261 -266 629 -98 201 -68 1323 -100 1689 -66 233 -326 1283 -66 261 -66 1381 -66 201 -100 4351 -66 331 -100 565 -134 2025 -66 563 -198 1657 -98 3057 -68 397 -98 297 -66 231 -100 931 -98 365 -66 1127 -66 165 -132 201 -100 99 -66 961 -100 99 -66 425 -66 1087 -166 463 -64 6307 -530 329 -66 297 -100 329 -66 201 -100 1123 -66 723 -98 523 -64 985 -66 131 -134 99 -66 4897 -66 1451 -132 199 -66 659 -66 195 -66 557 -66 263 -68 569 -100 661 -164 1975 -100 1361 -66 3647 -66 335 -100 363 -100 919 -66 887 -166 1775 -166 501 -66 495 -66 2753 -98 887 -66 3259 -66 821 -464 797 -134 567 -66 2401 -98 5415 -100 435 -100 99 -100 427 -66 599 -68 465 -132 435 -100 1495 -66 301 -68 233 -200 431 -66 397 -100 265 -68 231 -100 461 -234 199 -98 893 -132 265 -298 131 -166 2055 -68 199 -66 497 -66 1697 -66 1857 -66 5599 -66 2351 -66 131 -100 4929 -66 265 -134 561 -66 765 -132 861 -66 5351 -134 265 -102 99 -66 563 -66 267 -136 333 -98 233 -100 601 -66 365 -98 -RAW_Data: 1023 -66 5413 -232 631 -134 8367 -66 1583 -196 1901 -98 129 -66 1381 -98 3681 -98 1583 -98 133 -100 263 -134 1731 -100 465 -66 629 -66 4609 -166 201 -132 1995 -66 333 -66 499 -100 723 -134 561 -66 5317 -66 333 -66 1135 -66 1193 -66 1227 -100 885 -132 163 -230 719 -100 2305 -66 1025 -166 397 -68 793 -100 1381 -132 5385 -66 623 -164 763 -98 357 -396 3739 -134 4665 -66 393 -130 1153 -98 687 -66 229 -100 961 -66 467 -66 1301 -66 3647 -98 363 -66 329 -68 1589 -100 261 -66 5647 -66 2039 -66 297 -164 523 -66 197 -100 933 -66 99 -164 67 -66 97 -64 197 -64 559 -132 1151 -64 229 -98 2249 -64 1151 -66 533 -134 299 -134 665 -98 559 -98 1253 -66 2687 -134 995 -132 233 -68 131 -68 467 -132 297 -66 233 -66 167 -66 165 -134 201 -98 165 -166 1421 -428 333 -480 327 -448 361 -462 367 -424 371 -452 377 -420 379 -420 411 -418 381 -422 379 -424 381 -4072 773 -450 393 -852 397 -858 777 -448 811 -458 779 -480 793 -458 809 -448 385 -854 789 -426 411 -844 391 -852 815 -450 801 -444 415 -848 407 -848 383 -850 799 -450 409 -854 773 -450 395 -862 419 -844 401 -852 809 -456 379 -838 417 -848 397 -822 805 -446 411 -840 419 -848 799 -470 811 -418 411 -840 385 -844 423 -838 381 -882 369 -882 777 -480 383 -856 395 -862 385 -842 819 -432 387 -840 419 -850 399 -854 381 -850 403 -884 811 -418 415 -822 417 -826 405 -850 811 -420 817 -446 801 -452 811 -472 381 -840 833 -410 807 -454 785 -458 803 -444 805 -448 801 -458 417 -844 795 -440 809 -382 417 -16778 459 -354 455 -386 385 -422 419 -388 411 -382 405 -420 397 -398 421 -408 387 -410 385 -414 415 -418 383 -4046 787 -424 413 -844 427 -818 817 -416 835 -428 811 -450 811 -458 803 -448 409 -838 785 -450 383 -840 417 -862 809 -442 811 -416 421 -872 385 -850 403 -820 809 -452 397 -828 815 -446 413 -840 409 -838 413 -872 807 -412 411 -848 407 -824 415 -844 783 -460 379 -876 385 -850 807 -474 809 -418 411 -838 419 -810 429 -818 423 -838 419 -846 797 -436 413 -878 381 -828 411 -848 811 -418 411 -840 419 -846 397 -852 387 -870 387 -850 803 -442 413 -844 407 -824 415 -848 781 -462 809 -412 837 -448 803 -458 419 -810 827 -434 811 -414 801 -454 815 -450 779 -458 803 -450 395 -860 809 -416 809 -424 379 -16828 405 -414 379 -412 413 -410 383 -406 423 -396 399 -392 433 -382 407 -412 413 -380 411 -414 409 -394 413 -4044 805 -422 393 -854 395 -826 813 -448 -RAW_Data: 811 -456 811 -412 821 -486 775 -438 411 -844 801 -442 395 -838 413 -838 803 -448 801 -456 413 -840 407 -836 417 -834 805 -446 387 -848 801 -438 415 -844 407 -848 417 -848 819 -432 385 -844 419 -816 429 -826 809 -446 411 -838 419 -848 797 -468 805 -418 393 -866 385 -846 389 -854 409 -838 415 -838 799 -472 379 -886 367 -850 417 -846 781 -458 379 -842 419 -848 401 -854 381 -884 369 -884 783 -450 415 -820 407 -848 413 -814 809 -460 809 -444 805 -448 801 -460 417 -844 795 -434 811 -412 801 -450 809 -436 805 -448 805 -446 417 -864 809 -412 803 -416 409 -16802 423 -386 411 -382 405 -420 377 -122006 97 -98 165 -66 97 -132 65 -428 427 -132 3981 -66 265 -66 161 -66 327 -66 1625 -66 727 -100 4773 -134 863 -66 665 -66 2605 -98 4311 -98 99 -66 1249 -98 263 -100 5105 -68 461 -100 5065 -66 965 -68 799 -164 5737 -19106 327 -196 131 -362 131 -332 229 -134 363 -1162 297 -132 1197 -100 99 -132 231 -66 425 -200 267 -100 395 -134 331 -134 267 -100 563 -100 231 -66 655 -66 427 -66 299 -98 197 -132 3997 -66 463 -134 133 -134 1827 -132 1847 -66 131 -66 933 -100 3749 -132 131 -328 887 -98 457 -66 457 -100 593 -98 1813 -66 3895 -98 1117 -66 199 -66 881 -98 987 -66 397 -98 749 -66 195 -100 953 -100 819 -100 1017 -98 1915 -100 1707 -130 3865 -98 567 -132 265 -132 333 -66 1033 -132 791 -100 165 -100 1261 -66 531 -68 4337 -68 1191 -100 933 -100 767 -66 499 -164 363 -132 65 -100 5559 -66 233 -66 167 -68 265 -232 195 -100 329 -98 1795 -132 5043 -132 197 -66 223 -132 265 -200 525 -66 1147 -132 793 -100 4221 -132 703 -66 197 -132 595 -100 3891 -100 333 -100 1451 -164 261 -328 199 -166 1277 -1574 1699 -490 1705 -516 1663 -516 1695 -518 1699 -484 617 -1538 609 -1562 585 -1576 1693 -518 587 -1576 1701 -514 581 -1554 1737 -484 1737 -484 1711 -528 583 -1572 599 -1576 609 -1530 615 -1560 1719 -494 1739 -520 581 -1560 631 -1534 609 -16396 609 -1576 1707 -490 1713 -516 1697 -520 1699 -508 1711 -530 579 -1572 599 -1576 609 -1544 1707 -518 587 -1570 1731 -484 605 -1578 1699 -516 1729 -476 1753 -494 605 -1568 609 -1566 593 -1540 607 -1566 1733 -510 1705 -512 599 -1578 611 -1544 615 -16410 631 -1536 1711 -530 1703 -500 1731 -516 1737 -518 1741 -480 639 -1566 599 -1568 615 -1580 1743 -482 641 -1534 1751 -486 655 -1542 1735 -520 1735 -514 1745 -512 595 -1570 613 -1546 651 -1548 603 -1574 1767 -484 1745 -516 609 -1570 595 -1580 611 -16402 631 -1542 1733 -486 1747 -480 1773 -484 -RAW_Data: 1729 -486 1783 -462 643 -1542 637 -1548 613 -1562 1743 -480 611 -1566 1747 -490 623 -1540 1747 -502 1739 -492 1759 -500 615 -1544 639 -1532 617 -1556 623 -1542 1777 -462 1771 -490 623 -1540 619 -1570 609 -16422 629 -1546 1747 -486 1739 -474 1737 -516 1713 -506 1751 -488 621 -1574 611 -1558 617 -1542 1729 -520 619 -1534 1735 -512 611 -1546 1743 -508 1727 -514 1729 -482 647 -1554 615 -1546 605 -1568 613 -1560 1713 -512 1737 -486 607 -1568 613 -1562 621 -16400 643 -1538 1715 -518 1709 -482 1725 -514 1723 -520 1711 -530 581 -1570 603 -1596 575 -1592 1693 -516 601 -1580 1703 -520 591 -1574 1737 -484 1741 -482 1761 -482 605 -1576 611 -1560 613 -1548 623 -1538 1739 -478 1715 -520 587 -1556 603 -1568 607 -16414 661 -1524 1743 -478 1727 -486 1727 -486 1747 -490 1731 -522 587 -1558 623 -1546 623 -1574 1737 -484 597 -1566 1739 -480 635 -1560 1745 -464 1745 -516 1729 -504 619 -1546 623 -1546 623 -1574 609 -1560 1743 -486 1747 -480 639 -1572 337 -80934 65 -702 65 -960 165 -132 333 -100 1031 -134 163 -100 199 -398 2413 -66 853 -96 3999 -66 1027 -68 495 -66 431 -98 197 -134 65 -100 431 -68 663 -596 197 -132 525 -100 425 -100 397 -100 429 -100 263 -166 4697 -132 291 -228 459 -64 957 -66 233 -66 723 -364 5193 -198 759 -66 359 -98 231 -98 425 -66 265 -98 457 -66 3309 -198 863 -66 263 -166 393 -66 1025 -166 331 -134 463 -102 4119 -66 331 -66 465 -66 395 -66 267 -100 365 -136 97 -100 333 -134 363 -100 265 -100 799 -66 463 -132 1917 -100 2273 -132 99 -66 2537 -98 97 -100 195 -66 657 -98 1145 -66 359 -66 423 -98 227 -66 5391 -66 4069 -100 795 -130 455 -66 3877 -100 165 -98 365 -198 2085 -66 1059 -66 333 -66 7359 -102 431 -66 1349 -100 131 -66 459 -66 327 -66 4189 -98 533 -100 563 -66 365 -66 465 -66 863 -66 493 -66 1317 -66 3459 -98 99 -66 1153 -130 1875 -166 867 -232 5309 -132 363 -66 301 -66 1333 -166 1525 -66 865 -166 5313 -98 295 -98 363 -98 197 -98 229 -64 557 -100 97 -164 199 -66 99 -66 291 -66 393 -98 163 -326 327 -100 529 -134 467 -68 165 -66 201 -66 6277 -66 229 -64 361 -66 227 -66 1215 -66 525 -66 131 -66 1053 -66 1147 -66 1463 -100 565 -402 1127 -66 1329 -98 331 -66 97 -68 461 -66 265 -68 65 -66 1197 -66 831 -66 761 -66 233 -66 4805 -66 395 -68 297 -100 827 -198 625 -100 399 -66 1191 -68 429 -100 629 -66 499 -66 195 -100 2485 -66 655 -100 4745 -68 297 -66 1965 -134 4807 -68 99 -134 627 -66 233 -132 499 -66 -RAW_Data: 5047 -98 165 -102 167 -66 533 -66 133 -100 1359 -166 493 -100 2385 -100 3759 -66 635 -132 331 -100 2115 -100 431 -66 5673 -66 631 -66 231 -100 1589 -100 9979 -100 2797 -394 327 -66 687 -66 899 -100 5425 -134 461 -100 623 -132 5885 -68 399 -100 165 -98 165 -100 1761 -230 697 -66 227 -98 919 -66 1921 -134 1099 -66 331 -66 333 -166 531 -100 793 -66 129 -164 197 -66 131 -98 887 -100 131 -198 295 -66 197 -68 761 -68 827 -100 297 -100 1585 -100 1083 -66 199 -66 1553 -68 433 -100 631 -232 165 -66 297 -234 431 -66 2297 -100 233 -66 565 -230 129 -196 4915 -66 297 -66 529 -134 365 -132 433 -66 297 -166 359 -66 695 -68 1259 -130 689 -66 1645 -98 525 -100 791 -66 295 -66 1413 -64 165 -68 261 -100 1627 -134 1677 -66 357 -66 633 -132 197 -132 1061 -100 5353 -66 233 -100 897 -66 395 -66 459 -66 129 -100 897 -134 4679 -66 857 -130 1841 -66 1023 -134 5191 -98 361 -98 527 -66 1021 -98 591 -66 493 -66 955 -100 231 -98 295 -364 99 -66 4471 -66 99 -132 1193 -66 563 -66 267 -68 661 -66 1261 -134 827 -98 4705 -66 1643 -132 2273 -166 263 -66 459 -100 3483 -66 565 -100 2155 -130 195 -100 1695 -100 529 -100 1053 -202 133 -100 885 -98 297 -98 853 -68 531 -66 263 -100 4855 -66 331 -66 563 -64 391 -164 333 -68 97 -132 1789 -132 165 -132 267 -66 1191 -66 727 -100 197 -66 601 -98 231 -100 331 -200 465 -66 863 -66 97 -98 361 -66 231 -100 959 -66 525 -66 167 -98 531 -100 4877 -100 829 -66 331 -66 363 -66 589 -64 227 -98 393 -132 99 -98 197 -132 197 -100 763 -100 399 -100 627 -98 301 -66 1595 -1492 511 -1044 513 -1040 507 -1042 1005 -558 1007 -554 1005 -574 1007 -548 1005 -546 1003 -542 477 -1072 1007 -516 1013 -556 1009 -552 513 -1076 975 -550 1009 -554 1013 -524 1017 -552 487 -1042 999 -580 993 -584 493 -1050 985 -580 977 -576 483 -1044 515 -1044 1007 -516 1005 -586 1011 -550 481 -1072 1013 -548 1013 -548 975 -552 515 -1042 475 -1050 1017 -574 491 -1084 983 -570 475 -1056 525 -1048 979 -546 515 -1042 479 -1040 1003 -584 515 -1040 515 -1044 513 -1046 515 -1042 1009 -548 1007 -516 509 -1040 1003 -524 1513 -19022 1537 -1536 477 -1048 507 -1032 509 -1046 1001 -570 1013 -572 979 -580 483 -1064 499 -1052 981 -546 513 -1044 1009 -518 1009 -586 1009 -548 513 -1040 1011 -548 1009 -546 1009 -546 1005 -516 511 -1044 977 -620 981 -570 495 -1050 1013 -534 999 -550 525 -1048 485 -1044 1009 -514 1003 -586 1009 -546 515 -1074 979 -546 -RAW_Data: 1009 -542 1009 -546 513 -1040 479 -1040 1003 -586 513 -1040 1007 -576 483 -1074 483 -1044 1011 -548 479 -1044 511 -1042 1003 -588 477 -1076 479 -1074 513 -1040 513 -1040 1009 -548 1007 -516 509 -1040 1003 -522 1527 -18990 1541 -1550 481 -1042 509 -1048 481 -1046 1001 -588 1017 -554 985 -572 477 -1060 1023 -548 981 -544 515 -1040 1011 -514 1003 -586 1007 -548 513 -1042 1007 -576 981 -546 1013 -548 1007 -516 509 -1042 1005 -574 1005 -576 481 -1076 981 -578 977 -546 513 -1044 513 -1040 1005 -518 1007 -590 971 -584 479 -1074 1011 -548 1007 -548 1005 -516 511 -1040 509 -1044 975 -588 519 -1052 1003 -552 501 -1046 513 -1062 993 -548 485 -1042 515 -1040 1003 -574 485 -1076 485 -1076 485 -1076 483 -1042 1009 -548 1009 -516 509 -1040 1003 -520 1527 -19010 1537 -1524 481 -1076 481 -1046 511 -1044 1013 -554 1013 -552 519 -1044 1005 -556 497 -1050 983 -578 483 -1042 1011 -516 1009 -586 1009 -550 513 -115324 65 -2458 65 -66 231 -896 231 -68 131 -434 1191 -66 399 -66 295 -100 627 -132 561 -66 755 -100 593 -100 1155 -98 163 -66 3687 -100 793 -98 229 -98 425 -100 623 -98 393 -132 131 -132 393 -66 395 -66 259 -132 259 -132 99 -100 463 -166 5379 -132 631 -66 1131 -66 819 -164 1115 -66 1525 -66 3617 -98 297 -98 65 -66 497 -68 199 -100 267 -166 593 -66 787 -132 197 -68 5473 -68 527 -164 329 -66 229 -64 195 -230 359 -100 263 -66 391 -98 821 -66 165 -66 2935 -100 65 -68 1057 -66 267 -68 561 -98 559 -98 329 -166 367 -100 1925 -134 999 -66 399 -98 529 -66 299 -134 299 -164 4801 -132 299 -98 595 -66 265 -68 1259 -98 327 -132 755 -198 4311 -130 721 -68 1259 -200 399 -302 495 -100 1259 -66 665 -66 331 -66 531 -66 3979 -134 461 -66 1313 -66 325 -66 753 -68 329 -98 427 -66 5079 -66 299 -134 527 -68 165 -100 333 -100 1091 -66 195 -164 195 -66 4411 -66 199 -100 1695 -66 567 -66 435 -234 1415 -98 325 -98 97 -98 329 -66 165 -268 131 -164 1905 -132 1123 -68 263 -166 199 -234 533 -134 491 -98 293 -66 989 -98 4053 -134 299 -166 499 -98 893 -66 463 -134 5411 -66 4201 -100 4949 -66 533 -166 1327 -66 131 -100 1717 -100 2787 -166 471 -66 1051 -98 1183 -66 2895 -68 2289 -66 797 -66 295 -68 627 -200 697 -100 163 -66 5101 -100 1791 -66 965 -134 7887 -66 265 -100 99 -134 1891 -100 559 -66 661 -98 167 -68 465 -68 1851 -164 4741 -98 397 -66 261 -98 955 -198 567 -98 233 -132 699 -100 465 -66 265 -164 331 -66 131 -98 765 -100 461 -98 1125 -64 -RAW_Data: 3701 -100 1033 -66 197 -68 4443 -68 595 -134 565 -66 499 -66 199 -134 5461 -66 645 -344 635 -326 677 -648 345 -312 697 -310 669 -350 667 -348 667 -634 345 -312 669 -650 373 -636 323 -348 635 -23014 363 -320 669 -322 641 -650 373 -312 667 -350 667 -346 667 -332 681 -642 351 -312 667 -640 319 -666 347 -318 645 -23022 349 -326 641 -352 659 -656 335 -350 661 -334 651 -354 657 -330 703 -658 329 -316 655 -646 349 -668 341 -318 625 -23038 323 -348 653 -322 663 -654 335 -354 661 -336 653 -322 685 -330 701 -652 321 -346 637 -648 351 -658 333 -354 623 -23022 319 -336 677 -320 661 -654 335 -354 661 -336 679 -322 687 -296 703 -656 331 -318 667 -652 353 -620 375 -318 625 -23036 321 -346 655 -322 661 -652 371 -320 659 -338 677 -322 691 -296 705 -654 333 -350 635 -634 345 -672 353 -300 635 -23040 349 -314 667 -314 665 -642 349 -352 645 -354 651 -344 693 -318 693 -644 345 -300 679 -626 361 -648 323 -354 651 -22990 345 -338 661 -346 653 -652 325 -350 647 -322 681 -350 673 -346 653 -654 321 -354 645 -646 349 -656 351 -340 609 -23046 321 -344 637 -328 679 -648 345 -314 697 -316 667 -348 669 -338 677 -646 351 -316 655 -646 343 -658 351 -314 655 -23016 333 -352 627 -328 681 -646 345 -350 655 -346 655 -322 691 -320 675 -652 337 -350 637 -666 315 -672 351 -300 669 -22998 349 -318 655 -340 653 -646 349 -348 667 -314 667 -352 669 -338 679 -646 351 -316 655 -646 345 -658 351 -314 653 -23020 335 -318 659 -322 665 -672 345 -316 659 -342 671 -330 677 -324 683 -650 341 -354 631 -656 349 -654 345 -352 621 -23002 329 -352 667 -322 641 -684 343 -316 659 -340 677 -322 695 -326 675 -654 333 -354 623 -648 353 -658 331 -354 629 -123230 99 -66 197 -568 97 -594 65 -100 229 -130 589 -134 5281 -66 99 -98 2761 -66 1385 -132 299 -66 4967 -230 1991 -200 627 -66 1515 -232 5531 -132 1693 -98 995 -66 465 -100 399 -66 893 -66 4501 -66 363 -66 197 -66 1989 -66 1727 -198 99 -66 499 -100 427 -98 1649 -132 165 -164 97 -98 295 -132 325 -98 131 -66 295 -66 657 -64 625 -66 295 -130 1515 -68 231 -66 455 -64 521 -66 559 -66 1977 -98 167 -166 335 -66 493 -66 233 -68 335 -66 199 -266 165 -134 167 -68 431 -100 425 -98 163 -98 197 -66 359 -98 691 -98 229 -66 689 -66 195 -66 785 -66 163 -230 231 -196 259 -66 425 -98 265 -102 597 -100 199 -100 231 -100 231 -68 565 -66 133 -166 165 -100 229 -66 629 -134 65 -132 -RAW_Data: 231 -68 2745 -66 4279 -132 1093 -200 663 -98 2791 -66 299 -66 4055 -64 691 -66 529 -66 165 -68 865 -66 629 -66 133 -100 597 -266 297 -68 4367 -66 261 -130 519 -68 1265 -66 2189 -66 1423 -66 131 -66 4185 -66 165 -100 333 -132 1125 -66 363 -66 701 -66 1523 -534 3887 -66 1941 -100 293 -66 327 -164 131 -98 589 -98 133 -66 129 -66 429 -98 1415 -132 1021 -130 555 -100 1791 -134 299 -66 165 -66 331 -100 927 -298 231 -66 97 -68 1025 -66 297 -66 199 -68 699 -66 97 -66 97 -66 261 -166 393 -98 129 -98 161 -100 425 -100 1873 -66 759 -66 799 -100 427 -66 299 -66 1031 -134 267 -100 2349 -68 2659 -100 2097 -132 629 -100 1129 -166 231 -66 231 -66 1719 -132 427 -200 5213 -66 1399 -66 3055 -132 7835 -66 1327 -66 4625 -66 359 -66 2607 -420 373 -452 345 -452 347 -454 379 -422 377 -450 349 -466 359 -434 359 -436 389 -442 353 -444 385 -4052 781 -448 797 -436 379 -872 803 -430 807 -448 813 -458 779 -478 803 -450 805 -430 801 -446 381 -848 815 -444 789 -472 381 -846 819 -454 803 -434 413 -840 375 -858 409 -838 387 -852 395 -864 815 -450 385 -854 391 -870 383 -846 389 -852 395 -860 383 -844 393 -886 781 -448 415 -858 411 -816 417 -820 405 -850 411 -848 779 -458 805 -446 805 -450 803 -458 415 -846 793 -434 387 -840 417 -848 791 -436 809 -448 805 -450 409 -860 383 -848 825 -434 387 -840 417 -846 793 -434 385 -874 383 -888 375 -882 779 -458 807 -416 803 -456 381 -842 821 -434 813 -450 413 -836 809 -444 811 -426 395 -828 409 -17022 421 -414 417 -382 417 -384 417 -386 417 -418 385 -420 409 -398 395 -398 391 -440 387 -408 387 -412 415 -4026 819 -420 793 -430 421 -838 811 -454 781 -462 813 -446 787 -488 777 -440 809 -450 793 -426 411 -844 817 -430 809 -448 397 -862 817 -452 783 -462 385 -838 419 -812 427 -820 393 -866 421 -810 823 -436 413 -842 409 -866 385 -844 389 -854 397 -826 419 -846 387 -854 813 -440 409 -848 419 -844 389 -856 399 -822 417 -844 783 -460 803 -446 801 -448 801 -454 417 -846 799 -438 385 -842 421 -846 793 -436 809 -448 821 -418 419 -848 407 -848 811 -420 409 -840 421 -810 823 -436 383 -874 407 -858 387 -844 821 -438 807 -414 835 -418 417 -844 797 -436 809 -450 395 -864 811 -452 785 -428 423 -804 415 -17028 413 -430 363 -450 339 -448 375 -450 343 -450 375 -452 371 -420 381 -432 351 -470 349 -442 381 -410 383 -4072 773 -446 805 -426 411 -844 787 -464 811 -448 -RAW_Data: 793 -456 813 -450 789 -460 805 -416 803 -456 381 -844 825 -434 811 -448 415 -834 811 -446 813 -458 357 -870 383 -846 391 -852 395 -862 383 -872 793 -468 379 -874 397 -860 383 -844 387 -854 365 -856 415 -848 387 -854 819 -450 383 -886 375 -850 383 -850 417 -826 403 -852 777 -450 805 -446 805 -454 817 -450 395 -850 819 -418 415 -848 397 -850 779 -448 805 -458 777 -482 387 -850 393 -864 809 -450 385 -854 379 -862 773 -474 383 -846 415 -858 377 -882 785 -458 805 -414 803 -456 381 -844 821 -434 809 -448 379 -868 815 -452 789 -432 391 -832 413 -17034 411 -398 427 -380 407 -412 413 -378 413 -412 411 -382 417 -396 417 -396 409 -414 379 -436 387 -410 385 -4044 819 -420 789 -426 411 -840 809 -456 821 -430 809 -448 821 -418 831 -430 805 -450 811 -414 409 -838 811 -454 783 -456 409 -848 819 -448 791 -440 415 -814 421 -814 431 -824 413 -850 385 -850 805 -474 379 -850 419 -828 411 -848 381 -850 417 -822 405 -852 381 -850 823 -452 409 -856 381 -840 417 -830 407 -852 379 -850 821 -422 827 -432 807 -448 821 -418 419 -850 803 -438 409 -810 407 -854 811 -448 809 -418 815 -446 419 -858 417 -814 811 -422 409 -838 421 -812 827 -434 415 -848 419 -816 411 -848 813 -456 805 -412 833 -418 413 -822 807 -452 817 -418 419 -850 803 -472 809 -420 397 -794 411 -17034 429 -394 435 -388 409 -386 411 -416 415 -384 415 -386 417 -386 417 -420 385 -420 383 -422 385 -422 417 -4022 805 -414 807 -448 407 -838 785 -454 809 -456 811 -412 833 -448 801 -454 809 -420 815 -420 409 -838 821 -420 813 -458 387 -876 787 -454 827 -432 385 -840 421 -814 429 -820 421 -836 421 -844 795 -436 409 -874 377 -866 383 -836 425 -824 409 -844 407 -822 415 -836 821 -434 409 -874 377 -866 387 -844 389 -854 397 -824 813 -450 813 -452 811 -412 831 -442 407 -864 783 -450 381 -840 419 -822 805 -442 809 -450 817 -448 419 -850 413 -814 813 -422 409 -840 419 -812 823 -434 415 -846 419 -818 409 -848 811 -458 805 -414 831 -416 415 -820 807 -450 801 -446 409 -864 819 -452 783 -428 393 -834 413 -17032 409 -432 363 -424 369 -448 375 -448 343 -450 375 -450 339 -454 375 -434 377 -432 351 -464 357 -436 389 -4050 773 -444 785 -466 385 -874 771 -452 793 -464 811 -446 803 -448 803 -454 809 -418 813 -422 411 -838 817 -448 781 -462 411 -846 797 -486 777 -438 415 -842 383 -850 403 -852 379 -848 405 -848 811 -448 415 -856 395 -856 387 -846 385 -834 391 -868 -RAW_Data: 387 -844 429 -852 781 -446 409 -862 419 -844 399 -824 385 -838 409 -854 811 -444 779 -452 815 -448 801 -472 385 -842 825 -434 387 -838 421 -812 827 -432 805 -450 819 -420 415 -850 411 -850 811 -422 393 -864 387 -846 787 -462 385 -876 385 -848 407 -848 811 -458 803 -414 831 -416 413 -852 773 -448 803 -446 405 -882 387 -113196 99 -362 163 -66 65 -1810 97 -66 165 -196 129 -98 523 -98 1525 -100 1095 -100 725 -66 6021 -66 261 -64 195 -98 293 -130 1745 -98 99 -132 397 -98 363 -98 199 -98 5181 -166 299 -66 429 -66 367 -66 431 -100 629 -134 1297 -98 229 -66 163 -100 4973 -132 995 -66 165 -98 395 -66 297 -66 1711 -200 5349 -100 1493 -232 663 -198 1721 -66 301 -100 5563 -66 1151 -66 331 -66 227 -66 97 -98 163 -100 65 -98 1975 -66 361 -66 97 -230 525 -66 657 -66 863 -66 4121 -230 231 -66 401 -132 129 -98 259 -98 263 -66 955 -100 327 -132 5011 -170 299 -64 763 -66 435 -100 297 -66 401 -132 827 -200 2287 -64 8629 -100 897 -66 1689 -100 2345 -132 97 -100 1155 -68 1915 -396 787 -66 1021 -130 1057 -100 267 -132 829 -100 4749 -68 599 -66 397 -100 329 -64 163 -296 99 -132 329 -66 4881 -66 2091 -66 165 -66 231 -98 359 -100 663 -98 297 -66 5775 -100 233 -98 699 -98 131 -66 361 -100 297 -66 331 -66 1017 -98 233 -66 231 -132 197 -98 1721 -130 523 -66 197 -98 953 -164 231 -164 231 -98 795 -100 1155 -66 753 -166 825 -66 4525 -98 955 -298 625 -66 885 -66 4759 -100 367 -66 495 -66 597 -100 465 -66 265 -100 601 -98 467 -230 463 -66 4177 -66 495 -66 693 -100 831 -66 431 -68 363 -132 295 -98 131 -166 265 -66 331 -100 3189 -66 395 -298 625 -64 327 -362 631 -66 333 -100 3927 -98 1743 -66 295 -100 197 -68 793 -66 891 -132 65 -100 5247 -132 427 -66 199 -66 263 -68 629 -68 297 -100 1345 -98 263 -66 693 -130 591 -68 263 -100 299 -66 399 -68 565 -200 231 -102 231 -100 497 -68 2259 -236 297 -66 663 -100 493 -98 133 -66 597 -132 265 -166 2459 -66 1857 -134 627 -100 827 -320 705 -354 695 -354 693 -354 721 -320 727 -322 733 -678 377 -358 677 -356 705 -644 381 -672 381 -348 691 -676 383 -672 415 -640 419 -324 697 -680 383 -650 381 -654 407 -658 365 -664 395 -382 685 -350 719 -350 653 -16570 709 -380 651 -384 687 -352 691 -346 701 -336 709 -362 715 -654 409 -352 681 -354 669 -678 383 -648 417 -326 711 -646 417 -648 413 -648 415 -326 711 -642 -RAW_Data: 383 -684 377 -656 367 -684 387 -672 385 -350 685 -384 685 -386 651 -16538 737 -332 705 -334 705 -332 705 -362 707 -320 719 -354 723 -646 417 -324 697 -354 693 -678 385 -652 379 -344 691 -698 361 -686 385 -654 417 -350 685 -652 385 -674 355 -700 357 -664 391 -664 393 -364 685 -364 709 -360 677 -16510 773 -310 695 -354 709 -320 733 -320 727 -322 729 -322 733 -680 387 -326 699 -352 693 -678 385 -648 381 -330 739 -648 415 -648 413 -650 413 -328 675 -682 379 -656 371 -688 353 -686 381 -652 413 -352 689 -386 693 -348 669 -16546 733 -348 697 -344 701 -346 699 -352 685 -354 721 -354 695 -644 423 -340 699 -332 711 -650 383 -652 419 -324 721 -642 419 -646 385 -680 387 -356 687 -646 387 -682 379 -656 401 -654 385 -654 383 -352 717 -352 723 -352 659 -16524 759 -324 697 -374 675 -358 679 -356 709 -352 703 -354 703 -680 375 -358 677 -354 709 -644 385 -680 385 -326 713 -680 385 -650 415 -648 415 -326 713 -648 383 -656 405 -654 385 -654 383 -652 413 -350 689 -386 693 -348 687 -16514 759 -322 699 -348 699 -336 721 -322 709 -354 707 -354 707 -680 385 -326 703 -354 691 -680 383 -638 417 -328 709 -646 419 -650 415 -648 413 -328 675 -682 381 -654 369 -688 385 -656 381 -684 381 -350 721 -354 695 -348 689 -16510 763 -322 697 -350 703 -350 701 -348 703 -350 701 -350 701 -678 379 -350 685 -350 697 -666 379 -662 395 -326 719 -654 417 -656 385 -650 409 -358 677 -680 377 -646 379 -686 367 -680 379 -662 395 -362 687 -364 709 -358 649 -16538 763 -324 699 -348 701 -346 687 -362 711 -322 707 -356 705 -682 377 -358 685 -354 707 -644 385 -682 385 -328 711 -648 417 -648 415 -648 415 -328 679 -684 379 -656 371 -686 353 -684 383 -684 381 -350 689 -386 687 -372 653 -16534 757 -328 689 -320 737 -320 729 -320 731 -320 729 -320 731 -644 407 -360 711 -320 707 -644 387 -650 419 -326 709 -644 423 -646 417 -648 415 -326 711 -646 383 -652 407 -656 387 -656 381 -654 413 -352 689 -352 723 -338 689 -16532 729 -328 711 -354 703 -320 695 -352 727 -322 727 -322 733 -678 375 -358 685 -354 671 -678 385 -648 417 -328 709 -646 417 -648 415 -650 413 -328 679 -684 379 -656 371 -664 393 -652 387 -684 383 -350 723 -350 695 -348 665 -16554 763 -308 713 -322 713 -322 731 -320 729 -322 727 -320 735 -678 387 -326 701 -356 689 -678 385 -650 381 -346 725 -652 387 -654 415 -644 415 -354 685 -654 383 -674 355 -662 391 -662 391 -664 395 -364 687 -362 -RAW_Data: 713 -326 679 -123960 161 -132 263 -624 263 -66 327 -162 131 -1094 131 -98 723 -66 621 -66 459 -66 229 -66 329 -66 591 -66 881 -66 3747 -66 1783 -66 3219 -68 263 -100 695 -66 165 -66 99 -66 1193 -98 531 -234 263 -68 7149 -66 461 -100 3253 -66 595 -134 561 -66 325 -66 685 -66 199 -98 359 -130 99 -98 787 -100 925 -100 5157 -66 591 -98 233 -100 827 -66 1345 -66 10323 -200 3355 -100 367 -100 965 -98 495 -98 233 -102 1923 -66 6641 -68 263 -68 299 -100 705 -198 827 -100 529 -68 625 -66 493 -66 3641 -98 897 -68 333 -100 6513 -68 825 -66 597 -132 231 -100 659 -100 1099 -102 963 -98 831 -68 261 -68 299 -134 4477 -66 561 -66 927 -98 393 -66 1871 -166 327 -132 229 -100 63 -66 561 -1216 479 -414 205 -636 517 -358 235 -642 243 -612 237 -628 227 -614 237 -682 241 -604 269 -628 525 -336 513 -348 537 -346 245 -600 513 -326 265 -644 279 -606 273 -596 257 -620 281 -556 281 -594 553 -314 283 -572 549 -324 299 -586 317 -550 567 -310 319 -558 555 -300 315 -554 281 -572 571 -324 557 -312 279 -598 289 -578 313 -552 577 -320 533 -312 559 -290 573 -342 285 -580 525 -350 279 -594 547 -318 283 -578 559 -310 269 -568 567 -346 281 -578 311 -564 545 -328 545 -350 513 -318 561 -316 283 -572 253 -628 579 -316 541 -318 279 -602 289 -574 313 -552 283 -560 555 -320 531 -288 1123 -1164 537 -332 279 -576 581 -320 283 -578 277 -568 287 -568 305 -550 315 -590 315 -564 275 -600 545 -328 543 -314 559 -320 285 -546 561 -310 285 -628 263 -608 283 -584 281 -600 269 -596 291 -558 549 -314 287 -554 577 -350 281 -598 257 -582 581 -320 283 -582 527 -344 269 -566 303 -548 579 -318 593 -314 283 -564 291 -578 313 -558 545 -318 571 -314 525 -292 569 -336 319 -562 569 -312 283 -596 549 -300 317 -556 553 -320 275 -566 543 -362 281 -594 281 -580 555 -324 541 -340 549 -318 543 -320 283 -540 309 -596 555 -338 549 -318 281 -580 311 -562 291 -578 275 -586 547 -320 511 -318 1099 -1150 557 -336 283 -594 539 -318 317 -534 291 -606 273 -552 317 -556 283 -604 287 -600 277 -586 547 -348 533 -320 541 -340 285 -542 565 -302 277 -632 283 -572 287 -594 303 -576 283 -594 283 -544 565 -310 285 -562 547 -362 283 -594 279 -580 551 -322 285 -604 553 -318 285 -548 281 -602 527 -346 557 -326 277 -588 283 -596 281 -578 553 -294 571 -302 551 -314 549 -352 279 -602 545 -324 275 -590 545 -318 319 -540 555 -324 279 -560 -RAW_Data: 545 -350 317 -574 273 -594 557 -302 549 -352 521 -318 567 -294 277 -582 275 -624 537 -340 565 -290 301 -582 319 -566 285 -568 299 -544 547 -316 549 -318 1103 -1150 543 -326 275 -586 547 -352 281 -574 309 -562 257 -588 275 -590 283 -592 317 -574 271 -594 557 -336 549 -318 543 -318 283 -574 547 -290 283 -634 283 -584 277 -600 257 -604 311 -556 317 -560 541 -320 283 -538 583 -320 297 -582 317 -564 567 -314 283 -562 581 -298 313 -548 283 -572 539 -354 555 -346 245 -598 289 -580 313 -552 575 -316 535 -292 569 -304 547 -348 279 -602 555 -326 279 -594 551 -320 283 -546 565 -310 285 -562 547 -364 283 -594 281 -600 525 -344 557 -290 565 -314 555 -320 287 -562 283 -580 585 -322 555 -310 283 -588 317 -538 309 -564 289 -576 543 -312 549 -320 1071 -1150 579 -290 301 -584 545 -352 283 -574 273 -566 291 -582 277 -588 283 -592 279 -610 273 -596 557 -300 549 -352 533 -316 279 -580 543 -312 285 -596 315 -568 307 -564 291 -580 313 -556 283 -584 537 -316 283 -570 545 -356 307 -590 283 -562 565 -346 247 -594 553 -302 279 -590 283 -570 541 -356 557 -310 279 -600 289 -576 313 -554 545 -316 565 -288 569 -302 547 -348 279 -598 557 -334 283 -572 541 -322 283 -600 547 -320 245 -598 543 -328 313 -574 285 -596 533 -342 549 -316 535 -332 531 -330 273 -586 281 -606 541 -322 565 -312 279 -600 289 -576 275 -588 281 -572 571 -290 543 -304 1117 -1144 557 -326 281 -592 547 -320 281 -580 311 -564 257 -602 275 -586 283 -590 315 -572 273 -594 555 -336 549 -318 523 -320 283 -578 557 -294 277 -618 317 -560 315 -568 273 -602 257 -604 277 -590 547 -318 285 -564 537 -350 317 -560 293 -598 551 -320 283 -564 565 -312 283 -562 291 -580 543 -348 555 -318 283 -602 257 -604 275 -590 545 -318 537 -348 527 -292 569 -336 319 -560 535 -348 283 -596 551 -298 313 -556 545 -318 277 -564 579 -330 283 -594 279 -600 523 -344 561 -290 567 -312 549 -318 283 -566 279 -600 549 -324 585 -308 285 -588 281 -600 277 -572 273 -594 551 -298 543 -312 1077 -1150 585 -290 289 -606 547 -316 319 -540 309 -566 289 -576 275 -586 281 -622 277 -570 291 -602 543 -314 547 -318 569 -312 283 -564 545 -328 273 -618 283 -578 309 -562 291 -580 313 -556 283 -586 537 -314 283 -570 577 -320 311 -588 283 -598 527 -346 269 -564 573 -312 285 -554 317 -564 539 -352 553 -322 291 -578 283 -594 281 -566 563 -302 563 -292 569 -312 549 -348 281 -598 529 -340 283 -570 -RAW_Data: 573 -290 285 -604 549 -318 245 -598 545 -358 281 -572 287 -596 567 -310 547 -320 535 -316 561 -294 277 -586 277 -624 557 -316 561 -326 281 -558 171 -112742 297 -66 231 -100 133 -98 131 -66 231 -66 65 -330 231 -790 165 -400 99 -434 459 -66 233 -132 1581 -98 163 -66 1049 -132 1849 -100 1855 -98 5435 -100 301 -68 761 -66 3115 -100 231 -68 2421 -68 765 -66 167 -68 233 -134 997 -66 233 -66 1061 -66 833 -98 565 -66 369 -102 131 -100 5197 -132 853 -232 825 -100 265 -100 765 -100 631 -66 827 -66 1749 -66 265 -136 165 -66 399 -100 3821 -98 793 -66 599 -100 231 -68 635 -68 197 -134 5091 -132 299 -68 1327 -100 757 -98 629 -66 601 -66 2887 -100 1167 -98 1879 -66 131 -98 295 -162 7033 -66 655 -68 1025 -100 261 -66 295 -66 195 -132 65 -66 9407 -100 4337 -68 795 -100 361 -132 99 -66 4915 -198 819 -100 327 -64 1611 -132 429 -100 331 -66 4037 -68 729 -66 827 -398 2751 -66 295 -66 4365 -98 1187 -66 389 -100 231 -166 1945 -66 431 -450 587 -458 587 -458 587 -458 587 -458 625 -424 623 -748 309 -424 627 -392 657 -716 343 -720 307 -408 657 -694 365 -692 365 -694 363 -410 653 -382 651 -382 651 -716 321 -704 355 -702 357 -370 717 -368 673 -364 675 -16544 735 -352 673 -362 677 -356 709 -320 733 -320 733 -352 703 -680 385 -358 669 -354 693 -678 381 -638 417 -328 709 -646 417 -646 417 -648 413 -326 713 -326 713 -326 713 -648 415 -650 379 -652 409 -346 723 -346 721 -346 653 -16532 777 -296 703 -360 709 -326 709 -348 689 -352 727 -320 731 -678 385 -326 701 -354 691 -678 385 -650 381 -346 725 -652 385 -656 415 -656 381 -386 685 -352 689 -330 711 -650 385 -652 419 -648 385 -328 751 -320 727 -352 671 -16536 757 -318 709 -320 731 -322 695 -352 725 -322 727 -322 731 -678 381 -348 705 -320 697 -676 383 -670 381 -346 687 -678 385 -680 383 -682 385 -326 717 -324 717 -326 711 -672 385 -650 415 -650 379 -330 737 -324 749 -320 699 -16538 747 -328 681 -354 705 -320 733 -320 729 -320 729 -354 703 -678 377 -358 677 -356 703 -642 385 -680 385 -328 711 -680 385 -650 417 -646 417 -326 713 -326 711 -326 713 -646 417 -650 379 -652 411 -344 725 -346 723 -346 653 -16538 707 -404 657 -376 653 -380 687 -382 655 -382 691 -380 679 -668 395 -366 671 -360 675 -682 381 -650 383 -358 687 -680 383 -682 383 -682 385 -326 715 -326 713 -326 715 -682 385 -650 379 -654 409 -346 723 -346 723 -348 651 -16540 743 -324 -RAW_Data: 713 -324 717 -356 687 -354 687 -354 727 -320 729 -682 379 -350 671 -354 693 -678 381 -672 379 -350 687 -678 381 -672 381 -674 413 -348 667 -356 691 -354 689 -678 381 -672 379 -672 381 -350 721 -320 727 -354 671 -16544 747 -328 689 -354 703 -320 731 -320 729 -320 729 -322 733 -680 385 -326 701 -354 693 -678 385 -650 381 -344 693 -686 387 -670 387 -670 385 -350 685 -352 689 -350 695 -672 363 -688 393 -660 395 -328 741 -332 711 -360 685 -16498 781 -328 679 -354 707 -320 731 -318 729 -320 731 -320 733 -680 373 -360 687 -352 705 -642 389 -648 419 -326 707 -646 419 -648 417 -648 415 -326 711 -326 711 -324 713 -646 419 -648 381 -652 409 -346 725 -348 719 -348 651 -16530 775 -298 703 -360 709 -326 711 -326 713 -354 723 -320 725 -646 409 -358 677 -354 707 -644 385 -650 417 -328 709 -646 419 -648 417 -650 415 -326 711 -326 711 -326 711 -648 381 -684 379 -652 407 -346 723 -346 689 -382 651 -16542 739 -316 715 -324 715 -350 689 -354 723 -322 725 -322 729 -680 375 -358 677 -354 705 -644 385 -680 385 -328 713 -678 387 -650 415 -650 413 -328 711 -326 711 -326 713 -648 381 -684 379 -652 407 -346 723 -346 717 -316 705 -16506 781 -288 717 -354 689 -356 687 -354 723 -320 725 -320 731 -680 381 -348 705 -320 695 -676 381 -672 381 -346 687 -678 381 -674 381 -672 419 -324 695 -356 687 -354 687 -678 385 -650 381 -682 379 -346 727 -346 721 -346 653 -16534 739 -346 693 -342 699 -344 699 -342 731 -346 687 -382 685 -686 383 -352 683 -352 689 -680 357 -664 391 -362 687 -654 409 -662 395 -662 393 -366 671 -360 679 -354 703 -646 419 -646 385 -648 417 -328 711 -354 725 -354 671 -16506 783 -316 705 -322 729 -320 727 -320 727 -320 727 -352 703 -678 377 -358 677 -354 707 -644 381 -672 383 -346 691 -678 381 -672 415 -638 419 -326 699 -354 689 -350 689 -678 381 -672 381 -672 383 -348 721 -322 727 -354 669 -16544 747 -328 689 -352 703 -320 727 -320 727 -322 731 -322 733 -680 385 -326 701 -356 691 -678 385 -650 379 -346 689 -686 387 -670 387 -654 417 -352 683 -354 689 -346 687 -656 377 -686 379 -666 393 -362 687 -364 715 -358 649 -129582 163 -694 263 -100 197 -68 199 -166 199 -100 131 -200 65 -662 629 -66 625 -164 65 -100 987 -66 587 -66 163 -66 95 -66 4701 -300 1593 -66 587 -98 587 -98 1907 -100 4575 -264 195 -296 1449 -132 161 -132 195 -66 359 -132 129 -66 5365 -66 65 -132 595 -100 731 -134 299 -68 1361 -100 -RAW_Data: 3975 -100 1023 -98 197 -132 887 -98 195 -100 261 -100 229 -132 1677 -98 5099 -132 1973 -98 2701 -130 4541 -102 165 -66 163 -66 163 -132 591 -66 791 -66 199 -102 4581 -68 1487 -100 161 -98 2113 -66 469 -66 3741 -100 4299 -66 163 -64 391 -166 65 -66 2619 -100 399 -66 4573 -66 2117 -132 6427 -66 1019 -132 529 -66 165 -66 797 -200 165 -102 199 -66 4713 -68 3163 -232 993 -100 363 -98 267 -66 5573 -98 263 -98 261 -66 197 -66 131 -100 2179 -264 4751 -166 467 -100 1727 -100 233 -100 2019 -100 4073 -66 861 -132 1359 -134 201 -66 235 -430 297 -66 599 -68 727 -100 263 -68 265 -68 4727 -100 499 -64 199 -134 65 -66 329 -100 97 -164 525 -64 485 -64 5219 -66 1149 -66 229 -230 1713 -66 131 -98 97 -66 1053 -66 5951 -66 2599 -100 131 -132 955 -132 259 -132 97 -66 99 -98 691 -66 889 -66 4877 -132 1543 -66 1147 -132 65 -298 793 -100 1329 -66 563 -66 595 -232 231 -66 165 -134 4975 -100 791 -100 231 -66 793 -68 1393 -100 463 -132 597 -132 165 -100 4941 -98 425 -132 165 -132 65 -68 231 -66 1033 -66 1003 -132 5379 -166 1745 -66 5759 -66 719 -200 2589 -66 5447 -66 2057 -234 1721 -100 467 -66 5485 -100 65 -66 1187 -134 599 -68 1131 -64 5741 -230 131 -66 819 -100 297 -68 133 -68 231 -132 5413 -100 131 -166 1157 -264 331 -66 727 -66 1283 -66 1513 -98 593 -66 265 -102 233 -66 797 -100 165 -134 665 -100 791 -132 99 -168 5317 -100 299 -100 929 -66 429 -66 835 -100 763 -100 165 -100 3941 -68 231 -68 699 -68 231 -68 1091 -66 299 -100 265 -100 365 -200 531 -66 761 -100 365 -68 5283 -100 299 -100 563 -166 999 -66 657 -132 5585 -64 131 -98 97 -132 361 -66 555 -98 293 -66 1247 -66 521 -196 263 -66 497 -132 6527 -66 399 -66 729 -100 197 -68 6605 -66 1115 -132 919 -98 7149 -66 1297 -66 197 -100 1151 -98 463 -266 197 -98 427 -68 891 -68 3379 -66 397 -68 67 -100 297 -98 983 -232 165 -132 883 -68 3773 -66 263 -64 653 -166 795 -100 397 -98 5091 -68 429 -66 297 -164 227 -98 621 -164 691 -130 2735 -64 1513 -98 299 -66 963 -166 333 -66 993 -100 4049 -66 555 -100 293 -98 815 -64 133 -66 729 -66 165 -66 1493 -66 1183 -234 165 -66 267 -202 1031 -100 693 -64 261 -66 261 -66 4057 -98 327 -98 425 -98 291 -100 65 -100 931 -68 297 -68 527 -68 463 -66 5135 -66 163 -98 5887 -4482 477 -1448 469 -1438 479 -1430 485 -518 467 -1440 451 -532 451 -1440 477 -1444 -RAW_Data: 471 -1474 451 -1446 481 -518 449 -1446 483 -514 475 -520 445 -502 491 -546 453 -1470 449 -1450 473 -1432 473 -1444 479 -482 473 -1460 461 -1404 481 -1484 461 -1444 449 -1446 483 -1440 477 -522 453 -1436 479 -1414 479 -1428 473 -1444 481 -1444 471 -1440 481 -1426 473 -1446 483 -520 451 -508 491 -1402 481 -1448 469 -1474 447 -556 443 -538 467 -514 481 -518 449 -1432 483 -482 485 -1418 4439 -4500 485 -1452 479 -1408 507 -1412 479 -504 495 -1406 483 -526 449 -1432 479 -1444 471 -1450 477 -1448 469 -516 479 -1430 481 -522 449 -510 475 -488 503 -522 481 -1466 461 -1446 447 -1448 481 -1442 477 -484 481 -1440 477 -1410 481 -1456 459 -1438 479 -520 483 -506 481 -538 463 -514 479 -1408 469 -516 481 -514 509 -1440 477 -518 451 -1436 483 -524 485 -478 475 -1444 469 -514 477 -540 477 -1438 475 -520 485 -1402 485 -1420 487 -1436 473 -512 463 -514 479 -1404 4423 -4516 489 -1442 483 -1420 473 -1440 471 -506 481 -1426 471 -512 471 -1408 483 -1484 463 -1442 475 -1448 465 -514 481 -1438 477 -484 481 -512 473 -490 499 -548 481 -1420 469 -1442 487 -1418 481 -1446 463 -516 479 -1408 471 -1442 449 -1486 461 -514 481 -524 491 -510 479 -1404 509 -1410 481 -500 491 -478 475 -1476 479 -520 489 -1434 451 -526 489 -1402 483 -524 487 -1404 479 -490 481 -1468 485 -1426 473 -1440 487 -524 451 -1454 477 -508 463 -1406 483 -492 483 -1406 4439 -4518 491 -1438 481 -1420 471 -1442 487 -522 451 -1418 479 -506 495 -1404 481 -1444 503 -1418 479 -1446 469 -516 481 -1438 479 -486 483 -506 475 -520 473 -522 479 -1464 459 -1436 475 -1444 467 -1442 481 -496 491 -1404 481 -1412 481 -1462 457 -1468 481 -514 475 -1412 483 -512 475 -1444 465 -1408 483 -1414 481 -538 507 -516 485 -512 477 -514 475 -492 503 -490 483 -1414 481 -1426 487 -514 509 -516 451 -542 475 -1430 479 -1414 479 -504 493 -514 475 -482 473 -1428 4417 -4510 483 -1440 481 -1452 475 -1438 461 -512 479 -1416 487 -506 489 -1406 479 -1448 487 -1444 459 -1434 479 -520 487 -1414 477 -518 473 -502 479 -504 491 -546 449 -1466 485 -1416 471 -1438 481 -1414 493 -514 477 -1406 471 -1442 483 -1456 461 -518 483 -1446 471 -514 479 -520 485 -476 477 -516 473 -1428 481 -1440 481 -1448 473 -512 473 -1436 481 -1426 473 -512 481 -1416 481 -1426 473 -1444 475 -518 507 -1422 491 -1436 481 -496 489 -1404 481 -1418 481 -502 491 -1400 4449 -4492 493 -1438 481 -1422 469 -1444 487 -522 449 -1450 471 -486 475 -1440 483 -1454 459 -1440 479 -1446 469 -516 481 -1436 477 -486 483 -510 475 -488 501 -518 421 -129440 263 -162 65 -362 -RAW_Data: 129 -264 65 -266 199 -132 67 -332 99 -98 3287 -100 4663 -132 4127 -98 331 -68 99 -66 5149 -200 1759 -66 65 -100 1051 -132 619 -66 5371 -132 1873 -232 195 -130 657 -132 357 -66 229 -66 4517 -100 365 -64 2447 -132 261 -66 633 -100 1153 -100 593 -132 259 -66 1589 -134 229 -66 65 -66 99 -66 491 -66 3789 -100 2059 -102 301 -66 663 -98 327 -98 5013 -98 4239 -164 987 -98 197 -66 163 -66 1479 -100 897 -66 99 -166 827 -66 465 -100 67 -100 563 -100 5179 -66 2043 -100 985 -66 1355 -66 431 -66 729 -232 165 -352 689 -346 685 -364 709 -322 709 -354 705 -354 701 -680 377 -360 675 -356 709 -644 385 -682 383 -328 711 -680 383 -648 415 -648 415 -328 711 -318 717 -326 713 -680 383 -650 379 -684 377 -330 741 -324 717 -354 691 -16506 751 -352 703 -322 693 -354 693 -354 725 -320 729 -322 731 -680 383 -356 669 -354 695 -678 385 -650 379 -344 691 -696 363 -686 387 -686 383 -352 685 -350 691 -346 699 -668 365 -684 367 -682 379 -366 685 -362 709 -360 677 -16540 731 -354 667 -352 699 -350 689 -362 711 -322 713 -354 707 -680 387 -326 737 -320 691 -676 383 -638 417 -316 715 -676 387 -648 421 -644 419 -326 711 -326 709 -326 713 -642 421 -648 415 -618 413 -346 725 -348 721 -314 685 -16528 775 -298 733 -326 709 -326 711 -324 715 -350 725 -320 731 -646 421 -326 699 -354 689 -678 383 -638 415 -318 711 -678 387 -648 417 -648 417 -326 715 -324 707 -318 717 -678 387 -648 381 -684 379 -346 725 -346 719 -348 651 -16544 741 -352 691 -354 687 -356 691 -348 691 -326 747 -320 743 -646 407 -330 725 -320 707 -644 389 -648 423 -296 735 -646 421 -648 417 -646 417 -326 707 -326 709 -324 711 -646 421 -648 381 -652 411 -346 725 -314 753 -316 683 -16546 649 -538 489 -574 455 -574 487 -542 557 -442 593 -474 575 -802 263 -464 599 -428 605 -752 319 -716 321 -422 639 -720 351 -720 319 -730 361 -366 669 -362 673 -356 705 -684 345 -686 381 -682 383 -326 715 -356 723 -354 669 -16540 747 -330 715 -320 707 -320 731 -320 729 -322 729 -354 701 -680 377 -358 675 -356 707 -646 381 -672 383 -328 717 -678 385 -680 383 -648 415 -326 717 -326 713 -326 713 -646 417 -652 379 -686 375 -346 723 -346 721 -346 653 -16542 775 -316 693 -354 687 -348 699 -354 691 -348 719 -332 711 -658 403 -354 677 -356 707 -644 385 -682 385 -328 713 -646 419 -650 415 -648 415 -326 713 -326 713 -326 711 -646 417 -648 379 -652 411 -344 725 -346 721 -346 -RAW_Data: 653 -16540 741 -348 691 -348 685 -354 687 -350 697 -378 705 -332 719 -652 405 -352 677 -354 705 -644 385 -682 387 -326 713 -646 419 -648 415 -650 415 -326 713 -326 713 -326 713 -646 381 -684 379 -652 407 -346 723 -346 719 -314 705 -16508 781 -290 715 -354 687 -354 689 -354 723 -320 729 -320 731 -646 419 -324 701 -354 689 -680 385 -650 379 -350 711 -648 417 -648 413 -638 415 -326 715 -324 713 -324 713 -648 417 -650 379 -652 411 -344 725 -346 723 -348 651 -16538 745 -324 711 -324 717 -324 717 -354 687 -352 725 -322 727 -678 375 -360 685 -354 705 -644 385 -648 419 -326 709 -648 419 -648 413 -650 415 -326 707 -318 715 -326 713 -646 419 -648 379 -684 379 -344 725 -346 719 -348 653 -16544 741 -336 697 -334 703 -332 737 -328 707 -326 717 -352 725 -646 415 -348 671 -352 693 -678 381 -638 415 -318 715 -678 383 -672 383 -674 383 -348 693 -354 687 -356 689 -678 387 -648 381 -684 379 -332 737 -326 745 -320 695 -16504 781 -286 737 -322 693 -354 691 -354 727 -318 729 -322 731 -680 381 -348 705 -320 697 -678 383 -650 381 -360 707 -648 415 -650 413 -650 413 -326 677 -362 675 -360 711 -648 381 -684 379 -654 405 -344 723 -346 685 -382 651 -16544 743 -350 689 -352 685 -352 691 -346 685 -364 709 -358 711 -650 407 -352 679 -354 671 -678 385 -648 417 -328 709 -648 415 -648 415 -648 413 -328 677 -360 711 -328 713 -648 381 -684 377 -656 405 -346 689 -380 687 -382 653 -16540 741 -352 689 -320 719 -354 689 -336 719 -322 711 -352 707 -680 407 -328 709 -322 711 -644 387 -680 385 -328 711 -678 385 -648 417 -648 415 -326 715 -326 713 -326 711 -648 419 -648 381 -652 409 -346 723 -348 719 -348 651 -130012 261 -132 261 -198 199 -130 791 -66 531 -66 231 -100 463 -66 299 -66 299 -100 785 -332 131 -66 267 -66 4069 -66 657 -100 589 -100 129 -100 523 -166 1325 -66 99 -132 4995 -98 429 -132 1151 -100 231 -130 361 -98 261 -66 1709 -98 165 -98 4413 -100 723 -132 63 -66 259 -98 359 -66 427 -68 665 -266 297 -330 961 -166 2625 -66 763 -98 65 -98 327 -166 463 -198 269 -100 529 -132 97 -98 227 -66 259 -162 327 -198 429 -66 295 -66 133 -162 3959 -234 789 -266 97 -230 827 -98 129 -100 97 -66 97 -66 361 -98 65 -66 525 -100 165 -64 757 -266 163 -166 3769 -166 563 -132 399 -68 563 -68 363 -66 133 -100 263 -66 529 -100 1065 -102 989 -132 1091 -100 729 -66 497 -98 133 -66 67 -98 565 -66 265 -234 -RAW_Data: 467 -166 499 -100 97 -66 4339 -100 165 -134 2121 -66 697 -134 531 -100 3557 -100 829 -130 461 -98 263 -300 199 -68 199 -66 733 -66 431 -132 365 -66 435 -66 637 -68 2411 -66 821 -66 65 -100 331 -100 693 -66 527 -200 131 -268 1959 -66 363 -464 5279 -132 199 -66 165 -200 329 -64 197 -98 1511 -132 525 -66 4411 -66 99 -100 131 -66 1161 -100 431 -66 165 -66 4607 -66 337 -68 267 -134 1623 -66 497 -66 295 -100 401 -100 65 -100 4201 -66 165 -68 233 -168 825 -100 599 -100 1857 -68 2819 -66 325 -164 359 -98 131 -298 99 -66 197 -66 4209 -100 1031 -66 131 -100 263 -66 267 -66 363 -100 1163 -132 633 -102 3097 -100 799 -66 265 -66 331 -100 763 -134 897 -100 333 -66 599 -66 467 -132 99 -66 3491 -66 821 -66 327 -66 557 -164 661 -68 165 -362 197 -98 757 -98 99 -98 525 -98 557 -134 1027 -132 197 -66 663 -100 991 -66 493 -68 131 -132 863 -132 529 -132 165 -102 397 -66 4095 -200 2929 -66 689 -66 397 -66 229 -98 163 -96 163 -98 227 -66 1063 -330 165 -66 495 -100 1021 -66 2569 -132 667 -134 331 -66 329 -64 555 -100 329 -66 227 -230 2225 -66 5081 -98 265 -66 669 -132 363 -100 265 -66 263 -132 731 -100 497 -98 199 -100 431 -166 233 -100 3069 -66 721 -132 329 -64 795 -66 535 -66 265 -100 531 -66 233 -3824 129 -706 305 -696 295 -688 323 -686 351 -644 353 -646 373 -644 375 -592 427 -584 421 -1078 909 -610 413 -602 399 -1080 451 -554 951 -1078 937 -552 455 -548 451 -1084 949 -1046 971 -516 481 -1046 449 -554 473 -542 475 -516 983 -1046 947 -552 451 -530 493 -1042 973 -522 473 -538 473 -540 471 -1040 975 -1006 477 -550 473 -508 1001 -522 487 -534 465 -548 451 -1044 447 -51358 455 -608 373 -628 365 -654 355 -652 353 -658 373 -640 373 -600 401 -618 389 -614 383 -622 415 -566 443 -580 407 -590 425 -1080 943 -564 413 -578 445 -1062 455 -548 943 -1078 947 -562 463 -548 451 -1044 967 -1050 983 -518 487 -1008 477 -524 485 -538 477 -510 1007 -1010 973 -556 453 -546 467 -1042 973 -542 447 -556 473 -510 475 -1044 979 -1016 499 -514 481 -526 991 -514 479 -522 483 -536 487 -1012 493 -51302 493 -3786 199 -670 339 -656 329 -684 321 -646 385 -628 373 -646 375 -626 365 -618 419 -586 417 -1088 909 -610 407 -590 427 -1082 415 -576 939 -1080 941 -584 425 -1074 941 -542 443 -1078 973 -1046 451 -548 445 -550 967 -554 475 -508 473 -1044 977 -1054 957 -1044 465 -516 483 -546 975 -520 485 -1048 461 -514 -RAW_Data: 483 -546 445 -552 975 -1018 979 -552 455 -1044 977 -51850 457 -4674 299 -704 305 -694 297 -720 323 -654 355 -626 385 -642 341 -632 401 -620 389 -1116 883 -610 413 -602 401 -1112 419 -552 943 -1066 937 -590 427 -1078 449 -556 951 -1044 475 -556 921 -1076 445 -550 981 -1030 985 -1012 485 -522 977 -528 495 -1010 479 -554 975 -506 479 -538 473 -548 453 -544 481 -1010 473 -554 985 -530 465 -514 481 -546 479 -1010 473 -556 475 -51320 459 -688 263 -754 225 -750 289 -742 297 -640 339 -658 365 -652 355 -648 353 -644 363 -640 375 -626 397 -586 419 -584 417 -1104 911 -578 445 -560 429 -1080 449 -554 937 -1082 941 -1082 947 -558 421 -584 449 -1034 455 -548 483 -516 479 -540 967 -1050 977 -520 489 -1038 443 -552 471 -538 967 -554 453 -540 467 -1042 977 -522 469 -544 477 -1016 481 -514 1009 -1014 481 -534 963 -1044 977 -554 443 -111144 233 -134 197 -196 131 -132 2559 -98 699 -98 233 -100 895 -100 897 -98 297 -230 295 -64 527 -66 895 -132 65 -98 4231 -134 667 -100 65 -66 297 -100 231 -102 563 -100 2053 -300 3873 -100 957 -66 291 -66 1225 -66 1095 -100 1029 -98 235 -100 4947 -66 131 -132 1159 -232 4881 -130 1649 -130 429 -66 463 -66 327 -266 133 -66 5073 -66 229 -100 697 -134 1353 -66 133 -130 4655 -264 299 -132 1065 -100 65 -100 199 -66 631 -66 1531 -98 233 -100 4353 -66 661 -100 233 -66 167 -68 1657 -66 267 -100 397 -98 761 -66 493 -68 761 -64 823 -98 525 -98 163 -66 197 -98 231 -66 167 -66 133 -98 953 -66 689 -66 735 -66 163 -98 229 -100 595 -168 195 -130 263 -132 957 -66 493 -98 65 -132 723 -98 431 -166 4275 -132 333 -100 97 -68 827 -66 491 -198 327 -264 4271 -134 1087 -100 797 -134 131 -302 695 -66 261 -236 465 -66 4191 -66 663 -100 433 -66 1325 -100 229 -132 295 -100 261 -100 431 -66 531 -130 195 -98 4647 -66 229 -66 359 -264 525 -98 365 -232 165 -264 457 -100 231 -66 629 -366 229 -66 3411 -68 1163 -132 763 -66 165 -66 1657 -66 393 -132 521 -262 5821 -132 1221 -98 129 -100 1345 -66 425 -198 165 -66 525 -66 299 -100 465 -66 497 -100 599 -298 4711 -100 397 -66 99 -132 627 -66 793 -98 327 -98 97 -68 265 -66 99 -98 265 -1152602 163 -1918 65 -330 197 -98 97 -198 721 -66 897 -232 4175 -166 299 -134 299 -132 331 -100 363 -66 499 -166 527 -98 1327 -68 931 -66 701 -198 265 -298 131 -100 301 -132 1029 -132 295 -134 3515 -66 197 -100 427 -100 99 -100 165 -98 231 -100 -RAW_Data: 4709 -232 163 -66 129 -96 1557 -100 229 -64 621 -100 4377 -100 1895 -66 2321 -66 4485 -66 167 -66 67 -66 1395 -68 65 -132 429 -134 1855 -68 231 -100 5991 -366 629 -100 2525 -68 233 -264 295 -98 853 -132 4433 -66 163 -66 457 -98 657 -66 331 -66 825 -66 785 -166 293 -66 559 -100 4747 -228 887 -66 895 -130 1457 -100 6935 -100 229 -66 329 -162 1051 -66 461 -100 97 -100 4427 -66 859 -68 299 -100 165 -100 1249 -132 197 -198 4129 -66 299 -268 927 -164 557 -98 261 -66 2419 -66 4739 -100 97 -66 2309 -66 97 -66 987 -198 325 -64 5109 -66 199 -100 1059 -66 723 -100 763 -100 5355 -98 201 -134 265 -100 165 -100 827 -66 595 -100 297 -100 231 -132 4787 -100 361 -100 429 -200 733 -66 633 -66 531 -330 4215 -68 1361 -200 5943 -66 1117 -132 953 -66 261 -132 199 -134 235 -200 261 -66 165 -132 65 -98 395 -266 197 -98 791 -134 229 -100 1195 -68 363 -100 99 -66 525 -98 759 -66 663 -98 163 -98 3411 -100 1033 -132 133 -134 133 -100 363 -100 663 -66 131 -66 331 -100 299 -134 65 -66 1025 -100 397 -66 2051 -66 697 -66 605 -390 415 -398 409 -400 393 -438 387 -410 387 -410 385 -448 383 -414 385 -416 387 -416 387 -448 387 -4030 787 -422 449 -810 425 -816 423 -838 417 -844 399 -852 805 -456 811 -412 837 -418 415 -814 823 -434 385 -878 385 -848 799 -474 383 -850 819 -452 405 -820 385 -844 415 -842 403 -848 809 -456 779 -446 417 -862 413 -814 417 -852 785 -452 799 -436 811 -450 415 -832 807 -444 411 -838 803 -446 407 -850 807 -410 807 -452 819 -418 817 -448 815 -434 809 -450 409 -840 817 -418 417 -818 417 -862 773 -476 773 -450 803 -446 409 -866 419 -848 793 -436 387 -838 419 -846 793 -438 415 -846 409 -846 811 -446 813 -422 423 -838 783 -448 413 -838 379 -866 807 -450 383 -862 807 -450 813 -412 415 -816 387 -15812 443 -358 433 -386 409 -386 447 -350 451 -382 419 -384 421 -384 419 -386 421 -386 421 -388 419 -388 419 -4024 837 -410 415 -814 421 -848 399 -822 413 -850 419 -824 841 -444 809 -422 815 -416 413 -842 801 -438 411 -844 409 -846 811 -446 413 -832 841 -414 409 -840 419 -812 425 -820 393 -866 811 -418 841 -430 415 -846 419 -820 405 -850 807 -418 819 -416 809 -454 395 -854 817 -450 419 -848 807 -412 413 -850 785 -450 811 -432 809 -412 837 -416 831 -458 811 -452 389 -854 787 -454 407 -824 413 -814 821 -448 815 -432 809 -450 395 -864 421 -814 827 -434 387 -840 421 -814 -RAW_Data: 825 -434 415 -846 421 -816 837 -444 809 -420 423 -806 813 -450 383 -840 417 -864 807 -444 381 -848 821 -454 805 -434 379 -806 417 -15798 421 -428 321 -504 321 -474 323 -508 293 -508 325 -468 323 -476 365 -444 365 -444 333 -466 359 -464 331 -4092 749 -490 351 -870 381 -882 361 -888 381 -838 411 -860 807 -450 815 -434 809 -414 417 -824 803 -442 413 -846 417 -854 773 -476 381 -882 793 -426 413 -844 387 -854 397 -826 417 -848 787 -462 813 -448 395 -864 419 -812 429 -818 821 -418 809 -448 783 -460 411 -846 785 -486 399 -850 811 -414 415 -828 801 -442 809 -450 789 -456 807 -452 785 -458 805 -450 411 -836 819 -418 419 -820 415 -830 805 -474 777 -450 815 -450 417 -852 413 -812 811 -424 411 -838 419 -844 795 -436 385 -878 409 -844 809 -444 813 -424 423 -838 777 -450 413 -842 399 -860 777 -448 413 -868 803 -448 791 -424 411 -812 413 -15768 435 -396 429 -358 435 -378 411 -410 413 -380 439 -394 409 -406 379 -412 407 -414 409 -382 413 -410 417 -3998 837 -414 409 -840 421 -810 427 -820 421 -838 421 -844 797 -472 805 -418 815 -408 419 -850 799 -438 415 -816 421 -852 799 -438 411 -874 803 -416 413 -842 403 -836 413 -842 395 -826 813 -452 807 -458 407 -846 419 -822 409 -816 839 -118766 99 -132 819 -98 917 -132 233 -100 167 -66 533 -66 525 -64 163 -98 947 -66 327 -66 425 -132 229 -66 331 -68 1949 -98 261 -132 261 -66 1187 -66 295 -100 5279 -68 595 -98 527 -66 1293 -68 97 -134 527 -68 4543 -66 229 -64 927 -332 199 -132 363 -264 825 -100 829 -68 565 -200 3743 -66 2027 -66 1425 -100 1301 -66 229 -196 99 -98 4645 -422 353 -446 385 -418 385 -416 387 -450 357 -450 355 -450 357 -450 359 -450 357 -450 359 -450 359 -4096 779 -448 771 -474 777 -444 779 -480 775 -482 367 -880 779 -446 387 -918 781 -418 819 -416 813 -454 779 -448 385 -882 379 -854 779 -484 385 -872 787 -454 793 -456 781 -448 385 -850 405 -854 779 -448 803 -458 785 -484 395 -854 787 -430 809 -446 383 -842 793 -486 383 -856 777 -478 379 -906 381 -824 803 -432 807 -446 785 -442 783 -482 785 -454 381 -872 375 -918 787 -418 815 -418 391 -860 785 -448 385 -876 787 -460 775 -484 387 -890 385 -834 777 -450 385 -852 417 -854 379 -856 381 -882 369 -876 777 -482 791 -454 771 -446 377 -886 779 -446 383 -874 769 -458 819 -450 785 -456 779 -420 383 -16402 383 -444 385 -416 385 -448 355 -452 355 -450 359 -450 355 -450 357 -452 359 -450 -RAW_Data: 357 -450 359 -450 357 -4096 777 -448 799 -446 761 -464 775 -478 769 -454 419 -846 799 -466 381 -876 799 -440 801 -440 775 -448 811 -448 379 -872 385 -848 803 -464 381 -876 781 -454 783 -448 783 -448 411 -836 421 -848 763 -494 775 -482 779 -480 379 -858 779 -448 777 -448 379 -866 787 -484 353 -886 785 -462 383 -878 411 -854 775 -444 769 -454 807 -454 789 -452 803 -446 377 -864 419 -878 779 -436 809 -414 415 -860 775 -434 383 -876 787 -486 765 -490 349 -906 379 -858 777 -446 383 -842 409 -856 387 -850 393 -856 391 -856 785 -518 763 -460 779 -446 385 -852 803 -430 381 -872 801 -452 783 -472 777 -450 785 -428 377 -16446 411 -422 355 -478 353 -448 353 -448 359 -448 361 -450 357 -450 357 -450 357 -450 359 -446 353 -450 361 -4086 807 -442 769 -454 809 -452 783 -448 773 -480 381 -860 777 -472 383 -908 769 -448 801 -452 779 -432 809 -448 365 -882 387 -846 795 -456 381 -910 787 -426 809 -446 779 -446 379 -862 407 -846 783 -472 779 -482 777 -482 391 -860 783 -446 779 -448 395 -852 777 -482 385 -872 773 -448 417 -888 385 -834 785 -456 777 -448 801 -456 769 -458 809 -444 391 -858 421 -878 777 -434 807 -444 377 -856 777 -448 387 -872 803 -450 801 -452 389 -886 391 -858 757 -454 381 -854 391 -856 387 -848 397 -866 377 -872 797 -488 781 -470 773 -446 369 -878 751 -476 383 -848 787 -482 763 -494 777 -448 789 -400 401 -16398 419 -418 355 -434 385 -442 351 -446 383 -450 353 -450 357 -450 355 -450 357 -448 359 -450 357 -450 357 -4088 805 -414 803 -458 781 -446 781 -482 783 -450 383 -882 771 -468 383 -884 783 -456 783 -446 779 -450 783 -456 417 -844 371 -888 777 -482 367 -910 769 -452 771 -460 777 -446 379 -886 385 -848 787 -456 807 -446 815 -482 379 -858 783 -446 779 -448 395 -852 775 -480 357 -872 803 -448 409 -890 383 -850 763 -460 779 -446 803 -460 777 -480 781 -448 381 -870 379 -914 769 -440 799 -434 383 -872 767 -486 383 -856 777 -478 771 -480 379 -890 383 -850 767 -462 385 -872 351 -882 367 -866 383 -878 365 -878 775 -516 767 -460 779 -448 383 -848 803 -464 349 -876 785 -486 763 -492 777 -412 787 -454 359 -16460 421 -392 415 -392 405 -412 379 -432 385 -440 351 -448 385 -416 385 -418 387 -450 355 -450 357 -450 357 -4092 775 -446 799 -454 769 -458 805 -444 769 -452 405 -882 785 -446 383 -884 813 -418 809 -454 785 -452 779 -448 375 -886 387 -848 787 -456 379 -908 801 -418 785 -452 813 -448 -RAW_Data: 379 -838 421 -846 767 -494 777 -480 777 -480 379 -858 779 -446 779 -448 379 -898 755 -482 385 -842 803 -446 409 -890 383 -850 765 -462 775 -446 801 -458 775 -484 783 -450 379 -876 383 -886 783 -474 775 -446 365 -880 779 -448 385 -850 793 -486 779 -468 381 -884 391 -856 779 -446 383 -850 383 -854 409 -854 355 -900 385 -848 805 -464 813 -452 779 -420 419 -844 763 -460 383 -876 799 -454 769 -494 775 -444 771 -426 411 -16408 355 -436 383 -444 383 -416 385 -418 385 -452 353 -450 357 -450 357 -452 357 -446 355 -446 387 -410 391 -4094 775 -448 789 -430 807 -446 777 -450 813 -450 385 -848 807 -468 381 -884 783 -456 777 -123134 99 -266 65 -594 99 -232 301 -134 2911 -134 861 -66 529 -98 569 -66 1593 -68 429 -66 5405 -132 263 -98 821 -66 397 -66 361 -66 491 -100 165 -66 495 -66 3677 -132 825 -68 529 -134 299 -66 165 -68 661 -68 203 -134 135 -100 429 -100 397 -98 859 -66 365 -166 399 -98 2811 -68 229 -66 567 -164 1153 -168 167 -134 265 -100 631 -66 1493 -132 65 -98 199 -68 4269 -132 365 -202 1493 -132 299 -66 131 -66 465 -66 231 -98 229 -96 165 -130 3593 -100 927 -100 231 -200 1825 -68 265 -66 1557 -100 65 -100 463 -68 495 -100 2591 -232 1027 -100 229 -98 461 -66 1847 -100 265 -66 4369 -270 131 -100 131 -66 1725 -66 397 -98 1659 -66 329 -132 6605 -234 397 -134 1427 -100 231 -166 5117 -66 663 -98 499 -66 1331 -66 931 -66 563 -166 4219 -66 265 -164 491 -166 263 -164 557 -100 1483 -100 361 -98 461 -66 497 -102 4575 -100 1327 -98 265 -234 131 -100 399 -100 565 -100 637 -100 463 -64 3975 -166 197 -202 333 -132 199 -132 693 -68 631 -66 165 -100 265 -66 297 -200 165 -166 199 -364 3717 -198 761 -66 197 -134 395 -66 99 -66 695 -198 797 -132 397 -200 431 -102 6087 -198 199 -100 399 -100 361 -100 333 -100 827 -166 1135 -132 333 -100 497 -68 3915 -130 363 -98 897 -66 99 -66 297 -230 395 -66 195 -66 5099 -100 687 -66 915 -66 165 -66 95 -230 1181 -98 593 -98 301 -198 4807 -132 431 -232 367 -198 629 -164 199 -68 65 -100 331 -100 431 -132 1397 -66 165 -332 297 -66 1131 -466 533 -132 463 -198 861 -98 293 -66 4561 -100 459 -66 691 -130 557 -292 393 -66 161 -66 425 -66 163 -130 1147 -96 4103 -100 399 -132 395 -66 429 -66 131 -68 197 -98 199 -98 167 -100 65 -232 67 -132 165 -68 235 -66 467 -68 1027 -66 4281 -198 261 -66 861 -166 231 -198 333 -66 -RAW_Data: 231 -196 1265 -100 133 -68 933 -100 7293 -66 331 -132 295 -68 1157 -98 261 -132 1223 -200 929 -98 1291 -68 3909 -132 199 -100 2729 -66 195 -134 829 -132 263 -262 329 -68 1559 -100 497 -66 5119 -64 949 -66 533 -166 397 -68 559 -98 263 -98 951 -66 3273 -134 665 -100 65 -100 231 -66 267 -132 267 -100 1131 -132 133 -66 129 -130 885 -66 193 -164 229 -264 65 -66 889 -130 259 -164 689 -66 4127 -130 229 -66 1545 -98 199 -100 131 -100 2059 -66 329 -98 197 -66 4261 -166 791 -98 199 -68 465 -66 233 -66 265 -66 2983 -64 327 -98 229 -66 2403 -132 259 -98 953 -100 1657 -100 761 -498 799 -66 433 -200 495 -98 229 -66 1179 -68 5479 -232 627 -66 693 -66 265 -66 265 -66 463 -100 231 -68 499 -66 133 -100 299 -66 4899 -66 195 -164 621 -130 229 -130 231 -98 261 -66 97 -334 397 -100 425 -98 729 -134 65 -134 367 -102 463 -66 3197 -132 893 -66 495 -164 461 -66 197 -164 1715 -100 663 -66 99 -166 265 -100 201 -98 233 -66 133 -100 829 -132 361 -68 561 -98 199 -66 431 -134 263 -100 2071 -100 661 -66 461 -98 227 -66 1149 -66 297 -98 631 -98 329 -66 463 -66 263 -66 951 -64 3587 -66 1413 -66 919 -66 689 -132 847 -132 459 -100 161 -196 4049 -66 301 -68 397 -134 427 -98 229 -100 263 -134 335 -68 397 -98 259 -100 823 -100 399 -66 165 -66 3571 -132 563 -166 231 -100 197 -132 867 -66 433 -68 761 -98 335 -100 99 -98 99 -66 1751 -230 1019 -100 765 -100 231 -68 365 -134 4643 -66 367 -68 297 -66 531 -166 1333 -66 531 -66 67 -98 959 -66 791 -66 3787 -66 261 -132 721 -362 133 -66 897 -134 629 -100 1395 -368 1127 -132 895 -100 463 -66 1793 -66 5239 -66 331 -66 1095 -68 433 -68 6899 -98 1527 -132 267 -66 67 -66 131 -100 495 -66 4027 -68 999 -132 959 -134 265 -66 199 -68 299 -132 999 -134 65 -66 497 -98 67 -66 265 -132 4751 -66 231 -98 263 -98 463 -66 99 -66 859 -66 327 -66 435 -66 2987 -100 993 -68 1025 -66 1491 -68 1095 -100 297 -66 4409 -98 787 -132 2569 -66 957 -68 795 -64 593 -100 335 -66 1675 -100 7041 -134 1727 -134 65 -100 1029 -132 1027 -66 4653 -66 65 -66 995 -100 1129 -66 267 -66 231 -68 469 -66 465 -134 4047 -68 165 -100 597 -166 729 -100 665 -66 1115 -68 293 -130 297 -66 1607 -398 393 -410 409 -410 415 -378 413 -412 413 -382 417 -396 419 -396 409 -382 409 -404 423 -408 385 -4052 417 -820 401 -820 809 -450 -RAW_Data: 823 -420 417 -848 797 -472 381 -876 791 -428 413 -844 817 -426 379 -878 385 -848 403 -852 807 -452 379 -878 801 -452 801 -438 385 -838 419 -846 399 -852 379 -850 315 -110416 645 -1548 1691 -486 1667 -518 1695 -488 1671 -498 1697 -508 571 -1588 583 -1570 561 -1574 1689 -504 583 -1560 1679 -520 587 -1574 1677 -488 1701 -484 1697 -520 1677 -492 1693 -488 581 -1566 591 -1574 601 -1546 607 -1568 577 -1566 581 -1576 579 -16394 591 -1548 1679 -518 1671 -520 1679 -516 1663 -514 1689 -486 583 -1580 579 -1578 579 -1566 1701 -494 571 -1568 1697 -514 563 -1570 1693 -506 1669 -516 1695 -496 1681 -518 1667 -506 573 -1576 603 -1544 613 -1568 597 -1538 607 -1566 581 -1580 577 -16400 619 -1534 1703 -494 1677 -480 1697 -506 1675 -518 1669 -518 587 -1580 581 -1562 579 -1576 1683 -488 589 -1582 1677 -524 583 -1570 1653 -510 1703 -518 1641 -518 1693 -502 1677 -516 579 -1570 579 -1578 581 -1566 615 -1566 585 -1564 585 -1580 585 -16408 595 -1546 1685 -518 1673 -490 1669 -548 1665 -518 1675 -494 587 -1552 595 -1570 605 -1556 1673 -520 581 -1578 1665 -520 581 -1558 1701 -484 1681 -516 1667 -516 1697 -508 1675 -490 603 -1570 577 -1566 593 -1572 603 -1576 575 -1568 579 -1578 579 -16400 625 -1554 1667 -500 1699 -482 1691 -506 1679 -518 1673 -520 585 -1566 579 -1560 611 -1548 1697 -488 577 -1602 1651 -518 583 -1578 1667 -508 1691 -522 1675 -512 1665 -502 1699 -482 605 -1574 585 -1566 601 -1570 577 -1576 581 -1576 579 -1566 579 -16452 591 -1534 1693 -508 1683 -518 1673 -520 1673 -488 1711 -514 575 -1562 589 -1570 603 -1568 1667 -518 589 -1578 1677 -486 605 -1570 1701 -484 1701 -504 1697 -514 1667 -510 1717 -518 581 -1578 615 -91426 233 -298 197 -332 593 -162 261 -98 131 -68 163 -692 821 -132 227 -64 131 -66 197 -132 163 -66 263 -68 363 -134 461 -98 65 -66 229 -298 295 -66 3173 -64 987 -164 425 -98 193 -98 693 -66 723 -196 391 -496 431 -100 229 -68 499 -100 63 -66 165 -66 397 -98 133 -100 533 -166 1179 -166 361 -100 3973 -98 825 -100 131 -98 723 -66 1053 -66 131 -68 197 -100 199 -100 331 -66 165 -100 429 -68 231 -66 567 -66 533 -100 593 -68 1463 -66 2093 -100 599 -66 361 -66 361 -132 261 -462 263 -66 621 -98 555 -100 131 -68 4609 -200 199 -164 1033 -66 863 -132 131 -68 331 -100 4565 -98 229 -66 1313 -130 589 -166 3959 -100 531 -100 263 -132 361 -166 229 -64 165 -66 231 -328 261 -66 229 -66 165 -100 235 -100 299 -132 265 -266 165 -66 4417 -100 261 -68 1625 -64 165 -98 229 -160 259 -98 99 -332 593 -166 167 -102 -RAW_Data: 199 -66 1063 -98 267 -100 263 -332 5513 -64 229 -232 685 -232 661 -98 431 -66 1021 -66 4085 -64 655 -66 2313 -66 531 -66 263 -100 197 -134 67 -132 901 -66 1591 -66 4217 -100 827 -100 165 -66 299 -166 1987 -166 4097 -66 365 -66 131 -332 131 -100 625 -66 361 -98 131 -196 1679 -66 163 -98 293 -100 2275 -98 1689 -66 825 -98 627 -164 297 -398 461 -66 4707 -66 795 -134 329 -132 597 -66 777 -6038 371 -1818 167 -668 693 -622 1573 -638 465 -1666 503 -1626 541 -1622 1635 -518 577 -1578 1669 -512 567 -1606 1667 -502 1677 -516 1693 -500 1673 -518 1675 -522 587 -1562 581 -1558 625 -1546 587 -1558 611 -1560 571 -1568 609 -16352 633 -1548 1689 -476 1707 -490 1687 -482 1709 -502 1675 -520 579 -1578 577 -1568 577 -1568 1705 -486 581 -1586 1695 -486 579 -1568 1679 -520 1673 -520 1669 -508 1707 -498 1677 -514 577 -1566 615 -1550 581 -1568 615 -1532 617 -1546 617 -1532 615 -16406 599 -1530 1691 -514 1695 -484 1679 -530 1659 -518 1669 -514 601 -1568 577 -1578 577 -1568 1699 -492 573 -1576 1707 -486 577 -1600 1655 -518 1669 -530 1691 -486 1673 -514 1691 -502 585 -1576 585 -1580 577 -1592 573 -1576 571 -1570 613 -1570 559 -16430 619 -1538 1703 -462 1723 -484 1697 -488 1691 -488 1715 -462 635 -1534 607 -1556 589 -1580 1685 -488 621 -1538 1711 -492 589 -1548 1717 -478 1693 -514 1687 -504 1693 -484 1705 -482 605 -1570 579 -1566 619 -1550 581 -1580 579 -1568 615 -1536 619 -16382 625 -1534 1709 -490 1683 -514 1673 -504 1677 -518 1671 -518 587 -1560 577 -1588 587 -1580 1681 -482 609 -1558 1677 -520 581 -1576 1663 -530 1659 -518 1673 -514 1691 -504 1697 -510 587 -1572 575 -1564 591 -1570 603 -1574 577 -1568 579 -1578 579 -16430 599 -1564 1693 -484 1695 -486 1675 -500 1713 -484 1709 -488 579 -1568 593 -1576 603 -1570 1665 -520 581 -1560 1699 -482 603 -1580 1677 -518 1667 -520 1671 -488 1711 -482 1699 -516 599 -1570 577 -1578 577 -1568 579 -1570 617 -1566 581 -1560 587 -16424 629 -1542 1677 -482 1695 -514 1687 -484 1711 -494 1675 -514 571 -1584 587 -1572 599 -1542 1695 -520 585 -1544 1701 -510 593 -1568 1665 -514 1689 -506 1669 -508 1679 -528 1669 -520 579 -1576 579 -1568 581 -1582 581 -1578 579 -1568 615 -1564 585 -16412 631 -1540 1683 -516 1671 -510 1653 -506 1691 -518 1671 -514 601 -1570 577 -1578 577 -1578 1665 -498 603 -1566 1693 -514 563 -1576 1699 -506 1663 -514 1701 -478 1687 -520 1669 -500 603 -1568 605 -1558 589 -1574 601 -1542 607 -1576 577 -1576 577 -16434 597 -1534 1695 -516 1687 -488 1707 -486 1683 -518 1693 -496 583 -1574 599 -1542 605 -1578 1665 -500 611 -1550 1711 -478 605 -1558 -RAW_Data: 1679 -518 1675 -502 1699 -482 1693 -506 1679 -510 613 -1562 581 -1590 569 -1570 605 -1560 587 -1572 599 -1542 607 -16400 625 -1560 1669 -508 1677 -518 1667 -518 1695 -486 1709 -464 601 -1566 607 -1560 589 -1576 1691 -476 615 -1562 1677 -486 625 -1542 1709 -486 1703 -484 1697 -520 1673 -506 1695 -498 585 -1570 603 -1544 609 -1572 579 -1570 583 -1580 581 -1582 579 -16410 629 -1524 1715 -484 1679 -518 1673 -516 1663 -518 1697 -504 583 -1576 577 -1592 587 -1552 1681 -514 579 -1580 1681 -516 577 -1566 1705 -494 1689 -488 1705 -486 1695 -520 1675 -522 587 -1574 579 -1584 581 -1576 599 -1570 575 -1578 579 -1564 613 -89724 99 -1166 129 -1158 167 -532 163 -98 361 -198 299 -298 197 -66 2489 -66 3797 -66 1937 -66 1479 -98 557 -66 361 -66 1709 -66 6741 -98 1889 -132 5693 -68 7367 -100 8643 -100 2655 -100 3727 -66 1023 -66 865 -66 1161 -100 6287 -66 3987 -66 1961 -132 3307 -68 2183 -66 2429 -132 4179 -66 753 -66 461 -66 621 -64 5761 -100 331 -66 1683 -198 465 -66 629 -68 7341 -66 357 -198 261 -66 593 -234 331 -100 593 -64 293 -64 1611 -100 459 -100 297 -100 561 -134 1725 -66 6901 -100 1407 -68 8017 -200 1989 -66 5721 -100 199 -166 1251 -66 359 -130 9965 -66 4361 -100 819 -100 1051 -66 2187 -64 555 -66 861 -66 5889 -66 -RAW_Data: 361 -4326 2555 -28090 65 -302 65 -1890 131 -2062 2547 -95068 2565 -48020 67 -47036 2551 -30016 2561 -47778 67 -14884 2481 -95118 2537 -95132 2549 -48496 65 -21526 2571 -16342 97 -4142 65 -1938 2543 -95008 2551 -44030 1579 -548 1469 -588 433 -1568 469 -1540 479 -1550 1489 -538 473 -1574 1467 -538 1493 -566 443 -22130 693 -3742 321 -1702 341 -1690 1353 -628 391 -1666 1401 -584 1445 -596 391 -22192 1487 -554 1471 -550 447 -1584 453 -1568 445 -1578 1469 -550 459 -1598 1465 -540 1461 -576 445 -22164 1479 -546 1501 -544 453 -1568 447 -1580 445 -1584 1451 -560 443 -1594 1473 -548 1479 -556 441 -22160 1475 -548 1487 -554 449 -1578 451 -1586 451 -1572 1469 -552 449 -1578 1473 -558 1469 -552 449 -22142 1507 -546 1479 -548 457 -1574 447 -1580 447 -1584 1475 -554 441 -1592 1465 -546 1473 -546 483 -22132 1157 -1082 67 -166 65 -198 65 -1150 257 -1706 339 -1666 365 -1642 1409 -610 407 -1626 1417 -620 1435 -580 409 -22184 1495 -572 1463 -552 447 -1584 453 -1574 447 -1580 1473 -548 459 -1568 1501 -520 1501 -552 463 -22124 535 -7674 339 -1680 1345 -678 361 -1664 1395 -618 1411 -646 373 -22194 1511 -526 1477 -550 483 -1542 479 -1562 471 -1566 1483 -546 445 -1574 1471 -534 1497 -548 483 -169938 2539 -89172 167 -5768 2551 -93014 967 -252 1571 -5844 2503 -87590 65 -5948 2523 -64008 1605 -520 1473 -554 449 -1584 449 -1576 447 -1546 1505 -546 451 -1574 1469 -546 1475 -548 459 -22160 1525 -506 1501 -516 479 -1550 469 -1570 469 -1570 1471 -538 471 -1568 1489 -516 1501 -554 443 -22152 1507 -522 1511 -518 481 -1580 451 -1542 483 -1548 1505 -546 451 -1570 1471 -552 1473 -548 455 -22180 1505 -522 1501 -544 451 -1560 475 -1548 481 -1556 1477 -552 475 -1546 1505 -516 1517 -516 473 -22148 1515 -518 1499 -546 485 -1570 443 -1580 447 -1582 1475 -526 483 -1548 1507 -546 1479 -558 449 -22138 1495 -586 1437 -616 403 -1594 429 -1608 417 -1606 1465 -556 433 -1606 1465 -536 1499 -544 445 -22170 1401 -792 1245 -770 271 -1716 325 -1680 359 -1664 1397 -620 393 -1644 1407 -614 1433 -586 429 -22202 1477 -572 1469 -548 449 -1576 457 -1578 479 -1544 1473 -590 429 -1570 1491 -540 1509 -554 429 -22196 1481 -556 1467 -580 439 -1592 431 -1608 415 -1608 1441 -586 463 -1568 1467 -552 1489 -534 473 -22166 1485 -574 1469 -580 453 -1572 443 -1578 447 -1578 1477 -554 447 -1580 1479 -550 1481 -550 475 -22160 1525 -542 1467 -540 483 -1574 447 -1582 451 -1578 1481 -558 447 -1560 1493 -550 1505 -544 449 -22182 1435 -646 1391 -664 369 -1642 385 -1652 395 -1600 1463 -584 417 -1606 1467 -576 1443 -586 429 -148588 275 -106 847 -128 877 -100 281 -97826 2541 -92318 2505 -12474 2563 -73674 2555 -41600 2543 -76554 1601 -516 1471 -554 -RAW_Data: 487 -1556 453 -1570 441 -1590 1467 -556 469 -1538 1501 -522 1501 -552 463 -22124 1513 -514 1509 -548 445 -1582 461 -1568 443 -1574 1467 -536 465 -1574 1499 -516 1503 -556 441 -22150 1537 -484 1495 -544 479 -1558 459 -1574 447 -1580 1471 -548 459 -1576 1471 -554 1471 -550 459 -22160 1517 -516 1503 -546 447 -1574 473 -1546 481 -1576 1479 -520 467 -1572 1497 -550 1469 -556 441 -22156 1533 -528 1495 -530 469 -1568 445 -1566 485 -1548 1505 -544 451 -1572 1471 -554 1503 -516 495 -22160 1511 -520 1477 -546 483 -1542 485 -1546 481 -1542 1509 -526 455 -1578 1507 -544 1481 -524 485 -22146 1543 -510 1481 -552 475 -1546 473 -1568 481 -1540 1503 -522 473 -1556 1507 -558 1491 -518 479 -22164 1523 -520 1503 -544 453 -1566 479 -1550 469 -1572 1493 -546 447 -1576 1477 -552 1509 -544 451 -22178 639 -596 65 -926 63 -1954 237 -1680 355 -1670 383 -1636 1423 -596 439 -1578 1471 -574 1465 -576 445 -22182 1439 -676 1335 -702 339 -1648 387 -1648 409 -1608 1429 -618 419 -1612 1447 -578 1439 -614 425 -22186 1497 -530 1505 -556 471 -1540 479 -1582 431 -1570 1497 -546 481 -1536 1497 -554 1501 -556 463 -22178 1503 -566 1433 -586 433 -1604 445 -1576 441 -1580 1503 -548 453 -1572 1473 -578 1467 -558 469 -149080 311 -1034 895 -1084 939 -1006 337 -78790 97 -6890 2535 -28048 2511 -64482 2573 -88138 65 -6956 2515 -95140 2509 -61986 1589 -522 1475 -548 449 -1576 475 -1548 481 -1546 1475 -564 443 -1564 1477 -580 1477 -554 449 -22166 1503 -532 1481 -544 447 -1576 449 -1572 473 -1548 1473 -580 451 -1568 1463 -552 1477 -552 461 -22164 1521 -528 1493 -548 443 -1580 461 -1540 475 -1574 1473 -554 471 -1568 1489 -516 1497 -538 471 -22154 1385 -786 1245 -796 261 -1702 341 -1650 391 -1644 1411 -608 409 -1620 1415 -624 1409 -614 413 -22178 1533 -498 1519 -546 445 -1576 449 -1578 457 -1568 1503 -546 451 -1570 1467 -554 1503 -552 463 -22162 1401 -774 1257 -754 297 -1678 353 -1650 383 -1666 1397 -618 385 -1642 1437 -612 1433 -586 427 -22194 1483 -552 1477 -546 485 -1544 453 -1576 481 -1576 1479 -560 449 -1548 1507 -546 1479 -558 447 -22170 1413 -786 1245 -776 295 -1664 337 -1680 387 -1652 1385 -638 407 -1614 1441 -582 1451 -606 409 -22204 1503 -528 1507 -550 465 -1534 481 -1584 429 -1572 1497 -548 447 -1580 1481 -550 1503 -546 455 -22166 1519 -552 1469 -580 419 -1612 419 -1596 447 -1582 1479 -554 447 -1582 1479 -550 1519 -518 479 -134730 169 -172 559 -194 579 -676 257 -87040 99 -7384 2269 -488 313 -37888 65 -12084 2515 -33298 2247 -474 309 -42656 63 -51970 2233 -456 309 -94658 2233 -430 325 -90692 357 -186 2417 -518 1517 -514 475 -1550 467 -1570 471 -1538 1499 -556 443 -1564 1483 -552 1501 -524 -RAW_Data: 481 -22144 1433 -668 1373 -666 337 -1680 355 -1634 383 -1634 1431 -622 399 -1606 1443 -582 1443 -586 431 -22166 1521 -512 1497 -548 447 -1580 459 -1568 447 -1574 1473 -554 465 -1572 1497 -516 1507 -554 453 -22148 1503 -540 1489 -546 479 -1542 449 -1578 457 -1576 1475 -546 483 -1540 1507 -552 1473 -548 453 -22154 1539 -516 1469 -586 431 -1574 447 -1576 479 -1558 1485 -554 439 -1580 1501 -550 1457 -552 473 -22154 1521 -550 1485 -540 447 -1578 457 -1578 447 -1572 1471 -556 485 -1540 1483 -552 1501 -556 443 -22186 1437 -662 1345 -704 339 -1688 361 -1646 375 -1640 1399 -616 421 -1612 1441 -614 1435 -586 429 -22200 1513 -520 1503 -544 453 -1572 447 -1580 483 -1550 1475 -552 475 -1548 1501 -554 1491 -540 475 -22184 1499 -530 1485 -548 457 -1570 477 -1578 443 -1576 1501 -546 449 -1562 1499 -538 1493 -572 443 -22166 1471 -612 1429 -624 401 -1608 417 -1620 431 -1600 1461 -582 419 -1610 1441 -582 1473 -548 459 -205658 1761 -436 863 -68558 2597 -23434 1775 -438 519 -94592 1803 -422 829 -86706 1727 -434 527 -66 319 -94536 1753 -444 865 -238 1489 -652 1385 -656 357 -1668 365 -1628 403 -1612 1439 -614 403 -1626 1425 -588 1443 -584 409 -22168 1531 -528 1491 -530 469 -1568 443 -1564 483 -1538 1505 -546 455 -1570 1469 -556 1503 -518 491 -22128 1455 -660 1359 -686 355 -1632 385 -1632 411 -1640 1411 -596 423 -1606 1437 -580 1443 -596 423 -22188 1495 -550 1467 -534 473 -1566 481 -1540 483 -1572 1477 -552 449 -1582 1477 -558 1471 -552 449 -22174 543 -3692 235 -1680 357 -1668 353 -1698 1375 -636 371 -1644 1409 -610 1437 -578 449 -22180 1499 -542 1491 -542 479 -1540 481 -1566 449 -1562 1499 -554 465 -1568 1463 -554 1493 -536 473 -22168 1405 -774 1257 -788 267 -1676 355 -1672 369 -1660 1387 -624 417 -1606 1427 -624 1425 -586 415 -22208 1507 -552 1489 -548 443 -1562 483 -1550 481 -1546 1505 -558 447 -1586 1473 -548 1481 -554 479 -22166 1383 -786 1245 -776 295 -1664 373 -1646 387 -1644 1403 -642 411 -1624 1417 -594 1445 -612 409 -22210 1515 -516 1503 -554 453 -1564 457 -1572 447 -1580 1501 -554 465 -1570 1467 -554 1493 -570 443 -195814 1353 -442 1267 -37900 99 -53710 411 -142 2207 -8336 2487 -46990 1301 -452 1317 -86162 65 -8376 1289 -444 1327 -94552 1311 -488 1251 -1726 1531 -544 1467 -580 417 -1578 457 -1578 443 -1576 1475 -566 441 -1568 1503 -512 1509 -526 455 -22168 1473 -582 1469 -538 483 -1538 459 -1574 447 -1580 1471 -582 429 -1572 1469 -552 1473 -548 461 -22170 1437 -640 1401 -620 393 -1636 385 -1634 409 -1604 1457 -588 415 -1608 1431 -590 1459 -554 449 -22188 1483 -556 1487 -542 445 -1578 459 -1576 443 -1574 1473 -556 469 -1570 1459 -550 1503 -554 443 -22160 1469 -590 -RAW_Data: 1469 -582 409 -1620 429 -1608 415 -1602 1455 -568 441 -1578 1475 -552 1479 -560 467 -22166 1483 -572 1463 -546 451 -1580 453 -1580 481 -1542 1475 -588 451 -1570 1453 -550 1503 -540 469 -22158 1505 -554 1475 -554 487 -1556 453 -1562 477 -1546 1495 -546 479 -1544 1511 -554 1477 -532 471 -22180 1471 -602 1437 -602 407 -1610 421 -1614 421 -1606 1433 -582 457 -1578 1475 -550 1469 -586 465 -22164 1515 -546 1469 -546 483 -1572 447 -1558 471 -1580 1471 -578 451 -1568 1495 -542 1497 -530 471 -22194 1433 -644 1393 -662 371 -1640 385 -1642 413 -1628 1431 -584 431 -1602 1469 -540 1501 -576 443 -22180 1479 -592 1439 -608 407 -1620 427 -1610 415 -1606 1465 -570 441 -1612 1441 -586 1451 -576 445 -22196 1533 -522 1489 -540 477 -1574 481 -1544 455 -1568 1505 -546 459 -1572 1501 -556 1497 -530 471 -210202 685 -540 475 -138 839 -138 239 -9950 2501 -71112 761 -462 511 -66 1297 -85614 97 -8850 745 -466 1821 -94546 827 -468 481 -140 815 -164 237 -32934 1583 -540 1477 -554 441 -1582 447 -1580 453 -1568 1465 -554 479 -1556 1487 -558 1477 -550 453 -22174 1503 -516 1483 -584 429 -1572 449 -1576 447 -1588 1475 -558 449 -1580 1473 -546 1485 -556 449 -22168 1469 -586 1443 -614 401 -1624 401 -1608 415 -1608 1463 -570 441 -1574 1471 -586 1449 -572 445 -22164 1401 -772 1255 -756 301 -1674 353 -1708 329 -1662 1397 -650 387 -1632 1405 -614 1433 -584 431 -22200 1489 -566 1463 -582 419 -1580 457 -1596 447 -1576 1473 -554 443 -1580 1469 -582 1451 -550 479 -22174 1387 -786 1251 -792 257 -1704 331 -1690 337 -1706 1377 -632 395 -1632 1433 -586 1451 -584 413 -22198 1541 -512 1485 -554 481 -1574 445 -1560 461 -1568 1495 -556 469 -1568 1495 -518 1503 -554 469 -22160 1541 -516 1501 -556 443 -1564 495 -1536 479 -1550 1517 -544 445 -1576 1507 -556 1485 -542 445 -22202 1501 -530 1503 -554 465 -1574 445 -1582 461 -1574 1469 -546 479 -1568 1471 -556 1513 -550 463 -22170 1473 -614 1419 -602 439 -1614 417 -1610 415 -1590 1465 -588 429 -1606 1469 -586 1455 -570 441 -22200 1407 -760 1277 -772 267 -1692 363 -1678 355 -1668 1393 -660 369 -1642 1441 -584 1449 -574 445 -22206 1533 -528 1497 -530 471 -1572 449 -1576 455 -1578 1507 -544 455 -1572 1503 -556 1467 -560 469 -22188 1439 -678 1347 -672 375 -1648 393 -1646 387 -1636 1431 -588 431 -1610 1439 -618 1437 -596 437 -143610 67 -510 99 -66 223 -272 173 -70 273 -204 235 -64 483 -26542 2521 -65448 297 -348 2417 -76720 65 -8388 65 -9450 203 -332 587 -72 885 -92 899 -27534 67 -39500 221 -406 521 -72 885 -98 919 -29920 1581 -534 1491 -548 445 -1580 461 -1544 477 -1574 1465 -556 473 -1568 1455 -554 1501 -524 483 -22136 1529 -498 -RAW_Data: -754 361 -17246 131 -8734 65 -71908 65 -27774 65 -1230 65 -13826 99 -800 65 -634 67 -796 99 -47716 65 -18338 67 -18176 131 -7986 65 -1084 131 -2090 65 -48694 163 -40926 65 -4538 65 -10224 65 -9874 20955 -1970 1023 -4928 1007 -4946 1011 -7910 981 -1992 1021 -7898 1013 -1962 1007 -7896 1019 -4930 995 -4962 987 -1974 1029 -4924 1019 -4920 1025 -7896 1005 -1964 1027 -7894 1007 -4932 1033 -1948 1009 -7916 1009 -1982 1001 -4942 1011 -7880 1011 -2000 1003 -13820 1011 -1990 985 -4944 1021 -4932 995 -7916 1025 -1944 1025 -7892 1015 -1970 1025 -7892 1001 -4934 1031 -4926 1015 -1970 989 -4938 1007 -4954 1019 -7874 1013 -2000 985 -7896 1011 -4950 1001 -1968 1023 -7900 999 -1968 1007 -4968 973 -7936 985 -1970 1009 -13856 1013 -1968 981 -4962 1009 -4926 1005 -7924 1003 -1968 1013 -7892 1003 -1992 985 -7904 1011 -4936 1023 -4948 997 -1960 1015 -4950 987 -4970 993 -7896 1005 -1970 1009 -7928 1005 -4914 1023 -1984 1001 -7894 1011 -1970 999 -4962 1003 -7890 1007 -1992 1013 -13810 1011 -1994 987 -4938 1005 -4964 999 -7904 1013 -1974 987 -7910 1021 -1964 1015 -7894 1021 -4912 1019 -4948 1007 -1970 1017 -4916 1021 -4946 985 -7914 1003 -1972 1009 -7898 1041 -4912 1013 -1994 985 -7892 1027 -1964 1013 -4938 1003 -7904 1023 -1966 1017 -13824 1025 -1968 983 -4950 1007 -4944 999 -7900 1029 -1964 1015 -7914 981 -2000 981 -7932 979 -4936 1021 -4926 1013 -1998 979 -4946 1035 -4908 1031 -7896 999 -1964 1025 -7902 1021 -4914 1023 -1966 1015 -7908 1007 -1970 1003 -4952 981 -7910 1001 -1974 1003 -13832 1015 -1976 999 -4958 1019 -4938 985 -7900 1025 -1966 1011 -7914 1015 -1964 1009 -7906 1017 -4908 1027 -4916 1011 -2000 981 -4954 1007 -4926 1017 -7920 1011 -1958 1009 -7906 1019 -4912 1025 -1980 1009 -7900 1013 -1948 1027 -4916 1011 -7918 1005 -1976 1003 -13846 1013 -1970 1005 -4914 1041 -4916 1007 -7918 987 -2000 987 -7902 1013 -1970 1015 -7894 1007 -4936 1019 -4944 1009 -1970 1013 -4918 1019 -4954 977 -7914 1019 -1964 1007 -7906 1003 -4956 983 -1982 1027 -7896 1005 -1962 1027 -4930 1007 -7906 999 -1972 1009 -13850 1009 -1952 1027 -4944 983 -4954 1023 -7894 1007 -1952 1031 -7882 1035 -1970 1011 -7894 1007 -4936 1025 -4920 1023 -1968 1011 -4928 1009 -4920 1015 -7902 1015 -1966 1021 -7894 1011 -4948 997 -1970 1009 -7902 1027 -1978 1011 -4930 999 -7918 1007 -1966 1013 -13824 1015 -1966 1027 -4918 1027 -4912 1021 -7888 1009 -1998 989 -7918 1019 -1960 989 -7930 983 -4946 1025 -4920 1013 -1970 1015 -4950 1009 -4926 1005 -7892 1037 -1942 1011 -7926 1005 -4918 1007 -1996 985 -7928 1011 -1964 993 -4946 1025 -7880 1003 -1972 1009 -13854 1011 -1948 1019 -4956 983 -4946 1025 -7898 1003 -1966 1025 -7900 1019 -1964 1011 -7890 1021 -4912 1017 -4930 1013 -1968 1015 -4952 1009 -4932 1007 -7920 1009 -1972 979 -7924 1009 -4944 1003 -1970 1015 -7894 1017 -RAW_Data: 131 -534 99 -798 2211 -134 297 -66 361 -66 197 -198 97 -398 199 -100 163 -100 65 -464 133 -134 233 -68 229 -696 199 -200 265 -330 65 -1218 163 -166 97 -426 229 -698 165 -100 99 -134 165 -372 65 -1426 65 -266 99 -794 65 -832 199 -464 165 -698 99 -266 165 -794 99 -298 133 -734 165 -266 99 -864 131 -232 1893 -166 1465 -1484 263 -560 99 -230 65 -462 197 -66 97 -890 165 -130 97 -132 163 -232 97 -132 161 -162 263 -262 99 -362 65 -432 65 -830 97 -234 99 -628 199 -66 99 -166 101 -268 265 -594 131 -1030 97 -1156 595 -800 167 -1920 65 -464 165 -264 131 -66 167 -134 233 -632 329 -100 97 -166 199 -466 199 -98 267 -200 97 -266 199 -300 65 -496 563 -100 101 -166 165 -730 233 -166 131 -590 97 -232 131 -164 359 -428 3159 -792 65 -624 197 -98 229 -132 393 -398 199 -200 133 -366 99 -132 67 -528 265 -730 197 -166 65 -200 397 -1358 131 -1712 363 -1462 299 -132 67 -1792 65 -794 99 -368 131 -134 165 -134 67 -266 265 -430 2315 -98 1473 -100 129 -66 229 -198 261 -166 97 -426 97 -100 229 -496 197 -162 163 -392 231 -166 229 -660 131 -366 297 -166 299 -232 97 -332 301 -866 133 -596 267 -364 99 -502 99 -1988 131 -134 65 -66 165 -366 231 -266 165 -690 99 -234 65 -498 67 -166 133 -762 2459 -166 1019 -264 99 -200 265 -562 65 -328 295 -330 197 -360 131 -98 227 -232 65 -426 259 -166 99 -496 131 -500 399 -992 67 -432 133 -66 99 -1394 231 -1524 133 -430 65 -886 65 -1716 131 -2818 63 -430 229 -296 97 -1706 131 -164 65 -1478 131 -458 99 -1568 165 -756 197 -890 99 -1088 359 -660 97 -98 229 -166 65 -500 99 -266 65 -566 65 -432 365 -200 99 -798 99 -296 327 -1218 165 -132 99 -466 165 -168 133 -632 363 -1482 163 -802 131 -862 133 -1332 65 -100 431 -830 97 -596 165 -3422 99 -896 65 -766 97 -3766 65 -132 65 -1152 163 -1810 99 -230 97 -654 229 -458 165 -200 67 -166 297 -134 99 -4840 133 -762 67 -764 231 -1248 97 -1780 97 -3332 65 -100 261 -1186 99 -3360 99 -364 97 -1738 163 -294 97 -4918 129 -1550 295 -1028 199 -1264 99 -1560 99 -2156 167 -1556 231 -1852 167 -998 67 -1116 65 -562 167 -334 133 -266 165 -394 99 -166 97 -3082 99 -232 301 -5148 131 -502 67 -732 197 -434 199 -200 99 -432 65 -232 199 -464 133 -1060 97 -1582 97 -98 97 -924 129 -462 229 -560 65 -132 97 -166 161 -164 97 -166 229 -360 97 -132 63 -198 131 -162 525 -1714 67 -732 99 -958 -RAW_Data: 65 -1166 165 -200 199 -166 133 -100 65 -1294 99 -166 265 -992 297 -728 65 -332 431 -166 261 -132 65 -132 65 -98 363 -230 163 -430 165 -230 129 -660 129 -66 131 -398 2453 -132 1329 -166 1187 -232 1657 -166 499 -68 99 -132 233 -894 99 -232 99 -134 363 -628 67 -998 131 -234 99 -266 197 -398 97 -200 167 -334 197 -430 429 -168 263 -1160 99 -364 131 -264 97 -1318 131 -790 99 -432 131 -132 199 -100 131 -930 133 -1424 99 -398 165 -1486 299 -166 165 -334 2325 -66 795 -102 397 -100 533 -100 429 -98 131 -464 65 -532 329 -366 165 -332 99 -432 295 -132 99 -662 67 -332 331 -1226 131 -102 199 -562 469 -430 97 -232 63 -328 65 -1914 133 -466 131 -832 163 -1660 331 -464 199 -828 131 -696 65 -132 2139 -66 2051 -66 263 -790 99 -566 429 -428 99 -330 297 -698 65 -200 197 -68 461 -1292 131 -168 165 -634 131 -234 133 -594 163 -360 165 -700 99 -964 133 -1026 65 -532 197 -1520 263 -688 65 -888 295 -658 65 -824 67 -598 97 -426 65 -130 161 -132 63 -658 99 -64 165 -522 199 -298 65 -428 197 -560 65 -200 165 -100 167 -594 229 -100 297 -268 65 -100 459 -200 97 -334 2997 -200 597 -498 97 -626 97 -134 363 -166 333 -266 65 -664 331 -300 299 -568 529 -662 263 -626 67 -594 99 -266 165 -230 361 -462 67 -66 131 -694 397 -1490 167 -132 165 -768 165 -130 165 -466 99 -298 99 -952 163 -428 97 -296 97 -360 99 -196 65 -658 65 -4052 229 -6240 165 -228 65 -426 99 -994 99 -398 363 -1512 161 -2042 65 -592 131 -2866 65 -500 167 -5246 97 -462 67 -1156 99 -2428 99 -960 65 -132 99 -164 65 -166 329 -2484 263 -232 67 -1368 99 -996 267 -498 67 -4612 131 -4866 65 -100 363 -464 231 -264 231 -262 297 -1594 99 -1596 65 -136 133 -1662 67 -1694 429 -1290 197 -198 293 -858 99 -1976 165 -66 97 -460 131 -460 97 -328 163 -1624 397 -300 67 -166 97 -430 299 -300 299 -1792 65 -4438 131 -622 67 -500 131 -1064 65 -400 65 -426 261 -1644 131 -3468 165 -1684 99 -866 101 -66 231 -464 65 -498 133 -492 97 -1712 99 -98 197 -66 265 -1586 199 -1334 99 -1948 97 -200 99 -266 65 -666 131 -132 99 -264 195 -3066 99 -3512 99 -1746 65 -598 97 -6068 167 -600 99 -1262 65 -166 65 -134 65 -132 131 -1520 99 -1254 97 -1828 197 -1294 129 -66 65 -1248 97 -754 165 -6542 231 -662 197 -560 359 -98 97 -130 99 -3080 63 -952 129 -296 65 -230 63 -428 99 -1788 133 -100 65 -462 199 -66 165 -1094 99 -66 -RAW_Data: 167 -3190 265 -1128 195 -298 525 -166 65 -594 597 -664 131 -498 165 -232 65 -296 129 -296 65 -494 297 -498 297 -596 201 -332 2027 -132 593 -68 891 -66 893 -98 365 -396 97 -398 99 -300 199 -366 163 -598 99 -100 131 -234 65 -132 331 -532 165 -166 231 -1660 133 -532 265 -100 131 -100 131 -298 199 -396 199 -334 197 -166 67 -334 131 -1662 165 -298 97 -534 65 -628 65 -534 65 -266 97 -826 133 -696 65 -294 233 -198 165 -134 2081 -66 929 -198 167 -300 99 -232 297 -234 199 -662 67 -166 99 -66 233 -132 433 -232 265 -300 297 -132 65 -132 97 -496 199 -360 465 -132 65 -132 297 -460 293 -64 161 -262 97 -492 199 -200 233 -132 97 -396 99 -858 199 -864 65 -66 99 -532 131 -1608 297 -1550 163 -432 229 -66 163 -394 1953 -134 1491 -100 991 -66 165 -232 265 -130 299 -432 167 -724 363 -134 167 -200 231 -66 199 -98 395 -66 165 -266 99 -332 197 -268 265 -466 265 -166 493 -362 65 -132 99 -360 199 -200 133 -758 99 -330 165 -298 163 -428 199 -368 165 -334 131 -500 163 -986 97 -394 329 -196 97 -558 195 -230 97 -532 2373 -232 165 -232 795 -132 267 -168 363 -166 297 -298 131 -134 65 -466 131 -464 65 -232 67 -266 229 -364 231 -232 297 -464 65 -198 133 -398 529 -134 65 -364 163 -732 233 -298 299 -134 231 -66 129 -264 397 -132 131 -200 329 -430 131 -132 265 -1292 65 -362 131 -264 97 -1020 165 -398 131 -364 65 -300 265 -924 195 -396 331 -166 167 -132 433 -198 99 -464 297 -266 165 -396 131 -1226 165 -694 65 -230 197 -896 65 -594 2969 -164 461 -298 133 -330 133 -234 65 -134 131 -66 65 -68 97 -66 99 -132 231 -694 363 -298 267 -268 231 -368 131 -300 99 -134 65 -166 461 -396 263 -66 165 -298 131 -66 229 -1644 163 -1610 229 -658 97 -132 129 -162 97 -428 65 -66 129 -1912 65 -1194 233 -2218 133 -360 65 -132 229 -3428 65 -262 65 -3208 133 -1224 99 -296 65 -2212 65 -496 65 -200 199 -498 65 -1144 65 -1764 165 -1726 65 -434 65 -898 165 -2354 67 -134 99 -464 99 -792 263 -954 1161 -100 295 -296 131 -132 457 -462 363 -200 725 -298 131 -134 231 -68 531 -166 667 -298 99 -66 595 -794 165 -202 199 -68 99 -298 165 -68 231 -202 201 -200 97 -1360 197 -266 165 -334 99 -234 165 -200 165 -100 131 -664 65 -566 397 -100 165 -132 199 -132 165 -988 197 -394 99 -164 65 -294 99 -262 63 -1288 233 -132 199 -168 265 -64 99 -728 133 -1192 65 -334 165 -132 199 -200 -RAW_Data: 131 -400 65 -398 165 -866 131 -264 393 -1680 231 -1494 299 -894 265 -364 65 -132 131 -960 99 -296 165 -824 65 -1716 99 -664 97 -134 165 -1526 165 -634 131 -166 135 -466 231 -696 2331 -100 329 -100 961 -198 65 -164 199 -98 331 -132 65 -298 731 -66 163 -332 265 -894 65 -132 265 -300 97 -334 229 -100 65 -1026 233 -366 329 -922 99 -266 329 -428 97 -462 361 -758 163 -1886 65 -498 229 -558 65 -600 267 -68 65 -1592 133 -202 2299 -332 393 -132 329 -326 231 -232 97 -590 231 -136 201 -1226 265 -530 199 -134 65 -232 297 -398 65 -564 65 -1458 65 -500 99 -302 99 -1120 65 -66 131 -98 197 -66 133 -66 65 -730 99 -200 197 -730 165 -664 297 -1462 165 -664 133 -168 65 -168 133 -368 197 -466 1957 -66 1525 -266 131 -366 65 -394 65 -98 163 -296 99 -130 265 -696 65 -398 131 -1128 197 -134 67 -894 97 -296 231 -166 401 -98 131 -928 265 -398 131 -798 131 -298 165 -830 65 -230 231 -168 97 -1252 131 -590 65 -460 229 -692 97 -332 65 -366 265 -732 2297 -66 197 -66 131 -166 231 -100 99 -298 365 -168 361 -130 129 -66 161 -198 97 -626 593 -66 99 -98 99 -556 297 -132 65 -134 65 -526 295 -196 197 -68 163 -264 327 -130 855 -66 393 -132 131 -100 65 -98 227 -164 197 -758 131 -268 99 -430 65 -1686 131 -1652 197 -788 63 -726 165 -100 131 -264 197 -1026 197 -1250 131 -1650 65 -1980 97 -132 63 -1424 97 -198 99 -100 65 -1090 65 -398 99 -1686 99 -754 63 -1714 97 -1842 65 -2988 65 -4980 165 -1624 65 -232 67 -1464 65 -3048 65 -166 97 -1986 65 -1062 99 -862 131 -2732 99 -2360 97 -1620 65 -1326 65 -330 197 -3238 67 -266 131 -2968 67 -132 135 -1688 131 -1718 97 -1648 65 -166 65 -1292 65 -1908 99 -424 131 -1222 197 -798 99 -700 97 -1758 165 -1562 99 -396 165 -166 99 -98 65 -366 65 -762 131 -332 233 -498 133 -98 163 -562 65 -68 131 -858 65 -896 65 -366 233 -532 197 -168 65 -68 65 -630 195 -724 231 -296 361 -164 261 -2862 233 -232 99 -3186 67 -232 163 -496 165 -1620 97 -1622 131 -7022 267 -268 165 -466 63 -528 295 -200 197 -332 65 -1656 131 -5478 65 -1296 99 -130 161 -694 67 -1694 131 -734 99 -2590 99 -1056 65 -432 327 -66 165 -164 65 -1120 229 -1656 165 -1128 131 -1232 99 -1560 65 -568 131 -360 231 -1598 99 -3250 67 -1818 65 -1216 263 -3328 65 -1508 133 -2600 65 -528 65 -2418 65 -460 97 -100 97 -164 65 -1560 133 -1822 163 -296 165 -1090 165 -168 133 -1264 -RAW_Data: 229 -1458 165 -66 133 -1626 197 -132 65 -394 65 -724 229 -132 65 -200 263 -296 97 -264 131 -130 229 -132 161 -1230 431 -132 99 -234 233 -362 99 -698 67 -334 65 -366 299 -398 99 -328 131 -396 165 -1458 99 -926 2239 -66 763 -68 1727 -266 65 -364 65 -464 365 -366 97 -664 165 -200 265 -234 261 -264 395 -362 329 -690 261 -164 495 -232 131 -326 165 -264 99 -198 397 -928 299 -268 299 -166 99 -298 167 -436 65 -266 67 -498 231 -896 367 -428 97 -332 133 -628 165 -1394 231 -926 131 -232 3127 -132 921 -428 263 -66 97 -230 97 -1022 329 -430 97 -166 65 -198 63 -100 65 -66 97 -66 361 -132 67 -232 131 -268 99 -366 99 -98 331 -364 99 -428 97 -66 491 -164 99 -164 65 -428 131 -260 429 -1380 263 -1564 65 -300 65 -498 99 -364 167 -200 231 -264 67 -264 365 -266 231 -262 65 -724 65 -130 2213 -130 361 -166 2065 -166 165 -100 133 -264 299 -332 99 -98 229 -198 65 -328 131 -100 263 -628 131 -330 231 -98 65 -132 129 -264 65 -130 163 -692 131 -66 99 -532 133 -234 99 -232 367 -298 401 -702 165 -498 399 -166 65 -132 131 -400 165 -1232 165 -1296 65 -232 165 -300 97 -396 2033 -198 463 -230 67 -68 563 -262 295 -330 197 -1190 197 -66 167 -66 263 -430 263 -100 163 -198 167 -102 165 -100 99 -234 67 -66 267 -396 197 -296 131 -1190 331 -200 67 -234 231 -530 263 -1610 263 -264 329 -196 129 -98 131 -132 227 -132 425 -526 129 -694 299 -856 63 -132 165 -166 165 -436 297 -1430 65 -66 99 -198 167 -266 65 -864 267 -996 65 -500 265 -1456 297 -726 163 -66 579 -1508 523 -1504 509 -1006 1033 -486 1513 -1514 507 -510 1537 -982 1011 -528 1509 -512 1495 -1034 999 -1016 999 -516 1539 -1014 989 -1510 527 -486 1537 -1508 517 -1512 485 -1546 483 -524 1519 -484 1529 -60168 1539 -1506 507 -1006 1015 -1016 1007 -1512 509 -1006 1033 -506 1495 -1544 497 -1014 1007 -1008 1001 -1544 477 -1024 1011 -1538 485 -1512 525 -1010 1005 -482 1529 -1018 1005 -1020 1009 -1532 475 -540 1507 -512 1491 -59188 513 -1506 515 -1512 519 -1020 985 -506 1519 -1538 491 -504 1533 -998 1013 -516 1505 -522 1505 -1018 1005 -1002 1035 -494 1525 -1014 995 -1546 483 -524 1513 -1504 515 -1502 509 -1512 509 -514 1527 -514 1501 -60186 1539 -1506 509 -1008 1003 -1016 1009 -1520 515 -1014 989 -518 1509 -1544 485 -1016 999 -1016 1003 -1544 483 -1016 1017 -1506 515 -1534 475 -1036 987 -520 1507 -1012 1035 -1006 1017 -1502 503 -526 1503 -508 1533 -59160 523 -1508 507 -1502 537 -984 1013 -528 1505 -1504 503 -520 1537 -994 -RAW_Data: -1968 997 -4952 1011 -7896 1013 -1954 1005 -13860 985 -2000 979 -4952 1005 -4946 993 -7930 979 -1990 1009 -7908 1017 -1968 1011 -7900 985 -4946 1027 -4920 1009 -1964 1015 -4942 1009 -263646 67 -9708 99 -68890 97 -48712 65 -62028 65 -4652 65 -108870 99 -71758 99 -3200 97 -47548 65 -7036 65 -104448 97 -38184 99 -6502 97 -17756 65 -10136 20983 -1968 999 -4950 1005 -4940 1001 -7898 1025 -1966 1011 -7876 1017 -1966 1023 -7918 981 -4936 1027 -4946 975 -2006 979 -4946 1017 -4954 977 -7930 983 -1980 1003 -7920 989 -4952 1007 -1970 1013 -7896 1007 -1970 1015 -4954 987 -7900 1007 -1970 1007 -13860 993 -1972 1007 -4956 1013 -4926 1005 -7908 995 -1972 1029 -7914 987 -1986 1009 -7906 981 -4956 1009 -4930 1005 -1966 1039 -4938 1007 -4916 1009 -7918 1019 -1968 989 -7892 1017 -4934 1027 -1956 1039 -7898 985 -1974 1007 -4960 981 -7930 985 -1996 989 -13824 1045 -1946 1025 -4928 1009 -4942 997 -7926 1011 -1968 1003 -7894 1023 -1962 993 -7902 1017 -4948 999 -4948 1007 -1968 1015 -4918 1045 -4926 999 -7888 1039 -1972 1013 -7888 1005 -4948 1001 -1970 1011 -7886 1013 -1974 1023 -4942 1005 -7890 1033 -1952 1021 -13806 1047 -1964 1001 -4936 1009 -4950 1001 -7882 1031 -1962 1007 -7906 1021 -1966 1011 -7908 985 -4946 1027 -4918 1011 -1968 1017 -4948 1009 -4932 1015 -7882 1009 -1964 1013 -7900 1017 -4950 1001 -1970 1013 -7916 1013 -1944 1031 -4916 1029 -7888 1007 -1976 1005 -13848 1013 -1968 995 -4952 1003 -4946 991 -7920 1019 -1968 983 -7904 1015 -1966 1019 -7894 1011 -4942 1027 -4924 1011 -1966 1015 -4944 1005 -4926 1007 -7914 1007 -1972 1013 -7884 1013 -4940 1027 -1968 1007 -7906 1001 -1980 999 -4944 1013 -7880 1009 -2000 1003 -13830 997 -1974 1009 -4930 1029 -4938 1013 -7880 1011 -1998 1001 -7908 997 -1966 1025 -7902 1013 -4938 983 -4962 979 -1998 1011 -4932 1005 -4924 1011 -7922 1009 -1970 1015 -7890 1011 -4946 1003 -1970 1013 -7892 1013 -1964 1027 -4928 1021 -7886 1007 -1980 1001 -13846 1013 -1968 999 -4948 1009 -4938 993 -7926 1011 -1964 991 -7926 1011 -1962 993 -7898 1041 -4906 1009 -4960 999 -1964 1023 -4932 1007 -4928 1013 -7898 1015 -1968 1013 -7894 1017 -4948 997 -1968 1031 -7892 1027 -1970 1005 -4914 1039 -7884 1023 -1964 1011 -13838 1013 -1948 1023 -4924 1015 -4942 1025 -7872 1029 -1964 1007 -7898 1025 -1960 1025 -7898 1021 -4912 1023 -4920 1011 -1970 1013 -4954 1011 -4926 1003 -7910 1011 -1968 1013 -7890 1009 -4938 1023 -1982 1007 -7902 1013 -1940 1023 -4926 1011 -7922 1007 -1980 997 -13816 1045 -1964 1001 -4948 1005 -4938 993 -7904 1005 -1966 1035 -7888 1027 -1964 1011 -7906 1015 -4916 1027 -4920 1011 -1968 1019 -4944 1009 -4928 1001 -7924 1009 -1972 979 -7914 1013 -4948 1001 -1970 1013 -7892 1015 -1966 1027 -4928 1021 -7872 1035 -1968 1011 -13832 1011 -1980 983 -4956 987 -4948 1025 -7892 1005 -1966 1007 -7914 1021 -1962 1007 -RAW_Data: -7912 985 -4942 1023 -4926 1007 -1992 981 -4944 1035 -4920 1007 -7904 1025 -1962 991 -7934 981 -4948 995 -2004 1007 -7880 1007 -1992 983 -4946 1027 -7890 1007 -1982 999 -13830 1011 -1968 1025 -4926 1013 -4942 997 -7898 1027 -1960 1023 -7910 983 -2000 979 -7906 1015 -4940 1025 -4920 1011 -1968 1013 -225644 65 -2082 65 -155560 133 -5172 65 -1102 131 -48576 99 -24714 67 -6858 65 -1314 67 -38246 65 -64888 65 -4564 67 -59374 99 -20160 99 -17606 65 -42096 97 -11950 131 -29302 65 -19034 99 -32020 97 -366 65 -4430 131 -14620 99 -17318 65 -5556 -RAW_Data: 297 -1592 167 -1594 231 -366 65 -598 65 -600 197 -98 199 -1098 99 -98 65 -862 133 -628 165 -597 361 -6828 65 -1668 231 -632 65 -792 163 -1284 163 -5554 199 -1588 165 -5300 97 -992 65 -432 197 -2566 99 -2340 63 -5932 65 -1462 65 -1062 131 -2454 65 -1446 133 -1682 65 -1260 65 -368 131 -1482 65 -134 131 -1512 197 -1710 131 -824 99 -66 133 -464 131 -866 65 -698 65 -200 65 -894 99 -1692 233 -1130 97 -2160 265 -1392 99 -132 131 -166 65 -1318 327 -1548 163 -6980 165 -994 65 -698 131 -1580 129 -132 63 -758 97 -466 99 -1590 395 -1024 97 -600 99 -732 63 -228 129 -98 165 -292 99 -696 231 -232 197 -166 133 -132 131 -430 165 -664 199 -1596 197 -530 65 -1054 63 -3474 165 -4504 65 -2980 99 -268 133 -2356 131 -798 99 -132 99 -796 97 -1692 97 -3804 199 -166 67 -66 97 -132 99 -5058 129 -1512 163 -1290 65 -298 199 -398 65 -1034 65 -1332 167 -3448 65 -1750 65 -1186 131 -462 65 -920 65 -166 97 -362 131 -1704 131 -1748 65 -2124 65 -1064 133 -1694 197 -5148 99 -566 131 -1696 99 -598 65 -698 231 -692 267 -2490 99 -1618 165 -1760 197 -2152 165 -1226 195 -100 99 -66 131 -100 99 -400 65 -456 131 -198 165 -368 261 -826 365 -858 99 -132 163 -460 229 -264 65 -664 165 -234 231 -98 165 -100 99 -1986 99 -2526 65 -832 167 -1762 231 -728 67 -530 133 -2082 97 -960 67 -498 199 -1658 133 -1488 99 -2652 99 -1258 165 -5284 99 -1660 65 -1790 65 -432 65 -3804 197 -2170 131 -196 129 -66 97 -832 99 -2466 97 -1622 99 -1624 163 -394 163 -1018 133 -1856 99 -430 67 -826 197 -3156 65 -166 97 -558 65 -1690 131 -1498 99 -66 65 -2716 161 -658 99 -1054 65 -230 65 -2074 97 -2042 65 -3074 263 -1698 165 -492 65 -400 131 -196 131 -368 165 -134 203 -168 201 -134 165 -398 301 -628 99 -1032 99 -830 65 -66 65 -432 229 -230 161 -394 197 -228 197 -330 327 -66 197 -822 231 -392 131 -892 393 -1456 131 -64 163 -1418 65 -166 165 -1428 133 -1526 163 -954 65 -888 99 -1956 133 -1194 131 -3514 65 -1728 97 -662 67 -1590 167 -2714 65 -930 65 -166 65 -728 131 -2752 65 -434 133 -1828 99 -3704 129 -1912 133 -1544 227 -494 133 -202 97 -466 65 -200 131 -1580 263 -1708 65 -626 65 -626 199 -166 133 -3446 99 -3420 99 -398 131 -1164 131 -7018 131 -3468 63 -1658 199 -956 101 -598 99 -1790 97 -1192 65 -362 133 -630 65 -100 429 -794 65 -696 231 -1130 65 -268 395 -730 131 -466 431 -66 233 -168 197 -998 97 -264 65 -796 163 -236 -RAW_Data: 265 -100 133 -1464 99 -464 67 -592 229 -362 131 -262 163 -134 325 -164 163 -130 97 -164 65 -724 97 -132 97 -958 131 -594 131 -264 161 -98 229 -100 97 -890 131 -364 99 -198 97 -262 131 -234 2497 -134 65 -68 165 -400 563 -400 433 -496 99 -100 131 -266 99 -100 65 -200 231 -894 65 -166 463 -200 201 -1228 131 -266 65 -496 99 -300 97 -202 165 -896 365 -132 163 -1698 165 -132 99 -168 67 -166 67 -796 165 -400 131 -200 165 -796 165 -98 2161 -66 65 -166 293 -228 887 -66 395 -656 297 -100 97 -530 97 -66 97 -594 65 -428 97 -332 65 -400 131 -164 261 -66 299 -334 65 -596 97 -166 99 -1184 161 -460 131 -132 229 -398 299 -664 165 -722 165 -166 263 -300 99 -232 199 -266 267 -660 97 -758 133 -530 65 -632 99 -264 329 -1028 67 -166 2451 -562 427 -464 163 -198 229 -264 229 -790 295 -164 297 -132 263 -726 267 -66 99 -364 197 -564 99 -1162 67 -960 97 -198 163 -630 133 -66 163 -132 195 -1288 197 -430 197 -1546 65 -890 97 -394 231 -924 2173 -100 65 -594 165 -100 99 -528 99 -166 99 -298 497 -66 65 -100 131 -198 265 -362 131 -198 293 -294 129 -1150 263 -262 65 -100 131 -494 231 -166 231 -468 99 -66 165 -1262 197 -298 65 -298 397 -432 297 -498 361 -200 65 -466 131 -166 101 -496 231 -364 233 -64 197 -196 65 -426 265 -198 99 -1218 97 -200 67 -232 131 -200 167 -200 99 -66 165 -928 67 -1722 99 -764 65 -892 99 -232 133 -300 133 -1080 621 -1526 487 -1504 507 -506 1517 -1524 519 -1508 509 -1512 447 -546 1503 -1038 1035 -1482 547 -976 1007 -1208 823 -490 1503 -532 1481 -1048 989 -1534 499 -524 1507 -514 1499 -1014 1007 -524 1503 -1530 473 -60198 1533 -1516 481 -1040 985 -1044 981 -1554 479 -1530 479 -1044 1007 -1502 511 -1538 483 -516 1503 -1542 473 -1016 1033 -1510 483 -1542 505 -1002 1003 -508 1521 -1010 1015 -1512 507 -526 1515 -518 1505 -1016 1007 -59156 529 -1528 463 -1580 447 -542 1507 -1544 469 -1554 481 -1512 499 -518 1507 -1016 1007 -1512 511 -1012 1021 -1010 1007 -518 1529 -472 1555 -982 1023 -1502 505 -508 1537 -488 1545 -1012 1003 -480 1531 -1516 507 -60180 493 -12296 821 -1698 299 -1686 387 -590 1465 -1570 459 -1046 979 -1546 475 -1534 489 -1044 1009 -482 1529 -1032 973 -1538 513 -480 1537 -492 1523 -1012 1017 -59154 497 -11798 301 -704 1325 -1148 913 -1612 417 -1102 897 -1118 945 -586 1437 -568 1465 -1058 977 -1534 477 -550 1495 -502 1515 -1006 1037 -494 1527 -1510 489 -60174 531 -16794 355 -640 1399 -1608 449 -1050 989 -1540 461 -1546 477 -1068 973 -518 -RAW_Data: 1503 -1024 1009 -1536 473 -552 1503 -498 1517 -1010 1017 -59162 513 -9838 227 -1670 371 -604 1425 -1116 949 -1554 477 -1030 981 -1042 973 -550 1477 -532 1511 -1010 1003 -1544 511 -496 1515 -516 1509 -1018 1007 -488 1539 -1504 503 -60216 1525 -1530 485 -1018 999 -1012 1019 -1538 475 -1546 477 -1012 1025 -1506 517 -1530 505 -510 1507 -1512 513 -1010 999 -1516 507 -1520 513 -1012 1015 -506 1527 -1014 991 -1542 483 -524 1517 -482 1529 -1008 1005 -137640 165 -1660 165 -1362 231 -3114 67 -2286 131 -1652 197 -1448 195 -1648 97 -3534 163 -988 65 -556 65 -132 133 -2594 199 -2054 65 -1918 65 -1458 163 -690 65 -1134 131 -1194 67 -1158 131 -1656 199 -198 99 -298 199 -2676 67 -2122 231 -1762 263 -1560 131 -1228 99 -198 197 -830 99 -166 97 -6274 99 -1058 163 -1676 97 -1788 65 -5742 99 -3890 131 -2682 195 -166 65 -1394 265 -432 99 -368 99 -100 67 -198 163 -564 133 -992 131 -266 133 -962 233 -200 131 -196 231 -560 131 -66 65 -764 131 -1058 133 -564 99 -1792 133 -2820 65 -3480 165 -9536 99 -66 97 -626 97 -1828 199 -1860 99 -66 197 -1060 99 -1746 65 -1690 99 -198 99 -1660 165 -1764 65 -1558 199 -1456 99 -164 65 -1318 395 -5966 131 -928 165 -1026 99 -534 65 -434 99 -100 97 -100 97 -3272 65 -2168 131 -2904 231 -1564 67 -958 263 -3780 65 -166 99 -3506 99 -1594 165 -1492 197 -164 63 -1382 197 -1162 65 -364 133 -822 65 -498 495 -230 165 -366 65 -464 231 -666 231 -198 65 -332 163 -566 65 -164 67 -332 231 -232 265 -198 97 -1254 361 -200 65 -2056 129 -430 131 -100 65 -1588 263 -198 97 -2992 97 -528 297 -134 97 -368 427 -66 67 -132 65 -132 367 -330 65 -266 229 -66 65 -66 65 -198 231 -66 133 -528 363 -162 161 -230 525 -230 65 -230 461 -132 327 -330 295 -130 197 -230 165 -270 265 -464 65 -698 265 -264 229 -400 99 -3476 97 -1490 199 -134 99 -2970 165 -1618 167 -1598 65 -1162 99 -234 167 -1162 135 -464 65 -664 165 -1126 197 -362 97 -1222 65 -3496 65 -7024 131 -1592 131 -530 97 -6464 231 -998 263 -428 65 -698 131 -198 131 -1414 63 -1346 165 -1060 65 -1664 167 -566 263 -666 165 -566 331 -100 131 -1522 167 -368 199 -896 229 -232 99 -968 99 -168 231 -66 197 -68 131 -168 133 -400 131 -268 233 -596 233 -132 97 -494 131 -1710 197 -1616 67 -592 99 -1226 267 -268 65 -266 133 -1986 165 -332 99 -366 131 -2700 97 -1398 65 -130 269 -198 65 -1664 131 -3180 165 -992 65 -866 99 -166 163 -2782 65 -2354 265 -5176 65 -66 163 -1290 67 -164 99 -2584 67 -3084 -RAW_Data: 195 -364 65 -164 229 -958 63 -166 193 -130 65 -556 99 -332 199 -430 197 -996 297 -1426 235 -1160 2053 -166 1063 -100 501 -132 535 -198 67 -66 165 -98 165 -460 365 -366 97 -432 329 -264 133 -100 165 -328 197 -360 1087 -264 97 -166 63 -166 233 -98 195 -294 97 -264 163 -266 197 -100 359 -66 65 -1958 165 -694 99 -166 99 -596 299 -466 97 -66 99 -696 231 -1492 297 -1554 165 -1680 165 -368 99 -166 99 -168 65 -794 197 -194 2581 -98 1151 -592 99 -426 197 -328 295 -164 65 -232 163 -530 165 -264 129 -98 229 -294 493 -426 99 -66 163 -164 261 -264 129 -166 229 -68 65 -558 131 -132 65 -132 197 -1550 361 -98 67 -132 99 -132 131 -786 99 -198 301 -366 165 -530 99 -234 2159 -264 1691 -166 367 -132 231 -100 197 -166 97 -366 163 -68 131 -366 165 -268 133 -430 233 -100 133 -132 199 -932 561 -1416 231 -794 199 -1296 165 -564 165 -666 99 -490 97 -760 163 -1582 295 -464 267 -330 2561 -134 931 -66 65 -132 99 -264 63 -132 99 -198 97 -364 129 -460 65 -230 263 -164 163 -100 365 -100 131 -398 97 -530 65 -266 299 -1028 133 -100 97 -166 65 -296 65 -166 133 -498 331 -962 99 -98 199 -1328 165 -200 65 -234 99 -400 231 -632 65 -232 199 -530 65 -866 429 -958 197 -368 165 -668 65 -984 163 -100 129 -1088 259 -752 97 -522 131 -892 65 -298 67 -364 199 -132 65 -788 97 -396 363 -1052 99 -228 131 -98 99 -1526 463 -330 131 -898 263 -332 97 -996 163 -494 99 -2950 65 -798 131 -2490 165 -1424 65 -1694 65 -1822 65 -1756 67 -3820 65 -2536 97 -2410 65 -1030 131 -404 263 -732 165 -1566 197 -1554 199 -400 65 -100 99 -566 165 -3584 65 -764 101 -2630 65 -2896 163 -364 67 -100 65 -1228 263 -232 63 -884 65 -4092 133 -1622 325 -166 99 -2352 65 -500 65 -1324 99 -366 65 -1592 297 -5134 131 -1130 65 -1962 99 -66 99 -1454 67 -1130 99 -134 199 -134 165 -1222 229 -166 131 -464 197 -196 263 -234 99 -534 65 -132 131 -166 133 -134 199 -1590 231 -66 131 -1160 131 -300 65 -698 199 -462 133 -3446 99 -2876 65 -596 65 -1716 133 -2886 97 -134 199 -1628 131 -1790 67 -2556 615 -1510 453 -1584 443 -1566 485 -1014 977 -1054 1003 -516 1501 -1010 1007 -520 1495 -1034 1005 -1530 481 -514 1541 -492 1523 -1012 1001 -1508 521 -1016 999 -512 1527 -1506 509 -488 1521 -1040 989 -1538 473 -60194 1519 -1540 511 -1010 1001 -1020 1009 -1516 515 -1010 1001 -518 1507 -524 1503 -1534 493 -1510 501 -1022 1011 -516 1507 -522 1507 -1512 509 -1506 519 -516 -RAW_Data: -130 1115 -68 55471 -1962 167 -1664 99 -364 427 -232 759 -132 199 -100 267 -134 527 -132 99 -1292 99 -266 65 -68 131 -762 327 -98 393 -298 37611 -730 131 -428 1487 -3910 327 -100 295 -98 491 -492 65 -98 197 -860 97 -98 131 -856 131 -134 231 -792 229 -66 919 -198 7527 -7444 131 -722 97 -230 65 -98 527 -100 267 -68 229 -262 361 -528 195 -624 131 -164 495 -66 1697 -66 1791 -132 3715 -8320 65 -394 165 -166 461 -1094 297 -532 131 -764 197 -98 261 -230 165 -100 6235 -12144 265 -600 131 -134 65 -298 233 -958 163 -196 97 -756 263 -98 465 -134 97 -164 3051 -16552 431 -68 131 -166 131 -264 331 -298 561 -166 689 -66 557 -264 3173 -8316 65 -166 531 -298 197 -234 233 -134 233 -892 163 -296 65 -890 161 -232 331 -100 397 -15072 199 -100 99 -394 165 -164 129 -132 197 -132 199 -798 99 -1032 67 -398 427 -100 391 -98 363 -166 297 -66 2869 -66 2299 -12300 97 -660 99 -662 263 -594 131 -662 133 -66 199 -332 97 -134 99 -132 65 -134 131 -100 431 -100 2143 -66 429 -8840 97 -6976 97 -66 397 -66 65 -166 131 -230 131 -294 99 -228 97 -230 129 -756 133 -1298 1293 -132 529 -100 2567 -15654 229 -328 97 -198 131 -66 195 -1492 131 -398 99 -134 99 -366 265 -168 99 -100 235 -100 131 -396 299 -200 1029 -66 6461 -6804 99 -1062 65 -960 65 -232 131 -98 195 -1708 63 -196 261 -164 331 -66 261 -12094 65 -298 265 -198 163 -592 131 -2402 63 -66 297 -198 97 -66 393 -264 4003 -15878 231 -98 261 -496 229 -264 65 -858 131 -994 133 -66 331 -66 429 -100 165 -132 297 -15004 99 -1466 97 -266 165 -198 463 -796 231 -66 131 -298 99 -100 133 -134 167 -430 99 -66 365 -100 297 -134 265 -132 563 -98 1217 -66 6399 -8742 99 -592 99 -426 397 -2338 199 -66 995 -134 229 -132 65 -164 3989 -66 3675 -6962 165 -466 65 -564 399 -66 199 -134 263 -396 97 -132 97 -100 97 -428 393 -624 131 -988 229 -66 363 -230 791 -164 7883 -8286 165 -134 99 -66 197 -100 99 -68 131 -164 163 -398 197 -2162 2005 -66 97 -100 4365 -98 1255 -12012 99 -132 165 -462 65 -166 97 -564 65 -100 331 -794 199 -364 261 -496 331 -132 823 -66 6233 -10976 165 -764 165 -200 195 -296 97 -19176 195 -230 129 -658 131 -132 293 -66 133 -860 65 -858 131 -64 229 -66 227 -66 161 -66 9051 -7316 65 -1494 131 -98 165 -198 65 -134 365 -398 297 -100 3969 -14874 99 -998 65 -564 67 -364 263 -132 163 -528 197 -132 65 -264 65 -264 431 -100 301 -66 297 -RAW_Data: -164 6323 -10770 65 -1420 227 -196 263 -198 197 -1086 99 -98 163 -164 163 -426 65 -362 97 -264 295 -132 197 -13586 65 -1808 131 -166 301 -66 465 -432 165 -330 65 -332 297 -962 99 -266 97 -166 265 -132 327 -198 329 -98 293 -14984 99 -862 131 -166 331 -68 165 -98 233 -132 201 -300 197 -364 133 -662 99 -398 99 -166 65 -432 133 -132 1447 -5882 197 -1082 65 -198 163 -1580 129 -264 67 -632 625 -134 165 -68 827 -100 165 -100 99 -164 3949 -9126 67 -164 131 -986 241 -534 309 -208 267 -226 247 -250 247 -248 247 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 247 -250 249 -504 257 -248 245 -246 247 -248 501 -258 247 -246 245 -248 247 -248 249 -248 249 -504 513 -226 243 -274 243 -500 513 -238 253 -256 253 -492 231 -262 485 -514 223 -244 527 -478 507 -274 255 -218 253 -498 261 -224 251 -250 503 -258 247 -246 243 -246 247 -504 515 -238 251 -472 277 -242 497 -246 289 -220 247 -498 513 -476 255 -68610 311 -208 235 -256 249 -248 247 -248 249 -248 249 -248 249 -250 249 -248 249 -250 247 -250 249 -248 249 -250 249 -248 249 -248 249 -250 249 -504 257 -248 245 -246 247 -248 503 -514 479 -274 253 -256 217 -254 247 -236 255 -250 247 -502 513 -240 251 -256 253 -254 249 -240 227 -252 249 -502 259 -246 245 -246 501 -514 237 -254 473 -280 253 -250 239 -230 251 -250 247 -504 257 -246 501 -226 245 -276 245 -246 245 -248 247 -504 515 -478 255 -244 495 -274 253 -476 511 -240 241 -69142 321 -218 243 -230 251 -248 247 -248 247 -250 247 -250 247 -250 249 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -506 257 -248 243 -246 247 -248 501 -258 247 -246 245 -246 247 -248 249 -248 249 -504 513 -238 251 -254 255 -490 499 -228 253 -250 249 -506 259 -248 499 -478 257 -244 495 -510 509 -238 255 -254 253 -494 227 -254 249 -248 505 -258 247 -246 245 -248 247 -504 513 -238 253 -472 279 -254 491 -226 253 -250 249 -504 513 -478 257 -68880 315 -216 241 -258 249 -248 247 -248 247 -248 249 -248 249 -250 249 -250 247 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -250 249 -504 257 -246 245 -246 247 -248 503 -516 477 -274 253 -256 217 -254 247 -236 255 -250 249 -502 481 -272 253 -254 255 -254 249 -238 227 -250 249 -504 257 -248 243 -246 501 -516 237 -254 473 -280 253 -250 239 -230 251 -250 247 -504 257 -248 499 -228 245 -274 245 -246 245 -248 247 -RAW_Data: -504 513 -478 257 -244 495 -274 253 -500 483 -232 265 -69136 287 -256 249 -238 227 -250 249 -248 249 -248 249 -248 249 -250 247 -250 247 -250 249 -250 247 -250 249 -250 249 -248 249 -248 249 -250 249 -504 257 -248 245 -246 245 -248 503 -260 245 -246 245 -246 247 -248 249 -250 247 -504 513 -238 251 -256 253 -496 499 -228 249 -250 249 -504 257 -248 499 -478 257 -244 495 -510 509 -238 255 -254 255 -492 227 -256 249 -248 505 -258 247 -246 245 -248 245 -504 513 -238 253 -472 279 -254 491 -228 253 -250 249 -504 513 -478 257 -68886 309 -208 235 -256 249 -248 249 -248 249 -248 249 -250 247 -250 249 -250 247 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -250 249 -504 257 -248 245 -246 245 -248 503 -514 477 -274 253 -254 217 -256 247 -238 255 -250 247 -504 513 -238 251 -256 253 -256 249 -240 227 -252 249 -504 257 -248 243 -246 501 -514 237 -252 473 -280 253 -250 241 -228 253 -248 249 -504 257 -246 499 -226 245 -276 245 -246 245 -248 247 -506 511 -478 257 -244 495 -272 255 -474 511 -240 241 -69150 323 -220 251 -242 229 -252 247 -248 247 -248 249 -248 249 -248 249 -250 249 -248 249 -250 249 -248 249 -250 247 -250 249 -248 249 -250 249 -504 257 -248 243 -248 247 -248 503 -258 245 -246 245 -248 245 -250 247 -250 247 -504 515 -236 253 -254 253 -496 499 -228 251 -250 247 -504 257 -248 499 -478 257 -244 495 -510 509 -240 253 -256 253 -492 229 -254 249 -250 505 -258 247 -246 245 -246 247 -504 513 -238 253 -472 247 -272 501 -246 253 -254 243 -494 513 -476 255 -68934 287 -254 213 -240 257 -250 249 -248 247 -250 247 -250 247 -250 249 -250 247 -250 247 -250 249 -250 249 -248 249 -250 247 -250 249 -250 247 -506 257 -248 245 -246 245 -248 503 -516 479 -272 255 -254 217 -256 245 -236 255 -250 247 -504 513 -238 253 -254 253 -256 249 -238 229 -252 249 -504 257 -246 245 -246 501 -514 237 -254 473 -280 253 -250 239 -230 251 -250 247 -504 257 -248 499 -226 245 -276 245 -246 245 -248 247 -504 511 -478 255 -244 495 -256 243 -496 509 -274 253 -69130 309 -218 253 -256 249 -240 227 -252 249 -250 247 -248 249 -248 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 247 -250 249 -250 249 -504 259 -246 245 -246 247 -248 501 -258 247 -244 245 -248 247 -248 247 -250 249 -504 511 -226 245 -276 243 -500 477 -272 253 -254 255 -494 231 -262 481 -514 225 -246 527 -478 507 -256 241 -244 243 -494 257 -244 243 -RAW_Data: -246 527 -228 243 -276 245 -246 245 -500 513 -238 253 -474 279 -254 489 -228 251 -250 249 -506 513 -478 255 -68936 287 -218 249 -236 255 -248 247 -248 247 -248 249 -248 249 -248 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -250 247 -250 249 -506 257 -246 245 -246 245 -248 503 -514 477 -272 255 -254 255 -218 247 -240 255 -250 247 -502 511 -226 273 -244 243 -244 247 -246 247 -250 247 -506 259 -246 245 -246 501 -512 225 -244 527 -238 251 -256 253 -254 251 -242 227 -508 257 -216 527 -226 275 -244 243 -246 245 -248 247 -504 513 -478 257 -242 527 -238 255 -472 511 -242 281 -119614 299 -200 65 -470 65 -466 297 -630 97 -1592 133 -166 299 -66 231 -100 131 -98 265 -134 165 -166 433 -100 2287 -9916 231 -166 65 -98 199 -166 133 -166 165 -756 65 -724 195 -428 231 -260 263 -98 229 -130 261 -66 163 -264 65 -132 1181 -66 6315 -6798 131 -296 559 -334 131 -166 233 -132 165 -66 133 -264 99 -66 65 -366 99 -630 301 -166 97 -100 167 -164 535 -202 7269 -8266 197 -1022 131 -756 99 -98 99 -164 163 -990 65 -530 163 -230 297 -136 635 -66 2113 -8426 67 -6674 97 -722 197 -362 263 -232 165 -134 99 -234 297 -362 129 -198 131 -556 297 -68 167 -98 331 -200 165 -66 295 -66 8689 -4994 65 -1750 165 -762 163 -864 135 -100 167 -694 1093 -66 695 -102 99 -100 9899 -1650 297 -1216 97 -66 99 -396 65 -198 165 -164 233 -1658 199 -98 465 -134 463 -166 1883 -98 6283 -7302 99 -932 133 -696 263 -298 97 -98 165 -1708 131 -820 229 -98 231 -130 163 -590 131 -130 99 -66 97 -16220 261 -1062 265 -998 197 -1290 97 -362 165 -494 895 -264 7839 -7804 99 -66 99 -364 231 -630 133 -166 427 -496 131 -1252 263 -100 233 -66 133 -132 165 -66 259 -98 3109 -10438 101 -5322 99 -100 65 -666 65 -166 331 -98 197 -132 233 -662 261 -1516 559 -66 263 -130 689 -132 229 -64 3613 -15976 231 -166 133 -66 399 -264 99 -132 295 -366 97 -1692 99 -398 529 -68 397 -130 899 -164 3559 -98 1197 -12106 199 -98 65 -166 99 -266 99 -134 231 -100 133 -132 297 -430 99 -1394 299 -64 397 -166 99 -100 465 -200 331 -132 599 -100 2333 -15214 65 -1230 231 -266 265 -432 165 -398 65 -532 333 -632 65 -232 957 -98 9785 -6320 97 -830 167 -166 133 -732 299 -958 327 -98 197 -66 229 -164 327 -98 653 -66 7993 -6418 65 -1284 97 -458 129 -196 197 -166 393 -134 99 -332 427 -132 131 -66 133 -98 233 -66 133 -364 163 -566 4873 -16030 97 -RAW_Data: -360 65 -364 65 -68 857 -98 65 -232 131 -264 63 -98 391 -396 65 -130 99 -98 65 -66 861 -166 265 -166 7611 -10336 65 -1822 165 -300 165 -166 295 -134 199 -100 67 -264 165 -166 99 -500 99 -198 97 -200 165 -268 197 -130 65 -300 629 -166 561 -132 333 -132 7459 -6294 131 -1096 165 -964 197 -332 65 -166 129 -132 99 -130 99 -100 97 -134 65 -164 131 -494 165 -396 97 -164 131 -198 99 -232 229 -66 821 -64 131 -14954 97 -788 65 -100 263 -66 99 -300 65 -400 131 -198 293 -294 163 -132 65 -692 99 -132 131 -200 1847 -132 8773 -5968 133 -330 65 -66 295 -430 197 -166 565 -132 467 -98 65 -430 165 -262 131 -528 131 -296 131 -100 131 -66 557 -166 787 -98 3221 -16236 299 -166 133 -562 199 -1692 99 -66 65 -364 65 -366 231 -168 367 -100 5541 -14968 297 -164 97 -132 163 -328 99 -532 99 -134 131 -370 397 -66 397 -98 293 -98 197 -98 1151 -66 7019 -6746 129 -296 163 -954 261 -230 229 -64 231 -264 431 -100 99 -466 165 -100 333 -166 133 -666 695 -200 67 -134 397 -100 1667 -7686 97 -426 195 -266 97 -330 63 -98 99 -594 97 -132 133 -270 131 -600 131 -362 833 -98 297 -166 199 -66 99 -200 65 -66 197 -100 2963 -98 1125 -2238 199 -554 275 -242 273 -212 271 -242 241 -242 271 -244 241 -244 273 -244 245 -246 247 -248 247 -250 247 -250 249 -250 249 -248 249 -248 249 -250 249 -504 257 -248 245 -246 247 -248 503 -258 247 -244 245 -246 247 -248 249 -250 247 -504 513 -228 243 -244 273 -246 245 -500 513 -478 257 -242 241 -244 273 -244 499 -512 225 -244 525 -228 273 -496 475 -508 507 -240 253 -254 255 -494 261 -222 505 -258 245 -246 245 -246 245 -250 247 -506 513 -478 257 -242 241 -244 273 -244 499 -68858 319 -218 245 -230 251 -248 245 -248 247 -250 247 -250 249 -248 249 -248 249 -250 249 -250 249 -248 249 -248 249 -250 249 -248 249 -250 247 -506 257 -246 245 -246 247 -248 503 -516 477 -274 253 -474 511 -478 527 -484 511 -240 253 -472 281 -254 489 -484 513 -478 513 -480 255 -242 523 -472 507 -276 253 -462 275 -240 491 -258 247 -244 245 -246 247 -502 515 -238 253 -254 253 -492 497 -230 253 -250 247 -250 247 -250 247 -250 247 -506 257 -68880 279 -248 241 -266 223 -252 249 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -506 225 -280 213 -278 245 -248 503 -260 215 -276 245 -246 247 -248 247 -250 247 -504 481 -272 251 -RAW_Data: -254 255 -254 213 -506 483 -514 271 -254 217 -292 217 -248 497 -516 237 -254 473 -278 251 -488 489 -516 477 -272 253 -254 219 -494 261 -260 481 -260 215 -278 247 -248 247 -248 247 -504 481 -514 235 -290 217 -256 253 -244 497 -69132 311 -212 235 -258 249 -248 247 -248 249 -248 249 -248 249 -250 249 -250 247 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -506 257 -248 245 -246 245 -248 503 -512 479 -272 253 -474 513 -478 527 -482 513 -238 253 -472 281 -254 487 -486 515 -478 509 -480 255 -242 529 -480 509 -240 255 -496 271 -234 487 -260 247 -244 245 -248 247 -504 511 -226 243 -244 275 -498 479 -272 253 -254 255 -254 249 -238 225 -252 247 -504 257 -68882 319 -188 265 -280 235 -228 251 -250 249 -248 249 -248 249 -248 249 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -250 247 -250 249 -504 257 -248 245 -246 247 -248 503 -258 247 -244 245 -248 245 -250 247 -250 249 -504 515 -238 251 -256 253 -256 249 -472 515 -480 271 -256 253 -218 255 -248 497 -482 271 -252 473 -280 253 -490 487 -516 477 -274 253 -256 217 -496 261 -224 507 -258 249 -246 245 -248 247 -248 249 -504 511 -478 255 -244 243 -244 275 -246 499 -69134 311 -208 231 -254 249 -246 247 -250 247 -248 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -248 249 -250 249 -248 249 -506 259 -246 245 -246 247 -246 503 -516 479 -274 253 -464 519 -496 487 -516 477 -274 255 -498 249 -248 469 -514 479 -516 483 -488 277 -254 491 -498 487 -260 247 -498 227 -244 529 -228 243 -274 245 -246 245 -502 515 -238 251 -256 253 -496 497 -226 251 -248 249 -248 249 -248 249 -250 249 -504 257 -68898 247 -274 213 -312 185 -312 185 -312 185 -312 185 -312 185 -312 185 -310 185 -310 185 -310 215 -278 217 -278 215 -278 217 -278 217 -280 215 -532 237 -288 217 -290 215 -276 461 -260 219 -280 217 -280 217 -278 215 -280 247 -506 481 -270 251 -256 217 -290 213 -502 483 -514 271 -254 253 -218 255 -246 499 -482 271 -254 473 -282 253 -488 485 -516 477 -274 253 -256 217 -496 263 -224 507 -260 247 -246 245 -246 247 -248 249 -504 513 -478 255 -242 243 -244 275 -246 499 -69122 319 -220 243 -228 251 -248 247 -248 247 -250 247 -250 249 -248 249 -250 249 -248 249 -248 249 -250 249 -250 247 -250 249 -250 247 -250 249 -504 257 -248 245 -246 247 -246 505 -514 479 -274 253 -476 511 -476 493 -516 481 -270 253 -474 279 -254 487 -484 513 -RAW_Data: -478 507 -506 225 -272 495 -482 505 -254 241 -494 255 -242 525 -226 241 -274 243 -242 243 -496 509 -274 253 -254 219 -494 491 -258 249 -248 247 -248 249 -248 249 -248 249 -504 257 -68896 281 -214 241 -260 247 -248 247 -248 247 -250 247 -250 249 -248 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -250 249 -504 257 -248 245 -246 245 -248 503 -260 245 -246 245 -248 247 -248 247 -248 249 -504 513 -238 251 -254 255 -254 251 -474 515 -478 273 -254 255 -218 253 -248 499 -514 237 -254 473 -280 253 -490 485 -516 479 -274 253 -256 217 -496 263 -224 507 -258 247 -248 245 -246 247 -250 247 -504 513 -478 257 -244 241 -244 275 -246 499 -69128 321 -218 241 -230 249 -248 247 -246 249 -248 249 -248 249 -248 249 -250 249 -250 247 -250 247 -250 249 -250 247 -250 249 -250 249 -248 249 -504 257 -248 245 -246 245 -250 503 -514 477 -272 255 -474 515 -480 491 -518 479 -274 253 -474 281 -254 483 -486 479 -512 507 -506 223 -242 527 -482 509 -240 253 -496 273 -236 487 -260 247 -246 245 -246 247 -504 513 -238 251 -256 253 -492 495 -228 253 -250 249 -250 249 -250 249 -248 249 -504 257 -68860 273 -242 233 -256 249 -250 247 -248 247 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -250 247 -250 249 -248 249 -250 249 -248 249 -504 257 -248 245 -246 247 -248 503 -258 247 -244 245 -248 247 -248 247 -248 249 -504 513 -238 253 -254 253 -256 249 -474 515 -480 273 -254 255 -218 253 -248 497 -516 237 -252 473 -280 253 -490 487 -514 479 -274 253 -254 219 -496 263 -224 507 -258 247 -246 245 -246 247 -248 249 -504 513 -476 257 -242 243 -244 273 -246 499 -68728 129 -604 249 -242 243 -276 213 -276 245 -244 245 -246 245 -278 215 -278 215 -278 247 -248 247 -248 247 -248 249 -248 247 -250 247 -250 247 -250 247 -504 259 -246 245 -246 247 -248 503 -514 477 -272 255 -474 511 -478 527 -484 481 -272 253 -472 281 -254 485 -486 511 -478 511 -480 255 -242 527 -482 505 -254 241 -494 253 -242 495 -254 241 -244 273 -244 243 -494 509 -238 255 -254 255 -492 489 -260 247 -248 245 -248 247 -248 249 -248 249 -504 257 -129658 99 -98 131 -132 97 -100 97 -132 131 -98 131 -496 297 -266 163 -198 99 -398 165 -626 133 -198 531 -166 67 -66 431 -132 331 -100 65 -132 99 -100 2725 -9960 65 -3686 295 -1552 99 -362 195 -100 887 -98 263 -100 1495 -8372 67 -298 99 -100 131 -332 133 -198 233 -398 65 -1060 99 -164 327 -RAW_Data: 2243 -98 331 -100 1129 -66 761 -100 1393 -100 165 -66 2883 -64 357 -66 4703 -68 927 -98 233 -134 461 -66 3855 -134 165 -98 1281 -100 2053 -66 3061 -98 331 -98 8981 -66 365 -66 631 -100 1027 -100 4521 -134 597 -66 3187 -66 2619 -100 3011 -98 1151 -66 953 -100 1423 -66 1755 -166 333 -98 1557 -66 761 -66 865 -66 4837 -132 357 -132 2419 -100 1023 -66 65 -66 2507 -66 131 -66 761 -66 997 -66 333 -100 2259 -68 431 -100 2523 -66 987 -100 363 -66 363 -66 1197 -68 1589 -164 951 -96 5351 -66 697 -100 163 -100 4683 -66 2265 -68 2051 -64 457 -64 3005 -132 1057 -66 2221 -100 1661 -98 695 -100 99 -66 861 -66 1957 -100 731 -132 1857 -100 3177 -98 1807 -98 463 -66 499 -134 1129 -100 3737 -100 1889 -66 263 -98 623 -66 2103 -98 3165 -66 131 -100 195 -66 691 -66 67 -132 531 -66 1857 -100 199 -68 97 -68 197 -68 697 -68 233 -100 3749 -134 1691 -68 3289 -66 3751 -68 65 -100 853 -66 531 -132 1299 -66 1585 -98 65 -98 1577 -66 785 -98 1151 -66 165 -68 397 -100 4255 -100 857 -100 1017 -66 1575 -130 1255 -234 1923 -66 199 -102 301 -66 231 -66 691 -64 227 -64 195 -66 1257 -100 2353 -100 235 -100 1163 -66 5423 -66 2049 -66 1807 -66 523 -198 693 -100 367 -100 597 -100 4013 -100 233 -166 365 -66 1827 -100 1491 -100 785 -64 885 -66 599 -134 2847 -100 667 -100 4943 -98 3319 -98 6729 -98 361 -96 391 -66 723 -132 503 -66 1583 -166 297 -234 2045 -66 1185 -134 661 -66 195 -66 291 -164 523 -98 1679 -134 233 -132 761 -394 855 -100 2003 -164 261 -66 229 -96 953 -66 3889 -66 929 -66 993 -68 3099 -132 1673 -66 1833 -100 563 -100 1131 -100 3219 -232 4411 -100 1095 -100 5315 -100 631 -198 461 -198 1907 -100 1743 -68 863 -132 4013 -64 295 -66 3883 -100 2707 -198 923 -100 2539 -166 629 -100 563 -100 3783 -68 893 -66 2987 -98 2357 -98 1665 -66 599 -66 1259 -232 165 -66 1361 -66 1645 -166 1543 -66 565 -66 401 -134 465 -100 831 -98 2405 -100 1055 -66 2109 -100 1161 -68 431 -100 265 -68 235 -66 463 -66 3453 -100 433 -66 2693 -132 263 -166 729 -134 763 -134 1327 -100 397 -234 795 -68 563 -66 1625 -98 267 -66 4835 -66 197 -66 589 -66 7575 -100 1959 -100 131 -68 297 -134 261 -98 433 -66 1427 -66 2421 -100 2925 -166 1921 -134 1645 -66 97 -132 5423 -100 2423 -98 1065 -66 1715 -132 963 -66 2403 -66 1117 -328 1981 -66 527 -100 427 -164 865 -66 2129 -232 165 -68 165 -66 131 -366 131 -100 2613 -450 -RAW_Data: 937 -900 447 -454 969 -884 479 -466 939 -452 935 -454 981 -454 943 -452 955 -458 945 -452 979 -444 943 -486 945 -448 951 -464 977 -440 967 -478 935 -480 951 -456 969 -460 975 -450 973 -450 979 -470 977 -450 981 -910 485 -466 939 -928 489 -448 971 -940 445 -484 951 -928 485 -484 945 -948 479 -456 973 -918 481 -944 451 -928 471 -478 995 -912 487 -472 977 -15948 479 -444 1021 -910 521 -444 995 -942 471 -480 991 -450 1013 -452 1023 -446 1001 -484 981 -486 1007 -458 1015 -470 1007 -450 1033 -452 1005 -480 1005 -458 1013 -470 1007 -482 1015 -450 1023 -482 1009 -462 1015 -468 1011 -484 983 -482 1021 -944 487 -484 1015 -942 471 -486 1003 -948 503 -478 991 -948 501 -478 1031 -914 521 -486 991 -946 519 -916 511 -944 485 -474 1009 -974 487 -482 1015 -16224 521 -468 1005 -970 503 -478 1001 -954 505 -458 1035 -484 1019 -482 1039 -482 1019 -486 1031 -490 1009 -478 1033 -486 1033 -486 1011 -494 1039 -478 1039 -450 1049 -486 1033 -488 1005 -476 1071 -448 1067 -486 1017 -468 1045 -482 1045 -484 1015 -952 515 -482 1043 -944 519 -482 1049 -950 519 -454 1039 -956 523 -484 1011 -960 505 -486 1065 -956 509 -926 539 -944 519 -480 1017 -984 521 -454 1037 -16440 553 -440 1043 -976 507 -460 1069 -940 513 -486 1041 -480 1067 -482 1033 -476 1061 -472 1043 -510 1049 -486 1041 -482 1043 -482 1065 -476 1037 -486 1069 -492 1037 -484 1047 -504 1047 -486 1041 -484 1041 -514 1015 -520 1049 -476 1053 -490 1041 -980 519 -486 1043 -962 507 -482 1049 -994 507 -500 1043 -946 507 -516 1033 -982 517 -478 1049 -984 509 -976 505 -950 527 -490 1039 -980 519 -486 1047 -111258 195 -428 263 -162 163 -362 97 -132 65 -98 163 -132 825 -100 795 -100 1795 -134 587 -66 229 -100 1349 -164 3261 -66 2305 -132 2219 -66 5549 -234 497 -132 201 -66 667 -298 2369 -68 4381 -66 3909 -134 923 -98 723 -100 1651 -168 1197 -100 65 -66 199 -68 195 -100 197 -134 1135 -66 2787 -66 3163 -68 231 -68 197 -100 6675 -100 667 -98 1125 -66 67 -98 2423 -66 2017 -332 2949 -100 1129 -68 1655 -100 1229 -66 1285 -130 163 -132 1315 -66 525 -98 295 -100 131 -64 427 -132 2207 -98 1153 -66 99 -100 697 -98 1397 -166 863 -66 1393 -132 5005 -66 497 -100 1753 -100 597 -66 1667 -66 397 -100 961 -66 763 -134 859 -64 689 -98 1917 -134 199 -234 167 -100 131 -166 2061 -66 1521 -98 759 -100 983 -66 825 -166 459 -66 2049 -166 1615 -100 829 -234 631 -66 465 -66 1493 -68 433 -66 1623 -132 65 -100 1133 -132 3083 -66 199 -132 199 -68 1257 -66 -RAW_Data: 265 -68 1061 -98 533 -100 1233 -68 1721 -68 995 -100 2535 -66 4193 -232 727 -100 727 -100 2773 -66 133 -98 399 -134 233 -232 67 -66 497 -100 267 -132 1127 -134 1063 -66 565 -132 97 -132 523 -132 919 -66 891 -66 855 -98 495 -66 3363 -296 3199 -98 563 -66 133 -100 495 -98 1165 -134 1161 -166 1849 -98 853 -132 5647 -134 563 -98 1827 -100 131 -100 1125 -132 1659 -132 265 -68 1121 -66 465 -232 431 -68 3589 -98 197 -68 97 -164 1717 -66 1645 -66 397 -66 97 -68 231 -166 631 -100 627 -66 1757 -66 131 -164 527 -98 1285 -328 1213 -134 2059 -100 1791 -68 931 -66 1611 -66 1511 -66 2211 -66 2597 -100 2545 -98 197 -162 1089 -98 589 -360 495 -132 1685 -202 1095 -100 729 -100 2825 -100 231 -100 567 -100 231 -66 1027 -66 131 -68 525 -132 1613 -232 461 -232 1597 -66 627 -198 231 -98 131 -98 65 -100 1229 -68 2507 -64 1349 -66 195 -134 97 -66 1321 -100 855 -132 163 -132 1151 -100 1025 -164 329 -66 891 -98 951 -132 163 -166 591 -98 1149 -132 955 -66 1329 -98 923 -66 331 -64 4269 -66 797 -134 399 -98 267 -170 197 -100 429 -198 1225 -100 331 -100 231 -132 1463 -100 597 -164 331 -66 1863 -134 659 -98 5507 -100 719 -100 131 -64 655 -164 1579 -98 1423 -130 1381 -98 1317 -132 467 -66 495 -132 361 -132 4417 -98 631 -364 299 -100 1499 -132 267 -68 663 -98 691 -100 2433 -66 953 -98 721 -66 1355 -232 897 -134 897 -134 365 -100 267 -132 2059 -132 199 -102 797 -68 695 -66 601 -66 265 -68 499 -66 1327 -164 1355 -64 1279 -66 3257 -66 1351 -66 131 -96 359 -132 499 -232 623 -96 427 -68 1909 -98 591 -98 4671 -100 4541 -66 1491 -66 3347 -98 1277 -100 1679 -198 295 -130 357 -98 697 -98 865 -100 2817 -66 329 -98 787 -64 1117 -66 3313 -202 1721 -100 199 -100 399 -66 199 -132 1891 -100 235 -100 201 -134 765 -166 761 -132 1529 -66 629 -202 861 -130 3501 -98 1377 -100 1741 -164 1509 -66 735 -68 733 -66 265 -166 2015 -134 131 -100 663 -100 2995 -132 1577 -98 1885 -66 2461 -100 1189 -66 1425 -100 201 -100 1691 -100 199 -98 499 -166 233 -100 233 -134 661 -68 1393 -100 295 -164 2079 -66 1289 -66 329 -198 599 -100 465 -98 3995 -98 199 -268 2045 -264 199 -66 1593 -66 165 -68 1561 -164 629 -66 635 -100 1251 -230 2733 -66 1727 -66 629 -100 1229 -132 731 -66 163 -198 131 -100 693 -66 3223 -68 565 -132 1091 -134 531 -100 3223 -68 729 -100 1527 -134 895 -166 1265 -66 527 -100 201 -200 1463 -66 1233 -132 -RAW_Data: 2397 -200 167 -234 1803 -66 821 -100 1351 -66 1687 -100 165 -66 233 -66 1125 -100 2203 -132 197 -98 97 -66 593 -164 4187 -102 529 -66 1161 -68 799 -66 427 -232 263 -66 589 -68 495 -68 197 -100 525 -66 327 -98 427 -130 1551 -66 727 -102 133 -234 265 -98 459 -66 2337 -64 585 -68 297 -68 691 -98 1857 -134 665 -132 365 -66 931 -166 495 -430 689 -196 1191 -98 465 -100 931 -366 1351 -102 3185 -164 1151 -98 2465 -66 2193 -100 331 -134 165 -98 267 -166 1985 -98 889 -132 765 -66 531 -100 1449 -166 457 -98 1715 -66 299 -166 3131 -130 197 -98 817 -294 793 -100 97 -66 3415 -164 1019 -98 1675 -132 197 -100 133 -68 199 -134 3319 -298 297 -66 791 -66 1029 -134 2153 -100 1629 -132 1391 -68 1229 -100 665 -66 2039 -164 461 -64 261 -66 395 -202 395 -166 3159 -134 2253 -166 265 -132 395 -66 887 -98 163 -66 589 -98 227 -130 1151 -230 293 -66 591 -68 527 -132 2883 -100 231 -66 99 -232 761 -134 499 -64 929 -100 167 -300 2259 -100 691 -164 459 -66 493 -132 163 -64 1283 -164 757 -132 295 -264 1023 -100 197 -198 6635 -198 2407 -100 2091 -132 1531 -66 1889 -100 199 -134 3567 -100 2981 -100 263 -198 425 -164 595 -100 231 -68 2691 -66 965 -100 2907 -98 367 -132 885 -198 1721 -100 659 -100 97 -296 495 -166 299 -134 397 -132 699 -66 1165 -66 465 -68 197 -66 659 -66 1543 -66 819 -164 2913 -98 1061 -66 5475 -132 167 -100 1035 -66 3427 -298 429 -166 2723 -66 831 -98 133 -66 133 -66 495 -98 701 -66 1063 -98 1991 -100 3319 -66 263 -66 233 -66 695 -66 593 -132 595 -66 553 -66 459 -66 197 -164 2241 -66 165 -68 959 -98 1587 -166 65 -102 233 -66 465 -134 1227 -100 2359 -66 1959 -198 331 -232 165 -102 531 -100 63 -66 1999 -68 265 -100 429 -66 657 -166 297 -132 823 -100 129 -132 4511 -164 659 -68 299 -66 593 -66 99 -134 65 -100 397 -66 1561 -66 697 -100 429 -66 265 -134 361 -132 195 -130 1319 -66 133 -66 265 -100 397 -268 895 -100 363 -134 433 -66 133 -100 2321 -68 -RAW_Data: -98 3573 -98 533 -68 961 -68 729 -132 559 -166 2189 -100 131 -68 657 -100 1387 -132 133 -68 2255 -68 429 -66 231 -134 793 -100 887 -98 361 -166 2141 -66 227 -130 663 -100 759 -100 1161 -134 1821 -66 327 -98 985 -130 757 -132 131 -132 1693 -66 361 -98 1411 -100 591 -132 1025 -66 663 -66 1065 -166 1059 -166 365 -66 723 -100 1659 -132 1883 -98 785 -132 1031 -66 261 -66 2501 -98 297 -66 1195 -100 691 -134 3009 -100 3921 -66 861 -66 363 -132 3361 -132 723 -66 459 -164 163 -164 333 -66 1291 -98 821 -230 591 -164 97 -262 361 -66 689 -66 733 -66 233 -134 1627 -66 533 -66 195 -100 521 -100 493 -98 493 -98 2173 -66 2037 -132 165 -100 429 -132 695 -100 67 -132 465 -68 1491 -100 1257 -66 965 -100 365 -68 929 -132 561 -66 899 -132 597 -132 861 -100 2627 -166 197 -98 2079 -66 2223 -100 1791 -364 895 -132 1027 -132 235 -68 599 -132 829 -66 197 -132 695 -66 133 -66 531 -68 333 -64 563 -66 265 -132 369 -134 2239 -164 4269 -100 793 -66 1495 -198 821 -164 133 -66 867 -66 797 -66 429 -66 365 -166 1729 -168 959 -100 1417 -66 233 -100 2579 -166 993 -164 461 -66 1529 -68 961 -66 1049 -98 1061 -132 2847 -66 229 -66 397 -134 263 -100 3285 -66 4115 -66 1547 -134 297 -132 431 -100 2895 -100 563 -66 1491 -66 399 -100 721 -66 395 -68 399 -66 1289 -66 293 -164 2307 -98 525 -66 3663 -64 927 -132 499 -134 1127 -264 397 -98 399 -198 131 -100 333 -100 663 -164 921 -166 1481 -262 691 -64 659 -64 2167 -98 3689 -100 833 -100 2085 -66 697 -100 595 -66 923 -134 893 -232 265 -98 367 -66 1157 -66 263 -130 1017 -66 623 -66 753 -100 2873 -132 395 -198 2787 -100 861 -132 3847 -100 297 -66 233 -98 1333 -100 495 -100 1325 -134 367 -66 595 -66 361 -230 4931 -66 1821 -98 329 -98 365 -168 333 -300 897 -100 2777 -66 1945 -132 2601 -66 951 -66 425 -98 789 -98 359 -64 1051 -66 1443 -132 851 -98 625 -100 97 -66 731 -232 263 -134 2757 -68 3021 -166 265 -100 1633 -132 427 -66 233 -98 799 -100 1059 -100 263 -98 557 -68 1063 -66 461 -100 1023 -98 163 -198 1481 -132 1227 -98 327 -100 327 -66 1317 -66 1853 -66 1061 -134 1287 -66 1315 -66 1345 -132 723 -66 1225 -68 1463 -166 3261 -98 2883 -66 563 -100 821 -100 2077 -166 3137 -66 565 -66 1355 -234 1415 -132 165 -66 397 -132 493 -132 563 -166 893 -66 1193 -66 1249 -100 333 -132 2083 -66 921 -100 1225 -262 861 -166 1321 -100 895 -100 591 -98 1249 -RAW_Data: -98 97 -66 6825 -66 231 -68 14077 -66 1787 -66 1547 -64 2617 -66 2925 -66 1723 -132 1529 -66 865 -166 827 -198 431 -66 495 -66 1121 -198 1327 -100 397 -130 557 -66 97 -100 261 -98 723 -98 557 -98 463 -98 463 -100 325 -66 3703 -100 465 -198 1123 -98 2545 -66 361 -66 857 -64 3455 -132 663 -98 1991 -200 825 -100 919 -98 893 -164 1749 -66 7759 -132 3321 -66 1807 -132 527 -66 393 -100 817 -130 657 -164 1485 -98 2367 -66 4171 -100 197 -130 3665 -134 1059 -132 597 -66 533 -66 1023 -98 1253 -134 2021 -100 231 -100 233 -66 197 -66 199 -66 1961 -168 729 -100 531 -100 461 -98 1361 -100 11161 -100 659 -166 229 -98 1675 -98 1027 -100 2063 -298 431 -100 99 -134 1059 -66 199 -100 763 -134 231 -66 233 -102 1761 -98 331 -68 757 -132 425 -64 457 -132 99 -66 2091 -66 567 -164 2121 -68 2125 -132 595 -200 759 -102 797 -132 1345 -66 429 -132 1019 -66 195 -66 791 -68 1227 -68 797 -132 1591 -200 199 -134 165 -66 1053 -66 559 -98 853 -164 825 -100 329 -98 891 -196 689 -132 657 -100 2341 -98 1119 -66 1883 -100 2607 -100 467 -100 1067 -164 6935 -66 2409 -132 855 -66 1809 -98 1119 -164 65 -66 199 -100 233 -132 931 -132 563 -66 1393 -132 567 -66 301 -68 1295 -66 529 -98 793 -66 131 -134 533 -132 827 -132 731 -332 1251 -98 921 -98 327 -198 361 -234 529 -66 1577 -132 97 -134 199 -100 1099 -68 1193 -132 991 -100 953 -98 2895 -166 1679 -98 161 -130 129 -66 1019 -100 261 -264 531 -100 263 -134 299 -68 495 -98 831 -100 531 -66 1357 -100 2051 -100 229 -98 829 -66 427 -66 859 -134 995 -68 665 -66 1793 -134 361 -100 2349 -66 331 -100 197 -66 1591 -66 959 -66 431 -234 2219 -332 661 -66 1487 -100 3381 -68 261 -164 463 -134 3377 -68 1127 -134 691 -66 529 -132 99 -66 6687 -98 889 -132 197 -164 725 -100 963 -66 2947 -132 327 -132 889 -66 393 -98 1581 -100 193 -130 97 -66 293 -66 1675 -100 1887 -98 2017 -100 597 -66 293 -98 557 -100 259 -98 985 -100 1727 -100 165 -100 301 -232 329 -100 533 -98 727 -100 761 -66 961 -68 2759 -100 2019 -66 855 -230 859 -98 1215 -98 1887 -98 131 -98 819 -166 227 -130 723 -132 625 -66 501 -66 429 -66 831 -66 1291 -66 331 -132 431 -132 1389 -100 265 -166 1461 -66 1907 -490 911 -962 409 -508 919 -944 419 -486 913 -526 915 -480 925 -488 911 -488 943 -476 925 -486 947 -462 945 -482 933 -484 951 -460 977 -440 967 -484 945 -468 945 -466 975 -450 983 -RAW_Data: -444 977 -458 975 -456 975 -444 965 -940 485 -430 967 -928 487 -448 981 -910 489 -446 963 -944 485 -430 975 -930 489 -912 485 -896 487 -452 977 -916 509 -454 977 -922 479 -448 985 -15898 487 -456 973 -926 493 -448 1001 -918 481 -484 981 -478 979 -450 1013 -476 977 -450 1017 -476 983 -448 999 -480 985 -484 983 -478 985 -484 981 -480 983 -476 1001 -474 979 -496 973 -486 1015 -476 977 -476 1005 -484 973 -486 1007 -920 505 -456 1011 -918 509 -458 1011 -950 497 -452 1011 -920 507 -458 1007 -950 477 -958 483 -942 481 -480 1003 -918 515 -446 1039 -922 505 -458 1007 -16154 501 -462 1013 -934 517 -474 999 -958 483 -478 993 -484 1037 -450 1027 -482 1027 -452 1037 -482 1025 -444 1035 -490 1009 -482 1013 -484 1001 -484 1039 -474 1003 -484 1045 -464 1011 -480 1049 -446 1037 -492 1007 -482 1013 -510 1005 -494 1005 -970 485 -482 1017 -974 473 -498 1007 -974 481 -486 1047 -948 479 -482 1051 -948 497 -952 519 -938 485 -482 1041 -946 517 -478 1013 -944 503 -472 415 -83640 97 -200 65 -632 131 -300 365 -364 231 -132 1061 -100 3047 -100 1393 -66 499 -66 1091 -66 333 -66 2353 -100 2273 -66 1427 -166 563 -132 1559 -132 427 -100 925 -98 13469 -100 2019 -98 821 -132 1097 -68 297 -100 897 -100 1129 -166 465 -166 961 -66 597 -100 165 -66 267 -100 201 -100 765 -134 297 -66 165 -66 3081 -100 1293 -100 1289 -136 233 -66 357 -66 1155 -166 295 -100 1197 -68 1089 -98 425 -132 1187 -100 523 -98 463 -98 197 -98 131 -98 493 -66 393 -98 2797 -164 359 -232 325 -66 229 -164 625 -98 1215 -164 425 -66 589 -98 195 -66 1083 -100 197 -68 1557 -66 1427 -66 525 -66 429 -132 863 -66 1129 -166 831 -98 265 -98 1183 -66 3157 -100 2735 -98 2819 -166 4645 -66 301 -68 1395 -132 1097 -100 897 -198 629 -200 1419 -132 493 -66 521 -132 697 -100 695 -66 459 -298 859 -66 559 -100 1029 -100 4113 -66 1167 -66 14017 -66 2123 -68 525 -132 861 -100 329 -66 399 -134 1523 -132 327 -64 691 -98 463 -132 1803 -132 853 -166 715 -66 953 -66 525 -98 723 -132 989 -132 461 -98 459 -164 2239 -66 1185 -66 589 -100 1945 -230 1483 -66 399 -66 265 -168 965 -66 197 -168 699 -68 1125 -68 529 -98 491 -66 987 -130 525 -168 397 -66 597 -100 561 -132 1353 -66 391 -132 393 -66 591 -98 557 -98 787 -66 463 -100 199 -134 395 -100 759 -66 295 -130 261 -98 229 -100 99 -100 1595 -66 699 -100 499 -66 595 -98 327 -132 957 -132 331 -100 493 -100 1313 -66 295 -132 197 -198 1279 -RAW_Data: -66 9461 -100 329 -68 27921 -66 24331 -68 13415 -66 6439 -98 133 -66 4193 -98 395 -66 653 -66 983 -66 163 -66 955 -132 1791 -66 861 -100 363 -132 1659 -132 667 -166 467 -134 429 -166 265 -66 4065 -98 293 -98 3183 -130 555 -98 163 -162 259 -100 661 -100 7057 -100 931 -100 1297 -66 2559 -98 1193 -100 333 -100 563 -132 65 -100 793 -66 855 -64 659 -100 929 -102 893 -132 689 -66 3475 -68 1361 -198 331 -134 691 -66 295 -66 425 -164 731 -266 921 -100 599 -100 165 -66 227 -98 1091 -66 263 -66 1215 -100 227 -164 657 -66 953 -132 359 -66 1845 -66 1779 -132 753 -164 393 -66 731 -66 1195 -66 533 -66 797 -132 1623 -98 1281 -100 493 -98 659 -98 2417 -166 799 -132 1259 -100 559 -134 595 -166 199 -66 1461 -198 865 -100 459 -66 463 -166 165 -100 497 -66 1097 -66 1579 -100 1449 -98 885 -98 263 -100 1097 -132 627 -68 329 -132 487 -132 427 -132 361 -66 525 -98 687 -66 1161 -100 263 -66 729 -100 229 -98 559 -66 1213 -100 1015 -66 795 -66 5475 -66 4043 -66 1683 -166 1151 -132 429 -98 1447 -68 261 -98 985 -100 429 -100 1289 -198 2269 -132 7999 -98 1591 -132 3233 -66 861 -66 2087 -98 557 -98 719 -66 981 -98 563 -100 199 -100 523 -100 2319 -134 833 -100 495 -132 197 -66 295 -64 989 -66 1059 -198 7343 -66 2023 -66 963 -66 593 -66 2401 -100 491 -100 959 -66 297 -134 999 -132 99 -68 3609 -230 97 -198 1911 -66 265 -100 1195 -132 633 -132 595 -66 1381 -66 491 -66 1681 -100 297 -100 1827 -132 2269 -100 1351 -132 1513 -66 1225 -134 231 -66 1523 -100 363 -200 1227 -66 2943 -66 923 -134 2249 -66 1809 -100 1121 -132 265 -66 827 -98 199 -66 201 -100 3279 -100 565 -132 1689 -66 395 -66 2979 -134 1065 -66 367 -168 3585 -200 463 -100 563 -66 97 -166 2293 -66 265 -134 1255 -132 2401 -66 1579 -166 365 -100 861 -298 261 -98 761 -66 363 -132 657 -130 63 -130 557 -66 131 -130 2041 -100 233 -66 1791 -100 925 -134 265 -100 1063 -100 301 -168 661 -66 657 -64 263 -64 197 -66 1853 -100 663 -98 231 -66 731 -100 5539 -166 197 -68 1423 -134 361 -68 1727 -68 929 -100 1397 -134 1885 -66 1661 -66 265 -66 1183 -66 295 -166 263 -166 165 -66 329 -66 465 -100 1159 -134 697 -100 2443 -100 393 -98 1093 -66 953 -296 787 -132 425 -66 2019 -66 461 -98 1201 -100 397 -132 3551 -100 1431 -264 725 -330 1455 -66 263 -100 531 -296 499 -100 265 -100 163 -66 1145 -132 1313 -98 2101 -98 261 -132 1083 -66 5403 -RAW_Data: -66 2223 -66 11583 -66 131 -66 5071 -66 3723 -132 1415 -132 6905 -64 9685 -102 4739 -66 3355 -66 5301 -98 29993 -508 897 -950 437 -490 909 -974 423 -510 935 -486 925 -508 943 -490 937 -494 947 -482 941 -484 979 -440 979 -456 1003 -462 975 -460 971 -458 977 -468 973 -484 975 -472 971 -450 1011 -452 1003 -446 983 -480 979 -450 1017 -908 487 -472 971 -944 471 -450 999 -944 485 -468 977 -918 493 -448 1011 -934 499 -920 487 -914 487 -452 1011 -912 521 -446 1009 -904 517 -468 1007 -16000 531 -444 1009 -908 519 -468 1009 -912 515 -466 1009 -452 1021 -466 1011 -450 1019 -480 1017 -448 1045 -448 1019 -460 1041 -450 1019 -480 1019 -450 1039 -474 1001 -480 1021 -484 1005 -476 1015 -480 1017 -484 1011 -486 1017 -464 1041 -446 1047 -922 503 -458 1037 -946 513 -442 1047 -938 503 -480 1023 -916 537 -450 1049 -926 521 -904 519 -942 519 -450 1055 -910 519 -486 1033 -916 519 -486 1029 -16258 533 -464 1015 -940 515 -456 1053 -946 511 -482 1051 -434 1075 -442 1075 -448 1065 -440 1065 -450 1049 -480 1067 -462 1041 -446 1075 -450 1063 -460 1053 -480 1047 -450 1075 -446 1079 -452 1055 -478 1051 -448 1067 -444 1065 -480 753 -66842 99 -1090 465 -332 131 -68 131 -134 99 -132 167 -200 429 -100 1809 -132 2385 -230 265 -102 597 -134 1025 -66 365 -100 361 -66 825 -168 1331 -100 797 -132 431 -132 299 -198 661 -168 501 -100 463 -164 329 -66 559 -98 391 -98 1085 -198 1939 -66 1871 -164 2251 -134 493 -66 719 -198 361 -98 361 -64 197 -132 391 -164 691 -300 489 -98 2139 -66 1413 -66 1875 -196 557 -66 263 -132 1359 -66 1397 -66 631 -100 793 -132 723 -100 65 -66 529 -134 463 -68 789 -100 227 -66 923 -100 2649 -166 363 -66 395 -200 295 -130 1757 -68 2057 -100 1023 -66 359 -66 391 -132 1679 -66 359 -66 1217 -98 663 -98 463 -100 821 -98 165 -98 1589 -132 2367 -98 559 -132 1079 -100 9617 -66 3669 -134 1787 -68 1679 -132 361 -66 555 -100 661 -66 1523 -100 2057 -198 1025 -66 4177 -100 165 -66 265 -132 465 -134 299 -232 265 -100 1125 -132 1461 -132 1295 -100 499 -132 367 -68 263 -66 331 -66 365 -100 1643 -130 197 -132 997 -98 867 -98 1191 -100 2945 -100 2339 -98 1779 -66 295 -132 597 -66 165 -100 665 -100 463 -66 331 -66 593 -100 459 -68 489 -164 855 -66 261 -64 163 -100 4449 -100 859 -100 699 -132 199 -100 1685 -66 301 -132 2317 -68 231 -100 827 -66 1749 -132 99 -64 1185 -100 329 -100 1253 -66 1127 -98 827 -198 363 -132 265 -134 365 -66 297 -66 1125 -66 261 -RAW_Data: -266 29863 -66 2443 -66 5113 -100 5947 -21026 99 -134 301 -132 199 -132 131 -266 163 -196 131 -66 365 -66 465 -98 13819 -98 525 -98 329 -100 893 -66 1259 -66 431 -98 427 -130 1051 -392 463 -200 795 -164 399 -66 1489 -66 1377 -100 1423 -132 597 -100 689 -68 1559 -100 2263 -100 1327 -98 1059 -98 497 -66 595 -132 265 -66 299 -66 199 -66 563 -134 627 -66 165 -134 889 -66 2751 -232 893 -264 231 -66 299 -132 467 -132 861 -68 1263 -164 795 -66 2601 -100 429 -66 1525 -66 961 -98 265 -98 997 -66 233 -68 695 -100 697 -66 795 -66 1195 -66 1223 -68 2173 -66 467 -66 827 -66 535 -68 697 -100 1221 -166 165 -100 365 -132 723 -66 829 -132 2091 -232 265 -66 195 -66 459 -262 499 -100 461 -68 759 -100 1087 -66 259 -164 2845 -66 1365 -98 561 -200 331 -168 201 -166 1397 -198 197 -66 697 -68 1713 -68 293 -134 1317 -66 593 -328 395 -100 499 -132 2251 -100 563 -134 333 -134 1921 -134 1187 -68 561 -132 933 -66 797 -100 631 -100 399 -132 929 -66 2769 -66 851 -130 2047 -66 265 -100 7219 -66 1987 -66 299 -98 2199 -134 1063 -98 2843 -98 655 -132 231 -66 1123 -198 2137 -64 327 -66 3183 -66 1127 -66 631 -100 263 -102 3173 -132 267 -68 1289 -98 1593 -66 2415 -66 1185 -66 359 -132 1051 -66 2169 -66 427 -98 395 -132 793 -98 293 -166 727 -134 131 -100 1287 -98 427 -98 687 -164 823 -64 853 -66 865 -100 763 -66 2025 -100 959 -66 1891 -64 793 -100 763 -66 729 -166 99 -98 399 -134 763 -100 4203 -66 1321 -230 4023 -98 1053 -66 985 -98 1383 -66 3559 -164 1515 -100 2899 -66 797 -134 1169 -100 3055 -134 1615 -66 429 -100 495 -64 1583 -134 923 -66 921 -66 723 -68 1359 -98 787 -98 425 -100 393 -64 1189 -98 263 -98 491 -100 1455 -98 -RAW_Data: -202 531 -66 531 -66 1093 -66 1389 -66 1551 -134 2699 -66 1291 -132 65 -64 657 -98 1083 -164 393 -98 1359 -134 1461 -66 393 -100 561 -130 2113 -132 597 -66 431 -102 1759 -302 985 -66 235 -100 1395 -66 901 -66 1061 -100 463 -66 5673 -66 227 -66 225 -66 855 -66 1581 -132 2503 -100 657 -66 2535 -98 259 -64 1015 -66 231 -132 1197 -66 827 -166 9641 -66 1823 -132 1565 -132 299 -66 797 -66 1631 -132 327 -132 2227 -232 433 -68 499 -100 1793 -66 1161 -132 525 -66 129 -100 361 -66 1765 -132 229 -66 491 -132 2255 -100 3043 -332 299 -100 499 -100 267 -68 2967 -66 991 -100 729 -100 633 -66 529 -98 825 -100 1033 -100 331 -66 723 -100 725 -264 2987 -68 825 -66 2601 -134 333 -100 3181 -134 1059 -100 299 -134 3279 -100 1221 -132 659 -66 3157 -98 1595 -132 1561 -98 201 -134 465 -66 1843 -130 589 -66 1413 -66 331 -100 333 -66 661 -100 265 -68 201 -234 1027 -166 297 -100 1161 -132 1561 -134 629 -66 431 -66 1025 -98 427 -198 1527 -66 793 -66 1903 -66 131 -130 1285 -66 299 -134 397 -98 229 -132 499 -132 4747 -100 2355 -100 263 -132 1915 -132 1749 -132 759 -66 2253 -100 4545 -66 391 -100 521 -100 1083 -100 929 -134 565 -66 2355 -66 1331 -66 167 -100 465 -100 1727 -132 633 -330 433 -66 897 -132 165 -134 331 -98 627 -66 231 -66 167 -66 1397 -66 729 -132 1397 -68 165 -66 1627 -134 2187 -66 231 -134 795 -200 6469 -232 829 -66 3929 -66 891 -98 1977 -100 525 -68 859 -66 921 -264 1029 -68 959 -134 1555 -66 259 -100 687 -66 429 -264 663 -66 1559 -100 1127 -100 2327 -132 1913 -64 4193 -132 293 -98 99 -100 5613 -132 1351 -66 1545 -66 1677 -66 295 -64 1943 -100 595 -132 1959 -166 765 -66 1389 -100 823 -66 1749 -66 1217 -100 597 -100 297 -66 2019 -98 165 -100 4165 -100 67 -100 2477 -262 295 -66 919 -200 3555 -66 229 -66 2531 -98 557 -66 2525 -66 1463 -100 1293 -68 197 -68 1391 -66 1421 -66 595 -164 327 -68 2285 -66 593 -98 99 -68 463 -98 1063 -100 165 -68 99 -100 631 -66 1085 -66 859 -98 6599 -66 1429 -66 233 -66 397 -98 231 -132 1975 -132 333 -66 131 -134 3373 -100 4277 -66 1363 -232 2893 -166 3133 -64 951 -66 2815 -100 425 -98 327 -66 599 -68 1031 -98 133 -68 633 -68 429 -100 1129 -66 327 -130 2679 -66 1321 -100 463 -200 367 -98 667 -66 493 -132 885 -98 2183 -166 559 -98 981 -66 3201 -164 593 -66 493 -130 1923 -166 565 -100 2421 -98 461 -66 1427 -130 1955 -64 197 -66 1643 -RAW_Data: -132 2291 -66 3057 -68 2521 -166 333 -134 503 -400 3235 -66 2329 -68 995 -100 333 -100 97 -166 1757 -100 397 -100 165 -66 2755 -132 297 -134 163 -100 565 -100 1793 -100 1813 -162 1293 -98 97 -66 999 -66 1763 -68 261 -68 2391 -100 765 -364 859 -100 1855 -98 1399 -230 463 -134 301 -198 397 -100 961 -68 431 -134 695 -202 133 -100 365 -66 925 -98 165 -66 365 -132 663 -98 4573 -134 1479 -66 1019 -66 629 -66 233 -68 201 -66 569 -66 295 -134 1755 -296 3199 -100 3261 -168 3373 -132 1425 -100 759 -66 895 -98 201 -100 265 -166 99 -66 695 -66 1091 -66 855 -168 299 -100 229 -164 589 -66 521 -66 655 -134 329 -98 493 -200 429 -66 929 -66 673 -100 953 -66 823 -66 1283 -66 1979 -68 233 -66 1547 -164 589 -132 597 -66 131 -66 265 -100 761 -200 759 -66 689 -332 263 -100 1227 -68 1067 -164 2945 -100 959 -100 995 -100 399 -100 1193 -100 625 -66 399 -66 3021 -134 393 -66 4805 -66 1095 -68 231 -332 399 -166 1663 -68 561 -66 927 -98 1085 -164 1155 -98 627 -66 265 -132 263 -130 2211 -66 2159 -66 1029 -264 2669 -66 295 -66 8747 -100 329 -232 625 -134 429 -68 1329 -168 1355 -98 987 -66 1545 -98 1015 -98 699 -134 133 -134 1263 -66 4687 -166 8299 -66 1349 -434 933 -906 443 -452 949 -894 441 -480 909 -486 907 -456 947 -448 949 -434 969 -454 931 -460 941 -448 933 -450 979 -450 945 -450 935 -458 935 -486 927 -456 947 -450 951 -482 945 -428 975 -446 967 -452 955 -458 945 -912 485 -434 971 -902 481 -450 949 -926 451 -478 941 -920 481 -450 949 -920 473 -450 955 -916 471 -452 981 -918 449 -486 945 -910 483 -924 473 -15780 479 -450 967 -948 449 -482 943 -944 485 -468 941 -484 979 -446 1001 -444 999 -446 967 -484 969 -482 979 -478 947 -484 985 -470 973 -458 983 -492 971 -458 979 -494 971 -458 983 -494 973 -458 981 -498 975 -458 1013 -466 973 -952 475 -458 973 -950 489 -450 1011 -916 481 -478 1001 -918 481 -478 1001 -916 481 -478 1001 -920 483 -480 983 -946 479 -458 1013 -932 485 -952 479 -16040 493 -476 1009 -912 515 -464 1007 -910 517 -464 1007 -450 1001 -488 1001 -484 1011 -450 1019 -458 1039 -450 1031 -454 1005 -484 1009 -458 1015 -476 1009 -478 1035 -462 1015 -468 1007 -480 1001 -486 1015 -460 1041 -450 1017 -484 1019 -482 1009 -952 477 -494 1003 -944 485 -478 1013 -944 519 -482 1013 -942 487 -482 1013 -946 487 -484 1015 -944 487 -484 1015 -946 519 -454 1019 -942 505 -952 487 -16238 517 -468 1007 -942 521 -482 1011 -944 519 -RAW_Data: -450 1019 -482 1035 -480 1033 -460 1047 -476 1017 -484 1007 -484 1051 -484 1027 -452 1039 -478 1035 -458 1049 -480 1017 -480 1035 -480 1035 -472 1047 -484 1027 -454 1039 -480 1033 -488 1031 -488 1009 -962 521 -486 1017 -966 485 -490 1015 -139210 229 -98 461 -364 165 -334 131 -168 2121 -66 1049 -66 1215 -166 297 -136 1449 -100 3877 -100 1495 -234 331 -64 1345 -262 393 -100 529 -132 2921 -164 1223 -132 1807 -66 765 -66 397 -98 3405 -132 2123 -230 231 -66 2541 -100 2489 -98 4397 -132 461 -98 293 -64 991 -66 1125 -166 401 -100 131 -100 99 -100 265 -100 2555 -100 499 -98 1361 -134 265 -166 895 -100 2253 -100 1057 -100 129 -296 1147 -198 197 -66 1163 -66 1935 -98 1675 -66 1103 -100 891 -100 989 -164 1019 -66 2967 -68 1293 -166 3161 -66 133 -264 1065 -100 731 -66 1693 -66 529 -100 165 -68 865 -66 825 -232 1117 -196 2401 -66 3051 -296 229 -132 1843 -132 1687 -68 1119 -68 299 -68 97 -66 4741 -66 197 -200 2319 -100 1097 -66 3765 -66 131 -100 695 -132 2753 -66 2287 -100 1129 -68 331 -98 1433 -132 893 -100 465 -100 801 -66 529 -66 1515 -264 393 -98 263 -66 1831 -166 3533 -100 633 -100 1051 -100 331 -98 795 -134 959 -132 1229 -100 627 -132 2517 -66 165 -98 131 -66 2301 -166 163 -134 465 -66 2767 -66 1019 -66 401 -134 397 -232 893 -66 397 -66 833 -66 199 -66 303 -66 2775 -66 2069 -98 1841 -100 399 -66 793 -98 2793 -68 3769 -100 867 -66 861 -100 399 -66 1859 -100 631 -132 755 -100 689 -66 163 -64 2045 -64 2191 -102 1127 -68 727 -68 625 -164 1381 -66 1153 -132 1115 -98 1017 -100 491 -100 593 -132 991 -98 1415 -98 4813 -66 331 -98 131 -102 1847 -98 197 -68 263 -100 3265 -66 431 -100 493 -98 435 -134 133 -68 1185 -134 395 -100 131 -66 399 -134 767 -134 1125 -66 429 -198 3185 -100 2261 -66 523 -230 2475 -168 1297 -66 3243 -66 1853 -100 1657 -66 459 -66 827 -100 263 -66 303 -234 197 -166 1167 -100 2299 -66 1329 -68 461 -100 763 -132 3819 -366 757 -66 591 -164 621 -98 1445 -100 2155 -100 231 -100 631 -68 1161 -66 131 -166 67 -98 1915 -166 1891 -66 1261 -68 999 -164 165 -132 133 -168 2695 -68 1055 -198 97 -98 229 -66 229 -66 1215 -66 885 -100 303 -132 297 -164 619 -198 459 -64 989 -66 229 -66 597 -134 693 -64 1255 -100 65 -132 331 -66 199 -98 529 -100 2831 -98 1259 -66 4855 -100 1163 -166 299 -66 395 -98 3141 -66 1319 -66 2139 -100 161 -132 261 -130 821 -200 263 -134 931 -330 65 -98 99 -134 793 -RAW_Data: -66 597 -100 231 -68 167 -66 1659 -100 733 -66 1631 -100 165 -66 199 -66 233 -166 165 -100 1925 -68 595 -198 1785 -134 2177 -134 131 -66 1049 -98 3087 -132 195 -64 589 -66 397 -134 329 -66 2565 -164 327 -100 689 -64 1775 -100 5183 -132 1187 -66 329 -66 395 -132 165 -98 261 -98 1247 -64 1217 -66 927 -66 997 -66 199 -98 1419 -66 531 -166 1231 -66 697 -100 97 -66 563 -66 161 -264 3205 -200 525 -98 293 -100 291 -100 133 -66 759 -66 659 -100 983 -64 523 -130 431 -166 919 -66 1097 -100 1757 -66 1119 -66 917 -98 2647 -166 1247 -66 165 -264 1189 -100 899 -134 597 -68 2323 -66 1893 -66 1095 -100 533 -64 965 -100 1817 -130 1215 -66 1879 -64 821 -164 1117 -132 263 -132 131 -66 557 -66 431 -132 661 -100 1183 -98 629 -100 1679 -132 259 -66 623 -98 431 -66 399 -164 923 -100 297 -66 165 -166 2521 -198 99 -66 431 -132 1225 -66 1063 -68 131 -136 631 -66 163 -100 99 -298 965 -68 465 -68 465 -298 2545 -134 2639 -230 1489 -66 299 -66 1991 -234 65 -132 693 -134 429 -102 101 -68 461 -66 3333 -64 1229 -68 333 -66 265 -66 885 -64 3163 -100 467 -66 2651 -164 1221 -100 1527 -66 1259 -134 431 -232 1259 -100 6029 -164 297 -98 1151 -66 1415 -100 5289 -66 2467 -100 493 -132 495 -200 1121 -66 129 -66 757 -166 327 -130 5477 -66 1227 -230 395 -100 265 -132 497 -132 1133 -132 361 -100 1051 -164 3089 -132 1583 -100 65 -68 2315 -100 529 -132 2157 -68 1257 -66 1975 -98 427 -98 1347 -66 719 -164 857 -66 165 -66 1029 -132 297 -132 467 -100 731 -130 1985 -98 199 -166 899 -100 1391 -166 3425 -100 261 -132 721 -66 4845 -98 1193 -68 1225 -66 721 -100 1015 -64 983 -66 557 -130 693 -98 99 -64 1091 -98 197 -100 2321 -66 431 -134 727 -66 467 -102 891 -98 167 -134 2619 -66 393 -64 97 -100 589 -98 1583 -164 301 -68 1481 -98 295 -98 959 -66 365 -98 1253 -66 231 -100 1255 -132 1813 -132 1645 -100 361 -132 395 -100 427 -164 1197 -98 1001 -100 861 -66 1161 -98 195 -100 197 -66 1429 -66 663 -66 1427 -98 665 -66 699 -100 663 -66 855 -196 161 -100 361 -98 823 -66 227 -66 621 -132 1853 -230 461 -230 623 -100 557 -98 229 -98 133 -134 1291 -66 533 -166 627 -134 195 -134 593 -64 591 -66 1019 -66 1049 -262 297 -100 2921 -66 133 -66 963 -134 165 -100 -RAW_Data: -68 521674 -200 213 -258 217 -218 217 -246 193 -242 211 -466 451 -450 225 -212 211 -242 211 -238 243 -200 223 -254 419 -226 221 -432 251 -212 467 -240 203 -450 449 -246 205 -238 217 -218 217 -444 439 -478 431 -230 211 -462 247 -214 435 -454 449 -456 441 -442 471 -448 449 -246 205 -238 215 -220 217 -244 201 -254 217 -430 235 -220 461 -198 223 -442 275 -212 429 -458 245 -212 203 -228 253 -218 431 -448 451 -442 235 -218 463 -426 485 -214 209 -234 221 -254 219 -214 207 -228 253 -428 459 -418 245 -216 243 -194 241 -212 241 -212 211 -244 441 -228 215 -460 217 -244 435 -236 235 -440 439 -246 213 -208 229 -256 217 -430 445 -450 445 -236 253 -420 225 -232 429 -450 465 -448 439 -448 449 -454 453 -230 225 -214 241 -212 211 -242 211 -244 237 -442 237 -218 429 -230 221 -442 241 -246 425 -458 245 -212 203 -228 255 -218 427 -448 451 -446 235 -256 425 -462 417 -244 251 -208 201 -256 217 -218 209 -232 255 -420 451 -448 217 -242 211 -238 209 -234 221 -256 217 -218 437 -238 217 -464 203 -256 419 -226 223 -464 441 -244 213 -240 197 -254 219 -430 445 -452 479 -204 253 -420 225 -222 463 -452 449 -450 449 -452 447 -450 451 -238 215 -220 217 -244 199 -256 217 -218 207 -440 241 -254 423 -238 217 -462 197 -224 471 -448 225 -220 213 -242 211 -212 465 -446 449 -456 245 -214 437 -456 449 -232 233 -216 219 -254 207 -204 253 -218 219 -440 451 -456 237 -216 217 -254 209 -202 253 -218 219 -210 437 -244 253 -422 237 -218 423 -226 247 -430 451 -242 203 -228 253 -220 217 -442 449 -454 449 -240 217 -430 237 -218 461 -426 453 -450 465 -448 451 -440 443 -246 215 -250 201 -224 255 -218 215 -208 227 -442 239 -208 457 -238 219 -464 201 -256 425 -464 201 -256 217 -210 241 -216 451 -426 445 -486 207 -236 441 -430 479 -194 223 -256 217 -218 209 -234 253 -218 217 -438 449 -454 239 -216 217 -256 207 -202 255 -218 217 -210 439 -244 253 -424 235 -218 457 -224 229 -436 449 -230 221 -214 211 -242 211 -468 447 -450 449 -250 207 -454 239 -218 431 -446 451 -448 443 -486 447 -448 431 -240 207 -232 223 -254 219 -216 209 -230 253 -426 233 -234 433 -244 213 -432 237 -218 463 -446 221 -212 211 -242 239 -208 457 -456 451 -456 223 -212 467 -450 453 -204 253 -218 211 -240 215 -218 255 -208 201 -442 479 -452 203 -254 217 -212 241 -216 217 -256 207 -202 439 -240 245 -430 237 -218 463 -240 217 -428 465 -202 253 -218 213 -234 219 -RAW_Data: -444 443 -452 455 -244 213 -440 237 -254 427 -450 429 -480 417 -454 477 -430 445 -244 217 -250 203 -224 253 -220 215 -206 221 -462 217 -212 467 -234 231 -440 239 -220 427 -446 219 -242 211 -244 237 -206 451 -458 451 -458 203 -254 427 -450 441 -246 213 -242 193 -244 211 -242 211 -212 241 -442 451 -454 245 -214 241 -198 255 -218 217 -210 225 -212 461 -246 213 -440 225 -242 435 -240 209 -456 457 -246 211 -204 229 -254 219 -428 445 -452 445 -238 253 -426 231 -232 435 -448 455 -448 439 -446 449 -456 451 -230 223 -214 241 -212 211 -242 211 -244 237 -442 237 -218 427 -232 221 -442 239 -246 427 -458 245 -212 205 -228 253 -220 427 -446 471 -442 237 -216 451 -462 411 -244 251 -216 205 -222 211 -242 211 -242 211 -480 453 -418 245 -252 207 -202 253 -218 219 -210 229 -256 425 -232 221 -430 251 -212 469 -204 235 -446 441 -246 213 -242 193 -244 211 -464 451 -456 441 -248 215 -450 201 -256 427 -452 457 -450 455 -450 439 -446 451 -238 217 -256 207 -202 255 -218 217 -210 231 -442 237 -244 423 -240 217 -464 203 -254 419 -454 227 -210 243 -212 241 -212 477 -418 453 -484 199 -226 431 -267004 97 -422 97 -164 65 -490 97 -2222 559 -66 163 -64 295 -100 497 -100 263 -98 361 -460 793 -66 1803 -100 339 -68 1733 -RAW_Data: 12477 16271 -64 12623 -102 40819 -98 3729 -66 14371 -66 14943 -64 5931 -66 11147 -68 74641 -102 54299 -70 18441 -66 82993 -100 66161 -68 61869 -66 6627 -66 12987 -68 30427 -68 25761 -98 6305 -66 5019 -64 30857 -132 23929 -68 25129 -39378 317 -2020 311 -2010 2033 -312 2017 -282 325 -2018 291 -2024 2035 -316 2023 -276 319 -2028 297 -2032 2029 -284 2023 -314 321 -2016 319 -2012 2003 -310 2015 -318 321 -1984 327 -16000 351 -1974 321 -2020 2041 -276 2047 -288 327 -1992 327 -2000 2055 -282 2053 -274 305 -2022 301 -2014 2049 -286 2055 -274 319 -1992 329 -2006 2065 -282 2023 -316 323 -1988 303 -16008 323 -2014 315 -1990 2053 -284 2061 -274 319 -1988 319 -2038 2033 -286 2055 -264 339 -2008 289 -2034 2035 -284 2061 -262 339 -2008 287 -2040 2037 -286 2035 -268 347 -2006 317 -15988 311 -2014 343 -2014 2027 -300 2031 -258 311 -2024 323 -2028 2023 -280 2051 -318 285 -2024 309 -2002 2039 -312 2017 -318 289 -2020 293 -2028 2035 -316 2023 -276 321 -2030 299 -15982 345 -1988 345 -2014 2029 -268 2069 -258 337 -1998 321 -2024 2025 -320 2025 -276 321 -1994 331 -2000 2057 -282 2043 -282 305 -2034 291 -2024 2039 -320 2019 -278 323 -1992 333 -15976 389 -1978 321 -1986 2069 -276 2053 -288 303 -2020 315 -1980 2067 -258 2075 -258 337 -1996 321 -2030 2027 -284 2059 -274 319 -2004 319 -2016 2033 -282 2059 -264 345 -2006 289 -16006 355 -1984 315 -2018 2033 -312 2015 -284 327 -2000 329 -2000 2063 -282 2049 -284 327 -1984 319 -2018 2035 -284 2057 -264 343 -2004 289 -2032 2059 -280 2019 -316 323 -1984 321 -15968 387 -1978 321 -1984 2069 -276 2049 -288 325 -1996 303 -2004 2055 -284 2049 -278 325 -2018 321 -1984 2075 -276 2049 -288 327 -1990 325 -1996 2031 -318 2025 -278 323 -2018 289 -16010 357 -1982 315 -2018 2035 -278 2053 -288 307 -2022 321 -2000 2049 -274 2045 -284 327 -2022 311 -2014 2033 -276 2053 -286 327 -2000 323 -2000 2061 -282 2015 -316 325 -2002 289 -15994 353 -1980 357 -1990 2035 -312 2015 -286 327 -2020 289 -2022 2033 -318 2019 -278 323 -2004 319 -2016 2059 -282 2023 -316 327 -1970 321 -2018 2059 -280 2019 -318 325 -1972 321 -15988 353 -1982 357 -1972 2071 -276 2051 -288 327 -1988 323 -1996 2061 -280 2053 -284 325 -1984 321 -2014 2031 -282 2059 -276 321 -2018 289 -2036 2031 -282 2059 -276 321 -2018 319 -15984 341 -1984 343 -2016 2029 -298 2033 -258 309 -2028 321 -2032 2035 -284 2017 -298 337 -2008 289 -2034 2027 -314 2023 -310 285 -2016 321 -2014 2035 -282 2027 -308 317 -2026 297 -15982 357 -1984 345 -1982 2067 -264 2057 -258 341 -1996 317 -2026 2057 -282 2017 -318 325 -1972 319 -2018 2059 -280 2029 -302 315 -2006 319 -2006 2059 -274 2047 -276 307 -RAW_Data: -2024 311 -15984 349 -2004 317 -2012 2029 -276 2047 -312 305 -2018 311 -2018 2037 -258 2077 -258 337 -1998 321 -2026 2031 -278 2043 -284 327 -2020 311 -2018 2031 -278 2051 -288 325 -2004 323 -15962 353 -2018 317 -2014 2007 -308 2019 -320 287 -2024 325 -1998 2035 -314 2025 -302 311 -2004 319 -2010 2027 -312 2011 -310 309 -2028 303 -2018 2027 -304 2007 -294 309 -2024 323 -16002 361 -1946 347 -2008 2027 -286 2045 -312 325 -1978 319 -2024 2043 -276 2049 -286 325 -1988 341 -1978 2061 -278 2049 -284 327 -1990 321 -2020 2029 -318 2021 -278 323 -2008 321 -15990 355 -1982 355 -1984 2031 -284 2049 -278 325 -2018 321 -2000 2047 -276 2049 -286 327 -1994 341 -1976 2067 -274 2043 -284 303 -2018 341 -1982 2061 -266 2069 -258 339 -1998 321 -16002 311 -2014 329 -2012 2027 -318 1999 -298 339 -2008 287 -2042 2031 -282 2027 -306 317 -2012 291 -2044 2037 -284 2033 -302 315 -2004 321 -2012 2037 -282 2027 -306 317 -2028 297 -15980 345 -2018 285 -2026 2037 -320 2017 -278 323 -2024 299 -2002 2069 -278 2055 -286 325 -2002 289 -2024 2037 -282 2063 -276 321 -2016 287 -2036 2025 -320 2023 -278 323 -2018 287 -16006 355 -1982 355 -1986 2033 -302 2039 -282 307 -2032 309 -2014 2043 -264 2057 -258 341 -1992 319 -2036 2033 -284 2029 -306 319 -2000 319 -2014 2035 -282 2061 -266 343 -2006 289 -16018 331 -2010 313 -2014 2021 -320 2017 -278 323 -2010 321 -2016 2037 -284 2033 -304 317 -2018 287 -2036 2037 -284 2025 -296 339 -1978 321 -2028 2057 -282 2021 -278 359 -1972 321 -15990 355 -1978 355 -1990 2035 -300 2015 -312 305 -2016 315 -2016 2053 -290 2013 -316 277 -2020 353 -1980 2059 -256 2077 -276 307 -2020 311 -2012 2047 -258 2073 -256 341 -1998 321 -15976 369 -1976 313 -2018 2051 -280 2043 -290 317 -2004 321 -2024 2023 -282 2053 -282 327 -2020 313 -2020 2035 -278 2055 -284 325 -1986 321 -2016 2033 -280 2049 -282 325 -2020 313 -15986 315 -2026 319 -2028 2003 -318 2013 -294 339 -2008 289 -2032 2027 -316 2019 -302 311 -2004 321 -2014 2033 -314 1995 -308 317 -2028 297 -2032 2035 -276 2023 -316 321 -2014 289 -15998 347 -2014 283 -2020 2035 -292 2031 -288 311 -2024 321 -2036 2001 -310 2027 -318 275 -2054 277 -2026 2029 -296 2027 -292 311 -2024 321 -2030 2031 -282 2033 -304 313 -2004 321 -15992 329 -2010 349 -1980 2037 -292 2051 -256 339 -1998 319 -2034 2029 -286 2055 -264 339 -1978 321 -2030 2027 -280 2043 -316 327 -1984 315 -2016 2063 -278 2041 -288 327 -1990 325 -15964 389 -1978 319 -1984 2063 -256 2055 -292 307 -2016 323 -2028 2027 -286 2057 -274 319 -2010 319 -2010 2033 -282 2061 -276 321 -2018 289 -2032 2027 -318 2021 -278 321 -2020 319 -15974 355 -1980 357 -2000 2035 -RAW_Data: -254 2079 -276 309 -2016 309 -2012 2035 -296 2063 -258 337 -1992 321 -2030 2037 -286 2027 -294 333 -1978 321 -2034 2029 -286 2031 -306 315 -2008 321 -15972 355 -1984 315 -2014 2037 -310 2011 -318 323 -1984 329 -2002 2033 -316 2029 -278 323 -2026 299 -2030 2035 -278 2019 -316 325 -1980 323 -2018 2053 -252 2075 -280 327 -1984 355 -15954 345 -2008 309 -2016 2045 -266 2065 -258 341 -235740 101 -202 65 -734 133 -372 401 -68 269 -236 505 -68 235 -234 875 -68 13969 -100 14297 -70 3863 -96 59337 -104 11859 -68 17409 -68 7317 -66 11443 -64 15589 -66 4381 -98 32297 -168 45445 -100 59295 -100 41417 -66 1539 -66 23001 -RAW_Data: 1549 -1166 409 -1138 363 -1144 407 -1144 385 -408 1599 -1138 377 -430 1593 -1138 421 -1110 393 -1126 409 -1136 377 -1162 353 -24626 823 -1176 379 -1150 353 -1174 349 -1158 377 -454 1541 -450 1561 -1190 383 -1118 411 -398 1557 -460 1571 -450 1547 -482 1537 -450 1561 -482 1547 -470 1537 -450 1547 -482 1539 -1176 387 -422 1541 -474 1537 -448 1545 -444 1539 -450 1543 -1136 375 -414 1549 -1142 417 -1102 387 -1118 385 -1140 381 -406 1549 -1180 383 -392 1549 -1140 417 -1122 383 -1116 383 -1140 415 -1102 355 -24628 823 -1188 357 -1180 343 -1172 385 -1150 361 -440 1513 -450 1563 -1184 369 -1158 381 -448 1539 -448 1527 -482 1545 -456 1545 -450 1533 -454 1579 -450 1561 -458 1513 -450 1555 -1182 389 -390 1571 -436 1571 -418 1581 -446 1505 -456 1511 -1160 391 -390 1339 -1108 381 -1098 361 -1150 309 -1124 331 -416 1065 -1142 353 -420 1049 -1148 319 -1156 345 -1128 347 -1134 317 -1138 319 -24716 687 -1178 309 -1138 317 -1166 319 -1162 327 -408 1039 -448 1067 -1152 321 -1132 313 -436 1065 -426 1039 -442 1039 -448 1049 -418 1041 -450 1035 -426 1039 -444 1041 -418 1047 -1144 339 -402 1069 -414 1071 -422 1051 -450 1029 -424 1069 -1144 335 -418 1043 -1178 319 -1134 321 -1174 309 -1164 313 -454 1049 -1148 317 -410 1051 -1160 353 -1124 321 -1166 349 -1122 329 -1178 277 -24734 677 -1178 299 -1182 309 -1154 337 -1144 307 -430 1047 -448 1039 -1174 319 -1164 289 -452 1033 -444 1031 -450 1035 -426 1063 -412 1047 -448 1041 -446 1033 -426 1041 -438 1049 -1152 289 -438 1071 -414 1031 -448 1039 -460 1033 -414 1079 -1146 309 -428 1049 -1178 311 -1154 337 -1144 329 -1162 319 -408 1051 -1160 353 -404 1059 -1164 317 -1154 325 -1140 347 -1152 333 -1138 285 -24770 657 -1178 315 -1162 289 -1184 313 -1152 329 -446 1039 -416 1047 -1170 343 -1128 351 -418 1021 -454 1047 -416 1061 -422 1033 -454 1043 -446 1039 -416 1065 -414 1041 -424 1033 -1178 301 -450 1041 -416 1051 -452 1045 -422 1033 -454 1043 -1172 283 -436 1065 -1146 329 -1176 311 -1150 337 -1148 309 -430 1049 -1174 299 -432 1061 -1180 289 -1178 311 -1162 347 -1150 307 -1160 311 -24722 607 -1296 203 -1264 207 -1266 227 -1252 239 -498 969 -506 1013 -1214 275 -1166 289 -480 1005 -484 1003 -476 1009 -454 1009 -480 1025 -458 1003 -448 1045 -448 1009 -450 1035 -1146 321 -444 1033 -444 1029 -444 1041 -442 1041 -416 1081 -1148 343 -396 1081 -1146 345 -1120 341 -1150 345 -1120 343 -420 1077 -1146 321 -420 1053 -1152 355 -1122 321 -1162 349 -1150 297 -1178 311 -24720 523 -4340 133 -1370 99 -636 67 -1406 393 -98 397 -1320 207 -1260 225 -498 975 -522 937 -540 977 -488 973 -510 987 -490 1005 -456 1003 -476 -RAW_Data: 1015 -450 1005 -1194 289 -454 1011 -478 1009 -478 1007 -450 1033 -446 1035 -1158 353 -404 1057 -1168 309 -1162 349 -1150 309 -1158 313 -452 1047 -1146 321 -420 1059 -1152 355 -1126 319 -1160 345 -1128 353 -1152 309 -78850 165 -1196 97 -3256 65 -626 165 -130 525 -66 625 -430 891 -492 163 -792 163 -66 197 -100 595 -132 229 -15220 165 -464 97 -66 197 -264 99 -998 67 -198 195 -132 65 -296 163 -198 65 -198 691 -66 985 -134 97 -66 1485 -15420 197 -200 63 -132 97 -526 231 -64 263 -754 425 -198 97 -166 97 -132 495 -100 555 -164 391 -98 261 -98 1221 -9074 97 -2604 65 -1942 195 -590 853 -132 1225 -66 987 -1058 97 -100 131 -132 593 -98 425 -66 3965 -11360 65 -730 299 -98 99 -66 133 -232 327 -132 65 -456 163 -132 97 -196 327 -364 561 -264 1851 -234 1191 -8710 65 -596 163 -134 99 -234 97 -168 131 -496 165 -202 65 -20716 231 -68 231 -134 363 -100 133 -132 133 -198 231 -168 131 -166 99 -162 131 -196 295 -66 261 -166 955 -98 695 -66 1215 -9510 65 -3698 165 -328 65 -492 131 -66 129 -692 231 -64 163 -132 163 -98 229 -132 131 -134 97 -100 563 -15732 197 -796 165 -132 99 -562 97 -168 295 -462 99 -66 131 -332 133 -100 299 -66 329 -132 301 -68 267 -12994 299 -98 197 -228 295 -922 63 -988 65 -100 129 -164 823 -98 931 -66 331 -98 955 -9454 231 -100 163 -134 99 -796 133 -628 263 -430 67 -364 399 -98 365 -66 889 -66 5041 -4044 131 -166 265 -298 231 -98 197 -232 197 -164 163 -198 197 -98 65 -132 3525 -15068 67 -698 133 -1056 199 -2322 959 -200 297 -232 1223 -13532 65 -1650 197 -198 65 -330 129 -100 295 -164 65 -66 131 -1290 99 -134 231 -262 889 -98 231 -64 489 -66 663 -66 563 -15458 97 -362 229 -98 165 -496 131 -266 65 -98 165 -832 729 -66 133 -18682 231 -298 263 -132 363 -132 195 -132 361 -232 197 -1480 131 -164 163 -12854 65 -2522 299 -166 1357 -132 99 -98 399 -166 329 -264 395 -64 195 -196 1055 -132 1417 -14994 197 -132 459 -824 131 -428 133 -832 231 -200 65 -432 231 -100 467 -66 1151 -100 1741 -8616 97 -6904 1085 -198 261 -196 261 -232 265 -132 563 -166 65 -166 197 -100 331 -134 2009 -8664 97 -164 65 -64 263 -132 357 -64 97 -166 493 -166 165 -98 195 -130 361 -854 891 -66 365 -166 1019 -15320 97 -198 331 -166 1359 -266 229 -134 65 -100 331 -98 65 -132 265 -20774 65 -2222 97 -66 229 -132 161 -162 133 -18134 229 -198 65 -132 197 -200 263 -364 97 -100 427 -526 65 -460 131 -428 -RAW_Data: 655 -98 2625 -8562 99 -432 97 -1924 67 -632 199 -498 65 -100 1819 -132 197 -228 263 -232 133 -296 229 -66 97 -20434 199 -432 65 -764 65 -232 233 -232 133 -334 463 -232 329 -98 357 -130 131 -132 1423 -4018 133 -1984 65 -2926 99 -930 97 -430 1293 -100 531 -66 493 -100 131 -100 429 -134 465 -132 3063 -9636 67 -3696 97 -132 229 -298 131 -694 627 -132 1247 -132 297 -166 133 -66 199 -166 663 -21440 133 -400 131 -130 99 -66 531 -232 229 -134 131 -202 165 -564 131 -1258 65 -100 133 -132 299 -166 1325 -66 833 -66 1521 -9032 97 -1544 295 -98 231 -164 261 -66 131 -394 361 -296 163 -298 463 -66 195 -96 721 -13330 65 -468 99 -134 65 -4696 199 -166 659 -98 361 -198 229 -132 557 -166 625 -164 229 -66 329 -100 131 -130 263 -66 1221 -8436 329 -198 99 -66 99 -66 165 -398 65 -1326 97 -794 165 -592 131 -66 265 -266 99 -430 2421 -100 465 -100 199 -66 699 -100 65 -132 1351 -66 897 -130 1653 -15200 231 -264 195 -296 99 -328 295 -296 163 -98 129 -98 295 -264 131 -398 65 -232 97 -98 887 -132 3157 -12396 199 -134 131 -66 231 -200 267 -132 265 -500 97 -732 131 -200 165 -396 763 -166 859 -66 1391 -9164 65 -3808 165 -66 131 -1692 65 -100 1017 -330 65 -132 65 -196 685 -198 65 -198 165 -98 231 -68 99 -22854 231 -168 67 -200 65 -66 263 -100 201 -302 65 -134 65 -22948 165 -396 263 -134 131 -68 165 -862 231 -1494 1261 -66 399 -302 3089 -6572 65 -396 65 -4140 7317 -5010 7415 -15982 811 -1174 381 -1122 381 -1148 351 -1164 345 -466 1509 -482 1541 -1176 387 -1144 363 -416 1519 -478 1525 -470 1533 -460 1539 -444 1535 -460 1531 -476 1527 -448 1513 -482 1509 -1178 379 -398 1549 -444 1539 -450 1547 -446 1541 -450 1545 -1142 381 -426 1529 -1172 381 -1140 387 -1114 395 -1130 379 -438 1531 -1172 381 -398 1555 -1192 351 -1146 407 -1122 381 -1146 381 -1126 349 -24662 821 -1174 379 -1122 383 -1146 415 -1102 385 -426 1567 -432 1567 -1178 381 -1120 385 -444 1511 -482 1515 -480 1539 -452 1545 -446 1545 -486 1541 -442 1555 -456 1495 -466 1531 -1148 387 -422 1509 -466 1533 -456 1539 -470 1503 -458 1539 -1164 351 -424 1509 -1190 353 -1148 369 -1130 379 -1136 317 -404 1331 -1128 319 -406 1231 -1140 337 -1114 349 -1122 329 -1140 349 -1120 293 -24712 627 -1236 237 -1226 233 -1230 271 -1228 239 -480 1017 -452 1039 -1184 293 -1172 275 -464 1041 -444 1007 -450 1033 -448 1035 -430 1047 -450 1031 -428 1047 -444 1005 -466 1015 -1158 289 -460 1047 -430 1033 -440 1039 -442 1033 -442 1037 -1168 309 -436 1051 -1140 -RAW_Data: 343 -1158 311 -1152 347 -1120 341 -420 1041 -1178 319 -422 1051 -1150 321 -1152 345 -1126 351 -1118 345 -1158 313 -24714 689 -1170 317 -1154 323 -1138 349 -1154 301 -448 1037 -416 1083 -1150 345 -1124 313 -454 1047 -418 1031 -446 1045 -448 1037 -416 1061 -412 1043 -442 1053 -426 1039 -436 1033 -1170 309 -418 1039 -454 1049 -418 1059 -424 1049 -452 1029 -1158 319 -454 1031 -1152 353 -1152 289 -1176 311 -1162 349 -418 1051 -1148 319 -454 1027 -1152 353 -1124 319 -1162 347 -1152 333 -1144 311 -24722 685 -1138 343 -1138 329 -1162 353 -1130 325 -412 1069 -414 1085 -1150 311 -1160 311 -422 1081 -416 1031 -444 1049 -414 1071 -416 1063 -430 1037 -440 1039 -412 1049 -450 1039 -1140 351 -420 1049 -418 1065 -420 1045 -448 1041 -414 1053 -1148 345 -398 1085 -1140 315 -1166 355 -1126 319 -1158 351 -398 1071 -1140 317 -432 1071 -1138 343 -1130 339 -1144 331 -1160 319 -1162 289 -24740 693 -1130 353 -1156 319 -1136 347 -1150 301 -448 1041 -416 1063 -1178 297 -1180 311 -428 1047 -448 1037 -416 1065 -428 1037 -442 1051 -428 1039 -440 1051 -428 1037 -434 1033 -1172 311 -414 1071 -420 1049 -428 1035 -440 1069 -414 1051 -1150 343 -398 1083 -1148 345 -1120 341 -1148 347 -1122 341 -416 1071 -1148 319 -420 1061 -1152 353 -1126 321 -1158 349 -1150 297 -1176 277 -24762 631 -1238 239 -1232 271 -1230 243 -1210 271 -470 1007 -476 993 -1222 281 -1202 269 -480 1011 -454 1035 -446 1039 -446 1041 -416 1047 -450 1045 -420 1033 -454 1015 -444 1051 -1154 321 -408 1071 -414 1065 -398 1065 -440 1049 -428 1037 -1172 317 -434 1069 -1138 317 -1152 327 -1170 315 -1164 319 -410 1081 -1162 319 -404 1061 -1166 317 -1154 325 -1140 347 -1152 333 -1148 311 -24742 691 -1138 341 -1138 317 -1164 353 -1124 319 -434 1069 -412 1069 -1148 325 -1140 347 -398 1075 -414 1069 -414 1051 -452 1047 -422 1051 -416 1057 -428 1049 -450 1029 -420 1033 -1166 335 -418 1041 -416 1063 -412 1079 -412 1071 -414 1065 -1148 325 -410 1069 -1142 351 -1152 309 -1160 349 -1116 329 -436 1051 -1162 319 -426 1051 -1148 327 -1172 311 -1156 337 -1150 345 -1120 305 -83468 65 -1920 133 -98 397 -1062 199 -464 165 -728 165 -168 97 -466 497 -132 1123 -66 3163 -12612 229 -198 65 -134 363 -66 67 -132 97 -896 197 -132 199 -166 165 -166 67 -134 165 -334 365 -134 2963 -15534 231 -132 231 -168 165 -262 97 -266 65 -724 65 -166 97 -198 295 -98 131 -132 563 -100 1483 -8892 99 -954 131 -234 67 -432 1087 -98 687 -132 163 -8794 165 -6460 165 -332 331 -498 99 -200 199 -266 67 -100 131 -596 165 -332 67 -98 299 -100 265 -68 633 -100 5793 -4102 99 -198 65 -566 -RAW_Data: 65 -658 99 -132 165 -332 167 -198 99 -132 133 -2530 65 -166 795 -300 197 -366 227 -262 361 -66 2073 -20762 65 -598 231 -264 97 -592 327 -132 295 -132 297 -100 363 -68 763 -20474 63 -166 757 -200 391 -100 97 -134 131 -134 591 -98 261 -262 229 -64 195 -100 195 -100 65 -132 261 -100 919 -66 333 -13642 65 -2324 131 -130 193 -462 195 -930 865 -66 2157 -100 1923 -12176 65 -168 197 -196 131 -1250 65 -132 391 -232 659 -66 393 -100 459 -230 197 -296 3479 -12728 99 -166 131 -134 131 -366 131 -98 97 -198 263 -164 231 -2074 693 -66 597 -66 433 -98 2509 -15286 65 -198 65 -198 165 -200 461 -132 757 -16060 65 -6768 199 -98 131 -262 163 -130 197 -198 165 -398 233 -334 65 -132 131 -166 331 -134 231 -564 365 -66 265 -100 465 -66 433 -100 6915 -6612 99 -264 65 -230 63 -4800 97 -66 393 -134 65 -100 165 -100 265 -66 165 -1818 99 -230 331 -66 265 -13068 131 -100 295 -298 427 -132 131 -1530 65 -100 165 -500 165 -132 995 -68 1685 -15810 263 -130 163 -462 231 -100 233 -632 165 -528 327 -196 197 -198 1253 -100 3583 -6278 97 -788 99 -3200 65 -166 133 -438 99 -132 165 -300 329 -166 1723 -134 725 -164 133 -16880 97 -960 99 -696 65 -200 231 -132 299 -66 297 -164 131 -730 197 -23052 365 -66 99 -332 131 -166 297 -494 429 -1326 265 -132 295 -66 367 -232 263 -66 857 -15396 395 -130 229 -98 233 -132 331 -364 65 -100 529 -68 231 -830 297 -100 233 -66 8533 -5582 101 -168 67 -3968 231 -66 129 -132 163 -1154 97 -166 199 -166 233 -132 467 -134 263 -66 431 -66 363 -66 957 -100 1821 -132 497 -132 1159 -15358 397 -66 99 -98 65 -66 265 -564 65 -68 97 -166 99 -100 165 -134 297 -896 165 -330 97 -134 963 -132 4737 -7096 65 -5664 265 -266 97 -166 265 -598 65 -332 65 -66 65 -198 231 -132 329 -100 65 -17202 99 -1564 595 -98 329 -198 227 -66 459 -230 97 -1480 63 -66 131 -166 99 -166 931 -8662 133 -398 99 -134 265 -98 299 -264 233 -66 99 -100 99 -66 97 -166 199 -166 265 -234 5785 -10012 163 -2324 331 -196 331 -134 65 -100 297 -100 165 -100 131 -332 163 -302 297 -164 199 -300 199 -166 229 -68 99 -68 959 -15384 365 -134 263 -1818 229 -698 99 -232 131 -100 789 -66 491 -132 4039 -12084 99 -134 331 -796 265 -132 265 -362 167 -2310 65 -98 131 -100 525 -164 295 -15032 297 -560 197 -330 131 -196 397 -266 197 -200 263 -68 261 -496 329 -166 361 -166 1517 -66 331 -10126 97 -3736 99 -1626 131 -100 -RAW_Data: 3487 -68 18413 -66 6689 -17938 65 -68 495 -98 1129 -860 697 -166 395 -100 463 -100 163 -14692 263 -794 65 -64 99 -2922 131 -200 97 -168 99 -100 199 -962 495 -68 165 -98 299 -198 133 -168 3917 -98 963 -132 461 -100 1089 -166 331 -134 633 -164 201 -100 363 -164 335 -200 265 -68 531 -166 699 -132 529 -166 397 -98 895 -168 65 -100 399 -366 463 -66 197 -134 431 -66 163 -98 623 -166 301 -98 263 -98 263 -66 197 -98 329 -98 525 -66 331 -200 1025 -66 629 -132 763 -166 233 -66 431 -132 133 -100 429 -264 165 -132 299 -166 429 -68 4605 -134 593 -134 4917 -60834 167 -10282 1941 -1122 865 -2086 921 -1072 1921 -1042 939 -1056 945 -1044 939 -1052 945 -1024 971 -1024 973 -1002 969 -1010 989 -1996 1007 -994 979 -1014 1965 -1014 977 -1020 969 -2004 1993 -982 1011 -982 1005 -984 977 -2008 981 -1006 1993 -1966 999 -994 1007 -982 1005 -984 1007 -996 975 -1016 1987 -996 973 -1016 969 -1004 1013 -1970 1011 -984 1999 -1970 1995 -984 1021 -1968 1005 -994 1003 -970 999 -984 1023 -974 1015 -8920 1995 -1014 1001 -1966 1013 -986 1991 -986 1007 -996 973 -1012 999 -978 985 -1006 1005 -984 997 -978 1015 -1000 973 -1982 1025 -982 985 -1008 1973 -1004 1007 -982 983 -1998 2009 -972 1009 -980 999 -978 1011 -1970 1011 -986 1999 -1978 1007 -988 1007 -994 975 -1012 1003 -972 1009 -978 1987 -986 1007 -982 1017 -994 971 -1990 1009 -984 1997 -1978 1999 -1010 993 -1964 1005 -1000 1007 -980 997 -984 1017 -972 999 -8932 2031 -980 989 -1988 985 -1016 1983 -986 1007 -994 975 -1016 975 -998 977 -1012 999 -982 985 -1006 983 -1018 993 -1968 1017 -996 975 -1016 1965 -1012 979 -1018 971 -2004 1991 -978 1003 -984 1015 -992 973 -2004 1005 -976 1991 -2008 975 -986 1005 -998 977 -1012 999 -978 985 -1010 1973 -1004 1013 -980 981 -1004 1007 -1996 975 -986 1993 -2006 1961 -1002 1013 -1974 985 -1008 1011 -978 997 -1006 981 -1012 991 -8918 8005 -3924 1887 -174620 131 -3974 131 -298 559 -230 65 -132 265 -98 863 -168 333 -66 299 -234 463 -166 331 -102 697 -200 199 -98 265 -100 663 -166 331 -66 599 -132 99 -66 1261 -66 399 -100 265 -100 1199 -66 265 -166 101 -66 599 -232 197 -100 561 -66 499 -132 797 -132 427 -66 265 -298 465 -66 565 -198 97 -100 695 -100 531 -132 267 -66 429 -98 231 -100 331 -100 333 -98 363 -198 67 -100 165 -100 231 -100 729 -134 1061 -100 493 -164 665 -132 259 -128 257 -130 329 -98 361 -66 263 -100 227 -66 491 -98 295 -166 229 -132 229 -130 263 -130 233 -100 331 -232 99 -100 165 -66 667 -164 795 -66 695 -68 429 -132 -RAW_Data: 559 -66 16863 -66 19215 -12276 65 -2086 131 -2188 65 -132 67 -166 299 -364 65 -100 131 -468 233 -200 497 -498 297 -100 363 -198 329 -130 327 -164 229 -64 427 -164 657 -132 829 -532 895 -66 297 -130 425 -166 363 -132 329 -100 465 -100 465 -198 65 -262 429 -98 495 -100 295 -528 131 -132 1129 -134 259 -130 327 -100 397 -66 261 -230 99 -66 229 -132 625 -68 131 -100 587 -66 329 -64 293 -130 1741 -132 325 -132 197 -98 261 -98 99 -132 561 -166 165 -66 391 -66 329 -98 231 -132 465 -100 331 -300 199 -132 299 -132 165 -164 229 -98 565 -100 395 -132 131 -100 327 -130 97 -98 163 -132 293 -98 557 -66 199 -166 233 -132 265 -166 627 -100 265 -132 595 -166 99 -98 199 -100 267 -166 795 -166 265 -166 297 -66 131 -166 331 -134 793 -164 263 -198 299 -100 265 -166 257 -100 197 -198 599 -132 333 -66 265 -100 229 -100 165 -204 465 -166 97 -232 365 -166 431 -68 199 -166 99 -236 365 -132 1297 -66 1361 -100 989 -98 297 -132 12467 -232 361 -100 263 -132 261 -196 229 -296 65 -66 329 -164 229 -164 263 -66 563 -232 3415 -132 11115 -100 97 -100 363 -164 99 -66 199 -100 99 -66 561 -164 259 -168 233 -132 333 -166 567 -98 297 -66 399 -66 399 -298 327 -132 685 -98 393 -98 819 -100 231 -270 429 -68 461 -132 131 -134 1121 -230 787 -132 1393 -132 233 -100 331 -66 497 -166 563 -196 425 -98 525 -66 723 -98 265 -134 365 -66 1297 -166 433 -98 165 -134 231 -134 263 -100 369 -198 99 -100 631 -634 331 -132 565 -132 265 -102 267 -98 467 -200 297 -130 731 -100 367 -98 229 -130 99 -98 263 -164 493 -100 297 -198 429 -66 425 -66 1021 -164 295 -198 97 -198 263 -132 425 -98 493 -98 197 -98 593 -264 65 -294 197 -66 295 -66 561 -196 565 -166 2675 -132 19393 -134 5403 -164 369 -166 467 -132 361 -526 395 -66 361 -132 529 -66 431 -66 465 -166 363 -98 65 -98 229 -98 359 -100 197 -132 201 -100 467 -166 397 -100 297 -130 229 -98 361 -98 199 -130 297 -66 659 -132 261 -98 819 -98 393 -132 329 -132 557 -132 163 -130 657 -66 295 -134 1189 -100 399 -102 861 -100 369 -132 331 -134 263 -666 65 -134 427 -64 197 -98 559 -68 297 -98 493 -164 197 -164 525 -66 131 -98 493 -66 595 -100 233 -100 265 -132 65 -66 465 -266 527 -98 855 -132 493 -66 393 -298 331 -100 131 -66 859 -198 495 -100 265 -98 367 -66 727 -196 535 -66 263 -66 397 -332 65 -198 331 -134 297 -330 -RAW_Data: 261 -100 3291 -68 2553 -19050 1855 -100 1259 -200 165 -100 39479 -100 233 -68 1027 -166 595 -66 231 -168 199 -98 265 -232 563 -166 795 -98 99 -460 65 -362 1017 -98 229 -98 625 -66 231 -98 161 -196 363 -98 363 -100 631 -132 297 -66 333 -300 99 -66 295 -230 985 -100 623 -132 1319 -100 65 -68 231 -232 197 -232 331 -100 199 -168 567 -166 133 -232 823 -396 327 -132 855 -232 165 -100 401 -166 599 -198 167 -132 299 -198 663 -132 165 -134 67 -66 1687 -66 1705 -100 265 -232 297 -100 531 -66 333 -100 1263 -66 297 -164 299 -98 431 -398 97 -66 199 -296 231 -232 925 -100 131 -132 229 -164 361 -66 267 -166 197 -100 491 -132 261 -132 1183 -98 891 -100 265 -100 99 -100 725 -100 303 -60886 131 -10310 1911 -1226 749 -2146 899 -1084 1905 -1070 917 -1044 973 -1042 927 -1050 949 -1044 965 -1012 949 -1046 965 -1014 977 -2008 981 -980 995 -1012 1969 -1018 971 -1014 975 -2014 1967 -1016 971 -1014 977 -984 1003 -2006 973 -1000 2001 -1978 1013 -980 1001 -972 1009 -978 991 -1010 983 -1004 1989 -1012 983 -978 995 -1014 983 -2002 977 -984 2023 -1968 1997 -986 999 -1970 1003 -1000 1009 -978 1017 -974 1009 -978 1001 -8948 1975 -1016 973 -2004 999 -978 2001 -986 1001 -978 1009 -986 1001 -1010 975 -998 1011 -992 975 -982 1005 -1000 1009 -1970 1011 -982 1009 -984 2003 -978 1001 -976 1015 -1966 1999 -1010 989 -982 989 -1004 1001 -1978 1013 -978 2005 -1966 1019 -972 1011 -980 997 -976 1011 -1004 999 -966 1997 -1000 999 -1002 975 -986 999 -1994 1007 -980 1993 -1978 2001 -1008 991 -1970 1009 -998 975 -1016 971 -1006 1013 -980 1001 -8922 2001 -994 1009 -1972 997 -1012 1971 -984 1005 -1012 977 -998 1009 -978 1011 -984 977 -1012 979 -1016 977 -1010 977 -2010 981 -1006 971 -1020 1985 -984 1005 -978 999 -1990 1997 -980 997 -1010 981 -1014 989 -1976 1005 -966 1995 -1998 997 -982 1021 -972 1003 -986 997 -1012 975 -982 1995 -1014 999 -980 983 -1004 1003 -1976 1007 -980 1983 -2012 1983 -998 977 -2010 973 -1014 973 -1018 973 -1014 975 -1016 971 -8956 7999 -3926 1865 -177334 165 -166 199 -496 431 -66 1319 -132 297 -164 457 -68 231 -100 863 -98 603 -100 663 -100 789 -262 491 -166 729 -100 795 -364 365 -132 293 -100 197 -98 625 -200 231 -132 229 -132 227 -100 755 -526 329 -134 793 -100 167 -200 335 -66 65 -134 265 -66 201 -66 1023 -100 63 -21396 133 -198 65 -66 263 -98 393 -134 65 -66 329 -68 199 -432 263 -394 131 -130 195 -66 687 -66 295 -164 229 -196 97 -98 559 -98 1283 -98 531 -66 331 -68 435 -100 393 -628 431 -132 99 -66 -RAW_Data: 329 -66 2519 -100 6759 -12690 463 -232 97 -3252 65 -2592 65 -132 99 -198 67 -496 99 -300 231 -132 233 -66 165 -132 131 -164 201 -66 4113 -66 1987 -68 15385 -98 8199 -16614 165 -1258 495 -1354 229 -166 297 -134 3653 -100 12533 -66 3759 -162 591 -132 361 -130 527 -100 327 -200 65 -66 461 -132 327 -132 461 -100 559 -198 263 -132 161 -98 329 -132 327 -68 395 -66 165 -132 163 -196 261 -66 361 -98 229 -132 301 -134 761 -66 399 -166 99 -66 461 -98 857 -98 131 -262 359 -132 199 -100 299 -166 363 -132 297 -132 199 -132 265 -166 133 -100 301 -268 463 -98 231 -134 265 -98 527 -298 265 -136 531 -132 363 -132 129 -130 527 -132 297 -66 297 -68 397 -466 99 -132 697 -98 233 -464 131 -132 365 -134 465 -98 635 -98 299 -66 497 -132 197 -132 489 -166 327 -98 1151 -130 295 -132 623 -164 97 -68 297 -100 1293 -100 1759 -66 1259 -132 729 -100 797 -66 99 -66 231 -98 529 -166 331 -166 299 -202 431 -134 467 -462 329 -462 1051 -98 625 -66 195 -100 461 -196 429 -132 197 -66 129 -66 229 -166 589 -164 629 -66 1053 -166 231 -98 263 -132 329 -132 267 -132 427 -130 65 -130 229 -166 4121 -66 15899 -19980 495 -264 165 -432 65 -132 65 -662 663 -100 793 -66 723 -164 565 -100 363 -166 199 -98 465 -100 299 -168 265 -198 695 -132 131 -268 497 -66 265 -134 199 -98 231 -134 499 -100 989 -132 429 -196 327 -132 131 -164 427 -66 263 -66 525 -132 1091 -100 793 -132 491 -66 195 -526 921 -134 363 -98 693 -230 165 -98 1711 -132 293 -66 659 -196 231 -66 261 -66 163 -66 1349 -166 363 -100 65 -100 299 -100 393 -66 495 -134 229 -98 131 -98 463 -362 361 -132 231 -66 491 -396 361 -132 97 -66 163 -198 229 -294 229 -198 165 -164 65 -100 623 -66 195 -98 261 -130 65 -98 563 -66 267 -166 1057 -632 531 -132 463 -100 663 -166 133 -100 569 -164 197 -66 431 -164 261 -132 363 -100 427 -164 263 -198 623 -66 589 -166 133 -166 199 -66 199 -268 65 -98 427 -66 163 -132 563 -198 363 -166 397 -98 1025 -66 197 -66 163 -132 693 -164 195 -66 427 -100 131 -66 433 -132 199 -402 231 -66 335 -100 993 -100 463 -232 65 -100 65 -66 7165 -66 6785 -66 895 -66 923 -198 97 -164 689 -166 361 -198 99 -66 753 -498 365 -66 567 -232 397 -66 199 -132 233 -200 995 -296 365 -166 597 -100 199 -134 99 -166 197 -332 431 -166 331 -66 199 -100 431 -232 493 -166 457 -98 231 -132 427 -230 559 -98 -RAW_Data: 261 -132 1051 -66 7501 -98 22107 -19232 2271 -166 133 -60834 165 -10284 595 -5446 885 -1106 1891 -1088 897 -1068 949 -1050 929 -1040 947 -1048 959 -1008 985 -1008 973 -1012 993 -2004 979 -998 975 -1014 1987 -998 977 -1014 971 -1998 2007 -974 1013 -976 999 -982 1023 -1962 1005 -1000 1969 -2010 977 -1016 971 -1004 1009 -980 985 -1002 979 -1014 1977 -1012 979 -1016 971 -1012 977 -1986 1021 -974 2005 -1968 2007 -976 1011 -1966 1009 -984 1009 -984 985 -1020 969 -1002 997 -8952 1995 -986 1017 -1988 989 -976 1979 -1028 975 -988 1009 -986 1007 -992 973 -1014 1001 -974 1011 -978 995 -1006 979 -2004 975 -1022 973 -984 1999 -1014 977 -984 1003 -2004 1995 -976 1005 -998 999 -982 981 -2002 983 -986 1989 -1998 1009 -976 999 -1010 991 -978 1013 -968 1011 -986 1995 -982 1023 -974 981 -1020 993 -1970 1007 -996 1995 -1982 1999 -986 993 -2002 985 -998 975 -1014 969 -1004 1009 -978 997 -8962 1989 -990 1009 -1954 1025 -984 2007 -962 1011 -980 1001 -982 1009 -1002 1007 -980 1005 -976 1007 -980 997 -976 1009 -2002 977 -988 1005 -982 2001 -976 1009 -988 1005 -1968 2029 -974 1003 -990 1011 -958 1005 -1972 1039 -978 1999 -1978 1009 -954 1037 -978 1007 -954 1039 -980 1011 -958 1999 -974 1031 -990 993 -972 1011 -1980 1011 -978 2009 -1968 1993 -982 1003 -1974 1007 -1006 1001 -986 985 -986 1001 -982 1033 -8924 8013 -3924 1851 -170400 493 -132 131 -164 65 -724 461 -164 693 -100 265 -100 229 -100 199 -98 363 -232 363 -100 131 -68 531 -166 165 -134 265 -66 727 -300 131 -198 99 -132 99 -134 597 -102 295 -130 131 -230 129 -98 689 -264 263 -134 231 -134 199 -132 131 -68 295 -100 163 -130 591 -98 327 -164 231 -66 589 -98 2077 -134 1389 -66 2913 -66 3045 -66 4463 -98 391 -132 489 -164 295 -66 523 -166 329 -98 195 -98 165 -100 165 -66 231 -100 895 -98 265 -132 595 -132 297 -494 261 -198 331 -100 131 -134 429 -68 231 -298 1193 -66 99 -100 627 -132 331 -98 393 -98 657 -132 163 -392 359 -132 163 -64 655 -164 263 -64 531 -98 265 -164 499 -100 229 -64 459 -100 199 -66 165 -130 197 -132 393 -66 163 -228 229 -132 65 -132 263 -100 357 -98 991 -328 595 -132 197 -130 759 -66 131 -166 267 -100 1251 -64 361 -66 395 -954 559 -132 295 -98 327 -100 295 -100 367 -198 391 -298 363 -132 495 -132 523 -66 97 -132 763 -198 131 -66 197 -100 525 -130 1059 -64 461 -166 563 -134 331 -66 463 -134 329 -198 199 -166 265 -266 1061 -66 331 -366 65 -66 1225 -100 299 -66 299 -132 133 -298 265 -100 1129 -364 163 -460 297 -132 297 -100 299 -100 -RAW_Data: -465 9659 -100 885 -162 2507 -98 1805 -64 427 -98 1879 -198 489 -66 525 -98 851 -100 525 -66 819 -66 395 -98 459 -66 1155 -66 863 -68 265 -98 2359 -68 435 -166 2031 -66 829 -100 561 -66 701 -66 1365 -134 731 -132 655 -68 1623 -66 263 -98 1087 -98 591 -64 987 -132 299 -66 885 -66 1087 -66 2605 -100 303 -100 731 -132 1323 -66 267 -100 627 -66 431 -100 397 -100 1027 -66 559 -102 1565 -100 865 -66 629 -100 327 -66 1281 -98 851 -132 593 -132 429 -100 2931 -134 759 -298 2985 -132 1129 -66 1131 -166 235 -100 725 -66 199 -134 365 -64 293 -66 489 -130 15135 -132 899 -100 691 -100 1271 -134 1161 -66 1789 -100 293 -64 651 -130 719 -100 1061 -66 529 -66 501 -132 1523 -68 231 -68 895 -100 1557 -134 895 -66 2297 -68 4497 -134 1319 -64 525 -98 949 -66 463 -132 399 -134 1129 -66 263 -170 1027 -100 763 -98 265 -68 603 -66 563 -166 1221 -68 1253 -66 1163 -66 697 -134 699 -98 1929 -68 931 -100 399 -100 1589 -98 663 -98 261 -66 337 -100 497 -98 767 -66 471 -66 365 -66 1363 -68 967 -300 461 -66 879 -98 623 -98 623 -100 725 -98 1685 -66 1061 -264 1523 -66 665 -130 2239 -66 229 -130 1509 -66 763 -100 1301 -132 12207 -98 261 -98 3707 -66 323 -298 821 -66 623 -98 2467 -66 2177 -164 625 -100 629 -132 1263 -132 1027 -100 2093 -132 397 -98 1855 -66 2515 -64 391 -132 1163 -66 503 -68 499 -98 2383 -98 1143 -66 523 -66 821 -98 525 -98 163 -164 1775 -68 231 -134 2159 -100 3065 -66 401 -134 925 -200 165 -18904 99 -296 261 -1018 99 -198 235 -166 329 -298 133 -266 1877 -132 227 -198 797 -66 933 -100 1093 -98 197 -66 723 -98 293 -132 953 -66 1507 -132 261 -64 1183 -98 393 -66 461 -100 863 -66 463 -100 865 -66 2721 -200 11457 -66 4203 -100 969 -66 765 -234 229 -100 491 -66 433 -66 231 -134 1189 -100 1015 -96 1349 -66 687 -132 655 -100 365 -132 397 -132 761 -100 1393 -66 525 -100 397 -132 327 -68 495 -66 429 -68 533 -134 529 -66 565 -102 695 -102 1353 -66 793 -228 197 -100 801 -64 657 -98 1439 -98 393 -98 359 -130 2707 -100 1101 -166 1523 -100 961 -100 397 -66 1217 -66 531 -166 199 -102 365 -166 1863 -66 367 -66 361 -68 703 -100 763 -100 299 -66 501 -200 231 -100 2879 -98 2005 -66 825 -134 263 -66 1155 -132 929 -66 335 -100 13393 -100 4113 -266 99 -198 1357 -66 1015 -100 1627 -66 999 -132 1329 -132 529 -66 2591 -100 2545 -68 429 -100 499 -166 961 -66 467 -66 699 -RAW_Data: -100 44175 -12032 99 -400 99 -334 65 -490 165 -328 65 -298 367 -1488 131 -166 131 -264 65 -66 99 -558 197 -198 129 -164 263 -100 2897 -66 33999 -98 1313 -166 657 -132 261 -66 197 -98 1477 -98 229 -66 1319 -66 231 -68 1199 -66 763 -100 857 -134 1963 -18216 99 -500 165 -330 65 -360 163 -202 65 -100 99 -366 267 -402 163 -68 233 -164 65 -66 167 -396 365 -100 1527 -66 393 -132 757 -66 1759 -100 821 -98 233 -66 1197 -66 399 -130 495 -100 435 -100 365 -66 199 -66 6399 -66 3541 -132 369 -132 1361 -98 597 -132 729 -68 733 -66 693 -100 199 -68 565 -134 363 -66 917 -68 267 -134 757 -132 1415 -132 327 -98 1053 -68 897 -166 263 -296 793 -102 297 -98 391 -100 893 -100 233 -68 563 -100 729 -66 431 -66 403 -68 397 -66 601 -164 563 -66 861 -132 1793 -66 637 -134 297 -66 525 -66 1421 -66 265 -100 463 -66 835 -100 699 -100 1029 -132 265 -166 567 -164 2155 -66 263 -66 1945 -100 10073 -132 463 -100 799 -66 729 -100 1655 -66 233 -66 265 -134 1961 -132 733 -98 301 -134 367 -134 1033 -64 889 -166 1463 -100 1327 -100 573 -132 497 -100 903 -100 1951 -66 427 -132 895 -198 1387 -98 723 -66 363 -100 263 -134 263 -166 561 -300 363 -198 929 -132 999 -168 65 -134 867 -132 719 -64 6625 -100 3415 -66 1553 -132 757 -130 329 -66 1625 -66 199 -66 265 -100 365 -132 267 -66 235 -100 703 -100 431 -100 601 -68 565 -100 399 -100 365 -100 365 -98 633 -100 2067 -100 495 -98 1121 -66 1253 -100 433 -132 199 -132 567 -100 99 -98 829 -198 363 -98 1717 -66 301 -132 403 -66 1791 -66 401 -100 633 -98 697 -66 1127 -66 433 -66 965 -100 2485 -66 461 -66 899 -98 231 -100 763 -100 523 -198 361 -132 1911 -66 2307 -168 265 -100 659 -134 461 -66 1165 -68 1423 -66 2693 -66 499 -66 733 -134 893 -98 435 -134 2359 -132 197 -68 661 -100 231 -68 369 -100 231 -66 495 -132 1751 -68 233 -68 1765 -132 233 -130 491 -162 855 -98 2113 -100 333 -134 765 -132 529 -66 293 -66 987 -132 329 -64 293 -98 99 -98 361 -100 367 -166 267 -68 261 -100 997 -408 855 -456 845 -868 417 -882 397 -900 411 -416 869 -884 417 -844 441 -846 427 -456 843 -452 831 -876 409 -898 409 -430 837 -462 839 -462 847 -854 417 -876 437 -880 397 -886 385 -486 841 -874 411 -868 415 -892 383 -878 439 -418 847 -466 835 -882 427 -440 843 -870 417 -882 411 -878 435 -846 427 -886 385 -886 415 -880 427 -870 409 -454 837 -888 419 -RAW_Data: -448 837 -448 835 -880 413 -448 869 -414 885 -420 849 -450 843 -876 443 -440 841 -438 873 -862 413 -448 855 -876 413 -25804 2597 -422 879 -418 867 -884 419 -844 445 -876 399 -454 849 -886 417 -878 415 -872 419 -418 893 -410 865 -876 415 -876 437 -418 851 -450 875 -418 877 -874 405 -866 419 -884 413 -878 437 -418 883 -850 415 -882 425 -874 409 -884 419 -452 851 -418 885 -850 415 -452 859 -876 409 -898 407 -862 449 -844 449 -874 409 -872 435 -870 397 -882 425 -426 875 -868 413 -452 871 -418 881 -874 413 -448 839 -460 847 -424 879 -420 867 -890 417 -416 887 -418 885 -860 413 -450 869 -848 437 -25854 475 -3978 135 -398 65 -1128 225 -1058 225 -1032 299 -562 749 -994 307 -956 341 -946 369 -488 809 -494 803 -930 353 -932 383 -474 815 -510 815 -468 811 -928 377 -914 381 -914 407 -882 399 -454 839 -890 413 -900 409 -870 417 -880 425 -442 843 -476 841 -864 417 -454 867 -882 417 -876 411 -876 435 -846 429 -882 421 -886 419 -850 445 -872 411 -450 853 -878 419 -430 881 -418 867 -890 417 -416 889 -418 885 -432 871 -416 855 -878 451 -438 845 -458 843 -884 419 -446 837 -880 449 -25864 497 -9138 659 -1080 233 -1046 297 -976 295 -558 737 -578 763 -942 339 -972 341 -530 785 -492 807 -486 807 -916 397 -918 357 -922 387 -928 383 -482 801 -906 395 -898 407 -888 415 -882 411 -480 841 -448 839 -878 415 -448 871 -876 411 -880 437 -880 397 -884 423 -884 419 -882 413 -886 387 -912 397 -466 843 -866 413 -448 869 -456 837 -888 419 -450 837 -450 851 -450 871 -454 837 -888 419 -450 835 -450 871 -884 417 -448 837 -880 415 -172818 65 -1022 197 -1852 10287 -134 695 -132 263 -130 1717 -66 625 -100 1031 -68 301 -68 265 -100 665 -98 1059 -100 931 -66 1093 -132 333 -68 3011 -132 459 -66 659 -98 521 -98 1511 -98 163 -98 857 -132 231 -98 491 -66 587 -66 393 -98 1513 -98 263 -130 529 -100 299 -100 1545 -132 1313 -66 399 -68 299 -134 201 -68 265 -98 691 -132 1099 -66 427 -100 461 -264 427 -134 327 -100 227 -64 493 -66 633 -66 501 -66 2067 -228 595 -132 97 -66 231 -66 299 -66 925 -98 661 -100 433 -134 231 -166 999 -98 691 -66 197 -66 293 -66 265 -362 1557 -100 231 -134 265 -68 433 -66 2483 -66 333 -100 233 -166 917 -132 295 -132 949 -164 1775 -100 7629 -66 7259 -66 263 -202 1263 -100 265 -68 861 -166 365 -98 233 -164 569 -66 199 -100 399 -98 1189 -130 261 -100 655 -164 723 -264 231 -100 327 -130 395 -RAW_Data: -130 84629 -16530 199 -594 163 -562 65 -164 65 -230 1383 -100 1031 -66 427 -66 401 -68 265 -102 233 -134 923 -100 493 -66 555 -132 619 -66 3165 -66 463 -198 1025 -68 233 -100 957 -132 793 -134 1233 -100 1553 -98 431 -100 1429 -100 393 -164 259 -166 65 -100 297 -134 263 -68 1797 -66 887 -100 497 -100 565 -134 363 -96 1649 -130 393 -98 327 -100 563 -68 891 -68 24545 -68 231 -134 167 -100 131 -68 863 -102 627 -164 267 -166 631 -66 569 -100 797 -66 231 -166 301 -102 569 -134 1423 -66 1115 -66 759 -66 599 -68 1265 -268 527 -134 531 -98 269 -68 231 -66 325 -98 329 -66 399 -64 393 -98 459 -98 921 -66 457 -100 2541 -266 927 -68 231 -64 357 -98 495 -166 133 -100 529 -98 1091 -100 199 -66 597 -100 931 -66 663 -134 333 -166 763 -68 599 -100 333 -102 333 -232 363 -66 1491 -132 697 -134 629 -66 369 -66 561 -134 863 -98 895 -100 199 -98 763 -100 299 -168 561 -100 331 -100 467 -100 1231 -100 1821 -66 229 -66 325 -66 1419 -66 195 -66 553 -132 363 -164 857 -66 491 -66 227 -98 227 -98 529 -100 2093 -164 501 -132 1441 -98 1095 -100 263 -66 931 -66 1397 -66 1627 -98 433 -66 697 -200 363 -266 297 -100 301 -200 263 -98 565 -134 65 -102 297 -100 165 -100 929 -266 1325 -66 3213 -100 4403 -100 665 -134 1859 -66 631 -66 967 -200 165 -134 1551 -66 791 -100 331 -132 163 -64 163 -98 361 -132 557 -98 629 -66 885 -100 1087 -100 959 -100 535 -66 265 -66 1931 -66 465 -66 1165 -100 565 -100 865 -100 493 -66 597 -166 1293 -100 463 -68 563 -66 565 -66 595 -100 197 -68 929 -66 665 -134 399 -66 665 -100 1255 -166 935 -98 1791 -98 265 -136 501 -100 1581 -66 265 -66 567 -98 699 -134 -RAW_Data: 12477 16271 -64 12623 -102 40819 -98 3729 -66 14371 -66 14943 -64 5931 -66 11147 -68 74641 -102 54299 -70 18441 -66 82993 -100 66161 -68 61869 -66 6627 -66 12987 -68 30427 -68 25761 -98 6305 -66 5019 -64 30857 -132 23929 -68 25129 -39378 317 -2020 311 -2010 2033 -312 2017 -282 325 -2018 291 -2024 2035 -316 2023 -276 319 -2028 297 -2032 2029 -284 2023 -314 321 -2016 319 -2012 2003 -310 2015 -318 321 -1984 327 -16000 351 -1974 321 -2020 2041 -276 2047 -288 327 -1992 327 -2000 2055 -282 2053 -274 305 -2022 301 -2014 2049 -286 2055 -274 319 -1992 329 -2006 2065 -282 2023 -316 323 -1988 303 -16008 323 -2014 315 -1990 2053 -284 2061 -274 319 -1988 319 -2038 2033 -286 2055 -264 339 -2008 289 -2034 2035 -284 2061 -262 339 -2008 287 -2040 2037 -286 2035 -268 347 -2006 317 -15988 311 -2014 343 -2014 2027 -300 2031 -258 311 -2024 323 -2028 2023 -280 2051 -318 285 -2024 309 -2002 2039 -312 2017 -318 289 -2020 293 -2028 2035 -316 2023 -276 321 -2030 299 -15982 345 -1988 345 -2014 2029 -268 2069 -258 337 -1998 321 -2024 2025 -320 2025 -276 321 -1994 331 -2000 2057 -282 2043 -282 305 -2034 291 -2024 2039 -320 2019 -278 323 -1992 333 -15976 389 -1978 321 -1986 2069 -276 2053 -288 303 -2020 315 -1980 2067 -258 2075 -258 337 -1996 321 -2030 2027 -284 2059 -274 319 -2004 319 -2016 2033 -282 2059 -264 345 -2006 289 -16006 355 -1984 315 -2018 2033 -312 2015 -284 327 -2000 329 -2000 2063 -282 2049 -284 327 -1984 319 -2018 2035 -284 2057 -264 343 -2004 289 -2032 2059 -280 2019 -316 323 -1984 321 -15968 387 -1978 321 -1984 2069 -276 2049 -288 325 -1996 303 -2004 2055 -284 2049 -278 325 -2018 321 -1984 2075 -276 2049 -288 327 -1990 325 -1996 2031 -318 2025 -278 323 -2018 289 -16010 357 -1982 315 -2018 2035 -278 2053 -288 307 -2022 321 -2000 2049 -274 2045 -284 327 -2022 311 -2014 2033 -276 2053 -286 327 -2000 323 -2000 2061 -282 2015 -316 325 -2002 289 -15994 353 -1980 357 -1990 2035 -312 2015 -286 327 -2020 289 -2022 2033 -318 2019 -278 323 -2004 319 -2016 2059 -282 2023 -316 327 -1970 321 -2018 2059 -280 2019 -318 325 -1972 321 -15988 353 -1982 357 -1972 2071 -276 2051 -288 327 -1988 323 -1996 2061 -280 2053 -284 325 -1984 321 -2014 2031 -282 2059 -276 321 -2018 289 -2036 2031 -282 2059 -276 321 -2018 319 -15984 341 -1984 343 -2016 2029 -298 2033 -258 309 -2028 321 -2032 2035 -284 2017 -298 337 -2008 289 -2034 2027 -314 2023 -310 285 -2016 321 -2014 2035 -282 2027 -308 317 -2026 297 -15982 357 -1984 345 -1982 2067 -264 2057 -258 341 -1996 317 -2026 2057 -282 2017 -318 325 -1972 319 -2018 2059 -280 2029 -302 315 -2006 319 -2006 2059 -274 2047 -276 307 -RAW_Data: -2024 311 -15984 349 -2004 317 -2012 2029 -276 2047 -312 305 -2018 311 -2018 2037 -258 2077 -258 337 -1998 321 -2026 2031 -278 2043 -284 327 -2020 311 -2018 2031 -278 2051 -288 325 -2004 323 -15962 353 -2018 317 -2014 2007 -308 2019 -320 287 -2024 325 -1998 2035 -314 2025 -302 311 -2004 319 -2010 2027 -312 2011 -310 309 -2028 303 -2018 2027 -304 2007 -294 309 -2024 323 -16002 361 -1946 347 -2008 2027 -286 2045 -312 325 -1978 319 -2024 2043 -276 2049 -286 325 -1988 341 -1978 2061 -278 2049 -284 327 -1990 321 -2020 2029 -318 2021 -278 323 -2008 321 -15990 355 -1982 355 -1984 2031 -284 2049 -278 325 -2018 321 -2000 2047 -276 2049 -286 327 -1994 341 -1976 2067 -274 2043 -284 303 -2018 341 -1982 2061 -266 2069 -258 339 -1998 321 -16002 311 -2014 329 -2012 2027 -318 1999 -298 339 -2008 287 -2042 2031 -282 2027 -306 317 -2012 291 -2044 2037 -284 2033 -302 315 -2004 321 -2012 2037 -282 2027 -306 317 -2028 297 -15980 345 -2018 285 -2026 2037 -320 2017 -278 323 -2024 299 -2002 2069 -278 2055 -286 325 -2002 289 -2024 2037 -282 2063 -276 321 -2016 287 -2036 2025 -320 2023 -278 323 -2018 287 -16006 355 -1982 355 -1986 2033 -302 2039 -282 307 -2032 309 -2014 2043 -264 2057 -258 341 -1992 319 -2036 2033 -284 2029 -306 319 -2000 319 -2014 2035 -282 2061 -266 343 -2006 289 -16018 331 -2010 313 -2014 2021 -320 2017 -278 323 -2010 321 -2016 2037 -284 2033 -304 317 -2018 287 -2036 2037 -284 2025 -296 339 -1978 321 -2028 2057 -282 2021 -278 359 -1972 321 -15990 355 -1978 355 -1990 2035 -300 2015 -312 305 -2016 315 -2016 2053 -290 2013 -316 277 -2020 353 -1980 2059 -256 2077 -276 307 -2020 311 -2012 2047 -258 2073 -256 341 -1998 321 -15976 369 -1976 313 -2018 2051 -280 2043 -290 317 -2004 321 -2024 2023 -282 2053 -282 327 -2020 313 -2020 2035 -278 2055 -284 325 -1986 321 -2016 2033 -280 2049 -282 325 -2020 313 -15986 315 -2026 319 -2028 2003 -318 2013 -294 339 -2008 289 -2032 2027 -316 2019 -302 311 -2004 321 -2014 2033 -314 1995 -308 317 -2028 297 -2032 2035 -276 2023 -316 321 -2014 289 -15998 347 -2014 283 -2020 2035 -292 2031 -288 311 -2024 321 -2036 2001 -310 2027 -318 275 -2054 277 -2026 2029 -296 2027 -292 311 -2024 321 -2030 2031 -282 2033 -304 313 -2004 321 -15992 329 -2010 349 -1980 2037 -292 2051 -256 339 -1998 319 -2034 2029 -286 2055 -264 339 -1978 321 -2030 2027 -280 2043 -316 327 -1984 315 -2016 2063 -278 2041 -288 327 -1990 325 -15964 389 -1978 319 -1984 2063 -256 2055 -292 307 -2016 323 -2028 2027 -286 2057 -274 319 -2010 319 -2010 2033 -282 2061 -276 321 -2018 289 -2032 2027 -318 2021 -278 321 -2020 319 -15974 355 -1980 357 -2000 2035 -RAW_Data: -254 2079 -276 309 -2016 309 -2012 2035 -296 2063 -258 337 -1992 321 -2030 2037 -286 2027 -294 333 -1978 321 -2034 2029 -286 2031 -306 315 -2008 321 -15972 355 -1984 315 -2014 2037 -310 2011 -318 323 -1984 329 -2002 2033 -316 2029 -278 323 -2026 299 -2030 2035 -278 2019 -316 325 -1980 323 -2018 2053 -252 2075 -280 327 -1984 355 -15954 345 -2008 309 -2016 2045 -266 2065 -258 341 -235740 101 -202 65 -734 133 -372 401 -68 269 -236 505 -68 235 -234 875 -68 13969 -100 14297 -70 3863 -96 59337 -104 11859 -68 17409 -68 7317 -66 11443 -64 15589 -66 4381 -98 32297 -168 45445 -100 59295 -100 41417 -66 1539 -66 23001 -RAW_Data: 171 -316 311 -154 187 -292 343 -134 337 -136 199 -286 311 -182 157 -294 327 -160 171 -316 161 -312 159 -314 333 -170 311 -164 141 -324 305 -164 299 -176 301 -170 167 -340 133 -338 163 -312 159 -294 189 -298 165 -314 311 -180 155 -294 189 -300 165 -312 187 -284 189 -312 159 -294 189 -296 167 -314 161 -310 189 -312 159 -292 189 -296 167 -312 161 -312 159 -314 193 -268 175 -304 193 -314 163 -312 159 -312 193 -294 495 -458 161 -338 301 -168 165 -304 331 -134 199 -286 309 -184 309 -170 141 -318 331 -162 143 -316 309 -182 157 -294 189 -302 163 -312 309 -184 311 -170 141 -318 331 -162 299 -150 319 -196 139 -314 165 -334 137 -340 159 -312 161 -302 149 -318 331 -162 173 -312 161 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -312 159 -296 189 -300 193 -284 187 -284 189 -312 159 -292 189 -298 167 -312 161 -312 189 -300 173 -294 195 -282 191 -310 485 -472 167 -300 331 -134 171 -312 333 -170 169 -290 331 -162 299 -176 155 -300 333 -162 171 -300 323 -150 145 -340 159 -320 167 -302 319 -150 319 -166 169 -312 331 -162 299 -150 319 -194 139 -314 165 -308 163 -340 133 -338 163 -312 157 -296 327 -160 171 -316 163 -310 161 -312 163 -298 177 -302 193 -314 163 -310 161 -312 163 -300 173 -300 169 -318 177 -290 189 -300 165 -312 161 -312 189 -284 185 -292 189 -300 191 -286 187 -284 191 -312 471 -486 167 -314 313 -152 159 -300 335 -162 169 -308 319 -150 319 -166 169 -312 331 -162 143 -314 311 -180 157 -294 189 -300 165 -314 309 -182 311 -168 141 -318 333 -162 299 -174 295 -194 141 -314 165 -336 137 -338 135 -338 161 -312 159 -294 327 -160 171 -316 161 -312 159 -314 191 -270 201 -278 193 -314 163 -310 161 -312 163 -300 175 -300 169 -316 177 -292 189 -300 165 -312 161 -312 189 -284 183 -294 189 -298 193 -284 187 -312 163 -312 497 -460 145 -320 325 -168 171 -290 333 -162 171 -314 305 -160 317 -168 169 -290 333 -162 173 -312 283 -182 159 -294 189 -302 165 -312 309 -182 313 -168 143 -318 331 -162 301 -148 321 -194 141 -312 167 -334 135 -340 161 -310 163 -312 159 -294 327 -160 171 -316 163 -310 161 -312 163 -300 175 -302 195 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -300 165 -312 161 -310 189 -284 185 -292 189 -300 191 -286 187 -312 161 -300 487 -484 151 -318 335 -138 169 -322 305 -164 171 -314 305 -190 295 -166 171 -290 333 -162 173 -312 283 -182 159 -296 191 -300 -RAW_Data: 165 -312 333 -160 311 -170 141 -318 331 -164 299 -148 321 -194 139 -316 165 -336 135 -340 133 -338 163 -300 149 -320 329 -164 171 -314 159 -312 163 -300 175 -298 193 -310 163 -312 159 -312 163 -300 175 -296 193 -312 163 -312 159 -312 163 -300 175 -298 167 -320 175 -290 191 -296 193 -286 187 -310 163 -300 175 -296 501 -458 187 -288 347 -154 151 -316 325 -162 171 -316 313 -152 301 -194 141 -316 331 -162 143 -314 311 -182 155 -294 189 -302 163 -314 309 -182 311 -168 143 -318 331 -162 301 -174 295 -194 141 -314 165 -336 135 -340 133 -338 163 -312 159 -294 327 -158 173 -314 163 -312 159 -306 173 -314 165 -306 191 -284 189 -284 189 -312 157 -294 191 -300 163 -314 187 -284 189 -284 187 -292 189 -296 165 -314 161 -312 189 -284 185 -294 189 -298 193 -286 485 -492 167 -298 323 -150 175 -302 331 -164 171 -300 321 -150 317 -166 167 -308 331 -134 171 -316 309 -182 129 -318 189 -302 163 -314 309 -182 311 -170 141 -318 331 -162 301 -148 321 -194 139 -314 167 -334 137 -338 161 -310 163 -312 159 -294 327 -160 171 -316 161 -312 159 -312 193 -270 175 -304 193 -314 163 -312 159 -312 161 -314 161 -296 189 -304 165 -312 187 -284 189 -276 173 -318 195 -278 193 -284 187 -312 163 -300 175 -298 169 -318 511 -460 147 -318 327 -168 169 -292 331 -164 171 -314 305 -160 315 -168 171 -290 333 -162 171 -314 283 -182 159 -294 189 -300 165 -314 307 -184 311 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -336 135 -340 133 -338 163 -312 157 -294 327 -160 171 -316 163 -310 161 -304 175 -314 167 -304 191 -286 187 -284 189 -284 187 -292 189 -300 165 -312 189 -284 187 -286 185 -294 189 -296 165 -314 161 -312 187 -286 185 -294 189 -298 193 -284 485 -494 167 -298 321 -150 177 -312 313 -168 171 -292 331 -164 299 -174 151 -316 325 -162 171 -316 289 -176 151 -316 187 -296 167 -312 311 -180 307 -170 141 -318 331 -162 299 -150 319 -196 139 -314 165 -334 137 -340 159 -312 161 -302 149 -320 329 -164 171 -314 159 -312 163 -300 175 -298 193 -310 163 -312 161 -310 163 -312 161 -296 189 -302 191 -286 187 -284 189 -312 159 -292 189 -296 167 -314 161 -310 189 -284 187 -292 189 -300 193 -284 485 -494 167 -296 323 -150 175 -312 313 -170 169 -292 333 -162 299 -176 151 -314 327 -160 171 -316 291 -176 149 -316 189 -296 165 -314 309 -182 307 -168 141 -318 331 -164 299 -148 321 -194 141 -314 165 -334 137 -338 161 -312 -RAW_Data: 161 -302 147 -320 331 -162 171 -314 161 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 173 -296 193 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -296 195 -284 189 -284 189 -312 159 -296 513 -468 159 -300 333 -162 171 -300 323 -150 171 -314 325 -162 311 -164 171 -314 305 -160 159 -296 327 -160 171 -316 161 -312 161 -312 335 -170 311 -164 141 -324 321 -154 299 -194 309 -164 143 -326 167 -314 161 -312 159 -312 163 -302 173 -300 331 -162 173 -312 161 -312 163 -300 175 -300 167 -320 153 -312 191 -296 167 -312 161 -312 189 -312 159 -292 189 -298 167 -312 161 -312 159 -314 193 -268 177 -302 193 -316 161 -312 159 -312 193 -294 497 -456 187 -312 303 -166 165 -306 331 -132 201 -286 309 -182 313 -168 143 -318 331 -162 143 -314 311 -182 157 -294 189 -302 165 -312 309 -182 311 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -338 161 -312 161 -300 149 -320 331 -162 171 -314 161 -310 163 -302 173 -298 193 -310 165 -310 161 -310 163 -312 161 -296 189 -300 193 -286 187 -284 189 -312 157 -294 189 -298 165 -314 161 -310 189 -284 187 -292 189 -300 193 -284 485 -494 167 -298 321 -150 171 -318 323 -162 171 -314 289 -176 297 -194 141 -314 331 -162 143 -316 309 -182 155 -294 189 -302 163 -314 309 -182 311 -168 143 -318 331 -162 301 -148 321 -194 139 -314 167 -334 137 -338 161 -310 163 -300 149 -320 331 -162 171 -314 161 -312 163 -300 173 -298 193 -310 163 -312 159 -312 163 -300 175 -296 193 -312 163 -312 159 -312 163 -300 175 -298 167 -320 175 -290 191 -296 193 -286 187 -310 163 -312 161 -296 513 -468 159 -300 333 -162 171 -302 321 -150 175 -300 333 -162 311 -162 171 -316 289 -176 153 -314 327 -160 171 -316 163 -312 159 -312 307 -168 335 -138 169 -320 307 -164 297 -176 301 -194 141 -318 165 -336 137 -338 133 -338 163 -312 159 -294 327 -160 171 -316 161 -312 161 -312 163 -298 177 -302 169 -318 177 -292 189 -298 167 -312 189 -284 189 -312 157 -294 189 -298 167 -312 161 -312 159 -312 193 -270 203 -302 169 -314 161 -312 189 -310 467 -484 145 -318 327 -168 143 -316 331 -162 173 -312 307 -160 313 -170 141 -318 331 -164 143 -342 281 -184 157 -294 189 -302 165 -312 307 -184 313 -168 141 -320 331 -162 299 -150 319 -194 141 -314 165 -336 137 -338 135 -338 161 -312 159 -294 327 -158 173 -314 163 -310 161 -312 165 -298 175 -304 193 -312 163 -312 159 -312 -RAW_Data: 163 -300 175 -298 169 -318 177 -290 191 -298 165 -314 161 -310 189 -284 185 -292 189 -298 193 -284 189 -310 163 -312 497 -460 145 -320 325 -170 169 -290 333 -162 171 -314 307 -158 317 -168 169 -290 333 -162 171 -314 281 -182 159 -296 189 -300 165 -312 309 -182 313 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 137 -338 135 -338 161 -312 159 -294 327 -160 173 -314 163 -312 159 -312 163 -300 175 -302 195 -312 163 -312 159 -312 163 -300 175 -298 169 -318 175 -292 189 -300 165 -312 161 -312 187 -284 185 -292 189 -300 193 -284 187 -312 161 -314 471 -486 167 -314 313 -152 159 -300 335 -162 169 -308 321 -156 329 -170 127 -318 321 -176 155 -300 333 -164 169 -302 165 -314 161 -310 329 -168 299 -166 151 -328 311 -154 331 -168 299 -178 143 -318 153 -322 167 -332 149 -320 151 -318 165 -304 321 -156 181 -290 189 -300 173 -294 177 -294 189 -302 173 -294 177 -294 189 -302 173 -294 177 -318 193 -278 173 -294 179 -292 191 -302 173 -294 177 -294 189 -302 173 -320 177 -294 195 -278 175 -318 491 -460 163 -314 333 -158 159 -294 327 -160 171 -316 311 -180 307 -170 141 -318 331 -162 143 -316 309 -182 157 -294 189 -302 165 -312 309 -182 313 -168 141 -318 331 -164 299 -148 321 -194 141 -314 165 -334 137 -338 135 -338 161 -312 159 -294 329 -158 173 -314 163 -310 161 -306 173 -314 165 -304 193 -284 187 -286 189 -276 197 -294 193 -310 163 -312 161 -310 163 -300 175 -296 193 -310 165 -284 187 -310 163 -300 175 -300 169 -318 177 -316 483 -466 187 -292 341 -134 171 -308 319 -150 179 -300 333 -164 309 -162 171 -316 313 -152 153 -316 327 -160 171 -316 163 -310 161 -312 307 -166 337 -136 171 -322 305 -164 299 -174 301 -194 141 -318 165 -336 137 -338 161 -310 163 -300 149 -320 331 -162 171 -314 161 -312 163 -300 173 -296 195 -310 163 -312 159 -312 163 -312 159 -296 189 -302 165 -312 189 -284 189 -284 185 -294 189 -296 165 -314 161 -310 189 -284 187 -294 189 -298 193 -284 485 -494 167 -298 321 -150 177 -312 313 -168 171 -290 333 -162 299 -176 151 -316 325 -162 171 -316 289 -176 151 -314 189 -296 165 -314 309 -182 305 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -340 133 -338 161 -302 149 -320 329 -164 171 -286 187 -312 163 -300 173 -298 193 -310 163 -312 159 -312 163 -312 159 -296 189 -302 191 -286 187 -284 189 -312 159 -292 191 -296 165 -314 161 -310 189 -284 187 -292 -RAW_Data: 189 -300 193 -284 485 -494 167 -298 321 -150 177 -300 327 -150 167 -328 305 -160 319 -168 171 -290 333 -162 171 -314 281 -184 159 -296 189 -300 165 -314 307 -184 311 -170 141 -318 333 -162 299 -150 319 -194 139 -314 167 -334 135 -340 133 -338 163 -312 159 -294 327 -158 173 -314 163 -312 159 -312 165 -298 177 -302 193 -314 163 -310 161 -312 163 -300 173 -300 167 -318 177 -292 189 -300 165 -312 161 -312 187 -284 185 -292 189 -300 191 -286 187 -284 189 -300 487 -484 149 -320 333 -138 171 -322 321 -154 151 -316 327 -160 311 -164 173 -312 307 -160 161 -298 355 -132 173 -314 163 -310 161 -312 335 -170 311 -164 143 -322 307 -164 323 -150 301 -196 141 -318 165 -334 137 -338 161 -312 161 -302 147 -320 331 -162 173 -312 161 -312 163 -300 173 -298 195 -310 163 -312 159 -312 163 -312 159 -296 189 -300 193 -284 189 -284 189 -284 185 -294 189 -296 165 -314 161 -312 189 -284 185 -294 189 -298 193 -286 485 -468 193 -296 323 -150 147 -340 313 -168 171 -292 331 -164 299 -174 151 -316 325 -160 173 -316 289 -176 149 -316 189 -294 165 -314 309 -182 307 -170 141 -318 331 -164 299 -148 321 -194 139 -314 167 -306 165 -338 133 -338 163 -312 159 -294 327 -160 173 -314 163 -312 161 -310 165 -300 175 -300 169 -318 177 -290 189 -298 165 -314 187 -284 189 -312 159 -294 189 -298 165 -314 161 -310 161 -306 173 -316 193 -280 191 -312 159 -312 163 -300 515 -460 161 -312 305 -190 135 -328 329 -134 201 -286 309 -182 311 -168 143 -318 331 -162 143 -316 309 -182 157 -294 189 -300 165 -312 309 -184 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -334 137 -340 159 -312 163 -300 149 -318 331 -162 173 -312 161 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 173 -296 193 -312 163 -312 159 -312 163 -312 161 -298 189 -300 165 -312 161 -312 189 -312 159 -292 191 -300 191 -286 485 -468 193 -298 323 -148 147 -342 323 -160 173 -314 289 -176 297 -196 139 -314 331 -162 143 -314 311 -180 157 -292 191 -300 165 -312 311 -182 309 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -338 161 -312 161 -312 159 -294 329 -158 173 -314 163 -310 161 -312 165 -298 175 -304 193 -312 163 -310 161 -310 163 -302 173 -300 169 -318 175 -292 189 -300 165 -312 161 -310 189 -278 173 -316 195 -280 191 -312 159 -312 163 -300 515 -460 163 -310 305 -190 137 -328 329 -134 199 -288 309 -182 311 -168 143 -318 -RAW_Data: 331 -162 143 -316 309 -182 157 -292 191 -300 165 -312 309 -182 313 -168 141 -318 331 -164 299 -174 295 -194 141 -314 165 -334 137 -340 133 -338 163 -312 159 -292 327 -160 171 -316 163 -310 161 -312 165 -298 175 -304 193 -312 163 -312 159 -312 163 -300 175 -300 169 -316 177 -292 189 -298 167 -312 161 -310 189 -278 173 -316 193 -282 191 -312 159 -312 163 -300 515 -460 163 -310 305 -190 137 -326 331 -132 201 -286 309 -184 309 -170 141 -318 333 -162 143 -314 311 -180 159 -294 189 -300 165 -312 309 -184 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 135 -340 159 -312 163 -300 149 -320 329 -164 171 -314 159 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 175 -294 195 -310 163 -312 161 -310 163 -300 175 -298 169 -318 153 -314 191 -296 193 -286 187 -284 189 -312 161 -294 509 -468 159 -298 335 -162 169 -304 323 -150 175 -300 335 -162 311 -160 173 -316 313 -152 151 -316 327 -160 171 -316 163 -312 159 -312 307 -168 335 -138 169 -322 305 -164 299 -176 299 -194 141 -316 167 -334 137 -340 159 -312 161 -302 147 -320 331 -162 173 -312 161 -312 163 -300 175 -296 193 -310 165 -310 161 -312 161 -302 173 -296 195 -310 163 -312 161 -310 163 -300 175 -298 169 -318 177 -290 189 -296 193 -286 187 -310 163 -300 175 -296 501 -484 161 -288 347 -154 151 -316 325 -160 173 -314 313 -152 301 -196 141 -314 331 -162 143 -316 309 -182 157 -294 189 -300 165 -314 309 -182 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 137 -338 133 -338 163 -312 159 -294 327 -158 173 -316 161 -312 159 -312 163 -298 177 -302 193 -314 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -298 165 -314 161 -310 189 -284 193 -292 179 -308 167 -314 161 -312 189 -302 481 -460 173 -320 303 -168 169 -294 331 -164 171 -314 307 -160 319 -168 169 -292 331 -162 143 -342 283 -182 159 -294 189 -302 165 -312 309 -184 311 -168 143 -318 331 -162 299 -150 319 -194 141 -314 165 -336 135 -340 133 -338 163 -312 159 -294 327 -158 173 -316 161 -312 159 -312 165 -298 177 -302 193 -312 163 -312 161 -310 163 -314 161 -296 189 -302 165 -312 187 -284 189 -284 185 -294 189 -296 165 -314 187 -284 189 -302 173 -294 195 -310 491 -458 163 -318 319 -156 153 -314 327 -160 171 -316 311 -152 327 -170 141 -316 331 -164 143 -314 309 -182 157 -294 189 -300 165 -314 309 -182 311 -168 143 -316 333 -162 299 -150 diff --git a/debug/PyCortexMDebug/README.md b/debug/PyCortexMDebug/README.md deleted file mode 100644 index 32c76e765d2..00000000000 --- a/debug/PyCortexMDebug/README.md +++ /dev/null @@ -1,84 +0,0 @@ -PyCortexMDebug -============== - -*A set of GDB/Python-based utilities to make life debugging ARM Cortex-M processors a bit easier* - -It will consist of several modules which will hopefully become integrated as they evolve. Presently, there is only one: - -## SVD -ARM defines an SVD (System View Description) file format in its CMSIS -standard as a means for Cortex-M-based chip manufacturers to provide a -common description of peripherals, registers, and register fields. You -can download SVD files for different manufacturers -[here](http://www.arm.com/products/processors/cortex-m/cortex-microcontroller-software-interface-standard.php). - -My implementation so far has only tested STM32 chips but should hold for others. If others are like those from ST, -expect plenty of errors in the file. Like GPIOA having a register named GPIOB_OSPEEDR and lots of 16-bit registers -that are listed as 32! - -The implementation consists of two components -- An xml parser module (pysvd) and a GDB file (gdb_svd). -I haven't yet worked out a perfect workflow for this, though it's quite easy to use when -you already tend to have a GDB initialization file for starting up OpenOCD and the like. -However your workflow works, just make sure to, in GDB: - - source gdb_svd.py - svd_load [your_svd_file].svd - -These files can be huge so it might take a second or two. Anyways, after that, you can do - - svd - -to list available peripherals with descriptions. Or you can do - - svd [some_peripheral_name] - -to see all of the registers (with their values) for a given peripheral. For more details, run - - svd [some_peripheral_name] [some_register_name] - -to see all of the field values with descriptions. - -You can add format modifiers like: - -* `svd/x` will display values in hex -* `svd/o` will display values in octal -* `svd/t` or `svd/b` will display values in binary -* `svd/a` will display values in hex and try to resolve symbols from the values - -All field values are displayed at the correct lengths as provided by the SVD files. -Also, tab completion exists for nearly everything! When in doubt, run `svd help`. - -### TODO - -Enable writing to registers and individual fields - -### Bugs - -There are probably a few. All planning, writing, and testing of this was done in an afternoon. There may be -some oddities in working with non-STM32 parts. I'll play with this when I start working with other -controllers again. If something's giving you trouble, describe the problem and it shall be fixed. - -## DWT -The ARM Data Watchpoint and Trace Unit (DWT) offers data watchpoints and a series of gated cycle counters. For now, -I only support the raw cycle counter but facilities are in place to make use of others. As this is independent of the -specific device under test, commands are simple and you can configure a clock speed to get real time values from -counters. - - dwt configclk 48000000 - -will set the current core clock speed. Then - - dwt cyccnt reset - dwt cyccnt enable - -will reset and start the cycle counter. At any point - - dwt cycnt - -will then indicate the number of cycles and amount of time that has passed. - -## ITM/ETM support - -This is not implemented yet. I want to have more complete support for some of the nicer debug and trace features -on Cortex-M processors. Parts of this will probably be dependent on OpenOCD and possibly on specific interfaces. -I'll try to avoid this where possible but can't make any promises. diff --git a/debug/PyCortexMDebug/cmdebug/dwt_gdb.py b/debug/PyCortexMDebug/cmdebug/dwt_gdb.py deleted file mode 100755 index dd7ccd2073b..00000000000 --- a/debug/PyCortexMDebug/cmdebug/dwt_gdb.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env python -""" -This file is part of PyCortexMDebug - -PyCortexMDebug 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. - -PyCortexMDebug 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 PyCortexMDebug. If not, see . -""" - -import gdb -import struct - -DWT_CTRL = 0xE0001000 -DWT_CYCCNT = 0xE0001004 -DWT_CPICNT = 0xE0001008 -DWT_EXTCNT = 0xE000100C -DWT_SLEEPCNT = 0xE0001010 -DWT_LSUCNT = 0xE0001014 -DWT_FOLDCNT = 0xE0001018 -DWT_PCSR = 0xE000101C - -prefix = "dwt : " - - -class DWT(gdb.Command): - clk = None - is_init = False - - def __init__(self): - gdb.Command.__init__(self, "dwt", gdb.COMMAND_DATA) - - @staticmethod - def read(address, bits=32): - """Read from memory (using print) and return an integer""" - value = gdb.selected_inferior().read_memory(address, bits / 8) - return struct.unpack_from(" 1: - if s[1][:2] == "en": - self.cyccnt_en() - elif s[1][0] == "r": - self.cyccnt_reset() - elif s[1][0] == "d": - self.cyccnt_dis() - gdb.write( - prefix - + "CYCCNT ({}): ".format("ON" if (self.read(DWT_CTRL) & 1) else "OFF") - + self.cycles_str(self.read(DWT_CYCCNT)) - ) - elif s[0] == "reset": - if len(s) > 1: - if s[1] == "cyccnt": - self.cyccnt_reset() - gdb.write(prefix + "CYCCNT reset\n") - if s[1] == "counters": - self.cyccnt_reset() - gdb.write(prefix + "CYCCNT reset\n") - else: - self.cyccnt_reset() - gdb.write(prefix + "CYCCNT reset\n") - else: - # Reset everything - self.cyccnt_reset() - gdb.write(prefix + "CYCCNT reset\n") - elif s[0] == "configclk": - if len(s) == 2: - try: - self.clk = float(s[1]) - except: - self.print_help() - else: - self.print_help() - else: - # Try to figure out what stupid went on here - gdb.write(args) - self.print_help() - - @staticmethod - def complete(text, word): - text = str(text).lower() - s = text.split(" ") - - commands = ["configclk", "reset", "cyccnt"] - reset_commands = ["counters", "cyccnt"] - cyccnt_commands = ["enable", "reset", "disable"] - - if len(s) == 1: - return filter(lambda x: x.startswith(s[0]), commands) - - if len(s) == 2: - if s[0] == "reset": - return filter(lambda x: x.startswith(s[1]), reset_commands) - if s[0] == "cyccnt": - return filter(lambda x: x.startswith(s[1]), cyccnt_commands) - - def cycles_str(self, cycles): - if self.clk: - return "%d cycles, %.3es\n" % (cycles, cycles * 1.0 / self.clk) - else: - return "%d cycles" - - def cyccnt_en(self): - self.write(DWT_CTRL, self.read(DWT_CTRL) | 1) - - def cyccnt_dis(self): - self.write(DWT_CTRL, self.read(DWT_CTRL) & 0xFFFFFFFE) - - def cyccnt_reset(self, value=0): - self.write(DWT_CYCCNT, value) - - def cpicnt_reset(self, value=0): - self.write(DWT_CPICNT, value & 0xFF) - - @staticmethod - def print_help(): - gdb.write("Usage:\n") - gdb.write("=========\n") - gdb.write("dwt:\n") - gdb.write("\tList available peripherals\n") - gdb.write("dwt configclk [Hz]:\n") - gdb.write("\tSet clock for rendering time values in seconds\n") - gdb.write("dwt reset:\n") - gdb.write("\tReset everything in DWT\n") - gdb.write("dwt reset counters:\n") - gdb.write("\tReset all DWT counters\n") - gdb.write("dwt cyccnt\n") - gdb.write("\tDisplay the cycle count\n") - gdb.write("\td(default):decimal, x: hex, o: octal, b: binary\n") - return - - -# Registers our class to GDB when sourced: -DWT() diff --git a/debug/PyCortexMDebug/cmdebug/x2d.py b/debug/PyCortexMDebug/cmdebug/x2d.py deleted file mode 100644 index fc3f185db20..00000000000 --- a/debug/PyCortexMDebug/cmdebug/x2d.py +++ /dev/null @@ -1,586 +0,0 @@ -#!/usr/bin/env python -"Makes working with XML feel like you are working with JSON" - -try: - from defusedexpat import pyexpat as expat -except ImportError: - from xml.parsers import expat - -from xml.sax.saxutils import XMLGenerator -from xml.sax.xmlreader import AttributesImpl - -try: # pragma no cover - from cStringIO import StringIO -except ImportError: # pragma no cover - try: - from StringIO import StringIO - except ImportError: - from io import StringIO - -from inspect import isgenerator - - -class ObjectDict(dict): - def __getattr__(self, name): - if name in self: - return self[name] - else: - raise AttributeError("No such attribute: " + name) - - -try: # pragma no cover - _basestring = basestring -except NameError: # pragma no cover - _basestring = str -try: # pragma no cover - _unicode = unicode -except NameError: # pragma no cover - _unicode = str - -__author__ = "Martin Blech" -__version__ = "0.12.0" -__license__ = "MIT" - - -class ParsingInterrupted(Exception): - pass - - -class _DictSAXHandler(object): - def __init__( - self, - item_depth=0, - item_callback=lambda *args: True, - xml_attribs=True, - attr_prefix="@", - cdata_key="#text", - force_cdata=False, - cdata_separator="", - postprocessor=None, - dict_constructor=ObjectDict, - strip_whitespace=True, - namespace_separator=":", - namespaces=None, - force_list=None, - comment_key="#comment", - ): - self.path = [] - self.stack = [] - self.data = [] - self.item = None - self.item_depth = item_depth - self.xml_attribs = xml_attribs - self.item_callback = item_callback - self.attr_prefix = attr_prefix - self.cdata_key = cdata_key - self.force_cdata = force_cdata - self.cdata_separator = cdata_separator - self.postprocessor = postprocessor - self.dict_constructor = dict_constructor - self.strip_whitespace = strip_whitespace - self.namespace_separator = namespace_separator - self.namespaces = namespaces - self.namespace_declarations = ObjectDict() - self.force_list = force_list - self.comment_key = comment_key - - def _build_name(self, full_name): - if self.namespaces is None: - return full_name - i = full_name.rfind(self.namespace_separator) - if i == -1: - return full_name - namespace, name = full_name[:i], full_name[i + 1 :] - try: - short_namespace = self.namespaces[namespace] - except KeyError: - short_namespace = namespace - if not short_namespace: - return name - else: - return self.namespace_separator.join((short_namespace, name)) - - def _attrs_to_dict(self, attrs): - if isinstance(attrs, dict): - return attrs - return self.dict_constructor(zip(attrs[0::2], attrs[1::2])) - - def startNamespaceDecl(self, prefix, uri): - self.namespace_declarations[prefix or ""] = uri - - def startElement(self, full_name, attrs): - name = self._build_name(full_name) - attrs = self._attrs_to_dict(attrs) - if attrs and self.namespace_declarations: - attrs["xmlns"] = self.namespace_declarations - self.namespace_declarations = ObjectDict() - self.path.append((name, attrs or None)) - if len(self.path) > self.item_depth: - self.stack.append((self.item, self.data)) - if self.xml_attribs: - attr_entries = [] - for key, value in attrs.items(): - key = self.attr_prefix + self._build_name(key) - if self.postprocessor: - entry = self.postprocessor(self.path, key, value) - else: - entry = (key, value) - if entry: - attr_entries.append(entry) - attrs = self.dict_constructor(attr_entries) - else: - attrs = None - self.item = attrs or None - self.data = [] - - def endElement(self, full_name): - name = self._build_name(full_name) - if len(self.path) == self.item_depth: - item = self.item - if item is None: - item = None if not self.data else self.cdata_separator.join(self.data) - - should_continue = self.item_callback(self.path, item) - if not should_continue: - raise ParsingInterrupted() - if len(self.stack): - data = None if not self.data else self.cdata_separator.join(self.data) - item = self.item - self.item, self.data = self.stack.pop() - if self.strip_whitespace and data: - data = data.strip() or None - if data and self.force_cdata and item is None: - item = self.dict_constructor() - if item is not None: - if data: - self.push_data(item, self.cdata_key, data) - self.item = self.push_data(self.item, name, item) - else: - self.item = self.push_data(self.item, name, data) - else: - self.item = None - self.data = [] - self.path.pop() - - def characters(self, data): - if not self.data: - self.data = [data] - else: - self.data.append(data) - - def comments(self, data): - if self.strip_whitespace: - data = data.strip() - self.item = self.push_data(self.item, self.comment_key, data) - - def push_data(self, item, key, data): - if self.postprocessor is not None: - result = self.postprocessor(self.path, key, data) - if result is None: - return item - key, data = result - if item is None: - item = self.dict_constructor() - try: - value = item[key] - if isinstance(value, list): - value.append(data) - else: - item[key] = [value, data] - except KeyError: - if self._should_force_list(key, data): - item[key] = [data] - else: - item[key] = data - return item - - def _should_force_list(self, key, value): - if not self.force_list: - return False - if isinstance(self.force_list, bool): - return self.force_list - try: - return key in self.force_list - except TypeError: - return self.force_list(self.path[:-1], key, value) - - -def parse( - xml_input, - encoding=None, - expat=expat, - process_namespaces=False, - namespace_separator=":", - disable_entities=True, - process_comments=False, - **kwargs -): - """Parse the given XML input and convert it into a dictionary. - - `xml_input` can either be a `string`, a file-like object, or a generator of strings. - - If `xml_attribs` is `True`, element attributes are put in the dictionary - among regular child elements, using `@` as a prefix to avoid collisions. If - set to `False`, they are just ignored. - - Simple example:: - - >>> import xmltodict - >>> doc = xmltodict.parse(\"\"\" - ... - ... 1 - ... 2 - ... - ... \"\"\") - >>> doc['a']['@prop'] - u'x' - >>> doc['a']['b'] - [u'1', u'2'] - - If `item_depth` is `0`, the function returns a dictionary for the root - element (default behavior). Otherwise, it calls `item_callback` every time - an item at the specified depth is found and returns `None` in the end - (streaming mode). - - The callback function receives two parameters: the `path` from the document - root to the item (name-attribs pairs), and the `item` (dict). If the - callback's return value is false-ish, parsing will be stopped with the - :class:`ParsingInterrupted` exception. - - Streaming example:: - - >>> def handle(path, item): - ... print('path:%s item:%s' % (path, item)) - ... return True - ... - >>> xmltodict.parse(\"\"\" - ... - ... 1 - ... 2 - ... \"\"\", item_depth=2, item_callback=handle) - path:[(u'a', {u'prop': u'x'}), (u'b', None)] item:1 - path:[(u'a', {u'prop': u'x'}), (u'b', None)] item:2 - - The optional argument `postprocessor` is a function that takes `path`, - `key` and `value` as positional arguments and returns a new `(key, value)` - pair where both `key` and `value` may have changed. Usage example:: - - >>> def postprocessor(path, key, value): - ... try: - ... return key + ':int', int(value) - ... except (ValueError, TypeError): - ... return key, value - >>> xmltodict.parse('12x', - ... postprocessor=postprocessor) - ObjectDict([(u'a', ObjectDict([(u'b:int', [1, 2]), (u'b', u'x')]))]) - - You can pass an alternate version of `expat` (such as `defusedexpat`) by - using the `expat` parameter. E.g: - - >>> import defusedexpat - >>> xmltodict.parse('hello', expat=defusedexpat.pyexpat) - ObjectDict([(u'a', u'hello')]) - - You can use the force_list argument to force lists to be created even - when there is only a single child of a given level of hierarchy. The - force_list argument is a tuple of keys. If the key for a given level - of hierarchy is in the force_list argument, that level of hierarchy - will have a list as a child (even if there is only one sub-element). - The index_keys operation takes precedence over this. This is applied - after any user-supplied postprocessor has already run. - - For example, given this input: - - - host1 - Linux - - - em0 - 10.0.0.1 - - - - - - If called with force_list=('interface',), it will produce - this dictionary: - {'servers': - {'server': - {'name': 'host1', - 'os': 'Linux'}, - 'interfaces': - {'interface': - [ {'name': 'em0', 'ip_address': '10.0.0.1' } ] } } } - - `force_list` can also be a callable that receives `path`, `key` and - `value`. This is helpful in cases where the logic that decides whether - a list should be forced is more complex. - - - If `process_comment` is `True` then comment will be added with comment_key - (default=`'#comment'`) to then tag which contains comment - - For example, given this input: - - - - - - 1 - - 2 - - - - If called with process_comment=True, it will produce - this dictionary: - 'a': { - 'b': { - '#comment': 'b comment', - 'c': { - - '#comment': 'c comment', - '#text': '1', - }, - 'd': '2', - }, - } - """ - handler = _DictSAXHandler(namespace_separator=namespace_separator, **kwargs) - if isinstance(xml_input, _unicode): - if not encoding: - encoding = "utf-8" - xml_input = xml_input.encode(encoding) - if not process_namespaces: - namespace_separator = None - parser = expat.ParserCreate(encoding, namespace_separator) - try: - parser.ordered_attributes = True - except AttributeError: - # Jython's expat does not support ordered_attributes - pass - parser.StartNamespaceDeclHandler = handler.startNamespaceDecl - parser.StartElementHandler = handler.startElement - parser.EndElementHandler = handler.endElement - parser.CharacterDataHandler = handler.characters - if process_comments: - parser.CommentHandler = handler.comments - parser.buffer_text = True - if disable_entities: - try: - # Attempt to disable DTD in Jython's expat parser (Xerces-J). - feature = "http://apache.org/xml/features/disallow-doctype-decl" - parser._reader.setFeature(feature, True) - except AttributeError: - # For CPython / expat parser. - # Anything not handled ends up here and entities aren't expanded. - parser.DefaultHandler = lambda x: None - # Expects an integer return; zero means failure -> expat.ExpatError. - parser.ExternalEntityRefHandler = lambda *x: 1 - if hasattr(xml_input, "read"): - parser.ParseFile(xml_input) - elif isgenerator(xml_input): - for chunk in xml_input: - parser.Parse(chunk, False) - parser.Parse(b"", True) - else: - parser.Parse(xml_input, True) - return handler.item - - -def _process_namespace(name, namespaces, ns_sep=":", attr_prefix="@"): - if not namespaces: - return name - try: - ns, name = name.rsplit(ns_sep, 1) - except ValueError: - pass - else: - ns_res = namespaces.get(ns.strip(attr_prefix)) - name = ( - "{}{}{}{}".format( - attr_prefix if ns.startswith(attr_prefix) else "", ns_res, ns_sep, name - ) - if ns_res - else name - ) - return name - - -def _emit( - key, - value, - content_handler, - attr_prefix="@", - cdata_key="#text", - depth=0, - preprocessor=None, - pretty=False, - newl="\n", - indent="\t", - namespace_separator=":", - namespaces=None, - full_document=True, - expand_iter=None, -): - key = _process_namespace(key, namespaces, namespace_separator, attr_prefix) - if preprocessor is not None: - result = preprocessor(key, value) - if result is None: - return - key, value = result - if ( - not hasattr(value, "__iter__") - or isinstance(value, _basestring) - or isinstance(value, dict) - ): - value = [value] - for index, v in enumerate(value): - if full_document and depth == 0 and index > 0: - raise ValueError("document with multiple roots") - if v is None: - v = ObjectDict() - elif isinstance(v, bool): - if v: - v = _unicode("true") - else: - v = _unicode("false") - elif not isinstance(v, dict): - if ( - expand_iter - and hasattr(v, "__iter__") - and not isinstance(v, _basestring) - ): - v = ObjectDict(((expand_iter, v),)) - else: - v = _unicode(v) - if isinstance(v, _basestring): - v = ObjectDict(((cdata_key, v),)) - cdata = None - attrs = ObjectDict() - children = [] - for ik, iv in v.items(): - if ik == cdata_key: - cdata = iv - continue - if ik.startswith(attr_prefix): - ik = _process_namespace( - ik, namespaces, namespace_separator, attr_prefix - ) - if ik == "@xmlns" and isinstance(iv, dict): - for k, v in iv.items(): - attr = "xmlns{}".format(":{}".format(k) if k else "") - attrs[attr] = _unicode(v) - continue - if not isinstance(iv, _unicode): - iv = _unicode(iv) - attrs[ik[len(attr_prefix) :]] = iv - continue - children.append((ik, iv)) - if pretty: - content_handler.ignorableWhitespace(depth * indent) - content_handler.startElement(key, AttributesImpl(attrs)) - if pretty and children: - content_handler.ignorableWhitespace(newl) - for child_key, child_value in children: - _emit( - child_key, - child_value, - content_handler, - attr_prefix, - cdata_key, - depth + 1, - preprocessor, - pretty, - newl, - indent, - namespaces=namespaces, - namespace_separator=namespace_separator, - expand_iter=expand_iter, - ) - if cdata is not None: - content_handler.characters(cdata) - if pretty and children: - content_handler.ignorableWhitespace(depth * indent) - content_handler.endElement(key) - if pretty and depth: - content_handler.ignorableWhitespace(newl) - - -def unparse( - input_dict, - output=None, - encoding="utf-8", - full_document=True, - short_empty_elements=False, - **kwargs -): - """Emit an XML document for the given `input_dict` (reverse of `parse`). - - The resulting XML document is returned as a string, but if `output` (a - file-like object) is specified, it is written there instead. - - Dictionary keys prefixed with `attr_prefix` (default=`'@'`) are interpreted - as XML node attributes, whereas keys equal to `cdata_key` - (default=`'#text'`) are treated as character data. - - The `pretty` parameter (default=`False`) enables pretty-printing. In this - mode, lines are terminated with `'\n'` and indented with `'\t'`, but this - can be customized with the `newl` and `indent` parameters. - - """ - if full_document and len(input_dict) != 1: - raise ValueError("Document must have exactly one root.") - must_return = False - if output is None: - output = StringIO() - must_return = True - if short_empty_elements: - content_handler = XMLGenerator(output, encoding, True) - else: - content_handler = XMLGenerator(output, encoding) - if full_document: - content_handler.startDocument() - for key, value in input_dict.items(): - _emit(key, value, content_handler, full_document=full_document, **kwargs) - if full_document: - content_handler.endDocument() - if must_return: - value = output.getvalue() - try: # pragma no cover - value = value.decode(encoding) - except AttributeError: # pragma no cover - pass - return value - - -if __name__ == "__main__": # pragma: no cover - import sys - import marshal - - try: - stdin = sys.stdin.buffer - stdout = sys.stdout.buffer - except AttributeError: - stdin = sys.stdin - stdout = sys.stdout - - (item_depth,) = sys.argv[1:] - item_depth = int(item_depth) - - def handle_item(path, item): - marshal.dump((path, item), stdout) - return True - - try: - root = parse( - stdin, - item_depth=item_depth, - item_callback=handle_item, - dict_constructor=dict, - ) - if item_depth == 0: - handle_item([], root) - except KeyboardInterrupt: - pass diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 39aca411a19..00000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,12 +0,0 @@ -version: '3' -services: - dev: - image: flipperdevices/flipperzero-toolchain - network_mode: host - privileged: true - tty: true - stdin_open: true - volumes: - - .:/project - - /dev/bus/usb:/dev/bus/usb - working_dir: '/project' diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index 64db408f3af..00000000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,41 +0,0 @@ -FROM ubuntu:hirsute - -RUN apt-get update \ - && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \ - ca-certificates \ - build-essential \ - python3 \ - git \ - clang-format-12 \ - dfu-util \ - openocd \ - libncurses5 \ - python-setuptools \ - libpython2.7-dev \ - libxml2-dev \ - libxslt1-dev \ - zlib1g-dev \ - wget \ - python3-protobuf \ - protobuf-compiler \ - python3-pip \ - libpython3-dev \ - ccache \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* - -RUN wget --progress=dot:giga "https://developer.arm.com/-/media/Files/downloads/gnu-rm/10.3-2021.07/gcc-arm-none-eabi-10.3-2021.07-$(uname -m)-linux.tar.bz2" && \ - tar xjf gcc-arm-none-eabi-10.3-2021.07-$(uname -m)-linux.tar.bz2 && \ - rm gcc-arm-none-eabi-10.3-2021.07-$(uname -m)-linux.tar.bz2 && \ - cd gcc-arm-none-eabi-10.3-2021.07/bin/ && \ - rm -rf ../share && \ - for file in * ; do ln -s "${PWD}/${file}" "/usr/bin/${file}" ; done && \ - cd / && arm-none-eabi-gcc -v && arm-none-eabi-gdb -v - -RUN pip3 install heatshrink2==0.11.0 Pillow==9.1.1 - -RUN ln -s `which clang-format-12` /usr/local/bin/clang-format - -COPY entrypoint.sh / - -ENTRYPOINT ["/entrypoint.sh"] diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh deleted file mode 100755 index 4d553e0b427..00000000000 --- a/docker/entrypoint.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -if [ -z "$1" ]; then - bash -else - echo "Running $1" - set -ex - bash -c "$1" -fi diff --git a/documentation/.gitignore b/documentation/.gitignore new file mode 100644 index 00000000000..c18ff03bbb8 --- /dev/null +++ b/documentation/.gitignore @@ -0,0 +1,2 @@ +/html +/latex \ No newline at end of file diff --git a/documentation/AppManifests.md b/documentation/AppManifests.md index 7b117789003..9afdccb0e41 100644 --- a/documentation/AppManifests.md +++ b/documentation/AppManifests.md @@ -1,3 +1,139 @@ # Flipper Application Manifests (.fam) -TBD \ No newline at end of file +All components of Flipper Zero firmware — services, user applications, and system settings — are developed independently. Each component has a build system manifest file named `application.fam`, which defines the basic properties of that component and its relations to other parts of the system. + +When building firmware, **`fbt`** collects all application manifests and processes their dependencies. Then it builds only those components referenced in the current build configuration. See [FBT docs](./fbt.md#firmware-application-set) for details on build configurations. + +## Application definition + +A firmware component's properties are declared in a Python code snippet, forming a call to the `App()` function with various parameters. + +Only two parameters are mandatory: **_appid_** and **_apptype_**. Others are optional and may only be meaningful for certain application types. + +### Parameters + +- **appid**: string, application ID within the build system. It is used to specify which applications to include in the build configuration and resolve dependencies and conflicts. + +- **apptype**: member of FlipperAppType.\* enumeration. Valid values are: + +| Enum member | Firmware component type | +| ----------- | ------------------------------------------------------------------------------------------- | +| SERVICE | System service, created at early startup | +| SYSTEM | Application is not being shown in any menus. It can be started by other apps or from CLI | +| APP | Regular application for the main menu | +| PLUGIN | Application to be built as a part of the firmware and to be placed in the Plugins menu | +| DEBUG | Application only visible in Debug menu with debug mode enabled | +| ARCHIVE | One and only Archive app | +| SETTINGS | Application to be placed in the system settings menu | +| STARTUP | Callback function to run at system startup. Does not define a separate app | +| EXTERNAL | Application to be built as `.fap` plugin | +| METAPACKAGE | Does not define any code to be run, used for declaring dependencies and application bundles | + +- **name**: name displayed in menus. +- **entry_point**: C function to be used as the application's entry point. Note that C++ function names are mangled, so you need to wrap them in `extern "C"` to use them as entry points. +- **flags**: internal flags for system apps. Do not use. +- **cdefines**: C preprocessor definitions to declare globally for other apps when the current application is included in the active build configuration. **For external applications**: specified definitions are used when building the application itself. +- **requires**: list of application IDs to include in the build configuration when the current application is referenced in the list of applications to build. +- **conflicts**: list of application IDs with which the current application conflicts. If any of them is found in the constructed application list, **`fbt`** will abort the firmware build process. +- **provides**: functionally identical to **_requires_** field. +- **stack_size**: stack size in bytes to allocate for an application on its startup. Note that allocating a stack too small for an app to run will cause a system crash due to stack overflow, and allocating too much stack space will reduce usable heap memory size for apps to process data. _Note: you can use `ps` and `free` CLI commands to profile your app's memory usage._ +- **icon**: animated icon name from built-in assets to be used when building the app as a part of the firmware. +- **order**: order of an application within its group when sorting entries in it. The lower the order is, the closer to the start of the list the item is placed. _Used for ordering startup hooks and menu entries._ +- **sdk_headers**: list of C header files from this app's code to include in API definitions for external applications. +- **targets**: list of strings and target names with which this application is compatible. If not specified, the application is built for all targets. The default value is `["all"]`. +- **resources**: name of a folder within the application's source folder to be used for packacking SD card resources for this application. They will only be used if application is included in build configuration. The default value is `""`, meaning no resources are packaged. + +#### Parameters for external applications + +The following parameters are used only for [FAPs](./AppsOnSDCard.md): + +- **sources**: list of strings, file name masks used for gathering sources within the app folder. The default value of `["*.c*"]` includes C and C++ source files. Applications cannot use the `"lib"` folder for their own source code, as it is reserved for **fap_private_libs**. Paths starting with `"!"` are excluded from the list of sources. They can also include wildcard characters and directory names. For example, a value of `["*.c*", "!plugins"]` will include all C and C++ source files in the app folder except those in the `plugins` (and `lib`) folders. Paths with no wildcards (`*, ?`) are treated as full literal paths for both inclusion and exclusion. +- **fap_version**: string, application version. The default value is "0.1". You can also use a tuple of 2 numbers in the form of (x,y) to specify the version. It is also possible to add more dot-separated parts to the version, like patch number, but only major and minor version numbers are stored in the built .fap. +- **fap_icon**: name of a `.png` file, 1-bit color depth, 10x10px, to be embedded within `.fap` file. +- **fap_libs**: list of extra libraries to link the application against. Provides access to extra functions that are not exported as a part of main firmware at the expense of increased `.fap` file size and RAM consumption. +- **fap_category**: string, may be empty. App subcategory, also determines the path of the FAP within the apps folder in the file system. +- **fap_description**: string, may be empty. Short application description. +- **fap_author**: string, may be empty. Application's author. +- **fap_weburl**: string, may be empty. Application's homepage. +- **fap_icon_assets**: string. If present, it defines a folder name to be used for gathering image assets for this application. These images will be preprocessed and built alongside the application. See [FAP assets](./AppsOnSDCard.md#fap-assets) for details. +- **fap_extbuild**: provides support for parts of application sources to be built by external tools. Contains a list of `ExtFile(path="file name", command="shell command")` definitions. **`fbt`** will run the specified command for each file in the list. +- **fal_embedded**: boolean, default `False`. Applies only to PLUGIN type. If `True`, the plugin will be embedded into host application's .fap file as a resource and extracted to `apps_assets/APPID` folder on its start. This allows plugins to be distributed as a part of the host application. + +Note that commands are executed at the firmware root folder, and all intermediate files must be placed in an application's temporary build folder. For that, you can use pattern expansion by **`fbt`**: `${FAP_WORK_DIR}` will be replaced with the path to the application's temporary build folder, and `${FAP_SRC_DIR}` will be replaced with the path to the application's source folder. You can also use other variables defined internally by **`fbt`**. + +Example for building an app from Rust sources: + +```python + sources=["target/thumbv7em-none-eabihf/release/libhello_rust.a"], + fap_extbuild=( + ExtFile( + path="${FAP_WORK_DIR}/target/thumbv7em-none-eabihf/release/libhello_rust.a", + command="cargo build --release --verbose --target thumbv7em-none-eabihf --target-dir ${FAP_WORK_DIR}/target --manifest-path ${FAP_SRC_DIR}/Cargo.toml", + ), + ), +``` + +- **fap_private_libs**: list of additional libraries distributed as sources alongside the application. These libraries will be built as a part of the application build process. + Library sources must be placed in a subfolder of the `lib` folder within the application's source folder. + Each library is defined as a call to the `Lib()` function, accepting the following parameters: + + - **name**: name of the library's folder. Required. + - **fap_include_paths**: list of the library's relative paths to add to the parent fap's include path list. The default value is `["."]`, meaning the library's source root. + - **sources**: list of filename masks to be used for gathering include files for this library. Paths are relative to the library's source root. The default value is `["*.c*"]`. + - **cflags**: list of additional compiler flags to be used for building this library. The default value is `[]`. + - **cdefines**: list of additional preprocessor definitions to be used for building this library. The default value is `[]`. + - **cincludes**: list of additional include paths to be used for building this library. Paths are relative to the application's root. This can be used for providing external search paths for this library's code — for configuration headers. The default value is `[]`. + +Example for building an app with a private library: + +```python + fap_private_libs=[ + Lib( + name="mbedtls", + fap_include_paths=["include"], + sources=[ + "library/des.c", + "library/sha1.c", + "library/platform_util.c", + ], + cdefines=["MBEDTLS_ERROR_C"], + ), + Lib( + name="loclass", + cflags=["-Wno-error"], + ), + ], +``` + +For that snippet, **`fbt`** will build 2 libraries: one from sources in `lib/mbedtls` folder and another from sources in the `lib/loclass` folder. For the `mbedtls` library, **`fbt`** will add `lib/mbedtls/include` to the list of include paths for the application and compile only the files specified in the `sources` list. Additionally, **`fbt`** will enable `MBEDTLS_ERROR_C` preprocessor definition for `mbedtls` sources. +For the `loclass` library, **`fbt`** will add `lib/loclass` to the list of the include paths for the application and build all sources in that folder. Also, **`fbt`** will disable treating compiler warnings as errors for the `loclass` library, which can be useful when compiling large 3rd-party codebases. + +Both libraries will be linked with the application. + +## `.fam` file contents + +The `.fam` file contains one or more application definitions. For example, here's a part of `applications/service/bt/application.fam`: + +```python +App( + appid="bt_start", + apptype=FlipperAppType.STARTUP, + entry_point="bt_on_system_start", + order=70, +) + +App( + appid="bt_settings", + name="Bluetooth", + apptype=FlipperAppType.SETTINGS, + entry_point="bt_settings_app", + stack_size=1 * 1024, + requires=[ + "bt", + "gui", + ], + order=10, +) +``` + +For more examples, see `.fam` files from various firmware parts. diff --git a/documentation/AppsOnSDCard.md b/documentation/AppsOnSDCard.md new file mode 100644 index 00000000000..3f6d51acf06 --- /dev/null +++ b/documentation/AppsOnSDCard.md @@ -0,0 +1,83 @@ +# FAP (Flipper Application Package) + +[fbt](./fbt.md) supports building applications as FAP files. FAPs are essentially `.elf` executables with extra metadata and resources bundled in. + +FAPs are built with the `faps` target. They can also be deployed to the `dist` folder with the `fap_dist` target. + +FAPs do not depend on being run on a specific firmware version. Compatibility is determined by the FAP's metadata, which includes the required [API version](#api-versioning). + +## How to set up an application to be built as a FAP + +FAPs are created and developed the same way as internal applications that are part of the firmware. + +To build your application as a FAP, create a folder with your app's source code in `applications_user`, then write its code the way you'd do when creating a regular built-in application. Then configure its `application.fam` manifest, and set its _apptype_ to FlipperAppType.EXTERNAL. See [Application Manifests](./AppManifests.md#application-definition) for more details. + +- To build your application, run `./fbt fap_{APPID}`, where APPID is your application's ID in its manifest. +- To build your app and upload it over USB to run on Flipper, use `./fbt launch APPSRC=applications_user/path/to/app`. This command is configured in the default [VS Code profile](../.vscode/ReadMe.md) as a "Launch App on Flipper" build action (Ctrl+Shift+B menu). +- To build an app without uploading it to Flipper, use `./fbt build APPSRC=applications_user/path/to/app`. This command is also available in VSCode configuration as "Build App". +- To build all FAPs, run `./fbt faps` or `./fbt fap_dist`. + +## FAP assets + +FAPs can include static and animated images as private assets. They will be automatically compiled alongside application sources and can be referenced the same way as assets from the main firmware. + +To use that feature, put your images in a subfolder inside your application's folder, then reference that folder in your application's manifest in the `fap_icon_assets` field. See [Application Manifests](./AppManifests.md#application-definition) for more details. + +To use these assets in your application, put `#include "{APPID}_icons.h"` in your application's source code, where `{APPID}` is the `appid` value field from your application's manifest. Then you can use all icons from your application's assets the same way as if they were a part of `assets_icons.h` of the main firmware. + +Images and animated icons should follow the same [naming convention](../assets/ReadMe.md#asset-naming-rules) as those from the main firmware. + +## Debugging FAPs + +**`fbt`** includes a script for gdb-py to provide debugging support for FAPs, `debug/flipperapps.py`. It is loaded in default debugging configurations by **`fbt`** and stock VS Code configurations. + +With it, you can debug FAPs as if they were a part of the main firmware — inspect variables, set breakpoints, step through the code, etc. + +If debugging session is active, firmware will trigger a breakpoint after loading a FAP it into memory, but before running any code from it. This allows you to set breakpoints in the FAP's code. Note that any breakpoints set before the FAP is loaded may need re-setting after the FAP is actually loaded, since before loading it debugger cannot know the exact address of the FAP's code. + +### Setting up debugging environment + +The debugging support script looks up debugging information in the latest firmware build directory (`build/latest`). That directory is symlinked by `fbt` to the latest firmware configuration (Debug or Release) build directory when you run `./fbt` for the chosen configuration. See [fbt docs](./fbt.md#nb) for details. + +To debug FAPs, do the following: + +1. Build firmware with `./fbt` +2. Flash it with `./fbt flash` +3. [Build your FAP](#how-to-set-up-an-application-to-be-built-as-a-fap) and run it on Flipper + +After that, you can attach with `./fbt debug` or VS Code and use all debug features. + +It is **important** that firmware and application build type (debug/release) match and that the matching firmware folder is linked as `build/latest`. Otherwise, debugging will not work. + +## How Flipper runs an application from an SD card + +Flipper's MCU cannot run code directly from external storage, so it needs to be copied to RAM first. That is done by the App Loader application responsible for loading the FAP from the SD card, verifying its integrity and compatibility, copying it to RAM, and adjusting it for its new location. + +Since FAP has to be loaded to RAM to be executed, the amount of RAM available for allocations from heap is reduced compared to running the same app from flash, as a part of the firmware. Note that the amount of occupied RAM is less than the total FAP file size since only code and data sections are allocated, while the FAP file includes extra information only used at app load time. + +Applications are built for a specific API version. It is a part of the hardware target's definition and contains a major and minor version number. The App Loader checks if the application's major API version matches the firmware's major API version. + +The App Loader allocates memory for the application and copies it to RAM, processing relocations and providing concrete addresses for imported symbols using the [symbol table](#symbol-table). Then it starts the application. + +## API versioning + +Not all parts of firmware are available for external applications. A subset of available functions and variables is defined in the "api_symbols.csv" file, which is a part of the firmware target definition in the `targets/` directory. + +**`fbt`** uses semantic versioning for the API. The major version is incremented when there are breaking changes in the API. The minor version is incremented when new features are added. + +Breaking changes include: + +- Removing a function or a global variable +- Changing the signature of a function + +API versioning is mostly automated by **`fbt`**. When rebuilding the firmware, **`fbt`** checks if there are any changes in the API exposed by headers gathered from `SDK_HEADERS`. If so, it stops the build, adjusts the API version, and asks the user to go through the changes in the `.csv` file. New entries are marked with a "`?`" mark, and the user is supposed to change the mark to "`+`" for the entry to be exposed for FAPs, or to "`-`" for it to be unavailable. + +**`fbt`** will not allow building a firmware until all "`?`" entries are changed to "`+`" or "`-`". + +**NB:** **`fbt`** automatically manages the API version. The only case where manually incrementing the major API version is allowed (and required) is when existing "`+`" entries are to be changed to "`-`". + +### Symbol table + +The symbol table is a list of symbols exported by firmware and available for external applications. It is generated by **`fbt`** from the API symbols file and is used by the App Loader to resolve addresses of imported symbols. It is build as a part of the `fap_loader` application. + +**`fbt`** also checks if all imported symbols are present in the symbol table. If there are any missing symbols, it will issue a warning listing them. The application won't be able to run on the device until all required symbols are provided in the symbol table. diff --git a/documentation/Doxyfile b/documentation/Doxyfile index 6d6bb8aa8cf..f31cbb9d868 100644 --- a/documentation/Doxyfile +++ b/documentation/Doxyfile @@ -872,12 +872,9 @@ WARN_LOGFILE = # Note: If this tag is empty the current directory is searched. INPUT = applications \ - core \ - lib/infrared \ - lib/subghz \ - lib/toolbox \ - lib/onewire \ - firmware/targets/furi_hal_include + lib \ + firmware \ + furi # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -930,7 +927,19 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = +EXCLUDE = \ + lib/mlib \ + lib/stm32wb_cmsis \ + lib/stm32wb_copro \ + lib/stm32wb_hal_driver \ + lib/littlefs \ + lib/nanopb \ + assets/protobuf \ + lib/libusb_stm32 \ + lib/FreeRTOS-Kernel \ + lib/microtar \ + lib/mbedtls \ + lib/cxxheaderparser # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/documentation/FuriCheck.md b/documentation/FuriCheck.md new file mode 100644 index 00000000000..02f3fc9173b --- /dev/null +++ b/documentation/FuriCheck.md @@ -0,0 +1,40 @@ +# Run time checks and forced system crash + +The best way to protect system integrity is to reduce amount cases that we must handle and crash the system as early as possible. +For that purpose we have bunch of helpers located in Furi Core check.h. + +## Couple notes before start + +- Definition of Crash - log event, save crash information in RTC and reboot the system. +- Definition of Halt - log event, stall the system. +- Debug and production builds behaves differently: debug build will never reset system in order to preserve state for debugging. +- If you have debugger connected we will stop before reboot automatically. +- All helpers accept optional MESSAGE_CSTR: it can be in RAM or Flash memory, but only messages from Flash will be shown after system reboot. +- MESSAGE_CSTR can be NULL, but macros magic already doing it for you, so just don't. + +## `furi_assert(CONDITION)` or `furi_assert(CONDITION, MESSAGE_CSTR)` + +Assert condition in development environment and crash the system if CONDITION is false. + +- Should be used at development stage in apps and services +- Keep in mind that release never contains this check +- Keep in mind that libraries never contains this check by default, use `LIB_DEBUG=1` if you need it +- Avoid putting function calls into CONDITION, since it may be omitted in some builds + +## `furi_check(CONDITION)` or `furi_check(CONDITION, MESSAGE_CSTR)` + +Always assert condition and crash the system if CONDITION is false. + +- Use it if you always need to check conditions + +## `furi_crash()` or `furi_crash(MESSAGE_CSTR)` + +Crash the system. + +- Use it to crash the system. For example: if abnormal condition detected. + +## `furi_halt()` or `furi_halt(MESSAGE_CSTR)` + +Halt the system. + +- We use it internally to shutdown flipper if poweroff is not possible. diff --git a/documentation/FuriHalBus.md b/documentation/FuriHalBus.md new file mode 100644 index 00000000000..230a98050fd --- /dev/null +++ b/documentation/FuriHalBus.md @@ -0,0 +1,113 @@ +# Using FuriHalBus API + +## Basic info + +On system startup, most of the peripheral devices are under reset and not clocked by default. This is done to reduce power consumption and to guarantee that the device will always be in the same state before use. +Some crucial peripherals are enabled right away by the system, others must be explicitly enabled by the user code. + +**NOTE:** Here and afterwards the word *"system"* refers to any code belonging to the operating system, hardware drivers or built-in applications. + +To **ENABLE** a peripheral, call `furi_hal_bus_enable()`. At the time of the call, the peripheral in question MUST be disabled, otherwise a crash will occur to indicate improper use. This means that any given peripheral cannot be enabled twice or more without disabling it first. + +To **DISABLE** a peripheral, call `furi_hal_bus_disable()`. Likewise, the peripheral in question MUST be enabled, otherwise a crash will occur. + +To **RESET** a peripheral, call `furi_hal_bus_reset()`. The peripheral in question MUST be enabled, otherwise a crash will occur. This method is used whenever it is necessary to reset all the peripheral's registers to their initial states without disabling it. + +## Peripherals + +Built-in peripherals are divided into three categories: +- Enabled by the system on startup, never disabled; +- Enabled and disabled by the system on demand; +- Enabled and disabled by the user code. + +### Always-on peripherals + +Below is the list of peripherals that are enabled by the system. The user code must NEVER attempt to disable them. If a corresponding API is provided, the user code must employ it in order to access the peripheral. + +*Table 1* - Peripherals enabled by the system + +| Peripheral | Enabled at | +| :-----------: | :-----------------------: | +| DMA1 | `furi_hal_dma.c` | +| DMA2 | -- | +| DMAMUX | -- | +| GPIOA | `furi_hal_resources.c` | +| GPIOB | -- | +| GPIOC | -- | +| GPIOD | -- | +| GPIOE | -- | +| GPIOH | -- | +| PKA | `furi_hal_bt.c` | +| AES2 | -- | +| HSEM | -- | +| IPCC | -- | +| FLASH | enabled by hardware | + +### On-demand system peripherals + +Below is the list of peripherals that are enabled and disabled by the system. The user code must avoid using them directly, preferring the respective APIs instead. + +When not using the API, these peripherals MUST be enabled by the user code and then disabled when not needed anymore. + +*Table 2* - Peripherals enabled and disabled by the system + +| Peripheral | API header file | +| :-----------: | :-------------------: | +| RNG | `furi_hal_random.h` | +| SPI1 | `furi_hal_spi.h` | +| SPI2 | -- | +| I2C1 | `furi_hal_i2c.h` | +| I2C3 | -- | +| USART1 | `furi_hal_uart.h` | +| LPUART1 | -- | +| USB | `furi_hal_usb.h` | + +### On-demand shared peripherals + +Below is the list of peripherals that are not enabled by default and MUST be enabled by the user code each time it accesses them. + +Note that some of these peripherals may also be used by the system to implement its certain features. +The system will take over any given peripheral only when the respective feature is in use. + +*Table 3* - Peripherals enabled and disabled by user + +| Peripheral | System | Purpose | +| :-----------: | :-------: | ------------------------------------- | +| CRC | | | +| TSC | | | +| ADC | | | +| QUADSPI | | | +| TIM1 | yes | subghz, lfrfid, nfc, infrared, etc... | +| TIM2 | yes | subghz, infrared, etc... | +| TIM16 | yes | speaker | +| TIM17 | yes | cc1101_ext | +| LPTIM1 | yes | tickless idle timer | +| LPTIM2 | yes | pwm | +| SAI1 | | | +| LCD | | | + + +## DMA + +The DMA1,2 peripherals are a special case in that they have multiple independent channels. Some of the channels may be in use by the system. + +Below is the list of DMA channels and their usage by the system. + +*Table 4* - DMA channels + +| DMA | Channel | System | Purpose | +| :---: | :-------: | :-------: | ------------------------- | +| DMA1 | 1 | yes | digital signal | +| -- | 2 | yes | -- | +| -- | 3 | | | +| -- | 4 | yes | pulse reader | +| -- | 5 | | | +| -- | 6 | | | +| -- | 7 | | | +| DMA2 | 1 | yes | infrared, lfrfid, subghz, | +| -- | 2 | yes | -- | +| -- | 3 | yes | cc1101_ext | +| -- | 4 | yes | cc1101_ext | +| -- | 5 | yes | cc1101_ext | +| -- | 6 | yes | SPI | +| -- | 7 | yes | SPI | diff --git a/documentation/FuriHalDebuging.md b/documentation/FuriHalDebuging.md new file mode 100644 index 00000000000..da00cbdfb73 --- /dev/null +++ b/documentation/FuriHalDebuging.md @@ -0,0 +1,30 @@ +# Furi HAL Debugging + +Some Furi subsystems got additional debugging features that can be enabled by adding additional defines to firmware compilation. +Usually they are used for low level tracing and profiling or signal redirection/duplication. + + +## FuriHalOs + +`--extra-define=FURI_HAL_OS_DEBUG` enables tick, tick suppression, idle and time flow. + +There are 3 signals that will be exposed to external GPIO pins: + +- `AWAKE` - `PA7` - High when system is busy with computations, low when sleeping. Can be used to track transitions to sleep mode. +- `TICK` - `PA6` - Flipped on system tick, only flips when no tick suppression in progress. Can be used to track tick skew and abnormal task scheduling. +- `SECOND` - `PA4` - Flipped each second. Can be used for tracing RT issue: time flow disturbance means system doesn't conforms Hard RT. + + + +## FuriHalPower + +`--extra-define=FURI_HAL_POWER_DEBUG` enables power subsystem mode transitions tracing. + +There are 2 signals that will be exposed to external GPIO pins: + +- `WFI` - `PB2` - Light sleep (wait for interrupt) used. Basically this is lightest and most non-breaking things power save mode. All function and debug should work correctly in this mode. +- `STOP` - `PC3` - STOP mode used. Platform deep sleep mode. Extremely fragile mode where most of the silicon is disabled or in unusable state. Debugging MCU in this mode is nearly impossible. + +## FuriHalSD + +`--extra-define=FURI_HAL_SD_SPI_DEBUG` enables SD card SPI bus logging. diff --git a/documentation/HardwareTargets.md b/documentation/HardwareTargets.md new file mode 100644 index 00000000000..b3213d4f506 --- /dev/null +++ b/documentation/HardwareTargets.md @@ -0,0 +1,44 @@ +## 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 `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/KeyCombo.md b/documentation/KeyCombo.md index 359fd5b9bd3..6db5b411354 100644 --- a/documentation/KeyCombo.md +++ b/documentation/KeyCombo.md @@ -1,134 +1,119 @@ # Key Combos -There are times when your flipper feels blue and don't respond to your commands. -In that case you may find this guide useful. +There are times when your Flipper feels blue and doesn't respond to any of your commands due to a software issue. This guide will help you solve this problem. +## Basic combos -## Basic Combos +### Hardware reset - -### Hardware Reset - -- Press `LEFT` and `BACK` and hold for couple seconds +- Press `LEFT` and `BACK` and hold for a couple of seconds - Release `LEFT` and `BACK` -This combo performs hardware reset by pulling MCU reset line down. -Main components involved: Keys -> DD8(NC7SZ32M5X, OR-gate) -> DD1(STM32WB55, MCU) - -There is 1 case when it's not working: +This combo performs a hardware reset by pulling the MCU reset line down. +Main components involved: Keys -> DD8(NC7SZ32M5X, OR-gate) -> DD1(STM32WB55, MCU). -- MCU debug block is active and holding reset line from inside. +It won't work only in one case: +- The MCU debug block is active and holding the reset line from inside. ### Hardware Power Reset -- Disconnect USB and any external power supplies -- Disconnect USB once again -- Make sure that you've disconnected USB and any external power supplies -- Press `BACK` and hold for 30 seconds (Only will work with USB Disconnected) -- If you have not disconnected USB, then disconnect USB and repeat previous step -- Release `BACK` key +- Disconnect the USB cable and any external power supplies +- Disconnect the USB once again +- Make sure you've disconnected the USB and any external power supplies +- Press `BACK` and hold for 30 seconds (this will only work with the USB disconnected) +- If you haven't disconnected the USB, then disconnect it and repeat the previous step +- Release the `BACK` key -This combo performs reset by switching SYS power line off and then on. -Main components involved: Keys -> DD6(bq25896, charger) +This combo performs a reset by switching SYS power line off and then on. +Main components involved: Keys -> DD6(bq25896, charger). -There is 1 case when it's not working: +It won't work only in one case: - Power supply is connected to USB or 5V_ext - ### Software DFU -- Press `LEFT` on boot to enter DFU with flipper boot-loader - -There is 1 case when it's not working: +- Press `LEFT` on boot to enter DFU with Flipper boot-loader -- Flipper Boot-loader is damaged or absent +It won't work only in one case: +- Flipper boot-loader is damaged or absent ### Hardware DFU - Press `OK` on boot to enter DFU with ST boot-loader -There is 1 case when it's not working: +It won't work only in one case: -- Option Bytes are damaged or set to ignore `OK` key - - -## DFU Combos +- Option Bytes are damaged or set to ignore the `OK` key +## DFU combos ### Hardware Reset + Software DFU -- Press `LEFT` and `BACK` and hold for couple seconds +- Press `LEFT` and `BACK` and hold for a couple of seconds - Release `BACK` -- Device will enter DFU with indication (Blue LED + DFU Screen) +- Device will enter DFU with an indication (Blue LED + DFU Screen) - Release `LEFT` -This combo performs hardware reset by pulling MCU reset line down. -Then `LEFT` key indicates to boot-loader that DFU mode requested. - -There are 2 cases when it's not working: +This combo performs a hardware reset by pulling the MCU reset line down. Then, the `LEFT` key indicates to the boot-loader that DFU mode is requested. -- MCU debug block is active and holding reset line from inside -- Flipper Boot-loader is damaged or absent +It won't work in two cases: +- The MCU debug block is active and holding the reset line from inside +- Flipper boot-loader is damaged or absent ### Hardware Reset + Hardware DFU -- Press `LEFT` and `BACK` and `OK` and hold for couple seconds +- Press `LEFT`, `BACK` and `OK` and hold for a couple of seconds - Release `BACK` and `LEFT` -- Device will enter DFU without indication +- The device will enter DFU without an indication -This combo performs hardware reset by pulling MCU reset line down. -Then `OK` key forces MCU to load internal boot-loader. +This combo performs a hardware reset by pulling the MCU reset line down. Then, the `OK` key forces MCU to load the internal boot-loader. -There are 2 cases when it's not working: - -- MCU debug block is active and holding reset line from inside -- Option Bytes are damaged or set to ignore `OK` key +It won't work in two cases: +- The MCU debug block is active and holding the reset line from inside +- Option Bytes are damaged or set to ignore the `OK` key ### Hardware Power Reset + Software DFU -- Disconnect USB and any external power supplies +- Disconnect the USB and any external power supplies - Press `BACK` and `LEFT` for 30 seconds - Release `BACK` -- Device will enter DFU with indication (Blue LED + DFU Screen) +- The device will enter DFU with an indication (Blue LED + DFU Screen) - Release `LEFT` -- Plug USB +- Plug in the USB -This combo performs reset by switching SYS power line off and then on. -Then `LEFT` key indicates to boot-loader that DFU mode requested. +This combo performs a reset by switching the SYS power line off and then on. Next, the `LEFT` key indicates to the boot-loader that DFU mode is requested. -There are 2 cases when it's not working: +It won't work in two cases: - Power supply is connected to USB or 5V_ext -- Flipper Boot-loader is damaged or absent - +- Flipper boot-loader is damaged or absent ### Hardware Power Reset + Hardware DFU -- Disconnect USB and any external power supplies +- Disconnect the USB and any external power supplies - Press `BACK` and `OK` and hold for 30 seconds - Release `BACK` and `OK` -- Device will enter DFU without indication -- Plug USB +- The device will enter DFU without indication +- Plug in the USB -This combo performs reset by switching SYS power line off and then on. -Then `OK` key forces MCU to load internal boot-loader. +This combo performs a reset by switching the SYS power line off and then on. Next, the `OK` key forces MCU to load the internal boot-loader. -There are 2 cases when it's not working: +It won't work in two cases: - Power supply is connected to USB or 5V_ext -- Option Bytes are damaged or set to ignore `OK` key +- Option Bytes are damaged or set to ignore the `OK` key # Alternative ways to recover your device -If none of the described methods were useful: +If none of the described methods helped you: -- Ensure battery charged -- Disconnect battery and connect again (Requires disassembly) -- Try to Flash device with ST-Link or other programmer that support SWD +- Make sure the battery charged +- Disconnect the battery and connect again (requires disassembly) +- Try to flash the device with ST-Link or another programmer that supports SWD -If you still here and your device is not working: it's not software issue. +If you're still here and your device is not working: it's not a software issue. diff --git a/documentation/LFRFIDRaw.md b/documentation/LFRFIDRaw.md new file mode 100644 index 00000000000..5a8cbde60d7 --- /dev/null +++ b/documentation/LFRFIDRaw.md @@ -0,0 +1,23 @@ +# Reading RAW RFID data + +Flipper Zero has the option to read RAW data from 125 kHz cards that allows you to record the card's data and save it, similar to how a dictaphone records sound. + +To use this function, you need to activate the Debug mode on your Flipper Zero by doing the following: + +1. Go to **Main Menu** → **Settings** → **System**. + +2. Set **Debug** to **ON**. + +Once the Debug mode is activated on your Flipper Zero, you can read RAW data from 125 kHz RFID cards: + +1. Go to **Main Menu** → **125 kHz RFID** → **Extra Actions**. + +2. Select **RAW RFID** data and name the raw file. + +3. Read instructions and press **OK**. + +4. Apply the card to Flipper Zero's back. + +5. Once the reading is finished, press **OK**. + +Two files with data (with ASK and PSK modulations) will be saved in the `lfrfid` folder on the microSD card. Now, you can share it and the card's photo with developers by creating an issue on GitHub. diff --git a/documentation/OTA.md b/documentation/OTA.md index 2a6b09846c6..ed75560cfe6 100644 --- a/documentation/OTA.md +++ b/documentation/OTA.md @@ -1,84 +1,79 @@ # Executing code from RAM -In Flipper firmware, we have a special boot mode that loads a specially crafted system image into RAM and transfers control to it. System image executing in RAM has full write access to whole Flipper's flash memory — something that's not possible when running main code from same flash. - -We leverage that boot mode to perform OTA firmware updates, including operations on radio stack running on second MCU core. +In Flipper firmware, we have a special boot mode that loads a specially crafted system image into RAM and transfers control to it. System image executing in RAM has full write access to Flipper's entire flash memory — something that's not possible when running main code from the same flash. +We leverage that boot mode to perform OTA firmware updates, including operations on a radio stack running on the second MCU core. # How does Flipper OTA work? Installation of OTA updates goes through 3 stages: -## 1. Backing up internal storage (`/int/`) - -It is a special partition of Flipper's flash memory, taking up all available space not used by firmware code. Newer versions of firmware may be of different size, and simply installing them would cause flash repartitioning and data loss. +## 1. Backing up internal storage (`/int`) -So, before taking any action upon the firmware, we back up current configuration from `/int/` into a plain tar archive on SD card. +It is a special partition of Flipper's flash memory, taking up all available space not used by the firmware code. Newer versions of firmware may be of different size, and simply installing them would cause flash repartitioning and data loss. +So, before taking any action on the firmware, we back up the current configuration from `/int` into a plain tar archive on the SD card. ## 2. Performing device update -For that, main firmware loads an updater image - a customized build of main Flipper firmware — into RAM and runs it. Updater performs operations on system flash that are described by an Update manifest file. +The main firmware loads an updater image — a customized build of the main Flipper firmware — into RAM and runs it. Updater performs operations on system flash as described by an Update manifest file. -First, if there's a Radio stack image bundled with the update, updater compares its version with currently installed one. If they don't match, updater performs stack deinstallation followed by writing and installing a new one. The installation itself is performed by proprietary software, FUS, running on Core2, and leads to a series of system restarts. +First, if there's a Radio stack image bundled with the update, updater compares its version with the currently installed one. If they don't match, updater performs stack deinstallation followed by writing and installing a new one. The installation itself is performed by proprietary software FUS running on Core2, and leads to a series of system restarts. -Then updater validates and corrects Option Bytes — a special memory region containing low-level configuration for Flipper's MCU. +Then, updater validates and corrects Option Bytes — a special memory region containing low-level configuration for Flipper's MCU. After that, updater loads a `.dfu` file with firmware to be flashed, checks its integrity using CRC32, writes it to system flash and validates written data. - ## 3. Restoring internal storage and updating resources -After performing operations on flash memory, system restarts into newly flashed firmware. Then it performs restoration of previously backed up `/int` contents. - -If update package contains an additional resources archive, it is extracted onto SD card. +After performing operations on flash memory, the system restarts into newly flashed firmware. Then it performs restoration of previously backed up `/int` contents. +If the update package contains an additional resources archive, it is extracted onto the SD card. # Update manifest -Update packages come with a manifest that contains a description of its contents. The manifest is in Flipper File Format — a simple text file, comprised of key-value pairs. +An update package comes with a manifest that contains a description of its contents. The manifest is in Flipper File Format — a simple text file, comprised of key-value pairs. ## Mandatory fields -Update manifest must contain the following keys in given order: +An update manifest must contain the following keys in the given order: -* __Filetype__: a constant string, "Flipper firmware upgrade configuration"; +- **Filetype**: a constant string, "Flipper firmware upgrade configuration". -* __Version__: manifest version. Current value is 2; +- **Version**: manifest version. The current value is 2. -* __Info__: arbitraty string, describing package contents; +- **Info**: arbitrary string, describing package contents. -* __Target__: hardware revision the package is built for; +- **Target**: hardware revision for which the package is built. -* __Loader__: file name of stage 2 loader that is executed from RAM; +- **Loader**: file name of stage 2 loader that is executed from RAM. -* __Loader CRC__: CRC32 of loader file. Note that it is represented in little-endian hex. +- **Loader CRC**: CRC32 of loader file. Note that it is represented in little-endian hex. ## Optional fields -Other fields may have empty values, is such case updater skips all operations related to such values. +Other fields may have empty values. In this case, updater skips all operations related to these values. -* __Radio__: file name of radio stack image, provided by STM; +- **Radio**: file name of radio stack image, provided by STM. -* __Radio address__: address to install the radio stack at. It is specified in Release Notes by STM; +- **Radio address**: address to install the radio stack at. It is specified in Release Notes by STM. -* __Radio version__: Radio major, minor and sub versions followed by branch, release, and stack type packed into 6 hex-encoded bytes; +- **Radio version**: radio major, minor and sub versions followed by branch, release and stack type packed into 6 hex-encoded bytes. -* __Radio CRC__: CRC32 of radio image; +- **Radio CRC**: CRC32 of radio image. -* __Resources__: file name of TAR acrhive with resources to be extracted on SD card; - -* __OB reference__, __OB mask__, __OB write mask__: reference values for validating and correcting option bytes. +- **Resources**: file name of TAR archive with resources to be extracted onto the SD card. +- **OB reference**, **OB mask**, **OB write mask**: reference values for validating and correcting option bytes. # OTA update error codes -We designed the OTA update process to be as fail-safe as possible. We don't start any risky operation before validating all related pieces of data to ensure we don't leave the device in partially updated, or bricked, state. +We designed the OTA update process to be as fail-safe as possible. We don't start any risky operations before validating all related pieces of data to ensure we don't leave the device in a partially updated, or bricked, state. -Even if something goes wrong, Updater gives you an option to retry failed operations, and reports its state with an error code. These error codes have an `[XX-YY]` format, where `XX` encodes an operation that failed, and `YY` contains extra details on its progress where the error occured. +Even if something goes wrong, updater allows you to retry failed operations and reports its state with an error code. These error codes have an `[XX-YY]` format, where `XX` encodes the failed operation, and `YY` contains extra details on its progress where the error occurred. | Stage description | Code | Progress | Description | -|:-----------------------:|-------:|------------|--------------------------------------------| +| :---------------------: | -----: | ---------- | ------------------------------------------ | | Loading update manifest | **1** | **13** | Updater reported hardware version mismatch | | | | **20** | Failed to get saved manifest path | | | | **30** | Failed to load manifest | @@ -86,61 +81,63 @@ Even if something goes wrong, Updater gives you an option to retry failed operat | | | **50** | Package has mismatching HW target | | | | **60** | Missing DFU file | | | | **80** | Missing radio firmware file | -| Backing up LFS | **2** | **0-100** | FS read/write error | -| Checking radio FW | **3** | **0-99** | Error reading radio firmware file | +| Backing up LFS | **2** | **0-100** | FS read/write error | +| Checking radio FW | **3** | **0-99** | Error reading radio firmware file | | | | **100** | CRC mismatch | -| Uninstalling radio FW | **4** | **0** | SHCI Delete command error | +| Uninstalling radio FW | **4** | **0** | SHCI Delete command error | | | | **80** | Error awaiting command status | -| Writing radio FW | **5** | **0-100** | Block read/write error | -| Installing radio FW | **6** | **0** | SHCI Install command error | +| Writing radio FW | **5** | **0-100** | Block read/write error | +| Installing radio FW | **6** | **10** | SHCI Install command error | | | | **80** | Error awaiting command status | -| Radio is updating | **7** | **10** | Error waiting for operation completion | -| Validating opt. bytes | **8** | **yy** | Option byte code | -| Checking DFU file | **9** | **0** | Error opening DFU file | +| Core2 is busy | **7** | **10** | Couldn't start C2 | +| | | **20** | Failed to switch C2 to FUS mode | +| | | **30** | Error in FUS operation | +| | | **50** | Failed to switch C2 to stack mode | +| Validating opt. bytes | **8** | **yy** | Option byte code | +| Checking DFU file | **9** | **0** | Error opening DFU file | | | | **1-98** | Error reading DFU file | | | | **99-100** | Corrupted DFU file | -| Writing flash | **10** | **0-100** | Block read/write error | -| Validating flash | **11** | **0-100** | Block read/write error | -| Restoring LFS | **12** | **0-100** | FS read/write error | -| Updating resources | **13** | **0-100** | SD card read/write error | - +| Writing flash | **10** | **0-100** | Block read/write error | +| Validating flash | **11** | **0-100** | Block read/write error | +| Restoring LFS | **12** | **0-100** | FS read/write error | +| Updating resources | **13** | **0-100** | SD card read/write error | # Building update packages - ## Full package -To build full update package, including firmware, radio stack and resources for SD card, run `./fbt COMPACT=1 DEBUG=0 updater_package` +To build a full update package, including firmware, radio stack and resources for the SD card, run: +`./fbt COMPACT=1 DEBUG=0 updater_package` ## Minimal package -To build minimal update package, including only firmware, run `./fbt COMPACT=1 DEBUG=0 updater_minpackage` +To build a minimal update package, including only firmware, run: +`./fbt COMPACT=1 DEBUG=0 updater_minpackage` ## Customizing update bundles -Default update packages are built with Bluetooth Light stack. -You can pick a different stack, if your firmware version supports it, and build a bundle with it passing stack type and binary name to `fbt`: +Default update packages are built with Bluetooth Light stack. +You can pick a different stack if your firmware version supports it, and build a bundle with it by passing the stack type and binary name to `fbt`: -`./fbt updater_package COMPACT=1 DEBUG=0 COPRO_OB_DATA=scripts/ob_custradio.data COPRO_STACK_BIN=stm32wb5x_BLE_Stack_full_fw.bin COPRO_STACK_TYPE=ble_full` +`./fbt updater_package COMPACT=1 DEBUG=0 COPRO_OB_DATA=scripts/ob_custradio.data COPRO_STACK_BIN=stm32wb5x_BLE_Stack_full_fw.bin COPRO_STACK_TYPE=ble_full` -Note that `COPRO_OB_DATA` must point to a valid file in `scripts` folder containing reference Option Byte data matching to your radio stack type. +Note that `COPRO_OB_DATA` must point to a valid file in the `scripts` folder containing reference Option Byte data matching your radio stack type. In certain cases, you might have to confirm your intentions by adding `COPRO_DISCLAIMER=...` to the build command line. - ## Building partial update packages -You can customize package contents by calling `scripts/update.py` directly. +You can customize package contents by calling `scripts/update.py` directly. For example, to build a package only for installing BLE FULL stack: ```shell scripts/update.py generate \ - -t f7 -d r13.3_full -v "BLE FULL 13.3" \ - --stage dist/f7/flipper-z-f7-updater-*.bin \ - --radio lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x/stm32wb5x_BLE_Stack_full_fw.bin \ - --radiotype ble_full + -t f7 -d r13.3_full -v "BLE FULL 13.3" \ + --stage dist/f7/flipper-z-f7-updater-*.bin \ + --radio lib/stm32wb_copro/firmware/stm32wb5x_BLE_Stack_full_fw.bin \ + --radiotype ble_full ``` -For full list of options, check `scripts/update.py generate` help. +For the full list of options, check `scripts/update.py generate` help. diff --git a/documentation/UnitTests.md b/documentation/UnitTests.md new file mode 100644 index 00000000000..9352917cdc2 --- /dev/null +++ b/documentation/UnitTests.md @@ -0,0 +1,64 @@ +# Unit tests + +## Intro + +Unit tests are special pieces of code that apply known inputs to the feature code and check the results to see if they are correct. +They are crucial for writing robust, bug-free code. + +Flipper Zero firmware includes a separate application called [unit_tests](/applications/debug/unit_tests). +It is run directly on Flipper devices in order to employ their hardware features and rule out any platform-related differences. + +When contributing code to the Flipper Zero firmware, it is highly desirable to supply unit tests along with the proposed features. +Running existing unit tests is useful to ensure that the new code doesn't introduce any regressions. + +## Running unit tests + +To run the unit tests, follow these steps: + +1. Compile the firmware with the tests enabled: `./fbt FIRMWARE_APP_SET=unit_tests updater_package`. +2. Flash the firmware using your preferred method, including SD card resources (`build/latest/resources`). +3. Launch the CLI session and run the `unit_tests` command. + +**NOTE:** To run a particular test (and skip all others), specify its name as the command argument. +See [test_index.c](/applications/debug/unit_tests/test_index.c) for the complete list of test names. + +## Adding unit tests + +### General + +#### Entry point + +The common entry point for all tests is the [unit_tests](/applications/debug/unit_tests) application. Test-specific code is placed into an arbitrarily named subdirectory and is then called from the [test_index.c](/applications/debug/unit_tests/test_index.c) source file. + +#### Test assets + +Some unit tests require external data in order to function. These files (commonly called assets) reside in the [unit_tests](/applications/debug/unit_tests/resources/unit_tests) directory in their respective subdirectories. Asset files can be of any type (plain text, FlipperFormat (FFF), binary, etc.). + +### Application-specific + +#### Infrared + +Each infrared protocol has a corresponding set of unit tests, so it makes sense to implement one when adding support for a new protocol. +To add unit tests for your protocol, follow these steps: + +1. Create a file named `test_.irtest` in the [assets](/applications/debug/unit_tests/resources/unit_tests/infrared) directory. +2. Fill it with the test data (more on it below). +3. Add the test code to [infrared_test.c](/applications/debug/unit_tests/infrared/infrared_test.c). +4. Build and install firmware with resources, install it on your Flipper and run the tests to see if they pass. + +##### Test data format + +Each unit test has three sections: + +1. `decoder` - takes in a raw signal and outputs decoded messages. +2. `encoder` - takes in decoded messages and outputs a raw signal. +3. `encoder_decoder` - takes in decoded messages, turns them into a raw signal, and then decodes again. + +Infrared test asset files have an `.irtest` extension and are regular `.ir` files with a few additions. +Decoder input data has signal names `decoder_input_N`, where N is a test sequence number. Expected data goes under the name `decoder_expected_N`. When testing the encoder, these two are switched. + +Decoded data is represented in arrays (since a single raw signal may be decoded into several messages). If there is only one signal, then it has to be an array of size 1. Use the existing files as syntax examples. + +##### Getting raw signals + +Recording raw IR signals are possible using the Flipper Zero. Launch the CLI session, run `ir rx raw`, then point the remote towards Flipper's receiver and send the signals. The raw signal data will be printed to the console in a convenient format. diff --git a/documentation/UniversalRemotes.md b/documentation/UniversalRemotes.md new file mode 100644 index 00000000000..213709afbfe --- /dev/null +++ b/documentation/UniversalRemotes.md @@ -0,0 +1,76 @@ +# 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](/applications/main/infrared/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](/applications/main/infrared/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](/applications/main/infrared/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/documentation/fbt.md b/documentation/fbt.md index 53fc4b5e39c..02de2949fa8 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -3,101 +3,126 @@ FBT is the entry point for firmware-related commands and utilities. It is invoked by `./fbt` in the firmware project root directory. Internally, it is a wrapper around [scons](https://scons.org/) build system. -## Requirements +If you don't need all features of `fbt` - like building the whole firmware - and only want to build and debug a single application, you can use [ufbt](https://pypi.org/project/ufbt/). -Please install Python packages required by assets build scripts: `pip3 install -r scripts/requirements.txt` -Make sure that `gcc-arm-none-eabi` toolchain & OpenOCD executables are in system's PATH. +## Environment -## NB +To use `fbt`, you only need `git` installed in your system. + +`fbt` by default downloads and unpacks a pre-built toolchain, and then modifies environment variables for itself to use it. It does not contaminate your global system's path with the toolchain. + > However, if you wish to use tools supplied with the toolchain outside `fbt`, you can open an *fbt shell*, with properly configured environment. + > - On Windows, simply run `scripts/toolchain/fbtenv.cmd`. + > - On Linux & MacOS, run `source scripts/toolchain/fbtenv.sh` in a new shell. + > - You can also type ```. `./fbt -s env` ``` in your shell. (Keep the "." at the beginning.) + + If your system is not supported by pre-built toolchain variants or you want to use custom versions of dependencies, you can `set FBT_NOENV=1`. `fbt` will skip toolchain & environment configuration and will expect all tools to be available on your system's `PATH`. *(this option is not available on Windows)* + + If `FBT_TOOLCHAIN_PATH` variable is set, `fbt` will use that directory to unpack toolchain into. By default, it downloads toolchain into `toolchain` subdirectory repo's root. + +If you want to enable extra debug output for `fbt` and toolchain management scripts, you can `set FBT_VERBOSE=1`. + +`fbt` always performs `git submodule update --init` on start, unless you set `FBT_NO_SYNC=1` in the environment: + - On Windows, it's `set "FBT_NO_SYNC=1"` in the shell you're running `fbt` from + - On \*nix, it's `$ FBT_NO_SYNC=1 ./fbt ...` + + > There are more variables controlling basic `fbt` behavior. See `fbt` & `fbtenv` scripts' sources for details. -* `fbt` constructs all referenced environments & their targets' dependency trees on startup. So, to keep startup time as low as possible, we're hiding construction of certain targets behind command-line options. -* `fbt` always performs `git submodule update --init` on start, unless you set `FBT_NO_SYNC=1` in environment: - * On Windows, that's `set "FBT_NO_SYNC=1"` in the shell you're running `fbt` from - * On \*nix, it's `$ FBT_NO_SYNC=1 ./fbt ...` -* `fbt` builds updater & firmware in separate subdirectories in `build`, with their names depending on optimization settings (`COMPACT` & `DEBUG` options). However, for ease of integration with IDEs, latest built variant's directory is always linked as `built/latest`. Additionally, `compile_commands.json` is generated in that folder, which is used for code completion support in IDE. ## Invoking FBT -To build with FBT, call it specifying configuration options & targets to build. For example, +To build with FBT, call it and specify configuration options & targets to build. For example: `./fbt COMPACT=1 DEBUG=0 VERBOSE=1 updater_package copro_dist` -To run cleanup (think of `make clean`) for specified targets, add `-c` option. +To run cleanup (think of `make clean`) for specified targets, add the `-c` option. + +## Build directories + +`fbt` builds updater & firmware in separate subdirectories in `build`, and their names depend on optimization settings (`COMPACT` & `DEBUG` options). However, for ease of integration with IDEs, the latest built variant's directory is always linked as `built/latest`. Additionally, `compile_commands.json` is generated in that folder (it is used for code completion support in IDEs). + +`build/latest` symlink & compilation database are only updated upon *firmware build targets* - that is, when you're re-building the firmware itself. Running other tasks, like firmware flashing or building update bundles *for a different debug/release configuration or hardware target*, does not update `built/latest` dir to point to that configuration. ## VSCode integration -`fbt` includes basic development environment configuration for VSCode. To deploy it, run `./fbt vscode_dist`. That will copy initial environment configuration to `.vscode` folder. After that, you can use that configuration by starting VSCode and choosing firmware root folder in "File > Open Folder" menu. +`fbt` includes basic development environment configuration for VS Code. Run `./fbt vscode_dist` to deploy it. That will copy the initial environment configuration to the `.vscode` folder. After that, you can use that configuration by starting VS Code and choosing the firmware root folder in the "File > Open Folder" menu. - * On first start, you'll be prompted to install recommended plug-ins. Please install them for best development experience. _You can find a list of them in `.vscode/extensions.json`._ - * Basic build tasks are invoked in Ctrl+Shift+B menu. - * Debugging requires a supported probe. That includes: - * Wi-Fi devboard with stock firmware (blackmagic), - * ST-Link and compatible devices, - * J-Link for flashing and debugging (in VSCode only). _Note that J-Link tools are not included with our toolchain and you have to [download](https://www.segger.com/downloads/jlink/) them yourself and put on your system's PATH._ - * Without a supported probe, you can install firmware on Flipper using USB installation method. +To use language servers other than the default VS Code C/C++ language server, use `./fbt vscode_dist LANG_SERVER=` instead. Currently `fbt` supports the default language server (`cpptools`) and `clangd`. +- On the first start, you'll be prompted to install recommended plugins. We highly recommend installing them for the best development experience. _You can find a list of them in `.vscode/extensions.json`._ +- Basic build tasks are invoked in the Ctrl+Shift+B menu. +- Debugging requires a supported probe. That includes: + - Wi-Fi devboard with stock firmware (blackmagic). + - ST-Link and compatible devices. + - J-Link for flashing and debugging (in VSCode only). _Note that J-Link tools are not included with our toolchain and you have to [download](https://www.segger.com/downloads/jlink/) them yourself and put them on your system's PATH._ +- Without a supported probe, you can install firmware on Flipper using the USB installation method. ## FBT targets -FBT keeps track of internal dependencies, so you only need to build the highest-level target you need, and FBT will make sure everything they depend on is up-to-date. +**`fbt`** keeps track of internal dependencies, so you only need to build the highest-level target you need, and **`fbt`** will make sure everything they depend on is up-to-date. ### High-level (what you most likely need) - -- `fw_dist` - build & publish firmware to `dist` folder. This is a default target, when no other are specified -- `updater_package`, `updater_minpackage` - build self-update package. Minimal version only inclues firmware's DFU file; full version also includes radio stack & resources for SD card -- `copro_dist` - bundle Core2 FUS+stack binaries for qFlipper -- `flash` - flash attached device with OpenOCD over ST-Link -- `flash_usb`, `flash_usb_full` - build, upload and install update package to device over USB. See details on `updater_package`, `updater_minpackage` -- `debug` - build and flash firmware, then attach with gdb with firmware's .elf loaded -- `debug_other` - attach gdb without loading any .elf. Allows to manually add external elf files with `add-symbol-file` in gdb -- `updater_debug` - attach gdb with updater's .elf loaded -- `blackmagic` - debug firmware with Blackmagic probe (WiFi dev board) -- `openocd` - just start OpenOCD -- `get_blackmagic` - output blackmagic address in gdb remote format. Useful for IDE integration -- `lint`, `format` - run clang-tidy on C source code to check and reformat it according to `.clang-format` specs -- `lint_py`, `format_py` - run [black](https://black.readthedocs.io/en/stable/index.html) on Python source code, build system files & application manifests + +- `fw_dist` - build & publish firmware to the `dist` folder. This is a default target when no others are specified. +- `fap_dist` - build external plugins & publish to the `dist` folder. +- `updater_package`, `updater_minpackage` - build a self-update package. The minimal version only includes the firmware's DFU file; the full version also includes a radio stack & resources for the SD card. +- `copro_dist` - bundle Core2 FUS+stack binaries for qFlipper. +- `flash` - flash the attached device over SWD interface with supported probes. Probe is detected automatically; you can override it with `SWD_TRANSPORT=...` variable. If multiple probes are attached, you can specify the serial number of the probe to use with `SWD_TRANSPORT_SERIAL=...`. +- `flash_usb`, `flash_usb_full` - build, upload and install the update package to the device over USB. See details on `updater_package` and `updater_minpackage`. +- `debug` - build and flash firmware, then attach with gdb with firmware's .elf loaded. +- `debug_other`, `debug_other_blackmagic` - attach GDB without loading any `.elf`. It will allow you to manually add external `.elf` files with `add-symbol-file` in GDB. +- `updater_debug` - attach GDB with the updater's `.elf` loaded. +- `devboard_flash` - update WiFi dev board with the latest firmware. +- `blackmagic` - debug firmware with Blackmagic probe (WiFi dev board). +- `openocd` - just start OpenOCD. +- `get_blackmagic` - output the blackmagic address in the GDB remote format. Useful for IDE integration. +- `get_stlink` - output serial numbers for attached STLink probes. Used for specifying an adapter with `SWD_TRANSPORT_SERIAL=...`. +- `lint`, `format` - run clang-format on the C source code to check and reformat it according to the `.clang-format` specs. +- `lint_py`, `format_py` - run [black](https://black.readthedocs.io/en/stable/index.html) on the Python source code, build system files & application manifests. +- `firmware_pvs` - generate a PVS Studio report for the firmware. Requires PVS Studio to be available on your system's `PATH`. +- `cli` - start a Flipper CLI session over USB. ### Firmware targets -- `firmware_extapps` - build all plug-ins as separate .elf files - - `firmware_snake_game`, etc - build single plug-in as .elf by its name - - Check out `--extra-ext-apps` for force adding extra apps to external build - - `firmware_snake_game_list`, etc - generate source + assembler listing for app's .elf -- `flash`, `firmware_flash` - flash current version to attached device with OpenOCD over ST-Link -- `jflash` - flash current version to attached device with JFlash using J-Link probe. JFlash executable must be on your $PATH -- `flash_blackmagic` - flash current version to attached device with Blackmagic probe -- `firmware_all`, `updater_all` - build basic set of binaries -- `firmware_list`, `updater_list` - generate source + assembler listing -- `firmware_cdb`, `updater_cdb` - generate `compilation_database.json` file for external tools and IDEs. It can be created without actually building the firmware. +- `faps` - build all external & plugin apps as [`.faps`](./AppsOnSDCard.md#fap-flipper-application-package). +- **`fbt`** also defines per-app targets. For example, for an app with `appid=snake_game` target names are: + - `fap_snake_game`, etc. - build single app as `.fap` by its application ID. + - Check out [`--extra-ext-apps`](#command-line-parameters) for force adding extra apps to external build. + - `fap_snake_game_list`, etc - generate source + assembler listing for app's `.fap`. +- `flash`, `firmware_flash` - flash the current version to the attached device over SWD. +- `jflash` - flash the current version to the attached device with JFlash using a J-Link probe. The JFlash executable must be on your `$PATH`. +- `firmware_all`, `updater_all` - build a basic set of binaries. +- `firmware_list`, `updater_list` - generate source + assembler listing. +- `firmware_cdb`, `updater_cdb` - generate a `compilation_database.json` file for external tools and IDEs. It can be created without actually building the firmware. ### Assets -- `resources` - build resources and their Manifest - - `dolphin_ext` - process dolphin animations for SD card -- `icons` - generate .c+.h for icons from png assets -- `proto` - generate .pb.c+.pb.h for .proto sources -- `proto_ver` - generate .h with protobuf version -- `dolphin_internal`, `dolphin_blocking` - generate .c+.h for corresponding dolphin assets - +- `resources` - build resources and their manifest files + - `dolphin_ext` - process dolphin animations for the SD card +- `icons` - generate `.c+.h` for icons from PNG assets +- `proto` - generate `.pb.c+.pb.h` for `.proto` sources +- `proto_ver` - generate `.h` with a protobuf version +- `dolphin_internal`, `dolphin_blocking` - generate `.c+.h` for corresponding dolphin assets ## Command-line parameters -- `--options optionfile.py` (default value `fbt_options.py`) - load file with multiple configuration values -- `--with-updater` - enables updater-related targets and dependency tracking. Enabling this option introduces extra startup time costs, so use it when bundling update packages. _Explicily enabling this should no longer be required, fbt now has specific handling for updater-related targets_ -- `--extra-int-apps=app1,app2,appN` - forces listed apps to be built as internal with `firmware` target -- `--extra-ext-apps=app1,app2,appN` - forces listed apps to be built as external with `firmware_extapps` target +- `--options optionfile.py` (default value `fbt_options.py`) - load a file with multiple configuration values +- `--extra-int-apps=app1,app2,appN` - force listed apps to be built as internal with the `firmware` target +- `--extra-ext-apps=app1,app2,appN` - force listed apps to be built as external with the `firmware_extapps` target +- `--extra-define=A --extra-define=B=C ` - extra global defines that will be passed to the C/C++ compiler, can be specified multiple times +- `--proxy-env=VAR1,VAR2` - additional environment variables to expose to subprocesses spawned by `fbt`. By default, `fbt` sanitizes the execution environment and doesn't forward all inherited environment variables. You can find the list of variables that are always forwarded in the `environ.scons` file. +## Configuration -## Configuration +Default configuration variables are set in the configuration file: `fbt_options.py`. +Values set in the command line have higher precedence over the configuration file. -Default configuration variables are set in the configuration file `fbt_options.py`. -Values set on command-line have higher precedence over configuration file. +You can also create a file called `fbt_options_local.py` that will be evaluated when loading default options file, enabling persisent overriding of default options without modifying default configuration. You can find out available options with `./fbt -h`. ### Firmware application set -You can create customized firmware builds by modifying the application list to be included in the build. Application presets are configured with the `FIRMWARE_APPS` option, which is a map(configuration_name:str -> application_list:tuple(str)). To specify application set to use in a build, set `FIRMWARE_APP_SET` to its name. +You can create customized firmware builds by modifying the list of applications to be included in the build. Application presets are configured with the `FIRMWARE_APPS` option, which is a `map(configuration_name:str -> application_list:tuple(str))`. To specify an application set to use in the build, set `FIRMWARE_APP_SET` to its name. For example, to build a firmware image with unit tests, run `./fbt FIRMWARE_APP_SET=unit_tests`. Check out `fbt_options.py` for details. diff --git a/documentation/file_formats/BadUsbScriptFormat.md b/documentation/file_formats/BadUsbScriptFormat.md new file mode 100644 index 00000000000..1eb8eb5180b --- /dev/null +++ b/documentation/file_formats/BadUsbScriptFormat.md @@ -0,0 +1,136 @@ +# Command syntax + +BadUsb app uses extended Duckyscript syntax. It is compatible with classic USB Rubber Ducky 1.0 scripts but provides some additional commands and features, such as custom USB ID, ALT+Numpad input method, SYSRQ command, and more functional keys. + +# Script file format + +BadUsb app can execute only text scripts from `.txt` files, no compilation is required. Both `\n` and `\r\n` line endings are supported. Empty lines are allowed. You can use spaces or tabs for line indentation. + +# Command set + +## Comment line + +Just a single comment line. The interpreter will ignore all text after the REM command. +| Command | Parameters | Notes | +| ------- | ------------ | ----- | +| REM | Comment text | | + +## Delay + +Pause script execution by a defined time. +| Command | Parameters | Notes | +| ------------- | ----------------- | ----------------------------------- | +| DELAY | Delay value in ms | Single delay | +| DEFAULT_DELAY | Delay value in ms | Add delay before every next command | +| DEFAULTDELAY | Delay value in ms | Same as DEFAULT_DELAY | + +## Special keys + +| Command | Notes | +| ------------------ | ---------------- | +| DOWNARROW / DOWN | | +| LEFTARROW / LEFT | | +| RIGHTARROW / RIGHT | | +| UPARROW / UP | | +| ENTER | | +| DELETE | | +| BACKSPACE | | +| END | | +| HOME | | +| ESCAPE / ESC | | +| INSERT | | +| PAGEUP | | +| PAGEDOWN | | +| CAPSLOCK | | +| NUMLOCK | | +| SCROLLLOCK | | +| PRINTSCREEN | | +| BREAK | Pause/Break key | +| PAUSE | Pause/Break key | +| SPACE | | +| TAB | | +| MENU | Context menu key | +| APP | Same as MENU | +| Fx | F1-F12 keys | + +## Modifier keys + +Can be combined with a special key command or a single character. +| Command | Notes | +| -------------- | ---------- | +| CONTROL / CTRL | | +| SHIFT | | +| ALT | | +| WINDOWS / GUI | | +| CTRL-ALT | CTRL+ALT | +| CTRL-SHIFT | CTRL+SHIFT | +| ALT-SHIFT | ALT+SHIFT | +| ALT-GUI | ALT+WIN | +| GUI-SHIFT | WIN+SHIFT | +| GUI-CTRL | WIN+CTRL | + +## Key hold and release + +Up to 5 keys can be hold simultaneously. +| Command | Parameters | Notes | +| ------- | ------------------------------- | ---------------------------------------- | +| HOLD | Special key or single character | Press and hold key until RELEASE command | +| RELEASE | Special key or single character | Release key | + +## Wait for button press + +Will wait indefinitely for a button to be pressed +| Command | Parameters | Notes | +| --------------------- | ------------ | --------------------------------------------------------------------- | +| WAIT_FOR_BUTTON_PRESS | None | Will wait for the user to press a button to continue script execution | + + +## String + +| Command | Parameters | Notes | +| ------- | ----------- | ----------------- | +| STRING | Text string | Print text string | +| STRINGLN | Text string | Print text string and press enter after it | + +## String delay + +Delay between keypresses. +| Command | Parameters | Notes | +| ------------ | ----------------- | --------------------------------------------- | +| STRING_DELAY | Delay value in ms | Applied once to next appearing STRING command | +| STRINGDELAY | Delay value in ms | Same as STRING_DELAY | + +## Repeat + +| Command | Parameters | Notes | +| ------- | ---------------------------- | ----------------------- | +| REPEAT | Number of additional repeats | Repeat previous command | + +## ALT+Numpad input + +On Windows and some Linux systems, you can print characters by holding `ALT` key and entering its code on Numpad. +| Command | Parameters | Notes | +| --------- | -------------- | --------------------------------------------------------------- | +| ALTCHAR | Character code | Print single character | +| ALTSTRING | Text string | Print text string using ALT+Numpad method | +| ALTCODE | Text string | Same as ALTSTRING, presents in some Duckyscript implementations | + +## SysRq + +Send [SysRq command](https://en.wikipedia.org/wiki/Magic_SysRq_key) +| Command | Parameters | Notes | +| ------- | ---------------- | ----- | +| SYSRQ | Single character | | + +## USB device ID + +You can set the custom ID of the Flipper USB HID device. ID command should be in the **first line** of script, it is executed before script run. + +| Command | Parameters | Notes | +| ------- | ---------------------------- | ----- | +| ID | VID:PID Manufacturer:Product | | + +Example: +`ID 1234:abcd Flipper Devices:Flipper Zero` + +VID and PID are hex codes and are mandatory. Manufacturer and Product are text strings and are optional. diff --git a/documentation/file_formats/InfraredFileFormats.md b/documentation/file_formats/InfraredFileFormats.md new file mode 100644 index 00000000000..4d43bd5b8e0 --- /dev/null +++ b/documentation/file_formats/InfraredFileFormats.md @@ -0,0 +1,144 @@ +# Infrared Flipper File Formats + + +## Supported protocols list for `type: parsed` +``` + NEC + NECext + NEC42 + NEC42ext + Samsung32 + RC6 + RC5 + RC5X + SIRC + SIRC15 + SIRC20 + Kaseikyo + RCA +``` +## Infrared Remote File Format + +### Example + + Filetype: IR signals file + Version: 1 + # + name: Button_1 + type: parsed + protocol: NECext + address: EE 87 00 00 + command: 5D A0 00 00 + # + name: Button_2 + type: raw + frequency: 38000 + duty_cycle: 0.330000 + data: 504 3432 502 483 500 484 510 502 502 482 501 485 509 1452 504 1458 509 1452 504 481 501 474 509 3420 503 + # + name: Button_3 + type: parsed + protocol: SIRC + address: 01 00 00 00 + command: 15 00 00 00 + +### Description + +Filename extension: `.ir` + +This file format is used to store an infrared remote that consists of an arbitrary number of buttons. +Each button is separated from others by a comment character (`#`) for better readability. + +Known protocols are represented in the `parsed` form, whereas non-recognized signals may be saved and re-transmitted as `raw` data. + +#### Version history: + +1. Initial version. + +#### Format fields + +| Name | Use | Type | Description | +| ---------- | ------ | ------ | --------------------------------------------------------------------------------------------------------------------------------------------- | +| name | both | string | Name of the button. Only printable ASCII characters are allowed. | +| type | both | string | Type of the signal. Must be `parsed` or `raw`. | +| protocol | parsed | string | Name of the infrared protocol. Refer to `ir` console command for the complete list of supported protocols. | +| address | parsed | hex | Payload address. Must be 4 bytes long. | +| command | parsed | hex | Payload command. Must be 4 bytes long. | +| frequency | raw | uint32 | Carrier frequency, in Hertz, usually 38000 Hz. | +| duty_cycle | raw | float | Carrier duty cycle, usually 0.33. | +| data | raw | uint32 | Raw signal timings, in microseconds between logic level changes. Individual elements must be space-separated. Maximum timings amount is 1024. | + +## Infrared Library File Format + +### Examples + +- [TV Universal Library](/applications/main/infrared/resources/infrared/assets/tv.ir) +- [A/C Universal Library](/applications/main/infrared/resources/infrared/assets/ac.ir) +- [Audio Universal Library](/applications/main/infrared/resources/infrared/assets/audio.ir) + +### Description + +Filename extension: `.ir` + +This file format is used to store universal remote libraries. It is identical to the previous format, differing only in the `Filetype` field.\ +It also has predefined button names for each universal library type, so that the universal remote application can understand them. +See [Universal Remotes](/documentation/UniversalRemotes.md) for more information. + +### Version history: + +1. Initial version. + +## Infrared Test File Format + +### Examples + +See [Infrared Unit Tests](/applications/debug/unit_tests/resources/unit_tests/infrared/) for various examples. + +### Description + +Filename extension: `.irtest` + +This file format is used to store technical test data that is too large to keep directly in the firmware. +It is mostly similar to the two previous formats, with the main difference being the addition of the parsed signal arrays. + +Each infrared protocol must have corresponding unit tests complete with an `.irtest` file. + +Known protocols are represented in the `parsed_array` form, whereas raw data has the `raw` type.\ +Note: a single parsed signal must be represented as an array of size 1. + +### Version history: + +1. Initial version. + +#### Format fields + +| Name | Use | Type | Description | +| ---------- | ------------ | ------ | ---------------------------------------------------------------- | +| name | both | string | Name of the signal. Only printable ASCII characters are allowed. | +| type | both | string | Type of the signal. Must be `parsed_array` or `raw`. | +| count | parsed_array | uint32 | The number of parsed signals in an array. Must be at least 1. | +| protocol | parsed_array | string | Same as in previous formats. | +| address | parsed_array | hex | Ditto. | +| command | parsed_array | hex | Ditto. | +| repeat | parsed_array | bool | Indicates whether the signal is a repeated button press. | +| frequency | raw | uint32 | Same as in previous formats. | +| duty_cycle | raw | float | Ditto. | +| data | raw | uint32 | Ditto. | + +#### Signal names + +The signal names in an `.irtest` file follow a convention ``, where the name is one of: + +- decoder_input +- decoder_expected +- encoder_decoder_input, + +and the number is a sequential integer: 1, 2, 3, etc., which produces names like `decoder_input1`, `encoder_decoder_input3`, and so on. + +| Name | Type | Description | +| --------------------- | ------------ | ----------------------------------------------------------------------------------------------------- | +| decoder_input | raw | A raw signal containing the decoder input. Also used as the expected encoder output. | +| decoder_expected | parsed_array | An array of parsed signals containing the expected decoder output. Also used as the encoder input. | +| encoder_decoder_input | parsed_array | An array of parsed signals containing both the encoder-decoder input and expected output. | + +See [Unit Tests](/documentation/UnitTests.md#infrared) for more info. diff --git a/documentation/file_formats/LfRfidFileFormat.md b/documentation/file_formats/LfRfidFileFormat.md new file mode 100644 index 00000000000..5143d8bc1e7 --- /dev/null +++ b/documentation/file_formats/LfRfidFileFormat.md @@ -0,0 +1,49 @@ +# LF RFID key file format + +## Example + +``` +Filetype: Flipper RFID key +Version: 1 +Key type: EM4100 +Data: 01 23 45 67 89 +``` + +## Description + +Filename extension: `.rfid` + +The file stores a single RFID key of the type defined by the `Key type` parameter. + +### Version history + +1. Initial version. + +### Format fields + +| Name | Description | +| -------- | --------------------- | +| Key type | Key protocol type | +| Data | Key data (HEX values) | + +### Supported key types + +| Type | Full name | +| ----------- | ----------------- | +| EM4100 | EM-Micro EM4100 | +| H10301 | HID H10301 | +| Idteck | IDTECK | +| Indala26 | Motorola Indala26 | +| IOProxXSF | Kantech IOProxXSF | +| AWID | AWID | +| FDX-A | FECAVA FDX-A | +| FDX-B | ISO FDX-B | +| HIDProx | Generic HIDProx | +| HIDExt | Generic HIDExt | +| Pyramid | Farpointe Pyramid | +| Viking | Viking | +| Jablotron | Jablotron | +| Paradox | Paradox | +| PAC/Stanley | PAC/Stanley | +| Keri | Keri | +| Gallagher | Gallagher | diff --git a/documentation/file_formats/NfcFileFormats.md b/documentation/file_formats/NfcFileFormats.md new file mode 100644 index 00000000000..f752cdb901b --- /dev/null +++ b/documentation/file_formats/NfcFileFormats.md @@ -0,0 +1,327 @@ +# NFC Flipper File Formats + +## UID + Header (General format) + +### Example + + Filetype: Flipper NFC device + Version: 4 + # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire + Device type: ISO14443-4A + # UID is common for all formats + UID: 04 48 6A 32 33 58 80 + ------------------------- + (Device-specific data) + +### Description + +This file format is used to store the device type and the UID of an NFC device. It does not store any internal data, so it is only used as a header for other formats. + +Version differences: + +1. Initial version, deprecated +2. LSB ATQA (e.g. 4400 instead of 0044) +3. MSB ATQA (current version) +4. Replace UID device type with ISO14443-3A + +## ISO14443-3A + + Filetype: Flipper NFC device + Version: 4 + # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire + Device type: ISO14443-3A + # UID is common for all formats + UID: 34 19 6D 41 14 56 E6 + # ISO14443-3A specific data + ATQA: 00 44 + SAK: 00 + +### Description + +This file format is used to store the UID, SAK and ATQA of a ISO14443-3A device. +UID must be either 4 or 7 bytes long. ATQA is 2 bytes long. SAK is 1 byte long. + +Version differences: +None, there are no versions yet. + +## ISO14443-3B + + Filetype: Flipper NFC device + Version: 4 + # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire + Device type: ISO14443-3B + # UID is common for all formats + UID: 30 1D B3 28 + # ISO14443-3B specific data + Application data: 00 12 34 FF + Protocol info: 11 81 E1 + +### Description + +This file format is used to store the UID, Application data and Protocol info of a ISO14443-3B device. +UID must be 4 bytes long. Application data is 4 bytes long. Protocol info is 3 bytes long. + +Version differences: +None, there are no versions yet. + +## ISO14443-4A + +### Example + + Filetype: Flipper NFC device + Version: 4 + # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire + Device type: ISO14443-4A + # UID is common for all formats + UID: 04 48 6A 32 33 58 80 + # ISO14443-3A specific data + ATQA: 03 44 + SAK: 20 + # ISO14443-4A specific data + ATS: 06 75 77 81 02 80 + +### Description + +This file format is used to store the UID, SAK and ATQA of a ISO14443-4A device. It also stores the Answer to Select (ATS) data of the card. +ATS must be no less than 5 bytes long. + +Version differences: +None, there are no versions yet. + +## NTAG/Ultralight + +### Example + + Filetype: Flipper NFC device + Version: 4 + # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire + Device type: NTAG/Ultralight + # UID is common for all formats + UID: 04 85 90 54 12 98 23 + # ISO14443-3A specific data + ATQA: 00 44 + SAK: 00 + # NTAG/Ultralight specific data + Data format version: 2 + NTAG/Ultralight type: NTAG216 + Signature: 1B 84 EB 70 BD 4C BD 1B 1D E4 98 0B 18 58 BD 7C 72 85 B4 E4 7B 38 8E 96 CF 88 6B EE A3 43 AD 90 + Mifare version: 00 04 04 02 01 00 13 03 + Counter 0: 0 + Tearing 0: 00 + Counter 1: 0 + Tearing 1: 00 + Counter 2: 0 + Tearing 2: 00 + Pages total: 231 + Pages read: 231 + Page 0: 04 85 92 9B + Page 1: 8A A0 61 81 + Page 2: CA 48 0F 00 + ... + Page 224: 00 00 00 00 + Page 225: 00 00 00 00 + Page 226: 00 00 7F BD + Page 227: 04 00 00 E2 + Page 228: 00 05 00 00 + Page 229: 00 00 00 00 + Page 230: 00 00 00 00 + Failed authentication attempts: 0 + +### Description + +This file format is used to store the UID, SAK and ATQA of a Mifare Ultralight/NTAG device. It also stores the internal data of the card, the signature, the version, and the counters. The data is stored in pages, just like on the card itself. + +The "NTAG/Ultralight type" field contains the concrete device type. It must be one of: Mifare Ultralight, Mifare Ultralight 11, Mifare Ultralight 21, NTAG203, NTAG213, NTAG215, NTAG216, NTAG I2C 1K, NTAG I2C 2K, NTAG I2C Plus 1K, NTAG I2C Plus 2K. + +The "Signature" field contains the reply of the tag to the READ_SIG command. More on that can be found here: (page 31) + +The "Mifare version" field is not related to the file format version but to the Mifare Ultralight version. It contains the response of the tag to the GET_VERSION command. More on that can be found here: (page 21) + +Other fields are the direct representation of the card's internal state. Learn more about them in the same datasheet. + +Version differences: + +1. Mifare Ultralight type is stored directly in Device type field +2. Current version, Mifare Ultralight type is stored in the same-named field + +## Mifare Classic + +### Example + + Filetype: Flipper NFC device + Version: 4 + # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire + Device type: Mifare Classic + # UID is common for all formats + UID: BA E2 7C 9D + # ISO14443-3A specific data + ATQA: 00 02 + SAK: 18 + # Mifare Classic specific data + Mifare Classic type: 4K + Data format version: 2 + # Mifare Classic blocks, '??' means unknown data + Block 0: BA E2 7C 9D B9 18 02 00 46 44 53 37 30 56 30 31 + Block 1: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 2: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 3: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF + Block 4: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 5: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 6: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 7: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF + ... + Block 238: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 239: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF + Block 240: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 241: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 242: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 243: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 244: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 245: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 246: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 247: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 248: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 249: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 251: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 252: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 253: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 254: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + Block 255: FF FF FF FF FF FF FF 07 80 69 FF FF FF FF FF FF + +### Description + +This file format is used to store the NFC-A and Mifare Classic specific data of a Mifare Classic card. Aside from the NFC-A data, it stores the card type (1K/4K) and the internal data of the card. The data is stored in blocks, there is no sector grouping. If the block's data is unknown, it is represented by '??'. Otherwise, the data is represented as a hex string. + +Version differences: + +1. Initial version, has Key A and Key B masks instead of marking unknown data with '??'. + +Example: + + ... + Data format version: 1 + # Key map is the bit mask indicating valid key in each sector + Key A map: 000000000000FFFF + Key B map: 000000000000FFFF + # Mifare Classic blocks + ... + +2. Current version + +## Mifare DESFire + +### Example + + Filetype: Flipper NFC device + Version: 4 + # Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, NTAG/Ultralight, Mifare Classic, Mifare DESFire + Device type: Mifare DESFire + # UID is common for all formats + UID: 04 2F 19 0A CD 66 80 + # ISO14443-3A specific data + ATQA: 03 44 + SAK: 20 + # ISO14443-4A specific data + ATS: 06 75 77 81 02 80 + # Mifare DESFire specific data + PICC Version: 04 01 01 12 00 1A 05 04 01 01 02 01 1A 05 04 2F 19 0A CD 66 80 CE ED D4 51 80 31 19 + PICC Free Memory: 7520 + PICC Change Key ID: 00 + PICC Config Changeable: true + PICC Free Create Delete: true + PICC Free Directory List: true + PICC Key Changeable: true + PICC Max Keys: 01 + PICC Key 0 Version: 00 + Application Count: 1 + Application IDs: 56 34 12 + Application 563412 Change Key ID: 00 + Application 563412 Config Changeable: true + Application 563412 Free Create Delete: true + Application 563412 Free Directory List: true + Application 563412 Key Changeable: true + Application 563412 Max Keys: 0E + Application 563412 Key 0 Version: 00 + Application 563412 Key 1 Version: 00 + Application 563412 Key 2 Version: 00 + Application 563412 Key 3 Version: 00 + Application 563412 Key 4 Version: 00 + Application 563412 Key 5 Version: 00 + Application 563412 Key 6 Version: 00 + Application 563412 Key 7 Version: 00 + Application 563412 Key 8 Version: 00 + Application 563412 Key 9 Version: 00 + Application 563412 Key 10 Version: 00 + Application 563412 Key 11 Version: 00 + Application 563412 Key 12 Version: 00 + Application 563412 Key 13 Version: 00 + Application 563412 File IDs: 01 + Application 563412 File 1 Type: 00 + Application 563412 File 1 Communication Settings: 00 + Application 563412 File 1 Access Rights: EE EE + Application 563412 File 1 Size: 256 + Application 563412 File 1: 13 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +### Description + +This file format is used to store the NFC-A and Mifare DESFire specific data of a Mifare DESFire card. Aside from the NFC-A data, it stores the card type (DESFire) and the internal data of the card. The data is stored per-application, and per-file. Here, the card was written using those pm3 commands: + + hf mfdes createapp --aid 123456 --fid 2345 --dfname astra + hf mfdes createfile --aid 123456 --fid 01 --isofid 0001 --size 000100 + hf mfdes write --aid 123456 --fid 01 -d 1337 + +Version differences: +None, there are no versions yet. + +## Mifare Classic Dictionary + +### Example + + # Key dictionary from https://github.com/ikarus23/MifareClassicTool.git + + # More well known keys! + # Standard keys + FFFFFFFFFFFF + A0A1A2A3A4A5 + D3F7D3F7D3F7 + 000000000000 + + # Keys from mfoc + B0B1B2B3B4B5 + 4D3A99C351DD + 1A982C7E459A + AABBCCDDEEFF + 714C5C886E97 + 587EE5F9350F + A0478CC39091 + 533CB6C723F6 + 8FD0A4F256E9 + ... + +### Description + +This file contains a list of Mifare Classic keys. Each key is represented as a hex string. Lines starting with '#' are ignored as comments. Blank lines are ignored as well. + +## EMV resources + +### Example + + Filetype: Flipper EMV resources + Version: 1 + # EMV currency code: currency name + 0997: USN + 0994: XSU + 0990: CLF + 0986: BRL + 0985: PLN + 0984: BOV + ... + +### Description + +This file stores a list of EMV currency codes, country codes, or AIDs and their names. Each line contains a hex value and a name separated by a colon and a space. + +Version differences: + +1. Initial version diff --git a/documentation/file_formats/SubGhzFileFormats.md b/documentation/file_formats/SubGhzFileFormats.md new file mode 100644 index 00000000000..c22f97f8df1 --- /dev/null +++ b/documentation/file_formats/SubGhzFileFormats.md @@ -0,0 +1,303 @@ +# File Formats for Flipper's SubGhz Subsystem + +## `.sub` File Format + +Flipper uses `.sub` files to store SubGhz transmissions. These are text files in Flipper File Format. `.sub` files can contain either a SubGhz Key with a certain protocol or SubGhz RAW data. + +A `.sub` files consist of 3 parts: + +- **header**, contains file type, version, and frequency +- **preset information**, preset type and, in case of a custom preset, transceiver configuration data +- **protocol and its data**, contains protocol name and its specific data, such as key, bit length, etc., or RAW data + +Flipper's SubGhz subsystem uses presets to configure the radio transceiver. Presets are used to configure modulation, bandwidth, filters, etc. There are several presets available in stock firmware, and there is a way to create custom presets. See [SubGhz Presets](#adding-a-custom-preset) for more details. + +## Header format + +Header is a mandatory part of `.sub` file. It contains file type, version, and frequency. + +| Field | Type | Description | +| ----------- | ------ | ----------------------------------------------------------------- | +| `Filetype` | string | Filetype of subghz file format, must be `Flipper SubGhz Key File` | +| `Version` | uint | Version of subghz file format, current version is 1 | +| `Frequency` | uint | Frequency in Hertz | + +## Preset information + +Preset information is a mandatory part for `.sub` files. It contains preset type and, in case of custom preset, transceiver configuration data. + +When using one of the standard presets, only `Preset` field is required. When using a custom preset, `Custom_preset_module` and `Custom_preset_data` fields are required. + +| Field | Description | +| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | +| `Preset` | Radio preset name (configures modulation, bandwidth, filters, etc.). When using a custom preset, must be `FuriHalSubGhzPresetCustom` | +| `Custom_preset_module` | Transceiver identifier, `CC1101` for Flipper Zero | +| `Custom_preset_data` | Transceiver configuration data | + +Built-in presets: + +- `FuriHalSubGhzPresetOok270Async` — On/Off Keying, 270kHz bandwidth, async(IO throw GP0) +- `FuriHalSubGhzPresetOok650Async` — On/Off Keying, 650kHz bandwidth, async(IO throw GP0) +- `FuriHalSubGhzPreset2FSKDev238Async` — 2 Frequency Shift Keying, deviation 2kHz, 270kHz bandwidth, async(IO throw GP0) +- `FuriHalSubGhzPreset2FSKDev476Async` — 2 Frequency Shift Keying, deviation 47kHz, 270kHz bandwidth, async(IO throw GP0) + +### Transceiver Configuration Data + +Transceiver configuration data is a string of bytes, encoded in hex format, separated by spaces. For CC1101 data structure is: `XX YY XX YY .. 00 00 ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ`, where: + +- **XX**, holds register address, +- **YY**, contains register value, +- **00 00**, marks register block end, +- **ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ**, 8 byte PA table (Power amplifier ramp table). + +You can find more details in the [CC1101 datasheet](https://www.ti.com/lit/ds/symlink/cc1101.pdf) and `furi_hal_subghz` code. + +## File Data + +`.sub` file data section contains either key data — protocol name and its specific data, bit length, etc., or RAW data — an array of signal timings, recorded without any protocol-specific processing. + +### Key Files + +`.sub` files with key data files contain protocol name and its specific data, such as key value, bit length, etc. +Check out the protocol registry for the full list of supported protocol names. + +Example of a key data block in Princeton format: + +``` +... +Protocol: Princeton +Bit: 24 +Key: 00 00 00 00 00 95 D5 D4 +TE: 400 +``` + +Protocol-specific fields in this example: + +| Field | Description | +| ----- | --------------------------------- | +| `Bit` | Princeton payload length, in bits | +| `Key` | Princeton payload data | +| `TE` | Princeton quantization interval | + +This file may contain additional fields, more details on available fields can be found in subghz protocols library. + +### RAW Files + +RAW `.sub` files contain raw signal data that is not processed through protocol-specific decoding. These files are useful for testing or sending data not supported by any known protocol. + +For RAW files, 2 fields are required: + +- **Protocol**, must be `RAW` +- **RAW_Data**, contains an array of timings, specified in microseconds Values must be non-zero, start with a positive number, and interleaved (change sign with each value). Up to 512 values per line. Can be specified multiple times to store multiple lines of data. + +Example of RAW data: + + Protocol: RAW + RAW_Data: 29262 361 -68 2635 -66 24113 -66 11 ... + +Long payload not fitting into internal memory buffer and consisting of short duration timings (< 10us) may not be read fast enough from the SD card. That might cause the signal transmission to stop before reaching the end of the payload. Ensure that your SD Card has good performance before transmitting long or complex RAW payloads. + +### BIN_RAW Files + +BinRAW `.sub` files and `RAW` files both contain data that has not been decoded by any protocol. However, unlike `RAW`, `BinRAW` files only record a useful repeating sequence of durations with a restored byte transfer rate and without broadcast noise. These files can emulate nearly all static protocols, whether Flipper knows them or not. + +- Usually, you have to receive the signal a little longer so that Flipper accumulates sufficient data for correct analysis. + +For `BinRAW` files, the following parameters are required and must be aligned to the left: + +- **Protocol**, must be `BinRAW`. +- **Bit**, is the length of the payload of the entire file, in bits (max 4096). +- **TE**, is the quantization interval, in us. +- **Bit_RAW**, is the length of the payload in the next Data_RAW parameter, in bits. +- **Data_RAW**, is an encoded sequence of durations, where each bit in the sequence encodes one TE interval: 1 - high level (there is a carrier), 0 - low (no carrier). + For example, TE=100, Bit_RAW=8, Data_RAW=0x37 => 0b00110111, that is, `-200 200 -100 300` will be transmitted. + When sending uploads, `Bit_RAW` and `Data_RAW` form a repeating block. Several such blocks are necessary if you want to send different sequences sequentially. However, usually, there will be only one block. + +Example data from a `BinRAW` file: + +``` +... +Protocol: BinRAW +Bit: 1572 +TE: 597 +Bit_RAW: 260 +Data_RAW: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0F 4A B5 55 4C B3 52 AC D5 2D 53 52 AD 4A D5 35 00 +Bit_RAW: 263 +Data_RAW: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 04 D5 32 D2 AB 2B 33 32 CB 2C CC B3 52 D3 00 +Bit_RAW: 259 +Data_RAW: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 4A AB 55 34 D5 2D 4C CD 33 4A CD 55 4C D2 B3 00 +Bit_RAW: 263 +Data_RAW: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0F 7F 4A AA D5 2A CC B2 B4 CB 34 CC AA AB 4D 53 53 00 +Bit_RAW: 264 +Data_RAW: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 FC 00 00 15 2C CB 34 D3 35 35 4D 4B 32 B2 D3 33 00 +Bit_RAW: 263 +Data_RAW: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 DE 02 D3 54 D5 4C D2 CC AD 4B 2C B2 B5 54 CC AB 00 +``` + +## File examples + +### Key file, standard preset + + Filetype: Flipper SubGhz Key File + Version: 1 + Frequency: 433920000 + Preset: FuriHalSubGhzPresetOok650Async + Protocol: Princeton + Bit: 24 + Key: 00 00 00 00 00 95 D5 D4 + TE: 400 + +### Key file, custom preset + + Filetype: Flipper SubGhz Key File + Version: 1 + Frequency: 433920000 + Preset: FuriHalSubGhzPresetCustom + 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 + Protocol: Princeton + Bit: 24 + Key: 00 00 00 00 00 95 D5 D4 + TE: 400 + +### RAW file, standard preset + + Filetype: Flipper SubGhz RAW File + Version: 1 + Frequency: 433920000 + Preset: FuriHalSubGhzPresetOok650Async + Protocol: RAW + RAW_Data: 29262 361 -68 2635 -66 24113 -66 11 ... + RAW_Data: -424 205 -412 159 -412 381 -240 181 ... + RAW_Data: -1448 361 -17056 131 -134 233 -1462 131 -166 953 -100 ... + +### RAW file, custom preset + + Filetype: Flipper SubGhz RAW File + Version: 1 + Frequency: 433920000 + Preset: FuriHalSubGhzPresetCustom + Custom_preset_module: CC1101 + Сustom_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 + Protocol: RAW + RAW_Data: 29262 361 -68 2635 -66 24113 -66 11 ... + RAW_Data: -424 205 -412 159 -412 381 -240 181 ... + RAW_Data: -1448 361 -17056 131 -134 233 -1462 131 -166 953 -100 ... + +# SubGhz configuration files + +SubGhz application provides support for adding extra radio presets and additional keys for decoding transmissions in certain protocols. + +## SubGhz `keeloq_mfcodes_user` file + +This file contains additional manufacturer keys for Keeloq protocol. It is used to decode Keeloq transmissions. +This file is loaded at subghz application start and is located at path `/ext/subghz/assets/keeloq_mfcodes_user`. + +### File format + +File contains a header and a list of manufacturer keys. + +File header format: + +| Field | Type | Description | +| ------------ | ------ | ------------------------------------------------------------------ | +| `Filetype` | string | SubGhz Keystore file format, always `Flipper SubGhz Keystore File` | +| `Version` | uint | File format version, 0 | +| `Encryption` | uint | File encryption: for user-provided file, set to 0 (disabled) | + +Following the header, file contains a list of user-provided manufacture keys, one key per line. +For each key, a name and encryption method must be specified, according to comment in file header. More information can be found in keeloq decoder source code. + +### Example + + # to use manual settings and prevent them from being deleted on upgrade, rename *_user.example files to *_user + # for adding manufacture keys + # AABBCCDDEEFFAABB:X:NAME + # AABBCCDDEEFFAABB - man 64 bit + # X - encryption method: + # - 0 - iterates over both previous and man in direct and reverse byte sequence + # - 1 - Simple Learning + # - 2 - Normal_Learning + # - 3 - Secure_Learning + # - 4 - Magic_xor_type1 Learning + # + # NAME - name (string without spaces) max 64 characters long + Filetype: Flipper SubGhz Keystore File + Version: 0 + Encryption: 0 + AABBCCDDEEFFAABB:1:Test1 + AABBCCDDEEFFAABB:1:Test2 + +## SubGhz `setting_user` file + +This file contains additional radio presets and frequencies for SubGhz application. It is used to add new presets and frequencies for existing presets. This file is being loaded on subghz application start and is located at path `/ext/subghz/assets/setting_user`. + +### File format + +File contains a header, basic options, and optional lists of presets and frequencies. + +Header must contain the following fields: + +- `Filetype`: SubGhz setting file format, must be `Flipper SubGhz Setting File`. +- `Version`: file format version, current is `1`. + +#### Basic settings + +- `Add_standard_frequencies`: bool, flag indicating whether to load standard frequencies shipped with firmware. If set to `false`, only frequencies specified in this file will be used. +- `Default_frequency`: uint, default frequency used in SubGhz application. + +#### Adding more frequencies + +- `Frequency`: uint — additional frequency for the subghz application frequency list. Used in Read and Read RAW. You can specify multiple frequencies, one per line. + +#### Adding more hopper frequencies + +- `Hopper_frequency`: uint — additional frequency for subghz application frequency hopping. Used in Frequency Analyzer. You can specify multiple frequencies, one per line. + +Repeating the same frequency will cause Flipper to listen to this frequency more often. + +#### Adding a Custom Preset + +You can have as many presets as you want. Presets are embedded into `.sub` files, so another Flipper can load them directly from that file. +Each preset is defined by the following fields: + +| Field | Description | +| ---------------------- | ------------------------------------------------------------------------------------------------------------------ | +| `Custom_preset_name` | string, preset name that will be shown in SubGHz application | +| `Custom_preset_module` | string, transceiver identifier. Set to `CC1101` for Flipper Zero | +| `Custom_preset_data` | transceiver configuration data. See [Transceiver Configuration Data](#transceiver-configuration-data) for details. | + +### Example + +``` +# 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: 310000000 +Frequency: 320000000 + +# Frequencies used for hopping mode (keep this list small or Flipper will miss the signal) +Hopper_frequency: 300000000 +Hopper_frequency: 310000000 +Hopper_frequency: 310000000 + +# 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 + +#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/documentation/file_formats/iButtonFileFormat.md b/documentation/file_formats/iButtonFileFormat.md new file mode 100644 index 00000000000..63743f06372 --- /dev/null +++ b/documentation/file_formats/iButtonFileFormat.md @@ -0,0 +1,51 @@ +# iButton key file format + +## Example + +``` +Filetype: Flipper iButton key +Version: 2 +Protocol: DS1992 +Rom Data: 08 DE AD BE EF FA CE 4E +Sram Data: 4E 65 76 65 72 47 6F 6E 6E 61 47 69 76 65 59 6F 75 55 70 4E 65 76 65 72 47 6F 6E 6E 61 4C 65 74 59 6F 75 44 6F 77 6E 4E 65 76 65 72 47 6F 6E 6E 61 52 75 6E 41 72 6F 75 6E 64 41 6E 64 44 65 73 65 72 74 59 6F 75 4E 65 76 65 72 47 6F 6E 6E 61 4D 61 6B 65 59 6F 75 43 72 79 4E 65 76 65 72 47 6F 6E 6E 61 53 61 79 47 6F 6F 64 62 79 65 4E 65 76 65 72 47 6F 6E 6E 61 54 65 6C 6C 41 4C 69 65 +``` + +## Description + +Filename extension: `.ibtn` + +The file stores a single iButton key, complete with all data required by the protocol. + +## Version history +### 2. Current version. +Changelog: +- Added support for different Dallas protocols +- Fields after `Protocol` are protocol-dependent for flexibiliy + +#### Format fields + +| Name | Type | Description | +| ----------- | ------ | -------------------------------------------- | +| Protocol | string | Currently supported: DS1990, DS1992, DS1996, DS1971, DSGeneric*, Cyfral, Metakom | +| Rom Data | hex | Read-only memory data (Dallas protocols only) | +| Sram Data | hex | Static RAM data (DS1992 and DS1996 only) +| Eeprom Data | hex | EEPROM data (DS1971 only) +| Data | hex | Key data (Cyfral & Metakom only) | + +NOTE 1: DSGeneric is a catch-all protocol for all unknown 1-Wire devices. It reads only the ROM and does not perform any checks on the read data. +It can also be used if a key with a deliberately invalid family code or checksum is required. + +NOTE 2: When adding new protocols, it is not necessarily to increase the format version, define the format in the protocol implementation instead. + +### 1. Initial version. +Deprecated, will be converted to current version upon saving. + +#### Format fields + +| Name | Type | Description | +| -------- | ------ | -------------------------------------------- | +| Key type | string | Currently supported: Cyfral, Dallas, Metakom | +| Data | hex | Key data | + + + diff --git a/fbt b/fbt index 981489dd19c..e6133d07b17 100755 --- a/fbt +++ b/fbt @@ -5,24 +5,38 @@ set -eu; # private variables +N_CORES="$(getconf _NPROCESSORS_ONLN)"; +N_GIT_THREADS="$(($N_CORES * 2))"; SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd -P)"; -SCONS_DEFAULT_FLAGS="-Q --warn=target-not-built"; +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:-""}"; +FBT_GIT_SUBMODULE_SHALLOW="${FBT_GIT_SUBMODULE_SHALLOW:-""}"; 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 - if [ ! -d "$SCRIPT_PATH/.git" ]; then - echo "\".git\" directory not found, please clone repo via \"git clone --recursive\""; + if [ ! -e "$SCRIPT_PATH/.git" ]; then + echo "\".git\" directory not found, please clone repo via \"git clone\""; exit 1; fi - git submodule update --init; + _FBT_CLONE_FLAGS="--jobs $N_GIT_THREADS"; + if [ ! -z "$FBT_GIT_SUBMODULE_SHALLOW" ]; then + _FBT_CLONE_FLAGS="$_FBT_CLONE_FLAGS --depth 1"; + fi + + git submodule update --init --recursive $_FBT_CLONE_FLAGS; fi -python3 "$SCRIPT_PATH/lib/scons/scripts/scons.py" $SCONS_DEFAULT_FLAGS "$@" +$SCONS_EP $SCONS_DEFAULT_FLAGS "$@" diff --git a/fbt.cmd b/fbt.cmd index f09b98382b2..20432be1c53 100644 --- a/fbt.cmd +++ b/fbt.cmd @@ -1,16 +1,29 @@ @echo off call "%~dp0scripts\toolchain\fbtenv.cmd" env -set SCONS_EP=%~dp0\lib\scons\scripts\scons.py +set SCONS_EP=python -m SCons if [%FBT_NO_SYNC%] == [] ( + set _FBT_CLONE_FLAGS=--jobs %NUMBER_OF_PROCESSORS% + if not [%FBT_GIT_SUBMODULE_SHALLOW%] == [] ( + set _FBT_CLONE_FLAGS=%_FBT_CLONE_FLAGS% --depth 1 + ) if exist ".git" ( - git submodule update --init + git submodule update --init --recursive %_FBT_CLONE_FLAGS% + if %ERRORLEVEL% neq 0 ( + echo Failed to update submodules, set FBT_NO_SYNC to skip + exit /b 1 + ) ) else ( - echo Not in a git repo, please clone with git clone --recursive + echo .git not found, please clone repo with "git clone" exit /b 1 ) ) -set "SCONS_DEFAULT_FLAGS=-Q --warn=target-not-built" -python lib\scons\scripts\scons.py %SCONS_DEFAULT_FLAGS% %* +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 b154d26aa46..277790a4038 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -1,7 +1,9 @@ +from pathlib import Path import posixpath # For more details on these options, run 'fbt -h' +FIRMWARE_ORIGIN = "Official" # Default hardware target TARGET_HW = 7 @@ -19,10 +21,10 @@ # Coprocessor firmware COPRO_OB_DATA = "scripts/ob.data" -# Must match lib/STM32CubeWB version -COPRO_CUBE_VERSION = "1.13.3" +# Must match lib/stm32wb_copro version +COPRO_CUBE_VERSION = "1.17.3" -COPRO_CUBE_DIR = "lib/STM32CubeWB" +COPRO_CUBE_DIR = "lib/stm32wb_copro" # Default radio stack COPRO_STACK_BIN = "stm32wb5x_BLE_Stack_light_fw.bin" @@ -32,13 +34,8 @@ # Leave 0 to let scripts automatically calculate it COPRO_STACK_ADDR = "0x0" -# If you override COPRO_CUBE_DIR on commandline, override this aswell -COPRO_STACK_BIN_DIR = posixpath.join( - COPRO_CUBE_DIR, - "Projects", - "STM32WB_Copro_Wireless_Binaries", - "STM32WB5x", -) +# If you override COPRO_CUBE_DIR on commandline, override this as well +COPRO_STACK_BIN_DIR = posixpath.join(COPRO_CUBE_DIR, "firmware") # Supported toolchain versions FBT_TOOLCHAIN_VERSIONS = (" 10.3.",) @@ -49,14 +46,12 @@ "-c", "transport select hla_swd", "-f", - "debug/stm32wbx.cfg", + "${FBT_DEBUG_DIR}/stm32wbx.cfg", "-c", "stm32wbx.cpu configure -rtos auto", - "-c", - "init", ] -SVD_FILE = "debug/STM32WB55_CM4.svd" +SVD_FILE = "${FBT_DEBUG_DIR}/STM32WB55_CM4.svd" # Look for blackmagic probe on serial ports and local network BLACKMAGIC = "auto" @@ -66,28 +61,25 @@ FIRMWARE_APPS = { "default": [ - "crypto_start", # Svc "basic_services", # Apps - "basic_apps", - "updater_app", - "storage_move_to_sd", - "archive", + "main_apps", + "system_apps", # Settings - "passport", - "system_settings", - "about", - # Plugins - "basic_plugins", - # Debug - "debug_apps", + "settings_apps", ], "unit_tests": [ "basic_services", "updater_app", + "radio_device_cc1101_ext", "unit_tests", ], } FIRMWARE_APP_SET = "default" + +custom_options_fn = "fbt_options_local.py" + +if Path(custom_options_fn).exists(): + exec(compile(Path(custom_options_fn).read_text(), custom_options_fn, "exec")) diff --git a/firmware.scons b/firmware.scons index 863b35fca9f..004def9a999 100644 --- a/firmware.scons +++ b/firmware.scons @@ -1,11 +1,12 @@ -Import("ENV", "fw_build_meta") +import itertools -import os +from fbt.sdk.cache import LazySdkVersionLoader +from fbt.version import get_git_commit_unix_timestamp +from fbt_extra.util import link_elf_dir_as_latest, should_gen_cdb_and_link_dir +from SCons.Errors import UserError +from SCons.Node import FS -from fbt.util import ( - should_gen_cdb_and_link_dir, - link_elf_dir_as_latest, -) +Import("ENV", "fw_build_meta") # Building initial C environment for libs env = ENV.Clone( @@ -13,37 +14,38 @@ env = ENV.Clone( ("compilation_db", {"COMPILATIONDB_COMSTR": "\tCDB\t${TARGET}"}), "fwbin", "fbt_apps", + "pvsstudio", + "fbt_hwtarget", + "fbt_envhooks", + "fbt_resources", ], COMPILATIONDB_USE_ABSPATH=False, BUILD_DIR=fw_build_meta["build_dir"], IS_BASE_FIRMWARE=fw_build_meta["type"] == "firmware", FW_FLAVOR=fw_build_meta["flavor"], - PLUGIN_ELF_DIR="${BUILD_DIR}", - LIB_DIST_DIR="${BUILD_DIR}/lib", + LIB_DIST_DIR=fw_build_meta["build_dir"].Dir("lib"), + RESOURCES_ROOT=fw_build_meta["build_dir"].Dir("resources"), + TARGETS_ROOT=Dir("#/targets"), LINT_SOURCES=[ - "applications", + Dir("applications"), ], LIBPATH=[ "${LIB_DIST_DIR}", ], CPPPATH=[ "#/furi", - "#/applications", - "#/firmware/targets/f${TARGET_HW}/ble_glue", - "#/firmware/targets/f${TARGET_HW}/fatfs", - "#/firmware/targets/f${TARGET_HW}/furi_hal", - "#/firmware/targets/f${TARGET_HW}/Inc", - "#/firmware/targets/furi_hal_include", + *(f"#/{app_dir[0]}" for app_dir in ENV["APPDIRS"] if app_dir[1]), + "#/targets/furi_hal_include", ], # Specific flags for building libraries - always do optimized builds FW_LIB_OPTS={ "Default": { "CCFLAGS": [ - "-Os", + "-Og" if ENV["LIB_DEBUG"] else "-Os", ], "CPPDEFINES": [ "NDEBUG", - "FURI_NDEBUG", + "FURI_DEBUG" if ENV["LIB_DEBUG"] else "FURI_NDEBUG", ], # You can add other entries named after libraries # If they are present, they have precedence over Default @@ -58,30 +60,23 @@ env = ENV.Clone( "FURI_DEBUG" if ENV["DEBUG"] else "FURI_NDEBUG", ], }, + "flipper_application": { + "CCFLAGS": [ + "-Og", + ], + "CPPDEFINES": [ + "NDEBUG", + "FURI_DEBUG" if ENV["DEBUG"] else "FURI_NDEBUG", + ], + }, }, + FW_API_TABLE=None, + _APP_ICONS=None, + APPS=_.split(",") if (_ := GetOption("extra_int_apps")) else [], + EXTRA_EXT_APPS=_.split(",") if (_ := GetOption("extra_ext_apps")) else [], ) - -def ApplyLibFlags(env): - flags_to_apply = env["FW_LIB_OPTS"].get( - env.get("FW_LIB_NAME"), - env["FW_LIB_OPTS"]["Default"], - ) - # print("Flags for ", env.get("FW_LIB_NAME", "Default"), flags_to_apply) - env.MergeFlags(flags_to_apply) - - -env.AddMethod(ApplyLibFlags) - -Export("env") - -if not env["VERBOSE"]: - env.SetDefault( - HEXCOMSTR="\tHEX\t${TARGET}", - BINCOMSTR="\tBIN\t${TARGET}", - DFUCOMSTR="\tDFU\t${TARGET}", - ) - +env.PreConfigureFwEnvionment() if env["IS_BASE_FIRMWARE"]: env.Append( @@ -96,20 +91,34 @@ else: "FURI_RAM_EXEC", ], ) +env.AppendUnique(CPPDEFINES=["FW_CFG_${FIRMWARE_APP_SET}"]) -# Invoke child SConscripts to populate global `env` + build their own part of the code +env.ConfigureForTarget(env.subst("${TARGET_HW}")) + +Export("env") + +# Invoke child SCopscripts to populate global `env` + build their own part of the code lib_targets = env.BuildModules( [ "lib", "assets", - "firmware", + "targets", "furi", ], ) +# Configure firmware origin definitions +env.Append( + CPPDEFINES=[ + env.subst("FW_ORIGIN_${FIRMWARE_ORIGIN}"), + ] +) + # Now, env is fully set up with everything to build apps -fwenv = env.Clone() +fwenv = env.Clone(FW_ARTIFACTS=[]) + +fw_artifacts = fwenv["FW_ARTIFACTS"] # Set up additional app-specific build flags SConscript("site_scons/firmwareopts.scons", exports={"ENV": fwenv}) @@ -120,18 +129,43 @@ if env["IS_BASE_FIRMWARE"]: else: fwenv.Append(APPS=["updater"]) -if extra_int_apps := GetOption("extra_int_apps"): - fwenv.Append(APPS=extra_int_apps.split(",")) -fwenv.LoadApplicationManifests() +for app_dir, _ in fwenv["APPDIRS"]: + app_dir_node = env.Dir("#").Dir(app_dir) + + for entry in app_dir_node.glob("*"): + if isinstance(entry, FS.Dir) and not str(entry).startswith("."): + fwenv.LoadAppManifest(entry) + fwenv.PrepareApplicationsBuild() -# Build external apps -extapps = SConscript("applications/extapps.scons", exports={"ENV": fwenv}) +# Build external apps + configure SDK +if env["IS_BASE_FIRMWARE"]: + fwenv.SetDefault(FBT_FAP_DEBUG_ELF_ROOT=fwenv["BUILD_DIR"].Dir(".extapps")) + fw_extapps = fwenv["FW_EXTAPPS"] = SConscript( + "site_scons/extapps.scons", + exports={"ENV": fwenv}, + ) + fw_artifacts.append(fw_extapps.sdk_tree) + # Resources & manifest for SD card + manifest = fwenv.ManifestBuilder( + "${RESOURCES_ROOT}/Manifest", + GIT_UNIX_TIMESTAMP=get_git_commit_unix_timestamp(), + _EXTRA_DIST=[fwenv["DOLPHIN_EXTERNAL_OUT_DIR"]], + ) + fwenv.Replace(FW_RESOURCES_MANIFEST=manifest) + fwenv.Alias("resources", manifest) + + # API getter + fwenv.Append(FBT_API_VERSION=LazySdkVersionLoader(fwenv.subst("$SDK_DEFINITION"))) + fwenv.PhonyTarget( + "get_apiversion", + "@echo $( ${FBT_API_VERSION} $)", + ) # Add preprocessor definitions for current set of apps -fwenv.AppendUnique( +fwenv.Append( CPPDEFINES=fwenv["APPBUILD"].get_apps_cdefs(), ) @@ -141,78 +175,53 @@ apps_c = fwenv.ApplicationsC( "applications/applications.c", [Value(fwenv["APPS"]), Value(fwenv["LOADER_AUTOSTART"])], ) + # Adding dependency on manifest files so apps.c is rebuilt when any manifest is changed -fwenv.Depends(apps_c, fwenv.GlobRecursive("*.fam", "#/applications")) +for app_dir, _ in env["APPDIRS"]: + app_dir_node = env.Dir("#").Dir(app_dir) + fwenv.Depends(apps_c, app_dir_node.glob("*/application.fam")) + +# Sanity check - certain external apps are using features that are not available in base firmware +if advanced_faps := list( + filter( + lambda app: app.fap_extbuild or app.fap_private_libs or app.fap_icon_assets, + fwenv["APPBUILD"].get_builtin_apps(), + ) +): + raise UserError( + "An Application that is using fap-specific features cannot be built into base firmware." + f" Offending app(s): {', '.join(app.appid for app in advanced_faps)}" + ) sources = [apps_c] # Gather sources only from app folders in current configuration -for app_folder in fwenv["APPBUILD"].get_builtin_app_folders(): - sources += fwenv.GlobRecursive("*.c*", os.path.join("applications", app_folder)) - - -fwenv.AppendUnique( - LINKFLAGS=[ - "-specs=nano.specs", - "-specs=nosys.specs", - "-Wl,--start-group", - "-lstdc++", - "-lsupc++", - "-Wl,--end-group", - "-Wl,--gc-sections", - "-Wl,--undefined=uxTopUsedPriority", - "-Wl,--wrap,_malloc_r", - "-Wl,--wrap,_free_r", - "-Wl,--wrap,_calloc_r", - "-Wl,--wrap,_realloc_r", - "-n", - "-Xlinker", - "-Map=${TARGET}.map", - ], +sources.extend( + itertools.chain.from_iterable( + fwenv.GatherSources([source_type, "!lib"], appdir.relpath) + for appdir, source_type in fwenv["APPBUILD"].get_builtin_app_folders() + ) ) # Debug # print(fwenv.Dump()) # Full firmware definition - fwelf = fwenv["FW_ELF"] = fwenv.Program( "${FIRMWARE_BUILD_CFG}", sources, - LIBS=[ - "print", - "flipper${TARGET_HW}", - "furi", - "freertos", - "stm32cubewb", - "hwdrivers", - "fatfs", - "littlefs", - "subghz", - "flipperformat", - "toolbox", - "nfc", - "microtar", - "usb_stm32", - "st25rfal002", - "infrared", - "appframe", - "assets", - "misc", - "mbedtls", - "loclass", - # 2nd round - "flipperformat", - "toolbox", - ], + LIBS=fwenv["TARGET_CFG"].linker_dependencies, ) # Firmware depends on everything child builders returned -Depends(fwelf, lib_targets) +# Depends(fwelf, lib_targets) # Output extra details after building firmware AddPostAction(fwelf, fwenv["APPBUILD_DUMP"]) AddPostAction( fwelf, - Action('${PYTHON3} "${ROOT_DIR}/scripts/fwsize.py" elf ${TARGET}', "Firmware size"), + Action( + [["${PYTHON3}", "${BIN_SIZE_SCRIPT}", "elf", "${TARGET}"]], + "Firmware size", + ), ) # Produce extra firmware files @@ -220,7 +229,7 @@ fwhex = fwenv["FW_HEX"] = fwenv.HEXBuilder("${FIRMWARE_BUILD_CFG}") fwbin = fwenv["FW_BIN"] = fwenv.BINBuilder("${FIRMWARE_BUILD_CFG}") AddPostAction( fwbin, - Action('@${PYTHON3} "${ROOT_DIR}/scripts/fwsize.py" bin ${TARGET}'), + Action([["@${PYTHON3}", "${BIN_SIZE_SCRIPT}", "bin", "${TARGET}"]]), ) fwdfu = fwenv["FW_DFU"] = fwenv.DFUBuilder("${FIRMWARE_BUILD_CFG}") @@ -230,21 +239,48 @@ fwdump = fwenv.ObjDump("${FIRMWARE_BUILD_CFG}") Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_list", fwdump) -fw_artifacts = fwenv["FW_ARTIFACTS"] = [ - fwhex, - fwbin, - fwdfu, - fwenv["FW_VERSION_JSON"], -] +fw_artifacts.extend( + [ + fwhex, + fwbin, + fwdfu, + fwenv["FW_VERSION_JSON"], + ] +) + + +fwcdb = fwenv.CompilationDatabase() +# without filtering, both updater & firmware commands would be generated in same file +fwenv.Replace( + COMPILATIONDB_PATH_FILTER=fwenv.subst("*${FW_FLAVOR}*"), + COMPILATIONDB_SRCPATH_FILTER="*.c*", +) +AlwaysBuild(fwcdb) +Precious(fwcdb) +NoClean(fwcdb) +Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_cdb", fwcdb) + +pvscheck = fwenv.PVSCheck("pvsreport.log", fwcdb) +Depends( + pvscheck, + [ + fwenv["FW_VERSION_JSON"], + fwenv["FW_ASSETS_HEADERS"], + fwenv["FW_API_TABLE"], + fwenv["_APP_ICONS"], + ], +) +Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_pvscheck", pvscheck) +AlwaysBuild(pvscheck) +Precious(pvscheck) + +pvsreport = fwenv.PVSReport(None, pvscheck, REPORT_DIR=Dir("pvsreport")) +Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_pvs", pvsreport) +AlwaysBuild(pvsreport) # If current configuration was explicitly requested, generate compilation database # and link its directory as build/latest if should_gen_cdb_and_link_dir(fwenv, BUILD_TARGETS): - fwcdb = fwenv.CompilationDatabase() - # without filtering, both updater & firmware commands would be generated - fwenv.Replace(COMPILATIONDB_PATH_FILTER=fwenv.subst("*${FW_FLAVOR}*")) - AlwaysBuild(fwcdb) - Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_cdb", fwcdb) fw_artifacts.append(fwcdb) # Adding as a phony target, so folder link is updated even if elf didn't change @@ -260,5 +296,6 @@ if should_gen_cdb_and_link_dir(fwenv, BUILD_TARGETS): Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_all", fw_artifacts) +env.PostConfigureFwEnvionment() Return("fwenv") diff --git a/firmware/ReadMe.md b/firmware/ReadMe.md deleted file mode 100644 index d2baedfd150..00000000000 --- a/firmware/ReadMe.md +++ /dev/null @@ -1,22 +0,0 @@ -# Flipper firmware - -What does it do? - -- [x] RTOS -- [x] FuriHAL -- [x] FuriCore -- [x] Services -- [x] Applications - -# Targets - -| Name | Firmware Address | Reset Combo | DFU Combo | -|-----------|-------------------|-----------------------|-----------------------| -| f7 | 0x08000000 | L+Back, release both | L+Back, release Back | - -Also there is a "hardware" ST bootloader combo available even on a bricked or empty device: L+Ok+Back, release Back, Left. -Target independent code and headers in `target/include` folders. More details in `documentation/KeyCombo.md` - -# Building - -Check out `documentation/fbt.md` on how to build and flash firmware. \ No newline at end of file diff --git a/firmware/SConscript b/firmware/SConscript deleted file mode 100644 index 8dade34e11b..00000000000 --- a/firmware/SConscript +++ /dev/null @@ -1,14 +0,0 @@ -Import("env") - -env.Append(LINT_SOURCES=["firmware"]) - -libenv = env.Clone(FW_LIB_NAME="flipper${TARGET_HW}") -libenv.ApplyLibFlags() - - -sources = ["targets/f${TARGET_HW}/startup_stm32wb55xx_cm4.s"] -sources += libenv.GlobRecursive("*.c") - -lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) -libenv.Install("${LIB_DIST_DIR}", lib) -Return("lib") diff --git a/firmware/targets/f7/Inc/stm32.h b/firmware/targets/f7/Inc/stm32.h deleted file mode 100644 index 83dda96e27d..00000000000 --- a/firmware/targets/f7/Inc/stm32.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef _STM32_H_ -#define _STM32_H_ - -/* modify bitfield */ -#define _BMD(reg, msk, val) (reg) = (((reg) & ~(msk)) | (val)) -/* set bitfield */ -#define _BST(reg, bits) (reg) = ((reg) | (bits)) -/* clear bitfield */ -#define _BCL(reg, bits) (reg) = ((reg) & ~(bits)) -/* wait until bitfield set */ -#define _WBS(reg, bits) while(((reg) & (bits)) == 0) -/* wait until bitfield clear */ -#define _WBC(reg, bits) while(((reg) & (bits)) != 0) -/* wait for bitfield value */ -#define _WVL(reg, msk, val) while(((reg) & (msk)) != (val)) -/* bit value */ -#define _BV(bit) (0x01 << (bit)) - -#if defined(STM32F0) -#include "STM32F0xx/Include/stm32f0xx.h" -#elif defined(STM32F1) -#include "STM32F1xx/Include/stm32f1xx.h" -#elif defined(STM32F2) -#include "STM32F2xx/Include/stm32f2xx.h" -#elif defined(STM32F3) -#include "STM32F3xx/Include/stm32f3xx.h" -#elif defined(STM32F4) -#include "STM32F4xx/Include/stm32f4xx.h" -#elif defined(STM32F7) -#include "STM32F7xx/Include/stm32f7xx.h" -#elif defined(STM32H7) -#include "STM32H7xx/Include/stm32h7xx.h" -#elif defined(STM32L0) -#include "STM32L0xx/Include/stm32l0xx.h" -#elif defined(STM32L1) -#include "STM32L1xx/Include/stm32l1xx.h" -#elif defined(STM32L4) -#include "STM32L4xx/Include/stm32l4xx.h" -#elif defined(STM32L5) -#include "STM32L5xx/Include/stm32l5xx.h" -#elif defined(STM32G0) -#include "STM32G0xx/Include/stm32g0xx.h" -#elif defined(STM32G4) -#include "STM32G4xx/Include/stm32g4xx.h" -#elif defined(STM32WB) -#include "STM32WBxx/Include/stm32wbxx.h" -#else -#error "STM32 family not defined" -#endif - -#endif // _STM32_H_ diff --git a/firmware/targets/f7/Src/dfu.c b/firmware/targets/f7/Src/dfu.c deleted file mode 100644 index f32ac2ac48d..00000000000 --- a/firmware/targets/f7/Src/dfu.c +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include -#include -#include -#include -#include - -void flipper_boot_dfu_show_splash() { - // Initialize - furi_hal_compress_icon_init(); - - u8g2_t* fb = malloc(sizeof(u8g2_t)); - memset(fb, 0, sizeof(u8g2_t)); - u8g2_Setup_st756x_flipper(fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); - u8g2_InitDisplay(fb); - u8g2_SetDrawColor(fb, 0x01); - uint8_t* splash_data = NULL; - furi_hal_compress_icon_decode(icon_get_data(&I_DFU_128x50), &splash_data); - u8g2_DrawXBM(fb, 0, 64 - 50, 128, 50, splash_data); - u8g2_SetFont(fb, u8g2_font_helvB08_tr); - u8g2_DrawStr(fb, 2, 8, "Update & Recovery Mode"); - u8g2_DrawStr(fb, 2, 21, "DFU Started"); - u8g2_SetPowerSave(fb, 0); - u8g2_SendBuffer(fb); -} - -void flipper_boot_dfu_exec() { - // Show DFU splashscreen - flipper_boot_dfu_show_splash(); - - // Errata 2.2.9, Flash OPTVERR flag is always set after system reset - WRITE_REG(FLASH->SR, FLASH_SR_OPTVERR); - - // Cleanup before jumping to DFU - furi_hal_deinit_early(); - - // Remap memory to system bootloader - LL_SYSCFG_SetRemapMemory(LL_SYSCFG_REMAP_SYSTEMFLASH); - // Jump - furi_hal_switch(0x0); -} diff --git a/firmware/targets/f7/Src/update.c b/firmware/targets/f7/Src/update.c deleted file mode 100644 index 36204829e29..00000000000 --- a/firmware/targets/f7/Src/update.c +++ /dev/null @@ -1,201 +0,0 @@ -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#define UPDATE_POINTER_FILE_PATH "/" UPDATE_MANIFEST_POINTER_FILE_NAME - -static FATFS* pfs = NULL; - -#define CHECK_FRESULT(result) \ - { \ - if((result) != FR_OK) { \ - return false; \ - } \ - } - -static bool flipper_update_mount_sd() { - for(int i = 0; i < BSP_SD_MaxMountRetryCount(); ++i) { - if(BSP_SD_Init((i % 2) == 0) != MSD_OK) { - /* Next attempt will be without card reset, let it settle */ - furi_delay_ms(1000); - continue; - } - - if(f_mount(pfs, "/", 1) == FR_OK) { - return true; - } - } - return false; -} - -static bool flipper_update_init() { - furi_hal_clock_init(); - furi_hal_rtc_init(); - furi_hal_interrupt_init(); - - furi_hal_spi_init(); - - MX_FATFS_Init(); - if(!hal_sd_detect()) { - return false; - } - - pfs = malloc(sizeof(FATFS)); - - return flipper_update_mount_sd(); -} - -static bool flipper_update_load_stage(const string_t work_dir, UpdateManifest* manifest) { - FIL file; - FILINFO stat; - - string_t loader_img_path; - string_init_set(loader_img_path, work_dir); - path_append(loader_img_path, string_get_cstr(manifest->staged_loader_file)); - - if((f_stat(string_get_cstr(loader_img_path), &stat) != FR_OK) || - (f_open(&file, string_get_cstr(loader_img_path), FA_OPEN_EXISTING | FA_READ) != FR_OK)) { - string_clear(loader_img_path); - return false; - } - string_clear(loader_img_path); - - void* img = malloc(stat.fsize); - uint32_t bytes_read = 0; - const uint16_t MAX_READ = 0xFFFF; - - uint32_t crc = 0; - do { - uint16_t size_read = 0; - if(f_read(&file, img + bytes_read, MAX_READ, &size_read) != FR_OK) { - break; - } - crc = crc32_calc_buffer(crc, img + bytes_read, size_read); - bytes_read += size_read; - } while(bytes_read == MAX_READ); - - do { - if((bytes_read != stat.fsize) || (crc != manifest->staged_loader_crc)) { - break; - } - - /* Point of no return. Literally - * - * NB: we MUST disable IRQ, otherwise handlers from flash - * will change global variables (like tick count) - * that are located in .data. And we move staged loader - * to the same memory region. So, IRQ handlers will mess up - * memmove'd .text section and ruin your day. - * We don't want that to happen. - */ - __disable_irq(); - - memmove((void*)(SRAM1_BASE), img, stat.fsize); - LL_SYSCFG_SetRemapMemory(LL_SYSCFG_REMAP_SRAM); - furi_hal_switch((void*)SRAM1_BASE); - return true; - - } while(false); - - free(img); - return false; -} - -static bool flipper_update_get_manifest_path(string_t out_path) { - FIL file; - FILINFO stat; - uint16_t size_read = 0; - char manifest_name_buf[UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN] = {0}; - - string_reset(out_path); - CHECK_FRESULT(f_stat(UPDATE_POINTER_FILE_PATH, &stat)); - CHECK_FRESULT(f_open(&file, UPDATE_POINTER_FILE_PATH, FA_OPEN_EXISTING | FA_READ)); - do { - if(f_read(&file, manifest_name_buf, UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN, &size_read) != - FR_OK) { - break; - } - - if((size_read == 0) || (size_read == UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN)) { - break; - } - string_set_str(out_path, manifest_name_buf); - string_right(out_path, strlen(STORAGE_EXT_PATH_PREFIX)); - } while(0); - f_close(&file); - return !string_empty_p(out_path); -} - -static UpdateManifest* flipper_update_process_manifest(const string_t manifest_path) { - FIL file; - FILINFO stat; - - CHECK_FRESULT(f_stat(string_get_cstr(manifest_path), &stat)); - CHECK_FRESULT(f_open(&file, string_get_cstr(manifest_path), FA_OPEN_EXISTING | FA_READ)); - - uint8_t* manifest_data = malloc(stat.fsize); - uint32_t bytes_read = 0; - const uint16_t MAX_READ = 0xFFFF; - - do { - uint16_t size_read = 0; - if(f_read(&file, manifest_data + bytes_read, MAX_READ, &size_read) != FR_OK) { - break; - } - bytes_read += size_read; - } while(bytes_read == MAX_READ); - - UpdateManifest* manifest = NULL; - do { - if(bytes_read != stat.fsize) { - break; - } - - manifest = update_manifest_alloc(); - if(!update_manifest_init_mem(manifest, manifest_data, bytes_read)) { - update_manifest_free(manifest); - manifest = NULL; - } - } while(false); - - f_close(&file); - free(manifest_data); - return manifest; -} - -void flipper_boot_update_exec() { - if(!flipper_update_init()) { - return; - } - - string_t work_dir, manifest_path; - string_init(work_dir); - string_init(manifest_path); - do { - if(!flipper_update_get_manifest_path(manifest_path)) { - break; - } - - UpdateManifest* manifest = flipper_update_process_manifest(manifest_path); - if(!manifest) { - break; - } - - path_extract_dirname(string_get_cstr(manifest_path), work_dir); - if(!flipper_update_load_stage(work_dir, manifest)) { - update_manifest_free(manifest); - } - } while(false); - string_clear(manifest_path); - string_clear(work_dir); - free(pfs); -} diff --git a/firmware/targets/f7/application-ext.ld b/firmware/targets/f7/application-ext.ld deleted file mode 100644 index 45fe431cdd3..00000000000 --- a/firmware/targets/f7/application-ext.ld +++ /dev/null @@ -1,37 +0,0 @@ -SECTIONS -{ - .text 0x00000000 : - { - *(.text) - *(.text.*) - } - - .rodata : - { - *(.rodata) - *(.rodata1) - *(.rodata.*) - } - - .data : - { - *(.data) - *(.data1) - *(.data.*) - } - - .bss : - { - *(.bss) - *(.bss.*) - *(.sbss) - *(.sbss.*) - *(COMMON) - } - - /DISCARD/ : - { - *(.comment) - *(.comment.*) - } -} diff --git a/firmware/targets/f7/ble_glue/app_common.h b/firmware/targets/f7/ble_glue/app_common.h deleted file mode 100644 index 214c85acd23..00000000000 --- a/firmware/targets/f7/ble_glue/app_common.h +++ /dev/null @@ -1,39 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * File Name : app_common.h - * Description : App Common application configuration file for STM32WPAN Middleware. - * - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2020 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef APP_COMMON_H -#define APP_COMMON_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include -#include - -#include - -#include "app_conf.h" - -#endif diff --git a/firmware/targets/f7/ble_glue/app_conf.h b/firmware/targets/f7/ble_glue/app_conf.h deleted file mode 100644 index 1d7474da5cf..00000000000 --- a/firmware/targets/f7/ble_glue/app_conf.h +++ /dev/null @@ -1,465 +0,0 @@ -#pragma once - -#include "hw.h" -#include "hw_conf.h" -#include "hw_if.h" -#include "ble_bufsize.h" - -#define CFG_TX_POWER (0x19) /* +0dBm */ - -/** - * Define Advertising parameters - */ -#define CFG_ADV_BD_ADDRESS (0x7257acd87a6c) -#define CFG_FAST_CONN_ADV_INTERVAL_MIN (0x80) /**< 80ms */ -#define CFG_FAST_CONN_ADV_INTERVAL_MAX (0xa0) /**< 100ms */ -#define CFG_LP_CONN_ADV_INTERVAL_MIN (0x640) /**< 1s */ -#define CFG_LP_CONN_ADV_INTERVAL_MAX (0xfa0) /**< 2.5s */ - -/** - * Define IO Authentication - */ -#define CFG_BONDING_MODE (1) -#define CFG_FIXED_PIN (111111) -#define CFG_USED_FIXED_PIN (1) -#define CFG_ENCRYPTION_KEY_SIZE_MAX (16) -#define CFG_ENCRYPTION_KEY_SIZE_MIN (8) - -/** - * Define IO capabilities - */ -#define CFG_IO_CAPABILITY_DISPLAY_ONLY (0x00) -#define CFG_IO_CAPABILITY_DISPLAY_YES_NO (0x01) -#define CFG_IO_CAPABILITY_KEYBOARD_ONLY (0x02) -#define CFG_IO_CAPABILITY_NO_INPUT_NO_OUTPUT (0x03) -#define CFG_IO_CAPABILITY_KEYBOARD_DISPLAY (0x04) - -#define CFG_IO_CAPABILITY CFG_IO_CAPABILITY_DISPLAY_YES_NO - -/** - * Define MITM modes - */ -#define CFG_MITM_PROTECTION_NOT_REQUIRED (0x00) -#define CFG_MITM_PROTECTION_REQUIRED (0x01) - -#define CFG_MITM_PROTECTION CFG_MITM_PROTECTION_REQUIRED - -/** - * Define Secure Connections Support - */ -#define CFG_SECURE_NOT_SUPPORTED (0x00) -#define CFG_SECURE_OPTIONAL (0x01) -#define CFG_SECURE_MANDATORY (0x02) - -#define CFG_SC_SUPPORT CFG_SECURE_OPTIONAL - -/** - * Define Keypress Notification Support - */ -#define CFG_KEYPRESS_NOT_SUPPORTED (0x00) -#define CFG_KEYPRESS_SUPPORTED (0x01) - -#define CFG_KEYPRESS_NOTIFICATION_SUPPORT CFG_KEYPRESS_NOT_SUPPORTED - -/** - * Numeric Comparison Answers - */ -#define YES (0x01) -#define NO (0x00) - -/** - * Device name configuration for Generic Access Service - */ -#define CFG_GAP_DEVICE_NAME "TEMPLATE" -#define CFG_GAP_DEVICE_NAME_LENGTH (8) - -/** - * Define PHY - */ -#define ALL_PHYS_PREFERENCE 0x00 -#define RX_2M_PREFERRED 0x02 -#define TX_2M_PREFERRED 0x02 -#define TX_1M 0x01 -#define TX_2M 0x02 -#define RX_1M 0x01 -#define RX_2M 0x02 - -/** -* Identity root key used to derive LTK and CSRK -*/ -#define CFG_BLE_IRK \ - { \ - 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, \ - 0xf0 \ - } - -/** -* Encryption root key used to derive LTK and CSRK -*/ -#define CFG_BLE_ERK \ - { \ - 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, \ - 0x21 \ - } - -/* USER CODE BEGIN Generic_Parameters */ -/** - * SMPS supply - * SMPS not used when Set to 0 - * SMPS used when Set to 1 - */ -#define CFG_USE_SMPS 1 -/* USER CODE END Generic_Parameters */ - -/**< specific parameters */ -/*****************************************************/ - -/** -* AD Element - Group B Feature -*/ -/* LSB - Second Byte */ -#define CFG_FEATURE_OTA_REBOOT (0x20) - -/****************************************************************************** - * BLE Stack - ******************************************************************************/ -/** - * Maximum number of simultaneous connections that the device will support. - * Valid values are from 1 to 8 - */ -#define CFG_BLE_NUM_LINK 1 - -/** - * Maximum number of Services that can be stored in the GATT database. - * Note that the GAP and GATT services are automatically added so this parameter should be 2 plus the number of user services - */ -#define CFG_BLE_NUM_GATT_SERVICES 8 - -/** - * Maximum number of Attributes - * (i.e. the number of characteristic + the number of characteristic values + the number of descriptors, excluding the services) - * that can be stored in the GATT database. - * Note that certain characteristics and relative descriptors are added automatically during device initialization - * so this parameters should be 9 plus the number of user Attributes - */ -#define CFG_BLE_NUM_GATT_ATTRIBUTES 68 - -/** - * Maximum supported ATT_MTU size - */ -#define CFG_BLE_MAX_ATT_MTU (256 + 128 + 16 + 8 + 4 + 2) - -/** - * Size of the storage area for Attribute values - * This value depends on the number of attributes used by application. In particular the sum of the following quantities (in octets) should be made for each attribute: - * - attribute value length - * - 5, if UUID is 16 bit; 19, if UUID is 128 bit - * - 2, if server configuration descriptor is used - * - 2*DTM_NUM_LINK, if client configuration descriptor is used - * - 2, if extended properties is used - * The total amount of memory needed is the sum of the above quantities for each attribute. - */ -#define CFG_BLE_ATT_VALUE_ARRAY_SIZE (1344) - -/** - * Prepare Write List size in terms of number of packet - */ -#define CFG_BLE_PREPARE_WRITE_LIST_SIZE BLE_PREP_WRITE_X_ATT(CFG_BLE_MAX_ATT_MTU) - -/** - * Number of allocated memory blocks - */ -#define CFG_BLE_MBLOCK_COUNT \ - (BLE_MBLOCKS_CALC(CFG_BLE_PREPARE_WRITE_LIST_SIZE, CFG_BLE_MAX_ATT_MTU, CFG_BLE_NUM_LINK)) - -/** - * Enable or disable the Extended Packet length feature. Valid values are 0 or 1. - */ -#define CFG_BLE_DATA_LENGTH_EXTENSION 1 - -/** - * Sleep clock accuracy in Slave mode (ppm value) - */ -#define CFG_BLE_SLAVE_SCA 500 - -/** - * Sleep clock accuracy in Master mode - * 0 : 251 ppm to 500 ppm - * 1 : 151 ppm to 250 ppm - * 2 : 101 ppm to 150 ppm - * 3 : 76 ppm to 100 ppm - * 4 : 51 ppm to 75 ppm - * 5 : 31 ppm to 50 ppm - * 6 : 21 ppm to 30 ppm - * 7 : 0 ppm to 20 ppm - */ -#define CFG_BLE_MASTER_SCA 0 - -/** - * Source for the low speed clock for RF wake-up - * 1 : external high speed crystal HSE/32/32 - * 0 : external low speed crystal ( no calibration ) - */ -#define CFG_BLE_LSE_SOURCE 0 - -/** - * Start up time of the high speed (16 or 32 MHz) crystal oscillator in units of 625/256 us (~2.44 us) - */ -#define CFG_BLE_HSE_STARTUP_TIME 0x148 - -/** - * Maximum duration of the connection event when the device is in Slave mode in units of 625/256 us (~2.44 us) - */ -#define CFG_BLE_MAX_CONN_EVENT_LENGTH (0xFFFFFFFF) - -/** - * Viterbi Mode - * 1 : enabled - * 0 : disabled - */ -#define CFG_BLE_VITERBI_MODE 1 - -/** - * BLE stack Options flags to be configured with: - * - SHCI_C2_BLE_INIT_OPTIONS_LL_ONLY - * - SHCI_C2_BLE_INIT_OPTIONS_LL_HOST - * - SHCI_C2_BLE_INIT_OPTIONS_NO_SVC_CHANGE_DESC - * - SHCI_C2_BLE_INIT_OPTIONS_WITH_SVC_CHANGE_DESC - * - SHCI_C2_BLE_INIT_OPTIONS_DEVICE_NAME_RO - * - SHCI_C2_BLE_INIT_OPTIONS_DEVICE_NAME_RW - * - SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV - * - SHCI_C2_BLE_INIT_OPTIONS_NO_EXT_ADV - * - SHCI_C2_BLE_INIT_OPTIONS_CS_ALGO2 - * - SHCI_C2_BLE_INIT_OPTIONS_NO_CS_ALGO2 - * - SHCI_C2_BLE_INIT_OPTIONS_POWER_CLASS_1 - * - SHCI_C2_BLE_INIT_OPTIONS_POWER_CLASS_2_3 - * which are used to set following configuration bits: - * (bit 0): 1: LL only - * 0: LL + host - * (bit 1): 1: no service change desc. - * 0: with service change desc. - * (bit 2): 1: device name Read-Only - * 0: device name R/W - * (bit 3): 1: extended advertizing supported [NOT SUPPORTED] - * 0: extended advertizing not supported [NOT SUPPORTED] - * (bit 4): 1: CS Algo #2 supported - * 0: CS Algo #2 not supported - * (bit 7): 1: LE Power Class 1 - * 0: LE Power Class 2-3 - * other bits: reserved (shall be set to 0) - */ -#define CFG_BLE_OPTIONS \ - (SHCI_C2_BLE_INIT_OPTIONS_LL_HOST | SHCI_C2_BLE_INIT_OPTIONS_WITH_SVC_CHANGE_DESC | \ - SHCI_C2_BLE_INIT_OPTIONS_DEVICE_NAME_RW | SHCI_C2_BLE_INIT_OPTIONS_NO_EXT_ADV | \ - SHCI_C2_BLE_INIT_OPTIONS_NO_CS_ALGO2 | SHCI_C2_BLE_INIT_OPTIONS_POWER_CLASS_2_3) - -/** - * Queue length of BLE Event - * This parameter defines the number of asynchronous events that can be stored in the HCI layer before - * being reported to the application. When a command is sent to the BLE core coprocessor, the HCI layer - * is waiting for the event with the Num_HCI_Command_Packets set to 1. The receive queue shall be large - * enough to store all asynchronous events received in between. - * When CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE is set to 27, this allow to store three 255 bytes long asynchronous events - * between the HCI command and its event. - * This parameter depends on the value given to CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE. When the queue size is to small, - * the system may hang if the queue is full with asynchronous events and the HCI layer is still waiting - * for a CC/CS event, In that case, the notification TL_BLE_HCI_ToNot() is called to indicate - * to the application a HCI command did not receive its command event within 30s (Default HCI Timeout). - */ -#define CFG_TLBLE_EVT_QUEUE_LENGTH 5 -/** - * This parameter should be set to fit most events received by the HCI layer. It defines the buffer size of each element - * allocated in the queue of received events and can be used to optimize the amount of RAM allocated by the Memory Manager. - * It should not exceed 255 which is the maximum HCI packet payload size (a greater value is a lost of memory as it will - * never be used) - * With the current wireless firmware implementation, this parameter shall be kept to 255 - * - */ -#define CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE \ - 255 /**< Set to 255 with the memory manager and the mailbox */ - -#define TL_BLE_EVENT_FRAME_SIZE (TL_EVT_HDR_SIZE + CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE) -/****************************************************************************** - * UART interfaces - ******************************************************************************/ - -/** - * Select UART interfaces - */ -#define CFG_DEBUG_TRACE_UART hw_uart1 -#define CFG_CONSOLE_MENU 0 - -/****************************************************************************** - * Low Power - ******************************************************************************/ -/** - * When set to 1, the low power mode is enable - * When set to 0, the device stays in RUN mode - */ -#define CFG_LPM_SUPPORTED 1 - -/****************************************************************************** - * Timer Server - ******************************************************************************/ -/** - * CFG_RTC_WUCKSEL_DIVIDER: This sets the RTCCLK divider to the wakeup timer. - * The lower is the value, the better is the power consumption and the accuracy of the timerserver - * The higher is the value, the finest is the granularity - * - * CFG_RTC_ASYNCH_PRESCALER: This sets the asynchronous prescaler of the RTC. It should as high as possible ( to ouput - * clock as low as possible) but the output clock should be equal or higher frequency compare to the clock feeding - * the wakeup timer. A lower clock speed would impact the accuracy of the timer server. - * - * CFG_RTC_SYNCH_PRESCALER: This sets the synchronous prescaler of the RTC. - * When the 1Hz calendar clock is required, it shall be sets according to other settings - * When the 1Hz calendar clock is not needed, CFG_RTC_SYNCH_PRESCALER should be set to 0x7FFF (MAX VALUE) - * - * CFG_RTCCLK_DIVIDER_CONF: - * Shall be set to either 0,2,4,8,16 - * When set to either 2,4,8,16, the 1Hhz calendar is supported - * When set to 0, the user sets its own configuration - * - * The following settings are computed with LSI as input to the RTC - */ -#define CFG_RTCCLK_DIVIDER_CONF 0 - -#if(CFG_RTCCLK_DIVIDER_CONF == 0) -/** - * Custom configuration - * It does not support 1Hz calendar - * It divides the RTC CLK by 16 - */ -#define CFG_RTCCLK_DIV (16) -#define CFG_RTC_WUCKSEL_DIVIDER (0) -#define CFG_RTC_ASYNCH_PRESCALER (CFG_RTCCLK_DIV - 1) -#define CFG_RTC_SYNCH_PRESCALER (0x7FFF) - -#else - -#if(CFG_RTCCLK_DIVIDER_CONF == 2) -/** - * It divides the RTC CLK by 2 - */ -#define CFG_RTC_WUCKSEL_DIVIDER (3) -#endif - -#if(CFG_RTCCLK_DIVIDER_CONF == 4) -/** - * It divides the RTC CLK by 4 - */ -#define CFG_RTC_WUCKSEL_DIVIDER (2) -#endif - -#if(CFG_RTCCLK_DIVIDER_CONF == 8) -/** - * It divides the RTC CLK by 8 - */ -#define CFG_RTC_WUCKSEL_DIVIDER (1) -#endif - -#if(CFG_RTCCLK_DIVIDER_CONF == 16) -/** - * It divides the RTC CLK by 16 - */ -#define CFG_RTC_WUCKSEL_DIVIDER (0) -#endif - -#define CFG_RTCCLK_DIV CFG_RTCCLK_DIVIDER_CONF -#define CFG_RTC_ASYNCH_PRESCALER (CFG_RTCCLK_DIV - 1) -#define CFG_RTC_SYNCH_PRESCALER (DIVR(LSE_VALUE, (CFG_RTC_ASYNCH_PRESCALER + 1)) - 1) - -#endif - -/** tick timer value in us */ -#define CFG_TS_TICK_VAL DIVR((CFG_RTCCLK_DIV * 1000000), LSE_VALUE) - -typedef enum { - CFG_TIM_PROC_ID_ISR, - /* USER CODE BEGIN CFG_TimProcID_t */ - - /* USER CODE END CFG_TimProcID_t */ -} CFG_TimProcID_t; - -/****************************************************************************** - * Debug - ******************************************************************************/ -/** - * When set, this resets some hw resources to set the device in the same state than the power up - * The FW resets only register that may prevent the FW to run properly - * - * This shall be set to 0 in a final product - * - */ -#define CFG_HW_RESET_BY_FW 0 - -/** - * keep debugger enabled while in any low power mode when set to 1 - * should be set to 0 in production - */ -#define CFG_DEBUGGER_SUPPORTED 1 - -/** - * When set to 1, the traces are enabled in the BLE services - */ -#define CFG_DEBUG_BLE_TRACE 0 - -/** - * Enable or Disable traces in application - */ -#define CFG_DEBUG_APP_TRACE 0 - -#if(CFG_DEBUG_APP_TRACE != 0) -#define APP_DBG_MSG PRINT_MESG_DBG -#else -#define APP_DBG_MSG PRINT_NO_MESG -#endif - -#if((CFG_DEBUG_BLE_TRACE != 0) || (CFG_DEBUG_APP_TRACE != 0)) -#define CFG_DEBUG_TRACE 1 -#endif - -#if(CFG_DEBUG_TRACE != 0) -#undef CFG_LPM_SUPPORTED -#undef CFG_DEBUGGER_SUPPORTED -#define CFG_LPM_SUPPORTED 0 -#define CFG_DEBUGGER_SUPPORTED 1 -#endif - -/** - * When CFG_DEBUG_TRACE_FULL is set to 1, the trace are output with the API name, the file name and the line number - * When CFG_DEBUG_TRACE_LIGHT is set to 1, only the debug message is output - * - * When both are set to 0, no trace are output - * When both are set to 1, CFG_DEBUG_TRACE_FULL is selected - */ -#define CFG_DEBUG_TRACE_LIGHT 0 -#define CFG_DEBUG_TRACE_FULL 0 - -#if((CFG_DEBUG_TRACE != 0) && (CFG_DEBUG_TRACE_LIGHT == 0) && (CFG_DEBUG_TRACE_FULL == 0)) -#undef CFG_DEBUG_TRACE_FULL -#undef CFG_DEBUG_TRACE_LIGHT -#define CFG_DEBUG_TRACE_FULL 0 -#define CFG_DEBUG_TRACE_LIGHT 1 -#endif - -#if(CFG_DEBUG_TRACE == 0) -#undef CFG_DEBUG_TRACE_FULL -#undef CFG_DEBUG_TRACE_LIGHT -#define CFG_DEBUG_TRACE_FULL 0 -#define CFG_DEBUG_TRACE_LIGHT 0 -#endif - -/** - * When not set, the traces is looping on sending the trace over UART - */ -#define DBG_TRACE_USE_CIRCULAR_QUEUE 0 - -/** - * max buffer Size to queue data traces and max data trace allowed. - * Only Used if DBG_TRACE_USE_CIRCULAR_QUEUE is defined - */ -#define DBG_TRACE_MSG_QUEUE_SIZE 4096 -#define MAX_DBG_TRACE_MSG_SIZE 1024 - -#define CFG_OTP_BASE_ADDRESS OTP_AREA_BASE -#define CFG_OTP_END_ADRESS OTP_AREA_END_ADDR diff --git a/firmware/targets/f7/ble_glue/app_debug.h b/firmware/targets/f7/ble_glue/app_debug.h deleted file mode 100644 index 92a54d75b03..00000000000 --- a/firmware/targets/f7/ble_glue/app_debug.h +++ /dev/null @@ -1,38 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * File Name : app_debug.h - * Description : Header for app_debug.c module - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2020 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __APP_DEBUG_H -#define __APP_DEBUG_H - -#ifdef __cplusplus -extern "C" { -#endif - -void APPD_Init(void); -void APPD_EnableCPU2(void); - -#ifdef __cplusplus -} -#endif - -#endif /*__APP_DEBUG_H */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/ble_glue/battery_service.c b/firmware/targets/f7/ble_glue/battery_service.c deleted file mode 100644 index a95f9187284..00000000000 --- a/firmware/targets/f7/ble_glue/battery_service.c +++ /dev/null @@ -1,166 +0,0 @@ -#include "battery_service.h" -#include "app_common.h" -#include "ble.h" - -#include -#include - -#define TAG "BtBatterySvc" - -typedef struct { - uint16_t svc_handle; - uint16_t battery_level_char_handle; - uint16_t power_state_char_handle; -} BatterySvc; - -enum { - // Common states - BatterySvcPowerStateUnknown = 0b00, - BatterySvcPowerStateUnsupported = 0b01, - // Level states - BatterySvcPowerStateGoodLevel = 0b10, - BatterySvcPowerStateCriticallyLowLevel = 0b11, - // Charging states - BatterySvcPowerStateNotCharging = 0b10, - BatterySvcPowerStateCharging = 0b11, - // Discharging states - BatterySvcPowerStateNotDischarging = 0b10, - BatterySvcPowerStateDischarging = 0b11, - // Battery states - BatterySvcPowerStateBatteryNotPresent = 0b10, - BatterySvcPowerStateBatteryPresent = 0b11, -}; - -typedef struct { - uint8_t present : 2; - uint8_t discharging : 2; - uint8_t charging : 2; - uint8_t level : 2; -} BattrySvcPowerState; - -_Static_assert(sizeof(BattrySvcPowerState) == 1, "Incorrect structure size"); - -static BatterySvc* battery_svc = NULL; - -#define BATTERY_POWER_STATE (0x2A1A) - -static const uint16_t service_uuid = BATTERY_SERVICE_UUID; -static const uint16_t battery_level_char_uuid = BATTERY_LEVEL_CHAR_UUID; -static const uint16_t power_state_char_uuid = BATTERY_POWER_STATE; - -void battery_svc_start() { - battery_svc = malloc(sizeof(BatterySvc)); - tBleStatus status; - - // Add Battery service - status = aci_gatt_add_service( - UUID_TYPE_16, (Service_UUID_t*)&service_uuid, PRIMARY_SERVICE, 8, &battery_svc->svc_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add Battery service: %d", status); - } - // Add Battery level characteristic - status = aci_gatt_add_char( - battery_svc->svc_handle, - UUID_TYPE_16, - (Char_UUID_t*)&battery_level_char_uuid, - 1, - CHAR_PROP_READ | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &battery_svc->battery_level_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status); - } - // Add Power state characteristic - status = aci_gatt_add_char( - battery_svc->svc_handle, - UUID_TYPE_16, - (Char_UUID_t*)&power_state_char_uuid, - 1, - CHAR_PROP_READ | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &battery_svc->power_state_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status); - } - // Update power state charachteristic - battery_svc_update_power_state(); -} - -void battery_svc_stop() { - tBleStatus status; - if(battery_svc) { - // Delete Battery level characteristic - status = - aci_gatt_del_char(battery_svc->svc_handle, battery_svc->battery_level_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status); - } - // Delete Power state characteristic - status = aci_gatt_del_char(battery_svc->svc_handle, battery_svc->power_state_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status); - } - // Delete Battery service - status = aci_gatt_del_service(battery_svc->svc_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Battery service: %d", status); - } - free(battery_svc); - battery_svc = NULL; - } -} - -bool battery_svc_is_started() { - return battery_svc != NULL; -} - -bool battery_svc_update_level(uint8_t battery_charge) { - // Check if service was started - if(battery_svc == NULL) { - return false; - } - // Update battery level characteristic - FURI_LOG_D(TAG, "Updating battery level characteristic"); - tBleStatus result = aci_gatt_update_char_value( - battery_svc->svc_handle, battery_svc->battery_level_char_handle, 0, 1, &battery_charge); - if(result) { - FURI_LOG_E(TAG, "Failed updating RX characteristic: %d", result); - } - return result != BLE_STATUS_SUCCESS; -} - -bool battery_svc_update_power_state() { - // Check if service was started - if(battery_svc == NULL) { - return false; - } - // Update power state characteristic - BattrySvcPowerState power_state = { - .level = BatterySvcPowerStateUnsupported, - .present = BatterySvcPowerStateBatteryPresent, - }; - if(furi_hal_power_is_charging()) { - power_state.charging = BatterySvcPowerStateCharging; - power_state.discharging = BatterySvcPowerStateNotDischarging; - } else { - power_state.charging = BatterySvcPowerStateNotCharging; - power_state.discharging = BatterySvcPowerStateDischarging; - } - FURI_LOG_D(TAG, "Updating power state characteristic"); - tBleStatus result = aci_gatt_update_char_value( - battery_svc->svc_handle, - battery_svc->power_state_char_handle, - 0, - 1, - (uint8_t*)&power_state); - if(result) { - FURI_LOG_E(TAG, "Failed updating Power state characteristic: %d", result); - } - return result != BLE_STATUS_SUCCESS; -} diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c deleted file mode 100644 index 4d3c96e13c2..00000000000 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ /dev/null @@ -1,189 +0,0 @@ -#include "ble_app.h" - -#include "hci_tl.h" -#include "ble.h" -#include "shci.h" -#include "gap.h" - -#include -#include - -#define TAG "Bt" - -#define BLE_APP_FLAG_HCI_EVENT (1UL << 0) -#define BLE_APP_FLAG_KILL_THREAD (1UL << 1) -#define BLE_APP_FLAG_ALL (BLE_APP_FLAG_HCI_EVENT | BLE_APP_FLAG_KILL_THREAD) - -PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t ble_app_cmd_buffer; -PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint32_t ble_app_nvm[BLE_NVM_SRAM_SIZE]; - -_Static_assert( - sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 49, - "Ble stack config structure size mismatch"); - -typedef struct { - FuriMutex* hci_mtx; - FuriSemaphore* hci_sem; - FuriThread* thread; -} BleApp; - -static BleApp* ble_app = NULL; - -static int32_t ble_app_hci_thread(void* context); -static void ble_app_hci_event_handler(void* pPayload); -static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status); - -bool ble_app_init() { - SHCI_CmdStatus_t status; - ble_app = malloc(sizeof(BleApp)); - // Allocate semafore and mutex for ble command buffer access - ble_app->hci_mtx = furi_mutex_alloc(FuriMutexTypeNormal); - ble_app->hci_sem = furi_semaphore_alloc(1, 0); - // HCI transport layer thread to handle user asynch events - ble_app->thread = furi_thread_alloc(); - furi_thread_set_name(ble_app->thread, "BleHciDriver"); - furi_thread_set_stack_size(ble_app->thread, 1024); - furi_thread_set_context(ble_app->thread, ble_app); - furi_thread_set_callback(ble_app->thread, ble_app_hci_thread); - furi_thread_start(ble_app->thread); - - // Initialize Ble Transport Layer - HCI_TL_HciInitConf_t hci_tl_config = { - .p_cmdbuffer = (uint8_t*)&ble_app_cmd_buffer, - .StatusNotCallBack = ble_app_hci_status_not_handler, - }; - hci_init(ble_app_hci_event_handler, (void*)&hci_tl_config); - - // Configure NVM store for pairing data - SHCI_C2_CONFIG_Cmd_Param_t config_param = { - .PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE, - .Config1 = SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM, - .BleNvmRamAddress = (uint32_t)ble_app_nvm, - .EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE, - }; - status = SHCI_C2_Config(&config_param); - if(status) { - FURI_LOG_E(TAG, "Failed to configure 2nd core: %d", status); - } - - // Start ble stack on 2nd core - SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { - .Header = {{0, 0, 0}}, // Header unused - .Param = { - .pBleBufferAddress = 0, // pBleBufferAddress not used - .BleBufferSize = 0, // BleBufferSize not used - .NumAttrRecord = CFG_BLE_NUM_GATT_ATTRIBUTES, - .NumAttrServ = CFG_BLE_NUM_GATT_SERVICES, - .AttrValueArrSize = CFG_BLE_ATT_VALUE_ARRAY_SIZE, - .NumOfLinks = CFG_BLE_NUM_LINK, - .ExtendedPacketLengthEnable = CFG_BLE_DATA_LENGTH_EXTENSION, - .PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE, - .MblockCount = CFG_BLE_MBLOCK_COUNT, - .AttMtu = CFG_BLE_MAX_ATT_MTU, - .SlaveSca = CFG_BLE_SLAVE_SCA, - .MasterSca = CFG_BLE_MASTER_SCA, - .LsSource = CFG_BLE_LSE_SOURCE, - .MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH, - .HsStartupTime = CFG_BLE_HSE_STARTUP_TIME, - .ViterbiEnable = CFG_BLE_VITERBI_MODE, - .Options = CFG_BLE_OPTIONS, - .HwVersion = 0, - .max_coc_initiator_nbr = 32, - .min_tx_power = 0, - .max_tx_power = 0, - .rx_model_config = 1, - }}; - status = SHCI_C2_BLE_Init(&ble_init_cmd_packet); - if(status) { - FURI_LOG_E(TAG, "Failed to start ble stack: %d", status); - } - return status == SHCI_Success; -} - -void ble_app_get_key_storage_buff(uint8_t** addr, uint16_t* size) { - *addr = (uint8_t*)ble_app_nvm; - *size = sizeof(ble_app_nvm); -} - -void ble_app_thread_stop() { - if(ble_app) { - FuriThreadId thread_id = furi_thread_get_id(ble_app->thread); - furi_assert(thread_id); - furi_thread_flags_set(thread_id, BLE_APP_FLAG_KILL_THREAD); - furi_thread_join(ble_app->thread); - furi_thread_free(ble_app->thread); - // Free resources - furi_mutex_free(ble_app->hci_mtx); - furi_semaphore_free(ble_app->hci_sem); - free(ble_app); - ble_app = NULL; - memset(&ble_app_cmd_buffer, 0, sizeof(ble_app_cmd_buffer)); - } -} - -static int32_t ble_app_hci_thread(void* arg) { - UNUSED(arg); - uint32_t flags = 0; - - while(1) { - flags = furi_thread_flags_wait(BLE_APP_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever); - if(flags & BLE_APP_FLAG_KILL_THREAD) { - break; - } - if(flags & BLE_APP_FLAG_HCI_EVENT) { - hci_user_evt_proc(); - } - } - - return 0; -} - -// Called by WPAN lib -void hci_notify_asynch_evt(void* pdata) { - UNUSED(pdata); - if(ble_app) { - FuriThreadId thread_id = furi_thread_get_id(ble_app->thread); - furi_assert(thread_id); - furi_thread_flags_set(thread_id, BLE_APP_FLAG_HCI_EVENT); - } -} - -void hci_cmd_resp_release(uint32_t flag) { - UNUSED(flag); - if(ble_app) { - furi_semaphore_release(ble_app->hci_sem); - } -} - -void hci_cmd_resp_wait(uint32_t timeout) { - UNUSED(timeout); - if(ble_app) { - furi_semaphore_acquire(ble_app->hci_sem, FuriWaitForever); - } -} - -static void ble_app_hci_event_handler(void* pPayload) { - SVCCTL_UserEvtFlowStatus_t svctl_return_status; - tHCI_UserEvtRxParam* pParam = (tHCI_UserEvtRxParam*)pPayload; - - if(ble_app) { - svctl_return_status = SVCCTL_UserEvtRx((void*)&(pParam->pckt->evtserial)); - if(svctl_return_status != SVCCTL_UserEvtFlowDisable) { - pParam->status = HCI_TL_UserEventFlow_Enable; - } else { - pParam->status = HCI_TL_UserEventFlow_Disable; - } - } -} - -static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status) { - if(status == HCI_TL_CmdBusy) { - furi_mutex_acquire(ble_app->hci_mtx, FuriWaitForever); - } else if(status == HCI_TL_CmdAvailable) { - furi_mutex_release(ble_app->hci_mtx); - } -} - -void SVCCTL_ResumeUserEventFlow(void) { - hci_resume_flow(); -} diff --git a/firmware/targets/f7/ble_glue/ble_conf.h b/firmware/targets/f7/ble_glue/ble_conf.h deleted file mode 100644 index a04d1def1bd..00000000000 --- a/firmware/targets/f7/ble_glue/ble_conf.h +++ /dev/null @@ -1,73 +0,0 @@ -/** - ****************************************************************************** - * File Name : App/ble_conf.h - * Description : Configuration file for BLE Middleware. - * - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2020 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef BLE_CONF_H -#define BLE_CONF_H - -#include "app_conf.h" - -#ifndef __weak -#define __weak __attribute__((weak)) -#endif - -/****************************************************************************** - * - * BLE SERVICES CONFIGURATION - * blesvc - * - ******************************************************************************/ - -/** - * This setting shall be set to '1' if the device needs to support the Peripheral Role - * In the MS configuration, both BLE_CFG_PERIPHERAL and BLE_CFG_CENTRAL shall be set to '1' - */ -#define BLE_CFG_PERIPHERAL 1 - -/** - * This setting shall be set to '1' if the device needs to support the Central Role - * In the MS configuration, both BLE_CFG_PERIPHERAL and BLE_CFG_CENTRAL shall be set to '1' - */ -#define BLE_CFG_CENTRAL 0 - -/** - * There is one handler per service enabled - * Note: There is no handler for the Device Information Service - * - * This shall take into account all registered handlers - * (from either the provided services or the custom services) - */ -#define BLE_CFG_SVC_MAX_NBR_CB 7 - -#define BLE_CFG_CLT_MAX_NBR_CB 0 - -/****************************************************************************** - * GAP Service - Apprearance - ******************************************************************************/ - -#define BLE_CFG_UNKNOWN_APPEARANCE (0) -#define BLE_CFG_GAP_APPEARANCE (0x0086) - -/****************************************************************************** - * Over The Air Feature (OTA) - STM Proprietary - ******************************************************************************/ -#define BLE_CFG_OTA_REBOOT_CHAR 0 /**< REBOOT OTA MODE CHARACTERISTIC */ - -#endif /*BLE_CONF_H */ -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/ble_glue/ble_dbg_conf.h b/firmware/targets/f7/ble_glue/ble_dbg_conf.h deleted file mode 100644 index 8305f810615..00000000000 --- a/firmware/targets/f7/ble_glue/ble_dbg_conf.h +++ /dev/null @@ -1,199 +0,0 @@ -/** - ****************************************************************************** - * File Name : App/ble_dbg_conf.h - * Description : Debug configuration file for BLE Middleware. - * - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2020 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __BLE_DBG_CONF_H -#define __BLE_DBG_CONF_H - -/** - * Enable or Disable traces from BLE - */ - -#define BLE_DBG_APP_EN 1 -#define BLE_DBG_DIS_EN 1 -#define BLE_DBG_HRS_EN 1 -#define BLE_DBG_SVCCTL_EN 1 -#define BLE_DBG_BLS_EN 1 -#define BLE_DBG_HTS_EN 1 -#define BLE_DBG_P2P_STM_EN 1 - -/** - * Macro definition - */ -#if(BLE_DBG_APP_EN != 0) -#define BLE_DBG_APP_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_APP_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_DIS_EN != 0) -#define BLE_DBG_DIS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_DIS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_HRS_EN != 0) -#define BLE_DBG_HRS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_HRS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_P2P_STM_EN != 0) -#define BLE_DBG_P2P_STM_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_P2P_STM_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_TEMPLATE_STM_EN != 0) -#define BLE_DBG_TEMPLATE_STM_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_TEMPLATE_STM_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_EDS_STM_EN != 0) -#define BLE_DBG_EDS_STM_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_EDS_STM_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_LBS_STM_EN != 0) -#define BLE_DBG_LBS_STM_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_LBS_STM_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_SVCCTL_EN != 0) -#define BLE_DBG_SVCCTL_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_SVCCTL_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_CTS_EN != 0) -#define BLE_DBG_CTS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_CTS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_HIDS_EN != 0) -#define BLE_DBG_HIDS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_HIDS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_PASS_EN != 0) -#define BLE_DBG_PASS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_PASS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_BLS_EN != 0) -#define BLE_DBG_BLS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_BLS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_HTS_EN != 0) -#define BLE_DBG_HTS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_HTS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_ANS_EN != 0) -#define BLE_DBG_ANS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_ANS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_ESS_EN != 0) -#define BLE_DBG_ESS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_ESS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_GLS_EN != 0) -#define BLE_DBG_GLS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_GLS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_BAS_EN != 0) -#define BLE_DBG_BAS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_BAS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_RTUS_EN != 0) -#define BLE_DBG_RTUS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_RTUS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_HPS_EN != 0) -#define BLE_DBG_HPS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_HPS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_TPS_EN != 0) -#define BLE_DBG_TPS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_TPS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_LLS_EN != 0) -#define BLE_DBG_LLS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_LLS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_IAS_EN != 0) -#define BLE_DBG_IAS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_IAS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_WSS_EN != 0) -#define BLE_DBG_WSS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_WSS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_LNS_EN != 0) -#define BLE_DBG_LNS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_LNS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_SCPS_EN != 0) -#define BLE_DBG_SCPS_MSG PRINT_MESG_DBG -#else -#define BLE_DBG_SCPS_MSG PRINT_NO_MESG -#endif - -#if(BLE_DBG_DTS_EN != 0) -#define BLE_DBG_DTS_MSG PRINT_MESG_DBG -#define BLE_DBG_DTS_BUF PRINT_LOG_BUFF_DBG -#else -#define BLE_DBG_DTS_MSG PRINT_NO_MESG -#define BLE_DBG_DTS_BUF PRINT_NO_MESG -#endif - -#endif /*__BLE_DBG_CONF_H */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/ble_glue/dev_info_service.c b/firmware/targets/f7/ble_glue/dev_info_service.c deleted file mode 100755 index d6d1e479ef8..00000000000 --- a/firmware/targets/f7/ble_glue/dev_info_service.c +++ /dev/null @@ -1,222 +0,0 @@ -#include "dev_info_service.h" -#include "app_common.h" -#include "ble.h" - -#include -#include -#include -#include - -#define TAG "BtDevInfoSvc" - -typedef struct { - uint16_t service_handle; - uint16_t man_name_char_handle; - uint16_t serial_num_char_handle; - uint16_t firmware_rev_char_handle; - uint16_t software_rev_char_handle; - uint16_t rpc_version_char_handle; - string_t version_string; - char hardware_revision[4]; -} DevInfoSvc; - -static DevInfoSvc* dev_info_svc = NULL; - -static const char dev_info_man_name[] = "Flipper Devices Inc."; -static const char dev_info_serial_num[] = "1.0"; -static const char dev_info_rpc_version[] = TOSTRING(PROTOBUF_MAJOR_VERSION.PROTOBUF_MINOR_VERSION); - -static const uint8_t dev_info_rpc_version_uuid[] = - {0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03}; - -void dev_info_svc_start() { - dev_info_svc = malloc(sizeof(DevInfoSvc)); - string_init_printf( - dev_info_svc->version_string, - "%s %s %s %s", - version_get_githash(NULL), - version_get_gitbranch(NULL), - version_get_gitbranchnum(NULL), - version_get_builddate(NULL)); - snprintf( - dev_info_svc->hardware_revision, - sizeof(dev_info_svc->hardware_revision), - "%d", - version_get_target(NULL)); - tBleStatus status; - - // Add Device Information Service - uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; - status = aci_gatt_add_service( - UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 11, &dev_info_svc->service_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add Device Information Service: %d", status); - } - - // Add characteristics - uuid = MANUFACTURER_NAME_UUID; - status = aci_gatt_add_char( - dev_info_svc->service_handle, - UUID_TYPE_16, - (Char_UUID_t*)&uuid, - strlen(dev_info_man_name), - CHAR_PROP_READ, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &dev_info_svc->man_name_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add manufacturer name char: %d", status); - } - uuid = SERIAL_NUMBER_UUID; - status = aci_gatt_add_char( - dev_info_svc->service_handle, - UUID_TYPE_16, - (Char_UUID_t*)&uuid, - strlen(dev_info_serial_num), - CHAR_PROP_READ, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &dev_info_svc->serial_num_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add serial number char: %d", status); - } - uuid = FIRMWARE_REVISION_UUID; - status = aci_gatt_add_char( - dev_info_svc->service_handle, - UUID_TYPE_16, - (Char_UUID_t*)&uuid, - strlen(dev_info_svc->hardware_revision), - CHAR_PROP_READ, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &dev_info_svc->firmware_rev_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add firmware revision char: %d", status); - } - uuid = SOFTWARE_REVISION_UUID; - status = aci_gatt_add_char( - dev_info_svc->service_handle, - UUID_TYPE_16, - (Char_UUID_t*)&uuid, - string_size(dev_info_svc->version_string), - CHAR_PROP_READ, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &dev_info_svc->software_rev_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add software revision char: %d", status); - } - status = aci_gatt_add_char( - dev_info_svc->service_handle, - UUID_TYPE_128, - (const Char_UUID_t*)dev_info_rpc_version_uuid, - strlen(dev_info_rpc_version), - CHAR_PROP_READ, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &dev_info_svc->rpc_version_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add rpc version characteristic: %d", status); - } - - // Update characteristics - status = aci_gatt_update_char_value( - dev_info_svc->service_handle, - dev_info_svc->man_name_char_handle, - 0, - strlen(dev_info_man_name), - (uint8_t*)dev_info_man_name); - if(status) { - FURI_LOG_E(TAG, "Failed to update manufacturer name char: %d", status); - } - status = aci_gatt_update_char_value( - dev_info_svc->service_handle, - dev_info_svc->serial_num_char_handle, - 0, - strlen(dev_info_serial_num), - (uint8_t*)dev_info_serial_num); - if(status) { - FURI_LOG_E(TAG, "Failed to update serial number char: %d", status); - } - status = aci_gatt_update_char_value( - dev_info_svc->service_handle, - dev_info_svc->firmware_rev_char_handle, - 0, - strlen(dev_info_svc->hardware_revision), - (uint8_t*)dev_info_svc->hardware_revision); - if(status) { - FURI_LOG_E(TAG, "Failed to update firmware revision char: %d", status); - } - status = aci_gatt_update_char_value( - dev_info_svc->service_handle, - dev_info_svc->software_rev_char_handle, - 0, - string_size(dev_info_svc->version_string), - (uint8_t*)string_get_cstr(dev_info_svc->version_string)); - if(status) { - FURI_LOG_E(TAG, "Failed to update software revision char: %d", status); - } - status = aci_gatt_update_char_value( - dev_info_svc->service_handle, - dev_info_svc->rpc_version_char_handle, - 0, - strlen(dev_info_rpc_version), - (uint8_t*)dev_info_rpc_version); - if(status) { - FURI_LOG_E(TAG, "Failed to update rpc version char: %d", status); - } -} - -void dev_info_svc_stop() { - tBleStatus status; - if(dev_info_svc) { - string_clear(dev_info_svc->version_string); - // Delete service characteristics - status = - aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->man_name_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete manufacturer name char: %d", status); - } - status = - aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->serial_num_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete serial number char: %d", status); - } - status = aci_gatt_del_char( - dev_info_svc->service_handle, dev_info_svc->firmware_rev_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete firmware revision char: %d", status); - } - status = aci_gatt_del_char( - dev_info_svc->service_handle, dev_info_svc->software_rev_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete software revision char: %d", status); - } - status = - aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->rpc_version_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete rpc version char: %d", status); - } - // Delete service - status = aci_gatt_del_service(dev_info_svc->service_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete device info service: %d", status); - } - free(dev_info_svc); - dev_info_svc = NULL; - } -} - -bool dev_info_svc_is_started() { - return dev_info_svc != NULL; -} diff --git a/firmware/targets/f7/ble_glue/hid_service.c b/firmware/targets/f7/ble_glue/hid_service.c deleted file mode 100644 index 0efe1747b63..00000000000 --- a/firmware/targets/f7/ble_glue/hid_service.c +++ /dev/null @@ -1,332 +0,0 @@ -#include "hid_service.h" -#include "app_common.h" -#include "ble.h" - -#include - -#define TAG "BtHid" - -typedef struct { - uint16_t svc_handle; - uint16_t protocol_mode_char_handle; - uint16_t report_char_handle[HID_SVC_REPORT_COUNT]; - uint16_t report_ref_desc_handle[HID_SVC_REPORT_COUNT]; - uint16_t report_map_char_handle; - uint16_t info_char_handle; - uint16_t ctrl_point_char_handle; -} HIDSvc; - -static HIDSvc* hid_svc = NULL; - -static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { - SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; - hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); - evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; - // aci_gatt_attribute_modified_event_rp0* attribute_modified; - if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { - if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { - // Process modification events - ret = SVCCTL_EvtAckFlowEnable; - } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { - // Process notification confirmation - ret = SVCCTL_EvtAckFlowEnable; - } - } - return ret; -} - -void hid_svc_start() { - tBleStatus status; - hid_svc = malloc(sizeof(HIDSvc)); - Service_UUID_t svc_uuid = {}; - Char_Desc_Uuid_t desc_uuid = {}; - Char_UUID_t char_uuid = {}; - - // Register event handler - SVCCTL_RegisterSvcHandler(hid_svc_event_handler); - // Add service - svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID; - /** - * Add Human Interface Device Service - */ - status = aci_gatt_add_service( - UUID_TYPE_16, - &svc_uuid, - PRIMARY_SERVICE, - 2 + /* protocol mode */ - (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + - (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + - 2, /* Service + Report Map + HID Information + HID Control Point */ - &hid_svc->svc_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add HID service: %d", status); - } - // Add Protocol mode characteristics - char_uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - 1, - CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, - ATTR_PERMISSION_NONE, - GATT_NOTIFY_ATTRIBUTE_WRITE, - 10, - CHAR_VALUE_LEN_CONSTANT, - &hid_svc->protocol_mode_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add protocol mode characteristic: %d", status); - } - // Update Protocol mode characteristic - uint8_t protocol_mode = 1; - status = aci_gatt_update_char_value( - hid_svc->svc_handle, hid_svc->protocol_mode_char_handle, 0, 1, &protocol_mode); - if(status) { - FURI_LOG_E(TAG, "Failed to update protocol mode characteristic: %d", status); - } - -#if(HID_SVC_REPORT_COUNT != 0) - for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) { - if(i < HID_SVC_INPUT_REPORT_COUNT) { - uint8_t buf[2] = {i + 1, 1}; // 1 input - char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - HID_SVC_REPORT_MAX_LEN, - CHAR_PROP_READ | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_NONE, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_VARIABLE, - &(hid_svc->report_char_handle[i])); - if(status) { - FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); - } - - desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; - status = aci_gatt_add_char_desc( - hid_svc->svc_handle, - hid_svc->report_char_handle[i], - UUID_TYPE_16, - &desc_uuid, - HID_SVC_REPORT_REF_LEN, - HID_SVC_REPORT_REF_LEN, - buf, - ATTR_PERMISSION_NONE, - ATTR_ACCESS_READ_WRITE, - GATT_DONT_NOTIFY_EVENTS, - MIN_ENCRY_KEY_SIZE, - CHAR_VALUE_LEN_CONSTANT, - &(hid_svc->report_ref_desc_handle[i])); - if(status) { - FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); - } - } else if((i - HID_SVC_INPUT_REPORT_COUNT) < HID_SVC_OUTPUT_REPORT_COUNT) { - uint8_t buf[2] = {i + 1, 2}; // 2 output - char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - HID_SVC_REPORT_MAX_LEN, - CHAR_PROP_READ | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_NONE, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_VARIABLE, - &(hid_svc->report_char_handle[i])); - if(status) { - FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); - } - - desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; - status = aci_gatt_add_char_desc( - hid_svc->svc_handle, - hid_svc->report_char_handle[i], - UUID_TYPE_16, - &desc_uuid, - HID_SVC_REPORT_REF_LEN, - HID_SVC_REPORT_REF_LEN, - buf, - ATTR_PERMISSION_NONE, - ATTR_ACCESS_READ_WRITE, - GATT_DONT_NOTIFY_EVENTS, - MIN_ENCRY_KEY_SIZE, - CHAR_VALUE_LEN_CONSTANT, - &(hid_svc->report_ref_desc_handle[i])); - if(status) { - FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); - } - } else { - uint8_t buf[2] = {i + 1, 3}; // 3 feature - char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - HID_SVC_REPORT_MAX_LEN, - CHAR_PROP_READ | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_NONE, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_VARIABLE, - &(hid_svc->report_char_handle[i])); - if(status) { - FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); - } - - desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; - status = aci_gatt_add_char_desc( - hid_svc->svc_handle, - hid_svc->report_char_handle[i], - UUID_TYPE_16, - &desc_uuid, - HID_SVC_REPORT_REF_LEN, - HID_SVC_REPORT_REF_LEN, - buf, - ATTR_PERMISSION_NONE, - ATTR_ACCESS_READ_WRITE, - GATT_DONT_NOTIFY_EVENTS, - MIN_ENCRY_KEY_SIZE, - CHAR_VALUE_LEN_CONSTANT, - &(hid_svc->report_ref_desc_handle[i])); - if(status) { - FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); - } - } - } -#endif - // Add Report Map characteristic - char_uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - HID_SVC_REPORT_MAP_MAX_LEN, - CHAR_PROP_READ, - ATTR_PERMISSION_NONE, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_VARIABLE, - &hid_svc->report_map_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add report map characteristic: %d", status); - } - - // Add Information characteristic - char_uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - HID_SVC_INFO_LEN, - CHAR_PROP_READ, - ATTR_PERMISSION_NONE, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &hid_svc->info_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add information characteristic: %d", status); - } - // Add Control Point characteristic - char_uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - HID_SVC_CONTROL_POINT_LEN, - CHAR_PROP_WRITE_WITHOUT_RESP, - ATTR_PERMISSION_NONE, - GATT_NOTIFY_ATTRIBUTE_WRITE, - 10, - CHAR_VALUE_LEN_CONSTANT, - &hid_svc->ctrl_point_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add control point characteristic: %d", status); - } -} - -bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { - furi_assert(data); - furi_assert(hid_svc); - - tBleStatus status = aci_gatt_update_char_value( - hid_svc->svc_handle, hid_svc->report_map_char_handle, 0, len, data); - if(status) { - FURI_LOG_E(TAG, "Failed updating report map characteristic: %d", status); - return false; - } - return true; -} - -bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) { - furi_assert(data); - furi_assert(hid_svc); - - tBleStatus status = aci_gatt_update_char_value( - hid_svc->svc_handle, hid_svc->report_char_handle[input_report_num], 0, len, data); - if(status) { - FURI_LOG_E(TAG, "Failed updating report characteristic: %d", status); - return false; - } - return true; -} - -bool hid_svc_update_info(uint8_t* data, uint16_t len) { - furi_assert(data); - furi_assert(hid_svc); - - tBleStatus status = - aci_gatt_update_char_value(hid_svc->svc_handle, hid_svc->info_char_handle, 0, len, data); - if(status) { - FURI_LOG_E(TAG, "Failed updating info characteristic: %d", status); - return false; - } - return true; -} - -bool hid_svc_is_started() { - return hid_svc != NULL; -} - -void hid_svc_stop() { - tBleStatus status; - if(hid_svc) { - // Delete characteristics - status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_map_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Report Map characteristic: %d", status); - } -#if(HID_SVC_INPUT_REPORT_COUNT != 0) - for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) { - status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_char_handle[i]); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Report characteristic: %d", status); - } - } -#endif - status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->protocol_mode_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Protocol Mode characteristic: %d", status); - } - status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->info_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Information characteristic: %d", status); - } - status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->ctrl_point_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Control Point characteristic: %d", status); - } - // Delete service - status = aci_gatt_del_service(hid_svc->svc_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete HID service: %d", status); - } - // Delete buffer size mutex - free(hid_svc); - hid_svc = NULL; - } -} diff --git a/firmware/targets/f7/ble_glue/hw_conf.h b/firmware/targets/f7/ble_glue/hw_conf.h deleted file mode 100644 index bf18a7d0e55..00000000000 --- a/firmware/targets/f7/ble_glue/hw_conf.h +++ /dev/null @@ -1,231 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file hw_conf.h - * @author MCD Application Team - * @brief Configuration of hardware interface - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2019 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef HW_CONF_H -#define HW_CONF_H - -#include "FreeRTOSConfig.h" - -/****************************************************************************** - * Semaphores - * THIS SHALL NO BE CHANGED AS THESE SEMAPHORES ARE USED AS WELL ON THE CM0+ - *****************************************************************************/ -/** -* Index of the semaphore used the prevent conflicts after standby sleep. -* Each CPUs takes this semaphore at standby wakeup until conclicting elements are restored. -*/ -#define CFG_HW_PWR_STANDBY_SEMID 10 -/** -* The CPU2 may be configured to store the Thread persistent data either in internal NVM storage on CPU2 or in -* SRAM2 buffer provided by the user application. This can be configured with the system command SHCI_C2_Config() -* When the CPU2 is requested to store persistent data in SRAM2, it can write data in this buffer at any time when needed. -* In order to read consistent data with the CPU1 from the SRAM2 buffer, the flow should be: -* + CPU1 takes CFG_HW_THREAD_NVM_SRAM_SEMID semaphore -* + CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these data into an NVM managed by CPU1) -* + CPU1 releases CFG_HW_THREAD_NVM_SRAM_SEMID semaphore -* CFG_HW_THREAD_NVM_SRAM_SEMID semaphore makes sure CPU2 does not update the persistent data in SRAM2 at the same time CPU1 is reading them. -* There is no timing constraint on how long this semaphore can be kept. -*/ -#define CFG_HW_THREAD_NVM_SRAM_SEMID 9 - -/** -* The CPU2 may be configured to store the BLE persistent data either in internal NVM storage on CPU2 or in -* SRAM2 buffer provided by the user application. This can be configured with the system command SHCI_C2_Config() -* When the CPU2 is requested to store persistent data in SRAM2, it can write data in this buffer at any time when needed. -* In order to read consistent data with the CPU1 from the SRAM2 buffer, the flow should be: -* + CPU1 takes CFG_HW_BLE_NVM_SRAM_SEMID semaphore -* + CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these data into an NVM managed by CPU1) -* + CPU1 releases CFG_HW_BLE_NVM_SRAM_SEMID semaphore -* CFG_HW_BLE_NVM_SRAM_SEMID semaphore makes sure CPU2 does not update the persistent data in SRAM2 at the same time CPU1 is reading them. -* There is no timing constraint on how long this semaphore can be kept. -*/ -#define CFG_HW_BLE_NVM_SRAM_SEMID 8 - -/** -* Index of the semaphore used by CPU2 to prevent the CPU1 to either write or erase data in flash -* The CPU1 shall not either write or erase in flash when this semaphore is taken by the CPU2 -* When the CPU1 needs to either write or erase in flash, it shall first get the semaphore and release it just -* after writing a raw (64bits data) or erasing one sector. -* Once the Semaphore has been released, there shall be at least 1us before it can be taken again. This is required -* to give the opportunity to CPU2 to take it. -* On v1.4.0 and older CPU2 wireless firmware, this semaphore is unused and CPU2 is using PES bit. -* By default, CPU2 is using the PES bit to protect its timing. The CPU1 may request the CPU2 to use the semaphore -* instead of the PES bit by sending the system command SHCI_C2_SetFlashActivityControl() -*/ -#define CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID 7 - -/** -* Index of the semaphore used by CPU1 to prevent the CPU2 to either write or erase data in flash -* In order to protect its timing, the CPU1 may get this semaphore to prevent the CPU2 to either -* write or erase in flash (as this will stall both CPUs) -* The PES bit shall not be used as this may stall the CPU2 in some cases. -*/ -#define CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID 6 - -/** -* Index of the semaphore used to manage the CLK48 clock configuration -* When the USB is required, this semaphore shall be taken before configuring te CLK48 for USB -* and should be released after the application switch OFF the clock when the USB is not used anymore -* When using the RNG, it is good enough to use CFG_HW_RNG_SEMID to control CLK48. -* More details in AN5289 -*/ -#define CFG_HW_CLK48_CONFIG_SEMID 5 - -/* Index of the semaphore used to manage the entry Stop Mode procedure */ -#define CFG_HW_ENTRY_STOP_MODE_SEMID 4 - -/* Index of the semaphore used to access the RCC */ -#define CFG_HW_RCC_SEMID 3 - -/* Index of the semaphore used to access the FLASH */ -#define CFG_HW_FLASH_SEMID 2 - -/* Index of the semaphore used to access the PKA */ -#define CFG_HW_PKA_SEMID 1 - -/* Index of the semaphore used to access the RNG */ -#define CFG_HW_RNG_SEMID 0 - -/****************************************************************************** - * HW TIMER SERVER - *****************************************************************************/ -/** - * The user may define the maximum number of virtual timers supported. - * It shall not exceed 255 - */ -#define CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER 6 - -/** - * The user may define the priority in the NVIC of the RTC_WKUP interrupt handler that is used to manage the - * wakeup timer. - * This setting is the preemptpriority part of the NVIC. - */ -#define CFG_HW_TS_NVIC_RTC_WAKEUP_IT_PREEMPTPRIO \ - (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1) /* FreeRTOS requirement */ - -/** - * The user may define the priority in the NVIC of the RTC_WKUP interrupt handler that is used to manage the - * wakeup timer. - * This setting is the subpriority part of the NVIC. It does not exist on all processors. When it is not supported - * on the CPU, the setting is ignored - */ -#define CFG_HW_TS_NVIC_RTC_WAKEUP_IT_SUBPRIO 0 - -/** - * Define a critical section in the Timer server - * The Timer server does not support the API to be nested - * The Application shall either: - * a) Ensure this will never happen - * b) Define the critical section - * The default implementations is masking all interrupts using the PRIMASK bit - * The TimerServer driver uses critical sections to avoid context corruption. This is achieved with the macro - * TIMER_ENTER_CRITICAL_SECTION and TIMER_EXIT_CRITICAL_SECTION. When CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION is set - * to 1, all STM32 interrupts are masked with the PRIMASK bit of the CortexM CPU. It is possible to use the BASEPRI - * register of the CortexM CPU to keep allowed some interrupts with high priority. In that case, the user shall - * re-implement TIMER_ENTER_CRITICAL_SECTION and TIMER_EXIT_CRITICAL_SECTION and shall make sure that no TimerServer - * API are called when the TIMER critical section is entered - */ -#define CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION 1 - -/** - * This value shall reflect the maximum delay there could be in the application between the time the RTC interrupt - * is generated by the Hardware and the time when the RTC interrupt handler is called. This time is measured in - * number of RTCCLK ticks. - * A relaxed timing would be 10ms - * When the value is too short, the timerserver will not be able to count properly and all timeout may be random. - * When the value is too long, the device may wake up more often than the most optimal configuration. However, the - * impact on power consumption would be marginal (unless the value selected is extremely too long). It is strongly - * recommended to select a value large enough to make sure it is not too short to ensure reliability of the system - * as this will have marginal impact on low power mode - */ -#define CFG_HW_TS_RTC_HANDLER_MAX_DELAY (10 * (LSI_VALUE / 1000)) - -/** - * Interrupt ID in the NVIC of the RTC Wakeup interrupt handler - * It shall be type of IRQn_Type - */ -#define CFG_HW_TS_RTC_WAKEUP_HANDLER_ID RTC_WKUP_IRQn - -/****************************************************************************** - * HW UART - *****************************************************************************/ -#define CFG_HW_LPUART1_ENABLED 0 -#define CFG_HW_LPUART1_DMA_TX_SUPPORTED 0 - -#define CFG_HW_USART1_ENABLED 1 -#define CFG_HW_USART1_DMA_TX_SUPPORTED 1 - -/** - * UART1 - */ -#define CFG_HW_USART1_PREEMPTPRIORITY 0x0F -#define CFG_HW_USART1_SUBPRIORITY 0 - -/** < The application shall check the selected source clock is enable */ -#define CFG_HW_USART1_SOURCE_CLOCK RCC_USART1CLKSOURCE_SYSCLK - -#define CFG_HW_USART1_BAUDRATE 115200 -#define CFG_HW_USART1_WORDLENGTH UART_WORDLENGTH_8B -#define CFG_HW_USART1_STOPBITS UART_STOPBITS_1 -#define CFG_HW_USART1_PARITY UART_PARITY_NONE -#define CFG_HW_USART1_HWFLOWCTL UART_HWCONTROL_NONE -#define CFG_HW_USART1_MODE UART_MODE_TX_RX -#define CFG_HW_USART1_ADVFEATUREINIT UART_ADVFEATURE_NO_INIT -#define CFG_HW_USART1_OVERSAMPLING UART_OVERSAMPLING_8 - -#define CFG_HW_USART1_TX_PORT_CLK_ENABLE __HAL_RCC_GPIOB_CLK_ENABLE -#define CFG_HW_USART1_TX_PORT GPIOB -#define CFG_HW_USART1_TX_PIN GPIO_PIN_6 -#define CFG_HW_USART1_TX_MODE GPIO_MODE_AF_PP -#define CFG_HW_USART1_TX_PULL GPIO_NOPULL -#define CFG_HW_USART1_TX_SPEED GPIO_SPEED_FREQ_VERY_HIGH -#define CFG_HW_USART1_TX_ALTERNATE GPIO_AF7_USART1 - -#define CFG_HW_USART1_RX_PORT_CLK_ENABLE __HAL_RCC_GPIOB_CLK_ENABLE -#define CFG_HW_USART1_RX_PORT GPIOB -#define CFG_HW_USART1_RX_PIN GPIO_PIN_7 -#define CFG_HW_USART1_RX_MODE GPIO_MODE_AF_PP -#define CFG_HW_USART1_RX_PULL GPIO_NOPULL -#define CFG_HW_USART1_RX_SPEED GPIO_SPEED_FREQ_VERY_HIGH -#define CFG_HW_USART1_RX_ALTERNATE GPIO_AF7_USART1 - -#define CFG_HW_USART1_CTS_PORT_CLK_ENABLE __HAL_RCC_GPIOA_CLK_ENABLE -#define CFG_HW_USART1_CTS_PORT GPIOA -#define CFG_HW_USART1_CTS_PIN GPIO_PIN_11 -#define CFG_HW_USART1_CTS_MODE GPIO_MODE_AF_PP -#define CFG_HW_USART1_CTS_PULL GPIO_PULLDOWN -#define CFG_HW_USART1_CTS_SPEED GPIO_SPEED_FREQ_VERY_HIGH -#define CFG_HW_USART1_CTS_ALTERNATE GPIO_AF7_USART1 - -#define CFG_HW_USART1_DMA_TX_PREEMPTPRIORITY 0x0F -#define CFG_HW_USART1_DMA_TX_SUBPRIORITY 0 - -#define CFG_HW_USART1_DMAMUX_CLK_ENABLE __HAL_RCC_DMAMUX1_CLK_ENABLE -#define CFG_HW_USART1_DMA_CLK_ENABLE __HAL_RCC_DMA2_CLK_ENABLE -#define CFG_HW_USART1_TX_DMA_REQ DMA_REQUEST_USART1_TX -#define CFG_HW_USART1_TX_DMA_CHANNEL DMA2_Channel4 -#define CFG_HW_USART1_TX_DMA_IRQn DMA2_Channel4_IRQn -#define CFG_HW_USART1_DMA_TX_IRQHandler DMA2_Channel4_IRQHandler - -#endif /*HW_CONF_H */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/ble_glue/hw_if.h b/firmware/targets/f7/ble_glue/hw_if.h deleted file mode 100644 index a0ac23df374..00000000000 --- a/firmware/targets/f7/ble_glue/hw_if.h +++ /dev/null @@ -1,102 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file hw_if.h - * @author MCD Application Team - * @brief Hardware Interface - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2019 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef HW_IF_H -#define HW_IF_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include "stm32wbxx.h" -#include "stm32wbxx_ll_exti.h" -#include "stm32wbxx_ll_system.h" -#include "stm32wbxx_ll_rcc.h" -#include "stm32wbxx_ll_ipcc.h" -#include "stm32wbxx_ll_bus.h" -#include "stm32wbxx_ll_pwr.h" -#include "stm32wbxx_ll_cortex.h" -#include "stm32wbxx_ll_utils.h" -#include "stm32wbxx_ll_hsem.h" -#include "stm32wbxx_ll_gpio.h" -#include "stm32wbxx_ll_rtc.h" - -#ifdef USE_STM32WBXX_USB_DONGLE -#include "stm32wbxx_usb_dongle.h" -#endif -#ifdef USE_STM32WBXX_NUCLEO -#include "stm32wbxx_nucleo.h" -#endif -#ifdef USE_X_NUCLEO_EPD -#include "x_nucleo_epd.h" -#endif - -/* Private includes ----------------------------------------------------------*/ -/* USER CODE BEGIN Includes */ - -/* USER CODE END Includes */ - -/****************************************************************************** - * HW UART - ******************************************************************************/ -typedef enum { - hw_uart1, - hw_uart2, - hw_lpuart1, -} hw_uart_id_t; - -typedef enum { - hw_uart_ok, - hw_uart_error, - hw_uart_busy, - hw_uart_to, -} hw_status_t; - -void HW_UART_Init(hw_uart_id_t hw_uart_id); -void HW_UART_Receive_IT( - hw_uart_id_t hw_uart_id, - uint8_t* pData, - uint16_t Size, - void (*Callback)(void)); -void HW_UART_Transmit_IT( - hw_uart_id_t hw_uart_id, - uint8_t* pData, - uint16_t Size, - void (*Callback)(void)); -hw_status_t - HW_UART_Transmit(hw_uart_id_t hw_uart_id, uint8_t* p_data, uint16_t size, uint32_t timeout); -hw_status_t HW_UART_Transmit_DMA( - hw_uart_id_t hw_uart_id, - uint8_t* p_data, - uint16_t size, - void (*Callback)(void)); -void HW_UART_Interrupt_Handler(hw_uart_id_t hw_uart_id); -void HW_UART_DMA_Interrupt_Handler(hw_uart_id_t hw_uart_id); - -#ifdef __cplusplus -} -#endif - -#endif /*HW_IF_H */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/ble_glue/hw_ipcc.c b/firmware/targets/f7/ble_glue/hw_ipcc.c deleted file mode 100644 index ccdb0736e8b..00000000000 --- a/firmware/targets/f7/ble_glue/hw_ipcc.c +++ /dev/null @@ -1,593 +0,0 @@ -/** - ****************************************************************************** - * File Name : Target/hw_ipcc.c - * Description : Hardware IPCC source file for STM32WPAN Middleware. - * - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2020 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ - -/* Includes ------------------------------------------------------------------*/ -#include "app_common.h" -#include "mbox_def.h" - -/* Global variables ---------------------------------------------------------*/ -/* Private defines -----------------------------------------------------------*/ -#define HW_IPCC_TX_PENDING(channel) \ - (!(LL_C1_IPCC_IsActiveFlag_CHx(IPCC, channel))) && (((~(IPCC->C1MR)) & (channel << 16U))) -#define HW_IPCC_RX_PENDING(channel) \ - (LL_C2_IPCC_IsActiveFlag_CHx(IPCC, channel)) && (((~(IPCC->C1MR)) & (channel << 0U))) - -/* Private macros ------------------------------------------------------------*/ -/* Private typedef -----------------------------------------------------------*/ -/* Private variables ---------------------------------------------------------*/ -static void (*FreeBufCb)(void); - -/* Private function prototypes -----------------------------------------------*/ -static void HW_IPCC_BLE_EvtHandler(void); -static void HW_IPCC_BLE_AclDataEvtHandler(void); -static void HW_IPCC_MM_FreeBufHandler(void); -static void HW_IPCC_SYS_CmdEvtHandler(void); -static void HW_IPCC_SYS_EvtHandler(void); -static void HW_IPCC_TRACES_EvtHandler(void); - -#ifdef THREAD_WB -static void HW_IPCC_OT_CmdEvtHandler(void); -static void HW_IPCC_THREAD_NotEvtHandler(void); -static void HW_IPCC_THREAD_CliNotEvtHandler(void); -#endif - -#ifdef LLD_TESTS_WB -static void HW_IPCC_LLDTESTS_ReceiveCliRspHandler(void); -static void HW_IPCC_LLDTESTS_ReceiveM0CmdHandler(void); -#endif -#ifdef LLD_BLE_WB -/*static void HW_IPCC_LLD_BLE_ReceiveCliRspHandler( void );*/ -static void HW_IPCC_LLD_BLE_ReceiveRspHandler(void); -static void HW_IPCC_LLD_BLE_ReceiveM0CmdHandler(void); -#endif - -#ifdef MAC_802_15_4_WB -static void HW_IPCC_MAC_802_15_4_CmdEvtHandler(void); -static void HW_IPCC_MAC_802_15_4_NotEvtHandler(void); -#endif - -#ifdef ZIGBEE_WB -static void HW_IPCC_ZIGBEE_CmdEvtHandler(void); -static void HW_IPCC_ZIGBEE_StackNotifEvtHandler(void); -static void HW_IPCC_ZIGBEE_StackM0RequestHandler(void); -#endif - -/* Public function definition -----------------------------------------------*/ - -/****************************************************************************** - * INTERRUPT HANDLER - ******************************************************************************/ -void HW_IPCC_Rx_Handler(void) { - if(HW_IPCC_RX_PENDING(HW_IPCC_SYSTEM_EVENT_CHANNEL)) { - HW_IPCC_SYS_EvtHandler(); - } -#ifdef MAC_802_15_4_WB - else if(HW_IPCC_RX_PENDING(HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL)) { - HW_IPCC_MAC_802_15_4_NotEvtHandler(); - } -#endif /* MAC_802_15_4_WB */ -#ifdef THREAD_WB - else if(HW_IPCC_RX_PENDING(HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL)) { - HW_IPCC_THREAD_NotEvtHandler(); - } else if(HW_IPCC_RX_PENDING(HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL)) { - HW_IPCC_THREAD_CliNotEvtHandler(); - } -#endif /* THREAD_WB */ -#ifdef LLD_TESTS_WB - else if(HW_IPCC_RX_PENDING(HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL)) { - HW_IPCC_LLDTESTS_ReceiveCliRspHandler(); - } else if(HW_IPCC_RX_PENDING(HW_IPCC_LLDTESTS_M0_CMD_CHANNEL)) { - HW_IPCC_LLDTESTS_ReceiveM0CmdHandler(); - } -#endif /* LLD_TESTS_WB */ -#ifdef LLD_BLE_WB - else if(HW_IPCC_RX_PENDING(HW_IPCC_LLD_BLE_RSP_CHANNEL)) { - HW_IPCC_LLD_BLE_ReceiveRspHandler(); - } else if(HW_IPCC_RX_PENDING(HW_IPCC_LLD_BLE_M0_CMD_CHANNEL)) { - HW_IPCC_LLD_BLE_ReceiveM0CmdHandler(); - } -#endif /* LLD_TESTS_WB */ -#ifdef ZIGBEE_WB - else if(HW_IPCC_RX_PENDING(HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL)) { - HW_IPCC_ZIGBEE_StackNotifEvtHandler(); - } else if(HW_IPCC_RX_PENDING(HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL)) { - HW_IPCC_ZIGBEE_StackM0RequestHandler(); - } -#endif /* ZIGBEE_WB */ - else if(HW_IPCC_RX_PENDING(HW_IPCC_BLE_EVENT_CHANNEL)) { - HW_IPCC_BLE_EvtHandler(); - } else if(HW_IPCC_RX_PENDING(HW_IPCC_TRACES_CHANNEL)) { - HW_IPCC_TRACES_EvtHandler(); - } - - return; -} - -void HW_IPCC_Tx_Handler(void) { - if(HW_IPCC_TX_PENDING(HW_IPCC_SYSTEM_CMD_RSP_CHANNEL)) { - HW_IPCC_SYS_CmdEvtHandler(); - } -#ifdef MAC_802_15_4_WB - else if(HW_IPCC_TX_PENDING(HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL)) { - HW_IPCC_MAC_802_15_4_CmdEvtHandler(); - } -#endif /* MAC_802_15_4_WB */ -#ifdef THREAD_WB - else if(HW_IPCC_TX_PENDING(HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL)) { - HW_IPCC_OT_CmdEvtHandler(); - } -#endif /* THREAD_WB */ -#ifdef LLD_TESTS_WB -// No TX handler for LLD tests -#endif /* LLD_TESTS_WB */ -#ifdef ZIGBEE_WB - if(HW_IPCC_TX_PENDING(HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL)) { - HW_IPCC_ZIGBEE_CmdEvtHandler(); - } -#endif /* ZIGBEE_WB */ - else if(HW_IPCC_TX_PENDING(HW_IPCC_SYSTEM_CMD_RSP_CHANNEL)) { - HW_IPCC_SYS_CmdEvtHandler(); - } else if(HW_IPCC_TX_PENDING(HW_IPCC_MM_RELEASE_BUFFER_CHANNEL)) { - HW_IPCC_MM_FreeBufHandler(); - } else if(HW_IPCC_TX_PENDING(HW_IPCC_HCI_ACL_DATA_CHANNEL)) { - HW_IPCC_BLE_AclDataEvtHandler(); - } - - return; -} -/****************************************************************************** - * GENERAL - ******************************************************************************/ -void HW_IPCC_Enable(void) { - /** - * Such as IPCC IP available to the CPU2, it is required to keep the IPCC clock running - when FUS is running on CPU2 and CPU1 enters deep sleep mode - */ - /** - * When the device is out of standby, it is required to use the EXTI mechanism to wakeup CPU2 - */ - LL_C2_EXTI_EnableEvent_32_63(LL_EXTI_LINE_41); - LL_EXTI_EnableRisingTrig_32_63(LL_EXTI_LINE_41); - - /** - * In case the SBSFU is implemented, it may have already set the C2BOOT bit to startup the CPU2. - * In that case, to keep the mechanism transparent to the user application, it shall call the system command - * SHCI_C2_Reinit( ) before jumping to the application. - * When the CPU2 receives that command, it waits for its event input to be set to restart the CPU2 firmware. - * This is required because once C2BOOT has been set once, a clear/set on C2BOOT has no effect. - * When SHCI_C2_Reinit( ) is not called, generating an event to the CPU2 does not have any effect - * So, by default, the application shall both set the event flag and set the C2BOOT bit. - */ - __SEV(); /* Set the internal event flag and send an event to the CPU2 */ - __WFE(); /* Clear the internal event flag */ - LL_PWR_EnableBootC2(); - - return; -} - -void HW_IPCC_Init(void) { - LL_C1_IPCC_EnableIT_RXO(IPCC); - LL_C1_IPCC_EnableIT_TXF(IPCC); - - NVIC_SetPriority(IPCC_C1_RX_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 6, 0)); - NVIC_EnableIRQ(IPCC_C1_RX_IRQn); - NVIC_SetPriority(IPCC_C1_TX_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 6, 0)); - NVIC_EnableIRQ(IPCC_C1_TX_IRQn); - - return; -} - -/****************************************************************************** - * BLE - ******************************************************************************/ -void HW_IPCC_BLE_Init(void) { - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_BLE_EVENT_CHANNEL); - - return; -} - -void HW_IPCC_BLE_SendCmd(void) { - LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_BLE_CMD_CHANNEL); - - return; -} - -static void HW_IPCC_BLE_EvtHandler(void) { - HW_IPCC_BLE_RxEvtNot(); - - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_BLE_EVENT_CHANNEL); - - return; -} - -void HW_IPCC_BLE_SendAclData(void) { - LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_HCI_ACL_DATA_CHANNEL); - LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_HCI_ACL_DATA_CHANNEL); - - return; -} - -static void HW_IPCC_BLE_AclDataEvtHandler(void) { - LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_HCI_ACL_DATA_CHANNEL); - - HW_IPCC_BLE_AclDataAckNot(); - - return; -} - -__weak void HW_IPCC_BLE_AclDataAckNot(void){}; -__weak void HW_IPCC_BLE_RxEvtNot(void){}; - -/****************************************************************************** - * SYSTEM - ******************************************************************************/ -void HW_IPCC_SYS_Init(void) { - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_SYSTEM_EVENT_CHANNEL); - - return; -} - -void HW_IPCC_SYS_SendCmd(void) { - LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_SYSTEM_CMD_RSP_CHANNEL); - LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_SYSTEM_CMD_RSP_CHANNEL); - - return; -} - -static void HW_IPCC_SYS_CmdEvtHandler(void) { - LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_SYSTEM_CMD_RSP_CHANNEL); - - HW_IPCC_SYS_CmdEvtNot(); - - return; -} - -static void HW_IPCC_SYS_EvtHandler(void) { - HW_IPCC_SYS_EvtNot(); - - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_SYSTEM_EVENT_CHANNEL); - - return; -} - -__weak void HW_IPCC_SYS_CmdEvtNot(void){}; -__weak void HW_IPCC_SYS_EvtNot(void){}; - -/****************************************************************************** - * MAC 802.15.4 - ******************************************************************************/ -#ifdef MAC_802_15_4_WB -void HW_IPCC_MAC_802_15_4_Init(void) { - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL); - - return; -} - -void HW_IPCC_MAC_802_15_4_SendCmd(void) { - LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL); - LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL); - - return; -} - -void HW_IPCC_MAC_802_15_4_SendAck(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL); - - return; -} - -static void HW_IPCC_MAC_802_15_4_CmdEvtHandler(void) { - LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_MAC_802_15_4_CMD_RSP_CHANNEL); - - HW_IPCC_MAC_802_15_4_CmdEvtNot(); - - return; -} - -static void HW_IPCC_MAC_802_15_4_NotEvtHandler(void) { - LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL); - - HW_IPCC_MAC_802_15_4_EvtNot(); - - return; -} -__weak void HW_IPCC_MAC_802_15_4_CmdEvtNot(void){}; -__weak void HW_IPCC_MAC_802_15_4_EvtNot(void){}; -#endif - -/****************************************************************************** - * THREAD - ******************************************************************************/ -#ifdef THREAD_WB -void HW_IPCC_THREAD_Init(void) { - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL); - - return; -} - -void HW_IPCC_OT_SendCmd(void) { - LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL); - LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL); - - return; -} - -void HW_IPCC_CLI_SendCmd(void) { - LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_THREAD_CLI_CMD_CHANNEL); - - return; -} - -void HW_IPCC_THREAD_SendAck(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL); - - return; -} - -void HW_IPCC_THREAD_CliSendAck(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL); - - return; -} - -static void HW_IPCC_OT_CmdEvtHandler(void) { - LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_THREAD_OT_CMD_RSP_CHANNEL); - - HW_IPCC_OT_CmdEvtNot(); - - return; -} - -static void HW_IPCC_THREAD_NotEvtHandler(void) { - LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL); - - HW_IPCC_THREAD_EvtNot(); - - return; -} - -static void HW_IPCC_THREAD_CliNotEvtHandler(void) { - LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL); - - HW_IPCC_THREAD_CliEvtNot(); - - return; -} - -__weak void HW_IPCC_OT_CmdEvtNot(void){}; -__weak void HW_IPCC_CLI_CmdEvtNot(void){}; -__weak void HW_IPCC_THREAD_EvtNot(void){}; - -#endif /* THREAD_WB */ - -/****************************************************************************** - * LLD TESTS - ******************************************************************************/ -#ifdef LLD_TESTS_WB -void HW_IPCC_LLDTESTS_Init(void) { - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_M0_CMD_CHANNEL); - return; -} - -void HW_IPCC_LLDTESTS_SendCliCmd(void) { - LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_LLDTESTS_CLI_CMD_CHANNEL); - return; -} - -static void HW_IPCC_LLDTESTS_ReceiveCliRspHandler(void) { - LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL); - HW_IPCC_LLDTESTS_ReceiveCliRsp(); - return; -} - -void HW_IPCC_LLDTESTS_SendCliRspAck(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_CLI_RSP_CHANNEL); - return; -} - -static void HW_IPCC_LLDTESTS_ReceiveM0CmdHandler(void) { - LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_M0_CMD_CHANNEL); - HW_IPCC_LLDTESTS_ReceiveM0Cmd(); - return; -} - -void HW_IPCC_LLDTESTS_SendM0CmdAck(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLDTESTS_M0_CMD_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLDTESTS_M0_CMD_CHANNEL); - return; -} -__weak void HW_IPCC_LLDTESTS_ReceiveCliRsp(void){}; -__weak void HW_IPCC_LLDTESTS_ReceiveM0Cmd(void){}; -#endif /* LLD_TESTS_WB */ - -/****************************************************************************** - * LLD BLE - ******************************************************************************/ -#ifdef LLD_BLE_WB -void HW_IPCC_LLD_BLE_Init(void) { - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_RSP_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_M0_CMD_CHANNEL); - return; -} - -void HW_IPCC_LLD_BLE_SendCliCmd(void) { - LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_LLD_BLE_CLI_CMD_CHANNEL); - return; -} - -/*static void HW_IPCC_LLD_BLE_ReceiveCliRspHandler( void ) -{ - LL_C1_IPCC_DisableReceiveChannel( IPCC, HW_IPCC_LLD_BLE_CLI_RSP_CHANNEL ); - HW_IPCC_LLD_BLE_ReceiveCliRsp(); - return; -}*/ - -void HW_IPCC_LLD_BLE_SendCliRspAck(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLD_BLE_CLI_RSP_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_CLI_RSP_CHANNEL); - return; -} - -static void HW_IPCC_LLD_BLE_ReceiveM0CmdHandler(void) { - //LL_C1_IPCC_DisableReceiveChannel( IPCC, HW_IPCC_LLD_BLE_M0_CMD_CHANNEL ); - HW_IPCC_LLD_BLE_ReceiveM0Cmd(); - return; -} - -void HW_IPCC_LLD_BLE_SendM0CmdAck(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLD_BLE_M0_CMD_CHANNEL); - //LL_C1_IPCC_EnableReceiveChannel( IPCC, HW_IPCC_LLD_BLE_M0_CMD_CHANNEL ); - return; -} -__weak void HW_IPCC_LLD_BLE_ReceiveCliRsp(void){}; -__weak void HW_IPCC_LLD_BLE_ReceiveM0Cmd(void){}; - -/* Transparent Mode */ -void HW_IPCC_LLD_BLE_SendCmd(void) { - LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_LLD_BLE_CMD_CHANNEL); - return; -} - -static void HW_IPCC_LLD_BLE_ReceiveRspHandler(void) { - LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_RSP_CHANNEL); - HW_IPCC_LLD_BLE_ReceiveRsp(); - return; -} - -void HW_IPCC_LLD_BLE_SendRspAck(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_LLD_BLE_RSP_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_LLD_BLE_RSP_CHANNEL); - return; -} - -#endif /* LLD_BLE_WB */ - -/****************************************************************************** - * ZIGBEE - ******************************************************************************/ -#ifdef ZIGBEE_WB -void HW_IPCC_ZIGBEE_Init(void) { - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL); - - return; -} - -void HW_IPCC_ZIGBEE_SendM4RequestToM0(void) { - LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL); - LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL); - - return; -} - -void HW_IPCC_ZIGBEE_SendM4AckToM0Notify(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL); - - return; -} - -static void HW_IPCC_ZIGBEE_CmdEvtHandler(void) { - LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_ZIGBEE_CMD_APPLI_CHANNEL); - - HW_IPCC_ZIGBEE_RecvAppliAckFromM0(); - - return; -} - -static void HW_IPCC_ZIGBEE_StackNotifEvtHandler(void) { - LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_APPLI_NOTIF_ACK_CHANNEL); - - HW_IPCC_ZIGBEE_RecvM0NotifyToM4(); - - return; -} - -static void HW_IPCC_ZIGBEE_StackM0RequestHandler(void) { - LL_C1_IPCC_DisableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL); - - HW_IPCC_ZIGBEE_RecvM0RequestToM4(); - - return; -} - -void HW_IPCC_ZIGBEE_SendM4AckToM0Request(void) { - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL); - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_ZIGBEE_M0_REQUEST_CHANNEL); - - return; -} - -__weak void HW_IPCC_ZIGBEE_RecvAppliAckFromM0(void){}; -__weak void HW_IPCC_ZIGBEE_RecvM0NotifyToM4(void){}; -__weak void HW_IPCC_ZIGBEE_RecvM0RequestToM4(void){}; -#endif /* ZIGBEE_WB */ - -/****************************************************************************** - * MEMORY MANAGER - ******************************************************************************/ -void HW_IPCC_MM_SendFreeBuf(void (*cb)(void)) { - if(LL_C1_IPCC_IsActiveFlag_CHx(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL)) { - FreeBufCb = cb; - LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL); - } else { - cb(); - - LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL); - } - - return; -} - -static void HW_IPCC_MM_FreeBufHandler(void) { - LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL); - - FreeBufCb(); - - LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL); - - return; -} - -/****************************************************************************** - * TRACES - ******************************************************************************/ -void HW_IPCC_TRACES_Init(void) { - LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_TRACES_CHANNEL); - - return; -} - -static void HW_IPCC_TRACES_EvtHandler(void) { - HW_IPCC_TRACES_EvtNot(); - - LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_TRACES_CHANNEL); - - return; -} - -__weak void HW_IPCC_TRACES_EvtNot(void){}; - -/******************* (C) COPYRIGHT 2019 STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/ble_glue/serial_service.c b/firmware/targets/f7/ble_glue/serial_service.c deleted file mode 100644 index 91e12dd688e..00000000000 --- a/firmware/targets/f7/ble_glue/serial_service.c +++ /dev/null @@ -1,244 +0,0 @@ -#include "serial_service.h" -#include "app_common.h" -#include "ble.h" - -#include - -#define TAG "BtSerialSvc" - -typedef struct { - uint16_t svc_handle; - uint16_t rx_char_handle; - uint16_t tx_char_handle; - uint16_t flow_ctrl_char_handle; - FuriMutex* buff_size_mtx; - uint32_t buff_size; - uint16_t bytes_ready_to_receive; - SerialServiceEventCallback callback; - void* context; -} SerialSvc; - -static SerialSvc* serial_svc = NULL; - -static const uint8_t service_uuid[] = - {0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f}; -static const uint8_t char_tx_uuid[] = - {0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; -static const uint8_t char_rx_uuid[] = - {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; -static const uint8_t flow_ctrl_uuid[] = - {0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; - -static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { - SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; - hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); - evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; - aci_gatt_attribute_modified_event_rp0* attribute_modified; - if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { - if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { - attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data; - if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 2) { - // Descriptor handle - ret = SVCCTL_EvtAckFlowEnable; - FURI_LOG_D(TAG, "RX descriptor event"); - } else if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 1) { - FURI_LOG_D(TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); - if(serial_svc->callback) { - furi_check( - furi_mutex_acquire(serial_svc->buff_size_mtx, FuriWaitForever) == - FuriStatusOk); - if(attribute_modified->Attr_Data_Length > serial_svc->bytes_ready_to_receive) { - FURI_LOG_W( - TAG, - "Received %d, while was ready to receive %d bytes. Can lead to buffer overflow!", - attribute_modified->Attr_Data_Length, - serial_svc->bytes_ready_to_receive); - } - serial_svc->bytes_ready_to_receive -= MIN( - serial_svc->bytes_ready_to_receive, attribute_modified->Attr_Data_Length); - SerialServiceEvent event = { - .event = SerialServiceEventTypeDataReceived, - .data = { - .buffer = attribute_modified->Attr_Data, - .size = attribute_modified->Attr_Data_Length, - }}; - uint32_t buff_free_size = serial_svc->callback(event, serial_svc->context); - FURI_LOG_D(TAG, "Available buff size: %d", buff_free_size); - furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); - } - ret = SVCCTL_EvtAckFlowEnable; - } - } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { - FURI_LOG_T(TAG, "Ack received", blecore_evt->ecode); - if(serial_svc->callback) { - SerialServiceEvent event = { - .event = SerialServiceEventTypeDataSent, - }; - serial_svc->callback(event, serial_svc->context); - } - ret = SVCCTL_EvtAckFlowEnable; - } - } - return ret; -} - -void serial_svc_start() { - tBleStatus status; - serial_svc = malloc(sizeof(SerialSvc)); - // Register event handler - SVCCTL_RegisterSvcHandler(serial_svc_event_handler); - - // Add service - status = aci_gatt_add_service( - UUID_TYPE_128, (Service_UUID_t*)service_uuid, PRIMARY_SERVICE, 10, &serial_svc->svc_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add Serial service: %d", status); - } - - // Add RX characteristics - status = aci_gatt_add_char( - serial_svc->svc_handle, - UUID_TYPE_128, - (const Char_UUID_t*)char_rx_uuid, - SERIAL_SVC_DATA_LEN_MAX, - CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, - ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, - GATT_NOTIFY_ATTRIBUTE_WRITE, - 10, - CHAR_VALUE_LEN_VARIABLE, - &serial_svc->rx_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add RX characteristic: %d", status); - } - - // Add TX characteristic - status = aci_gatt_add_char( - serial_svc->svc_handle, - UUID_TYPE_128, - (const Char_UUID_t*)char_tx_uuid, - SERIAL_SVC_DATA_LEN_MAX, - CHAR_PROP_READ | CHAR_PROP_INDICATE, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_VARIABLE, - &serial_svc->tx_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add TX characteristic: %d", status); - } - // Add Flow Control characteristic - status = aci_gatt_add_char( - serial_svc->svc_handle, - UUID_TYPE_128, - (const Char_UUID_t*)flow_ctrl_uuid, - sizeof(uint32_t), - CHAR_PROP_READ | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &serial_svc->flow_ctrl_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add Flow Control characteristic: %d", status); - } - // Allocate buffer size mutex - serial_svc->buff_size_mtx = furi_mutex_alloc(FuriMutexTypeNormal); -} - -void serial_svc_set_callbacks( - uint16_t buff_size, - SerialServiceEventCallback callback, - void* context) { - furi_assert(serial_svc); - serial_svc->callback = callback; - serial_svc->context = context; - serial_svc->buff_size = buff_size; - serial_svc->bytes_ready_to_receive = buff_size; - uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); - aci_gatt_update_char_value( - serial_svc->svc_handle, - serial_svc->flow_ctrl_char_handle, - 0, - sizeof(uint32_t), - (uint8_t*)&buff_size_reversed); -} - -void serial_svc_notify_buffer_is_empty() { - furi_assert(serial_svc); - furi_assert(serial_svc->buff_size_mtx); - - furi_check(furi_mutex_acquire(serial_svc->buff_size_mtx, FuriWaitForever) == FuriStatusOk); - if(serial_svc->bytes_ready_to_receive == 0) { - FURI_LOG_D(TAG, "Buffer is empty. Notifying client"); - serial_svc->bytes_ready_to_receive = serial_svc->buff_size; - uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); - aci_gatt_update_char_value( - serial_svc->svc_handle, - serial_svc->flow_ctrl_char_handle, - 0, - sizeof(uint32_t), - (uint8_t*)&buff_size_reversed); - } - furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); -} - -void serial_svc_stop() { - tBleStatus status; - if(serial_svc) { - // Delete characteristics - status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->tx_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete TX characteristic: %d", status); - } - status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rx_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete RX characteristic: %d", status); - } - status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->flow_ctrl_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Flow Control characteristic: %d", status); - } - // Delete service - status = aci_gatt_del_service(serial_svc->svc_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Serial service: %d", status); - } - // Delete buffer size mutex - furi_mutex_free(serial_svc->buff_size_mtx); - free(serial_svc); - serial_svc = NULL; - } -} - -bool serial_svc_is_started() { - return serial_svc != NULL; -} - -bool serial_svc_update_tx(uint8_t* data, uint16_t data_len) { - if(data_len > SERIAL_SVC_DATA_LEN_MAX) { - return false; - } - - for(uint16_t remained = data_len; remained > 0;) { - uint8_t value_len = MIN(SERIAL_SVC_CHAR_VALUE_LEN_MAX, remained); - uint16_t value_offset = data_len - remained; - remained -= value_len; - - tBleStatus result = aci_gatt_update_char_value_ext( - 0, - serial_svc->svc_handle, - serial_svc->tx_char_handle, - remained ? 0x00 : 0x02, - data_len, - value_offset, - value_len, - data + value_offset); - - if(result) { - FURI_LOG_E(TAG, "Failed updating TX characteristic: %d", result); - return false; - } - } - - return true; -} diff --git a/firmware/targets/f7/ble_glue/tl_dbg_conf.h b/firmware/targets/f7/ble_glue/tl_dbg_conf.h deleted file mode 100644 index ce58af32b8a..00000000000 --- a/firmware/targets/f7/ble_glue/tl_dbg_conf.h +++ /dev/null @@ -1,135 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * File Name : App/tl_dbg_conf.h - * Description : Debug configuration file for stm32wpan transport layer interface. - * - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2020 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __TL_DBG_CONF_H -#define __TL_DBG_CONF_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* USER CODE BEGIN Tl_Conf */ - -/* Includes ------------------------------------------------------------------*/ -#include "app_conf.h" /* required as some configuration used in dbg_trace.h are set there */ -#include "dbg_trace.h" -#include "hw_if.h" - -/** - * Enable or Disable traces - * The raw data output is the hci binary packet format as specified by the BT specification * - */ -#define TL_SHCI_CMD_DBG_EN 1 /* Reports System commands sent to CPU2 and the command response */ -#define TL_SHCI_CMD_DBG_RAW_EN \ - 0 /* Reports raw data System commands sent to CPU2 and the command response */ -#define TL_SHCI_EVT_DBG_EN 1 /* Reports System Asynchronous Events received from CPU2 */ -#define TL_SHCI_EVT_DBG_RAW_EN \ - 0 /* Reports raw data System Asynchronous Events received from CPU2 */ - -#define TL_HCI_CMD_DBG_EN 1 /* Reports BLE command sent to CPU2 and the command response */ -#define TL_HCI_CMD_DBG_RAW_EN \ - 0 /* Reports raw data BLE command sent to CPU2 and the command response */ -#define TL_HCI_EVT_DBG_EN 1 /* Reports BLE Asynchronous Events received from CPU2 */ -#define TL_HCI_EVT_DBG_RAW_EN 0 /* Reports raw data BLE Asynchronous Events received from CPU2 */ - -#define TL_MM_DBG_EN 1 /* Reports the informations of the buffer released to CPU2 */ - -/** - * System Transport Layer - */ -#if(TL_SHCI_CMD_DBG_EN != 0) -#define TL_SHCI_CMD_DBG_MSG PRINT_MESG_DBG -#define TL_SHCI_CMD_DBG_BUF PRINT_LOG_BUFF_DBG -#else -#define TL_SHCI_CMD_DBG_MSG(...) -#define TL_SHCI_CMD_DBG_BUF(...) -#endif - -#if(TL_SHCI_CMD_DBG_RAW_EN != 0) -#define TL_SHCI_CMD_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) -#else -#define TL_SHCI_CMD_DBG_RAW(...) -#endif - -#if(TL_SHCI_EVT_DBG_EN != 0) -#define TL_SHCI_EVT_DBG_MSG PRINT_MESG_DBG -#define TL_SHCI_EVT_DBG_BUF PRINT_LOG_BUFF_DBG -#else -#define TL_SHCI_EVT_DBG_MSG(...) -#define TL_SHCI_EVT_DBG_BUF(...) -#endif - -#if(TL_SHCI_EVT_DBG_RAW_EN != 0) -#define TL_SHCI_EVT_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) -#else -#define TL_SHCI_EVT_DBG_RAW(...) -#endif - -/** - * BLE Transport Layer - */ -#if(TL_HCI_CMD_DBG_EN != 0) -#define TL_HCI_CMD_DBG_MSG PRINT_MESG_DBG -#define TL_HCI_CMD_DBG_BUF PRINT_LOG_BUFF_DBG -#else -#define TL_HCI_CMD_DBG_MSG(...) -#define TL_HCI_CMD_DBG_BUF(...) -#endif - -#if(TL_HCI_CMD_DBG_RAW_EN != 0) -#define TL_HCI_CMD_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) -#else -#define TL_HCI_CMD_DBG_RAW(...) -#endif - -#if(TL_HCI_EVT_DBG_EN != 0) -#define TL_HCI_EVT_DBG_MSG PRINT_MESG_DBG -#define TL_HCI_EVT_DBG_BUF PRINT_LOG_BUFF_DBG -#else -#define TL_HCI_EVT_DBG_MSG(...) -#define TL_HCI_EVT_DBG_BUF(...) -#endif - -#if(TL_HCI_EVT_DBG_RAW_EN != 0) -#define TL_HCI_EVT_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) -#else -#define TL_HCI_EVT_DBG_RAW(...) -#endif - -/** - * Memory Manager - Released buffer tracing - */ -#if(TL_MM_DBG_EN != 0) -#define TL_MM_DBG_MSG PRINT_MESG_DBG -#else -#define TL_MM_DBG_MSG(...) -#endif - -/* USER CODE END Tl_Conf */ - -#ifdef __cplusplus -} -#endif - -#endif /*__TL_DBG_CONF_H */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/ble_glue/utilities_conf.h b/firmware/targets/f7/ble_glue/utilities_conf.h deleted file mode 100644 index 9c15f2263f7..00000000000 --- a/firmware/targets/f7/ble_glue/utilities_conf.h +++ /dev/null @@ -1,68 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * File Name : utilities_conf.h - * Description : Configuration file for STM32 Utilities. - * - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2019 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under BSD 3-Clause license, - * the "License"; You may not use this file except in compliance with the - * License. You may obtain a copy of the License at: - * opensource.org/licenses/BSD-3-Clause - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef UTILITIES_CONF_H -#define UTILITIES_CONF_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "cmsis_compiler.h" -#include "string.h" -#include - -/****************************************************************************** - * common - ******************************************************************************/ -#define UTILS_ENTER_CRITICAL_SECTION() FURI_CRITICAL_ENTER() - -#define UTILS_EXIT_CRITICAL_SECTION() FURI_CRITICAL_EXIT() - -#define UTILS_MEMSET8(dest, value, size) memset(dest, value, size); - -/****************************************************************************** - * tiny low power manager - * (any macro that does not need to be modified can be removed) - ******************************************************************************/ -#define UTIL_LPM_INIT_CRITICAL_SECTION() -#define UTIL_LPM_ENTER_CRITICAL_SECTION() UTILS_ENTER_CRITICAL_SECTION() -#define UTIL_LPM_EXIT_CRITICAL_SECTION() UTILS_EXIT_CRITICAL_SECTION() - -/****************************************************************************** - * sequencer - * (any macro that does not need to be modified can be removed) - ******************************************************************************/ -#define UTIL_SEQ_INIT_CRITICAL_SECTION() -#define UTIL_SEQ_ENTER_CRITICAL_SECTION() UTILS_ENTER_CRITICAL_SECTION() -#define UTIL_SEQ_EXIT_CRITICAL_SECTION() UTILS_EXIT_CRITICAL_SECTION() -#define UTIL_SEQ_CONF_TASK_NBR (32) -#define UTIL_SEQ_CONF_PRIO_NBR (2) -#define UTIL_SEQ_MEMSET8(dest, value, size) UTILS_MEMSET8(dest, value, size) - -#ifdef __cplusplus -} -#endif - -#endif /*UTILITIES_CONF_H */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/fatfs/fatfs.c b/firmware/targets/f7/fatfs/fatfs.c deleted file mode 100644 index 1aa5fe44bcc..00000000000 --- a/firmware/targets/f7/fatfs/fatfs.c +++ /dev/null @@ -1,54 +0,0 @@ -/** - ****************************************************************************** - * @file fatfs.c - * @brief Code for fatfs applications - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2020 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ - -#include "fatfs.h" - -uint8_t retUSER; /* Return value for USER */ -char USERPath[4]; /* USER logical drive path */ -FATFS USERFatFS; /* File system object for USER logical drive */ -FIL USERFile; /* File object for USER */ - -/* USER CODE BEGIN Variables */ - -/* USER CODE END Variables */ - -void MX_FATFS_Init(void) { - /*## FatFS: Link the USER driver ###########################*/ - retUSER = FATFS_LinkDriver(&USER_Driver, USERPath); - - /* USER CODE BEGIN Init */ - /* additional user code for init */ - /* USER CODE END Init */ -} - -/** - * @brief Gets Time from RTC - * @param None - * @retval Time in DWORD - */ -DWORD get_fattime(void) { - /* USER CODE BEGIN get_fattime */ - return 0; - /* USER CODE END get_fattime */ -} - -/* USER CODE BEGIN Application */ - -/* USER CODE END Application */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/fatfs/fatfs.h b/firmware/targets/f7/fatfs/fatfs.h deleted file mode 100644 index a0775d88b3f..00000000000 --- a/firmware/targets/f7/fatfs/fatfs.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - ****************************************************************************** - * @file fatfs.h - * @brief Header for fatfs applications - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2020 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __fatfs_H -#define __fatfs_H -#ifdef __cplusplus -extern "C" { -#endif - -#include "fatfs/ff.h" -#include "fatfs/ff_gen_drv.h" -#include "user_diskio.h" /* defines USER_Driver as external */ - -/* USER CODE BEGIN Includes */ - -/* USER CODE END Includes */ - -extern uint8_t retUSER; /* Return value for USER */ -extern char USERPath[4]; /* USER logical drive path */ -extern FATFS USERFatFS; /* File system object for USER logical drive */ -extern FIL USERFile; /* File object for USER */ - -void MX_FATFS_Init(void); - -/* USER CODE BEGIN Prototypes */ - -/* USER CODE END Prototypes */ -#ifdef __cplusplus -} -#endif -#endif /*__fatfs_H */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 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 bfe046b588d..00000000000 --- 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 6db430a5359..00000000000 --- a/firmware/targets/f7/fatfs/stm32_adafruit_sd.c +++ /dev/null @@ -1,1109 +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 - -/** @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); - - /* 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 *= (1 << (pCardInfo->Csd.version.v1.DeviceSizeMul + 2)); - pCardInfo->LogBlockSize = 512; - pCardInfo->CardBlockSize = 1 << (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; - uint8_t* ptr = NULL; - SD_CmdAnswer_typedef response; - uint16_t BlockSize = 512; - - /* 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; - } - - ptr = malloc(sizeof(uint8_t) * BlockSize); - if(ptr == NULL) { - goto error; - } - memset(ptr, SD_DUMMY_BYTE, sizeof(uint8_t) * BlockSize); - - /* 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(ptr, (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); - } - - retr = BSP_SD_OK; - -error: - /* Send dummy byte: 8 Clock pulses of delay */ - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(ptr != NULL) free(ptr); - - /* 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; - uint8_t* ptr = NULL; - SD_CmdAnswer_typedef response; - uint16_t BlockSize = 512; - - /* 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; - } - - ptr = malloc(sizeof(uint8_t) * BlockSize); - if(ptr == NULL) { - 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, ptr, 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: - if(ptr != NULL) free(ptr); - /* 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 */ - Cid->OEM_AppliID = CID_Tab[1] << 8; - - /* Byte 2 */ - Cid->OEM_AppliID |= CID_Tab[2]; - - /* Byte 3 */ - Cid->ProdName1 = CID_Tab[3] << 24; - - /* Byte 4 */ - Cid->ProdName1 |= CID_Tab[4] << 16; - - /* Byte 5 */ - Cid->ProdName1 |= CID_Tab[5] << 8; - - /* Byte 6 */ - Cid->ProdName1 |= CID_Tab[6]; - - /* Byte 7 */ - Cid->ProdName2 = CID_Tab[7]; - - /* 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->ManufactDate = (CID_Tab[13] & 0x0F) << 8; - - /* Byte 14 */ - Cid->ManufactDate |= CID_Tab[14]; - - /* 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; -} - -/** - * @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 = 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 = 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 e0c5e3beff4..00000000000 --- a/firmware/targets/f7/fatfs/stm32_adafruit_sd.h +++ /dev/null @@ -1,244 +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 { - __IO uint8_t ManufacturerID; /* ManufacturerID */ - __IO uint16_t OEM_AppliID; /* OEM/Application ID */ - __IO uint32_t ProdName1; /* Product Name part1 */ - __IO uint8_t ProdName2; /* Product Name part2*/ - __IO uint8_t ProdRev; /* Product Revision */ - __IO uint32_t ProdSN; /* Product Serial Number */ - __IO uint8_t Reserved1; /* Reserved1 */ - __IO uint16_t ManufactDate; /* Manufacturing Date */ - __IO uint8_t CID_CRC; /* CID CRC */ - __IO 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); - -/* 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/syscall.c b/firmware/targets/f7/fatfs/syscall.c deleted file mode 100644 index 00eb8aedeaf..00000000000 --- a/firmware/targets/f7/fatfs/syscall.c +++ /dev/null @@ -1,116 +0,0 @@ -/*------------------------------------------------------------------------*/ -/* Sample code of OS dependent controls for FatFs */ -/* (C)ChaN, 2014 */ -/* Portions COPYRIGHT 2017 STMicroelectronics */ -/* Portions Copyright (C) 2014, ChaN, all right reserved */ -/*------------------------------------------------------------------------*/ - -/** - ****************************************************************************** - * @attention - * - * Copyright (c) 2017 STMicroelectronics. All rights reserved. - * - * This software component is licensed by ST under BSD 3-Clause license, - * the "License"; You may not use this file except in compliance with the - * License. You may obtain a copy of the License at: - * opensource.org/licenses/BSD-3-Clause - * - ****************************************************************************** -**/ - -#include "fatfs/ff.h" - -#if _FS_REENTRANT -/*------------------------------------------------------------------------*/ -/* Create a Synchronization Object */ -/*------------------------------------------------------------------------*/ -/* This function is called in f_mount() function to create a new -/ synchronization object, such as semaphore and mutex. When a 0 is returned, -/ the f_mount() function fails with FR_INT_ERR. -*/ - -int ff_cre_syncobj(/* 1:Function succeeded, 0:Could not create the sync object */ - BYTE vol, /* Corresponding volume (logical drive number) */ - _SYNC_t* sobj /* Pointer to return the created sync object */ -) { - int ret; - - //osSemaphoreDef(SEM); - //*sobj = osSemaphoreCreate(osSemaphore(SEM), 1); - *sobj = furi_mutex_alloc(FuriMutexTypeNormal); - ret = (*sobj != NULL); - - return ret; -} - -/*------------------------------------------------------------------------*/ -/* Delete a Synchronization Object */ -/*------------------------------------------------------------------------*/ -/* This function is called in f_mount() function to delete a synchronization -/ object that created with ff_cre_syncobj() function. When a 0 is returned, -/ the f_mount() function fails with FR_INT_ERR. -*/ - -int ff_del_syncobj(/* 1:Function succeeded, 0:Could not delete due to any error */ - _SYNC_t sobj /* Sync object tied to the logical drive to be deleted */ -) { - furi_mutex_free(sobj); - return 1; -} - -/*------------------------------------------------------------------------*/ -/* Request Grant to Access the Volume */ -/*------------------------------------------------------------------------*/ -/* This function is called on entering file functions to lock the volume. -/ When a 0 is returned, the file function fails with FR_TIMEOUT. -*/ - -int ff_req_grant(/* 1:Got a grant to access the volume, 0:Could not get a grant */ - _SYNC_t sobj /* Sync object to wait */ -) { - int ret = 0; - - if(furi_mutex_acquire(sobj, _FS_TIMEOUT) == FuriStatusOk) { - ret = 1; - } - - return ret; -} - -/*------------------------------------------------------------------------*/ -/* Release Grant to Access the Volume */ -/*------------------------------------------------------------------------*/ -/* This function is called on leaving file functions to unlock the volume. -*/ - -void ff_rel_grant(_SYNC_t sobj /* Sync object to be signaled */ -) { - furi_mutex_release(sobj); -} - -#endif - -#if _USE_LFN == 3 /* LFN with a working buffer on the heap */ -/*------------------------------------------------------------------------*/ -/* Allocate a memory block */ -/*------------------------------------------------------------------------*/ -/* If a NULL is returned, the file function fails with FR_NOT_ENOUGH_CORE. -*/ - -void* ff_memalloc(/* Returns pointer to the allocated memory block */ - UINT msize /* Number of bytes to allocate */ -) { - return ff_malloc(msize); /* Allocate a new memory block with POSIX API */ -} - -/*------------------------------------------------------------------------*/ -/* Free a memory block */ -/*------------------------------------------------------------------------*/ - -void ff_memfree(void* mblock /* Pointer to the memory block to free */ -) { - ff_free(mblock); /* Discard the memory block with POSIX API */ -} - -#endif diff --git a/firmware/targets/f7/fatfs/user_diskio.c b/firmware/targets/f7/fatfs/user_diskio.c deleted file mode 100644 index b504fcd516b..00000000000 --- a/firmware/targets/f7/fatfs/user_diskio.c +++ /dev/null @@ -1,236 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file user_diskio.c - * @brief This file includes a diskio driver skeleton to be completed by the user. - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2020 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -#ifdef USE_OBSOLETE_USER_CODE_SECTION_0 -/* - * Warning: the user section 0 is no more in use (starting from CubeMx version 4.16.0) - * To be suppressed in the future. - * Kept to ensure backward compatibility with previous CubeMx versions when - * migrating projects. - * User code previously added there should be copied in the new user sections before - * the section contents can be deleted. - */ -/* USER CODE BEGIN 0 */ -/* USER CODE END 0 */ -#endif - -/* USER CODE BEGIN DECL */ - -/* Includes ------------------------------------------------------------------*/ -#include "user_diskio.h" -#include -/* Private typedef -----------------------------------------------------------*/ -/* Private define ------------------------------------------------------------*/ - -/* Private variables ---------------------------------------------------------*/ -/* Disk status */ -static volatile DSTATUS Stat = STA_NOINIT; - -static DSTATUS User_CheckStatus(BYTE lun) { - UNUSED(lun); - Stat = STA_NOINIT; - if(BSP_SD_GetCardState() == MSD_OK) { - Stat &= ~STA_NOINIT; - } - - return Stat; -} - -/* USER CODE END DECL */ - -/* Private function prototypes -----------------------------------------------*/ -DSTATUS USER_initialize(BYTE pdrv); -DSTATUS USER_status(BYTE pdrv); -DRESULT USER_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count); -#if _USE_WRITE == 1 -DRESULT USER_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); -#endif /* _USE_WRITE == 1 */ -#if _USE_IOCTL == 1 -DRESULT USER_ioctl(BYTE pdrv, BYTE cmd, void* buff); -#endif /* _USE_IOCTL == 1 */ - -Diskio_drvTypeDef USER_Driver = { - USER_initialize, - USER_status, - USER_read, -#if _USE_WRITE - USER_write, -#endif /* _USE_WRITE == 1 */ -#if _USE_IOCTL == 1 - USER_ioctl, -#endif /* _USE_IOCTL == 1 */ -}; - -/* Private functions ---------------------------------------------------------*/ - -/** - * @brief Initializes a Drive - * @param pdrv: Physical drive number (0..) - * @retval DSTATUS: Operation status - */ -DSTATUS USER_initialize(BYTE pdrv) { - /* USER CODE BEGIN INIT */ - - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); - furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; - - DSTATUS status = User_CheckStatus(pdrv); - - furi_hal_sd_spi_handle = NULL; - furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); - - return status; - /* USER CODE END INIT */ -} - -/** - * @brief Gets Disk Status - * @param pdrv: Physical drive number (0..) - * @retval DSTATUS: Operation status - */ -DSTATUS USER_status(BYTE pdrv) { - /* USER CODE BEGIN STATUS */ - UNUSED(pdrv); - return Stat; - /* USER CODE END STATUS */ -} - -/** - * @brief Reads Sector(s) - * @param pdrv: Physical drive number (0..) - * @param *buff: Data buffer to store read data - * @param sector: Sector address (LBA) - * @param count: Number of sectors to read (1..128) - * @retval DRESULT: Operation result - */ -DRESULT USER_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) { - /* USER CODE BEGIN READ */ - UNUSED(pdrv); - DRESULT res = RES_ERROR; - - 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) { - /* wait until the read operation is finished */ - while(BSP_SD_GetCardState() != MSD_OK) { - } - res = RES_OK; - } - - furi_hal_sd_spi_handle = NULL; - furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); - - return res; - /* USER CODE END READ */ -} - -/** - * @brief Writes Sector(s) - * @param pdrv: Physical drive number (0..) - * @param *buff: Data to be written - * @param sector: Sector address (LBA) - * @param count: Number of sectors to write (1..128) - * @retval DRESULT: Operation result - */ -#if _USE_WRITE == 1 -DRESULT USER_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) { - /* USER CODE BEGIN WRITE */ - /* USER CODE HERE */ - UNUSED(pdrv); - DRESULT res = RES_ERROR; - - 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) { - /* wait until the Write operation is finished */ - while(BSP_SD_GetCardState() != MSD_OK) { - } - res = RES_OK; - } - - furi_hal_sd_spi_handle = NULL; - furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); - - return res; - /* USER CODE END WRITE */ -} -#endif /* _USE_WRITE == 1 */ - -/** - * @brief I/O control operation - * @param pdrv: Physical drive number (0..) - * @param cmd: Control code - * @param *buff: Buffer to send/receive control data - * @retval DRESULT: Operation result - */ -#if _USE_IOCTL == 1 -DRESULT USER_ioctl(BYTE pdrv, BYTE cmd, void* buff) { - /* USER CODE BEGIN IOCTL */ - UNUSED(pdrv); - DRESULT res = RES_ERROR; - BSP_SD_CardInfo CardInfo; - - if(Stat & STA_NOINIT) return RES_NOTRDY; - - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); - furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; - - switch(cmd) { - /* Make sure that no pending write process */ - case CTRL_SYNC: - res = RES_OK; - break; - - /* Get number of sectors on the disk (DWORD) */ - case GET_SECTOR_COUNT: - BSP_SD_GetCardInfo(&CardInfo); - *(DWORD*)buff = CardInfo.LogBlockNbr; - res = RES_OK; - break; - - /* Get R/W sector size (WORD) */ - case GET_SECTOR_SIZE: - BSP_SD_GetCardInfo(&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); - *(DWORD*)buff = CardInfo.LogBlockSize; - res = RES_OK; - break; - - default: - res = RES_PARERR; - } - - furi_hal_sd_spi_handle = NULL; - furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); - - return res; - /* USER CODE END IOCTL */ -} -#endif /* _USE_IOCTL == 1 */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/fatfs/user_diskio.h b/firmware/targets/f7/fatfs/user_diskio.h deleted file mode 100644 index 177723be1a9..00000000000 --- a/firmware/targets/f7/fatfs/user_diskio.h +++ /dev/null @@ -1,48 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file user_diskio.h - * @brief This file contains the common defines and functions prototypes for - * the user_diskio driver. - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2020 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __USER_DISKIO_H -#define __USER_DISKIO_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* USER CODE BEGIN 0 */ - -/* Includes ------------------------------------------------------------------*/ -#include "stm32_adafruit_sd.h" -#include "fatfs/ff_gen_drv.h" -/* Exported types ------------------------------------------------------------*/ -/* Exported constants --------------------------------------------------------*/ -/* Exported functions ------------------------------------------------------- */ -extern Diskio_drvTypeDef USER_Driver; - -/* USER CODE END 0 */ - -#ifdef __cplusplus -} -#endif - -#endif /* __USER_DISKIO_H */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/furi_hal/furi_hal.c b/firmware/targets/f7/furi_hal/furi_hal.c deleted file mode 100644 index d0856127ac3..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal.c +++ /dev/null @@ -1,97 +0,0 @@ -#include -#include - -#include - -#include - -#define TAG "FuriHal" - -void furi_hal_init_early() { - furi_hal_cortex_init_early(); - - furi_hal_clock_init_early(); - - furi_hal_resources_init_early(); - - furi_hal_os_init(); - - furi_hal_spi_init_early(); - - furi_hal_i2c_init_early(); - furi_hal_light_init(); - - furi_hal_rtc_init_early(); -} - -void furi_hal_deinit_early() { - furi_hal_rtc_deinit_early(); - - furi_hal_i2c_deinit_early(); - furi_hal_spi_deinit_early(); - - furi_hal_resources_deinit_early(); - - furi_hal_clock_deinit_early(); -} - -void furi_hal_init() { - furi_hal_mpu_init(); - furi_hal_clock_init(); - furi_hal_console_init(); - furi_hal_rtc_init(); - - furi_hal_interrupt_init(); - - furi_hal_flash_init(); - - furi_hal_resources_init(); - FURI_LOG_I(TAG, "GPIO OK"); - - furi_hal_version_init(); - furi_hal_region_init(); - - furi_hal_spi_init(); - - furi_hal_ibutton_init(); - FURI_LOG_I(TAG, "iButton OK"); - furi_hal_speaker_init(); - FURI_LOG_I(TAG, "Speaker OK"); - - furi_hal_crypto_init(); - - // USB -#ifndef FURI_RAM_EXEC - furi_hal_usb_init(); - FURI_LOG_I(TAG, "USB OK"); -#endif - - furi_hal_i2c_init(); - - // High Level - furi_hal_power_init(); - furi_hal_light_init(); -#ifndef FURI_RAM_EXEC - furi_hal_vibro_init(); - furi_hal_subghz_init(); - furi_hal_nfc_init(); - furi_hal_rfid_init(); -#endif - furi_hal_bt_init(); - furi_hal_compress_icon_init(); - - // FatFS driver initialization - MX_FATFS_Init(); - FURI_LOG_I(TAG, "FATFS OK"); -} - -void furi_hal_switch(void* address) { - __set_BASEPRI(0); - asm volatile("ldr r3, [%0] \n" - "msr msp, r3 \n" - "ldr r3, [%1] \n" - "mov pc, r3 \n" - : - : "r"(address), "r"(address + 0x4) - : "r3"); -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c deleted file mode 100644 index 9bdad5bf2ad..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "furi_hal_bt_serial.h" -#include "dev_info_service.h" -#include "battery_service.h" -#include "serial_service.h" - -#include - -void furi_hal_bt_serial_start() { - // Start device info - if(!dev_info_svc_is_started()) { - dev_info_svc_start(); - } - // Start battery service - if(!battery_svc_is_started()) { - battery_svc_start(); - } - // Start Serial service - if(!serial_svc_is_started()) { - serial_svc_start(); - } -} - -void furi_hal_bt_serial_set_event_callback( - uint16_t buff_size, - FuriHalBtSerialCallback callback, - void* context) { - serial_svc_set_callbacks(buff_size, callback, context); -} - -void furi_hal_bt_serial_notify_buffer_is_empty() { - serial_svc_notify_buffer_is_empty(); -} - -bool furi_hal_bt_serial_tx(uint8_t* data, uint16_t size) { - if(size > FURI_HAL_BT_SERIAL_PACKET_SIZE_MAX) { - return false; - } - return serial_svc_update_tx(data, size); -} - -void furi_hal_bt_serial_stop() { - // Stop all services - if(dev_info_svc_is_started()) { - dev_info_svc_stop(); - } - // Start battery service - if(battery_svc_is_started()) { - battery_svc_stop(); - } - // Start Serial service - if(serial_svc_is_started()) { - serial_svc_stop(); - } -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.c b/firmware/targets/f7/furi_hal/furi_hal_clock.c deleted file mode 100644 index a7c9b4d031d..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_clock.c +++ /dev/null @@ -1,238 +0,0 @@ -#include -#include - -#include -#include -#include -#include -#include - -#define TAG "FuriHalClock" - -#define CPU_CLOCK_HZ_EARLY 4000000 -#define CPU_CLOCK_HZ_MAIN 64000000 -#define TICK_INT_PRIORITY 15U -#define HS_CLOCK_IS_READY() (LL_RCC_HSE_IsReady() && LL_RCC_HSI_IsReady()) -#define LS_CLOCK_IS_READY() (LL_RCC_LSE_IsReady() && LL_RCC_LSI1_IsReady()) - -void furi_hal_clock_init_early() { - LL_SetSystemCoreClock(CPU_CLOCK_HZ_EARLY); - LL_Init1msTick(SystemCoreClock); - - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOD); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOE); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOH); - - LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1); - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2); - - LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPTIM2); - - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1); - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C3); -} - -void furi_hal_clock_deinit_early() { - LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_I2C1); - LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_I2C3); - - LL_APB2_GRP1_DisableClock(LL_APB2_GRP1_PERIPH_SPI1); - LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_SPI2); - - LL_AHB2_GRP1_DisableClock(LL_AHB2_GRP1_PERIPH_GPIOA); - LL_AHB2_GRP1_DisableClock(LL_AHB2_GRP1_PERIPH_GPIOB); - LL_AHB2_GRP1_DisableClock(LL_AHB2_GRP1_PERIPH_GPIOC); - LL_AHB2_GRP1_DisableClock(LL_AHB2_GRP1_PERIPH_GPIOD); - LL_AHB2_GRP1_DisableClock(LL_AHB2_GRP1_PERIPH_GPIOE); - LL_AHB2_GRP1_DisableClock(LL_AHB2_GRP1_PERIPH_GPIOH); -} - -void furi_hal_clock_init() { - /* Prepare Flash memory for 64MHz system clock */ - LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); - while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_3) - ; - - /* HSE and HSI configuration and activation */ - LL_RCC_HSE_SetCapacitorTuning(0x26); - LL_RCC_HSE_Enable(); - LL_RCC_HSI_Enable(); - while(!HS_CLOCK_IS_READY()) - ; - LL_RCC_HSE_EnableCSS(); - - /* LSE and LSI1 configuration and activation */ - LL_PWR_EnableBkUpAccess(); - LL_RCC_LSE_SetDriveCapability(LL_RCC_LSEDRIVE_HIGH); - LL_RCC_LSE_Enable(); - LL_RCC_LSI1_Enable(); - while(!LS_CLOCK_IS_READY()) - ; - LL_EXTI_EnableIT_0_31( - LL_EXTI_LINE_18); /* Why? Because that's why. See RM0434, Table 61. CPU1 vector table. */ - LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_18); - LL_RCC_EnableIT_LSECSS(); - /* ES0394, extended case of 2.2.2 */ - if(!LL_RCC_IsActiveFlag_BORRST()) { - LL_RCC_LSE_EnableCSS(); - } - - /* Main PLL configuration and activation */ - LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_2, 8, LL_RCC_PLLR_DIV_2); - LL_RCC_PLL_Enable(); - LL_RCC_PLL_EnableDomain_SYS(); - while(LL_RCC_PLL_IsReady() != 1) - ; - - LL_RCC_PLLSAI1_ConfigDomain_48M( - LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_2, 6, LL_RCC_PLLSAI1Q_DIV_2); - LL_RCC_PLLSAI1_ConfigDomain_ADC( - LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_2, 6, LL_RCC_PLLSAI1R_DIV_2); - LL_RCC_PLLSAI1_Enable(); - LL_RCC_PLLSAI1_EnableDomain_48M(); - LL_RCC_PLLSAI1_EnableDomain_ADC(); - while(LL_RCC_PLLSAI1_IsReady() != 1) - ; - - /* Sysclk activation on the main PLL */ - /* Set CPU1 prescaler*/ - LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); - - /* Set CPU2 prescaler*/ - LL_C2_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_2); - - LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); - while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) - ; - - /* Set AHB SHARED prescaler*/ - LL_RCC_SetAHB4Prescaler(LL_RCC_SYSCLK_DIV_1); - - /* Set APB1 prescaler*/ - LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1); - - /* Set APB2 prescaler*/ - LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1); - - /* Disable MSI */ - LL_RCC_MSI_Disable(); - while(LL_RCC_MSI_IsReady() != 0) - ; - - /* Update CMSIS variable (which can be updated also through SystemCoreClockUpdate function) */ - LL_SetSystemCoreClock(CPU_CLOCK_HZ_MAIN); - - /* Update the time base */ - LL_Init1msTick(SystemCoreClock); - LL_SYSTICK_EnableIT(); - NVIC_SetPriority( - SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), TICK_INT_PRIORITY, 0)); - NVIC_EnableIRQ(SysTick_IRQn); - - LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2); - LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1); - LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_PLLSAI1); - LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1); - LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_CLK48); - LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_PLLSAI1); - LL_RCC_SetCLK48ClockSource(LL_RCC_CLK48_CLKSOURCE_PLLSAI1); - LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE); - LL_RCC_SetSMPSPrescaler(LL_RCC_SMPS_DIV_1); - LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSE); - - // AHB1 GRP1 - LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1); - LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA2); - LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMAMUX1); - LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_CRC); - // LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_TSC); - - // AHB2 GRP1 - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOD); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOE); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOH); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_ADC); - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_AES1); - - // AHB3 GRP1 - // LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_QUADSPI); - LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_PKA); - LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_AES2); - LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_RNG); - LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_HSEM); - LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_IPCC); - LL_AHB3_GRP1_EnableClock(LL_AHB3_GRP1_PERIPH_FLASH); - - // APB1 GRP1 - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2); - // LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_LCD); - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_RTCAPB); - // LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_WWDG); - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2); - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1); - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C3); - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_CRS); - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USB); - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_LPTIM1); - - // APB1 GRP2 - LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPUART1); - - // APB2 - // LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC); - LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1); - LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1); - LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1); - LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM16); - LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM17); - // LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SAI1); - - FURI_LOG_I(TAG, "Init OK"); -} - -void furi_hal_clock_switch_to_hsi() { - LL_RCC_HSI_Enable(); - - while(!LL_RCC_HSI_IsReady()) - ; - - LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI); - LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSI); - - while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI) - ; - - LL_FLASH_SetLatency(LL_FLASH_LATENCY_1); -} - -void furi_hal_clock_switch_to_pll() { - LL_RCC_HSE_Enable(); - LL_RCC_PLL_Enable(); - - while(!LL_RCC_HSE_IsReady()) - ; - while(!LL_RCC_PLL_IsReady()) - ; - - LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); - - LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); - LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE); - - while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) - ; -} - -void furi_hal_clock_suspend_tick() { - CLEAR_BIT(SysTick->CTRL, SysTick_CTRL_ENABLE_Msk); -} - -void furi_hal_clock_resume_tick() { - SET_BIT(SysTick->CTRL, SysTick_CTRL_ENABLE_Msk); -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.h b/firmware/targets/f7/furi_hal/furi_hal_clock.h deleted file mode 100644 index 9cb11db4b98..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_clock.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -/** Early initialization */ -void furi_hal_clock_init_early(); - -/** Early deinitialization */ -void furi_hal_clock_deinit_early(); - -/** Initialize clocks */ -void furi_hal_clock_init(); - -/** Switch to HSI clock */ -void furi_hal_clock_switch_to_hsi(); - -/** Switch to PLL clock */ -void furi_hal_clock_switch_to_pll(); - -/** Stop SysTick counter without resetting */ -void furi_hal_clock_suspend_tick(); - -/** Continue SysTick counter operation */ -void furi_hal_clock_resume_tick(); diff --git a/firmware/targets/f7/furi_hal/furi_hal_compress.c b/firmware/targets/f7/furi_hal/furi_hal_compress.c deleted file mode 100644 index 7e31dbbf7f0..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_compress.c +++ /dev/null @@ -1,264 +0,0 @@ -#include - -#include -#include -#include - -#define TAG "FuriHalCompress" - -#define FURI_HAL_COMPRESS_ICON_ENCODED_BUFF_SIZE (2 * 512) -#define FURI_HAL_COMPRESS_ICON_DECODED_BUFF_SIZE (1024) - -#define FURI_HAL_COMPRESS_EXP_BUFF_SIZE (1 << FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG) - -typedef struct { - uint8_t is_compressed; - uint8_t reserved; - uint16_t compressed_buff_size; -} FuriHalCompressHeader; - -typedef struct { - heatshrink_decoder* decoder; - uint8_t - compress_buff[FURI_HAL_COMPRESS_EXP_BUFF_SIZE + FURI_HAL_COMPRESS_ICON_ENCODED_BUFF_SIZE]; - uint8_t decoded_buff[FURI_HAL_COMPRESS_ICON_DECODED_BUFF_SIZE]; -} FuriHalCompressIcon; - -struct FuriHalCompress { - heatshrink_encoder* encoder; - heatshrink_decoder* decoder; - uint8_t* compress_buff; - uint16_t compress_buff_size; -}; - -static FuriHalCompressIcon* icon_decoder; - -static void furi_hal_compress_reset(FuriHalCompress* compress) { - furi_assert(compress); - heatshrink_encoder_reset(compress->encoder); - heatshrink_decoder_reset(compress->decoder); - memset(compress->compress_buff, 0, compress->compress_buff_size); -} - -void furi_hal_compress_icon_init() { - icon_decoder = malloc(sizeof(FuriHalCompressIcon)); - icon_decoder->decoder = heatshrink_decoder_alloc( - icon_decoder->compress_buff, - FURI_HAL_COMPRESS_ICON_ENCODED_BUFF_SIZE, - FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG, - FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG); - heatshrink_decoder_reset(icon_decoder->decoder); - memset(icon_decoder->decoded_buff, 0, sizeof(icon_decoder->decoded_buff)); - FURI_LOG_I(TAG, "Init OK"); -} - -void furi_hal_compress_icon_decode(const uint8_t* icon_data, uint8_t** decoded_buff) { - furi_assert(icon_data); - furi_assert(decoded_buff); - - FuriHalCompressHeader* header = (FuriHalCompressHeader*)icon_data; - if(header->is_compressed) { - size_t data_processed = 0; - heatshrink_decoder_sink( - icon_decoder->decoder, - (uint8_t*)&icon_data[4], - header->compressed_buff_size, - &data_processed); - while(1) { - HSD_poll_res res = heatshrink_decoder_poll( - icon_decoder->decoder, - icon_decoder->decoded_buff, - sizeof(icon_decoder->decoded_buff), - &data_processed); - furi_assert((res == HSDR_POLL_EMPTY) || (res == HSDR_POLL_MORE)); - if(res != HSDR_POLL_MORE) { - break; - } - } - heatshrink_decoder_reset(icon_decoder->decoder); - memset(icon_decoder->compress_buff, 0, sizeof(icon_decoder->compress_buff)); - *decoded_buff = icon_decoder->decoded_buff; - } else { - *decoded_buff = (uint8_t*)&icon_data[1]; - } -} - -FuriHalCompress* furi_hal_compress_alloc(uint16_t compress_buff_size) { - FuriHalCompress* compress = malloc(sizeof(FuriHalCompress)); - compress->compress_buff = malloc(compress_buff_size + FURI_HAL_COMPRESS_EXP_BUFF_SIZE); - compress->encoder = heatshrink_encoder_alloc( - compress->compress_buff, - FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG, - FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG); - compress->decoder = heatshrink_decoder_alloc( - compress->compress_buff, - compress_buff_size, - FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG, - FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG); - - return compress; -} - -void furi_hal_compress_free(FuriHalCompress* compress) { - furi_assert(compress); - - heatshrink_encoder_free(compress->encoder); - heatshrink_decoder_free(compress->decoder); - free(compress->compress_buff); - free(compress); -} - -bool furi_hal_compress_encode( - FuriHalCompress* compress, - uint8_t* data_in, - size_t data_in_size, - uint8_t* data_out, - size_t data_out_size, - size_t* data_res_size) { - furi_assert(compress); - furi_assert(data_in); - furi_assert(data_in_size); - - size_t sink_size = 0; - size_t poll_size = 0; - HSE_sink_res sink_res; - HSE_poll_res poll_res; - HSE_finish_res finish_res; - bool encode_failed = false; - size_t sunk = 0; - size_t res_buff_size = sizeof(FuriHalCompressHeader); - - // Sink data to encoding buffer - while((sunk < data_in_size) && !encode_failed) { - sink_res = heatshrink_encoder_sink( - compress->encoder, &data_in[sunk], data_in_size - sunk, &sink_size); - if(sink_res != HSER_SINK_OK) { - encode_failed = true; - break; - } - sunk += sink_size; - do { - poll_res = heatshrink_encoder_poll( - compress->encoder, - &data_out[res_buff_size], - data_out_size - res_buff_size, - &poll_size); - if(poll_res < 0) { - encode_failed = true; - break; - } - res_buff_size += poll_size; - } while(poll_res == HSER_POLL_MORE); - } - - // Notify sinking complete and poll encoded data - finish_res = heatshrink_encoder_finish(compress->encoder); - if(finish_res < 0) { - encode_failed = true; - } else { - do { - poll_res = heatshrink_encoder_poll( - compress->encoder, - &data_out[res_buff_size], - data_out_size - 4 - res_buff_size, - &poll_size); - if(poll_res < 0) { - encode_failed = true; - break; - } - res_buff_size += poll_size; - finish_res = heatshrink_encoder_finish(compress->encoder); - } while(finish_res != HSER_FINISH_DONE); - } - - bool result = true; - // Write encoded data to output buffer if compression is efficient. Else - write header and original data - if(!encode_failed && (res_buff_size < data_in_size + 1)) { - FuriHalCompressHeader header = { - .is_compressed = 0x01, .reserved = 0x00, .compressed_buff_size = res_buff_size}; - memcpy(data_out, &header, sizeof(header)); - *data_res_size = res_buff_size; - } else if(data_out_size > data_in_size) { - data_out[0] = 0x00; - memcpy(&data_out[1], data_in, data_in_size); - *data_res_size = data_in_size + 1; - } else { - *data_res_size = 0; - result = false; - } - furi_hal_compress_reset(compress); - - return result; -} - -bool furi_hal_compress_decode( - FuriHalCompress* compress, - uint8_t* data_in, - size_t data_in_size, - uint8_t* data_out, - size_t data_out_size, - size_t* data_res_size) { - furi_assert(compress); - furi_assert(data_in); - furi_assert(data_out); - furi_assert(data_res_size); - - bool result = false; - bool decode_failed = false; - HSD_sink_res sink_res; - HSD_poll_res poll_res; - HSD_finish_res finish_res; - size_t sink_size = 0; - size_t res_buff_size = 0; - size_t poll_size = 0; - - FuriHalCompressHeader* header = (FuriHalCompressHeader*)data_in; - if(header->is_compressed) { - // Sink data to decoding buffer - size_t compressed_size = header->compressed_buff_size; - size_t sunk = sizeof(FuriHalCompressHeader); - while(sunk < compressed_size && !decode_failed) { - sink_res = heatshrink_decoder_sink( - compress->decoder, &data_in[sunk], compressed_size - sunk, &sink_size); - if(sink_res < 0) { - decode_failed = true; - break; - } - sunk += sink_size; - do { - poll_res = heatshrink_decoder_poll( - compress->decoder, &data_out[res_buff_size], data_out_size, &poll_size); - if(poll_res < 0) { - decode_failed = true; - break; - } - res_buff_size += poll_size; - } while(poll_res == HSDR_POLL_MORE); - } - // Notify sinking complete and poll decoded data - if(!decode_failed) { - finish_res = heatshrink_decoder_finish(compress->decoder); - if(finish_res < 0) { - decode_failed = true; - } else { - do { - poll_res = heatshrink_decoder_poll( - compress->decoder, &data_out[res_buff_size], data_out_size, &poll_size); - res_buff_size += poll_size; - finish_res = heatshrink_decoder_finish(compress->decoder); - } while(finish_res != HSDR_FINISH_DONE); - } - } - *data_res_size = res_buff_size; - result = !decode_failed; - } else if(data_out_size >= data_in_size - 1) { - memcpy(data_out, &data_in[1], data_in_size); - *data_res_size = data_in_size - 1; - result = true; - } else { - result = false; - } - furi_hal_compress_reset(compress); - - return result; -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_cortex.c b/firmware/targets/f7/furi_hal/furi_hal_cortex.c deleted file mode 100644 index 2b4ea6e99b2..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_cortex.c +++ /dev/null @@ -1,20 +0,0 @@ -#include "furi_hal_cortex.h" - -#include - -void furi_hal_cortex_init_early() { - CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; - DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; - DWT->CYCCNT = 0U; -} - -void furi_hal_cortex_delay_us(uint32_t microseconds) { - uint32_t start = DWT->CYCCNT; - uint32_t time_ticks = SystemCoreClock / 1000000 * microseconds; - while((DWT->CYCCNT - start) < time_ticks) { - }; -} - -uint32_t furi_hal_cortex_instructions_per_microsecond() { - return SystemCoreClock / 1000000; -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_crypto.c b/firmware/targets/f7/furi_hal/furi_hal_crypto.c deleted file mode 100644 index b3c68e2d01c..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_crypto.c +++ /dev/null @@ -1,342 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#define TAG "FuriHalCrypto" - -#define ENCLAVE_FACTORY_KEY_SLOTS 10 -#define ENCLAVE_SIGNATURE_SIZE 16 - -#define CRYPTO_BLK_LEN (4 * sizeof(uint32_t)) -#define CRYPTO_TIMEOUT (1000) - -#define CRYPTO_MODE_ENCRYPT 0U -#define CRYPTO_MODE_INIT (AES_CR_MODE_0) -#define CRYPTO_MODE_DECRYPT (AES_CR_MODE_1) -#define CRYPTO_MODE_DECRYPT_INIT (AES_CR_MODE_0 | AES_CR_MODE_1) - -#define CRYPTO_DATATYPE_32B 0U -#define CRYPTO_KEYSIZE_256B (AES_CR_KEYSIZE) -#define CRYPTO_AES_CBC (AES_CR_CHMOD_0) - -static FuriMutex* furi_hal_crypto_mutex = NULL; -static bool furi_hal_crypto_mode_init_done = false; - -static const uint8_t enclave_signature_iv[ENCLAVE_FACTORY_KEY_SLOTS][16] = { - {0xac, 0x5d, 0x68, 0xb8, 0x79, 0x74, 0xfc, 0x7f, 0x45, 0x02, 0x82, 0xf1, 0x48, 0x7e, 0x75, 0x8a}, - {0x38, 0xe6, 0x6a, 0x90, 0x5e, 0x5b, 0x8a, 0xa6, 0x70, 0x30, 0x04, 0x72, 0xc2, 0x42, 0xea, 0xaf}, - {0x73, 0xd5, 0x8e, 0xfb, 0x0f, 0x4b, 0xa9, 0x79, 0x0f, 0xde, 0x0e, 0x53, 0x44, 0x7d, 0xaa, 0xfd}, - {0x3c, 0x9a, 0xf4, 0x43, 0x2b, 0xfe, 0xea, 0xae, 0x8c, 0xc6, 0xd1, 0x60, 0xd2, 0x96, 0x64, 0xa9}, - {0x10, 0xac, 0x7b, 0x63, 0x03, 0x7f, 0x43, 0x18, 0xec, 0x9d, 0x9c, 0xc4, 0x01, 0xdc, 0x35, 0xa7}, - {0x26, 0x21, 0x64, 0xe6, 0xd0, 0xf2, 0x47, 0x49, 0xdc, 0x36, 0xcd, 0x68, 0x0c, 0x91, 0x03, 0x44}, - {0x7a, 0xbd, 0xce, 0x9c, 0x24, 0x7a, 0x2a, 0xb1, 0x3c, 0x4f, 0x5a, 0x7d, 0x80, 0x3e, 0xfc, 0x0d}, - {0xcd, 0xdd, 0xd3, 0x02, 0x85, 0x65, 0x43, 0x83, 0xf9, 0xac, 0x75, 0x2f, 0x21, 0xef, 0x28, 0x6b}, - {0xab, 0x73, 0x70, 0xe8, 0xe2, 0x56, 0x0f, 0x58, 0xab, 0x29, 0xa5, 0xb1, 0x13, 0x47, 0x5e, 0xe8}, - {0x4f, 0x3c, 0x43, 0x77, 0xde, 0xed, 0x79, 0xa1, 0x8d, 0x4c, 0x1f, 0xfd, 0xdb, 0x96, 0x87, 0x2e}, -}; - -static const uint8_t enclave_signature_input[ENCLAVE_FACTORY_KEY_SLOTS][ENCLAVE_SIGNATURE_SIZE] = { - {0x9f, 0x5c, 0xb1, 0x43, 0x17, 0x53, 0x18, 0x8c, 0x66, 0x3d, 0x39, 0x45, 0x90, 0x13, 0xa9, 0xde}, - {0xc5, 0x98, 0xe9, 0x17, 0xb8, 0x97, 0x9e, 0x03, 0x33, 0x14, 0x13, 0x8f, 0xce, 0x74, 0x0d, 0x54}, - {0x34, 0xba, 0x99, 0x59, 0x9f, 0x70, 0x67, 0xe9, 0x09, 0xee, 0x64, 0x0e, 0xb3, 0xba, 0xfb, 0x75}, - {0xdc, 0xfa, 0x6c, 0x9a, 0x6f, 0x0a, 0x3e, 0xdc, 0x42, 0xf6, 0xae, 0x0d, 0x3c, 0xf7, 0x83, 0xaf}, - {0xea, 0x2d, 0xe3, 0x1f, 0x02, 0x99, 0x1a, 0x7e, 0x6d, 0x93, 0x4c, 0xb5, 0x42, 0xf0, 0x7a, 0x9b}, - {0x53, 0x5e, 0x04, 0xa2, 0x49, 0xa0, 0x73, 0x49, 0x56, 0xb0, 0x88, 0x8c, 0x12, 0xa0, 0xe4, 0x18}, - {0x7d, 0xa7, 0xc5, 0x21, 0x7f, 0x12, 0x95, 0xdd, 0x4d, 0x77, 0x01, 0xfa, 0x71, 0x88, 0x2b, 0x7f}, - {0xdc, 0x9b, 0xc5, 0xa7, 0x6b, 0x84, 0x5c, 0x37, 0x7c, 0xec, 0x05, 0xa1, 0x9f, 0x91, 0x17, 0x3b}, - {0xea, 0xcf, 0xd9, 0x9b, 0x86, 0xcd, 0x2b, 0x43, 0x54, 0x45, 0x82, 0xc6, 0xfe, 0x73, 0x1a, 0x1a}, - {0x77, 0xb8, 0x1b, 0x90, 0xb4, 0xb7, 0x32, 0x76, 0x8f, 0x8a, 0x57, 0x06, 0xc7, 0xdd, 0x08, 0x90}, -}; - -static const uint8_t enclave_signature_expected[ENCLAVE_FACTORY_KEY_SLOTS][ENCLAVE_SIGNATURE_SIZE] = { - {0xe9, 0x9a, 0xce, 0xe9, 0x4d, 0xe1, 0x7f, 0x55, 0xcb, 0x8a, 0xbf, 0xf2, 0x4d, 0x98, 0x27, 0x67}, - {0x34, 0x27, 0xa7, 0xea, 0xa8, 0x98, 0x66, 0x9b, 0xed, 0x43, 0xd3, 0x93, 0xb5, 0xa2, 0x87, 0x8e}, - {0x6c, 0xf3, 0x01, 0x78, 0x53, 0x1b, 0x11, 0x32, 0xf0, 0x27, 0x2f, 0xe3, 0x7d, 0xa6, 0xe2, 0xfd}, - {0xdf, 0x7f, 0x37, 0x65, 0x2f, 0xdb, 0x7c, 0xcf, 0x5b, 0xb6, 0xe4, 0x9c, 0x63, 0xc5, 0x0f, 0xe0}, - {0x9b, 0x5c, 0xee, 0x44, 0x0e, 0xd1, 0xcb, 0x5f, 0x28, 0x9f, 0x12, 0x17, 0x59, 0x64, 0x40, 0xbb}, - {0x94, 0xc2, 0x09, 0x98, 0x62, 0xa7, 0x2b, 0x93, 0xed, 0x36, 0x1f, 0x10, 0xbc, 0x26, 0xbd, 0x41}, - {0x4d, 0xb2, 0x2b, 0xc5, 0x96, 0x47, 0x61, 0xf4, 0x16, 0xe0, 0x81, 0xc3, 0x8e, 0xb9, 0x9c, 0x9b}, - {0xc3, 0x6b, 0x83, 0x55, 0x90, 0x38, 0x0f, 0xea, 0xd1, 0x65, 0xbf, 0x32, 0x4f, 0x8e, 0x62, 0x5b}, - {0x8d, 0x5e, 0x27, 0xbc, 0x14, 0x4f, 0x08, 0xa8, 0x2b, 0x14, 0x89, 0x5e, 0xdf, 0x77, 0x04, 0x31}, - {0xc9, 0xf7, 0x03, 0xf1, 0x6c, 0x65, 0xad, 0x49, 0x74, 0xbe, 0x00, 0x54, 0xfd, 0xa6, 0x9c, 0x32}, -}; - -void furi_hal_crypto_init() { - furi_hal_crypto_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - FURI_LOG_I(TAG, "Init OK"); -} - -static bool furi_hal_crypto_generate_unique_keys(uint8_t start_slot, uint8_t end_slot) { - FuriHalCryptoKey key; - uint8_t key_data[32]; - FURI_LOG_I(TAG, "Generating keys %u..%u", start_slot, end_slot); - for(uint8_t slot = start_slot; slot <= end_slot; slot++) { - key.type = FuriHalCryptoKeyTypeSimple; - key.size = FuriHalCryptoKeySize256; - key.data = key_data; - furi_hal_random_fill_buf(key_data, 32); - if(!furi_hal_crypto_store_add_key(&key, &slot)) { - FURI_LOG_E(TAG, "Error writing key to slot %u", slot); - return false; - } - } - return true; -} - -bool furi_hal_crypto_verify_key(uint8_t key_slot) { - uint8_t keys_nb = 0; - uint8_t valid_keys_nb = 0; - uint8_t last_valid_slot = ENCLAVE_FACTORY_KEY_SLOTS; - uint8_t empty_iv[16]; - furi_hal_crypto_verify_enclave(&keys_nb, &valid_keys_nb); - if(key_slot <= ENCLAVE_FACTORY_KEY_SLOTS) { // It's a factory key - if(key_slot > keys_nb) return false; - } else { // Unique key - if(keys_nb < ENCLAVE_FACTORY_KEY_SLOTS) // Some factory keys are missing - return false; - for(uint8_t i = key_slot; i > ENCLAVE_FACTORY_KEY_SLOTS; i--) { - if(furi_hal_crypto_store_load_key(i, empty_iv)) { - last_valid_slot = i; - furi_hal_crypto_store_unload_key(i); - break; - } - } - if(last_valid_slot == key_slot) - return true; - else // Generate missing unique keys - return furi_hal_crypto_generate_unique_keys(last_valid_slot + 1, key_slot); - } - return true; -} - -bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb) { - furi_assert(keys_nb); - furi_assert(valid_keys_nb); - uint8_t keys = 0; - uint8_t keys_valid = 0; - uint8_t buffer[ENCLAVE_SIGNATURE_SIZE]; - for(size_t key_slot = 0; key_slot < ENCLAVE_FACTORY_KEY_SLOTS; key_slot++) { - if(furi_hal_crypto_store_load_key(key_slot + 1, enclave_signature_iv[key_slot])) { - keys++; - if(furi_hal_crypto_encrypt( - enclave_signature_input[key_slot], buffer, ENCLAVE_SIGNATURE_SIZE)) { - keys_valid += - memcmp(buffer, enclave_signature_expected[key_slot], ENCLAVE_SIGNATURE_SIZE) == - 0; - } - furi_hal_crypto_store_unload_key(key_slot + 1); - } - } - *keys_nb = keys; - *valid_keys_nb = keys_valid; - if(*valid_keys_nb == ENCLAVE_FACTORY_KEY_SLOTS) - return true; - else - return false; -} - -bool furi_hal_crypto_store_add_key(FuriHalCryptoKey* key, uint8_t* slot) { - furi_assert(key); - furi_assert(slot); - - furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk); - - if(!furi_hal_bt_is_alive()) { - return false; - } - - SHCI_C2_FUS_StoreUsrKey_Cmd_Param_t pParam; - size_t key_data_size = 0; - - if(key->type == FuriHalCryptoKeyTypeMaster) { - pParam.KeyType = KEYTYPE_MASTER; - } else if(key->type == FuriHalCryptoKeyTypeSimple) { - pParam.KeyType = KEYTYPE_SIMPLE; - } else if(key->type == FuriHalCryptoKeyTypeEncrypted) { - pParam.KeyType = KEYTYPE_ENCRYPTED; - key_data_size += 12; - } else { - furi_crash("Incorrect key type"); - } - - if(key->size == FuriHalCryptoKeySize128) { - pParam.KeySize = KEYSIZE_16; - key_data_size += 16; - } else if(key->size == FuriHalCryptoKeySize256) { - pParam.KeySize = KEYSIZE_32; - key_data_size += 32; - } else { - furi_crash("Incorrect key size"); - } - - memcpy(pParam.KeyData, key->data, key_data_size); - - SHCI_CmdStatus_t shci_state = SHCI_C2_FUS_StoreUsrKey(&pParam, slot); - furi_check(furi_mutex_release(furi_hal_crypto_mutex) == FuriStatusOk); - return (shci_state == SHCI_Success); -} - -static void crypto_key_init(uint32_t* key, uint32_t* iv) { - CLEAR_BIT(AES1->CR, AES_CR_EN); - MODIFY_REG( - AES1->CR, - AES_CR_DATATYPE | AES_CR_KEYSIZE | AES_CR_CHMOD, - CRYPTO_DATATYPE_32B | CRYPTO_KEYSIZE_256B | CRYPTO_AES_CBC); - - if(key != NULL) { - AES1->KEYR7 = key[0]; - AES1->KEYR6 = key[1]; - AES1->KEYR5 = key[2]; - AES1->KEYR4 = key[3]; - AES1->KEYR3 = key[4]; - AES1->KEYR2 = key[5]; - AES1->KEYR1 = key[6]; - AES1->KEYR0 = key[7]; - } - - AES1->IVR3 = iv[0]; - AES1->IVR2 = iv[1]; - AES1->IVR1 = iv[2]; - AES1->IVR0 = iv[3]; -} - -static bool crypto_process_block(uint32_t* in, uint32_t* out, uint8_t blk_len) { - furi_check((blk_len <= 4) && (blk_len > 0)); - - for(uint8_t i = 0; i < 4; i++) { - if(i < blk_len) { - AES1->DINR = in[i]; - } else { - AES1->DINR = 0; - } - } - - uint32_t countdown = CRYPTO_TIMEOUT; - while(!READ_BIT(AES1->SR, AES_SR_CCF)) { - if(LL_SYSTICK_IsActiveCounterFlag()) { - countdown--; - } - if(countdown == 0) { - return false; - } - } - - SET_BIT(AES1->CR, AES_CR_CCFC); - - uint32_t out_temp[4]; - for(uint8_t i = 0; i < 4; i++) { - out_temp[i] = AES1->DOUTR; - } - - memcpy(out, out_temp, blk_len * sizeof(uint32_t)); - return true; -} - -bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv) { - furi_assert(slot > 0 && slot <= 100); - furi_assert(furi_hal_crypto_mutex); - furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk); - - if(!furi_hal_bt_is_alive()) { - return false; - } - - furi_hal_crypto_mode_init_done = false; - crypto_key_init(NULL, (uint32_t*)iv); - - if(SHCI_C2_FUS_LoadUsrKey(slot) == SHCI_Success) { - return true; - } else { - CLEAR_BIT(AES1->CR, AES_CR_EN); - furi_check(furi_mutex_release(furi_hal_crypto_mutex) == FuriStatusOk); - return false; - } -} - -bool furi_hal_crypto_store_unload_key(uint8_t slot) { - if(!furi_hal_bt_is_alive()) { - return false; - } - - CLEAR_BIT(AES1->CR, AES_CR_EN); - - SHCI_CmdStatus_t shci_state = SHCI_C2_FUS_UnloadUsrKey(slot); - furi_assert(shci_state == SHCI_Success); - - FURI_CRITICAL_ENTER(); - LL_AHB2_GRP1_ForceReset(LL_AHB2_GRP1_PERIPH_AES1); - LL_AHB2_GRP1_ReleaseReset(LL_AHB2_GRP1_PERIPH_AES1); - FURI_CRITICAL_EXIT(); - - furi_check(furi_mutex_release(furi_hal_crypto_mutex) == FuriStatusOk); - return (shci_state == SHCI_Success); -} - -bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size) { - bool state = false; - - SET_BIT(AES1->CR, AES_CR_EN); - - MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_ENCRYPT); - - for(size_t i = 0; i < size; i += CRYPTO_BLK_LEN) { - size_t blk_len = size - i; - if(blk_len > CRYPTO_BLK_LEN) { - blk_len = CRYPTO_BLK_LEN; - } - state = crypto_process_block((uint32_t*)&input[i], (uint32_t*)&output[i], blk_len / 4); - if(state == false) { - break; - } - } - - CLEAR_BIT(AES1->CR, AES_CR_EN); - - return state; -} - -bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size) { - bool state = false; - - if(!furi_hal_crypto_mode_init_done) { - MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_INIT); - - SET_BIT(AES1->CR, AES_CR_EN); - - uint32_t countdown = CRYPTO_TIMEOUT; - while(!READ_BIT(AES1->SR, AES_SR_CCF)) { - if(LL_SYSTICK_IsActiveCounterFlag()) { - countdown--; - } - if(countdown == 0) { - return false; - } - } - - SET_BIT(AES1->CR, AES_CR_CCFC); - - furi_hal_crypto_mode_init_done = true; - } - - MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_DECRYPT); - SET_BIT(AES1->CR, AES_CR_EN); - - for(size_t i = 0; i < size; i += CRYPTO_BLK_LEN) { - size_t blk_len = size - i; - if(blk_len > CRYPTO_BLK_LEN) { - blk_len = CRYPTO_BLK_LEN; - } - state = crypto_process_block((uint32_t*)&input[i], (uint32_t*)&output[i], blk_len / 4); - if(state == false) { - break; - } - } - - CLEAR_BIT(AES1->CR, AES_CR_EN); - - return state; -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_debug.c b/firmware/targets/f7/furi_hal/furi_hal_debug.c deleted file mode 100644 index 3b5dfe622e0..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_debug.c +++ /dev/null @@ -1,20 +0,0 @@ -#include - -#include -#include - -void furi_hal_debug_enable() { - // Low power mode debug - LL_DBGMCU_EnableDBGSleepMode(); - LL_DBGMCU_EnableDBGStopMode(); - LL_DBGMCU_EnableDBGStandbyMode(); - LL_EXTI_EnableIT_32_63(LL_EXTI_LINE_48); -} - -void furi_hal_debug_disable() { - // Low power mode debug - LL_DBGMCU_DisableDBGSleepMode(); - LL_DBGMCU_DisableDBGStopMode(); - LL_DBGMCU_DisableDBGStandbyMode(); - LL_EXTI_DisableIT_32_63(LL_EXTI_LINE_48); -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_flash.c b/firmware/targets/f7/furi_hal/furi_hal_flash.c deleted file mode 100644 index ac141db0459..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_flash.c +++ /dev/null @@ -1,515 +0,0 @@ -#include -#include -#include -#include -#include - -#include - -#define TAG "FuriHalFlash" - -#define FURI_HAL_CRITICAL_MSG "Critical flash operation fail" -#define FURI_HAL_FLASH_READ_BLOCK 8 -#define FURI_HAL_FLASH_WRITE_BLOCK 8 -#define FURI_HAL_FLASH_PAGE_SIZE 4096 -#define FURI_HAL_FLASH_CYCLES_COUNT 10000 -#define FURI_HAL_FLASH_TIMEOUT 1000 -#define FURI_HAL_FLASH_KEY1 0x45670123U -#define FURI_HAL_FLASH_KEY2 0xCDEF89ABU -#define FURI_HAL_FLASH_TOTAL_PAGES 256 -#define FURI_HAL_FLASH_SR_ERRORS \ - (FLASH_SR_OPERR | FLASH_SR_PROGERR | FLASH_SR_WRPERR | FLASH_SR_PGAERR | FLASH_SR_SIZERR | \ - FLASH_SR_PGSERR | FLASH_SR_MISERR | FLASH_SR_FASTERR | FLASH_SR_RDERR | FLASH_SR_OPTVERR) - -//#define FURI_HAL_FLASH_OB_START_ADDRESS 0x1FFF8000 -#define FURI_HAL_FLASH_OPT_KEY1 0x08192A3B -#define FURI_HAL_FLASH_OPT_KEY2 0x4C5D6E7F -#define FURI_HAL_FLASH_OB_TOTAL_WORDS (0x80 / (sizeof(uint32_t) * 2)) - -#define IS_ADDR_ALIGNED_64BITS(__VALUE__) (((__VALUE__)&0x7U) == (0x00UL)) -#define IS_FLASH_PROGRAM_ADDRESS(__VALUE__) \ - (((__VALUE__) >= FLASH_BASE) && ((__VALUE__) <= (FLASH_BASE + FLASH_SIZE - 8UL)) && \ - (((__VALUE__) % 8UL) == 0UL)) - -/* Free flash space borders, exported by linker */ -extern const void __free_flash_start__; - -size_t furi_hal_flash_get_base() { - return FLASH_BASE; -} - -size_t furi_hal_flash_get_read_block_size() { - return FURI_HAL_FLASH_READ_BLOCK; -} - -size_t furi_hal_flash_get_write_block_size() { - return FURI_HAL_FLASH_WRITE_BLOCK; -} - -size_t furi_hal_flash_get_page_size() { - return FURI_HAL_FLASH_PAGE_SIZE; -} - -size_t furi_hal_flash_get_cycles_count() { - return FURI_HAL_FLASH_CYCLES_COUNT; -} - -const void* furi_hal_flash_get_free_start_address() { - return &__free_flash_start__; -} - -const void* furi_hal_flash_get_free_end_address() { - uint32_t sfr_reg_val = READ_REG(FLASH->SFR); - uint32_t sfsa = (READ_BIT(sfr_reg_val, FLASH_SFR_SFSA) >> FLASH_SFR_SFSA_Pos); - return (const void*)((sfsa * FURI_HAL_FLASH_PAGE_SIZE) + FLASH_BASE); -} - -size_t furi_hal_flash_get_free_page_start_address() { - size_t start = (size_t)furi_hal_flash_get_free_start_address(); - size_t page_start = start - start % FURI_HAL_FLASH_PAGE_SIZE; - if(page_start != start) { - page_start += FURI_HAL_FLASH_PAGE_SIZE; - } - return page_start; -} - -size_t furi_hal_flash_get_free_page_count() { - size_t end = (size_t)furi_hal_flash_get_free_end_address(); - size_t page_start = (size_t)furi_hal_flash_get_free_page_start_address(); - return (end - page_start) / FURI_HAL_FLASH_PAGE_SIZE; -} - -void furi_hal_flash_init() { - // Errata 2.2.9, Flash OPTVERR flag is always set after system reset - WRITE_REG(FLASH->SR, FLASH_SR_OPTVERR); - //__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR); -} - -static void furi_hal_flash_unlock() { - /* verify Flash is locked */ - furi_check(READ_BIT(FLASH->CR, FLASH_CR_LOCK) != 0U); - - /* Authorize the FLASH Registers access */ - WRITE_REG(FLASH->KEYR, FURI_HAL_FLASH_KEY1); - WRITE_REG(FLASH->KEYR, FURI_HAL_FLASH_KEY2); - - /* verify Flash is unlocked */ - furi_check(READ_BIT(FLASH->CR, FLASH_CR_LOCK) == 0U); -} - -static void furi_hal_flash_lock(void) { - /* verify Flash is unlocked */ - furi_check(READ_BIT(FLASH->CR, FLASH_CR_LOCK) == 0U); - - /* Set the LOCK Bit to lock the FLASH Registers access */ - /* @Note The lock and unlock procedure is done only using CR registers even from CPU2 */ - SET_BIT(FLASH->CR, FLASH_CR_LOCK); - - /* verify Flash is locked */ - furi_check(READ_BIT(FLASH->CR, FLASH_CR_LOCK) != 0U); -} - -static void furi_hal_flash_begin_with_core2(bool erase_flag) { - // Take flash controller ownership - while(LL_HSEM_1StepLock(HSEM, CFG_HW_FLASH_SEMID) != 0) { - furi_thread_yield(); - } - - // Unlock flash operation - furi_hal_flash_unlock(); - - // Erase activity notification - if(erase_flag) SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON); - - // 64mHz 5us core2 flag protection - for(volatile uint32_t i = 0; i < 35; i++) - ; - - while(true) { - // Wait till flash controller become usable - while(LL_FLASH_IsActiveFlag_OperationSuspended()) { - furi_thread_yield(); - }; - - // Just a little more love - taskENTER_CRITICAL(); - - // Actually we already have mutex for it, but specification is specification - if(LL_HSEM_IsSemaphoreLocked(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID)) { - taskEXIT_CRITICAL(); - furi_thread_yield(); - continue; - } - - // Take sempahopre and prevent core2 from anything funky - if(LL_HSEM_1StepLock(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID) != 0) { - taskEXIT_CRITICAL(); - furi_thread_yield(); - continue; - } - - break; - } -} - -static void furi_hal_flash_begin(bool erase_flag) { - // Acquire dangerous ops mutex - furi_hal_bt_lock_core2(); - - // If Core2 is running use IPC locking - if(furi_hal_bt_is_alive()) { - furi_hal_flash_begin_with_core2(erase_flag); - } else { - furi_hal_flash_unlock(); - } -} - -static void furi_hal_flash_end_with_core2(bool erase_flag) { - // Funky ops are ok at this point - LL_HSEM_ReleaseLock(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID, 0); - - // Task switching is ok - taskEXIT_CRITICAL(); - - // Doesn't make much sense, does it? - while(READ_BIT(FLASH->SR, FLASH_SR_BSY)) { - furi_thread_yield(); - } - - // Erase activity over, core2 can continue - if(erase_flag) SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_OFF); - - // Lock flash controller - furi_hal_flash_lock(); - - // Release flash controller ownership - LL_HSEM_ReleaseLock(HSEM, CFG_HW_FLASH_SEMID, 0); -} - -static void furi_hal_flash_end(bool erase_flag) { - // If Core2 is running use IPC locking - if(furi_hal_bt_is_alive()) { - furi_hal_flash_end_with_core2(erase_flag); - } else { - furi_hal_flash_lock(); - } - - // Release dangerous ops mutex - furi_hal_bt_unlock_core2(); -} - -static void furi_hal_flush_cache(void) { - /* Flush instruction cache */ - if(READ_BIT(FLASH->ACR, FLASH_ACR_ICEN) == FLASH_ACR_ICEN) { - /* Disable instruction cache */ - LL_FLASH_DisableInstCache(); - /* Reset instruction cache */ - LL_FLASH_EnableInstCacheReset(); - LL_FLASH_DisableInstCacheReset(); - /* Enable instruction cache */ - LL_FLASH_EnableInstCache(); - } - - /* Flush data cache */ - if(READ_BIT(FLASH->ACR, FLASH_ACR_DCEN) == FLASH_ACR_DCEN) { - /* Disable data cache */ - LL_FLASH_DisableDataCache(); - /* Reset data cache */ - LL_FLASH_EnableDataCacheReset(); - LL_FLASH_DisableDataCacheReset(); - /* Enable data cache */ - LL_FLASH_EnableDataCache(); - } -} - -bool furi_hal_flash_wait_last_operation(uint32_t timeout) { - uint32_t error = 0; - uint32_t countdown = 0; - - // Wait for the FLASH operation to complete by polling on BUSY flag to be reset. - // Even if the FLASH operation fails, the BUSY flag will be reset and an error - // flag will be set - countdown = timeout; - while(READ_BIT(FLASH->SR, FLASH_SR_BSY)) { - if(LL_SYSTICK_IsActiveCounterFlag()) { - countdown--; - } - if(countdown == 0) { - return false; - } - } - - /* Check FLASH operation error flags */ - error = FLASH->SR; - - /* Check FLASH End of Operation flag */ - if((error & FLASH_SR_EOP) != 0U) { - /* Clear FLASH End of Operation pending bit */ - CLEAR_BIT(FLASH->SR, FLASH_SR_EOP); - } - - /* Now update error variable to only error value */ - error &= FURI_HAL_FLASH_SR_ERRORS; - - furi_check(error == 0); - - /* clear error flags */ - CLEAR_BIT(FLASH->SR, error); - - /* Wait for control register to be written */ - countdown = timeout; - while(READ_BIT(FLASH->SR, FLASH_SR_CFGBSY)) { - if(LL_SYSTICK_IsActiveCounterFlag()) { - countdown--; - } - if(countdown == 0) { - return false; - } - } - return true; -} - -bool furi_hal_flash_erase(uint8_t page) { - furi_hal_flash_begin(true); - - // Ensure that controller state is valid - furi_check(FLASH->SR == 0); - - /* Verify that next operation can be proceed */ - furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); - - /* Select page and start operation */ - MODIFY_REG( - FLASH->CR, FLASH_CR_PNB, ((page << FLASH_CR_PNB_Pos) | FLASH_CR_PER | FLASH_CR_STRT)); - - /* Wait for last operation to be completed */ - furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); - - /* If operation is completed or interrupted, disable the Page Erase Bit */ - CLEAR_BIT(FLASH->CR, (FLASH_CR_PER | FLASH_CR_PNB)); - - /* Flush the caches to be sure of the data consistency */ - furi_hal_flush_cache(); - - furi_hal_flash_end(true); - - return true; -} - -static inline bool furi_hal_flash_write_dword_internal(size_t address, uint64_t* data) { - /* Program first word */ - *(uint32_t*)address = (uint32_t)*data; - - // Barrier to ensure programming is performed in 2 steps, in right order - // (independently of compiler optimization behavior) - __ISB(); - - /* Program second word */ - *(uint32_t*)(address + 4U) = (uint32_t)(*data >> 32U); - - /* Wait for last operation to be completed */ - furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); - return true; -} - -bool furi_hal_flash_write_dword(size_t address, uint64_t data) { - furi_hal_flash_begin(false); - - // Ensure that controller state is valid - furi_check(FLASH->SR == 0); - - /* Check the parameters */ - furi_check(IS_ADDR_ALIGNED_64BITS(address)); - furi_check(IS_FLASH_PROGRAM_ADDRESS(address)); - - /* Set PG bit */ - SET_BIT(FLASH->CR, FLASH_CR_PG); - - /* Do the thing */ - furi_check(furi_hal_flash_write_dword_internal(address, &data)); - - /* If the program operation is completed, disable the PG or FSTPG Bit */ - CLEAR_BIT(FLASH->CR, FLASH_CR_PG); - - furi_hal_flash_end(false); - - /* Wait for last operation to be completed */ - furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); - return true; -} - -static size_t furi_hal_flash_get_page_address(uint8_t page) { - return furi_hal_flash_get_base() + page * FURI_HAL_FLASH_PAGE_SIZE; -} - -bool furi_hal_flash_program_page(const uint8_t page, const uint8_t* data, uint16_t _length) { - uint16_t length = _length; - furi_check(length <= FURI_HAL_FLASH_PAGE_SIZE); - - furi_hal_flash_erase(page); - - furi_hal_flash_begin(false); - - // Ensure that controller state is valid - furi_check(FLASH->SR == 0); - - size_t page_start_address = furi_hal_flash_get_page_address(page); - - /* Set PG bit */ - SET_BIT(FLASH->CR, FLASH_CR_PG); - size_t i_dwords = 0; - for(i_dwords = 0; i_dwords < (length / 8); ++i_dwords) { - /* Do the thing */ - size_t data_offset = i_dwords * 8; - furi_check(furi_hal_flash_write_dword_internal( - page_start_address + data_offset, (uint64_t*)&data[data_offset])); - } - if((length % 8) != 0) { - /* there are more bytes, not fitting into dwords */ - uint64_t tail_data = 0; - size_t data_offset = i_dwords * 8; - for(int32_t tail_i = 0; tail_i < (length % 8); ++tail_i) { - tail_data |= (((uint64_t)data[data_offset + tail_i]) << (tail_i * 8)); - } - - furi_check( - furi_hal_flash_write_dword_internal(page_start_address + data_offset, &tail_data)); - } - - /* If the program operation is completed, disable the PG or FSTPG Bit */ - CLEAR_BIT(FLASH->CR, FLASH_CR_PG); - - furi_hal_flash_end(false); - return true; -} - -int16_t furi_hal_flash_get_page_number(size_t address) { - const size_t flash_base = furi_hal_flash_get_base(); - if((address < flash_base) || - (address > flash_base + FURI_HAL_FLASH_TOTAL_PAGES * FURI_HAL_FLASH_PAGE_SIZE)) { - return -1; - } - - return (address - flash_base) / FURI_HAL_FLASH_PAGE_SIZE; -} - -uint32_t furi_hal_flash_ob_get_word(size_t word_idx, bool complementary) { - furi_check(word_idx <= FURI_HAL_FLASH_OB_TOTAL_WORDS); - const uint32_t* ob_data = (const uint32_t*)(OPTION_BYTE_BASE); - size_t raw_word_idx = word_idx * 2; - if(complementary) { - raw_word_idx += 1; - } - return ob_data[raw_word_idx]; -} - -void furi_hal_flash_ob_unlock() { - furi_check(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) != 0U); - furi_hal_flash_begin(true); - WRITE_REG(FLASH->OPTKEYR, FURI_HAL_FLASH_OPT_KEY1); - __ISB(); - WRITE_REG(FLASH->OPTKEYR, FURI_HAL_FLASH_OPT_KEY2); - /* verify OB area is unlocked */ - furi_check(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) == 0U); -} - -void furi_hal_flash_ob_lock() { - furi_check(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) == 0U); - SET_BIT(FLASH->CR, FLASH_CR_OPTLOCK); - furi_hal_flash_end(true); - furi_check(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) != 0U); -} - -typedef enum { - FuriHalFlashObInvalid, - FuriHalFlashObRegisterUserRead, - FuriHalFlashObRegisterPCROP1AStart, - FuriHalFlashObRegisterPCROP1AEnd, - FuriHalFlashObRegisterWRPA, - FuriHalFlashObRegisterWRPB, - FuriHalFlashObRegisterPCROP1BStart, - FuriHalFlashObRegisterPCROP1BEnd, - FuriHalFlashObRegisterIPCCMail, - FuriHalFlashObRegisterSecureFlash, - FuriHalFlashObRegisterC2Opts, -} FuriHalFlashObRegister; - -typedef struct { - FuriHalFlashObRegister ob_reg; - uint32_t* ob_register_address; -} FuriHalFlashObMapping; - -#define OB_REG_DEF(INDEX, REG) \ - { .ob_reg = INDEX, .ob_register_address = (uint32_t*)(REG) } - -static const FuriHalFlashObMapping furi_hal_flash_ob_reg_map[FURI_HAL_FLASH_OB_TOTAL_WORDS] = { - OB_REG_DEF(FuriHalFlashObRegisterUserRead, (&FLASH->OPTR)), - OB_REG_DEF(FuriHalFlashObRegisterPCROP1AStart, (&FLASH->PCROP1ASR)), - OB_REG_DEF(FuriHalFlashObRegisterPCROP1AEnd, (&FLASH->PCROP1AER)), - OB_REG_DEF(FuriHalFlashObRegisterWRPA, (&FLASH->WRP1AR)), - OB_REG_DEF(FuriHalFlashObRegisterWRPB, (&FLASH->WRP1BR)), - OB_REG_DEF(FuriHalFlashObRegisterPCROP1BStart, (&FLASH->PCROP1BSR)), - OB_REG_DEF(FuriHalFlashObRegisterPCROP1BEnd, (&FLASH->PCROP1BER)), - - OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), - OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), - OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), - OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), - OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), - OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), - - OB_REG_DEF(FuriHalFlashObRegisterIPCCMail, (NULL)), - OB_REG_DEF(FuriHalFlashObRegisterSecureFlash, (NULL)), - OB_REG_DEF(FuriHalFlashObRegisterC2Opts, (NULL)), -}; - -void furi_hal_flash_ob_apply() { - furi_hal_flash_ob_unlock(); - /* OBL_LAUNCH: When set to 1, this bit forces the option byte reloading. - * It cannot be written if OPTLOCK is set */ - SET_BIT(FLASH->CR, FLASH_CR_OBL_LAUNCH); - furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); - furi_hal_flash_ob_lock(); -} - -bool furi_hal_flash_ob_set_word(size_t word_idx, const uint32_t value) { - furi_check(word_idx < FURI_HAL_FLASH_OB_TOTAL_WORDS); - - const FuriHalFlashObMapping* reg_def = &furi_hal_flash_ob_reg_map[word_idx]; - if(reg_def->ob_register_address == NULL) { - FURI_LOG_E(TAG, "Attempt to set RO OB word %d", word_idx); - return false; - } - - FURI_LOG_W( - TAG, - "Setting OB reg %d for word %d (addr 0x%08X) to 0x%08X", - reg_def->ob_reg, - word_idx, - reg_def->ob_register_address, - value); - - /* 1. Clear OPTLOCK option lock bit with the clearing sequence */ - furi_hal_flash_ob_unlock(); - - /* 2. Write the desired options value in the options registers */ - *reg_def->ob_register_address = value; - - /* 3. Check that no Flash memory operation is on going by checking the BSY && PESD */ - furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); - while(LL_FLASH_IsActiveFlag_OperationSuspended()) { - furi_thread_yield(); - }; - - /* 4. Set the Options start bit OPTSTRT */ - SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT); - - /* 5. Wait for the BSY bit to be cleared. */ - furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); - furi_hal_flash_ob_lock(); - return true; -} - -const FuriHalFlashRawOptionByteData* furi_hal_flash_ob_get_raw_ptr() { - return (const FuriHalFlashRawOptionByteData*)OPTION_BYTE_BASE; -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_i2c.c b/firmware/targets/f7/furi_hal/furi_hal_i2c.c deleted file mode 100644 index 36f5230c217..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_i2c.c +++ /dev/null @@ -1,354 +0,0 @@ -#include -#include -#include - -#include -#include -#include -#include - -#define TAG "FuriHalI2C" - -void furi_hal_i2c_init_early() { - furi_hal_i2c_bus_power.callback(&furi_hal_i2c_bus_power, FuriHalI2cBusEventInit); -} - -void furi_hal_i2c_deinit_early() { - furi_hal_i2c_bus_power.callback(&furi_hal_i2c_bus_power, FuriHalI2cBusEventDeinit); -} - -void furi_hal_i2c_init() { - furi_hal_i2c_bus_external.callback(&furi_hal_i2c_bus_external, FuriHalI2cBusEventInit); - FURI_LOG_I(TAG, "Init OK"); -} - -void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle) { - furi_hal_power_insomnia_enter(); - // Lock bus access - handle->bus->callback(handle->bus, FuriHalI2cBusEventLock); - // Ensuree that no active handle set - furi_check(handle->bus->current_handle == NULL); - // Set current handle - handle->bus->current_handle = handle; - // Activate bus - handle->bus->callback(handle->bus, FuriHalI2cBusEventActivate); - // Activate handle - handle->callback(handle, FuriHalI2cBusHandleEventActivate); -} - -void furi_hal_i2c_release(FuriHalI2cBusHandle* handle) { - // Ensure that current handle is our handle - furi_check(handle->bus->current_handle == handle); - // Deactivate handle - handle->callback(handle, FuriHalI2cBusHandleEventDeactivate); - // Deactivate bus - handle->bus->callback(handle->bus, FuriHalI2cBusEventDeactivate); - // Reset current handle - handle->bus->current_handle = NULL; - // Unlock bus - handle->bus->callback(handle->bus, FuriHalI2cBusEventUnlock); - furi_hal_power_insomnia_exit(); -} - -bool furi_hal_i2c_tx( - FuriHalI2cBusHandle* handle, - uint8_t address, - const uint8_t* data, - uint8_t size, - uint32_t timeout) { - furi_check(handle->bus->current_handle == handle); - furi_assert(timeout > 0); - - bool ret = true; - uint32_t timeout_tick = furi_get_tick() + timeout; - - do { - while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { - if(furi_get_tick() >= timeout_tick) { - ret = false; - break; - } - } - - if(!ret) { - break; - } - - LL_I2C_HandleTransfer( - handle->bus->i2c, - address, - LL_I2C_ADDRSLAVE_7BIT, - size, - LL_I2C_MODE_AUTOEND, - LL_I2C_GENERATE_START_WRITE); - - while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) { - if(LL_I2C_IsActiveFlag_TXIS(handle->bus->i2c)) { - LL_I2C_TransmitData8(handle->bus->i2c, (*data)); - data++; - size--; - } - - if(furi_get_tick() >= timeout_tick) { - ret = false; - break; - } - } - - LL_I2C_ClearFlag_STOP(handle->bus->i2c); - } while(0); - - return ret; -} - -bool furi_hal_i2c_rx( - FuriHalI2cBusHandle* handle, - uint8_t address, - uint8_t* data, - uint8_t size, - uint32_t timeout) { - furi_check(handle->bus->current_handle == handle); - furi_assert(timeout > 0); - - bool ret = true; - uint32_t timeout_tick = furi_get_tick() + timeout; - - do { - while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { - if(furi_get_tick() >= timeout_tick) { - ret = false; - break; - } - } - - if(!ret) { - break; - } - - LL_I2C_HandleTransfer( - handle->bus->i2c, - address, - LL_I2C_ADDRSLAVE_7BIT, - size, - LL_I2C_MODE_AUTOEND, - LL_I2C_GENERATE_START_READ); - - while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) { - if(LL_I2C_IsActiveFlag_RXNE(handle->bus->i2c)) { - *data = LL_I2C_ReceiveData8(handle->bus->i2c); - data++; - size--; - } - - if(furi_get_tick() >= timeout_tick) { - ret = false; - break; - } - } - - LL_I2C_ClearFlag_STOP(handle->bus->i2c); - } while(0); - - return ret; -} - -bool furi_hal_i2c_trx( - FuriHalI2cBusHandle* handle, - uint8_t address, - const uint8_t* tx_data, - uint8_t tx_size, - uint8_t* rx_data, - uint8_t rx_size, - uint32_t timeout) { - if(furi_hal_i2c_tx(handle, address, tx_data, tx_size, timeout) && - furi_hal_i2c_rx(handle, address, rx_data, rx_size, timeout)) { - return true; - } else { - return false; - } -} - -bool furi_hal_i2c_is_device_ready(FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint32_t timeout) { - furi_check(handle); - - furi_check(handle->bus->current_handle == handle); - furi_assert(timeout > 0); - - bool ret = true; - uint32_t timeout_tick = furi_get_tick() + timeout; - - do { - while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { - if(furi_get_tick() >= timeout_tick) { - return false; - } - } - - handle->bus->i2c->CR2 = - ((((uint32_t)(i2c_addr) & (I2C_CR2_SADD)) | (I2C_CR2_START) | (I2C_CR2_AUTOEND)) & - (~I2C_CR2_RD_WRN)); - - while((!LL_I2C_IsActiveFlag_NACK(handle->bus->i2c)) && - (!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c))) { - if(furi_get_tick() >= timeout_tick) { - return false; - } - } - - if(LL_I2C_IsActiveFlag_NACK(handle->bus->i2c)) { - while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c)) { - if(furi_get_tick() >= timeout_tick) { - return false; - } - } - - LL_I2C_ClearFlag_NACK(handle->bus->i2c); - - // Clear STOP Flag generated by autoend - LL_I2C_ClearFlag_STOP(handle->bus->i2c); - - // Generate actual STOP - LL_I2C_GenerateStopCondition(handle->bus->i2c); - - ret = false; - } - - while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c)) { - if(furi_get_tick() >= timeout_tick) { - return false; - } - } - - LL_I2C_ClearFlag_STOP(handle->bus->i2c); - } while(0); - - return ret; -} - -bool furi_hal_i2c_read_reg_8( - FuriHalI2cBusHandle* handle, - uint8_t i2c_addr, - uint8_t reg_addr, - uint8_t* data, - uint32_t timeout) { - furi_check(handle); - - return furi_hal_i2c_trx(handle, i2c_addr, ®_addr, 1, data, 1, timeout); -} - -bool furi_hal_i2c_read_reg_16( - FuriHalI2cBusHandle* handle, - uint8_t i2c_addr, - uint8_t reg_addr, - uint16_t* data, - uint32_t timeout) { - furi_check(handle); - - uint8_t reg_data[2]; - bool ret = furi_hal_i2c_trx(handle, i2c_addr, ®_addr, 1, reg_data, 2, timeout); - *data = (reg_data[0] << 8) | (reg_data[1]); - - return ret; -} - -bool furi_hal_i2c_read_mem( - FuriHalI2cBusHandle* handle, - uint8_t i2c_addr, - uint8_t mem_addr, - uint8_t* data, - uint8_t len, - uint32_t timeout) { - furi_check(handle); - - return furi_hal_i2c_trx(handle, i2c_addr, &mem_addr, 1, data, len, timeout); -} - -bool furi_hal_i2c_write_reg_8( - FuriHalI2cBusHandle* handle, - uint8_t i2c_addr, - uint8_t reg_addr, - uint8_t data, - uint32_t timeout) { - furi_check(handle); - - uint8_t tx_data[2]; - tx_data[0] = reg_addr; - tx_data[1] = data; - - return furi_hal_i2c_tx(handle, i2c_addr, (const uint8_t*)&tx_data, 2, timeout); -} - -bool furi_hal_i2c_write_reg_16( - FuriHalI2cBusHandle* handle, - uint8_t i2c_addr, - uint8_t reg_addr, - uint16_t data, - uint32_t timeout) { - furi_check(handle); - - uint8_t tx_data[3]; - tx_data[0] = reg_addr; - tx_data[1] = (data >> 8) & 0xFF; - tx_data[2] = data & 0xFF; - - return furi_hal_i2c_tx(handle, i2c_addr, (const uint8_t*)&tx_data, 3, timeout); -} - -bool furi_hal_i2c_write_mem( - FuriHalI2cBusHandle* handle, - uint8_t i2c_addr, - uint8_t mem_addr, - uint8_t* data, - uint8_t len, - uint32_t timeout) { - furi_check(handle); - - furi_check(handle->bus->current_handle == handle); - furi_assert(timeout > 0); - - bool ret = true; - uint8_t size = len + 1; - uint32_t timeout_tick = furi_get_tick() + timeout; - - do { - while(LL_I2C_IsActiveFlag_BUSY(handle->bus->i2c)) { - if(furi_get_tick() >= timeout_tick) { - ret = false; - break; - } - } - - if(!ret) { - break; - } - - LL_I2C_HandleTransfer( - handle->bus->i2c, - i2c_addr, - LL_I2C_ADDRSLAVE_7BIT, - size, - LL_I2C_MODE_AUTOEND, - LL_I2C_GENERATE_START_WRITE); - - while(!LL_I2C_IsActiveFlag_STOP(handle->bus->i2c) || size > 0) { - if(LL_I2C_IsActiveFlag_TXIS(handle->bus->i2c)) { - if(size == len + 1) { - LL_I2C_TransmitData8(handle->bus->i2c, mem_addr); - } else { - LL_I2C_TransmitData8(handle->bus->i2c, (*data)); - data++; - } - size--; - } - - if(furi_get_tick() >= timeout_tick) { - ret = false; - break; - } - } - - LL_I2C_ClearFlag_STOP(handle->bus->i2c); - } while(0); - - return ret; -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_ibutton.c b/firmware/targets/f7/furi_hal/furi_hal_ibutton.c deleted file mode 100644 index 0375893e7ae..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_ibutton.c +++ /dev/null @@ -1,135 +0,0 @@ -#include -#include -#include - -#include -#include - -#include - -#define FURI_HAL_IBUTTON_TIMER TIM1 -#define FURI_HAL_IBUTTON_TIMER_IRQ FuriHalInterruptIdTim1UpTim16 - -typedef enum { - FuriHalIbuttonStateIdle, - FuriHalIbuttonStateRunning, -} FuriHalIbuttonState; - -typedef struct { - FuriHalIbuttonState state; - FuriHalIbuttonEmulateCallback callback; - void* context; -} FuriHalIbutton; - -FuriHalIbutton* furi_hal_ibutton = NULL; - -static void furi_hal_ibutton_emulate_isr() { - if(LL_TIM_IsActiveFlag_UPDATE(FURI_HAL_IBUTTON_TIMER)) { - LL_TIM_ClearFlag_UPDATE(FURI_HAL_IBUTTON_TIMER); - furi_hal_ibutton->callback(furi_hal_ibutton->context); - } -} - -void furi_hal_ibutton_init() { - furi_hal_ibutton = malloc(sizeof(FuriHalIbutton)); - furi_hal_ibutton->state = FuriHalIbuttonStateIdle; -} - -void furi_hal_ibutton_emulate_start( - uint32_t period, - FuriHalIbuttonEmulateCallback callback, - void* context) { - furi_assert(furi_hal_ibutton); - furi_assert(furi_hal_ibutton->state == FuriHalIbuttonStateIdle); - - furi_hal_ibutton->state = FuriHalIbuttonStateRunning; - furi_hal_ibutton->callback = callback; - furi_hal_ibutton->context = context; - - FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(FURI_HAL_IBUTTON_TIMER); - FURI_CRITICAL_EXIT(); - - furi_hal_interrupt_set_isr(FURI_HAL_IBUTTON_TIMER_IRQ, furi_hal_ibutton_emulate_isr, NULL); - - LL_TIM_SetPrescaler(FURI_HAL_IBUTTON_TIMER, 0); - LL_TIM_SetCounterMode(FURI_HAL_IBUTTON_TIMER, LL_TIM_COUNTERMODE_UP); - LL_TIM_SetAutoReload(FURI_HAL_IBUTTON_TIMER, period); - LL_TIM_DisableARRPreload(FURI_HAL_IBUTTON_TIMER); - LL_TIM_SetRepetitionCounter(FURI_HAL_IBUTTON_TIMER, 0); - - LL_TIM_SetClockDivision(FURI_HAL_IBUTTON_TIMER, LL_TIM_CLOCKDIVISION_DIV1); - LL_TIM_SetClockSource(FURI_HAL_IBUTTON_TIMER, LL_TIM_CLOCKSOURCE_INTERNAL); - LL_TIM_GenerateEvent_UPDATE(FURI_HAL_IBUTTON_TIMER); - - LL_TIM_EnableIT_UPDATE(FURI_HAL_IBUTTON_TIMER); - - LL_TIM_EnableCounter(FURI_HAL_IBUTTON_TIMER); -} - -void furi_hal_ibutton_emulate_set_next(uint32_t period) { - LL_TIM_SetAutoReload(FURI_HAL_IBUTTON_TIMER, period); -} - -void furi_hal_ibutton_emulate_stop() { - furi_assert(furi_hal_ibutton); - - if(furi_hal_ibutton->state == FuriHalIbuttonStateRunning) { - furi_hal_ibutton->state = FuriHalIbuttonStateIdle; - LL_TIM_DisableCounter(FURI_HAL_IBUTTON_TIMER); - - FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(FURI_HAL_IBUTTON_TIMER); - FURI_CRITICAL_EXIT(); - - furi_hal_interrupt_set_isr(FURI_HAL_IBUTTON_TIMER_IRQ, NULL, NULL); - - furi_hal_ibutton->callback = NULL; - furi_hal_ibutton->context = NULL; - } -} - -void furi_hal_ibutton_start_drive() { - furi_hal_ibutton_pin_high(); - 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(); - 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); -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_info.c b/firmware/targets/f7/furi_hal/furi_hal_info.c deleted file mode 100644 index 1f75ea331ea..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_info.c +++ /dev/null @@ -1,141 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include - -void furi_hal_info_get(FuriHalInfoValueCallback out, void* context) { - string_t value; - string_init(value); - - // Device Info version - out("device_info_major", "2", false, context); - out("device_info_minor", "0", false, context); - - // Model name - out("hardware_model", furi_hal_version_get_model_name(), false, context); - - // Unique ID - string_reset(value); - const uint8_t* uid = furi_hal_version_uid(); - for(size_t i = 0; i < furi_hal_version_uid_size(); i++) { - string_cat_printf(value, "%02X", uid[i]); - } - out("hardware_uid", string_get_cstr(value), false, context); - - // OTP Revision - string_printf(value, "%d", furi_hal_version_get_otp_version()); - out("hardware_otp_ver", string_get_cstr(value), false, context); - string_printf(value, "%lu", furi_hal_version_get_hw_timestamp()); - out("hardware_timestamp", string_get_cstr(value), false, context); - - // Board Revision - string_printf(value, "%d", furi_hal_version_get_hw_version()); - out("hardware_ver", string_get_cstr(value), false, context); - string_printf(value, "%d", furi_hal_version_get_hw_target()); - out("hardware_target", string_get_cstr(value), false, context); - string_printf(value, "%d", furi_hal_version_get_hw_body()); - out("hardware_body", string_get_cstr(value), false, context); - string_printf(value, "%d", furi_hal_version_get_hw_connect()); - out("hardware_connect", string_get_cstr(value), false, context); - string_printf(value, "%d", furi_hal_version_get_hw_display()); - out("hardware_display", string_get_cstr(value), false, context); - - // Board Personification - string_printf(value, "%d", furi_hal_version_get_hw_color()); - out("hardware_color", string_get_cstr(value), false, context); - string_printf(value, "%d", furi_hal_version_get_hw_region()); - out("hardware_region", string_get_cstr(value), false, context); - out("hardware_region_provisioned", furi_hal_region_get_name(), false, context); - const char* name = furi_hal_version_get_name_ptr(); - if(name) { - out("hardware_name", name, false, context); - } - - // Firmware version - const Version* firmware_version = furi_hal_version_get_firmware_version(); - if(firmware_version) { - out("firmware_commit", version_get_githash(firmware_version), false, context); - out("firmware_commit_dirty", - version_get_dirty_flag(firmware_version) ? "true" : "false", - false, - context); - out("firmware_branch", version_get_gitbranch(firmware_version), false, context); - out("firmware_branch_num", version_get_gitbranchnum(firmware_version), false, context); - out("firmware_version", version_get_version(firmware_version), false, context); - out("firmware_build_date", version_get_builddate(firmware_version), false, context); - string_printf(value, "%d", version_get_target(firmware_version)); - out("firmware_target", string_get_cstr(value), false, context); - } - - if(furi_hal_bt_is_alive()) { - const BleGlueC2Info* ble_c2_info = ble_glue_get_c2_info(); - out("radio_alive", "true", false, context); - out("radio_mode", ble_c2_info->mode == BleGlueC2ModeFUS ? "FUS" : "Stack", false, context); - - // FUS Info - string_printf(value, "%d", ble_c2_info->FusVersionMajor); - out("radio_fus_major", string_get_cstr(value), false, context); - string_printf(value, "%d", ble_c2_info->FusVersionMinor); - out("radio_fus_minor", string_get_cstr(value), false, context); - string_printf(value, "%d", ble_c2_info->FusVersionSub); - out("radio_fus_sub", string_get_cstr(value), false, context); - string_printf(value, "%dK", ble_c2_info->FusMemorySizeSram2B); - out("radio_fus_sram2b", string_get_cstr(value), false, context); - string_printf(value, "%dK", ble_c2_info->FusMemorySizeSram2A); - out("radio_fus_sram2a", string_get_cstr(value), false, context); - string_printf(value, "%dK", ble_c2_info->FusMemorySizeFlash * 4); - out("radio_fus_flash", string_get_cstr(value), false, context); - - // Stack Info - string_printf(value, "%d", ble_c2_info->StackType); - out("radio_stack_type", string_get_cstr(value), false, context); - string_printf(value, "%d", ble_c2_info->VersionMajor); - out("radio_stack_major", string_get_cstr(value), false, context); - string_printf(value, "%d", ble_c2_info->VersionMinor); - out("radio_stack_minor", string_get_cstr(value), false, context); - string_printf(value, "%d", ble_c2_info->VersionSub); - out("radio_stack_sub", string_get_cstr(value), false, context); - string_printf(value, "%d", ble_c2_info->VersionBranch); - out("radio_stack_branch", string_get_cstr(value), false, context); - string_printf(value, "%d", ble_c2_info->VersionReleaseType); - out("radio_stack_release", string_get_cstr(value), false, context); - string_printf(value, "%dK", ble_c2_info->MemorySizeSram2B); - out("radio_stack_sram2b", string_get_cstr(value), false, context); - string_printf(value, "%dK", ble_c2_info->MemorySizeSram2A); - out("radio_stack_sram2a", string_get_cstr(value), false, context); - string_printf(value, "%dK", ble_c2_info->MemorySizeSram1); - out("radio_stack_sram1", string_get_cstr(value), false, context); - string_printf(value, "%dK", ble_c2_info->MemorySizeFlash * 4); - out("radio_stack_flash", string_get_cstr(value), false, context); - - // Mac address - string_reset(value); - const uint8_t* ble_mac = furi_hal_version_get_ble_mac(); - for(size_t i = 0; i < 6; i++) { - string_cat_printf(value, "%02X", ble_mac[i]); - } - out("radio_ble_mac", string_get_cstr(value), false, context); - - // Signature verification - uint8_t enclave_keys = 0; - uint8_t enclave_valid_keys = 0; - bool enclave_valid = furi_hal_crypto_verify_enclave(&enclave_keys, &enclave_valid_keys); - string_printf(value, "%d", enclave_valid_keys); - out("enclave_valid_keys", string_get_cstr(value), false, context); - out("enclave_valid", enclave_valid ? "true" : "false", false, context); - } else { - out("radio_alive", "false", false, context); - } - - string_printf(value, "%u", PROTOBUF_MAJOR_VERSION); - out("protobuf_version_major", string_get_cstr(value), false, context); - string_printf(value, "%u", PROTOBUF_MINOR_VERSION); - out("protobuf_version_minor", string_get_cstr(value), true, context); - - string_clear(value); -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_infrared.c b/firmware/targets/f7/furi_hal/furi_hal_infrared.c deleted file mode 100644 index 442ae715d0f..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_infrared.c +++ /dev/null @@ -1,652 +0,0 @@ -#include "furi_hal_infrared.h" -#include -#include "stm32wbxx_ll_dma.h" -#include "sys/_stdint.h" -#include -#include - -#include -#include -#include - -#include -#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 = GPIO_PIN_7}; -#endif - -#define INFRARED_TIM_TX_DMA_BUFFER_SIZE 200 -#define INFRARED_POLARITY_SHIFT 1 - -#define INFRARED_TX_CCMR_HIGH \ - (TIM_CCMR2_OC3PE | LL_TIM_OCMODE_PWM2) /* Mark time - enable PWM2 mode */ -#define INFRARED_TX_CCMR_LOW \ - (TIM_CCMR2_OC3PE | LL_TIM_OCMODE_FORCED_INACTIVE) /* Space time - force low */ - -typedef struct { - FuriHalInfraredRxCaptureCallback capture_callback; - void* capture_context; - FuriHalInfraredRxTimeoutCallback timeout_callback; - void* timeout_context; -} InfraredTimRx; - -typedef struct { - uint8_t* polarity; - uint16_t* data; - size_t size; - bool packet_end; - bool last_packet_end; -} InfraredTxBuf; - -typedef struct { - float cycle_duration; - FuriHalInfraredTxGetDataISRCallback data_callback; - FuriHalInfraredTxSignalSentISRCallback signal_sent_callback; - void* data_context; - void* signal_sent_context; - InfraredTxBuf buffer[2]; - FuriSemaphore* stop_semaphore; - uint32_t - tx_timing_rest_duration; /** if timing is too long (> 0xFFFF), send it in few iterations */ - bool tx_timing_rest_level; - FuriHalInfraredTxGetDataState tx_timing_rest_status; -} InfraredTimTx; - -typedef enum { - InfraredStateIdle, /** Furi Hal Infrared is ready to start RX or TX */ - InfraredStateAsyncRx, /** Async RX started */ - InfraredStateAsyncTx, /** Async TX started, DMA and timer is on */ - InfraredStateAsyncTxStopReq, /** Async TX started, async stop request received */ - InfraredStateAsyncTxStopInProgress, /** Async TX started, stop request is processed and we wait for last data to be sent */ - InfraredStateAsyncTxStopped, /** Async TX complete, cleanup needed */ - InfraredStateMAX, -} InfraredState; - -static volatile InfraredState furi_hal_infrared_state = InfraredStateIdle; -static InfraredTimTx infrared_tim_tx; -static InfraredTimRx infrared_tim_rx; - -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); -static void furi_hal_infrared_tx_dma_set_polarity(uint8_t buf_num, uint8_t polarity_shift); -static void furi_hal_infrared_tx_dma_set_buffer(uint8_t buf_num); -static void furi_hal_infrared_tx_fill_buffer_last(uint8_t buf_num); -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(); - -static void furi_hal_infrared_tim_rx_isr() { - static uint32_t previous_captured_ch2 = 0; - - /* Timeout */ - if(LL_TIM_IsActiveFlag_CC3(TIM2)) { - LL_TIM_ClearFlag_CC3(TIM2); - furi_assert(furi_hal_infrared_state == InfraredStateAsyncRx); - - /* Timers CNT register starts to counting from 0 to ARR, but it is - * reseted when Channel 1 catches interrupt. It is not reseted by - * channel 2, though, so we have to distract it's values (see TimerIRQSourceCCI1 ISR). - * This can cause false timeout: when time is over, but we started - * receiving new signal few microseconds ago, because CNT register - * is reseted once per period, not per sample. */ - if(LL_GPIO_IsInputPinSet(gpio_infrared_rx.port, gpio_infrared_rx.pin) != 0) { - if(infrared_tim_rx.timeout_callback) - infrared_tim_rx.timeout_callback(infrared_tim_rx.timeout_context); - } - } - - /* Rising Edge */ - if(LL_TIM_IsActiveFlag_CC1(TIM2)) { - LL_TIM_ClearFlag_CC1(TIM2); - furi_assert(furi_hal_infrared_state == InfraredStateAsyncRx); - - if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC1S)) { - /* Low pin level is a Mark state of INFRARED signal. Invert level for further processing. */ - uint32_t duration = LL_TIM_IC_GetCaptureCH1(TIM2) - previous_captured_ch2; - if(infrared_tim_rx.capture_callback) - infrared_tim_rx.capture_callback(infrared_tim_rx.capture_context, 1, duration); - } else { - furi_assert(0); - } - } - - /* Falling Edge */ - if(LL_TIM_IsActiveFlag_CC2(TIM2)) { - LL_TIM_ClearFlag_CC2(TIM2); - furi_assert(furi_hal_infrared_state == InfraredStateAsyncRx); - - if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC2S)) { - /* High pin level is a Space state of INFRARED signal. Invert level for further processing. */ - uint32_t duration = LL_TIM_IC_GetCaptureCH2(TIM2); - previous_captured_ch2 = duration; - if(infrared_tim_rx.capture_callback) - infrared_tim_rx.capture_callback(infrared_tim_rx.capture_context, 0, duration); - } else { - furi_assert(0); - } - } -} - -void furi_hal_infrared_async_rx_start(void) { - furi_assert(furi_hal_infrared_state == InfraredStateIdle); - - furi_hal_gpio_init_ex( - &gpio_infrared_rx, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); - - 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_DIV1; - LL_TIM_Init(TIM2, &TIM_InitStruct); - - LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); - LL_TIM_DisableARRPreload(TIM2); - LL_TIM_SetTriggerInput(TIM2, LL_TIM_TS_TI1FP1); - LL_TIM_SetSlaveMode(TIM2, LL_TIM_SLAVEMODE_RESET); - LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2); - LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1); - LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_FALLING); - LL_TIM_DisableIT_TRIG(TIM2); - LL_TIM_DisableDMAReq_TRIG(TIM2); - LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET); - LL_TIM_EnableMasterSlaveMode(TIM2); - LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_DIRECTTI); - LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1); - LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1); - LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_RISING); - LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_INDIRECTTI); - LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); - - furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_infrared_tim_rx_isr, NULL); - furi_hal_infrared_state = InfraredStateAsyncRx; - - 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); - - LL_TIM_SetCounter(TIM2, 0); - LL_TIM_EnableCounter(TIM2); -} - -void furi_hal_infrared_async_rx_stop(void) { - furi_assert(furi_hal_infrared_state == InfraredStateAsyncRx); - - FURI_CRITICAL_ENTER(); - - LL_TIM_DeInit(TIM2); - furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); - furi_hal_infrared_state = InfraredStateIdle; - - FURI_CRITICAL_EXIT(); -} - -void furi_hal_infrared_async_rx_set_timeout(uint32_t timeout_us) { - LL_TIM_OC_SetCompareCH3(TIM2, timeout_us); - LL_TIM_OC_SetMode(TIM2, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_ACTIVE); - LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH3); - LL_TIM_EnableIT_CC3(TIM2); -} - -bool furi_hal_infrared_is_busy(void) { - return furi_hal_infrared_state != InfraredStateIdle; -} - -void furi_hal_infrared_async_rx_set_capture_isr_callback( - FuriHalInfraredRxCaptureCallback callback, - void* ctx) { - infrared_tim_rx.capture_callback = callback; - infrared_tim_rx.capture_context = ctx; -} - -void furi_hal_infrared_async_rx_set_timeout_isr_callback( - FuriHalInfraredRxTimeoutCallback callback, - void* ctx) { - infrared_tim_rx.timeout_callback = callback; - infrared_tim_rx.timeout_context = ctx; -} - -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); - - 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_TIM_DisableCounter(TIM1); - FuriStatus status = furi_semaphore_release(infrared_tim_tx.stop_semaphore); - furi_check(status == FuriStatusOk); - furi_hal_infrared_state = InfraredStateAsyncTxStopped; -} - -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); - 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) { - buf_num = 1; - } else { - furi_assert(0); - } - return buf_num; -} - -static void furi_hal_infrared_tx_dma_polarity_isr() { - if(LL_DMA_IsActiveFlag_TE1(DMA1)) { - LL_DMA_ClearFlag_TE1(DMA1); - furi_crash(NULL); - } - if(LL_DMA_IsActiveFlag_TC1(DMA1) && LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_1)) { - LL_DMA_ClearFlag_TC1(DMA1); - - furi_check( - (furi_hal_infrared_state == InfraredStateAsyncTx) || - (furi_hal_infrared_state == InfraredStateAsyncTxStopReq) || - (furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress)); - /* actually TC2 is processed and buffer is next buffer */ - uint8_t next_buf_num = furi_hal_infrared_get_current_dma_tx_buffer(); - furi_hal_infrared_tx_dma_set_polarity(next_buf_num, 0); - } -} - -static void furi_hal_infrared_tx_dma_isr() { - if(LL_DMA_IsActiveFlag_TE2(DMA1)) { - LL_DMA_ClearFlag_TE2(DMA1); - furi_crash(NULL); - } - if(LL_DMA_IsActiveFlag_HT2(DMA1) && LL_DMA_IsEnabledIT_HT(DMA1, LL_DMA_CHANNEL_2)) { - LL_DMA_ClearFlag_HT2(DMA1); - 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); - } 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); - } - } else if(furi_hal_infrared_state == InfraredStateAsyncTxStopReq) { - /* fallthrough */ - } else { - furi_crash(NULL); - } - } - if(LL_DMA_IsActiveFlag_TC2(DMA1) && LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_2)) { - LL_DMA_ClearFlag_TC2(DMA1); - furi_check( - (furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress) || - (furi_hal_infrared_state == InfraredStateAsyncTxStopReq) || - (furi_hal_infrared_state == InfraredStateAsyncTx)); - - uint8_t buf_num = furi_hal_infrared_get_current_dma_tx_buffer(); - uint8_t next_buf_num = !buf_num; - if(furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress) { - furi_hal_infrared_tx_dma_terminate(); - } else if( - infrared_tim_tx.buffer[buf_num].last_packet_end || - (infrared_tim_tx.buffer[buf_num].packet_end && - (furi_hal_infrared_state == InfraredStateAsyncTxStopReq))) { - furi_hal_infrared_state = InfraredStateAsyncTxStopInProgress; - furi_hal_infrared_tx_fill_buffer_last(next_buf_num); - furi_hal_infrared_tx_dma_set_buffer(next_buf_num); - } else { - /* if it's not end of the packet - continue receiving */ - furi_hal_infrared_tx_dma_set_buffer(next_buf_num); - } - if(infrared_tim_tx.signal_sent_callback && infrared_tim_tx.buffer[buf_num].packet_end && - (furi_hal_infrared_state != InfraredStateAsyncTxStopped)) { - infrared_tim_tx.signal_sent_callback(infrared_tim_tx.signal_sent_context); - } - } -} - -static void furi_hal_infrared_configure_tim_pwm_tx(uint32_t freq, float duty_cycle) { - /* LL_DBGMCU_APB2_GRP1_FreezePeriph(LL_DBGMCU_APB2_GRP1_TIM1_STOP); */ - - LL_TIM_DisableCounter(TIM1); - LL_TIM_SetRepetitionCounter(TIM1, 0); - LL_TIM_SetCounter(TIM1, 0); - LL_TIM_SetPrescaler(TIM1, 0); - LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP); - 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 - LL_TIM_DisableMasterSlaveMode(TIM1); - LL_TIM_EnableAllOutputs(TIM1); - LL_TIM_DisableIT_UPDATE(TIM1); - LL_TIM_EnableDMAReq_UPDATE(TIM1); - - NVIC_SetPriority(TIM1_UP_TIM16_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); - NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn); -} - -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 - dma_config.MemoryOrM2MDstAddress = (uint32_t)NULL; - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - dma_config.Mode = LL_DMA_MODE_NORMAL; - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - /* fill word to have other bits set to 0 */ - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; - 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_ClearFlag_TE1(DMA1); - LL_DMA_ClearFlag_TC1(DMA1); - - LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); - - furi_hal_interrupt_set_isr_ex( - FuriHalInterruptIdDma1Ch1, 4, furi_hal_infrared_tx_dma_polarity_isr, NULL); -} - -static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) { - LL_DMA_InitTypeDef dma_config = {0}; - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM1->RCR); - dma_config.MemoryOrM2MDstAddress = (uint32_t)NULL; - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - dma_config.Mode = LL_DMA_MODE_NORMAL; - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD; - 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_ClearFlag_TC2(DMA1); - LL_DMA_ClearFlag_HT2(DMA1); - LL_DMA_ClearFlag_TE2(DMA1); - - 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); - - furi_hal_interrupt_set_isr_ex( - FuriHalInterruptIdDma1Ch2, 5, furi_hal_infrared_tx_dma_isr, NULL); -} - -static void furi_hal_infrared_tx_fill_buffer_last(uint8_t buf_num) { - furi_assert(buf_num < 2); - furi_assert(furi_hal_infrared_state != InfraredStateAsyncRx); - furi_assert(furi_hal_infrared_state < InfraredStateMAX); - furi_assert(infrared_tim_tx.data_callback); - InfraredTxBuf* buffer = &infrared_tim_tx.buffer[buf_num]; - furi_assert(buffer->data != NULL); - (void)buffer->data; - furi_assert(buffer->polarity != NULL); - (void)buffer->polarity; - - infrared_tim_tx.buffer[buf_num].data[0] = 0; // 1 pulse - infrared_tim_tx.buffer[buf_num].polarity[0] = INFRARED_TX_CCMR_LOW; - infrared_tim_tx.buffer[buf_num].data[1] = 0; // 1 pulse - infrared_tim_tx.buffer[buf_num].polarity[1] = INFRARED_TX_CCMR_LOW; - infrared_tim_tx.buffer[buf_num].size = 2; - infrared_tim_tx.buffer[buf_num].last_packet_end = true; - infrared_tim_tx.buffer[buf_num].packet_end = true; -} - -static void furi_hal_infrared_tx_fill_buffer(uint8_t buf_num, uint8_t polarity_shift) { - furi_assert(buf_num < 2); - furi_assert(furi_hal_infrared_state != InfraredStateAsyncRx); - furi_assert(furi_hal_infrared_state < InfraredStateMAX); - furi_assert(infrared_tim_tx.data_callback); - InfraredTxBuf* buffer = &infrared_tim_tx.buffer[buf_num]; - furi_assert(buffer->data != NULL); - furi_assert(buffer->polarity != NULL); - - FuriHalInfraredTxGetDataState status = FuriHalInfraredTxGetDataStateOk; - uint32_t duration = 0; - bool level = 0; - size_t* size = &buffer->size; - size_t polarity_counter = 0; - while(polarity_shift--) { - buffer->polarity[polarity_counter++] = INFRARED_TX_CCMR_LOW; - } - - for(*size = 0; (*size < INFRARED_TIM_TX_DMA_BUFFER_SIZE) && - (status == FuriHalInfraredTxGetDataStateOk);) { - if(infrared_tim_tx.tx_timing_rest_duration > 0) { - if(infrared_tim_tx.tx_timing_rest_duration > 0xFFFF) { - buffer->data[*size] = 0xFFFF; - status = FuriHalInfraredTxGetDataStateOk; - } else { - buffer->data[*size] = infrared_tim_tx.tx_timing_rest_duration; - status = infrared_tim_tx.tx_timing_rest_status; - } - infrared_tim_tx.tx_timing_rest_duration -= buffer->data[*size]; - buffer->polarity[polarity_counter] = infrared_tim_tx.tx_timing_rest_level ? - INFRARED_TX_CCMR_HIGH : - INFRARED_TX_CCMR_LOW; - ++(*size); - ++polarity_counter; - continue; - } - - status = infrared_tim_tx.data_callback(infrared_tim_tx.data_context, &duration, &level); - - uint32_t num_of_impulses = roundf(duration / infrared_tim_tx.cycle_duration); - - if(num_of_impulses == 0) { - if((*size == 0) && (status == FuriHalInfraredTxGetDataStateDone)) { - /* if this is one sample in current buffer, but we - * have more to send - continue - */ - status = FuriHalInfraredTxGetDataStateOk; - } - } else if((num_of_impulses - 1) > 0xFFFF) { - infrared_tim_tx.tx_timing_rest_duration = num_of_impulses - 1; - infrared_tim_tx.tx_timing_rest_status = status; - infrared_tim_tx.tx_timing_rest_level = level; - status = FuriHalInfraredTxGetDataStateOk; - } else { - buffer->polarity[polarity_counter] = level ? INFRARED_TX_CCMR_HIGH : - INFRARED_TX_CCMR_LOW; - buffer->data[*size] = num_of_impulses - 1; - ++(*size); - ++polarity_counter; - } - } - - buffer->last_packet_end = (status == FuriHalInfraredTxGetDataStateLastDone); - buffer->packet_end = buffer->last_packet_end || (status == FuriHalInfraredTxGetDataStateDone); - - if(*size == 0) { - buffer->data[0] = 0; // 1 pulse - buffer->polarity[0] = INFRARED_TX_CCMR_LOW; - buffer->size = 1; - } -} - -static void furi_hal_infrared_tx_dma_set_polarity(uint8_t buf_num, uint8_t polarity_shift) { - furi_assert(buf_num < 2); - furi_assert(furi_hal_infrared_state < InfraredStateMAX); - InfraredTxBuf* buffer = &infrared_tim_tx.buffer[buf_num]; - furi_assert(buffer->polarity != NULL); - - FURI_CRITICAL_ENTER(); - bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1); - if(channel_enabled) { - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); - } - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)buffer->polarity); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, buffer->size + polarity_shift); - if(channel_enabled) { - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); - } - FURI_CRITICAL_EXIT(); -} - -static void furi_hal_infrared_tx_dma_set_buffer(uint8_t buf_num) { - furi_assert(buf_num < 2); - furi_assert(furi_hal_infrared_state < InfraredStateMAX); - InfraredTxBuf* buffer = &infrared_tim_tx.buffer[buf_num]; - furi_assert(buffer->data != NULL); - - /* non-circular mode requires disabled channel before setup */ - FURI_CRITICAL_ENTER(); - bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_2); - if(channel_enabled) { - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); - } - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)buffer->data); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, buffer->size); - if(channel_enabled) { - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); - } - FURI_CRITICAL_EXIT(); -} - -static void furi_hal_infrared_async_tx_free_resources(void) { - furi_assert( - (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); - LL_TIM_DeInit(TIM1); - - furi_semaphore_free(infrared_tim_tx.stop_semaphore); - free(infrared_tim_tx.buffer[0].data); - free(infrared_tim_tx.buffer[1].data); - free(infrared_tim_tx.buffer[0].polarity); - free(infrared_tim_tx.buffer[1].polarity); - - infrared_tim_tx.buffer[0].data = NULL; - infrared_tim_tx.buffer[1].data = NULL; - infrared_tim_tx.buffer[0].polarity = NULL; - infrared_tim_tx.buffer[1].polarity = NULL; -} - -void furi_hal_infrared_async_tx_start(uint32_t freq, float duty_cycle) { - if((duty_cycle > 1) || (duty_cycle <= 0) || (freq > INFRARED_MAX_FREQUENCY) || - (freq < INFRARED_MIN_FREQUENCY) || (infrared_tim_tx.data_callback == NULL)) { - furi_crash(NULL); - } - - furi_assert(furi_hal_infrared_state == InfraredStateIdle); - furi_assert(infrared_tim_tx.buffer[0].data == NULL); - furi_assert(infrared_tim_tx.buffer[1].data == NULL); - furi_assert(infrared_tim_tx.buffer[0].polarity == NULL); - furi_assert(infrared_tim_tx.buffer[1].polarity == NULL); - - size_t alloc_size_data = INFRARED_TIM_TX_DMA_BUFFER_SIZE * sizeof(uint16_t); - infrared_tim_tx.buffer[0].data = malloc(alloc_size_data); - infrared_tim_tx.buffer[1].data = malloc(alloc_size_data); - - size_t alloc_size_polarity = - (INFRARED_TIM_TX_DMA_BUFFER_SIZE + INFRARED_POLARITY_SHIFT) * sizeof(uint8_t); - infrared_tim_tx.buffer[0].polarity = malloc(alloc_size_polarity); - infrared_tim_tx.buffer[1].polarity = malloc(alloc_size_polarity); - - infrared_tim_tx.stop_semaphore = furi_semaphore_alloc(1, 0); - infrared_tim_tx.cycle_duration = 1000000.0 / freq; - infrared_tim_tx.tx_timing_rest_duration = 0; - - furi_hal_infrared_tx_fill_buffer(0, INFRARED_POLARITY_SHIFT); - - furi_hal_infrared_configure_tim_pwm_tx(freq, duty_cycle); - furi_hal_infrared_configure_tim_cmgr2_dma_tx(); - furi_hal_infrared_configure_tim_rcr_dma_tx(); - furi_hal_infrared_tx_dma_set_polarity(0, INFRARED_POLARITY_SHIFT); - furi_hal_infrared_tx_dma_set_buffer(0); - - 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); - 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); - - FURI_CRITICAL_ENTER(); - LL_TIM_GenerateEvent_UPDATE(TIM1); /* TIMx_RCR -> Repetition counter */ - LL_TIM_EnableCounter(TIM1); - FURI_CRITICAL_EXIT(); -} - -void furi_hal_infrared_async_tx_wait_termination(void) { - furi_assert(furi_hal_infrared_state >= InfraredStateAsyncTx); - furi_assert(furi_hal_infrared_state < InfraredStateMAX); - - FuriStatus status; - status = furi_semaphore_acquire(infrared_tim_tx.stop_semaphore, FuriWaitForever); - furi_check(status == FuriStatusOk); - furi_hal_infrared_async_tx_free_resources(); - furi_hal_infrared_state = InfraredStateIdle; -} - -void furi_hal_infrared_async_tx_stop(void) { - furi_assert(furi_hal_infrared_state >= InfraredStateAsyncTx); - furi_assert(furi_hal_infrared_state < InfraredStateMAX); - - FURI_CRITICAL_ENTER(); - if(furi_hal_infrared_state == InfraredStateAsyncTx) - furi_hal_infrared_state = InfraredStateAsyncTxStopReq; - FURI_CRITICAL_EXIT(); - - furi_hal_infrared_async_tx_wait_termination(); -} - -void furi_hal_infrared_async_tx_set_data_isr_callback( - FuriHalInfraredTxGetDataISRCallback callback, - void* context) { - furi_assert(furi_hal_infrared_state == InfraredStateIdle); - infrared_tim_tx.data_callback = callback; - infrared_tim_tx.data_context = context; -} - -void furi_hal_infrared_async_tx_set_signal_sent_isr_callback( - FuriHalInfraredTxSignalSentISRCallback callback, - void* context) { - infrared_tim_tx.signal_sent_callback = callback; - infrared_tim_tx.signal_sent_context = context; -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c deleted file mode 100644 index 2d6db8fbf12..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ /dev/null @@ -1,727 +0,0 @@ -#include -#include "furi_hal_nfc.h" -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#define TAG "FuriHalNfc" - -static const uint32_t clocks_in_ms = 64 * 1000; - -FuriEventFlag* event = NULL; -#define EVENT_FLAG_INTERRUPT (1UL << 0) -#define EVENT_FLAG_STATE_CHANGED (1UL << 1) -#define EVENT_FLAG_STOP (1UL << 2) -#define EVENT_FLAG_ALL (EVENT_FLAG_INTERRUPT | EVENT_FLAG_STATE_CHANGED | EVENT_FLAG_STOP) - -#define FURI_HAL_NFC_UID_INCOMPLETE (0x04) - -void furi_hal_nfc_init() { - 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); - } -} - -bool furi_hal_nfc_is_busy() { - return rfalNfcGetState() != RFAL_NFC_STATE_IDLE; -} - -void furi_hal_nfc_field_on() { - furi_hal_nfc_exit_sleep(); - st25r3916TxRxOn(); -} - -void furi_hal_nfc_field_off() { - st25r3916TxRxOff(); - furi_hal_nfc_start_sleep(); -} - -void furi_hal_nfc_start_sleep() { - rfalLowPowerModeStart(); -} - -void furi_hal_nfc_exit_sleep() { - rfalLowPowerModeStop(); -} - -bool furi_hal_nfc_detect(FuriHalNfcDevData* nfc_data, uint32_t timeout) { - furi_assert(nfc_data); - - rfalNfcDevice* dev_list = NULL; - uint8_t dev_cnt = 0; - bool detected = false; - - rfalLowPowerModeStop(); - rfalNfcState state = rfalNfcGetState(); - rfalNfcState state_old = 0; - if(state == RFAL_NFC_STATE_NOTINIT) { - rfalNfcInitialize(); - } - rfalNfcDiscoverParam params; - params.compMode = RFAL_COMPLIANCE_MODE_EMV; - params.techs2Find = RFAL_NFC_POLL_TECH_A | RFAL_NFC_POLL_TECH_B | RFAL_NFC_POLL_TECH_F | - RFAL_NFC_POLL_TECH_V | RFAL_NFC_POLL_TECH_AP2P | RFAL_NFC_POLL_TECH_ST25TB; - params.totalDuration = 1000; - params.devLimit = 3; - params.wakeupEnabled = false; - params.wakeupConfigDefault = true; - params.nfcfBR = RFAL_BR_212; - params.ap2pBR = RFAL_BR_424; - params.maxBR = RFAL_BR_KEEP; - params.GBLen = RFAL_NFCDEP_GB_MAX_LEN; - params.notifyCb = NULL; - - uint32_t start = DWT->CYCCNT; - rfalNfcDiscover(¶ms); - while(true) { - rfalNfcWorker(); - state = rfalNfcGetState(); - if(state != state_old) { - FURI_LOG_T(TAG, "State change %d -> %d", state_old, state); - } - state_old = state; - if(state == RFAL_NFC_STATE_ACTIVATED) { - detected = true; - break; - } - if(state == RFAL_NFC_STATE_POLL_ACTIVATION) { - start = DWT->CYCCNT; - continue; - } - if(state == RFAL_NFC_STATE_POLL_SELECT) { - rfalNfcSelect(0); - } - if(DWT->CYCCNT - start > timeout * clocks_in_ms) { - rfalNfcDeactivate(true); - FURI_LOG_T(TAG, "Timeout"); - break; - } - furi_delay_tick(1); - } - rfalNfcGetDevicesFound(&dev_list, &dev_cnt); - if(detected) { - if(dev_list[0].type == RFAL_NFC_LISTEN_TYPE_NFCA) { - nfc_data->type = FuriHalNfcTypeA; - nfc_data->atqa[0] = dev_list[0].dev.nfca.sensRes.anticollisionInfo; - nfc_data->atqa[1] = dev_list[0].dev.nfca.sensRes.platformInfo; - nfc_data->sak = dev_list[0].dev.nfca.selRes.sak; - uint8_t* cuid_start = dev_list[0].nfcid; - if(dev_list[0].nfcidLen == 7) { - cuid_start = &dev_list[0].nfcid[3]; - } - nfc_data->cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) | - (cuid_start[3]); - } else if(dev_list[0].type == RFAL_NFC_LISTEN_TYPE_NFCB) { - nfc_data->type = FuriHalNfcTypeB; - } else if(dev_list[0].type == RFAL_NFC_LISTEN_TYPE_NFCF) { - nfc_data->type = FuriHalNfcTypeF; - } else if(dev_list[0].type == RFAL_NFC_LISTEN_TYPE_NFCV) { - nfc_data->type = FuriHalNfcTypeV; - } - if(dev_list[0].rfInterface == RFAL_NFC_INTERFACE_RF) { - nfc_data->interface = FuriHalNfcInterfaceRf; - } else if(dev_list[0].rfInterface == RFAL_NFC_INTERFACE_ISODEP) { - nfc_data->interface = FuriHalNfcInterfaceIsoDep; - } else if(dev_list[0].rfInterface == RFAL_NFC_INTERFACE_NFCDEP) { - nfc_data->interface = FuriHalNfcInterfaceNfcDep; - } - nfc_data->uid_len = dev_list[0].nfcidLen; - memcpy(nfc_data->uid, dev_list[0].nfcid, nfc_data->uid_len); - } - - return detected; -} - -bool furi_hal_nfc_activate_nfca(uint32_t timeout, uint32_t* cuid) { - rfalNfcDevice* dev_list; - uint8_t dev_cnt = 0; - rfalLowPowerModeStop(); - rfalNfcState state = rfalNfcGetState(); - if(state == RFAL_NFC_STATE_NOTINIT) { - rfalNfcInitialize(); - } - rfalNfcDiscoverParam params = { - .compMode = RFAL_COMPLIANCE_MODE_NFC, - .techs2Find = RFAL_NFC_POLL_TECH_A, - .totalDuration = 1000, - .devLimit = 3, - .wakeupEnabled = false, - .wakeupConfigDefault = true, - .nfcfBR = RFAL_BR_212, - .ap2pBR = RFAL_BR_424, - .maxBR = RFAL_BR_KEEP, - .GBLen = RFAL_NFCDEP_GB_MAX_LEN, - .notifyCb = NULL, - }; - uint32_t start = DWT->CYCCNT; - rfalNfcDiscover(¶ms); - while(state != RFAL_NFC_STATE_ACTIVATED) { - rfalNfcWorker(); - state = rfalNfcGetState(); - FURI_LOG_T(TAG, "Current state %d", state); - if(state == RFAL_NFC_STATE_POLL_ACTIVATION) { - start = DWT->CYCCNT; - continue; - } - if(state == RFAL_NFC_STATE_POLL_SELECT) { - rfalNfcSelect(0); - } - if(DWT->CYCCNT - start > timeout * clocks_in_ms) { - rfalNfcDeactivate(true); - FURI_LOG_T(TAG, "Timeout"); - return false; - } - furi_thread_yield(); - } - rfalNfcGetDevicesFound(&dev_list, &dev_cnt); - // Take first device and set cuid - if(cuid) { - uint8_t* cuid_start = dev_list[0].nfcid; - if(dev_list[0].nfcidLen == 7) { - cuid_start = &dev_list[0].nfcid[3]; - } - *cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) | - (cuid_start[3]); - FURI_LOG_T(TAG, "Activated tag with cuid: %lX", *cuid); - } - return true; -} - -bool furi_hal_nfc_listen( - uint8_t* uid, - uint8_t uid_len, - uint8_t* atqa, - uint8_t sak, - bool activate_after_sak, - uint32_t timeout) { - rfalNfcState state = rfalNfcGetState(); - if(state == RFAL_NFC_STATE_NOTINIT) { - rfalNfcInitialize(); - } else if(state >= RFAL_NFC_STATE_ACTIVATED) { - rfalNfcDeactivate(false); - } - rfalLowPowerModeStop(); - rfalNfcDiscoverParam params = { - .compMode = RFAL_COMPLIANCE_MODE_NFC, - .techs2Find = RFAL_NFC_LISTEN_TECH_A, - .totalDuration = 1000, - .devLimit = 1, - .wakeupEnabled = false, - .wakeupConfigDefault = true, - .nfcfBR = RFAL_BR_212, - .ap2pBR = RFAL_BR_424, - .maxBR = RFAL_BR_KEEP, - .GBLen = RFAL_NFCDEP_GB_MAX_LEN, - .notifyCb = NULL, - .activate_after_sak = activate_after_sak, - }; - params.lmConfigPA.nfcidLen = uid_len; - memcpy(params.lmConfigPA.nfcid, uid, uid_len); - params.lmConfigPA.SENS_RES[0] = atqa[0]; - params.lmConfigPA.SENS_RES[1] = atqa[1]; - params.lmConfigPA.SEL_RES = sak; - rfalNfcDiscover(¶ms); - - uint32_t start = DWT->CYCCNT; - while(state != RFAL_NFC_STATE_ACTIVATED) { - rfalNfcWorker(); - state = rfalNfcGetState(); - if(DWT->CYCCNT - start > timeout * clocks_in_ms) { - rfalNfcDeactivate(true); - return false; - } - furi_delay_tick(1); - } - return true; -} - -static void furi_hal_nfc_read_fifo(uint8_t* data, uint16_t* bits) { - uint8_t fifo_status[2]; - uint8_t rx_buff[64]; - - st25r3916ReadMultipleRegisters( - ST25R3916_REG_FIFO_STATUS1, fifo_status, ST25R3916_FIFO_STATUS_LEN); - uint16_t rx_bytes = - ((((uint16_t)fifo_status[1] & ST25R3916_REG_FIFO_STATUS2_fifo_b_mask) >> - ST25R3916_REG_FIFO_STATUS2_fifo_b_shift) - << 8); - rx_bytes |= (((uint16_t)fifo_status[0]) & 0x00FFU); - st25r3916ReadFifo(rx_buff, rx_bytes); - - memcpy(data, rx_buff, rx_bytes); - *bits = rx_bytes * 8; -} - -void furi_hal_nfc_listen_sleep() { - st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SLEEP); -} - -bool furi_hal_nfc_listen_rx(FuriHalNfcTxRxContext* tx_rx, uint32_t timeout_ms) { - furi_assert(tx_rx); - - // Wait for interrupts - uint32_t start = furi_get_tick(); - bool data_received = false; - while(true) { - if(furi_hal_gpio_read(&gpio_nfc_irq_rfid_pull) == true) { - st25r3916CheckForReceivedInterrupts(); - if(st25r3916GetInterrupt(ST25R3916_IRQ_MASK_RXE)) { - furi_hal_nfc_read_fifo(tx_rx->rx_data, &tx_rx->rx_bits); - data_received = true; - break; - } - continue; - } - if(furi_get_tick() - start > timeout_ms) { - FURI_LOG_T(TAG, "Interrupt waiting timeout"); - furi_delay_tick(1); - break; - } - } - - return data_received; -} - -void furi_hal_nfc_listen_start(FuriHalNfcDevData* nfc_data) { - furi_assert(nfc_data); - - furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); - // Clear interrupts - st25r3916ClearInterrupts(); - // Mask all interrupts - st25r3916DisableInterrupts(ST25R3916_IRQ_MASK_ALL); - // RESET - st25r3916ExecuteCommand(ST25R3916_CMD_STOP); - // Setup registers - st25r3916WriteRegister( - ST25R3916_REG_OP_CONTROL, - ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en | - ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); - st25r3916WriteRegister( - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_targ_targ | ST25R3916_REG_MODE_om3 | ST25R3916_REG_MODE_om0); - st25r3916WriteRegister( - ST25R3916_REG_PASSIVE_TARGET, - ST25R3916_REG_PASSIVE_TARGET_fdel_2 | ST25R3916_REG_PASSIVE_TARGET_fdel_0 | - ST25R3916_REG_PASSIVE_TARGET_d_ac_ap2p | ST25R3916_REG_PASSIVE_TARGET_d_212_424_1r); - st25r3916WriteRegister(ST25R3916_REG_MASK_RX_TIMER, 0x02); - - // Mask interrupts - uint32_t clear_irq_mask = - (ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_RXE_PTA | ST25R3916_IRQ_MASK_WU_A_X | - ST25R3916_IRQ_MASK_WU_A); - st25r3916EnableInterrupts(clear_irq_mask); - - // Set 4 or 7 bytes UID - if(nfc_data->uid_len == 4) { - st25r3916ChangeRegisterBits( - ST25R3916_REG_AUX, ST25R3916_REG_AUX_nfc_id_mask, ST25R3916_REG_AUX_nfc_id_4bytes); - } else { - st25r3916ChangeRegisterBits( - ST25R3916_REG_AUX, ST25R3916_REG_AUX_nfc_id_mask, ST25R3916_REG_AUX_nfc_id_7bytes); - } - // Write PT Memory - uint8_t pt_memory[15] = {}; - memcpy(pt_memory, nfc_data->uid, nfc_data->uid_len); - pt_memory[10] = nfc_data->atqa[0]; - pt_memory[11] = nfc_data->atqa[1]; - if(nfc_data->uid_len == 4) { - pt_memory[12] = nfc_data->sak & ~FURI_HAL_NFC_UID_INCOMPLETE; - } else { - pt_memory[12] = FURI_HAL_NFC_UID_INCOMPLETE; - } - pt_memory[13] = nfc_data->sak & ~FURI_HAL_NFC_UID_INCOMPLETE; - pt_memory[14] = nfc_data->sak & ~FURI_HAL_NFC_UID_INCOMPLETE; - - st25r3916WritePTMem(pt_memory, sizeof(pt_memory)); - // Go to sense - st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SENSE); -} - -void rfal_interrupt_callback_handler() { - furi_event_flag_set(event, EVENT_FLAG_INTERRUPT); -} - -void rfal_state_changed_callback(void* context) { - UNUSED(context); - furi_event_flag_set(event, EVENT_FLAG_STATE_CHANGED); -} - -void furi_hal_nfc_stop() { - if(event) { - furi_event_flag_set(event, EVENT_FLAG_STOP); - } -} - -bool furi_hal_nfc_emulate_nfca( - uint8_t* uid, - uint8_t uid_len, - uint8_t* atqa, - uint8_t sak, - FuriHalNfcEmulateCallback callback, - void* context, - uint32_t timeout) { - rfalSetUpperLayerCallback(rfal_interrupt_callback_handler); - rfal_set_state_changed_callback(rfal_state_changed_callback); - - rfalLmConfPA config; - config.nfcidLen = uid_len; - memcpy(config.nfcid, uid, uid_len); - memcpy(config.SENS_RES, atqa, RFAL_LM_SENS_RES_LEN); - config.SEL_RES = sak; - uint8_t buff_rx[256]; - uint16_t buff_rx_size = 256; - uint16_t buff_rx_len = 0; - uint8_t buff_tx[1040]; - uint16_t buff_tx_len = 0; - uint32_t data_type = FURI_HAL_NFC_TXRX_DEFAULT; - - rfalLowPowerModeStop(); - if(rfalListenStart( - RFAL_LM_MASK_NFCA, - &config, - NULL, - NULL, - buff_rx, - rfalConvBytesToBits(buff_rx_size), - &buff_rx_len)) { - rfalListenStop(); - FURI_LOG_E(TAG, "Failed to start listen mode"); - return false; - } - while(true) { - buff_rx_len = 0; - buff_tx_len = 0; - uint32_t flag = furi_event_flag_wait(event, EVENT_FLAG_ALL, FuriFlagWaitAny, timeout); - if(flag == FuriFlagErrorTimeout || flag == EVENT_FLAG_STOP) { - break; - } - bool data_received = false; - buff_rx_len = 0; - rfalWorker(); - rfalLmState state = rfalListenGetState(&data_received, NULL); - if(data_received) { - rfalTransceiveBlockingRx(); - if(nfca_emulation_handler(buff_rx, buff_rx_len, buff_tx, &buff_tx_len)) { - if(rfalListenSleepStart( - RFAL_LM_STATE_SLEEP_A, - buff_rx, - rfalConvBytesToBits(buff_rx_size), - &buff_rx_len)) { - FURI_LOG_E(TAG, "Failed to enter sleep mode"); - break; - } else { - continue; - } - } - if(buff_tx_len) { - ReturnCode ret = rfalTransceiveBitsBlockingTx( - buff_tx, - buff_tx_len, - buff_rx, - sizeof(buff_rx), - &buff_rx_len, - data_type, - RFAL_FWT_NONE); - if(ret) { - FURI_LOG_E(TAG, "Tranceive failed with status %d", ret); - break; - } - continue; - } - if((state == RFAL_LM_STATE_ACTIVE_A || state == RFAL_LM_STATE_ACTIVE_Ax)) { - if(callback) { - callback(buff_rx, buff_rx_len, buff_tx, &buff_tx_len, &data_type, context); - } - if(!rfalIsExtFieldOn()) { - break; - } - if(buff_tx_len) { - if(buff_tx_len == UINT16_MAX) buff_tx_len = 0; - - ReturnCode ret = rfalTransceiveBitsBlockingTx( - buff_tx, - buff_tx_len, - buff_rx, - sizeof(buff_rx), - &buff_rx_len, - data_type, - RFAL_FWT_NONE); - if(ret) { - FURI_LOG_E(TAG, "Tranceive failed with status %d", ret); - continue; - } - } else { - break; - } - } - } - } - rfalListenStop(); - return true; -} - -static bool furi_hal_nfc_transparent_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { - furi_assert(tx_rx->nfca_signal); - - bool ret = false; - - // Start transparent mode - st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE); - // Reconfigure gpio for Transparent mode - furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc); - - // Send signal - FURI_CRITICAL_ENTER(); - nfca_signal_encode(tx_rx->nfca_signal, tx_rx->tx_data, tx_rx->tx_bits, tx_rx->tx_parity); - digital_signal_send(tx_rx->nfca_signal->tx_signal, &gpio_spi_r_mosi); - FURI_CRITICAL_EXIT(); - furi_hal_gpio_write(&gpio_spi_r_mosi, false); - - // Configure gpio back to SPI and exit transparent - furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); - st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA); - - if(tx_rx->sniff_tx) { - tx_rx->sniff_tx(tx_rx->tx_data, tx_rx->tx_bits, false, tx_rx->sniff_context); - } - - // Manually wait for interrupt - furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); - st25r3916ClearAndEnableInterrupts(ST25R3916_IRQ_MASK_RXE); - - uint32_t irq = 0; - uint8_t rxe = 0; - uint32_t start = DWT->CYCCNT; - while(true) { - if(!rfalIsExtFieldOn()) { - return false; - } - if(furi_hal_gpio_read(&gpio_nfc_irq_rfid_pull) == true) { - st25r3916ReadRegister(ST25R3916_REG_IRQ_MAIN, &rxe); - if(rxe & (1 << 4)) { - irq = 1; - break; - } - } - uint32_t timeout = DWT->CYCCNT - start; - if(timeout / furi_hal_cortex_instructions_per_microsecond() > timeout_ms * 1000) { - FURI_LOG_D(TAG, "Interrupt waiting timeout"); - break; - } - } - if(irq) { - uint8_t fifo_stat[2]; - st25r3916ReadMultipleRegisters( - ST25R3916_REG_FIFO_STATUS1, fifo_stat, ST25R3916_FIFO_STATUS_LEN); - uint16_t len = - ((((uint16_t)fifo_stat[1] & ST25R3916_REG_FIFO_STATUS2_fifo_b_mask) >> - ST25R3916_REG_FIFO_STATUS2_fifo_b_shift) - << RFAL_BITS_IN_BYTE); - len |= (((uint16_t)fifo_stat[0]) & 0x00FFU); - uint8_t rx[100]; - st25r3916ReadFifo(rx, len); - - tx_rx->rx_bits = len * 8; - memcpy(tx_rx->rx_data, rx, len); - - if(tx_rx->sniff_rx) { - tx_rx->sniff_rx(tx_rx->rx_data, tx_rx->rx_bits, false, tx_rx->sniff_context); - } - - ret = true; - } else { - FURI_LOG_E(TAG, "Timeout error"); - ret = false; - } - - st25r3916ClearInterrupts(); - - return ret; -} - -static uint32_t furi_hal_nfc_tx_rx_get_flag(FuriHalNfcTxRxType type) { - uint32_t flags = 0; - - if(type == FuriHalNfcTxRxTypeRxNoCrc) { - flags = RFAL_TXRX_FLAGS_CRC_RX_KEEP; - } else if(type == FuriHalNfcTxRxTypeRxKeepPar) { - flags = RFAL_TXRX_FLAGS_CRC_TX_MANUAL | RFAL_TXRX_FLAGS_CRC_RX_KEEP | - RFAL_TXRX_FLAGS_PAR_RX_KEEP; - } else if(type == FuriHalNfcTxRxTypeRaw) { - flags = RFAL_TXRX_FLAGS_CRC_TX_MANUAL | RFAL_TXRX_FLAGS_CRC_RX_KEEP | - RFAL_TXRX_FLAGS_PAR_RX_KEEP | RFAL_TXRX_FLAGS_PAR_TX_NONE; - } else if(type == FuriHalNfcTxRxTypeRxRaw) { - flags = RFAL_TXRX_FLAGS_CRC_TX_MANUAL | RFAL_TXRX_FLAGS_CRC_RX_KEEP | - RFAL_TXRX_FLAGS_PAR_RX_KEEP | RFAL_TXRX_FLAGS_PAR_TX_NONE; - } - - return flags; -} - -static uint16_t furi_hal_nfc_data_and_parity_to_bitstream( - uint8_t* data, - uint16_t len, - uint8_t* parity, - uint8_t* out) { - furi_assert(data); - furi_assert(out); - - uint8_t next_par_bit = 0; - uint16_t curr_bit_pos = 0; - for(uint16_t i = 0; i < len; i++) { - next_par_bit = FURI_BIT(parity[i / 8], 7 - (i % 8)); - if(curr_bit_pos % 8 == 0) { - out[curr_bit_pos / 8] = data[i]; - curr_bit_pos += 8; - out[curr_bit_pos / 8] = next_par_bit; - curr_bit_pos++; - } else { - out[curr_bit_pos / 8] |= data[i] << curr_bit_pos % 8; - out[curr_bit_pos / 8 + 1] = data[i] >> (8 - curr_bit_pos % 8); - out[curr_bit_pos / 8 + 1] |= next_par_bit << curr_bit_pos % 8; - curr_bit_pos += 9; - } - } - return curr_bit_pos; -} - -uint16_t furi_hal_nfc_bitstream_to_data_and_parity( - uint8_t* in_buff, - uint16_t in_buff_bits, - uint8_t* out_data, - uint8_t* out_parity) { - if(in_buff_bits % 9 != 0) { - return 0; - } - - uint8_t curr_byte = 0; - uint16_t bit_processed = 0; - memset(out_parity, 0, in_buff_bits / 9); - while(bit_processed < in_buff_bits) { - out_data[curr_byte] = in_buff[bit_processed / 8] >> bit_processed % 8; - out_data[curr_byte] |= in_buff[bit_processed / 8 + 1] << (8 - bit_processed % 8); - out_parity[curr_byte / 8] |= FURI_BIT(in_buff[bit_processed / 8 + 1], bit_processed % 8) - << (7 - curr_byte % 8); - bit_processed += 9; - curr_byte++; - } - return curr_byte; -} - -bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { - furi_assert(tx_rx); - - ReturnCode ret; - rfalNfcState state = RFAL_NFC_STATE_ACTIVATED; - uint8_t temp_tx_buff[FURI_HAL_NFC_DATA_BUFF_SIZE] = {}; - uint16_t temp_tx_bits = 0; - uint8_t* temp_rx_buff = NULL; - uint16_t* temp_rx_bits = NULL; - - if(tx_rx->tx_rx_type == FuriHalNfcTxRxTransparent) { - return furi_hal_nfc_transparent_tx_rx(tx_rx, timeout_ms); - } - - // Prepare data for FIFO if necessary - uint32_t flags = furi_hal_nfc_tx_rx_get_flag(tx_rx->tx_rx_type); - if(tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRaw) { - temp_tx_bits = furi_hal_nfc_data_and_parity_to_bitstream( - tx_rx->tx_data, tx_rx->tx_bits / 8, tx_rx->tx_parity, temp_tx_buff); - ret = rfalNfcDataExchangeCustomStart( - temp_tx_buff, temp_tx_bits, &temp_rx_buff, &temp_rx_bits, RFAL_FWT_NONE, flags); - } else { - ret = rfalNfcDataExchangeCustomStart( - tx_rx->tx_data, tx_rx->tx_bits, &temp_rx_buff, &temp_rx_bits, RFAL_FWT_NONE, flags); - } - if(ret != ERR_NONE) { - FURI_LOG_E(TAG, "Failed to start data exchange"); - return false; - } - - if(tx_rx->sniff_tx) { - bool crc_dropped = !(flags & RFAL_TXRX_FLAGS_CRC_TX_MANUAL); - tx_rx->sniff_tx(tx_rx->tx_data, tx_rx->tx_bits, crc_dropped, tx_rx->sniff_context); - } - - uint32_t start = DWT->CYCCNT; - while(state != RFAL_NFC_STATE_DATAEXCHANGE_DONE) { - rfalNfcWorker(); - state = rfalNfcGetState(); - ret = rfalNfcDataExchangeGetStatus(); - if(ret == ERR_BUSY) { - if(DWT->CYCCNT - start > timeout_ms * clocks_in_ms) { - FURI_LOG_D(TAG, "Timeout during data exchange"); - return false; - } - continue; - } else { - start = DWT->CYCCNT; - } - furi_delay_tick(1); - } - - if(tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRaw || - tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRxRaw) { - tx_rx->rx_bits = 8 * furi_hal_nfc_bitstream_to_data_and_parity( - temp_rx_buff, *temp_rx_bits, tx_rx->rx_data, tx_rx->rx_parity); - } else { - memcpy(tx_rx->rx_data, temp_rx_buff, MIN(*temp_rx_bits / 8, FURI_HAL_NFC_DATA_BUFF_SIZE)); - tx_rx->rx_bits = *temp_rx_bits; - } - - if(tx_rx->sniff_rx) { - bool crc_dropped = !(flags & RFAL_TXRX_FLAGS_CRC_RX_KEEP); - tx_rx->sniff_rx(tx_rx->rx_data, tx_rx->rx_bits, crc_dropped, tx_rx->sniff_context); - } - - return true; -} - -bool furi_hal_nfc_tx_rx_full(FuriHalNfcTxRxContext* tx_rx) { - uint16_t part_len_bytes; - - if(!furi_hal_nfc_tx_rx(tx_rx, 1000)) { - return false; - } - while(tx_rx->rx_bits && tx_rx->rx_data[0] == 0xAF) { - FuriHalNfcTxRxContext tmp = *tx_rx; - tmp.tx_data[0] = 0xAF; - tmp.tx_bits = 8; - if(!furi_hal_nfc_tx_rx(&tmp, 1000)) { - return false; - } - part_len_bytes = tmp.rx_bits / 8; - if(part_len_bytes > FURI_HAL_NFC_DATA_BUFF_SIZE - tx_rx->rx_bits / 8) { - FURI_LOG_W(TAG, "Overrun rx buf"); - return false; - } - if(part_len_bytes == 0) { - FURI_LOG_W(TAG, "Empty 0xAF response"); - return false; - } - memcpy(tx_rx->rx_data + tx_rx->rx_bits / 8, tmp.rx_data + 1, part_len_bytes - 1); - tx_rx->rx_data[0] = tmp.rx_data[0]; - tx_rx->rx_bits += 8 * (part_len_bytes - 1); - } - - return true; -} - -void furi_hal_nfc_sleep() { - rfalNfcDeactivate(false); - rfalLowPowerModeStart(); -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_os.c b/firmware/targets/f7/furi_hal/furi_hal_os.c deleted file mode 100644 index 97d022c933a..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_os.c +++ /dev/null @@ -1,188 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#define TAG "FuriHalOs" - -#define FURI_HAL_IDLE_TIMER_CLK_HZ 32768 -#define FURI_HAL_OS_TICK_HZ configTICK_RATE_HZ - -#define FURI_HAL_OS_IDLE_CNT_TO_TICKS(x) ((x * FURI_HAL_OS_TICK_HZ) / FURI_HAL_IDLE_TIMER_CLK_HZ) -#define FURI_HAL_OS_TICKS_TO_IDLE_CNT(x) ((x * FURI_HAL_IDLE_TIMER_CLK_HZ) / FURI_HAL_OS_TICK_HZ) - -#define FURI_HAL_IDLE_TIMER_TICK_PER_EPOCH (FURI_HAL_OS_IDLE_CNT_TO_TICKS(FURI_HAL_IDLE_TIMER_MAX)) -#define FURI_HAL_OS_MAX_SLEEP (FURI_HAL_IDLE_TIMER_TICK_PER_EPOCH - 1) - -#define FURI_HAL_OS_NVIC_IS_PENDING() (NVIC->ISPR[0] || NVIC->ISPR[1]) -#define FURI_HAL_OS_EXTI_LINE_0_31 0 -#define FURI_HAL_OS_EXTI_LINE_32_63 1 - -// Arbitrary (but small) number for better tick consistency -#define FURI_HAL_OS_EXTRA_CNT 3 - -#ifdef FURI_HAL_OS_DEBUG -#include - -void furi_hal_os_timer_callback() { - furi_hal_gpio_write(&gpio_ext_pa4, !furi_hal_gpio_read(&gpio_ext_pa4)); -} -#endif - -extern void xPortSysTickHandler(); - -static volatile uint32_t furi_hal_os_skew; - -void furi_hal_os_init() { - furi_hal_idle_timer_init(); - -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull); - furi_hal_gpio_init_simple(&gpio_ext_pa6, GpioModeOutputPushPull); - furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull); - FuriTimer* second_timer = - furi_timer_alloc(furi_hal_os_timer_callback, FuriTimerTypePeriodic, NULL); - furi_timer_start(second_timer, FURI_HAL_OS_TICK_HZ); -#endif - - FURI_LOG_I(TAG, "Init OK"); -} - -void furi_hal_os_tick() { - if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pa6, !furi_hal_gpio_read(&gpio_ext_pa6)); -#endif - xPortSysTickHandler(); - } -} - -#ifdef FURI_HAL_OS_DEBUG -// Find out the IRQ number while debugging -static void furi_hal_os_nvic_dbg_trap() { - for(int32_t i = WWDG_IRQn; i <= DMAMUX1_OVR_IRQn; i++) { - if(NVIC_GetPendingIRQ(i)) { - (void)i; - // Break here - __NOP(); - } - } -} - -// Find out the EXTI line number while debugging -static void furi_hal_os_exti_dbg_trap(uint32_t exti, uint32_t val) { - for(uint32_t i = 0; val; val >>= 1U, ++i) { - if(val & 1U) { - (void)exti; - (void)i; - // Break here - __NOP(); - } - } -} -#endif - -static inline bool furi_hal_os_is_pending_irq() { - if(FURI_HAL_OS_NVIC_IS_PENDING()) { -#ifdef FURI_HAL_OS_DEBUG - furi_hal_os_nvic_dbg_trap(); -#endif - return true; - } - - uint32_t exti_lines_active; - if((exti_lines_active = LL_EXTI_ReadFlag_0_31(LL_EXTI_LINE_ALL_0_31))) { -#ifdef FURI_HAL_OS_DEBUG - furi_hal_os_exti_dbg_trap(FURI_HAL_OS_EXTI_LINE_0_31, exti_lines_active); -#endif - return true; - } else if((exti_lines_active = LL_EXTI_ReadFlag_32_63(LL_EXTI_LINE_ALL_32_63))) { -#ifdef FURI_HAL_OS_DEBUG - furi_hal_os_exti_dbg_trap(FURI_HAL_OS_EXTI_LINE_32_63, exti_lines_active); -#endif - return true; - } - - return false; -} - -static inline uint32_t furi_hal_os_sleep(TickType_t expected_idle_ticks) { - // Stop ticks - furi_hal_clock_suspend_tick(); - - // Start wakeup timer - furi_hal_idle_timer_start(FURI_HAL_OS_TICKS_TO_IDLE_CNT(expected_idle_ticks)); - -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pa7, 0); -#endif - - // Go to sleep mode - furi_hal_power_sleep(); - -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pa7, 1); -#endif - - // Calculate how much time we spent in the sleep - uint32_t after_cnt = furi_hal_idle_timer_get_cnt() + furi_hal_os_skew + FURI_HAL_OS_EXTRA_CNT; - uint32_t after_tick = FURI_HAL_OS_IDLE_CNT_TO_TICKS(after_cnt); - furi_hal_os_skew = after_cnt - FURI_HAL_OS_TICKS_TO_IDLE_CNT(after_tick); - - bool cmpm = LL_LPTIM_IsActiveFlag_CMPM(FURI_HAL_IDLE_TIMER); - bool arrm = LL_LPTIM_IsActiveFlag_ARRM(FURI_HAL_IDLE_TIMER); - if(cmpm && arrm) after_tick += expected_idle_ticks; - - // Prepare tick timer for new round - furi_hal_idle_timer_reset(); - - // Resume ticks - furi_hal_clock_resume_tick(); - return after_tick; -} - -void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) { - if(!furi_hal_power_sleep_available()) { - __WFI(); - return; - } - - // Limit amount of ticks to maximum that timer can count - if(expected_idle_ticks > FURI_HAL_OS_MAX_SLEEP) { - expected_idle_ticks = FURI_HAL_OS_MAX_SLEEP; - } - - // Stop IRQ handling, no one should disturb us till we finish - __disable_irq(); - - // Confirm OS that sleep is still possible - if(eTaskConfirmSleepModeStatus() == eAbortSleep || furi_hal_os_is_pending_irq()) { - __enable_irq(); - return; - } - - // Sleep and track how much ticks we spent sleeping - uint32_t completed_ticks = furi_hal_os_sleep(expected_idle_ticks); - // Notify system about time spent in sleep - if(completed_ticks > 0) { - vTaskStepTick(MIN(completed_ticks, expected_idle_ticks)); - } - - // Reenable IRQ - __enable_irq(); -} - -void vApplicationStackOverflowHook(TaskHandle_t xTask, char* pcTaskName) { - UNUSED(xTask); - furi_hal_console_puts("\r\n\r\n stack overflow in "); - furi_hal_console_puts(pcTaskName); - furi_hal_console_puts("\r\n\r\n"); - furi_crash("StackOverflow"); -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c deleted file mode 100644 index 246383921fb..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ /dev/null @@ -1,575 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#define TAG "FuriHalPower" - -#ifdef FURI_HAL_POWER_DEEP_SLEEP_ENABLED -#define FURI_HAL_POWER_DEEP_INSOMNIA 0 -#else -#define FURI_HAL_POWER_DEEP_INSOMNIA 1 -#endif - -typedef struct { - volatile uint8_t insomnia; - volatile uint8_t deep_insomnia; - volatile uint8_t suppress_charge; - - uint8_t gauge_initialized; - uint8_t charger_initialized; -} FuriHalPower; - -static volatile FuriHalPower furi_hal_power = { - .insomnia = 0, - .deep_insomnia = FURI_HAL_POWER_DEEP_INSOMNIA, - .suppress_charge = 0, -}; - -const ParamCEDV cedv = { - .cedv_conf.gauge_conf = - { - .CCT = 1, - .CSYNC = 0, - .EDV_CMP = 0, - .SC = 1, - .FIXED_EDV0 = 1, - .FCC_LIM = 1, - .FC_FOR_VDQ = 1, - .IGNORE_SD = 1, - .SME0 = 0, - }, - .full_charge_cap = 2101, - .design_cap = 2101, - .EDV0 = 3300, - .EDV1 = 3321, - .EDV2 = 3355, - .EMF = 3679, - .C0 = 430, - .C1 = 0, - .R1 = 408, - .R0 = 334, - .T0 = 4626, - .TC = 11, - .DOD0 = 4044, - .DOD10 = 3905, - .DOD20 = 3807, - .DOD30 = 3718, - .DOD40 = 3642, - .DOD50 = 3585, - .DOD60 = 3546, - .DOD70 = 3514, - .DOD80 = 3477, - .DOD90 = 3411, - .DOD100 = 3299, -}; - -void furi_hal_power_init() { - LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1); - LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN); - - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - bq27220_init(&furi_hal_i2c_handle_power, &cedv); - bq25896_init(&furi_hal_i2c_handle_power); - furi_hal_i2c_release(&furi_hal_i2c_handle_power); - -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_init_simple(&gpio_ext_pb2, GpioModeOutputPushPull); - furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull); -#endif - - FURI_LOG_I(TAG, "Init OK"); -} - -bool furi_hal_power_gauge_is_ok() { - bool ret = true; - - BatteryStatus battery_status; - OperationStatus operation_status; - - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - - if(bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) == BQ27220_ERROR || - bq27220_get_operation_status(&furi_hal_i2c_handle_power, &operation_status) == - BQ27220_ERROR) { - ret = false; - } else { - ret &= battery_status.BATTPRES; - ret &= operation_status.INITCOMP; - ret &= (cedv.design_cap == bq27220_get_design_capacity(&furi_hal_i2c_handle_power)); - } - - furi_hal_i2c_release(&furi_hal_i2c_handle_power); - - return ret; -} - -uint16_t furi_hal_power_insomnia_level() { - return furi_hal_power.insomnia; -} - -void furi_hal_power_insomnia_enter() { - FURI_CRITICAL_ENTER(); - furi_assert(furi_hal_power.insomnia < UINT8_MAX); - furi_hal_power.insomnia++; - FURI_CRITICAL_EXIT(); -} - -void furi_hal_power_insomnia_exit() { - FURI_CRITICAL_ENTER(); - furi_assert(furi_hal_power.insomnia > 0); - furi_hal_power.insomnia--; - FURI_CRITICAL_EXIT(); -} - -bool furi_hal_power_sleep_available() { - return furi_hal_power.insomnia == 0; -} - -bool furi_hal_power_deep_sleep_available() { - return furi_hal_bt_is_alive() && furi_hal_power.deep_insomnia == 0; -} - -void furi_hal_power_light_sleep() { - __WFI(); -} - -static inline void furi_hal_power_suspend_aux_periphs() { - // Disable USART - furi_hal_uart_suspend(FuriHalUartIdUSART1); - furi_hal_uart_suspend(FuriHalUartIdLPUART1); - // TODO: Disable USB -} - -static inline void furi_hal_power_resume_aux_periphs() { - // Re-enable USART - furi_hal_uart_resume(FuriHalUartIdUSART1); - furi_hal_uart_resume(FuriHalUartIdLPUART1); - // TODO: Re-enable USB -} - -void furi_hal_power_deep_sleep() { - furi_hal_power_suspend_aux_periphs(); - - while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) - ; - - if(!LL_HSEM_1StepLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID)) { - if(LL_PWR_IsActiveFlag_C2DS() || LL_PWR_IsActiveFlag_C2SB()) { - // Release ENTRY_STOP_MODE semaphore - LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0); - - // The switch on HSI before entering Stop Mode is required - furi_hal_clock_switch_to_hsi(); - } - } else { - /** - * The switch on HSI before entering Stop Mode is required - */ - furi_hal_clock_switch_to_hsi(); - } - - /* Release RCC semaphore */ - LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); - - // Prepare deep sleep - LL_PWR_SetPowerMode(LL_PWR_MODE_STOP2); - LL_C2_PWR_SetPowerMode(LL_PWR_MODE_STOP2); - LL_LPM_EnableDeepSleep(); - -#if defined(__CC_ARM) - // Force store operations - __force_stores(); -#endif - - __WFI(); - - LL_LPM_EnableSleep(); - - // Make sure that values differ to prevent disaster on wfi - LL_PWR_SetPowerMode(LL_PWR_MODE_STOP0); - LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); - - LL_PWR_ClearFlag_C1STOP_C1STB(); - LL_PWR_ClearFlag_C2STOP_C2STB(); - - /* Release ENTRY_STOP_MODE semaphore */ - LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0); - - while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) - ; - - if(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) { - furi_hal_clock_switch_to_pll(); - } - - LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); - - furi_hal_power_resume_aux_periphs(); -} - -void furi_hal_power_sleep() { - if(furi_hal_power_deep_sleep_available()) { -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pc3, 1); -#endif - - furi_hal_power_deep_sleep(); - -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pc3, 0); -#endif - } else { -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pb2, 1); -#endif - - furi_hal_power_light_sleep(); - -#ifdef FURI_HAL_OS_DEBUG - furi_hal_gpio_write(&gpio_ext_pb2, 0); -#endif - } -} - -uint8_t furi_hal_power_get_pct() { - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - uint8_t ret = bq27220_get_state_of_charge(&furi_hal_i2c_handle_power); - furi_hal_i2c_release(&furi_hal_i2c_handle_power); - return ret; -} - -uint8_t furi_hal_power_get_bat_health_pct() { - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - uint8_t ret = bq27220_get_state_of_health(&furi_hal_i2c_handle_power); - furi_hal_i2c_release(&furi_hal_i2c_handle_power); - return ret; -} - -bool furi_hal_power_is_charging() { - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - bool ret = bq25896_is_charging(&furi_hal_i2c_handle_power); - furi_hal_i2c_release(&furi_hal_i2c_handle_power); - return ret; -} - -void furi_hal_power_shutdown() { - furi_hal_power_insomnia_enter(); - - furi_hal_bt_reinit(); - - while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) - ; - - if(!LL_HSEM_1StepLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID)) { - if(LL_PWR_IsActiveFlag_C2DS() || LL_PWR_IsActiveFlag_C2SB()) { - // Release ENTRY_STOP_MODE semaphore - LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0); - } - } - - // Prepare Wakeup pin - LL_PWR_SetWakeUpPinPolarityLow(LL_PWR_WAKEUP_PIN2); - LL_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN2); - LL_C2_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN2); - - /* Release RCC semaphore */ - LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); - - LL_PWR_DisableBootC2(); - LL_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); - LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); - LL_LPM_EnableDeepSleep(); - - __WFI(); - furi_crash("Insomniac core2"); -} - -void furi_hal_power_off() { - // Crutch: shutting down with ext 3V3 off is causing LSE to stop - furi_hal_power_enable_external_3_3v(); - furi_delay_us(1000); - // Send poweroff to charger - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - bq25896_poweroff(&furi_hal_i2c_handle_power); - furi_hal_i2c_release(&furi_hal_i2c_handle_power); -} - -void furi_hal_power_reset() { - NVIC_SystemReset(); -} - -void furi_hal_power_enable_otg() { - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - bq25896_enable_otg(&furi_hal_i2c_handle_power); - furi_hal_i2c_release(&furi_hal_i2c_handle_power); -} - -void furi_hal_power_disable_otg() { - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - bq25896_disable_otg(&furi_hal_i2c_handle_power); - furi_hal_i2c_release(&furi_hal_i2c_handle_power); -} - -bool furi_hal_power_is_otg_enabled() { - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - bool ret = bq25896_is_otg_enabled(&furi_hal_i2c_handle_power); - furi_hal_i2c_release(&furi_hal_i2c_handle_power); - return ret; -} - -void furi_hal_power_check_otg_status() { - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - if(bq25896_check_otg_fault(&furi_hal_i2c_handle_power)) - bq25896_disable_otg(&furi_hal_i2c_handle_power); - furi_hal_i2c_release(&furi_hal_i2c_handle_power); -} - -uint32_t furi_hal_power_get_battery_remaining_capacity() { - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - uint32_t ret = bq27220_get_remaining_capacity(&furi_hal_i2c_handle_power); - furi_hal_i2c_release(&furi_hal_i2c_handle_power); - return ret; -} - -uint32_t furi_hal_power_get_battery_full_capacity() { - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - uint32_t ret = bq27220_get_full_charge_capacity(&furi_hal_i2c_handle_power); - furi_hal_i2c_release(&furi_hal_i2c_handle_power); - return ret; -} - -uint32_t furi_hal_power_get_battery_design_capacity() { - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - uint32_t ret = bq27220_get_design_capacity(&furi_hal_i2c_handle_power); - furi_hal_i2c_release(&furi_hal_i2c_handle_power); - return ret; -} - -float furi_hal_power_get_battery_voltage(FuriHalPowerIC ic) { - float ret = 0.0f; - - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - if(ic == FuriHalPowerICCharger) { - ret = (float)bq25896_get_vbat_voltage(&furi_hal_i2c_handle_power) / 1000.0f; - } else if(ic == FuriHalPowerICFuelGauge) { - ret = (float)bq27220_get_voltage(&furi_hal_i2c_handle_power) / 1000.0f; - } - furi_hal_i2c_release(&furi_hal_i2c_handle_power); - - return ret; -} - -float furi_hal_power_get_battery_current(FuriHalPowerIC ic) { - float ret = 0.0f; - - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - if(ic == FuriHalPowerICCharger) { - ret = (float)bq25896_get_vbat_current(&furi_hal_i2c_handle_power) / 1000.0f; - } else if(ic == FuriHalPowerICFuelGauge) { - ret = (float)bq27220_get_current(&furi_hal_i2c_handle_power) / 1000.0f; - } - furi_hal_i2c_release(&furi_hal_i2c_handle_power); - - return ret; -} - -static float furi_hal_power_get_battery_temperature_internal(FuriHalPowerIC ic) { - float ret = 0.0f; - - if(ic == FuriHalPowerICCharger) { - // Linear approximation, +/- 5 C - ret = (71.0f - (float)bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power) / 1000) / 0.6f; - } else if(ic == FuriHalPowerICFuelGauge) { - ret = ((float)bq27220_get_temperature(&furi_hal_i2c_handle_power) - 2731.0f) / 10.0f; - } - - return ret; -} - -float furi_hal_power_get_battery_temperature(FuriHalPowerIC ic) { - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - float ret = furi_hal_power_get_battery_temperature_internal(ic); - furi_hal_i2c_release(&furi_hal_i2c_handle_power); - - return ret; -} - -float furi_hal_power_get_usb_voltage() { - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - float ret = (float)bq25896_get_vbus_voltage(&furi_hal_i2c_handle_power) / 1000.0f; - furi_hal_i2c_release(&furi_hal_i2c_handle_power); - return ret; -} - -void furi_hal_power_dump_state() { - BatteryStatus battery_status; - OperationStatus operation_status; - - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - - if(bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) == BQ27220_ERROR || - bq27220_get_operation_status(&furi_hal_i2c_handle_power, &operation_status) == - BQ27220_ERROR) { - printf("Failed to get bq27220 status. Communication error.\r\n"); - } else { - // Operation status register - printf( - "bq27220: CALMD: %d, SEC: %d, EDV2: %d, VDQ: %d, INITCOMP: %d, SMTH: %d, BTPINT: %d, CFGUPDATE: %d\r\n", - operation_status.CALMD, - operation_status.SEC, - operation_status.EDV2, - operation_status.VDQ, - operation_status.INITCOMP, - operation_status.SMTH, - operation_status.BTPINT, - operation_status.CFGUPDATE); - // Battery status register, part 1 - printf( - "bq27220: CHGINH: %d, FC: %d, OTD: %d, OTC: %d, SLEEP: %d, OCVFAIL: %d, OCVCOMP: %d, FD: %d\r\n", - battery_status.CHGINH, - battery_status.FC, - battery_status.OTD, - battery_status.OTC, - battery_status.SLEEP, - battery_status.OCVFAIL, - battery_status.OCVCOMP, - battery_status.FD); - // Battery status register, part 2 - printf( - "bq27220: DSG: %d, SYSDWN: %d, TDA: %d, BATTPRES: %d, AUTH_GD: %d, OCVGD: %d, TCA: %d, RSVD: %d\r\n", - battery_status.DSG, - battery_status.SYSDWN, - battery_status.TDA, - battery_status.BATTPRES, - battery_status.AUTH_GD, - battery_status.OCVGD, - battery_status.TCA, - battery_status.RSVD); - // Voltage and current info - printf( - "bq27220: Full capacity: %dmAh, Design capacity: %dmAh, Remaining capacity: %dmAh, State of Charge: %d%%, State of health: %d%%\r\n", - bq27220_get_full_charge_capacity(&furi_hal_i2c_handle_power), - bq27220_get_design_capacity(&furi_hal_i2c_handle_power), - bq27220_get_remaining_capacity(&furi_hal_i2c_handle_power), - bq27220_get_state_of_charge(&furi_hal_i2c_handle_power), - bq27220_get_state_of_health(&furi_hal_i2c_handle_power)); - printf( - "bq27220: Voltage: %dmV, Current: %dmA, Temperature: %dC\r\n", - bq27220_get_voltage(&furi_hal_i2c_handle_power), - bq27220_get_current(&furi_hal_i2c_handle_power), - (int)furi_hal_power_get_battery_temperature_internal(FuriHalPowerICFuelGauge)); - } - - printf( - "bq25896: VBUS: %d, VSYS: %d, VBAT: %d, Current: %d, NTC: %ldm%%\r\n", - bq25896_get_vbus_voltage(&furi_hal_i2c_handle_power), - bq25896_get_vsys_voltage(&furi_hal_i2c_handle_power), - bq25896_get_vbat_voltage(&furi_hal_i2c_handle_power), - bq25896_get_vbat_current(&furi_hal_i2c_handle_power), - bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power)); - - furi_hal_i2c_release(&furi_hal_i2c_handle_power); -} - -void furi_hal_power_enable_external_3_3v() { - furi_hal_gpio_write(&periph_power, 1); -} - -void furi_hal_power_disable_external_3_3v() { - furi_hal_gpio_write(&periph_power, 0); -} - -void furi_hal_power_suppress_charge_enter() { - vTaskSuspendAll(); - bool disable_charging = furi_hal_power.suppress_charge == 0; - furi_hal_power.suppress_charge++; - xTaskResumeAll(); - - if(disable_charging) { - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - bq25896_disable_charging(&furi_hal_i2c_handle_power); - furi_hal_i2c_release(&furi_hal_i2c_handle_power); - } -} - -void furi_hal_power_suppress_charge_exit() { - vTaskSuspendAll(); - furi_hal_power.suppress_charge--; - bool enable_charging = furi_hal_power.suppress_charge == 0; - xTaskResumeAll(); - - if(enable_charging) { - furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); - bq25896_enable_charging(&furi_hal_i2c_handle_power); - furi_hal_i2c_release(&furi_hal_i2c_handle_power); - } -} - -void furi_hal_power_info_get(FuriHalPowerInfoCallback out, void* context) { - furi_assert(out); - - string_t value; - string_init(value); - - // Power Info version - out("power_info_major", "1", false, context); - out("power_info_minor", "0", false, context); - - uint8_t charge = furi_hal_power_get_pct(); - - string_printf(value, "%u", charge); - out("charge_level", string_get_cstr(value), false, context); - - if(furi_hal_power_is_charging()) { - if(charge < 100) { - string_printf(value, "charging"); - } else { - string_printf(value, "charged"); - } - } else { - string_printf(value, "discharging"); - } - out("charge_state", string_get_cstr(value), false, context); - - uint16_t voltage = - (uint16_t)(furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge) * 1000.f); - string_printf(value, "%u", voltage); - out("battery_voltage", string_get_cstr(value), false, context); - - int16_t current = - (int16_t)(furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge) * 1000.f); - string_printf(value, "%d", current); - out("battery_current", string_get_cstr(value), false, context); - - int16_t temperature = (int16_t)furi_hal_power_get_battery_temperature(FuriHalPowerICFuelGauge); - string_printf(value, "%d", temperature); - out("gauge_temp", string_get_cstr(value), false, context); - - string_printf(value, "%u", furi_hal_power_get_bat_health_pct()); - out("battery_health", string_get_cstr(value), false, context); - - string_printf(value, "%u", furi_hal_power_get_battery_remaining_capacity()); - out("capacity_remain", string_get_cstr(value), false, context); - - string_printf(value, "%u", furi_hal_power_get_battery_full_capacity()); - out("capacity_full", string_get_cstr(value), false, context); - - string_printf(value, "%u", furi_hal_power_get_battery_design_capacity()); - out("capacity_design", string_get_cstr(value), true, context); - - string_clear(value); -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_random.c b/firmware/targets/f7/furi_hal/furi_hal_random.c deleted file mode 100644 index cd019c0d9ad..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_random.c +++ /dev/null @@ -1,65 +0,0 @@ -#include "furi_hal_random.h" -#include -#include - -#include -#include - -#include - -#define TAG "FuriHalRandom" - -uint32_t furi_hal_random_get() { - while(LL_HSEM_1StepLock(HSEM, CFG_HW_RNG_SEMID)) - ; - LL_RNG_Enable(RNG); - - while(!LL_RNG_IsActiveFlag_DRDY(RNG)) - ; - - if((LL_RNG_IsActiveFlag_CECS(RNG)) || (LL_RNG_IsActiveFlag_SECS(RNG))) { - furi_crash("TRNG error"); - } - - uint32_t random_val = LL_RNG_ReadRandData32(RNG); - - LL_RNG_Disable(RNG); - LL_HSEM_ReleaseLock(HSEM, CFG_HW_RNG_SEMID, 0); - - return random_val; -} - -void furi_hal_random_fill_buf(uint8_t* buf, uint32_t len) { - while(LL_HSEM_1StepLock(HSEM, CFG_HW_RNG_SEMID)) - ; - LL_RNG_Enable(RNG); - - for(uint32_t i = 0; i < len; i += 4) { - while(!LL_RNG_IsActiveFlag_DRDY(RNG)) - ; - - if((LL_RNG_IsActiveFlag_CECS(RNG)) || (LL_RNG_IsActiveFlag_SECS(RNG))) { - furi_crash("TRNG error"); - } - - uint32_t random_val = LL_RNG_ReadRandData32(RNG); - - uint8_t len_cur = ((i + 4) < len) ? (4) : (len - i); - memcpy(&buf[i], &random_val, len_cur); - } - - LL_RNG_Disable(RNG); - LL_HSEM_ReleaseLock(HSEM, CFG_HW_RNG_SEMID, 0); -} - -void srand(unsigned seed) { - UNUSED(seed); -} - -int rand() { - return (furi_hal_random_get() & RAND_MAX); -} - -long random() { - return (furi_hal_random_get() & RAND_MAX); -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c deleted file mode 100644 index 98ebedf51cf..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ /dev/null @@ -1,168 +0,0 @@ -#include -#include - -#include -#include - -const GpioPin vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin}; -const GpioPin ibutton_gpio = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin}; - -const GpioPin gpio_cc1101_g0 = {.port = CC1101_G0_GPIO_Port, .pin = CC1101_G0_Pin}; -const GpioPin gpio_rf_sw_0 = {.port = RF_SW_0_GPIO_Port, .pin = RF_SW_0_Pin}; - -const GpioPin gpio_subghz_cs = {.port = CC1101_CS_GPIO_Port, .pin = CC1101_CS_Pin}; -const GpioPin gpio_display_cs = {.port = DISPLAY_CS_GPIO_Port, .pin = DISPLAY_CS_Pin}; -const GpioPin gpio_display_rst_n = {.port = DISPLAY_RST_GPIO_Port, .pin = DISPLAY_RST_Pin}; -const GpioPin gpio_display_di = {.port = DISPLAY_DI_GPIO_Port, .pin = DISPLAY_DI_Pin}; -const GpioPin gpio_sdcard_cs = {.port = SD_CS_GPIO_Port, .pin = SD_CS_Pin}; -const GpioPin gpio_sdcard_cd = {.port = SD_CD_GPIO_Port, .pin = SD_CD_Pin}; -const GpioPin gpio_nfc_cs = {.port = NFC_CS_GPIO_Port, .pin = NFC_CS_Pin}; - -const GpioPin gpio_button_up = {.port = GPIOB, .pin = LL_GPIO_PIN_10}; -const GpioPin gpio_button_down = {.port = GPIOC, .pin = LL_GPIO_PIN_6}; -const GpioPin gpio_button_right = {.port = GPIOB, .pin = LL_GPIO_PIN_12}; -const GpioPin gpio_button_left = {.port = GPIOB, .pin = LL_GPIO_PIN_11}; -const GpioPin gpio_button_ok = {.port = GPIOH, .pin = LL_GPIO_PIN_3}; -const GpioPin gpio_button_back = {.port = GPIOC, .pin = LL_GPIO_PIN_13}; - -const GpioPin gpio_spi_d_miso = {.port = SPI_D_MISO_GPIO_Port, .pin = SPI_D_MISO_Pin}; -const GpioPin gpio_spi_d_mosi = {.port = SPI_D_MOSI_GPIO_Port, .pin = SPI_D_MOSI_Pin}; -const GpioPin gpio_spi_d_sck = {.port = SPI_D_SCK_GPIO_Port, .pin = SPI_D_SCK_Pin}; -const GpioPin gpio_spi_r_miso = {.port = SPI_R_MISO_GPIO_Port, .pin = SPI_R_MISO_Pin}; -const GpioPin gpio_spi_r_mosi = {.port = SPI_R_MOSI_GPIO_Port, .pin = SPI_R_MOSI_Pin}; -const GpioPin gpio_spi_r_sck = {.port = SPI_R_SCK_GPIO_Port, .pin = SPI_R_SCK_Pin}; - -const GpioPin gpio_ext_pc0 = {.port = GPIOC, .pin = LL_GPIO_PIN_0}; -const GpioPin gpio_ext_pc1 = {.port = GPIOC, .pin = LL_GPIO_PIN_1}; -const GpioPin gpio_ext_pc3 = {.port = GPIOC, .pin = LL_GPIO_PIN_3}; -const GpioPin gpio_ext_pb2 = {.port = GPIOB, .pin = LL_GPIO_PIN_2}; -const GpioPin gpio_ext_pb3 = {.port = GPIOB, .pin = LL_GPIO_PIN_3}; -const GpioPin gpio_ext_pa4 = {.port = GPIOA, .pin = LL_GPIO_PIN_4}; -const GpioPin gpio_ext_pa6 = {.port = GPIOA, .pin = LL_GPIO_PIN_6}; -const GpioPin gpio_ext_pa7 = {.port = GPIOA, .pin = LL_GPIO_PIN_7}; - -const GpioPin gpio_nfc_irq_rfid_pull = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin}; -const GpioPin gpio_rfid_carrier_out = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin}; -const GpioPin gpio_rfid_data_in = {.port = RFID_RF_IN_GPIO_Port, .pin = RFID_RF_IN_Pin}; -const GpioPin gpio_rfid_carrier = {.port = RFID_CARRIER_GPIO_Port, .pin = RFID_CARRIER_Pin}; - -const GpioPin gpio_infrared_rx = {.port = IR_RX_GPIO_Port, .pin = IR_RX_Pin}; -const GpioPin gpio_infrared_tx = {.port = IR_TX_GPIO_Port, .pin = IR_TX_Pin}; - -const GpioPin gpio_usart_tx = {.port = USART1_TX_Port, .pin = USART1_TX_Pin}; -const GpioPin gpio_usart_rx = {.port = USART1_RX_Port, .pin = USART1_RX_Pin}; - -const GpioPin gpio_i2c_power_sda = {.port = GPIOA, .pin = LL_GPIO_PIN_10}; -const GpioPin gpio_i2c_power_scl = {.port = GPIOA, .pin = LL_GPIO_PIN_9}; - -const GpioPin gpio_speaker = {.port = GPIOB, .pin = LL_GPIO_PIN_8}; - -const GpioPin periph_power = {.port = GPIOA, .pin = LL_GPIO_PIN_3}; - -const GpioPin gpio_usb_dm = {.port = GPIOA, .pin = LL_GPIO_PIN_11}; -const GpioPin gpio_usb_dp = {.port = GPIOA, .pin = LL_GPIO_PIN_12}; - -const InputPin input_pins[] = { - {.gpio = &gpio_button_up, .key = InputKeyUp, .inverted = true, .name = "Up"}, - {.gpio = &gpio_button_down, .key = InputKeyDown, .inverted = true, .name = "Down"}, - {.gpio = &gpio_button_right, .key = InputKeyRight, .inverted = true, .name = "Right"}, - {.gpio = &gpio_button_left, .key = InputKeyLeft, .inverted = true, .name = "Left"}, - {.gpio = &gpio_button_ok, .key = InputKeyOk, .inverted = false, .name = "OK"}, - {.gpio = &gpio_button_back, .key = InputKeyBack, .inverted = true, .name = "Back"}, -}; - -const size_t input_pins_count = sizeof(input_pins) / sizeof(InputPin); - -void furi_hal_resources_init_early() { - furi_hal_gpio_init(&gpio_button_left, GpioModeInput, GpioPullUp, GpioSpeedLow); - - // SD Card stepdown control - furi_hal_gpio_write(&periph_power, 1); - furi_hal_gpio_init(&periph_power, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); - - // Display pins - furi_hal_gpio_write(&gpio_display_rst_n, 1); - furi_hal_gpio_init_simple(&gpio_display_rst_n, GpioModeOutputPushPull); - furi_hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull); - - // Alternative pull configuration for shutdown - SET_BIT(PWR->PUCRB, DISPLAY_RST_Pin); - CLEAR_BIT(PWR->PDCRB, DISPLAY_RST_Pin); - SET_BIT(PWR->CR3, PWR_CR3_APC); - - // Hard reset USB - furi_hal_gpio_write(&gpio_usb_dm, 1); - furi_hal_gpio_write(&gpio_usb_dp, 1); - furi_hal_gpio_init_simple(&gpio_usb_dm, GpioModeOutputOpenDrain); - furi_hal_gpio_init_simple(&gpio_usb_dp, GpioModeOutputOpenDrain); - furi_hal_gpio_write(&gpio_usb_dm, 0); - furi_hal_gpio_write(&gpio_usb_dp, 0); - furi_delay_us(5); // Device Driven disconnect: 2.5us + extra to compensate cables - furi_hal_gpio_write(&gpio_usb_dm, 1); - furi_hal_gpio_write(&gpio_usb_dp, 1); - furi_hal_gpio_init_simple(&gpio_usb_dm, GpioModeAnalog); - furi_hal_gpio_init_simple(&gpio_usb_dp, GpioModeAnalog); - furi_hal_gpio_write(&gpio_usb_dm, 0); - furi_hal_gpio_write(&gpio_usb_dp, 0); - - // External header pins - furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_ext_pc3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_ext_pb2, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_ext_pb3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_ext_pa4, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -} - -void furi_hal_resources_deinit_early() { -} - -void furi_hal_resources_init() { - // Button pins - for(size_t i = 0; i < input_pins_count; i++) { - furi_hal_gpio_init( - input_pins[i].gpio, GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedLow); - } - - // Display pins - furi_hal_gpio_init(&gpio_display_rst_n, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_display_rst_n, 0); - - furi_hal_gpio_init(&gpio_display_di, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_display_di, 0); - - // SD pins - furi_hal_gpio_init(&gpio_sdcard_cd, GpioModeInput, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_sdcard_cd, 0); - - furi_hal_gpio_init(&vibro_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - - furi_hal_gpio_init(&ibutton_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - - furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeInterruptRise, GpioPullNo, GpioSpeedLow); - - furi_hal_gpio_init(&gpio_rf_sw_0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - - NVIC_SetPriority(EXTI0_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); - NVIC_EnableIRQ(EXTI0_IRQn); - - NVIC_SetPriority(EXTI1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); - NVIC_EnableIRQ(EXTI1_IRQn); - - NVIC_SetPriority(EXTI2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); - NVIC_EnableIRQ(EXTI2_IRQn); - - NVIC_SetPriority(EXTI3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); - NVIC_EnableIRQ(EXTI3_IRQn); - - NVIC_SetPriority(EXTI4_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); - NVIC_EnableIRQ(EXTI4_IRQn); - - NVIC_SetPriority(EXTI9_5_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); - NVIC_EnableIRQ(EXTI9_5_IRQn); - - NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); - NVIC_EnableIRQ(EXTI15_10_IRQn); -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.h b/firmware/targets/f7/furi_hal/furi_hal_resources.h deleted file mode 100644 index d16c567e628..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.h +++ /dev/null @@ -1,211 +0,0 @@ -#pragma once - -#include - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* Input Related Constants */ -#define INPUT_DEBOUNCE_TICKS 30 - -/* Input Keys */ -typedef enum { - InputKeyUp, - InputKeyDown, - InputKeyRight, - InputKeyLeft, - InputKeyOk, - InputKeyBack, -} InputKey; - -/* Light */ -typedef enum { - LightRed = (1 << 0), - LightGreen = (1 << 1), - LightBlue = (1 << 2), - LightBacklight = (1 << 3), -} Light; - -typedef struct { - const GpioPin* gpio; - const InputKey key; - const bool inverted; - const char* name; -} InputPin; - -extern const InputPin input_pins[]; -extern const size_t input_pins_count; - -extern const GpioPin vibro_gpio; -extern const GpioPin ibutton_gpio; - -extern const GpioPin gpio_cc1101_g0; -extern const GpioPin gpio_rf_sw_0; - -extern const GpioPin gpio_subghz_cs; -extern const GpioPin gpio_display_cs; -extern const GpioPin gpio_display_rst_n; -extern const GpioPin gpio_display_di; -extern const GpioPin gpio_sdcard_cs; -extern const GpioPin gpio_sdcard_cd; -extern const GpioPin gpio_nfc_cs; - -extern const GpioPin gpio_button_up; -extern const GpioPin gpio_button_down; -extern const GpioPin gpio_button_right; -extern const GpioPin gpio_button_left; -extern const GpioPin gpio_button_ok; -extern const GpioPin gpio_button_back; - -extern const GpioPin gpio_spi_d_miso; -extern const GpioPin gpio_spi_d_mosi; -extern const GpioPin gpio_spi_d_sck; -extern const GpioPin gpio_spi_r_miso; -extern const GpioPin gpio_spi_r_mosi; -extern const GpioPin gpio_spi_r_sck; - -extern const GpioPin gpio_ext_pc0; -extern const GpioPin gpio_ext_pc1; -extern const GpioPin gpio_ext_pc3; -extern const GpioPin gpio_ext_pb2; -extern const GpioPin gpio_ext_pb3; -extern const GpioPin gpio_ext_pa4; -extern const GpioPin gpio_ext_pa6; -extern const GpioPin gpio_ext_pa7; - -extern const GpioPin gpio_nfc_irq_rfid_pull; -extern const GpioPin gpio_rfid_carrier_out; -extern const GpioPin gpio_rfid_data_in; -extern const GpioPin gpio_rfid_carrier; - -extern const GpioPin gpio_infrared_rx; -extern const GpioPin gpio_infrared_tx; - -extern const GpioPin gpio_usart_tx; -extern const GpioPin gpio_usart_rx; -extern const GpioPin gpio_i2c_power_sda; -extern const GpioPin gpio_i2c_power_scl; - -extern const GpioPin gpio_speaker; - -extern const GpioPin periph_power; - -extern const GpioPin gpio_usb_dm; -extern const GpioPin gpio_usb_dp; - -#define BUTTON_BACK_GPIO_Port GPIOC -#define BUTTON_BACK_Pin LL_GPIO_PIN_13 -#define BUTTON_DOWN_GPIO_Port GPIOC -#define BUTTON_DOWN_Pin LL_GPIO_PIN_6 -#define BUTTON_LEFT_GPIO_Port GPIOB -#define BUTTON_LEFT_Pin LL_GPIO_PIN_11 -#define BUTTON_OK_GPIO_Port GPIOH -#define BUTTON_OK_Pin LL_GPIO_PIN_3 -#define BUTTON_RIGHT_GPIO_Port GPIOB -#define BUTTON_RIGHT_Pin LL_GPIO_PIN_12 -#define BUTTON_UP_GPIO_Port GPIOB -#define BUTTON_UP_Pin LL_GPIO_PIN_10 - -#define CC1101_CS_GPIO_Port GPIOD -#define CC1101_CS_Pin LL_GPIO_PIN_0 -#define CC1101_G0_GPIO_Port GPIOA -#define CC1101_G0_Pin LL_GPIO_PIN_1 - -#define DISPLAY_CS_GPIO_Port GPIOC -#define DISPLAY_CS_Pin LL_GPIO_PIN_11 -#define DISPLAY_DI_GPIO_Port GPIOB -#define DISPLAY_DI_Pin LL_GPIO_PIN_1 -#define DISPLAY_RST_GPIO_Port GPIOB -#define DISPLAY_RST_Pin LL_GPIO_PIN_0 - -#define IR_RX_GPIO_Port GPIOA -#define IR_RX_Pin LL_GPIO_PIN_0 -#define IR_TX_GPIO_Port GPIOB -#define IR_TX_Pin LL_GPIO_PIN_9 - -#define NFC_CS_GPIO_Port GPIOE -#define NFC_CS_Pin LL_GPIO_PIN_4 - -#define PA4_GPIO_Port GPIOA -#define PA4_Pin LL_GPIO_PIN_4 -#define PA6_GPIO_Port GPIOA -#define PA6_Pin LL_GPIO_PIN_6 -#define PA7_GPIO_Port GPIOA -#define PA7_Pin LL_GPIO_PIN_7 -#define PB2_GPIO_Port GPIOB -#define PB2_Pin LL_GPIO_PIN_2 -#define PB3_GPIO_Port GPIOB -#define PB3_Pin LL_GPIO_PIN_3 -#define PC0_GPIO_Port GPIOC -#define PC0_Pin LL_GPIO_PIN_0 -#define PC1_GPIO_Port GPIOC -#define PC1_Pin LL_GPIO_PIN_1 -#define PC3_GPIO_Port GPIOC -#define PC3_Pin LL_GPIO_PIN_3 - -#define QUARTZ_32MHZ_IN_GPIO_Port GPIOC -#define QUARTZ_32MHZ_IN_Pin LL_GPIO_PIN_14 -#define QUARTZ_32MHZ_OUT_GPIO_Port GPIOC -#define QUARTZ_32MHZ_OUT_Pin LL_GPIO_PIN_15 - -#define RFID_OUT_GPIO_Port GPIOB -#define RFID_OUT_Pin LL_GPIO_PIN_13 -#define RFID_PULL_GPIO_Port GPIOA -#define RFID_PULL_Pin LL_GPIO_PIN_2 -#define RFID_RF_IN_GPIO_Port GPIOC -#define RFID_RF_IN_Pin LL_GPIO_PIN_5 -#define RFID_CARRIER_GPIO_Port GPIOA -#define RFID_CARRIER_Pin LL_GPIO_PIN_15 - -#define RF_SW_0_GPIO_Port GPIOC -#define RF_SW_0_Pin LL_GPIO_PIN_4 - -#define SD_CD_GPIO_Port GPIOC -#define SD_CD_Pin LL_GPIO_PIN_10 -#define SD_CS_GPIO_Port GPIOC -#define SD_CS_Pin LL_GPIO_PIN_12 - -#define SPEAKER_GPIO_Port GPIOB -#define SPEAKER_Pin LL_GPIO_PIN_8 - -#define VIBRO_GPIO_Port GPIOA -#define VIBRO_Pin LL_GPIO_PIN_8 - -#define iBTN_GPIO_Port GPIOB -#define iBTN_Pin LL_GPIO_PIN_14 - -#define USART1_TX_Pin LL_GPIO_PIN_6 -#define USART1_TX_Port GPIOB -#define USART1_RX_Pin LL_GPIO_PIN_7 -#define USART1_RX_Port GPIOB - -#define SPI_D_MISO_GPIO_Port GPIOC -#define SPI_D_MISO_Pin LL_GPIO_PIN_2 -#define SPI_D_MOSI_GPIO_Port GPIOB -#define SPI_D_MOSI_Pin LL_GPIO_PIN_15 -#define SPI_D_SCK_GPIO_Port GPIOD -#define SPI_D_SCK_Pin LL_GPIO_PIN_1 - -#define SPI_R_MISO_GPIO_Port GPIOB -#define SPI_R_MISO_Pin LL_GPIO_PIN_4 -#define SPI_R_MOSI_GPIO_Port GPIOB -#define SPI_R_MOSI_Pin LL_GPIO_PIN_5 -#define SPI_R_SCK_GPIO_Port GPIOA -#define SPI_R_SCK_Pin LL_GPIO_PIN_5 - -#define NFC_IRQ_Pin RFID_PULL_Pin -#define NFC_IRQ_GPIO_Port RFID_PULL_GPIO_Port - -void furi_hal_resources_init_early(); - -void furi_hal_resources_deinit_early(); - -void furi_hal_resources_init(); - -#ifdef __cplusplus -} -#endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_rfid.c b/firmware/targets/f7/furi_hal/furi_hal_rfid.c deleted file mode 100644 index 507c53bfe16..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_rfid.c +++ /dev/null @@ -1,288 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include - -#define FURI_HAL_RFID_READ_TIMER TIM1 -#define FURI_HAL_RFID_READ_TIMER_CHANNEL LL_TIM_CHANNEL_CH1N -// We can't use N channel for LL_TIM_OC_Init, so... -#define FURI_HAL_RFID_READ_TIMER_CHANNEL_CONFIG LL_TIM_CHANNEL_CH1 - -#define FURI_HAL_RFID_EMULATE_TIMER TIM2 -#define FURI_HAL_RFID_EMULATE_TIMER_IRQ FuriHalInterruptIdTIM2 -#define FURI_HAL_RFID_EMULATE_TIMER_CHANNEL LL_TIM_CHANNEL_CH3 - -typedef struct { - FuriHalRfidEmulateCallback callback; - void* context; -} FuriHalRfid; - -FuriHalRfid* furi_hal_rfid = NULL; - -#define LFRFID_LL_READ_TIM TIM1 -#define LFRFID_LL_READ_CONFIG_CHANNEL LL_TIM_CHANNEL_CH1 -#define LFRFID_LL_READ_CHANNEL LL_TIM_CHANNEL_CH1N - -#define LFRFID_LL_EMULATE_TIM TIM2 -#define LFRFID_LL_EMULATE_CHANNEL LL_TIM_CHANNEL_CH3 - -void furi_hal_rfid_init() { - furi_assert(furi_hal_rfid == NULL); - furi_hal_rfid = malloc(sizeof(FuriHalRfid)); - - furi_hal_rfid_pins_reset(); - - LL_COMP_InitTypeDef COMP_InitStruct = {0}; - COMP_InitStruct.PowerMode = LL_COMP_POWERMODE_MEDIUMSPEED; - COMP_InitStruct.InputPlus = LL_COMP_INPUT_PLUS_IO1; - COMP_InitStruct.InputMinus = LL_COMP_INPUT_MINUS_1_2VREFINT; - COMP_InitStruct.InputHysteresis = LL_COMP_HYSTERESIS_HIGH; -#ifdef INVERT_RFID_IN - COMP_InitStruct.OutputPolarity = LL_COMP_OUTPUTPOL_INVERTED; -#else - COMP_InitStruct.OutputPolarity = LL_COMP_OUTPUTPOL_NONINVERTED; -#endif - COMP_InitStruct.OutputBlankingSource = LL_COMP_BLANKINGSRC_NONE; - LL_COMP_Init(COMP1, &COMP_InitStruct); - LL_COMP_SetCommonWindowMode(__LL_COMP_COMMON_INSTANCE(COMP1), LL_COMP_WINDOWMODE_DISABLE); - - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_20); - LL_EXTI_EnableFallingTrig_0_31(LL_EXTI_LINE_20); - LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_20); - LL_EXTI_DisableEvent_0_31(LL_EXTI_LINE_20); - LL_EXTI_EnableIT_0_31(LL_EXTI_LINE_20); - - NVIC_SetPriority(COMP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); - NVIC_EnableIRQ(COMP_IRQn); -} - -void furi_hal_rfid_pins_reset() { - // ibutton bus disable - furi_hal_ibutton_stop(); - - // pulldown rfid antenna - furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_rfid_carrier_out, false); - - // from both sides - furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, true); - - furi_hal_gpio_init_simple(&gpio_rfid_carrier, GpioModeAnalog); - - furi_hal_gpio_init(&gpio_rfid_data_in, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -} - -void furi_hal_rfid_pins_emulate() { - // ibutton low - furi_hal_ibutton_start_drive(); - furi_hal_ibutton_pin_low(); - - // pull pin to timer out - furi_hal_gpio_init_ex( - &gpio_nfc_irq_rfid_pull, - GpioModeAltFunctionPushPull, - GpioPullNo, - GpioSpeedLow, - GpioAltFn1TIM2); - - // pull rfid antenna from carrier side - furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_rfid_carrier_out, false); - - furi_hal_gpio_init_ex( - &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn2TIM2); -} - -void furi_hal_rfid_pins_read() { - // ibutton low - furi_hal_ibutton_start_drive(); - furi_hal_ibutton_pin_low(); - - // dont pull rfid antenna - furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, false); - - // carrier pin to timer out - furi_hal_gpio_init_ex( - &gpio_rfid_carrier_out, - GpioModeAltFunctionPushPull, - GpioPullNo, - GpioSpeedLow, - GpioAltFn1TIM1); - - // comparator in - furi_hal_gpio_init(&gpio_rfid_data_in, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -} - -void furi_hal_rfid_pin_pull_release() { - furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, true); -} - -void furi_hal_rfid_pin_pull_pulldown() { - furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, false); -} - -void furi_hal_rfid_tim_read(float freq, float duty_cycle) { - FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(FURI_HAL_RFID_READ_TIMER); - FURI_CRITICAL_EXIT(); - - LL_TIM_InitTypeDef TIM_InitStruct = {0}; - TIM_InitStruct.Autoreload = (SystemCoreClock / freq) - 1; - LL_TIM_Init(FURI_HAL_RFID_READ_TIMER, &TIM_InitStruct); - LL_TIM_DisableARRPreload(FURI_HAL_RFID_READ_TIMER); - - LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; - TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; - TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_ENABLE; - TIM_OC_InitStruct.CompareValue = TIM_InitStruct.Autoreload * duty_cycle; - LL_TIM_OC_Init( - FURI_HAL_RFID_READ_TIMER, FURI_HAL_RFID_READ_TIMER_CHANNEL_CONFIG, &TIM_OC_InitStruct); - - LL_TIM_EnableCounter(FURI_HAL_RFID_READ_TIMER); -} - -void furi_hal_rfid_tim_read_start() { - LL_TIM_EnableAllOutputs(FURI_HAL_RFID_READ_TIMER); -} - -void furi_hal_rfid_tim_read_stop() { - LL_TIM_DisableAllOutputs(FURI_HAL_RFID_READ_TIMER); -} - -void furi_hal_rfid_tim_emulate(float freq) { - UNUSED(freq); // FIXME - // basic PWM setup with needed freq and internal clock - FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(FURI_HAL_RFID_EMULATE_TIMER); - FURI_CRITICAL_EXIT(); - - LL_TIM_SetPrescaler(FURI_HAL_RFID_EMULATE_TIMER, 0); - LL_TIM_SetCounterMode(FURI_HAL_RFID_EMULATE_TIMER, LL_TIM_COUNTERMODE_UP); - LL_TIM_SetAutoReload(FURI_HAL_RFID_EMULATE_TIMER, 1); - LL_TIM_DisableARRPreload(FURI_HAL_RFID_EMULATE_TIMER); - LL_TIM_SetRepetitionCounter(FURI_HAL_RFID_EMULATE_TIMER, 0); - - LL_TIM_SetClockDivision(FURI_HAL_RFID_EMULATE_TIMER, LL_TIM_CLOCKDIVISION_DIV1); - LL_TIM_SetClockSource(FURI_HAL_RFID_EMULATE_TIMER, LL_TIM_CLOCKSOURCE_EXT_MODE2); - LL_TIM_ConfigETR( - FURI_HAL_RFID_EMULATE_TIMER, - LL_TIM_ETR_POLARITY_INVERTED, - LL_TIM_ETR_PRESCALER_DIV1, - LL_TIM_ETR_FILTER_FDIV1); - - 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 = 1; - LL_TIM_OC_Init( - FURI_HAL_RFID_EMULATE_TIMER, FURI_HAL_RFID_EMULATE_TIMER_CHANNEL, &TIM_OC_InitStruct); - - LL_TIM_GenerateEvent_UPDATE(FURI_HAL_RFID_EMULATE_TIMER); -} - -static void furi_hal_rfid_emulate_isr() { - if(LL_TIM_IsActiveFlag_UPDATE(FURI_HAL_RFID_EMULATE_TIMER)) { - LL_TIM_ClearFlag_UPDATE(FURI_HAL_RFID_EMULATE_TIMER); - furi_hal_rfid->callback(furi_hal_rfid->context); - } -} - -void furi_hal_rfid_tim_emulate_start(FuriHalRfidEmulateCallback callback, void* context) { - furi_assert(furi_hal_rfid); - - furi_hal_rfid->callback = callback; - furi_hal_rfid->context = context; - - furi_hal_interrupt_set_isr(FURI_HAL_RFID_EMULATE_TIMER_IRQ, furi_hal_rfid_emulate_isr, NULL); - - LL_TIM_EnableIT_UPDATE(FURI_HAL_RFID_EMULATE_TIMER); - LL_TIM_EnableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER); - LL_TIM_EnableCounter(FURI_HAL_RFID_EMULATE_TIMER); -} - -void furi_hal_rfid_tim_emulate_stop() { - LL_TIM_DisableCounter(FURI_HAL_RFID_EMULATE_TIMER); - LL_TIM_DisableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER); - furi_hal_interrupt_set_isr(FURI_HAL_RFID_EMULATE_TIMER_IRQ, NULL, NULL); -} - -void furi_hal_rfid_tim_reset() { - FURI_CRITICAL_ENTER(); - - LL_TIM_DeInit(FURI_HAL_RFID_READ_TIMER); - LL_TIM_DeInit(FURI_HAL_RFID_EMULATE_TIMER); - - FURI_CRITICAL_EXIT(); -} - -void furi_hal_rfid_set_emulate_period(uint32_t period) { - LL_TIM_SetAutoReload(FURI_HAL_RFID_EMULATE_TIMER, period); -} - -void furi_hal_rfid_set_emulate_pulse(uint32_t pulse) { -#if FURI_HAL_RFID_EMULATE_TIMER_CHANNEL == LL_TIM_CHANNEL_CH3 - LL_TIM_OC_SetCompareCH3(FURI_HAL_RFID_EMULATE_TIMER, pulse); -#else -#error Update this code. Would you kindly? -#endif -} - -void furi_hal_rfid_set_read_period(uint32_t period) { - LL_TIM_SetAutoReload(FURI_HAL_RFID_READ_TIMER, period); -} - -void furi_hal_rfid_set_read_pulse(uint32_t pulse) { -#if FURI_HAL_RFID_READ_TIMER_CHANNEL == LL_TIM_CHANNEL_CH1N - LL_TIM_OC_SetCompareCH1(FURI_HAL_RFID_READ_TIMER, pulse); -#else -#error Update this code. Would you kindly? -#endif -} - -void furi_hal_rfid_change_read_config(float freq, float duty_cycle) { - uint32_t period = (uint32_t)((SystemCoreClock) / freq) - 1; - furi_hal_rfid_set_read_period(period); - furi_hal_rfid_set_read_pulse(period * duty_cycle); -} - -void furi_hal_rfid_comp_start() { - LL_COMP_Enable(COMP1); - // Magic - uint32_t wait_loop_index = ((80 / 10UL) * ((SystemCoreClock / (100000UL * 2UL)) + 1UL)); - while(wait_loop_index) { - wait_loop_index--; - } -} - -void furi_hal_rfid_comp_stop() { - LL_COMP_Disable(COMP1); -} - -FuriHalRfidCompCallback furi_hal_rfid_comp_callback = NULL; -void* furi_hal_rfid_comp_callback_context = NULL; - -void furi_hal_rfid_comp_set_callback(FuriHalRfidCompCallback callback, void* context) { - FURI_CRITICAL_ENTER(); - furi_hal_rfid_comp_callback = callback; - furi_hal_rfid_comp_callback_context = context; - __DMB(); - FURI_CRITICAL_EXIT(); -} - -/* Comparator trigger event */ -void COMP_IRQHandler() { - if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_20)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_20); - } - if(furi_hal_rfid_comp_callback) { - furi_hal_rfid_comp_callback( - (LL_COMP_ReadOutputLevel(COMP1) == LL_COMP_OUTPUT_LEVEL_LOW), - furi_hal_rfid_comp_callback_context); - } -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_rtc.c b/firmware/targets/f7/furi_hal/furi_hal_rtc.c deleted file mode 100644 index 24dad38fa02..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_rtc.c +++ /dev/null @@ -1,350 +0,0 @@ -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#define TAG "FuriHalRtc" - -#define FURI_HAL_RTC_LSE_STARTUP_TIME 300 - -#define FURI_HAL_RTC_CLOCK_IS_READY() (LL_RCC_LSE_IsReady() && LL_RCC_LSI1_IsReady()) - -#define FURI_HAL_RTC_HEADER_MAGIC 0x10F1 -#define FURI_HAL_RTC_HEADER_VERSION 0 - -typedef struct { - uint16_t magic; - uint8_t version; - uint8_t unused; -} FuriHalRtcHeader; - -typedef struct { - uint8_t log_level : 4; - uint8_t log_reserved : 4; - uint8_t flags; - uint8_t boot_mode : 4; - uint16_t reserved : 12; -} DeveloperReg; - -_Static_assert(sizeof(DeveloperReg) == 4, "DeveloperReg size mismatch"); - -#define FURI_HAL_RTC_SECONDS_PER_MINUTE 60 -#define FURI_HAL_RTC_SECONDS_PER_HOUR (FURI_HAL_RTC_SECONDS_PER_MINUTE * 60) -#define FURI_HAL_RTC_SECONDS_PER_DAY (FURI_HAL_RTC_SECONDS_PER_HOUR * 24) -#define FURI_HAL_RTC_MONTHS_COUNT 12 -#define FURI_HAL_RTC_EPOCH_START_YEAR 1970 -#define FURI_HAL_RTC_IS_LEAP_YEAR(year) \ - ((((year) % 4 == 0) && ((year) % 100 != 0)) || ((year) % 400 == 0)) - -static const uint8_t furi_hal_rtc_days_per_month[][FURI_HAL_RTC_MONTHS_COUNT] = { - {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, - {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}}; - -static const uint16_t furi_hal_rtc_days_per_year[] = {365, 366}; - -static void furi_hal_rtc_reset() { - LL_RCC_ForceBackupDomainReset(); - LL_RCC_ReleaseBackupDomainReset(); -} - -static bool furi_hal_rtc_start_clock_and_switch() { - // Clock operation require access to Backup Domain - LL_PWR_EnableBkUpAccess(); - - // Enable LSI and LSE - LL_RCC_LSI1_Enable(); - LL_RCC_LSE_SetDriveCapability(LL_RCC_LSEDRIVE_HIGH); - LL_RCC_LSE_Enable(); - - // Wait for LSI and LSE startup - uint32_t c = 0; - while(!FURI_HAL_RTC_CLOCK_IS_READY() && c < FURI_HAL_RTC_LSE_STARTUP_TIME) { - LL_mDelay(1); - c++; - } - - if(FURI_HAL_RTC_CLOCK_IS_READY()) { - LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE); - LL_RCC_EnableRTC(); - return LL_RCC_GetRTCClockSource() == LL_RCC_RTC_CLKSOURCE_LSE; - } else { - return false; - } -} - -static void furi_hal_rtc_recover() { - FuriHalRtcDateTime datetime = {0}; - - // Handle fixable LSE failure - if(LL_RCC_LSE_IsCSSDetected()) { - furi_hal_light_sequence("rgb B"); - // Shutdown LSE and LSECSS - LL_RCC_LSE_DisableCSS(); - LL_RCC_LSE_Disable(); - } else { - furi_hal_light_sequence("rgb R"); - } - - // Temporary switch to LSI - LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSI); - if(LL_RCC_GetRTCClockSource() == LL_RCC_RTC_CLKSOURCE_LSI) { - // Get datetime before RTC Domain reset - furi_hal_rtc_get_datetime(&datetime); - } - - // Reset RTC Domain - furi_hal_rtc_reset(); - - // Start Clock - if(!furi_hal_rtc_start_clock_and_switch()) { - // Plan C: reset RTC and restart - furi_hal_light_sequence("rgb R.r.R.r.R.r"); - furi_hal_rtc_reset(); - NVIC_SystemReset(); - } - - // Set date if it valid - if(datetime.year != 0) { - furi_hal_rtc_set_datetime(&datetime); - } -} - -void furi_hal_rtc_init_early() { - // Enable RTCAPB clock - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_RTCAPB); - - // Prepare clock - if(!furi_hal_rtc_start_clock_and_switch()) { - // Plan B: try to recover - furi_hal_rtc_recover(); - } - - // Verify header register - uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterHeader); - FuriHalRtcHeader* data = (FuriHalRtcHeader*)&data_reg; - if(data->magic != FURI_HAL_RTC_HEADER_MAGIC || data->version != FURI_HAL_RTC_HEADER_VERSION) { - // Reset all our registers to ensure consistency - for(size_t i = 0; i < FuriHalRtcRegisterMAX; i++) { - furi_hal_rtc_set_register(i, 0); - } - data->magic = FURI_HAL_RTC_HEADER_MAGIC; - data->version = FURI_HAL_RTC_HEADER_VERSION; - furi_hal_rtc_set_register(FuriHalRtcRegisterHeader, data_reg); - } - - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - furi_hal_debug_enable(); - } else { - furi_hal_debug_disable(); - } -} - -void furi_hal_rtc_deinit_early() { -} - -void furi_hal_rtc_init() { - LL_RTC_InitTypeDef RTC_InitStruct = {0}; - RTC_InitStruct.HourFormat = LL_RTC_HOURFORMAT_24HOUR; - RTC_InitStruct.AsynchPrescaler = 127; - RTC_InitStruct.SynchPrescaler = 255; - LL_RTC_Init(RTC, &RTC_InitStruct); - - furi_log_set_level(furi_hal_rtc_get_log_level()); - - FURI_LOG_I(TAG, "Init OK"); -} - -uint32_t furi_hal_rtc_get_register(FuriHalRtcRegister reg) { - return LL_RTC_BAK_GetRegister(RTC, reg); -} - -void furi_hal_rtc_set_register(FuriHalRtcRegister reg, uint32_t value) { - LL_RTC_BAK_SetRegister(RTC, reg, value); -} - -void furi_hal_rtc_set_log_level(uint8_t level) { - uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; - data->log_level = level; - furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); - furi_log_set_level(level); -} - -uint8_t furi_hal_rtc_get_log_level() { - uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; - return data->log_level; -} - -void furi_hal_rtc_set_flag(FuriHalRtcFlag flag) { - uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; - data->flags |= flag; - furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); - - if(flag & FuriHalRtcFlagDebug) { - furi_hal_debug_enable(); - } -} - -void furi_hal_rtc_reset_flag(FuriHalRtcFlag flag) { - uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; - data->flags &= ~flag; - furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); - - if(flag & FuriHalRtcFlagDebug) { - furi_hal_debug_disable(); - } -} - -bool furi_hal_rtc_is_flag_set(FuriHalRtcFlag flag) { - uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; - return data->flags & flag; -} - -void furi_hal_rtc_set_boot_mode(FuriHalRtcBootMode mode) { - uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; - data->boot_mode = mode; - furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); -} - -FuriHalRtcBootMode furi_hal_rtc_get_boot_mode() { - uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); - DeveloperReg* data = (DeveloperReg*)&data_reg; - return (FuriHalRtcBootMode)data->boot_mode; -} - -void furi_hal_rtc_set_datetime(FuriHalRtcDateTime* datetime) { - furi_assert(datetime); - - /* Disable write protection */ - LL_RTC_DisableWriteProtection(RTC); - - /* Enter Initialization mode and wait for INIT flag to be set */ - LL_RTC_EnableInitMode(RTC); - while(!LL_RTC_IsActiveFlag_INIT(RTC)) { - } - - /* Set time */ - LL_RTC_TIME_Config( - RTC, - LL_RTC_TIME_FORMAT_AM_OR_24, - __LL_RTC_CONVERT_BIN2BCD(datetime->hour), - __LL_RTC_CONVERT_BIN2BCD(datetime->minute), - __LL_RTC_CONVERT_BIN2BCD(datetime->second)); - - /* Set date */ - LL_RTC_DATE_Config( - RTC, - datetime->weekday, - __LL_RTC_CONVERT_BIN2BCD(datetime->day), - __LL_RTC_CONVERT_BIN2BCD(datetime->month), - __LL_RTC_CONVERT_BIN2BCD(datetime->year - 2000)); - - /* Exit Initialization mode */ - LL_RTC_DisableInitMode(RTC); - - /* If RTC_CR_BYPSHAD bit = 0, wait for synchro else this check is not needed */ - if(!LL_RTC_IsShadowRegBypassEnabled(RTC)) { - LL_RTC_ClearFlag_RS(RTC); - while(!LL_RTC_IsActiveFlag_RS(RTC)) { - }; - } - - /* Enable write protection */ - LL_RTC_EnableWriteProtection(RTC); -} - -void furi_hal_rtc_get_datetime(FuriHalRtcDateTime* datetime) { - furi_assert(datetime); - - uint32_t time = LL_RTC_TIME_Get(RTC); // 0x00HHMMSS - uint32_t date = LL_RTC_DATE_Get(RTC); // 0xWWDDMMYY - - datetime->second = __LL_RTC_CONVERT_BCD2BIN((time >> 0) & 0xFF); - datetime->minute = __LL_RTC_CONVERT_BCD2BIN((time >> 8) & 0xFF); - datetime->hour = __LL_RTC_CONVERT_BCD2BIN((time >> 16) & 0xFF); - datetime->year = __LL_RTC_CONVERT_BCD2BIN((date >> 0) & 0xFF) + 2000; - datetime->month = __LL_RTC_CONVERT_BCD2BIN((date >> 8) & 0xFF); - datetime->day = __LL_RTC_CONVERT_BCD2BIN((date >> 16) & 0xFF); - datetime->weekday = __LL_RTC_CONVERT_BCD2BIN((date >> 24) & 0xFF); -} - -bool furi_hal_rtc_validate_datetime(FuriHalRtcDateTime* datetime) { - bool invalid = false; - - invalid |= (datetime->second > 59); - invalid |= (datetime->minute > 59); - invalid |= (datetime->hour > 23); - - invalid |= (datetime->year < 2000); - invalid |= (datetime->year > 2099); - - invalid |= (datetime->month == 0); - invalid |= (datetime->month > 12); - - invalid |= (datetime->day == 0); - invalid |= (datetime->day > 31); - - invalid |= (datetime->weekday == 0); - invalid |= (datetime->weekday > 7); - - return !invalid; -} - -void furi_hal_rtc_set_fault_data(uint32_t value) { - furi_hal_rtc_set_register(FuriHalRtcRegisterFaultData, value); -} - -uint32_t furi_hal_rtc_get_fault_data() { - return furi_hal_rtc_get_register(FuriHalRtcRegisterFaultData); -} - -void furi_hal_rtc_set_pin_fails(uint32_t value) { - furi_hal_rtc_set_register(FuriHalRtcRegisterPinFails, value); -} - -uint32_t furi_hal_rtc_get_pin_fails() { - return furi_hal_rtc_get_register(FuriHalRtcRegisterPinFails); -} - -uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime) { - uint32_t timestamp = 0; - uint8_t years = 0; - uint8_t leap_years = 0; - - for(uint16_t y = FURI_HAL_RTC_EPOCH_START_YEAR; y < datetime->year; y++) { - if(FURI_HAL_RTC_IS_LEAP_YEAR(y)) { - leap_years++; - } else { - years++; - } - } - - timestamp += - ((years * furi_hal_rtc_days_per_year[0]) + (leap_years * furi_hal_rtc_days_per_year[1])) * - FURI_HAL_RTC_SECONDS_PER_DAY; - - uint8_t year_index = (FURI_HAL_RTC_IS_LEAP_YEAR(datetime->year)) ? 1 : 0; - - for(uint8_t m = 0; m < (datetime->month - 1); m++) { - timestamp += furi_hal_rtc_days_per_month[year_index][m] * FURI_HAL_RTC_SECONDS_PER_DAY; - } - - timestamp += (datetime->day - 1) * FURI_HAL_RTC_SECONDS_PER_DAY; - timestamp += datetime->hour * FURI_HAL_RTC_SECONDS_PER_HOUR; - timestamp += datetime->minute * FURI_HAL_RTC_SECONDS_PER_MINUTE; - timestamp += datetime->second; - - return timestamp; -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_sd.c b/firmware/targets/f7/furi_hal/furi_hal_sd.c deleted file mode 100644 index 688a4e61bc8..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_sd.c +++ /dev/null @@ -1,25 +0,0 @@ -#include "furi_hal_sd.h" -#include -#include -#include - -void hal_sd_detect_init(void) { - // low speed input with pullup - LL_GPIO_SetPinMode(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_MODE_INPUT); - LL_GPIO_SetPinSpeed(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_SPEED_FREQ_LOW); - LL_GPIO_SetPinPull(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_PULL_UP); -} - -void hal_sd_detect_set_low(void) { - // low speed input with pullup - LL_GPIO_SetPinMode(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_MODE_OUTPUT); - LL_GPIO_SetPinOutputType(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_OUTPUT_OPENDRAIN); - LL_GPIO_ResetOutputPin(SD_CD_GPIO_Port, SD_CD_Pin); -} - -bool hal_sd_detect(void) { - bool result = !(LL_GPIO_IsInputPinSet(SD_CD_GPIO_Port, SD_CD_Pin)); - return result; -} - -FuriHalSpiBusHandle* furi_hal_sd_spi_handle = NULL; diff --git a/firmware/targets/f7/furi_hal/furi_hal_speaker.c b/firmware/targets/f7/furi_hal/furi_hal_speaker.c deleted file mode 100644 index 03a7f094bba..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_speaker.c +++ /dev/null @@ -1,93 +0,0 @@ -#include -#include -#include - -#include - -#define FURI_HAL_SPEAKER_TIMER TIM16 -#define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1 -#define FURI_HAL_SPEAKER_PRESCALER 500 -#define FURI_HAL_SPEAKER_MAX_VOLUME 60 - -// #define FURI_HAL_SPEAKER_NEW_VOLUME - -void furi_hal_speaker_init() { - FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(FURI_HAL_SPEAKER_TIMER); - FURI_CRITICAL_EXIT(); - - furi_hal_gpio_init_ex( - &gpio_speaker, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn14TIM16); -} - -static inline uint32_t furi_hal_speaker_calculate_autoreload(float frequency) { - uint32_t autoreload = (SystemCoreClock / FURI_HAL_SPEAKER_PRESCALER / frequency) - 1; - if(autoreload < 2) { - autoreload = 2; - } else if(autoreload > UINT16_MAX) { - autoreload = UINT16_MAX; - } - - return autoreload; -} - -static inline uint32_t furi_hal_speaker_calculate_compare(float volume) { - if(volume < 0) volume = 0; - if(volume > 1) volume = 1; - volume = volume * volume * volume; - -#ifdef FURI_HAL_SPEAKER_NEW_VOLUME - uint32_t compare_value = volume * FURI_HAL_SPEAKER_MAX_VOLUME; - uint32_t clip_value = volume * LL_TIM_GetAutoReload(FURI_HAL_SPEAKER_TIMER) / 2; - if(compare_value > clip_value) { - compare_value = clip_value; - } -#else - uint32_t compare_value = volume * LL_TIM_GetAutoReload(FURI_HAL_SPEAKER_TIMER) / 2; -#endif - - if(compare_value == 0) { - compare_value = 1; - } - - return compare_value; -} - -void furi_hal_speaker_start(float frequency, float volume) { - if(volume <= 0) { - furi_hal_speaker_stop(); - return; - } - - LL_TIM_InitTypeDef TIM_InitStruct = {0}; - TIM_InitStruct.Prescaler = FURI_HAL_SPEAKER_PRESCALER - 1; - TIM_InitStruct.Autoreload = furi_hal_speaker_calculate_autoreload(frequency); - LL_TIM_Init(FURI_HAL_SPEAKER_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 = furi_hal_speaker_calculate_compare(volume); - LL_TIM_OC_Init(FURI_HAL_SPEAKER_TIMER, FURI_HAL_SPEAKER_CHANNEL, &TIM_OC_InitStruct); - - LL_TIM_EnableAllOutputs(FURI_HAL_SPEAKER_TIMER); - LL_TIM_EnableCounter(FURI_HAL_SPEAKER_TIMER); -} - -void furi_hal_speaker_set_volume(float volume) { - if(volume <= 0) { - furi_hal_speaker_stop(); - return; - } - -#if FURI_HAL_SPEAKER_CHANNEL == LL_TIM_CHANNEL_CH1 - LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, furi_hal_speaker_calculate_compare(volume)); -#else -#error Invalid channel -#endif -} - -void furi_hal_speaker_stop() { - LL_TIM_DisableAllOutputs(FURI_HAL_SPEAKER_TIMER); - LL_TIM_DisableCounter(FURI_HAL_SPEAKER_TIMER); -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi.c b/firmware/targets/f7/furi_hal/furi_hal_spi.c deleted file mode 100644 index f8c5a2c7820..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_spi.c +++ /dev/null @@ -1,169 +0,0 @@ -#include "furi_hal_spi.h" -#include "furi_hal_resources.h" -#include - -#include -#include -#include - -#include -#include -#include - -#define TAG "FuriHalSpi" - -void furi_hal_spi_init_early() { - furi_hal_spi_bus_init(&furi_hal_spi_bus_d); - furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_display); -} - -void furi_hal_spi_deinit_early() { - furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_display); - furi_hal_spi_bus_deinit(&furi_hal_spi_bus_d); -} - -void furi_hal_spi_init() { - furi_hal_spi_bus_init(&furi_hal_spi_bus_r); - - furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); - furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_fast); - furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_slow); - - FURI_LOG_I(TAG, "Init OK"); -} - -void furi_hal_spi_bus_init(FuriHalSpiBus* bus) { - furi_assert(bus); - bus->callback(bus, FuriHalSpiBusEventInit); -} - -void furi_hal_spi_bus_deinit(FuriHalSpiBus* bus) { - furi_assert(bus); - bus->callback(bus, FuriHalSpiBusEventDeinit); -} - -void furi_hal_spi_bus_handle_init(FuriHalSpiBusHandle* handle) { - furi_assert(handle); - handle->callback(handle, FuriHalSpiBusHandleEventInit); -} - -void furi_hal_spi_bus_handle_deinit(FuriHalSpiBusHandle* handle) { - furi_assert(handle); - handle->callback(handle, FuriHalSpiBusHandleEventDeinit); -} - -void furi_hal_spi_acquire(FuriHalSpiBusHandle* handle) { - furi_assert(handle); - - furi_hal_power_insomnia_enter(); - - handle->bus->callback(handle->bus, FuriHalSpiBusEventLock); - handle->bus->callback(handle->bus, FuriHalSpiBusEventActivate); - - furi_assert(handle->bus->current_handle == NULL); - - handle->bus->current_handle = handle; - handle->callback(handle, FuriHalSpiBusHandleEventActivate); -} - -void furi_hal_spi_release(FuriHalSpiBusHandle* handle) { - furi_assert(handle); - furi_assert(handle->bus->current_handle == handle); - - // Handle event and unset handle - handle->callback(handle, FuriHalSpiBusHandleEventDeactivate); - handle->bus->current_handle = NULL; - - // Bus events - handle->bus->callback(handle->bus, FuriHalSpiBusEventDeactivate); - handle->bus->callback(handle->bus, FuriHalSpiBusEventUnlock); - - furi_hal_power_insomnia_exit(); -} - -static void furi_hal_spi_bus_end_txrx(FuriHalSpiBusHandle* handle, uint32_t timeout) { - UNUSED(timeout); // FIXME - while(LL_SPI_GetTxFIFOLevel(handle->bus->spi) != LL_SPI_TX_FIFO_EMPTY) - ; - while(LL_SPI_IsActiveFlag_BSY(handle->bus->spi)) - ; - while(LL_SPI_GetRxFIFOLevel(handle->bus->spi) != LL_SPI_RX_FIFO_EMPTY) { - LL_SPI_ReceiveData8(handle->bus->spi); - } -} - -bool furi_hal_spi_bus_rx( - FuriHalSpiBusHandle* handle, - uint8_t* buffer, - size_t size, - uint32_t timeout) { - furi_assert(handle); - furi_assert(handle->bus->current_handle == handle); - furi_assert(buffer); - furi_assert(size > 0); - - return furi_hal_spi_bus_trx(handle, buffer, buffer, size, timeout); -} - -bool furi_hal_spi_bus_tx( - FuriHalSpiBusHandle* handle, - uint8_t* buffer, - size_t size, - uint32_t timeout) { - furi_assert(handle); - furi_assert(handle->bus->current_handle == handle); - furi_assert(buffer); - furi_assert(size > 0); - bool ret = true; - - while(size > 0) { - if(LL_SPI_IsActiveFlag_TXE(handle->bus->spi)) { - LL_SPI_TransmitData8(handle->bus->spi, *buffer); - buffer++; - size--; - } - } - - furi_hal_spi_bus_end_txrx(handle, timeout); - LL_SPI_ClearFlag_OVR(handle->bus->spi); - - return ret; -} - -bool furi_hal_spi_bus_trx( - FuriHalSpiBusHandle* handle, - uint8_t* tx_buffer, - uint8_t* rx_buffer, - size_t size, - uint32_t timeout) { - furi_assert(handle); - furi_assert(handle->bus->current_handle == handle); - furi_assert(tx_buffer); - furi_assert(rx_buffer); - furi_assert(size > 0); - - bool ret = true; - size_t tx_size = size; - bool tx_allowed = true; - - while(size > 0) { - if(tx_size > 0 && LL_SPI_IsActiveFlag_TXE(handle->bus->spi) && tx_allowed) { - LL_SPI_TransmitData8(handle->bus->spi, *tx_buffer); - tx_buffer++; - tx_size--; - tx_allowed = false; - } - - if(LL_SPI_IsActiveFlag_RXNE(handle->bus->spi)) { - *rx_buffer = LL_SPI_ReceiveData8(handle->bus->spi); - rx_buffer++; - size--; - tx_allowed = true; - } - } - - furi_hal_spi_bus_end_txrx(handle, timeout); - - return ret; -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi_config.c b/firmware/targets/f7/furi_hal/furi_hal_spi_config.c deleted file mode 100644 index 56f67bbf8a4..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_spi_config.c +++ /dev/null @@ -1,386 +0,0 @@ -#include -#include - -/* SPI Presets */ - -const LL_SPI_InitTypeDef furi_hal_spi_preset_2edge_low_8m = { - .Mode = LL_SPI_MODE_MASTER, - .TransferDirection = LL_SPI_FULL_DUPLEX, - .DataWidth = LL_SPI_DATAWIDTH_8BIT, - .ClockPolarity = LL_SPI_POLARITY_LOW, - .ClockPhase = LL_SPI_PHASE_2EDGE, - .NSS = LL_SPI_NSS_SOFT, - .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8, - .BitOrder = LL_SPI_MSB_FIRST, - .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, - .CRCPoly = 7, -}; - -const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_8m = { - .Mode = LL_SPI_MODE_MASTER, - .TransferDirection = LL_SPI_FULL_DUPLEX, - .DataWidth = LL_SPI_DATAWIDTH_8BIT, - .ClockPolarity = LL_SPI_POLARITY_LOW, - .ClockPhase = LL_SPI_PHASE_1EDGE, - .NSS = LL_SPI_NSS_SOFT, - .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8, - .BitOrder = LL_SPI_MSB_FIRST, - .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, - .CRCPoly = 7, -}; - -const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_4m = { - .Mode = LL_SPI_MODE_MASTER, - .TransferDirection = LL_SPI_FULL_DUPLEX, - .DataWidth = LL_SPI_DATAWIDTH_8BIT, - .ClockPolarity = LL_SPI_POLARITY_LOW, - .ClockPhase = LL_SPI_PHASE_1EDGE, - .NSS = LL_SPI_NSS_SOFT, - .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV16, - .BitOrder = LL_SPI_MSB_FIRST, - .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, - .CRCPoly = 7, -}; - -const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_16m = { - .Mode = LL_SPI_MODE_MASTER, - .TransferDirection = LL_SPI_FULL_DUPLEX, - .DataWidth = LL_SPI_DATAWIDTH_8BIT, - .ClockPolarity = LL_SPI_POLARITY_LOW, - .ClockPhase = LL_SPI_PHASE_1EDGE, - .NSS = LL_SPI_NSS_SOFT, - .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2, - .BitOrder = LL_SPI_MSB_FIRST, - .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, - .CRCPoly = 7, -}; - -const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_2m = { - .Mode = LL_SPI_MODE_MASTER, - .TransferDirection = LL_SPI_FULL_DUPLEX, - .DataWidth = LL_SPI_DATAWIDTH_8BIT, - .ClockPolarity = LL_SPI_POLARITY_LOW, - .ClockPhase = LL_SPI_PHASE_1EDGE, - .NSS = LL_SPI_NSS_SOFT, - .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV32, - .BitOrder = LL_SPI_MSB_FIRST, - .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, - .CRCPoly = 7, -}; - -/* SPI Buses */ - -FuriMutex* furi_hal_spi_bus_r_mutex = NULL; - -static void furi_hal_spi_bus_r_event_callback(FuriHalSpiBus* bus, FuriHalSpiBusEvent event) { - if(event == FuriHalSpiBusEventInit) { - furi_hal_spi_bus_r_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - FURI_CRITICAL_ENTER(); - LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1); - FURI_CRITICAL_EXIT(); - bus->current_handle = NULL; - } else if(event == FuriHalSpiBusEventDeinit) { - furi_mutex_free(furi_hal_spi_bus_r_mutex); - FURI_CRITICAL_ENTER(); - LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1); - LL_APB2_GRP1_ReleaseReset(LL_APB2_GRP1_PERIPH_SPI1); - FURI_CRITICAL_EXIT(); - } else if(event == FuriHalSpiBusEventLock) { - furi_check(furi_mutex_acquire(furi_hal_spi_bus_r_mutex, FuriWaitForever) == FuriStatusOk); - } else if(event == FuriHalSpiBusEventUnlock) { - furi_check(furi_mutex_release(furi_hal_spi_bus_r_mutex) == FuriStatusOk); - } else if(event == FuriHalSpiBusEventActivate) { - FURI_CRITICAL_ENTER(); - LL_APB2_GRP1_ReleaseReset(LL_APB2_GRP1_PERIPH_SPI1); - FURI_CRITICAL_EXIT(); - } else if(event == FuriHalSpiBusEventDeactivate) { - FURI_CRITICAL_ENTER(); - LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1); - FURI_CRITICAL_EXIT(); - } -} - -FuriHalSpiBus furi_hal_spi_bus_r = { - .spi = SPI1, - .callback = furi_hal_spi_bus_r_event_callback, -}; - -FuriMutex* furi_hal_spi_bus_d_mutex = NULL; - -static void furi_hal_spi_bus_d_event_callback(FuriHalSpiBus* bus, FuriHalSpiBusEvent event) { - if(event == FuriHalSpiBusEventInit) { - furi_hal_spi_bus_d_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI2); - FURI_CRITICAL_EXIT(); - bus->current_handle = NULL; - } else if(event == FuriHalSpiBusEventDeinit) { - furi_mutex_free(furi_hal_spi_bus_d_mutex); - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI2); - LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_SPI2); - FURI_CRITICAL_EXIT(); - } else if(event == FuriHalSpiBusEventLock) { - furi_check(furi_mutex_acquire(furi_hal_spi_bus_d_mutex, FuriWaitForever) == FuriStatusOk); - } else if(event == FuriHalSpiBusEventUnlock) { - furi_check(furi_mutex_release(furi_hal_spi_bus_d_mutex) == FuriStatusOk); - } else if(event == FuriHalSpiBusEventActivate) { - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_SPI2); - FURI_CRITICAL_EXIT(); - } else if(event == FuriHalSpiBusEventDeactivate) { - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI2); - FURI_CRITICAL_EXIT(); - } -} - -FuriHalSpiBus furi_hal_spi_bus_d = { - .spi = SPI2, - .callback = furi_hal_spi_bus_d_event_callback, -}; - -/* SPI Bus Handles */ - -inline static void furi_hal_spi_bus_r_handle_event_callback( - FuriHalSpiBusHandle* handle, - FuriHalSpiBusHandleEvent event, - const LL_SPI_InitTypeDef* preset) { - if(event == FuriHalSpiBusHandleEventInit) { - furi_hal_gpio_write(handle->cs, true); - furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - } else if(event == FuriHalSpiBusHandleEventDeinit) { - furi_hal_gpio_write(handle->cs, true); - furi_hal_gpio_init(handle->cs, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - } else if(event == FuriHalSpiBusHandleEventActivate) { - LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset); - LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER); - LL_SPI_Enable(handle->bus->spi); - - furi_hal_gpio_init_ex( - handle->miso, - GpioModeAltFunctionPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFn5SPI1); - furi_hal_gpio_init_ex( - handle->mosi, - GpioModeAltFunctionPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFn5SPI1); - furi_hal_gpio_init_ex( - handle->sck, - GpioModeAltFunctionPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFn5SPI1); - - furi_hal_gpio_write(handle->cs, false); - } else if(event == FuriHalSpiBusHandleEventDeactivate) { - furi_hal_gpio_write(handle->cs, true); - - furi_hal_gpio_init(handle->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(handle->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(handle->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - - LL_SPI_Disable(handle->bus->spi); - } -} - -inline static void furi_hal_spi_bus_nfc_handle_event_callback( - FuriHalSpiBusHandle* handle, - FuriHalSpiBusHandleEvent event, - const LL_SPI_InitTypeDef* preset) { - if(event == FuriHalSpiBusHandleEventInit) { - // Configure GPIOs in normal SPI mode - furi_hal_gpio_init_ex( - handle->miso, - GpioModeAltFunctionPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFn5SPI1); - furi_hal_gpio_init_ex( - handle->mosi, - GpioModeAltFunctionPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFn5SPI1); - furi_hal_gpio_init_ex( - handle->sck, - GpioModeAltFunctionPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFn5SPI1); - furi_hal_gpio_write(handle->cs, true); - furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - } else if(event == FuriHalSpiBusHandleEventDeinit) { - // Configure GPIOs for st25r3916 Transparent mode - furi_hal_gpio_init(handle->sck, GpioModeInput, GpioPullUp, GpioSpeedLow); - furi_hal_gpio_init(handle->miso, GpioModeInput, GpioPullUp, GpioSpeedLow); - furi_hal_gpio_init(handle->cs, GpioModeInput, GpioPullUp, GpioSpeedLow); - furi_hal_gpio_write(handle->mosi, false); - furi_hal_gpio_init(handle->mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - } else if(event == FuriHalSpiBusHandleEventActivate) { - LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset); - LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER); - LL_SPI_Enable(handle->bus->spi); - - furi_hal_gpio_init_ex( - handle->miso, - GpioModeAltFunctionPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFn5SPI1); - furi_hal_gpio_init_ex( - handle->mosi, - GpioModeAltFunctionPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFn5SPI1); - furi_hal_gpio_init_ex( - handle->sck, - GpioModeAltFunctionPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFn5SPI1); - - } else if(event == FuriHalSpiBusHandleEventDeactivate) { - furi_hal_gpio_init(handle->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(handle->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(handle->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - - LL_SPI_Disable(handle->bus->spi); - } -} - -static void furi_hal_spi_bus_handle_subghz_event_callback( - FuriHalSpiBusHandle* handle, - FuriHalSpiBusHandleEvent event) { - furi_hal_spi_bus_r_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_8m); -} - -FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz = { - .bus = &furi_hal_spi_bus_r, - .callback = furi_hal_spi_bus_handle_subghz_event_callback, - .miso = &gpio_spi_r_miso, - .mosi = &gpio_spi_r_mosi, - .sck = &gpio_spi_r_sck, - .cs = &gpio_subghz_cs, -}; - -static void furi_hal_spi_bus_handle_nfc_event_callback( - FuriHalSpiBusHandle* handle, - FuriHalSpiBusHandleEvent event) { - furi_hal_spi_bus_nfc_handle_event_callback(handle, event, &furi_hal_spi_preset_2edge_low_8m); -} - -FuriHalSpiBusHandle furi_hal_spi_bus_handle_nfc = { - .bus = &furi_hal_spi_bus_r, - .callback = furi_hal_spi_bus_handle_nfc_event_callback, - .miso = &gpio_spi_r_miso, - .mosi = &gpio_spi_r_mosi, - .sck = &gpio_spi_r_sck, - .cs = &gpio_nfc_cs, -}; - -static void furi_hal_spi_bus_handle_external_event_callback( - FuriHalSpiBusHandle* handle, - FuriHalSpiBusHandleEvent event) { - furi_hal_spi_bus_r_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_2m); -} - -FuriHalSpiBusHandle furi_hal_spi_bus_handle_external = { - .bus = &furi_hal_spi_bus_r, - .callback = furi_hal_spi_bus_handle_external_event_callback, - .miso = &gpio_ext_pa6, - .mosi = &gpio_ext_pa7, - .sck = &gpio_ext_pb3, - .cs = &gpio_ext_pa4, -}; - -inline static void furi_hal_spi_bus_d_handle_event_callback( - FuriHalSpiBusHandle* handle, - FuriHalSpiBusHandleEvent event, - const LL_SPI_InitTypeDef* preset) { - if(event == FuriHalSpiBusHandleEventInit) { - furi_hal_gpio_write(handle->cs, true); - furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh); - - furi_hal_gpio_init_ex( - handle->miso, - GpioModeAltFunctionPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFn5SPI2); - furi_hal_gpio_init_ex( - handle->mosi, - GpioModeAltFunctionPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFn5SPI2); - furi_hal_gpio_init_ex( - handle->sck, - GpioModeAltFunctionPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFn5SPI2); - - } else if(event == FuriHalSpiBusHandleEventDeinit) { - furi_hal_gpio_write(handle->cs, true); - furi_hal_gpio_init(handle->cs, GpioModeAnalog, GpioPullUp, GpioSpeedLow); - } else if(event == FuriHalSpiBusHandleEventActivate) { - LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset); - LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER); - LL_SPI_Enable(handle->bus->spi); - furi_hal_gpio_write(handle->cs, false); - } else if(event == FuriHalSpiBusHandleEventDeactivate) { - furi_hal_gpio_write(handle->cs, true); - LL_SPI_Disable(handle->bus->spi); - } -} - -static void furi_hal_spi_bus_handle_display_event_callback( - FuriHalSpiBusHandle* handle, - FuriHalSpiBusHandleEvent event) { - furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_4m); -} - -FuriHalSpiBusHandle furi_hal_spi_bus_handle_display = { - .bus = &furi_hal_spi_bus_d, - .callback = furi_hal_spi_bus_handle_display_event_callback, - .miso = &gpio_spi_d_miso, - .mosi = &gpio_spi_d_mosi, - .sck = &gpio_spi_d_sck, - .cs = &gpio_display_cs, -}; - -static void furi_hal_spi_bus_handle_sd_fast_event_callback( - FuriHalSpiBusHandle* handle, - FuriHalSpiBusHandleEvent event) { - furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_16m); -} - -FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast = { - .bus = &furi_hal_spi_bus_d, - .callback = furi_hal_spi_bus_handle_sd_fast_event_callback, - .miso = &gpio_spi_d_miso, - .mosi = &gpio_spi_d_mosi, - .sck = &gpio_spi_d_sck, - .cs = &gpio_sdcard_cs, -}; - -static void furi_hal_spi_bus_handle_sd_slow_event_callback( - FuriHalSpiBusHandle* handle, - FuriHalSpiBusHandleEvent event) { - furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_2m); -} - -FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_slow = { - .bus = &furi_hal_spi_bus_d, - .callback = furi_hal_spi_bus_handle_sd_slow_event_callback, - .miso = &gpio_spi_d_miso, - .mosi = &gpio_spi_d_mosi, - .sck = &gpio_spi_d_sck, - .cs = &gpio_sdcard_cs, -}; diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c deleted file mode 100644 index ade46238982..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ /dev/null @@ -1,679 +0,0 @@ -#include "furi_hal_subghz.h" -#include "furi_hal_subghz_configs.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#define TAG "FuriHalSubGhz" - -typedef struct { - volatile SubGhzState state; - volatile SubGhzRegulation regulation; - volatile FuriHalSubGhzPreset preset; -} FuriHalSubGhz; - -volatile FuriHalSubGhz furi_hal_subghz = { - .state = SubGhzStateInit, - .regulation = SubGhzRegulationTxRx, - .preset = FuriHalSubGhzPresetIDLE, -}; - -void furi_hal_subghz_init() { - furi_assert(furi_hal_subghz.state == SubGhzStateInit); - furi_hal_subghz.state = SubGhzStateIdle; - furi_hal_subghz.preset = FuriHalSubGhzPresetIDLE; - - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - -#ifdef FURI_HAL_SUBGHZ_TX_GPIO - furi_hal_gpio_init(&FURI_HAL_SUBGHZ_TX_GPIO, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); -#endif - - // Reset - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - cc1101_reset(&furi_hal_spi_bus_handle_subghz); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHighImpedance); - - // Prepare GD0 for power on self test - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); - - // GD0 low - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHW); - while(furi_hal_gpio_read(&gpio_cc1101_g0) != false) - ; - - // GD0 high - cc1101_write_reg( - &furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHW | CC1101_IOCFG_INV); - while(furi_hal_gpio_read(&gpio_cc1101_g0) != true) - ; - - // Reset GD0 to floating state - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHighImpedance); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - - // RF switches - furi_hal_gpio_init(&gpio_rf_sw_0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW); - - // Go to sleep - cc1101_shutdown(&furi_hal_spi_bus_handle_subghz); - - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); - FURI_LOG_I(TAG, "Init OK"); -} - -void furi_hal_subghz_sleep() { - furi_assert(furi_hal_subghz.state == SubGhzStateIdle); - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - - cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); - - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHighImpedance); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - - cc1101_shutdown(&furi_hal_spi_bus_handle_subghz); - - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); - - furi_hal_subghz.preset = FuriHalSubGhzPresetIDLE; -} - -void furi_hal_subghz_dump_state() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - printf( - "[furi_hal_subghz] cc1101 chip %d, version %d\r\n", - cc1101_get_partnumber(&furi_hal_spi_bus_handle_subghz), - cc1101_get_version(&furi_hal_spi_bus_handle_subghz)); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); -} - -void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset) { - if(preset == FuriHalSubGhzPresetOok650Async) { - furi_hal_subghz_load_registers((uint8_t*)furi_hal_subghz_preset_ook_650khz_async_regs); - furi_hal_subghz_load_patable(furi_hal_subghz_preset_ook_async_patable); - } else if(preset == FuriHalSubGhzPresetOok270Async) { - furi_hal_subghz_load_registers((uint8_t*)furi_hal_subghz_preset_ook_270khz_async_regs); - furi_hal_subghz_load_patable(furi_hal_subghz_preset_ook_async_patable); - } else if(preset == FuriHalSubGhzPreset2FSKDev238Async) { - furi_hal_subghz_load_registers( - (uint8_t*)furi_hal_subghz_preset_2fsk_dev2_38khz_async_regs); - furi_hal_subghz_load_patable(furi_hal_subghz_preset_2fsk_async_patable); - } else if(preset == FuriHalSubGhzPreset2FSKDev476Async) { - furi_hal_subghz_load_registers( - (uint8_t*)furi_hal_subghz_preset_2fsk_dev47_6khz_async_regs); - furi_hal_subghz_load_patable(furi_hal_subghz_preset_2fsk_async_patable); - } else if(preset == FuriHalSubGhzPresetMSK99_97KbAsync) { - furi_hal_subghz_load_registers((uint8_t*)furi_hal_subghz_preset_msk_99_97kb_async_regs); - furi_hal_subghz_load_patable(furi_hal_subghz_preset_msk_async_patable); - } else if(preset == FuriHalSubGhzPresetGFSK9_99KbAsync) { - furi_hal_subghz_load_registers((uint8_t*)furi_hal_subghz_preset_gfsk_9_99kb_async_regs); - furi_hal_subghz_load_patable(furi_hal_subghz_preset_gfsk_async_patable); - } else { - furi_crash("SubGhz: Missing config."); - } - furi_hal_subghz.preset = preset; -} - -void furi_hal_subghz_load_custom_preset(uint8_t* preset_data) { - //load config - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_reset(&furi_hal_spi_bus_handle_subghz); - uint32_t i = 0; - uint8_t pa[8] = {0}; - while(preset_data[i]) { - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, preset_data[i], preset_data[i + 1]); - i += 2; - } - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); - - //load pa table - memcpy(&pa[0], &preset_data[i + 2], 8); - furi_hal_subghz_load_patable(pa); - furi_hal_subghz.preset = FuriHalSubGhzPresetCustom; - - //show debug - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - i = 0; - FURI_LOG_D(TAG, "Loading custom preset"); - while(preset_data[i]) { - FURI_LOG_D(TAG, "Reg[%lu]: %02X=%02X", i, preset_data[i], preset_data[i + 1]); - i += 2; - } - for(uint8_t y = i; y < i + 10; y++) { - FURI_LOG_D(TAG, "PA[%lu]: %02X", y, preset_data[y]); - } - } -} - -void furi_hal_subghz_load_registers(uint8_t* data) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_reset(&furi_hal_spi_bus_handle_subghz); - uint32_t i = 0; - while(data[i]) { - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, data[i], data[i + 1]); - i += 2; - } - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); -} - -void furi_hal_subghz_load_patable(const uint8_t data[8]) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_set_pa_table(&furi_hal_spi_bus_handle_subghz, data); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); -} - -void furi_hal_subghz_write_packet(const uint8_t* data, uint8_t size) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_flush_tx(&furi_hal_spi_bus_handle_subghz); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_FIFO, size); - cc1101_write_fifo(&furi_hal_spi_bus_handle_subghz, data, size); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); -} - -void furi_hal_subghz_flush_rx() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_flush_rx(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); -} - -void furi_hal_subghz_flush_tx() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_flush_tx(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); -} - -bool furi_hal_subghz_rx_pipe_not_empty() { - CC1101RxBytes status[1]; - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_read_reg( - &furi_hal_spi_bus_handle_subghz, (CC1101_STATUS_RXBYTES) | CC1101_BURST, (uint8_t*)status); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); - // TODO: you can add a buffer overflow flag if needed - if(status->NUM_RXBYTES > 0) { - return true; - } else { - return false; - } -} - -bool furi_hal_subghz_is_rx_data_crc_valid() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - uint8_t data[1]; - cc1101_read_reg(&furi_hal_spi_bus_handle_subghz, CC1101_STATUS_LQI | CC1101_BURST, data); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); - if(((data[0] >> 7) & 0x01)) { - return true; - } else { - return false; - } -} - -void furi_hal_subghz_read_packet(uint8_t* data, uint8_t* size) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_read_fifo(&furi_hal_spi_bus_handle_subghz, data, size); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); -} - -void furi_hal_subghz_shutdown() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - // Reset and shutdown - cc1101_shutdown(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); -} - -void furi_hal_subghz_reset() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); - cc1101_reset(&furi_hal_spi_bus_handle_subghz); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHighImpedance); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); -} - -void furi_hal_subghz_idle() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); -} - -void furi_hal_subghz_rx() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); -} - -bool furi_hal_subghz_tx() { - if(furi_hal_subghz.regulation != SubGhzRegulationTxRx) return false; - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_switch_to_tx(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); - return true; -} - -float furi_hal_subghz_get_rssi() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - int32_t rssi_dec = cc1101_get_rssi(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); - - float rssi = rssi_dec; - if(rssi_dec >= 128) { - rssi = ((rssi - 256.0f) / 2.0f) - 74.0f; - } else { - rssi = (rssi / 2.0f) - 74.0f; - } - - return rssi; -} - -uint8_t furi_hal_subghz_get_lqi() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - uint8_t data[1]; - cc1101_read_reg(&furi_hal_spi_bus_handle_subghz, CC1101_STATUS_LQI | CC1101_BURST, data); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); - return data[0] & 0x7F; -} - -bool furi_hal_subghz_is_frequency_valid(uint32_t value) { - if(!(value >= 299999755 && value <= 348000335) && - !(value >= 386999938 && value <= 464000000) && - !(value >= 778999847 && value <= 928000000)) { - return false; - } - - return true; -} - -uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) { - value = furi_hal_subghz_set_frequency(value); - if(value >= 299999755 && value <= 348000335) { - furi_hal_subghz_set_path(FuriHalSubGhzPath315); - } else if(value >= 386999938 && value <= 464000000) { - furi_hal_subghz_set_path(FuriHalSubGhzPath433); - } else if(value >= 778999847 && value <= 928000000) { - furi_hal_subghz_set_path(FuriHalSubGhzPath868); - } else { - furi_crash("SubGhz: Incorrect frequency during set."); - } - return 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 = SubGhzRegulationOnlyRx; - } - - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - uint32_t real_frequency = cc1101_set_frequency(&furi_hal_spi_bus_handle_subghz, value); - cc1101_calibrate(&furi_hal_spi_bus_handle_subghz); - - while(true) { - CC1101Status status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz); - if(status.STATE == CC1101StateIDLE) break; - } - - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); - return real_frequency; -} - -void furi_hal_subghz_set_path(FuriHalSubGhzPath path) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - if(path == FuriHalSubGhzPath433) { - furi_hal_gpio_write(&gpio_rf_sw_0, 0); - cc1101_write_reg( - &furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW | CC1101_IOCFG_INV); - } else if(path == FuriHalSubGhzPath315) { - furi_hal_gpio_write(&gpio_rf_sw_0, 1); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW); - } else if(path == FuriHalSubGhzPath868) { - furi_hal_gpio_write(&gpio_rf_sw_0, 1); - cc1101_write_reg( - &furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW | CC1101_IOCFG_INV); - } else if(path == FuriHalSubGhzPathIsolate) { - furi_hal_gpio_write(&gpio_rf_sw_0, 0); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW); - } else { - furi_crash("SubGhz: Incorrect path during set."); - } - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); -} - -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() { - // 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) { - 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) { - furi_hal_subghz_capture_callback( - false, - LL_TIM_IC_GetCaptureCH2(TIM2) - furi_hal_subghz_capture_delta_duration, - (void*)furi_hal_subghz_capture_callback_context); - } - } -} - -void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* context) { - furi_assert(furi_hal_subghz.state == SubGhzStateIdle); - furi_hal_subghz.state = SubGhzStateAsyncRx; - - furi_hal_subghz_capture_callback = callback; - furi_hal_subghz_capture_callback_context = context; - - 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; - LL_TIM_Init(TIM2, &TIM_InitStruct); - - // Timer: advanced - LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); - LL_TIM_DisableARRPreload(TIM2); - 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); - - // 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); - LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_FALLING); - LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1); - - // Timer: channel 2 direct - LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI); - LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); - LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING); - 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); - - // 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); - - // Start timer - LL_TIM_SetCounter(TIM2, 0); - LL_TIM_EnableCounter(TIM2); - - // Switch to RX - furi_hal_subghz_rx(); -} - -void furi_hal_subghz_stop_async_rx() { - furi_assert(furi_hal_subghz.state == SubGhzStateAsyncRx); - furi_hal_subghz.state = SubGhzStateIdle; - - // Shutdown radio - furi_hal_subghz_idle(); - - FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(TIM2); - FURI_CRITICAL_EXIT(); - furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); - - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -} - -#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL (256) -#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF (API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL / 2) -#define API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME 333 - -typedef struct { - uint32_t* buffer; - bool flip_flop; - FuriHalSubGhzAsyncTxCallback callback; - void* callback_context; - uint64_t duty_high; - uint64_t duty_low; -} FuriHalSubGhzAsyncTx; - -static FuriHalSubGhzAsyncTx furi_hal_subghz_async_tx = {0}; - -static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { - while(samples > 0) { - bool is_odd = samples % 2; - LevelDuration ld = - furi_hal_subghz_async_tx.callback(furi_hal_subghz_async_tx.callback_context); - - if(level_duration_is_wait(ld)) { - return; - } else if(level_duration_is_reset(ld)) { - // One more even sample required to end at low level - if(is_odd) { - *buffer = API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; - buffer++; - samples--; - furi_hal_subghz_async_tx.duty_low += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; - } - break; - } else { - // Inject guard time if level is incorrect - bool level = level_duration_get_level(ld); - if(is_odd == level) { - *buffer = API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; - buffer++; - samples--; - if(!level) { - furi_hal_subghz_async_tx.duty_high += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; - } else { - furi_hal_subghz_async_tx.duty_low += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; - } - // This code must be invoked only once: when encoder starts with low level. - // Otherwise whole thing will crash. - furi_check(samples > 0); - } - - uint32_t duration = level_duration_get_duration(ld); - furi_assert(duration > 0); - *buffer = duration; - buffer++; - samples--; - - if(level) { - furi_hal_subghz_async_tx.duty_high += duration; - } else { - furi_hal_subghz_async_tx.duty_low += duration; - } - } - } - - memset(buffer, 0, samples * sizeof(uint32_t)); -} - -static void furi_hal_subghz_async_tx_dma_isr() { - furi_assert( - furi_hal_subghz.state == SubGhzStateAsyncTx || - furi_hal_subghz.state == SubGhzStateAsyncTxEnd || - furi_hal_subghz.state == SubGhzStateAsyncTxLast); - if(LL_DMA_IsActiveFlag_HT1(DMA1)) { - LL_DMA_ClearFlag_HT1(DMA1); - furi_hal_subghz_async_tx_refill( - furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); - } - if(LL_DMA_IsActiveFlag_TC1(DMA1)) { - LL_DMA_ClearFlag_TC1(DMA1); - furi_hal_subghz_async_tx_refill( - furi_hal_subghz_async_tx.buffer + API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF, - API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); - } -} - -static void furi_hal_subghz_async_tx_timer_isr() { - if(LL_TIM_IsActiveFlag_UPDATE(TIM2)) { - LL_TIM_ClearFlag_UPDATE(TIM2); - if(LL_TIM_GetAutoReload(TIM2) == 0) { - if(furi_hal_subghz.state == SubGhzStateAsyncTx) { - furi_hal_subghz.state = SubGhzStateAsyncTxLast; - //forcibly pulls the pin to the ground so that there is no carrier - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullDown, GpioSpeedLow); - } else { - furi_hal_subghz.state = SubGhzStateAsyncTxEnd; - LL_TIM_DisableCounter(TIM2); - } - } - } -} - -bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context) { - furi_assert(furi_hal_subghz.state == SubGhzStateIdle); - furi_assert(callback); - - //If transmission is prohibited by regional settings - if(furi_hal_subghz.regulation != SubGhzRegulationTxRx) return false; - - furi_hal_subghz_async_tx.callback = callback; - furi_hal_subghz_async_tx.callback_context = context; - - furi_hal_subghz.state = SubGhzStateAsyncTx; - - furi_hal_subghz_async_tx.duty_low = 0; - furi_hal_subghz_async_tx.duty_high = 0; - - furi_hal_subghz_async_tx.buffer = - malloc(API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * sizeof(uint32_t)); - furi_hal_subghz_async_tx_refill( - furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL); - - // Connect CC1101_GD0 to TIM2 as output - furi_hal_gpio_init_ex( - &gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullDown, GpioSpeedLow, GpioAltFn1TIM2); - - // Configure DMA - LL_DMA_InitTypeDef dma_config = {0}; - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR); - dma_config.MemoryOrM2MDstAddress = (uint32_t)furi_hal_subghz_async_tx.buffer; - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - dma_config.Mode = LL_DMA_MODE_CIRCULAR; - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.NbData = API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL; - 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); - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, furi_hal_subghz_async_tx_dma_isr, NULL); - LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); - - // Configure TIM2 - LL_TIM_InitTypeDef TIM_InitStruct = {0}; - TIM_InitStruct.Prescaler = 64 - 1; - TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; - TIM_InitStruct.Autoreload = 1000; - TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; - LL_TIM_Init(TIM2, &TIM_InitStruct); - LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); - LL_TIM_EnableARRPreload(TIM2); - - // Configure TIM2 CH2 - LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; - TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_TOGGLE; - TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE; - TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE; - TIM_OC_InitStruct.CompareValue = 0; - TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH; - LL_TIM_OC_Init(TIM2, LL_TIM_CHANNEL_CH2, &TIM_OC_InitStruct); - LL_TIM_OC_DisableFast(TIM2, LL_TIM_CHANNEL_CH2); - LL_TIM_DisableMasterSlaveMode(TIM2); - - furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_async_tx_timer_isr, NULL); - - LL_TIM_EnableIT_UPDATE(TIM2); - LL_TIM_EnableDMAReq_UPDATE(TIM2); - LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); - - // Start counter - LL_TIM_GenerateEvent_UPDATE(TIM2); -#ifdef FURI_HAL_SUBGHZ_TX_GPIO - furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, true); -#endif - furi_hal_subghz_tx(); - - LL_TIM_SetCounter(TIM2, 0); - LL_TIM_EnableCounter(TIM2); - return true; -} - -bool furi_hal_subghz_is_async_tx_complete() { - return furi_hal_subghz.state == SubGhzStateAsyncTxEnd; -} - -void furi_hal_subghz_stop_async_tx() { - furi_assert( - furi_hal_subghz.state == SubGhzStateAsyncTx || - furi_hal_subghz.state == SubGhzStateAsyncTxLast || - furi_hal_subghz.state == SubGhzStateAsyncTxEnd); - - // Shutdown radio - furi_hal_subghz_idle(); -#ifdef FURI_HAL_SUBGHZ_TX_GPIO - furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, false); -#endif - - // Deinitialize Timer - FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(TIM2); - furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); - - // Deinitialize DMA - LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1); - - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); - - // Deinitialize GPIO - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - FURI_CRITICAL_EXIT(); - - free(furi_hal_subghz_async_tx.buffer); - - float duty_cycle = - 100.0f * (float)furi_hal_subghz_async_tx.duty_high / - ((float)furi_hal_subghz_async_tx.duty_low + (float)furi_hal_subghz_async_tx.duty_high); - FURI_LOG_D( - TAG, - "Async TX Radio stats: on %0.0fus, off %0.0fus, DutyCycle: %0.0f%%", - (double)furi_hal_subghz_async_tx.duty_high, - (double)furi_hal_subghz_async_tx.duty_low, - (double)duty_cycle); - - furi_hal_subghz.state = SubGhzStateIdle; -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h b/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h deleted file mode 100644 index 5ea17b6ddd0..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h +++ /dev/null @@ -1,314 +0,0 @@ -#pragma once - -#include - -static const uint8_t furi_hal_subghz_preset_ook_270khz_async_regs[][2] = { - // https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration - - /* GPIO GD0 */ - {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input - - /* FIFO and internals */ - {CC1101_FIFOTHR, 0x47}, // The only important bit is ADC_RETENTION, FIFO Tx=33 Rx=32 - - /* Packet engine */ - {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening - - /* Frequency Synthesizer Control */ - {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz - - // 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, 0x32}, // Data rate is 3.79372 kBaud - {CC1101_MDMCFG4, 0x67}, // Rx BW filter is 270.833333kHz - - /* 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, - 0x18}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off - - /* Automatic Gain Control */ - {CC1101_AGCCTRL0, - 0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary - {CC1101_AGCCTRL1, - 0x00}, // 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, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 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, 0x11}, // Adjusts current TX LO buffer + high is PATABLE[1] - {CC1101_FREND1, 0xB6}, // - - /* End */ - {0, 0}, -}; - -static const uint8_t furi_hal_subghz_preset_ook_650khz_async_regs[][2] = { - // https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration - - /* 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 - - // 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, 0x32}, // Data rate is 3.79372 kBaud - {CC1101_MDMCFG4, 0x17}, // 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, - 0x18}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off - - /* Automatic Gain Control */ - // {CC1101_AGCTRL0,0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary - // {CC1101_AGCTRL1,0x00}, // 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, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB - //MAGN_TARGET for RX filter BW =< 100 kHz is 0x3. For higher RX filter BW's MAGN_TARGET is 0x7. - {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, 0x11}, // Adjusts current TX LO buffer + high is PATABLE[1] - {CC1101_FREND1, 0xB6}, // - - /* End */ - {0, 0}, -}; - -static const uint8_t furi_hal_subghz_preset_2fsk_dev2_38khz_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}, - {CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) - {CC1101_MDMCFG3, 0x83}, // Data rate is 4.79794 kBaud - {CC1101_MDMCFG4, 0x67}, //Rx BW filter is 270.833333 kHz - {CC1101_DEVIATN, 0x04}, //Deviation 2.380371 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, - 0x91}, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary - {CC1101_AGCCTRL1, - 0x00}, // 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}, - - /* End */ - {0, 0}, -}; - -static const uint8_t furi_hal_subghz_preset_2fsk_dev47_6khz_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}, - {CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) - {CC1101_MDMCFG3, 0x83}, // Data rate is 4.79794 kBaud - {CC1101_MDMCFG4, 0x67}, //Rx BW filter is 270.833333 kHz - {CC1101_DEVIATN, 0x47}, //Deviation 47.60742 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, - 0x91}, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary - {CC1101_AGCCTRL1, - 0x00}, // 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}, - - /* End */ - {0, 0}, -}; - -static const uint8_t furi_hal_subghz_preset_msk_99_97kb_async_regs[][2] = { - /* GPIO GD0 */ - {CC1101_IOCFG0, 0x06}, - - {CC1101_FIFOTHR, 0x07}, // The only important bit is ADC_RETENTION - {CC1101_SYNC1, 0x46}, - {CC1101_SYNC0, 0x4C}, - {CC1101_ADDR, 0x00}, - {CC1101_PKTLEN, 0x00}, - {CC1101_CHANNR, 0x00}, - - {CC1101_PKTCTRL0, 0x05}, - - {CC1101_FSCTRL0, 0x23}, - {CC1101_FSCTRL1, 0x06}, - - {CC1101_MDMCFG0, 0xF8}, - {CC1101_MDMCFG1, 0x22}, - {CC1101_MDMCFG2, 0x72}, - {CC1101_MDMCFG3, 0xF8}, - {CC1101_MDMCFG4, 0x5B}, - {CC1101_DEVIATN, 0x47}, - - {CC1101_MCSM0, 0x18}, - {CC1101_FOCCFG, 0x16}, - - {CC1101_AGCCTRL0, 0xB2}, - {CC1101_AGCCTRL1, 0x00}, - {CC1101_AGCCTRL2, 0xC7}, - - {CC1101_FREND0, 0x10}, - {CC1101_FREND1, 0x56}, - - {CC1101_BSCFG, 0x1C}, - {CC1101_FSTEST, 0x59}, - - /* End */ - {0, 0}, -}; - -static const uint8_t furi_hal_subghz_preset_gfsk_9_99kb_async_regs[][2] = { - - {CC1101_IOCFG0, 0x06}, //GDO0 Output Pin Configuration - {CC1101_FIFOTHR, 0x47}, //RX FIFO and TX FIFO Thresholds - - //1 : CRC calculation in TX and CRC check in RX enabled, - //1 : Variable packet length mode. Packet length configured by the first byte after sync word - {CC1101_PKTCTRL0, 0x05}, - - {CC1101_FSCTRL1, 0x06}, //Frequency Synthesizer Control - - {CC1101_SYNC1, 0x46}, - {CC1101_SYNC0, 0x4C}, - {CC1101_ADDR, 0x00}, - {CC1101_PKTLEN, 0x00}, - - {CC1101_MDMCFG4, 0xC8}, //Modem Configuration 9.99 - {CC1101_MDMCFG3, 0x93}, //Modem Configuration - {CC1101_MDMCFG2, 0x12}, // 2: 16/16 sync word bits detected - - {CC1101_DEVIATN, 0x34}, //Deviation = 19.042969 - {CC1101_MCSM0, 0x18}, //Main Radio Control State Machine Configuration - {CC1101_FOCCFG, 0x16}, //Frequency Offset Compensation Configuration - - {CC1101_AGCCTRL2, 0x43}, //AGC Control - {CC1101_AGCCTRL1, 0x40}, - {CC1101_AGCCTRL0, 0x91}, - - {CC1101_WORCTRL, 0xFB}, //Wake On Radio Control - /* End */ - {0, 0}, -}; - -static const uint8_t furi_hal_subghz_preset_ook_async_patable[8] = { - 0x00, - 0xC0, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03 - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00}; - -static const uint8_t furi_hal_subghz_preset_ook_async_patable_au[8] = { - 0x00, - 0x37, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03 - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00}; - -static const uint8_t furi_hal_subghz_preset_2fsk_async_patable[8] = { - 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00}; - -static const uint8_t furi_hal_subghz_preset_msk_async_patable[8] = { - 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00}; - -static const uint8_t furi_hal_subghz_preset_gfsk_async_patable[8] = { - 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00}; diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb.c b/firmware/targets/f7/furi_hal/furi_hal_usb.c deleted file mode 100644 index 86b10c79cfa..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_usb.c +++ /dev/null @@ -1,319 +0,0 @@ -#include "furi_hal_version.h" -#include "furi_hal_usb_i.h" -#include "furi_hal_usb.h" -#include -#include -#include - -#include "usb.h" - -#define TAG "FuriHalUsb" - -#define USB_RECONNECT_DELAY 500 - -typedef struct { - FuriThread* thread; - bool enabled; - bool connected; - bool mode_lock; - FuriHalUsbInterface* if_cur; - FuriHalUsbInterface* if_next; - void* if_ctx; - FuriHalUsbStateCallback callback; - void* cb_ctx; -} UsbSrv; - -typedef enum { - EventModeChange = (1 << 0), - EventEnable = (1 << 1), - EventDisable = (1 << 2), - EventReinit = (1 << 3), - - EventReset = (1 << 4), - EventRequest = (1 << 5), - - EventModeChangeStart = (1 << 6), -} UsbEvent; - -#define USB_SRV_ALL_EVENTS \ - (EventModeChange | EventEnable | EventDisable | EventReinit | EventReset | EventRequest | \ - EventModeChangeStart) - -static UsbSrv usb; - -static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US); - -static uint32_t ubuf[0x20]; -usbd_device udev; - -static int32_t furi_hal_usb_thread(void* context); -static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_t* length); -static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep); -static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep); -static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep); - -/* Low-level init */ -void furi_hal_usb_init(void) { - LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; - LL_PWR_EnableVddUSB(); - - GPIO_InitStruct.Pin = LL_GPIO_PIN_11 | LL_GPIO_PIN_12; - GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; - GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH; - GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; - GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; - GPIO_InitStruct.Alternate = LL_GPIO_AF_10; - LL_GPIO_Init(GPIOA, &GPIO_InitStruct); - - usbd_init(&udev, &usbd_hw, USB_EP0_SIZE, ubuf, sizeof(ubuf)); - usbd_enable(&udev, true); - - usbd_reg_descr(&udev, usb_descriptor_get); - usbd_reg_event(&udev, usbd_evt_susp, susp_evt); - usbd_reg_event(&udev, usbd_evt_wkup, wkup_evt); - // Reset callback will be enabled after first mode change to avoid getting false reset events - - usb.enabled = false; - usb.if_cur = NULL; - NVIC_SetPriority(USB_LP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); - NVIC_SetPriority(USB_HP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0)); - NVIC_EnableIRQ(USB_LP_IRQn); - NVIC_EnableIRQ(USB_HP_IRQn); - - usb.thread = furi_thread_alloc(); - furi_thread_set_name(usb.thread, "UsbDriver"); - furi_thread_set_stack_size(usb.thread, 1024); - furi_thread_set_callback(usb.thread, furi_hal_usb_thread); - furi_thread_start(usb.thread); - - FURI_LOG_I(TAG, "Init OK"); -} - -bool furi_hal_usb_set_config(FuriHalUsbInterface* new_if, void* ctx) { - if(usb.mode_lock) { - return false; - } - - usb.if_next = new_if; - usb.if_ctx = ctx; - if(usb.thread == NULL) { - // Service thread hasn't started yet, so just save interface mode - return true; - } - furi_assert(usb.thread); - furi_thread_flags_set(furi_thread_get_id(usb.thread), EventModeChange); - return true; -} - -FuriHalUsbInterface* furi_hal_usb_get_config() { - return usb.if_cur; -} - -void furi_hal_usb_lock() { - FURI_LOG_I(TAG, "Mode lock"); - usb.mode_lock = true; -} - -void furi_hal_usb_unlock() { - FURI_LOG_I(TAG, "Mode unlock"); - usb.mode_lock = false; -} - -bool furi_hal_usb_is_locked() { - return usb.mode_lock; -} - -void furi_hal_usb_disable() { - furi_assert(usb.thread); - furi_thread_flags_set(furi_thread_get_id(usb.thread), EventDisable); -} - -void furi_hal_usb_enable() { - furi_assert(usb.thread); - furi_thread_flags_set(furi_thread_get_id(usb.thread), EventEnable); -} - -void furi_hal_usb_reinit() { - furi_assert(usb.thread); - furi_thread_flags_set(furi_thread_get_id(usb.thread), EventReinit); -} - -/* Get device / configuration descriptors */ -static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_t* length) { - const uint8_t dtype = req->wValue >> 8; - const uint8_t dnumber = req->wValue & 0xFF; - const void* desc; - uint16_t len = 0; - if(usb.if_cur == NULL) return usbd_fail; - - switch(dtype) { - case USB_DTYPE_DEVICE: - furi_thread_flags_set(furi_thread_get_id(usb.thread), EventRequest); - if(usb.callback != NULL) { - usb.callback(FuriHalUsbStateEventDescriptorRequest, usb.cb_ctx); - } - desc = usb.if_cur->dev_descr; - break; - case USB_DTYPE_CONFIGURATION: - desc = usb.if_cur->cfg_descr; - len = ((struct usb_string_descriptor*)(usb.if_cur->cfg_descr))->wString[0]; - break; - case USB_DTYPE_STRING: - if(dnumber == UsbDevLang) { - desc = &dev_lang_desc; - } else if((dnumber == UsbDevManuf) && (usb.if_cur->str_manuf_descr != NULL)) { - desc = usb.if_cur->str_manuf_descr; - } else if((dnumber == UsbDevProduct) && (usb.if_cur->str_prod_descr != NULL)) { - desc = usb.if_cur->str_prod_descr; - } else if((dnumber == UsbDevSerial) && (usb.if_cur->str_serial_descr != NULL)) { - desc = usb.if_cur->str_serial_descr; - } else - return usbd_fail; - break; - default: - return usbd_fail; - } - if(desc == NULL) return usbd_fail; - - if(len == 0) { - len = ((struct usb_header_descriptor*)desc)->bLength; - } - *address = (void*)desc; - *length = len; - return usbd_ack; -} - -void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx) { - usb.callback = cb; - usb.cb_ctx = ctx; -} - -static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep) { - UNUSED(dev); - UNUSED(event); - UNUSED(ep); - furi_thread_flags_set(furi_thread_get_id(usb.thread), EventReset); - if(usb.callback != NULL) { - usb.callback(FuriHalUsbStateEventReset, usb.cb_ctx); - } -} - -static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) { - UNUSED(dev); - UNUSED(event); - UNUSED(ep); - if((usb.if_cur != NULL) && (usb.connected == true)) { - usb.connected = false; - usb.if_cur->suspend(&udev); - - furi_hal_power_insomnia_exit(); - } - if(usb.callback != NULL) { - usb.callback(FuriHalUsbStateEventSuspend, usb.cb_ctx); - } -} - -static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) { - UNUSED(dev); - UNUSED(event); - UNUSED(ep); - if((usb.if_cur != NULL) && (usb.connected == false)) { - usb.connected = true; - usb.if_cur->wakeup(&udev); - - furi_hal_power_insomnia_enter(); - } - if(usb.callback != NULL) { - usb.callback(FuriHalUsbStateEventWakeup, usb.cb_ctx); - } -} - -static int32_t furi_hal_usb_thread(void* context) { - UNUSED(context); - bool usb_request_pending = false; - uint8_t usb_wait_time = 0; - FuriHalUsbInterface* if_new = NULL; - FuriHalUsbInterface* if_ctx_new = NULL; - - if(usb.if_next != NULL) { - furi_thread_flags_set(furi_thread_get_id(usb.thread), EventModeChange); - } - - while(true) { - uint32_t flags = furi_thread_flags_wait(USB_SRV_ALL_EVENTS, FuriFlagWaitAny, 500); - if((flags & FuriFlagError) == 0) { - if(flags & EventModeChange) { - if(usb.if_next != usb.if_cur) { - if_new = usb.if_next; - if_ctx_new = usb.if_ctx; - if(usb.enabled) { // Disable current interface - susp_evt(&udev, 0, 0); - usbd_connect(&udev, false); - usb.enabled = false; - furi_delay_ms(USB_RECONNECT_DELAY); - } - flags |= EventModeChangeStart; - } - } - if(flags & EventReinit) { - // Temporary disable callback to avoid getting false reset events - usbd_reg_event(&udev, usbd_evt_reset, NULL); - FURI_LOG_I(TAG, "USB Reinit"); - susp_evt(&udev, 0, 0); - usbd_connect(&udev, false); - usb.enabled = false; - - usbd_enable(&udev, false); - usbd_enable(&udev, true); - - if_new = usb.if_cur; - furi_delay_ms(USB_RECONNECT_DELAY); - flags |= EventModeChangeStart; - } - if(flags & EventModeChangeStart) { // Second stage of mode change process - if(usb.if_cur != NULL) { - usb.if_cur->deinit(&udev); - } - if(if_new != NULL) { - if_new->init(&udev, if_new, if_ctx_new); - usbd_reg_event(&udev, usbd_evt_reset, reset_evt); - FURI_LOG_I(TAG, "USB Mode change done"); - usb.enabled = true; - } - usb.if_cur = if_new; - } - if(flags & EventEnable) { - if((!usb.enabled) && (usb.if_cur != NULL)) { - usbd_connect(&udev, true); - usb.enabled = true; - FURI_LOG_I(TAG, "USB Enable"); - } - } - if(flags & EventDisable) { - if(usb.enabled) { - susp_evt(&udev, 0, 0); - usbd_connect(&udev, false); - usb.enabled = false; - usb_request_pending = false; - FURI_LOG_I(TAG, "USB Disable"); - } - } - if(flags & EventReset) { - if(usb.enabled) { - usb_request_pending = true; - usb_wait_time = 0; - } - } - if(flags & EventRequest) { - usb_request_pending = false; - } - } else if(usb_request_pending) { - usb_wait_time++; - if(usb_wait_time > 4) { - furi_hal_usb_reinit(); - usb_request_pending = false; - } - } - } - return 0; -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_cdc_i.h b/firmware/targets/f7/furi_hal/furi_hal_usb_cdc_i.h deleted file mode 100644 index 4ba150eb5a1..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_cdc_i.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include "usb_cdc.h" - -#define CDC_DATA_SZ 64 - -typedef struct { - void (*tx_ep_callback)(void* context); - void (*rx_ep_callback)(void* context); - void (*state_callback)(void* context, uint8_t state); - void (*ctrl_line_callback)(void* context, uint8_t state); - void (*config_callback)(void* context, struct usb_cdc_line_coding* config); -} CdcCallbacks; - -void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb, void* context); - -struct usb_cdc_line_coding* furi_hal_cdc_get_port_settings(uint8_t if_num); - -uint8_t furi_hal_cdc_get_ctrl_line_state(uint8_t if_num); - -void furi_hal_cdc_send(uint8_t if_num, uint8_t* buf, uint16_t len); - -int32_t furi_hal_cdc_receive(uint8_t if_num, uint8_t* buf, uint16_t max_len); diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c deleted file mode 100644 index a7253223b90..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c +++ /dev/null @@ -1,523 +0,0 @@ -#include "furi_hal_version.h" -#include "furi_hal_usb_i.h" -#include "furi_hal_usb.h" -#include "furi_hal_usb_hid.h" -#include - -#include "usb.h" -#include "usb_hid.h" - -#define HID_EP_IN 0x81 -#define HID_EP_OUT 0x01 -#define HID_EP_SZ 0x10 - -#define HID_KB_MAX_KEYS 6 -#define HID_CONSUMER_MAX_KEYS 2 - -#define HID_INTERVAL 2 - -#define HID_VID_DEFAULT 0x046D -#define HID_PID_DEFAULT 0xC529 - -struct HidIadDescriptor { - struct usb_iad_descriptor hid_iad; - struct usb_interface_descriptor hid; - struct usb_hid_descriptor hid_desc; - struct usb_endpoint_descriptor hid_ep_in; - struct usb_endpoint_descriptor hid_ep_out; -}; - -struct HidConfigDescriptor { - struct usb_config_descriptor config; - struct HidIadDescriptor iad_0; -} __attribute__((packed)); - -enum HidReportId { - ReportIdKeyboard = 1, - ReportIdMouse = 2, - ReportIdConsumer = 3, -}; - -/* HID report: keyboard+mouse */ -static const uint8_t hid_report_desc[] = { - HID_USAGE_PAGE(HID_PAGE_DESKTOP), - HID_USAGE(HID_DESKTOP_KEYBOARD), - HID_COLLECTION(HID_APPLICATION_COLLECTION), - HID_REPORT_ID(ReportIdKeyboard), - HID_USAGE_PAGE(HID_DESKTOP_KEYPAD), - HID_USAGE_MINIMUM(HID_KEYBOARD_L_CTRL), - HID_USAGE_MAXIMUM(HID_KEYBOARD_R_GUI), - HID_LOGICAL_MINIMUM(0), - HID_LOGICAL_MAXIMUM(1), - HID_REPORT_SIZE(1), - HID_REPORT_COUNT(8), - HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), - 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(HID_KB_MAX_KEYS), - HID_REPORT_SIZE(8), - HID_LOGICAL_MINIMUM(0), - HID_LOGICAL_MAXIMUM(101), - HID_USAGE_PAGE(HID_DESKTOP_KEYPAD), - HID_USAGE_MINIMUM(0), - HID_USAGE_MAXIMUM(101), - HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE), - HID_END_COLLECTION, - HID_USAGE_PAGE(HID_PAGE_DESKTOP), - HID_USAGE(HID_DESKTOP_MOUSE), - HID_COLLECTION(HID_APPLICATION_COLLECTION), - HID_USAGE(HID_DESKTOP_POINTER), - HID_COLLECTION(HID_PHYSICAL_COLLECTION), - HID_REPORT_ID(ReportIdMouse), - HID_USAGE_PAGE(HID_PAGE_BUTTON), - HID_USAGE_MINIMUM(1), - HID_USAGE_MAXIMUM(3), - HID_LOGICAL_MINIMUM(0), - HID_LOGICAL_MAXIMUM(1), - HID_REPORT_COUNT(3), - HID_REPORT_SIZE(1), - HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), - HID_REPORT_SIZE(1), - HID_REPORT_COUNT(5), - HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), - HID_USAGE_PAGE(HID_PAGE_DESKTOP), - HID_USAGE(HID_DESKTOP_X), - HID_USAGE(HID_DESKTOP_Y), - HID_USAGE(HID_DESKTOP_WHEEL), - HID_LOGICAL_MINIMUM(-127), - HID_LOGICAL_MAXIMUM(127), - HID_REPORT_SIZE(8), - HID_REPORT_COUNT(3), - HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE), - HID_END_COLLECTION, - HID_END_COLLECTION, - HID_USAGE_PAGE(HID_PAGE_CONSUMER), - HID_USAGE(HID_CONSUMER_CONTROL), - HID_COLLECTION(HID_APPLICATION_COLLECTION), - HID_REPORT_ID(ReportIdConsumer), - HID_LOGICAL_MINIMUM(0), - HID_RI_LOGICAL_MAXIMUM(16, 0x3FF), - HID_USAGE_MINIMUM(0), - HID_RI_USAGE_MAXIMUM(16, 0x3FF), - HID_REPORT_COUNT(HID_CONSUMER_MAX_KEYS), - HID_REPORT_SIZE(16), - HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE), - HID_END_COLLECTION, -}; - -/* Device descriptor */ -static struct usb_device_descriptor hid_device_desc = { - .bLength = sizeof(struct usb_device_descriptor), - .bDescriptorType = USB_DTYPE_DEVICE, - .bcdUSB = VERSION_BCD(2, 0, 0), - .bDeviceClass = USB_CLASS_IAD, - .bDeviceSubClass = USB_SUBCLASS_IAD, - .bDeviceProtocol = USB_PROTO_IAD, - .bMaxPacketSize0 = USB_EP0_SIZE, - .idVendor = HID_VID_DEFAULT, - .idProduct = HID_PID_DEFAULT, - .bcdDevice = VERSION_BCD(1, 0, 0), - .iManufacturer = 0, - .iProduct = 0, - .iSerialNumber = 0, - .bNumConfigurations = 1, -}; - -/* Device configuration descriptor */ -static const struct HidConfigDescriptor hid_cfg_desc = { - .config = - { - .bLength = sizeof(struct usb_config_descriptor), - .bDescriptorType = USB_DTYPE_CONFIGURATION, - .wTotalLength = sizeof(struct HidConfigDescriptor), - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = NO_DESCRIPTOR, - .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED, - .bMaxPower = USB_CFG_POWER_MA(100), - }, - .iad_0 = - { - .hid_iad = - { - .bLength = sizeof(struct usb_iad_descriptor), - .bDescriptorType = USB_DTYPE_INTERFASEASSOC, - .bFirstInterface = 0, - .bInterfaceCount = 1, - .bFunctionClass = USB_CLASS_PER_INTERFACE, - .bFunctionSubClass = USB_SUBCLASS_NONE, - .bFunctionProtocol = USB_PROTO_NONE, - .iFunction = NO_DESCRIPTOR, - }, - .hid = - { - .bLength = sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DTYPE_INTERFACE, - .bInterfaceNumber = 0, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_HID, - .bInterfaceSubClass = USB_HID_SUBCLASS_NONBOOT, - .bInterfaceProtocol = USB_HID_PROTO_NONBOOT, - .iInterface = NO_DESCRIPTOR, - }, - .hid_desc = - { - .bLength = sizeof(struct usb_hid_descriptor), - .bDescriptorType = USB_DTYPE_HID, - .bcdHID = VERSION_BCD(1, 0, 0), - .bCountryCode = USB_HID_COUNTRY_NONE, - .bNumDescriptors = 1, - .bDescriptorType0 = USB_DTYPE_HID_REPORT, - .wDescriptorLength0 = sizeof(hid_report_desc), - }, - .hid_ep_in = - { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = HID_EP_IN, - .bmAttributes = USB_EPTYPE_INTERRUPT, - .wMaxPacketSize = HID_EP_SZ, - .bInterval = HID_INTERVAL, - }, - .hid_ep_out = - { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = HID_EP_OUT, - .bmAttributes = USB_EPTYPE_INTERRUPT, - .wMaxPacketSize = HID_EP_SZ, - .bInterval = HID_INTERVAL, - }, - }, -}; - -struct HidReportMouse { - uint8_t report_id; - uint8_t btn; - int8_t x; - int8_t y; - int8_t wheel; -} __attribute__((packed)); - -struct HidReportKB { - uint8_t report_id; - uint8_t mods; - uint8_t reserved; - uint8_t btn[HID_KB_MAX_KEYS]; -} __attribute__((packed)); - -struct HidReportConsumer { - uint8_t report_id; - uint16_t btn[HID_CONSUMER_MAX_KEYS]; -} __attribute__((packed)); - -struct HidReportLED { - uint8_t report_id; - uint8_t led_state; -} __attribute__((packed)); - -static struct HidReport { - struct HidReportKB keyboard; - struct HidReportMouse mouse; - struct HidReportConsumer consumer; -} __attribute__((packed)) hid_report; - -static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx); -static void hid_deinit(usbd_device* dev); -static void hid_on_wakeup(usbd_device* dev); -static void hid_on_suspend(usbd_device* dev); - -FuriHalUsbInterface usb_hid = { - .init = hid_init, - .deinit = hid_deinit, - .wakeup = hid_on_wakeup, - .suspend = hid_on_suspend, - - .dev_descr = (struct usb_device_descriptor*)&hid_device_desc, - - .str_manuf_descr = NULL, - .str_prod_descr = NULL, - .str_serial_descr = NULL, - - .cfg_descr = (void*)&hid_cfg_desc, -}; - -static bool hid_send_report(uint8_t report_id); -static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg); -static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback); -static usbd_device* usb_dev; -static FuriSemaphore* hid_semaphore = NULL; -static bool hid_connected = false; -static HidStateCallback callback; -static void* cb_ctx; -static uint8_t led_state; - -bool furi_hal_hid_is_connected() { - return hid_connected; -} - -uint8_t furi_hal_hid_get_led_state() { - return led_state; -} - -void furi_hal_hid_set_state_callback(HidStateCallback cb, void* ctx) { - if(callback != NULL) { - if(hid_connected == true) callback(false, cb_ctx); - } - - callback = cb; - cb_ctx = ctx; - - if(callback != NULL) { - if(hid_connected == true) callback(true, cb_ctx); - } -} - -bool furi_hal_hid_kb_press(uint16_t button) { - for(uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) { - if(hid_report.keyboard.btn[key_nb] == 0) { - hid_report.keyboard.btn[key_nb] = button & 0xFF; - break; - } - } - hid_report.keyboard.mods |= (button >> 8); - return hid_send_report(ReportIdKeyboard); -} - -bool furi_hal_hid_kb_release(uint16_t button) { - for(uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) { - if(hid_report.keyboard.btn[key_nb] == (button & 0xFF)) { - hid_report.keyboard.btn[key_nb] = 0; - break; - } - } - hid_report.keyboard.mods &= ~(button >> 8); - return hid_send_report(ReportIdKeyboard); -} - -bool furi_hal_hid_kb_release_all() { - for(uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) { - hid_report.keyboard.btn[key_nb] = 0; - } - hid_report.keyboard.mods = 0; - return hid_send_report(ReportIdKeyboard); -} - -bool furi_hal_hid_mouse_move(int8_t dx, int8_t dy) { - hid_report.mouse.x = dx; - hid_report.mouse.y = dy; - bool state = hid_send_report(ReportIdMouse); - hid_report.mouse.x = 0; - hid_report.mouse.y = 0; - return state; -} - -bool furi_hal_hid_mouse_press(uint8_t button) { - hid_report.mouse.btn |= button; - return hid_send_report(ReportIdMouse); -} - -bool furi_hal_hid_mouse_release(uint8_t button) { - hid_report.mouse.btn &= ~button; - return hid_send_report(ReportIdMouse); -} - -bool furi_hal_hid_mouse_scroll(int8_t delta) { - hid_report.mouse.wheel = delta; - bool state = hid_send_report(ReportIdMouse); - hid_report.mouse.wheel = 0; - return state; -} - -bool furi_hal_hid_consumer_key_press(uint16_t button) { - for(uint8_t key_nb = 0; key_nb < HID_CONSUMER_MAX_KEYS; key_nb++) { - if(hid_report.consumer.btn[key_nb] == 0) { - hid_report.consumer.btn[key_nb] = button; - break; - } - } - return hid_send_report(ReportIdConsumer); -} - -bool furi_hal_hid_consumer_key_release(uint16_t button) { - for(uint8_t key_nb = 0; key_nb < HID_CONSUMER_MAX_KEYS; key_nb++) { - if(hid_report.consumer.btn[key_nb] == button) { - hid_report.consumer.btn[key_nb] = 0; - break; - } - } - return hid_send_report(ReportIdConsumer); -} - -static void* hid_set_string_descr(char* str) { - furi_assert(str); - - uint8_t len = strlen(str); - struct usb_string_descriptor* dev_str_desc = malloc(len * 2 + 2); - dev_str_desc->bLength = len * 2 + 2; - dev_str_desc->bDescriptorType = USB_DTYPE_STRING; - for(uint8_t i = 0; i < len; i++) dev_str_desc->wString[i] = str[i]; - - return dev_str_desc; -} - -static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { - UNUSED(intf); - FuriHalUsbHidConfig* cfg = (FuriHalUsbHidConfig*)ctx; - if(hid_semaphore == NULL) hid_semaphore = furi_semaphore_alloc(1, 1); - usb_dev = dev; - hid_report.keyboard.report_id = ReportIdKeyboard; - hid_report.mouse.report_id = ReportIdMouse; - hid_report.consumer.report_id = ReportIdConsumer; - - usb_hid.dev_descr->iManufacturer = 0; - usb_hid.dev_descr->iProduct = 0; - usb_hid.str_manuf_descr = NULL; - usb_hid.str_prod_descr = NULL; - usb_hid.dev_descr->idVendor = HID_VID_DEFAULT; - usb_hid.dev_descr->idProduct = HID_PID_DEFAULT; - - if(cfg != NULL) { - usb_hid.dev_descr->idVendor = cfg->vid; - usb_hid.dev_descr->idProduct = cfg->pid; - - if(cfg->manuf[0] != '\0') { - usb_hid.str_manuf_descr = hid_set_string_descr(cfg->manuf); - usb_hid.dev_descr->iManufacturer = UsbDevManuf; - } - - if(cfg->product[0] != '\0') { - usb_hid.str_prod_descr = hid_set_string_descr(cfg->product); - usb_hid.dev_descr->iProduct = UsbDevProduct; - } - } - - usbd_reg_config(dev, hid_ep_config); - usbd_reg_control(dev, hid_control); - - usbd_connect(dev, true); -} - -static void hid_deinit(usbd_device* dev) { - usbd_reg_config(dev, NULL); - usbd_reg_control(dev, NULL); - - free(usb_hid.str_manuf_descr); - free(usb_hid.str_prod_descr); -} - -static void hid_on_wakeup(usbd_device* dev) { - UNUSED(dev); - if(!hid_connected) { - hid_connected = true; - if(callback != NULL) { - callback(true, cb_ctx); - } - } -} - -static void hid_on_suspend(usbd_device* dev) { - UNUSED(dev); - if(hid_connected) { - hid_connected = false; - furi_semaphore_release(hid_semaphore); - if(callback != NULL) { - callback(false, cb_ctx); - } - } -} - -static bool hid_send_report(uint8_t report_id) { - if((hid_semaphore == NULL) || (hid_connected == false)) return false; - - furi_check(furi_semaphore_acquire(hid_semaphore, FuriWaitForever) == FuriStatusOk); - if(hid_connected == true) { - if(report_id == ReportIdKeyboard) - usbd_ep_write(usb_dev, HID_EP_IN, &hid_report.keyboard, sizeof(hid_report.keyboard)); - else if(report_id == ReportIdMouse) - usbd_ep_write(usb_dev, HID_EP_IN, &hid_report.mouse, sizeof(hid_report.mouse)); - else if(report_id == ReportIdConsumer) - usbd_ep_write(usb_dev, HID_EP_IN, &hid_report.consumer, sizeof(hid_report.consumer)); - return true; - } - return false; -} - -static void hid_txrx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { - UNUSED(dev); - if(event == usbd_evt_eptx) { - furi_semaphore_release(hid_semaphore); - } else { - struct HidReportLED leds; - usbd_ep_read(usb_dev, ep, &leds, 2); - led_state = leds.led_state; - } -} - -/* Configure endpoints */ -static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg) { - switch(cfg) { - case 0: - /* deconfiguring device */ - usbd_ep_deconfig(dev, HID_EP_OUT); - usbd_ep_deconfig(dev, HID_EP_IN); - usbd_reg_endpoint(dev, HID_EP_OUT, 0); - usbd_reg_endpoint(dev, HID_EP_IN, 0); - return usbd_ack; - case 1: - /* configuring device */ - usbd_ep_config(dev, HID_EP_IN, USB_EPTYPE_INTERRUPT, HID_EP_SZ); - usbd_ep_config(dev, HID_EP_OUT, USB_EPTYPE_INTERRUPT, HID_EP_SZ); - usbd_reg_endpoint(dev, HID_EP_IN, hid_txrx_ep_callback); - usbd_reg_endpoint(dev, HID_EP_OUT, hid_txrx_ep_callback); - usbd_ep_write(dev, HID_EP_IN, 0, 0); - return usbd_ack; - default: - return usbd_fail; - } -} - -/* Control requests handler */ -static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { - UNUSED(callback); - /* HID control requests */ - if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == - (USB_REQ_INTERFACE | USB_REQ_CLASS) && - req->wIndex == 0) { - switch(req->bRequest) { - case USB_HID_SETIDLE: - return usbd_ack; - case USB_HID_GETREPORT: - dev->status.data_ptr = &hid_report; - dev->status.data_count = sizeof(hid_report); - return usbd_ack; - default: - return usbd_fail; - } - } - if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == - (USB_REQ_INTERFACE | USB_REQ_STANDARD) && - req->wIndex == 0 && req->bRequest == USB_STD_GET_DESCRIPTOR) { - switch(req->wValue >> 8) { - case USB_DTYPE_HID: - dev->status.data_ptr = (uint8_t*)&(hid_cfg_desc.iad_0.hid_desc); - dev->status.data_count = sizeof(hid_cfg_desc.iad_0.hid_desc); - return usbd_ack; - case USB_DTYPE_HID_REPORT: - dev->status.data_ptr = (uint8_t*)hid_report_desc; - dev->status.data_count = sizeof(hid_report_desc); - return usbd_ack; - default: - return usbd_fail; - } - } - return usbd_fail; -} diff --git a/firmware/targets/f7/furi_hal/furi_hal_vibro.c b/firmware/targets/f7/furi_hal/furi_hal_vibro.c deleted file mode 100644 index 4315ea63759..00000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_vibro.c +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include - -#define TAG "FuriHalVibro" - -void furi_hal_vibro_init() { - furi_hal_gpio_init(&vibro_gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&vibro_gpio, false); - FURI_LOG_I(TAG, "Init OK"); -} - -void furi_hal_vibro_on(bool value) { - furi_hal_gpio_write(&vibro_gpio, value); -} diff --git a/firmware/targets/furi_hal_include/furi_hal.h b/firmware/targets/furi_hal_include/furi_hal.h deleted file mode 100644 index 2a372a6c3e6..00000000000 --- a/firmware/targets/furi_hal_include/furi_hal.h +++ /dev/null @@ -1,65 +0,0 @@ -/** - * @file furi_hal.h - * Furi HAL API - */ - -#pragma once - -#ifdef __cplusplus -template struct STOP_EXTERNING_ME {}; -#endif - -#include "furi_hal_cortex.h" -#include "furi_hal_clock.h" -#include "furi_hal_crypto.h" -#include "furi_hal_console.h" -#include "furi_hal_debug.h" -#include "furi_hal_os.h" -#include "furi_hal_sd.h" -#include "furi_hal_i2c.h" -#include "furi_hal_resources.h" -#include "furi_hal_region.h" -#include "furi_hal_rtc.h" -#include "furi_hal_speaker.h" -#include "furi_hal_gpio.h" -#include "furi_hal_light.h" -#include "furi_hal_power.h" -#include "furi_hal_interrupt.h" -#include "furi_hal_version.h" -#include "furi_hal_bt.h" -#include "furi_hal_spi.h" -#include "furi_hal_flash.h" -#include "furi_hal_subghz.h" -#include "furi_hal_vibro.h" -#include "furi_hal_ibutton.h" -#include "furi_hal_rfid.h" -#include "furi_hal_nfc.h" -#include "furi_hal_usb.h" -#include "furi_hal_usb_hid.h" -#include "furi_hal_compress.h" -#include "furi_hal_uart.h" -#include "furi_hal_info.h" -#include "furi_hal_random.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** Early FuriHal init, only essential subsystems */ -void furi_hal_init_early(); - -/** Early FuriHal deinit */ -void furi_hal_deinit_early(); - -/** Init FuriHal */ -void furi_hal_init(); - -/** Transfer execution to address - * - * @param[in] address pointer to new executable - */ -void furi_hal_switch(void* address); - -#ifdef __cplusplus -} -#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h b/firmware/targets/furi_hal_include/furi_hal_bt_serial.h deleted file mode 100644 index 9cc4ba5bbc1..00000000000 --- a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include "serial_service.h" - -#define FURI_HAL_BT_SERIAL_PACKET_SIZE_MAX SERIAL_SVC_DATA_LEN_MAX - -/** Serial service callback type */ -typedef SerialServiceEventCallback FuriHalBtSerialCallback; - -/** Start Serial Profile - */ -void furi_hal_bt_serial_start(); - -/** Stop Serial Profile - */ -void furi_hal_bt_serial_stop(); - -/** Set Serial service events callback - * - * @param buffer_size Applicaition buffer size - * @param calback FuriHalBtSerialCallback instance - * @param context pointer to context - */ -void furi_hal_bt_serial_set_event_callback( - uint16_t buff_size, - FuriHalBtSerialCallback callback, - void* context); - -/** Notify that application buffer is empty - */ -void furi_hal_bt_serial_notify_buffer_is_empty(); - -/** Send data through BLE - * - * @param data data buffer - * @param size data buffer size - * - * @return true on success - */ -bool furi_hal_bt_serial_tx(uint8_t* data, uint16_t size); diff --git a/firmware/targets/furi_hal_include/furi_hal_compress.h b/firmware/targets/furi_hal_include/furi_hal_compress.h deleted file mode 100644 index 17ce3e69139..00000000000 --- a/firmware/targets/furi_hal_include/furi_hal_compress.h +++ /dev/null @@ -1,79 +0,0 @@ -/** - * @file furi_hal_compress.h - * LZSS based compression HAL API - */ -#pragma once - -#include -#include -#include - -/** Defines encoder and decoder window size */ -#define FURI_HAL_COMPRESS_EXP_BUFF_SIZE_LOG (8) - -/** Defines encoder and decoder lookahead buffer size */ -#define FURI_HAL_COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG (4) - -/** FuriHalCompress control structure */ -typedef struct FuriHalCompress FuriHalCompress; - -/** Initialize icon decoder - */ -void furi_hal_compress_icon_init(); - -/** Icon decoder - * - * @param icon_data pointer to icon data - * @param decoded_buff pointer to decoded buffer - */ -void furi_hal_compress_icon_decode(const uint8_t* icon_data, uint8_t** decoded_buff); - -/** Allocate encoder and decoder - * - * @param compress_buff_size size of decoder and encoder buffer to allocate - * - * @return FuriHalCompress instance - */ -FuriHalCompress* furi_hal_compress_alloc(uint16_t compress_buff_size); - -/** Free encoder and decoder - * - * @param compress FuriHalCompress instance - */ -void furi_hal_compress_free(FuriHalCompress* compress); - -/** Encode data - * - * @param compress FuriHalCompress instance - * @param data_in pointer to input data - * @param data_in_size size of input data - * @param data_out maximum size of output data - * @param data_res_size pointer to result output data size - * - * @return true on success - */ -bool furi_hal_compress_encode( - FuriHalCompress* compress, - uint8_t* data_in, - size_t data_in_size, - uint8_t* data_out, - size_t data_out_size, - size_t* data_res_size); - -/** Decode data - * - * @param compress FuriHalCompress instance - * @param data_in pointer to input data - * @param data_in_size size of input data - * @param data_out maximum size of output data - * @param data_res_size pointer to result output data size - * - * @return true on success - */ -bool furi_hal_compress_decode( - FuriHalCompress* compress, - uint8_t* data_in, - size_t data_in_size, - uint8_t* data_out, - size_t data_out_size, - size_t* data_res_size); diff --git a/firmware/targets/furi_hal_include/furi_hal_cortex.h b/firmware/targets/furi_hal_include/furi_hal_cortex.h deleted file mode 100644 index 13035161de7..00000000000 --- a/firmware/targets/furi_hal_include/furi_hal_cortex.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @file furi_hal_cortex.h - * ARM Cortex HAL - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Early init stage for cortex - */ -void furi_hal_cortex_init_early(); - -/** Microseconds delay - * - * @param[in] microseconds The microseconds to wait - */ -void furi_hal_cortex_delay_us(uint32_t microseconds); - -/** Get instructions per microsecond count - * - * @return instructions per microsecond count - */ -uint32_t furi_hal_cortex_instructions_per_microsecond(); - -#ifdef __cplusplus -} -#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_crypto.h b/firmware/targets/furi_hal_include/furi_hal_crypto.h deleted file mode 100644 index fc3974bbe45..00000000000 --- a/firmware/targets/furi_hal_include/furi_hal_crypto.h +++ /dev/null @@ -1,84 +0,0 @@ -/** - * @file furi_hal_crypto.h - * Cryptography HAL API - */ -#pragma once - -#include -#include -#include - -/** FuriHalCryptoKey Type */ -typedef enum { - FuriHalCryptoKeyTypeMaster, /**< Master key */ - FuriHalCryptoKeyTypeSimple, /**< Simple enencrypted key */ - FuriHalCryptoKeyTypeEncrypted, /**< Encrypted with Master key */ -} FuriHalCryptoKeyType; - -/** FuriHalCryptoKey Size in bits */ -typedef enum { - FuriHalCryptoKeySize128, - FuriHalCryptoKeySize256, -} FuriHalCryptoKeySize; - -/** FuriHalCryptoKey */ -typedef struct { - FuriHalCryptoKeyType type; - FuriHalCryptoKeySize size; - uint8_t* data; -} FuriHalCryptoKey; - -/** Initialize cryptography layer This includes AES engines, PKA and RNG - */ -void furi_hal_crypto_init(); - -bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb); - -bool furi_hal_crypto_verify_key(uint8_t key_slot); - -/** Store key in crypto storage - * - * @param key FuriHalCryptoKey to store. Only Master, Simple or - * Encrypted - * @param slot pinter to int where store slot number will be saved - * - * @return true on success - */ -bool furi_hal_crypto_store_add_key(FuriHalCryptoKey* key, uint8_t* slot); - -/** Init AES engine and load key from crypto store - * - * @param slot store slot number - * @param[in] iv pointer to 16 bytes Initialization Vector data - * - * @return true on success - */ -bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv); - -/** Unload key engine and deinit AES engine - * - * @param slot store slot number - * - * @return true on success - */ -bool furi_hal_crypto_store_unload_key(uint8_t slot); - -/** Encrypt data - * - * @param input pointer to input data - * @param output pointer to output data - * @param size input/output buffer size in bytes - * - * @return true on success - */ -bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size); - -/** Decrypt data - * - * @param input pointer to input data - * @param output pointer to output data - * @param size input/output buffer size in bytes - * - * @return true on success - */ -bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size); diff --git a/firmware/targets/furi_hal_include/furi_hal_i2c.h b/firmware/targets/furi_hal_include/furi_hal_i2c.h deleted file mode 100644 index 566574ab826..00000000000 --- a/firmware/targets/furi_hal_include/furi_hal_i2c.h +++ /dev/null @@ -1,210 +0,0 @@ -/** - * @file furi_hal_i2c.h - * I2C HAL API - */ - -#pragma once - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Early Init I2C */ -void furi_hal_i2c_init_early(); - -/** Early DeInit I2C */ -void furi_hal_i2c_deinit_early(); - -/** Init I2C */ -void furi_hal_i2c_init(); - -/** Acquire i2c bus handle - * - * @return Instance of FuriHalI2cBus - */ -void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle); - -/** Release i2c bus handle - * - * @param bus instance of FuriHalI2cBus aquired in `furi_hal_i2c_acquire` - */ -void furi_hal_i2c_release(FuriHalI2cBusHandle* handle); - -/** Perform I2C tx transfer - * - * @param handle pointer to FuriHalI2cBusHandle instance - * @param address I2C slave address - * @param data pointer to data buffer - * @param size size of data buffer - * @param timeout timeout in ticks - * - * @return true on successful transfer, false otherwise - */ -bool furi_hal_i2c_tx( - FuriHalI2cBusHandle* handle, - const uint8_t address, - const uint8_t* data, - const uint8_t size, - uint32_t timeout); - -/** Perform I2C rx transfer - * - * @param handle pointer to FuriHalI2cBusHandle instance - * @param address I2C slave address - * @param data pointer to data buffer - * @param size size of data buffer - * @param timeout timeout in ticks - * - * @return true on successful transfer, false otherwise - */ -bool furi_hal_i2c_rx( - FuriHalI2cBusHandle* handle, - const uint8_t address, - uint8_t* data, - const uint8_t size, - uint32_t timeout); - -/** Perform I2C tx and rx transfers - * - * @param handle pointer to FuriHalI2cBusHandle instance - * @param address I2C slave address - * @param tx_data pointer to tx data buffer - * @param tx_size size of tx data buffer - * @param rx_data pointer to rx data buffer - * @param rx_size size of rx data buffer - * @param timeout timeout in ticks - * - * @return true on successful transfer, false otherwise - */ -bool furi_hal_i2c_trx( - FuriHalI2cBusHandle* handle, - const uint8_t address, - const uint8_t* tx_data, - const uint8_t tx_size, - uint8_t* rx_data, - const uint8_t rx_size, - uint32_t timeout); - -/** Check if I2C device presents on bus - * - * @param handle pointer to FuriHalI2cBusHandle instance - * @param i2c_addr I2C slave address - * @param timeout timeout in ticks - * - * @return true if device present and is ready, false otherwise - */ -bool furi_hal_i2c_is_device_ready(FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint32_t timeout); - -/** Perform I2C device register read (8-bit) - * - * @param handle pointer to FuriHalI2cBusHandle instance - * @param i2c_addr I2C slave address - * @param reg_addr register address - * @param data pointer to register value - * @param timeout timeout in ticks - * - * @return true on successful transfer, false otherwise - */ -bool furi_hal_i2c_read_reg_8( - FuriHalI2cBusHandle* handle, - uint8_t i2c_addr, - uint8_t reg_addr, - uint8_t* data, - uint32_t timeout); - -/** Perform I2C device register read (16-bit) - * - * @param handle pointer to FuriHalI2cBusHandle instance - * @param i2c_addr I2C slave address - * @param reg_addr register address - * @param data pointer to register value - * @param timeout timeout in ticks - * - * @return true on successful transfer, false otherwise - */ -bool furi_hal_i2c_read_reg_16( - FuriHalI2cBusHandle* handle, - uint8_t i2c_addr, - uint8_t reg_addr, - uint16_t* data, - uint32_t timeout); - -/** Perform I2C device memory read - * - * @param handle pointer to FuriHalI2cBusHandle instance - * @param i2c_addr I2C slave address - * @param mem_addr memory start address - * @param data pointer to data buffer - * @param len size of data buffer - * @param timeout timeout in ticks - * - * @return true on successful transfer, false otherwise - */ -bool furi_hal_i2c_read_mem( - FuriHalI2cBusHandle* handle, - uint8_t i2c_addr, - uint8_t mem_addr, - uint8_t* data, - uint8_t len, - uint32_t timeout); - -/** Perform I2C device register write (8-bit) - * - * @param handle pointer to FuriHalI2cBusHandle instance - * @param i2c_addr I2C slave address - * @param reg_addr register address - * @param data register value - * @param timeout timeout in ticks - * - * @return true on successful transfer, false otherwise - */ -bool furi_hal_i2c_write_reg_8( - FuriHalI2cBusHandle* handle, - uint8_t i2c_addr, - uint8_t reg_addr, - uint8_t data, - uint32_t timeout); - -/** Perform I2C device register write (16-bit) - * - * @param handle pointer to FuriHalI2cBusHandle instance - * @param i2c_addr I2C slave address - * @param reg_addr register address - * @param data register value - * @param timeout timeout in ticks - * - * @return true on successful transfer, false otherwise - */ -bool furi_hal_i2c_write_reg_16( - FuriHalI2cBusHandle* handle, - uint8_t i2c_addr, - uint8_t reg_addr, - uint16_t data, - uint32_t timeout); - -/** Perform I2C device memory - * - * @param handle pointer to FuriHalI2cBusHandle instance - * @param i2c_addr I2C slave address - * @param mem_addr memory start address - * @param data pointer to data buffer - * @param len size of data buffer - * @param timeout timeout in ticks - * - * @return true on successful transfer, false otherwise - */ -bool furi_hal_i2c_write_mem( - FuriHalI2cBusHandle* handle, - uint8_t i2c_addr, - uint8_t mem_addr, - uint8_t* data, - uint8_t len, - uint32_t timeout); - -#ifdef __cplusplus -} -#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_ibutton.h b/firmware/targets/furi_hal_include/furi_hal_ibutton.h deleted file mode 100644 index 84ef0cd6a49..00000000000 --- a/firmware/targets/furi_hal_include/furi_hal_ibutton.h +++ /dev/null @@ -1,88 +0,0 @@ -/** - * @file furi_hal_ibutton.h - * iButton HAL API - */ - -#pragma once - -#include -#include -#include "furi_hal_gpio.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void (*FuriHalIbuttonEmulateCallback)(void* context); - -/** Initialize */ -void furi_hal_ibutton_init(); - -void furi_hal_ibutton_emulate_start( - uint32_t period, - FuriHalIbuttonEmulateCallback callback, - void* context); - -void furi_hal_ibutton_emulate_set_next(uint32_t period); - -void furi_hal_ibutton_emulate_stop(); - -/** - * Sets 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(); - -/** - * Sets the pin to analog mode, and sets it to float - */ -void furi_hal_ibutton_stop(); - -/** - * Attach interrupt callback to iButton pin - * @param cb callback - * @param context context - */ -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(); - -#ifdef __cplusplus -} -#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_info.h b/firmware/targets/furi_hal_include/furi_hal_info.h deleted file mode 100644 index 4a335f2afc2..00000000000 --- a/firmware/targets/furi_hal_include/furi_hal_info.h +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @file furi_hal_info.h - * Device info HAL API - */ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Callback type called every time another key-value pair of device information is ready - * - * @param key[in] device information type identifier - * @param value[in] device information value - * @param last[in] whether the passed key-value pair is the last one - * @param context[in] to pass to callback - */ -typedef void ( - *FuriHalInfoValueCallback)(const char* key, const char* value, bool last, void* context); - -/** Get device information - * - * @param[in] callback callback to provide with new data - * @param[in] context context to pass to callback - */ -void furi_hal_info_get(FuriHalInfoValueCallback callback, void* context); - -#ifdef __cplusplus -} -#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_nfc.h b/firmware/targets/furi_hal_include/furi_hal_nfc.h deleted file mode 100644 index 71186076f63..00000000000 --- a/firmware/targets/furi_hal_include/furi_hal_nfc.h +++ /dev/null @@ -1,232 +0,0 @@ -/** - * @file furi_hal_nfc.h - * NFC HAL API - */ - -#pragma once - -#include -#include -#include -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define FURI_HAL_NFC_UID_MAX_LEN 10 -#define FURI_HAL_NFC_DATA_BUFF_SIZE (512) -#define FURI_HAL_NFC_PARITY_BUFF_SIZE (FURI_HAL_NFC_DATA_BUFF_SIZE / 8) - -#define FURI_HAL_NFC_TXRX_DEFAULT \ - ((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_AUTO | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_REMV | \ - (uint32_t)RFAL_TXRX_FLAGS_PAR_RX_REMV | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_AUTO) - -#define FURI_HAL_NFC_TX_DEFAULT_RX_NO_CRC \ - ((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_AUTO | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP | \ - (uint32_t)RFAL_TXRX_FLAGS_PAR_RX_REMV | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_AUTO) - -#define FURI_HAL_NFC_TXRX_WITH_PAR \ - ((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP | \ - (uint32_t)RFAL_TXRX_FLAGS_PAR_RX_KEEP | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_AUTO) - -#define FURI_HAL_NFC_TXRX_RAW \ - ((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP | \ - (uint32_t)RFAL_TXRX_FLAGS_PAR_RX_KEEP | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_NONE) - -#define FURI_HAL_NFC_TX_RAW_RX_DEFAULT \ - ((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_REMV | \ - (uint32_t)RFAL_TXRX_FLAGS_PAR_RX_REMV | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_NONE) - -typedef enum { - FuriHalNfcTxRxTypeDefault, - FuriHalNfcTxRxTypeRxNoCrc, - FuriHalNfcTxRxTypeRxKeepPar, - FuriHalNfcTxRxTypeRaw, - FuriHalNfcTxRxTypeRxRaw, - FuriHalNfcTxRxTransparent, -} FuriHalNfcTxRxType; - -typedef bool (*FuriHalNfcEmulateCallback)( - uint8_t* buff_rx, - uint16_t buff_rx_len, - uint8_t* buff_tx, - uint16_t* buff_tx_len, - uint32_t* flags, - void* context); - -typedef enum { - FuriHalNfcTypeA, - FuriHalNfcTypeB, - FuriHalNfcTypeF, - FuriHalNfcTypeV, -} FuriHalNfcType; - -typedef enum { - FuriHalNfcInterfaceRf, - FuriHalNfcInterfaceIsoDep, - FuriHalNfcInterfaceNfcDep, -} FuriHalNfcInterface; - -typedef struct { - FuriHalNfcType type; - FuriHalNfcInterface interface; - uint8_t uid_len; - uint8_t uid[10]; - uint32_t cuid; - uint8_t atqa[2]; - uint8_t sak; -} FuriHalNfcDevData; - -typedef void ( - *FuriHalNfcTxRxSniffCallback)(uint8_t* data, uint16_t bits, bool crc_dropped, void* context); - -typedef struct { - uint8_t tx_data[FURI_HAL_NFC_DATA_BUFF_SIZE]; - uint8_t tx_parity[FURI_HAL_NFC_PARITY_BUFF_SIZE]; - uint16_t tx_bits; - uint8_t rx_data[FURI_HAL_NFC_DATA_BUFF_SIZE]; - uint8_t rx_parity[FURI_HAL_NFC_PARITY_BUFF_SIZE]; - uint16_t rx_bits; - FuriHalNfcTxRxType tx_rx_type; - NfcaSignal* nfca_signal; - - FuriHalNfcTxRxSniffCallback sniff_tx; - FuriHalNfcTxRxSniffCallback sniff_rx; - void* sniff_context; -} FuriHalNfcTxRxContext; - -/** Init nfc - */ -void furi_hal_nfc_init(); - -/** Check if nfc worker is busy - * - * @return true if busy - */ -bool furi_hal_nfc_is_busy(); - -/** NFC field on - */ -void furi_hal_nfc_field_on(); - -/** NFC field off - */ -void furi_hal_nfc_field_off(); - -/** NFC start sleep - */ -void furi_hal_nfc_start_sleep(); - -/** NFC stop sleep - */ -void furi_hal_nfc_exit_sleep(); - -/** NFC poll - * - * @param dev_list pointer to rfalNfcDevice buffer - * @param dev_cnt pointer device count - * @param timeout timeout in ms - * @param deactivate deactivate flag - * - * @return true on success - */ -bool furi_hal_nfc_detect(FuriHalNfcDevData* nfc_data, uint32_t timeout); - -/** Activate NFC-A tag - * - * @param timeout timeout in ms - * @param cuid pointer to 32bit uid - * - * @return true on succeess - */ -bool furi_hal_nfc_activate_nfca(uint32_t timeout, uint32_t* cuid); - -/** NFC listen - * - * @param uid pointer to uid buffer - * @param uid_len uid length - * @param atqa pointer to atqa - * @param sak sak - * @param activate_after_sak activate after sak flag - * @param timeout timeout in ms - * - * @return true on success - */ -bool furi_hal_nfc_listen( - uint8_t* uid, - uint8_t uid_len, - uint8_t* atqa, - uint8_t sak, - bool activate_after_sak, - uint32_t timeout); - -/** Start Target Listen mode - * @note RFAL free implementation - * - * @param nfc_data FuriHalNfcDevData instance - */ -void furi_hal_nfc_listen_start(FuriHalNfcDevData* nfc_data); - -/** Read data in Target Listen mode - * @note Must be called only after furi_hal_nfc_listen_start() - * - * @param tx_rx FuriHalNfcTxRxContext instance - * @param timeout_ms timeout im ms - * - * @return true on not empty receive - */ -bool furi_hal_nfc_listen_rx(FuriHalNfcTxRxContext* tx_rx, uint32_t timeout_ms); - -/** Set Target in Sleep state */ -void furi_hal_nfc_listen_sleep(); - -/** Emulate NFC-A Target - * @note RFAL based implementation - * - * @param uid NFC-A UID - * @param uid_len NFC-A UID length - * @param atqa NFC-A ATQA - * @param sak NFC-A SAK - * @param callback FuriHalNfcEmulateCallback instance - * @param context pointer to context for callback - * @param timeout timeout in ms - * - * @return true on success - */ -bool furi_hal_nfc_emulate_nfca( - uint8_t* uid, - uint8_t uid_len, - uint8_t* atqa, - uint8_t sak, - FuriHalNfcEmulateCallback callback, - void* context, - uint32_t timeout); - -/** NFC data exchange - * - * @param tx_rx_ctx FuriHalNfcTxRxContext instance - * - * @return true on success - */ -bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms); - -/** NFC data full exhange - * - * @param tx_rx_ctx FuriHalNfcTxRxContext instance - * - * @return true on success - */ -bool furi_hal_nfc_tx_rx_full(FuriHalNfcTxRxContext* tx_rx); - -/** NFC deactivate and start sleep - */ -void furi_hal_nfc_sleep(); - -void furi_hal_nfc_stop(); - -#ifdef __cplusplus -} -#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_rfid.h b/firmware/targets/furi_hal_include/furi_hal_rfid.h deleted file mode 100644 index d26ba53fee1..00000000000 --- a/firmware/targets/furi_hal_include/furi_hal_rfid.h +++ /dev/null @@ -1,118 +0,0 @@ -/** - * @file furi_hal_rfid.h - * RFID HAL API - */ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Initialize RFID subsystem - */ -void furi_hal_rfid_init(); - -/** Config rfid pins to reset state - */ -void furi_hal_rfid_pins_reset(); - -/** Config rfid pins to emulate state - */ -void furi_hal_rfid_pins_emulate(); - -/** Config rfid pins to read state - */ -void furi_hal_rfid_pins_read(); - -/** Release rfid pull pin - */ -void furi_hal_rfid_pin_pull_release(); - -/** Pulldown rfid pull pin - */ -void furi_hal_rfid_pin_pull_pulldown(); - -/** Config rfid timer to read state - * - * @param freq timer frequency - * @param duty_cycle timer duty cycle, 0.0-1.0 - */ -void furi_hal_rfid_tim_read(float freq, float duty_cycle); - -/** Start read timer - */ -void furi_hal_rfid_tim_read_start(); - -/** Stop read timer - */ -void furi_hal_rfid_tim_read_stop(); - -/** Config rfid timer to emulate state - * - * @param freq timer frequency - */ -void furi_hal_rfid_tim_emulate(float freq); - -typedef void (*FuriHalRfidEmulateCallback)(void* context); - -/** Start emulation timer - */ -void furi_hal_rfid_tim_emulate_start(FuriHalRfidEmulateCallback callback, void* context); - -/** Stop emulation timer - */ -void furi_hal_rfid_tim_emulate_stop(); - -/** Config rfid timers to reset state - */ -void furi_hal_rfid_tim_reset(); - -/** Set emulation timer period - * - * @param period overall duration - */ -void furi_hal_rfid_set_emulate_period(uint32_t period); - -/** Set emulation timer pulse - * - * @param pulse duration of high level - */ -void furi_hal_rfid_set_emulate_pulse(uint32_t pulse); - -/** Set read timer period - * - * @param period overall duration - */ -void furi_hal_rfid_set_read_period(uint32_t period); - -/** Set read timer pulse - * - * @param pulse duration of high level - */ -void furi_hal_rfid_set_read_pulse(uint32_t pulse); - -/** Сhanges the configuration of the RFID timer "on a fly" - * - * @param freq new frequency - * @param duty_cycle new duty cycle - */ -void furi_hal_rfid_change_read_config(float freq, float duty_cycle); - -/** Start/Enable comparator */ -void furi_hal_rfid_comp_start(); - -/** Stop/Disable comparator */ -void furi_hal_rfid_comp_stop(); - -typedef void (*FuriHalRfidCompCallback)(bool level, void* context); - -/** Set comparator callback */ -void furi_hal_rfid_comp_set_callback(FuriHalRfidCompCallback callback, void* context); - -#ifdef __cplusplus -} -#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_rtc.h b/firmware/targets/furi_hal_include/furi_hal_rtc.h deleted file mode 100644 index bdae3b93100..00000000000 --- a/firmware/targets/furi_hal_include/furi_hal_rtc.h +++ /dev/null @@ -1,100 +0,0 @@ -/** - * @file furi_hal_rtc.h - * Furi Hal RTC API - */ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - // Time - uint8_t hour; /**< Hour in 24H format: 0-23 */ - uint8_t minute; /**< Minute: 0-59 */ - uint8_t second; /**< Second: 0-59 */ - // Date - uint8_t day; /**< Current day: 1-31 */ - uint8_t month; /**< Current month: 1-12 */ - uint16_t year; /**< Current year: 2000-2099 */ - uint8_t weekday; /**< Current weekday: 1-7 */ -} FuriHalRtcDateTime; - -typedef enum { - FuriHalRtcFlagDebug = (1 << 0), - FuriHalRtcFlagFactoryReset = (1 << 1), - FuriHalRtcFlagLock = (1 << 2), - FuriHalRtcFlagC2Update = (1 << 3), -} FuriHalRtcFlag; - -typedef enum { - FuriHalRtcBootModeNormal = 0, /**< Normal boot mode, default value */ - FuriHalRtcBootModeDfu, /**< Boot to DFU (MCU bootloader by ST) */ - FuriHalRtcBootModePreUpdate, /**< Boot to Update, pre update */ - FuriHalRtcBootModeUpdate, /**< Boot to Update, main */ - FuriHalRtcBootModePostUpdate, /**< Boot to Update, post update */ -} FuriHalRtcBootMode; - -typedef enum { - FuriHalRtcRegisterHeader, /**< RTC structure header */ - FuriHalRtcRegisterSystem, /**< Various system bits */ - FuriHalRtcRegisterVersion, /**< Pointer to Version */ - FuriHalRtcRegisterLfsFingerprint, /**< LFS geometry fingerprint */ - FuriHalRtcRegisterFaultData, /**< Pointer to last fault message */ - FuriHalRtcRegisterPinFails, /**< Failed pins count */ - /* Index of FS directory entry corresponding to FW update to be applied */ - FuriHalRtcRegisterUpdateFolderFSIndex, - - FuriHalRtcRegisterMAX, /**< Service value, do not use */ -} FuriHalRtcRegister; - -/** Early initialization */ -void furi_hal_rtc_init_early(); - -/** Early deinitialization */ -void furi_hal_rtc_deinit_early(); - -/** Initialize RTC subsystem */ -void furi_hal_rtc_init(); - -uint32_t furi_hal_rtc_get_register(FuriHalRtcRegister reg); - -void furi_hal_rtc_set_register(FuriHalRtcRegister reg, uint32_t value); - -void furi_hal_rtc_set_log_level(uint8_t level); - -uint8_t furi_hal_rtc_get_log_level(); - -void furi_hal_rtc_set_flag(FuriHalRtcFlag flag); - -void furi_hal_rtc_reset_flag(FuriHalRtcFlag flag); - -bool furi_hal_rtc_is_flag_set(FuriHalRtcFlag flag); - -void furi_hal_rtc_set_boot_mode(FuriHalRtcBootMode mode); - -FuriHalRtcBootMode furi_hal_rtc_get_boot_mode(); - -void furi_hal_rtc_set_datetime(FuriHalRtcDateTime* datetime); - -void furi_hal_rtc_get_datetime(FuriHalRtcDateTime* datetime); - -bool furi_hal_rtc_validate_datetime(FuriHalRtcDateTime* datetime); - -void furi_hal_rtc_set_fault_data(uint32_t value); - -uint32_t furi_hal_rtc_get_fault_data(); - -void furi_hal_rtc_set_pin_fails(uint32_t value); - -uint32_t furi_hal_rtc_get_pin_fails(); - -uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime); - -#ifdef __cplusplus -} -#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_sd.h b/firmware/targets/furi_hal_include/furi_hal_sd.h deleted file mode 100644 index e1c08a35ca1..00000000000 --- a/firmware/targets/furi_hal_include/furi_hal_sd.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once -/** - * @file furi_hal_sd.h - * SD Card HAL API - */ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Init SD card detect - */ -void hal_sd_detect_init(void); - -/** Set SD card detect pin to low - */ -void hal_sd_detect_set_low(void); - -/** Get SD card status - * - * @return true if SD card present, false if SD card not present - */ -bool hal_sd_detect(void); - -/** Pointer to currently used SPI Handle */ -extern FuriHalSpiBusHandle* furi_hal_sd_spi_handle; - -#ifdef __cplusplus -} -#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_speaker.h b/firmware/targets/furi_hal_include/furi_hal_speaker.h deleted file mode 100644 index 67de41d9227..00000000000 --- a/firmware/targets/furi_hal_include/furi_hal_speaker.h +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @file furi_hal_speaker.h - * Speaker HAL - */ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -void furi_hal_speaker_init(); - -void furi_hal_speaker_start(float frequency, float volume); - -void furi_hal_speaker_set_volume(float volume); - -void furi_hal_speaker_stop(); - -#ifdef __cplusplus -} -#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_subghz.h b/firmware/targets/furi_hal_include/furi_hal_subghz.h deleted file mode 100644 index d610b01b7a7..00000000000 --- a/firmware/targets/furi_hal_include/furi_hal_subghz.h +++ /dev/null @@ -1,240 +0,0 @@ -/** - * @file furi_hal_subghz.h - * SubGhz HAL API - */ - -#pragma once - -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Radio Presets */ -typedef enum { - FuriHalSubGhzPresetIDLE, /**< default configuration */ - FuriHalSubGhzPresetOok270Async, /**< OOK, bandwidth 270kHz, asynchronous */ - FuriHalSubGhzPresetOok650Async, /**< OOK, bandwidth 650kHz, asynchronous */ - FuriHalSubGhzPreset2FSKDev238Async, /**< FM, deviation 2.380371 kHz, asynchronous */ - FuriHalSubGhzPreset2FSKDev476Async, /**< FM, deviation 47.60742 kHz, asynchronous */ - FuriHalSubGhzPresetMSK99_97KbAsync, /**< MSK, deviation 47.60742 kHz, 99.97Kb/s, asynchronous */ - FuriHalSubGhzPresetGFSK9_99KbAsync, /**< GFSK, deviation 19.042969 kHz, 9.996Kb/s, asynchronous */ - FuriHalSubGhzPresetCustom, /**Custom Preset*/ -} FuriHalSubGhzPreset; - -/** Switchable Radio Paths */ -typedef enum { - FuriHalSubGhzPathIsolate, /**< Isolate Radio from antenna */ - FuriHalSubGhzPath433, /**< Center Frquency: 433MHz. Path 1: SW1RF1-SW2RF2, LCLCL */ - FuriHalSubGhzPath315, /**< Center Frquency: 315MHz. Path 2: SW1RF2-SW2RF1, LCLCLCL */ - FuriHalSubGhzPath868, /**< Center Frquency: 868MHz. Path 3: SW1RF3-SW2RF3, LCLC */ -} FuriHalSubGhzPath; - -/** SubGhz state */ -typedef enum { - SubGhzStateInit, /**< Init pending */ - - SubGhzStateIdle, /**< Idle, energy save mode */ - - SubGhzStateAsyncRx, /**< Async RX started */ - - SubGhzStateAsyncTx, /**< Async TX started, DMA and timer is on */ - SubGhzStateAsyncTxLast, /**< Async TX continue, DMA completed and timer got last value to go */ - SubGhzStateAsyncTxEnd, /**< Async TX complete, cleanup needed */ - -} SubGhzState; - -/** SubGhz regulation, receive transmission on the current frequency for the - * region */ -typedef enum { - SubGhzRegulationOnlyRx, /**only Rx*/ - SubGhzRegulationTxRx, /**TxRx*/ -} SubGhzRegulation; - -/** Initialize and switch to power save mode Used by internal API-HAL - * initalization routine Can be used to reinitialize device to safe state and - * send it to sleep - */ -void furi_hal_subghz_init(); - -/** Send device to sleep mode - */ -void furi_hal_subghz_sleep(); - -/** Dump info to stdout - */ -void furi_hal_subghz_dump_state(); - -/** Load registers from preset by preset name - * - * @param preset to load - */ -void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset); - -/** Load custom registers from preset - * - * @param preset_data registers to load - */ -void furi_hal_subghz_load_custom_preset(uint8_t* preset_data); - -/** Load registers - * - * @param data Registers data - */ -void furi_hal_subghz_load_registers(uint8_t* data); - -/** Load PATABLE - * - * @param data 8 uint8_t values - */ -void furi_hal_subghz_load_patable(const uint8_t data[8]); - -/** Write packet to FIFO - * - * @param data bytes array - * @param size size - */ -void furi_hal_subghz_write_packet(const uint8_t* data, uint8_t size); - -/** Check if recieve pipe is not empty - * - * @return true if not empty - */ -bool furi_hal_subghz_rx_pipe_not_empty(); - -/** Check if recieved data crc is valid - * - * @return true if valid - */ -bool furi_hal_subghz_is_rx_data_crc_valid(); - -/** Read packet from FIFO - * - * @param data pointer - * @param size size - */ -void furi_hal_subghz_read_packet(uint8_t* data, uint8_t* size); - -/** Flush rx FIFO buffer - */ -void furi_hal_subghz_flush_rx(); - -/** Flush tx FIFO buffer - */ -void furi_hal_subghz_flush_tx(); - -/** Shutdown Issue spwd command - * @warning registers content will be lost - */ -void furi_hal_subghz_shutdown(); - -/** Reset Issue reset command - * @warning registers content will be lost - */ -void furi_hal_subghz_reset(); - -/** Switch to Idle - */ -void furi_hal_subghz_idle(); - -/** Switch to Recieve - */ -void furi_hal_subghz_rx(); - -/** Switch to Transmit - * - * @return true if the transfer is allowed by belonging to the region - */ -bool furi_hal_subghz_tx(); - -/** Get RSSI value in dBm - * - * @return RSSI value - */ -float furi_hal_subghz_get_rssi(); - -/** Get LQI - * - * @return LQI value - */ -uint8_t furi_hal_subghz_get_lqi(); - -/** Check if frequency is in valid range - * - * @param value frequency in Hz - * - * @return true if frequncy is valid, otherwise false - */ -bool furi_hal_subghz_is_frequency_valid(uint32_t value); - -/** Set frequency and path This function automatically selects antenna matching - * network - * - * @param value frequency in Hz - * - * @return real frequency in herz - */ -uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value); - -/** Set frequency - * - * @param value frequency in Hz - * - * @return real frequency in herz - */ -uint32_t furi_hal_subghz_set_frequency(uint32_t value); - -/** Set path - * - * @param path path to use - */ -void furi_hal_subghz_set_path(FuriHalSubGhzPath path); - -/* High Level API */ - -/** Signal Timings Capture callback */ -typedef void (*FuriHalSubGhzCaptureCallback)(bool level, uint32_t duration, void* context); - -/** Enable signal timings capture Initializes GPIO and TIM2 for timings capture - * - * @param callback FuriHalSubGhzCaptureCallback - * @param context callback context - */ -void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* context); - -/** Disable signal timings capture Resets GPIO and TIM2 - */ -void furi_hal_subghz_stop_async_rx(); - -/** Async TX callback type - * @param context callback context - * @return LevelDuration - */ -typedef LevelDuration (*FuriHalSubGhzAsyncTxCallback)(void* context); - -/** Start async TX Initializes GPIO, TIM2 and DMA1 for signal output - * - * @param callback FuriHalSubGhzAsyncTxCallback - * @param context callback context - * - * @return true if the transfer is allowed by belonging to the region - */ -bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context); - -/** Wait for async transmission to complete - * - * @return true if TX complete - */ -bool furi_hal_subghz_is_async_tx_complete(); - -/** Stop async transmission and cleanup resources Resets GPIO, TIM2, and DMA1 - */ -void furi_hal_subghz_stop_async_tx(); - -#ifdef __cplusplus -} -#endif diff --git a/furi/SConscript b/furi/SConscript index a751eb6e1bf..8f8caeb8771 100644 --- a/furi/SConscript +++ b/furi/SConscript @@ -1,6 +1,10 @@ Import("env") -env.Append(LINT_SOURCES=["furi"]) +env.Append( + LINT_SOURCES=[ + Dir("."), + ] +) libenv = env.Clone(FW_LIB_NAME="furi") diff --git a/furi/core/base.h b/furi/core/base.h index 29e87419200..642ff2b6cda 100644 --- a/furi/core/base.h +++ b/furi/core/base.h @@ -2,6 +2,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { diff --git a/furi/core/check.c b/furi/core/check.c index 3d0cd7a0463..b56db656379 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -1,18 +1,60 @@ #include "check.h" #include "common_defines.h" +#include #include #include #include +#include +#include #include #include #include #include +#include + +PLACE_IN_SECTION("MB_MEM2") const char* __furi_check_message = NULL; +PLACE_IN_SECTION("MB_MEM2") uint32_t __furi_check_registers[13] = {0}; + +/** Load r12 value to __furi_check_message and store registers to __furi_check_registers */ +#define GET_MESSAGE_AND_STORE_REGISTERS() \ + asm volatile("ldr r11, =__furi_check_message \n" \ + "str r12, [r11] \n" \ + "ldr r12, =__furi_check_registers \n" \ + "stm r12, {r0-r11} \n" \ + "str lr, [r12, #48] \n" \ + : \ + : \ + : "memory"); + +/** Restore registers and halt MCU + * + * - Always use it with GET_MESSAGE_AND_STORE_REGISTERS + * - If debugger is(was) connected this routine will raise bkpt + * - If debugger is not connected then endless loop + * + */ +#define RESTORE_REGISTERS_AND_HALT_MCU(debug) \ + register bool r0 asm("r0") = debug; \ + asm volatile("cbnz r0, with_debugger%= \n" \ + "ldr r12, =__furi_check_registers\n" \ + "ldm r12, {r0-r11} \n" \ + "loop%=: \n" \ + "wfi \n" \ + "b loop%= \n" \ + "with_debugger%=: \n" \ + "ldr r12, =__furi_check_registers\n" \ + "ldm r12, {r0-r11} \n" \ + "debug_loop%=: \n" \ + "bkpt 0x00 \n" \ + "wfi \n" \ + "b debug_loop%= \n" \ + : \ + : "r"(r0) \ + : "memory"); extern size_t xPortGetTotalHeapSize(void); -extern size_t xPortGetFreeHeapSize(void); -extern size_t xPortGetMinimumEverFreeHeapSize(void); static void __furi_put_uint32_as_text(uint32_t data) { char tmp_str[] = "-2147483648"; @@ -20,11 +62,44 @@ static void __furi_put_uint32_as_text(uint32_t data) { furi_hal_console_puts(tmp_str); } +static void __furi_put_uint32_as_hex(uint32_t data) { + char tmp_str[] = "0xFFFFFFFF"; + itoa(data, tmp_str, 16); + furi_hal_console_puts(tmp_str); +} + +static void __furi_print_register_info() { + // Print registers + for(uint8_t i = 0; i < 12; i++) { + furi_hal_console_puts("\r\n\tr"); + __furi_put_uint32_as_text(i); + furi_hal_console_puts(" : "); + __furi_put_uint32_as_hex(__furi_check_registers[i]); + } + + furi_hal_console_puts("\r\n\tlr : "); + __furi_put_uint32_as_hex(__furi_check_registers[12]); +} + static void __furi_print_stack_info() { furi_hal_console_puts("\r\n\tstack watermark: "); __furi_put_uint32_as_text(uxTaskGetStackHighWaterMark(NULL) * 4); } +static void __furi_print_bt_stack_info() { + const FuriHalBtHardfaultInfo* fault_info = furi_hal_bt_get_hardfault_info(); + if(fault_info == NULL) { + furi_hal_console_puts("\r\n\tcore2: not faulted"); + } else { + furi_hal_console_puts("\r\n\tcore2: hardfaulted.\r\n\tPC: "); + __furi_put_uint32_as_hex(fault_info->source_pc); + furi_hal_console_puts("\r\n\tLR: "); + __furi_put_uint32_as_hex(fault_info->source_lr); + furi_hal_console_puts("\r\n\tSP: "); + __furi_put_uint32_as_hex(fault_info->source_sp); + } +} + static void __furi_print_heap_info() { furi_hal_console_puts("\r\n\t heap total: "); __furi_put_uint32_as_text(xPortGetTotalHeapSize()); @@ -51,62 +126,77 @@ static void __furi_print_name(bool isr) { } } -static FURI_NORETURN void __furi_halt() { - asm volatile( -#ifdef FURI_DEBUG - "bkpt 0x00 \n" -#endif - "loop%=: \n" - "wfi \n" - "b loop%= \n" - : - : - : "memory"); - __builtin_unreachable(); -} - -FURI_NORETURN void furi_crash(const char* message) { - bool isr = FURI_IS_ISR(); +FURI_NORETURN void __furi_crash_implementation() { __disable_irq(); + GET_MESSAGE_AND_STORE_REGISTERS(); + + bool isr = FURI_IS_IRQ_MODE(); - if(message == NULL) { - message = "Fatal Error"; + if(__furi_check_message == NULL) { + __furi_check_message = "Fatal Error"; + } else if(__furi_check_message == (void*)__FURI_ASSERT_MESSAGE_FLAG) { + __furi_check_message = "furi_assert failed"; + } else if(__furi_check_message == (void*)__FURI_CHECK_MESSAGE_FLAG) { + __furi_check_message = "furi_check failed"; } furi_hal_console_puts("\r\n\033[0;31m[CRASH]"); __furi_print_name(isr); - furi_hal_console_puts(message); + furi_hal_console_puts(__furi_check_message); + __furi_print_register_info(); if(!isr) { __furi_print_stack_info(); } __furi_print_heap_info(); + __furi_print_bt_stack_info(); -#ifdef FURI_DEBUG - furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); - furi_hal_console_puts("\033[0m\r\n"); - __furi_halt(); -#else - furi_hal_rtc_set_fault_data((uint32_t)message); - furi_hal_console_puts("\r\nRebooting system.\r\n"); - furi_hal_console_puts("\033[0m\r\n"); - furi_hal_power_reset(); + // Check if debug enabled by DAP + // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en + bool debug = CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk; +#ifdef FURI_NDEBUG + if(debug) { +#endif + furi_hal_console_puts("\r\nSystem halted. Connect debugger for more info\r\n"); + furi_hal_console_puts("\033[0m\r\n"); + furi_hal_debug_enable(); + + RESTORE_REGISTERS_AND_HALT_MCU(debug); +#ifdef FURI_NDEBUG + } else { + uint32_t ptr = (uint32_t)__furi_check_message; + if(ptr < FLASH_BASE || ptr > (FLASH_BASE + FLASH_SIZE)) { + ptr = (uint32_t) "Check serial logs"; + } + furi_hal_rtc_set_fault_data(ptr); + furi_hal_console_puts("\r\nRebooting system.\r\n"); + furi_hal_console_puts("\033[0m\r\n"); + furi_hal_power_reset(); + } #endif __builtin_unreachable(); } -FURI_NORETURN void furi_halt(const char* message) { - bool isr = FURI_IS_ISR(); +FURI_NORETURN void __furi_halt_implementation() { __disable_irq(); + GET_MESSAGE_AND_STORE_REGISTERS(); + + bool isr = FURI_IS_IRQ_MODE(); - if(message == NULL) { - message = "System halt requested."; + if(__furi_check_message == NULL) { + __furi_check_message = "System halt requested."; } furi_hal_console_puts("\r\n\033[0;31m[HALT]"); __furi_print_name(isr); - furi_hal_console_puts(message); + furi_hal_console_puts(__furi_check_message); furi_hal_console_puts("\r\nSystem halted. Bye-bye!\r\n"); furi_hal_console_puts("\033[0m\r\n"); - __furi_halt(); + + // Check if debug enabled by DAP + // https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/Debug-register-support-in-the-SCS/Debug-Halting-Control-and-Status-Register--DHCSR?lang=en + bool debug = CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk; + RESTORE_REGISTERS_AND_HALT_MCU(debug); + + __builtin_unreachable(); } diff --git a/furi/core/check.h b/furi/core/check.h index 30efdf01068..2d5df4cf6c4 100644 --- a/furi/core/check.h +++ b/furi/core/check.h @@ -1,5 +1,20 @@ +/** + * @file check.h + * + * Furi crash and assert functions. + * + * The main problem with crashing is that you can't do anything without disturbing registers, + * and if you disturb registers, you won't be able to see the correct register values in the debugger. + * + * Current solution works around it by passing the message through r12 and doing some magic with registers in crash function. + * r0-r10 are stored in the ram2 on crash routine start and restored at the end. + * The only register that is going to be lost is r11. + * + */ #pragma once +#include + #ifdef __cplusplus extern "C" { #define FURI_NORETURN [[noreturn]] @@ -8,24 +23,85 @@ extern "C" { #define FURI_NORETURN noreturn #endif +// Flags instead of pointers will save ~4 bytes on furi_assert and furi_check calls. +#define __FURI_ASSERT_MESSAGE_FLAG (0x01) +#define __FURI_CHECK_MESSAGE_FLAG (0x02) + +/** Crash system */ +FURI_NORETURN void __furi_crash_implementation(); + +/** Halt system */ +FURI_NORETURN void __furi_halt_implementation(); + +/** Crash system with message. Show message after reboot. */ +#define __furi_crash(message) \ + do { \ + register const void* r12 asm("r12") = (void*)message; \ + asm volatile("sukima%=:" : : "r"(r12)); \ + __furi_crash_implementation(); \ + } while(0) + +/** Crash system + * + * @param optional message (const char*) + */ +#define furi_crash(...) M_APPLY(__furi_crash, M_IF_EMPTY(__VA_ARGS__)((NULL), (__VA_ARGS__))) + +/** Halt system with message. */ +#define __furi_halt(message) \ + do { \ + register const void* r12 asm("r12") = (void*)message; \ + asm volatile("sukima%=:" : : "r"(r12)); \ + __furi_halt_implementation(); \ + } while(0) + +/** Halt system + * + * @param optional message (const char*) + */ +#define furi_halt(...) M_APPLY(__furi_halt, M_IF_EMPTY(__VA_ARGS__)((NULL), (__VA_ARGS__))) + /** Check condition and crash if check failed */ -#define furi_check(__e) ((__e) ? (void)0 : furi_crash("furi_check failed\r\n")) +#define __furi_check(__e, __m) \ + do { \ + if(!(__e)) { \ + __furi_crash(__m); \ + } \ + } while(0) + +/** Check condition and crash if failed + * + * @param condition to check + * @param optional message (const char*) + */ +#define furi_check(...) \ + M_APPLY(__furi_check, M_DEFAULT_ARGS(2, (__FURI_CHECK_MESSAGE_FLAG), __VA_ARGS__)) /** Only in debug build: Assert condition and crash if assert failed */ #ifdef FURI_DEBUG -#define furi_assert(__e) ((__e) ? (void)0 : furi_crash("furi_assert failed\r\n")) +#define __furi_assert(__e, __m) \ + do { \ + if(!(__e)) { \ + __furi_crash(__m); \ + } \ + } while(0) #else -#define furi_assert(__e) \ - do { \ - ((void)(__e)); \ +#define __furi_assert(__e, __m) \ + do { \ + ((void)(__e)); \ + ((void)(__m)); \ } while(0) #endif -/** Crash system */ -FURI_NORETURN void furi_crash(const char* message); - -/** Halt system */ -FURI_NORETURN void furi_halt(const char* message); +/** Assert condition and crash if failed + * + * @warning only will do check if firmware compiled in debug mode + * + * @param condition to check + * @param optional message (const char*) + */ +#define furi_assert(...) \ + M_APPLY(__furi_assert, M_DEFAULT_ARGS(2, (__FURI_ASSERT_MESSAGE_FLAG), __VA_ARGS__)) #ifdef __cplusplus } diff --git a/furi/core/common_defines.h b/furi/core/common_defines.h index c211ad7ee74..b0062e65916 100644 --- a/furi/core/common_defines.h +++ b/furi/core/common_defines.h @@ -1,8 +1,7 @@ #pragma once +#include "core_defines.h" #include -#include -#include #ifdef __cplusplus extern "C" { @@ -10,90 +9,16 @@ extern "C" { #include -#ifndef MAX -#define MAX(a, b) \ - ({ \ - __typeof__(a) _a = (a); \ - __typeof__(b) _b = (b); \ - _a > _b ? _a : _b; \ - }) +#ifndef FURI_WARN_UNUSED +#define FURI_WARN_UNUSED __attribute__((warn_unused_result)) #endif -#ifndef MIN -#define MIN(a, b) \ - ({ \ - __typeof__(a) _a = (a); \ - __typeof__(b) _b = (b); \ - _a < _b ? _a : _b; \ - }) +#ifndef FURI_WEAK +#define FURI_WEAK __attribute__((weak)) #endif -#ifndef ROUND_UP_TO -#define ROUND_UP_TO(a, b) \ - ({ \ - __typeof__(a) _a = (a); \ - __typeof__(b) _b = (b); \ - _a / _b + !!(_a % _b); \ - }) -#endif - -#ifndef CLAMP -#define CLAMP(x, upper, lower) (MIN(upper, MAX(x, lower))) -#endif - -#ifndef COUNT_OF -#define COUNT_OF(x) (sizeof(x) / sizeof(x[0])) -#endif - -#ifndef FURI_SWAP -#define FURI_SWAP(x, y) \ - do { \ - typeof(x) SWAP = x; \ - x = y; \ - y = SWAP; \ - } while(0) -#endif - -#ifndef PLACE_IN_SECTION -#define PLACE_IN_SECTION(x) __attribute__((section(x))) -#endif - -#ifndef ALIGN -#define ALIGN(n) __attribute__((aligned(n))) -#endif - -#ifndef __weak -#define __weak __attribute__((weak)) -#endif - -#ifndef UNUSED -#define UNUSED(X) (void)(X) -#endif - -#ifndef STRINGIFY -#define STRINGIFY(x) #x -#endif - -#ifndef TOSTRING -#define TOSTRING(x) STRINGIFY(x) -#endif - -#ifndef REVERSE_BYTES_U32 -#define REVERSE_BYTES_U32(x) \ - ((((x)&0x000000FF) << 24) | (((x)&0x0000FF00) << 8) | (((x)&0x00FF0000) >> 8) | \ - (((x)&0xFF000000) >> 24)) -#endif - -#ifndef FURI_BIT -#define FURI_BIT(x, n) (((x) >> (n)) & 1) -#endif - -#ifndef FURI_BIT_SET -#define FURI_BIT_SET(x, n) ((x) |= (1 << (n))) -#endif - -#ifndef FURI_BIT_CLEAR -#define FURI_BIT_CLEAR(x, n) ((x) &= ~(1 << (n))) +#ifndef FURI_PACKED +#define FURI_PACKED __attribute__((packed)) #endif #ifndef FURI_IS_IRQ_MASKED @@ -108,54 +33,27 @@ extern "C" { #define FURI_IS_ISR() (FURI_IS_IRQ_MODE() || FURI_IS_IRQ_MASKED()) #endif +typedef struct { + uint32_t isrm; + bool from_isr; + bool kernel_running; +} __FuriCriticalInfo; + +__FuriCriticalInfo __furi_critical_enter(void); + +void __furi_critical_exit(__FuriCriticalInfo info); + #ifndef FURI_CRITICAL_ENTER -#define FURI_CRITICAL_ENTER() \ - uint32_t __isrm = 0; \ - bool __from_isr = FURI_IS_ISR(); \ - bool __kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING); \ - if(__from_isr) { \ - __isrm = taskENTER_CRITICAL_FROM_ISR(); \ - } else if(__kernel_running) { \ - taskENTER_CRITICAL(); \ - } else { \ - __disable_irq(); \ - } +#define FURI_CRITICAL_ENTER() __FuriCriticalInfo __furi_critical_info = __furi_critical_enter(); #endif #ifndef FURI_CRITICAL_EXIT -#define FURI_CRITICAL_EXIT() \ - if(__from_isr) { \ - taskEXIT_CRITICAL_FROM_ISR(__isrm); \ - } else if(__kernel_running) { \ - taskEXIT_CRITICAL(); \ - } else { \ - __enable_irq(); \ - } +#define FURI_CRITICAL_EXIT() __furi_critical_exit(__furi_critical_info); #endif -static inline bool furi_is_irq_context() { - bool irq = false; - BaseType_t state; - - if(FURI_IS_IRQ_MODE()) { - /* Called from interrupt context */ - irq = true; - } else { - /* Get FreeRTOS scheduler state */ - state = xTaskGetSchedulerState(); - - if(state != taskSCHEDULER_NOT_STARTED) { - /* Scheduler was started */ - if(FURI_IS_IRQ_MASKED()) { - /* Interrupts are masked */ - irq = true; - } - } - } - - /* Return context, 0: thread context, 1: IRQ context */ - return (irq); -} +#ifndef FURI_CHECK_RETURN +#define FURI_CHECK_RETURN __attribute__((__warn_unused_result__)) +#endif #ifdef __cplusplus } diff --git a/furi/core/core_defines.h b/furi/core/core_defines.h new file mode 100644 index 00000000000..4309c20c589 --- /dev/null +++ b/furi/core/core_defines.h @@ -0,0 +1,116 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define FURI_RETURNS_NONNULL __attribute__((returns_nonnull)) + +#ifndef MAX +#define MAX(a, b) \ + ({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a > _b ? _a : _b; \ + }) +#endif + +#ifndef MIN +#define MIN(a, b) \ + ({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a < _b ? _a : _b; \ + }) +#endif + +#ifndef ABS +#define ABS(a) ({ (a) < 0 ? -(a) : (a); }) +#endif + +#ifndef ROUND_UP_TO +#define ROUND_UP_TO(a, b) \ + ({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a / _b + !!(_a % _b); \ + }) +#endif + +#ifndef CLAMP +#define CLAMP(x, upper, lower) (MIN(upper, MAX(x, lower))) +#endif + +#ifndef COUNT_OF +#define COUNT_OF(x) (sizeof(x) / sizeof(x[0])) +#endif + +#ifndef FURI_SWAP +#define FURI_SWAP(x, y) \ + do { \ + typeof(x) SWAP = x; \ + x = y; \ + y = SWAP; \ + } while(0) +#endif + +#ifndef PLACE_IN_SECTION +#define PLACE_IN_SECTION(x) __attribute__((section(x))) +#endif + +#ifndef ALIGN +#define ALIGN(n) __attribute__((aligned(n))) +#endif + +#ifndef __weak +#define __weak __attribute__((weak)) +#endif + +#ifndef UNUSED +#define UNUSED(X) (void)(X) +#endif + +#ifndef STRINGIFY +#define STRINGIFY(x) #x +#endif + +#ifndef TOSTRING +#define TOSTRING(x) STRINGIFY(x) +#endif + +#ifndef CONCATENATE +#define CONCATENATE(a, b) CONCATENATE_(a, b) +#define CONCATENATE_(a, b) a##b +#endif + +#ifndef REVERSE_BYTES_U32 +#define REVERSE_BYTES_U32(x) \ + ((((x)&0x000000FF) << 24) | (((x)&0x0000FF00) << 8) | (((x)&0x00FF0000) >> 8) | \ + (((x)&0xFF000000) >> 24)) +#endif + +#ifndef FURI_BIT +#define FURI_BIT(x, n) (((x) >> (n)) & 1) +#endif + +#ifndef FURI_BIT_SET +#define FURI_BIT_SET(x, n) \ + ({ \ + __typeof__(x) _x = (1); \ + (x) |= (_x << (n)); \ + }) +#endif + +#ifndef FURI_BIT_CLEAR +#define FURI_BIT_CLEAR(x, n) \ + ({ \ + __typeof__(x) _x = (1); \ + (x) &= ~(_x << (n)); \ + }) +#endif + +#define FURI_SW_MEMBARRIER() asm volatile("" : : : "memory") + +#ifdef __cplusplus +} +#endif diff --git a/furi/core/critical.c b/furi/core/critical.c new file mode 100644 index 00000000000..3bef2be38e0 --- /dev/null +++ b/furi/core/critical.c @@ -0,0 +1,32 @@ +#include "common_defines.h" + +#include +#include + +__FuriCriticalInfo __furi_critical_enter(void) { + __FuriCriticalInfo info; + + info.isrm = 0; + info.from_isr = FURI_IS_ISR(); + info.kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING); + + if(info.from_isr) { + info.isrm = taskENTER_CRITICAL_FROM_ISR(); + } else if(info.kernel_running) { + taskENTER_CRITICAL(); + } else { + __disable_irq(); + } + + return info; +} + +void __furi_critical_exit(__FuriCriticalInfo info) { + if(info.from_isr) { + taskEXIT_CRITICAL_FROM_ISR(info.isrm); + } else if(info.kernel_running) { + taskEXIT_CRITICAL(); + } else { + __enable_irq(); + } +} \ No newline at end of file diff --git a/furi/core/event_flag.c b/furi/core/event_flag.c index 5d2a49910d4..96b9591877e 100644 --- a/furi/core/event_flag.c +++ b/furi/core/event_flag.c @@ -2,6 +2,7 @@ #include "common_defines.h" #include "check.h" +#include #include #define FURI_EVENT_FLAG_MAX_BITS_EVENT_GROUPS 24U @@ -9,7 +10,11 @@ FuriEventFlag* furi_event_flag_alloc() { furi_assert(!FURI_IS_IRQ_MODE()); - return ((FuriEventFlag*)xEventGroupCreate()); + + EventGroupHandle_t handle = xEventGroupCreate(); + furi_check(handle); + + return ((FuriEventFlag*)handle); } void furi_event_flag_free(FuriEventFlag* instance) { @@ -25,10 +30,10 @@ uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) { uint32_t rflags; BaseType_t yield; - if(FURI_IS_IRQ_MODE() != 0U) { + 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); @@ -48,7 +53,7 @@ uint32_t furi_event_flag_clear(FuriEventFlag* instance, uint32_t flags) { EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; uint32_t rflags; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { rflags = xEventGroupGetBitsFromISR(hEventGroup); if(xEventGroupClearBitsFromISR(hEventGroup, (EventBits_t)flags) == pdFAIL) { @@ -73,7 +78,7 @@ uint32_t furi_event_flag_get(FuriEventFlag* instance) { EventGroupHandle_t hEventGroup = (EventGroupHandle_t)instance; uint32_t rflags; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { rflags = xEventGroupGetBitsFromISR(hEventGroup); } else { rflags = xEventGroupGetBits(hEventGroup); diff --git a/furi/core/kernel.c b/furi/core/kernel.c index 73d2012b4d6..89a50a9b52f 100644 --- a/furi/core/kernel.c +++ b/furi/core/kernel.c @@ -5,10 +5,41 @@ #include +#include +#include + #include CMSIS_device_header +bool furi_kernel_is_irq_or_masked() { + bool irq = false; + BaseType_t state; + + if(FURI_IS_IRQ_MODE()) { + /* Called from interrupt context */ + irq = true; + } else { + /* Get FreeRTOS scheduler state */ + state = xTaskGetSchedulerState(); + + if(state != taskSCHEDULER_NOT_STARTED) { + /* Scheduler was started */ + if(FURI_IS_IRQ_MASKED()) { + /* Interrupts are masked */ + irq = true; + } + } + } + + /* Return context, 0: thread context, 1: IRQ context */ + return (irq); +} + +bool furi_kernel_is_running() { + return xTaskGetSchedulerState() != taskSCHEDULER_RUNNING; +} + int32_t furi_kernel_lock() { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); int32_t lock; @@ -33,7 +64,7 @@ int32_t furi_kernel_lock() { } int32_t furi_kernel_unlock() { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); int32_t lock; @@ -63,7 +94,7 @@ int32_t furi_kernel_unlock() { } int32_t furi_kernel_restore_lock(int32_t lock) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); switch(xTaskGetSchedulerState()) { case taskSCHEDULER_SUSPENDED: @@ -99,7 +130,7 @@ uint32_t furi_kernel_get_tick_frequency() { } void furi_delay_tick(uint32_t ticks) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); if(ticks == 0U) { taskYIELD(); } else { @@ -108,7 +139,7 @@ void furi_delay_tick(uint32_t ticks) { } FuriStatus furi_delay_until_tick(uint32_t tick) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); TickType_t tcnt, delay; FuriStatus stat; @@ -137,7 +168,7 @@ FuriStatus furi_delay_until_tick(uint32_t tick) { uint32_t furi_get_tick() { TickType_t ticks; - if(furi_is_irq_context() != 0U) { + if(furi_kernel_is_irq_or_masked() != 0U) { ticks = xTaskGetTickCountFromISR(); } else { ticks = xTaskGetTickCount(); diff --git a/furi/core/kernel.h b/furi/core/kernel.h index 28afffd4564..c962402efd6 100644 --- a/furi/core/kernel.h +++ b/furi/core/kernel.h @@ -1,5 +1,5 @@ /** - * @file kenrel.h + * @file kernel.h * Furi Kernel primitives */ #pragma once @@ -10,19 +10,48 @@ extern "C" { #endif +/** Check if CPU is in IRQ or kernel running and IRQ is masked + * + * Originally this primitive was born as a workaround for FreeRTOS kernel primitives shenanigans with PRIMASK. + * + * Meaningful use cases are: + * + * - When kernel is started and you want to ensure that you are not in IRQ or IRQ is not masked(like in critical section) + * - When kernel is not started and you want to make sure that you are not in IRQ mode, ignoring PRIMASK. + * + * As you can see there will be edge case when kernel is not started and PRIMASK is not 0 that may cause some funky behavior. + * Most likely it will happen after kernel primitives being used, but control not yet passed to kernel. + * It's up to you to figure out if it is safe for your code or not. + * + * @return true if CPU is in IRQ or kernel running and IRQ is masked + */ +bool furi_kernel_is_irq_or_masked(); + +/** Check if kernel is running + * + * @return true if running, false otherwise + */ +bool furi_kernel_is_running(); + /** Lock kernel, pause process scheduling + * + * @warning This should never be called in interrupt request context. * * @return previous lock state(0 - unlocked, 1 - locked) */ int32_t furi_kernel_lock(); /** Unlock kernel, resume process scheduling + * + * @warning This should never be called in interrupt request context. * * @return previous lock state(0 - unlocked, 1 - locked) */ int32_t furi_kernel_unlock(); /** Restore kernel lock state + * + * @warning This should never be called in interrupt request context. * * @param[in] lock The lock state * @@ -37,7 +66,9 @@ int32_t furi_kernel_restore_lock(int32_t lock); uint32_t furi_kernel_get_tick_frequency(); /** Delay execution - * + * + * @warning This should never be called in interrupt request context. + * * Also keep in mind delay is aliased to scheduler timer intervals. * * @param[in] ticks The ticks count to pause @@ -45,6 +76,8 @@ uint32_t furi_kernel_get_tick_frequency(); void furi_delay_tick(uint32_t ticks); /** Delay until tick + * + * @warning This should never be called in interrupt request context. * * @param[in] ticks The tick until which kerel should delay task execution * diff --git a/furi/core/log.c b/furi/core/log.c index 8a36a930be3..53467ecdb2d 100644 --- a/furi/core/log.c +++ b/furi/core/log.c @@ -8,47 +8,62 @@ typedef struct { FuriLogLevel log_level; FuriLogPuts puts; - FuriLogTimestamp timetamp; + FuriLogTimestamp timestamp; FuriMutex* mutex; } FuriLogParams; static FuriLogParams furi_log; +typedef struct { + const char* str; + FuriLogLevel level; +} FuriLogLevelDescription; + +static const FuriLogLevelDescription FURI_LOG_LEVEL_DESCRIPTIONS[] = { + {"default", FuriLogLevelDefault}, + {"none", FuriLogLevelNone}, + {"error", FuriLogLevelError}, + {"warn", FuriLogLevelWarn}, + {"info", FuriLogLevelInfo}, + {"debug", FuriLogLevelDebug}, + {"trace", FuriLogLevelTrace}, +}; + void furi_log_init() { // Set default logging parameters furi_log.log_level = FURI_LOG_LEVEL_DEFAULT; furi_log.puts = furi_hal_console_puts; - furi_log.timetamp = furi_get_tick; + furi_log.timestamp = furi_get_tick; furi_log.mutex = furi_mutex_alloc(FuriMutexTypeNormal); } void furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...) { if(level <= furi_log.log_level && furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk) { - string_t string; - string_init(string); + 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: @@ -56,23 +71,23 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form } // Timestamp - string_printf( + furi_string_printf( string, - "%lu %s[%s][%s] " FURI_LOG_CLR_RESET, - furi_log.timetamp(), + "%lu %s[%s][%s] " _FURI_LOG_CLR_RESET, + furi_log.timestamp(), color, log_letter, tag); - furi_log.puts(string_get_cstr(string)); - string_reset(string); + furi_log.puts(furi_string_get_cstr(string)); + furi_string_reset(string); va_list args; va_start(args, format); - string_vprintf(string, format, args); + furi_string_vprintf(string, format, args); va_end(args); - furi_log.puts(string_get_cstr(string)); - string_clear(string); + furi_log.puts(furi_string_get_cstr(string)); + furi_string_free(string); furi_log.puts("\r\n"); @@ -80,6 +95,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; @@ -98,5 +130,25 @@ void furi_log_set_puts(FuriLogPuts puts) { void furi_log_set_timestamp(FuriLogTimestamp timestamp) { furi_assert(timestamp); - furi_log.timetamp = timestamp; + furi_log.timestamp = timestamp; +} + +bool furi_log_level_to_string(FuriLogLevel level, const char** str) { + for(size_t i = 0; i < COUNT_OF(FURI_LOG_LEVEL_DESCRIPTIONS); i++) { + if(level == FURI_LOG_LEVEL_DESCRIPTIONS[i].level) { + *str = FURI_LOG_LEVEL_DESCRIPTIONS[i].str; + return true; + } + } + return false; } + +bool furi_log_level_from_string(const char* str, FuriLogLevel* level) { + for(size_t i = 0; i < COUNT_OF(FURI_LOG_LEVEL_DESCRIPTIONS); i++) { + if(strcmp(str, FURI_LOG_LEVEL_DESCRIPTIONS[i].str) == 0) { + *level = FURI_LOG_LEVEL_DESCRIPTIONS[i].level; + return true; + } + } + return false; +} \ No newline at end of file diff --git a/furi/core/log.h b/furi/core/log.h index 30026fc44ba..5d11add9b91 100644 --- a/furi/core/log.h +++ b/furi/core/log.h @@ -7,6 +7,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -22,21 +23,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); @@ -51,7 +52,17 @@ void furi_log_init(); * @param format * @param ... */ -void furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...); +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 * @@ -77,6 +88,23 @@ void furi_log_set_puts(FuriLogPuts puts); */ void furi_log_set_timestamp(FuriLogTimestamp timestamp); +/** Log level to string + * + * @param[in] level The level + * + * @return The string + */ +bool furi_log_level_to_string(FuriLogLevel level, const char** str); + +/** Log level from string + * + * @param[in] str The string + * @param level The level + * + * @return True if success, False otherwise + */ +bool furi_log_level_from_string(const char* str, FuriLogLevel* level); + /** Log methods * * @param tag The application tag @@ -94,6 +122,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/memmgr.c b/furi/core/memmgr.c index 80f87b930e7..768adc05dfa 100644 --- a/furi/core/memmgr.c +++ b/furi/core/memmgr.c @@ -1,6 +1,7 @@ #include "memmgr.h" #include "common_defines.h" #include +#include extern void* pvPortMalloc(size_t xSize); extern void vPortFree(void* pv); @@ -77,3 +78,34 @@ void* __wrap__realloc_r(struct _reent* r, void* ptr, size_t size) { UNUSED(r); return realloc(ptr, size); } + +void* memmgr_alloc_from_pool(size_t size) { + void* p = furi_hal_memory_alloc(size); + if(p == NULL) p = malloc(size); + + return p; +} + +size_t memmgr_pool_get_free(void) { + return furi_hal_memory_get_free(); +} + +size_t memmgr_pool_get_max_block(void) { + return furi_hal_memory_max_pool_block(); +} + +void* aligned_malloc(size_t size, size_t alignment) { + void* p1; // original block + void** p2; // aligned block + int offset = alignment - 1 + sizeof(void*); + if((p1 = (void*)malloc(size + offset)) == NULL) { + return NULL; + } + p2 = (void**)(((size_t)(p1) + offset) & ~(alignment - 1)); + p2[-1] = p1; + return p2; +} + +void aligned_free(void* p) { + free(((void**)p)[-1]); +} \ No newline at end of file diff --git a/furi/core/memmgr.h b/furi/core/memmgr.h index d7285fb238a..bc0c35faa71 100644 --- a/furi/core/memmgr.h +++ b/furi/core/memmgr.h @@ -1,6 +1,6 @@ /** * @file memmgr.h - * Furi: memory managment API and glue + * Furi: memory management API and glue */ #pragma once @@ -35,6 +35,43 @@ size_t memmgr_get_total_heap(void); */ size_t memmgr_get_minimum_free_heap(void); +/** + * An aligned version of malloc, used when you need to get the aligned space on the heap + * Freeing the received address is performed ONLY through the aligned_free function + * @param size + * @param alignment + * @return void* + */ +void* aligned_malloc(size_t size, size_t alignment); + +/** + * Freed space obtained through the aligned_malloc function + * @param p pointer to result of aligned_malloc + */ +void aligned_free(void* p); + +/** + * @brief Allocate memory from separate memory pool. That memory can't be freed. + * + * @param size + * @return void* + */ +void* memmgr_alloc_from_pool(size_t size); + +/** + * @brief Get free memory pool size + * + * @return size_t + */ +size_t memmgr_pool_get_free(void); + +/** + * @brief Get max free block size from memory pool + * + * @return size_t + */ +size_t memmgr_pool_get_max_block(void); + #ifdef __cplusplus } #endif diff --git a/furi/core/memmgr_heap.c b/furi/core/memmgr_heap.c index 32b2875cc40..a3e127c3c1b 100644 --- a/furi/core/memmgr_heap.c +++ b/furi/core/memmgr_heap.c @@ -47,8 +47,8 @@ all the API functions to use the MPU wrappers. That should only be done when task.h is included from an application file. */ #define MPU_WRAPPERS_INCLUDED_FROM_API_FILE -#include "FreeRTOS.h" -#include "task.h" +#include +#include #undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE @@ -115,8 +115,9 @@ static size_t xBlockAllocatedBit = 0; #include /* Allocation tracking types */ -DICT_DEF2(MemmgrHeapAllocDict, uint32_t, uint32_t) -DICT_DEF2( +DICT_DEF2(MemmgrHeapAllocDict, uint32_t, uint32_t) //-V1048 + +DICT_DEF2( //-V1048 MemmgrHeapThreadDict, uint32_t, M_DEFAULT_OPLIST, @@ -150,8 +151,7 @@ void memmgr_heap_disable_thread_trace(FuriThreadId thread_id) { vTaskSuspendAll(); { memmgr_heap_thread_trace_depth++; - furi_check(MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id) != NULL); - MemmgrHeapThreadDict_erase(memmgr_heap_thread_dict, (uint32_t)thread_id); + furi_check(MemmgrHeapThreadDict_erase(memmgr_heap_thread_dict, (uint32_t)thread_id)); memmgr_heap_thread_trace_depth--; } (void)xTaskResumeAll(); @@ -212,7 +212,9 @@ static inline void traceFREE(void* pointer, size_t size) { MemmgrHeapAllocDict_t* alloc_dict = MemmgrHeapThreadDict_get(memmgr_heap_thread_dict, (uint32_t)thread_id); if(alloc_dict) { - MemmgrHeapAllocDict_erase(*alloc_dict, (uint32_t)pointer); + // In some cases thread may want to release memory that was not allocated by it + const bool res = MemmgrHeapAllocDict_erase(*alloc_dict, (uint32_t)pointer); + UNUSED(res); } memmgr_heap_thread_trace_depth--; } @@ -340,6 +342,10 @@ void* pvPortMalloc(size_t xWantedSize) { void* pvReturn = NULL; size_t to_wipe = xWantedSize; + if(FURI_IS_IRQ_MODE()) { + furi_crash("memmgt in ISR"); + } + #ifdef HEAP_PRINT_DEBUG BlockLink_t* print_heap_block = NULL; #endif @@ -486,6 +492,10 @@ void vPortFree(void* pv) { uint8_t* puc = (uint8_t*)pv; BlockLink_t* pxLink; + if(FURI_IS_IRQ_MODE()) { + furi_crash("memmgt in ISR"); + } + if(pv != NULL) { /* The memory being freed will have an BlockLink_t structure immediately before it. */ @@ -512,8 +522,8 @@ void vPortFree(void* pv) { { furi_assert((size_t)pv >= SRAM_BASE); furi_assert((size_t)pv < SRAM_BASE + 1024 * 256); + furi_assert(pxLink->xBlockSize >= xHeapStructSize); furi_assert((pxLink->xBlockSize - xHeapStructSize) < 1024 * 256); - furi_assert((int32_t)(pxLink->xBlockSize - xHeapStructSize) >= 0); /* Add this block to the list of free blocks. */ xFreeBytesRemaining += pxLink->xBlockSize; diff --git a/furi/core/memmgr_heap.h b/furi/core/memmgr_heap.h index 1521fce425e..9aacba1ca79 100644 --- a/furi/core/memmgr_heap.h +++ b/furi/core/memmgr_heap.h @@ -1,6 +1,6 @@ /** * @file memmgr_heap.h - * Furi: heap memory managment API and allocator + * Furi: heap memory management API and allocator */ #pragma once diff --git a/furi/core/message_queue.c b/furi/core/message_queue.c index 2658d6ff736..e20fa420a08 100644 --- a/furi/core/message_queue.c +++ b/furi/core/message_queue.c @@ -1,17 +1,21 @@ +#include "kernel.h" #include "message_queue.h" -#include "core/common_defines.h" +#include "check.h" + #include #include -#include "check.h" FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) { - furi_assert((furi_is_irq_context() == 0U) && (msg_count > 0U) && (msg_size > 0U)); + furi_assert((furi_kernel_is_irq_or_masked() == 0U) && (msg_count > 0U) && (msg_size > 0U)); + + QueueHandle_t handle = xQueueCreate(msg_count, msg_size); + furi_check(handle); - return ((FuriMessageQueue*)xQueueCreate(msg_count, msg_size)); + return ((FuriMessageQueue*)handle); } void furi_message_queue_free(FuriMessageQueue* instance) { - furi_assert(furi_is_irq_context() == 0U); + furi_assert(furi_kernel_is_irq_or_masked() == 0U); furi_assert(instance); vQueueDelete((QueueHandle_t)instance); @@ -25,7 +29,7 @@ FuriStatus stat = FuriStatusOk; - if(furi_is_irq_context() != 0U) { + if(furi_kernel_is_irq_or_masked() != 0U) { if((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) { stat = FuriStatusErrorParameter; } else { @@ -62,7 +66,7 @@ FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uin stat = FuriStatusOk; - if(furi_is_irq_context() != 0U) { + if(furi_kernel_is_irq_or_masked() != 0U) { if((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) { stat = FuriStatusErrorParameter; } else { @@ -128,7 +132,7 @@ uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) { if(hQueue == NULL) { count = 0U; - } else if(furi_is_irq_context() != 0U) { + } else if(furi_kernel_is_irq_or_masked() != 0U) { count = uxQueueMessagesWaitingFromISR(hQueue); } else { count = uxQueueMessagesWaiting(hQueue); @@ -145,7 +149,7 @@ uint32_t furi_message_queue_get_space(FuriMessageQueue* instance) { if(mq == NULL) { space = 0U; - } else if(furi_is_irq_context() != 0U) { + } else if(furi_kernel_is_irq_or_masked() != 0U) { isrm = taskENTER_CRITICAL_FROM_ISR(); /* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */ @@ -164,7 +168,7 @@ FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) { QueueHandle_t hQueue = (QueueHandle_t)instance; FuriStatus stat; - if(furi_is_irq_context() != 0U) { + if(furi_kernel_is_irq_or_masked() != 0U) { stat = FuriStatusErrorISR; } else if(hQueue == NULL) { stat = FuriStatusErrorParameter; diff --git a/furi/core/mutex.c b/furi/core/mutex.c index 78ea0519692..8794e10dc39 100644 --- a/furi/core/mutex.c +++ b/furi/core/mutex.c @@ -2,6 +2,7 @@ #include "check.h" #include "common_defines.h" +#include #include FuriMutex* furi_mutex_alloc(FuriMutexType type) { @@ -30,6 +31,8 @@ FuriMutex* furi_mutex_alloc(FuriMutexType type) { void furi_mutex_free(FuriMutex* instance) { furi_assert(!FURI_IS_IRQ_MODE()); + furi_assert(instance); + vSemaphoreDelete((SemaphoreHandle_t)((uint32_t)instance & ~1U)); } @@ -45,7 +48,7 @@ FuriStatus furi_mutex_acquire(FuriMutex* instance, uint32_t timeout) { stat = FuriStatusOk; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { stat = FuriStatusErrorISR; } else if(hMutex == NULL) { stat = FuriStatusErrorParameter; @@ -85,7 +88,7 @@ FuriStatus furi_mutex_release(FuriMutex* instance) { stat = FuriStatusOk; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { stat = FuriStatusErrorISR; } else if(hMutex == NULL) { stat = FuriStatusErrorParameter; @@ -111,7 +114,7 @@ FuriThreadId furi_mutex_get_owner(FuriMutex* instance) { hMutex = (SemaphoreHandle_t)((uint32_t)instance & ~1U); - if((FURI_IS_IRQ_MODE() != 0U) || (hMutex == NULL)) { + if((FURI_IS_IRQ_MODE()) || (hMutex == NULL)) { owner = 0; } else { owner = (FuriThreadId)xSemaphoreGetMutexHolder(hMutex); diff --git a/furi/core/record.c b/furi/core/record.c index 666d50761a8..773585e7efc 100644 --- a/furi/core/record.c +++ b/furi/core/record.c @@ -4,8 +4,8 @@ #include "mutex.h" #include "event_flag.h" -#include #include +#include #define FURI_RECORD_FLAG_READY (0x1) @@ -15,7 +15,7 @@ typedef struct { size_t holders_count; } FuriRecordData; -DICT_DEF2(FuriRecordDataDict, string_t, STRING_OPLIST, FuriRecordData, M_POD_OPLIST) +DICT_DEF2(FuriRecordDataDict, const char*, M_CSTR_DUP_OPLIST, FuriRecordData, M_POD_OPLIST) typedef struct { FuriMutex* mutex; @@ -24,6 +24,19 @@ typedef struct { static FuriRecord* furi_record = NULL; +static FuriRecordData* furi_record_get(const char* name) { + return FuriRecordDataDict_get(furi_record->records, name); +} + +static void furi_record_put(const char* name, FuriRecordData* record_data) { + FuriRecordDataDict_set_at(furi_record->records, name, *record_data); +} + +static void furi_record_erase(const char* name, FuriRecordData* record_data) { + furi_event_flag_free(record_data->flags); + FuriRecordDataDict_erase(furi_record->records, name); +} + void furi_record_init() { furi_record = malloc(sizeof(FuriRecord)); furi_record->mutex = furi_mutex_alloc(FuriMutexTypeNormal); @@ -31,16 +44,16 @@ void furi_record_init() { FuriRecordDataDict_init(furi_record->records); } -static FuriRecordData* furi_record_data_get_or_create(string_t name_str) { +static FuriRecordData* furi_record_data_get_or_create(const char* name) { furi_assert(furi_record); - FuriRecordData* record_data = FuriRecordDataDict_get(furi_record->records, name_str); + FuriRecordData* record_data = furi_record_get(name); if(!record_data) { FuriRecordData new_record; new_record.flags = furi_event_flag_alloc(); new_record.data = NULL; new_record.holders_count = 0; - FuriRecordDataDict_set_at(furi_record->records, name_str, new_record); - record_data = FuriRecordDataDict_get(furi_record->records, name_str); + furi_record_put(name, &new_record); + record_data = furi_record_get(name); } return record_data; } @@ -59,35 +72,25 @@ bool furi_record_exists(const char* name) { bool ret = false; - string_t name_str; - string_init_set_str(name_str, name); - furi_record_lock(); - ret = (FuriRecordDataDict_get(furi_record->records, name_str) != NULL); + ret = (furi_record_get(name) != NULL); furi_record_unlock(); - string_clear(name_str); - return ret; } void furi_record_create(const char* name, void* data) { furi_assert(furi_record); - string_t name_str; - string_init_set_str(name_str, name); - furi_record_lock(); // Get record data and fill it - FuriRecordData* record_data = furi_record_data_get_or_create(name_str); + FuriRecordData* record_data = furi_record_data_get_or_create(name); furi_assert(record_data->data == NULL); record_data->data = data; furi_event_flag_set(record_data->flags, FURI_RECORD_FLAG_READY); furi_record_unlock(); - - string_clear(name_str); } bool furi_record_destroy(const char* name) { @@ -95,35 +98,26 @@ bool furi_record_destroy(const char* name) { bool ret = false; - string_t name_str; - string_init_set_str(name_str, name); - furi_record_lock(); - FuriRecordData* record_data = FuriRecordDataDict_get(furi_record->records, name_str); + FuriRecordData* record_data = furi_record_get(name); furi_assert(record_data); if(record_data->holders_count == 0) { - furi_event_flag_free(record_data->flags); - FuriRecordDataDict_erase(furi_record->records, name_str); + furi_record_erase(name, record_data); ret = true; } furi_record_unlock(); - string_clear(name_str); - return ret; } void* furi_record_open(const char* name) { furi_assert(furi_record); - string_t name_str; - string_init_set_str(name_str, name); - furi_record_lock(); - FuriRecordData* record_data = furi_record_data_get_or_create(name_str); + FuriRecordData* record_data = furi_record_data_get_or_create(name); record_data->holders_count++; furi_record_unlock(); @@ -136,24 +130,17 @@ void* furi_record_open(const char* name) { FuriFlagWaitAny | FuriFlagNoClear, FuriWaitForever) == FURI_RECORD_FLAG_READY); - string_clear(name_str); - return record_data->data; } void furi_record_close(const char* name) { furi_assert(furi_record); - string_t name_str; - string_init_set_str(name_str, name); - furi_record_lock(); - FuriRecordData* record_data = FuriRecordDataDict_get(furi_record->records, name_str); + FuriRecordData* record_data = furi_record_get(name); furi_assert(record_data); record_data->holders_count--; furi_record_unlock(); - - string_clear(name_str); } diff --git a/furi/core/record.h b/furi/core/record.h index cb4bd199f88..4819123e277 100644 --- a/furi/core/record.h +++ b/furi/core/record.h @@ -6,6 +6,7 @@ #pragma once #include +#include "core_defines.h" #ifdef __cplusplus extern "C" { @@ -51,7 +52,7 @@ bool furi_record_destroy(const char* name); * @note Thread safe. Open and close must be executed from the same * thread. Suspends caller thread till record is available */ -void* furi_record_open(const char* name); +FURI_RETURNS_NONNULL void* furi_record_open(const char* name); /** Close record * diff --git a/furi/core/semaphore.c b/furi/core/semaphore.c index a204cbe6eab..1f1a07780cd 100644 --- a/furi/core/semaphore.c +++ b/furi/core/semaphore.c @@ -2,6 +2,7 @@ #include "check.h" #include "common_defines.h" +#include #include FuriSemaphore* furi_semaphore_alloc(uint32_t max_count, uint32_t initial_count) { @@ -45,7 +46,7 @@ FuriStatus furi_semaphore_acquire(FuriSemaphore* instance, uint32_t timeout) { stat = FuriStatusOk; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { if(timeout != 0U) { stat = FuriStatusErrorParameter; } else { @@ -80,7 +81,7 @@ FuriStatus furi_semaphore_release(FuriSemaphore* instance) { stat = FuriStatusOk; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { yield = pdFALSE; if(xSemaphoreGiveFromISR(hSemaphore, &yield) != pdTRUE) { @@ -104,7 +105,7 @@ uint32_t furi_semaphore_get_count(FuriSemaphore* instance) { SemaphoreHandle_t hSemaphore = (SemaphoreHandle_t)instance; uint32_t count; - if(FURI_IS_IRQ_MODE() != 0U) { + if(FURI_IS_IRQ_MODE()) { count = (uint32_t)uxSemaphoreGetCountFromISR(hSemaphore); } else { count = (uint32_t)uxSemaphoreGetCount(hSemaphore); diff --git a/furi/core/stream_buffer.c b/furi/core/stream_buffer.c new file mode 100644 index 00000000000..a13d256b110 --- /dev/null +++ b/furi/core/stream_buffer.c @@ -0,0 +1,86 @@ +#include "base.h" +#include "check.h" +#include "stream_buffer.h" +#include "common_defines.h" + +#include +#include + +FuriStreamBuffer* furi_stream_buffer_alloc(size_t size, size_t trigger_level) { + furi_assert(size != 0); + + StreamBufferHandle_t handle = xStreamBufferCreate(size, trigger_level); + furi_check(handle); + + return handle; +}; + +void furi_stream_buffer_free(FuriStreamBuffer* stream_buffer) { + furi_assert(stream_buffer); + vStreamBufferDelete(stream_buffer); +}; + +bool furi_stream_set_trigger_level(FuriStreamBuffer* stream_buffer, size_t trigger_level) { + furi_assert(stream_buffer); + return xStreamBufferSetTriggerLevel(stream_buffer, trigger_level) == pdTRUE; +}; + +size_t furi_stream_buffer_send( + FuriStreamBuffer* stream_buffer, + const void* data, + size_t length, + uint32_t timeout) { + size_t ret; + + if(FURI_IS_IRQ_MODE()) { + BaseType_t yield; + ret = xStreamBufferSendFromISR(stream_buffer, data, length, &yield); + portYIELD_FROM_ISR(yield); + } else { + ret = xStreamBufferSend(stream_buffer, data, length, timeout); + } + + return ret; +}; + +size_t furi_stream_buffer_receive( + FuriStreamBuffer* stream_buffer, + void* data, + size_t length, + uint32_t timeout) { + size_t ret; + + if(FURI_IS_IRQ_MODE()) { + BaseType_t yield; + ret = xStreamBufferReceiveFromISR(stream_buffer, data, length, &yield); + portYIELD_FROM_ISR(yield); + } else { + ret = xStreamBufferReceive(stream_buffer, data, length, timeout); + } + + return ret; +} + +size_t furi_stream_buffer_bytes_available(FuriStreamBuffer* stream_buffer) { + return xStreamBufferBytesAvailable(stream_buffer); +}; + +size_t furi_stream_buffer_spaces_available(FuriStreamBuffer* stream_buffer) { + return xStreamBufferSpacesAvailable(stream_buffer); +}; + +bool furi_stream_buffer_is_full(FuriStreamBuffer* stream_buffer) { + return xStreamBufferIsFull(stream_buffer) == pdTRUE; +}; + +bool furi_stream_buffer_is_empty(FuriStreamBuffer* stream_buffer) { + return (xStreamBufferIsEmpty(stream_buffer) == pdTRUE); +}; + +FuriStatus furi_stream_buffer_reset(FuriStreamBuffer* stream_buffer) { + if(xStreamBufferReset(stream_buffer) == pdPASS) { + return FuriStatusOk; + } else { + return FuriStatusError; + } +} \ No newline at end of file diff --git a/furi/core/stream_buffer.h b/furi/core/stream_buffer.h new file mode 100644 index 00000000000..d07f7e60ba3 --- /dev/null +++ b/furi/core/stream_buffer.h @@ -0,0 +1,152 @@ +/** + * @file stream_buffer.h + * Furi stream buffer primitive. + * + * Stream buffers are used to send a continuous stream of data from one task or + * interrupt to another. Their implementation is light weight, making them + * particularly suited for interrupt to task and core to core communication + * scenarios. + * + * ***NOTE***: Stream buffer implementation assumes there is only one task or + * interrupt that will write to the buffer (the writer), and only one task or + * interrupt that will read from the buffer (the reader). + */ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void FuriStreamBuffer; + +/** + * @brief Allocate stream buffer instance. + * Stream buffer implementation assumes there is only one task or + * interrupt that will write to the buffer (the writer), and only one task or + * interrupt that will read from the buffer (the reader). + * + * @param size The total number of bytes the stream buffer will be able to hold at any one time. + * @param trigger_level The number of bytes that must be in the stream buffer + * before a task that is blocked on the stream buffer to wait for data is moved out of the blocked state. + * @return The stream buffer instance. + */ +FuriStreamBuffer* furi_stream_buffer_alloc(size_t size, size_t trigger_level); + +/** + * @brief Free stream buffer instance + * + * @param stream_buffer The stream buffer instance. + */ +void furi_stream_buffer_free(FuriStreamBuffer* stream_buffer); + +/** + * @brief Set trigger level for stream buffer. + * A stream buffer's trigger level is the number of bytes that must be in the + * stream buffer before a task that is blocked on the stream buffer to + * wait for data is moved out of the blocked state. + * + * @param stream_buffer The stream buffer instance + * @param trigger_level The new trigger level for the stream buffer. + * @return true if trigger level can be be updated (new trigger level was less than or equal to the stream buffer's length). + * @return false if trigger level can't be be updated (new trigger level was greater than the stream buffer's length). + */ +bool furi_stream_set_trigger_level(FuriStreamBuffer* stream_buffer, size_t trigger_level); + +/** + * @brief Sends bytes to a stream buffer. The bytes are copied into the stream buffer. + * Wakes up task waiting for data to become available if called from ISR. + * + * @param stream_buffer The stream buffer instance. + * @param data A pointer to the data that is to be copied into the stream buffer. + * @param length The maximum number of bytes to copy from data into the stream buffer. + * @param timeout The maximum amount of time the task should remain in the + * Blocked state to wait for space to become available if the stream buffer is full. + * Will return immediately if timeout is zero. + * Setting timeout to FuriWaitForever will cause the task to wait indefinitely. + * Ignored if called from ISR. + * @return The number of bytes actually written to the stream buffer. + */ +size_t furi_stream_buffer_send( + FuriStreamBuffer* stream_buffer, + const void* data, + size_t length, + uint32_t timeout); + +/** + * @brief Receives bytes from a stream buffer. + * Wakes up task waiting for space to become available if called from ISR. + * + * @param stream_buffer The stream buffer instance. + * @param data A pointer to the buffer into which the received bytes will be + * copied. + * @param length The length of the buffer pointed to by the data parameter. + * @param timeout The maximum amount of time the task should remain in the + * Blocked state to wait for data to become available if the stream buffer is empty. + * Will return immediately if timeout is zero. + * Setting timeout to FuriWaitForever will cause the task to wait indefinitely. + * Ignored if called from ISR. + * @return The number of bytes read from the stream buffer, if any. + */ +size_t furi_stream_buffer_receive( + FuriStreamBuffer* stream_buffer, + void* data, + size_t length, + uint32_t timeout); + +/** + * @brief Queries a stream buffer to see how much data it contains, which is equal to + * the number of bytes that can be read from the stream buffer before the stream + * buffer would be empty. + * + * @param stream_buffer The stream buffer instance. + * @return The number of bytes that can be read from the stream buffer before + * the stream buffer would be empty. + */ +size_t furi_stream_buffer_bytes_available(FuriStreamBuffer* stream_buffer); + +/** + * @brief Queries a stream buffer to see how much free space it contains, which is + * equal to the amount of data that can be sent to the stream buffer before it + * is full. + * + * @param stream_buffer The stream buffer instance. + * @return The number of bytes that can be written to the stream buffer before + * the stream buffer would be full. + */ +size_t furi_stream_buffer_spaces_available(FuriStreamBuffer* stream_buffer); + +/** + * @brief Queries a stream buffer to see if it is full. + * + * @param stream_buffer stream buffer instance. + * @return true if the stream buffer is full. + * @return false if the stream buffer is not full. + */ +bool furi_stream_buffer_is_full(FuriStreamBuffer* stream_buffer); + +/** + * @brief Queries a stream buffer to see if it is empty. + * + * @param stream_buffer The stream buffer instance. + * @return true if the stream buffer is empty. + * @return false if the stream buffer is not empty. + */ +bool furi_stream_buffer_is_empty(FuriStreamBuffer* stream_buffer); + +/** + * @brief Resets a stream buffer to its initial, empty, state. Any data that was + * in the stream buffer is discarded. A stream buffer can only be reset if there + * are no tasks blocked waiting to either send to or receive from the stream buffer. + * + * @param stream_buffer The stream buffer instance. + * @return FuriStatusOk if the stream buffer is reset. + * @return FuriStatusError if there was a task blocked waiting to send to or read + * from the stream buffer then the stream buffer is not reset. + */ +FuriStatus furi_stream_buffer_reset(FuriStreamBuffer* stream_buffer); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/furi/core/string.c b/furi/core/string.c new file mode 100644 index 00000000000..682c8d40977 --- /dev/null +++ b/furi/core/string.c @@ -0,0 +1,304 @@ +#include "string.h" +#include + +struct FuriString { + string_t string; +}; + +#undef furi_string_alloc_set +#undef furi_string_set +#undef furi_string_cmp +#undef furi_string_cmpi +#undef furi_string_search +#undef furi_string_search_str +#undef furi_string_equal +#undef furi_string_replace +#undef furi_string_replace_str +#undef furi_string_replace_all +#undef furi_string_start_with +#undef furi_string_end_with +#undef furi_string_search_char +#undef furi_string_search_rchar +#undef furi_string_trim +#undef furi_string_cat + +FuriString* furi_string_alloc() { + FuriString* string = malloc(sizeof(FuriString)); + string_init(string->string); + return string; +} + +FuriString* furi_string_alloc_set(const FuriString* s) { + FuriString* string = malloc(sizeof(FuriString)); //-V799 + string_init_set(string->string, s->string); + return string; +} //-V773 + +FuriString* furi_string_alloc_set_str(const char cstr[]) { + FuriString* string = malloc(sizeof(FuriString)); //-V799 + string_init_set(string->string, cstr); + return string; +} //-V773 + +FuriString* furi_string_alloc_printf(const char format[], ...) { + va_list args; + va_start(args, format); + FuriString* string = furi_string_alloc_vprintf(format, args); + va_end(args); + return string; +} + +FuriString* furi_string_alloc_vprintf(const char format[], va_list args) { + FuriString* string = malloc(sizeof(FuriString)); + string_init_vprintf(string->string, format, args); + return string; +} + +FuriString* furi_string_alloc_move(FuriString* s) { + FuriString* string = malloc(sizeof(FuriString)); + string_init_move(string->string, s->string); + free(s); + return string; +} + +void furi_string_free(FuriString* s) { + string_clear(s->string); + free(s); +} + +void furi_string_reserve(FuriString* s, size_t alloc) { + string_reserve(s->string, alloc); +} + +void furi_string_reset(FuriString* s) { + string_reset(s->string); +} + +void furi_string_swap(FuriString* v1, FuriString* v2) { + string_swap(v1->string, v2->string); +} + +void furi_string_move(FuriString* v1, FuriString* v2) { + string_clear(v1->string); + string_init_move(v1->string, v2->string); + free(v2); +} + +size_t furi_string_hash(const FuriString* v) { + return string_hash(v->string); +} + +char furi_string_get_char(const FuriString* v, size_t index) { + return string_get_char(v->string, index); +} + +const char* furi_string_get_cstr(const FuriString* s) { + return string_get_cstr(s->string); +} + +void furi_string_set(FuriString* s, FuriString* source) { + string_set(s->string, source->string); +} + +void furi_string_set_str(FuriString* s, const char cstr[]) { + string_set(s->string, cstr); +} + +void furi_string_set_strn(FuriString* s, const char str[], size_t n) { + string_set_strn(s->string, str, n); +} + +void furi_string_set_char(FuriString* s, size_t index, const char c) { + string_set_char(s->string, index, c); +} + +int furi_string_cmp(const FuriString* s1, const FuriString* s2) { + return string_cmp(s1->string, s2->string); +} + +int furi_string_cmp_str(const FuriString* s1, const char str[]) { + return string_cmp(s1->string, str); +} + +int furi_string_cmpi(const FuriString* v1, const FuriString* v2) { + return string_cmpi(v1->string, v2->string); +} + +int furi_string_cmpi_str(const FuriString* v1, const char p2[]) { + return string_cmpi_str(v1->string, p2); +} + +size_t furi_string_search(const FuriString* v, const FuriString* needle, size_t start) { + return string_search(v->string, needle->string, start); +} + +size_t furi_string_search_str(const FuriString* v, const char needle[], size_t start) { + return string_search(v->string, needle, start); +} + +bool furi_string_equal(const FuriString* v1, const FuriString* v2) { + return string_equal_p(v1->string, v2->string); +} + +bool furi_string_equal_str(const FuriString* v1, const char v2[]) { + return string_equal_p(v1->string, v2); +} + +void furi_string_push_back(FuriString* v, char c) { + string_push_back(v->string, c); +} + +size_t furi_string_size(const FuriString* s) { + return string_size(s->string); +} + +int furi_string_printf(FuriString* v, const char format[], ...) { + va_list args; + va_start(args, format); + int result = furi_string_vprintf(v, format, args); + va_end(args); + return result; +} + +int furi_string_vprintf(FuriString* v, const char format[], va_list args) { + return string_vprintf(v->string, format, args); +} + +int furi_string_cat_printf(FuriString* v, const char format[], ...) { + va_list args; + va_start(args, format); + int result = furi_string_cat_vprintf(v, format, args); + va_end(args); + return result; +} + +int furi_string_cat_vprintf(FuriString* v, const char format[], va_list args) { + FuriString* string = furi_string_alloc(); + int ret = furi_string_vprintf(string, format, args); + furi_string_cat(v, string); + furi_string_free(string); + return ret; +} + +bool furi_string_empty(const FuriString* v) { + return string_empty_p(v->string); +} + +void furi_string_replace_at(FuriString* v, size_t pos, size_t len, const char str2[]) { + string_replace_at(v->string, pos, len, str2); +} + +size_t + furi_string_replace(FuriString* string, FuriString* needle, FuriString* replace, size_t start) { + return string_replace(string->string, needle->string, replace->string, start); +} + +size_t furi_string_replace_str(FuriString* v, const char str1[], const char str2[], size_t start) { + return string_replace_str(v->string, str1, str2, start); +} + +void furi_string_replace_all_str(FuriString* v, const char str1[], const char str2[]) { + string_replace_all_str(v->string, str1, str2); +} + +void furi_string_replace_all(FuriString* v, const FuriString* str1, const FuriString* str2) { + string_replace_all(v->string, str1->string, str2->string); +} + +bool furi_string_start_with(const FuriString* v, const FuriString* v2) { + return string_start_with_string_p(v->string, v2->string); +} + +bool furi_string_start_with_str(const FuriString* v, const char str[]) { + return string_start_with_str_p(v->string, str); +} + +bool furi_string_end_with(const FuriString* v, const FuriString* v2) { + return string_end_with_string_p(v->string, v2->string); +} + +bool furi_string_end_with_str(const FuriString* v, const char str[]) { + return string_end_with_str_p(v->string, str); +} + +size_t furi_string_search_char(const FuriString* v, char c, size_t start) { + return string_search_char(v->string, c, start); +} + +size_t furi_string_search_rchar(const FuriString* v, char c, size_t start) { + return string_search_rchar(v->string, c, start); +} + +void furi_string_left(FuriString* v, size_t index) { + string_left(v->string, index); +} + +void furi_string_right(FuriString* v, size_t index) { + string_right(v->string, index); +} + +void furi_string_mid(FuriString* v, size_t index, size_t size) { + string_mid(v->string, index, size); +} + +void furi_string_trim(FuriString* v, const char charac[]) { + string_strim(v->string, charac); +} + +void furi_string_cat(FuriString* v, const FuriString* v2) { + string_cat(v->string, v2->string); +} + +void furi_string_cat_str(FuriString* v, const char str[]) { + string_cat(v->string, str); +} + +void furi_string_set_n(FuriString* v, const FuriString* ref, size_t offset, size_t length) { + string_set_n(v->string, ref->string, offset, length); +} + +size_t furi_string_utf8_length(FuriString* str) { + return string_length_u(str->string); +} + +void furi_string_utf8_push(FuriString* str, FuriStringUnicodeValue u) { + string_push_u(str->string, u); +} + +static m_str1ng_utf8_state_e furi_state_to_state(FuriStringUTF8State state) { + switch(state) { + case FuriStringUTF8StateStarting: + return M_STRING_UTF8_STARTING; + case FuriStringUTF8StateDecoding1: + return M_STRING_UTF8_DECODING_1; + case FuriStringUTF8StateDecoding2: + return M_STRING_UTF8_DECODING_2; + case FuriStringUTF8StateDecoding3: + return M_STRING_UTF8_DOCODING_3; + default: + return M_STRING_UTF8_ERROR; + } +} + +static FuriStringUTF8State state_to_furi_state(m_str1ng_utf8_state_e state) { + switch(state) { + case M_STRING_UTF8_STARTING: + return FuriStringUTF8StateStarting; + case M_STRING_UTF8_DECODING_1: + return FuriStringUTF8StateDecoding1; + case M_STRING_UTF8_DECODING_2: + return FuriStringUTF8StateDecoding2; + case M_STRING_UTF8_DOCODING_3: + return FuriStringUTF8StateDecoding3; + default: + return FuriStringUTF8StateError; + } +} + +void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnicodeValue* unicode) { + string_unicode_t m_u = *unicode; + m_str1ng_utf8_state_e m_state = furi_state_to_state(*state); + m_str1ng_utf8_decode(c, &m_state, &m_u); + *state = state_to_furi_state(m_state); + *unicode = m_u; +} diff --git a/furi/core/string.h b/furi/core/string.h new file mode 100644 index 00000000000..7529deacd7c --- /dev/null +++ b/furi/core/string.h @@ -0,0 +1,738 @@ +/** + * @file string.h + * Furi string primitive + */ +#pragma once + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Furi string failure constant. + */ +#define FURI_STRING_FAILURE ((size_t)-1) + +/** + * @brief Furi string primitive. + */ +typedef struct FuriString FuriString; + +//--------------------------------------------------------------------------- +// Constructors +//--------------------------------------------------------------------------- + +/** + * @brief Allocate new FuriString. + * @return FuriString* + */ +FuriString* furi_string_alloc(); + +/** + * @brief Allocate new FuriString and set it to string. + * Allocate & Set the string a to the string. + * @param source + * @return FuriString* + */ +FuriString* furi_string_alloc_set(const FuriString* source); + +/** + * @brief Allocate new FuriString and set it to C string. + * Allocate & Set the string a to the C string. + * @param cstr_source + * @return FuriString* + */ +FuriString* furi_string_alloc_set_str(const char cstr_source[]); + +/** + * @brief Allocate new FuriString and printf to it. + * Initialize and set a string to the given formatted value. + * @param format + * @param ... + * @return FuriString* + */ +FuriString* furi_string_alloc_printf(const char format[], ...) + _ATTRIBUTE((__format__(__printf__, 1, 2))); + +/** + * @brief Allocate new FuriString and printf to it. + * Initialize and set a string to the given formatted value. + * @param format + * @param args + * @return FuriString* + */ +FuriString* furi_string_alloc_vprintf(const char format[], va_list args); + +/** + * @brief Allocate new FuriString and move source string content to it. + * Allocate the string, set it to the other one, and destroy the other one. + * @param source + * @return FuriString* + */ +FuriString* furi_string_alloc_move(FuriString* source); + +//--------------------------------------------------------------------------- +// Destructors +//--------------------------------------------------------------------------- + +/** + * @brief Free FuriString. + * @param string + */ +void furi_string_free(FuriString* string); + +//--------------------------------------------------------------------------- +// String memory management +//--------------------------------------------------------------------------- + +/** + * @brief Reserve memory for string. + * Modify the string capacity to be able to handle at least 'alloc' characters (including final null char). + * @param string + * @param size + */ +void furi_string_reserve(FuriString* string, size_t size); + +/** + * @brief Reset string. + * Make the string empty. + * @param s + */ +void furi_string_reset(FuriString* string); + +/** + * @brief Swap two strings. + * Swap the two strings string_1 and string_2. + * @param string_1 + * @param string_2 + */ +void furi_string_swap(FuriString* string_1, FuriString* string_2); + +/** + * @brief Move string_2 content to string_1. + * Set the string to the other one, and destroy the other one. + * @param string_1 + * @param string_2 + */ +void furi_string_move(FuriString* string_1, FuriString* string_2); + +/** + * @brief Compute a hash for the string. + * @param string + * @return size_t + */ +size_t furi_string_hash(const FuriString* string); + +/** + * @brief Get string size (usually length, but not for UTF-8) + * @param string + * @return size_t + */ +size_t furi_string_size(const FuriString* string); + +/** + * @brief Check that string is empty or not + * @param string + * @return bool + */ +bool furi_string_empty(const FuriString* string); + +//--------------------------------------------------------------------------- +// Getters +//--------------------------------------------------------------------------- + +/** + * @brief Get the character at the given index. + * Return the selected character of the string. + * @param string + * @param index + * @return char + */ +char furi_string_get_char(const FuriString* string, size_t index); + +/** + * @brief Return the string view a classic C string. + * @param string + * @return const char* + */ +const char* furi_string_get_cstr(const FuriString* string); + +//--------------------------------------------------------------------------- +// Setters +//--------------------------------------------------------------------------- + +/** + * @brief Set the string to the other string. + * Set the string to the source string. + * @param string + * @param source + */ +void furi_string_set(FuriString* string, FuriString* source); + +/** + * @brief Set the string to the other C string. + * Set the string to the source C string. + * @param string + * @param source + */ +void furi_string_set_str(FuriString* string, const char source[]); + +/** + * @brief Set the string to the n first characters of the C string. + * @param string + * @param source + * @param length + */ +void furi_string_set_strn(FuriString* string, const char source[], size_t length); + +/** + * @brief Set the character at the given index. + * @param string + * @param index + * @param c + */ +void furi_string_set_char(FuriString* string, size_t index, const char c); + +/** + * @brief Set the string to the n first characters of other one. + * @param string + * @param source + * @param offset + * @param length + */ +void furi_string_set_n(FuriString* string, const FuriString* source, size_t offset, size_t length); + +/** + * @brief Format in the string the given printf format + * @param string + * @param format + * @param ... + * @return int + */ +int furi_string_printf(FuriString* string, const char format[], ...) + _ATTRIBUTE((__format__(__printf__, 2, 3))); + +/** + * @brief Format in the string the given printf format + * @param string + * @param format + * @param args + * @return int + */ +int furi_string_vprintf(FuriString* string, const char format[], va_list args); + +//--------------------------------------------------------------------------- +// Appending +//--------------------------------------------------------------------------- + +/** + * @brief Append a character to the string. + * @param string + * @param c + */ +void furi_string_push_back(FuriString* string, char c); + +/** + * @brief Append a string to the string. + * Concatenate the string with the other string. + * @param string_1 + * @param string_2 + */ +void furi_string_cat(FuriString* string_1, const FuriString* string_2); + +/** + * @brief Append a C string to the string. + * Concatenate the string with the C string. + * @param string_1 + * @param cstring_2 + */ +void furi_string_cat_str(FuriString* string_1, const char cstring_2[]); + +/** + * @brief Append to the string the formatted string of the given printf format. + * @param string + * @param format + * @param ... + * @return int + */ +int furi_string_cat_printf(FuriString* string, const char format[], ...) + _ATTRIBUTE((__format__(__printf__, 2, 3))); + +/** + * @brief Append to the string the formatted string of the given printf format. + * @param string + * @param format + * @param args + * @return int + */ +int furi_string_cat_vprintf(FuriString* string, const char format[], va_list args); + +//--------------------------------------------------------------------------- +// Comparators +//--------------------------------------------------------------------------- + +/** + * @brief Compare two strings and return the sort order. + * @param string_1 + * @param string_2 + * @return int + */ +int furi_string_cmp(const FuriString* string_1, const FuriString* string_2); + +/** + * @brief Compare string with C string and return the sort order. + * @param string_1 + * @param cstring_2 + * @return int + */ +int furi_string_cmp_str(const FuriString* string_1, const char cstring_2[]); + +/** + * @brief Compare two strings (case insensitive according to the current locale) and return the sort order. + * Note: doesn't work with UTF-8 strings. + * @param string_1 + * @param string_2 + * @return int + */ +int furi_string_cmpi(const FuriString* string_1, const FuriString* string_2); + +/** + * @brief Compare string with C string (case insensitive according to the current locale) and return the sort order. + * Note: doesn't work with UTF-8 strings. + * @param string_1 + * @param cstring_2 + * @return int + */ +int furi_string_cmpi_str(const FuriString* string_1, const char cstring_2[]); + +//--------------------------------------------------------------------------- +// Search +//--------------------------------------------------------------------------- + +/** + * @brief Search the first occurrence of the needle in the string from the position start. + * Return STRING_FAILURE if not found. + * By default, start is zero. + * @param string + * @param needle + * @param start + * @return size_t + */ +size_t furi_string_search(const FuriString* string, const FuriString* needle, size_t start); + +/** + * @brief Search the first occurrence of the needle in the string from the position start. + * Return STRING_FAILURE if not found. + * @param string + * @param needle + * @param start + * @return size_t + */ +size_t furi_string_search_str(const FuriString* string, const char needle[], size_t start); + +/** + * @brief Search for the position of the character c from the position start (include) in the string. + * Return STRING_FAILURE if not found. + * By default, start is zero. + * @param string + * @param c + * @param start + * @return size_t + */ +size_t furi_string_search_char(const FuriString* string, char c, size_t start); + +/** + * @brief Reverse search for the position of the character c from the position start (include) in the string. + * Return STRING_FAILURE if not found. + * By default, start is zero. + * @param string + * @param c + * @param start + * @return size_t + */ +size_t furi_string_search_rchar(const FuriString* string, char c, size_t start); + +//--------------------------------------------------------------------------- +// Equality +//--------------------------------------------------------------------------- + +/** + * @brief Test if two strings are equal. + * @param string_1 + * @param string_2 + * @return bool + */ +bool furi_string_equal(const FuriString* string_1, const FuriString* string_2); + +/** + * @brief Test if the string is equal to the C string. + * @param string_1 + * @param cstring_2 + * @return bool + */ +bool furi_string_equal_str(const FuriString* string_1, const char cstring_2[]); + +//--------------------------------------------------------------------------- +// Replace +//--------------------------------------------------------------------------- + +/** + * @brief Replace in the string the sub-string at position 'pos' for 'len' bytes into the C string 'replace'. + * @param string + * @param pos + * @param len + * @param replace + */ +void furi_string_replace_at(FuriString* string, size_t pos, size_t len, const char replace[]); + +/** + * @brief Replace a string 'needle' to string 'replace' in a string from 'start' position. + * By default, start is zero. + * Return STRING_FAILURE if 'needle' not found or replace position. + * @param string + * @param needle + * @param replace + * @param start + * @return size_t + */ +size_t + furi_string_replace(FuriString* string, FuriString* needle, FuriString* replace, size_t start); + +/** + * @brief Replace a C string 'needle' to C string 'replace' in a string from 'start' position. + * By default, start is zero. + * Return STRING_FAILURE if 'needle' not found or replace position. + * @param string + * @param needle + * @param replace + * @param start + * @return size_t + */ +size_t furi_string_replace_str( + FuriString* string, + const char needle[], + const char replace[], + size_t start); + +/** + * @brief Replace all occurrences of 'needle' string into 'replace' string. + * @param string + * @param needle + * @param replace + */ +void furi_string_replace_all( + FuriString* string, + const FuriString* needle, + const FuriString* replace); + +/** + * @brief Replace all occurrences of 'needle' C string into 'replace' C string. + * @param string + * @param needle + * @param replace + */ +void furi_string_replace_all_str(FuriString* string, const char needle[], const char replace[]); + +//--------------------------------------------------------------------------- +// Start / End tests +//--------------------------------------------------------------------------- + +/** + * @brief Test if the string starts with the given string. + * @param string + * @param start + * @return bool + */ +bool furi_string_start_with(const FuriString* string, const FuriString* start); + +/** + * @brief Test if the string starts with the given C string. + * @param string + * @param start + * @return bool + */ +bool furi_string_start_with_str(const FuriString* string, const char start[]); + +/** + * @brief Test if the string ends with the given string. + * @param string + * @param end + * @return bool + */ +bool furi_string_end_with(const FuriString* string, const FuriString* end); + +/** + * @brief Test if the string ends with the given C string. + * @param string + * @param end + * @return bool + */ +bool furi_string_end_with_str(const FuriString* string, const char end[]); + +//--------------------------------------------------------------------------- +// Trim +//--------------------------------------------------------------------------- + +/** + * @brief Trim the string left to the first 'index' bytes. + * @param string + * @param index + */ +void furi_string_left(FuriString* string, size_t index); + +/** + * @brief Trim the string right from the 'index' position to the last position. + * @param string + * @param index + */ +void furi_string_right(FuriString* string, size_t index); + +/** + * @brief Trim the string from position index to size bytes. + * See also furi_string_set_n. + * @param string + * @param index + * @param size + */ +void furi_string_mid(FuriString* string, size_t index, size_t size); + +/** + * @brief Trim a string from the given set of characters (default is " \n\r\t"). + * @param string + * @param chars + */ +void furi_string_trim(FuriString* string, const char chars[]); + +//--------------------------------------------------------------------------- +// UTF8 +//--------------------------------------------------------------------------- + +/** + * @brief An unicode value. + */ +typedef unsigned int FuriStringUnicodeValue; + +/** + * @brief Compute the length in UTF8 characters in the string. + * @param string + * @return size_t + */ +size_t furi_string_utf8_length(FuriString* string); + +/** + * @brief Push unicode into string, encoding it in UTF8. + * @param string + * @param unicode + */ +void furi_string_utf8_push(FuriString* string, FuriStringUnicodeValue unicode); + +/** + * @brief State of the UTF8 decoding machine state. + */ +typedef enum { + FuriStringUTF8StateStarting, + FuriStringUTF8StateDecoding1, + FuriStringUTF8StateDecoding2, + FuriStringUTF8StateDecoding3, + FuriStringUTF8StateError +} FuriStringUTF8State; + +/** + * @brief Main generic UTF8 decoder. + * It takes a character, and the previous state and the previous value of the unicode value. + * It updates the state and the decoded unicode value. + * A decoded unicode encoded value is valid only when the state is FuriStringUTF8StateStarting. + * @param c + * @param state + * @param unicode + */ +void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnicodeValue* unicode); + +//--------------------------------------------------------------------------- +// Lasciate ogne speranza, voi ch’entrate +//--------------------------------------------------------------------------- + +/** + * + * Select either the string function or the str function depending on + * the b operand to the function. + * func1 is the string function / func2 is the str function. + */ + +/** + * @brief Select for 1 argument + */ +#define FURI_STRING_SELECT1(func1, func2, a) \ + _Generic((a), char* : func2, const char* : func2, FuriString* : func1, const FuriString* : func1)(a) + +/** + * @brief Select for 2 arguments + */ +#define FURI_STRING_SELECT2(func1, func2, a, b) \ + _Generic((b), char* : func2, const char* : func2, FuriString* : func1, const FuriString* : func1)(a, b) + +/** + * @brief Select for 3 arguments + */ +#define FURI_STRING_SELECT3(func1, func2, a, b, c) \ + _Generic((b), char* : func2, const char* : func2, FuriString* : func1, const FuriString* : func1)(a, b, c) + +/** + * @brief Select for 4 arguments + */ +#define FURI_STRING_SELECT4(func1, func2, a, b, c, d) \ + _Generic((b), char* : func2, const char* : func2, FuriString* : func1, const FuriString* : func1)(a, b, c, d) + +/** + * @brief Allocate new FuriString and set it content to string (or C string). + * ([c]string) + */ +#define furi_string_alloc_set(a) \ + FURI_STRING_SELECT1(furi_string_alloc_set, furi_string_alloc_set_str, a) + +/** + * @brief Set the string content to string (or C string). + * (string, [c]string) + */ +#define furi_string_set(a, b) FURI_STRING_SELECT2(furi_string_set, furi_string_set_str, a, b) + +/** + * @brief Compare string with string (or C string) and return the sort order. + * Note: doesn't work with UTF-8 strings. + * (string, [c]string) + */ +#define furi_string_cmp(a, b) FURI_STRING_SELECT2(furi_string_cmp, furi_string_cmp_str, a, b) + +/** + * @brief Compare string with string (or C string) (case insensitive according to the current locale) and return the sort order. + * Note: doesn't work with UTF-8 strings. + * (string, [c]string) + */ +#define furi_string_cmpi(a, b) FURI_STRING_SELECT2(furi_string_cmpi, furi_string_cmpi_str, a, b) + +/** + * @brief Test if the string is equal to the string (or C string). + * (string, [c]string) + */ +#define furi_string_equal(a, b) FURI_STRING_SELECT2(furi_string_equal, furi_string_equal_str, a, b) + +/** + * @brief Replace all occurrences of string into string (or C string to another C string) in a string. + * (string, [c]string, [c]string) + */ +#define furi_string_replace_all(a, b, c) \ + FURI_STRING_SELECT3(furi_string_replace_all, furi_string_replace_all_str, a, b, c) + +/** + * @brief Search for a string (or C string) in a string + * (string, [c]string[, start=0]) + */ +#define furi_string_search(...) \ + M_APPLY( \ + FURI_STRING_SELECT3, \ + furi_string_search, \ + furi_string_search_str, \ + M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) +/** + * @brief Search for a C string in a string + * (string, cstring[, start=0]) + */ +#define furi_string_search_str(...) furi_string_search_str(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) + +/** + * @brief Test if the string starts with the given string (or C string). + * (string, [c]string) + */ +#define furi_string_start_with(a, b) \ + FURI_STRING_SELECT2(furi_string_start_with, furi_string_start_with_str, a, b) + +/** + * @brief Test if the string ends with the given string (or C string). + * (string, [c]string) + */ +#define furi_string_end_with(a, b) \ + FURI_STRING_SELECT2(furi_string_end_with, furi_string_end_with_str, a, b) + +/** + * @brief Append a string (or C string) to the string. + * (string, [c]string) + */ +#define furi_string_cat(a, b) FURI_STRING_SELECT2(furi_string_cat, furi_string_cat_str, a, b) + +/** + * @brief Trim a string from the given set of characters (default is " \n\r\t"). + * (string[, set=" \n\r\t"]) + */ +#define furi_string_trim(...) furi_string_trim(M_DEFAULT_ARGS(2, (" \n\r\t"), __VA_ARGS__)) + +/** + * @brief Search for a character in a string. + * (string, character[, start=0]) + */ +#define furi_string_search_char(...) furi_string_search_char(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) + +/** + * @brief Reverse Search for a character in a string. + * (string, character[, start=0]) + */ +#define furi_string_search_rchar(...) furi_string_search_rchar(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) + +/** + * @brief Replace a string to another string (or C string to another C string) in a string. + * (string, [c]string, [c]string[, start=0]) + */ +#define furi_string_replace(...) \ + M_APPLY( \ + FURI_STRING_SELECT4, \ + furi_string_replace, \ + furi_string_replace_str, \ + M_DEFAULT_ARGS(4, (0), __VA_ARGS__)) + +/** + * @brief Replace a C string to another C string in a string. + * (string, cstring, cstring[, start=0]) + */ +#define furi_string_replace_str(...) furi_string_replace_str(M_DEFAULT_ARGS(4, (0), __VA_ARGS__)) + +/** + * @brief INIT OPLIST for FuriString. + */ +#define F_STR_INIT(a) ((a) = furi_string_alloc()) + +/** + * @brief INIT SET OPLIST for FuriString. + */ +#define F_STR_INIT_SET(a, b) ((a) = furi_string_alloc_set(b)) + +/** + * @brief INIT MOVE OPLIST for FuriString. + */ +#define F_STR_INIT_MOVE(a, b) ((a) = furi_string_alloc_move(b)) + +/** + * @brief OPLIST for FuriString. + */ +#define FURI_STRING_OPLIST \ + (INIT(F_STR_INIT), \ + INIT_SET(F_STR_INIT_SET), \ + SET(furi_string_set), \ + INIT_MOVE(F_STR_INIT_MOVE), \ + MOVE(furi_string_move), \ + SWAP(furi_string_swap), \ + RESET(furi_string_reset), \ + EMPTY_P(furi_string_empty), \ + CLEAR(furi_string_free), \ + HASH(furi_string_hash), \ + EQUAL(furi_string_equal), \ + CMP(furi_string_cmp), \ + TYPE(FuriString*)) + +#ifdef __cplusplus +} +#endif diff --git a/furi/core/thread.c b/furi/core/thread.c index 044f83711b6..db4feeb4e16 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -5,18 +5,24 @@ #include "check.h" #include "common_defines.h" #include "mutex.h" +#include "string.h" -#include -#include +#include "log.h" +#include #include +#include +#include + +#define TAG "FuriThread" + #define THREAD_NOTIFY_INDEX 1 // Index 0 is used for stream buffers typedef struct FuriThreadStdout FuriThreadStdout; struct FuriThreadStdout { FuriThreadStdoutWriteCallback write_callback; - string_t buffer; + FuriString* buffer; }; struct FuriThread { @@ -30,23 +36,33 @@ struct FuriThread { void* state_context; char* name; - configSTACK_DEPTH_TYPE stack_size; + char* appid; + FuriThreadPriority priority; TaskHandle_t task_handle; - bool heap_trace_enabled; size_t heap_size; FuriThreadStdout output; + + // Keep all non-alignable byte types in one place, + // this ensures that the size of this structure is minimal + bool is_service; + bool heap_trace_enabled; + + configSTACK_DEPTH_TYPE stack_size; }; static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size); static int32_t __furi_thread_stdout_flush(FuriThread* thread); /** Catch threads that are trying to exit wrong way */ -__attribute__((__noreturn__)) void furi_thread_catch() { +__attribute__((__noreturn__)) void furi_thread_catch() { //-V1082 + // If you're here it means you're probably doing something wrong + // with critical sections or with scheduler state asm volatile("nop"); // extra magic - furi_crash("You are doing it wrong"); + furi_crash("You are doing it wrong"); //-V779 + __builtin_unreachable(); } static void furi_thread_set_state(FuriThread* thread, FuriThreadState state) { @@ -78,34 +94,88 @@ static void furi_thread_body(void* context) { if(thread->heap_trace_enabled == true) { furi_delay_ms(33); thread->heap_size = memmgr_heap_get_thread_memory((FuriThreadId)task_handle); + furi_log_print_format( + thread->heap_size ? FuriLogLevelError : FuriLogLevelInfo, + TAG, + "%s allocation balance: %zu", + thread->name ? thread->name : "Thread", + thread->heap_size); memmgr_heap_disable_thread_trace((FuriThreadId)task_handle); } furi_assert(thread->state == FuriThreadStateRunning); - furi_thread_set_state(thread, FuriThreadStateStopped); - // clear thread local storage + if(thread->is_service) { + FURI_LOG_W( + TAG, + "%s service thread TCB memory will not be reclaimed", + thread->name ? thread->name : ""); + } + + // flush stdout __furi_thread_stdout_flush(thread); - furi_assert(pvTaskGetThreadLocalStoragePointer(NULL, 0) != NULL); - vTaskSetThreadLocalStoragePointer(NULL, 0, NULL); - vTaskDelete(thread->task_handle); + furi_thread_set_state(thread, FuriThreadStateStopped); + + vTaskDelete(NULL); furi_thread_catch(); } FuriThread* furi_thread_alloc() { FuriThread* thread = malloc(sizeof(FuriThread)); - string_init(thread->output.buffer); + 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()) { + if(parent) thread->heap_trace_enabled = parent->heap_trace_enabled; + } else { + thread->heap_trace_enabled = false; + } return thread; } +FuriThread* furi_thread_alloc_ex( + const char* name, + uint32_t stack_size, + FuriThreadCallback callback, + void* context) { + FuriThread* thread = furi_thread_alloc(); + furi_thread_set_name(thread, name); + furi_thread_set_stack_size(thread, stack_size); + furi_thread_set_callback(thread, callback); + furi_thread_set_context(thread, context); + return thread; +} + void furi_thread_free(FuriThread* thread) { furi_assert(thread); + + // Ensure that use join before free furi_assert(thread->state == FuriThreadStateStopped); + furi_assert(thread->task_handle == NULL); - if(thread->name) free((void*)thread->name); - string_clear(thread->output.buffer); + if(thread->name) free(thread->name); + if(thread->appid) free(thread->appid); + furi_string_free(thread->output.buffer); free(thread); } @@ -113,10 +183,21 @@ void furi_thread_free(FuriThread* thread) { void furi_thread_set_name(FuriThread* thread, const char* name) { furi_assert(thread); furi_assert(thread->state == FuriThreadStateStopped); - if(thread->name) free((void*)thread->name); + if(thread->name) free(thread->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(thread->appid); + thread->appid = appid ? strdup(appid) : NULL; +} + +void furi_thread_mark_as_service(FuriThread* thread) { + thread->is_service = true; +} + void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size) { furi_assert(thread); furi_assert(thread->state == FuriThreadStateStopped); @@ -143,6 +224,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); @@ -164,30 +254,54 @@ void furi_thread_start(FuriThread* thread) { furi_assert(thread); furi_assert(thread->callback); furi_assert(thread->state == FuriThreadStateStopped); - furi_assert(thread->stack_size > 0 && thread->stack_size < 0xFFFF * 4); + furi_assert(thread->stack_size > 0 && thread->stack_size < (UINT16_MAX * sizeof(StackType_t))); furi_thread_set_state(thread, FuriThreadStateStarting); - BaseType_t ret = xTaskCreate( - furi_thread_body, - thread->name, - thread->stack_size / 4, - thread, - thread->priority ? thread->priority : FuriThreadPriorityNormal, - &thread->task_handle); + uint32_t stack = thread->stack_size / sizeof(StackType_t); + UBaseType_t priority = thread->priority ? thread->priority : FuriThreadPriorityNormal; + if(thread->is_service) { + thread->task_handle = xTaskCreateStatic( + furi_thread_body, + thread->name, + stack, + thread, + priority, + memmgr_alloc_from_pool(sizeof(StackType_t) * stack), + memmgr_alloc_from_pool(sizeof(StaticTask_t))); + } else { + BaseType_t ret = xTaskCreate( + furi_thread_body, thread->name, stack, thread, priority, &thread->task_handle); + furi_check(ret == pdPASS); + } - furi_check(ret == pdPASS); furi_check(thread->task_handle); } +void furi_thread_cleanup_tcb_event(TaskHandle_t task) { + FuriThread* thread = pvTaskGetThreadLocalStoragePointer(task, 0); + if(thread) { + // clear thread local storage + vTaskSetThreadLocalStoragePointer(task, 0, NULL); + furi_assert(thread->task_handle == task); + thread->task_handle = NULL; + } +} + bool furi_thread_join(FuriThread* thread) { furi_assert(thread); - while(thread->state != FuriThreadStateStopped) { + furi_check(furi_thread_get_current() != thread); + + // !!! IMPORTANT NOTICE !!! + // + // If your thread exited, but your app stuck here: some other thread uses + // all cpu time, which delays kernel from releasing task handle + while(thread->task_handle) { furi_delay_ms(10); } - return FuriStatusOk; + return true; } FuriThreadId furi_thread_get_id(FuriThread* thread) { @@ -198,14 +312,12 @@ FuriThreadId furi_thread_get_id(FuriThread* thread) { void furi_thread_enable_heap_trace(FuriThread* thread) { furi_assert(thread); furi_assert(thread->state == FuriThreadStateStopped); - furi_assert(thread->heap_trace_enabled == false); thread->heap_trace_enabled = true; } void furi_thread_disable_heap_trace(FuriThread* thread) { furi_assert(thread); furi_assert(thread->state == FuriThreadStateStopped); - furi_assert(thread->heap_trace_enabled == true); thread->heap_trace_enabled = false; } @@ -227,7 +339,6 @@ FuriThreadId furi_thread_get_current_id() { FuriThread* furi_thread_get_current() { FuriThread* thread = pvTaskGetThreadLocalStoragePointer(NULL, 0); - furi_assert(thread != NULL); return thread; } @@ -428,6 +539,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; @@ -451,27 +576,31 @@ static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, s } static int32_t __furi_thread_stdout_flush(FuriThread* thread) { - string_ptr buffer = thread->output.buffer; - size_t size = string_size(buffer); + FuriString* buffer = thread->output.buffer; + size_t size = furi_string_size(buffer); if(size > 0) { - __furi_thread_stdout_write(thread, string_get_cstr(buffer), size); - string_reset(buffer); + __furi_thread_stdout_write(thread, furi_string_get_cstr(buffer), size); + furi_string_reset(buffer); } return 0; } -bool furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback) { +void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback) { FuriThread* thread = furi_thread_get_current(); - + furi_assert(thread); __furi_thread_stdout_flush(thread); thread->output.write_callback = callback; +} - return true; +FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback() { + FuriThread* thread = furi_thread_get_current(); + furi_assert(thread); + return thread->output.write_callback; } size_t furi_thread_stdout_write(const char* data, size_t size) { FuriThread* thread = furi_thread_get_current(); - + furi_assert(thread); if(size == 0 || data == NULL) { return __furi_thread_stdout_flush(thread); } else { @@ -482,7 +611,7 @@ size_t furi_thread_stdout_write(const char* data, size_t size) { } else { // string_cat doesn't work here because we need to write the exact size data for(size_t i = 0; i < size; i++) { - string_push_back(thread->output.buffer, data[i]); + furi_string_push_back(thread->output.buffer, data[i]); if(data[i] == '\n') { __furi_thread_stdout_flush(thread); } @@ -494,5 +623,26 @@ size_t furi_thread_stdout_write(const char* data, size_t size) { } int32_t furi_thread_stdout_flush() { - return __furi_thread_stdout_flush(furi_thread_get_current()); -} \ No newline at end of file + FuriThread* thread = furi_thread_get_current(); + furi_assert(thread); + return __furi_thread_stdout_flush(thread); +} + +void furi_thread_suspend(FuriThreadId thread_id) { + TaskHandle_t hTask = (TaskHandle_t)thread_id; + vTaskSuspend(hTask); +} + +void furi_thread_resume(FuriThreadId thread_id) { + TaskHandle_t hTask = (TaskHandle_t)thread_id; + if(FURI_IS_IRQ_MODE()) { + xTaskResumeFromISR(hTask); + } else { + vTaskResume(hTask); + } +} + +bool furi_thread_is_suspended(FuriThreadId thread_id) { + TaskHandle_t hTask = (TaskHandle_t)thread_id; + return eTaskGetState(hTask) == eSuspended; +} diff --git a/furi/core/thread.h b/furi/core/thread.h index 7f746f03faa..44d66fb21a2 100644 --- a/furi/core/thread.h +++ b/furi/core/thread.h @@ -8,6 +8,9 @@ #include "base.h" #include "common_defines.h" +#include +#include + #ifdef __cplusplus extern "C" { #endif @@ -28,7 +31,8 @@ typedef enum { FuriThreadPriorityNormal = 16, /**< Normal */ FuriThreadPriorityHigh = 17, /**< High */ FuriThreadPriorityHighest = 18, /**< Highest */ - FuriThreadPriorityIsr = 32, /**< Deffered Isr (highest possible) */ + FuriThreadPriorityIsr = + (FURI_CONFIG_THREAD_MAX_PRIORITIES - 1), /**< Deferred ISR (highest possible) */ } FuriThreadPriority; /** FuriThread anonymous structure */ @@ -48,7 +52,7 @@ typedef int32_t (*FuriThreadCallback)(void* context); */ typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size); -/** FuriThread state change calback called upon thread state change +/** FuriThread state change callback called upon thread state change * @param state new thread state * @param context callback context */ @@ -60,7 +64,23 @@ typedef void (*FuriThreadStateCallback)(FuriThreadState state, void* context); */ FuriThread* furi_thread_alloc(); +/** Allocate FuriThread, shortcut version + * + * @param name + * @param stack_size + * @param callback + * @param context + * @return FuriThread* + */ +FuriThread* furi_thread_alloc_ex( + const char* name, + uint32_t stack_size, + FuriThreadCallback callback, + void* context); + /** Release FuriThread + * + * @warning see furi_thread_join * * @param thread FuriThread instance */ @@ -73,6 +93,23 @@ 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 + * + * @param thread + */ +void furi_thread_mark_as_service(FuriThread* thread); + /** Set FuriThread stack size * * @param thread FuriThread instance @@ -101,6 +138,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 @@ -130,6 +179,9 @@ FuriThreadState furi_thread_get_state(FuriThread* thread); void furi_thread_start(FuriThread* thread); /** Join FuriThread + * + * @warning Use this method only when CPU is not busy(Idle task receives + * control), otherwise it will wait forever. * * @param thread FuriThread instance * @@ -173,7 +225,7 @@ size_t furi_thread_get_heap_size(FuriThread* thread); */ int32_t furi_thread_get_return_code(FuriThread* thread); -/** Thread releated methods that doesn't involve FuriThread directly */ +/** Thread related methods that doesn't involve FuriThread directly */ /** Get FreeRTOS FuriThreadId for current thread * @@ -185,7 +237,7 @@ FuriThreadId furi_thread_get_current_id(); /** Get FuriThread instance for current thread * - * @return FuriThread* + * @return pointer to FuriThread or NULL if this thread doesn't belongs to Furi */ FuriThread* furi_thread_get_current(); @@ -200,19 +252,50 @@ 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 + * + * @return STDOUT callback + */ +FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(); + /** Set STDOUT callback for thread - * + * * @param callback callback or NULL to clear - * - * @return true on success, otherwise fail */ -bool furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback); +void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback); /** Write data to buffered STDOUT * @@ -229,6 +312,25 @@ size_t furi_thread_stdout_write(const char* data, size_t size); */ int32_t furi_thread_stdout_flush(); +/** Suspend thread + * + * @param thread_id thread id + */ +void furi_thread_suspend(FuriThreadId thread_id); + +/** Resume thread + * + * @param thread_id thread id + */ +void furi_thread_resume(FuriThreadId thread_id); + +/** Get thread suspended state + * + * @param thread_id thread id + * @return true if thread is suspended + */ +bool furi_thread_is_suspended(FuriThreadId thread_id); + #ifdef __cplusplus } #endif diff --git a/furi/core/timer.c b/furi/core/timer.c index 807f477e4ec..027e4cf40db 100644 --- a/furi/core/timer.c +++ b/furi/core/timer.c @@ -1,7 +1,8 @@ #include "timer.h" #include "check.h" +#include "memmgr.h" +#include "kernel.h" -#include "core/common_defines.h" #include #include @@ -25,56 +26,40 @@ static void TimerCallback(TimerHandle_t hTimer) { } FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context) { - furi_assert((furi_is_irq_context() == 0U) && (func != NULL)); + furi_assert((furi_kernel_is_irq_or_masked() == 0U) && (func != NULL)); TimerHandle_t hTimer; TimerCallback_t* callb; UBaseType_t reload; - uint32_t callb_dyn; hTimer = NULL; - callb = NULL; - callb_dyn = 0U; /* Dynamic memory allocation is available: if memory for callback and */ /* its context is not provided, allocate it from dynamic memory pool */ - if(callb == NULL) { - callb = (TimerCallback_t*)pvPortMalloc(sizeof(TimerCallback_t)); + callb = (TimerCallback_t*)malloc(sizeof(TimerCallback_t)); - if(callb != NULL) { - /* Callback memory was allocated from dynamic pool, set flag */ - callb_dyn = 1U; - } - } + callb->func = func; + callb->context = context; - if(callb != NULL) { - callb->func = func; - callb->context = context; - - if(type == FuriTimerTypeOnce) { - reload = pdFALSE; - } else { - reload = pdTRUE; - } - - /* Store callback memory dynamic allocation flag */ - callb = (TimerCallback_t*)((uint32_t)callb | callb_dyn); - // TimerCallback function is always provided as a callback and is used to call application - // specified function with its context both stored in structure callb. - hTimer = xTimerCreate(NULL, 1, reload, callb, TimerCallback); - if((hTimer == NULL) && (callb != NULL) && (callb_dyn == 1U)) { - /* Failed to create a timer, release allocated resources */ - callb = (TimerCallback_t*)((uint32_t)callb & ~1U); - vPortFree(callb); - } + if(type == FuriTimerTypeOnce) { + reload = pdFALSE; + } else { + reload = pdTRUE; } + /* Store callback memory dynamic allocation flag */ + callb = (TimerCallback_t*)((uint32_t)callb | 1U); + // TimerCallback function is always provided as a callback and is used to call application + // specified function with its context both stored in structure callb. + hTimer = xTimerCreate(NULL, portMAX_DELAY, reload, callb, TimerCallback); + furi_check(hTimer); + /* Return timer ID */ return ((FuriTimer*)hTimer); } void furi_timer_free(FuriTimer* instance) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(instance); TimerHandle_t hTimer = (TimerHandle_t)instance; @@ -82,20 +67,23 @@ void furi_timer_free(FuriTimer* instance) { callb = (TimerCallback_t*)pvTimerGetTimerID(hTimer); - if(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS) { - if((uint32_t)callb & 1U) { - /* Callback memory was allocated from dynamic pool, clear flag */ - callb = (TimerCallback_t*)((uint32_t)callb & ~1U); + furi_check(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS); + + while(furi_timer_is_running(instance)) furi_delay_tick(2); - /* Return allocated memory to dynamic pool */ - vPortFree(callb); - } + if((uint32_t)callb & 1U) { + /* Callback memory was allocated from dynamic pool, clear flag */ + callb = (TimerCallback_t*)((uint32_t)callb & ~1U); + + /* Return allocated memory to dynamic pool */ + free(callb); } } FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(instance); + furi_assert(ticks < portMAX_DELAY); TimerHandle_t hTimer = (TimerHandle_t)instance; FuriStatus stat; @@ -110,29 +98,38 @@ FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) { return (stat); } -FuriStatus furi_timer_stop(FuriTimer* instance) { - furi_assert(!furi_is_irq_context()); +FuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks) { + furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(instance); + furi_assert(ticks < portMAX_DELAY); TimerHandle_t hTimer = (TimerHandle_t)instance; FuriStatus stat; - if(xTimerIsTimerActive(hTimer) == pdFALSE) { - stat = FuriStatusErrorResource; + if(xTimerChangePeriod(hTimer, ticks, portMAX_DELAY) == pdPASS && + xTimerReset(hTimer, portMAX_DELAY) == pdPASS) { + stat = FuriStatusOk; } else { - if(xTimerStop(hTimer, portMAX_DELAY) == pdPASS) { - stat = FuriStatusOk; - } else { - stat = FuriStatusError; - } + stat = FuriStatusErrorResource; } /* Return execution status */ return (stat); } +FuriStatus furi_timer_stop(FuriTimer* instance) { + furi_assert(!furi_kernel_is_irq_or_masked()); + furi_assert(instance); + + TimerHandle_t hTimer = (TimerHandle_t)instance; + + furi_check(xTimerStop(hTimer, portMAX_DELAY) == pdPASS); + + return FuriStatusOk; +} + uint32_t furi_timer_is_running(FuriTimer* instance) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(instance); TimerHandle_t hTimer = (TimerHandle_t)instance; @@ -140,3 +137,37 @@ uint32_t furi_timer_is_running(FuriTimer* instance) { /* Return 0: not running, 1: running */ return (uint32_t)xTimerIsTimerActive(hTimer); } + +uint32_t furi_timer_get_expire_time(FuriTimer* instance) { + furi_assert(!furi_kernel_is_irq_or_masked()); + furi_assert(instance); + + TimerHandle_t hTimer = (TimerHandle_t)instance; + + return (uint32_t)xTimerGetExpiryTime(hTimer); +} + +void furi_timer_pending_callback(FuriTimerPendigCallback callback, void* context, uint32_t arg) { + BaseType_t ret = pdFAIL; + if(furi_kernel_is_irq_or_masked()) { + ret = xTimerPendFunctionCallFromISR(callback, context, arg, NULL); + } else { + ret = xTimerPendFunctionCall(callback, context, arg, FuriWaitForever); + } + furi_check(ret == pdPASS); +} + +void furi_timer_set_thread_priority(FuriTimerThreadPriority priority) { + furi_assert(!furi_kernel_is_irq_or_masked()); + + TaskHandle_t task_handle = xTimerGetTimerDaemonTaskHandle(); + furi_check(task_handle); // Don't call this method before timer task start + + if(priority == FuriTimerThreadPriorityNormal) { + vTaskPrioritySet(task_handle, configTIMER_TASK_PRIORITY); + } else if(priority == FuriTimerThreadPriorityElevated) { + vTaskPrioritySet(task_handle, configMAX_PRIORITIES - 1); + } else { + furi_crash(); + } +} \ No newline at end of file diff --git a/furi/core/timer.h b/furi/core/timer.h index e79c1868d90..f8f40c56267 100644 --- a/furi/core/timer.h +++ b/furi/core/timer.h @@ -32,15 +32,33 @@ FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* co void furi_timer_free(FuriTimer* instance); /** Start timer + * + * @warning This is asynchronous call, real operation will happen as soon as + * timer service process this request. * * @param instance The pointer to FuriTimer instance - * @param[in] ticks The ticks + * @param[in] ticks The interval in ticks * * @return The furi status. */ FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks); +/** Restart timer with previous timeout value + * + * @warning This is asynchronous call, real operation will happen as soon as + * timer service process this request. + * + * @param instance The pointer to FuriTimer instance + * @param[in] ticks The interval in ticks + * + * @return The furi status. + */ +FuriStatus furi_timer_restart(FuriTimer* instance, uint32_t ticks); + /** Stop timer + * + * @warning This is asynchronous call, real operation will happen as soon as + * timer service process this request. * * @param instance The pointer to FuriTimer instance * @@ -49,6 +67,10 @@ FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks); FuriStatus furi_timer_stop(FuriTimer* instance); /** Is timer running + * + * @warning This cal may and will return obsolete timer state if timer + * commands are still in the queue. Please read FreeRTOS timer + * documentation first. * * @param instance The pointer to FuriTimer instance * @@ -56,6 +78,29 @@ FuriStatus furi_timer_stop(FuriTimer* instance); */ uint32_t furi_timer_is_running(FuriTimer* instance); +/** Get timer expire time + * + * @param instance The Timer instance + * + * @return expire tick + */ +uint32_t furi_timer_get_expire_time(FuriTimer* instance); + +typedef void (*FuriTimerPendigCallback)(void* context, uint32_t arg); + +void furi_timer_pending_callback(FuriTimerPendigCallback callback, void* context, uint32_t arg); + +typedef enum { + FuriTimerThreadPriorityNormal, /**< Lower then other threads */ + FuriTimerThreadPriorityElevated, /**< Same as other threads */ +} FuriTimerThreadPriority; + +/** Set Timer thread priority + * + * @param[in] priority The priority + */ +void furi_timer_set_thread_priority(FuriTimerThreadPriority priority); + #ifdef __cplusplus } #endif diff --git a/furi/core/valuemutex.c b/furi/core/valuemutex.c deleted file mode 100644 index af2a0755c24..00000000000 --- 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 (unfortunatly 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 41762fdd392..00000000000 --- 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: infinitly 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); - } - - // continously 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 old mode 100755 new mode 100644 index d180acf73bd..a065f8d8862 --- a/furi/flipper.c +++ b/furi/flipper.c @@ -2,6 +2,10 @@ #include #include #include +#include +#include + +#include #define TAG "Flipper" @@ -28,20 +32,44 @@ 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); - - FuriThread* thread = furi_thread_alloc(); + FURI_LOG_D(TAG, "Starting service %s", FLIPPER_SERVICES[i].name); - furi_thread_set_name(thread, FLIPPER_SERVICES[i].name); - furi_thread_set_stack_size(thread, FLIPPER_SERVICES[i].stack_size); - furi_thread_set_callback(thread, FLIPPER_SERVICES[i].app); + FuriThread* thread = furi_thread_alloc_ex( + FLIPPER_SERVICES[i].name, + FLIPPER_SERVICES[i].stack_size, + FLIPPER_SERVICES[i].app, + NULL); + furi_thread_mark_as_service(thread); + furi_thread_set_appid(thread, FLIPPER_SERVICES[i].appid); furi_thread_start(thread); } +<<<<<<< HEAD FURI_LOG_I(TAG, "services startup complete"); furi_hal_version_load_custom_otp(); +======= + FURI_LOG_I(TAG, "Startup complete"); +>>>>>>> upstream/dev } + +void vApplicationGetIdleTaskMemory( + StaticTask_t** tcb_ptr, + StackType_t** stack_ptr, + uint32_t* stack_size) { + *tcb_ptr = memmgr_alloc_from_pool(sizeof(StaticTask_t)); + *stack_ptr = memmgr_alloc_from_pool(sizeof(StackType_t) * configIDLE_TASK_STACK_DEPTH); + *stack_size = configIDLE_TASK_STACK_DEPTH; +} + +void vApplicationGetTimerTaskMemory( + StaticTask_t** tcb_ptr, + StackType_t** stack_ptr, + uint32_t* stack_size) { + *tcb_ptr = memmgr_alloc_from_pool(sizeof(StaticTask_t)); + *stack_ptr = memmgr_alloc_from_pool(sizeof(StackType_t) * configTIMER_TASK_STACK_DEPTH); + *stack_size = configTIMER_TASK_STACK_DEPTH; +} \ No newline at end of file diff --git a/furi/furi.c b/furi/furi.c index 76aed024fa3..6247e259fb9 100644 --- a/furi/furi.c +++ b/furi/furi.c @@ -1,9 +1,11 @@ #include "furi.h" #include -#include "queue.h" + +#include +#include void furi_init() { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED); furi_log_init(); @@ -11,13 +13,13 @@ void furi_init() { } void furi_run() { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED); #if(__ARM_ARCH_7A__ == 0U) - /* Service Call interrupt might be configured before kernel start */ - /* and when its priority is lower or equal to BASEPRI, svc intruction */ - /* causes a Hard Fault. */ + /* Service Call interrupt might be configured before kernel start */ + /* and when its priority is lower or equal to BASEPRI, svc instruction */ + /* causes a Hard Fault. */ NVIC_SetPriority(SVCall_IRQn, 0U); #endif diff --git a/furi/furi.h b/furi/furi.h index 68914b502fd..422509055f7 100644 --- a/furi/furi.h +++ b/furi/furi.h @@ -2,26 +2,27 @@ #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "core/check.h" +#include "core/common_defines.h" +#include "core/event_flag.h" +#include "core/kernel.h" +#include "core/log.h" +#include "core/memmgr.h" +#include "core/memmgr_heap.h" +#include "core/message_queue.h" +#include "core/mutex.h" +#include "core/pubsub.h" +#include "core/record.h" +#include "core/semaphore.h" +#include "core/thread.h" +#include "core/timer.h" +#include "core/string.h" +#include "core/stream_buffer.h" #include -// FreeRTOS timer, REMOVE AFTER REFACTORING -#include +// Workaround for math.h leaking through HAL in older versions +#include #ifdef __cplusplus extern "C" { diff --git a/lib/FreeRTOS-Kernel b/lib/FreeRTOS-Kernel index 4c4089b1544..def7d2df2b0 160000 --- a/lib/FreeRTOS-Kernel +++ b/lib/FreeRTOS-Kernel @@ -1 +1 @@ -Subproject commit 4c4089b1544b590ed3b72491a15365ec020c921f +Subproject commit def7d2df2b0506d3d249334974f51e427c17a41c diff --git a/lib/ReadMe.md b/lib/ReadMe.md index 9cd846a0ac7..3adb7701877 100644 --- a/lib/ReadMe.md +++ b/lib/ReadMe.md @@ -1,25 +1,35 @@ # Structure -- `app-scened-template` - Scened template app library -- `app-template` - Template app library +- `FreeRTOS-Kernel` - FreeRTOS kernel source code +- `FreeRTOS-glue` - Extra glue to hold together FreeRTOS kernel and flipper firmware +- `app-scened-template` - C++ app library - `callback-connector` - Callback connector library -- `drivers` - Drivers that we wrote -- `fatfs` - External storage file system +- `cmsis_core` - CMSIS Core package, contain cortex-m core headers +- `cxxheaderparser` - C++ headers parser, used by SDK bundler +- `digital_signal` - Digital signal library: used by NFC for software implemented protocols +- `drivers` - Various flipper drivers +- `fatfs` - FatFS file system driver +- `flipper_application` - Flipper application library, used for FAPs - `flipper_format` - Flipper File Format library -- `fnv1a-hash` - Fnv1a hash library -- `heatshrink` - Image compression library -- `infrared` - Infrared library -- `libusb_stm32` - STM32 USB library -- `littlefs` - Internal storage file system -- `micro-ecc` - Elliptic Curve Crpytography library -- `microtar` - TAR archive support library -- `mlib` - Algorithms and containers -- `nanopb` - Nano Protobuf library -- `nfc` - Nfc library -- `one_wire` - One wire library -- `qrcode` - Qr code generator library -- `ST25RFAL002` - ST253916 driver and NFC hal -- `STM32CubeWB` - STM32WB series cube package -- `subghz` - SubGhz library -- `toolbox` - Toolbox of things that we are using but don't place in core -- `u8g2` - Graphics library that we use to draw GUI +- `heatshrink` - Heatshrink compression library +- `ibutton` - ibutton library, used by iButton application +- `infrared` - Infrared library, used by Infrared application +- `lfrfid` - LF-RFID library, used by LF RFID application +- `libusb_stm32` - LibUSB for STM32 series MCU +- `littlefs` - LittleFS file system driver, used by internal storage +- `mbedtls` - MbedTLS cryptography library +- `microtar` - MicroTAR library +- `mlib` - M-Lib C containers library +- `nanopb` - NanoPB library, protobuf implementation for MCU +- `nfc` - NFC library, used by NFC application +- `one_wire` - OneWire library, used by iButton application +- `print` - Tiny printf implementation +- `digital_signal` - Digital Signal library used by NFC for software implemented protocols +- `pulse_reader` - Pulse Reader library used by NFC for software implemented protocols +- `stm32wb_cmsis` - STM32WB series CMSIS headers, extends CMSIS Core +- `stm32wb_copro` - STM32WB Copro library: contains WPAN and radio co-processor firmware +- `stm32wb_hal` - STM32WB HAL library, extends STM32WB CMSIS and provides HAL +- `subghz` - Subghz library, used by SubGhz application +- `toolbox` - Toolbox library, contains various things that is used by Flipper firmware +- `u8g2` - u8g2 graphics library, used by GUI subsystem +- `update_util` - update utilities library, used by updater \ No newline at end of file diff --git a/lib/SConscript b/lib/SConscript index 5e5bb2eaa7f..4835724e09b 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -1,82 +1,46 @@ Import("env") -env.Append( - LINT_SOURCES=[ - "lib/app-scened-template", - "lib/digital_signal", - "lib/drivers", - "lib/flipper_format", - "lib/infrared", - "lib/nfc", - "lib/one_wire", - "lib/ST25RFAL002", - "lib/subghz", - "lib/toolbox", - "lib/u8g2", - "lib/update_util", - "lib/print", - ] -) env.Append( CPPPATH=[ "#/", - "#/lib", # TODO: remove! - "#/lib/mlib", + "#/lib", # TODO FL-3553: remove! # Ugly hack - "${BUILD_DIR}/assets/compiled", - ], - CPPDEFINES=[ - '"M_MEMORY_FULL(x)=abort()"', + Dir("../assets/compiled"), ], ) -# drivers -# fatfs -# flipper_format -# infrared -# littlefs -# subghz -# toolbox -# misc -# digital_signal -# fnv1a-hash -# micro-ecc -# microtar -# nfc -# one_wire -# qrcode -# u8g2 -# update_util -# heatshrink -# nanopb -# apps -# app-scened-template -# callback-connector -# app-template - - libs = env.BuildModules( [ - "STM32CubeWB", + "mlib", + "stm32wb", "freertos", "print", "microtar", + "mbedtls", "toolbox", - "ST25RFAL002", "libusb_stm32", "drivers", "fatfs", "flipper_format", + "one_wire", + "ibutton", "infrared", "littlefs", - "mbedtls", "subghz", "nfc", + "digital_signal", + "pulse_reader", + "signal_reader", "appframe", - "misc", - "loclass", + "u8g2", + "lfrfid", + "flipper_application", + "music_worker", + "nanopb", + "update_util", + "heatshrink", ], ) diff --git a/lib/ST25RFAL002/SConscript b/lib/ST25RFAL002/SConscript deleted file mode 100644 index d86d2d002e8..00000000000 --- a/lib/ST25RFAL002/SConscript +++ /dev/null @@ -1,19 +0,0 @@ -Import("env") - -env.Append( - CPPPATH=[ - "#/lib/ST25RFAL002", - "#/lib/ST25RFAL002/include", - "#/lib/ST25RFAL002/source/st25r3916", - ], -) - - -libenv = env.Clone(FW_LIB_NAME="st25rfal002") -libenv.ApplyLibFlags() - -sources = libenv.GlobRecursive("*.c") - -lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) -libenv.Install("${LIB_DIST_DIR}", lib) -Return("lib") diff --git a/lib/ST25RFAL002/doc/Release_Notes.html b/lib/ST25RFAL002/doc/Release_Notes.html deleted file mode 100755 index c48e0a6c8c2..00000000000 --- a/lib/ST25RFAL002/doc/Release_Notes.html +++ /dev/null @@ -1,354 +0,0 @@ - - - - - - - - - - - - - Release Notes for RFAL Library - - - - - - - - - -
-


-

-
- - - - - - -
- - - - - - - - - -

-
-

Release -Notes for RFAL software Library

-

Copyright -2019 STMicroelectronics

-

-
-

 

- - - - - - -

-
The RFAL Library -(RF Abstraction Layer) provides several functionalities required to perform RF/NFC communications. -The RFAL encapsulates the different RF ICs (ST25R3911, ST25R3916, ST25R95 and future ST25R devices) into a common and easy to use interface.
-
- The technologies currently supported by RFAL are: -
    -
  • NFC-A \ ISO14443A (T1T, T2T, T4TA)
  • -
  • NFC-B \ ISO14443B (T4TB)
  • -
  • NFC-F \ FeliCa (T3T)
  • -
  • NFC-V \ ISO15693 (T5T)
  • -
  • P2P \ ISO18092 (NFCIP1, Passive-Active P2P)
  • -
  • ST25TB (ISO14443-2 Type B with Proprietary Protocol)
  • -
  • PicoPass \ iClass
  • -
  • B' \ Calypso
  • -
  • CTS \ CTM
  • -
-

- The protocols provided by RFAL are: -
    -
  • ISO-DEP (ISO14443-4)
  • -
  • NFC-DEP (ISO18092)
  • -
-
-
-
    -
- -

Update History

-
- -

V2.2.0 / 22-May-2020

-

-

Main Changes

-
    -
  • Better alignment to NFC Forum latest requirements (CR12)
  • -
  • Extended NFC-V module with non-addressed mode support and improved aticollision
  • -
  • Feature Switches changed to be not mandatory. Modules disabled by default
  • -
  • Aligned APIs on platform.h (breaks compatibility with previous versions, see example in rfal.chm)
  • -
  • Added API for release/deletion of timers
  • -
  • ST25R3916 default analog table modified to X-NUCLEO-NFC06A1 board
  • -
  • Improved AP2P operation
  • -
  • Fixed issues introduced on previous release linked to SFGT and anticollision retries
  • -
  • Introduced Low-Power mode
  • -
  • Several driver improvements
  • -
-
- -

V2.1.2 / 27-Jan-2020

-

-

Main Changes

-
    -
  • Extended ISO-DEP and NFC-A module to support non-blocking activation interfaces
  • -
  • Extended NFC/HL module to make use of the new APIs further splitting the execution of the worker during the different activities
  • -
  • Modified NFC-A anticollision to strictly comply to NFC Forum DP. A separate proprietary method is now available.
  • -
  • NFC-V changed to use OOK (100% AM) by default
  • -
  • Fixed FWT used by NFC-V Sleep
  • -
  • Fixed NFC-F FDT Poll value
  • -
  • Fixed incorrect register access on ST25R3911B RFO Get/Set method
  • -
  • SPI driver modified to clear Rx buffer prior to operation
  • -
  • Added further code size optimizations based on enabled features
  • -
  • Updated ST25R3916 driver to DS Rev2
  • -
  • Updated SW Tag Detection as describded in AN Rev3
  • -
  • Several driver improvements
  • -
-
- -

V2.1.0 / 30-Sep-2019

-

-

Main Changes

-
    -
  • Extended RFAL NFC Higher Layer for increased functionality and configurations
  • -
  • Several improvements on the ISO-DEP protocol layer
  • -
  • Protocol buffer sizes made fully configurable for increased memory management
  • -
  • Introduced option for Collision Avoidance with Automatic Gain Control
  • -
  • Several driver improvements
  • -
  • ST25R3916 overheat protection disabled
  • -
  • RF Transceive modified for transmission errors to precede other errors
  • -
  • Analog Configs extended to support different DPO power levels
  • -
-
- -

V2.0.10 / 25-Jun-2019

-

-

Main Changes

-
    -
  • Various improvements on RFAL NFC Higher layer
  • -
  • Added alternative NFC-V anticollision method (non NFC Forum compliant)
  • -
  • Several minor improvements and fixes
  • -
-
- -

V2.0.6 / 10-Apr-2019

-

-

Main Changes

-
    -
  • Several NFC-V interoperability improvements
  • -
  • Extended support for specific features of ST's ISO15693 Tags. New ST25Dx module added
  • -
  • Interrupt handling changed and further protection added
  • -
  • RFAL feature switches have been modified and features are now disabled if omitted
  • -
  • ST25R3916 AAT (Automatic Antenna Tunning) module added
  • -
  • RFAL NFC Higher layer added
  • -
  • Several driver improvements
  • -
-
- -

V2.0.4 / 06-Fev-2019

-

Provided with ST25R3916 DISCO v1.0.0 / EMVCo v1.2.0

-

Main Changes

-
    -
  • Minor improvements on NFC-F module
  • -
  • Several improvements on NFC-V module including support for ST proprietary features
  • -
  • Fixed issue with Delta RWT calculation
  • -
  • Fixed incorrect usage of NFCB dTbPoll / DdFWT
  • -
  • Added compile switch for Listen Mode
  • -
  • Low power Listen Mode support added
  • -
  • Listen Mode aligned to NFC Forum Digital 2.1
  • -
  • Added handling for EMVCo 3.0 static FDTListen
  • -
  • Introduced SW Tag Detection
  • -
-
- -

V2.0.2 / 31-Oct-2018

-

Provided with ST25R3916 DISCO v0.9.4 (binary only)

-

Main Changes

-
    -
  • New T4T module added
  • -
  • Added support for T3T Check and Update commands
  • -
  • Improved NFC-V module and added Write Multiple Blocks support
  • -
  • New rfalWorker protection added for improved control in multi-thread environments
  • -
  • Added support for user defined Analog Config tables
  • -
  • Several driver improvements and protections added
  • -
-
- -

V2.0.0 / 28-Aug-2018

- -

Main Changes

-
    -
  • MISRA C 2012 compliant
  • -
  • ST25R3916 support added
  • -
  • ST25R95 support added
  • -
  • Fix unwanted Field Detector disable when entering Wake-up mode
  • -
  • Extended Analog Config to have specific events
  • -
  • Fixed NFC-DEP potential issue if DID used
  • -
  • Extended NFC-V commands
  • -
  • T2T module added
  • -
  • Improved initial Listen mode handling
  • -
  • Extended Wake-Up mode to support Capacitive measurement
  • -
-
- -

V1.3.6 / 08-May-2018

-

Provided with ST25R3911B DISCO v1.2.0

-

Main Changes

-
    -
  • Added ISO15693 x4 and x8 mode support
  • -
  • Added S(PARAMETERS) support
  • -
  • Interface changes for measurement, Wake-Up and DPO methods
  • -
  • Added further feature switches to enable/disable individual modules
  • -
  • Changed communication protection
  • -
  • Improved NFC-A anti-collision
  • -
  • Several driver improvements
  • -
-
-

V1.3.4 / 07-May-2018

- -

Main Changes

-
    -
  • Fixed NFC-V Read operation in addressed mode
  • -
-
-

V1.3.2 / 31-January-2018

- -

Main Changes

-
    -
  • Modified Wake-Up mode interface
  • -
  • Fixed SFGI calculation in ISO-DEP
  • -
-
-

V1.3.0 / 22-January-2018

- -

Main Changes

-
    -
  • Introduced a new IRQ status handling to read the registers only once
  • -
  • Several changes for supporting Linux platform
  • -
  • SPI Select/Deselect moved to platform.h
  • -
  • Aditional protection of the IRQ status reading, new macros available: platformProtectST25R391xIrqStatus / platformUnprotectST25R391xIrqStatus
  • -
  • Renamed the IRQ Enable/Disable macros to platformProtectST25R391xComm / platformUnprotectST25R391xComm
  • -
  • Renamed SPI pins from chip specific to ST25R391X
  • -
  • Introduced a new option ST25R391X_COM_SINGLETXRX which executes SPI in one single exchange (additional buffer required)
  • -
  • Updated and added errata handlings to latest ST25R3911 Errata version
  • -
  • Fixed inconsitency on Analog settings for NFC-V
  • -
  • Fixed issue on NFC-V 1of256 decoding
  • -
  • Changed the default NFC-A FDT Listen to be more strict
  • -
  • Added Wake-Up mode support
  • -
  • Added RFAL version definition
  • -
-
-

V1.2.0 / 17-August-2017

-

Provided with ST25R3911B Disco v1.1.16

-

Main Changes

-
    -
  • Aligned Technology modules to NFC Activity v1.1 and EMVCo v2.6
  • -
  • Extended NFC-B Collision Resolution allowing user define Slots
  • -
  • Added feature switches to enable/disable individual modules
  • -
  • ISO-DEP Interface changes allowing more user configurations and further EMVCo alignment
  • -
  • Changed ST25TB detection to always perform Anti Collision loop regardeless of the result of the Poll
  • -
  • Fixed FIFO WL handling
  • -
  • Modified FDT Poll handling
  • -
  • changed rfalCalibrate() to not overwrite dynamic configs
  • -
  • Added adjustment for TR1PUTMIN
  • -
- -
-

V1.1.0 / 30-June-2017

-

Provided with ST25R3911B Disco v1.1.12

-

Main Changes

-
    -
  • EMD supression enabled for ST25R3911B
  • -
- -
-

V1.0.0 / 16-May-2017

-

Provided with X-NUCLEO-NFC05A1 v1.0.0

-

Main Changes

-
    -
  • Added support for B', CTS and PicoPass/iClass mode
  • -
  • Several impromvements for NFC-V mode
  • -
  • Improved error detection during NFC-B collision resolution
  • -
  • Extended T1T module
  • -
- -
-

V0.9.0 / 02-March-2017

-

Provided with ST25R3911B Discovery Kit on Embedded World Conference (binary only)

-

Main Changes

- -
- -
-

-
-
-

 

-
- diff --git a/lib/ST25RFAL002/doc/ST25R3916_MisraComplianceReport.html b/lib/ST25RFAL002/doc/ST25R3916_MisraComplianceReport.html deleted file mode 100755 index 7076e7cc886..00000000000 --- a/lib/ST25RFAL002/doc/ST25R3916_MisraComplianceReport.html +++ /dev/null @@ -1,8867 +0,0 @@ - - - - - -PRQA GEP/GCS/GRP Report - - - - -
-
-
-
-
- -This section targets to provide an overview of Guidelines Enforcement Plan (GEP).
-
-This document will only focus on STMicroelectronics NFC RF Abstraction Layer (RFAL).
-
-The project has been designed to comply with the standard ISO/IEC 9899:1999 ([C99]). -
-
-

1. Tools version

-The tool used for MISRA compliance is:
-
-PRQA Framework - v2.2.2
-

-It is composed of the following subcomponents: -

-
    -
  • Component: qacpp

  • -
      Version: 4.2.0
    -
      Target: C++
    -
  • Component: rcma

  • -
      Version: 1.6.0
    -
      Target: C_CPP
    -
  • Component: m3cm

  • -
      Version: 2.3.1
    -
      Target: C
    -
  • Component: qac

  • -
      Version: 9.3.1
    -
      Target: C
    -
      -
    • Options:
    • -
        -d : __schedule_barrier=_ignore_semi
      -
        -namelength : 63
      -
    -
-

2. Configuration

-This section targets to provide the main configuration options used for MISRA compliance.
-
-The project complies to [C99],
-the variables length has been consequently set to a dedicated value (cf 'namelength' option in table above). -
-
-Repository/components:
-
    -
  • MCU target:
  • -
      STM32

    -
  • Parent repository:
  • -
      ST25R3916_nucleo

    -
  • RFAL informations:
  • -
      Path: .../ST25R3916_nucleo/rfal
    -
      Version: v2.1.2
    -
  • Project repositories SHA1:
  • -
      .../ST25R3916_nucleo: 959b80e
    -
      .../ST25R3916_nucleo/common: 09bc5ef
    -
      .../ST25R3916_nucleo/nucleo: 22a04ae
    -
      .../ST25R3916_nucleo/rfal: f08099c
    -
    -
-

3. Assistance/Enforcement

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GuidelineCategoryDescriptionAssistance/Enforcement Sub Rules
Dir-1.1RequiredAny implementation-defined behaviour on which the output of the program depends shall be documented and understood
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
0202 [I] '-' character in '[]' conversion specification is implementation defined.
0284 [I] Multiple character constants have implementation defined values.
0285 [I] Character constant contains character which is not a member of the basic source character set.
0286 [I] String literal contains character which is not a member of the basic source character set.
0287 [I] Header name contains character which is not a member of the basic source character set.
0288 [I] Source file '%s' has comments containing characters which are not members of the basic source character set.
0289 [I] Source file '%s' has preprocessing tokens containing characters which are not members of the basic source character set.
0292 [I] Source file '%s' has comments containing one of the characters '$', '@' or '`'.
0299 [I] Source file '%s' includes #pragma directives containing characters which are not members of the basic source character set.
0314 [I] Cast from a pointer to object type to a pointer to void.
0315 [I] Implicit conversion from a pointer to object type to a pointer to void.
0371 [L] Nesting levels of blocks exceeds 127 - program does not conform strictly to ISO:C99.
0372 [L] More than 63 levels of nested conditional inclusion - program does not conform strictly to ISO:C99.
0375 [L] Nesting of parenthesized expressions exceeds 63 - program does not conform strictly to ISO:C99.
0380 [L] Number of macro definitions exceeds 4095 - program does not conform strictly to ISO:C99.
0388 [L] '#include "%s"' causes nesting to exceed 15 levels - program does not conform strictly to ISO:C99.
0390 [L] Number of members in 'struct' or 'union' exceeds 1023 - program does not conform strictly to ISO:C99.
0391 [L] Number of enumeration constants exceeds 1023 - program does not conform strictly to ISO:C99.
0392 [L] Nesting of 'struct' or 'union' types exceeds 63 - program does not conform strictly to ISO:C99.
0581 [I] Floating-point constant may be too small to be representable.
0634 [I] Bit-fields in this struct/union have not been declared explicitly as unsigned or signed.
2850 Constant: Implicit conversion to a signed integer type of insufficient size.
2851 Definite: Implicit conversion to a signed integer type of insufficient size.
2852 Apparent: Implicit conversion to a signed integer type of insufficient size.
2855 Constant: Casting to a signed integer type of insufficient size.
2856 Definite: Casting to a signed integer type of insufficient size.
2857 Apparent: Casting to a signed integer type of insufficient size.
2860 Constant: Implementation-defined value resulting from left shift operation on expression of signed type.
2861 Definite: Implementation-defined value resulting from left shift operation on expression of signed type.
2862 Apparent: Implementation-defined value resulting from left shift operation on expression of signed type.
2895 Constant: Negative value cast to an unsigned type.
2896 Definite: Negative value cast to an unsigned type.
2897 Apparent: Negative value cast to an unsigned type.
3116 Unrecognized #pragma arguments '%s' This #pragma directive has been ignored.
-
Dir-2.1RequiredAll source files shall compile without any compilation errorsUnassisted

-Remarks:
-Dedicated checks deployed in Jenkins.
Dir-3.1RequiredAll code shall be traceable to documented requirementsUnassisted

-Remarks:
-Limited management of requirements.
Dir-4.1RequiredRun-time failures shall be minimized
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
2791 Definite: Right hand operand of shift operator is negative or too large.
2792 Apparent: Right hand operand of shift operator is negative or too large.
2801 Definite: Overflow in signed arithmetic operation.
2802 Apparent: Overflow in signed arithmetic operation.
2811 Definite: Dereference of NULL pointer.
2812 Apparent: Dereference of NULL pointer.
2821 Definite: Arithmetic operation on NULL pointer.
2822 Apparent: Arithmetic operation on NULL pointer.
2831 Definite: Division by zero.
2832 Apparent: Division by zero.
2841 Definite: Dereference of an invalid pointer value.
2842 Apparent: Dereference of an invalid pointer value.
2845 Constant: Maximum number of characters to be written is larger than the target buffer size.
2846 Definite: Maximum number of characters to be written is larger than the target buffer size.
2847 Apparent: Maximum number of characters to be written is larger than the target buffer size.
2871 Infinite loop identified.
2872 This loop, if entered, will never terminate.
2877 This loop will never be executed more than once.
-
Dir-4.10RequiredPrecautions shall be taken in order to prevent the contents of a header file being included more then once
- - - - - -
QacDescription
0883 Include file code is not protected against repeated inclusion
-
Dir-4.11RequiredThe validity of values passed to library functions shall be checkedUnassisted

-Remarks:
-No automated check deployed.
-Manual checks done by developers.
Dir-4.12RequiredDynamic memory allocation shall not be usedUnassisted

-Remarks:
-No memory allocation functions (malloc(), calloc(), realloc()) being called in RFAL.
Dir-4.13AdvisoryFunctions which are designed to provide operations on a resource should be called in an appropriate sequenceUnassisted
Dir-4.14RequiredThe validity of values received from external sources shall be checked
- - - - - -
QacDescription
2956 Definite: Using object '%s' with tainted value.
-
Dir-4.2AdvisoryAll usage of assembly language should be documented
- - - - - - - - - -
QacDescription
1003 [E] '#%s' is a language extension for in-line assembler. All statements located between #asm and #endasm will be ignored.
1006 [E] This in-line assembler construct is a language extension. The code has been ignored.
-
Dir-4.3RequiredAssembly language shall be encapsulated and isolated
- - - - - - - - - -
QacDescription
3006 This function contains a mixture of in-line assembler statements and C statements.
3008 This function contains a mixture of in-line assembler statements and C code.
-
Dir-4.4AdvisorySections of code should not be "commented out"Unassisted
Dir-4.5AdvisoryIdentifiers in the same name space with overlapping visibility should be typographically unambiguousUnassisted
Dir-4.6Advisorytypedefs that indicate size and signedness should be used in place of the basic numerical types
- - - - - -
QacDescription
5209 Use of basic type '%s'.
-
Dir-4.7RequiredIf a function returns error information, then that error information shall be testedUnassisted

-Remarks:
-Dir-4.7 is similar to Rule-17.7 which is currently dismissed.
-This directive is consequently considered as disapplied.
Dir-4.8AdvisoryIf a pointer to a structure or union is never dereferenced within a translation unit, then the implementation of the object should be hiddenUnassisted
Dir-4.9AdvisoryA function should be used in preference to a function-like macro where they are interchangeable
- - - - - -
QacDescription
3453 A function could probably be used instead of this function-like macro.
-
Rule-1.1RequiredThe program shall contain no violations of the standard C syntax and constraints, and shall not exceed the implementation's translation limits
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
0232 [C] Value of hex escape sequence is not representable in type 'unsigned char'.
0233 [C] Value of octal escape sequence is not representable in type 'unsigned char'.
0244 [C] Value of character constant is not representable in type 'int'.
0268 [S] Comment open at end of translation unit.
0321 [C] Declaration within 'for' statement defines an identifier '%s' which is not an object.
0322 [C] Illegal storage class specifier used in 'for' statement declaration.
0338 [C] Octal or hex escape sequence value is too large for 'unsigned char' or 'wchar_t' type.
0422 [C] Function call contains fewer arguments than prototype specifies.
0423 [C] Function call contains more arguments than prototype specifies.
0426 [C] Called function has incomplete return type.
0427 [C] Object identifier used as if it were a function or a function pointer identifier.
0429 [C] Function argument is not of arithmetic type.
0430 [C] Function argument is not of compatible 'struct'/'union' type.
0431 [C] Function argument points to a more heavily qualified type.
0432 [C] Function argument is not of compatible pointer type.
0435 [C] The 'struct'/'union' member '%s' does not exist.
0436 [C] Left operand of '.' must be a 'struct' or 'union' object.
0437 [C] Left operand of '->' must be a pointer to a 'struct' or 'union' object.
0446 [C] Operand of ++/-- must have scalar (arithmetic or pointer) type.
0447 [C] Operand of ++/-- must be a modifiable object.
0448 [C] Operand of ++/-- must not be a pointer to an object of unknown size.
0449 [C] Operand of ++/-- must not be a pointer to a function.
0450 [C] An expression of array type cannot be cast.
0451 [C] Subscripting requires a pointer (or array lvalue).
0452 [C] Cannot subscript a pointer to an object of unknown size.
0453 [C] An array subscript must have integral type.
0454 [C] The address-of operator '&' cannot be applied to an object declared with 'register'.
0456 [C] This expression does not have an address - '&' may only be applied to an lvalue or a function designator.
0457 [C] The address-of operator '&' cannot be applied to a bit-field.
0458 [C] Indirection operator '*' requires operand of pointer type.
0460 [C] The keyword static is used in the declaration of the index of an array which is not a function parameter.
0461 [C] The keyword static is used in the declaration of an inner index of a multi-dimensional array.
0462 [C] A type qualifier (const, volatile or restrict) is used in the declaration of the index of an array which is not a function parameter.
0463 [C] A type qualifier (const, volatile or restrict) is used in the declaration of an inner index of a multi-dimensional array.
0466 [C] Unary '+' requires arithmetic operand.
0467 [C] Operand of '!' must have scalar (arithmetic or pointer) type.
0468 [C] Unary '-' requires arithmetic operand.
0469 [C] Bitwise not '~' requires integral operand.
0476 [C] 'sizeof' cannot be applied to a bit-field.
0477 [C] 'sizeof' cannot be applied to a function.
0478 [C] 'sizeof' cannot be applied to an object of unknown size.
0481 [C] Only scalar expressions may be cast to other types.
0482 [C] Expressions may only be cast to 'void' or scalar types.
0483 [C] A pointer to an object of unknown size cannot be the operand of an addition operator.
0484 [C] A pointer to an object of unknown size cannot be the operand of a subtraction operator.
0485 [C] Only integral expressions may be added to pointers.
0486 [C] Only integral expressions and compatible pointers may be subtracted from pointers.
0487 [C] If two pointers are subtracted, they must be pointers that address compatible types.
0493 [C] Type of left operand is not compatible with this operator.
0494 [C] Type of right operand is not compatible with this operator.
0495 [C] Left operand of '%', '<<', '>>', '&', '^' or '|' must have integral type.
0496 [C] Right operand of '%', '<<', '>>', '&', '^' or '|' must have integral type.
0513 [C] Relational operator used to compare pointers to incompatible types.
0514 [C] Relational operator used to compare a pointer with an incompatible operand.
0515 [C] Equality operator used to compare a pointer with an incompatible operand.
0536 [C] First operand of '&&', '||' or '?' must have scalar (arithmetic or pointer) type.
0537 [C] Second operand of '&&' or '||' must have scalar (arithmetic or pointer) type.
0540 [C] 2nd and 3rd operands of conditional operator '?' must have compatible types.
0541 [C] Argument no. %s does not have object type.
0542 [C] Controlling expression must have scalar (arithmetic or pointer) type.
0546 [C] 'enum %s' has unknown content. Use of an enum tag with undefined content is not permitted.
0547 [C] This declaration of tag '%s' conflicts with a previous declaration.
0550 [C] Left operand of '+=' or '-=' is a pointer to an object of unknown size.
0554 [C] 'static %s()' has been declared and called but no definition has been given.
0555 [C] Invalid assignment to object of void type or array type.
0556 [C] Left operand of assignment must be a modifiable object.
0557 [C] Right operand of assignment is not of arithmetic type.
0558 [C] Right operand of '+=' or '-=' must have integral type when left operand is a pointer.
0559 [C] Right operand of '<<=', '>>=', '&=', '|=', '^=' or '%=' must have integral type.
0560 [C] Left operand of '<<=', '>>=', '&=', '|=', '^=' or '%=' must have integral type.
0561 [C] Right operand of assignment is not of compatible 'struct'/'union' type.
0562 [C] Right operand of assignment points to a more heavily qualified type.
0563 [C] Right operand of assignment is not of compatible pointer type.
0564 [C] Left operand of assignment must be an lvalue (it must designate an object).
0565 [C] Left operand of '+=' or '-=' must be of arithmetic or pointer to object type.
0580 [C] Constant is too large to be representable.
0588 [C] Width of bit-field must be an integral constant expression.
0589 [C] Enumeration constant must be an integral constant expression.
0590 [C] Array bound must be an integral constant expression.
0591 [C] A 'case' label must be an integral constant expression.
0605 [C] A declaration must declare a tag or an identifier.
0616 [C] Illegal combination of type specifiers or storage class specifiers.
0619 [C] The identifier '%s' has already been defined in the current scope within the ordinary identifier namespace.
0620 [C] Cannot initialize '%s' because it has unknown size.
0621 [C] The struct/union '%s' cannot be initialized because it has unknown size.
0622 [C] The identifier '%s' has been declared both with and without linkage in the same scope.
0627 [C] '%s' has different type to previous declaration in the same scope.
0628 [C] '%s' has different type to previous declaration at wider scope.
0629 [C] More than one definition of '%s' (with internal linkage).
0631 [C] More than one declaration of '%s' (with no linkage).
0638 [C] Duplicate member name '%s' in 'struct' or 'union'.
0640 [C] '%s' in 'struct' or 'union' type may not have 'void' type.
0641 [C] '%s' in 'struct' or 'union' type may not have function type.
0642 [C] '%s' in 'struct' or 'union' type may not be an array of unknown size.
0643 [C] '%s' in 'struct' or 'union' type may not be a 'struct' or 'union' with unknown content.
0644 [C] Width of bit-field must be no bigger than the width of an 'int'.
0645 [C] A zero width bit-field cannot be given a name.
0646 [C] Enumeration constants must have values representable as 'int's.
0649 [C] K&R style declaration of parameters is not legal after a function header that includes a parameter list.
0650 [C] Illegal storage class specifier on named function parameter.
0651 [C] Missing type specifiers in function declaration.
0653 [C] Duplicate definition of 'struct', 'union' or 'enum' tag '%s'.
0655 [C] Illegal storage class specifier on unnamed function parameter.
0656 [C] Function return type cannot be function or array type, or an incomplete struct/union (for function definition).
0657 [C] Unnamed parameter specified in function definition.
0659 [C] The identifier '%s' was not given in the parameter list.
0664 [C] Parameter specified with type 'void'.
0665 [C] Two parameters have been declared with the same name '%s'.
0669 [C] The restrict qualifier can only be applied to pointer types derived from object or incomplete types.
0671 [C] Initializer for object of arithmetic type is not of arithmetic type.
0673 [C] Initializer points to a more heavily qualified type.
0674 [C] Initializer for pointer is of incompatible type.
0675 [C] Initializer is not of compatible 'struct'/'union' type.
0677 [C] Array size is negative, or unrepresentable.
0682 [C] Initializer for object of a character type is a string literal.
0683 [C] Initializer for object of a character type is a wide string literal.
0684 [C] Too many initializers.
0685 [C] Initializer for any object with static storage duration must be a constant expression.
0690 [C] String literal contains too many characters to initialize object.
0698 [C] String literal used to initialize an object of incompatible type.
0699 [C] String literal used to initialize a pointer of incompatible type.
0708 [C] No definition found for the label '%s' in this function.
0709 [C] Initialization of locally declared 'extern %s' is illegal.
0736 [C] 'case' label does not have unique value within this 'switch' statement.
0737 [C] More than one 'default' label found in 'switch' statement.
0738 [C] Controlling expression in a 'switch' statement must have integral type.
0746 [C] 'return exp;' found in '%s()' whose return type is 'void'.
0747 [C] 'return exp;' found in '%s()' whose return type is qualified 'void'.
0755 [C] 'return' expression is not of arithmetic type.
0756 [C] 'return' expression is not of compatible 'struct'/'union' type.
0757 [C] 'return' expression points to a more heavily qualified type.
0758 [C] 'return' expression is not of compatible pointer type.
0766 [C] 'continue' statement found outside an iteration statement.
0767 [C] 'break' statement found outside a 'switch' or iteration statement.
0768 [C] 'case' or 'default' found outside a 'switch' statement.
0774 [C] 'auto' may not be specified on global declaration of '%s'.
0775 [C] 'register' may not be specified on global declaration of '%s'.
0801 [C] The '##' operator may not be the first token in a macro replacement list.
0802 [C] The '##' operator may not be the last token in a macro replacement list.
0803 [C] The '#' operator may only appear before a macro parameter.
0804 [C] Macro parameter '%s' is not unique.
0811 [C] The glue operator '##' may only appear in a '#define' preprocessing directive.
0812 [C] Header name token '' found outside '#include' preprocessing directive.
0817 [S] Closing quote or bracket '>' missing from include filename.
0818 [Q] Cannot find '%s' - Perhaps the appropriate search path was not given ?
0821 [C] '#include' does not identify a header or source file that can be processed.
0834 [C] Function-like macro '%s()' is being redefined as an object-like macro.
0835 [C] Macro '%s' is being redefined with different parameter names.
0844 [C] Macro '%s' is being redefined with a different replacement list.
0845 [C] Object-like macro '%s' is being redefined as a function-like macro.
0851 [C] More arguments in macro call than specified in definition.
0852 [S] Unable to find the ')' that marks the end of the macro call.
0866 [C] The string literal in a '#line' directive cannot be a 'wide string literal'.
0873 [C] Preprocessing token cannot be converted to an actual token.
0877 [C] '#if' and '#elif' expressions may contain only integral constants.
0940 [C] Illegal usage of a variably modified type.
0941 [C] A variable length array may not be initialized.
0943 [C] Jump to label '%s' is a jump into the scope of an identifier with variably modified type.
0944 [C] The label '%s' is inside the scope of an identifier with variably modified type.
1023 [C] Using '__alignof__' on function types is illegal.
1024 [C] Using '__alignof__' on incomplete types is illegal.
1025 [C] Using '__alignof__' on bit-fields is illegal.
1033 [C] The identifier __VA_ARGS__ may only be used in the replacement list of a variadic macro.
1047 [C] Function is being declared with default argument syntax after a previous call to the function. This is not allowed.
1048 [C] Default argument values are missing for some parameters in this function declaration. This is not allowed.
1050 [C] Nested functions cannot be 'extern' or 'static'.
1061 [C] Structure '%1s' with flexible array member '%2s' cannot be used in the declaration of structure member '%3s'.
1062 [C] Structure '%1s' with flexible array member '%2s' cannot be used in the declaration of array elements.
3236 [C] 'inline' may not be applied to function 'main'.
3237 [C] inline function '%1s' has external linkage and is defining an object, '%2s', with static storage duration.
3238 [C] inline function '%1s' has external linkage and is referring to an object, '%2s', with internal linkage.
3244 [C] 'inline' may only be used in the declaration of a function identifier.
-
Rule-1.2AdvisoryLanguage extensions should not be used
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
0240 [E] This file contains the control-M character at the end of a line.
0241 [E] This file contains the control-Z character - was this transferred from a PC?
0246 [E] Binary integer constants are a language extension.
0551 [E] Cast may not operate on the left operand of the assignment operator.
0601 [E] Function 'main()' is not of type 'int (void)' or 'int (int, char *[])'.
0633 [E] Empty structures and unions are a language extension.
0635 [E] Bit-fields in this struct/union have been declared with types other than int, signed int, unsigned int or _Bool.
0660 [E] Defining an unnamed member in a struct or union. This is a language extension.
0662 [E] Accessing a member of an unnamed struct or union member in this way is a language extension.
0830 [E] Unrecognized text encountered after a preprocessing directive.
0831 [E] Use of '\\' in this '#include' line is a PC extension - this usage is non-portable.
0840 [E] Extra tokens at end of #include directive.
0883 Include file code is not protected against repeated inclusion
0899 [E] Unrecognized preprocessing directive has been ignored - assumed to be a language extension.
0981 [E] Redundant semicolon in 'struct' or 'union' member declaration list is a language extension.
1001 [E] '#include %s' is a VMS extension.
1002 [E] '%s' is not a legal identifier in ISO C.
1003 [E] '#%s' is a language extension for in-line assembler. All statements located between #asm and #endasm will be ignored.
1006 [E] This in-line assembler construct is a language extension. The code has been ignored.
1008 [E] '#%s' is not a legal ISO C preprocessing directive.
1012 [E] Use of a C++ reference type ('type &') will be treated as a language extension.
1014 [E] Non-standard type specifier - this will be treated as a language extension.
1015 [E] '%s' is not a legal keyword in ISO C - this will be treated as a language extension.
1019 [E] '@ address' is not supported in ISO C - this will be treated as a language extension.
1020 [E] '__typeof__' is not supported in ISO C, and is treated as a language extension.
1021 [E] A statement expression is not supported in ISO C, and is treated as a language extension.
1022 [E] '__alignof__' is not supported in ISO C, and is treated as a language extension.
1026 [E] The indicated @word construct has been ignored.
1028 [E] Use of the sizeof operator in a preprocessing directive is a language extension.
1029 [E] Whitespace encountered between backslash and new-line has been ignored.
1034 [E] Macro defined with named variable argument list. This is a language extension.
1035 [E] No macro arguments supplied for variable argument list. This is a language extension.
1036 [E] Comma before ## ignored in expansion of variadic macro. This is a language extension.
1037 [E] Arrays of length zero are a language extension.
1038 [E] The sequence ", ##__VA_ARGS__" is a language extension.
1039 [E] Treating array of length one as potentially flexible member.
1041 [E] Empty aggregate initializers are a language extension.
1042 [E] Using I64 or UI64 as an integer constant suffix. This is a language extension.
1043 [E] Defining an anonymous union object. This is a language extension.
1044 [E] Defining an anonymous struct object. This is a language extension.
1045 [E] Use of the #include_next preprocessing directive is a language extension.
1046 [E] Function is being declared with default argument syntax. This is a language extension.
1049 [E] Nested functions are a language extension.
3445 [E] Conditional expression with middle operand omitted is a language extension.
3664 [E] Using a dot operator to access an individual bit is a language extension.
-
Rule-1.3RequiredThere shall be no occurrence of undefined or critical unspecified behaviour
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
0160 [U] Using unsupported conversion specifier number %s.
0161 [U] Unknown length modifier used with 'i' or 'd' conversion specifier, number %s.
0162 [U] Unknown length modifier used with 'o' conversion specifier, number %s.
0163 [U] Unknown length modifier used with 'u' conversion specifier, number %s.
0164 [U] Unknown length modifier used with 'x' conversion specifier, number %s.
0165 [U] Unknown length modifier used with 'X' conversion specifier, number %s.
0166 [U] Unknown length modifier used with 'f' conversion specifier, number %s.
0167 [U] Unknown length modifier used with 'e' conversion specifier, number %s.
0168 [U] Unknown length modifier used with 'E' conversion specifier, number %s.
0169 [U] Unknown length modifier used with 'g' conversion specifier, number %s.
0170 [U] Unknown length modifier used with 'G' conversion specifier, number %s.
0171 [U] Unknown length modifier used with 'c' conversion specifier, number %s.
0172 [U] Unknown length modifier used with '%%' conversion specifier, number %s.
0173 [U] Unknown length modifier used with 's' conversion specifier, number %s.
0174 [U] Unknown length modifier used with 'n' conversion specifier, number %s.
0175 [U] Unknown length modifier used with 'p' conversion specifier, number %s.
0176 [U] Incomplete conversion specifier, number %s.
0177 [U] Field width of format conversion specifier exceeds 509 characters.
0178 [U] Precision of format conversion specifier exceeds 509 characters.
0179 [U] Argument type does not match conversion specifier number %s.
0184 [U] Insufficient arguments to satisfy conversion specifier, number %s.
0185 [U] Call contains more arguments than conversion specifiers.
0186 [U] A call to this function must include at least one argument.
0190 [U] Using unsupported conversion specifier number %s.
0191 [U] Unknown length modifier used with 'd/i/n' conversion specifier, number %s.
0192 [U] Unknown length modifier used with 'o' conversion specifier, number %s.
0193 [U] Unknown length modifier used with 'u' conversion specifier, number %s.
0194 [U] Unknown length modifier used with 'x/X' conversion specifier, number %s.
0195 [U] Unknown length modifier used with 'e/E/f/F/g/G' conversion specifier, number %s.
0196 [U] Unknown length modifier used with 's' conversion specifier, number %s.
0197 [U] Unknown length modifier used with 'p' conversion specifier, number %s.
0198 [U] Unknown length modifier used with '%%' conversion specifier, number %s.
0199 [U] Unknown length modifier used with '[' conversion specifier, number %s.
0200 [U] Unknown length modifier used with 'c' conversion specifier, number %s.
0201 [U] Incomplete conversion specifier, number %s.
0203 [U] Value of character prior to '-' in '[]' is greater than following character.
0204 [U] Field width of format conversion specifier exceeds 509 characters.
0206 [U] Argument type does not match conversion specifier number %s.
0207 [U] 'scanf' expects address of objects being stored into.
0208 [U] Same character occurs in scanset more than once.
0235 [U] Unknown escape sequence.
0275 [U] Floating value is out of range for conversion to destination type.
0301 [u] Cast between a pointer to object and a floating type.
0302 [u] Cast between a pointer to function and a floating type.
0304 [U] The address of an array declared 'register' may not be computed.
0307 [u] Cast between a pointer to object and a pointer to function.
0309 [U] Integral type is not large enough to hold a pointer value.
0327 [I] Cast between a pointer to void and an floating type.
0337 [U] String literal has undefined value. This may be a result of using '#' on \\.
0400 [U] '%s' is modified more than once between sequence points - evaluation order unspecified.
0401 [U] '%s' may be modified more than once between sequence points - evaluation order unspecified.
0402 [U] '%s' is modified and accessed between sequence points - evaluation order unspecified.
0403 [U] '%s' may be modified and accessed between sequence points - evaluation order unspecified.
0404 More than one read access to volatile objects between sequence points.
0405 More than one modification of volatile objects between sequence points.
0475 [u] Operand of 'sizeof' is an expression designating a bit-field.
0543 [U] 'void' expressions have no value and may not be used in expressions.
0544 [U] The value of an incomplete 'union' may not be used.
0545 [U] The value of an incomplete 'struct' may not be used.
0602 [U] The identifier '%s' is reserved for use by the library.
0603 [U] The macro identifier '%s' is reserved.
0623 [U] '%s' has incomplete type and no linkage - this is undefined.
0625 [U] '%s' has been declared with both internal and external linkage - the behaviour is undefined.
0626 [U] '%s' has different type to previous declaration (which is no longer in scope).
0630 [U] More than one definition of '%s' (with external linkage).
0632 [U] Tentative definition of '%s' with internal linkage cannot have unknown size.
0636 [U] There are no named members in this 'struct' or 'union'.
0654 [U] Using 'const' or 'volatile' in a function return type is undefined.
0658 [U] Parameter cannot have 'void' type.
0661 [U] '%s()' may not have a storage class specifier of 'static' when declared at block scope.
0667 [U] '%s' is declared as a typedef and may not be redeclared as an object at an inner scope without an explicit type specifier.
0668 [U] '%s' is declared as a typedef and may not be redeclared as a member of a 'struct' or 'union' without an explicit type specifier.
0672 [U] The initializer for a 'struct', 'union' or array is not enclosed in braces.
0676 [u] Array element is of function type. Arrays cannot be constructed from function types.
0678 [u] Array element is array of unknown size. Arrays cannot be constructed from incomplete types.
0680 [u] Array element is 'void' or an incomplete 'struct' or 'union'. Arrays cannot be constructed from incomplete types.
0706 [U] Label '%s' is not unique within this function.
0745 [U] 'return;' found in '%s()', which has been defined with a non-'void' return type.
0777 [U] External identifier does not differ from other identifier(s) (e.g. '%s') within the specified number of significant characters.
0779 [U] Identifier does not differ from other identifier(s) (e.g. '%s') within the specified number of significant characters.
0813 [U] Using any of the characters ' " or /* in '#include <%s>' gives undefined behaviour.
0814 [U] Using the characters ' or /* in '#include "%s"' gives undefined behaviour.
0836 [U] Definition of macro named 'defined'.
0837 [U] Use of '#undef' to remove the operator 'defined'.
0840 [E] Extra tokens at end of #include directive.
0848 [U] Attempting to #undef '%s', which is a predefined macro name.
0853 [U] Macro arguments contain a sequence of tokens that has the form of a preprocessing directive.
0854 [U] Attempting to #define '%s', which is a predefined macro name.
0864 [U] '#line' directive specifies line number which is not in the range 1 to 32767.
0865 [U] '#line' directive is badly formed.
0867 [U] '#line' has not been followed by a line number.
0872 [U] Result of '##' operator is not a legal preprocessing token.
0874 [U] Character string literal and wide character string literal are adjacent.
0885 [U] The token 'defined' is generated in the expansion of this macro.
0887 [U] Use of 'defined' must match either 'defined(identifier)' or 'defined identifier'.
0888 [U] 'defined' requires an identifier as an argument.
0914 [U] Source file does not end with a newline character.
0915 [U] Source file ends with a backslash character followed by a newline.
0942 [U] A * can only be used to specify array size within function prototype scope.
1331 Type or number of arguments doesn't match previous use of the function.
1332 Type or number of arguments doesn't match prototype found later.
1333 Type or number of arguments doesn't match function definition found later.
2800 Constant: Overflow in signed arithmetic operation.
2810 Constant: Dereference of NULL pointer.
2820 Constant: Arithmetic operation on NULL pointer.
2830 Constant: Division by zero.
2840 Constant: Dereference of an invalid pointer value.
3113 [U] 'return' statement includes no expression but function '%s()' is implicitly of type 'int'.
3114 [U] Function '%s()' is implicitly of type 'int' but ends without returning a value.
3239 [U] inline function '%1s' has external linkage, but is not defined within this translation unit.
3311 [u] An earlier jump to this statement will bypass the initialization of local variables.
3312 [u] This goto statement will jump into a previous block and bypass the initialization of local variables.
3319 [U] Function called with number of arguments which differs from number of parameters in definition.
3320 Type of argument no. %s differs from its type in definition of function.
3437 [u] The assert macro has been suppressed to call a function of that name.
3438 [U] #undef'ing the assert macro to call a function of that name causes undefined behaviour.
1509 '%1s' has external linkage and has multiple definitions.
1510 '%1s' has external linkage and has incompatible declarations.
-
Rule-10.1RequiredOperands shall not be of an inappropriate essential type.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
3101 Unary '-' applied to an operand of type unsigned int or unsigned long gives an unsigned result.
3102 Unary '-' applied to an operand whose underlying type is unsigned.
4500 An expression of 'essentially Boolean' type (%1s) is being used as an array subscript.
4501 An expression of 'essentially Boolean' type (%1s) is being used as the %2s operand of this arithmetic operator (%3s).
4502 An expression of 'essentially Boolean' type (%1s) is being used as the %2s operand of this bitwise operator (%3s).
4503 An expression of 'essentially Boolean' type (%1s) is being used as the left-hand operand of this shift operator (%2s).
4504 An expression of 'essentially Boolean' type (%1s) is being used as the right-hand operand of this shift operator (%2s).
4505 An expression of 'essentially Boolean' type (%1s) is being used as the %2s operand of this relational operator (%3s).
4507 An expression of 'essentially Boolean' type (%1s) is being used as the operand of this increment/decrement operator (%2s).
4510 An expression of 'essentially character' type (%1s) is being used as an array subscript.
4511 An expression of 'essentially character' type (%1s) is being used as the %2s operand of this arithmetic operator (%3s).
4512 An expression of 'essentially character' type (%1s) is being used as the %2s operand of this bitwise operator (%3s).
4513 An expression of 'essentially character' type (%1s) is being used as the left-hand operand of this shift operator (%2s).
4514 An expression of 'essentially character' type (%1s) is being used as the right-hand operand of this shift operator (%2s).
4518 An expression of 'essentially character' type (%1s) is being used as the %2s operand of this logical operator (%3s).
4519 An expression of 'essentially character' type (%1s) is being used as the first operand of this conditional operator (%2s).
4521 An expression of 'essentially enum' type (%1s) is being used as the %2s operand of this arithmetic operator (%3s).
4522 An expression of 'essentially enum' type (%1s) is being used as the %2s operand of this bitwise operator (%3s).
4523 An expression of 'essentially enum' type (%1s) is being used as the left-hand operand of this shift operator (%2s).
4524 An expression of 'essentially enum' type (%1s) is being used as the right-hand operand of this shift operator (%2s).
4527 An expression of 'essentially enum' type is being used as the operand of this increment/decrement operator.
4528 An expression of 'essentially enum' type (%1s) is being used as the %2s operand of this logical operator (%3s).
4529 An expression of 'essentially enum' type (%1s) is being used as the first operand of this conditional operator (%2s).
4532 An expression of 'essentially signed' type (%1s) is being used as the %2s operand of this bitwise operator (%3s).
4533 An expression of 'essentially signed' type (%1s) is being used as the left-hand operand of this shift operator (%2s).
4534 An expression of 'essentially signed' type (%1s) is being used as the right-hand operand of this shift operator (%2s).
4538 An expression of 'essentially signed' type (%1s) is being used as the %2s operand of this logical operator (%3s).
4539 An expression of 'essentially signed' type (%1s) is being used as the first operand of this conditional operator (%2s).
4542 A non-negative constant expression of 'essentially signed' type (%1s) is being used as the %2s operand of this bitwise operator (%3s).
4543 A non-negative constant expression of 'essentially signed' type (%1s) is being used as the left-hand operand of this shift operator (%2s).
4548 A non-negative constant expression of 'essentially signed' type (%1s) is being used as the %2s operand of this logical operator (%3s).
4549 A non-negative constant expression of 'essentially signed' type (%1s) is being used as the first operand of this conditional operator (%2s).
4558 An expression of 'essentially unsigned' type (%1s) is being used as the %2s operand of this logical operator (%3s).
4559 An expression of 'essentially unsigned' type (%1s) is being used as the first operand of this conditional operator (%2s).
4568 An expression of 'essentially floating' type (%1s) is being used as the %2s operand of this logical operator (%3s).
4569 An expression of 'essentially floating' type (%1s) is being used as the first operand of this conditional operator (%2s).
-
Rule-10.2RequiredExpressions of essentially character type shall not be used inappropriately in addition and subtraction operations
- - - - - - - - - - - - - - - - - -
QacDescription
1810 An operand of 'essentially character' type is being added to another operand of 'essentially character' type.
1811 An operand of 'essentially character' type is being subtracted from an operand of 'essentially signed' type.
1812 An operand of 'essentially character' type is being subtracted from an operand of 'essentially unsigned' type.
1813 An operand of 'essentially character' type is being balanced with an operand of 'essentially floating' type in this arithmetic operation.
-
Rule-10.3RequiredThe value of an expression shall not be assigned to an object with a narrower essential type or of a different essential type category.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
0570 This switch case label of 'essential type' '%1s', is not consistent with a controlling expression of essential type '%2s'.
0572 This switch case label of 'essential type' '%1s' is not consistent with a controlling expression which has an essential type of lower rank (%2s).
1257 An integer constant suffixed with L or LL is being converted to a type of lower rank on assignment.
1264 A suffixed floating constant is being converted to a different floating type on assignment.
1265 An unsuffixed floating constant is being converted to a different floating type on assignment.
1266 A floating constant is being converted to integral type on assignment.
1291 An integer constant of 'essentially unsigned' type is being converted to signed type on assignment.
1292 An integer constant of 'essentially signed' type is being converted to type char on assignment.
1293 An integer constant of 'essentially unsigned' type is being converted to type char on assignment.
1294 An integer constant of 'essentially signed' type is being converted to type _Bool on assignment.
1295 An integer constant of 'essentially unsigned' type is being converted to type _Bool on assignment.
1296 An integer constant of 'essentially signed' type is being converted to enum type on assignment.
1297 An integer constant of 'essentially unsigned' type is being converted to enum type on assignment.
1298 An integer constant of 'essentially signed' type is being converted to floating type on assignment.
1299 An integer constant of 'essentially unsigned' type is being converted to floating type on assignment.
2850 Constant: Implicit conversion to a signed integer type of insufficient size.
2890 Constant: Negative value implicitly converted to an unsigned type.
2900 Constant: Positive integer value truncated by implicit conversion to a smaller unsigned type.
4401 An expression of 'essentially Boolean' type (%1s) is being converted to character type, '%2s' on assignment.
4402 An expression of 'essentially Boolean' type (%1s) is being converted to enum type, '%2s' on assignment.
4403 An expression of 'essentially Boolean' type (%1s) is being converted to signed type, '%2s' on assignment.
4404 An expression of 'essentially Boolean' type (%1s) is being converted to unsigned type, '%2s' on assignment.
4405 An expression of 'essentially Boolean' type (%1s) is being converted to floating type, '%2s' on assignment.
4410 An expression of 'essentially character' type (%1s) is being converted to Boolean type, '%2s' on assignment.
4412 An expression of 'essentially character' type (%1s) is being converted to enum type, '%2s' on assignment.
4413 An expression of 'essentially character' type (%1s) is being converted to signed type, '%2s' on assignment.
4414 An expression of 'essentially character' type (%1s) is being converted to unsigned type, '%2s' on assignment.
4415 An expression of 'essentially character' type (%1s) is being converted to floating type, '%2s' on assignment.
4420 An expression of 'essentially enum' type (%1s) is being converted to Boolean type, '%2s' on assignment.
4421 An expression of 'essentially enum' type (%1s) is being converted to character type, '%2s' on assignment.
4422 An expression of 'essentially enum' type (%1s) is being converted to a different enum type, '%2s' on assignment.
4423 An expression of 'essentially enum' type (%1s) is being converted to signed type, '%2s' on assignment.
4424 An expression of 'essentially enum' type (%1s) is being converted to unsigned type, '%2s' on assignment.
4425 An expression of 'essentially enum' type (%1s) is being converted to floating type, '%2s' on assignment.
4430 An expression of 'essentially signed' type (%1s) is being converted to Boolean type, '%2s' on assignment.
4431 An expression of 'essentially signed' type (%1s) is being converted to character type, '%2s' on assignment.
4432 An expression of 'essentially signed' type (%1s) is being converted to enum type, '%2s' on assignment.
4434 A non-constant expression of 'essentially signed' type (%1s) is being converted to unsigned type, '%2s' on assignment.
4435 A non-constant expression of 'essentially signed' type (%1s) is being converted to floating type, '%2s' on assignment.
4437 A constant expression of 'essentially signed' type (%1s) is being converted to floating type, '%2s' on assignment.
4440 An expression of 'essentially unsigned' type (%1s) is being converted to Boolean type, '%2s' on assignment.
4441 An expression of 'essentially unsigned' type (%1s) is being converted to character type, '%2s' on assignment.
4442 An expression of 'essentially unsigned' type (%1s) is being converted to enum type, '%2s' on assignment.
4443 A non-constant expression of 'essentially unsigned' type (%1s) is being converted to a wider signed type, '%2s' on assignment.
4445 An expression of 'essentially unsigned' type (%1s) is being converted to floating type, '%2s' on assignment.
4446 A non-constant expression of 'essentially unsigned' type (%1s) is being converted to signed type, '%2s' on assignment.
4447 A constant expression of 'essentially unsigned' type (%1s) is being converted to signed type, '%2s' on assignment.
4450 An expression of 'essentially floating' type (%1s) is being converted to Boolean type, '%2s' on assignment.
4451 An expression of 'essentially floating' type (%1s) is being converted to character type, '%2s' on assignment.
4452 An expression of 'essentially floating' type (%1s) is being converted to enum type, '%2s' on assignment.
4453 An expression of 'essentially floating' type (%1s) is being converted to signed type, '%2s' on assignment.
4454 An expression of 'essentially floating' type (%1s) is being converted to unsigned type, '%2s' on assignment.
4460 A non-constant expression of 'essentially signed' type (%1s) is being converted to narrower signed type, '%2s' on assignment.
4461 A non-constant expression of 'essentially unsigned' type (%1s) is being converted to narrower unsigned type, '%2s' on assignment.
4462 A non-constant expression of 'essentially floating' type (%1s) is being converted to narrower floating type, '%2s' on assignment.
4463 A constant expression of 'essentially signed' type (%1s) is being converted to narrower signed type, '%2s' on assignment.
4464 A constant expression of 'essentially unsigned' type (%1s) is being converted to narrower unsigned type, '%2s' on assignment.
4465 A constant expression of 'essentially floating' type (%1s) is being converted to narrower floating type, '%2s' on assignment.
-
Rule-10.4RequiredBoth operands of an operator in which the usual arithmetic conversions are performed shall have the same essential type category
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
1800 The %1s operand (essential type: '%2s') will be implicitly converted to a floating type, '%3s', in this arithmetic operation.
1802 The %1s operand (essential type: '%2s') will be implicitly converted to a floating type, '%3s', in this relational operation.
1803 The %1s operand (essential type: '%2s') will be implicitly converted to a floating type, '%3s', in this equality operation.
1804 The %1s operand (essential type: '%2s') will be implicitly converted to a floating type, '%3s', in this conditional operation.
1820 The %1s operand is non-constant and 'essentially signed' (%2s) but will be implicitly converted to an unsigned type (%3s) in this arithmetic operation.
1821 The %1s operand is non-constant and 'essentially signed' (%2s) but will be implicitly converted to an unsigned type (%3s) in this bitwise operation.
1822 The %1s operand is non-constant and 'essentially signed' (%2s) but will be implicitly converted to an unsigned type (%3s) in this relational operation.
1823 The %1s operand is non-constant and 'essentially signed' (%2s) but will be implicitly converted to an unsigned type (%3s) in this equality operation.
1824 The %1s operand is non-constant and 'essentially signed' (%2s) but will be implicitly converted to an unsigned type (%3s) in this conditional operation.
1830 The %1s operand is constant, 'essentially signed' (%2s) and negative but will be implicitly converted to an unsigned type (%3s) in this arithmetic operation.
1831 The %1s operand is constant, 'essentially signed' (%2s) and negative but will be implicitly converted to an unsigned type (%3s) in this bitwise operation.
1832 The %1s operand is constant, 'essentially signed' (%2s) and negative but will be implicitly converted to an unsigned type (%3s) in this relational operation.
1833 The %1s operand is constant, 'essentially signed' (%2s) and negative but will be implicitly converted to an unsigned type (%3s) in this equality operation.
1834 The %1s operand is constant, 'essentially signed' (%2s) and negative but will be implicitly converted to an unsigned type (%3s) in this conditional operation.
1840 The %1s operand is constant, 'essentially signed' (%2s) and non-negative but will be implicitly converted to an unsigned type (%3s) in this arithmetic operation.
1841 The %1s operand is constant, 'essentially signed' (%2s) and non-negative but will be implicitly converted to an unsigned type (%3s) in this bitwise operation.
1842 The %1s operand is constant, 'essentially signed' (%2s) and non-negative but will be implicitly converted to an unsigned type (%3s) in this relational operation.
1843 The %1s operand is constant, 'essentially signed' (%2s) and non-negative but will be implicitly converted to an unsigned type (%3s) in this equality operation.
1844 The %1s operand is constant, 'essentially signed' (%2s) and non-negative but will be implicitly converted to an unsigned type (%3s) in this conditional operation.
1850 The %1s operand is 'essentially unsigned' (%2s) but will be implicitly converted to a signed type (%3s) in this arithmetic operation.
1851 The %1s operand is 'essentially unsigned' (%2s) but will be implicitly converted to a signed type (%3s) in this bitwise operation.
1852 The %1s operand is 'essentially unsigned' (%2s) but will be implicitly converted to a signed type (%3s) in this relational operation.
1853 The %1s operand is 'essentially unsigned' (%2s) but will be implicitly converted to a signed type (%3s) in this equality operation.
1854 The %1s operand is 'essentially unsigned' (%2s) but will be implicitly converted to a signed type (%3s) in this conditional operation.
1860 The operands of this arithmetic operator are of different 'essential signedness' but will generate a result of type 'signed int'.
1861 The operands of this bitwise operator are of different 'essential signedness' but will generate a result of type 'signed int'.
1862 The operands of this relational operator are of different 'essential signedness' but will both be promoted to 'signed int' for comparison.
1863 The operands of this equality operator are of different 'essential signedness' but will both be promoted to 'signed int' for comparison.
1864 The 2nd and 3rd operands of this conditional operator are of different 'essential signedness'. The result will be in the promoted type 'signed int'.
1880 The operands of this relational operator are expressions of different 'essential type' categories (%1s and %2s).
1881 The operands of this equality operator are expressions of different 'essential type' categories (%1s and %2s).
1882 The 2nd and 3rd operands of this conditional operator are expressions of different 'essential type' categories (%1s and %2s).
-
Rule-10.5AdvisoryThe value of an expression should not be cast to an inappropriate essential type
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
4301 An expression of 'essentially Boolean' type (%1s) is being cast to character type '%2s'.
4302 An expression of 'essentially Boolean' type (%1s) is being cast to enum type '%2s'.
4303 An expression of 'essentially Boolean' type (%1s) is being cast to signed type '%2s'.
4304 An expression of 'essentially Boolean' type (%1s) is being cast to unsigned type '%2s'.
4305 An expression of 'essentially Boolean' type (%1s) is being cast to floating type '%2s'.
4310 An expression of 'essentially character' type (%1s) is being cast to Boolean type, '%2s'.
4312 An expression of 'essentially character' type (%1s) is being cast to enum type, '%2s'.
4315 An expression of 'essentially character' type (%1s) is being cast to floating type, '%2s'.
4320 An expression of 'essentially enum' type (%1s) is being cast to Boolean type, '%2s'.
4322 An expression of 'essentially enum' type (%1s) is being cast to a different enum type, '%2s'.
4330 An expression of 'essentially signed' type (%1s) is being cast to Boolean type '%2s'.
4332 An expression of 'essentially signed' type (%1s) is being cast to enum type, '%2s'.
4340 An expression of 'essentially unsigned' type (%1s) is being cast to Boolean type '%2s'.
4342 An expression of 'essentially unsigned' type (%1s) is being cast to enum type '%2s'.
4350 An expression of 'essentially floating' type (%1s) is being cast to Boolean type '%2s'.
4351 An expression of 'essentially floating' type (%1s) is being cast to character type '%2s'.
4352 An expression of 'essentially floating' type (%1s) is being cast to enum type, '%2s'.
-
Rule-10.6RequiredThe value of a composite expression shall not be assigned to an object with wider essential type
- - - - - - - - - - - - - - - - - -
QacDescription
4490 A composite expression of 'essentially signed' type (%1s) is being converted to wider signed type, '%2s' on assignment.
4491 A composite expression of 'essentially unsigned' type (%1s) is being converted to wider unsigned type, '%2s' on assignment.
4492 A composite expression of 'essentially floating' type (%1s) is being converted to wider floating type, '%2s' on assignment.
4499 An expression which is the result of a ~ or << operation has been converted to a wider essential type on assignment.
-
Rule-10.7RequiredIf a composite expression is used as one operand of an operator in which the usual arithmetic conversions are performed then the other operand shall not have wider essential type
- - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
1890 A composite expression of 'essentially signed' type (%1s) is being implicitly converted to a wider signed type, '%2s'.
1891 A composite expression of 'essentially unsigned' type (%1s) is being implicitly converted to a wider unsigned type, '%2s'.
1892 A composite expression of 'essentially floating' type (%1s) is being implicitly converted to a wider floating type, '%2s'.
1893 The 2nd and 3rd operands of this conditional operator are both 'essentially signed' ('%1s' and '%2s') but one is a composite expression of a narrower type than the other.
1894 The 2nd and 3rd operands of this conditional operator are both 'essentially unsigned' ('%1s' and '%2s') but one is a composite expression of a narrower type than the other.
1895 The 2nd and 3rd operands of this conditional operator are both 'essentially floating' ('%1s' and '%2s') but one is a composite expression of a narrower type than the other.
-
Rule-10.8RequiredThe value of a composite expression shall not be cast to a different essential type category or a wider essential type
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
4389 A composite expression of 'essentially char' type (%1s) is being cast to a different type category, '%2s'.
4390 A composite expression of 'essentially signed' type (%1s) is being cast to a wider signed type, '%2s'.
4391 A composite expression of 'essentially unsigned' type (%1s) is being cast to a wider unsigned type, '%2s'.
4392 A composite expression of 'essentially floating' type (%1s) is being cast to a wider floating type, '%2s'.
4393 A composite expression of 'essentially signed' type (%1s) is being cast to a different type category, '%2s'.
4394 A composite expression of 'essentially unsigned' type (%1s) is being cast to a different type category, '%2s'.
4395 A composite expression of 'essentially floating' type (%1s) is being cast to a different type category, '%2s'.
4398 An expression which is the result of a ~ or << operation has been cast to a different essential type category.
4399 An expression which is the result of a ~ or << operation has been cast to a wider type.
-
Rule-11.1RequiredConversions shall not be performed between a pointer to a function and any other type
- - - - - - - - - - - - - - - - - -
QacDescription
0302 [u] Cast between a pointer to function and a floating type.
0305 [I] Cast between a pointer to function and an integral type.
0307 [u] Cast between a pointer to object and a pointer to function.
0313 Casting to different function pointer type.
-
Rule-11.2RequiredConversions shall not be performed between a pointer to an incomplete type and any other type
- - - - - - - - - - - - - - - - - -
QacDescription
0308 Non-portable cast involving pointer to an incomplete type.
0323 [u] Cast between a pointer to incomplete type and a floating type.
0324 [u] Cast between a pointer to incomplete type and an integral type.
0325 [u] Cast between a pointer to incomplete type and a pointer to function.
-
Rule-11.3RequiredA cast shall not be performed between a pointer to object type and a pointer to a different object type
- - - - - - - - - -
QacDescription
0310 Casting to different object pointer type.
3305 Pointer cast to stricter alignment.
-
Rule-11.4AdvisoryA conversion should not be performed between a pointer to object and an integer type
- - - - - - - - - - - - - - - - - - - - - -
QacDescription
0303 [I] Cast between a pointer to volatile object and an integral type.
0306 [I] Cast between a pointer to object and an integral type.
0360 An expression of pointer type is being converted to type _Bool on assignment.
0361 An expression of pointer type is being cast to type _Bool.
0362 An expression of essentially Boolean type is being cast to a pointer.
-
Rule-11.5AdvisoryA conversion should not be performed from pointer to void into pointer to object
- - - - - - - - - -
QacDescription
0316 [I] Cast from a pointer to void to a pointer to object type.
0317 [I] Implicit conversion from a pointer to void to a pointer to object type.
-
Rule-11.6RequiredA cast shall not be performed between pointer to void and an arithmetic type
- - - - - - - - - -
QacDescription
0326 [I] Cast between a pointer to void and an integral type.
0327 [I] Cast between a pointer to void and an floating type.
-
Rule-11.7RequiredA cast shall not be performed between pointer to object and a non-integer arithmetic type
- - - - - - - - - -
QacDescription
0301 [u] Cast between a pointer to object and a floating type.
0328 [u] Cast between a pointer to object and an essential type other than signed/unsigned.
-
Rule-11.8RequiredA cast shall not remove any const or volatile qualification from the type pointed to by a pointer
- - - - - - - - - -
QacDescription
0311 Dangerous pointer cast results in loss of const qualification.
0312 Dangerous pointer cast results in loss of volatile qualification.
-
Rule-11.9RequiredThe macro NULL shall be the only permitted form of integer null pointer constant
- - - - - - - - - -
QacDescription
3003 This character constant is being interpreted as a NULL pointer constant.
3004 This integral constant expression is being interpreted as a NULL pointer constant.
-
Rule-12.1AdvisoryThe precedence of operators within expressions should be made explicit
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
3389 Extra parentheses recommended to clarify the ordering of a % operator and another arithmetic operator (* / % + -).
3391 Extra parentheses recommended. A conditional operation is the operand of another conditional operator.
3392 Extra parentheses recommended. A shift, relational or equality operation is the operand of a second identical operator.
3394 Extra parentheses recommended. A shift, relational or equality operation is the operand of a different operator with the same precedence.
3395 Extra parentheses recommended. A * or / operation is the operand of a + or - operator.
3396 Extra parentheses recommended. A binary operation is the operand of a conditional operator.
3397 Extra parentheses recommended. A binary operation is the operand of a binary operator with different precedence.
-
Rule-12.2RequiredThe right hand operand of a shift operator shall lie in the range zero to one less than the width in bits of the essential type of the left hand operand
- - - - - - - - - - - - - - - - - -
QacDescription
0499 Right operand of shift operator is greater than or equal to the width of the essential type of the left operand.
2790 Constant: Right hand operand of shift operator is negative or too large.
2791 Definite: Right hand operand of shift operator is negative or too large.
2792 Apparent: Right hand operand of shift operator is negative or too large.
-
Rule-12.3AdvisoryThe comma operator should not be used
- - - - - - - - - -
QacDescription
3417 The comma operator has been used outside a 'for' statement.
3418 The comma operator has been used in a 'for' statement.
-
Rule-12.4AdvisoryEvaluation of constant expressions should not lead to unsigned integer wrap-around
- - - - - -
QacDescription
2910 Constant: Wraparound in unsigned arithmetic operation.
-
Rule-12.5MandatoryThe sizeof operator shall not have an operand which is a function parameter declared as 'array of type'
- - - - - -
QacDescription
1321 Operand of sizeof is a function parameter of array type.
-
Rule-13.1RequiredInitializer lists shall not contain persistent side-effects
- - - - - -
QacDescription
3421 Expression with possible side effects is used in an initializer list.
-
Rule-13.2RequiredThe value of an expression and its persistent side-effects shall be the same under all permitted evaluation orders
- - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
0400 [U] '%s' is modified more than once between sequence points - evaluation order unspecified.
0401 [U] '%s' may be modified more than once between sequence points - evaluation order unspecified.
0402 [U] '%s' is modified and accessed between sequence points - evaluation order unspecified.
0403 [U] '%s' may be modified and accessed between sequence points - evaluation order unspecified.
0404 More than one read access to volatile objects between sequence points.
0405 More than one modification of volatile objects between sequence points.
-
Rule-13.3AdvisoryA full expression containing an increment (++) or decrement (--) operator should have no other potential side effects other than that caused by the increment or decrement operator
- - - - - -
QacDescription
3440 Using the value resulting from a ++ or -- operation.
-
Rule-13.4AdvisoryThe result of an assignment operator should not be used
- - - - - - - - - -
QacDescription
3226 The result of an assignment is being used in an arithmetic operation or another assigning operation.
3326 The result of an assignment is being used in a logical operation.
-
Rule-13.5RequiredThe right hand operand of a logical && or || operator shall not contain persistent side effects
- - - - - -
QacDescription
3415 Right hand operand of '&&' or '||' is an expression with possible side effects.
-
Rule-13.6MandatoryThe operand of the sizeof operator shall not contain any expression which has potential side-effects
- - - - - - - - - -
QacDescription
0945 [C99] Operand of sizeof is an expression of variable length array type with side effects.
3307 The operand of 'sizeof' is an expression with implied side effects, but they will not be evaluated.
-
Rule-14.1RequiredA loop counter shall not have essentially floating type
- - - - - - - - - - - - - -
QacDescription
3339 Floating point variable used as 'while' loop control variable.
3340 Floating point variable used as 'for' loop control variable.
3342 Controlling expression of 'for' loop is a floating point comparison.
-
Rule-14.2RequiredA for loop shall be well-formed
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
2461 Loop control variable in this 'for' statement, %s, has file scope.
2462 The variable initialized in the first expression of this 'for' statement is not the variable identified as the 'loop control variable' (%s).
2463 The variable incremented in the third expression of this 'for' statement is not the variable identified as the 'loop control variable' (%s).
2464 Loop control variable, %s, modified twice in for-loop header.
2467 Loop control variable in this 'for' statement, %s, is not modified inside loop.
2468 Loop control variable in this 'for' statement, %s, is not modified inside loop but has file scope.
2469 Loop control variable in this 'for' statement, %s, is modified in the body of the loop.
2471 Unable to identify a loop control variable.
2472 More than one possible loop control variable.
-
Rule-14.3RequiredControlling expressions shall not be invariant
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
2741 This 'if' controlling expression is a constant expression and its value is 'true'.
2742 This 'if' controlling expression is a constant expression and its value is 'false'.
2990 The value of this loop controlling expression is always 'true'.
2991 The value of this 'if' controlling expression is always 'true'.
2992 The value of this 'if' controlling expression is always 'false'.
2993 The value of this 'do - while' loop controlling expression is always 'false'. The loop will only be executed once.
2994 The value of this 'while' or 'for' loop controlling expression is always 'false'. The loop will not be entered.
2997 The first operand of this conditional operator is always 'true'.
2998 The first operand of this conditional operator is always 'false'.
3493 The first operand of this conditional operator is always constant 'true'.
3494 The first operand of this conditional operator is always constant 'false'.
-
Rule-14.4RequiredThe controlling expression of an if-statement and the controlling expression of an iteration-statement shall have essentially Boolean type
- - - - - -
QacDescription
3344 Controlling expression is not an 'essentially Boolean' expression.
-
Rule-15.1AdvisoryThe goto statement should not be used
- - - - - -
QacDescription
2001 A 'goto' statement has been used.
-
Rule-15.2RequiredThe goto statement shall jump to a label declared later in the same function
- - - - - -
QacDescription
3310 This 'goto' statement involves a backward jump.
-
Rule-15.3RequiredAny label referenced by a goto statement shall be declared in the same block, or in any block enclosing the goto statement
- - - - - -
QacDescription
3327 This goto statement references a label that is declared in a separate block.
-
Rule-15.4AdvisoryThere should be no more than one break or goto statement used to terminate any iteration statement
- - - - - -
QacDescription
0771 More than one 'break' statement has been used to terminate this iteration statement.
-
Rule-15.5AdvisoryA function should have a single point of exit at the end
- - - - - -
QacDescription
2889 This function has more than one 'return' path.
-
Rule-15.6RequiredThe body of an iteration-statement or a selection-statement shall be a compound-statement
- - - - - - - - - - - - - - - - - - - - - -
QacDescription
2212 Body of control statement is not enclosed within braces.
2214 Body of control statement is on the same line and is not enclosed within braces.
2218 Body of switch statement is not enclosed within braces.
2219 Body of switch statement is on the same line and is not enclosed within braces.
3402 Braces are needed to clarify the structure of this 'if'-'if'-'else' statement.
-
Rule-15.7RequiredAll if ... else if constructs shall be terminated with an else statement
- - - - - - - - - -
QacDescription
2004 No concluding 'else' exists in this 'if'-'else'-'if' statement.
2013 This 'if .. else if ' construct 'else' statement is empty.
-
Rule-16.1RequiredAll switch statements shall be well-formed
- - - - - - - - - -
QacDescription
2008 Code statements precede the first label in this 'switch' construct.
3234 Declarations precede the first label in this 'switch' construct.
-
Rule-16.2RequiredA switch label shall only be used when the most closely-enclosing compound statement is the body of a switch statement
- - - - - -
QacDescription
2019 'Switch' label is located within a nested code block.
-
Rule-16.3RequiredAn unconditional break statement shall terminate every switch-clause
- - - - - - - - - -
QacDescription
2003 The preceding 'switch' clause is not empty and does not end with a 'jump' statement. Execution will fall through.
2020 Final 'switch' clause does not end with an explicit 'jump' statement.
-
Rule-16.4RequiredEvery switch statement shall have a default label
- - - - - - - - - -
QacDescription
2002 No 'default' label found in this 'switch' statement.
2016 This 'switch' statement 'default' clause is empty.
-
Rule-16.5RequiredA default label shall appear as either the first or the last switch label of a switch statement
- - - - - -
QacDescription
2012 This 'default' label is neither the first nor the last label within the 'switch' block.
-
Rule-16.6RequiredEvery switch statement shall have at least two switch-clauses
- - - - - -
QacDescription
3315 This 'switch' statement is redundant.
-
Rule-16.7RequiredA switch-expression shall not have essentially Boolean type
- - - - - -
QacDescription
0735 Switch expression is of essentially Boolean type.
-
Rule-17.1RequiredThe features of shall not be used
- - - - - - - - - -
QacDescription
5130 Use of standard header file .
1337 Function defined with a variable number of parameters.
-
Rule-17.2RequiredFunctions shall not call themselves, either directly or indirectly
- - - - - - - - - -
QacDescription
3670 Recursive call to function containing this call.
1520 Functions are indirectly recursive.
-
Rule-17.3MandatoryA function shall not be declared implicitly
- - - - - -
QacDescription
3335 No function declaration. Implicit declaration inserted: 'extern int %s();'.
-
Rule-17.4MandatoryAll exit paths from a function with non-void return type shall have an explicit return statement with an expression
- - - - - - - - - - - - - - - - - - - - - -
QacDescription
0745 [U] 'return;' found in '%s()', which has been defined with a non-'void' return type.
2887 Function 'main' ends with an implicit 'return' statement.
2888 This function has been declared with a non-void 'return' type but ends with an implicit 'return ;' statement.
3113 [U] 'return' statement includes no expression but function '%s()' is implicitly of type 'int'.
3114 [U] Function '%s()' is implicitly of type 'int' but ends without returning a value.
-
Rule-17.5AdvisoryThe function argument corresponding to a parameter declared to have an array type shall have an appropriate number of elements
- - - - - - - - - - - - - - - - - -
QacDescription
2781 Definite: Function argument has fewer elements than the array dimension in the parameter declaration for non-inlined call.
2782 Apparent: Function argument has fewer elements than the array dimension in the parameter declaration for non-inlined call.
2783 Suspicious: Function argument has fewer elements than the array dimension in the parameter declaration for non-inlined call.
2784 Possible: Function argument has fewer elements than the array dimension in the parameter declaration for non-inlined call.
-
Rule-17.6MandatoryThe declaration of an array parameter shall not contain the static keyword between the [ ]
- - - - - -
QacDescription
1058 [C99] The keyword 'static' is used in the declaration of a function parameter of array type.
-
Rule-17.7RequiredThe value returned by a function having non-void return type shall be used
- - - - - -
QacDescription
3200 '%s' returns a value which is not being used.
-
Rule-17.8AdvisoryA function parameter should not be modified
- - - - - - - - - - - - - -
QacDescription
1338 The parameter '%s' is being modified.
1339 Evaluating the address of the parameter '%s'.
1340 Storing the address of the parameter '%s' in a constant pointer.
-
Rule-18.1RequiredA pointer resulting from arithmetic on a pointer operand shall address an element of the same array as that pointer operand
- - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
2840 Constant: Dereference of an invalid pointer value.
2841 Definite: Dereference of an invalid pointer value.
2842 Apparent: Dereference of an invalid pointer value.
2930 Constant: Computing an invalid pointer value.
2931 Definite: Computing an invalid pointer value.
2932 Apparent: Computing an invalid pointer value.
-
Rule-18.2RequiredSubtraction between pointers shall only be applied to pointers that address elements of the same array
- - - - - - - - - - - - - -
QacDescription
2668 Subtraction of a pointer to an array and a pointer to a non array.
2761 Definite: Subtracting pointers that address different objects.
2762 Apparent: Subtracting pointers that address different objects.
-
Rule-18.3RequiredThe relational operators >, >=, < and <= shall not be applied to objects of pointer type except where they point into the same object
- - - - - - - - - - - - - -
QacDescription
2669 Comparison of a pointer to an array and a pointer to a non array.
2771 Definite: Comparing pointers that address different objects.
2772 Apparent: Comparing pointers that address different objects.
-
Rule-18.4AdvisoryThe +, -, += and -= operators should not be applied to an expression of pointer type
- - - - - -
QacDescription
0488 Performing pointer arithmetic.
-
Rule-18.5AdvisoryDeclarations should contain no more than two levels of pointer nesting
- - - - - - - - - - - - - - - - - -
QacDescription
3260 Typedef defined with more than 2 levels of indirection.
3261 Member of struct/union defined with more than 2 levels of indirection.
3262 Object defined or declared with more than 2 levels of indirection.
3263 Function defined or declared with a return type which has more than 2 levels of indirection.
-
Rule-18.6RequiredThe address of an object with automatic storage shall not be copied to another object that persists after the first object has ceased to exist
- - - - - - - - - - - - - - - - - -
QacDescription
3217 Address of automatic object exported to a pointer with linkage or wider scope.
3225 Address of automatic object exported using a function parameter.
3230 Address of automatic object assigned to local pointer with static storage duration.
4140 Address of automatic object exported in function return value.
-
Rule-18.7RequiredFlexible array members shall not be declared
- - - - - -
QacDescription
1060 [C99] A flexible array member has been declared.
-
Rule-18.8RequiredVariable-length array types shall not be used
- - - - - - - - - -
QacDescription
1051 [C99] A variable length array has been declared.
1052 [C99] A variable length array of unspecified size has been declared.
-
Rule-19.1MandatoryAn object shall not be assigned or copied to an overlapping object
- - - - - - - - - - - - - -
QacDescription
0681 [U] Assignment between two incompatible members of the same union.
2776 Definite: Copy between overlapping objects.
2777 Apparent: Copy between overlapping objects.
-
Rule-19.2AdvisoryThe union keyword should not be used
- - - - - - - - - -
QacDescription
0750 A union type specifier has been defined.
0759 An object of union type has been defined.
-
Rule-2.1RequiredA project shall not contain unreachable code
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
0594 Negative 'case' label expression is incompatible with unsigned controlling expression in 'switch' statement.
1460 'Switch' label value, %s, not contained in enum type.
2744 This 'while' or 'for' loop controlling expression is a constant expression and its value is 'false'. The loop will not be entered.
2880 This code is unreachable.
2882 This 'switch' statement will bypass the initialization of local variables.
3219 Static function '%s()' is not used within this translation unit.
1503 The function '%1s' is defined but is not used within this project.
-
Rule-2.2RequiredThere shall be no dead code
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
2980 The value of this function parameter is never used before being modified.
2981 This initialization is redundant. The value of this object is never used before being modified.
2982 This assignment is redundant. The value of this object is never used before being modified.
2983 This assignment is redundant. The value of this object is never subsequently used.
2985 This operation is redundant. The value of the result is always that of the left-hand operand.
2986 This operation is redundant. The value of the result is always that of the right-hand operand.
2987 This function call produces no side effects and is redundant.
2995 The result of this logical operation is always 'true'.
2996 The result of this logical operation is always 'false'.
3110 The left-hand operand of this ',' has no side effects.
3112 This statement has no side-effect - it can be removed.
3404 Statement contains a redundant * operator at top level. *p++ means *(p++) not (*p)++.
3422 Statement contains a redundant operator at top level.
3423 Statement contains a redundant cast at top level.
3424 Statement contains a redundant & or | at top level.
3425 One branch of this conditional operation is a redundant expression.
3426 Right hand side of comma expression has no side effect and its value is not used.
3427 Right hand side of logical operator has no side effect and its value is not used.
-
Rule-2.3AdvisoryA project should not contain unused type declarations
- - - - - -
QacDescription
3205 The identifier '%s' is not used and could be removed.
-
Rule-2.4AdvisoryA project should not contain unused tag declarations
- - - - - - - - - -
QacDescription
3213 The tag '%s' is not used and could be removed.
1755 The tag '%1s' is declared but not used within this project.
-
Rule-2.5AdvisoryA project should not contain unused macro declarations
- - - - - -
QacDescription
3214 The macro '%s' is not used and could be removed.
-
Rule-2.6AdvisoryA function should not contain unused label declarations
- - - - - -
QacDescription
3202 The label '%s:' is not used in this function and could be removed.
-
Rule-2.7AdvisoryThere should be no unused parameters in functions
- - - - - -
QacDescription
3206 The parameter '%s' is not used in this function.
-
Rule-20.1Advisory#include directives should only be preceded by preprocessor directives or comments
- - - - - -
QacDescription
5087 Use of #include directive after code fragment.
-
Rule-20.10AdvisoryThe # and ## preprocessor operators should not be used
- - - - - - - - - -
QacDescription
0341 Using the stringify operator '#'.
0342 Using the glue operator '##'.
-
Rule-20.11RequiredA macro parameter immediately following a # operator shall not immediately be followed by a ## operator
- - - - - -
QacDescription
0892 This macro parameter is preceded by '#' and followed by '##'.
-
Rule-20.12RequiredA macro parameter used as an operand to the # or ## operators, which is itself subject to further macro replacement, shall only be used as an operand to these operators
- - - - - -
QacDescription
0893 Macro parameter '%s' is inconsistently subject to macro replacement.
-
Rule-20.13RequiredA line whose first token is # shall be a valid preprocessing directive
- - - - - -
QacDescription
3115 Unrecognized preprocessing directive has been ignored because of conditional inclusion directives.
-
Rule-20.14RequiredAll #else, #elif and #endif preprocessor directives shall reside in the same file as the #if, #ifdef or #ifndef directive to which they are related
- - - - - - - - - -
QacDescription
3317 '#if...' not matched by '#endif' in included file. This is probably an error.
3318 '#else'/'#elif'/'#endif' in included file matched '#if...' in parent file. This is probably an error.
-
Rule-20.2RequiredThe ', " or \ characters and the /* or // character sequences shall not occur in a header file name
- - - - - - - - - - - - - -
QacDescription
0813 [U] Using any of the characters ' " or /* in '#include <%s>' gives undefined behaviour.
0814 [U] Using the characters ' or /* in '#include "%s"' gives undefined behaviour.
0831 [E] Use of '\\' in this '#include' line is a PC extension - this usage is non-portable.
-
Rule-20.3RequiredThe #include directive shall be followed by either a or "filename" sequence
- - - - - - - - - - - - - -
QacDescription
0817 [S] Closing quote or bracket '>' missing from include filename.
0821 [C] '#include' does not identify a header or source file that can be processed.
0840 [E] Extra tokens at end of #include directive.
-
Rule-20.4RequiredA macro shall not be defined with the same name as a keyword
- - - - - - - - - -
QacDescription
3439 Macro redefines a keyword.
3468 The name of this macro is a reserved identifier in C90 and a keyword in C99.
-
Rule-20.5Advisory#undef should not be used
- - - - - -
QacDescription
0841 Using '#undef'.
-
Rule-20.6RequiredTokens that look like a preprocessing directive shall not occur within a macro argument
- - - - - -
QacDescription
0853 [U] Macro arguments contain a sequence of tokens that has the form of a preprocessing directive.
-
Rule-20.7RequiredExpressions resulting from the expansion of macro parameters shall be enclosed in parentheses
- - - - - - - - - -
QacDescription
3430 Macro argument expression may require parentheses.
3432 Simple macro argument expression is not parenthesized.
-
Rule-20.8RequiredThe controlling expression of a #if or #elif preprocessing directive shall evaluate to 0 or 1
- - - - - -
QacDescription
0894 '#%s' directive controlling expression does not evaluate to zero or one.
-
Rule-20.9RequiredAll identifiers used in the controlling expression of #if or #elif preprocessing directives shall be #define'd before evaluation
- - - - - -
QacDescription
3332 The macro '%s' used in this '#if' or '#elif' expression is not defined.
-
Rule-21.1Required#define and #undef shall not be used on a reserved identifier or reserved macro name
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
0603 [U] The macro identifier '%s' is reserved.
0836 [U] Definition of macro named 'defined'.
0848 [U] Attempting to #undef '%s', which is a predefined macro name.
0854 [U] Attempting to #define '%s', which is a predefined macro name.
4600 The macro '%1s' is also defined in '<%2s>'.
4601 The macro '%1s' is the name of an identifier in '<%2s>'.
4620 The macro '%1s' may also be defined as a macro in '<%2s>'.
4621 The macro '%1s' may also be defined as a typedef in '<%2s>'.
-
Rule-21.10RequiredThe Standard Library time and date functions shall not be used
- - - - - -
QacDescription
5127 Use of standard header file .
-
Rule-21.11RequiredThe standard header file shall not be used
- - - - - -
QacDescription
5131 Use of standard header file .
-
Rule-21.12AdvisoryThe exception handling features of should not be used
- - - - - -
QacDescription
5136 Use of exception handling identifier: feclearexcept, fegetexceptflag, feraiseexcept, fesetexceptflag or fetestexcept.
-
Rule-21.13MandatoryAny value passed to a function in shall be representable as an unsigned char or be the value EOF
- - - - - - - - - -
QacDescription
2796 Definite: Calling a standard library character handling function with an invalid character value.
2797 Apparent: Calling a standard library character handling function with an invalid character value.
-
Rule-21.14RequiredThe Standard Library function memcmp shall not be used to compare null terminated strings
- - - - - - - - - -
QacDescription
2785 Constant: Null terminated string is being passed as argument to Standard Library function memcmp.
2786 Definite: Null terminated string is being passed as argument to Standard Library function memcmp.
-
Rule-21.15RequiredThe pointer arguments to the Standard Library functions memcpy, memmove and memcmp shall be pointers to qualified or unqualified versions of compatible types
- - - - - - - - - - - - - -
QacDescription
1487 Comparing the representations of objects of different types.
1495 Destination and source objects have incompatible types.
1496 Destination and source objects may have incompatible types.
-
Rule-21.16RequiredThe pointer arguments to the Standard Library function memcpy shall point to either a pointer type, an essentially signed type, an essentially unsigned type, an essentially Boolean type or an essentially enum type
- - - - - - - - - - - - - - - - - - - - - -
QacDescription
1488 Comparison of a struct object representation.
1489 Comparison of a union object representation.
1490 Comparison of a floating point object representation.
1491 Comparison of an object representation.
1497 Comparison of a string object representation.
-
Rule-21.17MandatoryUse of the string handling functions from shall not result in accesses beyond the bounds of the objects referenced by their pointer parameters
- - - - - - - - - -
QacDescription
2835 Constant: Non null terminated string used in a string function.
2836 Definite: Non null terminated string used in a string function.
-
Rule-21.18MandatoryThe size_t argument passed to any function in shall have an appropriate value
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
2840 Constant: Dereference of an invalid pointer value.
2841 Definite: Dereference of an invalid pointer value.
2842 Apparent: Dereference of an invalid pointer value.
2865 Constant: Using 0 as size parameter of a function call.
2866 Definite: Using 0 as size parameter of a function call.
2867 Apparent: Using 0 as size parameter of a function call.
2868 Suspicious: Using 0 as size parameter of a function call.
2869 Possible: Using 0 as size parameter of a function call.
-
Rule-21.19MandatoryThe pointers returned by the Standard Library functions lovaleconv, getenv, setlocale or strerror shall only be used as if they have pointer to const-qualified type
- - - - - - - - - - - - - - - - - -
QacDescription
1492 The result of library function '%s' is used to modify the referenced object.
1493 The result of library function '%s' is used as a pointer to a modifiable object.
1494 The result of library function '%s' might be modified.
1498 The string referenced by type 'struct lconv' member '%s' is being modified.
-
Rule-21.2RequiredA reserved identifier or macro name shall not be declared
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
0602 [U] The identifier '%s' is reserved for use by the library.
4602 The identifier '%1s' is declared as a macro in '<%2s>'.
4603 The object/function '%1s'is being defined with the same name as an ordinary identifier defined in '<%2s>'.
4604 The object/function '%1s' is being declared with the same name as an ordinary identifier defined in '<%2s>'.
4605 The typedef '%1s' is also defined in '<%2s>'.
4606 The typedef '%1s' has the same name as another ordinary identifier in '<%2s>'.
4607 The enum constant '%1s' has the same name as another ordinary identifier in '<%2s>'.
4608 The tag '%1s' is also defined in '<%2s>'.
-
Rule-21.20MandatoryThe pointer returned by the Standard Library functions asctime, ctime, gmtime, localtime, localeconv, getenv, setlocale, or strerror shall not be used following a subsequent call to the same function
- - - - - - - - - -
QacDescription
2681 Definite: Using an invalidated value '%s' returned from a Standard Library function.
2682 Apparent: Using an invalidated value '%s' returned from a Standard Library function.
-
Rule-21.3RequiredThe memory allocation and deallocation functions of shall not be used
- - - - - -
QacDescription
5118 Use of memory allocation or deallocation function: calloc, malloc, realloc or free.
-
Rule-21.4RequiredThe standard header file shall not be used
- - - - - -
QacDescription
5132 Use of standard header file .
-
Rule-21.5RequiredThe standard header file shall not be used
- - - - - -
QacDescription
5123 Use of standard header file .
-
Rule-21.6RequiredThe Standard Library input/output functions shall not be used
- - - - - -
QacDescription
5124 The Standard Library input/output functions shall not be used
-
Rule-21.7RequiredThe atof, atoi, atol and atoll functions of shall not be used
- - - - - -
QacDescription
5125 Use of function: atof, atoi, atol or atoll.
-
Rule-21.8RequiredThe library functions abort, exit and system of shall not be used
- - - - - - - - - -
QacDescription
5126 Use of function: abort, exit or system.
5128 Use of function: getenv.
-
Rule-21.9RequiredThe library functions bsearch and qsort of shall not be used
- - - - - -
QacDescription
5135 Use of function: bsearch or qsort.
-
Rule-22.1RequiredAll resources obtained dynamically by means of Standard Library functions shall be explicitly released
- - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
2701 Definite: Opened file is not closed.
2702 Apparent: Opened file is not closed.
2706 Definite: Allocated memory is not deallocated.
2707 Apparent: Allocated memory is not deallocated.
2736 Definite: Created resource is not destroyed.
2737 Apparent: Created resource is not destroyed.
-
Rule-22.10RequiredThe value of errno shall only be tested when the last function to be called was an errno-setting-function
- - - - - -
QacDescription
2503 Testing of 'errno' is not immediately preceded by a call to an 'errno' setting function.
-
Rule-22.2MandatoryA block of memory shall only be freed if it was allocated by means of a Standard Library function
- - - - - - - - - -
QacDescription
2721 Definite: Deallocation of non dynamic memory.
2722 Apparent: Deallocation of non dynamic memory.
-
Rule-22.3RequiredThe same file shall not be open for read and write access at the same time on different streams
- - - - - - - - - - - - - -
QacDescription
2691 Definite: The same file will be open with write access and another mode.
2692 Apparent: The same file will be open with write access and another mode.
2693 Suspicious: The same file will be open with write access and another mode.
-
Rule-22.4MandatoryThere shall be no attempt to write to a stream which has been opened as read-only
- - - - - - - - - - - - - -
QacDescription
2686 Definite: Writing to a file opened for reading.
2687 Apparent: Writing to a file opened for reading.
2688 Suspicious: Writing to a file opened for reading.
-
Rule-22.5MandatoryA pointer to a FILE object shall not be dereferenced
- - - - - - - - - -
QacDescription
1485 A pointer to a FILE object is dereferenced.
1486 A pointer to a FILE object is converted to a different type.
-
Rule-22.6MandatoryThe value of a pointer to a FILE shall not be used after the associated stream has been closed
- - - - - - - - - -
QacDescription
2696 Definite: Attempt to access a file which has been closed.
2697 Apparent: Attempt to access a file which has been closed.
-
Rule-22.7RequiredThe macro EOF shall on ly be compared with the unmodified return value from any Standard Library function capable of returning EOF
- - - - - - - - - -
QacDescription
2671 Definite: The value being compared with macro EOF does not originate from an EOF returning function.
2676 Definite: The value originating from an EOF returning function was modified before being compared with macro EOF.
-
Rule-22.8RequiredThe value of errno shall be set to zero prior to a call to an errno-setting-function
- - - - - -
QacDescription
2500 Call to '%s' is not immediately preceded by the zeroing of 'errno'.
-
Rule-22.9RequiredThe value of errno shall be tested against zero after calling an errno-setting-function
- - - - - -
QacDescription
2501 Call to '%s' is not immediately followed by the testing of 'errno'.
-
Rule-3.1RequiredThe character sequences /* and // shall not be used within a comment.
- - - - - -
QacDescription
3108 Nested comments are not recognized in the ISO standard.
-
Rule-3.2RequiredLine-splicing shall not be used in // comments.
- - - - - -
QacDescription
5134 C++ style comment uses line splicing.
-
Rule-4.1RequiredOctal and hexadecimal escape sequences shall be terminated
- - - - - - - - - -
QacDescription
3636 Octal escape sequence '%s' is not terminated.
3637 Hexadecimal escape sequence '%s' is not terminated.
-
Rule-4.2AdvisoryTrigraphs should not be used
- - - - - -
QacDescription
3601 Trigraphs (??x) are an ISO feature.
-
Rule-5.1RequiredExternal identifiers shall be distinct
- - - - - -
QacDescription
0777 [U] External identifier does not differ from other identifier(s) (e.g. '%s') within the specified number of significant characters.
-
Rule-5.2RequiredIdentifiers declared in the same scope and name space shall be distinct
- - - - - -
QacDescription
0779 [U] Identifier does not differ from other identifier(s) (e.g. '%s') within the specified number of significant characters.
-
Rule-5.3RequiredAn identifier declared in an inner scope shall not hide an identifier declared in an outer scope
- - - - - - - - - - - - - -
QacDescription
0795 Identifier matches other identifier(s) (e.g. '%s') in an outer scope within the specified number of significant characters.
2547 This declaration of tag '%s' hides a more global declaration.
3334 This declaration of '%s' hides a more global declaration.
-
Rule-5.4RequiredMacro identifiers shall be distinct
- - - - - - - - - -
QacDescription
0788 This identifier, '%s', is used as both a macro name and a function-like macro parameter name.
0791 [U] Macro identifier does not differ from other macro identifier(s) (e.g. '%s') within the specified number of significant characters.
-
Rule-5.5RequiredIdentifiers shall be distinct from macro names
- - - - - - - - - - - - - - - - - -
QacDescription
0784 Identifier '%s' is also used as a macro name.
0785 Identifier matches other macro name(s) (e.g. '%s') in first 31 characters.
0786 Identifier matches other macro name(s) (e.g. '%s') in first 63 characters.
0787 Identifier does not differ from other macro name(s) (e.g. '%s') within the specified number of significant characters.
-
Rule-5.6RequiredA typedef name shall be a unique identifier
- - - - - - - - - - - - - -
QacDescription
1506 The identifier '%1s' is declared as a typedef and is used elsewhere for a different kind of declaration.
1507 '%1s' is used as a typedef for different types.
1508 The typedef '%1s' is declared in more than one location.
-
Rule-5.7RequiredA tag name shall be a unique identifier
- - - - - - - - - -
QacDescription
2547 This declaration of tag '%s' hides a more global declaration.
1750 '%1s' has multiple definitions.
-
Rule-5.8RequiredIdentifiers that define objects or functions with external linkage shall be unique
- - - - - - - - - - - - - -
QacDescription
1525 Object/function with external linkage has same identifier as another object/function with internal linkage.
1526 Object with no linkage has same identifier as another object/function with external linkage.
1756 External identifier '%1s' shall be unique.
-
Rule-5.9AdvisoryIdentifiers that define objects or functions with internal linkage should be unique
- - - - - - - - - - - - - -
QacDescription
1525 Object/function with external linkage has same identifier as another object/function with internal linkage.
1527 Object/function with internal linkage has same identifier as another object/function with internal linkage.
1528 Object with no linkage has same identifier as another object/function with internal linkage.
-
Rule-6.1RequiredBit-fields shall only be declared with an appropriate type
- - - - - - - - - -
QacDescription
0634 [I] Bit-fields in this struct/union have not been declared explicitly as unsigned or signed.
0635 [E] Bit-fields in this struct/union have been declared with types other than int, signed int, unsigned int or _Bool.
-
Rule-6.2RequiredSingle-bit named bit fields shall not be of a signed type
- - - - - -
QacDescription
3660 Named bit-field consisting of a single bit declared with a signed type.
-
Rule-7.1RequiredOctal constants shall not be used
- - - - - - - - - -
QacDescription
0336 Macro defined as an octal constant.
0339 Octal constant used.
-
Rule-7.2RequiredA "u" or "U" suffix shall be applied to all integer constants that are represented in an unsigned type
- - - - - -
QacDescription
1281 Integer literal constant is of an unsigned type but does not include a "U" suffix.
-
Rule-7.3RequiredThe lowercase character "l" shall not be used in a literal suffix
- - - - - -
QacDescription
1280 A lowercase letter L (l) has been used in an integer or floating suffix.
-
Rule-7.4RequiredA string literal shall not be assigned to an object unless the object's type is "pointer to const-qualified char"
- - - - - - - - - -
QacDescription
0752 String literal passed as argument to function whose parameter is not a 'pointer to const'.
0753 String literal assigned to pointer which is not a 'pointer to const'.
-
Rule-8.1RequiredTypes shall be explicitly specified
- - - - - - - - - - - - - -
QacDescription
2050 The 'int' type specifier has been omitted from a function declaration.
2051 The 'int' type specifier has been omitted from an object declaration.
1525 Object/function with external linkage has same identifier as another object/function with internal linkage.
-
Rule-8.10RequiredAn inline function shall be declared with the static storage class
- - - - - - - - - -
QacDescription
3240 inline function '%s' is being defined with external linkage.
3243 inline function '%s' is also an 'external definition'.
-
Rule-8.11AdvisoryWhen an array with external linkage is declared, its size should be explicitly specified
- - - - - -
QacDescription
3684 Array declared with unknown size.
-
Rule-8.12RequiredWithin an enumerator list, the value of an implicitly-specified enumeration constant shall be unique
- - - - - -
QacDescription
0724 The value of this implicitly-specified enumeration constant is not unique.
-
Rule-8.13AdvisoryA pointer should point to a const-qualified type whenever possible
- - - - - -
QacDescription
3673 The object addressed by the pointer parameter '%s' is not modified and so the pointer could be of type 'pointer to const'.
-
Rule-8.14RequiredThe restrict type qualifier shall not be used
- - - - - -
QacDescription
1057 [C99] The keyword 'restrict' has been used.
-
Rule-8.2RequiredFunction types shall be in prototype form with named parameters
- - - - - - - - - - - - - - - - - - - - - -
QacDescription
1335 Parameter identifiers missing in function prototype declaration.
1336 Parameter identifiers missing in declaration of a function type.
3001 Function has been declared with an empty parameter list.
3002 Defining '%s()' with an identifier list and separate parameter declarations is an obsolescent feature.
3007 "void" has been omitted when defining a function with no parameters.
-
Rule-8.3RequiredAll declarations of an object or function shall use the same names and type qualifiers
- - - - - - - - - - - - - -
QacDescription
0624 Function '%s' is declared using typedefs which are different to those in a previous declaration.
1330 The parameter identifiers in this function declaration differ from those in a previous declaration.
3675 Function parameter declared with type qualification which differs from previous declaration.
-
Rule-8.4RequiredA compatible declaration shall be visible when an object or function with external linkage is defined
- - - - - -
QacDescription
3408 '%s' has external linkage and is being defined without any previous declaration.
-
Rule-8.5RequiredAn external object or function shall be declared once in one and only one file
- - - - - - - - - - - - - -
QacDescription
3449 Multiple declarations of external object or function.
3451 The global identifier '%s' has been declared in more than one file.
1513 Identifier '%1s' with external linkage has separate non-defining declarations in more than one location.
-
Rule-8.6RequiredAn identifier with external linkage shall have exactly one external definition
- - - - - - - - - - - - - - - - - - - - - -
QacDescription
0630 [U] More than one definition of '%s' (with external linkage).
3406 Object/function '%s', with external linkage, has been defined in a header file.
1509 '%1s' has external linkage and has multiple definitions.
1752 The object '%1s' with external linkage is declared but not defined within this project.
1753 The function '%1s' with external linkage is declared but not defined within this project.
-
Rule-8.7AdvisoryFunctions and objects should not be defined with external linkage if they are referenced in only one translation unit
- - - - - - - - - - - - - - - - - -
QacDescription
1504 The object '%1s' is only referenced in the translation unit where it is defined.
1505 The function '%1s' is only referenced in the translation unit where it is defined.
1531 The object '%1s' is referenced in only one translation unit - but not the one in which it is defined.
1532 The function '%1s' is only referenced in one translation unit - but not the one in which it is defined.
-
Rule-8.8RequiredThe static storage class specifier shall be used in all declarations of objects and functions that have internal linkage
- - - - - -
QacDescription
3224 This identifier has previously been declared with internal linkage but is not declared here with the static storage class specifier.
-
Rule-8.9AdvisoryAn object should be defined at block scope if its identifier only appears in a single function
- - - - - - - - - - - - - -
QacDescription
3218 File scope static, '%s', is only accessed in one function.
1514 The object '%1s' is only referenced by function '%2s', in the translation unit where it is defined
1533 The object '%1s' is only referenced by function '%2s'.
-
Rule-9.1MandatoryThe value of an object with automatic storage duration shall not be read before it has been set
- - - - - - - - - - - - - - - - - - - - - -
QacDescription
2883 This 'goto' statement will always bypass the initialization of local variables.
2961 Definite: Using value of uninitialized automatic object '%s'.
2962 Apparent: Using value of uninitialized automatic object '%s'.
2971 Definite: Passing address of uninitialized object '%s' to a function parameter declared as a pointer to const.
2972 Apparent: Passing address of uninitialized object '%s' to a function parameter declared as a pointer to const.
-
Rule-9.2RequiredThe initializer for an aggregate or union shall be enclosed in braces
- - - - - - - - - -
QacDescription
0693 Struct initializer is missing the optional {.
0694 Array initializer is missing the optional {.
-
Rule-9.3RequiredArrays shall not be partially initialized
- - - - - -
QacDescription
0686 Array has fewer initializers than its declared size. Default initialization is applied to the remainder of the array elements.
-
Rule-9.4RequiredAn element of an object shall not be initialized more than once
- - - - - - - - - - - - - -
QacDescription
1397 Array element '%s' has already been initialized.
1398 Structure member '%s' has already been initialized.
1399 A union member has already been initialized.
-
Rule-9.5RequiredWhere designated initializers are used to initialize an array object the size of the array shall be specified explicitly
- - - - - -
QacDescription
3676 Designators are used to initialize an array of unspecified size.
-
-
-
- -This section targets to provide an overview of Guidelines Recategorization Plan. -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GuidelineDescriptionCategoryRevised Category
Dir-1.1Any implementation-defined behaviour on which the output of the program depends shall be documented and understoodRequiredRequired
Dir-2.1All source files shall compile without any compilation errorsRequiredDisapplied
Dir-3.1All code shall be traceable to documented requirementsRequiredDisapplied
Dir-4.1Run-time failures shall be minimizedRequiredRequired
Dir-4.10Precautions shall be taken in order to prevent the contents of a header file being included more then onceRequiredRequired
Dir-4.11The validity of values passed to library functions shall be checkedRequiredDisapplied
Dir-4.12Dynamic memory allocation shall not be usedRequiredDisapplied
Dir-4.13Functions which are designed to provide operations on a resource should be called in an appropriate sequenceAdvisoryDisapplied
Dir-4.14The validity of values received from external sources shall be checkedRequiredRequired
Dir-4.2All usage of assembly language should be documentedAdvisoryAdvisory
Dir-4.3Assembly language shall be encapsulated and isolatedRequiredRequired
Dir-4.4Sections of code should not be "commented out"AdvisoryDisapplied
Dir-4.5Identifiers in the same name space with overlapping visibility should be typographically unambiguousAdvisoryDisapplied
Dir-4.6typedefs that indicate size and signedness should be used in place of the basic numerical typesAdvisoryAdvisory
Dir-4.7If a function returns error information, then that error information shall be testedRequiredDisapplied
Dir-4.8If a pointer to a structure or union is never dereferenced within a translation unit, then the implementation of the object should be hiddenAdvisoryDisapplied
Dir-4.9A function should be used in preference to a function-like macro where they are interchangeableAdvisoryDisapplied
Rule-1.1The program shall contain no violations of the standard C syntax and constraints, and shall not exceed the implementation's translation limitsRequiredRequired
Rule-1.2Language extensions should not be usedAdvisoryAdvisory
Rule-1.3There shall be no occurrence of undefined or critical unspecified behaviourRequiredRequired
Rule-10.1Operands shall not be of an inappropriate essential type.RequiredRequired
Rule-10.2Expressions of essentially character type shall not be used inappropriately in addition and subtraction operationsRequiredRequired
Rule-10.3The value of an expression shall not be assigned to an object with a narrower essential type or of a different essential type category.RequiredRequired
Rule-10.4Both operands of an operator in which the usual arithmetic conversions are performed shall have the same essential type categoryRequiredRequired
Rule-10.5The value of an expression should not be cast to an inappropriate essential typeAdvisoryAdvisory
Rule-10.6The value of a composite expression shall not be assigned to an object with wider essential typeRequiredRequired
Rule-10.7If a composite expression is used as one operand of an operator in which the usual arithmetic conversions are performed then the other operand shall not have wider essential typeRequiredRequired
Rule-10.8The value of a composite expression shall not be cast to a different essential type category or a wider essential typeRequiredRequired
Rule-11.1Conversions shall not be performed between a pointer to a function and any other typeRequiredRequired
Rule-11.2Conversions shall not be performed between a pointer to an incomplete type and any other typeRequiredRequired
Rule-11.3A cast shall not be performed between a pointer to object type and a pointer to a different object typeRequiredRequired
Rule-11.4A conversion should not be performed between a pointer to object and an integer typeAdvisoryAdvisory
Rule-11.5A conversion should not be performed from pointer to void into pointer to objectAdvisoryAdvisory
Rule-11.6A cast shall not be performed between pointer to void and an arithmetic typeRequiredRequired
Rule-11.7A cast shall not be performed between pointer to object and a non-integer arithmetic typeRequiredRequired
Rule-11.8A cast shall not remove any const or volatile qualification from the type pointed to by a pointerRequiredRequired
Rule-11.9The macro NULL shall be the only permitted form of integer null pointer constantRequiredDisapplied
Rule-12.1The precedence of operators within expressions should be made explicitAdvisoryAdvisory
Rule-12.2The right hand operand of a shift operator shall lie in the range zero to one less than the width in bits of the essential type of the left hand operandRequiredRequired
Rule-12.3The comma operator should not be usedAdvisoryAdvisory
Rule-12.4Evaluation of constant expressions should not lead to unsigned integer wrap-aroundAdvisoryAdvisory
Rule-12.5The sizeof operator shall not have an operand which is a function parameter declared as 'array of type'MandatoryMandatory
Rule-13.1Initializer lists shall not contain persistent side-effectsRequiredRequired
Rule-13.2The value of an expression and its persistent side-effects shall be the same under all permitted evaluation ordersRequiredRequired
Rule-13.3A full expression containing an increment (++) or decrement (--) operator should have no other potential side effects other than that caused by the increment or decrement operatorAdvisoryDisapplied
Rule-13.4The result of an assignment operator should not be usedAdvisoryAdvisory
Rule-13.5The right hand operand of a logical && or || operator shall not contain persistent side effectsRequiredRequired
Rule-13.6The operand of the sizeof operator shall not contain any expression which has potential side-effectsMandatoryMandatory
Rule-14.1A loop counter shall not have essentially floating typeRequiredRequired
Rule-14.2A for loop shall be well-formedRequiredRequired
Rule-14.3Controlling expressions shall not be invariantRequiredRequired
Rule-14.4The controlling expression of an if-statement and the controlling expression of an iteration-statement shall have essentially Boolean typeRequiredRequired
Rule-15.1The goto statement should not be usedAdvisoryAdvisory
Rule-15.2The goto statement shall jump to a label declared later in the same functionRequiredRequired
Rule-15.3Any label referenced by a goto statement shall be declared in the same block, or in any block enclosing the goto statementRequiredRequired
Rule-15.4There should be no more than one break or goto statement used to terminate any iteration statementAdvisoryAdvisory
Rule-15.5A function should have a single point of exit at the endAdvisoryDisapplied
Rule-15.6The body of an iteration-statement or a selection-statement shall be a compound-statementRequiredRequired
Rule-15.7All if ... else if constructs shall be terminated with an else statementRequiredRequired
Rule-16.1All switch statements shall be well-formedRequiredRequired
Rule-16.2A switch label shall only be used when the most closely-enclosing compound statement is the body of a switch statementRequiredRequired
Rule-16.3An unconditional break statement shall terminate every switch-clauseRequiredRequired
Rule-16.4Every switch statement shall have a default labelRequiredRequired
Rule-16.5A default label shall appear as either the first or the last switch label of a switch statementRequiredRequired
Rule-16.6Every switch statement shall have at least two switch-clausesRequiredRequired
Rule-16.7A switch-expression shall not have essentially Boolean typeRequiredRequired
Rule-17.1The features of shall not be usedRequiredRequired
Rule-17.2Functions shall not call themselves, either directly or indirectlyRequiredRequired
Rule-17.3A function shall not be declared implicitlyMandatoryMandatory
Rule-17.4All exit paths from a function with non-void return type shall have an explicit return statement with an expressionMandatoryMandatory
Rule-17.5The function argument corresponding to a parameter declared to have an array type shall have an appropriate number of elementsAdvisoryAdvisory
Rule-17.6The declaration of an array parameter shall not contain the static keyword between the [ ]MandatoryMandatory
Rule-17.7The value returned by a function having non-void return type shall be usedRequiredDisapplied
Rule-17.8A function parameter should not be modifiedAdvisoryAdvisory
Rule-18.1A pointer resulting from arithmetic on a pointer operand shall address an element of the same array as that pointer operandRequiredRequired
Rule-18.2Subtraction between pointers shall only be applied to pointers that address elements of the same arrayRequiredRequired
Rule-18.3The relational operators >, >=, < and <= shall not be applied to objects of pointer type except where they point into the same objectRequiredRequired
Rule-18.4The +, -, += and -= operators should not be applied to an expression of pointer typeAdvisoryAdvisory
Rule-18.5Declarations should contain no more than two levels of pointer nestingAdvisoryAdvisory
Rule-18.6The address of an object with automatic storage shall not be copied to another object that persists after the first object has ceased to existRequiredRequired
Rule-18.7Flexible array members shall not be declaredRequiredRequired
Rule-18.8Variable-length array types shall not be usedRequiredRequired
Rule-19.1An object shall not be assigned or copied to an overlapping objectMandatoryMandatory
Rule-19.2The union keyword should not be usedAdvisoryAdvisory
Rule-2.1A project shall not contain unreachable codeRequiredRequired
Rule-2.2There shall be no dead codeRequiredRequired
Rule-2.3A project should not contain unused type declarationsAdvisoryDisapplied
Rule-2.4A project should not contain unused tag declarationsAdvisoryAdvisory
Rule-2.5A project should not contain unused macro declarationsAdvisoryDisapplied
Rule-2.6A function should not contain unused label declarationsAdvisoryAdvisory
Rule-2.7There should be no unused parameters in functionsAdvisoryAdvisory
Rule-20.1#include directives should only be preceded by preprocessor directives or commentsAdvisoryAdvisory
Rule-20.10The # and ## preprocessor operators should not be usedAdvisoryAdvisory
Rule-20.11A macro parameter immediately following a # operator shall not immediately be followed by a ## operatorRequiredRequired
Rule-20.12A macro parameter used as an operand to the # or ## operators, which is itself subject to further macro replacement, shall only be used as an operand to these operatorsRequiredRequired
Rule-20.13A line whose first token is # shall be a valid preprocessing directiveRequiredRequired
Rule-20.14All #else, #elif and #endif preprocessor directives shall reside in the same file as the #if, #ifdef or #ifndef directive to which they are relatedRequiredRequired
Rule-20.2The ', " or \ characters and the /* or // character sequences shall not occur in a header file nameRequiredRequired
Rule-20.3The #include directive shall be followed by either a or "filename" sequenceRequiredRequired
Rule-20.4A macro shall not be defined with the same name as a keywordRequiredRequired
Rule-20.5#undef should not be usedAdvisoryAdvisory
Rule-20.6Tokens that look like a preprocessing directive shall not occur within a macro argumentRequiredRequired
Rule-20.7Expressions resulting from the expansion of macro parameters shall be enclosed in parenthesesRequiredRequired
Rule-20.8The controlling expression of a #if or #elif preprocessing directive shall evaluate to 0 or 1RequiredRequired
Rule-20.9All identifiers used in the controlling expression of #if or #elif preprocessing directives shall be #define'd before evaluationRequiredRequired
Rule-21.1#define and #undef shall not be used on a reserved identifier or reserved macro nameRequiredRequired
Rule-21.10The Standard Library time and date functions shall not be usedRequiredRequired
Rule-21.11The standard header file shall not be usedRequiredRequired
Rule-21.12The exception handling features of should not be usedAdvisoryAdvisory
Rule-21.13Any value passed to a function in shall be representable as an unsigned char or be the value EOFMandatoryMandatory
Rule-21.14The Standard Library function memcmp shall not be used to compare null terminated stringsRequiredRequired
Rule-21.15The pointer arguments to the Standard Library functions memcpy, memmove and memcmp shall be pointers to qualified or unqualified versions of compatible typesRequiredRequired
Rule-21.16The pointer arguments to the Standard Library function memcpy shall point to either a pointer type, an essentially signed type, an essentially unsigned type, an essentially Boolean type or an essentially enum typeRequiredRequired
Rule-21.17Use of the string handling functions from shall not result in accesses beyond the bounds of the objects referenced by their pointer parametersMandatoryMandatory
Rule-21.18The size_t argument passed to any function in shall have an appropriate valueMandatoryMandatory
Rule-21.19The pointers returned by the Standard Library functions lovaleconv, getenv, setlocale or strerror shall only be used as if they have pointer to const-qualified typeMandatoryMandatory
Rule-21.2A reserved identifier or macro name shall not be declaredRequiredRequired
Rule-21.20The pointer returned by the Standard Library functions asctime, ctime, gmtime, localtime, localeconv, getenv, setlocale, or strerror shall not be used following a subsequent call to the same functionMandatoryMandatory
Rule-21.3The memory allocation and deallocation functions of shall not be usedRequiredRequired
Rule-21.4The standard header file shall not be usedRequiredRequired
Rule-21.5The standard header file shall not be usedRequiredRequired
Rule-21.6The Standard Library input/output functions shall not be usedRequiredRequired
Rule-21.7The atof, atoi, atol and atoll functions of shall not be usedRequiredRequired
Rule-21.8The library functions abort, exit and system of shall not be usedRequiredRequired
Rule-21.9The library functions bsearch and qsort of shall not be usedRequiredRequired
Rule-22.1All resources obtained dynamically by means of Standard Library functions shall be explicitly releasedRequiredRequired
Rule-22.10The value of errno shall only be tested when the last function to be called was an errno-setting-functionRequiredRequired
Rule-22.2A block of memory shall only be freed if it was allocated by means of a Standard Library functionMandatoryMandatory
Rule-22.3The same file shall not be open for read and write access at the same time on different streamsRequiredRequired
Rule-22.4There shall be no attempt to write to a stream which has been opened as read-onlyMandatoryMandatory
Rule-22.5A pointer to a FILE object shall not be dereferencedMandatoryMandatory
Rule-22.6The value of a pointer to a FILE shall not be used after the associated stream has been closedMandatoryMandatory
Rule-22.7The macro EOF shall on ly be compared with the unmodified return value from any Standard Library function capable of returning EOFRequiredRequired
Rule-22.8The value of errno shall be set to zero prior to a call to an errno-setting-functionRequiredRequired
Rule-22.9The value of errno shall be tested against zero after calling an errno-setting-functionRequiredRequired
Rule-3.1The character sequences /* and // shall not be used within a comment.RequiredRequired
Rule-3.2Line-splicing shall not be used in // comments.RequiredRequired
Rule-4.1Octal and hexadecimal escape sequences shall be terminatedRequiredRequired
Rule-4.2Trigraphs should not be usedAdvisoryAdvisory
Rule-5.1External identifiers shall be distinctRequiredRequired
Rule-5.2Identifiers declared in the same scope and name space shall be distinctRequiredRequired
Rule-5.3An identifier declared in an inner scope shall not hide an identifier declared in an outer scopeRequiredRequired
Rule-5.4Macro identifiers shall be distinctRequiredRequired
Rule-5.5Identifiers shall be distinct from macro namesRequiredRequired
Rule-5.6A typedef name shall be a unique identifierRequiredRequired
Rule-5.7A tag name shall be a unique identifierRequiredRequired
Rule-5.8Identifiers that define objects or functions with external linkage shall be uniqueRequiredRequired
Rule-5.9Identifiers that define objects or functions with internal linkage should be uniqueAdvisoryAdvisory
Rule-6.1Bit-fields shall only be declared with an appropriate typeRequiredRequired
Rule-6.2Single-bit named bit fields shall not be of a signed typeRequiredRequired
Rule-7.1Octal constants shall not be usedRequiredRequired
Rule-7.2A "u" or "U" suffix shall be applied to all integer constants that are represented in an unsigned typeRequiredRequired
Rule-7.3The lowercase character "l" shall not be used in a literal suffixRequiredRequired
Rule-7.4A string literal shall not be assigned to an object unless the object's type is "pointer to const-qualified char"RequiredRequired
Rule-8.1Types shall be explicitly specifiedRequiredRequired
Rule-8.10An inline function shall be declared with the static storage classRequiredRequired
Rule-8.11When an array with external linkage is declared, its size should be explicitly specifiedAdvisoryAdvisory
Rule-8.12Within an enumerator list, the value of an implicitly-specified enumeration constant shall be uniqueRequiredRequired
Rule-8.13A pointer should point to a const-qualified type whenever possibleAdvisoryAdvisory
Rule-8.14The restrict type qualifier shall not be usedRequiredRequired
Rule-8.2Function types shall be in prototype form with named parametersRequiredRequired
Rule-8.3All declarations of an object or function shall use the same names and type qualifiersRequiredRequired
Rule-8.4A compatible declaration shall be visible when an object or function with external linkage is definedRequiredRequired
Rule-8.5An external object or function shall be declared once in one and only one fileRequiredRequired
Rule-8.6An identifier with external linkage shall have exactly one external definitionRequiredRequired
Rule-8.7Functions and objects should not be defined with external linkage if they are referenced in only one translation unitAdvisoryDisapplied
Rule-8.8The static storage class specifier shall be used in all declarations of objects and functions that have internal linkageRequiredRequired
Rule-8.9An object should be defined at block scope if its identifier only appears in a single functionAdvisoryAdvisory
Rule-9.1The value of an object with automatic storage duration shall not be read before it has been setMandatoryMandatory
Rule-9.2The initializer for an aggregate or union shall be enclosed in bracesRequiredRequired
Rule-9.3Arrays shall not be partially initializedRequiredRequired
Rule-9.4An element of an object shall not be initialized more than onceRequiredRequired
Rule-9.5Where designated initializers are used to initialize an array object the size of the array shall be specified explicitlyRequiredRequired
-
-
- -This section targets to provide an overview of Guidelines Compliance Summary. -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GuidelineCategoryDescriptionCompliance
Dir-1.1RequiredAny implementation-defined behaviour on which the output of the program depends shall be documented and understoodCompliant

with deviations:
-
- - - - - - - - - - - - - -
QacDescription
0292 [I] Source file '%s' has comments containing one of the characters '$', '@' or '`'.
0315 [I] Implicit conversion from a pointer to object type to a pointer to void.
0380 [L] Number of macro definitions exceeds 4095 - program does not conform strictly to ISO:C99.
-
Dir-2.1RequiredAll source files shall compile without any compilation errorsDisapplied
Dir-3.1RequiredAll code shall be traceable to documented requirementsDisapplied
Dir-4.1RequiredRun-time failures shall be minimizedCompliant
Dir-4.10RequiredPrecautions shall be taken in order to prevent the contents of a header file being included more then onceCompliant
Dir-4.11RequiredThe validity of values passed to library functions shall be checkedDisapplied
Dir-4.12RequiredDynamic memory allocation shall not be usedDisapplied
Dir-4.13AdvisoryFunctions which are designed to provide operations on a resource should be called in an appropriate sequenceDisapplied
Dir-4.14RequiredThe validity of values received from external sources shall be checkedCompliant
Dir-4.2AdvisoryAll usage of assembly language should be documentedCompliant
Dir-4.3RequiredAssembly language shall be encapsulated and isolatedCompliant
Dir-4.4AdvisorySections of code should not be "commented out"Disapplied
Dir-4.5AdvisoryIdentifiers in the same name space with overlapping visibility should be typographically unambiguousDisapplied
Dir-4.6Advisorytypedefs that indicate size and signedness should be used in place of the basic numerical typesCompliant
Dir-4.7RequiredIf a function returns error information, then that error information shall be testedDisapplied
Dir-4.8AdvisoryIf a pointer to a structure or union is never dereferenced within a translation unit, then the implementation of the object should be hiddenDisapplied
Dir-4.9AdvisoryA function should be used in preference to a function-like macro where they are interchangeableDisapplied
Rule-1.1RequiredThe program shall contain no violations of the standard C syntax and constraints, and shall not exceed the implementation's translation limitsCompliant
Rule-1.2AdvisoryLanguage extensions should not be usedCompliant
Rule-1.3RequiredThere shall be no occurrence of undefined or critical unspecified behaviourCompliant
Rule-10.1RequiredOperands shall not be of an inappropriate essential type.Compliant
Rule-10.2RequiredExpressions of essentially character type shall not be used inappropriately in addition and subtraction operationsCompliant
Rule-10.3RequiredThe value of an expression shall not be assigned to an object with a narrower essential type or of a different essential type category.Compliant
Rule-10.4RequiredBoth operands of an operator in which the usual arithmetic conversions are performed shall have the same essential type categoryCompliant
Rule-10.5AdvisoryThe value of an expression should not be cast to an inappropriate essential typeCompliant
Rule-10.6RequiredThe value of a composite expression shall not be assigned to an object with wider essential typeCompliant
Rule-10.7RequiredIf a composite expression is used as one operand of an operator in which the usual arithmetic conversions are performed then the other operand shall not have wider essential typeCompliant
Rule-10.8RequiredThe value of a composite expression shall not be cast to a different essential type category or a wider essential typeCompliant
Rule-11.1RequiredConversions shall not be performed between a pointer to a function and any other typeCompliant
Rule-11.2RequiredConversions shall not be performed between a pointer to an incomplete type and any other typeCompliant
Rule-11.3RequiredA cast shall not be performed between a pointer to object type and a pointer to a different object typeCompliant
Rule-11.4AdvisoryA conversion should not be performed between a pointer to object and an integer typeCompliant

with deviations:
-
- - - - - -
QacDescription
0306 [I] Cast between a pointer to object and an integral type.
-
Rule-11.5AdvisoryA conversion should not be performed from pointer to void into pointer to objectCompliant
Rule-11.6RequiredA cast shall not be performed between pointer to void and an arithmetic typeCompliant
Rule-11.7RequiredA cast shall not be performed between pointer to object and a non-integer arithmetic typeCompliant
Rule-11.8RequiredA cast shall not remove any const or volatile qualification from the type pointed to by a pointerCompliant
Rule-11.9RequiredThe macro NULL shall be the only permitted form of integer null pointer constantDisapplied
Rule-12.1AdvisoryThe precedence of operators within expressions should be made explicitCompliant
Rule-12.2RequiredThe right hand operand of a shift operator shall lie in the range zero to one less than the width in bits of the essential type of the left hand operandCompliant
Rule-12.3AdvisoryThe comma operator should not be usedCompliant
Rule-12.4AdvisoryEvaluation of constant expressions should not lead to unsigned integer wrap-aroundCompliant
Rule-12.5MandatoryThe sizeof operator shall not have an operand which is a function parameter declared as 'array of type'Compliant
Rule-13.1RequiredInitializer lists shall not contain persistent side-effectsCompliant
Rule-13.2RequiredThe value of an expression and its persistent side-effects shall be the same under all permitted evaluation ordersCompliant
Rule-13.3AdvisoryA full expression containing an increment (++) or decrement (--) operator should have no other potential side effects other than that caused by the increment or decrement operatorDisapplied
Rule-13.4AdvisoryThe result of an assignment operator should not be usedCompliant
Rule-13.5RequiredThe right hand operand of a logical && or || operator shall not contain persistent side effectsCompliant
Rule-13.6MandatoryThe operand of the sizeof operator shall not contain any expression which has potential side-effectsCompliant
Rule-14.1RequiredA loop counter shall not have essentially floating typeCompliant
Rule-14.2RequiredA for loop shall be well-formedCompliant
Rule-14.3RequiredControlling expressions shall not be invariantCompliant

with deviations:
-
- - - - - - - - - - - - - - - - - - - - - -
QacDescription
2991 The value of this 'if' controlling expression is always 'true'.
2992 The value of this 'if' controlling expression is always 'false'.
2998 The first operand of this conditional operator is always 'false'.
3493 The first operand of this conditional operator is always constant 'true'.
3494 The first operand of this conditional operator is always constant 'false'.
-
Rule-14.4RequiredThe controlling expression of an if-statement and the controlling expression of an iteration-statement shall have essentially Boolean typeCompliant
Rule-15.1AdvisoryThe goto statement should not be usedCompliant
Rule-15.2RequiredThe goto statement shall jump to a label declared later in the same functionCompliant
Rule-15.3RequiredAny label referenced by a goto statement shall be declared in the same block, or in any block enclosing the goto statementCompliant
Rule-15.4AdvisoryThere should be no more than one break or goto statement used to terminate any iteration statementCompliant
Rule-15.5AdvisoryA function should have a single point of exit at the endDisapplied
Rule-15.6RequiredThe body of an iteration-statement or a selection-statement shall be a compound-statementCompliant
Rule-15.7RequiredAll if ... else if constructs shall be terminated with an else statementCompliant
Rule-16.1RequiredAll switch statements shall be well-formedCompliant
Rule-16.2RequiredA switch label shall only be used when the most closely-enclosing compound statement is the body of a switch statementCompliant
Rule-16.3RequiredAn unconditional break statement shall terminate every switch-clauseCompliant
Rule-16.4RequiredEvery switch statement shall have a default labelCompliant
Rule-16.5RequiredA default label shall appear as either the first or the last switch label of a switch statementCompliant
Rule-16.6RequiredEvery switch statement shall have at least two switch-clausesCompliant
Rule-16.7RequiredA switch-expression shall not have essentially Boolean typeCompliant
Rule-17.1RequiredThe features of shall not be usedCompliant
Rule-17.2RequiredFunctions shall not call themselves, either directly or indirectlyCompliant
Rule-17.3MandatoryA function shall not be declared implicitlyCompliant
Rule-17.4MandatoryAll exit paths from a function with non-void return type shall have an explicit return statement with an expressionCompliant
Rule-17.5AdvisoryThe function argument corresponding to a parameter declared to have an array type shall have an appropriate number of elementsCompliant
Rule-17.6MandatoryThe declaration of an array parameter shall not contain the static keyword between the [ ]Compliant
Rule-17.7RequiredThe value returned by a function having non-void return type shall be usedDisapplied
Rule-17.8AdvisoryA function parameter should not be modifiedCompliant
Rule-18.1RequiredA pointer resulting from arithmetic on a pointer operand shall address an element of the same array as that pointer operandCompliant
Rule-18.2RequiredSubtraction between pointers shall only be applied to pointers that address elements of the same arrayCompliant
Rule-18.3RequiredThe relational operators >, >=, < and <= shall not be applied to objects of pointer type except where they point into the same objectCompliant
Rule-18.4AdvisoryThe +, -, += and -= operators should not be applied to an expression of pointer typeCompliant
Rule-18.5AdvisoryDeclarations should contain no more than two levels of pointer nestingCompliant
Rule-18.6RequiredThe address of an object with automatic storage shall not be copied to another object that persists after the first object has ceased to existCompliant
Rule-18.7RequiredFlexible array members shall not be declaredCompliant
Rule-18.8RequiredVariable-length array types shall not be usedCompliant
Rule-19.1MandatoryAn object shall not be assigned or copied to an overlapping objectCompliant
Rule-19.2AdvisoryThe union keyword should not be usedCompliant
Rule-2.1RequiredA project shall not contain unreachable codeCompliant

with deviations:
-
- - - - - -
QacDescription
1503 The function '%1s' is defined but is not used within this project.
-
Rule-2.2RequiredThere shall be no dead codeCompliant

with deviations:
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
2982 This assignment is redundant. The value of this object is never used before being modified.
2983 This assignment is redundant. The value of this object is never subsequently used.
2985 This operation is redundant. The value of the result is always that of the left-hand operand.
2986 This operation is redundant. The value of the result is always that of the right-hand operand.
2995 The result of this logical operation is always 'true'.
2996 The result of this logical operation is always 'false'.
3112 This statement has no side-effect - it can be removed.
-
Rule-2.3AdvisoryA project should not contain unused type declarationsDisapplied
Rule-2.4AdvisoryA project should not contain unused tag declarationsCompliant
Rule-2.5AdvisoryA project should not contain unused macro declarationsDisapplied
Rule-2.6AdvisoryA function should not contain unused label declarationsCompliant
Rule-2.7AdvisoryThere should be no unused parameters in functionsCompliant
Rule-20.1Advisory#include directives should only be preceded by preprocessor directives or commentsCompliant
Rule-20.10AdvisoryThe # and ## preprocessor operators should not be usedCompliant
Rule-20.11RequiredA macro parameter immediately following a # operator shall not immediately be followed by a ## operatorCompliant
Rule-20.12RequiredA macro parameter used as an operand to the # or ## operators, which is itself subject to further macro replacement, shall only be used as an operand to these operatorsCompliant
Rule-20.13RequiredA line whose first token is # shall be a valid preprocessing directiveCompliant
Rule-20.14RequiredAll #else, #elif and #endif preprocessor directives shall reside in the same file as the #if, #ifdef or #ifndef directive to which they are relatedCompliant
Rule-20.2RequiredThe ', " or \ characters and the /* or // character sequences shall not occur in a header file nameCompliant
Rule-20.3RequiredThe #include directive shall be followed by either a or "filename" sequenceCompliant
Rule-20.4RequiredA macro shall not be defined with the same name as a keywordCompliant
Rule-20.5Advisory#undef should not be usedCompliant
Rule-20.6RequiredTokens that look like a preprocessing directive shall not occur within a macro argumentCompliant
Rule-20.7RequiredExpressions resulting from the expansion of macro parameters shall be enclosed in parenthesesCompliant
Rule-20.8RequiredThe controlling expression of a #if or #elif preprocessing directive shall evaluate to 0 or 1Compliant
Rule-20.9RequiredAll identifiers used in the controlling expression of #if or #elif preprocessing directives shall be #define'd before evaluationCompliant
Rule-21.1Required#define and #undef shall not be used on a reserved identifier or reserved macro nameCompliant
Rule-21.10RequiredThe Standard Library time and date functions shall not be usedCompliant
Rule-21.11RequiredThe standard header file shall not be usedCompliant
Rule-21.12AdvisoryThe exception handling features of should not be usedCompliant
Rule-21.13MandatoryAny value passed to a function in shall be representable as an unsigned char or be the value EOFCompliant
Rule-21.14RequiredThe Standard Library function memcmp shall not be used to compare null terminated stringsCompliant
Rule-21.15RequiredThe pointer arguments to the Standard Library functions memcpy, memmove and memcmp shall be pointers to qualified or unqualified versions of compatible typesCompliant
Rule-21.16RequiredThe pointer arguments to the Standard Library function memcpy shall point to either a pointer type, an essentially signed type, an essentially unsigned type, an essentially Boolean type or an essentially enum typeCompliant
Rule-21.17MandatoryUse of the string handling functions from shall not result in accesses beyond the bounds of the objects referenced by their pointer parametersCompliant
Rule-21.18MandatoryThe size_t argument passed to any function in shall have an appropriate valueCompliant
Rule-21.19MandatoryThe pointers returned by the Standard Library functions lovaleconv, getenv, setlocale or strerror shall only be used as if they have pointer to const-qualified typeCompliant
Rule-21.2RequiredA reserved identifier or macro name shall not be declaredCompliant
Rule-21.20MandatoryThe pointer returned by the Standard Library functions asctime, ctime, gmtime, localtime, localeconv, getenv, setlocale, or strerror shall not be used following a subsequent call to the same functionCompliant
Rule-21.3RequiredThe memory allocation and deallocation functions of shall not be usedCompliant
Rule-21.4RequiredThe standard header file shall not be usedCompliant
Rule-21.5RequiredThe standard header file shall not be usedCompliant
Rule-21.6RequiredThe Standard Library input/output functions shall not be usedCompliant
Rule-21.7RequiredThe atof, atoi, atol and atoll functions of shall not be usedCompliant
Rule-21.8RequiredThe library functions abort, exit and system of shall not be usedCompliant

with deviations:
-
- - - - - -
QacDescription
5128 Use of function: getenv.
-
Rule-21.9RequiredThe library functions bsearch and qsort of shall not be usedCompliant
Rule-22.1RequiredAll resources obtained dynamically by means of Standard Library functions shall be explicitly releasedCompliant
Rule-22.10RequiredThe value of errno shall only be tested when the last function to be called was an errno-setting-functionCompliant
Rule-22.2MandatoryA block of memory shall only be freed if it was allocated by means of a Standard Library functionCompliant
Rule-22.3RequiredThe same file shall not be open for read and write access at the same time on different streamsCompliant
Rule-22.4MandatoryThere shall be no attempt to write to a stream which has been opened as read-onlyCompliant
Rule-22.5MandatoryA pointer to a FILE object shall not be dereferencedCompliant
Rule-22.6MandatoryThe value of a pointer to a FILE shall not be used after the associated stream has been closedCompliant
Rule-22.7RequiredThe macro EOF shall on ly be compared with the unmodified return value from any Standard Library function capable of returning EOFCompliant
Rule-22.8RequiredThe value of errno shall be set to zero prior to a call to an errno-setting-functionCompliant
Rule-22.9RequiredThe value of errno shall be tested against zero after calling an errno-setting-functionCompliant
Rule-3.1RequiredThe character sequences /* and // shall not be used within a comment.Compliant
Rule-3.2RequiredLine-splicing shall not be used in // comments.Compliant
Rule-4.1RequiredOctal and hexadecimal escape sequences shall be terminatedCompliant
Rule-4.2AdvisoryTrigraphs should not be usedCompliant
Rule-5.1RequiredExternal identifiers shall be distinctCompliant
Rule-5.2RequiredIdentifiers declared in the same scope and name space shall be distinctCompliant
Rule-5.3RequiredAn identifier declared in an inner scope shall not hide an identifier declared in an outer scopeCompliant
Rule-5.4RequiredMacro identifiers shall be distinctCompliant
Rule-5.5RequiredIdentifiers shall be distinct from macro namesCompliant
Rule-5.6RequiredA typedef name shall be a unique identifierCompliant
Rule-5.7RequiredA tag name shall be a unique identifierCompliant
Rule-5.8RequiredIdentifiers that define objects or functions with external linkage shall be uniqueCompliant
Rule-5.9AdvisoryIdentifiers that define objects or functions with internal linkage should be uniqueCompliant
Rule-6.1RequiredBit-fields shall only be declared with an appropriate typeCompliant
Rule-6.2RequiredSingle-bit named bit fields shall not be of a signed typeCompliant
Rule-7.1RequiredOctal constants shall not be usedCompliant
Rule-7.2RequiredA "u" or "U" suffix shall be applied to all integer constants that are represented in an unsigned typeCompliant
Rule-7.3RequiredThe lowercase character "l" shall not be used in a literal suffixCompliant
Rule-7.4RequiredA string literal shall not be assigned to an object unless the object's type is "pointer to const-qualified char"Compliant
Rule-8.1RequiredTypes shall be explicitly specifiedCompliant
Rule-8.10RequiredAn inline function shall be declared with the static storage classCompliant
Rule-8.11AdvisoryWhen an array with external linkage is declared, its size should be explicitly specifiedCompliant
Rule-8.12RequiredWithin an enumerator list, the value of an implicitly-specified enumeration constant shall be uniqueCompliant
Rule-8.13AdvisoryA pointer should point to a const-qualified type whenever possibleCompliant
Rule-8.14RequiredThe restrict type qualifier shall not be usedCompliant
Rule-8.2RequiredFunction types shall be in prototype form with named parametersCompliant
Rule-8.3RequiredAll declarations of an object or function shall use the same names and type qualifiersCompliant
Rule-8.4RequiredA compatible declaration shall be visible when an object or function with external linkage is definedCompliant
Rule-8.5RequiredAn external object or function shall be declared once in one and only one fileCompliant
Rule-8.6RequiredAn identifier with external linkage shall have exactly one external definitionCompliant
Rule-8.7AdvisoryFunctions and objects should not be defined with external linkage if they are referenced in only one translation unitDisapplied
Rule-8.8RequiredThe static storage class specifier shall be used in all declarations of objects and functions that have internal linkageCompliant
Rule-8.9AdvisoryAn object should be defined at block scope if its identifier only appears in a single functionCompliant
Rule-9.1MandatoryThe value of an object with automatic storage duration shall not be read before it has been setCompliant
Rule-9.2RequiredThe initializer for an aggregate or union shall be enclosed in bracesCompliant
Rule-9.3RequiredArrays shall not be partially initializedCompliant
Rule-9.4RequiredAn element of an object shall not be initialized more than onceCompliant
Rule-9.5RequiredWhere designated initializers are used to initialize an array object the size of the array shall be specified explicitlyCompliant
-
-
- -This section targets to provide an overview of Deviation Permits.
-All the rules corresponding to the deviation permits are disabled inside PRQA and will not cause any violation or deviation in the Deviation records section below. -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GuidelineCategoryDescriptionRatioSub RulesCharacteristicsReason
Dir-1.1RequiredAny implementation-defined behaviour on which the output of the program depends shall be documented and understood3/34
- - - - - - - - - - - - - -
QacDescription
0292 [I] Source file '%s' has comments containing one of the characters '$', '@' or '`'.
0315 [I] Implicit conversion from a pointer to object type to a pointer to void.
0380 [L] Number of macro definitions exceeds 4095 - program does not conform strictly to ISO:C99.
-
Maintainability / Analysability0292: Invalid characters in comments: Doxygen comments are used.
-0315: Library string.h functions (memcpy, etc.) are used and trigger this implicit conversion.
-0380: Already CMSIS and STM32HAL trigger this.
-
Dir-4.9AdvisoryA function should be used in preference to a function-like macro where they are interchangeable1/1
- - - - - -
QacDescription
3453 A function could probably be used instead of this function-like macro.
-
Performance / Resource utilizationSuppressed due to code optimization and efficiency.
Rule-11.4AdvisoryA conversion should not be performed between a pointer to object and an integer type1/5
- - - - - -
QacDescription
0306 [I] Cast between a pointer to object and an integral type.
-
Maintainability / ModifiabilityUsing STM32 HAL already creates many violations. Also needed to do pointer arithmetic, calculating offsets inside a buffer.
Rule-11.9RequiredThe macro NULL shall be the only permitted form of integer null pointer constant1/2
- - - - - -
QacDescription
3004 This integral constant expression is being interpreted as a NULL pointer constant.
-
Keil stddef.h: "define NULL 0" causes violations. PRQA acknowledged this as a false positive.
Rule-13.3AdvisoryA full expression containing an increment (++) or decrement (--) operator should have no other potential side effects other than that caused by the increment or decrement operator1/1
- - - - - -
QacDescription
3440 Using the value resulting from a ++ or -- operation.
-
Maintainability / AnalysabilityRFAL uses the increment often for building buffers (array[i++] = 42; ...). Splitting this would decrease readability.
Rule-14.3RequiredControlling expressions shall not be invariant6/11
- - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
3440 Using the value resulting from a ++ or -- operation.
2991 The value of this 'if' controlling expression is always 'true'.
2992 The value of this 'if' controlling expression is always 'false'.
2998 The first operand of this conditional operator is always 'false'.
3493 The first operand of this conditional operator is always constant 'true'.
3494 The first operand of this conditional operator is always constant 'false'.
-
Portability / AdaptabilityRFAL is configurable through compile time switches. This causes some ifs to have invariant conditions at the used configuration. Suppress 14.3 for if statements.
Rule-15.5AdvisoryA function should have a single point of exit at the end1/1
- - - - - -
QacDescription
2889 This function has more than one 'return' path.
-
Maintainability / AnalysabilitySuppressed due to readability and simplicity of code logic.
Rule-17.7RequiredThe value returned by a function having non-void return type shall be used1/1
- - - - - -
QacDescription
3200 '%s' returns a value which is not being used.
-
Maintainability / AnalysabilityTreating the return codes of functions in all places without exception handling would makes the code hard to read and maintain. Error checking has been reduced to the places where needed.
Rule-2.1RequiredA project shall not contain unreachable code1/7
- - - - - -
QacDescription
1503 The function '%1s' is defined but is not used within this project.
-
Maintainability / ModularityRFAL provides many functions - some are not used within the checked project.
Rule-2.2RequiredThere shall be no dead code7/18
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
QacDescription
2982 This assignment is redundant. The value of this object is never used before being modified.
2983 This assignment is redundant. The value of this object is never subsequently used.
2985 This operation is redundant. The value of the result is always that of the left-hand operand.
2986 This operation is redundant. The value of the result is always that of the right-hand operand.
2996 The result of this logical operation is always 'false'.
2997 The first operand of this conditional operator is always 'true'.
3112 This statement has no side-effect - it can be removed.
-
Usability / User error protectionAll the violations were checked and fixing the violation would deteriorate robustness: Removing checks which are unnecessary at the given position, removing trailing iterator increment, etc.
Rule-2.3AdvisoryA project should not contain unused type declarations1/1
- - - - - -
QacDescription
3205 The identifier '%s' is not used and could be removed.
-
Compatibility / InteroperabilityRFAL defines enums for all identifiers available in NFC Forum - some are unused.
Rule-2.5AdvisoryA project should not contain unused macro declarations1/1
- - - - - -
QacDescription
3214 The macro '%s' is not used and could be removed.
-
Compatibility / InteroperabilityRFAL defines macros for all identifiers of NFC Forum and RF chip register map - some are not used.
Rule-8.7AdvisoryFunctions and objects should not be defined with external linkage if they are referenced in only one translation unit4/4
- - - - - - - - - - - - - - - - - -
QacDescription
1504 The object '%1s' is only referenced in the translation unit where it is defined.
1505 The function '%1s' is only referenced in the translation unit where it is defined.
1531 The object '%1s' is referenced in only one translation unit - but not the one in which it is defined.
1532 The function '%1s' is only referenced in one translation unit - but not the one in which it is defined.
-
Maintainability / ModularityRFAL defines functions which could be called by the user but are not called in the current project.
-
-
- -This section targets to provide an overview of Deviation Records. -
-
-
- -

File: .../ST25R3916_nucleo/rfal/source/rfal_isoDep.c

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
LinesCountSuppressed QacsComment
2266-22671
- - - - -
0310 Casting to different object pointer type.
-
MISRA 11.3 - Intentional safe cast to avoiding buffer duplication
421-4211
- - - - -
0750 A union type specifier has been defined.
-
MISRA 19.2 - Members of the union will not be used concurrently, only one frame at a time
797-7971
- - - - -
2003 The preceding 'switch' clause is not empty and does not end with a 'jump' statement. Execution will fall through.
-
MISRA 16.3 - Intentional fall through
2519-25191
- - - - -
4342 An expression of 'essentially unsigned' type (%1s) is being cast to enum type '%2s'.
-
MISRA 10.5 - Layout of enum rfalBitRate and above clamping of maxTxBR guarantee no invalid enum values to be created
2693-26931
- - - - -
0310 Casting to different object pointer type.
-
MISRA 11.3 - Intentional safe cast to avoiding large buffer duplication
1351-13511
- - - - -
2003 The preceding 'switch' clause is not empty and does not end with a 'jump' statement. Execution will fall through.
-
MISRA 16.3 - Intentional fall through
1028-10281
- - - - -
2003 The preceding 'switch' clause is not empty and does not end with a 'jump' statement. Execution will fall through.
-
MISRA 16.3 - Intentional fall through
2756-27561
- - - - -
2003 The preceding 'switch' clause is not empty and does not end with a 'jump' statement. Execution will fall through.
-
MISRA 16.3 - Intentional fall through
2615-26151
- - - - -
4342 An expression of 'essentially unsigned' type (%1s) is being cast to enum type '%2s'.
-
MISRA 10.5 - Layout of enum rfalBitRate and range of loop variable guarantee no invalid enum values to be created
2602-26021
- - - - -
4342 An expression of 'essentially unsigned' type (%1s) is being cast to enum type '%2s'.
-
MISRA 10.5 - Layout of enum rfalBitRate and range of loop variable guarantee no invalid enum values to be created
2175-21761
- - - - -
4342 An expression of 'essentially unsigned' type (%1s) is being cast to enum type '%2s'.
-
MISRA 10.5 - Layout of enum rfalIsoDepFSxI is guaranteed whithin 4bit range
2526-25261
- - - - -
4342 An expression of 'essentially unsigned' type (%1s) is being cast to enum type '%2s'.
-
MISRA 10.5 - Layout of enum rfalBitRate and above clamping of maxTxBR guarantee no invalid enum values to be created
1391-13932
- - - - -
4342 An expression of 'essentially unsigned' type (%1s) is being cast to enum type '%2s'.
-
MISRA 10.5 - Layout of enum rfalBitRate and above masks guarantee no invalid enum values to be created
-

File: .../ST25R3916_nucleo/rfal/source/rfal_nfc.c

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
LinesCountSuppressed QacsComment
1612-16121
- - - - -
0310 Casting to different object pointer type.
-
MISRA 11.3 - Intentional safe cast to avoiding large buffer duplication
81-811
- - - - -
0750 A union type specifier has been defined.
-
MISRA 19.2 - Members of the union will not be used concurrently, only one interface at a time
190-1901
- - - - -
2880 This code is unreachable.
-
MISRA 2.1 - Unreachable code due to configuration option being set/unset
1828-18281
- - - - -
0310 Casting to different object pointer type.
-
MISRA 11.3 - Intentional safe cast to avoiding large buffer duplication
-

File: .../ST25R3916_nucleo/rfal/source/rfal_nfcDep.c

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
LinesCountSuppressed QacsComment
1901-19032
- - - - -
4342 An expression of 'essentially unsigned' type (%1s) is being cast to enum type '%2s'.
-
MISRA 10.5 - Layout of enum rfalBitRate and definition of rfalNfcDepBRS2DSI guarantee no invalid enum values to be created
2595-25951
- - - - -
0310 Casting to different object pointer type.
-
MISRA 11.3 - Intentional safe cast to avoiding large buffer duplication
1589-15891
- - - - -
2003 The preceding 'switch' clause is not empty and does not end with a 'jump' statement. Execution will fall through.
-
MISRA 16.3 - Intentional fall through
902-9021
- - - - -
2880 This code is unreachable.
-
MISRA 2.1 - Guard code to prevent unexpected behavior
1661-16611
- - - - -
2003 The preceding 'switch' clause is not empty and does not end with a 'jump' statement. Execution will fall through.
-
MISRA 16.3 - Intentional fall through
2654-26541
- - - - -
2003 The preceding 'switch' clause is not empty and does not end with a 'jump' statement. Execution will fall through.
-
MISRA 16.3 - Intentional fall through
1269-12691
- - - - -
2880 This code is unreachable.
-
MISRA 2.1 - Guard code to prevent unexpected behavior
-

File: .../ST25R3916_nucleo/rfal/source/rfal_nfca.c

-
- - - - - - - - - - - - - - - -
LinesCountSuppressed QacsComment
278-2781
- - - - -
2003 The preceding 'switch' clause is not empty and does not end with a 'jump' statement. Execution will fall through.
-
MISRA 16.3 - Intentional fall through
637-6381
- - - - -
4342 An expression of 'essentially unsigned' type (%1s) is being cast to enum type '%2s'.
-
MISRA 10.5 - Guaranteed that no invalid enum values are created: see guard_eq_RFAL_NFCA_T2T, ....
-

File: .../ST25R3916_nucleo/rfal/source/rfal_nfcb.c

-
- - - - - - - - - -
LinesCountSuppressed QacsComment
391-3921
- - - - -
4342 An expression of 'essentially unsigned' type (%1s) is being cast to enum type '%2s'.
-
MISRA 10.5 - Layout of rfalNfcbSlots and above loop guarantee that no invalid enum values are created.
-

File: .../ST25R3916_nucleo/rfal/source/st25r3916/rfal_rfst25r3916.c

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
LinesCountSuppressed QacsComment
3344-33441
- - - - -
2003 The preceding 'switch' clause is not empty and does not end with a 'jump' statement. Execution will fall through.
-
MISRA 16.3 - Intentional fall through
3108-31081
- - - - -
0759 An object of union type has been defined.
-
MISRA 19.2 - Allocating Union where members are of the same type, just different names. Thus no problem can occur.
227-2271
- - - - -
0750 A union type specifier has been defined.
-
MISRA 19.2 - Both members are of the same type, just different names. Thus no problem can occur.
2046-20461
- - - - -
2003 The preceding 'switch' clause is not empty and does not end with a 'jump' statement. Execution will fall through.
-
MISRA 16.3 - Intentional fall through
3364-33641
- - - - -
4342 An expression of 'essentially unsigned' type (%1s) is being cast to enum type '%2s'.
-
MISRA 10.5 - Guaranteed that no invalid enum values may be created. See also equalityGuard_RFAL_BR_106 ff.
2179-21791
- - - - -
2003 The preceding 'switch' clause is not empty and does not end with a 'jump' statement. Execution will fall through.
-
MISRA 16.3 - Intentional fall through
1867-18671
- - - - -
2003 The preceding 'switch' clause is not empty and does not end with a 'jump' statement. Execution will fall through.
-
MISRA 16.3 - Intentional fall through
1851-18511
- - - - -
2003 The preceding 'switch' clause is not empty and does not end with a 'jump' statement. Execution will fall through.
-
MISRA 16.3 - Intentional fall through
2447-24471
- - - - -
2003 The preceding 'switch' clause is not empty and does not end with a 'jump' statement. Execution will fall through.
-
MISRA 16.3 - Intentional fall through
1972-19721
- - - - -
2003 The preceding 'switch' clause is not empty and does not end with a 'jump' statement. Execution will fall through.
-
MISRA 16.3 - Intentional fall through
1837-18371
- - - - -
2003 The preceding 'switch' clause is not empty and does not end with a 'jump' statement. Execution will fall through.
-
MISRA 16.3 - Intentional fall through
2341-23411
- - - - -
2003 The preceding 'switch' clause is not empty and does not end with a 'jump' statement. Execution will fall through.
-
MISRA 16.3 - Intentional fall through
2254-22541
- - - - -
2003 The preceding 'switch' clause is not empty and does not end with a 'jump' statement. Execution will fall through.
-
MISRA 16.3 - Intentional fall through
3563-35631
- - - - -
4342 An expression of 'essentially unsigned' type (%1s) is being cast to enum type '%2s'.
-
MISRA 10.5 - Guaranteed that no invalid enum values may be created. See also equalityGuard_RFAL_BR_106 ff.
1494-14941
- - - - -
5209 Use of basic type '%s'.
-
MISRA 4.9 - External function (sqrt()) requires double
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileRequiredAdvisoryTotal
.../ST25R3916_nucleo/rfal/include/rfal_nfcv.h011
.../ST25R3916_nucleo/rfal/include/rfal_nfcDep.h011
.../ST25R3916_nucleo/rfal/include/rfal_isoDep.h011
.../ST25R3916_nucleo/rfal/include/rfal_nfc.h033
.../ST25R3916_nucleo/rfal/include/rfal_analogConfig.h101
.../ST25R3916_nucleo/rfal/source/rfal_nfca.c112
.../ST25R3916_nucleo/rfal/source/rfal_nfc.c314
.../ST25R3916_nucleo/rfal/source/rfal_nfcDep.c628
.../ST25R3916_nucleo/rfal/source/rfal_isoDep.c6814
.../ST25R3916_nucleo/rfal/source/st25r3916/rfal_rfst25r3916.c10515
.../ST25R3916_nucleo/rfal/source/st25r3916/rfal_analogConfigTbl.h112
.../ST25R3916_nucleo/rfal/source/rfal_nfcb.c011
Total282553
-
-
- - -There are no duplicated suppressions. - -

File: .../ST25R3916_nucleo/rfal/source/rfal_isoDep.c

-
- - - - - - - -
LineUnused QacsComment
1414
- - - - -
2880 This code is unreachable.
-
MISRA 2.1 - Unreachable code due to configuration option being set/unset above (RFAL_SUPPORT_BR_CE_A_xxx)
-
-
- -There are no continuous suppressions by file. -
-
- -Active Diagnostics refers to diagnostics that are not suppressed (note: no suppressed diagnostics have been taken into account for the calculation of information in this document). -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FilesActive DiagnosticsViolated RulesViolation CountCompliance Index
.../ST25R3916_nucleo/rfal/include/rfal_analogConfig.h000100.00
.../ST25R3916_nucleo/rfal/include/rfal_chip.h000100.00
.../ST25R3916_nucleo/rfal/include/rfal_isoDep.h000100.00
.../ST25R3916_nucleo/rfal/include/rfal_nfc.h000100.00
.../ST25R3916_nucleo/rfal/include/rfal_nfcDep.h000100.00
.../ST25R3916_nucleo/rfal/include/rfal_nfca.h000100.00
.../ST25R3916_nucleo/rfal/include/rfal_nfcb.h000100.00
.../ST25R3916_nucleo/rfal/include/rfal_nfcf.h000100.00
.../ST25R3916_nucleo/rfal/include/rfal_nfcv.h000100.00
.../ST25R3916_nucleo/rfal/include/rfal_rf.h000100.00
.../ST25R3916_nucleo/rfal/include/rfal_st25tb.h000100.00
.../ST25R3916_nucleo/rfal/include/rfal_t1t.h000100.00
.../ST25R3916_nucleo/rfal/source/rfal_analogConfig.c000100.00
.../ST25R3916_nucleo/rfal/source/rfal_crc.c000100.00
.../ST25R3916_nucleo/rfal/source/rfal_crc.h000100.00
.../ST25R3916_nucleo/rfal/source/rfal_iso15693_2.c000100.00
.../ST25R3916_nucleo/rfal/source/rfal_iso15693_2.h000100.00
.../ST25R3916_nucleo/rfal/source/rfal_isoDep.c000100.00
.../ST25R3916_nucleo/rfal/source/rfal_nfc.c000100.00
.../ST25R3916_nucleo/rfal/source/rfal_nfcDep.c000100.00
.../ST25R3916_nucleo/rfal/source/rfal_nfca.c000100.00
.../ST25R3916_nucleo/rfal/source/rfal_nfcb.c000100.00
.../ST25R3916_nucleo/rfal/source/rfal_nfcf.c000100.00
.../ST25R3916_nucleo/rfal/source/rfal_nfcv.c000100.00
.../ST25R3916_nucleo/rfal/source/rfal_st25tb.c000100.00
.../ST25R3916_nucleo/rfal/source/rfal_t1t.c000100.00
.../ST25R3916_nucleo/rfal/source/st25r3916/rfal_analogConfigTbl.h000100.00
.../ST25R3916_nucleo/rfal/source/st25r3916/rfal_features.h000100.00
.../ST25R3916_nucleo/rfal/source/st25r3916/rfal_rfst25r3916.c000100.00
.../ST25R3916_nucleo/rfal/source/st25r3916/st25R3916_irq.h000100.00
.../ST25R3916_nucleo/rfal/source/st25r3916/st25r3916.c000100.00
.../ST25R3916_nucleo/rfal/source/st25r3916/st25r3916.h000100.00
.../ST25R3916_nucleo/rfal/source/st25r3916/st25r3916_com.c000100.00
.../ST25R3916_nucleo/rfal/source/st25r3916/st25r3916_com.h000100.00
.../ST25R3916_nucleo/rfal/source/st25r3916/st25r3916_irq.c000100.00
.../ST25R3916_nucleo/rfal/source/st25r3916/st25r3916_irq.h000100.00
.../ST25R3916_nucleo/rfal/source/st25r3916/st25r3916_led.c000100.00
.../ST25R3916_nucleo/rfal/source/st25r3916/st25r3916_led.h000100.00
Total000100.00
- -

-Nota: Calculation of Compliance Index
-The Compliance Index is the percentage of groups which have no messages in them.
-For each file it is calculated as follows:
-
-( Ntotal - Nerror ) / Ntotal x 100
-
-Ntotal is the total number of enforced rules (i.e. the number of rules that have at least one message mapped to it directly).
-Nerror is the number of rules for which messages appear in that file.
-The File Compliance Index is the mean of all the individual file compliances.
- -
-
-
-
- - diff --git a/lib/ST25RFAL002/doc/_htmresc/st_logo.png b/lib/ST25RFAL002/doc/_htmresc/st_logo.png deleted file mode 100755 index 8b80057fd3a..00000000000 Binary files a/lib/ST25RFAL002/doc/_htmresc/st_logo.png and /dev/null differ diff --git a/lib/ST25RFAL002/doc/rfal.chm b/lib/ST25RFAL002/doc/rfal.chm deleted file mode 100755 index d417709895d..00000000000 Binary files a/lib/ST25RFAL002/doc/rfal.chm and /dev/null differ diff --git a/lib/ST25RFAL002/include/rfal_analogConfig.h b/lib/ST25RFAL002/include/rfal_analogConfig.h deleted file mode 100644 index 009bc84842e..00000000000 --- a/lib/ST25RFAL002/include/rfal_analogConfig.h +++ /dev/null @@ -1,435 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_AnalogConfig.h - * - * \author bkam - * - * \brief RF Chip Analog Configuration Settings - * - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-HAL - * \brief RFAL Hardware Abstraction Layer - * @{ - * - * \addtogroup AnalogConfig - * \brief RFAL Analog Config Module - * @{ - * - */ - -#ifndef RFAL_ANALOG_CONFIG_H -#define RFAL_ANALOG_CONFIG_H - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "platform.h" -#include "st_errno.h" -#include "rfal_rf.h" - -/* - ****************************************************************************** - * DEFINES - ****************************************************************************** - */ - -#define RFAL_ANALOG_CONFIG_LUT_SIZE \ - (87U) /*!< Maximum number of Configuration IDs in the Loop Up Table */ -#define RFAL_ANALOG_CONFIG_LUT_NOT_FOUND \ - (0xFFU) /*!< Index value indicating no Configuration IDs found */ - -#define RFAL_ANALOG_CONFIG_TBL_SIZE \ - (1024U) /*!< Maximum number of Register-Mask-Value in the Setting List */ - -#define RFAL_ANALOG_CONFIG_POLL_LISTEN_MODE_MASK \ - (0x8000U) /*!< Mask bit of Poll Mode in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_TECH_MASK \ - (0x7F00U) /*!< Mask bits for Technology in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_BITRATE_MASK \ - (0x00F0U) /*!< Mask bits for Bit rate in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_DIRECTION_MASK \ - (0x000FU) /*!< Mask bits for Direction in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_CHIP_SPECIFIC_MASK \ - (0x00FFU) /*!< Mask bits for Chip Specific Technology */ - -#define RFAL_ANALOG_CONFIG_POLL_LISTEN_MODE_SHIFT \ - (15U) /*!< Shift value of Poll Mode in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_TECH_SHIFT \ - (8U) /*!< Shift value for Technology in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_BITRATE_SHIFT \ - (4U) /*!< Shift value for Technology in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_DIRECTION_SHIFT \ - (0U) /*!< Shift value for Direction in Analog Configuration ID */ - -#define RFAL_ANALOG_CONFIG_POLL \ - (0x0000U) /*!< Poll Mode bit setting in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_LISTEN \ - (0x8000U) /*!< Listen Mode bit setting in Analog Configuration ID */ - -#define RFAL_ANALOG_CONFIG_TECH_CHIP \ - (0x0000U) /*!< Chip-Specific bit setting in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_TECH_NFCA \ - (0x0100U) /*!< NFC-A Technology bits setting in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_TECH_NFCB \ - (0x0200U) /*!< NFC-B Technology bits setting in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_TECH_NFCF \ - (0x0400U) /*!< NFC-F Technology bits setting in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_TECH_AP2P \ - (0x0800U) /*!< AP2P Technology bits setting in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_TECH_NFCV \ - (0x1000U) /*!< NFC-V Technology bits setting in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_TECH_RFU (0x2000U) /*!< RFU for Technology bits */ - -#define RFAL_ANALOG_CONFIG_BITRATE_COMMON \ - (0x0000U) /*!< Common settings for all bit rates in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_BITRATE_106 \ - (0x0010U) /*!< 106kbits/s settings in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_BITRATE_212 \ - (0x0020U) /*!< 212kbits/s settings in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_BITRATE_424 \ - (0x0030U) /*!< 424kbits/s settings in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_BITRATE_848 \ - (0x0040U) /*!< 848kbits/s settings in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_BITRATE_1695 \ - (0x0050U) /*!< 1695kbits/s settings in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_BITRATE_3390 \ - (0x0060U) /*!< 3390kbits/s settings in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_BITRATE_6780 \ - (0x0070U) /*!< 6780kbits/s settings in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_BITRATE_1OF4 \ - (0x00C0U) /*!< 1 out of 4 for NFC-V setting in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_BITRATE_1OF256 \ - (0x00D0U) /*!< 1 out of 256 for NFC-V setting in Analog Configuration ID */ - -#define RFAL_ANALOG_CONFIG_NO_DIRECTION \ - (0x0000U) /*!< No direction setting in Analog Conf ID (Chip Specific only) */ -#define RFAL_ANALOG_CONFIG_TX \ - (0x0001U) /*!< Transmission bit setting in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_RX \ - (0x0002U) /*!< Reception bit setting in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_ANTICOL \ - (0x0003U) /*!< Anticollision setting in Analog Configuration ID */ -#define RFAL_ANALOG_CONFIG_DPO \ - (0x0004U) /*!< DPO setting in Analog Configuration ID */ - -#define RFAL_ANALOG_CONFIG_CHIP_INIT \ - (0x0000U) /*!< Chip-Specific event: Startup;Reset;Initialize */ -#define RFAL_ANALOG_CONFIG_CHIP_DEINIT \ - (0x0001U) /*!< Chip-Specific event: Deinitialize */ -#define RFAL_ANALOG_CONFIG_CHIP_FIELD_ON \ - (0x0002U) /*!< Chip-Specific event: Field On */ -#define RFAL_ANALOG_CONFIG_CHIP_FIELD_OFF \ - (0x0003U) /*!< Chip-Specific event: Field Off */ -#define RFAL_ANALOG_CONFIG_CHIP_WAKEUP_ON \ - (0x0004U) /*!< Chip-Specific event: Wake-up On */ -#define RFAL_ANALOG_CONFIG_CHIP_WAKEUP_OFF \ - (0x0005U) /*!< Chip-Specific event: Wake-up Off */ -#define RFAL_ANALOG_CONFIG_CHIP_LISTEN_ON \ - (0x0006U) /*!< Chip-Specific event: Listen On */ -#define RFAL_ANALOG_CONFIG_CHIP_LISTEN_OFF \ - (0x0007U) /*!< Chip-Specific event: Listen Off */ -#define RFAL_ANALOG_CONFIG_CHIP_POLL_COMMON \ - (0x0008U) /*!< Chip-Specific event: Poll common */ -#define RFAL_ANALOG_CONFIG_CHIP_LISTEN_COMMON \ - (0x0009U) /*!< Chip-Specific event: Listen common */ -#define RFAL_ANALOG_CONFIG_CHIP_LOWPOWER_ON \ - (0x000AU) /*!< Chip-Specific event: Low Power On */ -#define RFAL_ANALOG_CONFIG_CHIP_LOWPOWER_OFF \ - (0x000BU) /*!< Chip-Specific event: Low Power Off */ - -#define RFAL_ANALOG_CONFIG_UPDATE_LAST \ - (0x00U) /*!< Value indicating Last configuration set during update */ -#define RFAL_ANALOG_CONFIG_UPDATE_MORE \ - (0x01U) /*!< Value indicating More configuration set coming during update */ - -/* - ****************************************************************************** - * GLOBAL MACROS - ****************************************************************************** - */ - -#define RFAL_ANALOG_CONFIG_ID_GET_POLL_LISTEN(id) \ - (RFAL_ANALOG_CONFIG_POLL_LISTEN_MODE_MASK & (id)) /*!< Check if id indicates Listen mode */ - -#define RFAL_ANALOG_CONFIG_ID_GET_TECH(id) \ - (RFAL_ANALOG_CONFIG_TECH_MASK & (id)) /*!< Get the technology of Configuration ID */ -#define RFAL_ANALOG_CONFIG_ID_IS_CHIP(id) \ - (RFAL_ANALOG_CONFIG_TECH_MASK & (id)) /*!< Check if ID indicates Chip-specific */ -#define RFAL_ANALOG_CONFIG_ID_IS_NFCA(id) \ - (RFAL_ANALOG_CONFIG_TECH_NFCA & (id)) /*!< Check if ID indicates NFC-A */ -#define RFAL_ANALOG_CONFIG_ID_IS_NFCB(id) \ - (RFAL_ANALOG_CONFIG_TECH_NFCB & (id)) /*!< Check if ID indicates NFC-B */ -#define RFAL_ANALOG_CONFIG_ID_IS_NFCF(id) \ - (RFAL_ANALOG_CONFIG_TECH_NFCF & (id)) /*!< Check if ID indicates NFC-F */ -#define RFAL_ANALOG_CONFIG_ID_IS_AP2P(id) \ - (RFAL_ANALOG_CONFIG_TECH_AP2P & (id)) /*!< Check if ID indicates AP2P */ -#define RFAL_ANALOG_CONFIG_ID_IS_NFCV(id) \ - (RFAL_ANALOG_CONFIG_TECH_NFCV & (id)) /*!< Check if ID indicates NFC-V */ - -#define RFAL_ANALOG_CONFIG_ID_GET_BITRATE(id) \ - (RFAL_ANALOG_CONFIG_BITRATE_MASK & (id)) /*!< Get Bitrate of Configuration ID */ -#define RFAL_ANALOG_CONFIG_ID_IS_COMMON(id) \ - (RFAL_ANALOG_CONFIG_BITRATE_MASK & (id)) /*!< Check if ID indicates common bitrate */ -#define RFAL_ANALOG_CONFIG_ID_IS_106(id) \ - (RFAL_ANALOG_CONFIG_BITRATE_106 & (id)) /*!< Check if ID indicates 106kbits/s */ -#define RFAL_ANALOG_CONFIG_ID_IS_212(id) \ - (RFAL_ANALOG_CONFIG_BITRATE_212 & (id)) /*!< Check if ID indicates 212kbits/s */ -#define RFAL_ANALOG_CONFIG_ID_IS_424(id) \ - (RFAL_ANALOG_CONFIG_BITRATE_424 & (id)) /*!< Check if ID indicates 424kbits/s */ -#define RFAL_ANALOG_CONFIG_ID_IS_848(id) \ - (RFAL_ANALOG_CONFIG_BITRATE_848 & (id)) /*!< Check if ID indicates 848kbits/s */ -#define RFAL_ANALOG_CONFIG_ID_IS_1695(id) \ - (RFAL_ANALOG_CONFIG_BITRATE_1695 & (id)) /*!< Check if ID indicates 1695kbits/s */ -#define RFAL_ANALOG_CONFIG_ID_IS_3390(id) \ - (RFAL_ANALOG_CONFIG_BITRATE_3390 & (id)) /*!< Check if ID indicates 3390kbits/s */ -#define RFAL_ANALOG_CONFIG_ID_IS_6780(id) \ - (RFAL_ANALOG_CONFIG_BITRATE_6780 & (id)) /*!< Check if ID indicates 6780kbits/s */ -#define RFAL_ANALOG_CONFIG_ID_IS_1OF4(id) \ - (RFAL_ANALOG_CONFIG_BITRATE_1OF4 & (id)) /*!< Check if ID indicates 1 out of 4 bitrate */ -#define RFAL_ANALOG_CONFIG_ID_IS_1OF256(id) \ - (RFAL_ANALOG_CONFIG_BITRATE_1OF256 & (id)) /*!< Check if ID indicates 1 out of 256 bitrate */ - -#define RFAL_ANALOG_CONFIG_ID_GET_DIRECTION(id) \ - (RFAL_ANALOG_CONFIG_DIRECTION_MASK & (id)) /*!< Get Direction of Configuration ID */ -#define RFAL_ANALOG_CONFIG_ID_IS_TX(id) \ - (RFAL_ANALOG_CONFIG_TX & (id)) /*!< Check if id indicates TX */ -#define RFAL_ANALOG_CONFIG_ID_IS_RX(id) \ - (RFAL_ANALOG_CONFIG_RX & (id)) /*!< Check if id indicates RX */ - -#define RFAL_ANALOG_CONFIG_CONFIG_NUM(x) \ - (sizeof(x) / sizeof((x)[0])) /*!< Get Analog Config number */ - -/*! Set Analog Config ID value by: Mode, Technology, Bitrate and Direction */ -#define RFAL_ANALOG_CONFIG_ID_SET(mode, tech, br, direction) \ - (RFAL_ANALOG_CONFIG_ID_GET_POLL_LISTEN(mode) | RFAL_ANALOG_CONFIG_ID_GET_TECH(tech) | \ - RFAL_ANALOG_CONFIG_ID_GET_BITRATE(br) | RFAL_ANALOG_CONFIG_ID_GET_DIRECTION(direction)) - -/* - ****************************************************************************** - * GLOBAL DATA TYPES - ****************************************************************************** - */ - -typedef uint8_t - rfalAnalogConfigMode; /*!< Polling or Listening Mode of Configuration */ -typedef uint8_t - rfalAnalogConfigTech; /*!< Technology of Configuration */ -typedef uint8_t - rfalAnalogConfigBitrate; /*!< Bitrate of Configuration */ -typedef uint8_t - rfalAnalogConfigDirection; /*!< Transmit/Receive direction of Configuration */ - -typedef uint8_t - rfalAnalogConfigRegAddr[2]; /*!< Register Address to ST Chip */ -typedef uint8_t - rfalAnalogConfigRegMask; /*!< Register Mask Value */ -typedef uint8_t - rfalAnalogConfigRegVal; /*!< Register Value */ - -typedef uint16_t - rfalAnalogConfigId; /*!< Analog Configuration ID */ -typedef uint16_t - rfalAnalogConfigOffset; /*!< Analog Configuration offset address in the table */ -typedef uint8_t - rfalAnalogConfigNum; /*!< Number of Analog settings for the respective Configuration ID */ - -/*! Struct that contain the Register-Mask-Value set. Make sure that the whole structure size is even and unaligned! */ -typedef struct { - rfalAnalogConfigRegAddr addr; /*!< Register Address */ - rfalAnalogConfigRegMask mask; /*!< Register Mask Value */ - rfalAnalogConfigRegVal val; /*!< Register Value */ -} rfalAnalogConfigRegAddrMaskVal; - -/*! Struct that represents the Analog Configs */ -typedef struct { - uint8_t id[sizeof(rfalAnalogConfigId)]; /*!< Configuration ID */ - rfalAnalogConfigNum num; /*!< Number of Config Sets to follow */ - rfalAnalogConfigRegAddrMaskVal regSet[]; - /*!< Register-Mask-Value sets */ /* PRQA S 1060 # MISRA 18.7 - Flexible Array Members are the only meaningful way of denoting a variable length input buffer which follows a fixed header structure. */ -} rfalAnalogConfig; - -/* -****************************************************************************** -* GLOBAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -/*! - ***************************************************************************** - * \brief Initialize the Analog Configuration - * - * Reset the Analog Configuration LUT pointer to reference to default settings. - * - ***************************************************************************** - */ -void rfalAnalogConfigInitialize(void); - -/*! - ***************************************************************************** - * \brief Indicate if the current Analog Configuration Table is complete and ready to be used. - * - * \return true if current Analog Configuration Table is complete and ready to be used. - * \return false if current Analog Configuration Table is incomplete - * - ***************************************************************************** - */ -bool rfalAnalogConfigIsReady(void); - -/*! - ***************************************************************************** - * \brief Write the whole Analog Configuration table in raw format - * - * Writes the Analog Configuration and Look Up Table with the given raw table - * - * NOTE: Function does not check the validity of the given Table contents - * - * \param[in] configTbl: location of config Table to be loaded - * \param[in] configTblSize: size of the config Table to be loaded - * - * \return ERR_NONE : if setting is updated - * \return ERR_PARAM : if configTbl is invalid - * \return ERR_NOMEM : if the given Table is bigger exceeds the max size - * \return ERR_REQUEST : if the update Configuration Id is disabled - * - ***************************************************************************** - */ -ReturnCode rfalAnalogConfigListWriteRaw(const uint8_t* configTbl, uint16_t configTblSize); - -/*! - ***************************************************************************** - * \brief Write the Analog Configuration table with new analog settings. - * - * Writes the Analog Configuration and Look Up Table with the new list of register-mask-value - * and Configuration ID respectively. - * - * NOTE: Function does not check for the validity of the Register Address. - * - * \param[in] more: 0x00 indicates it is last Configuration ID settings; - * 0x01 indicates more Configuration ID setting(s) are coming. - * \param[in] *config: reference to the configuration list of current Configuraiton ID. - * - * \return ERR_PARAM : if Configuration ID or parameter is invalid - * \return ERR_NOMEM : if LUT is full - * \return ERR_REQUEST : if the update Configuration Id is disabled - * \return ERR_NONE : if setting is updated - * - ***************************************************************************** - */ -ReturnCode rfalAnalogConfigListWrite(uint8_t more, const rfalAnalogConfig* config); - -/*! - ***************************************************************************** - * \brief Read the whole Analog Configuration table in raw format - * - * Reads the whole Analog Configuration Table in raw format - * - * \param[out] tblBuf: location to the buffer to place the Config Table - * \param[in] tblBufLen: length of the buffer to place the Config Table - * \param[out] configTblSize: Config Table size - * - * \return ERR_PARAM : if configTbl or configTblSize is invalid - * \return ERR_NOMEM : if configTblSize is not enough for the whole table - * \return ERR_NONE : if read is successful - * - ***************************************************************************** - */ -ReturnCode - rfalAnalogConfigListReadRaw(uint8_t* tblBuf, uint16_t tblBufLen, uint16_t* configTblSize); - -/*! - ***************************************************************************** - * \brief Read the Analog Configuration table. - * - * Read the Analog Configuration Table - * - * \param[in] configOffset: offset to the next Configuration ID in the List Table to be read. - * \param[out] more: 0x00 indicates it is last Configuration ID settings; - * 0x01 indicates more Configuration ID setting(s) are coming. - * \param[out] config: configuration id, number of configuration sets and register-mask-value sets - * \param[in] numConfig: the remaining configuration settings space available; - * - * \return ERR_NOMEM : if number of Configuration for respective Configuration ID is greater the the remaining configuration setting space available - * \return ERR_NONE : if read is successful - * - ***************************************************************************** - */ -ReturnCode rfalAnalogConfigListRead( - rfalAnalogConfigOffset* configOffset, - uint8_t* more, - rfalAnalogConfig* config, - rfalAnalogConfigNum numConfig); - -/*! - ***************************************************************************** - * \brief Set the Analog settings of indicated Configuration ID. - * - * Update the chip with indicated analog settings of indicated Configuration ID. - * - * \param[in] configId: configuration ID - * - * \return ERR_PARAM if Configuration ID is invalid - * \return ERR_INTERNAL if error updating setting to chip - * \return ERR_NONE if new settings is applied to chip - * - ***************************************************************************** - */ -ReturnCode rfalSetAnalogConfig(rfalAnalogConfigId configId); - -/*! - ***************************************************************************** - * \brief Generates Analog Config mode ID - * - * Converts RFAL mode and bitrate into Analog Config Mode ID. - * - * Update the chip with indicated analog settings of indicated Configuration ID. - * - * \param[in] md: RFAL mode format - * \param[in] br: RFAL bit rate format - * \param[in] dir: Analog Config communication direction - * - * \return Analog Config Mode ID - * - ***************************************************************************** - */ -uint16_t rfalAnalogConfigGenModeID(rfalMode md, rfalBitRate br, uint16_t dir); - -#endif /* RFAL_ANALOG_CONFIG_H */ - -/** - * @} - * - * @} - * - * @} - */ diff --git a/lib/ST25RFAL002/include/rfal_chip.h b/lib/ST25RFAL002/include/rfal_chip.h deleted file mode 100644 index 296136b4189..00000000000 --- a/lib/ST25RFAL002/include/rfal_chip.h +++ /dev/null @@ -1,287 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_chip.h - * - * \author Gustavo Patricio - * - * \brief RF Chip specific Layer - * - * \warning This layer, which provides direct access to RF chip, should - * only be used for debug purposes and/or advanced features - * - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-HAL - * \brief RFAL Hardware Abstraction Layer - * @{ - * - * \addtogroup Chip - * \brief RFAL RF Chip Module - * @{ - * - */ - -#ifndef RFAL_CHIP_H -#define RFAL_CHIP_H - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ -#include "platform.h" -#include "st_errno.h" -#include "rfal_rf.h" - -/***************************************************************************** - * RF Chip * - *****************************************************************************/ - -/*! - ***************************************************************************** - * \brief Writes a register on the RF Chip - * - * Checks if the given register is valid and if so, writes the value(s) - * on the RF Chip register - * - * \param[in] reg: register address to be written, or the first if len > 1 - * \param[in] values: pointer with content to be written on the register(s) - * \param[in] len: number of consecutive registers to be written - * - * - * \return ERR_PARAM : Invalid register or bad request - * \return ERR_NOTSUPP : Feature not supported - * \return ERR_NONE : Write done with no error - ***************************************************************************** - */ -ReturnCode rfalChipWriteReg(uint16_t reg, const uint8_t* values, uint8_t len); - -/*! - ***************************************************************************** - * \brief Reads a register on the RF Chip - * - * Checks if the given register is valid and if so, reads the value(s) - * of the RF Chip register(s) - * - * \param[in] reg: register address to be read, or the first if len > 1 - * \param[out] values: pointer where the register(s) read content will be placed - * \param[in] len: number of consecutive registers to be read - * - * \return ERR_PARAM : Invalid register or bad request - * \return ERR_NOTSUPP : Feature not supported - * \return ERR_NONE : Read done with no error - ***************************************************************************** - */ -ReturnCode rfalChipReadReg(uint16_t reg, uint8_t* values, uint8_t len); - -/*! - ***************************************************************************** - * \brief Change a register on the RF Chip - * - * Change the value of the register bits on the RF Chip Test set in the valueMask. - * - * \param[in] reg: register address to be modified - * \param[in] valueMask: mask value of the register bits to be changed - * \param[in] value: register value to be set - * - * \return ERR_PARAM : Invalid register or bad request - * \return ERR_NOTSUPP : Feature not supported - * \return ERR_OK : Change done with no error - ***************************************************************************** - */ -ReturnCode rfalChipChangeRegBits(uint16_t reg, uint8_t valueMask, uint8_t value); - -/*! - ***************************************************************************** - * \brief Writes a Test register on the RF Chip - * - * Writes the value on the RF Chip Test register - * - * \param[in] reg: register address to be written - * \param[in] value: value to be written on the register - * - * - * \return ERR_PARAM : Invalid register or bad request - * \return ERR_NOTSUPP : Feature not supported - * \return ERR_NONE : Write done with no error - ***************************************************************************** - */ -ReturnCode rfalChipWriteTestReg(uint16_t reg, uint8_t value); - -/*! - ***************************************************************************** - * \brief Reads a Test register on the RF Chip - * - * Reads the value of the RF Chip Test register - * - * \param[in] reg: register address to be read - * \param[out] value: pointer where the register content will be placed - * - * \return ERR_PARAM :Invalid register or bad request - * \return ERR_NOTSUPP : Feature not supported - * \return ERR_NONE : Read done with no error - ***************************************************************************** - */ -ReturnCode rfalChipReadTestReg(uint16_t reg, uint8_t* value); - -/*! - ***************************************************************************** - * \brief Change a Test register on the RF Chip - * - * Change the value of the register bits on the RF Chip Test set in the valueMask. - * - * \param[in] reg: test register address to be modified - * \param[in] valueMask: mask value of the register bits to be changed - * \param[in] value: register value to be set - * - * \return ERR_PARAM : Invalid register or bad request - * \return ERR_NOTSUPP : Feature not supported - * \return ERR_OK : Change done with no error - ***************************************************************************** - */ -ReturnCode rfalChipChangeTestRegBits(uint16_t reg, uint8_t valueMask, uint8_t value); - -/*! - ***************************************************************************** - * \brief Execute command on the RF Chip - * - * Checks if the given command is valid and if so, executes it on - * the RF Chip - * - * \param[in] cmd: direct command to be executed - * - * \return ERR_PARAM : Invalid command or bad request - * \return ERR_NOTSUPP : Feature not supported - * \return ERR_NONE : Direct command executed with no error - ***************************************************************************** - */ -ReturnCode rfalChipExecCmd(uint16_t cmd); - -/*! - ***************************************************************************** - * \brief Set RFO - * - * Sets the RFO value to be used when the field is on (unmodulated/active) - * - * \param[in] rfo : the RFO value to be used - * - * \return ERR_IO : Internal error - * \return ERR_NOTSUPP : Feature not supported - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalChipSetRFO(uint8_t rfo); - -/*! - ***************************************************************************** - * \brief Get RFO - * - * Gets the RFO value used used when the field is on (unmodulated/active) - * - * \param[out] result : the current RFO value - * - * \return ERR_IO : Internal error - * \return ERR_NOTSUPP : Feature not supported - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalChipGetRFO(uint8_t* result); - -/*! - ***************************************************************************** - * \brief Measure Amplitude - * - * Measures the RF Amplitude - * - * \param[out] result : result of RF measurement - * - * \return ERR_IO : Internal error - * \return ERR_NOTSUPP : Feature not supported - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalChipMeasureAmplitude(uint8_t* result); - -/*! - ***************************************************************************** - * \brief Measure Phase - * - * Measures the Phase - * - * \param[out] result : result of Phase measurement - * - * \return ERR_IO : Internal error - * \return ERR_NOTSUPP : Feature not supported - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalChipMeasurePhase(uint8_t* result); - -/*! - ***************************************************************************** - * \brief Measure Capacitance - * - * Measures the Capacitance - * - * \param[out] result : result of Capacitance measurement - * - * \return ERR_IO : Internal error - * \return ERR_NOTSUPP : Feature not supported - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalChipMeasureCapacitance(uint8_t* result); - -/*! - ***************************************************************************** - * \brief Measure Power Supply - * - * Measures the Power Supply - * - * \param[in] param : measurement parameter (chip specific) - * \param[out] result : result of the measurement - * - * \return ERR_IO : Internal error - * \return ERR_NOTSUPP : Feature not supported - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalChipMeasurePowerSupply(uint8_t param, uint8_t* result); - -#endif /* RFAL_CHIP_H */ - -/** - * @} - * - * @} - * - * @} - */ diff --git a/lib/ST25RFAL002/include/rfal_crc.h b/lib/ST25RFAL002/include/rfal_crc.h deleted file mode 100644 index 134318cd1d2..00000000000 --- a/lib/ST25RFAL002/include/rfal_crc.h +++ /dev/null @@ -1,74 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_crc.h - * - * \author Ulrich Herrmann - * - * \brief CRC calculation module - * - */ -/*! - * - */ - -#ifndef RFAL_CRC_H_ -#define RFAL_CRC_H_ - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ -#include "platform.h" - -/* -****************************************************************************** -* GLOBAL FUNCTION PROTOTYPES -****************************************************************************** -*/ -/*! - ***************************************************************************** - * \brief Calculate CRC according to CCITT standard. - * - * This function takes \a length bytes from \a buf and calculates the CRC - * for this data. The result is returned. - * \note This implementation calculates the CRC with LSB first, i.e. all - * bytes are "read" from right to left. - * - * \param[in] preloadValue : Initial value of CRC calculation. - * \param[in] buf : buffer to calculate the CRC for. - * \param[in] length : size of the buffer. - * - * \return 16 bit long crc value. - * - ***************************************************************************** - */ -extern uint16_t rfalCrcCalculateCcitt(uint16_t preloadValue, const uint8_t* buf, uint16_t length); - -#endif /* RFAL_CRC_H_ */ diff --git a/lib/ST25RFAL002/include/rfal_dpo.h b/lib/ST25RFAL002/include/rfal_dpo.h deleted file mode 100644 index e86c48db8a8..00000000000 --- a/lib/ST25RFAL002/include/rfal_dpo.h +++ /dev/null @@ -1,207 +0,0 @@ - -/****************************************************************************** - * @attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * $Revision: $ - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_dpo.h - * - * \author Martin Zechleitner - * - * \brief Dynamic Power adjustment - * - * This module provides an interface to perform the power adjustment dynamically - * - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-HAL - * \brief RFAL Hardware Abstraction Layer - * @{ - * - * \addtogroup DPO - * \brief RFAL Dynamic Power Module - * @{ - * - */ - -#ifndef RFAL_DPO_H -#define RFAL_DPO_H - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "platform.h" -#include "st_errno.h" - -/* - ****************************************************************************** - * GLOBAL DEFINES - ****************************************************************************** - */ - -#define RFAL_DPO_TABLE_SIZE_MAX 15U /*!< Max DPO table size */ -#define RFAL_DPO_TABLE_PARAMETER 3U /*!< DPO table Parameter length */ - -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ - -/*! DPO table entry struct */ -typedef struct { - uint8_t rfoRes; /*!< Setting for the resistance level of the RFO */ - uint8_t inc; /*!< Threshold for incrementing the output power */ - uint8_t dec; /*!< Threshold for decrementing the output power */ -} rfalDpoEntry; - -/*! Function pointer to methode doing the reference measurement */ -typedef ReturnCode (*rfalDpoMeasureFunc)(uint8_t*); - -/* -****************************************************************************** -* GLOBAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -/*! - ***************************************************************************** - * \brief Initialize dynamic power table - * - * This function sets the internal dynamic power table to the default - * values stored in rfal_DpoTbl.h - * - ***************************************************************************** - */ -void rfalDpoInitialize(void); - -/*! - ***************************************************************************** - * \brief Set the measurement methode - * - * This function sets the measurement method used for reference measurement. - * Based on the measurement the power will then be adjusted - * - * \param[in] dpoMeasureFunc: callback of measurement function - * - ***************************************************************************** - */ -void rfalDpoSetMeasureCallback(rfalDpoMeasureFunc dpoMeasureFunc); - -/*! - ***************************************************************************** - * \brief Write dynamic power table - * - * Load the dynamic power table - * - * \param[in] powerTbl: location of power Table to be loaded - * \param[in] powerTblEntries: number of entries of the power Table to be loaded - * - * \return ERR_NONE : No error - * \return ERR_PARAM : if configTbl is invalid - * \return ERR_NOMEM : if the given Table is bigger exceeds the max size - ***************************************************************************** - */ -ReturnCode rfalDpoTableWrite(rfalDpoEntry* powerTbl, uint8_t powerTblEntries); - -/*! - ***************************************************************************** - * \brief Dynamic power table Read - * - * Read the dynamic power table - * - * \param[out] tblBuf: location to the rfalDpoEntry[] to place the Table - * \param[in] tblBufEntries: number of entries available in tblBuf to place the power Table - * \param[out] tableEntries: returned number of entries actually written into tblBuf - * - * \return ERR_NONE : No error - * \return ERR_PARAM : if configTbl is invalid or parameters are invalid - ***************************************************************************** - */ -ReturnCode rfalDpoTableRead(rfalDpoEntry* tblBuf, uint8_t tblBufEntries, uint8_t* tableEntries); - -/*! - ***************************************************************************** - * \brief Dynamic power adjust - * - * It measures the current output and adjusts the power accordingly to - * the dynamic power table - * - * \return ERR_NONE : No error - * \return ERR_PARAM : if configTbl is invalid or parameters are invalid - * \return ERR_WRONG_STATE : if the current state is valid for DPO Adjustment - ***************************************************************************** - */ -ReturnCode rfalDpoAdjust(void); - -/*! - ***************************************************************************** - * \brief Get Current Dynamic power table entry - * - * Return current used DPO power table entry settings - * - * \return ERR_NONE : Current DpoEntry. This includes d_res, inc and dec - * - ***************************************************************************** - */ -rfalDpoEntry* rfalDpoGetCurrentTableEntry(void); - -/*! - ***************************************************************************** - * \brief Dynamic power set enabled state - * - * \param[in] enable: new active state - * - * Set state to enable or disable the Dynamic power adjustment - * - ***************************************************************************** - */ -void rfalDpoSetEnabled(bool enable); - -/*! - ***************************************************************************** - * \brief Get the Dynamic power enabled state - * - * Get state of the Dynamic power adjustment - * - * \return true : enabled - * \return false : disabled - ***************************************************************************** - */ -bool rfalDpoIsEnabled(void); - -#endif /* RFAL_DPO_H */ - -/** - * @} - * - * @} - * - * @} - */ diff --git a/lib/ST25RFAL002/include/rfal_iso15693_2.h b/lib/ST25RFAL002/include/rfal_iso15693_2.h deleted file mode 100644 index 4ddb6b2be2a..00000000000 --- a/lib/ST25RFAL002/include/rfal_iso15693_2.h +++ /dev/null @@ -1,206 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_iso15693_2.h - * - * \author Ulrich Herrmann - * - * \brief Implementation of ISO-15693-2 - * - */ -/*! - * - */ - -#ifndef RFAL_ISO_15693_2_H -#define RFAL_ISO_15693_2_H - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ -#include "platform.h" -#include "st_errno.h" - -/* -****************************************************************************** -* GLOBAL DATATYPES -****************************************************************************** -*/ -/*! Enum holding possible VCD codings */ -typedef enum { ISO15693_VCD_CODING_1_4, ISO15693_VCD_CODING_1_256 } iso15693VcdCoding_t; - -/*! Enum holding possible VICC datarates */ - -/*! Configuration parameter used by #iso15693PhyConfigure */ -typedef struct { - iso15693VcdCoding_t coding; /*!< desired VCD coding */ - uint32_t - speedMode; /*!< 0: normal mode, 1: 2^1 = x2 Fast mode, 2 : 2^2 = x4 mode, 3 : 2^3 = x8 mode - all rx pulse numbers and times are divided by 1,2,4,8 */ -} iso15693PhyConfig_t; - -/*! Parameters how the stream mode should work */ -struct iso15693StreamConfig { - uint8_t useBPSK; /*!< 0: subcarrier, 1:BPSK */ - uint8_t din; /*!< the divider for the in subcarrier frequency: fc/2^din */ - uint8_t dout; /*!< the divider for the in subcarrier frequency fc/2^dout */ - uint8_t report_period_length; /*!< the length of the reporting period 2^report_period_length*/ -}; -/* -****************************************************************************** -* GLOBAL CONSTANTS -****************************************************************************** -*/ - -#define ISO15693_REQ_FLAG_TWO_SUBCARRIERS \ - 0x01U /*!< Flag indication that communication uses two subcarriers */ -#define ISO15693_REQ_FLAG_HIGH_DATARATE \ - 0x02U /*!< Flag indication that communication uses high bitrate */ -#define ISO15693_MASK_FDT_LISTEN \ - (65) /*!< t1min = 308,2us = 4192/fc = 65.5 * 64/fc */ - -/*! t1max = 323,3us = 4384/fc = 68.5 * 64/fc - * 12 = 768/fc unmodulated time of single subcarrior SoF */ -#define ISO15693_FWT (69 + 12) - -/* -****************************************************************************** -* GLOBAL FUNCTION PROTOTYPES -****************************************************************************** -*/ -/*! - ***************************************************************************** - * \brief Initialize the ISO15693 phy - * - * \param[in] config : ISO15693 phy related configuration (See #iso15693PhyConfig_t) - * \param[out] needed_stream_config : return a pointer to the stream config - * needed for this iso15693 config. To be used for configure RF chip. - * - * \return ERR_IO : Error during communication. - * \return ERR_NONE : No error. - * - ***************************************************************************** - */ -extern ReturnCode iso15693PhyConfigure( - const iso15693PhyConfig_t* config, - const struct iso15693StreamConfig** needed_stream_config); - -/*! - ***************************************************************************** - * \brief Return current phy configuration - * - * This function returns current Phy configuration previously - * set by #iso15693PhyConfigure - * - * \param[out] config : ISO15693 phy configuration. - * - * \return ERR_NONE : No error. - * - ***************************************************************************** - */ -extern ReturnCode iso15693PhyGetConfiguration(iso15693PhyConfig_t* config); - -/*! - ***************************************************************************** - * \brief Code an ISO15693 compatible frame - * - * This function takes \a length bytes from \a buffer, perform proper - * encoding and sends out the frame to the ST25R391x. - * - * \param[in] buffer : data to send, modified to adapt flags. - * \param[in] length : number of bytes to send. - * \param[in] sendCrc : If set to true, CRC is appended to the frame - * \param[in] sendFlags: If set to true, flag field is sent according to - * ISO15693. - * \param[in] picopassMode : If set to true, the coding will be according to Picopass - * \param[out] subbit_total_length : Return the complete bytes which need to - * be send for the current coding - * \param[in,out] offset : Set to 0 for first transfer, function will update it to - point to next byte to be coded - * \param[out] outbuf : buffer where the function will store the coded subbit stream - * \param[out] outBufSize : the size of the output buffer - * \param[out] actOutBufSize : the amount of data stored into the buffer at this call - * - * \return ERR_IO : Error during communication. - * \return ERR_AGAIN : Data was not coded all the way. Call function again with a new/emptied buffer - * \return ERR_NO_MEM : In case outBuf is not big enough. Needs to have at - least 5 bytes for 1of4 coding and 65 bytes for 1of256 coding - * \return ERR_NONE : No error. - * - ***************************************************************************** - */ -extern ReturnCode iso15693VCDCode( - uint8_t* buffer, - uint16_t length, - bool sendCrc, - bool sendFlags, - bool picopassMode, - uint16_t* subbit_total_length, - uint16_t* offset, - uint8_t* outbuf, - uint16_t outBufSize, - uint16_t* actOutBufSize); - -/*! - ***************************************************************************** - * \brief Receive an ISO15693 compatible frame - * - * This function receives an ISO15693 frame from the ST25R391x, decodes the frame - * and writes the raw data to \a buffer. - * \note Buffer needs to be big enough to hold CRC also (+2 bytes) - * - * \param[in] inBuf : buffer with the hamming coded stream to be decoded - * \param[in] inBufLen : number of bytes to decode (=length of buffer). - * \param[out] outBuf : buffer where received data shall be written to. - * \param[in] outBufLen : Length of output buffer, should be approx twice the size of inBuf - * \param[out] outBufPos : The number of decoded bytes. Could be used in - * extended implementation to allow multiple calls - * \param[out] bitsBeforeCol : in case of ERR_COLLISION this value holds the - * number of bits in the current byte where the collision happened. - * \param[in] ignoreBits : number of bits in the beginning where collisions will be ignored - * \param[in] picopassMode : if set to true, the decoding will be according to Picopass - * - * \return ERR_COLLISION : collision occured, data uncorrect - * \return ERR_CRC : CRC error, data uncorrect - * \return ERR_TIMEOUT : timeout waiting for data. - * \return ERR_NONE : No error. - * - ***************************************************************************** - */ -extern ReturnCode iso15693VICCDecode( - const uint8_t* inBuf, - uint16_t inBufLen, - uint8_t* outBuf, - uint16_t outBufLen, - uint16_t* outBufPos, - uint16_t* bitsBeforeCol, - uint16_t ignoreBits, - bool picopassMode); - -#endif /* RFAL_ISO_15693_2_H */ diff --git a/lib/ST25RFAL002/include/rfal_isoDep.h b/lib/ST25RFAL002/include/rfal_isoDep.h deleted file mode 100644 index f4ebdac5905..00000000000 --- a/lib/ST25RFAL002/include/rfal_isoDep.h +++ /dev/null @@ -1,1092 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_isoDep.h - * - * \author Gustavo Patricio - * - * \brief Implementation of ISO-DEP protocol - * - * This implementation was based on the following specs: - * - ISO/IEC 14443-4 2nd Edition 2008-07-15 - * - NFC Forum Digital Protocol 1.1 2014-01-14 - * - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-AL - * \brief RFAL Abstraction Layer - * @{ - * - * \addtogroup ISO-DEP - * \brief RFAL ISO-DEP Module - * @{ - * - */ - -#ifndef RFAL_ISODEP_H_ -#define RFAL_ISODEP_H_ -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "platform.h" -#include "rfal_nfcb.h" - -/* - ****************************************************************************** - * ENABLE SWITCH - ****************************************************************************** - */ - -#ifndef RFAL_FEATURE_ISO_DEP -#define RFAL_FEATURE_ISO_DEP \ - false /*!< ISO-DEP module configuration missing. Disabled by default */ -#endif - -/* If module is disabled remove the need for the user to set lengths */ -#if !RFAL_FEATURE_ISO_DEP -#undef RFAL_FEATURE_ISO_DEP_IBLOCK_MAX_LEN -#undef RFAL_FEATURE_ISO_DEP_APDU_MAX_LEN - -#define RFAL_FEATURE_ISO_DEP_IBLOCK_MAX_LEN (1U) /*!< ISO-DEP I-Block max length, set to "none" */ -#define RFAL_FEATURE_ISO_DEP_APDU_MAX_LEN (1U) /*!< ISO-DEP APDU max length, set to "none" */ -#endif /* !RFAL_FEATURE_NFC_DEP */ - -/* - ****************************************************************************** - * DEFINES - ****************************************************************************** - */ - -#define RFAL_ISODEP_PROLOGUE_SIZE \ - (3U) /*!< Length of Prologue Field for I-Block Format */ - -#define RFAL_ISODEP_PCB_LEN \ - (1U) /*!< PCB length */ -#define RFAL_ISODEP_DID_LEN \ - (1U) /*!< DID length */ -#define RFAL_ISODEP_NAD_LEN \ - (1U) /*!< NAD length */ -#define RFAL_ISODEP_NO_DID \ - (0x00U) /*!< DID value indicating the ISO-DEP layer not to use DID */ -#define RFAL_ISODEP_NO_NAD \ - (0xFFU) /*!< NAD value indicating the ISO-DEP layer not to use NAD */ - -#define RFAL_ISODEP_FWI_MASK \ - (0xF0U) /*!< Mask bits of FWI */ -#define RFAL_ISODEP_FWI_SHIFT \ - (4U) /*!< Shift val of FWI */ -#define RFAL_ISODEP_FWI_DEFAULT \ - (4U) /*!< Default value for FWI Digital 1.0 11.6.2.17 */ -#define RFAL_ISODEP_ADV_FEATURE \ - (0x0FU) /*!< Indicate 256 Bytes FSD and Advanc Proto Feature support:NAD & DID */ - -#define RFAL_ISODEP_DID_MAX \ - (14U) /*!< Maximum DID value */ - -#define RFAL_ISODEP_BRI_MASK \ - (0x07U) /*!< Mask bits for Poll to Listen Send bitrate */ -#define RFAL_ISODEP_BSI_MASK \ - (0x70U) /*!< Mask bits for Listen to Poll Send bitrate */ -#define RFAL_ISODEP_SAME_BITRATE_MASK \ - (0x80U) /*!< Mask bit indicate only same bit rate D for both direction support */ -#define RFAL_ISODEP_BITRATE_RFU_MASK \ - (0x08U) /*!< Mask bit for RFU */ - -/*! Maximum Frame Waiting Time = ((256 * 16/fc) * 2^FWImax) = ((256*16/fc)*2^14) = (67108864)/fc = 2^26 (1/fc) */ -#define RFAL_ISODEP_MAX_FWT ((uint32_t)1U << 26) - -#define RFAL_ISODEP_FSDI_DEFAULT \ - RFAL_ISODEP_FSXI_256 /*!< Default Frame Size Integer in Poll mode */ -#define RFAL_ISODEP_FSX_KEEP (0xFFU) /*!< Flag to keep FSX from activation */ -#define RFAL_ISODEP_DEFAULT_FSCI \ - RFAL_ISODEP_FSXI_256 /*!< FSCI default value to be used in Listen Mode */ -#define RFAL_ISODEP_DEFAULT_FSC \ - RFAL_ISODEP_FSX_256 /*!< FSC default value (aligned RFAL_ISODEP_DEFAULT_FSCI) */ -#define RFAL_ISODEP_DEFAULT_SFGI (0U) /*!< SFGI Default value to be used in Listen Mode */ -#define RFAL_ISODEP_DEFAULT_FWI (8U) /*!< Default Listener FWI (Max) Digital 2.0 B7 & B3 */ - -#define RFAL_ISODEP_APDU_MAX_LEN \ - RFAL_ISODEP_FSX_1024 /*!< Max APDU length */ - -#define RFAL_ISODEP_ATTRIB_RES_MBLI_NO_INFO \ - (0x00U) /*!< MBLI indicating no information on its internal input buffer size */ -#define RFAL_ISODEP_ATTRIB_REQ_PARAM1_DEFAULT \ - (0x00U) /*!< Default values of Param 1 of ATTRIB_REQ Digital 1.0 12.6.1.3-5 */ -#define RFAL_ISODEP_ATTRIB_HLINFO_LEN \ - (32U) /*!< Maximum Size of Higher Layer Information */ -#define RFAL_ISODEP_ATS_HB_MAX_LEN \ - (15U) /*!< Maximum length of Historical Bytes Digital 1.1 13.6.2.23 */ -#define RFAL_ISODEP_ATTRIB_REQ_MIN_LEN \ - (9U) /*!< Minimum Length of ATTRIB_REQ command */ -#define RFAL_ISODEP_ATTRIB_RES_MIN_LEN \ - (1U) /*!< Minimum Length of ATTRIB_RES response */ - -#define RFAL_ISODEP_SPARAM_VALUES_MAX_LEN \ - (16U) /*!< Maximum Length of the value field on S(PARAMETERS) */ -#define RFAL_ISODEP_SPARAM_TAG_BLOCKINFO \ - (0xA0U) /*!< S(PARAMETERS) tag Block information */ -#define RFAL_ISODEP_SPARAM_TAG_BRREQ \ - (0xA1U) /*!< S(PARAMETERS) tag Bit rates Request */ -#define RFAL_ISODEP_SPARAM_TAG_BRIND \ - (0xA2U) /*!< S(PARAMETERS) tag Bit rates Indication */ -#define RFAL_ISODEP_SPARAM_TAG_BRACT \ - (0xA3U) /*!< S(PARAMETERS) tag Bit rates Activation */ -#define RFAL_ISODEP_SPARAM_TAG_BRACK \ - (0xA4U) /*!< S(PARAMETERS) tag Bit rates Acknowledgement */ - -#define RFAL_ISODEP_SPARAM_TAG_SUP_PCD2PICC \ - (0x80U) /*!< S(PARAMETERS) tag Supported bit rates from PCD to PICC */ -#define RFAL_ISODEP_SPARAM_TAG_SUP_PICC2PCD \ - (0x81U) /*!< S(PARAMETERS) tag Supported bit rates from PICC to PCD */ -#define RFAL_ISODEP_SPARAM_TAG_SUP_FRAME \ - (0x82U) /*!< S(PARAMETERS) tag Supported framing options PICC to PCD */ -#define RFAL_ISODEP_SPARAM_TAG_SEL_PCD2PICC \ - (0x83U) /*!< S(PARAMETERS) tag Selected bit rate from PCD to PICC */ -#define RFAL_ISODEP_SPARAM_TAG_SEL_PICC2PCD \ - (0x84U) /*!< S(PARAMETERS) tag Selected bit rate from PICC to PCD */ -#define RFAL_ISODEP_SPARAM_TAG_SEL_FRAME \ - (0x85U) /*!< S(PARAMETERS) tag Selected framing options PICC to PCD */ - -#define RFAL_ISODEP_SPARAM_TAG_LEN \ - (1) /*!< S(PARAMETERS) Tag Length */ -#define RFAL_ISODEP_SPARAM_TAG_BRREQ_LEN \ - (0U) /*!< S(PARAMETERS) tag Bit rates Request Length */ -#define RFAL_ISODEP_SPARAM_TAG_PICC2PCD_LEN \ - (2U) /*!< S(PARAMETERS) bit rates from PCD to PICC Length */ -#define RFAL_ISODEP_SPARAM_TAG_PCD2PICC_LEN \ - (2U) /*!< S(PARAMETERS) bit rates from PICC to PCD Length */ -#define RFAL_ISODEP_SPARAM_TAG_BRACK_LEN \ - (0U) /*!< S(PARAMETERS) tag Bit rates Acknowledgement Length */ - -#define RFAL_ISODEP_ATS_TA_DPL_212 \ - (0x01U) /*!< ATS TA DSI 212 kbps support bit mask */ -#define RFAL_ISODEP_ATS_TA_DPL_424 \ - (0x02U) /*!< ATS TA DSI 424 kbps support bit mask */ -#define RFAL_ISODEP_ATS_TA_DPL_848 \ - (0x04U) /*!< ATS TA DSI 848 kbps support bit mask */ -#define RFAL_ISODEP_ATS_TA_DLP_212 \ - (0x10U) /*!< ATS TA DSI 212 kbps support bit mask */ -#define RFAL_ISODEP_ATS_TA_DLP_424 \ - (0x20U) /*!< ATS TA DRI 424 kbps support bit mask */ -#define RFAL_ISODEP_ATS_TA_DLP_848 \ - (0x40U) /*!< ATS TA DRI 848 kbps support bit mask */ -#define RFAL_ISODEP_ATS_TA_SAME_D \ - (0x80U) /*!< ATS TA same bit both directions bit mask */ -#define RFAL_ISODEP_ATS_TB_FWI_MASK \ - (0xF0U) /*!< Mask bits for FWI (Frame Waiting Integer) in TB byte */ -#define RFAL_ISODEP_ATS_TB_SFGI_MASK \ - (0x0FU) /*!< Mask bits for SFGI (Start-Up Frame Guard Integer) in TB byte */ - -#define RFAL_ISODEP_ATS_T0_TA_PRESENCE_MASK \ - (0x10U) /*!< Mask bit for TA presence */ -#define RFAL_ISODEP_ATS_T0_TB_PRESENCE_MASK \ - (0x20U) /*!< Mask bit for TB presence */ -#define RFAL_ISODEP_ATS_T0_TC_PRESENCE_MASK \ - (0x40U) /*!< Mask bit for TC presence */ -#define RFAL_ISODEP_ATS_T0_FSCI_MASK \ - (0x0FU) /*!< Mask bit for FSCI presence */ -#define RFAL_ISODEP_ATS_T0_OFFSET \ - (0x01U) /*!< Offset of T0 in ATS Response */ - -#define RFAL_ISODEP_MAX_I_RETRYS \ - (2U) /*!< Number of retries for a I-Block Digital 2.0 16.2.5.4 */ -#define RFAL_ISODEP_MAX_R_RETRYS \ - (3U) /*!< Number of retries for a R-Block Digital 2.0 B9 - nRETRY ACK/NAK: [2,5] */ -#define RFAL_ISODEP_MAX_WTX_NACK_RETRYS \ - (3U) /*!< Number of S(WTX) replied with NACK Digital 2.0 B9 - nRETRY WTX[2,5] */ -#define RFAL_ISODEP_MAX_WTX_RETRYS \ - (20U) /*!< Number of overall S(WTX) retries Digital 2.0 16.2.5.2 */ -#define RFAL_ISODEP_MAX_WTX_RETRYS_ULTD \ - (255U) /*!< Use unlimited number of overall S(WTX) */ -#define RFAL_ISODEP_MAX_DSL_RETRYS \ - (0U) /*!< Number of retries for a S(DESELECT) Digital 2.0 B9 - nRETRY DESELECT: [0,5] */ -#define RFAL_ISODEP_RATS_RETRIES \ - (1U) /*!< RATS retries upon fail Digital 2.0 B7 - nRETRY RATS [0,1] */ - -/*! Frame Size for Proximity Card Integer definitions */ -typedef enum { - RFAL_ISODEP_FSXI_16 = - 0, /*!< Frame Size for Proximity Card Integer with 16 bytes */ - RFAL_ISODEP_FSXI_24 = - 1, /*!< Frame Size for Proximity Card Integer with 24 bytes */ - RFAL_ISODEP_FSXI_32 = - 2, /*!< Frame Size for Proximity Card Integer with 32 bytes */ - RFAL_ISODEP_FSXI_40 = - 3, /*!< Frame Size for Proximity Card Integer with 40 bytes */ - RFAL_ISODEP_FSXI_48 = - 4, /*!< Frame Size for Proximity Card Integer with 48 bytes */ - RFAL_ISODEP_FSXI_64 = - 5, /*!< Frame Size for Proximity Card Integer with 64 bytes */ - RFAL_ISODEP_FSXI_96 = - 6, /*!< Frame Size for Proximity Card Integer with 96 bytes */ - RFAL_ISODEP_FSXI_128 = - 7, /*!< Frame Size for Proximity Card Integer with 128 bytes */ - RFAL_ISODEP_FSXI_256 = - 8, /*!< Frame Size for Proximity Card Integer with 256 bytes */ - RFAL_ISODEP_FSXI_512 = - 9, /*!< Frame Size for Proximity Card Integer with 512 bytes ISO14443-3 Amd2 2012 */ - RFAL_ISODEP_FSXI_1024 = - 10, /*!< Frame Size for Proximity Card Integer with 1024 bytes ISO14443-3 Amd2 2012 */ - RFAL_ISODEP_FSXI_2048 = - 11, /*!< Frame Size for Proximity Card Integer with 2048 bytes ISO14443-3 Amd2 2012 */ - RFAL_ISODEP_FSXI_4096 = - 12 /*!< Frame Size for Proximity Card Integer with 4096 bytes ISO14443-3 Amd2 2012 */ -} rfalIsoDepFSxI; - -/*! Frame Size for Proximity Card definitions */ -typedef enum { - RFAL_ISODEP_FSX_16 = - 16, /*!< Frame Size for Proximity Card with 16 bytes */ - RFAL_ISODEP_FSX_24 = - 24, /*!< Frame Size for Proximity Card with 24 bytes */ - RFAL_ISODEP_FSX_32 = - 32, /*!< Frame Size for Proximity Card with 32 bytes */ - RFAL_ISODEP_FSX_40 = - 40, /*!< Frame Size for Proximity Card with 40 bytes */ - RFAL_ISODEP_FSX_48 = - 48, /*!< Frame Size for Proximity Card with 48 bytes */ - RFAL_ISODEP_FSX_64 = - 64, /*!< Frame Size for Proximity Card with 64 bytes */ - RFAL_ISODEP_FSX_96 = - 96, /*!< Frame Size for Proximity Card with 96 bytes */ - RFAL_ISODEP_FSX_128 = - 128, /*!< Frame Size for Proximity Card with 128 bytes */ - RFAL_ISODEP_FSX_256 = - 256, /*!< Frame Size for Proximity Card with 256 bytes */ - RFAL_ISODEP_FSX_512 = - 512, /*!< Frame Size for Proximity Card with 512 bytes ISO14443-3 Amd2 2012 */ - RFAL_ISODEP_FSX_1024 = - 1024, /*!< Frame Size for Proximity Card with 1024 bytes ISO14443-3 Amd2 2012 */ - RFAL_ISODEP_FSX_2048 = - 2048, /*!< Frame Size for Proximity Card with 2048 bytes ISO14443-3 Amd2 2012 */ - RFAL_ISODEP_FSX_4096 = - 4096, /*!< Frame Size for Proximity Card with 4096 bytes ISO14443-3 Amd2 2012 */ -} rfalIsoDepFSx; - -/* - ****************************************************************************** - * GLOBAL MACROS - ****************************************************************************** - */ - -/* - ****************************************************************************** - * GLOBAL DATA TYPES - ****************************************************************************** - */ - -/*! RATS format Digital 1.1 13.6.1 */ -typedef struct { - uint8_t CMD; /*!< RATS command byte: 0xE0 */ - uint8_t PARAM; /*!< Param indicating FSDI and DID */ -} rfalIsoDepRats; - -/*! ATS response format Digital 1.1 13.6.2 */ -typedef struct { - uint8_t TL; /*!< Length Byte, including TL byte itself */ - uint8_t T0; /*!< Format Byte T0 indicating if TA, TB, TC */ - uint8_t TA; /*!< Interface Byte TA(1) */ - uint8_t TB; /*!< Interface Byte TB(1) */ - uint8_t TC; /*!< Interface Byte TC(1) */ - uint8_t HB[RFAL_ISODEP_ATS_HB_MAX_LEN]; /*!< Historical Bytes */ -} rfalIsoDepAts; - -/*! PPS Request format (Protocol and Parameter Selection) ISO14443-4 5.3 */ -typedef struct { - uint8_t PPSS; /*!< Start Byte: [ 1101b | CID[4b] ] */ - uint8_t PPS0; /*!< Parameter 0:[ 000b | PPS1[1n] | 0001b ] */ - uint8_t PPS1; /*!< Parameter 1:[ 0000b | DSI[2b] | DRI[2b] ]*/ -} rfalIsoDepPpsReq; - -/*! PPS Response format (Protocol and Parameter Selection) ISO14443-4 5.4 */ -typedef struct { - uint8_t PPSS; /*!< Start Byte: [ 1101b | CID[4b] ] */ -} rfalIsoDepPpsRes; - -/*! ATTRIB Command Format Digital 1.1 15.6.1 */ -typedef struct { - uint8_t cmd; /*!< ATTRIB_REQ command byte */ - uint8_t nfcid0[RFAL_NFCB_NFCID0_LEN]; /*!< NFCID0 of the card to be selected */ - struct { - uint8_t PARAM1; /*!< PARAM1 of ATTRIB command */ - uint8_t PARAM2; /*!< PARAM2 of ATTRIB command */ - uint8_t PARAM3; /*!< PARAM3 of ATTRIB command */ - uint8_t PARAM4; /*!< PARAM4 of ATTRIB command */ - } Param; /*!< Parameter of ATTRIB command */ - uint8_t HLInfo[RFAL_ISODEP_ATTRIB_HLINFO_LEN]; /*!< Higher Layer Information */ -} rfalIsoDepAttribCmd; - -/*! ATTRIB Response Format Digital 1.1 15.6.2 */ -typedef struct { - uint8_t mbliDid; /*!< Contains MBLI and DID */ - uint8_t HLInfo[RFAL_ISODEP_ATTRIB_HLINFO_LEN]; /*!< Higher Layer Information */ -} rfalIsoDepAttribRes; - -/*! S(Parameters) Command Format ISO14443-4 (2016) Table 4 */ -typedef struct { - uint8_t tag; /*!< S(PARAMETERS) Tag field */ - uint8_t length; /*!< S(PARAMETERS) Length field */ - uint8_t value[RFAL_ISODEP_SPARAM_VALUES_MAX_LEN]; /*!< S(PARAMETERS) Value field */ -} rfalIsoDepSParameter; - -/*! Activation info as Poller and Listener for NFC-A and NFC-B */ -typedef union { /* PRQA S 0750 # MISRA 19.2 - Both members of the union will not be used concurrently, device is only of type A or B at a time. Thus no problem can occur. */ - - /*! NFC-A information */ - union { /* PRQA S 0750 # MISRA 19.2 - Both members of the union will not be used concurrently, device is only PCD or PICC at a time. Thus no problem can occur. */ - struct { - rfalIsoDepAts ATS; /*!< ATS response (Poller mode) */ - uint8_t ATSLen; /*!< ATS response length (Poller mode) */ - } Listener; - struct { - rfalIsoDepRats RATS; /*!< RATS request (Listener mode) */ - } Poller; - } A; - - /*! NFC-B information */ - union { /* PRQA S 0750 # MISRA 19.2 - Both members of the union will not be used concurrently, device is only PCD or PICC at a time. Thus no problem can occur. */ - struct { - rfalIsoDepAttribRes ATTRIB_RES; /*!< ATTRIB_RES (Poller mode) */ - uint8_t ATTRIB_RESLen; /*!< ATTRIB_RES length (Poller mode) */ - } Listener; - struct { - rfalIsoDepAttribCmd ATTRIB; /*!< ATTRIB request (Listener mode) */ - uint8_t ATTRIBLen; /*!< ATTRIB request length (Listener mode) */ - } Poller; - } B; -} rfalIsoDepActivation; - -/*! ISO-DEP device Info */ -typedef struct { - uint8_t FWI; /*!< Frame Waiting Integer */ - uint32_t FWT; /*!< Frame Waiting Time (1/fc) */ - uint32_t dFWT; /*!< Delta Frame Waiting Time (1/fc) */ - uint32_t SFGI; /*!< Start-up Frame Guard time Integer */ - uint32_t SFGT; /*!< Start-up Frame Guard Time (ms) */ - uint8_t FSxI; /*!< Frame Size Device/Card Integer (FSDI or FSCI) */ - uint16_t FSx; /*!< Frame Size Device/Card (FSD or FSC) */ - uint32_t MBL; /*!< Maximum Buffer Length (optional for NFC-B) */ - rfalBitRate DSI; /*!< Bit Rate coding from Listener (PICC) to Poller (PCD) */ - rfalBitRate DRI; /*!< Bit Rate coding from Poller (PCD) to Listener (PICC) */ - uint8_t DID; /*!< Device ID */ - uint8_t NAD; /*!< Node ADdress */ - bool supDID; /*!< DID supported flag */ - bool supNAD; /*!< NAD supported flag */ - bool supAdFt; /*!< Advanced Features supported flag */ -} rfalIsoDepInfo; - -/*! ISO-DEP Device structure */ -typedef struct { - rfalIsoDepActivation activation; /*!< Activation Info */ - rfalIsoDepInfo info; /*!< ISO-DEP (ISO14443-4) device Info */ -} rfalIsoDepDevice; - -/*! ATTRIB Response parameters */ -typedef struct { - uint8_t mbli; /*!< MBLI */ - uint8_t HLInfo[RFAL_ISODEP_ATTRIB_HLINFO_LEN]; /*!< Hi Layer Information */ - uint8_t HLInfoLen; /*!< Hi Layer Information Length */ -} rfalIsoDepAttribResParam; - -/*! ATS Response parameter */ -typedef struct { - uint8_t fsci; /*!< Frame Size of Proximity Card Integer */ - uint8_t fwi; /*!< Frame Waiting Time Integer */ - uint8_t sfgi; /*!< Start-Up Frame Guard Time Integer */ - bool didSupport; /*!< DID Supported */ - uint8_t ta; /*!< Max supported bitrate both direction */ - uint8_t* hb; /*!< Historical Bytes data */ - uint8_t hbLen; /*!< Historical Bytes Length */ -} rfalIsoDepAtsParam; - -/*! Structure of I-Block Buffer format from caller */ -typedef struct { - uint8_t prologue[RFAL_ISODEP_PROLOGUE_SIZE]; /*!< Prologue/SoD buffer */ - uint8_t - inf[RFAL_FEATURE_ISO_DEP_IBLOCK_MAX_LEN]; /*!< INF/Payload buffer */ -} rfalIsoDepBufFormat; - -/*! Structure of APDU Buffer format from caller */ -typedef struct { - uint8_t prologue[RFAL_ISODEP_PROLOGUE_SIZE]; /*!< Prologue/SoD buffer */ - uint8_t apdu[RFAL_FEATURE_ISO_DEP_APDU_MAX_LEN]; /*!< APDU/Payload buffer */ -} rfalIsoDepApduBufFormat; - -/*! Listen Activation Parameters Structure */ -typedef struct { - rfalIsoDepBufFormat* rxBuf; /*!< Receive Buffer struct reference */ - uint16_t* rxLen; /*!< Received INF data length in Bytes */ - bool* isRxChaining; /*!< Received data is not complete */ - rfalIsoDepDevice* isoDepDev; /*!< ISO-DEP device info */ -} rfalIsoDepListenActvParam; - -/*! Structure of parameters used on ISO DEP Transceive */ -typedef struct { - rfalIsoDepBufFormat* txBuf; /*!< Transmit Buffer struct reference */ - uint16_t txBufLen; /*!< Transmit Buffer INF field length in Bytes*/ - bool isTxChaining; /*!< Transmit data is not complete */ - rfalIsoDepBufFormat* rxBuf; /*!< Receive Buffer struct reference in Bytes */ - uint16_t* rxLen; /*!< Received INF data length in Bytes */ - bool* isRxChaining; /*!< Received data is not complete */ - uint32_t FWT; /*!< FWT to be used (ignored in Listen Mode) */ - uint32_t dFWT; /*!< Delta FWT to be used */ - uint16_t ourFSx; /*!< Our device Frame Size (FSD or FSC) */ - uint16_t FSx; /*!< Other device Frame Size (FSD or FSC) */ - uint8_t DID; /*!< Device ID (RFAL_ISODEP_NO_DID if no DID) */ -} rfalIsoDepTxRxParam; - -/*! Structure of parameters used on ISO DEP APDU Transceive */ -typedef struct { - rfalIsoDepApduBufFormat* txBuf; /*!< Transmit Buffer struct reference */ - uint16_t txBufLen; /*!< Transmit Buffer INF field length in Bytes*/ - rfalIsoDepApduBufFormat* rxBuf; /*!< Receive Buffer struct reference in Bytes */ - uint16_t* rxLen; /*!< Received INF data length in Bytes */ - rfalIsoDepBufFormat* tmpBuf; /*!< Temp buffer for Rx I-Blocks (internal) */ - uint32_t FWT; /*!< FWT to be used (ignored in Listen Mode) */ - uint32_t dFWT; /*!< Delta FWT to be used */ - uint16_t FSx; /*!< Other device Frame Size (FSD or FSC) */ - uint16_t ourFSx; /*!< Our device Frame Size (FSD or FSC) */ - uint8_t DID; /*!< Device ID (RFAL_ISODEP_NO_DID if no DID) */ -} rfalIsoDepApduTxRxParam; - -/* - ****************************************************************************** - * GLOBAL FUNCTION PROTOTYPES - ****************************************************************************** - */ - -/*! - ****************************************************************************** - * \brief Initialize the ISO-DEP protocol - * - * Initialize the ISO-DEP protocol layer with default config - ****************************************************************************** - */ -void rfalIsoDepInitialize(void); - -/*! - ****************************************************************************** - * \brief Initialize the ISO-DEP protocol - * - * Initialize the ISO-DEP protocol layer with additional parameters allowing - * to customise the protocol layer for specific behaviours - * - - * \param[in] compMode : Compliance mode to be performed - * \param[in] maxRetriesR : Number of retries for a R-Block - * Digital 2.0 B9 - nRETRY ACK/NAK: [2,5] - * \param[in] maxRetriesSnWTX : Number of retries for a S(WTX) (only in case - * of NAKs) Digital 2.0 B9 - nRETRY WTX[2,5] - * \param[in] maxRetriesSWTX : Number of overall S(WTX) retries. - * Use RFAL_ISODEP_MAX_WTX_RETRYS_ULTD for disabling - * this limit check Digital 2.0 16.2.5.2 - * \param[in] maxRetriesSDSL : Number of retries for a S(DESELECT) - * Digital 2.0 B9 - nRETRY DESELECT: [0,5] - * \param[in] maxRetriesI : Number of retries for a I-Block - * Digital 2.0 16.2.5.4 - * \param[in] maxRetriesRATS : Number of retries for RATS - * Digital 2.0 B7 - nRETRY RATS [0,1] - * - ****************************************************************************** - */ -void rfalIsoDepInitializeWithParams( - rfalComplianceMode compMode, - uint8_t maxRetriesR, - uint8_t maxRetriesSnWTX, - uint8_t maxRetriesSWTX, - uint8_t maxRetriesSDSL, - uint8_t maxRetriesI, - uint8_t maxRetriesRATS); - -/*! - ***************************************************************************** - * \brief FSxI to FSx - * - * Convert Frame Size for proximity coupling Device Integer (FSxI) to - * Frame Size for proximity coupling Device (FSx) - * - * FSD - maximum frame size for NFC Forum Device in Poll Mode - * FSC - maximum frame size for NFC Forum Device in Listen Mode - * - * FSxI = FSDI or FSCI - * FSx = FSD or FSC - * - * The FSD/FSC value includes the header and CRC - * - * \param[in] FSxI : Frame Size for proximity coupling Device Integer - * - * \return fsx : Frame Size for proximity coupling Device (FSD or FSC) - * - ***************************************************************************** - */ -uint16_t rfalIsoDepFSxI2FSx(uint8_t FSxI); - -/*! - ***************************************************************************** - * \brief FWI to FWT - * - * Convert Frame Waiting time Integer (FWI) to Frame Waiting Time (FWT) in - * 1/fc units - * - * \param[in] fwi : Frame Waiting time Integer - * - * \return fwt : Frame Waiting Time in 1/fc units - * - ***************************************************************************** - */ -uint32_t rfalIsoDepFWI2FWT(uint8_t fwi); - -/*! - ***************************************************************************** - * \brief Check if the buffer data contains a valid RATS command - * - * Check if it is a well formed RATS command with 2 bytes - * This function does not check the validity of FSDI and DID - * - * \param[in] buf : reference to buffer containing the data to be checked - * \param[in] bufLen : length of data in the buffer in bytes - * - * \return true if the data indicates a RATS command; false otherwise - ***************************************************************************** - */ -bool rfalIsoDepIsRats(const uint8_t* buf, uint8_t bufLen); - -/*! - ***************************************************************************** - * \brief Check if the buffer data contains a valid ATTRIB command - * - * Check if it is a well formed ATTRIB command, but does not check the - * validity of the information inside - * - * \param[in] buf : reference to buffer containing the data to be checked - * \param[in] bufLen : length of data in the buffer in bytes - * - * \return true if the data indicates a ATTRIB command; false otherwise - ***************************************************************************** - */ -bool rfalIsoDepIsAttrib(const uint8_t* buf, uint8_t bufLen); - -/*! - ***************************************************************************** - * \brief Start Listen Activation Handling - * - * Start Listen Activation Handling and setup to receive first I-block which may - * contain complete or partial APDU after activation is completed - * - * Pass in RATS for T4AT, or ATTRIB for T4BT, to handle ATS or ATTRIB Response respectively - * The Activation Handling handles ATS and ATTRIB Response; and additionally PPS Response - * if a PPS is received for T4AT. - * The method uses the current RFAL state machine to determine if it is expecting RATS or ATTRIB - * - * Activation is completed if PPS Response is sent or if first PDU is received in T4T-A - * Activation is completed if ATTRIB Response is sent in T4T-B - * - * \ref rfalIsoDepListenGetActivationStatus provide status if activation is completed. - * \ref rfalIsoDepStartTransceive shall be called right after activation is completed - * - * \param[in] atsParam : reference to ATS parameters - * \param[in] attribResParam : reference to ATTRIB_RES parameters - * \param[in] buf : reference to buffer containing RATS or ATTRIB - * \param[in] bufLen : length in bytes of the given bufffer - * \param[in] actParam : reference to incoming reception information will be placed - * - * - * \warning Once the Activation has been completed the method - * rfalIsoDepGetTransceiveStatus() must be called. - * If activation has completed due to reception of a data block (not PPS) the - * buffer owned by the caller and passed on actParam must still contain this data. - * The first data will be processed (I-Block or S-DSL) by rfalIsoDepGetTransceiveStatus() - * inform the caller and then for the next transaction use rfalIsoDepStartTransceive() - * - * \return ERR_NONE : RATS/ATTRIB is valid and activation has started - * \return ERR_PARAM : Invalid parameters - * \return ERR_PROTO : Invalid request - * \return ERR_NOTSUPP : Feature not supported - ***************************************************************************** - */ -ReturnCode rfalIsoDepListenStartActivation( - rfalIsoDepAtsParam* atsParam, - const rfalIsoDepAttribResParam* attribResParam, - const uint8_t* buf, - uint16_t bufLen, - rfalIsoDepListenActvParam actParam); - -/*! - ***************************************************************************** - * \brief Get the current Activation Status - * - * \return ERR_NONE if Activation is already completed - * \return ERR_BUSY if Activation is ongoing - * \return ERR_LINK_LOSS if Remote Field is turned off - ***************************************************************************** - */ -ReturnCode rfalIsoDepListenGetActivationStatus(void); - -/*! - ***************************************************************************** - * \brief Get the ISO-DEP Communication Information - * - * Gets the maximum INF length in bytes based on current Frame Size - * for proximity coupling Device (FSD or FSC) excluding the header and CRC - * - * \return maximum INF length in bytes - ***************************************************************************** - */ -uint16_t rfalIsoDepGetMaxInfLen(void); - -/*! - ***************************************************************************** - * \brief ISO-DEP Start Transceive - * - * This method triggers a ISO-DEP Transceive containing a complete or - * partial APDU - * It transmits the given message and handles all protocol retransmitions, - * error handling and control messages - * - * The txBuf contains a complete or partial APDU (INF) to be transmitted - * The Prologue field will be manipulated by the Transceive - * - * If the buffer contains a partial APDU and is not the last block, - * then isTxChaining must be set to true - * - * \param[in] param: reference parameters to be used for the Transceive - * - * \return ERR_PARAM : Bad request - * \return ERR_WRONG_STATE : The module is not in a proper state - * \return ERR_NONE : The Transceive request has been started - ***************************************************************************** - */ -ReturnCode rfalIsoDepStartTransceive(rfalIsoDepTxRxParam param); - -/*! - ***************************************************************************** - * \brief Get the Transceive status - * - * Returns the status of the ISO-DEP Transceive - * - * \warning When the other device is performing chaining once a chained - * block is received the error ERR_AGAIN is sent. At this point - * caller must handle the received data immediately. - * When ERR_AGAIN is returned an ACK has already been sent to - * the other device and the next block might be incoming. - * If rfalWorker() is called frequently it will place the next - * block on the given buffer - * - * - * \return ERR_NONE : Transceive has been completed successfully - * \return ERR_BUSY : Transceive is ongoing - * \return ERR_PROTO : Protocol error occurred - * \return ERR_TIMEOUT : Timeout error occurred - * \return ERR_SLEEP_REQ : Deselect has been received and responded - * \return ERR_NOMEM : The received INF does not fit into the - * receive buffer - * \return ERR_LINK_LOSS : Communication is lost because Reader/Writer - * has turned off its field - * \return ERR_AGAIN : received one chaining block, continue to call - * this method to retrieve the remaining blocks - ***************************************************************************** - */ -ReturnCode rfalIsoDepGetTransceiveStatus(void); - -/*! - ***************************************************************************** - * \brief ISO-DEP Start APDU Transceive - * - * This method triggers a ISO-DEP Transceive containing a complete APDU - * It transmits the given message and handles all protocol retransmitions, - * error handling and control messages - * - * The txBuf contains a complete APDU to be transmitted - * The Prologue field will be manipulated by the Transceive - * - * \warning the txBuf will be modified during the transmission - * \warning the maximum RF frame which can be received is limited by param.tmpBuf - * - * \param[in] param: reference parameters to be used for the Transceive - * - * \return ERR_PARAM : Bad request - * \return ERR_WRONG_STATE : The module is not in a proper state - * \return ERR_NONE : The Transceive request has been started - ***************************************************************************** - */ -ReturnCode rfalIsoDepStartApduTransceive(rfalIsoDepApduTxRxParam param); - -/*! - ***************************************************************************** - * \brief Get the APDU Transceive status - * - * \return ERR_NONE : if Transceive has been completed successfully - * \return ERR_BUSY : if Transceive is ongoing - * \return ERR_PROTO : if a protocol error occurred - * \return ERR_TIMEOUT : if a timeout error occurred - * \return ERR_SLEEP_REQ : if Deselect is received and responded - * \return ERR_NOMEM : if the received INF does not fit into the - * receive buffer - * \return ERR_LINK_LOSS : if communication is lost because Reader/Writer - * has turned off its field - ***************************************************************************** - */ -ReturnCode rfalIsoDepGetApduTransceiveStatus(void); - -/*! - ***************************************************************************** - * \brief ISO-DEP Send RATS - * - * This sends a RATS to make a NFC-A Listen Device to enter - * ISO-DEP layer (ISO14443-4) and checks if the received ATS is valid - * - * \param[in] FSDI : Frame Size Device Integer to be used - * \param[in] DID : Device ID to be used or RFAL_ISODEP_NO_DID for not use DID - * \param[out] ats : pointer to place the ATS Response - * \param[out] atsLen : pointer to place the ATS length - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error - * \return ERR_PAR : Parity error detected - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error, ATS received - ***************************************************************************** - */ -ReturnCode rfalIsoDepRATS(rfalIsoDepFSxI FSDI, uint8_t DID, rfalIsoDepAts* ats, uint8_t* atsLen); - -/*! - ***************************************************************************** - * \brief ISO-DEP Send PPS - * - * This sends a PPS to make a NFC-A Listen Device change the communications - * bit rate from 106kbps to one of the supported bit rates - * Additionally checks if the received PPS response is valid - * - * \param[in] DID : Device ID - * \param[in] DSI : DSI code the divisor from Listener (PICC) to Poller (PCD) - * \param[in] DRI : DRI code the divisor from Poller (PCD) to Listener (PICC) - * \param[out] ppsRes : pointer to place the PPS Response - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error - * \return ERR_PAR : Parity error detected - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error, PPS Response received - ***************************************************************************** - */ -ReturnCode rfalIsoDepPPS(uint8_t DID, rfalBitRate DSI, rfalBitRate DRI, rfalIsoDepPpsRes* ppsRes); - -/*! - ***************************************************************************** - * \brief ISO-DEP Send ATTRIB - * - * This sends a ATTRIB to make a NFC-B Listen Device to enter - * ISO-DEP layer (ISO14443-4) and checks if the received ATTRIB Response is valid - * - * \param[in] nfcid0 : NFCID0 to be used for the ATTRIB - * \param[in] PARAM1 : ATTRIB PARAM1 byte (communication parameters) - * \param[in] DSI : DSI code the divisor from Listener (PICC) to Poller (PCD) - * \param[in] DRI : DRI code the divisor from Poller (PCD) to Listener (PICC) - * \param[in] FSDI : PCD's Frame Size to be announced on the ATTRIB - * \param[in] PARAM3 : ATTRIB PARAM1 byte (protocol type) - * \param[in] DID : Device ID to be used or RFAL_ISODEP_NO_DID for not use DID - * \param[in] HLInfo : pointer to Higher layer INF (NULL if none) - * \param[in] HLInfoLen : Length HLInfo - * \param[in] fwt : Frame Waiting Time to be used (from SENSB_RES) - * \param[out] attribRes : pointer to place the ATTRIB Response - * \param[out] attribResLen : pointer to place the ATTRIB Response length - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error, ATTRIB Response received - ***************************************************************************** - */ -ReturnCode rfalIsoDepATTRIB( - const uint8_t* nfcid0, - uint8_t PARAM1, - rfalBitRate DSI, - rfalBitRate DRI, - rfalIsoDepFSxI FSDI, - uint8_t PARAM3, - uint8_t DID, - const uint8_t* HLInfo, - uint8_t HLInfoLen, - uint32_t fwt, - rfalIsoDepAttribRes* attribRes, - uint8_t* attribResLen); - -/*! - ***************************************************************************** - * \brief Deselects PICC - * - * This function sends a deselect command to PICC and waits for it`s - * responce in a blocking way - * - * \return ERR_NONE : Deselect successfully sent and acknowledged by PICC - * \return ERR_TIMEOUT: No response rcvd from PICC - * - ***************************************************************************** - */ -ReturnCode rfalIsoDepDeselect(void); - -/*! - ***************************************************************************** - * \brief ISO-DEP Poller Handle NFC-A Activation - * - * This performs a NFC-A Activation into ISO-DEP layer (ISO14443-4) with the given - * parameters. It sends RATS and if the higher bit rates are supported by - * both devices it additionally sends PPS - * Once Activated all details of the device are provided on isoDepDev - * - * \param[in] FSDI : Frame Size Device Integer to be used - * \param[in] DID : Device ID to be used or RFAL_ISODEP_NO_DID for not use DID - * \param[in] maxBR : Max bit rate supported by the Poller - * \param[out] isoDepDev : ISO-DEP information of the activated Listen device - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error - * \return ERR_PAR : Parity error detected - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error, activation successful - ***************************************************************************** - */ -ReturnCode rfalIsoDepPollAHandleActivation( - rfalIsoDepFSxI FSDI, - uint8_t DID, - rfalBitRate maxBR, - rfalIsoDepDevice* isoDepDev); - -/*! - ***************************************************************************** - * \brief ISO-DEP Poller Handle NFC-B Activation - * - * This performs a NFC-B Activation into ISO-DEP layer (ISO14443-4) with the given - * parameters. It sends ATTRIB and calculates supported higher bit rates of both - * devices and performs activation. - * Once Activated all details of the device are provided on isoDepDev - * - * \param[in] FSDI : Frame Size Device Integer to be used - * \param[in] DID : Device ID to be used or RFAL_ISODEP_NO_DID for not use DID - * \param[in] maxBR : Max bit rate supported by the Poller - * \param[in] PARAM1 : ATTRIB PARAM1 byte (communication parameters) - * \param[in] nfcbDev : pointer to the NFC-B Device containing the SENSB_RES - * \param[in] HLInfo : pointer to Higher layer INF (NULL if none) - * \param[in] HLInfoLen : Length HLInfo - * \param[out] isoDepDev : ISO-DEP information of the activated Listen device - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error - * \return ERR_PAR : Parity error detected - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error, activation successful - ***************************************************************************** - */ -ReturnCode rfalIsoDepPollBHandleActivation( - rfalIsoDepFSxI FSDI, - uint8_t DID, - rfalBitRate maxBR, - uint8_t PARAM1, - const rfalNfcbListenDevice* nfcbDev, - const uint8_t* HLInfo, - uint8_t HLInfoLen, - rfalIsoDepDevice* isoDepDev); - -/*! - ***************************************************************************** - * \brief ISO-DEP Poller Handle S(Parameters) - * - * This checks if PICC supports S(PARAMETERS), retieves PICC's - * capabilities and sets the Bit Rate at the highest supported by both - * devices - * - * \param[out] isoDepDev : ISO-DEP information of the activated Listen device - * \param[in] maxTxBR : Maximum Tx bit rate supported by PCD - * \param[in] maxRxBR : Maximum Rx bit rate supported by PCD - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error, S(PARAMETERS) selection successful - ***************************************************************************** - */ -ReturnCode rfalIsoDepPollHandleSParameters( - rfalIsoDepDevice* isoDepDev, - rfalBitRate maxTxBR, - rfalBitRate maxRxBR); - -/*! - ***************************************************************************** - * \brief ISO-DEP Poller Start NFC-A Activation - * - * This starts a NFC-A Activation into ISO-DEP layer (ISO14443-4) with the given - * parameters. It sends RATS and if the higher bit rates are supported by - * both devices it additionally sends PPS - * Once Activated all details of the device are provided on isoDepDev - * - * - * \see rfalIsoDepPollAGetActivationStatus - * - * \param[in] FSDI : Frame Size Device Integer to be used - * \param[in] DID : Device ID to be used or RFAL_ISODEP_NO_DID for not use DID - * \param[in] maxBR : Max bit rate supported by the Poller - * \param[out] isoDepDev : ISO-DEP information of the activated Listen device - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error - * \return ERR_PAR : Parity error detected - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error, start of asynchronous operation successful - ***************************************************************************** - */ -ReturnCode rfalIsoDepPollAStartActivation( - rfalIsoDepFSxI FSDI, - uint8_t DID, - rfalBitRate maxBR, - rfalIsoDepDevice* isoDepDev); - -/*! - ***************************************************************************** - * \brief ISO-DEP Poller Get NFC-A Activation Status - * - * Returns the activation status started by rfalIsoDepPollAStartActivation - * - * \see rfalIsoDepPollAStartActivation - * - * \return ERR_BUSY : Operation is ongoing - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error - * \return ERR_PAR : Parity error detected - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error, activation successful - ***************************************************************************** - */ -ReturnCode rfalIsoDepPollAGetActivationStatus(void); - -/*! - ***************************************************************************** - * \brief ISO-DEP Poller Start NFC-B Activation - * - * This starts a NFC-B Activation into ISO-DEP layer (ISO14443-4) with the given - * parameters. It will send ATTRIB and calculate supported higher bit rates of both - * devices and perform activation. - * Once Activated all details of the device are provided on isoDepDev - * - * \see rfalIsoDepPollBGetActivationStatus - * - * \param[in] FSDI : Frame Size Device Integer to be used - * \param[in] DID : Device ID to be used or RFAL_ISODEP_NO_DID for not use DID - * \param[in] maxBR : Max bit rate supported by the Poller - * \param[in] PARAM1 : ATTRIB PARAM1 byte (communication parameters) - * \param[in] nfcbDev : pointer to the NFC-B Device containing the SENSB_RES - * \param[in] HLInfo : pointer to Higher layer INF (NULL if none) - * \param[in] HLInfoLen : Length HLInfo - * \param[out] isoDepDev : ISO-DEP information of the activated Listen device - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error - * \return ERR_PAR : Parity error detected - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error, start of asynchronous operation successful - ***************************************************************************** - */ -ReturnCode rfalIsoDepPollBStartActivation( - rfalIsoDepFSxI FSDI, - uint8_t DID, - rfalBitRate maxBR, - uint8_t PARAM1, - const rfalNfcbListenDevice* nfcbDev, - const uint8_t* HLInfo, - uint8_t HLInfoLen, - rfalIsoDepDevice* isoDepDev); - -/*! - ***************************************************************************** - * \brief ISO-DEP Poller Get NFC-B Activation Status - * - * Returns the activation status started by rfalIsoDepPollBStartActivation - * - * \see rfalIsoDepPollBStartActivation - * - * \return ERR_BUSY : Operation is ongoing - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error - * \return ERR_PAR : Parity error detected - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error, activation successful - ***************************************************************************** - */ -ReturnCode rfalIsoDepPollBGetActivationStatus(void); - -#endif /* RFAL_ISODEP_H_ */ - -/** - * @} - * - * @} - * - * @} - */ diff --git a/lib/ST25RFAL002/include/rfal_nfc.h b/lib/ST25RFAL002/include/rfal_nfc.h deleted file mode 100644 index 49cbe5f9cf8..00000000000 --- a/lib/ST25RFAL002/include/rfal_nfc.h +++ /dev/null @@ -1,425 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_nfc.h - * - * \brief RFAL NFC device - * - * This module provides the required features to behave as an NFC Poller - * or Listener device. It grants an easy to use interface for the following - * activities: Technology Detection, Collision Resolution, Activation, - * Data Exchange, and Deactivation - * - * This layer is influenced by (but not fully aligned with) the NFC Forum - * specifications, in particular: Activity 2.0 and NCI 2.0 - * - * - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-HL - * \brief RFAL Higher Layer - * @{ - * - * \addtogroup NFC - * \brief RFAL NFC Device - * @{ - * - */ - -#ifndef RFAL_NFC_H -#define RFAL_NFC_H - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ -#include "platform.h" -#include "st_errno.h" -#include "rfal_rf.h" -#include "rfal_nfca.h" -#include "rfal_nfcb.h" -#include "rfal_nfcf.h" -#include "rfal_nfcv.h" -#include "rfal_st25tb.h" -#include "rfal_nfcDep.h" -#include "rfal_isoDep.h" - -/* -****************************************************************************** -* GLOBAL DEFINES -****************************************************************************** -*/ - -#define RFAL_NFC_TECH_NONE 0x0000U /*!< No technology */ -#define RFAL_NFC_POLL_TECH_A 0x0001U /*!< NFC-A technology Flag */ -#define RFAL_NFC_POLL_TECH_B 0x0002U /*!< NFC-B technology Flag */ -#define RFAL_NFC_POLL_TECH_F 0x0004U /*!< NFC-F technology Flag */ -#define RFAL_NFC_POLL_TECH_V 0x0008U /*!< NFC-V technology Flag */ -#define RFAL_NFC_POLL_TECH_AP2P 0x0010U /*!< AP2P technology Flag */ -#define RFAL_NFC_POLL_TECH_ST25TB 0x0020U /*!< ST25TB technology Flag */ -#define RFAL_NFC_LISTEN_TECH_A 0x1000U /*!< NFC-V technology Flag */ -#define RFAL_NFC_LISTEN_TECH_B 0x2000U /*!< NFC-V technology Flag */ -#define RFAL_NFC_LISTEN_TECH_F 0x4000U /*!< NFC-V technology Flag */ -#define RFAL_NFC_LISTEN_TECH_AP2P 0x8000U /*!< NFC-V technology Flag */ - -/* -****************************************************************************** -* GLOBAL MACROS -****************************************************************************** -*/ - -/*! Checks if a device is currently activated */ -#define rfalNfcIsDevActivated(st) \ - (((st) >= RFAL_NFC_STATE_ACTIVATED) && ((st) < RFAL_NFC_STATE_DEACTIVATION)) - -/*! Checks if a device is in discovery */ -#define rfalNfcIsInDiscovery(st) \ - (((st) >= RFAL_NFC_STATE_START_DISCOVERY) && ((st) < RFAL_NFC_STATE_ACTIVATED)) - -/*! Checks if remote device is in Poll mode */ -#define rfalNfcIsRemDevPoller(tp) \ - (((tp) >= RFAL_NFC_POLL_TYPE_NFCA) && ((tp) <= RFAL_NFC_POLL_TYPE_AP2P)) - -/*! Checks if remote device is in Listen mode */ -#define rfalNfcIsRemDevListener(tp) \ - (((int16_t)(tp) >= (int16_t)RFAL_NFC_LISTEN_TYPE_NFCA) && ((tp) <= RFAL_NFC_LISTEN_TYPE_AP2P)) - -/* -****************************************************************************** -* GLOBAL ENUMS -****************************************************************************** -*/ - -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ - -/*! Main state */ -typedef enum { - RFAL_NFC_STATE_NOTINIT = 0, /*!< Not Initialized state */ - RFAL_NFC_STATE_IDLE = 1, /*!< Initialize state */ - RFAL_NFC_STATE_START_DISCOVERY = 2, /*!< Start Discovery loop state */ - RFAL_NFC_STATE_WAKEUP_MODE = 3, /*!< Wake-Up state */ - RFAL_NFC_STATE_POLL_TECHDETECT = 10, /*!< Technology Detection state */ - RFAL_NFC_STATE_POLL_COLAVOIDANCE = 11, /*!< Collision Avoidance state */ - RFAL_NFC_STATE_POLL_SELECT = 12, /*!< Wait for Selection state */ - RFAL_NFC_STATE_POLL_ACTIVATION = 13, /*!< Activation state */ - RFAL_NFC_STATE_LISTEN_TECHDETECT = 20, /*!< Listen Tech Detect */ - RFAL_NFC_STATE_LISTEN_COLAVOIDANCE = 21, /*!< Listen Collision Avoidance */ - RFAL_NFC_STATE_LISTEN_ACTIVATION = 22, /*!< Listen Activation state */ - RFAL_NFC_STATE_LISTEN_SLEEP = 23, /*!< Listen Sleep state */ - RFAL_NFC_STATE_ACTIVATED = 30, /*!< Activated state */ - RFAL_NFC_STATE_DATAEXCHANGE = 31, /*!< Data Exchange Start state */ - RFAL_NFC_STATE_DATAEXCHANGE_DONE = 33, /*!< Data Exchange terminated */ - RFAL_NFC_STATE_DEACTIVATION = 34 /*!< Deactivation state */ -} rfalNfcState; - -/*! Device type */ -typedef enum { - RFAL_NFC_LISTEN_TYPE_NFCA = 0, /*!< NFC-A Listener device type */ - RFAL_NFC_LISTEN_TYPE_NFCB = 1, /*!< NFC-B Listener device type */ - RFAL_NFC_LISTEN_TYPE_NFCF = 2, /*!< NFC-F Listener device type */ - RFAL_NFC_LISTEN_TYPE_NFCV = 3, /*!< NFC-V Listener device type */ - RFAL_NFC_LISTEN_TYPE_ST25TB = 4, /*!< ST25TB Listener device type */ - RFAL_NFC_LISTEN_TYPE_AP2P = 5, /*!< AP2P Listener device type */ - RFAL_NFC_POLL_TYPE_NFCA = 10, /*!< NFC-A Poller device type */ - RFAL_NFC_POLL_TYPE_NFCB = 11, /*!< NFC-B Poller device type */ - RFAL_NFC_POLL_TYPE_NFCF = 12, /*!< NFC-F Poller device type */ - RFAL_NFC_POLL_TYPE_NFCV = 13, /*!< NFC-V Poller device type */ - RFAL_NFC_POLL_TYPE_AP2P = 15 /*!< AP2P Poller device type */ -} rfalNfcDevType; - -/*! Device interface */ -typedef enum { - RFAL_NFC_INTERFACE_RF = 0, /*!< RF Frame interface */ - RFAL_NFC_INTERFACE_ISODEP = 1, /*!< ISO-DEP interface */ - RFAL_NFC_INTERFACE_NFCDEP = 2 /*!< NFC-DEP interface */ -} rfalNfcRfInterface; - -/*! Device struct containing all its details */ -typedef struct { - rfalNfcDevType type; /*!< Device's type */ - union { /* PRQA S 0750 # MISRA 19.2 - Members of the union will not be used concurrently, only one technology at a time */ - rfalNfcaListenDevice nfca; /*!< NFC-A Listen Device instance */ - rfalNfcbListenDevice nfcb; /*!< NFC-B Listen Device instance */ - rfalNfcfListenDevice nfcf; /*!< NFC-F Listen Device instance */ - rfalNfcvListenDevice nfcv; /*!< NFC-V Listen Device instance */ - rfalSt25tbListenDevice st25tb; /*!< ST25TB Listen Device instance*/ - } dev; /*!< Device's instance */ - - uint8_t* nfcid; /*!< Device's NFCID */ - uint8_t nfcidLen; /*!< Device's NFCID length */ - rfalNfcRfInterface rfInterface; /*!< Device's interface */ - - union { /* PRQA S 0750 # MISRA 19.2 - Members of the union will not be used concurrently, only one protocol at a time */ - rfalIsoDepDevice isoDep; /*!< ISO-DEP instance */ - rfalNfcDepDevice nfcDep; /*!< NFC-DEP instance */ - } proto; /*!< Device's protocol */ -} rfalNfcDevice; - -/*! Discovery parameters */ -typedef struct { - rfalComplianceMode compMode; /*!< Compliancy mode to be used */ - uint16_t techs2Find; /*!< Technologies to search for */ - uint16_t totalDuration; /*!< Duration of a whole Poll + Listen cycle */ - uint8_t devLimit; /*!< Max number of devices */ - rfalBitRate maxBR; /*!< Max Bit rate to be used for communications */ - - rfalBitRate nfcfBR; /*!< Bit rate to poll for NFC-F */ - uint8_t - nfcid3[RFAL_NFCDEP_NFCID3_LEN]; /*!< NFCID3 to be used on the ATR_REQ/ATR_RES */ - uint8_t GB[RFAL_NFCDEP_GB_MAX_LEN]; /*!< General bytes to be used on the ATR-REQ */ - uint8_t GBLen; /*!< Length of the General Bytes */ - rfalBitRate ap2pBR; /*!< Bit rate to poll for AP2P */ - - rfalLmConfPA lmConfigPA; /*!< Configuration for Passive Listen mode NFC-A */ - rfalLmConfPF lmConfigPF; /*!< Configuration for Passive Listen mode NFC-A */ - - void (*notifyCb)(rfalNfcState st); /*!< Callback to Notify upper layer */ - - bool wakeupEnabled; /*!< Enable Wake-Up mode before polling */ - bool wakeupConfigDefault; /*!< Wake-Up mode default configuration */ - rfalWakeUpConfig wakeupConfig; /*!< Wake-Up mode configuration */ - - bool activate_after_sak; // Set device to Active mode after SAK responce -} rfalNfcDiscoverParam; - -/*! Buffer union, only one interface is used at a time */ -typedef union { /* PRQA S 0750 # MISRA 19.2 - Members of the union will not be used concurrently, only one interface at a time */ - uint8_t rfBuf[RFAL_FEATURE_NFC_RF_BUF_LEN]; /*!< RF buffer */ - rfalIsoDepApduBufFormat isoDepBuf; /*!< ISO-DEP buffer format (with header/prologue) */ - rfalNfcDepPduBufFormat nfcDepBuf; /*!< NFC-DEP buffer format (with header/prologue) */ -} rfalNfcBuffer; - -/*******************************************************************************/ - -/* -****************************************************************************** -* GLOBAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -/*! - ***************************************************************************** - * \brief RFAL NFC Worker - * - * It runs the internal state machine and runs the RFAL RF worker. - ***************************************************************************** - */ -void rfalNfcWorker(void); - -/*! - ***************************************************************************** - * \brief RFAL NFC Initialize - * - * It initializes this module and its dependencies - * - * \return ERR_WRONG_STATE : Incorrect state for this operation - * \return ERR_IO : Generic internal error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcInitialize(void); - -/*! - ***************************************************************************** - * \brief RFAL NFC Discovery - * - * It set the device in Discovery state. - * In discovery it will Poll and/or Listen for the technologies configured, - * and perform Wake-up mode if configured to do so. - * - * The device list passed on disParams must not be empty. - * The number of devices on the list is indicated by the devLimit and shall - * be at >= 1. - * - * \param[in] disParams : discovery configuration parameters - * - * \return ERR_WRONG_STATE : Incorrect state for this operation - * \return ERR_PARAM : Invalid parameters - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcDiscover(const rfalNfcDiscoverParam* disParams); - -/*! - ***************************************************************************** - * \brief RFAL NFC Get State - * - * It returns the current state - * - * \return rfalNfcState : the current state - ***************************************************************************** - */ -rfalNfcState rfalNfcGetState(void); - -/*! - ***************************************************************************** - * \brief RFAL NFC Get Devices Found - * - * It returns the location of the device list and the number of - * devices found. - * - * \param[out] devList : device list location - * \param[out] devCnt : number of devices found - * - * \return ERR_WRONG_STATE : Incorrect state for this operation - * Discovery still ongoing - * \return ERR_PARAM : Invalid parameters - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcGetDevicesFound(rfalNfcDevice** devList, uint8_t* devCnt); - -/*! - ***************************************************************************** - * \brief RFAL NFC Get Active Device - * - * It returns the location of the device current Active device - * - * \param[out] dev : device info location - * - * \return ERR_WRONG_STATE : Incorrect state for this operation - * No device activated - * \return ERR_PARAM : Invalid parameters - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcGetActiveDevice(rfalNfcDevice** dev); - -/*! - ***************************************************************************** - * \brief RFAL NFC Select Device - * - * It selects the device to be activated. - * It shall be called when more than one device has been identified to - * indiacte which device shall be actived - * - * \param[in] devIdx : device index to be activated - * - * \return ERR_WRONG_STATE : Incorrect state for this operation - * Not in select state - * \return ERR_PARAM : Invalid parameters - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcSelect(uint8_t devIdx); - -/*! - ***************************************************************************** - * \brief RFAL NFC Start Data Exchange - * - * After a device has been activated, it starts a data exchange. - * It handles automatically which interface/protocol to be used and acts accordingly. - * - * In Listen mode the first frame/data shall be sent by the Reader/Initiator - * therefore this method must be called first with txDataLen set to zero - * to retrieve the rxData and rcvLen locations. - * - * - * \param[in] txData : data to be transmitted - * \param[in] txDataLen : size of the data to be transmitted - * \param[out] rxData : location of the received data after operation is completed - * \param[out] rvdLen : location of thelength of the received data - * \param[in] fwt : FWT to be used in case of RF interface. - * If ISO-DEP or NFC-DEP interface is used, this will be ignored - * - * \return ERR_WRONG_STATE : Incorrect state for this operation - * \return ERR_PARAM : Invalid parameters - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcDataExchangeStart( - uint8_t* txData, - uint16_t txDataLen, - uint8_t** rxData, - uint16_t** rvdLen, - uint32_t fwt, - uint32_t tx_flag); - -ReturnCode rfalNfcDataExchangeCustomStart( - uint8_t* txData, - uint16_t txDataLen, - uint8_t** rxData, - uint16_t** rvdLen, - uint32_t fwt, - uint32_t flags); - -/*! - ***************************************************************************** - * \brief RFAL NFC Get Data Exchange Status - * - * Gets current Data Exchange status - * - * \return ERR_NONE : Transceive done with no error - * \return ERR_BUSY : Transceive ongoing - * \return ERR_AGAIN : received one chaining block, copy received data - * and continue to call this method to retrieve the - * remaining blocks - * \return ERR_XXXX : Error occurred - * \return ERR_TIMEOUT : No response - * \return ERR_FRAMING : Framing error detected - * \return ERR_PAR : Parity error detected - * \return ERR_CRC : CRC error detected - * \return ERR_LINK_LOSS : Link Loss - External Field is Off - * \return ERR_RF_COLLISION : Collision detected - * \return ERR_IO : Internal error - ***************************************************************************** - */ -ReturnCode rfalNfcDataExchangeGetStatus(void); - -/*! - ***************************************************************************** - * \brief RFAL NFC Deactivate - * - * It triggers the deactivation procedure to terminate communications with - * remote device. At the end the field will be turned off. - * - * \param[in] discovery : TRUE if after deactivation go back into discovery - * : FALSE if after deactivation remain in idle - * - * \return ERR_WRONG_STATE : Incorrect state for this operation - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcDeactivate(bool discovery); - -#endif /* RFAL_NFC_H */ - -/** - * @} - * - * @} - * - * @} - */ diff --git a/lib/ST25RFAL002/include/rfal_nfcDep.h b/lib/ST25RFAL002/include/rfal_nfcDep.h deleted file mode 100644 index 8b33c6cc216..00000000000 --- a/lib/ST25RFAL002/include/rfal_nfcDep.h +++ /dev/null @@ -1,830 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_nfcDep.h - * - * \author Gustavo Patricio - * - * \brief Implementation of NFC-DEP protocol - * - * NFC-DEP is also known as NFCIP - Near Field Communication - * Interface and Protocol - * - * This implementation was based on the following specs: - * - NFC Forum Digital 1.1 - * - ECMA 340 3rd Edition 2013 - * - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-AL - * \brief RFAL Abstraction Layer - * @{ - * - * \addtogroup NFC-DEP - * \brief RFAL NFC-DEP Module - * @{ - */ - -#ifndef RFAL_NFCDEP_H_ -#define RFAL_NFCDEP_H_ - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "platform.h" -#include "st_errno.h" -#include "rfal_rf.h" - -/* - ****************************************************************************** - * ENABLE SWITCH - ****************************************************************************** - */ - -#ifndef RFAL_FEATURE_NFC_DEP -#define RFAL_FEATURE_NFC_DEP \ - false /*!< NFC-DEP module configuration missing. Disabled by default */ -#endif - -/* If module is disabled remove the need for the user to set lengths */ -#if !RFAL_FEATURE_NFC_DEP -#undef RFAL_FEATURE_NFC_DEP_BLOCK_MAX_LEN -#undef RFAL_FEATURE_NFC_DEP_PDU_MAX_LEN - -#define RFAL_FEATURE_NFC_DEP_BLOCK_MAX_LEN 1U /*!< NFC-DEP Block/Payload length, set to "none" */ -#define RFAL_FEATURE_NFC_DEP_PDU_MAX_LEN 1U /*!< NFC-DEP PDU length, set to "none" */ -#endif /* !RFAL_FEATURE_NFC_DEP */ - -/* - ****************************************************************************** - * DEFINES - ****************************************************************************** - */ -#define RFAL_NFCDEP_FRAME_SIZE_MAX_LEN \ - 254U /*!< Maximum Frame Size Digital 2.0 Table 90 */ -#define RFAL_NFCDEP_DEPREQ_HEADER_LEN \ - 5U /*!< DEP_REQ header length: CMD_TYPE + CMD_CMD + PBF + DID + NAD */ - -/*! Length NFCIP DEP REQ or RES header (incl LEN) */ -#define RFAL_NFCDEP_DEP_HEADER \ - (RFAL_NFCDEP_LEN_LEN + RFAL_NFCDEP_CMDTYPE_LEN + RFAL_NFCDEP_CMD_LEN + RFAL_NFCDEP_DEP_PFB_LEN) -#define RFAL_NFCDEP_HEADER \ - (RFAL_NFCDEP_CMDTYPE_LEN + RFAL_NFCDEP_CMD_LEN) /*!< NFCIP header length */ -#define RFAL_NFCDEP_SB_LEN \ - 1U /*!< SB length on NFCIP fram for NFC-A */ -#define RFAL_NFCDEP_LEN_LEN \ - 1U /*!< LEN length on NFCIP frame */ -#define RFAL_NFCDEP_CMDTYPE_LEN \ - 1U /*!< Length of the cmd type (REQ | RES) on NFCIP frame */ -#define RFAL_NFCDEP_CMD_LEN \ - 1U /*!< Length of the cmd on NFCIP frame */ -#define RFAL_NFCDEP_DID_LEN \ - 1U /*!< Length of did on NFCIP frame */ -#define RFAL_NFCDEP_DEP_PFB_LEN \ - 1U /*!< Length of the PFB field on NFCIP frame */ - -#define RFAL_NFCDEP_DSL_RLS_LEN_NO_DID \ - (RFAL_NFCDEP_LEN_LEN + RFAL_NFCDEP_CMDTYPE_LEN + \ - RFAL_NFCDEP_CMD_LEN) /*!< Length of DSL_REQ and RLS_REQ with no DID */ -#define RFAL_NFCDEP_DSL_RLS_LEN_DID \ - (RFAL_NFCDEP_DSL_RLS_LEN_NO_DID + \ - RFAL_NFCDEP_DID_LEN) /*!< Length of DSL_REQ and RLS_REQ with DID */ - -#define RFAL_NFCDEP_FS_VAL_MIN \ - 64U /*!< Minimum LR value */ -#define RFAL_NFCDEP_LR_VAL_MASK \ - 0x03U /*!< Bit mask for a LR value */ -#define RFAL_NFCDEP_PP_LR_MASK \ - 0x30U /*!< Bit mask for LR value in PP byte on a ATR REQ/RES */ -#define RFAL_NFCDEP_PP_LR_SHIFT \ - 4U /*!< Position of LR value in PP byte on a ATR REQ/RES */ - -#define RFAL_NFCDEP_DID_MAX \ - 14U /*!< Max DID value Digital 14.6.2.3 */ -#define RFAL_NFCDEP_DID_KEEP \ - 0xFFU /*!< Keep DID value already configured */ -#define RFAL_NFCDEP_DID_NO \ - 0x00U /*!< No DID shall be used */ -#define RFAL_NFCDEP_NAD_NO \ - 0x00U /*!< No NAD shall be used */ - -#define RFAL_NFCDEP_OPER_RTOX_REQ_DIS \ - 0x01U /*!< Operation config: RTOX REQ disable */ -#define RFAL_NFCDEP_OPER_RTOX_REQ_EN \ - 0x00U /*!< Operation config: RTOX REQ enable */ - -#define RFAL_NFCDEP_OPER_ATN_DIS \ - 0x00U /*!< Operation config: ATN disable */ -#define RFAL_NFCDEP_OPER_ATN_EN \ - 0x02U /*!< Operation config: ATN enable */ - -#define RFAL_NFCDEP_OPER_EMPTY_DEP_DIS \ - 0x04U /*!< Operation config: empty DEPs disable */ -#define RFAL_NFCDEP_OPER_EMPTY_DEP_EN \ - 0x00U /*!< Operation config: empty DEPs enable */ - -#define RFAL_NFCDEP_OPER_FULL_MI_DIS \ - 0x00U /*!< Operation config: full chaining DEPs disable */ -#define RFAL_NFCDEP_OPER_FULL_MI_EN \ - 0x08U /*!< Operation config: full chaining DEPs enable */ - -#define RFAL_NFCDEP_BRS_MAINTAIN \ - 0xC0U /*!< Value signalling that BR is to be maintained (no PSL) */ -#define RFAL_NFCDEP_BRS_Dx_MASK \ - 0x07U /*!< Value signalling that BR is to be maintained (no PSL) */ -#define RFAL_NFCDEP_BRS_DSI_POS \ - 3U /*!< Value signalling that BR is to be maintained (no PSL) */ - -#define RFAL_NFCDEP_WT_DELTA \ - (16U - RFAL_NFCDEP_WT_DELTA_ADJUST) /*!< NFC-DEP dWRT (adjusted) Digital 2.0 B.10 */ -#define RFAL_NFCDEP_WT_DELTA_ADJUST \ - 4U /*!< dWRT value adjustment */ - -#define RFAL_NFCDEP_ATR_REQ_NFCID3_POS \ - 2U /*!< NFCID3 offset in ATR_REQ frame */ -#define RFAL_NFCDEP_NFCID3_LEN \ - 10U /*!< NFCID3 Length */ - -#define RFAL_NFCDEP_LEN_MIN \ - 3U /*!< Minimum length byte LEN value */ -#define RFAL_NFCDEP_LEN_MAX \ - 255U /*!< Maximum length byte LEN value */ - -#define RFAL_NFCDEP_ATRRES_HEADER_LEN \ - 2U /*!< ATR RES Header Len: CmdType: 0xD5 + Cod: 0x01 */ -#define RFAL_NFCDEP_ATRRES_MIN_LEN \ - 17U /*!< Minimum length for an ATR RES */ -#define RFAL_NFCDEP_ATRRES_MAX_LEN \ - 64U /*!< Maximum length for an ATR RES Digital 1.0 14.6.1 */ -#define RFAL_NFCDEP_ATRREQ_MIN_LEN \ - 16U /*!< Minimum length for an ATR REQ */ -#define RFAL_NFCDEP_ATRREQ_MAX_LEN \ - RFAL_NFCDEP_ATRRES_MAX_LEN /*!< Maximum length for an ATR REQ Digital 1.0 14.6.1 */ - -#define RFAL_NFCDEP_GB_MAX_LEN \ - (RFAL_NFCDEP_ATRREQ_MAX_LEN - \ - RFAL_NFCDEP_ATRREQ_MIN_LEN) /*!< Maximum length the General Bytes on ATR Digital 1.1 16.6.3 */ - -#define RFAL_NFCDEP_WT_INI_DEFAULT \ - RFAL_NFCDEP_WT_INI_MAX /*!< WT Initiator default value Digital 1.0 14.6.3.8 */ -#define RFAL_NFCDEP_WT_INI_MIN 0U /*!< WT Initiator minimum value Digital 1.0 14.6.3.8 */ -#define RFAL_NFCDEP_WT_INI_MAX 14U /*!< WT Initiator maximum value Digital 1.0 14.6.3.8 A.10 */ -#define RFAL_NFCDEP_RWT_INI_MAX \ - rfalNfcDepWT2RWT(RFAL_NFCDEP_WT_INI_MAX) /*!< RWT Initiator maximum value */ - -#define RFAL_NFCDEP_WT_TRG_MAX_D10 8U /*!< WT target max Digital 1.0 14.6.3.8 A.10 */ -#define RFAL_NFCDEP_WT_TRG_MAX_D11 14U /*!< WT target max Digital 1.1 16.6.3.9 A.9 */ -#define RFAL_NFCDEP_WT_TRG_MAX_L13 10U /*!< WT target max [LLCP] 1.3 6.2.1 */ -#define RFAL_NFCDEP_WT_TRG_MAX \ - RFAL_NFCDEP_WT_TRG_MAX_D11 /*!< WT target max Digital x.x | LLCP x.x */ -#define RFAL_NFCDEP_RWT_TRG_MAX \ - rfalNfcDepWT2RWT(RFAL_NFCDEP_WT_TRG_MAX) /*!< RWT Initiator maximum value */ - -/*! Maximum Frame Waiting Time = ((256 * 16/fc)*2^FWImax) = ((256*16/fc)*2^14) = (1048576 / 64)/fc = (100000h*64)/fc */ -#define RFAL_NFCDEP_MAX_FWT ((uint32_t)1U << 20) - -#define RFAL_NFCDEP_WT_MASK \ - 0x0FU /*!< Bit mask for the Wait Time value */ - -#define RFAL_NFCDEP_BR_MASK_106 \ - 0x01U /*!< Enable mask bit rate 106 */ -#define RFAL_NFCDEP_BR_MASK_212 \ - 0x02U /*!< Enable mask bit rate 242 */ -#define RFAL_NFCDEP_BR_MASK_424 \ - 0x04U /*!< Enable mask bit rate 424 */ - -/* - ****************************************************************************** - * GLOBAL MACROS - ****************************************************************************** - */ - -#define rfalNfcDepWT2RWT(wt) \ - ((uint32_t)1U \ - << (((uint32_t)(wt)&RFAL_NFCDEP_WT_MASK) + \ - 12U)) /*!< Converts WT value to RWT (1/fc) */ - -/*! Returns the BRS value from the given bit rate */ -#define rfalNfcDepDx2BRS(br) \ - ((((uint8_t)(br)&RFAL_NFCDEP_BRS_Dx_MASK) << RFAL_NFCDEP_BRS_DSI_POS) | \ - ((uint8_t)(br)&RFAL_NFCDEP_BRS_Dx_MASK)) - -#define rfalNfcDepBRS2DRI(brs) \ - (uint8_t)(( \ - uint8_t)(brs)&RFAL_NFCDEP_BRS_Dx_MASK) /*!< Returns the DRI value from the given BRS byte */ -#define rfalNfcDepBRS2DSI(brs) \ - (uint8_t)( \ - ((uint8_t)(brs) >> RFAL_NFCDEP_BRS_DSI_POS) & \ - RFAL_NFCDEP_BRS_Dx_MASK) /*!< Returns the DSI value from the given BRS byte */ - -#define rfalNfcDepPP2LR(PPx) \ - (((uint8_t)(PPx)&RFAL_NFCDEP_PP_LR_MASK) >> \ - RFAL_NFCDEP_PP_LR_SHIFT) /*!< Returns the LR value from the given PPx byte */ -#define rfalNfcDepLR2PP(LRx) \ - (((uint8_t)(LRx) << RFAL_NFCDEP_PP_LR_SHIFT) & \ - RFAL_NFCDEP_PP_LR_MASK) /*!< Returns the PP byte with the given LRx value */ - -/*! Returns the Frame size value from the given LRx value */ -#define rfalNfcDepLR2FS(LRx) \ - (uint16_t)( \ - MIN((RFAL_NFCDEP_FS_VAL_MIN * ((uint16_t)(LRx) + 1U)), RFAL_NFCDEP_FRAME_SIZE_MAX_LEN)) - -/*! - * Despite DIGITAL 1.0 14.6.2.1 stating that the last two bytes may filled with - * any value, some devices (Samsung Google Nexus) only accept when these are 0 */ -#define rfalNfcDepSetNFCID(dst, src, len) \ - ST_MEMSET((dst), 0x00, RFAL_NFCDEP_NFCID3_LEN); \ - if((len) > 0U) { \ - ST_MEMCPY((dst), (src), (len)); \ - } - -/* - ****************************************************************************** - * GLOBAL ENUMERATIONS - ****************************************************************************** - */ - -/*! Enumeration of NFC-DEP bit rate in ATR Digital 1.0 Table 93 and 94 */ -enum { - RFAL_NFCDEP_Bx_NO_HIGH_BR = 0x00, /*!< Peer supports no high bit rates */ - RFAL_NFCDEP_Bx_08_848 = 0x01, /*!< Peer also supports 848 */ - RFAL_NFCDEP_Bx_16_1695 = 0x02, /*!< Peer also supports 1695 */ - RFAL_NFCDEP_Bx_32_3390 = 0x04, /*!< Peer also supports 3390 */ - RFAL_NFCDEP_Bx_64_6780 = 0x08 /*!< Peer also supports 6780 */ -}; - -/*! Enumeration of NFC-DEP bit rate Dividor in PSL Digital 1.0 Table 100 */ -enum { - RFAL_NFCDEP_Dx_01_106 = RFAL_BR_106, /*!< Divisor D = 1 : bit rate = 106 */ - RFAL_NFCDEP_Dx_02_212 = RFAL_BR_212, /*!< Divisor D = 2 : bit rate = 212 */ - RFAL_NFCDEP_Dx_04_424 = RFAL_BR_424, /*!< Divisor D = 4 : bit rate = 424 */ - RFAL_NFCDEP_Dx_08_848 = RFAL_BR_848, /*!< Divisor D = 8 : bit rate = 848 */ - RFAL_NFCDEP_Dx_16_1695 = RFAL_BR_1695, /*!< Divisor D = 16 : bit rate = 1695 */ - RFAL_NFCDEP_Dx_32_3390 = RFAL_BR_3390, /*!< Divisor D = 32 : bit rate = 3390 */ - RFAL_NFCDEP_Dx_64_6780 = RFAL_BR_6780 /*!< Divisor D = 64 : bit rate = 6780 */ -}; - -/*! Enumeration of NFC-DEP Length Reduction (LR) Digital 1.0 Table 91 */ -enum { - RFAL_NFCDEP_LR_64 = 0x00, /*!< Maximum payload size is 64 bytes */ - RFAL_NFCDEP_LR_128 = 0x01, /*!< Maximum payload size is 128 bytes */ - RFAL_NFCDEP_LR_192 = 0x02, /*!< Maximum payload size is 192 bytes */ - RFAL_NFCDEP_LR_254 = 0x03 /*!< Maximum payload size is 254 bytes */ -}; - -/* - ****************************************************************************** - * GLOBAL DATA TYPES - ****************************************************************************** - */ - -/*! NFC-DEP callback to check if upper layer has deactivation pending */ -typedef bool (*rfalNfcDepDeactCallback)(void); - -/*! Enumeration of the nfcip communication modes */ -typedef enum { - RFAL_NFCDEP_COMM_PASSIVE, /*!< Passive communication mode */ - RFAL_NFCDEP_COMM_ACTIVE /*!< Active communication mode */ -} rfalNfcDepCommMode; - -/*! Enumeration of the nfcip roles */ -typedef enum { - RFAL_NFCDEP_ROLE_INITIATOR, /*!< Perform as Initiator */ - RFAL_NFCDEP_ROLE_TARGET /*!< Perform as Target */ -} rfalNfcDepRole; - -/*! Struct that holds all NFCIP configs */ -typedef struct { - rfalNfcDepRole role; /*!< Current NFCIP role */ - rfalNfcDepCommMode commMode; /*!< Current NFCIP communication mode */ - uint8_t oper; /*!< Operation config similar to NCI 1.0 Table 81 */ - - uint8_t did; /*!< Current Device ID (DID) */ - uint8_t nad; /*!< Current Node Addressing (NAD) */ - uint8_t bs; /*!< Bit rate in Sending Direction */ - uint8_t br; /*!< Bit rate in Receiving Direction */ - uint8_t nfcid[RFAL_NFCDEP_NFCID3_LEN]; /*!< Pointer to the NFCID to be used */ - uint8_t nfcidLen; /*!< Length of the given NFCID in nfcid */ - uint8_t gb[RFAL_NFCDEP_GB_MAX_LEN]; /*!< Pointer General Bytes (GB) to be used */ - uint8_t gbLen; /*!< Length of the given GB in gb */ - uint8_t lr; /*!< Length Reduction (LR) to be used */ - uint8_t to; /*!< Timeout (TO) to be used */ - uint32_t fwt; /*!< Frame Waiting Time (FWT) to be used */ - uint32_t dFwt; /*!< Delta Frame Waiting Time (dFWT) to be used */ -} rfalNfcDepConfigs; - -/*! ATR_REQ command Digital 1.1 16.6.2 */ -typedef struct { - uint8_t CMD1; /*!< Command format 0xD4 */ - uint8_t CMD2; /*!< Command Value */ - uint8_t NFCID3[RFAL_NFCDEP_NFCID3_LEN]; /*!< NFCID3 value */ - uint8_t DID; /*!< DID */ - uint8_t BSi; /*!< Sending Bitrate for Initiator */ - uint8_t BRi; /*!< Receiving Bitrate for Initiator */ - uint8_t PPi; /*!< Optional Parameters presence indicator */ - uint8_t GBi[RFAL_NFCDEP_GB_MAX_LEN]; /*!< General Bytes */ -} rfalNfcDepAtrReq; - -/*! ATR_RES response Digital 1.1 16.6.3 */ -typedef struct { - uint8_t CMD1; /*!< Response Byte 0xD5 */ - uint8_t CMD2; /*!< Command Value */ - uint8_t NFCID3[RFAL_NFCDEP_NFCID3_LEN]; /*!< NFCID3 value */ - uint8_t DID; /*!< DID */ - uint8_t BSt; /*!< Sending Bitrate for Initiator */ - uint8_t BRt; /*!< Receiving Bitrate for Initiator */ - uint8_t TO; /*!< Timeout */ - uint8_t PPt; /*!< Optional Parameters presence indicator */ - uint8_t GBt[RFAL_NFCDEP_GB_MAX_LEN]; /*!< General Bytes */ -} rfalNfcDepAtrRes; - -/*! Structure of transmit I-PDU Buffer format from caller */ -typedef struct { - uint8_t prologue[RFAL_NFCDEP_DEPREQ_HEADER_LEN]; /*!< Prologue space for NFC-DEP header*/ - uint8_t inf[RFAL_FEATURE_NFC_DEP_BLOCK_MAX_LEN]; /*!< INF | Data area of the buffer */ -} rfalNfcDepBufFormat; - -/*! Structure of APDU Buffer format from caller */ -typedef struct { - uint8_t prologue[RFAL_NFCDEP_DEPREQ_HEADER_LEN]; /*!< Prologue/SoD buffer */ - uint8_t pdu[RFAL_FEATURE_NFC_DEP_PDU_MAX_LEN]; /*!< Complete PDU/Payload buffer */ -} rfalNfcDepPduBufFormat; - -/*! Activation info as Initiator and Target */ -typedef union { /* PRQA S 0750 # MISRA 19.2 - Both members of the union will not be used concurrently , device is only initiatior or target a time. No problem can occur. */ - struct { - rfalNfcDepAtrRes ATR_RES; /*!< ATR RES (Initiator mode) */ - uint8_t ATR_RESLen; /*!< ATR RES length (Initiator mode) */ - } Target; /*!< Target */ - struct { - rfalNfcDepAtrReq ATR_REQ; /*!< ATR REQ (Target mode) */ - uint8_t ATR_REQLen; /*!< ATR REQ length (Target mode) */ - } Initiator; /*!< Initiator */ -} rfalNfcDepActivation; - -/*! NFC-DEP device Info */ -typedef struct { - uint8_t GBLen; /*!< General Bytes length */ - uint8_t WT; /*!< WT to be used (ignored in Listen Mode) */ - uint32_t FWT; /*!< FWT to be used (1/fc)(ignored Listen Mode) */ - uint32_t dFWT; /*!< Delta FWT to be used (1/fc) */ - uint8_t LR; /*!< Length Reduction coding the max payload */ - uint16_t FS; /*!< Frame Size */ - rfalBitRate DSI; /*!< Bit Rate coding from Initiator to Target */ - rfalBitRate DRI; /*!< Bit Rate coding from Target to Initiator */ - uint8_t DID; /*!< Device ID (RFAL_NFCDEP_DID_NO if no DID) */ - uint8_t NAD; /*!< Node ADdress (RFAL_NFCDEP_NAD_NO if no NAD)*/ -} rfalNfcDepInfo; - -/*! NFC-DEP Device structure */ -typedef struct { - rfalNfcDepActivation activation; /*!< Activation Info */ - rfalNfcDepInfo info; /*!< NFC-DEP device Info */ -} rfalNfcDepDevice; - -/*! NFCIP Protocol structure for P2P Target - * - * operParam : derives from NFC-Forum NCI NFC-DEP Operation Parameter - * NCI 1.1 Table 86: NFC-DEP Operation Parameter - * and it's a bit mask composed as: - * [ 0000b - * | Chain SHALL use max. Transport Data Byte[1b] - * | I-PDU with no Transport Data SHALL NOT be sent [1b] - * | NFC-DEP Target SHALL NOT send RTOX request [1b] - * ] - * - */ -typedef struct { - rfalNfcDepCommMode commMode; /*!< Initiator in Active P2P or Passive P2P*/ - uint8_t operParam; /*!< NFC-DEP Operation Parameter */ - uint8_t* nfcid; /*!< Initiator's NFCID2 or NFCID3 */ - uint8_t nfcidLen; /*!< Initiator's NFCID length (NFCID2/3) */ - uint8_t DID; /*!< Initiator's Device ID DID */ - uint8_t NAD; /*!< Initiator's Node ID NAD */ - uint8_t BS; /*!< Initiator's Bit Rates supported in Tx */ - uint8_t BR; /*!< Initiator's Bit Rates supported in Rx */ - uint8_t LR; /*!< Initiator's Length reduction */ - uint8_t* GB; /*!< Initiator's General Bytes (Gi) */ - uint8_t GBLen; /*!< Initiator's General Bytes length */ -} rfalNfcDepAtrParam; - -/*! Structure of parameters to be passed in for nfcDepListenStartActivation */ -typedef struct { - rfalNfcDepBufFormat* rxBuf; /*!< Receive Buffer struct reference */ - uint16_t* rxLen; /*!< Receive INF data length in bytes */ - bool* isRxChaining; /*!< Received data is not complete */ - rfalNfcDepDevice* nfcDepDev; /*!< NFC-DEP device info */ -} rfalNfcDepListenActvParam; - -/*! NFCIP Protocol structure for P2P Target - * - * operParam : derives from NFC-Forum NCI NFC-DEP Operation Parameter - * NCI 1.1 Table 86: NFC-DEP Operation Parameter - * and it's a bit mask composed as: - * [ 0000b - * | Chain SHALL use max. Transport Data Byte[1b] - * | I-PDU with no Transport Data SHALL NOT be sent [1b] - * | NFC-DEP Target SHALL NOT send RTOX request [1b] - * ] - * - */ -typedef struct { - rfalNfcDepCommMode commMode; /*!< Target in Active P2P or Passive P2P */ - uint8_t nfcid3[RFAL_NFCDEP_NFCID3_LEN]; /*!< Target's NFCID3 */ - uint8_t bst; /*!< Target's Bit Rates supported in Tx */ - uint8_t brt; /*!< Target's Bit Rates supported in Rx */ - uint8_t to; /*!< Target's timeout (TO) value */ - uint8_t ppt; /*!< Target's Presence optional Params(PPt)*/ - uint8_t GBt[RFAL_NFCDEP_GB_MAX_LEN]; /*!< Target's General Bytes (Gt) */ - uint8_t GBtLen; /*!< Target's General Bytes length */ - uint8_t operParam; /*!< NFC-DEP Operation Parameter */ -} rfalNfcDepTargetParam; - -/*! Structure of parameters to be passed in for nfcDepStartIpduTransceive */ -typedef struct { - rfalNfcDepBufFormat* txBuf; /*!< Transmit Buffer struct reference */ - uint16_t txBufLen; /*!< Transmit Buffer INF field length in bytes */ - bool isTxChaining; /*!< Transmit data is not complete */ - rfalNfcDepBufFormat* rxBuf; /*!< Receive Buffer struct reference */ - uint16_t* rxLen; /*!< Receive INF data length */ - bool* isRxChaining; /*!< Received data is not complete */ - uint32_t FWT; /*!< FWT to be used (ignored in Listen Mode) */ - uint32_t dFWT; /*!< Delta FWT to be used */ - uint16_t FSx; /*!< Other device Frame Size (FSD or FSC) */ - uint8_t DID; /*!< Device ID (RFAL_ISODEP_NO_DID if no DID) */ -} rfalNfcDepTxRxParam; - -/*! Structure of parameters used on NFC DEP PDU Transceive */ -typedef struct { - rfalNfcDepPduBufFormat* txBuf; /*!< Transmit Buffer struct reference */ - uint16_t txBufLen; /*!< Transmit Buffer INF field length in Bytes*/ - rfalNfcDepPduBufFormat* rxBuf; /*!< Receive Buffer struct reference in Bytes */ - uint16_t* rxLen; /*!< Received INF data length in Bytes */ - rfalNfcDepBufFormat* tmpBuf; /*!< Temp buffer for single PDUs (internal) */ - uint32_t FWT; /*!< FWT to be used (ignored in Listen Mode) */ - uint32_t dFWT; /*!< Delta FWT to be used */ - uint16_t FSx; /*!< Other device Frame Size (FSD or FSC) */ - uint8_t DID; /*!< Device ID (RFAL_ISODEP_NO_DID if no DID) */ -} rfalNfcDepPduTxRxParam; - -/* - * ***************************************************************************** - * GLOBAL VARIABLE DECLARATIONS - ****************************************************************************** - */ - -/* - ****************************************************************************** - * GLOBAL FUNCTION PROTOTYPES - ****************************************************************************** - */ - -/*! - ****************************************************************************** - * \brief NFCIP Initialize - * - * This method resets all NFC-DEP inner states, counters and context and sets - * default values - * - ****************************************************************************** - */ -void rfalNfcDepInitialize(void); - -/*! - ****************************************************************************** - * \brief Set deactivating callback - * - * Sets the deactivating callback so that nfcip layer can check if upper layer - * has a deactivation pending, and not perform error recovery upon specific - * errors - * - * \param[in] pFunc : method pointer to deactivation flag check - ****************************************************************************** - */ -void rfalNfcDepSetDeactivatingCallback(rfalNfcDepDeactCallback pFunc); - -/*! - ****************************************************************************** - * \brief Calculate Response Waiting Time - * - * Calculates the Response Waiting Time (RWT) from the given Waiting Time (WT) - * - * \param[in] wt : the WT value to calculate RWT - * - * \return RWT value in 1/fc - ****************************************************************************** - */ -uint32_t rfalNfcDepCalculateRWT(uint8_t wt); - -/*! - ****************************************************************************** - * \brief NFC-DEP Initiator ATR (Attribute Request) - * - * This method configures the NFC-DEP layer with given parameters and then - * sends an ATR to the Target with and checks for a valid response response - * - * \param[in] param : parameters to initialize and compose the ATR - * \param[out] atrRes : location to store the ATR_RES - * \param[out] atrResLen : length of the ATR_RES received - * - * \return ERR_NONE : No error - * \return ERR_TIMEOUT : Timeout occurred - * \return ERR_PROTO : Protocol error occurred - ****************************************************************************** - */ -ReturnCode - rfalNfcDepATR(const rfalNfcDepAtrParam* param, rfalNfcDepAtrRes* atrRes, uint8_t* atrResLen); - -/*! - ****************************************************************************** - * \brief NFC-DEP Initiator PSL (Parameter Selection) - * - * This method sends a PSL to the Target with the given parameters and checks - * for a valid response response - * - * The parameters must be coded according to Digital 1.1 16.7.1 - * - * \param[in] BRS : the selected Bit Rates for Initiator and Target - * \param[in] FSL : the maximum length of Commands and Responses - * - * \return ERR_NONE : No error - * \return ERR_TIMEOUT : Timeout occurred - * \return ERR_PROTO : Protocol error occurred - ****************************************************************************** - */ -ReturnCode rfalNfcDepPSL(uint8_t BRS, uint8_t FSL); - -/*! - ****************************************************************************** - * \brief NFC-DEP Initiator DSL (Deselect) - * - * This method checks if the NFCIP module is configured as initiator and if - * so sends a DSL REQ, waits the target's response and checks it - * - * In case of performing as target no action is taken - * - * \return ERR_NONE : No error - * \return ERR_TIMEOUT : Timeout occurred - * \return ERR_MAX_RERUNS : Timeout occurred - * \return ERR_PROTO : Protocol error occurred - ****************************************************************************** - */ -ReturnCode rfalNfcDepDSL(void); - -/*! - ****************************************************************************** - * \brief NFC-DEP Initiator RLS (Release) - * - * This method checks if the NFCIP module is configured as initiator and if - * so sends a RLS REQ, waits target's response and checks it - * - * In case of performing as target no action is taken - * - * \return ERR_NONE : No error - * \return ERR_TIMEOUT : Timeout occurred - * \return ERR_MAX_RERUNS : Timeout occurred - * \return ERR_PROTO : Protocol error occurred - ****************************************************************************** - */ -ReturnCode rfalNfcDepRLS(void); - -/*! - ***************************************************************************** - * \brief NFC-DEP Initiator Handle Activation - * - * This performs a Activation into NFC-DEP layer with the given - * parameters. It sends ATR_REQ and if the higher bit rates are supported by - * both devices it additionally sends PSL - * Once Activated all details of the device are provided on nfcDepDev - * - * \param[in] param : required parameters to initialize and send ATR_REQ - * \param[in] desiredBR : Desired bit rate supported by the Poller - * \param[out] nfcDepDev : NFC-DEP information of the activated Listen device - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error - * \return ERR_PAR : Parity error detected - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error, activation successful - ***************************************************************************** - */ -ReturnCode rfalNfcDepInitiatorHandleActivation( - rfalNfcDepAtrParam* param, - rfalBitRate desiredBR, - rfalNfcDepDevice* nfcDepDev); - -/*! - ****************************************************************************** - * \brief Check if buffer contains valid ATR_REQ - * - * This method checks if the given ATR_REQ is valid - * - * - * \param[in] buf : buffer holding Initiator's received request - * \param[in] bufLen : size of the msg contained on the buf in Bytes - * \param[out] nfcid3 : pointer to where the NFCID3 may be outputed, - * nfcid3 has NFCF_SENSF_NFCID3_LEN as length - * Pass NULL if output parameter not desired - * - * \return true : Valid ATR_REQ received, the ATR_RES has been computed in txBuf - * \return false : Invalid protocol request - * - ****************************************************************************** - */ -bool rfalNfcDepIsAtrReq(const uint8_t* buf, uint16_t bufLen, uint8_t* nfcid3); - -/*! - ****************************************************************************** - * \brief Check is Target has received ATR - * - * This method checks if the NFCIP module is configured as target and if a - * ATR REQ has been received ( whether is in activation or in data exchange) - * - * \return true : a ATR has already been received - * \return false : no ATR has been received - ****************************************************************************** - */ -bool rfalNfcDepTargetRcvdATR(void); - -/*! - ***************************************************************************** - * \brief NFCDEP Start Listen Activation Handling - * - * Start Activation Handling and setup to receive first frame which may - * contain complete or partial DEP-REQ after activation is completed - * - * Pass in ATR_REQ for NFC-DEP to handle ATR_RES. The Activation Handling - * handles ATR_RES and PSL_RES if a PSL_REQ is received - * - * Activation is completed if PSL_RES is sent or if first I-PDU is received - * - * \ref rfalNfcDepListenGetActivationStatus() provide status of the - * ongoing activation - * - * \warning nfcDepGetTransceiveStatus() shall be called right after activation - * is completed (i.e. rfalNfcDepListenGetActivationStatus() return ERR_NONE) - * to check for first received frame. - * - * \param[in] param : Target parameters to be used - * \param[in] atrReq : reference to buffer containing ATR_REQ - * \param[in] atrReqLength: Length of ATR_REQ - * \param[out] rxParam : references to buffer, length and chaining indication - * for first complete LLCP to be received - * - * \return ERR_NONE : ATR_REQ is valid and activation ongoing - * \return ERR_PARAM : ATR_REQ or other params are invalid - * \return ERR_LINK_LOSS : Remote Field is turned off - ***************************************************************************** - */ -ReturnCode rfalNfcDepListenStartActivation( - const rfalNfcDepTargetParam* param, - const uint8_t* atrReq, - uint16_t atrReqLength, - rfalNfcDepListenActvParam rxParam); - -/*! - ***************************************************************************** - * \brief Get the current NFC-DEP Activation Status - * - * \return ERR_NONE : Activation has completed successfully - * \return ERR_BUSY : Activation is ongoing - * \return ERR_LINK_LOSS : Remote Field was turned off - ***************************************************************************** - */ -ReturnCode rfalNfcDepListenGetActivationStatus(void); - -/*! - ***************************************************************************** - * \brief Start Transceive - * - * Transceives a complete or partial DEP block - * - * The txBuf contains complete or partial of DEP to be transmitted. - * The Prologue field of the I-PDU is handled internally - * - * If the buffer contains partial LLCP and is not the last block, then - * isTxChaining must be set to true - * - * \param[in] param: reference parameters to be used for the Transceive - * - * \return ERR_PARAM : Bad request - * \return ERR_WRONG_STATE : The module is not in a proper state - * \return ERR_NONE : The Transceive request has been started - ***************************************************************************** - */ -ReturnCode rfalNfcDepStartTransceive(const rfalNfcDepTxRxParam* param); - -/*! - ***************************************************************************** - * \brief Return the Transceive status - * - * Returns the status of the NFC-DEP Transceive - * - * \warning When the other device is performing chaining once a chained - * block is received the error ERR_AGAIN is sent. At this point - * caller must handle the received data immediately. - * When ERR_AGAIN is returned an ACK has already been sent to - * the other device and the next block might be incoming. - * If rfalWorker() is called frequently it will place the next - * block on the given buffer - * - * \return ERR_NONE : Transceive has been completed successfully - * \return ERR_BUSY : Transceive is ongoing - * \return ERR_PROTO : Protocol error occurred - * \return ERR_TIMEOUT : Timeout error occurred - * \return ERR_SLEEP_REQ : Deselect has been received and responded - * \return ERR_NOMEM : The received I-PDU does not fit into the - * receive buffer - * \return ERR_LINK_LOSS : Communication is lost because Reader/Writer - * has turned off its field - * \return ERR_AGAIN : received one chaining block, continue to call - * this method to retrieve the remaining blocks - ***************************************************************************** - */ -ReturnCode rfalNfcDepGetTransceiveStatus(void); - -/*! - ***************************************************************************** - * \brief Start PDU Transceive - * - * This method triggers a NFC-DEP Transceive containing a complete PDU - * It transmits the given message and handles all protocol retransmitions, - * error handling and control messages - * - * The txBuf contains a complete PDU to be transmitted - * The Prologue field will be manipulated by the Transceive - * - * \warning the txBuf will be modified during the transmission - * \warning the maximum RF frame which can be received is limited by param.tmpBuf - * - * \param[in] param: reference parameters to be used for the Transceive - * - * \return ERR_PARAM : Bad request - * \return ERR_WRONG_STATE : The module is not in a proper state - * \return ERR_NONE : The Transceive request has been started - ***************************************************************************** - */ -ReturnCode rfalNfcDepStartPduTransceive(rfalNfcDepPduTxRxParam param); - -/*! - ***************************************************************************** - * \brief Return the PSU Transceive status - * - * Returns the status of the NFC-DEP PDU Transceive - * - * - * \return ERR_NONE : Transceive has been completed successfully - * \return ERR_BUSY : Transceive is ongoing - * \return ERR_PROTO : Protocol error occurred - * \return ERR_TIMEOUT : Timeout error occurred - * \return ERR_SLEEP_REQ : Deselect has been received and responded - * \return ERR_NOMEM : The received I-PDU does not fit into the - * receive buffer - * \return ERR_LINK_LOSS : Communication is lost because Reader/Writer - * has turned off its field - ***************************************************************************** - */ -ReturnCode rfalNfcDepGetPduTransceiveStatus(void); - -#endif /* RFAL_NFCDEP_H_ */ - -/** - * @} - * - * @} - * - * @} - */ diff --git a/lib/ST25RFAL002/include/rfal_nfca.h b/lib/ST25RFAL002/include/rfal_nfca.h deleted file mode 100644 index 2a265b16e3d..00000000000 --- a/lib/ST25RFAL002/include/rfal_nfca.h +++ /dev/null @@ -1,497 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_nfca.h - * - * \author Gustavo Patricio - * - * \brief Provides several NFC-A convenience methods and definitions - * - * It provides a Poller (ISO14443A PCD) interface and as well as - * some NFC-A Listener (ISO14443A PICC) helpers. - * - * The definitions and helpers methods provided by this module are only - * up to ISO14443-3 layer - * - * - * An usage example is provided here: \ref exampleRfalNfca.c - * \example exampleRfalNfca.c - * - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-AL - * \brief RFAL Abstraction Layer - * @{ - * - * \addtogroup NFC-A - * \brief RFAL NFC-A Module - * @{ - * - */ - -#ifndef RFAL_NFCA_H -#define RFAL_NFCA_H - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "platform.h" -#include "st_errno.h" -#include "rfal_rf.h" -#include "rfal_t1t.h" - -/* - ****************************************************************************** - * GLOBAL DEFINES - ****************************************************************************** - */ - -#define RFAL_NFCA_CASCADE_1_UID_LEN \ - 4U /*!< UID length of cascade level 1 only tag */ -#define RFAL_NFCA_CASCADE_2_UID_LEN \ - 7U /*!< UID length of cascade level 2 only tag */ -#define RFAL_NFCA_CASCADE_3_UID_LEN \ - 10U /*!< UID length of cascade level 3 only tag */ - -#define RFAL_NFCA_SENS_RES_PLATFORM_MASK \ - 0x0FU /*!< SENS_RES (ATQA) platform configuration mask Digital 1.1 Table 10 */ -#define RFAL_NFCA_SENS_RES_PLATFORM_T1T \ - 0x0CU /*!< SENS_RES (ATQA) T1T platform configuration Digital 1.1 Table 10 */ - -#define RFAL_NFCA_SEL_RES_CONF_MASK \ - 0x60U /*!< SEL_RES (SAK) platform configuration mask Digital 1.1 Table 19 */ -#define RFAL_NFCA_SEL_RES_CONF_T2T \ - 0x00U /*!< SEL_RES (SAK) T2T configuration Digital 1.1 Table 19 */ -#define RFAL_NFCA_SEL_RES_CONF_T4T \ - 0x20U /*!< SEL_RES (SAK) T4T configuration Digital 1.1 Table 19 */ -#define RFAL_NFCA_SEL_RES_CONF_NFCDEP \ - 0x40U /*!< SEL_RES (SAK) NFC-DEP configuration Digital 1.1 Table 19 */ -#define RFAL_NFCA_SEL_RES_CONF_T4T_NFCDEP \ - 0x60U /*!< SEL_RES (SAK) T4T and NFC-DEP configuration Digital 1.1 Table 19 */ - -/*! NFC-A minimum FDT(listen) = ((n * 128 + (84)) / fc) with n_min = 9 Digital 1.1 6.10.1 - * = (1236)/fc - * Relax with 3etu: (3*128)/fc as with multiple NFC-A cards, response may take longer (JCOP cards) - * = (1236 + 384)/fc = 1620 / fc */ -#define RFAL_NFCA_FDTMIN 1620U -/* - ****************************************************************************** - * GLOBAL MACROS - ****************************************************************************** - */ - -/*! Checks if device is a T1T given its SENS_RES */ -#define rfalNfcaIsSensResT1T(sensRes) \ - ((((rfalNfcaSensRes*)(sensRes))->platformInfo & RFAL_NFCA_SENS_RES_PLATFORM_MASK) == \ - RFAL_NFCA_SENS_RES_PLATFORM_T1T) - -/*! Checks if device is a T2T given its SENS_RES */ -#define rfalNfcaIsSelResT2T(selRes) \ - ((((rfalNfcaSelRes*)(selRes))->sak & RFAL_NFCA_SEL_RES_CONF_MASK) == \ - RFAL_NFCA_SEL_RES_CONF_T2T) - -/*! Checks if device is a T4T given its SENS_RES */ -#define rfalNfcaIsSelResT4T(selRes) \ - ((((rfalNfcaSelRes*)(selRes))->sak & RFAL_NFCA_SEL_RES_CONF_MASK) == \ - RFAL_NFCA_SEL_RES_CONF_T4T) - -/*! Checks if device supports NFC-DEP protocol given its SENS_RES */ -#define rfalNfcaIsSelResNFCDEP(selRes) \ - ((((rfalNfcaSelRes*)(selRes))->sak & RFAL_NFCA_SEL_RES_CONF_MASK) == \ - RFAL_NFCA_SEL_RES_CONF_NFCDEP) - -/*! Checks if device supports ISO-DEP and NFC-DEP protocol given its SENS_RES */ -#define rfalNfcaIsSelResT4TNFCDEP(selRes) \ - ((((rfalNfcaSelRes*)(selRes))->sak & RFAL_NFCA_SEL_RES_CONF_MASK) == \ - RFAL_NFCA_SEL_RES_CONF_T4T_NFCDEP) - -/*! Checks if a NFC-A listener device supports multiple protocols (ISO-DEP and NFC-DEP) */ -#define rfalNfcaLisDevIsMultiProto(lisDev) \ - (((rfalNfcaListenDevice*)(lisDev))->type == RFAL_NFCA_T4T_NFCDEP) - -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ - -/*! NFC-A Listen device types */ -typedef enum { - RFAL_NFCA_T1T = - 0x01, /* Device configured for T1T Digital 1.1 Table 9 */ - RFAL_NFCA_T2T = - 0x00, /* Device configured for T2T Digital 1.1 Table 19 */ - RFAL_NFCA_T4T = - 0x20, /* Device configured for T4T Digital 1.1 Table 19 */ - RFAL_NFCA_NFCDEP = - 0x40, /* Device configured for NFC-DEP Digital 1.1 Table 19 */ - RFAL_NFCA_T4T_NFCDEP = - 0x60 /* Device configured for NFC-DEP and T4T Digital 1.1 Table 19 */ -} rfalNfcaListenDeviceType; - -/*! SENS_RES (ATQA) format Digital 1.1 6.6.3 & Table 7 */ -typedef struct { - uint8_t - anticollisionInfo; /*!< SENS_RES Anticollision Information */ - uint8_t - platformInfo; /*!< SENS_RES Platform Information */ -} rfalNfcaSensRes; - -/*! SDD_REQ (Anticollision) format Digital 1.1 6.7.1 & Table 11 */ -typedef struct { - uint8_t - selCmd; /*!< SDD_REQ SEL_CMD: cascade Level */ - uint8_t - selPar; /*!< SDD_REQ SEL_PAR: Byte Count[4b] | Bit Count[4b] (NVB: Number of Valid Bits)*/ -} rfalNfcaSddReq; - -/*! SDD_RES (UID CLn) format Digital 1.1 6.7.2 & Table 15 */ -typedef struct { - uint8_t nfcid1 - [RFAL_NFCA_CASCADE_1_UID_LEN]; /*!< NFCID1 cascade level NFCID */ - uint8_t bcc; /*!< BCC Exclusive-OR over first 4 bytes of SDD_RES */ -} rfalNfcaSddRes; - -/*! SEL_REQ (Select) format Digital 1.1 6.8.1 & Table 17 */ -typedef struct { - uint8_t - selCmd; /*!< SDD_REQ SEL_CMD: cascade Level */ - uint8_t - selPar; /*!< SDD_REQ SEL_PAR: Byte Count[4b] | Bit Count[4b] (NVB: Number of Valid Bits)*/ - uint8_t nfcid1 - [RFAL_NFCA_CASCADE_1_UID_LEN]; /*!< NFCID1 data */ - uint8_t bcc; /*!< Checksum calculated as exclusive-OR over the 4 bytes of NFCID1 CLn */ -} rfalNfcaSelReq; - -/*! SEL_RES (SAK) format Digital 1.1 6.8.2 & Table 19 */ -typedef struct { - uint8_t sak; /*!< Select Acknowledge */ -} rfalNfcaSelRes; - -/*! NFC-A listener device (PICC) struct */ -typedef struct { - rfalNfcaListenDeviceType - type; /*!< NFC-A Listen device type */ - rfalNfcaSensRes - sensRes; /*!< SENS_RES (ATQA) */ - rfalNfcaSelRes - selRes; /*!< SEL_RES (SAK) */ - uint8_t - nfcId1Len; /*!< NFCID1 Length */ - uint8_t nfcId1 - [RFAL_NFCA_CASCADE_3_UID_LEN]; /*!< NFCID1 (UID) */ -#ifdef RFAL_FEATURE_T1T - rfalT1TRidRes - ridRes; /*!< RID_RES */ -#endif /* RFAL_FEATURE_T1T */ - bool isSleep; /*!< Device sleeping flag */ -} rfalNfcaListenDevice; - -/* -****************************************************************************** -* GLOBAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -/*! - ***************************************************************************** - * \brief Initialize NFC-A Poller mode - * - * This methods configures RFAL RF layer to perform as a - * NFC-A Poller/RW (ISO14443A PCD) including all default timings and bit rate - * to 106 kbps - - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcaPollerInitialize(void); - -/*! - ***************************************************************************** - * \brief NFC-A Poller Check Presence - * - * This method checks if a NFC-A Listen device (PICC) is present on the field - * by sending an ALL_REQ (WUPA) or SENS_REQ (REQA) - * - * \param[in] cmd : Indicate if to send an ALL_REQ or a SENS_REQ - * \param[out] sensRes : If received, the SENS_RES - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_RF_COLLISION : Collision detected one or more device in the field - * \return ERR_PAR : Parity error detected, one or more device in the field - * \return ERR_CRC : CRC error detected, one or more device in the field - * \return ERR_FRAMING : Framing error detected, one or more device in the field - * \return ERR_PROTO : Protocol error detected, one or more device in the field - * \return ERR_TIMEOUT : Timeout error, no listener device detected - * \return ERR_NONE : No error, one or more device in the field - ***************************************************************************** - */ -ReturnCode rfalNfcaPollerCheckPresence(rfal14443AShortFrameCmd cmd, rfalNfcaSensRes* sensRes); - -/*! - ***************************************************************************** - * \brief NFC-A Poller Select - * - * This method selects a NFC-A Listener device (PICC) - * - * \param[in] nfcid1 : Listener device NFCID1 to be selected - * \param[in] nfcidLen : Length of the NFCID1 to be selected - * \param[out] selRes : pointer to place the SEL_RES - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error - * \return ERR_PAR : Parity error detected - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error, SEL_RES received - ***************************************************************************** - */ -ReturnCode rfalNfcaPollerSelect(const uint8_t* nfcid1, uint8_t nfcidLen, rfalNfcaSelRes* selRes); - -/*! - ***************************************************************************** - * \brief NFC-A Poller Sleep - * - * This method sends a SLP_REQ (HLTA) - * No response is expected afterwards Digital 1.1 6.9.2.1 - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcaPollerSleep(void); - -/*! - ***************************************************************************** - * \brief NFC-A Technology Detection - * - * This method performs NFC-A Technology Detection as defined in the spec - * given in the compliance mode - * - * \param[in] compMode : compliance mode to be performed - * \param[out] sensRes : location to store the SENS_RES, if received - * - * When compMode is set to ISO compliance a SLP_REQ (HLTA) is not sent - * after detection. When set to EMV a ALL_REQ (WUPA) is sent instead of - * a SENS_REQ (REQA) - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_NONE : No error, one or more device in the field - ***************************************************************************** - */ -ReturnCode - rfalNfcaPollerTechnologyDetection(rfalComplianceMode compMode, rfalNfcaSensRes* sensRes); - -/*! - ***************************************************************************** - * \brief NFC-A Poller Collision Resolution - * - * Collision resolution for one NFC-A Listener device/card (PICC) as - * defined in Activity 2.1 9.3.4 - * - * This method executes anti collision loop and select the device with higher NFCID1 - * - * When devLimit = 0 it is configured to perform collision detection only. Once a collision - * is detected the collision resolution is aborted immidiatly. If only one device is found - * with no collisions, it will properly resolved. - * - * \param[in] devLimit : device limit value (CON_DEVICES_LIMIT) - * \param[out] collPending : pointer to collision pending flag (INT_COLL_PEND) - * \param[out] selRes : location to store the last Select Response from listener device (PICC) - * \param[out] nfcId1 : location to store the NFCID1 (UID), ensure RFAL_NFCA_CASCADE_3_UID_LEN - * \param[out] nfcId1Len : pointer to length of NFCID1 (UID) - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_PROTO : Card length invalid - * \return ERR_IGNORE : conDevLimit is 0 and there is a collision - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcaPollerSingleCollisionResolution( - uint8_t devLimit, - bool* collPending, - rfalNfcaSelRes* selRes, - uint8_t* nfcId1, - uint8_t* nfcId1Len); - -/*! - ***************************************************************************** - * \brief NFC-A Poller Full Collision Resolution - * - * Performs a full Collision resolution as defined in Activity 2.1 9.3.4 - * - * \param[in] compMode : compliance mode to be performed - * \param[in] devLimit : device limit value, and size nfcaDevList - * \param[out] nfcaDevList : NFC-A listener device info - * \param[out] devCnt : Devices found counter - * - * When compMode is set to ISO compliance it assumes that the device is - * not sleeping and therefore no ALL_REQ (WUPA) is sent at the beginning. - * When compMode is set to NFC compliance an additional ALL_REQ (WUPA) is sent - * at the beginning. - * - * - * When devLimit = 0 it is configured to perform collision detection only. Once a collision - * is detected the collision resolution is aborted immidiatly. If only one device is found - * with no collisions, it will properly resolved. - * - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcaPollerFullCollisionResolution( - rfalComplianceMode compMode, - uint8_t devLimit, - rfalNfcaListenDevice* nfcaDevList, - uint8_t* devCnt); - -/*! - ***************************************************************************** - * \brief NFC-A Poller Full Collision Resolution with Sleep - * - * Performs a full Collision resolution similar to rfalNfcaPollerFullCollisionResolution - * but an additional SLP_REQ (HLTA) -> SENS_RES (REQA) is sent regardless if there - * was a collision. - * This proprietary behaviour ensures proper activation of certain devices that suffer - * from influence of Type B commands as foreseen in ISO14443-3 5.2.3 or were somehow - * not detected by the first round of collision resolution - * - * \param[in] devLimit : device limit value, and size nfcaDevList - * \param[out] nfcaDevList : NFC-A listener device info - * \param[out] devCnt : Devices found counter - * - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcaPollerSleepFullCollisionResolution( - uint8_t devLimit, - rfalNfcaListenDevice* nfcaDevList, - uint8_t* devCnt); - -/*! - ***************************************************************************** - * \brief NFC-A Poller Start Full Collision Resolution - * - * This method starts the full Collision resolution as defined - * in Activity 1.0 or 1.1 9.3.4 - * - * \param[in] compMode : compliance mode to be performed - * \param[in] devLimit : device limit value, and size nfcaDevList - * \param[out] nfcaDevList : NFC-A listener device info - * \param[out] devCnt : Devices found counter - * - * When compMode is set to ISO compliance it assumes that the device is - * not sleeping and therefore no ALL_REQ (WUPA) is sent at the beginning. - * When compMode is set to NFC compliance an additional ALL_REQ (WUPA) is sent at - * the beginning. - * - * - * When devLimit = 0 it is configured to perform collision detection only. Once a collision - * is detected the collision resolution is aborted immidiatly. If only one device is found - * with no collisions, it will properly resolved. - * - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcaPollerStartFullCollisionResolution( - rfalComplianceMode compMode, - uint8_t devLimit, - rfalNfcaListenDevice* nfcaDevList, - uint8_t* devCnt); - -/*! - ***************************************************************************** - * \brief NFC-A Get Full Collision Resolution Status - * - * Returns the Collision Resolution status - * - * \return ERR_BUSY : Operation is ongoing - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error - * \return ERR_PAR : Parity error detected - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error, activation successful - ***************************************************************************** - */ -ReturnCode rfalNfcaPollerGetFullCollisionResolutionStatus(void); - -/*! - ***************************************************************************** - * \brief NFC-A Listener is SLP_REQ - * - * Checks if the given buffer contains valid NFC-A SLP_REQ (HALT) - * - * \param[in] buf: buffer containing data - * \param[in] bufLen: length of the data in buffer to be checked - * - * \return true if data in buf contains a SLP_REQ ; false otherwise - ***************************************************************************** - */ -bool rfalNfcaListenerIsSleepReq(const uint8_t* buf, uint16_t bufLen); - -#endif /* RFAL_NFCA_H */ - -/** - * @} - * - * @} - * - * @} - */ diff --git a/lib/ST25RFAL002/include/rfal_nfcb.h b/lib/ST25RFAL002/include/rfal_nfcb.h deleted file mode 100644 index 67bcb327135..00000000000 --- a/lib/ST25RFAL002/include/rfal_nfcb.h +++ /dev/null @@ -1,425 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_nfcb.h - * - * \author Gustavo Patricio - * - * \brief Implementation of NFC-B (ISO14443B) helpers - * - * It provides a NFC-B Poller (ISO14443B PCD) interface and - * also provides some NFC-B Listener (ISO14443B PICC) helpers - * - * The definitions and helpers methods provided by this module are only - * up to ISO14443-3 layer (excluding ATTRIB) - * - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-AL - * \brief RFAL Abstraction Layer - * @{ - * - * \addtogroup NFC-B - * \brief RFAL NFC-B Module - * @{ - * - */ - -#ifndef RFAL_NFCB_H -#define RFAL_NFCB_H - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "platform.h" -#include "st_errno.h" -#include "rfal_rf.h" - -/* - ****************************************************************************** - * GLOBAL DEFINES - ****************************************************************************** - */ - -#define RFAL_NFCB_FWTSENSB 7680U /*!< NFC-B FWT(SENSB) Digital 2.0 B.3 */ -#define RFAL_NFCB_DFWT 49152U /*!< NFC-B dFWT Delta 2.0 7.9.1.3 & B.3 */ -#define RFAL_NFCB_DTPOLL_10 rfalConvMsTo1fc(20) /*!< NFC-B Delta Tb Poll Digital 1.0 A.2 */ -#define RFAL_NFCB_DTPOLL_20 rfalConvMsTo1fc(17) /*!< NFC-B Delta Tb Poll Digital 2.1 B.3 */ - -#define RFAL_NFCB_AFI 0x00U /*!< NFC-B default Application Family Digital 1.1 7.6.1.1 */ -#define RFAL_NFCB_PARAM 0x00U /*!< NFC-B default SENSB_REQ PARAM */ -#define RFAL_NFCB_CRC_LEN 2U /*!< NFC-B CRC length and CRC_B(AID) Digital 1.1 Table 28 */ -#define RFAL_NFCB_NFCID0_LEN 4U /*!< Length of NFC-B NFCID0 */ -#define RFAL_NFCB_CMD_LEN 1U /*!< Length of NFC-B Command */ - -#define RFAL_NFCB_SENSB_RES_LEN 12U /*!< Standard length of SENSB_RES without SFGI byte */ -#define RFAL_NFCB_SENSB_RES_EXT_LEN \ - 13U /*!< Extended length of SENSB_RES with SFGI byte */ - -#define RFAL_NFCB_SENSB_REQ_ADV_FEATURE \ - 0x20U /*!< Bit mask for Advance Feature in SENSB_REQ */ -#define RFAL_NFCB_SENSB_RES_FSCI_MASK \ - 0x0FU /*!< Bit mask for FSCI value in SENSB_RES */ -#define RFAL_NFCB_SENSB_RES_FSCI_SHIFT \ - 4U /*!< Shift for FSCI value in SENSB_RES */ -#define RFAL_NFCB_SENSB_RES_PROTO_RFU_MASK \ - 0x08U /*!< Bit mask for Protocol Type RFU in SENSB_RES */ -#define RFAL_NFCB_SENSB_RES_PROTO_TR2_MASK \ - 0x03U /*!< Bit mask for Protocol Type TR2 in SENSB_RES */ -#define RFAL_NFCB_SENSB_RES_PROTO_TR2_SHIFT \ - 1U /*!< Shift for Protocol Type TR2 in SENSB_RES */ -#define RFAL_NFCB_SENSB_RES_PROTO_ISO_MASK \ - 0x01U /*!< Bit mask Protocol Type ISO14443 Compliant in SENSB_RES */ -#define RFAL_NFCB_SENSB_RES_FWI_MASK \ - 0x0FU /*!< Bit mask for FWI value in SENSB_RES */ -#define RFAL_NFCB_SENSB_RES_FWI_SHIFT \ - 4U /*!< Bit mask for FWI value in SENSB_RES */ -#define RFAL_NFCB_SENSB_RES_ADC_MASK \ - 0x0CU /*!< Bit mask for ADC value in SENSB_RES */ -#define RFAL_NFCB_SENSB_RES_ADC_ADV_FEATURE_MASK \ - 0x08U /*!< Bit mask for ADC.Advanced Proto Features in SENSB_RES */ -#define RFAL_NFCB_SENSB_RES_ADC_PROPRIETARY_MASK \ - 0x04U /*!< Bit mask for ADC.Proprietary Application in SENSB_RES */ -#define RFAL_NFCB_SENSB_RES_FO_DID_MASK \ - 0x01U /*!< Bit mask for DID in SENSB_RES */ -#define RFAL_NFCB_SENSB_RES_FO_NAD_MASK \ - 0x02U /*!< Bit mask for DID in SENSB_RES */ -#define RFAL_NFCB_SENSB_RES_FO_MASK \ - 0x03U /*!< Bit mask for FO value in SENSB_RES (NAD and DID) */ -#define RFAL_NFCB_SENSB_RES_SFGI_MASK \ - 0x0FU /*!< Bit mask for SFGI in SENSB_RES */ -#define RFAL_NFCB_SENSB_RES_SFGI_SHIFT \ - 4U /*!< Shift for SFGI in SENSB_RES */ - -/* -****************************************************************************** -* GLOBAL MACROS -****************************************************************************** -*/ - -/*! Get device's FSCI given its SENSB_RES Digital 1.1 7.6.2 */ -#define rfalNfcbGetFSCI(sensbRes) \ - ((((rfalNfcbSensbRes*)(sensbRes))->protInfo.FsciProType >> RFAL_NFCB_SENSB_RES_FSCI_SHIFT) & \ - RFAL_NFCB_SENSB_RES_FSCI_MASK) - -/*! Checks if the given NFC-B device indicates ISO-DEP support */ -#define rfalNfcbIsIsoDepSupported(dev) \ - ((((rfalNfcbListenDevice*)(dev))->sensbRes.protInfo.FsciProType & \ - RFAL_NFCB_SENSB_RES_PROTO_ISO_MASK) != 0U) - -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ - -/*! SENSB_REQ and ALLB_REQ param Digital 1.1 7.6.1 */ -typedef enum { - RFAL_NFCB_SENS_CMD_ALLB_REQ = 0x08, /*!< ALLB_REQ (WUPB) */ - RFAL_NFCB_SENS_CMD_SENSB_REQ = 0x00 /*!< SENSB_REQ (REQB) */ -} rfalNfcbSensCmd; - -/*! Number of Slots (NI) codes used for NFC-B anti collision Digital 1.1 Table 26 */ -typedef enum { - RFAL_NFCB_SLOT_NUM_1 = 0, /*!< N=0 : 1 slot */ - RFAL_NFCB_SLOT_NUM_2 = 1, /*!< N=1 : 2 slots */ - RFAL_NFCB_SLOT_NUM_4 = 2, /*!< N=2 : 4 slots */ - RFAL_NFCB_SLOT_NUM_8 = 3, /*!< N=3 : 8 slots */ - RFAL_NFCB_SLOT_NUM_16 = 4 /*!< N=4 : 16 slots */ -} rfalNfcbSlots; - -/*! SENSB_RES (ATQB) Application Data Format Digital 1.1 Table 28 */ -typedef struct { - uint8_t AFI; /*!< Application Family Identifier */ - uint8_t CRC_B[RFAL_NFCB_CRC_LEN]; /*!< CRC_B of AID */ - uint8_t numApps; /*!< Number of Applications */ -} rfalNfcbSensbResAppData; - -/*! SENSB_RES Protocol Info format Digital 1.1 Table 29 */ -typedef struct { - uint8_t - BRC; /*!< Bit Rate Capability */ - uint8_t - FsciProType; /*!< Frame Size Card Integer [4b] | Protocol Type[4 bits] */ - uint8_t - FwiAdcFo; /*!< Frame Waiting Integer [4b] | Application Data Coding [2b] | Frame Options [2b] */ - uint8_t - SFGI; /*!< Optional: Start-Up Frame Guard Time Integer[4b] | RFU [4b] */ -} rfalNfcbSensbResProtocolInfo; - -/*! SENSB_RES format Digital 1.1 7.6.2 */ -typedef struct { - uint8_t cmd; /*!< SENSB_RES: 50h */ - uint8_t nfcid0[RFAL_NFCB_NFCID0_LEN]; /*!< NFC Identifier (PUPI)*/ - rfalNfcbSensbResAppData appData; /*!< Application Data */ - rfalNfcbSensbResProtocolInfo protInfo; /*!< Protocol Information */ -} rfalNfcbSensbRes; - -/*! NFC-B listener device (PICC) struct */ -typedef struct { - uint8_t sensbResLen; /*!< SENSB_RES length */ - rfalNfcbSensbRes sensbRes; /*!< SENSB_RES */ - bool isSleep; /*!< Device sleeping flag */ -} rfalNfcbListenDevice; - -/* -****************************************************************************** -* GLOBAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -/*! - ***************************************************************************** - * \brief Initialize NFC-B Poller mode - * - * This methods configures RFAL RF layer to perform as a - * NFC-B Poller/RW (ISO14443B PCD) including all default timings - * - * It sets NFC-B parameters (AFI, PARAM) to default values - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcbPollerInitialize(void); - -/*! - ***************************************************************************** - * \brief Set NFC-B Poller parameters - * - * This methods configures RFAL RF layer to perform as a - * NFCA Poller/RW (ISO14443A PCD) including all default timings - * - * Additionally configures NFC-B specific parameters to be used on the - * following communications - * - * \param[in] AFI : Application Family Identifier to be used - * \param[in] PARAM : PARAM to be used, it announces whether Advanced - * Features or Extended SENSB_RES is supported - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcbPollerInitializeWithParams(uint8_t AFI, uint8_t PARAM); - -/*! - ***************************************************************************** - * \brief NFC-B Poller Check Presence - * - * This method checks if a NFC-B Listen device (PICC) is present on the field - * by sending an ALLB_REQ (WUPB) or SENSB_REQ (REQB) - * - * \param[in] cmd : Indicate if to send an ALL_REQ or a SENS_REQ - * \param[in] slots : The number of slots to be announced - * \param[out] sensbRes : If received, the SENSB_RES - * \param[out] sensbResLen : If received, the SENSB_RES length - * - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error, no listener device detected - * \return ERR_RF_COLLISION : Collision detected one or more device in the field - * \return ERR_PAR : Parity error detected, one or more device in the field - * \return ERR_CRC : CRC error detected, one or more device in the field - * \return ERR_FRAMING : Framing error detected, one or more device in the field - * \return ERR_PROTO : Protocol error detected, invalid SENSB_RES received - * \return ERR_NONE : No error, SENSB_RES received - ***************************************************************************** - */ -ReturnCode rfalNfcbPollerCheckPresence( - rfalNfcbSensCmd cmd, - rfalNfcbSlots slots, - rfalNfcbSensbRes* sensbRes, - uint8_t* sensbResLen); - -/*! - ***************************************************************************** - * \brief NFC-B Poller Sleep - * - * This function is used to send the SLPB_REQ (HLTB) command to put the PICC with - * the given NFCID0 to state HALT so that they do not reply to further SENSB_REQ - * commands (only to ALLB_REQ) - * - * \param[in] nfcid0 : NFCID of the device to be put to Sleep - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcbPollerSleep(const uint8_t* nfcid0); - -/*! - ***************************************************************************** - * \brief NFC-B Poller Slot Marker - * - * This method selects a NFC-B Slot marker frame - * - * \param[in] slotCode : Slot Code [1-15] - * \param[out] sensbRes : If received, the SENSB_RES - * \param[out] sensbResLen : If received, the SENSB_RES length - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error - * \return ERR_PAR : Parity error detected - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error, SEL_RES received - ***************************************************************************** - */ -ReturnCode - rfalNfcbPollerSlotMarker(uint8_t slotCode, rfalNfcbSensbRes* sensbRes, uint8_t* sensbResLen); - -/*! - ***************************************************************************** - * \brief NFC-B Technology Detection - * - * This method performs NFC-B Technology Detection as defined in the spec - * given in the compliance mode - * - * \param[in] compMode : compliance mode to be performed - * \param[out] sensbRes : location to store the SENSB_RES, if received - * \param[out] sensbResLen : length of the SENSB_RES, if received - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_NONE : No error, one or more device in the field - ***************************************************************************** - */ -ReturnCode rfalNfcbPollerTechnologyDetection( - rfalComplianceMode compMode, - rfalNfcbSensbRes* sensbRes, - uint8_t* sensbResLen); - -/*! - ***************************************************************************** - * \brief NFC-B Poller Collision Resolution - * - * NFC-B Collision resolution Listener device/card (PICC) as - * defined in Activity 1.1 9.3.5 - * - * This function is used to perform collision resolution for detection in case - * of multiple NFC Forum Devices with Technology B detected. - * Target with valid SENSB_RES will be stored in devInfo and nfcbDevCount incremented. - * - * \param[in] compMode : compliance mode to be performed - * \param[in] devLimit : device limit value, and size nfcbDevList - * \param[out] nfcbDevList : NFC-B listener device info - * \param[out] devCnt : devices found counter - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcbPollerCollisionResolution( - rfalComplianceMode compMode, - uint8_t devLimit, - rfalNfcbListenDevice* nfcbDevList, - uint8_t* devCnt); - -/*! - ***************************************************************************** - * \brief NFC-B Poller Collision Resolution Slotted - * - * NFC-B Collision resolution Listener device/card (PICC). The sequence can - * be configured to be according to NFC Forum Activity 1.1 9.3.5, ISO10373 - * or EMVCo - * - * This function is used to perform collision resolution for detection in case - * of multiple NFC Forum Devices with Technology B are detected. - * Target with valid SENSB_RES will be stored in devInfo and nfcbDevCount incremented. - * - * This method provides the means to perform a collision resolution loop with specific - * initial and end number of slots. This allows to user to start the loop already with - * greater number of slots, and or limit the end number of slots. At the end a flag - * indicating whether there were collisions pending is returned. - * - * If RFAL_COMPLIANCE_MODE_ISO is used \a initSlots must be set to RFAL_NFCB_SLOT_NUM_1 - * - * - * \param[in] compMode : compliance mode to be performed - * \param[in] devLimit : device limit value, and size nfcbDevList - * \param[in] initSlots : number of slots to open initially - * \param[in] endSlots : number of slots when to stop collision resolution - * \param[out] nfcbDevList : NFC-B listener device info - * \param[out] devCnt : devices found counter - * \param[out] colPending : flag indicating whether collision are still pending - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcbPollerSlottedCollisionResolution( - rfalComplianceMode compMode, - uint8_t devLimit, - rfalNfcbSlots initSlots, - rfalNfcbSlots endSlots, - rfalNfcbListenDevice* nfcbDevList, - uint8_t* devCnt, - bool* colPending); - -/*! - ***************************************************************************** - * \brief NFC-B TR2 code to FDT - * - * Converts the TR2 code as defined in Digital 1.1 Table 33 Minimum - * TR2 Coding to Frame Delay Time (FDT) in 1/Fc - * - * \param[in] tr2Code : TR2 code as defined in Digital 1.1 Table 33 - * - * \return FDT in 1/Fc - ***************************************************************************** - */ -uint32_t rfalNfcbTR2ToFDT(uint8_t tr2Code); - -#endif /* RFAL_NFCB_H */ - -/** - * @} - * - * @} - * - * @} - */ diff --git a/lib/ST25RFAL002/include/rfal_nfcf.h b/lib/ST25RFAL002/include/rfal_nfcf.h deleted file mode 100644 index d3dbfa3954e..00000000000 --- a/lib/ST25RFAL002/include/rfal_nfcf.h +++ /dev/null @@ -1,403 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_nfcf.h - * - * \author Gustavo Patricio - * - * \brief Implementation of NFC-F Poller (FeliCa PCD) device - * - * The definitions and helpers methods provided by this module are - * aligned with NFC-F (FeliCa - JIS X6319-4) - * - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-AL - * \brief RFAL Abstraction Layer - * @{ - * - * \addtogroup NFC-F - * \brief RFAL NFC-F Module - * @{ - * - */ - -#ifndef RFAL_NFCF_H -#define RFAL_NFCF_H - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "platform.h" -#include "st_errno.h" -#include "rfal_rf.h" - -/* - ****************************************************************************** - * GLOBAL DEFINES - ****************************************************************************** - */ - -#define RFAL_NFCF_NFCID2_LEN 8U /*!< NFCID2 (FeliCa IDm) length */ -#define RFAL_NFCF_SENSF_RES_LEN_MIN 16U /*!< SENSF_RES minimum length */ -#define RFAL_NFCF_SENSF_RES_LEN_MAX 18U /*!< SENSF_RES maximum length */ -#define RFAL_NFCF_SENSF_RES_PAD0_LEN 2U /*!< SENSF_RES PAD0 length */ -#define RFAL_NFCF_SENSF_RES_PAD1_LEN 2U /*!< SENSF_RES PAD1 length */ -#define RFAL_NFCF_SENSF_RES_RD_LEN 2U /*!< SENSF_RES Request Data length */ -#define RFAL_NFCF_SENSF_RES_BYTE1 1U /*!< SENSF_RES first byte value */ -#define RFAL_NFCF_SENSF_SC_LEN 2U /*!< Felica SENSF_REQ System Code length */ -#define RFAL_NFCF_SENSF_PARAMS_SC1_POS 0U /*!< System Code byte1 position in the SENSF_REQ */ -#define RFAL_NFCF_SENSF_PARAMS_SC2_POS 1U /*!< System Code byte2 position in the SENSF_REQ */ -#define RFAL_NFCF_SENSF_PARAMS_RC_POS 2U /*!< Request Code position in the SENSF_REQ */ -#define RFAL_NFCF_SENSF_PARAMS_TSN_POS 3U /*!< Time Slot Number position in the SENSF_REQ */ -#define RFAL_NFCF_POLL_MAXCARDS 16U /*!< Max number slots/cards 16 */ - -#define RFAL_NFCF_CMD_POS 0U /*!< Command/Responce code length */ -#define RFAL_NFCF_CMD_LEN 1U /*!< Command/Responce code length */ -#define RFAL_NFCF_LENGTH_LEN 1U /*!< LEN field length */ -#define RFAL_NFCF_HEADER_LEN (RFAL_NFCF_LENGTH_LEN + RFAL_NFCF_CMD_LEN) /*!< Header length*/ - -#define RFAL_NFCF_SENSF_NFCID2_BYTE1_POS \ - 0U /*!< NFCID2 byte1 position */ -#define RFAL_NFCF_SENSF_NFCID2_BYTE2_POS \ - 1U /*!< NFCID2 byte2 position */ - -#define RFAL_NFCF_SENSF_NFCID2_PROT_TYPE_LEN \ - 2U /*!< NFCID2 length for byte 1 and byte 2 indicating NFC-DEP or T3T support */ -#define RFAL_NFCF_SENSF_NFCID2_BYTE1_NFCDEP \ - 0x01U /*!< NFCID2 byte1 NFC-DEP support Digital 1.0 Table 44 */ -#define RFAL_NFCF_SENSF_NFCID2_BYTE2_NFCDEP \ - 0xFEU /*!< NFCID2 byte2 NFC-DEP support Digital 1.0 Table 44 */ - -#define RFAL_NFCF_SYSTEMCODE \ - 0xFFFFU /*!< SENSF_RES Default System Code Digital 1.0 6.6.1.1 */ - -#define RFAL_NFCF_BLOCK_LEN \ - 16U /*!< NFCF T3T Block size T3T 1.0 4.1 */ -#define RFAL_NFCF_CHECKUPDATE_RES_ST1_POS \ - 9U /*!< Check|Update Res Status Flag 1 position T3T 1.0 Table 8 */ -#define RFAL_NFCF_CHECKUPDATE_RES_ST2_POS \ - 10U /*!< Check|Update Res Status Flag 2 position T3T 1.0 Table 8 */ -#define RFAL_NFCF_CHECKUPDATE_RES_NOB_POS \ - 11U /*!< Check|Update Res Number of Blocks position T3T 1.0 Table 8 */ - -#define RFAL_NFCF_STATUS_FLAG_SUCCESS \ - 0x00U /*!< Check response Number of Blocks position T3T 1.0 Table 11 */ -#define RFAL_NFCF_STATUS_FLAG_ERROR \ - 0xFFU /*!< Check response Number of Blocks position T3T 1.0 Table 11 */ - -#define RFAL_NFCF_BLOCKLISTELEM_LEN \ - 0x80U /*!< Block List Element Length bit (2|3 bytes) T3T 1.0 5.6.1 */ - -#define RFAL_NFCF_SERVICECODE_RDONLY \ - 0x000BU /*!< NDEF Service Code as Read-Only T3T 1.0 7.2.1 */ -#define RFAL_NFCF_SERVICECODE_RDWR \ - 0x0009U /*!< NDEF Service Code as Read and Write T3T 1.0 7.2.1 */ - -/*! NFC-F Felica command set JIS X6319-4 9.1 */ -enum { - RFAL_NFCF_CMD_POLLING = - 0x00, /*!< SENSF_REQ (Felica Poll/REQC command to identify a card ) */ - RFAL_NFCF_CMD_POLLING_RES = - 0x01, /*!< SENSF_RES (Felica Poll/REQC command response ) */ - RFAL_NFCF_CMD_REQUEST_SERVICE = - 0x02, /*!< verify the existence of Area and Service */ - RFAL_NFCF_CMD_REQUEST_RESPONSE = - 0x04, /*!< verify the existence of a card */ - RFAL_NFCF_CMD_READ_WITHOUT_ENCRYPTION = - 0x06, /*!< read Block Data from a Service that requires no authentication */ - RFAL_NFCF_CMD_READ_WITHOUT_ENCRYPTION_RES = - 0x07, /*!< read Block Data response from a Service with no authentication */ - RFAL_NFCF_CMD_WRITE_WITHOUT_ENCRYPTION = - 0x08, /*!< write Block Data to a Service that requires no authentication */ - RFAL_NFCF_CMD_WRITE_WITHOUT_ENCRYPTION_RES = - 0x09, /*!< write Block Data response to a Service with no authentication */ - RFAL_NFCF_CMD_REQUEST_SYSTEM_CODE = - 0x0c, /*!< acquire the System Code registered to a card */ - RFAL_NFCF_CMD_AUTHENTICATION1 = - 0x10, /*!< authenticate a card */ - RFAL_NFCF_CMD_AUTHENTICATION2 = - 0x12, /*!< allow a card to authenticate a Reader/Writer */ - RFAL_NFCF_CMD_READ = - 0x14, /*!< read Block Data from a Service that requires authentication */ - RFAL_NFCF_CMD_WRITE = - 0x16, /*!< write Block Data to a Service that requires authentication */ -}; - -/* - ****************************************************************************** - * GLOBAL MACROS - ****************************************************************************** - */ - -/*! Checks if the given NFC-F device indicates NFC-DEP support */ -#define rfalNfcfIsNfcDepSupported(dev) \ - ((((rfalNfcfListenDevice*)(dev))->sensfRes.NFCID2[RFAL_NFCF_SENSF_NFCID2_BYTE1_POS] == \ - RFAL_NFCF_SENSF_NFCID2_BYTE1_NFCDEP) && \ - (((rfalNfcfListenDevice*)(dev))->sensfRes.NFCID2[RFAL_NFCF_SENSF_NFCID2_BYTE2_POS] == \ - RFAL_NFCF_SENSF_NFCID2_BYTE2_NFCDEP)) - -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ - -/*! NFC-F SENSF_RES format Digital 1.1 8.6.2 */ -typedef struct { - uint8_t CMD; /*!< Command Code: 01h */ - uint8_t NFCID2[RFAL_NFCF_NFCID2_LEN]; /*!< NFCID2 */ - uint8_t PAD0[RFAL_NFCF_SENSF_RES_PAD0_LEN]; /*!< PAD0 */ - uint8_t PAD1[RFAL_NFCF_SENSF_RES_PAD1_LEN]; /*!< PAD1 */ - uint8_t MRTIcheck; /*!< MRTIcheck */ - uint8_t MRTIupdate; /*!< MRTIupdate */ - uint8_t PAD2; /*!< PAD2 */ - uint8_t RD[RFAL_NFCF_SENSF_RES_RD_LEN]; /*!< Request Data */ -} rfalNfcfSensfRes; - -/*! NFC-F poller device (PCD) struct */ -typedef struct { - uint8_t NFCID2[RFAL_NFCF_NFCID2_LEN]; /*!< NFCID2 */ -} rfalNfcfPollDevice; - -/*! NFC-F listener device (PICC) struct */ -typedef struct { - uint8_t sensfResLen; /*!< SENF_RES length */ - rfalNfcfSensfRes sensfRes; /*!< SENF_RES */ -} rfalNfcfListenDevice; - -typedef uint16_t rfalNfcfServ; /*!< NFC-F Service Code */ - -/*! NFC-F Block List Element (2 or 3 bytes element) T3T 1.0 5.6.1 */ -typedef struct { - uint8_t conf; /*!< Access Mode | Serv Code List Order */ - uint16_t blockNum; /*!< Block Number */ -} rfalNfcfBlockListElem; - -/*! Check Update Service list and Block list parameter */ -typedef struct { - uint8_t numServ; /*!< Number of Services */ - rfalNfcfServ* servList; /*!< Service Code List */ - uint8_t numBlock; /*!< Number of Blocks */ - rfalNfcfBlockListElem* blockList; /*!< Block Number List */ -} rfalNfcfServBlockListParam; - -/* -****************************************************************************** -* GLOBAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -/*! - ***************************************************************************** - * \brief Initialize NFC-F Poller mode - * - * This methods configures RFAL RF layer to perform as a - * NFC-F Poller/RW (FeliCa PCD) including all default timings - * - * \param[in] bitRate : NFC-F bitrate to be initialize (212 or 424) - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_PARAM : Incorrect bitrate - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcfPollerInitialize(rfalBitRate bitRate); - -/*! - ***************************************************************************** - * \brief NFC-F Poller Check Presence - * - * This function sends a Poll/SENSF command according to NFC Activity spec - * It detects if a NCF-F device is within range - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error, no listener device detected - * \return ERR_NONE : No error and some NFC-F device was detected - * - ***************************************************************************** - */ -ReturnCode rfalNfcfPollerCheckPresence(void); - -/*! - ***************************************************************************** - * \brief NFC-F Poller Poll - * - * This function sends to all PICCs in field the POLL command with the given - * number of slots. - * - * \param[in] slots : the number of slots to be performed - * \param[in] sysCode : as given in FeliCa poll command - * \param[in] reqCode : FeliCa communication parameters - * \param[out] cardList : Parameter of type rfalFeliCaPollRes which will hold the cards found - * \param[out] devCnt : actual number of cards found - * \param[out] collisions : number of collisions encountered - * - * \warning the list cardList has to be as big as the number of slots for the Poll - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error, no listener device detected - * \return ERR_NONE : No error and some NFC-F device was detected - * - ***************************************************************************** - */ -ReturnCode rfalNfcfPollerPoll( - rfalFeliCaPollSlots slots, - uint16_t sysCode, - uint8_t reqCode, - rfalFeliCaPollRes* cardList, - uint8_t* devCnt, - uint8_t* collisions); - -/*! - ***************************************************************************** - * \brief NFC-F Poller Full Collision Resolution - * - * Performs a full Collision resolution as defined in Activity 1.1 9.3.4 - * - * \param[in] compMode : compliance mode to be performed - * \param[in] devLimit : device limit value, and size nfcaDevList - * \param[out] nfcfDevList : NFC-F listener devices list - * \param[out] devCnt : Devices found counter - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcfPollerCollisionResolution( - rfalComplianceMode compMode, - uint8_t devLimit, - rfalNfcfListenDevice* nfcfDevList, - uint8_t* devCnt); - -/*! - ***************************************************************************** - * \brief NFC-F Poller Check/Read - * - * It computes a Check / Read command accoring to T3T 1.0 and JIS X6319-4 and - * sends it to PICC. If sucessfully, the rxBuf will contain the the number of - * blocks in the first byte followed by the blocks data. - * - * \param[in] nfcid2 : nfcid2 of the device - * \param[in] servBlock : parameter containing the list of Services and - * Blocks to be addressed by this command - * \param[out] rxBuf : buffer to place check/read data - * \param[in] rxBufLen : size of the rxBuf - * \param[out] rcvdLen : length of data placed in rxBuf - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_REQUEST : The request was executed with error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcfPollerCheck( - const uint8_t* nfcid2, - const rfalNfcfServBlockListParam* servBlock, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvdLen); - -/*! - ***************************************************************************** - * \brief NFC-F Poller Update/Write - * - * It computes a Update / Write command accoring to T3T 1.0 and JIS X6319-4 and - * sends it to PICC. - * - * \param[in] nfcid2 : nfcid2 of the device - * \param[in] servBlock : parameter containing the list of Services and - * Blocks to be addressed by this command - * \param[in] txBuf : buffer where the request will be composed - * \param[in] txBufLen : size of txBuf - * \param[in] blockData : data to written on the given block(s) - * \param[out] rxBuf : buffer to place check/read data - * \param[in] rxBufLen : size of the rxBuf - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_REQUEST : The request was executed with error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalNfcfPollerUpdate( - const uint8_t* nfcid2, - const rfalNfcfServBlockListParam* servBlock, - uint8_t* txBuf, - uint16_t txBufLen, - const uint8_t* blockData, - uint8_t* rxBuf, - uint16_t rxBufLen); - -/*! - ***************************************************************************** - * \brief NFC-F Listener is T3T Request - * - * This method checks if the given data is a valid T3T command (Read or Write) - * and in case a valid request has been received it may output the request's NFCID2 - * - * \param[in] buf : buffer holding Initiator's received command - * \param[in] bufLen : length of received command in bytes - * \param[out] nfcid2 : pointer to where the NFCID2 may be outputed, - * nfcid2 has NFCF_SENSF_NFCID2_LEN as length - * Pass NULL if output parameter not desired - * - * \return true : Valid T3T command (Read or Write) received - * \return false : Invalid protocol request - * - ***************************************************************************** - */ -bool rfalNfcfListenerIsT3TReq(const uint8_t* buf, uint16_t bufLen, uint8_t* nfcid2); - -#endif /* RFAL_NFCF_H */ - -/** - * @} - * - * @} - * - * @} - */ diff --git a/lib/ST25RFAL002/include/rfal_nfcv.h b/lib/ST25RFAL002/include/rfal_nfcv.h deleted file mode 100644 index b5e1c00aeb3..00000000000 --- a/lib/ST25RFAL002/include/rfal_nfcv.h +++ /dev/null @@ -1,923 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_nfcv.h - * - * \author Gustavo Patricio - * - * \brief Implementation of NFC-V Poller (ISO15693) device - * - * The definitions and helpers methods provided by this module - * are aligned with NFC-V Digital 2.1 - * - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-AL - * \brief RFAL Abstraction Layer - * @{ - * - * \addtogroup NFC-V - * \brief RFAL NFC-V Module - * @{ - * - */ - -#ifndef RFAL_NFCV_H -#define RFAL_NFCV_H - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "platform.h" -#include "st_errno.h" -#include "rfal_rf.h" - -/* - ****************************************************************************** - * GLOBAL DEFINES - ****************************************************************************** - */ - -#define RFAL_NFCV_UID_LEN 8U /*!< NFC-V UID length */ -#define RFAL_NFCV_MAX_BLOCK_LEN \ - 32U /*!< Max Block size: can be of up to 256 bits ISO 15693 2000 5 */ -#define RFAL_NFCV_BNO_LEN 1U /*!< NFC-V Block Number length */ -#define RFAL_NFCV_CRC_LEN 2U /*!< NFC-V CRC length */ -#define RFAL_NFCV_MAX_GEN_DATA_LEN \ - (RFAL_NFCV_MAX_BLOCK_LEN + RFAL_NFCV_BNO_LEN + RFAL_NFCV_UID_LEN) /*!
© COPYRIGHT 2020 STMicroelectronics
- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_rf.h - * - * \author Gustavo Patricio - * - * \brief RF Abstraction Layer (RFAL) - * - * RFAL (RF Abstraction Layer) provides several functionalities required to - * perform RF/NFC communications.
The RFAL encapsulates the different - * RF ICs (ST25R3911, ST25R391x, etc) into a common and easy to use interface. - * - * It provides interfaces to configure the RF IC, set/get timings, modes, bit rates, - * specific handlings, execute listen mode, etc. - * - * Furthermore it provides a common interface to perform a Transceive operations. - * The Transceive can be executed in a blocking or non blocking way.
- * Additionally few specific Transceive methods are available to cope with the - * specifics of these particular operations. - * - * The most common interfaces are: - *
  rfalInitialize() - *
  rfalSetFDTPoll() - *
  rfalSetFDTListen() - *
  rfalSetGT() - *
  rfalSetBitRate() - *
  rfalSetMode() - *
  rfalFieldOnAndStartGT() - *
  rfalFieldOff() - *
  rfalStartTransceive() - *
  rfalGetTransceiveStatus() - *
  rfalTransceiveBlockingTxRx() - * - * An usage example is provided here: \ref exampleRfalPoller.c - * \example exampleRfalPoller.c - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-HAL - * \brief RFAL Hardware Abstraction Layer - * @{ - * - * \addtogroup RF - * \brief RFAL RF Abstraction Layer - * @{ - * - */ - -#ifndef RFAL_RF_H -#define RFAL_RF_H - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ -#include "platform.h" -#include "st_errno.h" -#include "rfal_features.h" - -/* -****************************************************************************** -* GLOBAL DEFINES -****************************************************************************** -*/ -#define RFAL_VERSION 0x020200U /*!< RFAL Current Version: v2.2.0 */ - -#define RFAL_FWT_NONE 0xFFFFFFFFU /*!< Disabled FWT: Wait forever for a response */ -#define RFAL_GT_NONE RFAL_TIMING_NONE /*!< Disabled GT: No GT will be applied after Field On */ - -#define RFAL_TIMING_NONE 0x00U /*!< Timing disabled | Don't apply */ - -#define RFAL_1FC_IN_4096FC \ - (uint32_t)4096U /*!< Number of 1/fc cycles in one 4096/fc */ -#define RFAL_1FC_IN_512FC (uint32_t)512U /*!< Number of 1/fc cycles in one 512/fc */ -#define RFAL_1FC_IN_64FC (uint32_t)64U /*!< Number of 1/fc cycles in one 64/fc */ -#define RFAL_1FC_IN_8FC (uint32_t)8U /*!< Number of 1/fc cycles in one 8/fc */ -#define RFAL_US_IN_MS (uint32_t)1000U /*!< Number of us in one ms */ -#define RFAL_1MS_IN_1FC (uint32_t)13560U /*!< Number of 1/fc cycles in 1ms */ -#define RFAL_BITS_IN_BYTE (uint16_t)8U /*!< Number of bits in one byte */ - -#define RFAL_CRC_LEN 2U /*!< RF CRC LEN */ - -/*! Default TxRx flags: Tx CRC automatic, Rx CRC removed, NFCIP1 mode off, AGC On, Tx Parity automatic, Rx Parity removed */ -#define RFAL_TXRX_FLAGS_DEFAULT \ - ((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_AUTO | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_REMV | \ - (uint32_t)RFAL_TXRX_FLAGS_NFCIP1_OFF | (uint32_t)RFAL_TXRX_FLAGS_AGC_ON | \ - (uint32_t)RFAL_TXRX_FLAGS_PAR_RX_REMV | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_AUTO | \ - (uint32_t)RFAL_TXRX_FLAGS_NFCV_FLAG_AUTO) -#define RFAL_TXRX_FLAGS_RAW \ - ((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP | \ - (uint32_t)RFAL_TXRX_FLAGS_NFCIP1_OFF | (uint32_t)RFAL_TXRX_FLAGS_AGC_ON | \ - (uint32_t)RFAL_TXRX_FLAGS_PAR_RX_KEEP | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_NONE | \ - (uint32_t)RFAL_TXRX_FLAGS_NFCV_FLAG_AUTO) - -#define RFAL_LM_MASK_NFCA \ - ((uint32_t)1U \ - << (uint8_t)RFAL_MODE_LISTEN_NFCA) /*!< Bitmask for Listen Mode enabling NFCA */ -#define RFAL_LM_MASK_NFCB \ - ((uint32_t)1U \ - << (uint8_t)RFAL_MODE_LISTEN_NFCB) /*!< Bitmask for Listen Mode enabling NFCB */ -#define RFAL_LM_MASK_NFCF \ - ((uint32_t)1U \ - << (uint8_t)RFAL_MODE_LISTEN_NFCF) /*!< Bitmask for Listen Mode enabling NFCF */ -#define RFAL_LM_MASK_ACTIVE_P2P \ - ((uint32_t)1U \ - << (uint8_t)RFAL_MODE_LISTEN_ACTIVE_P2P) /*!< Bitmask for Listen Mode enabling AP2P */ - -#define RFAL_LM_SENS_RES_LEN 2U /*!< NFC-A SENS_RES (ATQA) length */ -#define RFAL_LM_SENSB_RES_LEN 13U /*!< NFC-B SENSB_RES (ATQB) length */ -#define RFAL_LM_SENSF_RES_LEN 19U /*!< NFC-F SENSF_RES length */ -#define RFAL_LM_SENSF_SC_LEN 2U /*!< NFC-F System Code length */ - -#define RFAL_NFCID3_LEN 10U /*!< NFCID3 length */ -#define RFAL_NFCID2_LEN 8U /*!< NFCID2 length */ -#define RFAL_NFCID1_TRIPLE_LEN 10U /*!< NFCID1 length */ -#define RFAL_NFCID1_DOUBLE_LEN 7U /*!< NFCID1 length */ -#define RFAL_NFCID1_SINGLE_LEN 4U /*!< NFCID1 length */ - -/* -****************************************************************************** -* GLOBAL MACROS -****************************************************************************** -*/ - -/*! Returns the maximum supported bit rate for RW mode. Caller must check if mode is supported before, as even if mode is not supported will return the min */ -#define rfalGetMaxBrRW() \ - (((RFAL_SUPPORT_BR_RW_6780) ? \ - RFAL_BR_6780 : \ - ((RFAL_SUPPORT_BR_RW_3390) ? \ - RFAL_BR_3390 : \ - ((RFAL_SUPPORT_BR_RW_1695) ? \ - RFAL_BR_1695 : \ - ((RFAL_SUPPORT_BR_RW_848) ? \ - RFAL_BR_848 : \ - ((RFAL_SUPPORT_BR_RW_424) ? \ - RFAL_BR_424 : \ - ((RFAL_SUPPORT_BR_RW_212) ? RFAL_BR_212 : RFAL_BR_106))))))) - -/*! Returns the maximum supported bit rate for AP2P mode. Caller must check if mode is supported before, as even if mode is not supported will return the min */ -#define rfalGetMaxBrAP2P() \ - (((RFAL_SUPPORT_BR_AP2P_848) ? \ - RFAL_BR_848 : \ - ((RFAL_SUPPORT_BR_AP2P_424) ? \ - RFAL_BR_424 : \ - ((RFAL_SUPPORT_BR_AP2P_212) ? RFAL_BR_212 : RFAL_BR_106)))) - -/*! Returns the maximum supported bit rate for CE-A mode. Caller must check if mode is supported before, as even if mode is not supported will return the min */ -#define rfalGetMaxBrCEA() \ - (((RFAL_SUPPORT_BR_CE_A_848) ? \ - RFAL_BR_848 : \ - ((RFAL_SUPPORT_BR_CE_A_424) ? \ - RFAL_BR_424 : \ - ((RFAL_SUPPORT_BR_CE_A_212) ? RFAL_BR_212 : RFAL_BR_106)))) - -/*! Returns the maximum supported bit rate for CE-B mode. Caller must check if mode is supported before, as even if mode is not supported will return the min */ -#define rfalGetMaxBrCEB() \ - (((RFAL_SUPPORT_BR_CE_B_848) ? \ - RFAL_BR_848 : \ - ((RFAL_SUPPORT_BR_CE_B_424) ? \ - RFAL_BR_424 : \ - ((RFAL_SUPPORT_BR_CE_B_212) ? RFAL_BR_212 : RFAL_BR_106)))) - -/*! Returns the maximum supported bit rate for CE-F mode. Caller must check if mode is supported before, as even if mode is not supported will return the min */ -#define rfalGetMaxBrCEF() (((RFAL_SUPPORT_BR_CE_F_424) ? RFAL_BR_424 : RFAL_BR_212)) - -#define rfalIsModeActiveComm(md) \ - (((md) == RFAL_MODE_POLL_ACTIVE_P2P) || \ - ((md) == RFAL_MODE_LISTEN_ACTIVE_P2P)) /*!< Checks if mode md is Active Communication */ -#define rfalIsModePassiveComm(md) \ - (!rfalIsModeActiveComm(md)) /*!< Checks if mode md is Passive Communication */ -#define rfalIsModePassiveListen(md) \ - (((md) == RFAL_MODE_LISTEN_NFCA) || ((md) == RFAL_MODE_LISTEN_NFCB) || \ - ((md) == RFAL_MODE_LISTEN_NFCF)) /*!< Checks if mode md is Passive Listen */ -#define rfalIsModePassivePoll(md) \ - (rfalIsModePassiveComm(md) && \ - !rfalIsModePassiveListen(md)) /*!< Checks if mode md is Passive Poll */ - -#define rfalConv1fcTo8fc(t) \ - (uint32_t)((uint32_t)(t) / RFAL_1FC_IN_8FC) /*!< Converts the given t from 1/fc to 8/fc */ -#define rfalConv8fcTo1fc(t) \ - (uint32_t)((uint32_t)(t)*RFAL_1FC_IN_8FC) /*!< Converts the given t from 8/fc to 1/fc */ - -#define rfalConv1fcTo64fc(t) \ - (uint32_t)((uint32_t)(t) / RFAL_1FC_IN_64FC) /*!< Converts the given t from 1/fc to 64/fc */ -#define rfalConv64fcTo1fc(t) \ - (uint32_t)((uint32_t)(t)*RFAL_1FC_IN_64FC) /*!< Converts the given t from 64/fc to 1/fc */ - -#define rfalConv1fcTo512fc(t) \ - (uint32_t)( \ - (uint32_t)(t) / RFAL_1FC_IN_512FC) /*!< Converts the given t from 1/fc to 512/fc */ -#define rfalConv512fcTo1fc(t) \ - (uint32_t)((uint32_t)(t)*RFAL_1FC_IN_512FC) /*!< Converts the given t from 512/fc to 1/fc */ - -#define rfalConv1fcTo4096fc(t) \ - (uint32_t)( \ - (uint32_t)(t) / RFAL_1FC_IN_4096FC) /*!< Converts the given t from 1/fc to 4096/fc */ -#define rfalConv4096fcTo1fc(t) \ - (uint32_t)((uint32_t)(t)*RFAL_1FC_IN_4096FC) /*!< Converts the given t from 4096/fc to 1/fc */ - -#define rfalConv1fcToMs(t) \ - (uint32_t)((uint32_t)(t) / RFAL_1MS_IN_1FC) /*!< Converts the given t from 1/fc to ms */ -#define rfalConvMsTo1fc(t) \ - (uint32_t)((uint32_t)(t)*RFAL_1MS_IN_1FC) /*!< Converts the given t from ms to 1/fc */ - -#define rfalConv1fcToUs(t) \ - (uint32_t)( \ - ((uint32_t)(t)*RFAL_US_IN_MS) / \ - RFAL_1MS_IN_1FC) /*!< Converts the given t from 1/fc to us */ -#define rfalConvUsTo1fc(t) \ - (uint32_t)( \ - ((uint32_t)(t)*RFAL_1MS_IN_1FC) / \ - RFAL_US_IN_MS) /*!< Converts the given t from us to 1/fc */ - -#define rfalConv64fcToMs(t) \ - (uint32_t)( \ - (uint32_t)(t) / \ - (RFAL_1MS_IN_1FC / RFAL_1FC_IN_64FC)) /*!< Converts the given t from 64/fc to ms */ -#define rfalConvMsTo64fc(t) \ - (uint32_t)( \ - (uint32_t)(t) * \ - (RFAL_1MS_IN_1FC / RFAL_1FC_IN_64FC)) /*!< Converts the given t from ms to 64/fc */ - -#define rfalConvBitsToBytes(n) \ - (uint16_t)( \ - ((uint16_t)(n) + (RFAL_BITS_IN_BYTE - 1U)) / \ - (RFAL_BITS_IN_BYTE)) /*!< Converts the given n from bits to bytes */ -#define rfalConvBytesToBits(n) \ - (uint32_t)( \ - (uint32_t)(n) * (RFAL_BITS_IN_BYTE)) /*!< Converts the given n from bytes to bits */ - -/*! Computes a Transceive context \a ctx with default flags and the lengths - * in bytes with the given arguments - * \a ctx : Transceive context to be assigned - * \a tB : txBuf the pointer to the buffer to be sent - * \a tBL : txBuf length in bytes - * \a rB : rxBuf the pointer to the buffer to place the received frame - * \a rBL : rxBuf length in bytes - * \a rBL : rxBuf length in bytes - * \a t : FWT to be used on this transceive in 1/fc - */ -#define rfalCreateByteTxRxContext(ctx, tB, tBL, rB, rBL, rdL, t) \ - (ctx).txBuf = (uint8_t*)(tB); \ - (ctx).txBufLen = (uint16_t)rfalConvBytesToBits(tBL); \ - (ctx).rxBuf = (uint8_t*)(rB); \ - (ctx).rxBufLen = (uint16_t)rfalConvBytesToBits(rBL); \ - (ctx).rxRcvdLen = (uint16_t*)(rdL); \ - (ctx).flags = (uint32_t)RFAL_TXRX_FLAGS_DEFAULT; \ - (ctx).fwt = (uint32_t)(t); - -/*! Computes a Transceive context \a ctx using lengths in bytes - * with the given flags and arguments - * \a ctx : Transceive context to be assigned - * \a tB : txBuf the pointer to the buffer to be sent - * \a tBL : txBuf length in bytes - * \a rB : rxBuf the pointer to the buffer to place the received frame - * \a rBL : rxBuf length in bytes - * \a rBL : rxBuf length in bytes - * \a t : FWT to be used on this transceive in 1/fc - */ -#define rfalCreateByteFlagsTxRxContext(ctx, tB, tBL, rB, rBL, rdL, fl, t) \ - (ctx).txBuf = (uint8_t*)(tB); \ - (ctx).txBufLen = (uint16_t)rfalConvBytesToBits(tBL); \ - (ctx).rxBuf = (uint8_t*)(rB); \ - (ctx).rxBufLen = (uint16_t)rfalConvBytesToBits(rBL); \ - (ctx).rxRcvdLen = (uint16_t*)(rdL); \ - (ctx).flags = (uint32_t)(fl); \ - (ctx).fwt = (uint32_t)(t); - -#define rfalLogE(...) \ - platformLog(__VA_ARGS__) /*!< Macro for the error log method */ -#define rfalLogW(...) \ - platformLog(__VA_ARGS__) /*!< Macro for the warning log method */ -#define rfalLogI(...) \ - platformLog(__VA_ARGS__) /*!< Macro for the info log method */ -#define rfalLogD(...) \ - platformLog(__VA_ARGS__) /*!< Macro for the debug log method */ - -/* -****************************************************************************** -* GLOBAL ENUMS -****************************************************************************** -*/ - -/* RFAL Guard Time (GT) default values */ -#define RFAL_GT_NFCA \ - rfalConvMsTo1fc( \ - 5U) /*!< GTA Digital 2.0 6.10.4.1 & B.2 */ -#define RFAL_GT_NFCB \ - rfalConvMsTo1fc( \ - 5U) /*!< GTB Digital 2.0 7.9.4.1 & B.3 */ -#define RFAL_GT_NFCF \ - rfalConvMsTo1fc( \ - 20U) /*!< GTF Digital 2.0 8.7.4.1 & B.4 */ -#define RFAL_GT_NFCV \ - rfalConvMsTo1fc( \ - 5U) /*!< GTV Digital 2.0 9.7.5.1 & B.5 */ -#define RFAL_GT_PICOPASS \ - rfalConvMsTo1fc( \ - 1U) /*!< GT Picopass */ -#define RFAL_GT_AP2P \ - rfalConvMsTo1fc( \ - 5U) /*!< TIRFG Ecma 340 11.1.1 */ -#define RFAL_GT_AP2P_ADJUSTED \ - rfalConvMsTo1fc( \ - 5U + \ - 25U) /*!< Adjusted GT for greater interoperability (Sony XPERIA P, Nokia N9, Huawei P2) */ - -/* RFAL Frame Delay Time (FDT) Listen default values */ -#define RFAL_FDT_LISTEN_NFCA_POLLER \ - 1172U /*!< FDTA,LISTEN,MIN (n=9) Last bit: Logic "1" - tnn,min/2 Digital 1.1 6.10 ; EMV CCP Spec Book D v2.01 4.8.1.3 */ -#define RFAL_FDT_LISTEN_NFCB_POLLER \ - 1008U /*!< TR0B,MIN Digital 1.1 7.1.3 & A.3 ; EMV CCP Spec Book D v2.01 4.8.1.3 & Table A.5 */ -#define RFAL_FDT_LISTEN_NFCF_POLLER \ - 2672U /*!< TR0F,LISTEN,MIN Digital 1.1 8.7.1.1 & A.4 */ -#define RFAL_FDT_LISTEN_NFCV_POLLER \ - 4310U /*!< FDTV,LISTEN,MIN t1 min Digital 2.1 B.5 ; ISO15693-3 2009 9.1 */ -#define RFAL_FDT_LISTEN_PICOPASS_POLLER \ - 3400U /*!< ISO15693 t1 min - observed adjustment */ -#define RFAL_FDT_LISTEN_AP2P_POLLER \ - 64U /*!< FDT AP2P No actual FDTListen is required as fields switch and collision avoidance */ -#define RFAL_FDT_LISTEN_NFCA_LISTENER \ - 1172U /*!< FDTA,LISTEN,MIN Digital 1.1 6.10 */ -#define RFAL_FDT_LISTEN_NFCB_LISTENER \ - 1024U /*!< TR0B,MIN Digital 1.1 7.1.3 & A.3 ; EMV CCP Spec Book D v2.01 4.8.1.3 & Table A.5 */ -#define RFAL_FDT_LISTEN_NFCF_LISTENER \ - 2688U /*!< TR0F,LISTEN,MIN Digital 2.1 8.7.1.1 & B.4 */ -#define RFAL_FDT_LISTEN_AP2P_LISTENER \ - 64U /*!< FDT AP2P No actual FDTListen exists as fields switch and collision avoidance */ - -/* RFAL Frame Delay Time (FDT) Poll default values */ -#define RFAL_FDT_POLL_NFCA_POLLER \ - 6780U /*!< FDTA,POLL,MIN Digital 1.1 6.10.3.1 & A.2 */ -#define RFAL_FDT_POLL_NFCA_T1T_POLLER \ - 384U /*!< RRDDT1T,MIN,B1 Digital 1.1 10.7.1 & A.5 */ -#define RFAL_FDT_POLL_NFCB_POLLER \ - 6780U /*!< FDTB,POLL,MIN = TR2B,MIN,DEFAULT Digital 1.1 7.9.3 & A.3 ; EMVCo 3.0 FDTB,PCD,MIN Table A.5 */ -#define RFAL_FDT_POLL_NFCF_POLLER \ - 6800U /*!< FDTF,POLL,MIN Digital 2.1 8.7.3 & B.4 */ -#define RFAL_FDT_POLL_NFCV_POLLER \ - 4192U /*!< FDTV,POLL Digital 2.1 9.7.3.1 & B.5 */ -#define RFAL_FDT_POLL_PICOPASS_POLLER \ - 1790U /*!< FDT Max */ -#define RFAL_FDT_POLL_AP2P_POLLER \ - 0U /*!< FDT AP2P No actual FDTPoll exists as fields switch and collision avoidance */ - -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ - -/*! RFAL modes */ -typedef enum { - RFAL_MODE_NONE = 0, /*!< No mode selected/defined */ - RFAL_MODE_POLL_NFCA = - 1, /*!< Mode to perform as NFCA (ISO14443A) Poller (PCD) */ - RFAL_MODE_POLL_NFCA_T1T = - 2, /*!< Mode to perform as NFCA T1T (Topaz) Poller (PCD) */ - RFAL_MODE_POLL_NFCB = - 3, /*!< Mode to perform as NFCB (ISO14443B) Poller (PCD) */ - RFAL_MODE_POLL_B_PRIME = - 4, /*!< Mode to perform as B' Calypso (Innovatron) (PCD) */ - RFAL_MODE_POLL_B_CTS = - 5, /*!< Mode to perform as CTS Poller (PCD) */ - RFAL_MODE_POLL_NFCF = - 6, /*!< Mode to perform as NFCF (FeliCa) Poller (PCD) */ - RFAL_MODE_POLL_NFCV = - 7, /*!< Mode to perform as NFCV (ISO15963) Poller (PCD) */ - RFAL_MODE_POLL_PICOPASS = - 8, /*!< Mode to perform as PicoPass / iClass Poller (PCD) */ - RFAL_MODE_POLL_ACTIVE_P2P = - 9, /*!< Mode to perform as Active P2P (ISO18092) Initiator */ - RFAL_MODE_LISTEN_NFCA = - 10, /*!< Mode to perform as NFCA (ISO14443A) Listener (PICC) */ - RFAL_MODE_LISTEN_NFCB = - 11, /*!< Mode to perform as NFCA (ISO14443B) Listener (PICC) */ - RFAL_MODE_LISTEN_NFCF = - 12, /*!< Mode to perform as NFCA (ISO15963) Listener (PICC) */ - RFAL_MODE_LISTEN_ACTIVE_P2P = - 13 /*!< Mode to perform as Active P2P (ISO18092) Target */ -} rfalMode; - -/*! RFAL Bit rates */ -typedef enum { - RFAL_BR_106 = 0, /*!< Bit Rate 106 kbit/s (fc/128) */ - RFAL_BR_212 = 1, /*!< Bit Rate 212 kbit/s (fc/64) */ - RFAL_BR_424 = 2, /*!< Bit Rate 424 kbit/s (fc/32) */ - RFAL_BR_848 = 3, /*!< Bit Rate 848 kbit/s (fc/16) */ - RFAL_BR_1695 = 4, /*!< Bit Rate 1695 kbit/s (fc/8) */ - RFAL_BR_3390 = 5, /*!< Bit Rate 3390 kbit/s (fc/4) */ - RFAL_BR_6780 = 6, /*!< Bit Rate 6780 kbit/s (fc/2) */ - RFAL_BR_13560 = 7, /*!< Bit Rate 13560 kbit/s (fc) */ - RFAL_BR_52p97 = 0xEB, /*!< Bit Rate 52.97 kbit/s (fc/256) Fast Mode VICC->VCD */ - RFAL_BR_26p48 = 0xEC, /*!< Bit Rate 26,48 kbit/s (fc/512) NFCV VICC->VCD & VCD->VICC 1of4 */ - RFAL_BR_1p66 = 0xED, /*!< Bit Rate 1,66 kbit/s (fc/8192) NFCV VCD->VICC 1of256 */ - RFAL_BR_KEEP = 0xFF /*!< Value indicating to keep the same previous bit rate */ -} rfalBitRate; - -/*! RFAL Compliance modes for upper modules */ -typedef enum { - RFAL_COMPLIANCE_MODE_NFC, /*!< Perform with NFC Forum 1.1 compliance */ - RFAL_COMPLIANCE_MODE_EMV, /*!< Perform with EMVCo compliance */ - RFAL_COMPLIANCE_MODE_ISO /*!< Perform with ISO10373 compliance */ -} rfalComplianceMode; - -/*! RFAL main states flags */ -typedef enum { - RFAL_STATE_IDLE = 0, - RFAL_STATE_INIT = 1, - RFAL_STATE_MODE_SET = 2, - - RFAL_STATE_TXRX = 3, - RFAL_STATE_LM = 4, - RFAL_STATE_WUM = 5 - -} rfalState; - -/*! RFAL transceive states */ -typedef enum { - RFAL_TXRX_STATE_IDLE = 0, - RFAL_TXRX_STATE_INIT = 1, - RFAL_TXRX_STATE_START = 2, - - RFAL_TXRX_STATE_TX_IDLE = 11, - RFAL_TXRX_STATE_TX_WAIT_GT = 12, - RFAL_TXRX_STATE_TX_WAIT_FDT = 13, - RFAL_TXRX_STATE_TX_TRANSMIT = 14, - RFAL_TXRX_STATE_TX_WAIT_WL = 15, - RFAL_TXRX_STATE_TX_RELOAD_FIFO = 16, - RFAL_TXRX_STATE_TX_WAIT_TXE = 17, - RFAL_TXRX_STATE_TX_DONE = 18, - RFAL_TXRX_STATE_TX_FAIL = 19, - - RFAL_TXRX_STATE_RX_IDLE = 81, - RFAL_TXRX_STATE_RX_WAIT_EON = 82, - RFAL_TXRX_STATE_RX_WAIT_RXS = 83, - RFAL_TXRX_STATE_RX_WAIT_RXE = 84, - RFAL_TXRX_STATE_RX_READ_FIFO = 85, - RFAL_TXRX_STATE_RX_ERR_CHECK = 86, - RFAL_TXRX_STATE_RX_READ_DATA = 87, - RFAL_TXRX_STATE_RX_WAIT_EOF = 88, - RFAL_TXRX_STATE_RX_DONE = 89, - RFAL_TXRX_STATE_RX_FAIL = 90, - -} rfalTransceiveState; - -/*! RFAL transceive flags */ -enum { - RFAL_TXRX_FLAGS_CRC_TX_AUTO = - (0U - << 0), /*!< CRC will be generated automatic upon transmission */ - RFAL_TXRX_FLAGS_CRC_TX_MANUAL = - (1U - << 0), /*!< CRC was calculated manually, included in txBuffer */ - RFAL_TXRX_FLAGS_CRC_RX_KEEP = - (1U - << 1), /*!< Upon Reception keep the CRC in rxBuffer (reflected on rcvd length) */ - RFAL_TXRX_FLAGS_CRC_RX_REMV = - (0U - << 1), /*!< Enable CRC check and remove the CRC from rxBuffer */ - RFAL_TXRX_FLAGS_NFCIP1_ON = - (1U - << 2), /*!< Enable NFCIP1 mode: Add SB(F0) and LEN bytes during Tx and skip SB(F0) byte during Rx */ - RFAL_TXRX_FLAGS_NFCIP1_OFF = - (0U - << 2), /*!< Disable NFCIP1 mode: do not append protocol bytes while Tx nor skip while Rx */ - RFAL_TXRX_FLAGS_AGC_OFF = - (1U - << 3), /*!< Disable Automatic Gain Control, improving multiple devices collision detection */ - RFAL_TXRX_FLAGS_AGC_ON = - (0U - << 3), /*!< Enable Automatic Gain Control, improving single device reception */ - RFAL_TXRX_FLAGS_PAR_RX_KEEP = - (1U - << 4), /*!< Disable Parity and CRC check and keep the Parity and CRC bits in the received buffer */ - RFAL_TXRX_FLAGS_PAR_RX_REMV = - (0U - << 0), /*!< Enable Parity check and remove the parity bits from the received buffer */ - RFAL_TXRX_FLAGS_PAR_TX_NONE = - (1U - << 5), /*!< Disable automatic Parity generation (ISO14443A) and use the one provided in the buffer*/ - RFAL_TXRX_FLAGS_PAR_TX_AUTO = - (0U - << 5), /*!< Enable automatic Parity generation (ISO14443A) */ - RFAL_TXRX_FLAGS_NFCV_FLAG_MANUAL = - (1U - << 6), /*!< Disable automatic adaption of flag byte (ISO15693) according to current comm params */ - RFAL_TXRX_FLAGS_NFCV_FLAG_AUTO = - (0U - << 6), /*!< Enable automatic adaption of flag byte (ISO115693) according to current comm params */ -}; - -/*! RFAL error handling */ -typedef enum { - RFAL_ERRORHANDLING_NONE = - 0, /*!< No special error handling will be performed */ - RFAL_ERRORHANDLING_NFC = - 1, /*!< Error handling set to perform as NFC compliant device */ - RFAL_ERRORHANDLING_EMVCO = - 2 /*!< Error handling set to perform as EMVCo compliant device */ -} rfalEHandling; - -/*! Struct that holds all context to be used on a Transceive */ -typedef struct { - uint8_t* txBuf; /*!< (In) Buffer where outgoing message is located */ - uint16_t txBufLen; /*!< (In) Length of the outgoing message in bits */ - - uint8_t* rxBuf; /*!< (Out) Buffer where incoming message will be placed */ - uint16_t rxBufLen; /*!< (In) Maximum length of the incoming message in bits */ - uint16_t* rxRcvdLen; /*!< (Out) Actual received length in bits */ - - uint32_t flags; /*!< (In) TransceiveFlags indication special handling */ - uint32_t fwt; /*!< (In) Frame Waiting Time in 1/fc */ -} rfalTransceiveContext; - -/*! System callback to indicate an event that requires a system reRun */ -typedef void (*rfalUpperLayerCallback)(void); - -/*! Callback to be executed before a Transceive */ -typedef void (*rfalPreTxRxCallback)(void* context); - -/*! Callback to be executed after a Transceive */ -typedef void (*rfalPostTxRxCallback)(void* context); - -/** Callback to be executed on each RFAL state change */ -typedef void (*RfalStateChangedCallback)(void* context); - -/*******************************************************************************/ -/* ISO14443A */ -/*******************************************************************************/ - -/*! RFAL ISO 14443A Short Frame Command */ -typedef enum { - RFAL_14443A_SHORTFRAME_CMD_WUPA = 0x52, /*!< ISO14443A WUPA / NFC-A ALL_REQ */ - RFAL_14443A_SHORTFRAME_CMD_REQA = 0x26 /*!< ISO14443A REQA / NFC-A SENS_REQ */ -} rfal14443AShortFrameCmd; - -/*******************************************************************************/ - -/*******************************************************************************/ -/* FeliCa */ -/*******************************************************************************/ - -#define RFAL_FELICA_LEN_LEN \ - 1U /*!< FeliCa LEN byte length */ -#define RFAL_FELICA_POLL_REQ_LEN \ - (RFAL_FELICA_LEN_LEN + 1U + 2U + 1U + \ - 1U) /*!< FeliCa Poll Request length (LEN + CMD + SC + RC + TSN) */ -#define RFAL_FELICA_POLL_RES_LEN \ - (RFAL_FELICA_LEN_LEN + 1U + 8U + 8U + \ - 2U) /*!< Maximum FeliCa Poll Response length (LEN + CMD + NFCID2 + PAD + RD) */ -#define RFAL_FELICA_POLL_MAX_SLOTS \ - 16U /*!< Maximum number of slots (TSN) on FeliCa Poll */ - -/*! NFC-F RC (Request Code) codes NFC Forum Digital 1.1 Table 42 */ -enum { - RFAL_FELICA_POLL_RC_NO_REQUEST = - 0x00, /*!< RC: No System Code information requested */ - RFAL_FELICA_POLL_RC_SYSTEM_CODE = - 0x01, /*!< RC: System Code information requested */ - RFAL_FELICA_POLL_RC_COM_PERFORMANCE = - 0x02 /*!< RC: Advanced protocol features supported */ -}; - -/*! NFC-F TSN (Time Slot Number) codes NFC Forum Digital 1.1 Table 43 */ -typedef enum { - RFAL_FELICA_1_SLOT = 0, /*!< TSN with number of Time Slots: 1 */ - RFAL_FELICA_2_SLOTS = 1, /*!< TSN with number of Time Slots: 2 */ - RFAL_FELICA_4_SLOTS = 3, /*!< TSN with number of Time Slots: 4 */ - RFAL_FELICA_8_SLOTS = 7, /*!< TSN with number of Time Slots: 8 */ - RFAL_FELICA_16_SLOTS = 15 /*!< TSN with number of Time Slots: 16 */ -} rfalFeliCaPollSlots; - -/*! NFCF Poll Response NFC Forum Digital 1.1 Table 44 */ -typedef uint8_t rfalFeliCaPollRes[RFAL_FELICA_POLL_RES_LEN]; - -/*******************************************************************************/ - -/*******************************************************************************/ -/* Listen Mode */ -/*******************************************************************************/ - -/*! RFAL Listen Mode NFCID Length */ -typedef enum { - RFAL_LM_NFCID_LEN_04 = RFAL_NFCID1_SINGLE_LEN, /*!< Listen mode indicates 4 byte NFCID */ - RFAL_LM_NFCID_LEN_07 = RFAL_NFCID1_DOUBLE_LEN, /*!< Listen mode indicates 7 byte NFCID */ - RFAL_LM_NFCID_LEN_10 = RFAL_NFCID1_TRIPLE_LEN, /*!< Listen mode indicates 10 byte NFCID */ -} rfalLmNfcidLen; - -/*! RFAL Listen Mode States */ -typedef enum { - RFAL_LM_STATE_NOT_INIT = 0x00, /*!< Not Initialized state */ - RFAL_LM_STATE_POWER_OFF = 0x01, /*!< Power Off state */ - RFAL_LM_STATE_IDLE = 0x02, /*!< Idle state Activity 1.1 5.2 */ - RFAL_LM_STATE_READY_A = 0x03, /*!< Ready A state Activity 1.1 5.3 5.4 & 5.5 */ - RFAL_LM_STATE_READY_B = 0x04, /*!< Ready B state Activity 1.1 5.11 5.12 */ - RFAL_LM_STATE_READY_F = 0x05, /*!< Ready F state Activity 1.1 5.15 */ - RFAL_LM_STATE_ACTIVE_A = 0x06, /*!< Active A state Activity 1.1 5.6 */ - RFAL_LM_STATE_CARDEMU_4A = 0x07, /*!< Card Emulation 4A state Activity 1.1 5.10 */ - RFAL_LM_STATE_CARDEMU_4B = 0x08, /*!< Card Emulation 4B state Activity 1.1 5.14 */ - RFAL_LM_STATE_CARDEMU_3 = 0x09, /*!< Card Emulation 3 state Activity 1.1 5.18 */ - RFAL_LM_STATE_TARGET_A = 0x0A, /*!< Target A state Activity 1.1 5.9 */ - RFAL_LM_STATE_TARGET_F = 0x0B, /*!< Target F state Activity 1.1 5.17 */ - RFAL_LM_STATE_SLEEP_A = 0x0C, /*!< Sleep A state Activity 1.1 5.7 */ - RFAL_LM_STATE_SLEEP_B = 0x0D, /*!< Sleep B state Activity 1.1 5.13 */ - RFAL_LM_STATE_READY_Ax = 0x0E, /*!< Ready A* state Activity 1.1 5.3 5.4 & 5.5 */ - RFAL_LM_STATE_ACTIVE_Ax = 0x0F, /*!< Active A* state Activity 1.1 5.6 */ - RFAL_LM_STATE_SLEEP_AF = 0x10, /*!< Sleep AF state Activity 1.1 5.19 */ -} rfalLmState; - -/*! RFAL Listen Mode Passive A configs */ -typedef struct { - rfalLmNfcidLen nfcidLen; /*!< NFCID Len (4, 7 or 10 bytes) */ - uint8_t nfcid[RFAL_NFCID1_TRIPLE_LEN]; /*!< NFCID */ - uint8_t SENS_RES[RFAL_LM_SENS_RES_LEN]; /*!< NFC-106k; SENS_REQ Response */ - uint8_t SEL_RES; /*!< SEL_RES (SAK) with complete NFCID1 (UID) */ -} rfalLmConfPA; - -/*! RFAL Listen Mode Passive B configs */ -typedef struct { - uint8_t SENSB_RES[RFAL_LM_SENSB_RES_LEN]; /*!< SENSF_RES */ -} rfalLmConfPB; - -/*! RFAL Listen Mode Passive F configs */ -typedef struct { - uint8_t SC[RFAL_LM_SENSF_SC_LEN]; /*!< System Code to listen for */ - uint8_t SENSF_RES[RFAL_LM_SENSF_RES_LEN]; /*!< SENSF_RES */ -} rfalLmConfPF; - -/*******************************************************************************/ - -/*******************************************************************************/ -/* Wake-Up Mode */ -/*******************************************************************************/ - -#define RFAL_WUM_REFERENCE_AUTO 0xFFU /*!< Indicates new reference is set by the driver*/ - -/*! RFAL Wake-Up Mode States */ -typedef enum { - RFAL_WUM_STATE_NOT_INIT = 0x00, /*!< Not Initialized state */ - RFAL_WUM_STATE_ENABLED = 0x01, /*!< Wake-Up mode is enabled */ - RFAL_WUM_STATE_ENABLED_WOKE = 0x02, /*!< Wake-Up mode enabled and has received IRQ(s)*/ -} rfalWumState; - -/*! RFAL Wake-Up Period/Timer */ -typedef enum { - RFAL_WUM_PERIOD_10MS = 0x00, /*!< Wake-Up timer 10ms */ - RFAL_WUM_PERIOD_20MS = 0x01, /*!< Wake-Up timer 20ms */ - RFAL_WUM_PERIOD_30MS = 0x02, /*!< Wake-Up timer 30ms */ - RFAL_WUM_PERIOD_40MS = 0x03, /*!< Wake-Up timer 40ms */ - RFAL_WUM_PERIOD_50MS = 0x04, /*!< Wake-Up timer 50ms */ - RFAL_WUM_PERIOD_60MS = 0x05, /*!< Wake-Up timer 60ms */ - RFAL_WUM_PERIOD_70MS = 0x06, /*!< Wake-Up timer 70ms */ - RFAL_WUM_PERIOD_80MS = 0x07, /*!< Wake-Up timer 80ms */ - RFAL_WUM_PERIOD_100MS = 0x10, /*!< Wake-Up timer 100ms */ - RFAL_WUM_PERIOD_200MS = 0x11, /*!< Wake-Up timer 200ms */ - RFAL_WUM_PERIOD_300MS = 0x12, /*!< Wake-Up timer 300ms */ - RFAL_WUM_PERIOD_400MS = 0x13, /*!< Wake-Up timer 400ms */ - RFAL_WUM_PERIOD_500MS = 0x14, /*!< Wake-Up timer 500ms */ - RFAL_WUM_PERIOD_600MS = 0x15, /*!< Wake-Up timer 600ms */ - RFAL_WUM_PERIOD_700MS = 0x16, /*!< Wake-Up timer 700ms */ - RFAL_WUM_PERIOD_800MS = 0x17, /*!< Wake-Up timer 800ms */ -} rfalWumPeriod; - -/*! RFAL Wake-Up Period/Timer */ -typedef enum { - RFAL_WUM_AA_WEIGHT_4 = 0x00, /*!< Wake-Up Auto Average Weight 4 */ - RFAL_WUM_AA_WEIGHT_8 = 0x01, /*!< Wake-Up Auto Average Weight 8 */ - RFAL_WUM_AA_WEIGHT_16 = 0x02, /*!< Wake-Up Auto Average Weight 16 */ - RFAL_WUM_AA_WEIGHT_32 = 0x03, /*!< Wake-Up Auto Average Weight 32 */ -} rfalWumAAWeight; - -/*! RFAL Wake-Up Mode configuration */ -typedef struct { - rfalWumPeriod period; /*!< Wake-Up Timer period;how often measurement(s) is performed */ - bool irqTout; /*!< IRQ at every timeout will refresh the measurement(s) */ - bool swTagDetect; /*!< Use SW Tag Detection instead of HW Wake-Up mode */ - - struct { - bool enabled; /*!< Inductive Amplitude measurement enabled */ - uint8_t delta; /*!< Delta between the reference and measurement to wake-up */ - uint16_t reference; /*!< Reference to be used;RFAL_WUM_REFERENCE_AUTO sets it auto */ - bool autoAvg; /*!< Use the HW Auto Averaging feature */ - bool aaInclMeas; /*!< When AutoAvg is enabled, include IRQ measurement */ - rfalWumAAWeight aaWeight; /*!< When AutoAvg is enabled, last measure weight */ - } indAmp; /*!< Inductive Amplitude Configuration */ - struct { - bool enabled; /*!< Inductive Phase measurement enabled */ - uint8_t delta; /*!< Delta between the reference and measurement to wake-up */ - uint16_t reference; /*!< Reference to be used;RFAL_WUM_REFERENCE_AUTO sets it auto */ - bool autoAvg; /*!< Use the HW Auto Averaging feature */ - bool aaInclMeas; /*!< When AutoAvg is enabled, include IRQ measurement */ - rfalWumAAWeight aaWeight; /*!< When AutoAvg is enabled, last measure weight */ - } indPha; /*!< Inductive Phase Configuration */ - struct { - bool enabled; /*!< Capacitive measurement enabled */ - uint8_t delta; /*!< Delta between the reference and measurement to wake-up */ - uint16_t reference; /*!< Reference to be used;RFAL_WUM_REFERENCE_AUTO sets it auto */ - bool autoAvg; /*!< Use the HW Auto Averaging feature */ - bool aaInclMeas; /*!< When AutoAvg is enabled, include IRQ measurement */ - rfalWumAAWeight aaWeight; /*!< When AutoAvg is enabled, last measure weight */ - } cap; /*!< Capacitive Configuration */ -} rfalWakeUpConfig; - -/*******************************************************************************/ - -/* -****************************************************************************** -* GLOBAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -/*! - ***************************************************************************** - * \brief RFAL Initialize - * - * Initializes RFAL layer and the ST25R391x - * Ensures that ST25R391x is properly connected and returns error if any problem - * is detected - * - * \warning rfalAnalogConfigInitialize() should be called before so that - * the Analog config table has been previously initialized. - * - * \return ERR_HW_MISMATCH : Expected HW do not match or communication error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalInitialize(void); - -/*! - ***************************************************************************** - * \brief RFAL Calibrate - * - * Performs necessary calibration of RF chip in case it is indicated by current - * register settings. E.g. antenna calibration and regulator calibration - * - * \return ERR_WRONG_STATE : RFAL not initialized - * \return ERR_NONE : No error - * - ***************************************************************************** - */ -ReturnCode rfalCalibrate(void); - -/*! - ***************************************************************************** - * \brief RFAL Adjust Regulators - * - * Adjusts ST25R391x regulators - * - * \param[out] result : the result of the calibrate antenna in mV - * NULL if result not requested - * - * \return ERR_WRONG_STATE : RFAL not initialized - * \return ERR_NONE : No error - * - ***************************************************************************** - */ -ReturnCode rfalAdjustRegulators(uint16_t* result); - -/*! - ***************************************************************************** - * \brief RFAL Set System Callback - * - * Sets a callback for the driver to call when an event has occurred that - * may require the system to be notified - * - * \param[in] pFunc : method pointer for the upper layer callback - * - ***************************************************************************** - */ -void rfalSetUpperLayerCallback(rfalUpperLayerCallback pFunc); - -/*! - ***************************************************************************** - * \brief RFAL Set Pre Tx Callback - * - * Sets a callback for the driver to call before a Transceive - * - * \param[in] pFunc : method pointer for the Pre Tx callback - * - ***************************************************************************** - */ -void rfalSetPreTxRxCallback(rfalPreTxRxCallback pFunc); - -/*! - ***************************************************************************** - * \brief RFAL Set Post Tx Callback - * - * Sets a callback for the driver to call after a Transceive - * - * \param[in] pFunc : method pointer for the Post Tx callback - * - ***************************************************************************** - */ -void rfalSetPostTxRxCallback(rfalPostTxRxCallback pFunc); - -/** Set RFAL state changed callback - * - * @param cb RfalStateChangedCallback instance - * @param ctx pointer to context - */ -void rfal_set_state_changed_callback(RfalStateChangedCallback callback); - -/** Set callback context - * - * @param ctx pointer to context - */ -void rfal_set_callback_context(void* context); - -/*! - ***************************************************************************** - * \brief RFAL Deinitialize - * - * Deinitializes RFAL layer and the ST25R391x - * - * \return ERR_NONE : No error - * - ***************************************************************************** - */ -ReturnCode rfalDeinitialize(void); - -/*! - ***************************************************************************** - * \brief RFAL Set Mode - * - * Sets the mode that RFAL will operate on the following communications. - * Proper initializations will be performed on the ST25R391x - * - * \warning bit rate value RFAL_BR_KEEP is not allowed, only in rfalSetBitRate() - * - * \warning the mode will be applied immediately on the RFchip regardless of - * any ongoing operations like Transceive, ListenMode - * - * \param[in] mode : mode for the RFAL/RFchip to perform - * \param[in] txBR : transmit bit rate - * \param[in] rxBR : receive bit rate - * - * \see rfalIsGTExpired - * \see rfalMode - * - * \return ERR_WRONG_STATE : RFAL not initialized - * \return ERR_PARAM : Invalid parameter - * \return ERR_NONE : No error - * - ***************************************************************************** - */ -ReturnCode rfalSetMode(rfalMode mode, rfalBitRate txBR, rfalBitRate rxBR); - -/*! - ***************************************************************************** - * \brief RFAL Get Mode - * - * Gets the mode that RFAL is set to operate - * - * \see rfalMode - * - * \return rfalMode : The current RFAL mode - ***************************************************************************** - */ -rfalMode rfalGetMode(void); - -/*! - ***************************************************************************** - * \brief RFAL Set Bit Rate - * - * Sets the Tx and Rx bit rates with the given values - * The bit rate change is applied on the RF chip remaining in the same - * mode previous defined with rfalSetMode() - * - * If no mode is defined bit rates will not be applied and an error - * is returned - * - * \param[in] txBR : transmit bit rate - * \param[in] rxBR : receive bit rate - * - * \see rfalSetMode - * \see rfalMode - * \see rfalBitRate - * - * \return ERR_WRONG_STATE : RFAL not initialized - * \return ERR_PARAM : Invalid parameter - * \return ERR_NOT_IMPLEMENTED : Mode not implemented - * \return ERR_NONE : No error - * - ***************************************************************************** - */ -ReturnCode rfalSetBitRate(rfalBitRate txBR, rfalBitRate rxBR); - -/*! - ***************************************************************************** - * \brief RFAL Get Bit Rate - * - * Gets the Tx and Rx current bit rates - * - * If RFAL is not initialized or mode not set the bit rates return will - * be invalid RFAL_BR_KEEP - * - * \param[out] txBR : RFAL's current Tx Bit Rate - * \param[out] rxBR : RFAL's current Rx Bit Rate - * - * \see rfalSetBitRate - * \see rfalBitRate - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalGetBitRate(rfalBitRate* txBR, rfalBitRate* rxBR); - -/*! - ***************************************************************************** - * \brief Set Error Handling Mode - * - * Sets the error handling mode to be used by the RFAL - * - * \param[in] eHandling : the error handling mode - * - ***************************************************************************** - */ -void rfalSetErrorHandling(rfalEHandling eHandling); - -/*! - ***************************************************************************** - * \brief Get Error Handling Mode - * - * Gets the error handling mode currently used by the RFAL - * - * \return rfalEHandling : Current error handling mode - ***************************************************************************** - */ -rfalEHandling rfalGetErrorHandling(void); - -/*! - ***************************************************************************** - * \brief Set Observation Mode - * - * Sets ST25R391x observation modes for RF debug purposes - * - * \param[in] txMode : the observation mode to be used during transmission - * \param[in] rxMode : the observation mode to be used during reception - * - * \warning The Observation Mode is an advanced feature and should be set - * according to the documentation of the part number in use. - * Please refer to the corresponding Datasheet or Application Note(s) - ***************************************************************************** - */ -void rfalSetObsvMode(uint8_t txMode, uint8_t rxMode); - -/*! - ***************************************************************************** - * \brief Get Observation Mode - * - * Gets ST25R391x the current configured observation modes - * - * \param[in] txMode : the current observation mode configured for transmission - * \param[in] rxMode : the current observation mode configured for reception - * - ***************************************************************************** - */ -void rfalGetObsvMode(uint8_t* txMode, uint8_t* rxMode); - -/*! - ***************************************************************************** - * \brief Disable Observation Mode - * - * Disables the ST25R391x observation mode - ***************************************************************************** - */ -void rfalDisableObsvMode(void); - -/*! - ***************************************************************************** - * \brief RFAL Set FDT Poll - * - * Sets the Frame Delay Time (FDT) to be used on the following - * communications. - * - * FDT Poll is the minimum time following a Poll Frame during - * which no subsequent Poll Frame can be sent (without a response from - * the Listener in between) - * FDTx,PP,MIN - Digital 1.1 6.10.2 & 7.9.2 & 8.7.2 - * - * \param[in] FDTPoll : Frame Delay Time in 1/fc cycles - * - ***************************************************************************** - */ -void rfalSetFDTPoll(uint32_t FDTPoll); - -/*! - ***************************************************************************** - * \brief RFAL Set FDT Poll - * - * Gets the current Frame Delay Time (FDT) - * - * FDT Poll is the minimum time following a Poll Frame during - * which no subsequent Poll Frame can be sent (without a response from - * the Listener in between) - * FDTx,PP,MIN - Digital 1.1 6.10.2 & 7.9.2 & 8.7.2 - * - * \return FDT : current FDT value in 1/fc cycles - * - ***************************************************************************** - */ -uint32_t rfalGetFDTPoll(void); - -/*! - ***************************************************************************** - * \brief RFAL Set FDT Listen - * - * Sets the Frame Delay Time (FDT) Listen minimum to be used on the - * following communications. - * - * FDT Listen is the minimum time between a Poll Frame and a Listen Frame - * FDTx,LISTEN,MIN - Digital 1.1 6.10.1 & 7.9.1 & 8.7.1 - * - * \param[in] FDTListen : Frame Delay Time in 1/fc cycles - * - ***************************************************************************** - */ -void rfalSetFDTListen(uint32_t FDTListen); - -/*! - ***************************************************************************** - * \brief RFAL Set FDT Listen - * - * Gets the Frame Delay Time (FDT) Listen minimum - * - * FDT Listen is the minimum time between a Poll Frame and a Listen Frame - * FDTx,LISTEN,MIN - Digital 1.1 6.10.1 & 7.9.1 & 8.7.1 - * - * \return FDT : current FDT value in 1/fc cycles - * - ***************************************************************************** - */ -uint32_t rfalGetFDTListen(void); - -/*! - ***************************************************************************** - * \brief RFAL Get GT - * - * Gets the current Guard Time (GT) - * - * GT is the minimum time when a device in Listen Mode is exposed to an - * unmodulated carrier - * - * \return GT : Guard Time in 1/fc cycles - * - ***************************************************************************** - */ -uint32_t rfalGetGT(void); - -/*! - ***************************************************************************** - * \brief RFAL Set GT - * - * Sets the Guard Time (GT) to be used on the following communications. - * - * GT is the minimum time when a device in Listen Mode is exposed to an - * unmodulated carrier - * - * \param[in] GT : Guard Time in 1/fc cycles - * RFAL_GT_NONE if no GT should be applied - * - ***************************************************************************** - */ -void rfalSetGT(uint32_t GT); - -/*! - ***************************************************************************** - * \brief RFAL Is GT expired - * - * Checks whether the GT timer has expired - * - * \return true : GT has expired or not running - * \return false : GT is still running - * - ***************************************************************************** - */ -bool rfalIsGTExpired(void); - -/*! - ***************************************************************************** - * \brief RFAL Turn Field On and Start GT - * - * Turns the Field On, performing Initial Collision Avoidance - * - * After Field On, if GT was set before, it starts the GT timer to be - * used on the following communications. - * - * \return ERR_RF_COLLISION : External field detected - * \return ERR_NONE : Field turned On - * - ***************************************************************************** - */ -ReturnCode rfalFieldOnAndStartGT(void); - -/*! - ***************************************************************************** - * \brief RFAL Turn Field Off - * - * Turns the Field Off - * - * \return ERR_NONE : Field turned Off - ***************************************************************************** - */ -ReturnCode rfalFieldOff(void); - -/***************************************************************************** - * Transceive * - *****************************************************************************/ - -/*! - ***************************************************************************** - * \brief RFAL Set transceive context - * - * Set the context that will be used for the following Transceive - * Output and input buffers have to be passed and all other details prior to - * the Transceive itself has been started - * - * This method only sets the context, once set rfalWorker has - * to be executed until is done - * - * \param[in] ctx : the context for the following Transceive - * - * \see rfalWorker - * \see rfalGetTransceiveStatus - * - * \return ERR_NONE : Done with no error - * \return ERR_WRONG_STATE : Not initialized properly - * \return ERR_PARAM : Invalid parameter or configuration - ***************************************************************************** - */ -ReturnCode rfalStartTransceive(const rfalTransceiveContext* ctx); - -/*! - ***************************************************************************** - * \brief Get Transceive State - * - * Gets current Transceive internal State - * - * \return rfalTransceiveState : the current Transceive internal State - ***************************************************************************** - */ -rfalTransceiveState rfalGetTransceiveState(void); - -/*! - ***************************************************************************** - * \brief Get Transceive Status - * - * Gets current Transceive status - * - * \return ERR_NONE : Transceive done with no error - * \return ERR_BUSY : Transceive ongoing - * \return ERR_XXXX : Error occurred - * \return ERR_TIMEOUT : No response - * \return ERR_FRAMING : Framing error detected - * \return ERR_PAR : Parity error detected - * \return ERR_CRC : CRC error detected - * \return ERR_LINK_LOSS : Link Loss - External Field is Off - * \return ERR_RF_COLLISION : Collision detected - * \return ERR_IO : Internal error - ***************************************************************************** - */ -ReturnCode rfalGetTransceiveStatus(void); - -/*! - ***************************************************************************** - * \brief Is Transceive in Tx - * - * Checks if Transceive is in Transmission state - * - * \return true Transmission ongoing - * \return false Not in transmission state - ***************************************************************************** - */ -bool rfalIsTransceiveInTx(void); - -/*! - ***************************************************************************** - * \brief Is Transceive in Rx - * - * Checks if Transceive is in Reception state - * - * \return true Transmission done/reception ongoing - * \return false Not in reception state - ***************************************************************************** - */ -bool rfalIsTransceiveInRx(void); - -/*! - ***************************************************************************** - * \brief Get Transceive RSSI - * - * Gets the RSSI value of the last executed Transceive in mV - * - * \param[out] rssi : RSSI value - * - * \return ERR_NOTSUPP : Feature not supported - * \return ERR_PARAM : Invalid parameter - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalGetTransceiveRSSI(uint16_t* rssi); - -/*! - ***************************************************************************** - * \brief RFAL Worker - * - * This runs RFAL layer, which drives the actual Transceive procedure - * It MUST be executed frequently in order to execute the RFAL internal - * states and perform the requested operations - * - ***************************************************************************** - */ -void rfalWorker(void); - -/***************************************************************************** - * ISO1443A * - *****************************************************************************/ - -/*! - ***************************************************************************** - * \brief Transceives an ISO14443A ShortFrame - * - * Sends REQA to detect if there is any PICC in the field - * - * \param[in] txCmd: Command to be sent: - * 0x52 WUPA / ALL_REQ - * 0x26 REQA / SENS_REQ - * - * \param[in] txCmd : type of short frame to be sent REQA or WUPA - * \param[out] rxBuf : buffer to place the response - * \param[in] rxBufLen : length of rxBuf - * \param[out] rxRcvdLen: received length - * \param[in] fwt : Frame Waiting Time in 1/fc - * - * \warning If fwt is set to RFAL_FWT_NONE it will make endlessly for - * a response, which on a blocking method may not be the - * desired usage - * - * \return ERR_NONE if there is response - * \return ERR_TIMEOUT if there is no response - * \return ERR_COLLISION collision has occurred - * - ***************************************************************************** - */ -ReturnCode rfalISO14443ATransceiveShortFrame( - rfal14443AShortFrameCmd txCmd, - uint8_t* rxBuf, - uint8_t rxBufLen, - uint16_t* rxRcvdLen, - uint32_t fwt); - -/*! - ***************************************************************************** - * \brief Sends an ISO14443A Anticollision Frame - * - * This is use to perform ISO14443A anti-collision. - * \note Anticollision is sent without CRC - * - * - * \param[in] buf : reference to ANTICOLLISION command (with known UID if any) to be sent (also out param) - * reception will be place on this buf after bytesToSend - * \param[in] bytesToSend: reference number of full bytes to be sent (including CMD byte and SEL_PAR) - * if a collision occurs will contain the number of clear bytes - * \param[in] bitsToSend : reference to number of bits (0-7) to be sent; and received (also out param) - * if a collision occurs will indicate the number of clear bits (also out param) - * \param[out] rxLength : reference to the return the received length - * \param[in] fwt : Frame Waiting Time in 1/fc - * - * \return ERR_NONE if there is no error - ***************************************************************************** - */ -ReturnCode rfalISO14443ATransceiveAnticollisionFrame( - uint8_t* buf, - uint8_t* bytesToSend, - uint8_t* bitsToSend, - uint16_t* rxLength, - uint32_t fwt); - -/***************************************************************************** - * FeliCa * - *****************************************************************************/ - -/*! - ***************************************************************************** - * \brief FeliCa Poll - * - * Sends a Poll Request and collects all Poll Responses according to the - * given slots - * - * - * \param[in] slots : number of slots for the Poll Request - * \param[in] sysCode : system code (SC) for the Poll Request - * \param[in] reqCode : request code (RC) for the Poll Request - * \param[out] pollResList : list of all responses - * \param[in] pollResListSize : number of responses that can be placed in pollResList - * \param[out] devicesDetected : number of cards found - * \param[out] collisionsDetected: number of collisions detected - * - * \return ERR_NONE if there is no error - * \return ERR_TIMEOUT if there is no response - ***************************************************************************** - */ -ReturnCode rfalFeliCaPoll( - rfalFeliCaPollSlots slots, - uint16_t sysCode, - uint8_t reqCode, - rfalFeliCaPollRes* pollResList, - uint8_t pollResListSize, - uint8_t* devicesDetected, - uint8_t* collisionsDetected); - -/***************************************************************************** - * ISO15693 * - *****************************************************************************/ - -/*! - ***************************************************************************** - * \brief Sends an ISO15693 Anticollision Frame - * - * This send the Anticollision|Inventory frame (INVENTORY_REQ) - * - * \warning rxBuf must be able to contain the payload and CRC - * - * \param[in] txBuf : Buffer where outgoing message is located - * \param[in] txBufLen : Length of the outgoing message in bytes - * \param[out] rxBuf : Buffer where incoming message will be placed - * \param[in] rxBufLen : Maximum length of the incoming message in bytes - * \param[out] actLen : Actual received length in bits - * - * \return ERR_NONE : Transceive done with no error - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_IO : Internal error - ***************************************************************************** - */ -ReturnCode rfalISO15693TransceiveAnticollisionFrame( - uint8_t* txBuf, - uint8_t txBufLen, - uint8_t* rxBuf, - uint8_t rxBufLen, - uint16_t* actLen); - -/*! - ***************************************************************************** - * \brief Sends an ISO15693 Anticollision EOF - * - * This sends the Anticollision|Inventory EOF used as a slot marker - * - * \warning rxBuf must be able to contain the payload and CRC - * - * \param[out] rxBuf : Buffer where incoming message will be placed - * \param[in] rxBufLen : Maximum length of the incoming message in bytes - * \param[out] actLen : Actual received length in bits - * - * \return ERR_NONE : Transceive done with no error - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_IO : Internal error - ***************************************************************************** - */ -ReturnCode - rfalISO15693TransceiveEOFAnticollision(uint8_t* rxBuf, uint8_t rxBufLen, uint16_t* actLen); - -/*! - ***************************************************************************** - * \brief Sends an ISO15693 EOF - * - * This is method sends an ISO15693 (EoF) used for a Write operation - * - * \warning rxBuf must be able to contain the payload and CRC - * - * \param[out] rxBuf : Buffer where incoming message will be placed - * \param[in] rxBufLen : Maximum length of the incoming message in bytes - * \param[out] actLen : Actual received length in bytes - * - * \return ERR_NONE : Transceive done with no error - * \return ERR_IO : Internal error - ***************************************************************************** - */ -ReturnCode rfalISO15693TransceiveEOF(uint8_t* rxBuf, uint8_t rxBufLen, uint16_t* actLen); - -/*! - ***************************************************************************** - * \brief Transceive Blocking Tx - * - * This is method triggers a Transceive and executes it blocking until the - * Tx has been completed - * - * \param[in] txBuf : Buffer where outgoing message is located - * \param[in] txBufLen : Length of the outgoing message in bytes - * \param[out] rxBuf : Buffer where incoming message will be placed - * \param[in] rxBufLen : Maximum length of the incoming message in bytes - * \param[out] actLen : Actual received length in bits - * \param[in] flags : TransceiveFlags indication special handling - * \param[in] fwt : Frame Waiting Time in 1/fc - * - * \return ERR_NONE : Transceive done with no error - * \return ERR_BUSY : Transceive ongoing - * \return ERR_XXXX : Error occurred - * \return ERR_LINK_LOSS : Link Loss - External Field is Off - * \return ERR_RF_COLLISION : Collision detected - * \return ERR_IO : Internal error - ***************************************************************************** - */ -ReturnCode rfalTransceiveBlockingTx( - uint8_t* txBuf, - uint16_t txBufLen, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* actLen, - uint32_t flags, - uint32_t fwt); - -/*! - ***************************************************************************** - * \brief Transceive Blocking Rx - * - * This is method executes the reception of an ongoing Transceive triggered - * before by rfalTransceiveBlockingTx() - * - * \return ERR_NONE : Transceive done with no error - * \return ERR_BUSY : Transceive ongoing - * \return ERR_XXXX : Error occurred - * \return ERR_TIMEOUT : No response - * \return ERR_FRAMING : Framing error detected - * \return ERR_PAR : Parity error detected - * \return ERR_CRC : CRC error detected - * \return ERR_LINK_LOSS : Link Loss - External Field is Off - * \return ERR_RF_COLLISION : Collision detected - * \return ERR_IO : Internal error - ***************************************************************************** - */ -ReturnCode rfalTransceiveBlockingRx(void); - -/*! - ***************************************************************************** - * \brief Transceive Blocking - * - * This is method triggers a Transceive and executes it blocking until it - * has been completed - * - * \param[in] txBuf : Buffer where outgoing message is located - * \param[in] txBufLen : Length of the outgoing message in bytes - * \param[out] rxBuf : Buffer where incoming message will be placed - * \param[in] rxBufLen : Maximum length of the incoming message in bytes - * \param[out] actLen : Actual received length in bytes - * \param[in] flags : TransceiveFlags indication special handling - * \param[in] fwt : Frame Waiting Time in 1/fc - * - * \return ERR_NONE : Transceive done with no error - * \return ERR_BUSY : Transceive ongoing - * \return ERR_XXXX : Error occurred - * \return ERR_TIMEOUT : No response - * \return ERR_FRAMING : Framing error detected - * \return ERR_PAR : Parity error detected - * \return ERR_CRC : CRC error detected - * \return ERR_LINK_LOSS : Link Loss - External Field is Off - * \return ERR_RF_COLLISION : Collision detected - * \return ERR_IO : Internal error - ***************************************************************************** - */ -ReturnCode rfalTransceiveBlockingTxRx( - uint8_t* txBuf, - uint16_t txBufLen, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* actLen, - uint32_t flags, - uint32_t fwt); - -ReturnCode rfalTransceiveBitsBlockingTx( - uint8_t* txBuf, - uint16_t txBufLen, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* actLen, - uint32_t flags, - uint32_t fwt); - -/***************************************************************************** - * Listen Mode * - *****************************************************************************/ - -/*! - ***************************************************************************** - * \brief Is external Field On - * - * Checks if external field (other peer/device) is on/detected - * - * \return true External field is On - * \return false No external field is detected - * - ***************************************************************************** - */ -bool rfalIsExtFieldOn(void); - -/*! - ***************************************************************************** - * \brief Listen Mode start - * - * Configures RF Chip to go into listen mode enabling the given technologies - * - * - * \param[in] lmMask: mask with the enabled/disabled listen modes - * use: RFAL_LM_MASK_NFCA ; RFAL_LM_MASK_NFCB ; - * RFAL_LM_MASK_NFCF ; RFAL_LM_MASK_ACTIVE_P2P - * \param[in] confA: pointer to Passive A configurations (NULL if disabled) - * \param[in] confB: pointer to Passive B configurations (NULL if disabled) - * \param[in] confF: pointer to Passive F configurations (NULL if disabled) - * \param[in] rxBuf: buffer to place incoming data - * \param[in] rxBufLen: length in bits of rxBuf - * \param[in] rxLen: pointer to write the data length in bits placed into rxBuf - * - * - * \return ERR_PARAM Invalid parameter - * \return ERR_REQUEST Invalid listen mode mask - * \return ERR_NONE Done with no error - * - ***************************************************************************** - */ -ReturnCode rfalListenStart( - uint32_t lmMask, - const rfalLmConfPA* confA, - const rfalLmConfPB* confB, - const rfalLmConfPF* confF, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rxLen); - -/*! - ***************************************************************************** - * \brief Listen Mode start Sleeping - * - * - ***************************************************************************** - */ -ReturnCode - rfalListenSleepStart(rfalLmState sleepSt, uint8_t* rxBuf, uint16_t rxBufLen, uint16_t* rxLen); - -/*! - ***************************************************************************** - * \brief Listen Mode Stop - * - * Disables the listen mode on the RF Chip - * - * \warning the listen mode will be disabled immediately on the RFchip regardless - * of any ongoing operations like Transceive - * - * \return ERR_NONE Done with no error - * - ***************************************************************************** - */ -ReturnCode rfalListenStop(void); - -/*! - ***************************************************************************** - * \brief Listen Mode get state - * - * Sets the new state of the Listen Mode and applies the necessary changes - * on the RF Chip - * - * \param[out] dataFlag: indicates that Listen Mode has rcvd data and caller - * must process it. The received message is located - * at the rxBuf passed on rfalListenStart(). - * rfalListenSetState() will clear this flag - * if NULL output parameter will no be written/returned - * \param[out] lastBR: bit rate detected of the last initiator request - * if NULL output parameter will no be written/returned - * - * \return rfalLmState RFAL_LM_STATE_NOT_INIT : LM not initialized properly - * Any Other : LM State - * - ***************************************************************************** - */ -rfalLmState rfalListenGetState(bool* dataFlag, rfalBitRate* lastBR); - -/*! - ***************************************************************************** - * \brief Listen Mode set state - * - * Sets the new state of the Listen Mode and applies the necessary changes - * on the RF Chip - * - * \param[in] newSt : New state to go to - * - * \return ERR_WRONG_STATE : Not initialized properly - * \return ERR_PARAM : Invalid parameter - * \return ERR_NONE : Done with no error - * - ***************************************************************************** - */ -ReturnCode rfalListenSetState(rfalLmState newSt); - -/***************************************************************************** - * Wake-Up Mode * - *****************************************************************************/ - -/*! - ***************************************************************************** - * \brief Wake-Up Mode Start - * - * Sets the RF Chip in Low Power Wake-Up Mode according to the given - * configuration. - * - * \param[in] config : Generic Wake-Up configuration provided by lower - * layers. If NULL will automatically configure the - * Wake-Up mode - * - * \return ERR_WRONG_STATE : Not initialized properly - * \return ERR_PARAM : Invalid parameter - * \return ERR_NONE : Done with no error - * - ***************************************************************************** - */ -ReturnCode rfalWakeUpModeStart(const rfalWakeUpConfig* config); - -/*! - ***************************************************************************** - * \brief Wake-Up Has Woke - * - * Returns true if the Wake-Up mode is enabled and it has already received - * the indication from the RF Chip that the surrounding environment has changed - * and flagged at least one wake-Up interrupt - * - * \return true : Wake-Up mode enabled and has received a wake-up IRQ - * \return false : no Wake-Up IRQ has been received - * - ***************************************************************************** - */ -bool rfalWakeUpModeHasWoke(void); - -/*! - ***************************************************************************** - * \brief Wake-Up Mode Stop - * - * Stops the Wake-Up Mode - * - * \return ERR_WRONG_STATE : Not initialized properly - * \return ERR_PARAM : Invalid parameter - * \return ERR_NONE : Done with no error - * - ***************************************************************************** - */ -ReturnCode rfalWakeUpModeStop(void); - -/*! - ***************************************************************************** - * \brief Low Power Mode Start - * - * Sets the RF Chip in Low Power Mode. - * In this mode the RF Chip is placed in Low Power Mode, similar to Wake-up - * mode but no operation nor period measurement is performed. - * Mode must be terminated by rfalLowPowerModeStop() - * - * \return ERR_WRONG_STATE : Not initialized properly - * \return ERR_PARAM : Invalid parameter - * \return ERR_NONE : Done with no error - * - ***************************************************************************** - */ -ReturnCode rfalLowPowerModeStart(void); - -/*! - ***************************************************************************** - * \brief Low Power Mode Stop - * - * Stops the Low Power Mode re-enabling the device - * - * \return ERR_WRONG_STATE : Not initialized properly - * \return ERR_PARAM : Invalid parameter - * \return ERR_NONE : Done with no error - * - ***************************************************************************** - */ -ReturnCode rfalLowPowerModeStop(void); - -#endif /* RFAL_RF_H */ - -/** - * @} - * - * @} - * - * @} - */ diff --git a/lib/ST25RFAL002/include/rfal_st25tb.h b/lib/ST25RFAL002/include/rfal_st25tb.h deleted file mode 100644 index dcd7baa5789..00000000000 --- a/lib/ST25RFAL002/include/rfal_st25tb.h +++ /dev/null @@ -1,340 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_st25tb.h - * - * \author Gustavo Patricio - * - * \brief Implementation of ST25TB interface - * - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-AL - * \brief RFAL Abstraction Layer - * @{ - * - * \addtogroup ST25TB - * \brief RFAL ST25TB Module - * @{ - * - */ - -#ifndef RFAL_ST25TB_H -#define RFAL_ST25TB_H - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "platform.h" -#include "st_errno.h" -#include "rfal_rf.h" -#include "rfal_nfcb.h" - -/* - ****************************************************************************** - * GLOBAL DEFINES - ****************************************************************************** - */ - -#define RFAL_ST25TB_CHIP_ID_LEN 1U /*!< ST25TB chip ID length */ -#define RFAL_ST25TB_CRC_LEN 2U /*!< ST25TB CRC length */ -#define RFAL_ST25TB_UID_LEN 8U /*!< ST25TB Unique ID length */ -#define RFAL_ST25TB_BLOCK_LEN 4U /*!< ST25TB Data Block length */ - -/* -****************************************************************************** -* GLOBAL MACROS -****************************************************************************** -*/ - -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ -typedef uint8_t rfalSt25tbUID[RFAL_ST25TB_UID_LEN]; /*!< ST25TB UID type */ -typedef uint8_t rfalSt25tbBlock[RFAL_ST25TB_BLOCK_LEN]; /*!< ST25TB Block type */ - -/*! ST25TB listener device (PICC) struct */ -typedef struct { - uint8_t chipID; /*!< Device's session Chip ID */ - rfalSt25tbUID UID; /*!< Device's UID */ - bool isDeselected; /*!< Device deselect flag */ -} rfalSt25tbListenDevice; - -/* -****************************************************************************** -* GLOBAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -/*! - ***************************************************************************** - * \brief Initialize ST25TB Poller mode - * - * This methods configures RFAL RF layer to perform as a - * ST25TB Poller/RW including all default timings - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalSt25tbPollerInitialize(void); - -/*! - ***************************************************************************** - * \brief ST25TB Poller Check Presence - * - * This method checks if a ST25TB Listen device (PICC) is present on the field - * by sending an Initiate command - * - * \param[out] chipId : if successfully retrieved, the device's chip ID - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error, no listener device detected - * \return ERR_RF_COLLISION : Collision detected one or more device in the field - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalSt25tbPollerCheckPresence(uint8_t* chipId); - -/*! - ***************************************************************************** - * \brief ST25TB Poller Collision Resolution - * - * This method performs ST25TB Collision resolution, selects the each device, - * retrieves its UID and then deselects. - * In case only one device is identified the ST25TB device is left in select - * state. - * - * \param[in] devLimit : device limit value, and size st25tbDevList - * \param[out] st25tbDevList : ST35TB listener device info - * \param[out] devCnt : Devices found counter - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error, no listener device detected - * \return ERR_RF_COLLISION : Collision detected one or more device in the field - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalSt25tbPollerCollisionResolution( - uint8_t devLimit, - rfalSt25tbListenDevice* st25tbDevList, - uint8_t* devCnt); - -/*! - ***************************************************************************** - * \brief ST25TB Poller Initiate - * - * This method sends an Initiate command - * - * If a single device responds the chip ID will be retrieved - * - * \param[out] chipId : chip ID of the device - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error, no listener device detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalSt25tbPollerInitiate(uint8_t* chipId); - -/*! - ***************************************************************************** - * \brief ST25TB Poller Pcall - * - * This method sends a Pcall command - * If successful the device's chip ID will be retrieved - * - * \param[out] chipId : Chip ID of the device - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error, no listener device detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalSt25tbPollerPcall(uint8_t* chipId); - -/*! - ***************************************************************************** - * \brief ST25TB Poller Slot Marker - * - * This method sends a Slot Marker - * - * If a single device responds the chip ID will be retrieved - * - * \param[in] slotNum : Slot Number - * \param[out] chipIdRes : Chip ID of the device - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error, no listener device detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalSt25tbPollerSlotMarker(uint8_t slotNum, uint8_t* chipIdRes); - -/*! - ***************************************************************************** - * \brief ST25TB Poller Select - * - * This method sends a ST25TB Select command with the given chip ID. - * - * If the device is already in Selected state and receives an incorrect chip - * ID, it goes into Deselected state - * - * \param[in] chipId : chip ID of the device to be selected - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error, no listener device detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalSt25tbPollerSelect(uint8_t chipId); - -/*! - ***************************************************************************** - * \brief ST25TB Get UID - * - * This method sends a Get_UID command - * - * If a single device responds the chip UID will be retrieved - * - * \param[out] UID : UID of the found device - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error, no listener device detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalSt25tbPollerGetUID(rfalSt25tbUID* UID); - -/*! - ***************************************************************************** - * \brief ST25TB Poller Read Block - * - * This method reads a block of the ST25TB - * - * \param[in] blockAddress : address of the block to be read - * \param[out] blockData : location to place the data read from block - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error, no listener device detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalSt25tbPollerReadBlock(uint8_t blockAddress, rfalSt25tbBlock* blockData); - -/*! - ***************************************************************************** - * \brief ST25TB Poller Write Block - * - * This method writes a block of the ST25TB - * - * \param[in] blockAddress : address of the block to be written - * \param[in] blockData : data to be written on the block - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error, no listener device detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalSt25tbPollerWriteBlock(uint8_t blockAddress, const rfalSt25tbBlock* blockData); - -/*! - ***************************************************************************** - * \brief ST25TB Poller Completion - * - * This method sends a completion command to the ST25TB. After the - * completion the card no longer will reply to any command. - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error, no listener device detected - * \return ERR_PROTO : Protocol error detected, invalid SENSB_RES received - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalSt25tbPollerCompletion(void); - -/*! - ***************************************************************************** - * \brief ST25TB Poller Reset to Inventory - * - * This method sends a Reset to Inventory command to the ST25TB. - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_TIMEOUT : Timeout error, no listener device detected - * \return ERR_PROTO : Protocol error detected, invalid SENSB_RES received - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalSt25tbPollerResetToInventory(void); - -#endif /* RFAL_ST25TB_H */ - -/** - * @} - * - * @} - * - * @} - */ diff --git a/lib/ST25RFAL002/include/rfal_st25xv.h b/lib/ST25RFAL002/include/rfal_st25xv.h deleted file mode 100644 index d7bec837795..00000000000 --- a/lib/ST25RFAL002/include/rfal_st25xv.h +++ /dev/null @@ -1,844 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_st25xv.h - * - * \author Gustavo Patricio - * - * \brief NFC-V ST25 NFC-V Tag specific features - * - * This module provides support for ST's specific features available on - * NFC-V (ISO15693) tag families: ST25D, ST25TV, M24LR - * - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-AL - * \brief RFAL Abstraction Layer - * @{ - * - * \addtogroup ST25xV - * \brief RFAL ST25xV Module - * @{ - * - */ - -#ifndef RFAL_ST25xV_H -#define RFAL_ST25xV_H - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "platform.h" -#include "st_errno.h" -#include "rfal_nfc.h" -#include "rfal_rf.h" - -/* - ****************************************************************************** - * GLOBAL DEFINES - ****************************************************************************** - */ - -#define RFAL_NFCV_BLOCKNUM_M24LR_LEN \ - 2U /*!< Block Number length of MR24LR tags: 16 bits */ -#define RFAL_NFCV_ST_IC_MFG_CODE \ - 0x02 /*!< ST IC Mfg code (used for custom commands) */ - -/*! - ***************************************************************************** - * \brief NFC-V Poller Read Single Block (M24LR) - * - * Reads a Single Block from a M24LR tag which has the number of blocks - * bigger than 256 (M24LR16 ; M24LR64) - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * default: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[in] blockNum : Number of the block to read (16 bits) - * \param[out] rxBuf : buffer to store response (also with RES_FLAGS) - * \param[in] rxBufLen : length of rxBuf - * \param[out] rcvLen : number of bytes received - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerM24LRReadSingleBlock( - uint8_t flags, - const uint8_t* uid, - uint16_t blockNum, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Fast Read Single Block (M24LR) - * - * Reads a Single Block from a M24LR tag which has the number of blocks - * bigger than 256 (M24LR16 ; M24LR64) using ST Fast mode - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * default: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[in] blockNum : Number of the block to read (16 bits) - * \param[out] rxBuf : buffer to store response (also with RES_FLAGS) - * \param[in] rxBufLen : length of rxBuf - * \param[out] rcvLen : number of bytes received - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerM24LRFastReadSingleBlock( - uint8_t flags, - const uint8_t* uid, - uint16_t blockNum, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Write Single Block (M24LR) - * - * Writes a Single Block from a M24LR tag which has the number of blocks - * bigger than 256 (M24LR16 ; M24LR64) - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be written - * if not provided Select mode will be used - * \param[in] blockNum : Number of the block to write (16 bits) - * \param[in] wrData : data to be written on the given block - * \param[in] blockLen : number of bytes of a block - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerM24LRWriteSingleBlock( - uint8_t flags, - const uint8_t* uid, - uint16_t blockNum, - const uint8_t* wrData, - uint8_t blockLen); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Read Multiple Blocks (M24LR) - * - * Reads Multiple Blocks from a device from a M24LR tag which has the number of blocks - * bigger than 256 (M24LR16 ; M24LR64) - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[in] firstBlockNum : first block to be read (16 bits) - * \param[in] numOfBlocks : number of block to read - * \param[out] rxBuf : buffer to store response (also with RES_FLAGS) - * \param[in] rxBufLen : length of rxBuf - * \param[out] rcvLen : number of bytes received - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerM24LRReadMultipleBlocks( - uint8_t flags, - const uint8_t* uid, - uint16_t firstBlockNum, - uint8_t numOfBlocks, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Fast Read Multiple Blocks (M24LR) - * - * Reads Multiple Blocks from a device from a M24LR tag which has the number of blocks - * bigger than 256 (M24LR16 ; M24LR64) using ST Fast mode - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[in] firstBlockNum : first block to be read (16 bits) - * \param[in] numOfBlocks : number of block to read - * \param[out] rxBuf : buffer to store response (also with RES_FLAGS) - * \param[in] rxBufLen : length of rxBuf - * \param[out] rcvLen : number of bytes received - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerM24LRFastReadMultipleBlocks( - uint8_t flags, - const uint8_t* uid, - uint16_t firstBlockNum, - uint8_t numOfBlocks, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Fast Read Single Block - * - * Reads a Single Block from a device (VICC) using ST Fast mode - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[in] blockNum : Number of the block to read - * \param[out] rxBuf : buffer to store response (also with RES_FLAGS) - * \param[in] rxBufLen : length of rxBuf - * \param[out] rcvLen : number of bytes received - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerFastReadSingleBlock( - uint8_t flags, - const uint8_t* uid, - uint8_t blockNum, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Fast Read Multiple Blocks - * - * Reads Multiple Blocks from a device (VICC) using ST Fast mode - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[in] firstBlockNum : first block to be read - * \param[in] numOfBlocks : number of block to read - * \param[out] rxBuf : buffer to store response (also with RES_FLAGS) - * \param[in] rxBufLen : length of rxBuf - * \param[out] rcvLen : number of bytes received - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerFastReadMultipleBlocks( - uint8_t flags, - const uint8_t* uid, - uint8_t firstBlockNum, - uint8_t numOfBlocks, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Fast Extended Read Single Block - * - * Reads a Single Block from a device (VICC) supporting extended commands using ST Fast mode - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[in] blockNum : Number of the block to read (16 bits) - * \param[out] rxBuf : buffer to store response (also with RES_FLAGS) - * \param[in] rxBufLen : length of rxBuf - * \param[out] rcvLen : number of bytes received - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerFastExtendedReadSingleBlock( - uint8_t flags, - const uint8_t* uid, - uint16_t blockNum, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Fast Extended Read Multiple Blocks - * - * Reads Multiple Blocks from a device (VICC) supporting extended commands using ST Fast mode - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[in] firstBlockNum : first block to be read (16 bits) - * \param[in] numOfBlocks : number of consecutive blocks to read (16 bits) - * \param[out] rxBuf : buffer to store response (also with RES_FLAGS) - * \param[in] rxBufLen : length of rxBuf - * \param[out] rcvLen : number of bytes received - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerFastExtReadMultipleBlocks( - uint8_t flags, - const uint8_t* uid, - uint16_t firstBlockNum, - uint16_t numOfBlocks, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Read Configuration - * - * Reads static configuration registers at the Pointer address - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[in] pointer : Pointer address - * \param[out] regValue : Register value - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerReadConfiguration( - uint8_t flags, - const uint8_t* uid, - uint8_t pointer, - uint8_t* regValue); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Write Configuration - * - * Writes static configuration registers at the Pointer address - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[in] pointer : Pointer address - * \param[in] regValue : Register value - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerWriteConfiguration( - uint8_t flags, - const uint8_t* uid, - uint8_t pointer, - uint8_t regValue); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Read Dynamic Configuration - * - * Reads dynamic registers at the Pointer address - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[in] pointer : Pointer address - * \param[out] regValue : Register value - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerReadDynamicConfiguration( - uint8_t flags, - const uint8_t* uid, - uint8_t pointer, - uint8_t* regValue); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Write Dynamic Configuration - * - * Writes dynamic registers at the Pointer address - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[in] pointer : Pointer address - * \param[in] regValue : Register value - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerWriteDynamicConfiguration( - uint8_t flags, - const uint8_t* uid, - uint8_t pointer, - uint8_t regValue); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Fast Read Dynamic Configuration - * - * Reads dynamic registers at the Pointer address using ST Fast mode - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[in] pointer : Pointer address - * \param[out] regValue : Register value - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerFastReadDynamicConfiguration( - uint8_t flags, - const uint8_t* uid, - uint8_t pointer, - uint8_t* regValue); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Fast Write Dynamic Configuration - * - * Writes dynamic registers at the Pointer address using ST Fast mode - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[in] pointer : Pointer address - * \param[in] regValue : Register value - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerFastWriteDynamicConfiguration( - uint8_t flags, - const uint8_t* uid, - uint8_t pointer, - uint8_t regValue); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Present Password - * - * Sends the Present Password command - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[in] pwdNum : Password number - * \param[in] pwd : Password - * \param[in] pwdLen : Password length - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerPresentPassword( - uint8_t flags, - const uint8_t* uid, - uint8_t pwdNum, - const uint8_t* pwd, - uint8_t pwdLen); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Get Random Number - * - * Returns a 16 bit random number - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[out] rxBuf : buffer to store response (also with RES_FLAGS) - * \param[in] rxBufLen : length of rxBuf - * \param[out] rcvLen : number of bytes received - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerGetRandomNumber( - uint8_t flags, - const uint8_t* uid, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Read Message length - * - * Sends a Read Message Length message to retrieve the value of MB_LEN_Dyn - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[out] msgLen : Message Length - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerReadMessageLength(uint8_t flags, const uint8_t* uid, uint8_t* msgLen); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Fast Read Message length - * - * Sends a Fast Read Message Length message to retrieve the value of MB_LEN_Dyn using ST Fast mode. - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[out] msgLen : Message Length - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerFastReadMsgLength(uint8_t flags, const uint8_t* uid, uint8_t* msgLen); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Read Message - * - * Reads up to 256 bytes in the Mailbox from the location - * specified by MBpointer and sends back their value in the rxBuf response. - * First MailBox location is '00'. When Number of bytes is set to 00h - * and MBPointer is equals to 00h, the MB_LEN bytes of the full message - * are returned. Otherwise, Read Message command returns (Number of Bytes + 1) bytes - * (i.e. 01h returns 2 bytes, FFh returns 256 bytes). - * An error is reported if (Pointer + Nb of bytes + 1) is greater than the message length. - * RF Reading of the last byte of the mailbox message automatically clears b1 - * of MB_CTRL_Dyn HOST_PUT_MSG, and allows RF to put a new message. - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[in] mbPointer : MPpointer - * \param[in] numBytes : number of bytes - * \param[out] rxBuf : buffer to store response (also with RES_FLAGS) - * \param[in] rxBufLen : length of rxBuf - * \param[out] rcvLen : number of bytes received - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerReadMessage( - uint8_t flags, - const uint8_t* uid, - uint8_t mbPointer, - uint8_t numBytes, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Fast Read Message - * - * Reads up to 256 bytes in the Mailbox from the location - * specified by MBpointer and sends back their value in the rxBuf response using ST Fast mode. - * First MailBox location is '00'. When Number of bytes is set to 00h - * and MBPointer is equals to 00h, the MB_LEN bytes of the full message - * are returned. Otherwise, Read Message command returns (Number of Bytes + 1) bytes - * (i.e. 01h returns 2 bytes, FFh returns 256 bytes). - * An error is reported if (Pointer + Nb of bytes + 1) is greater than the message length. - * RF Reading of the last byte of the mailbox message automatically clears b1 - * of MB_CTRL_Dyn HOST_PUT_MSG, and allows RF to put a new message. - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[in] mbPointer : MPpointer - * \param[in] numBytes : number of bytes - * \param[out] rxBuf : buffer to store response (also with RES_FLAGS) - * \param[in] rxBufLen : length of rxBuf - * \param[out] rcvLen : number of bytes received - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerFastReadMessage( - uint8_t flags, - const uint8_t* uid, - uint8_t mbPointer, - uint8_t numBytes, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Write Message - * - * Sends Write message Command - * - * On receiving the Write Message command, the ST25DVxxx puts the data contained - * in the request into the Mailbox buffer, update the MB_LEN_Dyn register, and - * set bit RF_PUT_MSG in MB_CTRL_Dyn register. It then reports if the write operation was successful - * in the response. The ST25DVxxx Mailbox contains up to 256 data bytes which are filled from the - * first location '00'. MSGlength parameter of the command is the number of - * Data bytes minus 1 (00 for 1 byte of data, FFh for 256 bytes of data). - * Write Message could be executed only when Mailbox is accessible by RF. - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[in] msgLen : MSGLen number of Data bytes minus 1 - * \param[in] msgData : Message Data - * \param[out] txBuf : buffer to used to build the Write Message command - * \param[in] txBufLen : length of txBuf - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerWriteMessage( - uint8_t flags, - const uint8_t* uid, - uint8_t msgLen, - const uint8_t* msgData, - uint8_t* txBuf, - uint16_t txBufLen); - -/*! - ***************************************************************************** - * \brief NFC-V Poller Fast Write Message - * - * Sends Fast Write message Command using ST Fast mode - * - * On receiving the Write Message command, the ST25DVxxx puts the data contained - * in the request into the Mailbox buffer, update the MB_LEN_Dyn register, and - * set bit RF_PUT_MSG in MB_CTRL_Dyn register. It then reports if the write operation was successful - * in the response. The ST25DVxxx Mailbox contains up to 256 data bytes which are filled from the - * first location '00'. MSGlength parameter of the command is the number of - * Data bytes minus 1 (00 for 1 byte of data, FFh for 256 bytes of data). - * Write Message could be executed only when Mailbox is accessible by RF. - * - * \param[in] flags : Flags to be used: Sub-carrier; Data_rate; Option - * for NFC-Forum use: RFAL_NFCV_REQ_FLAG_DEFAULT - * \param[in] uid : UID of the device to be put to be read - * if not provided Select mode will be used - * \param[in] msgLen : MSGLen number of Data bytes minus 1 - * \param[in] msgData : Message Data - * \param[out] txBuf : buffer to used to build the Write Message command - * \param[in] txBufLen : length of txBuf - * - * \return ERR_WRONG_STATE : RFAL not initialized or incorrect mode - * \return ERR_PARAM : Invalid parameters - * \return ERR_IO : Generic internal error - * \return ERR_CRC : CRC error detected - * \return ERR_FRAMING : Framing error detected - * \return ERR_PROTO : Protocol error detected - * \return ERR_TIMEOUT : Timeout error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalST25xVPollerFastWriteMessage( - uint8_t flags, - const uint8_t* uid, - uint8_t msgLen, - const uint8_t* msgData, - uint8_t* txBuf, - uint16_t txBufLen); - -#endif /* RFAL_ST25xV_H */ - -/** - * @} - * - * @} - * - * @} - */ diff --git a/lib/ST25RFAL002/include/rfal_t1t.h b/lib/ST25RFAL002/include/rfal_t1t.h deleted file mode 100644 index e9e39d23c2c..00000000000 --- a/lib/ST25RFAL002/include/rfal_t1t.h +++ /dev/null @@ -1,178 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_t1t.h - * - * \author Gustavo Patricio - * - * \brief Provides NFC-A T1T convenience methods and definitions - * - * This module provides an interface to perform as a NFC-A Reader/Writer - * to handle a Type 1 Tag T1T (Topaz) - * - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-AL - * \brief RFAL Abstraction Layer - * @{ - * - * \addtogroup T1T - * \brief RFAL T1T Module - * @{ - * - */ - -#ifndef RFAL_T1T_H -#define RFAL_T1T_H - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "platform.h" -#include "st_errno.h" -#include "rfal_rf.h" - -/* - ****************************************************************************** - * GLOBAL DEFINES - ****************************************************************************** - */ -#define RFAL_T1T_UID_LEN 4 /*!< T1T UID length of cascade level 1 only tag */ -#define RFAL_T1T_HR_LENGTH 2 /*!< T1T HR(Header ROM) length */ - -#define RFAL_T1T_HR0_NDEF_MASK 0xF0 /*!< T1T HR0 NDEF capability mask T1T 1.2 2.2.2 */ -#define RFAL_T1T_HR0_NDEF_SUPPORT 0x10 /*!< T1T HR0 NDEF capable value T1T 1.2 2.2.2 */ - -/*! NFC-A T1T (Topaz) command set */ -typedef enum { - RFAL_T1T_CMD_RID = 0x78, /*!< T1T Read UID */ - RFAL_T1T_CMD_RALL = 0x00, /*!< T1T Read All */ - RFAL_T1T_CMD_READ = 0x01, /*!< T1T Read */ - RFAL_T1T_CMD_WRITE_E = 0x53, /*!< T1T Write with erase (single byte) */ - RFAL_T1T_CMD_WRITE_NE = 0x1A /*!< T1T Write with no erase (single byte) */ -} rfalT1Tcmds; - -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ - -/*! NFC-A T1T (Topaz) RID_RES Digital 1.1 10.6.2 & Table 50 */ -typedef struct { - uint8_t hr0; /*!< T1T Header ROM: HR0 */ - uint8_t hr1; /*!< T1T Header ROM: HR1 */ - uint8_t uid[RFAL_T1T_UID_LEN]; /*!< T1T UID */ -} rfalT1TRidRes; - -/* -****************************************************************************** -* GLOBAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -/*! - ***************************************************************************** - * \brief Initialize NFC-A T1T Poller mode - * - * This methods configures RFAL RF layer to perform as a - * NFC-A T1T Poller/RW (Topaz) including all default timings - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalT1TPollerInitialize(void); - -/*! - ***************************************************************************** - * \brief NFC-A T1T Poller RID - * - * This method reads the UID of a NFC-A T1T Listener device - * - * - * \param[out] ridRes : pointer to place the RID_RES - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_PARAM : Invalid parameter - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalT1TPollerRid(rfalT1TRidRes* ridRes); - -/*! - ***************************************************************************** - * \brief NFC-A T1T Poller RALL - * - * This method send a Read All command to a NFC-A T1T Listener device - * - * - * \param[in] uid : the UID of the device to read data - * \param[out] rxBuf : pointer to place the read data - * \param[in] rxBufLen : size of rxBuf - * \param[out] rxRcvdLen : actual received data - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_PARAM : Invalid parameter - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode - rfalT1TPollerRall(const uint8_t* uid, uint8_t* rxBuf, uint16_t rxBufLen, uint16_t* rxRcvdLen); - -/*! - ***************************************************************************** - * \brief NFC-A T1T Poller Write - * - * This method writes the given data on the address of a NFC-A T1T Listener device - * - * - * \param[in] uid : the UID of the device to read data - * \param[in] address : address to write the data - * \param[in] data : the data to be written - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_PARAM : Invalid parameter - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalT1TPollerWrite(const uint8_t* uid, uint8_t address, uint8_t data); - -#endif /* RFAL_T1T_H */ - -/** - * @} - * - * @} - * - * @} - */ diff --git a/lib/ST25RFAL002/include/rfal_t2t.h b/lib/ST25RFAL002/include/rfal_t2t.h deleted file mode 100644 index b90462fd2c4..00000000000 --- a/lib/ST25RFAL002/include/rfal_t2t.h +++ /dev/null @@ -1,150 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_t2t.h - * - * \author Gustavo Patricio - * - * \brief Provides NFC-A T2T convenience methods and definitions - * - * This module provides an interface to perform as a NFC-A Reader/Writer - * to handle a Type 2 Tag T2T - * - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-AL - * \brief RFAL Abstraction Layer - * @{ - * - * \addtogroup T2T - * \brief RFAL T2T Module - * @{ - * - */ - -#ifndef RFAL_T2T_H -#define RFAL_T2T_H - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "platform.h" -#include "st_errno.h" -#include "rfal_rf.h" - -/* - ****************************************************************************** - * GLOBAL DEFINES - ****************************************************************************** - */ - -#define RFAL_T2T_BLOCK_LEN 4U /*!< T2T block length */ -#define RFAL_T2T_READ_DATA_LEN (4U * RFAL_T2T_BLOCK_LEN) /*!< T2T READ data length */ -#define RFAL_T2T_WRITE_DATA_LEN RFAL_T2T_BLOCK_LEN /*!< T2T WRITE data length */ - -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ - -/* -****************************************************************************** -* GLOBAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -/*! - ***************************************************************************** - * \brief NFC-A T2T Poller Read - * - * This method sends a Read command to a NFC-A T2T Listener device - * - * - * \param[in] blockNum : Number of the block to read - * \param[out] rxBuf : pointer to place the read data - * \param[in] rxBufLen : size of rxBuf (RFAL_T2T_READ_DATA_LEN) - * \param[out] rcvLen : actual received data - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_PARAM : Invalid parameter - * \return ERR_PROTO : Protocol error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode - rfalT2TPollerRead(uint8_t blockNum, uint8_t* rxBuf, uint16_t rxBufLen, uint16_t* rcvLen); - -/*! - ***************************************************************************** - * \brief NFC-A T2T Poller Write - * - * This method sends a Write command to a NFC-A T2T Listener device - * - * - * \param[in] blockNum : Number of the block to write - * \param[in] wrData : data to be written on the given block - * size must be of RFAL_T2T_WRITE_DATA_LEN - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_PARAM : Invalid parameter - * \return ERR_PROTO : Protocol error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalT2TPollerWrite(uint8_t blockNum, const uint8_t* wrData); - -/*! - ***************************************************************************** - * \brief NFC-A T2T Poller Sector Select - * - * This method sends a Sector Select commands to a NFC-A T2T Listener device - * - * \param[in] sectorNum : Sector Number - * - * \return ERR_WRONG_STATE : RFAL not initialized or mode not set - * \return ERR_PARAM : Invalid parameter - * \return ERR_PROTO : Protocol error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalT2TPollerSectorSelect(uint8_t sectorNum); - -#endif /* RFAL_T2T_H */ - -/** - * @} - * - * @} - * - * @} - */ diff --git a/lib/ST25RFAL002/include/rfal_t4t.h b/lib/ST25RFAL002/include/rfal_t4t.h deleted file mode 100644 index ff026e1a999..00000000000 --- a/lib/ST25RFAL002/include/rfal_t4t.h +++ /dev/null @@ -1,395 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_t4t.h - * - * \author Gustavo Patricio - * - * \brief Provides convenience methods and definitions for T4T (ISO7816-4) - * - * This module provides an interface to exchange T4T APDUs according to - * NFC Forum T4T and ISO7816-4 - * - * This implementation was based on the following specs: - * - ISO/IEC 7816-4 3rd Edition 2013-04-15 - * - NFC Forum T4T Technical Specification 1.0 2017-08-28 - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-AL - * \brief RFAL Abstraction Layer - * @{ - * - * \addtogroup T4T - * \brief RFAL T4T Module - * @{ - * - */ - -#ifndef RFAL_T4T_H -#define RFAL_T4T_H - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "platform.h" -#include "st_errno.h" -#include "rfal_rf.h" -#include "rfal_isoDep.h" - -/* - ****************************************************************************** - * GLOBAL DEFINES - ****************************************************************************** - */ - -#define RFAL_T4T_MAX_CAPDU_PROLOGUE_LEN \ - 4U /*!< Command-APDU prologue length (CLA INS P1 P2) */ -#define RFAL_T4T_LE_LEN 1U /*!< Le Expected Response Length (short field coding) */ -#define RFAL_T4T_LC_LEN 1U /*!< Lc Data field length (short field coding) */ -#define RFAL_T4T_MAX_RAPDU_SW1SW2_LEN \ - 2U /*!< SW1 SW2 length */ -#define RFAL_T4T_CLA 0x00U /*!< Class byte (contains 00h because secure message are not used) */ - -#define RFAL_T4T_ISO7816_P1_SELECT_BY_DF_NAME \ - 0x04U /*!< P1 value for Select by name */ -#define RFAL_T4T_ISO7816_P1_SELECT_BY_FILEID \ - 0x00U /*!< P1 value for Select by file identifier */ -#define RFAL_T4T_ISO7816_P2_SELECT_FIRST_OR_ONLY_OCCURENCE \ - 0x00U /*!< b2b1 P2 value for First or only occurence */ -#define RFAL_T4T_ISO7816_P2_SELECT_RETURN_FCI_TEMPLATE \ - 0x00U /*!< b4b3 P2 value for Return FCI template */ -#define RFAL_T4T_ISO7816_P2_SELECT_NO_RESPONSE_DATA \ - 0x0CU /*!< b4b3 P2 value for No responce data */ - -#define RFAL_T4T_ISO7816_STATUS_COMPLETE \ - 0x9000U /*!< Command completed \ Normal processing - No further qualification*/ - -/* -****************************************************************************** -* GLOBAL VARIABLES -****************************************************************************** -*/ - -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ -/*! NFC-A T4T Command-APDU structure */ -typedef struct { - uint8_t CLA; /*!< Class byte */ - uint8_t INS; /*!< Instruction byte */ - uint8_t P1; /*!< Parameter byte 1 */ - uint8_t P2; /*!< Parameter byte 2 */ - uint8_t Lc; /*!< Data field length */ - bool LcFlag; /*!< Lc flag (append Lc when true) */ - uint8_t Le; /*!< Expected Response Length */ - bool LeFlag; /*!< Le flag (append Le when true) */ - - rfalIsoDepApduBufFormat* cApduBuf; /*!< Command-APDU buffer (Tx) */ - uint16_t* cApduLen; /*!< Command-APDU Length */ -} rfalT4tCApduParam; - -/*! NFC-A T4T Response-APDU structure */ -typedef struct { - rfalIsoDepApduBufFormat* rApduBuf; /*!< Response-APDU buffer (Rx) */ - uint16_t rcvdLen; /*!< Full response length */ - uint16_t rApduBodyLen; /*!< Response body length */ - uint16_t statusWord; /*!< R-APDU Status Word SW1|SW2 */ -} rfalT4tRApduParam; - -/*! NFC-A T4T command set T4T 1.0 & ISO7816-4 2013 Table 4 */ -typedef enum { - RFAL_T4T_INS_SELECT = 0xA4U, /*!< T4T Select */ - RFAL_T4T_INS_READBINARY = 0xB0U, /*!< T4T ReadBinary */ - RFAL_T4T_INS_UPDATEBINARY = 0xD6U, /*!< T4T UpdateBinay */ - RFAL_T4T_INS_READBINARY_ODO = 0xB1U, /*!< T4T ReadBinary using ODO */ - RFAL_T4T_INS_UPDATEBINARY_ODO = - 0xD7U /*!< T4T UpdateBinay using ODO */ -} rfalT4tCmds; - -/* -****************************************************************************** -* GLOBAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -/*! - ***************************************************************************** - * \brief T4T Compose APDU - * - * This method computes a C-APDU according to NFC Forum T4T and ISO7816-4. - * - * If C-APDU contains data to be sent, it must be placed inside the buffer - * rfalT4tTxRxApduParam.txRx.cApduBuf.apdu and signaled by Lc - * - * To transceive the formed APDU the ISO-DEP layer shall be used - * - * \see rfalIsoDepStartApduTransceive() - * \see rfalIsoDepGetApduTransceiveStatus() - * \see rfalT4TPollerParseRAPDU() - * - * \warning The ISO-DEP module is used to perform the tranceive. Usually - * activation has been done via ISO-DEP activatiavtion. If not - * please call rfalIsoDepInitialize() before. - * - * \param[in,out] apduParam : APDU parameters - * apduParam.cApduLen will contain the APDU length - * - * \return ERR_PARAM : Invalid parameter - * \return ERR_PROTO : Protocol error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalT4TPollerComposeCAPDU(const rfalT4tCApduParam* apduParam); - -/*! - ***************************************************************************** - * \brief T4T Parse R-APDU - * - * This method parses a R-APDU according to NFC Forum T4T and ISO7816-4. - * It will extract the data length and check if the Satus word is expected. - * - * \param[in,out] apduParam : APDU parameters - * apduParam.rApduBodyLen will contain the data length - * apduParam.statusWord will contain the SW1 and SW2 - * - * \return ERR_REQUEST : Status word (SW1 SW2) different from 9000 - * \return ERR_PARAM : Invalid parameter - * \return ERR_PROTO : Protocol error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalT4TPollerParseRAPDU(rfalT4tRApduParam* apduParam); - -/*! - ***************************************************************************** - * \brief T4T Compose Select Application APDU - * - * This method computes a Select Application APDU according to NFC Forum T4T - * - * To transceive the formed APDU the ISO-DEP layer shall be used - * - * \see rfalIsoDepStartApduTransceive() - * \see rfalIsoDepGetApduTransceiveStatus() - * - * \param[out] cApduBuf : buffer where the C-APDU will be placed - * \param[in] aid : Application ID to be used - * \param[in] aidLen : Application ID length - * \param[out] cApduLen : Composed C-APDU length - * - * \return ERR_PARAM : Invalid parameter - * \return ERR_PROTO : Protocol error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalT4TPollerComposeSelectAppl( - rfalIsoDepApduBufFormat* cApduBuf, - const uint8_t* aid, - uint8_t aidLen, - uint16_t* cApduLen); - -/*! - ***************************************************************************** - * \brief T4T Compose Select File APDU - * - * This method computes a Select File APDU according to NFC Forum T4T - * - * To transceive the formed APDU the ISO-DEP layer shall be used - * - * \see rfalIsoDepStartApduTransceive() - * \see rfalIsoDepGetApduTransceiveStatus() - * - * \param[out] cApduBuf : buffer where the C-APDU will be placed - * \param[in] fid : File ID to be used - * \param[in] fidLen : File ID length - * \param[out] cApduLen : Composed C-APDU length - * - * \return ERR_PARAM : Invalid parameter - * \return ERR_PROTO : Protocol error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalT4TPollerComposeSelectFile( - rfalIsoDepApduBufFormat* cApduBuf, - const uint8_t* fid, - uint8_t fidLen, - uint16_t* cApduLen); - -/*! - ***************************************************************************** - * \brief T4T Compose Select File APDU for Mapping Version 1 - * - * This method computes a Select File APDU according to NFC Forum T4TOP_v1.0 - * - * To transceive the formed APDU the ISO-DEP layer shall be used - * - * \see rfalIsoDepStartApduTransceive() - * \see rfalIsoDepGetApduTransceiveStatus() - * - * \param[out] cApduBuf : buffer where the C-APDU will be placed - * \param[in] fid : File ID to be used - * \param[in] fidLen : File ID length - * \param[out] cApduLen : Composed C-APDU length - * - * \return ERR_PARAM : Invalid parameter - * \return ERR_PROTO : Protocol error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalT4TPollerComposeSelectFileV1Mapping( - rfalIsoDepApduBufFormat* cApduBuf, - const uint8_t* fid, - uint8_t fidLen, - uint16_t* cApduLen); - -/*! - ***************************************************************************** - * \brief T4T Compose Read Data APDU - * - * This method computes a Read Data APDU according to NFC Forum T4T - * - * To transceive the formed APDU the ISO-DEP layer shall be used - * - * \see rfalIsoDepStartApduTransceive() - * \see rfalIsoDepGetApduTransceiveStatus() - * - * \param[out] cApduBuf : buffer where the C-APDU will be placed - * \param[in] offset : File offset - * \param[in] expLen : Expected length (Le) - * \param[out] cApduLen : Composed C-APDU length - * - * \return ERR_PARAM : Invalid parameter - * \return ERR_PROTO : Protocol error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalT4TPollerComposeReadData( - rfalIsoDepApduBufFormat* cApduBuf, - uint16_t offset, - uint8_t expLen, - uint16_t* cApduLen); - -/*! - ***************************************************************************** - * \brief T4T Compose Read Data ODO APDU - * - * This method computes a Read Data ODO APDU according to NFC Forum T4T - * - * To transceive the formed APDU the ISO-DEP layer shall be used - * - * \see rfalIsoDepStartApduTransceive() - * \see rfalIsoDepGetApduTransceiveStatus() - * - * \param[out] cApduBuf : buffer where the C-APDU will be placed - * \param[in] offset : File offset - * \param[in] expLen : Expected length (Le) - * \param[out] cApduLen : Composed C-APDU length - * - * \return ERR_PARAM : Invalid parameter - * \return ERR_PROTO : Protocol error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalT4TPollerComposeReadDataODO( - rfalIsoDepApduBufFormat* cApduBuf, - uint32_t offset, - uint8_t expLen, - uint16_t* cApduLen); - -/*! - ***************************************************************************** - * \brief T4T Compose Write Data APDU - * - * This method computes a Write Data APDU according to NFC Forum T4T - * - * To transceive the formed APDU the ISO-DEP layer shall be used - * - * \see rfalIsoDepStartApduTransceive() - * \see rfalIsoDepGetApduTransceiveStatus() - * - * \param[out] cApduBuf : buffer where the C-APDU will be placed - * \param[in] offset : File offset - * \param[in] data : Data to be written - * \param[in] dataLen : Data length to be written (Lc) - * \param[out] cApduLen : Composed C-APDU length - * - * \return ERR_PARAM : Invalid parameter - * \return ERR_PROTO : Protocol error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalT4TPollerComposeWriteData( - rfalIsoDepApduBufFormat* cApduBuf, - uint16_t offset, - const uint8_t* data, - uint8_t dataLen, - uint16_t* cApduLen); - -/*! - ***************************************************************************** - * \brief T4T Compose Write Data ODO APDU - * - * This method computes a Write Data ODO sAPDU according to NFC Forum T4T - * - * To transceive the formed APDU the ISO-DEP layer shall be used - * - * \see rfalIsoDepStartApduTransceive() - * \see rfalIsoDepGetApduTransceiveStatus() - * - * \param[out] cApduBuf : buffer where the C-APDU will be placed - * \param[in] offset : File offset - * \param[in] data : Data to be written - * \param[in] dataLen : Data length to be written (Lc) - * \param[out] cApduLen : Composed C-APDU length - * - * \return ERR_PARAM : Invalid parameter - * \return ERR_PROTO : Protocol error - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode rfalT4TPollerComposeWriteDataODO( - rfalIsoDepApduBufFormat* cApduBuf, - uint32_t offset, - const uint8_t* data, - uint8_t dataLen, - uint16_t* cApduLen); - -#endif /* RFAL_T4T_H */ - -/** - * @} - * - * @} - * - * @} - */ diff --git a/lib/ST25RFAL002/platform.c b/lib/ST25RFAL002/platform.c deleted file mode 100644 index af9dc6ff736..00000000000 --- a/lib/ST25RFAL002/platform.c +++ /dev/null @@ -1,102 +0,0 @@ -#include "platform.h" -#include -#include -#include - -typedef struct { - FuriThread* thread; - volatile PlatformIrqCallback callback; - bool need_spi_lock; -} RfalPlatform; - -static volatile RfalPlatform rfal_platform = { - .thread = NULL, - .callback = NULL, - .need_spi_lock = true, -}; - -void nfc_isr(void* _ctx) { - UNUSED(_ctx); - if(rfal_platform.callback && platformGpioIsHigh(ST25R_INT_PORT, ST25R_INT_PIN)) { - furi_thread_flags_set(furi_thread_get_id(rfal_platform.thread), 0x1); - } -} - -int32_t rfal_platform_irq_thread(void* context) { - UNUSED(context); - - while(1) { - uint32_t flags = furi_thread_flags_wait(0x1, FuriFlagWaitAny, FuriWaitForever); - if(flags & 0x1) { - rfal_platform.callback(); - } - } -} - -void platformEnableIrqCallback() { - furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeInterruptRise, GpioPullDown, GpioSpeedLow); - furi_hal_gpio_enable_int_callback(&gpio_nfc_irq_rfid_pull); -} - -void platformDisableIrqCallback() { - furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_disable_int_callback(&gpio_nfc_irq_rfid_pull); -} - -void platformSetIrqCallback(PlatformIrqCallback callback) { - rfal_platform.callback = callback; - rfal_platform.thread = furi_thread_alloc(); - - furi_thread_set_name(rfal_platform.thread, "RfalIrqDriver"); - furi_thread_set_callback(rfal_platform.thread, rfal_platform_irq_thread); - furi_thread_set_stack_size(rfal_platform.thread, 1024); - furi_thread_set_priority(rfal_platform.thread, FuriThreadPriorityIsr); - furi_thread_start(rfal_platform.thread); - - furi_hal_gpio_add_int_callback(&gpio_nfc_irq_rfid_pull, nfc_isr, NULL); - // Disable interrupt callback as the pin is shared between 2 apps - // It is enabled in rfalLowPowerModeStop() - furi_hal_gpio_disable_int_callback(&gpio_nfc_irq_rfid_pull); -} - -bool platformSpiTxRx(const uint8_t* txBuf, uint8_t* rxBuf, uint16_t len) { - bool ret = false; - if(txBuf && rxBuf) { - ret = - furi_hal_spi_bus_trx(&furi_hal_spi_bus_handle_nfc, (uint8_t*)txBuf, rxBuf, len, 1000); - } else if(txBuf) { - ret = furi_hal_spi_bus_tx(&furi_hal_spi_bus_handle_nfc, (uint8_t*)txBuf, len, 1000); - } else if(rxBuf) { - ret = furi_hal_spi_bus_rx(&furi_hal_spi_bus_handle_nfc, (uint8_t*)rxBuf, len, 1000); - } - - return ret; -} - -// Until we completely remove RFAL, NFC works with SPI from rfal_platform_irq_thread and nfc_worker -// threads. Some nfc features already stop using RFAL and work with SPI from nfc_worker only. -// rfal_platform_spi_acquire() and rfal_platform_spi_release() functions are used to lock SPI for a -// long term without locking it for each SPI transaction. This is needed for time critical communications. -void rfal_platform_spi_acquire() { - platformDisableIrqCallback(); - rfal_platform.need_spi_lock = false; - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_nfc); -} - -void rfal_platform_spi_release() { - furi_hal_spi_release(&furi_hal_spi_bus_handle_nfc); - rfal_platform.need_spi_lock = true; - platformEnableIrqCallback(); -} - -void platformProtectST25RComm() { - if(rfal_platform.need_spi_lock) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_nfc); - } -} - -void platformUnprotectST25RComm() { - if(rfal_platform.need_spi_lock) { - furi_hal_spi_release(&furi_hal_spi_bus_handle_nfc); - } -} diff --git a/lib/ST25RFAL002/platform.h b/lib/ST25RFAL002/platform.h deleted file mode 100644 index d86bba73e36..00000000000 --- a/lib/ST25RFAL002/platform.h +++ /dev/null @@ -1,188 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include "timer.h" -#include "math.h" -#include -#include -#include - -typedef void (*PlatformIrqCallback)(); -void platformSetIrqCallback(PlatformIrqCallback cb); -void platformEnableIrqCallback(); -void platformDisableIrqCallback(); - -bool platformSpiTxRx(const uint8_t* txBuf, uint8_t* rxBuf, uint16_t len); -void platformProtectST25RComm(); -void platformUnprotectST25RComm(); -void rfal_platform_spi_acquire(); -void rfal_platform_spi_release(); - -#define ST25R_SS_PIN NFC_CS_Pin -#define ST25R_SS_PORT NFC_CS_GPIO_Port - -#define ST25R_INT_PIN NFC_IRQ_Pin -#define ST25R_INT_PORT NFC_IRQ_GPIO_Port - -#define RFAL_ANALOG_CONFIG_CUSTOM \ - true /*!< Enable/Disable RFAL custom analog configuration */ - -#define RFAL_FEATURE_LISTEN_MODE \ - true /*!< Enable/Disable RFAL support for Listen Mode */ -#define RFAL_FEATURE_WAKEUP_MODE \ - true /*!< Enable/Disable RFAL support for the Wake-Up mode */ -#define RFAL_FEATURE_LOWPOWER_MODE \ - true /*!< Enable/Disable RFAL support for the Low Power mode */ -#define RFAL_FEATURE_NFCA \ - true /*!< Enable/Disable RFAL support for NFC-A (ISO14443A) */ -#define RFAL_FEATURE_NFCB \ - true /*!< Enable/Disable RFAL support for NFC-B (ISO14443B) */ -#define RFAL_FEATURE_NFCF \ - true /*!< Enable/Disable RFAL support for NFC-F (FeliCa) */ -#define RFAL_FEATURE_NFCV \ - true /*!< Enable/Disable RFAL support for NFC-V (ISO15693) */ -#define RFAL_FEATURE_T1T \ - true /*!< Enable/Disable RFAL support for T1T (Topaz) */ -#define RFAL_FEATURE_T2T \ - true /*!< Enable/Disable RFAL support for T2T (MIFARE Ultralight) */ -#define RFAL_FEATURE_T4T \ - true /*!< Enable/Disable RFAL support for T4T */ -#define RFAL_FEATURE_ST25TB \ - true /*!< Enable/Disable RFAL support for ST25TB */ -#define RFAL_FEATURE_ST25xV \ - true /*!< Enable/Disable RFAL support for ST25TV/ST25DV */ -#define RFAL_FEATURE_DYNAMIC_ANALOG_CONFIG \ - false /*!< Enable/Disable Analog Configs to be dynamically updated (RAM) */ -#define RFAL_FEATURE_DPO \ - false /*!< Enable/Disable RFAL Dynamic Power Output upport */ -#define RFAL_FEATURE_ISO_DEP \ - true /*!< Enable/Disable RFAL support for ISO-DEP (ISO14443-4) */ -#define RFAL_FEATURE_ISO_DEP_POLL \ - true /*!< Enable/Disable RFAL support for Poller mode (PCD) ISO-DEP (ISO14443-4) */ -#define RFAL_FEATURE_ISO_DEP_LISTEN \ - true /*!< Enable/Disable RFAL support for Listen mode (PICC) ISO-DEP (ISO14443-4) */ -#define RFAL_FEATURE_NFC_DEP \ - true /*!< Enable/Disable RFAL support for NFC-DEP (NFCIP1/P2P) */ - -#define RFAL_FEATURE_ISO_DEP_IBLOCK_MAX_LEN \ - 256U /*!< ISO-DEP I-Block max length. Please use values as defined by rfalIsoDepFSx */ -#define RFAL_FEATURE_NFC_DEP_BLOCK_MAX_LEN \ - 254U /*!< NFC-DEP Block/Payload length. Allowed values: 64, 128, 192, 254 */ -#define RFAL_FEATURE_NFC_RF_BUF_LEN \ - 256U /*!< RF buffer length used by RFAL NFC layer */ - -#define RFAL_FEATURE_ISO_DEP_APDU_MAX_LEN \ - 512U /*!< ISO-DEP APDU max length. */ -#define RFAL_FEATURE_NFC_DEP_PDU_MAX_LEN \ - 512U /*!< NFC-DEP PDU max length. */ - -#define platformIrqST25RSetCallback(cb) platformSetIrqCallback(cb) - -#define platformProtectST25RIrqStatus() \ - platformProtectST25RComm() /*!< Protect unique access to IRQ status var - IRQ disable on single thread environment (MCU) ; Mutex lock on a multi thread environment */ -#define platformUnprotectST25RIrqStatus() \ - platformUnprotectST25RComm() /*!< Unprotect the IRQ status var - IRQ enable on a single thread environment (MCU) ; Mutex unlock on a multi thread environment */ - -#define platformGpioSet(port, pin) \ - furi_hal_gpio_write_port_pin( \ - port, pin, true) /*!< Turns the given GPIO High */ -#define platformGpioClear(port, pin) \ - furi_hal_gpio_write_port_pin( \ - port, pin, false) /*!< Turns the given GPIO Low */ - -#define platformGpioIsHigh(port, pin) \ - (furi_hal_gpio_read_port_pin(port, pin) == \ - true) /*!< Checks if the given LED is High */ -#define platformGpioIsLow(port, pin) \ - (!platformGpioIsHigh(port, pin)) /*!< Checks if the given LED is Low */ - -#define platformTimerCreate(t) \ - timerCalculateTimer(t) /*!< Create a timer with the given time (ms) */ -#define platformTimerIsExpired(timer) \ - timerIsExpired(timer) /*!< Checks if the given timer is expired */ -#define platformDelay(t) furi_delay_ms(t) /*!< Performs a delay for the given time (ms) */ - -#define platformGetSysTick() furi_get_tick() /*!< Get System Tick (1 tick = 1 ms) */ - -#define platformAssert(exp) assert_param(exp) /*!< Asserts whether the given expression is true*/ - -#define platformSpiSelect() \ - platformGpioClear( \ - ST25R_SS_PORT, ST25R_SS_PIN) /*!< SPI SS\CS: Chip|Slave Select */ -#define platformSpiDeselect() \ - platformGpioSet( \ - ST25R_SS_PORT, ST25R_SS_PIN) /*!< SPI SS\CS: Chip|Slave Deselect */ - -#define platformI2CTx(txBuf, len, last, txOnly) /*!< I2C Transmit */ -#define platformI2CRx(txBuf, len) /*!< I2C Receive */ -#define platformI2CStart() /*!< I2C Start condition */ -#define platformI2CStop() /*!< I2C Stop condition */ -#define platformI2CRepeatStart() /*!< I2C Repeat Start */ -#define platformI2CSlaveAddrWR(add) /*!< I2C Slave address for Write operation */ -#define platformI2CSlaveAddrRD(add) /*!< I2C Slave address for Read operation */ - -#define platformLog(...) /*!< Log method */ - -/* - ****************************************************************************** - * RFAL OPTIONAL MACROS (Do not change) - ****************************************************************************** - */ -#ifndef platformProtectST25RIrqStatus -#define platformProtectST25RIrqStatus() /*!< Protect unique access to IRQ status var - IRQ disable on single thread environment (MCU) ; Mutex lock on a multi thread environment */ -#endif /* platformProtectST25RIrqStatus */ - -#ifndef platformUnprotectST25RIrqStatus -#define platformUnprotectST25RIrqStatus() /*!< Unprotect the IRQ status var - IRQ enable on a single thread environment (MCU) ; Mutex unlock on a multi thread environment */ -#endif /* platformUnprotectST25RIrqStatus */ - -#ifndef platformProtectWorker -#define platformProtectWorker() /* Protect RFAL Worker/Task/Process from concurrent execution on multi thread platforms */ -#endif /* platformProtectWorker */ - -#ifndef platformUnprotectWorker -#define platformUnprotectWorker() /* Unprotect RFAL Worker/Task/Process from concurrent execution on multi thread platforms */ -#endif /* platformUnprotectWorker */ - -#ifndef platformIrqST25RPinInitialize -#define platformIrqST25RPinInitialize() /*!< Initializes ST25R IRQ pin */ -#endif /* platformIrqST25RPinInitialize */ - -#ifndef platformIrqST25RSetCallback -#define platformIrqST25RSetCallback(cb) /*!< Sets ST25R ISR callback */ -#endif /* platformIrqST25RSetCallback */ - -#ifndef platformLedsInitialize -#define platformLedsInitialize() /*!< Initializes the pins used as LEDs to outputs */ -#endif /* platformLedsInitialize */ - -#ifndef platformLedOff -#define platformLedOff(port, pin) /*!< Turns the given LED Off */ -#endif /* platformLedOff */ - -#ifndef platformLedOn -#define platformLedOn(port, pin) /*!< Turns the given LED On */ -#endif /* platformLedOn */ - -#ifndef platformLedToogle -#define platformLedToogle(port, pin) /*!< Toggles the given LED */ -#endif /* platformLedToogle */ - -#ifndef platformGetSysTick -#define platformGetSysTick() /*!< Get System Tick (1 tick = 1 ms) */ -#endif /* platformGetSysTick */ - -#ifndef platformTimerDestroy -#define platformTimerDestroy(timer) /*!< Stops and released the given timer */ -#endif /* platformTimerDestroy */ - -#ifndef platformAssert -#define platformAssert(exp) /*!< Asserts whether the given expression is true */ -#endif /* platformAssert */ - -#ifndef platformErrorHandle -#define platformErrorHandle() /*!< Global error handler or trap */ -#endif /* platformErrorHandle */ diff --git a/lib/ST25RFAL002/source/custom_analog_config.c b/lib/ST25RFAL002/source/custom_analog_config.c deleted file mode 100644 index 00a54db2605..00000000000 --- a/lib/ST25RFAL002/source/custom_analog_config.c +++ /dev/null @@ -1,777 +0,0 @@ -#include "rfal_analogConfigTbl.h" - -const uint8_t rfalAnalogConfigCustomSettings[] = { - /****** Default Analog Configuration for Chip-Specific Reset ******/ - MODE_ENTRY_16_REG( - (RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_INIT), - ST25R3916_REG_IO_CONF1, - (ST25R3916_REG_IO_CONF1_out_cl_mask | ST25R3916_REG_IO_CONF1_lf_clk_off), - 0x07 /* Disable MCU_CLK */ - , - ST25R3916_REG_IO_CONF2, - (ST25R3916_REG_IO_CONF2_miso_pd1 | ST25R3916_REG_IO_CONF2_miso_pd2), - 0x18 /* SPI Pull downs */ - // , ST25R3916_REG_IO_CONF2, ST25R3916_REG_IO_CONF2_aat_en, ST25R3916_REG_IO_CONF2_aat_en /* Enable AAT */ - , - ST25R3916_REG_TX_DRIVER, - ST25R3916_REG_TX_DRIVER_d_res_mask, - 0x00 /* Set RFO resistance Active Tx */ - , - ST25R3916_REG_RES_AM_MOD, - 0xFF, - 0x80 /* Use minimum non-overlap */ - , - ST25R3916_REG_FIELD_THRESHOLD_ACTV, - ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_mask, - ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_105mV /* Lower activation threshold (higher than deactivation)*/ - , - ST25R3916_REG_FIELD_THRESHOLD_ACTV, - ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_mask, - ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_105mV /* Lower activation threshold (higher than deactivation)*/ - , - ST25R3916_REG_FIELD_THRESHOLD_DEACTV, - ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_mask, - ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_75mV /* Lower deactivation threshold */ - , - ST25R3916_REG_FIELD_THRESHOLD_DEACTV, - ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_mask, - ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_75mV /* Lower deactivation threshold */ - , - ST25R3916_REG_AUX_MOD, - ST25R3916_REG_AUX_MOD_lm_ext, - ST25R3916_REG_AUX_MOD_lm_ext /* Disable External Load Modulation */ - , - ST25R3916_REG_AUX_MOD, - ST25R3916_REG_AUX_MOD_lm_dri, - ST25R3916_REG_AUX_MOD_lm_dri /* Use internal Load Modulation */ - , - ST25R3916_REG_PASSIVE_TARGET, - ST25R3916_REG_PASSIVE_TARGET_fdel_mask, - (5U - << ST25R3916_REG_PASSIVE_TARGET_fdel_shift) /* Adjust the FDT to be aligned with the bitgrid */ - , - ST25R3916_REG_PT_MOD, - (ST25R3916_REG_PT_MOD_ptm_res_mask | ST25R3916_REG_PT_MOD_pt_res_mask), - 0x0f /* Reduce RFO resistance in Modulated state */ - , - ST25R3916_REG_EMD_SUP_CONF, - ST25R3916_REG_EMD_SUP_CONF_rx_start_emv, - ST25R3916_REG_EMD_SUP_CONF_rx_start_emv_on /* Enable start on first 4 bits */ - , - ST25R3916_REG_ANT_TUNE_A, - 0xFF, - 0x82 /* Set Antenna Tuning (Poller): ANTL */ - , - ST25R3916_REG_ANT_TUNE_B, - 0xFF, - 0x82 /* Set Antenna Tuning (Poller): ANTL */ - , - 0x84U, - 0x10, - 0x10 /* Avoid chip internal overheat protection */ - ) - - /****** Default Analog Configuration for Chip-Specific Poll Common ******/ - , - MODE_ENTRY_9_REG( - (RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_POLL_COMMON), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_am /* Use AM modulation */ - , - ST25R3916_REG_TX_DRIVER, - ST25R3916_REG_TX_DRIVER_am_mod_mask, - ST25R3916_REG_TX_DRIVER_am_mod_12percent /* Set Modulation index */ - , - ST25R3916_REG_AUX_MOD, - (ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am), - 0x00 /* Use AM via regulator */ - , - ST25R3916_REG_ANT_TUNE_A, - 0xFF, - 0x82 /* Set Antenna Tuning (Poller): ANTL */ - , - ST25R3916_REG_ANT_TUNE_B, - 0xFF, - 0x82 /* Set Antenna Tuning (Poller): ANTL */ - , - ST25R3916_REG_OVERSHOOT_CONF1, - 0xFF, - 0x00 /* Disable Overshoot Protection */ - , - ST25R3916_REG_OVERSHOOT_CONF2, - 0xFF, - 0x00 /* Disable Overshoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF1, - 0xFF, - 0x00 /* Disable Undershoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF2, - 0xFF, - 0x00 /* Disable Undershoot Protection */ - ) - - /****** Default Analog Configuration for Poll NFC-A Rx Common ******/ - , - MODE_ENTRY_1_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_AUX, - ST25R3916_REG_AUX_dis_corr, - ST25R3916_REG_AUX_dis_corr_correlator /* Use Correlator Receiver */ - ) - - /****** Default Analog Configuration for Poll NFC-A Tx 106 ******/ - , - MODE_ENTRY_5_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | RFAL_ANALOG_CONFIG_BITRATE_106 | - RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_ook /* Use OOK */ - , - ST25R3916_REG_OVERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Overshoot Protection */ - , - ST25R3916_REG_OVERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Overshoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Undershoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Undershoot Protection */ - ) - - /****** Default Analog Configuration for Poll NFC-A Rx 106 ******/ - , - MODE_ENTRY_6_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | RFAL_ANALOG_CONFIG_BITRATE_106 | - RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x08, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x2D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x51, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) - - /****** Default Analog Configuration for Poll NFC-A Tx 212 ******/ - , - MODE_ENTRY_7_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | RFAL_ANALOG_CONFIG_BITRATE_212 | - RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_am /* Use AM modulation */ - , - ST25R3916_REG_AUX_MOD, - (ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am), - 0x88 /* Use Resistive AM */ - , - ST25R3916_REG_RES_AM_MOD, - ST25R3916_REG_RES_AM_MOD_md_res_mask, - 0x7F /* Set Resistive modulation */ - , - ST25R3916_REG_OVERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Overshoot Protection */ - , - ST25R3916_REG_OVERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Overshoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Undershoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Undershoot Protection */ - ) - - /****** Default Analog Configuration for Poll NFC-A Rx 212 ******/ - , - MODE_ENTRY_6_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | RFAL_ANALOG_CONFIG_BITRATE_212 | - RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x02, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x3D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x14, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) - - /****** Default Analog Configuration for Poll NFC-A Tx 424 ******/ - , - MODE_ENTRY_7_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | RFAL_ANALOG_CONFIG_BITRATE_424 | - RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_am /* Use AM modulation */ - , - ST25R3916_REG_AUX_MOD, - (ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am), - 0x88 /* Use Resistive AM */ - , - ST25R3916_REG_RES_AM_MOD, - ST25R3916_REG_RES_AM_MOD_md_res_mask, - 0x7F /* Set Resistive modulation */ - , - ST25R3916_REG_OVERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Overshoot Protection */ - , - ST25R3916_REG_OVERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Overshoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Undershoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Undershoot Protection */ - ) - - /****** Default Analog Configuration for Poll NFC-A Rx 424 ******/ - , - MODE_ENTRY_6_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | RFAL_ANALOG_CONFIG_BITRATE_424 | - RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x42, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x3D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x54, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) - - /****** Default Analog Configuration for Poll NFC-A Tx 848 ******/ - , - MODE_ENTRY_7_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | RFAL_ANALOG_CONFIG_BITRATE_848 | - RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_am /* Use AM modulation */ - , - ST25R3916_REG_TX_DRIVER, - ST25R3916_REG_TX_DRIVER_am_mod_mask, - ST25R3916_REG_TX_DRIVER_am_mod_40percent /* Set Modulation index */ - , - ST25R3916_REG_AUX_MOD, - (ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am), - 0x00 /* Use AM via regulator */ - , - ST25R3916_REG_OVERSHOOT_CONF1, - 0xFF, - 0x00 /* Disable Overshoot Protection */ - , - ST25R3916_REG_OVERSHOOT_CONF2, - 0xFF, - 0x00 /* Disable Overshoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF1, - 0xFF, - 0x00 /* Disable Undershoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF2, - 0xFF, - 0x00 /* Disable Undershoot Protection */ - ) - - /****** Default Analog Configuration for Poll NFC-A Rx 848 ******/ - , - MODE_ENTRY_6_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | RFAL_ANALOG_CONFIG_BITRATE_848 | - RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x42, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x3D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x44, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) - - /****** Default Analog Configuration for Poll NFC-A Anticolision setting ******/ - , - MODE_ENTRY_1_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_ANTICOL), - ST25R3916_REG_CORR_CONF1, - ST25R3916_REG_CORR_CONF1_corr_s6, - 0x00 /* Set collision detection level different from data */ - ) - -#ifdef RFAL_USE_COHE - /****** Default Analog Configuration for Poll NFC-B Rx Common ******/ - , - MODE_ENTRY_1_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_AUX, - ST25R3916_REG_AUX_dis_corr, - ST25R3916_REG_AUX_dis_corr_coherent /* Use Coherent Receiver */ - ) -#else - /****** Default Analog Configuration for Poll NFC-B Rx Common ******/ - , - MODE_ENTRY_1_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_AUX, - ST25R3916_REG_AUX_dis_corr, - ST25R3916_REG_AUX_dis_corr_correlator /* Use Correlator Receiver */ - ) -#endif /*RFAL_USE_COHE*/ - - /****** Default Analog Configuration for Poll NFC-B Rx 106 ******/ - , - MODE_ENTRY_6_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | RFAL_ANALOG_CONFIG_BITRATE_106 | - RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x04, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x3D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x1B, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) - - /****** Default Analog Configuration for Poll NFC-B Rx 212 ******/ - , - MODE_ENTRY_6_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | RFAL_ANALOG_CONFIG_BITRATE_212 | - RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x02, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x3D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x14, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) - - /****** Default Analog Configuration for Poll NFC-B Rx 424 ******/ - , - MODE_ENTRY_6_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | RFAL_ANALOG_CONFIG_BITRATE_424 | - RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x42, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x3D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x54, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) - - /****** Default Analog Configuration for Poll NFC-B Rx 848 ******/ - , - MODE_ENTRY_6_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | RFAL_ANALOG_CONFIG_BITRATE_848 | - RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x42, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x3D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x44, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) -#ifdef RFAL_USE_COHE - - /****** Default Analog Configuration for Poll NFC-F Rx Common ******/ - , - MODE_ENTRY_7_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCF | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_AUX, - ST25R3916_REG_AUX_dis_corr, - ST25R3916_REG_AUX_dis_corr_coherent /* Use Pulse Receiver */ - , - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x13, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x3D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x54, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) -#else - /****** Default Analog Configuration for Poll NFC-F Rx Common ******/ - , - MODE_ENTRY_7_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCF | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_AUX, - ST25R3916_REG_AUX_dis_corr, - ST25R3916_REG_AUX_dis_corr_correlator /* Use Correlator Receiver */ - , - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x13, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x3D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x54, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) -#endif /*RFAL_USE_COHE*/ - - , - MODE_ENTRY_1_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCV | RFAL_ANALOG_CONFIG_BITRATE_1OF4 | - RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_ook /* Use OOK */ - ) - -#ifdef RFAL_USE_COHE - /****** Default Analog Configuration for Poll NFC-V Rx Common ******/ - , - MODE_ENTRY_7_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCV | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_AUX, - ST25R3916_REG_AUX_dis_corr, - ST25R3916_REG_AUX_dis_corr_coherent /* Use Pulse Receiver */ - , - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x13, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x2D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x13, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x01) -#else - /****** Default Analog Configuration for Poll NFC-V Rx Common ******/ - , - MODE_ENTRY_7_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCV | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_AUX, - ST25R3916_REG_AUX_dis_corr, - ST25R3916_REG_AUX_dis_corr_correlator /* Use Correlator Receiver */ - , - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x13, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x2D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x13, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x01) -#endif /*RFAL_USE_COHE*/ - - /****** Default Analog Configuration for Poll AP2P Tx 106 ******/ - , - MODE_ENTRY_5_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_AP2P | RFAL_ANALOG_CONFIG_BITRATE_106 | - RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_ook /* Use OOK modulation */ - , - ST25R3916_REG_OVERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Overshoot Protection */ - , - ST25R3916_REG_OVERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Overshoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Undershoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Undershoot Protection */ - ) - - /****** Default Analog Configuration for Poll AP2P Tx 212 ******/ - , - MODE_ENTRY_1_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_AP2P | RFAL_ANALOG_CONFIG_BITRATE_212 | - RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_am /* Use AM modulation */ - ) - - /****** Default Analog Configuration for Poll AP2P Tx 424 ******/ - , - MODE_ENTRY_1_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_AP2P | RFAL_ANALOG_CONFIG_BITRATE_424 | - RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_am /* Use AM modulation */ - ) - - /****** Default Analog Configuration for Chip-Specific Listen On ******/ - , - MODE_ENTRY_6_REG( - (RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_LISTEN_ON), - ST25R3916_REG_ANT_TUNE_A, - 0xFF, - 0x00 /* Set Antenna Tuning (Listener): ANTL */ - , - ST25R3916_REG_ANT_TUNE_B, - 0xFF, - 0xff /* Set Antenna Tuning (Listener): ANTL */ - , - ST25R3916_REG_OVERSHOOT_CONF1, - 0xFF, - 0x00 /* Disable Overshoot Protection */ - , - ST25R3916_REG_OVERSHOOT_CONF2, - 0xFF, - 0x00 /* Disable Overshoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF1, - 0xFF, - 0x00 /* Disable Undershoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF2, - 0xFF, - 0x00 /* Disable Undershoot Protection */ - ) - - /****** Default Analog Configuration for Listen AP2P Tx Common ******/ - , - MODE_ENTRY_7_REG( - (RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_AP2P | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_ANT_TUNE_A, - 0xFF, - 0x82 /* Set Antenna Tuning (Poller): ANTL */ - , - ST25R3916_REG_ANT_TUNE_B, - 0xFF, - 0x82 /* Set Antenna Tuning (Poller): ANTL */ - , - ST25R3916_REG_TX_DRIVER, - ST25R3916_REG_TX_DRIVER_am_mod_mask, - ST25R3916_REG_TX_DRIVER_am_mod_12percent /* Set Modulation index */ - , - ST25R3916_REG_OVERSHOOT_CONF1, - 0xFF, - 0x00 /* Disable Overshoot Protection */ - , - ST25R3916_REG_OVERSHOOT_CONF2, - 0xFF, - 0x00 /* Disable Overshoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF1, - 0xFF, - 0x00 /* Disable Undershoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF2, - 0xFF, - 0x00 /* Disable Undershoot Protection */ - ) - - /****** Default Analog Configuration for Listen AP2P Rx Common ******/ - , - MODE_ENTRY_3_REG( - (RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_AP2P | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_RX_CONF1, - ST25R3916_REG_RX_CONF1_lp_mask, - ST25R3916_REG_RX_CONF1_lp_1200khz /* Set Rx filter configuration */ - , - ST25R3916_REG_RX_CONF1, - ST25R3916_REG_RX_CONF1_hz_mask, - ST25R3916_REG_RX_CONF1_hz_12_200khz /* Set Rx filter configuration */ - , - ST25R3916_REG_RX_CONF2, - ST25R3916_REG_RX_CONF2_amd_sel, - ST25R3916_REG_RX_CONF2_amd_sel_mixer /* AM demodulator: mixer */ - ) - - /****** Default Analog Configuration for Listen AP2P Tx 106 ******/ - , - MODE_ENTRY_5_REG( - (RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_AP2P | - RFAL_ANALOG_CONFIG_BITRATE_106 | RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_ook /* Use OOK modulation */ - , - ST25R3916_REG_OVERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Overshoot Protection */ - , - ST25R3916_REG_OVERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Overshoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Undershoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Undershoot Protection */ - ) - - /****** Default Analog Configuration for Listen AP2P Tx 212 ******/ - , - MODE_ENTRY_1_REG( - (RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_AP2P | - RFAL_ANALOG_CONFIG_BITRATE_212 | RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_am /* Use AM modulation */ - ) - - /****** Default Analog Configuration for Listen AP2P Tx 424 ******/ - , - MODE_ENTRY_1_REG( - (RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_AP2P | - RFAL_ANALOG_CONFIG_BITRATE_424 | RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_am /* Use AM modulation */ - ) - -}; - -const uint16_t rfalAnalogConfigCustomSettingsLength = sizeof(rfalAnalogConfigCustomSettings); diff --git a/lib/ST25RFAL002/source/rfal_analogConfig.c b/lib/ST25RFAL002/source/rfal_analogConfig.c deleted file mode 100644 index 1237af18bda..00000000000 --- a/lib/ST25RFAL002/source/rfal_analogConfig.c +++ /dev/null @@ -1,476 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_analogConfig.c - * - * \author bkam - * - * \brief Funcitons to manage and set analog settings. - * - */ - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "rfal_analogConfig.h" -#include "rfal_chip.h" -#include "st_errno.h" -#include "platform.h" -#include "utils.h" - -/* Check whether the Default Analog settings are to be used or custom ones */ -#ifdef RFAL_ANALOG_CONFIG_CUSTOM -extern const uint8_t* rfalAnalogConfigCustomSettings; -extern const uint16_t rfalAnalogConfigCustomSettingsLength; -#else -#include "rfal_analogConfigTbl.h" -#endif - -/* - ****************************************************************************** - * DEFINES - ****************************************************************************** - */ - -#define RFAL_TEST_REG 0x0080U /*!< Test Register indicator */ - -/* - ****************************************************************************** - * MACROS - ****************************************************************************** - */ - -/* - ****************************************************************************** - * LOCAL DATA TYPES - ****************************************************************************** - */ - -#if RFAL_FEATURE_DYNAMIC_ANALOG_CONFIG -static uint8_t - gRfalAnalogConfig[RFAL_ANALOG_CONFIG_TBL_SIZE]; /*!< Analog Configuration Settings List */ -#endif /* RFAL_FEATURE_DYNAMIC_ANALOG_CONFIG */ - -/*! Struct for Analog Config Look Up Table Update */ -typedef struct { - const uint8_t* - currentAnalogConfigTbl; /*!< Reference to start of current Analog Configuration */ - uint16_t configTblSize; /*!< Total size of Analog Configuration */ - bool ready; /*!< Indicate if Look Up Table is complete and ready for use */ -} rfalAnalogConfigMgmt; - -static rfalAnalogConfigMgmt gRfalAnalogConfigMgmt; /*!< Analog Configuration LUT management */ - -/* - ****************************************************************************** - * LOCAL TABLES - ****************************************************************************** - */ - -/* - ****************************************************************************** - * LOCAL FUNCTION PROTOTYPES - ****************************************************************************** - */ -static rfalAnalogConfigNum - rfalAnalogConfigSearch(rfalAnalogConfigId configId, uint16_t* configOffset); - -#if RFAL_FEATURE_DYNAMIC_ANALOG_CONFIG -static void rfalAnalogConfigPtrUpdate(const uint8_t* analogConfigTbl); -#endif /* RFAL_FEATURE_DYNAMIC_ANALOG_CONFIG */ - -/* - ****************************************************************************** - * GLOBAL VARIABLE DEFINITIONS - ****************************************************************************** - */ - -/* - ****************************************************************************** - * GLOBAL FUNCTIONS - ****************************************************************************** - */ - -void rfalAnalogConfigInitialize(void) { - /* Use default Analog configuration settings in Flash by default. */ - -/* Check whether the Default Analog settings are to be used or custom ones */ -#ifdef RFAL_ANALOG_CONFIG_CUSTOM - gRfalAnalogConfigMgmt.currentAnalogConfigTbl = (const uint8_t*)&rfalAnalogConfigCustomSettings; - gRfalAnalogConfigMgmt.configTblSize = rfalAnalogConfigCustomSettingsLength; -#else - gRfalAnalogConfigMgmt.currentAnalogConfigTbl = - (const uint8_t*)&rfalAnalogConfigDefaultSettings; - gRfalAnalogConfigMgmt.configTblSize = sizeof(rfalAnalogConfigDefaultSettings); -#endif - - gRfalAnalogConfigMgmt.ready = true; -} /* rfalAnalogConfigInitialize() */ - -bool rfalAnalogConfigIsReady(void) { - return gRfalAnalogConfigMgmt.ready; -} - -ReturnCode rfalAnalogConfigListWriteRaw(const uint8_t* configTbl, uint16_t configTblSize) { -#if RFAL_FEATURE_DYNAMIC_ANALOG_CONFIG - - /* Check if the Configuration Table exceed the Table size */ - if(configTblSize >= RFAL_ANALOG_CONFIG_TBL_SIZE) { - rfalAnalogConfigInitialize(); /* Revert to default Analog Configuration */ - return ERR_NOMEM; - } - - /* Check for invalid parameters */ - if((configTbl == NULL) || (configTblSize == 0U)) { - return ERR_PARAM; - } - - /* NOTE: Function does not check for the validity of the Table contents (conf IDs, conf sets, register address) */ - ST_MEMCPY(gRfalAnalogConfig, configTbl, configTblSize); - - /* Update the total size of configuration settings */ - gRfalAnalogConfigMgmt.configTblSize = configTblSize; - - rfalAnalogConfigPtrUpdate(gRfalAnalogConfig); - return ERR_NONE; - -#else - - // If Analog Configuration Update is to be disabled - NO_WARNING(configTbl); - NO_WARNING(configTblSize); - return ERR_REQUEST; - -#endif /* RFAL_FEATURE_DYNAMIC_ANALOG_CONFIG */ -} - -ReturnCode rfalAnalogConfigListWrite(uint8_t more, const rfalAnalogConfig* config) { -#if RFAL_FEATURE_DYNAMIC_ANALOG_CONFIG - - rfalAnalogConfigId configId; - rfalAnalogConfigNum numConfig; - uint8_t configSize; - - if(true == gRfalAnalogConfigMgmt.ready) { /* First Update to the Configuration list. */ - gRfalAnalogConfigMgmt.ready = false; // invalidate the config List - gRfalAnalogConfigMgmt.configTblSize = 0; // Clear the config List - } - - configId = GETU16(config->id); - - /* Check validity of the Configuration ID. */ - if((RFAL_ANALOG_CONFIG_TECH_RFU <= RFAL_ANALOG_CONFIG_ID_GET_TECH(configId)) || - ((RFAL_ANALOG_CONFIG_BITRATE_6780 < RFAL_ANALOG_CONFIG_ID_GET_BITRATE(configId)) && - (RFAL_ANALOG_CONFIG_BITRATE_1OF4 > RFAL_ANALOG_CONFIG_ID_GET_BITRATE(configId))) || - (RFAL_ANALOG_CONFIG_BITRATE_1OF256 < RFAL_ANALOG_CONFIG_ID_GET_BITRATE(configId))) { - rfalAnalogConfigInitialize(); /* Revert to default Analog Configuration */ - return ERR_PARAM; - } - - numConfig = config->num; - configSize = - (uint8_t)(sizeof(rfalAnalogConfigId) + sizeof(rfalAnalogConfigNum) + (numConfig * sizeof(rfalAnalogConfigRegAddrMaskVal))); - - /* Check if the Configuration Set exceed the Table size. */ - if(RFAL_ANALOG_CONFIG_TBL_SIZE <= (gRfalAnalogConfigMgmt.configTblSize + configSize)) { - rfalAnalogConfigInitialize(); /* Revert to default Analog Configuration */ - return ERR_NOMEM; - } - - /* NOTE: Function does not check for the validity of the Register Address. */ - ST_MEMCPY( - &gRfalAnalogConfig[gRfalAnalogConfigMgmt.configTblSize], - (const uint8_t*)config, - configSize); - - /* Increment the total size of configuration settings. */ - gRfalAnalogConfigMgmt.configTblSize += configSize; - - /* Check if it is the last Analog Configuration to load. */ - if(RFAL_ANALOG_CONFIG_UPDATE_LAST == - more) { /* Update the Analog Configuration to the new settings. */ - rfalAnalogConfigPtrUpdate(gRfalAnalogConfig); - } - - return ERR_NONE; - -#else - - // If Analog Configuration Update is to be disabled - NO_WARNING(config); - NO_WARNING(more); - return ERR_DISABLED; - -#endif /* RFAL_FEATURE_DYNAMIC_ANALOG_CONFIG */ - -} /* rfalAnalogConfigListUpdate() */ - -ReturnCode - rfalAnalogConfigListReadRaw(uint8_t* tblBuf, uint16_t tblBufLen, uint16_t* configTblSize) { - /* Check if the the current table will fit into the given buffer */ - if(tblBufLen < gRfalAnalogConfigMgmt.configTblSize) { - return ERR_NOMEM; - } - - /* Check for invalid parameters */ - if(configTblSize == NULL) { - return ERR_PARAM; - } - - /* Copy the whole Table to the given buffer */ - if(gRfalAnalogConfigMgmt.configTblSize > 0U) /* MISRA 21.18 */ - { - ST_MEMCPY( - tblBuf, - gRfalAnalogConfigMgmt.currentAnalogConfigTbl, - gRfalAnalogConfigMgmt.configTblSize); - } - *configTblSize = gRfalAnalogConfigMgmt.configTblSize; - - return ERR_NONE; -} - -ReturnCode rfalAnalogConfigListRead( - rfalAnalogConfigOffset* configOffset, - uint8_t* more, - rfalAnalogConfig* config, - rfalAnalogConfigNum numConfig) { - uint16_t configSize; - rfalAnalogConfigOffset offset = *configOffset; - rfalAnalogConfigNum numConfigSet; - - /* Check if the number of register-mask-value settings for the respective Configuration ID will fit into the buffer passed in. */ - if(gRfalAnalogConfigMgmt.currentAnalogConfigTbl[offset + sizeof(rfalAnalogConfigId)] > - numConfig) { - return ERR_NOMEM; - } - - /* Get the number of Configuration set */ - numConfigSet = - gRfalAnalogConfigMgmt.currentAnalogConfigTbl[offset + sizeof(rfalAnalogConfigId)]; - - /* Pass Configuration Register-Mask-Value sets */ - configSize = - (sizeof(rfalAnalogConfigId) + sizeof(rfalAnalogConfigNum) + - (uint16_t)(numConfigSet * sizeof(rfalAnalogConfigRegAddrMaskVal))); - ST_MEMCPY((uint8_t*)config, &gRfalAnalogConfigMgmt.currentAnalogConfigTbl[offset], configSize); - *configOffset = offset + configSize; - - /* Check if it is the last Analog Configuration in the Table.*/ - *more = - (uint8_t)((*configOffset >= gRfalAnalogConfigMgmt.configTblSize) ? RFAL_ANALOG_CONFIG_UPDATE_LAST : RFAL_ANALOG_CONFIG_UPDATE_MORE); - - return ERR_NONE; -} /* rfalAnalogConfigListRead() */ - -ReturnCode rfalSetAnalogConfig(rfalAnalogConfigId configId) { - rfalAnalogConfigOffset configOffset = 0; - rfalAnalogConfigNum numConfigSet; - const rfalAnalogConfigRegAddrMaskVal* configTbl; - ReturnCode retCode = ERR_NONE; - rfalAnalogConfigNum i; - - if(true != gRfalAnalogConfigMgmt.ready) { - return ERR_REQUEST; - } - - /* Search LUT for the specific Configuration ID. */ - while(true) { - numConfigSet = rfalAnalogConfigSearch(configId, &configOffset); - if(RFAL_ANALOG_CONFIG_LUT_NOT_FOUND == numConfigSet) { - break; - } - - configTbl = - (rfalAnalogConfigRegAddrMaskVal*)((uint32_t)gRfalAnalogConfigMgmt.currentAnalogConfigTbl + (uint32_t)configOffset); - /* Increment the offset to the next index to search from. */ - configOffset += (uint16_t)(numConfigSet * sizeof(rfalAnalogConfigRegAddrMaskVal)); - - if((gRfalAnalogConfigMgmt.configTblSize + 1U) < - configOffset) { /* Error check make sure that the we do not access outside the configuration Table Size */ - return ERR_NOMEM; - } - - for(i = 0; i < numConfigSet; i++) { - if((GETU16(configTbl[i].addr) & RFAL_TEST_REG) != 0U) { - EXIT_ON_ERR( - retCode, - rfalChipChangeTestRegBits( - (GETU16(configTbl[i].addr) & ~RFAL_TEST_REG), - configTbl[i].mask, - configTbl[i].val)); - } else { - EXIT_ON_ERR( - retCode, - rfalChipChangeRegBits( - GETU16(configTbl[i].addr), configTbl[i].mask, configTbl[i].val)); - } - } - - } /* while(found Analog Config Id) */ - - return retCode; - -} /* rfalSetAnalogConfig() */ - -uint16_t rfalAnalogConfigGenModeID(rfalMode md, rfalBitRate br, uint16_t dir) { - uint16_t id; - - /* Assign Poll/Listen Mode */ - id = ((md >= RFAL_MODE_LISTEN_NFCA) ? RFAL_ANALOG_CONFIG_LISTEN : RFAL_ANALOG_CONFIG_POLL); - - /* Assign Technology */ - switch(md) { - case RFAL_MODE_POLL_NFCA: - case RFAL_MODE_POLL_NFCA_T1T: - case RFAL_MODE_LISTEN_NFCA: - id |= RFAL_ANALOG_CONFIG_TECH_NFCA; - break; - - case RFAL_MODE_POLL_NFCB: - case RFAL_MODE_POLL_B_PRIME: - case RFAL_MODE_POLL_B_CTS: - case RFAL_MODE_LISTEN_NFCB: - id |= RFAL_ANALOG_CONFIG_TECH_NFCB; - break; - - case RFAL_MODE_POLL_NFCF: - case RFAL_MODE_LISTEN_NFCF: - id |= RFAL_ANALOG_CONFIG_TECH_NFCF; - break; - - case RFAL_MODE_POLL_NFCV: - case RFAL_MODE_POLL_PICOPASS: - id |= RFAL_ANALOG_CONFIG_TECH_NFCV; - break; - - case RFAL_MODE_POLL_ACTIVE_P2P: - case RFAL_MODE_LISTEN_ACTIVE_P2P: - id |= RFAL_ANALOG_CONFIG_TECH_AP2P; - break; - - default: - id = RFAL_ANALOG_CONFIG_TECH_CHIP; - break; - } - - /* Assign Bitrate */ - id |= - (((((uint16_t)(br) >= (uint16_t)RFAL_BR_52p97) ? (uint16_t)(br) : ((uint16_t)(br) + 1U)) - << RFAL_ANALOG_CONFIG_BITRATE_SHIFT) & - RFAL_ANALOG_CONFIG_BITRATE_MASK); - - /* Assign Direction */ - id |= ((dir << RFAL_ANALOG_CONFIG_DIRECTION_SHIFT) & RFAL_ANALOG_CONFIG_DIRECTION_MASK); - - return id; - -} /* rfalAnalogConfigGenModeID() */ - -/* - ****************************************************************************** - * LOCAL FUNCTIONS - ****************************************************************************** - */ - -/*! - ***************************************************************************** - * \brief Update the link to Analog Configuration LUT - * - * Update the link to the Analog Configuration LUT for the subsequent search - * of Analog Settings. - * - * \param[in] analogConfigTbl: reference to the start of the new Analog Configuration Table - * - ***************************************************************************** - */ -#if RFAL_FEATURE_DYNAMIC_ANALOG_CONFIG -static void rfalAnalogConfigPtrUpdate(const uint8_t* analogConfigTbl) { - gRfalAnalogConfigMgmt.currentAnalogConfigTbl = analogConfigTbl; - gRfalAnalogConfigMgmt.ready = true; - -} /* rfalAnalogConfigPtrUpdate() */ -#endif /* RFAL_FEATURE_DYNAMIC_ANALOG_CONFIG */ - -/*! - ***************************************************************************** - * \brief Search the Analog Configuration LUT for a specific Configuration ID. - * - * Search the Analog Configuration LUT for the Configuration ID. - * - * \param[in] configId: Configuration ID to search for. - * \param[in] configOffset: Configuration Offset in Table - * - * \return number of Configuration Sets - * \return #RFAL_ANALOG_CONFIG_LUT_NOT_FOUND in case Configuration ID is not found. - ***************************************************************************** - */ -static rfalAnalogConfigNum - rfalAnalogConfigSearch(rfalAnalogConfigId configId, uint16_t* configOffset) { - rfalAnalogConfigId foundConfigId; - rfalAnalogConfigId configIdMaskVal; - const uint8_t* configTbl; - const uint8_t* currentConfigTbl; - uint16_t i; - - currentConfigTbl = gRfalAnalogConfigMgmt.currentAnalogConfigTbl; - configIdMaskVal = - ((RFAL_ANALOG_CONFIG_POLL_LISTEN_MODE_MASK | RFAL_ANALOG_CONFIG_BITRATE_MASK) | - ((RFAL_ANALOG_CONFIG_TECH_CHIP == RFAL_ANALOG_CONFIG_ID_GET_TECH(configId)) ? - (RFAL_ANALOG_CONFIG_TECH_MASK | RFAL_ANALOG_CONFIG_CHIP_SPECIFIC_MASK) : - configId) | - ((RFAL_ANALOG_CONFIG_NO_DIRECTION == RFAL_ANALOG_CONFIG_ID_GET_DIRECTION(configId)) ? - RFAL_ANALOG_CONFIG_DIRECTION_MASK : - configId)); - - /* When specific ConfigIDs are to be used, override search mask */ - if((RFAL_ANALOG_CONFIG_ID_GET_DIRECTION(configId) == RFAL_ANALOG_CONFIG_DPO)) { - configIdMaskVal = - (RFAL_ANALOG_CONFIG_POLL_LISTEN_MODE_MASK | RFAL_ANALOG_CONFIG_TECH_MASK | - RFAL_ANALOG_CONFIG_BITRATE_MASK | RFAL_ANALOG_CONFIG_DIRECTION_MASK); - } - - i = *configOffset; - while(i < gRfalAnalogConfigMgmt.configTblSize) { - configTbl = ¤tConfigTbl[i]; - foundConfigId = GETU16(configTbl); - if(configId == (foundConfigId & configIdMaskVal)) { - *configOffset = - (uint16_t)(i + sizeof(rfalAnalogConfigId) + sizeof(rfalAnalogConfigNum)); - return configTbl[sizeof(rfalAnalogConfigId)]; - } - - /* If Config Id does not match, increment to next Configuration Id */ - i += - (uint16_t)(sizeof(rfalAnalogConfigId) + sizeof(rfalAnalogConfigNum) + (configTbl[sizeof(rfalAnalogConfigId)] * sizeof(rfalAnalogConfigRegAddrMaskVal))); - } /* for */ - - return RFAL_ANALOG_CONFIG_LUT_NOT_FOUND; -} /* rfalAnalogConfigSearch() */ diff --git a/lib/ST25RFAL002/source/rfal_crc.c b/lib/ST25RFAL002/source/rfal_crc.c deleted file mode 100644 index bcf5ad593c8..00000000000 --- a/lib/ST25RFAL002/source/rfal_crc.c +++ /dev/null @@ -1,82 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_crc.c - * - * \author Oliver Regenfelder - * - * \brief CRC calculation implementation - * - */ - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ -#include "rfal_crc.h" - -/* -****************************************************************************** -* LOCAL FUNCTION PROTOTYPES -****************************************************************************** -*/ -static uint16_t rfalCrcUpdateCcitt(uint16_t crcSeed, uint8_t dataByte); - -/* -****************************************************************************** -* GLOBAL FUNCTIONS -****************************************************************************** -*/ -uint16_t rfalCrcCalculateCcitt(uint16_t preloadValue, const uint8_t* buf, uint16_t length) { - uint16_t crc = preloadValue; - uint16_t index; - - for(index = 0; index < length; index++) { - crc = rfalCrcUpdateCcitt(crc, buf[index]); - } - - return crc; -} - -/* -****************************************************************************** -* LOCAL FUNCTIONS -****************************************************************************** -*/ -static uint16_t rfalCrcUpdateCcitt(uint16_t crcSeed, uint8_t dataByte) { - uint16_t crc = crcSeed; - uint8_t dat = dataByte; - - dat ^= (uint8_t)(crc & 0xFFU); - dat ^= (dat << 4); - - crc = (crc >> 8) ^ (((uint16_t)dat) << 8) ^ (((uint16_t)dat) << 3) ^ (((uint16_t)dat) >> 4); - - return crc; -} diff --git a/lib/ST25RFAL002/source/rfal_dpo.c b/lib/ST25RFAL002/source/rfal_dpo.c deleted file mode 100644 index 48dd89c77cc..00000000000 --- a/lib/ST25RFAL002/source/rfal_dpo.c +++ /dev/null @@ -1,232 +0,0 @@ - -/****************************************************************************** - * @attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * $Revision: $ - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_dpo.c - * - * \author Martin Zechleitner - * - * \brief Functions to manage and set dynamic power settings. - * - */ - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "rfal_dpoTbl.h" -#include "rfal_dpo.h" -#include "platform.h" -#include "rfal_rf.h" -#include "rfal_chip.h" -#include "rfal_analogConfig.h" -#include "utils.h" - -/* - ****************************************************************************** - * ENABLE SWITCH - ****************************************************************************** - */ - -#ifndef RFAL_FEATURE_DPO -#define RFAL_FEATURE_DPO \ - false /* Dynamic Power Module configuration missing. Disabled by default */ -#endif - -#if RFAL_FEATURE_DPO - -/* - ****************************************************************************** - * DEFINES - ****************************************************************************** - */ -#define RFAL_DPO_ANALOGCONFIG_SHIFT 13U -#define RFAL_DPO_ANALOGCONFIG_MASK 0x6000U - -/* - ****************************************************************************** - * LOCAL DATA TYPES - ****************************************************************************** - */ - -static bool gRfalDpoIsEnabled = false; -static uint8_t* gRfalCurrentDpo; -static uint8_t gRfalDpoTableEntries; -static uint8_t gRfalDpo[RFAL_DPO_TABLE_SIZE_MAX]; -static uint8_t gRfalDpoTableEntry; -static rfalDpoMeasureFunc gRfalDpoMeasureCallback = NULL; - -/* - ****************************************************************************** - * GLOBAL FUNCTIONS - ****************************************************************************** - */ -void rfalDpoInitialize(void) { - /* Use the default Dynamic Power values */ - gRfalCurrentDpo = (uint8_t*)rfalDpoDefaultSettings; - gRfalDpoTableEntries = (sizeof(rfalDpoDefaultSettings) / RFAL_DPO_TABLE_PARAMETER); - - ST_MEMCPY(gRfalDpo, gRfalCurrentDpo, sizeof(rfalDpoDefaultSettings)); - - /* by default use amplitude measurement */ - gRfalDpoMeasureCallback = rfalChipMeasureAmplitude; - - /* by default DPO is disabled */ - gRfalDpoIsEnabled = false; - - gRfalDpoTableEntry = 0; -} - -void rfalDpoSetMeasureCallback(rfalDpoMeasureFunc pMeasureFunc) { - gRfalDpoMeasureCallback = pMeasureFunc; -} - -/*******************************************************************************/ -ReturnCode rfalDpoTableWrite(rfalDpoEntry* powerTbl, uint8_t powerTblEntries) { - uint8_t entry = 0; - - /* check if the table size parameter is too big */ - if((powerTblEntries * RFAL_DPO_TABLE_PARAMETER) > RFAL_DPO_TABLE_SIZE_MAX) { - return ERR_NOMEM; - } - - /* check if the first increase entry is 0xFF */ - if((powerTblEntries == 0) || (powerTbl == NULL)) { - return ERR_PARAM; - } - - /* check if the entries of the dynamic power table are valid */ - for(entry = 0; entry < powerTblEntries; entry++) { - if(powerTbl[entry].inc < powerTbl[entry].dec) { - return ERR_PARAM; - } - } - - /* copy the data set */ - ST_MEMCPY(gRfalDpo, powerTbl, (powerTblEntries * RFAL_DPO_TABLE_PARAMETER)); - gRfalCurrentDpo = gRfalDpo; - gRfalDpoTableEntries = powerTblEntries; - - if(gRfalDpoTableEntry > powerTblEntries) { - /* is always greater then zero, otherwise we already returned ERR_PARAM */ - gRfalDpoTableEntry = (powerTblEntries - 1); - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalDpoTableRead(rfalDpoEntry* tblBuf, uint8_t tblBufEntries, uint8_t* tableEntries) { - /* wrong request */ - if((tblBuf == NULL) || (tblBufEntries < gRfalDpoTableEntries) || (tableEntries == NULL)) { - return ERR_PARAM; - } - - /* Copy the whole Table to the given buffer */ - ST_MEMCPY(tblBuf, gRfalCurrentDpo, (tblBufEntries * RFAL_DPO_TABLE_PARAMETER)); - *tableEntries = gRfalDpoTableEntries; - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalDpoAdjust(void) { - uint8_t refValue = 0; - uint16_t modeID; - rfalBitRate br; - rfalDpoEntry* dpoTable = (rfalDpoEntry*)gRfalCurrentDpo; - - /* Check if the Power Adjustment is disabled and * - * if the callback to the measurement method is properly set */ - if((gRfalCurrentDpo == NULL) || (!gRfalDpoIsEnabled) || (gRfalDpoMeasureCallback == NULL)) { - return ERR_PARAM; - } - - /* Ensure that the current mode is Passive Poller */ - if(!rfalIsModePassivePoll(rfalGetMode())) { - return ERR_WRONG_STATE; - } - - /* Ensure a proper measure reference value */ - if(ERR_NONE != gRfalDpoMeasureCallback(&refValue)) { - return ERR_IO; - } - - if(refValue >= dpoTable[gRfalDpoTableEntry].inc) { /* Increase the output power */ - /* the top of the table represents the highest amplitude value*/ - if(gRfalDpoTableEntry == 0) { - /* maximum driver value has been reached */ - } else { - /* go up in the table to decrease the driver resistance */ - gRfalDpoTableEntry--; - } - } else if(refValue <= dpoTable[gRfalDpoTableEntry].dec) { /* decrease the output power */ - /* The bottom is the highest possible value */ - if((gRfalDpoTableEntry + 1) >= gRfalDpoTableEntries) { - /* minimum driver value has been reached */ - } else { - /* go down in the table to increase the driver resistance */ - gRfalDpoTableEntry++; - } - } else { - /* Fall through to always write dpo and its associated analog configs */ - } - - /* Get the new value for RFO resistance form the table and apply the new RFO resistance setting */ - rfalChipSetRFO(dpoTable[gRfalDpoTableEntry].rfoRes); - - /* Apply the DPO Analog Config according to this treshold */ - /* Technology field is being extended for DPO: 2msb are used for treshold step (only 4 allowed) */ - rfalGetBitRate(&br, NULL); /* Obtain current Tx bitrate */ - modeID = rfalAnalogConfigGenModeID( - rfalGetMode(), br, RFAL_ANALOG_CONFIG_DPO); /* Generate Analog Config mode ID */ - modeID |= - ((gRfalDpoTableEntry << RFAL_DPO_ANALOGCONFIG_SHIFT) & - RFAL_DPO_ANALOGCONFIG_MASK); /* Add DPO treshold step|level */ - rfalSetAnalogConfig(modeID); /* Apply DPO Analog Config */ - - return ERR_NONE; -} - -/*******************************************************************************/ -rfalDpoEntry* rfalDpoGetCurrentTableEntry(void) { - rfalDpoEntry* dpoTable = (rfalDpoEntry*)gRfalCurrentDpo; - return &dpoTable[gRfalDpoTableEntry]; -} - -/*******************************************************************************/ -void rfalDpoSetEnabled(bool enable) { - gRfalDpoIsEnabled = enable; -} - -/*******************************************************************************/ -bool rfalDpoIsEnabled(void) { - return gRfalDpoIsEnabled; -} - -#endif /* RFAL_FEATURE_DPO */ diff --git a/lib/ST25RFAL002/source/rfal_iso15693_2.c b/lib/ST25RFAL002/source/rfal_iso15693_2.c deleted file mode 100644 index 34ecd41113c..00000000000 --- a/lib/ST25RFAL002/source/rfal_iso15693_2.c +++ /dev/null @@ -1,514 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_iso15693_2.c - * - * \author Ulrich Herrmann - * - * \brief Implementation of ISO-15693-2 - * - */ - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ -#include "rfal_iso15693_2.h" -#include "rfal_crc.h" -#include "utils.h" - -/* - ****************************************************************************** - * ENABLE SWITCH - ****************************************************************************** - */ - -#ifndef RFAL_FEATURE_NFCV -#define RFAL_FEATURE_NFCV false /* NFC-V module configuration missing. Disabled by default */ -#endif - -#if RFAL_FEATURE_NFCV - -/* -****************************************************************************** -* LOCAL MACROS -****************************************************************************** -*/ - -#define ISO_15693_DEBUG(...) /*!< Macro for the log method */ - -/* -****************************************************************************** -* LOCAL DEFINES -****************************************************************************** -*/ -#define ISO15693_DAT_SOF_1_4 0x21 /* LSB constants */ -#define ISO15693_DAT_EOF_1_4 0x04 -#define ISO15693_DAT_00_1_4 0x02 -#define ISO15693_DAT_01_1_4 0x08 -#define ISO15693_DAT_10_1_4 0x20 -#define ISO15693_DAT_11_1_4 0x80 - -#define ISO15693_DAT_SOF_1_256 0x81 -#define ISO15693_DAT_EOF_1_256 0x04 -#define ISO15693_DAT_SLOT0_1_256 0x02 -#define ISO15693_DAT_SLOT1_1_256 0x08 -#define ISO15693_DAT_SLOT2_1_256 0x20 -#define ISO15693_DAT_SLOT3_1_256 0x80 - -#define ISO15693_PHY_DAT_MANCHESTER_1 0xaaaa - -#define ISO15693_PHY_BIT_BUFFER_SIZE \ - 1000 /*!< size of the receiving buffer. Might be adjusted if longer datastreams are expected. */ - -/* -****************************************************************************** -* LOCAL VARIABLES -****************************************************************************** -*/ -static iso15693PhyConfig_t iso15693PhyConfig; /*!< current phy configuration */ - -/* -****************************************************************************** -* LOCAL FUNCTION PROTOTYPES -****************************************************************************** -*/ -static ReturnCode iso15693PhyVCDCode1Of4( - const uint8_t data, - uint8_t* outbuffer, - uint16_t maxOutBufLen, - uint16_t* outBufLen); -static ReturnCode iso15693PhyVCDCode1Of256( - const uint8_t data, - uint8_t* outbuffer, - uint16_t maxOutBufLen, - uint16_t* outBufLen); - -/* -****************************************************************************** -* GLOBAL FUNCTIONS -****************************************************************************** -*/ -ReturnCode iso15693PhyConfigure( - const iso15693PhyConfig_t* config, - const struct iso15693StreamConfig** needed_stream_config) { - static struct iso15693StreamConfig stream_config = { - /* MISRA 8.9 */ - .useBPSK = 0, /* 0: subcarrier, 1:BPSK */ - .din = 5, /* 2^5*fc = 423750 Hz: divider for the in subcarrier frequency */ - .dout = 7, /*!< 2^7*fc = 105937 : divider for the in subcarrier frequency */ - .report_period_length = 3, /*!< 8=2^3 the length of the reporting period */ - }; - - /* make a copy of the configuration */ - ST_MEMCPY((uint8_t*)&iso15693PhyConfig, (const uint8_t*)config, sizeof(iso15693PhyConfig_t)); - - if(config->speedMode <= 3U) { /* If valid speed mode adjust report period accordingly */ - stream_config.report_period_length = (3U - (uint8_t)config->speedMode); - } else { /* If invalid default to normal (high) speed */ - stream_config.report_period_length = 3; - } - - *needed_stream_config = &stream_config; - - return ERR_NONE; -} - -ReturnCode iso15693PhyGetConfiguration(iso15693PhyConfig_t* config) { - ST_MEMCPY(config, &iso15693PhyConfig, sizeof(iso15693PhyConfig_t)); - - return ERR_NONE; -} - -ReturnCode iso15693VCDCode( - uint8_t* buffer, - uint16_t length, - bool sendCrc, - bool sendFlags, - bool picopassMode, - uint16_t* subbit_total_length, - uint16_t* offset, - uint8_t* outbuf, - uint16_t outBufSize, - uint16_t* actOutBufSize) { - ReturnCode err = ERR_NONE; - uint8_t eof, sof; - uint8_t transbuf[2]; - uint16_t crc = 0; - ReturnCode (*txFunc)( - const uint8_t data, uint8_t* outbuffer, uint16_t maxOutBufLen, uint16_t* outBufLen); - uint8_t crc_len; - uint8_t* outputBuf; - uint16_t outputBufSize; - - crc_len = (uint8_t)((sendCrc) ? 2 : 0); - - *actOutBufSize = 0; - - if(ISO15693_VCD_CODING_1_4 == iso15693PhyConfig.coding) { - sof = ISO15693_DAT_SOF_1_4; - eof = ISO15693_DAT_EOF_1_4; - txFunc = iso15693PhyVCDCode1Of4; - *subbit_total_length = - ((1U /* SOF */ - + ((length + (uint16_t)crc_len) * 4U) + 1U) /* EOF */ - ); - if(outBufSize < 5U) { /* 5 should be safe: enough for sof + 1byte data in 1of4 */ - return ERR_NOMEM; - } - } else { - sof = ISO15693_DAT_SOF_1_256; - eof = ISO15693_DAT_EOF_1_256; - txFunc = iso15693PhyVCDCode1Of256; - *subbit_total_length = - ((1U /* SOF */ - + ((length + (uint16_t)crc_len) * 64U) + 1U) /* EOF */ - ); - - if(*offset != 0U) { - if(outBufSize < 64U) { /* 64 should be safe: enough a single byte data in 1of256 */ - return ERR_NOMEM; - } - } else { - if(outBufSize < - 65U) { /* At beginning of a frame we need at least 65 bytes to start: enough for sof + 1byte data in 1of256 */ - return ERR_NOMEM; - } - } - } - - if(length == 0U) { - *subbit_total_length = 1; - } - - if((length != 0U) && (0U == *offset) && sendFlags && !picopassMode) { - /* set high datarate flag */ - buffer[0] |= (uint8_t)ISO15693_REQ_FLAG_HIGH_DATARATE; - /* clear sub-carrier flag - we only support single sub-carrier */ - buffer[0] = (uint8_t)(buffer[0] & ~ISO15693_REQ_FLAG_TWO_SUBCARRIERS); /* MISRA 10.3 */ - } - - outputBuf = outbuf; /* MISRA 17.8: Use intermediate variable */ - outputBufSize = outBufSize; /* MISRA 17.8: Use intermediate variable */ - - /* Send SOF if at 0 offset */ - if((length != 0U) && (0U == *offset)) { - *outputBuf = sof; - (*actOutBufSize)++; - outputBufSize--; - outputBuf++; - } - - while((*offset < length) && (err == ERR_NONE)) { - uint16_t filled_size; - /* send data */ - err = txFunc(buffer[*offset], outputBuf, outputBufSize, &filled_size); - (*actOutBufSize) += filled_size; - outputBuf = &outputBuf[filled_size]; /* MISRA 18.4: Avoid pointer arithmetic */ - outputBufSize -= filled_size; - if(err == ERR_NONE) { - (*offset)++; - } - } - if(err != ERR_NONE) { - return ERR_AGAIN; - } - - while((err == ERR_NONE) && sendCrc && (*offset < (length + 2U))) { - uint16_t filled_size; - if(0U == crc) { - crc = rfalCrcCalculateCcitt( - (uint16_t)((picopassMode) ? 0xE012U : 0xFFFFU), /* In PicoPass Mode a different Preset Value is used */ - ((picopassMode) ? - (buffer + 1U) : - buffer), /* CMD byte is not taken into account in PicoPass mode */ - ((picopassMode) ? - (length - 1U) : - length)); /* CMD byte is not taken into account in PicoPass mode */ - - crc = (uint16_t)((picopassMode) ? crc : ~crc); - } - /* send crc */ - transbuf[0] = (uint8_t)(crc & 0xffU); - transbuf[1] = (uint8_t)((crc >> 8) & 0xffU); - err = txFunc(transbuf[*offset - length], outputBuf, outputBufSize, &filled_size); - (*actOutBufSize) += filled_size; - outputBuf = &outputBuf[filled_size]; /* MISRA 18.4: Avoid pointer arithmetic */ - outputBufSize -= filled_size; - if(err == ERR_NONE) { - (*offset)++; - } - } - if(err != ERR_NONE) { - return ERR_AGAIN; - } - - if((!sendCrc && (*offset == length)) || (sendCrc && (*offset == (length + 2U)))) { - *outputBuf = eof; - (*actOutBufSize)++; - outputBufSize--; - outputBuf++; - } else { - return ERR_AGAIN; - } - - return err; -} - -ReturnCode iso15693VICCDecode( - const uint8_t* inBuf, - uint16_t inBufLen, - uint8_t* outBuf, - uint16_t outBufLen, - uint16_t* outBufPos, - uint16_t* bitsBeforeCol, - uint16_t ignoreBits, - bool picopassMode) { - ReturnCode err = ERR_NONE; - uint16_t crc; - uint16_t mp; /* Current bit position in manchester bit inBuf*/ - uint16_t bp; /* Current bit position in outBuf */ - - *bitsBeforeCol = 0; - *outBufPos = 0; - - /* first check for valid SOF. Since it starts with 3 unmodulated pulses it is 0x17. */ - if((inBuf[0] & 0x1fU) != 0x17U) { - ISO_15693_DEBUG("0x%x\n", iso15693PhyBitBuffer[0]); - return ERR_FRAMING; - } - ISO_15693_DEBUG("SOF\n"); - - if(outBufLen == 0U) { - return ERR_NONE; - } - - mp = 5; /* 5 bits were SOF, now manchester starts: 2 bits per payload bit */ - bp = 0; - - ST_MEMSET(outBuf, 0, outBufLen); - - if(inBufLen == 0U) { - return ERR_CRC; - } - - for(; mp < ((inBufLen * 8U) - 2U); mp += 2U) { - bool isEOF = false; - - uint8_t man; - man = (inBuf[mp / 8U] >> (mp % 8U)) & 0x1U; - man |= ((inBuf[(mp + 1U) / 8U] >> ((mp + 1U) % 8U)) & 0x1U) << 1; - if(1U == man) { - bp++; - } - if(2U == man) { - outBuf[bp / 8U] = (uint8_t)(outBuf[bp / 8U] | (1U << (bp % 8U))); /* MISRA 10.3 */ - bp++; - } - if((bp % 8U) == 0U) { /* Check for EOF */ - ISO_15693_DEBUG("ceof %hhx %hhx\n", inBuf[mp / 8U], inBuf[mp / 8 + 1]); - if(((inBuf[mp / 8U] & 0xe0U) == 0xa0U) && - (inBuf[(mp / 8U) + 1U] == 0x03U)) { /* Now we know that it was 10111000 = EOF */ - ISO_15693_DEBUG("EOF\n"); - isEOF = true; - } - } - if(((0U == man) || (3U == man)) && !isEOF) { - if(bp >= ignoreBits) { - err = ERR_RF_COLLISION; - } else { - /* ignored collision: leave as 0 */ - bp++; - } - } - if((bp >= (outBufLen * 8U)) || (err == ERR_RF_COLLISION) || - isEOF) { /* Don't write beyond the end */ - break; - } - } - - *outBufPos = (bp / 8U); - *bitsBeforeCol = bp; - - if(err != ERR_NONE) { - return err; - } - - if((bp % 8U) != 0U) { - return ERR_CRC; - } - - if(*outBufPos > 2U) { - /* finally, check crc */ - ISO_15693_DEBUG("Calculate CRC, val: 0x%x, outBufLen: ", *outBuf); - ISO_15693_DEBUG("0x%x ", *outBufPos - 2); - - crc = rfalCrcCalculateCcitt(((picopassMode) ? 0xE012U : 0xFFFFU), outBuf, *outBufPos - 2U); - crc = (uint16_t)((picopassMode) ? crc : ~crc); - - if(((crc & 0xffU) == outBuf[*outBufPos - 2U]) && - (((crc >> 8U) & 0xffU) == outBuf[*outBufPos - 1U])) { - err = ERR_NONE; - ISO_15693_DEBUG("OK\n"); - } else { - ISO_15693_DEBUG("error! Expected: 0x%x, got ", crc); - ISO_15693_DEBUG("0x%hhx 0x%hhx\n", outBuf[*outBufPos - 2], outBuf[*outBufPos - 1]); - err = ERR_CRC; - } - } else { - err = ERR_CRC; - } - - return err; -} - -/* -****************************************************************************** -* LOCAL FUNCTIONS -****************************************************************************** -*/ -/*! - ***************************************************************************** - * \brief Perform 1 of 4 coding and send coded data - * - * This function takes \a length bytes from \a buffer, perform 1 of 4 coding - * (see ISO15693-2 specification) and sends the data using stream mode. - * - * \param[in] sendSof : send SOF prior to data. - * \param[in] buffer : data to send. - * \param[in] length : number of bytes to send. - * - * \return ERR_IO : Error during communication. - * \return ERR_NONE : No error. - * - ***************************************************************************** - */ -static ReturnCode iso15693PhyVCDCode1Of4( - const uint8_t data, - uint8_t* outbuffer, - uint16_t maxOutBufLen, - uint16_t* outBufLen) { - uint8_t tmp; - ReturnCode err = ERR_NONE; - uint16_t a; - uint8_t* outbuf = outbuffer; - - *outBufLen = 0; - - if(maxOutBufLen < 4U) { - return ERR_NOMEM; - } - - tmp = data; - for(a = 0; a < 4U; a++) { - switch(tmp & 0x3U) { - case 0: - *outbuf = ISO15693_DAT_00_1_4; - break; - case 1: - *outbuf = ISO15693_DAT_01_1_4; - break; - case 2: - *outbuf = ISO15693_DAT_10_1_4; - break; - case 3: - *outbuf = ISO15693_DAT_11_1_4; - break; - default: - /* MISRA 16.4: mandatory default statement */ - break; - } - outbuf++; - (*outBufLen)++; - tmp >>= 2; - } - return err; -} - -/*! - ***************************************************************************** - * \brief Perform 1 of 256 coding and send coded data - * - * This function takes \a length bytes from \a buffer, perform 1 of 256 coding - * (see ISO15693-2 specification) and sends the data using stream mode. - * \note This function sends SOF prior to the data. - * - * \param[in] sendSof : send SOF prior to data. - * \param[in] buffer : data to send. - * \param[in] length : number of bytes to send. - * - * \return ERR_IO : Error during communication. - * \return ERR_NONE : No error. - * - ***************************************************************************** - */ -static ReturnCode iso15693PhyVCDCode1Of256( - const uint8_t data, - uint8_t* outbuffer, - uint16_t maxOutBufLen, - uint16_t* outBufLen) { - uint8_t tmp; - ReturnCode err = ERR_NONE; - uint16_t a; - uint8_t* outbuf = outbuffer; - - *outBufLen = 0; - - if(maxOutBufLen < 64U) { - return ERR_NOMEM; - } - - tmp = data; - for(a = 0; a < 64U; a++) { - switch(tmp) { - case 0: - *outbuf = ISO15693_DAT_SLOT0_1_256; - break; - case 1: - *outbuf = ISO15693_DAT_SLOT1_1_256; - break; - case 2: - *outbuf = ISO15693_DAT_SLOT2_1_256; - break; - case 3: - *outbuf = ISO15693_DAT_SLOT3_1_256; - break; - default: - *outbuf = 0; - break; - } - outbuf++; - (*outBufLen)++; - tmp -= 4U; - } - - return err; -} - -#endif /* RFAL_FEATURE_NFCV */ diff --git a/lib/ST25RFAL002/source/rfal_isoDep.c b/lib/ST25RFAL002/source/rfal_isoDep.c deleted file mode 100644 index 77e67521dcd..00000000000 --- a/lib/ST25RFAL002/source/rfal_isoDep.c +++ /dev/null @@ -1,3032 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: NFCC firmware - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_isoDep.c - * - * \author Gustavo Patricio - * - * \brief Implementation of ISO-DEP protocol - * - * This implementation was based on the following specs: - * - ISO/IEC 14443-4 2nd Edition 2008-07-15 - * - NFC Forum Digital Protocol 1.1 2014-01-14 - * - */ - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ - -#include "rfal_isoDep.h" -#include "rfal_rf.h" -#include "utils.h" - -/* - ****************************************************************************** - * ENABLE SWITCH - ****************************************************************************** - */ - -#if RFAL_FEATURE_ISO_DEP - -#if(!RFAL_FEATURE_ISO_DEP_POLL && !RFAL_FEATURE_ISO_DEP_LISTEN) -#error \ - " RFAL: Invalid ISO-DEP Configuration. Please select at least one mode: Poller and/or Listener. " -#endif - -/* Check for valid I-Block length [RFAL_ISODEP_FSX_16 ; RFAL_ISODEP_FSX_4096]*/ -#if((RFAL_FEATURE_ISO_DEP_IBLOCK_MAX_LEN > 4096) || (RFAL_FEATURE_ISO_DEP_IBLOCK_MAX_LEN < 16)) -#error \ - " RFAL: Invalid ISO-DEP IBlock Max length. Please change RFAL_FEATURE_ISO_DEP_IBLOCK_MAX_LEN. " -#endif - -/* Check for valid APDU length. */ -#if((RFAL_FEATURE_ISO_DEP_APDU_MAX_LEN < RFAL_FEATURE_ISO_DEP_IBLOCK_MAX_LEN)) -#error " RFAL: Invalid ISO-DEP APDU Max length. Please change RFAL_FEATURE_ISO_DEP_APDU_MAX_LEN. " -#endif - -/* - ****************************************************************************** - * DEFINES - ****************************************************************************** - */ -#define ISODEP_CRC_LEN RFAL_CRC_LEN /*!< ISO1443 CRC Length */ - -#define ISODEP_PCB_POS (0U) /*!< PCB position on message header*/ -#define ISODEP_SWTX_INF_POS (1U) /*!< INF position in a S-WTX */ - -#define ISODEP_DID_POS (1U) /*!< DID position on message header*/ -#define ISODEP_SWTX_PARAM_LEN (1U) /*!< SWTX parameter length */ - -#define ISODEP_DSL_MAX_LEN \ - (RFAL_ISODEP_PCB_LEN + RFAL_ISODEP_DID_LEN) /*!< Deselect Req/Res length */ - -#define ISODEP_PCB_xBLOCK_MASK (0xC0U) /*!< Bit mask for Block type */ -#define ISODEP_PCB_IBLOCK (0x00U) /*!< Bit mask indicating a I-Block */ -#define ISODEP_PCB_RBLOCK (0x80U) /*!< Bit mask indicating a R-Block */ -#define ISODEP_PCB_SBLOCK (0xC0U) /*!< Bit mask indicating a S-Block */ -#define ISODEP_PCB_INVALID (0x40U) /*!< Bit mask of an Invalid PCB */ - -#define ISODEP_HDR_MAX_LEN \ - (RFAL_ISODEP_PCB_LEN + RFAL_ISODEP_DID_LEN + \ - RFAL_ISODEP_NAD_LEN) /*!< Max header length (PCB + DID + NAD) */ - -#define ISODEP_PCB_IB_VALID_MASK \ - (ISODEP_PCB_B6_BIT | ISODEP_PCB_B2_BIT) /*!< Bit mask for the MUST bits on I-Block */ -#define ISODEP_PCB_IB_VALID_VAL \ - (ISODEP_PCB_B2_BIT) /*!< Value for the MUST bits on I-Block */ -#define ISODEP_PCB_RB_VALID_MASK \ - (ISODEP_PCB_B6_BIT | ISODEP_PCB_B3_BIT | \ - ISODEP_PCB_B2_BIT) /*!< Bit mask for the MUST bits on R-Block */ -#define ISODEP_PCB_RB_VALID_VAL \ - (ISODEP_PCB_B6_BIT | ISODEP_PCB_B2_BIT) /*!< Value for the MUST bits on R-Block */ -#define ISODEP_PCB_SB_VALID_MASK \ - (ISODEP_PCB_B3_BIT | ISODEP_PCB_B2_BIT | \ - ISODEP_PCB_B1_BIT) /*!< Bit mask for the MUST bits on I-Block */ -#define ISODEP_PCB_SB_VALID_VAL \ - (ISODEP_PCB_B2_BIT) /*!< Value for the MUST bits on I-Block */ - -#define ISODEP_PCB_B1_BIT \ - (0x01U) /*!< Bit mask for the RFU S Blocks */ -#define ISODEP_PCB_B2_BIT \ - (0x02U) /*!< Bit mask for the RFU bit2 in I,S,R Blocks */ -#define ISODEP_PCB_B3_BIT \ - (0x04U) /*!< Bit mask for the RFU bit3 in R Blocks */ -#define ISODEP_PCB_B6_BIT \ - (0x20U) /*!< Bit mask for the RFU bit2 in R Blocks */ -#define ISODEP_PCB_CHAINING_BIT \ - (0x10U) /*!< Bit mask for the chaining bit of an ISO DEP I-Block in PCB. */ -#define ISODEP_PCB_DID_BIT \ - (0x08U) /*!< Bit mask for the DID presence bit of an ISO DEP I,S,R Blocks PCB. */ -#define ISODEP_PCB_NAD_BIT \ - (0x04U) /*!< Bit mask for the NAD presence bit of an ISO DEP I,S,R Blocks in PCB */ -#define ISODEP_PCB_BN_MASK \ - (0x01U) /*!< Bit mask for the block number of an ISO DEP I,R Block in PCB */ - -#define ISODEP_SWTX_PL_MASK \ - (0xC0U) /*!< Bit mask for the Power Level bits of the inf byte of an WTX request or response */ -#define ISODEP_SWTX_WTXM_MASK \ - (0x3FU) /*!< Bit mask for the WTXM bits of the inf byte of an WTX request or response */ - -#define ISODEP_RBLOCK_INF_LEN (0U) /*!< INF length of R-Block Digital 1.1 15.1.3 */ -#define ISODEP_SDSL_INF_LEN (0U) /*!< INF length of S(DSL) Digital 1.1 15.1.3 */ -#define ISODEP_SWTX_INF_LEN (1U) /*!< INF length of S(WTX) Digital 1.1 15.2.2 */ - -#define ISODEP_WTXM_MIN (1U) /*!< Minimum allowed value for the WTXM, Digital 1.0 13.2.2 */ -#define ISODEP_WTXM_MAX (59U) /*!< Maximum allowed value for the WTXM, Digital 1.0 13.2.2 */ - -#define ISODEP_PCB_Sxx_MASK (0x30U) /*!< Bit mask for the S-Block type */ -#define ISODEP_PCB_DESELECT (0x00U) /*!< Bit mask for S-Block indicating Deselect */ -#define ISODEP_PCB_WTX (0x30U) /*!< Bit mask for S-Block indicating Waiting Time eXtension */ - -#define ISODEP_PCB_Rx_MASK (0x10U) /*!< Bit mask for the R-Block type */ -#define ISODEP_PCB_ACK (0x00U) /*!< Bit mask for R-Block indicating ACK */ -#define ISODEP_PCB_NAK (0x10U) /*!< Bit mask for R-Block indicating NAK */ - -/*! Maximum length of control message (no INF) */ -#define ISODEP_CONTROLMSG_BUF_LEN \ - (RFAL_ISODEP_PCB_LEN + RFAL_ISODEP_DID_LEN + RFAL_ISODEP_NAD_LEN + ISODEP_SWTX_PARAM_LEN) - -#define ISODEP_FWT_DEACTIVATION \ - (71680U) /*!< FWT used for DESELECT Digital 2.2 B10 ISO1444-4 7.2 & 8.1 */ -#define ISODEP_MAX_RERUNS (0x0FFFFFFFU) /*!< Maximum rerun retrys for a blocking protocol run*/ - -#define ISODEP_PCBSBLOCK \ - (0x00U | ISODEP_PCB_SBLOCK | ISODEP_PCB_B2_BIT) /*!< PCB Value of a S-Block */ -#define ISODEP_PCB_SDSL \ - (ISODEP_PCBSBLOCK | ISODEP_PCB_DESELECT) /*!< PCB Value of a S-Block with DESELECT */ -#define ISODEP_PCB_SWTX \ - (ISODEP_PCBSBLOCK | ISODEP_PCB_WTX) /*!< PCB Value of a S-Block with WTX */ -#define ISODEP_PCB_SPARAMETERS \ - (ISODEP_PCB_SBLOCK | ISODEP_PCB_WTX) /*!< PCB Value of a S-Block with PARAMETERS */ - -#define ISODEP_FWI_LIS_MAX_NFC \ - 8U /*!< FWT Listener Max FWIT4ATmax FWIBmax Digital 1.1 A6 & A3 */ -#define ISODEP_FWI_LIS_MAX_EMVCO \ - 7U /*!< FWT Listener Max FWIMAX EMVCo 2.6 A.5 */ -#define ISODEP_FWI_LIS_MAX \ - (uint8_t)( \ - (gIsoDep.compMode == RFAL_COMPLIANCE_MODE_EMV) ? \ - ISODEP_FWI_LIS_MAX_EMVCO : \ - ISODEP_FWI_LIS_MAX_NFC) /*!< FWI Listener Max as NFC / EMVCo */ -#define ISODEP_FWT_LIS_MAX \ - rfalIsoDepFWI2FWT(ISODEP_FWI_LIS_MAX) /*!< FWT Listener Max */ - -#define ISODEP_FWI_MIN_10 (1U) /*!< Minimum value for FWI Digital 1.0 11.6.2.17 */ -#define ISODEP_FWI_MIN_11 (0U) /*!< Default value for FWI Digital 1.1 13.6.2 */ -#define ISODEP_FWI_MAX (14U) /*!< Maximum value for FWI Digital 1.0 11.6.2.17 */ -#define ISODEP_SFGI_MIN (0U) /*!< Default value for FWI Digital 1.1 13.6.2.22 */ -#define ISODEP_SFGI_MAX (14U) /*!< Maximum value for FWI Digital 1.1 13.6.2.22 */ - -#define RFAL_ISODEP_SPARAM_TVL_HDR_LEN (2U) /*!< S(PARAMETERS) TVL header length: Tag + Len */ -#define RFAL_ISODEP_SPARAM_HDR_LEN \ - (RFAL_ISODEP_PCB_LEN + \ - RFAL_ISODEP_SPARAM_TVL_HDR_LEN) /*!< S(PARAMETERS) header length: PCB + Tag + Len */ - -/**********************************************************************************************************************/ -/**********************************************************************************************************************/ -#define RFAL_ISODEP_NO_PARAM (0U) /*!< No parameter flag for isoDepHandleControlMsg() */ - -#define RFAL_ISODEP_CMD_RATS (0xE0U) /*!< RATS command Digital 1.1 13.6.1 */ - -#define RFAL_ISODEP_ATS_MIN_LEN (1U) /*!< Minimum ATS length Digital 1.1 13.6.2 */ -#define RFAL_ISODEP_ATS_HDR_LEN (5U) /*!< ATS headerlength Digital 1.1 13.6.2 */ -#define RFAL_ISODEP_ATS_MAX_LEN \ - (RFAL_ISODEP_ATS_HDR_LEN + \ - RFAL_ISODEP_ATS_HB_MAX_LEN) /*!< Maximum ATS length Digital 1.1 13.6.2 */ -#define RFAL_ISODEP_ATS_T0_FSCI_MASK (0x0FU) /*!< ATS T0's FSCI mask Digital 1.1 13.6.2 */ -#define RFAL_ISODEP_ATS_TB_FWI_SHIFT (4U) /*!< ATS TB's FWI shift Digital 1.1 13.6.2 */ -#define RFAL_ISODEP_ATS_FWI_MASK (0x0FU) /*!< ATS TB's FWI shift Digital 1.1 13.6.2 */ -#define RFAL_ISODEP_ATS_TL_POS (0x00U) /*!< ATS TL's position Digital 1.1 13.6.2 */ - -#define RFAL_ISODEP_PPS_SB (0xD0U) /*!< PPS REQ PPSS's SB value (no CID) ISO14443-4 5.3 */ -#define RFAL_ISODEP_PPS_MASK (0xF0U) /*!< PPS REQ PPSS's SB mask ISO14443-4 5.3 */ -#define RFAL_ISODEP_PPS_SB_DID_MASK \ - (0x0FU) /*!< PPS REQ PPSS's DID|CID mask ISO14443-4 5.3 */ -#define RFAL_ISODEP_PPS_PPS0_PPS1_PRESENT \ - (0x11U) /*!< PPS REQ PPS0 indicating that PPS1 is present */ -#define RFAL_ISODEP_PPS_PPS1 (0x00U) /*!< PPS REQ PPS1 fixed value ISO14443-4 5.3 */ -#define RFAL_ISODEP_PPS_PPS1_DSI_SHIFT \ - (2U) /*!< PPS REQ PPS1 fixed value ISO14443-4 5.3 */ -#define RFAL_ISODEP_PPS_PPS1_DXI_MASK \ - (0x0FU) /*!< PPS REQ PPS1 fixed value ISO14443-4 5.3 */ -#define RFAL_ISODEP_PPS_RES_LEN (1U) /*!< PPS Response length ISO14443-4 5.4 */ -#define RFAL_ISODEP_PPS_STARTBYTE_POS \ - (0U) /*!< PPS REQ PPSS's byte position ISO14443-4 5.4 */ -#define RFAL_ISODEP_PPS_PPS0_POS (1U) /*!< PPS REQ PPS0's byte position ISO14443-4 5.4 */ -#define RFAL_ISODEP_PPS_PPS1_POS (2U) /*!< PPS REQ PPS1's byte position ISO14443-4 5.4 */ -#define RFAL_ISODEP_PPS0_VALID_MASK \ - (0xEFU) /*!< PPS REQ PPS0 valid coding mask ISO14443-4 5.4 */ - -#define RFAL_ISODEP_CMD_ATTRIB (0x1DU) /*!< ATTRIB command Digital 1.1 14.6.1 */ -#define RFAL_ISODEP_ATTRIB_PARAM2_DSI_SHIFT \ - (6U) /*!< ATTRIB PARAM2 DSI shift Digital 1.1 14.6.1 */ -#define RFAL_ISODEP_ATTRIB_PARAM2_DRI_SHIFT \ - (4U) /*!< ATTRIB PARAM2 DRI shift Digital 1.1 14.6.1 */ -#define RFAL_ISODEP_ATTRIB_PARAM2_DXI_MASK \ - (0xF0U) /*!< ATTRIB PARAM2 DxI mask Digital 1.1 14.6.1 */ -#define RFAL_ISODEP_ATTRIB_PARAM2_FSDI_MASK \ - (0x0FU) /*!< ATTRIB PARAM2 FSDI mask Digital 1.1 14.6.1 */ -#define RFAL_ISODEP_ATTRIB_PARAM4_DID_MASK \ - (0x0FU) /*!< ATTRIB PARAM4 DID mask Digital 1.1 14.6.1 */ -#define RFAL_ISODEP_ATTRIB_HDR_LEN (9U) /*!< ATTRIB REQ header length Digital 1.1 14.6.1 */ - -#define RFAL_ISODEP_ATTRIB_RES_HDR_LEN \ - (1U) /*!< ATTRIB RES header length Digital 1.1 14.6.2 */ -#define RFAL_ISODEP_ATTRIB_RES_MBLIDID_POS \ - (0U) /*!< ATTRIB RES MBLI|DID position Digital 1.1 14.6.2 */ -#define RFAL_ISODEP_ATTRIB_RES_DID_MASK \ - (0x0FU) /*!< ATTRIB RES DID mask Digital 1.1 14.6.2 */ -#define RFAL_ISODEP_ATTRIB_RES_MBLI_MASK \ - (0x0FU) /*!< ATTRIB RES MBLI mask Digital 1.1 14.6.2 */ -#define RFAL_ISODEP_ATTRIB_RES_MBLI_SHIFT \ - (4U) /*!< ATTRIB RES MBLI shift Digital 1.1 14.6.2 */ - -#define RFAL_ISODEP_DID_MASK (0x0FU) /*!< ISODEP's DID mask */ -#define RFAL_ISODEP_DID_00 (0U) /*!< ISODEP's DID value 0 */ - -#define RFAL_ISODEP_FSDI_MAX_NFC (8U) /*!< Max FSDI value Digital 2.0 14.6.1.9 & B7 & B8 */ -#define RFAL_ISODEP_FSDI_MAX_NFC_21 \ - (0x0CU) /*!< Max FSDI value Digital 2.1 14.6.1.9 & Table 72 */ -#define RFAL_ISODEP_FSDI_MAX_EMV (0x0CU) /*!< Max FSDI value EMVCo 3.0 5.7.2.5 */ - -#define RFAL_ISODEP_RATS_PARAM_FSDI_MASK \ - (0xF0U) /*!< Mask bits for FSDI in RATS */ -#define RFAL_ISODEP_RATS_PARAM_FSDI_SHIFT \ - (4U) /*!< Shift for FSDI in RATS */ -#define RFAL_ISODEP_RATS_PARAM_DID_MASK \ - (0x0FU) /*!< Mask bits for DID in RATS */ - -#define RFAL_ISODEP_ATS_TL_OFFSET \ - (0x00U) /*!< Offset of TL on ATS */ -#define RFAL_ISODEP_ATS_TA_OFFSET \ - (0x02U) /*!< Offset of TA if it is present on ATS */ -#define RFAL_ISODEP_ATS_TB_OFFSET \ - (0x03U) /*!< Offset of TB if both TA and TB is present on ATS */ -#define RFAL_ISODEP_ATS_TC_OFFSET \ - (0x04U) /*!< Offset of TC if both TA,TB & TC are present on ATS */ -#define RFAL_ISODEP_ATS_HIST_OFFSET \ - (0x05U) /*!< Offset of Historical Bytes if TA, TB & TC are present on ATS */ -#define RFAL_ISODEP_ATS_TC_ADV_FEAT \ - (0x10U) /*!< Bit mask indicating support for Advanced protocol features: DID & NAD */ -#define RFAL_ISODEP_ATS_TC_DID (0x02U) /*!< Bit mask indicating support for DID */ -#define RFAL_ISODEP_ATS_TC_NAD (0x01U) /*!< Bit mask indicating support for NAD */ - -#define RFAL_ISODEP_PPS0_PPS1_PRESENT \ - (0x11U) /*!< PPS0 byte indicating that PPS1 is present */ -#define RFAL_ISODEP_PPS0_PPS1_NOT_PRESENT \ - (0x01U) /*!< PPS0 byte indicating that PPS1 is NOT present */ -#define RFAL_ISODEP_PPS1_DRI_MASK \ - (0x03U) /*!< PPS1 byte DRI mask bits */ -#define RFAL_ISODEP_PPS1_DSI_MASK \ - (0x0CU) /*!< PPS1 byte DSI mask bits */ -#define RFAL_ISODEP_PPS1_DSI_SHIFT \ - (2U) /*!< PPS1 byte DSI shift */ -#define RFAL_ISODEP_PPS1_DxI_MASK \ - (0x03U) /*!< PPS1 byte DSI/DRS mask bits */ - -/*! Delta Time for polling during Activation (ATS) : 20ms Digital 1.0 11.7.1.1 & A.7 */ -#define RFAL_ISODEP_T4T_DTIME_POLL_10 rfalConvMsTo1fc(20) - -/*! Delta Time for polling during Activation (ATS) : 16.4ms Digital 1.1 13.8.1.1 & A.6 - * Use 16 ms as testcase T4AT_BI_10_03 sends a frame exactly at the border */ -#define RFAL_ISODEP_T4T_DTIME_POLL_11 216960U - -/*! Activation frame waiting time FWT(act) = 71680/fc (~5286us) Digital 1.1 13.8.1.1 & A.6 */ -#define RFAL_ISODEP_T4T_FWT_ACTIVATION (71680U + RFAL_ISODEP_T4T_DTIME_POLL_11) - -/*! Delta frame waiting time = 16/fc Digital 1.0 11.7.1.3 & A.7*/ -#define RFAL_ISODEP_DFWT_10 16U - -/*! Delta frame waiting time = 16/fc Digital 2.0 14.8.1.3 & B.7*/ -#define RFAL_ISODEP_DFWT_20 49152U - -/* - ****************************************************************************** - * MACROS - ****************************************************************************** - */ - -#define isoDep_PCBisIBlock(pcb) \ - (((pcb) & (ISODEP_PCB_xBLOCK_MASK | ISODEP_PCB_IB_VALID_MASK)) == \ - (ISODEP_PCB_IBLOCK | ISODEP_PCB_IB_VALID_VAL)) /*!< Checks if pcb is a I-Block */ -#define isoDep_PCBisRBlock(pcb) \ - (((pcb) & (ISODEP_PCB_xBLOCK_MASK | ISODEP_PCB_RB_VALID_MASK)) == \ - (ISODEP_PCB_RBLOCK | ISODEP_PCB_RB_VALID_VAL)) /*!< Checks if pcb is a R-Block */ -#define isoDep_PCBisSBlock(pcb) \ - (((pcb) & (ISODEP_PCB_xBLOCK_MASK | ISODEP_PCB_SB_VALID_MASK)) == \ - (ISODEP_PCB_SBLOCK | ISODEP_PCB_SB_VALID_VAL)) /*!< Checks if pcb is a S-Block */ - -#define isoDep_PCBisChaining(pcb) \ - (((pcb)&ISODEP_PCB_CHAINING_BIT) == \ - ISODEP_PCB_CHAINING_BIT) /*!< Checks if pcb is indicating chaining */ - -#define isoDep_PCBisDeselect(pcb) \ - (((pcb)&ISODEP_PCB_Sxx_MASK) == \ - ISODEP_PCB_DESELECT) /*!< Checks if pcb is indicating DESELECT */ -#define isoDep_PCBisWTX(pcb) \ - (((pcb)&ISODEP_PCB_Sxx_MASK) == ISODEP_PCB_WTX) /*!< Checks if pcb is indicating WTX */ - -#define isoDep_PCBisACK(pcb) \ - (((pcb)&ISODEP_PCB_Rx_MASK) == ISODEP_PCB_ACK) /*!< Checks if pcb is indicating ACK */ -#define isoDep_PCBisNAK(pcb) \ - (((pcb)&ISODEP_PCB_Rx_MASK) == ISODEP_PCB_NAK) /*!< Checks if pcb is indicating ACK */ - -#define isoDep_PCBhasDID(pcb) \ - (((pcb)&ISODEP_PCB_DID_BIT) == ISODEP_PCB_DID_BIT) /*!< Checks if pcb is indicating DID */ -#define isoDep_PCBhasNAD(pcb) \ - (((pcb)&ISODEP_PCB_NAD_BIT) == ISODEP_PCB_NAD_BIT) /*!< Checks if pcb is indicating NAD */ - -#define isoDep_PCBisIChaining(pcb) \ - (isoDep_PCBisIBlock(pcb) && \ - isoDep_PCBisChaining(pcb)) /*!< Checks if pcb is I-Block indicating chaining*/ - -#define isoDep_PCBisSDeselect(pcb) \ - (isoDep_PCBisSBlock(pcb) && \ - isoDep_PCBisDeselect(pcb)) /*!< Checks if pcb is S-Block indicating DESELECT*/ -#define isoDep_PCBisSWTX(pcb) \ - (isoDep_PCBisSBlock(pcb) && \ - isoDep_PCBisWTX(pcb)) /*!< Checks if pcb is S-Block indicating WTX */ - -#define isoDep_PCBisRACK(pcb) \ - (isoDep_PCBisRBlock(pcb) && \ - isoDep_PCBisACK(pcb)) /*!< Checks if pcb is R-Block indicating ACK */ -#define isoDep_PCBisRNAK(pcb) \ - (isoDep_PCBisRBlock(pcb) && \ - isoDep_PCBisNAK(pcb)) /*!< Checks if pcb is R-Block indicating NAK */ - -#define isoDep_PCBIBlock(bn) \ - ((uint8_t)(0x00U | ISODEP_PCB_IBLOCK | ISODEP_PCB_B2_BIT | ((bn)&ISODEP_PCB_BN_MASK))) /*!< Returns an I-Block with the given block number (bn) */ -#define isoDep_PCBIBlockChaining(bn) \ - ((uint8_t)(isoDep_PCBIBlock(bn) | ISODEP_PCB_CHAINING_BIT)) /*!< Returns an I-Block with the given block number (bn) indicating chaining */ - -#define isoDep_PCBRBlock(bn) \ - ((uint8_t)(0x00U | ISODEP_PCB_RBLOCK | ISODEP_PCB_B6_BIT | ISODEP_PCB_B2_BIT | ((bn)&ISODEP_PCB_BN_MASK))) /*!< Returns an R-Block with the given block number (bn) */ -#define isoDep_PCBRACK(bn) \ - ((uint8_t)(isoDep_PCBRBlock(bn) | ISODEP_PCB_ACK)) /*!< Returns an R-Block with the given block number (bn) indicating ACK */ -#define isoDep_PCBRNAK(bn) \ - ((uint8_t)(isoDep_PCBRBlock(bn) | ISODEP_PCB_NAK)) /*!< Returns an R-Block with the given block number (bn) indicating NAK */ - -#define isoDep_GetBN(pcb) \ - ((uint8_t)((pcb)&ISODEP_PCB_BN_MASK)) /*!< Returns the block number (bn) from the given pcb */ -#define isoDep_GetWTXM(inf) \ - ((uint8_t)(( \ - inf)&ISODEP_SWTX_WTXM_MASK)) /*!< Returns the WTX value from the given inf byte */ -#define isoDep_isWTXMValid(wtxm) \ - (((wtxm) >= ISODEP_WTXM_MIN) && \ - ((wtxm) <= ISODEP_WTXM_MAX)) /*!< Checks if the given wtxm is valid */ - -#define isoDep_WTXMListenerMax(fwt) \ - (MIN( \ - (uint8_t)(ISODEP_FWT_LIS_MAX / (fwt)), \ - ISODEP_WTXM_MAX)) /*!< Calculates the Max WTXM value for the given fwt as a Listener */ - -#define isoDepCalcdSGFT(s) \ - (384U * ((uint32_t)1U \ - << (s))) /*!< Calculates the dSFGT with given SFGI Digital 1.1 13.8.2.1 & A.6*/ -#define isoDepCalcSGFT(s) \ - (4096U * ((uint32_t)1U \ - << (s))) /*!< Calculates the SFGT with given SFGI Digital 1.1 13.8.2 */ - -#define isoDep_PCBNextBN(bn) \ - (((uint8_t)(bn) ^ 0x01U) & \ - ISODEP_PCB_BN_MASK) /*!< Returns the value of the next block number based on bn */ -#define isoDep_PCBPrevBN(bn) \ - isoDep_PCBNextBN(bn) /*!< Returns the value of the previous block number based on bn */ -#define isoDep_ToggleBN(bn) \ - ((bn) = \ - (((bn) ^ 0x01U) & \ - ISODEP_PCB_BN_MASK)) /*!< Toggles the block number value of the given bn */ - -#define isoDep_WTXAdjust(v) \ - ((v) - ((v) >> 3)) /*!< Adjust WTX timer value to a percentage of the total, current 88% */ - -/*! ISO 14443-4 7.5.6.2 & Digital 1.1 - 15.2.6.2 The CE SHALL NOT attempt error recovery and remains in Rx mode upon Transmission or a Protocol Error */ -#define isoDepReEnableRx(rxB, rxBL, rxL) \ - rfalTransceiveBlockingTx(NULL, 0, rxB, rxBL, rxL, RFAL_TXRX_FLAGS_DEFAULT, RFAL_FWT_NONE) - -/*! Macro used for the blocking methods */ -#define rfalIsoDepRunBlocking(e, fn) \ - do { \ - (e) = (fn); \ - rfalWorker(); \ - } while((e) == ERR_BUSY) - -#define isoDepTimerStart(timer, time_ms) \ - do { \ - platformTimerDestroy(timer); \ - (timer) = platformTimerCreate((uint16_t)(time_ms)); \ - } while(0) /*!< Configures and starts the WTX timer */ -#define isoDepTimerisExpired(timer) \ - platformTimerIsExpired(timer) /*!< Checks WTX timer has expired */ -#define isoDepTimerDestroy(timer) \ - platformTimerDestroy(timer) /*!< Destroys WTX timer */ - -/* - ****************************************************************************** - * LOCAL DATA TYPES - ****************************************************************************** - */ - -/*! Internal structure to be used in handling of S(PARAMETRS) only */ -typedef struct { - uint8_t pcb; /*!< PCB byte */ - rfalIsoDepSParameter sParam; /*!< S(PARAMETERS) */ -} rfalIsoDepControlMsgSParam; - -/*! Enumeration of the possible control message types */ -typedef enum { - ISODEP_R_ACK, /*!< R-ACK Acknowledge */ - ISODEP_R_NAK, /*!< R-NACK Negative acknowledge */ - ISODEP_S_WTX, /*!< S-WTX Waiting Time Extension */ - ISODEP_S_DSL /*!< S-DSL Deselect */ -} rfalIsoDepControlMsg; - -/*! Enumeration of the IsoDep roles */ -typedef enum { - ISODEP_ROLE_PCD, /*!< Perform as Reader/PCD */ - ISODEP_ROLE_PICC /*!< Perform as Card/PICC */ -} rfalIsoDepRole; - -/*! ISO-DEP layer states */ -typedef enum { - ISODEP_ST_IDLE, /*!< Idle State */ - ISODEP_ST_PCD_TX, /*!< PCD Transmission State */ - ISODEP_ST_PCD_RX, /*!< PCD Reception State */ - ISODEP_ST_PCD_WAIT_DSL, /*!< PCD Wait for DSL response */ - - ISODEP_ST_PICC_ACT_ATS, /*!< PICC has replied to RATS (ATS) */ - ISODEP_ST_PICC_ACT_ATTRIB, /*!< PICC has replied to ATTRIB */ - ISODEP_ST_PICC_RX, /*!< PICC Reception State */ - ISODEP_ST_PICC_SWTX, /*!< PICC Waiting Time eXtension */ - ISODEP_ST_PICC_SDSL, /*!< PICC S(DSL) response ongoing */ - ISODEP_ST_PICC_TX, /*!< PICC Transmission State */ - - ISODEP_ST_PCD_ACT_RATS, /*!< PCD activation (RATS) */ - ISODEP_ST_PCD_ACT_PPS, /*!< PCD activation (PPS) */ - -} rfalIsoDepState; - -/*! Holds all ISO-DEP data(counters, buffers, ID, timeouts, frame size) */ -typedef struct { - rfalIsoDepState state; /*!< ISO-DEP module state */ - rfalIsoDepRole role; /*!< Current ISO-DEP role */ - - uint8_t blockNumber; /*!< Current block number */ - uint8_t did; /*!< Current DID */ - uint8_t nad; /*!< Current DID */ - uint8_t cntIRetrys; /*!< I-Block retry counter */ - uint8_t cntRRetrys; /*!< R-Block retry counter */ - uint8_t cntSDslRetrys; /*!< S(DESELECT) retry counter */ - uint8_t cntSWtxRetrys; /*!< Overall S(WTX) retry counter */ - uint8_t cntSWtxNack; /*!< R(NACK) answered with S(WTX) counter */ - uint32_t fwt; /*!< Current FWT (Frame Waiting Time) */ - uint32_t dFwt; /*!< Current delta FWT */ - uint16_t fsx; /*!< Current FSx FSC or FSD (max Frame size) */ - bool isTxChaining; /*!< Flag for chaining on Tx */ - bool isRxChaining; /*!< Flag for chaining on Rx */ - uint8_t* txBuf; /*!< Tx buffer pointer */ - uint8_t* rxBuf; /*!< Rx buffer pointer */ - uint16_t txBufLen; /*!< Tx buffer length */ - uint16_t rxBufLen; /*!< Rx buffer length */ - uint8_t txBufInfPos; /*!< Start of payload in txBuf */ - uint8_t rxBufInfPos; /*!< Start of payload in rxBuf */ - - uint16_t ourFsx; /*!< Our current FSx FSC or FSD (Frame size) */ - uint8_t lastPCB; /*!< Last PCB sent */ - uint8_t lastWTXM; /*!< Last WTXM sent */ - uint8_t atsTA; /*!< TA on ATS */ - uint8_t hdrLen; /*!< Current ISO-DEP length */ - rfalBitRate txBR; /*!< Current Tx Bit Rate */ - rfalBitRate rxBR; /*!< Current Rx Bit Rate */ - uint16_t* rxLen; /*!< Output parameter ptr to Rx length */ - bool* rxChaining; /*!< Output parameter ptr to Rx chaining flag */ - uint32_t WTXTimer; /*!< Timer used for WTX */ - bool lastDID00; /*!< Last PCD block had DID flag (for DID = 0) */ - - bool isTxPending; /*!< Flag pending Block while waiting WTX Ack */ - bool isWait4WTX; /*!< Flag for waiting WTX Ack */ - - uint8_t maxRetriesI; /*!< Number of retries for a I-Block */ - uint8_t maxRetriesR; /*!< Number of retries for a R-Block */ - uint8_t maxRetriesSDSL; /*!< Number of retries for S(DESELECT) errors */ - uint8_t maxRetriesSWTX; /*!< Number of retries for S(WTX) errors */ - uint8_t maxRetriesSnWTX; /*!< Number of retries S(WTX) replied w NACK */ - uint8_t maxRetriesRATS; /*!< Number of retries for RATS */ - - rfalComplianceMode compMode; /*!< Compliance mode */ - - uint8_t ctrlBuf[ISODEP_CONTROLMSG_BUF_LEN]; /*!< Control msg buf */ - uint16_t ctrlRxLen; /*!< Control msg rcvd len */ - - union { /* PRQA S 0750 # MISRA 19.2 - Members of the union will not be used concurrently, only one frame at a time */ -#if RFAL_FEATURE_NFCA - rfalIsoDepRats ratsReq; - rfalIsoDepPpsReq ppsReq; -#endif /* RFAL_FEATURE_NFCA */ - -#if RFAL_FEATURE_NFCB - rfalIsoDepAttribCmd attribReq; -#endif /* RFAL_FEATURE_NFCB */ - } actv; /*!< Activation buffer */ - - uint8_t* rxLen8; /*!< Receive length (8-bit) */ - rfalIsoDepDevice* actvDev; /*!< Activation Device Info */ - rfalIsoDepListenActvParam actvParam; /*!< Listen Activation context */ - - rfalIsoDepApduTxRxParam APDUParam; /*!< APDU TxRx params */ - uint16_t APDUTxPos; /*!< APDU Tx position */ - uint16_t APDURxPos; /*!< APDU Rx position */ - bool isAPDURxChaining; /*!< APDU Transceive chaining flag */ - -} rfalIsoDep; - -/* - ****************************************************************************** - * LOCAL VARIABLES - ****************************************************************************** - */ - -static rfalIsoDep gIsoDep; /*!< ISO-DEP Module instance */ - -/* - ****************************************************************************** - * LOCAL FUNCTION PROTOTYPES - ****************************************************************************** - */ -static void isoDepClearCounters(void); -static ReturnCode - isoDepTx(uint8_t pcb, const uint8_t* txBuf, uint8_t* infBuf, uint16_t infLen, uint32_t fwt); -static ReturnCode isoDepHandleControlMsg(rfalIsoDepControlMsg controlMsg, uint8_t param); -static void rfalIsoDepApdu2IBLockParam( - rfalIsoDepApduTxRxParam apduParam, - rfalIsoDepTxRxParam* iBlockParam, - uint16_t txPos, - uint16_t rxPos); - -#if RFAL_FEATURE_ISO_DEP_POLL -static ReturnCode isoDepDataExchangePCD(uint16_t* outActRxLen, bool* outIsChaining); -static void rfalIsoDepCalcBitRate( - rfalBitRate maxAllowedBR, - uint8_t piccBRCapability, - rfalBitRate* dsi, - rfalBitRate* dri); -static uint32_t rfalIsoDepSFGI2SFGT(uint8_t sfgi); - -#if RFAL_FEATURE_NFCA -static ReturnCode - rfalIsoDepStartRATS(rfalIsoDepFSxI FSDI, uint8_t DID, rfalIsoDepAts* ats, uint8_t* atsLen); -static ReturnCode rfalIsoDepGetRATSStatus(void); -static ReturnCode - rfalIsoDepStartPPS(uint8_t DID, rfalBitRate DSI, rfalBitRate DRI, rfalIsoDepPpsRes* ppsRes); -static ReturnCode rfalIsoDepGetPPSSTatus(void); -#endif /* RFAL_FEATURE_NFCA */ - -#if RFAL_FEATURE_NFCB -static ReturnCode rfalIsoDepStartATTRIB( - const uint8_t* nfcid0, - uint8_t PARAM1, - rfalBitRate DSI, - rfalBitRate DRI, - rfalIsoDepFSxI FSDI, - uint8_t PARAM3, - uint8_t DID, - const uint8_t* HLInfo, - uint8_t HLInfoLen, - uint32_t fwt, - rfalIsoDepAttribRes* attribRes, - uint8_t* attribResLen); -static ReturnCode rfalIsoDepGetATTRIBStatus(void); -#endif /* RFAL_FEATURE_NFCB */ - -#endif /* RFAL_FEATURE_ISO_DEP_POLL */ - -#if RFAL_FEATURE_ISO_DEP_LISTEN -static ReturnCode isoDepDataExchangePICC(void); -static ReturnCode isoDepReSendControlMsg(void); -#endif - -/* - ****************************************************************************** - * LOCAL FUNCTIONS - ****************************************************************************** - */ - -/*******************************************************************************/ -static void isoDepClearCounters(void) { - gIsoDep.cntIRetrys = 0; - gIsoDep.cntRRetrys = 0; - gIsoDep.cntSDslRetrys = 0; - gIsoDep.cntSWtxRetrys = 0; - gIsoDep.cntSWtxNack = 0; -} - -/*******************************************************************************/ -static ReturnCode - isoDepTx(uint8_t pcb, const uint8_t* txBuf, uint8_t* infBuf, uint16_t infLen, uint32_t fwt) { - uint8_t* txBlock; - uint16_t txBufLen; - uint8_t computedPcb; - rfalTransceiveContext ctx; - - txBlock = infBuf; /* Point to beginning of the INF, and go backwards */ - gIsoDep.lastPCB = pcb; /* Store the last PCB sent */ - - if(infLen > 0U) { - if(((uint32_t)infBuf - (uint32_t)txBuf) < - gIsoDep.hdrLen) /* Check that we can fit the header in the given space */ - { - return ERR_NOMEM; - } - } - - /*******************************************************************************/ - /* Compute optional PCB bits */ - computedPcb = pcb; - if((gIsoDep.did != RFAL_ISODEP_NO_DID) || - ((gIsoDep.did == RFAL_ISODEP_DID_00) && gIsoDep.lastDID00)) { - computedPcb |= ISODEP_PCB_DID_BIT; - } - if(gIsoDep.nad != RFAL_ISODEP_NO_NAD) { - computedPcb |= ISODEP_PCB_NAD_BIT; - } - if((gIsoDep.isTxChaining) && (isoDep_PCBisIBlock(computedPcb))) { - computedPcb |= ISODEP_PCB_CHAINING_BIT; - } - - /*******************************************************************************/ - /* Compute Payload on the given txBuf, start by the PCB | DID | NAD | before INF */ - - if(gIsoDep.nad != RFAL_ISODEP_NO_NAD) { - *(--txBlock) = gIsoDep.nad; /* NAD is optional */ - } - - if((gIsoDep.did != RFAL_ISODEP_NO_DID) || - ((gIsoDep.did == RFAL_ISODEP_DID_00) && gIsoDep.lastDID00)) { - *(--txBlock) = gIsoDep.did; /* DID is optional */ - } - - *(--txBlock) = computedPcb; /* PCB always present */ - - txBufLen = - (infLen + - (uint16_t)((uint32_t)infBuf - (uint32_t)txBlock)); /* Calculate overall buffer size */ - - if(txBufLen > (gIsoDep.fsx - - ISODEP_CRC_LEN)) /* Check if msg length violates the maximum frame size FSC */ - { - return ERR_NOTSUPP; - } - - rfalCreateByteFlagsTxRxContext( - ctx, - txBlock, - txBufLen, - gIsoDep.rxBuf, - gIsoDep.rxBufLen, - gIsoDep.rxLen, - RFAL_TXRX_FLAGS_DEFAULT, - ((gIsoDep.role == ISODEP_ROLE_PICC) ? RFAL_FWT_NONE : fwt)); - return rfalStartTransceive(&ctx); -} - -/*******************************************************************************/ -static ReturnCode isoDepHandleControlMsg(rfalIsoDepControlMsg controlMsg, uint8_t param) { - uint8_t pcb; - uint8_t infLen; - uint32_t fwtTemp; - - infLen = 0; - fwtTemp = (gIsoDep.fwt + gIsoDep.dFwt); - ST_MEMSET(gIsoDep.ctrlBuf, 0x00, ISODEP_CONTROLMSG_BUF_LEN); - - switch(controlMsg) { - /*******************************************************************************/ - case ISODEP_R_ACK: - - if(gIsoDep.cntRRetrys++ > gIsoDep.maxRetriesR) { - return ERR_TIMEOUT; /* NFC Forum mandates timeout or transmission error depending on previous errors */ - } - - pcb = isoDep_PCBRACK(gIsoDep.blockNumber); - break; - - /*******************************************************************************/ - case ISODEP_R_NAK: - - if((gIsoDep.cntRRetrys++ > gIsoDep.maxRetriesR) || /* Max R Block retries reached */ - (gIsoDep.cntSWtxNack >= - gIsoDep - .maxRetriesSnWTX)) /* Max number PICC is allowed to respond with S(WTX) to R(NAK) */ - { - return ERR_TIMEOUT; - } - - pcb = isoDep_PCBRNAK(gIsoDep.blockNumber); - break; - - /*******************************************************************************/ - case ISODEP_S_WTX: - - if((gIsoDep.cntSWtxRetrys++ > gIsoDep.maxRetriesSWTX) && - (gIsoDep.maxRetriesSWTX != RFAL_ISODEP_MAX_WTX_RETRYS_ULTD)) { - return ERR_PROTO; - } - - /* Check if WTXM is valid */ - if(!isoDep_isWTXMValid(param)) { - return ERR_PROTO; - } - - if(gIsoDep.role == ISODEP_ROLE_PCD) { - /* Calculate temp Wait Time eXtension */ - fwtTemp = (gIsoDep.fwt * param); - fwtTemp = MIN(RFAL_ISODEP_MAX_FWT, fwtTemp); - fwtTemp += gIsoDep.dFwt; - } - - pcb = ISODEP_PCB_SWTX; - gIsoDep.ctrlBuf[RFAL_ISODEP_PCB_LEN + RFAL_ISODEP_DID_LEN + infLen++] = param; - break; - - /*******************************************************************************/ - case ISODEP_S_DSL: - - if(gIsoDep.cntSDslRetrys++ > gIsoDep.maxRetriesSDSL) { - return ERR_TIMEOUT; /* NFC Forum mandates timeout or transmission error depending on previous errors */ - } - - if(gIsoDep.role == ISODEP_ROLE_PCD) { - /* Digital 1.0 - 13.2.7.3 Poller must wait fwtDEACTIVATION */ - fwtTemp = ISODEP_FWT_DEACTIVATION; - gIsoDep.state = ISODEP_ST_PCD_WAIT_DSL; - } - pcb = ISODEP_PCB_SDSL; - break; - - /*******************************************************************************/ - default: - return ERR_INTERNAL; - } - - return isoDepTx( - pcb, - gIsoDep.ctrlBuf, - &gIsoDep.ctrlBuf[RFAL_ISODEP_PCB_LEN + RFAL_ISODEP_DID_LEN], - infLen, - fwtTemp); -} - -#if RFAL_FEATURE_ISO_DEP_LISTEN -/*******************************************************************************/ -static ReturnCode isoDepReSendControlMsg(void) { - if(isoDep_PCBisRACK(gIsoDep.lastPCB)) { - return isoDepHandleControlMsg(ISODEP_R_ACK, RFAL_ISODEP_NO_PARAM); - } - - if(isoDep_PCBisRNAK(gIsoDep.lastPCB)) { - return isoDepHandleControlMsg(ISODEP_R_NAK, RFAL_ISODEP_NO_PARAM); - } - - if(isoDep_PCBisSDeselect(gIsoDep.lastPCB)) { - return isoDepHandleControlMsg(ISODEP_S_DSL, RFAL_ISODEP_NO_PARAM); - } - - if(isoDep_PCBisSWTX(gIsoDep.lastPCB)) { - return isoDepHandleControlMsg(ISODEP_S_WTX, gIsoDep.lastWTXM); - } - return ERR_WRONG_STATE; -} -#endif /* RFAL_FEATURE_ISO_DEP_LISTEN */ - -/* - ****************************************************************************** - * GLOBAL FUNCTIONS - ****************************************************************************** - */ - -/*******************************************************************************/ -void rfalIsoDepInitialize(void) { - gIsoDep.state = ISODEP_ST_IDLE; - gIsoDep.role = ISODEP_ROLE_PCD; - gIsoDep.did = RFAL_ISODEP_NO_DID; - gIsoDep.nad = RFAL_ISODEP_NO_NAD; - gIsoDep.blockNumber = 0; - gIsoDep.isTxChaining = false; - gIsoDep.isRxChaining = false; - gIsoDep.lastDID00 = false; - gIsoDep.lastPCB = ISODEP_PCB_INVALID; - gIsoDep.fsx = (uint16_t)RFAL_ISODEP_FSX_16; - gIsoDep.ourFsx = (uint16_t)RFAL_ISODEP_FSX_16; - gIsoDep.hdrLen = RFAL_ISODEP_PCB_LEN; - - gIsoDep.rxLen = NULL; - gIsoDep.rxBuf = NULL; - gIsoDep.rxBufInfPos = 0U; - gIsoDep.txBufInfPos = 0U; - - gIsoDep.isTxPending = false; - gIsoDep.isWait4WTX = false; - - gIsoDep.compMode = RFAL_COMPLIANCE_MODE_NFC; - gIsoDep.maxRetriesR = RFAL_ISODEP_MAX_R_RETRYS; - gIsoDep.maxRetriesI = RFAL_ISODEP_MAX_I_RETRYS; - gIsoDep.maxRetriesSDSL = RFAL_ISODEP_MAX_DSL_RETRYS; - gIsoDep.maxRetriesSWTX = RFAL_ISODEP_MAX_WTX_RETRYS; - gIsoDep.maxRetriesSnWTX = RFAL_ISODEP_MAX_WTX_NACK_RETRYS; - gIsoDep.maxRetriesRATS = RFAL_ISODEP_RATS_RETRIES; - - gIsoDep.APDURxPos = 0; - gIsoDep.APDUTxPos = 0; - gIsoDep.APDUParam.rxLen = NULL; - gIsoDep.APDUParam.rxBuf = NULL; - gIsoDep.APDUParam.txBuf = NULL; - - isoDepClearCounters(); - - /* Destroy any ongoing WTX timer */ - isoDepTimerDestroy(gIsoDep.WTXTimer); - gIsoDep.WTXTimer = 0U; -} - -/*******************************************************************************/ -void rfalIsoDepInitializeWithParams( - rfalComplianceMode compMode, - uint8_t maxRetriesR, - uint8_t maxRetriesSnWTX, - uint8_t maxRetriesSWTX, - uint8_t maxRetriesSDSL, - uint8_t maxRetriesI, - uint8_t maxRetriesRATS) { - rfalIsoDepInitialize(); - - gIsoDep.compMode = compMode; - gIsoDep.maxRetriesR = maxRetriesR; - gIsoDep.maxRetriesSnWTX = maxRetriesSnWTX; - gIsoDep.maxRetriesSWTX = maxRetriesSWTX; - gIsoDep.maxRetriesSDSL = maxRetriesSDSL; - gIsoDep.maxRetriesI = maxRetriesI; - gIsoDep.maxRetriesRATS = maxRetriesRATS; -} - -#if RFAL_FEATURE_ISO_DEP_POLL -/*******************************************************************************/ -static ReturnCode isoDepDataExchangePCD(uint16_t* outActRxLen, bool* outIsChaining) { - ReturnCode ret; - uint8_t rxPCB; - - /* Check out parameters */ - if((outActRxLen == NULL) || (outIsChaining == NULL)) { - return ERR_PARAM; - } - - *outIsChaining = false; - - /* Calculate header required and check if the buffers InfPositions are suitable */ - gIsoDep.hdrLen = RFAL_ISODEP_PCB_LEN; - if(gIsoDep.did != RFAL_ISODEP_NO_DID) { - gIsoDep.hdrLen += RFAL_ISODEP_DID_LEN; - } - if(gIsoDep.nad != RFAL_ISODEP_NO_NAD) { - gIsoDep.hdrLen += RFAL_ISODEP_NAD_LEN; - } - - /* Check if there is enough space before the infPos to append ISO-DEP headers on rx and tx */ - if((gIsoDep.rxBufInfPos < gIsoDep.hdrLen) || (gIsoDep.txBufInfPos < gIsoDep.hdrLen)) { - return ERR_PARAM; - } - - /*******************************************************************************/ - switch(gIsoDep.state) { - /*******************************************************************************/ - case ISODEP_ST_IDLE: - return ERR_NONE; - - /*******************************************************************************/ - case ISODEP_ST_PCD_TX: - ret = isoDepTx( - isoDep_PCBIBlock(gIsoDep.blockNumber), - gIsoDep.txBuf, - &gIsoDep.txBuf[gIsoDep.txBufInfPos], - gIsoDep.txBufLen, - (gIsoDep.fwt + gIsoDep.dFwt)); - switch(ret) { - case ERR_NONE: - gIsoDep.state = ISODEP_ST_PCD_RX; - break; - - default: - return ret; - } - /* fall through */ - - /*******************************************************************************/ - case ISODEP_ST_PCD_WAIT_DSL: /* PRQA S 2003 # MISRA 16.3 - Intentional fall through */ - case ISODEP_ST_PCD_RX: - - ret = rfalGetTransceiveStatus(); - switch(ret) { - /* Data rcvd with error or timeout -> Send R-NAK */ - case ERR_TIMEOUT: - case ERR_CRC: - case ERR_PAR: - case ERR_FRAMING: /* added to handle test cases scenario TC_POL_NFCB_T4AT_BI_82_x_y & TC_POL_NFCB_T4BT_BI_82_x_y */ - case ERR_INCOMPLETE_BYTE: /* added to handle test cases scenario TC_POL_NFCB_T4AT_BI_82_x_y & TC_POL_NFCB_T4BT_BI_82_x_y */ - - if(gIsoDep - .isRxChaining) { /* Rule 5 - In PICC chaining when a invalid/timeout occurs -> R-ACK */ - EXIT_ON_ERR(ret, isoDepHandleControlMsg(ISODEP_R_ACK, RFAL_ISODEP_NO_PARAM)); - } else if( - gIsoDep.state == - ISODEP_ST_PCD_WAIT_DSL) { /* Rule 8 - If s-Deselect response fails MAY retransmit */ - EXIT_ON_ERR(ret, isoDepHandleControlMsg(ISODEP_S_DSL, RFAL_ISODEP_NO_PARAM)); - } else { /* Rule 4 - When a invalid block or timeout occurs -> R-NACK */ - EXIT_ON_ERR(ret, isoDepHandleControlMsg(ISODEP_R_NAK, RFAL_ISODEP_NO_PARAM)); - } - return ERR_BUSY; - - case ERR_NONE: - break; - - case ERR_BUSY: - return ERR_BUSY; /* Debug purposes */ - - default: - return ret; - } - - /*******************************************************************************/ - /* No error, process incoming msg */ - /*******************************************************************************/ - - (*outActRxLen) = rfalConvBitsToBytes(*outActRxLen); - - /* Check rcvd msg length, cannot be less then the expected header */ - if(((*outActRxLen) < gIsoDep.hdrLen) || ((*outActRxLen) >= gIsoDep.ourFsx)) { - return ERR_PROTO; - } - - /* Grab rcvd PCB */ - rxPCB = gIsoDep.rxBuf[ISODEP_PCB_POS]; - - /* EMVCo doesn't allow usage of for CID or NAD EMVCo 2.6 TAble 10.2 */ - if((gIsoDep.compMode == RFAL_COMPLIANCE_MODE_EMV) && - (isoDep_PCBhasDID(rxPCB) || isoDep_PCBhasNAD(rxPCB))) { - return ERR_PROTO; - } - - /* If we are expecting DID, check if PCB signals its presence and if device ID match*/ - if((gIsoDep.did != RFAL_ISODEP_NO_DID) && - (!isoDep_PCBhasDID(rxPCB) || (gIsoDep.did != gIsoDep.rxBuf[ISODEP_DID_POS]))) { - return ERR_PROTO; - } - - /*******************************************************************************/ - /* Process S-Block */ - /*******************************************************************************/ - if(isoDep_PCBisSBlock(rxPCB)) { - /* Check if is a Wait Time eXtension */ - if(isoDep_PCBisSWTX(rxPCB)) { - /* Check if PICC has requested S(WTX) as response to R(NAK) EMVCo 3.0 10.3.5.5 / Digital 2.0 16.2.6.5 */ - if(isoDep_PCBisRNAK(gIsoDep.lastPCB)) { - gIsoDep.cntSWtxNack++; /* Count S(WTX) upon R(NAK) */ - gIsoDep.cntRRetrys = 0; /* Reset R-Block counter has PICC has responded */ - } else { - gIsoDep.cntSWtxNack = 0; /* Reset R(NACK)->S(WTX) counter */ - } - - /* Rule 3 - respond to S-block: get 1st INF byte S(STW): Power + WTXM */ - EXIT_ON_ERR( - ret, - isoDepHandleControlMsg( - ISODEP_S_WTX, isoDep_GetWTXM(gIsoDep.rxBuf[gIsoDep.hdrLen]))); - return ERR_BUSY; - } - - /* Check if is a deselect response */ - if(isoDep_PCBisSDeselect(rxPCB)) { - if(gIsoDep.state == ISODEP_ST_PCD_WAIT_DSL) { - rfalIsoDepInitialize(); /* Session finished reInit vars */ - return ERR_NONE; - } - - /* Deselect response not expected */ - /* fall through to PROTO error */ - } - /* Unexpected S-Block */ - return ERR_PROTO; - } - - /*******************************************************************************/ - /* Process R-Block */ - /*******************************************************************************/ - else if(isoDep_PCBisRBlock(rxPCB)) { - if(isoDep_PCBisRACK(rxPCB)) /* Check if is a R-ACK */ - { - if(isoDep_GetBN(rxPCB) == gIsoDep.blockNumber) /* Expected block number */ - { - /* Rule B - ACK with expected bn -> Increment block number */ - gIsoDep.blockNumber = isoDep_PCBNextBN(gIsoDep.blockNumber); - - /* R-ACK only allowed when PCD chaining */ - if(!gIsoDep.isTxChaining) { - return ERR_PROTO; - } - - /* Rule 7 - Chaining transaction done, continue chaining */ - isoDepClearCounters(); - return ERR_NONE; /* This block has been transmitted */ - } else { - /* Rule 6 - R-ACK with wrong block number retransmit */ - /* Digital 2.0 16.2.5.4 - Retransmit maximum two times */ - /* EMVCo 3.0 10.3.4.3 - PCD may re-transmit the last I-Block or report error */ - if(gIsoDep.cntIRetrys++ < gIsoDep.maxRetriesI) { - gIsoDep.cntRRetrys = 0; /* Clear R counter only */ - gIsoDep.state = ISODEP_ST_PCD_TX; - return ERR_BUSY; - } - return ERR_TIMEOUT; /* NFC Forum mandates timeout or transmission error depending on previous errors */ - } - } else /* Unexcpected R-Block */ - { - return ERR_PROTO; - } - } - - /*******************************************************************************/ - /* Process I-Block */ - /*******************************************************************************/ - else if(isoDep_PCBisIBlock(rxPCB)) { - /*******************************************************************************/ - /* is PICC performing chaining */ - if(isoDep_PCBisChaining(rxPCB)) { - gIsoDep.isRxChaining = true; - *outIsChaining = true; - - if(isoDep_GetBN(rxPCB) == gIsoDep.blockNumber) { - /* Rule B - ACK with correct block number -> Increase Block number */ - isoDep_ToggleBN(gIsoDep.blockNumber); - - isoDepClearCounters(); /* Clear counters in case R counter is already at max */ - - /* Rule 2 - Send ACK */ - EXIT_ON_ERR(ret, isoDepHandleControlMsg(ISODEP_R_ACK, RFAL_ISODEP_NO_PARAM)); - - /* Received I-Block with chaining, send current data to DH */ - - /* remove ISO DEP header, check is necessary to move the INF data on the buffer */ - *outActRxLen -= gIsoDep.hdrLen; - if((gIsoDep.hdrLen != gIsoDep.rxBufInfPos) && (*outActRxLen > 0U)) { - ST_MEMMOVE( - &gIsoDep.rxBuf[gIsoDep.rxBufInfPos], - &gIsoDep.rxBuf[gIsoDep.hdrLen], - *outActRxLen); - } - - isoDepClearCounters(); - return ERR_AGAIN; /* Send Again signalling to run again, but some chaining data has arrived */ - } else { - /* Rule 5 - PICC chaining invalid I-Block -> R-ACK */ - EXIT_ON_ERR(ret, isoDepHandleControlMsg(ISODEP_R_ACK, RFAL_ISODEP_NO_PARAM)); - } - return ERR_BUSY; - } - - gIsoDep.isRxChaining = false; /* clear PICC chaining flag */ - - if(isoDep_GetBN(rxPCB) == gIsoDep.blockNumber) { - /* Rule B - I-Block with correct block number -> Increase Block number */ - isoDep_ToggleBN(gIsoDep.blockNumber); - - /* I-Block transaction done successfully */ - - /* remove ISO DEP header, check is necessary to move the INF data on the buffer */ - *outActRxLen -= gIsoDep.hdrLen; - if((gIsoDep.hdrLen != gIsoDep.rxBufInfPos) && (*outActRxLen > 0U)) { - ST_MEMMOVE( - &gIsoDep.rxBuf[gIsoDep.rxBufInfPos], - &gIsoDep.rxBuf[gIsoDep.hdrLen], - *outActRxLen); - } - - gIsoDep.state = ISODEP_ST_IDLE; - isoDepClearCounters(); - return ERR_NONE; - } else { - if((gIsoDep.compMode != RFAL_COMPLIANCE_MODE_ISO)) { - /* Invalid Block (not chaining) -> Raise error Digital 1.1 15.2.6.4 EMVCo 2.6 10.3.5.4 */ - return ERR_PROTO; - } - - /* Rule 4 - Invalid Block -> R-NAK */ - EXIT_ON_ERR(ret, isoDepHandleControlMsg(ISODEP_R_NAK, RFAL_ISODEP_NO_PARAM)); - return ERR_BUSY; - } - } else /* not S/R/I - Block */ - { - return ERR_PROTO; - } - /* fall through */ - - /*******************************************************************************/ - default: /* PRQA S 2003 # MISRA 16.3 - Intentional fall through */ - /* MISRA 16.4: no empty default (comment will suffice) */ - break; - } - - return ERR_INTERNAL; -} - -/*******************************************************************************/ -ReturnCode rfalIsoDepDeselect(void) { - ReturnCode ret; - uint32_t cntRerun; - bool dummyB; - - /*******************************************************************************/ - /* Using local static vars and static config to cope with a Deselect after * - * RATS\ATTRIB without any I-Block exchanged */ - gIsoDep.rxLen = &gIsoDep.ctrlRxLen; - gIsoDep.rxBuf = gIsoDep.ctrlBuf; - gIsoDep.rxBufLen = ISODEP_CONTROLMSG_BUF_LEN - (RFAL_ISODEP_PCB_LEN + RFAL_ISODEP_DID_LEN); - gIsoDep.rxBufInfPos = (RFAL_ISODEP_PCB_LEN + RFAL_ISODEP_DID_LEN); - gIsoDep.txBufInfPos = (RFAL_ISODEP_PCB_LEN + RFAL_ISODEP_DID_LEN); - - /*******************************************************************************/ - /* The Deselect process is being done blocking, Digital 1.0 - 13.2.7.1 MUST wait response and retry*/ - /* Set the maximum reruns while we will wait for a response */ - cntRerun = ISODEP_MAX_RERUNS; - - /* Send DSL request and run protocol until get a response, error or "timeout" */ - EXIT_ON_ERR(ret, isoDepHandleControlMsg(ISODEP_S_DSL, RFAL_ISODEP_NO_PARAM)); - do { - ret = isoDepDataExchangePCD(gIsoDep.rxLen, &dummyB); - rfalWorker(); - } while(((cntRerun--) != 0U) && (ret == ERR_BUSY)); - - rfalIsoDepInitialize(); - return ((cntRerun == 0U) ? ERR_TIMEOUT : ret); -} - -#endif /* RFAL_FEATURE_ISO_DEP_POLL */ - -/*******************************************************************************/ -uint32_t rfalIsoDepFWI2FWT(uint8_t fwi) { - uint32_t result; - uint8_t tmpFWI; - - tmpFWI = fwi; - - /* RFU values -> take the default value - * Digital 1.0 11.6.2.17 FWI[1,14] - * Digital 1.1 7.6.2.22 FWI[0,14] - * EMVCo 2.6 Table A.5 FWI[0,14] */ - if(tmpFWI > ISODEP_FWI_MAX) { - tmpFWI = RFAL_ISODEP_FWI_DEFAULT; - } - - /* FWT = (256 x 16/fC) x 2^FWI => 2^(FWI+12) Digital 1.1 13.8.1 & 7.9.1 */ - - result = ((uint32_t)1U << (tmpFWI + 12U)); - result = MIN(RFAL_ISODEP_MAX_FWT, result); /* Maximum Frame Waiting Time must be fulfilled */ - - return result; -} - -/*******************************************************************************/ -uint16_t rfalIsoDepFSxI2FSx(uint8_t FSxI) { - uint16_t fsx; - uint8_t fsi; - - /* Enforce maximum FSxI/FSx allowed - NFC Forum and EMVCo differ */ - fsi = - ((gIsoDep.compMode == RFAL_COMPLIANCE_MODE_EMV) ? MIN(FSxI, RFAL_ISODEP_FSDI_MAX_EMV) : - MIN(FSxI, RFAL_ISODEP_FSDI_MAX_NFC)); - - switch(fsi) { - case(uint8_t)RFAL_ISODEP_FSXI_16: - fsx = (uint16_t)RFAL_ISODEP_FSX_16; - break; - case(uint8_t)RFAL_ISODEP_FSXI_24: - fsx = (uint16_t)RFAL_ISODEP_FSX_24; - break; - case(uint8_t)RFAL_ISODEP_FSXI_32: - fsx = (uint16_t)RFAL_ISODEP_FSX_32; - break; - case(uint8_t)RFAL_ISODEP_FSXI_40: - fsx = (uint16_t)RFAL_ISODEP_FSX_40; - break; - case(uint8_t)RFAL_ISODEP_FSXI_48: - fsx = (uint16_t)RFAL_ISODEP_FSX_48; - break; - case(uint8_t)RFAL_ISODEP_FSXI_64: - fsx = (uint16_t)RFAL_ISODEP_FSX_64; - break; - case(uint8_t)RFAL_ISODEP_FSXI_96: - fsx = (uint16_t)RFAL_ISODEP_FSX_96; - break; - case(uint8_t)RFAL_ISODEP_FSXI_128: - fsx = (uint16_t)RFAL_ISODEP_FSX_128; - break; - case(uint8_t)RFAL_ISODEP_FSXI_256: - fsx = (uint16_t)RFAL_ISODEP_FSX_256; - break; - case(uint8_t)RFAL_ISODEP_FSXI_512: - fsx = (uint16_t)RFAL_ISODEP_FSX_512; - break; - case(uint8_t)RFAL_ISODEP_FSXI_1024: - fsx = (uint16_t)RFAL_ISODEP_FSX_1024; - break; - case(uint8_t)RFAL_ISODEP_FSXI_2048: - fsx = (uint16_t)RFAL_ISODEP_FSX_2048; - break; - case(uint8_t)RFAL_ISODEP_FSXI_4096: - fsx = (uint16_t)RFAL_ISODEP_FSX_4096; - break; - default: - fsx = (uint16_t)RFAL_ISODEP_FSX_256; - break; - } - return fsx; -} - -#if RFAL_FEATURE_ISO_DEP_LISTEN - -/*******************************************************************************/ -bool rfalIsoDepIsRats(const uint8_t* buf, uint8_t bufLen) { - if(buf != NULL) { - if((RFAL_ISODEP_CMD_RATS == (uint8_t)*buf) && (sizeof(rfalIsoDepRats) == bufLen)) { - return true; - } - } - return false; -} - -/*******************************************************************************/ -bool rfalIsoDepIsAttrib(const uint8_t* buf, uint8_t bufLen) { - if(buf != NULL) { - if((RFAL_ISODEP_CMD_ATTRIB == (uint8_t)*buf) && - (RFAL_ISODEP_ATTRIB_REQ_MIN_LEN <= bufLen) && - ((RFAL_ISODEP_ATTRIB_REQ_MIN_LEN + RFAL_ISODEP_ATTRIB_HLINFO_LEN) >= bufLen)) { - return true; - } - } - return false; -} - -/*******************************************************************************/ -ReturnCode rfalIsoDepListenStartActivation( - rfalIsoDepAtsParam* atsParam, - const rfalIsoDepAttribResParam* attribResParam, - const uint8_t* buf, - uint16_t bufLen, - rfalIsoDepListenActvParam actParam) { - uint8_t* txBuf; - uint8_t bufIt; - const uint8_t* buffer = buf; - - /*******************************************************************************/ - bufIt = 0; - txBuf = - (uint8_t*)actParam - .rxBuf; /* Use the rxBuf as TxBuf as well, the struct enforces a size enough MAX( NFCA_ATS_MAX_LEN, NFCB_ATTRIB_RES_MAX_LEN ) */ - gIsoDep.txBR = RFAL_BR_106; - gIsoDep.rxBR = RFAL_BR_106; - - /* Check for a valid buffer pointer */ - if(buffer == NULL) { - return ERR_PARAM; - } - - /*******************************************************************************/ - if(*buffer == RFAL_ISODEP_CMD_RATS) { - /* Check ATS parameters */ - if(atsParam == NULL) { - return ERR_PARAM; - } - - /* If requested copy RATS to device info */ - if(actParam.isoDepDev != NULL) { - ST_MEMCPY( - (uint8_t*)&actParam.isoDepDev->activation.A.Poller.RATS, - buffer, - sizeof(rfalIsoDepRats)); /* Copy RATS' CMD + PARAM */ - } - - /*******************************************************************************/ - /* Process RATS */ - buffer++; - gIsoDep.fsx = rfalIsoDepFSxI2FSx( - (((*buffer) & RFAL_ISODEP_RATS_PARAM_FSDI_MASK) >> RFAL_ISODEP_RATS_PARAM_FSDI_SHIFT)); - gIsoDep.did = (*buffer & RFAL_ISODEP_DID_MASK); - - /*******************************************************************************/ - /* Digital 1.1 13.6.1.8 - DID as to between 0 and 14 */ - if(gIsoDep.did > RFAL_ISODEP_DID_MAX) { - return ERR_PROTO; - } - - /* Check if we are configured to support DID */ - if((gIsoDep.did != RFAL_ISODEP_DID_00) && (!atsParam->didSupport)) { - return ERR_NOTSUPP; - } - - /*******************************************************************************/ - /* Check RFAL supported bit rates */ - if((!(RFAL_SUPPORT_BR_CE_A_212) && - (((atsParam->ta & RFAL_ISODEP_ATS_TA_DPL_212) != 0U) || - ((atsParam->ta & RFAL_ISODEP_ATS_TA_DLP_212) != 0U))) || - (!(RFAL_SUPPORT_BR_CE_A_424) && - (((atsParam->ta & RFAL_ISODEP_ATS_TA_DPL_424) != 0U) || - ((atsParam->ta & RFAL_ISODEP_ATS_TA_DLP_424) != 0U))) || - (!(RFAL_SUPPORT_BR_CE_A_848) && - (((atsParam->ta & RFAL_ISODEP_ATS_TA_DPL_848) != 0U) || - ((atsParam->ta & RFAL_ISODEP_ATS_TA_DLP_848) != 0U)))) { - return ERR_NOTSUPP; - } - - /* Enforce proper FWI configuration */ - if(atsParam->fwi > ISODEP_FWI_LIS_MAX) { - atsParam->fwi = ISODEP_FWI_LIS_MAX; - } - - gIsoDep.atsTA = atsParam->ta; - gIsoDep.fwt = rfalIsoDepFWI2FWT(atsParam->fwi); - gIsoDep.ourFsx = rfalIsoDepFSxI2FSx(atsParam->fsci); - - /* Ensure proper/maximum Historical Bytes length */ - atsParam->hbLen = MIN(RFAL_ISODEP_ATS_HB_MAX_LEN, atsParam->hbLen); - - /*******************************************************************************/ - /* Compute ATS */ - - txBuf[bufIt++] = (RFAL_ISODEP_ATS_HIST_OFFSET + atsParam->hbLen); /* TL */ - txBuf[bufIt++] = - ((RFAL_ISODEP_ATS_T0_TA_PRESENCE_MASK | RFAL_ISODEP_ATS_T0_TB_PRESENCE_MASK | - RFAL_ISODEP_ATS_T0_TC_PRESENCE_MASK) | - atsParam->fsci); /* T0 */ - txBuf[bufIt++] = atsParam->ta; /* TA */ - txBuf[bufIt++] = - ((atsParam->fwi << RFAL_ISODEP_RATS_PARAM_FSDI_SHIFT) | - (atsParam->sfgi & RFAL_ISODEP_RATS_PARAM_FSDI_MASK)); /* TB */ - txBuf[bufIt++] = (uint8_t)((atsParam->didSupport) ? RFAL_ISODEP_ATS_TC_DID : 0U); /* TC */ - - if(atsParam->hbLen > 0U) /* MISRA 21.18 */ - { - ST_MEMCPY(&txBuf[bufIt], atsParam->hb, atsParam->hbLen); /* T1-Tk */ - bufIt += atsParam->hbLen; - } - - gIsoDep.state = ISODEP_ST_PICC_ACT_ATS; - - } - /*******************************************************************************/ - else if(*buffer == RFAL_ISODEP_CMD_ATTRIB) { - /* Check ATTRIB parameters */ - if(attribResParam == NULL) { - return ERR_PARAM; - } - - /* REMARK: ATTRIB handling */ - NO_WARNING(attribResParam); - NO_WARNING(bufLen); - return ERR_NOT_IMPLEMENTED; - } else { - return ERR_PARAM; - } - - gIsoDep.actvParam = actParam; - - /*******************************************************************************/ - /* If requested copy to ISO-DEP device info */ - if(actParam.isoDepDev != NULL) { - actParam.isoDepDev->info.DID = gIsoDep.did; - actParam.isoDepDev->info.FSx = gIsoDep.fsx; - actParam.isoDepDev->info.FWT = gIsoDep.fwt; - actParam.isoDepDev->info.dFWT = 0; - actParam.isoDepDev->info.DSI = gIsoDep.txBR; - actParam.isoDepDev->info.DRI = gIsoDep.rxBR; - } - - return rfalTransceiveBlockingTx( - txBuf, - bufIt, - (uint8_t*)actParam.rxBuf, - sizeof(rfalIsoDepBufFormat), - actParam.rxLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_FWT_NONE); -} - -/*******************************************************************************/ -ReturnCode rfalIsoDepListenGetActivationStatus(void) { - ReturnCode err; - uint8_t* txBuf; - uint8_t bufIt; - - rfalBitRate dsi; - rfalBitRate dri; - - /* Check if Activation is running */ - if(gIsoDep.state < ISODEP_ST_PICC_ACT_ATS) { - return ERR_WRONG_STATE; - } - - /* Check if Activation has finished already */ - if(gIsoDep.state >= ISODEP_ST_PICC_RX) { - return ERR_NONE; - } - - /*******************************************************************************/ - /* Check for incoming msg */ - err = rfalGetTransceiveStatus(); - switch(err) { - /*******************************************************************************/ - case ERR_NONE: - break; - - /*******************************************************************************/ - case ERR_LINK_LOSS: - case ERR_BUSY: - return err; - - /*******************************************************************************/ - case ERR_CRC: - case ERR_PAR: - case ERR_FRAMING: - - /* ISO14443 4 5.6.2.2 2 If ATS has been replied upon a invalid block, PICC disables the PPS responses */ - if(gIsoDep.state == ISODEP_ST_PICC_ACT_ATS) { - gIsoDep.state = ISODEP_ST_PICC_RX; - break; - } - /* fall through */ - - /*******************************************************************************/ - default: /* PRQA S 2003 # MISRA 16.3 - Intentional fall through */ - /* ReEnable the receiver and wait for another frame */ - isoDepReEnableRx( - (uint8_t*)gIsoDep.actvParam.rxBuf, - sizeof(rfalIsoDepBufFormat), - gIsoDep.actvParam.rxLen); - - return ERR_BUSY; - } - - txBuf = - (uint8_t*)gIsoDep.actvParam - .rxBuf; /* Use the rxBuf as TxBuf as well, the struct enforces a size enough MAX(NFCA_PPS_RES_LEN, ISODEP_DSL_MAX_LEN) */ - dri = RFAL_BR_KEEP; /* The RFAL_BR_KEEP is used to check if PPS with BR change was requested */ - dsi = RFAL_BR_KEEP; /* MISRA 9.1 */ - bufIt = 0; - - /*******************************************************************************/ - gIsoDep.role = ISODEP_ROLE_PICC; - - /*******************************************************************************/ - if(gIsoDep.state == ISODEP_ST_PICC_ACT_ATS) { - /* Check for a PPS ISO 14443-4 5.3 */ - if((((uint8_t*)gIsoDep.actvParam.rxBuf)[RFAL_ISODEP_PPS_STARTBYTE_POS] & - RFAL_ISODEP_PPS_MASK) == RFAL_ISODEP_PPS_SB) { - /* ISO 14443-4 5.3.1 Check if the we are the addressed DID/CID */ - /* ISO 14443-4 5.3.2 Check for a valid PPS0 */ - if(((((uint8_t*)gIsoDep.actvParam.rxBuf)[RFAL_ISODEP_PPS_STARTBYTE_POS] & - RFAL_ISODEP_DID_MASK) != gIsoDep.did) || - ((((uint8_t*)gIsoDep.actvParam.rxBuf)[RFAL_ISODEP_PPS_PPS0_POS] & - RFAL_ISODEP_PPS0_VALID_MASK) != RFAL_ISODEP_PPS0_PPS1_NOT_PRESENT)) { - /* Invalid DID on PPS request or Invalid PPS0, reEnable the receiver and wait another frame */ - isoDepReEnableRx( - (uint8_t*)gIsoDep.actvParam.rxBuf, - sizeof(rfalIsoDepBufFormat), - gIsoDep.actvParam.rxLen); - - return ERR_BUSY; - } - - /*******************************************************************************/ - /* Check PPS1 presence */ - if(((uint8_t*)gIsoDep.actvParam.rxBuf)[RFAL_ISODEP_PPS_PPS0_POS] == - RFAL_ISODEP_PPS0_PPS1_PRESENT) { - uint8_t newdri = ((uint8_t*)gIsoDep.actvParam.rxBuf)[RFAL_ISODEP_PPS_PPS1_POS] & - RFAL_ISODEP_PPS1_DxI_MASK; /* MISRA 10.8 */ - uint8_t newdsi = (((uint8_t*)gIsoDep.actvParam.rxBuf)[RFAL_ISODEP_PPS_PPS1_POS] >> - RFAL_ISODEP_PPS1_DSI_SHIFT) & - RFAL_ISODEP_PPS1_DxI_MASK; /* MISRA 10.8 */ - /* PRQA S 4342 2 # MISRA 10.5 - Layout of enum rfalBitRate and above masks guarantee no invalid enum values to be created */ - dri = (rfalBitRate)(newdri); - dsi = (rfalBitRate)(newdsi); - - if((!(RFAL_SUPPORT_BR_CE_A_106) && - ((dsi == RFAL_BR_106) || (dri == RFAL_BR_106))) || - (!(RFAL_SUPPORT_BR_CE_A_212) && - ((dsi == RFAL_BR_212) || (dri == RFAL_BR_212))) || - (!(RFAL_SUPPORT_BR_CE_A_424) && - ((dsi == RFAL_BR_424) || (dri == RFAL_BR_424))) || - (!(RFAL_SUPPORT_BR_CE_A_848) && - ((dsi == RFAL_BR_848) || (dri == RFAL_BR_848)))) { - return ERR_PROTO; - } - } - - /*******************************************************************************/ - /* Compute and send PPS RES / Ack */ - txBuf[bufIt++] = ((uint8_t*)gIsoDep.actvParam.rxBuf)[RFAL_ISODEP_PPS_STARTBYTE_POS]; - - rfalTransceiveBlockingTx( - txBuf, - bufIt, - (uint8_t*)gIsoDep.actvParam.rxBuf, - sizeof(rfalIsoDepBufFormat), - gIsoDep.actvParam.rxLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_FWT_NONE); - - /*******************************************************************************/ - /* Exchange the bit rates if requested */ - if(dri != RFAL_BR_KEEP) { - rfalSetBitRate( - dsi, - dri); /* PRQA S 2880 # MISRA 2.1 - Unreachable code due to configuration option being set/unset above (RFAL_SUPPORT_BR_CE_A_xxx) */ - - gIsoDep.txBR = dsi; /* DSI codes the divisor from PICC to PCD */ - gIsoDep.rxBR = dri; /* DRI codes the divisor from PCD to PICC */ - - if(gIsoDep.actvParam.isoDepDev != NULL) { - gIsoDep.actvParam.isoDepDev->info.DSI = dsi; - gIsoDep.actvParam.isoDepDev->info.DRI = dri; - } - } - } - /* Check for a S-Deselect is done on Data Exchange Activity */ - } - - /*******************************************************************************/ - gIsoDep.hdrLen = RFAL_ISODEP_PCB_LEN; - gIsoDep.hdrLen += - RFAL_ISODEP_DID_LEN; /* Always assume DID to be aligned with Digital 1.1 15.1.2 and ISO14443 4 5.6.3 #454 */ - gIsoDep.hdrLen += (uint8_t)((gIsoDep.nad != RFAL_ISODEP_NO_NAD) ? RFAL_ISODEP_NAD_LEN : 0U); - - /*******************************************************************************/ - /* Rule C - The PICC block number shall be initialized to 1 at activation */ - gIsoDep.blockNumber = 1; - - /* Activation done, keep the rcvd data in, reMap the activation buffer to the global to be retrieved by the DEP method */ - gIsoDep.rxBuf = (uint8_t*)gIsoDep.actvParam.rxBuf; - gIsoDep.rxBufLen = sizeof(rfalIsoDepBufFormat); - gIsoDep.rxBufInfPos = - (uint8_t)((uint32_t)gIsoDep.actvParam.rxBuf->inf - (uint32_t)gIsoDep.actvParam.rxBuf->prologue); - gIsoDep.rxLen = gIsoDep.actvParam.rxLen; - gIsoDep.rxChaining = gIsoDep.actvParam.isRxChaining; - - gIsoDep.state = ISODEP_ST_PICC_RX; - return ERR_NONE; -} - -#endif /* RFAL_FEATURE_ISO_DEP_LISTEN */ - -/*******************************************************************************/ -uint16_t rfalIsoDepGetMaxInfLen(void) { - /* Check whether all parameters are valid, otherwise return minimum default value */ - if((gIsoDep.fsx < (uint16_t)RFAL_ISODEP_FSX_16) || - (gIsoDep.fsx > (uint16_t)RFAL_ISODEP_FSX_4096) || (gIsoDep.hdrLen > ISODEP_HDR_MAX_LEN)) { - uint16_t isodepFsx16 = (uint16_t)RFAL_ISODEP_FSX_16; /* MISRA 10.1 */ - return (isodepFsx16 - RFAL_ISODEP_PCB_LEN - ISODEP_CRC_LEN); - } - - return (gIsoDep.fsx - gIsoDep.hdrLen - ISODEP_CRC_LEN); -} - -/*******************************************************************************/ -ReturnCode rfalIsoDepStartTransceive(rfalIsoDepTxRxParam param) { - gIsoDep.txBuf = param.txBuf->prologue; - gIsoDep.txBufInfPos = (uint8_t)((uint32_t)param.txBuf->inf - (uint32_t)param.txBuf->prologue); - gIsoDep.txBufLen = param.txBufLen; - gIsoDep.isTxChaining = param.isTxChaining; - - gIsoDep.rxBuf = param.rxBuf->prologue; - gIsoDep.rxBufInfPos = (uint8_t)((uint32_t)param.rxBuf->inf - (uint32_t)param.rxBuf->prologue); - gIsoDep.rxBufLen = sizeof(rfalIsoDepBufFormat); - - gIsoDep.rxLen = param.rxLen; - gIsoDep.rxChaining = param.isRxChaining; - - gIsoDep.fwt = param.FWT; - gIsoDep.dFwt = param.dFWT; - gIsoDep.fsx = param.FSx; - gIsoDep.did = param.DID; - - /* Only change the FSx from activation if no to Keep */ - gIsoDep.ourFsx = ((param.ourFSx != RFAL_ISODEP_FSX_KEEP) ? param.ourFSx : gIsoDep.ourFsx); - - /* Clear inner control params for next dataExchange */ - gIsoDep.isRxChaining = false; - isoDepClearCounters(); - - if(gIsoDep.role == ISODEP_ROLE_PICC) { - if(gIsoDep.txBufLen > 0U) { - /* Ensure that an RTOX Ack is not being expected at moment */ - if(!gIsoDep.isWait4WTX) { - gIsoDep.state = ISODEP_ST_PICC_TX; - return ERR_NONE; - } else { - /* If RTOX Ack is expected, signal a pending Tx to be transmitted right after */ - gIsoDep.isTxPending = true; - } - } - - /* Digital 1.1 15.2.5.1 The first block SHALL be sent by the Reader/Writer */ - gIsoDep.state = ISODEP_ST_PICC_RX; - return ERR_NONE; - } - - gIsoDep.state = ISODEP_ST_PCD_TX; - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalIsoDepGetTransceiveStatus(void) { - if(gIsoDep.role == ISODEP_ROLE_PICC) { -#if RFAL_FEATURE_ISO_DEP_LISTEN - return isoDepDataExchangePICC(); -#else - return ERR_NOTSUPP; -#endif /* RFAL_FEATURE_ISO_DEP_LISTEN */ - } else { -#if RFAL_FEATURE_ISO_DEP_POLL - return isoDepDataExchangePCD(gIsoDep.rxLen, gIsoDep.rxChaining); -#else - return ERR_NOTSUPP; -#endif /* RFAL_FEATURE_ISO_DEP_POLL */ - } -} - -#if RFAL_FEATURE_ISO_DEP_LISTEN - -/*******************************************************************************/ -static ReturnCode isoDepDataExchangePICC(void) { - uint8_t rxPCB; - ReturnCode ret; - - switch(gIsoDep.state) { - /*******************************************************************************/ - case ISODEP_ST_IDLE: - return ERR_NONE; - - /*******************************************************************************/ - case ISODEP_ST_PICC_TX: - - ret = isoDepTx( - isoDep_PCBIBlock(gIsoDep.blockNumber), - gIsoDep.txBuf, - &gIsoDep.txBuf[gIsoDep.txBufInfPos], - gIsoDep.txBufLen, - RFAL_FWT_NONE); - - /* Clear pending Tx flag */ - gIsoDep.isTxPending = false; - - switch(ret) { - case ERR_NONE: - gIsoDep.state = ISODEP_ST_PICC_RX; - return ERR_BUSY; - - default: - /* MISRA 16.4: no empty default statement (a comment being enough) */ - break; - } - return ret; - - /*******************************************************************************/ - case ISODEP_ST_PICC_RX: - - ret = rfalGetTransceiveStatus(); - switch(ret) { - /*******************************************************************************/ - /* Data rcvd with error or timeout -> mute */ - case ERR_TIMEOUT: - case ERR_CRC: - case ERR_PAR: - case ERR_FRAMING: - - /* Digital 1.1 - 15.2.6.2 The CE SHALL NOT attempt error recovery and remains in Rx mode upon Transmission or a Protocol Error */ - isoDepReEnableRx((uint8_t*)gIsoDep.rxBuf, sizeof(rfalIsoDepBufFormat), gIsoDep.rxLen); - - return ERR_BUSY; - - /*******************************************************************************/ - case ERR_LINK_LOSS: - return ret; /* Debug purposes */ - - case ERR_BUSY: - return ret; /* Debug purposes */ - - /*******************************************************************************/ - case ERR_NONE: - *gIsoDep.rxLen = rfalConvBitsToBytes(*gIsoDep.rxLen); - break; - - /*******************************************************************************/ - default: - return ret; - } - break; - - /*******************************************************************************/ - case ISODEP_ST_PICC_SWTX: - - if(!isoDepTimerisExpired(gIsoDep.WTXTimer)) /* Do nothing until WTX timer has expired */ - { - return ERR_BUSY; - } - - /* Set waiting for WTX Ack Flag */ - gIsoDep.isWait4WTX = true; - - /* Digital 1.1 15.2.2.9 - Calculate the WTXM such that FWTtemp <= FWTmax */ - gIsoDep.lastWTXM = (uint8_t)isoDep_WTXMListenerMax(gIsoDep.fwt); - EXIT_ON_ERR(ret, isoDepHandleControlMsg(ISODEP_S_WTX, gIsoDep.lastWTXM)); - - gIsoDep.state = ISODEP_ST_PICC_RX; /* Go back to Rx to process WTX ack */ - return ERR_BUSY; - - /*******************************************************************************/ - case ISODEP_ST_PICC_SDSL: - - if(rfalIsTransceiveInRx()) /* Wait until DSL response has been sent */ - { - rfalIsoDepInitialize(); /* Session finished reInit vars */ - return ERR_SLEEP_REQ; /* Notify Deselect request */ - } - return ERR_BUSY; - - /*******************************************************************************/ - default: - return ERR_INTERNAL; - } - - /* ISO 14443-4 7.5.6.2 CE SHALL NOT attempt error recovery -> clear counters */ - isoDepClearCounters(); - - /*******************************************************************************/ - /* No error, process incoming msg */ - /*******************************************************************************/ - - /* Grab rcvd PCB */ - rxPCB = gIsoDep.rxBuf[ISODEP_PCB_POS]; - - /*******************************************************************************/ - /* When DID=0 PCD may or may not use DID, therefore check whether current PCD request - * has DID present to be reflected on max INF length #454 */ - - /* ReCalculate Header Length */ - gIsoDep.hdrLen = RFAL_ISODEP_PCB_LEN; - gIsoDep.hdrLen += (uint8_t)((isoDep_PCBhasDID(rxPCB)) ? RFAL_ISODEP_DID_LEN : 0U); - gIsoDep.hdrLen += (uint8_t)((isoDep_PCBhasNAD(rxPCB)) ? RFAL_ISODEP_NAD_LEN : 0U); - - /* Store whether last PCD block had DID. for PICC special handling of DID = 0 */ - if(gIsoDep.did == RFAL_ISODEP_DID_00) { - gIsoDep.lastDID00 = ((isoDep_PCBhasDID(rxPCB)) ? true : false); - } - - /*******************************************************************************/ - /* Check rcvd msg length, cannot be less then the expected header OR * - * if the rcvd msg exceeds our announced frame size (FSD) */ - if(((*gIsoDep.rxLen) < gIsoDep.hdrLen) || - ((*gIsoDep.rxLen) > (gIsoDep.ourFsx - ISODEP_CRC_LEN))) { - isoDepReEnableRx( - (uint8_t*)gIsoDep.actvParam.rxBuf, - sizeof(rfalIsoDepBufFormat), - gIsoDep.actvParam.rxLen); - return ERR_BUSY; /* ERR_PROTO Ignore this protocol request */ - } - - /* If we are expecting DID, check if PCB signals its presence and if device ID match OR - * If our DID=0 and DID is sent but with an incorrect value */ - if(((gIsoDep.did != RFAL_ISODEP_DID_00) && - (!isoDep_PCBhasDID(rxPCB) || (gIsoDep.did != gIsoDep.rxBuf[ISODEP_DID_POS]))) || - ((gIsoDep.did == RFAL_ISODEP_DID_00) && isoDep_PCBhasDID(rxPCB) && - (RFAL_ISODEP_DID_00 != gIsoDep.rxBuf[ISODEP_DID_POS]))) { - isoDepReEnableRx( - (uint8_t*)gIsoDep.actvParam.rxBuf, - sizeof(rfalIsoDepBufFormat), - gIsoDep.actvParam.rxLen); - return ERR_BUSY; /* Ignore a wrong DID request */ - } - - /* If we aren't expecting NAD and it's received */ - if((gIsoDep.nad == RFAL_ISODEP_NO_NAD) && isoDep_PCBhasNAD(rxPCB)) { - isoDepReEnableRx( - (uint8_t*)gIsoDep.actvParam.rxBuf, - sizeof(rfalIsoDepBufFormat), - gIsoDep.actvParam.rxLen); - return ERR_BUSY; /* Ignore a unexpected NAD request */ - } - - /*******************************************************************************/ - /* Process S-Block */ - /*******************************************************************************/ - if(isoDep_PCBisSBlock(rxPCB)) { - /* Check if is a Wait Time eXtension */ - if(isoDep_PCBisSWTX(rxPCB)) { - /* Check if we're expecting a S-WTX */ - if(isoDep_PCBisWTX(gIsoDep.lastPCB)) { - /* Digital 1.1 15.2.2.11 S(WTX) Ack with different WTXM -> Protocol Error * - * Power level indication also should be set to 0 */ - if((gIsoDep.rxBuf[gIsoDep.hdrLen] == gIsoDep.lastWTXM) && - ((*gIsoDep.rxLen - gIsoDep.hdrLen) == ISODEP_SWTX_INF_LEN)) { - /* Clear waiting for RTOX Ack Flag */ - gIsoDep.isWait4WTX = false; - - /* Check if a Tx is already pending */ - if(gIsoDep.isTxPending) { - /* Has a pending Tx, go immediately to TX */ - gIsoDep.state = ISODEP_ST_PICC_TX; - return ERR_BUSY; - } - - /* Set WTX timer */ - isoDepTimerStart( - gIsoDep.WTXTimer, - isoDep_WTXAdjust((gIsoDep.lastWTXM * rfalConv1fcToMs(gIsoDep.fwt)))); - - gIsoDep.state = ISODEP_ST_PICC_SWTX; - return ERR_BUSY; - } - } - /* Unexpected/Incorrect S-WTX, fall into reRenable */ - } - - /* Check if is a Deselect request */ - if(isoDep_PCBisSDeselect(rxPCB) && - ((*gIsoDep.rxLen - gIsoDep.hdrLen) == ISODEP_SDSL_INF_LEN)) { - EXIT_ON_ERR(ret, isoDepHandleControlMsg(ISODEP_S_DSL, RFAL_ISODEP_NO_PARAM)); - - /* S-DSL transmission ongoing, wait until complete */ - gIsoDep.state = ISODEP_ST_PICC_SDSL; - return ERR_BUSY; - } - - /* Unexpected S-Block, fall into reRenable */ - } - - /*******************************************************************************/ - /* Process R-Block */ - /*******************************************************************************/ - else if(isoDep_PCBisRBlock(rxPCB) && ((*gIsoDep.rxLen - gIsoDep.hdrLen) == ISODEP_RBLOCK_INF_LEN)) { - if(isoDep_PCBisRACK(rxPCB)) /* Check if is a R-ACK */ - { - if(isoDep_GetBN(rxPCB) == gIsoDep.blockNumber) /* Check block number */ - { - /* Rule 11 - R(ACK) with current bn -> re-transmit */ - if(!isoDep_PCBisIBlock(gIsoDep.lastPCB)) { - isoDepReSendControlMsg(); - } else { - gIsoDep.state = ISODEP_ST_PICC_TX; - } - - return ERR_BUSY; - } else { - if(!gIsoDep.isTxChaining) { - /* Rule 13 violation R(ACK) without performing chaining */ - isoDepReEnableRx( - (uint8_t*)gIsoDep.rxBuf, sizeof(rfalIsoDepBufFormat), gIsoDep.rxLen); - return ERR_BUSY; - } - - /* Rule E - R(ACK) with not current bn -> toogle bn */ - isoDep_ToggleBN(gIsoDep.blockNumber); - - /* This block has been transmitted and acknowledged, perform WTX until next data is provided */ - - /* Rule 9 - PICC is allowed to send an S(WTX) instead of an I-block or an R(ACK) */ - isoDepTimerStart(gIsoDep.WTXTimer, isoDep_WTXAdjust(rfalConv1fcToMs(gIsoDep.fwt))); - gIsoDep.state = ISODEP_ST_PICC_SWTX; - - /* Rule 13 - R(ACK) with not current bn -> continue chaining */ - return ERR_NONE; /* This block has been transmitted */ - } - } else if(isoDep_PCBisRNAK(rxPCB)) /* Check if is a R-NACK */ - { - if(isoDep_GetBN(rxPCB) == gIsoDep.blockNumber) /* Check block number */ - { - /* Rule 11 - R(NAK) with current bn -> re-transmit last x-Block */ - if(!isoDep_PCBisIBlock(gIsoDep.lastPCB)) { - isoDepReSendControlMsg(); - } else { - gIsoDep.state = ISODEP_ST_PICC_TX; - } - - return ERR_BUSY; - } else { - /* Rule 12 - R(NAK) with not current bn -> R(ACK) */ - EXIT_ON_ERR(ret, isoDepHandleControlMsg(ISODEP_R_ACK, RFAL_ISODEP_NO_PARAM)); - - return ERR_BUSY; - } - } else { - /* MISRA 15.7 - Empty else */ - } - - /* Unexpected R-Block, fall into reRenable */ - } - - /*******************************************************************************/ - /* Process I-Block */ - /*******************************************************************************/ - else if(isoDep_PCBisIBlock(rxPCB)) { - /* Rule D - When an I-block is received, the PICC shall toggle its block number before sending a block */ - isoDep_ToggleBN(gIsoDep.blockNumber); - - /*******************************************************************************/ - /* Check if the block number is the one expected */ - /* Check if PCD sent an I-Block instead ACK/NACK when we are chaining */ - if((isoDep_GetBN(rxPCB) != gIsoDep.blockNumber) || (gIsoDep.isTxChaining)) { - /* Remain in the same Block Number */ - isoDep_ToggleBN(gIsoDep.blockNumber); - - /* ISO 14443-4 7.5.6.2 & Digital 1.1 - 15.2.6.2 The CE SHALL NOT attempt error recovery and remains in Rx mode upon Transmission or a Protocol Error */ - isoDepReEnableRx((uint8_t*)gIsoDep.rxBuf, sizeof(rfalIsoDepBufFormat), gIsoDep.rxLen); - return ERR_BUSY; - } - - /*******************************************************************************/ - /* is PCD performing chaining ? */ - if(isoDep_PCBisChaining(rxPCB)) { - gIsoDep.isRxChaining = true; - *gIsoDep.rxChaining = true; /* Output Parameter*/ - - EXIT_ON_ERR(ret, isoDepHandleControlMsg(ISODEP_R_ACK, RFAL_ISODEP_NO_PARAM)); - - /* Received I-Block with chaining, send current data to DH */ - - /* remove ISO DEP header, check is necessary to move the INF data on the buffer */ - *gIsoDep.rxLen -= gIsoDep.hdrLen; - if((gIsoDep.hdrLen != gIsoDep.rxBufInfPos) && (*gIsoDep.rxLen > 0U)) { - ST_MEMMOVE( - &gIsoDep.rxBuf[gIsoDep.rxBufInfPos], - &gIsoDep.rxBuf[gIsoDep.hdrLen], - *gIsoDep.rxLen); - } - return ERR_AGAIN; /* Send Again signalling to run again, but some chaining data has arrived*/ - } - - /*******************************************************************************/ - /* PCD is not performing chaining */ - gIsoDep.isRxChaining = false; /* clear PCD chaining flag */ - *gIsoDep.rxChaining = false; /* Output Parameter */ - - /* remove ISO DEP header, check is necessary to move the INF data on the buffer */ - *gIsoDep.rxLen -= gIsoDep.hdrLen; - if((gIsoDep.hdrLen != gIsoDep.rxBufInfPos) && (*gIsoDep.rxLen > 0U)) { - ST_MEMMOVE( - &gIsoDep.rxBuf[gIsoDep.rxBufInfPos], - &gIsoDep.rxBuf[gIsoDep.hdrLen], - *gIsoDep.rxLen); - } - - /*******************************************************************************/ - /* Reception done, send data back and start WTX timer */ - isoDepTimerStart(gIsoDep.WTXTimer, isoDep_WTXAdjust(rfalConv1fcToMs(gIsoDep.fwt))); - - gIsoDep.state = ISODEP_ST_PICC_SWTX; - return ERR_NONE; - } else { - /* MISRA 15.7 - Empty else */ - } - - /* Unexpected/Unknown Block */ - /* ISO 14443-4 7.5.6.2 & Digital 1.1 - 15.2.6.2 The CE SHALL NOT attempt error recovery and remains in Rx mode upon Transmission or a Protocol Error */ - isoDepReEnableRx((uint8_t*)gIsoDep.rxBuf, sizeof(rfalIsoDepBufFormat), gIsoDep.rxLen); - - return ERR_BUSY; -} -#endif /* RFAL_FEATURE_ISO_DEP_LISTEN */ - -#if RFAL_FEATURE_ISO_DEP_POLL - -#if RFAL_FEATURE_NFCA - -/*******************************************************************************/ -static ReturnCode - rfalIsoDepStartRATS(rfalIsoDepFSxI FSDI, uint8_t DID, rfalIsoDepAts* ats, uint8_t* atsLen) { - rfalTransceiveContext ctx; - - if(ats == NULL) { - return ERR_PARAM; - } - - gIsoDep.rxBuf = (uint8_t*)ats; - gIsoDep.rxLen8 = atsLen; - gIsoDep.did = DID; - - /*******************************************************************************/ - /* Compose RATS */ - gIsoDep.actv.ratsReq.CMD = RFAL_ISODEP_CMD_RATS; - gIsoDep.actv.ratsReq.PARAM = - (((uint8_t)FSDI << RFAL_ISODEP_RATS_PARAM_FSDI_SHIFT) & RFAL_ISODEP_RATS_PARAM_FSDI_MASK) | - (DID & RFAL_ISODEP_RATS_PARAM_DID_MASK); - - rfalCreateByteFlagsTxRxContext( - ctx, - (uint8_t*)&gIsoDep.actv.ratsReq, - sizeof(rfalIsoDepRats), - (uint8_t*)ats, - sizeof(rfalIsoDepAts), - &gIsoDep.rxBufLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_ISODEP_T4T_FWT_ACTIVATION); - return rfalStartTransceive(&ctx); -} - -/*******************************************************************************/ -static ReturnCode rfalIsoDepGetRATSStatus(void) { - ReturnCode ret; - - ret = rfalGetTransceiveStatus(); - if(ret == ERR_NONE) { - gIsoDep.rxBufLen = rfalConvBitsToBytes(gIsoDep.rxBufLen); - - /* Check for valid ATS length Digital 1.1 13.6.2.1 & 13.6.2.3 */ - if((gIsoDep.rxBufLen < RFAL_ISODEP_ATS_MIN_LEN) || - (gIsoDep.rxBufLen > RFAL_ISODEP_ATS_MAX_LEN) || - (gIsoDep.rxBuf[RFAL_ISODEP_ATS_TL_POS] != gIsoDep.rxBufLen)) { - return ERR_PROTO; - } - - /* Assign our FSx, in case the a Deselect is send without Transceive */ - gIsoDep.ourFsx = rfalIsoDepFSxI2FSx( - (uint8_t)(gIsoDep.actv.ratsReq.PARAM >> RFAL_ISODEP_RATS_PARAM_FSDI_SHIFT)); - - /* Check and assign if ATS length was requested (length also available on TL) */ - if(gIsoDep.rxLen8 != NULL) { - *gIsoDep.rxLen8 = (uint8_t)gIsoDep.rxBufLen; - } - } - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalIsoDepRATS(rfalIsoDepFSxI FSDI, uint8_t DID, rfalIsoDepAts* ats, uint8_t* atsLen) { - ReturnCode ret; - - EXIT_ON_ERR(ret, rfalIsoDepStartRATS(FSDI, DID, ats, atsLen)); - rfalIsoDepRunBlocking(ret, rfalIsoDepGetRATSStatus()); - - return ret; -} - -/*******************************************************************************/ -static ReturnCode - rfalIsoDepStartPPS(uint8_t DID, rfalBitRate DSI, rfalBitRate DRI, rfalIsoDepPpsRes* ppsRes) { - rfalTransceiveContext ctx; - - if((ppsRes == NULL) || (DSI > RFAL_BR_848) || (DRI > RFAL_BR_848) || - (DID > RFAL_ISODEP_DID_MAX)) { - return ERR_PARAM; - } - - gIsoDep.rxBuf = (uint8_t*)ppsRes; - - /*******************************************************************************/ - /* Compose PPS Request */ - gIsoDep.actv.ppsReq.PPSS = (RFAL_ISODEP_PPS_SB | (DID & RFAL_ISODEP_PPS_SB_DID_MASK)); - gIsoDep.actv.ppsReq.PPS0 = RFAL_ISODEP_PPS_PPS0_PPS1_PRESENT; - gIsoDep.actv.ppsReq.PPS1 = - (RFAL_ISODEP_PPS_PPS1 | - ((((uint8_t)DSI << RFAL_ISODEP_PPS_PPS1_DSI_SHIFT) | (uint8_t)DRI) & - RFAL_ISODEP_PPS_PPS1_DXI_MASK)); - - rfalCreateByteFlagsTxRxContext( - ctx, - (uint8_t*)&gIsoDep.actv.ppsReq, - sizeof(rfalIsoDepPpsReq), - (uint8_t*)ppsRes, - sizeof(rfalIsoDepPpsRes), - &gIsoDep.rxBufLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_ISODEP_T4T_FWT_ACTIVATION); - return rfalStartTransceive(&ctx); -} - -/*******************************************************************************/ -static ReturnCode rfalIsoDepGetPPSSTatus(void) { - ReturnCode ret; - - ret = rfalGetTransceiveStatus(); - if(ret == ERR_NONE) { - gIsoDep.rxBufLen = rfalConvBitsToBytes(gIsoDep.rxBufLen); - - /* Check for valid PPS Response */ - if((gIsoDep.rxBufLen != RFAL_ISODEP_PPS_RES_LEN) || - (*gIsoDep.rxBuf != gIsoDep.actv.ppsReq.PPSS)) { - return ERR_PROTO; - } - } - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalIsoDepPPS(uint8_t DID, rfalBitRate DSI, rfalBitRate DRI, rfalIsoDepPpsRes* ppsRes) { - ReturnCode ret; - - EXIT_ON_ERR(ret, rfalIsoDepStartPPS(DID, DSI, DRI, ppsRes)); - rfalIsoDepRunBlocking(ret, rfalIsoDepGetPPSSTatus()); - - return ret; -} - -#endif /* RFAL_FEATURE_NFCA */ - -#if RFAL_FEATURE_NFCB - -static ReturnCode rfalIsoDepStartATTRIB( - const uint8_t* nfcid0, - uint8_t PARAM1, - rfalBitRate DSI, - rfalBitRate DRI, - rfalIsoDepFSxI FSDI, - uint8_t PARAM3, - uint8_t DID, - const uint8_t* HLInfo, - uint8_t HLInfoLen, - uint32_t fwt, - rfalIsoDepAttribRes* attribRes, - uint8_t* attribResLen) { - rfalTransceiveContext ctx; - - if((attribRes == NULL) || (attribResLen == NULL) || (DSI > RFAL_BR_848) || - (DRI > RFAL_BR_848) || (DID > RFAL_ISODEP_DID_MAX)) { - return ERR_NONE; - } - - gIsoDep.rxBuf = (uint8_t*)attribRes; - gIsoDep.rxLen8 = attribResLen; - gIsoDep.did = DID; - - /*******************************************************************************/ - /* Compose ATTRIB command */ - gIsoDep.actv.attribReq.cmd = RFAL_ISODEP_CMD_ATTRIB; - gIsoDep.actv.attribReq.Param.PARAM1 = PARAM1; - gIsoDep.actv.attribReq.Param.PARAM2 = - (((((uint8_t)DSI << RFAL_ISODEP_ATTRIB_PARAM2_DSI_SHIFT) | - ((uint8_t)DRI << RFAL_ISODEP_ATTRIB_PARAM2_DRI_SHIFT)) & - RFAL_ISODEP_ATTRIB_PARAM2_DXI_MASK) | - ((uint8_t)FSDI & RFAL_ISODEP_ATTRIB_PARAM2_FSDI_MASK)); - gIsoDep.actv.attribReq.Param.PARAM3 = PARAM3; - gIsoDep.actv.attribReq.Param.PARAM4 = (DID & RFAL_ISODEP_ATTRIB_PARAM4_DID_MASK); - ST_MEMCPY(gIsoDep.actv.attribReq.nfcid0, nfcid0, RFAL_NFCB_NFCID0_LEN); - - /* Append the Higher layer Info if provided */ - if((HLInfo != NULL) && (HLInfoLen > 0U)) { - ST_MEMCPY( - gIsoDep.actv.attribReq.HLInfo, HLInfo, MIN(HLInfoLen, RFAL_ISODEP_ATTRIB_HLINFO_LEN)); - } - - rfalCreateByteFlagsTxRxContext( - ctx, - (uint8_t*)&gIsoDep.actv.attribReq, - (uint16_t)(RFAL_ISODEP_ATTRIB_HDR_LEN + MIN((uint16_t)HLInfoLen, RFAL_ISODEP_ATTRIB_HLINFO_LEN)), - (uint8_t*)gIsoDep.rxBuf, - sizeof(rfalIsoDepAttribRes), - &gIsoDep.rxBufLen, - RFAL_TXRX_FLAGS_DEFAULT, - fwt); - return rfalStartTransceive(&ctx); -} - -/*******************************************************************************/ -static ReturnCode rfalIsoDepGetATTRIBStatus(void) { - ReturnCode ret; - - ret = rfalGetTransceiveStatus(); - if(ret == ERR_NONE) { - gIsoDep.rxBufLen = rfalConvBitsToBytes(gIsoDep.rxBufLen); - - /* Check a for valid ATTRIB Response Digital 1.1 15.6.2.1 */ - if((gIsoDep.rxBufLen < RFAL_ISODEP_ATTRIB_RES_HDR_LEN) || - ((gIsoDep.rxBuf[RFAL_ISODEP_ATTRIB_RES_MBLIDID_POS] & - RFAL_ISODEP_ATTRIB_RES_DID_MASK) != gIsoDep.did)) { - return ERR_PROTO; - } - - if(gIsoDep.rxLen8 != NULL) { - *gIsoDep.rxLen8 = (uint8_t)gIsoDep.rxBufLen; - } - - gIsoDep.ourFsx = rfalIsoDepFSxI2FSx( - (uint8_t)(gIsoDep.actv.attribReq.Param.PARAM2 & RFAL_ISODEP_ATTRIB_PARAM2_FSDI_MASK)); - } - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalIsoDepATTRIB( - const uint8_t* nfcid0, - uint8_t PARAM1, - rfalBitRate DSI, - rfalBitRate DRI, - rfalIsoDepFSxI FSDI, - uint8_t PARAM3, - uint8_t DID, - const uint8_t* HLInfo, - uint8_t HLInfoLen, - uint32_t fwt, - rfalIsoDepAttribRes* attribRes, - uint8_t* attribResLen) { - ReturnCode ret; - - EXIT_ON_ERR( - ret, - rfalIsoDepStartATTRIB( - nfcid0, - PARAM1, - DSI, - DRI, - FSDI, - PARAM3, - DID, - HLInfo, - HLInfoLen, - fwt, - attribRes, - attribResLen)); - rfalIsoDepRunBlocking(ret, rfalIsoDepGetATTRIBStatus()); - - return ret; -} - -#endif /* RFAL_FEATURE_NFCB */ - -#if RFAL_FEATURE_NFCA - -/*******************************************************************************/ -ReturnCode rfalIsoDepPollAHandleActivation( - rfalIsoDepFSxI FSDI, - uint8_t DID, - rfalBitRate maxBR, - rfalIsoDepDevice* isoDepDev) { - ReturnCode ret; - - EXIT_ON_ERR(ret, rfalIsoDepPollAStartActivation(FSDI, DID, maxBR, isoDepDev)); - rfalIsoDepRunBlocking(ret, rfalIsoDepPollAGetActivationStatus()); - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalIsoDepPollAStartActivation( - rfalIsoDepFSxI FSDI, - uint8_t DID, - rfalBitRate maxBR, - rfalIsoDepDevice* isoDepDev) { - ReturnCode ret; - - if(isoDepDev == NULL) { - return ERR_PARAM; - } - - /* Enable EMD handling according Digital 1.1 4.1.1.1 ; EMVCo 2.6 4.9.2 */ - rfalSetErrorHandling(RFAL_ERRORHANDLING_EMVCO); - - /* Start RATS Transceive */ - EXIT_ON_ERR( - ret, - rfalIsoDepStartRATS( - FSDI, - DID, - &isoDepDev->activation.A.Listener.ATS, - &isoDepDev->activation.A.Listener.ATSLen)); - - isoDepDev->info.DSI = maxBR; - gIsoDep.actvDev = isoDepDev; - gIsoDep.cntRRetrys = gIsoDep.maxRetriesRATS; - gIsoDep.state = ISODEP_ST_PCD_ACT_RATS; - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalIsoDepPollAGetActivationStatus(void) { - ReturnCode ret; - uint8_t msgIt; - rfalBitRate maxBR; - - switch(gIsoDep.state) { - /*******************************************************************************/ - case ISODEP_ST_PCD_ACT_RATS: - - ret = rfalIsoDepGetRATSStatus(); - if(ret != ERR_BUSY) { - if(ret != ERR_NONE) { - /* EMVCo 2.6 9.6.1.1 & 9.6.1.2 If a timeout error is detected retransmit, on transmission error abort */ - if((gIsoDep.compMode == RFAL_COMPLIANCE_MODE_EMV) && (ret != ERR_TIMEOUT)) { - break; - } - - if(gIsoDep.cntRRetrys != 0U) { - /* Ensure FDT before retransmission (reuse RFAL GT timer) */ - rfalSetGT(rfalGetFDTPoll()); - rfalFieldOnAndStartGT(); - - /* Send RATS retransmission */ /* PRQA S 4342 1 # MISRA 10.5 - Layout of enum rfalIsoDepFSxI is guaranteed whithin 4bit range */ - EXIT_ON_ERR( - ret, - rfalIsoDepStartRATS( - (rfalIsoDepFSxI)(uint8_t)(gIsoDep.actv.ratsReq.PARAM >> RFAL_ISODEP_RATS_PARAM_FSDI_SHIFT), - gIsoDep.did, - &gIsoDep.actvDev->activation.A.Listener.ATS, - &gIsoDep.actvDev->activation.A.Listener.ATSLen)); - gIsoDep.cntRRetrys--; - ret = ERR_BUSY; - } - /* Switch between NFC Forum and ISO14443-4 behaviour #595 - * ISO14443-4 5.6.1 If RATS fails, a Deactivation sequence should be performed as defined on clause 8 - * Activity 1.1 9.6 Device Deactivation Activity is to be only performed when there's an active device */ - else if(gIsoDep.compMode == RFAL_COMPLIANCE_MODE_ISO) { - rfalIsoDepDeselect(); - } else { - /* MISRA 15.7 - Empty else */ - } - } else /* ATS received */ - { - maxBR = gIsoDep.actvDev->info.DSI; /* Retrieve requested max bitrate */ - - /*******************************************************************************/ - /* Process ATS Response */ - gIsoDep.actvDev->info.FWI = - RFAL_ISODEP_FWI_DEFAULT; /* Default value EMVCo 2.6 5.7.2.6 */ - gIsoDep.actvDev->info.SFGI = 0U; - gIsoDep.actvDev->info.MBL = 0U; - gIsoDep.actvDev->info.DSI = RFAL_BR_106; - gIsoDep.actvDev->info.DRI = RFAL_BR_106; - gIsoDep.actvDev->info.FSxI = (uint8_t) - RFAL_ISODEP_FSXI_32; /* FSC default value is 32 bytes ISO14443-A 5.2.3 */ - - /*******************************************************************************/ - /* Check for ATS optional fields */ - if(gIsoDep.actvDev->activation.A.Listener.ATS.TL > RFAL_ISODEP_ATS_MIN_LEN) { - msgIt = RFAL_ISODEP_ATS_MIN_LEN; - - /* Format byte T0 is optional, if present assign FSDI */ - gIsoDep.actvDev->info.FSxI = - (gIsoDep.actvDev->activation.A.Listener.ATS.T0 & - RFAL_ISODEP_ATS_T0_FSCI_MASK); - - /* T0 has already been processed, always the same position */ - msgIt++; - - /* Check if TA is present */ - if((gIsoDep.actvDev->activation.A.Listener.ATS.T0 & - RFAL_ISODEP_ATS_T0_TA_PRESENCE_MASK) != 0U) { - rfalIsoDepCalcBitRate( - maxBR, - ((uint8_t*)&gIsoDep.actvDev->activation.A.Listener.ATS)[msgIt++], - &gIsoDep.actvDev->info.DSI, - &gIsoDep.actvDev->info.DRI); - } - - /* Check if TB is present */ - if((gIsoDep.actvDev->activation.A.Listener.ATS.T0 & - RFAL_ISODEP_ATS_T0_TB_PRESENCE_MASK) != 0U) { - gIsoDep.actvDev->info.SFGI = - ((uint8_t*)&gIsoDep.actvDev->activation.A.Listener.ATS)[msgIt++]; - gIsoDep.actvDev->info.FWI = - (uint8_t)((gIsoDep.actvDev->info.SFGI >> RFAL_ISODEP_ATS_TB_FWI_SHIFT) & RFAL_ISODEP_ATS_FWI_MASK); - gIsoDep.actvDev->info.SFGI &= RFAL_ISODEP_ATS_TB_SFGI_MASK; - } - - /* Check if TC is present */ - if((gIsoDep.actvDev->activation.A.Listener.ATS.T0 & - RFAL_ISODEP_ATS_T0_TC_PRESENCE_MASK) != 0U) { - /* Check for Protocol features support */ - /* Advanced protocol features defined on Digital 1.0 Table 69, removed after */ - gIsoDep.actvDev->info.supAdFt = - (((((uint8_t*)&gIsoDep.actvDev->activation.A.Listener.ATS)[msgIt] & - RFAL_ISODEP_ATS_TC_ADV_FEAT) != 0U) ? - true : - false); - gIsoDep.actvDev->info.supDID = - (((((uint8_t*)&gIsoDep.actvDev->activation.A.Listener.ATS)[msgIt] & - RFAL_ISODEP_ATS_TC_DID) != 0U) ? - true : - false); - gIsoDep.actvDev->info.supNAD = - (((((uint8_t*)&gIsoDep.actvDev->activation.A.Listener.ATS)[msgIt++] & - RFAL_ISODEP_ATS_TC_NAD) != 0U) ? - true : - false); - } - } - - gIsoDep.actvDev->info.FSx = rfalIsoDepFSxI2FSx(gIsoDep.actvDev->info.FSxI); - gIsoDep.fsx = gIsoDep.actvDev->info.FSx; - - gIsoDep.actvDev->info.SFGT = - rfalIsoDepSFGI2SFGT((uint8_t)gIsoDep.actvDev->info.SFGI); - - /* Ensure SFGT before following frame (reuse RFAL GT timer) */ - rfalSetGT(rfalConvMsTo1fc(gIsoDep.actvDev->info.SFGT)); - rfalFieldOnAndStartGT(); - - gIsoDep.actvDev->info.FWT = rfalIsoDepFWI2FWT(gIsoDep.actvDev->info.FWI); - gIsoDep.actvDev->info.dFWT = RFAL_ISODEP_DFWT_20; - - gIsoDep.actvDev->info.DID = - ((gIsoDep.actvDev->info.supDID) ? gIsoDep.did : RFAL_ISODEP_NO_DID); - gIsoDep.actvDev->info.NAD = RFAL_ISODEP_NO_NAD; - - /*******************************************************************************/ - /* If higher bit rates are supported by both devices, send PPS */ - if((gIsoDep.actvDev->info.DSI != RFAL_BR_106) || - (gIsoDep.actvDev->info.DRI != RFAL_BR_106)) { - /* Send PPS */ /* PRQA S 0310 1 # MISRA 11.3 - Intentional safe cast to avoiding buffer duplication */ - EXIT_ON_ERR( - ret, - rfalIsoDepStartPPS( - gIsoDep.actvDev->info.DID, - gIsoDep.actvDev->info.DSI, - gIsoDep.actvDev->info.DRI, - (rfalIsoDepPpsRes*)&gIsoDep.ctrlBuf)); - - gIsoDep.state = ISODEP_ST_PCD_ACT_PPS; - return ERR_BUSY; - } - - return ERR_NONE; - } - } - break; - - /*******************************************************************************/ - case ISODEP_ST_PCD_ACT_PPS: - ret = rfalIsoDepGetPPSSTatus(); - if(ret != ERR_BUSY) { - /* Check whether PPS has been acknowledge */ - if(ret == ERR_NONE) { - /* DSI code the divisor from PICC to PCD */ - /* DRI code the divisor from PCD to PICC */ - rfalSetBitRate(gIsoDep.actvDev->info.DRI, gIsoDep.actvDev->info.DSI); - } else { - /* If PPS has faled keep activation bit rate */ - gIsoDep.actvDev->info.DSI = RFAL_BR_106; - gIsoDep.actvDev->info.DRI = RFAL_BR_106; - } - } - break; - - /*******************************************************************************/ - default: - ret = ERR_WRONG_STATE; - break; - } - - return ret; -} -#endif /* RFAL_FEATURE_NFCA */ - -#if RFAL_FEATURE_NFCB - -/*******************************************************************************/ -ReturnCode rfalIsoDepPollBHandleActivation( - rfalIsoDepFSxI FSDI, - uint8_t DID, - rfalBitRate maxBR, - uint8_t PARAM1, - const rfalNfcbListenDevice* nfcbDev, - const uint8_t* HLInfo, - uint8_t HLInfoLen, - rfalIsoDepDevice* isoDepDev) { - ReturnCode ret; - - EXIT_ON_ERR( - ret, - rfalIsoDepPollBStartActivation( - FSDI, DID, maxBR, PARAM1, nfcbDev, HLInfo, HLInfoLen, isoDepDev)); - rfalIsoDepRunBlocking(ret, rfalIsoDepPollBGetActivationStatus()); - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalIsoDepPollBStartActivation( - rfalIsoDepFSxI FSDI, - uint8_t DID, - rfalBitRate maxBR, - uint8_t PARAM1, - const rfalNfcbListenDevice* nfcbDev, - const uint8_t* HLInfo, - uint8_t HLInfoLen, - rfalIsoDepDevice* isoDepDev) { - ReturnCode ret; - - /***************************************************************************/ - /* Initialize ISO-DEP Device with info from SENSB_RES */ - isoDepDev->info.FWI = - ((nfcbDev->sensbRes.protInfo.FwiAdcFo >> RFAL_NFCB_SENSB_RES_FWI_SHIFT) & - RFAL_NFCB_SENSB_RES_FWI_MASK); - isoDepDev->info.FWT = rfalIsoDepFWI2FWT(isoDepDev->info.FWI); - isoDepDev->info.dFWT = RFAL_NFCB_DFWT; - isoDepDev->info.SFGI = - (((uint32_t)nfcbDev->sensbRes.protInfo.SFGI >> RFAL_NFCB_SENSB_RES_SFGI_SHIFT) & - RFAL_NFCB_SENSB_RES_SFGI_MASK); - isoDepDev->info.SFGT = rfalIsoDepSFGI2SFGT((uint8_t)isoDepDev->info.SFGI); - isoDepDev->info.FSxI = - ((nfcbDev->sensbRes.protInfo.FsciProType >> RFAL_NFCB_SENSB_RES_FSCI_SHIFT) & - RFAL_NFCB_SENSB_RES_FSCI_MASK); - isoDepDev->info.FSx = rfalIsoDepFSxI2FSx(isoDepDev->info.FSxI); - isoDepDev->info.DID = DID; - isoDepDev->info.supDID = - (((nfcbDev->sensbRes.protInfo.FwiAdcFo & RFAL_NFCB_SENSB_RES_FO_DID_MASK) != 0U) ? true : - false); - isoDepDev->info.supNAD = - (((nfcbDev->sensbRes.protInfo.FwiAdcFo & RFAL_NFCB_SENSB_RES_FO_NAD_MASK) != 0U) ? true : - false); - - /* Check if DID requested is supported by PICC */ - if((DID != RFAL_ISODEP_NO_DID) && (!isoDepDev->info.supDID)) { - return ERR_PARAM; - } - - /* Enable EMD handling according Digital 2.1 4.1.1.1 ; EMVCo 3.0 4.9.2 */ - rfalSetErrorHandling(RFAL_ERRORHANDLING_EMVCO); - - /***************************************************************************/ - /* Set FDT Poll to be used on upcoming communications */ - if(gIsoDep.compMode == RFAL_COMPLIANCE_MODE_EMV) { - /* Disregard Minimum TR2 returned by PICC, always use FDTb MIN EMVCo 3.0 6.3.2.10 */ - rfalSetFDTPoll(RFAL_FDT_POLL_NFCB_POLLER); - } else { - /* Apply minimum TR2 from SENSB_RES Digital 2.1 7.6.2.23 */ - rfalSetFDTPoll(rfalNfcbTR2ToFDT( - ((nfcbDev->sensbRes.protInfo.FsciProType >> RFAL_NFCB_SENSB_RES_PROTO_TR2_SHIFT) & - RFAL_NFCB_SENSB_RES_PROTO_TR2_MASK))); - } - - /* Calculate max Bit Rate */ - rfalIsoDepCalcBitRate( - maxBR, nfcbDev->sensbRes.protInfo.BRC, &isoDepDev->info.DSI, &isoDepDev->info.DRI); - - /***************************************************************************/ - /* Send ATTRIB Command */ - EXIT_ON_ERR( - ret, - rfalIsoDepStartATTRIB( - (const uint8_t*)&nfcbDev->sensbRes.nfcid0, - (((nfcbDev->sensbRes.protInfo.FwiAdcFo & RFAL_NFCB_SENSB_RES_ADC_ADV_FEATURE_MASK) != - 0U) ? - PARAM1 : - RFAL_ISODEP_ATTRIB_REQ_PARAM1_DEFAULT), - isoDepDev->info.DSI, - isoDepDev->info.DRI, - FSDI, - (gIsoDep.compMode == RFAL_COMPLIANCE_MODE_EMV) ? - RFAL_NFCB_SENSB_RES_PROTO_ISO_MASK : - (nfcbDev->sensbRes.protInfo.FsciProType & - ((RFAL_NFCB_SENSB_RES_PROTO_TR2_MASK << RFAL_NFCB_SENSB_RES_PROTO_TR2_SHIFT) | - RFAL_NFCB_SENSB_RES_PROTO_ISO_MASK)), /* EMVCo 2.6 6.4.1.9 */ - DID, - HLInfo, - HLInfoLen, - (isoDepDev->info.FWT + isoDepDev->info.dFWT), - &isoDepDev->activation.B.Listener.ATTRIB_RES, - &isoDepDev->activation.B.Listener.ATTRIB_RESLen)); - - gIsoDep.actvDev = isoDepDev; - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalIsoDepPollBGetActivationStatus(void) { - ReturnCode ret; - uint8_t mbli; - - /***************************************************************************/ - /* Process ATTRIB Response */ - ret = rfalIsoDepGetATTRIBStatus(); - if(ret != ERR_BUSY) { - if(ret == ERR_NONE) { - /* Digital 1.1 14.6.2.3 - Check if received DID match */ - if((gIsoDep.actvDev->activation.B.Listener.ATTRIB_RES.mbliDid & - RFAL_ISODEP_ATTRIB_RES_DID_MASK) != gIsoDep.did) { - return ERR_PROTO; - } - - /* Retrieve MBLI and calculate new FDS/MBL (Maximum Buffer Length) */ - mbli = - ((gIsoDep.actvDev->activation.B.Listener.ATTRIB_RES.mbliDid >> - RFAL_ISODEP_ATTRIB_RES_MBLI_SHIFT) & - RFAL_ISODEP_ATTRIB_RES_MBLI_MASK); - if(mbli > 0U) { - /* Digital 1.1 14.6.2 Calculate Maximum Buffer Length MBL = FSC x 2^(MBLI-1) */ - gIsoDep.actvDev->info.MBL = - (gIsoDep.actvDev->info.FSx * ((uint32_t)1U << (mbli - 1U))); - } - - /* DSI code the divisor from PICC to PCD */ - /* DRI code the divisor from PCD to PICC */ - rfalSetBitRate(gIsoDep.actvDev->info.DRI, gIsoDep.actvDev->info.DSI); - - /* REMARK: SoF EoF TR0 and TR1 are not passed on to RF layer */ - - /* Start the SFGT timer (reuse RFAL GT timer) */ - rfalSetGT(rfalConvMsTo1fc(gIsoDep.actvDev->info.SFGT)); - rfalFieldOnAndStartGT(); - } else { - gIsoDep.actvDev->info.DSI = RFAL_BR_106; - gIsoDep.actvDev->info.DRI = RFAL_BR_106; - } - - /*******************************************************************************/ - /* Store already FS info, rfalIsoDepGetMaxInfLen() may be called before setting TxRx params */ - gIsoDep.fsx = gIsoDep.actvDev->info.FSx; - } - - return ret; -} - -#endif /* RFAL_FEATURE_NFCB */ - -/*******************************************************************************/ -ReturnCode rfalIsoDepPollHandleSParameters( - rfalIsoDepDevice* isoDepDev, - rfalBitRate maxTxBR, - rfalBitRate maxRxBR) { - uint8_t it; - uint8_t supPCD2PICC; - uint8_t supPICC2PCD; - uint8_t currenttxBR; - uint8_t currentrxBR; - rfalBitRate txBR; - rfalBitRate rxBR; - uint16_t rcvLen; - ReturnCode ret; - rfalIsoDepControlMsgSParam sParam; - - if((isoDepDev == NULL) || (maxTxBR > RFAL_BR_13560) || (maxRxBR > RFAL_BR_13560)) { - return ERR_PARAM; - } - - it = 0; - supPICC2PCD = 0x00; - supPCD2PICC = 0x00; - txBR = RFAL_BR_106; - rxBR = RFAL_BR_106; - sParam.pcb = ISODEP_PCB_SPARAMETERS; - - /*******************************************************************************/ - /* Send S(PARAMETERS) - Block Info */ - sParam.sParam.tag = RFAL_ISODEP_SPARAM_TAG_BLOCKINFO; - sParam.sParam.value[it++] = RFAL_ISODEP_SPARAM_TAG_BRREQ; - sParam.sParam.value[it++] = RFAL_ISODEP_SPARAM_TAG_BRREQ_LEN; - sParam.sParam.length = it; - - /* Send S(PARAMETERS). Use a fixed FWI of 4 ISO14443-4 2016 7.2 */ - EXIT_ON_ERR( - ret, - rfalTransceiveBlockingTxRx( - (uint8_t*)&sParam, - (RFAL_ISODEP_SPARAM_HDR_LEN + (uint16_t)it), - (uint8_t*)&sParam, - sizeof(rfalIsoDepControlMsgSParam), - &rcvLen, - RFAL_TXRX_FLAGS_DEFAULT, - ISODEP_FWT_DEACTIVATION)); - - it = 0; - - /*******************************************************************************/ - /* Check S(PARAMETERS) response */ - if((sParam.pcb != ISODEP_PCB_SPARAMETERS) || - (sParam.sParam.tag != RFAL_ISODEP_SPARAM_TAG_BLOCKINFO) || - (sParam.sParam.value[it] != RFAL_ISODEP_SPARAM_TAG_BRIND) || - (rcvLen < RFAL_ISODEP_SPARAM_HDR_LEN) || - (rcvLen != ((uint16_t)sParam.sParam.length + RFAL_ISODEP_SPARAM_HDR_LEN))) { - return ERR_PROTO; - } - - /* Retrieve PICC's bit rate PICC capabilities */ - for(it = 0; it < (rcvLen - (uint16_t)RFAL_ISODEP_SPARAM_TAG_LEN); it++) { - if((sParam.sParam.value[it] == RFAL_ISODEP_SPARAM_TAG_SUP_PCD2PICC) && - (sParam.sParam.value[it + (uint16_t)RFAL_ISODEP_SPARAM_TAG_LEN] == - RFAL_ISODEP_SPARAM_TAG_PCD2PICC_LEN)) { - supPCD2PICC = sParam.sParam.value[it + RFAL_ISODEP_SPARAM_TAG_PCD2PICC_LEN]; - } - - if((sParam.sParam.value[it] == RFAL_ISODEP_SPARAM_TAG_SUP_PICC2PCD) && - (sParam.sParam.value[it + (uint16_t)RFAL_ISODEP_SPARAM_TAG_LEN] == - RFAL_ISODEP_SPARAM_TAG_PICC2PCD_LEN)) { - supPICC2PCD = sParam.sParam.value[it + RFAL_ISODEP_SPARAM_TAG_PICC2PCD_LEN]; - } - } - - /*******************************************************************************/ - /* Check if requested bit rates are supported by PICC */ - if((supPICC2PCD == 0x00U) || (supPCD2PICC == 0x00U)) { - return ERR_PROTO; - } - - for(it = 0; it <= (uint8_t)maxTxBR; it++) { - if((supPCD2PICC & (0x01U << it)) != 0U) { - txBR = (rfalBitRate) - it; /* PRQA S 4342 # MISRA 10.5 - Layout of enum rfalBitRate and above clamping of maxTxBR guarantee no invalid enum values to be created */ - } - } - for(it = 0; it <= (uint8_t)maxRxBR; it++) { - if((supPICC2PCD & (0x01U << it)) != 0U) { - rxBR = (rfalBitRate) - it; /* PRQA S 4342 # MISRA 10.5 - Layout of enum rfalBitRate and above clamping of maxTxBR guarantee no invalid enum values to be created */ - } - } - - it = 0; - currenttxBR = (uint8_t)txBR; - currentrxBR = (uint8_t)rxBR; - - /*******************************************************************************/ - /* Send S(PARAMETERS) - Bit rates Activation */ - sParam.sParam.tag = RFAL_ISODEP_SPARAM_TAG_BLOCKINFO; - sParam.sParam.value[it++] = RFAL_ISODEP_SPARAM_TAG_BRACT; - sParam.sParam.value[it++] = - (RFAL_ISODEP_SPARAM_TVL_HDR_LEN + RFAL_ISODEP_SPARAM_TAG_PCD2PICC_LEN + - RFAL_ISODEP_SPARAM_TVL_HDR_LEN + RFAL_ISODEP_SPARAM_TAG_PICC2PCD_LEN); - sParam.sParam.value[it++] = RFAL_ISODEP_SPARAM_TAG_SEL_PCD2PICC; - sParam.sParam.value[it++] = RFAL_ISODEP_SPARAM_TAG_PCD2PICC_LEN; - sParam.sParam.value[it++] = ((uint8_t)0x01U << currenttxBR); - sParam.sParam.value[it++] = 0x00U; - sParam.sParam.value[it++] = RFAL_ISODEP_SPARAM_TAG_SEL_PICC2PCD; - sParam.sParam.value[it++] = RFAL_ISODEP_SPARAM_TAG_PICC2PCD_LEN; - sParam.sParam.value[it++] = ((uint8_t)0x01U << currentrxBR); - sParam.sParam.value[it++] = 0x00U; - sParam.sParam.length = it; - - EXIT_ON_ERR( - ret, - rfalTransceiveBlockingTxRx( - (uint8_t*)&sParam, - (RFAL_ISODEP_SPARAM_HDR_LEN + (uint16_t)it), - (uint8_t*)&sParam, - sizeof(rfalIsoDepControlMsgSParam), - &rcvLen, - RFAL_TXRX_FLAGS_DEFAULT, - (isoDepDev->info.FWT + isoDepDev->info.dFWT))); - - it = 0; - - /*******************************************************************************/ - /* Check S(PARAMETERS) Acknowledge */ - if((sParam.pcb != ISODEP_PCB_SPARAMETERS) || - (sParam.sParam.tag != RFAL_ISODEP_SPARAM_TAG_BLOCKINFO) || - (sParam.sParam.value[it] != RFAL_ISODEP_SPARAM_TAG_BRACK) || - (rcvLen < RFAL_ISODEP_SPARAM_HDR_LEN)) { - return ERR_PROTO; - } - - EXIT_ON_ERR(ret, rfalSetBitRate(txBR, rxBR)); - - isoDepDev->info.DRI = txBR; - isoDepDev->info.DSI = rxBR; - - return ERR_NONE; -} - -/*******************************************************************************/ -static void rfalIsoDepCalcBitRate( - rfalBitRate maxAllowedBR, - uint8_t piccBRCapability, - rfalBitRate* dsi, - rfalBitRate* dri) { - uint8_t driMask; - uint8_t dsiMask; - int8_t i; - bool bitrateFound; - rfalBitRate curMaxBR; - - curMaxBR = maxAllowedBR; - - do { - bitrateFound = true; - - (*dsi) = RFAL_BR_106; - (*dri) = RFAL_BR_106; - - /* Digital 1.0 5.6.2.5 & 11.6.2.14: A received RFU value of b4 = 1b MUST be interpreted as if b7 to b1 ? 0000000b (only 106 kbits/s in both direction) */ - if(((RFAL_ISODEP_BITRATE_RFU_MASK & piccBRCapability) != 0U) || (curMaxBR > RFAL_BR_848) || - (curMaxBR == RFAL_BR_KEEP)) { - return; - } - - /***************************************************************************/ - /* Determine Listen->Poll bit rate */ - dsiMask = (piccBRCapability & RFAL_ISODEP_BSI_MASK); - for(i = 2; i >= 0; i--) // Check supported bit rate from the highest - { - if(((dsiMask & (0x10U << (uint8_t)i)) != 0U) && - (((uint8_t)i + 1U) <= (uint8_t)curMaxBR)) { - uint8_t newdsi = ((uint8_t)i) + 1U; - (*dsi) = (rfalBitRate) - newdsi; /* PRQA S 4342 # MISRA 10.5 - Layout of enum rfalBitRate and range of loop variable guarantee no invalid enum values to be created */ - break; - } - } - - /***************************************************************************/ - /* Determine Poll->Listen bit rate */ - driMask = (piccBRCapability & RFAL_ISODEP_BRI_MASK); - for(i = 2; i >= 0; i--) /* Check supported bit rate from the highest */ - { - if(((driMask & (0x01U << (uint8_t)i)) != 0U) && - (((uint8_t)i + 1U) <= (uint8_t)curMaxBR)) { - uint8_t newdri = ((uint8_t)i) + 1U; - (*dri) = (rfalBitRate) - newdri; /* PRQA S 4342 # MISRA 10.5 - Layout of enum rfalBitRate and range of loop variable guarantee no invalid enum values to be created */ - break; - } - } - - /***************************************************************************/ - /* Check if different bit rate is supported */ - - /* Digital 1.0 Table 67: if b8=1b, then only the same bit rate divisor for both directions is supported */ - if((piccBRCapability & RFAL_ISODEP_SAME_BITRATE_MASK) != 0U) { - (*dsi) = MIN((*dsi), (*dri)); - (*dri) = (*dsi); - /* Check that the baudrate is supported */ - if((RFAL_BR_106 != (*dsi)) && - (!(((dsiMask & (0x10U << ((uint8_t)(*dsi) - 1U))) != 0U) && - ((driMask & (0x01U << ((uint8_t)(*dri) - 1U))) != 0U)))) { - bitrateFound = false; - curMaxBR = - (*dsi); /* set allowed bitrate to be lowest and determine bit rate again */ - } - } - } while(!(bitrateFound)); -} - -/*******************************************************************************/ -static uint32_t rfalIsoDepSFGI2SFGT(uint8_t sfgi) { - uint32_t sfgt; - uint8_t tmpSFGI; - - tmpSFGI = sfgi; - - if(tmpSFGI > ISODEP_SFGI_MAX) { - tmpSFGI = ISODEP_SFGI_MIN; - } - - if(tmpSFGI != ISODEP_SFGI_MIN) { - /* If sfgi != 0 wait SFGT + dSFGT Digital 1.1 13.8.2.1 */ - sfgt = isoDepCalcSGFT(sfgi) + isoDepCalcdSGFT(sfgi); - } - /* Otherwise use FDTPoll min Digital 1.1 13.8.2.3*/ - else { - sfgt = RFAL_FDT_POLL_NFCA_POLLER; - } - - /* Convert carrier cycles to milli seconds */ - return (rfalConv1fcToMs(sfgt) + 1U); -} - -#endif /* RFAL_FEATURE_ISO_DEP_POLL */ - -/*******************************************************************************/ -static void rfalIsoDepApdu2IBLockParam( - rfalIsoDepApduTxRxParam apduParam, - rfalIsoDepTxRxParam* iBlockParam, - uint16_t txPos, - uint16_t rxPos) { - NO_WARNING(rxPos); /* Keep this param for future use */ - - iBlockParam->DID = apduParam.DID; - iBlockParam->FSx = apduParam.FSx; - iBlockParam->ourFSx = apduParam.ourFSx; - iBlockParam->FWT = apduParam.FWT; - iBlockParam->dFWT = apduParam.dFWT; - - if((apduParam.txBufLen - txPos) > rfalIsoDepGetMaxInfLen()) { - iBlockParam->isTxChaining = true; - iBlockParam->txBufLen = rfalIsoDepGetMaxInfLen(); - } else { - iBlockParam->isTxChaining = false; - iBlockParam->txBufLen = (apduParam.txBufLen - txPos); - } - - /* TxBuf is moved to the beginning for every I-Block */ - iBlockParam->txBuf = - (rfalIsoDepBufFormat*)apduParam - .txBuf; /* PRQA S 0310 # MISRA 11.3 - Intentional safe cast to avoiding large buffer duplication */ - iBlockParam->rxBuf = - apduParam - .tmpBuf; /* Simply using the apdu buffer is not possible because of current ACK handling */ - iBlockParam->isRxChaining = &gIsoDep.isAPDURxChaining; - iBlockParam->rxLen = apduParam.rxLen; -} - -/*******************************************************************************/ -ReturnCode rfalIsoDepStartApduTransceive(rfalIsoDepApduTxRxParam param) { - rfalIsoDepTxRxParam txRxParam; - - /* Initialize and store APDU context */ - gIsoDep.APDUParam = param; - gIsoDep.APDUTxPos = 0; - gIsoDep.APDURxPos = 0; - - /* Assign current FSx to calculate INF length (only change the FSx from activation if no to Keep) */ - gIsoDep.ourFsx = ((param.ourFSx != RFAL_ISODEP_FSX_KEEP) ? param.ourFSx : gIsoDep.ourFsx); - gIsoDep.fsx = param.FSx; - - /* Convert APDU TxRxParams to I-Block TxRxParams */ - rfalIsoDepApdu2IBLockParam( - gIsoDep.APDUParam, &txRxParam, gIsoDep.APDUTxPos, gIsoDep.APDURxPos); - - return rfalIsoDepStartTransceive(txRxParam); -} - -/*******************************************************************************/ -ReturnCode rfalIsoDepGetApduTransceiveStatus(void) { - ReturnCode ret; - rfalIsoDepTxRxParam txRxParam; - - ret = rfalIsoDepGetTransceiveStatus(); - switch(ret) { - /*******************************************************************************/ - case ERR_NONE: - - /* Check if we are still doing chaining on Tx */ - if(gIsoDep.isTxChaining) { - /* Add already Tx bytes */ - gIsoDep.APDUTxPos += gIsoDep.txBufLen; - - /* Convert APDU TxRxParams to I-Block TxRxParams */ - rfalIsoDepApdu2IBLockParam( - gIsoDep.APDUParam, &txRxParam, gIsoDep.APDUTxPos, gIsoDep.APDURxPos); - - if(txRxParam.txBufLen > 0U) /* MISRA 21.18 */ - { - /* Move next I-Block to beginning of APDU Tx buffer */ - ST_MEMCPY( - gIsoDep.APDUParam.txBuf->apdu, - &gIsoDep.APDUParam.txBuf->apdu[gIsoDep.APDUTxPos], - txRxParam.txBufLen); - } - - EXIT_ON_ERR(ret, rfalIsoDepStartTransceive(txRxParam)); - return ERR_BUSY; - } - - /* APDU TxRx is done */ - /* fall through */ - - /*******************************************************************************/ - case ERR_AGAIN: /* PRQA S 2003 # MISRA 16.3 - Intentional fall through */ - - /* Check if no APDU transceive has been started before (data from rfalIsoDepListenStartActivation) */ - if(gIsoDep.APDUParam.rxLen == NULL) { - if(ret == ERR_AGAIN) { - /* In Listen mode first chained packet cannot be retrieved via APDU interface */ - return ERR_NOTSUPP; - } - - /* TxRx is complete and full data is already available */ - return ERR_NONE; - } - - if(*gIsoDep.APDUParam.rxLen > 0U) /* MISRA 21.18 */ - { - /* Ensure that data in tmpBuf still fits into APDU buffer */ - if((gIsoDep.APDURxPos + (*gIsoDep.APDUParam.rxLen)) > - (uint16_t)RFAL_FEATURE_ISO_DEP_APDU_MAX_LEN) { - return ERR_NOMEM; - } - - /* Copy chained packet from tmp buffer to APDU buffer */ - ST_MEMCPY( - &gIsoDep.APDUParam.rxBuf->apdu[gIsoDep.APDURxPos], - gIsoDep.APDUParam.tmpBuf->inf, - *gIsoDep.APDUParam.rxLen); - gIsoDep.APDURxPos += *gIsoDep.APDUParam.rxLen; - } - - /* Update output param rxLen */ - *gIsoDep.APDUParam.rxLen = gIsoDep.APDURxPos * 8; - - /* Wait for following I-Block or APDU TxRx has finished */ - return ((ret == ERR_AGAIN) ? ERR_BUSY : ERR_NONE); - - /*******************************************************************************/ - default: - /* MISRA 16.4: no empty default statement (a comment being enough) */ - break; - } - - return ret; -} - -#endif /* RFAL_FEATURE_ISO_DEP */ diff --git a/lib/ST25RFAL002/source/rfal_nfc.c b/lib/ST25RFAL002/source/rfal_nfc.c deleted file mode 100755 index 9040b7f9d2a..00000000000 --- a/lib/ST25RFAL002/source/rfal_nfc.c +++ /dev/null @@ -1,2118 +0,0 @@ -/** - ****************************************************************************** - * - * COPYRIGHT(c) 2020 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 rfal_nfc.c - * - * \author Gustavo Patricio - * - * \brief RFAL NFC device - * - * This module provides the required features to behave as an NFC Poller - * or Listener device. It grants an easy to use interface for the following - * activities: Technology Detection, Collision Resollution, Activation, - * Data Exchange, and Deactivation - * - * This layer is influenced by (but not fully aligned with) the NFC Forum - * specifications, in particular: Activity 2.0 and NCI 2.0 - * - */ - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "rfal_nfc.h" -#include "utils.h" -#include "rfal_analogConfig.h" - -/* -****************************************************************************** -* GLOBAL DEFINES -****************************************************************************** -*/ -#define RFAL_NFC_MAX_DEVICES 5U /* Max number of devices supported */ - -/* -****************************************************************************** -* GLOBAL MACROS -****************************************************************************** -*/ - -#define rfalNfcNfcNotify(st) \ - if(gNfcDev.disc.notifyCb != NULL) gNfcDev.disc.notifyCb(st) - -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ - -/*! Buffer union, only one interface is used at a time */ -typedef union { /* PRQA S 0750 # MISRA 19.2 - Members of the union will not be used concurrently, only one interface at a time */ - rfalIsoDepBufFormat isoDepBuf; /*!< ISO-DEP buffer format (with header/prologue) */ - rfalNfcDepBufFormat nfcDepBuf; /*!< NFC-DEP buffer format (with header/prologue) */ -} rfalNfcTmpBuffer; - -typedef struct { - rfalNfcState state; /* Main state */ - uint16_t techsFound; /* Technologies found bitmask */ - uint16_t techs2do; /* Technologies still to be performed */ - rfalBitRate ap2pBR; /* Bit rate to poll for AP2P */ - uint8_t selDevIdx; /* Selected device index */ - rfalNfcDevice* activeDev; /* Active device pointer */ - rfalNfcDiscoverParam disc; /* Discovery parameters */ - rfalNfcDevice devList[RFAL_NFC_MAX_DEVICES]; /*!< Location of device list */ - uint8_t devCnt; /* Decices found counter */ - uint32_t discTmr; /* Discovery Total duration timer */ - ReturnCode dataExErr; /* Last Data Exchange error */ - bool discRestart; /* Restart discover after deactivation flag */ - bool isRxChaining; /* Flag indicating Other device is chaining */ - uint32_t lmMask; /* Listen Mode mask */ - bool isTechInit; /* Flag indicating technology has been set */ - bool isOperOngoing; /* Flag indicating opration is ongoing */ - - rfalNfcBuffer txBuf; /* Tx buffer for Data Exchange */ - rfalNfcBuffer rxBuf; /* Rx buffer for Data Exchange */ - uint16_t rxLen; /* Length of received data on Data Exchange */ - -#if RFAL_FEATURE_NFC_DEP || RFAL_FEATURE_ISO_DEP - rfalNfcTmpBuffer tmpBuf; /* Tmp buffer for Data Exchange */ -#endif /* RFAL_FEATURE_NFC_DEP || RFAL_FEATURE_ISO_DEP */ - -} rfalNfc; - -/* - ****************************************************************************** - * LOCAL VARIABLES - ****************************************************************************** - */ -#ifdef RFAL_TEST_MODE -rfalNfc gNfcDev; -#else /* RFAL_TEST_MODE */ -static rfalNfc gNfcDev; -#endif /* RFAL_TEST_MODE */ - -/* -****************************************************************************** -* LOCAL FUNCTION PROTOTYPES -****************************************************************************** -*/ -static ReturnCode rfalNfcPollTechDetetection(void); -static ReturnCode rfalNfcPollCollResolution(void); -static ReturnCode rfalNfcPollActivation(uint8_t devIt); -static ReturnCode rfalNfcDeactivation(void); - -#if RFAL_FEATURE_NFC_DEP -static ReturnCode rfalNfcNfcDepActivate( - rfalNfcDevice* device, - rfalNfcDepCommMode commMode, - const uint8_t* atrReq, - uint16_t atrReqLen); -#endif /* RFAL_FEATURE_NFC_DEP */ - -#if RFAL_FEATURE_LISTEN_MODE -static ReturnCode rfalNfcListenActivation(void); -#endif /* RFAL_FEATURE_LISTEN_MODE*/ - -/*******************************************************************************/ -ReturnCode rfalNfcInitialize(void) { - ReturnCode err; - - gNfcDev.state = RFAL_NFC_STATE_NOTINIT; - - rfalAnalogConfigInitialize(); /* Initialize RFAL's Analog Configs */ - EXIT_ON_ERR(err, rfalInitialize()); /* Initialize RFAL */ - - gNfcDev.state = RFAL_NFC_STATE_IDLE; /* Go to initialized */ - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcDiscover(const rfalNfcDiscoverParam* disParams) { - /* Check if initialization has been performed */ - if(gNfcDev.state != RFAL_NFC_STATE_IDLE) { - return ERR_WRONG_STATE; - } - - /* Check valid parameters */ - if((disParams == NULL) || (disParams->devLimit > RFAL_NFC_MAX_DEVICES) || - (disParams->devLimit == 0U) || - ((disParams->maxBR > RFAL_BR_1695) && (disParams->maxBR != RFAL_BR_KEEP)) || - (((disParams->techs2Find & RFAL_NFC_POLL_TECH_F) != 0U) && - (disParams->nfcfBR != RFAL_BR_212) && (disParams->nfcfBR != RFAL_BR_424)) || - ((((disParams->techs2Find & RFAL_NFC_POLL_TECH_AP2P) != 0U) && - (disParams->ap2pBR > RFAL_BR_424)) || - (disParams->GBLen > RFAL_NFCDEP_GB_MAX_LEN))) { - return ERR_PARAM; - } - - if((((disParams->techs2Find & RFAL_NFC_POLL_TECH_A) != 0U) && !((bool)RFAL_FEATURE_NFCA)) || - (((disParams->techs2Find & RFAL_NFC_POLL_TECH_B) != 0U) && !((bool)RFAL_FEATURE_NFCB)) || - (((disParams->techs2Find & RFAL_NFC_POLL_TECH_F) != 0U) && !((bool)RFAL_FEATURE_NFCF)) || - (((disParams->techs2Find & RFAL_NFC_POLL_TECH_V) != 0U) && !((bool)RFAL_FEATURE_NFCV)) || - (((disParams->techs2Find & RFAL_NFC_POLL_TECH_ST25TB) != 0U) && - !((bool)RFAL_FEATURE_ST25TB)) || - (((disParams->techs2Find & RFAL_NFC_POLL_TECH_AP2P) != 0U) && - !((bool)RFAL_FEATURE_NFC_DEP)) || - (((disParams->techs2Find & RFAL_NFC_LISTEN_TECH_A) != 0U) && !((bool)RFAL_FEATURE_NFCA)) || - (((disParams->techs2Find & RFAL_NFC_LISTEN_TECH_B) != 0U) && !((bool)RFAL_FEATURE_NFCB)) || - (((disParams->techs2Find & RFAL_NFC_LISTEN_TECH_F) != 0U) && !((bool)RFAL_FEATURE_NFCF)) || - (((disParams->techs2Find & RFAL_NFC_LISTEN_TECH_AP2P) != 0U) && - !((bool)RFAL_FEATURE_NFC_DEP))) { - return ERR_DISABLED; /* PRQA S 2880 # MISRA 2.1 - Unreachable code due to configuration option being set/unset */ - } - - /* Initialize context for discovery */ - gNfcDev.activeDev = NULL; - gNfcDev.techsFound = RFAL_NFC_TECH_NONE; - gNfcDev.devCnt = 0; - gNfcDev.discRestart = true; - gNfcDev.isTechInit = false; - gNfcDev.disc = *disParams; - - /* Calculate Listen Mask */ - gNfcDev.lmMask = 0U; - gNfcDev.lmMask |= - (((gNfcDev.disc.techs2Find & RFAL_NFC_LISTEN_TECH_A) != 0U) ? RFAL_LM_MASK_NFCA : 0U); - gNfcDev.lmMask |= - (((gNfcDev.disc.techs2Find & RFAL_NFC_LISTEN_TECH_B) != 0U) ? RFAL_LM_MASK_NFCB : 0U); - gNfcDev.lmMask |= - (((gNfcDev.disc.techs2Find & RFAL_NFC_LISTEN_TECH_F) != 0U) ? RFAL_LM_MASK_NFCF : 0U); - gNfcDev.lmMask |= - (((gNfcDev.disc.techs2Find & RFAL_NFC_LISTEN_TECH_AP2P) != 0U) ? RFAL_LM_MASK_ACTIVE_P2P : - 0U); - -#if !RFAL_FEATURE_LISTEN_MODE - /* Check if Listen Mode is supported/Enabled */ - if(gNfcDev.lmMask != 0U) { - return ERR_DISABLED; - } -#endif - - gNfcDev.state = RFAL_NFC_STATE_START_DISCOVERY; - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcDeactivate(bool discovery) { - /* Check for valid state */ - if(gNfcDev.state <= RFAL_NFC_STATE_IDLE) { - return ERR_WRONG_STATE; - } - - /* Check if discovery is to continue afterwards */ - if((discovery == true) && (gNfcDev.disc.techs2Find != RFAL_NFC_TECH_NONE)) { - /* If so let the state machine continue*/ - gNfcDev.discRestart = discovery; - gNfcDev.state = RFAL_NFC_STATE_DEACTIVATION; - } else { - /* Otherwise deactivate immediately and go to IDLE */ - rfalNfcDeactivation(); - gNfcDev.state = RFAL_NFC_STATE_IDLE; - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcSelect(uint8_t devIdx) { - /* Check for valid state */ - if(gNfcDev.state != RFAL_NFC_STATE_POLL_SELECT) { - return ERR_WRONG_STATE; - } - - gNfcDev.selDevIdx = devIdx; - gNfcDev.state = RFAL_NFC_STATE_POLL_ACTIVATION; - - return ERR_NONE; -} - -/*******************************************************************************/ -rfalNfcState rfalNfcGetState(void) { - return gNfcDev.state; -} - -/*******************************************************************************/ -ReturnCode rfalNfcGetDevicesFound(rfalNfcDevice** devList, uint8_t* devCnt) { - /* Check for valid state */ - if(gNfcDev.state < RFAL_NFC_STATE_POLL_SELECT) { - return ERR_WRONG_STATE; - } - - /* Check valid parameters */ - if((devList == NULL) || (devCnt == NULL)) { - return ERR_PARAM; - } - - *devCnt = gNfcDev.devCnt; - *devList = gNfcDev.devList; - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcGetActiveDevice(rfalNfcDevice** dev) { - /* Check for valid state */ - if(gNfcDev.state < RFAL_NFC_STATE_ACTIVATED) { - return ERR_WRONG_STATE; - } - - /* Check valid parameter */ - if(dev == NULL) { - return ERR_PARAM; - } - - /* Check for valid state */ - if((gNfcDev.devCnt == 0U) || (gNfcDev.activeDev == NULL)) { - return ERR_REQUEST; - } - - *dev = gNfcDev.activeDev; - return ERR_NONE; -} - -/*******************************************************************************/ -void rfalNfcWorker(void) { - ReturnCode err; - - rfalWorker(); /* Execute RFAL process */ - - switch(gNfcDev.state) { - /*******************************************************************************/ - case RFAL_NFC_STATE_NOTINIT: - case RFAL_NFC_STATE_IDLE: - break; - - /*******************************************************************************/ - case RFAL_NFC_STATE_START_DISCOVERY: - - /* Initialize context for discovery cycle */ - gNfcDev.devCnt = 0; - gNfcDev.selDevIdx = 0; - gNfcDev.techsFound = RFAL_NFC_TECH_NONE; - gNfcDev.techs2do = gNfcDev.disc.techs2Find; - gNfcDev.state = RFAL_NFC_STATE_POLL_TECHDETECT; - -#if RFAL_FEATURE_WAKEUP_MODE - /* Check if Low power Wake-Up is to be performed */ - if(gNfcDev.disc.wakeupEnabled) { - /* Initialize Low power Wake-up mode and wait */ - err = rfalWakeUpModeStart( - (gNfcDev.disc.wakeupConfigDefault ? NULL : &gNfcDev.disc.wakeupConfig)); - if(err == ERR_NONE) { - gNfcDev.state = RFAL_NFC_STATE_WAKEUP_MODE; - rfalNfcNfcNotify(gNfcDev.state); /* Notify caller that WU was started */ - } - } -#endif /* RFAL_FEATURE_WAKEUP_MODE */ - break; - - /*******************************************************************************/ - case RFAL_NFC_STATE_WAKEUP_MODE: - -#if RFAL_FEATURE_WAKEUP_MODE - /* Check if the Wake-up mode has woke */ - if(rfalWakeUpModeHasWoke()) { - rfalWakeUpModeStop(); /* Disable Wake-up mode */ - gNfcDev.state = RFAL_NFC_STATE_POLL_TECHDETECT; /* Go to Technology detection */ - - rfalNfcNfcNotify(gNfcDev.state); /* Notify caller that WU has woke */ - } -#endif /* RFAL_FEATURE_WAKEUP_MODE */ - - break; - - /*******************************************************************************/ - case RFAL_NFC_STATE_POLL_TECHDETECT: - - /* Start total duration timer */ - platformTimerDestroy(gNfcDev.discTmr); - gNfcDev.discTmr = (uint32_t)platformTimerCreate(gNfcDev.disc.totalDuration); - - err = - rfalNfcPollTechDetetection(); /* Perform Technology Detection */ - if(err != ERR_BUSY) /* Wait until all technologies are performed */ - { - if((err != ERR_NONE) || - (gNfcDev.techsFound == - RFAL_NFC_TECH_NONE)) /* Check if any error occurred or no techs were found */ - { - rfalFieldOff(); - gNfcDev.state = - RFAL_NFC_STATE_LISTEN_TECHDETECT; /* Nothing found as poller, go to listener */ - break; - } - - gNfcDev.techs2do = - gNfcDev.techsFound; /* Store the found technologies for collision resolution */ - gNfcDev.state = - RFAL_NFC_STATE_POLL_COLAVOIDANCE; /* One or more devices found, go to Collision Avoidance */ - } - break; - - /*******************************************************************************/ - case RFAL_NFC_STATE_POLL_COLAVOIDANCE: - - err = - rfalNfcPollCollResolution(); /* Resolve any eventual collision */ - if(err != ERR_BUSY) /* Wait until all technologies are performed */ - { - if((err != ERR_NONE) || - (gNfcDev.devCnt == 0U)) /* Check if any error occurred or no devices were found */ - { - gNfcDev.state = RFAL_NFC_STATE_DEACTIVATION; - break; /* Unable to retrieve any device, restart loop */ - } - - /* Check if more than one device has been found */ - if(gNfcDev.devCnt > 1U) { - /* If more than one device was found inform upper layer to choose which one to activate */ - if(gNfcDev.disc.notifyCb != NULL) { - gNfcDev.state = RFAL_NFC_STATE_POLL_SELECT; - gNfcDev.disc.notifyCb(gNfcDev.state); - break; - } - } - - /* If only one device or no callback has been set, activate the first device found */ - gNfcDev.selDevIdx = 0U; - gNfcDev.state = RFAL_NFC_STATE_POLL_ACTIVATION; - } - break; - - /*******************************************************************************/ - case RFAL_NFC_STATE_POLL_ACTIVATION: - - err = rfalNfcPollActivation(gNfcDev.selDevIdx); - if(err != ERR_BUSY) /* Wait until all Activation is complete */ - { - if(err != ERR_NONE) /* Activation failed selected device */ - { - gNfcDev.state = - RFAL_NFC_STATE_DEACTIVATION; /* If Activation failed, restart loop */ - break; - } - - gNfcDev.state = RFAL_NFC_STATE_ACTIVATED; /* Device has been properly activated */ - rfalNfcNfcNotify( - gNfcDev.state); /* Inform upper layer that a device has been activated */ - } - break; - - /*******************************************************************************/ - case RFAL_NFC_STATE_DATAEXCHANGE: - - rfalNfcDataExchangeGetStatus(); /* Run the internal state machine */ - - if(gNfcDev.dataExErr != ERR_BUSY) /* If Dataexchange has terminated */ - { - gNfcDev.state = RFAL_NFC_STATE_DATAEXCHANGE_DONE; /* Go to done state */ - rfalNfcNfcNotify(gNfcDev.state); /* And notify caller */ - } - if(gNfcDev.dataExErr == ERR_SLEEP_REQ) /* Check if Listen mode has to go to Sleep */ - { - gNfcDev.state = RFAL_NFC_STATE_LISTEN_SLEEP; /* Go to Listen Sleep state */ - rfalNfcNfcNotify(gNfcDev.state); /* And notify caller */ - } - break; - - /*******************************************************************************/ - case RFAL_NFC_STATE_DEACTIVATION: - - rfalNfcDeactivation(); /* Deactivate current device */ - - gNfcDev.state = - ((gNfcDev.discRestart) ? RFAL_NFC_STATE_START_DISCOVERY : RFAL_NFC_STATE_IDLE); - rfalNfcNfcNotify(gNfcDev.state); /* Notify caller */ - break; - - /*******************************************************************************/ - case RFAL_NFC_STATE_LISTEN_TECHDETECT: - - if(platformTimerIsExpired(gNfcDev.discTmr)) { -#if RFAL_FEATURE_LISTEN_MODE - rfalListenStop(); -#else - rfalFieldOff(); -#endif /* RFAL_FEATURE_LISTEN_MODE */ - - gNfcDev.state = RFAL_NFC_STATE_START_DISCOVERY; /* Restart the discovery loop */ - rfalNfcNfcNotify(gNfcDev.state); /* Notify caller */ - break; - } - -#if RFAL_FEATURE_LISTEN_MODE - - if(gNfcDev.lmMask != 0U) /* Check if configured to perform Listen mode */ - { - err = rfalListenStart( - gNfcDev.lmMask, - &gNfcDev.disc.lmConfigPA, - NULL, - &gNfcDev.disc.lmConfigPF, - (uint8_t*)&gNfcDev.rxBuf.rfBuf, - (uint16_t)rfalConvBytesToBits(sizeof(gNfcDev.rxBuf.rfBuf)), - &gNfcDev.rxLen); - if(err == ERR_NONE) { - gNfcDev.state = - RFAL_NFC_STATE_LISTEN_COLAVOIDANCE; /* Wait for listen mode to be activated */ - } - } - break; - - /*******************************************************************************/ - case RFAL_NFC_STATE_LISTEN_COLAVOIDANCE: - - if(platformTimerIsExpired( - gNfcDev.discTmr)) /* Check if the total duration has been reached */ - { - rfalListenStop(); - gNfcDev.state = RFAL_NFC_STATE_START_DISCOVERY; /* Restart the discovery loop */ - rfalNfcNfcNotify(gNfcDev.state); /* Notify caller */ - break; - } - - /* Check for external field */ - if(rfalListenGetState(NULL, NULL) >= RFAL_LM_STATE_IDLE) { - gNfcDev.state = - RFAL_NFC_STATE_LISTEN_ACTIVATION; /* Wait for listen mode to be activated */ - } - break; - - /*******************************************************************************/ - case RFAL_NFC_STATE_LISTEN_ACTIVATION: - case RFAL_NFC_STATE_LISTEN_SLEEP: - - err = rfalNfcListenActivation(); - if(err != ERR_BUSY) { - if(err == ERR_NONE) { - gNfcDev.activeDev = - gNfcDev.devList; /* Assign the active device to be used further on */ - gNfcDev.devCnt++; - - gNfcDev.state = RFAL_NFC_STATE_ACTIVATED; /* Device has been properly activated */ - rfalNfcNfcNotify( - gNfcDev.state); /* Inform upper layer that a device has been activated */ - } else { - rfalListenStop(); - gNfcDev.state = RFAL_NFC_STATE_START_DISCOVERY; /* Restart the discovery loop */ - rfalNfcNfcNotify(gNfcDev.state); /* Notify caller */ - } - } -#endif /* RFAL_FEATURE_LISTEN_MODE */ - break; - - /*******************************************************************************/ - case RFAL_NFC_STATE_ACTIVATED: - case RFAL_NFC_STATE_POLL_SELECT: - case RFAL_NFC_STATE_DATAEXCHANGE_DONE: - default: - return; - } -} - -/*******************************************************************************/ -ReturnCode rfalNfcDataExchangeStart( - uint8_t* txData, - uint16_t txDataLen, - uint8_t** rxData, - uint16_t** rvdLen, - uint32_t fwt, - uint32_t flags) { - ReturnCode err; - rfalTransceiveContext ctx; - - /*******************************************************************************/ - /* The Data Exchange is divided in two different moments, the trigger/Start of * - * the transfer followed by the check until its completion */ - if((gNfcDev.state >= RFAL_NFC_STATE_ACTIVATED) && (gNfcDev.activeDev != NULL)) { - /*******************************************************************************/ - /* In Listen mode is the Poller that initiates the communicatation */ - /* Assign output parameters and rfalNfcDataExchangeGetStatus will return */ - /* incoming data from Poller/Initiator */ - if((gNfcDev.state == RFAL_NFC_STATE_ACTIVATED) && - rfalNfcIsRemDevPoller(gNfcDev.activeDev->type)) { - if(txDataLen > 0U) { - return ERR_WRONG_STATE; - } - - *rvdLen = (uint16_t*)&gNfcDev.rxLen; - *rxData = (uint8_t*)( (gNfcDev.activeDev->rfInterface == RFAL_NFC_INTERFACE_ISODEP) ? gNfcDev.rxBuf.isoDepBuf.apdu : - ((gNfcDev.activeDev->rfInterface == RFAL_NFC_INTERFACE_NFCDEP) ? gNfcDev.rxBuf.nfcDepBuf.pdu : gNfcDev.rxBuf.rfBuf)); - if(gNfcDev.disc.activate_after_sak) { - gNfcDev.state = RFAL_NFC_STATE_DATAEXCHANGE_DONE; - } - return ERR_NONE; - } - - /*******************************************************************************/ - switch(gNfcDev.activeDev - ->rfInterface) /* Check which RF interface shall be used/has been activated */ - { - /*******************************************************************************/ - case RFAL_NFC_INTERFACE_RF: - - rfalCreateByteFlagsTxRxContext( - ctx, - (uint8_t*)txData, - txDataLen, - gNfcDev.rxBuf.rfBuf, - sizeof(gNfcDev.rxBuf.rfBuf), - &gNfcDev.rxLen, - flags, - fwt); - if(flags == RFAL_TXRX_FLAGS_RAW) { - ctx.txBufLen = txDataLen; - } - *rxData = (uint8_t*)gNfcDev.rxBuf.rfBuf; - *rvdLen = (uint16_t*)&gNfcDev.rxLen; - err = rfalStartTransceive(&ctx); - break; - -#if RFAL_FEATURE_ISO_DEP - /*******************************************************************************/ - case RFAL_NFC_INTERFACE_ISODEP: { - rfalIsoDepApduTxRxParam isoDepTxRx; - - if(txDataLen > sizeof(gNfcDev.txBuf.isoDepBuf.apdu)) { - return ERR_NOMEM; - } - - if(txDataLen > 0U) { - ST_MEMCPY((uint8_t*)gNfcDev.txBuf.isoDepBuf.apdu, txData, txDataLen); - } - - isoDepTxRx.DID = RFAL_ISODEP_NO_DID; - isoDepTxRx.ourFSx = RFAL_ISODEP_FSX_KEEP; - isoDepTxRx.FSx = gNfcDev.activeDev->proto.isoDep.info.FSx; - isoDepTxRx.dFWT = gNfcDev.activeDev->proto.isoDep.info.dFWT; - isoDepTxRx.FWT = gNfcDev.activeDev->proto.isoDep.info.FWT; - isoDepTxRx.txBuf = &gNfcDev.txBuf.isoDepBuf; - isoDepTxRx.txBufLen = txDataLen; - isoDepTxRx.rxBuf = &gNfcDev.rxBuf.isoDepBuf; - isoDepTxRx.rxLen = &gNfcDev.rxLen; - isoDepTxRx.tmpBuf = &gNfcDev.tmpBuf.isoDepBuf; - *rxData = (uint8_t*)gNfcDev.rxBuf.isoDepBuf.apdu; - *rvdLen = (uint16_t*)&gNfcDev.rxLen; - - /*******************************************************************************/ - /* Trigger a RFAL ISO-DEP Transceive */ - err = rfalIsoDepStartApduTransceive(isoDepTxRx); - break; - } -#endif /* RFAL_FEATURE_ISO_DEP */ - -#if RFAL_FEATURE_NFC_DEP - /*******************************************************************************/ - case RFAL_NFC_INTERFACE_NFCDEP: { - rfalNfcDepPduTxRxParam nfcDepTxRx; - - if(txDataLen > sizeof(gNfcDev.txBuf.nfcDepBuf.pdu)) { - return ERR_NOMEM; - } - - if(txDataLen > 0U) { - ST_MEMCPY((uint8_t*)gNfcDev.txBuf.nfcDepBuf.pdu, txData, txDataLen); - } - - nfcDepTxRx.DID = RFAL_NFCDEP_DID_KEEP; - nfcDepTxRx.FSx = - rfalNfcIsRemDevListener(gNfcDev.activeDev->type) ? - rfalNfcDepLR2FS((uint8_t)rfalNfcDepPP2LR( - gNfcDev.activeDev->proto.nfcDep.activation.Target.ATR_RES.PPt)) : - rfalNfcDepLR2FS((uint8_t)rfalNfcDepPP2LR( - gNfcDev.activeDev->proto.nfcDep.activation.Initiator.ATR_REQ.PPi)); - nfcDepTxRx.dFWT = gNfcDev.activeDev->proto.nfcDep.info.dFWT; - nfcDepTxRx.FWT = gNfcDev.activeDev->proto.nfcDep.info.FWT; - nfcDepTxRx.txBuf = &gNfcDev.txBuf.nfcDepBuf; - nfcDepTxRx.txBufLen = txDataLen; - nfcDepTxRx.rxBuf = &gNfcDev.rxBuf.nfcDepBuf; - nfcDepTxRx.rxLen = &gNfcDev.rxLen; - nfcDepTxRx.tmpBuf = &gNfcDev.tmpBuf.nfcDepBuf; - *rxData = (uint8_t*)gNfcDev.rxBuf.nfcDepBuf.pdu; - *rvdLen = (uint16_t*)&gNfcDev.rxLen; - - /*******************************************************************************/ - /* Trigger a RFAL NFC-DEP Transceive */ - err = rfalNfcDepStartPduTransceive(nfcDepTxRx); - break; - } -#endif /* RFAL_FEATURE_NFC_DEP */ - - /*******************************************************************************/ - default: - err = ERR_PARAM; - break; - } - - /* If a transceive has succesfully started flag Data Exchange as ongoing */ - if(err == ERR_NONE) { - gNfcDev.dataExErr = ERR_BUSY; - gNfcDev.state = RFAL_NFC_STATE_DATAEXCHANGE; - } - - return err; - } - - return ERR_WRONG_STATE; -} - -ReturnCode rfalNfcDataExchangeCustomStart( - uint8_t* txData, - uint16_t txDataLen, - uint8_t** rxData, - uint16_t** rvdLen, - uint32_t fwt, - uint32_t flags) { - ReturnCode err; - rfalTransceiveContext ctx; - - /*******************************************************************************/ - /* The Data Exchange is divided in two different moments, the trigger/Start of * - * the transfer followed by the check until its completion */ - if((gNfcDev.state >= RFAL_NFC_STATE_ACTIVATED) && (gNfcDev.activeDev != NULL)) { - /*******************************************************************************/ - /* In Listen mode is the Poller that initiates the communicatation */ - /* Assign output parameters and rfalNfcDataExchangeGetStatus will return */ - /* incoming data from Poller/Initiator */ - if((gNfcDev.state == RFAL_NFC_STATE_ACTIVATED) && - rfalNfcIsRemDevPoller(gNfcDev.activeDev->type)) { - if(txDataLen > 0U) { - return ERR_WRONG_STATE; - } - - *rvdLen = (uint16_t*)&gNfcDev.rxLen; - *rxData = (uint8_t*)( (gNfcDev.activeDev->rfInterface == RFAL_NFC_INTERFACE_ISODEP) ? gNfcDev.rxBuf.isoDepBuf.apdu : - ((gNfcDev.activeDev->rfInterface == RFAL_NFC_INTERFACE_NFCDEP) ? gNfcDev.rxBuf.nfcDepBuf.pdu : gNfcDev.rxBuf.rfBuf)); - if(gNfcDev.disc.activate_after_sak) { - gNfcDev.state = RFAL_NFC_STATE_DATAEXCHANGE_DONE; - } - return ERR_NONE; - } - - /*******************************************************************************/ - switch(gNfcDev.activeDev - ->rfInterface) /* Check which RF interface shall be used/has been activated */ - { - /*******************************************************************************/ - case RFAL_NFC_INTERFACE_RF: - ctx.rxBuf = gNfcDev.rxBuf.rfBuf; - ctx.rxBufLen = 8 * sizeof(gNfcDev.rxBuf.rfBuf); - ctx.rxRcvdLen = &gNfcDev.rxLen; - ctx.txBuf = txData; - ctx.txBufLen = txDataLen; - ctx.flags = flags; - ctx.fwt = fwt; - *rxData = (uint8_t*)gNfcDev.rxBuf.rfBuf; - *rvdLen = (uint16_t*)&gNfcDev.rxLen; - err = rfalStartTransceive(&ctx); - break; - -#if RFAL_FEATURE_ISO_DEP - /*******************************************************************************/ - case RFAL_NFC_INTERFACE_ISODEP: { - rfalIsoDepApduTxRxParam isoDepTxRx; - uint16_t tx_bytes = txDataLen / 8; - - if(tx_bytes > sizeof(gNfcDev.txBuf.isoDepBuf.apdu)) { - return ERR_NOMEM; - } - - if(tx_bytes > 0U) { - ST_MEMCPY((uint8_t*)gNfcDev.txBuf.isoDepBuf.apdu, txData, tx_bytes); - } - - isoDepTxRx.DID = RFAL_ISODEP_NO_DID; - isoDepTxRx.ourFSx = RFAL_ISODEP_FSX_KEEP; - isoDepTxRx.FSx = gNfcDev.activeDev->proto.isoDep.info.FSx; - isoDepTxRx.dFWT = gNfcDev.activeDev->proto.isoDep.info.dFWT; - isoDepTxRx.FWT = gNfcDev.activeDev->proto.isoDep.info.FWT; - isoDepTxRx.txBuf = &gNfcDev.txBuf.isoDepBuf; - isoDepTxRx.txBufLen = tx_bytes; - isoDepTxRx.rxBuf = &gNfcDev.rxBuf.isoDepBuf; - isoDepTxRx.rxLen = &gNfcDev.rxLen; - isoDepTxRx.tmpBuf = &gNfcDev.tmpBuf.isoDepBuf; - *rxData = (uint8_t*)gNfcDev.rxBuf.isoDepBuf.apdu; - *rvdLen = (uint16_t*)&gNfcDev.rxLen; - - /*******************************************************************************/ - /* Trigger a RFAL ISO-DEP Transceive */ - err = rfalIsoDepStartApduTransceive(isoDepTxRx); - break; - } -#endif /* RFAL_FEATURE_ISO_DEP */ - -#if RFAL_FEATURE_NFC_DEP - /*******************************************************************************/ - case RFAL_NFC_INTERFACE_NFCDEP: { - rfalNfcDepPduTxRxParam nfcDepTxRx; - - if(txDataLen > sizeof(gNfcDev.txBuf.nfcDepBuf.pdu)) { - return ERR_NOMEM; - } - - if(txDataLen > 0U) { - ST_MEMCPY((uint8_t*)gNfcDev.txBuf.nfcDepBuf.pdu, txData, txDataLen); - } - - nfcDepTxRx.DID = RFAL_NFCDEP_DID_KEEP; - nfcDepTxRx.FSx = - rfalNfcIsRemDevListener(gNfcDev.activeDev->type) ? - rfalNfcDepLR2FS((uint8_t)rfalNfcDepPP2LR( - gNfcDev.activeDev->proto.nfcDep.activation.Target.ATR_RES.PPt)) : - rfalNfcDepLR2FS((uint8_t)rfalNfcDepPP2LR( - gNfcDev.activeDev->proto.nfcDep.activation.Initiator.ATR_REQ.PPi)); - nfcDepTxRx.dFWT = gNfcDev.activeDev->proto.nfcDep.info.dFWT; - nfcDepTxRx.FWT = gNfcDev.activeDev->proto.nfcDep.info.FWT; - nfcDepTxRx.txBuf = &gNfcDev.txBuf.nfcDepBuf; - nfcDepTxRx.txBufLen = txDataLen; - nfcDepTxRx.rxBuf = &gNfcDev.rxBuf.nfcDepBuf; - nfcDepTxRx.rxLen = &gNfcDev.rxLen; - nfcDepTxRx.tmpBuf = &gNfcDev.tmpBuf.nfcDepBuf; - *rxData = (uint8_t*)gNfcDev.rxBuf.nfcDepBuf.pdu; - *rvdLen = (uint16_t*)&gNfcDev.rxLen; - - /*******************************************************************************/ - /* Trigger a RFAL NFC-DEP Transceive */ - err = rfalNfcDepStartPduTransceive(nfcDepTxRx); - break; - } -#endif /* RFAL_FEATURE_NFC_DEP */ - - /*******************************************************************************/ - default: - err = ERR_PARAM; - break; - } - - /* If a transceive has succesfully started flag Data Exchange as ongoing */ - if(err == ERR_NONE) { - gNfcDev.dataExErr = ERR_BUSY; - gNfcDev.state = RFAL_NFC_STATE_DATAEXCHANGE; - } - - return err; - } - - return ERR_WRONG_STATE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcDataExchangeGetStatus(void) { - /*******************************************************************************/ - /* Check if it's the first frame received in Listen mode */ - if(gNfcDev.state == RFAL_NFC_STATE_ACTIVATED) { - /* Continue data exchange as normal */ - gNfcDev.dataExErr = ERR_BUSY; - gNfcDev.state = RFAL_NFC_STATE_DATAEXCHANGE; - - /* Check if we performing in T3T CE */ - if((gNfcDev.activeDev->type == RFAL_NFC_POLL_TYPE_NFCF) && - (gNfcDev.activeDev->rfInterface == RFAL_NFC_INTERFACE_RF)) { - /* The first frame has been retrieved by rfalListenMode, flag data immediately */ - /* Can only call rfalGetTransceiveStatus() after starting a transceive with rfalStartTransceive */ - gNfcDev.dataExErr = ERR_NONE; - } - } - - /*******************************************************************************/ - /* Check if we are in we have been placed to sleep, and return last error */ - if(gNfcDev.state == RFAL_NFC_STATE_LISTEN_SLEEP) { - return gNfcDev.dataExErr; /* ERR_SLEEP_REQ */ - } - - /*******************************************************************************/ - /* Check if Data exchange has been started */ - if((gNfcDev.state != RFAL_NFC_STATE_DATAEXCHANGE) && - (gNfcDev.state != RFAL_NFC_STATE_DATAEXCHANGE_DONE)) { - return ERR_WRONG_STATE; - } - - /* Check if Data exchange is still ongoing */ - if(gNfcDev.dataExErr == ERR_BUSY) { - switch(gNfcDev.activeDev->rfInterface) { - /*******************************************************************************/ - case RFAL_NFC_INTERFACE_RF: - gNfcDev.dataExErr = rfalGetTransceiveStatus(); - break; - -#if RFAL_FEATURE_ISO_DEP - /*******************************************************************************/ - case RFAL_NFC_INTERFACE_ISODEP: - gNfcDev.dataExErr = rfalIsoDepGetApduTransceiveStatus(); - break; -#endif /* RFAL_FEATURE_ISO_DEP */ - - /*******************************************************************************/ -#if RFAL_FEATURE_NFC_DEP - case RFAL_NFC_INTERFACE_NFCDEP: - gNfcDev.dataExErr = rfalNfcDepGetPduTransceiveStatus(); - break; -#endif /* RFAL_FEATURE_NFC_DEP */ - - /*******************************************************************************/ - default: - gNfcDev.dataExErr = ERR_PARAM; - break; - } - -#if RFAL_FEATURE_LISTEN_MODE - /*******************************************************************************/ - /* If a Sleep request has been received (Listen Mode) go to sleep immediately */ - if(gNfcDev.dataExErr == ERR_SLEEP_REQ) { - EXIT_ON_ERR( - gNfcDev.dataExErr, - rfalListenSleepStart( - RFAL_LM_STATE_SLEEP_A, - gNfcDev.rxBuf.rfBuf, - sizeof(gNfcDev.rxBuf.rfBuf), - &gNfcDev.rxLen)); - - /* If set Sleep was succesfull keep restore the Sleep request signal */ - gNfcDev.dataExErr = ERR_SLEEP_REQ; - } -#endif /* RFAL_FEATURE_LISTEN_MODE */ - } - - return gNfcDev.dataExErr; -} - -/*! - ****************************************************************************** - * \brief Poller Technology Detection - * - * This method implements the Technology Detection / Poll for different - * device technologies. - * - * \return ERR_NONE : Operation completed with no error - * \return ERR_BUSY : Operation ongoing - * \return ERR_XXXX : Error occurred - * - ****************************************************************************** - */ -static ReturnCode rfalNfcPollTechDetetection(void) { - ReturnCode err; - - err = ERR_NONE; - - /* Supress warning when specific RFAL features have been disabled */ - NO_WARNING(err); - - /*******************************************************************************/ - /* AP2P Technology Detection */ - /*******************************************************************************/ - if(((gNfcDev.disc.techs2Find & RFAL_NFC_POLL_TECH_AP2P) != 0U) && - ((gNfcDev.techs2do & RFAL_NFC_POLL_TECH_AP2P) != 0U)) { -#if RFAL_FEATURE_NFC_DEP - - if(!gNfcDev.isTechInit) { - EXIT_ON_ERR( - err, - rfalSetMode(RFAL_MODE_POLL_ACTIVE_P2P, gNfcDev.disc.ap2pBR, gNfcDev.disc.ap2pBR)); - rfalSetErrorHandling(RFAL_ERRORHANDLING_NFC); - rfalSetFDTListen(RFAL_FDT_LISTEN_AP2P_POLLER); - rfalSetFDTPoll(RFAL_TIMING_NONE); - rfalSetGT(RFAL_GT_AP2P_ADJUSTED); - EXIT_ON_ERR(err, rfalFieldOnAndStartGT()); /* Turns the Field On and starts GT timer */ - gNfcDev.isTechInit = true; - } - - if(rfalIsGTExpired()) /* Wait until Guard Time is fulfilled */ - { - gNfcDev.techs2do &= ~RFAL_NFC_POLL_TECH_AP2P; - - err = rfalNfcNfcDepActivate( - gNfcDev.devList, RFAL_NFCDEP_COMM_ACTIVE, NULL, 0); /* Poll for NFC-A devices */ - if(err == ERR_NONE) { - gNfcDev.techsFound |= RFAL_NFC_POLL_TECH_AP2P; - - gNfcDev.devList->type = RFAL_NFC_LISTEN_TYPE_AP2P; - gNfcDev.devList->rfInterface = RFAL_NFC_INTERFACE_NFCDEP; - gNfcDev.devCnt++; - - return ERR_NONE; - } - - gNfcDev.isTechInit = false; - rfalFieldOff(); - } - return ERR_BUSY; - -#endif /* RFAL_FEATURE_NFC_DEP */ - } - - /*******************************************************************************/ - /* Passive NFC-A Technology Detection */ - /*******************************************************************************/ - if(((gNfcDev.disc.techs2Find & RFAL_NFC_POLL_TECH_A) != 0U) && - ((gNfcDev.techs2do & RFAL_NFC_POLL_TECH_A) != 0U)) { -#if RFAL_FEATURE_NFCA - - rfalNfcaSensRes sensRes; - - if(!gNfcDev.isTechInit) { - EXIT_ON_ERR(err, rfalNfcaPollerInitialize()); /* Initialize RFAL for NFC-A */ - EXIT_ON_ERR(err, rfalFieldOnAndStartGT()); /* Turns the Field On and starts GT timer */ - gNfcDev.isTechInit = true; - } - - if(rfalIsGTExpired()) /* Wait until Guard Time is fulfilled */ - { - err = rfalNfcaPollerTechnologyDetection( - gNfcDev.disc.compMode, &sensRes); /* Poll for NFC-A devices */ - if(err == ERR_NONE) { - gNfcDev.techsFound |= RFAL_NFC_POLL_TECH_A; - } - - gNfcDev.isTechInit = false; - gNfcDev.techs2do &= ~RFAL_NFC_POLL_TECH_A; - } - - return ERR_BUSY; - -#endif /* RFAL_FEATURE_NFCA */ - } - - /*******************************************************************************/ - /* Passive NFC-B Technology Detection */ - /*******************************************************************************/ - if(((gNfcDev.disc.techs2Find & RFAL_NFC_POLL_TECH_B) != 0U) && - ((gNfcDev.techs2do & RFAL_NFC_POLL_TECH_B) != 0U)) { -#if RFAL_FEATURE_NFCB - - rfalNfcbSensbRes sensbRes; - uint8_t sensbResLen; - - if(!gNfcDev.isTechInit) { - EXIT_ON_ERR(err, rfalNfcbPollerInitialize()); /* Initialize RFAL for NFC-B */ - EXIT_ON_ERR( - err, rfalFieldOnAndStartGT()); /* As field is already On only starts GT timer */ - gNfcDev.isTechInit = true; - } - - if(rfalIsGTExpired()) /* Wait until Guard Time is fulfilled */ - { - err = rfalNfcbPollerTechnologyDetection( - gNfcDev.disc.compMode, &sensbRes, &sensbResLen); /* Poll for NFC-B devices */ - if(err == ERR_NONE) { - gNfcDev.techsFound |= RFAL_NFC_POLL_TECH_B; - } - - gNfcDev.isTechInit = false; - gNfcDev.techs2do &= ~RFAL_NFC_POLL_TECH_B; - } - - return ERR_BUSY; - -#endif /* RFAL_FEATURE_NFCB */ - } - - /*******************************************************************************/ - /* Passive NFC-F Technology Detection */ - /*******************************************************************************/ - if(((gNfcDev.disc.techs2Find & RFAL_NFC_POLL_TECH_F) != 0U) && - ((gNfcDev.techs2do & RFAL_NFC_POLL_TECH_F) != 0U)) { -#if RFAL_FEATURE_NFCF - - if(!gNfcDev.isTechInit) { - EXIT_ON_ERR( - err, - rfalNfcfPollerInitialize(gNfcDev.disc.nfcfBR)); /* Initialize RFAL for NFC-F */ - EXIT_ON_ERR( - err, rfalFieldOnAndStartGT()); /* As field is already On only starts GT timer */ - gNfcDev.isTechInit = true; - } - - if(rfalIsGTExpired()) /* Wait until Guard Time is fulfilled */ - { - err = rfalNfcfPollerCheckPresence(); /* Poll for NFC-F devices */ - if(err == ERR_NONE) { - gNfcDev.techsFound |= RFAL_NFC_POLL_TECH_F; - } - - gNfcDev.isTechInit = false; - gNfcDev.techs2do &= ~RFAL_NFC_POLL_TECH_F; - } - - return ERR_BUSY; - -#endif /* RFAL_FEATURE_NFCF */ - } - - /*******************************************************************************/ - /* Passive NFC-V Technology Detection */ - /*******************************************************************************/ - if(((gNfcDev.disc.techs2Find & RFAL_NFC_POLL_TECH_V) != 0U) && - ((gNfcDev.techs2do & RFAL_NFC_POLL_TECH_V) != 0U)) { -#if RFAL_FEATURE_NFCV - - rfalNfcvInventoryRes invRes; - - if(!gNfcDev.isTechInit) { - EXIT_ON_ERR(err, rfalNfcvPollerInitialize()); /* Initialize RFAL for NFC-V */ - EXIT_ON_ERR( - err, rfalFieldOnAndStartGT()); /* As field is already On only starts GT timer */ - gNfcDev.isTechInit = true; - } - - if(rfalIsGTExpired()) /* Wait until Guard Time is fulfilled */ - { - err = rfalNfcvPollerCheckPresence(&invRes); /* Poll for NFC-V devices */ - if(err == ERR_NONE) { - gNfcDev.techsFound |= RFAL_NFC_POLL_TECH_V; - } - - gNfcDev.isTechInit = false; - gNfcDev.techs2do &= ~RFAL_NFC_POLL_TECH_V; - } - - return ERR_BUSY; - -#endif /* RFAL_FEATURE_NFCV */ - } - - /*******************************************************************************/ - /* Passive Proprietary Technology ST25TB */ - /*******************************************************************************/ - if(((gNfcDev.disc.techs2Find & RFAL_NFC_POLL_TECH_ST25TB) != 0U) && - ((gNfcDev.techs2do & RFAL_NFC_POLL_TECH_ST25TB) != 0U)) { -#if RFAL_FEATURE_ST25TB - - if(!gNfcDev.isTechInit) { - EXIT_ON_ERR(err, rfalSt25tbPollerInitialize()); /* Initialize RFAL for NFC-V */ - EXIT_ON_ERR( - err, rfalFieldOnAndStartGT()); /* As field is already On only starts GT timer */ - gNfcDev.isTechInit = true; - } - - if(rfalIsGTExpired()) /* Wait until Guard Time is fulfilled */ - { - err = rfalSt25tbPollerCheckPresence(NULL); /* Poll for ST25TB devices */ - if(err == ERR_NONE) { - gNfcDev.techsFound |= RFAL_NFC_POLL_TECH_ST25TB; - } - - gNfcDev.isTechInit = false; - gNfcDev.techs2do &= ~RFAL_NFC_POLL_TECH_ST25TB; - } - - return ERR_BUSY; - -#endif /* RFAL_FEATURE_ST25TB */ - } - - return ERR_NONE; -} - -/*! - ****************************************************************************** - * \brief Poller Collision Resolution - * - * This method implements the Collision Resolution on all technologies that - * have been detected before. - * - * \return ERR_NONE : Operation completed with no error - * \return ERR_BUSY : Operation ongoing - * \return ERR_XXXX : Error occurred - * - ****************************************************************************** - */ -static ReturnCode rfalNfcPollCollResolution(void) { - uint8_t i; - static uint8_t devCnt; - ReturnCode err; - - err = ERR_NONE; - i = 0; - - /* Supress warning when specific RFAL features have been disabled */ - NO_WARNING(err); - NO_WARNING(devCnt); - NO_WARNING(i); - - /* Check if device limit has been reached */ - if(gNfcDev.devCnt >= gNfcDev.disc.devLimit) { - return ERR_NONE; - } - - /*******************************************************************************/ - /* NFC-A Collision Resolution */ - /*******************************************************************************/ -#if RFAL_FEATURE_NFCA - if(((gNfcDev.techsFound & RFAL_NFC_POLL_TECH_A) != 0U) && - ((gNfcDev.techs2do & RFAL_NFC_POLL_TECH_A) != - 0U)) /* If a NFC-A device was found/detected, perform Collision Resolution */ - { - static rfalNfcaListenDevice nfcaDevList[RFAL_NFC_MAX_DEVICES]; - - if(!gNfcDev.isTechInit) { - EXIT_ON_ERR(err, rfalNfcaPollerInitialize()); /* Initialize RFAL for NFC-A */ - EXIT_ON_ERR(err, rfalFieldOnAndStartGT()); /* Turns the Field On and starts GT timer */ - - gNfcDev.isTechInit = true; /* Technology has been initialized */ - gNfcDev.isOperOngoing = false; /* No operation currently ongoing */ - } - - if(!rfalIsGTExpired()) { - return ERR_BUSY; - } - - if(!gNfcDev.isOperOngoing) { - EXIT_ON_ERR( - err, - rfalNfcaPollerStartFullCollisionResolution( - gNfcDev.disc.compMode, - (gNfcDev.disc.devLimit - gNfcDev.devCnt), - nfcaDevList, - &devCnt)); - - gNfcDev.isOperOngoing = true; - return ERR_BUSY; - } - - err = rfalNfcaPollerGetFullCollisionResolutionStatus(); - if(err != ERR_BUSY) { - gNfcDev.isTechInit = false; - gNfcDev.techs2do &= ~RFAL_NFC_POLL_TECH_A; - - if((err == ERR_NONE) && (devCnt != 0U)) { - for(i = 0; i < devCnt; - i++) /* Copy devices found form local Nfca list into global device list */ - { - gNfcDev.devList[gNfcDev.devCnt].type = RFAL_NFC_LISTEN_TYPE_NFCA; - gNfcDev.devList[gNfcDev.devCnt].dev.nfca = nfcaDevList[i]; - gNfcDev.devCnt++; - } - } - } - - return ERR_BUSY; - } -#endif /* RFAL_FEATURE_NFCA */ - - /*******************************************************************************/ - /* NFC-B Collision Resolution */ - /*******************************************************************************/ -#if RFAL_FEATURE_NFCB - if(((gNfcDev.techsFound & RFAL_NFC_POLL_TECH_B) != 0U) && - ((gNfcDev.techs2do & RFAL_NFC_POLL_TECH_B) != - 0U)) /* If a NFC-B device was found/detected, perform Collision Resolution */ - { - rfalNfcbListenDevice nfcbDevList[RFAL_NFC_MAX_DEVICES]; - - if(!gNfcDev.isTechInit) { - EXIT_ON_ERR(err, rfalNfcbPollerInitialize()); /* Initialize RFAL for NFC-B */ - EXIT_ON_ERR( - err, - rfalFieldOnAndStartGT()); /* Ensure GT again as other technologies have also been polled */ - gNfcDev.isTechInit = true; - } - - if(!rfalIsGTExpired()) { - return ERR_BUSY; - } - - devCnt = 0; - gNfcDev.isTechInit = false; - gNfcDev.techs2do &= ~RFAL_NFC_POLL_TECH_B; - - err = rfalNfcbPollerCollisionResolution( - gNfcDev.disc.compMode, (gNfcDev.disc.devLimit - gNfcDev.devCnt), nfcbDevList, &devCnt); - if((err == ERR_NONE) && (devCnt != 0U)) { - for(i = 0; i < devCnt; - i++) /* Copy devices found form local Nfcb list into global device list */ - { - gNfcDev.devList[gNfcDev.devCnt].type = RFAL_NFC_LISTEN_TYPE_NFCB; - gNfcDev.devList[gNfcDev.devCnt].dev.nfcb = nfcbDevList[i]; - gNfcDev.devCnt++; - } - } - - return ERR_BUSY; - } -#endif /* RFAL_FEATURE_NFCB*/ - - /*******************************************************************************/ - /* NFC-F Collision Resolution */ - /*******************************************************************************/ -#if RFAL_FEATURE_NFCF - if(((gNfcDev.techsFound & RFAL_NFC_POLL_TECH_F) != 0U) && - ((gNfcDev.techs2do & RFAL_NFC_POLL_TECH_F) != - 0U)) /* If a NFC-F device was found/detected, perform Collision Resolution */ - { - rfalNfcfListenDevice nfcfDevList[RFAL_NFC_MAX_DEVICES]; - - if(!gNfcDev.isTechInit) { - EXIT_ON_ERR( - err, - rfalNfcfPollerInitialize(gNfcDev.disc.nfcfBR)); /* Initialize RFAL for NFC-F */ - EXIT_ON_ERR( - err, - rfalFieldOnAndStartGT()); /* Ensure GT again as other technologies have also been polled */ - gNfcDev.isTechInit = true; - } - - if(!rfalIsGTExpired()) { - return ERR_BUSY; - } - - devCnt = 0; - gNfcDev.isTechInit = false; - gNfcDev.techs2do &= ~RFAL_NFC_POLL_TECH_F; - - err = rfalNfcfPollerCollisionResolution( - gNfcDev.disc.compMode, (gNfcDev.disc.devLimit - gNfcDev.devCnt), nfcfDevList, &devCnt); - if((err == ERR_NONE) && (devCnt != 0U)) { - for(i = 0; i < devCnt; - i++) /* Copy devices found form local Nfcf list into global device list */ - { - gNfcDev.devList[gNfcDev.devCnt].type = RFAL_NFC_LISTEN_TYPE_NFCF; - gNfcDev.devList[gNfcDev.devCnt].dev.nfcf = nfcfDevList[i]; - gNfcDev.devCnt++; - } - } - - return ERR_BUSY; - } -#endif /* RFAL_FEATURE_NFCF */ - - /*******************************************************************************/ - /* NFC-V Collision Resolution */ - /*******************************************************************************/ -#if RFAL_FEATURE_NFCV - if(((gNfcDev.techsFound & RFAL_NFC_POLL_TECH_V) != 0U) && - ((gNfcDev.techs2do & RFAL_NFC_POLL_TECH_V) != - 0U)) /* If a NFC-V device was found/detected, perform Collision Resolution */ - { - rfalNfcvListenDevice nfcvDevList[RFAL_NFC_MAX_DEVICES]; - - if(!gNfcDev.isTechInit) { - EXIT_ON_ERR(err, rfalNfcvPollerInitialize()); /* Initialize RFAL for NFC-V */ - EXIT_ON_ERR( - err, - rfalFieldOnAndStartGT()); /* Ensure GT again as other technologies have also been polled */ - gNfcDev.isTechInit = true; - } - - if(!rfalIsGTExpired()) { - return ERR_BUSY; - } - - devCnt = 0; - gNfcDev.isTechInit = false; - gNfcDev.techs2do &= ~RFAL_NFC_POLL_TECH_V; - - err = rfalNfcvPollerCollisionResolution( - RFAL_COMPLIANCE_MODE_NFC, - (gNfcDev.disc.devLimit - gNfcDev.devCnt), - nfcvDevList, - &devCnt); - if((err == ERR_NONE) && (devCnt != 0U)) { - for(i = 0; i < devCnt; - i++) /* Copy devices found form local Nfcf list into global device list */ - { - gNfcDev.devList[gNfcDev.devCnt].type = RFAL_NFC_LISTEN_TYPE_NFCV; - gNfcDev.devList[gNfcDev.devCnt].dev.nfcv = nfcvDevList[i]; - gNfcDev.devCnt++; - } - } - - return ERR_BUSY; - } -#endif /* RFAL_FEATURE_NFCV */ - - /*******************************************************************************/ - /* ST25TB Collision Resolution */ - /*******************************************************************************/ -#if RFAL_FEATURE_ST25TB - if(((gNfcDev.techsFound & RFAL_NFC_POLL_TECH_ST25TB) != 0U) && - ((gNfcDev.techs2do & RFAL_NFC_POLL_TECH_ST25TB) != - 0U)) /* If a ST25TB device was found/detected, perform Collision Resolution */ - { - rfalSt25tbListenDevice st25tbDevList[RFAL_NFC_MAX_DEVICES]; - - if(!gNfcDev.isTechInit) { - EXIT_ON_ERR(err, rfalSt25tbPollerInitialize()); /* Initialize RFAL for ST25TB */ - EXIT_ON_ERR( - err, - rfalFieldOnAndStartGT()); /* Ensure GT again as other technologies have also been polled */ - gNfcDev.isTechInit = true; - } - - if(!rfalIsGTExpired()) { - return ERR_BUSY; - } - - devCnt = 0; - gNfcDev.isTechInit = false; - gNfcDev.techs2do &= ~RFAL_NFC_POLL_TECH_ST25TB; - - err = rfalSt25tbPollerCollisionResolution( - (gNfcDev.disc.devLimit - gNfcDev.devCnt), st25tbDevList, &devCnt); - if((err == ERR_NONE) && (devCnt != 0U)) { - for(i = 0; i < devCnt; - i++) /* Copy devices found form local Nfcf list into global device list */ - { - gNfcDev.devList[gNfcDev.devCnt].type = RFAL_NFC_LISTEN_TYPE_ST25TB; - gNfcDev.devList[gNfcDev.devCnt].dev.st25tb = st25tbDevList[i]; - gNfcDev.devCnt++; - } - } - - return ERR_BUSY; - } -#endif /* RFAL_FEATURE_ST25TB */ - - return ERR_NONE; /* All technologies have been performed */ -} - -/*! - ****************************************************************************** - * \brief Poller Activation - * - * This method Activates a given device according to it's type and - * protocols supported - * - * \param[in] devIt : device's position on the list to be activated - * - * \return ERR_NONE : Operation completed with no error - * \return ERR_BUSY : Operation ongoing - * \return ERR_XXXX : Error occurred - * - ****************************************************************************** - */ -static ReturnCode rfalNfcPollActivation(uint8_t devIt) { - ReturnCode err; - - err = ERR_NONE; - - /* Supress warning when specific RFAL features have been disabled */ - NO_WARNING(err); - - if(devIt > gNfcDev.devCnt) { - return ERR_WRONG_STATE; - } - - switch(gNfcDev.devList[devIt].type) { - /*******************************************************************************/ - /* AP2P Activation */ - /*******************************************************************************/ -#if RFAL_FEATURE_NFC_DEP - case RFAL_NFC_LISTEN_TYPE_AP2P: - /* Activation has already been perfomed (ATR_REQ) */ - - gNfcDev.devList[devIt].nfcid = - gNfcDev.devList[devIt].proto.nfcDep.activation.Target.ATR_RES.NFCID3; - gNfcDev.devList[devIt].nfcidLen = RFAL_NFCDEP_NFCID3_LEN; - break; -#endif /* RFAL_FEATURE_NFC_DEP */ - - /*******************************************************************************/ - /* Passive NFC-A Activation */ - /*******************************************************************************/ -#if RFAL_FEATURE_NFCA - case RFAL_NFC_LISTEN_TYPE_NFCA: - - if(!gNfcDev.isTechInit) { - rfalNfcaPollerInitialize(); - gNfcDev.isTechInit = true; - gNfcDev.isOperOngoing = false; - return ERR_BUSY; - } - - if(gNfcDev.devList[devIt].dev.nfca.isSleep) /* Check if desired device is in Sleep */ - { - rfalNfcaSensRes sensRes; - rfalNfcaSelRes selRes; - - if(!gNfcDev.isOperOngoing) { - /* Wake up all cards */ - EXIT_ON_ERR( - err, rfalNfcaPollerCheckPresence(RFAL_14443A_SHORTFRAME_CMD_WUPA, &sensRes)); - gNfcDev.isOperOngoing = true; - } else { - /* Select specific device */ - EXIT_ON_ERR( - err, - rfalNfcaPollerSelect( - gNfcDev.devList[devIt].dev.nfca.nfcId1, - gNfcDev.devList[devIt].dev.nfca.nfcId1Len, - &selRes)); - gNfcDev.devList[devIt].dev.nfca.isSleep = false; - gNfcDev.isOperOngoing = false; - } - return ERR_BUSY; - } - - /* Set NFCID */ - gNfcDev.devList[devIt].nfcid = gNfcDev.devList[devIt].dev.nfca.nfcId1; - gNfcDev.devList[devIt].nfcidLen = gNfcDev.devList[devIt].dev.nfca.nfcId1Len; - - /*******************************************************************************/ - /* Perform protocol specific activation */ - switch(gNfcDev.devList[devIt].dev.nfca.type) { - /*******************************************************************************/ - case RFAL_NFCA_T1T: - - /* No further activation needed for T1T (RID already performed) */ - - gNfcDev.devList[devIt].nfcid = gNfcDev.devList[devIt].dev.nfca.ridRes.uid; - gNfcDev.devList[devIt].nfcidLen = RFAL_T1T_UID_LEN; - - gNfcDev.devList[devIt].rfInterface = RFAL_NFC_INTERFACE_RF; - break; - - case RFAL_NFCA_T2T: - - /* No further activation needed for a T2T */ - - gNfcDev.devList[devIt].rfInterface = RFAL_NFC_INTERFACE_RF; - break; - - /*******************************************************************************/ - case RFAL_NFCA_T4T: /* Device supports ISO-DEP */ - -#if RFAL_FEATURE_ISO_DEP && RFAL_FEATURE_ISO_DEP_POLL - if(!gNfcDev.isOperOngoing) { - /* Perform ISO-DEP (ISO14443-4) activation: RATS and PPS if supported */ - rfalIsoDepInitialize(); - EXIT_ON_ERR( - err, - rfalIsoDepPollAStartActivation( - (rfalIsoDepFSxI)RFAL_ISODEP_FSDI_DEFAULT, - RFAL_ISODEP_NO_DID, - gNfcDev.disc.maxBR, - &gNfcDev.devList[devIt].proto.isoDep)); - - gNfcDev.isOperOngoing = true; - return ERR_BUSY; - } - - err = rfalIsoDepPollAGetActivationStatus(); - if(err != ERR_NONE) { - return err; - } - - gNfcDev.devList[devIt].rfInterface = - RFAL_NFC_INTERFACE_ISODEP; /* NFC-A T4T device activated */ -#else - gNfcDev.devList[devIt].rfInterface = - RFAL_NFC_INTERFACE_RF; /* No ISO-DEP supported activate using RF interface */ -#endif /* RFAL_FEATURE_ISO_DEP_POLL */ - break; - - /*******************************************************************************/ - case RFAL_NFCA_T4T_NFCDEP: /* Device supports both T4T and NFC-DEP */ - case RFAL_NFCA_NFCDEP: /* Device supports NFC-DEP */ - -#if RFAL_FEATURE_NFC_DEP - /* Perform NFC-DEP (P2P) activation: ATR and PSL if supported */ - EXIT_ON_ERR( - err, - rfalNfcNfcDepActivate(&gNfcDev.devList[devIt], RFAL_NFCDEP_COMM_PASSIVE, NULL, 0)); - - gNfcDev.devList[devIt].nfcid = - gNfcDev.devList[devIt].proto.nfcDep.activation.Target.ATR_RES.NFCID3; - gNfcDev.devList[devIt].nfcidLen = RFAL_NFCDEP_NFCID3_LEN; - - gNfcDev.devList[devIt].rfInterface = - RFAL_NFC_INTERFACE_NFCDEP; /* NFC-A P2P device activated */ -#else - gNfcDev.devList[devIt].rfInterface = - RFAL_NFC_INTERFACE_RF; /* No NFC-DEP supported activate using RF interface */ -#endif /* RFAL_FEATURE_NFC_DEP */ - break; - - /*******************************************************************************/ - default: - return ERR_WRONG_STATE; - } - break; -#endif /* RFAL_FEATURE_NFCA */ - - /*******************************************************************************/ - /* Passive NFC-B Activation */ - /*******************************************************************************/ -#if RFAL_FEATURE_NFCB - case RFAL_NFC_LISTEN_TYPE_NFCB: - - if(!gNfcDev.isTechInit) { - rfalNfcbPollerInitialize(); - gNfcDev.isTechInit = true; - gNfcDev.isOperOngoing = false; - return ERR_BUSY; - } - - if(gNfcDev.devList[devIt].dev.nfcb.isSleep) /* Check if desired device is in Sleep */ - { - rfalNfcbSensbRes sensbRes; - uint8_t sensbResLen; - - /* Wake up all cards. SENSB_RES may return collision but the NFCID0 is available to explicitly select NFC-B card via ATTRIB; so error will be ignored here */ - rfalNfcbPollerCheckPresence( - RFAL_NFCB_SENS_CMD_ALLB_REQ, RFAL_NFCB_SLOT_NUM_1, &sensbRes, &sensbResLen); - } - - /* Set NFCID */ - gNfcDev.devList[devIt].nfcid = gNfcDev.devList[devIt].dev.nfcb.sensbRes.nfcid0; - gNfcDev.devList[devIt].nfcidLen = RFAL_NFCB_NFCID0_LEN; - -#if RFAL_FEATURE_ISO_DEP && RFAL_FEATURE_ISO_DEP_POLL - /* Check if device supports ISO-DEP (ISO14443-4) */ - if((gNfcDev.devList[devIt].dev.nfcb.sensbRes.protInfo.FsciProType & - RFAL_NFCB_SENSB_RES_PROTO_ISO_MASK) != 0U) { - if(!gNfcDev.isOperOngoing) { - rfalIsoDepInitialize(); - /* Perform ISO-DEP (ISO14443-4) activation: ATTRIB */ - EXIT_ON_ERR( - err, - rfalIsoDepPollBStartActivation( - (rfalIsoDepFSxI)RFAL_ISODEP_FSDI_DEFAULT, - RFAL_ISODEP_NO_DID, - gNfcDev.disc.maxBR, - 0x00, - &gNfcDev.devList[devIt].dev.nfcb, - NULL, - 0, - &gNfcDev.devList[devIt].proto.isoDep)); - - gNfcDev.isOperOngoing = true; - return ERR_BUSY; - } - - err = rfalIsoDepPollBGetActivationStatus(); - if(err != ERR_NONE) { - return err; - } - - gNfcDev.devList[devIt].rfInterface = - RFAL_NFC_INTERFACE_ISODEP; /* NFC-B T4T device activated */ - break; - } - -#endif /* RFAL_FEATURE_ISO_DEP_POLL */ - - gNfcDev.devList[devIt].rfInterface = - RFAL_NFC_INTERFACE_RF; /* NFC-B device activated */ - break; - -#endif /* RFAL_FEATURE_NFCB */ - - /*******************************************************************************/ - /* Passive NFC-F Activation */ - /*******************************************************************************/ -#if RFAL_FEATURE_NFCF - case RFAL_NFC_LISTEN_TYPE_NFCF: - - rfalNfcfPollerInitialize(gNfcDev.disc.nfcfBR); - -#if RFAL_FEATURE_NFC_DEP - if(rfalNfcfIsNfcDepSupported(&gNfcDev.devList[devIt].dev.nfcf)) { - /* Perform NFC-DEP (P2P) activation: ATR and PSL if supported */ - EXIT_ON_ERR( - err, - rfalNfcNfcDepActivate(&gNfcDev.devList[devIt], RFAL_NFCDEP_COMM_PASSIVE, NULL, 0)); - - /* Set NFCID */ - gNfcDev.devList[devIt].nfcid = - gNfcDev.devList[devIt].proto.nfcDep.activation.Target.ATR_RES.NFCID3; - gNfcDev.devList[devIt].nfcidLen = RFAL_NFCDEP_NFCID3_LEN; - - gNfcDev.devList[devIt].rfInterface = - RFAL_NFC_INTERFACE_NFCDEP; /* NFC-F P2P device activated */ - break; - } -#endif /* RFAL_FEATURE_NFC_DEP */ - - /* Set NFCID */ - gNfcDev.devList[devIt].nfcid = gNfcDev.devList[devIt].dev.nfcf.sensfRes.NFCID2; - gNfcDev.devList[devIt].nfcidLen = RFAL_NFCF_NFCID2_LEN; - - gNfcDev.devList[devIt].rfInterface = - RFAL_NFC_INTERFACE_RF; /* NFC-F T3T device activated */ - break; -#endif /* RFAL_FEATURE_NFCF */ - - /*******************************************************************************/ - /* Passive NFC-V Activation */ - /*******************************************************************************/ -#if RFAL_FEATURE_NFCV - case RFAL_NFC_LISTEN_TYPE_NFCV: - - rfalNfcvPollerInitialize(); - - /* No specific activation needed for a T5T */ - - /* Set NFCID */ - gNfcDev.devList[devIt].nfcid = gNfcDev.devList[devIt].dev.nfcv.InvRes.UID; - gNfcDev.devList[devIt].nfcidLen = RFAL_NFCV_UID_LEN; - - gNfcDev.devList[devIt].rfInterface = - RFAL_NFC_INTERFACE_RF; /* NFC-V T5T device activated */ - break; -#endif /* RFAL_FEATURE_NFCV */ - - /*******************************************************************************/ - /* Passive ST25TB Activation */ - /*******************************************************************************/ -#if RFAL_FEATURE_ST25TB - case RFAL_NFC_LISTEN_TYPE_ST25TB: - - rfalSt25tbPollerInitialize(); - - /* No specific activation needed for a ST25TB */ - - /* Set NFCID */ - gNfcDev.devList[devIt].nfcid = gNfcDev.devList[devIt].dev.st25tb.UID; - gNfcDev.devList[devIt].nfcidLen = RFAL_ST25TB_UID_LEN; - - gNfcDev.devList[devIt].rfInterface = RFAL_NFC_INTERFACE_RF; /* ST25TB device activated */ - break; -#endif /* RFAL_FEATURE_ST25TB */ - - /*******************************************************************************/ - default: - return ERR_WRONG_STATE; - } - - gNfcDev.activeDev = &gNfcDev.devList[devIt]; /* Assign active device to be used further on */ - return ERR_NONE; -} - -/*! - ****************************************************************************** - * \brief Listener Activation - * - * This method handles the listen mode Activation according to the different - * protocols the Reader/Initiator performs - * - * \return ERR_NONE : Operation completed with no error - * \return ERR_BUSY : Operation ongoing - * \return ERR_PROTO : Unexpected frame received - * \return ERR_XXXX : Error occurred - * - ****************************************************************************** - */ -#if RFAL_FEATURE_LISTEN_MODE -static ReturnCode rfalNfcListenActivation(void) { - bool isDataRcvd; - ReturnCode ret; - rfalLmState lmSt; - rfalBitRate bitRate; -#if RFAL_FEATURE_NFC_DEP - uint8_t hdrLen; - - /* Set the header length in NFC-A */ - hdrLen = (RFAL_NFCDEP_SB_LEN + RFAL_NFCDEP_LEN_LEN); -#endif /* RFAL_FEATURE_NFC_DEP */ - - lmSt = rfalListenGetState(&isDataRcvd, &bitRate); - - switch(lmSt) { -#if RFAL_FEATURE_NFCA - /*******************************************************************************/ - case RFAL_LM_STATE_ACTIVE_A: /* NFC-A CE activation */ - case RFAL_LM_STATE_ACTIVE_Ax: - - if(isDataRcvd) /* Check if Reader/Initator has sent some data */ - { - /* Check if received data is a Sleep request */ - if(rfalNfcaListenerIsSleepReq( - gNfcDev.rxBuf.rfBuf, - rfalConvBitsToBytes(gNfcDev.rxLen))) /* Check if received data is a SLP_REQ */ - { - /* Set the Listen Mode in Sleep state */ - EXIT_ON_ERR( - ret, - rfalListenSleepStart( - RFAL_LM_STATE_SLEEP_A, - gNfcDev.rxBuf.rfBuf, - sizeof(gNfcDev.rxBuf.rfBuf), - &gNfcDev.rxLen)); - } - - else if(gNfcDev.disc.activate_after_sak) { - gNfcDev.devList->type = RFAL_NFC_POLL_TYPE_NFCA; - rfalListenSetState(RFAL_LM_STATE_ACTIVE_A); - return ERR_NONE; - } -#if RFAL_FEATURE_ISO_DEP && RFAL_FEATURE_ISO_DEP_LISTEN - /* Check if received data is a valid RATS */ - else if(rfalIsoDepIsRats( - gNfcDev.rxBuf.rfBuf, (uint8_t)rfalConvBitsToBytes(gNfcDev.rxLen))) { - rfalIsoDepAtsParam atsParam; - rfalIsoDepListenActvParam rxParam; - - /* Set ATS parameters */ - atsParam.fsci = (uint8_t)RFAL_ISODEP_DEFAULT_FSCI; - atsParam.fwi = RFAL_ISODEP_DEFAULT_FWI; - atsParam.sfgi = RFAL_ISODEP_DEFAULT_SFGI; - atsParam.didSupport = false; - atsParam.ta = RFAL_ISODEP_ATS_TA_SAME_D; - atsParam.hb = NULL; - atsParam.hbLen = 0; - - /* Set Rx parameters */ - rxParam.rxBuf = - (rfalIsoDepBufFormat*)&gNfcDev.rxBuf - .isoDepBuf; /* PRQA S 0310 # MISRA 11.3 - Intentional safe cast to avoiding large buffer duplication */ - rxParam.rxLen = &gNfcDev.rxLen; - rxParam.isoDepDev = &gNfcDev.devList->proto.isoDep; - rxParam.isRxChaining = &gNfcDev.isRxChaining; - - rfalListenSetState(RFAL_LM_STATE_CARDEMU_4A); /* Set next state CE T4T */ - rfalIsoDepInitialize(); /* Initialize ISO-DEP layer to handle ISO14443-a activation / RATS */ - - /* Set ISO-DEP layer to digest RATS and handle activation */ - EXIT_ON_ERR( - ret, - rfalIsoDepListenStartActivation( - &atsParam, NULL, gNfcDev.rxBuf.rfBuf, gNfcDev.rxLen, rxParam)); - } -#endif /* RFAL_FEATURE_ISO_DEP_LISTEN */ - -#if RFAL_FEATURE_NFC_DEP - - /* Check if received data is a valid ATR_REQ */ - else if(rfalNfcDepIsAtrReq( - &gNfcDev.rxBuf.rfBuf[hdrLen], - (rfalConvBitsToBytes(gNfcDev.rxLen) - hdrLen), - gNfcDev.devList->nfcid)) { - gNfcDev.devList->type = RFAL_NFC_POLL_TYPE_NFCA; - EXIT_ON_ERR( - ret, - rfalNfcNfcDepActivate( - gNfcDev.devList, - RFAL_NFCDEP_COMM_PASSIVE, - &gNfcDev.rxBuf.rfBuf[hdrLen], - (rfalConvBitsToBytes(gNfcDev.rxLen) - hdrLen))); - } -#endif /* RFAL_FEATURE_NFC_DEP */ - - else { - return ERR_PROTO; - } - } - return ERR_BUSY; - -#endif /* RFAL_FEATURE_NFCA */ - -#if RFAL_FEATURE_ISO_DEP && RFAL_FEATURE_ISO_DEP_LISTEN - /*******************************************************************************/ - case RFAL_LM_STATE_CARDEMU_4A: /* T4T ISO-DEP activation */ - - ret = rfalIsoDepListenGetActivationStatus(); - if(ret == ERR_NONE) { - gNfcDev.devList->type = RFAL_NFC_POLL_TYPE_NFCA; - gNfcDev.devList->rfInterface = RFAL_NFC_INTERFACE_ISODEP; - gNfcDev.devList->nfcid = NULL; - gNfcDev.devList->nfcidLen = 0; - } - return ret; -#endif /* RFAL_FEATURE_ISO_DEP_LISTEN */ - - /*******************************************************************************/ - case RFAL_LM_STATE_READY_F: /* NFC-F CE activation */ - - if(isDataRcvd) /* Wait for the first received data */ - { -#if RFAL_FEATURE_NFC_DEP - /* Set the header length in NFC-F */ - hdrLen = RFAL_NFCDEP_LEN_LEN; - - if(rfalNfcDepIsAtrReq( - &gNfcDev.rxBuf.rfBuf[hdrLen], - (rfalConvBitsToBytes(gNfcDev.rxLen) - hdrLen), - gNfcDev.devList->nfcid)) { - gNfcDev.devList->type = RFAL_NFC_POLL_TYPE_NFCF; - EXIT_ON_ERR( - ret, - rfalNfcNfcDepActivate( - gNfcDev.devList, - RFAL_NFCDEP_COMM_PASSIVE, - &gNfcDev.rxBuf.rfBuf[hdrLen], - (rfalConvBitsToBytes(gNfcDev.rxLen) - hdrLen))); - } else -#endif /* RFAL_FEATURE_NFC_DEP */ - { - rfalListenSetState( - RFAL_LM_STATE_CARDEMU_3); /* First data already received - set T3T CE */ - } - } - return ERR_BUSY; - - /*******************************************************************************/ - case RFAL_LM_STATE_CARDEMU_3: /* T3T activated */ - - gNfcDev.devList->type = RFAL_NFC_POLL_TYPE_NFCF; - gNfcDev.devList->rfInterface = RFAL_NFC_INTERFACE_RF; - gNfcDev.devList->nfcid = NULL; - gNfcDev.devList->nfcidLen = 0; - - return ERR_NONE; - -#if RFAL_FEATURE_NFC_DEP - /*******************************************************************************/ - case RFAL_LM_STATE_TARGET_A: /* NFC-DEP activation */ - case RFAL_LM_STATE_TARGET_F: - - ret = rfalNfcDepListenGetActivationStatus(); - if(ret == ERR_NONE) { - gNfcDev.devList->rfInterface = RFAL_NFC_INTERFACE_NFCDEP; - gNfcDev.devList->nfcidLen = RFAL_NFCDEP_NFCID3_LEN; - } - return ret; -#endif /* RFAL_FEATURE_NFC_DEP */ - - /*******************************************************************************/ - case RFAL_LM_STATE_IDLE: /* AP2P activation */ - if(isDataRcvd) /* Check if Reader/Initator has sent some data */ - { - if((gNfcDev.lmMask & RFAL_LM_MASK_ACTIVE_P2P) != 0U) /* Check if AP2P is enabled */ - { -#if RFAL_FEATURE_NFC_DEP - /* Calculate the header length in NFC-A or NFC-F mode*/ - hdrLen = - ((bitRate == RFAL_BR_106) ? (RFAL_NFCDEP_SB_LEN + RFAL_NFCDEP_LEN_LEN) : - RFAL_NFCDEP_LEN_LEN); - - if(rfalNfcDepIsAtrReq( - &gNfcDev.rxBuf.rfBuf[hdrLen], - (rfalConvBitsToBytes(gNfcDev.rxLen) - hdrLen), - NULL)) { - gNfcDev.devList->type = RFAL_NFC_POLL_TYPE_AP2P; - rfalSetMode((RFAL_MODE_LISTEN_ACTIVE_P2P), bitRate, bitRate); - EXIT_ON_ERR( - ret, - rfalNfcNfcDepActivate( - gNfcDev.devList, - RFAL_NFCDEP_COMM_ACTIVE, - &gNfcDev.rxBuf.rfBuf[hdrLen], - (rfalConvBitsToBytes(gNfcDev.rxLen) - hdrLen))); - } else -#endif /* RFAL_FEATURE_NFC_DEP */ - { - return ERR_PROTO; - } - } - } - return ERR_BUSY; - - /*******************************************************************************/ - case RFAL_LM_STATE_READY_A: - case RFAL_LM_STATE_READY_Ax: - case RFAL_LM_STATE_SLEEP_A: - case RFAL_LM_STATE_SLEEP_AF: - return ERR_BUSY; - - /*******************************************************************************/ - case RFAL_LM_STATE_POWER_OFF: - return ERR_LINK_LOSS; - - default: /* Wait for activation */ - break; - } - - return ERR_INTERNAL; -} -#endif /* RFAL_FEATURE_LISTEN_MODE */ - -/*! - ****************************************************************************** - * \brief Poller NFC DEP Activate - * - * This method performs NFC-DEP Activation - * - * \param[in] device : device info - * \param[in] commMode : communication mode (Passive/Active) - * \param[in] atrReq : received ATR_REQ - * \param[in] atrReqLen : received ATR_REQ size - * - * \return ERR_NONE : Operation completed with no error - * \return ERR_BUSY : Operation ongoing - * \return ERR_XXXX : Error occurred - * - ****************************************************************************** - */ -#if RFAL_FEATURE_NFC_DEP -static ReturnCode rfalNfcNfcDepActivate( - rfalNfcDevice* device, - rfalNfcDepCommMode commMode, - const uint8_t* atrReq, - uint16_t atrReqLen) { - rfalNfcDepAtrParam initParam; - - /* Supress warnings if Listen mode is disabled */ - NO_WARNING(atrReq); - NO_WARNING(atrReqLen); - - /* If we are in Poll mode */ - if(rfalNfcIsRemDevListener(device->type)) { - /*******************************************************************************/ - /* If Passive F use the NFCID2 retrieved from SENSF */ - if(device->type == RFAL_NFC_LISTEN_TYPE_NFCF) { - initParam.nfcid = device->dev.nfcf.sensfRes.NFCID2; - initParam.nfcidLen = RFAL_NFCF_NFCID2_LEN; - } else { - initParam.nfcid = gNfcDev.disc.nfcid3; - initParam.nfcidLen = RFAL_NFCDEP_NFCID3_LEN; - } - - initParam.BS = RFAL_NFCDEP_Bx_NO_HIGH_BR; - initParam.BR = RFAL_NFCDEP_Bx_NO_HIGH_BR; - initParam.DID = RFAL_NFCDEP_DID_NO; - initParam.NAD = RFAL_NFCDEP_NAD_NO; - initParam.LR = RFAL_NFCDEP_LR_254; - initParam.GB = gNfcDev.disc.GB; - initParam.GBLen = gNfcDev.disc.GBLen; - initParam.commMode = commMode; - initParam.operParam = - (RFAL_NFCDEP_OPER_FULL_MI_EN | RFAL_NFCDEP_OPER_EMPTY_DEP_DIS | - RFAL_NFCDEP_OPER_ATN_EN | RFAL_NFCDEP_OPER_RTOX_REQ_EN); - - rfalNfcDepInitialize(); - /* Perform NFC-DEP (P2P) activation: ATR and PSL if supported */ - return rfalNfcDepInitiatorHandleActivation( - &initParam, gNfcDev.disc.maxBR, &device->proto.nfcDep); - } - /* If we are in Listen mode */ -#if RFAL_FEATURE_LISTEN_MODE - else if(rfalNfcIsRemDevPoller(device->type)) { - rfalNfcDepListenActvParam actvParams; - rfalNfcDepTargetParam targetParam; - - ST_MEMCPY(targetParam.nfcid3, (uint8_t*)gNfcDev.disc.nfcid3, RFAL_NFCDEP_NFCID3_LEN); - targetParam.bst = RFAL_NFCDEP_Bx_NO_HIGH_BR; - targetParam.brt = RFAL_NFCDEP_Bx_NO_HIGH_BR; - targetParam.to = RFAL_NFCDEP_WT_TRG_MAX_L13; /* [LLCP] 1.3 6.2.1 */ - targetParam.ppt = rfalNfcDepLR2PP(RFAL_NFCDEP_LR_254); - if(gNfcDev.disc.GBLen >= RFAL_NFCDEP_GB_MAX_LEN) { - return ERR_PARAM; - } - targetParam.GBtLen = gNfcDev.disc.GBLen; - if(gNfcDev.disc.GBLen > 0U) { - ST_MEMCPY(targetParam.GBt, gNfcDev.disc.GB, gNfcDev.disc.GBLen); - } - targetParam.operParam = - (RFAL_NFCDEP_OPER_FULL_MI_EN | RFAL_NFCDEP_OPER_EMPTY_DEP_DIS | - RFAL_NFCDEP_OPER_ATN_EN | RFAL_NFCDEP_OPER_RTOX_REQ_EN); - targetParam.commMode = commMode; - - /* Set activation buffer (including header) for NFC-DEP */ - actvParams.rxBuf = - (rfalNfcDepBufFormat*)&gNfcDev.rxBuf - .nfcDepBuf; /* PRQA S 0310 # MISRA 11.3 - Intentional safe cast to avoiding large buffer duplication */ - actvParams.rxLen = &gNfcDev.rxLen; - actvParams.isRxChaining = &gNfcDev.isRxChaining; - actvParams.nfcDepDev = &gNfcDev.devList->proto.nfcDep; - - rfalListenSetState( - ((device->type == RFAL_NFC_POLL_TYPE_NFCA) ? RFAL_LM_STATE_TARGET_A : - RFAL_LM_STATE_TARGET_F)); - - rfalNfcDepInitialize(); - /* Perform NFC-DEP (P2P) activation: send ATR_RES and handle activation */ - return rfalNfcDepListenStartActivation(&targetParam, atrReq, atrReqLen, actvParams); - } -#endif /* RFAL_FEATURE_LISTEN_MODE */ - - else { - return ERR_INTERNAL; - } -} -#endif /* RFAL_FEATURE_NFC_DEP */ - -/*! - ****************************************************************************** - * \brief Poller NFC Deactivate - * - * This method Deactivates the device if a deactivation procedure exists - * - * \return ERR_NONE : Operation completed with no error - * \return ERR_BUSY : Operation ongoing - * \return ERR_XXXX : Error occurred - * - ****************************************************************************** - */ -static ReturnCode rfalNfcDeactivation(void) { - /* Check if a device has been activated */ - if(gNfcDev.activeDev != NULL) { - if(rfalNfcIsRemDevListener( - gNfcDev.activeDev->type)) /* Listen mode no additional deactivation to be performed*/ - { -#ifndef RFAL_NFC_SKIP_DEACT - switch(gNfcDev.activeDev->rfInterface) { - /*******************************************************************************/ - case RFAL_NFC_INTERFACE_RF: - break; /* No specific deactivation to be performed */ - - /*******************************************************************************/ -#if RFAL_FEATURE_ISO_DEP && RFAL_FEATURE_ISO_DEP_POLL - case RFAL_NFC_INTERFACE_ISODEP: - rfalIsoDepDeselect(); /* Send a Deselect to device */ - break; -#endif /* RFAL_FEATURE_ISO_DEP_POLL */ - - /*******************************************************************************/ -#if RFAL_FEATURE_NFC_DEP - case RFAL_NFC_INTERFACE_NFCDEP: - switch(gNfcDev.activeDev->type) { - case RFAL_NFC_LISTEN_TYPE_AP2P: - rfalNfcDepRLS(); /* Send a Release to device */ - break; - default: - rfalNfcDepDSL(); /* Send a Deselect to device */ - break; - } - break; -#endif /* RFAL_FEATURE_NFC_DEP */ - - default: - return ERR_REQUEST; - } -#endif /* RFAL_NFC_SKIP_DEACT */ - } - } - -#if RFAL_FEATURE_WAKEUP_MODE - rfalWakeUpModeStop(); -#endif /* RFAL_FEATURE_WAKEUP_MODE */ - -#if RFAL_FEATURE_LISTEN_MODE - rfalListenStop(); -#else - rfalFieldOff(); -#endif - - gNfcDev.activeDev = NULL; - return ERR_NONE; -} diff --git a/lib/ST25RFAL002/source/rfal_nfcDep.c b/lib/ST25RFAL002/source/rfal_nfcDep.c deleted file mode 100644 index ad851ac5d2a..00000000000 --- a/lib/ST25RFAL002/source/rfal_nfcDep.c +++ /dev/null @@ -1,2730 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: NFCC firmware - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_nfcDep.c - * - * \author Gustavo Patricio - * - * \brief Implementation of NFC-DEP protocol - * - * NFC-DEP is also known as NFCIP - Near Field Communication - * Interface and Protocol - * - * This implementation was based on the following specs: - * - NFC Forum Digital 1.1 - * - ECMA 340 3rd Edition 2013 - * - */ - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "rfal_nfcDep.h" -#include "rfal_nfcf.h" -#include "utils.h" - -/* - ****************************************************************************** - * ENABLE SWITCH - ****************************************************************************** - */ - -#if RFAL_FEATURE_NFC_DEP - -/* Check for valid Block/Payload length Digital 2.0 Table 90*/ -#if((RFAL_FEATURE_NFC_DEP_BLOCK_MAX_LEN != 64) && (RFAL_FEATURE_NFC_DEP_BLOCK_MAX_LEN != 128) && \ - (RFAL_FEATURE_NFC_DEP_BLOCK_MAX_LEN != 192) && (RFAL_FEATURE_NFC_DEP_BLOCK_MAX_LEN != 254)) -#error \ - " RFAL: Invalid NFC-DEP Block Max length. Please change RFAL_FEATURE_NFC_DEP_BLOCK_MAX_LEN. " -#endif - -/* Check for valid PDU length */ -#if((RFAL_FEATURE_NFC_DEP_PDU_MAX_LEN < RFAL_FEATURE_NFC_DEP_BLOCK_MAX_LEN)) -#error " RFAL: Invalid NFC-DEP PDU Max length. Please change RFAL_FEATURE_NFC_DEP_PDU_MAX_LEN. " -#endif - -/* - ****************************************************************************** - * DEFINES - ****************************************************************************** - */ -#define NFCIP_ATR_RETRY_MAX 2U /*!< Max consecutive retrys of an ATR REQ with transm error*/ - -#define NFCIP_PSLPAY_LEN (2U) /*!< PSL Payload length (BRS + FSL) */ -#define NFCIP_PSLREQ_LEN \ - (3U + RFAL_NFCDEP_LEN_LEN) /*!< PSL REQ length (incl LEN) */ -#define NFCIP_PSLRES_LEN \ - (3U + RFAL_NFCDEP_LEN_LEN) /*!< PSL RES length (incl LEN) */ - -#define NFCIP_ATRREQ_BUF_LEN \ - (RFAL_NFCDEP_ATRREQ_MAX_LEN + RFAL_NFCDEP_LEN_LEN) /*!< ATR REQ max length (incl LEN) */ -#define NFCIP_ATRRES_BUF_LEN \ - (RFAL_NFCDEP_ATRRES_MAX_LEN + RFAL_NFCDEP_LEN_LEN) /*!< ATR RES max length (incl LEN) */ - -#define NFCIP_RLSREQ_LEN \ - (3U + RFAL_NFCDEP_LEN_LEN) /*!< RLS REQ length (incl LEN) */ -#define NFCIP_RLSRES_LEN \ - (3U + RFAL_NFCDEP_LEN_LEN) /*!< RSL RES length (incl LEN) */ -#define NFCIP_RLSRES_MIN \ - (2U + RFAL_NFCDEP_LEN_LEN) /*!< Minimum length for a RLS RES (incl LEN) */ - -#define NFCIP_DSLREQ_LEN \ - (3U + RFAL_NFCDEP_LEN_LEN) /*!< DSL REQ length (incl LEN) */ -#define NFCIP_DSLRES_LEN \ - (3U + RFAL_NFCDEP_LEN_LEN) /*!< DSL RES length (incl LEN) */ -#define NFCIP_DSLRES_MIN \ - (2U + RFAL_NFCDEP_LEN_LEN) /*!< Minimum length for a DSL RES (incl LEN) */ - -#define NFCIP_DSLRES_MAX_LEN \ - (3U + RFAL_NFCDEP_LEN_LEN) /*!< Maximum length for a DSL RES (incl LEN) */ -#define NFCIP_RLSRES_MAX_LEN \ - (3U + RFAL_NFCDEP_LEN_LEN) /*!< Minimum length for a RLS RES (incl LEN) */ -#define NFCIP_TARGET_RES_MAX \ - (MAX(NFCIP_RLSRES_MAX_LEN, NFCIP_DSLRES_MAX_LEN)) /*!< Max target control res length */ - -#define NFCIP_NO_FWT RFAL_FWT_NONE /*!< No FWT value - Target Mode */ -#define NFCIP_INIT_MIN_RTOX 1U /*!< Minimum RTOX value Digital 1.0 14.8.4.1 */ -#define NFCIP_INIT_MAX_RTOX 59U /*!< Maximum RTOX value Digital 1.0 14.8.4.1 */ - -#define NFCIP_TARG_MIN_RTOX 1U /*!< Minimum target RTOX value Digital 1.0 14.8.4.1 */ -#define NFCIP_TARG_MAX_RTOX 59U /*!< Maximum target RTOX value Digital 1.0 14.8.4.1 */ - -#define NFCIP_TRECOV 1280U /*!< Digital 1.0 A.10 Trecov */ - -#define NFCIP_TIMEOUT_ADJUSTMENT \ - 3072U /*!< Timeout Adjustment to compensate timing from end of Tx to end of frame */ -#define NFCIP_RWT_ACTIVATION \ - (0x1000001U + \ - NFCIP_TIMEOUT_ADJUSTMENT) /*!< Digital 2.2 B.11 RWT ACTIVATION 2^24 + RWT Delta + Adjustment*/ -#define NFCIP_RWT_ACM_ACTIVATION \ - (0x200001U + \ - NFCIP_TIMEOUT_ADJUSTMENT) /*!< Digital 2.2 B.11 RWT ACTIVATION 2^21 + RWT Delta + Adjustment*/ - -#define RFAL_NFCDEP_HEADER_PAD \ - (RFAL_NFCDEP_DEPREQ_HEADER_LEN - \ - RFAL_NFCDEP_LEN_MIN) /*!< Difference between expected rcvd header len and max foreseen */ - -#ifndef RFAL_NFCDEP_MAX_TX_RETRYS -#define RFAL_NFCDEP_MAX_TX_RETRYS \ - (uint8_t)3U /*!< Number of retransmit retyrs */ -#endif /* RFAL_NFCDEP_MAX_TX_RETRYS */ - -#ifndef RFAL_NFCDEP_TO_RETRYS -#define RFAL_NFCDEP_TO_RETRYS \ - (uint8_t)3U /*!< Number of retrys for Timeout */ -#endif /* RFAL_NFCDEP_TO_RETRYS */ - -#ifndef RFAL_NFCDEP_MAX_RTOX_RETRYS -#define RFAL_NFCDEP_MAX_RTOX_RETRYS \ - (uint8_t)10U /*!< Number of retrys for RTOX Digital 2.0 17.12.4.3 */ -#endif /* RFAL_NFCDEP_MAX_RTOX_RETRYS */ - -#ifndef RFAL_NFCDEP_MAX_NACK_RETRYS -#define RFAL_NFCDEP_MAX_NACK_RETRYS \ - (uint8_t)3U /*!< Number of retrys for NACK */ -#endif /* RFAL_NFCDEP_MAX_NACK_RETRYS */ - -#ifndef RFAL_NFCDEP_MAX_ATN_RETRYS -#define RFAL_NFCDEP_MAX_ATN_RETRYS \ - (uint8_t)3U /*!< Number of retrys for ATN */ -#endif /* RFAL_NFCDEP_MAX_ATN_RETRYS */ - -#define NFCIP_MIN_TXERROR_LEN \ - 4U /*!< Minimum frame length with error to be ignored Digital 1.0 14.12.5.4 */ - -#define NFCIP_REQ (uint8_t)0xD4U /*!= NFCIP_ST_INIT_IDLE) && \ - ((st) <= \ - NFCIP_ST_INIT_RLS)) /*!< Checks if module is set as Initiator */ -#define nfcipIsTarget(st) \ - (!nfcipIsInitiator( \ - st)) /*!< Checks if module is set as Target */ - -#define nfcipIsBRAllowed(br, mBR) \ - (((1U << (br)) & (mBR)) != \ - 0U) /*!< Checks bit rate is allowed by given mask */ - -#define nfcipIsEmptyDEPEnabled(op) \ - (!nfcipIsEmptyDEPDisabled( \ - op)) /*!< Checks if empty payload is allowed by operation config NCI 1.0 Table 81 */ -#define nfcipIsEmptyDEPDisabled(op) \ - (((op)&RFAL_NFCDEP_OPER_EMPTY_DEP_DIS) != \ - 0U) /*!< Checks if empty payload is not allowed by operation config NCI 1.0 Table 81 */ - -#define nfcipIsRTOXReqEnabled(op) \ - (!nfcipIsRTOXReqDisabled( \ - op)) /*!< Checks if send a RTOX_REQ is allowed by operation config NCI 1.0 Table 81 */ -#define nfcipIsRTOXReqDisabled(op) \ - (((op)&RFAL_NFCDEP_OPER_RTOX_REQ_DIS) != \ - 0U) /*!< Checks if send a RTOX_REQ is not allowed by operation config NCI 1.0 Table 81 */ - -/*! Checks if isDeactivating callback is set and calls it, otherwise returns false */ -#define nfcipIsDeactivationPending() \ - ((gNfcip.isDeactivating == NULL) ? false : gNfcip.isDeactivating()) - -/*! Returns the RWT Activation according to the current communication mode */ -#define nfcipRWTActivation() \ - ((gNfcip.cfg.commMode == RFAL_NFCDEP_COMM_ACTIVE) ? NFCIP_RWT_ACM_ACTIVATION : \ - NFCIP_RWT_ACTIVATION) - -#define nfcipRTOXAdjust(v) \ - ((v) - ((v) >> 3)) /*!< Adjust RTOX timer value to a percentage of the total, current 88% */ - -/*******************************************************************************/ - -// timerPollTimeoutValue is necessary after timerCalculateTimeout so that system will wake up upon timer timeout. -#define nfcipTimerStart(timer, time_ms) \ - do { \ - platformTimerDestroy(timer); \ - (timer) = platformTimerCreate((uint16_t)(time_ms)); \ - } while(0) /*!< Configures and starts the RTOX timer */ -#define nfcipTimerisExpired(timer) \ - platformTimerIsExpired(timer) /*!< Checks RTOX timer has expired */ -#define nfcipTimerDestroy(timer) \ - platformTimerDestroy(timer) /*!< Destroys RTOX timer */ - -#define nfcipLogE(...) /*!< Macro for the error log method */ -#define nfcipLogW(...) /*!< Macro for the warning log method */ -#define nfcipLogI(...) /*!< Macro for the info log method */ -#define nfcipLogD(...) /*!< Macro for the debug log method */ - -/*! Digital 1.1 - 16.12.5.2 The Target SHALL NOT attempt any error recovery and remains in Rx mode upon Transmission or a Protocol Error */ -#define nfcDepReEnableRx(rxB, rxBL, rxL) \ - rfalTransceiveBlockingTx( \ - NULL, \ - 0, \ - (rxB), \ - (rxBL), \ - (rxL), \ - (RFAL_TXRX_FLAGS_DEFAULT | (uint32_t)RFAL_TXRX_FLAGS_NFCIP1_ON), \ - RFAL_FWT_NONE) - -/* - ****************************************************************************** - * LOCAL DATA TYPES - ****************************************************************************** - */ - -/*! Struct that holds all DEP parameters/configs for the following communications */ -typedef struct { - uint8_t did; /*!< Device ID (DID) to be used */ - - uint8_t* txBuf; /*!< Pointer to the Tx buffer to be sent */ - uint16_t txBufLen; /*!< Length of the data in the txBuf */ - uint8_t txBufPaylPos; /*!< Position inside txBuf where data starts */ - bool txChaining; /*!< Flag indicating chaining on transmission */ - - uint8_t* rxBuf; /*!< Pointer to the Rx buffer for incoming data */ - uint16_t rxBufLen; /*!< Length of the data in the rxBuf */ - uint8_t rxBufPaylPos; /*!< Position inside rxBuf where data is to be placed*/ - - uint32_t fwt; /*!< Frame Waiting Time (FWT) to be used */ - uint32_t dFwt; /*!< Delta Frame Waiting Time (dFWT) to be used */ - uint16_t fsc; /*!< Frame Size (FSC) to be used */ - -} rfalNfcDepDEPParams; - -/*! NFCIP module states */ -typedef enum { - NFCIP_ST_IDLE, - NFCIP_ST_INIT_IDLE, - NFCIP_ST_INIT_ATR, - NFCIP_ST_INIT_PSL, - NFCIP_ST_INIT_DEP_IDLE, - NFCIP_ST_INIT_DEP_TX, - NFCIP_ST_INIT_DEP_RX, - NFCIP_ST_INIT_DEP_ATN, - NFCIP_ST_INIT_DSL, - NFCIP_ST_INIT_RLS, - - NFCIP_ST_TARG_WAIT_ATR, - NFCIP_ST_TARG_WAIT_ACTV, - NFCIP_ST_TARG_DEP_IDLE, - NFCIP_ST_TARG_DEP_RX, - NFCIP_ST_TARG_DEP_RTOX, - NFCIP_ST_TARG_DEP_TX, - NFCIP_ST_TARG_DEP_SLEEP -} rfalNfcDepState; - -/*! NFCIP commands (Request, Response) */ -typedef enum { - NFCIP_CMD_ATR_REQ = 0x00, - NFCIP_CMD_ATR_RES = 0x01, - NFCIP_CMD_WUP_REQ = 0x02, - NFCIP_CMD_WUP_RES = 0x03, - NFCIP_CMD_PSL_REQ = 0x04, - NFCIP_CMD_PSL_RES = 0x05, - NFCIP_CMD_DEP_REQ = 0x06, - NFCIP_CMD_DEP_RES = 0x07, - NFCIP_CMD_DSL_REQ = 0x08, - NFCIP_CMD_DSL_RES = 0x09, - NFCIP_CMD_RLS_REQ = 0x0A, - NFCIP_CMD_RLS_RES = 0x0B -} rfalNfcDepCmd; - -/*! Struct that holds all NFCIP data */ -typedef struct { - rfalNfcDepConfigs cfg; /*!< Holds the current configuration to be used */ - - rfalNfcDepState state; /*!< Current state of the NFCIP module */ - uint8_t pni; /*!< Packet Number Information (PNI) counter */ - - uint8_t lastCmd; /*!< Last command sent */ - uint8_t lastPFB; /*!< Last PFB sent */ - uint8_t lastPFBnATN; /*!< Last PFB sent (excluding ATN) */ - uint8_t lastRTOX; /*!< Last RTOX value sent */ - - uint8_t cntTxRetrys; /*!< Retransmissions counter */ - uint8_t cntTORetrys; /*!< Timeouts counter */ - uint8_t cntRTOXRetrys; /*!< RTOX counter */ - uint8_t cntNACKRetrys; /*!< NACK counter */ - uint8_t cntATNRetrys; /*!< Attention (ATN) counter */ - - uint16_t fsc; /*!< Current Frame Size (FSC) to be used */ - bool isTxChaining; /*!< Flag for chaining on Transmission */ - bool isRxChaining; /*!< Flag for chaining on Reception */ - uint8_t* txBuf; /*!< Pointer to the Tx buffer to be sent */ - uint8_t* rxBuf; /*!< Pointer to the Rx buffer for incoming data */ - uint16_t txBufLen; /*!< Length of the data in the txBuf */ - uint16_t rxBufLen; /*!< Length of rxBuf buffer */ - uint16_t* rxRcvdLen; /*!< Length of the data in the rxBuf */ - uint8_t txBufPaylPos; /*!< Position in txBuf where data starts */ - uint8_t rxBufPaylPos; /*!< Position in rxBuf where data is to be placed */ - bool* isChaining; /*!< Flag for chaining on Reception */ - - rfalNfcDepDevice* nfcDepDev; /*!< Pointer to NFC-DEP device info */ - - uint32_t RTOXTimer; /*!< Timer used for RTOX */ - rfalNfcDepDeactCallback isDeactivating; /*!< Deactivating flag check callback */ - - bool isReqPending; /*!< Flag pending REQ from Target activation */ - bool isTxPending; /*!< Flag pending DEP Block while waiting RTOX Ack */ - bool isWait4RTOX; /*!< Flag for waiting RTOX Ack */ - - rfalNfcDepPduTxRxParam PDUParam; /*!< PDU TxRx params */ - uint16_t PDUTxPos; /*!< PDU Tx position */ - uint16_t PDURxPos; /*!< PDU Rx position */ - bool isPDURxChaining; /*!< PDU Transceive chaining flag */ -} rfalNfcDep; - -/* - ****************************************************************************** - * LOCAL VARIABLES - ****************************************************************************** - */ - -static rfalNfcDep gNfcip; /*!< NFCIP module instance */ - -/* - ****************************************************************************** - * LOCAL FUNCTION PROTOTYPES - ****************************************************************************** - */ - -static ReturnCode nfcipTxRx( - rfalNfcDepCmd cmd, - uint8_t* txBuf, - uint32_t fwt, - uint8_t* paylBuf, - uint8_t paylBufLen, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rxActLen); -static ReturnCode nfcipTx( - rfalNfcDepCmd cmd, - uint8_t* txBuf, - uint8_t* paylBuf, - uint16_t paylLen, - uint8_t pfbData, - uint32_t fwt); -static ReturnCode nfcipDEPControlMsg(uint8_t pfb, uint8_t RTOX); -static ReturnCode nfcipInitiatorHandleDEP( - ReturnCode rxRes, - uint16_t rxLen, - uint16_t* outActRxLen, - bool* outIsChaining); -static ReturnCode - nfcipTargetHandleRX(ReturnCode rxRes, uint16_t* outActRxLen, bool* outIsChaining); -static ReturnCode nfcipTargetHandleActivation(rfalNfcDepDevice* nfcDepDev, uint8_t* outBRS); - -/*! - ****************************************************************************** - * \brief NFCIP Configure - * - * Configures the nfcip layer with the given configurations - * - * \param[in] cfg : nfcip configuration for following communication - ****************************************************************************** - */ -static void nfcipConfig(const rfalNfcDepConfigs* cfg); - -/*! - ****************************************************************************** - * \brief Set DEP parameters - * - * This method sets the parameters/configs for following Data Exchange - * Sets the nfcip module state according to the role it is configured - * - * - * \warning To be used only after proper Initiator/Target activation: - * nfcipTargetHandleActivation() or nfcipInitiatorActivate() has - * returned success - * - * This must be called before nfcipRun() in case of Target to pass - * rxBuffer - * - * Everytime some data needs to be transmitted call this to set it and - * call nfcipRun() until done or error - * - * \param[in] DEPParams : the parameters to be used during Data Exchange - ****************************************************************************** - */ -static void nfcipSetDEPParams(const rfalNfcDepDEPParams* DEPParams); - -/*! - ****************************************************************************** - * \brief NFCIP run protocol - * - * This method handles all the nfcip protocol during Data Exchange (DEP - * requests and responses). - * - * A data exchange cycle is considered a DEP REQ and a DEP RES. - * - * In case of Tx chaining(MI) must signal it with nfcipSetDEPParams() - * In case of Rx chaining(MI) outIsChaining will be set to true and the - * current data returned - * - * \param[out] outActRxLen : data received length - * \param[out] outIsChaining : true if other peer is performing chaining(MI) - * - * \return ERR_NONE : Data exchange cycle completed successfully - * \return ERR_TIMEOUT : Timeout occurred - * \return ERR_PROTO : Protocol error occurred - * \return ERR_AGAIN : Other peer is doing chaining(MI), current block - * was received successfully call again until complete - * - ****************************************************************************** - */ -static ReturnCode nfcipRun(uint16_t* outActRxLen, bool* outIsChaining); - -/*! - ****************************************************************************** - * \brief Transmission method - * - * This method checks if the current communication is Active or Passive - * and performs the necessary procedures for each communication type - * - * Transmits the data hold in txBuf - * - * \param[in] txBuf : buffer to transmit - * \param[in] txBufLen : txBuffer capacity - * \param[in] fwt : fwt for current Tx - * - * \return ERR_NONE : No error - ****************************************************************************** - */ -static ReturnCode nfcipDataTx(uint8_t* txBuf, uint16_t txBufLen, uint32_t fwt); - -/*! - ****************************************************************************** - * \brief Reception method - * - * This method checks if the current communication is Active or Passive - * and calls the appropriate reception method - * - * Copies incoming data to rxBuf - * - * \param[in] blocking : reception is to be done blocking or non-blocking - * - * \return ERR_BUSY : Busy - * \return ERR_NONE : No error - ****************************************************************************** - */ -static ReturnCode nfcipDataRx(bool blocking); - -/* - ****************************************************************************** - * LOCAL FUNCTIONS - ****************************************************************************** - */ - -/*******************************************************************************/ - -/*******************************************************************************/ -static bool nfcipDxIsSupported(uint8_t Dx, uint8_t BRx, uint8_t BSx) { - uint8_t Bx; - - /* Take the min of the possible bit rates, we'll use one for both directions */ - Bx = MIN(BRx, BSx); - - /* Lower bit rates must be supported for P2P */ - if((Dx <= (uint8_t)RFAL_NFCDEP_Dx_04_424)) { - return true; - } - - if((Dx == (uint8_t)RFAL_NFCDEP_Dx_08_848) && (Bx >= (uint8_t)RFAL_NFCDEP_Bx_08_848)) { - return true; - } - - return false; -} - -/*******************************************************************************/ -static ReturnCode nfcipTxRx( - rfalNfcDepCmd cmd, - uint8_t* txBuf, - uint32_t fwt, - uint8_t* paylBuf, - uint8_t paylBufLen, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rxActLen) { - ReturnCode ret; - - if((cmd == NFCIP_CMD_DEP_REQ) || - (cmd == NFCIP_CMD_DEP_RES)) /* this method cannot be used for DEPs */ - { - return ERR_PARAM; - } - - /* Assign the global params for this TxRx */ - gNfcip.rxBuf = rxBuf; - gNfcip.rxBufLen = rxBufLen; - gNfcip.rxRcvdLen = rxActLen; - - /*******************************************************************************/ - /* Transmission */ - /*******************************************************************************/ - if(txBuf != NULL) /* if nothing to Tx, just do Rx */ - { - EXIT_ON_ERR(ret, nfcipTx(cmd, txBuf, paylBuf, paylBufLen, 0, fwt)); - } - - /*******************************************************************************/ - /* Reception */ - /*******************************************************************************/ - ret = nfcipDataRx(true); - if(ret != ERR_NONE) { - return ret; - } - - /*******************************************************************************/ - *rxActLen = *rxBuf; /* Use LEN byte instead due to with/without CRC modes */ - return ERR_NONE; /* Tx and Rx completed successfully */ -} - -/*******************************************************************************/ -static ReturnCode nfcipDEPControlMsg(uint8_t pfb, uint8_t RTOX) { - uint8_t ctrlMsg[20]; - rfalNfcDepCmd depCmd; - uint32_t fwt; - - /*******************************************************************************/ - /* Calculate Cmd and fwt to be used */ - /*******************************************************************************/ - depCmd = - ((gNfcip.cfg.role == RFAL_NFCDEP_ROLE_TARGET) ? NFCIP_CMD_DEP_RES : NFCIP_CMD_DEP_REQ); - fwt = - ((gNfcip.cfg.role == RFAL_NFCDEP_ROLE_TARGET) ? - NFCIP_NO_FWT : - (nfcip_PFBisSTO(pfb) ? ((RTOX * gNfcip.cfg.fwt) + gNfcip.cfg.dFwt) : - (gNfcip.cfg.fwt + gNfcip.cfg.dFwt))); - - if(nfcip_PFBisSTO(pfb)) { - ctrlMsg[RFAL_NFCDEP_DEPREQ_HEADER_LEN] = RTOX; - return nfcipTx( - depCmd, ctrlMsg, &ctrlMsg[RFAL_NFCDEP_DEPREQ_HEADER_LEN], sizeof(uint8_t), pfb, fwt); - } else { - return nfcipTx(depCmd, ctrlMsg, NULL, 0, pfb, fwt); - } -} - -/*******************************************************************************/ -static void nfcipClearCounters(void) { - gNfcip.cntATNRetrys = 0; - gNfcip.cntNACKRetrys = 0; - gNfcip.cntTORetrys = 0; - gNfcip.cntTxRetrys = 0; - gNfcip.cntRTOXRetrys = 0; -} - -/*******************************************************************************/ -static ReturnCode nfcipInitiatorHandleDEP( - ReturnCode rxRes, - uint16_t rxLen, - uint16_t* outActRxLen, - bool* outIsChaining) { - ReturnCode ret; - uint8_t nfcDepLen; - uint8_t rxMsgIt; - uint8_t rxPFB; - uint8_t rxRTOX; - uint8_t optHdrLen; - - ret = ERR_INTERNAL; - rxMsgIt = 0; - optHdrLen = 0; - - *outActRxLen = 0; - *outIsChaining = false; - - /*******************************************************************************/ - /* Handle reception errors */ - /*******************************************************************************/ - switch(rxRes) { - /*******************************************************************************/ - /* Timeout -> Digital 1.0 14.15.5.6 */ - case ERR_TIMEOUT: - - nfcipLogI(" NFCIP(I) TIMEOUT TORetrys:%d \r\n", gNfcip.cntTORetrys); - - /* Digital 1.0 14.15.5.6 - If nTO >= Max raise protocol error */ - if(gNfcip.cntTORetrys++ >= RFAL_NFCDEP_TO_RETRYS) { - return ERR_PROTO; - } - - /*******************************************************************************/ - /* Upon Timeout error, if Deactivation is pending, no more error recovery - * will be done #54. - * This is used to address the issue some devices that havea big TO. - * Normally LLCP layer has timeout already, and NFCIP layer is still - * running error handling, retrying ATN/NACKs */ - /*******************************************************************************/ - if(nfcipIsDeactivationPending()) { - nfcipLogI(" skipping error recovery due deactivation pending \r\n"); - return ERR_TIMEOUT; - } - - /* Digital 1.0 14.15.5.6 1) If last PDU was NACK */ - if(nfcip_PFBisRNACK(gNfcip.lastPFB)) { - /* Digital 1.0 14.15.5.6 2) if NACKs failed raise protocol error */ - if(gNfcip.cntNACKRetrys++ >= RFAL_NFCDEP_MAX_NACK_RETRYS) { - return ERR_PROTO; - } - - /* Send NACK */ - nfcipLogI(" NFCIP(I) Sending NACK retry: %d \r\n", gNfcip.cntNACKRetrys); - EXIT_ON_ERR(ret, nfcipDEPControlMsg(nfcip_PFBRPDU_NACK(gNfcip.pni), 0)); - return ERR_BUSY; - } - - nfcipLogI(" NFCIP(I) Checking if to send ATN ATNRetrys: %d \r\n", gNfcip.cntATNRetrys); - - /* Digital 1.0 14.15.5.6 3) Otherwise send ATN */ - if(gNfcip.cntATNRetrys++ >= RFAL_NFCDEP_MAX_NACK_RETRYS) { - return ERR_PROTO; - } - - /* Send ATN */ - nfcipLogI(" NFCIP(I) Sending ATN \r\n"); - EXIT_ON_ERR(ret, nfcipDEPControlMsg(nfcip_PFBSPDU_ATN(), 0)); - return ERR_BUSY; - - /*******************************************************************************/ - /* Data rcvd with error -> Digital 1.0 14.12.5.4 */ - case ERR_CRC: - case ERR_PAR: - case ERR_FRAMING: - case ERR_RF_COLLISION: - - nfcipLogI(" NFCIP(I) rx Error: %d \r\n", rxRes); - - /* Digital 1.0 14.12.5.4 Tx Error with data, ignore */ - if(rxLen < NFCIP_MIN_TXERROR_LEN) { - nfcipLogI(" NFCIP(I) Transmission error w data \r\n"); -#if 0 - if(gNfcip.cfg.commMode == RFAL_NFCDEP_COMM_PASSIVE) - { - nfcipLogI( " NFCIP(I) Transmission error w data -> reEnabling Rx \r\n" ); - nfcipReEnableRxTout( NFCIP_TRECOV ); - return ERR_BUSY; - } -#endif /* 0 */ - } - - /* Digital 1.1 16.12.5.4 if NACKs failed raise Transmission error */ - if(gNfcip.cntNACKRetrys++ >= RFAL_NFCDEP_MAX_NACK_RETRYS) { - return ERR_FRAMING; - } - - /* Send NACK */ - nfcipLogI(" NFCIP(I) Sending NACK \r\n"); - EXIT_ON_ERR(ret, nfcipDEPControlMsg(nfcip_PFBRPDU_NACK(gNfcip.pni), 0)); - return ERR_BUSY; - - case ERR_NONE: - break; - - case ERR_BUSY: - return ERR_BUSY; /* Debug purposes */ - - default: - nfcipLogW(" NFCIP(I) Error: %d \r\n", rxRes); - return rxRes; - } - - /*******************************************************************************/ - /* Rx OK check if valid DEP PDU */ - /*******************************************************************************/ - - /* Due to different modes on ST25R391x (with/without CRC) use NFC-DEP LEN instead of bytes retrieved */ - nfcDepLen = gNfcip.rxBuf[rxMsgIt++]; - - nfcipLogD(" NFCIP(I) rx OK: %d bytes \r\n", nfcDepLen); - - /* Digital 1.0 14.15.5.5 Protocol Error */ - if(gNfcip.rxBuf[rxMsgIt++] != NFCIP_RES) { - nfcipLogW(" NFCIP(I) error %02X instead of %02X \r\n", gNfcip.rxBuf[--rxMsgIt], NFCIP_RES); - return ERR_PROTO; - } - - /* Digital 1.0 14.15.5.5 Protocol Error */ - if(gNfcip.rxBuf[rxMsgIt++] != (uint8_t)NFCIP_CMD_DEP_RES) { - nfcipLogW( - " NFCIP(I) error %02X instead of %02X \r\n", - gNfcip.rxBuf[--rxMsgIt], - NFCIP_CMD_DEP_RES); - return ERR_PROTO; - } - - rxPFB = gNfcip.rxBuf[rxMsgIt++]; - - /*******************************************************************************/ - /* Check for valid PFB type */ - if(!(nfcip_PFBisSPDU(rxPFB) || nfcip_PFBisRPDU(rxPFB) || nfcip_PFBisIPDU(rxPFB))) { - return ERR_PROTO; - } - - /*******************************************************************************/ - /* Digital 1.0 14.8.2.1 check if DID is expected and match -> Protocol Error */ - if(gNfcip.cfg.did != RFAL_NFCDEP_DID_NO) { - if((gNfcip.rxBuf[rxMsgIt++] != gNfcip.cfg.did) || !nfcip_PFBhasDID(rxPFB)) { - return ERR_PROTO; - } - optHdrLen++; /* Inc header optional field cnt*/ - } else if(nfcip_PFBhasDID(rxPFB)) /* DID not expected but rcv */ - { - return ERR_PROTO; - } else { - /* MISRA 15.7 - Empty else */ - } - - /*******************************************************************************/ - /* Digital 1.0 14.6.2.8 & 14.6.3.11 NAD must not be used */ - if(gNfcip.cfg.nad != RFAL_NFCDEP_NAD_NO) { - if((gNfcip.rxBuf[rxMsgIt++] != gNfcip.cfg.nad) || !nfcip_PFBhasNAD(rxPFB)) { - return ERR_PROTO; - } - optHdrLen++; /* Inc header optional field cnt*/ - } else if(nfcip_PFBhasNAD(rxPFB)) /* NAD not expected but rcv */ - { - return ERR_PROTO; - } else { - /* MISRA 15.7 - Empty else */ - } - - /*******************************************************************************/ - /* Process R-PDU */ - /*******************************************************************************/ - if(nfcip_PFBisRPDU(rxPFB)) { - /*******************************************************************************/ - /* R ACK */ - /*******************************************************************************/ - if(nfcip_PFBisRACK(rxPFB)) { - nfcipLogI(" NFCIP(I) Rcvd ACK \r\n"); - if(gNfcip.pni == nfcip_PBF_PNI(rxPFB)) { - /* 14.12.3.3 R-ACK with correct PNI -> Increment */ - gNfcip.pni = nfcip_PNIInc(gNfcip.pni); - - /* R-ACK while not performing chaining -> Protocol error*/ - if(!gNfcip.isTxChaining) { - return ERR_PROTO; - } - - nfcipClearCounters(); - gNfcip.state = NFCIP_ST_INIT_DEP_IDLE; - return ERR_NONE; /* This block has been transmitted */ - } else /* Digital 1.0 14.12.4.5 ACK with wrong PNI Initiator may retransmit */ - { - if(gNfcip.cntTxRetrys++ >= RFAL_NFCDEP_MAX_TX_RETRYS) { - return ERR_PROTO; - } - - /* Extended the MAY in Digital 1.0 14.12.4.5 to only reTransmit if the ACK - * is for the previous DEP, otherwise raise Protocol immediately - * If the PNI difference is more than 1 it is worthless to reTransmit 3x - * and after raise the error */ - - if(nfcip_PNIDec(gNfcip.pni) == nfcip_PBF_PNI(rxPFB)) { - /* ReTransmit */ - nfcipLogI(" NFCIP(I) Rcvd ACK prev PNI -> reTx \r\n"); - gNfcip.state = NFCIP_ST_INIT_DEP_TX; - return ERR_BUSY; - } - - nfcipLogI(" NFCIP(I) Rcvd ACK unexpected far PNI -> Error \r\n"); - return ERR_PROTO; - } - } else /* Digital 1.0 - 14.12.5.2 Target must never send NACK */ - { - return ERR_PROTO; - } - } - - /*******************************************************************************/ - /* Process S-PDU */ - /*******************************************************************************/ - if(nfcip_PFBisSPDU(rxPFB)) { - nfcipLogI(" NFCIP(I) Rcvd S-PDU \r\n"); - /*******************************************************************************/ - /* S ATN */ - /*******************************************************************************/ - if(nfcip_PFBisSATN(rxPFB)) /* If is a S-ATN */ - { - nfcipLogI(" NFCIP(I) Rcvd ATN \r\n"); - if(nfcip_PFBisSATN(gNfcip.lastPFB)) /* Check if is expected */ - { - gNfcip.cntATNRetrys = 0; /* Clear ATN counter */ - - /* Although spec is not clear NFC Forum Digital test is expecting to - * retransmit upon receiving ATN_RES */ - if(nfcip_PFBisSTO(gNfcip.lastPFBnATN)) { - nfcipLogI(" NFCIP(I) Rcvd ATN -> reTx RTOX_RES \r\n"); - EXIT_ON_ERR(ret, nfcipDEPControlMsg(nfcip_PFBSPDU_TO(), gNfcip.lastRTOX)); - } else { - /* ReTransmit ? */ - if(gNfcip.cntTxRetrys++ >= RFAL_NFCDEP_MAX_TX_RETRYS) { - return ERR_PROTO; - } - - nfcipLogI(" NFCIP(I) Rcvd ATN -> reTx PNI: %d \r\n", gNfcip.pni); - gNfcip.state = NFCIP_ST_INIT_DEP_TX; - } - - return ERR_BUSY; - } else /* Digital 1.0 14.12.4.4 & 14.12.4.8 */ - { - return ERR_PROTO; - } - } - /*******************************************************************************/ - /* S TO */ - /*******************************************************************************/ - else if(nfcip_PFBisSTO(rxPFB)) /* If is a S-TO (RTOX) */ - { - nfcipLogI(" NFCIP(I) Rcvd TO \r\n"); - - rxRTOX = gNfcip.rxBuf[rxMsgIt++]; - - /* Digital 1.1 16.12.4.3 - Initiator MAY stop accepting subsequent RTOX Req * - * - RTOX request to an ATN -> Protocol error */ - if((gNfcip.cntRTOXRetrys++ > RFAL_NFCDEP_MAX_RTOX_RETRYS) || - nfcip_PFBisSATN(gNfcip.lastPFB)) { - return ERR_PROTO; - } - - /* Digital 1.1 16.8.4.1 RTOX must be between [1,59] */ - if((rxRTOX < NFCIP_INIT_MIN_RTOX) || (rxRTOX > NFCIP_INIT_MAX_RTOX)) { - return ERR_PROTO; - } - - EXIT_ON_ERR(ret, nfcipDEPControlMsg(nfcip_PFBSPDU_TO(), rxRTOX)); - gNfcip.lastRTOX = rxRTOX; - - return ERR_BUSY; - } else { - /* Unexpected S-PDU */ - return ERR_PROTO; /* PRQA S 2880 # MISRA 2.1 - Guard code to prevent unexpected behavior */ - } - } - - /*******************************************************************************/ - /* Process I-PDU */ - /*******************************************************************************/ - if(nfcip_PFBisIPDU(rxPFB)) { - if(gNfcip.pni != nfcip_PBF_PNI(rxPFB)) { - nfcipLogI( - " NFCIP(I) Rcvd IPDU wrong PNI curPNI: %d rxPNI: %d \r\n", - gNfcip.pni, - nfcip_PBF_PNI(rxPFB)); - return ERR_PROTO; - } - - nfcipLogD(" NFCIP(I) Rcvd IPDU OK PNI: %d \r\n", gNfcip.pni); - - /* 14.12.3.3 I-PDU with correct PNI -> Increment */ - gNfcip.pni = nfcip_PNIInc(gNfcip.pni); - - /* Successful data Exchange */ - nfcipClearCounters(); - *outActRxLen = ((uint16_t)nfcDepLen - RFAL_NFCDEP_DEP_HEADER - (uint16_t)optHdrLen); - - if((&gNfcip.rxBuf[gNfcip.rxBufPaylPos] != - &gNfcip.rxBuf[RFAL_NFCDEP_DEP_HEADER + optHdrLen]) && - (*outActRxLen > 0U)) { - ST_MEMMOVE( - &gNfcip.rxBuf[gNfcip.rxBufPaylPos], - &gNfcip.rxBuf[RFAL_NFCDEP_DEP_HEADER + optHdrLen], - *outActRxLen); - } - - /*******************************************************************************/ - /* Check if target is indicating chaining MI */ - /*******************************************************************************/ - if(nfcip_PFBisIMI(rxPFB)) { - gNfcip.isRxChaining = true; - *outIsChaining = true; - - nfcipLogD(" NFCIP(I) Rcvd IPDU OK w MI -> ACK \r\n"); - EXIT_ON_ERR( - ret, nfcipDEPControlMsg(nfcip_PFBRPDU_ACK(gNfcip.pni), gNfcip.rxBuf[rxMsgIt++])); - - return ERR_AGAIN; /* Send Again signalling to run again, but some chaining data has arrived*/ - } else { - gNfcip.isRxChaining = false; - gNfcip.state = NFCIP_ST_INIT_DEP_IDLE; - - ret = ERR_NONE; /* Data exchange done */ - } - } - return ret; -} - -/*******************************************************************************/ -static ReturnCode - nfcipTargetHandleRX(ReturnCode rxRes, uint16_t* outActRxLen, bool* outIsChaining) { - ReturnCode ret; - uint8_t nfcDepLen; - uint8_t rxMsgIt; - uint8_t rxPFB; - uint8_t optHdrLen; - uint8_t resBuf[RFAL_NFCDEP_HEADER_PAD + NFCIP_TARGET_RES_MAX]; - - ret = ERR_INTERNAL; - rxMsgIt = 0; - optHdrLen = 0; - - *outActRxLen = 0; - *outIsChaining = false; - - /*******************************************************************************/ - /* Handle reception errors */ - /*******************************************************************************/ - switch(rxRes) { - /*******************************************************************************/ - case ERR_NONE: - break; - - case ERR_LINK_LOSS: - nfcipLogW(" NFCIP(T) Error: %d \r\n", rxRes); - return rxRes; - - case ERR_BUSY: - return ERR_BUSY; /* Debug purposes */ - - case ERR_TIMEOUT: - case ERR_CRC: - case ERR_PAR: - case ERR_FRAMING: - case ERR_PROTO: - default: - /* Digital 1.1 16.12.5.2 The Target MUST NOT attempt any error recovery. * - * The Target MUST always stay in receive mode when a * - * Transmission Error or a Protocol Error occurs. * - * * - * Do not push Transmission/Protocol Errors to upper layer in Listen Mode #766 */ - - nfcDepReEnableRx(gNfcip.rxBuf, gNfcip.rxBufLen, gNfcip.rxRcvdLen); - return ERR_BUSY; - } - - /*******************************************************************************/ - /* Rx OK check if valid DEP PDU */ - /*******************************************************************************/ - - /* Due to different modes on ST25R391x (with/without CRC) use NFC-DEP LEN instead of bytes retrieved */ - nfcDepLen = gNfcip.rxBuf[rxMsgIt++]; - - nfcipLogD(" NFCIP(T) rx OK: %d bytes \r\n", nfcDepLen); - - if(gNfcip.rxBuf[rxMsgIt++] != NFCIP_REQ) { - nfcDepReEnableRx(gNfcip.rxBuf, gNfcip.rxBufLen, gNfcip.rxRcvdLen); - return ERR_BUSY; /* ERR_PROTO - Ignore bad request */ - } - - /*******************************************************************************/ - /* Check whether target rcvd a normal DEP or deactivation request */ - /*******************************************************************************/ - switch(gNfcip.rxBuf[rxMsgIt++]) { - /*******************************************************************************/ - case(uint8_t)NFCIP_CMD_DEP_REQ: - break; /* Continue to normal DEP processing */ - - /*******************************************************************************/ - case(uint8_t)NFCIP_CMD_DSL_REQ: - - nfcipLogI(" NFCIP(T) rx DSL \r\n"); - - /* Digital 1.0 14.9.1.2 If DID is used and incorrect ignore it */ - /* [Digital 1.0, 16.9.1.2]: If DID == 0, Target SHALL ignore DSL_REQ with DID */ - if((((gNfcip.rxBuf[rxMsgIt++] != gNfcip.cfg.did) || - (nfcDepLen != RFAL_NFCDEP_DSL_RLS_LEN_DID)) && - (gNfcip.cfg.did != RFAL_NFCDEP_DID_NO)) || - ((gNfcip.cfg.did == RFAL_NFCDEP_DID_NO) && - (nfcDepLen != RFAL_NFCDEP_DSL_RLS_LEN_NO_DID))) { - nfcipLogI(" NFCIP(T) DSL wrong DID, ignoring \r\n"); - return ERR_BUSY; - } - - nfcipTx(NFCIP_CMD_DSL_RES, resBuf, NULL, 0, 0, NFCIP_NO_FWT); - - gNfcip.state = NFCIP_ST_TARG_DEP_SLEEP; - return ERR_SLEEP_REQ; - - /*******************************************************************************/ - case(uint8_t)NFCIP_CMD_RLS_REQ: - - nfcipLogI(" NFCIP(T) rx RLS \r\n"); - - /* Digital 1.0 14.10.1.2 If DID is used and incorrect ignore it */ - /* [Digital 1.0, 16.10.2.2]: If DID == 0, Target SHALL ignore DSL_REQ with DID */ - if((((gNfcip.rxBuf[rxMsgIt++] != gNfcip.cfg.did) || - (nfcDepLen != RFAL_NFCDEP_DSL_RLS_LEN_DID)) && - (gNfcip.cfg.did != RFAL_NFCDEP_DID_NO)) || - ((gNfcip.cfg.did == RFAL_NFCDEP_DID_NO) && - (nfcDepLen > RFAL_NFCDEP_DSL_RLS_LEN_NO_DID))) { - nfcipLogI(" NFCIP(T) RLS wrong DID, ignoring \r\n"); - return ERR_BUSY; - } - - nfcipTx(NFCIP_CMD_RLS_RES, resBuf, NULL, 0, 0, NFCIP_NO_FWT); - - gNfcip.state = NFCIP_ST_TARG_DEP_IDLE; - return ERR_RELEASE_REQ; - - /*******************************************************************************/ - /*case NFCIP_CMD_PSL_REQ: PSL must be handled in Activation only */ - /*case NFCIP_CMD_WUP_REQ: WUP not in NFC Forum Digital 1.0 */ - default: - - /* Don't go to NFCIP_ST_TARG_DEP_IDLE state as it needs to ignore this * - * invalid frame, and keep waiting for more frames */ - - nfcDepReEnableRx(gNfcip.rxBuf, gNfcip.rxBufLen, gNfcip.rxRcvdLen); - return ERR_BUSY; /* ERR_PROTO - Ignore bad frame */ - } - - /*******************************************************************************/ - - rxPFB = gNfcip.rxBuf[rxMsgIt++]; /* Store rcvd PFB */ - - /*******************************************************************************/ - /* Check for valid PFB type */ - if(!(nfcip_PFBisSPDU(rxPFB) || nfcip_PFBisRPDU(rxPFB) || nfcip_PFBisIPDU(rxPFB))) { - nfcDepReEnableRx(gNfcip.rxBuf, gNfcip.rxBufLen, gNfcip.rxRcvdLen); - return ERR_BUSY; /* ERR_PROTO - Ignore invalid PFB */ - } - - /*******************************************************************************/ - if(gNfcip.cfg.did != RFAL_NFCDEP_DID_NO) { - if(!nfcip_PFBhasDID(rxPFB)) { - nfcDepReEnableRx(gNfcip.rxBuf, gNfcip.rxBufLen, gNfcip.rxRcvdLen); - return ERR_BUSY; /* ERR_PROTO - Ignore bad/missing DID */ - } - if(gNfcip.rxBuf[rxMsgIt++] != gNfcip.cfg.did) /* MISRA 13.5 */ - { - nfcDepReEnableRx(gNfcip.rxBuf, gNfcip.rxBufLen, gNfcip.rxRcvdLen); - return ERR_BUSY; /* ERR_PROTO - Ignore bad/missing DID */ - } - optHdrLen++; /* Inc header optional field cnt*/ - } else if(nfcip_PFBhasDID(rxPFB)) /* DID not expected but rcv */ - { - nfcDepReEnableRx(gNfcip.rxBuf, gNfcip.rxBufLen, gNfcip.rxRcvdLen); - return ERR_BUSY; /* ERR_PROTO - Ignore unexpected DID */ - } else { - /* MISRA 15.7 - Empty else */ - } - - /*******************************************************************************/ - if(gNfcip.cfg.nad != RFAL_NFCDEP_NAD_NO) { - if((gNfcip.rxBuf[rxMsgIt++] != gNfcip.cfg.did) || !nfcip_PFBhasDID(rxPFB)) { - nfcDepReEnableRx(gNfcip.rxBuf, gNfcip.rxBufLen, gNfcip.rxRcvdLen); - return ERR_BUSY; /* ERR_PROTO - Ignore bad/missing DID */ - } - optHdrLen++; /* Inc header optional field cnt*/ - } else if(nfcip_PFBhasNAD(rxPFB)) /* NAD not expected but rcv */ - { - nfcDepReEnableRx(gNfcip.rxBuf, gNfcip.rxBufLen, gNfcip.rxRcvdLen); - return ERR_BUSY; /* ERR_PROTO - Ignore unexpected NAD */ - } else { - /* MISRA 15.7 - Empty else */ - } - - /*******************************************************************************/ - /* Process R-PDU */ - /*******************************************************************************/ - if(nfcip_PFBisRPDU(rxPFB)) { - nfcipLogD(" NFCIP(T) Rcvd R-PDU \r\n"); - /*******************************************************************************/ - /* R ACK */ - /*******************************************************************************/ - if(nfcip_PFBisRACK(rxPFB)) { - nfcipLogI(" NFCIP(T) Rcvd ACK \r\n"); - if(gNfcip.pni == nfcip_PBF_PNI(rxPFB)) { - /* R-ACK while not performing chaining -> Protocol error */ - if(!gNfcip.isTxChaining) { - nfcDepReEnableRx(gNfcip.rxBuf, gNfcip.rxBufLen, gNfcip.rxRcvdLen); - return ERR_BUSY; /* ERR_PROTO - Ignore unexpected ACK */ - } - - /* This block has been transmitted and acknowledged, perform RTOX until next data is provided */ - - /* Digital 1.1 16.12.4.7 - If ACK rcvd continue with chaining or an RTOX */ - nfcipTimerStart( - gNfcip.RTOXTimer, - nfcipRTOXAdjust(nfcipConv1FcToMs(rfalNfcDepWT2RWT(gNfcip.cfg.to)))); - gNfcip.state = NFCIP_ST_TARG_DEP_RTOX; - - return ERR_NONE; /* This block has been transmitted */ - } - - /* Digital 1.0 14.12.3.4 - If last send was ATN and rx PNI is minus 1 */ - else if( - nfcip_PFBisSATN(gNfcip.lastPFB) && - (nfcip_PNIDec(gNfcip.pni) == nfcip_PBF_PNI(rxPFB))) { - nfcipLogI(" NFCIP(T) wrong PNI, last was ATN reTx \r\n"); - /* Spec says to leave current PNI as is, but will be Inc after Tx, remaining the same */ - gNfcip.pni = nfcip_PNIDec(gNfcip.pni); - - gNfcip.state = NFCIP_ST_TARG_DEP_TX; - return ERR_BUSY; - } else { - /* MISRA 15.7 - Empty else */ - } - } - /*******************************************************************************/ - /* R NACK */ - /*******************************************************************************/ - /* ISO 18092 12.6.1.3.3 When rcv NACK if PNI = prev PNI sent -> reTx */ - else if(nfcip_PFBisRNACK(rxPFB) && (nfcip_PNIDec(gNfcip.pni) == nfcip_PBF_PNI(rxPFB))) { - nfcipLogI(" NFCIP(T) Rcvd NACK \r\n"); - - gNfcip.pni = nfcip_PNIDec(gNfcip.pni); /* Dec so that has the prev PNI */ - - gNfcip.state = NFCIP_ST_TARG_DEP_TX; - return ERR_BUSY; - } else { - nfcipLogI(" NFCIP(T) Unexpected R-PDU \r\n"); - - nfcDepReEnableRx(gNfcip.rxBuf, gNfcip.rxBufLen, gNfcip.rxRcvdLen); - return ERR_BUSY; /* ERR_PROTO - Ignore unexpected R-PDU */ - } - } - - /*******************************************************************************/ - /* Process S-PDU */ - /*******************************************************************************/ - if(nfcip_PFBisSPDU(rxPFB)) { - nfcipLogD(" NFCIP(T) Rcvd S-PDU \r\n"); - - /*******************************************************************************/ - /* S ATN */ - /*******************************************************************************/ - /* ISO 18092 12.6.3 Attention */ - if(nfcip_PFBisSATN(rxPFB)) /* If is a S-ATN */ - { - nfcipLogI(" NFCIP(T) Rcvd ATN curPNI: %d \r\n", gNfcip.pni); - EXIT_ON_ERR(ret, nfcipDEPControlMsg(nfcip_PFBSPDU_ATN(), 0)); - return ERR_BUSY; - } - - /*******************************************************************************/ - /* S TO */ - /*******************************************************************************/ - else if(nfcip_PFBisSTO(rxPFB)) /* If is a S-TO (RTOX) */ - { - if(nfcip_PFBisSTO(gNfcip.lastPFBnATN)) { - nfcipLogI(" NFCIP(T) Rcvd TO \r\n"); - - /* Digital 1.1 16.8.4.6 RTOX value in RES different that in REQ -> Protocol Error */ - if(gNfcip.lastRTOX != gNfcip.rxBuf[rxMsgIt++]) { - nfcipLogI(" NFCIP(T) Mismatched RTOX value \r\n"); - - nfcDepReEnableRx(gNfcip.rxBuf, gNfcip.rxBufLen, gNfcip.rxRcvdLen); - return ERR_BUSY; /* ERR_PROTO - Ignore unexpected RTOX value */ - } - - /* Clear waiting for RTOX Ack Flag */ - gNfcip.isWait4RTOX = false; - - /* Check if a Tx is already pending */ - if(gNfcip.isTxPending) { - nfcipLogW(" NFCIP(T) Tx pending, go immediately to TX \r\n"); - - gNfcip.state = NFCIP_ST_TARG_DEP_TX; - return ERR_BUSY; - } - - /* Start RTOX timer and change to check state */ - nfcipTimerStart( - gNfcip.RTOXTimer, - nfcipRTOXAdjust( - nfcipConv1FcToMs(gNfcip.lastRTOX * rfalNfcDepWT2RWT(gNfcip.cfg.to)))); - gNfcip.state = NFCIP_ST_TARG_DEP_RTOX; - - return ERR_BUSY; - } - } else { - /* Unexpected S-PDU */ - nfcipLogI( - " NFCIP(T) Unexpected S-PDU \r\n"); /* PRQA S 2880 # MISRA 2.1 - Guard code to prevent unexpected behavior */ - - nfcDepReEnableRx(gNfcip.rxBuf, gNfcip.rxBufLen, gNfcip.rxRcvdLen); - return ERR_BUSY; /* ERR_PROTO - Ignore unexpected S-PDU */ - } - } - - /*******************************************************************************/ - /* Process I-PDU */ - /*******************************************************************************/ - if(nfcip_PFBisIPDU(rxPFB)) { - if(gNfcip.pni != nfcip_PBF_PNI(rxPFB)) { - nfcipLogI( - " NFCIP(T) Rcvd IPDU wrong PNI curPNI: %d rxPNI: %d \r\n", - gNfcip.pni, - nfcip_PBF_PNI(rxPFB)); - - /* Digital 1.1 16.12.3.4 - If last send was ATN and rx PNI is minus 1 */ - if(nfcip_PFBisSATN(gNfcip.lastPFB) && - (nfcip_PNIDec(gNfcip.pni) == nfcip_PBF_PNI(rxPFB))) { - /* Spec says to leave current PNI as is, but will be Inc after Data Tx, remaining the same */ - gNfcip.pni = nfcip_PNIDec(gNfcip.pni); - - if(nfcip_PFBisIMI(rxPFB)) { - nfcipLogI( - " NFCIP(T) PNI = prevPNI && ATN before && chaining -> send ACK \r\n"); - EXIT_ON_ERR( - ret, - nfcipDEPControlMsg(nfcip_PFBRPDU_ACK(gNfcip.pni), gNfcip.rxBuf[rxMsgIt++])); - - /* Digital 1.1 16.12.3.4 (...) leave the current PNI unchanged afterwards */ - gNfcip.pni = nfcip_PNIInc(gNfcip.pni); - } else { - nfcipLogI(" NFCIP(T) PNI = prevPNI && ATN before -> reTx last I-PDU \r\n"); - gNfcip.state = NFCIP_ST_TARG_DEP_TX; - } - - return ERR_BUSY; - } - - nfcDepReEnableRx(gNfcip.rxBuf, gNfcip.rxBufLen, gNfcip.rxRcvdLen); - return ERR_BUSY; /* ERR_PROTO - Ignore bad PNI value */ - } - - nfcipLogD(" NFCIP(T) Rcvd IPDU OK PNI: %d \r\n", gNfcip.pni); - - /*******************************************************************************/ - /* Successful data exchange */ - /*******************************************************************************/ - *outActRxLen = ((uint16_t)nfcDepLen - RFAL_NFCDEP_DEP_HEADER - (uint16_t)optHdrLen); - - nfcipClearCounters(); - - if((&gNfcip.rxBuf[gNfcip.rxBufPaylPos] != - &gNfcip.rxBuf[RFAL_NFCDEP_DEP_HEADER + optHdrLen]) && - (*outActRxLen > 0U)) { - ST_MEMMOVE( - &gNfcip.rxBuf[gNfcip.rxBufPaylPos], - &gNfcip.rxBuf[RFAL_NFCDEP_DEP_HEADER + optHdrLen], - *outActRxLen); - } - - /*******************************************************************************/ - /* Check if Initiator is indicating chaining MI */ - /*******************************************************************************/ - if(nfcip_PFBisIMI(rxPFB)) { - gNfcip.isRxChaining = true; - *outIsChaining = true; - - nfcipLogD(" NFCIP(T) Rcvd IPDU OK w MI -> ACK \r\n"); - EXIT_ON_ERR( - ret, nfcipDEPControlMsg(nfcip_PFBRPDU_ACK(gNfcip.pni), gNfcip.rxBuf[rxMsgIt++])); - - gNfcip.pni = nfcip_PNIInc(gNfcip.pni); - - return ERR_AGAIN; /* Send Again signalling to run again, but some chaining data has arrived*/ - } else { - if(gNfcip.isRxChaining) { - nfcipLogI(" NFCIP(T) Rcvd last IPDU chaining finished \r\n"); - } - - /*******************************************************************************/ - /* Reception done, send to DH and start RTOX timer */ - /*******************************************************************************/ - nfcipTimerStart( - gNfcip.RTOXTimer, - nfcipRTOXAdjust(nfcipConv1FcToMs(rfalNfcDepWT2RWT(gNfcip.cfg.to)))); - gNfcip.state = NFCIP_ST_TARG_DEP_RTOX; - - gNfcip.isRxChaining = false; - ret = ERR_NONE; /* Data exchange done */ - } - } - return ret; -} - -/*******************************************************************************/ -static ReturnCode nfcipTx( - rfalNfcDepCmd cmd, - uint8_t* txBuf, - uint8_t* paylBuf, - uint16_t paylLen, - uint8_t pfbData, - uint32_t fwt) { - uint16_t txBufIt; - uint8_t* txBlock; - uint8_t* payloadBuf; - uint8_t pfb; - - if(txBuf == NULL) { - return ERR_PARAM; - } - - payloadBuf = paylBuf; /* MISRA 17.8: Use intermediate variable */ - - if((paylLen == 0U) || (payloadBuf == NULL)) { - payloadBuf = (uint8_t*)&txBuf - [RFAL_NFCDEP_DEPREQ_HEADER_LEN]; /* If not a DEP (no Data) ensure enough space for header */ - } - - txBufIt = 0; - pfb = pfbData; /* MISRA 17.8: Use intermediate variable */ - - txBlock = payloadBuf; /* Point to beginning of the Data, and go backwards */ - - gNfcip.lastCmd = (uint8_t)cmd; /* Store last cmd sent */ - gNfcip.lastPFB = NFCIP_PFB_INVALID; /* Reset last pfb sent */ - - /*******************************************************************************/ - /* Compute outgoing NFCIP message */ - /*******************************************************************************/ - switch(cmd) { - /*******************************************************************************/ - case NFCIP_CMD_ATR_RES: - case NFCIP_CMD_ATR_REQ: - - rfalNfcDepSetNFCID(payloadBuf, gNfcip.cfg.nfcid, gNfcip.cfg.nfcidLen); /* NFCID */ - txBufIt += RFAL_NFCDEP_NFCID3_LEN; - - payloadBuf[txBufIt++] = gNfcip.cfg.did; /* DID */ - payloadBuf[txBufIt++] = gNfcip.cfg.bs; /* BS */ - payloadBuf[txBufIt++] = gNfcip.cfg.br; /* BR */ - - if(cmd == NFCIP_CMD_ATR_RES) { - payloadBuf[txBufIt++] = gNfcip.cfg.to; /* ATR_RES[ TO ] */ - } - - if(gNfcip.cfg.gbLen > 0U) { - payloadBuf[txBufIt++] = nfcip_PPwGB(gNfcip.cfg.lr); /* PP signalling GB */ - ST_MEMCPY( - &payloadBuf[txBufIt], gNfcip.cfg.gb, gNfcip.cfg.gbLen); /* set General Bytes */ - txBufIt += gNfcip.cfg.gbLen; - } else { - payloadBuf[txBufIt++] = rfalNfcDepLR2PP(gNfcip.cfg.lr); /* PP without GB */ - } - - if((txBufIt + RFAL_NFCDEP_CMDTYPE_LEN + RFAL_NFCDEP_CMD_LEN) > - RFAL_NFCDEP_ATRREQ_MAX_LEN) /* Check max ATR length (ATR_REQ = ATR_RES)*/ - { - return ERR_PARAM; - } - break; - - /*******************************************************************************/ - case NFCIP_CMD_WUP_REQ: /* ISO 18092 - 12.5.2.1 */ - - rfalNfcDepSetNFCID((payloadBuf), gNfcip.cfg.nfcid, gNfcip.cfg.nfcidLen); /* NFCID */ - txBufIt += RFAL_NFCDEP_NFCID3_LEN; - - *(--txBlock) = gNfcip.cfg.did; /* DID */ - break; - - /*******************************************************************************/ - case NFCIP_CMD_WUP_RES: /* ISO 18092 - 12.5.2.2 */ - case NFCIP_CMD_PSL_REQ: - case NFCIP_CMD_PSL_RES: - - *(--txBlock) = gNfcip.cfg.did; /* DID */ - break; - - /*******************************************************************************/ - case NFCIP_CMD_RLS_REQ: - case NFCIP_CMD_RLS_RES: - case NFCIP_CMD_DSL_REQ: - case NFCIP_CMD_DSL_RES: - - /* Digital 1.0 - 14.8.1.1 & 14.9.1.1 & 14.10.1.1 Only add DID if not 0 */ - if(gNfcip.cfg.did != RFAL_NFCDEP_DID_NO) { - *(--txBlock) = gNfcip.cfg.did; /* DID */ - } - break; - - /*******************************************************************************/ - case NFCIP_CMD_DEP_REQ: - case NFCIP_CMD_DEP_RES: - - /* Compute optional PFB bits */ - if(gNfcip.cfg.did != RFAL_NFCDEP_DID_NO) { - pfb |= NFCIP_PFB_DID_BIT; - } - if(gNfcip.cfg.nad != RFAL_NFCDEP_NAD_NO) { - pfb |= NFCIP_PFB_NAD_BIT; - } - if((gNfcip.isTxChaining) && (nfcip_PFBisIPDU(pfb))) { - pfb |= NFCIP_PFB_MI_BIT; - } - - /* Store PFB for future handling */ - gNfcip.lastPFB = pfb; /* store PFB sent */ - - if(!nfcip_PFBisSATN(pfb)) { - gNfcip.lastPFBnATN = pfb; /* store last PFB different then ATN */ - } - - /* Add NAD if it is to be supported */ - if(gNfcip.cfg.nad != RFAL_NFCDEP_NAD_NO) { - *(--txBlock) = gNfcip.cfg.nad; /* NAD */ - } - - /* Digital 1.0 - 14.8.1.1 & 14.8.1.1 Only add DID if not 0 */ - if(gNfcip.cfg.did != RFAL_NFCDEP_DID_NO) { - *(--txBlock) = gNfcip.cfg.did; /* DID */ - } - - *(--txBlock) = pfb; /* PFB */ - - /* NCI 1.0 - Check if Empty frames are allowed */ - if((paylLen == 0U) && nfcipIsEmptyDEPDisabled(gNfcip.cfg.oper) && nfcip_PFBisIPDU(pfb)) { - return ERR_PARAM; - } - break; - - /*******************************************************************************/ - default: - return ERR_PARAM; - } - - /*******************************************************************************/ - /* Prepend Header */ - /*******************************************************************************/ - *(--txBlock) = (uint8_t)cmd; /* CMD */ - *(--txBlock) = (uint8_t)(nfcipCmdIsReq(cmd) ? NFCIP_REQ : NFCIP_RES); /* CMDType */ - - txBufIt += - paylLen + - (uint16_t)((uint32_t)payloadBuf - (uint32_t)txBlock); /* Calculate overall buffer size */ - - if(txBufIt > gNfcip.fsc) /* Check if msg length violates the maximum payload size FSC */ - { - return ERR_NOTSUPP; - } - - /*******************************************************************************/ - return nfcipDataTx(txBlock, txBufIt, fwt); -} - -/* - ****************************************************************************** - * GLOBAL FUNCTIONS - ****************************************************************************** - */ - -/*******************************************************************************/ -static void nfcipConfig(const rfalNfcDepConfigs* cfg) { - if(cfg == NULL) { - return; - } - - ST_MEMCPY(&gNfcip.cfg, cfg, sizeof(rfalNfcDepConfigs)); /* Copy given config to local */ - - gNfcip.cfg.to = - MIN(RFAL_NFCDEP_WT_TRG_MAX, gNfcip.cfg.to); /* Ensure proper WT value */ - gNfcip.cfg.did = nfcip_DIDMax(gNfcip.cfg.did); /* Ensure proper DID value */ - gNfcip.fsc = rfalNfcDepLR2FS(gNfcip.cfg.lr); /* Calculate FSC based on given LR */ - - gNfcip.state = - ((gNfcip.cfg.role == RFAL_NFCDEP_ROLE_TARGET) ? NFCIP_ST_TARG_WAIT_ATR : - NFCIP_ST_INIT_IDLE); -} - -/*******************************************************************************/ -static ReturnCode nfcipRun(uint16_t* outActRxLen, bool* outIsChaining) { - ReturnCode ret; - - ret = ERR_SYNTAX; - - nfcipLogD(" NFCIP Run() state: %d \r\n", gNfcip.state); - - switch(gNfcip.state) { - /*******************************************************************************/ - case NFCIP_ST_IDLE: - case NFCIP_ST_INIT_DEP_IDLE: - case NFCIP_ST_TARG_DEP_IDLE: - case NFCIP_ST_TARG_DEP_SLEEP: - return ERR_NONE; - - /*******************************************************************************/ - case NFCIP_ST_INIT_DEP_TX: - - nfcipLogD(" NFCIP(I) Tx PNI: %d txLen: %d \r\n", gNfcip.pni, gNfcip.txBufLen); - ret = nfcipTx( - NFCIP_CMD_DEP_REQ, - gNfcip.txBuf, - &gNfcip.txBuf[gNfcip.txBufPaylPos], - gNfcip.txBufLen, - nfcip_PFBIPDU(gNfcip.pni), - (gNfcip.cfg.fwt + gNfcip.cfg.dFwt)); - - switch(ret) { - case ERR_NONE: - gNfcip.state = NFCIP_ST_INIT_DEP_RX; - break; - - case ERR_PARAM: - default: - gNfcip.state = NFCIP_ST_INIT_DEP_IDLE; - return ret; - } - /* fall through */ - - /*******************************************************************************/ - case NFCIP_ST_INIT_DEP_RX: /* PRQA S 2003 # MISRA 16.3 - Intentional fall through */ - - ret = nfcipDataRx(false); - - if(ret != ERR_BUSY) { - ret = nfcipInitiatorHandleDEP(ret, *gNfcip.rxRcvdLen, outActRxLen, outIsChaining); - } - - break; - - /*******************************************************************************/ - case NFCIP_ST_TARG_DEP_RTOX: - - if(!nfcipTimerisExpired(gNfcip.RTOXTimer)) /* Do nothing until RTOX timer has expired */ - { - return ERR_BUSY; - } - - /* If we cannot send a RTOX raise a Timeout error so that we do not - * hold the field On forever in AP2P */ - if(nfcipIsRTOXReqDisabled(gNfcip.cfg.oper)) { - /* We should reEnable Rx, and measure time between our field Off to - * either report link loss or recover #287 */ - nfcipLogI(" NFCIP(T) RTOX not sent due to config, NOT reenabling Rx \r\n"); - return ERR_TIMEOUT; - } - - if(gNfcip.cntRTOXRetrys++ > - RFAL_NFCDEP_MAX_RTOX_RETRYS) /* Check maximum consecutive RTOX requests */ - { - return ERR_PROTO; - } - - nfcipLogI(" NFCIP(T) RTOX sent \r\n"); - - gNfcip.lastRTOX = - nfcip_RTOXTargMax(gNfcip.cfg.to); /* Calculate requested RTOX value, and send it */ - EXIT_ON_ERR(ret, nfcipDEPControlMsg(nfcip_PFBSPDU_TO(), gNfcip.lastRTOX)); - - /* Set waiting for RTOX Ack Flag */ - gNfcip.isWait4RTOX = true; - - gNfcip.state = NFCIP_ST_TARG_DEP_RX; /* Go back to Rx to process RTOX ack */ - return ERR_BUSY; - - /*******************************************************************************/ - case NFCIP_ST_TARG_DEP_TX: - - nfcipLogD(" NFCIP(T) Tx PNI: %d txLen: %d \r\n", gNfcip.pni, gNfcip.txBufLen); - ret = nfcipTx( - NFCIP_CMD_DEP_RES, - gNfcip.txBuf, - &gNfcip.txBuf[gNfcip.txBufPaylPos], - gNfcip.txBufLen, - nfcip_PFBIPDU(gNfcip.pni), - NFCIP_NO_FWT); - - /* Clear flags */ - gNfcip.isTxPending = false; - gNfcip.isWait4RTOX = false; - - /* Digital 1.0 14.12.3.4 Increment the current PNI after Tx */ - gNfcip.pni = nfcip_PNIInc(gNfcip.pni); - - switch(ret) { - case ERR_NONE: - gNfcip.state = NFCIP_ST_TARG_DEP_RX; /* All OK, goto Rx state */ - break; - - case ERR_PARAM: - default: - gNfcip.state = NFCIP_ST_TARG_DEP_IDLE; /* Upon Tx error, goto IDLE state */ - return ret; - } - /* fall through */ - - /*******************************************************************************/ - case NFCIP_ST_TARG_DEP_RX: /* PRQA S 2003 # MISRA 16.3 - Intentional fall through */ - - if(gNfcip.isReqPending) /* if already has Data should be from a DEP from nfcipTargetHandleActivation() */ - { - nfcipLogD(" NFCIP(T) Skipping Rx Using DEP from Activation \r\n"); - - gNfcip.isReqPending = false; - ret = ERR_NONE; - } else { - ret = nfcipDataRx(false); - } - - if(ret != ERR_BUSY) { - ret = nfcipTargetHandleRX(ret, outActRxLen, outIsChaining); - } - - break; - - /*******************************************************************************/ - default: - /* MISRA 16.4: no empty default statement (a comment being enough) */ - break; - } - return ret; -} - -/*******************************************************************************/ -void rfalNfcDepSetDeactivatingCallback(rfalNfcDepDeactCallback pFunc) { - gNfcip.isDeactivating = pFunc; -} - -/*******************************************************************************/ -void rfalNfcDepInitialize(void) { - nfcipLogD(" NFCIP Ini() \r\n"); - - gNfcip.state = NFCIP_ST_IDLE; - gNfcip.isDeactivating = NULL; - - gNfcip.isTxPending = false; - gNfcip.isWait4RTOX = false; - gNfcip.isReqPending = false; - - gNfcip.cfg.oper = - (RFAL_NFCDEP_OPER_FULL_MI_DIS | RFAL_NFCDEP_OPER_EMPTY_DEP_EN | RFAL_NFCDEP_OPER_ATN_EN | - RFAL_NFCDEP_OPER_RTOX_REQ_EN); - - gNfcip.cfg.did = RFAL_NFCDEP_DID_NO; - gNfcip.cfg.nad = RFAL_NFCDEP_NAD_NO; - - gNfcip.cfg.br = RFAL_NFCDEP_Bx_NO_HIGH_BR; - gNfcip.cfg.bs = RFAL_NFCDEP_Bx_NO_HIGH_BR; - - gNfcip.cfg.lr = RFAL_NFCDEP_LR_254; - gNfcip.fsc = rfalNfcDepLR2FS(gNfcip.cfg.lr); - - gNfcip.cfg.gbLen = 0; - - gNfcip.cfg.fwt = RFAL_NFCDEP_MAX_FWT; - gNfcip.cfg.dFwt = RFAL_NFCDEP_MAX_FWT; - - gNfcip.pni = 0; - - /* Destroy any ongoing RTOX timer*/ - nfcipTimerDestroy(gNfcip.RTOXTimer); - gNfcip.RTOXTimer = 0U; - - gNfcip.PDUTxPos = 0; - gNfcip.PDURxPos = 0; - gNfcip.PDUParam.rxLen = NULL; - gNfcip.PDUParam.rxBuf = NULL; - gNfcip.PDUParam.txBuf = NULL; - - nfcipClearCounters(); -} - -/*******************************************************************************/ -static void nfcipSetDEPParams(const rfalNfcDepDEPParams* DEPParams) { - nfcipLogD(" NFCIP SetDEP() txLen: %d \r\n", DEPParams->txBufLen); - - gNfcip.isTxChaining = DEPParams->txChaining; - gNfcip.txBuf = DEPParams->txBuf; - gNfcip.rxBuf = DEPParams->rxBuf; - gNfcip.txBufLen = DEPParams->txBufLen; - gNfcip.rxBufLen = DEPParams->rxBufLen; - gNfcip.txBufPaylPos = DEPParams->txBufPaylPos; - gNfcip.rxBufPaylPos = DEPParams->rxBufPaylPos; - - if(DEPParams->did != RFAL_NFCDEP_DID_KEEP) { - gNfcip.cfg.did = nfcip_DIDMax(DEPParams->did); - } - - gNfcip.cfg.fwt = DEPParams->fwt; - gNfcip.cfg.dFwt = DEPParams->dFwt; - gNfcip.fsc = DEPParams->fsc; - - if(gNfcip.cfg.role == RFAL_NFCDEP_ROLE_TARGET) { - /* If there's any data to be sent go for Tx */ - if(DEPParams->txBufLen > 0U) { - /* Ensure that an RTOX Ack is not being expected at moment */ - if(!gNfcip.isWait4RTOX) { - gNfcip.state = NFCIP_ST_TARG_DEP_TX; - return; - } else { - /* If RTOX Ack is expected, signal a pending Tx to be transmitted right after */ - gNfcip.isTxPending = true; - nfcipLogW(" NFCIP(T) Waiting RTOX, queueing outgoing DEP Block \r\n"); - } - } - - /*Digital 1.0 14.12.4.1 In target mode the first PDU MUST be sent by the Initiator */ - gNfcip.state = NFCIP_ST_TARG_DEP_RX; - return; - } - - /* New data TxRx request clear previous error counters for consecutive TxRx without reseting communication/protocol layer*/ - nfcipClearCounters(); - - gNfcip.state = NFCIP_ST_INIT_DEP_TX; -} - -/*******************************************************************************/ -bool rfalNfcDepTargetRcvdATR(void) { - return ( - (gNfcip.cfg.role == RFAL_NFCDEP_ROLE_TARGET) && nfcipIsTarget(gNfcip.state) && - (gNfcip.state > NFCIP_ST_TARG_WAIT_ATR)); -} - -/*******************************************************************************/ -bool rfalNfcDepIsAtrReq(const uint8_t* buf, uint16_t bufLen, uint8_t* nfcid3) { - uint8_t msgIt; - - msgIt = 0; - - if((bufLen < RFAL_NFCDEP_ATRREQ_MIN_LEN) || (bufLen > RFAL_NFCDEP_ATRREQ_MAX_LEN)) { - return false; - } - - if(buf[msgIt++] != NFCIP_REQ) { - return false; - } - - if(buf[msgIt++] != (uint8_t)NFCIP_CMD_ATR_REQ) { - return false; - } - - /* Output NFID3 if requested */ - if(nfcid3 != NULL) { - ST_MEMCPY(nfcid3, &buf[RFAL_NFCDEP_ATR_REQ_NFCID3_POS], RFAL_NFCDEP_NFCID3_LEN); - } - - return true; -} - -/*******************************************************************************/ -static ReturnCode nfcipTargetHandleActivation(rfalNfcDepDevice* nfcDepDev, uint8_t* outBRS) { - ReturnCode ret; - uint8_t msgIt; - uint8_t txBuf[RFAL_NFCDEP_HEADER_PAD + NFCIP_PSLRES_LEN]; - - /*******************************************************************************/ - /* Check if we are in correct state */ - /*******************************************************************************/ - if(gNfcip.state != NFCIP_ST_TARG_WAIT_ACTV) { - return ERR_WRONG_STATE; - } - - /*******************************************************************************/ - /* Check required parameters */ - /*******************************************************************************/ - if(outBRS == NULL) { - return ERR_PARAM; - } - - /*******************************************************************************/ - /* Wait and process incoming cmd (PSL / DEP) */ - /*******************************************************************************/ - ret = nfcipDataRx(false); - - if(ret != ERR_NONE) { - return ret; - } - - msgIt = 0; - *outBRS = RFAL_NFCDEP_BRS_MAINTAIN; /* set out BRS to be maintained */ - - msgIt++; /* Skip LEN byte */ - - if(gNfcip.rxBuf[msgIt++] != NFCIP_REQ) { - return ERR_PROTO; - } - - if(gNfcip.rxBuf[msgIt] == (uint8_t)NFCIP_CMD_PSL_REQ) { - msgIt++; - - if(gNfcip.rxBuf[msgIt++] != gNfcip.cfg.did) /* Checking DID */ - { - return ERR_PROTO; - } - - nfcipLogI(" NFCIP(T) PSL REQ rcvd \r\n"); - - *outBRS = gNfcip.rxBuf[msgIt++]; /* assign output BRS value */ - - /* Store FSL(LR) and update current config */ - gNfcip.cfg.lr = (gNfcip.rxBuf[msgIt++] & RFAL_NFCDEP_LR_VAL_MASK); - gNfcip.fsc = rfalNfcDepLR2FS(gNfcip.cfg.lr); - - /*******************************************************************************/ - /* Update NFC-DDE Device info */ - if(nfcDepDev != NULL) { - /* Update Bitrate info */ - /* PRQA S 4342 2 # MISRA 10.5 - Layout of enum rfalBitRate and definition of rfalNfcDepBRS2DSI guarantee no invalid enum values to be created */ - nfcDepDev->info.DSI = (rfalBitRate)rfalNfcDepBRS2DSI( - *outBRS); /* DSI codes the bit rate from Initiator to Target */ - nfcDepDev->info.DRI = (rfalBitRate)rfalNfcDepBRS2DRI( - *outBRS); /* DRI codes the bit rate from Target to Initiator */ - - /* Update Length Reduction and Frame Size */ - nfcDepDev->info.LR = gNfcip.cfg.lr; - nfcDepDev->info.FS = gNfcip.fsc; - - /* Update PPi byte */ - nfcDepDev->activation.Initiator.ATR_REQ.PPi &= ~RFAL_NFCDEP_PP_LR_MASK; - nfcDepDev->activation.Initiator.ATR_REQ.PPi |= rfalNfcDepLR2PP(gNfcip.cfg.lr); - } - - rfalSetBitRate(RFAL_BR_KEEP, gNfcip.nfcDepDev->info.DSI); - - EXIT_ON_ERR(ret, nfcipTx(NFCIP_CMD_PSL_RES, txBuf, NULL, 0, 0, NFCIP_NO_FWT)); - } else { - if(gNfcip.rxBuf[msgIt] == (uint8_t)NFCIP_CMD_DEP_REQ) { - msgIt++; - - /*******************************************************************************/ - /* Digital 1.0 14.12.3.1 PNI must be initialized to 0 */ - if(nfcip_PBF_PNI(gNfcip.rxBuf[msgIt]) != 0U) { - return ERR_PROTO; - } - - /*******************************************************************************/ - /* Digital 1.0 14.8.2.1 check if DID is expected and match -> Protocol Error */ - if(nfcip_PFBhasDID(gNfcip.rxBuf[msgIt])) { - if(gNfcip.rxBuf[++msgIt] != gNfcip.cfg.did) { - return ERR_PROTO; - } - } else if(gNfcip.cfg.did != RFAL_NFCDEP_DID_NO) /* DID expected but not rcv */ - { - return ERR_PROTO; - } else { - /* MISRA 15.7 - Empty else */ - } - } - - /* Signal Request pending to be digested on normal Handling (DEP_REQ, DSL_REQ, RLS_REQ) */ - gNfcip.isReqPending = true; - } - - gNfcip.state = NFCIP_ST_TARG_DEP_RX; - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode - rfalNfcDepATR(const rfalNfcDepAtrParam* param, rfalNfcDepAtrRes* atrRes, uint8_t* atrResLen) { - ReturnCode ret; - rfalNfcDepConfigs cfg; - uint16_t rxLen; - uint8_t msgIt; - uint8_t txBuf[RFAL_NFCDEP_ATRREQ_MAX_LEN]; - uint8_t rxBuf[NFCIP_ATRRES_BUF_LEN]; - - if((param == NULL) || (atrRes == NULL) || (atrResLen == NULL)) { - return ERR_PARAM; - } - - /*******************************************************************************/ - /* Configure NFC-DEP layer */ - /*******************************************************************************/ - - cfg.did = param->DID; - cfg.nad = param->NAD; - cfg.fwt = RFAL_NFCDEP_MAX_FWT; - cfg.dFwt = RFAL_NFCDEP_MAX_FWT; - cfg.br = param->BR; - cfg.bs = param->BS; - cfg.lr = param->LR; - cfg.to = RFAL_NFCDEP_WT_TRG_MAX; /* Not used in Initiator mode */ - - cfg.gbLen = param->GBLen; - if(cfg.gbLen > 0U) /* MISRA 21.18 */ - { - ST_MEMCPY(cfg.gb, param->GB, cfg.gbLen); - } - - cfg.nfcidLen = param->nfcidLen; - if(cfg.nfcidLen > 0U) /* MISRA 21.18 */ - { - ST_MEMCPY(cfg.nfcid, param->nfcid, cfg.nfcidLen); - } - - cfg.role = RFAL_NFCDEP_ROLE_INITIATOR; - cfg.oper = param->operParam; - cfg.commMode = param->commMode; - - rfalNfcDepInitialize(); - nfcipConfig(&cfg); - - /*******************************************************************************/ - /* Send ATR_REQ */ - /*******************************************************************************/ - - EXIT_ON_ERR( - ret, - nfcipTxRx( - NFCIP_CMD_ATR_REQ, - txBuf, - nfcipRWTActivation(), - NULL, - 0, - rxBuf, - NFCIP_ATRRES_BUF_LEN, - &rxLen)); - - /*******************************************************************************/ - /* ATR sent, check response */ - /*******************************************************************************/ - msgIt = 0; - rxLen = ((uint16_t)rxBuf[msgIt++] - RFAL_NFCDEP_LEN_LEN); /* use LEN byte */ - - if((rxLen < RFAL_NFCDEP_ATRRES_MIN_LEN) || - (rxLen > RFAL_NFCDEP_ATRRES_MAX_LEN)) /* Checking length: ATR_RES */ - { - return ERR_PROTO; - } - - if(rxBuf[msgIt++] != NFCIP_RES) /* Checking if is a response*/ - { - return ERR_PROTO; - } - - if(rxBuf[msgIt++] != (uint8_t)NFCIP_CMD_ATR_RES) /* Checking if is a ATR RES */ - { - return ERR_PROTO; - } - - ST_MEMCPY((uint8_t*)atrRes, (rxBuf + RFAL_NFCDEP_LEN_LEN), rxLen); - *atrResLen = (uint8_t)rxLen; - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcDepPSL(uint8_t BRS, uint8_t FSL) { - ReturnCode ret; - uint16_t rxLen; - uint8_t msgIt; - uint8_t txBuf[NFCIP_PSLREQ_LEN + NFCIP_PSLPAY_LEN]; - uint8_t rxBuf[NFCIP_PSLRES_LEN]; - - msgIt = NFCIP_PSLREQ_LEN; - - txBuf[msgIt++] = BRS; - txBuf[msgIt++] = FSL; - - /*******************************************************************************/ - /* Send PSL REQ and wait for response */ - /*******************************************************************************/ - EXIT_ON_ERR( - ret, - nfcipTxRx( - NFCIP_CMD_PSL_REQ, - txBuf, - nfcipRWTActivation(), - &txBuf[NFCIP_PSLREQ_LEN], - (msgIt - NFCIP_PSLREQ_LEN), - rxBuf, - NFCIP_PSLRES_LEN, - &rxLen)); - - /*******************************************************************************/ - /* PSL sent, check response */ - /*******************************************************************************/ - msgIt = 0; - rxLen = (uint16_t)(rxBuf[msgIt++]); /* use LEN byte */ - - if(rxLen < NFCIP_PSLRES_LEN) /* Checking length: LEN + RLS_RES */ - { - return ERR_PROTO; - } - - if(rxBuf[msgIt++] != NFCIP_RES) /* Checking if is a response */ - { - return ERR_PROTO; - } - - if(rxBuf[msgIt++] != (uint8_t)NFCIP_CMD_PSL_RES) /* Checking if is a PSL RES */ - { - return ERR_PROTO; - } - - if(rxBuf[msgIt++] != gNfcip.cfg.did) /* Checking DID */ - { - return ERR_PROTO; - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcDepDSL(void) { - ReturnCode ret; - uint8_t txBuf[RFAL_NFCDEP_HEADER_PAD + NFCIP_DSLREQ_LEN]; - uint8_t rxBuf[NFCIP_DSLRES_LEN]; - uint8_t rxMsgIt; - uint16_t rxLen = 0; - - if(gNfcip.cfg.role == RFAL_NFCDEP_ROLE_TARGET) { - return ERR_NONE; /* Target has no deselect procedure */ - } - - /* Repeating a DSL REQ is optional, not doing it */ - EXIT_ON_ERR( - ret, - nfcipTxRx( - NFCIP_CMD_DSL_REQ, - txBuf, - nfcipRWTActivation(), - NULL, - 0, - rxBuf, - (uint16_t)sizeof(rxBuf), - &rxLen)); - - /*******************************************************************************/ - rxMsgIt = 0; - - if(rxBuf[rxMsgIt++] < NFCIP_DSLRES_MIN) /* Checking length: LEN + DSL_RES */ - { - return ERR_PROTO; - } - - if(rxBuf[rxMsgIt++] != NFCIP_RES) /* Checking if is a response */ - { - return ERR_PROTO; - } - - if(rxBuf[rxMsgIt++] != (uint8_t)NFCIP_CMD_DSL_RES) /* Checking if is DSL RES */ - { - return ERR_PROTO; - } - - if(gNfcip.cfg.did != RFAL_NFCDEP_DID_NO) { - if(rxBuf[rxMsgIt++] != gNfcip.cfg.did) { - return ERR_PROTO; - } - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcDepRLS(void) { - ReturnCode ret; - uint8_t txBuf[RFAL_NFCDEP_HEADER_PAD + NFCIP_RLSREQ_LEN]; - uint8_t rxBuf[NFCIP_RLSRES_LEN]; - uint8_t rxMsgIt; - uint16_t rxLen = 0; - - if(gNfcip.cfg.role == RFAL_NFCDEP_ROLE_TARGET) /* Target has no release procedure */ - { - return ERR_NONE; - } - - /* Repeating a RLS REQ is optional, not doing it */ - EXIT_ON_ERR( - ret, - nfcipTxRx( - NFCIP_CMD_RLS_REQ, - txBuf, - nfcipRWTActivation(), - NULL, - 0, - rxBuf, - (uint16_t)sizeof(rxBuf), - &rxLen)); - - /*******************************************************************************/ - rxMsgIt = 0; - - if(rxBuf[rxMsgIt++] < NFCIP_RLSRES_MIN) /* Checking length: LEN + RLS_RES */ - { - return ERR_PROTO; - } - - if(rxBuf[rxMsgIt++] != NFCIP_RES) /* Checking if is a response */ - { - return ERR_PROTO; - } - - if(rxBuf[rxMsgIt++] != (uint8_t)NFCIP_CMD_RLS_RES) /* Checking if is RLS RES */ - { - return ERR_PROTO; - } - - if(gNfcip.cfg.did != RFAL_NFCDEP_DID_NO) { - if(rxBuf[rxMsgIt++] != gNfcip.cfg.did) { - return ERR_PROTO; - } - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcDepInitiatorHandleActivation( - rfalNfcDepAtrParam* param, - rfalBitRate desiredBR, - rfalNfcDepDevice* nfcDepDev) { - ReturnCode ret; - uint8_t maxRetyrs; - uint8_t PSL_BRS; - uint8_t PSL_FSL; - bool sendPSL; - - if((param == NULL) || (nfcDepDev == NULL)) { - return ERR_PARAM; - } - - param->NAD = RFAL_NFCDEP_NAD_NO; /* Digital 1.1 16.6.2.9 Initiator SHALL NOT use NAD */ - maxRetyrs = NFCIP_ATR_RETRY_MAX; - - /*******************************************************************************/ - /* Send ATR REQ and wait for response */ - /*******************************************************************************/ - do { /* Upon transmission error ATR REQ should be retried */ - - ret = rfalNfcDepATR( - param, - &nfcDepDev->activation.Target.ATR_RES, - &nfcDepDev->activation.Target.ATR_RESLen); - - if(nfcipIsTransmissionError(ret)) { - continue; - } - break; - } while((maxRetyrs--) != 0U); - - if(ret != ERR_NONE) { - return ret; - } - - /*******************************************************************************/ - /* Compute NFC-DEP device with ATR_RES */ - /*******************************************************************************/ - nfcDepDev->info.GBLen = (nfcDepDev->activation.Target.ATR_RESLen - RFAL_NFCDEP_ATRRES_MIN_LEN); - nfcDepDev->info.DID = nfcDepDev->activation.Target.ATR_RES.DID; - nfcDepDev->info.NAD = - RFAL_NFCDEP_NAD_NO; /* Digital 1.1 16.6.3.11 Initiator SHALL ignore b1 of PPt */ - nfcDepDev->info.LR = rfalNfcDepPP2LR(nfcDepDev->activation.Target.ATR_RES.PPt); - nfcDepDev->info.FS = rfalNfcDepLR2FS(nfcDepDev->info.LR); - nfcDepDev->info.WT = (nfcDepDev->activation.Target.ATR_RES.TO & RFAL_NFCDEP_WT_MASK); - nfcDepDev->info.FWT = rfalNfcDepCalculateRWT(nfcDepDev->info.WT); - nfcDepDev->info.dFWT = RFAL_NFCDEP_WT_DELTA; - - rfalGetBitRate(&nfcDepDev->info.DSI, &nfcDepDev->info.DRI); - - /*******************************************************************************/ - /* Check if a PSL needs to be sent */ - /*******************************************************************************/ - sendPSL = false; - PSL_BRS = rfalNfcDepDx2BRS( - nfcDepDev->info.DSI); /* Set current bit rate divisor on both directions */ - PSL_FSL = nfcDepDev->info.LR; /* Set current Frame Size */ - - /* Activity 1.0 9.4.4.15 & 9.4.6.3 NFC-DEP Activation PSL - * Activity 2.0 9.4.4.17 & 9.4.6.6 NFC-DEP Activation PSL - * - * PSL_REQ shall only be sent if desired bit rate is different from current (Activity 1.0) - * PSL_REQ shall be sent to update LR or bit rate (Activity 2.0) - * */ - -#if 0 /* PSL due to LR is disabled, can be enabled if desired*/ - /*******************************************************************************/ - /* Check Frame Size */ - /*******************************************************************************/ - if( gNfcip.cfg.lr < nfcDepDev->info.LR ) /* If our Length reduction is smaller */ - { - sendPSL = true; - - nfcDepDev->info.LR = MIN( nfcDepDev->info.LR, gNfcip.cfg.lr ); - - gNfcip.cfg.lr = nfcDepDev->info.LR; /* Update nfcip LR to be used */ - gNfcip.fsc = rfalNfcDepLR2FS( gNfcip.cfg.lr ); /* Update nfcip FSC to be used */ - - PSL_FSL = gNfcip.cfg.lr; /* Set LR to be sent */ - - nfcipLogI( " NFCIP(I) Frame Size differ, PSL new fsc: %d \r\n", gNfcip.fsc ); - } -#endif - - /*******************************************************************************/ - /* Check Baud rates */ - /*******************************************************************************/ - if((nfcDepDev->info.DSI != desiredBR) && - (desiredBR != RFAL_BR_KEEP)) /* if desired BR is different */ - { - if(nfcipDxIsSupported( - (uint8_t)desiredBR, - nfcDepDev->activation.Target.ATR_RES.BRt, - nfcDepDev->activation.Target.ATR_RES - .BSt)) /* if desired BR is supported */ /* MISRA 13.5 */ - { - sendPSL = true; - PSL_BRS = rfalNfcDepDx2BRS(desiredBR); - - nfcipLogI(" NFCIP(I) BR differ, PSL BR: 0x%02X \r\n", PSL_BRS); - } - } - - /*******************************************************************************/ - if(sendPSL) { - /*******************************************************************************/ - /* Send PSL REQ and wait for response */ - /*******************************************************************************/ - EXIT_ON_ERR(ret, rfalNfcDepPSL(PSL_BRS, PSL_FSL)); - - /* Check if bit rate has been changed */ - if(nfcDepDev->info.DSI != desiredBR) { - /* Check if device was in Passive NFC-A and went to higher bit rates, use NFC-F */ - if((nfcDepDev->info.DSI == RFAL_BR_106) && - (gNfcip.cfg.commMode == RFAL_NFCDEP_COMM_PASSIVE)) { -#if RFAL_FEATURE_NFCF - /* If Passive initialize NFC-F module */ - rfalNfcfPollerInitialize(desiredBR); -#else /* RFAL_FEATURE_NFCF */ - return ERR_NOTSUPP; -#endif /* RFAL_FEATURE_NFCF */ - } - - nfcDepDev->info.DRI = desiredBR; /* DSI Bit Rate coding from Initiator to Target */ - nfcDepDev->info.DSI = desiredBR; /* DRI Bit Rate coding from Target to Initiator */ - - rfalSetBitRate(nfcDepDev->info.DSI, nfcDepDev->info.DRI); - } - - return ERR_NONE; /* PSL has been sent */ - } - - return ERR_NONE; /* No PSL has been sent */ -} - -/*******************************************************************************/ -uint32_t rfalNfcDepCalculateRWT(uint8_t wt) { - /* Digital 1.0 14.6.3.8 & Digital 1.1 16.6.3.9 */ - /* Digital 1.1 16.6.3.9 treat all RFU values as WT=14 */ - uint8_t responseWaitTime = MIN(RFAL_NFCDEP_WT_INI_MAX, wt); - - return (uint32_t)rfalNfcDepWT2RWT(responseWaitTime); -} - -/*******************************************************************************/ -static ReturnCode nfcipDataTx(uint8_t* txBuf, uint16_t txBufLen, uint32_t fwt) { - return rfalTransceiveBlockingTx( - txBuf, - txBufLen, - gNfcip.rxBuf, - gNfcip.rxBufLen, - gNfcip.rxRcvdLen, - (RFAL_TXRX_FLAGS_DEFAULT | (uint32_t)RFAL_TXRX_FLAGS_NFCIP1_ON), - ((fwt == NFCIP_NO_FWT) ? RFAL_FWT_NONE : fwt)); -} - -/*******************************************************************************/ -static ReturnCode nfcipDataRx(bool blocking) { - ReturnCode ret; - - /* Perform Rx either blocking or non-blocking */ - if(blocking) { - ret = rfalTransceiveBlockingRx(); - } else { - ret = rfalGetTransceiveStatus(); - } - - if(ret != ERR_BUSY) { - if(gNfcip.rxRcvdLen != NULL) { - (*gNfcip.rxRcvdLen) = rfalConvBitsToBytes(*gNfcip.rxRcvdLen); - - if((ret == ERR_NONE) && (gNfcip.rxBuf != NULL)) { - /* Digital 1.1 16.4.1.3 - Length byte LEN SHALL have a value between 3 and 255 -> otherwise treat as Transmission Error * - * - Ensure that actual received and frame length do match, otherwise treat as Transmission error */ - if((*gNfcip.rxRcvdLen != (uint16_t)*gNfcip.rxBuf) || - (*gNfcip.rxRcvdLen < RFAL_NFCDEP_LEN_MIN) || - (*gNfcip.rxRcvdLen > RFAL_NFCDEP_LEN_MAX)) { - return ERR_FRAMING; - } - } - } - } - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalNfcDepListenStartActivation( - const rfalNfcDepTargetParam* param, - const uint8_t* atrReq, - uint16_t atrReqLength, - rfalNfcDepListenActvParam rxParam) { - ReturnCode ret; - rfalNfcDepConfigs cfg; - - if((param == NULL) || (atrReq == NULL) || (rxParam.rxLen == NULL)) { - return ERR_PARAM; - } - - /*******************************************************************************/ - /* Check whether is a valid ATR_REQ Compute NFC-DEP device */ - if(!rfalNfcDepIsAtrReq(atrReq, atrReqLength, NULL)) { - return ERR_PARAM; - } - - rxParam.nfcDepDev->activation.Initiator.ATR_REQLen = - (uint8_t)atrReqLength; /* nfcipIsAtrReq() is already checking Min and Max buffer lengths */ - if(atrReqLength > 0U) /* MISRA 21.18 */ - { - ST_MEMCPY( - (uint8_t*)&rxParam.nfcDepDev->activation.Initiator.ATR_REQ, atrReq, atrReqLength); - } - - rxParam.nfcDepDev->info.GBLen = (uint8_t)(atrReqLength - RFAL_NFCDEP_ATRREQ_MIN_LEN); - rxParam.nfcDepDev->info.DID = rxParam.nfcDepDev->activation.Initiator.ATR_REQ.DID; - rxParam.nfcDepDev->info.NAD = - RFAL_NFCDEP_NAD_NO; /* Digital 1.1 16.6.2.9 Initiator SHALL NOT use NAD */ - rxParam.nfcDepDev->info.LR = - rfalNfcDepPP2LR(rxParam.nfcDepDev->activation.Initiator.ATR_REQ.PPi); - rxParam.nfcDepDev->info.FS = rfalNfcDepLR2FS(rxParam.nfcDepDev->info.LR); - rxParam.nfcDepDev->info.WT = 0; - rxParam.nfcDepDev->info.FWT = NFCIP_NO_FWT; - rxParam.nfcDepDev->info.dFWT = NFCIP_NO_FWT; - - rfalGetBitRate(&rxParam.nfcDepDev->info.DSI, &rxParam.nfcDepDev->info.DRI); - - /* Store Device Info location, updated upon a PSL */ - gNfcip.nfcDepDev = rxParam.nfcDepDev; - - /*******************************************************************************/ - cfg.did = rxParam.nfcDepDev->activation.Initiator.ATR_REQ.DID; - cfg.nad = RFAL_NFCDEP_NAD_NO; - - cfg.fwt = RFAL_NFCDEP_MAX_FWT; - cfg.dFwt = RFAL_NFCDEP_MAX_FWT; - - cfg.br = param->brt; - cfg.bs = param->bst; - - cfg.lr = rfalNfcDepPP2LR(param->ppt); - - cfg.gbLen = param->GBtLen; - if(cfg.gbLen > 0U) /* MISRA 21.18 */ - { - ST_MEMCPY(cfg.gb, param->GBt, cfg.gbLen); - } - - cfg.nfcidLen = RFAL_NFCDEP_NFCID3_LEN; - ST_MEMCPY(cfg.nfcid, param->nfcid3, RFAL_NFCDEP_NFCID3_LEN); - - cfg.to = param->to; - - cfg.role = RFAL_NFCDEP_ROLE_TARGET; - cfg.oper = param->operParam; - cfg.commMode = param->commMode; - - rfalNfcDepInitialize(); - nfcipConfig(&cfg); - - /*******************************************************************************/ - /* Reply with ATR RES to Initiator */ - /*******************************************************************************/ - gNfcip.rxBuf = (uint8_t*)rxParam.rxBuf; - gNfcip.rxBufLen = sizeof(rfalNfcDepBufFormat); - gNfcip.rxRcvdLen = rxParam.rxLen; - gNfcip.rxBufPaylPos = RFAL_NFCDEP_DEPREQ_HEADER_LEN; - gNfcip.isChaining = rxParam.isRxChaining; - gNfcip.txBufPaylPos = RFAL_NFCDEP_DEPREQ_HEADER_LEN; - - EXIT_ON_ERR(ret, nfcipTx(NFCIP_CMD_ATR_RES, (uint8_t*)gNfcip.rxBuf, NULL, 0, 0, NFCIP_NO_FWT)); - - gNfcip.state = NFCIP_ST_TARG_WAIT_ACTV; - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcDepListenGetActivationStatus(void) { - ReturnCode err; - uint8_t BRS; - - BRS = RFAL_NFCDEP_BRS_MAINTAIN; - - err = nfcipTargetHandleActivation(gNfcip.nfcDepDev, &BRS); - - switch(err) { - case ERR_NONE: - - if(BRS != RFAL_NFCDEP_BRS_MAINTAIN) { - /* DSI codes the bit rate from Initiator to Target */ - /* DRI codes the bit rate from Target to Initiator */ - - if(gNfcip.cfg.commMode == RFAL_NFCDEP_COMM_ACTIVE) { - EXIT_ON_ERR( - err, - rfalSetMode( - RFAL_MODE_LISTEN_ACTIVE_P2P, - gNfcip.nfcDepDev->info.DRI, - gNfcip.nfcDepDev->info.DSI)); - } else { - EXIT_ON_ERR( - err, - rfalSetMode( - ((RFAL_BR_106 == gNfcip.nfcDepDev->info.DRI) ? RFAL_MODE_LISTEN_NFCA : - RFAL_MODE_LISTEN_NFCF), - gNfcip.nfcDepDev->info.DRI, - gNfcip.nfcDepDev->info.DSI)); - } - } - break; - - case ERR_BUSY: - // do nothing - break; - - case ERR_PROTO: - default: - // re-enable receiving of data - nfcDepReEnableRx(gNfcip.rxBuf, gNfcip.rxBufLen, gNfcip.rxRcvdLen); - break; - } - - return err; -} - -/*******************************************************************************/ -ReturnCode rfalNfcDepStartTransceive(const rfalNfcDepTxRxParam* param) { - rfalNfcDepDEPParams nfcDepParams; - - nfcDepParams.txBuf = (uint8_t*)param->txBuf; - nfcDepParams.txBufLen = param->txBufLen; - nfcDepParams.txChaining = param->isTxChaining; - nfcDepParams.txBufPaylPos = - RFAL_NFCDEP_DEPREQ_HEADER_LEN; /* position in txBuf where actual outgoing data is located */ - nfcDepParams.did = RFAL_NFCDEP_DID_KEEP; - nfcDepParams.rxBufPaylPos = RFAL_NFCDEP_DEPREQ_HEADER_LEN; - nfcDepParams.rxBuf = (uint8_t*)param->rxBuf; - nfcDepParams.rxBufLen = sizeof(rfalNfcDepBufFormat); - nfcDepParams.fsc = param->FSx; - nfcDepParams.fwt = param->FWT; - nfcDepParams.dFwt = param->dFWT; - - gNfcip.rxRcvdLen = param->rxLen; - gNfcip.isChaining = param->isRxChaining; - - nfcipSetDEPParams(&nfcDepParams); - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcDepGetTransceiveStatus(void) { - return nfcipRun(gNfcip.rxRcvdLen, gNfcip.isChaining); -} - -/*******************************************************************************/ -static void rfalNfcDepPdu2BLockParam( - rfalNfcDepPduTxRxParam pduParam, - rfalNfcDepTxRxParam* blockParam, - uint16_t txPos, - uint16_t rxPos) { - uint16_t maxInfLen; - - NO_WARNING(rxPos); /* Keep this param for future use */ - - blockParam->DID = pduParam.DID; - blockParam->FSx = pduParam.FSx; - blockParam->FWT = pduParam.FWT; - blockParam->dFWT = pduParam.dFWT; - - /* Calculate max INF/Payload to be sent to other device */ - maxInfLen = (blockParam->FSx - (RFAL_NFCDEP_HEADER + RFAL_NFCDEP_DEP_PFB_LEN)); - maxInfLen += ((blockParam->DID != RFAL_NFCDEP_DID_NO) ? RFAL_NFCDEP_DID_LEN : 0U); - - if((pduParam.txBufLen - txPos) > maxInfLen) { - blockParam->isTxChaining = true; - blockParam->txBufLen = maxInfLen; - } else { - blockParam->isTxChaining = false; - blockParam->txBufLen = (pduParam.txBufLen - txPos); - } - - /* TxBuf is moved to the beginning for every Block */ - blockParam->txBuf = - (rfalNfcDepBufFormat*)pduParam - .txBuf; /* PRQA S 0310 # MISRA 11.3 - Intentional safe cast to avoiding large buffer duplication */ - blockParam->rxBuf = - pduParam - .tmpBuf; /* Simply using the pdu buffer is not possible because of current ACK handling */ - blockParam->isRxChaining = &gNfcip.isPDURxChaining; - blockParam->rxLen = pduParam.rxLen; -} - -/*******************************************************************************/ -ReturnCode rfalNfcDepStartPduTransceive(rfalNfcDepPduTxRxParam param) { - rfalNfcDepTxRxParam txRxParam; - - /* Initialize and store APDU context */ - gNfcip.PDUParam = param; - gNfcip.PDUTxPos = 0; - gNfcip.PDURxPos = 0; - - /* Convert PDU TxRxParams to Block TxRxParams */ - rfalNfcDepPdu2BLockParam(gNfcip.PDUParam, &txRxParam, gNfcip.PDUTxPos, gNfcip.PDURxPos); - - return rfalNfcDepStartTransceive(&txRxParam); -} - -/*******************************************************************************/ -ReturnCode rfalNfcDepGetPduTransceiveStatus(void) { - ReturnCode ret; - rfalNfcDepTxRxParam txRxParam; - - ret = rfalNfcDepGetTransceiveStatus(); - switch(ret) { - /*******************************************************************************/ - case ERR_NONE: - - /* Check if we are still doing chaining on Tx */ - if(gNfcip.isTxChaining) { - /* Add already Tx bytes */ - gNfcip.PDUTxPos += gNfcip.txBufLen; - - /* Convert APDU TxRxParams to I-Block TxRxParams */ - rfalNfcDepPdu2BLockParam( - gNfcip.PDUParam, &txRxParam, gNfcip.PDUTxPos, gNfcip.PDURxPos); - - if(txRxParam.txBufLen > 0U) /* MISRA 21.18 */ - { - /* Move next Block to beginning of APDU Tx buffer */ - ST_MEMCPY( - gNfcip.PDUParam.txBuf->pdu, - &gNfcip.PDUParam.txBuf->pdu[gNfcip.PDUTxPos], - txRxParam.txBufLen); - } - - EXIT_ON_ERR(ret, rfalNfcDepStartTransceive(&txRxParam)); - return ERR_BUSY; - } - - /* PDU TxRx is done */ - /* fall through */ - - /*******************************************************************************/ - case ERR_AGAIN: /* PRQA S 2003 # MISRA 16.3 - Intentional fall through */ - - /* Check if no PDU transceive has been started before (data from rfalNfcDepListenStartActivation) */ - if(gNfcip.PDUParam.rxLen == NULL) { - /* In Listen mode first chained packet cannot be retrieved via APDU interface */ - if(ret == ERR_AGAIN) { - return ERR_NOTSUPP; - } - - /* TxRx is complete and full data is already available */ - return ERR_NONE; - } - - if((*gNfcip.PDUParam.rxLen) > 0U) /* MISRA 21.18 */ - { - /* Ensure that data in tmpBuf still fits into PDU buffer */ - if((uint16_t)((uint16_t)gNfcip.PDURxPos + (*gNfcip.PDUParam.rxLen)) > - RFAL_FEATURE_NFC_DEP_PDU_MAX_LEN) { - return ERR_NOMEM; - } - - /* Copy chained packet from tmp buffer to PDU buffer */ - ST_MEMCPY( - &gNfcip.PDUParam.rxBuf->pdu[gNfcip.PDURxPos], - gNfcip.PDUParam.tmpBuf->inf, - *gNfcip.PDUParam.rxLen); - gNfcip.PDURxPos += *gNfcip.PDUParam.rxLen; - } - - /* Update output param rxLen */ - *gNfcip.PDUParam.rxLen = gNfcip.PDURxPos; - - /* Wait for following Block or PDU TxRx is done */ - return ((ret == ERR_AGAIN) ? ERR_BUSY : ERR_NONE); - - /*******************************************************************************/ - default: - /* MISRA 16.4: no empty default statement (a comment being enough) */ - break; - } - - return ret; -} - -#endif /* RFAL_FEATURE_NFC_DEP */ diff --git a/lib/ST25RFAL002/source/rfal_nfca.c b/lib/ST25RFAL002/source/rfal_nfca.c deleted file mode 100644 index d4e4c93880f..00000000000 --- a/lib/ST25RFAL002/source/rfal_nfca.c +++ /dev/null @@ -1,886 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_nfca.c - * - * \author Gustavo Patricio - * - * \brief Provides several NFC-A convenience methods and definitions - * - * It provides a Poller (ISO14443A PCD) interface and as well as - * some NFC-A Listener (ISO14443A PICC) helpers. - * - * The definitions and helpers methods provided by this module are only - * up to ISO14443-3 layer - * - */ - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "rfal_nfca.h" -#include "utils.h" - -/* - ****************************************************************************** - * ENABLE SWITCH - ****************************************************************************** - */ - -#ifndef RFAL_FEATURE_NFCA -#define RFAL_FEATURE_NFCA false /* NFC-A module configuration missing. Disabled by default */ -#endif - -#if RFAL_FEATURE_NFCA - -/* - ****************************************************************************** - * GLOBAL DEFINES - ****************************************************************************** - */ - -#define RFAL_NFCA_SLP_FWT \ - rfalConvMsTo1fc(1) /*!< Check 1ms for any modulation ISO14443-3 6.4.3 */ -#define RFAL_NFCA_SLP_CMD 0x50U /*!< SLP cmd (byte1) Digital 1.1 6.9.1 & Table 20 */ -#define RFAL_NFCA_SLP_BYTE2 0x00U /*!< SLP byte2 Digital 1.1 6.9.1 & Table 20 */ -#define RFAL_NFCA_SLP_CMD_POS 0U /*!< SLP cmd position Digital 1.1 6.9.1 & Table 20 */ -#define RFAL_NFCA_SLP_BYTE2_POS 1U /*!< SLP byte2 position Digital 1.1 6.9.1 & Table 20 */ - -#define RFAL_NFCA_SDD_CT 0x88U /*!< Cascade Tag value Digital 1.1 6.7.2 */ -#define RFAL_NFCA_SDD_CT_LEN 1U /*!< Cascade Tag length */ - -#define RFAL_NFCA_SLP_REQ_LEN 2U /*!< SLP_REQ length */ - -#define RFAL_NFCA_SEL_CMD_LEN 1U /*!< SEL_CMD length */ -#define RFAL_NFCA_SEL_PAR_LEN 1U /*!< SEL_PAR length */ -#define RFAL_NFCA_SEL_SELPAR \ - rfalNfcaSelPar(7U, 0U) /*!< SEL_PAR on Select is always with 4 data/nfcid */ -#define RFAL_NFCA_BCC_LEN 1U /*!< BCC length */ - -#define RFAL_NFCA_SDD_REQ_LEN \ - (RFAL_NFCA_SEL_CMD_LEN + RFAL_NFCA_SEL_PAR_LEN) /*!< SDD_REQ length */ -#define RFAL_NFCA_SDD_RES_LEN \ - (RFAL_NFCA_CASCADE_1_UID_LEN + RFAL_NFCA_BCC_LEN) /*!< SDD_RES length */ - -#define RFAL_NFCA_T_RETRANS 5U /*!< t RETRANSMISSION [3, 33]ms EMVCo 2.6 A.5 */ -#define RFAL_NFCA_N_RETRANS 2U /*!< Number of retries EMVCo 2.6 9.6.1.3 */ - -/*! SDD_REQ (Select) Cascade Levels */ -enum { - RFAL_NFCA_SEL_CASCADE_L1 = 0, /*!< SDD_REQ Cascade Level 1 */ - RFAL_NFCA_SEL_CASCADE_L2 = 1, /*!< SDD_REQ Cascade Level 2 */ - RFAL_NFCA_SEL_CASCADE_L3 = 2 /*!< SDD_REQ Cascade Level 3 */ -}; - -/*! SDD_REQ (Select) request Cascade Level command Digital 1.1 Table 15 */ -enum { - RFAL_NFCA_CMD_SEL_CL1 = 0x93, /*!< SDD_REQ command Cascade Level 1 */ - RFAL_NFCA_CMD_SEL_CL2 = 0x95, /*!< SDD_REQ command Cascade Level 2 */ - RFAL_NFCA_CMD_SEL_CL3 = 0x97, /*!< SDD_REQ command Cascade Level 3 */ -}; - -/* -****************************************************************************** -* GLOBAL MACROS -****************************************************************************** -*/ -#define rfalNfcaSelPar(nBy, nbi) \ - (uint8_t)( \ - (((nBy) << 4U) & 0xF0U) | \ - ((nbi)&0x0FU)) /*!< Calculates SEL_PAR with the bytes/bits to be sent */ -#define rfalNfcaCLn2SELCMD(cl) \ - (uint8_t)( \ - (uint8_t)(RFAL_NFCA_CMD_SEL_CL1) + \ - (2U * (cl))) /*!< Calculates SEL_CMD with the given cascade level */ -#define rfalNfcaNfcidLen2CL(len) \ - ((len) / 5U) /*!< Calculates cascade level by the NFCID length */ -#define rfalNfcaRunBlocking(e, fn) \ - do { \ - (e) = (fn); \ - rfalWorker(); \ - } while((e) == ERR_BUSY) /*!< Macro used for the blocking methods */ - -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ - -/*! Colission Resolution states */ -typedef enum { - RFAL_NFCA_CR_IDLE, /*!< IDLE state */ - RFAL_NFCA_CR_CL, /*!< New Cascading Level state */ - RFAL_NFCA_CR_SDD, /*!< Perform anticollsion state */ - RFAL_NFCA_CR_SEL, /*!< Perform CL Selection state */ - RFAL_NFCA_CR_DONE /*!< Collision Resolution done state */ -} colResState; - -/*! Colission Resolution context */ -typedef struct { - uint8_t devLimit; /*!< Device limit to be used */ - rfalComplianceMode compMode; /*!< Compliancy mode to be used */ - rfalNfcaListenDevice* - nfcaDevList; /*!< Location of the device list */ - uint8_t* devCnt; /*!< Location of the device counter */ - bool collPending; /*!< Collision pending flag */ - - bool* collPend; /*!< Location of collision pending flag (Single CR) */ - rfalNfcaSelReq selReq; /*!< SelReqused during anticollision (Single CR) */ - rfalNfcaSelRes* selRes; /*!< Location to place of the SEL_RES(SAK) (Single CR) */ - uint8_t* nfcId1; /*!< Location to place the NFCID1 (Single CR) */ - uint8_t* nfcId1Len; /*!< Location to place the NFCID1 length (Single CR) */ - uint8_t cascadeLv; /*!< Current Cascading Level (Single CR) */ - colResState state; /*!< Single Collision Resolution state (Single CR) */ - uint8_t bytesTxRx; /*!< TxRx bytes used during anticollision loop (Single CR) */ - uint8_t bitsTxRx; /*!< TxRx bits used during anticollision loop (Single CR) */ - uint16_t rxLen; - uint32_t tmrFDT; /*!< FDT timer used between SED_REQs (Single CR) */ - uint8_t retries; /*!< Retries to be performed upon a timeout error (Single CR)*/ - uint8_t backtrackCnt; /*!< Backtrack retries (Single CR) */ - bool doBacktrack; /*!< Backtrack flag (Single CR) */ -} colResParams; - -/*! RFAL NFC-A instance */ -typedef struct { - colResParams CR; /*!< Collision Resolution context */ -} rfalNfca; - -/*! SLP_REQ (HLTA) format Digital 1.1 6.9.1 & Table 20 */ -typedef struct { - uint8_t frame[RFAL_NFCA_SLP_REQ_LEN]; /*!< SLP: 0x50 0x00 */ -} rfalNfcaSlpReq; - -/* -****************************************************************************** -* LOCAL VARIABLES -****************************************************************************** -*/ -static rfalNfca gNfca; /*!< RFAL NFC-A instance */ - -/* -****************************************************************************** -* LOCAL FUNCTION PROTOTYPES -****************************************************************************** -*/ -static uint8_t rfalNfcaCalculateBcc(const uint8_t* buf, uint8_t bufLen); -static ReturnCode rfalNfcaPollerStartSingleCollisionResolution( - uint8_t devLimit, - bool* collPending, - rfalNfcaSelRes* selRes, - uint8_t* nfcId1, - uint8_t* nfcId1Len); -static ReturnCode rfalNfcaPollerGetSingleCollisionResolutionStatus(void); - -/* - ****************************************************************************** - * LOCAL FUNCTIONS - ****************************************************************************** - */ - -static uint8_t rfalNfcaCalculateBcc(const uint8_t* buf, uint8_t bufLen) { - uint8_t i; - uint8_t BCC; - - BCC = 0; - - /* BCC is XOR over first 4 bytes of the SDD_RES Digital 1.1 6.7.2 */ - for(i = 0; i < bufLen; i++) { - BCC ^= buf[i]; - } - - return BCC; -} - -/*******************************************************************************/ -static ReturnCode rfalNfcaPollerStartSingleCollisionResolution( - uint8_t devLimit, - bool* collPending, - rfalNfcaSelRes* selRes, - uint8_t* nfcId1, - uint8_t* nfcId1Len) { - /* Check parameters */ - if((collPending == NULL) || (selRes == NULL) || (nfcId1 == NULL) || (nfcId1Len == NULL)) { - return ERR_PARAM; - } - - /* Initialize output parameters */ - *collPending = false; /* Activity 1.1 9.3.4.6 */ - *nfcId1Len = 0; - ST_MEMSET(nfcId1, 0x00, RFAL_NFCA_CASCADE_3_UID_LEN); - - /* Save parameters */ - gNfca.CR.devLimit = devLimit; - gNfca.CR.collPend = collPending; - gNfca.CR.selRes = selRes; - gNfca.CR.nfcId1 = nfcId1; - gNfca.CR.nfcId1Len = nfcId1Len; - - platformTimerDestroy(gNfca.CR.tmrFDT); - gNfca.CR.tmrFDT = 0U; - gNfca.CR.retries = RFAL_NFCA_N_RETRANS; - gNfca.CR.cascadeLv = (uint8_t)RFAL_NFCA_SEL_CASCADE_L1; - gNfca.CR.state = RFAL_NFCA_CR_CL; - - gNfca.CR.doBacktrack = false; - gNfca.CR.backtrackCnt = 3U; - - return ERR_NONE; -} - -/*******************************************************************************/ -static ReturnCode rfalNfcaPollerGetSingleCollisionResolutionStatus(void) { - ReturnCode ret; - uint8_t collBit = 1U; /* standards mandate or recommend collision bit to be set to One. */ - - /* Check if FDT timer is still running */ - if(!platformTimerIsExpired(gNfca.CR.tmrFDT) && (gNfca.CR.tmrFDT != 0U)) { - return ERR_BUSY; - } - - /*******************************************************************************/ - /* Go through all Cascade Levels Activity 1.1 9.3.4 */ - if(gNfca.CR.cascadeLv > (uint8_t)RFAL_NFCA_SEL_CASCADE_L3) { - return ERR_INTERNAL; - } - - switch(gNfca.CR.state) { - /*******************************************************************************/ - case RFAL_NFCA_CR_CL: - - /* Initialize the SDD_REQ to send for the new cascade level */ - ST_MEMSET((uint8_t*)&gNfca.CR.selReq, 0x00, sizeof(rfalNfcaSelReq)); - - gNfca.CR.bytesTxRx = RFAL_NFCA_SDD_REQ_LEN; - gNfca.CR.bitsTxRx = 0U; - gNfca.CR.state = RFAL_NFCA_CR_SDD; - - /* fall through */ - - /*******************************************************************************/ - case RFAL_NFCA_CR_SDD: /* PRQA S 2003 # MISRA 16.3 - Intentional fall through */ - - /* Calculate SEL_CMD and SEL_PAR with the bytes/bits to be sent */ - gNfca.CR.selReq.selCmd = rfalNfcaCLn2SELCMD(gNfca.CR.cascadeLv); - gNfca.CR.selReq.selPar = rfalNfcaSelPar(gNfca.CR.bytesTxRx, gNfca.CR.bitsTxRx); - - /* Send SDD_REQ (Anticollision frame) */ - ret = rfalISO14443ATransceiveAnticollisionFrame( - (uint8_t*)&gNfca.CR.selReq, - &gNfca.CR.bytesTxRx, - &gNfca.CR.bitsTxRx, - &gNfca.CR.rxLen, - RFAL_NFCA_FDTMIN); - - /* Retry upon timeout EMVCo 2.6 9.6.1.3 */ - if((ret == ERR_TIMEOUT) && (gNfca.CR.devLimit == 0U) && (gNfca.CR.retries != 0U)) { - gNfca.CR.retries--; - platformTimerDestroy(gNfca.CR.tmrFDT); - gNfca.CR.tmrFDT = platformTimerCreate(RFAL_NFCA_T_RETRANS); - break; - } - - /* Covert rxLen into bytes */ - gNfca.CR.rxLen = rfalConvBitsToBytes(gNfca.CR.rxLen); - - if((ret == ERR_TIMEOUT) && (gNfca.CR.backtrackCnt != 0U) && (!gNfca.CR.doBacktrack) && - !((RFAL_NFCA_SDD_REQ_LEN == gNfca.CR.bytesTxRx) && (0U == gNfca.CR.bitsTxRx))) { - /* In multiple card scenarios it may always happen that some - * collisions of a weaker tag go unnoticed. If then a later - * collision is recognized and the strong tag has a 0 at the - * collision position then no tag will respond. Catch this - * corner case and then try with the bit being sent as zero. */ - rfalNfcaSensRes sensRes; - ret = ERR_RF_COLLISION; - rfalNfcaPollerCheckPresence(RFAL_14443A_SHORTFRAME_CMD_REQA, &sensRes); - /* Algorithm below does a post-increment, decrement to go back to current position */ - if(0U == gNfca.CR.bitsTxRx) { - gNfca.CR.bitsTxRx = 7; - gNfca.CR.bytesTxRx--; - } else { - gNfca.CR.bitsTxRx--; - } - collBit = - (uint8_t)(((uint8_t*)&gNfca.CR.selReq)[gNfca.CR.bytesTxRx] & (1U << gNfca.CR.bitsTxRx)); - collBit = (uint8_t)((0U == collBit) ? 1U : 0U); // invert the collision bit - gNfca.CR.doBacktrack = true; - gNfca.CR.backtrackCnt--; - } else { - gNfca.CR.doBacktrack = false; - } - - if(ret == ERR_RF_COLLISION) { - /* Check received length */ - if((gNfca.CR.bytesTxRx + ((gNfca.CR.bitsTxRx != 0U) ? 1U : 0U)) > - (RFAL_NFCA_SDD_RES_LEN + RFAL_NFCA_SDD_REQ_LEN)) { - return ERR_PROTO; - } - - if(((gNfca.CR.bytesTxRx + ((gNfca.CR.bitsTxRx != 0U) ? 1U : 0U)) > - (RFAL_NFCA_CASCADE_1_UID_LEN + RFAL_NFCA_SDD_REQ_LEN)) && - (gNfca.CR.backtrackCnt != 0U)) { /* Collision in BCC: Anticollide only UID part */ - gNfca.CR.backtrackCnt--; - gNfca.CR.bytesTxRx = RFAL_NFCA_CASCADE_1_UID_LEN + RFAL_NFCA_SDD_REQ_LEN - 1U; - gNfca.CR.bitsTxRx = 7; - collBit = - (uint8_t)(((uint8_t*)&gNfca.CR.selReq)[gNfca.CR.bytesTxRx] & (1U << gNfca.CR.bitsTxRx)); /* Not a real collision, extract the actual bit for the subsequent code */ - } - - if((gNfca.CR.devLimit == 0U) && !(*gNfca.CR.collPend)) { - /* Activity 1.0 & 1.1 9.3.4.12: If CON_DEVICES_LIMIT has a value of 0, then - * NFC Forum Device is configured to perform collision detection only */ - *gNfca.CR.collPend = true; - return ERR_IGNORE; - } - - *gNfca.CR.collPend = true; - - /* Set and select the collision bit, with the number of bytes/bits successfully TxRx */ - if(collBit != 0U) { - ((uint8_t*)&gNfca.CR.selReq)[gNfca.CR.bytesTxRx] = - (uint8_t)(((uint8_t*)&gNfca.CR.selReq)[gNfca.CR.bytesTxRx] | (1U << gNfca.CR.bitsTxRx)); /* MISRA 10.3 */ - } else { - ((uint8_t*)&gNfca.CR.selReq)[gNfca.CR.bytesTxRx] = - (uint8_t)(((uint8_t*)&gNfca.CR.selReq)[gNfca.CR.bytesTxRx] & ~(1U << gNfca.CR.bitsTxRx)); /* MISRA 10.3 */ - } - - gNfca.CR.bitsTxRx++; - - /* Check if number of bits form a byte */ - if(gNfca.CR.bitsTxRx == RFAL_BITS_IN_BYTE) { - gNfca.CR.bitsTxRx = 0; - gNfca.CR.bytesTxRx++; - } - break; - } - - /*******************************************************************************/ - /* Check if Collision loop has failed */ - if(ret != ERR_NONE) { - return ret; - } - - /* If collisions are to be reported check whether the response is complete */ - if((gNfca.CR.devLimit == 0U) && (gNfca.CR.rxLen != sizeof(rfalNfcaSddRes))) { - return ERR_PROTO; - } - - /* Check if the received BCC match */ - if(gNfca.CR.selReq.bcc != - rfalNfcaCalculateBcc(gNfca.CR.selReq.nfcid1, RFAL_NFCA_CASCADE_1_UID_LEN)) { - return ERR_PROTO; - } - - /*******************************************************************************/ - /* Anticollision OK, Select this Cascade Level */ - gNfca.CR.selReq.selPar = RFAL_NFCA_SEL_SELPAR; - - gNfca.CR.retries = RFAL_NFCA_N_RETRANS; - gNfca.CR.state = RFAL_NFCA_CR_SEL; - break; - - /*******************************************************************************/ - case RFAL_NFCA_CR_SEL: - - /* Send SEL_REQ (Select command) - Retry upon timeout EMVCo 2.6 9.6.1.3 */ - ret = rfalTransceiveBlockingTxRx( - (uint8_t*)&gNfca.CR.selReq, - sizeof(rfalNfcaSelReq), - (uint8_t*)gNfca.CR.selRes, - sizeof(rfalNfcaSelRes), - &gNfca.CR.rxLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_NFCA_FDTMIN); - - /* Retry upon timeout EMVCo 2.6 9.6.1.3 */ - if((ret == ERR_TIMEOUT) && (gNfca.CR.devLimit == 0U) && (gNfca.CR.retries != 0U)) { - gNfca.CR.retries--; - platformTimerDestroy(gNfca.CR.tmrFDT); - gNfca.CR.tmrFDT = platformTimerCreate(RFAL_NFCA_T_RETRANS); - break; - } - - if(ret != ERR_NONE) { - return ret; - } - - /* Ensure proper response length */ - if(gNfca.CR.rxLen != sizeof(rfalNfcaSelRes)) { - return ERR_PROTO; - } - - /*******************************************************************************/ - /* Check cascade byte, if cascade tag then go next cascade level */ - if(*gNfca.CR.selReq.nfcid1 == RFAL_NFCA_SDD_CT) { - /* Cascade Tag present, store nfcid1 bytes (excluding cascade tag) and continue for next CL */ - ST_MEMCPY( - &gNfca.CR.nfcId1[*gNfca.CR.nfcId1Len], - &((uint8_t*)&gNfca.CR.selReq.nfcid1)[RFAL_NFCA_SDD_CT_LEN], - (RFAL_NFCA_CASCADE_1_UID_LEN - RFAL_NFCA_SDD_CT_LEN)); - *gNfca.CR.nfcId1Len += (RFAL_NFCA_CASCADE_1_UID_LEN - RFAL_NFCA_SDD_CT_LEN); - - /* Go to next cascade level */ - gNfca.CR.state = RFAL_NFCA_CR_CL; - gNfca.CR.cascadeLv++; - } else { - /* UID Selection complete, Stop Cascade Level loop */ - ST_MEMCPY( - &gNfca.CR.nfcId1[*gNfca.CR.nfcId1Len], - (uint8_t*)&gNfca.CR.selReq.nfcid1, - RFAL_NFCA_CASCADE_1_UID_LEN); - *gNfca.CR.nfcId1Len += RFAL_NFCA_CASCADE_1_UID_LEN; - - gNfca.CR.state = RFAL_NFCA_CR_DONE; - break; /* Only flag operation complete on the next execution */ - } - break; - - /*******************************************************************************/ - case RFAL_NFCA_CR_DONE: - return ERR_NONE; - - /*******************************************************************************/ - default: - return ERR_WRONG_STATE; - } - return ERR_BUSY; -} - -/* -****************************************************************************** -* GLOBAL FUNCTIONS -****************************************************************************** -*/ - -/*******************************************************************************/ -ReturnCode rfalNfcaPollerInitialize(void) { - ReturnCode ret; - - EXIT_ON_ERR(ret, rfalSetMode(RFAL_MODE_POLL_NFCA, RFAL_BR_106, RFAL_BR_106)); - rfalSetErrorHandling(RFAL_ERRORHANDLING_NFC); - - rfalSetGT(RFAL_GT_NFCA); - rfalSetFDTListen(RFAL_FDT_LISTEN_NFCA_POLLER); - rfalSetFDTPoll(RFAL_FDT_POLL_NFCA_POLLER); - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcaPollerCheckPresence(rfal14443AShortFrameCmd cmd, rfalNfcaSensRes* sensRes) { - ReturnCode ret; - uint16_t rcvLen; - - /* Digital 1.1 6.10.1.3 For Commands ALL_REQ, SENS_REQ, SDD_REQ, and SEL_REQ, the NFC Forum Device * - * MUST treat receipt of a Listen Frame at a time after FDT(Listen, min) as a Timeour Error */ - - ret = rfalISO14443ATransceiveShortFrame( - cmd, - (uint8_t*)sensRes, - (uint8_t)rfalConvBytesToBits(sizeof(rfalNfcaSensRes)), - &rcvLen, - RFAL_NFCA_FDTMIN); - if((ret == ERR_RF_COLLISION) || (ret == ERR_CRC) || (ret == ERR_NOMEM) || - (ret == ERR_FRAMING) || (ret == ERR_PAR)) { - ret = ERR_NONE; - } - - return ret; -} - -/*******************************************************************************/ -ReturnCode - rfalNfcaPollerTechnologyDetection(rfalComplianceMode compMode, rfalNfcaSensRes* sensRes) { - ReturnCode ret; - - EXIT_ON_ERR( - ret, - rfalNfcaPollerCheckPresence( - ((compMode == RFAL_COMPLIANCE_MODE_EMV) ? RFAL_14443A_SHORTFRAME_CMD_WUPA : - RFAL_14443A_SHORTFRAME_CMD_REQA), - sensRes)); - - /* Send SLP_REQ as Activity 1.1 9.2.3.6 and EMVCo 2.6 9.2.1.3 */ - if(compMode != RFAL_COMPLIANCE_MODE_ISO) { - rfalNfcaPollerSleep(); - } - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcaPollerSingleCollisionResolution( - uint8_t devLimit, - bool* collPending, - rfalNfcaSelRes* selRes, - uint8_t* nfcId1, - uint8_t* nfcId1Len) { - ReturnCode ret; - - EXIT_ON_ERR( - ret, - rfalNfcaPollerStartSingleCollisionResolution( - devLimit, collPending, selRes, nfcId1, nfcId1Len)); - rfalNfcaRunBlocking(ret, rfalNfcaPollerGetSingleCollisionResolutionStatus()); - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalNfcaPollerStartFullCollisionResolution( - rfalComplianceMode compMode, - uint8_t devLimit, - rfalNfcaListenDevice* nfcaDevList, - uint8_t* devCnt) { - ReturnCode ret; - rfalNfcaSensRes sensRes; - uint16_t rcvLen; - - if((nfcaDevList == NULL) || (devCnt == NULL)) { - return ERR_PARAM; - } - - *devCnt = 0; - ret = ERR_NONE; - - /*******************************************************************************/ - /* Send ALL_REQ before Anticollision if a Sleep was sent before Activity 1.1 9.3.4.1 and EMVco 2.6 9.3.2.1 */ - if(compMode != RFAL_COMPLIANCE_MODE_ISO) { - ret = rfalISO14443ATransceiveShortFrame( - RFAL_14443A_SHORTFRAME_CMD_WUPA, - (uint8_t*)&nfcaDevList->sensRes, - (uint8_t)rfalConvBytesToBits(sizeof(rfalNfcaSensRes)), - &rcvLen, - RFAL_NFCA_FDTMIN); - if(ret != ERR_NONE) { - if((compMode == RFAL_COMPLIANCE_MODE_EMV) || - ((ret != ERR_RF_COLLISION) && (ret != ERR_CRC) && (ret != ERR_FRAMING) && - (ret != ERR_PAR))) { - return ret; - } - } - - /* Check proper SENS_RES/ATQA size */ - if((ret == ERR_NONE) && (rfalConvBytesToBits(sizeof(rfalNfcaSensRes)) != rcvLen)) { - return ERR_PROTO; - } - } - - /*******************************************************************************/ - /* Store the SENS_RES from Technology Detection or from WUPA */ - sensRes = nfcaDevList->sensRes; - - if(devLimit > 0U) /* MISRA 21.18 */ - { - ST_MEMSET(nfcaDevList, 0x00, (sizeof(rfalNfcaListenDevice) * devLimit)); - } - - /* Restore the prev SENS_RES, assuming that the SENS_RES received is from first device - * When only one device is detected it's not woken up then we'll have no SENS_RES (ATQA) */ - nfcaDevList->sensRes = sensRes; - - /* Save parameters */ - gNfca.CR.devCnt = devCnt; - gNfca.CR.devLimit = devLimit; - gNfca.CR.nfcaDevList = nfcaDevList; - gNfca.CR.compMode = compMode; - -#if RFAL_FEATURE_T1T - /*******************************************************************************/ - /* Only check for T1T if previous SENS_RES was received without a transmission * - * error. When collisions occur bits in the SENS_RES may look like a T1T */ - /* If T1T Anticollision is not supported Activity 1.1 9.3.4.3 */ - if(rfalNfcaIsSensResT1T(&nfcaDevList->sensRes) && (devLimit != 0U) && (ret == ERR_NONE) && - (compMode != RFAL_COMPLIANCE_MODE_EMV)) { - /* RID_REQ shall be performed Activity 1.1 9.3.4.24 */ - rfalT1TPollerInitialize(); - EXIT_ON_ERR(ret, rfalT1TPollerRid(&nfcaDevList->ridRes)); - - *devCnt = 1U; - nfcaDevList->isSleep = false; - nfcaDevList->type = RFAL_NFCA_T1T; - nfcaDevList->nfcId1Len = RFAL_NFCA_CASCADE_1_UID_LEN; - ST_MEMCPY(&nfcaDevList->nfcId1, &nfcaDevList->ridRes.uid, RFAL_NFCA_CASCADE_1_UID_LEN); - - return ERR_NONE; - } -#endif /* RFAL_FEATURE_T1T */ - - return rfalNfcaPollerStartSingleCollisionResolution( - devLimit, - &gNfca.CR.collPending, - &nfcaDevList->selRes, - (uint8_t*)&nfcaDevList->nfcId1, - &nfcaDevList->nfcId1Len); -} - -/*******************************************************************************/ -ReturnCode rfalNfcaPollerGetFullCollisionResolutionStatus(void) { - ReturnCode ret; - uint8_t newDevType; - - if((gNfca.CR.nfcaDevList == NULL) || (gNfca.CR.devCnt == NULL)) { - return ERR_WRONG_STATE; - } - - /*******************************************************************************/ - /* Check whether a T1T has already been detected */ - if(rfalNfcaIsSensResT1T(&gNfca.CR.nfcaDevList->sensRes) && - (gNfca.CR.nfcaDevList->type == RFAL_NFCA_T1T)) { - /* T1T doesn't support Anticollision */ - return ERR_NONE; - } - - /*******************************************************************************/ - EXIT_ON_ERR(ret, rfalNfcaPollerGetSingleCollisionResolutionStatus()); - - /* Assign Listen Device */ - newDevType = ((uint8_t)gNfca.CR.nfcaDevList[*gNfca.CR.devCnt].selRes.sak) & - RFAL_NFCA_SEL_RES_CONF_MASK; /* MISRA 10.8 */ - /* PRQA S 4342 1 # MISRA 10.5 - Guaranteed that no invalid enum values are created: see guard_eq_RFAL_NFCA_T2T, .... */ - gNfca.CR.nfcaDevList[*gNfca.CR.devCnt].type = (rfalNfcaListenDeviceType)newDevType; - gNfca.CR.nfcaDevList[*gNfca.CR.devCnt].isSleep = false; - (*gNfca.CR.devCnt)++; - - /* If a collision was detected and device counter is lower than limit Activity 1.1 9.3.4.21 */ - if((*gNfca.CR.devCnt < gNfca.CR.devLimit) && (gNfca.CR.collPending)) { - /* Put this device to Sleep Activity 1.1 9.3.4.22 */ - rfalNfcaPollerSleep(); - gNfca.CR.nfcaDevList[(*gNfca.CR.devCnt - 1U)].isSleep = true; - - /* Send a new SENS_REQ to check for other cards Activity 1.1 9.3.4.23 */ - ret = rfalNfcaPollerCheckPresence( - RFAL_14443A_SHORTFRAME_CMD_REQA, &gNfca.CR.nfcaDevList[*gNfca.CR.devCnt].sensRes); - if(ret == ERR_TIMEOUT) { - /* No more devices found, exit */ - gNfca.CR.collPending = false; - } else { - /* Another device found, continue loop */ - gNfca.CR.collPending = true; - } - } else { - /* Exit loop */ - gNfca.CR.collPending = false; - } - - /*******************************************************************************/ - /* Check if collision resolution shall continue */ - if((*gNfca.CR.devCnt < gNfca.CR.devLimit) && (gNfca.CR.collPending)) { - EXIT_ON_ERR( - ret, - rfalNfcaPollerStartSingleCollisionResolution( - gNfca.CR.devLimit, - &gNfca.CR.collPending, - &gNfca.CR.nfcaDevList[*gNfca.CR.devCnt].selRes, - (uint8_t*)&gNfca.CR.nfcaDevList[*gNfca.CR.devCnt].nfcId1, - &gNfca.CR.nfcaDevList[*gNfca.CR.devCnt].nfcId1Len)); - - return ERR_BUSY; - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcaPollerFullCollisionResolution( - rfalComplianceMode compMode, - uint8_t devLimit, - rfalNfcaListenDevice* nfcaDevList, - uint8_t* devCnt) { - ReturnCode ret; - - EXIT_ON_ERR( - ret, rfalNfcaPollerStartFullCollisionResolution(compMode, devLimit, nfcaDevList, devCnt)); - rfalNfcaRunBlocking(ret, rfalNfcaPollerGetFullCollisionResolutionStatus()); - - return ret; -} - -ReturnCode rfalNfcaPollerSleepFullCollisionResolution( - uint8_t devLimit, - rfalNfcaListenDevice* nfcaDevList, - uint8_t* devCnt) { - bool firstRound; - uint8_t tmpDevCnt; - ReturnCode ret; - - if((nfcaDevList == NULL) || (devCnt == NULL)) { - return ERR_PARAM; - } - - /* Only use ALL_REQ (WUPA) on the first round */ - firstRound = true; - *devCnt = 0; - - /* Perform collision resolution until no new device is found */ - do { - tmpDevCnt = 0; - ret = rfalNfcaPollerFullCollisionResolution( - (firstRound ? RFAL_COMPLIANCE_MODE_NFC : RFAL_COMPLIANCE_MODE_ISO), - (devLimit - *devCnt), - &nfcaDevList[*devCnt], - &tmpDevCnt); - - if((ret == ERR_NONE) && (tmpDevCnt > 0U)) { - *devCnt += tmpDevCnt; - - /* Check whether to seacrh for more devices */ - if(*devCnt < devLimit) { - /* Set last found device to sleep (all others are slept already) */ - rfalNfcaPollerSleep(); - nfcaDevList[((*devCnt) - 1U)].isSleep = true; - - /* Check if any other device is present */ - ret = rfalNfcaPollerCheckPresence( - RFAL_14443A_SHORTFRAME_CMD_REQA, &nfcaDevList[*devCnt].sensRes); - if(ret == ERR_NONE) { - firstRound = false; - continue; - } - } - } - break; - } while(true); - - return ((*devCnt > 0U) ? ERR_NONE : ret); -} - -/*******************************************************************************/ -ReturnCode rfalNfcaPollerSelect(const uint8_t* nfcid1, uint8_t nfcidLen, rfalNfcaSelRes* selRes) { - uint8_t i; - uint8_t cl; - uint8_t nfcidOffset; - uint16_t rxLen; - ReturnCode ret; - rfalNfcaSelReq selReq; - - if((nfcid1 == NULL) || (nfcidLen > RFAL_NFCA_CASCADE_3_UID_LEN) || (selRes == NULL)) { - return ERR_PARAM; - } - - /* Calculate Cascate Level */ - cl = rfalNfcaNfcidLen2CL(nfcidLen); - nfcidOffset = 0; - - /*******************************************************************************/ - /* Go through all Cascade Levels Activity 1.1 9.4.4 */ - for(i = RFAL_NFCA_SEL_CASCADE_L1; i <= cl; i++) { - /* Assign SEL_CMD according to the CLn and SEL_PAR*/ - selReq.selCmd = rfalNfcaCLn2SELCMD(i); - selReq.selPar = RFAL_NFCA_SEL_SELPAR; - - /* Compute NFCID/Data on the SEL_REQ command Digital 1.1 Table 18 */ - if(cl != i) { - *selReq.nfcid1 = RFAL_NFCA_SDD_CT; - ST_MEMCPY( - &selReq.nfcid1[RFAL_NFCA_SDD_CT_LEN], - &nfcid1[nfcidOffset], - (RFAL_NFCA_CASCADE_1_UID_LEN - RFAL_NFCA_SDD_CT_LEN)); - nfcidOffset += (RFAL_NFCA_CASCADE_1_UID_LEN - RFAL_NFCA_SDD_CT_LEN); - } else { - ST_MEMCPY(selReq.nfcid1, &nfcid1[nfcidOffset], RFAL_NFCA_CASCADE_1_UID_LEN); - } - - /* Calculate nfcid's BCC */ - selReq.bcc = rfalNfcaCalculateBcc((uint8_t*)&selReq.nfcid1, sizeof(selReq.nfcid1)); - - /*******************************************************************************/ - /* Send SEL_REQ */ - EXIT_ON_ERR( - ret, - rfalTransceiveBlockingTxRx( - (uint8_t*)&selReq, - sizeof(rfalNfcaSelReq), - (uint8_t*)selRes, - sizeof(rfalNfcaSelRes), - &rxLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_NFCA_FDTMIN)); - - /* Ensure proper response length */ - if(rxLen != sizeof(rfalNfcaSelRes)) { - return ERR_PROTO; - } - } - - /* REMARK: Could check if NFCID1 is complete */ - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcaPollerSleep(void) { - rfalNfcaSlpReq slpReq; - uint8_t rxBuf; /* dummy buffer, just to perform Rx */ - - slpReq.frame[RFAL_NFCA_SLP_CMD_POS] = RFAL_NFCA_SLP_CMD; - slpReq.frame[RFAL_NFCA_SLP_BYTE2_POS] = RFAL_NFCA_SLP_BYTE2; - - rfalTransceiveBlockingTxRx( - (uint8_t*)&slpReq, - sizeof(rfalNfcaSlpReq), - &rxBuf, - sizeof(rxBuf), - NULL, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_NFCA_SLP_FWT); - - /* ISO14443-3 6.4.3 HLTA - If PICC responds with any modulation during 1 ms this response shall be interpreted as not acknowledge - Digital 2.0 6.9.2.1 & EMVCo 3.0 5.6.2.1 - consider the HLTA command always acknowledged - No check to be compliant with NFC and EMVCo, and to improve interoprability (Kovio RFID Tag) - */ - - return ERR_NONE; -} - -/*******************************************************************************/ -bool rfalNfcaListenerIsSleepReq(const uint8_t* buf, uint16_t bufLen) { - /* Check if length and payload match */ - if((bufLen != sizeof(rfalNfcaSlpReq)) || (buf[RFAL_NFCA_SLP_CMD_POS] != RFAL_NFCA_SLP_CMD) || - (buf[RFAL_NFCA_SLP_BYTE2_POS] != RFAL_NFCA_SLP_BYTE2)) { - return false; - } - - return true; -} - -/* If the guards here don't compile then the code above cannot work anymore. */ -extern uint8_t guard_eq_RFAL_NFCA_T2T - [((RFAL_NFCA_SEL_RES_CONF_MASK & (uint8_t)RFAL_NFCA_T2T) == (uint8_t)RFAL_NFCA_T2T) ? 1 : (-1)]; -extern uint8_t guard_eq_RFAL_NFCA_T4T - [((RFAL_NFCA_SEL_RES_CONF_MASK & (uint8_t)RFAL_NFCA_T4T) == (uint8_t)RFAL_NFCA_T4T) ? 1 : (-1)]; -extern uint8_t guard_eq_RFAL_NFCA_NFCDEP - [((RFAL_NFCA_SEL_RES_CONF_MASK & (uint8_t)RFAL_NFCA_NFCDEP) == (uint8_t)RFAL_NFCA_NFCDEP) ? - 1 : - (-1)]; -extern uint8_t guard_eq_RFAL_NFCA_T4T_NFCDEP - [((RFAL_NFCA_SEL_RES_CONF_MASK & (uint8_t)RFAL_NFCA_T4T_NFCDEP) == - (uint8_t)RFAL_NFCA_T4T_NFCDEP) ? - 1 : - (-1)]; -#endif /* RFAL_FEATURE_NFCA */ diff --git a/lib/ST25RFAL002/source/rfal_nfcb.c b/lib/ST25RFAL002/source/rfal_nfcb.c deleted file mode 100644 index 841b2554aad..00000000000 --- a/lib/ST25RFAL002/source/rfal_nfcb.c +++ /dev/null @@ -1,519 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_nfcb.c - * - * \author Gustavo Patricio - * - * \brief Implementation of NFC-B (ISO14443B) helpers - * - */ - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "rfal_nfcb.h" -#include "utils.h" - -/* - ****************************************************************************** - * ENABLE SWITCH - ****************************************************************************** - */ - -#ifndef RFAL_FEATURE_NFCB -#define RFAL_FEATURE_NFCB false /* NFC-B module configuration missing. Disabled by default */ -#endif - -#if RFAL_FEATURE_NFCB - -/* - ****************************************************************************** - * GLOBAL DEFINES - ****************************************************************************** - */ - -#define RFAL_NFCB_SENSB_REQ_EXT_SENSB_RES_SUPPORTED \ - 0x10U /*!< Bit mask for Extended SensB Response support in SENSB_REQ */ -#define RFAL_NFCB_SENSB_RES_PROT_TYPE_RFU \ - 0x08U /*!< Bit mask for Protocol Type RFU in SENSB_RES */ -#define RFAL_NFCB_SLOT_MARKER_SC_SHIFT \ - 4U /*!< Slot Code position on SLOT_MARKER APn */ - -#define RFAL_NFCB_SLOTMARKER_SLOTCODE_MIN \ - 1U /*!< SLOT_MARKER Slot Code minimum Digital 1.1 Table 37 */ -#define RFAL_NFCB_SLOTMARKER_SLOTCODE_MAX \ - 16U /*!< SLOT_MARKER Slot Code maximum Digital 1.1 Table 37 */ - -#define RFAL_NFCB_ACTIVATION_FWT \ - (RFAL_NFCB_FWTSENSB + RFAL_NFCB_DTPOLL_20) /*!< FWT(SENSB) + dTbPoll Digital 2.0 7.9.1.3 */ - -/*! Advanced and Extended bit mask in Parameter of SENSB_REQ */ -#define RFAL_NFCB_SENSB_REQ_PARAM \ - (RFAL_NFCB_SENSB_REQ_ADV_FEATURE | RFAL_NFCB_SENSB_REQ_EXT_SENSB_RES_SUPPORTED) - -/*! NFC-B commands definition */ -enum { - RFAL_NFCB_CMD_SENSB_REQ = 0x05, /*!< SENSB_REQ (REQB) & SLOT_MARKER Digital 1.1 Table 24 */ - RFAL_NFCB_CMD_SENSB_RES = 0x50, /*!< SENSB_RES (ATQB) & SLOT_MARKER Digital 1.1 Table 27 */ - RFAL_NFCB_CMD_SLPB_REQ = 0x50, /*!< SLPB_REQ (HLTB command) Digital 1.1 Table 38 */ - RFAL_NFCB_CMD_SLPB_RES = 0x00 /*!< SLPB_RES (HLTB Answer) Digital 1.1 Table 39 */ -}; - -/* - ****************************************************************************** - * GLOBAL MACROS - ****************************************************************************** - */ - -#define rfalNfcbNI2NumberOfSlots(ni) \ - (uint8_t)(1U << (ni)) /*!< Converts the Number of slots Identifier to slot number */ - -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ - -/*! ALLB_REQ (WUPB) and SENSB_REQ (REQB) Command Format Digital 1.1 7.6.1 */ -typedef struct { - uint8_t cmd; /*!< xxxxB_REQ: 05h */ - uint8_t AFI; /*!< NFC Identifier */ - uint8_t PARAM; /*!< Application Data */ -} rfalNfcbSensbReq; - -/*! SLOT_MARKER Command format Digital 1.1 7.7.1 */ -typedef struct { - uint8_t APn; /*!< Slot number 2..16 | 0101b */ -} rfalNfcbSlotMarker; - -/*! SLPB_REQ (HLTB) Command Format Digital 1.1 7.8.1 */ -typedef struct { - uint8_t cmd; /*!< SLPB_REQ: 50h */ - uint8_t nfcid0[RFAL_NFCB_NFCID0_LEN]; /*!< NFC Identifier (PUPI)*/ -} rfalNfcbSlpbReq; - -/*! SLPB_RES (HLTB) Response Format Digital 1.1 7.8.2 */ -typedef struct { - uint8_t cmd; /*!< SLPB_RES: 00h */ -} rfalNfcbSlpbRes; - -/*! RFAL NFC-B instance */ -typedef struct { - uint8_t AFI; /*!< AFI to be used */ - uint8_t PARAM; /*!< PARAM to be used */ -} rfalNfcb; - -/* -****************************************************************************** -* LOCAL FUNCTION PROTOTYPES -****************************************************************************** -*/ -static ReturnCode rfalNfcbCheckSensbRes(const rfalNfcbSensbRes* sensbRes, uint8_t sensbResLen); - -/* -****************************************************************************** -* LOCAL VARIABLES -****************************************************************************** -*/ - -static rfalNfcb gRfalNfcb; /*!< RFAL NFC-B Instance */ - -/* -****************************************************************************** -* LOCAL FUNCTIONS -****************************************************************************** -*/ - -/*******************************************************************************/ -static ReturnCode rfalNfcbCheckSensbRes(const rfalNfcbSensbRes* sensbRes, uint8_t sensbResLen) { - /* Check response length */ - if(((sensbResLen != RFAL_NFCB_SENSB_RES_LEN) && - (sensbResLen != RFAL_NFCB_SENSB_RES_EXT_LEN))) { - return ERR_PROTO; - } - - /* Check SENSB_RES and Protocol Type Digital 1.1 7.6.2.19 */ - if(((sensbRes->protInfo.FsciProType & RFAL_NFCB_SENSB_RES_PROT_TYPE_RFU) != 0U) || - (sensbRes->cmd != (uint8_t)RFAL_NFCB_CMD_SENSB_RES)) { - return ERR_PROTO; - } - return ERR_NONE; -} - -/* -****************************************************************************** -* GLOBAL FUNCTIONS -****************************************************************************** -*/ - -/*******************************************************************************/ -ReturnCode rfalNfcbPollerInitialize(void) { - ReturnCode ret; - - EXIT_ON_ERR(ret, rfalSetMode(RFAL_MODE_POLL_NFCB, RFAL_BR_106, RFAL_BR_106)); - rfalSetErrorHandling(RFAL_ERRORHANDLING_NFC); - - rfalSetGT(RFAL_GT_NFCB); - rfalSetFDTListen(RFAL_FDT_LISTEN_NFCB_POLLER); - rfalSetFDTPoll(RFAL_FDT_POLL_NFCB_POLLER); - - gRfalNfcb.AFI = RFAL_NFCB_AFI; - gRfalNfcb.PARAM = RFAL_NFCB_PARAM; - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcbPollerInitializeWithParams(uint8_t AFI, uint8_t PARAM) { - ReturnCode ret; - - EXIT_ON_ERR(ret, rfalNfcbPollerInitialize()); - - gRfalNfcb.AFI = AFI; - gRfalNfcb.PARAM = (PARAM & RFAL_NFCB_SENSB_REQ_PARAM); - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcbPollerCheckPresence( - rfalNfcbSensCmd cmd, - rfalNfcbSlots slots, - rfalNfcbSensbRes* sensbRes, - uint8_t* sensbResLen) { - uint16_t rxLen; - ReturnCode ret; - rfalNfcbSensbReq sensbReq; - - /* Check if the command requested and given the slot number are valid */ - if(((RFAL_NFCB_SENS_CMD_SENSB_REQ != cmd) && (RFAL_NFCB_SENS_CMD_ALLB_REQ != cmd)) || - (slots > RFAL_NFCB_SLOT_NUM_16) || (sensbRes == NULL) || (sensbResLen == NULL)) { - return ERR_PARAM; - } - - *sensbResLen = 0; - ST_MEMSET(sensbRes, 0x00, sizeof(rfalNfcbSensbRes)); - - /* Compute SENSB_REQ */ - sensbReq.cmd = RFAL_NFCB_CMD_SENSB_REQ; - sensbReq.AFI = gRfalNfcb.AFI; - sensbReq.PARAM = - (((uint8_t)gRfalNfcb.PARAM & RFAL_NFCB_SENSB_REQ_PARAM) | (uint8_t)cmd | (uint8_t)slots); - - /* Send SENSB_REQ and disable AGC to detect collisions */ - ret = rfalTransceiveBlockingTxRx( - (uint8_t*)&sensbReq, - sizeof(rfalNfcbSensbReq), - (uint8_t*)sensbRes, - sizeof(rfalNfcbSensbRes), - &rxLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_NFCB_FWTSENSB); - - *sensbResLen = (uint8_t)rxLen; - - /* Check if a transmission error was detected */ - if((ret == ERR_CRC) || (ret == ERR_FRAMING)) { - /* Invalidate received frame as an error was detected (CollisionResolution checks if valid) */ - *sensbResLen = 0; - return ERR_NONE; - } - - if(ret == ERR_NONE) { - return rfalNfcbCheckSensbRes(sensbRes, *sensbResLen); - } - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalNfcbPollerSleep(const uint8_t* nfcid0) { - uint16_t rxLen; - ReturnCode ret; - rfalNfcbSlpbReq slpbReq; - rfalNfcbSlpbRes slpbRes; - - if(nfcid0 == NULL) { - return ERR_PARAM; - } - - /* Compute SLPB_REQ */ - slpbReq.cmd = RFAL_NFCB_CMD_SLPB_REQ; - ST_MEMCPY(slpbReq.nfcid0, nfcid0, RFAL_NFCB_NFCID0_LEN); - - EXIT_ON_ERR( - ret, - rfalTransceiveBlockingTxRx( - (uint8_t*)&slpbReq, - sizeof(rfalNfcbSlpbReq), - (uint8_t*)&slpbRes, - sizeof(rfalNfcbSlpbRes), - &rxLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_NFCB_ACTIVATION_FWT)); - - /* Check SLPB_RES */ - if((rxLen != sizeof(rfalNfcbSlpbRes)) || (slpbRes.cmd != (uint8_t)RFAL_NFCB_CMD_SLPB_RES)) { - return ERR_PROTO; - } - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode - rfalNfcbPollerSlotMarker(uint8_t slotCode, rfalNfcbSensbRes* sensbRes, uint8_t* sensbResLen) { - ReturnCode ret; - rfalNfcbSlotMarker slotMarker; - uint16_t rxLen; - - /* Check parameters */ - if((sensbRes == NULL) || (sensbResLen == NULL) || - (slotCode < RFAL_NFCB_SLOTMARKER_SLOTCODE_MIN) || - (slotCode > RFAL_NFCB_SLOTMARKER_SLOTCODE_MAX)) { - return ERR_PARAM; - } - /* Compose and send SLOT_MARKER with disabled AGC to detect collisions */ - slotMarker.APn = - ((slotCode << RFAL_NFCB_SLOT_MARKER_SC_SHIFT) | (uint8_t)RFAL_NFCB_CMD_SENSB_REQ); - - ret = rfalTransceiveBlockingTxRx( - (uint8_t*)&slotMarker, - sizeof(rfalNfcbSlotMarker), - (uint8_t*)sensbRes, - sizeof(rfalNfcbSensbRes), - &rxLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_NFCB_ACTIVATION_FWT); - - *sensbResLen = (uint8_t)rxLen; - - /* Check if a transmission error was detected */ - if((ret == ERR_CRC) || (ret == ERR_FRAMING)) { - return ERR_RF_COLLISION; - } - - if(ret == ERR_NONE) { - return rfalNfcbCheckSensbRes(sensbRes, *sensbResLen); - } - - return ret; -} - -ReturnCode rfalNfcbPollerTechnologyDetection( - rfalComplianceMode compMode, - rfalNfcbSensbRes* sensbRes, - uint8_t* sensbResLen) { - NO_WARNING(compMode); - - return rfalNfcbPollerCheckPresence( - RFAL_NFCB_SENS_CMD_SENSB_REQ, RFAL_NFCB_SLOT_NUM_1, sensbRes, sensbResLen); -} - -/*******************************************************************************/ -ReturnCode rfalNfcbPollerCollisionResolution( - rfalComplianceMode compMode, - uint8_t devLimit, - rfalNfcbListenDevice* nfcbDevList, - uint8_t* devCnt) { - bool colPending; /* dummy */ - return rfalNfcbPollerSlottedCollisionResolution( - compMode, - devLimit, - RFAL_NFCB_SLOT_NUM_1, - RFAL_NFCB_SLOT_NUM_16, - nfcbDevList, - devCnt, - &colPending); -} - -/*******************************************************************************/ -ReturnCode rfalNfcbPollerSlottedCollisionResolution( - rfalComplianceMode compMode, - uint8_t devLimit, - rfalNfcbSlots initSlots, - rfalNfcbSlots endSlots, - rfalNfcbListenDevice* nfcbDevList, - uint8_t* devCnt, - bool* colPending) { - ReturnCode ret; - uint8_t slotsNum; - uint8_t slotCode; - uint8_t curDevCnt; - - /* Check parameters. In ISO | Activity 1.0 mode the initial slots must be 1 as continuation of Technology Detection */ - if((nfcbDevList == NULL) || (devCnt == NULL) || (colPending == NULL) || - (initSlots > RFAL_NFCB_SLOT_NUM_16) || (endSlots > RFAL_NFCB_SLOT_NUM_16) || - ((compMode == RFAL_COMPLIANCE_MODE_ISO) && (initSlots != RFAL_NFCB_SLOT_NUM_1))) { - return ERR_PARAM; - } - - /* Initialise as no error in case Activity 1.0 where the previous SENSB_RES from technology detection should be used */ - ret = ERR_NONE; - *devCnt = 0; - curDevCnt = 0; - *colPending = false; - - /* Send ALLB_REQ Activity 1.1 9.3.5.2 and 9.3.5.3 (Symbol 1 and 2) */ - if(compMode != RFAL_COMPLIANCE_MODE_ISO) { - ret = rfalNfcbPollerCheckPresence( - RFAL_NFCB_SENS_CMD_ALLB_REQ, - initSlots, - &nfcbDevList->sensbRes, - &nfcbDevList->sensbResLen); - if((ret != ERR_NONE) && (initSlots == RFAL_NFCB_SLOT_NUM_1)) { - return ret; - } - } - - /* Check if there was a transmission error on WUPB EMVCo 2.6 9.3.3.1 */ - if((compMode == RFAL_COMPLIANCE_MODE_EMV) && (nfcbDevList->sensbResLen == 0U)) { - return ERR_FRAMING; - } - - for(slotsNum = (uint8_t)initSlots; slotsNum <= (uint8_t)endSlots; slotsNum++) { - do { - /* Activity 1.1 9.3.5.23 - Symbol 22 */ - if((compMode == RFAL_COMPLIANCE_MODE_NFC) && (curDevCnt != 0U)) { - rfalNfcbPollerSleep(nfcbDevList[((*devCnt) - (uint8_t)1U)].sensbRes.nfcid0); - nfcbDevList[((*devCnt) - (uint8_t)1U)].isSleep = true; - } - - /* Send SENSB_REQ with number of slots if not the first Activity 1.1 9.3.5.24 - Symbol 23 */ - if((slotsNum != (uint8_t)initSlots) || *colPending) { - /* PRQA S 4342 1 # MISRA 10.5 - Layout of rfalNfcbSlots and above loop guarantee that no invalid enum values are created. */ - ret = rfalNfcbPollerCheckPresence( - RFAL_NFCB_SENS_CMD_SENSB_REQ, - (rfalNfcbSlots)slotsNum, - &nfcbDevList[*devCnt].sensbRes, - &nfcbDevList[*devCnt].sensbResLen); - } - - /* Activity 1.1 9.3.5.6 - Symbol 5 */ - slotCode = 0; - curDevCnt = 0; - *colPending = false; - - do { - /* Activity 1.1 9.3.5.26 - Symbol 25 */ - if(slotCode != 0U) { - ret = rfalNfcbPollerSlotMarker( - slotCode, - &nfcbDevList[*devCnt].sensbRes, - &nfcbDevList[*devCnt].sensbResLen); - } - - /* Activity 1.1 9.3.5.7 and 9.3.5.8 - Symbol 6 */ - if(ret != ERR_TIMEOUT) { - /* Activity 1.1 9.3.5.8 - Symbol 7 */ - if((rfalNfcbCheckSensbRes( - &nfcbDevList[*devCnt].sensbRes, nfcbDevList[*devCnt].sensbResLen) == - ERR_NONE) && - (ret == ERR_NONE)) { - nfcbDevList[*devCnt].isSleep = false; - - if(compMode == RFAL_COMPLIANCE_MODE_EMV) { - (*devCnt)++; - return ret; - } else if(compMode == RFAL_COMPLIANCE_MODE_ISO) { - /* Activity 1.0 9.3.5.8 - Symbol 7 */ - (*devCnt)++; - curDevCnt++; - - /* Activity 1.0 9.3.5.10 - Symbol 9 */ - if((*devCnt >= devLimit) || - (slotsNum == (uint8_t)RFAL_NFCB_SLOT_NUM_1)) { - return ret; - } - - /* Activity 1.0 9.3.5.11 - Symbol 10 */ - rfalNfcbPollerSleep(nfcbDevList[*devCnt - 1U].sensbRes.nfcid0); - nfcbDevList[*devCnt - 1U].isSleep = true; - } else if(compMode == RFAL_COMPLIANCE_MODE_NFC) { - /* Activity 1.1 9.3.5.10 and 9.3.5.11 - Symbol 9 and Symbol 11*/ - if(curDevCnt != 0U) { - rfalNfcbPollerSleep( - nfcbDevList[(*devCnt) - (uint8_t)1U].sensbRes.nfcid0); - nfcbDevList[(*devCnt) - (uint8_t)1U].isSleep = true; - } - - /* Activity 1.1 9.3.5.12 - Symbol 11 */ - (*devCnt)++; - curDevCnt++; - - /* Activity 1.1 9.3.5.6 - Symbol 13 */ - if((*devCnt >= devLimit) || - (slotsNum == (uint8_t)RFAL_NFCB_SLOT_NUM_1)) { - return ret; - } - } else { - /* MISRA 15.7 - Empty else */ - } - } else { - /* If deviceLimit is set to 0 the NFC Forum Device is configured to perform collision detection only Activity 1.0 and 1.1 9.3.5.5 - Symbol 4 */ - if((devLimit == 0U) && (slotsNum == (uint8_t)RFAL_NFCB_SLOT_NUM_1)) { - return ERR_RF_COLLISION; - } - - /* Activity 1.1 9.3.5.9 - Symbol 8 */ - *colPending = true; - } - } - - /* Activity 1.1 9.3.5.15 - Symbol 14 */ - slotCode++; - } while(slotCode < rfalNfcbNI2NumberOfSlots(slotsNum)); - - /* Activity 1.1 9.3.5.17 - Symbol 16 */ - if(!(*colPending)) { - return ERR_NONE; - } - - /* Activity 1.1 9.3.5.18 - Symbol 17 */ - } while( - curDevCnt != - 0U); /* If a collision is detected and card(s) were found on this loop keep the same number of available slots */ - } - - return ERR_NONE; -} - -/*******************************************************************************/ -uint32_t rfalNfcbTR2ToFDT(uint8_t tr2Code) { - /*******************************************************************************/ - /* MISRA 8.9 An object should be defined at block scope if its identifier only appears in a single function */ - /*! TR2 Table according to Digital 1.1 Table 33 */ - const uint16_t rfalNfcbTr2Table[] = {1792, 3328, 5376, 9472}; - /*******************************************************************************/ - - return rfalNfcbTr2Table[(tr2Code & RFAL_NFCB_SENSB_RES_PROTO_TR2_MASK)]; -} - -#endif /* RFAL_FEATURE_NFCB */ diff --git a/lib/ST25RFAL002/source/rfal_nfcf.c b/lib/ST25RFAL002/source/rfal_nfcf.c deleted file mode 100644 index 7b5e4d58fc5..00000000000 --- a/lib/ST25RFAL002/source/rfal_nfcf.c +++ /dev/null @@ -1,585 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_nfcf.c - * - * \author Gustavo Patricio - * - * \brief Implementation of NFC-F Poller (FeliCa PCD) device - * - * The definitions and helpers methods provided by this module are - * aligned with NFC-F (FeliCa - JIS X6319-4) - * - */ - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "rfal_nfcf.h" -#include "utils.h" - -/* - ****************************************************************************** - * ENABLE SWITCH - ****************************************************************************** - */ - -#ifndef RFAL_FEATURE_NFCF -#define RFAL_FEATURE_NFCF false /* NFC-F module configuration missing. Disabled by default */ -#endif - -#if RFAL_FEATURE_NFCF - -/* - ****************************************************************************** - * GLOBAL DEFINES - ****************************************************************************** - */ -#define RFAL_NFCF_SENSF_REQ_LEN_MIN \ - 5U /*!< SENSF_RES minimum length */ - -#define RFAL_NFCF_READ_WO_ENCRYPTION_MIN_LEN \ - 15U /*!< Minimum length for a Check Command T3T 5.4.1 */ -#define RFAL_NFCF_WRITE_WO_ENCRYPTION_MIN_LEN \ - 31U /*!< Minimum length for an Update Command T3T 5.5.1 */ - -#define RFAL_NFCF_CHECK_RES_MIN_LEN \ - 11U /*!< CHECK Response minimum length T3T 1.0 Table 8 */ -#define RFAL_NFCF_UPDATE_RES_MIN_LEN \ - 11U /*!< UPDATE Response minimum length T3T 1.0 Table 8 */ - -#define RFAL_NFCF_CHECK_REQ_MAX_LEN \ - 86U /*!< Max length of a Check request T3T 1.0 Table 7 */ -#define RFAL_NFCF_CHECK_REQ_MAX_SERV \ - 15U /*!< Max Services number on Check request T3T 1.0 5.4.1.5 */ -#define RFAL_NFCF_CHECK_REQ_MAX_BLOCK \ - 15U /*!< Max Blocks number on Check request T3T 1.0 5.4.1.10 */ -#define RFAL_NFCF_UPDATE_REQ_MAX_SERV \ - 15U /*!< Max Services number Update request T3T 1.0 5.4.1.5 */ -#define RFAL_NFCF_UPDATE_REQ_MAX_BLOCK \ - 13U /*!< Max Blocks number on Update request T3T 1.0 5.4.1.10 */ - -/*! MRT Check | Uupdate = (Tt3t x ((A+1) + n (B+1)) x 4^E) + dRWTt3t T3T 5.8 - Max values used: A = 7 ; B = 7 ; E = 3 ; n = 15 (NFC Forum n = 15, JIS n = 32) -*/ -#define RFAL_NFCF_MRT_CHECK_UPDATE ((4096 * (8 + (15 * 8)) * 64) + 16) - -/* - ****************************************************************************** - * GLOBAL MACROS - ****************************************************************************** - */ -#define rfalNfcfSlots2CardNum(s) \ - ((uint8_t)(s) + 1U) /*!< Converts Time Slot Number (TSN) into num of slots */ - -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ - -/*! Structure/Buffer to hold the SENSF_RES with LEN byte prepended */ -typedef struct { - uint8_t LEN; /*!< NFC-F LEN byte */ - rfalNfcfSensfRes SENSF_RES; /*!< SENSF_RES */ -} rfalNfcfSensfResBuf; - -/*! Greedy collection for NFCF GRE_POLL_F Activity 1.0 Table 10 */ -typedef struct { - uint8_t pollFound; /*!< Number of devices found by the Poll */ - uint8_t pollCollision; /*!< Number of collisions detected */ - rfalFeliCaPollRes POLL_F[RFAL_NFCF_POLL_MAXCARDS]; /*!< GRE_POLL_F Activity 1.0 Table 10 */ -} rfalNfcfGreedyF; - -/*! NFC-F SENSF_REQ format Digital 1.1 8.6.1 */ -typedef struct { - uint8_t CMD; /*!< Command code: 00h */ - uint8_t SC[RFAL_NFCF_SENSF_SC_LEN]; /*!< System Code */ - uint8_t RC; /*!< Request Code */ - uint8_t TSN; /*!< Time Slot Number */ -} rfalNfcfSensfReq; - -/* -****************************************************************************** -* LOCAL VARIABLES -****************************************************************************** -*/ -static rfalNfcfGreedyF gRfalNfcfGreedyF; /*!< Activity's NFCF Greedy collection */ - -/* -****************************************************************************** -* LOCAL FUNCTION PROTOTYPES -****************************************************************************** -*/ -static void rfalNfcfComputeValidSENF( - rfalNfcfListenDevice* outDevInfo, - uint8_t* curDevIdx, - uint8_t devLimit, - bool overwrite, - bool* nfcDepFound); - -/* -****************************************************************************** -* LOCAL VARIABLES -****************************************************************************** -*/ - -/*******************************************************************************/ -static void rfalNfcfComputeValidSENF( - rfalNfcfListenDevice* outDevInfo, - uint8_t* curDevIdx, - uint8_t devLimit, - bool overwrite, - bool* nfcDepFound) { - uint8_t tmpIdx; - bool duplicate; - const rfalNfcfSensfResBuf* sensfBuf; - rfalNfcfSensfResBuf sensfCopy; - - /*******************************************************************************/ - /* Go through all responses check if valid and duplicates */ - /*******************************************************************************/ - while((gRfalNfcfGreedyF.pollFound > 0U) && ((*curDevIdx) < devLimit)) { - duplicate = false; - gRfalNfcfGreedyF.pollFound--; - - /* MISRA 11.3 - Cannot point directly into different object type, use local copy */ - ST_MEMCPY( - (uint8_t*)&sensfCopy, - (uint8_t*)&gRfalNfcfGreedyF.POLL_F[gRfalNfcfGreedyF.pollFound], - sizeof(rfalNfcfSensfResBuf)); - - /* Point to received SENSF_RES */ - sensfBuf = &sensfCopy; - - /* Check for devices that are already in device list */ - for(tmpIdx = 0; tmpIdx < (*curDevIdx); tmpIdx++) { - if(ST_BYTECMP( - sensfBuf->SENSF_RES.NFCID2, - outDevInfo[tmpIdx].sensfRes.NFCID2, - RFAL_NFCF_NFCID2_LEN) == 0) { - duplicate = true; - break; - } - } - - /* If is a duplicate skip this (and not to overwrite)*/ - if(duplicate && !overwrite) { - continue; - } - - /* Check if response length is OK */ - if(((sensfBuf->LEN - RFAL_NFCF_HEADER_LEN) < RFAL_NFCF_SENSF_RES_LEN_MIN) || - ((sensfBuf->LEN - RFAL_NFCF_HEADER_LEN) > RFAL_NFCF_SENSF_RES_LEN_MAX)) { - continue; - } - - /* Check if the response is a SENSF_RES / Polling response */ - if(sensfBuf->SENSF_RES.CMD != (uint8_t)RFAL_NFCF_CMD_POLLING_RES) { - continue; - } - - /* Check if is an overwrite request or new device*/ - if(duplicate && overwrite) { - /* overwrite deviceInfo/GRE_SENSF_RES with SENSF_RES */ - outDevInfo[tmpIdx].sensfResLen = (sensfBuf->LEN - RFAL_NFCF_LENGTH_LEN); - ST_MEMCPY( - &outDevInfo[tmpIdx].sensfRes, - &sensfBuf->SENSF_RES, - outDevInfo[tmpIdx].sensfResLen); - continue; - } else { - /* fill deviceInfo/GRE_SENSF_RES with new SENSF_RES */ - outDevInfo[(*curDevIdx)].sensfResLen = (sensfBuf->LEN - RFAL_NFCF_LENGTH_LEN); - ST_MEMCPY( - &outDevInfo[(*curDevIdx)].sensfRes, - &sensfBuf->SENSF_RES, - outDevInfo[(*curDevIdx)].sensfResLen); - } - - /* Check if this device supports NFC-DEP and signal it (ACTIVITY 1.1 9.3.6.63) */ - *nfcDepFound = rfalNfcfIsNfcDepSupported(&outDevInfo[(*curDevIdx)]); - - (*curDevIdx)++; - } -} - -/* -****************************************************************************** -* GLOBAL FUNCTIONS -****************************************************************************** -*/ - -/*******************************************************************************/ -ReturnCode rfalNfcfPollerInitialize(rfalBitRate bitRate) { - ReturnCode ret; - - if((bitRate != RFAL_BR_212) && (bitRate != RFAL_BR_424)) { - return ERR_PARAM; - } - - EXIT_ON_ERR(ret, rfalSetMode(RFAL_MODE_POLL_NFCF, bitRate, bitRate)); - rfalSetErrorHandling(RFAL_ERRORHANDLING_NFC); - - rfalSetGT(RFAL_GT_NFCF); - rfalSetFDTListen(RFAL_FDT_LISTEN_NFCF_POLLER); - rfalSetFDTPoll(RFAL_FDT_POLL_NFCF_POLLER); - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcfPollerPoll( - rfalFeliCaPollSlots slots, - uint16_t sysCode, - uint8_t reqCode, - rfalFeliCaPollRes* cardList, - uint8_t* devCnt, - uint8_t* collisions) { - return rfalFeliCaPoll( - slots, sysCode, reqCode, cardList, rfalNfcfSlots2CardNum(slots), devCnt, collisions); -} - -/*******************************************************************************/ -ReturnCode rfalNfcfPollerCheckPresence(void) { - gRfalNfcfGreedyF.pollFound = 0; - gRfalNfcfGreedyF.pollCollision = 0; - - /* ACTIVITY 1.0 & 1.1 - 9.2.3.17 SENSF_REQ must be with number of slots equal to 4 - * SC must be 0xFFFF - * RC must be 0x00 (No system code info required) */ - return rfalFeliCaPoll( - RFAL_FELICA_4_SLOTS, - RFAL_NFCF_SYSTEMCODE, - RFAL_FELICA_POLL_RC_NO_REQUEST, - gRfalNfcfGreedyF.POLL_F, - rfalNfcfSlots2CardNum(RFAL_FELICA_4_SLOTS), - &gRfalNfcfGreedyF.pollFound, - &gRfalNfcfGreedyF.pollCollision); -} - -/*******************************************************************************/ -ReturnCode rfalNfcfPollerCollisionResolution( - rfalComplianceMode compMode, - uint8_t devLimit, - rfalNfcfListenDevice* nfcfDevList, - uint8_t* devCnt) { - ReturnCode ret; - bool nfcDepFound; - - if((nfcfDevList == NULL) || (devCnt == NULL)) { - return ERR_PARAM; - } - - *devCnt = 0; - nfcDepFound = false; - - /*******************************************************************************************/ - /* ACTIVITY 1.0 - 9.3.6.3 Copy valid SENSF_RES in GRE_POLL_F into GRE_SENSF_RES */ - /* ACTIVITY 1.0 - 9.3.6.6 The NFC Forum Device MUST remove all entries from GRE_SENSF_RES[]*/ - /* ACTIVITY 1.1 - 9.3.63.59 Populate GRE_SENSF_RES with data from GRE_POLL_F */ - /* */ - /* CON_DEVICES_LIMIT = 0 Just check if devices from Tech Detection exceeds -> always true */ - /* Allow the number of slots open on Technology Detection */ - /*******************************************************************************************/ - rfalNfcfComputeValidSENF( - nfcfDevList, - devCnt, - ((devLimit == 0U) ? rfalNfcfSlots2CardNum(RFAL_FELICA_4_SLOTS) : devLimit), - false, - &nfcDepFound); - - /*******************************************************************************/ - /* ACTIVITY 1.0 - 9.3.6.4 */ - /* ACTIVITY 1.1 - 9.3.63.60 Check if devices found are lower than the limit */ - /* and send a SENSF_REQ if so */ - /*******************************************************************************/ - if(*devCnt < devLimit) { - /* ACTIVITY 1.0 - 9.3.6.5 Copy valid SENSF_RES and then to remove it - * ACTIVITY 1.1 - 9.3.6.65 Copy and filter duplicates - * For now, due to some devices keep generating different nfcid2, we use 1.0 - * Phones detected: Samsung Galaxy Nexus,Samsung Galaxy S3,Samsung Nexus S */ - *devCnt = 0; - - ret = rfalNfcfPollerPoll( - RFAL_FELICA_16_SLOTS, - RFAL_NFCF_SYSTEMCODE, - RFAL_FELICA_POLL_RC_NO_REQUEST, - gRfalNfcfGreedyF.POLL_F, - &gRfalNfcfGreedyF.pollFound, - &gRfalNfcfGreedyF.pollCollision); - if(ret == ERR_NONE) { - rfalNfcfComputeValidSENF(nfcfDevList, devCnt, devLimit, false, &nfcDepFound); - } - - /*******************************************************************************/ - /* ACTIVITY 1.1 - 9.3.6.63 Check if any device supports NFC DEP */ - /*******************************************************************************/ - if(nfcDepFound && (compMode == RFAL_COMPLIANCE_MODE_NFC)) { - ret = rfalNfcfPollerPoll( - RFAL_FELICA_16_SLOTS, - RFAL_NFCF_SYSTEMCODE, - RFAL_FELICA_POLL_RC_SYSTEM_CODE, - gRfalNfcfGreedyF.POLL_F, - &gRfalNfcfGreedyF.pollFound, - &gRfalNfcfGreedyF.pollCollision); - if(ret == ERR_NONE) { - rfalNfcfComputeValidSENF(nfcfDevList, devCnt, devLimit, true, &nfcDepFound); - } - } - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcfPollerCheck( - const uint8_t* nfcid2, - const rfalNfcfServBlockListParam* servBlock, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvdLen) { - uint8_t txBuf[RFAL_NFCF_CHECK_REQ_MAX_LEN]; - uint8_t msgIt; - uint8_t i; - ReturnCode ret; - const uint8_t* checkRes; - - /* Check parameters */ - if((nfcid2 == NULL) || (rxBuf == NULL) || (servBlock == NULL) || (servBlock->numBlock == 0U) || - (servBlock->numBlock > RFAL_NFCF_CHECK_REQ_MAX_BLOCK) || (servBlock->numServ == 0U) || - (servBlock->numServ > RFAL_NFCF_CHECK_REQ_MAX_SERV) || - (rxBufLen < (RFAL_NFCF_LENGTH_LEN + RFAL_NFCF_CHECK_RES_MIN_LEN))) { - return ERR_PARAM; - } - - msgIt = 0; - - /*******************************************************************************/ - /* Compose CHECK command/request */ - - txBuf[msgIt++] = RFAL_NFCF_CMD_READ_WITHOUT_ENCRYPTION; /* Command Code */ - - ST_MEMCPY(&txBuf[msgIt], nfcid2, RFAL_NFCF_NFCID2_LEN); /* NFCID2 */ - msgIt += RFAL_NFCF_NFCID2_LEN; - - txBuf[msgIt++] = servBlock->numServ; /* NoS */ - for(i = 0; i < servBlock->numServ; i++) { - txBuf[msgIt++] = (uint8_t)((servBlock->servList[i] >> 0U) & 0xFFU); /* Service Code */ - txBuf[msgIt++] = (uint8_t)((servBlock->servList[i] >> 8U) & 0xFFU); - } - - txBuf[msgIt++] = servBlock->numBlock; /* NoB */ - for(i = 0; i < servBlock->numBlock; i++) { - txBuf[msgIt++] = - servBlock->blockList[i].conf; /* Block list element conf (Flag|Access|Service) */ - if((servBlock->blockList[i].conf & 0x80U) != - 0U) /* Check if 2 or 3 byte block list element */ - { - txBuf[msgIt++] = - (uint8_t)(servBlock->blockList[i].blockNum & 0xFFU); /* 1byte Block Num */ - } else { - txBuf[msgIt++] = - (uint8_t)((servBlock->blockList[i].blockNum >> 0U) & 0xFFU); /* 2byte Block Num */ - txBuf[msgIt++] = (uint8_t)((servBlock->blockList[i].blockNum >> 8U) & 0xFFU); - } - } - - /*******************************************************************************/ - /* Transceive CHECK command/request */ - ret = rfalTransceiveBlockingTxRx( - txBuf, - msgIt, - rxBuf, - rxBufLen, - rcvdLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_NFCF_MRT_CHECK_UPDATE); - - if(ret == ERR_NONE) { - /* Skip LEN byte */ - checkRes = (rxBuf + RFAL_NFCF_LENGTH_LEN); - - /* Check response length */ - if(*rcvdLen < (RFAL_NFCF_LENGTH_LEN + RFAL_NFCF_CHECKUPDATE_RES_ST2_POS)) { - ret = ERR_PROTO; - } - /* Check for a valid response */ - else if( - (checkRes[RFAL_NFCF_CMD_POS] != (uint8_t)RFAL_NFCF_CMD_READ_WITHOUT_ENCRYPTION_RES) || - (checkRes[RFAL_NFCF_CHECKUPDATE_RES_ST1_POS] != RFAL_NFCF_STATUS_FLAG_SUCCESS) || - (checkRes[RFAL_NFCF_CHECKUPDATE_RES_ST2_POS] != RFAL_NFCF_STATUS_FLAG_SUCCESS)) { - ret = ERR_REQUEST; - } - /* CHECK succesfull, remove header */ - else { - (*rcvdLen) -= (RFAL_NFCF_LENGTH_LEN + RFAL_NFCF_CHECKUPDATE_RES_NOB_POS); - - if(*rcvdLen > 0U) { - ST_MEMMOVE(rxBuf, &checkRes[RFAL_NFCF_CHECKUPDATE_RES_NOB_POS], (*rcvdLen)); - } - } - } - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalNfcfPollerUpdate( - const uint8_t* nfcid2, - const rfalNfcfServBlockListParam* servBlock, - uint8_t* txBuf, - uint16_t txBufLen, - const uint8_t* blockData, - uint8_t* rxBuf, - uint16_t rxBufLen) { - uint8_t i; - uint16_t msgIt; - uint16_t rcvdLen; - uint16_t auxLen; - const uint8_t* updateRes; - ReturnCode ret; - - /* Check parameters */ - if((nfcid2 == NULL) || (rxBuf == NULL) || (servBlock == NULL) || (txBuf == NULL) || - (servBlock->numBlock == 0U) || (servBlock->numBlock > RFAL_NFCF_UPDATE_REQ_MAX_BLOCK) || - (servBlock->numServ == 0U) || (servBlock->numServ > RFAL_NFCF_UPDATE_REQ_MAX_SERV) || - (rxBufLen < (RFAL_NFCF_LENGTH_LEN + RFAL_NFCF_UPDATE_RES_MIN_LEN))) { - return ERR_PARAM; - } - - /* Calculate required txBuffer lenth */ - auxLen = (uint16_t)( RFAL_NFCF_CMD_LEN + RFAL_NFCF_NFCID2_LEN + ( servBlock->numServ * sizeof(rfalNfcfServ) ) + - (servBlock->numBlock * sizeof(rfalNfcfBlockListElem)) + (uint16_t)((uint16_t)servBlock->numBlock * RFAL_NFCF_BLOCK_LEN) ); - - /* Check whether the provided buffer is sufficient for this request */ - if(txBufLen < auxLen) { - return ERR_PARAM; - } - - msgIt = 0; - - /*******************************************************************************/ - /* Compose UPDATE command/request */ - - txBuf[msgIt++] = RFAL_NFCF_CMD_WRITE_WITHOUT_ENCRYPTION; /* Command Code */ - - ST_MEMCPY(&txBuf[msgIt], nfcid2, RFAL_NFCF_NFCID2_LEN); /* NFCID2 */ - msgIt += RFAL_NFCF_NFCID2_LEN; - - txBuf[msgIt++] = servBlock->numServ; /* NoS */ - for(i = 0; i < servBlock->numServ; i++) { - txBuf[msgIt++] = (uint8_t)((servBlock->servList[i] >> 0U) & 0xFFU); /* Service Code */ - txBuf[msgIt++] = (uint8_t)((servBlock->servList[i] >> 8U) & 0xFFU); - } - - txBuf[msgIt++] = servBlock->numBlock; /* NoB */ - for(i = 0; i < servBlock->numBlock; i++) { - txBuf[msgIt++] = - servBlock->blockList[i].conf; /* Block list element conf (Flag|Access|Service) */ - if((servBlock->blockList[i].conf & 0x80U) != - 0U) /* Check if 2 or 3 byte block list element */ - { - txBuf[msgIt++] = - (uint8_t)(servBlock->blockList[i].blockNum & 0xFFU); /* 1byte Block Num */ - } else { - txBuf[msgIt++] = - (uint8_t)((servBlock->blockList[i].blockNum >> 0U) & 0xFFU); /* 2byte Block Num */ - txBuf[msgIt++] = (uint8_t)((servBlock->blockList[i].blockNum >> 8U) & 0xFFU); - } - } - - auxLen = ((uint16_t)servBlock->numBlock * RFAL_NFCF_BLOCK_LEN); - ST_MEMCPY(&txBuf[msgIt], blockData, auxLen); /* Block Data */ - msgIt += auxLen; - - /*******************************************************************************/ - /* Transceive UPDATE command/request */ - ret = rfalTransceiveBlockingTxRx( - txBuf, - msgIt, - rxBuf, - rxBufLen, - &rcvdLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_NFCF_MRT_CHECK_UPDATE); - - if(ret == ERR_NONE) { - /* Skip LEN byte */ - updateRes = (rxBuf + RFAL_NFCF_LENGTH_LEN); - - /* Check response length */ - if(rcvdLen < (RFAL_NFCF_LENGTH_LEN + RFAL_NFCF_CHECKUPDATE_RES_ST2_POS)) { - ret = ERR_PROTO; - } - /* Check for a valid response */ - else if( - (updateRes[RFAL_NFCF_CMD_POS] != - (uint8_t)RFAL_NFCF_CMD_WRITE_WITHOUT_ENCRYPTION_RES) || - (updateRes[RFAL_NFCF_CHECKUPDATE_RES_ST1_POS] != RFAL_NFCF_STATUS_FLAG_SUCCESS) || - (updateRes[RFAL_NFCF_CHECKUPDATE_RES_ST2_POS] != RFAL_NFCF_STATUS_FLAG_SUCCESS)) { - ret = ERR_REQUEST; - } else { - /* MISRA 15.7 - Empty else */ - } - } - - return ret; -} - -/*******************************************************************************/ -bool rfalNfcfListenerIsT3TReq(const uint8_t* buf, uint16_t bufLen, uint8_t* nfcid2) { - /* Check cmd byte */ - switch(*buf) { - case RFAL_NFCF_CMD_READ_WITHOUT_ENCRYPTION: - if(bufLen < RFAL_NFCF_READ_WO_ENCRYPTION_MIN_LEN) { - return false; - } - break; - - case RFAL_NFCF_CMD_WRITE_WITHOUT_ENCRYPTION: - if(bufLen < RFAL_NFCF_WRITE_WO_ENCRYPTION_MIN_LEN) { - return false; - } - break; - - default: - return false; - } - - /* Output NFID2 if requested */ - if(nfcid2 != NULL) { - ST_MEMCPY(nfcid2, &buf[RFAL_NFCF_CMD_LEN], RFAL_NFCF_NFCID2_LEN); - } - - return true; -} - -#endif /* RFAL_FEATURE_NFCF */ diff --git a/lib/ST25RFAL002/source/rfal_nfcv.c b/lib/ST25RFAL002/source/rfal_nfcv.c deleted file mode 100644 index 22c9b874169..00000000000 --- a/lib/ST25RFAL002/source/rfal_nfcv.c +++ /dev/null @@ -1,1057 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_nfcv.c - * - * \author Gustavo Patricio - * - * \brief Implementation of NFC-V Poller (ISO15693) device - * - * The definitions and helpers methods provided by this module are - * aligned with NFC-V (ISO15693) - * - * The definitions and helpers methods provided by this module - * are aligned with NFC-V Digital 2.1 - * - */ - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "rfal_nfcv.h" -#include "utils.h" - -/* - ****************************************************************************** - * ENABLE SWITCH - ****************************************************************************** - */ - -#ifndef RFAL_FEATURE_NFCV -#define RFAL_FEATURE_NFCV false /* NFC-V module configuration missing. Disabled by default */ -#endif - -#if RFAL_FEATURE_NFCV - -/* - ****************************************************************************** - * GLOBAL DEFINES - ****************************************************************************** - */ - -#define RFAL_NFCV_INV_REQ_FLAG \ - 0x06U /*!< INVENTORY_REQ INV_FLAG Digital 2.1 9.6.1 */ -#define RFAL_NFCV_MASKVAL_MAX_LEN \ - 8U /*!< Mask value max length: 64 bits (UID length) */ -#define RFAL_NFCV_MASKVAL_MAX_1SLOT_LEN \ - 64U /*!< Mask value max length in 1 Slot mode in bits Digital 2.1 9.6.1.6 */ -#define RFAL_NFCV_MASKVAL_MAX_16SLOT_LEN \ - 60U /*!< Mask value max length in 16 Slot mode in bits Digital 2.1 9.6.1.6 */ -#define RFAL_NFCV_MAX_SLOTS \ - 16U /*!< NFC-V max number of Slots */ -#define RFAL_NFCV_INV_REQ_HEADER_LEN \ - 3U /*!< INVENTORY_REQ header length (INV_FLAG, CMD, MASK_LEN) */ -#define RFAL_NFCV_INV_RES_LEN \ - 10U /*!< INVENTORY_RES length */ -#define RFAL_NFCV_WR_MUL_REQ_HEADER_LEN \ - 4U /*!< Write Multiple header length (INV_FLAG, CMD, [UID], BNo, Bno) */ - -#define RFAL_NFCV_CMD_LEN \ - 1U /*!< Commandbyte length */ -#define RFAL_NFCV_FLAG_POS \ - 0U /*!< Flag byte position */ -#define RFAL_NFCV_FLAG_LEN \ - 1U /*!< Flag byte length */ -#define RFAL_NFCV_DATASTART_POS \ - 1U /*!< Position of start of data */ -#define RFAL_NFCV_DSFI_LEN \ - 1U /*!< DSFID length */ -#define RFAL_NFCV_SLPREQ_REQ_FLAG \ - 0x22U /*!< SLPV_REQ request flags Digital 2.0 (Candidate) 9.7.1.1 */ -#define RFAL_NFCV_RES_FLAG_NOERROR \ - 0x00U /*!< RES_FLAG indicating no error (checked during activation) */ - -#define RFAL_NFCV_MAX_COLL_SUPPORTED \ - 16U /*!< Maximum number of collisions supported by the Anticollision loop */ - -#define RFAL_NFCV_FDT_MAX \ - rfalConvMsTo1fc(20) /*!< Maximum Wait time FDTV,EOF and MAX2 Digital 2.1 B.5*/ -#define RFAL_NFCV_FDT_MAX1 \ - 4394U /*!< Read alike command FWT FDTV,LISTEN,MAX1 Digital 2.0 B.5 */ - -/*! Time from special frame to EOF - * ISO15693 2009 10.4.2 : 20ms - * NFC Forum defines Digital 2.0 9.7.4 : FDTV,EOF = [10 ; 20]ms - */ -#define RFAL_NFCV_FDT_EOF 20U - -/*! Time between slots - ISO 15693 defines t3min depending on modulation depth and data rate. - * With only high-bitrate supported, AM modulation and a length of 12 bytes (96bits) for INV_RES we get: - * - ISO t3min = 96/26 ms + 300us = 4 ms - * - NFC Forum defines FDTV,INVENT_NORES = (4394 + 2048)/fc. Digital 2.0 B.5*/ -#define RFAL_NFCV_FDT_V_INVENT_NORES 4U - -/* - ****************************************************************************** - * GLOBAL MACROS - ****************************************************************************** - */ - -/*! Checks if a valid INVENTORY_RES is valid Digital 2.2 9.6.2.1 & 9.6.2.3 */ -#define rfalNfcvCheckInvRes(f, l) \ - (((l) == rfalConvBytesToBits(RFAL_NFCV_INV_RES_LEN + RFAL_NFCV_CRC_LEN)) && \ - ((f) == RFAL_NFCV_RES_FLAG_NOERROR)) - -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ - -/*! NFC-V INVENTORY_REQ format Digital 2.0 9.6.1 */ -typedef struct { - uint8_t INV_FLAG; /*!< Inventory Flags */ - uint8_t CMD; /*!< Command code: 01h */ - uint8_t MASK_LEN; /*!< Mask Value Length */ - uint8_t MASK_VALUE[RFAL_NFCV_MASKVAL_MAX_LEN]; /*!< Mask Value */ -} rfalNfcvInventoryReq; - -/*! NFC-V SLP_REQ format Digital 2.0 (Candidate) 9.7.1 */ -typedef struct { - uint8_t REQ_FLAG; /*!< Request Flags */ - uint8_t CMD; /*!< Command code: 02h */ - uint8_t UID[RFAL_NFCV_UID_LEN]; /*!< Mask Value */ -} rfalNfcvSlpvReq; - -/*! Container for a collision found during Anticollision loop */ -typedef struct { - uint8_t maskLen; - uint8_t maskVal[RFAL_NFCV_MASKVAL_MAX_LEN]; -} rfalNfcvCollision; - -/* -****************************************************************************** -* LOCAL FUNCTION PROTOTYPES -****************************************************************************** -*/ -static ReturnCode rfalNfcvParseError(uint8_t err); - -/* -****************************************************************************** -* LOCAL VARIABLES -****************************************************************************** -*/ - -/* -****************************************************************************** -* LOCAL FUNCTIONS -****************************************************************************** -*/ - -/*******************************************************************************/ -static ReturnCode rfalNfcvParseError(uint8_t err) { - switch(err) { - case RFAL_NFCV_ERROR_CMD_NOT_SUPPORTED: - case RFAL_NFCV_ERROR_OPTION_NOT_SUPPORTED: - return ERR_NOTSUPP; - - case RFAL_NFCV_ERROR_CMD_NOT_RECOGNIZED: - return ERR_PROTO; - - case RFAL_NFCV_ERROR_WRITE_FAILED: - return ERR_WRITE; - - default: - return ERR_REQUEST; - } -} - -/* -****************************************************************************** -* GLOBAL FUNCTIONS -****************************************************************************** -*/ - -/*******************************************************************************/ -ReturnCode rfalNfcvPollerInitialize(void) { - ReturnCode ret; - - EXIT_ON_ERR(ret, rfalSetMode(RFAL_MODE_POLL_NFCV, RFAL_BR_26p48, RFAL_BR_26p48)); - rfalSetErrorHandling(RFAL_ERRORHANDLING_NFC); - - rfalSetGT(RFAL_GT_NFCV); - rfalSetFDTListen(RFAL_FDT_LISTEN_NFCV_POLLER); - rfalSetFDTPoll(RFAL_FDT_POLL_NFCV_POLLER); - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcvPollerCheckPresence(rfalNfcvInventoryRes* invRes) { - ReturnCode ret; - - /* INVENTORY_REQ with 1 slot and no Mask Activity 2.0 (Candidate) 9.2.3.32 */ - ret = rfalNfcvPollerInventory(RFAL_NFCV_NUM_SLOTS_1, 0, NULL, invRes, NULL); - - if((ret == ERR_RF_COLLISION) || (ret == ERR_CRC) || (ret == ERR_FRAMING) || - (ret == ERR_PROTO)) { - ret = ERR_NONE; - } - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalNfcvPollerInventory( - rfalNfcvNumSlots nSlots, - uint8_t maskLen, - const uint8_t* maskVal, - rfalNfcvInventoryRes* invRes, - uint16_t* rcvdLen) { - ReturnCode ret; - rfalNfcvInventoryReq invReq; - uint16_t rxLen; - - if(((maskVal == NULL) && (maskLen != 0U)) || (invRes == NULL)) { - return ERR_PARAM; - } - - invReq.INV_FLAG = (RFAL_NFCV_INV_REQ_FLAG | (uint8_t)nSlots); - invReq.CMD = RFAL_NFCV_CMD_INVENTORY; - invReq.MASK_LEN = (uint8_t)MIN( - maskLen, - ((nSlots == RFAL_NFCV_NUM_SLOTS_1) ? - RFAL_NFCV_MASKVAL_MAX_1SLOT_LEN : - RFAL_NFCV_MASKVAL_MAX_16SLOT_LEN)); /* Digital 2.0 9.6.1.6 */ - - if((rfalConvBitsToBytes(invReq.MASK_LEN) > 0U) && (maskVal != NULL)) /* MISRA 21.18 & 1.3 */ - { - ST_MEMCPY(invReq.MASK_VALUE, maskVal, rfalConvBitsToBytes(invReq.MASK_LEN)); - } - - ret = rfalISO15693TransceiveAnticollisionFrame( - (uint8_t*)&invReq, - (uint8_t)(RFAL_NFCV_INV_REQ_HEADER_LEN + rfalConvBitsToBytes(invReq.MASK_LEN)), - (uint8_t*)invRes, - sizeof(rfalNfcvInventoryRes), - &rxLen); - - /* Check for optional output parameter */ - if(rcvdLen != NULL) { - *rcvdLen = rxLen; - } - - if(ret == ERR_NONE) { - /* Check for valid INVENTORY_RES Digital 2.2 9.6.2.1 & 9.6.2.3 */ - if(!rfalNfcvCheckInvRes(invRes->RES_FLAG, rxLen)) { - return ERR_PROTO; - } - } - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalNfcvPollerCollisionResolution( - rfalComplianceMode compMode, - uint8_t devLimit, - rfalNfcvListenDevice* nfcvDevList, - uint8_t* devCnt) { - ReturnCode ret; - uint8_t slotNum; - uint16_t rcvdLen; - uint8_t colIt; - uint8_t colCnt; - uint8_t colPos; - bool colPending; - rfalNfcvCollision colFound[RFAL_NFCV_MAX_COLL_SUPPORTED]; - - if((nfcvDevList == NULL) || (devCnt == NULL)) { - return ERR_PARAM; - } - - /* Initialize parameters */ - *devCnt = 0; - colIt = 0; - colCnt = 0; - colPending = false; - ST_MEMSET(colFound, 0x00, (sizeof(rfalNfcvCollision) * RFAL_NFCV_MAX_COLL_SUPPORTED)); - - if(devLimit > 0U) /* MISRA 21.18 */ - { - ST_MEMSET(nfcvDevList, 0x00, (sizeof(rfalNfcvListenDevice) * devLimit)); - } - - NO_WARNING( - colPending); /* colPending is not exposed externally, in future it might become exposed/ouput parameter */ - - if(compMode == RFAL_COMPLIANCE_MODE_NFC) { - /* Send INVENTORY_REQ with one slot Activity 2.1 9.3.7.1 (Symbol 0) */ - ret = rfalNfcvPollerInventory(RFAL_NFCV_NUM_SLOTS_1, 0, NULL, &nfcvDevList->InvRes, NULL); - - if(ret == ERR_TIMEOUT) /* Exit if no device found Activity 2.1 9.3.7.2 (Symbol 1) */ - { - return ERR_NONE; - } - if(ret == - ERR_NONE) /* Device found without transmission error/collision Activity 2.1 9.3.7.3 (Symbol 2) */ - { - (*devCnt)++; - return ERR_NONE; - } - - /* A Collision has been identified Activity 2.1 9.3.7.4 (Symbol 3) */ - colPending = true; - colCnt = 1; - - /* Check if the Collision Resolution is set to perform only Collision detection Activity 2.1 9.3.7.5 (Symbol 4)*/ - if(devLimit == 0U) { - return ERR_RF_COLLISION; - } - - platformDelay(RFAL_NFCV_FDT_V_INVENT_NORES); - - /*******************************************************************************/ - /* Collisions pending, Anticollision loop must be executed */ - /*******************************************************************************/ - } else { - /* Advance to 16 slots below without mask. Will give a good chance to identify multiple cards */ - colPending = true; - colCnt = 1; - } - - /* Execute until all collisions are resolved Activity 2.1 9.3.7.18 (Symbol 17) */ - do { - /* Activity 2.1 9.3.7.7 (Symbol 6 / 7) */ - colPending = false; - slotNum = 0; - - do { - if(slotNum == 0U) { - /* Send INVENTORY_REQ with 16 slots Activity 2.1 9.3.7.9 (Symbol 8) */ - ret = rfalNfcvPollerInventory( - RFAL_NFCV_NUM_SLOTS_16, - colFound[colIt].maskLen, - colFound[colIt].maskVal, - &nfcvDevList[(*devCnt)].InvRes, - &rcvdLen); - } else { - ret = rfalISO15693TransceiveEOFAnticollision( - (uint8_t*)&nfcvDevList[(*devCnt)].InvRes, - sizeof(rfalNfcvInventoryRes), - &rcvdLen); - } - slotNum++; - - /*******************************************************************************/ - if(ret != ERR_TIMEOUT) { - if(rcvdLen < - rfalConvBytesToBits( - RFAL_NFCV_INV_RES_LEN + - RFAL_NFCV_CRC_LEN)) { /* If only a partial frame was received make sure the FDT_V_INVENT_NORES is fulfilled */ - platformDelay(RFAL_NFCV_FDT_V_INVENT_NORES); - } - - /* Check if response is a correct frame (no TxRx error) Activity 2.1 9.3.7.11 (Symbol 10)*/ - if((ret == ERR_NONE) || (ret == ERR_PROTO)) { - /* Check if the device found is already on the list and its response is a valid INVENTORY_RES */ - if(rfalNfcvCheckInvRes(nfcvDevList[(*devCnt)].InvRes.RES_FLAG, rcvdLen)) { - /* Activity 2.1 9.3.7.12 (Symbol 11) */ - (*devCnt)++; - } - } else /* Treat everything else as collision */ - { - /* Activity 2.1 9.3.7.17 (Symbol 16) */ - colPending = true; - - /*******************************************************************************/ - /* Ensure that this collision still fits on the container */ - if(colCnt < RFAL_NFCV_MAX_COLL_SUPPORTED) { - /* Store this collision on the container to be resolved later */ - /* Activity 2.1 9.3.7.17 (Symbol 16): add the collision information - * (MASK_VAL + SN) to the list containing the collision information */ - ST_MEMCPY( - colFound[colCnt].maskVal, colFound[colIt].maskVal, RFAL_NFCV_UID_LEN); - colPos = colFound[colIt].maskLen; - colFound[colCnt].maskVal[(colPos / RFAL_BITS_IN_BYTE)] &= - (uint8_t)((1U << (colPos % RFAL_BITS_IN_BYTE)) - 1U); - colFound[colCnt].maskVal[(colPos / RFAL_BITS_IN_BYTE)] |= - (uint8_t)((slotNum - 1U) << (colPos % RFAL_BITS_IN_BYTE)); - colFound[colCnt].maskVal[((colPos / RFAL_BITS_IN_BYTE) + 1U)] = - (uint8_t)((slotNum - 1U) >> (RFAL_BITS_IN_BYTE - (colPos % RFAL_BITS_IN_BYTE))); - - colFound[colCnt].maskLen = (colFound[colIt].maskLen + 4U); - - colCnt++; - } - } - } else { - /* Timeout */ - platformDelay(RFAL_NFCV_FDT_V_INVENT_NORES); - } - - /* Check if devices found have reached device limit Activity 2.1 9.3.7.13 (Symbol 12) */ - if(*devCnt >= devLimit) { - return ERR_NONE; - } - - } while(slotNum < RFAL_NFCV_MAX_SLOTS); /* Slot loop */ - colIt++; - } while(colIt < colCnt); /* Collisions found loop */ - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcvPollerSleepCollisionResolution( - uint8_t devLimit, - rfalNfcvListenDevice* nfcvDevList, - uint8_t* devCnt) { - uint8_t tmpDevCnt; - ReturnCode ret; - uint8_t i; - - if((nfcvDevList == NULL) || (devCnt == NULL)) { - return ERR_PARAM; - } - - *devCnt = 0; - - do { - tmpDevCnt = 0; - ret = rfalNfcvPollerCollisionResolution( - RFAL_COMPLIANCE_MODE_ISO, (devLimit - *devCnt), &nfcvDevList[*devCnt], &tmpDevCnt); - - for(i = *devCnt; i < (*devCnt + tmpDevCnt); i++) { - rfalNfcvPollerSleep(0x00, nfcvDevList[i].InvRes.UID); - nfcvDevList[i].isSleep = true; - } - *devCnt += tmpDevCnt; - } while((ret == ERR_NONE) && (tmpDevCnt > 0U) && (*devCnt < devLimit)); - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalNfcvPollerSleep(uint8_t flags, const uint8_t* uid) { - ReturnCode ret; - rfalNfcvSlpvReq slpReq; - uint8_t rxBuf; /* dummy buffer, just to perform Rx */ - - if(uid == NULL) { - return ERR_PARAM; - } - - /* Compute SLPV_REQ */ - slpReq.REQ_FLAG = - (flags | - (uint8_t) - RFAL_NFCV_REQ_FLAG_ADDRESS); /* Should be with UID according Digital 2.0 (Candidate) 9.7.1.1 */ - slpReq.CMD = RFAL_NFCV_CMD_SLPV; - ST_MEMCPY(slpReq.UID, uid, RFAL_NFCV_UID_LEN); - - /* NFC Forum device SHALL wait at least FDTVpp to consider the SLPV acknowledged (FDTVpp = FDTVpoll) Digital 2.0 (Candidate) 9.7 9.8.2 */ - ret = rfalTransceiveBlockingTxRx( - (uint8_t*)&slpReq, - sizeof(rfalNfcvSlpvReq), - &rxBuf, - sizeof(rxBuf), - NULL, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_NFCV_FDT_MAX1); - if(ret != ERR_TIMEOUT) { - return ret; - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcvPollerSelect(uint8_t flags, const uint8_t* uid) { - uint16_t rcvLen; - rfalNfcvGenericRes res; - - if(uid == NULL) { - return ERR_PARAM; - } - - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_SELECT, - flags, - RFAL_NFCV_PARAM_SKIP, - uid, - NULL, - 0U, - (uint8_t*)&res, - sizeof(rfalNfcvGenericRes), - &rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalNfcvPollerReadSingleBlock( - uint8_t flags, - const uint8_t* uid, - uint8_t blockNum, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen) { - uint8_t bn; - - bn = blockNum; - - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_READ_SINGLE_BLOCK, - flags, - RFAL_NFCV_PARAM_SKIP, - uid, - &bn, - sizeof(uint8_t), - rxBuf, - rxBufLen, - rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalNfcvPollerWriteSingleBlock( - uint8_t flags, - const uint8_t* uid, - uint8_t blockNum, - const uint8_t* wrData, - uint8_t blockLen) { - uint8_t data[(RFAL_NFCV_BLOCKNUM_LEN + RFAL_NFCV_MAX_BLOCK_LEN)]; - uint8_t dataLen; - uint16_t rcvLen; - rfalNfcvGenericRes res; - - /* Check for valid parameters */ - if((blockLen == 0U) || (blockLen > (uint8_t)RFAL_NFCV_MAX_BLOCK_LEN) || (wrData == NULL)) { - return ERR_PARAM; - } - - dataLen = 0U; - - /* Compute Request Data */ - data[dataLen++] = blockNum; /* Set Block Number (8 bits) */ - ST_MEMCPY(&data[dataLen], wrData, blockLen); /* Append Block data to write */ - dataLen += blockLen; - - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_WRITE_SINGLE_BLOCK, - flags, - RFAL_NFCV_PARAM_SKIP, - uid, - data, - dataLen, - (uint8_t*)&res, - sizeof(rfalNfcvGenericRes), - &rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalNfcvPollerLockBlock(uint8_t flags, const uint8_t* uid, uint8_t blockNum) { - uint16_t rcvLen; - rfalNfcvGenericRes res; - uint8_t bn; - - bn = blockNum; - - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_LOCK_BLOCK, - flags, - RFAL_NFCV_PARAM_SKIP, - uid, - &bn, - sizeof(uint8_t), - (uint8_t*)&res, - sizeof(rfalNfcvGenericRes), - &rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalNfcvPollerReadMultipleBlocks( - uint8_t flags, - const uint8_t* uid, - uint8_t firstBlockNum, - uint8_t numOfBlocks, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen) { - uint8_t data[(RFAL_NFCV_BLOCKNUM_LEN + RFAL_NFCV_BLOCKNUM_LEN)]; - uint8_t dataLen; - - dataLen = 0U; - - /* Compute Request Data */ - data[dataLen++] = firstBlockNum; /* Set first Block Number */ - data[dataLen++] = numOfBlocks; /* Set number of blocks to read */ - - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_READ_MULTIPLE_BLOCKS, - flags, - RFAL_NFCV_PARAM_SKIP, - uid, - data, - dataLen, - rxBuf, - rxBufLen, - rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalNfcvPollerWriteMultipleBlocks( - uint8_t flags, - const uint8_t* uid, - uint8_t firstBlockNum, - uint8_t numOfBlocks, - uint8_t* txBuf, - uint16_t txBufLen, - uint8_t blockLen, - const uint8_t* wrData, - uint16_t wrDataLen) { - ReturnCode ret; - uint16_t rcvLen; - uint16_t reqLen; - rfalNfcvGenericRes res; - uint16_t msgIt; - - /* Calculate required buffer length */ - reqLen = - (uint16_t)((uid != NULL) ? (RFAL_NFCV_WR_MUL_REQ_HEADER_LEN + RFAL_NFCV_UID_LEN + wrDataLen) : (RFAL_NFCV_WR_MUL_REQ_HEADER_LEN + wrDataLen)); - - if((reqLen > txBufLen) || (blockLen > (uint8_t)RFAL_NFCV_MAX_BLOCK_LEN) || - ((((uint16_t)numOfBlocks) * (uint16_t)blockLen) != wrDataLen) || (numOfBlocks == 0U) || - (wrData == NULL)) { - return ERR_PARAM; - } - - msgIt = 0; - - /* Compute Request Command */ - txBuf[msgIt++] = (uint8_t)(flags & (~((uint32_t)RFAL_NFCV_REQ_FLAG_ADDRESS))); - txBuf[msgIt++] = RFAL_NFCV_CMD_WRITE_MULTIPLE_BLOCKS; - - /* Check if Request is to be sent in Addressed mode. Select mode flag shall be set by user */ - if(uid != NULL) { - txBuf[RFAL_NFCV_FLAG_POS] |= (uint8_t)RFAL_NFCV_REQ_FLAG_ADDRESS; - ST_MEMCPY(&txBuf[msgIt], uid, RFAL_NFCV_UID_LEN); - msgIt += (uint8_t)RFAL_NFCV_UID_LEN; - } - - txBuf[msgIt++] = firstBlockNum; - txBuf[msgIt++] = (numOfBlocks - 1U); - - if(wrDataLen > 0U) /* MISRA 21.18 */ - { - ST_MEMCPY(&txBuf[msgIt], wrData, wrDataLen); - msgIt += wrDataLen; - } - - /* Transceive Command */ - ret = rfalTransceiveBlockingTxRx( - txBuf, - msgIt, - (uint8_t*)&res, - sizeof(rfalNfcvGenericRes), - &rcvLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_NFCV_FDT_MAX); - - if(ret != ERR_NONE) { - return ret; - } - - /* Check if the response minimum length has been received */ - if(rcvLen < (uint8_t)RFAL_NFCV_FLAG_LEN) { - return ERR_PROTO; - } - - /* Check if an error has been signalled */ - if((res.RES_FLAG & (uint8_t)RFAL_NFCV_RES_FLAG_ERROR) != 0U) { - return rfalNfcvParseError(*res.data); - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcvPollerExtendedReadSingleBlock( - uint8_t flags, - const uint8_t* uid, - uint16_t blockNum, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen) { - uint8_t data[RFAL_NFCV_BLOCKNUM_EXTENDED_LEN]; - uint8_t dataLen; - - dataLen = 0U; - - /* Compute Request Data */ - data[dataLen++] = (uint8_t) - blockNum; /* TS T5T 1.0 BNo is considered as a multi-byte field. TS T5T 1.0 5.1.1.13 multi-byte field follows [DIGITAL]. [DIGITAL] 9.3.1 A multiple byte field is transmitted LSB first. */ - data[dataLen++] = (uint8_t)((blockNum >> 8U) & 0xFFU); - - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_EXTENDED_READ_SINGLE_BLOCK, - flags, - RFAL_NFCV_PARAM_SKIP, - uid, - data, - dataLen, - rxBuf, - rxBufLen, - rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalNfcvPollerExtendedWriteSingleBlock( - uint8_t flags, - const uint8_t* uid, - uint16_t blockNum, - const uint8_t* wrData, - uint8_t blockLen) { - uint8_t data[(RFAL_NFCV_BLOCKNUM_EXTENDED_LEN + RFAL_NFCV_MAX_BLOCK_LEN)]; - uint8_t dataLen; - uint16_t rcvLen; - rfalNfcvGenericRes res; - - /* Check for valid parameters */ - if((blockLen == 0U) || (blockLen > (uint8_t)RFAL_NFCV_MAX_BLOCK_LEN)) { - return ERR_PARAM; - } - - dataLen = 0U; - - /* Compute Request Data */ - data[dataLen++] = (uint8_t) - blockNum; /* TS T5T 1.0 BNo is considered as a multi-byte field. TS T5T 1.0 5.1.1.13 multi-byte field follows [DIGITAL]. [DIGITAL] 9.3.1 A multiple byte field is transmitted LSB first. */ - data[dataLen++] = (uint8_t)((blockNum >> 8U) & 0xFFU); - ST_MEMCPY(&data[dataLen], wrData, blockLen); /* Append Block data to write */ - dataLen += blockLen; - - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_EXTENDED_WRITE_SINGLE_BLOCK, - flags, - RFAL_NFCV_PARAM_SKIP, - uid, - data, - dataLen, - (uint8_t*)&res, - sizeof(rfalNfcvGenericRes), - &rcvLen); -} - -/*******************************************************************************/ -ReturnCode - rfalNfcvPollerExtendedLockSingleBlock(uint8_t flags, const uint8_t* uid, uint16_t blockNum) { - uint8_t data[RFAL_NFCV_BLOCKNUM_EXTENDED_LEN]; - uint8_t dataLen; - uint16_t rcvLen; - rfalNfcvGenericRes res; - - dataLen = 0U; - - /* Compute Request Data */ - data[dataLen++] = (uint8_t) - blockNum; /* TS T5T 1.0 BNo is considered as a multi-byte field. TS T5T 1.0 5.1.1.13 multi-byte field follows [DIGITAL]. [DIGITAL] 9.3.1 A multiple byte field is transmitted LSB first. */ - data[dataLen++] = (uint8_t)((blockNum >> 8U) & 0xFFU); - - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_EXTENDED_LOCK_SINGLE_BLOCK, - flags, - RFAL_NFCV_PARAM_SKIP, - uid, - data, - dataLen, - (uint8_t*)&res, - sizeof(rfalNfcvGenericRes), - &rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalNfcvPollerExtendedReadMultipleBlocks( - uint8_t flags, - const uint8_t* uid, - uint16_t firstBlockNum, - uint16_t numOfBlocks, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen) { - uint8_t data[(RFAL_NFCV_BLOCKNUM_EXTENDED_LEN + RFAL_NFCV_BLOCKNUM_EXTENDED_LEN)]; - uint8_t dataLen; - - dataLen = 0U; - - /* Compute Request Data */ - data[dataLen++] = (uint8_t)((firstBlockNum >> 0U) & 0xFFU); - data[dataLen++] = (uint8_t)((firstBlockNum >> 8U) & 0xFFU); - data[dataLen++] = (uint8_t)((numOfBlocks >> 0U) & 0xFFU); - data[dataLen++] = (uint8_t)((numOfBlocks >> 8U) & 0xFFU); - - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_EXTENDED_READ_MULTIPLE_BLOCK, - flags, - RFAL_NFCV_PARAM_SKIP, - uid, - data, - dataLen, - rxBuf, - rxBufLen, - rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalNfcvPollerExtendedWriteMultipleBlocks( - uint8_t flags, - const uint8_t* uid, - uint16_t firstBlockNum, - uint16_t numOfBlocks, - uint8_t* txBuf, - uint16_t txBufLen, - uint8_t blockLen, - const uint8_t* wrData, - uint16_t wrDataLen) { - ReturnCode ret; - uint16_t rcvLen; - uint16_t reqLen; - rfalNfcvGenericRes res; - uint16_t msgIt; - uint16_t nBlocks; - - /* Calculate required buffer length */ - reqLen = - ((uid != NULL) ? (RFAL_NFCV_WR_MUL_REQ_HEADER_LEN + RFAL_NFCV_UID_LEN + wrDataLen) : - (RFAL_NFCV_WR_MUL_REQ_HEADER_LEN + wrDataLen)); - - if((reqLen > txBufLen) || (blockLen > (uint8_t)RFAL_NFCV_MAX_BLOCK_LEN) || - (((uint16_t)numOfBlocks * (uint16_t)blockLen) != wrDataLen) || (numOfBlocks == 0U)) { - return ERR_PARAM; - } - - msgIt = 0; - nBlocks = (numOfBlocks - 1U); - - /* Compute Request Command */ - txBuf[msgIt++] = (uint8_t)(flags & (~((uint32_t)RFAL_NFCV_REQ_FLAG_ADDRESS))); - txBuf[msgIt++] = RFAL_NFCV_CMD_EXTENDED_WRITE_MULTIPLE_BLOCK; - - /* Check if Request is to be sent in Addressed mode. Select mode flag shall be set by user */ - if(uid != NULL) { - txBuf[RFAL_NFCV_FLAG_POS] |= (uint8_t)RFAL_NFCV_REQ_FLAG_ADDRESS; - ST_MEMCPY(&txBuf[msgIt], uid, RFAL_NFCV_UID_LEN); - msgIt += (uint8_t)RFAL_NFCV_UID_LEN; - } - - txBuf[msgIt++] = (uint8_t)((firstBlockNum >> 0) & 0xFFU); - txBuf[msgIt++] = (uint8_t)((firstBlockNum >> 8) & 0xFFU); - txBuf[msgIt++] = (uint8_t)((nBlocks >> 0) & 0xFFU); - txBuf[msgIt++] = (uint8_t)((nBlocks >> 8) & 0xFFU); - - if(wrDataLen > 0U) /* MISRA 21.18 */ - { - ST_MEMCPY(&txBuf[msgIt], wrData, wrDataLen); - msgIt += wrDataLen; - } - - /* Transceive Command */ - ret = rfalTransceiveBlockingTxRx( - txBuf, - msgIt, - (uint8_t*)&res, - sizeof(rfalNfcvGenericRes), - &rcvLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_NFCV_FDT_MAX); - - if(ret != ERR_NONE) { - return ret; - } - - /* Check if the response minimum length has been received */ - if(rcvLen < (uint8_t)RFAL_NFCV_FLAG_LEN) { - return ERR_PROTO; - } - - /* Check if an error has been signalled */ - if((res.RES_FLAG & (uint8_t)RFAL_NFCV_RES_FLAG_ERROR) != 0U) { - return rfalNfcvParseError(*res.data); - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalNfcvPollerGetSystemInformation( - uint8_t flags, - const uint8_t* uid, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen) { - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_GET_SYS_INFO, - flags, - RFAL_NFCV_PARAM_SKIP, - uid, - NULL, - 0U, - rxBuf, - rxBufLen, - rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalNfcvPollerExtendedGetSystemInformation( - uint8_t flags, - const uint8_t* uid, - uint8_t requestField, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen) { - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_EXTENDED_GET_SYS_INFO, - flags, - requestField, - uid, - NULL, - 0U, - rxBuf, - rxBufLen, - rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalNfcvPollerTransceiveReq( - uint8_t cmd, - uint8_t flags, - uint8_t param, - const uint8_t* uid, - const uint8_t* data, - uint16_t dataLen, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen) { - ReturnCode ret; - rfalNfcvGenericReq req; - uint8_t msgIt; - rfalBitRate rxBR; - bool fastMode; - - msgIt = 0; - fastMode = false; - - /* Check for valid parameters */ - if((rxBuf == NULL) || (rcvLen == NULL) || ((dataLen > 0U) && (data == NULL)) || - (dataLen > ((uid != NULL) ? RFAL_NFCV_MAX_GEN_DATA_LEN : - (RFAL_NFCV_MAX_GEN_DATA_LEN - RFAL_NFCV_UID_LEN)))) { - return ERR_PARAM; - } - - /* Check if the command is an ST's Fast command */ - if((cmd == (uint8_t)RFAL_NFCV_CMD_FAST_READ_SINGLE_BLOCK) || - (cmd == (uint8_t)RFAL_NFCV_CMD_FAST_EXTENDED_READ_SINGLE_BLOCK) || - (cmd == (uint8_t)RFAL_NFCV_CMD_FAST_READ_MULTIPLE_BLOCKS) || - (cmd == (uint8_t)RFAL_NFCV_CMD_FAST_EXTENDED_READ_MULTIPLE_BLOCKS) || - (cmd == (uint8_t)RFAL_NFCV_CMD_FAST_WRITE_MESSAGE) || - (cmd == (uint8_t)RFAL_NFCV_CMD_FAST_READ_MESSAGE_LENGTH) || - (cmd == (uint8_t)RFAL_NFCV_CMD_FAST_READ_MESSAGE) || - (cmd == (uint8_t)RFAL_NFCV_CMD_FAST_READ_DYN_CONFIGURATION) || - (cmd == (uint8_t)RFAL_NFCV_CMD_FAST_WRITE_DYN_CONFIGURATION)) { - /* Store current Rx bit rate and move to fast mode */ - rfalGetBitRate(NULL, &rxBR); - rfalSetBitRate(RFAL_BR_KEEP, RFAL_BR_52p97); - - fastMode = true; - } - - /* Compute Request Command */ - req.REQ_FLAG = (uint8_t)(flags & (~((uint32_t)RFAL_NFCV_REQ_FLAG_ADDRESS))); - req.CMD = cmd; - - /* Prepend parameter on ceratin proprietary requests: IC Manuf, Parameters */ - if(param != RFAL_NFCV_PARAM_SKIP) { - req.payload.data[msgIt++] = param; - } - - /* Check if Request is to be sent in Addressed mode. Select mode flag shall be set by user */ - if(uid != NULL) { - req.REQ_FLAG |= (uint8_t)RFAL_NFCV_REQ_FLAG_ADDRESS; - ST_MEMCPY(&req.payload.data[msgIt], uid, RFAL_NFCV_UID_LEN); - msgIt += RFAL_NFCV_UID_LEN; - } - - if(dataLen > 0U) { - ST_MEMCPY(&req.payload.data[msgIt], data, dataLen); - msgIt += (uint8_t)dataLen; - } - - /* Transceive Command */ - ret = rfalTransceiveBlockingTxRx( - (uint8_t*)&req, - (RFAL_NFCV_CMD_LEN + RFAL_NFCV_FLAG_LEN + (uint16_t)msgIt), - rxBuf, - rxBufLen, - rcvLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_NFCV_FDT_MAX); - - /* If the Option Flag is set in certain commands an EOF needs to be sent after 20ms to retrieve the VICC response ISO15693-3 2009 10.4.2 & 10.4.3 & 10.4.5 */ - if(((flags & (uint8_t)RFAL_NFCV_REQ_FLAG_OPTION) != 0U) && - ((cmd == (uint8_t)RFAL_NFCV_CMD_WRITE_SINGLE_BLOCK) || - (cmd == (uint8_t)RFAL_NFCV_CMD_WRITE_MULTIPLE_BLOCKS) || - (cmd == (uint8_t)RFAL_NFCV_CMD_LOCK_BLOCK) || - (cmd == (uint8_t)RFAL_NFCV_CMD_EXTENDED_WRITE_SINGLE_BLOCK) || - (cmd == (uint8_t)RFAL_NFCV_CMD_EXTENDED_LOCK_SINGLE_BLOCK) || - (cmd == (uint8_t)RFAL_NFCV_CMD_EXTENDED_WRITE_MULTIPLE_BLOCK))) { - ret = rfalISO15693TransceiveEOF(rxBuf, (uint8_t)rxBufLen, rcvLen); - } - - /* Restore Rx BitRate */ - if(fastMode) { - rfalSetBitRate(RFAL_BR_KEEP, rxBR); - } - - if(ret != ERR_NONE) { - return ret; - } - - /* Check if the response minimum length has been received */ - if((*rcvLen) < (uint8_t)RFAL_NFCV_FLAG_LEN) { - return ERR_PROTO; - } - - /* Check if an error has been signalled */ - if((rxBuf[RFAL_NFCV_FLAG_POS] & (uint8_t)RFAL_NFCV_RES_FLAG_ERROR) != 0U) { - return rfalNfcvParseError(rxBuf[RFAL_NFCV_DATASTART_POS]); - } - - return ERR_NONE; -} - -#endif /* RFAL_FEATURE_NFCV */ diff --git a/lib/ST25RFAL002/source/rfal_picopass.c b/lib/ST25RFAL002/source/rfal_picopass.c deleted file mode 100644 index 55dbe6497b1..00000000000 --- a/lib/ST25RFAL002/source/rfal_picopass.c +++ /dev/null @@ -1,160 +0,0 @@ - -#include "rfal_picopass.h" -#include "utils.h" - -typedef struct { - uint8_t CMD; - uint8_t CSN[RFAL_PICOPASS_UID_LEN]; -} rfalPicoPassSelectReq; - -typedef struct { - uint8_t CMD; - uint8_t null[4]; - uint8_t mac[4]; -} rfalPicoPassCheckReq; - -ReturnCode rfalPicoPassPollerInitialize(void) { - ReturnCode ret; - - EXIT_ON_ERR(ret, rfalSetMode(RFAL_MODE_POLL_PICOPASS, RFAL_BR_26p48, RFAL_BR_26p48)); - rfalSetErrorHandling(RFAL_ERRORHANDLING_NFC); - - rfalSetGT(RFAL_GT_PICOPASS); - rfalSetFDTListen(RFAL_FDT_LISTEN_PICOPASS_POLLER); - rfalSetFDTPoll(RFAL_FDT_POLL_PICOPASS_POLLER); - - return ERR_NONE; -} - -ReturnCode rfalPicoPassPollerCheckPresence(void) { - ReturnCode ret; - uint8_t txBuf[1] = {RFAL_PICOPASS_CMD_ACTALL}; - uint8_t rxBuf[32] = {0}; - uint16_t recvLen = 0; - uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; - uint32_t fwt = rfalConvMsTo1fc(20); - - ret = rfalTransceiveBlockingTxRx(txBuf, 1, rxBuf, 32, &recvLen, flags, fwt); - return ret; -} - -ReturnCode rfalPicoPassPollerIdentify(rfalPicoPassIdentifyRes* idRes) { - ReturnCode ret; - - uint8_t txBuf[1] = {RFAL_PICOPASS_CMD_IDENTIFY}; - uint16_t recvLen = 0; - uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; - uint32_t fwt = rfalConvMsTo1fc(20); - - ret = rfalTransceiveBlockingTxRx( - txBuf, - sizeof(txBuf), - (uint8_t*)idRes, - sizeof(rfalPicoPassIdentifyRes), - &recvLen, - flags, - fwt); - // printf("identify rx: %d %s\n", recvLen, hex2Str(idRes->CSN, RFAL_PICOPASS_UID_LEN)); - - return ret; -} - -ReturnCode rfalPicoPassPollerSelect(uint8_t* csn, rfalPicoPassSelectRes* selRes) { - ReturnCode ret; - - rfalPicoPassSelectReq selReq; - selReq.CMD = RFAL_PICOPASS_CMD_SELECT; - ST_MEMCPY(selReq.CSN, csn, RFAL_PICOPASS_UID_LEN); - uint16_t recvLen = 0; - uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; - uint32_t fwt = rfalConvMsTo1fc(20); - - ret = rfalTransceiveBlockingTxRx( - (uint8_t*)&selReq, - sizeof(rfalPicoPassSelectReq), - (uint8_t*)selRes, - sizeof(rfalPicoPassSelectRes), - &recvLen, - flags, - fwt); - // printf("select rx: %d %s\n", recvLen, hex2Str(selRes->CSN, RFAL_PICOPASS_UID_LEN)); - if(ret == ERR_TIMEOUT) { - return ERR_NONE; - } - - return ret; -} - -ReturnCode rfalPicoPassPollerReadCheck(rfalPicoPassReadCheckRes* rcRes) { - ReturnCode ret; - uint8_t txBuf[2] = {RFAL_PICOPASS_CMD_READCHECK, 0x02}; - uint16_t recvLen = 0; - uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; - uint32_t fwt = rfalConvMsTo1fc(20); - - ret = rfalTransceiveBlockingTxRx( - txBuf, - sizeof(txBuf), - (uint8_t*)rcRes, - sizeof(rfalPicoPassReadCheckRes), - &recvLen, - flags, - fwt); - // printf("readcheck rx: %d %s\n", recvLen, hex2Str(rcRes->CCNR, 8)); - - if(ret == ERR_CRC) { - return ERR_NONE; - } - - return ret; -} - -ReturnCode rfalPicoPassPollerCheck(uint8_t* mac, rfalPicoPassCheckRes* chkRes) { - ReturnCode ret; - rfalPicoPassCheckReq chkReq; - chkReq.CMD = RFAL_PICOPASS_CMD_CHECK; - ST_MEMCPY(chkReq.mac, mac, 4); - ST_MEMSET(chkReq.null, 0, 4); - uint16_t recvLen = 0; - uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; - uint32_t fwt = rfalConvMsTo1fc(20); - - // printf("check tx: %s\n", hex2Str((uint8_t *)&chkReq, sizeof(rfalPicoPassCheckReq))); - ret = rfalTransceiveBlockingTxRx( - (uint8_t*)&chkReq, - sizeof(rfalPicoPassCheckReq), - (uint8_t*)chkRes, - sizeof(rfalPicoPassCheckRes), - &recvLen, - flags, - fwt); - // printf("check rx: %d %s\n", recvLen, hex2Str(chkRes->mac, 4)); - if(ret == ERR_CRC) { - return ERR_NONE; - } - - return ret; -} - -ReturnCode rfalPicoPassPollerReadBlock(uint8_t blockNum, rfalPicoPassReadBlockRes* readRes) { - ReturnCode ret; - - uint8_t txBuf[4] = {RFAL_PICOPASS_CMD_READ, 0, 0, 0}; - txBuf[1] = blockNum; - uint16_t crc = rfalCrcCalculateCcitt(0xE012, txBuf + 1, 1); - memcpy(txBuf + 2, &crc, sizeof(uint16_t)); - - uint16_t recvLen = 0; - uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; - uint32_t fwt = rfalConvMsTo1fc(20); - - ret = rfalTransceiveBlockingTxRx( - txBuf, - sizeof(txBuf), - (uint8_t*)readRes, - sizeof(rfalPicoPassReadBlockRes), - &recvLen, - flags, - fwt); - return ret; -} diff --git a/lib/ST25RFAL002/source/rfal_st25tb.c b/lib/ST25RFAL002/source/rfal_st25tb.c deleted file mode 100644 index 6e110c91e74..00000000000 --- a/lib/ST25RFAL002/source/rfal_st25tb.c +++ /dev/null @@ -1,563 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_st25tb.c - * - * \author Gustavo Patricio - * - * \brief Implementation of ST25TB interface - * - */ - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "rfal_st25tb.h" -#include "utils.h" - -/* - ****************************************************************************** - * ENABLE SWITCH - ****************************************************************************** - */ -#ifndef RFAL_FEATURE_ST25TB -#define RFAL_FEATURE_ST25TB false /* ST25TB module configuration missing. Disabled by default */ -#endif - -#if RFAL_FEATURE_ST25TB - -/* - ****************************************************************************** - * GLOBAL DEFINES - ****************************************************************************** - */ - -#define RFAL_ST25TB_CMD_LEN 1U /*!< ST25TB length of a command */ -#define RFAL_ST25TB_SLOTS 16U /*!< ST25TB number of slots */ -#define RFAL_ST25TB_SLOTNUM_MASK 0x0FU /*!< ST25TB Slot Number bit mask on SlotMarker */ -#define RFAL_ST25TB_SLOTNUM_SHIFT 4U /*!< ST25TB Slot Number shift on SlotMarker */ - -#define RFAL_ST25TB_INITIATE_CMD1 0x06U /*!< ST25TB Initiate command byte1 */ -#define RFAL_ST25TB_INITIATE_CMD2 0x00U /*!< ST25TB Initiate command byte2 */ -#define RFAL_ST25TB_PCALL_CMD1 0x06U /*!< ST25TB Pcall16 command byte1 */ -#define RFAL_ST25TB_PCALL_CMD2 0x04U /*!< ST25TB Pcall16 command byte2 */ -#define RFAL_ST25TB_SELECT_CMD 0x0EU /*!< ST25TB Select command */ -#define RFAL_ST25TB_GET_UID_CMD 0x0BU /*!< ST25TB Get UID command */ -#define RFAL_ST25TB_COMPLETION_CMD 0x0FU /*!< ST25TB Completion command */ -#define RFAL_ST25TB_RESET_INV_CMD 0x0CU /*!< ST25TB Reset to Inventory command */ -#define RFAL_ST25TB_READ_BLOCK_CMD 0x08U /*!< ST25TB Read Block command */ -#define RFAL_ST25TB_WRITE_BLOCK_CMD 0x09U /*!< ST25TB Write Block command */ - -#define RFAL_ST25TB_T0 2157U /*!< ST25TB t0 159 us ST25TB RF characteristics */ -#define RFAL_ST25TB_T1 2048U /*!< ST25TB t1 151 us ST25TB RF characteristics */ - -#define RFAL_ST25TB_FWT \ - (RFAL_ST25TB_T0 + RFAL_ST25TB_T1) /*!< ST25TB FWT = T0 + T1 */ -#define RFAL_ST25TB_TW rfalConvMsTo1fc(7U) /*!< ST25TB TW : Programming time for write max 7ms */ - -/* - ****************************************************************************** - * GLOBAL MACROS - ****************************************************************************** - */ - -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ - -/*! Initiate Request */ -typedef struct { - uint8_t cmd1; /*!< Initiate Request cmd1: 0x06 */ - uint8_t cmd2; /*!< Initiate Request cmd2: 0x00 */ -} rfalSt25tbInitiateReq; - -/*! Pcall16 Request */ -typedef struct { - uint8_t cmd1; /*!< Pcal16 Request cmd1: 0x06 */ - uint8_t cmd2; /*!< Pcal16 Request cmd2: 0x04 */ -} rfalSt25tbPcallReq; - -/*! Select Request */ -typedef struct { - uint8_t cmd; /*!< Select Request cmd: 0x0E */ - uint8_t chipId; /*!< Chip ID */ -} rfalSt25tbSelectReq; - -/*! Read Block Request */ -typedef struct { - uint8_t cmd; /*!< Select Request cmd: 0x08 */ - uint8_t address; /*!< Block address */ -} rfalSt25tbReadBlockReq; - -/*! Write Block Request */ -typedef struct { - uint8_t cmd; /*!< Select Request cmd: 0x09 */ - uint8_t address; /*!< Block address */ - rfalSt25tbBlock data; /*!< Block Data */ -} rfalSt25tbWriteBlockReq; - -/* -****************************************************************************** -* LOCAL FUNCTION PROTOTYPES -****************************************************************************** -*/ -/*! - ***************************************************************************** - * \brief ST25TB Poller Do Collision Resolution - * - * This method performs ST25TB Collision resolution loop for each slot - * - * \param[in] devLimit : device limit value, and size st25tbDevList - * \param[out] st25tbDevList : ST35TB listener device info - * \param[out] devCnt : Devices found counter - * - * \return colPending : true if a collision was detected - ***************************************************************************** - */ -static bool rfalSt25tbPollerDoCollisionResolution( - uint8_t devLimit, - rfalSt25tbListenDevice* st25tbDevList, - uint8_t* devCnt); - -/* -****************************************************************************** -* LOCAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -static bool rfalSt25tbPollerDoCollisionResolution( - uint8_t devLimit, - rfalSt25tbListenDevice* st25tbDevList, - uint8_t* devCnt) { - uint8_t i; - uint8_t chipId; - ReturnCode ret; - bool col; - - col = false; - - for(i = 0; i < RFAL_ST25TB_SLOTS; i++) { - platformDelay(1); /* Wait t2: Answer to new request delay */ - - if(i == 0U) { - /* Step 2: Send Pcall16 */ - ret = rfalSt25tbPollerPcall(&chipId); - } else { - /* Step 3-17: Send Pcall16 */ - ret = rfalSt25tbPollerSlotMarker(i, &chipId); - } - - if(ret == ERR_NONE) { - /* Found another device */ - st25tbDevList[*devCnt].chipID = chipId; - st25tbDevList[*devCnt].isDeselected = false; - - /* Select Device, retrieve its UID */ - ret = rfalSt25tbPollerSelect(chipId); - - /* By Selecting this device, the previous gets Deselected */ - if((*devCnt) > 0U) { - st25tbDevList[(*devCnt) - 1U].isDeselected = true; - } - - if(ERR_NONE == ret) { - rfalSt25tbPollerGetUID(&st25tbDevList[*devCnt].UID); - } - - if(ERR_NONE == ret) { - (*devCnt)++; - } - } else if((ret == ERR_CRC) || (ret == ERR_FRAMING)) { - col = true; - } else { - /* MISRA 15.7 - Empty else */ - } - - if(*devCnt >= devLimit) { - break; - } - } - return col; -} - -/* -****************************************************************************** -* LOCAL VARIABLES -****************************************************************************** -*/ - -/* -****************************************************************************** -* GLOBAL FUNCTIONS -****************************************************************************** -*/ - -/*******************************************************************************/ -ReturnCode rfalSt25tbPollerInitialize(void) { - return rfalNfcbPollerInitialize(); -} - -/*******************************************************************************/ -ReturnCode rfalSt25tbPollerCheckPresence(uint8_t* chipId) { - ReturnCode ret; - uint8_t chipIdRes; - - chipIdRes = 0x00; - - /* Send Initiate Request */ - ret = rfalSt25tbPollerInitiate(&chipIdRes); - - /* Check if a transmission error was detected */ - if((ret == ERR_CRC) || (ret == ERR_FRAMING)) { - return ERR_NONE; - } - - /* Copy chip ID if requested */ - if(chipId != NULL) { - *chipId = chipIdRes; - } - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalSt25tbPollerInitiate(uint8_t* chipId) { - ReturnCode ret; - uint16_t rxLen; - rfalSt25tbInitiateReq initiateReq; - uint8_t rxBuf - [RFAL_ST25TB_CHIP_ID_LEN + - RFAL_ST25TB_CRC_LEN]; /* In case we receive less data that CRC, RF layer will not remove the CRC from buffer */ - - /* Compute Initiate Request */ - initiateReq.cmd1 = RFAL_ST25TB_INITIATE_CMD1; - initiateReq.cmd2 = RFAL_ST25TB_INITIATE_CMD2; - - /* Send Initiate Request */ - ret = rfalTransceiveBlockingTxRx( - (uint8_t*)&initiateReq, - sizeof(rfalSt25tbInitiateReq), - (uint8_t*)rxBuf, - sizeof(rxBuf), - &rxLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_ST25TB_FWT); - - /* Check for valid Select Response */ - if((ret == ERR_NONE) && (rxLen != RFAL_ST25TB_CHIP_ID_LEN)) { - return ERR_PROTO; - } - - /* Copy chip ID if requested */ - if(chipId != NULL) { - *chipId = *rxBuf; - } - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalSt25tbPollerPcall(uint8_t* chipId) { - ReturnCode ret; - uint16_t rxLen; - rfalSt25tbPcallReq pcallReq; - - /* Compute Pcal16 Request */ - pcallReq.cmd1 = RFAL_ST25TB_PCALL_CMD1; - pcallReq.cmd2 = RFAL_ST25TB_PCALL_CMD2; - - /* Send Pcal16 Request */ - ret = rfalTransceiveBlockingTxRx( - (uint8_t*)&pcallReq, - sizeof(rfalSt25tbPcallReq), - (uint8_t*)chipId, - RFAL_ST25TB_CHIP_ID_LEN, - &rxLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_ST25TB_FWT); - - /* Check for valid Select Response */ - if((ret == ERR_NONE) && (rxLen != RFAL_ST25TB_CHIP_ID_LEN)) { - return ERR_PROTO; - } - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalSt25tbPollerSlotMarker(uint8_t slotNum, uint8_t* chipIdRes) { - ReturnCode ret; - uint16_t rxLen; - uint8_t slotMarker; - - if((slotNum == 0U) || (slotNum > 15U)) { - return ERR_PARAM; - } - - /* Compute SlotMarker */ - slotMarker = - (((slotNum & RFAL_ST25TB_SLOTNUM_MASK) << RFAL_ST25TB_SLOTNUM_SHIFT) | - RFAL_ST25TB_PCALL_CMD1); - - /* Send SlotMarker */ - ret = rfalTransceiveBlockingTxRx( - (uint8_t*)&slotMarker, - RFAL_ST25TB_CMD_LEN, - (uint8_t*)chipIdRes, - RFAL_ST25TB_CHIP_ID_LEN, - &rxLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_ST25TB_FWT); - - /* Check for valid ChipID Response */ - if((ret == ERR_NONE) && (rxLen != RFAL_ST25TB_CHIP_ID_LEN)) { - return ERR_PROTO; - } - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalSt25tbPollerSelect(uint8_t chipId) { - ReturnCode ret; - uint16_t rxLen; - rfalSt25tbSelectReq selectReq; - uint8_t chipIdRes; - - /* Compute Select Request */ - selectReq.cmd = RFAL_ST25TB_SELECT_CMD; - selectReq.chipId = chipId; - - /* Send Select Request */ - ret = rfalTransceiveBlockingTxRx( - (uint8_t*)&selectReq, - sizeof(rfalSt25tbSelectReq), - (uint8_t*)&chipIdRes, - RFAL_ST25TB_CHIP_ID_LEN, - &rxLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_ST25TB_FWT); - - /* Check for valid Select Response */ - if((ret == ERR_NONE) && ((rxLen != RFAL_ST25TB_CHIP_ID_LEN) || (chipIdRes != chipId))) { - return ERR_PROTO; - } - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalSt25tbPollerGetUID(rfalSt25tbUID* UID) { - ReturnCode ret; - uint16_t rxLen; - uint8_t getUidReq; - - /* Compute Get UID Request */ - getUidReq = RFAL_ST25TB_GET_UID_CMD; - - /* Send Select Request */ - ret = rfalTransceiveBlockingTxRx( - (uint8_t*)&getUidReq, - RFAL_ST25TB_CMD_LEN, - (uint8_t*)UID, - sizeof(rfalSt25tbUID), - &rxLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_ST25TB_FWT); - - /* Check for valid UID Response */ - if((ret == ERR_NONE) && (rxLen != RFAL_ST25TB_UID_LEN)) { - return ERR_PROTO; - } - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalSt25tbPollerCollisionResolution( - uint8_t devLimit, - rfalSt25tbListenDevice* st25tbDevList, - uint8_t* devCnt) { - uint8_t chipId; - ReturnCode ret; - bool detected; /* collision or device was detected */ - - if((st25tbDevList == NULL) || (devCnt == NULL) || (devLimit == 0U)) { - return ERR_PARAM; - } - - *devCnt = 0; - - /* Step 1: Send Initiate */ - ret = rfalSt25tbPollerInitiate(&chipId); - if(ret == ERR_NONE) { - /* If only 1 answer is detected */ - st25tbDevList[*devCnt].chipID = chipId; - st25tbDevList[*devCnt].isDeselected = false; - - /* Retrieve its UID and keep it Selected*/ - ret = rfalSt25tbPollerSelect(chipId); - - if(ERR_NONE == ret) { - ret = rfalSt25tbPollerGetUID(&st25tbDevList[*devCnt].UID); - } - - if(ERR_NONE == ret) { - (*devCnt)++; - } - } - /* Always proceed to Pcall16 anticollision as phase differences of tags can lead to no tag recognized, even if there is one */ - if(*devCnt < devLimit) { - /* Multiple device responses */ - do { - detected = rfalSt25tbPollerDoCollisionResolution(devLimit, st25tbDevList, devCnt); - } while((detected == true) && (*devCnt < devLimit)); - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalSt25tbPollerReadBlock(uint8_t blockAddress, rfalSt25tbBlock* blockData) { - ReturnCode ret; - uint16_t rxLen; - rfalSt25tbReadBlockReq readBlockReq; - - /* Compute Read Block Request */ - readBlockReq.cmd = RFAL_ST25TB_READ_BLOCK_CMD; - readBlockReq.address = blockAddress; - - /* Send Read Block Request */ - ret = rfalTransceiveBlockingTxRx( - (uint8_t*)&readBlockReq, - sizeof(rfalSt25tbReadBlockReq), - (uint8_t*)blockData, - sizeof(rfalSt25tbBlock), - &rxLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_ST25TB_FWT); - - /* Check for valid UID Response */ - if((ret == ERR_NONE) && (rxLen != RFAL_ST25TB_BLOCK_LEN)) { - return ERR_PROTO; - } - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalSt25tbPollerWriteBlock(uint8_t blockAddress, const rfalSt25tbBlock* blockData) { - ReturnCode ret; - uint16_t rxLen; - rfalSt25tbWriteBlockReq writeBlockReq; - rfalSt25tbBlock tmpBlockData; - - /* Compute Write Block Request */ - writeBlockReq.cmd = RFAL_ST25TB_WRITE_BLOCK_CMD; - writeBlockReq.address = blockAddress; - ST_MEMCPY(&writeBlockReq.data, blockData, RFAL_ST25TB_BLOCK_LEN); - - /* Send Write Block Request */ - ret = rfalTransceiveBlockingTxRx( - (uint8_t*)&writeBlockReq, - sizeof(rfalSt25tbWriteBlockReq), - tmpBlockData, - RFAL_ST25TB_BLOCK_LEN, - &rxLen, - RFAL_TXRX_FLAGS_DEFAULT, - (RFAL_ST25TB_FWT + RFAL_ST25TB_TW)); - - /* Check if there was any error besides timeout */ - if(ret != ERR_TIMEOUT) { - /* Check if an unexpected answer was received */ - if(ret == ERR_NONE) { - return ERR_PROTO; - } - - /* Check whether a transmission error occurred */ - if((ret != ERR_CRC) && (ret != ERR_FRAMING) && (ret != ERR_NOMEM) && - (ret != ERR_RF_COLLISION)) { - return ret; - } - - /* If a transmission error occurred (maybe noise while commiting data) wait maximum programming time and verify data afterwards */ - rfalSetGT((RFAL_ST25TB_FWT + RFAL_ST25TB_TW)); - rfalFieldOnAndStartGT(); - } - - ret = rfalSt25tbPollerReadBlock(blockAddress, &tmpBlockData); - if(ret == ERR_NONE) { - if(ST_BYTECMP(&tmpBlockData, blockData, RFAL_ST25TB_BLOCK_LEN) == 0) { - return ERR_NONE; - } - return ERR_PROTO; - } - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalSt25tbPollerCompletion(void) { - uint8_t completionReq; - - /* Compute Completion Request */ - completionReq = RFAL_ST25TB_COMPLETION_CMD; - - /* Send Completion Request, no response is expected */ - return rfalTransceiveBlockingTxRx( - (uint8_t*)&completionReq, - RFAL_ST25TB_CMD_LEN, - NULL, - 0, - NULL, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_ST25TB_FWT); -} - -/*******************************************************************************/ -ReturnCode rfalSt25tbPollerResetToInventory(void) { - uint8_t resetInvReq; - - /* Compute Completion Request */ - resetInvReq = RFAL_ST25TB_RESET_INV_CMD; - - /* Send Completion Request, no response is expected */ - return rfalTransceiveBlockingTxRx( - (uint8_t*)&resetInvReq, - RFAL_ST25TB_CMD_LEN, - NULL, - 0, - NULL, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_ST25TB_FWT); -} - -#endif /* RFAL_FEATURE_ST25TB */ diff --git a/lib/ST25RFAL002/source/rfal_st25xv.c b/lib/ST25RFAL002/source/rfal_st25xv.c deleted file mode 100644 index 56c9ccb6fae..00000000000 --- a/lib/ST25RFAL002/source/rfal_st25xv.c +++ /dev/null @@ -1,818 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_st25xv.c - * - * \author Gustavo Patricio - * - * \brief NFC-V ST25 NFC-V Tag specific features - * - * This module provides support for ST's specific features available on - * NFC-V (ISO15693) tag families: ST25D, ST25TV, M24LR - * - */ - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "rfal_st25xv.h" -#include "rfal_nfcv.h" -#include "utils.h" - -/* - ****************************************************************************** - * ENABLE SWITCH - ****************************************************************************** - */ - -#ifndef RFAL_FEATURE_ST25xV -#define RFAL_FEATURE_ST25xV false /* ST25xV module configuration missing. Disabled by default */ -#endif - -#if RFAL_FEATURE_ST25xV - -/* - ****************************************************************************** - * GLOBAL DEFINES - ****************************************************************************** - */ - -#define RFAL_ST25xV_READ_CONFIG_LEN \ - 2U /*!< READ CONFIGURATION length */ -#define RFAL_ST25xV_READ_MSG_LEN_LEN \ - 2U /*!< READ MESSAGE LENGTH length */ -#define RFAL_ST25xV_CONF_POINTER_LEN \ - 1U /*!< READ/WRITE CONFIGURATION Pointer length */ -#define RFAL_ST25xV_CONF_REGISTER_LEN \ - 1U /*!< READ/WRITE CONFIGURATION Register length */ -#define RFAL_ST25xV_PWDNUM_LEN \ - 1U /*!< Password Number length */ -#define RFAL_ST25xV_PWD_LEN \ - 8U /*!< Password length */ -#define RFAL_ST25xV_MBPOINTER_LEN \ - 1U /*!< Read Message MBPointer length */ -#define RFAL_ST25xV_NUMBYTES_LEN \ - 1U /*!< Read Message Number of Bytes length */ - -#define RFAL_ST25TV02K_TBOOT_RF \ - 1U /*!< RF Boot time (Minimum time from carrier generation to first data) */ -#define RFAL_ST25TV02K_TRF_OFF \ - 2U /*!< RF OFF time */ - -#define RFAL_ST25xV_FDT_POLL_MAX \ - rfalConvMsTo1fc(20) /*!< Maximum Wait time FDTV,EOF 20 ms Digital 2.1 B.5 */ -#define RFAL_NFCV_FLAG_POS \ - 0U /*!< Flag byte position */ -#define RFAL_NFCV_FLAG_LEN \ - 1U /*!< Flag byte length */ - -/* -****************************************************************************** -* LOCAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -static ReturnCode rfalST25xVPollerGenericReadConfiguration( - uint8_t cmd, - uint8_t flags, - const uint8_t* uid, - uint8_t pointer, - uint8_t* regValue); -static ReturnCode rfalST25xVPollerGenericWriteConfiguration( - uint8_t cmd, - uint8_t flags, - const uint8_t* uid, - uint8_t pointer, - uint8_t regValue); -static ReturnCode rfalST25xVPollerGenericReadMessageLength( - uint8_t cmd, - uint8_t flags, - const uint8_t* uid, - uint8_t* msgLen); -static ReturnCode rfalST25xVPollerGenericReadMessage( - uint8_t cmd, - uint8_t flags, - const uint8_t* uid, - uint8_t mbPointer, - uint8_t numBytes, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen); -static ReturnCode rfalST25xVPollerGenericWriteMessage( - uint8_t cmd, - uint8_t flags, - const uint8_t* uid, - uint8_t msgLen, - const uint8_t* msgData, - uint8_t* txBuf, - uint16_t txBufLen); -/* -****************************************************************************** -* LOCAL FUNCTIONS -****************************************************************************** -*/ - -/*******************************************************************************/ -static ReturnCode rfalST25xVPollerGenericReadConfiguration( - uint8_t cmd, - uint8_t flags, - const uint8_t* uid, - uint8_t pointer, - uint8_t* regValue) { - ReturnCode ret; - uint8_t p; - uint16_t rcvLen; - rfalNfcvGenericRes res; - - if(regValue == NULL) { - return ERR_PARAM; - } - - p = pointer; - - ret = rfalNfcvPollerTransceiveReq( - cmd, - flags, - RFAL_NFCV_ST_IC_MFG_CODE, - uid, - &p, - sizeof(uint8_t), - (uint8_t*)&res, - sizeof(rfalNfcvGenericRes), - &rcvLen); - if(ret == ERR_NONE) { - if(rcvLen < RFAL_ST25xV_READ_CONFIG_LEN) { - ret = ERR_PROTO; - } else { - *regValue = res.data[0]; - } - } - return ret; -} - -/*******************************************************************************/ -static ReturnCode rfalST25xVPollerGenericWriteConfiguration( - uint8_t cmd, - uint8_t flags, - const uint8_t* uid, - uint8_t pointer, - uint8_t regValue) { - uint8_t data[RFAL_ST25xV_CONF_POINTER_LEN + RFAL_ST25xV_CONF_REGISTER_LEN]; - uint8_t dataLen; - uint16_t rcvLen; - rfalNfcvGenericRes res; - - dataLen = 0U; - - data[dataLen++] = pointer; - data[dataLen++] = regValue; - - return rfalNfcvPollerTransceiveReq( - cmd, - flags, - RFAL_NFCV_ST_IC_MFG_CODE, - uid, - data, - dataLen, - (uint8_t*)&res, - sizeof(rfalNfcvGenericRes), - &rcvLen); -} - -/*******************************************************************************/ -static ReturnCode rfalST25xVPollerGenericReadMessageLength( - uint8_t cmd, - uint8_t flags, - const uint8_t* uid, - uint8_t* msgLen) { - ReturnCode ret; - uint16_t rcvLen; - rfalNfcvGenericRes res; - - if(msgLen == NULL) { - return ERR_PARAM; - } - - ret = rfalNfcvPollerTransceiveReq( - cmd, - flags, - RFAL_NFCV_ST_IC_MFG_CODE, - uid, - NULL, - 0, - (uint8_t*)&res, - sizeof(rfalNfcvGenericRes), - &rcvLen); - if(ret == ERR_NONE) { - if(rcvLen < RFAL_ST25xV_READ_MSG_LEN_LEN) { - ret = ERR_PROTO; - } else { - *msgLen = res.data[0]; - } - } - return ret; -} - -/*******************************************************************************/ -static ReturnCode rfalST25xVPollerGenericReadMessage( - uint8_t cmd, - uint8_t flags, - const uint8_t* uid, - uint8_t mbPointer, - uint8_t numBytes, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen) { - uint8_t data[RFAL_ST25xV_MBPOINTER_LEN + RFAL_ST25xV_NUMBYTES_LEN]; - uint8_t dataLen; - - dataLen = 0; - - /* Compute Request Data */ - data[dataLen++] = mbPointer; - data[dataLen++] = numBytes; - - return rfalNfcvPollerTransceiveReq( - cmd, flags, RFAL_NFCV_ST_IC_MFG_CODE, uid, data, dataLen, rxBuf, rxBufLen, rcvLen); -} - -/*******************************************************************************/ -static ReturnCode rfalST25xVPollerGenericWriteMessage( - uint8_t cmd, - uint8_t flags, - const uint8_t* uid, - uint8_t msgLen, - const uint8_t* msgData, - uint8_t* txBuf, - uint16_t txBufLen) { - ReturnCode ret; - uint8_t reqFlag; - uint16_t msgIt; - rfalBitRate rxBR; - bool fastMode; - rfalNfcvGenericRes res; - uint16_t rcvLen; - - /* Calculate required Tx buf length: Mfg Code UID MSGLen MSGLen+1 */ - msgIt = - (uint16_t)(msgLen + sizeof(flags) + sizeof(cmd) + 1U + ((uid != NULL) ? RFAL_NFCV_UID_LEN : 0U) + 1U + 1U); - /* Note: MSGlength parameter of the command is the number of Data bytes minus - 1 (00 for 1 byte of data, FFh for 256 bytes of data) */ - - /* Check for valid parameters */ - if((txBuf == NULL) || (msgData == NULL) || (txBufLen < msgIt)) { - return ERR_PARAM; - } - - msgIt = 0; - fastMode = false; - - /* Check if the command is an ST's Fast command */ - if(cmd == (uint8_t)RFAL_NFCV_CMD_FAST_WRITE_MESSAGE) { - /* Store current Rx bit rate and move to fast mode */ - rfalGetBitRate(NULL, &rxBR); - rfalSetBitRate(RFAL_BR_KEEP, RFAL_BR_52p97); - - fastMode = true; - } - - /* Compute Request Command */ - reqFlag = - (uint8_t)(flags & (~((uint32_t)RFAL_NFCV_REQ_FLAG_ADDRESS) & ~((uint32_t)RFAL_NFCV_REQ_FLAG_SELECT))); - reqFlag |= - ((uid != NULL) ? (uint8_t)RFAL_NFCV_REQ_FLAG_ADDRESS : (uint8_t)RFAL_NFCV_REQ_FLAG_SELECT); - - txBuf[msgIt++] = reqFlag; - txBuf[msgIt++] = cmd; - txBuf[msgIt++] = RFAL_NFCV_ST_IC_MFG_CODE; - - if(uid != NULL) { - ST_MEMCPY(&txBuf[msgIt], uid, RFAL_NFCV_UID_LEN); - msgIt += RFAL_NFCV_UID_LEN; - } - txBuf[msgIt++] = msgLen; - ST_MEMCPY( - &txBuf[msgIt], - msgData, - (uint16_t)(msgLen + (uint16_t)1U)); /* Message Data contains (MSGLength + 1) bytes */ - msgIt += (uint16_t)(msgLen + (uint16_t)1U); - - /* Transceive Command */ - ret = rfalTransceiveBlockingTxRx( - txBuf, - msgIt, - (uint8_t*)&res, - sizeof(rfalNfcvGenericRes), - &rcvLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_ST25xV_FDT_POLL_MAX); - - /* Restore Rx BitRate */ - if(fastMode) { - rfalSetBitRate(RFAL_BR_KEEP, rxBR); - } - - if(ret != ERR_NONE) { - return ret; - } - - /* Check if the response minimum length has been received */ - if(rcvLen < (uint8_t)RFAL_NFCV_FLAG_LEN) { - return ERR_PROTO; - } - - /* Check if an error has been signalled */ - if((res.RES_FLAG & (uint8_t)RFAL_NFCV_RES_FLAG_ERROR) != 0U) { - return ERR_PROTO; - } - - return ERR_NONE; -} - -/* -****************************************************************************** -* GLOBAL FUNCTIONS -****************************************************************************** -*/ - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerM24LRReadSingleBlock( - uint8_t flags, - const uint8_t* uid, - uint16_t blockNum, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen) { - uint8_t data[RFAL_NFCV_BLOCKNUM_M24LR_LEN]; - uint8_t dataLen; - - dataLen = 0; - - /* Compute Request Data */ - data[dataLen++] = (uint8_t)blockNum; /* Set M24LR Block Number (16 bits) LSB */ - data[dataLen++] = (uint8_t)(blockNum >> 8U); /* Set M24LR Block Number (16 bits) MSB */ - - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_READ_SINGLE_BLOCK, - (flags | (uint8_t)RFAL_NFCV_REQ_FLAG_PROTOCOL_EXT), - RFAL_NFCV_PARAM_SKIP, - uid, - data, - dataLen, - rxBuf, - rxBufLen, - rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerM24LRWriteSingleBlock( - uint8_t flags, - const uint8_t* uid, - uint16_t blockNum, - const uint8_t* wrData, - uint8_t blockLen) { - uint8_t data[(RFAL_NFCV_BLOCKNUM_M24LR_LEN + RFAL_NFCV_MAX_BLOCK_LEN)]; - uint8_t dataLen; - uint16_t rcvLen; - rfalNfcvGenericRes res; - - /* Check for valid parameters */ - if((blockLen == 0U) || (blockLen > (uint8_t)RFAL_NFCV_MAX_BLOCK_LEN) || (wrData == NULL)) { - return ERR_PARAM; - } - - dataLen = 0U; - - /* Compute Request Data */ - data[dataLen++] = (uint8_t)blockNum; /* Set M24LR Block Number (16 bits) LSB */ - data[dataLen++] = (uint8_t)(blockNum >> 8U); /* Set M24LR Block Number (16 bits) MSB */ - ST_MEMCPY(&data[dataLen], wrData, blockLen); /* Append Block data to write */ - dataLen += blockLen; - - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_WRITE_SINGLE_BLOCK, - (flags | (uint8_t)RFAL_NFCV_REQ_FLAG_PROTOCOL_EXT), - RFAL_NFCV_PARAM_SKIP, - uid, - data, - dataLen, - (uint8_t*)&res, - sizeof(rfalNfcvGenericRes), - &rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerM24LRReadMultipleBlocks( - uint8_t flags, - const uint8_t* uid, - uint16_t firstBlockNum, - uint8_t numOfBlocks, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen) { - uint8_t data[(RFAL_NFCV_BLOCKNUM_M24LR_LEN + RFAL_NFCV_BLOCKNUM_M24LR_LEN)]; - uint8_t dataLen; - - dataLen = 0U; - - /* Compute Request Data */ - data[dataLen++] = (uint8_t)firstBlockNum; /* Set M24LR Block Number (16 bits) LSB */ - data[dataLen++] = (uint8_t)(firstBlockNum >> 8U); /* Set M24LR Block Number (16 bits) MSB */ - data[dataLen++] = numOfBlocks; /* Set number of blocks to read */ - - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_READ_MULTIPLE_BLOCKS, - (flags | (uint8_t)RFAL_NFCV_REQ_FLAG_PROTOCOL_EXT), - RFAL_NFCV_PARAM_SKIP, - uid, - data, - dataLen, - rxBuf, - rxBufLen, - rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerFastReadSingleBlock( - uint8_t flags, - const uint8_t* uid, - uint8_t blockNum, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen) { - uint8_t bn; - - bn = blockNum; - - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_FAST_READ_SINGLE_BLOCK, - flags, - RFAL_NFCV_ST_IC_MFG_CODE, - uid, - &bn, - sizeof(uint8_t), - rxBuf, - rxBufLen, - rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerM24LRFastReadSingleBlock( - uint8_t flags, - const uint8_t* uid, - uint16_t blockNum, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen) { - uint8_t data[RFAL_NFCV_BLOCKNUM_M24LR_LEN]; - uint8_t dataLen; - - dataLen = 0; - - /* Compute Request Data */ - data[dataLen++] = (uint8_t)blockNum; /* Set M24LR Block Number (16 bits) LSB */ - data[dataLen++] = (uint8_t)(blockNum >> 8U); /* Set M24LR Block Number (16 bits) MSB */ - - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_FAST_READ_SINGLE_BLOCK, - (flags | (uint8_t)RFAL_NFCV_REQ_FLAG_PROTOCOL_EXT), - RFAL_NFCV_ST_IC_MFG_CODE, - uid, - data, - dataLen, - rxBuf, - rxBufLen, - rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerM24LRFastReadMultipleBlocks( - uint8_t flags, - const uint8_t* uid, - uint16_t firstBlockNum, - uint8_t numOfBlocks, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen) { - uint8_t data[(RFAL_NFCV_BLOCKNUM_M24LR_LEN + RFAL_NFCV_BLOCKNUM_M24LR_LEN)]; - uint8_t dataLen; - - dataLen = 0U; - - /* Compute Request Data */ - data[dataLen++] = (uint8_t)firstBlockNum; /* Set M24LR Block Number (16 bits) LSB */ - data[dataLen++] = (uint8_t)(firstBlockNum >> 8U); /* Set M24LR Block Number (16 bits) MSB */ - data[dataLen++] = numOfBlocks; /* Set number of blocks to read */ - - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_FAST_READ_MULTIPLE_BLOCKS, - (flags | (uint8_t)RFAL_NFCV_REQ_FLAG_PROTOCOL_EXT), - RFAL_NFCV_ST_IC_MFG_CODE, - uid, - data, - dataLen, - rxBuf, - rxBufLen, - rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerFastReadMultipleBlocks( - uint8_t flags, - const uint8_t* uid, - uint8_t firstBlockNum, - uint8_t numOfBlocks, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen) { - uint8_t data[(RFAL_NFCV_BLOCKNUM_LEN + RFAL_NFCV_BLOCKNUM_LEN)]; - uint8_t dataLen; - - dataLen = 0U; - - /* Compute Request Data */ - data[dataLen++] = firstBlockNum; /* Set first Block Number */ - data[dataLen++] = numOfBlocks; /* Set number of blocks to read */ - - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_FAST_READ_MULTIPLE_BLOCKS, - flags, - RFAL_NFCV_ST_IC_MFG_CODE, - uid, - data, - dataLen, - rxBuf, - rxBufLen, - rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerFastExtendedReadSingleBlock( - uint8_t flags, - const uint8_t* uid, - uint16_t blockNum, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen) { - uint8_t data[RFAL_NFCV_BLOCKNUM_EXTENDED_LEN]; - uint8_t dataLen; - - dataLen = 0U; - - /* Compute Request Data */ - data[dataLen++] = (uint8_t) - blockNum; /* TS T5T 1.0 BNo is considered as a multi-byte field. TS T5T 1.0 5.1.1.13 multi-byte field follows [DIGITAL]. [DIGITAL] 9.3.1 A multiple byte field is transmitted LSB first. */ - data[dataLen++] = (uint8_t)((blockNum >> 8U) & 0xFFU); - - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_FAST_EXTENDED_READ_SINGLE_BLOCK, - flags, - RFAL_NFCV_ST_IC_MFG_CODE, - uid, - data, - dataLen, - rxBuf, - rxBufLen, - rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerFastExtReadMultipleBlocks( - uint8_t flags, - const uint8_t* uid, - uint16_t firstBlockNum, - uint16_t numOfBlocks, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen) { - uint8_t data[(RFAL_NFCV_BLOCKNUM_EXTENDED_LEN + RFAL_NFCV_BLOCKNUM_EXTENDED_LEN)]; - uint8_t dataLen; - - dataLen = 0U; - - /* Compute Request Data */ - data[dataLen++] = (uint8_t)((firstBlockNum >> 0U) & 0xFFU); - data[dataLen++] = (uint8_t)((firstBlockNum >> 8U) & 0xFFU); - data[dataLen++] = (uint8_t)((numOfBlocks >> 0U) & 0xFFU); - data[dataLen++] = (uint8_t)((numOfBlocks >> 8U) & 0xFFU); - - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_FAST_EXTENDED_READ_MULTIPLE_BLOCKS, - flags, - RFAL_NFCV_ST_IC_MFG_CODE, - uid, - data, - dataLen, - rxBuf, - rxBufLen, - rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerReadConfiguration( - uint8_t flags, - const uint8_t* uid, - uint8_t pointer, - uint8_t* regValue) { - return rfalST25xVPollerGenericReadConfiguration( - RFAL_NFCV_CMD_READ_CONFIGURATION, flags, uid, pointer, regValue); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerWriteConfiguration( - uint8_t flags, - const uint8_t* uid, - uint8_t pointer, - uint8_t regValue) { - return rfalST25xVPollerGenericWriteConfiguration( - RFAL_NFCV_CMD_WRITE_CONFIGURATION, flags, uid, pointer, regValue); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerReadDynamicConfiguration( - uint8_t flags, - const uint8_t* uid, - uint8_t pointer, - uint8_t* regValue) { - return rfalST25xVPollerGenericReadConfiguration( - RFAL_NFCV_CMD_READ_DYN_CONFIGURATION, flags, uid, pointer, regValue); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerWriteDynamicConfiguration( - uint8_t flags, - const uint8_t* uid, - uint8_t pointer, - uint8_t regValue) { - return rfalST25xVPollerGenericWriteConfiguration( - RFAL_NFCV_CMD_WRITE_DYN_CONFIGURATION, flags, uid, pointer, regValue); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerFastReadDynamicConfiguration( - uint8_t flags, - const uint8_t* uid, - uint8_t pointer, - uint8_t* regValue) { - return rfalST25xVPollerGenericReadConfiguration( - RFAL_NFCV_CMD_FAST_READ_DYN_CONFIGURATION, flags, uid, pointer, regValue); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerFastWriteDynamicConfiguration( - uint8_t flags, - const uint8_t* uid, - uint8_t pointer, - uint8_t regValue) { - return rfalST25xVPollerGenericWriteConfiguration( - RFAL_NFCV_CMD_FAST_WRITE_DYN_CONFIGURATION, flags, uid, pointer, regValue); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerPresentPassword( - uint8_t flags, - const uint8_t* uid, - uint8_t pwdNum, - const uint8_t* pwd, - uint8_t pwdLen) { - uint8_t data[RFAL_ST25xV_PWDNUM_LEN + RFAL_ST25xV_PWD_LEN]; - uint8_t dataLen; - uint16_t rcvLen; - rfalNfcvGenericRes res; - - if((pwdLen > RFAL_ST25xV_PWD_LEN) || (pwd == NULL)) { - return ERR_PARAM; - } - - dataLen = 0U; - data[dataLen++] = pwdNum; - if(pwdLen > 0U) { - ST_MEMCPY(&data[dataLen], pwd, pwdLen); - } - dataLen += pwdLen; - - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_PRESENT_PASSWORD, - flags, - RFAL_NFCV_ST_IC_MFG_CODE, - uid, - data, - dataLen, - (uint8_t*)&res, - sizeof(rfalNfcvGenericRes), - &rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerGetRandomNumber( - uint8_t flags, - const uint8_t* uid, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen) { - rfalFieldOff(); - platformDelay(RFAL_ST25TV02K_TRF_OFF); - rfalNfcvPollerInitialize(); - rfalFieldOnAndStartGT(); - platformDelay(RFAL_ST25TV02K_TBOOT_RF); - return rfalNfcvPollerTransceiveReq( - RFAL_NFCV_CMD_GET_RANDOM_NUMBER, - flags, - RFAL_NFCV_ST_IC_MFG_CODE, - uid, - NULL, - 0U, - rxBuf, - rxBufLen, - rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerWriteMessage( - uint8_t flags, - const uint8_t* uid, - uint8_t msgLen, - const uint8_t* msgData, - uint8_t* txBuf, - uint16_t txBufLen) { - return rfalST25xVPollerGenericWriteMessage( - RFAL_NFCV_CMD_WRITE_MESSAGE, flags, uid, msgLen, msgData, txBuf, txBufLen); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerFastWriteMessage( - uint8_t flags, - const uint8_t* uid, - uint8_t msgLen, - const uint8_t* msgData, - uint8_t* txBuf, - uint16_t txBufLen) { - return rfalST25xVPollerGenericWriteMessage( - RFAL_NFCV_CMD_FAST_WRITE_MESSAGE, flags, uid, msgLen, msgData, txBuf, txBufLen); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerReadMessageLength(uint8_t flags, const uint8_t* uid, uint8_t* msgLen) { - return rfalST25xVPollerGenericReadMessageLength( - RFAL_NFCV_CMD_READ_MESSAGE_LENGTH, flags, uid, msgLen); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerFastReadMsgLength(uint8_t flags, const uint8_t* uid, uint8_t* msgLen) { - return rfalST25xVPollerGenericReadMessageLength( - RFAL_NFCV_CMD_FAST_READ_MESSAGE_LENGTH, flags, uid, msgLen); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerReadMessage( - uint8_t flags, - const uint8_t* uid, - uint8_t mbPointer, - uint8_t numBytes, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen) { - return rfalST25xVPollerGenericReadMessage( - RFAL_NFCV_CMD_READ_MESSAGE, flags, uid, mbPointer, numBytes, rxBuf, rxBufLen, rcvLen); -} - -/*******************************************************************************/ -ReturnCode rfalST25xVPollerFastReadMessage( - uint8_t flags, - const uint8_t* uid, - uint8_t mbPointer, - uint8_t numBytes, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rcvLen) { - return rfalST25xVPollerGenericReadMessage( - RFAL_NFCV_CMD_FAST_READ_MESSAGE, flags, uid, mbPointer, numBytes, rxBuf, rxBufLen, rcvLen); -} - -#endif /* RFAL_FEATURE_ST25xV */ diff --git a/lib/ST25RFAL002/source/rfal_t1t.c b/lib/ST25RFAL002/source/rfal_t1t.c deleted file mode 100644 index be990efcd2e..00000000000 --- a/lib/ST25RFAL002/source/rfal_t1t.c +++ /dev/null @@ -1,233 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_t1t.c - * - * \author Gustavo Patricio - * - * \brief Provides NFC-A T1T convenience methods and definitions - * - * This module provides an interface to perform as a NFC-A Reader/Writer - * to handle a Type 1 Tag T1T (Topaz) - * - */ - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "rfal_t1t.h" -#include "utils.h" - -/* - ****************************************************************************** - * ENABLE SWITCH - ****************************************************************************** - */ - -#ifndef RFAL_FEATURE_T1T -#define RFAL_FEATURE_T1T false /* T1T module configuration missing. Disabled by default */ -#endif - -#if RFAL_FEATURE_T1T - -/* - ****************************************************************************** - * GLOBAL DEFINES - ****************************************************************************** - */ - -#define RFAL_T1T_DRD_READ \ - (1236U * 2U) /*!< DRD for Reads with n=9 => 1236/fc ~= 91 us T1T 1.2 4.4.2 */ -#define RFAL_T1T_DRD_WRITE \ - 36052U /*!< DRD for Write with n=281 => 36052/fc ~= 2659 us T1T 1.2 4.4.2 */ -#define RFAL_T1T_DRD_WRITE_E \ - 70996U /*!< DRD for Write/Erase with n=554 => 70996/fc ~= 5236 us T1T 1.2 4.4.2 */ - -#define RFAL_T1T_RID_RES_HR0_VAL \ - 0x10U /*!< HR0 indicating NDEF support Digital 2.0 (Candidate) 11.6.2.1 */ -#define RFAL_T1T_RID_RES_HR0_MASK \ - 0xF0U /*!< HR0 most significant nibble mask */ - -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ - -/*! NFC-A T1T (Topaz) RID_REQ Digital 1.1 10.6.1 & Table 49 */ -typedef struct { - uint8_t cmd; /*!< T1T cmd: RID */ - uint8_t add; /*!< ADD: undefined value */ - uint8_t data; /*!< DATA: undefined value */ - uint8_t uid[RFAL_T1T_UID_LEN]; /*!< UID-echo: undefined value */ -} rfalT1TRidReq; - -/*! NFC-A T1T (Topaz) RALL_REQ T1T 1.2 Table 4 */ -typedef struct { - uint8_t cmd; /*!< T1T cmd: RALL */ - uint8_t add1; /*!< ADD: 0x00 */ - uint8_t add0; /*!< ADD: 0x00 */ - uint8_t uid[RFAL_T1T_UID_LEN]; /*!< UID */ -} rfalT1TRallReq; - -/*! NFC-A T1T (Topaz) WRITE_REQ T1T 1.2 Table 4 */ -typedef struct { - uint8_t cmd; /*!< T1T cmd: RALL */ - uint8_t add; /*!< ADD */ - uint8_t data; /*!< DAT */ - uint8_t uid[RFAL_T1T_UID_LEN]; /*!< UID */ -} rfalT1TWriteReq; - -/*! NFC-A T1T (Topaz) WRITE_RES T1T 1.2 Table 4 */ -typedef struct { - uint8_t add; /*!< ADD */ - uint8_t data; /*!< DAT */ -} rfalT1TWriteRes; - -/* -****************************************************************************** -* LOCAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -/* -****************************************************************************** -* GLOBAL FUNCTIONS -****************************************************************************** -*/ - -ReturnCode rfalT1TPollerInitialize(void) { - ReturnCode ret; - - EXIT_ON_ERR(ret, rfalSetMode(RFAL_MODE_POLL_NFCA_T1T, RFAL_BR_106, RFAL_BR_106)); - rfalSetErrorHandling(RFAL_ERRORHANDLING_NFC); - - rfalSetGT( - RFAL_GT_NONE); /* T1T should only be initialized after NFC-A mode, therefore the GT has been fulfilled */ - rfalSetFDTListen( - RFAL_FDT_LISTEN_NFCA_POLLER); /* T1T uses NFC-A FDT Listen with n=9 Digital 1.1 10.7.2 */ - rfalSetFDTPoll(RFAL_FDT_POLL_NFCA_T1T_POLLER); - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalT1TPollerRid(rfalT1TRidRes* ridRes) { - ReturnCode ret; - rfalT1TRidReq ridReq; - uint16_t rcvdLen; - - if(ridRes == NULL) { - return ERR_PARAM; - } - - /* Compute RID command and set Undefined Values to 0x00 Digital 1.1 10.6.1 */ - ST_MEMSET(&ridReq, 0x00, sizeof(rfalT1TRidReq)); - ridReq.cmd = (uint8_t)RFAL_T1T_CMD_RID; - - EXIT_ON_ERR( - ret, - rfalTransceiveBlockingTxRx( - (uint8_t*)&ridReq, - sizeof(rfalT1TRidReq), - (uint8_t*)ridRes, - sizeof(rfalT1TRidRes), - &rcvdLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_T1T_DRD_READ)); - - /* Check expected RID response length and the HR0 Digital 2.0 (Candidate) 11.6.2.1 */ - if((rcvdLen != sizeof(rfalT1TRidRes)) || - ((ridRes->hr0 & RFAL_T1T_RID_RES_HR0_MASK) != RFAL_T1T_RID_RES_HR0_VAL)) { - return ERR_PROTO; - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode - rfalT1TPollerRall(const uint8_t* uid, uint8_t* rxBuf, uint16_t rxBufLen, uint16_t* rxRcvdLen) { - rfalT1TRallReq rallReq; - - if((rxBuf == NULL) || (uid == NULL) || (rxRcvdLen == NULL)) { - return ERR_PARAM; - } - - /* Compute RALL command and set Add to 0x00 */ - ST_MEMSET(&rallReq, 0x00, sizeof(rfalT1TRallReq)); - rallReq.cmd = (uint8_t)RFAL_T1T_CMD_RALL; - ST_MEMCPY(rallReq.uid, uid, RFAL_T1T_UID_LEN); - - return rfalTransceiveBlockingTxRx( - (uint8_t*)&rallReq, - sizeof(rfalT1TRallReq), - (uint8_t*)rxBuf, - rxBufLen, - rxRcvdLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_T1T_DRD_READ); -} - -/*******************************************************************************/ -ReturnCode rfalT1TPollerWrite(const uint8_t* uid, uint8_t address, uint8_t data) { - rfalT1TWriteReq writeReq; - rfalT1TWriteRes writeRes; - uint16_t rxRcvdLen; - ReturnCode err; - - if(uid == NULL) { - return ERR_PARAM; - } - - writeReq.cmd = (uint8_t)RFAL_T1T_CMD_WRITE_E; - writeReq.add = address; - writeReq.data = data; - ST_MEMCPY(writeReq.uid, uid, RFAL_T1T_UID_LEN); - - err = rfalTransceiveBlockingTxRx( - (uint8_t*)&writeReq, - sizeof(rfalT1TWriteReq), - (uint8_t*)&writeRes, - sizeof(rfalT1TWriteRes), - &rxRcvdLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_T1T_DRD_WRITE_E); - - if(err == ERR_NONE) { - if((writeReq.add != writeRes.add) || (writeReq.data != writeRes.data) || - (rxRcvdLen != sizeof(rfalT1TWriteRes))) { - return ERR_PROTO; - } - } - return err; -} - -#endif /* RFAL_FEATURE_T1T */ diff --git a/lib/ST25RFAL002/source/rfal_t2t.c b/lib/ST25RFAL002/source/rfal_t2t.c deleted file mode 100644 index 2837898a7a5..00000000000 --- a/lib/ST25RFAL002/source/rfal_t2t.c +++ /dev/null @@ -1,253 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_t2t.c - * - * \author - * - * \brief Provides NFC-A T2T convenience methods and definitions - * - * This module provides an interface to perform as a NFC-A Reader/Writer - * to handle a Type 2 Tag T2T - * - */ - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "rfal_t2t.h" -#include "utils.h" - -/* - ****************************************************************************** - * ENABLE SWITCH - ****************************************************************************** - */ - -#ifndef RFAL_FEATURE_T2T -#define RFAL_FEATURE_T2T false /* T2T module configuration missing. Disabled by default */ -#endif - -#if RFAL_FEATURE_T2T - -/* - ****************************************************************************** - * GLOBAL DEFINES - ****************************************************************************** - */ -#define RFAL_FDT_POLL_READ_MAX \ - rfalConvMsTo1fc( \ - 5U) /*!< Maximum Wait time for Read command as defined in TS T2T 1.0 table 18 */ -#define RFAL_FDT_POLL_WRITE_MAX \ - rfalConvMsTo1fc( \ - 10U) /*!< Maximum Wait time for Write command as defined in TS T2T 1.0 table 18 */ -#define RFAL_FDT_POLL_SL_MAX \ - rfalConvMsTo1fc( \ - 1U) /*!< Maximum Wait time for Sector Select as defined in TS T2T 1.0 table 18 */ -#define RFAL_T2T_ACK_NACK_LEN \ - 1U /*!< Len of NACK in bytes (4 bits) */ -#define RFAL_T2T_ACK \ - 0x0AU /*!< ACK value */ -#define RFAL_T2T_ACK_MASK \ - 0x0FU /*!< ACK value */ - -#define RFAL_T2T_SECTOR_SELECT_P1_BYTE2 \ - 0xFFU /*!< Sector Select Packet 1 byte 2 */ -#define RFAL_T2T_SECTOR_SELECT_P2_RFU_LEN \ - 3U /*!< Sector Select RFU length */ - -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ - -/*! NFC-A T2T command set T2T 1.0 5.1 */ -typedef enum { - RFAL_T2T_CMD_READ = 0x30, /*!< T2T Read */ - RFAL_T2T_CMD_WRITE = 0xA2, /*!< T2T Write */ - RFAL_T2T_CMD_SECTOR_SELECT = 0xC2 /*!< T2T Sector Select */ -} rfalT2Tcmds; - -/*! NFC-A T2T READ T2T 1.0 5.2 and table 11 */ -typedef struct { - uint8_t code; /*!< Command code */ - uint8_t blNo; /*!< Block number */ -} rfalT2TReadReq; - -/*! NFC-A T2T WRITE T2T 1.0 5.3 and table 12 */ -typedef struct { - uint8_t code; /*!< Command code */ - uint8_t blNo; /*!< Block number */ - uint8_t data[RFAL_T2T_WRITE_DATA_LEN]; /*!< Data */ -} rfalT2TWriteReq; - -/*! NFC-A T2T SECTOR SELECT Packet 1 T2T 1.0 5.4 and table 13 */ -typedef struct { - uint8_t code; /*!< Command code */ - uint8_t byte2; /*!< Sector Select Packet 1 byte 2 */ -} rfalT2TSectorSelectP1Req; - -/*! NFC-A T2T SECTOR SELECT Packet 2 T2T 1.0 5.4 and table 13 */ -typedef struct { - uint8_t secNo; /*!< Block number */ - uint8_t rfu[RFAL_T2T_SECTOR_SELECT_P2_RFU_LEN]; /*!< Sector Select Packet RFU */ -} rfalT2TSectorSelectP2Req; - -/* - ****************************************************************************** - * GLOBAL FUNCTIONS - ****************************************************************************** - */ - -ReturnCode - rfalT2TPollerRead(uint8_t blockNum, uint8_t* rxBuf, uint16_t rxBufLen, uint16_t* rcvLen) { - ReturnCode ret; - rfalT2TReadReq req; - - if((rxBuf == NULL) || (rcvLen == NULL)) { - return ERR_PARAM; - } - - req.code = (uint8_t)RFAL_T2T_CMD_READ; - req.blNo = blockNum; - - /* Transceive Command */ - ret = rfalTransceiveBlockingTxRx( - (uint8_t*)&req, - sizeof(rfalT2TReadReq), - rxBuf, - rxBufLen, - rcvLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_FDT_POLL_READ_MAX); - - /* T2T 1.0 5.2.1.7 The Reader/Writer SHALL treat a NACK in response to a READ Command as a Protocol Error */ - if((ret == ERR_INCOMPLETE_BYTE) && (*rcvLen == RFAL_T2T_ACK_NACK_LEN) && - ((*rxBuf & RFAL_T2T_ACK_MASK) != RFAL_T2T_ACK)) { - return ERR_PROTO; - } - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalT2TPollerWrite(uint8_t blockNum, const uint8_t* wrData) { - ReturnCode ret; - rfalT2TWriteReq req; - uint8_t res; - uint16_t rxLen; - - req.code = (uint8_t)RFAL_T2T_CMD_WRITE; - req.blNo = blockNum; - ST_MEMCPY(req.data, wrData, RFAL_T2T_WRITE_DATA_LEN); - - /* Transceive WRITE Command */ - ret = rfalTransceiveBlockingTxRx( - (uint8_t*)&req, - sizeof(rfalT2TWriteReq), - &res, - sizeof(uint8_t), - &rxLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_FDT_POLL_READ_MAX); - - /* Check for a valid ACK */ - if((ret == ERR_INCOMPLETE_BYTE) || (ret == ERR_NONE)) { - ret = ERR_PROTO; - - if((rxLen == RFAL_T2T_ACK_NACK_LEN) && ((res & RFAL_T2T_ACK_MASK) == RFAL_T2T_ACK)) { - ret = ERR_NONE; - } - } - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalT2TPollerSectorSelect(uint8_t sectorNum) { - rfalT2TSectorSelectP1Req p1Req; - rfalT2TSectorSelectP2Req p2Req; - ReturnCode ret; - uint8_t res; - uint16_t rxLen; - - /* Compute SECTOR SELECT Packet 1 */ - p1Req.code = (uint8_t)RFAL_T2T_CMD_SECTOR_SELECT; - p1Req.byte2 = RFAL_T2T_SECTOR_SELECT_P1_BYTE2; - - /* Transceive SECTOR SELECT Packet 1 */ - ret = rfalTransceiveBlockingTxRx( - (uint8_t*)&p1Req, - sizeof(rfalT2TSectorSelectP1Req), - &res, - sizeof(uint8_t), - &rxLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_FDT_POLL_SL_MAX); - - /* Check and report any transmission error */ - if((ret != ERR_INCOMPLETE_BYTE) && (ret != ERR_NONE)) { - return ret; - } - - /* Ensure that an ACK was received */ - if((ret != ERR_INCOMPLETE_BYTE) || (rxLen != RFAL_T2T_ACK_NACK_LEN) || - ((res & RFAL_T2T_ACK_MASK) != RFAL_T2T_ACK)) { - return ERR_PROTO; - } - - /* Compute SECTOR SELECT Packet 2 */ - p2Req.secNo = sectorNum; - ST_MEMSET(&p2Req.rfu, 0x00, RFAL_T2T_SECTOR_SELECT_P2_RFU_LEN); - - /* Transceive SECTOR SELECT Packet 2 */ - ret = rfalTransceiveBlockingTxRx( - (uint8_t*)&p2Req, - sizeof(rfalT2TSectorSelectP2Req), - &res, - sizeof(uint8_t), - &rxLen, - RFAL_TXRX_FLAGS_DEFAULT, - RFAL_FDT_POLL_SL_MAX); - - /* T2T 1.0 5.4.1.14 The Reader/Writer SHALL treat any response received before the end of PATT2T,SL,MAX as a Protocol Error */ - if((ret == ERR_NONE) || (ret == ERR_INCOMPLETE_BYTE)) { - return ERR_PROTO; - } - - /* T2T 1.0 5.4.1.13 The Reader/Writer SHALL treat the transmission of the SECTOR SELECT Command Packet 2 as being successful when it receives no response until PATT2T,SL,MAX. */ - if(ret == ERR_TIMEOUT) { - return ERR_NONE; - } - - return ret; -} - -#endif /* RFAL_FEATURE_T2T */ diff --git a/lib/ST25RFAL002/source/rfal_t4t.c b/lib/ST25RFAL002/source/rfal_t4t.c deleted file mode 100644 index 8592daf8fbb..00000000000 --- a/lib/ST25RFAL002/source/rfal_t4t.c +++ /dev/null @@ -1,397 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_t4t.h - * - * \author Gustavo Patricio - * - * \brief Provides convenience methods and definitions for T4T (ISO7816-4) - * - * This module provides an interface to exchange T4T APDUs according to - * NFC Forum T4T and ISO7816-4 - * - * This implementation was based on the following specs: - * - ISO/IEC 7816-4 3rd Edition 2013-04-15 - * - NFC Forum T4T Technical Specification 1.0 2017-08-28 - * - */ - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "rfal_t4t.h" -#include "utils.h" - -/* - ****************************************************************************** - * ENABLE SWITCH - ****************************************************************************** - */ - -#ifndef RFAL_FEATURE_T4T -#define RFAL_FEATURE_T4T false /* T4T module configuration missing. Disabled by default */ -#endif - -#if RFAL_FEATURE_T4T - -/* - ****************************************************************************** - * GLOBAL DEFINES - ****************************************************************************** - */ -#define RFAL_T4T_OFFSET_DO 0x54U /*!< Tag value for offset BER-TLV data object */ -#define RFAL_T4T_LENGTH_DO 0x03U /*!< Len value for offset BER-TLV data object */ -#define RFAL_T4T_DATA_DO 0x53U /*!< Tag value for data BER-TLV data object */ - -#define RFAL_T4T_MAX_LC 255U /*!< Maximum Lc value for short Lc coding */ -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ - -/* -****************************************************************************** -* GLOBAL MACROS -****************************************************************************** -*/ - -/* - ****************************************************************************** - * LOCAL VARIABLES - ****************************************************************************** - */ - -/* - ****************************************************************************** - * GLOBAL FUNCTIONS - ****************************************************************************** - */ - -/*******************************************************************************/ -ReturnCode rfalT4TPollerComposeCAPDU(const rfalT4tCApduParam* apduParam) { - uint8_t hdrLen; - uint16_t msgIt; - - if((apduParam == NULL) || (apduParam->cApduBuf == NULL) || (apduParam->cApduLen == NULL)) { - return ERR_PARAM; - } - - msgIt = 0; - *(apduParam->cApduLen) = 0; - - /*******************************************************************************/ - /* Compute Command-APDU according to the format T4T 1.0 5.1.2 & ISO7816-4 2013 Table 1 */ - - /* Check if Data is present */ - if(apduParam->LcFlag) { - if(apduParam->Lc == 0U) { - /* Extented field coding not supported */ - return ERR_PARAM; - } - - /* Check whether requested Lc fits */ -#pragma GCC diagnostic ignored "-Wtype-limits" - if((uint16_t)apduParam->Lc > - (uint16_t)(RFAL_FEATURE_ISO_DEP_APDU_MAX_LEN - RFAL_T4T_LE_LEN)) { - return ERR_PARAM; /* PRQA S 2880 # MISRA 2.1 - Unreachable code due to configuration option being set/unset */ - } - - /* Calculate the header length a place the data/body where it should be */ - hdrLen = RFAL_T4T_MAX_CAPDU_PROLOGUE_LEN + RFAL_T4T_LC_LEN; - - /* make sure not to exceed buffer size */ - if(((uint16_t)hdrLen + (uint16_t)apduParam->Lc + - (apduParam->LeFlag ? RFAL_T4T_LC_LEN : 0U)) > RFAL_FEATURE_ISO_DEP_APDU_MAX_LEN) { - return ERR_NOMEM; /* PRQA S 2880 # MISRA 2.1 - Unreachable code due to configuration option being set/unset */ - } - ST_MEMMOVE(&apduParam->cApduBuf->apdu[hdrLen], apduParam->cApduBuf->apdu, apduParam->Lc); - } - - /* Prepend the ADPDU's header */ - apduParam->cApduBuf->apdu[msgIt++] = apduParam->CLA; - apduParam->cApduBuf->apdu[msgIt++] = apduParam->INS; - apduParam->cApduBuf->apdu[msgIt++] = apduParam->P1; - apduParam->cApduBuf->apdu[msgIt++] = apduParam->P2; - - /* Check if Data field length is to be added */ - if(apduParam->LcFlag) { - apduParam->cApduBuf->apdu[msgIt++] = apduParam->Lc; - msgIt += apduParam->Lc; - } - - /* Check if Expected Response Length is to be added */ - if(apduParam->LeFlag) { - apduParam->cApduBuf->apdu[msgIt++] = apduParam->Le; - } - - *(apduParam->cApduLen) = msgIt; - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalT4TPollerParseRAPDU(rfalT4tRApduParam* apduParam) { - if((apduParam == NULL) || (apduParam->rApduBuf == NULL)) { - return ERR_PARAM; - } - - if(apduParam->rcvdLen < RFAL_T4T_MAX_RAPDU_SW1SW2_LEN) { - return ERR_PROTO; - } - - apduParam->rApduBodyLen = (apduParam->rcvdLen - (uint16_t)RFAL_T4T_MAX_RAPDU_SW1SW2_LEN); - apduParam->statusWord = GETU16((&apduParam->rApduBuf->apdu[apduParam->rApduBodyLen])); - - /* Check SW1 SW2 T4T 1.0 5.1.3 NOTE */ - if(apduParam->statusWord == RFAL_T4T_ISO7816_STATUS_COMPLETE) { - return ERR_NONE; - } - - return ERR_REQUEST; -} - -/*******************************************************************************/ -ReturnCode rfalT4TPollerComposeSelectAppl( - rfalIsoDepApduBufFormat* cApduBuf, - const uint8_t* aid, - uint8_t aidLen, - uint16_t* cApduLen) { - rfalT4tCApduParam cAPDU; - - /* CLA INS P1 P2 Lc Data Le */ - /* 00h A4h 00h 00h 07h AID 00h */ - cAPDU.CLA = RFAL_T4T_CLA; - cAPDU.INS = (uint8_t)RFAL_T4T_INS_SELECT; - cAPDU.P1 = RFAL_T4T_ISO7816_P1_SELECT_BY_DF_NAME; - cAPDU.P2 = RFAL_T4T_ISO7816_P2_SELECT_FIRST_OR_ONLY_OCCURENCE | - RFAL_T4T_ISO7816_P2_SELECT_RETURN_FCI_TEMPLATE; - cAPDU.Lc = aidLen; - cAPDU.Le = 0x00; - cAPDU.LcFlag = true; - cAPDU.LeFlag = true; - cAPDU.cApduBuf = cApduBuf; - cAPDU.cApduLen = cApduLen; - - if(aidLen > 0U) { - ST_MEMCPY(cAPDU.cApduBuf->apdu, aid, aidLen); - } - - return rfalT4TPollerComposeCAPDU(&cAPDU); -} - -/*******************************************************************************/ -ReturnCode rfalT4TPollerComposeSelectFile( - rfalIsoDepApduBufFormat* cApduBuf, - const uint8_t* fid, - uint8_t fidLen, - uint16_t* cApduLen) { - rfalT4tCApduParam cAPDU; - - /* CLA INS P1 P2 Lc Data Le */ - /* 00h A4h 00h 0Ch 02h FID - */ - cAPDU.CLA = RFAL_T4T_CLA; - cAPDU.INS = (uint8_t)RFAL_T4T_INS_SELECT; - cAPDU.P1 = RFAL_T4T_ISO7816_P1_SELECT_BY_FILEID; - cAPDU.P2 = RFAL_T4T_ISO7816_P2_SELECT_FIRST_OR_ONLY_OCCURENCE | - RFAL_T4T_ISO7816_P2_SELECT_NO_RESPONSE_DATA; - cAPDU.Lc = fidLen; - cAPDU.Le = 0x00; - cAPDU.LcFlag = true; - cAPDU.LeFlag = false; - cAPDU.cApduBuf = cApduBuf; - cAPDU.cApduLen = cApduLen; - - if(fidLen > 0U) { - ST_MEMCPY(cAPDU.cApduBuf->apdu, fid, fidLen); - } - - return rfalT4TPollerComposeCAPDU(&cAPDU); -} - -/*******************************************************************************/ -ReturnCode rfalT4TPollerComposeSelectFileV1Mapping( - rfalIsoDepApduBufFormat* cApduBuf, - const uint8_t* fid, - uint8_t fidLen, - uint16_t* cApduLen) { - rfalT4tCApduParam cAPDU; - - /* CLA INS P1 P2 Lc Data Le */ - /* 00h A4h 00h 00h 02h FID - */ - cAPDU.CLA = RFAL_T4T_CLA; - cAPDU.INS = (uint8_t)RFAL_T4T_INS_SELECT; - cAPDU.P1 = RFAL_T4T_ISO7816_P1_SELECT_BY_FILEID; - cAPDU.P2 = RFAL_T4T_ISO7816_P2_SELECT_FIRST_OR_ONLY_OCCURENCE | - RFAL_T4T_ISO7816_P2_SELECT_RETURN_FCI_TEMPLATE; - cAPDU.Lc = fidLen; - cAPDU.Le = 0x00; - cAPDU.LcFlag = true; - cAPDU.LeFlag = false; - cAPDU.cApduBuf = cApduBuf; - cAPDU.cApduLen = cApduLen; - - if(fidLen > 0U) { - ST_MEMCPY(cAPDU.cApduBuf->apdu, fid, fidLen); - } - - return rfalT4TPollerComposeCAPDU(&cAPDU); -} - -/*******************************************************************************/ -ReturnCode rfalT4TPollerComposeReadData( - rfalIsoDepApduBufFormat* cApduBuf, - uint16_t offset, - uint8_t expLen, - uint16_t* cApduLen) { - rfalT4tCApduParam cAPDU; - - /* CLA INS P1 P2 Lc Data Le */ - /* 00h B0h [Offset] - - len */ - cAPDU.CLA = RFAL_T4T_CLA; - cAPDU.INS = (uint8_t)RFAL_T4T_INS_READBINARY; - cAPDU.P1 = (uint8_t)((offset >> 8U) & 0xFFU); - cAPDU.P2 = (uint8_t)((offset >> 0U) & 0xFFU); - cAPDU.Le = expLen; - cAPDU.LcFlag = false; - cAPDU.LeFlag = true; - cAPDU.cApduBuf = cApduBuf; - cAPDU.cApduLen = cApduLen; - - return rfalT4TPollerComposeCAPDU(&cAPDU); -} - -/*******************************************************************************/ -ReturnCode rfalT4TPollerComposeReadDataODO( - rfalIsoDepApduBufFormat* cApduBuf, - uint32_t offset, - uint8_t expLen, - uint16_t* cApduLen) { - rfalT4tCApduParam cAPDU; - uint8_t dataIt; - - /* CLA INS P1 P2 Lc Data Le */ - /* 00h B1h 00h 00h Lc 54 03 xxyyzz len */ - /* [Offset] */ - cAPDU.CLA = RFAL_T4T_CLA; - cAPDU.INS = (uint8_t)RFAL_T4T_INS_READBINARY_ODO; - cAPDU.P1 = 0x00U; - cAPDU.P2 = 0x00U; - cAPDU.Le = expLen; - cAPDU.LcFlag = true; - cAPDU.LeFlag = true; - cAPDU.cApduBuf = cApduBuf; - cAPDU.cApduLen = cApduLen; - - dataIt = 0U; - cApduBuf->apdu[dataIt++] = RFAL_T4T_OFFSET_DO; - cApduBuf->apdu[dataIt++] = RFAL_T4T_LENGTH_DO; - cApduBuf->apdu[dataIt++] = (uint8_t)(offset >> 16U); - cApduBuf->apdu[dataIt++] = (uint8_t)(offset >> 8U); - cApduBuf->apdu[dataIt++] = (uint8_t)(offset); - cAPDU.Lc = dataIt; - - return rfalT4TPollerComposeCAPDU(&cAPDU); -} - -/*******************************************************************************/ -ReturnCode rfalT4TPollerComposeWriteData( - rfalIsoDepApduBufFormat* cApduBuf, - uint16_t offset, - const uint8_t* data, - uint8_t dataLen, - uint16_t* cApduLen) { - rfalT4tCApduParam cAPDU; - - /* CLA INS P1 P2 Lc Data Le */ - /* 00h D6h [Offset] len Data - */ - cAPDU.CLA = RFAL_T4T_CLA; - cAPDU.INS = (uint8_t)RFAL_T4T_INS_UPDATEBINARY; - cAPDU.P1 = (uint8_t)((offset >> 8U) & 0xFFU); - cAPDU.P2 = (uint8_t)((offset >> 0U) & 0xFFU); - cAPDU.Lc = dataLen; - cAPDU.LcFlag = true; - cAPDU.LeFlag = false; - cAPDU.cApduBuf = cApduBuf; - cAPDU.cApduLen = cApduLen; - - if(dataLen > 0U) { - ST_MEMCPY(cAPDU.cApduBuf->apdu, data, dataLen); - } - - return rfalT4TPollerComposeCAPDU(&cAPDU); -} - -/*******************************************************************************/ -ReturnCode rfalT4TPollerComposeWriteDataODO( - rfalIsoDepApduBufFormat* cApduBuf, - uint32_t offset, - const uint8_t* data, - uint8_t dataLen, - uint16_t* cApduLen) { - rfalT4tCApduParam cAPDU; - uint8_t dataIt; - - /* CLA INS P1 P2 Lc Data Le */ - /* 00h D7h 00h 00h len 54 03 xxyyzz 53 Ld data - */ - /* [offset] [data] */ - cAPDU.CLA = RFAL_T4T_CLA; - cAPDU.INS = (uint8_t)RFAL_T4T_INS_UPDATEBINARY_ODO; - cAPDU.P1 = 0x00U; - cAPDU.P2 = 0x00U; - cAPDU.LcFlag = true; - cAPDU.LeFlag = false; - cAPDU.cApduBuf = cApduBuf; - cAPDU.cApduLen = cApduLen; - - dataIt = 0U; - cApduBuf->apdu[dataIt++] = RFAL_T4T_OFFSET_DO; - cApduBuf->apdu[dataIt++] = RFAL_T4T_LENGTH_DO; - cApduBuf->apdu[dataIt++] = (uint8_t)(offset >> 16U); - cApduBuf->apdu[dataIt++] = (uint8_t)(offset >> 8U); - cApduBuf->apdu[dataIt++] = (uint8_t)(offset); - cApduBuf->apdu[dataIt++] = RFAL_T4T_DATA_DO; - cApduBuf->apdu[dataIt++] = dataLen; - - if((((uint32_t)dataLen + (uint32_t)dataIt) >= RFAL_T4T_MAX_LC) || - (((uint32_t)dataLen + (uint32_t)dataIt) >= RFAL_FEATURE_ISO_DEP_APDU_MAX_LEN)) { - return (ERR_NOMEM); - } - - if(dataLen > 0U) { - ST_MEMCPY(&cAPDU.cApduBuf->apdu[dataIt], data, dataLen); - } - dataIt += dataLen; - cAPDU.Lc = dataIt; - - return rfalT4TPollerComposeCAPDU(&cAPDU); -} - -#endif /* RFAL_FEATURE_T4T */ diff --git a/lib/ST25RFAL002/source/st25r3916/rfal_analogConfigTbl.h b/lib/ST25RFAL002/source/st25r3916/rfal_analogConfigTbl.h deleted file mode 100644 index 554d6539a64..00000000000 --- a/lib/ST25RFAL002/source/st25r3916/rfal_analogConfigTbl.h +++ /dev/null @@ -1,1477 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file rfal_analogConfig.h - * - * \author bkam - * - * \brief ST25R3916 Analog Configuration Settings - * - */ - -#ifndef ST25R3916_ANALOGCONFIG_H -#define ST25R3916_ANALOGCONFIG_H - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "rfal_analogConfig.h" -#include "st25r3916_com.h" - -/* - ****************************************************************************** - * DEFINES - ****************************************************************************** - */ - -/* - ****************************************************************************** - * GLOBAL MACROS - ****************************************************************************** - */ - -/*! Macro for Configuration Setting with only one register-mask-value set: - * - Configuration ID[2], Number of Register sets to follow[1], Register[2], Mask[1], Value[1] */ -#define MODE_ENTRY_1_REG(MODE, R0, M0, V0) \ - (uint8_t)((uint16_t)(MODE) >> 8U), (uint8_t)((MODE)&0xFFU), 1, \ - (uint8_t)((uint16_t)(R0) >> 8U), (uint8_t)((R0)&0xFFU), (uint8_t)(M0), (uint8_t)(V0) - -/*! Macro for Configuration Setting with only two register-mask-value sets: - * - Configuration ID[2], Number of Register sets to follow[1], Register[2], Mask[1], Value[1], Register[2], Mask[1], Value[1] */ -#define MODE_ENTRY_2_REG(MODE, R0, M0, V0, R1, M1, V1) \ - (uint8_t)((uint16_t)(MODE) >> 8U), (uint8_t)((MODE)&0xFFU), 2, \ - (uint8_t)((uint16_t)(R0) >> 8U), (uint8_t)((R0)&0xFFU), (uint8_t)(M0), (uint8_t)(V0), \ - (uint8_t)((uint16_t)(R1) >> 8U), (uint8_t)((R1)&0xFFU), (uint8_t)(M1), (uint8_t)(V1) - -/*! Macro for Configuration Setting with only three register-mask-value sets: - * - Configuration ID[2], Number of Register sets to follow[1], Register[2], Mask[1], Value[1], Register[2], Mask[1], Value[1], Register[2]... */ -#define MODE_ENTRY_3_REG(MODE, R0, M0, V0, R1, M1, V1, R2, M2, V2) \ - (uint8_t)((uint16_t)(MODE) >> 8U), (uint8_t)((MODE)&0xFFU), 3, \ - (uint8_t)((uint16_t)(R0) >> 8U), (uint8_t)((R0)&0xFFU), (uint8_t)(M0), (uint8_t)(V0), \ - (uint8_t)((uint16_t)(R1) >> 8U), (uint8_t)((R1)&0xFFU), (uint8_t)(M1), (uint8_t)(V1), \ - (uint8_t)((uint16_t)(R2) >> 8U), (uint8_t)((R2)&0xFFU), (uint8_t)(M2), (uint8_t)(V2) - -/*! Macro for Configuration Setting with only four register-mask-value sets: - * - Configuration ID[2], Number of Register sets to follow[1], Register[2], Mask[1], Value[1], Register[2], Mask[1], Value[1], Register[2]... */ -#define MODE_ENTRY_4_REG(MODE, R0, M0, V0, R1, M1, V1, R2, M2, V2, R3, M3, V3) \ - (uint8_t)((uint16_t)(MODE) >> 8U), (uint8_t)((MODE)&0xFFU), 4, \ - (uint8_t)((uint16_t)(R0) >> 8U), (uint8_t)((R0)&0xFFU), (uint8_t)(M0), (uint8_t)(V0), \ - (uint8_t)((uint16_t)(R1) >> 8U), (uint8_t)((R1)&0xFFU), (uint8_t)(M1), (uint8_t)(V1), \ - (uint8_t)((uint16_t)(R2) >> 8U), (uint8_t)((R2)&0xFFU), (uint8_t)(M2), (uint8_t)(V2), \ - (uint8_t)((uint16_t)(R3) >> 8U), (uint8_t)((R3)&0xFFU), (uint8_t)(M3), (uint8_t)(V3) - -/*! Macro for Configuration Setting with only five register-mask-value sets: - * - Configuration ID[2], Number of Register sets to follow[1], Register[2], Mask[1], Value[1], Register[2], Mask[1], Value[1], Register[2]... */ -#define MODE_ENTRY_5_REG(MODE, R0, M0, V0, R1, M1, V1, R2, M2, V2, R3, M3, V3, R4, M4, V4) \ - (uint8_t)((uint16_t)(MODE) >> 8U), (uint8_t)((MODE)&0xFFU), 5, \ - (uint8_t)((uint16_t)(R0) >> 8U), (uint8_t)((R0)&0xFFU), (uint8_t)(M0), (uint8_t)(V0), \ - (uint8_t)((uint16_t)(R1) >> 8U), (uint8_t)((R1)&0xFFU), (uint8_t)(M1), (uint8_t)(V1), \ - (uint8_t)((uint16_t)(R2) >> 8U), (uint8_t)((R2)&0xFFU), (uint8_t)(M2), (uint8_t)(V2), \ - (uint8_t)((uint16_t)(R3) >> 8U), (uint8_t)((R3)&0xFFU), (uint8_t)(M3), (uint8_t)(V3), \ - (uint8_t)((uint16_t)(R4) >> 8U), (uint8_t)((R4)&0xFFU), (uint8_t)(M4), (uint8_t)(V4) - -/*! Macro for Configuration Setting with only six register-mask-value sets: - * - Configuration ID[2], Number of Register sets to follow[1], Register[2], Mask[1], Value[1], Register[2], Mask[1], Value[1], Register[2]... */ -#define MODE_ENTRY_6_REG( \ - MODE, R0, M0, V0, R1, M1, V1, R2, M2, V2, R3, M3, V3, R4, M4, V4, R5, M5, V5) \ - (uint8_t)((uint16_t)(MODE) >> 8U), (uint8_t)((MODE)&0xFFU), 6, \ - (uint8_t)((uint16_t)(R0) >> 8U), (uint8_t)((R0)&0xFFU), (uint8_t)(M0), (uint8_t)(V0), \ - (uint8_t)((uint16_t)(R1) >> 8U), (uint8_t)((R1)&0xFFU), (uint8_t)(M1), (uint8_t)(V1), \ - (uint8_t)((uint16_t)(R2) >> 8U), (uint8_t)((R2)&0xFFU), (uint8_t)(M2), (uint8_t)(V2), \ - (uint8_t)((uint16_t)(R3) >> 8U), (uint8_t)((R3)&0xFFU), (uint8_t)(M3), (uint8_t)(V3), \ - (uint8_t)((uint16_t)(R4) >> 8U), (uint8_t)((R4)&0xFFU), (uint8_t)(M4), (uint8_t)(V4), \ - (uint8_t)((uint16_t)(R5) >> 8U), (uint8_t)((R5)&0xFFU), (uint8_t)(M5), (uint8_t)(V5) - -/*! Macro for Configuration Setting with only seven register-mask-value sets: - * - Configuration ID[2], Number of Register sets to follow[1], Register[2], Mask[1], Value[1], Register[2], Mask[1], Value[1], Register[2]... */ -#define MODE_ENTRY_7_REG( \ - MODE, R0, M0, V0, R1, M1, V1, R2, M2, V2, R3, M3, V3, R4, M4, V4, R5, M5, V5, R6, M6, V6) \ - (uint8_t)((uint16_t)(MODE) >> 8U), (uint8_t)((MODE)&0xFFU), 7, \ - (uint8_t)((uint16_t)(R0) >> 8U), (uint8_t)((R0)&0xFFU), (uint8_t)(M0), (uint8_t)(V0), \ - (uint8_t)((uint16_t)(R1) >> 8U), (uint8_t)((R1)&0xFFU), (uint8_t)(M1), (uint8_t)(V1), \ - (uint8_t)((uint16_t)(R2) >> 8U), (uint8_t)((R2)&0xFFU), (uint8_t)(M2), (uint8_t)(V2), \ - (uint8_t)((uint16_t)(R3) >> 8U), (uint8_t)((R3)&0xFFU), (uint8_t)(M3), (uint8_t)(V3), \ - (uint8_t)((uint16_t)(R4) >> 8U), (uint8_t)((R4)&0xFFU), (uint8_t)(M4), (uint8_t)(V4), \ - (uint8_t)((uint16_t)(R5) >> 8U), (uint8_t)((R5)&0xFFU), (uint8_t)(M5), (uint8_t)(V5), \ - (uint8_t)((uint16_t)(R6) >> 8U), (uint8_t)((R6)&0xFFU), (uint8_t)(M6), (uint8_t)(V6) - -/*! Macro for Configuration Setting with only eight register-mask-value sets: - * - Configuration ID[2], Number of Register sets to follow[1], Register[2], Mask[1], Value[1], Register[2], Mask[1], Value[1], Register[2]... */ -#define MODE_ENTRY_8_REG( \ - MODE, \ - R0, \ - M0, \ - V0, \ - R1, \ - M1, \ - V1, \ - R2, \ - M2, \ - V2, \ - R3, \ - M3, \ - V3, \ - R4, \ - M4, \ - V4, \ - R5, \ - M5, \ - V5, \ - R6, \ - M6, \ - V6, \ - R7, \ - M7, \ - V7) \ - (uint8_t)((uint16_t)(MODE) >> 8U), (uint8_t)((MODE)&0xFFU), 8, \ - (uint8_t)((uint16_t)(R0) >> 8U), (uint8_t)((R0)&0xFFU), (uint8_t)(M0), (uint8_t)(V0), \ - (uint8_t)((uint16_t)(R1) >> 8U), (uint8_t)((R1)&0xFFU), (uint8_t)(M1), (uint8_t)(V1), \ - (uint8_t)((uint16_t)(R2) >> 8U), (uint8_t)((R2)&0xFFU), (uint8_t)(M2), (uint8_t)(V2), \ - (uint8_t)((uint16_t)(R3) >> 8U), (uint8_t)((R3)&0xFFU), (uint8_t)(M3), (uint8_t)(V3), \ - (uint8_t)((uint16_t)(R4) >> 8U), (uint8_t)((R4)&0xFFU), (uint8_t)(M4), (uint8_t)(V4), \ - (uint8_t)((uint16_t)(R5) >> 8U), (uint8_t)((R5)&0xFFU), (uint8_t)(M5), (uint8_t)(V5), \ - (uint8_t)((uint16_t)(R6) >> 8U), (uint8_t)((R6)&0xFFU), (uint8_t)(M6), (uint8_t)(V6), \ - (uint8_t)((uint16_t)(R7) >> 8U), (uint8_t)((R7)&0xFFU), (uint8_t)(M7), (uint8_t)(V7) - -/*! Macro for Configuration Setting with only nine register-mask-value sets: - * - Configuration ID[2], Number of Register sets to follow[1], Register[2], Mask[1], Value[1], Register[2], Mask[1], Value[1], Register[2]... */ -#define MODE_ENTRY_9_REG( \ - MODE, \ - R0, \ - M0, \ - V0, \ - R1, \ - M1, \ - V1, \ - R2, \ - M2, \ - V2, \ - R3, \ - M3, \ - V3, \ - R4, \ - M4, \ - V4, \ - R5, \ - M5, \ - V5, \ - R6, \ - M6, \ - V6, \ - R7, \ - M7, \ - V7, \ - R8, \ - M8, \ - V8) \ - (uint8_t)((uint16_t)(MODE) >> 8U), (uint8_t)((MODE)&0xFFU), 9, \ - (uint8_t)((uint16_t)(R0) >> 8U), (uint8_t)((R0)&0xFFU), (uint8_t)(M0), (uint8_t)(V0), \ - (uint8_t)((uint16_t)(R1) >> 8U), (uint8_t)((R1)&0xFFU), (uint8_t)(M1), (uint8_t)(V1), \ - (uint8_t)((uint16_t)(R2) >> 8U), (uint8_t)((R2)&0xFFU), (uint8_t)(M2), (uint8_t)(V2), \ - (uint8_t)((uint16_t)(R3) >> 8U), (uint8_t)((R3)&0xFFU), (uint8_t)(M3), (uint8_t)(V3), \ - (uint8_t)((uint16_t)(R4) >> 8U), (uint8_t)((R4)&0xFFU), (uint8_t)(M4), (uint8_t)(V4), \ - (uint8_t)((uint16_t)(R5) >> 8U), (uint8_t)((R5)&0xFFU), (uint8_t)(M5), (uint8_t)(V5), \ - (uint8_t)((uint16_t)(R6) >> 8U), (uint8_t)((R6)&0xFFU), (uint8_t)(M6), (uint8_t)(V6), \ - (uint8_t)((uint16_t)(R7) >> 8U), (uint8_t)((R7)&0xFFU), (uint8_t)(M7), (uint8_t)(V7), \ - (uint8_t)((uint16_t)(R8) >> 8U), (uint8_t)((R8)&0xFFU), (uint8_t)(M8), (uint8_t)(V8) - -/*! Macro for Configuration Setting with only ten register-mask-value sets: - * - Configuration ID[2], Number of Register sets to follow[1], Register[2], Mask[1], Value[1], Register[2], Mask[1], Value[1], Register[2]... */ -#define MODE_ENTRY_10_REG( \ - MODE, \ - R0, \ - M0, \ - V0, \ - R1, \ - M1, \ - V1, \ - R2, \ - M2, \ - V2, \ - R3, \ - M3, \ - V3, \ - R4, \ - M4, \ - V4, \ - R5, \ - M5, \ - V5, \ - R6, \ - M6, \ - V6, \ - R7, \ - M7, \ - V7, \ - R8, \ - M8, \ - V8, \ - R9, \ - M9, \ - V9) \ - (uint8_t)((uint16_t)(MODE) >> 8U), (uint8_t)((MODE)&0xFFU), 10, \ - (uint8_t)((uint16_t)(R0) >> 8U), (uint8_t)((R0)&0xFFU), (uint8_t)(M0), (uint8_t)(V0), \ - (uint8_t)((uint16_t)(R1) >> 8U), (uint8_t)((R1)&0xFFU), (uint8_t)(M1), (uint8_t)(V1), \ - (uint8_t)((uint16_t)(R2) >> 8U), (uint8_t)((R2)&0xFFU), (uint8_t)(M2), (uint8_t)(V2), \ - (uint8_t)((uint16_t)(R3) >> 8U), (uint8_t)((R3)&0xFFU), (uint8_t)(M3), (uint8_t)(V3), \ - (uint8_t)((uint16_t)(R4) >> 8U), (uint8_t)((R4)&0xFFU), (uint8_t)(M4), (uint8_t)(V4), \ - (uint8_t)((uint16_t)(R5) >> 8U), (uint8_t)((R5)&0xFFU), (uint8_t)(M5), (uint8_t)(V5), \ - (uint8_t)((uint16_t)(R6) >> 8U), (uint8_t)((R6)&0xFFU), (uint8_t)(M6), (uint8_t)(V6), \ - (uint8_t)((uint16_t)(R7) >> 8U), (uint8_t)((R7)&0xFFU), (uint8_t)(M7), (uint8_t)(V7), \ - (uint8_t)((uint16_t)(R8) >> 8U), (uint8_t)((R8)&0xFFU), (uint8_t)(M8), (uint8_t)(V8), \ - (uint8_t)((uint16_t)(R9) >> 8U), (uint8_t)((R9)&0xFFU), (uint8_t)(M9), (uint8_t)(V9) - -/*! Macro for Configuration Setting with eleven register-mask-value sets: - * - Configuration ID[2], Number of Register sets to follow[1], Register[2], Mask[1], Value[1], Register[2], Mask[1], Value[1], Register[2]... */ -#define MODE_ENTRY_11_REG( \ - MODE, \ - R0, \ - M0, \ - V0, \ - R1, \ - M1, \ - V1, \ - R2, \ - M2, \ - V2, \ - R3, \ - M3, \ - V3, \ - R4, \ - M4, \ - V4, \ - R5, \ - M5, \ - V5, \ - R6, \ - M6, \ - V6, \ - R7, \ - M7, \ - V7, \ - R8, \ - M8, \ - V8, \ - R9, \ - M9, \ - V9, \ - R10, \ - M10, \ - V10) \ - (uint8_t)((uint16_t)(MODE) >> 8U), (uint8_t)((MODE)&0xFFU), 11, \ - (uint8_t)((uint16_t)(R0) >> 8U), (uint8_t)((R0)&0xFFU), (uint8_t)(M0), (uint8_t)(V0), \ - (uint8_t)((uint16_t)(R1) >> 8U), (uint8_t)((R1)&0xFFU), (uint8_t)(M1), (uint8_t)(V1), \ - (uint8_t)((uint16_t)(R2) >> 8U), (uint8_t)((R2)&0xFFU), (uint8_t)(M2), (uint8_t)(V2), \ - (uint8_t)((uint16_t)(R3) >> 8U), (uint8_t)((R3)&0xFFU), (uint8_t)(M3), (uint8_t)(V3), \ - (uint8_t)((uint16_t)(R4) >> 8U), (uint8_t)((R4)&0xFFU), (uint8_t)(M4), (uint8_t)(V4), \ - (uint8_t)((uint16_t)(R5) >> 8U), (uint8_t)((R5)&0xFFU), (uint8_t)(M5), (uint8_t)(V5), \ - (uint8_t)((uint16_t)(R6) >> 8U), (uint8_t)((R6)&0xFFU), (uint8_t)(M6), (uint8_t)(V6), \ - (uint8_t)((uint16_t)(R7) >> 8U), (uint8_t)((R7)&0xFFU), (uint8_t)(M7), (uint8_t)(V7), \ - (uint8_t)((uint16_t)(R8) >> 8U), (uint8_t)((R8)&0xFFU), (uint8_t)(M8), (uint8_t)(V8), \ - (uint8_t)((uint16_t)(R9) >> 8U), (uint8_t)((R9)&0xFFU), (uint8_t)(M9), (uint8_t)(V9), \ - (uint8_t)((uint16_t)(R10) >> 8U), (uint8_t)((R10)&0xFFU), (uint8_t)(M10), (uint8_t)(V10) - -/*! Macro for Configuration Setting with twelve register-mask-value sets: - * - Configuration ID[2], Number of Register sets to follow[1], Register[2], Mask[1], Value[1], Register[2], Mask[1], Value[1], Register[2]... */ -#define MODE_ENTRY_12_REG( \ - MODE, \ - R0, \ - M0, \ - V0, \ - R1, \ - M1, \ - V1, \ - R2, \ - M2, \ - V2, \ - R3, \ - M3, \ - V3, \ - R4, \ - M4, \ - V4, \ - R5, \ - M5, \ - V5, \ - R6, \ - M6, \ - V6, \ - R7, \ - M7, \ - V7, \ - R8, \ - M8, \ - V8, \ - R9, \ - M9, \ - V9, \ - R10, \ - M10, \ - V10, \ - R11, \ - M11, \ - V11) \ - (uint8_t)((uint16_t)(MODE) >> 8U), (uint8_t)((MODE)&0xFFU), 12, \ - (uint8_t)((uint16_t)(R0) >> 8U), (uint8_t)((R0)&0xFFU), (uint8_t)(M0), (uint8_t)(V0), \ - (uint8_t)((uint16_t)(R1) >> 8U), (uint8_t)((R1)&0xFFU), (uint8_t)(M1), (uint8_t)(V1), \ - (uint8_t)((uint16_t)(R2) >> 8U), (uint8_t)((R2)&0xFFU), (uint8_t)(M2), (uint8_t)(V2), \ - (uint8_t)((uint16_t)(R3) >> 8U), (uint8_t)((R3)&0xFFU), (uint8_t)(M3), (uint8_t)(V3), \ - (uint8_t)((uint16_t)(R4) >> 8U), (uint8_t)((R4)&0xFFU), (uint8_t)(M4), (uint8_t)(V4), \ - (uint8_t)((uint16_t)(R5) >> 8U), (uint8_t)((R5)&0xFFU), (uint8_t)(M5), (uint8_t)(V5), \ - (uint8_t)((uint16_t)(R6) >> 8U), (uint8_t)((R6)&0xFFU), (uint8_t)(M6), (uint8_t)(V6), \ - (uint8_t)((uint16_t)(R7) >> 8U), (uint8_t)((R7)&0xFFU), (uint8_t)(M7), (uint8_t)(V7), \ - (uint8_t)((uint16_t)(R8) >> 8U), (uint8_t)((R8)&0xFFU), (uint8_t)(M8), (uint8_t)(V8), \ - (uint8_t)((uint16_t)(R9) >> 8U), (uint8_t)((R9)&0xFFU), (uint8_t)(M9), (uint8_t)(V9), \ - (uint8_t)((uint16_t)(R10) >> 8U), (uint8_t)((R10)&0xFFU), (uint8_t)(M10), (uint8_t)(V10), \ - (uint8_t)((uint16_t)(R11) >> 8U), (uint8_t)((R11)&0xFFU), (uint8_t)(M11), (uint8_t)(V11) - -/*! Macro for Configuration Setting with thirteen register-mask-value sets: - * - Configuration ID[2], Number of Register sets to follow[1], Register[2], Mask[1], Value[1], Register[2], Mask[1], Value[1], Register[2]... */ -#define MODE_ENTRY_13_REG( \ - MODE, \ - R0, \ - M0, \ - V0, \ - R1, \ - M1, \ - V1, \ - R2, \ - M2, \ - V2, \ - R3, \ - M3, \ - V3, \ - R4, \ - M4, \ - V4, \ - R5, \ - M5, \ - V5, \ - R6, \ - M6, \ - V6, \ - R7, \ - M7, \ - V7, \ - R8, \ - M8, \ - V8, \ - R9, \ - M9, \ - V9, \ - R10, \ - M10, \ - V10, \ - R11, \ - M11, \ - V11, \ - R12, \ - M12, \ - V12) \ - (uint8_t)((uint16_t)(MODE) >> 8U), (uint8_t)((MODE)&0xFFU), 13, \ - (uint8_t)((uint16_t)(R0) >> 8U), (uint8_t)((R0)&0xFFU), (uint8_t)(M0), (uint8_t)(V0), \ - (uint8_t)((uint16_t)(R1) >> 8U), (uint8_t)((R1)&0xFFU), (uint8_t)(M1), (uint8_t)(V1), \ - (uint8_t)((uint16_t)(R2) >> 8U), (uint8_t)((R2)&0xFFU), (uint8_t)(M2), (uint8_t)(V2), \ - (uint8_t)((uint16_t)(R3) >> 8U), (uint8_t)((R3)&0xFFU), (uint8_t)(M3), (uint8_t)(V3), \ - (uint8_t)((uint16_t)(R4) >> 8U), (uint8_t)((R4)&0xFFU), (uint8_t)(M4), (uint8_t)(V4), \ - (uint8_t)((uint16_t)(R5) >> 8U), (uint8_t)((R5)&0xFFU), (uint8_t)(M5), (uint8_t)(V5), \ - (uint8_t)((uint16_t)(R6) >> 8U), (uint8_t)((R6)&0xFFU), (uint8_t)(M6), (uint8_t)(V6), \ - (uint8_t)((uint16_t)(R7) >> 8U), (uint8_t)((R7)&0xFFU), (uint8_t)(M7), (uint8_t)(V7), \ - (uint8_t)((uint16_t)(R8) >> 8U), (uint8_t)((R8)&0xFFU), (uint8_t)(M8), (uint8_t)(V8), \ - (uint8_t)((uint16_t)(R9) >> 8U), (uint8_t)((R9)&0xFFU), (uint8_t)(M9), (uint8_t)(V9), \ - (uint8_t)((uint16_t)(R10) >> 8U), (uint8_t)((R10)&0xFFU), (uint8_t)(M10), (uint8_t)(V10), \ - (uint8_t)((uint16_t)(R11) >> 8U), (uint8_t)((R11)&0xFFU), (uint8_t)(M11), (uint8_t)(V11), \ - (uint8_t)((uint16_t)(R12) >> 8U), (uint8_t)((R12)&0xFFU), (uint8_t)(M12), (uint8_t)(V12) - -/*! Macro for Configuration Setting with fourteen register-mask-value sets: - * - Configuration ID[2], Number of Register sets to follow[1], Register[2], Mask[1], Value[1], Register[2], Mask[1], Value[1], Register[2]... */ -#define MODE_ENTRY_14_REG( \ - MODE, \ - R0, \ - M0, \ - V0, \ - R1, \ - M1, \ - V1, \ - R2, \ - M2, \ - V2, \ - R3, \ - M3, \ - V3, \ - R4, \ - M4, \ - V4, \ - R5, \ - M5, \ - V5, \ - R6, \ - M6, \ - V6, \ - R7, \ - M7, \ - V7, \ - R8, \ - M8, \ - V8, \ - R9, \ - M9, \ - V9, \ - R10, \ - M10, \ - V10, \ - R11, \ - M11, \ - V11, \ - R12, \ - M12, \ - V12, \ - R13, \ - M13, \ - V13, \ - R14, \ - M14, \ - V14, \ - R15, \ - M15, \ - V15) \ - (uint8_t)((uint16_t)(MODE) >> 8), (uint8_t)((MODE)&0xFFU), 14, \ - (uint8_t)((uint16_t)(R0) >> 8), (uint8_t)((R0)&0xFFU), (uint8_t)(M0), (uint8_t)(V0), \ - (uint8_t)((uint16_t)(R1) >> 8), (uint8_t)((R1)&0xFFU), (uint8_t)(M1), (uint8_t)(V1), \ - (uint8_t)((uint16_t)(R2) >> 8), (uint8_t)((R2)&0xFFU), (uint8_t)(M2), (uint8_t)(V2), \ - (uint8_t)((uint16_t)(R3) >> 8), (uint8_t)((R3)&0xFFU), (uint8_t)(M3), (uint8_t)(V3), \ - (uint8_t)((uint16_t)(R4) >> 8), (uint8_t)((R4)&0xFFU), (uint8_t)(M4), (uint8_t)(V4), \ - (uint8_t)((uint16_t)(R5) >> 8), (uint8_t)((R5)&0xFFU), (uint8_t)(M5), (uint8_t)(V5), \ - (uint8_t)((uint16_t)(R6) >> 8), (uint8_t)((R6)&0xFFU), (uint8_t)(M6), (uint8_t)(V6), \ - (uint8_t)((uint16_t)(R7) >> 8), (uint8_t)((R7)&0xFFU), (uint8_t)(M7), (uint8_t)(V7), \ - (uint8_t)((uint16_t)(R8) >> 8), (uint8_t)((R8)&0xFFU), (uint8_t)(M8), (uint8_t)(V8), \ - (uint8_t)((uint16_t)(R9) >> 8), (uint8_t)((R9)&0xFFU), (uint8_t)(M9), (uint8_t)(V9), \ - (uint8_t)((uint16_t)(R10) >> 8), (uint8_t)((R10)&0xFFU), (uint8_t)(M10), (uint8_t)(V10), \ - (uint8_t)((uint16_t)(R11) >> 8), (uint8_t)((R11)&0xFFU), (uint8_t)(M11), (uint8_t)(V11), \ - (uint8_t)((uint16_t)(R12) >> 8), (uint8_t)((R12)&0xFFU), (uint8_t)(M12), (uint8_t)(V12), \ - (uint8_t)((uint16_t)(R13) >> 8), (uint8_t)((R13)&0xFFU), (uint8_t)(M13), (uint8_t)(V13) - -/*! Macro for Configuration Setting with fifteen register-mask-value sets: - * - Configuration ID[2], Number of Register sets to follow[1], Register[2], Mask[1], Value[1], Register[2], Mask[1], Value[1], Register[2]... */ -#define MODE_ENTRY_15_REG( \ - MODE, \ - R0, \ - M0, \ - V0, \ - R1, \ - M1, \ - V1, \ - R2, \ - M2, \ - V2, \ - R3, \ - M3, \ - V3, \ - R4, \ - M4, \ - V4, \ - R5, \ - M5, \ - V5, \ - R6, \ - M6, \ - V6, \ - R7, \ - M7, \ - V7, \ - R8, \ - M8, \ - V8, \ - R9, \ - M9, \ - V9, \ - R10, \ - M10, \ - V10, \ - R11, \ - M11, \ - V11, \ - R12, \ - M12, \ - V12, \ - R13, \ - M13, \ - V13, \ - R14, \ - M14, \ - V14, \ - R15, \ - M15, \ - V15) \ - (uint8_t)((uint16_t)(MODE) >> 8), (uint8_t)((MODE)&0xFFU), 15, \ - (uint8_t)((uint16_t)(R0) >> 8), (uint8_t)((R0)&0xFFU), (uint8_t)(M0), (uint8_t)(V0), \ - (uint8_t)((uint16_t)(R1) >> 8), (uint8_t)((R1)&0xFFU), (uint8_t)(M1), (uint8_t)(V1), \ - (uint8_t)((uint16_t)(R2) >> 8), (uint8_t)((R2)&0xFFU), (uint8_t)(M2), (uint8_t)(V2), \ - (uint8_t)((uint16_t)(R3) >> 8), (uint8_t)((R3)&0xFFU), (uint8_t)(M3), (uint8_t)(V3), \ - (uint8_t)((uint16_t)(R4) >> 8), (uint8_t)((R4)&0xFFU), (uint8_t)(M4), (uint8_t)(V4), \ - (uint8_t)((uint16_t)(R5) >> 8), (uint8_t)((R5)&0xFFU), (uint8_t)(M5), (uint8_t)(V5), \ - (uint8_t)((uint16_t)(R6) >> 8), (uint8_t)((R6)&0xFFU), (uint8_t)(M6), (uint8_t)(V6), \ - (uint8_t)((uint16_t)(R7) >> 8), (uint8_t)((R7)&0xFFU), (uint8_t)(M7), (uint8_t)(V7), \ - (uint8_t)((uint16_t)(R8) >> 8), (uint8_t)((R8)&0xFFU), (uint8_t)(M8), (uint8_t)(V8), \ - (uint8_t)((uint16_t)(R9) >> 8), (uint8_t)((R9)&0xFFU), (uint8_t)(M9), (uint8_t)(V9), \ - (uint8_t)((uint16_t)(R10) >> 8), (uint8_t)((R10)&0xFFU), (uint8_t)(M10), (uint8_t)(V10), \ - (uint8_t)((uint16_t)(R11) >> 8), (uint8_t)((R11)&0xFFU), (uint8_t)(M11), (uint8_t)(V11), \ - (uint8_t)((uint16_t)(R12) >> 8), (uint8_t)((R12)&0xFFU), (uint8_t)(M12), (uint8_t)(V12), \ - (uint8_t)((uint16_t)(R13) >> 8), (uint8_t)((R13)&0xFFU), (uint8_t)(M13), (uint8_t)(V13), \ - (uint8_t)((uint16_t)(R14) >> 8), (uint8_t)((R14)&0xFFU), (uint8_t)(M14), (uint8_t)(V14) - -/*! Macro for Configuration Setting with sixteen register-mask-value sets: - * - Configuration ID[2], Number of Register sets to follow[1], Register[2], Mask[1], Value[1], Register[2], Mask[1], Value[1], Register[2]... */ -#define MODE_ENTRY_16_REG( \ - MODE, \ - R0, \ - M0, \ - V0, \ - R1, \ - M1, \ - V1, \ - R2, \ - M2, \ - V2, \ - R3, \ - M3, \ - V3, \ - R4, \ - M4, \ - V4, \ - R5, \ - M5, \ - V5, \ - R6, \ - M6, \ - V6, \ - R7, \ - M7, \ - V7, \ - R8, \ - M8, \ - V8, \ - R9, \ - M9, \ - V9, \ - R10, \ - M10, \ - V10, \ - R11, \ - M11, \ - V11, \ - R12, \ - M12, \ - V12, \ - R13, \ - M13, \ - V13, \ - R14, \ - M14, \ - V14, \ - R15, \ - M15, \ - V15) \ - (uint8_t)((uint16_t)(MODE) >> 8), (uint8_t)((MODE)&0xFFU), 16, \ - (uint8_t)((uint16_t)(R0) >> 8), (uint8_t)((R0)&0xFFU), (uint8_t)(M0), (uint8_t)(V0), \ - (uint8_t)((uint16_t)(R1) >> 8), (uint8_t)((R1)&0xFFU), (uint8_t)(M1), (uint8_t)(V1), \ - (uint8_t)((uint16_t)(R2) >> 8), (uint8_t)((R2)&0xFFU), (uint8_t)(M2), (uint8_t)(V2), \ - (uint8_t)((uint16_t)(R3) >> 8), (uint8_t)((R3)&0xFFU), (uint8_t)(M3), (uint8_t)(V3), \ - (uint8_t)((uint16_t)(R4) >> 8), (uint8_t)((R4)&0xFFU), (uint8_t)(M4), (uint8_t)(V4), \ - (uint8_t)((uint16_t)(R5) >> 8), (uint8_t)((R5)&0xFFU), (uint8_t)(M5), (uint8_t)(V5), \ - (uint8_t)((uint16_t)(R6) >> 8), (uint8_t)((R6)&0xFFU), (uint8_t)(M6), (uint8_t)(V6), \ - (uint8_t)((uint16_t)(R7) >> 8), (uint8_t)((R7)&0xFFU), (uint8_t)(M7), (uint8_t)(V7), \ - (uint8_t)((uint16_t)(R8) >> 8), (uint8_t)((R8)&0xFFU), (uint8_t)(M8), (uint8_t)(V8), \ - (uint8_t)((uint16_t)(R9) >> 8), (uint8_t)((R9)&0xFFU), (uint8_t)(M9), (uint8_t)(V9), \ - (uint8_t)((uint16_t)(R10) >> 8), (uint8_t)((R10)&0xFFU), (uint8_t)(M10), (uint8_t)(V10), \ - (uint8_t)((uint16_t)(R11) >> 8), (uint8_t)((R11)&0xFFU), (uint8_t)(M11), (uint8_t)(V11), \ - (uint8_t)((uint16_t)(R12) >> 8), (uint8_t)((R12)&0xFFU), (uint8_t)(M12), (uint8_t)(V12), \ - (uint8_t)((uint16_t)(R13) >> 8), (uint8_t)((R13)&0xFFU), (uint8_t)(M13), (uint8_t)(V13), \ - (uint8_t)((uint16_t)(R14) >> 8), (uint8_t)((R14)&0xFFU), (uint8_t)(M14), (uint8_t)(V14), \ - (uint8_t)((uint16_t)(R15) >> 8), (uint8_t)((R15)&0xFFU), (uint8_t)(M15), (uint8_t)(V15) - -/*! Macro for Configuration Setting with seventeen register-mask-value sets: - * - Configuration ID[2], Number of Register sets to follow[1], Register[2], Mask[1], Value[1], Register[2], Mask[1], Value[1], Register[2]... */ -#define MODE_ENTRY_17_REG( \ - MODE, \ - R0, \ - M0, \ - V0, \ - R1, \ - M1, \ - V1, \ - R2, \ - M2, \ - V2, \ - R3, \ - M3, \ - V3, \ - R4, \ - M4, \ - V4, \ - R5, \ - M5, \ - V5, \ - R6, \ - M6, \ - V6, \ - R7, \ - M7, \ - V7, \ - R8, \ - M8, \ - V8, \ - R9, \ - M9, \ - V9, \ - R10, \ - M10, \ - V10, \ - R11, \ - M11, \ - V11, \ - R12, \ - M12, \ - V12, \ - R13, \ - M13, \ - V13, \ - R14, \ - M14, \ - V14, \ - R15, \ - M15, \ - V15, \ - R16, \ - M16, \ - V16) \ - (uint8_t)((uint16_t)(MODE) >> 8), (uint8_t)((MODE)&0xFFU), 17, \ - (uint8_t)((uint16_t)(R0) >> 8), (uint8_t)((R0)&0xFFU), (uint8_t)(M0), (uint8_t)(V0), \ - (uint8_t)((uint16_t)(R1) >> 8), (uint8_t)((R1)&0xFFU), (uint8_t)(M1), (uint8_t)(V1), \ - (uint8_t)((uint16_t)(R2) >> 8), (uint8_t)((R2)&0xFFU), (uint8_t)(M2), (uint8_t)(V2), \ - (uint8_t)((uint16_t)(R3) >> 8), (uint8_t)((R3)&0xFFU), (uint8_t)(M3), (uint8_t)(V3), \ - (uint8_t)((uint16_t)(R4) >> 8), (uint8_t)((R4)&0xFFU), (uint8_t)(M4), (uint8_t)(V4), \ - (uint8_t)((uint16_t)(R5) >> 8), (uint8_t)((R5)&0xFFU), (uint8_t)(M5), (uint8_t)(V5), \ - (uint8_t)((uint16_t)(R6) >> 8), (uint8_t)((R6)&0xFFU), (uint8_t)(M6), (uint8_t)(V6), \ - (uint8_t)((uint16_t)(R7) >> 8), (uint8_t)((R7)&0xFFU), (uint8_t)(M7), (uint8_t)(V7), \ - (uint8_t)((uint16_t)(R8) >> 8), (uint8_t)((R8)&0xFFU), (uint8_t)(M8), (uint8_t)(V8), \ - (uint8_t)((uint16_t)(R9) >> 8), (uint8_t)((R9)&0xFFU), (uint8_t)(M9), (uint8_t)(V9), \ - (uint8_t)((uint16_t)(R10) >> 8), (uint8_t)((R10)&0xFFU), (uint8_t)(M10), (uint8_t)(V10), \ - (uint8_t)((uint16_t)(R11) >> 8), (uint8_t)((R11)&0xFFU), (uint8_t)(M11), (uint8_t)(V11), \ - (uint8_t)((uint16_t)(R12) >> 8), (uint8_t)((R12)&0xFFU), (uint8_t)(M12), (uint8_t)(V12), \ - (uint8_t)((uint16_t)(R13) >> 8), (uint8_t)((R13)&0xFFU), (uint8_t)(M13), (uint8_t)(V13), \ - (uint8_t)((uint16_t)(R14) >> 8), (uint8_t)((R14)&0xFFU), (uint8_t)(M14), (uint8_t)(V14), \ - (uint8_t)((uint16_t)(R15) >> 8), (uint8_t)((R15)&0xFFU), (uint8_t)(M15), (uint8_t)(V15), \ - (uint8_t)((uint16_t)(R16) >> 8), (uint8_t)((R16)&0xFFU), (uint8_t)(M16), (uint8_t)(V16) -/* - ****************************************************************************** - * GLOBAL DATA TYPES - ****************************************************************************** - */ -/* PRQA S 3406 1 # MISRA 8.6 - Externally generated table included by the library */ /* PRQA S 1514 1 # MISRA 8.9 - Externally generated table included by the library */ -const uint8_t rfalAnalogConfigDefaultSettings[] = { - - /****** Default Analog Configuration for Chip-Specific Reset ******/ - MODE_ENTRY_17_REG( - (RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_INIT), - ST25R3916_REG_IO_CONF1, - (ST25R3916_REG_IO_CONF1_out_cl_mask | ST25R3916_REG_IO_CONF1_lf_clk_off), - 0x07 /* Disable MCU_CLK */ - , - ST25R3916_REG_IO_CONF2, - (ST25R3916_REG_IO_CONF2_miso_pd1 | ST25R3916_REG_IO_CONF2_miso_pd2), - 0x18 /* SPI Pull downs */ - , - ST25R3916_REG_IO_CONF2, - ST25R3916_REG_IO_CONF2_aat_en, - ST25R3916_REG_IO_CONF2_aat_en /* Enable AAT */ - , - ST25R3916_REG_TX_DRIVER, - ST25R3916_REG_TX_DRIVER_d_res_mask, - 0x00 /* Set RFO resistance Active Tx */ - , - ST25R3916_REG_RES_AM_MOD, - 0xFF, - 0x80 /* Use minimum non-overlap */ - , - ST25R3916_REG_FIELD_THRESHOLD_ACTV, - ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_mask, - ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_105mV /* Lower activation threshold (higher than deactivation)*/ - , - ST25R3916_REG_FIELD_THRESHOLD_ACTV, - ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_mask, - ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_105mV /* Lower activation threshold (higher than deactivation)*/ - , - ST25R3916_REG_FIELD_THRESHOLD_DEACTV, - ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_mask, - ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_75mV /* Lower deactivation threshold */ - , - ST25R3916_REG_FIELD_THRESHOLD_DEACTV, - ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_mask, - ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_75mV /* Lower deactivation threshold */ - , - ST25R3916_REG_AUX_MOD, - ST25R3916_REG_AUX_MOD_lm_ext, - 0x00 /* Disable External Load Modulation */ - , - ST25R3916_REG_AUX_MOD, - ST25R3916_REG_AUX_MOD_lm_dri, - ST25R3916_REG_AUX_MOD_lm_dri /* Use internal Load Modulation */ - , - ST25R3916_REG_PASSIVE_TARGET, - ST25R3916_REG_PASSIVE_TARGET_fdel_mask, - (5U - << ST25R3916_REG_PASSIVE_TARGET_fdel_shift) /* Adjust the FDT to be aligned with the bitgrid */ - , - ST25R3916_REG_PT_MOD, - (ST25R3916_REG_PT_MOD_ptm_res_mask | ST25R3916_REG_PT_MOD_pt_res_mask), - 0x5f /* Reduce RFO resistance in Modulated state */ - , - ST25R3916_REG_EMD_SUP_CONF, - ST25R3916_REG_EMD_SUP_CONF_rx_start_emv, - ST25R3916_REG_EMD_SUP_CONF_rx_start_emv_on /* Enable start on first 4 bits */ - , - ST25R3916_REG_ANT_TUNE_A, - 0xFF, - 0x82 /* Set Antenna Tuning (Poller): ANTL */ - , - ST25R3916_REG_ANT_TUNE_B, - 0xFF, - 0x82 /* Set Antenna Tuning (Poller): ANTL */ - , - 0x84U, - 0x10, - 0x10 /* Avoid chip internal overheat protection */ - ) - - /****** Default Analog Configuration for Chip-Specific Poll Common ******/ - , - MODE_ENTRY_9_REG( - (RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_POLL_COMMON), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_am /* Use AM modulation */ - , - ST25R3916_REG_TX_DRIVER, - ST25R3916_REG_TX_DRIVER_am_mod_mask, - ST25R3916_REG_TX_DRIVER_am_mod_12percent /* Set Modulation index */ - , - ST25R3916_REG_AUX_MOD, - (ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am), - 0x00 /* Use AM via regulator */ - , - ST25R3916_REG_ANT_TUNE_A, - 0xFF, - 0x82 /* Set Antenna Tuning (Poller): ANTL */ - , - ST25R3916_REG_ANT_TUNE_B, - 0xFF, - 0x82 /* Set Antenna Tuning (Poller): ANTL */ - , - ST25R3916_REG_OVERSHOOT_CONF1, - 0xFF, - 0x00 /* Disable Overshoot Protection */ - , - ST25R3916_REG_OVERSHOOT_CONF2, - 0xFF, - 0x00 /* Disable Overshoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF1, - 0xFF, - 0x00 /* Disable Undershoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF2, - 0xFF, - 0x00 /* Disable Undershoot Protection */ - ) - - /****** Default Analog Configuration for Poll NFC-A Rx Common ******/ - , - MODE_ENTRY_1_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_AUX, - ST25R3916_REG_AUX_dis_corr, - ST25R3916_REG_AUX_dis_corr_correlator /* Use Correlator Receiver */ - ) - - /****** Default Analog Configuration for Poll NFC-A Tx 106 ******/ - , - MODE_ENTRY_5_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | RFAL_ANALOG_CONFIG_BITRATE_106 | - RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_ook /* Use OOK */ - , - ST25R3916_REG_OVERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Overshoot Protection */ - , - ST25R3916_REG_OVERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Overshoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Undershoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Undershoot Protection */ - ) - - /****** Default Analog Configuration for Poll NFC-A Rx 106 ******/ - , - MODE_ENTRY_6_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | RFAL_ANALOG_CONFIG_BITRATE_106 | - RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x08, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x2D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x51, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) - - /****** Default Analog Configuration for Poll NFC-A Tx 212 ******/ - , - MODE_ENTRY_7_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | RFAL_ANALOG_CONFIG_BITRATE_212 | - RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_am /* Use AM modulation */ - , - ST25R3916_REG_AUX_MOD, - (ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am), - 0x88 /* Use Resistive AM */ - , - ST25R3916_REG_RES_AM_MOD, - ST25R3916_REG_RES_AM_MOD_md_res_mask, - 0x7F /* Set Resistive modulation */ - , - ST25R3916_REG_OVERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Overshoot Protection */ - , - ST25R3916_REG_OVERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Overshoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Undershoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Undershoot Protection */ - ) - - /****** Default Analog Configuration for Poll NFC-A Rx 212 ******/ - , - MODE_ENTRY_6_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | RFAL_ANALOG_CONFIG_BITRATE_212 | - RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x02, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x3D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x14, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) - - /****** Default Analog Configuration for Poll NFC-A Tx 424 ******/ - , - MODE_ENTRY_7_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | RFAL_ANALOG_CONFIG_BITRATE_424 | - RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_am /* Use AM modulation */ - , - ST25R3916_REG_AUX_MOD, - (ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am), - 0x88 /* Use Resistive AM */ - , - ST25R3916_REG_RES_AM_MOD, - ST25R3916_REG_RES_AM_MOD_md_res_mask, - 0x7F /* Set Resistive modulation */ - , - ST25R3916_REG_OVERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Overshoot Protection */ - , - ST25R3916_REG_OVERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Overshoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Undershoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Undershoot Protection */ - ) - - /****** Default Analog Configuration for Poll NFC-A Rx 424 ******/ - , - MODE_ENTRY_6_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | RFAL_ANALOG_CONFIG_BITRATE_424 | - RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x42, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x3D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x54, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) - - /****** Default Analog Configuration for Poll NFC-A Tx 848 ******/ - , - MODE_ENTRY_7_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | RFAL_ANALOG_CONFIG_BITRATE_848 | - RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_am /* Use AM modulation */ - , - ST25R3916_REG_TX_DRIVER, - ST25R3916_REG_TX_DRIVER_am_mod_mask, - ST25R3916_REG_TX_DRIVER_am_mod_40percent /* Set Modulation index */ - , - ST25R3916_REG_AUX_MOD, - (ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am), - 0x00 /* Use AM via regulator */ - , - ST25R3916_REG_OVERSHOOT_CONF1, - 0xFF, - 0x00 /* Disable Overshoot Protection */ - , - ST25R3916_REG_OVERSHOOT_CONF2, - 0xFF, - 0x00 /* Disable Overshoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF1, - 0xFF, - 0x00 /* Disable Undershoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF2, - 0xFF, - 0x00 /* Disable Undershoot Protection */ - ) - - /****** Default Analog Configuration for Poll NFC-A Rx 848 ******/ - , - MODE_ENTRY_6_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | RFAL_ANALOG_CONFIG_BITRATE_848 | - RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x42, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x3D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x44, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) - - /****** Default Analog Configuration for Poll NFC-A Anticolision setting ******/ - , - MODE_ENTRY_1_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_ANTICOL), - ST25R3916_REG_CORR_CONF1, - ST25R3916_REG_CORR_CONF1_corr_s6, - 0x00 /* Set collision detection level different from data */ - ) - -#ifdef RFAL_USE_COHE - /****** Default Analog Configuration for Poll NFC-B Rx Common ******/ - , - MODE_ENTRY_1_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_AUX, - ST25R3916_REG_AUX_dis_corr, - ST25R3916_REG_AUX_dis_corr_coherent /* Use Coherent Receiver */ - ) -#else - /****** Default Analog Configuration for Poll NFC-B Rx Common ******/ - , - MODE_ENTRY_1_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_AUX, - ST25R3916_REG_AUX_dis_corr, - ST25R3916_REG_AUX_dis_corr_correlator /* Use Correlator Receiver */ - ) -#endif /*RFAL_USE_COHE*/ - - /****** Default Analog Configuration for Poll NFC-B Rx 106 ******/ - , - MODE_ENTRY_6_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | RFAL_ANALOG_CONFIG_BITRATE_106 | - RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x04, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x3D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x1B, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) - - /****** Default Analog Configuration for Poll NFC-B Rx 212 ******/ - , - MODE_ENTRY_6_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | RFAL_ANALOG_CONFIG_BITRATE_212 | - RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x02, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x3D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x14, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) - - /****** Default Analog Configuration for Poll NFC-B Rx 424 ******/ - , - MODE_ENTRY_6_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | RFAL_ANALOG_CONFIG_BITRATE_424 | - RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x42, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x3D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x54, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) - - /****** Default Analog Configuration for Poll NFC-B Rx 848 ******/ - , - MODE_ENTRY_6_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | RFAL_ANALOG_CONFIG_BITRATE_848 | - RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x42, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x3D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x44, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) -#ifdef RFAL_USE_COHE - - /****** Default Analog Configuration for Poll NFC-F Rx Common ******/ - , - MODE_ENTRY_7_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCF | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_AUX, - ST25R3916_REG_AUX_dis_corr, - ST25R3916_REG_AUX_dis_corr_coherent /* Use Pulse Receiver */ - , - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x13, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x3D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x54, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) -#else - /****** Default Analog Configuration for Poll NFC-F Rx Common ******/ - , - MODE_ENTRY_7_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCF | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_AUX, - ST25R3916_REG_AUX_dis_corr, - ST25R3916_REG_AUX_dis_corr_correlator /* Use Correlator Receiver */ - , - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x13, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x3D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x54, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x00) -#endif /*RFAL_USE_COHE*/ - - , - MODE_ENTRY_1_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCV | RFAL_ANALOG_CONFIG_BITRATE_1OF4 | - RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_ook /* Use OOK */ - ) - -#ifdef RFAL_USE_COHE - /****** Default Analog Configuration for Poll NFC-V Rx Common ******/ - , - MODE_ENTRY_7_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCV | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_AUX, - ST25R3916_REG_AUX_dis_corr, - ST25R3916_REG_AUX_dis_corr_coherent /* Use Pulse Receiver */ - , - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x13, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x2D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x13, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x01) -#else - /****** Default Analog Configuration for Poll NFC-V Rx Common ******/ - , - MODE_ENTRY_7_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCV | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_AUX, - ST25R3916_REG_AUX_dis_corr, - ST25R3916_REG_AUX_dis_corr_correlator /* Use Correlator Receiver */ - , - ST25R3916_REG_RX_CONF1, - 0xFF, - 0x13, - ST25R3916_REG_RX_CONF2, - 0xFF, - 0x2D, - ST25R3916_REG_RX_CONF3, - 0xFF, - 0x00, - ST25R3916_REG_RX_CONF4, - 0xFF, - 0x00, - ST25R3916_REG_CORR_CONF1, - 0xFF, - 0x13, - ST25R3916_REG_CORR_CONF2, - 0xFF, - 0x01) -#endif /*RFAL_USE_COHE*/ - - /****** Default Analog Configuration for Poll AP2P Tx 106 ******/ - , - MODE_ENTRY_5_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_AP2P | RFAL_ANALOG_CONFIG_BITRATE_106 | - RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_ook /* Use OOK modulation */ - , - ST25R3916_REG_OVERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Overshoot Protection */ - , - ST25R3916_REG_OVERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Overshoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Undershoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Undershoot Protection */ - ) - - /****** Default Analog Configuration for Poll AP2P Tx 212 ******/ - , - MODE_ENTRY_1_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_AP2P | RFAL_ANALOG_CONFIG_BITRATE_212 | - RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_am /* Use AM modulation */ - ) - - /****** Default Analog Configuration for Poll AP2P Tx 424 ******/ - , - MODE_ENTRY_1_REG( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_AP2P | RFAL_ANALOG_CONFIG_BITRATE_424 | - RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_am /* Use AM modulation */ - ) - - /****** Default Analog Configuration for Chip-Specific Listen On ******/ - , - MODE_ENTRY_6_REG( - (RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_LISTEN_ON), - ST25R3916_REG_ANT_TUNE_A, - 0xFF, - 0x00 /* Set Antenna Tuning (Listener): ANTL */ - , - ST25R3916_REG_ANT_TUNE_B, - 0xFF, - 0xff /* Set Antenna Tuning (Listener): ANTL */ - , - ST25R3916_REG_OVERSHOOT_CONF1, - 0xFF, - 0x00 /* Disable Overshoot Protection */ - , - ST25R3916_REG_OVERSHOOT_CONF2, - 0xFF, - 0x00 /* Disable Overshoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF1, - 0xFF, - 0x00 /* Disable Undershoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF2, - 0xFF, - 0x00 /* Disable Undershoot Protection */ - ) - - /****** Default Analog Configuration for Listen AP2P Tx Common ******/ - , - MODE_ENTRY_7_REG( - (RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_AP2P | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_ANT_TUNE_A, - 0xFF, - 0x82 /* Set Antenna Tuning (Poller): ANTL */ - , - ST25R3916_REG_ANT_TUNE_B, - 0xFF, - 0x82 /* Set Antenna Tuning (Poller): ANTL */ - , - ST25R3916_REG_TX_DRIVER, - ST25R3916_REG_TX_DRIVER_am_mod_mask, - ST25R3916_REG_TX_DRIVER_am_mod_12percent /* Set Modulation index */ - , - ST25R3916_REG_OVERSHOOT_CONF1, - 0xFF, - 0x00 /* Disable Overshoot Protection */ - , - ST25R3916_REG_OVERSHOOT_CONF2, - 0xFF, - 0x00 /* Disable Overshoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF1, - 0xFF, - 0x00 /* Disable Undershoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF2, - 0xFF, - 0x00 /* Disable Undershoot Protection */ - ) - - /****** Default Analog Configuration for Listen AP2P Rx Common ******/ - , - MODE_ENTRY_3_REG( - (RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_AP2P | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX), - ST25R3916_REG_RX_CONF1, - ST25R3916_REG_RX_CONF1_lp_mask, - ST25R3916_REG_RX_CONF1_lp_1200khz /* Set Rx filter configuration */ - , - ST25R3916_REG_RX_CONF1, - ST25R3916_REG_RX_CONF1_hz_mask, - ST25R3916_REG_RX_CONF1_hz_12_200khz /* Set Rx filter configuration */ - , - ST25R3916_REG_RX_CONF2, - ST25R3916_REG_RX_CONF2_amd_sel, - ST25R3916_REG_RX_CONF2_amd_sel_mixer /* AM demodulator: mixer */ - ) - - /****** Default Analog Configuration for Listen AP2P Tx 106 ******/ - , - MODE_ENTRY_5_REG( - (RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_AP2P | - RFAL_ANALOG_CONFIG_BITRATE_106 | RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_ook /* Use OOK modulation */ - , - ST25R3916_REG_OVERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Overshoot Protection */ - , - ST25R3916_REG_OVERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Overshoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF1, - 0xFF, - 0x40 /* Set default Undershoot Protection */ - , - ST25R3916_REG_UNDERSHOOT_CONF2, - 0xFF, - 0x03 /* Set default Undershoot Protection */ - ) - - /****** Default Analog Configuration for Listen AP2P Tx 212 ******/ - , - MODE_ENTRY_1_REG( - (RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_AP2P | - RFAL_ANALOG_CONFIG_BITRATE_212 | RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_am /* Use AM modulation */ - ) - - /****** Default Analog Configuration for Listen AP2P Tx 424 ******/ - , - MODE_ENTRY_1_REG( - (RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_AP2P | - RFAL_ANALOG_CONFIG_BITRATE_424 | RFAL_ANALOG_CONFIG_TX), - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_tr_am, - ST25R3916_REG_MODE_tr_am_am /* Use AM modulation */ - ) - -}; - -#endif /* ST25R3916_ANALOGCONFIG_H */ diff --git a/lib/ST25RFAL002/source/st25r3916/rfal_dpoTbl.h b/lib/ST25RFAL002/source/st25r3916/rfal_dpoTbl.h deleted file mode 100644 index 3075776c55e..00000000000 --- a/lib/ST25RFAL002/source/st25r3916/rfal_dpoTbl.h +++ /dev/null @@ -1,68 +0,0 @@ - -/****************************************************************************** - * @attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * $Revision: $ - * LANGUAGE: ISO C99 - */ - -/*! \file - * - * \author Martin Zechleitner - * - * \brief RF Dynamic Power Table default values - */ - -#ifndef ST25R3916_DPO_H -#define ST25R3916_DPO_H - -/* - ****************************************************************************** - * INCLUDES - ****************************************************************************** - */ -#include "rfal_dpo.h" - -/* - ****************************************************************************** - * GLOBAL DATA TYPES - ****************************************************************************** - */ - -/*! Default DPO table */ -const uint8_t rfalDpoDefaultSettings[] = { - 0x00, - 255, - 200, - 0x01, - 210, - 150, - 0x02, - 160, - 100, - 0x03, - 110, - 50, -}; - -#endif /* ST25R3916_DPO_H */ diff --git a/lib/ST25RFAL002/source/st25r3916/rfal_features.h b/lib/ST25RFAL002/source/st25r3916/rfal_features.h deleted file mode 100644 index 92f26acf575..00000000000 --- a/lib/ST25RFAL002/source/st25r3916/rfal_features.h +++ /dev/null @@ -1,109 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R391x firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file - * - * \author Gustavo Patricio - * - * \brief RFAL Features/Capabilities Definition for ST25R3916 - */ - -#ifndef RFAL_FEATURES_H -#define RFAL_FEATURES_H - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ -#include "platform.h" - -/* -****************************************************************************** -* GLOBAL DEFINES -****************************************************************************** -*/ - -#define RFAL_SUPPORT_MODE_POLL_NFCA true /*!< RFAL Poll NFCA mode support switch */ -#define RFAL_SUPPORT_MODE_POLL_NFCB true /*!< RFAL Poll NFCB mode support switch */ -#define RFAL_SUPPORT_MODE_POLL_NFCF true /*!< RFAL Poll NFCF mode support switch */ -#define RFAL_SUPPORT_MODE_POLL_NFCV true /*!< RFAL Poll NFCV mode support switch */ -#define RFAL_SUPPORT_MODE_POLL_ACTIVE_P2P true /*!< RFAL Poll AP2P mode support switch */ -#define RFAL_SUPPORT_MODE_LISTEN_NFCA true /*!< RFAL Listen NFCA mode support switch */ -#define RFAL_SUPPORT_MODE_LISTEN_NFCB false /*!< RFAL Listen NFCB mode support switch */ -#define RFAL_SUPPORT_MODE_LISTEN_NFCF true /*!< RFAL Listen NFCF mode support switch */ -#define RFAL_SUPPORT_MODE_LISTEN_ACTIVE_P2P true /*!< RFAL Listen AP2P mode support switch */ - -/*******************************************************************************/ -/*! RFAL supported Card Emulation (CE) */ -#define RFAL_SUPPORT_CE \ - (RFAL_SUPPORT_MODE_LISTEN_NFCA || RFAL_SUPPORT_MODE_LISTEN_NFCB || \ - RFAL_SUPPORT_MODE_LISTEN_NFCF) - -/*! RFAL supported Reader/Writer (RW) */ -#define RFAL_SUPPORT_RW \ - (RFAL_SUPPORT_MODE_POLL_NFCA || RFAL_SUPPORT_MODE_POLL_NFCB || RFAL_SUPPORT_MODE_POLL_NFCF || \ - RFAL_SUPPORT_MODE_POLL_NFCV) - -/*! RFAL support for Active P2P (AP2P) */ -#define RFAL_SUPPORT_AP2P \ - (RFAL_SUPPORT_MODE_POLL_ACTIVE_P2P || RFAL_SUPPORT_MODE_LISTEN_ACTIVE_P2P) - -/*******************************************************************************/ -#define RFAL_SUPPORT_BR_RW_106 true /*!< RFAL RW 106 Bit Rate support switch */ -#define RFAL_SUPPORT_BR_RW_212 true /*!< RFAL RW 212 Bit Rate support switch */ -#define RFAL_SUPPORT_BR_RW_424 true /*!< RFAL RW 424 Bit Rate support switch */ -#define RFAL_SUPPORT_BR_RW_848 true /*!< RFAL RW 848 Bit Rate support switch */ -#define RFAL_SUPPORT_BR_RW_1695 false /*!< RFAL RW 1695 Bit Rate support switch */ -#define RFAL_SUPPORT_BR_RW_3390 false /*!< RFAL RW 3390 Bit Rate support switch */ -#define RFAL_SUPPORT_BR_RW_6780 false /*!< RFAL RW 6780 Bit Rate support switch */ -#define RFAL_SUPPORT_BR_RW_13560 false /*!< RFAL RW 6780 Bit Rate support switch */ - -/*******************************************************************************/ -#define RFAL_SUPPORT_BR_AP2P_106 true /*!< RFAL AP2P 106 Bit Rate support switch */ -#define RFAL_SUPPORT_BR_AP2P_212 true /*!< RFAL AP2P 212 Bit Rate support switch */ -#define RFAL_SUPPORT_BR_AP2P_424 true /*!< RFAL AP2P 424 Bit Rate support switch */ -#define RFAL_SUPPORT_BR_AP2P_848 false /*!< RFAL AP2P 848 Bit Rate support switch */ - -/*******************************************************************************/ -#define RFAL_SUPPORT_BR_CE_A_106 true /*!< RFAL CE A 106 Bit Rate support switch */ -#define RFAL_SUPPORT_BR_CE_A_212 false /*!< RFAL CE A 212 Bit Rate support switch */ -#define RFAL_SUPPORT_BR_CE_A_424 false /*!< RFAL CE A 424 Bit Rate support switch */ -#define RFAL_SUPPORT_BR_CE_A_848 false /*!< RFAL CE A 848 Bit Rate support switch */ - -/*******************************************************************************/ -#define RFAL_SUPPORT_BR_CE_B_106 false /*!< RFAL CE B 106 Bit Rate support switch */ -#define RFAL_SUPPORT_BR_CE_B_212 false /*!< RFAL CE B 212 Bit Rate support switch */ -#define RFAL_SUPPORT_BR_CE_B_424 false /*!< RFAL CE B 424 Bit Rate support switch */ -#define RFAL_SUPPORT_BR_CE_B_848 false /*!< RFAL CE B 848 Bit Rate support switch */ - -/*******************************************************************************/ -#define RFAL_SUPPORT_BR_CE_F_212 true /*!< RFAL CE F 212 Bit Rate support switch */ -#define RFAL_SUPPORT_BR_CE_F_424 true /*!< RFAL CE F 424 Bit Rate support switch */ - -#endif /* RFAL_FEATURES_H */ diff --git a/lib/ST25RFAL002/source/st25r3916/rfal_rfst25r3916.c b/lib/ST25RFAL002/source/st25r3916/rfal_rfst25r3916.c deleted file mode 100644 index 9ad35bcb6bb..00000000000 --- a/lib/ST25RFAL002/source/st25r3916/rfal_rfst25r3916.c +++ /dev/null @@ -1,4798 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R3916 firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file - * - * \author Gustavo Patricio - * - * \brief RF Abstraction Layer (RFAL) - * - * RFAL implementation for ST25R3916 - */ - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ - -#include "rfal_chip.h" -#include "utils.h" -#include "st25r3916.h" -#include "st25r3916_com.h" -#include "st25r3916_irq.h" -#include "rfal_analogConfig.h" -#include "rfal_iso15693_2.h" -#include "rfal_crc.h" - -/* - ****************************************************************************** - * ENABLE SWITCHS - ****************************************************************************** - */ - -#ifndef RFAL_FEATURE_LISTEN_MODE -#define RFAL_FEATURE_LISTEN_MODE false /* Listen Mode configuration missing. Disabled by default */ -#endif /* RFAL_FEATURE_LISTEN_MODE */ - -#ifndef RFAL_FEATURE_WAKEUP_MODE -#define RFAL_FEATURE_WAKEUP_MODE \ - false /* Wake-Up mode configuration missing. Disabled by default */ -#endif /* RFAL_FEATURE_WAKEUP_MODE */ - -#ifndef RFAL_FEATURE_LOWPOWER_MODE -#define RFAL_FEATURE_LOWPOWER_MODE \ - false /* Low Power mode configuration missing. Disabled by default */ -#endif /* RFAL_FEATURE_LOWPOWER_MODE */ - -/* -****************************************************************************** -* GLOBAL TYPES -****************************************************************************** -*/ - -/*! Struct that holds all involved on a Transceive including the context passed by the caller */ -typedef struct { - rfalTransceiveState state; /*!< Current transceive state */ - rfalTransceiveState lastState; /*!< Last transceive state (debug purposes) */ - ReturnCode status; /*!< Current status/error of the transceive */ - - rfalTransceiveContext ctx; /*!< The transceive context given by the caller */ -} rfalTxRx; - -/*! Struct that holds all context for the Listen Mode */ -typedef struct { - rfalLmState state; /*!< Current Listen Mode state */ - uint32_t mdMask; /*!< Listen Mode mask used */ - uint32_t mdReg; /*!< Listen Mode register value used */ - uint32_t mdIrqs; /*!< Listen Mode IRQs used */ - rfalBitRate brDetected; /*!< Last bit rate detected */ - - uint8_t* rxBuf; /*!< Location to store incoming data in Listen Mode */ - uint16_t rxBufLen; /*!< Length of rxBuf */ - uint16_t* rxLen; /*!< Pointer to write the data length placed into rxBuf */ - bool dataFlag; /*!< Listen Mode current Data Flag */ - bool iniFlag; /*!< Listen Mode initialized Flag (FeliCa slots) */ -} rfalLm; - -/*! Struct that holds all context for the Wake-Up Mode */ -typedef struct { - rfalWumState state; /*!< Current Wake-Up Mode state */ - rfalWakeUpConfig cfg; /*!< Current Wake-Up Mode context */ -} rfalWum; - -/*! Struct that holds all context for the Low Power Mode */ -typedef struct { - bool isRunning; -} rfalLpm; - -/*! Struct that holds the timings GT and FDTs */ -typedef struct { - uint32_t GT; /*!< GT in 1/fc */ - uint32_t FDTListen; /*!< FDTListen in 1/fc */ - uint32_t FDTPoll; /*!< FDTPoll in 1/fc */ - uint8_t nTRFW; /*!< n*TRFW used during RF CA */ -} rfalTimings; - -/*! Struct that holds the software timers */ -typedef struct { - uint32_t GT; /*!< RFAL's GT timer */ - uint32_t RXE; /*!< Timer between RXS and RXE */ - uint32_t txRx; /*!< Transceive sanity timer */ -} rfalTimers; - -/*! Struct that holds the RFAL's callbacks */ -typedef struct { - rfalPreTxRxCallback preTxRx; /*!< RFAL's Pre TxRx callback */ - rfalPostTxRxCallback postTxRx; /*!< RFAL's Post TxRx callback */ - RfalStateChangedCallback state_changed_cb; - void* ctx; -} rfalCallbacks; - -/*! Struct that holds counters to control the FIFO on Tx and Rx */ -typedef struct { - uint16_t - expWL; /*!< The amount of bytes expected to be Tx when a WL interrupt occours */ - uint16_t - bytesTotal; /*!< Total bytes to be transmitted OR the total bytes received */ - uint16_t - bytesWritten; /*!< Amount of bytes already written on FIFO (Tx) OR read (RX) from FIFO and written on rxBuffer*/ - uint8_t status - [ST25R3916_FIFO_STATUS_LEN]; /*!< FIFO Status Registers */ -} rfalFIFO; - -/*! Struct that holds RFAL's configuration settings */ -typedef struct { - uint8_t obsvModeTx; /*!< RFAL's config of the ST25R3916's observation mode while Tx */ - uint8_t obsvModeRx; /*!< RFAL's config of the ST25R3916's observation mode while Rx */ - rfalEHandling eHandling; /*!< RFAL's error handling config/mode */ -} rfalConfigs; - -/*! Struct that holds NFC-F data - Used only inside rfalFelicaPoll() (static to avoid adding it into stack) */ -typedef struct { - rfalFeliCaPollRes - pollResponses[RFAL_FELICA_POLL_MAX_SLOTS]; /* FeliCa Poll response container for 16 slots */ -} rfalNfcfWorkingData; - -/*! Struct that holds NFC-V current context - * - * This buffer has to be big enough for coping with maximum response size (hamming coded) - * - inventory requests responses: 14*2+2 bytes - * - read single block responses: (32+4)*2+2 bytes - * - read multiple block could be very long... -> not supported - * - current implementation expects it be written in one bulk into FIFO - * - needs to be above FIFO water level of ST25R3916 (200) - * - the coding function needs to be able to - * put more than FIFO water level bytes into it (n*64+1)>200 */ -typedef struct { - uint8_t codingBuffer[( - (2 + 255 + 3) * 2)]; /*!< Coding buffer, length MUST be above 257: [257; ...] */ - uint16_t - nfcvOffset; /*!< Offset needed for ISO15693 coding function */ - rfalTransceiveContext - origCtx; /*!< context provided by user */ - uint16_t - ignoreBits; /*!< Number of bits at the beginning of a frame to be ignored when decoding */ -} rfalNfcvWorkingData; - -/*! RFAL instance */ -typedef struct { - rfalState state; /*!< RFAL's current state */ - rfalMode mode; /*!< RFAL's current mode */ - rfalBitRate txBR; /*!< RFAL's current Tx Bit Rate */ - rfalBitRate rxBR; /*!< RFAL's current Rx Bit Rate */ - bool field; /*!< Current field state (On / Off) */ - - rfalConfigs conf; /*!< RFAL's configuration settings */ - rfalTimings timings; /*!< RFAL's timing setting */ - rfalTxRx TxRx; /*!< RFAL's transceive management */ - rfalFIFO fifo; /*!< RFAL's FIFO management */ - rfalTimers tmr; /*!< RFAL's Software timers */ - rfalCallbacks callbacks; /*!< RFAL's callbacks */ - -#if RFAL_FEATURE_LISTEN_MODE - rfalLm Lm; /*!< RFAL's listen mode management */ -#endif /* RFAL_FEATURE_LISTEN_MODE */ - -#if RFAL_FEATURE_WAKEUP_MODE - rfalWum wum; /*!< RFAL's Wake-up mode management */ -#endif /* RFAL_FEATURE_WAKEUP_MODE */ - -#if RFAL_FEATURE_LOWPOWER_MODE - rfalLpm lpm; /*!< RFAL's Low power mode management */ -#endif /* RFAL_FEATURE_LOWPOWER_MODE */ - -#if RFAL_FEATURE_NFCF - rfalNfcfWorkingData nfcfData; /*!< RFAL's working data when supporting NFC-F */ -#endif /* RFAL_FEATURE_NFCF */ - -#if RFAL_FEATURE_NFCV - rfalNfcvWorkingData nfcvData; /*!< RFAL's working data when performing NFC-V */ -#endif /* RFAL_FEATURE_NFCV */ - -} rfal; - -/*! Felica's command set */ -typedef enum { - FELICA_CMD_POLLING = - 0x00, /*!< Felica Poll/REQC command (aka SENSF_REQ) to identify a card */ - FELICA_CMD_POLLING_RES = - 0x01, /*!< Felica Poll/REQC command (aka SENSF_RES) response */ - FELICA_CMD_REQUEST_SERVICE = - 0x02, /*!< verify the existence of Area and Service */ - FELICA_CMD_REQUEST_RESPONSE = - 0x04, /*!< verify the existence of a card */ - FELICA_CMD_READ_WITHOUT_ENCRYPTION = - 0x06, /*!< read Block Data from a Service that requires no authentication */ - FELICA_CMD_WRITE_WITHOUT_ENCRYPTION = - 0x08, /*!< write Block Data to a Service that requires no authentication */ - FELICA_CMD_REQUEST_SYSTEM_CODE = - 0x0C, /*!< acquire the System Code registered to a card */ - FELICA_CMD_AUTHENTICATION1 = - 0x10, /*!< authenticate a card */ - FELICA_CMD_AUTHENTICATION2 = - 0x12, /*!< allow a card to authenticate a Reader/Writer */ - FELICA_CMD_READ = 0x14, /*!< read Block Data from a Service that requires authentication */ - FELICA_CMD_WRITE = 0x16, /*!< write Block Data to a Service that requires authentication */ -} t_rfalFeliCaCmd; - -/*! Union representing all PTMem sections */ -typedef union { /* PRQA S 0750 # MISRA 19.2 - Both members are of the same type, just different names. Thus no problem can occur. */ - uint8_t PTMem_A - [ST25R3916_PTM_A_LEN]; /*!< PT_Memory area allocated for NFC-A configuration */ - uint8_t PTMem_F - [ST25R3916_PTM_F_LEN]; /*!< PT_Memory area allocated for NFC-F configuration */ - uint8_t - TSN[ST25R3916_PTM_TSN_LEN]; /*!< PT_Memory area allocated for TSN - Random numbers */ -} t_rfalPTMem; - -/* -****************************************************************************** -* GLOBAL DEFINES -****************************************************************************** -*/ - -#define RFAL_FIFO_IN_WL \ - 200U /*!< Number of bytes in the FIFO when WL interrupt occurs while Tx */ -#define RFAL_FIFO_OUT_WL \ - (ST25R3916_FIFO_DEPTH - \ - RFAL_FIFO_IN_WL) /*!< Number of bytes sent/out of the FIFO when WL interrupt occurs while Tx */ - -#define RFAL_FIFO_STATUS_REG1 \ - 0U /*!< Location of FIFO status register 1 in local copy */ -#define RFAL_FIFO_STATUS_REG2 \ - 1U /*!< Location of FIFO status register 2 in local copy */ -#define RFAL_FIFO_STATUS_INVALID \ - 0xFFU /*!< Value indicating that the local FIFO status in invalid|cleared */ - -#define RFAL_ST25R3916_GPT_MAX_1FC \ - rfalConv8fcTo1fc( \ - 0xFFFFU) /*!< Max GPT steps in 1fc (0xFFFF steps of 8/fc => 0xFFFF * 590ns = 38,7ms) */ -#define RFAL_ST25R3916_NRT_MAX_1FC \ - rfalConv4096fcTo1fc( \ - 0xFFFFU) /*!< Max NRT steps in 1fc (0xFFFF steps of 4096/fc => 0xFFFF * 302us = 19.8s ) */ -#define RFAL_ST25R3916_NRT_DISABLED \ - 0U /*!< NRT Disabled: All 0 No-response timer is not started, wait forever */ -#define RFAL_ST25R3916_MRT_MAX_1FC \ - rfalConv64fcTo1fc( \ - 0x00FFU) /*!< Max MRT steps in 1fc (0x00FF steps of 64/fc => 0x00FF * 4.72us = 1.2ms ) */ -#define RFAL_ST25R3916_MRT_MIN_1FC \ - rfalConv64fcTo1fc( \ - 0x0004U) /*!< Min MRT steps in 1fc ( 0<=mrt<=4 ; 4 (64/fc) => 0x0004 * 4.72us = 18.88us ) */ -#define RFAL_ST25R3916_GT_MAX_1FC \ - rfalConvMsTo1fc( \ - 6000U) /*!< Max GT value allowed in 1/fc (SFGI=14 => SFGT + dSFGT = 5.4s) */ -#define RFAL_ST25R3916_GT_MIN_1FC \ - rfalConvMsTo1fc( \ - RFAL_ST25R3916_SW_TMR_MIN_1MS) /*!< Min GT value allowed in 1/fc */ -#define RFAL_ST25R3916_SW_TMR_MIN_1MS \ - 1U /*!< Min value of a SW timer in ms */ - -#define RFAL_OBSMODE_DISABLE \ - 0x00U /*!< Observation Mode disabled */ - -#define RFAL_RX_INCOMPLETE_MAXLEN \ - (uint8_t)1U /*!< Threshold value where incoming rx may be considered as incomplete */ -#define RFAL_EMVCO_RX_MAXLEN \ - (uint8_t)4U /*!< Maximum value where EMVCo to apply special error handling */ - -#define RFAL_NORXE_TOUT \ - 50U /*!< Timeout to be used on a potential missing RXE - Silicon ST25R3916 Errata #TBD */ - -#define RFAL_ISO14443A_SDD_RES_LEN \ - 5U /*!< SDD_RES | Anticollision (UID CLn) length - rfalNfcaSddRes */ -#define RFAL_ISO14443A_CRC_INTVAL \ - 0x6363 /*!< ISO14443 CRC Initial Value|Register */ - -#define RFAL_FELICA_POLL_DELAY_TIME \ - 512U /*!< FeliCa Poll Processing time is 2.417 ms ~512*64/fc Digital 1.1 A4 */ -#define RFAL_FELICA_POLL_SLOT_TIME \ - 256U /*!< FeliCa Poll Time Slot duration is 1.208 ms ~256*64/fc Digital 1.1 A4 */ - -#define RFAL_LM_SENSF_RD0_POS \ - 17U /*!< FeliCa SENSF_RES Request Data RD0 position */ -#define RFAL_LM_SENSF_RD1_POS \ - 18U /*!< FeliCa SENSF_RES Request Data RD1 position */ - -#define RFAL_LM_NFCID_INCOMPLETE \ - 0x04U /*!< NFCA NFCID not complete bit in SEL_RES (SAK) */ - -#define RFAL_ISO15693_IGNORE_BITS \ - rfalConvBytesToBits( \ - 2U) /*!< Ignore collisions before the UID (RES_FLAG + DSFID) */ -#define RFAL_ISO15693_INV_RES_LEN \ - 12U /*!< ISO15693 Inventory response length with CRC (bytes) */ -#define RFAL_ISO15693_INV_RES_DUR \ - 4U /*!< ISO15693 Inventory response duration @ 26 kbps (ms) */ - -#define RFAL_WU_MIN_WEIGHT_VAL \ - 4U /*!< ST25R3916 minimum Wake-up weight value */ - -/*******************************************************************************/ - -#define RFAL_LM_GT \ - rfalConvUsTo1fc( \ - 100U) /*!< Listen Mode Guard Time enforced (GT - Passive; TIRFG - Active) */ -#define RFAL_FDT_POLL_ADJUSTMENT \ - rfalConvUsTo1fc( \ - 80U) /*!< FDT Poll adjustment: Time between the expiration of GPT to the actual Tx */ -#define RFAL_FDT_LISTEN_MRT_ADJUSTMENT \ - 64U /*!< MRT jitter adjustment: timeout will be between [ tout ; tout + 64 cycles ] */ -#define RFAL_AP2P_FIELDOFF_TRFW \ - rfalConv8fcTo1fc( \ - 64U) /*!< Time after TXE and Field Off in AP2P Trfw: 37.76us -> 64 (8/fc) */ - -#ifndef RFAL_ST25R3916_AAT_SETTLE -#define RFAL_ST25R3916_AAT_SETTLE \ - 5U /*!< Time in ms required for AAT pins and Osc to settle after en bit set */ -#endif /* RFAL_ST25R3916_AAT_SETTLE */ - -/*! FWT adjustment: - * 64 : NRT jitter between TXE and NRT start */ -#define RFAL_FWT_ADJUSTMENT 64U - -/*! FWT ISO14443A adjustment: - * 512 : 4bit length - * 64 : Half a bit duration due to ST25R3916 Coherent receiver (1/fc) */ -#define RFAL_FWT_A_ADJUSTMENT (512U + 64U) - -/*! FWT ISO14443B adjustment: - * SOF (14etu) + 1Byte (10etu) + 1etu (IRQ comes 1etu after first byte) - 3etu (ST25R3916 sends TXE 3etu after) */ -#define RFAL_FWT_B_ADJUSTMENT ((14U + 10U + 1U - 3U) * 128U) - -/*! FWT FeliCa 212 adjustment: - * 1024 : Length of the two Sync bytes at 212kbps */ -#define RFAL_FWT_F_212_ADJUSTMENT 1024U - -/*! FWT FeliCa 424 adjustment: - * 512 : Length of the two Sync bytes at 424kbps */ -#define RFAL_FWT_F_424_ADJUSTMENT 512U - -/*! Time between our field Off and other peer field On : Tadt + (n x Trfw) - * Ecma 340 11.1.2 - Tadt: [56.64 , 188.72] us ; n: [0 , 3] ; Trfw = 37.76 us - * Should be: 189 + (3*38) = 303us ; we'll use a more relaxed setting: 605 us */ -#define RFAL_AP2P_FIELDON_TADTTRFW rfalConvUsTo1fc(605U) - -/*! FDT Listen adjustment for ISO14443A EMVCo 2.6 4.8.1.3 ; Digital 1.1 6.10 - * - * 276: Time from the rising pulse of the pause of the logic '1' (i.e. the time point to measure the deaftime from), - * to the actual end of the EOF sequence (the point where the MRT starts). Please note that the ST25R391x uses the - * ISO14443-2 definition where the EOF consists of logic '0' followed by sequence Y. - * -64: Further adjustment for receiver to be ready just before first bit - */ -#define RFAL_FDT_LISTEN_A_ADJUSTMENT (276U - 64U) - -/*! FDT Listen adjustment for ISO14443B EMVCo 2.6 4.8.1.6 ; Digital 1.1 7.9 - * - * 340: Time from the rising edge of the EoS to the starting point of the MRT timer (sometime after the final high - * part of the EoS is completed) - */ -#define RFAL_FDT_LISTEN_B_ADJUSTMENT 340U - -/*! FDT Listen adjustment for ISO15693 - * ISO15693 2000 8.4 t1 MIN = 4192/fc - * ISO15693 2009 9.1 t1 MIN = 4320/fc - * Digital 2.1 B.5 FDTV,LISTEN,MIN = 4310/fc - * Set FDT Listen one step earlier than on the more recent spec versions for greater interoprability - */ -#define RFAL_FDT_LISTEN_V_ADJUSTMENT 64U - -/*! FDT Poll adjustment for ISO14443B Correlator - sst 5 etu */ -#define RFAL_FDT_LISTEN_B_ADJT_CORR 128U - -/*! FDT Poll adjustment for ISO14443B Correlator sst window - 5 etu */ -#define RFAL_FDT_LISTEN_B_ADJT_CORR_SST 20U - -/* -****************************************************************************** -* GLOBAL MACROS -****************************************************************************** -*/ - -/*! Calculates Transceive Sanity Timer. It accounts for the slowest bit rate and the longest data format - * 1s for transmission and reception of a 4K message at 106kpbs (~425ms each direction) - * plus TxRx preparation and FIFO load over Serial Interface */ -#define rfalCalcSanityTmr(fwt) (uint16_t)(1000U + rfalConv1fcToMs((fwt))) - -#define rfalGennTRFW(n) \ - (((n) + 1U) & \ - ST25R3916_REG_AUX_nfc_n_mask) /*!< Generates the next n*TRRW used for RFCA */ - -#define rfalCalcNumBytes(nBits) \ - (((uint32_t)(nBits) + 7U) / \ - 8U) /*!< Returns the number of bytes required to fit given the number of bits */ - -#define rfalTimerStart(timer, time_ms) \ - do { \ - platformTimerDestroy(timer); \ - (timer) = platformTimerCreate((uint16_t)(time_ms)); \ - } while(0) /*!< Configures and starts timer */ -#define rfalTimerisExpired(timer) \ - platformTimerIsExpired( \ - timer) /*!< Checks if timer has expired */ -#define rfalTimerDestroy(timer) \ - platformTimerDestroy( \ - timer) /*!< Destroys timer */ - -#define rfalST25R3916ObsModeDisable() \ - st25r3916WriteTestRegister( \ - 0x01U, \ - (0x40U)) /*!< Disable ST25R3916 Observation mode */ -#define rfalST25R3916ObsModeTx() \ - st25r3916WriteTestRegister( \ - 0x01U, \ - (0x40U | \ - gRFAL.conf \ - .obsvModeTx)) /*!< Enable Tx Observation mode */ -#define rfalST25R3916ObsModeRx() \ - st25r3916WriteTestRegister( \ - 0x01U, \ - (0x40U | \ - gRFAL.conf \ - .obsvModeRx)) /*!< Enable Rx Observation mode */ - -#define rfalCheckDisableObsMode() \ - if(gRFAL.conf.obsvModeRx != 0U) { \ - rfalST25R3916ObsModeDisable(); \ - } /*!< Checks if the observation mode is enabled, and applies on ST25R3916 */ -#define rfalCheckEnableObsModeTx() \ - if(gRFAL.conf.obsvModeTx != 0U) { \ - rfalST25R3916ObsModeTx(); \ - } /*!< Checks if the observation mode is enabled, and applies on ST25R3916 */ -#define rfalCheckEnableObsModeRx() \ - if(gRFAL.conf.obsvModeRx != 0U) { \ - rfalST25R3916ObsModeRx(); \ - } /*!< Checks if the observation mode is enabled, and applies on ST25R3916 */ - -#define rfalGetIncmplBits(FIFOStatus2) \ - (((FIFOStatus2) >> 1) & \ - 0x07U) /*!< Returns the number of bits from fifo status */ -#define rfalIsIncompleteByteError(error) \ - (((error) >= ERR_INCOMPLETE_BYTE) && \ - ((error) <= \ - ERR_INCOMPLETE_BYTE_07)) /*!< Checks if given error is a Incomplete error */ - -#define rfalAdjACBR(b) \ - (((uint16_t)(b) >= (uint16_t)RFAL_BR_52p97) ? \ - (uint16_t)(b) : \ - ((uint16_t)(b) + \ - 1U)) /*!< Adjusts ST25R391x Bit rate to Analog Configuration */ -#define rfalConvBR2ACBR(b) \ - (((rfalAdjACBR((b))) << RFAL_ANALOG_CONFIG_BITRATE_SHIFT) & \ - RFAL_ANALOG_CONFIG_BITRATE_MASK) /*!< Converts ST25R391x Bit rate to Analog Configuration bit rate id */ - -#define rfalConvTDFormat(v) \ - ((uint16_t)(v) << 8U) /*!< Converts a uint8_t to the format used in SW Tag Detection */ - -/* - ****************************************************************************** - * LOCAL VARIABLES - ****************************************************************************** - */ - -static rfal gRFAL; /*!< RFAL module instance */ - -/* -****************************************************************************** -* LOCAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -static void rfalTransceiveTx(void); -static void rfalTransceiveRx(void); -static ReturnCode rfalTransceiveRunBlockingTx(void); -static void rfalPrepareTransceive(void); -static void rfalCleanupTransceive(void); -static void rfalErrorHandling(void); - -static ReturnCode rfalRunTransceiveWorker(void); -#if RFAL_FEATURE_LISTEN_MODE -static ReturnCode rfalRunListenModeWorker(void); -#endif /* RFAL_FEATURE_LISTEN_MODE */ -#if RFAL_FEATURE_WAKEUP_MODE -static void rfalRunWakeUpModeWorker(void); -static uint16_t rfalWakeUpModeFilter(uint16_t curRef, uint16_t curVal, uint8_t weight); -#endif /* RFAL_FEATURE_WAKEUP_MODE */ - -static void rfalFIFOStatusUpdate(void); -static void rfalFIFOStatusClear(void); -static bool rfalFIFOStatusIsMissingPar(void); -static bool rfalFIFOStatusIsIncompleteByte(void); -static uint16_t rfalFIFOStatusGetNumBytes(void); -static uint8_t rfalFIFOGetNumIncompleteBits(void); - -/* -****************************************************************************** -* GLOBAL FUNCTIONS -****************************************************************************** -*/ - -/*******************************************************************************/ -ReturnCode rfalInitialize(void) { - ReturnCode err; - - EXIT_ON_ERR(err, st25r3916Initialize()); - - st25r3916ClearInterrupts(); - - /* Disable any previous observation mode */ - rfalST25R3916ObsModeDisable(); - - /*******************************************************************************/ - /* Apply RF Chip generic initialization */ - rfalSetAnalogConfig((RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_INIT)); - - // TODO: - // I don't want to mess with config table ("Default Analog Configuration for Chip-Specific Reset", rfal_analogConfigTbl.h) - // so with every rfalSetAnalogConfig((RFAL_ANALOG_CONFIG_CHIP_INIT)) currently we need to clear pulldown bits - // luckily for us this is done only here - - // disable pulldowns - st25r3916ClrRegisterBits( - ST25R3916_REG_IO_CONF2, - (ST25R3916_REG_IO_CONF2_miso_pd1 | ST25R3916_REG_IO_CONF2_miso_pd2)); - - /*******************************************************************************/ - /* Enable External Field Detector as: Automatics */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_OP_CONTROL, - ST25R3916_REG_OP_CONTROL_en_fd_mask, - ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); - - /* Clear FIFO status local copy */ - rfalFIFOStatusClear(); - - /*******************************************************************************/ - gRFAL.state = RFAL_STATE_INIT; - gRFAL.mode = RFAL_MODE_NONE; - gRFAL.field = false; - - /* Set RFAL default configs */ - gRFAL.conf.obsvModeRx = RFAL_OBSMODE_DISABLE; - gRFAL.conf.obsvModeTx = RFAL_OBSMODE_DISABLE; - gRFAL.conf.eHandling = RFAL_ERRORHANDLING_NONE; - - /* Transceive set to IDLE */ - gRFAL.TxRx.lastState = RFAL_TXRX_STATE_IDLE; - gRFAL.TxRx.state = RFAL_TXRX_STATE_IDLE; - - /* Disable all timings */ - gRFAL.timings.FDTListen = RFAL_TIMING_NONE; - gRFAL.timings.FDTPoll = RFAL_TIMING_NONE; - gRFAL.timings.GT = RFAL_TIMING_NONE; - gRFAL.timings.nTRFW = 0U; - - /* Destroy any previous pending timers */ - rfalTimerDestroy(gRFAL.tmr.GT); - rfalTimerDestroy(gRFAL.tmr.txRx); - rfalTimerDestroy(gRFAL.tmr.RXE); - gRFAL.tmr.GT = RFAL_TIMING_NONE; - gRFAL.tmr.txRx = RFAL_TIMING_NONE; - gRFAL.tmr.RXE = RFAL_TIMING_NONE; - - gRFAL.callbacks.preTxRx = NULL; - gRFAL.callbacks.postTxRx = NULL; - gRFAL.callbacks.state_changed_cb = NULL; - gRFAL.callbacks.ctx = NULL; - -#if RFAL_FEATURE_NFCV - /* Initialize NFC-V Data */ - gRFAL.nfcvData.ignoreBits = 0; -#endif /* RFAL_FEATURE_NFCV */ - -#if RFAL_FEATURE_LISTEN_MODE - /* Initialize Listen Mode */ - gRFAL.Lm.state = RFAL_LM_STATE_NOT_INIT; - gRFAL.Lm.brDetected = RFAL_BR_KEEP; - gRFAL.Lm.iniFlag = false; -#endif /* RFAL_FEATURE_LISTEN_MODE */ - -#if RFAL_FEATURE_WAKEUP_MODE - /* Initialize Wake-Up Mode */ - gRFAL.wum.state = RFAL_WUM_STATE_NOT_INIT; -#endif /* RFAL_FEATURE_WAKEUP_MODE */ - -#if RFAL_FEATURE_LOWPOWER_MODE - /* Initialize Low Power Mode */ - gRFAL.lpm.isRunning = false; -#endif /* RFAL_FEATURE_LOWPOWER_MODE */ - - /*******************************************************************************/ - /* Perform Automatic Calibration (if configured to do so). * - * Registers set by rfalSetAnalogConfig will tell rfalCalibrate what to perform*/ - rfalCalibrate(); - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalCalibrate(void) { - uint16_t resValue; - - /* Check if RFAL is not initialized */ - if(gRFAL.state == RFAL_STATE_IDLE) { - return ERR_WRONG_STATE; - } - - /*******************************************************************************/ - /* Perform ST25R3916 regulators and antenna calibration */ - /*******************************************************************************/ - - /* Automatic regulator adjustment only performed if not set manually on Analog Configs */ - if(st25r3916CheckReg( - ST25R3916_REG_REGULATOR_CONTROL, ST25R3916_REG_REGULATOR_CONTROL_reg_s, 0x00)) { - /* Adjust the regulators so that Antenna Calibrate has better Regulator values */ - st25r3916AdjustRegulators(&resValue); - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalAdjustRegulators(uint16_t* result) { - return st25r3916AdjustRegulators(result); -} - -/*******************************************************************************/ -void rfalSetUpperLayerCallback(rfalUpperLayerCallback pFunc) { - st25r3916IRQCallbackSet(pFunc); -} - -/*******************************************************************************/ -void rfalSetPreTxRxCallback(rfalPreTxRxCallback pFunc) { - gRFAL.callbacks.preTxRx = pFunc; -} - -/*******************************************************************************/ -void rfalSetPostTxRxCallback(rfalPostTxRxCallback pFunc) { - gRFAL.callbacks.postTxRx = pFunc; -} - -void rfal_set_state_changed_callback(RfalStateChangedCallback callback) { - gRFAL.callbacks.state_changed_cb = callback; -} - -void rfal_set_callback_context(void* context) { - gRFAL.callbacks.ctx = context; -} - -/*******************************************************************************/ -ReturnCode rfalDeinitialize(void) { - /* Deinitialize chip */ - st25r3916Deinitialize(); - - /* Set Analog configurations for deinitialization */ - rfalSetAnalogConfig((RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_DEINIT)); - - gRFAL.state = RFAL_STATE_IDLE; - return ERR_NONE; -} - -/*******************************************************************************/ -void rfalSetObsvMode(uint8_t txMode, uint8_t rxMode) { - gRFAL.conf.obsvModeTx = txMode; - gRFAL.conf.obsvModeRx = rxMode; -} - -/*******************************************************************************/ -void rfalGetObsvMode(uint8_t* txMode, uint8_t* rxMode) { - if(txMode != NULL) { - *txMode = gRFAL.conf.obsvModeTx; - } - - if(rxMode != NULL) { - *rxMode = gRFAL.conf.obsvModeRx; - } -} - -/*******************************************************************************/ -void rfalDisableObsvMode(void) { - gRFAL.conf.obsvModeTx = RFAL_OBSMODE_DISABLE; - gRFAL.conf.obsvModeRx = RFAL_OBSMODE_DISABLE; -} - -/*******************************************************************************/ -ReturnCode rfalSetMode(rfalMode mode, rfalBitRate txBR, rfalBitRate rxBR) { - /* Check if RFAL is not initialized */ - if(gRFAL.state == RFAL_STATE_IDLE) { - return ERR_WRONG_STATE; - } - - /* Check allowed bit rate value */ - if((txBR == RFAL_BR_KEEP) || (rxBR == RFAL_BR_KEEP)) { - return ERR_PARAM; - } - - switch(mode) { - /*******************************************************************************/ - case RFAL_MODE_POLL_NFCA: - - /* Disable wake up mode, if set */ - st25r3916ClrRegisterBits(ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_wu); - - /* Enable ISO14443A mode */ - st25r3916WriteRegister(ST25R3916_REG_MODE, ST25R3916_REG_MODE_om_iso14443a); - - /* Set Analog configurations for this mode and bit rate */ - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_TX)); - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX)); - break; - - /*******************************************************************************/ - case RFAL_MODE_POLL_NFCA_T1T: - /* Disable wake up mode, if set */ - st25r3916ClrRegisterBits(ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_wu); - - /* Enable Topaz mode */ - st25r3916WriteRegister(ST25R3916_REG_MODE, ST25R3916_REG_MODE_om_topaz); - - /* Set Analog configurations for this mode and bit rate */ - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_TX)); - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX)); - break; - - /*******************************************************************************/ - case RFAL_MODE_POLL_NFCB: - - /* Disable wake up mode, if set */ - st25r3916ClrRegisterBits(ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_wu); - - /* Enable ISO14443B mode */ - st25r3916WriteRegister(ST25R3916_REG_MODE, ST25R3916_REG_MODE_om_iso14443b); - - /* Set the EGT, SOF, EOF and EOF */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_ISO14443B_1, - (ST25R3916_REG_ISO14443B_1_egt_mask | ST25R3916_REG_ISO14443B_1_sof_mask | - ST25R3916_REG_ISO14443B_1_eof), - ((0U << ST25R3916_REG_ISO14443B_1_egt_shift) | ST25R3916_REG_ISO14443B_1_sof_0_10etu | - ST25R3916_REG_ISO14443B_1_sof_1_2etu | ST25R3916_REG_ISO14443B_1_eof_10etu)); - - /* Set the minimum TR1, SOF, EOF and EOF12 */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_ISO14443B_2, - (ST25R3916_REG_ISO14443B_2_tr1_mask | ST25R3916_REG_ISO14443B_2_no_sof | - ST25R3916_REG_ISO14443B_2_no_eof), - (ST25R3916_REG_ISO14443B_2_tr1_80fs80fs)); - - /* Set Analog configurations for this mode and bit rate */ - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_TX)); - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX)); - break; - - /*******************************************************************************/ - case RFAL_MODE_POLL_B_PRIME: - - /* Disable wake up mode, if set */ - st25r3916ClrRegisterBits(ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_wu); - - /* Enable ISO14443B mode */ - st25r3916WriteRegister(ST25R3916_REG_MODE, ST25R3916_REG_MODE_om_iso14443b); - - /* Set the EGT, SOF, EOF and EOF */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_ISO14443B_1, - (ST25R3916_REG_ISO14443B_1_egt_mask | ST25R3916_REG_ISO14443B_1_sof_mask | - ST25R3916_REG_ISO14443B_1_eof), - ((0U << ST25R3916_REG_ISO14443B_1_egt_shift) | ST25R3916_REG_ISO14443B_1_sof_0_10etu | - ST25R3916_REG_ISO14443B_1_sof_1_2etu | ST25R3916_REG_ISO14443B_1_eof_10etu)); - - /* Set the minimum TR1, EOF and EOF12 */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_ISO14443B_2, - (ST25R3916_REG_ISO14443B_2_tr1_mask | ST25R3916_REG_ISO14443B_2_no_sof | - ST25R3916_REG_ISO14443B_2_no_eof), - (ST25R3916_REG_ISO14443B_2_tr1_80fs80fs | ST25R3916_REG_ISO14443B_2_no_sof)); - - /* Set Analog configurations for this mode and bit rate */ - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_TX)); - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX)); - break; - - /*******************************************************************************/ - case RFAL_MODE_POLL_B_CTS: - - /* Disable wake up mode, if set */ - st25r3916ClrRegisterBits(ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_wu); - - /* Enable ISO14443B mode */ - st25r3916WriteRegister(ST25R3916_REG_MODE, ST25R3916_REG_MODE_om_iso14443b); - - /* Set the EGT, SOF, EOF and EOF */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_ISO14443B_1, - (ST25R3916_REG_ISO14443B_1_egt_mask | ST25R3916_REG_ISO14443B_1_sof_mask | - ST25R3916_REG_ISO14443B_1_eof), - ((0U << ST25R3916_REG_ISO14443B_1_egt_shift) | ST25R3916_REG_ISO14443B_1_sof_0_10etu | - ST25R3916_REG_ISO14443B_1_sof_1_2etu | ST25R3916_REG_ISO14443B_1_eof_10etu)); - - /* Set the minimum TR1, clear SOF, EOF and EOF12 */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_ISO14443B_2, - (ST25R3916_REG_ISO14443B_2_tr1_mask | ST25R3916_REG_ISO14443B_2_no_sof | - ST25R3916_REG_ISO14443B_2_no_eof), - (ST25R3916_REG_ISO14443B_2_tr1_80fs80fs | ST25R3916_REG_ISO14443B_2_no_sof | - ST25R3916_REG_ISO14443B_2_no_eof)); - - /* Set Analog configurations for this mode and bit rate */ - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_TX)); - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX)); - break; - - /*******************************************************************************/ - case RFAL_MODE_POLL_NFCF: - - /* Disable wake up mode, if set */ - st25r3916ClrRegisterBits(ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_wu); - - /* Enable FeliCa mode */ - st25r3916WriteRegister(ST25R3916_REG_MODE, ST25R3916_REG_MODE_om_felica); - - /* Set Analog configurations for this mode and bit rate */ - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCF | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_TX)); - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCF | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX)); - break; - - /*******************************************************************************/ - case RFAL_MODE_POLL_NFCV: - case RFAL_MODE_POLL_PICOPASS: - -#if !RFAL_FEATURE_NFCV - return ERR_DISABLED; -#else - - /* Disable wake up mode, if set */ - st25r3916ClrRegisterBits(ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_wu); - - /* Set Analog configurations for this mode and bit rate */ - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCV | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_TX)); - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCV | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX)); - break; - -#endif /* RFAL_FEATURE_NFCV */ - - /*******************************************************************************/ - case RFAL_MODE_POLL_ACTIVE_P2P: - - /* Set NFCIP1 active communication Initiator mode and Automatic Response RF Collision Avoidance to always after EOF */ - st25r3916WriteRegister( - ST25R3916_REG_MODE, - (ST25R3916_REG_MODE_targ_init | ST25R3916_REG_MODE_om_nfc | - ST25R3916_REG_MODE_nfc_ar_eof)); - - /* External Field Detector enabled as Automatics on rfalInitialize() */ - - /* Set NRT to start at end of TX (own) field */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_TIMER_EMV_CONTROL, - ST25R3916_REG_TIMER_EMV_CONTROL_nrt_nfc, - ST25R3916_REG_TIMER_EMV_CONTROL_nrt_nfc_off); - - /* Set GPT to start after end of TX, as GPT is used in active communication mode to timeout the field switching off */ - /* The field is turned off 37.76us after the end of the transmission Trfw */ - st25r3916SetStartGPTimer( - (uint16_t)rfalConv1fcTo8fc(RFAL_AP2P_FIELDOFF_TRFW), - ST25R3916_REG_TIMER_EMV_CONTROL_gptc_etx_nfc); - - /* Set PPon2 timer with the max time between our field Off and other peer field On : Tadt + (n x Trfw) */ - st25r3916WriteRegister( - ST25R3916_REG_PPON2, (uint8_t)rfalConv1fcTo64fc(RFAL_AP2P_FIELDON_TADTTRFW)); - - /* Set Analog configurations for this mode and bit rate */ - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_AP2P | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_TX)); - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_AP2P | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX)); - break; - - /*******************************************************************************/ - case RFAL_MODE_LISTEN_ACTIVE_P2P: - - /* Set NFCIP1 active communication Target mode and Automatic Response RF Collision Avoidance to always after EOF */ - st25r3916WriteRegister( - ST25R3916_REG_MODE, - (ST25R3916_REG_MODE_targ_targ | ST25R3916_REG_MODE_om_targ_nfcip | - ST25R3916_REG_MODE_nfc_ar_eof)); - - /* Set TARFG: 0 (75us+0ms=75us), as Target no Guard time needed */ - st25r3916WriteRegister(ST25R3916_REG_FIELD_ON_GT, 0U); - - /* External Field Detector enabled as Automatics on rfalInitialize() */ - - /* Set NRT to start at end of TX (own) field */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_TIMER_EMV_CONTROL, - ST25R3916_REG_TIMER_EMV_CONTROL_nrt_nfc, - ST25R3916_REG_TIMER_EMV_CONTROL_nrt_nfc_off); - - /* Set GPT to start after end of TX, as GPT is used in active communication mode to timeout the field switching off */ - /* The field is turned off 37.76us after the end of the transmission Trfw */ - st25r3916SetStartGPTimer( - (uint16_t)rfalConv1fcTo8fc(RFAL_AP2P_FIELDOFF_TRFW), - ST25R3916_REG_TIMER_EMV_CONTROL_gptc_etx_nfc); - - /* Set PPon2 timer with the max time between our field Off and other peer field On : Tadt + (n x Trfw) */ - st25r3916WriteRegister( - ST25R3916_REG_PPON2, (uint8_t)rfalConv1fcTo64fc(RFAL_AP2P_FIELDON_TADTTRFW)); - - /* Set Analog configurations for this mode and bit rate */ - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_AP2P | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_TX)); - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_AP2P | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX)); - break; - - /*******************************************************************************/ - case RFAL_MODE_LISTEN_NFCA: - - /* Disable wake up mode, if set */ - st25r3916ClrRegisterBits(ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_wu); - - /* Enable Passive Target NFC-A mode, disable any Collision Avoidance */ - st25r3916WriteRegister( - ST25R3916_REG_MODE, - (ST25R3916_REG_MODE_targ | ST25R3916_REG_MODE_om_targ_nfca | - ST25R3916_REG_MODE_nfc_ar_off)); - - /* Set Analog configurations for this mode */ - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_NFCA | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_TX)); - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_NFCA | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX)); - break; - - /*******************************************************************************/ - case RFAL_MODE_LISTEN_NFCF: - - /* Disable wake up mode, if set */ - st25r3916ClrRegisterBits(ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_wu); - - /* Enable Passive Target NFC-F mode, disable any Collision Avoidance */ - st25r3916WriteRegister( - ST25R3916_REG_MODE, - (ST25R3916_REG_MODE_targ | ST25R3916_REG_MODE_om_targ_nfcf | - ST25R3916_REG_MODE_nfc_ar_off)); - - /* Set Analog configurations for this mode */ - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_NFCF | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_TX)); - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_NFCF | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_RX)); - break; - - /*******************************************************************************/ - case RFAL_MODE_LISTEN_NFCB: - return ERR_NOTSUPP; - - /*******************************************************************************/ - default: - return ERR_NOT_IMPLEMENTED; - } - - /* Set state as STATE_MODE_SET only if not initialized yet (PSL) */ - gRFAL.state = ((gRFAL.state < RFAL_STATE_MODE_SET) ? RFAL_STATE_MODE_SET : gRFAL.state); - gRFAL.mode = mode; - - /* Apply the given bit rate */ - return rfalSetBitRate(txBR, rxBR); -} - -/*******************************************************************************/ -rfalMode rfalGetMode(void) { - return gRFAL.mode; -} - -/*******************************************************************************/ -ReturnCode rfalSetBitRate(rfalBitRate txBR, rfalBitRate rxBR) { - ReturnCode ret; - - /* Check if RFAL is not initialized */ - if(gRFAL.state == RFAL_STATE_IDLE) { - return ERR_WRONG_STATE; - } - - /* Store the new Bit Rates */ - gRFAL.txBR = ((txBR == RFAL_BR_KEEP) ? gRFAL.txBR : txBR); - gRFAL.rxBR = ((rxBR == RFAL_BR_KEEP) ? gRFAL.rxBR : rxBR); - - /* Update the bitrate reg if not in NFCV mode (streaming) */ - if((RFAL_MODE_POLL_NFCV != gRFAL.mode) && (RFAL_MODE_POLL_PICOPASS != gRFAL.mode)) { - /* Set bit rate register */ - EXIT_ON_ERR(ret, st25r3916SetBitrate((uint8_t)gRFAL.txBR, (uint8_t)gRFAL.rxBR)); - } - - switch(gRFAL.mode) { - /*******************************************************************************/ - case RFAL_MODE_POLL_NFCA: - case RFAL_MODE_POLL_NFCA_T1T: - - /* Set Analog configurations for this bit rate */ - rfalSetAnalogConfig((RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_POLL_COMMON)); - rfalSetAnalogConfig( (rfalAnalogConfigId)(RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | rfalConvBR2ACBR(gRFAL.txBR) | RFAL_ANALOG_CONFIG_TX ) ); - rfalSetAnalogConfig( (rfalAnalogConfigId)(RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | rfalConvBR2ACBR(gRFAL.rxBR) | RFAL_ANALOG_CONFIG_RX ) ); - break; - - /*******************************************************************************/ - case RFAL_MODE_POLL_NFCB: - case RFAL_MODE_POLL_B_PRIME: - case RFAL_MODE_POLL_B_CTS: - - /* Set Analog configurations for this bit rate */ - rfalSetAnalogConfig((RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_POLL_COMMON)); - rfalSetAnalogConfig( (rfalAnalogConfigId)(RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | rfalConvBR2ACBR(gRFAL.txBR) | RFAL_ANALOG_CONFIG_TX ) ); - rfalSetAnalogConfig( (rfalAnalogConfigId)(RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCB | rfalConvBR2ACBR(gRFAL.rxBR) | RFAL_ANALOG_CONFIG_RX ) ); - break; - - /*******************************************************************************/ - case RFAL_MODE_POLL_NFCF: - - /* Set Analog configurations for this bit rate */ - rfalSetAnalogConfig((RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_POLL_COMMON)); - rfalSetAnalogConfig( (rfalAnalogConfigId)(RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCF | rfalConvBR2ACBR(gRFAL.txBR) | RFAL_ANALOG_CONFIG_TX ) ); - rfalSetAnalogConfig( (rfalAnalogConfigId)(RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCF | rfalConvBR2ACBR(gRFAL.rxBR) | RFAL_ANALOG_CONFIG_RX ) ); - break; - - /*******************************************************************************/ - case RFAL_MODE_POLL_NFCV: - case RFAL_MODE_POLL_PICOPASS: - -#if !RFAL_FEATURE_NFCV - return ERR_DISABLED; -#else - - if(((gRFAL.rxBR != RFAL_BR_26p48) && (gRFAL.rxBR != RFAL_BR_52p97)) || - ((gRFAL.txBR != RFAL_BR_1p66) && (gRFAL.txBR != RFAL_BR_26p48))) { - return ERR_PARAM; - } - - { - const struct iso15693StreamConfig* isoStreamConfig; - struct st25r3916StreamConfig streamConf; - iso15693PhyConfig_t config; - - config.coding = - ((gRFAL.txBR == RFAL_BR_1p66) ? ISO15693_VCD_CODING_1_256 : - ISO15693_VCD_CODING_1_4); - switch(gRFAL.rxBR) { - case RFAL_BR_52p97: - config.speedMode = 1; - break; - default: - config.speedMode = 0; - break; - } - - iso15693PhyConfigure(&config, &isoStreamConfig); - - /* MISRA 11.3 - Cannot point directly into different object type, copy to local var */ - streamConf.din = isoStreamConfig->din; - streamConf.dout = isoStreamConfig->dout; - streamConf.report_period_length = isoStreamConfig->report_period_length; - streamConf.useBPSK = isoStreamConfig->useBPSK; - st25r3916StreamConfigure(&streamConf); - } - - /* Set Analog configurations for this bit rate */ - rfalSetAnalogConfig((RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_POLL_COMMON)); - rfalSetAnalogConfig( (rfalAnalogConfigId)(RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCV | rfalConvBR2ACBR(gRFAL.txBR) | RFAL_ANALOG_CONFIG_TX ) ); - rfalSetAnalogConfig( (rfalAnalogConfigId)(RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCV | rfalConvBR2ACBR(gRFAL.rxBR) | RFAL_ANALOG_CONFIG_RX ) ); - break; - -#endif /* RFAL_FEATURE_NFCV */ - - /*******************************************************************************/ - case RFAL_MODE_POLL_ACTIVE_P2P: - - /* Set Analog configurations for this bit rate */ - rfalSetAnalogConfig((RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_POLL_COMMON)); - rfalSetAnalogConfig( (rfalAnalogConfigId)(RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_AP2P | rfalConvBR2ACBR(gRFAL.txBR) | RFAL_ANALOG_CONFIG_TX ) ); - rfalSetAnalogConfig( (rfalAnalogConfigId)(RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_AP2P | rfalConvBR2ACBR(gRFAL.rxBR) | RFAL_ANALOG_CONFIG_RX ) ); - break; - - /*******************************************************************************/ - case RFAL_MODE_LISTEN_ACTIVE_P2P: - - /* Set Analog configurations for this bit rate */ - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_LISTEN_COMMON)); - rfalSetAnalogConfig( (rfalAnalogConfigId)(RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_AP2P | rfalConvBR2ACBR(gRFAL.txBR) | RFAL_ANALOG_CONFIG_TX ) ); - rfalSetAnalogConfig( (rfalAnalogConfigId)(RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_AP2P | rfalConvBR2ACBR(gRFAL.rxBR) | RFAL_ANALOG_CONFIG_RX ) ); - break; - - /*******************************************************************************/ - case RFAL_MODE_LISTEN_NFCA: - - /* Set Analog configurations for this bit rate */ - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_LISTEN_COMMON)); - rfalSetAnalogConfig( (rfalAnalogConfigId)(RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_NFCA | rfalConvBR2ACBR(gRFAL.txBR) | RFAL_ANALOG_CONFIG_TX ) ); - rfalSetAnalogConfig( (rfalAnalogConfigId)(RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_NFCA | rfalConvBR2ACBR(gRFAL.rxBR) | RFAL_ANALOG_CONFIG_RX ) ); - break; - - /*******************************************************************************/ - case RFAL_MODE_LISTEN_NFCF: - - /* Set Analog configurations for this bit rate */ - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_LISTEN_COMMON)); - rfalSetAnalogConfig( (rfalAnalogConfigId)(RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_NFCF | rfalConvBR2ACBR(gRFAL.txBR) | RFAL_ANALOG_CONFIG_TX ) ); - rfalSetAnalogConfig( (rfalAnalogConfigId)(RFAL_ANALOG_CONFIG_LISTEN | RFAL_ANALOG_CONFIG_TECH_NFCF | rfalConvBR2ACBR(gRFAL.rxBR) | RFAL_ANALOG_CONFIG_RX ) ); - break; - - /*******************************************************************************/ - case RFAL_MODE_LISTEN_NFCB: - case RFAL_MODE_NONE: - return ERR_WRONG_STATE; - - /*******************************************************************************/ - default: - return ERR_NOT_IMPLEMENTED; - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalGetBitRate(rfalBitRate* txBR, rfalBitRate* rxBR) { - if((gRFAL.state == RFAL_STATE_IDLE) || (gRFAL.mode == RFAL_MODE_NONE)) { - return ERR_WRONG_STATE; - } - - if(txBR != NULL) { - *txBR = gRFAL.txBR; - } - - if(rxBR != NULL) { - *rxBR = gRFAL.rxBR; - } - - return ERR_NONE; -} - -/*******************************************************************************/ -void rfalSetErrorHandling(rfalEHandling eHandling) { - switch(eHandling) { - case RFAL_ERRORHANDLING_NFC: - case RFAL_ERRORHANDLING_NONE: - st25r3916ClrRegisterBits(ST25R3916_REG_EMD_SUP_CONF, ST25R3916_REG_EMD_SUP_CONF_emd_emv); - break; - - case RFAL_ERRORHANDLING_EMVCO: - /* MISRA 16.4: no empty default statement (in case RFAL_SW_EMD is defined) */ -#ifndef RFAL_SW_EMD - st25r3916ModifyRegister( - ST25R3916_REG_EMD_SUP_CONF, - (ST25R3916_REG_EMD_SUP_CONF_emd_emv | ST25R3916_REG_EMD_SUP_CONF_emd_thld_mask), - (ST25R3916_REG_EMD_SUP_CONF_emd_emv_on | RFAL_EMVCO_RX_MAXLEN)); -#endif /* RFAL_SW_EMD */ - break; - default: - /* MISRA 16.4: no empty default statement (a comment being enough) */ - break; - } - - gRFAL.conf.eHandling = eHandling; -} - -/*******************************************************************************/ -rfalEHandling rfalGetErrorHandling(void) { - return gRFAL.conf.eHandling; -} - -/*******************************************************************************/ -void rfalSetFDTPoll(uint32_t FDTPoll) { - gRFAL.timings.FDTPoll = MIN(FDTPoll, RFAL_ST25R3916_GPT_MAX_1FC); -} - -/*******************************************************************************/ -uint32_t rfalGetFDTPoll(void) { - return gRFAL.timings.FDTPoll; -} - -/*******************************************************************************/ -void rfalSetFDTListen(uint32_t FDTListen) { - gRFAL.timings.FDTListen = MIN(FDTListen, RFAL_ST25R3916_MRT_MAX_1FC); -} - -/*******************************************************************************/ -uint32_t rfalGetFDTListen(void) { - return gRFAL.timings.FDTListen; -} - -/*******************************************************************************/ -void rfalSetGT(uint32_t GT) { - gRFAL.timings.GT = MIN(GT, RFAL_ST25R3916_GT_MAX_1FC); -} - -/*******************************************************************************/ -uint32_t rfalGetGT(void) { - return gRFAL.timings.GT; -} - -/*******************************************************************************/ -bool rfalIsGTExpired(void) { - if(gRFAL.tmr.GT != RFAL_TIMING_NONE) { - if(!rfalTimerisExpired(gRFAL.tmr.GT)) { - return false; - } - } - return true; -} - -/*******************************************************************************/ -ReturnCode rfalFieldOnAndStartGT(void) { - ReturnCode ret; - - /* Check if RFAL has been initialized (Oscillator should be running) and also - * if a direct register access has been performed and left the Oscillator Off */ - if(!st25r3916IsOscOn() || (gRFAL.state < RFAL_STATE_INIT)) { - return ERR_WRONG_STATE; - } - - ret = ERR_NONE; - - /* Set Analog configurations for Field On event */ - rfalSetAnalogConfig((RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_FIELD_ON)); - - /*******************************************************************************/ - /* Perform collision avoidance and turn field On if not already On */ - if(!st25r3916IsTxEnabled() || !gRFAL.field) { - /* Set TARFG: 0 (75us+0ms=75us), GT is fulfilled using a SW timer */ - st25r3916WriteRegister(ST25R3916_REG_FIELD_ON_GT, 0U); - - /* Use Thresholds set by AnalogConfig */ - ret = st25r3916PerformCollisionAvoidance( - ST25R3916_CMD_INITIAL_RF_COLLISION, - ST25R3916_THRESHOLD_DO_NOT_SET, - ST25R3916_THRESHOLD_DO_NOT_SET, - gRFAL.timings.nTRFW); - - /* n * TRFW timing shall vary Activity 2.1 3.3.1.1 */ - gRFAL.timings.nTRFW = rfalGennTRFW(gRFAL.timings.nTRFW); - - gRFAL.field = st25r3916IsTxEnabled(); //(ret == ERR_NONE); - - /* Only turn on Receiver and Transmitter if field was successfully turned On */ - if(gRFAL.field) { - st25r3916TxRxOn(); /* Enable Tx and Rx (Tx is already On)*/ - } - } - - /*******************************************************************************/ - /* Start GT timer in case the GT value is set */ - if((gRFAL.timings.GT != RFAL_TIMING_NONE)) { - /* Ensure that a SW timer doesn't have a lower value then the minimum */ - rfalTimerStart( - gRFAL.tmr.GT, rfalConv1fcToMs(MAX((gRFAL.timings.GT), RFAL_ST25R3916_GT_MIN_1FC))); - } - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalFieldOff(void) { - /* Check whether a TxRx is not yet finished */ - if(gRFAL.TxRx.state != RFAL_TXRX_STATE_IDLE) { - rfalCleanupTransceive(); - } - - /* Disable Tx and Rx */ - st25r3916TxRxOff(); - - /* Set Analog configurations for Field Off event */ - rfalSetAnalogConfig((RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_FIELD_OFF)); - gRFAL.field = false; - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalStartTransceive(const rfalTransceiveContext* ctx) { - uint32_t FxTAdj; /* FWT or FDT adjustment calculation */ - - /* Check for valid parameters */ - if(ctx == NULL) { - return ERR_PARAM; - } - - /* Ensure that RFAL is already Initialized and the mode has been set */ - if((gRFAL.state >= RFAL_STATE_MODE_SET) /*&& (gRFAL.TxRx.state == RFAL_TXRX_STATE_INIT )*/) { - /*******************************************************************************/ - /* Check whether the field is already On, otherwise no TXE will be received */ - if(!st25r3916IsTxEnabled() && - (!rfalIsModePassiveListen(gRFAL.mode) && (ctx->txBuf != NULL))) { - return ERR_WRONG_STATE; - } - - gRFAL.TxRx.ctx = *ctx; - - /*******************************************************************************/ - if(gRFAL.timings.FDTListen != RFAL_TIMING_NONE) { - /* Calculate MRT adjustment accordingly to the current mode */ - FxTAdj = RFAL_FDT_LISTEN_MRT_ADJUSTMENT; - if(gRFAL.mode == RFAL_MODE_POLL_NFCA) { - FxTAdj += (uint32_t)RFAL_FDT_LISTEN_A_ADJUSTMENT; - } - if(gRFAL.mode == RFAL_MODE_POLL_NFCA_T1T) { - FxTAdj += (uint32_t)RFAL_FDT_LISTEN_A_ADJUSTMENT; - } - if(gRFAL.mode == RFAL_MODE_POLL_NFCB) { - FxTAdj += (uint32_t)RFAL_FDT_LISTEN_B_ADJUSTMENT; - } - if(gRFAL.mode == RFAL_MODE_POLL_NFCV) { - FxTAdj += (uint32_t)RFAL_FDT_LISTEN_V_ADJUSTMENT; - } - - /* Ensure that MRT is using 64/fc steps */ - st25r3916ClrRegisterBits( - ST25R3916_REG_TIMER_EMV_CONTROL, ST25R3916_REG_TIMER_EMV_CONTROL_mrt_step); - - /* If Correlator is being used further adjustment is required for NFCB */ - if((st25r3916CheckReg(ST25R3916_REG_AUX, ST25R3916_REG_AUX_dis_corr, 0x00U)) && - (gRFAL.mode == RFAL_MODE_POLL_NFCB)) { - FxTAdj += (uint32_t) - RFAL_FDT_LISTEN_B_ADJT_CORR; /* Reduce FDT(Listen) */ - st25r3916SetRegisterBits( - ST25R3916_REG_CORR_CONF1, - ST25R3916_REG_CORR_CONF1_corr_s3); /* Ensure BPSK start to 33 pilot pulses */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_SUBC_START_TIME, - ST25R3916_REG_SUBC_START_TIME_sst_mask, - RFAL_FDT_LISTEN_B_ADJT_CORR_SST); /* Set sst */ - } - - /* Set Minimum FDT(Listen) in which PICC is not allowed to send a response */ - st25r3916WriteRegister( - ST25R3916_REG_MASK_RX_TIMER, - (uint8_t)rfalConv1fcTo64fc( - (FxTAdj > gRFAL.timings.FDTListen) ? RFAL_ST25R3916_MRT_MIN_1FC : - (gRFAL.timings.FDTListen - FxTAdj))); - } - - /*******************************************************************************/ - /* FDT Poll will be loaded in rfalPrepareTransceive() once the previous was expired */ - - /*******************************************************************************/ - if((gRFAL.TxRx.ctx.fwt != RFAL_FWT_NONE) && (gRFAL.TxRx.ctx.fwt != 0U)) { - /* Ensure proper timing configuration */ - if(gRFAL.timings.FDTListen >= gRFAL.TxRx.ctx.fwt) { - return ERR_PARAM; - } - - FxTAdj = RFAL_FWT_ADJUSTMENT; - if(gRFAL.mode == RFAL_MODE_POLL_NFCA) { - FxTAdj += (uint32_t)RFAL_FWT_A_ADJUSTMENT; - } - if(gRFAL.mode == RFAL_MODE_POLL_NFCA_T1T) { - FxTAdj += (uint32_t)RFAL_FWT_A_ADJUSTMENT; - } - if(gRFAL.mode == RFAL_MODE_POLL_NFCB) { - FxTAdj += (uint32_t)RFAL_FWT_B_ADJUSTMENT; - } - if((gRFAL.mode == RFAL_MODE_POLL_NFCF) || (gRFAL.mode == RFAL_MODE_POLL_ACTIVE_P2P)) { - FxTAdj += - (uint32_t)((gRFAL.txBR == RFAL_BR_212) ? RFAL_FWT_F_212_ADJUSTMENT : RFAL_FWT_F_424_ADJUSTMENT); - } - - /* Ensure that the given FWT doesn't exceed NRT maximum */ - gRFAL.TxRx.ctx.fwt = MIN((gRFAL.TxRx.ctx.fwt + FxTAdj), RFAL_ST25R3916_NRT_MAX_1FC); - - /* Set FWT in the NRT */ - st25r3916SetNoResponseTime(rfalConv1fcTo64fc(gRFAL.TxRx.ctx.fwt)); - } else { - /* Disable NRT, no NRE will be triggered, therefore wait endlessly for Rx */ - st25r3916SetNoResponseTime(RFAL_ST25R3916_NRT_DISABLED); - } - - gRFAL.state = RFAL_STATE_TXRX; - gRFAL.TxRx.state = RFAL_TXRX_STATE_TX_IDLE; - gRFAL.TxRx.status = ERR_BUSY; - -#if RFAL_FEATURE_NFCV - /*******************************************************************************/ - if((RFAL_MODE_POLL_NFCV == gRFAL.mode) || - (RFAL_MODE_POLL_PICOPASS == - gRFAL.mode)) { /* Exchange receive buffer with internal buffer */ - gRFAL.nfcvData.origCtx = gRFAL.TxRx.ctx; - - gRFAL.TxRx.ctx.rxBuf = - ((gRFAL.nfcvData.origCtx.rxBuf != NULL) ? gRFAL.nfcvData.codingBuffer : NULL); - gRFAL.TxRx.ctx.rxBufLen = - (uint16_t)rfalConvBytesToBits(sizeof(gRFAL.nfcvData.codingBuffer)); - gRFAL.TxRx.ctx.flags = - (uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP | - (uint32_t)RFAL_TXRX_FLAGS_NFCIP1_OFF | - (uint32_t)(gRFAL.nfcvData.origCtx.flags & (uint32_t)RFAL_TXRX_FLAGS_AGC_OFF) | - (uint32_t)RFAL_TXRX_FLAGS_PAR_RX_KEEP | (uint32_t)RFAL_TXRX_FLAGS_PAR_TX_NONE; - - /* In NFCV a TxRx with a valid txBuf and txBufSize==0 indicates to send an EOF */ - /* Skip logic below that would go directly into receive */ - if(gRFAL.TxRx.ctx.txBuf != NULL) { - return ERR_NONE; - } - } -#endif /* RFAL_FEATURE_NFCV */ - - /*******************************************************************************/ - /* Check if the Transceive start performing Tx or goes directly to Rx */ - if((gRFAL.TxRx.ctx.txBuf == NULL) || (gRFAL.TxRx.ctx.txBufLen == 0U)) { - /* Clear FIFO, Clear and Enable the Interrupts */ - rfalPrepareTransceive(); - - /* In AP2P check the field status */ - if(rfalIsModeActiveComm(gRFAL.mode)) { - /* Disable our field upon a Rx reEnable, and start PPON2 manually */ - st25r3916TxOff(); - st25r3916ExecuteCommand(ST25R3916_CMD_START_PPON2_TIMER); - } - - /* No Tx done, enable the Receiver */ - st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA); - - /* Start NRT manually, if FWT = 0 (wait endlessly for Rx) chip will ignore anyhow */ - st25r3916ExecuteCommand(ST25R3916_CMD_START_NO_RESPONSE_TIMER); - - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_IDLE; - } - - return ERR_NONE; - } - - return ERR_WRONG_STATE; -} - -/*******************************************************************************/ -bool rfalIsTransceiveInTx(void) { - return ( - (gRFAL.TxRx.state >= RFAL_TXRX_STATE_TX_IDLE) && - (gRFAL.TxRx.state < RFAL_TXRX_STATE_RX_IDLE)); -} - -/*******************************************************************************/ -bool rfalIsTransceiveInRx(void) { - return (gRFAL.TxRx.state >= RFAL_TXRX_STATE_RX_IDLE); -} - -/*******************************************************************************/ -ReturnCode rfalTransceiveBlockingTx( - uint8_t* txBuf, - uint16_t txBufLen, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* actLen, - uint32_t flags, - uint32_t fwt) { - ReturnCode ret; - rfalTransceiveContext ctx; - - rfalCreateByteFlagsTxRxContext(ctx, txBuf, txBufLen, rxBuf, rxBufLen, actLen, flags, fwt); - EXIT_ON_ERR(ret, rfalStartTransceive(&ctx)); - - return rfalTransceiveRunBlockingTx(); -} - -ReturnCode rfalTransceiveBitsBlockingTx( - uint8_t* txBuf, - uint16_t txBufLen, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* actLen, - uint32_t flags, - uint32_t fwt) { - ReturnCode ret; - rfalTransceiveContext ctx = { - .rxBuf = rxBuf, - .rxBufLen = rxBufLen, - .rxRcvdLen = actLen, - .txBuf = txBuf, - .txBufLen = txBufLen, - .flags = flags, - .fwt = fwt, - }; - - EXIT_ON_ERR(ret, rfalStartTransceive(&ctx)); - - return rfalTransceiveRunBlockingTx(); -} - -/*******************************************************************************/ -static ReturnCode rfalTransceiveRunBlockingTx(void) { - ReturnCode ret; - - do { - rfalWorker(); - ret = rfalGetTransceiveStatus(); - } while(rfalIsTransceiveInTx() && (ret == ERR_BUSY)); - - if(rfalIsTransceiveInRx()) { - return ERR_NONE; - } - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalTransceiveBlockingRx(void) { - ReturnCode ret; - - do { - rfalWorker(); - ret = rfalGetTransceiveStatus(); - } while(rfalIsTransceiveInRx() && (ret == ERR_BUSY)); - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalTransceiveBlockingTxRx( - uint8_t* txBuf, - uint16_t txBufLen, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* actLen, - uint32_t flags, - uint32_t fwt) { - ReturnCode ret; - - EXIT_ON_ERR( - ret, rfalTransceiveBlockingTx(txBuf, txBufLen, rxBuf, rxBufLen, actLen, flags, fwt)); - ret = rfalTransceiveBlockingRx(); - - /* Convert received bits to bytes */ - if(actLen != NULL) { - *actLen = rfalConvBitsToBytes(*actLen); - } - - return ret; -} - -/*******************************************************************************/ -static ReturnCode rfalRunTransceiveWorker(void) { - if(gRFAL.state == RFAL_STATE_TXRX) { - /*******************************************************************************/ - /* Check Transceive Sanity Timer has expired */ - if(gRFAL.tmr.txRx != RFAL_TIMING_NONE) { - if(rfalTimerisExpired(gRFAL.tmr.txRx)) { - /* If sanity timer has expired abort ongoing transceive and signal error */ - gRFAL.TxRx.status = ERR_IO; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_FAIL; - } - } - - /*******************************************************************************/ - /* Run Tx or Rx state machines */ - if(rfalIsTransceiveInTx()) { - rfalTransceiveTx(); - return rfalGetTransceiveStatus(); - } - if(rfalIsTransceiveInRx()) { - rfalTransceiveRx(); - return rfalGetTransceiveStatus(); - } - } - return ERR_WRONG_STATE; -} - -/*******************************************************************************/ -rfalTransceiveState rfalGetTransceiveState(void) { - return gRFAL.TxRx.state; -} - -/*******************************************************************************/ -ReturnCode rfalGetTransceiveStatus(void) { - return ((gRFAL.TxRx.state == RFAL_TXRX_STATE_IDLE) ? gRFAL.TxRx.status : ERR_BUSY); -} - -/*******************************************************************************/ -ReturnCode rfalGetTransceiveRSSI(uint16_t* rssi) { - uint16_t amRSSI; - uint16_t pmRSSI; - bool isSumMode; - - if(rssi == NULL) { - return ERR_PARAM; - } - - st25r3916GetRSSI(&amRSSI, &pmRSSI); - - /* Check if Correlator Summation mode is being used */ - isSumMode = - (st25r3916CheckReg( - ST25R3916_REG_CORR_CONF1, - ST25R3916_REG_CORR_CONF1_corr_s4, - ST25R3916_REG_CORR_CONF1_corr_s4) ? - st25r3916CheckReg(ST25R3916_REG_AUX, ST25R3916_REG_AUX_dis_corr, 0x00) : - false); - if(isSumMode) { - /*******************************************************************************/ - /* Using SQRT from math.h and float. If due to compiler, resources or performance - * issue this cannot be used, other approaches can be foreseen with less accuracy: - * Use a simpler sqrt algorithm - * *rssi = MAX( amRSSI, pmRSSI ); - * *rssi = ( (amRSSI + pmRSSI) / 2); - */ - *rssi = (uint16_t)sqrt( - ((double)amRSSI * (double)amRSSI) + - ((double)pmRSSI * - (double) - pmRSSI)); /* PRQA S 5209 # MISRA 4.9 - External function (sqrt()) requires double */ - } else { - /* Check which channel was used */ - *rssi = - (st25r3916CheckReg( - ST25R3916_REG_AUX_DISPLAY, - ST25R3916_REG_AUX_DISPLAY_a_cha, - ST25R3916_REG_AUX_DISPLAY_a_cha) ? - pmRSSI : - amRSSI); - } - return ERR_NONE; -} - -/*******************************************************************************/ -void rfalWorker(void) { - platformProtectWorker(); /* Protect RFAL Worker/Task/Process */ - - switch(gRFAL.state) { - case RFAL_STATE_TXRX: - rfalRunTransceiveWorker(); - break; - -#if RFAL_FEATURE_LISTEN_MODE - case RFAL_STATE_LM: - rfalRunListenModeWorker(); - break; -#endif /* RFAL_FEATURE_LISTEN_MODE */ - -#if RFAL_FEATURE_WAKEUP_MODE - case RFAL_STATE_WUM: - rfalRunWakeUpModeWorker(); - break; -#endif /* RFAL_FEATURE_WAKEUP_MODE */ - - /* Nothing to be done */ - default: - /* MISRA 16.4: no empty default statement (a comment being enough) */ - break; - } - - platformUnprotectWorker(); /* Unprotect RFAL Worker/Task/Process */ -} - -/*******************************************************************************/ -static void rfalErrorHandling(void) { - uint16_t fifoBytesToRead; - - fifoBytesToRead = rfalFIFOStatusGetNumBytes(); - -#ifdef RFAL_SW_EMD - /*******************************************************************************/ - /* EMVCo */ - /*******************************************************************************/ - if(gRFAL.conf.eHandling == RFAL_ERRORHANDLING_EMVCO) { - bool rxHasIncParError; - - /*******************************************************************************/ - /* EMD Handling - NFC Forum Digital 1.1 4.1.1.1 ; EMVCo v2.5 4.9.2 */ - /* ReEnable the receiver on frames with a length < 4 bytes, upon: */ - /* - Collision or Framing error detected */ - /* - Residual bits are detected (hard framing error) */ - /* - Parity error */ - /* - CRC error */ - /*******************************************************************************/ - - /* Check if reception has incomplete bytes or parity error */ - rxHasIncParError = - (rfalFIFOStatusIsIncompleteByte() ? true : - rfalFIFOStatusIsMissingPar()); /* MISRA 13.5 */ - - /* In case there are residual bits decrement FIFO bytes */ - /* Ensure FIFO contains some byte as the FIFO might be empty upon Framing errors */ - if((fifoBytesToRead > 0U) && rxHasIncParError) { - fifoBytesToRead--; - } - - if(((gRFAL.fifo.bytesTotal + fifoBytesToRead) < RFAL_EMVCO_RX_MAXLEN) && - ((gRFAL.TxRx.status == ERR_RF_COLLISION) || (gRFAL.TxRx.status == ERR_FRAMING) || - (gRFAL.TxRx.status == ERR_PAR) || (gRFAL.TxRx.status == ERR_CRC) || - rxHasIncParError)) { - /* Ignore this reception, ReEnable receiver which also clears the FIFO */ - st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA); - - /* Ensure that the NRT has not expired meanwhile */ - if(st25r3916CheckReg( - ST25R3916_REG_NFCIP1_BIT_RATE, ST25R3916_REG_NFCIP1_BIT_RATE_nrt_on, 0x00)) { - if(st25r3916CheckReg( - ST25R3916_REG_AUX_DISPLAY, ST25R3916_REG_AUX_DISPLAY_rx_act, 0x00)) { - /* Abort reception */ - st25r3916ExecuteCommand(ST25R3916_CMD_MASK_RECEIVE_DATA); - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_FAIL; - return; - } - } - - rfalFIFOStatusClear(); - gRFAL.fifo.bytesTotal = 0; - gRFAL.TxRx.status = ERR_BUSY; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_WAIT_RXS; - } - return; - } -#endif - - /*******************************************************************************/ - /* ISO14443A Mode */ - /*******************************************************************************/ - if(gRFAL.mode == RFAL_MODE_POLL_NFCA) { - /*******************************************************************************/ - /* If we received a frame with a incomplete byte we`ll raise a specific error * - * ( support for T2T 4 bit ACK / NAK, MIFARE and Kovio ) */ - /*******************************************************************************/ - if((gRFAL.TxRx.status == ERR_PAR) || (gRFAL.TxRx.status == ERR_CRC)) { - if(rfalFIFOStatusIsIncompleteByte()) { - st25r3916ReadFifo((uint8_t*)(gRFAL.TxRx.ctx.rxBuf), fifoBytesToRead); - if((gRFAL.TxRx.ctx.rxRcvdLen) != NULL) { - *gRFAL.TxRx.ctx.rxRcvdLen = rfalFIFOGetNumIncompleteBits(); - } - - gRFAL.TxRx.status = ERR_INCOMPLETE_BYTE; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_FAIL; - } - } - } -} - -/*******************************************************************************/ -static void rfalCleanupTransceive(void) { - /*******************************************************************************/ - /* Transceive flags */ - /*******************************************************************************/ - - /* Restore default settings on NFCIP1 mode, Receiving parity + CRC bits and manual Tx Parity*/ - st25r3916ClrRegisterBits( - ST25R3916_REG_ISO14443A_NFC, - (ST25R3916_REG_ISO14443A_NFC_no_tx_par | ST25R3916_REG_ISO14443A_NFC_no_rx_par | - ST25R3916_REG_ISO14443A_NFC_nfc_f0)); - - /* Restore AGC enabled */ - st25r3916SetRegisterBits(ST25R3916_REG_RX_CONF2, ST25R3916_REG_RX_CONF2_agc_en); - - /*******************************************************************************/ - - /*******************************************************************************/ - /* Transceive timers */ - /*******************************************************************************/ - rfalTimerDestroy(gRFAL.tmr.txRx); - rfalTimerDestroy(gRFAL.tmr.RXE); - gRFAL.tmr.txRx = RFAL_TIMING_NONE; - gRFAL.tmr.RXE = RFAL_TIMING_NONE; - /*******************************************************************************/ - - /*******************************************************************************/ - /* Execute Post Transceive Callback */ - /*******************************************************************************/ - if(gRFAL.callbacks.postTxRx != NULL) { - gRFAL.callbacks.postTxRx(gRFAL.callbacks.ctx); - } - /*******************************************************************************/ -} - -/*******************************************************************************/ -static void rfalPrepareTransceive(void) { - uint32_t maskInterrupts; - uint8_t reg; - - /* If we are in RW or AP2P mode */ - if(!rfalIsModePassiveListen(gRFAL.mode)) { - /* Reset receive logic with STOP command */ - st25r3916ExecuteCommand(ST25R3916_CMD_STOP); - - /* Reset Rx Gain */ - st25r3916ExecuteCommand(ST25R3916_CMD_RESET_RXGAIN); - } else { - /* In Passive Listen Mode do not use STOP as it stops FDT timer */ - st25r3916ExecuteCommand(ST25R3916_CMD_CLEAR_FIFO); - } - - /*******************************************************************************/ - /* FDT Poll */ - /*******************************************************************************/ - if(rfalIsModePassiveComm(gRFAL.mode)) /* Passive Comms */ - { - /* In Passive communications General Purpose Timer is used to measure FDT Poll */ - if(gRFAL.timings.FDTPoll != RFAL_TIMING_NONE) { - /* Configure GPT to start at RX end */ - st25r3916SetStartGPTimer( - (uint16_t)rfalConv1fcTo8fc(MIN( - gRFAL.timings.FDTPoll, (gRFAL.timings.FDTPoll - RFAL_FDT_POLL_ADJUSTMENT))), - ST25R3916_REG_TIMER_EMV_CONTROL_gptc_erx); - } - } - - /*******************************************************************************/ - /* Execute Pre Transceive Callback */ - /*******************************************************************************/ - if(gRFAL.callbacks.preTxRx != NULL) { - gRFAL.callbacks.preTxRx(gRFAL.callbacks.ctx); - } - /*******************************************************************************/ - - maskInterrupts = - (ST25R3916_IRQ_MASK_FWL | ST25R3916_IRQ_MASK_TXE | ST25R3916_IRQ_MASK_RXS | - ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_PAR | ST25R3916_IRQ_MASK_CRC | - ST25R3916_IRQ_MASK_ERR1 | ST25R3916_IRQ_MASK_ERR2 | ST25R3916_IRQ_MASK_NRE); - - /*******************************************************************************/ - /* Transceive flags */ - /*******************************************************************************/ - - reg = - (ST25R3916_REG_ISO14443A_NFC_no_tx_par_off | ST25R3916_REG_ISO14443A_NFC_no_rx_par_off | - ST25R3916_REG_ISO14443A_NFC_nfc_f0_off); - - /* Check if NFCIP1 mode is to be enabled */ - if((gRFAL.TxRx.ctx.flags & (uint8_t)RFAL_TXRX_FLAGS_NFCIP1_ON) != 0U) { - reg |= ST25R3916_REG_ISO14443A_NFC_nfc_f0; - } - - /* Check if Parity check is to be skipped and to keep the parity + CRC bits in FIFO */ - if((gRFAL.TxRx.ctx.flags & (uint8_t)RFAL_TXRX_FLAGS_PAR_RX_KEEP) != 0U) { - reg |= ST25R3916_REG_ISO14443A_NFC_no_rx_par; - } - - /* Check if automatic Parity bits is to be disabled */ - if((gRFAL.TxRx.ctx.flags & (uint8_t)RFAL_TXRX_FLAGS_PAR_TX_NONE) != 0U) { - reg |= ST25R3916_REG_ISO14443A_NFC_no_tx_par; - } - - /* Apply current TxRx flags on ISO14443A and NFC 106kb/s Settings Register */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_ISO14443A_NFC, - (ST25R3916_REG_ISO14443A_NFC_no_tx_par | ST25R3916_REG_ISO14443A_NFC_no_rx_par | - ST25R3916_REG_ISO14443A_NFC_nfc_f0), - reg); - - /* Check if AGC is to be disabled */ - if((gRFAL.TxRx.ctx.flags & (uint8_t)RFAL_TXRX_FLAGS_AGC_OFF) != 0U) { - st25r3916ClrRegisterBits(ST25R3916_REG_RX_CONF2, ST25R3916_REG_RX_CONF2_agc_en); - } else { - st25r3916SetRegisterBits(ST25R3916_REG_RX_CONF2, ST25R3916_REG_RX_CONF2_agc_en); - } - /*******************************************************************************/ - - /*******************************************************************************/ - /* EMVCo NRT mode */ - /*******************************************************************************/ - if(gRFAL.conf.eHandling == RFAL_ERRORHANDLING_EMVCO) { - st25r3916SetRegisterBits( - ST25R3916_REG_TIMER_EMV_CONTROL, ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv); - maskInterrupts |= ST25R3916_IRQ_MASK_RX_REST; - } else { - st25r3916ClrRegisterBits( - ST25R3916_REG_TIMER_EMV_CONTROL, ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv); - } - /*******************************************************************************/ - - /* In Passive Listen mode additionally enable External Field interrupts */ - if(rfalIsModePassiveListen(gRFAL.mode)) { - maskInterrupts |= - (ST25R3916_IRQ_MASK_EOF | - ST25R3916_IRQ_MASK_WU_F); /* Enable external Field interrupts to detect Link Loss and SENF_REQ auto responses */ - } - - /* In Active comms enable also External Field interrupts and set RF Collsion Avoindance */ - if(rfalIsModeActiveComm(gRFAL.mode)) { - maskInterrupts |= - (ST25R3916_IRQ_MASK_EOF | ST25R3916_IRQ_MASK_EON | ST25R3916_IRQ_MASK_PPON2 | - ST25R3916_IRQ_MASK_CAT | ST25R3916_IRQ_MASK_CAC); - - /* Set n=0 for subsequent RF Collision Avoidance */ - st25r3916ChangeRegisterBits(ST25R3916_REG_AUX, ST25R3916_REG_AUX_nfc_n_mask, 0); - } - - /*******************************************************************************/ - /* Start transceive Sanity Timer if a FWT is used */ - if((gRFAL.TxRx.ctx.fwt != RFAL_FWT_NONE) && (gRFAL.TxRx.ctx.fwt != 0U)) { - rfalTimerStart(gRFAL.tmr.txRx, rfalCalcSanityTmr(gRFAL.TxRx.ctx.fwt)); - } - /*******************************************************************************/ - - /*******************************************************************************/ - /* Clear and enable these interrupts */ - st25r3916GetInterrupt(maskInterrupts); - st25r3916EnableInterrupts(maskInterrupts); - - /* Clear FIFO status local copy */ - rfalFIFOStatusClear(); -} - -/*******************************************************************************/ -static void rfalTransceiveTx(void) { - volatile uint32_t irqs; - uint16_t tmp; - ReturnCode ret; - - /* Supress warning in case NFC-V feature is disabled */ - ret = ERR_NONE; - NO_WARNING(ret); - - irqs = ST25R3916_IRQ_MASK_NONE; - - if(gRFAL.TxRx.state != gRFAL.TxRx.lastState) { - /* rfalLogD( "RFAL: lastSt: %d curSt: %d \r\n", gRFAL.TxRx.lastState, gRFAL.TxRx.state ); */ - gRFAL.TxRx.lastState = gRFAL.TxRx.state; - } - - switch(gRFAL.TxRx.state) { - /*******************************************************************************/ - case RFAL_TXRX_STATE_TX_IDLE: - - /* Nothing to do */ - - gRFAL.TxRx.state = RFAL_TXRX_STATE_TX_WAIT_GT; - /* fall through */ - - /*******************************************************************************/ - case RFAL_TXRX_STATE_TX_WAIT_GT: /* PRQA S 2003 # MISRA 16.3 - Intentional fall through */ - - if(!rfalIsGTExpired()) { - break; - } - - rfalTimerDestroy(gRFAL.tmr.GT); - gRFAL.tmr.GT = RFAL_TIMING_NONE; - - gRFAL.TxRx.state = RFAL_TXRX_STATE_TX_WAIT_FDT; - /* fall through */ - - /*******************************************************************************/ - case RFAL_TXRX_STATE_TX_WAIT_FDT: /* PRQA S 2003 # MISRA 16.3 - Intentional fall through */ - - /* Only in Passive communications GPT is used to measure FDT Poll */ - if(rfalIsModePassiveComm(gRFAL.mode)) { - if(st25r3916IsGPTRunning()) { - break; - } - } - - gRFAL.TxRx.state = RFAL_TXRX_STATE_TX_TRANSMIT; - /* fall through */ - - /*******************************************************************************/ - case RFAL_TXRX_STATE_TX_TRANSMIT: /* PRQA S 2003 # MISRA 16.3 - Intentional fall through */ - - /* Clear FIFO, Clear and Enable the Interrupts */ - rfalPrepareTransceive(); - - /* ST25R3916 has a fixed FIFO water level */ - gRFAL.fifo.expWL = RFAL_FIFO_OUT_WL; - -#if RFAL_FEATURE_NFCV - /*******************************************************************************/ - /* In NFC-V streaming mode, the FIFO needs to be loaded with the coded bits */ - if((RFAL_MODE_POLL_NFCV == gRFAL.mode) || (RFAL_MODE_POLL_PICOPASS == gRFAL.mode)) { -#if 0 - /* Debugging code: output the payload bits by writing into the FIFO and subsequent clearing */ - st25r3916WriteFifo(gRFAL.TxRx.ctx.txBuf, rfalConvBitsToBytes(gRFAL.TxRx.ctx.txBufLen)); - st25r3916ExecuteCommand( ST25R3916_CMD_CLEAR_FIFO ); -#endif - /* Calculate the bytes needed to be Written into FIFO (a incomplete byte will be added as 1byte) */ - gRFAL.nfcvData.nfcvOffset = 0; - ret = iso15693VCDCode( - gRFAL.TxRx.ctx.txBuf, - rfalConvBitsToBytes(gRFAL.TxRx.ctx.txBufLen), - (((gRFAL.nfcvData.origCtx.flags & (uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL) != 0U) ? - false : - true), - (((gRFAL.nfcvData.origCtx.flags & (uint32_t)RFAL_TXRX_FLAGS_NFCV_FLAG_MANUAL) != - 0U) ? - false : - true), - (RFAL_MODE_POLL_PICOPASS == gRFAL.mode), - &gRFAL.fifo.bytesTotal, - &gRFAL.nfcvData.nfcvOffset, - gRFAL.nfcvData.codingBuffer, - MIN((uint16_t)ST25R3916_FIFO_DEPTH, (uint16_t)sizeof(gRFAL.nfcvData.codingBuffer)), - &gRFAL.fifo.bytesWritten); - - if((ret != ERR_NONE) && (ret != ERR_AGAIN)) { - gRFAL.TxRx.status = ret; - gRFAL.TxRx.state = RFAL_TXRX_STATE_TX_FAIL; - break; - } - /* Set the number of full bytes and bits to be transmitted */ - st25r3916SetNumTxBits((uint16_t)rfalConvBytesToBits(gRFAL.fifo.bytesTotal)); - - /* Load FIFO with coded bytes */ - st25r3916WriteFifo(gRFAL.nfcvData.codingBuffer, gRFAL.fifo.bytesWritten); - - } - /*******************************************************************************/ - else -#endif /* RFAL_FEATURE_NFCV */ - { - /* Calculate the bytes needed to be Written into FIFO (a incomplete byte will be added as 1byte) */ - gRFAL.fifo.bytesTotal = (uint16_t)rfalCalcNumBytes(gRFAL.TxRx.ctx.txBufLen); - - /* Set the number of full bytes and bits to be transmitted */ - st25r3916SetNumTxBits(gRFAL.TxRx.ctx.txBufLen); - - /* Load FIFO with total length or FIFO's maximum */ - gRFAL.fifo.bytesWritten = MIN(gRFAL.fifo.bytesTotal, ST25R3916_FIFO_DEPTH); - st25r3916WriteFifo(gRFAL.TxRx.ctx.txBuf, gRFAL.fifo.bytesWritten); - } - - /*Check if Observation Mode is enabled and set it on ST25R391x */ - rfalCheckEnableObsModeTx(); - - /*******************************************************************************/ - /* If we're in Passive Listen mode ensure that the external field is still On */ - if(rfalIsModePassiveListen(gRFAL.mode)) { - if(!rfalIsExtFieldOn()) { - gRFAL.TxRx.status = ERR_LINK_LOSS; - gRFAL.TxRx.state = RFAL_TXRX_STATE_TX_FAIL; - break; - } - } - - /*******************************************************************************/ - /* Trigger/Start transmission */ - if((gRFAL.TxRx.ctx.flags & (uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL) != 0U) { - st25r3916ExecuteCommand(ST25R3916_CMD_TRANSMIT_WITHOUT_CRC); - } else { - st25r3916ExecuteCommand(ST25R3916_CMD_TRANSMIT_WITH_CRC); - } - - /* Check if a WL level is expected or TXE should come */ - gRFAL.TxRx.state = - ((gRFAL.fifo.bytesWritten < gRFAL.fifo.bytesTotal) ? RFAL_TXRX_STATE_TX_WAIT_WL : - RFAL_TXRX_STATE_TX_WAIT_TXE); - break; - - /*******************************************************************************/ - case RFAL_TXRX_STATE_TX_WAIT_WL: - - irqs = st25r3916GetInterrupt((ST25R3916_IRQ_MASK_FWL | ST25R3916_IRQ_MASK_TXE)); - if(irqs == ST25R3916_IRQ_MASK_NONE) { - break; /* No interrupt to process */ - } - - if(((irqs & ST25R3916_IRQ_MASK_FWL) != 0U) && ((irqs & ST25R3916_IRQ_MASK_TXE) == 0U)) { - gRFAL.TxRx.state = RFAL_TXRX_STATE_TX_RELOAD_FIFO; - } else { - gRFAL.TxRx.status = ERR_IO; - gRFAL.TxRx.state = RFAL_TXRX_STATE_TX_FAIL; - break; - } - - /* fall through */ - - /*******************************************************************************/ - case RFAL_TXRX_STATE_TX_RELOAD_FIFO: /* PRQA S 2003 # MISRA 16.3 - Intentional fall through */ - -#if RFAL_FEATURE_NFCV - /*******************************************************************************/ - /* In NFC-V streaming mode, the FIFO needs to be loaded with the coded bits */ - if((RFAL_MODE_POLL_NFCV == gRFAL.mode) || (RFAL_MODE_POLL_PICOPASS == gRFAL.mode)) { - uint16_t maxLen; - - /* Load FIFO with the remaining length or maximum available (which fit on the coding buffer) */ - maxLen = - (uint16_t)MIN((gRFAL.fifo.bytesTotal - gRFAL.fifo.bytesWritten), gRFAL.fifo.expWL); - maxLen = (uint16_t)MIN(maxLen, sizeof(gRFAL.nfcvData.codingBuffer)); - tmp = 0; - - /* Calculate the bytes needed to be Written into FIFO (a incomplete byte will be added as 1byte) */ - ret = iso15693VCDCode( - gRFAL.TxRx.ctx.txBuf, - rfalConvBitsToBytes(gRFAL.TxRx.ctx.txBufLen), - (((gRFAL.nfcvData.origCtx.flags & (uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL) != 0U) ? - false : - true), - (((gRFAL.nfcvData.origCtx.flags & (uint32_t)RFAL_TXRX_FLAGS_NFCV_FLAG_MANUAL) != - 0U) ? - false : - true), - (RFAL_MODE_POLL_PICOPASS == gRFAL.mode), - &gRFAL.fifo.bytesTotal, - &gRFAL.nfcvData.nfcvOffset, - gRFAL.nfcvData.codingBuffer, - maxLen, - &tmp); - - if((ret != ERR_NONE) && (ret != ERR_AGAIN)) { - gRFAL.TxRx.status = ret; - gRFAL.TxRx.state = RFAL_TXRX_STATE_TX_FAIL; - break; - } - - /* Load FIFO with coded bytes */ - st25r3916WriteFifo(gRFAL.nfcvData.codingBuffer, tmp); - } - /*******************************************************************************/ - else -#endif /* RFAL_FEATURE_NFCV */ - { - /* Load FIFO with the remaining length or maximum available */ - tmp = MIN( - (gRFAL.fifo.bytesTotal - gRFAL.fifo.bytesWritten), - gRFAL.fifo.expWL); /* tmp holds the number of bytes written on this iteration */ - st25r3916WriteFifo(&gRFAL.TxRx.ctx.txBuf[gRFAL.fifo.bytesWritten], tmp); - } - - /* Update total written bytes to FIFO */ - gRFAL.fifo.bytesWritten += tmp; - - /* Check if a WL level is expected or TXE should come */ - gRFAL.TxRx.state = - ((gRFAL.fifo.bytesWritten < gRFAL.fifo.bytesTotal) ? RFAL_TXRX_STATE_TX_WAIT_WL : - RFAL_TXRX_STATE_TX_WAIT_TXE); - break; - - /*******************************************************************************/ - case RFAL_TXRX_STATE_TX_WAIT_TXE: - - irqs = st25r3916GetInterrupt((ST25R3916_IRQ_MASK_FWL | ST25R3916_IRQ_MASK_TXE)); - if(irqs == ST25R3916_IRQ_MASK_NONE) { - break; /* No interrupt to process */ - } - - if((irqs & ST25R3916_IRQ_MASK_TXE) != 0U) { - gRFAL.TxRx.state = RFAL_TXRX_STATE_TX_DONE; - } else if((irqs & ST25R3916_IRQ_MASK_FWL) != 0U) { - break; /* Ignore ST25R3916 FIFO WL if total TxLen is already on the FIFO */ - } else { - gRFAL.TxRx.status = ERR_IO; - gRFAL.TxRx.state = RFAL_TXRX_STATE_TX_FAIL; - break; - } - - /* fall through */ - - /*******************************************************************************/ - case RFAL_TXRX_STATE_TX_DONE: /* PRQA S 2003 # MISRA 16.3 - Intentional fall through */ - - /* If no rxBuf is provided do not wait/expect Rx */ - if(gRFAL.TxRx.ctx.rxBuf == NULL) { - /*Check if Observation Mode was enabled and disable it on ST25R391x */ - rfalCheckDisableObsMode(); - - /* Clean up Transceive */ - rfalCleanupTransceive(); - - gRFAL.TxRx.status = ERR_NONE; - gRFAL.TxRx.state = RFAL_TXRX_STATE_IDLE; - break; - } - - rfalCheckEnableObsModeRx(); - - /* Goto Rx */ - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_IDLE; - break; - - /*******************************************************************************/ - case RFAL_TXRX_STATE_TX_FAIL: - - /* Error should be assigned by previous state */ - if(gRFAL.TxRx.status == ERR_BUSY) { - gRFAL.TxRx.status = ERR_SYSTEM; - } - - /*Check if Observation Mode was enabled and disable it on ST25R391x */ - rfalCheckDisableObsMode(); - - /* Clean up Transceive */ - rfalCleanupTransceive(); - - gRFAL.TxRx.state = RFAL_TXRX_STATE_IDLE; - break; - - /*******************************************************************************/ - default: - gRFAL.TxRx.status = ERR_SYSTEM; - gRFAL.TxRx.state = RFAL_TXRX_STATE_TX_FAIL; - break; - } -} - -/*******************************************************************************/ -static void rfalTransceiveRx(void) { - volatile uint32_t irqs; - uint16_t tmp; - uint16_t aux; - - irqs = ST25R3916_IRQ_MASK_NONE; - - if(gRFAL.TxRx.state != gRFAL.TxRx.lastState) { - /* rfalLogD( "RFAL: lastSt: %d curSt: %d \r\n", gRFAL.TxRx.lastState, gRFAL.TxRx.state ); */ - gRFAL.TxRx.lastState = gRFAL.TxRx.state; - } - - switch(gRFAL.TxRx.state) { - /*******************************************************************************/ - case RFAL_TXRX_STATE_RX_IDLE: - - /* Clear rx counters */ - gRFAL.fifo.bytesWritten = 0; /* Total bytes written on RxBuffer */ - gRFAL.fifo.bytesTotal = 0; /* Total bytes in FIFO will now be from Rx */ - if(gRFAL.TxRx.ctx.rxRcvdLen != NULL) { - *gRFAL.TxRx.ctx.rxRcvdLen = 0; - } - - gRFAL.TxRx.state = - (rfalIsModeActiveComm(gRFAL.mode) ? RFAL_TXRX_STATE_RX_WAIT_EON : - RFAL_TXRX_STATE_RX_WAIT_RXS); - break; - - /*******************************************************************************/ - case RFAL_TXRX_STATE_RX_WAIT_RXS: - - /*******************************************************************************/ - irqs = st25r3916GetInterrupt( - (ST25R3916_IRQ_MASK_RXS | ST25R3916_IRQ_MASK_NRE | ST25R3916_IRQ_MASK_EOF)); - if(irqs == ST25R3916_IRQ_MASK_NONE) { - break; /* No interrupt to process */ - } - - /* Only raise Timeout if NRE is detected with no Rx Start (NRT EMV mode) */ - if(((irqs & ST25R3916_IRQ_MASK_NRE) != 0U) && ((irqs & ST25R3916_IRQ_MASK_RXS) == 0U)) { - gRFAL.TxRx.status = ERR_TIMEOUT; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_FAIL; - break; - } - - /* Only raise Link Loss if EOF is detected with no Rx Start */ - if(((irqs & ST25R3916_IRQ_MASK_EOF) != 0U) && ((irqs & ST25R3916_IRQ_MASK_RXS) == 0U)) { - /* In AP2P a Field On has already occurred - treat this as timeout | mute */ - gRFAL.TxRx.status = (rfalIsModeActiveComm(gRFAL.mode) ? ERR_TIMEOUT : ERR_LINK_LOSS); - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_FAIL; - break; - } - - if((irqs & ST25R3916_IRQ_MASK_RXS) != 0U) { - /*******************************************************************************/ - /* REMARK: Silicon workaround ST25R3916 Errata #TBD */ - /* Rarely on corrupted frames I_rxs gets signaled but I_rxe is not signaled */ - /* Use a SW timer to handle an eventual missing RXE */ - rfalTimerStart(gRFAL.tmr.RXE, RFAL_NORXE_TOUT); - /*******************************************************************************/ - - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_WAIT_RXE; - } else { - gRFAL.TxRx.status = ERR_IO; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_FAIL; - break; - } - - /* remove NRE that might appear together (NRT EMV mode), and remove RXS, but keep EOF if present for next state */ - irqs &= ~(ST25R3916_IRQ_MASK_RXS | ST25R3916_IRQ_MASK_NRE); - - /* fall through */ - - /*******************************************************************************/ - case RFAL_TXRX_STATE_RX_WAIT_RXE: /* PRQA S 2003 # MISRA 16.3 - Intentional fall through */ - - /*******************************************************************************/ - /* REMARK: Silicon workaround ST25R3916 Errata #TBD */ - /* ST25R396 may indicate RXS without RXE afterwards, this happens rarely on */ - /* corrupted frames. */ - /* SW timer is used to timeout upon a missing RXE */ - if(rfalTimerisExpired(gRFAL.tmr.RXE)) { - gRFAL.TxRx.status = ERR_FRAMING; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_FAIL; - } - /*******************************************************************************/ - - irqs |= st25r3916GetInterrupt( - (ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_FWL | ST25R3916_IRQ_MASK_EOF | - ST25R3916_IRQ_MASK_RX_REST | ST25R3916_IRQ_MASK_WU_F)); - if(irqs == ST25R3916_IRQ_MASK_NONE) { - break; /* No interrupt to process */ - } - - if((irqs & ST25R3916_IRQ_MASK_RX_REST) != 0U) { - /* RX_REST indicates that Receiver has been reseted due to EMD, therefore a RXS + RXE should * - * follow if a good reception is followed within the valid initial timeout */ - - /* Check whether NRT has expired already, if so signal a timeout */ - if(st25r3916GetInterrupt(ST25R3916_IRQ_MASK_NRE) != 0U) { - gRFAL.TxRx.status = ERR_TIMEOUT; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_FAIL; - break; - } - if(st25r3916CheckReg( - ST25R3916_REG_NFCIP1_BIT_RATE, - ST25R3916_REG_NFCIP1_BIT_RATE_nrt_on, - 0)) /* MISRA 13.5 */ - { - gRFAL.TxRx.status = ERR_TIMEOUT; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_FAIL; - break; - } - - /* Discard any previous RXS */ - st25r3916GetInterrupt(ST25R3916_IRQ_MASK_RXS); - - /* Check whether a following reception has already started */ - if(st25r3916CheckReg( - ST25R3916_REG_AUX_DISPLAY, - ST25R3916_REG_AUX_DISPLAY_rx_act, - ST25R3916_REG_AUX_DISPLAY_rx_act)) { - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_WAIT_RXE; - break; - } - - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_WAIT_RXS; - break; - } - - if(((irqs & ST25R3916_IRQ_MASK_FWL) != 0U) && ((irqs & ST25R3916_IRQ_MASK_RXE) == 0U)) { - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_READ_FIFO; - break; - } - - /* Automatic responses allowed during TxRx only for the SENSF_REQ */ - if((irqs & ST25R3916_IRQ_MASK_WU_F) != 0U) { - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_WAIT_RXS; - break; - } - - /* After RXE retrieve and check for any error irqs */ - irqs |= st25r3916GetInterrupt( - (ST25R3916_IRQ_MASK_CRC | ST25R3916_IRQ_MASK_PAR | ST25R3916_IRQ_MASK_ERR1 | - ST25R3916_IRQ_MASK_ERR2 | ST25R3916_IRQ_MASK_COL)); - - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_ERR_CHECK; - /* fall through */ - - /*******************************************************************************/ - case RFAL_TXRX_STATE_RX_ERR_CHECK: /* PRQA S 2003 # MISRA 16.3 - Intentional fall through */ - - if((irqs & ST25R3916_IRQ_MASK_ERR1) != 0U) { - gRFAL.TxRx.status = ERR_FRAMING; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_READ_DATA; - - /* Check if there's a specific error handling for this */ - rfalErrorHandling(); - break; - } - /* Discard Soft Framing errors in AP2P and CE */ - else if(rfalIsModePassivePoll(gRFAL.mode) && ((irqs & ST25R3916_IRQ_MASK_ERR2) != 0U)) { - gRFAL.TxRx.status = ERR_FRAMING; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_READ_DATA; - - /* Check if there's a specific error handling for this */ - rfalErrorHandling(); - break; - } else if((irqs & ST25R3916_IRQ_MASK_PAR) != 0U) { - gRFAL.TxRx.status = ERR_PAR; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_READ_DATA; - - /* Check if there's a specific error handling for this */ - rfalErrorHandling(); - break; - } else if((irqs & ST25R3916_IRQ_MASK_CRC) != 0U) { - gRFAL.TxRx.status = ERR_CRC; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_READ_DATA; - - /* Check if there's a specific error handling for this */ - rfalErrorHandling(); - break; - } else if((irqs & ST25R3916_IRQ_MASK_COL) != 0U) { - gRFAL.TxRx.status = ERR_RF_COLLISION; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_READ_DATA; - - /* Check if there's a specific error handling for this */ - rfalErrorHandling(); - break; - } else if(rfalIsModePassiveListen(gRFAL.mode) && ((irqs & ST25R3916_IRQ_MASK_EOF) != 0U)) { - gRFAL.TxRx.status = ERR_LINK_LOSS; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_FAIL; - break; - } else if((irqs & ST25R3916_IRQ_MASK_RXE) != 0U) { - /* Reception ended without any error indication, * - * check FIFO status for malformed or incomplete frames */ - - /* Check if the reception ends with an incomplete byte (residual bits) */ - if(rfalFIFOStatusIsIncompleteByte()) { - gRFAL.TxRx.status = ERR_INCOMPLETE_BYTE; - } - /* Check if the reception ends missing parity bit */ - else if(rfalFIFOStatusIsMissingPar()) { - gRFAL.TxRx.status = ERR_FRAMING; - } else { - /* MISRA 15.7 - Empty else */ - } - - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_READ_DATA; - } else { - gRFAL.TxRx.status = ERR_IO; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_FAIL; - break; - } - - /* fall through */ - - /*******************************************************************************/ - case RFAL_TXRX_STATE_RX_READ_DATA: /* PRQA S 2003 # MISRA 16.3 - Intentional fall through */ - - tmp = rfalFIFOStatusGetNumBytes(); - - /*******************************************************************************/ - /* Check if CRC should not be placed in rxBuf */ - if(((gRFAL.TxRx.ctx.flags & (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP) == 0U)) { - /* if received frame was bigger than CRC */ - if((uint16_t)(gRFAL.fifo.bytesTotal + tmp) > 0U) { - /* By default CRC will not be placed into the rxBuffer */ - if((tmp > RFAL_CRC_LEN)) { - tmp -= RFAL_CRC_LEN; - } - /* If the CRC was already placed into rxBuffer (due to WL interrupt where CRC was already in FIFO Read) - * cannot remove it from rxBuf. Can only remove it from rxBufLen not indicate the presence of CRC */ - else if(gRFAL.fifo.bytesTotal > RFAL_CRC_LEN) { - gRFAL.fifo.bytesTotal -= RFAL_CRC_LEN; - } else { - /* MISRA 15.7 - Empty else */ - } - } - } - - gRFAL.fifo.bytesTotal += tmp; /* add to total bytes counter */ - - /*******************************************************************************/ - /* Check if remaining bytes fit on the rxBuf available */ - if(gRFAL.fifo.bytesTotal > rfalConvBitsToBytes(gRFAL.TxRx.ctx.rxBufLen)) { - tmp = - (uint16_t)(rfalConvBitsToBytes(gRFAL.TxRx.ctx.rxBufLen) - gRFAL.fifo.bytesWritten); - - /* Transmission errors have precedence over buffer error */ - if(gRFAL.TxRx.status == ERR_BUSY) { - gRFAL.TxRx.status = ERR_NOMEM; - } - } - - /*******************************************************************************/ - /* Retrieve remaining bytes from FIFO to rxBuf, and assign total length rcvd */ - st25r3916ReadFifo(&gRFAL.TxRx.ctx.rxBuf[gRFAL.fifo.bytesWritten], tmp); - if(gRFAL.TxRx.ctx.rxRcvdLen != NULL) { - (*gRFAL.TxRx.ctx.rxRcvdLen) = (uint16_t)rfalConvBytesToBits(gRFAL.fifo.bytesTotal); - if(rfalFIFOStatusIsIncompleteByte()) { - (*gRFAL.TxRx.ctx.rxRcvdLen) -= - (RFAL_BITS_IN_BYTE - rfalFIFOGetNumIncompleteBits()); - } - } - -#if RFAL_FEATURE_NFCV - /*******************************************************************************/ - /* Decode sub bit stream into payload bits for NFCV, if no error found so far */ - if(((RFAL_MODE_POLL_NFCV == gRFAL.mode) || (RFAL_MODE_POLL_PICOPASS == gRFAL.mode)) && - (gRFAL.TxRx.status == ERR_BUSY)) { - ReturnCode ret; - uint16_t offset = 0; /* REMARK offset not currently used */ - - ret = iso15693VICCDecode( - gRFAL.TxRx.ctx.rxBuf, - gRFAL.fifo.bytesTotal, - gRFAL.nfcvData.origCtx.rxBuf, - rfalConvBitsToBytes(gRFAL.nfcvData.origCtx.rxBufLen), - &offset, - gRFAL.nfcvData.origCtx.rxRcvdLen, - gRFAL.nfcvData.ignoreBits, - (RFAL_MODE_POLL_PICOPASS == gRFAL.mode)); - - if(((ERR_NONE == ret) || (ERR_CRC == ret)) && - (((uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP & gRFAL.nfcvData.origCtx.flags) == 0U) && - ((*gRFAL.nfcvData.origCtx.rxRcvdLen % RFAL_BITS_IN_BYTE) == 0U) && - (*gRFAL.nfcvData.origCtx.rxRcvdLen >= rfalConvBytesToBits(RFAL_CRC_LEN))) { - *gRFAL.nfcvData.origCtx.rxRcvdLen -= - (uint16_t)rfalConvBytesToBits(RFAL_CRC_LEN); /* Remove CRC */ - } -#if 0 - /* Debugging code: output the payload bits by writing into the FIFO and subsequent clearing */ - st25r3916WriteFifo(gRFAL.nfcvData.origCtx.rxBuf, rfalConvBitsToBytes( *gRFAL.nfcvData.origCtx.rxRcvdLen)); - st25r3916ExecuteCommand( ST25R3916_CMD_CLEAR_FIFO ); -#endif - - /* Restore original ctx */ - gRFAL.TxRx.ctx = gRFAL.nfcvData.origCtx; - gRFAL.TxRx.status = ((ret != ERR_NONE) ? ret : ERR_BUSY); - } -#endif /* RFAL_FEATURE_NFCV */ - - /*******************************************************************************/ - /* If an error as been marked/detected don't fall into to RX_DONE */ - if(gRFAL.TxRx.status != ERR_BUSY) { - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_FAIL; - break; - } - - if(rfalIsModeActiveComm(gRFAL.mode)) { - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_WAIT_EOF; - break; - } - - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_DONE; - /* fall through */ - - /*******************************************************************************/ - case RFAL_TXRX_STATE_RX_DONE: /* PRQA S 2003 # MISRA 16.3 - Intentional fall through */ - - /*Check if Observation Mode was enabled and disable it on ST25R391x */ - rfalCheckDisableObsMode(); - - /* Clean up Transceive */ - rfalCleanupTransceive(); - - gRFAL.TxRx.status = ERR_NONE; - gRFAL.TxRx.state = RFAL_TXRX_STATE_IDLE; - break; - - /*******************************************************************************/ - case RFAL_TXRX_STATE_RX_READ_FIFO: - - /*******************************************************************************/ - /* REMARK: Silicon workaround ST25R3916 Errata #TBD */ - /* Rarely on corrupted frames I_rxs gets signaled but I_rxe is not signaled */ - /* Use a SW timer to handle an eventual missing RXE */ - rfalTimerStart(gRFAL.tmr.RXE, RFAL_NORXE_TOUT); - /*******************************************************************************/ - - tmp = rfalFIFOStatusGetNumBytes(); - gRFAL.fifo.bytesTotal += tmp; - - /*******************************************************************************/ - /* Calculate the amount of bytes that still fits in rxBuf */ - aux = - ((gRFAL.fifo.bytesTotal > rfalConvBitsToBytes(gRFAL.TxRx.ctx.rxBufLen)) ? - (rfalConvBitsToBytes(gRFAL.TxRx.ctx.rxBufLen) - gRFAL.fifo.bytesWritten) : - tmp); - - /*******************************************************************************/ - /* Retrieve incoming bytes from FIFO to rxBuf, and store already read amount */ - st25r3916ReadFifo(&gRFAL.TxRx.ctx.rxBuf[gRFAL.fifo.bytesWritten], aux); - gRFAL.fifo.bytesWritten += aux; - - /*******************************************************************************/ - /* If the bytes already read were not the full FIFO WL, dump the remaining * - * FIFO so that ST25R391x can continue with reception */ - if(aux < tmp) { - st25r3916ReadFifo(NULL, (tmp - aux)); - } - - rfalFIFOStatusClear(); - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_WAIT_RXE; - break; - - /*******************************************************************************/ - case RFAL_TXRX_STATE_RX_FAIL: - - /*Check if Observation Mode was enabled and disable it on ST25R391x */ - rfalCheckDisableObsMode(); - - /* Clean up Transceive */ - rfalCleanupTransceive(); - - /* Error should be assigned by previous state */ - if(gRFAL.TxRx.status == ERR_BUSY) { - gRFAL.TxRx.status = ERR_SYSTEM; - } - - /*rfalLogD( "RFAL: curSt: %d Error: %d \r\n", gRFAL.TxRx.state, gRFAL.TxRx.status );*/ - gRFAL.TxRx.state = RFAL_TXRX_STATE_IDLE; - break; - - /*******************************************************************************/ - case RFAL_TXRX_STATE_RX_WAIT_EON: - - irqs = st25r3916GetInterrupt( - (ST25R3916_IRQ_MASK_EON | ST25R3916_IRQ_MASK_NRE | ST25R3916_IRQ_MASK_PPON2)); - if(irqs == ST25R3916_IRQ_MASK_NONE) { - break; /* No interrupt to process */ - } - - if((irqs & ST25R3916_IRQ_MASK_EON) != 0U) { - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_WAIT_RXS; - } - - if((irqs & ST25R3916_IRQ_MASK_NRE) != 0U) { - gRFAL.TxRx.status = ERR_TIMEOUT; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_FAIL; - } - if((irqs & ST25R3916_IRQ_MASK_PPON2) != 0U) { - gRFAL.TxRx.status = ERR_LINK_LOSS; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_FAIL; - } - break; - - /*******************************************************************************/ - case RFAL_TXRX_STATE_RX_WAIT_EOF: - - irqs = st25r3916GetInterrupt((ST25R3916_IRQ_MASK_CAT | ST25R3916_IRQ_MASK_CAC)); - if(irqs == ST25R3916_IRQ_MASK_NONE) { - break; /* No interrupt to process */ - } - - if((irqs & ST25R3916_IRQ_MASK_CAT) != 0U) { - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_DONE; - } else if((irqs & ST25R3916_IRQ_MASK_CAC) != 0U) { - gRFAL.TxRx.status = ERR_RF_COLLISION; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_FAIL; - } else { - gRFAL.TxRx.status = ERR_IO; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_FAIL; - } - break; - - /*******************************************************************************/ - default: - gRFAL.TxRx.status = ERR_SYSTEM; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_FAIL; - break; - } -} - -/*******************************************************************************/ -static void rfalFIFOStatusUpdate(void) { - if(gRFAL.fifo.status[RFAL_FIFO_STATUS_REG2] == RFAL_FIFO_STATUS_INVALID) { - st25r3916ReadMultipleRegisters( - ST25R3916_REG_FIFO_STATUS1, gRFAL.fifo.status, ST25R3916_FIFO_STATUS_LEN); - } -} - -/*******************************************************************************/ -static void rfalFIFOStatusClear(void) { - gRFAL.fifo.status[RFAL_FIFO_STATUS_REG2] = RFAL_FIFO_STATUS_INVALID; -} - -/*******************************************************************************/ -static uint16_t rfalFIFOStatusGetNumBytes(void) { - uint16_t result; - - rfalFIFOStatusUpdate(); - - result = - ((((uint16_t)gRFAL.fifo.status[RFAL_FIFO_STATUS_REG2] & - ST25R3916_REG_FIFO_STATUS2_fifo_b_mask) >> - ST25R3916_REG_FIFO_STATUS2_fifo_b_shift) - << RFAL_BITS_IN_BYTE); - result |= (((uint16_t)gRFAL.fifo.status[RFAL_FIFO_STATUS_REG1]) & 0x00FFU); - return result; -} - -/*******************************************************************************/ -static bool rfalFIFOStatusIsIncompleteByte(void) { - rfalFIFOStatusUpdate(); - return ( - (gRFAL.fifo.status[RFAL_FIFO_STATUS_REG2] & ST25R3916_REG_FIFO_STATUS2_fifo_lb_mask) != - 0U); -} - -/*******************************************************************************/ -static bool rfalFIFOStatusIsMissingPar(void) { - rfalFIFOStatusUpdate(); - return ((gRFAL.fifo.status[RFAL_FIFO_STATUS_REG2] & ST25R3916_REG_FIFO_STATUS2_np_lb) != 0U); -} - -/*******************************************************************************/ -static uint8_t rfalFIFOGetNumIncompleteBits(void) { - rfalFIFOStatusUpdate(); - return ( - (gRFAL.fifo.status[RFAL_FIFO_STATUS_REG2] & ST25R3916_REG_FIFO_STATUS2_fifo_lb_mask) >> - ST25R3916_REG_FIFO_STATUS2_fifo_lb_shift); -} - -#if RFAL_FEATURE_NFCA - -/*******************************************************************************/ -ReturnCode rfalISO14443ATransceiveShortFrame( - rfal14443AShortFrameCmd txCmd, - uint8_t* rxBuf, - uint8_t rxBufLen, - uint16_t* rxRcvdLen, - uint32_t fwt) { - ReturnCode ret; - uint8_t directCmd; - - /* Check if RFAL is properly initialized */ - if(!st25r3916IsTxEnabled() || (gRFAL.state < RFAL_STATE_MODE_SET) || - ((gRFAL.mode != RFAL_MODE_POLL_NFCA) && (gRFAL.mode != RFAL_MODE_POLL_NFCA_T1T))) { - return ERR_WRONG_STATE; - } - - /* Check for valid parameters */ - if((rxBuf == NULL) || (rxRcvdLen == NULL) || (fwt == RFAL_FWT_NONE)) { - return ERR_PARAM; - } - - /*******************************************************************************/ - /* Select the Direct Command to be performed */ - switch(txCmd) { - case RFAL_14443A_SHORTFRAME_CMD_WUPA: - directCmd = ST25R3916_CMD_TRANSMIT_WUPA; - break; - - case RFAL_14443A_SHORTFRAME_CMD_REQA: - directCmd = ST25R3916_CMD_TRANSMIT_REQA; - break; - - default: - return ERR_PARAM; - } - - /* Disable CRC while receiving since ATQA has no CRC included */ - st25r3916SetRegisterBits(ST25R3916_REG_AUX, ST25R3916_REG_AUX_no_crc_rx); - - /*******************************************************************************/ - /* Wait for GT and FDT */ - while(!rfalIsGTExpired()) { /* MISRA 15.6: mandatory brackets */ - }; - while(st25r3916IsGPTRunning()) { /* MISRA 15.6: mandatory brackets */ - }; - - rfalTimerDestroy(gRFAL.tmr.GT); - gRFAL.tmr.GT = RFAL_TIMING_NONE; - - /*******************************************************************************/ - /* Prepare for Transceive, Receive only (bypass Tx states) */ - gRFAL.TxRx.ctx.flags = - ((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP); - gRFAL.TxRx.ctx.rxBuf = rxBuf; - gRFAL.TxRx.ctx.rxBufLen = rxBufLen; - gRFAL.TxRx.ctx.rxRcvdLen = rxRcvdLen; - gRFAL.TxRx.ctx.fwt = fwt; - - /*******************************************************************************/ - /* Load NRT with FWT */ - st25r3916SetNoResponseTime(rfalConv1fcTo64fc( - MIN((fwt + RFAL_FWT_ADJUSTMENT + RFAL_FWT_A_ADJUSTMENT), RFAL_ST25R3916_NRT_MAX_1FC))); - - if(gRFAL.timings.FDTListen != RFAL_TIMING_NONE) { - /* Ensure that MRT is using 64/fc steps */ - st25r3916ClrRegisterBits( - ST25R3916_REG_TIMER_EMV_CONTROL, ST25R3916_REG_TIMER_EMV_CONTROL_mrt_step); - - /* Set Minimum FDT(Listen) in which PICC is not allowed to send a response */ - st25r3916WriteRegister( - ST25R3916_REG_MASK_RX_TIMER, - (uint8_t)rfalConv1fcTo64fc( - ((RFAL_FDT_LISTEN_MRT_ADJUSTMENT + RFAL_FDT_LISTEN_A_ADJUSTMENT) > - gRFAL.timings.FDTListen) ? - RFAL_ST25R3916_MRT_MIN_1FC : - (gRFAL.timings.FDTListen - - (RFAL_FDT_LISTEN_MRT_ADJUSTMENT + RFAL_FDT_LISTEN_A_ADJUSTMENT)))); - } - - /* In Passive communications General Purpose Timer is used to measure FDT Poll */ - if(gRFAL.timings.FDTPoll != RFAL_TIMING_NONE) { - /* Configure GPT to start at RX end */ - st25r3916SetStartGPTimer( - (uint16_t)rfalConv1fcTo8fc( - MIN(gRFAL.timings.FDTPoll, (gRFAL.timings.FDTPoll - RFAL_FDT_POLL_ADJUSTMENT))), - ST25R3916_REG_TIMER_EMV_CONTROL_gptc_erx); - } - - /*******************************************************************************/ - rfalPrepareTransceive(); - - /* Also enable bit collision interrupt */ - st25r3916GetInterrupt(ST25R3916_IRQ_MASK_COL); - st25r3916EnableInterrupts(ST25R3916_IRQ_MASK_COL); - - /*Check if Observation Mode is enabled and set it on ST25R391x */ - rfalCheckEnableObsModeTx(); - - /*******************************************************************************/ - /* Clear nbtx bits before sending WUPA/REQA - otherwise ST25R3916 will report parity error, Note2 of the register */ - st25r3916WriteRegister(ST25R3916_REG_NUM_TX_BYTES2, 0); - - /* Send either WUPA or REQA. All affected tags will backscatter ATQA and change to READY state */ - st25r3916ExecuteCommand(directCmd); - - /* Wait for TXE */ - if(st25r3916WaitForInterruptsTimed( - ST25R3916_IRQ_MASK_TXE, - (uint16_t)MAX(rfalConv1fcToMs(fwt), RFAL_ST25R3916_SW_TMR_MIN_1MS)) == 0U) { - ret = ERR_IO; - } else { - /*Check if Observation Mode is enabled and set it on ST25R391x */ - rfalCheckEnableObsModeRx(); - - /* Jump into a transceive Rx state for reception (bypass Tx states) */ - gRFAL.state = RFAL_STATE_TXRX; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_IDLE; - gRFAL.TxRx.status = ERR_BUSY; - - /* Execute Transceive Rx blocking */ - ret = rfalTransceiveBlockingRx(); - } - - /* Disable Collision interrupt */ - st25r3916DisableInterrupts((ST25R3916_IRQ_MASK_COL)); - - /* ReEnable CRC on Rx */ - st25r3916ClrRegisterBits(ST25R3916_REG_AUX, ST25R3916_REG_AUX_no_crc_rx); - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalISO14443ATransceiveAnticollisionFrame( - uint8_t* buf, - uint8_t* bytesToSend, - uint8_t* bitsToSend, - uint16_t* rxLength, - uint32_t fwt) { - ReturnCode ret; - rfalTransceiveContext ctx; - uint8_t collByte; - uint8_t collData; - - /* Check if RFAL is properly initialized */ - if((gRFAL.state < RFAL_STATE_MODE_SET) || (gRFAL.mode != RFAL_MODE_POLL_NFCA)) { - return ERR_WRONG_STATE; - } - - /* Check for valid parameters */ - if((buf == NULL) || (bytesToSend == NULL) || (bitsToSend == NULL) || (rxLength == NULL)) { - return ERR_PARAM; - } - - /*******************************************************************************/ - /* Set speficic Analog Config for Anticolission if needed */ - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_ANTICOL)); - - /*******************************************************************************/ - /* Enable anti collision to recognise collision in first byte of SENS_REQ */ - st25r3916SetRegisterBits(ST25R3916_REG_ISO14443A_NFC, ST25R3916_REG_ISO14443A_NFC_antcl); - - /* Disable CRC while receiving */ - st25r3916SetRegisterBits(ST25R3916_REG_AUX, ST25R3916_REG_AUX_no_crc_rx); - - /*******************************************************************************/ - /* Prepare for Transceive */ - ctx.flags = ((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP); - ctx.txBuf = buf; - ctx.txBufLen = (uint16_t)(rfalConvBytesToBits(*bytesToSend) + *bitsToSend); - ctx.rxBuf = &buf[*bytesToSend]; - ctx.rxBufLen = (uint16_t)rfalConvBytesToBits(RFAL_ISO14443A_SDD_RES_LEN); - ctx.rxRcvdLen = rxLength; - ctx.fwt = fwt; - - /* Disable Automatic Gain Control (AGC) for better detection of collisions if using Coherent Receiver */ - ctx.flags |= - (st25r3916CheckReg( - ST25R3916_REG_AUX, ST25R3916_REG_AUX_dis_corr, ST25R3916_REG_AUX_dis_corr) ? - (uint32_t)RFAL_TXRX_FLAGS_AGC_OFF : - 0x00U); - - rfalStartTransceive(&ctx); - - /* Additionally enable bit collision interrupt */ - st25r3916GetInterrupt(ST25R3916_IRQ_MASK_COL); - st25r3916EnableInterrupts(ST25R3916_IRQ_MASK_COL); - - /*******************************************************************************/ - collByte = 0; - - /* save the collision byte */ - if((*bitsToSend) > 0U) { - buf[(*bytesToSend)] <<= (RFAL_BITS_IN_BYTE - (*bitsToSend)); - buf[(*bytesToSend)] >>= (RFAL_BITS_IN_BYTE - (*bitsToSend)); - collByte = buf[(*bytesToSend)]; - } - - /*******************************************************************************/ - /* Run Transceive blocking */ - ret = rfalTransceiveRunBlockingTx(); - if(ret == ERR_NONE) { - ret = rfalTransceiveBlockingRx(); - - /*******************************************************************************/ - if((*bitsToSend) > 0U) { - buf[(*bytesToSend)] >>= (*bitsToSend); - buf[(*bytesToSend)] <<= (*bitsToSend); - buf[(*bytesToSend)] |= collByte; - } - - if((ERR_RF_COLLISION == ret)) { - /* read out collision register */ - st25r3916ReadRegister(ST25R3916_REG_COLLISION_STATUS, &collData); - - (*bytesToSend) = - ((collData >> ST25R3916_REG_COLLISION_STATUS_c_byte_shift) & - 0x0FU); // 4-bits Byte information - (*bitsToSend) = - ((collData >> ST25R3916_REG_COLLISION_STATUS_c_bit_shift) & - 0x07U); // 3-bits bit information - } - } - - /*******************************************************************************/ - /* Disable Collision interrupt */ - st25r3916DisableInterrupts((ST25R3916_IRQ_MASK_COL)); - - /* Disable anti collision again */ - st25r3916ClrRegisterBits(ST25R3916_REG_ISO14443A_NFC, ST25R3916_REG_ISO14443A_NFC_antcl); - - /* ReEnable CRC on Rx */ - st25r3916ClrRegisterBits(ST25R3916_REG_AUX, ST25R3916_REG_AUX_no_crc_rx); - /*******************************************************************************/ - - /* Restore common Analog configurations for this mode */ - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | rfalConvBR2ACBR(gRFAL.txBR) | - RFAL_ANALOG_CONFIG_TX)); - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCA | rfalConvBR2ACBR(gRFAL.rxBR) | - RFAL_ANALOG_CONFIG_RX)); - - return ret; -} - -#endif /* RFAL_FEATURE_NFCA */ - -#if RFAL_FEATURE_NFCV - -/*******************************************************************************/ -ReturnCode rfalISO15693TransceiveAnticollisionFrame( - uint8_t* txBuf, - uint8_t txBufLen, - uint8_t* rxBuf, - uint8_t rxBufLen, - uint16_t* actLen) { - ReturnCode ret; - rfalTransceiveContext ctx; - - /* Check if RFAL is properly initialized */ - if((gRFAL.state < RFAL_STATE_MODE_SET) || (gRFAL.mode != RFAL_MODE_POLL_NFCV)) { - return ERR_WRONG_STATE; - } - - /*******************************************************************************/ - /* Set speficic Analog Config for Anticolission if needed */ - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCV | - RFAL_ANALOG_CONFIG_BITRATE_COMMON | RFAL_ANALOG_CONFIG_ANTICOL)); - - /* Ignoring collisions before the UID (RES_FLAG + DSFID) */ - gRFAL.nfcvData.ignoreBits = (uint16_t)RFAL_ISO15693_IGNORE_BITS; - - /*******************************************************************************/ - /* Prepare for Transceive */ - ctx.flags = - ((txBufLen == 0U) ? (uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL : - (uint32_t)RFAL_TXRX_FLAGS_CRC_TX_AUTO) | - (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP | (uint32_t)RFAL_TXRX_FLAGS_AGC_OFF | - ((txBufLen == 0U) ? - (uint32_t)RFAL_TXRX_FLAGS_NFCV_FLAG_MANUAL : - (uint32_t) - RFAL_TXRX_FLAGS_NFCV_FLAG_AUTO); /* Disable Automatic Gain Control (AGC) for better detection of collision */ - ctx.txBuf = txBuf; - ctx.txBufLen = (uint16_t)rfalConvBytesToBits(txBufLen); - ctx.rxBuf = rxBuf; - ctx.rxBufLen = (uint16_t)rfalConvBytesToBits(rxBufLen); - ctx.rxRcvdLen = actLen; - ctx.fwt = rfalConv64fcTo1fc(ISO15693_FWT); - - rfalStartTransceive(&ctx); - - /*******************************************************************************/ - /* Run Transceive blocking */ - ret = rfalTransceiveRunBlockingTx(); - if(ret == ERR_NONE) { - ret = rfalTransceiveBlockingRx(); - } - - /* Check if a Transmission error and received data is less then expected */ - if(((ret == ERR_RF_COLLISION) || (ret == ERR_CRC) || (ret == ERR_FRAMING)) && - (rfalConvBitsToBytes(*ctx.rxRcvdLen) < RFAL_ISO15693_INV_RES_LEN)) { - /* If INVENTORY_RES is shorter than expected, tag is still modulating * - * Ensure that response is complete before next frame */ - platformDelay(( - uint8_t)((RFAL_ISO15693_INV_RES_LEN - rfalConvBitsToBytes(*ctx.rxRcvdLen)) / ((RFAL_ISO15693_INV_RES_LEN / RFAL_ISO15693_INV_RES_DUR) + 1U))); - } - - /* Restore common Analog configurations for this mode */ - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCV | rfalConvBR2ACBR(gRFAL.txBR) | - RFAL_ANALOG_CONFIG_TX)); - rfalSetAnalogConfig( - (RFAL_ANALOG_CONFIG_POLL | RFAL_ANALOG_CONFIG_TECH_NFCV | rfalConvBR2ACBR(gRFAL.rxBR) | - RFAL_ANALOG_CONFIG_RX)); - - gRFAL.nfcvData.ignoreBits = 0; - return ret; -} - -/*******************************************************************************/ -ReturnCode - rfalISO15693TransceiveEOFAnticollision(uint8_t* rxBuf, uint8_t rxBufLen, uint16_t* actLen) { - uint8_t dummy; - - return rfalISO15693TransceiveAnticollisionFrame(&dummy, 0, rxBuf, rxBufLen, actLen); -} - -/*******************************************************************************/ -ReturnCode rfalISO15693TransceiveEOF(uint8_t* rxBuf, uint8_t rxBufLen, uint16_t* actLen) { - ReturnCode ret; - uint8_t dummy; - - /* Check if RFAL is properly initialized */ - if((gRFAL.state < RFAL_STATE_MODE_SET) || (gRFAL.mode != RFAL_MODE_POLL_NFCV)) { - return ERR_WRONG_STATE; - } - - /*******************************************************************************/ - /* Run Transceive blocking */ - ret = rfalTransceiveBlockingTxRx( - &dummy, - 0, - rxBuf, - rxBufLen, - actLen, - ((uint32_t)RFAL_TXRX_FLAGS_CRC_TX_MANUAL | (uint32_t)RFAL_TXRX_FLAGS_CRC_RX_KEEP | - (uint32_t)RFAL_TXRX_FLAGS_AGC_ON), - rfalConv64fcTo1fc(ISO15693_FWT)); - return ret; -} - -#endif /* RFAL_FEATURE_NFCV */ - -#if RFAL_FEATURE_NFCF - -/*******************************************************************************/ -ReturnCode rfalFeliCaPoll( - rfalFeliCaPollSlots slots, - uint16_t sysCode, - uint8_t reqCode, - rfalFeliCaPollRes* pollResList, - uint8_t pollResListSize, - uint8_t* devicesDetected, - uint8_t* collisionsDetected) { - ReturnCode ret; - uint8_t frame - [RFAL_FELICA_POLL_REQ_LEN - RFAL_FELICA_LEN_LEN]; // LEN is added by ST25R391x automatically - uint16_t actLen; - uint8_t frameIdx; - uint8_t devDetected; - uint8_t colDetected; - rfalEHandling curHandling; - uint8_t nbSlots; - - /* Check if RFAL is properly initialized */ - if((gRFAL.state < RFAL_STATE_MODE_SET) || (gRFAL.mode != RFAL_MODE_POLL_NFCF)) { - return ERR_WRONG_STATE; - } - - frameIdx = 0; - colDetected = 0; - devDetected = 0; - nbSlots = (uint8_t)slots; - - /*******************************************************************************/ - /* Compute SENSF_REQ frame */ - frame[frameIdx++] = (uint8_t)FELICA_CMD_POLLING; /* CMD: SENF_REQ */ - frame[frameIdx++] = (uint8_t)(sysCode >> 8); /* System Code (SC) */ - frame[frameIdx++] = (uint8_t)(sysCode & 0xFFU); /* System Code (SC) */ - frame[frameIdx++] = reqCode; /* Communication Parameter Request (RC)*/ - frame[frameIdx++] = nbSlots; /* TimeSlot (TSN) */ - - /*******************************************************************************/ - /* NRT should not stop on reception - Use EMVCo mode to run NRT in nrt_emv * - * ERRORHANDLING_EMVCO has no special handling for NFC-F mode */ - curHandling = gRFAL.conf.eHandling; - rfalSetErrorHandling(RFAL_ERRORHANDLING_EMVCO); - - /*******************************************************************************/ - /* Run transceive blocking, - * Calculate Total Response Time in(64/fc): - * 512 PICC process time + (n * 256 Time Slot duration) */ - ret = rfalTransceiveBlockingTx( - frame, - (uint16_t)frameIdx, - (uint8_t*)gRFAL.nfcfData.pollResponses, - RFAL_FELICA_POLL_RES_LEN, - &actLen, - (RFAL_TXRX_FLAGS_DEFAULT), - rfalConv64fcTo1fc( - RFAL_FELICA_POLL_DELAY_TIME + - (RFAL_FELICA_POLL_SLOT_TIME * ((uint32_t)nbSlots + 1U)))); - - /*******************************************************************************/ - /* If Tx OK, Wait for all responses, store them as soon as they appear */ - if(ret == ERR_NONE) { - bool timeout; - - do { - ret = rfalTransceiveBlockingRx(); - if(ret == ERR_TIMEOUT) { - /* Upon timeout the full Poll Delay + (Slot time)*(nbSlots) has expired */ - timeout = true; - } else { - /* Reception done, reEnabled Rx for following Slot */ - st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA); - st25r3916ExecuteCommand(ST25R3916_CMD_RESET_RXGAIN); - - /* If the reception was OK, new device found */ - if(ret == ERR_NONE) { - devDetected++; - - /* Overwrite the Transceive context for the next reception */ - gRFAL.TxRx.ctx.rxBuf = (uint8_t*)gRFAL.nfcfData.pollResponses[devDetected]; - } - /* If the reception was not OK, mark as collision */ - else { - colDetected++; - } - - /* Check whether NRT has expired meanwhile */ - timeout = st25r3916CheckReg( - ST25R3916_REG_NFCIP1_BIT_RATE, ST25R3916_REG_NFCIP1_BIT_RATE_nrt_on, 0x00); - if(!timeout) { - /* Jump again into transceive Rx state for the following reception */ - gRFAL.TxRx.status = ERR_BUSY; - gRFAL.state = RFAL_STATE_TXRX; - gRFAL.TxRx.state = RFAL_TXRX_STATE_RX_IDLE; - } - } - } while(((nbSlots--) != 0U) && !timeout); - } - - /*******************************************************************************/ - /* Restore NRT to normal mode - back to previous error handling */ - rfalSetErrorHandling(curHandling); - - /*******************************************************************************/ - /* Assign output parameters if requested */ - - if((pollResList != NULL) && (pollResListSize > 0U) && (devDetected > 0U)) { - ST_MEMCPY( - pollResList, - gRFAL.nfcfData.pollResponses, - (RFAL_FELICA_POLL_RES_LEN * (uint32_t)MIN(pollResListSize, devDetected))); - } - - if(devicesDetected != NULL) { - *devicesDetected = devDetected; - } - - if(collisionsDetected != NULL) { - *collisionsDetected = colDetected; - } - - return (((colDetected != 0U) || (devDetected != 0U)) ? ERR_NONE : ret); -} - -#endif /* RFAL_FEATURE_NFCF */ - -/***************************************************************************** - * Listen Mode * - *****************************************************************************/ - -/*******************************************************************************/ -bool rfalIsExtFieldOn(void) { - return st25r3916IsExtFieldOn(); -} - -#if RFAL_FEATURE_LISTEN_MODE - -/*******************************************************************************/ -ReturnCode rfalListenStart( - uint32_t lmMask, - const rfalLmConfPA* confA, - const rfalLmConfPB* confB, - const rfalLmConfPF* confF, - uint8_t* rxBuf, - uint16_t rxBufLen, - uint16_t* rxLen) { - t_rfalPTMem - PTMem; /* PRQA S 0759 # MISRA 19.2 - Allocating Union where members are of the same type, just different names. Thus no problem can occur. */ - uint8_t* pPTMem; - uint8_t autoResp; - - /* Check if RFAL is initialized */ - if(gRFAL.state < RFAL_STATE_INIT) { - return ERR_WRONG_STATE; - } - - gRFAL.Lm.state = RFAL_LM_STATE_NOT_INIT; - gRFAL.Lm.mdIrqs = ST25R3916_IRQ_MASK_NONE; - gRFAL.Lm.mdReg = - (ST25R3916_REG_MODE_targ_init | ST25R3916_REG_MODE_om_nfc | ST25R3916_REG_MODE_nfc_ar_off); - - /* By default disable all automatic responses */ - autoResp = - (ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a | ST25R3916_REG_PASSIVE_TARGET_rfu | - ST25R3916_REG_PASSIVE_TARGET_d_212_424_1r | ST25R3916_REG_PASSIVE_TARGET_d_ac_ap2p); - - /*******************************************************************************/ - if((lmMask & RFAL_LM_MASK_NFCA) != 0U) { - /* Check if the conf has been provided */ - if(confA == NULL) { - return ERR_PARAM; - } - - pPTMem = (uint8_t*)PTMem.PTMem_A; - - /*******************************************************************************/ - /* Check and set supported NFCID Length */ - switch(confA->nfcidLen) { - case RFAL_LM_NFCID_LEN_04: - st25r3916ChangeRegisterBits( - ST25R3916_REG_AUX, ST25R3916_REG_AUX_nfc_id_mask, ST25R3916_REG_AUX_nfc_id_4bytes); - break; - - case RFAL_LM_NFCID_LEN_07: - st25r3916ChangeRegisterBits( - ST25R3916_REG_AUX, ST25R3916_REG_AUX_nfc_id_mask, ST25R3916_REG_AUX_nfc_id_7bytes); - break; - - default: - return ERR_PARAM; - } - - /*******************************************************************************/ - /* Set NFCID */ - ST_MEMCPY(pPTMem, confA->nfcid, RFAL_NFCID1_TRIPLE_LEN); - pPTMem = &pPTMem[RFAL_NFCID1_TRIPLE_LEN]; /* MISRA 18.4 */ - - /* Set SENS_RES */ - ST_MEMCPY(pPTMem, confA->SENS_RES, RFAL_LM_SENS_RES_LEN); - pPTMem = &pPTMem[RFAL_LM_SENS_RES_LEN]; /* MISRA 18.4 */ - - /* Set SEL_RES */ - *pPTMem++ = - ((confA->nfcidLen == RFAL_LM_NFCID_LEN_04) ? - (confA->SEL_RES & ~RFAL_LM_NFCID_INCOMPLETE) : - (confA->SEL_RES | RFAL_LM_NFCID_INCOMPLETE)); - *pPTMem++ = (confA->SEL_RES & ~RFAL_LM_NFCID_INCOMPLETE); - *pPTMem++ = (confA->SEL_RES & ~RFAL_LM_NFCID_INCOMPLETE); - - /* Write into PTMem-A */ - st25r3916WritePTMem(PTMem.PTMem_A, ST25R3916_PTM_A_LEN); - - /*******************************************************************************/ - /* Enable automatic responses for A */ - autoResp &= ~ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a; - - /* Set Target mode, Bit Rate detection and Listen Mode for NFC-F */ - gRFAL.Lm.mdReg |= - (ST25R3916_REG_MODE_targ_targ | ST25R3916_REG_MODE_om3 | ST25R3916_REG_MODE_om0 | - ST25R3916_REG_MODE_nfc_ar_off); - - gRFAL.Lm.mdIrqs |= - (ST25R3916_IRQ_MASK_WU_A | ST25R3916_IRQ_MASK_WU_A_X | ST25R3916_IRQ_MASK_RXE_PTA); - } - - /*******************************************************************************/ - if((lmMask & RFAL_LM_MASK_NFCB) != 0U) { - /* Check if the conf has been provided */ - if(confB == NULL) { - return ERR_PARAM; - } - - return ERR_NOTSUPP; - } - - /*******************************************************************************/ - if((lmMask & RFAL_LM_MASK_NFCF) != 0U) { - pPTMem = (uint8_t*)PTMem.PTMem_F; - - /* Check if the conf has been provided */ - if(confF == NULL) { - return ERR_PARAM; - } - - /*******************************************************************************/ - /* Set System Code */ - ST_MEMCPY(pPTMem, confF->SC, RFAL_LM_SENSF_SC_LEN); - pPTMem = &pPTMem[RFAL_LM_SENSF_SC_LEN]; /* MISRA 18.4 */ - - /* Set SENSF_RES */ - ST_MEMCPY(pPTMem, confF->SENSF_RES, RFAL_LM_SENSF_RES_LEN); - - /* Set RD bytes to 0x00 as ST25R3916 cannot support advances features */ - pPTMem[RFAL_LM_SENSF_RD0_POS] = - 0x00; /* NFC Forum Digital 1.1 Table 46: 0x00 */ - pPTMem[RFAL_LM_SENSF_RD1_POS] = - 0x00; /* NFC Forum Digital 1.1 Table 47: No automatic bit rates */ - - pPTMem = &pPTMem[RFAL_LM_SENS_RES_LEN]; /* MISRA 18.4 */ - - /* Write into PTMem-F */ - st25r3916WritePTMemF(PTMem.PTMem_F, ST25R3916_PTM_F_LEN); - - /*******************************************************************************/ - /* Write 24 TSN "Random" Numbers at first initialization and let it rollover */ - if(!gRFAL.Lm.iniFlag) { - pPTMem = (uint8_t*)PTMem.TSN; - - *pPTMem++ = 0x12; - *pPTMem++ = 0x34; - *pPTMem++ = 0x56; - *pPTMem++ = 0x78; - *pPTMem++ = 0x9A; - *pPTMem++ = 0xBC; - *pPTMem++ = 0xDF; - *pPTMem++ = 0x21; - *pPTMem++ = 0x43; - *pPTMem++ = 0x65; - *pPTMem++ = 0x87; - *pPTMem++ = 0xA9; - - /* Write into PTMem-TSN */ - st25r3916WritePTMemTSN(PTMem.TSN, ST25R3916_PTM_TSN_LEN); - } - - /*******************************************************************************/ - /* Enable automatic responses for F */ - autoResp &= ~(ST25R3916_REG_PASSIVE_TARGET_d_212_424_1r); - - /* Set Target mode, Bit Rate detection and Listen Mode for NFC-F */ - gRFAL.Lm.mdReg |= - (ST25R3916_REG_MODE_targ_targ | ST25R3916_REG_MODE_om3 | ST25R3916_REG_MODE_om2 | - ST25R3916_REG_MODE_nfc_ar_off); - - /* In CE NFC-F any data without error will be passed to FIFO, to support CUP */ - gRFAL.Lm.mdIrqs |= - (ST25R3916_IRQ_MASK_WU_F | ST25R3916_IRQ_MASK_RXE_PTA | ST25R3916_IRQ_MASK_RXE); - } - - /*******************************************************************************/ - if((lmMask & RFAL_LM_MASK_ACTIVE_P2P) != 0U) { - /* Enable Reception of P2P frames */ - autoResp &= ~(ST25R3916_REG_PASSIVE_TARGET_d_ac_ap2p); - - /* Set Target mode, Bit Rate detection and Automatic Response RF Collision Avoidance */ - gRFAL.Lm.mdReg |= - (ST25R3916_REG_MODE_targ_targ | ST25R3916_REG_MODE_om3 | ST25R3916_REG_MODE_om2 | - ST25R3916_REG_MODE_om0 | ST25R3916_REG_MODE_nfc_ar_auto_rx); - - /* n * TRFW timing shall vary Activity 2.1 3.4.1.1 */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_AUX, ST25R3916_REG_AUX_nfc_n_mask, gRFAL.timings.nTRFW); - gRFAL.timings.nTRFW = rfalGennTRFW(gRFAL.timings.nTRFW); - - gRFAL.Lm.mdIrqs |= (ST25R3916_IRQ_MASK_RXE); - } - - /* Check if one of the modes were selected */ - if((gRFAL.Lm.mdReg & ST25R3916_REG_MODE_targ) == ST25R3916_REG_MODE_targ_targ) { - gRFAL.state = RFAL_STATE_LM; - gRFAL.Lm.mdMask = lmMask; - - gRFAL.Lm.rxBuf = rxBuf; - gRFAL.Lm.rxBufLen = rxBufLen; - gRFAL.Lm.rxLen = rxLen; - *gRFAL.Lm.rxLen = 0; - gRFAL.Lm.dataFlag = false; - gRFAL.Lm.iniFlag = true; - - /* Apply the Automatic Responses configuration */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_PASSIVE_TARGET, - (ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a | ST25R3916_REG_PASSIVE_TARGET_rfu | - ST25R3916_REG_PASSIVE_TARGET_d_212_424_1r | ST25R3916_REG_PASSIVE_TARGET_d_ac_ap2p), - autoResp); - - /* Disable GPT trigger source */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_TIMER_EMV_CONTROL, - ST25R3916_REG_TIMER_EMV_CONTROL_gptc_mask, - ST25R3916_REG_TIMER_EMV_CONTROL_gptc_no_trigger); - - /* On Bit Rate Detection Mode ST25R391x will filter incoming frames during MRT time starting on External Field On event, use 512/fc steps */ - st25r3916SetRegisterBits( - ST25R3916_REG_TIMER_EMV_CONTROL, ST25R3916_REG_TIMER_EMV_CONTROL_mrt_step_512); - st25r3916WriteRegister( - ST25R3916_REG_MASK_RX_TIMER, (uint8_t)rfalConv1fcTo512fc(RFAL_LM_GT)); - - /* Restore default settings on NFCIP1 mode, Receiving parity + CRC bits and manual Tx Parity*/ - st25r3916ClrRegisterBits( - ST25R3916_REG_ISO14443A_NFC, - (ST25R3916_REG_ISO14443A_NFC_no_tx_par | ST25R3916_REG_ISO14443A_NFC_no_rx_par | - ST25R3916_REG_ISO14443A_NFC_nfc_f0)); - - /* External Field Detector enabled as Automatics on rfalInitialize() */ - - /* Set Analog configurations for generic Listen mode */ - /* Not on SetState(POWER OFF) as otherwise would be applied on every Field Event */ - rfalSetAnalogConfig((RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_LISTEN_ON)); - - /* Initialize as POWER_OFF and set proper mode in RF Chip */ - rfalListenSetState(RFAL_LM_STATE_POWER_OFF); - } else { - return ERR_REQUEST; /* Listen Start called but no mode was enabled */ - } - - return ERR_NONE; -} - -/*******************************************************************************/ -static ReturnCode rfalRunListenModeWorker(void) { - volatile uint32_t irqs; - uint8_t tmp; - - if(gRFAL.state != RFAL_STATE_LM) { - return ERR_WRONG_STATE; - } - - switch(gRFAL.Lm.state) { - /*******************************************************************************/ - case RFAL_LM_STATE_POWER_OFF: - - irqs = st25r3916GetInterrupt((ST25R3916_IRQ_MASK_EON)); - if(irqs == ST25R3916_IRQ_MASK_NONE) { - break; /* No interrupt to process */ - } - - if((irqs & ST25R3916_IRQ_MASK_EON) != 0U) { - rfalListenSetState(RFAL_LM_STATE_IDLE); - } else { - break; - } - /* fall through */ - - /*******************************************************************************/ - case RFAL_LM_STATE_IDLE: /* PRQA S 2003 # MISRA 16.3 - Intentional fall through */ - - irqs = st25r3916GetInterrupt( - (ST25R3916_IRQ_MASK_NFCT | ST25R3916_IRQ_MASK_WU_F | ST25R3916_IRQ_MASK_RXE | - ST25R3916_IRQ_MASK_EOF | ST25R3916_IRQ_MASK_RXE_PTA)); - if(irqs == ST25R3916_IRQ_MASK_NONE) { - break; /* No interrupt to process */ - } - - if((irqs & ST25R3916_IRQ_MASK_NFCT) != 0U) { - /* Retrieve detected bitrate */ - uint8_t newBr; - st25r3916ReadRegister(ST25R3916_REG_NFCIP1_BIT_RATE, &newBr); - newBr >>= ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rate_shift; - - if(newBr > ST25R3916_REG_BIT_RATE_rxrate_424) { - newBr = ST25R3916_REG_BIT_RATE_rxrate_424; - } - - gRFAL.Lm.brDetected = - (rfalBitRate)(newBr); /* PRQA S 4342 # MISRA 10.5 - Guaranteed that no invalid enum values may be created. See also equalityGuard_RFAL_BR_106 ff.*/ - } - - if(((irqs & ST25R3916_IRQ_MASK_WU_F) != 0U) && (gRFAL.Lm.brDetected != RFAL_BR_KEEP)) { - rfalListenSetState(RFAL_LM_STATE_READY_F); - } else if(((irqs & ST25R3916_IRQ_MASK_RXE) != 0U) && (gRFAL.Lm.brDetected != RFAL_BR_KEEP)) { - irqs = st25r3916GetInterrupt( - (ST25R3916_IRQ_MASK_WU_F | ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_EOF | - ST25R3916_IRQ_MASK_CRC | ST25R3916_IRQ_MASK_PAR | ST25R3916_IRQ_MASK_ERR2 | - ST25R3916_IRQ_MASK_ERR1)); - - if(((irqs & ST25R3916_IRQ_MASK_CRC) != 0U) || - ((irqs & ST25R3916_IRQ_MASK_PAR) != 0U) || - ((irqs & ST25R3916_IRQ_MASK_ERR1) != 0U)) { - st25r3916ExecuteCommand(ST25R3916_CMD_CLEAR_FIFO); - st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA); - st25r3916TxOff(); - break; /* A bad reception occurred, remain in same state */ - } - - /* Retrieve received data */ - *gRFAL.Lm.rxLen = st25r3916GetNumFIFOBytes(); - st25r3916ReadFifo( - gRFAL.Lm.rxBuf, MIN(*gRFAL.Lm.rxLen, rfalConvBitsToBytes(gRFAL.Lm.rxBufLen))); - - /*******************************************************************************/ - /* REMARK: Silicon workaround ST25R3916 Errata #TBD */ - /* In bitrate detection mode CRC is now checked for NFC-A frames */ - if((*gRFAL.Lm.rxLen > RFAL_CRC_LEN) && (gRFAL.Lm.brDetected == RFAL_BR_106)) { - if(rfalCrcCalculateCcitt( - RFAL_ISO14443A_CRC_INTVAL, gRFAL.Lm.rxBuf, *gRFAL.Lm.rxLen) != 0U) { - st25r3916ExecuteCommand(ST25R3916_CMD_CLEAR_FIFO); - st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA); - st25r3916TxOff(); - break; /* A bad reception occurred, remain in same state */ - } - } - /*******************************************************************************/ - - /* Check if the data we got has at least the CRC and remove it, otherwise leave at 0 */ - *gRFAL.Lm.rxLen -= ((*gRFAL.Lm.rxLen > RFAL_CRC_LEN) ? RFAL_CRC_LEN : *gRFAL.Lm.rxLen); - *gRFAL.Lm.rxLen = (uint16_t)rfalConvBytesToBits(*gRFAL.Lm.rxLen); - gRFAL.Lm.dataFlag = true; - - /*Check if Observation Mode was enabled and disable it on ST25R391x */ - rfalCheckDisableObsMode(); - } else if( - ((irqs & ST25R3916_IRQ_MASK_RXE_PTA) != 0U) && (gRFAL.Lm.brDetected != RFAL_BR_KEEP)) { - if(((gRFAL.Lm.mdMask & RFAL_LM_MASK_NFCA) != 0U) && - (gRFAL.Lm.brDetected == RFAL_BR_106)) { - st25r3916ReadRegister(ST25R3916_REG_PASSIVE_TARGET_STATUS, &tmp); - if(tmp > ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_idle) { - rfalListenSetState(RFAL_LM_STATE_READY_A); - } - } - } else if(((irqs & ST25R3916_IRQ_MASK_EOF) != 0U) && (!gRFAL.Lm.dataFlag)) { - rfalListenSetState(RFAL_LM_STATE_POWER_OFF); - } else { - /* MISRA 15.7 - Empty else */ - } - break; - - /*******************************************************************************/ - case RFAL_LM_STATE_READY_F: - - irqs = st25r3916GetInterrupt( - (ST25R3916_IRQ_MASK_WU_F | ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_EOF)); - if(irqs == ST25R3916_IRQ_MASK_NONE) { - break; /* No interrupt to process */ - } - - if((irqs & ST25R3916_IRQ_MASK_WU_F) != 0U) { - break; - } else if((irqs & ST25R3916_IRQ_MASK_RXE) != 0U) { - /* Retrieve the error flags/irqs */ - irqs |= st25r3916GetInterrupt( - (ST25R3916_IRQ_MASK_CRC | ST25R3916_IRQ_MASK_ERR2 | ST25R3916_IRQ_MASK_ERR1)); - - if(((irqs & ST25R3916_IRQ_MASK_CRC) != 0U) || - ((irqs & ST25R3916_IRQ_MASK_ERR1) != 0U)) { - st25r3916ExecuteCommand(ST25R3916_CMD_CLEAR_FIFO); - st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA); - break; /* A bad reception occurred, remain in same state */ - } - - /* Retrieve received data */ - *gRFAL.Lm.rxLen = st25r3916GetNumFIFOBytes(); - st25r3916ReadFifo( - gRFAL.Lm.rxBuf, MIN(*gRFAL.Lm.rxLen, rfalConvBitsToBytes(gRFAL.Lm.rxBufLen))); - - /* Check if the data we got has at least the CRC and remove it, otherwise leave at 0 */ - *gRFAL.Lm.rxLen -= ((*gRFAL.Lm.rxLen > RFAL_CRC_LEN) ? RFAL_CRC_LEN : *gRFAL.Lm.rxLen); - *gRFAL.Lm.rxLen = (uint16_t)rfalConvBytesToBits(*gRFAL.Lm.rxLen); - gRFAL.Lm.dataFlag = true; - } else if((irqs & ST25R3916_IRQ_MASK_EOF) != 0U) { - rfalListenSetState(RFAL_LM_STATE_POWER_OFF); - } else { - /* MISRA 15.7 - Empty else */ - } - break; - - /*******************************************************************************/ - case RFAL_LM_STATE_READY_A: - - irqs = st25r3916GetInterrupt((ST25R3916_IRQ_MASK_EOF | ST25R3916_IRQ_MASK_WU_A)); - if(irqs == ST25R3916_IRQ_MASK_NONE) { - break; /* No interrupt to process */ - } - - if((irqs & ST25R3916_IRQ_MASK_WU_A) != 0U) { - rfalListenSetState(RFAL_LM_STATE_ACTIVE_A); - } else if((irqs & ST25R3916_IRQ_MASK_EOF) != 0U) { - rfalListenSetState(RFAL_LM_STATE_POWER_OFF); - } else { - /* MISRA 15.7 - Empty else */ - } - break; - - /*******************************************************************************/ - case RFAL_LM_STATE_ACTIVE_A: - case RFAL_LM_STATE_ACTIVE_Ax: - - irqs = st25r3916GetInterrupt((ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_EOF)); - if(irqs == ST25R3916_IRQ_MASK_NONE) { - break; /* No interrupt to process */ - } - - if((irqs & ST25R3916_IRQ_MASK_RXE) != 0U) { - /* Retrieve the error flags/irqs */ - irqs |= st25r3916GetInterrupt( - (ST25R3916_IRQ_MASK_PAR | ST25R3916_IRQ_MASK_CRC | ST25R3916_IRQ_MASK_ERR2 | - ST25R3916_IRQ_MASK_ERR1)); - *gRFAL.Lm.rxLen = st25r3916GetNumFIFOBytes(); - - if(((irqs & ST25R3916_IRQ_MASK_CRC) != 0U) || - ((irqs & ST25R3916_IRQ_MASK_ERR1) != 0U) || - ((irqs & ST25R3916_IRQ_MASK_PAR) != 0U) || (*gRFAL.Lm.rxLen <= RFAL_CRC_LEN)) { - /* Clear rx context and FIFO */ - *gRFAL.Lm.rxLen = 0; - st25r3916ExecuteCommand(ST25R3916_CMD_CLEAR_FIFO); - st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA); - - /* Check if we should go to IDLE or Sleep */ - if(gRFAL.Lm.state == RFAL_LM_STATE_ACTIVE_Ax) { - rfalListenSleepStart( - RFAL_LM_STATE_SLEEP_A, gRFAL.Lm.rxBuf, gRFAL.Lm.rxBufLen, gRFAL.Lm.rxLen); - } else { - rfalListenSetState(RFAL_LM_STATE_IDLE); - } - - st25r3916DisableInterrupts(ST25R3916_IRQ_MASK_RXE); - break; - } - - /* Remove CRC from length */ - *gRFAL.Lm.rxLen -= RFAL_CRC_LEN; - - /* Retrieve received data */ - st25r3916ReadFifo( - gRFAL.Lm.rxBuf, MIN(*gRFAL.Lm.rxLen, rfalConvBitsToBytes(gRFAL.Lm.rxBufLen))); - *gRFAL.Lm.rxLen = (uint16_t)rfalConvBytesToBits(*gRFAL.Lm.rxLen); - gRFAL.Lm.dataFlag = true; - } else if((irqs & ST25R3916_IRQ_MASK_EOF) != 0U) { - rfalListenSetState(RFAL_LM_STATE_POWER_OFF); - } else { - /* MISRA 15.7 - Empty else */ - } - break; - - /*******************************************************************************/ - case RFAL_LM_STATE_SLEEP_A: - case RFAL_LM_STATE_SLEEP_B: - case RFAL_LM_STATE_SLEEP_AF: - - irqs = st25r3916GetInterrupt( - (ST25R3916_IRQ_MASK_NFCT | ST25R3916_IRQ_MASK_WU_F | ST25R3916_IRQ_MASK_RXE | - ST25R3916_IRQ_MASK_EOF | ST25R3916_IRQ_MASK_RXE_PTA)); - if(irqs == ST25R3916_IRQ_MASK_NONE) { - break; /* No interrupt to process */ - } - - if((irqs & ST25R3916_IRQ_MASK_NFCT) != 0U) { - uint8_t newBr; - /* Retrieve detected bitrate */ - st25r3916ReadRegister(ST25R3916_REG_NFCIP1_BIT_RATE, &newBr); - newBr >>= ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rate_shift; - - if(newBr > ST25R3916_REG_BIT_RATE_rxrate_424) { - newBr = ST25R3916_REG_BIT_RATE_rxrate_424; - } - - gRFAL.Lm.brDetected = - (rfalBitRate)(newBr); /* PRQA S 4342 # MISRA 10.5 - Guaranteed that no invalid enum values may be created. See also equalityGuard_RFAL_BR_106 ff.*/ - } - - if(((irqs & ST25R3916_IRQ_MASK_WU_F) != 0U) && (gRFAL.Lm.brDetected != RFAL_BR_KEEP)) { - rfalListenSetState(RFAL_LM_STATE_READY_F); - } else if(((irqs & ST25R3916_IRQ_MASK_RXE) != 0U) && (gRFAL.Lm.brDetected != RFAL_BR_KEEP)) { - /* Clear rx context and FIFO */ - *gRFAL.Lm.rxLen = 0; - st25r3916ExecuteCommand(ST25R3916_CMD_CLEAR_FIFO); - st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA); - - /* REMARK: In order to support CUP or proprietary frames, handling could be added here */ - } else if( - ((irqs & ST25R3916_IRQ_MASK_RXE_PTA) != 0U) && (gRFAL.Lm.brDetected != RFAL_BR_KEEP)) { - if(((gRFAL.Lm.mdMask & RFAL_LM_MASK_NFCA) != 0U) && - (gRFAL.Lm.brDetected == RFAL_BR_106)) { - st25r3916ReadRegister(ST25R3916_REG_PASSIVE_TARGET_STATUS, &tmp); - if(tmp > ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_halt) { - rfalListenSetState(RFAL_LM_STATE_READY_Ax); - } - } - } else if((irqs & ST25R3916_IRQ_MASK_EOF) != 0U) { - rfalListenSetState(RFAL_LM_STATE_POWER_OFF); - } else { - /* MISRA 15.7 - Empty else */ - } - break; - - /*******************************************************************************/ - case RFAL_LM_STATE_READY_Ax: - - irqs = st25r3916GetInterrupt((ST25R3916_IRQ_MASK_EOF | ST25R3916_IRQ_MASK_WU_A_X)); - if(irqs == ST25R3916_IRQ_MASK_NONE) { - break; /* No interrupt to process */ - } - - if((irqs & ST25R3916_IRQ_MASK_WU_A_X) != 0U) { - rfalListenSetState(RFAL_LM_STATE_ACTIVE_Ax); - } else if((irqs & ST25R3916_IRQ_MASK_EOF) != 0U) { - rfalListenSetState(RFAL_LM_STATE_POWER_OFF); - } else { - /* MISRA 15.7 - Empty else */ - } - break; - - /*******************************************************************************/ - case RFAL_LM_STATE_CARDEMU_4A: - case RFAL_LM_STATE_CARDEMU_4B: - case RFAL_LM_STATE_CARDEMU_3: - case RFAL_LM_STATE_TARGET_F: - case RFAL_LM_STATE_TARGET_A: - break; - - /*******************************************************************************/ - default: - return ERR_WRONG_STATE; - } - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalListenStop(void) { - /* Check if RFAL is initialized */ - if(gRFAL.state < RFAL_STATE_INIT) { - return ERR_WRONG_STATE; - } - - gRFAL.Lm.state = RFAL_LM_STATE_NOT_INIT; - - /*Check if Observation Mode was enabled and disable it on ST25R391x */ - rfalCheckDisableObsMode(); - - /* Re-Enable the Oscillator if not running */ - st25r3916OscOn(); - - /* Disable Receiver and Transmitter */ - rfalFieldOff(); - - /* Disable all automatic responses */ - st25r3916SetRegisterBits( - ST25R3916_REG_PASSIVE_TARGET, - (ST25R3916_REG_PASSIVE_TARGET_d_212_424_1r | ST25R3916_REG_PASSIVE_TARGET_rfu | - ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a | ST25R3916_REG_PASSIVE_TARGET_d_ac_ap2p)); - - /* As there's no Off mode, set default value: ISO14443A with automatic RF Collision Avoidance Off */ - st25r3916WriteRegister( - ST25R3916_REG_MODE, - (ST25R3916_REG_MODE_om_iso14443a | ST25R3916_REG_MODE_tr_am_ook | - ST25R3916_REG_MODE_nfc_ar_off)); - - st25r3916DisableInterrupts( - (ST25R3916_IRQ_MASK_RXE_PTA | ST25R3916_IRQ_MASK_WU_F | ST25R3916_IRQ_MASK_WU_A | - ST25R3916_IRQ_MASK_WU_A_X | ST25R3916_IRQ_MASK_RFU2 | ST25R3916_IRQ_MASK_OSC)); - st25r3916GetInterrupt( - (ST25R3916_IRQ_MASK_RXE_PTA | ST25R3916_IRQ_MASK_WU_F | ST25R3916_IRQ_MASK_WU_A | - ST25R3916_IRQ_MASK_WU_A_X | ST25R3916_IRQ_MASK_RFU2)); - - /* Set Analog configurations for Listen Off event */ - rfalSetAnalogConfig((RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_LISTEN_OFF)); - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode - rfalListenSleepStart(rfalLmState sleepSt, uint8_t* rxBuf, uint16_t rxBufLen, uint16_t* rxLen) { - /* Check if RFAL is not initialized */ - if(gRFAL.state < RFAL_STATE_INIT) { - return ERR_WRONG_STATE; - } - - switch(sleepSt) { - /*******************************************************************************/ - case RFAL_LM_STATE_SLEEP_A: - - /* Enable automatic responses for A */ - st25r3916ClrRegisterBits( - ST25R3916_REG_PASSIVE_TARGET, (ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a)); - - /* Reset NFCA target */ - st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SLEEP); - - /* Set Target mode, Bit Rate detection and Listen Mode for NFC-A */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_MODE, - (ST25R3916_REG_MODE_targ | ST25R3916_REG_MODE_om_mask | - ST25R3916_REG_MODE_nfc_ar_mask), - (ST25R3916_REG_MODE_targ_targ | ST25R3916_REG_MODE_om3 | ST25R3916_REG_MODE_om0 | - ST25R3916_REG_MODE_nfc_ar_off)); - break; - - /*******************************************************************************/ - case RFAL_LM_STATE_SLEEP_AF: - - /* Enable automatic responses for A + F */ - st25r3916ClrRegisterBits( - ST25R3916_REG_PASSIVE_TARGET, - (ST25R3916_REG_PASSIVE_TARGET_d_212_424_1r | ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a)); - - /* Reset NFCA target state */ - st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SLEEP); - - /* Set Target mode, Bit Rate detection, Listen Mode for NFC-A and NFC-F */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_MODE, - (ST25R3916_REG_MODE_targ | ST25R3916_REG_MODE_om_mask | - ST25R3916_REG_MODE_nfc_ar_mask), - (ST25R3916_REG_MODE_targ_targ | ST25R3916_REG_MODE_om3 | ST25R3916_REG_MODE_om2 | - ST25R3916_REG_MODE_om0 | ST25R3916_REG_MODE_nfc_ar_off)); - break; - - /*******************************************************************************/ - case RFAL_LM_STATE_SLEEP_B: - /* REMARK: Support for CE-B would be added here */ - return ERR_NOT_IMPLEMENTED; - - /*******************************************************************************/ - default: - return ERR_PARAM; - } - - /* Ensure that the NFCIP1 mode is disabled */ - st25r3916ClrRegisterBits(ST25R3916_REG_ISO14443A_NFC, ST25R3916_REG_ISO14443A_NFC_nfc_f0); - - st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA); - - /* Clear and enable required IRQs */ - st25r3916ClearAndEnableInterrupts( - (ST25R3916_IRQ_MASK_NFCT | ST25R3916_IRQ_MASK_RXS | ST25R3916_IRQ_MASK_CRC | - ST25R3916_IRQ_MASK_ERR1 | ST25R3916_IRQ_MASK_ERR2 | ST25R3916_IRQ_MASK_PAR | - ST25R3916_IRQ_MASK_EON | ST25R3916_IRQ_MASK_EOF | gRFAL.Lm.mdIrqs)); - - /* Check whether the field was turn off right after the Sleep request */ - if(!rfalIsExtFieldOn()) { - /*rfalLogD( "RFAL: curState: %02X newState: %02X \r\n", gRFAL.Lm.state, RFAL_LM_STATE_NOT_INIT );*/ - - rfalListenStop(); - return ERR_LINK_LOSS; - } - - /*rfalLogD( "RFAL: curState: %02X newState: %02X \r\n", gRFAL.Lm.state, sleepSt );*/ - - /* Set the new Sleep State*/ - gRFAL.Lm.state = sleepSt; - gRFAL.state = RFAL_STATE_LM; - - gRFAL.Lm.rxBuf = rxBuf; - gRFAL.Lm.rxBufLen = rxBufLen; - gRFAL.Lm.rxLen = rxLen; - *gRFAL.Lm.rxLen = 0; - gRFAL.Lm.dataFlag = false; - - return ERR_NONE; -} - -/*******************************************************************************/ -rfalLmState rfalListenGetState(bool* dataFlag, rfalBitRate* lastBR) { - /* Allow state retrieval even if gRFAL.state != RFAL_STATE_LM so * - * that this Lm state can be used by caller after activation */ - - if(lastBR != NULL) { - *lastBR = gRFAL.Lm.brDetected; - } - - if(dataFlag != NULL) { - *dataFlag = gRFAL.Lm.dataFlag; - } - - return gRFAL.Lm.state; -} - -/*******************************************************************************/ -ReturnCode rfalListenSetState(rfalLmState newSt) { - ReturnCode ret; - rfalLmState newState; - bool reSetState; - - /* Check if RFAL is initialized */ - if(gRFAL.state < RFAL_STATE_INIT) { - return ERR_WRONG_STATE; - } - - /* SetState clears the Data flag */ - gRFAL.Lm.dataFlag = false; - newState = newSt; - ret = ERR_NONE; - - do { - reSetState = false; - - /*******************************************************************************/ - switch(newState) { - /*******************************************************************************/ - case RFAL_LM_STATE_POWER_OFF: - - /* Enable the receiver and reset logic */ - st25r3916SetRegisterBits(ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_rx_en); - st25r3916ExecuteCommand(ST25R3916_CMD_STOP); - - if((gRFAL.Lm.mdMask & RFAL_LM_MASK_NFCA) != 0U) { - /* Enable automatic responses for A */ - st25r3916ClrRegisterBits( - ST25R3916_REG_PASSIVE_TARGET, ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a); - - /* Prepares the NFCIP-1 Passive target logic to wait in the Sense/Idle state */ - st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SENSE); - } - - if((gRFAL.Lm.mdMask & RFAL_LM_MASK_NFCF) != 0U) { - /* Enable automatic responses for F */ - st25r3916ClrRegisterBits( - ST25R3916_REG_PASSIVE_TARGET, (ST25R3916_REG_PASSIVE_TARGET_d_212_424_1r)); - } - - if((gRFAL.Lm.mdMask & RFAL_LM_MASK_ACTIVE_P2P) != 0U) { - /* Ensure automatic response RF Collision Avoidance is back to only after Rx */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_nfc_ar_mask, - ST25R3916_REG_MODE_nfc_ar_auto_rx); - - /* Ensure that our field is Off, as automatic response RF Collision Avoidance may have been triggered */ - st25r3916TxOff(); - } - - /*******************************************************************************/ - /* Ensure that the NFCIP1 mode is disabled */ - st25r3916ClrRegisterBits( - ST25R3916_REG_ISO14443A_NFC, ST25R3916_REG_ISO14443A_NFC_nfc_f0); - - /*******************************************************************************/ - /* Clear and enable required IRQs */ - st25r3916DisableInterrupts(ST25R3916_IRQ_MASK_ALL); - - st25r3916ClearAndEnableInterrupts( - (ST25R3916_IRQ_MASK_NFCT | ST25R3916_IRQ_MASK_RXS | ST25R3916_IRQ_MASK_CRC | - ST25R3916_IRQ_MASK_ERR1 | ST25R3916_IRQ_MASK_OSC | ST25R3916_IRQ_MASK_ERR2 | - ST25R3916_IRQ_MASK_PAR | ST25R3916_IRQ_MASK_EON | ST25R3916_IRQ_MASK_EOF | - gRFAL.Lm.mdIrqs)); - - /*******************************************************************************/ - /* Clear the bitRate previously detected */ - gRFAL.Lm.brDetected = RFAL_BR_KEEP; - - /*******************************************************************************/ - /* Apply the initial mode */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_MODE, - (ST25R3916_REG_MODE_targ | ST25R3916_REG_MODE_om_mask | - ST25R3916_REG_MODE_nfc_ar_mask), - (uint8_t)gRFAL.Lm.mdReg); - - /*******************************************************************************/ - /* Check if external Field is already On */ - if(rfalIsExtFieldOn()) { - reSetState = true; - newState = RFAL_LM_STATE_IDLE; /* Set IDLE state */ - } -#if 1 /* Perform bit rate detection in Low power mode */ - else { - st25r3916ClrRegisterBits( - ST25R3916_REG_OP_CONTROL, - (ST25R3916_REG_OP_CONTROL_tx_en | ST25R3916_REG_OP_CONTROL_rx_en | - ST25R3916_REG_OP_CONTROL_en)); - } -#endif - break; - - /*******************************************************************************/ - case RFAL_LM_STATE_IDLE: - - /*******************************************************************************/ - /* Check if device is coming from Low Power bit rate detection */ - if(!st25r3916CheckReg( - ST25R3916_REG_OP_CONTROL, - ST25R3916_REG_OP_CONTROL_en, - ST25R3916_REG_OP_CONTROL_en)) { - /* Exit Low Power mode and confirm the temporarily enable */ - st25r3916SetRegisterBits( - ST25R3916_REG_OP_CONTROL, - (ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en)); - - if(!st25r3916CheckReg( - ST25R3916_REG_AUX_DISPLAY, - ST25R3916_REG_AUX_DISPLAY_osc_ok, - ST25R3916_REG_AUX_DISPLAY_osc_ok)) { - /* Wait for Oscilator ready */ - if(st25r3916WaitForInterruptsTimed( - ST25R3916_IRQ_MASK_OSC, ST25R3916_TOUT_OSC_STABLE) == 0U) { - ret = ERR_IO; - break; - } - } - } else { - st25r3916GetInterrupt(ST25R3916_IRQ_MASK_OSC); - } - - /*******************************************************************************/ - /* In Active P2P the Initiator may: Turn its field On; LM goes into IDLE state; - * Initiator sends an unexpected frame raising a Protocol error; Initiator - * turns its field Off and ST25R3916 performs the automatic RF Collision - * Avoidance keeping our field On; upon a Protocol error upper layer sets - * again the state to IDLE to clear dataFlag and wait for next data. - * - * Ensure that when upper layer calls SetState(IDLE), it restores initial - * configuration and that check whether an external Field is still present */ - if((gRFAL.Lm.mdMask & RFAL_LM_MASK_ACTIVE_P2P) != 0U) { - /* Ensure nfc_ar is reseted and back to only after Rx */ - st25r3916ExecuteCommand(ST25R3916_CMD_STOP); - st25r3916ChangeRegisterBits( - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_nfc_ar_mask, - ST25R3916_REG_MODE_nfc_ar_auto_rx); - - /* Ensure that our field is Off, as automatic response RF Collision Avoidance may have been triggered */ - st25r3916TxOff(); - - /* If external Field is no longer detected go back to POWER_OFF */ - if(!st25r3916IsExtFieldOn()) { - reSetState = true; - newState = RFAL_LM_STATE_POWER_OFF; /* Set POWER_OFF state */ - } - } - /*******************************************************************************/ - - /* If we are in ACTIVE_A, reEnable Listen for A before going to IDLE, otherwise do nothing */ - if(gRFAL.Lm.state == RFAL_LM_STATE_ACTIVE_A) { - /* Enable automatic responses for A and Reset NFCA target state */ - st25r3916ClrRegisterBits( - ST25R3916_REG_PASSIVE_TARGET, (ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a)); - st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SENSE); - } - - /* ReEnable the receiver */ - st25r3916ExecuteCommand(ST25R3916_CMD_CLEAR_FIFO); - st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA); - - /*******************************************************************************/ - /*Check if Observation Mode is enabled and set it on ST25R391x */ - rfalCheckEnableObsModeRx(); - break; - - /*******************************************************************************/ - case RFAL_LM_STATE_READY_F: - - /*******************************************************************************/ - /* If we're coming from BitRate detection mode, the Bit Rate Definition reg - * still has the last bit rate used. - * If a frame is received between setting the mode to Listen NFCA and - * setting Bit Rate Definition reg, it will raise a framing error. - * Set the bitrate immediately, and then the normal SetMode procedure */ - st25r3916SetBitrate((uint8_t)gRFAL.Lm.brDetected, (uint8_t)gRFAL.Lm.brDetected); - /*******************************************************************************/ - - /* Disable automatic responses for NFC-A */ - st25r3916SetRegisterBits( - ST25R3916_REG_PASSIVE_TARGET, (ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a)); - - /* Set Mode NFC-F only */ - ret = rfalSetMode(RFAL_MODE_LISTEN_NFCF, gRFAL.Lm.brDetected, gRFAL.Lm.brDetected); - gRFAL.state = RFAL_STATE_LM; /* Keep in Listen Mode */ - - /* ReEnable the receiver */ - st25r3916ExecuteCommand(ST25R3916_CMD_CLEAR_FIFO); - st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA); - - /* Clear any previous transmission errors (if Reader polled for other/unsupported technologies) */ - st25r3916GetInterrupt( - (ST25R3916_IRQ_MASK_PAR | ST25R3916_IRQ_MASK_CRC | ST25R3916_IRQ_MASK_ERR2 | - ST25R3916_IRQ_MASK_ERR1)); - - st25r3916EnableInterrupts( - ST25R3916_IRQ_MASK_RXE); /* Start looking for any incoming data */ - break; - - /*******************************************************************************/ - case RFAL_LM_STATE_CARDEMU_3: - - /* Set Listen NFCF mode */ - ret = rfalSetMode(RFAL_MODE_LISTEN_NFCF, gRFAL.Lm.brDetected, gRFAL.Lm.brDetected); - break; - - /*******************************************************************************/ - case RFAL_LM_STATE_READY_Ax: - case RFAL_LM_STATE_READY_A: - - /*******************************************************************************/ - /* If we're coming from BitRate detection mode, the Bit Rate Definition reg - * still has the last bit rate used. - * If a frame is received between setting the mode to Listen NFCA and - * setting Bit Rate Definition reg, it will raise a framing error. - * Set the bitrate immediately, and then the normal SetMode procedure */ - st25r3916SetBitrate((uint8_t)gRFAL.Lm.brDetected, (uint8_t)gRFAL.Lm.brDetected); - /*******************************************************************************/ - - /* Disable automatic responses for NFC-F */ - st25r3916SetRegisterBits( - ST25R3916_REG_PASSIVE_TARGET, (ST25R3916_REG_PASSIVE_TARGET_d_212_424_1r)); - - /* Set Mode NFC-A only */ - ret = rfalSetMode(RFAL_MODE_LISTEN_NFCA, gRFAL.Lm.brDetected, gRFAL.Lm.brDetected); - - gRFAL.state = RFAL_STATE_LM; /* Keep in Listen Mode */ - break; - - /*******************************************************************************/ - case RFAL_LM_STATE_ACTIVE_Ax: - case RFAL_LM_STATE_ACTIVE_A: - - /* Disable automatic responses for A */ - st25r3916SetRegisterBits( - ST25R3916_REG_PASSIVE_TARGET, (ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a)); - - /* Clear any previous transmission errors (if Reader polled for other/unsupported technologies) */ - st25r3916GetInterrupt( - (ST25R3916_IRQ_MASK_PAR | ST25R3916_IRQ_MASK_CRC | ST25R3916_IRQ_MASK_ERR2 | - ST25R3916_IRQ_MASK_ERR1)); - - st25r3916EnableInterrupts( - ST25R3916_IRQ_MASK_RXE); /* Start looking for any incoming data */ - break; - - case RFAL_LM_STATE_TARGET_F: - /* Disable Automatic response SENSF_REQ */ - st25r3916SetRegisterBits( - ST25R3916_REG_PASSIVE_TARGET, (ST25R3916_REG_PASSIVE_TARGET_d_212_424_1r)); - break; - - /*******************************************************************************/ - case RFAL_LM_STATE_SLEEP_A: - case RFAL_LM_STATE_SLEEP_B: - case RFAL_LM_STATE_SLEEP_AF: - /* These sleep states have to be set by the rfalListenSleepStart() method */ - return ERR_REQUEST; - - /*******************************************************************************/ - case RFAL_LM_STATE_CARDEMU_4A: - case RFAL_LM_STATE_CARDEMU_4B: - case RFAL_LM_STATE_TARGET_A: - /* States not handled by the LM, just keep state context */ - break; - - /*******************************************************************************/ - default: - return ERR_WRONG_STATE; - } - } while(reSetState); - - gRFAL.Lm.state = newState; - - // Call callback on state change - if(gRFAL.callbacks.state_changed_cb) { - gRFAL.callbacks.state_changed_cb(gRFAL.callbacks.ctx); - } - - return ret; -} - -#endif /* RFAL_FEATURE_LISTEN_MODE */ - -/******************************************************************************* - * Wake-Up Mode * - *******************************************************************************/ - -#if RFAL_FEATURE_WAKEUP_MODE - -/*******************************************************************************/ -ReturnCode rfalWakeUpModeStart(const rfalWakeUpConfig* config) { - uint8_t aux; - uint8_t reg; - uint32_t irqs; - - /* Check if RFAL is not initialized */ - if(gRFAL.state < RFAL_STATE_INIT) { - return ERR_WRONG_STATE; - } - - /* The Wake-Up procedure is explained in detail in Application Note: AN4985 */ - - if(config == NULL) { - gRFAL.wum.cfg.period = RFAL_WUM_PERIOD_200MS; - gRFAL.wum.cfg.irqTout = false; - gRFAL.wum.cfg.indAmp.enabled = true; - gRFAL.wum.cfg.indPha.enabled = false; - gRFAL.wum.cfg.cap.enabled = false; - gRFAL.wum.cfg.indAmp.delta = 2U; - gRFAL.wum.cfg.indAmp.reference = RFAL_WUM_REFERENCE_AUTO; - gRFAL.wum.cfg.indAmp.autoAvg = false; - - /*******************************************************************************/ - /* Check if AAT is enabled and if so make use of the SW Tag Detection */ - if(st25r3916CheckReg( - ST25R3916_REG_IO_CONF2, - ST25R3916_REG_IO_CONF2_aat_en, - ST25R3916_REG_IO_CONF2_aat_en)) { - gRFAL.wum.cfg.swTagDetect = true; - gRFAL.wum.cfg.indAmp.autoAvg = true; - gRFAL.wum.cfg.indAmp.aaWeight = RFAL_WUM_AA_WEIGHT_16; - } - } else { - gRFAL.wum.cfg = *config; - } - - /* Check for valid configuration */ - if((!gRFAL.wum.cfg.cap.enabled && !gRFAL.wum.cfg.indAmp.enabled && - !gRFAL.wum.cfg.indPha.enabled) || - (gRFAL.wum.cfg.cap.enabled && - (gRFAL.wum.cfg.indAmp.enabled || gRFAL.wum.cfg.indPha.enabled)) || - (gRFAL.wum.cfg.cap.enabled && gRFAL.wum.cfg.swTagDetect) || - ((gRFAL.wum.cfg.indAmp.reference > RFAL_WUM_REFERENCE_AUTO) || - (gRFAL.wum.cfg.indPha.reference > RFAL_WUM_REFERENCE_AUTO) || - (gRFAL.wum.cfg.cap.reference > RFAL_WUM_REFERENCE_AUTO))) { - return ERR_PARAM; - } - - irqs = ST25R3916_IRQ_MASK_NONE; - - /* Disable Tx, Rx, External Field Detector and set default ISO14443A mode */ - st25r3916TxRxOff(); - st25r3916ClrRegisterBits(ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_en_fd_mask); - st25r3916ChangeRegisterBits( - ST25R3916_REG_MODE, - (ST25R3916_REG_MODE_targ | ST25R3916_REG_MODE_om_mask), - (ST25R3916_REG_MODE_targ_init | ST25R3916_REG_MODE_om_iso14443a)); - - /* Set Analog configurations for Wake-up On event */ - rfalSetAnalogConfig((RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_WAKEUP_ON)); - - /*******************************************************************************/ - /* Prepare Wake-Up Timer Control Register */ - reg = - (uint8_t)(((uint8_t)gRFAL.wum.cfg.period & 0x0FU) << ST25R3916_REG_WUP_TIMER_CONTROL_wut_shift); - reg |= - (uint8_t)(((uint8_t)gRFAL.wum.cfg.period < (uint8_t)RFAL_WUM_PERIOD_100MS) ? ST25R3916_REG_WUP_TIMER_CONTROL_wur : 0x00U); - - if(gRFAL.wum.cfg.irqTout || gRFAL.wum.cfg.swTagDetect) { - reg |= ST25R3916_REG_WUP_TIMER_CONTROL_wto; - irqs |= ST25R3916_IRQ_MASK_WT; - } - - /* Check if HW Wake-up is to be used or SW Tag detection */ - if(gRFAL.wum.cfg.swTagDetect) { - gRFAL.wum.cfg.indAmp.reference = 0U; - gRFAL.wum.cfg.indPha.reference = 0U; - gRFAL.wum.cfg.cap.reference = 0U; - } else { - /*******************************************************************************/ - /* Check if Inductive Amplitude is to be performed */ - if(gRFAL.wum.cfg.indAmp.enabled) { - aux = - (uint8_t)((gRFAL.wum.cfg.indAmp.delta) << ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d_shift); - aux |= - (uint8_t)(gRFAL.wum.cfg.indAmp.aaInclMeas ? ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aam : 0x00U); - aux |= - (uint8_t)(((uint8_t)gRFAL.wum.cfg.indAmp.aaWeight << ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aew_shift) & ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aew_mask); - aux |= - (uint8_t)(gRFAL.wum.cfg.indAmp.autoAvg ? ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_ae : 0x00U); - - st25r3916WriteRegister(ST25R3916_REG_AMPLITUDE_MEASURE_CONF, aux); - - /* Only need to set the reference if not using Auto Average */ - if(!gRFAL.wum.cfg.indAmp.autoAvg) { - if(gRFAL.wum.cfg.indAmp.reference == RFAL_WUM_REFERENCE_AUTO) { - st25r3916MeasureAmplitude(&aux); - gRFAL.wum.cfg.indAmp.reference = aux; - } - st25r3916WriteRegister( - ST25R3916_REG_AMPLITUDE_MEASURE_REF, (uint8_t)gRFAL.wum.cfg.indAmp.reference); - } - - reg |= ST25R3916_REG_WUP_TIMER_CONTROL_wam; - irqs |= ST25R3916_IRQ_MASK_WAM; - } - - /*******************************************************************************/ - /* Check if Inductive Phase is to be performed */ - if(gRFAL.wum.cfg.indPha.enabled) { - aux = - (uint8_t)((gRFAL.wum.cfg.indPha.delta) << ST25R3916_REG_PHASE_MEASURE_CONF_pm_d_shift); - aux |= - (uint8_t)(gRFAL.wum.cfg.indPha.aaInclMeas ? ST25R3916_REG_PHASE_MEASURE_CONF_pm_aam : 0x00U); - aux |= - (uint8_t)(((uint8_t)gRFAL.wum.cfg.indPha.aaWeight << ST25R3916_REG_PHASE_MEASURE_CONF_pm_aew_shift) & ST25R3916_REG_PHASE_MEASURE_CONF_pm_aew_mask); - aux |= - (uint8_t)(gRFAL.wum.cfg.indPha.autoAvg ? ST25R3916_REG_PHASE_MEASURE_CONF_pm_ae : 0x00U); - - st25r3916WriteRegister(ST25R3916_REG_PHASE_MEASURE_CONF, aux); - - /* Only need to set the reference if not using Auto Average */ - if(!gRFAL.wum.cfg.indPha.autoAvg) { - if(gRFAL.wum.cfg.indPha.reference == RFAL_WUM_REFERENCE_AUTO) { - st25r3916MeasurePhase(&aux); - gRFAL.wum.cfg.indPha.reference = aux; - } - st25r3916WriteRegister( - ST25R3916_REG_PHASE_MEASURE_REF, (uint8_t)gRFAL.wum.cfg.indPha.reference); - } - - reg |= ST25R3916_REG_WUP_TIMER_CONTROL_wph; - irqs |= ST25R3916_IRQ_MASK_WPH; - } - - /*******************************************************************************/ - /* Check if Capacitive is to be performed */ - if(gRFAL.wum.cfg.cap.enabled) { - /*******************************************************************************/ - /* Perform Capacitive sensor calibration */ - - /* Disable Oscillator and Field */ - st25r3916ClrRegisterBits( - ST25R3916_REG_OP_CONTROL, - (ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_tx_en)); - - /* Sensor gain should be configured on Analog Config: RFAL_ANALOG_CONFIG_CHIP_WAKEUP_ON */ - - /* Perform calibration procedure */ - st25r3916CalibrateCapacitiveSensor(NULL); - - /*******************************************************************************/ - aux = - (uint8_t)((gRFAL.wum.cfg.cap.delta) << ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d_shift); - aux |= - (uint8_t)(gRFAL.wum.cfg.cap.aaInclMeas ? ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aam : 0x00U); - aux |= - (uint8_t)(((uint8_t)gRFAL.wum.cfg.cap.aaWeight << ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aew_shift) & ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aew_mask); - aux |= - (uint8_t)(gRFAL.wum.cfg.cap.autoAvg ? ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_ae : 0x00U); - - st25r3916WriteRegister(ST25R3916_REG_CAPACITANCE_MEASURE_CONF, aux); - - /* Only need to set the reference if not using Auto Average */ - if(!gRFAL.wum.cfg.cap.autoAvg || gRFAL.wum.cfg.swTagDetect) { - if(gRFAL.wum.cfg.indPha.reference == RFAL_WUM_REFERENCE_AUTO) { - st25r3916MeasureCapacitance(&aux); - gRFAL.wum.cfg.cap.reference = aux; - } - st25r3916WriteRegister( - ST25R3916_REG_CAPACITANCE_MEASURE_REF, (uint8_t)gRFAL.wum.cfg.cap.reference); - } - - reg |= ST25R3916_REG_WUP_TIMER_CONTROL_wcap; - irqs |= ST25R3916_IRQ_MASK_WCAP; - } - } - - /* Disable and clear all interrupts except Wake-Up IRQs */ - st25r3916DisableInterrupts(ST25R3916_IRQ_MASK_ALL); - st25r3916GetInterrupt(irqs); - st25r3916EnableInterrupts(irqs); - - /* Enable Low Power Wake-Up Mode (Disable: Oscilattor, Tx, Rx and External Field Detector) */ - st25r3916WriteRegister(ST25R3916_REG_WUP_TIMER_CONTROL, reg); - st25r3916ChangeRegisterBits( - ST25R3916_REG_OP_CONTROL, - (ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en | - ST25R3916_REG_OP_CONTROL_tx_en | ST25R3916_REG_OP_CONTROL_en_fd_mask | - ST25R3916_REG_OP_CONTROL_wu), - ST25R3916_REG_OP_CONTROL_wu); - - gRFAL.wum.state = RFAL_WUM_STATE_ENABLED; - gRFAL.state = RFAL_STATE_WUM; - - return ERR_NONE; -} - -/*******************************************************************************/ -bool rfalWakeUpModeHasWoke(void) { - return (gRFAL.wum.state >= RFAL_WUM_STATE_ENABLED_WOKE); -} - -/*******************************************************************************/ -static uint16_t rfalWakeUpModeFilter(uint16_t curRef, uint16_t curVal, uint8_t weight) { - uint16_t newRef; - - /* Perform the averaging|filter as describded in ST25R3916 DS */ - - /* Avoid signed arithmetics by spliting in two cases */ - if(curVal > curRef) { - newRef = curRef + ((curVal - curRef) / weight); - - /* In order for the reference to converge to final value * - * increment once the diff is smaller that the weight */ - if((curVal != curRef) && (curRef == newRef)) { - newRef &= 0xFF00U; - newRef += 0x0100U; - } - } else { - newRef = curRef - ((curRef - curVal) / weight); - - /* In order for the reference to converge to final value * - * decrement once the diff is smaller that the weight */ - if((curVal != curRef) && (curRef == newRef)) { - newRef &= 0xFF00U; - } - } - - return newRef; -} - -/*******************************************************************************/ -static void rfalRunWakeUpModeWorker(void) { - uint32_t irqs; - uint8_t reg; - uint16_t value; - uint16_t delta; - - if(gRFAL.state != RFAL_STATE_WUM) { - return; - } - - switch(gRFAL.wum.state) { - case RFAL_WUM_STATE_ENABLED: - case RFAL_WUM_STATE_ENABLED_WOKE: - - irqs = st25r3916GetInterrupt( - (ST25R3916_IRQ_MASK_WT | ST25R3916_IRQ_MASK_WAM | ST25R3916_IRQ_MASK_WPH | - ST25R3916_IRQ_MASK_WCAP)); - if(irqs == ST25R3916_IRQ_MASK_NONE) { - break; /* No interrupt to process */ - } - - /*******************************************************************************/ - /* Check and mark which measurement(s) cause interrupt */ - if((irqs & ST25R3916_IRQ_MASK_WAM) != 0U) { - st25r3916ReadRegister(ST25R3916_REG_AMPLITUDE_MEASURE_RESULT, ®); - gRFAL.wum.state = RFAL_WUM_STATE_ENABLED_WOKE; - } - - if((irqs & ST25R3916_IRQ_MASK_WPH) != 0U) { - st25r3916ReadRegister(ST25R3916_REG_PHASE_MEASURE_RESULT, ®); - gRFAL.wum.state = RFAL_WUM_STATE_ENABLED_WOKE; - } - - if((irqs & ST25R3916_IRQ_MASK_WCAP) != 0U) { - st25r3916ReadRegister(ST25R3916_REG_CAPACITANCE_MEASURE_RESULT, ®); - gRFAL.wum.state = RFAL_WUM_STATE_ENABLED_WOKE; - } - - if((irqs & ST25R3916_IRQ_MASK_WT) != 0U) { - /*******************************************************************************/ - if(gRFAL.wum.cfg.swTagDetect) { - /* Enable Ready mode and wait the settle time */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_OP_CONTROL, - (ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_wu), - ST25R3916_REG_OP_CONTROL_en); - platformDelay(RFAL_ST25R3916_AAT_SETTLE); - - /*******************************************************************************/ - if(gRFAL.wum.cfg.indAmp.enabled) { - /* Perform amplitude measurement */ - st25r3916MeasureAmplitude(®); - - /* Convert inputs to TD format */ - value = rfalConvTDFormat(reg); - delta = rfalConvTDFormat(gRFAL.wum.cfg.indAmp.delta); - - /* Set first measurement as reference */ - if(gRFAL.wum.cfg.indAmp.reference == 0U) { - gRFAL.wum.cfg.indAmp.reference = value; - } - - /* Check if device should be woken */ - if((value >= (gRFAL.wum.cfg.indAmp.reference + delta)) || - (value <= (gRFAL.wum.cfg.indAmp.reference - delta))) { - gRFAL.wum.state = RFAL_WUM_STATE_ENABLED_WOKE; - break; - } - - /* Update moving reference if enabled */ - if(gRFAL.wum.cfg.indAmp.autoAvg) { - gRFAL.wum.cfg.indAmp.reference = rfalWakeUpModeFilter( - gRFAL.wum.cfg.indAmp.reference, - value, - (RFAL_WU_MIN_WEIGHT_VAL << (uint8_t)gRFAL.wum.cfg.indAmp.aaWeight)); - } - } - - /*******************************************************************************/ - if(gRFAL.wum.cfg.indPha.enabled) { - /* Perform Phase measurement */ - st25r3916MeasurePhase(®); - - /* Convert inputs to TD format */ - value = rfalConvTDFormat(reg); - delta = rfalConvTDFormat(gRFAL.wum.cfg.indPha.delta); - - /* Set first measurement as reference */ - if(gRFAL.wum.cfg.indPha.reference == 0U) { - gRFAL.wum.cfg.indPha.reference = value; - } - - /* Check if device should be woken */ - if((value >= (gRFAL.wum.cfg.indPha.reference + delta)) || - (value <= (gRFAL.wum.cfg.indPha.reference - delta))) { - gRFAL.wum.state = RFAL_WUM_STATE_ENABLED_WOKE; - break; - } - - /* Update moving reference if enabled */ - if(gRFAL.wum.cfg.indPha.autoAvg) { - gRFAL.wum.cfg.indPha.reference = rfalWakeUpModeFilter( - gRFAL.wum.cfg.indPha.reference, - value, - (RFAL_WU_MIN_WEIGHT_VAL << (uint8_t)gRFAL.wum.cfg.indPha.aaWeight)); - } - } - - /* Re-Enable low power Wake-Up mode for wto to trigger another measurement(s) */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_OP_CONTROL, - (ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_wu), - (ST25R3916_REG_OP_CONTROL_wu)); - } - } - break; - - default: - /* MISRA 16.4: no empty default statement (a comment being enough) */ - break; - } -} - -/*******************************************************************************/ -ReturnCode rfalWakeUpModeStop(void) { - /* Check if RFAL is in Wake-up mode */ - if(gRFAL.state != RFAL_STATE_WUM) { - return ERR_WRONG_STATE; - } - - gRFAL.wum.state = RFAL_WUM_STATE_NOT_INIT; - - /* Disable Wake-Up Mode */ - st25r3916ClrRegisterBits(ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_wu); - st25r3916DisableInterrupts( - (ST25R3916_IRQ_MASK_WT | ST25R3916_IRQ_MASK_WAM | ST25R3916_IRQ_MASK_WPH | - ST25R3916_IRQ_MASK_WCAP)); - - /* Re-Enable External Field Detector as: Automatics */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_OP_CONTROL, - ST25R3916_REG_OP_CONTROL_en_fd_mask, - ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); - - /* Re-Enable the Oscillator */ - st25r3916OscOn(); - - /* Set Analog configurations for Wake-up Off event */ - rfalSetAnalogConfig((RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_WAKEUP_OFF)); - - return ERR_NONE; -} - -#endif /* RFAL_FEATURE_WAKEUP_MODE */ - -/******************************************************************************* - * Low-Power Mode * - *******************************************************************************/ - -#if RFAL_FEATURE_LOWPOWER_MODE - -/*******************************************************************************/ -ReturnCode rfalLowPowerModeStart(void) { - /* Check if RFAL is not initialized */ - if(gRFAL.state < RFAL_STATE_INIT) { - return ERR_WRONG_STATE; - } - - /* Stop any ongoing activity and set the device in low power by disabling oscillator, transmitter, receiver and external field detector */ - st25r3916ExecuteCommand(ST25R3916_CMD_STOP); - st25r3916ClrRegisterBits( - ST25R3916_REG_OP_CONTROL, - (ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en | - ST25R3916_REG_OP_CONTROL_wu | ST25R3916_REG_OP_CONTROL_tx_en | - ST25R3916_REG_OP_CONTROL_en_fd_mask)); - - rfalSetAnalogConfig((RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_LOWPOWER_ON)); - - gRFAL.state = RFAL_STATE_IDLE; - gRFAL.lpm.isRunning = true; - - platformDisableIrqCallback(); - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalLowPowerModeStop(void) { - ReturnCode ret; - - platformEnableIrqCallback(); - - /* Check if RFAL is on right state */ - if(!gRFAL.lpm.isRunning) { - return ERR_WRONG_STATE; - } - - /* Re-enable device */ - EXIT_ON_ERR(ret, st25r3916OscOn()); - st25r3916ChangeRegisterBits( - ST25R3916_REG_OP_CONTROL, - ST25R3916_REG_OP_CONTROL_en_fd_mask, - ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); - - rfalSetAnalogConfig((RFAL_ANALOG_CONFIG_TECH_CHIP | RFAL_ANALOG_CONFIG_CHIP_LOWPOWER_OFF)); - - gRFAL.state = RFAL_STATE_INIT; - return ERR_NONE; -} - -#endif /* RFAL_FEATURE_LOWPOWER_MODE */ - -/******************************************************************************* - * RF Chip * - *******************************************************************************/ - -/*******************************************************************************/ -ReturnCode rfalChipWriteReg(uint16_t reg, const uint8_t* values, uint8_t len) { - if(!st25r3916IsRegValid((uint8_t)reg)) { - return ERR_PARAM; - } - - return st25r3916WriteMultipleRegisters((uint8_t)reg, values, len); -} - -/*******************************************************************************/ -ReturnCode rfalChipReadReg(uint16_t reg, uint8_t* values, uint8_t len) { - if(!st25r3916IsRegValid((uint8_t)reg)) { - return ERR_PARAM; - } - - return st25r3916ReadMultipleRegisters((uint8_t)reg, values, len); -} - -/*******************************************************************************/ -ReturnCode rfalChipExecCmd(uint16_t cmd) { - if(!st25r3916IsCmdValid((uint8_t)cmd)) { - return ERR_PARAM; - } - - return st25r3916ExecuteCommand((uint8_t)cmd); -} - -/*******************************************************************************/ -ReturnCode rfalChipWriteTestReg(uint16_t reg, uint8_t value) { - return st25r3916WriteTestRegister((uint8_t)reg, value); -} - -/*******************************************************************************/ -ReturnCode rfalChipReadTestReg(uint16_t reg, uint8_t* value) { - return st25r3916ReadTestRegister((uint8_t)reg, value); -} - -/*******************************************************************************/ -ReturnCode rfalChipChangeRegBits(uint16_t reg, uint8_t valueMask, uint8_t value) { - if(!st25r3916IsRegValid((uint8_t)reg)) { - return ERR_PARAM; - } - - return st25r3916ChangeRegisterBits((uint8_t)reg, valueMask, value); -} - -/*******************************************************************************/ -ReturnCode rfalChipChangeTestRegBits(uint16_t reg, uint8_t valueMask, uint8_t value) { - st25r3916ChangeTestRegisterBits((uint8_t)reg, valueMask, value); - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalChipSetRFO(uint8_t rfo) { - return st25r3916ChangeRegisterBits( - ST25R3916_REG_TX_DRIVER, ST25R3916_REG_TX_DRIVER_d_res_mask, rfo); -} - -/*******************************************************************************/ -ReturnCode rfalChipGetRFO(uint8_t* result) { - ReturnCode ret; - - ret = st25r3916ReadRegister(ST25R3916_REG_TX_DRIVER, result); - - (*result) = ((*result) & ST25R3916_REG_TX_DRIVER_d_res_mask); - - return ret; -} - -/*******************************************************************************/ -ReturnCode rfalChipMeasureAmplitude(uint8_t* result) { - ReturnCode err; - uint8_t reg_opc, reg_mode, reg_conf1, reg_conf2; - - /* Save registers which will be adjusted below */ - st25r3916ReadRegister(ST25R3916_REG_OP_CONTROL, ®_opc); - st25r3916ReadRegister(ST25R3916_REG_MODE, ®_mode); - st25r3916ReadRegister(ST25R3916_REG_RX_CONF1, ®_conf1); - st25r3916ReadRegister(ST25R3916_REG_RX_CONF2, ®_conf2); - - /* Set values as per defaults of DS. These regs/bits influence receiver chain and change amplitude */ - /* Doing so achieves an amplitude comparable over a complete polling cylce */ - st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, (reg_opc & ~ST25R3916_REG_OP_CONTROL_rx_chn)); - st25r3916WriteRegister( - ST25R3916_REG_MODE, - ST25R3916_REG_MODE_om_iso14443a | ST25R3916_REG_MODE_targ_init | - ST25R3916_REG_MODE_tr_am_ook | ST25R3916_REG_MODE_nfc_ar_off); - st25r3916WriteRegister( - ST25R3916_REG_RX_CONF1, (reg_conf1 & ~ST25R3916_REG_RX_CONF1_ch_sel_AM)); - st25r3916WriteRegister( - ST25R3916_REG_RX_CONF2, - ((reg_conf2 & ~(ST25R3916_REG_RX_CONF2_demod_mode | ST25R3916_REG_RX_CONF2_amd_sel)) | - ST25R3916_REG_RX_CONF2_amd_sel_peak)); - - /* Perform the actual measurement */ - err = st25r3916MeasureAmplitude(result); - - /* Restore values */ - st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, reg_opc); - st25r3916WriteRegister(ST25R3916_REG_MODE, reg_mode); - st25r3916WriteRegister(ST25R3916_REG_RX_CONF1, reg_conf1); - st25r3916WriteRegister(ST25R3916_REG_RX_CONF2, reg_conf2); - - return err; -} - -/*******************************************************************************/ -ReturnCode rfalChipMeasurePhase(uint8_t* result) { - st25r3916MeasurePhase(result); - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalChipMeasureCapacitance(uint8_t* result) { - st25r3916MeasureCapacitance(result); - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode rfalChipMeasurePowerSupply(uint8_t param, uint8_t* result) { - *result = st25r3916MeasurePowerSupply(param); - - return ERR_NONE; -} - -/*******************************************************************************/ -extern uint8_t invalid_size_of_stream_configs - [(sizeof(struct st25r3916StreamConfig) == sizeof(struct iso15693StreamConfig)) ? 1 : (-1)]; diff --git a/lib/ST25RFAL002/source/st25r3916/st25r3916.c b/lib/ST25RFAL002/source/st25r3916/st25r3916.c deleted file mode 100644 index 6d8e07b1ab1..00000000000 --- a/lib/ST25RFAL002/source/st25r3916/st25r3916.c +++ /dev/null @@ -1,792 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R3916 firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file - * - * \author Gustavo Patricio - * - * \brief ST25R3916 high level interface - * - */ - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ - -#include "st25r3916.h" -#include "st25r3916_com.h" -#include "st25r3916_led.h" -#include "st25r3916_irq.h" -#include "utils.h" - -/* -****************************************************************************** -* LOCAL DEFINES -****************************************************************************** -*/ - -#define ST25R3916_SUPPLY_THRESHOLD \ - 3600U /*!< Power supply measure threshold between 3.3V or 5V */ -#define ST25R3916_NRT_MAX \ - 0xFFFFU /*!< Max Register value of NRT */ - -#define ST25R3916_TOUT_MEASURE_VDD \ - 100U /*!< Max duration time of Measure Power Supply command Datasheet: 25us */ -#define ST25R3916_TOUT_MEASURE_AMPLITUDE \ - 10U /*!< Max duration time of Measure Amplitude command Datasheet: 25us */ -#define ST25R3916_TOUT_MEASURE_PHASE \ - 10U /*!< Max duration time of Measure Phase command Datasheet: 25us */ -#define ST25R3916_TOUT_MEASURE_CAPACITANCE \ - 10U /*!< Max duration time of Measure Capacitance command Datasheet: 25us */ -#define ST25R3916_TOUT_CALIBRATE_CAP_SENSOR \ - 4U /*!< Max duration Calibrate Capacitive Sensor command Datasheet: 3ms */ -#define ST25R3916_TOUT_ADJUST_REGULATORS \ - 6U /*!< Max duration time of Adjust Regulators command Datasheet: 5ms */ -#define ST25R3916_TOUT_CA \ - 10U /*!< Max duration time of Collision Avoidance command */ - -#define ST25R3916_TEST_REG_PATTERN \ - 0x33U /*!< Register Read Write test pattern used during selftest */ -#define ST25R3916_TEST_WU_TOUT \ - 12U /*!< Timeout used on WU timer during self test */ -#define ST25R3916_TEST_TMR_TOUT \ - 20U /*!< Timeout used during self test */ -#define ST25R3916_TEST_TMR_TOUT_DELTA \ - 2U /*!< Timeout used during self test */ -#define ST25R3916_TEST_TMR_TOUT_8FC \ - (ST25R3916_TEST_TMR_TOUT * 16950U) /*!< Timeout in 8/fc */ - -/* -****************************************************************************** -* LOCAL CONSTANTS -****************************************************************************** -*/ - -/* -****************************************************************************** -* LOCAL VARIABLES -****************************************************************************** -*/ - -static uint32_t gST25R3916NRT_64fcs; - -/* -****************************************************************************** -* LOCAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -/* - ****************************************************************************** - * LOCAL FUNCTION - ****************************************************************************** - */ - -ReturnCode st25r3916ExecuteCommandAndGetResult( - uint8_t cmd, - uint8_t resReg, - uint8_t tout, - uint8_t* result) { - /* Clear and enable Direct Command interrupt */ - st25r3916GetInterrupt(ST25R3916_IRQ_MASK_DCT); - st25r3916EnableInterrupts(ST25R3916_IRQ_MASK_DCT); - - st25r3916ExecuteCommand(cmd); - - st25r3916WaitForInterruptsTimed(ST25R3916_IRQ_MASK_DCT, tout); - st25r3916DisableInterrupts(ST25R3916_IRQ_MASK_DCT); - - /* After execution read out the result if the pointer is not NULL */ - if(result != NULL) { - st25r3916ReadRegister(resReg, result); - } - - return ERR_NONE; -} - -/* -****************************************************************************** -* GLOBAL FUNCTIONS -****************************************************************************** -*/ - -ReturnCode st25r3916Initialize(void) { - uint16_t vdd_mV; - ReturnCode ret; - - /* Set default state on the ST25R3916 */ - st25r3916ExecuteCommand(ST25R3916_CMD_SET_DEFAULT); - -#ifndef RFAL_USE_I2C - /* Increase MISO driving level as SPI can go up to 10MHz */ - st25r3916WriteRegister(ST25R3916_REG_IO_CONF2, ST25R3916_REG_IO_CONF2_io_drv_lvl); -#endif /* RFAL_USE_I2C */ - - if(!st25r3916CheckChipID(NULL)) { - platformErrorHandle(); - return ERR_HW_MISMATCH; - } - - st25r3916InitInterrupts(); - st25r3916ledInit(); - - gST25R3916NRT_64fcs = 0; - -#ifndef RFAL_USE_I2C - /* Enable pull downs on MISO line */ - st25r3916SetRegisterBits( - ST25R3916_REG_IO_CONF2, - (ST25R3916_REG_IO_CONF2_miso_pd1 | ST25R3916_REG_IO_CONF2_miso_pd2)); -#endif /* RFAL_USE_I2C */ - - /* Disable internal overheat protection */ - st25r3916ChangeTestRegisterBits(0x04, 0x10, 0x10); - -#ifdef ST25R_SELFTEST - /****************************************************************************** - * Check communication interface: - * - write a pattern in a register - * - reads back the register value - * - return ERR_IO in case the read value is different - */ - st25r3916WriteRegister(ST25R3916_REG_BIT_RATE, ST25R3916_TEST_REG_PATTERN); - if(!st25r3916CheckReg( - ST25R3916_REG_BIT_RATE, - (ST25R3916_REG_BIT_RATE_rxrate_mask | ST25R3916_REG_BIT_RATE_txrate_mask), - ST25R3916_TEST_REG_PATTERN)) { - platformErrorHandle(); - return ERR_IO; - } - - /* Restore default value */ - st25r3916WriteRegister(ST25R3916_REG_BIT_RATE, 0x00); - - /* - * Check IRQ Handling: - * - use the Wake-up timer to trigger an IRQ - * - wait the Wake-up timer interrupt - * - return ERR_TIMEOUT when the Wake-up timer interrupt is not received - */ - st25r3916WriteRegister( - ST25R3916_REG_WUP_TIMER_CONTROL, - ST25R3916_REG_WUP_TIMER_CONTROL_wur | ST25R3916_REG_WUP_TIMER_CONTROL_wto); - st25r3916EnableInterrupts(ST25R3916_IRQ_MASK_WT); - st25r3916ExecuteCommand(ST25R3916_CMD_START_WUP_TIMER); - if(st25r3916WaitForInterruptsTimed(ST25R3916_IRQ_MASK_WT, ST25R3916_TEST_WU_TOUT) == 0U) { - platformErrorHandle(); - return ERR_TIMEOUT; - } - st25r3916DisableInterrupts(ST25R3916_IRQ_MASK_WT); - st25r3916WriteRegister(ST25R3916_REG_WUP_TIMER_CONTROL, 0U); - /*******************************************************************************/ -#endif /* ST25R_SELFTEST */ - - /* Enable Oscillator and wait until it gets stable */ - ret = st25r3916OscOn(); - if(ret != ERR_NONE) { - platformErrorHandle(); - return ret; - } - - /* Measure VDD and set sup3V bit according to Power supplied */ - vdd_mV = st25r3916MeasureVoltage(ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd); - st25r3916ChangeRegisterBits( - ST25R3916_REG_IO_CONF2, - ST25R3916_REG_IO_CONF2_sup3V, - ((vdd_mV < ST25R3916_SUPPLY_THRESHOLD) ? ST25R3916_REG_IO_CONF2_sup3V_3V : - ST25R3916_REG_IO_CONF2_sup3V_5V)); - - /* Make sure Transmitter and Receiver are disabled */ - st25r3916TxRxOff(); - -#ifdef ST25R_SELFTEST_TIMER - /****************************************************************************** - * Check SW timer operation : - * - use the General Purpose timer to measure an amount of time - * - test whether an interrupt is seen when less time was given - * - test whether an interrupt is seen when sufficient time was given - */ - - st25r3916EnableInterrupts(ST25R3916_IRQ_MASK_GPE); - st25r3916SetStartGPTimer( - (uint16_t)ST25R3916_TEST_TMR_TOUT_8FC, ST25R3916_REG_TIMER_EMV_CONTROL_gptc_no_trigger); - if(st25r3916WaitForInterruptsTimed( - ST25R3916_IRQ_MASK_GPE, (ST25R3916_TEST_TMR_TOUT - ST25R3916_TEST_TMR_TOUT_DELTA)) != - 0U) { - platformErrorHandle(); - return ERR_SYSTEM; - } - - /* Stop all activities to stop the GP timer */ - st25r3916ExecuteCommand(ST25R3916_CMD_STOP); - st25r3916ClearAndEnableInterrupts(ST25R3916_IRQ_MASK_GPE); - st25r3916SetStartGPTimer( - (uint16_t)ST25R3916_TEST_TMR_TOUT_8FC, ST25R3916_REG_TIMER_EMV_CONTROL_gptc_no_trigger); - if(st25r3916WaitForInterruptsTimed( - ST25R3916_IRQ_MASK_GPE, (ST25R3916_TEST_TMR_TOUT + ST25R3916_TEST_TMR_TOUT_DELTA)) == - 0U) { - platformErrorHandle(); - return ERR_SYSTEM; - } - - /* Stop all activities to stop the GP timer */ - st25r3916ExecuteCommand(ST25R3916_CMD_STOP); - /*******************************************************************************/ -#endif /* ST25R_SELFTEST_TIMER */ - - /* After reset all interrupts are enabled, so disable them at first */ - st25r3916DisableInterrupts(ST25R3916_IRQ_MASK_ALL); - - /* And clear them, just to be sure */ - st25r3916ClearInterrupts(); - - return ERR_NONE; -} - -/*******************************************************************************/ -void st25r3916Deinitialize(void) { - st25r3916DisableInterrupts(ST25R3916_IRQ_MASK_ALL); - - /* Disabe Tx and Rx, Keep OSC On */ - st25r3916TxRxOff(); - - return; -} - -/*******************************************************************************/ -ReturnCode st25r3916OscOn(void) { - /* Check if oscillator is already turned on and stable */ - /* Use ST25R3916_REG_OP_CONTROL_en instead of ST25R3916_REG_AUX_DISPLAY_osc_ok to be on the safe side */ - if(!st25r3916CheckReg( - ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_en, ST25R3916_REG_OP_CONTROL_en)) { - /* Clear any eventual previous oscillator IRQ */ - st25r3916GetInterrupt(ST25R3916_IRQ_MASK_OSC); - - /* Enable oscillator frequency stable interrupt */ - st25r3916EnableInterrupts(ST25R3916_IRQ_MASK_OSC); - - /* Enable oscillator and regulator output */ - st25r3916SetRegisterBits(ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_en); - - /* Wait for the oscillator interrupt */ - st25r3916WaitForInterruptsTimed(ST25R3916_IRQ_MASK_OSC, ST25R3916_TOUT_OSC_STABLE); - st25r3916DisableInterrupts(ST25R3916_IRQ_MASK_OSC); - } - - if(!st25r3916CheckReg( - ST25R3916_REG_AUX_DISPLAY, - ST25R3916_REG_AUX_DISPLAY_osc_ok, - ST25R3916_REG_AUX_DISPLAY_osc_ok)) { - return ERR_SYSTEM; - } - - return ERR_NONE; -} - -/*******************************************************************************/ -uint8_t st25r3916MeasurePowerSupply(uint8_t mpsv) { - uint8_t result; - - /* Set the source of direct command: Measure Power Supply Voltage */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_REGULATOR_CONTROL, ST25R3916_REG_REGULATOR_CONTROL_mpsv_mask, mpsv); - - /* Execute command: Measure Power Supply Voltage */ - st25r3916ExecuteCommandAndGetResult( - ST25R3916_CMD_MEASURE_VDD, ST25R3916_REG_AD_RESULT, ST25R3916_TOUT_MEASURE_VDD, &result); - - return result; -} - -/*******************************************************************************/ -uint16_t st25r3916MeasureVoltage(uint8_t mpsv) { - uint8_t result; - uint16_t mV; - - result = st25r3916MeasurePowerSupply(mpsv); - - /* Convert cmd output into mV (each step represents 23.4 mV )*/ - mV = ((uint16_t)result) * 23U; - mV += (((((uint16_t)result) * 4U) + 5U) / 10U); - - return mV; -} - -/*******************************************************************************/ -ReturnCode st25r3916AdjustRegulators(uint16_t* result_mV) { - uint8_t result; - - /* Reset logic and set regulated voltages to be defined by result of Adjust Regulators command */ - st25r3916SetRegisterBits( - ST25R3916_REG_REGULATOR_CONTROL, ST25R3916_REG_REGULATOR_CONTROL_reg_s); - st25r3916ClrRegisterBits( - ST25R3916_REG_REGULATOR_CONTROL, ST25R3916_REG_REGULATOR_CONTROL_reg_s); - - /* Execute Adjust regulators cmd and retrieve result */ - st25r3916ExecuteCommandAndGetResult( - ST25R3916_CMD_ADJUST_REGULATORS, - ST25R3916_REG_REGULATOR_RESULT, - ST25R3916_TOUT_ADJUST_REGULATORS, - &result); - - /* Calculate result in mV */ - result >>= ST25R3916_REG_REGULATOR_RESULT_reg_shift; - - if(result_mV != NULL) { - if(st25r3916CheckReg( - ST25R3916_REG_IO_CONF2, - ST25R3916_REG_IO_CONF2_sup3V, - ST25R3916_REG_IO_CONF2_sup3V)) { - result = MIN( - result, - (uint8_t)(result - 5U)); /* In 3.3V mode [0,4] are not used */ - *result_mV = 2400U; /* Minimum regulated voltage 2.4V in case of 3.3V supply */ - } else { - *result_mV = 3600U; /* Minimum regulated voltage 3.6V in case of 5V supply */ - } - - *result_mV += - (uint16_t)result * 100U; /* 100mV steps in both 3.3V and 5V supply */ - } - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode st25r3916MeasureAmplitude(uint8_t* result) { - return st25r3916ExecuteCommandAndGetResult( - ST25R3916_CMD_MEASURE_AMPLITUDE, - ST25R3916_REG_AD_RESULT, - ST25R3916_TOUT_MEASURE_AMPLITUDE, - result); -} - -/*******************************************************************************/ -ReturnCode st25r3916MeasurePhase(uint8_t* result) { - return st25r3916ExecuteCommandAndGetResult( - ST25R3916_CMD_MEASURE_PHASE, ST25R3916_REG_AD_RESULT, ST25R3916_TOUT_MEASURE_PHASE, result); -} - -/*******************************************************************************/ -ReturnCode st25r3916MeasureCapacitance(uint8_t* result) { - return st25r3916ExecuteCommandAndGetResult( - ST25R3916_CMD_MEASURE_CAPACITANCE, - ST25R3916_REG_AD_RESULT, - ST25R3916_TOUT_MEASURE_CAPACITANCE, - result); -} - -/*******************************************************************************/ -ReturnCode st25r3916CalibrateCapacitiveSensor(uint8_t* result) { - ReturnCode ret; - uint8_t res; - - /* Clear Manual calibration values to enable automatic calibration mode */ - st25r3916ClrRegisterBits( - ST25R3916_REG_CAP_SENSOR_CONTROL, ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal_mask); - - /* Execute automatic calibration */ - ret = st25r3916ExecuteCommandAndGetResult( - ST25R3916_CMD_CALIBRATE_C_SENSOR, - ST25R3916_REG_CAP_SENSOR_RESULT, - ST25R3916_TOUT_CALIBRATE_CAP_SENSOR, - &res); - - /* Check wether the calibration was successull */ - if(((res & ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_end) != - ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_end) || - ((res & ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_err) == - ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_err) || - (ret != ERR_NONE)) { - return ERR_IO; - } - - if(result != NULL) { - (*result) = (uint8_t)(res >> ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_shift); - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode st25r3916SetBitrate(uint8_t txrate, uint8_t rxrate) { - uint8_t reg; - - st25r3916ReadRegister(ST25R3916_REG_BIT_RATE, ®); - if(rxrate != ST25R3916_BR_DO_NOT_SET) { - if(rxrate > ST25R3916_BR_848) { - return ERR_PARAM; - } - - reg = (uint8_t)(reg & ~ST25R3916_REG_BIT_RATE_rxrate_mask); /* MISRA 10.3 */ - reg |= rxrate << ST25R3916_REG_BIT_RATE_rxrate_shift; - } - if(txrate != ST25R3916_BR_DO_NOT_SET) { - if(txrate > ST25R3916_BR_6780) { - return ERR_PARAM; - } - - reg = (uint8_t)(reg & ~ST25R3916_REG_BIT_RATE_txrate_mask); /* MISRA 10.3 */ - reg |= txrate << ST25R3916_REG_BIT_RATE_txrate_shift; - } - return st25r3916WriteRegister(ST25R3916_REG_BIT_RATE, reg); -} - -/*******************************************************************************/ -ReturnCode st25r3916PerformCollisionAvoidance( - uint8_t FieldONCmd, - uint8_t pdThreshold, - uint8_t caThreshold, - uint8_t nTRFW) { - uint8_t treMask; - uint32_t irqs; - ReturnCode err; - - if((FieldONCmd != ST25R3916_CMD_INITIAL_RF_COLLISION) && - (FieldONCmd != ST25R3916_CMD_RESPONSE_RF_COLLISION_N)) { - return ERR_PARAM; - } - - err = ERR_INTERNAL; - - /* Check if new thresholds are to be applied */ - if((pdThreshold != ST25R3916_THRESHOLD_DO_NOT_SET) || - (caThreshold != ST25R3916_THRESHOLD_DO_NOT_SET)) { - treMask = 0; - - if(pdThreshold != ST25R3916_THRESHOLD_DO_NOT_SET) { - treMask |= ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_mask; - } - - if(caThreshold != ST25R3916_THRESHOLD_DO_NOT_SET) { - treMask |= ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_mask; - } - - /* Set Detection Threshold and|or Collision Avoidance Threshold */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_FIELD_THRESHOLD_ACTV, - treMask, - (pdThreshold & ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_mask) | - (caThreshold & ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_mask)); - } - - /* Set n x TRFW */ - st25r3916ChangeRegisterBits(ST25R3916_REG_AUX, ST25R3916_REG_AUX_nfc_n_mask, nTRFW); - - /*******************************************************************************/ - /* Enable and clear CA specific interrupts and execute command */ - st25r3916GetInterrupt( - (ST25R3916_IRQ_MASK_CAC | ST25R3916_IRQ_MASK_CAT | ST25R3916_IRQ_MASK_APON)); - st25r3916EnableInterrupts( - (ST25R3916_IRQ_MASK_CAC | ST25R3916_IRQ_MASK_CAT | ST25R3916_IRQ_MASK_APON)); - - st25r3916ExecuteCommand(FieldONCmd); - - /*******************************************************************************/ - /* Wait for initial APON interrupt, indicating anticollision avoidance done and ST25R3916's - * field is now on, or a CAC indicating a collision */ - irqs = st25r3916WaitForInterruptsTimed( - (ST25R3916_IRQ_MASK_CAC | ST25R3916_IRQ_MASK_APON), ST25R3916_TOUT_CA); - - if((ST25R3916_IRQ_MASK_CAC & irqs) != 0U) /* Collision occurred */ - { - err = ERR_RF_COLLISION; - } else if((ST25R3916_IRQ_MASK_APON & irqs) != 0U) { - /* After APON wait for CAT interrupt, indication field was switched on minimum guard time has been fulfilled */ - irqs = st25r3916WaitForInterruptsTimed((ST25R3916_IRQ_MASK_CAT), ST25R3916_TOUT_CA); - - if((ST25R3916_IRQ_MASK_CAT & irqs) != 0U) /* No Collision detected, Field On */ - { - err = ERR_NONE; - } - } else { - /* MISRA 15.7 - Empty else */ - } - - /* Clear any previous External Field events and disable CA specific interrupts */ - st25r3916GetInterrupt((ST25R3916_IRQ_MASK_EOF | ST25R3916_IRQ_MASK_EON)); - st25r3916DisableInterrupts( - (ST25R3916_IRQ_MASK_CAC | ST25R3916_IRQ_MASK_CAT | ST25R3916_IRQ_MASK_APON)); - - return err; -} - -/*******************************************************************************/ -void st25r3916SetNumTxBits(uint16_t nBits) { - st25r3916WriteRegister(ST25R3916_REG_NUM_TX_BYTES2, (uint8_t)((nBits >> 0) & 0xFFU)); - st25r3916WriteRegister(ST25R3916_REG_NUM_TX_BYTES1, (uint8_t)((nBits >> 8) & 0xFFU)); -} - -/*******************************************************************************/ -uint16_t st25r3916GetNumFIFOBytes(void) { - uint8_t reg; - uint16_t result; - - st25r3916ReadRegister(ST25R3916_REG_FIFO_STATUS2, ®); - reg = - ((reg & ST25R3916_REG_FIFO_STATUS2_fifo_b_mask) >> - ST25R3916_REG_FIFO_STATUS2_fifo_b_shift); - result = ((uint16_t)reg << 8); - - st25r3916ReadRegister(ST25R3916_REG_FIFO_STATUS1, ®); - result |= (((uint16_t)reg) & 0x00FFU); - - return result; -} - -/*******************************************************************************/ -uint8_t st25r3916GetNumFIFOLastBits(void) { - uint8_t reg; - - st25r3916ReadRegister(ST25R3916_REG_FIFO_STATUS2, ®); - - return ( - (reg & ST25R3916_REG_FIFO_STATUS2_fifo_lb_mask) >> - ST25R3916_REG_FIFO_STATUS2_fifo_lb_shift); -} - -/*******************************************************************************/ -uint32_t st25r3916GetNoResponseTime(void) { - return gST25R3916NRT_64fcs; -} - -/*******************************************************************************/ -ReturnCode st25r3916SetNoResponseTime(uint32_t nrt_64fcs) { - ReturnCode err; - uint8_t nrt_step; - uint32_t tmpNRT; - - tmpNRT = nrt_64fcs; /* MISRA 17.8 */ - err = ERR_NONE; - - gST25R3916NRT_64fcs = tmpNRT; /* Store given NRT value in 64/fc into local var */ - nrt_step = - ST25R3916_REG_TIMER_EMV_CONTROL_nrt_step_64fc; /* Set default NRT in steps of 64/fc */ - - if(tmpNRT > ST25R3916_NRT_MAX) /* Check if the given NRT value fits using 64/fc steps */ - { - nrt_step = - ST25R3916_REG_TIMER_EMV_CONTROL_nrt_step_4096_fc; /* If not, change NRT set to 4096/fc */ - tmpNRT = ((tmpNRT + 63U) / 64U); /* Calculate number of steps in 4096/fc */ - - if(tmpNRT > ST25R3916_NRT_MAX) /* Check if the NRT value fits using 64/fc steps */ - { - tmpNRT = ST25R3916_NRT_MAX; /* Assign the maximum possible */ - err = ERR_PARAM; /* Signal parameter error */ - } - gST25R3916NRT_64fcs = (64U * tmpNRT); - } - - /* Set the ST25R3916 NRT step units and the value */ - st25r3916ChangeRegisterBits( - ST25R3916_REG_TIMER_EMV_CONTROL, ST25R3916_REG_TIMER_EMV_CONTROL_nrt_step, nrt_step); - st25r3916WriteRegister(ST25R3916_REG_NO_RESPONSE_TIMER1, (uint8_t)(tmpNRT >> 8U)); - st25r3916WriteRegister(ST25R3916_REG_NO_RESPONSE_TIMER2, (uint8_t)(tmpNRT & 0xFFU)); - - return err; -} - -/*******************************************************************************/ -ReturnCode st25r3916SetStartNoResponseTimer(uint32_t nrt_64fcs) { - ReturnCode err; - - err = st25r3916SetNoResponseTime(nrt_64fcs); - if(err == ERR_NONE) { - st25r3916ExecuteCommand(ST25R3916_CMD_START_NO_RESPONSE_TIMER); - } - - return err; -} - -/*******************************************************************************/ -void st25r3916SetGPTime(uint16_t gpt_8fcs) { - st25r3916WriteRegister(ST25R3916_REG_GPT1, (uint8_t)(gpt_8fcs >> 8)); - st25r3916WriteRegister(ST25R3916_REG_GPT2, (uint8_t)(gpt_8fcs & 0xFFU)); -} - -/*******************************************************************************/ -ReturnCode st25r3916SetStartGPTimer(uint16_t gpt_8fcs, uint8_t trigger_source) { - st25r3916SetGPTime(gpt_8fcs); - st25r3916ChangeRegisterBits( - ST25R3916_REG_TIMER_EMV_CONTROL, - ST25R3916_REG_TIMER_EMV_CONTROL_gptc_mask, - trigger_source); - - /* If there's no trigger source, start GPT immediately */ - if(trigger_source == ST25R3916_REG_TIMER_EMV_CONTROL_gptc_no_trigger) { - st25r3916ExecuteCommand(ST25R3916_CMD_START_GP_TIMER); - } - - return ERR_NONE; -} - -/*******************************************************************************/ -bool st25r3916CheckChipID(uint8_t* rev) { - uint8_t ID; - - ID = 0; - st25r3916ReadRegister(ST25R3916_REG_IC_IDENTITY, &ID); - - /* Check if IC Identity Register contains ST25R3916's IC type code */ - if((ID & ST25R3916_REG_IC_IDENTITY_ic_type_mask) != - ST25R3916_REG_IC_IDENTITY_ic_type_st25r3916) { - return false; - } - - if(rev != NULL) { - *rev = (ID & ST25R3916_REG_IC_IDENTITY_ic_rev_mask); - } - - return true; -} - -/*******************************************************************************/ -ReturnCode st25r3916GetRegsDump(t_st25r3916Regs* regDump) { - uint8_t regIt; - - if(regDump == NULL) { - return ERR_PARAM; - } - - /* Dump Registers on space A */ - for(regIt = ST25R3916_REG_IO_CONF1; regIt <= ST25R3916_REG_IC_IDENTITY; regIt++) { - st25r3916ReadRegister(regIt, ®Dump->RsA[regIt]); - } - - regIt = 0; - - /* Read non-consecutive Registers on space B */ - st25r3916ReadRegister(ST25R3916_REG_EMD_SUP_CONF, ®Dump->RsB[regIt++]); - st25r3916ReadRegister(ST25R3916_REG_SUBC_START_TIME, ®Dump->RsB[regIt++]); - st25r3916ReadRegister(ST25R3916_REG_P2P_RX_CONF, ®Dump->RsB[regIt++]); - st25r3916ReadRegister(ST25R3916_REG_CORR_CONF1, ®Dump->RsB[regIt++]); - st25r3916ReadRegister(ST25R3916_REG_CORR_CONF2, ®Dump->RsB[regIt++]); - st25r3916ReadRegister(ST25R3916_REG_SQUELCH_TIMER, ®Dump->RsB[regIt++]); - st25r3916ReadRegister(ST25R3916_REG_FIELD_ON_GT, ®Dump->RsB[regIt++]); - st25r3916ReadRegister(ST25R3916_REG_AUX_MOD, ®Dump->RsB[regIt++]); - st25r3916ReadRegister(ST25R3916_REG_TX_DRIVER_TIMING, ®Dump->RsB[regIt++]); - st25r3916ReadRegister(ST25R3916_REG_RES_AM_MOD, ®Dump->RsB[regIt++]); - st25r3916ReadRegister(ST25R3916_REG_TX_DRIVER_STATUS, ®Dump->RsB[regIt++]); - st25r3916ReadRegister(ST25R3916_REG_REGULATOR_RESULT, ®Dump->RsB[regIt++]); - st25r3916ReadRegister(ST25R3916_REG_OVERSHOOT_CONF1, ®Dump->RsB[regIt++]); - st25r3916ReadRegister(ST25R3916_REG_OVERSHOOT_CONF2, ®Dump->RsB[regIt++]); - st25r3916ReadRegister(ST25R3916_REG_UNDERSHOOT_CONF1, ®Dump->RsB[regIt++]); - st25r3916ReadRegister(ST25R3916_REG_UNDERSHOOT_CONF2, ®Dump->RsB[regIt++]); - - return ERR_NONE; -} - -/*******************************************************************************/ -bool st25r3916IsCmdValid(uint8_t cmd) { - if(!((cmd >= ST25R3916_CMD_SET_DEFAULT) && (cmd <= ST25R3916_CMD_RESPONSE_RF_COLLISION_N)) && - !((cmd >= ST25R3916_CMD_GOTO_SENSE) && (cmd <= ST25R3916_CMD_GOTO_SLEEP)) && - !((cmd >= ST25R3916_CMD_MASK_RECEIVE_DATA) && (cmd <= ST25R3916_CMD_MEASURE_AMPLITUDE)) && - !((cmd >= ST25R3916_CMD_RESET_RXGAIN) && (cmd <= ST25R3916_CMD_ADJUST_REGULATORS)) && - !((cmd >= ST25R3916_CMD_CALIBRATE_DRIVER_TIMING) && - (cmd <= ST25R3916_CMD_START_PPON2_TIMER)) && - (cmd != ST25R3916_CMD_SPACE_B_ACCESS) && (cmd != ST25R3916_CMD_STOP_NRT)) { - return false; - } - return true; -} - -/*******************************************************************************/ -ReturnCode st25r3916StreamConfigure(const struct st25r3916StreamConfig* config) { - uint8_t smd; - uint8_t mode; - - smd = 0; - - if(config->useBPSK != 0U) { - mode = ST25R3916_REG_MODE_om_bpsk_stream; - if((config->din < 2U) || (config->din > 4U)) /* not in fc/4 .. fc/16 */ - { - return ERR_PARAM; - } - smd |= ((4U - config->din) << ST25R3916_REG_STREAM_MODE_scf_shift); - } else { - mode = ST25R3916_REG_MODE_om_subcarrier_stream; - if((config->din < 3U) || (config->din > 6U)) /* not in fc/8 .. fc/64 */ - { - return ERR_PARAM; - } - smd |= ((6U - config->din) << ST25R3916_REG_STREAM_MODE_scf_shift); - if(config->report_period_length == 0U) { - return ERR_PARAM; - } - } - - if((config->dout < 1U) || (config->dout > 7U)) /* not in fc/2 .. fc/128 */ - { - return ERR_PARAM; - } - smd |= (7U - config->dout) << ST25R3916_REG_STREAM_MODE_stx_shift; - - if(config->report_period_length > 3U) { - return ERR_PARAM; - } - smd |= (config->report_period_length << ST25R3916_REG_STREAM_MODE_scp_shift); - - st25r3916WriteRegister(ST25R3916_REG_STREAM_MODE, smd); - st25r3916ChangeRegisterBits(ST25R3916_REG_MODE, ST25R3916_REG_MODE_om_mask, mode); - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode st25r3916GetRSSI(uint16_t* amRssi, uint16_t* pmRssi) { - /*******************************************************************************/ - /* MISRA 8.9 An object should be defined at block scope if its identifier only appears in a single function */ - /*< ST25R3916 RSSI Display Reg values: 0 1 2 3 4 5 6 7 8 9 a b c d e f */ - static const uint16_t st25r3916Rssi2mV[] = { - 0, 20, 27, 37, 52, 72, 99, 136, 190, 262, 357, 500, 686, 950, 1150, 1150}; - - /* ST25R3916 2/3 stage gain reduction [dB] 0 0 0 0 0 3 6 9 12 15 18 na na na na na */ - static const uint16_t st25r3916Gain2Percent[] = { - 100, 100, 100, 100, 100, 141, 200, 281, 398, 562, 794, 1, 1, 1, 1, 1}; - /*******************************************************************************/ - - uint8_t rssi; - uint8_t gainRed; - - st25r3916ReadRegister(ST25R3916_REG_RSSI_RESULT, &rssi); - st25r3916ReadRegister(ST25R3916_REG_GAIN_RED_STATE, &gainRed); - - if(amRssi != NULL) { - *amRssi = - (uint16_t)(((uint32_t)st25r3916Rssi2mV[(rssi >> ST25R3916_REG_RSSI_RESULT_rssi_am_shift)] * (uint32_t)st25r3916Gain2Percent[(gainRed >> ST25R3916_REG_GAIN_RED_STATE_gs_am_shift)]) / 100U); - } - - if(pmRssi != NULL) { - *pmRssi = - (uint16_t)(((uint32_t)st25r3916Rssi2mV[(rssi & ST25R3916_REG_RSSI_RESULT_rssi_pm_mask)] * (uint32_t)st25r3916Gain2Percent[(gainRed & ST25R3916_REG_GAIN_RED_STATE_gs_pm_mask)]) / 100U); - } - - return ERR_NONE; -} diff --git a/lib/ST25RFAL002/source/st25r3916/st25r3916.h b/lib/ST25RFAL002/source/st25r3916/st25r3916.h deleted file mode 100644 index d546d79e31a..00000000000 --- a/lib/ST25RFAL002/source/st25r3916/st25r3916.h +++ /dev/null @@ -1,669 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R3916 firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file - * - * \author Gustavo Patricio - * - * \brief ST25R3916 high level interface - * - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-HAL - * \brief RFAL Hardware Abstraction Layer - * @{ - * - * \addtogroup ST25R3916 - * \brief RFAL ST25R3916 Driver - * @{ - * - * \addtogroup ST25R3916_Driver - * \brief RFAL ST25R3916 Driver - * @{ - * - */ - -#ifndef ST25R3916_H -#define ST25R3916_H - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ -#include "platform.h" -#include "st_errno.h" -#include "st25r3916_com.h" - -/* -****************************************************************************** -* GLOBAL DATATYPES -****************************************************************************** -*/ - -/*! Struct to represent all regs on ST25R3916 */ -typedef struct { - uint8_t RsA[( - ST25R3916_REG_IC_IDENTITY + 1U)]; /*!< Registers contained on ST25R3916 space A (Rs-A) */ - uint8_t - RsB[ST25R3916_SPACE_B_REG_LEN]; /*!< Registers contained on ST25R3916 space B (Rs-B) */ -} t_st25r3916Regs; - -/*! Parameters how the stream mode should work */ -struct st25r3916StreamConfig { - uint8_t useBPSK; /*!< 0: subcarrier, 1:BPSK */ - uint8_t din; /*!< Divider for the in subcarrier frequency: fc/2^din */ - uint8_t dout; /*!< Divider for the in subcarrier frequency fc/2^dout */ - uint8_t report_period_length; /*!< Length of the reporting period 2^report_period_length*/ -}; - -/* -****************************************************************************** -* GLOBAL DEFINES -****************************************************************************** -*/ - -/* ST25R3916 direct commands */ -#define ST25R3916_CMD_SET_DEFAULT \ - 0xC1U /*!< Puts the chip in default state (same as after power-up) */ -#define ST25R3916_CMD_STOP 0xC2U /*!< Stops all activities and clears FIFO */ -#define ST25R3916_CMD_TRANSMIT_WITH_CRC \ - 0xC4U /*!< Transmit with CRC */ -#define ST25R3916_CMD_TRANSMIT_WITHOUT_CRC \ - 0xC5U /*!< Transmit without CRC */ -#define ST25R3916_CMD_TRANSMIT_REQA \ - 0xC6U /*!< Transmit REQA */ -#define ST25R3916_CMD_TRANSMIT_WUPA \ - 0xC7U /*!< Transmit WUPA */ -#define ST25R3916_CMD_INITIAL_RF_COLLISION \ - 0xC8U /*!< NFC transmit with Initial RF Collision Avoidance */ -#define ST25R3916_CMD_RESPONSE_RF_COLLISION_N \ - 0xC9U /*!< NFC transmit with Response RF Collision Avoidance */ -#define ST25R3916_CMD_GOTO_SENSE \ - 0xCDU /*!< Passive target logic to Sense/Idle state */ -#define ST25R3916_CMD_GOTO_SLEEP \ - 0xCEU /*!< Passive target logic to Sleep/Halt state */ -#define ST25R3916_CMD_MASK_RECEIVE_DATA \ - 0xD0U /*!< Mask receive data */ -#define ST25R3916_CMD_UNMASK_RECEIVE_DATA \ - 0xD1U /*!< Unmask receive data */ -#define ST25R3916_CMD_AM_MOD_STATE_CHANGE \ - 0xD2U /*!< AM Modulation state change */ -#define ST25R3916_CMD_MEASURE_AMPLITUDE \ - 0xD3U /*!< Measure singal amplitude on RFI inputs */ -#define ST25R3916_CMD_RESET_RXGAIN \ - 0xD5U /*!< Reset RX Gain */ -#define ST25R3916_CMD_ADJUST_REGULATORS \ - 0xD6U /*!< Adjust regulators */ -#define ST25R3916_CMD_CALIBRATE_DRIVER_TIMING \ - 0xD8U /*!< Starts the sequence to adjust the driver timing */ -#define ST25R3916_CMD_MEASURE_PHASE \ - 0xD9U /*!< Measure phase between RFO and RFI signal */ -#define ST25R3916_CMD_CLEAR_RSSI \ - 0xDAU /*!< Clear RSSI bits and restart the measurement */ -#define ST25R3916_CMD_CLEAR_FIFO \ - 0xDBU /*!< Clears FIFO, Collision and IRQ status */ -#define ST25R3916_CMD_TRANSPARENT_MODE \ - 0xDCU /*!< Transparent mode */ -#define ST25R3916_CMD_CALIBRATE_C_SENSOR \ - 0xDDU /*!< Calibrate the capacitive sensor */ -#define ST25R3916_CMD_MEASURE_CAPACITANCE \ - 0xDEU /*!< Measure capacitance */ -#define ST25R3916_CMD_MEASURE_VDD \ - 0xDFU /*!< Measure power supply voltage */ -#define ST25R3916_CMD_START_GP_TIMER \ - 0xE0U /*!< Start the general purpose timer */ -#define ST25R3916_CMD_START_WUP_TIMER \ - 0xE1U /*!< Start the wake-up timer */ -#define ST25R3916_CMD_START_MASK_RECEIVE_TIMER \ - 0xE2U /*!< Start the mask-receive timer */ -#define ST25R3916_CMD_START_NO_RESPONSE_TIMER \ - 0xE3U /*!< Start the no-response timer */ -#define ST25R3916_CMD_START_PPON2_TIMER \ - 0xE4U /*!< Start PPon2 timer */ -#define ST25R3916_CMD_STOP_NRT \ - 0xE8U /*!< Stop No Response Timer */ -#define ST25R3916_CMD_SPACE_B_ACCESS \ - 0xFBU /*!< Enable R/W access to the test registers */ -#define ST25R3916_CMD_TEST_ACCESS \ - 0xFCU /*!< Enable R/W access to the test registers */ - -#define ST25R3916_THRESHOLD_DO_NOT_SET \ - 0xFFU /*!< Indicates not to change this Threshold */ - -#define ST25R3916_BR_DO_NOT_SET \ - 0xFFU /*!< Indicates not to change this Bit Rate */ -#define ST25R3916_BR_106 0x00U /*!< ST25R3916 Bit Rate 106 kbit/s (fc/128) */ -#define ST25R3916_BR_212 0x01U /*!< ST25R3916 Bit Rate 212 kbit/s (fc/64) */ -#define ST25R3916_BR_424 0x02U /*!< ST25R3916 Bit Rate 424 kbit/s (fc/32) */ -#define ST25R3916_BR_848 0x03U /*!< ST25R3916 Bit Rate 848 kbit/s (fc/16) */ -#define ST25R3916_BR_1695 0x04U /*!< ST25R3916 Bit Rate 1696 kbit/s (fc/8) */ -#define ST25R3916_BR_3390 0x05U /*!< ST25R3916 Bit Rate 3390 kbit/s (fc/4) */ -#define ST25R3916_BR_6780 0x07U /*!< ST25R3916 Bit Rate 6780 kbit/s (fc/2) */ - -#define ST25R3916_FIFO_DEPTH 512U /*!< Depth of FIFO */ -#define ST25R3916_TOUT_OSC_STABLE \ - 10U /*!< Max timeout for Oscillator to get stable DS: 700us */ - -/* -****************************************************************************** -* GLOBAL MACROS -****************************************************************************** -*/ - -/*! Enables the Transmitter (Field On) and Receiver */ -#define st25r3916TxRxOn() \ - st25r3916SetRegisterBits( \ - ST25R3916_REG_OP_CONTROL, \ - (ST25R3916_REG_OP_CONTROL_rx_en | ST25R3916_REG_OP_CONTROL_tx_en)) - -/*! Disables the Transmitter (Field Off) and Receiver */ -#define st25r3916TxRxOff() \ - st25r3916ClrRegisterBits( \ - ST25R3916_REG_OP_CONTROL, \ - (ST25R3916_REG_OP_CONTROL_rx_en | ST25R3916_REG_OP_CONTROL_tx_en)) - -/*! Disables the Transmitter (Field Off) */ -#define st25r3916TxOff() \ - st25r3916ClrRegisterBits(ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_tx_en) - -/*! Checks if General Purpose Timer is still running by reading gpt_on flag */ -#define st25r3916IsGPTRunning() \ - st25r3916CheckReg( \ - ST25R3916_REG_NFCIP1_BIT_RATE, \ - ST25R3916_REG_NFCIP1_BIT_RATE_gpt_on, \ - ST25R3916_REG_NFCIP1_BIT_RATE_gpt_on) - -/*! Checks if External Filed is detected by reading ST25R3916 External Field Detector output */ -#define st25r3916IsExtFieldOn() \ - st25r3916CheckReg( \ - ST25R3916_REG_AUX_DISPLAY, \ - ST25R3916_REG_AUX_DISPLAY_efd_o, \ - ST25R3916_REG_AUX_DISPLAY_efd_o) - -/*! Checks if Transmitter is enabled (Field On) */ -#define st25r3916IsTxEnabled() \ - st25r3916CheckReg( \ - ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_tx_en, ST25R3916_REG_OP_CONTROL_tx_en) - -/*! Checks if NRT is in EMV mode */ -#define st25r3916IsNRTinEMV() \ - st25r3916CheckReg( \ - ST25R3916_REG_TIMER_EMV_CONTROL, \ - ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv, \ - ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv_on) - -/*! Checks if last FIFO byte is complete */ -#define st25r3916IsLastFIFOComplete() \ - st25r3916CheckReg(ST25R3916_REG_FIFO_STATUS2, ST25R3916_REG_FIFO_STATUS2_fifo_lb_mask, 0) - -/*! Checks if the Oscillator is enabled */ -#define st25r3916IsOscOn() \ - st25r3916CheckReg( \ - ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_en, ST25R3916_REG_OP_CONTROL_en) - -/* -****************************************************************************** -* GLOBAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -/*! - ***************************************************************************** - * \brief Initialise ST25R3916 driver - * - * This function initialises the ST25R3916 driver. - * - * \return ERR_NONE : Operation successful - * \return ERR_HW_MISMATCH : Expected HW do not match or communication error - * \return ERR_IO : Error during communication selftest. Check communication interface - * \return ERR_TIMEOUT : Timeout during IRQ selftest. Check IRQ handling - * \return ERR_SYSTEM : Failure during oscillator activation or timer error - * - ***************************************************************************** - */ -ReturnCode st25r3916Initialize(void); - -/*! - ***************************************************************************** - * \brief Deinitialize ST25R3916 driver - * - * Calling this function deinitializes the ST25R3916 driver. - * - ***************************************************************************** - */ -void st25r3916Deinitialize(void); - -/*! - ***************************************************************************** - * \brief Turn on Oscillator and Regulator - * - * This function turn on oscillator and regulator and waits for the - * oscillator to become stable - * - * \return ERR_SYSTEM : Failure dusring Oscillator activation - * \return ERR_NONE : No error, Oscillator is active and stable, Regulator is on - * - ***************************************************************************** - */ -ReturnCode st25r3916OscOn(void); - -/*! - ***************************************************************************** - * \brief Sets the bitrate - * - * This function sets the bitrates for rx and tx - * - * \param txrate : speed is 2^txrate * 106 kb/s - * 0xff : don't set txrate (ST25R3916_BR_DO_NOT_SET) - * \param rxrate : speed is 2^rxrate * 106 kb/s - * 0xff : don't set rxrate (ST25R3916_BR_DO_NOT_SET) - * - * \return ERR_PARAM: At least one bit rate was invalid - * \return ERR_NONE : No error, both bit rates were set - * - ***************************************************************************** - */ -ReturnCode st25r3916SetBitrate(uint8_t txrate, uint8_t rxrate); - -/*! - ***************************************************************************** - * \brief Adjusts supply regulators according to the current supply voltage - * - * This function the power level is measured in maximum load conditions and - * the regulated voltage reference is set to 250mV below this level. - * Execution of this function lasts arround 5ms. - * - * The regulated voltages will be set to the result of Adjust Regulators - * - * \param [out] result_mV : Result of calibration in milliVolts - * - * \return ERR_IO : Error during communication with ST25R3916 - * \return ERR_NONE : No error - * - ***************************************************************************** - */ -ReturnCode st25r3916AdjustRegulators(uint16_t* result_mV); - -/*! - ***************************************************************************** - * \brief Measure Amplitude - * - * This function measured the amplitude on the RFI inputs and stores the - * result in parameter \a result. - * - * \param[out] result: result of RF measurement. - * - * \return ERR_PARAM : Invalid parameter - * \return ERR_NONE : No error - * - ***************************************************************************** - */ -ReturnCode st25r3916MeasureAmplitude(uint8_t* result); - -/*! - ***************************************************************************** - * \brief Measure Power Supply - * - * This function executes Measure Power Supply and returns the raw value - * - * \param[in] mpsv : one of ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd - * ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_rf - * ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_a - * ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_d - * ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_am - * - * \return the measured voltage in raw format. - * - ***************************************************************************** - */ -uint8_t st25r3916MeasurePowerSupply(uint8_t mpsv); - -/*! - ***************************************************************************** - * \brief Measure Voltage - * - * This function measures the voltage on one of VDD and VDD_* and returns - * the result in mV - * - * \param[in] mpsv : one of ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd - * ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_rf - * ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_a - * ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_d - * or ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_am - * - * \return the measured voltage in mV - * - ***************************************************************************** - */ -uint16_t st25r3916MeasureVoltage(uint8_t mpsv); - -/*! - ***************************************************************************** - * \brief Measure Phase - * - * This function performs a Phase measurement. - * The result is stored in the \a result parameter. - * - * \param[out] result: 8 bit long result of the measurement. - * - * \return ERR_PARAM : Invalid parameter - * \return ERR_NONE : No error - * - ***************************************************************************** - */ -ReturnCode st25r3916MeasurePhase(uint8_t* result); - -/*! - ***************************************************************************** - * \brief Measure Capacitance - * - * This function performs the capacitance measurement and stores the - * result in parameter \a result. - * - * \param[out] result: 8 bit long result of RF measurement. - * - * \return ERR_PARAM : Invalid parameter - * \return ERR_NONE : No error - * - ***************************************************************************** - */ -ReturnCode st25r3916MeasureCapacitance(uint8_t* result); - -/*! - ***************************************************************************** - * \brief Calibrates Capacitive Sensor - * - * This function performs automatic calibration of the capacitive sensor - * and stores the result in parameter \a result. - * - * \warning To avoid interference with Xtal oscillator and reader magnetic - * field, it is strongly recommended to perform calibration - * in Power-down mode only. - * This method does not modify the Oscillator nor transmitter state, - * these should be configured before by user. - * - * \param[out] result: 5 bit long result of the calibration. - * Binary weighted, step 0.1 pF, max 3.1 pF - * - * \return ERR_PARAM : Invalid parameter - * \return ERR_IO : The calibration was not successful - * \return ERR_NONE : No error - * - ***************************************************************************** - */ -ReturnCode st25r3916CalibrateCapacitiveSensor(uint8_t* result); - -/*! - ***************************************************************************** - * \brief Get NRT time - * - * This returns the last value set on the NRT - * - * \warning it does not read chip register, just the sw var that contains the - * last value set before - * - * \return the value of the NRT in 64/fc - */ -uint32_t st25r3916GetNoResponseTime(void); - -/*! - ***************************************************************************** - * \brief Set NRT time - * - * This function sets the No Response Time with the given value - * - * \param [in] nrt_64fcs : no response time in steps of 64/fc (4.72us) - * - * \return ERR_PARAM : Invalid parameter (time is too large) - * \return ERR_NONE : No error - * - ***************************************************************************** - */ -ReturnCode st25r3916SetNoResponseTime(uint32_t nrt_64fcs); - -/*! - ***************************************************************************** - * \brief Set and Start NRT - * - * This function sets the No Response Time with the given value and - * immediately starts it - * Used when needs to add more time before timeout without performing Tx - * - * \param [in] nrt_64fcs : no response time in steps of 64/fc (4.72us) - * - * \return ERR_PARAM : Invalid parameter - * \return ERR_NONE : No error - * - ***************************************************************************** - */ -ReturnCode st25r3916SetStartNoResponseTimer(uint32_t nrt_64fcs); - -/*! - ***************************************************************************** - * \brief Set GPT time - * - * This function sets the General Purpose Timer time registers - * - * \param [in] gpt_8fcs : general purpose timer timeout in steps of 8/fc (590ns) - * - ***************************************************************************** - */ -void st25r3916SetGPTime(uint16_t gpt_8fcs); - -/*! - ***************************************************************************** - * \brief Set and Start GPT - * - * This function sets the General Purpose Timer with the given timeout and - * immediately starts it ONLY if the trigger source is not set to none. - * - * \param [in] gpt_8fcs : general purpose timer timeout in steps of8/fc (590ns) - * \param [in] trigger_source : no trigger, start of Rx, end of Rx, end of Tx in NFC mode - * - * \return ERR_PARAM : Invalid parameter - * \return ERR_NONE : No error - * - ***************************************************************************** - */ -ReturnCode st25r3916SetStartGPTimer(uint16_t gpt_8fcs, uint8_t trigger_source); - -/*! - ***************************************************************************** - * \brief Sets the number Tx Bits - * - * Sets ST25R3916 internal registers with correct number of complete bytes and - * bits to be sent - * - * \param [in] nBits : number of bits to be set/transmitted - * - ***************************************************************************** - */ -void st25r3916SetNumTxBits(uint16_t nBits); - -/*! - ***************************************************************************** - * \brief Get amount of bytes in FIFO - * - * Gets the number of bytes currently in the FIFO - * - * \return the number of bytes currently in the FIFO - * - ***************************************************************************** - */ -uint16_t st25r3916GetNumFIFOBytes(void); - -/*! - ***************************************************************************** - * \brief Get amount of bits of the last FIFO byte if incomplete - * - * Gets the number of bits of the last FIFO byte if incomplete - * - * \return the number of bits of the last FIFO byte if incomplete, 0 if - * the last byte is complete - * - ***************************************************************************** - */ -uint8_t st25r3916GetNumFIFOLastBits(void); - -/*! - ***************************************************************************** - * \brief Perform Collision Avoidance - * - * Performs Collision Avoidance with the given threshold and with the - * n number of TRFW - * - * \param[in] FieldONCmd : Field ON command to be executed ST25R3916_CMD_INITIAL_RF_COLLISION - * or ST25R3916_CMD_RESPONSE_RF_COLLISION_N - * \param[in] pdThreshold : Peer Detection Threshold (ST25R3916_REG_FIELD_THRESHOLD_trg_xx) - * 0xff : don't set Threshold (ST25R3916_THRESHOLD_DO_NOT_SET) - * \param[in] caThreshold : Collision Avoidance Threshold (ST25R3916_REG_FIELD_THRESHOLD_rfe_xx) - * 0xff : don't set Threshold (ST25R3916_THRESHOLD_DO_NOT_SET) - * \param[in] nTRFW : Number of TRFW - * - * \return ERR_PARAM : Invalid parameter - * \return ERR_RF_COLLISION : Collision detected - * \return ERR_NONE : No collision detected - * - ***************************************************************************** - */ -ReturnCode st25r3916PerformCollisionAvoidance( - uint8_t FieldONCmd, - uint8_t pdThreshold, - uint8_t caThreshold, - uint8_t nTRFW); - -/*! - ***************************************************************************** - * \brief Check Identity - * - * Checks if the chip ID is as expected. - * - * 5 bit IC type code for ST25R3916: 00101 - * The 3 lsb contain the IC revision code - * - * \param[out] rev : the IC revision code - * - * \return true when IC type is as expected - * \return false otherwise - */ -bool st25r3916CheckChipID(uint8_t* rev); - -/*! - ***************************************************************************** - * \brief Retrieves all internal registers from ST25R3916 - * - * \param[out] regDump : pointer to the struct/buffer where the reg dump - * will be written - * - * \return ERR_PARAM : Invalid parameter - * \return ERR_NONE : No error - ***************************************************************************** - */ -ReturnCode st25r3916GetRegsDump(t_st25r3916Regs* regDump); - -/*! - ***************************************************************************** - * \brief Check if command is valid - * - * Checks if the given command is a valid ST25R3916 command - * - * \param[in] cmd: Command to check - * - * \return true if is a valid command - * \return false otherwise - * - ***************************************************************************** - */ -bool st25r3916IsCmdValid(uint8_t cmd); - -/*! - ***************************************************************************** - * \brief Configure the stream mode of ST25R3916 - * - * This function initializes the stream with the given parameters - * - * \param[in] config : all settings for bitrates, type, etc. - * - * \return ERR_PARAM : Invalid parameter - * \return ERR_NONE : No error, stream mode driver initialized - * - ***************************************************************************** - */ -ReturnCode st25r3916StreamConfigure(const struct st25r3916StreamConfig* config); - -/*! - ***************************************************************************** - * \brief Executes a direct command and returns the result - * - * This function executes the direct command given by \a cmd waits for - * \a sleeptime for I_dct and returns the result read from register \a resreg. - * The value of cmd is not checked. - * - * \param[in] cmd : direct command to execute - * \param[in] resReg: address of the register containing the result - * \param[in] tout : time in milliseconds to wait before reading the result - * \param[out] result: result - * - * \return ERR_NONE : No error - * - ***************************************************************************** - */ -ReturnCode - st25r3916ExecuteCommandAndGetResult(uint8_t cmd, uint8_t resReg, uint8_t tout, uint8_t* result); - -/*! - ***************************************************************************** - * \brief Gets the RSSI values - * - * This function gets the RSSI value of the previous reception taking into - * account the gain reductions that were used. - * RSSI value for both AM and PM channel can be retrieved. - * - * \param[out] amRssi: the RSSI on the AM channel expressed in mV - * \param[out] pmRssi: the RSSI on the PM channel expressed in mV - * - * \return ERR_PARAM : Invalid parameter - * \return ERR_NONE : No error - * - ***************************************************************************** - */ -ReturnCode st25r3916GetRSSI(uint16_t* amRssi, uint16_t* pmRssi); -#endif /* ST25R3916_H */ - -/** - * @} - * - * @} - * - * @} - * - * @} - */ diff --git a/lib/ST25RFAL002/source/st25r3916/st25r3916_aat.c b/lib/ST25RFAL002/source/st25r3916/st25r3916_aat.c deleted file mode 100644 index 234bb2e9954..00000000000 --- a/lib/ST25RFAL002/source/st25r3916/st25r3916_aat.c +++ /dev/null @@ -1,366 +0,0 @@ -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R3916 firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file st25r3916_aat.c - * - * \author - * - * \brief ST25R3916 Antenna Tuning - * - * The antenna tuning algorithm tries to find the optimal settings for - * the AAT_A and AAT_B registers, which are connected to variable capacitors - * to tune the antenna matching. - * - */ - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ -#include "st25r3916_aat.h" -#include "utils.h" -#include "st_errno.h" -#include "st25r3916.h" -#include "st25r3916_com.h" -#include "platform.h" -#include "rfal_chip.h" - -/* -****************************************************************************** -* GLOBAL DEFINES -****************************************************************************** -*/ -#define ST25R3916_AAT_CAP_DELAY_MAX 10 /*!< Max Variable Capacitor settle delay */ - -/* -****************************************************************************** -* GLOBAL MACROS -****************************************************************************** -*/ -#define st25r3916AatLog(...) /* platformLog(__VA_ARGS__) */ /*!< Logging macro */ - -/* -****************************************************************************** -* LOCAL FUNCTION PROTOTYPES -****************************************************************************** -*/ -static ReturnCode aatHillClimb( - const struct st25r3916AatTuneParams* tuningParams, - struct st25r3916AatTuneResult* tuningStatus); -static int32_t aatGreedyDescent( - uint32_t* f_min, - const struct st25r3916AatTuneParams* tuningParams, - struct st25r3916AatTuneResult* tuningStatus, - int32_t previousDir); -static int32_t aatSteepestDescent( - uint32_t* f_min, - const struct st25r3916AatTuneParams* tuningParams, - struct st25r3916AatTuneResult* tuningStatus, - int32_t previousDir, - int32_t previousDir2); - -static ReturnCode aatMeasure( - uint8_t serCap, - uint8_t parCap, - uint8_t* amplitude, - uint8_t* phase, - uint16_t* measureCnt); -static uint32_t - aatCalcF(const struct st25r3916AatTuneParams* tuningParams, uint8_t amplitude, uint8_t phase); -static ReturnCode aatStepDacVals( - const struct st25r3916AatTuneParams* tuningParams, - uint8_t* a, - uint8_t* b, - int32_t dir); - -/*******************************************************************************/ -ReturnCode st25r3916AatTune( - const struct st25r3916AatTuneParams* tuningParams, - struct st25r3916AatTuneResult* tuningStatus) { - ReturnCode err; - const struct st25r3916AatTuneParams* tp = tuningParams; - struct st25r3916AatTuneResult* ts = tuningStatus; - struct st25r3916AatTuneParams defaultTuningParams = { - .aat_a_min = 0, - .aat_a_max = 255, - .aat_a_start = 127, - .aat_a_stepWidth = 32, - .aat_b_min = 0, - .aat_b_max = 255, - .aat_b_start = 127, - .aat_b_stepWidth = 32, - - .phaTarget = 128, - .phaWeight = 2, - .ampTarget = 196, - .ampWeight = 1, - - .doDynamicSteps = true, - .measureLimit = 50, - }; - struct st25r3916AatTuneResult defaultTuneResult; - - if((NULL != tp) && ((tp->aat_a_min > tp->aat_a_max) || (tp->aat_a_start < tp->aat_a_min) || - (tp->aat_a_start > tp->aat_a_max) || (tp->aat_b_min > tp->aat_b_max) || - (tp->aat_b_start < tp->aat_b_min) || (tp->aat_b_start > tp->aat_b_max))) { - return ERR_PARAM; - } - - if(NULL == tp) { /* Start from current caps with default params */ - st25r3916ReadRegister(ST25R3916_REG_ANT_TUNE_A, &defaultTuningParams.aat_a_start); - st25r3916ReadRegister(ST25R3916_REG_ANT_TUNE_B, &defaultTuningParams.aat_b_start); - tp = &defaultTuningParams; - } - - if(NULL == ts) { - ts = &defaultTuneResult; - } - - ts->measureCnt = 0; /* Clear current measure count */ - - err = aatHillClimb(tp, ts); - - return err; -} - -/*******************************************************************************/ -static ReturnCode aatHillClimb( - const struct st25r3916AatTuneParams* tuningParams, - struct st25r3916AatTuneResult* tuningStatus) { - ReturnCode err = ERR_NONE; - uint32_t f_min; - int32_t direction, gdirection; - uint8_t amp, phs; - struct st25r3916AatTuneParams tp = *tuningParams; // local copy to obey const - - tuningStatus->aat_a = tuningParams->aat_a_start; - tuningStatus->aat_b = tuningParams->aat_b_start; - - /* Get a proper start value */ - aatMeasure(tuningStatus->aat_a, tuningStatus->aat_b, &, &phs, &tuningStatus->measureCnt); - f_min = aatCalcF(&tp, amp, phs); - direction = 0; - - st25r3916AatLog("%d %d: %d***\n", tuningStatus->aat_a, tuningStatus->aat_b, f_min); - - do { - direction = - 0; /* Initially and after reducing step sizes we don't have a previous direction */ - do { - /* With the greedy step below always executed aftwards the -direction does never need to be investigated */ - direction = aatSteepestDescent(&f_min, &tp, tuningStatus, direction, -direction); - if(tuningStatus->measureCnt > tp.measureLimit) { - err = ERR_OVERRUN; - break; - } - do { - gdirection = aatGreedyDescent(&f_min, &tp, tuningStatus, direction); - if(tuningStatus->measureCnt > tp.measureLimit) { - err = ERR_OVERRUN; - break; - } - } while(0 != gdirection); - } while(0 != direction); - tp.aat_a_stepWidth /= 2U; /* Reduce step sizes */ - tp.aat_b_stepWidth /= 2U; - } while(tp.doDynamicSteps && ((tp.aat_a_stepWidth > 0U) || (tp.aat_b_stepWidth > 0U))); - - return err; -} - -/*******************************************************************************/ -static int32_t aatSteepestDescent( - uint32_t* f_min, - const struct st25r3916AatTuneParams* tuningParams, - struct st25r3916AatTuneResult* tuningStatus, - int32_t previousDir, - int32_t previousDir2) { - int32_t i; - uint8_t amp, phs; - uint32_t f; - int32_t bestdir = - 0; /* Negative direction: decrease, Positive: increase. (-)1: aat_a, (-)2: aat_b */ - - for(i = -2; i <= 2; i++) { - uint8_t a = tuningStatus->aat_a, b = tuningStatus->aat_b; - - if((0 == i) || (i == -previousDir) || - (i == -previousDir2)) { /* Skip no direction and avoid going backwards */ - continue; - } - if(0U != aatStepDacVals( - tuningParams, - &a, - &b, - i)) { /* If stepping did not change the value, omit this direction */ - continue; - } - - aatMeasure(a, b, &, &phs, &tuningStatus->measureCnt); - f = aatCalcF(tuningParams, amp, phs); - st25r3916AatLog("%d : %d %d: %d", i, a, b, f); - if(f < *f_min) { /* Value is better than all previous ones */ - st25r3916AatLog("*"); - *f_min = f; - bestdir = i; - } - st25r3916AatLog("\n"); - } - if(0 != bestdir) { /* Walk into the best direction */ - aatStepDacVals(tuningParams, &tuningStatus->aat_a, &tuningStatus->aat_b, bestdir); - } - return bestdir; -} - -/*******************************************************************************/ -static int32_t aatGreedyDescent( - uint32_t* f_min, - const struct st25r3916AatTuneParams* tuningParams, - struct st25r3916AatTuneResult* tuningStatus, - int32_t previousDir) { - uint8_t amp, phs; - uint32_t f; - uint8_t a = tuningStatus->aat_a, b = tuningStatus->aat_b; - - if(0U != aatStepDacVals( - tuningParams, - &a, - &b, - previousDir)) { /* If stepping did not change the value, omit this direction */ - return 0; - } - - aatMeasure(a, b, &, &phs, &tuningStatus->measureCnt); - f = aatCalcF(tuningParams, amp, phs); - st25r3916AatLog("g : %d %d: %d", a, b, f); - if(f < *f_min) { /* Value is better than previous one */ - st25r3916AatLog("*\n"); - tuningStatus->aat_a = a; - tuningStatus->aat_b = b; - *f_min = f; - return previousDir; - } - - st25r3916AatLog("\n"); - return 0; -} - -/*******************************************************************************/ -static uint32_t - aatCalcF(const struct st25r3916AatTuneParams* tuningParams, uint8_t amplitude, uint8_t phase) { - /* f(amp, pha) = (ampWeight * |amp - ampTarget|) + (phaWeight * |pha - phaTarget|) */ - uint8_t ampTarget = tuningParams->ampTarget; - uint8_t phaTarget = tuningParams->phaTarget; - - uint32_t ampWeight = tuningParams->ampWeight; - uint32_t phaWeight = tuningParams->phaWeight; - - /* Temp variables to avoid MISRA R10.8 (cast on composite expression) */ - uint8_t ad = ((amplitude > ampTarget) ? (amplitude - ampTarget) : (ampTarget - amplitude)); - uint8_t pd = ((phase > phaTarget) ? (phase - phaTarget) : (phaTarget - phase)); - - uint32_t ampDelta = (uint32_t)ad; - uint32_t phaDelta = (uint32_t)pd; - - return ((ampWeight * ampDelta) + (phaWeight * phaDelta)); -} - -/*******************************************************************************/ -static ReturnCode aatStepDacVals( - const struct st25r3916AatTuneParams* tuningParams, - uint8_t* a, - uint8_t* b, - int32_t dir) { - int16_t aat_a = (int16_t)*a, aat_b = (int16_t)*b; - - switch(abs(dir)) { /* Advance by steps size in requested direction */ - case 1: - aat_a = (dir < 0) ? (aat_a - (int16_t)tuningParams->aat_a_stepWidth) : - (aat_a + (int16_t)tuningParams->aat_a_stepWidth); - if(aat_a < (int16_t)tuningParams->aat_a_min) { - aat_a = (int16_t)tuningParams->aat_a_min; - } - if(aat_a > (int16_t)tuningParams->aat_a_max) { - aat_a = (int16_t)tuningParams->aat_a_max; - } - if((int16_t)*a == aat_a) { - return ERR_PARAM; - } - break; - case 2: - aat_b = (dir < 0) ? (aat_b - (int16_t)tuningParams->aat_b_stepWidth) : - (aat_b + (int16_t)tuningParams->aat_b_stepWidth); - if(aat_b < (int16_t)tuningParams->aat_b_min) { - aat_b = (int16_t)tuningParams->aat_b_min; - } - if(aat_b > (int16_t)tuningParams->aat_b_max) { - aat_b = (int16_t)tuningParams->aat_b_max; - } - if((int16_t)*b == aat_b) { - return ERR_PARAM; - } - break; - default: - return ERR_REQUEST; - } - /* We only get here if actual values have changed. In all other cases an error is returned */ - *a = (uint8_t)aat_a; - *b = (uint8_t)aat_b; - - return ERR_NONE; -} - -/*******************************************************************************/ -static ReturnCode aatMeasure( - uint8_t serCap, - uint8_t parCap, - uint8_t* amplitude, - uint8_t* phase, - uint16_t* measureCnt) { - ReturnCode err; - - *amplitude = 0; - *phase = 0; - - st25r3916WriteRegister(ST25R3916_REG_ANT_TUNE_A, serCap); - st25r3916WriteRegister(ST25R3916_REG_ANT_TUNE_B, parCap); - - /* Wait till caps have settled.. */ - platformDelay(ST25R3916_AAT_CAP_DELAY_MAX); - - /* Get amplitude and phase .. */ - err = rfalChipMeasureAmplitude(amplitude); - if(ERR_NONE == err) { - err = rfalChipMeasurePhase(phase); - } - - if(measureCnt != NULL) { - (*measureCnt)++; - } - return err; -} diff --git a/lib/ST25RFAL002/source/st25r3916/st25r3916_aat.h b/lib/ST25RFAL002/source/st25r3916/st25r3916_aat.h deleted file mode 100644 index 4c97ad35571..00000000000 --- a/lib/ST25RFAL002/source/st25r3916/st25r3916_aat.h +++ /dev/null @@ -1,109 +0,0 @@ -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R3916 firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file st25r3916_aat.h - * - * \author - * - * \brief ST25R3916 Antenna Tuning - * - * The antenna tuning algorithm tries to find the optimal settings for - * the AAT_A and AAT_B registers, which are connected to variable capacitors - * to tune the antenna matching. - * - */ - -#ifndef ST25R3916_AAT_H -#define ST25R3916_AAT_H - -#include "platform.h" -#include "st_errno.h" - -/* -****************************************************************************** -* GLOBAL DATATYPES -****************************************************************************** -*/ - -/*! - * struct representing input parameters for the antenna tuning - */ -struct st25r3916AatTuneParams { - uint8_t aat_a_min; /*!< min value of A cap */ - uint8_t aat_a_max; /*!< max value of A cap */ - uint8_t aat_a_start; /*!< start value of A cap */ - uint8_t aat_a_stepWidth; /*!< increment stepWidth for A cap */ - uint8_t aat_b_min; /*!< min value of B cap */ - uint8_t aat_b_max; /*!< max value of B cap */ - uint8_t aat_b_start; /*!< start value of B cap */ - uint8_t aat_b_stepWidth; /*!< increment stepWidth for B cap */ - - uint8_t phaTarget; /*!< target phase */ - uint8_t phaWeight; /*!< weight of target phase */ - uint8_t ampTarget; /*!< target amplitude */ - uint8_t ampWeight; /*!< weight of target amplitude */ - - bool doDynamicSteps; /*!< dynamically reduce step size in algo */ - uint8_t measureLimit; /*!< max number of allowed steps/measurements */ -}; - -/*! - * struct representing out parameters for the antenna tuning - */ -struct st25r3916AatTuneResult { - uint8_t aat_a; /*!< serial cap after tuning */ - uint8_t aat_b; /*!< parallel cap after tuning */ - uint8_t pha; /*!< phase after tuning */ - uint8_t amp; /*!< amplitude after tuning */ - uint16_t measureCnt; /*!< number of measures performed */ -}; - -/*! - ***************************************************************************** - * \brief Perform antenna tuning - * - * This function starts an antenna tuning procedure by modifying the serial - * and parallel capacitors of the antenna matching circuit via the AAT_A - * and AAT_B registers. - * - * \param[in] tuningParams : Input parameters for the tuning algorithm. If NULL - * default values will be used. - * \param[out] tuningStatus : Result information of performed tuning. If NULL - * no further information is returned, only registers - * ST25R3916 (AAT_A,B) will be adapted. - * - * \return ERR_IO : Error during communication. - * \return ERR_PARAM : Invalid input parameters - * \return ERR_NONE : No error. - * - ***************************************************************************** - */ -extern ReturnCode st25r3916AatTune( - const struct st25r3916AatTuneParams* tuningParams, - struct st25r3916AatTuneResult* tuningStatus); - -#endif /* ST25R3916_AAT_H */ diff --git a/lib/ST25RFAL002/source/st25r3916/st25r3916_com.c b/lib/ST25RFAL002/source/st25r3916/st25r3916_com.c deleted file mode 100644 index 40d65807d17..00000000000 --- a/lib/ST25RFAL002/source/st25r3916/st25r3916_com.c +++ /dev/null @@ -1,618 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R3916 firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file - * - * \author Gustavo Patricio - * - * \brief Implementation of ST25R3916 communication - * - */ - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ - -#include "st25r3916.h" -#include "st25r3916_com.h" -#include "st25r3916_led.h" -#include "st_errno.h" -#include "platform.h" -#include "utils.h" - -/* -****************************************************************************** -* LOCAL DEFINES -****************************************************************************** -*/ - -#define ST25R3916_OPTIMIZE \ - true /*!< Optimization switch: false always write value to register */ -#define ST25R3916_I2C_ADDR \ - (0xA0U >> 1) /*!< ST25R3916's default I2C address */ -#define ST25R3916_REG_LEN 1U /*!< Byte length of a ST25R3916 register */ - -#define ST25R3916_WRITE_MODE \ - (0U << 6) /*!< ST25R3916 Operation Mode: Write */ -#define ST25R3916_READ_MODE \ - (1U << 6) /*!< ST25R3916 Operation Mode: Read */ -#define ST25R3916_CMD_MODE \ - (3U << 6) /*!< ST25R3916 Operation Mode: Direct Command */ -#define ST25R3916_FIFO_LOAD \ - (0x80U) /*!< ST25R3916 Operation Mode: FIFO Load */ -#define ST25R3916_FIFO_READ \ - (0x9FU) /*!< ST25R3916 Operation Mode: FIFO Read */ -#define ST25R3916_PT_A_CONFIG_LOAD \ - (0xA0U) /*!< ST25R3916 Operation Mode: Passive Target Memory A-Config Load */ -#define ST25R3916_PT_F_CONFIG_LOAD \ - (0xA8U) /*!< ST25R3916 Operation Mode: Passive Target Memory F-Config Load */ -#define ST25R3916_PT_TSN_DATA_LOAD \ - (0xACU) /*!< ST25R3916 Operation Mode: Passive Target Memory TSN Load */ -#define ST25R3916_PT_MEM_READ \ - (0xBFU) /*!< ST25R3916 Operation Mode: Passive Target Memory Read */ - -#define ST25R3916_CMD_LEN \ - (1U) /*!< ST25R3916 CMD length */ -#define ST25R3916_BUF_LEN \ - (ST25R3916_CMD_LEN + \ - ST25R3916_FIFO_DEPTH) /*!< ST25R3916 communication buffer: CMD + FIFO length */ - -/* -****************************************************************************** -* MACROS -****************************************************************************** -*/ -#ifdef RFAL_USE_I2C -#define st25r3916I2CStart() \ - platformI2CStart() /*!< ST25R3916 HAL I2C driver macro to start a I2C transfer */ -#define st25r3916I2CStop() \ - platformI2CStop() /*!< ST25R3916 HAL I2C driver macro to stop a I2C transfer */ -#define st25r3916I2CRepeatStart() \ - platformI2CRepeatStart() /*!< ST25R3916 HAL I2C driver macro to repeat Start */ -#define st25r3916I2CSlaveAddrWR(sA) \ - platformI2CSlaveAddrWR( \ - sA) /*!< ST25R3916 HAL I2C driver macro to repeat Start */ -#define st25r3916I2CSlaveAddrRD(sA) \ - platformI2CSlaveAddrRD( \ - sA) /*!< ST25R3916 HAL I2C driver macro to repeat Start */ -#endif /* RFAL_USE_I2C */ - -#if defined(ST25R_COM_SINGLETXRX) && !defined(RFAL_USE_I2C) -static uint8_t - comBuf[ST25R3916_BUF_LEN]; /*!< ST25R3916 communication buffer */ -static uint16_t comBufIt; /*!< ST25R3916 communication buffer iterator */ -#endif /* ST25R_COM_SINGLETXRX */ - -/* - ****************************************************************************** - * LOCAL FUNCTION PROTOTYPES - ****************************************************************************** - */ - -/*! - ****************************************************************************** - * \brief ST25R3916 communication Start - * - * This method performs the required actions to start communications with - * ST25R3916, either by SPI or I2C - ****************************************************************************** - */ -static void st25r3916comStart(void); - -/*! - ****************************************************************************** - * \brief ST25R3916 communication Stop - * - * This method performs the required actions to terminate communications with - * ST25R3916, either by SPI or I2C - ****************************************************************************** - */ -static void st25r3916comStop(void); - -/*! - ****************************************************************************** - * \brief ST25R3916 communication Repeat Start - * - * This method performs the required actions to repeat start a transmission - * with ST25R3916, either by SPI or I2C - ****************************************************************************** - */ -#ifdef RFAL_USE_I2C -static void st25r3916comRepeatStart(void); -#else -#define st25r3916comRepeatStart() -#endif /* RFAL_USE_I2C */ - -/*! - ****************************************************************************** - * \brief ST25R3916 communication Tx - * - * This method performs the required actions to transmit the given buffer - * to ST25R3916, either by SPI or I2C - * - * \param[in] txBuf : the buffer to transmit - * \param[in] txLen : the length of the buffer to transmit - * \param[in] last : true if last data to be transmitted - * \param[in] txOnly : true no reception is to be performed - * - ****************************************************************************** - */ -static void st25r3916comTx(const uint8_t* txBuf, uint16_t txLen, bool last, bool txOnly); - -/*! - ****************************************************************************** - * \brief ST25R3916 communication Rx - * - * This method performs the required actions to receive from ST25R3916 the given - * amount of bytes, either by SPI or I2C - * - * \param[out] rxBuf : the buffer place the received bytes - * \param[in] rxLen : the length to receive - * - ****************************************************************************** - */ -static void st25r3916comRx(uint8_t* rxBuf, uint16_t rxLen); - -/*! - ****************************************************************************** - * \brief ST25R3916 communication Tx Byte - * - * This helper method transmits a byte passed by value and not by reference - * - * \param[in] txByte : the value of the byte to be transmitted - * \param[in] last : true if last byte to be transmitted - * \param[in] txOnly : true no reception is to be performed - * - ****************************************************************************** - */ -static void st25r3916comTxByte(uint8_t txByte, bool last, bool txOnly); - -/* - ****************************************************************************** - * LOCAL FUNCTION - ****************************************************************************** - */ -static void st25r3916comStart(void) { - /* Make this operation atomic, disabling ST25R3916 interrupt during communications*/ - platformProtectST25RComm(); - -#ifdef RFAL_USE_I2C - /* I2C Start and send Slave Address */ - st25r3916I2CStart(); - st25r3916I2CSlaveAddrWR(ST25R3916_I2C_ADDR); -#else - /* Perform the chip select */ - platformSpiSelect(); - -#if defined(ST25R_COM_SINGLETXRX) - comBufIt = 0; /* reset local buffer position */ -#endif /* ST25R_COM_SINGLETXRX */ - -#endif /* RFAL_USE_I2C */ -} - -/*******************************************************************************/ -static void st25r3916comStop(void) { -#ifdef RFAL_USE_I2C - /* Generate Stop signal */ - st25r3916I2CStop(); -#else - /* Release the chip select */ - platformSpiDeselect(); -#endif /* RFAL_USE_I2C */ - - /* reEnable the ST25R3916 interrupt */ - platformUnprotectST25RComm(); -} - -/*******************************************************************************/ -#ifdef RFAL_USE_I2C -static void st25r3916comRepeatStart(void) { - st25r3916I2CRepeatStart(); - st25r3916I2CSlaveAddrRD(ST25R3916_I2C_ADDR); -} -#endif /* RFAL_USE_I2C */ - -/*******************************************************************************/ -static void st25r3916comTx(const uint8_t* txBuf, uint16_t txLen, bool last, bool txOnly) { - NO_WARNING(last); - NO_WARNING(txOnly); - - if(txLen > 0U) { -#ifdef RFAL_USE_I2C - platformI2CTx(txBuf, txLen, last, txOnly); -#else /* RFAL_USE_I2C */ - -#ifdef ST25R_COM_SINGLETXRX - - ST_MEMCPY( - &comBuf[comBufIt], - txBuf, - MIN(txLen, - (ST25R3916_BUF_LEN - - comBufIt))); /* copy tx data to local buffer */ - comBufIt += - MIN(txLen, - (ST25R3916_BUF_LEN - - comBufIt)); /* store position on local buffer */ - - if(last && txOnly) /* only perform SPI transaction if no Rx will follow */ - { - platformSpiTxRx(comBuf, NULL, comBufIt); - } - -#else - platformSpiTxRx(txBuf, NULL, txLen); -#endif /* ST25R_COM_SINGLETXRX */ - -#endif /* RFAL_USE_I2C */ - } -} - -/*******************************************************************************/ -static void st25r3916comRx(uint8_t* rxBuf, uint16_t rxLen) { - if(rxLen > 0U) { -#ifdef RFAL_USE_I2C - platformI2CRx(rxBuf, rxLen); -#else /* RFAL_USE_I2C */ - -#ifdef ST25R_COM_SINGLETXRX - ST_MEMSET( - &comBuf[comBufIt], - 0x00, - MIN(rxLen, - (ST25R3916_BUF_LEN - - comBufIt))); /* clear outgoing buffer */ - platformSpiTxRx( - comBuf, - comBuf, - MIN((comBufIt + rxLen), - ST25R3916_BUF_LEN)); /* transceive as a single SPI call */ - ST_MEMCPY( - rxBuf, - &comBuf[comBufIt], - MIN(rxLen, - (ST25R3916_BUF_LEN - - comBufIt))); /* copy from local buf to output buffer and skip cmd byte */ -#else - if(rxBuf != NULL) { - ST_MEMSET( - rxBuf, 0x00, rxLen); /* clear outgoing buffer */ - } - platformSpiTxRx(NULL, rxBuf, rxLen); -#endif /* ST25R_COM_SINGLETXRX */ -#endif /* RFAL_USE_I2C */ - } -} - -/*******************************************************************************/ -static void st25r3916comTxByte(uint8_t txByte, bool last, bool txOnly) { - uint8_t val = txByte; /* MISRA 17.8: use intermediate variable */ - st25r3916comTx(&val, ST25R3916_REG_LEN, last, txOnly); -} - -/* -****************************************************************************** -* GLOBAL FUNCTIONS -****************************************************************************** -*/ - -/*******************************************************************************/ -ReturnCode st25r3916ReadRegister(uint8_t reg, uint8_t* val) { - return st25r3916ReadMultipleRegisters(reg, val, ST25R3916_REG_LEN); -} - -/*******************************************************************************/ -ReturnCode st25r3916ReadMultipleRegisters(uint8_t reg, uint8_t* values, uint8_t length) { - if(length > 0U) { - st25r3916comStart(); - - /* If is a space-B register send a direct command first */ - if((reg & ST25R3916_SPACE_B) != 0U) { - st25r3916comTxByte(ST25R3916_CMD_SPACE_B_ACCESS, false, false); - } - - st25r3916comTxByte(((reg & ~ST25R3916_SPACE_B) | ST25R3916_READ_MODE), true, false); - st25r3916comRepeatStart(); - st25r3916comRx(values, length); - st25r3916comStop(); - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode st25r3916WriteRegister(uint8_t reg, uint8_t val) { - uint8_t value = val; /* MISRA 17.8: use intermediate variable */ - return st25r3916WriteMultipleRegisters(reg, &value, ST25R3916_REG_LEN); -} - -/*******************************************************************************/ -ReturnCode st25r3916WriteMultipleRegisters(uint8_t reg, const uint8_t* values, uint8_t length) { - if(length > 0U) { - st25r3916comStart(); - - if((reg & ST25R3916_SPACE_B) != 0U) { - st25r3916comTxByte(ST25R3916_CMD_SPACE_B_ACCESS, false, true); - } - - st25r3916comTxByte(((reg & ~ST25R3916_SPACE_B) | ST25R3916_WRITE_MODE), false, true); - st25r3916comTx(values, length, true, true); - st25r3916comStop(); - - /* Send a WriteMultiReg event to LED handling */ - st25r3916ledEvtWrMultiReg(reg, values, length); - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode st25r3916WriteFifo(const uint8_t* values, uint16_t length) { - if(length > ST25R3916_FIFO_DEPTH) { - return ERR_PARAM; - } - - if(length > 0U) { - st25r3916comStart(); - st25r3916comTxByte(ST25R3916_FIFO_LOAD, false, true); - st25r3916comTx(values, length, true, true); - st25r3916comStop(); - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode st25r3916ReadFifo(uint8_t* buf, uint16_t length) { - if(length > 0U) { - st25r3916comStart(); - st25r3916comTxByte(ST25R3916_FIFO_READ, true, false); - - st25r3916comRepeatStart(); - st25r3916comRx(buf, length); - st25r3916comStop(); - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode st25r3916WritePTMem(const uint8_t* values, uint16_t length) { - if(length > ST25R3916_PTM_LEN) { - return ERR_PARAM; - } - - if(length > 0U) { - st25r3916comStart(); - st25r3916comTxByte(ST25R3916_PT_A_CONFIG_LOAD, false, true); - st25r3916comTx(values, length, true, true); - st25r3916comStop(); - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode st25r3916ReadPTMem(uint8_t* values, uint16_t length) { - uint8_t - tmp[ST25R3916_REG_LEN + - ST25R3916_PTM_LEN]; /* local buffer to handle prepended byte on I2C and SPI */ - - if(length > 0U) { - if(length > ST25R3916_PTM_LEN) { - return ERR_PARAM; - } - - st25r3916comStart(); - st25r3916comTxByte(ST25R3916_PT_MEM_READ, true, false); - - st25r3916comRepeatStart(); - st25r3916comRx(tmp, (ST25R3916_REG_LEN + length)); /* skip prepended byte */ - st25r3916comStop(); - - /* Copy PTMem content without prepended byte */ - ST_MEMCPY(values, (tmp + ST25R3916_REG_LEN), length); - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode st25r3916WritePTMemF(const uint8_t* values, uint16_t length) { - if(length > (ST25R3916_PTM_F_LEN + ST25R3916_PTM_TSN_LEN)) { - return ERR_PARAM; - } - - if(length > 0U) { - st25r3916comStart(); - st25r3916comTxByte(ST25R3916_PT_F_CONFIG_LOAD, false, true); - st25r3916comTx(values, length, true, true); - st25r3916comStop(); - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode st25r3916WritePTMemTSN(const uint8_t* values, uint16_t length) { - if(length > ST25R3916_PTM_TSN_LEN) { - return ERR_PARAM; - } - - if(length > 0U) { - st25r3916comStart(); - st25r3916comTxByte(ST25R3916_PT_TSN_DATA_LOAD, false, true); - st25r3916comTx(values, length, true, true); - st25r3916comStop(); - } - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode st25r3916ExecuteCommand(uint8_t cmd) { - st25r3916comStart(); - st25r3916comTxByte((cmd | ST25R3916_CMD_MODE), true, true); - st25r3916comStop(); - - /* Send a cmd event to LED handling */ - st25r3916ledEvtCmd(cmd); - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode st25r3916ReadTestRegister(uint8_t reg, uint8_t* val) { - st25r3916comStart(); - st25r3916comTxByte(ST25R3916_CMD_TEST_ACCESS, false, false); - st25r3916comTxByte((reg | ST25R3916_READ_MODE), true, false); - st25r3916comRepeatStart(); - st25r3916comRx(val, ST25R3916_REG_LEN); - st25r3916comStop(); - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode st25r3916WriteTestRegister(uint8_t reg, uint8_t val) { - uint8_t value = val; /* MISRA 17.8: use intermediate variable */ - - st25r3916comStart(); - st25r3916comTxByte(ST25R3916_CMD_TEST_ACCESS, false, true); - st25r3916comTxByte((reg | ST25R3916_WRITE_MODE), false, true); - st25r3916comTx(&value, ST25R3916_REG_LEN, true, true); - st25r3916comStop(); - - return ERR_NONE; -} - -/*******************************************************************************/ -ReturnCode st25r3916ClrRegisterBits(uint8_t reg, uint8_t clr_mask) { - ReturnCode ret; - uint8_t rdVal; - - /* Read current reg value */ - EXIT_ON_ERR(ret, st25r3916ReadRegister(reg, &rdVal)); - - /* Only perform a Write if value to be written is different */ - if(ST25R3916_OPTIMIZE && (rdVal == (uint8_t)(rdVal & ~clr_mask))) { - return ERR_NONE; - } - - /* Write new reg value */ - return st25r3916WriteRegister(reg, (uint8_t)(rdVal & ~clr_mask)); -} - -/*******************************************************************************/ -ReturnCode st25r3916SetRegisterBits(uint8_t reg, uint8_t set_mask) { - ReturnCode ret; - uint8_t rdVal; - - /* Read current reg value */ - EXIT_ON_ERR(ret, st25r3916ReadRegister(reg, &rdVal)); - - /* Only perform a Write if the value to be written is different */ - if(ST25R3916_OPTIMIZE && (rdVal == (rdVal | set_mask))) { - return ERR_NONE; - } - - /* Write new reg value */ - return st25r3916WriteRegister(reg, (rdVal | set_mask)); -} - -/*******************************************************************************/ -ReturnCode st25r3916ChangeRegisterBits(uint8_t reg, uint8_t valueMask, uint8_t value) { - return st25r3916ModifyRegister(reg, valueMask, (valueMask & value)); -} - -/*******************************************************************************/ -ReturnCode st25r3916ModifyRegister(uint8_t reg, uint8_t clr_mask, uint8_t set_mask) { - ReturnCode ret; - uint8_t rdVal; - uint8_t wrVal; - - /* Read current reg value */ - EXIT_ON_ERR(ret, st25r3916ReadRegister(reg, &rdVal)); - - /* Compute new value */ - wrVal = (uint8_t)(rdVal & ~clr_mask); - wrVal |= set_mask; - - /* Only perform a Write if the value to be written is different */ - if(ST25R3916_OPTIMIZE && (rdVal == wrVal)) { - return ERR_NONE; - } - - /* Write new reg value */ - return st25r3916WriteRegister(reg, wrVal); -} - -/*******************************************************************************/ -ReturnCode st25r3916ChangeTestRegisterBits(uint8_t reg, uint8_t valueMask, uint8_t value) { - ReturnCode ret; - uint8_t rdVal; - uint8_t wrVal; - - /* Read current reg value */ - EXIT_ON_ERR(ret, st25r3916ReadTestRegister(reg, &rdVal)); - - /* Compute new value */ - wrVal = (uint8_t)(rdVal & ~valueMask); - wrVal |= (uint8_t)(value & valueMask); - - /* Only perform a Write if the value to be written is different */ - if(ST25R3916_OPTIMIZE && (rdVal == wrVal)) { - return ERR_NONE; - } - - /* Write new reg value */ - return st25r3916WriteTestRegister(reg, wrVal); -} - -/*******************************************************************************/ -bool st25r3916CheckReg(uint8_t reg, uint8_t mask, uint8_t val) { - uint8_t regVal; - - regVal = 0; - st25r3916ReadRegister(reg, ®Val); - - return ((regVal & mask) == val); -} - -/*******************************************************************************/ -bool st25r3916IsRegValid(uint8_t reg) { -#pragma GCC diagnostic ignored "-Wtype-limits" - if(!(((int16_t)reg >= (int32_t)ST25R3916_REG_IO_CONF1) && - (reg <= (ST25R3916_SPACE_B | ST25R3916_REG_IC_IDENTITY)))) { - return false; - } - return true; -} diff --git a/lib/ST25RFAL002/source/st25r3916/st25r3916_com.h b/lib/ST25RFAL002/source/st25r3916/st25r3916_com.h deleted file mode 100644 index 726758c83d8..00000000000 --- a/lib/ST25RFAL002/source/st25r3916/st25r3916_com.h +++ /dev/null @@ -1,1384 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R3916 firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file - * - * \author Gustavo Patricio - * - * \brief ST25R3916 communication declaration file - * - * This driver provides basic abstraction for communication with the ST25R3916 - * - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-HAL - * \brief RFAL Hardware Abstraction Layer - * @{ - * - * \addtogroup ST25R3916 - * \brief RFAL ST25R3916 Driver - * @{ - * - * \addtogroup ST25R3916_COM - * \brief RFAL ST25R3916 Communications - * @{ - * - */ - -#ifndef ST25R3916_COM_H -#define ST25R3916_COM_H - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ -#include "platform.h" -#include "st_errno.h" - -/* -****************************************************************************** -* GLOBAL DEFINES -****************************************************************************** -*/ -#define ST25R3916_SPACE_B 0x40U /*!< ST25R3916 Space-B indicator */ -#define ST25R3916_SPACE_B_REG_LEN 16U /*!< Number of register in the space B */ - -#define ST25R3916_FIFO_STATUS_LEN 2 /*!< Number of FIFO Status Register */ - -#define ST25R3916_PTM_A_LEN 15U /*!< Passive target memory A config length */ -#define ST25R3916_PTM_B_LEN 0U /*!< Passive target memory B config length */ -#define ST25R3916_PTM_F_LEN 21U /*!< Passive target memory F config length */ -#define ST25R3916_PTM_TSN_LEN 12U /*!< Passive target memory TSN data length */ - -/*! Full Passive target memory length */ -#define ST25R3916_PTM_LEN \ - (ST25R3916_PTM_A_LEN + ST25R3916_PTM_B_LEN + ST25R3916_PTM_F_LEN + ST25R3916_PTM_TSN_LEN) - -/* IO configuration registers */ -#define ST25R3916_REG_IO_CONF1 0x00U /*!< RW IO Configuration Register 1 */ -#define ST25R3916_REG_IO_CONF2 0x01U /*!< RW IO Configuration Register 2 */ - -/* Operation control and mode definition registers */ -#define ST25R3916_REG_OP_CONTROL 0x02U /*!< RW Operation Control Register */ -#define ST25R3916_REG_MODE 0x03U /*!< RW Mode Definition Register */ -#define ST25R3916_REG_BIT_RATE 0x04U /*!< RW Bit Rate Definition Register */ - -/* Protocol Configuration registers */ -#define ST25R3916_REG_ISO14443A_NFC \ - 0x05U /*!< RW ISO14443A and NFC 106 kBit/s Settings Register */ -#define ST25R3916_REG_EMD_SUP_CONF \ - (ST25R3916_SPACE_B | 0x05U) /*!< RW EMD Suppression Configuration Register */ -#define ST25R3916_REG_ISO14443B_1 \ - 0x06U /*!< RW ISO14443B Settings Register 1 */ -#define ST25R3916_REG_SUBC_START_TIME \ - (ST25R3916_SPACE_B | 0x06U) /*!< RW Subcarrier Start Time Register */ -#define ST25R3916_REG_ISO14443B_2 \ - 0x07U /*!< RW ISO14443B Settings Register 2 */ -#define ST25R3916_REG_PASSIVE_TARGET \ - 0x08U /*!< RW Passive Target Definition Register */ -#define ST25R3916_REG_STREAM_MODE \ - 0x09U /*!< RW Stream Mode Definition Register */ -#define ST25R3916_REG_AUX 0x0AU /*!< RW Auxiliary Definition Register */ - -/* Receiver Configuration registers */ -#define ST25R3916_REG_RX_CONF1 0x0BU /*!< RW Receiver Configuration Register 1 */ -#define ST25R3916_REG_RX_CONF2 0x0CU /*!< RW Receiver Configuration Register 2 */ -#define ST25R3916_REG_RX_CONF3 0x0DU /*!< RW Receiver Configuration Register 3 */ -#define ST25R3916_REG_RX_CONF4 0x0EU /*!< RW Receiver Configuration Register 4 */ -#define ST25R3916_REG_P2P_RX_CONF \ - (ST25R3916_SPACE_B | 0x0BU) /*!< RW P2P Receiver Configuration Register 1 */ -#define ST25R3916_REG_CORR_CONF1 \ - (ST25R3916_SPACE_B | 0x0CU) /*!< RW Correlator configuration register 1 */ -#define ST25R3916_REG_CORR_CONF2 \ - (ST25R3916_SPACE_B | 0x0DU) /*!< RW Correlator configuration register 2 */ - -/* Timer definition registers */ -#define ST25R3916_REG_MASK_RX_TIMER \ - 0x0FU /*!< RW Mask Receive Timer Register */ -#define ST25R3916_REG_NO_RESPONSE_TIMER1 \ - 0x10U /*!< RW No-response Timer Register 1 */ -#define ST25R3916_REG_NO_RESPONSE_TIMER2 \ - 0x11U /*!< RW No-response Timer Register 2 */ -#define ST25R3916_REG_TIMER_EMV_CONTROL \ - 0x12U /*!< RW Timer and EMV Control */ -#define ST25R3916_REG_GPT1 0x13U /*!< RW General Purpose Timer Register 1 */ -#define ST25R3916_REG_GPT2 0x14U /*!< RW General Purpose Timer Register 2 */ -#define ST25R3916_REG_PPON2 0x15U /*!< RW PPON2 Field waiting Timer Register */ -#define ST25R3916_REG_SQUELCH_TIMER \ - (ST25R3916_SPACE_B | 0x0FU) /*!< RW Squelch timeout Register */ -#define ST25R3916_REG_FIELD_ON_GT \ - (ST25R3916_SPACE_B | 0x15U) /*!< RW NFC Field on guard time */ - -/* Interrupt and associated reporting registers */ -#define ST25R3916_REG_IRQ_MASK_MAIN \ - 0x16U /*!< RW Mask Main Interrupt Register */ -#define ST25R3916_REG_IRQ_MASK_TIMER_NFC \ - 0x17U /*!< RW Mask Timer and NFC Interrupt Register */ -#define ST25R3916_REG_IRQ_MASK_ERROR_WUP \ - 0x18U /*!< RW Mask Error and Wake-up Interrupt Register */ -#define ST25R3916_REG_IRQ_MASK_TARGET \ - 0x19U /*!< RW Mask 3916 Target Interrupt Register */ -#define ST25R3916_REG_IRQ_MAIN 0x1AU /*!< R Main Interrupt Register */ -#define ST25R3916_REG_IRQ_TIMER_NFC \ - 0x1BU /*!< R Timer and NFC Interrupt Register */ -#define ST25R3916_REG_IRQ_ERROR_WUP \ - 0x1CU /*!< R Error and Wake-up Interrupt Register */ -#define ST25R3916_REG_IRQ_TARGET 0x1DU /*!< R ST25R3916 Target Interrupt Register */ -#define ST25R3916_REG_FIFO_STATUS1 \ - 0x1EU /*!< R FIFO Status Register 1 */ -#define ST25R3916_REG_FIFO_STATUS2 \ - 0x1FU /*!< R FIFO Status Register 2 */ -#define ST25R3916_REG_COLLISION_STATUS \ - 0x20U /*!< R Collision Display Register */ -#define ST25R3916_REG_PASSIVE_TARGET_STATUS \ - 0x21U /*!< R Passive target state status */ - -/* Definition of number of transmitted bytes */ -#define ST25R3916_REG_NUM_TX_BYTES1 \ - 0x22U /*!< RW Number of Transmitted Bytes Register 1 */ -#define ST25R3916_REG_NUM_TX_BYTES2 \ - 0x23U /*!< RW Number of Transmitted Bytes Register 2 */ - -/* NFCIP Bit Rate Display Register */ -#define ST25R3916_REG_NFCIP1_BIT_RATE \ - 0x24U /*!< R NFCIP Bit Rate Detection Display Register */ - -/* A/D Converter Output Register */ -#define ST25R3916_REG_AD_RESULT 0x25U /*!< R A/D Converter Output Register */ - -/* Antenna tuning registers */ -#define ST25R3916_REG_ANT_TUNE_A 0x26U /*!< RW Antenna Tuning Control (AAT-A) Register 1 */ -#define ST25R3916_REG_ANT_TUNE_B 0x27U /*!< RW Antenna Tuning Control (AAT-B) Register 2 */ - -/* Antenna Driver and Modulation registers */ -#define ST25R3916_REG_TX_DRIVER 0x28U /*!< RW TX driver register */ -#define ST25R3916_REG_PT_MOD 0x29U /*!< RW PT modulation Register */ -#define ST25R3916_REG_AUX_MOD \ - (ST25R3916_SPACE_B | 0x28U) /*!< RW Aux Modulation setting Register */ -#define ST25R3916_REG_TX_DRIVER_TIMING \ - (ST25R3916_SPACE_B | 0x29U) /*!< RW TX driver timing Register */ -#define ST25R3916_REG_RES_AM_MOD \ - (ST25R3916_SPACE_B | 0x2AU) /*!< RW Resistive AM modulation register */ -#define ST25R3916_REG_TX_DRIVER_STATUS \ - (ST25R3916_SPACE_B | 0x2BU) /*!< R TX driver timing readout Register */ - -/* External Field Detector Threshold Registers */ -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV \ - 0x2AU /*!< RW External Field Detector Activation Threshold Reg */ -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV \ - 0x2BU /*!< RW External Field Detector Deactivation Threshold Reg*/ - -/* Regulator registers */ -#define ST25R3916_REG_REGULATOR_CONTROL \ - 0x2CU /*!< RW Regulated Voltage Control Register */ -#define ST25R3916_REG_REGULATOR_RESULT \ - (ST25R3916_SPACE_B | 0x2CU) /*!< R Regulator Display Register */ - -/* Receiver State Display Register */ -#define ST25R3916_REG_RSSI_RESULT \ - 0x2DU /*!< R RSSI Display Register */ -#define ST25R3916_REG_GAIN_RED_STATE \ - 0x2EU /*!< R Gain Reduction State Register */ -#define ST25R3916_REG_CAP_SENSOR_CONTROL \ - 0x2FU /*!< RW Capacitive Sensor Control Register */ -#define ST25R3916_REG_CAP_SENSOR_RESULT \ - 0x30U /*!< R Capacitive Sensor Display Register */ -#define ST25R3916_REG_AUX_DISPLAY \ - 0x31U /*!< R Auxiliary Display Register */ - -/* Over/Undershoot Protection Configuration Registers */ -#define ST25R3916_REG_OVERSHOOT_CONF1 \ - (ST25R3916_SPACE_B | 0x30U) /*!< RW Overshoot Protection Configuration Register 1 */ -#define ST25R3916_REG_OVERSHOOT_CONF2 \ - (ST25R3916_SPACE_B | 0x31U) /*!< RW Overshoot Protection Configuration Register 2 */ -#define ST25R3916_REG_UNDERSHOOT_CONF1 \ - (ST25R3916_SPACE_B | 0x32U) /*!< RW Undershoot Protection Configuration Register 1 */ -#define ST25R3916_REG_UNDERSHOOT_CONF2 \ - (ST25R3916_SPACE_B | 0x33U) /*!< RW Undershoot Protection Configuration Register 2 */ - -/* Detection of card presence */ -#define ST25R3916_REG_WUP_TIMER_CONTROL \ - 0x32U /*!< RW Wake-up Timer Control Register */ -#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF \ - 0x33U /*!< RW Amplitude Measurement Configuration Register */ -#define ST25R3916_REG_AMPLITUDE_MEASURE_REF \ - 0x34U /*!< RW Amplitude Measurement Reference Register */ -#define ST25R3916_REG_AMPLITUDE_MEASURE_AA_RESULT \ - 0x35U /*!< R Amplitude Measurement Auto Averaging Display Reg */ -#define ST25R3916_REG_AMPLITUDE_MEASURE_RESULT \ - 0x36U /*!< R Amplitude Measurement Display Register */ -#define ST25R3916_REG_PHASE_MEASURE_CONF \ - 0x37U /*!< RW Phase Measurement Configuration Register */ -#define ST25R3916_REG_PHASE_MEASURE_REF \ - 0x38U /*!< RW Phase Measurement Reference Register */ -#define ST25R3916_REG_PHASE_MEASURE_AA_RESULT \ - 0x39U /*!< R Phase Measurement Auto Averaging Display Register */ -#define ST25R3916_REG_PHASE_MEASURE_RESULT \ - 0x3AU /*!< R Phase Measurement Display Register */ -#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF \ - 0x3BU /*!< RW Capacitance Measurement Configuration Register */ -#define ST25R3916_REG_CAPACITANCE_MEASURE_REF \ - 0x3CU /*!< RW Capacitance Measurement Reference Register */ -#define ST25R3916_REG_CAPACITANCE_MEASURE_AA_RESULT \ - 0x3DU /*!< R Capacitance Measurement Auto Averaging Display Reg*/ -#define ST25R3916_REG_CAPACITANCE_MEASURE_RESULT \ - 0x3EU /*!< R Capacitance Measurement Display Register */ - -/* IC identity */ -#define ST25R3916_REG_IC_IDENTITY \ - 0x3FU /*!< R Chip Id: 0 for old silicon, v2 silicon: 0x09 */ - -/*! Register bit definitions \cond DOXYGEN_SUPRESS */ - -#define ST25R3916_REG_IO_CONF1_single (1U << 7) -#define ST25R3916_REG_IO_CONF1_rfo2 (1U << 6) -#define ST25R3916_REG_IO_CONF1_i2c_thd1 (1U << 5) -#define ST25R3916_REG_IO_CONF1_i2c_thd0 (1U << 4) -#define ST25R3916_REG_IO_CONF1_i2c_thd_mask (3U << 4) -#define ST25R3916_REG_IO_CONF1_i2c_thd_shift (4U) -#define ST25R3916_REG_IO_CONF1_rfu (1U << 3) -#define ST25R3916_REG_IO_CONF1_out_cl1 (1U << 2) -#define ST25R3916_REG_IO_CONF1_out_cl0 (1U << 1) -#define ST25R3916_REG_IO_CONF1_out_cl_disabled (3U << 1) -#define ST25R3916_REG_IO_CONF1_out_cl_13_56MHZ (2U << 1) -#define ST25R3916_REG_IO_CONF1_out_cl_4_78MHZ (1U << 1) -#define ST25R3916_REG_IO_CONF1_out_cl_3_39MHZ (0U << 1) -#define ST25R3916_REG_IO_CONF1_out_cl_mask (3U << 1) -#define ST25R3916_REG_IO_CONF1_out_cl_shift (1U) -#define ST25R3916_REG_IO_CONF1_lf_clk_off (1U << 0) -#define ST25R3916_REG_IO_CONF1_lf_clk_off_on (1U << 0) -#define ST25R3916_REG_IO_CONF1_lf_clk_off_off (0U << 0) - -#define ST25R3916_REG_IO_CONF2_sup3V (1U << 7) -#define ST25R3916_REG_IO_CONF2_sup3V_3V (1U << 7) -#define ST25R3916_REG_IO_CONF2_sup3V_5V (0U << 7) -#define ST25R3916_REG_IO_CONF2_vspd_off (1U << 6) -#define ST25R3916_REG_IO_CONF2_aat_en (1U << 5) -#define ST25R3916_REG_IO_CONF2_miso_pd2 (1U << 4) -#define ST25R3916_REG_IO_CONF2_miso_pd1 (1U << 3) -#define ST25R3916_REG_IO_CONF2_io_drv_lvl (1U << 2) -#define ST25R3916_REG_IO_CONF2_slow_up (1U << 0) - -#define ST25R3916_REG_OP_CONTROL_en (1U << 7) -#define ST25R3916_REG_OP_CONTROL_rx_en (1U << 6) -#define ST25R3916_REG_OP_CONTROL_rx_chn (1U << 5) -#define ST25R3916_REG_OP_CONTROL_rx_man (1U << 4) -#define ST25R3916_REG_OP_CONTROL_tx_en (1U << 3) -#define ST25R3916_REG_OP_CONTROL_wu (1U << 2) -#define ST25R3916_REG_OP_CONTROL_en_fd_c1 (1U << 1) -#define ST25R3916_REG_OP_CONTROL_en_fd_c0 (1U << 0) -#define ST25R3916_REG_OP_CONTROL_en_fd_efd_off (0U << 0) -#define ST25R3916_REG_OP_CONTROL_en_fd_manual_efd_ca (1U << 0) -#define ST25R3916_REG_OP_CONTROL_en_fd_manual_efd_pdt (2U << 0) -#define ST25R3916_REG_OP_CONTROL_en_fd_auto_efd (3U << 0) -#define ST25R3916_REG_OP_CONTROL_en_fd_shift (0U) -#define ST25R3916_REG_OP_CONTROL_en_fd_mask (3U << 0) - -#define ST25R3916_REG_MODE_targ (1U << 7) -#define ST25R3916_REG_MODE_targ_targ (1U << 7) -#define ST25R3916_REG_MODE_targ_init (0U << 7) -#define ST25R3916_REG_MODE_om3 (1U << 6) -#define ST25R3916_REG_MODE_om2 (1U << 5) -#define ST25R3916_REG_MODE_om1 (1U << 4) -#define ST25R3916_REG_MODE_om0 (1U << 3) -#define ST25R3916_REG_MODE_om_bpsk_stream (0xfU << 3) -#define ST25R3916_REG_MODE_om_subcarrier_stream (0xeU << 3) -#define ST25R3916_REG_MODE_om_topaz (0x4U << 3) -#define ST25R3916_REG_MODE_om_felica (0x3U << 3) -#define ST25R3916_REG_MODE_om_iso14443b (0x2U << 3) -#define ST25R3916_REG_MODE_om_iso14443a (0x1U << 3) -#define ST25R3916_REG_MODE_om_targ_nfca (0x1U << 3) -#define ST25R3916_REG_MODE_om_targ_nfcb (0x2U << 3) -#define ST25R3916_REG_MODE_om_targ_nfcf (0x4U << 3) -#define ST25R3916_REG_MODE_om_targ_nfcip (0x7U << 3) -#define ST25R3916_REG_MODE_om_nfc (0x0U << 3) -#define ST25R3916_REG_MODE_om_mask (0xfU << 3) -#define ST25R3916_REG_MODE_om_shift (3U) -#define ST25R3916_REG_MODE_tr_am (1U << 2) -#define ST25R3916_REG_MODE_tr_am_ook (0U << 2) -#define ST25R3916_REG_MODE_tr_am_am (1U << 2) -#define ST25R3916_REG_MODE_nfc_ar1 (1U << 1) -#define ST25R3916_REG_MODE_nfc_ar0 (1U << 0) -#define ST25R3916_REG_MODE_nfc_ar_off (0U << 0) -#define ST25R3916_REG_MODE_nfc_ar_auto_rx (1U << 0) -#define ST25R3916_REG_MODE_nfc_ar_eof (2U << 0) -#define ST25R3916_REG_MODE_nfc_ar_rfu (3U << 0) -#define ST25R3916_REG_MODE_nfc_ar_mask (3U << 0) -#define ST25R3916_REG_MODE_nfc_ar_shift (0U) - -#define ST25R3916_REG_BIT_RATE_txrate_106 (0x0U << 4) -#define ST25R3916_REG_BIT_RATE_txrate_212 (0x1U << 4) -#define ST25R3916_REG_BIT_RATE_txrate_424 (0x2U << 4) -#define ST25R3916_REG_BIT_RATE_txrate_848 (0x3U << 4) -#define ST25R3916_REG_BIT_RATE_txrate_mask (0x3U << 4) -#define ST25R3916_REG_BIT_RATE_txrate_shift (4U) -#define ST25R3916_REG_BIT_RATE_rxrate_106 (0x0U << 0) -#define ST25R3916_REG_BIT_RATE_rxrate_212 (0x1U << 0) -#define ST25R3916_REG_BIT_RATE_rxrate_424 (0x2U << 0) -#define ST25R3916_REG_BIT_RATE_rxrate_848 (0x3U << 0) -#define ST25R3916_REG_BIT_RATE_rxrate_mask (0x3U << 0) -#define ST25R3916_REG_BIT_RATE_rxrate_shift (0U) - -#define ST25R3916_REG_ISO14443A_NFC_no_tx_par (1U << 7) -#define ST25R3916_REG_ISO14443A_NFC_no_tx_par_off (0U << 7) -#define ST25R3916_REG_ISO14443A_NFC_no_rx_par (1U << 6) -#define ST25R3916_REG_ISO14443A_NFC_no_rx_par_off (0U << 6) -#define ST25R3916_REG_ISO14443A_NFC_nfc_f0 (1U << 5) -#define ST25R3916_REG_ISO14443A_NFC_nfc_f0_off (0U << 5) -#define ST25R3916_REG_ISO14443A_NFC_p_len3 (1U << 4) -#define ST25R3916_REG_ISO14443A_NFC_p_len2 (1U << 3) -#define ST25R3916_REG_ISO14443A_NFC_p_len1 (1U << 2) -#define ST25R3916_REG_ISO14443A_NFC_p_len0 (1U << 1) -#define ST25R3916_REG_ISO14443A_NFC_p_len_mask (0xfU << 1) -#define ST25R3916_REG_ISO14443A_NFC_p_len_shift (1U) -#define ST25R3916_REG_ISO14443A_NFC_antcl (1U << 0) - -#define ST25R3916_REG_EMD_SUP_CONF_emd_emv (1U << 7) -#define ST25R3916_REG_EMD_SUP_CONF_emd_emv_on (1U << 7) -#define ST25R3916_REG_EMD_SUP_CONF_emd_emv_off (0U << 7) -#define ST25R3916_REG_EMD_SUP_CONF_rx_start_emv (1U << 6) -#define ST25R3916_REG_EMD_SUP_CONF_rx_start_emv_on (1U << 6) -#define ST25R3916_REG_EMD_SUP_CONF_rx_start_emv_off (0U << 6) -#define ST25R3916_REG_EMD_SUP_CONF_rfu1 (1U << 5) -#define ST25R3916_REG_EMD_SUP_CONF_rfu0 (1U << 4) -#define ST25R3916_REG_EMD_SUP_CONF_emd_thld3 (1U << 3) -#define ST25R3916_REG_EMD_SUP_CONF_emd_thld2 (1U << 2) -#define ST25R3916_REG_EMD_SUP_CONF_emd_thld1 (1U << 1) -#define ST25R3916_REG_EMD_SUP_CONF_emd_thld0 (1U << 0) -#define ST25R3916_REG_EMD_SUP_CONF_emd_thld_mask (0xfU << 0) -#define ST25R3916_REG_EMD_SUP_CONF_emd_thld_shift (0U) - -#define ST25R3916_REG_SUBC_START_TIME_rfu2 (1U << 7) -#define ST25R3916_REG_SUBC_START_TIME_rfu1 (1U << 6) -#define ST25R3916_REG_SUBC_START_TIME_rfu0 (1U << 5) -#define ST25R3916_REG_SUBC_START_TIME_sst4 (1U << 4) -#define ST25R3916_REG_SUBC_START_TIME_sst3 (1U << 3) -#define ST25R3916_REG_SUBC_START_TIME_sst2 (1U << 2) -#define ST25R3916_REG_SUBC_START_TIME_sst1 (1U << 1) -#define ST25R3916_REG_SUBC_START_TIME_sst0 (1U << 0) -#define ST25R3916_REG_SUBC_START_TIME_sst_mask (0x1fU << 0) -#define ST25R3916_REG_SUBC_START_TIME_sst_shift (0U) - -#define ST25R3916_REG_ISO14443B_1_egt2 (1U << 7) -#define ST25R3916_REG_ISO14443B_1_egt1 (1U << 6) -#define ST25R3916_REG_ISO14443B_1_egt0 (1U << 5) -#define ST25R3916_REG_ISO14443B_1_egt_shift (5U) -#define ST25R3916_REG_ISO14443B_1_egt_mask (7U << 5) -#define ST25R3916_REG_ISO14443B_1_sof_1 (1U << 3) -#define ST25R3916_REG_ISO14443B_1_sof_1_3etu (1U << 3) -#define ST25R3916_REG_ISO14443B_1_sof_1_2etu (0U << 3) -#define ST25R3916_REG_ISO14443B_1_sof_0 (1U << 4) -#define ST25R3916_REG_ISO14443B_1_sof_0_11etu (1U << 4) -#define ST25R3916_REG_ISO14443B_1_sof_0_10etu (0U << 4) -#define ST25R3916_REG_ISO14443B_1_sof_mask (3U << 3) -#define ST25R3916_REG_ISO14443B_1_eof (1U << 2) -#define ST25R3916_REG_ISO14443B_1_eof_11etu (1U << 2) -#define ST25R3916_REG_ISO14443B_1_eof_10etu (0U << 2) -#define ST25R3916_REG_ISO14443B_1_half (1U << 1) -#define ST25R3916_REG_ISO14443B_1_rx_st_om (1U << 0) - -#define ST25R3916_REG_ISO14443B_2_tr1_1 (1U << 7) -#define ST25R3916_REG_ISO14443B_2_tr1_0 (1U << 6) -#define ST25R3916_REG_ISO14443B_2_tr1_64fs32fs (1U << 6) -#define ST25R3916_REG_ISO14443B_2_tr1_80fs80fs (0U << 6) -#define ST25R3916_REG_ISO14443B_2_tr1_mask (3U << 6) -#define ST25R3916_REG_ISO14443B_2_tr1_shift (6U) -#define ST25R3916_REG_ISO14443B_2_no_sof (1U << 5) -#define ST25R3916_REG_ISO14443B_2_no_eof (1U << 4) -#define ST25R3916_REG_ISO14443B_rfu1 (1U << 3) -#define ST25R3916_REG_ISO14443B_rfu0 (1U << 2) -#define ST25R3916_REG_ISO14443B_2_f_p1 (1U << 1) -#define ST25R3916_REG_ISO14443B_2_f_p0 (1U << 0) -#define ST25R3916_REG_ISO14443B_2_f_p_96 (3U << 0) -#define ST25R3916_REG_ISO14443B_2_f_p_80 (2U << 0) -#define ST25R3916_REG_ISO14443B_2_f_p_64 (1U << 0) -#define ST25R3916_REG_ISO14443B_2_f_p_48 (0U << 0) -#define ST25R3916_REG_ISO14443B_2_f_p_mask (3U << 0) -#define ST25R3916_REG_ISO14443B_2_f_p_shift (0U) - -#define ST25R3916_REG_PASSIVE_TARGET_fdel_3 (1U << 7) -#define ST25R3916_REG_PASSIVE_TARGET_fdel_2 (1U << 6) -#define ST25R3916_REG_PASSIVE_TARGET_fdel_1 (1U << 5) -#define ST25R3916_REG_PASSIVE_TARGET_fdel_0 (1U << 4) -#define ST25R3916_REG_PASSIVE_TARGET_fdel_mask (0xfU << 4) -#define ST25R3916_REG_PASSIVE_TARGET_fdel_shift (4U) -#define ST25R3916_REG_PASSIVE_TARGET_d_ac_ap2p (1U << 3) -#define ST25R3916_REG_PASSIVE_TARGET_d_212_424_1r (1U << 2) -#define ST25R3916_REG_PASSIVE_TARGET_rfu (1U << 1) -#define ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a (1U << 0) - -#define ST25R3916_REG_STREAM_MODE_rfu (1U << 7) -#define ST25R3916_REG_STREAM_MODE_scf1 (1U << 6) -#define ST25R3916_REG_STREAM_MODE_scf0 (1U << 5) -#define ST25R3916_REG_STREAM_MODE_scf_sc212 (0U << 5) -#define ST25R3916_REG_STREAM_MODE_scf_sc424 (1U << 5) -#define ST25R3916_REG_STREAM_MODE_scf_sc848 (2U << 5) -#define ST25R3916_REG_STREAM_MODE_scf_sc1695 (3U << 5) -#define ST25R3916_REG_STREAM_MODE_scf_bpsk848 (0U << 5) -#define ST25R3916_REG_STREAM_MODE_scf_bpsk1695 (1U << 5) -#define ST25R3916_REG_STREAM_MODE_scf_bpsk3390 (2U << 5) -#define ST25R3916_REG_STREAM_MODE_scf_bpsk106 (3U << 5) -#define ST25R3916_REG_STREAM_MODE_scf_mask (3U << 5) -#define ST25R3916_REG_STREAM_MODE_scf_shift (5U) -#define ST25R3916_REG_STREAM_MODE_scp1 (1U << 4) -#define ST25R3916_REG_STREAM_MODE_scp0 (1U << 3) -#define ST25R3916_REG_STREAM_MODE_scp_1pulse (0U << 3) -#define ST25R3916_REG_STREAM_MODE_scp_2pulses (1U << 3) -#define ST25R3916_REG_STREAM_MODE_scp_4pulses (2U << 3) -#define ST25R3916_REG_STREAM_MODE_scp_8pulses (3U << 3) -#define ST25R3916_REG_STREAM_MODE_scp_mask (3U << 3) -#define ST25R3916_REG_STREAM_MODE_scp_shift (3U) -#define ST25R3916_REG_STREAM_MODE_stx2 (1U << 2) -#define ST25R3916_REG_STREAM_MODE_stx1 (1U << 1) -#define ST25R3916_REG_STREAM_MODE_stx0 (1U << 0) -#define ST25R3916_REG_STREAM_MODE_stx_106 (0U << 0) -#define ST25R3916_REG_STREAM_MODE_stx_212 (1U << 0) -#define ST25R3916_REG_STREAM_MODE_stx_424 (2U << 0) -#define ST25R3916_REG_STREAM_MODE_stx_848 (3U << 0) -#define ST25R3916_REG_STREAM_MODE_stx_mask (7U << 0) -#define ST25R3916_REG_STREAM_MODE_stx_shift (0U) - -#define ST25R3916_REG_AUX_no_crc_rx (1U << 7) -#define ST25R3916_REG_AUX_rfu (1U << 6) -#define ST25R3916_REG_AUX_nfc_id1 (1U << 5) -#define ST25R3916_REG_AUX_nfc_id0 (1U << 4) -#define ST25R3916_REG_AUX_nfc_id_7bytes (1U << 4) -#define ST25R3916_REG_AUX_nfc_id_4bytes (0U << 4) -#define ST25R3916_REG_AUX_nfc_id_mask (3U << 4) -#define ST25R3916_REG_AUX_nfc_id_shift (4U) -#define ST25R3916_REG_AUX_mfaz_cl90 (1U << 3) -#define ST25R3916_REG_AUX_dis_corr (1U << 2) -#define ST25R3916_REG_AUX_dis_corr_coherent (1U << 2) -#define ST25R3916_REG_AUX_dis_corr_correlator (0U << 2) -#define ST25R3916_REG_AUX_nfc_n1 (1U << 1) -#define ST25R3916_REG_AUX_nfc_n0 (1U << 0) -#define ST25R3916_REG_AUX_nfc_n_mask (3U << 0) -#define ST25R3916_REG_AUX_nfc_n_shift (0U) - -#define ST25R3916_REG_RX_CONF1_ch_sel (1U << 7) -#define ST25R3916_REG_RX_CONF1_ch_sel_PM (1U << 7) -#define ST25R3916_REG_RX_CONF1_ch_sel_AM (0U << 7) -#define ST25R3916_REG_RX_CONF1_lp2 (1U << 6) -#define ST25R3916_REG_RX_CONF1_lp1 (1U << 5) -#define ST25R3916_REG_RX_CONF1_lp0 (1U << 4) -#define ST25R3916_REG_RX_CONF1_lp_1200khz (0U << 4) -#define ST25R3916_REG_RX_CONF1_lp_600khz (1U << 4) -#define ST25R3916_REG_RX_CONF1_lp_300khz (2U << 4) -#define ST25R3916_REG_RX_CONF1_lp_2000khz (4U << 4) -#define ST25R3916_REG_RX_CONF1_lp_7000khz (5U << 4) -#define ST25R3916_REG_RX_CONF1_lp_mask (7U << 4) -#define ST25R3916_REG_RX_CONF1_lp_shift (4U) -#define ST25R3916_REG_RX_CONF1_z600k (1U << 3) -#define ST25R3916_REG_RX_CONF1_h200 (1U << 2) -#define ST25R3916_REG_RX_CONF1_h80 (1U << 1) -#define ST25R3916_REG_RX_CONF1_z12k (1U << 0) -#define ST25R3916_REG_RX_CONF1_hz_60_400khz (0U << 0) -#define ST25R3916_REG_RX_CONF1_hz_60_200khz (4U << 0) -#define ST25R3916_REG_RX_CONF1_hz_40_80khz (2U << 0) -#define ST25R3916_REG_RX_CONF1_hz_12_200khz (1U << 0) -#define ST25R3916_REG_RX_CONF1_hz_12_80khz (3U << 0) -#define ST25R3916_REG_RX_CONF1_hz_12_200khz_alt (5U << 0) -#define ST25R3916_REG_RX_CONF1_hz_600_400khz (8U << 0) -#define ST25R3916_REG_RX_CONF1_hz_600_200khz (12U << 0) -#define ST25R3916_REG_RX_CONF1_hz_mask (0xfU << 0) -#define ST25R3916_REG_RX_CONF1_hz_shift (0U) - -#define ST25R3916_REG_RX_CONF2_demod_mode (1U << 7) -#define ST25R3916_REG_RX_CONF2_amd_sel (1U << 6) -#define ST25R3916_REG_RX_CONF2_amd_sel_mixer (1U << 6) -#define ST25R3916_REG_RX_CONF2_amd_sel_peak (0U << 6) -#define ST25R3916_REG_RX_CONF2_sqm_dyn (1U << 5) -#define ST25R3916_REG_RX_CONF2_pulz_61 (1U << 4) -#define ST25R3916_REG_RX_CONF2_agc_en (1U << 3) -#define ST25R3916_REG_RX_CONF2_agc_m (1U << 2) -#define ST25R3916_REG_RX_CONF2_agc_alg (1U << 1) -#define ST25R3916_REG_RX_CONF2_agc6_3 (1U << 0) - -#define ST25R3916_REG_RX_CONF3_rg1_am2 (1U << 7) -#define ST25R3916_REG_RX_CONF3_rg1_am1 (1U << 6) -#define ST25R3916_REG_RX_CONF3_rg1_am0 (1U << 5) -#define ST25R3916_REG_RX_CONF3_rg1_am_mask (0x7U << 5) -#define ST25R3916_REG_RX_CONF3_rg1_am_shift (5U) -#define ST25R3916_REG_RX_CONF3_rg1_pm2 (1U << 4) -#define ST25R3916_REG_RX_CONF3_rg1_pm1 (1U << 3) -#define ST25R3916_REG_RX_CONF3_rg1_pm0 (1U << 2) -#define ST25R3916_REG_RX_CONF3_rg1_pm_mask (0x7U << 2) -#define ST25R3916_REG_RX_CONF3_rg1_pm_shift (2U) -#define ST25R3916_REG_RX_CONF3_lf_en (1U << 1) -#define ST25R3916_REG_RX_CONF3_lf_op (1U << 0) - -#define ST25R3916_REG_RX_CONF4_rg2_am3 (1U << 7) -#define ST25R3916_REG_RX_CONF4_rg2_am2 (1U << 6) -#define ST25R3916_REG_RX_CONF4_rg2_am1 (1U << 5) -#define ST25R3916_REG_RX_CONF4_rg2_am0 (1U << 4) -#define ST25R3916_REG_RX_CONF4_rg2_am_mask (0xfU << 4) -#define ST25R3916_REG_RX_CONF4_rg2_am_shift (4U) -#define ST25R3916_REG_RX_CONF4_rg2_pm3 (1U << 3) -#define ST25R3916_REG_RX_CONF4_rg2_pm2 (1U << 2) -#define ST25R3916_REG_RX_CONF4_rg2_pm1 (1U << 1) -#define ST25R3916_REG_RX_CONF4_rg2_pm0 (1U << 0) -#define ST25R3916_REG_RX_CONF4_rg2_pm_mask (0xfU << 0) -#define ST25R3916_REG_RX_CONF4_rg2_pm_shift (0U) - -#define ST25R3916_REG_P2P_RX_CONF_ook_fd (1U << 7) -#define ST25R3916_REG_P2P_RX_CONF_ook_rc1 (1U << 6) -#define ST25R3916_REG_P2P_RX_CONF_ook_rc0 (1U << 5) -#define ST25R3916_REG_P2P_RX_CONF_ook_thd1 (1U << 4) -#define ST25R3916_REG_P2P_RX_CONF_ook_thd0 (1U << 3) -#define ST25R3916_REG_P2P_RX_CONF_ask_rc1 (1U << 2) -#define ST25R3916_REG_P2P_RX_CONF_ask_rc0 (1U << 1) -#define ST25R3916_REG_P2P_RX_CONF_ask_thd (1U << 0) - -#define ST25R3916_REG_CORR_CONF1_corr_s7 (1U << 7) -#define ST25R3916_REG_CORR_CONF1_corr_s6 (1U << 6) -#define ST25R3916_REG_CORR_CONF1_corr_s5 (1U << 5) -#define ST25R3916_REG_CORR_CONF1_corr_s4 (1U << 4) -#define ST25R3916_REG_CORR_CONF1_corr_s3 (1U << 3) -#define ST25R3916_REG_CORR_CONF1_corr_s2 (1U << 2) -#define ST25R3916_REG_CORR_CONF1_corr_s1 (1U << 1) -#define ST25R3916_REG_CORR_CONF1_corr_s0 (1U << 0) - -#define ST25R3916_REG_CORR_CONF2_rfu5 (1U << 7) -#define ST25R3916_REG_CORR_CONF2_rfu4 (1U << 6) -#define ST25R3916_REG_CORR_CONF2_rfu3 (1U << 5) -#define ST25R3916_REG_CORR_CONF2_rfu2 (1U << 4) -#define ST25R3916_REG_CORR_CONF2_rfu1 (1U << 3) -#define ST25R3916_REG_CORR_CONF2_rfu0 (1U << 2) -#define ST25R3916_REG_CORR_CONF2_corr_s9 (1U << 1) -#define ST25R3916_REG_CORR_CONF2_corr_s8 (1U << 0) - -#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc2 (1U << 7) -#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc1 (1U << 6) -#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc0 (1U << 5) -#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_no_trigger (0U << 5) -#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_erx (1U << 5) -#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_srx (2U << 5) -#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_etx_nfc (3U << 5) -#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_mask (7U << 5) -#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_shift (5U) -#define ST25R3916_REG_TIMER_EMV_CONTROL_rfu (1U << 4) -#define ST25R3916_REG_TIMER_EMV_CONTROL_mrt_step (1U << 3) -#define ST25R3916_REG_TIMER_EMV_CONTROL_mrt_step_512 (1U << 3) -#define ST25R3916_REG_TIMER_EMV_CONTROL_mrt_step_64 (0U << 3) -#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_nfc (1U << 2) -#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_nfc_on (1U << 2) -#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_nfc_off (0U << 2) -#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv (1U << 1) -#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv_on (1U << 1) -#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv_off (0U << 1) -#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_step (1U << 0) -#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_step_64fc (0U << 0) -#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_step_4096_fc (1U << 0) - -#define ST25R3916_REG_FIFO_STATUS2_fifo_b9 (1U << 7) -#define ST25R3916_REG_FIFO_STATUS2_fifo_b8 (1U << 6) -#define ST25R3916_REG_FIFO_STATUS2_fifo_b_mask (3U << 6) -#define ST25R3916_REG_FIFO_STATUS2_fifo_b_shift (6U) -#define ST25R3916_REG_FIFO_STATUS2_fifo_unf (1U << 5) -#define ST25R3916_REG_FIFO_STATUS2_fifo_ovr (1U << 4) -#define ST25R3916_REG_FIFO_STATUS2_fifo_lb2 (1U << 3) -#define ST25R3916_REG_FIFO_STATUS2_fifo_lb1 (1U << 2) -#define ST25R3916_REG_FIFO_STATUS2_fifo_lb0 (1U << 1) -#define ST25R3916_REG_FIFO_STATUS2_fifo_lb_mask (7U << 1) -#define ST25R3916_REG_FIFO_STATUS2_fifo_lb_shift (1U) -#define ST25R3916_REG_FIFO_STATUS2_np_lb (1U << 0) - -#define ST25R3916_REG_COLLISION_STATUS_c_byte3 (1U << 7) -#define ST25R3916_REG_COLLISION_STATUS_c_byte2 (1U << 6) -#define ST25R3916_REG_COLLISION_STATUS_c_byte1 (1U << 5) -#define ST25R3916_REG_COLLISION_STATUS_c_byte0 (1U << 4) -#define ST25R3916_REG_COLLISION_STATUS_c_byte_mask (0xfU << 4) -#define ST25R3916_REG_COLLISION_STATUS_c_byte_shift (4U) -#define ST25R3916_REG_COLLISION_STATUS_c_bit2 (1U << 3) -#define ST25R3916_REG_COLLISION_STATUS_c_bit1 (1U << 2) -#define ST25R3916_REG_COLLISION_STATUS_c_bit0 (1U << 1) -#define ST25R3916_REG_COLLISION_STATUS_c_pb (1U << 0) -#define ST25R3916_REG_COLLISION_STATUS_c_bit_mask (3U << 1) -#define ST25R3916_REG_COLLISION_STATUS_c_bit_shift (1U) - -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_rfu (1U << 7) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_rfu1 (1U << 6) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_rfu2 (1U << 5) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_rfu3 (1U << 4) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state3 (1U << 3) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state2 (1U << 2) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state1 (1U << 1) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state0 (1U << 0) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_power_off (0x0U << 0) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_idle (0x1U << 0) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_ready_l1 (0x2U << 0) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_ready_l2 (0x3U << 0) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu4 (0x4U << 0) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_active (0x5U << 0) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu6 (0x6U << 0) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu7 (0x7U << 0) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu8 (0x8U << 0) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_halt (0x9U << 0) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_ready_l1_x (0xaU << 0) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_ready_l2_x (0xbU << 0) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu12 (0xcU << 0) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_active_x (0xdU << 0) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state_mask (0xfU << 0) -#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state_shift (0U) - -#define ST25R3916_REG_NUM_TX_BYTES2_ntx4 (1U << 7) -#define ST25R3916_REG_NUM_TX_BYTES2_ntx3 (1U << 6) -#define ST25R3916_REG_NUM_TX_BYTES2_ntx2 (1U << 5) -#define ST25R3916_REG_NUM_TX_BYTES2_ntx1 (1U << 4) -#define ST25R3916_REG_NUM_TX_BYTES2_ntx0 (1U << 3) -#define ST25R3916_REG_NUM_TX_BYTES2_ntx_mask (0x1fU << 3) -#define ST25R3916_REG_NUM_TX_BYTES2_ntx_shift (3U) -#define ST25R3916_REG_NUM_TX_BYTES2_nbtx2 (1U << 2) -#define ST25R3916_REG_NUM_TX_BYTES2_nbtx1 (1U << 1) -#define ST25R3916_REG_NUM_TX_BYTES2_nbtx0 (1U << 0) -#define ST25R3916_REG_NUM_TX_BYTES2_nbtx_mask (7U << 0) -#define ST25R3916_REG_NUM_TX_BYTES2_nbtx_shift (0U) - -#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rfu1 (1U << 7) -#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rfu0 (1U << 6) -#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rate1 (1U << 5) -#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rate0 (1U << 4) -#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rate_mask (0x3U << 4) -#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rate_shift (4U) -#define ST25R3916_REG_NFCIP1_BIT_RATE_ppt2_on (1U << 3) -#define ST25R3916_REG_NFCIP1_BIT_RATE_gpt_on (1U << 2) -#define ST25R3916_REG_NFCIP1_BIT_RATE_nrt_on (1U << 1) -#define ST25R3916_REG_NFCIP1_BIT_RATE_mrt_on (1U << 0) - -#define ST25R3916_REG_TX_DRIVER_am_mod3 (1U << 7) -#define ST25R3916_REG_TX_DRIVER_am_mod2 (1U << 6) -#define ST25R3916_REG_TX_DRIVER_am_mod1 (1U << 5) -#define ST25R3916_REG_TX_DRIVER_am_mod0 (1U << 4) -#define ST25R3916_REG_TX_DRIVER_am_mod_5percent (0x0U << 4) -#define ST25R3916_REG_TX_DRIVER_am_mod_6percent (0x1U << 4) -#define ST25R3916_REG_TX_DRIVER_am_mod_7percent (0x2U << 4) -#define ST25R3916_REG_TX_DRIVER_am_mod_8percent (0x3U << 4) -#define ST25R3916_REG_TX_DRIVER_am_mod_9percent (0x4U << 4) -#define ST25R3916_REG_TX_DRIVER_am_mod_10percent (0x5U << 4) -#define ST25R3916_REG_TX_DRIVER_am_mod_11percent (0x6U << 4) -#define ST25R3916_REG_TX_DRIVER_am_mod_12percent (0x7U << 4) -#define ST25R3916_REG_TX_DRIVER_am_mod_13percent (0x8U << 4) -#define ST25R3916_REG_TX_DRIVER_am_mod_14percent (0x9U << 4) -#define ST25R3916_REG_TX_DRIVER_am_mod_15percent (0xaU << 4) -#define ST25R3916_REG_TX_DRIVER_am_mod_17percent (0xbU << 4) -#define ST25R3916_REG_TX_DRIVER_am_mod_19percent (0xcU << 4) -#define ST25R3916_REG_TX_DRIVER_am_mod_22percent (0xdU << 4) -#define ST25R3916_REG_TX_DRIVER_am_mod_26percent (0xeU << 4) -#define ST25R3916_REG_TX_DRIVER_am_mod_40percent (0xfU << 4) -#define ST25R3916_REG_TX_DRIVER_am_mod_mask (0xfU << 4) -#define ST25R3916_REG_TX_DRIVER_am_mod_shift (4U) -#define ST25R3916_REG_TX_DRIVER_d_res3 (1U << 3) -#define ST25R3916_REG_TX_DRIVER_d_res2 (1U << 2) -#define ST25R3916_REG_TX_DRIVER_d_res1 (1U << 1) -#define ST25R3916_REG_TX_DRIVER_d_res0 (1U << 0) -#define ST25R3916_REG_TX_DRIVER_d_res_mask (0xfU << 0) -#define ST25R3916_REG_TX_DRIVER_d_res_shift (0U) - -#define ST25R3916_REG_PT_MOD_ptm_res3 (1U << 7) -#define ST25R3916_REG_PT_MOD_ptm_res2 (1U << 6) -#define ST25R3916_REG_PT_MOD_ptm_res1 (1U << 5) -#define ST25R3916_REG_PT_MOD_ptm_res0 (1U << 4) -#define ST25R3916_REG_PT_MOD_ptm_res_mask (0xfU << 4) -#define ST25R3916_REG_PT_MOD_ptm_res_shift (4U) -#define ST25R3916_REG_PT_MOD_pt_res3 (1U << 3) -#define ST25R3916_REG_PT_MOD_pt_res2 (1U << 2) -#define ST25R3916_REG_PT_MOD_pt_res1 (1U << 1) -#define ST25R3916_REG_PT_MOD_pt_res0 (1U << 0) -#define ST25R3916_REG_PT_MOD_pt_res_mask (0xfU << 0) -#define ST25R3916_REG_PT_MOD_pt_res_shift (0U) - -#define ST25R3916_REG_AUX_MOD_dis_reg_am (1U << 7) -#define ST25R3916_REG_AUX_MOD_lm_ext_pol (1U << 6) -#define ST25R3916_REG_AUX_MOD_lm_ext (1U << 5) -#define ST25R3916_REG_AUX_MOD_lm_dri (1U << 4) -#define ST25R3916_REG_AUX_MOD_res_am (1U << 3) -#define ST25R3916_REG_AUX_MOD_rfu2 (1U << 2) -#define ST25R3916_REG_AUX_MOD_rfu1 (1U << 1) -#define ST25R3916_REG_AUX_MOD_rfu0 (1U << 0) - -#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_t3 (1U << 7) -#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_t2 (1U << 6) -#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_t1 (1U << 5) -#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_t0 (1U << 4) -#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_mask (0xfU << 4) -#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_shift (4U) -#define ST25R3916_REG_TX_DRIVER_TIMING_rfu (1U << 3) -#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m2 (1U << 2) -#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m1 (1U << 1) -#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m0 (1U << 0) -#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m_mask (0x7U << 0) -#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m_shift (0U) - -#define ST25R3916_REG_RES_AM_MOD_fa3_f (1U << 7) -#define ST25R3916_REG_RES_AM_MOD_md_res6 (1U << 6) -#define ST25R3916_REG_RES_AM_MOD_md_res5 (1U << 5) -#define ST25R3916_REG_RES_AM_MOD_md_res4 (1U << 4) -#define ST25R3916_REG_RES_AM_MOD_md_res3 (1U << 3) -#define ST25R3916_REG_RES_AM_MOD_md_res2 (1U << 2) -#define ST25R3916_REG_RES_AM_MOD_md_res1 (1U << 1) -#define ST25R3916_REG_RES_AM_MOD_md_res0 (1U << 0) -#define ST25R3916_REG_RES_AM_MOD_md_res_mask (0x7FU << 0) -#define ST25R3916_REG_RES_AM_MOD_md_res_shift (0U) - -#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_r3 (1U << 7) -#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_r2 (1U << 6) -#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_r1 (1U << 5) -#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_r0 (1U << 4) -#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_mask (0xfU << 4) -#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_shift (4U) -#define ST25R3916_REG_TX_DRIVER_STATUS_rfu (1U << 3) -#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_r2 (1U << 2) -#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_r1 (1U << 1) -#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_r0 (1U << 0) -#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_mask (0x7U << 0) -#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_shift (0U) - -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_l2a (1U << 6) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_l1a (1U << 5) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_l0a (1U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_75mV (0x0U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_105mV (0x1U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_150mV (0x2U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_205mV (0x3U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_290mV (0x4U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_400mV (0x5U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_560mV (0x6U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_800mV (0x7U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_mask (7U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_shift (4U) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_t3a (1U << 3) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_t2a (1U << 2) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_t1a (1U << 1) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_t0a (1U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_75mV (0x0U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_105mV (0x1U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_150mV (0x2U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_205mV (0x3U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_290mV (0x4U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_400mV (0x5U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_560mV (0x6U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_800mV (0x7U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_25mV (0x8U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_33mV (0x9U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_47mV (0xAU << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_64mV (0xBU << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_90mV (0xCU << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_125mV (0xDU << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_175mV (0xEU << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_250mV (0xFU << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_mask (0xfU << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_shift (0U) - -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_l2d (1U << 6) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_l1d (1U << 5) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_l0d (1U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_75mV (0x0U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_105mV (0x1U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_150mV (0x2U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_205mV (0x3U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_290mV (0x4U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_400mV (0x5U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_560mV (0x6U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_800mV (0x7U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_mask (7U << 4) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_shift (4U) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_t3d (1U << 3) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_t2d (1U << 2) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_t1d (1U << 1) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_t0d (1U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_75mV (0x0U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_105mV (0x1U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_150mV (0x2U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_205mV (0x3U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_290mV (0x4U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_400mV (0x5U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_560mV (0x6U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_800mV (0x7U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_25mV (0x8U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_33mV (0x9U << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_47mV (0xAU << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_64mV (0xBU << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_90mV (0xCU << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_125mV (0xDU << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_175mV (0xEU << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_250mV (0xFU << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_mask (0xfU << 0) -#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_shift (0U) - -#define ST25R3916_REG_REGULATOR_CONTROL_reg_s (1U << 7) -#define ST25R3916_REG_REGULATOR_CONTROL_rege_3 (1U << 6) -#define ST25R3916_REG_REGULATOR_CONTROL_rege_2 (1U << 5) -#define ST25R3916_REG_REGULATOR_CONTROL_rege_1 (1U << 4) -#define ST25R3916_REG_REGULATOR_CONTROL_rege_0 (1U << 3) -#define ST25R3916_REG_REGULATOR_CONTROL_rege_mask (0xfU << 3) -#define ST25R3916_REG_REGULATOR_CONTROL_rege_shift (3U) -#define ST25R3916_REG_REGULATOR_CONTROL_mpsv2 (2U << 2) -#define ST25R3916_REG_REGULATOR_CONTROL_mpsv1 (1U << 1) -#define ST25R3916_REG_REGULATOR_CONTROL_mpsv0 (1U << 0) -#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd (0U) -#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_a (1U) -#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_d (2U) -#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_rf (3U) -#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_am (4U) -#define ST25R3916_REG_REGULATOR_CONTROL_rfu (5U) -#define ST25R3916_REG_REGULATOR_CONTROL_rfu1 (6U) -#define ST25R3916_REG_REGULATOR_CONTROL_rfu2 (7U) -#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_mask (7U) -#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_shift (0U) - -#define ST25R3916_REG_REGULATOR_RESULT_reg_3 (1U << 7) -#define ST25R3916_REG_REGULATOR_RESULT_reg_2 (1U << 6) -#define ST25R3916_REG_REGULATOR_RESULT_reg_1 (1U << 5) -#define ST25R3916_REG_REGULATOR_RESULT_reg_0 (1U << 4) -#define ST25R3916_REG_REGULATOR_RESULT_reg_mask (0xfU << 4) -#define ST25R3916_REG_REGULATOR_RESULT_reg_shift (4U) -#define ST25R3916_REG_REGULATOR_RESULT_i_lim (1U << 0) - -#define ST25R3916_REG_RSSI_RESULT_rssi_am_3 (1U << 7) -#define ST25R3916_REG_RSSI_RESULT_rssi_am_2 (1U << 6) -#define ST25R3916_REG_RSSI_RESULT_rssi_am_1 (1U << 5) -#define ST25R3916_REG_RSSI_RESULT_rssi_am_0 (1U << 4) -#define ST25R3916_REG_RSSI_RESULT_rssi_am_mask (0xfU << 4) -#define ST25R3916_REG_RSSI_RESULT_rssi_am_shift (4U) -#define ST25R3916_REG_RSSI_RESULT_rssi_pm3 (1U << 3) -#define ST25R3916_REG_RSSI_RESULT_rssi_pm2 (1U << 2) -#define ST25R3916_REG_RSSI_RESULT_rssi_pm1 (1U << 1) -#define ST25R3916_REG_RSSI_RESULT_rssi_pm0 (1U << 0) -#define ST25R3916_REG_RSSI_RESULT_rssi_pm_mask (0xfU << 0) -#define ST25R3916_REG_RSSI_RESULT_rssi_pm_shift (0U) - -#define ST25R3916_REG_GAIN_RED_STATE_gs_am_3 (1U << 7) -#define ST25R3916_REG_GAIN_RED_STATE_gs_am_2 (1U << 6) -#define ST25R3916_REG_GAIN_RED_STATE_gs_am_1 (1U << 5) -#define ST25R3916_REG_GAIN_RED_STATE_gs_am_0 (1U << 4) -#define ST25R3916_REG_GAIN_RED_STATE_gs_am_mask (0xfU << 4) -#define ST25R3916_REG_GAIN_RED_STATE_gs_am_shift (4U) -#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_3 (1U << 3) -#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_2 (1U << 2) -#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_1 (1U << 1) -#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_0 (1U << 0) -#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_mask (0xfU << 0) -#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_shift (0U) - -#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal4 (1U << 7) -#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal3 (1U << 6) -#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal2 (1U << 5) -#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal1 (1U << 4) -#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal0 (1U << 3) -#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal_mask (0x1fU << 3) -#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal_shift (3U) -#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g2 (1U << 2) -#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g1 (1U << 1) -#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g0 (1U << 0) -#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g_mask (7U << 0) -#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g_shift (0U) - -#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal4 (1U << 7) -#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal3 (1U << 6) -#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal2 (1U << 5) -#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal1 (1U << 4) -#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal0 (1U << 3) -#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_mask (0x1fU << 3) -#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_shift (3U) -#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_end (1U << 2) -#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_err (1U << 1) - -#define ST25R3916_REG_AUX_DISPLAY_a_cha (1U << 7) -#define ST25R3916_REG_AUX_DISPLAY_efd_o (1U << 6) -#define ST25R3916_REG_AUX_DISPLAY_tx_on (1U << 5) -#define ST25R3916_REG_AUX_DISPLAY_osc_ok (1U << 4) -#define ST25R3916_REG_AUX_DISPLAY_rx_on (1U << 3) -#define ST25R3916_REG_AUX_DISPLAY_rx_act (1U << 2) -#define ST25R3916_REG_AUX_DISPLAY_en_peer (1U << 1) -#define ST25R3916_REG_AUX_DISPLAY_en_ac (1U << 0) - -#define ST25R3916_REG_OVERSHOOT_CONF1_ov_tx_mode1 (1U << 7) -#define ST25R3916_REG_OVERSHOOT_CONF1_ov_tx_mode0 (1U << 6) -#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern13 (1U << 5) -#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern12 (1U << 4) -#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern11 (1U << 3) -#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern10 (1U << 2) -#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern9 (1U << 1) -#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern8 (1U << 0) - -#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern7 (1U << 7) -#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern6 (1U << 6) -#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern5 (1U << 5) -#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern4 (1U << 4) -#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern3 (1U << 3) -#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern2 (1U << 2) -#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern1 (1U << 1) -#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern0 (1U << 0) - -#define ST25R3916_REG_UNDERSHOOT_CONF1_un_tx_mode1 (1U << 7) -#define ST25R3916_REG_UNDERSHOOT_CONF1_un_tx_mode0 (1U << 6) -#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern13 (1U << 5) -#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern12 (1U << 4) -#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern11 (1U << 3) -#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern10 (1U << 2) -#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern9 (1U << 1) -#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern8 (1U << 0) - -#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern7 (1U << 7) -#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern6 (1U << 6) -#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern5 (1U << 5) -#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern4 (1U << 4) -#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern3 (1U << 3) -#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern2 (1U << 2) -#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern1 (1U << 1) -#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern0 (1U << 0) - -#define ST25R3916_REG_WUP_TIMER_CONTROL_wur (1U << 7) -#define ST25R3916_REG_WUP_TIMER_CONTROL_wut2 (1U << 6) -#define ST25R3916_REG_WUP_TIMER_CONTROL_wut1 (1U << 5) -#define ST25R3916_REG_WUP_TIMER_CONTROL_wut0 (1U << 4) -#define ST25R3916_REG_WUP_TIMER_CONTROL_wut_mask (7U << 4) -#define ST25R3916_REG_WUP_TIMER_CONTROL_wut_shift (4U) -#define ST25R3916_REG_WUP_TIMER_CONTROL_wto (1U << 3) -#define ST25R3916_REG_WUP_TIMER_CONTROL_wam (1U << 2) -#define ST25R3916_REG_WUP_TIMER_CONTROL_wph (1U << 1) -#define ST25R3916_REG_WUP_TIMER_CONTROL_wcap (1U << 0) - -#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d3 (1U << 7) -#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d2 (1U << 6) -#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d1 (1U << 5) -#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d0 (1U << 4) -#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d_mask (0xfU << 4) -#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d_shift (4U) -#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aam (1U << 3) -#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aew1 (1U << 2) -#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aew0 (1U << 1) -#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aew_mask (0x3U << 1) -#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aew_shift (1U) -#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_ae (1U << 0) - -#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d3 (1U << 7) -#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d2 (1U << 6) -#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d1 (1U << 5) -#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d0 (1U << 4) -#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d_mask (0xfU << 4) -#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d_shift (4U) -#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aam (1U << 3) -#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aew1 (1U << 2) -#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aew0 (1U << 1) -#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aew_mask (0x3U << 1) -#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aew_shift (1U) -#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_ae (1U << 0) - -#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d3 (1U << 7) -#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d2 (1U << 6) -#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d1 (1U << 5) -#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d0 (1U << 4) -#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d_mask (0xfU << 4) -#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d_shift (4U) -#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aam (1U << 3) -#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aew1 (1U << 2) -#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aew0 (1U << 1) -#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aew_mask (0x3U << 1) -#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aew_shift (1U) -#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_ae (1U << 0) - -#define ST25R3916_REG_IC_IDENTITY_ic_type4 (1U << 7) -#define ST25R3916_REG_IC_IDENTITY_ic_type3 (1U << 6) -#define ST25R3916_REG_IC_IDENTITY_ic_type2 (1U << 5) -#define ST25R3916_REG_IC_IDENTITY_ic_type1 (1U << 4) -#define ST25R3916_REG_IC_IDENTITY_ic_type0 (1U << 3) -#define ST25R3916_REG_IC_IDENTITY_ic_type_st25r3916 (5U << 3) -#define ST25R3916_REG_IC_IDENTITY_ic_type_mask (0x1fU << 3) -#define ST25R3916_REG_IC_IDENTITY_ic_type_shift (3U) -#define ST25R3916_REG_IC_IDENTITY_ic_rev2 (1U << 2) -#define ST25R3916_REG_IC_IDENTITY_ic_rev1 (1U << 1) -#define ST25R3916_REG_IC_IDENTITY_ic_rev0 (1U << 0) -#define ST25R3916_REG_IC_IDENTITY_ic_rev_v0 (0U << 0) -#define ST25R3916_REG_IC_IDENTITY_ic_rev_mask (7U << 0) -#define ST25R3916_REG_IC_IDENTITY_ic_rev_shift (0U) - -/*! \endcond DOXYGEN_SUPRESS */ - -/* -****************************************************************************** -* GLOBAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -/*! - ***************************************************************************** - * \brief Returns the content of a register within the ST25R3916 - * - * This function is used to read out the content of ST25R3916 registers. - * - * \param[in] reg: Address of register to read. - * \param[out] val: Returned value. - * - * \return ERR_NONE : Operation successful - * \return ERR_PARAM : Invalid parameter - * \return ERR_SEND : Transmission error or acknowledge not received - ***************************************************************************** - */ -ReturnCode st25r3916ReadRegister(uint8_t reg, uint8_t* val); - -/*! - ***************************************************************************** - * \brief Reads from multiple ST25R3916 registers - * - * This function is used to read from multiple registers using the - * auto-increment feature. That is, after each read the address pointer - * inside the ST25R3916 gets incremented automatically. - * - * \param[in] reg: Address of the frist register to read from. - * \param[in] values: pointer to a buffer where the result shall be written to. - * \param[in] length: Number of registers to be read out. - * - * \return ERR_NONE : Operation successful - * \return ERR_PARAM : Invalid parameter - * \return ERR_SEND : Transmission error or acknowledge not received - ***************************************************************************** - */ -ReturnCode st25r3916ReadMultipleRegisters(uint8_t reg, uint8_t* values, uint8_t length); - -/*! - ***************************************************************************** - * \brief Writes a given value to a register within the ST25R3916 - * - * This function is used to write \a val to address \a reg within the ST25R3916. - * - * \param[in] reg: Address of the register to write. - * \param[in] val: Value to be written. - * - * \return ERR_NONE : Operation successful - * \return ERR_PARAM : Invalid parameter - * \return ERR_SEND : Transmission error or acknowledge not received - ***************************************************************************** - */ -ReturnCode st25r3916WriteRegister(uint8_t reg, uint8_t val); - -/*! - ***************************************************************************** - * \brief Writes multiple values to ST25R3916 registers - * - * This function is used to write multiple values to the ST25R3916 using the - * auto-increment feature. That is, after each write the address pointer - * inside the ST25R3916 gets incremented automatically. - * - * \param[in] reg: Address of the frist register to write. - * \param[in] values: pointer to a buffer containing the values to be written. - * \param[in] length: Number of values to be written. - * - * \return ERR_NONE : Operation successful - * \return ERR_PARAM : Invalid parameter - * \return ERR_SEND : Transmission error or acknowledge not received - ***************************************************************************** - */ -ReturnCode st25r3916WriteMultipleRegisters(uint8_t reg, const uint8_t* values, uint8_t length); - -/*! - ***************************************************************************** - * \brief Writes values to ST25R3916 FIFO - * - * This function needs to be called in order to write to the ST25R3916 FIFO. - * - * \param[in] values: pointer to a buffer containing the values to be written - * to the FIFO. - * \param[in] length: Number of values to be written. - * - * \return ERR_NONE : Operation successful - * \return ERR_PARAM : Invalid parameter - * \return ERR_SEND : Transmission error or acknowledge not received - ***************************************************************************** - */ -ReturnCode st25r3916WriteFifo(const uint8_t* values, uint16_t length); - -/*! - ***************************************************************************** - * \brief Read values from ST25R3916 FIFO - * - * This function needs to be called in order to read from ST25R3916 FIFO. - * - * \param[out] buf: pointer to a buffer where the FIFO content shall be - * written to. - * \param[in] length: Number of bytes to read. - * - * \note: This function doesn't check whether \a length is really the - * number of available bytes in FIFO - * - * \return ERR_NONE : Operation successful - * \return ERR_PARAM : Invalid parameter - * \return ERR_SEND : Transmission error or acknowledge not received - ***************************************************************************** - */ -ReturnCode st25r3916ReadFifo(uint8_t* buf, uint16_t length); - -/*! - ***************************************************************************** - * \brief Writes values to ST25R3916 PTM - * - * Accesses to the begging of ST25R3916 Passive Target Memory (PTM A Config) - * and writes the given values - * - * \param[in] values: pointer to a buffer containing the values to be written - * to the Passive Target Memory. - * \param[in] length: Number of values to be written. - * - * \return ERR_NONE : Operation successful - * \return ERR_PARAM : Invalid parameter - * \return ERR_SEND : Transmission error or acknowledge not received - ***************************************************************************** - */ -ReturnCode st25r3916WritePTMem(const uint8_t* values, uint16_t length); - -/*! - ***************************************************************************** - * \brief Reads the ST25R3916 PTM - * - * Accesses to the begging of ST25R3916 Passive Target Memory (PTM A Config) - * and reads the memory for the given length - * - * \param[out] values: pointer to a buffer where the PTM content shall be - * written to. - * \param[in] length: Number of bytes to read. - * - * \return ERR_NONE : Operation successful - * \return ERR_PARAM : Invalid parameter - * \return ERR_SEND : Transmission error or acknowledge not received - ***************************************************************************** - */ -ReturnCode st25r3916ReadPTMem(uint8_t* values, uint16_t length); - -/*! - ***************************************************************************** - * \brief Writes values to ST25R3916 PTM F config - * - * Accesses ST25R3916 Passive Target Memory F config and writes the given values - * - * \param[in] values: pointer to a buffer containing the values to be written - * to the Passive Target Memory - * \param[in] length: Number of values to be written. - * - * \return ERR_NONE : Operation successful - * \return ERR_PARAM : Invalid parameter - * \return ERR_SEND : Transmission error or acknowledge not received - ***************************************************************************** - */ -ReturnCode st25r3916WritePTMemF(const uint8_t* values, uint16_t length); - -/*! - ***************************************************************************** - * \brief Writes values to ST25R3916 PTM TSN Data - * - * Accesses ST25R3916 Passive Target Memory TSN data and writes the given values - * - * \param[in] values: pointer to a buffer containing the values to be written - * to the Passive Target Memory. - * \param[in] length: Number of values to be written. - * - * \return ERR_NONE : Operation successful - * \return ERR_PARAM : Invalid parameter - * \return ERR_SEND : Transmission error or acknowledge not received - ***************************************************************************** - */ -ReturnCode st25r3916WritePTMemTSN(const uint8_t* values, uint16_t length); - -/*! - ***************************************************************************** - * \brief Execute a direct command - * - * This function is used to start so-called direct command. These commands - * are implemented inside the chip and each command has unique code (see - * datasheet). - * - * \param[in] cmd : code of the direct command to be executed. - * - * \return ERR_NONE : Operation successful - * \return ERR_PARAM : Invalid parameter - * \return ERR_SEND : Transmission error or acknowledge not received - ***************************************************************************** - */ -ReturnCode st25r3916ExecuteCommand(uint8_t cmd); - -/*! - ***************************************************************************** - * \brief Read a test register within the ST25R3916 - * - * This function is used to read the content of test address \a reg within the ST25R3916 - * - * \param[in] reg: Address of the register to read - * \param[out] val: Returned read value - * - * \return ERR_NONE : Operation successful - * \return ERR_PARAM : Invalid parameter - * \return ERR_SEND : Transmission error or acknowledge not received - ***************************************************************************** - */ -ReturnCode st25r3916ReadTestRegister(uint8_t reg, uint8_t* val); - -/*! - ***************************************************************************** - * \brief Writes a given value to a test register within the ST25R3916 - * - * This function is used to write \a val to test address \a reg within the ST25R3916 - * - * \param[in] reg: Address of the register to write - * \param[in] val: Value to be written - * - * \return ERR_NONE : Operation successful - * \return ERR_PARAM : Invalid parameter - * \return ERR_SEND : Transmission error or acknowledge not received - ***************************************************************************** - */ -ReturnCode st25r3916WriteTestRegister(uint8_t reg, uint8_t val); - -/*! - ***************************************************************************** - * \brief Cleart bits on Register - * - * This function clears the given bitmask on the register - * - * \param[in] reg: Address of the register clear - * \param[in] clr_mask: Bitmask of bit to be cleared - * - * \return ERR_NONE : Operation successful - * \return ERR_PARAM : Invalid parameter - * \return ERR_SEND : Transmission error or acknowledge not received - ***************************************************************************** - */ -ReturnCode st25r3916ClrRegisterBits(uint8_t reg, uint8_t clr_mask); - -/*! - ***************************************************************************** - * \brief Set bits on Register - * - * This function sets the given bitmask on the register - * - * \param[in] reg: Address of the register clear - * \param[in] set_mask: Bitmask of bit to be cleared - * - * \return ERR_NONE : Operation successful - * \return ERR_PARAM : Invalid parameter - * \return ERR_SEND : Transmission error or acknowledge not received - ***************************************************************************** - */ -ReturnCode st25r3916SetRegisterBits(uint8_t reg, uint8_t set_mask); - -/*! - ***************************************************************************** - * \brief Changes the given bits on a ST25R3916 register - * - * This function is used if only a particular bits should be changed within - * an ST25R3916 register. - * - * \param[in] reg: Address of the register to change. - * \param[in] valueMask: bitmask of bits to be changed - * \param[in] value: the bits to be written on the enabled valueMask bits - * - * \return ERR_NONE : Operation successful - * \return ERR_PARAM : Invalid parameter - * \return ERR_SEND : Transmission error or acknowledge not received - ***************************************************************************** - */ -ReturnCode st25r3916ChangeRegisterBits(uint8_t reg, uint8_t valueMask, uint8_t value); - -/*! - ***************************************************************************** - * \brief Modifies a value within a ST25R3916 register - * - * This function is used if only a particular bits should be changed within - * an ST25R3916 register. - * - * \param[in] reg: Address of the register to write. - * \param[in] clr_mask: bitmask of bits to be cleared to 0. - * \param[in] set_mask: bitmask of bits to be set to 1. - * - * \return ERR_NONE : Operation successful - * \return ERR_PARAM : Invalid parameter - * \return ERR_SEND : Transmission error or acknowledge not received - ***************************************************************************** - */ -ReturnCode st25r3916ModifyRegister(uint8_t reg, uint8_t clr_mask, uint8_t set_mask); - -/*! - ***************************************************************************** - * \brief Changes the given bits on a ST25R3916 Test register - * - * This function is used if only a particular bits should be changed within - * an ST25R3916 register. - * - * \param[in] reg: Address of the Test register to change. - * \param[in] valueMask: bitmask of bits to be changed - * \param[in] value: the bits to be written on the enabled valueMask bits - * - * \return ERR_NONE : Operation successful - * \return ERR_PARAM : Invalid parameter - * \return ERR_SEND : Transmission error or acknowledge not received - ***************************************************************************** - */ -ReturnCode st25r3916ChangeTestRegisterBits(uint8_t reg, uint8_t valueMask, uint8_t value); - -/*! - ***************************************************************************** - * \brief Checks if register contains a expected value - * - * This function checks if the given reg contains a value that once masked - * equals the expected value - * - * \param reg : the register to check the value - * \param mask : the mask apply on register value - * \param val : expected value to be compared to - * - * \return true when reg contains the expected value | false otherwise - */ -bool st25r3916CheckReg(uint8_t reg, uint8_t mask, uint8_t val); - -/*! - ***************************************************************************** - * \brief Check if register ID is valid - * - * Checks if the given register ID a valid ST25R3916 register - * - * \param[in] reg: Address of register to check - * - * \return true if is a valid register ID - * \return false otherwise - * - ***************************************************************************** - */ -bool st25r3916IsRegValid(uint8_t reg); - -#endif /* ST25R3916_COM_H */ - -/** - * @} - * - * @} - * - * @} - * - * @} - */ diff --git a/lib/ST25RFAL002/source/st25r3916/st25r3916_irq.c b/lib/ST25RFAL002/source/st25r3916/st25r3916_irq.c deleted file mode 100644 index 74c2797ce4d..00000000000 --- a/lib/ST25RFAL002/source/st25r3916/st25r3916_irq.c +++ /dev/null @@ -1,231 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R3916 firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file - * - * \author Gustavo Patricio - * - * \brief ST25R3916 Interrupt handling - * - */ - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ - -#include "st25r3916_irq.h" -#include "st25r3916_com.h" -#include "st25r3916_led.h" -#include "st25r3916.h" -#include "utils.h" - -/* - ****************************************************************************** - * LOCAL DATA TYPES - ****************************************************************************** - */ - -/*! Holds current and previous interrupt callback pointer as well as current Interrupt status and mask */ -typedef struct { - void (*prevCallback)(void); /*!< call back function for ST25R3916 interrupt */ - void (*callback)(void); /*!< call back function for ST25R3916 interrupt */ - uint32_t status; /*!< latest interrupt status */ - uint32_t mask; /*!< Interrupt mask. Negative mask = ST25R3916 mask regs */ -} st25r3916Interrupt; - -/* -****************************************************************************** -* GLOBAL DEFINES -****************************************************************************** -*/ - -/*! Length of the interrupt registers */ -#define ST25R3916_INT_REGS_LEN ((ST25R3916_REG_IRQ_TARGET - ST25R3916_REG_IRQ_MAIN) + 1U) - -/* -****************************************************************************** -* GLOBAL VARIABLES -****************************************************************************** -*/ - -static volatile st25r3916Interrupt st25r3916interrupt; /*!< Instance of ST25R3916 interrupt */ - -/* -****************************************************************************** -* GLOBAL FUNCTIONS -****************************************************************************** -*/ -void st25r3916InitInterrupts(void) { - platformIrqST25RPinInitialize(); - platformIrqST25RSetCallback(st25r3916Isr); - - st25r3916interrupt.callback = NULL; - st25r3916interrupt.prevCallback = NULL; - st25r3916interrupt.status = ST25R3916_IRQ_MASK_NONE; - st25r3916interrupt.mask = ST25R3916_IRQ_MASK_NONE; -} - -/*******************************************************************************/ -void st25r3916Isr(void) { - st25r3916CheckForReceivedInterrupts(); - - // Check if callback is set and run it - if(NULL != st25r3916interrupt.callback) { - st25r3916interrupt.callback(); - } -} - -/*******************************************************************************/ -void st25r3916CheckForReceivedInterrupts(void) { - uint8_t iregs[ST25R3916_INT_REGS_LEN]; - uint32_t irqStatus; - - /* Initialize iregs */ - irqStatus = ST25R3916_IRQ_MASK_NONE; - ST_MEMSET(iregs, (int32_t)(ST25R3916_IRQ_MASK_ALL & 0xFFU), ST25R3916_INT_REGS_LEN); - - /* In case the IRQ is Edge (not Level) triggered read IRQs until done */ - while(platformGpioIsHigh(ST25R_INT_PORT, ST25R_INT_PIN)) { - st25r3916ReadMultipleRegisters(ST25R3916_REG_IRQ_MAIN, iregs, ST25R3916_INT_REGS_LEN); - - irqStatus |= (uint32_t)iregs[0]; - irqStatus |= (uint32_t)iregs[1] << 8; - irqStatus |= (uint32_t)iregs[2] << 16; - irqStatus |= (uint32_t)iregs[3] << 24; - } - - /* Forward all interrupts, even masked ones to application */ - platformProtectST25RIrqStatus(); - st25r3916interrupt.status |= irqStatus; - platformUnprotectST25RIrqStatus(); - - /* Send an IRQ event to LED handling */ - st25r3916ledEvtIrq(st25r3916interrupt.status); -} - -/*******************************************************************************/ -void st25r3916ModifyInterrupts(uint32_t clr_mask, uint32_t set_mask) { - uint8_t i; - uint32_t old_mask; - uint32_t new_mask; - - old_mask = st25r3916interrupt.mask; - new_mask = ((~old_mask & set_mask) | (old_mask & clr_mask)); - st25r3916interrupt.mask &= ~clr_mask; - st25r3916interrupt.mask |= set_mask; - - for(i = 0; i < ST25R3916_INT_REGS_LEN; i++) { - if(((new_mask >> (8U * i)) & 0xFFU) == 0U) { - continue; - } - - st25r3916WriteRegister( - ST25R3916_REG_IRQ_MASK_MAIN + i, - (uint8_t)((st25r3916interrupt.mask >> (8U * i)) & 0xFFU)); - } - return; -} - -/*******************************************************************************/ -uint32_t st25r3916WaitForInterruptsTimed(uint32_t mask, uint16_t tmo) { - uint32_t tmrDelay; - uint32_t status; - - tmrDelay = platformTimerCreate(tmo); - - /* Run until specific interrupt has happen or the timer has expired */ - do { - status = (st25r3916interrupt.status & mask); - } while((!platformTimerIsExpired(tmrDelay) || (tmo == 0U)) && (status == 0U)); - - platformTimerDestroy(tmrDelay); - - status = st25r3916interrupt.status & mask; - - platformProtectST25RIrqStatus(); - st25r3916interrupt.status &= ~status; - platformUnprotectST25RIrqStatus(); - - return status; -} - -/*******************************************************************************/ -uint32_t st25r3916GetInterrupt(uint32_t mask) { - uint32_t irqs; - - irqs = (st25r3916interrupt.status & mask); - if(irqs != ST25R3916_IRQ_MASK_NONE) { - platformProtectST25RIrqStatus(); - st25r3916interrupt.status &= ~irqs; - platformUnprotectST25RIrqStatus(); - } - - return irqs; -} - -/*******************************************************************************/ -void st25r3916ClearAndEnableInterrupts(uint32_t mask) { - st25r3916GetInterrupt(mask); - st25r3916EnableInterrupts(mask); -} - -/*******************************************************************************/ -void st25r3916EnableInterrupts(uint32_t mask) { - st25r3916ModifyInterrupts(mask, 0); -} - -/*******************************************************************************/ -void st25r3916DisableInterrupts(uint32_t mask) { - st25r3916ModifyInterrupts(0, mask); -} - -/*******************************************************************************/ -void st25r3916ClearInterrupts(void) { - uint8_t iregs[ST25R3916_INT_REGS_LEN]; - - st25r3916ReadMultipleRegisters(ST25R3916_REG_IRQ_MAIN, iregs, ST25R3916_INT_REGS_LEN); - - platformProtectST25RIrqStatus(); - st25r3916interrupt.status = ST25R3916_IRQ_MASK_NONE; - platformUnprotectST25RIrqStatus(); - return; -} - -/*******************************************************************************/ -void st25r3916IRQCallbackSet(void (*cb)(void)) { - st25r3916interrupt.prevCallback = st25r3916interrupt.callback; - st25r3916interrupt.callback = cb; -} - -/*******************************************************************************/ -void st25r3916IRQCallbackRestore(void) { - st25r3916interrupt.callback = st25r3916interrupt.prevCallback; - st25r3916interrupt.prevCallback = NULL; -} diff --git a/lib/ST25RFAL002/source/st25r3916/st25r3916_irq.h b/lib/ST25RFAL002/source/st25r3916/st25r3916_irq.h deleted file mode 100644 index 2433173718e..00000000000 --- a/lib/ST25RFAL002/source/st25r3916/st25r3916_irq.h +++ /dev/null @@ -1,296 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R3916 firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file - * - * \author Gustavo Patricio - * - * \brief ST25R3916 Interrupt handling - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-HAL - * \brief RFAL Hardware Abstraction Layer - * @{ - * - * \addtogroup ST25R3916 - * \brief RFAL ST25R3916 Driver - * @{ - * - * \addtogroup ST25R3916_IRQ - * \brief RFAL ST25R3916 IRQ - * @{ - * - */ - -#ifndef ST25R3916_IRQ_H -#define ST25R3916_IRQ_H - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ - -#include "platform.h" - -/* -****************************************************************************** -* GLOBAL DEFINES -****************************************************************************** -*/ - -#define ST25R3916_IRQ_MASK_ALL \ - (uint32_t)(0xFFFFFFFFUL) /*!< All ST25R3916 interrupt sources */ -#define ST25R3916_IRQ_MASK_NONE \ - (uint32_t)(0x00000000UL) /*!< No ST25R3916 interrupt source */ - -/* Main interrupt register */ -#define ST25R3916_IRQ_MASK_OSC \ - (uint32_t)(0x00000080U) /*!< ST25R3916 oscillator stable interrupt */ -#define ST25R3916_IRQ_MASK_FWL \ - (uint32_t)(0x00000040U) /*!< ST25R3916 FIFO water level interrupt */ -#define ST25R3916_IRQ_MASK_RXS \ - (uint32_t)(0x00000020U) /*!< ST25R3916 start of receive interrupt */ -#define ST25R3916_IRQ_MASK_RXE \ - (uint32_t)(0x00000010U) /*!< ST25R3916 end of receive interrupt */ -#define ST25R3916_IRQ_MASK_TXE \ - (uint32_t)(0x00000008U) /*!< ST25R3916 end of transmission interrupt */ -#define ST25R3916_IRQ_MASK_COL \ - (uint32_t)(0x00000004U) /*!< ST25R3916 bit collision interrupt */ -#define ST25R3916_IRQ_MASK_RX_REST \ - (uint32_t)(0x00000002U) /*!< ST25R3916 automatic reception restart interrupt */ -#define ST25R3916_IRQ_MASK_RFU \ - (uint32_t)(0x00000001U) /*!< ST25R3916 RFU interrupt */ - -/* Timer and NFC interrupt register */ -#define ST25R3916_IRQ_MASK_DCT \ - (uint32_t)(0x00008000U) /*!< ST25R3916 termination of direct command interrupt. */ -#define ST25R3916_IRQ_MASK_NRE \ - (uint32_t)(0x00004000U) /*!< ST25R3916 no-response timer expired interrupt */ -#define ST25R3916_IRQ_MASK_GPE \ - (uint32_t)(0x00002000U) /*!< ST25R3916 general purpose timer expired interrupt */ -#define ST25R3916_IRQ_MASK_EON \ - (uint32_t)(0x00001000U) /*!< ST25R3916 external field on interrupt */ -#define ST25R3916_IRQ_MASK_EOF \ - (uint32_t)(0x00000800U) /*!< ST25R3916 external field off interrupt */ -#define ST25R3916_IRQ_MASK_CAC \ - (uint32_t)(0x00000400U) /*!< ST25R3916 collision during RF collision avoidance interrupt */ -#define ST25R3916_IRQ_MASK_CAT \ - (uint32_t)(0x00000200U) /*!< ST25R3916 minimum guard time expired interrupt */ -#define ST25R3916_IRQ_MASK_NFCT \ - (uint32_t)(0x00000100U) /*!< ST25R3916 initiator bit rate recognised interrupt */ - -/* Error and wake-up interrupt register */ -#define ST25R3916_IRQ_MASK_CRC \ - (uint32_t)(0x00800000U) /*!< ST25R3916 CRC error interrupt */ -#define ST25R3916_IRQ_MASK_PAR \ - (uint32_t)(0x00400000U) /*!< ST25R3916 parity error interrupt */ -#define ST25R3916_IRQ_MASK_ERR2 \ - (uint32_t)(0x00200000U) /*!< ST25R3916 soft framing error interrupt */ -#define ST25R3916_IRQ_MASK_ERR1 \ - (uint32_t)(0x00100000U) /*!< ST25R3916 hard framing error interrupt */ -#define ST25R3916_IRQ_MASK_WT \ - (uint32_t)(0x00080000U) /*!< ST25R3916 wake-up interrupt */ -#define ST25R3916_IRQ_MASK_WAM \ - (uint32_t)(0x00040000U) /*!< ST25R3916 wake-up due to amplitude interrupt */ -#define ST25R3916_IRQ_MASK_WPH \ - (uint32_t)(0x00020000U) /*!< ST25R3916 wake-up due to phase interrupt */ -#define ST25R3916_IRQ_MASK_WCAP \ - (uint32_t)(0x00010000U) /*!< ST25R3916 wake-up due to capacitance measurement */ - -/* Passive Target Interrupt Register */ -#define ST25R3916_IRQ_MASK_PPON2 \ - (uint32_t)(0x80000000U) /*!< ST25R3916 PPON2 Field on waiting Timer interrupt */ -#define ST25R3916_IRQ_MASK_SL_WL \ - (uint32_t)(0x40000000U) /*!< ST25R3916 Passive target slot number water level interrupt */ -#define ST25R3916_IRQ_MASK_APON \ - (uint32_t)(0x20000000U) /*!< ST25R3916 Anticollision done and Field On interrupt */ -#define ST25R3916_IRQ_MASK_RXE_PTA \ - (uint32_t)(0x10000000U) /*!< ST25R3916 RXE with an automatic response interrupt */ -#define ST25R3916_IRQ_MASK_WU_F \ - (uint32_t)(0x08000000U) /*!< ST25R3916 212/424b/s Passive target interrupt: Active */ -#define ST25R3916_IRQ_MASK_RFU2 \ - (uint32_t)(0x04000000U) /*!< ST25R3916 RFU2 interrupt */ -#define ST25R3916_IRQ_MASK_WU_A_X \ - (uint32_t)(0x02000000U) /*!< ST25R3916 106kb/s Passive target state interrupt: Active* */ -#define ST25R3916_IRQ_MASK_WU_A \ - (uint32_t)(0x01000000U) /*!< ST25R3916 106kb/s Passive target state interrupt: Active */ - -/* -****************************************************************************** -* GLOBAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -/*! - ***************************************************************************** - * \brief Wait until an ST25R3916 interrupt occurs - * - * This function is used to access the ST25R3916 interrupt flags. Use this - * to wait for max. \a tmo milliseconds for the \b first interrupt indicated - * with mask \a mask to occur. - * - * \param[in] mask : mask indicating the interrupts to wait for. - * \param[in] tmo : time in milliseconds until timeout occurs. If set to 0 - * the functions waits forever. - * - * \return : 0 if timeout occured otherwise a mask indicating the cleared - * interrupts. - * - ***************************************************************************** - */ -uint32_t st25r3916WaitForInterruptsTimed(uint32_t mask, uint16_t tmo); - -/*! - ***************************************************************************** - * \brief Get status for the given interrupt - * - * This function is used to check whether the interrupt given by \a mask - * has occured. If yes the interrupt gets cleared. This function returns - * only status bits which are inside \a mask. - * - * \param[in] mask : mask indicating the interrupt to check for. - * - * \return the mask of the interrupts occurred - * - ***************************************************************************** - */ -uint32_t st25r3916GetInterrupt(uint32_t mask); - -/*! - ***************************************************************************** - * \brief Init the 3916 interrupt - * - * This function is used to check whether the interrupt given by \a mask - * has occured. - * - ***************************************************************************** - */ -void st25r3916InitInterrupts(void); - -/*! - ***************************************************************************** - * \brief Modifies the Interrupt - * - * This function modifies the interrupt - * - * \param[in] clr_mask : bit mask to be cleared on the interrupt mask - * \param[in] set_mask : bit mask to be set on the interrupt mask - ***************************************************************************** - */ -void st25r3916ModifyInterrupts(uint32_t clr_mask, uint32_t set_mask); - -/*! - ***************************************************************************** - * \brief Checks received interrupts - * - * Checks received interrupts and saves the result into global params - ***************************************************************************** - */ -void st25r3916CheckForReceivedInterrupts(void); - -/*! - ***************************************************************************** - * \brief ISR Service routine - * - * This function modiefies the interupt - ***************************************************************************** - */ -void st25r3916Isr(void); - -/*! - ***************************************************************************** - * \brief Enable a given ST25R3916 Interrupt source - * - * This function enables all interrupts given by \a mask, - * ST25R3916_IRQ_MASK_ALL enables all interrupts. - * - * \param[in] mask: mask indicating the interrupts to be enabled - * - ***************************************************************************** - */ -void st25r3916EnableInterrupts(uint32_t mask); - -/*! - ***************************************************************************** - * \brief Disable one or more a given ST25R3916 Interrupt sources - * - * This function disables all interrupts given by \a mask. 0xff disables all. - * - * \param[in] mask: mask indicating the interrupts to be disabled. - * - ***************************************************************************** - */ -void st25r3916DisableInterrupts(uint32_t mask); - -/*! - ***************************************************************************** - * \brief Clear all ST25R3916 irq flags - * - ***************************************************************************** - */ -void st25r3916ClearInterrupts(void); - -/*! - ***************************************************************************** - * \brief Clears and then enables the given ST25R3916 Interrupt sources - * - * \param[in] mask: mask indicating the interrupts to be cleared and enabled - ***************************************************************************** - */ -void st25r3916ClearAndEnableInterrupts(uint32_t mask); - -/*! - ***************************************************************************** - * \brief Sets IRQ callback for the ST25R3916 interrupt - * - ***************************************************************************** - */ -void st25r3916IRQCallbackSet(void (*cb)(void)); - -/*! - ***************************************************************************** - * \brief Sets IRQ callback for the ST25R3916 interrupt - * - ***************************************************************************** - */ -void st25r3916IRQCallbackRestore(void); - -#endif /* ST25R3916_IRQ_H */ - -/** - * @} - * - * @} - * - * @} - * - * @} - */ diff --git a/lib/ST25RFAL002/source/st25r3916/st25r3916_led.c b/lib/ST25RFAL002/source/st25r3916/st25r3916_led.c deleted file mode 100644 index 9737337ca34..00000000000 --- a/lib/ST25RFAL002/source/st25r3916/st25r3916_led.c +++ /dev/null @@ -1,148 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R3916 firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file - * - * \author Gustavo Patricio - * - * \brief ST25R3916 LEDs handling - * - */ - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ - -#include "st25r3916_led.h" -#include "st25r3916_irq.h" -#include "st25r3916_com.h" -#include "st25r3916.h" - -/* -****************************************************************************** -* MACROS -****************************************************************************** -*/ - -#ifdef PLATFORM_LED_RX_PIN -#define st25r3916ledRxOn() \ - platformLedOn( \ - PLATFORM_LED_RX_PORT, \ - PLATFORM_LED_RX_PIN); /*!< LED Rx Pin On from system HAL */ -#define st25r3916ledRxOff() \ - platformLedOff( \ - PLATFORM_LED_RX_PORT, \ - PLATFORM_LED_RX_PIN); /*!< LED Rx Pin Off from system HAL */ -#else /* PLATFORM_LED_RX_PIN */ -#define st25r3916ledRxOn() -#define st25r3916ledRxOff() -#endif /* PLATFORM_LED_RX_PIN */ - -#ifdef PLATFORM_LED_FIELD_PIN -#define st25r3916ledFieldOn() \ - platformLedOn( \ - PLATFORM_LED_FIELD_PORT, \ - PLATFORM_LED_FIELD_PIN); /*!< LED Field Pin On from system HAL */ -#define st25r3916ledFieldOff() \ - platformLedOff( \ - PLATFORM_LED_FIELD_PORT, \ - PLATFORM_LED_FIELD_PIN); /*!< LED Field Pin Off from system HAL */ -#else /* PLATFORM_LED_FIELD_PIN */ -#define st25r3916ledFieldOn() -#define st25r3916ledFieldOff() -#endif /* PLATFORM_LED_FIELD_PIN */ - -/* -****************************************************************************** -* GLOBAL FUNCTIONS -****************************************************************************** -*/ - -void st25r3916ledInit(void) { - /* Initialize LEDs if existing and defined */ - platformLedsInitialize(); - - st25r3916ledRxOff(); - st25r3916ledFieldOff(); -} - -/*******************************************************************************/ -void st25r3916ledEvtIrq(uint32_t irqs) { - if((irqs & (ST25R3916_IRQ_MASK_TXE | ST25R3916_IRQ_MASK_CAT)) != 0U) { - st25r3916ledFieldOn(); - } - - if((irqs & (ST25R3916_IRQ_MASK_RXS | ST25R3916_IRQ_MASK_NFCT)) != 0U) { - st25r3916ledRxOn(); - } - - if((irqs & (ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_NRE | ST25R3916_IRQ_MASK_RX_REST | - ST25R3916_IRQ_MASK_RXE_PTA | ST25R3916_IRQ_MASK_WU_A | ST25R3916_IRQ_MASK_WU_A_X | - ST25R3916_IRQ_MASK_WU_F | ST25R3916_IRQ_MASK_RFU2)) != 0U) { - st25r3916ledRxOff(); - } -} - -/*******************************************************************************/ -void st25r3916ledEvtWrReg(uint8_t reg, uint8_t val) { - if(reg == ST25R3916_REG_OP_CONTROL) { - if((ST25R3916_REG_OP_CONTROL_tx_en & val) != 0U) { - st25r3916ledFieldOn(); - } else { - st25r3916ledFieldOff(); - } - } -} - -/*******************************************************************************/ -void st25r3916ledEvtWrMultiReg(uint8_t reg, const uint8_t* vals, uint8_t len) { - uint8_t i; - - for(i = 0; i < (len); i++) { - st25r3916ledEvtWrReg((reg + i), vals[i]); - } -} - -/*******************************************************************************/ -void st25r3916ledEvtCmd(uint8_t cmd) { - if((cmd >= ST25R3916_CMD_TRANSMIT_WITH_CRC) && - (cmd <= ST25R3916_CMD_RESPONSE_RF_COLLISION_N)) { - st25r3916ledFieldOff(); - } - - if(cmd == ST25R3916_CMD_UNMASK_RECEIVE_DATA) { - st25r3916ledRxOff(); - } - - if(cmd == ST25R3916_CMD_SET_DEFAULT) { - st25r3916ledFieldOff(); - st25r3916ledRxOff(); - } -} diff --git a/lib/ST25RFAL002/source/st25r3916/st25r3916_led.h b/lib/ST25RFAL002/source/st25r3916/st25r3916_led.h deleted file mode 100644 index 376a8d7df7f..00000000000 --- a/lib/ST25RFAL002/source/st25r3916/st25r3916_led.h +++ /dev/null @@ -1,151 +0,0 @@ - -/****************************************************************************** - * \attention - * - *

© COPYRIGHT 2020 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: ST25R3916 firmware - * Revision: - * LANGUAGE: ISO C99 - */ - -/*! \file - * - * \author Gustavo Patricio - * - * \brief ST25R3916 LEDs handling - * - * - * \addtogroup RFAL - * @{ - * - * \addtogroup RFAL-HAL - * \brief RFAL Hardware Abstraction Layer - * @{ - * - * \addtogroup ST25R3916 - * \brief RFAL ST25R3916 Driver - * @{ - * - * \addtogroup ST25R3916_LED - * \brief RFAL ST25R3916 LED - * @{ - * - */ - -#ifndef ST25R3916_LED_H -#define ST25R3916_LED_H - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ - -#include "platform.h" - -/* -****************************************************************************** -* GLOBAL DEFINES -****************************************************************************** -*/ - -/* -****************************************************************************** -* GLOBAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -/*! - ***************************************************************************** - * \brief ST25R3916 LED Initialize - * - * This function initializes the LEDs that represent ST25R3916 activity - * - ***************************************************************************** - */ -void st25r3916ledInit(void); - -/*! - ***************************************************************************** - * \brief ST25R3916 LED Event Interrupt - * - * This function should be called upon a ST25R3916 Interrupt, providing - * the interrupt event with ST25R3916 irq flags to update LEDs - * - * \param[in] irqs: ST25R3916 irqs mask - * - ***************************************************************************** - */ -void st25r3916ledEvtIrq(uint32_t irqs); - -/*! - ***************************************************************************** - * \brief ST25R3916 LED Event Write Register - * - * This function should be called on a ST25R3916 Write Register operation - * providing the event with the register and value to update LEDs - * - * \param[in] reg: ST25R3916 register to be written - * \param[in] val: value to be written on the register - * - ***************************************************************************** - */ -void st25r3916ledEvtWrReg(uint8_t reg, uint8_t val); - -/*! - ***************************************************************************** - * \brief ST25R3916 LED Event Write Multiple Register - * - * This function should be called upon a ST25R3916 Write Multiple Registers, - * providing the event with the registers and values to update LEDs - * - * \param[in] reg : ST25R3916 first register written - * \param[in] vals: pointer to the values written - * \param[in] len : number of registers written - * - ***************************************************************************** - */ -void st25r3916ledEvtWrMultiReg(uint8_t reg, const uint8_t* vals, uint8_t len); - -/*! - ***************************************************************************** - * \brief ST25R3916 LED Event Direct Command - * - * This function should be called upon a ST25R3916 direct command, providing - * the event with the command executed - * - * \param[in] cmd: ST25R3916 cmd executed - * - ***************************************************************************** - */ -void st25r3916ledEvtCmd(uint8_t cmd); - -#endif /* ST25R3916_LED_H */ - -/** - * @} - * - * @} - * - * @} - * - * @} - */ diff --git a/lib/ST25RFAL002/st_errno.h b/lib/ST25RFAL002/st_errno.h deleted file mode 100644 index cd706b3d4c5..00000000000 --- a/lib/ST25RFAL002/st_errno.h +++ /dev/null @@ -1,158 +0,0 @@ - -/****************************************************************************** - * @attention - * - *

© COPYRIGHT 2018 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: STxxxx firmware - * LANGUAGE: ISO C99 - */ - -/*! \file st_errno.h - * - * \author - * - * \brief Main error codes - * - */ - -#ifndef ST_ERRNO_H -#define ST_ERRNO_H - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ - -#include - -/* -****************************************************************************** -* GLOBAL DATA TYPES -****************************************************************************** -*/ - -typedef uint16_t ReturnCode; /*!< Standard Return Code type from function. */ - -/* -****************************************************************************** -* DEFINES -****************************************************************************** -*/ - -/* - * Error codes to be used within the application. - * They are represented by an uint8_t - */ -enum { - ERR_NONE = 0, /*!< no error occurred */ - ERR_NOMEM = 1, /*!< not enough memory to perform the requested operation */ - ERR_BUSY = 2, /*!< device or resource busy */ - ERR_IO = 3, /*!< generic IO error */ - ERR_TIMEOUT = 4, /*!< error due to timeout */ - ERR_REQUEST = 5, /*!< invalid request or requested function can't be executed at the moment */ - ERR_NOMSG = 6, /*!< No message of desired type */ - ERR_PARAM = 7, /*!< Parameter error */ - ERR_SYSTEM = 8, /*!< System error */ - ERR_FRAMING = 9, /*!< Framing error */ - ERR_OVERRUN = 10, /*!< lost one or more received bytes */ - ERR_PROTO = 11, /*!< protocol error */ - ERR_INTERNAL = 12, /*!< Internal Error */ - ERR_AGAIN = 13, /*!< Call again */ - ERR_MEM_CORRUPT = 14, /*!< memory corruption */ - ERR_NOT_IMPLEMENTED = 15, /*!< not implemented */ - ERR_PC_CORRUPT = - 16, /*!< Program Counter has been manipulated or spike/noise trigger illegal operation */ - ERR_SEND = 17, /*!< error sending*/ - ERR_IGNORE = 18, /*!< indicates error detected but to be ignored */ - ERR_SEMANTIC = 19, /*!< indicates error in state machine (unexpected cmd) */ - ERR_SYNTAX = 20, /*!< indicates error in state machine (unknown cmd) */ - ERR_CRC = 21, /*!< crc error */ - ERR_NOTFOUND = 22, /*!< transponder not found */ - ERR_NOTUNIQUE = 23, /*!< transponder not unique - more than one transponder in field */ - ERR_NOTSUPP = 24, /*!< requested operation not supported */ - ERR_WRITE = 25, /*!< write error */ - ERR_FIFO = 26, /*!< fifo over or underflow error */ - ERR_PAR = 27, /*!< parity error */ - ERR_DONE = 28, /*!< transfer has already finished */ - ERR_RF_COLLISION = - 29, /*!< collision error (Bit Collision or during RF Collision avoidance ) */ - ERR_HW_OVERRUN = 30, /*!< lost one or more received bytes */ - ERR_RELEASE_REQ = 31, /*!< device requested release */ - ERR_SLEEP_REQ = 32, /*!< device requested sleep */ - ERR_WRONG_STATE = 33, /*!< incorrent state for requested operation */ - ERR_MAX_RERUNS = 34, /*!< blocking procedure reached maximum runs */ - ERR_DISABLED = 35, /*!< operation aborted due to disabled configuration */ - ERR_HW_MISMATCH = 36, /*!< expected hw do not match */ - ERR_LINK_LOSS = - 37, /*!< Other device's field didn't behave as expected: turned off by Initiator in Passive mode, or AP2P did not turn on field */ - ERR_INVALID_HANDLE = 38, /*!< invalid or not initalized device handle */ - - ERR_INCOMPLETE_BYTE = 40, /*!< Incomplete byte rcvd */ - ERR_INCOMPLETE_BYTE_01 = 41, /*!< Incomplete byte rcvd - 1 bit */ - ERR_INCOMPLETE_BYTE_02 = 42, /*!< Incomplete byte rcvd - 2 bit */ - ERR_INCOMPLETE_BYTE_03 = 43, /*!< Incomplete byte rcvd - 3 bit */ - ERR_INCOMPLETE_BYTE_04 = 44, /*!< Incomplete byte rcvd - 4 bit */ - ERR_INCOMPLETE_BYTE_05 = 45, /*!< Incomplete byte rcvd - 5 bit */ - ERR_INCOMPLETE_BYTE_06 = 46, /*!< Incomplete byte rcvd - 6 bit */ - ERR_INCOMPLETE_BYTE_07 = 47, /*!< Incomplete byte rcvd - 7 bit */ -}; - -/* General Sub-category number */ -#define ERR_GENERIC_GRP (0x0000) /*!< Reserved value for generic error no */ -#define ERR_WARN_GRP (0x0100) /*!< Errors which are not expected in normal operation */ -#define ERR_PROCESS_GRP (0x0200) /*!< Processes management errors */ -#define ERR_SIO_GRP (0x0800) /*!< SIO errors due to logging */ -#define ERR_RINGBUF_GRP (0x0900) /*!< Ring Buffer errors */ -#define ERR_MQ_GRP (0x0A00) /*!< MQ errors */ -#define ERR_TIMER_GRP (0x0B00) /*!< Timer errors */ -#define ERR_RFAL_GRP (0x0C00) /*!< RFAL errors */ -#define ERR_UART_GRP (0x0D00) /*!< UART errors */ -#define ERR_SPI_GRP (0x0E00) /*!< SPI errors */ -#define ERR_I2C_GRP (0x0F00) /*!< I2c errors */ - -#define ERR_INSERT_SIO_GRP(x) (ERR_SIO_GRP | x) /*!< Insert the SIO grp */ -#define ERR_INSERT_RINGBUF_GRP(x) (ERR_RINGBUF_GRP | x) /*!< Insert the Ring Buffer grp */ -#define ERR_INSERT_RFAL_GRP(x) (ERR_RFAL_GRP | x) /*!< Insert the RFAL grp */ -#define ERR_INSERT_SPI_GRP(x) (ERR_SPI_GRP | x) /*!< Insert the spi grp */ -#define ERR_INSERT_I2C_GRP(x) (ERR_I2C_GRP | x) /*!< Insert the i2c grp */ -#define ERR_INSERT_UART_GRP(x) (ERR_UART_GRP | x) /*!< Insert the uart grp */ -#define ERR_INSERT_TIMER_GRP(x) (ERR_TIMER_GRP | x) /*!< Insert the timer grp */ -#define ERR_INSERT_MQ_GRP(x) (ERR_MQ_GRP | x) /*!< Insert the mq grp */ -#define ERR_INSERT_PROCESS_GRP(x) (ERR_PROCESS_GRP | x) /*!< Insert the process grp */ -#define ERR_INSERT_WARN_GRP(x) (ERR_WARN_GRP | x) /*!< Insert the i2c grp */ -#define ERR_INSERT_GENERIC_GRP(x) (ERR_GENERIC_GRP | x) /*!< Insert the generic grp */ - -/* -****************************************************************************** -* GLOBAL MACROS -****************************************************************************** -*/ - -#define ERR_NO_MASK(x) (x & 0x00FF) /*!< Mask the error number */ - -/*! Common code to exit a function with the error if function f return error */ -#define EXIT_ON_ERR(r, f) \ - if(ERR_NONE != (r = f)) { \ - return r; \ - } - -#endif /* ST_ERRNO_H */ diff --git a/lib/ST25RFAL002/timer.c b/lib/ST25RFAL002/timer.c deleted file mode 100644 index ea0678a600d..00000000000 --- a/lib/ST25RFAL002/timer.c +++ /dev/null @@ -1,105 +0,0 @@ -/****************************************************************************** - * @attention - * - *

© COPYRIGHT 2016 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ -/* - * PROJECT: ST25R391x firmware - * $Revision: $ - * LANGUAGE: ANSI C - */ - -/*! \file timer.c - * - * \brief SW Timer implementation - * - * \author Gustavo Patricio - * - * This module makes use of a System Tick in millisconds and provides - * an abstraction for SW timers - * - */ - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ -#include "timer.h" -#include - -/* -****************************************************************************** -* LOCAL DEFINES -****************************************************************************** -*/ - -/* -****************************************************************************** -* LOCAL VARIABLES -****************************************************************************** -*/ - -static uint32_t timerStopwatchTick; - -/* -****************************************************************************** -* GLOBAL FUNCTIONS -****************************************************************************** -*/ - -/*******************************************************************************/ -uint32_t timerCalculateTimer(uint16_t time) { - return (furi_get_tick() + time); -} - -/*******************************************************************************/ -bool timerIsExpired(uint32_t timer) { - uint32_t uDiff; - int32_t sDiff; - - uDiff = (timer - furi_get_tick()); /* Calculate the diff between the timers */ - sDiff = uDiff; /* Convert the diff to a signed var */ - - /* Check if the given timer has expired already */ - if(sDiff < 0) { - return true; - } - - return false; -} - -/*******************************************************************************/ -void timerDelay(uint16_t tOut) { - uint32_t t; - - /* Calculate the timer and wait blocking until is running */ - t = timerCalculateTimer(tOut); - while(timerIsRunning(t)) - ; -} - -/*******************************************************************************/ -void timerStopwatchStart(void) { - timerStopwatchTick = furi_get_tick(); -} - -/*******************************************************************************/ -uint32_t timerStopwatchMeasure(void) { - return (uint32_t)(furi_get_tick() - timerStopwatchTick); -} diff --git a/lib/ST25RFAL002/timer.h b/lib/ST25RFAL002/timer.h deleted file mode 100644 index b5f5eb329d5..00000000000 --- a/lib/ST25RFAL002/timer.h +++ /dev/null @@ -1,125 +0,0 @@ -#pragma once -/****************************************************************************** - * @attention - * - *

© COPYRIGHT 2016 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ -/* - * PROJECT: ST25R391x firmware - * $Revision: $ - * LANGUAGE: ANSI C - */ - -/*! \file timer.h - * - * \brief SW Timer implementation header file - * - * This module makes use of a System Tick in millisconds and provides - * an abstraction for SW timers - * - */ - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ -#include -#include - -/* -****************************************************************************** -* GLOBAL MACROS -****************************************************************************** -*/ -#define timerIsRunning(t) (!timerIsExpired(t)) - -/* -****************************************************************************** -* GLOBAL DEFINES -****************************************************************************** -*/ - -/*! - ***************************************************************************** - * \brief Calculate Timer - * - * This method calculates when the timer will be expired given the amount - * time in milliseconds /a tOut. - * Once the timer has been calculated it will then be used to check when - * it expires. - * - * \see timersIsExpired - * - * \param[in] time : time/duration in Milliseconds for the timer - * - * \return u32 : The new timer calculated based on the given time - ***************************************************************************** - */ -uint32_t timerCalculateTimer(uint16_t time); - -/*! - ***************************************************************************** - * \brief Checks if a Timer is Expired - * - * This method checks if a timer has already expired. - * Based on the given timer previously calculated it checks if this timer - * has already elapsed - * - * \see timersCalculateTimer - * - * \param[in] timer : the timer to check - * - * \return true : timer has already expired - * \return false : timer is still running - ***************************************************************************** - */ -bool timerIsExpired(uint32_t timer); - -/*! - ***************************************************************************** - * \brief Performs a Delay - * - * This method performs a delay for the given amount of time in Milliseconds - * - * \param[in] time : time/duration in Milliseconds of the delay - * - ***************************************************************************** - */ -void timerDelay(uint16_t time); - -/*! - ***************************************************************************** - * \brief Stopwatch start - * - * This method initiates the stopwatch to later measure the time in ms - * - ***************************************************************************** - */ -void timerStopwatchStart(void); - -/*! - ***************************************************************************** - * \brief Stopwatch Measure - * - * This method returns the elapsed time in ms since the stopwatch was initiated - * - * \return The time in ms since the stopwatch was started - ***************************************************************************** - */ -uint32_t timerStopwatchMeasure(void); diff --git a/lib/ST25RFAL002/utils.h b/lib/ST25RFAL002/utils.h deleted file mode 100644 index 44a141986c5..00000000000 --- a/lib/ST25RFAL002/utils.h +++ /dev/null @@ -1,100 +0,0 @@ - -/****************************************************************************** - * @attention - * - *

© COPYRIGHT 2018 STMicroelectronics

- * - * Licensed under ST MYLIBERTY SOFTWARE LICENSE AGREEMENT (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/myliberty - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, - * AND SPECIFICALLY DISCLAIMING THE IMPLIED WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * See the License for the specific language governing permissions and - * limitations under the License. - * -******************************************************************************/ - -/* - * PROJECT: NFCC firmware - * $Revision: $ - * LANGUAGE: ISO C99 - */ - -/*! \file - * - * \author Ulrich Herrmann - * - * \brief Common and helpful macros - * - */ - -#ifndef UTILS_H -#define UTILS_H - -/* -****************************************************************************** -* INCLUDES -****************************************************************************** -*/ -#include -#include - -/* -****************************************************************************** -* GLOBAL MACROS -****************************************************************************** -*/ -/*! - * this macro evaluates an error variable \a ERR against an error code \a EC. - * in case it is not equal it jumps to the given label \a LABEL. - */ -#define EVAL_ERR_NE_GOTO(EC, ERR, LABEL) \ - if(EC != ERR) goto LABEL; - -/*! - * this macro evaluates an error variable \a ERR against an error code \a EC. - * in case it is equal it jumps to the given label \a LABEL. - */ -#define EVAL_ERR_EQ_GOTO(EC, ERR, LABEL) \ - if(EC == ERR) goto LABEL; -#define BITMASK_1 (0x01) /*!< Bit mask for lsb bit */ -#define BITMASK_2 (0x03) /*!< Bit mask for two lsb bits */ -#define BITMASK_3 (0x07) /*!< Bit mask for three lsb bits */ -#define BITMASK_4 (0x0F) /*!< Bit mask for four lsb bits */ -#define U16TOU8(a) ((a)&0x00FF) /*!< Cast 16-bit unsigned to 8-bit unsigned */ -#define GETU16(a) \ - (uint16_t)( \ - (a[0] << 8) | a[1]) /*!< Cast two Big Endian 8-bits byte array to 16-bits unsigned */ - -#define REVERSE_BYTES(pData, nDataSize) \ - unsigned char swap, *lo = pData, *hi = pData + nDataSize - 1; \ - while(lo < hi) { \ - swap = *lo; \ - *lo++ = *hi; \ - *hi-- = swap; \ - } - -#define ST_MEMMOVE memmove /*!< map memmove to string library code */ -#define ST_MEMCPY memcpy /*!< map memcpy to string library code */ -#define ST_MEMSET memset /*!< map memset to string library code */ -#define ST_BYTECMP memcmp /*!< map bytecmp to string library code */ - -#define NO_WARNING(v) ((void)(v)) /*!< Macro to suppress compiler warning */ - -#ifndef NULL -#define NULL (void*)0 /*!< represents a NULL pointer */ -#endif /* !NULL */ - -/* -****************************************************************************** -* GLOBAL FUNCTION PROTOTYPES -****************************************************************************** -*/ - -#endif /* UTILS_H */ diff --git a/lib/STM32CubeWB b/lib/STM32CubeWB deleted file mode 160000 index a9e29b431f6..00000000000 --- a/lib/STM32CubeWB +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a9e29b431f6dac95b6fc860a717834f35b7f78e5 diff --git a/lib/STM32CubeWB.scons b/lib/STM32CubeWB.scons deleted file mode 100644 index 02618ae73a3..00000000000 --- a/lib/STM32CubeWB.scons +++ /dev/null @@ -1,66 +0,0 @@ -Import("env") - -env.Append( - CPPPATH=[ - "#/lib/STM32CubeWB/Drivers/CMSIS/Device/ST", - "#/lib/STM32CubeWB/Drivers/CMSIS/Device/ST/STM32WBxx/Include", - "#/lib/STM32CubeWB/Drivers/CMSIS/Include", - "#/lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc", - "#/lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/Legacy", - "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN", - "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/ble", - "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/ble/core", - "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/ble/core/template", - "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread", - "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/shci", - "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl", - "#/lib/STM32CubeWB/Middlewares/ST/STM32_WPAN/utilities", - ], - CPPDEFINES=[ - "STM32WB", - "STM32WB55xx", - "USE_FULL_ASSERT", - "USE_FULL_LL_DRIVER", - ], -) - -if env["RAM_EXEC"]: - env.Append( - CPPDEFINES=[ - "VECT_TAB_SRAM", - ], - ) - - -libenv = env.Clone(FW_LIB_NAME="stm32cubewb") -libenv.ApplyLibFlags() - -sources = libenv.GlobRecursive( - "*_ll_*.c", "STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Src/", exclude="*usb.c" -) -sources += Glob( - "STM32CubeWB/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/shci/*.c", - source=True, -) -sources += Glob( - "STM32CubeWB/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/*_tl*.c", - source=True, -) -sources += [ - "STM32CubeWB/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/tl_mbox.c", - "STM32CubeWB/Middlewares/ST/STM32_WPAN/ble/svc/Src/svc_ctl.c", - "STM32CubeWB/Middlewares/ST/STM32_WPAN/ble/core/auto/ble_gap_aci.c", - "STM32CubeWB/Middlewares/ST/STM32_WPAN/ble/core/auto/ble_gatt_aci.c", - "STM32CubeWB/Middlewares/ST/STM32_WPAN/ble/core/auto/ble_hal_aci.c", - "STM32CubeWB/Middlewares/ST/STM32_WPAN/ble/core/auto/ble_hci_le.c", - "STM32CubeWB/Middlewares/ST/STM32_WPAN/ble/core/auto/ble_l2cap_aci.c", - "STM32CubeWB/Middlewares/ST/STM32_WPAN/ble/core/template/osal.c", - "STM32CubeWB/Middlewares/ST/STM32_WPAN/utilities/dbg_trace.c", - "STM32CubeWB/Middlewares/ST/STM32_WPAN/utilities/otp.c", - "STM32CubeWB/Middlewares/ST/STM32_WPAN/utilities/stm_list.c", -] - - -lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) -libenv.Install("${LIB_DIST_DIR}", lib) -Return("lib") diff --git a/lib/app-scened-template/generic_scene.hpp b/lib/app-scened-template/generic_scene.hpp index 979e974787b..bcdf0b46413 100644 --- a/lib/app-scened-template/generic_scene.hpp +++ b/lib/app-scened-template/generic_scene.hpp @@ -1,4 +1,5 @@ -template class GenericScene { +template +class GenericScene { public: virtual void on_enter(TApp* app, bool need_restore) = 0; virtual bool on_event(TApp* app, typename TApp::Event* event) = 0; diff --git a/lib/app-scened-template/record_controller.hpp b/lib/app-scened-template/record_controller.hpp index 9b14274a8ef..171115e15bf 100644 --- a/lib/app-scened-template/record_controller.hpp +++ b/lib/app-scened-template/record_controller.hpp @@ -6,7 +6,8 @@ * * @tparam TRecordClass record class */ -template class RecordController { +template +class RecordController { public: /** * @brief Construct a new Record Controller object for record with record name diff --git a/lib/app-scened-template/scene_controller.hpp b/lib/app-scened-template/scene_controller.hpp index 678f6c0df7b..052eca73166 100644 --- a/lib/app-scened-template/scene_controller.hpp +++ b/lib/app-scened-template/scene_controller.hpp @@ -11,7 +11,8 @@ * @tparam TScene generic scene class * @tparam TApp application class */ -template class SceneController { +template +class SceneController { public: /** * @brief Add scene to scene container diff --git a/lib/app-scened-template/typeindex_no_rtti.hpp b/lib/app-scened-template/typeindex_no_rtti.hpp index 4ac52725f18..0f399385ab5 100644 --- a/lib/app-scened-template/typeindex_no_rtti.hpp +++ b/lib/app-scened-template/typeindex_no_rtti.hpp @@ -33,12 +33,14 @@ namespace ext { /** * Dummy type for tag-dispatching. */ -template struct tag_type {}; +template +struct tag_type {}; /** * A value of tag_type. */ -template constexpr tag_type tag{}; +template +constexpr tag_type tag{}; /** * A type_index implementation without RTTI. @@ -47,7 +49,8 @@ struct type_index { /** * Creates a type_index object for the specified type. */ - template type_index(tag_type) noexcept : hash_code_{index} { + template + type_index(tag_type) noexcept : hash_code_{index} { } /** @@ -61,7 +64,8 @@ struct type_index { /** * Unique integral index associated to template type argument. */ - template static std::size_t const index; + template + static std::size_t const index; /** * Global counter for generating index values. @@ -75,14 +79,16 @@ struct type_index { std::size_t hash_code_; }; -template std::size_t const type_index::index = type_index::counter()++; +template +std::size_t const type_index::index = type_index::counter()++; /** * Creates a type_index object for the specified type. * * Equivalent to `ext::type_index{ext::tag}`. */ -template type_index make_type_index() noexcept { +template +type_index make_type_index() noexcept { return tag; } @@ -111,7 +117,8 @@ inline bool operator>=(type_index const& a, type_index const& b) noexcept { } } -template <> struct std::hash { +template <> +struct std::hash { using argument_type = ext::type_index; using result_type = std::size_t; diff --git a/lib/app-scened-template/view_controller.hpp b/lib/app-scened-template/view_controller.hpp index 15028f533f9..8c48fcf747a 100644 --- a/lib/app-scened-template/view_controller.hpp +++ b/lib/app-scened-template/view_controller.hpp @@ -12,7 +12,8 @@ * @tparam TApp application class * @tparam TViewModules variadic list of ViewModules */ -template class ViewController { +template +class ViewController { public: ViewController() { event_queue = furi_message_queue_alloc(10, sizeof(typename TApp::Event)); @@ -44,7 +45,8 @@ template class ViewController { * @tparam T Concrete ViewModule class * @return T* ViewModule pointer */ - template T* get() { + template + T* get() { uint32_t view_index = ext::make_type_index().hash_code(); furi_check(holder.count(view_index) != 0); return static_cast(holder[view_index]); @@ -56,7 +58,8 @@ template class ViewController { * @tparam T Concrete ViewModule class * @return T* ViewModule pointer */ - template operator T*() { + template + operator T*() { uint32_t view_index = ext::make_type_index().hash_code(); furi_check(holder.count(view_index) != 0); return static_cast(holder[view_index]); @@ -68,7 +71,8 @@ template class ViewController { * @tparam T Concrete ViewModule class * @return T* ViewModule pointer */ - template void switch_to() { + template + void switch_to() { uint32_t view_index = ext::make_type_index().hash_code(); furi_check(holder.count(view_index) != 0); view_dispatcher_switch_to_view(view_dispatcher, view_index); diff --git a/lib/app-scened-template/view_modules/popup_vm.cpp b/lib/app-scened-template/view_modules/popup_vm.cpp index c378d9cc94a..330aa44ca96 100644 --- a/lib/app-scened-template/view_modules/popup_vm.cpp +++ b/lib/app-scened-template/view_modules/popup_vm.cpp @@ -1,4 +1,6 @@ #include "popup_vm.h" +#include + PopupVM::PopupVM() { popup = popup_alloc(); } @@ -50,5 +52,5 @@ void PopupVM::enable_timeout() { } void PopupVM::disable_timeout() { - popup_enable_timeout(popup); + popup_disable_timeout(popup); } diff --git a/lib/appframe.scons b/lib/appframe.scons index 935986d644f..fb268579d66 100644 --- a/lib/appframe.scons +++ b/lib/appframe.scons @@ -5,6 +5,9 @@ env.Append( "#/lib/app-scened-template", "#/lib/callback-connector", ], + LINT_SOURCES=[ + Dir("app-scened-template"), + ], ) diff --git a/lib/cmsis_core/cmsis_armcc.h b/lib/cmsis_core/cmsis_armcc.h new file mode 100644 index 00000000000..2edff5af552 --- /dev/null +++ b/lib/cmsis_core/cmsis_armcc.h @@ -0,0 +1,894 @@ +/**************************************************************************//** + * @file cmsis_armcc.h + * @brief CMSIS compiler ARMCC (Arm Compiler 5) header file + * @version V5.4.0 + * @date 20. January 2023 + ******************************************************************************/ +/* + * Copyright (c) 2009-2023 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CMSIS_ARMCC_H +#define __CMSIS_ARMCC_H + + +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 400677) + #error "Please use Arm Compiler Toolchain V4.0.677 or later!" +#endif + +/* CMSIS compiler control architecture macros */ +#if ((defined (__TARGET_ARCH_6_M ) && (__TARGET_ARCH_6_M == 1)) || \ + (defined (__TARGET_ARCH_6S_M ) && (__TARGET_ARCH_6S_M == 1)) ) + #define __ARM_ARCH_6M__ 1 +#endif + +#if (defined (__TARGET_ARCH_7_M ) && (__TARGET_ARCH_7_M == 1)) + #define __ARM_ARCH_7M__ 1 +#endif + +#if (defined (__TARGET_ARCH_7E_M) && (__TARGET_ARCH_7E_M == 1)) + #define __ARM_ARCH_7EM__ 1 +#endif + + /* __ARM_ARCH_8M_BASE__ not applicable */ + /* __ARM_ARCH_8M_MAIN__ not applicable */ + /* __ARM_ARCH_8_1M_MAIN__ not applicable */ + +/* CMSIS compiler control DSP macros */ +#if ((defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) + #define __ARM_FEATURE_DSP 1 +#endif + +/* CMSIS compiler specific defines */ +#ifndef __ASM + #define __ASM __asm +#endif +#ifndef __INLINE + #define __INLINE __inline +#endif +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static __inline +#endif +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE static __forceinline +#endif +#ifndef __NO_RETURN + #define __NO_RETURN __declspec(noreturn) +#endif +#ifndef __USED + #define __USED __attribute__((used)) +#endif +#ifndef __WEAK + #define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED + #define __PACKED __attribute__((packed)) +#endif +#ifndef __PACKED_STRUCT + #define __PACKED_STRUCT __packed struct +#endif +#ifndef __PACKED_UNION + #define __PACKED_UNION __packed union +#endif +#ifndef __UNALIGNED_UINT32 /* deprecated */ + #define __UNALIGNED_UINT32(x) (*((__packed uint32_t *)(x))) +#endif +#ifndef __UNALIGNED_UINT16_WRITE + #define __UNALIGNED_UINT16_WRITE(addr, val) ((*((__packed uint16_t *)(addr))) = (val)) +#endif +#ifndef __UNALIGNED_UINT16_READ + #define __UNALIGNED_UINT16_READ(addr) (*((const __packed uint16_t *)(addr))) +#endif +#ifndef __UNALIGNED_UINT32_WRITE + #define __UNALIGNED_UINT32_WRITE(addr, val) ((*((__packed uint32_t *)(addr))) = (val)) +#endif +#ifndef __UNALIGNED_UINT32_READ + #define __UNALIGNED_UINT32_READ(addr) (*((const __packed uint32_t *)(addr))) +#endif +#ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#ifndef __RESTRICT + #define __RESTRICT __restrict +#endif +#ifndef __COMPILER_BARRIER + #define __COMPILER_BARRIER() __memory_changed() +#endif +#ifndef __NO_INIT + #define __NO_INIT __attribute__ ((section (".bss.noinit"), zero_init)) +#endif +#ifndef __ALIAS + #define __ALIAS(x) __attribute__ ((alias(x))) +#endif + +/* ######################### Startup and Lowlevel Init ######################## */ + +#ifndef __PROGRAM_START +#define __PROGRAM_START __main +#endif + +#ifndef __INITIAL_SP +#define __INITIAL_SP Image$$ARM_LIB_STACK$$ZI$$Limit +#endif + +#ifndef __STACK_LIMIT +#define __STACK_LIMIT Image$$ARM_LIB_STACK$$ZI$$Base +#endif + +#ifndef __VECTOR_TABLE +#define __VECTOR_TABLE __Vectors +#endif + +#ifndef __VECTOR_TABLE_ATTRIBUTE +#define __VECTOR_TABLE_ATTRIBUTE __attribute__((used, section("RESET"))) +#endif + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +/** + \brief No Operation + \details No Operation does nothing. This instruction can be used for code alignment purposes. + */ +#define __NOP __nop + + +/** + \brief Wait For Interrupt + \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. + */ +#define __WFI __wfi + + +/** + \brief Wait For Event + \details Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +#define __WFE __wfe + + +/** + \brief Send Event + \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +#define __SEV __sev + + +/** + \brief Instruction Synchronization Barrier + \details Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or memory, + after the instruction has been completed. + */ +#define __ISB() __isb(0xF) + +/** + \brief Data Synchronization Barrier + \details Acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +#define __DSB() __dsb(0xF) + +/** + \brief Data Memory Barrier + \details Ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +#define __DMB() __dmb(0xF) + + +/** + \brief Reverse byte order (32 bit) + \details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REV __rev + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856. + \param [in] value Value to reverse + \return Reversed value + */ +#ifndef __NO_EMBEDDED_ASM +__attribute__((section(".rev16_text"))) __STATIC_INLINE __ASM uint32_t __REV16(uint32_t value) +{ + rev16 r0, r0 + bx lr +} +#endif + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000. + \param [in] value Value to reverse + \return Reversed value + */ +#ifndef __NO_EMBEDDED_ASM +__attribute__((section(".revsh_text"))) __STATIC_INLINE __ASM int16_t __REVSH(int16_t value) +{ + revsh r0, r0 + bx lr +} +#endif + + +/** + \brief Rotate Right in unsigned value (32 bit) + \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + \param [in] op1 Value to rotate + \param [in] op2 Number of Bits to rotate + \return Rotated value + */ +#define __ROR __ror + + +/** + \brief Breakpoint + \details Causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __breakpoint(value) + + +/** + \brief Reverse bit order of value + \details Reverses the bit order of the given value. + \param [in] value Value to reverse + \return Reversed value + */ +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) + #define __RBIT __rbit +#else +__attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value) +{ + uint32_t result; + uint32_t s = (4U /*sizeof(v)*/ * 8U) - 1U; /* extra shift needed at end */ + + result = value; /* r will be reversed bits of v; first get LSB of v */ + for (value >>= 1U; value != 0U; value >>= 1U) + { + result <<= 1U; + result |= value & 1U; + s--; + } + result <<= s; /* shift when v's highest bits are zero */ + return result; +} +#endif + + +/** + \brief Count leading zeros + \details Counts the number of leading zeros of a data value. + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +#define __CLZ __clz + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) + +/** + \brief LDR Exclusive (8 bit) + \details Executes a exclusive LDR instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020) + #define __LDREXB(ptr) ((uint8_t ) __ldrex(ptr)) +#else + #define __LDREXB(ptr) _Pragma("push") _Pragma("diag_suppress 3731") ((uint8_t ) __ldrex(ptr)) _Pragma("pop") +#endif + + +/** + \brief LDR Exclusive (16 bit) + \details Executes a exclusive LDR instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020) + #define __LDREXH(ptr) ((uint16_t) __ldrex(ptr)) +#else + #define __LDREXH(ptr) _Pragma("push") _Pragma("diag_suppress 3731") ((uint16_t) __ldrex(ptr)) _Pragma("pop") +#endif + + +/** + \brief LDR Exclusive (32 bit) + \details Executes a exclusive LDR instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020) + #define __LDREXW(ptr) ((uint32_t ) __ldrex(ptr)) +#else + #define __LDREXW(ptr) _Pragma("push") _Pragma("diag_suppress 3731") ((uint32_t ) __ldrex(ptr)) _Pragma("pop") +#endif + + +/** + \brief STR Exclusive (8 bit) + \details Executes a exclusive STR instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020) + #define __STREXB(value, ptr) __strex(value, ptr) +#else + #define __STREXB(value, ptr) _Pragma("push") _Pragma("diag_suppress 3731") __strex(value, ptr) _Pragma("pop") +#endif + + +/** + \brief STR Exclusive (16 bit) + \details Executes a exclusive STR instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020) + #define __STREXH(value, ptr) __strex(value, ptr) +#else + #define __STREXH(value, ptr) _Pragma("push") _Pragma("diag_suppress 3731") __strex(value, ptr) _Pragma("pop") +#endif + + +/** + \brief STR Exclusive (32 bit) + \details Executes a exclusive STR instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020) + #define __STREXW(value, ptr) __strex(value, ptr) +#else + #define __STREXW(value, ptr) _Pragma("push") _Pragma("diag_suppress 3731") __strex(value, ptr) _Pragma("pop") +#endif + + +/** + \brief Remove the exclusive lock + \details Removes the exclusive lock which is created by LDREX. + */ +#define __CLREX __clrex + + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT __ssat + + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT __usat + + +/** + \brief Rotate Right with Extend (32 bit) + \details Moves each bit of a bitstring right by one bit. + The carry input is shifted in at the left end of the bitstring. + \param [in] value Value to rotate + \return Rotated value + */ +#ifndef __NO_EMBEDDED_ASM +__attribute__((section(".rrx_text"))) __STATIC_INLINE __ASM uint32_t __RRX(uint32_t value) +{ + rrx r0, r0 + bx lr +} +#endif + + +/** + \brief LDRT Unprivileged (8 bit) + \details Executes a Unprivileged LDRT instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#define __LDRBT(ptr) ((uint8_t ) __ldrt(ptr)) + + +/** + \brief LDRT Unprivileged (16 bit) + \details Executes a Unprivileged LDRT instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#define __LDRHT(ptr) ((uint16_t) __ldrt(ptr)) + + +/** + \brief LDRT Unprivileged (32 bit) + \details Executes a Unprivileged LDRT instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#define __LDRT(ptr) ((uint32_t ) __ldrt(ptr)) + + +/** + \brief STRT Unprivileged (8 bit) + \details Executes a Unprivileged STRT instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +#define __STRBT(value, ptr) __strt(value, ptr) + + +/** + \brief STRT Unprivileged (16 bit) + \details Executes a Unprivileged STRT instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +#define __STRHT(value, ptr) __strt(value, ptr) + + +/** + \brief STRT Unprivileged (32 bit) + \details Executes a Unprivileged STRT instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +#define __STRT(value, ptr) __strt(value, ptr) + +#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */ + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +__attribute__((always_inline)) __STATIC_INLINE int32_t __SSAT(int32_t val, uint32_t sat) +{ + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; +} + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +__attribute__((always_inline)) __STATIC_INLINE uint32_t __USAT(int32_t val, uint32_t sat) +{ + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */ + +/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ + + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ + */ + +/** + \brief Enable IRQ Interrupts + \details Enables IRQ interrupts by clearing special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +/* intrinsic void __enable_irq(); */ + + +/** + \brief Disable IRQ Interrupts + \details Disables IRQ interrupts by setting special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +/* intrinsic void __disable_irq(); */ + +/** + \brief Get Control Register + \details Returns the content of the Control Register. + \return Control Register value + */ +__STATIC_INLINE uint32_t __get_CONTROL(void) +{ + register uint32_t __regControl __ASM("control"); + return(__regControl); +} + + +/** + \brief Set Control Register + \details Writes the given value to the Control Register. + \param [in] control Control Register value to set + */ +__STATIC_INLINE void __set_CONTROL(uint32_t control) +{ + register uint32_t __regControl __ASM("control"); + __regControl = control; + __ISB(); +} + + +/** + \brief Get IPSR Register + \details Returns the content of the IPSR Register. + \return IPSR Register value + */ +__STATIC_INLINE uint32_t __get_IPSR(void) +{ + register uint32_t __regIPSR __ASM("ipsr"); + return(__regIPSR); +} + + +/** + \brief Get APSR Register + \details Returns the content of the APSR Register. + \return APSR Register value + */ +__STATIC_INLINE uint32_t __get_APSR(void) +{ + register uint32_t __regAPSR __ASM("apsr"); + return(__regAPSR); +} + + +/** + \brief Get xPSR Register + \details Returns the content of the xPSR Register. + \return xPSR Register value + */ +__STATIC_INLINE uint32_t __get_xPSR(void) +{ + register uint32_t __regXPSR __ASM("xpsr"); + return(__regXPSR); +} + + +/** + \brief Get Process Stack Pointer + \details Returns the current value of the Process Stack Pointer (PSP). + \return PSP Register value + */ +__STATIC_INLINE uint32_t __get_PSP(void) +{ + register uint32_t __regProcessStackPointer __ASM("psp"); + return(__regProcessStackPointer); +} + + +/** + \brief Set Process Stack Pointer + \details Assigns the given value to the Process Stack Pointer (PSP). + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_INLINE void __set_PSP(uint32_t topOfProcStack) +{ + register uint32_t __regProcessStackPointer __ASM("psp"); + __regProcessStackPointer = topOfProcStack; +} + + +/** + \brief Get Main Stack Pointer + \details Returns the current value of the Main Stack Pointer (MSP). + \return MSP Register value + */ +__STATIC_INLINE uint32_t __get_MSP(void) +{ + register uint32_t __regMainStackPointer __ASM("msp"); + return(__regMainStackPointer); +} + + +/** + \brief Set Main Stack Pointer + \details Assigns the given value to the Main Stack Pointer (MSP). + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_INLINE void __set_MSP(uint32_t topOfMainStack) +{ + register uint32_t __regMainStackPointer __ASM("msp"); + __regMainStackPointer = topOfMainStack; +} + + +/** + \brief Get Priority Mask + \details Returns the current state of the priority mask bit from the Priority Mask Register. + \return Priority Mask value + */ +__STATIC_INLINE uint32_t __get_PRIMASK(void) +{ + register uint32_t __regPriMask __ASM("primask"); + return(__regPriMask); +} + + +/** + \brief Set Priority Mask + \details Assigns the given value to the Priority Mask Register. + \param [in] priMask Priority Mask + */ +__STATIC_INLINE void __set_PRIMASK(uint32_t priMask) +{ + register uint32_t __regPriMask __ASM("primask"); + __regPriMask = (priMask); +} + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) + +/** + \brief Enable FIQ + \details Enables FIQ interrupts by clearing special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +#define __enable_fault_irq __enable_fiq + + +/** + \brief Disable FIQ + \details Disables FIQ interrupts by setting special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +#define __disable_fault_irq __disable_fiq + + +/** + \brief Get Base Priority + \details Returns the current value of the Base Priority register. + \return Base Priority register value + */ +__STATIC_INLINE uint32_t __get_BASEPRI(void) +{ + register uint32_t __regBasePri __ASM("basepri"); + return(__regBasePri); +} + + +/** + \brief Set Base Priority + \details Assigns the given value to the Base Priority register. + \param [in] basePri Base Priority value to set + */ +__STATIC_INLINE void __set_BASEPRI(uint32_t basePri) +{ + register uint32_t __regBasePri __ASM("basepri"); + __regBasePri = (basePri & 0xFFU); +} + + +/** + \brief Set Base Priority with condition + \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, + or the new value increases the BASEPRI priority level. + \param [in] basePri Base Priority value to set + */ +__STATIC_INLINE void __set_BASEPRI_MAX(uint32_t basePri) +{ + register uint32_t __regBasePriMax __ASM("basepri_max"); + __regBasePriMax = (basePri & 0xFFU); +} + + +/** + \brief Get Fault Mask + \details Returns the current value of the Fault Mask register. + \return Fault Mask register value + */ +__STATIC_INLINE uint32_t __get_FAULTMASK(void) +{ + register uint32_t __regFaultMask __ASM("faultmask"); + return(__regFaultMask); +} + + +/** + \brief Set Fault Mask + \details Assigns the given value to the Fault Mask register. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) +{ + register uint32_t __regFaultMask __ASM("faultmask"); + __regFaultMask = (faultMask & (uint32_t)1U); +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */ + + +/** + \brief Get FPSCR + \details Returns the current value of the Floating Point Status/Control register. + \return Floating Point Status/Control register value + */ +__STATIC_INLINE uint32_t __get_FPSCR(void) +{ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) + register uint32_t __regfpscr __ASM("fpscr"); + return(__regfpscr); +#else + return(0U); +#endif +} + + +/** + \brief Set FPSCR + \details Assigns the given value to the Floating Point Status/Control register. + \param [in] fpscr Floating Point Status/Control value to set + */ +__STATIC_INLINE void __set_FPSCR(uint32_t fpscr) +{ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) + register uint32_t __regfpscr __ASM("fpscr"); + __regfpscr = (fpscr); +#else + (void)fpscr; +#endif +} + + +/*@} end of CMSIS_Core_RegAccFunctions */ + + +/* ################### Compiler specific Intrinsics ########################### */ +/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics + Access to dedicated SIMD instructions + @{ +*/ + +#if ((defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) + +#define __SADD8 __sadd8 +#define __QADD8 __qadd8 +#define __SHADD8 __shadd8 +#define __UADD8 __uadd8 +#define __UQADD8 __uqadd8 +#define __UHADD8 __uhadd8 +#define __SSUB8 __ssub8 +#define __QSUB8 __qsub8 +#define __SHSUB8 __shsub8 +#define __USUB8 __usub8 +#define __UQSUB8 __uqsub8 +#define __UHSUB8 __uhsub8 +#define __SADD16 __sadd16 +#define __QADD16 __qadd16 +#define __SHADD16 __shadd16 +#define __UADD16 __uadd16 +#define __UQADD16 __uqadd16 +#define __UHADD16 __uhadd16 +#define __SSUB16 __ssub16 +#define __QSUB16 __qsub16 +#define __SHSUB16 __shsub16 +#define __USUB16 __usub16 +#define __UQSUB16 __uqsub16 +#define __UHSUB16 __uhsub16 +#define __SASX __sasx +#define __QASX __qasx +#define __SHASX __shasx +#define __UASX __uasx +#define __UQASX __uqasx +#define __UHASX __uhasx +#define __SSAX __ssax +#define __QSAX __qsax +#define __SHSAX __shsax +#define __USAX __usax +#define __UQSAX __uqsax +#define __UHSAX __uhsax +#define __USAD8 __usad8 +#define __USADA8 __usada8 +#define __SSAT16 __ssat16 +#define __USAT16 __usat16 +#define __UXTB16 __uxtb16 +#define __UXTAB16 __uxtab16 +#define __SXTB16 __sxtb16 +#define __SXTAB16 __sxtab16 +#define __SMUAD __smuad +#define __SMUADX __smuadx +#define __SMLAD __smlad +#define __SMLADX __smladx +#define __SMLALD __smlald +#define __SMLALDX __smlaldx +#define __SMUSD __smusd +#define __SMUSDX __smusdx +#define __SMLSD __smlsd +#define __SMLSDX __smlsdx +#define __SMLSLD __smlsld +#define __SMLSLDX __smlsldx +#define __SEL __sel +#define __QADD __qadd +#define __QSUB __qsub + +#define __PKHBT(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0x0000FFFFUL) | \ + ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL) ) + +#define __PKHTB(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0xFFFF0000UL) | \ + ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL) ) + +#define __SMMLA(ARG1,ARG2,ARG3) ( (int32_t)((((int64_t)(ARG1) * (ARG2)) + \ + ((int64_t)(ARG3) << 32U) ) >> 32U)) + +#define __SXTB16_RORn(ARG1, ARG2) __SXTB16(__ROR(ARG1, ARG2)) + +#define __SXTAB16_RORn(ARG1, ARG2, ARG3) __SXTAB16(ARG1, __ROR(ARG2, ARG3)) + +#endif /* ((defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */ +/*@} end of group CMSIS_SIMD_intrinsics */ + + +#endif /* __CMSIS_ARMCC_H */ diff --git a/lib/cmsis_core/cmsis_armclang.h b/lib/cmsis_core/cmsis_armclang.h new file mode 100644 index 00000000000..139923dab27 --- /dev/null +++ b/lib/cmsis_core/cmsis_armclang.h @@ -0,0 +1,1510 @@ +/**************************************************************************//** + * @file cmsis_armclang.h + * @brief CMSIS compiler armclang (Arm Compiler 6) header file + * @version V5.5.0 + * @date 20. January 2023 + ******************************************************************************/ +/* + * Copyright (c) 2009-2023 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*lint -esym(9058, IRQn)*/ /* disable MISRA 2012 Rule 2.4 for IRQn */ + +#ifndef __CMSIS_ARMCLANG_H +#define __CMSIS_ARMCLANG_H + +#pragma clang system_header /* treat file as system include file */ + +/* CMSIS compiler specific defines */ +#ifndef __ASM + #define __ASM __asm +#endif +#ifndef __INLINE + #define __INLINE __inline +#endif +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static __inline +#endif +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __attribute__((always_inline)) static __inline +#endif +#ifndef __NO_RETURN + #define __NO_RETURN __attribute__((__noreturn__)) +#endif +#ifndef __USED + #define __USED __attribute__((used)) +#endif +#ifndef __WEAK + #define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED + #define __PACKED __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed, aligned(1))) +#endif +#ifndef __UNALIGNED_UINT32 /* deprecated */ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32 */ + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) +#endif +#ifndef __UNALIGNED_UINT16_WRITE + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT16_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_WRITE */ + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT16_READ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT16_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_READ */ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) +#endif +#ifndef __UNALIGNED_UINT32_WRITE + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_WRITE */ + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT32_READ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_READ */ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) +#endif +#ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#ifndef __RESTRICT + #define __RESTRICT __restrict +#endif +#ifndef __COMPILER_BARRIER + #define __COMPILER_BARRIER() __ASM volatile("":::"memory") +#endif +#ifndef __NO_INIT + #define __NO_INIT __attribute__ ((section (".bss.noinit"))) +#endif +#ifndef __ALIAS + #define __ALIAS(x) __attribute__ ((alias(x))) +#endif + + +/* ######################### Startup and Lowlevel Init ######################## */ + +#ifndef __PROGRAM_START +#define __PROGRAM_START __main +#endif + +#ifndef __INITIAL_SP +#define __INITIAL_SP Image$$ARM_LIB_STACK$$ZI$$Limit +#endif + +#ifndef __STACK_LIMIT +#define __STACK_LIMIT Image$$ARM_LIB_STACK$$ZI$$Base +#endif + +#ifndef __VECTOR_TABLE +#define __VECTOR_TABLE __Vectors +#endif + +#ifndef __VECTOR_TABLE_ATTRIBUTE +#define __VECTOR_TABLE_ATTRIBUTE __attribute__((used, section("RESET"))) +#endif + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +#ifndef __STACK_SEAL +#define __STACK_SEAL Image$$STACKSEAL$$ZI$$Base +#endif + +#ifndef __TZ_STACK_SEAL_SIZE +#define __TZ_STACK_SEAL_SIZE 8U +#endif + +#ifndef __TZ_STACK_SEAL_VALUE +#define __TZ_STACK_SEAL_VALUE 0xFEF5EDA5FEF5EDA5ULL +#endif + + +__STATIC_FORCEINLINE void __TZ_set_STACKSEAL_S (uint32_t* stackTop) { + *((uint64_t *)stackTop) = __TZ_STACK_SEAL_VALUE; +} +#endif + + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +/* Define macros for porting to both thumb1 and thumb2. + * For thumb1, use low register (r0-r7), specified by constraint "l" + * Otherwise, use general registers, specified by constraint "r" */ +#if defined (__thumb__) && !defined (__thumb2__) +#define __CMSIS_GCC_OUT_REG(r) "=l" (r) +#define __CMSIS_GCC_RW_REG(r) "+l" (r) +#define __CMSIS_GCC_USE_REG(r) "l" (r) +#else +#define __CMSIS_GCC_OUT_REG(r) "=r" (r) +#define __CMSIS_GCC_RW_REG(r) "+r" (r) +#define __CMSIS_GCC_USE_REG(r) "r" (r) +#endif + +/** + \brief No Operation + \details No Operation does nothing. This instruction can be used for code alignment purposes. + */ +#define __NOP __builtin_arm_nop + +/** + \brief Wait For Interrupt + \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. + */ +#define __WFI __builtin_arm_wfi + + +/** + \brief Wait For Event + \details Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +#define __WFE __builtin_arm_wfe + + +/** + \brief Send Event + \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +#define __SEV __builtin_arm_sev + + +/** + \brief Instruction Synchronization Barrier + \details Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or memory, + after the instruction has been completed. + */ +#define __ISB() __builtin_arm_isb(0xF) + +/** + \brief Data Synchronization Barrier + \details Acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +#define __DSB() __builtin_arm_dsb(0xF) + + +/** + \brief Data Memory Barrier + \details Ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +#define __DMB() __builtin_arm_dmb(0xF) + + +/** + \brief Reverse byte order (32 bit) + \details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REV(value) __builtin_bswap32(value) + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REV16(value) __ROR(__REV(value), 16) + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REVSH(value) (int16_t)__builtin_bswap16(value) + + +/** + \brief Rotate Right in unsigned value (32 bit) + \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + \param [in] op1 Value to rotate + \param [in] op2 Number of Bits to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __ROR(uint32_t op1, uint32_t op2) +{ + op2 %= 32U; + if (op2 == 0U) + { + return op1; + } + return (op1 >> op2) | (op1 << (32U - op2)); +} + + +/** + \brief Breakpoint + \details Causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __ASM volatile ("bkpt "#value) + + +/** + \brief Reverse bit order of value + \details Reverses the bit order of the given value. + \param [in] value Value to reverse + \return Reversed value + */ +#define __RBIT __builtin_arm_rbit + +/** + \brief Count leading zeros + \details Counts the number of leading zeros of a data value. + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +__STATIC_FORCEINLINE uint8_t __CLZ(uint32_t value) +{ + /* Even though __builtin_clz produces a CLZ instruction on ARM, formally + __builtin_clz(0) is undefined behaviour, so handle this case specially. + This guarantees ARM-compatible results if happening to compile on a non-ARM + target, and ensures the compiler doesn't decide to activate any + optimisations using the logic "value was passed to __builtin_clz, so it + is non-zero". + ARM Compiler 6.10 and possibly earlier will optimise this test away, leaving a + single CLZ instruction. + */ + if (value == 0U) + { + return 32U; + } + return __builtin_clz(value); +} + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) + +/** + \brief LDR Exclusive (8 bit) + \details Executes a exclusive LDR instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#define __LDREXB (uint8_t)__builtin_arm_ldrex + + +/** + \brief LDR Exclusive (16 bit) + \details Executes a exclusive LDR instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#define __LDREXH (uint16_t)__builtin_arm_ldrex + + +/** + \brief LDR Exclusive (32 bit) + \details Executes a exclusive LDR instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#define __LDREXW (uint32_t)__builtin_arm_ldrex + + +/** + \brief STR Exclusive (8 bit) + \details Executes a exclusive STR instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXB (uint32_t)__builtin_arm_strex + + +/** + \brief STR Exclusive (16 bit) + \details Executes a exclusive STR instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXH (uint32_t)__builtin_arm_strex + + +/** + \brief STR Exclusive (32 bit) + \details Executes a exclusive STR instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXW (uint32_t)__builtin_arm_strex + + +/** + \brief Remove the exclusive lock + \details Removes the exclusive lock which is created by LDREX. + */ +#define __CLREX __builtin_arm_clrex + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT __builtin_arm_ssat + + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT __builtin_arm_usat + + +/** + \brief Rotate Right with Extend (32 bit) + \details Moves each bit of a bitstring right by one bit. + The carry input is shifted in at the left end of the bitstring. + \param [in] value Value to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __RRX(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rrx %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +} + + +/** + \brief LDRT Unprivileged (8 bit) + \details Executes a Unprivileged LDRT instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDRBT(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (16 bit) + \details Executes a Unprivileged LDRT instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDRHT(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (32 bit) + \details Executes a Unprivileged LDRT instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDRT(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief STRT Unprivileged (8 bit) + \details Executes a Unprivileged STRT instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRBT(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("strbt %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (16 bit) + \details Executes a Unprivileged STRT instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRHT(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("strht %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (32 bit) + \details Executes a Unprivileged STRT instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRT(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("strt %1, %0" : "=Q" (*ptr) : "r" (value) ); +} + +#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +__STATIC_FORCEINLINE int32_t __SSAT(int32_t val, uint32_t sat) +{ + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; +} + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +__STATIC_FORCEINLINE uint32_t __USAT(int32_t val, uint32_t sat) +{ + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) + +/** + \brief Load-Acquire (8 bit) + \details Executes a LDAB instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldab %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire (16 bit) + \details Executes a LDAH instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldah %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire (32 bit) + \details Executes a LDA instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDA(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("lda %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return(result); +} + + +/** + \brief Store-Release (8 bit) + \details Executes a STLB instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLB(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("stlb %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Store-Release (16 bit) + \details Executes a STLH instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLH(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("stlh %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Store-Release (32 bit) + \details Executes a STL instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STL(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("stl %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Load-Acquire Exclusive (8 bit) + \details Executes a LDAB exclusive instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#define __LDAEXB (uint8_t)__builtin_arm_ldaex + + +/** + \brief Load-Acquire Exclusive (16 bit) + \details Executes a LDAH exclusive instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#define __LDAEXH (uint16_t)__builtin_arm_ldaex + + +/** + \brief Load-Acquire Exclusive (32 bit) + \details Executes a LDA exclusive instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#define __LDAEX (uint32_t)__builtin_arm_ldaex + + +/** + \brief Store-Release Exclusive (8 bit) + \details Executes a STLB exclusive instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STLEXB (uint32_t)__builtin_arm_stlex + + +/** + \brief Store-Release Exclusive (16 bit) + \details Executes a STLH exclusive instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STLEXH (uint32_t)__builtin_arm_stlex + + +/** + \brief Store-Release Exclusive (32 bit) + \details Executes a STL exclusive instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STLEX (uint32_t)__builtin_arm_stlex + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + +/** @}*/ /* end of group CMSIS_Core_InstructionInterface */ + + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ + */ + +/** + \brief Enable IRQ Interrupts + \details Enables IRQ interrupts by clearing special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +#ifndef __ARM_COMPAT_H +__STATIC_FORCEINLINE void __enable_irq(void) +{ + __ASM volatile ("cpsie i" : : : "memory"); +} +#endif + + +/** + \brief Disable IRQ Interrupts + \details Disables IRQ interrupts by setting special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +#ifndef __ARM_COMPAT_H +__STATIC_FORCEINLINE void __disable_irq(void) +{ + __ASM volatile ("cpsid i" : : : "memory"); +} +#endif + + +/** + \brief Get Control Register + \details Returns the content of the Control Register. + \return Control Register value + */ +__STATIC_FORCEINLINE uint32_t __get_CONTROL(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Control Register (non-secure) + \details Returns the content of the non-secure Control Register when in secure mode. + \return non-secure Control Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_CONTROL_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Control Register + \details Writes the given value to the Control Register. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control) +{ + __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); + __ISB(); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Control Register (non-secure) + \details Writes the given value to the non-secure Control Register when in secure state. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control) +{ + __ASM volatile ("MSR control_ns, %0" : : "r" (control) : "memory"); + __ISB(); +} +#endif + + +/** + \brief Get IPSR Register + \details Returns the content of the IPSR Register. + \return IPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_IPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get APSR Register + \details Returns the content of the APSR Register. + \return APSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_APSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, apsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get xPSR Register + \details Returns the content of the xPSR Register. + \return xPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_xPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get Process Stack Pointer + \details Returns the current value of the Process Stack Pointer (PSP). + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer (non-secure) + \details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state. + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Process Stack Pointer + \details Assigns the given value to the Process Stack Pointer (PSP). + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_PSP(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp, %0" : : "r" (topOfProcStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state. + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp_ns, %0" : : "r" (topOfProcStack) : ); +} +#endif + + +/** + \brief Get Main Stack Pointer + \details Returns the current value of the Main Stack Pointer (MSP). + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer (non-secure) + \details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state. + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Main Stack Pointer + \details Assigns the given value to the Main Stack Pointer (MSP). + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state. + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp_ns, %0" : : "r" (topOfMainStack) : ); +} +#endif + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Stack Pointer (non-secure) + \details Returns the current value of the non-secure Stack Pointer (SP) when in secure state. + \return SP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_SP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, sp_ns" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Stack Pointer (SP) when in secure state. + \param [in] topOfStack Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack) +{ + __ASM volatile ("MSR sp_ns, %0" : : "r" (topOfStack) : ); +} +#endif + + +/** + \brief Get Priority Mask + \details Returns the current state of the priority mask bit from the Priority Mask Register. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Priority Mask (non-secure) + \details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PRIMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Priority Mask + \details Assigns the given value to the Priority Mask Register. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __set_PRIMASK(uint32_t priMask) +{ + __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Priority Mask (non-secure) + \details Assigns the given value to the non-secure Priority Mask Register when in secure state. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __TZ_set_PRIMASK_NS(uint32_t priMask) +{ + __ASM volatile ("MSR primask_ns, %0" : : "r" (priMask) : "memory"); +} +#endif + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) +/** + \brief Enable FIQ + \details Enables FIQ interrupts by clearing special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_fault_irq(void) +{ + __ASM volatile ("cpsie f" : : : "memory"); +} + + +/** + \brief Disable FIQ + \details Disables FIQ interrupts by setting special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_fault_irq(void) +{ + __ASM volatile ("cpsid f" : : : "memory"); +} + + +/** + \brief Get Base Priority + \details Returns the current value of the Base Priority register. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Base Priority (non-secure) + \details Returns the current value of the non-secure Base Priority register when in secure state. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_BASEPRI_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Base Priority + \details Assigns the given value to the Base Priority register. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri) +{ + __ASM volatile ("MSR basepri, %0" : : "r" (basePri) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Base Priority (non-secure) + \details Assigns the given value to the non-secure Base Priority register when in secure state. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_ns, %0" : : "r" (basePri) : "memory"); +} +#endif + + +/** + \brief Set Base Priority with condition + \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, + or the new value increases the BASEPRI priority level. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_max, %0" : : "r" (basePri) : "memory"); +} + + +/** + \brief Get Fault Mask + \details Returns the current value of the Fault Mask register. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __get_FAULTMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Fault Mask (non-secure) + \details Returns the current value of the non-secure Fault Mask register when in secure state. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_FAULTMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Fault Mask + \details Assigns the given value to the Fault Mask register. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __set_FAULTMASK(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Fault Mask (non-secure) + \details Assigns the given value to the non-secure Fault Mask register when in secure state. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask_ns, %0" : : "r" (faultMask) : "memory"); +} +#endif + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) + +/** + \brief Get Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Process Stack Pointer Limit (PSPLIM). + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSPLIM(void) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim" : "=r" (result) ); + return result; +#endif +} + +#if (defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSPLIM_NS(void) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) ) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Process Stack Pointer Limit (PSPLIM). + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim, %0" : : "r" (ProcStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) ) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim_ns, %0\n" : : "r" (ProcStackPtrLimit)); +#endif +} +#endif + + +/** + \brief Get Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the Main Stack Pointer Limit (MSPLIM). + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSPLIM(void) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim" : "=r" (result) ); + return result; +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state. + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSPLIM_NS(void) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) ) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the Main Stack Pointer Limit (MSPLIM). + \param [in] MainStackPtrLimit Main Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_MSPLIM(uint32_t MainStackPtrLimit) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim, %0" : : "r" (MainStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state. + \param [in] MainStackPtrLimit Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) ) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim_ns, %0" : : "r" (MainStackPtrLimit)); +#endif +} +#endif + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + +/** + \brief Get FPSCR + \details Returns the current value of the Floating Point Status/Control register. + \return Floating Point Status/Control register value + */ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#define __get_FPSCR (uint32_t)__builtin_arm_get_fpscr +#else +#define __get_FPSCR() ((uint32_t)0U) +#endif + +/** + \brief Set FPSCR + \details Assigns the given value to the Floating Point Status/Control register. + \param [in] fpscr Floating Point Status/Control value to set + */ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#define __set_FPSCR __builtin_arm_set_fpscr +#else +#define __set_FPSCR(fpscr) ((void)(fpscr)) +#endif + + +/** @} end of CMSIS_Core_RegAccFunctions */ + + +/* ################### Compiler specific Intrinsics ########################### */ +/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics + Access to dedicated SIMD instructions + @{ +*/ + +#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + +#define __SADD8 __builtin_arm_sadd8 +#define __QADD8 __builtin_arm_qadd8 +#define __SHADD8 __builtin_arm_shadd8 +#define __UADD8 __builtin_arm_uadd8 +#define __UQADD8 __builtin_arm_uqadd8 +#define __UHADD8 __builtin_arm_uhadd8 +#define __SSUB8 __builtin_arm_ssub8 +#define __QSUB8 __builtin_arm_qsub8 +#define __SHSUB8 __builtin_arm_shsub8 +#define __USUB8 __builtin_arm_usub8 +#define __UQSUB8 __builtin_arm_uqsub8 +#define __UHSUB8 __builtin_arm_uhsub8 +#define __SADD16 __builtin_arm_sadd16 +#define __QADD16 __builtin_arm_qadd16 +#define __SHADD16 __builtin_arm_shadd16 +#define __UADD16 __builtin_arm_uadd16 +#define __UQADD16 __builtin_arm_uqadd16 +#define __UHADD16 __builtin_arm_uhadd16 +#define __SSUB16 __builtin_arm_ssub16 +#define __QSUB16 __builtin_arm_qsub16 +#define __SHSUB16 __builtin_arm_shsub16 +#define __USUB16 __builtin_arm_usub16 +#define __UQSUB16 __builtin_arm_uqsub16 +#define __UHSUB16 __builtin_arm_uhsub16 +#define __SASX __builtin_arm_sasx +#define __QASX __builtin_arm_qasx +#define __SHASX __builtin_arm_shasx +#define __UASX __builtin_arm_uasx +#define __UQASX __builtin_arm_uqasx +#define __UHASX __builtin_arm_uhasx +#define __SSAX __builtin_arm_ssax +#define __QSAX __builtin_arm_qsax +#define __SHSAX __builtin_arm_shsax +#define __USAX __builtin_arm_usax +#define __UQSAX __builtin_arm_uqsax +#define __UHSAX __builtin_arm_uhsax +#define __USAD8 __builtin_arm_usad8 +#define __USADA8 __builtin_arm_usada8 +#define __SSAT16 __builtin_arm_ssat16 +#define __USAT16 __builtin_arm_usat16 +#define __UXTB16 __builtin_arm_uxtb16 +#define __UXTAB16 __builtin_arm_uxtab16 +#define __SXTB16 __builtin_arm_sxtb16 +#define __SXTAB16 __builtin_arm_sxtab16 +#define __SMUAD __builtin_arm_smuad +#define __SMUADX __builtin_arm_smuadx +#define __SMLAD __builtin_arm_smlad +#define __SMLADX __builtin_arm_smladx +#define __SMLALD __builtin_arm_smlald +#define __SMLALDX __builtin_arm_smlaldx +#define __SMUSD __builtin_arm_smusd +#define __SMUSDX __builtin_arm_smusdx +#define __SMLSD __builtin_arm_smlsd +#define __SMLSDX __builtin_arm_smlsdx +#define __SMLSLD __builtin_arm_smlsld +#define __SMLSLDX __builtin_arm_smlsldx +#define __SEL __builtin_arm_sel +#define __QADD __builtin_arm_qadd +#define __QSUB __builtin_arm_qsub + +#define __PKHBT(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0x0000FFFFUL) | \ + ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL) ) + +#define __PKHTB(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0xFFFF0000UL) | \ + ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL) ) + +#define __SXTB16_RORn(ARG1, ARG2) __SXTB16(__ROR(ARG1, ARG2)) + +#define __SXTAB16_RORn(ARG1, ARG2, ARG3) __SXTAB16(ARG1, __ROR(ARG2, ARG3)) + +__STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) +{ + int32_t result; + + __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#endif /* (__ARM_FEATURE_DSP == 1) */ +/** @} end of group CMSIS_SIMD_intrinsics */ + + +#endif /* __CMSIS_ARMCLANG_H */ diff --git a/lib/cmsis_core/cmsis_armclang_ltm.h b/lib/cmsis_core/cmsis_armclang_ltm.h new file mode 100644 index 00000000000..477136e8483 --- /dev/null +++ b/lib/cmsis_core/cmsis_armclang_ltm.h @@ -0,0 +1,1934 @@ +/**************************************************************************//** + * @file cmsis_armclang_ltm.h + * @brief CMSIS compiler armclang (Arm Compiler 6) header file + * @version V1.6.0 + * @date 20. January 2023 + ******************************************************************************/ +/* + * Copyright (c) 2018-2023 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*lint -esym(9058, IRQn)*/ /* disable MISRA 2012 Rule 2.4 for IRQn */ + +#ifndef __CMSIS_ARMCLANG_H +#define __CMSIS_ARMCLANG_H + +#pragma clang system_header /* treat file as system include file */ + +/* CMSIS compiler specific defines */ +#ifndef __ASM + #define __ASM __asm +#endif +#ifndef __INLINE + #define __INLINE __inline +#endif +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static __inline +#endif +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __attribute__((always_inline)) static __inline +#endif +#ifndef __NO_RETURN + #define __NO_RETURN __attribute__((__noreturn__)) +#endif +#ifndef __USED + #define __USED __attribute__((used)) +#endif +#ifndef __WEAK + #define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED + #define __PACKED __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed, aligned(1))) +#endif +#ifndef __UNALIGNED_UINT32 /* deprecated */ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32 */ + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) +#endif +#ifndef __UNALIGNED_UINT16_WRITE + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT16_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_WRITE */ + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT16_READ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT16_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_READ */ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) +#endif +#ifndef __UNALIGNED_UINT32_WRITE + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_WRITE */ + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT32_READ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_READ */ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) +#endif +#ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#ifndef __RESTRICT + #define __RESTRICT __restrict +#endif +#ifndef __COMPILER_BARRIER + #define __COMPILER_BARRIER() __ASM volatile("":::"memory") +#endif +#ifndef __NO_INIT + #define __NO_INIT __attribute__ ((section (".bss.noinit"))) +#endif +#ifndef __ALIAS + #define __ALIAS(x) __attribute__ ((alias(x))) +#endif + +/* ######################### Startup and Lowlevel Init ######################## */ + +#ifndef __PROGRAM_START +#define __PROGRAM_START __main +#endif + +#ifndef __INITIAL_SP +#define __INITIAL_SP Image$$ARM_LIB_STACK$$ZI$$Limit +#endif + +#ifndef __STACK_LIMIT +#define __STACK_LIMIT Image$$ARM_LIB_STACK$$ZI$$Base +#endif + +#ifndef __VECTOR_TABLE +#define __VECTOR_TABLE __Vectors +#endif + +#ifndef __VECTOR_TABLE_ATTRIBUTE +#define __VECTOR_TABLE_ATTRIBUTE __attribute__((used, section("RESET"))) +#endif + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +#ifndef __STACK_SEAL +#define __STACK_SEAL Image$$STACKSEAL$$ZI$$Base +#endif + +#ifndef __TZ_STACK_SEAL_SIZE +#define __TZ_STACK_SEAL_SIZE 8U +#endif + +#ifndef __TZ_STACK_SEAL_VALUE +#define __TZ_STACK_SEAL_VALUE 0xFEF5EDA5FEF5EDA5ULL +#endif + + +__STATIC_FORCEINLINE void __TZ_set_STACKSEAL_S (uint32_t* stackTop) { + *((uint64_t *)stackTop) = __TZ_STACK_SEAL_VALUE; +} +#endif + + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +/* Define macros for porting to both thumb1 and thumb2. + * For thumb1, use low register (r0-r7), specified by constraint "l" + * Otherwise, use general registers, specified by constraint "r" */ +#if defined (__thumb__) && !defined (__thumb2__) +#define __CMSIS_GCC_OUT_REG(r) "=l" (r) +#define __CMSIS_GCC_USE_REG(r) "l" (r) +#else +#define __CMSIS_GCC_OUT_REG(r) "=r" (r) +#define __CMSIS_GCC_USE_REG(r) "r" (r) +#endif + +/** + \brief No Operation + \details No Operation does nothing. This instruction can be used for code alignment purposes. + */ +#define __NOP __builtin_arm_nop + +/** + \brief Wait For Interrupt + \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. + */ +#define __WFI __builtin_arm_wfi + + +/** + \brief Wait For Event + \details Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +#define __WFE __builtin_arm_wfe + + +/** + \brief Send Event + \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +#define __SEV __builtin_arm_sev + + +/** + \brief Instruction Synchronization Barrier + \details Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or memory, + after the instruction has been completed. + */ +#define __ISB() __builtin_arm_isb(0xF) + +/** + \brief Data Synchronization Barrier + \details Acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +#define __DSB() __builtin_arm_dsb(0xF) + + +/** + \brief Data Memory Barrier + \details Ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +#define __DMB() __builtin_arm_dmb(0xF) + + +/** + \brief Reverse byte order (32 bit) + \details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REV(value) __builtin_bswap32(value) + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REV16(value) __ROR(__REV(value), 16) + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REVSH(value) (int16_t)__builtin_bswap16(value) + + +/** + \brief Rotate Right in unsigned value (32 bit) + \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + \param [in] op1 Value to rotate + \param [in] op2 Number of Bits to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __ROR(uint32_t op1, uint32_t op2) +{ + op2 %= 32U; + if (op2 == 0U) + { + return op1; + } + return (op1 >> op2) | (op1 << (32U - op2)); +} + + +/** + \brief Breakpoint + \details Causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __ASM volatile ("bkpt "#value) + + +/** + \brief Reverse bit order of value + \details Reverses the bit order of the given value. + \param [in] value Value to reverse + \return Reversed value + */ +#define __RBIT __builtin_arm_rbit + +/** + \brief Count leading zeros + \details Counts the number of leading zeros of a data value. + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +__STATIC_FORCEINLINE uint8_t __CLZ(uint32_t value) +{ + /* Even though __builtin_clz produces a CLZ instruction on ARM, formally + __builtin_clz(0) is undefined behaviour, so handle this case specially. + This guarantees ARM-compatible results if happening to compile on a non-ARM + target, and ensures the compiler doesn't decide to activate any + optimisations using the logic "value was passed to __builtin_clz, so it + is non-zero". + ARM Compiler 6.10 and possibly earlier will optimise this test away, leaving a + single CLZ instruction. + */ + if (value == 0U) + { + return 32U; + } + return __builtin_clz(value); +} + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) +/** + \brief LDR Exclusive (8 bit) + \details Executes a exclusive LDR instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#define __LDREXB (uint8_t)__builtin_arm_ldrex + + +/** + \brief LDR Exclusive (16 bit) + \details Executes a exclusive LDR instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#define __LDREXH (uint16_t)__builtin_arm_ldrex + + +/** + \brief LDR Exclusive (32 bit) + \details Executes a exclusive LDR instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#define __LDREXW (uint32_t)__builtin_arm_ldrex + + +/** + \brief STR Exclusive (8 bit) + \details Executes a exclusive STR instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXB (uint32_t)__builtin_arm_strex + + +/** + \brief STR Exclusive (16 bit) + \details Executes a exclusive STR instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXH (uint32_t)__builtin_arm_strex + + +/** + \brief STR Exclusive (32 bit) + \details Executes a exclusive STR instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXW (uint32_t)__builtin_arm_strex + + +/** + \brief Remove the exclusive lock + \details Removes the exclusive lock which is created by LDREX. + */ +#define __CLREX __builtin_arm_clrex + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT __builtin_arm_ssat + + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT __builtin_arm_usat + + +/** + \brief Rotate Right with Extend (32 bit) + \details Moves each bit of a bitstring right by one bit. + The carry input is shifted in at the left end of the bitstring. + \param [in] value Value to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __RRX(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rrx %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +} + + +/** + \brief LDRT Unprivileged (8 bit) + \details Executes a Unprivileged LDRT instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDRBT(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (16 bit) + \details Executes a Unprivileged LDRT instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDRHT(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (32 bit) + \details Executes a Unprivileged LDRT instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDRT(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief STRT Unprivileged (8 bit) + \details Executes a Unprivileged STRT instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRBT(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("strbt %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (16 bit) + \details Executes a Unprivileged STRT instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRHT(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("strht %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (32 bit) + \details Executes a Unprivileged STRT instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRT(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("strt %1, %0" : "=Q" (*ptr) : "r" (value) ); +} + +#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +__STATIC_FORCEINLINE int32_t __SSAT(int32_t val, uint32_t sat) +{ + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; +} + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +__STATIC_FORCEINLINE uint32_t __USAT(int32_t val, uint32_t sat) +{ + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) +/** + \brief Load-Acquire (8 bit) + \details Executes a LDAB instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldab %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire (16 bit) + \details Executes a LDAH instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldah %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire (32 bit) + \details Executes a LDA instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDA(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("lda %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return(result); +} + + +/** + \brief Store-Release (8 bit) + \details Executes a STLB instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLB(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("stlb %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Store-Release (16 bit) + \details Executes a STLH instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLH(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("stlh %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Store-Release (32 bit) + \details Executes a STL instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STL(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("stl %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Load-Acquire Exclusive (8 bit) + \details Executes a LDAB exclusive instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#define __LDAEXB (uint8_t)__builtin_arm_ldaex + + +/** + \brief Load-Acquire Exclusive (16 bit) + \details Executes a LDAH exclusive instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#define __LDAEXH (uint16_t)__builtin_arm_ldaex + + +/** + \brief Load-Acquire Exclusive (32 bit) + \details Executes a LDA exclusive instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#define __LDAEX (uint32_t)__builtin_arm_ldaex + + +/** + \brief Store-Release Exclusive (8 bit) + \details Executes a STLB exclusive instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STLEXB (uint32_t)__builtin_arm_stlex + + +/** + \brief Store-Release Exclusive (16 bit) + \details Executes a STLH exclusive instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STLEXH (uint32_t)__builtin_arm_stlex + + +/** + \brief Store-Release Exclusive (32 bit) + \details Executes a STL exclusive instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STLEX (uint32_t)__builtin_arm_stlex + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + +/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ + + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ + */ + +/** + \brief Enable IRQ Interrupts + \details Enables IRQ interrupts by clearing special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +#ifndef __ARM_COMPAT_H +__STATIC_FORCEINLINE void __enable_irq(void) +{ + __ASM volatile ("cpsie i" : : : "memory"); +} +#endif + + +/** + \brief Disable IRQ Interrupts + \details Disables IRQ interrupts by setting special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +#ifndef __ARM_COMPAT_H +__STATIC_FORCEINLINE void __disable_irq(void) +{ + __ASM volatile ("cpsid i" : : : "memory"); +} +#endif + + +/** + \brief Get Control Register + \details Returns the content of the Control Register. + \return Control Register value + */ +__STATIC_FORCEINLINE uint32_t __get_CONTROL(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Control Register (non-secure) + \details Returns the content of the non-secure Control Register when in secure mode. + \return non-secure Control Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_CONTROL_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Control Register + \details Writes the given value to the Control Register. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control) +{ + __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); + __ISB(); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Control Register (non-secure) + \details Writes the given value to the non-secure Control Register when in secure state. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control) +{ + __ASM volatile ("MSR control_ns, %0" : : "r" (control) : "memory"); + __ISB(); +} +#endif + + +/** + \brief Get IPSR Register + \details Returns the content of the IPSR Register. + \return IPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_IPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get APSR Register + \details Returns the content of the APSR Register. + \return APSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_APSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, apsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get xPSR Register + \details Returns the content of the xPSR Register. + \return xPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_xPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get Process Stack Pointer + \details Returns the current value of the Process Stack Pointer (PSP). + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer (non-secure) + \details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state. + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Process Stack Pointer + \details Assigns the given value to the Process Stack Pointer (PSP). + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_PSP(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp, %0" : : "r" (topOfProcStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state. + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp_ns, %0" : : "r" (topOfProcStack) : ); +} +#endif + + +/** + \brief Get Main Stack Pointer + \details Returns the current value of the Main Stack Pointer (MSP). + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer (non-secure) + \details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state. + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Main Stack Pointer + \details Assigns the given value to the Main Stack Pointer (MSP). + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state. + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp_ns, %0" : : "r" (topOfMainStack) : ); +} +#endif + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Stack Pointer (non-secure) + \details Returns the current value of the non-secure Stack Pointer (SP) when in secure state. + \return SP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_SP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, sp_ns" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Stack Pointer (SP) when in secure state. + \param [in] topOfStack Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack) +{ + __ASM volatile ("MSR sp_ns, %0" : : "r" (topOfStack) : ); +} +#endif + + +/** + \brief Get Priority Mask + \details Returns the current state of the priority mask bit from the Priority Mask Register. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Priority Mask (non-secure) + \details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PRIMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Priority Mask + \details Assigns the given value to the Priority Mask Register. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __set_PRIMASK(uint32_t priMask) +{ + __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Priority Mask (non-secure) + \details Assigns the given value to the non-secure Priority Mask Register when in secure state. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __TZ_set_PRIMASK_NS(uint32_t priMask) +{ + __ASM volatile ("MSR primask_ns, %0" : : "r" (priMask) : "memory"); +} +#endif + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) +/** + \brief Enable FIQ + \details Enables FIQ interrupts by clearing special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_fault_irq(void) +{ + __ASM volatile ("cpsie f" : : : "memory"); +} + + +/** + \brief Disable FIQ + \details Disables FIQ interrupts by setting special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_fault_irq(void) +{ + __ASM volatile ("cpsid f" : : : "memory"); +} + + +/** + \brief Get Base Priority + \details Returns the current value of the Base Priority register. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Base Priority (non-secure) + \details Returns the current value of the non-secure Base Priority register when in secure state. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_BASEPRI_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Base Priority + \details Assigns the given value to the Base Priority register. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri) +{ + __ASM volatile ("MSR basepri, %0" : : "r" (basePri) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Base Priority (non-secure) + \details Assigns the given value to the non-secure Base Priority register when in secure state. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_ns, %0" : : "r" (basePri) : "memory"); +} +#endif + + +/** + \brief Set Base Priority with condition + \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, + or the new value increases the BASEPRI priority level. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_max, %0" : : "r" (basePri) : "memory"); +} + + +/** + \brief Get Fault Mask + \details Returns the current value of the Fault Mask register. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __get_FAULTMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Fault Mask (non-secure) + \details Returns the current value of the non-secure Fault Mask register when in secure state. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_FAULTMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Fault Mask + \details Assigns the given value to the Fault Mask register. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __set_FAULTMASK(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Fault Mask (non-secure) + \details Assigns the given value to the non-secure Fault Mask register when in secure state. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask_ns, %0" : : "r" (faultMask) : "memory"); +} +#endif + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) + +/** + \brief Get Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Process Stack Pointer Limit (PSPLIM). + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSPLIM(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim" : "=r" (result) ); + return result; +#endif +} + +#if (defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSPLIM_NS(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Process Stack Pointer Limit (PSPLIM). + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim, %0" : : "r" (ProcStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim_ns, %0\n" : : "r" (ProcStackPtrLimit)); +#endif +} +#endif + + +/** + \brief Get Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the Main Stack Pointer Limit (MSPLIM). + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSPLIM(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim" : "=r" (result) ); + return result; +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state. + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSPLIM_NS(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the Main Stack Pointer Limit (MSPLIM). + \param [in] MainStackPtrLimit Main Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_MSPLIM(uint32_t MainStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim, %0" : : "r" (MainStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state. + \param [in] MainStackPtrLimit Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim_ns, %0" : : "r" (MainStackPtrLimit)); +#endif +} +#endif + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + +/** + \brief Get FPSCR + \details Returns the current value of the Floating Point Status/Control register. + \return Floating Point Status/Control register value + */ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#define __get_FPSCR (uint32_t)__builtin_arm_get_fpscr +#else +#define __get_FPSCR() ((uint32_t)0U) +#endif + +/** + \brief Set FPSCR + \details Assigns the given value to the Floating Point Status/Control register. + \param [in] fpscr Floating Point Status/Control value to set + */ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#define __set_FPSCR __builtin_arm_set_fpscr +#else +#define __set_FPSCR(x) ((void)(x)) +#endif + + +/*@} end of CMSIS_Core_RegAccFunctions */ + + +/* ################### Compiler specific Intrinsics ########################### */ +/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics + Access to dedicated SIMD instructions + @{ +*/ + +#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + +__STATIC_FORCEINLINE uint32_t __SADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__STATIC_FORCEINLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__STATIC_FORCEINLINE uint32_t __SADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("qsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("shsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uqsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uhsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USAD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usad8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("usada8 %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#define __SSAT16(ARG1,ARG2) \ +({ \ + int32_t __RES, __ARG1 = (ARG1); \ + __ASM ("ssat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + +#define __USAT16(ARG1,ARG2) \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM ("usat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) ); \ + __RES; \ + }) + +__STATIC_FORCEINLINE uint32_t __UXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM volatile ("uxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM volatile ("sxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUAD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuad %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuadx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlad %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smladx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint32_t __SMUSD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusdx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsd %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsdx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint32_t __SEL (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sel %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE int32_t __QADD( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qadd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE int32_t __QSUB( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qsub %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +#define __PKHBT(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0x0000FFFFUL) | \ + ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL) ) + +#define __PKHTB(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0xFFFF0000UL) | \ + ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL) ) + +#define __SXTB16_RORn(ARG1, ARG2) __SXTB16(__ROR(ARG1, ARG2)) + +#define __SXTAB16_RORn(ARG1, ARG2, ARG3) __SXTAB16(ARG1, __ROR(ARG2, ARG3)) + +__STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) +{ + int32_t result; + + __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#endif /* (__ARM_FEATURE_DSP == 1) */ +/*@} end of group CMSIS_SIMD_intrinsics */ + + +#endif /* __CMSIS_ARMCLANG_H */ diff --git a/lib/cmsis_core/cmsis_compiler.h b/lib/cmsis_core/cmsis_compiler.h new file mode 100644 index 00000000000..192f9b7c931 --- /dev/null +++ b/lib/cmsis_core/cmsis_compiler.h @@ -0,0 +1,303 @@ +/**************************************************************************//** + * @file cmsis_compiler.h + * @brief CMSIS compiler generic header file + * @version V5.3.0 + * @date 04. April 2023 + ******************************************************************************/ +/* + * Copyright (c) 2009-2023 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CMSIS_COMPILER_H +#define __CMSIS_COMPILER_H + +#include + +/* + * Arm Compiler 4/5 + */ +#if defined ( __CC_ARM ) + #include "cmsis_armcc.h" + + +/* + * Arm Compiler 6.6 LTM (armclang) + */ +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) && (__ARMCC_VERSION < 6100100) + #include "cmsis_armclang_ltm.h" + + /* + * Arm Compiler above 6.10.1 (armclang) + */ +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6100100) + #include "cmsis_armclang.h" + +/* + * TI Arm Clang Compiler (tiarmclang) + */ +#elif defined (__ti__) + #include "cmsis_tiarmclang.h" + +/* + * GNU Compiler + */ +#elif defined ( __GNUC__ ) + #include "cmsis_gcc.h" + + +/* + * IAR Compiler + */ +#elif defined ( __ICCARM__ ) + #include + + +/* + * TI Arm Compiler (armcl) + */ +#elif defined ( __TI_ARM__ ) + #include + + #ifndef __ASM + #define __ASM __asm + #endif + #ifndef __INLINE + #define __INLINE inline + #endif + #ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline + #endif + #ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __STATIC_INLINE + #endif + #ifndef __NO_RETURN + #define __NO_RETURN __attribute__((noreturn)) + #endif + #ifndef __USED + #define __USED __attribute__((used)) + #endif + #ifndef __WEAK + #define __WEAK __attribute__((weak)) + #endif + #ifndef __PACKED + #define __PACKED __attribute__((packed)) + #endif + #ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed)) + #endif + #ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed)) + #endif + #ifndef __UNALIGNED_UINT32 /* deprecated */ + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) + #endif + #ifndef __UNALIGNED_UINT16_WRITE + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void*)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT16_READ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) + #endif + #ifndef __UNALIGNED_UINT32_WRITE + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT32_READ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) + #endif + #ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) + #endif + #ifndef __RESTRICT + #define __RESTRICT __restrict + #endif + #ifndef __COMPILER_BARRIER + #warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored. + #define __COMPILER_BARRIER() (void)0 + #endif + #ifndef __NO_INIT + #define __NO_INIT __attribute__ ((section (".bss.noinit"))) + #endif + #ifndef __ALIAS + #define __ALIAS(x) __attribute__ ((alias(x))) + #endif + +/* + * TASKING Compiler + */ +#elif defined ( __TASKING__ ) + /* + * The CMSIS functions have been implemented as intrinsics in the compiler. + * Please use "carm -?i" to get an up to date list of all intrinsics, + * Including the CMSIS ones. + */ + + #ifndef __ASM + #define __ASM __asm + #endif + #ifndef __INLINE + #define __INLINE inline + #endif + #ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline + #endif + #ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __STATIC_INLINE + #endif + #ifndef __NO_RETURN + #define __NO_RETURN __attribute__((noreturn)) + #endif + #ifndef __USED + #define __USED __attribute__((used)) + #endif + #ifndef __WEAK + #define __WEAK __attribute__((weak)) + #endif + #ifndef __PACKED + #define __PACKED __packed__ + #endif + #ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __packed__ + #endif + #ifndef __PACKED_UNION + #define __PACKED_UNION union __packed__ + #endif + #ifndef __UNALIGNED_UINT32 /* deprecated */ + struct __packed__ T_UINT32 { uint32_t v; }; + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) + #endif + #ifndef __UNALIGNED_UINT16_WRITE + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT16_READ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) + #endif + #ifndef __UNALIGNED_UINT32_WRITE + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT32_READ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) + #endif + #ifndef __ALIGNED + #define __ALIGNED(x) __align(x) + #endif + #ifndef __RESTRICT + #warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored. + #define __RESTRICT + #endif + #ifndef __COMPILER_BARRIER + #warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored. + #define __COMPILER_BARRIER() (void)0 + #endif + #ifndef __NO_INIT + #define __NO_INIT __attribute__ ((section (".bss.noinit"))) + #endif + #ifndef __ALIAS + #define __ALIAS(x) __attribute__ ((alias(x))) + #endif + +/* + * COSMIC Compiler + */ +#elif defined ( __CSMC__ ) + #include + + #ifndef __ASM + #define __ASM _asm + #endif + #ifndef __INLINE + #define __INLINE inline + #endif + #ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline + #endif + #ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __STATIC_INLINE + #endif + #ifndef __NO_RETURN + // NO RETURN is automatically detected hence no warning here + #define __NO_RETURN + #endif + #ifndef __USED + #warning No compiler specific solution for __USED. __USED is ignored. + #define __USED + #endif + #ifndef __WEAK + #define __WEAK __weak + #endif + #ifndef __PACKED + #define __PACKED @packed + #endif + #ifndef __PACKED_STRUCT + #define __PACKED_STRUCT @packed struct + #endif + #ifndef __PACKED_UNION + #define __PACKED_UNION @packed union + #endif + #ifndef __UNALIGNED_UINT32 /* deprecated */ + @packed struct T_UINT32 { uint32_t v; }; + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) + #endif + #ifndef __UNALIGNED_UINT16_WRITE + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT16_READ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) + #endif + #ifndef __UNALIGNED_UINT32_WRITE + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) + #endif + #ifndef __UNALIGNED_UINT32_READ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) + #endif + #ifndef __ALIGNED + #warning No compiler specific solution for __ALIGNED. __ALIGNED is ignored. + #define __ALIGNED(x) + #endif + #ifndef __RESTRICT + #warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored. + #define __RESTRICT + #endif + #ifndef __COMPILER_BARRIER + #warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored. + #define __COMPILER_BARRIER() (void)0 + #endif + #ifndef __NO_INIT + #define __NO_INIT __attribute__ ((section (".bss.noinit"))) + #endif + #ifndef __ALIAS + #define __ALIAS(x) __attribute__ ((alias(x))) + #endif + +#else + #error Unknown compiler. +#endif + + +#endif /* __CMSIS_COMPILER_H */ + diff --git a/lib/cmsis_core/cmsis_gcc.h b/lib/cmsis_core/cmsis_gcc.h new file mode 100644 index 00000000000..4f0762d6dc4 --- /dev/null +++ b/lib/cmsis_core/cmsis_gcc.h @@ -0,0 +1,2217 @@ +/**************************************************************************//** + * @file cmsis_gcc.h + * @brief CMSIS compiler GCC header file + * @version V5.4.2 + * @date 17. December 2022 + ******************************************************************************/ +/* + * Copyright (c) 2009-2021 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CMSIS_GCC_H +#define __CMSIS_GCC_H + +/* ignore some GCC warnings */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wunused-parameter" + +/* Fallback for __has_builtin */ +#ifndef __has_builtin + #define __has_builtin(x) (0) +#endif + +/* CMSIS compiler specific defines */ +#ifndef __ASM + #define __ASM __asm +#endif +#ifndef __INLINE + #define __INLINE inline +#endif +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline +#endif +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __attribute__((always_inline)) static inline +#endif +#ifndef __NO_RETURN + #define __NO_RETURN __attribute__((__noreturn__)) +#endif +#ifndef __USED + #define __USED __attribute__((used)) +#endif +#ifndef __WEAK + #define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED + #define __PACKED __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed, aligned(1))) +#endif +#ifndef __UNALIGNED_UINT32 /* deprecated */ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) +#endif +#ifndef __UNALIGNED_UINT16_WRITE + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT16_READ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) +#endif +#ifndef __UNALIGNED_UINT32_WRITE + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT32_READ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpacked" + #pragma GCC diagnostic ignored "-Wattributes" + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #pragma GCC diagnostic pop + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) +#endif +#ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#ifndef __RESTRICT + #define __RESTRICT __restrict +#endif +#ifndef __COMPILER_BARRIER + #define __COMPILER_BARRIER() __ASM volatile("":::"memory") +#endif +#ifndef __NO_INIT + #define __NO_INIT __attribute__ ((section (".bss.noinit"))) +#endif +#ifndef __ALIAS + #define __ALIAS(x) __attribute__ ((alias(x))) +#endif + +/* ######################### Startup and Lowlevel Init ######################## */ + +#ifndef __PROGRAM_START + +/** + \brief Initializes data and bss sections + \details This default implementations initialized all data and additional bss + sections relying on .copy.table and .zero.table specified properly + in the used linker script. + + */ +__STATIC_FORCEINLINE __NO_RETURN void __cmsis_start(void) +{ + extern void _start(void) __NO_RETURN; + + typedef struct __copy_table { + uint32_t const* src; + uint32_t* dest; + uint32_t wlen; + } __copy_table_t; + + typedef struct __zero_table { + uint32_t* dest; + uint32_t wlen; + } __zero_table_t; + + extern const __copy_table_t __copy_table_start__; + extern const __copy_table_t __copy_table_end__; + extern const __zero_table_t __zero_table_start__; + extern const __zero_table_t __zero_table_end__; + + for (__copy_table_t const* pTable = &__copy_table_start__; pTable < &__copy_table_end__; ++pTable) { + for(uint32_t i=0u; iwlen; ++i) { + pTable->dest[i] = pTable->src[i]; + } + } + + for (__zero_table_t const* pTable = &__zero_table_start__; pTable < &__zero_table_end__; ++pTable) { + for(uint32_t i=0u; iwlen; ++i) { + pTable->dest[i] = 0u; + } + } + + _start(); +} + +#define __PROGRAM_START __cmsis_start +#endif + +#ifndef __INITIAL_SP +#define __INITIAL_SP __StackTop +#endif + +#ifndef __STACK_LIMIT +#define __STACK_LIMIT __StackLimit +#endif + +#ifndef __VECTOR_TABLE +#define __VECTOR_TABLE __Vectors +#endif + +#ifndef __VECTOR_TABLE_ATTRIBUTE +#define __VECTOR_TABLE_ATTRIBUTE __attribute__((used, section(".vectors"))) +#endif + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +#ifndef __STACK_SEAL +#define __STACK_SEAL __StackSeal +#endif + +#ifndef __TZ_STACK_SEAL_SIZE +#define __TZ_STACK_SEAL_SIZE 8U +#endif + +#ifndef __TZ_STACK_SEAL_VALUE +#define __TZ_STACK_SEAL_VALUE 0xFEF5EDA5FEF5EDA5ULL +#endif + + +__STATIC_FORCEINLINE void __TZ_set_STACKSEAL_S (uint32_t* stackTop) { + *((uint64_t *)stackTop) = __TZ_STACK_SEAL_VALUE; +} +#endif + + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +/* Define macros for porting to both thumb1 and thumb2. + * For thumb1, use low register (r0-r7), specified by constraint "l" + * Otherwise, use general registers, specified by constraint "r" */ +#if defined (__thumb__) && !defined (__thumb2__) +#define __CMSIS_GCC_OUT_REG(r) "=l" (r) +#define __CMSIS_GCC_RW_REG(r) "+l" (r) +#define __CMSIS_GCC_USE_REG(r) "l" (r) +#else +#define __CMSIS_GCC_OUT_REG(r) "=r" (r) +#define __CMSIS_GCC_RW_REG(r) "+r" (r) +#define __CMSIS_GCC_USE_REG(r) "r" (r) +#endif + +/** + \brief No Operation + \details No Operation does nothing. This instruction can be used for code alignment purposes. + */ +#define __NOP() __ASM volatile ("nop") + +/** + \brief Wait For Interrupt + \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. + */ +#define __WFI() __ASM volatile ("wfi":::"memory") + + +/** + \brief Wait For Event + \details Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +#define __WFE() __ASM volatile ("wfe":::"memory") + + +/** + \brief Send Event + \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +#define __SEV() __ASM volatile ("sev") + + +/** + \brief Instruction Synchronization Barrier + \details Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or memory, + after the instruction has been completed. + */ +__STATIC_FORCEINLINE void __ISB(void) +{ + __ASM volatile ("isb 0xF":::"memory"); +} + + +/** + \brief Data Synchronization Barrier + \details Acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +__STATIC_FORCEINLINE void __DSB(void) +{ + __ASM volatile ("dsb 0xF":::"memory"); +} + + +/** + \brief Data Memory Barrier + \details Ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +__STATIC_FORCEINLINE void __DMB(void) +{ + __ASM volatile ("dmb 0xF":::"memory"); +} + + +/** + \brief Reverse byte order (32 bit) + \details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __REV(uint32_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) + return __builtin_bswap32(value); +#else + uint32_t result; + + __ASM ("rev %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +#endif +} + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __REV16(uint32_t value) +{ + uint32_t result; + + __ASM ("rev16 %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +} + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE int16_t __REVSH(int16_t value) +{ +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + return (int16_t)__builtin_bswap16(value); +#else + int16_t result; + + __ASM ("revsh %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return result; +#endif +} + + +/** + \brief Rotate Right in unsigned value (32 bit) + \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + \param [in] op1 Value to rotate + \param [in] op2 Number of Bits to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __ROR(uint32_t op1, uint32_t op2) +{ + op2 %= 32U; + if (op2 == 0U) + { + return op1; + } + return (op1 >> op2) | (op1 << (32U - op2)); +} + + +/** + \brief Breakpoint + \details Causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __ASM volatile ("bkpt "#value) + + +/** + \brief Reverse bit order of value + \details Reverses the bit order of the given value. + \param [in] value Value to reverse + \return Reversed value + */ +__STATIC_FORCEINLINE uint32_t __RBIT(uint32_t value) +{ + uint32_t result; + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) + __ASM ("rbit %0, %1" : "=r" (result) : "r" (value) ); +#else + uint32_t s = (4U /*sizeof(v)*/ * 8U) - 1U; /* extra shift needed at end */ + + result = value; /* r will be reversed bits of v; first get LSB of v */ + for (value >>= 1U; value != 0U; value >>= 1U) + { + result <<= 1U; + result |= value & 1U; + s--; + } + result <<= s; /* shift when v's highest bits are zero */ +#endif + return result; +} + + +/** + \brief Count leading zeros + \details Counts the number of leading zeros of a data value. + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +__STATIC_FORCEINLINE uint8_t __CLZ(uint32_t value) +{ + /* Even though __builtin_clz produces a CLZ instruction on ARM, formally + __builtin_clz(0) is undefined behaviour, so handle this case specially. + This guarantees ARM-compatible results if happening to compile on a non-ARM + target, and ensures the compiler doesn't decide to activate any + optimisations using the logic "value was passed to __builtin_clz, so it + is non-zero". + ARM GCC 7.3 and possibly earlier will optimise this test away, leaving a + single CLZ instruction. + */ + if (value == 0U) + { + return 32U; + } + return __builtin_clz(value); +} + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) +/** + \brief LDR Exclusive (8 bit) + \details Executes a exclusive LDR instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDREXB(volatile uint8_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexb %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexb %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDR Exclusive (16 bit) + \details Executes a exclusive LDR instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDREXH(volatile uint16_t *addr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrexh %0, %1" : "=r" (result) : "Q" (*addr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrexh %0, [%1]" : "=r" (result) : "r" (addr) : "memory" ); +#endif + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDR Exclusive (32 bit) + \details Executes a exclusive LDR instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDREXW(volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("ldrex %0, %1" : "=r" (result) : "Q" (*addr) ); + return(result); +} + + +/** + \brief STR Exclusive (8 bit) + \details Executes a exclusive STR instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXB(uint8_t value, volatile uint8_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexb %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief STR Exclusive (16 bit) + \details Executes a exclusive STR instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXH(uint16_t value, volatile uint16_t *addr) +{ + uint32_t result; + + __ASM volatile ("strexh %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" ((uint32_t)value) ); + return(result); +} + + +/** + \brief STR Exclusive (32 bit) + \details Executes a exclusive STR instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STREXW(uint32_t value, volatile uint32_t *addr) +{ + uint32_t result; + + __ASM volatile ("strex %0, %2, %1" : "=&r" (result), "=Q" (*addr) : "r" (value) ); + return(result); +} + + +/** + \brief Remove the exclusive lock + \details Removes the exclusive lock which is created by LDREX. + */ +__STATIC_FORCEINLINE void __CLREX(void) +{ + __ASM volatile ("clrex" ::: "memory"); +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] ARG1 Value to be saturated + \param [in] ARG2 Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT(ARG1, ARG2) \ +__extension__ \ +({ \ + int32_t __RES, __ARG1 = (ARG1); \ + __ASM volatile ("ssat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) : "cc" ); \ + __RES; \ + }) + + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] ARG1 Value to be saturated + \param [in] ARG2 Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT(ARG1, ARG2) \ +__extension__ \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM volatile ("usat %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) : "cc" ); \ + __RES; \ + }) + + +/** + \brief Rotate Right with Extend (32 bit) + \details Moves each bit of a bitstring right by one bit. + The carry input is shifted in at the left end of the bitstring. + \param [in] value Value to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __RRX(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rrx %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +} + + +/** + \brief LDRT Unprivileged (8 bit) + \details Executes a Unprivileged LDRT instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDRBT(volatile uint8_t *ptr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*ptr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrbt %0, [%1]" : "=r" (result) : "r" (ptr) : "memory" ); +#endif + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (16 bit) + \details Executes a Unprivileged LDRT instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDRHT(volatile uint16_t *ptr) +{ + uint32_t result; + +#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*ptr) ); +#else + /* Prior to GCC 4.8, "Q" will be expanded to [rx, #0] which is not + accepted by assembler. So has to use following less efficient pattern. + */ + __ASM volatile ("ldrht %0, [%1]" : "=r" (result) : "r" (ptr) : "memory" ); +#endif + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (32 bit) + \details Executes a Unprivileged LDRT instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDRT(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief STRT Unprivileged (8 bit) + \details Executes a Unprivileged STRT instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRBT(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("strbt %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (16 bit) + \details Executes a Unprivileged STRT instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRHT(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("strht %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (32 bit) + \details Executes a Unprivileged STRT instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRT(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("strt %1, %0" : "=Q" (*ptr) : "r" (value) ); +} + +#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +__STATIC_FORCEINLINE int32_t __SSAT(int32_t val, uint32_t sat) +{ + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; +} + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +__STATIC_FORCEINLINE uint32_t __USAT(int32_t val, uint32_t sat) +{ + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) +/** + \brief Load-Acquire (8 bit) + \details Executes a LDAB instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldab %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire (16 bit) + \details Executes a LDAH instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldah %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire (32 bit) + \details Executes a LDA instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDA(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("lda %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return(result); +} + + +/** + \brief Store-Release (8 bit) + \details Executes a STLB instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLB(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("stlb %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Store-Release (16 bit) + \details Executes a STLH instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLH(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("stlh %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Store-Release (32 bit) + \details Executes a STL instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STL(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("stl %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Load-Acquire Exclusive (8 bit) + \details Executes a LDAB exclusive instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAEXB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaexb %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire Exclusive (16 bit) + \details Executes a LDAH exclusive instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAEXH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaexh %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire Exclusive (32 bit) + \details Executes a LDA exclusive instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDAEX(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldaex %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return(result); +} + + +/** + \brief Store-Release Exclusive (8 bit) + \details Executes a STLB exclusive instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEXB(uint8_t value, volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlexb %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); + return(result); +} + + +/** + \brief Store-Release Exclusive (16 bit) + \details Executes a STLH exclusive instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEXH(uint16_t value, volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlexh %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); + return(result); +} + + +/** + \brief Store-Release Exclusive (32 bit) + \details Executes a STL exclusive instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +__STATIC_FORCEINLINE uint32_t __STLEX(uint32_t value, volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("stlex %0, %2, %1" : "=&r" (result), "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); + return(result); +} + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + +/*@}*/ /* end of group CMSIS_Core_InstructionInterface */ + + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ + */ + +/** + \brief Enable IRQ Interrupts + \details Enables IRQ interrupts by clearing special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_irq(void) +{ + __ASM volatile ("cpsie i" : : : "memory"); +} + + +/** + \brief Disable IRQ Interrupts + \details Disables IRQ interrupts by setting special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_irq(void) +{ + __ASM volatile ("cpsid i" : : : "memory"); +} + + +/** + \brief Get Control Register + \details Returns the content of the Control Register. + \return Control Register value + */ +__STATIC_FORCEINLINE uint32_t __get_CONTROL(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Control Register (non-secure) + \details Returns the content of the non-secure Control Register when in secure mode. + \return non-secure Control Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_CONTROL_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Control Register + \details Writes the given value to the Control Register. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control) +{ + __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); + __ISB(); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Control Register (non-secure) + \details Writes the given value to the non-secure Control Register when in secure state. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control) +{ + __ASM volatile ("MSR control_ns, %0" : : "r" (control) : "memory"); + __ISB(); +} +#endif + + +/** + \brief Get IPSR Register + \details Returns the content of the IPSR Register. + \return IPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_IPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get APSR Register + \details Returns the content of the APSR Register. + \return APSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_APSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, apsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get xPSR Register + \details Returns the content of the xPSR Register. + \return xPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_xPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get Process Stack Pointer + \details Returns the current value of the Process Stack Pointer (PSP). + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer (non-secure) + \details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state. + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Process Stack Pointer + \details Assigns the given value to the Process Stack Pointer (PSP). + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_PSP(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp, %0" : : "r" (topOfProcStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state. + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp_ns, %0" : : "r" (topOfProcStack) : ); +} +#endif + + +/** + \brief Get Main Stack Pointer + \details Returns the current value of the Main Stack Pointer (MSP). + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer (non-secure) + \details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state. + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Main Stack Pointer + \details Assigns the given value to the Main Stack Pointer (MSP). + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state. + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp_ns, %0" : : "r" (topOfMainStack) : ); +} +#endif + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Stack Pointer (non-secure) + \details Returns the current value of the non-secure Stack Pointer (SP) when in secure state. + \return SP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_SP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, sp_ns" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Stack Pointer (SP) when in secure state. + \param [in] topOfStack Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack) +{ + __ASM volatile ("MSR sp_ns, %0" : : "r" (topOfStack) : ); +} +#endif + + +/** + \brief Get Priority Mask + \details Returns the current state of the priority mask bit from the Priority Mask Register. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Priority Mask (non-secure) + \details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PRIMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Priority Mask + \details Assigns the given value to the Priority Mask Register. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __set_PRIMASK(uint32_t priMask) +{ + __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Priority Mask (non-secure) + \details Assigns the given value to the non-secure Priority Mask Register when in secure state. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __TZ_set_PRIMASK_NS(uint32_t priMask) +{ + __ASM volatile ("MSR primask_ns, %0" : : "r" (priMask) : "memory"); +} +#endif + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) +/** + \brief Enable FIQ + \details Enables FIQ interrupts by clearing special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_fault_irq(void) +{ + __ASM volatile ("cpsie f" : : : "memory"); +} + + +/** + \brief Disable FIQ + \details Disables FIQ interrupts by setting special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_fault_irq(void) +{ + __ASM volatile ("cpsid f" : : : "memory"); +} + + +/** + \brief Get Base Priority + \details Returns the current value of the Base Priority register. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Base Priority (non-secure) + \details Returns the current value of the non-secure Base Priority register when in secure state. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_BASEPRI_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Base Priority + \details Assigns the given value to the Base Priority register. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri) +{ + __ASM volatile ("MSR basepri, %0" : : "r" (basePri) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Base Priority (non-secure) + \details Assigns the given value to the non-secure Base Priority register when in secure state. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_ns, %0" : : "r" (basePri) : "memory"); +} +#endif + + +/** + \brief Set Base Priority with condition + \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, + or the new value increases the BASEPRI priority level. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_max, %0" : : "r" (basePri) : "memory"); +} + + +/** + \brief Get Fault Mask + \details Returns the current value of the Fault Mask register. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __get_FAULTMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Fault Mask (non-secure) + \details Returns the current value of the non-secure Fault Mask register when in secure state. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_FAULTMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Fault Mask + \details Assigns the given value to the Fault Mask register. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __set_FAULTMASK(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Fault Mask (non-secure) + \details Assigns the given value to the non-secure Fault Mask register when in secure state. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask_ns, %0" : : "r" (faultMask) : "memory"); +} +#endif + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) + +/** + \brief Get Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Process Stack Pointer Limit (PSPLIM). + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSPLIM(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim" : "=r" (result) ); + return result; +#endif +} + +#if (defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSPLIM_NS(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Process Stack Pointer Limit (PSPLIM). + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim, %0" : : "r" (ProcStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim_ns, %0\n" : : "r" (ProcStackPtrLimit)); +#endif +} +#endif + + +/** + \brief Get Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Main Stack Pointer Limit (MSPLIM). + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSPLIM(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim" : "=r" (result) ); + return result; +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state. + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSPLIM_NS(void) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Main Stack Pointer Limit (MSPLIM). + \param [in] MainStackPtrLimit Main Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_MSPLIM(uint32_t MainStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim, %0" : : "r" (MainStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state. + \param [in] MainStackPtrLimit Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit) +{ +#if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim_ns, %0" : : "r" (MainStackPtrLimit)); +#endif +} +#endif + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) */ + + +/** + \brief Get FPSCR + \details Returns the current value of the Floating Point Status/Control register. + \return Floating Point Status/Control register value + */ +__STATIC_FORCEINLINE uint32_t __get_FPSCR(void) +{ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#if __has_builtin(__builtin_arm_get_fpscr) +// Re-enable using built-in when GCC has been fixed +// || (__GNUC__ > 7) || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2) + /* see https://gcc.gnu.org/ml/gcc-patches/2017-04/msg00443.html */ + return __builtin_arm_get_fpscr(); +#else + uint32_t result; + + __ASM volatile ("VMRS %0, fpscr" : "=r" (result) ); + return(result); +#endif +#else + return(0U); +#endif +} + + +/** + \brief Set FPSCR + \details Assigns the given value to the Floating Point Status/Control register. + \param [in] fpscr Floating Point Status/Control value to set + */ +__STATIC_FORCEINLINE void __set_FPSCR(uint32_t fpscr) +{ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#if __has_builtin(__builtin_arm_set_fpscr) +// Re-enable using built-in when GCC has been fixed +// || (__GNUC__ > 7) || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2) + /* see https://gcc.gnu.org/ml/gcc-patches/2017-04/msg00443.html */ + __builtin_arm_set_fpscr(fpscr); +#else + __ASM volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc", "memory"); +#endif +#else + (void)fpscr; +#endif +} + + +/*@} end of CMSIS_Core_RegAccFunctions */ + + +/* ################### Compiler specific Intrinsics ########################### */ +/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics + Access to dedicated SIMD instructions + @{ +*/ + +#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + +__STATIC_FORCEINLINE uint32_t __SADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("qadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("shadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uqadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHADD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uhadd8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__STATIC_FORCEINLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("qsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("shsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uqsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSUB8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uhsub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +__STATIC_FORCEINLINE uint32_t __SADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("qadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("shadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uqadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHADD16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uhadd16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("qsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("shsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uqsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSUB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uhsub16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("qasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("shasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("uasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uqasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHASX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uhasx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("ssax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __QSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("qsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("shsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("usax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UQSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uqsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UHSAX(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uhsax %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USAD8(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("usad8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __USADA8(uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM ("usada8 %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#define __SSAT16(ARG1, ARG2) \ +__extension__ \ +({ \ + int32_t __RES, __ARG1 = (ARG1); \ + __ASM volatile ("ssat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) : "cc" ); \ + __RES; \ + }) + +#define __USAT16(ARG1, ARG2) \ +__extension__ \ +({ \ + uint32_t __RES, __ARG1 = (ARG1); \ + __ASM volatile ("usat16 %0, %1, %2" : "=r" (__RES) : "I" (ARG2), "r" (__ARG1) : "cc" ); \ + __RES; \ + }) + +__STATIC_FORCEINLINE uint32_t __UXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM ("uxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __UXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("uxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTB16(uint32_t op1) +{ + uint32_t result; + + __ASM ("sxtb16 %0, %1" : "=r" (result) : "r" (op1)); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTB16_RORn(uint32_t op1, uint32_t rotate) +{ + uint32_t result; + if (__builtin_constant_p(rotate) && ((rotate == 8U) || (rotate == 16U) || (rotate == 24U))) { + __ASM volatile ("sxtb16 %0, %1, ROR %2" : "=r" (result) : "r" (op1), "i" (rotate) ); + } else { + result = __SXTB16(__ROR(op1, rotate)) ; + } + return result; +} + +__STATIC_FORCEINLINE uint32_t __SXTAB16(uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM ("sxtab16 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SXTAB16_RORn(uint32_t op1, uint32_t op2, uint32_t rotate) +{ + uint32_t result; + if (__builtin_constant_p(rotate) && ((rotate == 8U) || (rotate == 16U) || (rotate == 24U))) { + __ASM volatile ("sxtab16 %0, %1, %2, ROR %3" : "=r" (result) : "r" (op1) , "r" (op2) , "i" (rotate)); + } else { + result = __SXTAB16(op1, __ROR(op2, rotate)); + } + return result; +} + + +__STATIC_FORCEINLINE uint32_t __SMUAD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuad %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUADX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smuadx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLAD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlad %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLADX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smladx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint64_t __SMLALD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlald %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint64_t __SMLALDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlaldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint32_t __SMUSD (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMUSDX (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("smusdx %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLSD (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsd %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint32_t __SMLSDX (uint32_t op1, uint32_t op2, uint32_t op3) +{ + uint32_t result; + + __ASM volatile ("smlsdx %0, %1, %2, %3" : "=r" (result) : "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +__STATIC_FORCEINLINE uint64_t __SMLSLD (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsld %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint64_t __SMLSLDX (uint32_t op1, uint32_t op2, uint64_t acc) +{ + union llreg_u{ + uint32_t w32[2]; + uint64_t w64; + } llr; + llr.w64 = acc; + +#ifndef __ARMEB__ /* Little endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[0]), "=r" (llr.w32[1]): "r" (op1), "r" (op2) , "0" (llr.w32[0]), "1" (llr.w32[1]) ); +#else /* Big endian */ + __ASM volatile ("smlsldx %0, %1, %2, %3" : "=r" (llr.w32[1]), "=r" (llr.w32[0]): "r" (op1), "r" (op2) , "0" (llr.w32[1]), "1" (llr.w32[0]) ); +#endif + + return(llr.w64); +} + +__STATIC_FORCEINLINE uint32_t __SEL (uint32_t op1, uint32_t op2) +{ + uint32_t result; + + __ASM volatile ("sel %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE int32_t __QADD( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qadd %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + +__STATIC_FORCEINLINE int32_t __QSUB( int32_t op1, int32_t op2) +{ + int32_t result; + + __ASM volatile ("qsub %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) ); + return(result); +} + + +#define __PKHBT(ARG1,ARG2,ARG3) \ +__extension__ \ +({ \ + uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ + __ASM ("pkhbt %0, %1, %2, lsl %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ + __RES; \ + }) + +#define __PKHTB(ARG1,ARG2,ARG3) \ +__extension__ \ +({ \ + uint32_t __RES, __ARG1 = (ARG1), __ARG2 = (ARG2); \ + if (ARG3 == 0) \ + __ASM ("pkhtb %0, %1, %2" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2) ); \ + else \ + __ASM ("pkhtb %0, %1, %2, asr %3" : "=r" (__RES) : "r" (__ARG1), "r" (__ARG2), "I" (ARG3) ); \ + __RES; \ + }) + + +__STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) +{ + int32_t result; + + __ASM ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#endif /* (__ARM_FEATURE_DSP == 1) */ +/*@} end of group CMSIS_SIMD_intrinsics */ + + +#pragma GCC diagnostic pop + +#endif /* __CMSIS_GCC_H */ diff --git a/lib/cmsis_core/cmsis_iccarm.h b/lib/cmsis_core/cmsis_iccarm.h new file mode 100644 index 00000000000..47d6a859e51 --- /dev/null +++ b/lib/cmsis_core/cmsis_iccarm.h @@ -0,0 +1,1008 @@ +/**************************************************************************//** + * @file cmsis_iccarm.h + * @brief CMSIS compiler ICCARM (IAR Compiler for Arm) header file + * @version V5.4.0 + * @date 20. January 2023 + ******************************************************************************/ + +//------------------------------------------------------------------------------ +// +// Copyright (c) 2017-2021 IAR Systems +// Copyright (c) 2017-2023 Arm Limited. All rights reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License") +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//------------------------------------------------------------------------------ + + +#ifndef __CMSIS_ICCARM_H__ +#define __CMSIS_ICCARM_H__ + +#ifndef __ICCARM__ + #error This file should only be compiled by ICCARM +#endif + +#pragma system_include + +#define __IAR_FT _Pragma("inline=forced") __intrinsic + +#if (__VER__ >= 8000000) + #define __ICCARM_V8 1 +#else + #define __ICCARM_V8 0 +#endif + +#ifndef __ALIGNED + #if __ICCARM_V8 + #define __ALIGNED(x) __attribute__((aligned(x))) + #elif (__VER__ >= 7080000) + /* Needs IAR language extensions */ + #define __ALIGNED(x) __attribute__((aligned(x))) + #else + #warning No compiler specific solution for __ALIGNED.__ALIGNED is ignored. + #define __ALIGNED(x) + #endif +#endif + + +/* Define compiler macros for CPU architecture, used in CMSIS 5. + */ +#if __ARM_ARCH_6M__ || __ARM_ARCH_7M__ || __ARM_ARCH_7EM__ || __ARM_ARCH_8M_BASE__ || __ARM_ARCH_8M_MAIN__ +/* Macros already defined */ +#else + #if defined(__ARM8M_MAINLINE__) || defined(__ARM8EM_MAINLINE__) + #define __ARM_ARCH_8M_MAIN__ 1 + #elif defined(__ARM8M_BASELINE__) + #define __ARM_ARCH_8M_BASE__ 1 + #elif defined(__ARM_ARCH_PROFILE) && __ARM_ARCH_PROFILE == 'M' + #if __ARM_ARCH == 6 + #define __ARM_ARCH_6M__ 1 + #elif __ARM_ARCH == 7 + #if __ARM_FEATURE_DSP + #define __ARM_ARCH_7EM__ 1 + #else + #define __ARM_ARCH_7M__ 1 + #endif + #endif /* __ARM_ARCH */ + #endif /* __ARM_ARCH_PROFILE == 'M' */ +#endif + +/* Alternativ core deduction for older ICCARM's */ +#if !defined(__ARM_ARCH_6M__) && !defined(__ARM_ARCH_7M__) && !defined(__ARM_ARCH_7EM__) && \ + !defined(__ARM_ARCH_8M_BASE__) && !defined(__ARM_ARCH_8M_MAIN__) + #if defined(__ARM6M__) && (__CORE__ == __ARM6M__) + #define __ARM_ARCH_6M__ 1 + #elif defined(__ARM7M__) && (__CORE__ == __ARM7M__) + #define __ARM_ARCH_7M__ 1 + #elif defined(__ARM7EM__) && (__CORE__ == __ARM7EM__) + #define __ARM_ARCH_7EM__ 1 + #elif defined(__ARM8M_BASELINE__) && (__CORE == __ARM8M_BASELINE__) + #define __ARM_ARCH_8M_BASE__ 1 + #elif defined(__ARM8M_MAINLINE__) && (__CORE == __ARM8M_MAINLINE__) + #define __ARM_ARCH_8M_MAIN__ 1 + #elif defined(__ARM8EM_MAINLINE__) && (__CORE == __ARM8EM_MAINLINE__) + #define __ARM_ARCH_8M_MAIN__ 1 + #else + #error "Unknown target." + #endif +#endif + + + +#if defined(__ARM_ARCH_6M__) && __ARM_ARCH_6M__==1 + #define __IAR_M0_FAMILY 1 +#elif defined(__ARM_ARCH_8M_BASE__) && __ARM_ARCH_8M_BASE__==1 + #define __IAR_M0_FAMILY 1 +#else + #define __IAR_M0_FAMILY 0 +#endif + +#ifndef __NO_INIT + #define __NO_INIT __attribute__ ((section (".noinit"))) +#endif +#ifndef __ALIAS + #define __ALIAS(x) __attribute__ ((alias(x))) +#endif + +#ifndef __ASM + #define __ASM __asm +#endif + +#ifndef __COMPILER_BARRIER + #define __COMPILER_BARRIER() __ASM volatile("":::"memory") +#endif + +#ifndef __INLINE + #define __INLINE inline +#endif + +#ifndef __NO_RETURN + #if __ICCARM_V8 + #define __NO_RETURN __attribute__((__noreturn__)) + #else + #define __NO_RETURN _Pragma("object_attribute=__noreturn") + #endif +#endif + +#ifndef __PACKED + #if __ICCARM_V8 + #define __PACKED __attribute__((packed, aligned(1))) + #else + /* Needs IAR language extensions */ + #define __PACKED __packed + #endif +#endif + +#ifndef __PACKED_STRUCT + #if __ICCARM_V8 + #define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) + #else + /* Needs IAR language extensions */ + #define __PACKED_STRUCT __packed struct + #endif +#endif + +#ifndef __PACKED_UNION + #if __ICCARM_V8 + #define __PACKED_UNION union __attribute__((packed, aligned(1))) + #else + /* Needs IAR language extensions */ + #define __PACKED_UNION __packed union + #endif +#endif + +#ifndef __RESTRICT + #if __ICCARM_V8 + #define __RESTRICT __restrict + #else + /* Needs IAR language extensions */ + #define __RESTRICT restrict + #endif +#endif + +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static inline +#endif + +#ifndef __FORCEINLINE + #define __FORCEINLINE _Pragma("inline=forced") +#endif + +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __FORCEINLINE __STATIC_INLINE +#endif + +#ifndef __UNALIGNED_UINT16_READ +#pragma language=save +#pragma language=extended +__IAR_FT uint16_t __iar_uint16_read(void const *ptr) +{ + return *(__packed uint16_t*)(ptr); +} +#pragma language=restore +#define __UNALIGNED_UINT16_READ(PTR) __iar_uint16_read(PTR) +#endif + + +#ifndef __UNALIGNED_UINT16_WRITE +#pragma language=save +#pragma language=extended +__IAR_FT void __iar_uint16_write(void const *ptr, uint16_t val) +{ + *(__packed uint16_t*)(ptr) = val;; +} +#pragma language=restore +#define __UNALIGNED_UINT16_WRITE(PTR,VAL) __iar_uint16_write(PTR,VAL) +#endif + +#ifndef __UNALIGNED_UINT32_READ +#pragma language=save +#pragma language=extended +__IAR_FT uint32_t __iar_uint32_read(void const *ptr) +{ + return *(__packed uint32_t*)(ptr); +} +#pragma language=restore +#define __UNALIGNED_UINT32_READ(PTR) __iar_uint32_read(PTR) +#endif + +#ifndef __UNALIGNED_UINT32_WRITE +#pragma language=save +#pragma language=extended +__IAR_FT void __iar_uint32_write(void const *ptr, uint32_t val) +{ + *(__packed uint32_t*)(ptr) = val;; +} +#pragma language=restore +#define __UNALIGNED_UINT32_WRITE(PTR,VAL) __iar_uint32_write(PTR,VAL) +#endif + +#ifndef __UNALIGNED_UINT32 /* deprecated */ +#pragma language=save +#pragma language=extended +__packed struct __iar_u32 { uint32_t v; }; +#pragma language=restore +#define __UNALIGNED_UINT32(PTR) (((struct __iar_u32 *)(PTR))->v) +#endif + +#ifndef __USED + #if __ICCARM_V8 + #define __USED __attribute__((used)) + #else + #define __USED _Pragma("__root") + #endif +#endif + +#undef __WEAK /* undo the definition from DLib_Defaults.h */ +#ifndef __WEAK + #if __ICCARM_V8 + #define __WEAK __attribute__((weak)) + #else + #define __WEAK _Pragma("__weak") + #endif +#endif + +#ifndef __PROGRAM_START +#define __PROGRAM_START __iar_program_start +#endif + +#ifndef __INITIAL_SP +#define __INITIAL_SP CSTACK$$Limit +#endif + +#ifndef __STACK_LIMIT +#define __STACK_LIMIT CSTACK$$Base +#endif + +#ifndef __VECTOR_TABLE +#define __VECTOR_TABLE __vector_table +#endif + +#ifndef __VECTOR_TABLE_ATTRIBUTE +#define __VECTOR_TABLE_ATTRIBUTE @".intvec" +#endif + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +#ifndef __STACK_SEAL +#define __STACK_SEAL STACKSEAL$$Base +#endif + +#ifndef __TZ_STACK_SEAL_SIZE +#define __TZ_STACK_SEAL_SIZE 8U +#endif + +#ifndef __TZ_STACK_SEAL_VALUE +#define __TZ_STACK_SEAL_VALUE 0xFEF5EDA5FEF5EDA5ULL +#endif + +__STATIC_FORCEINLINE void __TZ_set_STACKSEAL_S (uint32_t* stackTop) { + *((uint64_t *)stackTop) = __TZ_STACK_SEAL_VALUE; +} +#endif + +#ifndef __ICCARM_INTRINSICS_VERSION__ + #define __ICCARM_INTRINSICS_VERSION__ 0 +#endif + +#if __ICCARM_INTRINSICS_VERSION__ == 2 + + #if defined(__CLZ) + #undef __CLZ + #endif + #if defined(__REVSH) + #undef __REVSH + #endif + #if defined(__RBIT) + #undef __RBIT + #endif + #if defined(__SSAT) + #undef __SSAT + #endif + #if defined(__USAT) + #undef __USAT + #endif + + #include "iccarm_builtin.h" + + #define __disable_fault_irq __iar_builtin_disable_fiq + #define __disable_irq __iar_builtin_disable_interrupt + #define __enable_fault_irq __iar_builtin_enable_fiq + #define __enable_irq __iar_builtin_enable_interrupt + #define __arm_rsr __iar_builtin_rsr + #define __arm_wsr __iar_builtin_wsr + + + #define __get_APSR() (__arm_rsr("APSR")) + #define __get_BASEPRI() (__arm_rsr("BASEPRI")) + #define __get_CONTROL() (__arm_rsr("CONTROL")) + #define __get_FAULTMASK() (__arm_rsr("FAULTMASK")) + + #if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) + #define __get_FPSCR() (__arm_rsr("FPSCR")) + #define __set_FPSCR(VALUE) (__arm_wsr("FPSCR", (VALUE))) + #else + #define __get_FPSCR() ( 0 ) + #define __set_FPSCR(VALUE) ((void)VALUE) + #endif + + #define __get_IPSR() (__arm_rsr("IPSR")) + #define __get_MSP() (__arm_rsr("MSP")) + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + #define __get_MSPLIM() (0U) + #else + #define __get_MSPLIM() (__arm_rsr("MSPLIM")) + #endif + #define __get_PRIMASK() (__arm_rsr("PRIMASK")) + #define __get_PSP() (__arm_rsr("PSP")) + + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + #define __get_PSPLIM() (0U) + #else + #define __get_PSPLIM() (__arm_rsr("PSPLIM")) + #endif + + #define __get_xPSR() (__arm_rsr("xPSR")) + + #define __set_BASEPRI(VALUE) (__arm_wsr("BASEPRI", (VALUE))) + #define __set_BASEPRI_MAX(VALUE) (__arm_wsr("BASEPRI_MAX", (VALUE))) + +__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control) +{ + __arm_wsr("CONTROL", control); + __iar_builtin_ISB(); +} + + #define __set_FAULTMASK(VALUE) (__arm_wsr("FAULTMASK", (VALUE))) + #define __set_MSP(VALUE) (__arm_wsr("MSP", (VALUE))) + + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + #define __set_MSPLIM(VALUE) ((void)(VALUE)) + #else + #define __set_MSPLIM(VALUE) (__arm_wsr("MSPLIM", (VALUE))) + #endif + #define __set_PRIMASK(VALUE) (__arm_wsr("PRIMASK", (VALUE))) + #define __set_PSP(VALUE) (__arm_wsr("PSP", (VALUE))) + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + #define __set_PSPLIM(VALUE) ((void)(VALUE)) + #else + #define __set_PSPLIM(VALUE) (__arm_wsr("PSPLIM", (VALUE))) + #endif + + #define __TZ_get_CONTROL_NS() (__arm_rsr("CONTROL_NS")) + +__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control) +{ + __arm_wsr("CONTROL_NS", control); + __iar_builtin_ISB(); +} + + #define __TZ_get_PSP_NS() (__arm_rsr("PSP_NS")) + #define __TZ_set_PSP_NS(VALUE) (__arm_wsr("PSP_NS", (VALUE))) + #define __TZ_get_MSP_NS() (__arm_rsr("MSP_NS")) + #define __TZ_set_MSP_NS(VALUE) (__arm_wsr("MSP_NS", (VALUE))) + #define __TZ_get_SP_NS() (__arm_rsr("SP_NS")) + #define __TZ_set_SP_NS(VALUE) (__arm_wsr("SP_NS", (VALUE))) + #define __TZ_get_PRIMASK_NS() (__arm_rsr("PRIMASK_NS")) + #define __TZ_set_PRIMASK_NS(VALUE) (__arm_wsr("PRIMASK_NS", (VALUE))) + #define __TZ_get_BASEPRI_NS() (__arm_rsr("BASEPRI_NS")) + #define __TZ_set_BASEPRI_NS(VALUE) (__arm_wsr("BASEPRI_NS", (VALUE))) + #define __TZ_get_FAULTMASK_NS() (__arm_rsr("FAULTMASK_NS")) + #define __TZ_set_FAULTMASK_NS(VALUE)(__arm_wsr("FAULTMASK_NS", (VALUE))) + + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + #define __TZ_get_PSPLIM_NS() (0U) + #define __TZ_set_PSPLIM_NS(VALUE) ((void)(VALUE)) + #else + #define __TZ_get_PSPLIM_NS() (__arm_rsr("PSPLIM_NS")) + #define __TZ_set_PSPLIM_NS(VALUE) (__arm_wsr("PSPLIM_NS", (VALUE))) + #endif + + #define __TZ_get_MSPLIM_NS() (__arm_rsr("MSPLIM_NS")) + #define __TZ_set_MSPLIM_NS(VALUE) (__arm_wsr("MSPLIM_NS", (VALUE))) + + #define __NOP __iar_builtin_no_operation + + #define __CLZ __iar_builtin_CLZ + #define __CLREX __iar_builtin_CLREX + + #define __DMB __iar_builtin_DMB + #define __DSB __iar_builtin_DSB + #define __ISB __iar_builtin_ISB + + #define __LDREXB __iar_builtin_LDREXB + #define __LDREXH __iar_builtin_LDREXH + #define __LDREXW __iar_builtin_LDREX + + #define __RBIT __iar_builtin_RBIT + #define __REV __iar_builtin_REV + #define __REV16 __iar_builtin_REV16 + + __IAR_FT int16_t __REVSH(int16_t val) + { + return (int16_t) __iar_builtin_REVSH(val); + } + + #define __ROR __iar_builtin_ROR + #define __RRX __iar_builtin_RRX + + #define __SEV __iar_builtin_SEV + + #if !__IAR_M0_FAMILY + #define __SSAT __iar_builtin_SSAT + #endif + + #define __STREXB __iar_builtin_STREXB + #define __STREXH __iar_builtin_STREXH + #define __STREXW __iar_builtin_STREX + + #if !__IAR_M0_FAMILY + #define __USAT __iar_builtin_USAT + #endif + + #define __WFE __iar_builtin_WFE + #define __WFI __iar_builtin_WFI + + #if __ARM_MEDIA__ + #define __SADD8 __iar_builtin_SADD8 + #define __QADD8 __iar_builtin_QADD8 + #define __SHADD8 __iar_builtin_SHADD8 + #define __UADD8 __iar_builtin_UADD8 + #define __UQADD8 __iar_builtin_UQADD8 + #define __UHADD8 __iar_builtin_UHADD8 + #define __SSUB8 __iar_builtin_SSUB8 + #define __QSUB8 __iar_builtin_QSUB8 + #define __SHSUB8 __iar_builtin_SHSUB8 + #define __USUB8 __iar_builtin_USUB8 + #define __UQSUB8 __iar_builtin_UQSUB8 + #define __UHSUB8 __iar_builtin_UHSUB8 + #define __SADD16 __iar_builtin_SADD16 + #define __QADD16 __iar_builtin_QADD16 + #define __SHADD16 __iar_builtin_SHADD16 + #define __UADD16 __iar_builtin_UADD16 + #define __UQADD16 __iar_builtin_UQADD16 + #define __UHADD16 __iar_builtin_UHADD16 + #define __SSUB16 __iar_builtin_SSUB16 + #define __QSUB16 __iar_builtin_QSUB16 + #define __SHSUB16 __iar_builtin_SHSUB16 + #define __USUB16 __iar_builtin_USUB16 + #define __UQSUB16 __iar_builtin_UQSUB16 + #define __UHSUB16 __iar_builtin_UHSUB16 + #define __SASX __iar_builtin_SASX + #define __QASX __iar_builtin_QASX + #define __SHASX __iar_builtin_SHASX + #define __UASX __iar_builtin_UASX + #define __UQASX __iar_builtin_UQASX + #define __UHASX __iar_builtin_UHASX + #define __SSAX __iar_builtin_SSAX + #define __QSAX __iar_builtin_QSAX + #define __SHSAX __iar_builtin_SHSAX + #define __USAX __iar_builtin_USAX + #define __UQSAX __iar_builtin_UQSAX + #define __UHSAX __iar_builtin_UHSAX + #define __USAD8 __iar_builtin_USAD8 + #define __USADA8 __iar_builtin_USADA8 + #define __SSAT16 __iar_builtin_SSAT16 + #define __USAT16 __iar_builtin_USAT16 + #define __UXTB16 __iar_builtin_UXTB16 + #define __UXTAB16 __iar_builtin_UXTAB16 + #define __SXTB16 __iar_builtin_SXTB16 + #define __SXTAB16 __iar_builtin_SXTAB16 + #define __SMUAD __iar_builtin_SMUAD + #define __SMUADX __iar_builtin_SMUADX + #define __SMMLA __iar_builtin_SMMLA + #define __SMLAD __iar_builtin_SMLAD + #define __SMLADX __iar_builtin_SMLADX + #define __SMLALD __iar_builtin_SMLALD + #define __SMLALDX __iar_builtin_SMLALDX + #define __SMUSD __iar_builtin_SMUSD + #define __SMUSDX __iar_builtin_SMUSDX + #define __SMLSD __iar_builtin_SMLSD + #define __SMLSDX __iar_builtin_SMLSDX + #define __SMLSLD __iar_builtin_SMLSLD + #define __SMLSLDX __iar_builtin_SMLSLDX + #define __SEL __iar_builtin_SEL + #define __QADD __iar_builtin_QADD + #define __QSUB __iar_builtin_QSUB + #define __PKHBT __iar_builtin_PKHBT + #define __PKHTB __iar_builtin_PKHTB + #endif + +#else /* __ICCARM_INTRINSICS_VERSION__ == 2 */ + + #if __IAR_M0_FAMILY + /* Avoid clash between intrinsics.h and arm_math.h when compiling for Cortex-M0. */ + #define __CLZ __cmsis_iar_clz_not_active + #define __SSAT __cmsis_iar_ssat_not_active + #define __USAT __cmsis_iar_usat_not_active + #define __RBIT __cmsis_iar_rbit_not_active + #define __get_APSR __cmsis_iar_get_APSR_not_active + #endif + + + #if (!((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) )) + #define __get_FPSCR __cmsis_iar_get_FPSR_not_active + #define __set_FPSCR __cmsis_iar_set_FPSR_not_active + #endif + + #ifdef __INTRINSICS_INCLUDED + #error intrinsics.h is already included previously! + #endif + + #include + + #if __IAR_M0_FAMILY + /* Avoid clash between intrinsics.h and arm_math.h when compiling for Cortex-M0. */ + #undef __CLZ + #undef __SSAT + #undef __USAT + #undef __RBIT + #undef __get_APSR + + __STATIC_INLINE uint8_t __CLZ(uint32_t data) + { + if (data == 0U) { return 32U; } + + uint32_t count = 0U; + uint32_t mask = 0x80000000U; + + while ((data & mask) == 0U) + { + count += 1U; + mask = mask >> 1U; + } + return count; + } + + __STATIC_INLINE uint32_t __RBIT(uint32_t v) + { + uint8_t sc = 31U; + uint32_t r = v; + for (v >>= 1U; v; v >>= 1U) + { + r <<= 1U; + r |= v & 1U; + sc--; + } + return (r << sc); + } + + __STATIC_INLINE uint32_t __get_APSR(void) + { + uint32_t res; + __asm("MRS %0,APSR" : "=r" (res)); + return res; + } + + #endif + + #if (!((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) )) + #undef __get_FPSCR + #undef __set_FPSCR + #define __get_FPSCR() (0) + #define __set_FPSCR(VALUE) ((void)VALUE) + #endif + + #pragma diag_suppress=Pe940 + #pragma diag_suppress=Pe177 + + #define __enable_irq __enable_interrupt + #define __disable_irq __disable_interrupt + #define __NOP __no_operation + + #define __get_xPSR __get_PSR + + #if (!defined(__ARM_ARCH_6M__) || __ARM_ARCH_6M__==0) + + __IAR_FT uint32_t __LDREXW(uint32_t volatile *ptr) + { + return __LDREX((unsigned long *)ptr); + } + + __IAR_FT uint32_t __STREXW(uint32_t value, uint32_t volatile *ptr) + { + return __STREX(value, (unsigned long *)ptr); + } + #endif + + + /* __CORTEX_M is defined in core_cm0.h, core_cm3.h and core_cm4.h. */ + #if (__CORTEX_M >= 0x03) + + __IAR_FT uint32_t __RRX(uint32_t value) + { + uint32_t result; + __ASM volatile("RRX %0, %1" : "=r"(result) : "r" (value)); + return(result); + } + + __IAR_FT void __set_BASEPRI_MAX(uint32_t value) + { + __asm volatile("MSR BASEPRI_MAX,%0"::"r" (value)); + } + + + #define __enable_fault_irq __enable_fiq + #define __disable_fault_irq __disable_fiq + + + #endif /* (__CORTEX_M >= 0x03) */ + + __IAR_FT uint32_t __ROR(uint32_t op1, uint32_t op2) + { + return (op1 >> op2) | (op1 << ((sizeof(op1)*8)-op2)); + } + + #if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) + + __IAR_FT uint32_t __get_MSPLIM(void) + { + uint32_t res; + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + res = 0U; + #else + __asm volatile("MRS %0,MSPLIM" : "=r" (res)); + #endif + return res; + } + + __IAR_FT void __set_MSPLIM(uint32_t value) + { + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)value; + #else + __asm volatile("MSR MSPLIM,%0" :: "r" (value)); + #endif + } + + __IAR_FT uint32_t __get_PSPLIM(void) + { + uint32_t res; + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + res = 0U; + #else + __asm volatile("MRS %0,PSPLIM" : "=r" (res)); + #endif + return res; + } + + __IAR_FT void __set_PSPLIM(uint32_t value) + { + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)value; + #else + __asm volatile("MSR PSPLIM,%0" :: "r" (value)); + #endif + } + + __IAR_FT uint32_t __TZ_get_CONTROL_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,CONTROL_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_CONTROL_NS(uint32_t value) + { + __asm volatile("MSR CONTROL_NS,%0" :: "r" (value)); + __iar_builtin_ISB(); + } + + __IAR_FT uint32_t __TZ_get_PSP_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,PSP_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_PSP_NS(uint32_t value) + { + __asm volatile("MSR PSP_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_MSP_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,MSP_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_MSP_NS(uint32_t value) + { + __asm volatile("MSR MSP_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_SP_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,SP_NS" : "=r" (res)); + return res; + } + __IAR_FT void __TZ_set_SP_NS(uint32_t value) + { + __asm volatile("MSR SP_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_PRIMASK_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,PRIMASK_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_PRIMASK_NS(uint32_t value) + { + __asm volatile("MSR PRIMASK_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_BASEPRI_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,BASEPRI_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_BASEPRI_NS(uint32_t value) + { + __asm volatile("MSR BASEPRI_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_FAULTMASK_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,FAULTMASK_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_FAULTMASK_NS(uint32_t value) + { + __asm volatile("MSR FAULTMASK_NS,%0" :: "r" (value)); + } + + __IAR_FT uint32_t __TZ_get_PSPLIM_NS(void) + { + uint32_t res; + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + res = 0U; + #else + __asm volatile("MRS %0,PSPLIM_NS" : "=r" (res)); + #endif + return res; + } + + __IAR_FT void __TZ_set_PSPLIM_NS(uint32_t value) + { + #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ + (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)value; + #else + __asm volatile("MSR PSPLIM_NS,%0" :: "r" (value)); + #endif + } + + __IAR_FT uint32_t __TZ_get_MSPLIM_NS(void) + { + uint32_t res; + __asm volatile("MRS %0,MSPLIM_NS" : "=r" (res)); + return res; + } + + __IAR_FT void __TZ_set_MSPLIM_NS(uint32_t value) + { + __asm volatile("MSR MSPLIM_NS,%0" :: "r" (value)); + } + + #endif /* __ARM_ARCH_8M_MAIN__ or __ARM_ARCH_8M_BASE__ */ + +#endif /* __ICCARM_INTRINSICS_VERSION__ == 2 */ + +#define __BKPT(value) __asm volatile ("BKPT %0" : : "i"(value)) + +#if __IAR_M0_FAMILY + __STATIC_INLINE int32_t __SSAT(int32_t val, uint32_t sat) + { + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; + } + + __STATIC_INLINE uint32_t __USAT(int32_t val, uint32_t sat) + { + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; + } +#endif + +#if (__CORTEX_M >= 0x03) /* __CORTEX_M is defined in core_cm0.h, core_cm3.h and core_cm4.h. */ + + __IAR_FT uint8_t __LDRBT(volatile uint8_t *addr) + { + uint32_t res; + __ASM volatile ("LDRBT %0, [%1]" : "=r" (res) : "r" (addr) : "memory"); + return ((uint8_t)res); + } + + __IAR_FT uint16_t __LDRHT(volatile uint16_t *addr) + { + uint32_t res; + __ASM volatile ("LDRHT %0, [%1]" : "=r" (res) : "r" (addr) : "memory"); + return ((uint16_t)res); + } + + __IAR_FT uint32_t __LDRT(volatile uint32_t *addr) + { + uint32_t res; + __ASM volatile ("LDRT %0, [%1]" : "=r" (res) : "r" (addr) : "memory"); + return res; + } + + __IAR_FT void __STRBT(uint8_t value, volatile uint8_t *addr) + { + __ASM volatile ("STRBT %1, [%0]" : : "r" (addr), "r" ((uint32_t)value) : "memory"); + } + + __IAR_FT void __STRHT(uint16_t value, volatile uint16_t *addr) + { + __ASM volatile ("STRHT %1, [%0]" : : "r" (addr), "r" ((uint32_t)value) : "memory"); + } + + __IAR_FT void __STRT(uint32_t value, volatile uint32_t *addr) + { + __ASM volatile ("STRT %1, [%0]" : : "r" (addr), "r" (value) : "memory"); + } + +#endif /* (__CORTEX_M >= 0x03) */ + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) ) + + + __IAR_FT uint8_t __LDAB(volatile uint8_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAB %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return ((uint8_t)res); + } + + __IAR_FT uint16_t __LDAH(volatile uint16_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAH %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return ((uint16_t)res); + } + + __IAR_FT uint32_t __LDA(volatile uint32_t *ptr) + { + uint32_t res; + __ASM volatile ("LDA %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return res; + } + + __IAR_FT void __STLB(uint8_t value, volatile uint8_t *ptr) + { + __ASM volatile ("STLB %1, [%0]" :: "r" (ptr), "r" (value) : "memory"); + } + + __IAR_FT void __STLH(uint16_t value, volatile uint16_t *ptr) + { + __ASM volatile ("STLH %1, [%0]" :: "r" (ptr), "r" (value) : "memory"); + } + + __IAR_FT void __STL(uint32_t value, volatile uint32_t *ptr) + { + __ASM volatile ("STL %1, [%0]" :: "r" (ptr), "r" (value) : "memory"); + } + + __IAR_FT uint8_t __LDAEXB(volatile uint8_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAEXB %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return ((uint8_t)res); + } + + __IAR_FT uint16_t __LDAEXH(volatile uint16_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAEXH %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return ((uint16_t)res); + } + + __IAR_FT uint32_t __LDAEX(volatile uint32_t *ptr) + { + uint32_t res; + __ASM volatile ("LDAEX %0, [%1]" : "=r" (res) : "r" (ptr) : "memory"); + return res; + } + + __IAR_FT uint32_t __STLEXB(uint8_t value, volatile uint8_t *ptr) + { + uint32_t res; + __ASM volatile ("STLEXB %0, %2, [%1]" : "=r" (res) : "r" (ptr), "r" (value) : "memory"); + return res; + } + + __IAR_FT uint32_t __STLEXH(uint16_t value, volatile uint16_t *ptr) + { + uint32_t res; + __ASM volatile ("STLEXH %0, %2, [%1]" : "=r" (res) : "r" (ptr), "r" (value) : "memory"); + return res; + } + + __IAR_FT uint32_t __STLEX(uint32_t value, volatile uint32_t *ptr) + { + uint32_t res; + __ASM volatile ("STLEX %0, %2, [%1]" : "=r" (res) : "r" (ptr), "r" (value) : "memory"); + return res; + } + +#endif /* __ARM_ARCH_8M_MAIN__ or __ARM_ARCH_8M_BASE__ */ + +#undef __IAR_FT +#undef __IAR_M0_FAMILY +#undef __ICCARM_V8 + +#pragma diag_default=Pe940 +#pragma diag_default=Pe177 + +#define __SXTB16_RORn(ARG1, ARG2) __SXTB16(__ROR(ARG1, ARG2)) + +#define __SXTAB16_RORn(ARG1, ARG2, ARG3) __SXTAB16(ARG1, __ROR(ARG2, ARG3)) + +#endif /* __CMSIS_ICCARM_H__ */ diff --git a/lib/cmsis_core/cmsis_tiarmclang.h b/lib/cmsis_core/cmsis_tiarmclang.h new file mode 100644 index 00000000000..4d799c277c8 --- /dev/null +++ b/lib/cmsis_core/cmsis_tiarmclang.h @@ -0,0 +1,1510 @@ +/**************************************************************************//** + * @file cmsis_tiarmclang.h + * @brief CMSIS compiler tiarmclang header file + * @version V1.0.0 + * @date 04. April 2023 + ******************************************************************************/ +/* + * Copyright (c) 2023 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*lint -esym(9058, IRQn)*/ /* disable MISRA 2012 Rule 2.4 for IRQn */ + +#ifndef __CMSIS_TIARMCLANG_H +#define __CMSIS_TIARMCLANG_H + +#pragma clang system_header /* treat file as system include file */ + +/* CMSIS compiler specific defines */ +#ifndef __ASM + #define __ASM __asm +#endif +#ifndef __INLINE + #define __INLINE __inline +#endif +#ifndef __STATIC_INLINE + #define __STATIC_INLINE static __inline +#endif +#ifndef __STATIC_FORCEINLINE + #define __STATIC_FORCEINLINE __attribute__((always_inline)) static __inline +#endif +#ifndef __NO_RETURN + #define __NO_RETURN __attribute__((__noreturn__)) +#endif +#ifndef __USED + #define __USED __attribute__((used)) +#endif +#ifndef __WEAK + #define __WEAK __attribute__((weak)) +#endif +#ifndef __PACKED + #define __PACKED __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_STRUCT + #define __PACKED_STRUCT struct __attribute__((packed, aligned(1))) +#endif +#ifndef __PACKED_UNION + #define __PACKED_UNION union __attribute__((packed, aligned(1))) +#endif +#ifndef __UNALIGNED_UINT32 /* deprecated */ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32 */ + struct __attribute__((packed)) T_UINT32 { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v) +#endif +#ifndef __UNALIGNED_UINT16_WRITE + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT16_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_WRITE */ + __PACKED_STRUCT T_UINT16_WRITE { uint16_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT16_READ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT16_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT16_READ */ + __PACKED_STRUCT T_UINT16_READ { uint16_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v) +#endif +#ifndef __UNALIGNED_UINT32_WRITE + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32_WRITE)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_WRITE */ + __PACKED_STRUCT T_UINT32_WRITE { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val)) +#endif +#ifndef __UNALIGNED_UINT32_READ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wpacked" +/*lint -esym(9058, T_UINT32_READ)*/ /* disable MISRA 2012 Rule 2.4 for T_UINT32_READ */ + __PACKED_STRUCT T_UINT32_READ { uint32_t v; }; + #pragma clang diagnostic pop + #define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v) +#endif +#ifndef __ALIGNED + #define __ALIGNED(x) __attribute__((aligned(x))) +#endif +#ifndef __RESTRICT + #define __RESTRICT __restrict +#endif +#ifndef __COMPILER_BARRIER + #define __COMPILER_BARRIER() __ASM volatile("":::"memory") +#endif +#ifndef __NO_INIT + #define __NO_INIT __attribute__ ((section (".bss.noinit"))) +#endif +#ifndef __ALIAS + #define __ALIAS(x) __attribute__ ((alias(x))) +#endif + + +/* ######################### Startup and Lowlevel Init ######################## */ + +#ifndef __PROGRAM_START +#define __PROGRAM_START _c_int00 +#endif + +#ifndef __INITIAL_SP +#define __INITIAL_SP __STACK_END +#endif + +#ifndef __STACK_LIMIT +#define __STACK_LIMIT __STACK_SIZE +#endif + +#ifndef __VECTOR_TABLE +#define __VECTOR_TABLE __Vectors +#endif + +#ifndef __VECTOR_TABLE_ATTRIBUTE +#define __VECTOR_TABLE_ATTRIBUTE __attribute__((used, section(".intvecs"))) +#endif + +#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) +#ifndef __STACK_SEAL +#define __STACK_SEAL Image$$STACKSEAL$$ZI$$Base +#endif + +#ifndef __TZ_STACK_SEAL_SIZE +#define __TZ_STACK_SEAL_SIZE 8U +#endif + +#ifndef __TZ_STACK_SEAL_VALUE +#define __TZ_STACK_SEAL_VALUE 0xFEF5EDA5FEF5EDA5ULL +#endif + + +__STATIC_FORCEINLINE void __TZ_set_STACKSEAL_S (uint32_t* stackTop) { + *((uint64_t *)stackTop) = __TZ_STACK_SEAL_VALUE; +} +#endif + + +/* ########################## Core Instruction Access ######################### */ +/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface + Access to dedicated instructions + @{ +*/ + +/* Define macros for porting to both thumb1 and thumb2. + * For thumb1, use low register (r0-r7), specified by constraint "l" + * Otherwise, use general registers, specified by constraint "r" */ +#if defined (__thumb__) && !defined (__thumb2__) +#define __CMSIS_GCC_OUT_REG(r) "=l" (r) +#define __CMSIS_GCC_RW_REG(r) "+l" (r) +#define __CMSIS_GCC_USE_REG(r) "l" (r) +#else +#define __CMSIS_GCC_OUT_REG(r) "=r" (r) +#define __CMSIS_GCC_RW_REG(r) "+r" (r) +#define __CMSIS_GCC_USE_REG(r) "r" (r) +#endif + +/** + \brief No Operation + \details No Operation does nothing. This instruction can be used for code alignment purposes. + */ +#define __NOP __builtin_arm_nop + +/** + \brief Wait For Interrupt + \details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs. + */ +#define __WFI __builtin_arm_wfi + + +/** + \brief Wait For Event + \details Wait For Event is a hint instruction that permits the processor to enter + a low-power state until one of a number of events occurs. + */ +#define __WFE __builtin_arm_wfe + + +/** + \brief Send Event + \details Send Event is a hint instruction. It causes an event to be signaled to the CPU. + */ +#define __SEV __builtin_arm_sev + + +/** + \brief Instruction Synchronization Barrier + \details Instruction Synchronization Barrier flushes the pipeline in the processor, + so that all instructions following the ISB are fetched from cache or memory, + after the instruction has been completed. + */ +#define __ISB() __builtin_arm_isb(0xF) + +/** + \brief Data Synchronization Barrier + \details Acts as a special kind of Data Memory Barrier. + It completes when all explicit memory accesses before this instruction complete. + */ +#define __DSB() __builtin_arm_dsb(0xF) + + +/** + \brief Data Memory Barrier + \details Ensures the apparent order of the explicit memory operations before + and after the instruction, without ensuring their completion. + */ +#define __DMB() __builtin_arm_dmb(0xF) + + +/** + \brief Reverse byte order (32 bit) + \details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REV(value) __builtin_bswap32(value) + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REV16(value) __ROR(__REV(value), 16) + + +/** + \brief Reverse byte order (16 bit) + \details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000. + \param [in] value Value to reverse + \return Reversed value + */ +#define __REVSH(value) (int16_t)__builtin_bswap16(value) + + +/** + \brief Rotate Right in unsigned value (32 bit) + \details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits. + \param [in] op1 Value to rotate + \param [in] op2 Number of Bits to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __ROR(uint32_t op1, uint32_t op2) +{ + op2 %= 32U; + if (op2 == 0U) + { + return op1; + } + return (op1 >> op2) | (op1 << (32U - op2)); +} + + +/** + \brief Breakpoint + \details Causes the processor to enter Debug state. + Debug tools can use this to investigate system state when the instruction at a particular address is reached. + \param [in] value is ignored by the processor. + If required, a debugger can use it to store additional information about the breakpoint. + */ +#define __BKPT(value) __ASM volatile ("bkpt "#value) + + +/** + \brief Reverse bit order of value + \details Reverses the bit order of the given value. + \param [in] value Value to reverse + \return Reversed value + */ +#define __RBIT __builtin_arm_rbit + +/** + \brief Count leading zeros + \details Counts the number of leading zeros of a data value. + \param [in] value Value to count the leading zeros + \return number of leading zeros in value + */ +__STATIC_FORCEINLINE uint8_t __CLZ(uint32_t value) +{ + /* Even though __builtin_clz produces a CLZ instruction on ARM, formally + __builtin_clz(0) is undefined behaviour, so handle this case specially. + This guarantees ARM-compatible results if happening to compile on a non-ARM + target, and ensures the compiler doesn't decide to activate any + optimisations using the logic "value was passed to __builtin_clz, so it + is non-zero". + ARM Compiler 6.10 and possibly earlier will optimise this test away, leaving a + single CLZ instruction. + */ + if (value == 0U) + { + return 32U; + } + return __builtin_clz(value); +} + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) + +/** + \brief LDR Exclusive (8 bit) + \details Executes a exclusive LDR instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#define __LDREXB (uint8_t)__builtin_arm_ldrex + + +/** + \brief LDR Exclusive (16 bit) + \details Executes a exclusive LDR instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#define __LDREXH (uint16_t)__builtin_arm_ldrex + + +/** + \brief LDR Exclusive (32 bit) + \details Executes a exclusive LDR instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#define __LDREXW (uint32_t)__builtin_arm_ldrex + + +/** + \brief STR Exclusive (8 bit) + \details Executes a exclusive STR instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXB (uint32_t)__builtin_arm_strex + + +/** + \brief STR Exclusive (16 bit) + \details Executes a exclusive STR instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXH (uint32_t)__builtin_arm_strex + + +/** + \brief STR Exclusive (32 bit) + \details Executes a exclusive STR instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STREXW (uint32_t)__builtin_arm_strex + + +/** + \brief Remove the exclusive lock + \details Removes the exclusive lock which is created by LDREX. + */ +#define __CLREX __builtin_arm_clrex + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +#define __SSAT __builtin_arm_ssat + + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +#define __USAT __builtin_arm_usat + + +/** + \brief Rotate Right with Extend (32 bit) + \details Moves each bit of a bitstring right by one bit. + The carry input is shifted in at the left end of the bitstring. + \param [in] value Value to rotate + \return Rotated value + */ +__STATIC_FORCEINLINE uint32_t __RRX(uint32_t value) +{ + uint32_t result; + + __ASM volatile ("rrx %0, %1" : __CMSIS_GCC_OUT_REG (result) : __CMSIS_GCC_USE_REG (value) ); + return(result); +} + + +/** + \brief LDRT Unprivileged (8 bit) + \details Executes a Unprivileged LDRT instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDRBT(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrbt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint8_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (16 bit) + \details Executes a Unprivileged LDRT instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDRHT(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrht %0, %1" : "=r" (result) : "Q" (*ptr) ); + return ((uint16_t) result); /* Add explicit type cast here */ +} + + +/** + \brief LDRT Unprivileged (32 bit) + \details Executes a Unprivileged LDRT instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDRT(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldrt %0, %1" : "=r" (result) : "Q" (*ptr) ); + return(result); +} + + +/** + \brief STRT Unprivileged (8 bit) + \details Executes a Unprivileged STRT instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRBT(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("strbt %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (16 bit) + \details Executes a Unprivileged STRT instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRHT(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("strht %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) ); +} + + +/** + \brief STRT Unprivileged (32 bit) + \details Executes a Unprivileged STRT instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STRT(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("strt %1, %0" : "=Q" (*ptr) : "r" (value) ); +} + +#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + +/** + \brief Signed Saturate + \details Saturates a signed value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (1..32) + \return Saturated value + */ +__STATIC_FORCEINLINE int32_t __SSAT(int32_t val, uint32_t sat) +{ + if ((sat >= 1U) && (sat <= 32U)) + { + const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U); + const int32_t min = -1 - max ; + if (val > max) + { + return max; + } + else if (val < min) + { + return min; + } + } + return val; +} + +/** + \brief Unsigned Saturate + \details Saturates an unsigned value. + \param [in] value Value to be saturated + \param [in] sat Bit position to saturate to (0..31) + \return Saturated value + */ +__STATIC_FORCEINLINE uint32_t __USAT(int32_t val, uint32_t sat) +{ + if (sat <= 31U) + { + const uint32_t max = ((1U << sat) - 1U); + if (val > (int32_t)max) + { + return max; + } + else if (val < 0) + { + return 0U; + } + } + return (uint32_t)val; +} + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) + +/** + \brief Load-Acquire (8 bit) + \details Executes a LDAB instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +__STATIC_FORCEINLINE uint8_t __LDAB(volatile uint8_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldab %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint8_t) result); +} + + +/** + \brief Load-Acquire (16 bit) + \details Executes a LDAH instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +__STATIC_FORCEINLINE uint16_t __LDAH(volatile uint16_t *ptr) +{ + uint32_t result; + + __ASM volatile ("ldah %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return ((uint16_t) result); +} + + +/** + \brief Load-Acquire (32 bit) + \details Executes a LDA instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +__STATIC_FORCEINLINE uint32_t __LDA(volatile uint32_t *ptr) +{ + uint32_t result; + + __ASM volatile ("lda %0, %1" : "=r" (result) : "Q" (*ptr) : "memory" ); + return(result); +} + + +/** + \brief Store-Release (8 bit) + \details Executes a STLB instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLB(uint8_t value, volatile uint8_t *ptr) +{ + __ASM volatile ("stlb %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Store-Release (16 bit) + \details Executes a STLH instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STLH(uint16_t value, volatile uint16_t *ptr) +{ + __ASM volatile ("stlh %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Store-Release (32 bit) + \details Executes a STL instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + */ +__STATIC_FORCEINLINE void __STL(uint32_t value, volatile uint32_t *ptr) +{ + __ASM volatile ("stl %1, %0" : "=Q" (*ptr) : "r" ((uint32_t)value) : "memory" ); +} + + +/** + \brief Load-Acquire Exclusive (8 bit) + \details Executes a LDAB exclusive instruction for 8 bit value. + \param [in] ptr Pointer to data + \return value of type uint8_t at (*ptr) + */ +#define __LDAEXB (uint8_t)__builtin_arm_ldaex + + +/** + \brief Load-Acquire Exclusive (16 bit) + \details Executes a LDAH exclusive instruction for 16 bit values. + \param [in] ptr Pointer to data + \return value of type uint16_t at (*ptr) + */ +#define __LDAEXH (uint16_t)__builtin_arm_ldaex + + +/** + \brief Load-Acquire Exclusive (32 bit) + \details Executes a LDA exclusive instruction for 32 bit values. + \param [in] ptr Pointer to data + \return value of type uint32_t at (*ptr) + */ +#define __LDAEX (uint32_t)__builtin_arm_ldaex + + +/** + \brief Store-Release Exclusive (8 bit) + \details Executes a STLB exclusive instruction for 8 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STLEXB (uint32_t)__builtin_arm_stlex + + +/** + \brief Store-Release Exclusive (16 bit) + \details Executes a STLH exclusive instruction for 16 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STLEXH (uint32_t)__builtin_arm_stlex + + +/** + \brief Store-Release Exclusive (32 bit) + \details Executes a STL exclusive instruction for 32 bit values. + \param [in] value Value to store + \param [in] ptr Pointer to location + \return 0 Function succeeded + \return 1 Function failed + */ +#define __STLEX (uint32_t)__builtin_arm_stlex + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + +/** @}*/ /* end of group CMSIS_Core_InstructionInterface */ + + +/* ########################### Core Function Access ########################### */ +/** \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions + @{ + */ + +/** + \brief Enable IRQ Interrupts + \details Enables IRQ interrupts by clearing special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +#ifndef __ARM_COMPAT_H +__STATIC_FORCEINLINE void __enable_irq(void) +{ + __ASM volatile ("cpsie i" : : : "memory"); +} +#endif + + +/** + \brief Disable IRQ Interrupts + \details Disables IRQ interrupts by setting special-purpose register PRIMASK. + Can only be executed in Privileged modes. + */ +#ifndef __ARM_COMPAT_H +__STATIC_FORCEINLINE void __disable_irq(void) +{ + __ASM volatile ("cpsid i" : : : "memory"); +} +#endif + + +/** + \brief Get Control Register + \details Returns the content of the Control Register. + \return Control Register value + */ +__STATIC_FORCEINLINE uint32_t __get_CONTROL(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Control Register (non-secure) + \details Returns the content of the non-secure Control Register when in secure mode. + \return non-secure Control Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_CONTROL_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, control_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Control Register + \details Writes the given value to the Control Register. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control) +{ + __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); + __ISB(); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Control Register (non-secure) + \details Writes the given value to the non-secure Control Register when in secure state. + \param [in] control Control Register value to set + */ +__STATIC_FORCEINLINE void __TZ_set_CONTROL_NS(uint32_t control) +{ + __ASM volatile ("MSR control_ns, %0" : : "r" (control) : "memory"); + __ISB(); +} +#endif + + +/** + \brief Get IPSR Register + \details Returns the content of the IPSR Register. + \return IPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_IPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get APSR Register + \details Returns the content of the APSR Register. + \return APSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_APSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, apsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get xPSR Register + \details Returns the content of the xPSR Register. + \return xPSR Register value + */ +__STATIC_FORCEINLINE uint32_t __get_xPSR(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); + return(result); +} + + +/** + \brief Get Process Stack Pointer + \details Returns the current value of the Process Stack Pointer (PSP). + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer (non-secure) + \details Returns the current value of the non-secure Process Stack Pointer (PSP) when in secure state. + \return PSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, psp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Process Stack Pointer + \details Assigns the given value to the Process Stack Pointer (PSP). + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_PSP(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp, %0" : : "r" (topOfProcStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Process Stack Pointer (PSP) when in secure state. + \param [in] topOfProcStack Process Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSP_NS(uint32_t topOfProcStack) +{ + __ASM volatile ("MSR psp_ns, %0" : : "r" (topOfProcStack) : ); +} +#endif + + +/** + \brief Get Main Stack Pointer + \details Returns the current value of the Main Stack Pointer (MSP). + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSP(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer (non-secure) + \details Returns the current value of the non-secure Main Stack Pointer (MSP) when in secure state. + \return MSP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, msp_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Main Stack Pointer + \details Assigns the given value to the Main Stack Pointer (MSP). + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __set_MSP(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp, %0" : : "r" (topOfMainStack) : ); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Main Stack Pointer (MSP) when in secure state. + \param [in] topOfMainStack Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSP_NS(uint32_t topOfMainStack) +{ + __ASM volatile ("MSR msp_ns, %0" : : "r" (topOfMainStack) : ); +} +#endif + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Stack Pointer (non-secure) + \details Returns the current value of the non-secure Stack Pointer (SP) when in secure state. + \return SP Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_SP_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, sp_ns" : "=r" (result) ); + return(result); +} + + +/** + \brief Set Stack Pointer (non-secure) + \details Assigns the given value to the non-secure Stack Pointer (SP) when in secure state. + \param [in] topOfStack Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_SP_NS(uint32_t topOfStack) +{ + __ASM volatile ("MSR sp_ns, %0" : : "r" (topOfStack) : ); +} +#endif + + +/** + \brief Get Priority Mask + \details Returns the current state of the priority mask bit from the Priority Mask Register. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __get_PRIMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Priority Mask (non-secure) + \details Returns the current state of the non-secure priority mask bit from the Priority Mask Register when in secure state. + \return Priority Mask value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PRIMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, primask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Priority Mask + \details Assigns the given value to the Priority Mask Register. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __set_PRIMASK(uint32_t priMask) +{ + __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Priority Mask (non-secure) + \details Assigns the given value to the non-secure Priority Mask Register when in secure state. + \param [in] priMask Priority Mask + */ +__STATIC_FORCEINLINE void __TZ_set_PRIMASK_NS(uint32_t priMask) +{ + __ASM volatile ("MSR primask_ns, %0" : : "r" (priMask) : "memory"); +} +#endif + + +#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) +/** + \brief Enable FIQ + \details Enables FIQ interrupts by clearing special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __enable_fault_irq(void) +{ + __ASM volatile ("cpsie f" : : : "memory"); +} + + +/** + \brief Disable FIQ + \details Disables FIQ interrupts by setting special-purpose register FAULTMASK. + Can only be executed in Privileged modes. + */ +__STATIC_FORCEINLINE void __disable_fault_irq(void) +{ + __ASM volatile ("cpsid f" : : : "memory"); +} + + +/** + \brief Get Base Priority + \details Returns the current value of the Base Priority register. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Base Priority (non-secure) + \details Returns the current value of the non-secure Base Priority register when in secure state. + \return Base Priority register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_BASEPRI_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, basepri_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Base Priority + \details Assigns the given value to the Base Priority register. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri) +{ + __ASM volatile ("MSR basepri, %0" : : "r" (basePri) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Base Priority (non-secure) + \details Assigns the given value to the non-secure Base Priority register when in secure state. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_ns, %0" : : "r" (basePri) : "memory"); +} +#endif + + +/** + \brief Set Base Priority with condition + \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled, + or the new value increases the BASEPRI priority level. + \param [in] basePri Base Priority value to set + */ +__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri) +{ + __ASM volatile ("MSR basepri_max, %0" : : "r" (basePri) : "memory"); +} + + +/** + \brief Get Fault Mask + \details Returns the current value of the Fault Mask register. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __get_FAULTMASK(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); + return(result); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Fault Mask (non-secure) + \details Returns the current value of the non-secure Fault Mask register when in secure state. + \return Fault Mask register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_FAULTMASK_NS(void) +{ + uint32_t result; + + __ASM volatile ("MRS %0, faultmask_ns" : "=r" (result) ); + return(result); +} +#endif + + +/** + \brief Set Fault Mask + \details Assigns the given value to the Fault Mask register. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __set_FAULTMASK(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Fault Mask (non-secure) + \details Assigns the given value to the non-secure Fault Mask register when in secure state. + \param [in] faultMask Fault Mask value to set + */ +__STATIC_FORCEINLINE void __TZ_set_FAULTMASK_NS(uint32_t faultMask) +{ + __ASM volatile ("MSR faultmask_ns, %0" : : "r" (faultMask) : "memory"); +} +#endif + +#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \ + (defined (__ARM_ARCH_7EM__ ) && (__ARM_ARCH_7EM__ == 1)) || \ + (defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + + +#if ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) + +/** + \brief Get Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the Process Stack Pointer Limit (PSPLIM). + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_PSPLIM(void) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim" : "=r" (result) ); + return result; +#endif +} + +#if (defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Process Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always in non-secure + mode. + + \details Returns the current value of the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \return PSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_PSPLIM_NS(void) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) ) + // without main extensions, the non-secure PSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, psplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Process Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the Process Stack Pointer Limit (PSPLIM). + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_PSPLIM(uint32_t ProcStackPtrLimit) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim, %0" : : "r" (ProcStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Process Stack Pointer (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored in non-secure + mode. + + \details Assigns the given value to the non-secure Process Stack Pointer Limit (PSPLIM) when in secure state. + \param [in] ProcStackPtrLimit Process Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __TZ_set_PSPLIM_NS(uint32_t ProcStackPtrLimit) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) ) + // without main extensions, the non-secure PSPLIM is RAZ/WI + (void)ProcStackPtrLimit; +#else + __ASM volatile ("MSR psplim_ns, %0\n" : : "r" (ProcStackPtrLimit)); +#endif +} +#endif + + +/** + \brief Get Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the Main Stack Pointer Limit (MSPLIM). + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __get_MSPLIM(void) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim" : "=r" (result) ); + return result; +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Get Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence zero is returned always. + + \details Returns the current value of the non-secure Main Stack Pointer Limit(MSPLIM) when in secure state. + \return MSPLIM Register value + */ +__STATIC_FORCEINLINE uint32_t __TZ_get_MSPLIM_NS(void) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) ) + // without main extensions, the non-secure MSPLIM is RAZ/WI + return 0U; +#else + uint32_t result; + __ASM volatile ("MRS %0, msplim_ns" : "=r" (result) ); + return result; +#endif +} +#endif + + +/** + \brief Set Main Stack Pointer Limit + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the Main Stack Pointer Limit (MSPLIM). + \param [in] MainStackPtrLimit Main Stack Pointer Limit value to set + */ +__STATIC_FORCEINLINE void __set_MSPLIM(uint32_t MainStackPtrLimit) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) && \ + (!defined (__ARM_FEATURE_CMSE) || (__ARM_FEATURE_CMSE < 3))) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim, %0" : : "r" (MainStackPtrLimit)); +#endif +} + + +#if (defined (__ARM_FEATURE_CMSE ) && (__ARM_FEATURE_CMSE == 3)) +/** + \brief Set Main Stack Pointer Limit (non-secure) + Devices without ARMv8-M Main Extensions (i.e. Cortex-M23) lack the non-secure + Stack Pointer Limit register hence the write is silently ignored. + + \details Assigns the given value to the non-secure Main Stack Pointer Limit (MSPLIM) when in secure state. + \param [in] MainStackPtrLimit Main Stack Pointer value to set + */ +__STATIC_FORCEINLINE void __TZ_set_MSPLIM_NS(uint32_t MainStackPtrLimit) +{ +#if (!((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__ ) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) ) + // without main extensions, the non-secure MSPLIM is RAZ/WI + (void)MainStackPtrLimit; +#else + __ASM volatile ("MSR msplim_ns, %0" : : "r" (MainStackPtrLimit)); +#endif +} +#endif + +#endif /* ((defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) || \ + (defined (__ARM_ARCH_8M_BASE__ ) && (__ARM_ARCH_8M_BASE__ == 1)) || \ + (defined (__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ == 1)) ) */ + +/** + \brief Get FPSCR + \details Returns the current value of the Floating Point Status/Control register. + \return Floating Point Status/Control register value + */ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#define __get_FPSCR (uint32_t)__builtin_arm_get_fpscr +#else +#define __get_FPSCR() ((uint32_t)0U) +#endif + +/** + \brief Set FPSCR + \details Assigns the given value to the Floating Point Status/Control register. + \param [in] fpscr Floating Point Status/Control value to set + */ +#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \ + (defined (__FPU_USED ) && (__FPU_USED == 1U)) ) +#define __set_FPSCR __builtin_arm_set_fpscr +#else +#define __set_FPSCR(fpscr) ((void)(fpscr)) +#endif + + +/** @} end of CMSIS_Core_RegAccFunctions */ + + +/* ################### Compiler specific Intrinsics ########################### */ +/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics + Access to dedicated SIMD instructions + @{ +*/ + +#if (defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1)) + +#define __SADD8 __builtin_arm_sadd8 +#define __QADD8 __builtin_arm_qadd8 +#define __SHADD8 __builtin_arm_shadd8 +#define __UADD8 __builtin_arm_uadd8 +#define __UQADD8 __builtin_arm_uqadd8 +#define __UHADD8 __builtin_arm_uhadd8 +#define __SSUB8 __builtin_arm_ssub8 +#define __QSUB8 __builtin_arm_qsub8 +#define __SHSUB8 __builtin_arm_shsub8 +#define __USUB8 __builtin_arm_usub8 +#define __UQSUB8 __builtin_arm_uqsub8 +#define __UHSUB8 __builtin_arm_uhsub8 +#define __SADD16 __builtin_arm_sadd16 +#define __QADD16 __builtin_arm_qadd16 +#define __SHADD16 __builtin_arm_shadd16 +#define __UADD16 __builtin_arm_uadd16 +#define __UQADD16 __builtin_arm_uqadd16 +#define __UHADD16 __builtin_arm_uhadd16 +#define __SSUB16 __builtin_arm_ssub16 +#define __QSUB16 __builtin_arm_qsub16 +#define __SHSUB16 __builtin_arm_shsub16 +#define __USUB16 __builtin_arm_usub16 +#define __UQSUB16 __builtin_arm_uqsub16 +#define __UHSUB16 __builtin_arm_uhsub16 +#define __SASX __builtin_arm_sasx +#define __QASX __builtin_arm_qasx +#define __SHASX __builtin_arm_shasx +#define __UASX __builtin_arm_uasx +#define __UQASX __builtin_arm_uqasx +#define __UHASX __builtin_arm_uhasx +#define __SSAX __builtin_arm_ssax +#define __QSAX __builtin_arm_qsax +#define __SHSAX __builtin_arm_shsax +#define __USAX __builtin_arm_usax +#define __UQSAX __builtin_arm_uqsax +#define __UHSAX __builtin_arm_uhsax +#define __USAD8 __builtin_arm_usad8 +#define __USADA8 __builtin_arm_usada8 +#define __SSAT16 __builtin_arm_ssat16 +#define __USAT16 __builtin_arm_usat16 +#define __UXTB16 __builtin_arm_uxtb16 +#define __UXTAB16 __builtin_arm_uxtab16 +#define __SXTB16 __builtin_arm_sxtb16 +#define __SXTAB16 __builtin_arm_sxtab16 +#define __SMUAD __builtin_arm_smuad +#define __SMUADX __builtin_arm_smuadx +#define __SMLAD __builtin_arm_smlad +#define __SMLADX __builtin_arm_smladx +#define __SMLALD __builtin_arm_smlald +#define __SMLALDX __builtin_arm_smlaldx +#define __SMUSD __builtin_arm_smusd +#define __SMUSDX __builtin_arm_smusdx +#define __SMLSD __builtin_arm_smlsd +#define __SMLSDX __builtin_arm_smlsdx +#define __SMLSLD __builtin_arm_smlsld +#define __SMLSLDX __builtin_arm_smlsldx +#define __SEL __builtin_arm_sel +#define __QADD __builtin_arm_qadd +#define __QSUB __builtin_arm_qsub + +#define __PKHBT(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0x0000FFFFUL) | \ + ((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL) ) + +#define __PKHTB(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0xFFFF0000UL) | \ + ((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL) ) + +#define __SXTB16_RORn(ARG1, ARG2) __SXTB16(__ROR(ARG1, ARG2)) + +#define __SXTAB16_RORn(ARG1, ARG2, ARG3) __SXTAB16(ARG1, __ROR(ARG2, ARG3)) + +__STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) +{ + int32_t result; + + __ASM volatile ("smmla %0, %1, %2, %3" : "=r" (result): "r" (op1), "r" (op2), "r" (op3) ); + return(result); +} + +#endif /* (__ARM_FEATURE_DSP == 1) */ +/** @} end of group CMSIS_SIMD_intrinsics */ + + +#endif /* __CMSIS_TIARMCLANG_H */ diff --git a/lib/cmsis_core/cmsis_version.h b/lib/cmsis_core/cmsis_version.h new file mode 100644 index 00000000000..8b4765f186e --- /dev/null +++ b/lib/cmsis_core/cmsis_version.h @@ -0,0 +1,39 @@ +/**************************************************************************//** + * @file cmsis_version.h + * @brief CMSIS Core(M) Version definitions + * @version V5.0.5 + * @date 02. February 2022 + ******************************************************************************/ +/* + * Copyright (c) 2009-2022 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CMSIS_VERSION_H +#define __CMSIS_VERSION_H + +/* CMSIS Version definitions */ +#define __CM_CMSIS_VERSION_MAIN ( 5U) /*!< [31:16] CMSIS Core(M) main version */ +#define __CM_CMSIS_VERSION_SUB ( 6U) /*!< [15:0] CMSIS Core(M) sub version */ +#define __CM_CMSIS_VERSION ((__CM_CMSIS_VERSION_MAIN << 16U) | \ + __CM_CMSIS_VERSION_SUB ) /*!< CMSIS Core(M) version number */ +#endif diff --git a/lib/cmsis_core/core_cm4.h b/lib/cmsis_core/core_cm4.h new file mode 100644 index 00000000000..711c11326c9 --- /dev/null +++ b/lib/cmsis_core/core_cm4.h @@ -0,0 +1,2170 @@ +/**************************************************************************//** + * @file core_cm4.h + * @brief CMSIS Cortex-M4 Core Peripheral Access Layer Header File + * @version V5.2.0 + * @date 04. April 2023 + ******************************************************************************/ +/* + * Copyright (c) 2009-2023 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef __CORE_CM4_H_GENERIC +#define __CORE_CM4_H_GENERIC + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/** + \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ Function definitions in header files are used to allow 'inlining'. + + \li Required Rule 18.4, declaration of union type or object of union type: '{...}'.
+ Unions are used for effective representation of core registers. + + \li Advisory Rule 19.7, Function-like macro defined.
+ Function-like macros are used to allow more efficient code. + */ + + +/******************************************************************************* + * CMSIS definitions + ******************************************************************************/ +/** + \ingroup Cortex_M4 + @{ + */ + +#include "cmsis_version.h" + +/* CMSIS CM4 definitions */ +#define __CM4_CMSIS_VERSION_MAIN (__CM_CMSIS_VERSION_MAIN) /*!< \deprecated [31:16] CMSIS HAL main version */ +#define __CM4_CMSIS_VERSION_SUB (__CM_CMSIS_VERSION_SUB) /*!< \deprecated [15:0] CMSIS HAL sub version */ +#define __CM4_CMSIS_VERSION ((__CM4_CMSIS_VERSION_MAIN << 16U) | \ + __CM4_CMSIS_VERSION_SUB ) /*!< \deprecated CMSIS HAL version number */ + +#define __CORTEX_M (4U) /*!< Cortex-M Core */ + +/** __FPU_USED indicates whether an FPU is used or not. + For this, __FPU_PRESENT has to be checked prior to making use of FPU specific registers and functions. +*/ +#if defined ( __CC_ARM ) + #if defined __TARGET_FPU_VFP + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) + #if defined __ARM_FP + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined (__ti__) + #if defined (__ARM_FP) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #warning "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __GNUC__ ) + #if defined (__VFP_FP__) && !defined(__SOFTFP__) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __ICCARM__ ) + #if defined __ARMVFP__ + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __TI_ARM__ ) + #if defined __TI_VFP_SUPPORT__ + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __TASKING__ ) + #if defined __FPU_VFP__ + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#elif defined ( __CSMC__ ) + #if ( __CSMC__ & 0x400U) + #if defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U) + #define __FPU_USED 1U + #else + #error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)" + #define __FPU_USED 0U + #endif + #else + #define __FPU_USED 0U + #endif + +#endif + +#include "cmsis_compiler.h" /* CMSIS compiler specific defines */ + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM4_H_GENERIC */ + +#ifndef __CMSIS_GENERIC + +#ifndef __CORE_CM4_H_DEPENDANT +#define __CORE_CM4_H_DEPENDANT + +#ifdef __cplusplus + extern "C" { +#endif + +/* check device defines and use defaults */ +#if defined __CHECK_DEVICE_DEFINES + #ifndef __CM4_REV + #define __CM4_REV 0x0000U + #warning "__CM4_REV not defined in device header file; using default!" + #endif + + #ifndef __FPU_PRESENT + #define __FPU_PRESENT 0U + #warning "__FPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __MPU_PRESENT + #define __MPU_PRESENT 0U + #warning "__MPU_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __VTOR_PRESENT + #define __VTOR_PRESENT 1U + #warning "__VTOR_PRESENT not defined in device header file; using default!" + #endif + + #ifndef __NVIC_PRIO_BITS + #define __NVIC_PRIO_BITS 3U + #warning "__NVIC_PRIO_BITS not defined in device header file; using default!" + #endif + + #ifndef __Vendor_SysTickConfig + #define __Vendor_SysTickConfig 0U + #warning "__Vendor_SysTickConfig not defined in device header file; using default!" + #endif +#endif + +/* IO definitions (access restrictions to peripheral registers) */ +/** + \defgroup CMSIS_glob_defs CMSIS Global Defines + + IO Type Qualifiers are used + \li to specify the access to peripheral variables. + \li for automatic generation of peripheral register debug information. +*/ +#ifdef __cplusplus + #define __I volatile /*!< Defines 'read only' permissions */ +#else + #define __I volatile const /*!< Defines 'read only' permissions */ +#endif +#define __O volatile /*!< Defines 'write only' permissions */ +#define __IO volatile /*!< Defines 'read / write' permissions */ + +/* following defines should be used for structure members */ +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ + +/*@} end of group Cortex_M4 */ + + + +/******************************************************************************* + * Register Abstraction + Core Register contain: + - Core Register + - Core NVIC Register + - Core SCB Register + - Core SysTick Register + - Core Debug Register + - Core MPU Register + - Core FPU Register + ******************************************************************************/ +/** + \defgroup CMSIS_core_register Defines and Type Definitions + \brief Type definitions and defines for Cortex-M processor based devices. +*/ + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CORE Status and Control Registers + \brief Core Register type definitions. + @{ + */ + +/** + \brief Union type to access the Application Program Status Register (APSR). + */ +typedef union +{ + struct + { + uint32_t _reserved0:16; /*!< bit: 0..15 Reserved */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:7; /*!< bit: 20..26 Reserved */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} APSR_Type; + +/* APSR Register Definitions */ +#define APSR_N_Pos 31U /*!< APSR: N Position */ +#define APSR_N_Msk (1UL << APSR_N_Pos) /*!< APSR: N Mask */ + +#define APSR_Z_Pos 30U /*!< APSR: Z Position */ +#define APSR_Z_Msk (1UL << APSR_Z_Pos) /*!< APSR: Z Mask */ + +#define APSR_C_Pos 29U /*!< APSR: C Position */ +#define APSR_C_Msk (1UL << APSR_C_Pos) /*!< APSR: C Mask */ + +#define APSR_V_Pos 28U /*!< APSR: V Position */ +#define APSR_V_Msk (1UL << APSR_V_Pos) /*!< APSR: V Mask */ + +#define APSR_Q_Pos 27U /*!< APSR: Q Position */ +#define APSR_Q_Msk (1UL << APSR_Q_Pos) /*!< APSR: Q Mask */ + +#define APSR_GE_Pos 16U /*!< APSR: GE Position */ +#define APSR_GE_Msk (0xFUL << APSR_GE_Pos) /*!< APSR: GE Mask */ + + +/** + \brief Union type to access the Interrupt Program Status Register (IPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:23; /*!< bit: 9..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} IPSR_Type; + +/* IPSR Register Definitions */ +#define IPSR_ISR_Pos 0U /*!< IPSR: ISR Position */ +#define IPSR_ISR_Msk (0x1FFUL /*<< IPSR_ISR_Pos*/) /*!< IPSR: ISR Mask */ + + +/** + \brief Union type to access the Special-Purpose Program Status Registers (xPSR). + */ +typedef union +{ + struct + { + uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ + uint32_t _reserved0:1; /*!< bit: 9 Reserved */ + uint32_t ICI_IT_1:6; /*!< bit: 10..15 ICI/IT part 1 */ + uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ + uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ + uint32_t T:1; /*!< bit: 24 Thumb bit */ + uint32_t ICI_IT_2:2; /*!< bit: 25..26 ICI/IT part 2 */ + uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ + uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ + uint32_t C:1; /*!< bit: 29 Carry condition code flag */ + uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ + uint32_t N:1; /*!< bit: 31 Negative condition code flag */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} xPSR_Type; + +/* xPSR Register Definitions */ +#define xPSR_N_Pos 31U /*!< xPSR: N Position */ +#define xPSR_N_Msk (1UL << xPSR_N_Pos) /*!< xPSR: N Mask */ + +#define xPSR_Z_Pos 30U /*!< xPSR: Z Position */ +#define xPSR_Z_Msk (1UL << xPSR_Z_Pos) /*!< xPSR: Z Mask */ + +#define xPSR_C_Pos 29U /*!< xPSR: C Position */ +#define xPSR_C_Msk (1UL << xPSR_C_Pos) /*!< xPSR: C Mask */ + +#define xPSR_V_Pos 28U /*!< xPSR: V Position */ +#define xPSR_V_Msk (1UL << xPSR_V_Pos) /*!< xPSR: V Mask */ + +#define xPSR_Q_Pos 27U /*!< xPSR: Q Position */ +#define xPSR_Q_Msk (1UL << xPSR_Q_Pos) /*!< xPSR: Q Mask */ + +#define xPSR_ICI_IT_2_Pos 25U /*!< xPSR: ICI/IT part 2 Position */ +#define xPSR_ICI_IT_2_Msk (3UL << xPSR_ICI_IT_2_Pos) /*!< xPSR: ICI/IT part 2 Mask */ + +#define xPSR_T_Pos 24U /*!< xPSR: T Position */ +#define xPSR_T_Msk (1UL << xPSR_T_Pos) /*!< xPSR: T Mask */ + +#define xPSR_GE_Pos 16U /*!< xPSR: GE Position */ +#define xPSR_GE_Msk (0xFUL << xPSR_GE_Pos) /*!< xPSR: GE Mask */ + +#define xPSR_ICI_IT_1_Pos 10U /*!< xPSR: ICI/IT part 1 Position */ +#define xPSR_ICI_IT_1_Msk (0x3FUL << xPSR_ICI_IT_1_Pos) /*!< xPSR: ICI/IT part 1 Mask */ + +#define xPSR_ISR_Pos 0U /*!< xPSR: ISR Position */ +#define xPSR_ISR_Msk (0x1FFUL /*<< xPSR_ISR_Pos*/) /*!< xPSR: ISR Mask */ + + +/** + \brief Union type to access the Control Registers (CONTROL). + */ +typedef union +{ + struct + { + uint32_t nPRIV:1; /*!< bit: 0 Execution privilege in Thread mode */ + uint32_t SPSEL:1; /*!< bit: 1 Stack to be used */ + uint32_t FPCA:1; /*!< bit: 2 FP extension active flag */ + uint32_t _reserved0:29; /*!< bit: 3..31 Reserved */ + } b; /*!< Structure used for bit access */ + uint32_t w; /*!< Type used for word access */ +} CONTROL_Type; + +/* CONTROL Register Definitions */ +#define CONTROL_FPCA_Pos 2U /*!< CONTROL: FPCA Position */ +#define CONTROL_FPCA_Msk (1UL << CONTROL_FPCA_Pos) /*!< CONTROL: FPCA Mask */ + +#define CONTROL_SPSEL_Pos 1U /*!< CONTROL: SPSEL Position */ +#define CONTROL_SPSEL_Msk (1UL << CONTROL_SPSEL_Pos) /*!< CONTROL: SPSEL Mask */ + +#define CONTROL_nPRIV_Pos 0U /*!< CONTROL: nPRIV Position */ +#define CONTROL_nPRIV_Msk (1UL /*<< CONTROL_nPRIV_Pos*/) /*!< CONTROL: nPRIV Mask */ + +/*@} end of group CMSIS_CORE */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC) + \brief Type definitions for the NVIC Registers + @{ + */ + +/** + \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC). + */ +typedef struct +{ + __IOM uint32_t ISER[8U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */ + uint32_t RESERVED0[24U]; + __IOM uint32_t ICER[8U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */ + uint32_t RESERVED1[24U]; + __IOM uint32_t ISPR[8U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */ + uint32_t RESERVED2[24U]; + __IOM uint32_t ICPR[8U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */ + uint32_t RESERVED3[24U]; + __IOM uint32_t IABR[8U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */ + uint32_t RESERVED4[56U]; + __IOM uint8_t IP[240U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */ + uint32_t RESERVED5[644U]; + __OM uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */ +} NVIC_Type; + +/* Software Triggered Interrupt Register Definitions */ +#define NVIC_STIR_INTID_Pos 0U /*!< STIR: INTLINESNUM Position */ +#define NVIC_STIR_INTID_Msk (0x1FFUL /*<< NVIC_STIR_INTID_Pos*/) /*!< STIR: INTLINESNUM Mask */ + +/*@} end of group CMSIS_NVIC */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCB System Control Block (SCB) + \brief Type definitions for the System Control Block Registers + @{ + */ + +/** + \brief Structure type to access the System Control Block (SCB). + */ +typedef struct +{ + __IM uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */ + __IOM uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */ + __IOM uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */ + __IOM uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */ + __IOM uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */ + __IOM uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */ + __IOM uint8_t SHP[12U]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */ + __IOM uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */ + __IOM uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */ + __IOM uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */ + __IOM uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */ + __IOM uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */ + __IOM uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */ + __IOM uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */ + __IM uint32_t PFR[2U]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */ + __IM uint32_t DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */ + __IM uint32_t ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */ + __IM uint32_t MMFR[4U]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */ + __IM uint32_t ISAR[5U]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */ + uint32_t RESERVED0[5U]; + __IOM uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */ +} SCB_Type; + +/* SCB CPUID Register Definitions */ +#define SCB_CPUID_IMPLEMENTER_Pos 24U /*!< SCB CPUID: IMPLEMENTER Position */ +#define SCB_CPUID_IMPLEMENTER_Msk (0xFFUL << SCB_CPUID_IMPLEMENTER_Pos) /*!< SCB CPUID: IMPLEMENTER Mask */ + +#define SCB_CPUID_VARIANT_Pos 20U /*!< SCB CPUID: VARIANT Position */ +#define SCB_CPUID_VARIANT_Msk (0xFUL << SCB_CPUID_VARIANT_Pos) /*!< SCB CPUID: VARIANT Mask */ + +#define SCB_CPUID_ARCHITECTURE_Pos 16U /*!< SCB CPUID: ARCHITECTURE Position */ +#define SCB_CPUID_ARCHITECTURE_Msk (0xFUL << SCB_CPUID_ARCHITECTURE_Pos) /*!< SCB CPUID: ARCHITECTURE Mask */ + +#define SCB_CPUID_PARTNO_Pos 4U /*!< SCB CPUID: PARTNO Position */ +#define SCB_CPUID_PARTNO_Msk (0xFFFUL << SCB_CPUID_PARTNO_Pos) /*!< SCB CPUID: PARTNO Mask */ + +#define SCB_CPUID_REVISION_Pos 0U /*!< SCB CPUID: REVISION Position */ +#define SCB_CPUID_REVISION_Msk (0xFUL /*<< SCB_CPUID_REVISION_Pos*/) /*!< SCB CPUID: REVISION Mask */ + +/* SCB Interrupt Control State Register Definitions */ +#define SCB_ICSR_NMIPENDSET_Pos 31U /*!< SCB ICSR: NMIPENDSET Position */ +#define SCB_ICSR_NMIPENDSET_Msk (1UL << SCB_ICSR_NMIPENDSET_Pos) /*!< SCB ICSR: NMIPENDSET Mask */ + +#define SCB_ICSR_PENDSVSET_Pos 28U /*!< SCB ICSR: PENDSVSET Position */ +#define SCB_ICSR_PENDSVSET_Msk (1UL << SCB_ICSR_PENDSVSET_Pos) /*!< SCB ICSR: PENDSVSET Mask */ + +#define SCB_ICSR_PENDSVCLR_Pos 27U /*!< SCB ICSR: PENDSVCLR Position */ +#define SCB_ICSR_PENDSVCLR_Msk (1UL << SCB_ICSR_PENDSVCLR_Pos) /*!< SCB ICSR: PENDSVCLR Mask */ + +#define SCB_ICSR_PENDSTSET_Pos 26U /*!< SCB ICSR: PENDSTSET Position */ +#define SCB_ICSR_PENDSTSET_Msk (1UL << SCB_ICSR_PENDSTSET_Pos) /*!< SCB ICSR: PENDSTSET Mask */ + +#define SCB_ICSR_PENDSTCLR_Pos 25U /*!< SCB ICSR: PENDSTCLR Position */ +#define SCB_ICSR_PENDSTCLR_Msk (1UL << SCB_ICSR_PENDSTCLR_Pos) /*!< SCB ICSR: PENDSTCLR Mask */ + +#define SCB_ICSR_ISRPREEMPT_Pos 23U /*!< SCB ICSR: ISRPREEMPT Position */ +#define SCB_ICSR_ISRPREEMPT_Msk (1UL << SCB_ICSR_ISRPREEMPT_Pos) /*!< SCB ICSR: ISRPREEMPT Mask */ + +#define SCB_ICSR_ISRPENDING_Pos 22U /*!< SCB ICSR: ISRPENDING Position */ +#define SCB_ICSR_ISRPENDING_Msk (1UL << SCB_ICSR_ISRPENDING_Pos) /*!< SCB ICSR: ISRPENDING Mask */ + +#define SCB_ICSR_VECTPENDING_Pos 12U /*!< SCB ICSR: VECTPENDING Position */ +#define SCB_ICSR_VECTPENDING_Msk (0x1FFUL << SCB_ICSR_VECTPENDING_Pos) /*!< SCB ICSR: VECTPENDING Mask */ + +#define SCB_ICSR_RETTOBASE_Pos 11U /*!< SCB ICSR: RETTOBASE Position */ +#define SCB_ICSR_RETTOBASE_Msk (1UL << SCB_ICSR_RETTOBASE_Pos) /*!< SCB ICSR: RETTOBASE Mask */ + +#define SCB_ICSR_VECTACTIVE_Pos 0U /*!< SCB ICSR: VECTACTIVE Position */ +#define SCB_ICSR_VECTACTIVE_Msk (0x1FFUL /*<< SCB_ICSR_VECTACTIVE_Pos*/) /*!< SCB ICSR: VECTACTIVE Mask */ + +/* SCB Vector Table Offset Register Definitions */ +#define SCB_VTOR_TBLOFF_Pos 7U /*!< SCB VTOR: TBLOFF Position */ +#define SCB_VTOR_TBLOFF_Msk (0x1FFFFFFUL << SCB_VTOR_TBLOFF_Pos) /*!< SCB VTOR: TBLOFF Mask */ + +/* SCB Application Interrupt and Reset Control Register Definitions */ +#define SCB_AIRCR_VECTKEY_Pos 16U /*!< SCB AIRCR: VECTKEY Position */ +#define SCB_AIRCR_VECTKEY_Msk (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos) /*!< SCB AIRCR: VECTKEY Mask */ + +#define SCB_AIRCR_VECTKEYSTAT_Pos 16U /*!< SCB AIRCR: VECTKEYSTAT Position */ +#define SCB_AIRCR_VECTKEYSTAT_Msk (0xFFFFUL << SCB_AIRCR_VECTKEYSTAT_Pos) /*!< SCB AIRCR: VECTKEYSTAT Mask */ + +#define SCB_AIRCR_ENDIANESS_Pos 15U /*!< SCB AIRCR: ENDIANESS Position */ +#define SCB_AIRCR_ENDIANESS_Msk (1UL << SCB_AIRCR_ENDIANESS_Pos) /*!< SCB AIRCR: ENDIANESS Mask */ + +#define SCB_AIRCR_PRIGROUP_Pos 8U /*!< SCB AIRCR: PRIGROUP Position */ +#define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ + +#define SCB_AIRCR_SYSRESETREQ_Pos 2U /*!< SCB AIRCR: SYSRESETREQ Position */ +#define SCB_AIRCR_SYSRESETREQ_Msk (1UL << SCB_AIRCR_SYSRESETREQ_Pos) /*!< SCB AIRCR: SYSRESETREQ Mask */ + +#define SCB_AIRCR_VECTCLRACTIVE_Pos 1U /*!< SCB AIRCR: VECTCLRACTIVE Position */ +#define SCB_AIRCR_VECTCLRACTIVE_Msk (1UL << SCB_AIRCR_VECTCLRACTIVE_Pos) /*!< SCB AIRCR: VECTCLRACTIVE Mask */ + +#define SCB_AIRCR_VECTRESET_Pos 0U /*!< SCB AIRCR: VECTRESET Position */ +#define SCB_AIRCR_VECTRESET_Msk (1UL /*<< SCB_AIRCR_VECTRESET_Pos*/) /*!< SCB AIRCR: VECTRESET Mask */ + +/* SCB System Control Register Definitions */ +#define SCB_SCR_SEVONPEND_Pos 4U /*!< SCB SCR: SEVONPEND Position */ +#define SCB_SCR_SEVONPEND_Msk (1UL << SCB_SCR_SEVONPEND_Pos) /*!< SCB SCR: SEVONPEND Mask */ + +#define SCB_SCR_SLEEPDEEP_Pos 2U /*!< SCB SCR: SLEEPDEEP Position */ +#define SCB_SCR_SLEEPDEEP_Msk (1UL << SCB_SCR_SLEEPDEEP_Pos) /*!< SCB SCR: SLEEPDEEP Mask */ + +#define SCB_SCR_SLEEPONEXIT_Pos 1U /*!< SCB SCR: SLEEPONEXIT Position */ +#define SCB_SCR_SLEEPONEXIT_Msk (1UL << SCB_SCR_SLEEPONEXIT_Pos) /*!< SCB SCR: SLEEPONEXIT Mask */ + +/* SCB Configuration Control Register Definitions */ +#define SCB_CCR_STKALIGN_Pos 9U /*!< SCB CCR: STKALIGN Position */ +#define SCB_CCR_STKALIGN_Msk (1UL << SCB_CCR_STKALIGN_Pos) /*!< SCB CCR: STKALIGN Mask */ + +#define SCB_CCR_BFHFNMIGN_Pos 8U /*!< SCB CCR: BFHFNMIGN Position */ +#define SCB_CCR_BFHFNMIGN_Msk (1UL << SCB_CCR_BFHFNMIGN_Pos) /*!< SCB CCR: BFHFNMIGN Mask */ + +#define SCB_CCR_DIV_0_TRP_Pos 4U /*!< SCB CCR: DIV_0_TRP Position */ +#define SCB_CCR_DIV_0_TRP_Msk (1UL << SCB_CCR_DIV_0_TRP_Pos) /*!< SCB CCR: DIV_0_TRP Mask */ + +#define SCB_CCR_UNALIGN_TRP_Pos 3U /*!< SCB CCR: UNALIGN_TRP Position */ +#define SCB_CCR_UNALIGN_TRP_Msk (1UL << SCB_CCR_UNALIGN_TRP_Pos) /*!< SCB CCR: UNALIGN_TRP Mask */ + +#define SCB_CCR_USERSETMPEND_Pos 1U /*!< SCB CCR: USERSETMPEND Position */ +#define SCB_CCR_USERSETMPEND_Msk (1UL << SCB_CCR_USERSETMPEND_Pos) /*!< SCB CCR: USERSETMPEND Mask */ + +#define SCB_CCR_NONBASETHRDENA_Pos 0U /*!< SCB CCR: NONBASETHRDENA Position */ +#define SCB_CCR_NONBASETHRDENA_Msk (1UL /*<< SCB_CCR_NONBASETHRDENA_Pos*/) /*!< SCB CCR: NONBASETHRDENA Mask */ + +/* SCB System Handler Control and State Register Definitions */ +#define SCB_SHCSR_USGFAULTENA_Pos 18U /*!< SCB SHCSR: USGFAULTENA Position */ +#define SCB_SHCSR_USGFAULTENA_Msk (1UL << SCB_SHCSR_USGFAULTENA_Pos) /*!< SCB SHCSR: USGFAULTENA Mask */ + +#define SCB_SHCSR_BUSFAULTENA_Pos 17U /*!< SCB SHCSR: BUSFAULTENA Position */ +#define SCB_SHCSR_BUSFAULTENA_Msk (1UL << SCB_SHCSR_BUSFAULTENA_Pos) /*!< SCB SHCSR: BUSFAULTENA Mask */ + +#define SCB_SHCSR_MEMFAULTENA_Pos 16U /*!< SCB SHCSR: MEMFAULTENA Position */ +#define SCB_SHCSR_MEMFAULTENA_Msk (1UL << SCB_SHCSR_MEMFAULTENA_Pos) /*!< SCB SHCSR: MEMFAULTENA Mask */ + +#define SCB_SHCSR_SVCALLPENDED_Pos 15U /*!< SCB SHCSR: SVCALLPENDED Position */ +#define SCB_SHCSR_SVCALLPENDED_Msk (1UL << SCB_SHCSR_SVCALLPENDED_Pos) /*!< SCB SHCSR: SVCALLPENDED Mask */ + +#define SCB_SHCSR_BUSFAULTPENDED_Pos 14U /*!< SCB SHCSR: BUSFAULTPENDED Position */ +#define SCB_SHCSR_BUSFAULTPENDED_Msk (1UL << SCB_SHCSR_BUSFAULTPENDED_Pos) /*!< SCB SHCSR: BUSFAULTPENDED Mask */ + +#define SCB_SHCSR_MEMFAULTPENDED_Pos 13U /*!< SCB SHCSR: MEMFAULTPENDED Position */ +#define SCB_SHCSR_MEMFAULTPENDED_Msk (1UL << SCB_SHCSR_MEMFAULTPENDED_Pos) /*!< SCB SHCSR: MEMFAULTPENDED Mask */ + +#define SCB_SHCSR_USGFAULTPENDED_Pos 12U /*!< SCB SHCSR: USGFAULTPENDED Position */ +#define SCB_SHCSR_USGFAULTPENDED_Msk (1UL << SCB_SHCSR_USGFAULTPENDED_Pos) /*!< SCB SHCSR: USGFAULTPENDED Mask */ + +#define SCB_SHCSR_SYSTICKACT_Pos 11U /*!< SCB SHCSR: SYSTICKACT Position */ +#define SCB_SHCSR_SYSTICKACT_Msk (1UL << SCB_SHCSR_SYSTICKACT_Pos) /*!< SCB SHCSR: SYSTICKACT Mask */ + +#define SCB_SHCSR_PENDSVACT_Pos 10U /*!< SCB SHCSR: PENDSVACT Position */ +#define SCB_SHCSR_PENDSVACT_Msk (1UL << SCB_SHCSR_PENDSVACT_Pos) /*!< SCB SHCSR: PENDSVACT Mask */ + +#define SCB_SHCSR_MONITORACT_Pos 8U /*!< SCB SHCSR: MONITORACT Position */ +#define SCB_SHCSR_MONITORACT_Msk (1UL << SCB_SHCSR_MONITORACT_Pos) /*!< SCB SHCSR: MONITORACT Mask */ + +#define SCB_SHCSR_SVCALLACT_Pos 7U /*!< SCB SHCSR: SVCALLACT Position */ +#define SCB_SHCSR_SVCALLACT_Msk (1UL << SCB_SHCSR_SVCALLACT_Pos) /*!< SCB SHCSR: SVCALLACT Mask */ + +#define SCB_SHCSR_USGFAULTACT_Pos 3U /*!< SCB SHCSR: USGFAULTACT Position */ +#define SCB_SHCSR_USGFAULTACT_Msk (1UL << SCB_SHCSR_USGFAULTACT_Pos) /*!< SCB SHCSR: USGFAULTACT Mask */ + +#define SCB_SHCSR_BUSFAULTACT_Pos 1U /*!< SCB SHCSR: BUSFAULTACT Position */ +#define SCB_SHCSR_BUSFAULTACT_Msk (1UL << SCB_SHCSR_BUSFAULTACT_Pos) /*!< SCB SHCSR: BUSFAULTACT Mask */ + +#define SCB_SHCSR_MEMFAULTACT_Pos 0U /*!< SCB SHCSR: MEMFAULTACT Position */ +#define SCB_SHCSR_MEMFAULTACT_Msk (1UL /*<< SCB_SHCSR_MEMFAULTACT_Pos*/) /*!< SCB SHCSR: MEMFAULTACT Mask */ + +/* SCB Configurable Fault Status Register Definitions */ +#define SCB_CFSR_USGFAULTSR_Pos 16U /*!< SCB CFSR: Usage Fault Status Register Position */ +#define SCB_CFSR_USGFAULTSR_Msk (0xFFFFUL << SCB_CFSR_USGFAULTSR_Pos) /*!< SCB CFSR: Usage Fault Status Register Mask */ + +#define SCB_CFSR_BUSFAULTSR_Pos 8U /*!< SCB CFSR: Bus Fault Status Register Position */ +#define SCB_CFSR_BUSFAULTSR_Msk (0xFFUL << SCB_CFSR_BUSFAULTSR_Pos) /*!< SCB CFSR: Bus Fault Status Register Mask */ + +#define SCB_CFSR_MEMFAULTSR_Pos 0U /*!< SCB CFSR: Memory Manage Fault Status Register Position */ +#define SCB_CFSR_MEMFAULTSR_Msk (0xFFUL /*<< SCB_CFSR_MEMFAULTSR_Pos*/) /*!< SCB CFSR: Memory Manage Fault Status Register Mask */ + +/* MemManage Fault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_MMARVALID_Pos (SCB_CFSR_MEMFAULTSR_Pos + 7U) /*!< SCB CFSR (MMFSR): MMARVALID Position */ +#define SCB_CFSR_MMARVALID_Msk (1UL << SCB_CFSR_MMARVALID_Pos) /*!< SCB CFSR (MMFSR): MMARVALID Mask */ + +#define SCB_CFSR_MLSPERR_Pos (SCB_CFSR_MEMFAULTSR_Pos + 5U) /*!< SCB CFSR (MMFSR): MLSPERR Position */ +#define SCB_CFSR_MLSPERR_Msk (1UL << SCB_CFSR_MLSPERR_Pos) /*!< SCB CFSR (MMFSR): MLSPERR Mask */ + +#define SCB_CFSR_MSTKERR_Pos (SCB_CFSR_MEMFAULTSR_Pos + 4U) /*!< SCB CFSR (MMFSR): MSTKERR Position */ +#define SCB_CFSR_MSTKERR_Msk (1UL << SCB_CFSR_MSTKERR_Pos) /*!< SCB CFSR (MMFSR): MSTKERR Mask */ + +#define SCB_CFSR_MUNSTKERR_Pos (SCB_CFSR_MEMFAULTSR_Pos + 3U) /*!< SCB CFSR (MMFSR): MUNSTKERR Position */ +#define SCB_CFSR_MUNSTKERR_Msk (1UL << SCB_CFSR_MUNSTKERR_Pos) /*!< SCB CFSR (MMFSR): MUNSTKERR Mask */ + +#define SCB_CFSR_DACCVIOL_Pos (SCB_CFSR_MEMFAULTSR_Pos + 1U) /*!< SCB CFSR (MMFSR): DACCVIOL Position */ +#define SCB_CFSR_DACCVIOL_Msk (1UL << SCB_CFSR_DACCVIOL_Pos) /*!< SCB CFSR (MMFSR): DACCVIOL Mask */ + +#define SCB_CFSR_IACCVIOL_Pos (SCB_CFSR_MEMFAULTSR_Pos + 0U) /*!< SCB CFSR (MMFSR): IACCVIOL Position */ +#define SCB_CFSR_IACCVIOL_Msk (1UL /*<< SCB_CFSR_IACCVIOL_Pos*/) /*!< SCB CFSR (MMFSR): IACCVIOL Mask */ + +/* BusFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_BFARVALID_Pos (SCB_CFSR_BUSFAULTSR_Pos + 7U) /*!< SCB CFSR (BFSR): BFARVALID Position */ +#define SCB_CFSR_BFARVALID_Msk (1UL << SCB_CFSR_BFARVALID_Pos) /*!< SCB CFSR (BFSR): BFARVALID Mask */ + +#define SCB_CFSR_LSPERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 5U) /*!< SCB CFSR (BFSR): LSPERR Position */ +#define SCB_CFSR_LSPERR_Msk (1UL << SCB_CFSR_LSPERR_Pos) /*!< SCB CFSR (BFSR): LSPERR Mask */ + +#define SCB_CFSR_STKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 4U) /*!< SCB CFSR (BFSR): STKERR Position */ +#define SCB_CFSR_STKERR_Msk (1UL << SCB_CFSR_STKERR_Pos) /*!< SCB CFSR (BFSR): STKERR Mask */ + +#define SCB_CFSR_UNSTKERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 3U) /*!< SCB CFSR (BFSR): UNSTKERR Position */ +#define SCB_CFSR_UNSTKERR_Msk (1UL << SCB_CFSR_UNSTKERR_Pos) /*!< SCB CFSR (BFSR): UNSTKERR Mask */ + +#define SCB_CFSR_IMPRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 2U) /*!< SCB CFSR (BFSR): IMPRECISERR Position */ +#define SCB_CFSR_IMPRECISERR_Msk (1UL << SCB_CFSR_IMPRECISERR_Pos) /*!< SCB CFSR (BFSR): IMPRECISERR Mask */ + +#define SCB_CFSR_PRECISERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 1U) /*!< SCB CFSR (BFSR): PRECISERR Position */ +#define SCB_CFSR_PRECISERR_Msk (1UL << SCB_CFSR_PRECISERR_Pos) /*!< SCB CFSR (BFSR): PRECISERR Mask */ + +#define SCB_CFSR_IBUSERR_Pos (SCB_CFSR_BUSFAULTSR_Pos + 0U) /*!< SCB CFSR (BFSR): IBUSERR Position */ +#define SCB_CFSR_IBUSERR_Msk (1UL << SCB_CFSR_IBUSERR_Pos) /*!< SCB CFSR (BFSR): IBUSERR Mask */ + +/* UsageFault Status Register (part of SCB Configurable Fault Status Register) */ +#define SCB_CFSR_DIVBYZERO_Pos (SCB_CFSR_USGFAULTSR_Pos + 9U) /*!< SCB CFSR (UFSR): DIVBYZERO Position */ +#define SCB_CFSR_DIVBYZERO_Msk (1UL << SCB_CFSR_DIVBYZERO_Pos) /*!< SCB CFSR (UFSR): DIVBYZERO Mask */ + +#define SCB_CFSR_UNALIGNED_Pos (SCB_CFSR_USGFAULTSR_Pos + 8U) /*!< SCB CFSR (UFSR): UNALIGNED Position */ +#define SCB_CFSR_UNALIGNED_Msk (1UL << SCB_CFSR_UNALIGNED_Pos) /*!< SCB CFSR (UFSR): UNALIGNED Mask */ + +#define SCB_CFSR_NOCP_Pos (SCB_CFSR_USGFAULTSR_Pos + 3U) /*!< SCB CFSR (UFSR): NOCP Position */ +#define SCB_CFSR_NOCP_Msk (1UL << SCB_CFSR_NOCP_Pos) /*!< SCB CFSR (UFSR): NOCP Mask */ + +#define SCB_CFSR_INVPC_Pos (SCB_CFSR_USGFAULTSR_Pos + 2U) /*!< SCB CFSR (UFSR): INVPC Position */ +#define SCB_CFSR_INVPC_Msk (1UL << SCB_CFSR_INVPC_Pos) /*!< SCB CFSR (UFSR): INVPC Mask */ + +#define SCB_CFSR_INVSTATE_Pos (SCB_CFSR_USGFAULTSR_Pos + 1U) /*!< SCB CFSR (UFSR): INVSTATE Position */ +#define SCB_CFSR_INVSTATE_Msk (1UL << SCB_CFSR_INVSTATE_Pos) /*!< SCB CFSR (UFSR): INVSTATE Mask */ + +#define SCB_CFSR_UNDEFINSTR_Pos (SCB_CFSR_USGFAULTSR_Pos + 0U) /*!< SCB CFSR (UFSR): UNDEFINSTR Position */ +#define SCB_CFSR_UNDEFINSTR_Msk (1UL << SCB_CFSR_UNDEFINSTR_Pos) /*!< SCB CFSR (UFSR): UNDEFINSTR Mask */ + +/* SCB Hard Fault Status Register Definitions */ +#define SCB_HFSR_DEBUGEVT_Pos 31U /*!< SCB HFSR: DEBUGEVT Position */ +#define SCB_HFSR_DEBUGEVT_Msk (1UL << SCB_HFSR_DEBUGEVT_Pos) /*!< SCB HFSR: DEBUGEVT Mask */ + +#define SCB_HFSR_FORCED_Pos 30U /*!< SCB HFSR: FORCED Position */ +#define SCB_HFSR_FORCED_Msk (1UL << SCB_HFSR_FORCED_Pos) /*!< SCB HFSR: FORCED Mask */ + +#define SCB_HFSR_VECTTBL_Pos 1U /*!< SCB HFSR: VECTTBL Position */ +#define SCB_HFSR_VECTTBL_Msk (1UL << SCB_HFSR_VECTTBL_Pos) /*!< SCB HFSR: VECTTBL Mask */ + +/* SCB Debug Fault Status Register Definitions */ +#define SCB_DFSR_EXTERNAL_Pos 4U /*!< SCB DFSR: EXTERNAL Position */ +#define SCB_DFSR_EXTERNAL_Msk (1UL << SCB_DFSR_EXTERNAL_Pos) /*!< SCB DFSR: EXTERNAL Mask */ + +#define SCB_DFSR_VCATCH_Pos 3U /*!< SCB DFSR: VCATCH Position */ +#define SCB_DFSR_VCATCH_Msk (1UL << SCB_DFSR_VCATCH_Pos) /*!< SCB DFSR: VCATCH Mask */ + +#define SCB_DFSR_DWTTRAP_Pos 2U /*!< SCB DFSR: DWTTRAP Position */ +#define SCB_DFSR_DWTTRAP_Msk (1UL << SCB_DFSR_DWTTRAP_Pos) /*!< SCB DFSR: DWTTRAP Mask */ + +#define SCB_DFSR_BKPT_Pos 1U /*!< SCB DFSR: BKPT Position */ +#define SCB_DFSR_BKPT_Msk (1UL << SCB_DFSR_BKPT_Pos) /*!< SCB DFSR: BKPT Mask */ + +#define SCB_DFSR_HALTED_Pos 0U /*!< SCB DFSR: HALTED Position */ +#define SCB_DFSR_HALTED_Msk (1UL /*<< SCB_DFSR_HALTED_Pos*/) /*!< SCB DFSR: HALTED Mask */ + +/*@} end of group CMSIS_SCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SCnSCB System Controls not in SCB (SCnSCB) + \brief Type definitions for the System Control and ID Register not in the SCB + @{ + */ + +/** + \brief Structure type to access the System Control and ID Register not in the SCB. + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IM uint32_t ICTR; /*!< Offset: 0x004 (R/ ) Interrupt Controller Type Register */ + __IOM uint32_t ACTLR; /*!< Offset: 0x008 (R/W) Auxiliary Control Register */ +} SCnSCB_Type; + +/* Interrupt Controller Type Register Definitions */ +#define SCnSCB_ICTR_INTLINESNUM_Pos 0U /*!< ICTR: INTLINESNUM Position */ +#define SCnSCB_ICTR_INTLINESNUM_Msk (0xFUL /*<< SCnSCB_ICTR_INTLINESNUM_Pos*/) /*!< ICTR: INTLINESNUM Mask */ + +/* Auxiliary Control Register Definitions */ +#define SCnSCB_ACTLR_DISOOFP_Pos 9U /*!< ACTLR: DISOOFP Position */ +#define SCnSCB_ACTLR_DISOOFP_Msk (1UL << SCnSCB_ACTLR_DISOOFP_Pos) /*!< ACTLR: DISOOFP Mask */ + +#define SCnSCB_ACTLR_DISFPCA_Pos 8U /*!< ACTLR: DISFPCA Position */ +#define SCnSCB_ACTLR_DISFPCA_Msk (1UL << SCnSCB_ACTLR_DISFPCA_Pos) /*!< ACTLR: DISFPCA Mask */ + +#define SCnSCB_ACTLR_DISFOLD_Pos 2U /*!< ACTLR: DISFOLD Position */ +#define SCnSCB_ACTLR_DISFOLD_Msk (1UL << SCnSCB_ACTLR_DISFOLD_Pos) /*!< ACTLR: DISFOLD Mask */ + +#define SCnSCB_ACTLR_DISDEFWBUF_Pos 1U /*!< ACTLR: DISDEFWBUF Position */ +#define SCnSCB_ACTLR_DISDEFWBUF_Msk (1UL << SCnSCB_ACTLR_DISDEFWBUF_Pos) /*!< ACTLR: DISDEFWBUF Mask */ + +#define SCnSCB_ACTLR_DISMCYCINT_Pos 0U /*!< ACTLR: DISMCYCINT Position */ +#define SCnSCB_ACTLR_DISMCYCINT_Msk (1UL /*<< SCnSCB_ACTLR_DISMCYCINT_Pos*/) /*!< ACTLR: DISMCYCINT Mask */ + +/*@} end of group CMSIS_SCnotSCB */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_SysTick System Tick Timer (SysTick) + \brief Type definitions for the System Timer Registers. + @{ + */ + +/** + \brief Structure type to access the System Timer (SysTick). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */ + __IOM uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */ + __IOM uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */ + __IM uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */ +} SysTick_Type; + +/* SysTick Control / Status Register Definitions */ +#define SysTick_CTRL_COUNTFLAG_Pos 16U /*!< SysTick CTRL: COUNTFLAG Position */ +#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ + +#define SysTick_CTRL_CLKSOURCE_Pos 2U /*!< SysTick CTRL: CLKSOURCE Position */ +#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ + +#define SysTick_CTRL_TICKINT_Pos 1U /*!< SysTick CTRL: TICKINT Position */ +#define SysTick_CTRL_TICKINT_Msk (1UL << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ + +#define SysTick_CTRL_ENABLE_Pos 0U /*!< SysTick CTRL: ENABLE Position */ +#define SysTick_CTRL_ENABLE_Msk (1UL /*<< SysTick_CTRL_ENABLE_Pos*/) /*!< SysTick CTRL: ENABLE Mask */ + +/* SysTick Reload Register Definitions */ +#define SysTick_LOAD_RELOAD_Pos 0U /*!< SysTick LOAD: RELOAD Position */ +#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFUL /*<< SysTick_LOAD_RELOAD_Pos*/) /*!< SysTick LOAD: RELOAD Mask */ + +/* SysTick Current Register Definitions */ +#define SysTick_VAL_CURRENT_Pos 0U /*!< SysTick VAL: CURRENT Position */ +#define SysTick_VAL_CURRENT_Msk (0xFFFFFFUL /*<< SysTick_VAL_CURRENT_Pos*/) /*!< SysTick VAL: CURRENT Mask */ + +/* SysTick Calibration Register Definitions */ +#define SysTick_CALIB_NOREF_Pos 31U /*!< SysTick CALIB: NOREF Position */ +#define SysTick_CALIB_NOREF_Msk (1UL << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ + +#define SysTick_CALIB_SKEW_Pos 30U /*!< SysTick CALIB: SKEW Position */ +#define SysTick_CALIB_SKEW_Msk (1UL << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ + +#define SysTick_CALIB_TENMS_Pos 0U /*!< SysTick CALIB: TENMS Position */ +#define SysTick_CALIB_TENMS_Msk (0xFFFFFFUL /*<< SysTick_CALIB_TENMS_Pos*/) /*!< SysTick CALIB: TENMS Mask */ + +/*@} end of group CMSIS_SysTick */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_ITM Instrumentation Trace Macrocell (ITM) + \brief Type definitions for the Instrumentation Trace Macrocell (ITM) + @{ + */ + +/** + \brief Structure type to access the Instrumentation Trace Macrocell Register (ITM). + */ +typedef struct +{ + __OM union + { + __OM uint8_t u8; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 8-bit */ + __OM uint16_t u16; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 16-bit */ + __OM uint32_t u32; /*!< Offset: 0x000 ( /W) ITM Stimulus Port 32-bit */ + } PORT [32U]; /*!< Offset: 0x000 ( /W) ITM Stimulus Port Registers */ + uint32_t RESERVED0[864U]; + __IOM uint32_t TER; /*!< Offset: 0xE00 (R/W) ITM Trace Enable Register */ + uint32_t RESERVED1[15U]; + __IOM uint32_t TPR; /*!< Offset: 0xE40 (R/W) ITM Trace Privilege Register */ + uint32_t RESERVED2[15U]; + __IOM uint32_t TCR; /*!< Offset: 0xE80 (R/W) ITM Trace Control Register */ + uint32_t RESERVED3[32U]; + uint32_t RESERVED4[43U]; + __OM uint32_t LAR; /*!< Offset: 0xFB0 ( /W) ITM Lock Access Register */ + __IM uint32_t LSR; /*!< Offset: 0xFB4 (R/ ) ITM Lock Status Register */ + uint32_t RESERVED5[6U]; + __IM uint32_t PID4; /*!< Offset: 0xFD0 (R/ ) ITM Peripheral Identification Register #4 */ + __IM uint32_t PID5; /*!< Offset: 0xFD4 (R/ ) ITM Peripheral Identification Register #5 */ + __IM uint32_t PID6; /*!< Offset: 0xFD8 (R/ ) ITM Peripheral Identification Register #6 */ + __IM uint32_t PID7; /*!< Offset: 0xFDC (R/ ) ITM Peripheral Identification Register #7 */ + __IM uint32_t PID0; /*!< Offset: 0xFE0 (R/ ) ITM Peripheral Identification Register #0 */ + __IM uint32_t PID1; /*!< Offset: 0xFE4 (R/ ) ITM Peripheral Identification Register #1 */ + __IM uint32_t PID2; /*!< Offset: 0xFE8 (R/ ) ITM Peripheral Identification Register #2 */ + __IM uint32_t PID3; /*!< Offset: 0xFEC (R/ ) ITM Peripheral Identification Register #3 */ + __IM uint32_t CID0; /*!< Offset: 0xFF0 (R/ ) ITM Component Identification Register #0 */ + __IM uint32_t CID1; /*!< Offset: 0xFF4 (R/ ) ITM Component Identification Register #1 */ + __IM uint32_t CID2; /*!< Offset: 0xFF8 (R/ ) ITM Component Identification Register #2 */ + __IM uint32_t CID3; /*!< Offset: 0xFFC (R/ ) ITM Component Identification Register #3 */ +} ITM_Type; + +/* ITM Trace Privilege Register Definitions */ +#define ITM_TPR_PRIVMASK_Pos 0U /*!< ITM TPR: PRIVMASK Position */ +#define ITM_TPR_PRIVMASK_Msk (0xFFFFFFFFUL /*<< ITM_TPR_PRIVMASK_Pos*/) /*!< ITM TPR: PRIVMASK Mask */ + +/* ITM Trace Control Register Definitions */ +#define ITM_TCR_BUSY_Pos 23U /*!< ITM TCR: BUSY Position */ +#define ITM_TCR_BUSY_Msk (1UL << ITM_TCR_BUSY_Pos) /*!< ITM TCR: BUSY Mask */ + +#define ITM_TCR_TRACEBUSID_Pos 16U /*!< ITM TCR: ATBID Position */ +#define ITM_TCR_TRACEBUSID_Msk (0x7FUL << ITM_TCR_TRACEBUSID_Pos) /*!< ITM TCR: ATBID Mask */ + +#define ITM_TCR_GTSFREQ_Pos 10U /*!< ITM TCR: Global timestamp frequency Position */ +#define ITM_TCR_GTSFREQ_Msk (3UL << ITM_TCR_GTSFREQ_Pos) /*!< ITM TCR: Global timestamp frequency Mask */ + +#define ITM_TCR_TSPRESCALE_Pos 8U /*!< ITM TCR: TSPrescale Position */ +#define ITM_TCR_TSPRESCALE_Msk (3UL << ITM_TCR_TSPRESCALE_Pos) /*!< ITM TCR: TSPrescale Mask */ + +#define ITM_TCR_SWOENA_Pos 4U /*!< ITM TCR: SWOENA Position */ +#define ITM_TCR_SWOENA_Msk (1UL << ITM_TCR_SWOENA_Pos) /*!< ITM TCR: SWOENA Mask */ + +#define ITM_TCR_DWTENA_Pos 3U /*!< ITM TCR: DWTENA Position */ +#define ITM_TCR_DWTENA_Msk (1UL << ITM_TCR_DWTENA_Pos) /*!< ITM TCR: DWTENA Mask */ + +#define ITM_TCR_SYNCENA_Pos 2U /*!< ITM TCR: SYNCENA Position */ +#define ITM_TCR_SYNCENA_Msk (1UL << ITM_TCR_SYNCENA_Pos) /*!< ITM TCR: SYNCENA Mask */ + +#define ITM_TCR_TSENA_Pos 1U /*!< ITM TCR: TSENA Position */ +#define ITM_TCR_TSENA_Msk (1UL << ITM_TCR_TSENA_Pos) /*!< ITM TCR: TSENA Mask */ + +#define ITM_TCR_ITMENA_Pos 0U /*!< ITM TCR: ITM Enable bit Position */ +#define ITM_TCR_ITMENA_Msk (1UL /*<< ITM_TCR_ITMENA_Pos*/) /*!< ITM TCR: ITM Enable bit Mask */ + +/* ITM Lock Status Register Definitions */ +#define ITM_LSR_BYTEACC_Pos 2U /*!< ITM LSR: ByteAcc Position */ +#define ITM_LSR_BYTEACC_Msk (1UL << ITM_LSR_BYTEACC_Pos) /*!< ITM LSR: ByteAcc Mask */ + +#define ITM_LSR_ACCESS_Pos 1U /*!< ITM LSR: Access Position */ +#define ITM_LSR_ACCESS_Msk (1UL << ITM_LSR_ACCESS_Pos) /*!< ITM LSR: Access Mask */ + +#define ITM_LSR_PRESENT_Pos 0U /*!< ITM LSR: Present Position */ +#define ITM_LSR_PRESENT_Msk (1UL /*<< ITM_LSR_PRESENT_Pos*/) /*!< ITM LSR: Present Mask */ + +/*@}*/ /* end of group CMSIS_ITM */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_DWT Data Watchpoint and Trace (DWT) + \brief Type definitions for the Data Watchpoint and Trace (DWT) + @{ + */ + +/** + \brief Structure type to access the Data Watchpoint and Trace Register (DWT). + */ +typedef struct +{ + __IOM uint32_t CTRL; /*!< Offset: 0x000 (R/W) Control Register */ + __IOM uint32_t CYCCNT; /*!< Offset: 0x004 (R/W) Cycle Count Register */ + __IOM uint32_t CPICNT; /*!< Offset: 0x008 (R/W) CPI Count Register */ + __IOM uint32_t EXCCNT; /*!< Offset: 0x00C (R/W) Exception Overhead Count Register */ + __IOM uint32_t SLEEPCNT; /*!< Offset: 0x010 (R/W) Sleep Count Register */ + __IOM uint32_t LSUCNT; /*!< Offset: 0x014 (R/W) LSU Count Register */ + __IOM uint32_t FOLDCNT; /*!< Offset: 0x018 (R/W) Folded-instruction Count Register */ + __IM uint32_t PCSR; /*!< Offset: 0x01C (R/ ) Program Counter Sample Register */ + __IOM uint32_t COMP0; /*!< Offset: 0x020 (R/W) Comparator Register 0 */ + __IOM uint32_t MASK0; /*!< Offset: 0x024 (R/W) Mask Register 0 */ + __IOM uint32_t FUNCTION0; /*!< Offset: 0x028 (R/W) Function Register 0 */ + uint32_t RESERVED0[1U]; + __IOM uint32_t COMP1; /*!< Offset: 0x030 (R/W) Comparator Register 1 */ + __IOM uint32_t MASK1; /*!< Offset: 0x034 (R/W) Mask Register 1 */ + __IOM uint32_t FUNCTION1; /*!< Offset: 0x038 (R/W) Function Register 1 */ + uint32_t RESERVED1[1U]; + __IOM uint32_t COMP2; /*!< Offset: 0x040 (R/W) Comparator Register 2 */ + __IOM uint32_t MASK2; /*!< Offset: 0x044 (R/W) Mask Register 2 */ + __IOM uint32_t FUNCTION2; /*!< Offset: 0x048 (R/W) Function Register 2 */ + uint32_t RESERVED2[1U]; + __IOM uint32_t COMP3; /*!< Offset: 0x050 (R/W) Comparator Register 3 */ + __IOM uint32_t MASK3; /*!< Offset: 0x054 (R/W) Mask Register 3 */ + __IOM uint32_t FUNCTION3; /*!< Offset: 0x058 (R/W) Function Register 3 */ +} DWT_Type; + +/* DWT Control Register Definitions */ +#define DWT_CTRL_NUMCOMP_Pos 28U /*!< DWT CTRL: NUMCOMP Position */ +#define DWT_CTRL_NUMCOMP_Msk (0xFUL << DWT_CTRL_NUMCOMP_Pos) /*!< DWT CTRL: NUMCOMP Mask */ + +#define DWT_CTRL_NOTRCPKT_Pos 27U /*!< DWT CTRL: NOTRCPKT Position */ +#define DWT_CTRL_NOTRCPKT_Msk (0x1UL << DWT_CTRL_NOTRCPKT_Pos) /*!< DWT CTRL: NOTRCPKT Mask */ + +#define DWT_CTRL_NOEXTTRIG_Pos 26U /*!< DWT CTRL: NOEXTTRIG Position */ +#define DWT_CTRL_NOEXTTRIG_Msk (0x1UL << DWT_CTRL_NOEXTTRIG_Pos) /*!< DWT CTRL: NOEXTTRIG Mask */ + +#define DWT_CTRL_NOCYCCNT_Pos 25U /*!< DWT CTRL: NOCYCCNT Position */ +#define DWT_CTRL_NOCYCCNT_Msk (0x1UL << DWT_CTRL_NOCYCCNT_Pos) /*!< DWT CTRL: NOCYCCNT Mask */ + +#define DWT_CTRL_NOPRFCNT_Pos 24U /*!< DWT CTRL: NOPRFCNT Position */ +#define DWT_CTRL_NOPRFCNT_Msk (0x1UL << DWT_CTRL_NOPRFCNT_Pos) /*!< DWT CTRL: NOPRFCNT Mask */ + +#define DWT_CTRL_CYCEVTENA_Pos 22U /*!< DWT CTRL: CYCEVTENA Position */ +#define DWT_CTRL_CYCEVTENA_Msk (0x1UL << DWT_CTRL_CYCEVTENA_Pos) /*!< DWT CTRL: CYCEVTENA Mask */ + +#define DWT_CTRL_FOLDEVTENA_Pos 21U /*!< DWT CTRL: FOLDEVTENA Position */ +#define DWT_CTRL_FOLDEVTENA_Msk (0x1UL << DWT_CTRL_FOLDEVTENA_Pos) /*!< DWT CTRL: FOLDEVTENA Mask */ + +#define DWT_CTRL_LSUEVTENA_Pos 20U /*!< DWT CTRL: LSUEVTENA Position */ +#define DWT_CTRL_LSUEVTENA_Msk (0x1UL << DWT_CTRL_LSUEVTENA_Pos) /*!< DWT CTRL: LSUEVTENA Mask */ + +#define DWT_CTRL_SLEEPEVTENA_Pos 19U /*!< DWT CTRL: SLEEPEVTENA Position */ +#define DWT_CTRL_SLEEPEVTENA_Msk (0x1UL << DWT_CTRL_SLEEPEVTENA_Pos) /*!< DWT CTRL: SLEEPEVTENA Mask */ + +#define DWT_CTRL_EXCEVTENA_Pos 18U /*!< DWT CTRL: EXCEVTENA Position */ +#define DWT_CTRL_EXCEVTENA_Msk (0x1UL << DWT_CTRL_EXCEVTENA_Pos) /*!< DWT CTRL: EXCEVTENA Mask */ + +#define DWT_CTRL_CPIEVTENA_Pos 17U /*!< DWT CTRL: CPIEVTENA Position */ +#define DWT_CTRL_CPIEVTENA_Msk (0x1UL << DWT_CTRL_CPIEVTENA_Pos) /*!< DWT CTRL: CPIEVTENA Mask */ + +#define DWT_CTRL_EXCTRCENA_Pos 16U /*!< DWT CTRL: EXCTRCENA Position */ +#define DWT_CTRL_EXCTRCENA_Msk (0x1UL << DWT_CTRL_EXCTRCENA_Pos) /*!< DWT CTRL: EXCTRCENA Mask */ + +#define DWT_CTRL_PCSAMPLENA_Pos 12U /*!< DWT CTRL: PCSAMPLENA Position */ +#define DWT_CTRL_PCSAMPLENA_Msk (0x1UL << DWT_CTRL_PCSAMPLENA_Pos) /*!< DWT CTRL: PCSAMPLENA Mask */ + +#define DWT_CTRL_SYNCTAP_Pos 10U /*!< DWT CTRL: SYNCTAP Position */ +#define DWT_CTRL_SYNCTAP_Msk (0x3UL << DWT_CTRL_SYNCTAP_Pos) /*!< DWT CTRL: SYNCTAP Mask */ + +#define DWT_CTRL_CYCTAP_Pos 9U /*!< DWT CTRL: CYCTAP Position */ +#define DWT_CTRL_CYCTAP_Msk (0x1UL << DWT_CTRL_CYCTAP_Pos) /*!< DWT CTRL: CYCTAP Mask */ + +#define DWT_CTRL_POSTINIT_Pos 5U /*!< DWT CTRL: POSTINIT Position */ +#define DWT_CTRL_POSTINIT_Msk (0xFUL << DWT_CTRL_POSTINIT_Pos) /*!< DWT CTRL: POSTINIT Mask */ + +#define DWT_CTRL_POSTPRESET_Pos 1U /*!< DWT CTRL: POSTPRESET Position */ +#define DWT_CTRL_POSTPRESET_Msk (0xFUL << DWT_CTRL_POSTPRESET_Pos) /*!< DWT CTRL: POSTPRESET Mask */ + +#define DWT_CTRL_CYCCNTENA_Pos 0U /*!< DWT CTRL: CYCCNTENA Position */ +#define DWT_CTRL_CYCCNTENA_Msk (0x1UL /*<< DWT_CTRL_CYCCNTENA_Pos*/) /*!< DWT CTRL: CYCCNTENA Mask */ + +/* DWT CPI Count Register Definitions */ +#define DWT_CPICNT_CPICNT_Pos 0U /*!< DWT CPICNT: CPICNT Position */ +#define DWT_CPICNT_CPICNT_Msk (0xFFUL /*<< DWT_CPICNT_CPICNT_Pos*/) /*!< DWT CPICNT: CPICNT Mask */ + +/* DWT Exception Overhead Count Register Definitions */ +#define DWT_EXCCNT_EXCCNT_Pos 0U /*!< DWT EXCCNT: EXCCNT Position */ +#define DWT_EXCCNT_EXCCNT_Msk (0xFFUL /*<< DWT_EXCCNT_EXCCNT_Pos*/) /*!< DWT EXCCNT: EXCCNT Mask */ + +/* DWT Sleep Count Register Definitions */ +#define DWT_SLEEPCNT_SLEEPCNT_Pos 0U /*!< DWT SLEEPCNT: SLEEPCNT Position */ +#define DWT_SLEEPCNT_SLEEPCNT_Msk (0xFFUL /*<< DWT_SLEEPCNT_SLEEPCNT_Pos*/) /*!< DWT SLEEPCNT: SLEEPCNT Mask */ + +/* DWT LSU Count Register Definitions */ +#define DWT_LSUCNT_LSUCNT_Pos 0U /*!< DWT LSUCNT: LSUCNT Position */ +#define DWT_LSUCNT_LSUCNT_Msk (0xFFUL /*<< DWT_LSUCNT_LSUCNT_Pos*/) /*!< DWT LSUCNT: LSUCNT Mask */ + +/* DWT Folded-instruction Count Register Definitions */ +#define DWT_FOLDCNT_FOLDCNT_Pos 0U /*!< DWT FOLDCNT: FOLDCNT Position */ +#define DWT_FOLDCNT_FOLDCNT_Msk (0xFFUL /*<< DWT_FOLDCNT_FOLDCNT_Pos*/) /*!< DWT FOLDCNT: FOLDCNT Mask */ + +/* DWT Comparator Mask Register Definitions */ +#define DWT_MASK_MASK_Pos 0U /*!< DWT MASK: MASK Position */ +#define DWT_MASK_MASK_Msk (0x1FUL /*<< DWT_MASK_MASK_Pos*/) /*!< DWT MASK: MASK Mask */ + +/* DWT Comparator Function Register Definitions */ +#define DWT_FUNCTION_MATCHED_Pos 24U /*!< DWT FUNCTION: MATCHED Position */ +#define DWT_FUNCTION_MATCHED_Msk (0x1UL << DWT_FUNCTION_MATCHED_Pos) /*!< DWT FUNCTION: MATCHED Mask */ + +#define DWT_FUNCTION_DATAVADDR1_Pos 16U /*!< DWT FUNCTION: DATAVADDR1 Position */ +#define DWT_FUNCTION_DATAVADDR1_Msk (0xFUL << DWT_FUNCTION_DATAVADDR1_Pos) /*!< DWT FUNCTION: DATAVADDR1 Mask */ + +#define DWT_FUNCTION_DATAVADDR0_Pos 12U /*!< DWT FUNCTION: DATAVADDR0 Position */ +#define DWT_FUNCTION_DATAVADDR0_Msk (0xFUL << DWT_FUNCTION_DATAVADDR0_Pos) /*!< DWT FUNCTION: DATAVADDR0 Mask */ + +#define DWT_FUNCTION_DATAVSIZE_Pos 10U /*!< DWT FUNCTION: DATAVSIZE Position */ +#define DWT_FUNCTION_DATAVSIZE_Msk (0x3UL << DWT_FUNCTION_DATAVSIZE_Pos) /*!< DWT FUNCTION: DATAVSIZE Mask */ + +#define DWT_FUNCTION_LNK1ENA_Pos 9U /*!< DWT FUNCTION: LNK1ENA Position */ +#define DWT_FUNCTION_LNK1ENA_Msk (0x1UL << DWT_FUNCTION_LNK1ENA_Pos) /*!< DWT FUNCTION: LNK1ENA Mask */ + +#define DWT_FUNCTION_DATAVMATCH_Pos 8U /*!< DWT FUNCTION: DATAVMATCH Position */ +#define DWT_FUNCTION_DATAVMATCH_Msk (0x1UL << DWT_FUNCTION_DATAVMATCH_Pos) /*!< DWT FUNCTION: DATAVMATCH Mask */ + +#define DWT_FUNCTION_CYCMATCH_Pos 7U /*!< DWT FUNCTION: CYCMATCH Position */ +#define DWT_FUNCTION_CYCMATCH_Msk (0x1UL << DWT_FUNCTION_CYCMATCH_Pos) /*!< DWT FUNCTION: CYCMATCH Mask */ + +#define DWT_FUNCTION_EMITRANGE_Pos 5U /*!< DWT FUNCTION: EMITRANGE Position */ +#define DWT_FUNCTION_EMITRANGE_Msk (0x1UL << DWT_FUNCTION_EMITRANGE_Pos) /*!< DWT FUNCTION: EMITRANGE Mask */ + +#define DWT_FUNCTION_FUNCTION_Pos 0U /*!< DWT FUNCTION: FUNCTION Position */ +#define DWT_FUNCTION_FUNCTION_Msk (0xFUL /*<< DWT_FUNCTION_FUNCTION_Pos*/) /*!< DWT FUNCTION: FUNCTION Mask */ + +/*@}*/ /* end of group CMSIS_DWT */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_TPI Trace Port Interface (TPI) + \brief Type definitions for the Trace Port Interface (TPI) + @{ + */ + +/** + \brief Structure type to access the Trace Port Interface Register (TPI). + */ +typedef struct +{ + __IM uint32_t SSPSR; /*!< Offset: 0x000 (R/ ) Supported Parallel Port Size Register */ + __IOM uint32_t CSPSR; /*!< Offset: 0x004 (R/W) Current Parallel Port Size Register */ + uint32_t RESERVED0[2U]; + __IOM uint32_t ACPR; /*!< Offset: 0x010 (R/W) Asynchronous Clock Prescaler Register */ + uint32_t RESERVED1[55U]; + __IOM uint32_t SPPR; /*!< Offset: 0x0F0 (R/W) Selected Pin Protocol Register */ + uint32_t RESERVED2[131U]; + __IM uint32_t FFSR; /*!< Offset: 0x300 (R/ ) Formatter and Flush Status Register */ + __IOM uint32_t FFCR; /*!< Offset: 0x304 (R/W) Formatter and Flush Control Register */ + __IM uint32_t FSCR; /*!< Offset: 0x308 (R/ ) Formatter Synchronization Counter Register */ + uint32_t RESERVED3[759U]; + __IM uint32_t TRIGGER; /*!< Offset: 0xEE8 (R/ ) TRIGGER Register */ + __IM uint32_t FIFO0; /*!< Offset: 0xEEC (R/ ) Integration ETM Data */ + __IM uint32_t ITATBCTR2; /*!< Offset: 0xEF0 (R/ ) ITATBCTR2 */ + uint32_t RESERVED4[1U]; + __IM uint32_t ITATBCTR0; /*!< Offset: 0xEF8 (R/ ) ITATBCTR0 */ + __IM uint32_t FIFO1; /*!< Offset: 0xEFC (R/ ) Integration ITM Data */ + __IOM uint32_t ITCTRL; /*!< Offset: 0xF00 (R/W) Integration Mode Control */ + uint32_t RESERVED5[39U]; + __IOM uint32_t CLAIMSET; /*!< Offset: 0xFA0 (R/W) Claim tag set */ + __IOM uint32_t CLAIMCLR; /*!< Offset: 0xFA4 (R/W) Claim tag clear */ + uint32_t RESERVED7[8U]; + __IM uint32_t DEVID; /*!< Offset: 0xFC8 (R/ ) TPIU_DEVID */ + __IM uint32_t DEVTYPE; /*!< Offset: 0xFCC (R/ ) TPIU_DEVTYPE */ +} TPI_Type; + +/* TPI Asynchronous Clock Prescaler Register Definitions */ +#define TPI_ACPR_PRESCALER_Pos 0U /*!< TPI ACPR: PRESCALER Position */ +#define TPI_ACPR_PRESCALER_Msk (0x1FFFUL /*<< TPI_ACPR_PRESCALER_Pos*/) /*!< TPI ACPR: PRESCALER Mask */ + +/* TPI Selected Pin Protocol Register Definitions */ +#define TPI_SPPR_TXMODE_Pos 0U /*!< TPI SPPR: TXMODE Position */ +#define TPI_SPPR_TXMODE_Msk (0x3UL /*<< TPI_SPPR_TXMODE_Pos*/) /*!< TPI SPPR: TXMODE Mask */ + +/* TPI Formatter and Flush Status Register Definitions */ +#define TPI_FFSR_FtNonStop_Pos 3U /*!< TPI FFSR: FtNonStop Position */ +#define TPI_FFSR_FtNonStop_Msk (0x1UL << TPI_FFSR_FtNonStop_Pos) /*!< TPI FFSR: FtNonStop Mask */ + +#define TPI_FFSR_TCPresent_Pos 2U /*!< TPI FFSR: TCPresent Position */ +#define TPI_FFSR_TCPresent_Msk (0x1UL << TPI_FFSR_TCPresent_Pos) /*!< TPI FFSR: TCPresent Mask */ + +#define TPI_FFSR_FtStopped_Pos 1U /*!< TPI FFSR: FtStopped Position */ +#define TPI_FFSR_FtStopped_Msk (0x1UL << TPI_FFSR_FtStopped_Pos) /*!< TPI FFSR: FtStopped Mask */ + +#define TPI_FFSR_FlInProg_Pos 0U /*!< TPI FFSR: FlInProg Position */ +#define TPI_FFSR_FlInProg_Msk (0x1UL /*<< TPI_FFSR_FlInProg_Pos*/) /*!< TPI FFSR: FlInProg Mask */ + +/* TPI Formatter and Flush Control Register Definitions */ +#define TPI_FFCR_TrigIn_Pos 8U /*!< TPI FFCR: TrigIn Position */ +#define TPI_FFCR_TrigIn_Msk (0x1UL << TPI_FFCR_TrigIn_Pos) /*!< TPI FFCR: TrigIn Mask */ + +#define TPI_FFCR_EnFCont_Pos 1U /*!< TPI FFCR: EnFCont Position */ +#define TPI_FFCR_EnFCont_Msk (0x1UL << TPI_FFCR_EnFCont_Pos) /*!< TPI FFCR: EnFCont Mask */ + +/* TPI TRIGGER Register Definitions */ +#define TPI_TRIGGER_TRIGGER_Pos 0U /*!< TPI TRIGGER: TRIGGER Position */ +#define TPI_TRIGGER_TRIGGER_Msk (0x1UL /*<< TPI_TRIGGER_TRIGGER_Pos*/) /*!< TPI TRIGGER: TRIGGER Mask */ + +/* TPI Integration ETM Data Register Definitions (FIFO0) */ +#define TPI_FIFO0_ITM_ATVALID_Pos 29U /*!< TPI FIFO0: ITM_ATVALID Position */ +#define TPI_FIFO0_ITM_ATVALID_Msk (0x1UL << TPI_FIFO0_ITM_ATVALID_Pos) /*!< TPI FIFO0: ITM_ATVALID Mask */ + +#define TPI_FIFO0_ITM_bytecount_Pos 27U /*!< TPI FIFO0: ITM_bytecount Position */ +#define TPI_FIFO0_ITM_bytecount_Msk (0x3UL << TPI_FIFO0_ITM_bytecount_Pos) /*!< TPI FIFO0: ITM_bytecount Mask */ + +#define TPI_FIFO0_ETM_ATVALID_Pos 26U /*!< TPI FIFO0: ETM_ATVALID Position */ +#define TPI_FIFO0_ETM_ATVALID_Msk (0x1UL << TPI_FIFO0_ETM_ATVALID_Pos) /*!< TPI FIFO0: ETM_ATVALID Mask */ + +#define TPI_FIFO0_ETM_bytecount_Pos 24U /*!< TPI FIFO0: ETM_bytecount Position */ +#define TPI_FIFO0_ETM_bytecount_Msk (0x3UL << TPI_FIFO0_ETM_bytecount_Pos) /*!< TPI FIFO0: ETM_bytecount Mask */ + +#define TPI_FIFO0_ETM2_Pos 16U /*!< TPI FIFO0: ETM2 Position */ +#define TPI_FIFO0_ETM2_Msk (0xFFUL << TPI_FIFO0_ETM2_Pos) /*!< TPI FIFO0: ETM2 Mask */ + +#define TPI_FIFO0_ETM1_Pos 8U /*!< TPI FIFO0: ETM1 Position */ +#define TPI_FIFO0_ETM1_Msk (0xFFUL << TPI_FIFO0_ETM1_Pos) /*!< TPI FIFO0: ETM1 Mask */ + +#define TPI_FIFO0_ETM0_Pos 0U /*!< TPI FIFO0: ETM0 Position */ +#define TPI_FIFO0_ETM0_Msk (0xFFUL /*<< TPI_FIFO0_ETM0_Pos*/) /*!< TPI FIFO0: ETM0 Mask */ + +/* TPI ITATBCTR2 Register Definitions */ +#define TPI_ITATBCTR2_ATREADY2_Pos 0U /*!< TPI ITATBCTR2: ATREADY2 Position */ +#define TPI_ITATBCTR2_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY2_Pos*/) /*!< TPI ITATBCTR2: ATREADY2 Mask */ + +#define TPI_ITATBCTR2_ATREADY1_Pos 0U /*!< TPI ITATBCTR2: ATREADY1 Position */ +#define TPI_ITATBCTR2_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR2_ATREADY1_Pos*/) /*!< TPI ITATBCTR2: ATREADY1 Mask */ + +/* TPI Integration ITM Data Register Definitions (FIFO1) */ +#define TPI_FIFO1_ITM_ATVALID_Pos 29U /*!< TPI FIFO1: ITM_ATVALID Position */ +#define TPI_FIFO1_ITM_ATVALID_Msk (0x1UL << TPI_FIFO1_ITM_ATVALID_Pos) /*!< TPI FIFO1: ITM_ATVALID Mask */ + +#define TPI_FIFO1_ITM_bytecount_Pos 27U /*!< TPI FIFO1: ITM_bytecount Position */ +#define TPI_FIFO1_ITM_bytecount_Msk (0x3UL << TPI_FIFO1_ITM_bytecount_Pos) /*!< TPI FIFO1: ITM_bytecount Mask */ + +#define TPI_FIFO1_ETM_ATVALID_Pos 26U /*!< TPI FIFO1: ETM_ATVALID Position */ +#define TPI_FIFO1_ETM_ATVALID_Msk (0x1UL << TPI_FIFO1_ETM_ATVALID_Pos) /*!< TPI FIFO1: ETM_ATVALID Mask */ + +#define TPI_FIFO1_ETM_bytecount_Pos 24U /*!< TPI FIFO1: ETM_bytecount Position */ +#define TPI_FIFO1_ETM_bytecount_Msk (0x3UL << TPI_FIFO1_ETM_bytecount_Pos) /*!< TPI FIFO1: ETM_bytecount Mask */ + +#define TPI_FIFO1_ITM2_Pos 16U /*!< TPI FIFO1: ITM2 Position */ +#define TPI_FIFO1_ITM2_Msk (0xFFUL << TPI_FIFO1_ITM2_Pos) /*!< TPI FIFO1: ITM2 Mask */ + +#define TPI_FIFO1_ITM1_Pos 8U /*!< TPI FIFO1: ITM1 Position */ +#define TPI_FIFO1_ITM1_Msk (0xFFUL << TPI_FIFO1_ITM1_Pos) /*!< TPI FIFO1: ITM1 Mask */ + +#define TPI_FIFO1_ITM0_Pos 0U /*!< TPI FIFO1: ITM0 Position */ +#define TPI_FIFO1_ITM0_Msk (0xFFUL /*<< TPI_FIFO1_ITM0_Pos*/) /*!< TPI FIFO1: ITM0 Mask */ + +/* TPI ITATBCTR0 Register Definitions */ +#define TPI_ITATBCTR0_ATREADY2_Pos 0U /*!< TPI ITATBCTR0: ATREADY2 Position */ +#define TPI_ITATBCTR0_ATREADY2_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY2_Pos*/) /*!< TPI ITATBCTR0: ATREADY2 Mask */ + +#define TPI_ITATBCTR0_ATREADY1_Pos 0U /*!< TPI ITATBCTR0: ATREADY1 Position */ +#define TPI_ITATBCTR0_ATREADY1_Msk (0x1UL /*<< TPI_ITATBCTR0_ATREADY1_Pos*/) /*!< TPI ITATBCTR0: ATREADY1 Mask */ + +/* TPI Integration Mode Control Register Definitions */ +#define TPI_ITCTRL_Mode_Pos 0U /*!< TPI ITCTRL: Mode Position */ +#define TPI_ITCTRL_Mode_Msk (0x3UL /*<< TPI_ITCTRL_Mode_Pos*/) /*!< TPI ITCTRL: Mode Mask */ + +/* TPI DEVID Register Definitions */ +#define TPI_DEVID_NRZVALID_Pos 11U /*!< TPI DEVID: NRZVALID Position */ +#define TPI_DEVID_NRZVALID_Msk (0x1UL << TPI_DEVID_NRZVALID_Pos) /*!< TPI DEVID: NRZVALID Mask */ + +#define TPI_DEVID_MANCVALID_Pos 10U /*!< TPI DEVID: MANCVALID Position */ +#define TPI_DEVID_MANCVALID_Msk (0x1UL << TPI_DEVID_MANCVALID_Pos) /*!< TPI DEVID: MANCVALID Mask */ + +#define TPI_DEVID_PTINVALID_Pos 9U /*!< TPI DEVID: PTINVALID Position */ +#define TPI_DEVID_PTINVALID_Msk (0x1UL << TPI_DEVID_PTINVALID_Pos) /*!< TPI DEVID: PTINVALID Mask */ + +#define TPI_DEVID_MinBufSz_Pos 6U /*!< TPI DEVID: MinBufSz Position */ +#define TPI_DEVID_MinBufSz_Msk (0x7UL << TPI_DEVID_MinBufSz_Pos) /*!< TPI DEVID: MinBufSz Mask */ + +#define TPI_DEVID_AsynClkIn_Pos 5U /*!< TPI DEVID: AsynClkIn Position */ +#define TPI_DEVID_AsynClkIn_Msk (0x1UL << TPI_DEVID_AsynClkIn_Pos) /*!< TPI DEVID: AsynClkIn Mask */ + +#define TPI_DEVID_NrTraceInput_Pos 0U /*!< TPI DEVID: NrTraceInput Position */ +#define TPI_DEVID_NrTraceInput_Msk (0x1FUL /*<< TPI_DEVID_NrTraceInput_Pos*/) /*!< TPI DEVID: NrTraceInput Mask */ + +/* TPI DEVTYPE Register Definitions */ +#define TPI_DEVTYPE_SubType_Pos 4U /*!< TPI DEVTYPE: SubType Position */ +#define TPI_DEVTYPE_SubType_Msk (0xFUL /*<< TPI_DEVTYPE_SubType_Pos*/) /*!< TPI DEVTYPE: SubType Mask */ + +#define TPI_DEVTYPE_MajorType_Pos 0U /*!< TPI DEVTYPE: MajorType Position */ +#define TPI_DEVTYPE_MajorType_Msk (0xFUL << TPI_DEVTYPE_MajorType_Pos) /*!< TPI DEVTYPE: MajorType Mask */ + +/*@}*/ /* end of group CMSIS_TPI */ + + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_MPU Memory Protection Unit (MPU) + \brief Type definitions for the Memory Protection Unit (MPU) + @{ + */ + +/** + \brief Structure type to access the Memory Protection Unit (MPU). + */ +typedef struct +{ + __IM uint32_t TYPE; /*!< Offset: 0x000 (R/ ) MPU Type Register */ + __IOM uint32_t CTRL; /*!< Offset: 0x004 (R/W) MPU Control Register */ + __IOM uint32_t RNR; /*!< Offset: 0x008 (R/W) MPU Region RNRber Register */ + __IOM uint32_t RBAR; /*!< Offset: 0x00C (R/W) MPU Region Base Address Register */ + __IOM uint32_t RASR; /*!< Offset: 0x010 (R/W) MPU Region Attribute and Size Register */ + __IOM uint32_t RBAR_A1; /*!< Offset: 0x014 (R/W) MPU Alias 1 Region Base Address Register */ + __IOM uint32_t RASR_A1; /*!< Offset: 0x018 (R/W) MPU Alias 1 Region Attribute and Size Register */ + __IOM uint32_t RBAR_A2; /*!< Offset: 0x01C (R/W) MPU Alias 2 Region Base Address Register */ + __IOM uint32_t RASR_A2; /*!< Offset: 0x020 (R/W) MPU Alias 2 Region Attribute and Size Register */ + __IOM uint32_t RBAR_A3; /*!< Offset: 0x024 (R/W) MPU Alias 3 Region Base Address Register */ + __IOM uint32_t RASR_A3; /*!< Offset: 0x028 (R/W) MPU Alias 3 Region Attribute and Size Register */ +} MPU_Type; + +#define MPU_TYPE_RALIASES 4U + +/* MPU Type Register Definitions */ +#define MPU_TYPE_IREGION_Pos 16U /*!< MPU TYPE: IREGION Position */ +#define MPU_TYPE_IREGION_Msk (0xFFUL << MPU_TYPE_IREGION_Pos) /*!< MPU TYPE: IREGION Mask */ + +#define MPU_TYPE_DREGION_Pos 8U /*!< MPU TYPE: DREGION Position */ +#define MPU_TYPE_DREGION_Msk (0xFFUL << MPU_TYPE_DREGION_Pos) /*!< MPU TYPE: DREGION Mask */ + +#define MPU_TYPE_SEPARATE_Pos 0U /*!< MPU TYPE: SEPARATE Position */ +#define MPU_TYPE_SEPARATE_Msk (1UL /*<< MPU_TYPE_SEPARATE_Pos*/) /*!< MPU TYPE: SEPARATE Mask */ + +/* MPU Control Register Definitions */ +#define MPU_CTRL_PRIVDEFENA_Pos 2U /*!< MPU CTRL: PRIVDEFENA Position */ +#define MPU_CTRL_PRIVDEFENA_Msk (1UL << MPU_CTRL_PRIVDEFENA_Pos) /*!< MPU CTRL: PRIVDEFENA Mask */ + +#define MPU_CTRL_HFNMIENA_Pos 1U /*!< MPU CTRL: HFNMIENA Position */ +#define MPU_CTRL_HFNMIENA_Msk (1UL << MPU_CTRL_HFNMIENA_Pos) /*!< MPU CTRL: HFNMIENA Mask */ + +#define MPU_CTRL_ENABLE_Pos 0U /*!< MPU CTRL: ENABLE Position */ +#define MPU_CTRL_ENABLE_Msk (1UL /*<< MPU_CTRL_ENABLE_Pos*/) /*!< MPU CTRL: ENABLE Mask */ + +/* MPU Region Number Register Definitions */ +#define MPU_RNR_REGION_Pos 0U /*!< MPU RNR: REGION Position */ +#define MPU_RNR_REGION_Msk (0xFFUL /*<< MPU_RNR_REGION_Pos*/) /*!< MPU RNR: REGION Mask */ + +/* MPU Region Base Address Register Definitions */ +#define MPU_RBAR_ADDR_Pos 5U /*!< MPU RBAR: ADDR Position */ +#define MPU_RBAR_ADDR_Msk (0x7FFFFFFUL << MPU_RBAR_ADDR_Pos) /*!< MPU RBAR: ADDR Mask */ + +#define MPU_RBAR_VALID_Pos 4U /*!< MPU RBAR: VALID Position */ +#define MPU_RBAR_VALID_Msk (1UL << MPU_RBAR_VALID_Pos) /*!< MPU RBAR: VALID Mask */ + +#define MPU_RBAR_REGION_Pos 0U /*!< MPU RBAR: REGION Position */ +#define MPU_RBAR_REGION_Msk (0xFUL /*<< MPU_RBAR_REGION_Pos*/) /*!< MPU RBAR: REGION Mask */ + +/* MPU Region Attribute and Size Register Definitions */ +#define MPU_RASR_ATTRS_Pos 16U /*!< MPU RASR: MPU Region Attribute field Position */ +#define MPU_RASR_ATTRS_Msk (0xFFFFUL << MPU_RASR_ATTRS_Pos) /*!< MPU RASR: MPU Region Attribute field Mask */ + +#define MPU_RASR_XN_Pos 28U /*!< MPU RASR: ATTRS.XN Position */ +#define MPU_RASR_XN_Msk (1UL << MPU_RASR_XN_Pos) /*!< MPU RASR: ATTRS.XN Mask */ + +#define MPU_RASR_AP_Pos 24U /*!< MPU RASR: ATTRS.AP Position */ +#define MPU_RASR_AP_Msk (0x7UL << MPU_RASR_AP_Pos) /*!< MPU RASR: ATTRS.AP Mask */ + +#define MPU_RASR_TEX_Pos 19U /*!< MPU RASR: ATTRS.TEX Position */ +#define MPU_RASR_TEX_Msk (0x7UL << MPU_RASR_TEX_Pos) /*!< MPU RASR: ATTRS.TEX Mask */ + +#define MPU_RASR_S_Pos 18U /*!< MPU RASR: ATTRS.S Position */ +#define MPU_RASR_S_Msk (1UL << MPU_RASR_S_Pos) /*!< MPU RASR: ATTRS.S Mask */ + +#define MPU_RASR_C_Pos 17U /*!< MPU RASR: ATTRS.C Position */ +#define MPU_RASR_C_Msk (1UL << MPU_RASR_C_Pos) /*!< MPU RASR: ATTRS.C Mask */ + +#define MPU_RASR_B_Pos 16U /*!< MPU RASR: ATTRS.B Position */ +#define MPU_RASR_B_Msk (1UL << MPU_RASR_B_Pos) /*!< MPU RASR: ATTRS.B Mask */ + +#define MPU_RASR_SRD_Pos 8U /*!< MPU RASR: Sub-Region Disable Position */ +#define MPU_RASR_SRD_Msk (0xFFUL << MPU_RASR_SRD_Pos) /*!< MPU RASR: Sub-Region Disable Mask */ + +#define MPU_RASR_SIZE_Pos 1U /*!< MPU RASR: Region Size Field Position */ +#define MPU_RASR_SIZE_Msk (0x1FUL << MPU_RASR_SIZE_Pos) /*!< MPU RASR: Region Size Field Mask */ + +#define MPU_RASR_ENABLE_Pos 0U /*!< MPU RASR: Region enable bit Position */ +#define MPU_RASR_ENABLE_Msk (1UL /*<< MPU_RASR_ENABLE_Pos*/) /*!< MPU RASR: Region enable bit Disable Mask */ + +/*@} end of group CMSIS_MPU */ +#endif /* defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_FPU Floating Point Unit (FPU) + \brief Type definitions for the Floating Point Unit (FPU) + @{ + */ + +/** + \brief Structure type to access the Floating Point Unit (FPU). + */ +typedef struct +{ + uint32_t RESERVED0[1U]; + __IOM uint32_t FPCCR; /*!< Offset: 0x004 (R/W) Floating-Point Context Control Register */ + __IOM uint32_t FPCAR; /*!< Offset: 0x008 (R/W) Floating-Point Context Address Register */ + __IOM uint32_t FPDSCR; /*!< Offset: 0x00C (R/W) Floating-Point Default Status Control Register */ + __IM uint32_t MVFR0; /*!< Offset: 0x010 (R/ ) Media and FP Feature Register 0 */ + __IM uint32_t MVFR1; /*!< Offset: 0x014 (R/ ) Media and FP Feature Register 1 */ + __IM uint32_t MVFR2; /*!< Offset: 0x018 (R/ ) Media and FP Feature Register 2 */ +} FPU_Type; + +/* Floating-Point Context Control Register Definitions */ +#define FPU_FPCCR_ASPEN_Pos 31U /*!< FPCCR: ASPEN bit Position */ +#define FPU_FPCCR_ASPEN_Msk (1UL << FPU_FPCCR_ASPEN_Pos) /*!< FPCCR: ASPEN bit Mask */ + +#define FPU_FPCCR_LSPEN_Pos 30U /*!< FPCCR: LSPEN Position */ +#define FPU_FPCCR_LSPEN_Msk (1UL << FPU_FPCCR_LSPEN_Pos) /*!< FPCCR: LSPEN bit Mask */ + +#define FPU_FPCCR_MONRDY_Pos 8U /*!< FPCCR: MONRDY Position */ +#define FPU_FPCCR_MONRDY_Msk (1UL << FPU_FPCCR_MONRDY_Pos) /*!< FPCCR: MONRDY bit Mask */ + +#define FPU_FPCCR_BFRDY_Pos 6U /*!< FPCCR: BFRDY Position */ +#define FPU_FPCCR_BFRDY_Msk (1UL << FPU_FPCCR_BFRDY_Pos) /*!< FPCCR: BFRDY bit Mask */ + +#define FPU_FPCCR_MMRDY_Pos 5U /*!< FPCCR: MMRDY Position */ +#define FPU_FPCCR_MMRDY_Msk (1UL << FPU_FPCCR_MMRDY_Pos) /*!< FPCCR: MMRDY bit Mask */ + +#define FPU_FPCCR_HFRDY_Pos 4U /*!< FPCCR: HFRDY Position */ +#define FPU_FPCCR_HFRDY_Msk (1UL << FPU_FPCCR_HFRDY_Pos) /*!< FPCCR: HFRDY bit Mask */ + +#define FPU_FPCCR_THREAD_Pos 3U /*!< FPCCR: processor mode bit Position */ +#define FPU_FPCCR_THREAD_Msk (1UL << FPU_FPCCR_THREAD_Pos) /*!< FPCCR: processor mode active bit Mask */ + +#define FPU_FPCCR_USER_Pos 1U /*!< FPCCR: privilege level bit Position */ +#define FPU_FPCCR_USER_Msk (1UL << FPU_FPCCR_USER_Pos) /*!< FPCCR: privilege level bit Mask */ + +#define FPU_FPCCR_LSPACT_Pos 0U /*!< FPCCR: Lazy state preservation active bit Position */ +#define FPU_FPCCR_LSPACT_Msk (1UL /*<< FPU_FPCCR_LSPACT_Pos*/) /*!< FPCCR: Lazy state preservation active bit Mask */ + +/* Floating-Point Context Address Register Definitions */ +#define FPU_FPCAR_ADDRESS_Pos 3U /*!< FPCAR: ADDRESS bit Position */ +#define FPU_FPCAR_ADDRESS_Msk (0x1FFFFFFFUL << FPU_FPCAR_ADDRESS_Pos) /*!< FPCAR: ADDRESS bit Mask */ + +/* Floating-Point Default Status Control Register Definitions */ +#define FPU_FPDSCR_AHP_Pos 26U /*!< FPDSCR: AHP bit Position */ +#define FPU_FPDSCR_AHP_Msk (1UL << FPU_FPDSCR_AHP_Pos) /*!< FPDSCR: AHP bit Mask */ + +#define FPU_FPDSCR_DN_Pos 25U /*!< FPDSCR: DN bit Position */ +#define FPU_FPDSCR_DN_Msk (1UL << FPU_FPDSCR_DN_Pos) /*!< FPDSCR: DN bit Mask */ + +#define FPU_FPDSCR_FZ_Pos 24U /*!< FPDSCR: FZ bit Position */ +#define FPU_FPDSCR_FZ_Msk (1UL << FPU_FPDSCR_FZ_Pos) /*!< FPDSCR: FZ bit Mask */ + +#define FPU_FPDSCR_RMode_Pos 22U /*!< FPDSCR: RMode bit Position */ +#define FPU_FPDSCR_RMode_Msk (3UL << FPU_FPDSCR_RMode_Pos) /*!< FPDSCR: RMode bit Mask */ + +/* Media and FP Feature Register 0 Definitions */ +#define FPU_MVFR0_FP_rounding_modes_Pos 28U /*!< MVFR0: FP rounding modes bits Position */ +#define FPU_MVFR0_FP_rounding_modes_Msk (0xFUL << FPU_MVFR0_FP_rounding_modes_Pos) /*!< MVFR0: FP rounding modes bits Mask */ + +#define FPU_MVFR0_Short_vectors_Pos 24U /*!< MVFR0: Short vectors bits Position */ +#define FPU_MVFR0_Short_vectors_Msk (0xFUL << FPU_MVFR0_Short_vectors_Pos) /*!< MVFR0: Short vectors bits Mask */ + +#define FPU_MVFR0_Square_root_Pos 20U /*!< MVFR0: Square root bits Position */ +#define FPU_MVFR0_Square_root_Msk (0xFUL << FPU_MVFR0_Square_root_Pos) /*!< MVFR0: Square root bits Mask */ + +#define FPU_MVFR0_Divide_Pos 16U /*!< MVFR0: Divide bits Position */ +#define FPU_MVFR0_Divide_Msk (0xFUL << FPU_MVFR0_Divide_Pos) /*!< MVFR0: Divide bits Mask */ + +#define FPU_MVFR0_FP_excep_trapping_Pos 12U /*!< MVFR0: FP exception trapping bits Position */ +#define FPU_MVFR0_FP_excep_trapping_Msk (0xFUL << FPU_MVFR0_FP_excep_trapping_Pos) /*!< MVFR0: FP exception trapping bits Mask */ + +#define FPU_MVFR0_Double_precision_Pos 8U /*!< MVFR0: Double-precision bits Position */ +#define FPU_MVFR0_Double_precision_Msk (0xFUL << FPU_MVFR0_Double_precision_Pos) /*!< MVFR0: Double-precision bits Mask */ + +#define FPU_MVFR0_Single_precision_Pos 4U /*!< MVFR0: Single-precision bits Position */ +#define FPU_MVFR0_Single_precision_Msk (0xFUL << FPU_MVFR0_Single_precision_Pos) /*!< MVFR0: Single-precision bits Mask */ + +#define FPU_MVFR0_A_SIMD_registers_Pos 0U /*!< MVFR0: A_SIMD registers bits Position */ +#define FPU_MVFR0_A_SIMD_registers_Msk (0xFUL /*<< FPU_MVFR0_A_SIMD_registers_Pos*/) /*!< MVFR0: A_SIMD registers bits Mask */ + +/* Media and FP Feature Register 1 Definitions */ +#define FPU_MVFR1_FP_fused_MAC_Pos 28U /*!< MVFR1: FP fused MAC bits Position */ +#define FPU_MVFR1_FP_fused_MAC_Msk (0xFUL << FPU_MVFR1_FP_fused_MAC_Pos) /*!< MVFR1: FP fused MAC bits Mask */ + +#define FPU_MVFR1_FP_HPFP_Pos 24U /*!< MVFR1: FP HPFP bits Position */ +#define FPU_MVFR1_FP_HPFP_Msk (0xFUL << FPU_MVFR1_FP_HPFP_Pos) /*!< MVFR1: FP HPFP bits Mask */ + +#define FPU_MVFR1_D_NaN_mode_Pos 4U /*!< MVFR1: D_NaN mode bits Position */ +#define FPU_MVFR1_D_NaN_mode_Msk (0xFUL << FPU_MVFR1_D_NaN_mode_Pos) /*!< MVFR1: D_NaN mode bits Mask */ + +#define FPU_MVFR1_FtZ_mode_Pos 0U /*!< MVFR1: FtZ mode bits Position */ +#define FPU_MVFR1_FtZ_mode_Msk (0xFUL /*<< FPU_MVFR1_FtZ_mode_Pos*/) /*!< MVFR1: FtZ mode bits Mask */ + +/* Media and FP Feature Register 2 Definitions */ + +#define FPU_MVFR2_VFP_Misc_Pos 4U /*!< MVFR2: VFP Misc bits Position */ +#define FPU_MVFR2_VFP_Misc_Msk (0xFUL << FPU_MVFR2_VFP_Misc_Pos) /*!< MVFR2: VFP Misc bits Mask */ + +/*@} end of group CMSIS_FPU */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_CoreDebug Core Debug Registers (CoreDebug) + \brief Type definitions for the Core Debug Registers + @{ + */ + +/** + \brief Structure type to access the Core Debug Register (CoreDebug). + */ +typedef struct +{ + __IOM uint32_t DHCSR; /*!< Offset: 0x000 (R/W) Debug Halting Control and Status Register */ + __OM uint32_t DCRSR; /*!< Offset: 0x004 ( /W) Debug Core Register Selector Register */ + __IOM uint32_t DCRDR; /*!< Offset: 0x008 (R/W) Debug Core Register Data Register */ + __IOM uint32_t DEMCR; /*!< Offset: 0x00C (R/W) Debug Exception and Monitor Control Register */ +} CoreDebug_Type; + +/* Debug Halting Control and Status Register Definitions */ +#define CoreDebug_DHCSR_DBGKEY_Pos 16U /*!< CoreDebug DHCSR: DBGKEY Position */ +#define CoreDebug_DHCSR_DBGKEY_Msk (0xFFFFUL << CoreDebug_DHCSR_DBGKEY_Pos) /*!< CoreDebug DHCSR: DBGKEY Mask */ + +#define CoreDebug_DHCSR_S_RESET_ST_Pos 25U /*!< CoreDebug DHCSR: S_RESET_ST Position */ +#define CoreDebug_DHCSR_S_RESET_ST_Msk (1UL << CoreDebug_DHCSR_S_RESET_ST_Pos) /*!< CoreDebug DHCSR: S_RESET_ST Mask */ + +#define CoreDebug_DHCSR_S_RETIRE_ST_Pos 24U /*!< CoreDebug DHCSR: S_RETIRE_ST Position */ +#define CoreDebug_DHCSR_S_RETIRE_ST_Msk (1UL << CoreDebug_DHCSR_S_RETIRE_ST_Pos) /*!< CoreDebug DHCSR: S_RETIRE_ST Mask */ + +#define CoreDebug_DHCSR_S_LOCKUP_Pos 19U /*!< CoreDebug DHCSR: S_LOCKUP Position */ +#define CoreDebug_DHCSR_S_LOCKUP_Msk (1UL << CoreDebug_DHCSR_S_LOCKUP_Pos) /*!< CoreDebug DHCSR: S_LOCKUP Mask */ + +#define CoreDebug_DHCSR_S_SLEEP_Pos 18U /*!< CoreDebug DHCSR: S_SLEEP Position */ +#define CoreDebug_DHCSR_S_SLEEP_Msk (1UL << CoreDebug_DHCSR_S_SLEEP_Pos) /*!< CoreDebug DHCSR: S_SLEEP Mask */ + +#define CoreDebug_DHCSR_S_HALT_Pos 17U /*!< CoreDebug DHCSR: S_HALT Position */ +#define CoreDebug_DHCSR_S_HALT_Msk (1UL << CoreDebug_DHCSR_S_HALT_Pos) /*!< CoreDebug DHCSR: S_HALT Mask */ + +#define CoreDebug_DHCSR_S_REGRDY_Pos 16U /*!< CoreDebug DHCSR: S_REGRDY Position */ +#define CoreDebug_DHCSR_S_REGRDY_Msk (1UL << CoreDebug_DHCSR_S_REGRDY_Pos) /*!< CoreDebug DHCSR: S_REGRDY Mask */ + +#define CoreDebug_DHCSR_C_SNAPSTALL_Pos 5U /*!< CoreDebug DHCSR: C_SNAPSTALL Position */ +#define CoreDebug_DHCSR_C_SNAPSTALL_Msk (1UL << CoreDebug_DHCSR_C_SNAPSTALL_Pos) /*!< CoreDebug DHCSR: C_SNAPSTALL Mask */ + +#define CoreDebug_DHCSR_C_MASKINTS_Pos 3U /*!< CoreDebug DHCSR: C_MASKINTS Position */ +#define CoreDebug_DHCSR_C_MASKINTS_Msk (1UL << CoreDebug_DHCSR_C_MASKINTS_Pos) /*!< CoreDebug DHCSR: C_MASKINTS Mask */ + +#define CoreDebug_DHCSR_C_STEP_Pos 2U /*!< CoreDebug DHCSR: C_STEP Position */ +#define CoreDebug_DHCSR_C_STEP_Msk (1UL << CoreDebug_DHCSR_C_STEP_Pos) /*!< CoreDebug DHCSR: C_STEP Mask */ + +#define CoreDebug_DHCSR_C_HALT_Pos 1U /*!< CoreDebug DHCSR: C_HALT Position */ +#define CoreDebug_DHCSR_C_HALT_Msk (1UL << CoreDebug_DHCSR_C_HALT_Pos) /*!< CoreDebug DHCSR: C_HALT Mask */ + +#define CoreDebug_DHCSR_C_DEBUGEN_Pos 0U /*!< CoreDebug DHCSR: C_DEBUGEN Position */ +#define CoreDebug_DHCSR_C_DEBUGEN_Msk (1UL /*<< CoreDebug_DHCSR_C_DEBUGEN_Pos*/) /*!< CoreDebug DHCSR: C_DEBUGEN Mask */ + +/* Debug Core Register Selector Register Definitions */ +#define CoreDebug_DCRSR_REGWnR_Pos 16U /*!< CoreDebug DCRSR: REGWnR Position */ +#define CoreDebug_DCRSR_REGWnR_Msk (1UL << CoreDebug_DCRSR_REGWnR_Pos) /*!< CoreDebug DCRSR: REGWnR Mask */ + +#define CoreDebug_DCRSR_REGSEL_Pos 0U /*!< CoreDebug DCRSR: REGSEL Position */ +#define CoreDebug_DCRSR_REGSEL_Msk (0x1FUL /*<< CoreDebug_DCRSR_REGSEL_Pos*/) /*!< CoreDebug DCRSR: REGSEL Mask */ + +/* Debug Exception and Monitor Control Register Definitions */ +#define CoreDebug_DEMCR_TRCENA_Pos 24U /*!< CoreDebug DEMCR: TRCENA Position */ +#define CoreDebug_DEMCR_TRCENA_Msk (1UL << CoreDebug_DEMCR_TRCENA_Pos) /*!< CoreDebug DEMCR: TRCENA Mask */ + +#define CoreDebug_DEMCR_MON_REQ_Pos 19U /*!< CoreDebug DEMCR: MON_REQ Position */ +#define CoreDebug_DEMCR_MON_REQ_Msk (1UL << CoreDebug_DEMCR_MON_REQ_Pos) /*!< CoreDebug DEMCR: MON_REQ Mask */ + +#define CoreDebug_DEMCR_MON_STEP_Pos 18U /*!< CoreDebug DEMCR: MON_STEP Position */ +#define CoreDebug_DEMCR_MON_STEP_Msk (1UL << CoreDebug_DEMCR_MON_STEP_Pos) /*!< CoreDebug DEMCR: MON_STEP Mask */ + +#define CoreDebug_DEMCR_MON_PEND_Pos 17U /*!< CoreDebug DEMCR: MON_PEND Position */ +#define CoreDebug_DEMCR_MON_PEND_Msk (1UL << CoreDebug_DEMCR_MON_PEND_Pos) /*!< CoreDebug DEMCR: MON_PEND Mask */ + +#define CoreDebug_DEMCR_MON_EN_Pos 16U /*!< CoreDebug DEMCR: MON_EN Position */ +#define CoreDebug_DEMCR_MON_EN_Msk (1UL << CoreDebug_DEMCR_MON_EN_Pos) /*!< CoreDebug DEMCR: MON_EN Mask */ + +#define CoreDebug_DEMCR_VC_HARDERR_Pos 10U /*!< CoreDebug DEMCR: VC_HARDERR Position */ +#define CoreDebug_DEMCR_VC_HARDERR_Msk (1UL << CoreDebug_DEMCR_VC_HARDERR_Pos) /*!< CoreDebug DEMCR: VC_HARDERR Mask */ + +#define CoreDebug_DEMCR_VC_INTERR_Pos 9U /*!< CoreDebug DEMCR: VC_INTERR Position */ +#define CoreDebug_DEMCR_VC_INTERR_Msk (1UL << CoreDebug_DEMCR_VC_INTERR_Pos) /*!< CoreDebug DEMCR: VC_INTERR Mask */ + +#define CoreDebug_DEMCR_VC_BUSERR_Pos 8U /*!< CoreDebug DEMCR: VC_BUSERR Position */ +#define CoreDebug_DEMCR_VC_BUSERR_Msk (1UL << CoreDebug_DEMCR_VC_BUSERR_Pos) /*!< CoreDebug DEMCR: VC_BUSERR Mask */ + +#define CoreDebug_DEMCR_VC_STATERR_Pos 7U /*!< CoreDebug DEMCR: VC_STATERR Position */ +#define CoreDebug_DEMCR_VC_STATERR_Msk (1UL << CoreDebug_DEMCR_VC_STATERR_Pos) /*!< CoreDebug DEMCR: VC_STATERR Mask */ + +#define CoreDebug_DEMCR_VC_CHKERR_Pos 6U /*!< CoreDebug DEMCR: VC_CHKERR Position */ +#define CoreDebug_DEMCR_VC_CHKERR_Msk (1UL << CoreDebug_DEMCR_VC_CHKERR_Pos) /*!< CoreDebug DEMCR: VC_CHKERR Mask */ + +#define CoreDebug_DEMCR_VC_NOCPERR_Pos 5U /*!< CoreDebug DEMCR: VC_NOCPERR Position */ +#define CoreDebug_DEMCR_VC_NOCPERR_Msk (1UL << CoreDebug_DEMCR_VC_NOCPERR_Pos) /*!< CoreDebug DEMCR: VC_NOCPERR Mask */ + +#define CoreDebug_DEMCR_VC_MMERR_Pos 4U /*!< CoreDebug DEMCR: VC_MMERR Position */ +#define CoreDebug_DEMCR_VC_MMERR_Msk (1UL << CoreDebug_DEMCR_VC_MMERR_Pos) /*!< CoreDebug DEMCR: VC_MMERR Mask */ + +#define CoreDebug_DEMCR_VC_CORERESET_Pos 0U /*!< CoreDebug DEMCR: VC_CORERESET Position */ +#define CoreDebug_DEMCR_VC_CORERESET_Msk (1UL /*<< CoreDebug_DEMCR_VC_CORERESET_Pos*/) /*!< CoreDebug DEMCR: VC_CORERESET Mask */ + +/*@} end of group CMSIS_CoreDebug */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_bitfield Core register bit field macros + \brief Macros for use with bit field definitions (xxx_Pos, xxx_Msk). + @{ + */ + +/** + \brief Mask and shift a bit field value for use in a register bit range. + \param[in] field Name of the register bit field. + \param[in] value Value of the bit field. This parameter is interpreted as an uint32_t type. + \return Masked and shifted value. +*/ +#define _VAL2FLD(field, value) (((uint32_t)(value) << field ## _Pos) & field ## _Msk) + +/** + \brief Mask and shift a register value to extract a bit filed value. + \param[in] field Name of the register bit field. + \param[in] value Value of register. This parameter is interpreted as an uint32_t type. + \return Masked and shifted bit field value. +*/ +#define _FLD2VAL(field, value) (((uint32_t)(value) & field ## _Msk) >> field ## _Pos) + +/*@} end of group CMSIS_core_bitfield */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_core_base Core Definitions + \brief Definitions for base addresses, unions, and structures. + @{ + */ + +/* Memory mapping of Core Hardware */ +#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */ +#define ITM_BASE (0xE0000000UL) /*!< ITM Base Address */ +#define DWT_BASE (0xE0001000UL) /*!< DWT Base Address */ +#define TPI_BASE (0xE0040000UL) /*!< TPI Base Address */ +#define CoreDebug_BASE (0xE000EDF0UL) /*!< Core Debug Base Address */ +#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */ +#define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */ +#define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */ + +#define SCnSCB ((SCnSCB_Type *) SCS_BASE ) /*!< System control Register not in SCB */ +#define SCB ((SCB_Type *) SCB_BASE ) /*!< SCB configuration struct */ +#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */ +#define NVIC ((NVIC_Type *) NVIC_BASE ) /*!< NVIC configuration struct */ +#define ITM ((ITM_Type *) ITM_BASE ) /*!< ITM configuration struct */ +#define DWT ((DWT_Type *) DWT_BASE ) /*!< DWT configuration struct */ +#define TPI ((TPI_Type *) TPI_BASE ) /*!< TPI configuration struct */ +#define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + #define MPU_BASE (SCS_BASE + 0x0D90UL) /*!< Memory Protection Unit */ + #define MPU ((MPU_Type *) MPU_BASE ) /*!< Memory Protection Unit */ +#endif + +#define FPU_BASE (SCS_BASE + 0x0F30UL) /*!< Floating Point Unit */ +#define FPU ((FPU_Type *) FPU_BASE ) /*!< Floating Point Unit */ + +/*@} */ + + +/** + \ingroup CMSIS_core_register + \defgroup CMSIS_register_aliases Backwards Compatibility Aliases + \brief Register alias definitions for backwards compatibility. + @{ + */ + +/* Capitalize ITM_TCR Register Definitions */ + +/* ITM Trace Control Register Definitions */ +#define ITM_TCR_TraceBusID_Pos (ITM_TCR_TRACEBUSID_Pos) /*!< \deprecated ITM_TCR_TraceBusID_Pos */ +#define ITM_TCR_TraceBusID_Msk (ITM_TCR_TRACEBUSID_Msk) /*!< \deprecated ITM_TCR_TraceBusID_Msk */ + +#define ITM_TCR_TSPrescale_Pos (ITM_TCR_TSPRESCALE_Pos) /*!< \deprecated ITM_TCR_TSPrescale_Pos */ +#define ITM_TCR_TSPrescale_Msk (ITM_TCR_TSPRESCALE_Msk) /*!< \deprecated ITM_TCR_TSPrescale_Msk */ + +/* ITM Lock Status Register Definitions */ +#define ITM_LSR_ByteAcc_Pos (ITM_LSR_BYTEACC_Pos) /*!< \deprecated ITM_LSR_ByteAcc_Pos */ +#define ITM_LSR_ByteAcc_Msk (ITM_LSR_BYTEACC_Msk) /*!< \deprecated ITM_LSR_ByteAcc_Msk */ + +#define ITM_LSR_Access_Pos (ITM_LSR_ACCESS_Pos) /*!< \deprecated ITM_LSR_Access_Pos */ +#define ITM_LSR_Access_Msk (ITM_LSR_ACCESS_Msk) /*!< \deprecated ITM_LSR_Access_Msk */ + +#define ITM_LSR_Present_Pos (ITM_LSR_PRESENT_Pos) /*!< \deprecated ITM_LSR_Present_Pos */ +#define ITM_LSR_Present_Msk (ITM_LSR_PRESENT_Msk) /*!< \deprecated ITM_LSR_Present_Msk */ + +/*@} */ + + + +/******************************************************************************* + * Hardware Abstraction Layer + Core Function Interface contains: + - Core NVIC Functions + - Core SysTick Functions + - Core Debug Functions + - Core Register Access Functions + ******************************************************************************/ +/** + \defgroup CMSIS_Core_FunctionInterface Functions and Instructions Reference +*/ + + + +/* ########################## NVIC functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_NVICFunctions NVIC Functions + \brief Functions that manage interrupts and exceptions via the NVIC. + @{ + */ + +#ifdef CMSIS_NVIC_VIRTUAL + #ifndef CMSIS_NVIC_VIRTUAL_HEADER_FILE + #define CMSIS_NVIC_VIRTUAL_HEADER_FILE "cmsis_nvic_virtual.h" + #endif + #include CMSIS_NVIC_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping + #define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping + #define NVIC_EnableIRQ __NVIC_EnableIRQ + #define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ + #define NVIC_DisableIRQ __NVIC_DisableIRQ + #define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ + #define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ + #define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ + #define NVIC_GetActive __NVIC_GetActive + #define NVIC_SetPriority __NVIC_SetPriority + #define NVIC_GetPriority __NVIC_GetPriority + #define NVIC_SystemReset __NVIC_SystemReset +#endif /* CMSIS_NVIC_VIRTUAL */ + +#ifdef CMSIS_VECTAB_VIRTUAL + #ifndef CMSIS_VECTAB_VIRTUAL_HEADER_FILE + #define CMSIS_VECTAB_VIRTUAL_HEADER_FILE "cmsis_vectab_virtual.h" + #endif + #include CMSIS_VECTAB_VIRTUAL_HEADER_FILE +#else + #define NVIC_SetVector __NVIC_SetVector + #define NVIC_GetVector __NVIC_GetVector +#endif /* (CMSIS_VECTAB_VIRTUAL) */ + +#define NVIC_USER_IRQ_OFFSET 16 + + +/* The following EXC_RETURN values are saved the LR on exception entry */ +#define EXC_RETURN_HANDLER (0xFFFFFFF1UL) /* return to Handler mode, uses MSP after return */ +#define EXC_RETURN_THREAD_MSP (0xFFFFFFF9UL) /* return to Thread mode, uses MSP after return */ +#define EXC_RETURN_THREAD_PSP (0xFFFFFFFDUL) /* return to Thread mode, uses PSP after return */ +#define EXC_RETURN_HANDLER_FPU (0xFFFFFFE1UL) /* return to Handler mode, uses MSP after return, restore floating-point state */ +#define EXC_RETURN_THREAD_MSP_FPU (0xFFFFFFE9UL) /* return to Thread mode, uses MSP after return, restore floating-point state */ +#define EXC_RETURN_THREAD_PSP_FPU (0xFFFFFFEDUL) /* return to Thread mode, uses PSP after return, restore floating-point state */ + + +/** + \brief Set Priority Grouping + \details Sets the priority grouping field using the required unlock sequence. + The parameter PriorityGroup is assigned to the field SCB->AIRCR [10:8] PRIGROUP field. + Only values from 0..7 are used. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Priority grouping field. + */ +__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup) +{ + uint32_t reg_value; + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + + reg_value = SCB->AIRCR; /* read old register configuration */ + reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */ + reg_value = (reg_value | + ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos) ); /* Insert write key and priority group */ + SCB->AIRCR = reg_value; +} + + +/** + \brief Get Priority Grouping + \details Reads the priority grouping field from the NVIC Interrupt Controller. + \return Priority grouping field (SCB->AIRCR [10:8] PRIGROUP field). + */ +__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void) +{ + return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos)); +} + + +/** + \brief Enable Interrupt + \details Enables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + __COMPILER_BARRIER(); + NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __COMPILER_BARRIER(); + } +} + + +/** + \brief Get Interrupt Enable status + \details Returns a device specific interrupt enable status from the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt is not enabled. + \return 1 Interrupt is enabled. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Disable Interrupt + \details Disables a device specific interrupt in the NVIC interrupt controller. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + __DSB(); + __ISB(); + } +} + + +/** + \brief Get Pending Interrupt + \details Reads the NVIC pending register and returns the pending bit for the specified device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not pending. + \return 1 Interrupt status is pending. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Pending Interrupt + \details Sets the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Clear Pending Interrupt + \details Clears the pending bit of a device specific interrupt in the NVIC pending register. + \param [in] IRQn Device specific interrupt number. + \note IRQn must not be negative. + */ +__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); + } +} + + +/** + \brief Get Active Interrupt + \details Reads the active register in the NVIC and returns the active bit for the device specific interrupt. + \param [in] IRQn Device specific interrupt number. + \return 0 Interrupt status is not active. + \return 1 Interrupt status is active. + \note IRQn must not be negative. + */ +__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn) +{ + if ((int32_t)(IRQn) >= 0) + { + return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL)); + } + else + { + return(0U); + } +} + + +/** + \brief Set Interrupt Priority + \details Sets the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \param [in] priority Priority to set. + \note The priority cannot be set for every processor exception. + */ +__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) +{ + if ((int32_t)(IRQn) >= 0) + { + NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } + else + { + SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL); + } +} + + +/** + \brief Get Interrupt Priority + \details Reads the priority of a device specific interrupt or a processor exception. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Interrupt Priority. + Value is aligned automatically to the implemented priority bits of the microcontroller. + */ +__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn) +{ + + if ((int32_t)(IRQn) >= 0) + { + return(((uint32_t)NVIC->IP[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS))); + } + else + { + return(((uint32_t)SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS))); + } +} + + +/** + \brief Encode Priority + \details Encodes the priority for an interrupt with the given priority group, + preemptive priority value, and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS), the smallest possible priority group is set. + \param [in] PriorityGroup Used priority group. + \param [in] PreemptPriority Preemptive priority value (starting from 0). + \param [in] SubPriority Subpriority value (starting from 0). + \return Encoded priority. Value can be used in the function \ref NVIC_SetPriority(). + */ +__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + return ( + ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) | + ((SubPriority & (uint32_t)((1UL << (SubPriorityBits )) - 1UL))) + ); +} + + +/** + \brief Decode Priority + \details Decodes an interrupt priority value with a given priority group to + preemptive priority value and subpriority value. + In case of a conflict between priority grouping and available + priority bits (__NVIC_PRIO_BITS) the smallest possible priority group is set. + \param [in] Priority Priority value, which can be retrieved with the function \ref NVIC_GetPriority(). + \param [in] PriorityGroup Used priority group. + \param [out] pPreemptPriority Preemptive priority value (starting from 0). + \param [out] pSubPriority Subpriority value (starting from 0). + */ +__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority) +{ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */ + uint32_t PreemptPriorityBits; + uint32_t SubPriorityBits; + + PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp); + SubPriorityBits = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS)); + + *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL); + *pSubPriority = (Priority ) & (uint32_t)((1UL << (SubPriorityBits )) - 1UL); +} + + +/** + \brief Set Interrupt Vector + \details Sets an interrupt vector in SRAM based interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + VTOR must been relocated to SRAM before. + \param [in] IRQn Interrupt number + \param [in] vector Address of interrupt handler function + */ +__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector; + /* ARM Application Note 321 states that the M4 does not require the architectural barrier */ +} + + +/** + \brief Get Interrupt Vector + \details Reads an interrupt vector from interrupt vector table. + The interrupt number can be positive to specify a device specific interrupt, + or negative to specify a processor exception. + \param [in] IRQn Interrupt number. + \return Address of interrupt handler function + */ +__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn) +{ + uint32_t *vectors = (uint32_t *)SCB->VTOR; + return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET]; +} + + +/** + \brief System Reset + \details Initiates a system reset request to reset the MCU. + */ +__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void) +{ + __DSB(); /* Ensure all outstanding memory accesses included + buffered write are completed before reset */ + SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | + (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | + SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */ + __DSB(); /* Ensure completion of memory access */ + + for(;;) /* wait until reset */ + { + __NOP(); + } +} + +/*@} end of CMSIS_Core_NVICFunctions */ + + +/* ########################## MPU functions #################################### */ + +#if defined (__MPU_PRESENT) && (__MPU_PRESENT == 1U) + +#include "mpu_armv7.h" + +#endif + + +/* ########################## FPU functions #################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_FpuFunctions FPU Functions + \brief Function that provides FPU type. + @{ + */ + +/** + \brief get FPU type + \details returns the FPU type + \returns + - \b 0: No FPU + - \b 1: Single precision FPU + - \b 2: Double + Single precision FPU + */ +__STATIC_INLINE uint32_t SCB_GetFPUType(void) +{ + uint32_t mvfr0; + + mvfr0 = FPU->MVFR0; + if ((mvfr0 & (FPU_MVFR0_Single_precision_Msk | FPU_MVFR0_Double_precision_Msk)) == 0x020U) + { + return 1U; /* Single precision FPU */ + } + else + { + return 0U; /* No FPU */ + } +} + + +/*@} end of CMSIS_Core_FpuFunctions */ + + + +/* ################################## SysTick function ############################################ */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_Core_SysTickFunctions SysTick Functions + \brief Functions that configure the System. + @{ + */ + +#if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U) + +/** + \brief System Tick Configuration + \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. + Counter is in free running mode to generate periodic interrupts. + \param [in] ticks Number of ticks between two interrupts. + \return 0 Function succeeded. + \return 1 Function failed. + \note When the variable __Vendor_SysTickConfig is set to 1, then the + function SysTick_Config is not included. In this case, the file device.h + must contain a vendor-specific implementation of this function. + */ +__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) +{ + if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) + { + return (1UL); /* Reload value impossible */ + } + + SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ + NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ + SysTick->VAL = 0UL; /* Load the SysTick Counter Value */ + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_TICKINT_Msk | + SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */ + return (0UL); /* Function successful */ +} + +#endif + +/*@} end of CMSIS_Core_SysTickFunctions */ + + + +/* ##################################### Debug In/Output function ########################################### */ +/** + \ingroup CMSIS_Core_FunctionInterface + \defgroup CMSIS_core_DebugFunctions ITM Functions + \brief Functions that access the ITM debug interface. + @{ + */ + +extern volatile int32_t ITM_RxBuffer; /*!< External variable to receive characters. */ +#define ITM_RXBUFFER_EMPTY ((int32_t)0x5AA55AA5U) /*!< Value identifying \ref ITM_RxBuffer is ready for next character. */ + + +/** + \brief ITM Send Character + \details Transmits a character via the ITM channel 0, and + \li Just returns when no debugger is connected that has booked the output. + \li Is blocking when a debugger is connected, but the previous character sent has not been transmitted. + \param [in] ch Character to transmit. + \returns Character to transmit. + */ +__STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch) +{ + if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) && /* ITM enabled */ + ((ITM->TER & 1UL ) != 0UL) ) /* ITM Port #0 enabled */ + { + while (ITM->PORT[0U].u32 == 0UL) + { + __NOP(); + } + ITM->PORT[0U].u8 = (uint8_t)ch; + } + return (ch); +} + + +/** + \brief ITM Receive Character + \details Inputs a character via the external variable \ref ITM_RxBuffer. + \return Received character. + \return -1 No character pending. + */ +__STATIC_INLINE int32_t ITM_ReceiveChar (void) +{ + int32_t ch = -1; /* no character available */ + + if (ITM_RxBuffer != ITM_RXBUFFER_EMPTY) + { + ch = ITM_RxBuffer; + ITM_RxBuffer = ITM_RXBUFFER_EMPTY; /* ready for next character */ + } + + return (ch); +} + + +/** + \brief ITM Check Character + \details Checks whether a character is pending for reading in the variable \ref ITM_RxBuffer. + \return 0 No character available. + \return 1 Character available. + */ +__STATIC_INLINE int32_t ITM_CheckChar (void) +{ + + if (ITM_RxBuffer == ITM_RXBUFFER_EMPTY) + { + return (0); /* no character available */ + } + else + { + return (1); /* character available */ + } +} + +/*@} end of CMSIS_core_DebugFunctions */ + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_CM4_H_DEPENDANT */ + +#endif /* __CMSIS_GENERIC */ diff --git a/lib/cmsis_core/mpu_armv7.h b/lib/cmsis_core/mpu_armv7.h new file mode 100644 index 00000000000..d9eedf81a64 --- /dev/null +++ b/lib/cmsis_core/mpu_armv7.h @@ -0,0 +1,275 @@ +/****************************************************************************** + * @file mpu_armv7.h + * @brief CMSIS MPU API for Armv7-M MPU + * @version V5.1.2 + * @date 25. May 2020 + ******************************************************************************/ +/* + * Copyright (c) 2017-2020 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#elif defined (__clang__) + #pragma clang system_header /* treat file as system include file */ +#endif + +#ifndef ARM_MPU_ARMV7_H +#define ARM_MPU_ARMV7_H + +#define ARM_MPU_REGION_SIZE_32B ((uint8_t)0x04U) ///!< MPU Region Size 32 Bytes +#define ARM_MPU_REGION_SIZE_64B ((uint8_t)0x05U) ///!< MPU Region Size 64 Bytes +#define ARM_MPU_REGION_SIZE_128B ((uint8_t)0x06U) ///!< MPU Region Size 128 Bytes +#define ARM_MPU_REGION_SIZE_256B ((uint8_t)0x07U) ///!< MPU Region Size 256 Bytes +#define ARM_MPU_REGION_SIZE_512B ((uint8_t)0x08U) ///!< MPU Region Size 512 Bytes +#define ARM_MPU_REGION_SIZE_1KB ((uint8_t)0x09U) ///!< MPU Region Size 1 KByte +#define ARM_MPU_REGION_SIZE_2KB ((uint8_t)0x0AU) ///!< MPU Region Size 2 KBytes +#define ARM_MPU_REGION_SIZE_4KB ((uint8_t)0x0BU) ///!< MPU Region Size 4 KBytes +#define ARM_MPU_REGION_SIZE_8KB ((uint8_t)0x0CU) ///!< MPU Region Size 8 KBytes +#define ARM_MPU_REGION_SIZE_16KB ((uint8_t)0x0DU) ///!< MPU Region Size 16 KBytes +#define ARM_MPU_REGION_SIZE_32KB ((uint8_t)0x0EU) ///!< MPU Region Size 32 KBytes +#define ARM_MPU_REGION_SIZE_64KB ((uint8_t)0x0FU) ///!< MPU Region Size 64 KBytes +#define ARM_MPU_REGION_SIZE_128KB ((uint8_t)0x10U) ///!< MPU Region Size 128 KBytes +#define ARM_MPU_REGION_SIZE_256KB ((uint8_t)0x11U) ///!< MPU Region Size 256 KBytes +#define ARM_MPU_REGION_SIZE_512KB ((uint8_t)0x12U) ///!< MPU Region Size 512 KBytes +#define ARM_MPU_REGION_SIZE_1MB ((uint8_t)0x13U) ///!< MPU Region Size 1 MByte +#define ARM_MPU_REGION_SIZE_2MB ((uint8_t)0x14U) ///!< MPU Region Size 2 MBytes +#define ARM_MPU_REGION_SIZE_4MB ((uint8_t)0x15U) ///!< MPU Region Size 4 MBytes +#define ARM_MPU_REGION_SIZE_8MB ((uint8_t)0x16U) ///!< MPU Region Size 8 MBytes +#define ARM_MPU_REGION_SIZE_16MB ((uint8_t)0x17U) ///!< MPU Region Size 16 MBytes +#define ARM_MPU_REGION_SIZE_32MB ((uint8_t)0x18U) ///!< MPU Region Size 32 MBytes +#define ARM_MPU_REGION_SIZE_64MB ((uint8_t)0x19U) ///!< MPU Region Size 64 MBytes +#define ARM_MPU_REGION_SIZE_128MB ((uint8_t)0x1AU) ///!< MPU Region Size 128 MBytes +#define ARM_MPU_REGION_SIZE_256MB ((uint8_t)0x1BU) ///!< MPU Region Size 256 MBytes +#define ARM_MPU_REGION_SIZE_512MB ((uint8_t)0x1CU) ///!< MPU Region Size 512 MBytes +#define ARM_MPU_REGION_SIZE_1GB ((uint8_t)0x1DU) ///!< MPU Region Size 1 GByte +#define ARM_MPU_REGION_SIZE_2GB ((uint8_t)0x1EU) ///!< MPU Region Size 2 GBytes +#define ARM_MPU_REGION_SIZE_4GB ((uint8_t)0x1FU) ///!< MPU Region Size 4 GBytes + +#define ARM_MPU_AP_NONE 0U ///!< MPU Access Permission no access +#define ARM_MPU_AP_PRIV 1U ///!< MPU Access Permission privileged access only +#define ARM_MPU_AP_URO 2U ///!< MPU Access Permission unprivileged access read-only +#define ARM_MPU_AP_FULL 3U ///!< MPU Access Permission full access +#define ARM_MPU_AP_PRO 5U ///!< MPU Access Permission privileged access read-only +#define ARM_MPU_AP_RO 6U ///!< MPU Access Permission read-only access + +/** MPU Region Base Address Register Value +* +* \param Region The region to be configured, number 0 to 15. +* \param BaseAddress The base address for the region. +*/ +#define ARM_MPU_RBAR(Region, BaseAddress) \ + (((BaseAddress) & MPU_RBAR_ADDR_Msk) | \ + ((Region) & MPU_RBAR_REGION_Msk) | \ + (MPU_RBAR_VALID_Msk)) + +/** +* MPU Memory Access Attributes +* +* \param TypeExtField Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral. +* \param IsShareable Region is shareable between multiple bus masters. +* \param IsCacheable Region is cacheable, i.e. its value may be kept in cache. +* \param IsBufferable Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy. +*/ +#define ARM_MPU_ACCESS_(TypeExtField, IsShareable, IsCacheable, IsBufferable) \ + ((((TypeExtField) << MPU_RASR_TEX_Pos) & MPU_RASR_TEX_Msk) | \ + (((IsShareable) << MPU_RASR_S_Pos) & MPU_RASR_S_Msk) | \ + (((IsCacheable) << MPU_RASR_C_Pos) & MPU_RASR_C_Msk) | \ + (((IsBufferable) << MPU_RASR_B_Pos) & MPU_RASR_B_Msk)) + +/** +* MPU Region Attribute and Size Register Value +* +* \param DisableExec Instruction access disable bit, 1= disable instruction fetches. +* \param AccessPermission Data access permissions, allows you to configure read/write access for User and Privileged mode. +* \param AccessAttributes Memory access attribution, see \ref ARM_MPU_ACCESS_. +* \param SubRegionDisable Sub-region disable field. +* \param Size Region size of the region to be configured, for example 4K, 8K. +*/ +#define ARM_MPU_RASR_EX(DisableExec, AccessPermission, AccessAttributes, SubRegionDisable, Size) \ + ((((DisableExec) << MPU_RASR_XN_Pos) & MPU_RASR_XN_Msk) | \ + (((AccessPermission) << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk) | \ + (((AccessAttributes) & (MPU_RASR_TEX_Msk | MPU_RASR_S_Msk | MPU_RASR_C_Msk | MPU_RASR_B_Msk))) | \ + (((SubRegionDisable) << MPU_RASR_SRD_Pos) & MPU_RASR_SRD_Msk) | \ + (((Size) << MPU_RASR_SIZE_Pos) & MPU_RASR_SIZE_Msk) | \ + (((MPU_RASR_ENABLE_Msk)))) + +/** +* MPU Region Attribute and Size Register Value +* +* \param DisableExec Instruction access disable bit, 1= disable instruction fetches. +* \param AccessPermission Data access permissions, allows you to configure read/write access for User and Privileged mode. +* \param TypeExtField Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral. +* \param IsShareable Region is shareable between multiple bus masters. +* \param IsCacheable Region is cacheable, i.e. its value may be kept in cache. +* \param IsBufferable Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy. +* \param SubRegionDisable Sub-region disable field. +* \param Size Region size of the region to be configured, for example 4K, 8K. +*/ +#define ARM_MPU_RASR(DisableExec, AccessPermission, TypeExtField, IsShareable, IsCacheable, IsBufferable, SubRegionDisable, Size) \ + ARM_MPU_RASR_EX(DisableExec, AccessPermission, ARM_MPU_ACCESS_(TypeExtField, IsShareable, IsCacheable, IsBufferable), SubRegionDisable, Size) + +/** +* MPU Memory Access Attribute for strongly ordered memory. +* - TEX: 000b +* - Shareable +* - Non-cacheable +* - Non-bufferable +*/ +#define ARM_MPU_ACCESS_ORDERED ARM_MPU_ACCESS_(0U, 1U, 0U, 0U) + +/** +* MPU Memory Access Attribute for device memory. +* - TEX: 000b (if shareable) or 010b (if non-shareable) +* - Shareable or non-shareable +* - Non-cacheable +* - Bufferable (if shareable) or non-bufferable (if non-shareable) +* +* \param IsShareable Configures the device memory as shareable or non-shareable. +*/ +#define ARM_MPU_ACCESS_DEVICE(IsShareable) ((IsShareable) ? ARM_MPU_ACCESS_(0U, 1U, 0U, 1U) : ARM_MPU_ACCESS_(2U, 0U, 0U, 0U)) + +/** +* MPU Memory Access Attribute for normal memory. +* - TEX: 1BBb (reflecting outer cacheability rules) +* - Shareable or non-shareable +* - Cacheable or non-cacheable (reflecting inner cacheability rules) +* - Bufferable or non-bufferable (reflecting inner cacheability rules) +* +* \param OuterCp Configures the outer cache policy. +* \param InnerCp Configures the inner cache policy. +* \param IsShareable Configures the memory as shareable or non-shareable. +*/ +#define ARM_MPU_ACCESS_NORMAL(OuterCp, InnerCp, IsShareable) ARM_MPU_ACCESS_((4U | (OuterCp)), IsShareable, ((InnerCp) >> 1U), ((InnerCp) & 1U)) + +/** +* MPU Memory Access Attribute non-cacheable policy. +*/ +#define ARM_MPU_CACHEP_NOCACHE 0U + +/** +* MPU Memory Access Attribute write-back, write and read allocate policy. +*/ +#define ARM_MPU_CACHEP_WB_WRA 1U + +/** +* MPU Memory Access Attribute write-through, no write allocate policy. +*/ +#define ARM_MPU_CACHEP_WT_NWA 2U + +/** +* MPU Memory Access Attribute write-back, no write allocate policy. +*/ +#define ARM_MPU_CACHEP_WB_NWA 3U + + +/** +* Struct for a single MPU Region +*/ +typedef struct { + uint32_t RBAR; //!< The region base address register value (RBAR) + uint32_t RASR; //!< The region attribute and size register value (RASR) \ref MPU_RASR +} ARM_MPU_Region_t; + +/** Enable the MPU. +* \param MPU_Control Default access permissions for unconfigured regions. +*/ +__STATIC_INLINE void ARM_MPU_Enable(uint32_t MPU_Control) +{ + __DMB(); + MPU->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk; +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; +#endif + __DSB(); + __ISB(); +} + +/** Disable the MPU. +*/ +__STATIC_INLINE void ARM_MPU_Disable(void) +{ + __DMB(); +#ifdef SCB_SHCSR_MEMFAULTENA_Msk + SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk; +#endif + MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk; + __DSB(); + __ISB(); +} + +/** Clear and disable the given MPU region. +* \param rnr Region number to be cleared. +*/ +__STATIC_INLINE void ARM_MPU_ClrRegion(uint32_t rnr) +{ + MPU->RNR = rnr; + MPU->RASR = 0U; +} + +/** Configure an MPU region. +* \param rbar Value for RBAR register. +* \param rasr Value for RASR register. +*/ +__STATIC_INLINE void ARM_MPU_SetRegion(uint32_t rbar, uint32_t rasr) +{ + MPU->RBAR = rbar; + MPU->RASR = rasr; +} + +/** Configure the given MPU region. +* \param rnr Region number to be configured. +* \param rbar Value for RBAR register. +* \param rasr Value for RASR register. +*/ +__STATIC_INLINE void ARM_MPU_SetRegionEx(uint32_t rnr, uint32_t rbar, uint32_t rasr) +{ + MPU->RNR = rnr; + MPU->RBAR = rbar; + MPU->RASR = rasr; +} + +/** Memcpy with strictly ordered memory access, e.g. used by code in ARM_MPU_Load(). +* \param dst Destination data is copied to. +* \param src Source data is copied from. +* \param len Amount of data words to be copied. +*/ +__STATIC_INLINE void ARM_MPU_OrderedMemcpy(volatile uint32_t* dst, const uint32_t* __RESTRICT src, uint32_t len) +{ + uint32_t i; + for (i = 0U; i < len; ++i) + { + dst[i] = src[i]; + } +} + +/** Load the given number of MPU regions from a table. +* \param table Pointer to the MPU configuration table. +* \param cnt Amount of regions to be configured. +*/ +__STATIC_INLINE void ARM_MPU_Load(ARM_MPU_Region_t const* table, uint32_t cnt) +{ + const uint32_t rowWordSize = sizeof(ARM_MPU_Region_t)/4U; + while (cnt > MPU_TYPE_RALIASES) { + ARM_MPU_OrderedMemcpy(&(MPU->RBAR), &(table->RBAR), MPU_TYPE_RALIASES*rowWordSize); + table += MPU_TYPE_RALIASES; + cnt -= MPU_TYPE_RALIASES; + } + ARM_MPU_OrderedMemcpy(&(MPU->RBAR), &(table->RBAR), cnt*rowWordSize); +} + +#endif diff --git a/lib/cxxheaderparser b/lib/cxxheaderparser new file mode 160000 index 00000000000..ba4222560fc --- /dev/null +++ b/lib/cxxheaderparser @@ -0,0 +1 @@ +Subproject commit ba4222560fc1040670b1a917d5d357198e8ec5d6 diff --git a/lib/digital_signal/SConscript b/lib/digital_signal/SConscript new file mode 100644 index 00000000000..41d33489047 --- /dev/null +++ b/lib/digital_signal/SConscript @@ -0,0 +1,24 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/digital_signal", + ], + SDK_HEADERS=[ + File("digital_signal.h"), + File("digital_sequence.h"), + ], + LINT_SOURCES=[ + Dir("."), + ], +) + +libenv = env.Clone(FW_LIB_NAME="digital_signal") +libenv.ApplyLibFlags() +libenv.Append(CCFLAGS=["-O3", "-funroll-loops", "-Ofast"]) + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/digital_signal/digital_sequence.c b/lib/digital_signal/digital_sequence.c new file mode 100644 index 00000000000..c85aae8c062 --- /dev/null +++ b/lib/digital_signal/digital_sequence.c @@ -0,0 +1,381 @@ +#include "digital_sequence.h" +#include "digital_signal_i.h" + +#include +#include + +#include +#include + +/** + * To enable debug output on an additional pin, set DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN to the required + * GpioPin variable. It can be passed at compile time via the --extra-define fbt switch. + * NOTE: This pin must be on the same GPIO port as the main pin. + * + * Example: + * ./fbt --extra-define=DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN=gpio_ext_pb3 + */ + +#define TAG "DigitalSequence" + +/* Special value used to indicate the end of DMA ring buffer. */ +#define DIGITAL_SEQUENCE_TIMER_MAX 0xFFFFFFFFUL + +/* Time to wait in loops before returning */ +#define DIGITAL_SEQUENCE_LOCK_WAIT_MS 10UL +#define DIGITAL_SEQUENCE_LOCK_WAIT_TICKS (DIGITAL_SEQUENCE_LOCK_WAIT_MS * 1000 * 64) + +#define DIGITAL_SEQUENCE_GPIO_BUFFER_SIZE 2 + +/* Maximum capacity of the DMA ring buffer. */ +#define DIGITAL_SEQUENCE_RING_BUFFER_SIZE 128 + +#define DIGITAL_SEQUENCE_RING_BUFFER_MIN_FREE_SIZE 2 + +/* Maximum amount of registered signals. */ +#define DIGITAL_SEQUENCE_BANK_SIZE 32 + +typedef enum { + DigitalSequenceStateIdle, + DigitalSequenceStateActive, +} DigitalSequenceState; + +typedef struct { + uint32_t data[DIGITAL_SEQUENCE_RING_BUFFER_SIZE]; + uint32_t write_pos; + uint32_t read_pos; +} DigitalSequenceRingBuffer; + +typedef uint32_t DigitalSequenceGpioBuffer[DIGITAL_SEQUENCE_GPIO_BUFFER_SIZE]; + +typedef const DigitalSignal* DigitalSequenceSignalBank[DIGITAL_SEQUENCE_BANK_SIZE]; + +struct DigitalSequence { + const GpioPin* gpio; + + uint32_t size; + uint32_t max_size; + uint8_t* data; + + LL_DMA_InitTypeDef dma_config_gpio; + LL_DMA_InitTypeDef dma_config_timer; + + DigitalSequenceGpioBuffer gpio_buf; + DigitalSequenceRingBuffer timer_buf; + DigitalSequenceSignalBank signals; + DigitalSequenceState state; +}; + +DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { + furi_assert(size); + furi_assert(gpio); + + DigitalSequence* sequence = malloc(sizeof(DigitalSequence)); + + sequence->gpio = gpio; + sequence->max_size = size; + + sequence->data = malloc(sequence->max_size); + + sequence->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t)&gpio->port->BSRR; + sequence->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)sequence->gpio_buf; + sequence->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + sequence->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; + sequence->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + sequence->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + sequence->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + sequence->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + sequence->dma_config_gpio.NbData = DIGITAL_SEQUENCE_GPIO_BUFFER_SIZE; + sequence->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + sequence->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; + + sequence->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t)&TIM2->ARR; + sequence->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)sequence->timer_buf.data; + sequence->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + sequence->dma_config_timer.Mode = LL_DMA_MODE_CIRCULAR; + sequence->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + sequence->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + sequence->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + sequence->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + sequence->dma_config_timer.NbData = DIGITAL_SEQUENCE_RING_BUFFER_SIZE; + sequence->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + sequence->dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH; + + return sequence; +} + +void digital_sequence_free(DigitalSequence* sequence) { + furi_assert(sequence); + + free(sequence->data); + free(sequence); +} + +void digital_sequence_register_signal( + DigitalSequence* sequence, + uint8_t signal_index, + const DigitalSignal* signal) { + furi_assert(sequence); + furi_assert(signal); + furi_assert(signal_index < DIGITAL_SEQUENCE_BANK_SIZE); + + sequence->signals[signal_index] = signal; +} + +void digital_sequence_add_signal(DigitalSequence* sequence, uint8_t signal_index) { + furi_assert(sequence); + furi_assert(signal_index < DIGITAL_SEQUENCE_BANK_SIZE); + furi_assert(sequence->size < sequence->max_size); + + sequence->data[sequence->size++] = signal_index; +} + +static inline void digital_sequence_start_dma(DigitalSequence* sequence) { + furi_assert(sequence); + + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &sequence->dma_config_gpio); + LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &sequence->dma_config_timer); + + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); +} + +static inline void digital_sequence_stop_dma() { + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_ClearFlag_TC1(DMA1); + LL_DMA_ClearFlag_TC2(DMA1); +} + +static inline void digital_sequence_start_timer() { + furi_hal_bus_enable(FuriHalBusTIM2); + + LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetPrescaler(TIM2, 0); + LL_TIM_SetAutoReload(TIM2, DIGITAL_SEQUENCE_TIMER_MAX); + LL_TIM_SetCounter(TIM2, 0); + + LL_TIM_EnableCounter(TIM2); + LL_TIM_EnableUpdateEvent(TIM2); + LL_TIM_EnableDMAReq_UPDATE(TIM2); + LL_TIM_GenerateEvent_UPDATE(TIM2); +} + +static void digital_sequence_stop_timer() { + LL_TIM_DisableCounter(TIM2); + LL_TIM_DisableUpdateEvent(TIM2); + LL_TIM_DisableDMAReq_UPDATE(TIM2); + + furi_hal_bus_disable(FuriHalBusTIM2); +} + +static inline void digital_sequence_init_gpio_buffer( + DigitalSequence* sequence, + const DigitalSignal* first_signal) { + const uint32_t bit_set = sequence->gpio->pin << GPIO_BSRR_BS0_Pos +#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN + | DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin << GPIO_BSRR_BS0_Pos +#endif + ; + + const uint32_t bit_reset = sequence->gpio->pin << GPIO_BSRR_BR0_Pos +#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN + | DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin << GPIO_BSRR_BR0_Pos +#endif + ; + + if(first_signal->start_level) { + sequence->gpio_buf[0] = bit_set; + sequence->gpio_buf[1] = bit_reset; + } else { + sequence->gpio_buf[0] = bit_reset; + sequence->gpio_buf[1] = bit_set; + } +} + +static inline void digital_sequence_finish(DigitalSequence* sequence) { + if(sequence->state == DigitalSequenceStateActive) { + const uint32_t prev_timer = DWT->CYCCNT; + + do { + /* Special value has been loaded into the timer, signaling the end of transmission. */ + if(TIM2->ARR == DIGITAL_SEQUENCE_TIMER_MAX) { + break; + } + + if(DWT->CYCCNT - prev_timer > DIGITAL_SEQUENCE_LOCK_WAIT_TICKS) { + DigitalSequenceRingBuffer* dma_buffer = &sequence->timer_buf; + dma_buffer->read_pos = DIGITAL_SEQUENCE_RING_BUFFER_SIZE - + LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); + FURI_LOG_D( + TAG, + "[SEQ] hung %lu ms in finish (ARR 0x%08lx, read %lu, write %lu)", + DIGITAL_SEQUENCE_LOCK_WAIT_MS, + TIM2->ARR, + dma_buffer->read_pos, + dma_buffer->write_pos); + break; + } + } while(true); + } + + digital_sequence_stop_timer(); + digital_sequence_stop_dma(); +} + +static inline void digital_sequence_enqueue_period(DigitalSequence* sequence, uint32_t length) { + DigitalSequenceRingBuffer* dma_buffer = &sequence->timer_buf; + + if(sequence->state == DigitalSequenceStateActive) { + const uint32_t prev_timer = DWT->CYCCNT; + + do { + dma_buffer->read_pos = + DIGITAL_SEQUENCE_RING_BUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); + + const uint32_t size_free = (DIGITAL_SEQUENCE_RING_BUFFER_SIZE + dma_buffer->read_pos - + dma_buffer->write_pos) % + DIGITAL_SEQUENCE_RING_BUFFER_SIZE; + + if(size_free > DIGITAL_SEQUENCE_RING_BUFFER_MIN_FREE_SIZE) { + break; + } + + if(DWT->CYCCNT - prev_timer > DIGITAL_SEQUENCE_LOCK_WAIT_TICKS) { + FURI_LOG_D( + TAG, + "[SEQ] hung %lu ms in queue (ARR 0x%08lx, read %lu, write %lu)", + DIGITAL_SEQUENCE_LOCK_WAIT_MS, + TIM2->ARR, + dma_buffer->read_pos, + dma_buffer->write_pos); + break; + } + + if(TIM2->ARR == DIGITAL_SEQUENCE_TIMER_MAX) { + FURI_LOG_D( + TAG, + "[SEQ] buffer underrun in queue (ARR 0x%08lx, read %lu, write %lu)", + TIM2->ARR, + dma_buffer->read_pos, + dma_buffer->write_pos); + break; + } + } while(true); + } + + dma_buffer->data[dma_buffer->write_pos] = length; + + dma_buffer->write_pos += 1; + dma_buffer->write_pos %= DIGITAL_SEQUENCE_RING_BUFFER_SIZE; + + dma_buffer->data[dma_buffer->write_pos] = DIGITAL_SEQUENCE_TIMER_MAX; +} + +static inline void digital_sequence_timer_buffer_reset(DigitalSequence* sequence) { + sequence->timer_buf.data[0] = DIGITAL_SEQUENCE_TIMER_MAX; + sequence->timer_buf.read_pos = 0; + sequence->timer_buf.write_pos = 0; +} + +void digital_sequence_transmit(DigitalSequence* sequence) { + furi_assert(sequence); + furi_assert(sequence->size); + furi_assert(sequence->state == DigitalSequenceStateIdle); + + FURI_CRITICAL_ENTER(); + + furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN + furi_hal_gpio_init( + &DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +#endif + + const DigitalSignal* signal_current = sequence->signals[sequence->data[0]]; + + digital_sequence_init_gpio_buffer(sequence, signal_current); + + int32_t remainder_ticks = 0; + uint32_t reload_value_carry = 0; + uint32_t next_signal_index = 1; + + for(;;) { + const DigitalSignal* signal_next = + (next_signal_index < sequence->size) ? + sequence->signals[sequence->data[next_signal_index++]] : + NULL; + + for(uint32_t i = 0; i < signal_current->size; i++) { + const bool is_last_value = (i == signal_current->size - 1); + const uint32_t reload_value = signal_current->data[i] + reload_value_carry; + + reload_value_carry = 0; + + if(is_last_value) { + if(signal_next != NULL) { + /* Special case: signal boundary. Depending on whether the adjacent levels are equal or not, + * they will be combined to a single one or handled separately. */ + const bool end_level = signal_current->start_level ^ + ((signal_current->size % 2) == 0); + + /* If the adjacent levels are equal, carry the current period duration over to the next signal. */ + if(end_level == signal_next->start_level) { + reload_value_carry = reload_value; + } + } else { + /** Special case: during the last period of the last signal, hold the output level indefinitely. + * @see digital_signal.h + * + * Setting reload_value_carry to a non-zero value will prevent the respective period from being + * added to the DMA ring buffer. */ + reload_value_carry = 1; + } + } + + /* A non-zero reload_value_carry means that the level was the same on the both sides of the signal boundary + * and the two respective periods were combined to one. */ + if(reload_value_carry == 0) { + digital_sequence_enqueue_period(sequence, reload_value); + } + + if(sequence->state == DigitalSequenceStateIdle) { + const bool is_buffer_filled = sequence->timer_buf.write_pos >= + (DIGITAL_SEQUENCE_RING_BUFFER_SIZE - + DIGITAL_SEQUENCE_RING_BUFFER_MIN_FREE_SIZE); + const bool is_end_of_data = (signal_next == NULL) && is_last_value; + + if(is_buffer_filled || is_end_of_data) { + digital_sequence_start_dma(sequence); + digital_sequence_start_timer(); + sequence->state = DigitalSequenceStateActive; + } + } + } + + /* Exit the loop here when no further signals are available */ + if(signal_next == NULL) break; + + /* Prevent the rounding error from accumulating by distributing it across multiple periods. */ + remainder_ticks += signal_current->remainder; + if(remainder_ticks >= DIGITAL_SIGNAL_T_TIM_DIV2) { + remainder_ticks -= DIGITAL_SIGNAL_T_TIM; + reload_value_carry += 1; + } + + signal_current = signal_next; + }; + + digital_sequence_finish(sequence); + digital_sequence_timer_buffer_reset(sequence); + + FURI_CRITICAL_EXIT(); + + sequence->state = DigitalSequenceStateIdle; +} + +void digital_sequence_clear(DigitalSequence* sequence) { + furi_assert(sequence); + + sequence->size = 0; +} diff --git a/lib/digital_signal/digital_sequence.h b/lib/digital_signal/digital_sequence.h new file mode 100644 index 00000000000..4f04ecc464b --- /dev/null +++ b/lib/digital_signal/digital_sequence.h @@ -0,0 +1,112 @@ +/** + * @file digital_sequence.h + * @brief Fast and precise digital signal generation library. + * + * Each sequence is represented by one or more (up to 32) registered signals, which are addressed + * by their indices, and a list of signal indices to be transmitted. + * + * The registered signals must be set up prior to actually defining the sequence. + * + * Example: A sequence containing 4 registered signals and n indices to transmit. + * + * |Signal | Index | + * |:-----:|:-----:| + * | SOF | 0 | + * | EOF | 1 | + * | Zero | 2 | + * | One | 3 | + * + * ``` + * Signal index | 0 | 3 | 2 | 2 | ... | 3 | 1 | + * 0 1 2 3 ... n - 2 n - 1 + * ``` + * + * The above sequence starts by transmitting the signal with index 0, which is SOF in this case, + * then it proceeds with indices 3, 2, 2, which are One, Zero, Zero and after n - 2 signals, + * it will conclude with indices 3 and 1 which are One and EOF respectively. + * + * This way, only the order in which the signals are sent is stored, while the signals themselves + * are not duplicated. + */ +#pragma once + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DigitalSequence DigitalSequence; + +/** + * @brief Allocate a DigitalSequence instance of a given size which will operate on a set GPIO pin. + * + * @param[in] size maximum number of signal indices contained in the instance. + * @param[in] gpio the GPIO pin used to generate the signal. + * @returns pointer to the allocated DigitalSequence instance. + */ +DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio); + +/** + * @brief Delete a previously allocated DigitalSequence instance. + * + * @param[in,out] sequence pointer to the instance to be deleted. + */ +void digital_sequence_free(DigitalSequence* sequence); + +/** + * @brief Register a signal within a DigitalSequence instance by its index. + * + * This function must be called for each signal to be used in the sequence. The DigitalSequence + * instance does not own the signals, therefore, their lifetime must be no less than the instance's. + * + * The user is responsible for creation and deletion of DigitalSignal instances and + * also for keeping track of their respective indices. + * + * @param[in,out] sequence pointer to the instance to be modified. + * @param[in] signal_index index to register the signal under (must be less than 32). + * @param[in] signal pointer to the DigitalSignal instance to be registered. + */ +void digital_sequence_register_signal( + DigitalSequence* sequence, + uint8_t signal_index, + const DigitalSignal* signal); + +/** + * @brief Append a signal index to a DigitalSequence instance. + * + * The signal under the index must be registered beforehand by calling digital_sequence_set_signal(). + * + * @param[in,out] sequence pointer to the instance to be modified. + * @param[in] signal_index signal index to be appended to the sequence (must be less than 32). + */ +void digital_sequence_add_signal(DigitalSequence* sequence, uint8_t signal_index); + +/** + * @brief Transmit the sequence contained in the DigitalSequence instance. + * + * Must contain at least one registered signal and one signal index. + * + * NOTE: The current implementation will properly initialise the GPIO provided during construction, + * but it is the caller's responsibility to reconfigure it back before reusing for other purposes. + * This is due to performance reasons. + * + * @param[in] sequence pointer to the sequence to be transmitted. + */ +void digital_sequence_transmit(DigitalSequence* sequence); + +/** + * @brief Clear the signal sequence in a DigitalSequence instance. + * + * Calling this function does not un-register the registered signals, so it is + * safe to call digital_sequence_add_signal() right afterwards. + * + * @param[in,out] sequence pointer to the instance to be cleared. + */ +void digital_sequence_clear(DigitalSequence* sequence); + +#ifdef __cplusplus +} +#endif diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 46ca307a7fb..a0408900de8 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -1,23 +1,15 @@ #include "digital_signal.h" +#include "digital_signal_i.h" #include -#include -#include -#include -#pragma GCC optimize("O3,unroll-loops,Ofast") +#define TAG "DigitalSignal" -#define F_TIM (64000000.0) -#define T_TIM 1562 //15.625 ns *100 -#define T_TIM_DIV2 781 //15.625 ns / 2 *100 - -DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { +DigitalSignal* digital_signal_alloc(uint32_t max_size) { DigitalSignal* signal = malloc(sizeof(DigitalSignal)); - signal->start_level = true; - signal->edges_max_cnt = max_edges_cnt; - signal->edge_timings = malloc(max_edges_cnt * sizeof(uint32_t)); - signal->reload_reg_buff = malloc(max_edges_cnt * sizeof(uint32_t)); - signal->edge_cnt = 0; + + signal->max_size = max_size; + signal->data = malloc(max_size * sizeof(uint32_t)); return signal; } @@ -25,149 +17,81 @@ DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) { void digital_signal_free(DigitalSignal* signal) { furi_assert(signal); - free(signal->edge_timings); - free(signal->reload_reg_buff); + free(signal->data); free(signal); } -bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b) { - furi_assert(signal_a); - furi_assert(signal_b); - - if(signal_a->edges_max_cnt < signal_a->edge_cnt + signal_b->edge_cnt) { - return false; - } - - bool end_level = signal_a->start_level; - if(signal_a->edge_cnt) { - end_level = signal_a->start_level ^ !(signal_a->edge_cnt % 2); - } - uint8_t start_copy = 0; - if(end_level == signal_b->start_level) { - if(signal_a->edge_cnt) { - signal_a->edge_timings[signal_a->edge_cnt - 1] += signal_b->edge_timings[0]; - start_copy += 1; - } else { - signal_a->edge_timings[signal_a->edge_cnt] += signal_b->edge_timings[0]; - } - } - - for(size_t i = 0; i < signal_b->edge_cnt - start_copy; i++) { - signal_a->edge_timings[signal_a->edge_cnt + i] = signal_b->edge_timings[start_copy + i]; - } - signal_a->edge_cnt += signal_b->edge_cnt - start_copy; +bool digital_signal_get_start_level(const DigitalSignal* signal) { + furi_assert(signal); - return true; + return signal->start_level; } -bool digital_signal_get_start_level(DigitalSignal* signal) { +void digital_signal_set_start_level(DigitalSignal* signal, bool level) { furi_assert(signal); - return signal->start_level; + signal->start_level = level; } -uint32_t digital_signal_get_edges_cnt(DigitalSignal* signal) { +uint32_t digital_signal_get_size(const DigitalSignal* signal) { furi_assert(signal); - return signal->edge_cnt; + return signal->size; } -uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) { +void digital_signal_add_period(DigitalSignal* signal, uint32_t ticks) { furi_assert(signal); - furi_assert(edge_num < signal->edge_cnt); + furi_assert(signal->size < signal->max_size); - return signal->edge_timings[edge_num]; + const uint32_t duration = ticks + signal->remainder; + + uint32_t reload_value = duration / DIGITAL_SIGNAL_T_TIM; + int32_t remainder = duration - reload_value * DIGITAL_SIGNAL_T_TIM; + + if(remainder >= DIGITAL_SIGNAL_T_TIM_DIV2) { + reload_value += 1; + remainder -= DIGITAL_SIGNAL_T_TIM; + } + + furi_check(reload_value > 1); + + signal->data[signal->size++] = reload_value - 1; + signal->remainder = remainder; } -void digital_signal_prepare_arr(DigitalSignal* signal) { - uint32_t t_signal_rest = signal->edge_timings[0]; - uint32_t r_count_tick_arr = 0; - uint32_t r_rest_div = 0; +static void digital_signal_extend_last_period(DigitalSignal* signal, uint32_t ticks) { + furi_assert(signal->size <= signal->max_size); - for(size_t i = 0; i < signal->edge_cnt - 1; i++) { - r_count_tick_arr = t_signal_rest / T_TIM; - r_rest_div = t_signal_rest % T_TIM; - t_signal_rest = signal->edge_timings[i + 1] + r_rest_div; + const uint32_t reload_value_old = signal->data[signal->size - 1] + 1; + const uint32_t duration = ticks + signal->remainder + reload_value_old * DIGITAL_SIGNAL_T_TIM; - if(r_rest_div < T_TIM_DIV2) { - signal->reload_reg_buff[i] = r_count_tick_arr - 1; - } else { - signal->reload_reg_buff[i] = r_count_tick_arr; - t_signal_rest -= T_TIM; - } + uint32_t reload_value = duration / DIGITAL_SIGNAL_T_TIM; + int32_t remainder = duration - reload_value * DIGITAL_SIGNAL_T_TIM; + + if(remainder >= DIGITAL_SIGNAL_T_TIM_DIV2) { + reload_value += 1; + remainder -= DIGITAL_SIGNAL_T_TIM; } + + furi_check(reload_value > 1); + + signal->data[signal->size - 1] = reload_value - 1; + signal->remainder = remainder; } -void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) { +void digital_signal_add_period_with_level(DigitalSignal* signal, uint32_t ticks, bool level) { furi_assert(signal); - furi_assert(gpio); - - // Configure gpio as output - furi_hal_gpio_init(gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - // Init gpio buffer and DMA channel - uint16_t gpio_reg = gpio->port->ODR; - uint16_t gpio_buff[2]; - if(signal->start_level) { - gpio_buff[0] = gpio_reg | gpio->pin; - gpio_buff[1] = gpio_reg & ~(gpio->pin); + if(signal->size == 0) { + signal->start_level = level; + digital_signal_add_period(signal, ticks); } else { - gpio_buff[0] = gpio_reg & ~(gpio->pin); - gpio_buff[1] = gpio_reg | gpio->pin; + const bool end_level = signal->start_level ^ !(signal->size % 2); + + if(level != end_level) { + digital_signal_add_period(signal, ticks); + } else { + digital_signal_extend_last_period(signal, ticks); + } } - LL_DMA_InitTypeDef dma_config = {}; - dma_config.MemoryOrM2MDstAddress = (uint32_t)gpio_buff; - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->ODR); - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - dma_config.Mode = LL_DMA_MODE_CIRCULAR; - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD; - dma_config.NbData = 2; - dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 2); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); - - // Init timer arr register buffer and DMA channel - digital_signal_prepare_arr(signal); - dma_config.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff; - dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR); - dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; - dma_config.Mode = LL_DMA_MODE_NORMAL; - dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; - dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; - dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; - dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; - dma_config.NbData = signal->edge_cnt - 2; - dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; - dma_config.Priority = LL_DMA_PRIORITY_HIGH; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, signal->edge_cnt - 2); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); - - // Set up timer - LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); - LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); - LL_TIM_SetPrescaler(TIM2, 0); - LL_TIM_SetAutoReload(TIM2, 10); - LL_TIM_SetCounter(TIM2, 0); - LL_TIM_EnableUpdateEvent(TIM2); - LL_TIM_EnableDMAReq_UPDATE(TIM2); - - // Start transactions - LL_TIM_GenerateEvent_UPDATE(TIM2); // Do we really need it? - LL_TIM_EnableCounter(TIM2); - - while(!LL_DMA_IsActiveFlag_TC2(DMA1)) - ; - - LL_DMA_ClearFlag_TC1(DMA1); - LL_DMA_ClearFlag_TC2(DMA1); - LL_TIM_DisableCounter(TIM2); - LL_TIM_SetCounter(TIM2, 0); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); } diff --git a/lib/digital_signal/digital_signal.h b/lib/digital_signal/digital_signal.h index d828444ca18..f29facfd83d 100644 --- a/lib/digital_signal/digital_signal.h +++ b/lib/digital_signal/digital_signal.h @@ -1,31 +1,122 @@ +/** + * @file digital_signal.h + * @brief Simple digital signal container for the DigitalSequence library. + * + * Each signal is represented by its start level (high or low) and one or more periods. + * The output will transition to its inverse value on each period boundary. + * + * Example: A signal with n periods and HIGH start level. + * + * ``` + * ----+ +------+ +- ... -+ + * t0 | t1 | t2 | t3 | | tn - 1 + * +--------+ +----+ +-------- + * ``` + * + */ #pragma once #include -#include #include -#include +#ifdef __cplusplus +extern "C" { +#endif -typedef struct { - bool start_level; - uint32_t edge_cnt; - uint32_t edges_max_cnt; - uint32_t* edge_timings; - uint32_t* reload_reg_buff; -} DigitalSignal; +// DigitalSignal uses 10 picosecond time units (1 tick = 10 ps). +// Use the macros below to convert the time from other units. -DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt); +#define DIGITAL_SIGNAL_MS(x) ((x)*100000000UL) +#define DIGITAL_SIGNAL_US(x) ((x)*100000UL) +#define DIGITAL_SIGNAL_NS(x) ((x)*100UL) +#define DIGITAL_SIGNAL_PS(x) ((x) / 10UL) +typedef struct DigitalSignal DigitalSignal; + +/** + * @brief Allocate a DigitalSignal instance with a defined maximum size. + * + * @param[in] max_size the maximum number of periods the instance will be able to contain. + * @returns pointer to the allocated instance. + */ +DigitalSignal* digital_signal_alloc(uint32_t max_size); + +/** + * @brief Delete a previously allocated DigitalSignal instance. + * + * @param[in,out] signal pointer to the instance to be deleted. + */ void digital_signal_free(DigitalSignal* signal); -bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b); +/** + * @brief Append one period to the end of the DigitalSignal instance. + * + * @param[in,out] signal pointer to a the instance to append to. + * @param[in] ticks the period length, in 10 picosecond units. + */ +void digital_signal_add_period(DigitalSignal* signal, uint32_t ticks); -void digital_signal_prepare_arr(DigitalSignal* signal); +/** + * @brief Append one period to the end of the DigitalSignal instance, with the level specified. + * + * If the level is the same as the last level contained in the instance, then it is extened + * by the given ticks value. Otherwise, the behaviour is identical to digital_signal_add_period(). + * + * Example 1: add tc with HIGH level + * ``` + * before: + * ... ------+ + * ta | tb + * +------- + * after: + * ... ------+ +------- + * ta | tb | tc + * +------+ + * ``` + * Example 2: add tc with LOW level + * ``` + * before: + * ... ------+ + * ta | tb + * +------- + * after: + * ... ------+ + * ta | tb + tc + * +-------------- + * ``` + * + * @param[in,out] signal pointer to the instance to append to. + * @param[in] ticks the period length, in 10 picosecond units. + * @param[in] level the level to be set during the period. + */ +void digital_signal_add_period_with_level(DigitalSignal* signal, uint32_t ticks, bool level); -bool digital_signal_get_start_level(DigitalSignal* signal); +/** + * @brief Get the current start level contained in the DigitalSignal instance. + * + * If not explicitly set with digital_signal_set_start_level(), it defaults to false. + * + * @param[in] signal pointer to the instance to be queried. + * @returns the start level value. + */ +bool digital_signal_get_start_level(const DigitalSignal* signal); -uint32_t digital_signal_get_edges_cnt(DigitalSignal* signal); +/** + * @brief Set the start level contained in the DigitalSignal instance. + * + * @param[in,out] signal pointer to the instance to be modified. + * @param[in] level signal level to be set as the start level. + */ +void digital_signal_set_start_level(DigitalSignal* signal, bool level); -uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num); +/** + * @brief Get the number of periods currently stored in a DigitalSignal instance. + * + * @param[in] signal pointer to the instance to be queried. + * @return the number of periods stored in the instance. + */ +uint32_t digital_signal_get_size(const DigitalSignal* signal); -void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio); +#ifdef __cplusplus +} +#endif diff --git a/lib/digital_signal/digital_signal_i.h b/lib/digital_signal/digital_signal_i.h new file mode 100644 index 00000000000..e473c80c091 --- /dev/null +++ b/lib/digital_signal/digital_signal_i.h @@ -0,0 +1,23 @@ +/** + * @file digital_signal_i.h + * @brief DigitalSignal private definitions. + * + * This file is an implementation detail. It must not be included in + * any public API-related headers. + */ +#include +#include + +#define DIGITAL_SIGNAL_T_TIM 1562 /**< 15.625 ns *100 */ +#define DIGITAL_SIGNAL_T_TIM_DIV2 (DIGITAL_SIGNAL_T_TIM / 2) /**< 15.625 ns / 2 *100 */ + +/** + * @brief DigitalSignal structure type. + */ +struct DigitalSignal { + bool start_level; /**< The level to begin the signal with. */ + uint32_t size; /**< Current period count contained in the instance. */ + uint32_t max_size; /**< Maximum period count this instance can hold. */ + uint32_t* data; /**< Pointer to the array of time periods. */ + int32_t remainder; /**< Remainder left after converting all periods into timer ticks. */ +}; diff --git a/lib/digital_signal/presets/nfc/iso14443_3a_signal.c b/lib/digital_signal/presets/nfc/iso14443_3a_signal.c new file mode 100644 index 00000000000..1f3824e2dcd --- /dev/null +++ b/lib/digital_signal/presets/nfc/iso14443_3a_signal.c @@ -0,0 +1,139 @@ +#include "iso14443_3a_signal.h" + +#include + +#define BITS_IN_BYTE (8) + +#define ISO14443_3A_SIGNAL_BIT_MAX_EDGES (10) +#define ISO14443_3A_SIGNAL_MAX_EDGES (1350) + +#define ISO14443_3A_SIGNAL_SEQUENCE_SIZE \ + (ISO14443_3A_SIGNAL_MAX_EDGES / (ISO14443_3A_SIGNAL_BIT_MAX_EDGES - 2)) + +#define ISO14443_3A_SIGNAL_F_SIG (13560000.0) +#define ISO14443_3A_SIGNAL_T_SIG 7374 //73.746ns*100 +#define ISO14443_3A_SIGNAL_T_SIG_X8 58992 //T_SIG*8 +#define ISO14443_3A_SIGNAL_T_SIG_X8_X8 471936 //T_SIG*8*8 +#define ISO14443_3A_SIGNAL_T_SIG_X8_X9 530928 //T_SIG*8*9 + +typedef enum { + Iso14443_3aSignalIndexZero, + Iso14443_3aSignalIndexOne, + Iso14443_3aSignalIndexCount, +} Iso14443_3aSignalIndex; + +typedef DigitalSignal* Iso14443_3aSignalBank[Iso14443_3aSignalIndexCount]; + +struct Iso14443_3aSignal { + DigitalSequence* tx_sequence; + Iso14443_3aSignalBank signals; +}; + +static void iso14443_3a_signal_add_byte(Iso14443_3aSignal* instance, uint8_t byte, bool parity) { + for(size_t i = 0; i < BITS_IN_BYTE; i++) { + digital_sequence_add_signal( + instance->tx_sequence, + FURI_BIT(byte, i) ? Iso14443_3aSignalIndexOne : Iso14443_3aSignalIndexZero); + } + digital_sequence_add_signal( + instance->tx_sequence, parity ? Iso14443_3aSignalIndexOne : Iso14443_3aSignalIndexZero); +} + +static void iso14443_3a_signal_encode( + Iso14443_3aSignal* instance, + const uint8_t* tx_data, + const uint8_t* tx_parity, + size_t tx_bits) { + furi_assert(instance); + furi_assert(tx_data); + furi_assert(tx_parity); + + // Start of frame + digital_sequence_add_signal(instance->tx_sequence, Iso14443_3aSignalIndexOne); + + if(tx_bits < BITS_IN_BYTE) { + for(size_t i = 0; i < tx_bits; i++) { + digital_sequence_add_signal( + instance->tx_sequence, + FURI_BIT(tx_data[0], i) ? Iso14443_3aSignalIndexOne : Iso14443_3aSignalIndexZero); + } + } else { + for(size_t i = 0; i < tx_bits / BITS_IN_BYTE; i++) { + bool parity = FURI_BIT(tx_parity[i / BITS_IN_BYTE], i % BITS_IN_BYTE); + iso14443_3a_signal_add_byte(instance, tx_data[i], parity); + } + } +} + +static inline void iso14443_3a_signal_set_bit(DigitalSignal* signal, bool bit) { + digital_signal_set_start_level(signal, bit); + + if(bit) { + for(uint32_t i = 0; i < 7; ++i) { + digital_signal_add_period(signal, ISO14443_3A_SIGNAL_T_SIG_X8); + } + digital_signal_add_period(signal, ISO14443_3A_SIGNAL_T_SIG_X8_X9); + } else { + digital_signal_add_period(signal, ISO14443_3A_SIGNAL_T_SIG_X8_X8); + for(uint32_t i = 0; i < 8; ++i) { + digital_signal_add_period(signal, ISO14443_3A_SIGNAL_T_SIG_X8); + } + } +} + +static inline void iso14443_3a_signal_bank_fill(Iso14443_3aSignalBank bank) { + for(uint32_t i = 0; i < Iso14443_3aSignalIndexCount; ++i) { + bank[i] = digital_signal_alloc(ISO14443_3A_SIGNAL_BIT_MAX_EDGES); + iso14443_3a_signal_set_bit(bank[i], i % Iso14443_3aSignalIndexCount != 0); + } +} + +static inline void iso14443_3a_signal_bank_clear(Iso14443_3aSignalBank bank) { + for(uint32_t i = 0; i < Iso14443_3aSignalIndexCount; ++i) { + digital_signal_free(bank[i]); + } +} + +static inline void + iso14443_3a_signal_bank_register(Iso14443_3aSignalBank bank, DigitalSequence* sequence) { + for(uint32_t i = 0; i < Iso14443_3aSignalIndexCount; ++i) { + digital_sequence_register_signal(sequence, i, bank[i]); + } +} + +Iso14443_3aSignal* iso14443_3a_signal_alloc(const GpioPin* pin) { + furi_assert(pin); + + Iso14443_3aSignal* instance = malloc(sizeof(Iso14443_3aSignal)); + instance->tx_sequence = digital_sequence_alloc(ISO14443_3A_SIGNAL_SEQUENCE_SIZE, pin); + + iso14443_3a_signal_bank_fill(instance->signals); + iso14443_3a_signal_bank_register(instance->signals, instance->tx_sequence); + + return instance; +} + +void iso14443_3a_signal_free(Iso14443_3aSignal* instance) { + furi_assert(instance); + furi_assert(instance->tx_sequence); + + iso14443_3a_signal_bank_clear(instance->signals); + digital_sequence_free(instance->tx_sequence); + free(instance); +} + +void iso14443_3a_signal_tx( + Iso14443_3aSignal* instance, + const uint8_t* tx_data, + const uint8_t* tx_parity, + size_t tx_bits) { + furi_assert(instance); + furi_assert(tx_data); + furi_assert(tx_parity); + + FURI_CRITICAL_ENTER(); + digital_sequence_clear(instance->tx_sequence); + iso14443_3a_signal_encode(instance, tx_data, tx_parity, tx_bits); + digital_sequence_transmit(instance->tx_sequence); + FURI_CRITICAL_EXIT(); +} diff --git a/lib/digital_signal/presets/nfc/iso14443_3a_signal.h b/lib/digital_signal/presets/nfc/iso14443_3a_signal.h new file mode 100644 index 00000000000..73269e66c8d --- /dev/null +++ b/lib/digital_signal/presets/nfc/iso14443_3a_signal.h @@ -0,0 +1,51 @@ +/** + * @file iso14443_3a_signal.h + * @brief DigitalSequence preset for generating ISO14443-3A compliant signals. + */ +#pragma once + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Iso14443_3aSignal Iso14443_3aSignal; + +/** + * @brief Allocate an Iso14443_3aSignal instance with a set GPIO pin. + * + * @param[in] pin GPIO pin to use during transmission. + * @returns pointer to the allocated instance. + */ +Iso14443_3aSignal* iso14443_3a_signal_alloc(const GpioPin* pin); + +/** + * @brief Delete an Iso14443_3aSignal instance. + * + * @param[in,out] instance pointer to the instance to be deleted. + */ +void iso14443_3a_signal_free(Iso14443_3aSignal* instance); + +/** + * @brief Transmit arbitrary bytes using an Iso14443_3aSignal instance. + * + * This function will block until the transmisson has been completed. + * + * @param[in] instance pointer to the instance used in transmission. + * @param[in] tx_data pointer to the data to be transmitted. + * @param[in] tx_parity pointer to the bit-packed parity array. + * @param[in] tx_bits size of the data to be transmitted in bits. + */ +void iso14443_3a_signal_tx( + Iso14443_3aSignal* instance, + const uint8_t* tx_data, + const uint8_t* tx_parity, + size_t tx_bits); + +#ifdef __cplusplus +} +#endif diff --git a/lib/digital_signal/presets/nfc/iso15693_signal.c b/lib/digital_signal/presets/nfc/iso15693_signal.c new file mode 100644 index 00000000000..735a88f57aa --- /dev/null +++ b/lib/digital_signal/presets/nfc/iso15693_signal.c @@ -0,0 +1,204 @@ +#include "iso15693_signal.h" + +#include + +#define BITS_IN_BYTE (8U) + +#define ISO15693_SIGNAL_COEFF_HI (1U) +#define ISO15693_SIGNAL_COEFF_LO (4U) + +#define ISO15693_SIGNAL_ZERO_EDGES (16U) +#define ISO15693_SIGNAL_ONE_EDGES (ISO15693_SIGNAL_ZERO_EDGES + 1U) +#define ISO15693_SIGNAL_EOF_EDGES (64U) +#define ISO15693_SIGNAL_SOF_EDGES (ISO15693_SIGNAL_EOF_EDGES + 1U) +#define ISO15693_SIGNAL_EDGES (1350U) + +#define ISO15693_SIGNAL_FC (13.56e6) +#define ISO15693_SIGNAL_FC_16 (16.0e11 / ISO15693_SIGNAL_FC) +#define ISO15693_SIGNAL_FC_256 (256.0e11 / ISO15693_SIGNAL_FC) +#define ISO15693_SIGNAL_FC_768 (768.0e11 / ISO15693_SIGNAL_FC) + +typedef enum { + Iso15693SignalIndexSof, + Iso15693SignalIndexEof, + Iso15693SignalIndexOne, + Iso15693SignalIndexZero, + Iso15693SignalIndexNum, +} Iso15693SignalIndex; + +typedef DigitalSignal* Iso15693SignalBank[Iso15693SignalIndexNum]; + +struct Iso15693Signal { + DigitalSequence* tx_sequence; + Iso15693SignalBank banks[Iso15693SignalDataRateNum]; +}; + +// Add an unmodulated signal for the length of Fc / 256 * k (where k = 1 or 4) +static void iso15693_add_silence(DigitalSignal* signal, Iso15693SignalDataRate data_rate) { + const uint32_t k = data_rate == Iso15693SignalDataRateHi ? ISO15693_SIGNAL_COEFF_HI : + ISO15693_SIGNAL_COEFF_LO; + digital_signal_add_period_with_level(signal, ISO15693_SIGNAL_FC_256 * k, false); +} + +// Add 8 * k subcarrier pulses of Fc / 16 (where k = 1 or 4) +static void iso15693_add_subcarrier(DigitalSignal* signal, Iso15693SignalDataRate data_rate) { + const uint32_t k = data_rate == Iso15693SignalDataRateHi ? ISO15693_SIGNAL_COEFF_HI : + ISO15693_SIGNAL_COEFF_LO; + for(uint32_t i = 0; i < ISO15693_SIGNAL_ZERO_EDGES * k; ++i) { + digital_signal_add_period_with_level(signal, ISO15693_SIGNAL_FC_16, !(i % 2)); + } +} + +static void iso15693_add_bit(DigitalSignal* signal, Iso15693SignalDataRate data_rate, bool bit) { + if(bit) { + iso15693_add_silence(signal, data_rate); + iso15693_add_subcarrier(signal, data_rate); + } else { + iso15693_add_subcarrier(signal, data_rate); + iso15693_add_silence(signal, data_rate); + } +} + +static inline void iso15693_add_sof(DigitalSignal* signal, Iso15693SignalDataRate data_rate) { + // Not adding silence since it only increases response time + + for(uint32_t i = 0; i < ISO15693_SIGNAL_FC_768 / ISO15693_SIGNAL_FC_256; ++i) { + iso15693_add_subcarrier(signal, data_rate); + } + + iso15693_add_bit(signal, data_rate, true); +} + +static inline void iso15693_add_eof(DigitalSignal* signal, Iso15693SignalDataRate data_rate) { + iso15693_add_bit(signal, data_rate, false); + + for(uint32_t i = 0; i < ISO15693_SIGNAL_FC_768 / ISO15693_SIGNAL_FC_256; ++i) { + iso15693_add_subcarrier(signal, data_rate); + } + + // Not adding silence since it does nothing here +} + +static inline uint32_t + iso15693_get_sequence_index(Iso15693SignalIndex index, Iso15693SignalDataRate data_rate) { + return index + data_rate * Iso15693SignalIndexNum; +} + +static inline void + iso15693_add_byte(Iso15693Signal* instance, Iso15693SignalDataRate data_rate, uint8_t byte) { + for(size_t i = 0; i < BITS_IN_BYTE; i++) { + const uint8_t bit = byte & (1U << i); + digital_sequence_add_signal( + instance->tx_sequence, + iso15693_get_sequence_index( + bit ? Iso15693SignalIndexOne : Iso15693SignalIndexZero, data_rate)); + } +} + +static inline void iso15693_signal_encode( + Iso15693Signal* instance, + Iso15693SignalDataRate data_rate, + const uint8_t* tx_data, + size_t tx_data_size) { + digital_sequence_add_signal( + instance->tx_sequence, iso15693_get_sequence_index(Iso15693SignalIndexSof, data_rate)); + + for(size_t i = 0; i < tx_data_size; i++) { + iso15693_add_byte(instance, data_rate, tx_data[i]); + } + + digital_sequence_add_signal( + instance->tx_sequence, iso15693_get_sequence_index(Iso15693SignalIndexEof, data_rate)); +} + +static void iso15693_signal_bank_fill(Iso15693Signal* instance, Iso15693SignalDataRate data_rate) { + const uint32_t k = data_rate == Iso15693SignalDataRateHi ? ISO15693_SIGNAL_COEFF_HI : + ISO15693_SIGNAL_COEFF_LO; + DigitalSignal** bank = instance->banks[data_rate]; + + bank[Iso15693SignalIndexSof] = digital_signal_alloc(ISO15693_SIGNAL_SOF_EDGES * k); + bank[Iso15693SignalIndexEof] = digital_signal_alloc(ISO15693_SIGNAL_EOF_EDGES * k); + bank[Iso15693SignalIndexOne] = digital_signal_alloc(ISO15693_SIGNAL_ONE_EDGES * k); + bank[Iso15693SignalIndexZero] = digital_signal_alloc(ISO15693_SIGNAL_ZERO_EDGES * k); + + iso15693_add_sof(bank[Iso15693SignalIndexSof], data_rate); + iso15693_add_eof(bank[Iso15693SignalIndexEof], data_rate); + iso15693_add_bit(bank[Iso15693SignalIndexOne], data_rate, true); + iso15693_add_bit(bank[Iso15693SignalIndexZero], data_rate, false); +} + +static void + iso15693_signal_bank_clear(Iso15693Signal* instance, Iso15693SignalDataRate data_rate) { + DigitalSignal** bank = instance->banks[data_rate]; + + for(uint32_t i = 0; i < Iso15693SignalIndexNum; ++i) { + digital_signal_free(bank[i]); + } +} + +static void + iso15693_signal_bank_register(Iso15693Signal* instance, Iso15693SignalDataRate data_rate) { + for(uint32_t i = 0; i < Iso15693SignalIndexNum; ++i) { + digital_sequence_register_signal( + instance->tx_sequence, + iso15693_get_sequence_index(i, data_rate), + instance->banks[data_rate][i]); + } +} + +Iso15693Signal* iso15693_signal_alloc(const GpioPin* pin) { + furi_assert(pin); + + Iso15693Signal* instance = malloc(sizeof(Iso15693Signal)); + + instance->tx_sequence = digital_sequence_alloc(BITS_IN_BYTE * 255 + 2, pin); + + for(uint32_t i = 0; i < Iso15693SignalDataRateNum; ++i) { + iso15693_signal_bank_fill(instance, i); + iso15693_signal_bank_register(instance, i); + } + + return instance; +} + +void iso15693_signal_free(Iso15693Signal* instance) { + furi_assert(instance); + + digital_sequence_free(instance->tx_sequence); + + for(uint32_t i = 0; i < Iso15693SignalDataRateNum; ++i) { + iso15693_signal_bank_clear(instance, i); + } + + free(instance); +} + +void iso15693_signal_tx( + Iso15693Signal* instance, + Iso15693SignalDataRate data_rate, + const uint8_t* tx_data, + size_t tx_data_size) { + furi_assert(instance); + furi_assert(data_rate < Iso15693SignalDataRateNum); + furi_assert(tx_data); + + FURI_CRITICAL_ENTER(); + digital_sequence_clear(instance->tx_sequence); + iso15693_signal_encode(instance, data_rate, tx_data, tx_data_size); + digital_sequence_transmit(instance->tx_sequence); + + FURI_CRITICAL_EXIT(); +} + +void iso15693_signal_tx_sof(Iso15693Signal* instance, Iso15693SignalDataRate data_rate) { + furi_assert(instance); + furi_assert(data_rate < Iso15693SignalDataRateNum); + + FURI_CRITICAL_ENTER(); + digital_sequence_clear(instance->tx_sequence); + digital_sequence_add_signal( + instance->tx_sequence, iso15693_get_sequence_index(Iso15693SignalIndexSof, data_rate)); + digital_sequence_transmit(instance->tx_sequence); + + FURI_CRITICAL_EXIT(); +} diff --git a/lib/digital_signal/presets/nfc/iso15693_signal.h b/lib/digital_signal/presets/nfc/iso15693_signal.h new file mode 100644 index 00000000000..8219cff157e --- /dev/null +++ b/lib/digital_signal/presets/nfc/iso15693_signal.h @@ -0,0 +1,73 @@ +/** + * @file iso15693_signal.h + * @brief DigitalSequence preset for generating ISO15693-compliant signals. + * + */ +#pragma once + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Iso15693Signal Iso15693Signal; + +/** + * @brief Supported data rates. + */ +typedef enum { + Iso15693SignalDataRateHi, /**< High data rate. */ + Iso15693SignalDataRateLo, /**< Low data rate. */ + Iso15693SignalDataRateNum, /**< Data rate mode count. Internal use. */ +} Iso15693SignalDataRate; + +/** + * @brief Allocate an Iso15693Signal instance with a set GPIO pin. + * + * @param[in] pin GPIO pin to use during transmission. + * @returns pointer to the allocated instance. + */ +Iso15693Signal* iso15693_signal_alloc(const GpioPin* pin); + +/** + * @brief Delete an Iso15693Signal instance. + * + * @param[in,out] instance pointer to the instance to be deleted. + */ +void iso15693_signal_free(Iso15693Signal* instance); + +/** + * @brief Transmit arbitrary bytes using an Iso15693Signal instance. + * @see Iso15693SignalDataRate + * + * This function will block until the transmisson has been completed. + * + * @param[in] instance pointer to the instance used in transmission. + * @param[in] data_rate data rate to transmit at. + * @param[in] tx_data pointer to the data to be transmitted. + * @param[in] tx_data_size size of the data to be transmitted in bytes. + */ +void iso15693_signal_tx( + Iso15693Signal* instance, + Iso15693SignalDataRate data_rate, + const uint8_t* tx_data, + size_t tx_data_size); + +/** + * @brief Transmit Start of Frame using an Iso15693Signal instance. + * @see Iso15693SignalDataRate + * + * This function will block until the transmisson has been completed. + * + * @param[in] instance pointer to the instance used in transmission. + * @param[in] data_rate data rate to transmit at. + */ +void iso15693_signal_tx_sof(Iso15693Signal* instance, Iso15693SignalDataRate data_rate); + +#ifdef __cplusplus +} +#endif diff --git a/lib/drivers/SConscript b/lib/drivers/SConscript index 3b7ee2401e6..a790d54a7a4 100644 --- a/lib/drivers/SConscript +++ b/lib/drivers/SConscript @@ -4,6 +4,14 @@ env.Append( CPPPATH=[ "#/lib/drivers", ], + SDK_HEADERS=[ + File("cc1101_regs.h"), + File("st25r3916_reg.h"), + File("st25r3916.h"), + ], + LINT_SOURCES=[ + Dir("."), + ], ) diff --git a/lib/drivers/bq25896.c b/lib/drivers/bq25896.c index 73135d93a27..76aae5e8236 100644 --- a/lib/drivers/bq25896.c +++ b/lib/drivers/bq25896.c @@ -1,5 +1,4 @@ #include "bq25896.h" -#include "bq25896_reg.h" #include @@ -36,13 +35,15 @@ typedef struct { static bq25896_regs_t bq25896_regs; -void bq25896_init(FuriHalI2cBusHandle* handle) { +bool bq25896_init(FuriHalI2cBusHandle* handle) { + bool result = true; + bq25896_regs.r14.REG_RST = 1; - furi_hal_i2c_write_reg_8( + result &= furi_hal_i2c_write_reg_8( handle, BQ25896_ADDRESS, 0x14, *(uint8_t*)&bq25896_regs.r14, BQ25896_I2C_TIMEOUT); // Readout all registers - furi_hal_i2c_read_mem( + result &= furi_hal_i2c_read_mem( handle, BQ25896_ADDRESS, 0x00, @@ -53,26 +54,34 @@ void bq25896_init(FuriHalI2cBusHandle* handle) { // Poll ADC forever bq25896_regs.r02.CONV_START = 1; bq25896_regs.r02.CONV_RATE = 1; - furi_hal_i2c_write_reg_8( + result &= furi_hal_i2c_write_reg_8( handle, BQ25896_ADDRESS, 0x02, *(uint8_t*)&bq25896_regs.r02, BQ25896_I2C_TIMEOUT); bq25896_regs.r07.WATCHDOG = WatchdogDisable; - furi_hal_i2c_write_reg_8( + result &= furi_hal_i2c_write_reg_8( handle, BQ25896_ADDRESS, 0x07, *(uint8_t*)&bq25896_regs.r07, BQ25896_I2C_TIMEOUT); // OTG power configuration bq25896_regs.r0A.BOOSTV = 0x8; // BOOST Voltage: 5.062V - bq25896_regs.r0A.BOOST_LIM = BOOST_LIM_1400; // BOOST Current limit: 1.4A - furi_hal_i2c_write_reg_8( + bq25896_regs.r0A.BOOST_LIM = BoostLim_1400; // BOOST Current limit: 1.4A + result &= furi_hal_i2c_write_reg_8( handle, BQ25896_ADDRESS, 0x0A, *(uint8_t*)&bq25896_regs.r0A, BQ25896_I2C_TIMEOUT); - furi_hal_i2c_read_mem( + result &= furi_hal_i2c_read_mem( handle, BQ25896_ADDRESS, 0x00, (uint8_t*)&bq25896_regs, sizeof(bq25896_regs), BQ25896_I2C_TIMEOUT); + + return result; +} + +void bq25896_set_boost_lim(FuriHalI2cBusHandle* handle, BoostLim boost_lim) { + bq25896_regs.r0A.BOOST_LIM = boost_lim; + furi_hal_i2c_write_reg_8( + handle, BQ25896_ADDRESS, 0x0A, *(uint8_t*)&bq25896_regs.r0A, BQ25896_I2C_TIMEOUT); } void bq25896_poweroff(FuriHalI2cBusHandle* handle) { @@ -81,7 +90,7 @@ void bq25896_poweroff(FuriHalI2cBusHandle* handle) { handle, BQ25896_ADDRESS, 0x09, *(uint8_t*)&bq25896_regs.r09, BQ25896_I2C_TIMEOUT); } -bool bq25896_is_charging(FuriHalI2cBusHandle* handle) { +ChrgStat bq25896_get_charge_status(FuriHalI2cBusHandle* handle) { furi_hal_i2c_read_mem( handle, BQ25896_ADDRESS, @@ -91,7 +100,16 @@ bool bq25896_is_charging(FuriHalI2cBusHandle* handle) { BQ25896_I2C_TIMEOUT); furi_hal_i2c_read_reg_8( handle, BQ25896_ADDRESS, 0x0B, (uint8_t*)&bq25896_regs.r0B, BQ25896_I2C_TIMEOUT); - return bq25896_regs.r0B.CHRG_STAT != ChrgStatNo; + return bq25896_regs.r0B.CHRG_STAT; +} + +bool bq25896_is_charging(FuriHalI2cBusHandle* handle) { + // Include precharge, fast charging, and charging termination done as "charging" + return bq25896_get_charge_status(handle) != ChrgStatNo; +} + +bool bq25896_is_charging_done(FuriHalI2cBusHandle* handle) { + return bq25896_get_charge_status(handle) == ChrgStatDone; } void bq25896_enable_charging(FuriHalI2cBusHandle* handle) { @@ -124,6 +142,30 @@ bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle) { return bq25896_regs.r03.OTG_CONFIG; } +uint16_t bq25896_get_vreg_voltage(FuriHalI2cBusHandle* handle) { + furi_hal_i2c_read_reg_8( + handle, BQ25896_ADDRESS, 0x06, (uint8_t*)&bq25896_regs.r06, BQ25896_I2C_TIMEOUT); + return (uint16_t)bq25896_regs.r06.VREG * 16 + 3840; +} + +void bq25896_set_vreg_voltage(FuriHalI2cBusHandle* handle, uint16_t vreg_voltage) { + if(vreg_voltage < 3840) { + // Minimum valid value is 3840 mV + vreg_voltage = 3840; + } else if(vreg_voltage > 4208) { + // Maximum safe value is 4208 mV + vreg_voltage = 4208; + } + + // 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( + handle, BQ25896_ADDRESS, 0x06, *(uint8_t*)&bq25896_regs.r06, BQ25896_I2C_TIMEOUT); +} + bool bq25896_check_otg_fault(FuriHalI2cBusHandle* handle) { furi_hal_i2c_read_reg_8( handle, BQ25896_ADDRESS, 0x0C, (uint8_t*)&bq25896_regs.r0C, BQ25896_I2C_TIMEOUT); diff --git a/lib/drivers/bq25896.h b/lib/drivers/bq25896.h index 39d343c3385..d35625ab3f5 100644 --- a/lib/drivers/bq25896.h +++ b/lib/drivers/bq25896.h @@ -1,18 +1,29 @@ #pragma once +#include "bq25896_reg.h" + #include #include #include /** Initialize Driver */ -void bq25896_init(FuriHalI2cBusHandle* handle); +bool bq25896_init(FuriHalI2cBusHandle* handle); + +/** Set boost lim*/ +void bq25896_set_boost_lim(FuriHalI2cBusHandle* handle, BoostLim boost_lim); /** Send device into shipping mode */ void bq25896_poweroff(FuriHalI2cBusHandle* handle); +/** Get charging status */ +ChrgStat bq25896_get_charge_status(FuriHalI2cBusHandle* handle); + /** Is currently charging */ bool bq25896_is_charging(FuriHalI2cBusHandle* handle); +/** Is charging completed while connected to charger */ +bool bq25896_is_charging_done(FuriHalI2cBusHandle* handle); + /** Enable charging */ void bq25896_enable_charging(FuriHalI2cBusHandle* handle); @@ -28,6 +39,15 @@ void bq25896_disable_otg(FuriHalI2cBusHandle* handle); /** Is otg enabled */ bool bq25896_is_otg_enabled(FuriHalI2cBusHandle* handle); +/** Get VREG (charging limit) voltage in mV */ +uint16_t bq25896_get_vreg_voltage(FuriHalI2cBusHandle* handle); + +/** Set VREG (charging limit) voltage in mV + * + * Valid range: 3840mV - 4208mV, in steps of 16mV + */ +void bq25896_set_vreg_voltage(FuriHalI2cBusHandle* handle, uint16_t vreg_voltage); + /** Check OTG BOOST Fault status */ bool bq25896_check_otg_fault(FuriHalI2cBusHandle* handle); diff --git a/lib/drivers/bq25896_reg.h b/lib/drivers/bq25896_reg.h index 3cb3d71402d..a6ca3e1c77e 100644 --- a/lib/drivers/bq25896_reg.h +++ b/lib/drivers/bq25896_reg.h @@ -159,14 +159,16 @@ typedef struct { #define BOOSTV_128 (1 << 1) #define BOOSTV_64 (1 << 0) -#define BOOST_LIM_500 (0b000) -#define BOOST_LIM_750 (0b001) -#define BOOST_LIM_1200 (0b010) -#define BOOST_LIM_1400 (0b011) -#define BOOST_LIM_1650 (0b100) -#define BOOST_LIM_1875 (0b101) -#define BOOST_LIM_2150 (0b110) -#define BOOST_LIM_RSVD (0b111) +typedef enum { + BoostLim_500 = 0b000, + BoostLim_750 = 0b001, + BoostLim_1200 = 0b010, + BoostLim_1400 = 0b011, + BoostLim_1650 = 0b100, + BoostLim_1875 = 0b101, + BoostLim_2150 = 0b110, + BoostLim_Rsvd = 0b111, +} BoostLim; typedef struct { uint8_t BOOST_LIM : 3; // Boost Mode Current Limit diff --git a/lib/drivers/bq27220.c b/lib/drivers/bq27220.c index f64120fa83e..a3a88603d72 100644 --- a/lib/drivers/bq27220.c +++ b/lib/drivers/bq27220.c @@ -1,12 +1,16 @@ + #include "bq27220.h" #include "bq27220_reg.h" +#include "bq27220_data_memory.h" + +_Static_assert(sizeof(BQ27220DMGaugingConfig) == 2, "Incorrect structure size"); #include #include #define TAG "Gauge" -uint16_t bq27220_read_word(FuriHalI2cBusHandle* handle, uint8_t address) { +static uint16_t bq27220_read_word(FuriHalI2cBusHandle* handle, uint8_t address) { uint16_t buf = 0; furi_hal_i2c_read_mem( @@ -15,14 +19,14 @@ uint16_t bq27220_read_word(FuriHalI2cBusHandle* handle, uint8_t address) { return buf; } -bool bq27220_control(FuriHalI2cBusHandle* handle, uint16_t control) { +static bool bq27220_control(FuriHalI2cBusHandle* handle, uint16_t control) { bool ret = furi_hal_i2c_write_mem( handle, BQ27220_ADDRESS, CommandControl, (uint8_t*)&control, 2, BQ27220_I2C_TIMEOUT); return ret; } -uint8_t bq27220_get_checksum(uint8_t* data, uint16_t len) { +static uint8_t bq27220_get_checksum(uint8_t* data, uint16_t len) { uint8_t ret = 0; for(uint16_t i = 0; i < len; i++) { ret += data[i]; @@ -30,80 +34,188 @@ uint8_t bq27220_get_checksum(uint8_t* data, uint16_t len) { return 0xFF - ret; } -bool bq27220_set_parameter_u16(FuriHalI2cBusHandle* handle, uint16_t address, uint16_t value) { - bool ret; - uint8_t buffer[4]; +static bool bq27220_parameter_check( + FuriHalI2cBusHandle* handle, + uint16_t address, + uint32_t value, + size_t size, + bool update) { + furi_assert(size == 1 || size == 2 || size == 4); + bool ret = false; + uint8_t buffer[6] = {0}; + uint8_t old_data[4] = {0}; + + do { + buffer[0] = address & 0xFF; + buffer[1] = (address >> 8) & 0xFF; + + for(size_t i = 0; i < size; i++) { + buffer[1 + size - i] = (value >> (i * 8)) & 0xFF; + } + + if(update) { + // Datasheet contains incorrect procedure for memory update, more info: + // https://e2e.ti.com/support/power-management-group/power-management/f/power-management-forum/719878/bq27220-technical-reference-manual-sluubd4-is-missing-extended-data-commands-chapter + + // 2. Write the address AND the parameter data to 0x3E+ (auto increment) + if(!furi_hal_i2c_write_mem( + handle, + BQ27220_ADDRESS, + CommandSelectSubclass, + buffer, + size + 2, + BQ27220_I2C_TIMEOUT)) { + FURI_LOG_I(TAG, "DM write failed"); + break; + } + + furi_delay_us(10000); + + // 3. Calculate the check sum: 0xFF - (sum of address and data) OR 0xFF + uint8_t checksum = bq27220_get_checksum(buffer, size + 2); + // 4. Write the check sum to 0x60 and the total length of (address + parameter data + check sum + length) to 0x61 + buffer[0] = checksum; + // 2 bytes address, `size` bytes data, 1 byte check sum, 1 byte length + buffer[1] = 2 + size + 1 + 1; + if(!furi_hal_i2c_write_mem( + handle, BQ27220_ADDRESS, CommandMACDataSum, buffer, 2, BQ27220_I2C_TIMEOUT)) { + FURI_LOG_I(TAG, "CRC write failed"); + break; + } - buffer[0] = address & 0xFF; - buffer[1] = (address >> 8) & 0xFF; - buffer[2] = (value >> 8) & 0xFF; - buffer[3] = value & 0xFF; - ret = furi_hal_i2c_write_mem( - handle, BQ27220_ADDRESS, CommandSelectSubclass, buffer, 4, BQ27220_I2C_TIMEOUT); + furi_delay_us(10000); + ret = true; + } else { + if(!furi_hal_i2c_write_mem( + handle, BQ27220_ADDRESS, CommandSelectSubclass, buffer, 2, BQ27220_I2C_TIMEOUT)) { + FURI_LOG_I(TAG, "DM SelectSubclass for read failed"); + break; + } - furi_delay_us(10000); + if(!furi_hal_i2c_rx(handle, BQ27220_ADDRESS, old_data, size, BQ27220_I2C_TIMEOUT)) { + FURI_LOG_I(TAG, "DM read failed"); + break; + } - uint8_t checksum = bq27220_get_checksum(buffer, 4); - buffer[0] = checksum; - buffer[1] = 6; - ret &= furi_hal_i2c_write_mem( - handle, BQ27220_ADDRESS, CommandMACDataSum, buffer, 2, BQ27220_I2C_TIMEOUT); + if(*(uint32_t*)&(old_data[0]) != *(uint32_t*)&(buffer[2])) { + FURI_LOG_W( //-V641 + TAG, + "Data at 0x%04x(%zu): 0x%08lx!=0x%08lx", + address, + size, + *(uint32_t*)&(old_data[0]), + *(uint32_t*)&(buffer[2])); + } else { + ret = true; + } + } + } while(0); - furi_delay_us(10000); return ret; } -bool bq27220_init(FuriHalI2cBusHandle* handle, const ParamCEDV* cedv) { - uint32_t timeout = 100; - uint16_t design_cap = bq27220_get_design_capacity(handle); - if(cedv->design_cap == design_cap) { - FURI_LOG_I(TAG, "Skip battery profile update"); - return true; +static bool bq27220_data_memory_check( + FuriHalI2cBusHandle* handle, + const BQ27220DMData* data_memory, + bool update) { + if(update) { + if(!bq27220_control(handle, Control_ENTER_CFG_UPDATE)) { + FURI_LOG_E(TAG, "ENTER_CFG_UPDATE command failed"); + return false; + }; + + // Wait for enter CFG update mode + uint32_t timeout = 100; + OperationStatus status = {0}; + while((status.CFGUPDATE != true) && (timeout-- > 0)) { + bq27220_get_operation_status(handle, &status); + } + + if(timeout == 0) { + FURI_LOG_E(TAG, "CFGUPDATE mode failed"); + return false; + } } - FURI_LOG_I(TAG, "Start updating battery profile"); - OperationStatus status = {0}; - if(!bq27220_control(handle, Control_ENTER_CFG_UPDATE)) { - FURI_LOG_E(TAG, "Can't configure update"); - return false; - }; - while((status.CFGUPDATE != true) && (timeout-- > 0)) { - bq27220_get_operation_status(handle, &status); + // Process data memory records + bool result = true; + while(data_memory->type != BQ27220DMTypeEnd) { + if(data_memory->type == BQ27220DMTypeWait) { + furi_delay_us(data_memory->value.u32); + } else if(data_memory->type == BQ27220DMTypeU8) { + result &= bq27220_parameter_check( + handle, data_memory->address, data_memory->value.u8, 1, update); + } else if(data_memory->type == BQ27220DMTypeU16) { + result &= bq27220_parameter_check( + handle, data_memory->address, data_memory->value.u16, 2, update); + } else if(data_memory->type == BQ27220DMTypeU32) { + result &= bq27220_parameter_check( + handle, data_memory->address, data_memory->value.u32, 4, update); + } else if(data_memory->type == BQ27220DMTypeI8) { + result &= bq27220_parameter_check( + handle, data_memory->address, data_memory->value.i8, 1, update); + } else if(data_memory->type == BQ27220DMTypeI16) { + result &= bq27220_parameter_check( + handle, data_memory->address, data_memory->value.i16, 2, update); + } else if(data_memory->type == BQ27220DMTypeI32) { + result &= bq27220_parameter_check( + handle, data_memory->address, data_memory->value.i32, 4, update); + } else if(data_memory->type == BQ27220DMTypeF32) { + result &= bq27220_parameter_check( + handle, data_memory->address, data_memory->value.u32, 4, update); + } else if(data_memory->type == BQ27220DMTypePtr8) { + result &= bq27220_parameter_check( + handle, data_memory->address, *(uint8_t*)data_memory->value.u32, 1, update); + } else if(data_memory->type == BQ27220DMTypePtr16) { + result &= bq27220_parameter_check( + handle, data_memory->address, *(uint16_t*)data_memory->value.u32, 2, update); + } else if(data_memory->type == BQ27220DMTypePtr32) { + result &= bq27220_parameter_check( + handle, data_memory->address, *(uint32_t*)data_memory->value.u32, 4, update); + } else { + furi_crash("Invalid DM Type"); + } + data_memory++; } - bq27220_set_parameter_u16(handle, AddressGaugingConfig, cedv->cedv_conf.gauge_conf_raw); - bq27220_set_parameter_u16(handle, AddressFullChargeCapacity, cedv->full_charge_cap); - bq27220_set_parameter_u16(handle, AddressDesignCapacity, cedv->design_cap); - bq27220_set_parameter_u16(handle, AddressEMF, cedv->EMF); - bq27220_set_parameter_u16(handle, AddressC0, cedv->C0); - bq27220_set_parameter_u16(handle, AddressR0, cedv->R0); - bq27220_set_parameter_u16(handle, AddressT0, cedv->T0); - bq27220_set_parameter_u16(handle, AddressR1, cedv->R1); - bq27220_set_parameter_u16(handle, AddressTC, (cedv->TC) << 8 | cedv->C1); - bq27220_set_parameter_u16(handle, AddressStartDOD0, cedv->DOD0); - bq27220_set_parameter_u16(handle, AddressStartDOD10, cedv->DOD10); - bq27220_set_parameter_u16(handle, AddressStartDOD20, cedv->DOD20); - bq27220_set_parameter_u16(handle, AddressStartDOD30, cedv->DOD30); - bq27220_set_parameter_u16(handle, AddressStartDOD40, cedv->DOD40); - bq27220_set_parameter_u16(handle, AddressStartDOD50, cedv->DOD40); - bq27220_set_parameter_u16(handle, AddressStartDOD60, cedv->DOD60); - bq27220_set_parameter_u16(handle, AddressStartDOD70, cedv->DOD70); - bq27220_set_parameter_u16(handle, AddressStartDOD80, cedv->DOD80); - bq27220_set_parameter_u16(handle, AddressStartDOD90, cedv->DOD90); - bq27220_set_parameter_u16(handle, AddressStartDOD100, cedv->DOD100); - bq27220_set_parameter_u16(handle, AddressEDV0, cedv->EDV0); - bq27220_set_parameter_u16(handle, AddressEDV1, cedv->EDV1); - bq27220_set_parameter_u16(handle, AddressEDV2, cedv->EDV2); - - bq27220_control(handle, Control_EXIT_CFG_UPDATE_REINIT); - furi_delay_us(10000); - design_cap = bq27220_get_design_capacity(handle); - if(cedv->design_cap == design_cap) { - FURI_LOG_I(TAG, "Battery profile update success"); - return true; - } else { - FURI_LOG_E(TAG, "Battery profile update failed"); + + // Finalize configuration update + if(update) { + bq27220_control(handle, Control_EXIT_CFG_UPDATE_REINIT); + furi_delay_us(10000); + } + + return result; +} + +bool bq27220_init(FuriHalI2cBusHandle* handle) { + // Request device number(chip PN) + if(!bq27220_control(handle, Control_DEVICE_NUMBER)) { + FURI_LOG_E(TAG, "Device is not present"); return false; + }; + // Check control response + uint16_t data = 0; + data = bq27220_read_word(handle, CommandControl); + if(data != 0xFF00) { + FURI_LOG_E(TAG, "Invalid control response: %x", data); + return false; + }; + + data = bq27220_read_word(handle, CommandMACData); + FURI_LOG_I(TAG, "Device Number %04x", data); + + return data == 0x0220; +} + +bool bq27220_apply_data_memory(FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory) { + FURI_LOG_I(TAG, "Verifying data memory"); + if(!bq27220_data_memory_check(handle, data_memory, false)) { + FURI_LOG_I(TAG, "Updating data memory"); + bq27220_data_memory_check(handle, data_memory, true); } + FURI_LOG_I(TAG, "Data memory verification complete"); + + return true; } uint16_t bq27220_get_voltage(FuriHalI2cBusHandle* handle) { @@ -114,24 +226,23 @@ int16_t bq27220_get_current(FuriHalI2cBusHandle* handle) { return bq27220_read_word(handle, CommandCurrent); } -uint8_t bq27220_get_battery_status(FuriHalI2cBusHandle* handle, BatteryStatus* battery_status) { +bool bq27220_get_battery_status(FuriHalI2cBusHandle* handle, BatteryStatus* battery_status) { uint16_t data = bq27220_read_word(handle, CommandBatteryStatus); if(data == BQ27220_ERROR) { - return BQ27220_ERROR; + return false; } else { *(uint16_t*)battery_status = data; - return BQ27220_SUCCESS; + return true; } } -uint8_t - bq27220_get_operation_status(FuriHalI2cBusHandle* handle, OperationStatus* operation_status) { +bool bq27220_get_operation_status(FuriHalI2cBusHandle* handle, OperationStatus* operation_status) { uint16_t data = bq27220_read_word(handle, CommandOperationStatus); if(data == BQ27220_ERROR) { - return BQ27220_ERROR; + return false; } else { *(uint16_t*)operation_status = data; - return BQ27220_SUCCESS; + return true; } } diff --git a/lib/drivers/bq27220.h b/lib/drivers/bq27220.h index c822301a479..ca9e0312e87 100644 --- a/lib/drivers/bq27220.h +++ b/lib/drivers/bq27220.h @@ -47,60 +47,17 @@ typedef struct { _Static_assert(sizeof(OperationStatus) == 2, "Incorrect structure size"); -typedef struct { - // Low byte, Low bit first - bool CCT : 1; - bool CSYNC : 1; - bool RSVD0 : 1; - bool EDV_CMP : 1; - bool SC : 1; - bool FIXED_EDV0 : 1; - uint8_t RSVD1 : 2; - // High byte, Low bit first - bool FCC_LIM : 1; - bool RSVD2 : 1; - bool FC_FOR_VDQ : 1; - bool IGNORE_SD : 1; - bool SME0 : 1; - uint8_t RSVD3 : 3; -} GaugingConfig; - -_Static_assert(sizeof(GaugingConfig) == 2, "Incorrect structure size"); +typedef struct BQ27220DMData BQ27220DMData; -typedef struct { - union { - GaugingConfig gauge_conf; - uint16_t gauge_conf_raw; - } cedv_conf; - uint16_t full_charge_cap; - uint16_t design_cap; - uint16_t EDV0; - uint16_t EDV1; - uint16_t EDV2; - uint16_t EMF; - uint16_t C0; - uint16_t R0; - uint16_t T0; - uint16_t R1; - uint8_t TC; - uint8_t C1; - uint16_t DOD0; - uint16_t DOD10; - uint16_t DOD20; - uint16_t DOD30; - uint16_t DOD40; - uint16_t DOD50; - uint16_t DOD60; - uint16_t DOD70; - uint16_t DOD80; - uint16_t DOD90; - uint16_t DOD100; -} ParamCEDV; +/** Initialize Driver + * @return true on success, false otherwise + */ +bool bq27220_init(FuriHalI2cBusHandle* handle); /** Initialize Driver * @return true on success, false otherwise */ -bool bq27220_init(FuriHalI2cBusHandle* handle, const ParamCEDV* cedv); +bool bq27220_apply_data_memory(FuriHalI2cBusHandle* handle, const BQ27220DMData* data_memory); /** Get battery voltage in mV or error */ uint16_t bq27220_get_voltage(FuriHalI2cBusHandle* handle); @@ -109,11 +66,10 @@ uint16_t bq27220_get_voltage(FuriHalI2cBusHandle* handle); int16_t bq27220_get_current(FuriHalI2cBusHandle* handle); /** Get battery status */ -uint8_t bq27220_get_battery_status(FuriHalI2cBusHandle* handle, BatteryStatus* battery_status); +bool bq27220_get_battery_status(FuriHalI2cBusHandle* handle, BatteryStatus* battery_status); /** Get operation status */ -uint8_t - bq27220_get_operation_status(FuriHalI2cBusHandle* handle, OperationStatus* operation_status); +bool bq27220_get_operation_status(FuriHalI2cBusHandle* handle, OperationStatus* operation_status); /** Get temperature in units of 0.1°K */ uint16_t bq27220_get_temperature(FuriHalI2cBusHandle* handle); diff --git a/lib/drivers/bq27220_data_memory.h b/lib/drivers/bq27220_data_memory.h new file mode 100644 index 00000000000..ae00be88360 --- /dev/null +++ b/lib/drivers/bq27220_data_memory.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include + +typedef enum { + BQ27220DMTypeEnd, + BQ27220DMTypeWait, + BQ27220DMTypeU8, + BQ27220DMTypeU16, + BQ27220DMTypeU32, + BQ27220DMTypeI8, + BQ27220DMTypeI16, + BQ27220DMTypeI32, + BQ27220DMTypeF32, + BQ27220DMTypePtr8, + BQ27220DMTypePtr16, + BQ27220DMTypePtr32, +} BQ27220DMType; + +typedef enum { + BQ27220DMAddressGasGaugingCEDVProfile1GaugingConfig = 0x929B, + BQ27220DMAddressGasGaugingCEDVProfile1FullChargeCapacity = 0x929D, + BQ27220DMAddressGasGaugingCEDVProfile1DesignCapacity = 0x929F, + BQ27220DMAddressGasGaugingCEDVProfile1EMF = 0x92A3, + BQ27220DMAddressGasGaugingCEDVProfile1C0 = 0x92A9, + BQ27220DMAddressGasGaugingCEDVProfile1R0 = 0x92AB, + BQ27220DMAddressGasGaugingCEDVProfile1T0 = 0x92AD, + BQ27220DMAddressGasGaugingCEDVProfile1R1 = 0x92AF, + BQ27220DMAddressGasGaugingCEDVProfile1TC = 0x92B1, + BQ27220DMAddressGasGaugingCEDVProfile1C1 = 0x92B2, + BQ27220DMAddressGasGaugingCEDVProfile1EDV0 = 0x92B4, + BQ27220DMAddressGasGaugingCEDVProfile1EDV1 = 0x92B7, + BQ27220DMAddressGasGaugingCEDVProfile1EDV2 = 0x92BA, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD0 = 0x92BD, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD10 = 0x92BF, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD20 = 0x92C1, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD30 = 0x92C3, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD40 = 0x92C5, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD50 = 0x92C7, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD60 = 0x92C9, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD70 = 0x92CB, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD80 = 0x92CD, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD90 = 0x92CF, + BQ27220DMAddressGasGaugingCEDVProfile1StartDOD100 = 0x92D1, + BQ27220DMAddressCalibrationCurrentDeadband = 0x91DE, + BQ27220DMAddressConfigurationPowerSleepCurrent = 0x9217, + BQ27220DMAddressConfigurationCurrentThresholdsDischargeDetectionThreshold = 0x9228, + BQ27220DMAddressConfigurationDataInitialStandby = 0x923C, +} BQ27220DMAddress; + +typedef struct BQ27220DMData BQ27220DMData; + +struct BQ27220DMData { + uint16_t type; + uint16_t address; + union { + uint8_t u8; + uint16_t u16; + uint32_t u32; + int8_t i8; + int16_t i16; + int32_t i32; + float f32; + } value; +}; + +typedef struct { + // Low byte, Low bit first + const bool CCT : 1; + const bool CSYNC : 1; + const bool RSVD0 : 1; + const bool EDV_CMP : 1; + const bool SC : 1; + const bool FIXED_EDV0 : 1; + const uint8_t RSVD1 : 2; + // High byte, Low bit first + const bool FCC_LIM : 1; + const bool RSVD2 : 1; + const bool FC_FOR_VDQ : 1; + const bool IGNORE_SD : 1; + const bool SME0 : 1; + const uint8_t RSVD3 : 3; +} BQ27220DMGaugingConfig; diff --git a/lib/drivers/bq27220_reg.h b/lib/drivers/bq27220_reg.h index fa81b66eb4a..2e6e54aabef 100644 --- a/lib/drivers/bq27220_reg.h +++ b/lib/drivers/bq27220_reg.h @@ -66,28 +66,3 @@ #define Control_EXIT_CFG_UPDATE_REINIT 0x0091 #define Control_EXIT_CFG_UPDATE 0x0092 #define Control_RETURN_TO_ROM 0x0F00 - -#define AddressGaugingConfig 0x929B -#define AddressFullChargeCapacity 0x929D -#define AddressDesignCapacity 0x929F -#define AddressEMF 0x92A3 -#define AddressC0 0x92A9 -#define AddressR0 0x92AB -#define AddressT0 0x92AD -#define AddressR1 0x92AF -#define AddressTC 0x92B1 -#define AddressC1 0x92B2 -#define AddressEDV0 0x92B4 -#define AddressEDV1 0x92B7 -#define AddressEDV2 0x92BA -#define AddressStartDOD0 0x92BD -#define AddressStartDOD10 0x92BF -#define AddressStartDOD20 0x92C1 -#define AddressStartDOD30 0x92C3 -#define AddressStartDOD40 0x92C5 -#define AddressStartDOD50 0x92C7 -#define AddressStartDOD60 0x92C9 -#define AddressStartDOD70 0x92CB -#define AddressStartDOD80 0x92CD -#define AddressStartDOD90 0x92CF -#define AddressStartDOD100 0x92D1 diff --git a/lib/drivers/cc1101.c b/lib/drivers/cc1101.c index f28165e89c0..85d915acdcc 100644 --- a/lib/drivers/cc1101.c +++ b/lib/drivers/cc1101.c @@ -1,14 +1,27 @@ #include "cc1101.h" #include #include +#include + +static bool cc1101_spi_trx(FuriHalSpiBusHandle* handle, uint8_t* tx, uint8_t* rx, uint8_t size) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(CC1101_TIMEOUT * 1000); + + while(furi_hal_gpio_read(handle->miso)) { + if(furi_hal_cortex_timer_is_expired(timer)) { + //timeout + return false; + } + } + if(!furi_hal_spi_bus_trx(handle, tx, rx, size, CC1101_TIMEOUT)) return false; + return true; +} CC1101Status cc1101_strobe(FuriHalSpiBusHandle* handle, uint8_t strobe) { uint8_t tx[1] = {strobe}; CC1101Status rx[1] = {0}; + rx[0].CHIP_RDYn = 1; - while(furi_hal_gpio_read(handle->miso)) - ; - furi_hal_spi_bus_trx(handle, tx, (uint8_t*)rx, 1, CC1101_TIMEOUT); + cc1101_spi_trx(handle, tx, (uint8_t*)rx, 1); assert(rx[0].CHIP_RDYn == 0); return rx[0]; @@ -17,10 +30,10 @@ CC1101Status cc1101_strobe(FuriHalSpiBusHandle* handle, uint8_t strobe) { CC1101Status cc1101_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data) { uint8_t tx[2] = {reg, data}; CC1101Status rx[2] = {0}; + rx[0].CHIP_RDYn = 1; + rx[1].CHIP_RDYn = 1; - while(furi_hal_gpio_read(handle->miso)) - ; - furi_hal_spi_bus_trx(handle, tx, (uint8_t*)rx, 2, CC1101_TIMEOUT); + cc1101_spi_trx(handle, tx, (uint8_t*)rx, 2); assert((rx[0].CHIP_RDYn | rx[1].CHIP_RDYn) == 0); return rx[1]; @@ -30,10 +43,9 @@ CC1101Status cc1101_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* assert(sizeof(CC1101Status) == 1); uint8_t tx[2] = {reg | CC1101_READ, 0}; CC1101Status rx[2] = {0}; + rx[0].CHIP_RDYn = 1; - while(furi_hal_gpio_read(handle->miso)) - ; - furi_hal_spi_bus_trx(handle, tx, (uint8_t*)rx, 2, CC1101_TIMEOUT); + cc1101_spi_trx(handle, tx, (uint8_t*)rx, 2); assert((rx[0].CHIP_RDYn) == 0); *data = *(uint8_t*)&rx[1]; @@ -58,40 +70,40 @@ uint8_t cc1101_get_rssi(FuriHalSpiBusHandle* handle) { return rssi; } -void cc1101_reset(FuriHalSpiBusHandle* handle) { - cc1101_strobe(handle, CC1101_STROBE_SRES); +CC1101Status cc1101_reset(FuriHalSpiBusHandle* handle) { + return cc1101_strobe(handle, CC1101_STROBE_SRES); } CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle) { return cc1101_strobe(handle, CC1101_STROBE_SNOP); } -void cc1101_shutdown(FuriHalSpiBusHandle* handle) { - cc1101_strobe(handle, CC1101_STROBE_SPWD); +CC1101Status cc1101_shutdown(FuriHalSpiBusHandle* handle) { + return cc1101_strobe(handle, CC1101_STROBE_SPWD); } -void cc1101_calibrate(FuriHalSpiBusHandle* handle) { - cc1101_strobe(handle, CC1101_STROBE_SCAL); +CC1101Status cc1101_calibrate(FuriHalSpiBusHandle* handle) { + return cc1101_strobe(handle, CC1101_STROBE_SCAL); } -void cc1101_switch_to_idle(FuriHalSpiBusHandle* handle) { - cc1101_strobe(handle, CC1101_STROBE_SIDLE); +CC1101Status cc1101_switch_to_idle(FuriHalSpiBusHandle* handle) { + return cc1101_strobe(handle, CC1101_STROBE_SIDLE); } -void cc1101_switch_to_rx(FuriHalSpiBusHandle* handle) { - cc1101_strobe(handle, CC1101_STROBE_SRX); +CC1101Status cc1101_switch_to_rx(FuriHalSpiBusHandle* handle) { + return cc1101_strobe(handle, CC1101_STROBE_SRX); } -void cc1101_switch_to_tx(FuriHalSpiBusHandle* handle) { - cc1101_strobe(handle, CC1101_STROBE_STX); +CC1101Status cc1101_switch_to_tx(FuriHalSpiBusHandle* handle) { + return cc1101_strobe(handle, CC1101_STROBE_STX); } -void cc1101_flush_rx(FuriHalSpiBusHandle* handle) { - cc1101_strobe(handle, CC1101_STROBE_SFRX); +CC1101Status cc1101_flush_rx(FuriHalSpiBusHandle* handle) { + return cc1101_strobe(handle, CC1101_STROBE_SFRX); } -void cc1101_flush_tx(FuriHalSpiBusHandle* handle) { - cc1101_strobe(handle, CC1101_STROBE_SFTX); +CC1101Status cc1101_flush_tx(FuriHalSpiBusHandle* handle) { + return cc1101_strobe(handle, CC1101_STROBE_SFTX); } uint32_t cc1101_set_frequency(FuriHalSpiBusHandle* handle, uint32_t value) { @@ -121,14 +133,14 @@ uint32_t cc1101_set_intermediate_frequency(FuriHalSpiBusHandle* handle, uint32_t } void cc1101_set_pa_table(FuriHalSpiBusHandle* handle, const uint8_t value[8]) { - uint8_t tx[9] = {CC1101_PATABLE | CC1101_BURST}; + uint8_t tx[9] = {CC1101_PATABLE | CC1101_BURST}; //-V1009 CC1101Status rx[9] = {0}; + rx[0].CHIP_RDYn = 1; + rx[8].CHIP_RDYn = 1; memcpy(&tx[1], &value[0], 8); - while(furi_hal_gpio_read(handle->miso)) - ; - furi_hal_spi_bus_trx(handle, tx, (uint8_t*)rx, sizeof(rx), CC1101_TIMEOUT); + cc1101_spi_trx(handle, tx, (uint8_t*)rx, sizeof(rx)); assert((rx[0].CHIP_RDYn | rx[8].CHIP_RDYn) == 0); } @@ -139,36 +151,24 @@ uint8_t cc1101_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* data, uint buff_tx[0] = CC1101_FIFO | CC1101_BURST; memcpy(&buff_tx[1], data, size); - // Start transaction - // Wait IC to become ready - while(furi_hal_gpio_read(handle->miso)) - ; - // Tell IC what we want - furi_hal_spi_bus_trx(handle, buff_tx, (uint8_t*)buff_rx, size + 1, CC1101_TIMEOUT); + cc1101_spi_trx(handle, buff_tx, (uint8_t*)buff_rx, size + 1); return size; } 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]; - - // Start transaction - // Wait IC to become ready - while(furi_hal_gpio_read(handle->miso)) - ; + uint8_t buff_trx[2]; + buff_trx[0] = CC1101_FIFO | CC1101_READ | CC1101_BURST; - // First byte - packet length - furi_hal_spi_bus_trx(handle, buff_tx, buff_rx, 2, CC1101_TIMEOUT); + cc1101_spi_trx(handle, buff_trx, buff_trx, 2); // 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/drivers/cc1101.h b/lib/drivers/cc1101.h index af1f15569d1..d8ee05d5289 100644 --- a/lib/drivers/cc1101.h +++ b/lib/drivers/cc1101.h @@ -46,8 +46,10 @@ CC1101Status cc1101_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* /** Reset * * @param handle - pointer to FuriHalSpiHandle + * + * @return CC1101Status structure */ -void cc1101_reset(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_reset(FuriHalSpiBusHandle* handle); /** Get status * @@ -60,8 +62,10 @@ CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle); /** Enable shutdown mode * * @param handle - pointer to FuriHalSpiHandle + * + * @return CC1101Status structure */ -void cc1101_shutdown(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_shutdown(FuriHalSpiBusHandle* handle); /** Get Partnumber * @@ -90,38 +94,46 @@ uint8_t cc1101_get_rssi(FuriHalSpiBusHandle* handle); /** Calibrate oscillator * * @param handle - pointer to FuriHalSpiHandle + * + * @return CC1101Status structure */ -void cc1101_calibrate(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_calibrate(FuriHalSpiBusHandle* handle); /** Switch to idle * * @param handle - pointer to FuriHalSpiHandle */ -void cc1101_switch_to_idle(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_switch_to_idle(FuriHalSpiBusHandle* handle); /** Switch to RX * * @param handle - pointer to FuriHalSpiHandle + * + * @return CC1101Status structure */ -void cc1101_switch_to_rx(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_switch_to_rx(FuriHalSpiBusHandle* handle); /** Switch to TX * * @param handle - pointer to FuriHalSpiHandle + * + * @return CC1101Status structure */ -void cc1101_switch_to_tx(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_switch_to_tx(FuriHalSpiBusHandle* handle); /** Flush RX FIFO * * @param handle - pointer to FuriHalSpiHandle + * + * @return CC1101Status structure */ -void cc1101_flush_rx(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_flush_rx(FuriHalSpiBusHandle* handle); /** Flush TX FIFO * * @param handle - pointer to FuriHalSpiHandle */ -void cc1101_flush_tx(FuriHalSpiBusHandle* handle); +CC1101Status cc1101_flush_tx(FuriHalSpiBusHandle* handle); /** Set Frequency * diff --git a/lib/drivers/cc1101_regs.h b/lib/drivers/cc1101_regs.h index a326dc92ce8..e0aed6bd93c 100644 --- a/lib/drivers/cc1101_regs.h +++ b/lib/drivers/cc1101_regs.h @@ -14,7 +14,7 @@ extern "C" { #define CC1101_IFDIV 0x400 /* IO Bus constants */ -#define CC1101_TIMEOUT 500 +#define CC1101_TIMEOUT 250 /* Bits and pieces */ #define CC1101_READ (1 << 7) /** Read Bit */ diff --git a/lib/drivers/lp5562.c b/lib/drivers/lp5562.c index f5d765fa937..e755d2d6038 100644 --- a/lib/drivers/lp5562.c +++ b/lib/drivers/lp5562.c @@ -105,7 +105,7 @@ void lp5562_set_channel_src(FuriHalI2cBusHandle* handle, LP5562Channel channel, reg_val &= ~(0x3 << bit_offset); reg_val |= ((src & 0x03) << bit_offset); furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x70, reg_val, LP5562_I2C_TIMEOUT); - } while(channel); + } while(channel != 0); } void lp5562_execute_program( @@ -166,7 +166,7 @@ void lp5562_stop_program(FuriHalI2cBusHandle* handle, LP5562Engine eng) { bit_offset = (3 - eng) * 2; furi_hal_i2c_read_reg_8(handle, LP5562_ADDRESS, 0x01, ®_val, LP5562_I2C_TIMEOUT); reg_val &= ~(0x3 << bit_offset); - reg_val |= (0x00 << bit_offset); // Disabled + // Not setting lowest 2 bits here furi_hal_i2c_write_reg_8(handle, LP5562_ADDRESS, 0x01, reg_val, LP5562_I2C_TIMEOUT); } diff --git a/lib/drivers/st25r3916.c b/lib/drivers/st25r3916.c new file mode 100644 index 00000000000..47726121358 --- /dev/null +++ b/lib/drivers/st25r3916.c @@ -0,0 +1,81 @@ +#include "st25r3916.h" + +#include + +void st25r3916_mask_irq(FuriHalSpiBusHandle* handle, uint32_t mask) { + furi_assert(handle); + + uint8_t irq_mask_regs[4] = { + mask & 0xff, + (mask >> 8) & 0xff, + (mask >> 16) & 0xff, + (mask >> 24) & 0xff, + }; + st25r3916_write_burst_regs(handle, ST25R3916_REG_IRQ_MASK_MAIN, irq_mask_regs, 4); +} + +uint32_t st25r3916_get_irq(FuriHalSpiBusHandle* handle) { + furi_assert(handle); + + uint8_t irq_regs[4] = {}; + uint32_t irq = 0; + st25r3916_read_burst_regs(handle, ST25R3916_REG_IRQ_MASK_MAIN, irq_regs, 4); + // FURI_LOG_I( + // "Mask Irq", "%02X %02X %02X %02X", irq_regs[0], irq_regs[1], irq_regs[2], irq_regs[3]); + st25r3916_read_burst_regs(handle, ST25R3916_REG_IRQ_MAIN, irq_regs, 4); + irq = (uint32_t)irq_regs[0]; + irq |= (uint32_t)irq_regs[1] << 8; + irq |= (uint32_t)irq_regs[2] << 16; + irq |= (uint32_t)irq_regs[3] << 24; + // FURI_LOG_I("iRQ", "%02X %02X %02X %02X", irq_regs[0], irq_regs[1], irq_regs[2], irq_regs[3]); + + return irq; +} + +void st25r3916_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t bits) { + furi_assert(handle); + furi_assert(buff); + + size_t bytes = (bits + 7) / 8; + + st25r3916_write_reg(handle, ST25R3916_REG_NUM_TX_BYTES2, (uint8_t)(bits & 0xFFU)); + st25r3916_write_reg(handle, ST25R3916_REG_NUM_TX_BYTES1, (uint8_t)((bits >> 8) & 0xFFU)); + + st25r3916_reg_write_fifo(handle, buff, bytes); +} + +bool st25r3916_read_fifo( + FuriHalSpiBusHandle* handle, + uint8_t* buff, + size_t buff_size, + size_t* buff_bits) { + furi_assert(handle); + furi_assert(buff); + + bool read_success = false; + + do { + uint8_t fifo_status[2] = {}; + st25r3916_read_burst_regs(handle, ST25R3916_REG_FIFO_STATUS1, fifo_status, 2); + size_t bytes = ((fifo_status[1] & ST25R3916_REG_FIFO_STATUS2_fifo_b_mask) >> + ST25R3916_REG_FIFO_STATUS2_fifo_b_shift) | + fifo_status[0]; + uint8_t bits = + ((fifo_status[1] & ST25R3916_REG_FIFO_STATUS2_fifo_lb_mask) >> + ST25R3916_REG_FIFO_STATUS2_fifo_lb_shift); + + if(bytes == 0) break; + if(bytes > buff_size) break; + + st25r3916_reg_read_fifo(handle, buff, bytes); + + if(bits) { + *buff_bits = (bytes - 1) * 8 + bits; + } else { + *buff_bits = bytes * 8; + } + read_success = true; + } while(false); + + return read_success; +} diff --git a/lib/drivers/st25r3916.h b/lib/drivers/st25r3916.h new file mode 100644 index 00000000000..532239f4728 --- /dev/null +++ b/lib/drivers/st25r3916.h @@ -0,0 +1,113 @@ +#pragma once + +#include "st25r3916_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ST25R3916_IRQ_MASK_ALL (uint32_t)(0xFFFFFFFFUL) /** All ST25R3916 interrupt sources */ +#define ST25R3916_IRQ_MASK_NONE (uint32_t)(0x00000000UL) /**No ST25R3916 interrupt source */ + +/** Main interrupt register */ +#define ST25R3916_IRQ_MASK_OSC (uint32_t)(0x00000080U) /** ST25R3916 oscillator stable interrupt */ +#define ST25R3916_IRQ_MASK_FWL (uint32_t)(0x00000040U) /** ST25R3916 FIFO water level interrupt */ +#define ST25R3916_IRQ_MASK_RXS (uint32_t)(0x00000020U) /** ST25R3916 start of receive interrupt */ +#define ST25R3916_IRQ_MASK_RXE (uint32_t)(0x00000010U) /** ST25R3916 end of receive interrupt */ +#define ST25R3916_IRQ_MASK_TXE \ + (uint32_t)(0x00000008U) /** ST25R3916 end of transmission interrupt */ +#define ST25R3916_IRQ_MASK_COL (uint32_t)(0x00000004U) /** ST25R3916 bit collision interrupt */ +#define ST25R3916_IRQ_MASK_RX_REST \ + (uint32_t)(0x00000002U) /** ST25R3916 automatic reception restart interrupt */ +#define ST25R3916_IRQ_MASK_RFU (uint32_t)(0x00000001U) /** ST25R3916 RFU interrupt */ + +/** Timer and NFC interrupt register */ +#define ST25R3916_IRQ_MASK_DCT \ + (uint32_t)(0x00008000U) /** ST25R3916 termination of direct command interrupt. */ +#define ST25R3916_IRQ_MASK_NRE \ + (uint32_t)(0x00004000U) /** ST25R3916 no-response timer expired interrupt */ +#define ST25R3916_IRQ_MASK_GPE \ + (uint32_t)(0x00002000U) /** ST25R3916 general purpose timer expired interrupt */ +#define ST25R3916_IRQ_MASK_EON (uint32_t)(0x00001000U) /** ST25R3916 external field on interrupt */ +#define ST25R3916_IRQ_MASK_EOF \ + (uint32_t)(0x00000800U) /** ST25R3916 external field off interrupt */ +#define ST25R3916_IRQ_MASK_CAC \ + (uint32_t)(0x00000400U) /** ST25R3916 collision during RF collision avoidance */ +#define ST25R3916_IRQ_MASK_CAT \ + (uint32_t)(0x00000200U) /** ST25R3916 minimum guard time expired interrupt */ +#define ST25R3916_IRQ_MASK_NFCT \ + (uint32_t)(0x00000100U) /** ST25R3916 initiator bit rate recognised interrupt */ + +/** Error and wake-up interrupt register */ +#define ST25R3916_IRQ_MASK_CRC (uint32_t)(0x00800000U) /** ST25R3916 CRC error interrupt */ +#define ST25R3916_IRQ_MASK_PAR (uint32_t)(0x00400000U) /** ST25R3916 parity error interrupt */ +#define ST25R3916_IRQ_MASK_ERR2 \ + (uint32_t)(0x00200000U) /** ST25R3916 soft framing error interrupt */ +#define ST25R3916_IRQ_MASK_ERR1 \ + (uint32_t)(0x00100000U) /** ST25R3916 hard framing error interrupt */ +#define ST25R3916_IRQ_MASK_WT (uint32_t)(0x00080000U) /** ST25R3916 wake-up interrupt */ +#define ST25R3916_IRQ_MASK_WAM \ + (uint32_t)(0x00040000U) /** ST25R3916 wake-up due to amplitude interrupt */ +#define ST25R3916_IRQ_MASK_WPH \ + (uint32_t)(0x00020000U) /** ST25R3916 wake-up due to phase interrupt */ +#define ST25R3916_IRQ_MASK_WCAP \ + (uint32_t)(0x00010000U) /** ST25R3916 wake-up due to capacitance measurement */ + +/** Passive Target Interrupt Register */ +#define ST25R3916_IRQ_MASK_PPON2 \ + (uint32_t)(0x80000000U) /** ST25R3916 PPON2 Field on waiting Timer interrupt */ +#define ST25R3916_IRQ_MASK_SL_WL \ + (uint32_t)(0x40000000U) /** ST25R3916 Passive target slot number water level interrupt */ +#define ST25R3916_IRQ_MASK_APON \ + (uint32_t)(0x20000000U) /** ST25R3916 Anticollision done and Field On interrupt */ +#define ST25R3916_IRQ_MASK_RXE_PTA \ + (uint32_t)(0x10000000U) /** ST25R3916 RXE with an automatic response interrupt */ +#define ST25R3916_IRQ_MASK_WU_F \ + (uint32_t)(0x08000000U) /** ST25R3916 212/424b/s Passive target interrupt: Active */ +#define ST25R3916_IRQ_MASK_RFU2 (uint32_t)(0x04000000U) /** ST25R3916 RFU2 interrupt */ +#define ST25R3916_IRQ_MASK_WU_A_X \ + (uint32_t)(0x02000000U) /** ST25R3916 106kb/s Passive target state interrupt: Active* */ +#define ST25R3916_IRQ_MASK_WU_A \ + (uint32_t)(0x01000000U) /** ST25R3916 106kb/s Passive target state interrupt: Active */ + +/** Mask st25r3916 interrupts + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param mask - mask of interrupts to be disabled + */ +void st25r3916_mask_irq(FuriHalSpiBusHandle* handle, uint32_t mask); + +/** Get st25r3916 interrupts + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * + * @return received interrupts + */ +uint32_t st25r3916_get_irq(FuriHalSpiBusHandle* handle); + +/** Write FIFO + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param buff - buffer to write to FIFO + * @param bits - number of bits to write + */ +void st25r3916_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t bits); + +/** Read FIFO + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param buff - buffer to read from FIFO + * @param buff_size - buffer size n bytes + * @param buff_bits - pointer to number of bits read + * + * @return true if read success, false otherwise +*/ +bool st25r3916_read_fifo( + FuriHalSpiBusHandle* handle, + uint8_t* buff, + size_t buff_size, + size_t* buff_bits); + +#ifdef __cplusplus +} +#endif diff --git a/lib/drivers/st25r3916_reg.c b/lib/drivers/st25r3916_reg.c new file mode 100644 index 00000000000..8ac4672383c --- /dev/null +++ b/lib/drivers/st25r3916_reg.c @@ -0,0 +1,257 @@ +#include "st25r3916_reg.h" + +#include + +#define ST25R3916_WRITE_MODE \ + (0U << 6) /*!< ST25R3916 Operation Mode: Write */ +#define ST25R3916_READ_MODE \ + (1U << 6) /*!< ST25R3916 Operation Mode: Read */ +#define ST25R3916_CMD_MODE \ + (3U << 6) /*!< ST25R3916 Operation Mode: Direct Command */ +#define ST25R3916_FIFO_LOAD \ + (0x80U) /*!< ST25R3916 Operation Mode: FIFO Load */ +#define ST25R3916_FIFO_READ \ + (0x9FU) /*!< ST25R3916 Operation Mode: FIFO Read */ +#define ST25R3916_PT_A_CONFIG_LOAD \ + (0xA0U) /*!< ST25R3916 Operation Mode: Passive Target Memory A-Config Load */ +#define ST25R3916_PT_F_CONFIG_LOAD \ + (0xA8U) /*!< ST25R3916 Operation Mode: Passive Target Memory F-Config Load */ +#define ST25R3916_PT_TSN_DATA_LOAD \ + (0xACU) /*!< ST25R3916 Operation Mode: Passive Target Memory TSN Load */ +#define ST25R3916_PT_MEM_READ \ + (0xBFU) /*!< ST25R3916 Operation Mode: Passive Target Memory Read */ + +#define ST25R3916_CMD_LEN \ + (1U) /*!< ST25R3916 CMD length */ +#define ST25R3916_FIFO_DEPTH (512U) +#define ST25R3916_BUF_LEN \ + (ST25R3916_CMD_LEN + \ + ST25R3916_FIFO_DEPTH) /*!< ST25R3916 communication buffer: CMD + FIFO length */ + +static void st25r3916_reg_tx_byte(FuriHalSpiBusHandle* handle, uint8_t byte) { + uint8_t val = byte; + furi_hal_spi_bus_tx(handle, &val, 1, 5); +} + +void st25r3916_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val) { + furi_assert(handle); + st25r3916_read_burst_regs(handle, reg, val, 1); +} + +void st25r3916_read_burst_regs( + FuriHalSpiBusHandle* handle, + uint8_t reg_start, + uint8_t* values, + uint8_t length) { + furi_assert(handle); + furi_assert(values); + furi_assert(length); + + furi_hal_gpio_write(handle->cs, false); + + if(reg_start & ST25R3916_SPACE_B) { + // Send direct command first + st25r3916_reg_tx_byte(handle, ST25R3916_CMD_SPACE_B_ACCESS); + } + st25r3916_reg_tx_byte(handle, (reg_start & ~ST25R3916_SPACE_B) | ST25R3916_READ_MODE); + furi_hal_spi_bus_rx(handle, values, length, 5); + + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val) { + furi_assert(handle); + uint8_t reg_val = val; + st25r3916_write_burst_regs(handle, reg, ®_val, 1); +} + +void st25r3916_write_burst_regs( + FuriHalSpiBusHandle* handle, + uint8_t reg_start, + const uint8_t* values, + uint8_t length) { + furi_assert(handle); + furi_assert(values); + furi_assert(length); + + furi_hal_gpio_write(handle->cs, false); + + if(reg_start & ST25R3916_SPACE_B) { + // Send direct command first + st25r3916_reg_tx_byte(handle, ST25R3916_CMD_SPACE_B_ACCESS); + } + st25r3916_reg_tx_byte(handle, (reg_start & ~ST25R3916_SPACE_B) | ST25R3916_WRITE_MODE); + furi_hal_spi_bus_tx(handle, values, length, 5); + + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_reg_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t length) { + furi_assert(handle); + furi_assert(buff); + furi_assert(length); + furi_assert(length <= ST25R3916_FIFO_DEPTH); + + furi_hal_gpio_write(handle->cs, false); + st25r3916_reg_tx_byte(handle, ST25R3916_FIFO_LOAD); + furi_hal_spi_bus_tx(handle, buff, length, 200); + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_reg_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) { + furi_assert(handle); + furi_assert(buff); + furi_assert(length); + furi_assert(length <= ST25R3916_FIFO_DEPTH); + + furi_hal_gpio_write(handle->cs, false); + st25r3916_reg_tx_byte(handle, ST25R3916_FIFO_READ); + furi_hal_spi_bus_rx(handle, buff, length, 200); + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_write_pta_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, size_t length) { + furi_assert(handle); + furi_assert(values); + furi_assert(length); + furi_assert(length <= ST25R3916_PTM_LEN); + + furi_hal_gpio_write(handle->cs, false); + st25r3916_reg_tx_byte(handle, ST25R3916_PT_A_CONFIG_LOAD); + furi_hal_spi_bus_tx(handle, values, length, 200); + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_read_pta_mem(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) { + furi_assert(handle); + furi_assert(buff); + furi_assert(length); + furi_assert(length <= ST25R3916_PTM_LEN); + + uint8_t tmp_buff[ST25R3916_PTM_LEN + 1]; + furi_hal_gpio_write(handle->cs, false); + st25r3916_reg_tx_byte(handle, ST25R3916_PT_MEM_READ); + furi_hal_spi_bus_rx(handle, tmp_buff, length + 1, 200); + furi_hal_gpio_write(handle->cs, true); + memcpy(buff, tmp_buff + 1, length); +} + +void st25r3916_write_ptf_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, size_t length) { + furi_assert(handle); + furi_assert(values); + + furi_hal_gpio_write(handle->cs, false); + st25r3916_reg_tx_byte(handle, ST25R3916_PT_F_CONFIG_LOAD); + furi_hal_spi_bus_tx(handle, values, length, 200); + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_write_pttsn_mem(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length) { + furi_assert(handle); + furi_assert(buff); + + furi_hal_gpio_write(handle->cs, false); + st25r3916_reg_tx_byte(handle, ST25R3916_PT_TSN_DATA_LOAD); + furi_hal_spi_bus_tx(handle, buff, length, 200); + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_direct_cmd(FuriHalSpiBusHandle* handle, uint8_t cmd) { + furi_assert(handle); + + furi_hal_gpio_write(handle->cs, false); + st25r3916_reg_tx_byte(handle, cmd | ST25R3916_CMD_MODE); + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_read_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val) { + furi_assert(handle); + + furi_hal_gpio_write(handle->cs, false); + st25r3916_reg_tx_byte(handle, ST25R3916_CMD_TEST_ACCESS); + st25r3916_reg_tx_byte(handle, reg | ST25R3916_READ_MODE); + furi_hal_spi_bus_rx(handle, val, 1, 5); + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_write_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val) { + furi_assert(handle); + + furi_hal_gpio_write(handle->cs, false); + st25r3916_reg_tx_byte(handle, ST25R3916_CMD_TEST_ACCESS); + st25r3916_reg_tx_byte(handle, reg | ST25R3916_WRITE_MODE); + furi_hal_spi_bus_tx(handle, &val, 1, 5); + furi_hal_gpio_write(handle->cs, true); +} + +void st25r3916_clear_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t clr_mask) { + furi_assert(handle); + + uint8_t reg_val = 0; + st25r3916_read_reg(handle, reg, ®_val); + if((reg_val & ~clr_mask) != reg_val) { + reg_val &= ~clr_mask; + st25r3916_write_reg(handle, reg, reg_val); + } +} + +void st25r3916_set_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t set_mask) { + furi_assert(handle); + + uint8_t reg_val = 0; + st25r3916_read_reg(handle, reg, ®_val); + if((reg_val | set_mask) != reg_val) { + reg_val |= set_mask; + st25r3916_write_reg(handle, reg, reg_val); + } +} + +void st25r3916_change_reg_bits( + FuriHalSpiBusHandle* handle, + uint8_t reg, + uint8_t mask, + uint8_t value) { + furi_assert(handle); + + st25r3916_modify_reg(handle, reg, mask, (mask & value)); +} + +void st25r3916_modify_reg( + FuriHalSpiBusHandle* handle, + uint8_t reg, + uint8_t clr_mask, + uint8_t set_mask) { + furi_assert(handle); + + uint8_t reg_val = 0; + uint8_t new_val = 0; + st25r3916_read_reg(handle, reg, ®_val); + new_val = (reg_val & ~clr_mask) | set_mask; + if(new_val != reg_val) { + st25r3916_write_reg(handle, reg, new_val); + } +} + +void st25r3916_change_test_reg_bits( + FuriHalSpiBusHandle* handle, + uint8_t reg, + uint8_t mask, + uint8_t value) { + furi_assert(handle); + + uint8_t reg_val = 0; + uint8_t new_val = 0; + st25r3916_read_test_reg(handle, reg, ®_val); + new_val = (reg_val & ~mask) | (mask & value); + if(new_val != reg_val) { + st25r3916_write_test_reg(handle, reg, new_val); + } +} + +bool st25r3916_check_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t mask, uint8_t val) { + furi_assert(handle); + + uint8_t reg_val = 0; + st25r3916_read_reg(handle, reg, ®_val); + return ((reg_val & mask) == val); +} diff --git a/lib/drivers/st25r3916_reg.h b/lib/drivers/st25r3916_reg.h new file mode 100644 index 00000000000..8a924fc3929 --- /dev/null +++ b/lib/drivers/st25r3916_reg.h @@ -0,0 +1,1144 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** ST25R3916 direct commands */ +#define ST25R3916_CMD_SET_DEFAULT \ + 0xC1U /** Puts the chip in default state (same as after power-up */ +#define ST25R3916_CMD_STOP 0xC2U /*!< Stops all activities and clears FIFO */ +#define ST25R3916_CMD_TRANSMIT_WITH_CRC 0xC4U /** Transmit with CRC */ +#define ST25R3916_CMD_TRANSMIT_WITHOUT_CRC 0xC5U /** Transmit without CRC */ +#define ST25R3916_CMD_TRANSMIT_REQA 0xC6U /** Transmit REQA */ +#define ST25R3916_CMD_TRANSMIT_WUPA 0xC7U /** Transmit WUPA */ +#define ST25R3916_CMD_INITIAL_RF_COLLISION \ + 0xC8U /** NFC transmit with Initial RF Collision Avoidance */ +#define ST25R3916_CMD_RESPONSE_RF_COLLISION_N \ + 0xC9U /** NFC transmit with Response RF Collision Avoidance */ +#define ST25R3916_CMD_GOTO_SENSE 0xCDU /** Passive target logic to Sense/Idle state */ +#define ST25R3916_CMD_GOTO_SLEEP 0xCEU /** Passive target logic to Sleep/Halt state */ +#define ST25R3916_CMD_MASK_RECEIVE_DATA 0xD0U /** Mask receive data */ +#define ST25R3916_CMD_UNMASK_RECEIVE_DATA 0xD1U /** Unmask receive data */ +#define ST25R3916_CMD_AM_MOD_STATE_CHANGE 0xD2U /** AM Modulation state change */ +#define ST25R3916_CMD_MEASURE_AMPLITUDE 0xD3U /** Measure singal amplitude on RFI inputs */ +#define ST25R3916_CMD_RESET_RXGAIN 0xD5U /** Reset RX Gain */ +#define ST25R3916_CMD_ADJUST_REGULATORS 0xD6U /** Adjust regulators */ +#define ST25R3916_CMD_CALIBRATE_DRIVER_TIMING \ + 0xD8U /** Starts the sequence to adjust the driver timing */ +#define ST25R3916_CMD_MEASURE_PHASE 0xD9U /** Measure phase between RFO and RFI signal */ +#define ST25R3916_CMD_CLEAR_RSSI 0xDAU /** Clear RSSI bits and restart the measurement */ +#define ST25R3916_CMD_CLEAR_FIFO 0xDBU /** Clears FIFO, Collision and IRQ status */ +#define ST25R3916_CMD_TRANSPARENT_MODE 0xDCU /** Transparent mode */ +#define ST25R3916_CMD_CALIBRATE_C_SENSOR 0xDDU /** Calibrate the capacitive sensor */ +#define ST25R3916_CMD_MEASURE_CAPACITANCE 0xDEU /** Measure capacitance */ +#define ST25R3916_CMD_MEASURE_VDD 0xDFU /** Measure power supply voltage */ +#define ST25R3916_CMD_START_GP_TIMER 0xE0U /** Start the general purpose timer */ +#define ST25R3916_CMD_START_WUP_TIMER 0xE1U /** Start the wake-up timer */ +#define ST25R3916_CMD_START_MASK_RECEIVE_TIMER 0xE2U /** Start the mask-receive timer */ +#define ST25R3916_CMD_START_NO_RESPONSE_TIMER 0xE3U /** Start the no-response timer */ +#define ST25R3916_CMD_START_PPON2_TIMER 0xE4U /** Start PPon2 timer */ +#define ST25R3916_CMD_STOP_NRT 0xE8U /** Stop No Response Timer */ +#define ST25R3916_CMD_SPACE_B_ACCESS 0xFBU /** Enable R/W access to the test registers */ +#define ST25R3916_CMD_TEST_ACCESS 0xFCU /** Enable R/W access to the test registers */ + +#define ST25R3916_SPACE_B 0x40U /** ST25R3916 Space-B indicator */ +#define ST25R3916_SPACE_B_REG_LEN 16U /** Number of register in the space B */ + +#define ST25R3916_FIFO_STATUS_LEN 2 /** Number of FIFO Status Register */ + +#define ST25R3916_PTM_A_LEN 15U /** Passive target memory A config length */ +#define ST25R3916_PTM_B_LEN 0U /** Passive target memory B config length */ +#define ST25R3916_PTM_F_LEN 21U /** Passive target memory F config length */ +#define ST25R3916_PTM_TSN_LEN 12U /** Passive target memory TSN data length */ + +/** Full Passive target memory length */ +#define ST25R3916_PTM_LEN \ + (ST25R3916_PTM_A_LEN + ST25R3916_PTM_B_LEN + ST25R3916_PTM_F_LEN + ST25R3916_PTM_TSN_LEN) + +/** IO configuration registers */ +#define ST25R3916_REG_IO_CONF1 0x00U /** RW IO Configuration Register 1 */ +#define ST25R3916_REG_IO_CONF2 0x01U /** RW IO Configuration Register 2 */ + +/** Operation control and mode definition */ +#define ST25R3916_REG_OP_CONTROL 0x02U /** RW Operation Control Register */ +#define ST25R3916_REG_MODE 0x03U /** RW Mode Definition Register */ +#define ST25R3916_REG_BIT_RATE 0x04U /** RW Bit Rate Definition Register */ + +/** Protocol Configuration registers */ +#define ST25R3916_REG_ISO14443A_NFC 0x05U /** RW ISO14443A and NFC 106 kBit/s Settings Register */ +#define ST25R3916_REG_EMD_SUP_CONF \ + (ST25R3916_SPACE_B | 0x05U) /*!< RW EMD Suppression Configuration Register */ +#define ST25R3916_REG_ISO14443B_1 0x06U /** RW ISO14443B Settings Register 1 */ +#define ST25R3916_REG_SUBC_START_TIME \ + (ST25R3916_SPACE_B | 0x06U) /*!< RW Subcarrier Start Time Register */ +#define ST25R3916_REG_ISO14443B_2 0x07U /** RW ISO14443B Settings Register 2 */ +#define ST25R3916_REG_PASSIVE_TARGET 0x08U /** RW Passive Target Definition Register */ +#define ST25R3916_REG_STREAM_MODE 0x09U /** RW Stream Mode Definition Register */ +#define ST25R3916_REG_AUX 0x0AU /** RW Auxiliary Definition Register */ + +/** Receiver Configuration registers */ +#define ST25R3916_REG_RX_CONF1 0x0BU /** RW Receiver Configuration Register 1 */ +#define ST25R3916_REG_RX_CONF2 0x0CU /** RW Receiver Configuration Register 2 */ +#define ST25R3916_REG_RX_CONF3 0x0DU /** RW Receiver Configuration Register 3 */ +#define ST25R3916_REG_RX_CONF4 0x0EU /** RW Receiver Configuration Register 4 */ +#define ST25R3916_REG_P2P_RX_CONF \ + (ST25R3916_SPACE_B | 0x0BU) /** RW P2P Receiver Configuration Register 1 */ +#define ST25R3916_REG_CORR_CONF1 \ + (ST25R3916_SPACE_B | 0x0CU) /** RW Correlator configuration register 1 */ +#define ST25R3916_REG_CORR_CONF2 \ + (ST25R3916_SPACE_B | 0x0DU) /** RW Correlator configuration register 2 */ + +/** Timer definition registers */ +#define ST25R3916_REG_MASK_RX_TIMER 0x0FU /** RW Mask Receive Timer Register */ +#define ST25R3916_REG_NO_RESPONSE_TIMER1 0x10U /** RW No-response Timer Register 1 */ +#define ST25R3916_REG_NO_RESPONSE_TIMER2 0x11U /** RW No-response Timer Register 2 */ +#define ST25R3916_REG_TIMER_EMV_CONTROL 0x12U /** RW Timer and EMV Control */ +#define ST25R3916_REG_GPT1 0x13U /** RW General Purpose Timer Register 1 */ +#define ST25R3916_REG_GPT2 0x14U /** RW General Purpose Timer Register 2 */ +#define ST25R3916_REG_PPON2 0x15U /** RW PPON2 Field waiting Timer Register */ +#define ST25R3916_REG_SQUELCH_TIMER (ST25R3916_SPACE_B | 0x0FU) /** RW Squelch timeout Register */ +#define ST25R3916_REG_FIELD_ON_GT (ST25R3916_SPACE_B | 0x15U) /** RW NFC Field on guard time */ + +/** Interrupt and associated reporting registers */ +#define ST25R3916_REG_IRQ_MASK_MAIN 0x16U /** RW Mask Main Interrupt Register */ +#define ST25R3916_REG_IRQ_MASK_TIMER_NFC 0x17U /** RW Mask Timer and NFC Interrupt Register */ +#define ST25R3916_REG_IRQ_MASK_ERROR_WUP 0x18U /** RW Mask Error and Wake-up Interrupt Register */ +#define ST25R3916_REG_IRQ_MASK_TARGET 0x19U /** RW Mask 3916 Target Interrupt Register */ +#define ST25R3916_REG_IRQ_MAIN 0x1AU /** R Main Interrupt Register */ +#define ST25R3916_REG_IRQ_TIMER_NFC 0x1BU /** R Timer and NFC Interrupt Register */ +#define ST25R3916_REG_IRQ_ERROR_WUP 0x1CU /** R Error and Wake-up Interrupt Register */ +#define ST25R3916_REG_IRQ_TARGET 0x1DU /*!< R ST25R3916 Target Interrupt Register */ +#define ST25R3916_REG_FIFO_STATUS1 0x1EU /** R FIFO Status Register 1 */ +#define ST25R3916_REG_FIFO_STATUS2 0x1FU /** R FIFO Status Register 2 */ +#define ST25R3916_REG_COLLISION_STATUS 0x20U /** R Collision Display Register */ +#define ST25R3916_REG_PASSIVE_TARGET_STATUS 0x21U /** R Passive target state status */ + +/** Definition of number of transmitted bytes */ +#define ST25R3916_REG_NUM_TX_BYTES1 0x22U /** RW Number of Transmitted Bytes Register 1 */ +#define ST25R3916_REG_NUM_TX_BYTES2 0x23U /** RW Number of Transmitted Bytes Register 2 */ + +/** NFCIP Bit Rate Display Register */ +#define ST25R3916_REG_NFCIP1_BIT_RATE 0x24U /** R NFCIP Bit Rate Detection Display Register */ + +/** A/D Converter Output Register */ +#define ST25R3916_REG_AD_RESULT 0x25U /** R A/D Converter Output Register */ + +/** Antenna tuning registers */ +#define ST25R3916_REG_ANT_TUNE_A 0x26U /** RW Antenna Tuning Control (AAT-A) Register 1 */ +#define ST25R3916_REG_ANT_TUNE_B 0x27U /** RW Antenna Tuning Control (AAT-B) Register 2 */ + +/** Antenna Driver and Modulation registers */ +#define ST25R3916_REG_TX_DRIVER 0x28U /** RW TX driver register */ +#define ST25R3916_REG_PT_MOD 0x29U /** RW PT modulation Register */ +#define ST25R3916_REG_AUX_MOD (ST25R3916_SPACE_B | 0x28U) /** RW Aux Modulation setting Register */ +#define ST25R3916_REG_TX_DRIVER_TIMING \ + (ST25R3916_SPACE_B | 0x29U) /** RW TX driver timing Register */ +#define ST25R3916_REG_RES_AM_MOD \ + (ST25R3916_SPACE_B | 0x2AU) /** RW Resistive AM modulation register */ +#define ST25R3916_REG_TX_DRIVER_STATUS \ + (ST25R3916_SPACE_B | 0x2BU) /** R TX driver timing readout Register */ + +/** External Field Detector Threshold Registers */ +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV \ + 0x2AU /** RW External Field Detector Activation Threshold Reg */ +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV \ + 0x2BU /** RW External Field Detector Deactivation Threshold Reg */ + +/** Regulator registers */ +#define ST25R3916_REG_REGULATOR_CONTROL 0x2CU /** RW Regulated Voltage Control Register */ +#define ST25R3916_REG_REGULATOR_RESULT \ + (ST25R3916_SPACE_B | 0x2CU) /** R Regulator Display Register */ + +/** Receiver State Display Register */ +#define ST25R3916_REG_RSSI_RESULT 0x2DU /** R RSSI Display Register */ +#define ST25R3916_REG_GAIN_RED_STATE 0x2EU /** R Gain Reduction State Register */ +#define ST25R3916_REG_CAP_SENSOR_CONTROL 0x2FU /** RW Capacitive Sensor Control Register */ +#define ST25R3916_REG_CAP_SENSOR_RESULT 0x30U /** R Capacitive Sensor Display Register */ +#define ST25R3916_REG_AUX_DISPLAY 0x31U /** R Auxiliary Display Register */ + +/** Over/Undershoot Protection Configuration Registers */ +#define ST25R3916_REG_OVERSHOOT_CONF1 \ + (ST25R3916_SPACE_B | 0x30U) /** RW Overshoot Protection Configuration Register 1 */ +#define ST25R3916_REG_OVERSHOOT_CONF2 \ + (ST25R3916_SPACE_B | 0x31U) /** RW Overshoot Protection Configuration Register 2 */ +#define ST25R3916_REG_UNDERSHOOT_CONF1 \ + (ST25R3916_SPACE_B | 0x32U) /** RW Undershoot Protection Configuration Register 1 */ +#define ST25R3916_REG_UNDERSHOOT_CONF2 \ + (ST25R3916_SPACE_B | 0x33U) /** RW Undershoot Protection Configuration Register 2 */ + +/** Detection of card presence */ +#define ST25R3916_REG_WUP_TIMER_CONTROL 0x32U /** RW Wake-up Timer Control Register */ +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF \ + 0x33U /** RW Amplitude Measurement Configuration Register */ +#define ST25R3916_REG_AMPLITUDE_MEASURE_REF \ + 0x34U /** RW Amplitude Measurement Reference Register */ +#define ST25R3916_REG_AMPLITUDE_MEASURE_AA_RESULT \ + 0x35U /** R Amplitude Measurement Auto Averaging Display Reg */ +#define ST25R3916_REG_AMPLITUDE_MEASURE_RESULT \ + 0x36U /** R Amplitude Measurement Display Register */ +#define ST25R3916_REG_PHASE_MEASURE_CONF 0x37U /** RW Phase Measurement Configuration Register */ +#define ST25R3916_REG_PHASE_MEASURE_REF 0x38U /** RW Phase Measurement Reference Register */ +#define ST25R3916_REG_PHASE_MEASURE_AA_RESULT \ + 0x39U /** R Phase Measurement Auto Averaging Display */ +#define ST25R3916_REG_PHASE_MEASURE_RESULT 0x3AU /** R Phase Measurement Display Register */ +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF \ + 0x3BU /** RW Capacitance Measurement Configuration Register */ +#define ST25R3916_REG_CAPACITANCE_MEASURE_REF \ + 0x3CU /** RW Capacitance Measurement Reference Register */ +#define ST25R3916_REG_CAPACITANCE_MEASURE_AA_RESULT \ + 0x3DU /** R Capacitance Measurement Auto Averaging Display Reg */ +#define ST25R3916_REG_CAPACITANCE_MEASURE_RESULT \ + 0x3EU /** R Capacitance Measurement Display Register */ + +/** IC identity */ +#define ST25R3916_REG_IC_IDENTITY 0x3FU /** R Chip Id: 0 for old silicon, v2 silicon: 0x09 */ + +/** Register bit definitions */ + +#define ST25R3916_REG_IO_CONF1_single (1U << 7) +#define ST25R3916_REG_IO_CONF1_rfo2 (1U << 6) +#define ST25R3916_REG_IO_CONF1_i2c_thd1 (1U << 5) +#define ST25R3916_REG_IO_CONF1_i2c_thd0 (1U << 4) +#define ST25R3916_REG_IO_CONF1_i2c_thd_mask (3U << 4) +#define ST25R3916_REG_IO_CONF1_i2c_thd_shift (4U) +#define ST25R3916_REG_IO_CONF1_rfu (1U << 3) +#define ST25R3916_REG_IO_CONF1_out_cl1 (1U << 2) +#define ST25R3916_REG_IO_CONF1_out_cl0 (1U << 1) +#define ST25R3916_REG_IO_CONF1_out_cl_disabled (3U << 1) +#define ST25R3916_REG_IO_CONF1_out_cl_13_56MHZ (2U << 1) +#define ST25R3916_REG_IO_CONF1_out_cl_4_78MHZ (1U << 1) +#define ST25R3916_REG_IO_CONF1_out_cl_3_39MHZ (0U << 1) +#define ST25R3916_REG_IO_CONF1_out_cl_mask (3U << 1) +#define ST25R3916_REG_IO_CONF1_out_cl_shift (1U) +#define ST25R3916_REG_IO_CONF1_lf_clk_off (1U << 0) +#define ST25R3916_REG_IO_CONF1_lf_clk_off_on (1U << 0) +#define ST25R3916_REG_IO_CONF1_lf_clk_off_off (0U << 0) + +#define ST25R3916_REG_IO_CONF2_sup3V (1U << 7) +#define ST25R3916_REG_IO_CONF2_sup3V_3V (1U << 7) +#define ST25R3916_REG_IO_CONF2_sup3V_5V (0U << 7) +#define ST25R3916_REG_IO_CONF2_vspd_off (1U << 6) +#define ST25R3916_REG_IO_CONF2_aat_en (1U << 5) +#define ST25R3916_REG_IO_CONF2_miso_pd2 (1U << 4) +#define ST25R3916_REG_IO_CONF2_miso_pd1 (1U << 3) +#define ST25R3916_REG_IO_CONF2_io_drv_lvl (1U << 2) +#define ST25R3916_REG_IO_CONF2_slow_up (1U << 0) + +#define ST25R3916_REG_OP_CONTROL_en (1U << 7) +#define ST25R3916_REG_OP_CONTROL_rx_en (1U << 6) +#define ST25R3916_REG_OP_CONTROL_rx_chn (1U << 5) +#define ST25R3916_REG_OP_CONTROL_rx_man (1U << 4) +#define ST25R3916_REG_OP_CONTROL_tx_en (1U << 3) +#define ST25R3916_REG_OP_CONTROL_wu (1U << 2) +#define ST25R3916_REG_OP_CONTROL_en_fd_c1 (1U << 1) +#define ST25R3916_REG_OP_CONTROL_en_fd_c0 (1U << 0) +#define ST25R3916_REG_OP_CONTROL_en_fd_efd_off (0U << 0) +#define ST25R3916_REG_OP_CONTROL_en_fd_manual_efd_ca (1U << 0) +#define ST25R3916_REG_OP_CONTROL_en_fd_manual_efd_pdt (2U << 0) +#define ST25R3916_REG_OP_CONTROL_en_fd_auto_efd (3U << 0) +#define ST25R3916_REG_OP_CONTROL_en_fd_shift (0U) +#define ST25R3916_REG_OP_CONTROL_en_fd_mask (3U << 0) + +#define ST25R3916_REG_MODE_targ (1U << 7) +#define ST25R3916_REG_MODE_targ_targ (1U << 7) +#define ST25R3916_REG_MODE_targ_init (0U << 7) +#define ST25R3916_REG_MODE_om3 (1U << 6) +#define ST25R3916_REG_MODE_om2 (1U << 5) +#define ST25R3916_REG_MODE_om1 (1U << 4) +#define ST25R3916_REG_MODE_om0 (1U << 3) +#define ST25R3916_REG_MODE_om_bpsk_stream (0xfU << 3) +#define ST25R3916_REG_MODE_om_subcarrier_stream (0xeU << 3) +#define ST25R3916_REG_MODE_om_topaz (0x4U << 3) +#define ST25R3916_REG_MODE_om_felica (0x3U << 3) +#define ST25R3916_REG_MODE_om_iso14443b (0x2U << 3) +#define ST25R3916_REG_MODE_om_iso14443a (0x1U << 3) +#define ST25R3916_REG_MODE_om_targ_nfca (0x1U << 3) +#define ST25R3916_REG_MODE_om_targ_nfcb (0x2U << 3) +#define ST25R3916_REG_MODE_om_targ_nfcf (0x4U << 3) +#define ST25R3916_REG_MODE_om_targ_nfcip (0x7U << 3) +#define ST25R3916_REG_MODE_om_nfc (0x0U << 3) +#define ST25R3916_REG_MODE_om_mask (0xfU << 3) +#define ST25R3916_REG_MODE_om_shift (3U) +#define ST25R3916_REG_MODE_tr_am (1U << 2) +#define ST25R3916_REG_MODE_tr_am_ook (0U << 2) +#define ST25R3916_REG_MODE_tr_am_am (1U << 2) +#define ST25R3916_REG_MODE_nfc_ar1 (1U << 1) +#define ST25R3916_REG_MODE_nfc_ar0 (1U << 0) +#define ST25R3916_REG_MODE_nfc_ar_off (0U << 0) +#define ST25R3916_REG_MODE_nfc_ar_auto_rx (1U << 0) +#define ST25R3916_REG_MODE_nfc_ar_eof (2U << 0) +#define ST25R3916_REG_MODE_nfc_ar_rfu (3U << 0) +#define ST25R3916_REG_MODE_nfc_ar_mask (3U << 0) +#define ST25R3916_REG_MODE_nfc_ar_shift (0U) + +#define ST25R3916_REG_BIT_RATE_txrate_106 (0x0U << 4) +#define ST25R3916_REG_BIT_RATE_txrate_212 (0x1U << 4) +#define ST25R3916_REG_BIT_RATE_txrate_424 (0x2U << 4) +#define ST25R3916_REG_BIT_RATE_txrate_848 (0x3U << 4) +#define ST25R3916_REG_BIT_RATE_txrate_mask (0x3U << 4) +#define ST25R3916_REG_BIT_RATE_txrate_shift (4U) +#define ST25R3916_REG_BIT_RATE_rxrate_106 (0x0U << 0) +#define ST25R3916_REG_BIT_RATE_rxrate_212 (0x1U << 0) +#define ST25R3916_REG_BIT_RATE_rxrate_424 (0x2U << 0) +#define ST25R3916_REG_BIT_RATE_rxrate_848 (0x3U << 0) +#define ST25R3916_REG_BIT_RATE_rxrate_mask (0x3U << 0) +#define ST25R3916_REG_BIT_RATE_rxrate_shift (0U) + +#define ST25R3916_REG_ISO14443A_NFC_no_tx_par (1U << 7) +#define ST25R3916_REG_ISO14443A_NFC_no_tx_par_off (0U << 7) +#define ST25R3916_REG_ISO14443A_NFC_no_rx_par (1U << 6) +#define ST25R3916_REG_ISO14443A_NFC_no_rx_par_off (0U << 6) +#define ST25R3916_REG_ISO14443A_NFC_nfc_f0 (1U << 5) +#define ST25R3916_REG_ISO14443A_NFC_nfc_f0_off (0U << 5) +#define ST25R3916_REG_ISO14443A_NFC_p_len3 (1U << 4) +#define ST25R3916_REG_ISO14443A_NFC_p_len2 (1U << 3) +#define ST25R3916_REG_ISO14443A_NFC_p_len1 (1U << 2) +#define ST25R3916_REG_ISO14443A_NFC_p_len0 (1U << 1) +#define ST25R3916_REG_ISO14443A_NFC_p_len_mask (0xfU << 1) +#define ST25R3916_REG_ISO14443A_NFC_p_len_shift (1U) +#define ST25R3916_REG_ISO14443A_NFC_antcl (1U << 0) + +#define ST25R3916_REG_EMD_SUP_CONF_emd_emv (1U << 7) +#define ST25R3916_REG_EMD_SUP_CONF_emd_emv_on (1U << 7) +#define ST25R3916_REG_EMD_SUP_CONF_emd_emv_off (0U << 7) +#define ST25R3916_REG_EMD_SUP_CONF_rx_start_emv (1U << 6) +#define ST25R3916_REG_EMD_SUP_CONF_rx_start_emv_on (1U << 6) +#define ST25R3916_REG_EMD_SUP_CONF_rx_start_emv_off (0U << 6) +#define ST25R3916_REG_EMD_SUP_CONF_rfu1 (1U << 5) +#define ST25R3916_REG_EMD_SUP_CONF_rfu0 (1U << 4) +#define ST25R3916_REG_EMD_SUP_CONF_emd_thld3 (1U << 3) +#define ST25R3916_REG_EMD_SUP_CONF_emd_thld2 (1U << 2) +#define ST25R3916_REG_EMD_SUP_CONF_emd_thld1 (1U << 1) +#define ST25R3916_REG_EMD_SUP_CONF_emd_thld0 (1U << 0) +#define ST25R3916_REG_EMD_SUP_CONF_emd_thld_mask (0xfU << 0) +#define ST25R3916_REG_EMD_SUP_CONF_emd_thld_shift (0U) + +#define ST25R3916_REG_SUBC_START_TIME_rfu2 (1U << 7) +#define ST25R3916_REG_SUBC_START_TIME_rfu1 (1U << 6) +#define ST25R3916_REG_SUBC_START_TIME_rfu0 (1U << 5) +#define ST25R3916_REG_SUBC_START_TIME_sst4 (1U << 4) +#define ST25R3916_REG_SUBC_START_TIME_sst3 (1U << 3) +#define ST25R3916_REG_SUBC_START_TIME_sst2 (1U << 2) +#define ST25R3916_REG_SUBC_START_TIME_sst1 (1U << 1) +#define ST25R3916_REG_SUBC_START_TIME_sst0 (1U << 0) +#define ST25R3916_REG_SUBC_START_TIME_sst_mask (0x1fU << 0) +#define ST25R3916_REG_SUBC_START_TIME_sst_shift (0U) + +#define ST25R3916_REG_ISO14443B_1_egt2 (1U << 7) +#define ST25R3916_REG_ISO14443B_1_egt1 (1U << 6) +#define ST25R3916_REG_ISO14443B_1_egt0 (1U << 5) +#define ST25R3916_REG_ISO14443B_1_egt_shift (5U) +#define ST25R3916_REG_ISO14443B_1_egt_mask (7U << 5) +#define ST25R3916_REG_ISO14443B_1_sof_1 (1U << 3) +#define ST25R3916_REG_ISO14443B_1_sof_1_3etu (1U << 3) +#define ST25R3916_REG_ISO14443B_1_sof_1_2etu (0U << 3) +#define ST25R3916_REG_ISO14443B_1_sof_0 (1U << 4) +#define ST25R3916_REG_ISO14443B_1_sof_0_11etu (1U << 4) +#define ST25R3916_REG_ISO14443B_1_sof_0_10etu (0U << 4) +#define ST25R3916_REG_ISO14443B_1_sof_mask (3U << 3) +#define ST25R3916_REG_ISO14443B_1_eof (1U << 2) +#define ST25R3916_REG_ISO14443B_1_eof_11etu (1U << 2) +#define ST25R3916_REG_ISO14443B_1_eof_10etu (0U << 2) +#define ST25R3916_REG_ISO14443B_1_half (1U << 1) +#define ST25R3916_REG_ISO14443B_1_rx_st_om (1U << 0) + +#define ST25R3916_REG_ISO14443B_2_tr1_1 (1U << 7) +#define ST25R3916_REG_ISO14443B_2_tr1_0 (1U << 6) +#define ST25R3916_REG_ISO14443B_2_tr1_64fs32fs (1U << 6) +#define ST25R3916_REG_ISO14443B_2_tr1_80fs80fs (0U << 6) +#define ST25R3916_REG_ISO14443B_2_tr1_mask (3U << 6) +#define ST25R3916_REG_ISO14443B_2_tr1_shift (6U) +#define ST25R3916_REG_ISO14443B_2_no_sof (1U << 5) +#define ST25R3916_REG_ISO14443B_2_no_eof (1U << 4) +#define ST25R3916_REG_ISO14443B_rfu1 (1U << 3) +#define ST25R3916_REG_ISO14443B_rfu0 (1U << 2) +#define ST25R3916_REG_ISO14443B_2_f_p1 (1U << 1) +#define ST25R3916_REG_ISO14443B_2_f_p0 (1U << 0) +#define ST25R3916_REG_ISO14443B_2_f_p_96 (3U << 0) +#define ST25R3916_REG_ISO14443B_2_f_p_80 (2U << 0) +#define ST25R3916_REG_ISO14443B_2_f_p_64 (1U << 0) +#define ST25R3916_REG_ISO14443B_2_f_p_48 (0U << 0) +#define ST25R3916_REG_ISO14443B_2_f_p_mask (3U << 0) +#define ST25R3916_REG_ISO14443B_2_f_p_shift (0U) + +#define ST25R3916_REG_PASSIVE_TARGET_fdel_3 (1U << 7) +#define ST25R3916_REG_PASSIVE_TARGET_fdel_2 (1U << 6) +#define ST25R3916_REG_PASSIVE_TARGET_fdel_1 (1U << 5) +#define ST25R3916_REG_PASSIVE_TARGET_fdel_0 (1U << 4) +#define ST25R3916_REG_PASSIVE_TARGET_fdel_mask (0xfU << 4) +#define ST25R3916_REG_PASSIVE_TARGET_fdel_shift (4U) +#define ST25R3916_REG_PASSIVE_TARGET_d_ac_ap2p (1U << 3) +#define ST25R3916_REG_PASSIVE_TARGET_d_212_424_1r (1U << 2) +#define ST25R3916_REG_PASSIVE_TARGET_rfu (1U << 1) +#define ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a (1U << 0) + +#define ST25R3916_REG_STREAM_MODE_rfu (1U << 7) +#define ST25R3916_REG_STREAM_MODE_scf1 (1U << 6) +#define ST25R3916_REG_STREAM_MODE_scf0 (1U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_sc212 (0U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_sc424 (1U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_sc848 (2U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_sc1695 (3U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_bpsk848 (0U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_bpsk1695 (1U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_bpsk3390 (2U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_bpsk106 (3U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_mask (3U << 5) +#define ST25R3916_REG_STREAM_MODE_scf_shift (5U) +#define ST25R3916_REG_STREAM_MODE_scp1 (1U << 4) +#define ST25R3916_REG_STREAM_MODE_scp0 (1U << 3) +#define ST25R3916_REG_STREAM_MODE_scp_1pulse (0U << 3) +#define ST25R3916_REG_STREAM_MODE_scp_2pulses (1U << 3) +#define ST25R3916_REG_STREAM_MODE_scp_4pulses (2U << 3) +#define ST25R3916_REG_STREAM_MODE_scp_8pulses (3U << 3) +#define ST25R3916_REG_STREAM_MODE_scp_mask (3U << 3) +#define ST25R3916_REG_STREAM_MODE_scp_shift (3U) +#define ST25R3916_REG_STREAM_MODE_stx2 (1U << 2) +#define ST25R3916_REG_STREAM_MODE_stx1 (1U << 1) +#define ST25R3916_REG_STREAM_MODE_stx0 (1U << 0) +#define ST25R3916_REG_STREAM_MODE_stx_106 (0U << 0) +#define ST25R3916_REG_STREAM_MODE_stx_212 (1U << 0) +#define ST25R3916_REG_STREAM_MODE_stx_424 (2U << 0) +#define ST25R3916_REG_STREAM_MODE_stx_848 (3U << 0) +#define ST25R3916_REG_STREAM_MODE_stx_mask (7U << 0) +#define ST25R3916_REG_STREAM_MODE_stx_shift (0U) + +#define ST25R3916_REG_AUX_no_crc_rx (1U << 7) +#define ST25R3916_REG_AUX_rfu (1U << 6) +#define ST25R3916_REG_AUX_nfc_id1 (1U << 5) +#define ST25R3916_REG_AUX_nfc_id0 (1U << 4) +#define ST25R3916_REG_AUX_nfc_id_7bytes (1U << 4) +#define ST25R3916_REG_AUX_nfc_id_4bytes (0U << 4) +#define ST25R3916_REG_AUX_nfc_id_mask (3U << 4) +#define ST25R3916_REG_AUX_nfc_id_shift (4U) +#define ST25R3916_REG_AUX_mfaz_cl90 (1U << 3) +#define ST25R3916_REG_AUX_dis_corr (1U << 2) +#define ST25R3916_REG_AUX_dis_corr_coherent (1U << 2) +#define ST25R3916_REG_AUX_dis_corr_correlator (0U << 2) +#define ST25R3916_REG_AUX_nfc_n1 (1U << 1) +#define ST25R3916_REG_AUX_nfc_n0 (1U << 0) +#define ST25R3916_REG_AUX_nfc_n_mask (3U << 0) +#define ST25R3916_REG_AUX_nfc_n_shift (0U) + +#define ST25R3916_REG_RX_CONF1_ch_sel (1U << 7) +#define ST25R3916_REG_RX_CONF1_ch_sel_PM (1U << 7) +#define ST25R3916_REG_RX_CONF1_ch_sel_AM (0U << 7) +#define ST25R3916_REG_RX_CONF1_lp2 (1U << 6) +#define ST25R3916_REG_RX_CONF1_lp1 (1U << 5) +#define ST25R3916_REG_RX_CONF1_lp0 (1U << 4) +#define ST25R3916_REG_RX_CONF1_lp_1200khz (0U << 4) +#define ST25R3916_REG_RX_CONF1_lp_600khz (1U << 4) +#define ST25R3916_REG_RX_CONF1_lp_300khz (2U << 4) +#define ST25R3916_REG_RX_CONF1_lp_2000khz (4U << 4) +#define ST25R3916_REG_RX_CONF1_lp_7000khz (5U << 4) +#define ST25R3916_REG_RX_CONF1_lp_mask (7U << 4) +#define ST25R3916_REG_RX_CONF1_lp_shift (4U) +#define ST25R3916_REG_RX_CONF1_z600k (1U << 3) +#define ST25R3916_REG_RX_CONF1_h200 (1U << 2) +#define ST25R3916_REG_RX_CONF1_h80 (1U << 1) +#define ST25R3916_REG_RX_CONF1_z12k (1U << 0) +#define ST25R3916_REG_RX_CONF1_hz_60_400khz (0U << 0) +#define ST25R3916_REG_RX_CONF1_hz_60_200khz (4U << 0) +#define ST25R3916_REG_RX_CONF1_hz_40_80khz (2U << 0) +#define ST25R3916_REG_RX_CONF1_hz_12_200khz (1U << 0) +#define ST25R3916_REG_RX_CONF1_hz_12_80khz (3U << 0) +#define ST25R3916_REG_RX_CONF1_hz_12_200khz_alt (5U << 0) +#define ST25R3916_REG_RX_CONF1_hz_600_400khz (8U << 0) +#define ST25R3916_REG_RX_CONF1_hz_600_200khz (12U << 0) +#define ST25R3916_REG_RX_CONF1_hz_mask (0xfU << 0) +#define ST25R3916_REG_RX_CONF1_hz_shift (0U) + +#define ST25R3916_REG_RX_CONF2_demod_mode (1U << 7) +#define ST25R3916_REG_RX_CONF2_amd_sel (1U << 6) +#define ST25R3916_REG_RX_CONF2_amd_sel_mixer (1U << 6) +#define ST25R3916_REG_RX_CONF2_amd_sel_peak (0U << 6) +#define ST25R3916_REG_RX_CONF2_sqm_dyn (1U << 5) +#define ST25R3916_REG_RX_CONF2_pulz_61 (1U << 4) +#define ST25R3916_REG_RX_CONF2_agc_en (1U << 3) +#define ST25R3916_REG_RX_CONF2_agc_m (1U << 2) +#define ST25R3916_REG_RX_CONF2_agc_alg (1U << 1) +#define ST25R3916_REG_RX_CONF2_agc6_3 (1U << 0) + +#define ST25R3916_REG_RX_CONF3_rg1_am2 (1U << 7) +#define ST25R3916_REG_RX_CONF3_rg1_am1 (1U << 6) +#define ST25R3916_REG_RX_CONF3_rg1_am0 (1U << 5) +#define ST25R3916_REG_RX_CONF3_rg1_am_mask (0x7U << 5) +#define ST25R3916_REG_RX_CONF3_rg1_am_shift (5U) +#define ST25R3916_REG_RX_CONF3_rg1_pm2 (1U << 4) +#define ST25R3916_REG_RX_CONF3_rg1_pm1 (1U << 3) +#define ST25R3916_REG_RX_CONF3_rg1_pm0 (1U << 2) +#define ST25R3916_REG_RX_CONF3_rg1_pm_mask (0x7U << 2) +#define ST25R3916_REG_RX_CONF3_rg1_pm_shift (2U) +#define ST25R3916_REG_RX_CONF3_lf_en (1U << 1) +#define ST25R3916_REG_RX_CONF3_lf_op (1U << 0) + +#define ST25R3916_REG_RX_CONF4_rg2_am3 (1U << 7) +#define ST25R3916_REG_RX_CONF4_rg2_am2 (1U << 6) +#define ST25R3916_REG_RX_CONF4_rg2_am1 (1U << 5) +#define ST25R3916_REG_RX_CONF4_rg2_am0 (1U << 4) +#define ST25R3916_REG_RX_CONF4_rg2_am_mask (0xfU << 4) +#define ST25R3916_REG_RX_CONF4_rg2_am_shift (4U) +#define ST25R3916_REG_RX_CONF4_rg2_pm3 (1U << 3) +#define ST25R3916_REG_RX_CONF4_rg2_pm2 (1U << 2) +#define ST25R3916_REG_RX_CONF4_rg2_pm1 (1U << 1) +#define ST25R3916_REG_RX_CONF4_rg2_pm0 (1U << 0) +#define ST25R3916_REG_RX_CONF4_rg2_pm_mask (0xfU << 0) +#define ST25R3916_REG_RX_CONF4_rg2_pm_shift (0U) + +#define ST25R3916_REG_P2P_RX_CONF_ook_fd (1U << 7) +#define ST25R3916_REG_P2P_RX_CONF_ook_rc1 (1U << 6) +#define ST25R3916_REG_P2P_RX_CONF_ook_rc0 (1U << 5) +#define ST25R3916_REG_P2P_RX_CONF_ook_thd1 (1U << 4) +#define ST25R3916_REG_P2P_RX_CONF_ook_thd0 (1U << 3) +#define ST25R3916_REG_P2P_RX_CONF_ask_rc1 (1U << 2) +#define ST25R3916_REG_P2P_RX_CONF_ask_rc0 (1U << 1) +#define ST25R3916_REG_P2P_RX_CONF_ask_thd (1U << 0) + +#define ST25R3916_REG_CORR_CONF1_corr_s7 (1U << 7) +#define ST25R3916_REG_CORR_CONF1_corr_s6 (1U << 6) +#define ST25R3916_REG_CORR_CONF1_corr_s5 (1U << 5) +#define ST25R3916_REG_CORR_CONF1_corr_s4 (1U << 4) +#define ST25R3916_REG_CORR_CONF1_corr_s3 (1U << 3) +#define ST25R3916_REG_CORR_CONF1_corr_s2 (1U << 2) +#define ST25R3916_REG_CORR_CONF1_corr_s1 (1U << 1) +#define ST25R3916_REG_CORR_CONF1_corr_s0 (1U << 0) + +#define ST25R3916_REG_CORR_CONF2_rfu5 (1U << 7) +#define ST25R3916_REG_CORR_CONF2_rfu4 (1U << 6) +#define ST25R3916_REG_CORR_CONF2_rfu3 (1U << 5) +#define ST25R3916_REG_CORR_CONF2_rfu2 (1U << 4) +#define ST25R3916_REG_CORR_CONF2_rfu1 (1U << 3) +#define ST25R3916_REG_CORR_CONF2_rfu0 (1U << 2) +#define ST25R3916_REG_CORR_CONF2_corr_s9 (1U << 1) +#define ST25R3916_REG_CORR_CONF2_corr_s8 (1U << 0) + +#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc2 (1U << 7) +#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc1 (1U << 6) +#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc0 (1U << 5) +#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_no_trigger (0U << 5) +#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_erx (1U << 5) +#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_srx (2U << 5) +#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_etx_nfc (3U << 5) +#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_mask (7U << 5) +#define ST25R3916_REG_TIMER_EMV_CONTROL_gptc_shift (5U) +#define ST25R3916_REG_TIMER_EMV_CONTROL_rfu (1U << 4) +#define ST25R3916_REG_TIMER_EMV_CONTROL_mrt_step (1U << 3) +#define ST25R3916_REG_TIMER_EMV_CONTROL_mrt_step_512 (1U << 3) +#define ST25R3916_REG_TIMER_EMV_CONTROL_mrt_step_64 (0U << 3) +#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_nfc (1U << 2) +#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_nfc_on (1U << 2) +#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_nfc_off (0U << 2) +#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv (1U << 1) +#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv_on (1U << 1) +#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv_off (0U << 1) +#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_step (1U << 0) +#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_step_64fc (0U << 0) +#define ST25R3916_REG_TIMER_EMV_CONTROL_nrt_step_4096_fc (1U << 0) + +#define ST25R3916_REG_FIFO_STATUS2_fifo_b9 (1U << 7) +#define ST25R3916_REG_FIFO_STATUS2_fifo_b8 (1U << 6) +#define ST25R3916_REG_FIFO_STATUS2_fifo_b_mask (3U << 6) +#define ST25R3916_REG_FIFO_STATUS2_fifo_b_shift (6U) +#define ST25R3916_REG_FIFO_STATUS2_fifo_unf (1U << 5) +#define ST25R3916_REG_FIFO_STATUS2_fifo_ovr (1U << 4) +#define ST25R3916_REG_FIFO_STATUS2_fifo_lb2 (1U << 3) +#define ST25R3916_REG_FIFO_STATUS2_fifo_lb1 (1U << 2) +#define ST25R3916_REG_FIFO_STATUS2_fifo_lb0 (1U << 1) +#define ST25R3916_REG_FIFO_STATUS2_fifo_lb_mask (7U << 1) +#define ST25R3916_REG_FIFO_STATUS2_fifo_lb_shift (1U) +#define ST25R3916_REG_FIFO_STATUS2_np_lb (1U << 0) + +#define ST25R3916_REG_COLLISION_STATUS_c_byte3 (1U << 7) +#define ST25R3916_REG_COLLISION_STATUS_c_byte2 (1U << 6) +#define ST25R3916_REG_COLLISION_STATUS_c_byte1 (1U << 5) +#define ST25R3916_REG_COLLISION_STATUS_c_byte0 (1U << 4) +#define ST25R3916_REG_COLLISION_STATUS_c_byte_mask (0xfU << 4) +#define ST25R3916_REG_COLLISION_STATUS_c_byte_shift (4U) +#define ST25R3916_REG_COLLISION_STATUS_c_bit2 (1U << 3) +#define ST25R3916_REG_COLLISION_STATUS_c_bit1 (1U << 2) +#define ST25R3916_REG_COLLISION_STATUS_c_bit0 (1U << 1) +#define ST25R3916_REG_COLLISION_STATUS_c_pb (1U << 0) +#define ST25R3916_REG_COLLISION_STATUS_c_bit_mask (3U << 1) +#define ST25R3916_REG_COLLISION_STATUS_c_bit_shift (1U) + +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_rfu (1U << 7) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_rfu1 (1U << 6) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_rfu2 (1U << 5) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_rfu3 (1U << 4) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state3 (1U << 3) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state2 (1U << 2) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state1 (1U << 1) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state0 (1U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_power_off (0x0U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_idle (0x1U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_ready_l1 (0x2U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_ready_l2 (0x3U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu4 (0x4U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_active (0x5U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu6 (0x6U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu7 (0x7U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu8 (0x8U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_halt (0x9U << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_ready_l1_x (0xaU << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_ready_l2_x (0xbU << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_rfu12 (0xcU << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_st_active_x (0xdU << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state_mask (0xfU << 0) +#define ST25R3916_REG_PASSIVE_TARGET_STATUS_pta_state_shift (0U) + +#define ST25R3916_REG_NUM_TX_BYTES2_ntx4 (1U << 7) +#define ST25R3916_REG_NUM_TX_BYTES2_ntx3 (1U << 6) +#define ST25R3916_REG_NUM_TX_BYTES2_ntx2 (1U << 5) +#define ST25R3916_REG_NUM_TX_BYTES2_ntx1 (1U << 4) +#define ST25R3916_REG_NUM_TX_BYTES2_ntx0 (1U << 3) +#define ST25R3916_REG_NUM_TX_BYTES2_ntx_mask (0x1fU << 3) +#define ST25R3916_REG_NUM_TX_BYTES2_ntx_shift (3U) +#define ST25R3916_REG_NUM_TX_BYTES2_nbtx2 (1U << 2) +#define ST25R3916_REG_NUM_TX_BYTES2_nbtx1 (1U << 1) +#define ST25R3916_REG_NUM_TX_BYTES2_nbtx0 (1U << 0) +#define ST25R3916_REG_NUM_TX_BYTES2_nbtx_mask (7U << 0) +#define ST25R3916_REG_NUM_TX_BYTES2_nbtx_shift (0U) + +#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rfu1 (1U << 7) +#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rfu0 (1U << 6) +#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rate1 (1U << 5) +#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rate0 (1U << 4) +#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rate_mask (0x3U << 4) +#define ST25R3916_REG_NFCIP1_BIT_RATE_nfc_rate_shift (4U) +#define ST25R3916_REG_NFCIP1_BIT_RATE_ppt2_on (1U << 3) +#define ST25R3916_REG_NFCIP1_BIT_RATE_gpt_on (1U << 2) +#define ST25R3916_REG_NFCIP1_BIT_RATE_nrt_on (1U << 1) +#define ST25R3916_REG_NFCIP1_BIT_RATE_mrt_on (1U << 0) + +#define ST25R3916_REG_TX_DRIVER_am_mod3 (1U << 7) +#define ST25R3916_REG_TX_DRIVER_am_mod2 (1U << 6) +#define ST25R3916_REG_TX_DRIVER_am_mod1 (1U << 5) +#define ST25R3916_REG_TX_DRIVER_am_mod0 (1U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_5percent (0x0U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_6percent (0x1U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_7percent (0x2U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_8percent (0x3U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_9percent (0x4U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_10percent (0x5U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_11percent (0x6U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_12percent (0x7U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_13percent (0x8U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_14percent (0x9U << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_15percent (0xaU << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_17percent (0xbU << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_19percent (0xcU << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_22percent (0xdU << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_26percent (0xeU << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_40percent (0xfU << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_mask (0xfU << 4) +#define ST25R3916_REG_TX_DRIVER_am_mod_shift (4U) +#define ST25R3916_REG_TX_DRIVER_d_res3 (1U << 3) +#define ST25R3916_REG_TX_DRIVER_d_res2 (1U << 2) +#define ST25R3916_REG_TX_DRIVER_d_res1 (1U << 1) +#define ST25R3916_REG_TX_DRIVER_d_res0 (1U << 0) +#define ST25R3916_REG_TX_DRIVER_d_res_mask (0xfU << 0) +#define ST25R3916_REG_TX_DRIVER_d_res_shift (0U) + +#define ST25R3916_REG_PT_MOD_ptm_res3 (1U << 7) +#define ST25R3916_REG_PT_MOD_ptm_res2 (1U << 6) +#define ST25R3916_REG_PT_MOD_ptm_res1 (1U << 5) +#define ST25R3916_REG_PT_MOD_ptm_res0 (1U << 4) +#define ST25R3916_REG_PT_MOD_ptm_res_mask (0xfU << 4) +#define ST25R3916_REG_PT_MOD_ptm_res_shift (4U) +#define ST25R3916_REG_PT_MOD_pt_res3 (1U << 3) +#define ST25R3916_REG_PT_MOD_pt_res2 (1U << 2) +#define ST25R3916_REG_PT_MOD_pt_res1 (1U << 1) +#define ST25R3916_REG_PT_MOD_pt_res0 (1U << 0) +#define ST25R3916_REG_PT_MOD_pt_res_mask (0xfU << 0) +#define ST25R3916_REG_PT_MOD_pt_res_shift (0U) + +#define ST25R3916_REG_AUX_MOD_dis_reg_am (1U << 7) +#define ST25R3916_REG_AUX_MOD_lm_ext_pol (1U << 6) +#define ST25R3916_REG_AUX_MOD_lm_ext (1U << 5) +#define ST25R3916_REG_AUX_MOD_lm_dri (1U << 4) +#define ST25R3916_REG_AUX_MOD_res_am (1U << 3) +#define ST25R3916_REG_AUX_MOD_rfu2 (1U << 2) +#define ST25R3916_REG_AUX_MOD_rfu1 (1U << 1) +#define ST25R3916_REG_AUX_MOD_rfu0 (1U << 0) + +#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_t3 (1U << 7) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_t2 (1U << 6) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_t1 (1U << 5) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_t0 (1U << 4) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_mask (0xfU << 4) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_rat_shift (4U) +#define ST25R3916_REG_TX_DRIVER_TIMING_rfu (1U << 3) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m2 (1U << 2) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m1 (1U << 1) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m0 (1U << 0) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m_mask (0x7U << 0) +#define ST25R3916_REG_TX_DRIVER_TIMING_d_tim_m_shift (0U) + +#define ST25R3916_REG_RES_AM_MOD_fa3_f (1U << 7) +#define ST25R3916_REG_RES_AM_MOD_md_res6 (1U << 6) +#define ST25R3916_REG_RES_AM_MOD_md_res5 (1U << 5) +#define ST25R3916_REG_RES_AM_MOD_md_res4 (1U << 4) +#define ST25R3916_REG_RES_AM_MOD_md_res3 (1U << 3) +#define ST25R3916_REG_RES_AM_MOD_md_res2 (1U << 2) +#define ST25R3916_REG_RES_AM_MOD_md_res1 (1U << 1) +#define ST25R3916_REG_RES_AM_MOD_md_res0 (1U << 0) +#define ST25R3916_REG_RES_AM_MOD_md_res_mask (0x7FU << 0) +#define ST25R3916_REG_RES_AM_MOD_md_res_shift (0U) + +#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_r3 (1U << 7) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_r2 (1U << 6) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_r1 (1U << 5) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_r0 (1U << 4) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_mask (0xfU << 4) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_rat_shift (4U) +#define ST25R3916_REG_TX_DRIVER_STATUS_rfu (1U << 3) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_r2 (1U << 2) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_r1 (1U << 1) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_r0 (1U << 0) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_mask (0x7U << 0) +#define ST25R3916_REG_TX_DRIVER_STATUS_d_tim_shift (0U) + +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_l2a (1U << 6) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_l1a (1U << 5) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_l0a (1U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_75mV (0x0U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_105mV (0x1U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_150mV (0x2U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_205mV (0x3U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_290mV (0x4U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_400mV (0x5U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_560mV (0x6U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_800mV (0x7U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_mask (7U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_shift (4U) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_t3a (1U << 3) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_t2a (1U << 2) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_t1a (1U << 1) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_t0a (1U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_75mV (0x0U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_105mV (0x1U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_150mV (0x2U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_205mV (0x3U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_290mV (0x4U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_400mV (0x5U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_560mV (0x6U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_800mV (0x7U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_25mV (0x8U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_33mV (0x9U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_47mV (0xAU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_64mV (0xBU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_90mV (0xCU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_125mV (0xDU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_175mV (0xEU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_250mV (0xFU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_mask (0xfU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_shift (0U) + +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_l2d (1U << 6) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_l1d (1U << 5) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_l0d (1U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_75mV (0x0U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_105mV (0x1U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_150mV (0x2U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_205mV (0x3U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_290mV (0x4U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_400mV (0x5U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_560mV (0x6U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_800mV (0x7U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_mask (7U << 4) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_shift (4U) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_t3d (1U << 3) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_t2d (1U << 2) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_t1d (1U << 1) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_t0d (1U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_75mV (0x0U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_105mV (0x1U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_150mV (0x2U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_205mV (0x3U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_290mV (0x4U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_400mV (0x5U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_560mV (0x6U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_800mV (0x7U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_25mV (0x8U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_33mV (0x9U << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_47mV (0xAU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_64mV (0xBU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_90mV (0xCU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_125mV (0xDU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_175mV (0xEU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_250mV (0xFU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_mask (0xfU << 0) +#define ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_shift (0U) + +#define ST25R3916_REG_REGULATOR_CONTROL_reg_s (1U << 7) +#define ST25R3916_REG_REGULATOR_CONTROL_rege_3 (1U << 6) +#define ST25R3916_REG_REGULATOR_CONTROL_rege_2 (1U << 5) +#define ST25R3916_REG_REGULATOR_CONTROL_rege_1 (1U << 4) +#define ST25R3916_REG_REGULATOR_CONTROL_rege_0 (1U << 3) +#define ST25R3916_REG_REGULATOR_CONTROL_rege_mask (0xfU << 3) +#define ST25R3916_REG_REGULATOR_CONTROL_rege_shift (3U) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv2 (2U << 2) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv1 (1U << 1) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv0 (1U << 0) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd (0U) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_a (1U) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_d (2U) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_rf (3U) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd_am (4U) +#define ST25R3916_REG_REGULATOR_CONTROL_rfu (5U) +#define ST25R3916_REG_REGULATOR_CONTROL_rfu1 (6U) +#define ST25R3916_REG_REGULATOR_CONTROL_rfu2 (7U) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_mask (7U) +#define ST25R3916_REG_REGULATOR_CONTROL_mpsv_shift (0U) + +#define ST25R3916_REG_REGULATOR_RESULT_reg_3 (1U << 7) +#define ST25R3916_REG_REGULATOR_RESULT_reg_2 (1U << 6) +#define ST25R3916_REG_REGULATOR_RESULT_reg_1 (1U << 5) +#define ST25R3916_REG_REGULATOR_RESULT_reg_0 (1U << 4) +#define ST25R3916_REG_REGULATOR_RESULT_reg_mask (0xfU << 4) +#define ST25R3916_REG_REGULATOR_RESULT_reg_shift (4U) +#define ST25R3916_REG_REGULATOR_RESULT_i_lim (1U << 0) + +#define ST25R3916_REG_RSSI_RESULT_rssi_am_3 (1U << 7) +#define ST25R3916_REG_RSSI_RESULT_rssi_am_2 (1U << 6) +#define ST25R3916_REG_RSSI_RESULT_rssi_am_1 (1U << 5) +#define ST25R3916_REG_RSSI_RESULT_rssi_am_0 (1U << 4) +#define ST25R3916_REG_RSSI_RESULT_rssi_am_mask (0xfU << 4) +#define ST25R3916_REG_RSSI_RESULT_rssi_am_shift (4U) +#define ST25R3916_REG_RSSI_RESULT_rssi_pm3 (1U << 3) +#define ST25R3916_REG_RSSI_RESULT_rssi_pm2 (1U << 2) +#define ST25R3916_REG_RSSI_RESULT_rssi_pm1 (1U << 1) +#define ST25R3916_REG_RSSI_RESULT_rssi_pm0 (1U << 0) +#define ST25R3916_REG_RSSI_RESULT_rssi_pm_mask (0xfU << 0) +#define ST25R3916_REG_RSSI_RESULT_rssi_pm_shift (0U) + +#define ST25R3916_REG_GAIN_RED_STATE_gs_am_3 (1U << 7) +#define ST25R3916_REG_GAIN_RED_STATE_gs_am_2 (1U << 6) +#define ST25R3916_REG_GAIN_RED_STATE_gs_am_1 (1U << 5) +#define ST25R3916_REG_GAIN_RED_STATE_gs_am_0 (1U << 4) +#define ST25R3916_REG_GAIN_RED_STATE_gs_am_mask (0xfU << 4) +#define ST25R3916_REG_GAIN_RED_STATE_gs_am_shift (4U) +#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_3 (1U << 3) +#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_2 (1U << 2) +#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_1 (1U << 1) +#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_0 (1U << 0) +#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_mask (0xfU << 0) +#define ST25R3916_REG_GAIN_RED_STATE_gs_pm_shift (0U) + +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal4 (1U << 7) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal3 (1U << 6) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal2 (1U << 5) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal1 (1U << 4) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal0 (1U << 3) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal_mask (0x1fU << 3) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_mcal_shift (3U) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g2 (1U << 2) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g1 (1U << 1) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g0 (1U << 0) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g_mask (7U << 0) +#define ST25R3916_REG_CAP_SENSOR_CONTROL_cs_g_shift (0U) + +#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal4 (1U << 7) +#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal3 (1U << 6) +#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal2 (1U << 5) +#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal1 (1U << 4) +#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal0 (1U << 3) +#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_mask (0x1fU << 3) +#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_shift (3U) +#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_end (1U << 2) +#define ST25R3916_REG_CAP_SENSOR_RESULT_cs_cal_err (1U << 1) + +#define ST25R3916_REG_AUX_DISPLAY_a_cha (1U << 7) +#define ST25R3916_REG_AUX_DISPLAY_efd_o (1U << 6) +#define ST25R3916_REG_AUX_DISPLAY_tx_on (1U << 5) +#define ST25R3916_REG_AUX_DISPLAY_osc_ok (1U << 4) +#define ST25R3916_REG_AUX_DISPLAY_rx_on (1U << 3) +#define ST25R3916_REG_AUX_DISPLAY_rx_act (1U << 2) +#define ST25R3916_REG_AUX_DISPLAY_en_peer (1U << 1) +#define ST25R3916_REG_AUX_DISPLAY_en_ac (1U << 0) + +#define ST25R3916_REG_OVERSHOOT_CONF1_ov_tx_mode1 (1U << 7) +#define ST25R3916_REG_OVERSHOOT_CONF1_ov_tx_mode0 (1U << 6) +#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern13 (1U << 5) +#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern12 (1U << 4) +#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern11 (1U << 3) +#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern10 (1U << 2) +#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern9 (1U << 1) +#define ST25R3916_REG_OVERSHOOT_CONF1_ov_pattern8 (1U << 0) + +#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern7 (1U << 7) +#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern6 (1U << 6) +#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern5 (1U << 5) +#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern4 (1U << 4) +#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern3 (1U << 3) +#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern2 (1U << 2) +#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern1 (1U << 1) +#define ST25R3916_REG_OVERSHOOT_CONF2_ov_pattern0 (1U << 0) + +#define ST25R3916_REG_UNDERSHOOT_CONF1_un_tx_mode1 (1U << 7) +#define ST25R3916_REG_UNDERSHOOT_CONF1_un_tx_mode0 (1U << 6) +#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern13 (1U << 5) +#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern12 (1U << 4) +#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern11 (1U << 3) +#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern10 (1U << 2) +#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern9 (1U << 1) +#define ST25R3916_REG_UNDERSHOOT_CONF1_un_pattern8 (1U << 0) + +#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern7 (1U << 7) +#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern6 (1U << 6) +#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern5 (1U << 5) +#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern4 (1U << 4) +#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern3 (1U << 3) +#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern2 (1U << 2) +#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern1 (1U << 1) +#define ST25R3916_REG_UNDERSHOOT_CONF2_un_pattern0 (1U << 0) + +#define ST25R3916_REG_WUP_TIMER_CONTROL_wur (1U << 7) +#define ST25R3916_REG_WUP_TIMER_CONTROL_wut2 (1U << 6) +#define ST25R3916_REG_WUP_TIMER_CONTROL_wut1 (1U << 5) +#define ST25R3916_REG_WUP_TIMER_CONTROL_wut0 (1U << 4) +#define ST25R3916_REG_WUP_TIMER_CONTROL_wut_mask (7U << 4) +#define ST25R3916_REG_WUP_TIMER_CONTROL_wut_shift (4U) +#define ST25R3916_REG_WUP_TIMER_CONTROL_wto (1U << 3) +#define ST25R3916_REG_WUP_TIMER_CONTROL_wam (1U << 2) +#define ST25R3916_REG_WUP_TIMER_CONTROL_wph (1U << 1) +#define ST25R3916_REG_WUP_TIMER_CONTROL_wcap (1U << 0) + +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d3 (1U << 7) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d2 (1U << 6) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d1 (1U << 5) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d0 (1U << 4) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d_mask (0xfU << 4) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_d_shift (4U) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aam (1U << 3) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aew1 (1U << 2) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aew0 (1U << 1) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aew_mask (0x3U << 1) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_aew_shift (1U) +#define ST25R3916_REG_AMPLITUDE_MEASURE_CONF_am_ae (1U << 0) + +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d3 (1U << 7) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d2 (1U << 6) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d1 (1U << 5) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d0 (1U << 4) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d_mask (0xfU << 4) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_d_shift (4U) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aam (1U << 3) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aew1 (1U << 2) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aew0 (1U << 1) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aew_mask (0x3U << 1) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_aew_shift (1U) +#define ST25R3916_REG_PHASE_MEASURE_CONF_pm_ae (1U << 0) + +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d3 (1U << 7) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d2 (1U << 6) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d1 (1U << 5) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d0 (1U << 4) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d_mask (0xfU << 4) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_d_shift (4U) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aam (1U << 3) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aew1 (1U << 2) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aew0 (1U << 1) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aew_mask (0x3U << 1) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_aew_shift (1U) +#define ST25R3916_REG_CAPACITANCE_MEASURE_CONF_cm_ae (1U << 0) + +#define ST25R3916_REG_IC_IDENTITY_ic_type4 (1U << 7) +#define ST25R3916_REG_IC_IDENTITY_ic_type3 (1U << 6) +#define ST25R3916_REG_IC_IDENTITY_ic_type2 (1U << 5) +#define ST25R3916_REG_IC_IDENTITY_ic_type1 (1U << 4) +#define ST25R3916_REG_IC_IDENTITY_ic_type0 (1U << 3) +#define ST25R3916_REG_IC_IDENTITY_ic_type_st25r3916 (5U << 3) +#define ST25R3916_REG_IC_IDENTITY_ic_type_mask (0x1fU << 3) +#define ST25R3916_REG_IC_IDENTITY_ic_type_shift (3U) +#define ST25R3916_REG_IC_IDENTITY_ic_rev2 (1U << 2) +#define ST25R3916_REG_IC_IDENTITY_ic_rev1 (1U << 1) +#define ST25R3916_REG_IC_IDENTITY_ic_rev0 (1U << 0) +#define ST25R3916_REG_IC_IDENTITY_ic_rev_v0 (0U << 0) +#define ST25R3916_REG_IC_IDENTITY_ic_rev_mask (7U << 0) +#define ST25R3916_REG_IC_IDENTITY_ic_rev_shift (0U) + +/** Read register + * + * @param handle - pointer t FuriHalSpiBusHandle instance + * @param reg - register address + * @param val - pointer to the variable to store the read value + */ +void st25r3916_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val); + +/** Read multiple registers + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param reg_start - start register address + * @param values - pointer to the buffer to store the read values + * @param length - number of registers to read + */ +void st25r3916_read_burst_regs( + FuriHalSpiBusHandle* handle, + uint8_t reg_start, + uint8_t* values, + uint8_t length); + +/** Write register + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param reg - register address + * @param val - value to write + */ +void st25r3916_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val); + +/** Write multiple registers + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param reg_start - start register address + * @param values - pointer to buffer to write + * @param length - number of registers to write + */ +void st25r3916_write_burst_regs( + FuriHalSpiBusHandle* handle, + uint8_t reg_start, + const uint8_t* values, + uint8_t length); + +/** Write fifo register + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param buff - buffer to write to FIFO + * @param length - number of bytes to write + */ +void st25r3916_reg_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* buff, size_t length); + +/** Read fifo register + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param buff - buffer to store the read values + * @param length - number of bytes to read + */ +void st25r3916_reg_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* buff, size_t length); + +/** Write PTA memory register + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param values - pointer to buffer to write + * @param length - number of bytes to write + */ +void st25r3916_write_pta_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, size_t length); + +/** Read PTA memory register + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param values - buffer to store the read values + * @param length - number of bytes to read + */ +void st25r3916_read_pta_mem(FuriHalSpiBusHandle* handle, uint8_t* values, size_t length); + +/** Write PTF memory register + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param values - pointer to buffer to write + * @param length - number of bytes to write + */ +void st25r3916_write_ptf_mem(FuriHalSpiBusHandle* handle, const uint8_t* values, size_t length); + +/** Read PTTSN memory register + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param values - pointer to buffer to write + * @param length - number of bytes to write + */ +void st25r3916_write_pttsn_mem(FuriHalSpiBusHandle* handle, uint8_t* values, size_t length); + +/** Send Direct command + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param cmd - direct command + */ +void st25r3916_direct_cmd(FuriHalSpiBusHandle* handle, uint8_t cmd); + +/** Read test register + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param reg - register address + * @param val - pointer to the variable to store the read value + */ +void st25r3916_read_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* val); + +/** Write test register + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param reg - register address + * @param val - value to write + */ +void st25r3916_write_test_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t val); + +/** Clear register bits + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param reg - register address + * @param clr_mask - bit mask to clear + */ +void st25r3916_clear_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t clr_mask); + +/** Set register bits + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param reg - register address + * @param set_mask - bit mask to set + */ +void st25r3916_set_reg_bits(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t set_mask); + +/** Change register bits + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param reg - register address + * @param mask - bit mask to change + * @param value - new register value to write + */ +void st25r3916_change_reg_bits( + FuriHalSpiBusHandle* handle, + uint8_t reg, + uint8_t mask, + uint8_t value); + +/** Modify register + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param reg - register address + * @param clr_mask - bit mask to clear + * @param set_mask - bit mask to set + */ +void st25r3916_modify_reg( + FuriHalSpiBusHandle* handle, + uint8_t reg, + uint8_t clr_mask, + uint8_t set_mask); + +/** Change test register bits + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param reg - register address + * @param mask - bit mask to change + * @param value - new register value to write + */ +void st25r3916_change_test_reg_bits( + FuriHalSpiBusHandle* handle, + uint8_t reg, + uint8_t mask, + uint8_t value); + +/** Check register + * + * @param handle - pointer to FuriHalSpiBusHandle instance + * @param reg - register address + * @param mask - bit mask to check + * @param val - expected register value + * + * @return true if register value matches the expected value, false otherwise + */ +bool st25r3916_check_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t mask, uint8_t val); + +#ifdef __cplusplus +} +#endif diff --git a/lib/err.h b/lib/err.h new file mode 100644 index 00000000000..a0e93874e6c --- /dev/null +++ b/lib/err.h @@ -0,0 +1,4 @@ +#pragma once +#include + +#define err(...) FURI_LOG_E("Heatshrink", "Error: %d-%s", __VA_ARGS__) \ No newline at end of file diff --git a/lib/fatfs/diskio.h b/lib/fatfs/diskio.h index 5b61e570ba9..0ccc9d461d0 100644 --- a/lib/fatfs/diskio.h +++ b/lib/fatfs/diskio.h @@ -37,7 +37,6 @@ DSTATUS disk_status (BYTE pdrv); DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count); DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); -DWORD get_fattime (void); /* Disk Status Bits (DSTATUS) */ diff --git a/lib/fatfs/ff.c b/lib/fatfs/ff.c index 85ab9736eb5..86646c32ca6 100644 --- a/lib/fatfs/ff.c +++ b/lib/fatfs/ff.c @@ -1927,7 +1927,7 @@ DWORD xsum32 ( static void get_xdir_info ( - BYTE* dirb, /* Pointer to the direcotry entry block 85+C0+C1s */ + BYTE* dirb, /* Pointer to the directory entry block 85+C0+C1s */ FILINFO* fno /* Buffer to store the extracted file information */ ) { @@ -1971,17 +1971,17 @@ void get_xdir_info ( /*-----------------------------------*/ -/* exFAT: Get a directry entry block */ +/* exFAT: Get a directory entry block */ /*-----------------------------------*/ static FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ - DIR* dp /* Pointer to the reading direcotry object pointing the 85 entry */ + DIR* dp /* Pointer to the reading directory object pointing the 85 entry */ ) { FRESULT res; UINT i, sz_ent; - BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */ + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory directory entry block 85+C0+C1s */ /* Load 85 entry */ @@ -2026,7 +2026,7 @@ FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ /*------------------------------------------------*/ static FRESULT load_obj_dir ( - DIR* dp, /* Blank directory object to be used to access containing direcotry */ + DIR* dp, /* Blank directory object to be used to access containing directory */ const _FDID* obj /* Object with its containing directory information */ ) { @@ -2054,12 +2054,12 @@ FRESULT load_obj_dir ( /*-----------------------------------------------*/ static FRESULT store_xdir ( - DIR* dp /* Pointer to the direcotry object */ + DIR* dp /* Pointer to the directory object */ ) { FRESULT res; UINT nent; - BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry entry block 85+C0+C1s */ + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the directory entry block 85+C0+C1s */ /* Create set sum */ st_word(dirb + XDIR_SetSum, xdir_sum(dirb)); @@ -2087,7 +2087,7 @@ FRESULT store_xdir ( static void create_xdir ( - BYTE* dirb, /* Pointer to the direcotry entry block buffer */ + BYTE* dirb, /* Pointer to the directory entry block buffer */ const WCHAR* lfn /* Pointer to the nul terminated file name */ ) { @@ -3975,7 +3975,7 @@ FRESULT f_getcwd ( #endif if (i == len) { /* Root-directory */ *tp++ = '/'; - } else { /* Sub-directroy */ + } else { /* Sub-directory */ do /* Add stacked path str */ *tp++ = buff[i++]; while (i < len); @@ -4673,7 +4673,7 @@ FRESULT f_mkdir ( } } if (res == FR_OK) { - res = dir_register(&dj); /* Register the object to the directoy */ + res = dir_register(&dj); /* Register the object to the directory */ } if (res == FR_OK) { #if _FS_EXFAT diff --git a/lib/flipper_application/SConscript b/lib/flipper_application/SConscript new file mode 100644 index 00000000000..d253cc82c5f --- /dev/null +++ b/lib/flipper_application/SConscript @@ -0,0 +1,25 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/flipper_application", + ], + SDK_HEADERS=[ + File("flipper_application.h"), + File("plugins/plugin_manager.h"), + File("plugins/composite_resolver.h"), + File("api_hashtable/api_hashtable.h"), + File("api_hashtable/compilesort.hpp"), + ], +) + + +libenv = env.Clone(FW_LIB_NAME="flipper_application") +libenv.ApplyLibFlags() + +sources = libenv.GlobRecursive("*.c") +sources.append(File("api_hashtable/api_hashtable.cpp")) + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/flipper_application/api_hashtable/api_hashtable.cpp b/lib/flipper_application/api_hashtable/api_hashtable.cpp new file mode 100644 index 00000000000..ef22ee9ad98 --- /dev/null +++ b/lib/flipper_application/api_hashtable/api_hashtable.cpp @@ -0,0 +1,37 @@ +#include "api_hashtable.h" + +#include +#include + +#define TAG "ApiHashtable" + +bool elf_resolve_from_hashtable( + const ElfApiInterface* interface, + uint32_t hash, + Elf32_Addr* address) { + bool result = false; + const HashtableApiInterface* hashtable_interface = + static_cast(interface); + + sym_entry key = { + .hash = hash, + .address = 0, + }; + + auto find_res = + std::lower_bound(hashtable_interface->table_cbegin, hashtable_interface->table_cend, key); + if((find_res == hashtable_interface->table_cend || (find_res->hash != hash))) { + FURI_LOG_W( + TAG, "Can't find symbol with hash %lx @ %p!", hash, hashtable_interface->table_cbegin); + result = false; + } else { + result = true; + *address = find_res->address; + } + + return result; +} + +uint32_t elf_symbolname_hash(const char* s) { + return elf_gnu_hash(s); +} \ No newline at end of file diff --git a/lib/flipper_application/api_hashtable/api_hashtable.h b/lib/flipper_application/api_hashtable/api_hashtable.h new file mode 100644 index 00000000000..7ba6aab9727 --- /dev/null +++ b/lib/flipper_application/api_hashtable/api_hashtable.h @@ -0,0 +1,89 @@ +#pragma once + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Symbol table entry + */ +struct sym_entry { + uint32_t hash; + uint32_t address; +}; + +/** + * @brief Resolver for API entries using a pre-sorted table with hashes + * @param interface pointer to HashtableApiInterface + * @param hash gnu hash of function name + * @param address output for function address + * @return true if the table contains a function + */ +bool elf_resolve_from_hashtable( + const ElfApiInterface* interface, + uint32_t hash, + Elf32_Addr* address); + +uint32_t elf_symbolname_hash(const char* s); + +#ifdef __cplusplus +} + +#include +#include + +/** + * @brief HashtableApiInterface is an implementation of ElfApiInterface + * that uses a hash table to resolve function addresses. + * table_cbegin and table_cend must point to a sorted array of sym_entry + */ +struct HashtableApiInterface : public ElfApiInterface { + const sym_entry *table_cbegin, *table_cend; +}; + +#define API_METHOD(x, ret_type, args_type) \ + sym_entry { \ + .hash = elf_gnu_hash(#x), .address = (uint32_t)(static_cast(x)) \ + } + +#define API_VARIABLE(x, var_type) \ + sym_entry { \ + .hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), \ + } + +constexpr bool operator<(const sym_entry& k1, const sym_entry& k2) { + return k1.hash < k2.hash; +} + +/** + * @brief Calculate hash for a string using the ELF GNU hash algorithm + * @param s string to calculate hash for + * @return hash value + */ +constexpr uint32_t elf_gnu_hash(const char* s) { + uint32_t h = 0x1505; + for(unsigned char c = *s; c != '\0'; c = *++s) { + h = (h << 5) + h + c; + } + return h; +} + +/* Compile-time check for hash collisions in API table. + * Usage: static_assert(!has_hash_collisions(api_methods), "Hash collision detected"); + */ +template +constexpr bool has_hash_collisions(const std::array& api_methods) { + for(std::size_t i = 0; i < (N - 1); ++i) { + if(api_methods[i].hash == api_methods[i + 1].hash) { + return true; + } + } + + return false; +} + +#endif \ No newline at end of file diff --git a/lib/flipper_application/api_hashtable/compilesort.hpp b/lib/flipper_application/api_hashtable/compilesort.hpp new file mode 100644 index 00000000000..9737fd02276 --- /dev/null +++ b/lib/flipper_application/api_hashtable/compilesort.hpp @@ -0,0 +1,115 @@ +/** + * Implementation of compile-time sort for symbol table entries. + */ + +#pragma once + +#ifdef __cplusplus + +#include +#include + +namespace cstd { + +template +constexpr RAIt next(RAIt it, typename std::iterator_traits::difference_type n = 1) { + return it + n; +} + +template +constexpr auto distance(RAIt first, RAIt last) { + return last - first; +} + +template +constexpr void iter_swap(ForwardIt1 a, ForwardIt2 b) { + auto temp = std::move(*a); + *a = std::move(*b); + *b = std::move(temp); +} + +template +constexpr InputIt find_if_not(InputIt first, InputIt last, UnaryPredicate q) { + for(; first != last; ++first) { + if(!q(*first)) { + return first; + } + } + return last; +} + +template +constexpr ForwardIt partition(ForwardIt first, ForwardIt last, UnaryPredicate p) { + first = cstd::find_if_not(first, last, p); + if(first == last) return first; + + for(ForwardIt i = cstd::next(first); i != last; ++i) { + if(p(*i)) { + cstd::iter_swap(i, first); + ++first; + } + } + return first; +} + +} + +template > +constexpr void quick_sort(RAIt first, RAIt last, Compare cmp = Compare{}) { + auto const N = cstd::distance(first, last); + if(N <= 1) return; + auto const pivot = *cstd::next(first, N / 2); + auto const middle1 = + cstd::partition(first, last, [=](auto const& elem) { return cmp(elem, pivot); }); + auto const middle2 = + cstd::partition(middle1, last, [=](auto const& elem) { return !cmp(pivot, elem); }); + quick_sort(first, middle1, cmp); // assert(std::is_sorted(first, middle1, cmp)); + quick_sort(middle2, last, cmp); // assert(std::is_sorted(middle2, last, cmp)); +} + +template +constexpr auto sort(Range&& range) { + quick_sort(std::begin(range), std::end(range)); + return range; +} + +template +constexpr auto array_of(T&&... t) -> std::array { + return {{std::forward(t)...}}; +} + +template +constexpr auto my_make_array(N&&... args) -> std::array { + return {std::forward(args)...}; +} + +namespace traits { +template +struct array_type { + using type = T; +}; + +template +static constexpr bool are_same_type() { + return std::conjunction_v...>; +} + +} + +template +constexpr auto create_array(const T&&... values) { + using array_type = typename traits::array_type::type; + static_assert(sizeof...(T) > 0, "an array must have at least one element"); + static_assert(traits::are_same_type(), "all elements must have same type"); + return std::array{values...}; +} + +template +constexpr auto create_array_t(const Ts&&... values) { + using array_type = T; + static_assert(sizeof...(Ts) > 0, "an array must have at least one element"); + static_assert(traits::are_same_type(), "all elements must have same type"); + return std::array{static_cast(values)...}; +} + +#endif diff --git a/lib/flipper_application/application_assets.c b/lib/flipper_application/application_assets.c new file mode 100644 index 00000000000..ec3fc22ee69 --- /dev/null +++ b/lib/flipper_application/application_assets.c @@ -0,0 +1,366 @@ +#include "application_assets.h" +#include +#include + +// #define ELF_ASSETS_DEBUG_LOG 1 + +#ifndef ELF_ASSETS_DEBUG_LOG +#undef FURI_LOG_D +#define FURI_LOG_D(...) +#undef FURI_LOG_E +#define FURI_LOG_E(...) +#endif + +#define FLIPPER_APPLICATION_ASSETS_MAGIC 0x4F4C5A44 +#define FLIPPER_APPLICATION_ASSETS_VERSION 1 +#define FLIPPER_APPLICATION_ASSETS_SIGNATURE_FILENAME ".assets.signature" + +#define BUFFER_SIZE 512 + +#define TAG "FapAssets" + +#pragma pack(push, 1) + +typedef struct { + uint32_t magic; + uint32_t version; + uint32_t dirs_count; + uint32_t files_count; +} FlipperApplicationAssetsHeader; + +#pragma pack(pop) + +typedef enum { + AssetsSignatureResultEqual, + AssetsSignatureResultNotEqual, + AssetsSignatureResultError, +} AssetsSignatureResult; + +static FuriString* flipper_application_assets_alloc_app_full_path(FuriString* app_name) { + furi_assert(app_name); + FuriString* full_path = furi_string_alloc_set(APPS_ASSETS_PATH "/"); + furi_string_cat(full_path, app_name); + return full_path; +} + +static FuriString* flipper_application_assets_alloc_signature_file_path(FuriString* app_name) { + furi_assert(app_name); + FuriString* signature_file_path = flipper_application_assets_alloc_app_full_path(app_name); + furi_string_cat(signature_file_path, "/" FLIPPER_APPLICATION_ASSETS_SIGNATURE_FILENAME); + + return signature_file_path; +} + +static uint8_t* flipper_application_assets_alloc_and_load_data(File* file, size_t* size) { + furi_assert(file); + + uint8_t* data = NULL; + uint32_t length = 0; + + // read data length + if(storage_file_read(file, &length, sizeof(length)) != sizeof(length)) { + return NULL; + } + + data = malloc(length); + + // read data + if(storage_file_read(file, (void*)data, length) != length) { + free((void*)data); + return NULL; + } + + if(size != NULL) { + *size = length; + } + + return data; +} + +static bool flipper_application_assets_process_files( + Storage* storage, + File* file, + FuriString* app_name, + uint32_t files_count) { + furi_assert(storage); + furi_assert(file); + furi_assert(app_name); + + UNUSED(storage); + + bool success = false; + uint32_t length = 0; + char* path = NULL; + FuriString* file_path = furi_string_alloc(); + File* destination = storage_file_alloc(storage); + + FuriString* full_path = flipper_application_assets_alloc_app_full_path(app_name); + + for(uint32_t i = 0; i < files_count; i++) { + path = (char*)flipper_application_assets_alloc_and_load_data(file, NULL); + + if(path == NULL) { + break; + } + + // read file size + if(storage_file_read(file, &length, sizeof(length)) != sizeof(length)) { + break; + } + + furi_string_set(file_path, full_path); + furi_string_cat(file_path, "/"); + furi_string_cat(file_path, path); + + if(!storage_file_open( + destination, furi_string_get_cstr(file_path), FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + FURI_LOG_E(TAG, "Can't create file: %s", furi_string_get_cstr(file_path)); + break; + } + + // copy data to file + if(!storage_file_copy_to_file(file, destination, length)) { + FURI_LOG_E(TAG, "Can't copy data to file: %s", furi_string_get_cstr(file_path)); + break; + } + + storage_file_close(destination); + + free(path); + path = NULL; + + if(i == files_count - 1) { + success = true; + } + } + + if(path != NULL) { + free(path); + } + + storage_file_free(destination); + furi_string_free(file_path); + + return success; +} + +static bool flipper_application_assets_process_dirs( + Storage* storage, + File* file, + FuriString* app_name, + uint32_t dirs_count) { + furi_assert(storage); + furi_assert(file); + furi_assert(app_name); + + bool success = false; + FuriString* full_path = flipper_application_assets_alloc_app_full_path(app_name); + + do { + FuriString* dir_path = furi_string_alloc(); + char* path = NULL; + + for(uint32_t i = 0; i < dirs_count; i++) { + path = (char*)flipper_application_assets_alloc_and_load_data(file, NULL); + + if(path == NULL) { + break; + } + + furi_string_set(dir_path, full_path); + furi_string_cat(dir_path, "/"); + furi_string_cat(dir_path, path); + + if(!storage_simply_mkdir(storage, furi_string_get_cstr(dir_path))) { + FURI_LOG_E(TAG, "Can't create directory: %s", furi_string_get_cstr(dir_path)); + break; + } + + free(path); + path = NULL; + + if(i == dirs_count - 1) { + success = true; + } + } + + if(path != NULL) { + free(path); + } + + furi_string_free(dir_path); + } while(false); + + furi_string_free(full_path); + + return success; +} + +static AssetsSignatureResult flipper_application_assets_process_signature( + Storage* storage, + File* file, + FuriString* app_name, + uint8_t** signature_data, + size_t* signature_data_size) { + furi_assert(storage); + furi_assert(file); + furi_assert(app_name); + furi_assert(signature_data); + furi_assert(signature_data_size); + + AssetsSignatureResult result = AssetsSignatureResultError; + File* signature_file = storage_file_alloc(storage); + FuriString* signature_file_path = + flipper_application_assets_alloc_signature_file_path(app_name); + + do { + // read signature + *signature_data = + flipper_application_assets_alloc_and_load_data(file, signature_data_size); + + if(*signature_data == NULL) { //-V547 + FURI_LOG_E(TAG, "Can't read signature"); + break; + } + + result = AssetsSignatureResultNotEqual; + + if(!storage_file_open( + signature_file, + furi_string_get_cstr(signature_file_path), + FSAM_READ_WRITE, + FSOM_OPEN_EXISTING)) { + FURI_LOG_E(TAG, "Can't open signature file"); + break; + } + + size_t signature_size = storage_file_size(signature_file); + uint8_t* signature_file_data = malloc(signature_size); + if(storage_file_read(signature_file, signature_file_data, signature_size) != + signature_size) { + FURI_LOG_E(TAG, "Can't read signature file"); + free(signature_file_data); + break; + } + + if(memcmp(*signature_data, signature_file_data, signature_size) == 0) { + FURI_LOG_D(TAG, "Assets signature is equal"); + result = AssetsSignatureResultEqual; + } + + free(signature_file_data); + } while(0); + + storage_file_free(signature_file); + furi_string_free(signature_file_path); + + return result; +} + +bool flipper_application_assets_load(File* file, const char* elf_path, size_t offset, size_t size) { + UNUSED(size); + furi_assert(file); + furi_assert(elf_path); + FlipperApplicationAssetsHeader header; + bool result = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + uint8_t* signature_data = NULL; + size_t signature_data_size = 0; + FuriString* app_name = furi_string_alloc(); + path_extract_filename_no_ext(elf_path, app_name); + + FURI_LOG_D(TAG, "Loading assets for %s", furi_string_get_cstr(app_name)); + + FuriString* full_path = flipper_application_assets_alloc_app_full_path(app_name); + + do { + if(!storage_file_seek(file, offset, true)) { + break; + } + + // read header + if(storage_file_read(file, &header, sizeof(header)) != sizeof(header)) { + break; + } + + if(header.magic != FLIPPER_APPLICATION_ASSETS_MAGIC) { + break; + } + + if(header.version != FLIPPER_APPLICATION_ASSETS_VERSION) { + break; + } + + // process signature + AssetsSignatureResult signature_result = flipper_application_assets_process_signature( + storage, file, app_name, &signature_data, &signature_data_size); + + if(signature_result == AssetsSignatureResultError) { + FURI_LOG_E(TAG, "Assets signature error"); + break; + } else if(signature_result == AssetsSignatureResultEqual) { + FURI_LOG_D(TAG, "Assets signature equal, skip loading"); + result = true; + break; + } else { + FURI_LOG_D(TAG, "Assets signature not equal, loading"); + + // remove old assets + FuriString* full_path = flipper_application_assets_alloc_app_full_path(app_name); + storage_simply_remove_recursive(storage, furi_string_get_cstr(full_path)); + furi_string_free(full_path); + + FURI_LOG_D(TAG, "Assets removed"); + } + + if(!storage_simply_mkdir(storage, APPS_ASSETS_PATH)) { + break; + } + + if(!storage_simply_mkdir(storage, furi_string_get_cstr(full_path))) { + break; + } + + // process directories + if(header.dirs_count && + !flipper_application_assets_process_dirs(storage, file, app_name, header.dirs_count)) { + break; + } + + // process files + if(header.files_count && !flipper_application_assets_process_files( + storage, file, app_name, header.files_count)) { + break; + } + + // write signature + FuriString* signature_file_path = + flipper_application_assets_alloc_signature_file_path(app_name); + File* signature_file = storage_file_alloc(storage); + + if(storage_file_open( + signature_file, + furi_string_get_cstr(signature_file_path), + FSAM_WRITE, + FSOM_CREATE_ALWAYS)) { + storage_file_write(signature_file, signature_data, signature_data_size); + } + + storage_file_free(signature_file); + furi_string_free(signature_file_path); + + result = true; + } while(false); + + if(signature_data != NULL) { + free(signature_data); + } + + furi_record_close(RECORD_STORAGE); + furi_string_free(full_path); + furi_string_free(app_name); + + FURI_LOG_D(TAG, "Assets loading %s", result ? "success" : "failed"); + + return result; +} \ No newline at end of file diff --git a/lib/flipper_application/application_assets.h b/lib/flipper_application/application_assets.h new file mode 100644 index 00000000000..83bb14fb669 --- /dev/null +++ b/lib/flipper_application/application_assets.h @@ -0,0 +1,17 @@ +/** + * @file application_assets.h + * Flipper application assets + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool flipper_application_assets_load(File* file, const char* elf_path, size_t offset, size_t size); + +#ifdef __cplusplus +} +#endif diff --git a/lib/flipper_application/application_manifest.c b/lib/flipper_application/application_manifest.c new file mode 100644 index 00000000000..addbd5e4cb1 --- /dev/null +++ b/lib/flipper_application/application_manifest.c @@ -0,0 +1,39 @@ +#include "application_manifest.h" + +#include + +bool flipper_application_manifest_is_valid(const FlipperApplicationManifest* manifest) { + if((manifest->base.manifest_magic != FAP_MANIFEST_MAGIC) || + (manifest->base.manifest_version != FAP_MANIFEST_SUPPORTED_VERSION)) { + return false; + } + + return true; +} + +bool flipper_application_manifest_is_too_old( + const FlipperApplicationManifest* manifest, + const ElfApiInterface* api_interface) { + if(manifest->base.api_version.major < api_interface->api_version_major /* || + manifest->base.api_version.minor > app->api_interface->api_version_minor */) { + return false; + } + + return true; +} + +bool flipper_application_manifest_is_too_new( + const FlipperApplicationManifest* manifest, + const ElfApiInterface* api_interface) { + if(manifest->base.api_version.major > api_interface->api_version_major /* || + manifest->base.api_version.minor > app->api_interface->api_version_minor */) { + return false; + } + + return true; +} + +bool flipper_application_manifest_is_target_compatible(const FlipperApplicationManifest* manifest) { + const Version* version = furi_hal_version_get_firmware_version(); + return version_get_target(version) == manifest->base.hardware_target_id; +} \ No newline at end of file diff --git a/lib/flipper_application/application_manifest.h b/lib/flipper_application/application_manifest.h new file mode 100644 index 00000000000..5b87b811c5f --- /dev/null +++ b/lib/flipper_application/application_manifest.h @@ -0,0 +1,89 @@ +/** + * @file application_manifest.h + * Flipper application manifest + */ +#pragma once + +#include +#include +#include "elf/elf_api_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define FAP_MANIFEST_MAGIC 0x52474448 +#define FAP_MANIFEST_SUPPORTED_VERSION 1 + +#define FAP_MANIFEST_MAX_APP_NAME_LENGTH 32 +#define FAP_MANIFEST_MAX_ICON_SIZE 32 // TODO FL-3524: reduce size? + +#pragma pack(push, 1) + +typedef struct { + uint32_t manifest_magic; + uint32_t manifest_version; + union { + struct { + uint16_t minor; + uint16_t major; + }; + uint32_t version; + } api_version; + uint16_t hardware_target_id; +} FlipperApplicationManifestBase; + +typedef struct { + FlipperApplicationManifestBase base; + uint16_t stack_size; + uint32_t app_version; + char name[FAP_MANIFEST_MAX_APP_NAME_LENGTH]; + char has_icon; + char icon[FAP_MANIFEST_MAX_ICON_SIZE]; +} FlipperApplicationManifestV1; + +typedef FlipperApplicationManifestV1 FlipperApplicationManifest; + +#pragma pack(pop) + +/** + * @brief Check if manifest is valid + * + * @param manifest + * @return bool + */ +bool flipper_application_manifest_is_valid(const FlipperApplicationManifest* manifest); + +/** Check if API Version declared in manifest is older than firmware ELF API interface + * + * @param manifest The manifest + * @param api_interface The api interface + * + * @return bool + */ +bool flipper_application_manifest_is_too_old( + const FlipperApplicationManifest* manifest, + const ElfApiInterface* api_interface); + +/** Check if API Version declared in manifest is newer than firmware ELF API interface + * + * @param manifest The manifest + * @param api_interface The api interface + * + * @return bool + */ +bool flipper_application_manifest_is_too_new( + const FlipperApplicationManifest* manifest, + const ElfApiInterface* api_interface); + +/** + * @brief Check if application is compatible with current hardware + * + * @param manifest + * @return bool + */ +bool flipper_application_manifest_is_target_compatible(const FlipperApplicationManifest* manifest); + +#ifdef __cplusplus +} +#endif diff --git a/lib/flipper_application/elf/elf.h b/lib/flipper_application/elf/elf.h new file mode 100644 index 00000000000..a36622b52dc --- /dev/null +++ b/lib/flipper_application/elf/elf.h @@ -0,0 +1,1150 @@ +#ifndef _ELF_H +#define _ELF_H 1 + +/* Standard ELF types. */ + +#include + +/* Type for a 16-bit quantity. */ +typedef uint16_t Elf32_Half; +typedef uint16_t Elf64_Half; + +/* Types for signed and unsigned 32-bit quantities. */ +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf64_Word; +typedef int32_t Elf64_Sword; + +/* Types for signed and unsigned 64-bit quantities. */ +typedef uint64_t Elf32_Xword; +typedef int64_t Elf32_Sxword; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +/* Type of addresses. */ +typedef uint32_t Elf32_Addr; +typedef uint64_t Elf64_Addr; + +/* Type of file offsets. */ +typedef uint32_t Elf32_Off; +typedef uint64_t Elf64_Off; + +/* Type for section indices, which are 16-bit quantities. */ +typedef uint16_t Elf32_Section; +typedef uint16_t Elf64_Section; + +/* Type for version symbol information. */ +typedef Elf32_Half Elf32_Versym; +typedef Elf64_Half Elf64_Versym; + +/* The ELF file header. This appears at the start of every ELF file. */ + +#define EI_NIDENT (16) + +typedef struct { + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +} Elf32_Ehdr; + +typedef struct { + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf64_Half e_type; /* Object file type */ + Elf64_Half e_machine; /* Architecture */ + Elf64_Word e_version; /* Object file version */ + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Word e_flags; /* Processor-specific flags */ + Elf64_Half e_ehsize; /* ELF header size in bytes */ + Elf64_Half e_phentsize; /* Program header table entry size */ + Elf64_Half e_phnum; /* Program header table entry count */ + Elf64_Half e_shentsize; /* Section header table entry size */ + Elf64_Half e_shnum; /* Section header table entry count */ + Elf64_Half e_shstrndx; /* Section header string table index */ +} Elf64_Ehdr; + +/* Fields in the e_ident array. The EI_* macros are indices into the + array. The macros under each EI_* macro are the values the byte + may have. */ + +#define EI_MAG0 0 /* File identification byte 0 index */ +#define ELFMAG0 0x7f /* Magic number byte 0 */ + +#define EI_MAG1 1 /* File identification byte 1 index */ +#define ELFMAG1 'E' /* Magic number byte 1 */ + +#define EI_MAG2 2 /* File identification byte 2 index */ +#define ELFMAG2 'L' /* Magic number byte 2 */ + +#define EI_MAG3 3 /* File identification byte 3 index */ +#define ELFMAG3 'F' /* Magic number byte 3 */ + +/* Conglomeration of the identification bytes, for easy testing as a word. */ +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASSNONE 0 /* Invalid class */ +#define ELFCLASS32 1 /* 32-bit objects */ +#define ELFCLASS64 2 /* 64-bit objects */ +#define ELFCLASSNUM 3 + +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATANONE 0 /* Invalid data encoding */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ +#define ELFDATA2MSB 2 /* 2's complement, big endian */ +#define ELFDATANUM 3 + +#define EI_VERSION 6 /* File version byte index */ +/* Value must be EV_CURRENT */ + +#define EI_OSABI 7 /* OS ABI identification */ +#define ELFOSABI_NONE 0 /* UNIX System V ABI */ +#define ELFOSABI_SYSV 0 /* Alias. */ +#define ELFOSABI_HPUX 1 /* HP-UX */ +#define ELFOSABI_NETBSD 2 /* NetBSD. */ +#define ELFOSABI_LINUX 3 /* Linux. */ +#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ +#define ELFOSABI_AIX 7 /* IBM AIX. */ +#define ELFOSABI_IRIX 8 /* SGI Irix. */ +#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ +#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ +#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ +#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ +#define ELFOSABI_ARM 97 /* ARM */ +#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ + +#define EI_ABIVERSION 8 /* ABI version */ + +#define EI_PAD 9 /* Byte index of padding bytes */ + +/* Legal values for e_type (object file type). */ + +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Relocatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ +#define ET_NUM 5 /* Number of defined types */ +#define ET_LOOS 0xfe00 /* OS-specific range start */ +#define ET_HIOS 0xfeff /* OS-specific range end */ +#define ET_LOPROC 0xff00 /* Processor-specific range start */ +#define ET_HIPROC 0xffff /* Processor-specific range end */ + +/* Legal values for e_machine (architecture). */ + +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SUN SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola m68k family */ +#define EM_88K 5 /* Motorola m88k family */ +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 big-endian */ +#define EM_S370 9 /* IBM System/370 */ +#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ + +#define EM_PARISC 15 /* HPPA */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* PowerPC 64-bit */ +#define EM_S390 22 /* IBM S390 */ + +#define EM_V800 36 /* NEC V800 series */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH-32 */ +#define EM_RCE 39 /* Motorola RCE */ +#define EM_ARM 40 /* ARM */ +#define EM_FAKE_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_TRICORE 44 /* Siemens Tricore */ +#define EM_ARC 45 /* Argonaut RISC Core */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel Merced */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola Coldfire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Start*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronic ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ +#define EM_X86_64 62 /* AMD x86-64 architecture */ +#define EM_PDSP 63 /* Sony DSP Processor */ + +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ +#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ +#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ +#define EM_HUANY 81 /* Harvard University machine-independent object files */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi D10V */ +#define EM_D30V 86 /* Mitsubishi D30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10300 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ +#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ +#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define EM_NUM 95 + +/* Legal values for e_version (version). */ + +#define EV_NONE 0 /* Invalid ELF version */ +#define EV_CURRENT 1 /* Current version */ +#define EV_NUM 2 + +/* Section header. */ + +typedef struct { + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +} Elf32_Shdr; + +typedef struct { + Elf64_Word sh_name; /* Section name (string tbl index) */ + Elf64_Word sh_type; /* Section type */ + Elf64_Xword sh_flags; /* Section flags */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Xword sh_size; /* Section size in bytes */ + Elf64_Word sh_link; /* Link to another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ +} Elf64_Shdr; + +/* Special section indices. */ + +#define SHN_UNDEF 0 /* Undefined section */ +#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ +#define SHN_LOPROC 0xff00 /* Start of processor-specific */ +#define SHN_BEFORE \ + 0xff00 /* Order section before all others + (Solaris). */ +#define SHN_AFTER \ + 0xff01 /* Order section after all others + (Solaris). */ +#define SHN_HIPROC 0xff1f /* End of processor-specific */ +#define SHN_LOOS 0xff20 /* Start of OS-specific */ +#define SHN_HIOS 0xff3f /* End of OS-specific */ +#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ +#define SHN_COMMON 0xfff2 /* Associated symbol is common */ +#define SHN_XINDEX 0xffff /* Index is in extra table. */ +#define SHN_HIRESERVE 0xffff /* End of reserved indices */ + +/* Legal values for sh_type (section type). */ + +#define SHT_NULL 0 /* Section header table entry unused */ +#define SHT_PROGBITS 1 /* Program data */ +#define SHT_SYMTAB 2 /* Symbol table */ +#define SHT_STRTAB 3 /* String table */ +#define SHT_RELA 4 /* Relocation entries with addends */ +#define SHT_HASH 5 /* Symbol hash table */ +#define SHT_DYNAMIC 6 /* Dynamic linking information */ +#define SHT_NOTE 7 /* Notes */ +#define SHT_NOBITS 8 /* Program space with no data (bss) */ +#define SHT_REL 9 /* Relocation entries, no addends */ +#define SHT_SHLIB 10 /* Reserved */ +#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ +#define SHT_INIT_ARRAY 14 /* Array of constructors */ +#define SHT_FINI_ARRAY 15 /* Array of destructors */ +#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ +#define SHT_GROUP 17 /* Section group */ +#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ +#define SHT_NUM 19 /* Number of defined types. */ +#define SHT_LOOS 0x60000000 /* Start OS-specific. */ +#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */ +#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ +#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ +#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ +#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ +#define SHT_SUNW_move 0x6ffffffa +#define SHT_SUNW_COMDAT 0x6ffffffb +#define SHT_SUNW_syminfo 0x6ffffffc +#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ +#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ +#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ +#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ +#define SHT_HIOS 0x6fffffff /* End OS-specific type */ +#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ +#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ +#define SHT_LOUSER 0x80000000 /* Start of application-specific */ +#define SHT_HIUSER 0x8fffffff /* End of application-specific */ + +/* Legal values for sh_flags (section flags). */ + +#define SHF_WRITE (1 << 0) /* Writable */ +#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ +#define SHF_EXECINSTR (1 << 2) /* Executable */ +#define SHF_MERGE (1 << 4) /* Might be merged */ +#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ +#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ +#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ +#define SHF_OS_NONCONFORMING \ + (1 << 8) /* Non-standard OS specific handling + required */ +#define SHF_GROUP (1 << 9) /* Section is member of a group. */ +#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ +#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ +#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ +#define SHF_ORDERED \ + (1 << 30) /* Special ordering requirement + (Solaris). */ +#define SHF_EXCLUDE \ + (1 << 31) /* Section is excluded unless + referenced or allocated (Solaris).*/ + +/* Section group handling. */ +#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ + +/* Symbol table entry. */ + +typedef struct { + Elf32_Word st_name; /* Symbol name (string tbl index) */ + Elf32_Addr st_value; /* Symbol value */ + Elf32_Word st_size; /* Symbol size */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf32_Section st_shndx; /* Section index */ +} Elf32_Sym; + +typedef struct { + Elf64_Word st_name; /* Symbol name (string tbl index) */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf64_Section st_shndx; /* Section index */ + Elf64_Addr st_value; /* Symbol value */ + Elf64_Xword st_size; /* Symbol size */ +} Elf64_Sym; + +/* The syminfo section if available contains additional information about + every dynamic symbol. */ + +typedef struct { + Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf32_Half si_flags; /* Per symbol flags */ +} Elf32_Syminfo; + +typedef struct { + Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf64_Half si_flags; /* Per symbol flags */ +} Elf64_Syminfo; + +/* Possible values for si_boundto. */ +#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ +#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ +#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ + +/* Possible bitmasks for si_flags. */ +#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ +#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ +#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ +#define SYMINFO_FLG_LAZYLOAD \ + 0x0008 /* Symbol bound to object to be lazy + loaded */ +/* Syminfo version values. */ +#define SYMINFO_NONE 0 +#define SYMINFO_CURRENT 1 +#define SYMINFO_NUM 2 + +/* How to extract and insert information held in the st_info field. */ + +#define ELF32_ST_BIND(val) (((unsigned char)(val)) >> 4) +#define ELF32_ST_TYPE(val) ((val)&0xf) +#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type)&0xf)) + +/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ +#define ELF64_ST_BIND(val) ELF32_ST_BIND(val) +#define ELF64_ST_TYPE(val) ELF32_ST_TYPE(val) +#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO((bind), (type)) + +/* Legal values for ST_BIND subfield of st_info (symbol binding). */ + +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* Weak symbol */ +#define STB_NUM 3 /* Number of defined types. */ +#define STB_LOOS 10 /* Start of OS-specific */ +#define STB_GNU_UNIQUE 10 /* Unique symbol. */ +#define STB_HIOS 12 /* End of OS-specific */ +#define STB_LOPROC 13 /* Start of processor-specific */ +#define STB_HIPROC 15 /* End of processor-specific */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_NOTYPE 0 /* Symbol type is unspecified */ +#define STT_OBJECT 1 /* Symbol is a data object */ +#define STT_FUNC 2 /* Symbol is a code object */ +#define STT_SECTION 3 /* Symbol associated with a section */ +#define STT_FILE 4 /* Symbol's name is file name */ +#define STT_COMMON 5 /* Symbol is a common data object */ +#define STT_TLS 6 /* Symbol is thread-local data object*/ +#define STT_NUM 7 /* Number of defined types. */ +#define STT_LOOS 10 /* Start of OS-specific */ +#define STT_GNU_IFUNC 10 /* Symbol is indirect code object */ +#define STT_HIOS 12 /* End of OS-specific */ +#define STT_LOPROC 13 /* Start of processor-specific */ +#define STT_HIPROC 15 /* End of processor-specific */ + +/* Symbol table indices are found in the hash buckets and chain table + of a symbol hash table section. This special index value indicates + the end of a chain, meaning no further symbols are found in that bucket. */ + +#define STN_UNDEF 0 /* End of a chain. */ + +/* How to extract and insert information held in the st_other field. */ + +#define ELF32_ST_VISIBILITY(o) ((o)&0x03) + +/* For ELF64 the definitions are the same. */ +#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY(o) + +/* Symbol visibility specification encoded in the st_other field. */ +#define STV_DEFAULT 0 /* Default symbol visibility rules */ +#define STV_INTERNAL 1 /* Processor specific hidden class */ +#define STV_HIDDEN 2 /* Sym unavailable in other modules */ +#define STV_PROTECTED 3 /* Not preemptible, not exported */ + +/* Relocation table entry without addend (in section of type SHT_REL). */ + +typedef struct { + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ +} Elf32_Rel; + +/* I have seen two different definitions of the Elf64_Rel and + Elf64_Rela structures, so we'll leave them out until Novell (or + whoever) gets their act together. */ +/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ + +typedef struct { + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ +} Elf64_Rel; + +/* Relocation table entry with addend (in section of type SHT_RELA). */ + +typedef struct { + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ + Elf32_Sword r_addend; /* Addend */ +} Elf32_Rela; + +typedef struct { + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ + Elf64_Sxword r_addend; /* Addend */ +} Elf64_Rela; + +/* How to extract and insert information held in the r_info field. */ + +#define ELF32_R_SYM(val) ((val) >> 8) +#define ELF32_R_TYPE(val) ((val)&0xff) +#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type)&0xff)) + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i)&0xffffffff) +#define ELF64_R_INFO(sym, type) ((((Elf64_Xword)(sym)) << 32) + (type)) + +/* Program segment header. */ + +typedef struct { + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +} Elf32_Phdr; + +typedef struct { + Elf64_Word p_type; /* Segment type */ + Elf64_Word p_flags; /* Segment flags */ + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment */ +} Elf64_Phdr; + +/* Legal values for p_type (segment type). */ + +#define PT_NULL 0 /* Program header table entry unused */ +#define PT_LOAD 1 /* Loadable program segment */ +#define PT_DYNAMIC 2 /* Dynamic linking information */ +#define PT_INTERP 3 /* Program interpreter */ +#define PT_NOTE 4 /* Auxiliary information */ +#define PT_SHLIB 5 /* Reserved */ +#define PT_PHDR 6 /* Entry for header table itself */ +#define PT_TLS 7 /* Thread-local storage segment */ +#define PT_NUM 8 /* Number of defined types */ +#define PT_LOOS 0x60000000 /* Start of OS-specific */ +#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ +#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ +#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ +#define PT_LOSUNW 0x6ffffffa +#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ +#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ +#define PT_HISUNW 0x6fffffff +#define PT_HIOS 0x6fffffff /* End of OS-specific */ +#define PT_LOPROC 0x70000000 /* Start of processor-specific */ +#define PT_HIPROC 0x7fffffff /* End of processor-specific */ + +/* Legal values for p_flags (segment flags). */ + +#define PF_X (1 << 0) /* Segment is executable */ +#define PF_W (1 << 1) /* Segment is writable */ +#define PF_R (1 << 2) /* Segment is readable */ +#define PF_MASKOS 0x0ff00000 /* OS-specific */ +#define PF_MASKPROC 0xf0000000 /* Processor-specific */ + +/* Legal values for note segment descriptor types for core files. */ + +#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ +#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ +#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ +#define NT_PRXREG 4 /* Contains copy of prxregset struct */ +#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ +#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ +#define NT_AUXV 6 /* Contains copy of auxv array */ +#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ +#define NT_ASRS 8 /* Contains copy of asrset struct */ +#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ +#define NT_PSINFO 13 /* Contains copy of psinfo struct */ +#define NT_PRCRED 14 /* Contains copy of prcred struct */ +#define NT_UTSNAME 15 /* Contains copy of utsname struct */ +#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ +#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ +#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct */ +#define NT_PRXFPREG 0x46e62b7f /* Contains copy of user_fxsr_struct */ +#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ +#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ +#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ +#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ +#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ + +/* Legal values for the note segment descriptor types for object files. */ + +#define NT_VERSION 1 /* Contains a version string. */ + +/* Dynamic section entry. */ + +typedef struct { + Elf32_Sword d_tag; /* Dynamic entry type */ + union { + Elf32_Word d_val; /* Integer value */ + Elf32_Addr d_ptr; /* Address value */ + } d_un; +} Elf32_Dyn; + +typedef struct { + Elf64_Sxword d_tag; /* Dynamic entry type */ + union { + Elf64_Xword d_val; /* Integer value */ + Elf64_Addr d_ptr; /* Address value */ + } d_un; +} Elf64_Dyn; + +/* Legal values for d_tag (dynamic entry type). */ + +#define DT_NULL 0 /* Marks end of dynamic section */ +#define DT_NEEDED 1 /* Name of needed library */ +#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ +#define DT_PLTGOT 3 /* Processor defined value */ +#define DT_HASH 4 /* Address of symbol hash table */ +#define DT_STRTAB 5 /* Address of string table */ +#define DT_SYMTAB 6 /* Address of symbol table */ +#define DT_RELA 7 /* Address of Rela relocs */ +#define DT_RELASZ 8 /* Total size of Rela relocs */ +#define DT_RELAENT 9 /* Size of one Rela reloc */ +#define DT_STRSZ 10 /* Size of string table */ +#define DT_SYMENT 11 /* Size of one symbol table entry */ +#define DT_INIT 12 /* Address of init function */ +#define DT_FINI 13 /* Address of termination function */ +#define DT_SONAME 14 /* Name of shared object */ +#define DT_RPATH 15 /* Library search path (deprecated) */ +#define DT_SYMBOLIC 16 /* Start symbol search here */ +#define DT_REL 17 /* Address of Rel relocs */ +#define DT_RELSZ 18 /* Total size of Rel relocs */ +#define DT_RELENT 19 /* Size of one Rel reloc */ +#define DT_PLTREL 20 /* Type of reloc in PLT */ +#define DT_DEBUG 21 /* For debugging; unspecified */ +#define DT_TEXTREL 22 /* Reloc might modify .text */ +#define DT_JMPREL 23 /* Address of PLT relocs */ +#define DT_BIND_NOW 24 /* Process relocations of object */ +#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ +#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ +#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ +#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ +#define DT_RUNPATH 29 /* Library search path */ +#define DT_FLAGS 30 /* Flags for the object being loaded */ +#define DT_ENCODING 32 /* Start of encoded range */ +#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ +#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ +#define DT_NUM 34 /* Number used */ +#define DT_LOOS 0x6000000d /* Start of OS-specific */ +#define DT_HIOS 0x6ffff000 /* End of OS-specific */ +#define DT_LOPROC 0x70000000 /* Start of processor-specific */ +#define DT_HIPROC 0x7fffffff /* End of processor-specific */ +#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ + +/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the + Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's + approach. */ +#define DT_VALRNGLO 0x6ffffd00 +#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ +#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ +#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ +#define DT_CHECKSUM 0x6ffffdf8 +#define DT_PLTPADSZ 0x6ffffdf9 +#define DT_MOVEENT 0x6ffffdfa +#define DT_MOVESZ 0x6ffffdfb +#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ +#define DT_POSFLAG_1 \ + 0x6ffffdfd /* Flags for DT_* entries, effecting + the following DT_* entry. */ +#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ +#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ +#define DT_VALRNGHI 0x6ffffdff +#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ +#define DT_VALNUM 12 + +/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the + Dyn.d_un.d_ptr field of the Elf*_Dyn structure. + + If any adjustment is made to the ELF object after it has been + built these entries will need to be adjusted. */ +#define DT_ADDRRNGLO 0x6ffffe00 +#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */ +#define DT_TLSDESC_PLT 0x6ffffef6 +#define DT_TLSDESC_GOT 0x6ffffef7 +#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ +#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ +#define DT_CONFIG 0x6ffffefa /* Configuration information. */ +#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ +#define DT_AUDIT 0x6ffffefc /* Object auditing. */ +#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ +#define DT_MOVETAB 0x6ffffefe /* Move table. */ +#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ +#define DT_ADDRRNGHI 0x6ffffeff +#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ +#define DT_ADDRNUM 11 + +/* The versioning entry types. The next are defined as part of the + GNU extension. */ +#define DT_VERSYM 0x6ffffff0 + +#define DT_RELACOUNT 0x6ffffff9 +#define DT_RELCOUNT 0x6ffffffa + +/* These were chosen by Sun. */ +#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ +#define DT_VERDEF \ + 0x6ffffffc /* Address of version definition + table */ +#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ +#define DT_VERNEED \ + 0x6ffffffe /* Address of table with needed + versions */ +#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ +#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ +#define DT_VERSIONTAGNUM 16 + +/* Sun added these machine-independent extensions in the "processor-specific" + range. Be compatible. */ +#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ +#define DT_FILTER 0x7fffffff /* Shared object to get values from */ +#define DT_EXTRATAGIDX(tag) ((Elf32_Word) - ((Elf32_Sword)(tag) << 1 >> 1) - 1) +#define DT_EXTRANUM 3 + +/* Values of `d_un.d_val' in the DT_FLAGS entry. */ +#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ +#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ +#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ +#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ +#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ + +/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 + entry in the dynamic section. */ +#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ +#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ +#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ +#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ +#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ +#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ +#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ +#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ +#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ +#define DF_1_TRANS 0x00000200 +#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ +#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ +#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ +#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ +#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ +#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ +#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ + +/* Flags for the feature selection in DT_FEATURE_1. */ +#define DTF_1_PARINIT 0x00000001 +#define DTF_1_CONFEXP 0x00000002 + +/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ +#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ +#define DF_P1_GROUPPERM \ + 0x00000002 /* Symbols from next object are not + generally available. */ + +/* Version definition sections. */ + +typedef struct { + Elf32_Half vd_version; /* Version revision */ + Elf32_Half vd_flags; /* Version information */ + Elf32_Half vd_ndx; /* Version Index */ + Elf32_Half vd_cnt; /* Number of associated aux entries */ + Elf32_Word vd_hash; /* Version name hash value */ + Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf32_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf32_Verdef; + +typedef struct { + Elf64_Half vd_version; /* Version revision */ + Elf64_Half vd_flags; /* Version information */ + Elf64_Half vd_ndx; /* Version Index */ + Elf64_Half vd_cnt; /* Number of associated aux entries */ + Elf64_Word vd_hash; /* Version name hash value */ + Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf64_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf64_Verdef; + +/* Legal values for vd_version (version revision). */ +#define VER_DEF_NONE 0 /* No version */ +#define VER_DEF_CURRENT 1 /* Current version */ +#define VER_DEF_NUM 2 /* Given version number */ + +/* Legal values for vd_flags (version information flags). */ +#define VER_FLG_BASE 0x1 /* Version definition of file itself */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + +/* Versym symbol index values. */ +#define VER_NDX_LOCAL 0 /* Symbol is local. */ +#define VER_NDX_GLOBAL 1 /* Symbol is global. */ +#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ +#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ + +/* Auxialiary version information. */ + +typedef struct { + Elf32_Word vda_name; /* Version or dependency names */ + Elf32_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf32_Verdaux; + +typedef struct { + Elf64_Word vda_name; /* Version or dependency names */ + Elf64_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf64_Verdaux; + +/* Version dependency section. */ + +typedef struct { + Elf32_Half vn_version; /* Version of structure */ + Elf32_Half vn_cnt; /* Number of associated aux entries */ + Elf32_Word vn_file; /* Offset of filename for this + dependency */ + Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf32_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf32_Verneed; + +typedef struct { + Elf64_Half vn_version; /* Version of structure */ + Elf64_Half vn_cnt; /* Number of associated aux entries */ + Elf64_Word vn_file; /* Offset of filename for this + dependency */ + Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf64_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf64_Verneed; + +/* Legal values for vn_version (version revision). */ +#define VER_NEED_NONE 0 /* No version */ +#define VER_NEED_CURRENT 1 /* Current version */ +#define VER_NEED_NUM 2 /* Given version number */ + +/* Auxiliary needed version information. */ + +typedef struct { + Elf32_Word vna_hash; /* Hash value of dependency name */ + Elf32_Half vna_flags; /* Dependency specific information */ + Elf32_Half vna_other; /* Unused */ + Elf32_Word vna_name; /* Dependency name string offset */ + Elf32_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf32_Vernaux; + +typedef struct { + Elf64_Word vna_hash; /* Hash value of dependency name */ + Elf64_Half vna_flags; /* Dependency specific information */ + Elf64_Half vna_other; /* Unused */ + Elf64_Word vna_name; /* Dependency name string offset */ + Elf64_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf64_Vernaux; + +/* Legal values for vna_flags. */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + +/* Auxiliary vector. */ + +/* This vector is normally only used by the program interpreter. The + usual definition in an ABI supplement uses the name auxv_t. The + vector is not usually defined in a standard file, but it + can't hurt. We rename it to avoid conflicts. The sizes of these + types are an arrangement between the exec server and the program + interpreter, so we don't fully specify them here. */ + +typedef struct { + uint32_t a_type; /* Entry type */ + union { + uint32_t a_val; /* Integer value */ + /* We use to have pointer elements added here. We cannot do that, + though, since it does not work when using 32-bit definitions + on 64-bit platforms and vice versa. */ + } a_un; +} Elf32_auxv_t; + +typedef struct { + uint64_t a_type; /* Entry type */ + union { + uint64_t a_val; /* Integer value */ + /* We use to have pointer elements added here. We cannot do that, + though, since it does not work when using 32-bit definitions + on 64-bit platforms and vice versa. */ + } a_un; +} Elf64_auxv_t; + +/* Legal values for a_type (entry type). */ + +#define AT_NULL 0 /* End of vector */ +#define AT_IGNORE 1 /* Entry should be ignored */ +#define AT_EXECFD 2 /* File descriptor of program */ +#define AT_PHDR 3 /* Program headers for program */ +#define AT_PHENT 4 /* Size of program header entry */ +#define AT_PHNUM 5 /* Number of program headers */ +#define AT_PAGESZ 6 /* System page size */ +#define AT_BASE 7 /* Base address of interpreter */ +#define AT_FLAGS 8 /* Flags */ +#define AT_ENTRY 9 /* Entry point of program */ +#define AT_NOTELF 10 /* Program is not ELF */ +#define AT_UID 11 /* Real uid */ +#define AT_EUID 12 /* Effective uid */ +#define AT_GID 13 /* Real gid */ +#define AT_EGID 14 /* Effective gid */ +#define AT_CLKTCK 17 /* Frequency of times() */ + +/* Some more special a_type values describing the hardware. */ +#define AT_PLATFORM 15 /* String identifying platform. */ +#define AT_HWCAP \ + 16 /* Machine dependent hints about + processor capabilities. */ + +/* This entry gives some information about the FPU initialization + performed by the kernel. */ +#define AT_FPUCW 18 /* Used FPU control word. */ + +/* Cache block sizes. */ +#define AT_DCACHEBSIZE 19 /* Data cache block size. */ +#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ +#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ + +/* A special ignored value for PPC, used by the kernel to control the + interpretation of the AUXV. Must be > 16. */ +#define AT_IGNOREPPC 22 /* Entry should be ignored. */ + +#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ + +#define AT_BASE_PLATFORM 24 /* String identifying real platforms.*/ + +#define AT_RANDOM 25 /* Address of 16 random bytes. */ + +#define AT_EXECFN 31 /* Filename of executable. */ + +/* Pointer to the global system page used for system calls and other + nice things. */ +#define AT_SYSINFO 32 +#define AT_SYSINFO_EHDR 33 + +/* Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains + log2 of line size; mask those to get cache size. */ +#define AT_L1I_CACHESHAPE 34 +#define AT_L1D_CACHESHAPE 35 +#define AT_L2_CACHESHAPE 36 +#define AT_L3_CACHESHAPE 37 + +/* Note section contents. Each entry in the note section begins with + a header of a fixed form. */ + +typedef struct { + Elf32_Word n_namesz; /* Length of the note's name. */ + Elf32_Word n_descsz; /* Length of the note's descriptor. */ + Elf32_Word n_type; /* Type of the note. */ +} Elf32_Nhdr; + +typedef struct { + Elf64_Word n_namesz; /* Length of the note's name. */ + Elf64_Word n_descsz; /* Length of the note's descriptor. */ + Elf64_Word n_type; /* Type of the note. */ +} Elf64_Nhdr; + +/* Known names of notes. */ + +/* Solaris entries in the note section have this name. */ +#define ELF_NOTE_SOLARIS "SUNW Solaris" + +/* Note entries for GNU systems have this name. */ +#define ELF_NOTE_GNU "GNU" + +/* Defined types of notes for Solaris. */ + +/* Value of descriptor (one word) is desired pagesize for the binary. */ +#define ELF_NOTE_PAGESIZE_HINT 1 + +/* Defined note types for GNU systems. */ + +/* ABI information. The descriptor consists of words: + word 0: OS descriptor + word 1: major version of the ABI + word 2: minor version of the ABI + word 3: subminor version of the ABI +*/ +#define NT_GNU_ABI_TAG 1 +#define ELF_NOTE_ABI NT_GNU_ABI_TAG /* Old name. */ + +/* Known OSes. These values can appear in word 0 of an + NT_GNU_ABI_TAG note section entry. */ +#define ELF_NOTE_OS_LINUX 0 +#define ELF_NOTE_OS_GNU 1 +#define ELF_NOTE_OS_SOLARIS2 2 +#define ELF_NOTE_OS_FREEBSD 3 + +/* Synthetic hwcap information. The descriptor begins with two words: + word 0: number of entries + word 1: bitmask of enabled entries + Then follow variable-length entries, one byte followed by a + '\0'-terminated hwcap name string. The byte gives the bit + number to test if enabled, (1U << bit) & bitmask. */ +#define NT_GNU_HWCAP 2 + +/* Build ID bits as generated by ld --build-id. + The descriptor consists of any nonzero number of bytes. */ +#define NT_GNU_BUILD_ID 3 + +/* Version note generated by GNU gold containing a version string. */ +#define NT_GNU_GOLD_VERSION 4 + +/* Move records. */ +typedef struct { + Elf32_Xword m_value; /* Symbol value. */ + Elf32_Word m_info; /* Size and index. */ + Elf32_Word m_poffset; /* Symbol offset. */ + Elf32_Half m_repeat; /* Repeat count. */ + Elf32_Half m_stride; /* Stride info. */ +} Elf32_Move; + +typedef struct { + Elf64_Xword m_value; /* Symbol value. */ + Elf64_Xword m_info; /* Size and index. */ + Elf64_Xword m_poffset; /* Symbol offset. */ + Elf64_Half m_repeat; /* Repeat count. */ + Elf64_Half m_stride; /* Stride info. */ +} Elf64_Move; + +/* Macro to construct move records. */ +#define ELF32_M_SYM(info) ((info) >> 8) +#define ELF32_M_SIZE(info) ((unsigned char)(info)) +#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char)(size)) + +#define ELF64_M_SYM(info) ELF32_M_SYM(info) +#define ELF64_M_SIZE(info) ELF32_M_SIZE(info) +#define ELF64_M_INFO(sym, size) ELF32_M_INFO(sym, size) + +/* ARM specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_ARM_RELEXEC 0x01 +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_INTERWORK 0x04 +#define EF_ARM_APCS_26 0x08 +#define EF_ARM_APCS_FLOAT 0x10 +#define EF_ARM_PIC 0x20 +#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ +#define EF_ARM_NEW_ABI 0x80 +#define EF_ARM_OLD_ABI 0x100 +#define EF_ARM_SOFT_FLOAT 0x200 +#define EF_ARM_VFP_FLOAT 0x400 +#define EF_ARM_MAVERICK_FLOAT 0x800 + +/* Other constants defined in the ARM ELF spec. version B-01. */ +/* NB. These conflict with values defined above. */ +#define EF_ARM_SYMSARESORTED 0x04 +#define EF_ARM_DYNSYMSUSESEGIDX 0x08 +#define EF_ARM_MAPSYMSFIRST 0x10 +#define EF_ARM_EABIMASK 0XFF000000 + +/* Constants defined in AAELF. */ +#define EF_ARM_BE8 0x00800000 +#define EF_ARM_LE8 0x00400000 + +#define EF_ARM_EABI_VERSION(flags) ((flags)&EF_ARM_EABIMASK) +#define EF_ARM_EABI_UNKNOWN 0x00000000 +#define EF_ARM_EABI_VER1 0x01000000 +#define EF_ARM_EABI_VER2 0x02000000 +#define EF_ARM_EABI_VER3 0x03000000 +#define EF_ARM_EABI_VER4 0x04000000 +#define EF_ARM_EABI_VER5 0x05000000 + +/* Additional symbol types for Thumb. */ +#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */ +#define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */ + +/* ARM-specific values for sh_flags */ +#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ +#define SHF_ARM_COMDEF \ + 0x80000000 /* Section may be multiply defined + in the input to a link step. */ + +/* ARM-specific program header flags */ +#define PF_ARM_SB \ + 0x10000000 /* Segment contains the location + addressed by the static base. */ +#define PF_ARM_PI 0x20000000 /* Position-independent segment. */ +#define PF_ARM_ABS 0x40000000 /* Absolute segment. */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_ARM_EXIDX (PT_LOPROC + 1) /* ARM unwind segment. */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_ARM_EXIDX (SHT_LOPROC + 1) /* ARM unwind section. */ +#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) /* Preemption details. */ +#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */ + +/* ARM relocs. */ + +#define R_ARM_NONE 0 /* No reloc */ +#define R_ARM_PC24 1 /* PC relative 26 bit branch */ +#define R_ARM_ABS32 2 /* Direct 32 bit */ +#define R_ARM_REL32 3 /* PC relative 32 bit */ +#define R_ARM_PC13 4 +#define R_ARM_ABS16 5 /* Direct 16 bit */ +#define R_ARM_ABS12 6 /* Direct 12 bit */ +#define R_ARM_THM_ABS5 7 +#define R_ARM_ABS8 8 /* Direct 8 bit */ +#define R_ARM_SBREL32 9 +#define R_ARM_THM_PC22 10 +#define R_ARM_THM_PC8 11 +#define R_ARM_AMP_VCALL9 12 +#define R_ARM_SWI24 13 +#define R_ARM_THM_SWI8 14 +#define R_ARM_XPC25 15 +#define R_ARM_THM_XPC22 16 +#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */ +#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */ +#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */ +#define R_ARM_COPY 20 /* Copy symbol at runtime */ +#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ +#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ +#define R_ARM_RELATIVE 23 /* Adjust by program base */ +#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ +#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ +#define R_ARM_GOT32 26 /* 32 bit GOT entry */ +#define R_ARM_PLT32 27 /* 32 bit PLT address */ +#define R_ARM_THM_JUMP24 30 /* Thumb32 ((S + A) | T) - P */ +#define R_ARM_ALU_PCREL_7_0 32 +#define R_ARM_ALU_PCREL_15_8 33 +#define R_ARM_ALU_PCREL_23_15 34 +#define R_ARM_LDR_SBREL_11_0 35 +#define R_ARM_ALU_SBREL_19_12 36 +#define R_ARM_ALU_SBREL_27_20 37 +#define R_ARM_THM_MOVW_ABS_NC 47 /* Direct 16 bit (Thumb32 MOVW) */ +#define R_ARM_THM_MOVT_ABS 48 /* Direct high 16 bit */ +#define R_ARM_GNU_VTENTRY 100 +#define R_ARM_GNU_VTINHERIT 101 +#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ +#define R_ARM_THM_PC9 103 /* thumb conditional branch */ +#define R_ARM_TLS_GD32 \ + 104 /* PC-rel 32 bit for global dynamic + thread local data */ +#define R_ARM_TLS_LDM32 \ + 105 /* PC-rel 32 bit for local dynamic + thread local data */ +#define R_ARM_TLS_LDO32 \ + 106 /* 32 bit offset relative to TLS + block */ +#define R_ARM_TLS_IE32 \ + 107 /* PC-rel 32 bit for GOT entry of + static TLS block offset */ +#define R_ARM_TLS_LE32 \ + 108 /* 32 bit offset relative to static + TLS block */ +#define R_ARM_RXPC25 249 +#define R_ARM_RSBREL32 250 +#define R_ARM_THM_RPC22 251 +#define R_ARM_RREL32 252 +#define R_ARM_RABS22 253 +#define R_ARM_RPC24 254 +#define R_ARM_RBASE 255 +/* Keep this the last entry. */ +#define R_ARM_NUM 256 + +#endif /* elf.h */ diff --git a/lib/flipper_application/elf/elf_api_interface.h b/lib/flipper_application/elf/elf_api_interface.h new file mode 100644 index 00000000000..facdc44473d --- /dev/null +++ b/lib/flipper_application/elf/elf_api_interface.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +/** + * @brief Interface for ELF loader to resolve symbols + */ +typedef struct ElfApiInterface { + uint16_t api_version_major; + uint16_t api_version_minor; + bool (*resolver_callback)( + const struct ElfApiInterface* interface, + uint32_t hash, + Elf32_Addr* address); +} ElfApiInterface; diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c new file mode 100644 index 00000000000..65431686625 --- /dev/null +++ b/lib/flipper_application/elf/elf_file.c @@ -0,0 +1,1010 @@ +#include "elf_file.h" +#include "elf_file_i.h" + +#include +#include +#include "elf_api_interface.h" +#include "../api_hashtable/api_hashtable.h" + +#define TAG "Elf" + +#define ELF_NAME_BUFFER_LEN 32 +#define SECTION_OFFSET(e, n) ((e)->section_table + (n) * sizeof(Elf32_Shdr)) +#define IS_FLAGS_SET(v, m) (((v) & (m)) == (m)) +#define RESOLVER_THREAD_YIELD_STEP 30 +#define FAST_RELOCATION_VERSION 1 + +// #define ELF_DEBUG_LOG 1 + +#ifndef ELF_DEBUG_LOG +#undef FURI_LOG_D +#define FURI_LOG_D(...) +#endif + +#define ELF_INVALID_ADDRESS 0xFFFFFFFF + +#define TRAMPOLINE_CODE_SIZE 6 + +/** +ldr r12, [pc, #2] +bx r12 +*/ +const uint8_t trampoline_code_little_endian[TRAMPOLINE_CODE_SIZE] = + {0xdf, 0xf8, 0x02, 0xc0, 0x60, 0x47}; + +typedef struct { + uint8_t code[TRAMPOLINE_CODE_SIZE]; + uint32_t addr; +} FURI_PACKED JMPTrampoline; + +/**************************************************************************************************/ +/********************************************* Caches *********************************************/ +/**************************************************************************************************/ + +static bool address_cache_get(AddressCache_t cache, int symEntry, Elf32_Addr* symAddr) { + Elf32_Addr* addr = AddressCache_get(cache, symEntry); + if(addr) { + *symAddr = *addr; + return true; + } else { + return false; + } +} + +static void address_cache_put(AddressCache_t cache, int symEntry, Elf32_Addr symAddr) { + AddressCache_set_at(cache, symEntry, symAddr); +} + +/**************************************************************************************************/ +/********************************************** ELF ***********************************************/ +/**************************************************************************************************/ + +static void elf_file_maybe_release_fd(ELFFile* elf) { + if(elf->fd) { + storage_file_free(elf->fd); + elf->fd = NULL; + } +} + +static ELFSection* elf_file_get_section(ELFFile* elf, const char* name) { + return ELFSectionDict_get(elf->sections, name); +} + +static ELFSection* elf_file_get_or_put_section(ELFFile* elf, const char* name) { + ELFSection* section_p = elf_file_get_section(elf, name); + if(!section_p) { + ELFSectionDict_set_at( + elf->sections, + strdup(name), + (ELFSection){ + .data = NULL, + .sec_idx = 0, + .size = 0, + .rel_count = 0, + .rel_offset = 0, + .fast_rel = NULL, + }); + section_p = elf_file_get_section(elf, name); + } + + return section_p; +} + +static bool elf_read_string_from_offset(ELFFile* elf, off_t offset, FuriString* name) { + bool result = false; + + off_t old = storage_file_tell(elf->fd); + + do { + if(!storage_file_seek(elf->fd, offset, true)) break; + + char buffer[ELF_NAME_BUFFER_LEN + 1]; + buffer[ELF_NAME_BUFFER_LEN] = 0; + + while(true) { + size_t read = storage_file_read(elf->fd, buffer, ELF_NAME_BUFFER_LEN); + furi_string_cat(name, buffer); + if(strlen(buffer) < ELF_NAME_BUFFER_LEN) { + result = true; + break; + } + + if(storage_file_get_error(elf->fd) != FSE_OK || read == 0) break; + } + + } while(false); + storage_file_seek(elf->fd, old, true); + + return result; +} + +static bool elf_read_section_name(ELFFile* elf, off_t offset, FuriString* name) { + return elf_read_string_from_offset(elf, elf->section_table_strings + offset, name); +} + +static bool elf_read_symbol_name(ELFFile* elf, off_t offset, FuriString* name) { + return elf_read_string_from_offset(elf, elf->symbol_table_strings + offset, name); +} + +static bool elf_read_section_header(ELFFile* elf, size_t section_idx, Elf32_Shdr* section_header) { + off_t offset = SECTION_OFFSET(elf, section_idx); + return storage_file_seek(elf->fd, offset, true) && + storage_file_read(elf->fd, section_header, sizeof(Elf32_Shdr)) == sizeof(Elf32_Shdr); +} + +static bool elf_read_section( + ELFFile* elf, + size_t section_idx, + Elf32_Shdr* section_header, + FuriString* name) { + if(!elf_read_section_header(elf, section_idx, section_header)) { + return false; + } + + if(section_header->sh_name && !elf_read_section_name(elf, section_header->sh_name, name)) { + return false; + } + + return true; +} + +static bool elf_read_symbol(ELFFile* elf, int n, Elf32_Sym* sym, FuriString* name) { + bool success = false; + off_t old = storage_file_tell(elf->fd); + off_t pos = elf->symbol_table + n * sizeof(Elf32_Sym); + if(storage_file_seek(elf->fd, pos, true) && + storage_file_read(elf->fd, sym, sizeof(Elf32_Sym)) == sizeof(Elf32_Sym)) { + if(sym->st_name) + success = elf_read_symbol_name(elf, sym->st_name, name); + else { + Elf32_Shdr shdr; + success = elf_read_section(elf, sym->st_shndx, &shdr, name); + } + } + storage_file_seek(elf->fd, old, true); + return success; +} + +static ELFSection* elf_section_of(ELFFile* elf, int index) { + ELFSectionDict_it_t it; + for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) { + ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it); + if(itref->value.sec_idx == index) { + return &itref->value; + } + } + + return NULL; +} + +static Elf32_Addr elf_address_of(ELFFile* elf, Elf32_Sym* sym, const char* sName) { + if(sym->st_shndx == SHN_UNDEF) { + Elf32_Addr addr = 0; + uint32_t hash = elf_symbolname_hash(sName); + if(elf->api_interface->resolver_callback(elf->api_interface, hash, &addr)) { + return addr; + } + } else { + ELFSection* symSec = elf_section_of(elf, sym->st_shndx); + if(symSec) { + return ((Elf32_Addr)symSec->data) + sym->st_value; + } + } + FURI_LOG_D(TAG, " Can not find address for symbol %s", sName); + return ELF_INVALID_ADDRESS; +} + +__attribute__((unused)) static const char* elf_reloc_type_to_str(int symt) { +#define STRCASE(name) \ + case name: \ + return #name; + switch(symt) { + STRCASE(R_ARM_NONE) + STRCASE(R_ARM_TARGET1) + STRCASE(R_ARM_ABS32) + STRCASE(R_ARM_THM_PC22) + STRCASE(R_ARM_THM_JUMP24) + default: + return "R_"; + } +#undef STRCASE +} + +static JMPTrampoline* elf_create_trampoline(Elf32_Addr addr) { + JMPTrampoline* trampoline = malloc(sizeof(JMPTrampoline)); + memcpy(trampoline->code, trampoline_code_little_endian, TRAMPOLINE_CODE_SIZE); + trampoline->addr = addr; + return trampoline; +} + +static void elf_relocate_jmp_call(ELFFile* elf, Elf32_Addr relAddr, int type, Elf32_Addr symAddr) { + int offset, hi, lo, s, j1, j2, i1, i2, imm10, imm11; + int to_thumb, is_call, blx_bit = 1 << 12; + + /* Get initial offset */ + hi = ((uint16_t*)relAddr)[0]; + lo = ((uint16_t*)relAddr)[1]; + s = (hi >> 10) & 1; + j1 = (lo >> 13) & 1; + j2 = (lo >> 11) & 1; + i1 = (j1 ^ s) ^ 1; + i2 = (j2 ^ s) ^ 1; + imm10 = hi & 0x3ff; + imm11 = lo & 0x7ff; + offset = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); + if(offset & 0x01000000) offset -= 0x02000000; + + to_thumb = symAddr & 1; + is_call = (type == R_ARM_THM_PC22); + + /* Store offset */ + int offset_copy = offset; + + /* Compute final offset */ + offset += symAddr - relAddr; + if(!to_thumb && is_call) { + blx_bit = 0; /* bl -> blx */ + offset = (offset + 3) & -4; /* Compute offset from aligned PC */ + } + + /* Check that relocation is possible + * offset must not be out of range + * if target is to be entered in arm mode: + - bit 1 must not set + - instruction must be a call (bl) or a jump to PLT */ + if(!to_thumb || offset >= 0x1000000 || offset < -0x1000000) { + if(to_thumb || (symAddr & 2) || (!is_call)) { + FURI_LOG_D( + TAG, + "can't relocate value at %lx, %s, doing trampoline", + relAddr, + elf_reloc_type_to_str(type)); + + Elf32_Addr addr; + if(!address_cache_get(elf->trampoline_cache, symAddr, &addr)) { + addr = (Elf32_Addr)elf_create_trampoline(symAddr); + address_cache_put(elf->trampoline_cache, symAddr, addr); + } + + offset = offset_copy; + offset += (int)addr - relAddr; + if(!to_thumb && is_call) { + blx_bit = 0; /* bl -> blx */ + offset = (offset + 3) & -4; /* Compute offset from aligned PC */ + } + } + } + + /* Compute and store final offset */ + s = (offset >> 24) & 1; + i1 = (offset >> 23) & 1; + i2 = (offset >> 22) & 1; + j1 = s ^ (i1 ^ 1); + j2 = s ^ (i2 ^ 1); + imm10 = (offset >> 12) & 0x3ff; + imm11 = (offset >> 1) & 0x7ff; + (*(uint16_t*)relAddr) = (uint16_t)((hi & 0xf800) | (s << 10) | imm10); + (*(uint16_t*)(relAddr + 2)) = + (uint16_t)((lo & 0xc000) | (j1 << 13) | blx_bit | (j2 << 11) | imm11); +} + +static void elf_relocate_mov(Elf32_Addr relAddr, int type, Elf32_Addr symAddr) { + uint16_t upper_insn = ((uint16_t*)relAddr)[0]; + uint16_t lower_insn = ((uint16_t*)relAddr)[1]; + + /* MOV* ,# + * + * i = upper[10] + * imm4 = upper[3:0] + * imm3 = lower[14:12] + * imm8 = lower[7:0] + * + * imm16 = imm4:i:imm3:imm8 + */ + uint32_t i = (upper_insn >> 10) & 1; /* upper[10] */ + uint32_t imm4 = upper_insn & 0x000F; /* upper[3:0] */ + uint32_t imm3 = (lower_insn >> 12) & 0x7; /* lower[14:12] */ + uint32_t imm8 = lower_insn & 0x00FF; /* lower[7:0] */ + + int32_t addend = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; /* imm16 */ + + uint32_t addr = (symAddr + addend); + if(type == R_ARM_THM_MOVT_ABS) { + addr >>= 16; /* upper 16 bits */ + } else { + addr &= 0x0000FFFF; /* lower 16 bits */ + } + + /* Re-encode */ + ((uint16_t*)relAddr)[0] = (upper_insn & 0xFBF0) | (((addr >> 11) & 1) << 10) /* i */ + | ((addr >> 12) & 0x000F); /* imm4 */ + ((uint16_t*)relAddr)[1] = (lower_insn & 0x8F00) | (((addr >> 8) & 0x7) << 12) /* imm3 */ + | (addr & 0x00FF); /* imm8 */ +} + +static bool elf_relocate_symbol(ELFFile* elf, Elf32_Addr relAddr, int type, Elf32_Addr symAddr) { + switch(type) { + case R_ARM_TARGET1: + case R_ARM_ABS32: + *((uint32_t*)relAddr) += symAddr; + FURI_LOG_D(TAG, " R_ARM_ABS32 relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr)); + break; + case R_ARM_THM_PC22: + case R_ARM_CALL: + case R_ARM_THM_JUMP24: + elf_relocate_jmp_call(elf, relAddr, type, symAddr); + FURI_LOG_D( + TAG, " R_ARM_THM_CALL/JMP relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr)); + break; + case R_ARM_THM_MOVW_ABS_NC: + case R_ARM_THM_MOVT_ABS: + elf_relocate_mov(relAddr, type, symAddr); + FURI_LOG_D( + TAG, + " R_ARM_THM_MOVW_ABS_NC/MOVT_ABS relocated is 0x%08X", + (unsigned int)*((uint32_t*)relAddr)); + break; + default: + FURI_LOG_E(TAG, " Undefined relocation %d", type); + return false; + } + return true; +} + +static bool elf_relocate(ELFFile* elf, ELFSection* s) { + if(s->data) { + Elf32_Rel rel; + size_t relEntries = s->rel_count; + size_t relCount; + (void)storage_file_seek(elf->fd, s->rel_offset, true); + FURI_LOG_D(TAG, " Offset Info Type Name"); + + int relocate_result = true; + FuriString* symbol_name; + symbol_name = furi_string_alloc(); + + for(relCount = 0; relCount < relEntries; relCount++) { + if(relCount % RESOLVER_THREAD_YIELD_STEP == 0) { + FURI_LOG_D(TAG, " reloc YIELD"); + furi_delay_tick(1); + } + + if(storage_file_read(elf->fd, &rel, sizeof(Elf32_Rel)) != sizeof(Elf32_Rel)) { + FURI_LOG_E(TAG, " reloc read fail"); + furi_string_free(symbol_name); + return false; + } + + Elf32_Addr symAddr; + + int symEntry = ELF32_R_SYM(rel.r_info); + int relType = ELF32_R_TYPE(rel.r_info); + Elf32_Addr relAddr = ((Elf32_Addr)s->data) + rel.r_offset; + + if(!address_cache_get(elf->relocation_cache, symEntry, &symAddr)) { + Elf32_Sym sym; + furi_string_reset(symbol_name); + if(!elf_read_symbol(elf, symEntry, &sym, symbol_name)) { + FURI_LOG_E(TAG, " symbol read fail"); + furi_string_free(symbol_name); + return false; + } + + FURI_LOG_D( + TAG, + " %08X %08X %-16s %s", + (unsigned int)rel.r_offset, + (unsigned int)rel.r_info, + elf_reloc_type_to_str(relType), + furi_string_get_cstr(symbol_name)); + + symAddr = elf_address_of(elf, &sym, furi_string_get_cstr(symbol_name)); + address_cache_put(elf->relocation_cache, symEntry, symAddr); + } + + if(symAddr != ELF_INVALID_ADDRESS) { + FURI_LOG_D( + TAG, + " symAddr=%08X relAddr=%08X", + (unsigned int)symAddr, + (unsigned int)relAddr); + if(!elf_relocate_symbol(elf, relAddr, relType, symAddr)) { + relocate_result = false; + } + } else { + FURI_LOG_E(TAG, " No symbol address of %s", furi_string_get_cstr(symbol_name)); + relocate_result = false; + } + } + furi_string_free(symbol_name); + + return relocate_result; + } else { + FURI_LOG_D(TAG, "Section not loaded"); + } + + return false; +} + +/**************************************************************************************************/ +/************************************ Internal FAP interfaces *************************************/ +/**************************************************************************************************/ +typedef enum { + SectionTypeERROR = 0, + SectionTypeUnused = 1 << 0, + SectionTypeData = 1 << 1, + SectionTypeRelData = 1 << 2, + SectionTypeSymTab = 1 << 3, + SectionTypeStrTab = 1 << 4, + SectionTypeDebugLink = 1 << 5, + SectionTypeFastRelData = 1 << 6, + + SectionTypeValid = SectionTypeSymTab | SectionTypeStrTab, +} SectionType; + +static bool elf_load_debug_link(ELFFile* elf, Elf32_Shdr* section_header) { + elf->debug_link_info.debug_link_size = section_header->sh_size; + elf->debug_link_info.debug_link = malloc(section_header->sh_size); + + return storage_file_seek(elf->fd, section_header->sh_offset, true) && + storage_file_read(elf->fd, elf->debug_link_info.debug_link, section_header->sh_size) == + section_header->sh_size; +} + +static bool str_prefix(const char* str, const char* prefix) { + return strncmp(prefix, str, strlen(prefix)) == 0; +} + +static bool elf_load_section_data(ELFFile* elf, ELFSection* section, Elf32_Shdr* section_header) { + if(section_header->sh_size == 0) { + FURI_LOG_D(TAG, "No data for section"); + return true; + } + + section->data = aligned_malloc(section_header->sh_size, section_header->sh_addralign); + section->size = section_header->sh_size; + + if(section_header->sh_type == SHT_NOBITS) { + // BSS section, no data to load + return true; + } + + if((!storage_file_seek(elf->fd, section_header->sh_offset, true)) || + (storage_file_read(elf->fd, section->data, section_header->sh_size) != + section_header->sh_size)) { + FURI_LOG_E(TAG, " seek/read fail"); + return false; + } + + FURI_LOG_D(TAG, "0x%p", section->data); + return true; +} + +static SectionType elf_preload_section( + ELFFile* elf, + size_t section_idx, + Elf32_Shdr* section_header, + FuriString* name_string) { + const char* name = furi_string_get_cstr(name_string); + +#ifdef ELF_DEBUG_LOG + // log section name, type and flags + FuriString* flags_string = furi_string_alloc(); + if(section_header->sh_flags & SHF_WRITE) furi_string_cat(flags_string, "W"); + if(section_header->sh_flags & SHF_ALLOC) furi_string_cat(flags_string, "A"); + if(section_header->sh_flags & SHF_EXECINSTR) furi_string_cat(flags_string, "X"); + if(section_header->sh_flags & SHF_MERGE) furi_string_cat(flags_string, "M"); + if(section_header->sh_flags & SHF_STRINGS) furi_string_cat(flags_string, "S"); + if(section_header->sh_flags & SHF_INFO_LINK) furi_string_cat(flags_string, "I"); + if(section_header->sh_flags & SHF_LINK_ORDER) furi_string_cat(flags_string, "L"); + if(section_header->sh_flags & SHF_OS_NONCONFORMING) furi_string_cat(flags_string, "O"); + if(section_header->sh_flags & SHF_GROUP) furi_string_cat(flags_string, "G"); + if(section_header->sh_flags & SHF_TLS) furi_string_cat(flags_string, "T"); + if(section_header->sh_flags & SHF_COMPRESSED) furi_string_cat(flags_string, "T"); + if(section_header->sh_flags & SHF_MASKOS) furi_string_cat(flags_string, "o"); + if(section_header->sh_flags & SHF_MASKPROC) furi_string_cat(flags_string, "p"); + if(section_header->sh_flags & SHF_ORDERED) furi_string_cat(flags_string, "R"); + if(section_header->sh_flags & SHF_EXCLUDE) furi_string_cat(flags_string, "E"); + + FURI_LOG_I( + TAG, + "Section %s: type: %ld, flags: %s", + name, + section_header->sh_type, + furi_string_get_cstr(flags_string)); + furi_string_free(flags_string); +#endif + + // ignore .ARM and .rel.ARM sections + // TODO FL-3525: how to do it not by name? + // .ARM: type 0x70000001, flags SHF_ALLOC | SHF_LINK_ORDER + // .rel.ARM: type 0x9, flags SHT_REL + if(str_prefix(name, ".ARM.") || str_prefix(name, ".rel.ARM.") || + str_prefix(name, ".fast.rel.ARM.")) { + FURI_LOG_D(TAG, "Ignoring ARM section"); + return SectionTypeUnused; + } + + // Load allocable section + if(section_header->sh_flags & SHF_ALLOC) { + ELFSection* section_p = elf_file_get_or_put_section(elf, name); + section_p->sec_idx = section_idx; + + if(section_header->sh_type == SHT_PREINIT_ARRAY) { + furi_assert(elf->preinit_array == NULL); + elf->preinit_array = section_p; + } else if(section_header->sh_type == SHT_INIT_ARRAY) { + furi_assert(elf->init_array == NULL); + elf->init_array = section_p; + } else if(section_header->sh_type == SHT_FINI_ARRAY) { + furi_assert(elf->fini_array == NULL); + elf->fini_array = section_p; + } + + if(!elf_load_section_data(elf, section_p, section_header)) { + FURI_LOG_E(TAG, "Error loading section '%s'", name); + return SectionTypeERROR; + } else { + return SectionTypeData; + } + } + + // Load link info section + if(section_header->sh_flags & SHF_INFO_LINK) { + if(str_prefix(name, ".rel")) { + name = name + strlen(".rel"); + ELFSection* section_p = elf_file_get_or_put_section(elf, name); + section_p->rel_count = section_header->sh_size / sizeof(Elf32_Rel); + section_p->rel_offset = section_header->sh_offset; + return SectionTypeRelData; + } else { + FURI_LOG_E(TAG, "Unknown link info section '%s'", name); + return SectionTypeERROR; + } + } + + // Load fast rel section + if(str_prefix(name, ".fast.rel")) { + name = name + strlen(".fast.rel"); + ELFSection* section_p = elf_file_get_or_put_section(elf, name); + section_p->fast_rel = malloc(sizeof(ELFSection)); + + if(!elf_load_section_data(elf, section_p->fast_rel, section_header)) { + FURI_LOG_E(TAG, "Error loading section '%s'", name); + return SectionTypeERROR; + } + + FURI_LOG_D(TAG, "Loaded fast rel section for '%s'", name); + return SectionTypeFastRelData; + } + + // Load symbol table + if(strcmp(name, ".symtab") == 0) { + FURI_LOG_D(TAG, "Found .symtab section"); + elf->symbol_table = section_header->sh_offset; + elf->symbol_count = section_header->sh_size / sizeof(Elf32_Sym); + return SectionTypeSymTab; + } + + // Load string table + if(strcmp(name, ".strtab") == 0) { + FURI_LOG_D(TAG, "Found .strtab section"); + elf->symbol_table_strings = section_header->sh_offset; + return SectionTypeStrTab; + } + + // Load debug link section + if(strcmp(name, ".gnu_debuglink") == 0) { + FURI_LOG_D(TAG, "Found .gnu_debuglink section"); + if(elf_load_debug_link(elf, section_header)) { + return SectionTypeDebugLink; + } else { + return SectionTypeERROR; + } + } + + return SectionTypeUnused; +} + +static Elf32_Addr elf_address_of_by_hash(ELFFile* elf, uint32_t hash) { + Elf32_Addr addr = 0; + if(elf->api_interface->resolver_callback(elf->api_interface, hash, &addr)) { + return addr; + } + return ELF_INVALID_ADDRESS; +} + +static bool elf_file_find_string_by_hash(ELFFile* elf, uint32_t hash, FuriString* out) { + bool result = false; + + FuriString* symbol_name = furi_string_alloc(); + Elf32_Sym sym; + for(size_t i = 0; i < elf->symbol_count; i++) { + furi_string_reset(symbol_name); + if(elf_read_symbol(elf, i, &sym, symbol_name)) { + if(elf_symbolname_hash(furi_string_get_cstr(symbol_name)) == hash) { + furi_string_set(out, symbol_name); + result = true; + break; + } + } + } + furi_string_free(symbol_name); + + return result; +} + +static bool elf_relocate_fast(ELFFile* elf, ELFSection* s) { + UNUSED(elf); + const uint8_t* start = s->fast_rel->data; + const uint8_t version = *start; + bool no_errors = true; + + if(version != FAST_RELOCATION_VERSION) { + FURI_LOG_E(TAG, "Unsupported fast relocation version %d", version); + return false; + } + start += 1; + + const uint32_t records_count = *((uint32_t*)start); + start += 4; + FURI_LOG_D(TAG, "Fast relocation records count: %ld", records_count); + + for(uint32_t i = 0; i < records_count; i++) { + bool is_section = (*start & (0x1 << 7)) ? true : false; + uint8_t type = *start & 0x7F; + start += 1; + uint32_t hash_or_section_index = *((uint32_t*)start); + start += 4; + + uint32_t section_value = ELF_INVALID_ADDRESS; + if(is_section) { + section_value = *((uint32_t*)start); + start += 4; + } + + const uint32_t offsets_count = *((uint32_t*)start); + start += 4; + + FURI_LOG_D( + TAG, + "Fast relocation record %ld: is_section=%d, type=%d, hash_or_section_index=%lX, offsets_count=%ld", + i, + is_section, + type, + hash_or_section_index, + offsets_count); + + Elf32_Addr address = 0; + if(is_section) { + ELFSection* symSec = elf_section_of(elf, hash_or_section_index); + if(symSec) { + address = ((Elf32_Addr)symSec->data) + section_value; + } + } else { + address = elf_address_of_by_hash(elf, hash_or_section_index); + } + + if(address == ELF_INVALID_ADDRESS) { + FuriString* symbol_name = furi_string_alloc(); + if(elf_file_find_string_by_hash(elf, hash_or_section_index, symbol_name)) { + FURI_LOG_E( + TAG, + "Failed to resolve address for symbol %s (hash %lX)", + furi_string_get_cstr(symbol_name), + hash_or_section_index); + } else { + FURI_LOG_E( + TAG, + "Failed to resolve address for hash %lX (string not found)", + hash_or_section_index); + } + furi_string_free(symbol_name); + + no_errors = false; + start += 3 * offsets_count; + } else { + for(uint32_t j = 0; j < offsets_count; j++) { + uint32_t offset = *((uint32_t*)start) & 0x00FFFFFF; + start += 3; + Elf32_Addr relAddr = ((Elf32_Addr)s->data) + offset; + elf_relocate_symbol(elf, relAddr, type, address); + } + } + } + + aligned_free(s->fast_rel->data); + free(s->fast_rel); + s->fast_rel = NULL; + + return no_errors; +} + +static bool elf_relocate_section(ELFFile* elf, ELFSection* section) { + if(section->fast_rel) { + FURI_LOG_D(TAG, "Fast relocating section"); + return elf_relocate_fast(elf, section); + } else if(section->rel_count) { + FURI_LOG_D(TAG, "Relocating section"); + return elf_relocate(elf, section); + } else { + FURI_LOG_D(TAG, "No relocation index"); /* Not an error */ + } + return true; +} + +static void elf_file_call_section_list(ELFSection* section, bool reverse_order) { + if(section && section->size) { + const uint32_t* start = section->data; + const uint32_t* end = section->data + section->size; + + if(reverse_order) { + while(end > start) { + end--; + ((void (*)(void))(*end))(); + } + } else { + while(start < end) { + ((void (*)(void))(*start))(); + start++; + } + } + } +} + +/**************************************************************************************************/ +/********************************************* Public *********************************************/ +/**************************************************************************************************/ + +ELFFile* elf_file_alloc(Storage* storage, const ElfApiInterface* api_interface) { + ELFFile* elf = malloc(sizeof(ELFFile)); + elf->fd = storage_file_alloc(storage); + elf->api_interface = api_interface; + ELFSectionDict_init(elf->sections); + AddressCache_init(elf->trampoline_cache); + elf->init_array_called = false; + return elf; +} + +void elf_file_free(ELFFile* elf) { + // furi_check(!elf->init_array_called); + if(elf->init_array_called) { + FURI_LOG_W(TAG, "Init array was called, but fini array wasn't"); + elf_file_call_section_list(elf->fini_array, true); + } + + // free sections data + { + ELFSectionDict_it_t it; + for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); + ELFSectionDict_next(it)) { + const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it); + if(itref->value.data) { + aligned_free(itref->value.data); + } + if(itref->value.fast_rel) { + aligned_free(itref->value.fast_rel->data); + free(itref->value.fast_rel); + } + free((void*)itref->key); + } + + ELFSectionDict_clear(elf->sections); + } + + // free trampoline data + { + AddressCache_it_t it; + for(AddressCache_it(it, elf->trampoline_cache); !AddressCache_end_p(it); + AddressCache_next(it)) { + const AddressCache_itref_t* itref = AddressCache_cref(it); + free((void*)itref->value); + } + + AddressCache_clear(elf->trampoline_cache); + } + + if(elf->debug_link_info.debug_link) { + free(elf->debug_link_info.debug_link); + } + + elf_file_maybe_release_fd(elf); + free(elf); +} + +bool elf_file_open(ELFFile* elf, const char* path) { + Elf32_Ehdr h; + Elf32_Shdr sH; + + if(!storage_file_open(elf->fd, path, FSAM_READ, FSOM_OPEN_EXISTING) || + !storage_file_seek(elf->fd, 0, true) || + storage_file_read(elf->fd, &h, sizeof(h)) != sizeof(h) || + !storage_file_seek(elf->fd, h.e_shoff + h.e_shstrndx * sizeof(sH), true) || + storage_file_read(elf->fd, &sH, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr)) { + return false; + } + + elf->entry = h.e_entry; + elf->sections_count = h.e_shnum; + elf->section_table = h.e_shoff; + elf->section_table_strings = sH.sh_offset; + return true; +} + +bool elf_file_load_section_table(ELFFile* elf) { + SectionType loaded_sections = SectionTypeERROR; + FuriString* name = furi_string_alloc(); + + FURI_LOG_D(TAG, "Scan ELF indexs..."); + // TODO FL-3526: why we start from 1? + for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) { + Elf32_Shdr section_header; + + furi_string_reset(name); + if(!elf_read_section(elf, section_idx, §ion_header, name)) { + loaded_sections = SectionTypeERROR; + break; + } + + FURI_LOG_D( + TAG, "Preloading data for section #%d %s", section_idx, furi_string_get_cstr(name)); + SectionType section_type = elf_preload_section(elf, section_idx, §ion_header, name); + loaded_sections |= section_type; + + if(section_type == SectionTypeERROR) { + loaded_sections = SectionTypeERROR; + break; + } + } + + furi_string_free(name); + + return IS_FLAGS_SET(loaded_sections, SectionTypeValid); +} + +ElfProcessSectionResult elf_process_section( + ELFFile* elf, + const char* name, + ElfProcessSection* process_section, + void* context) { + ElfProcessSectionResult result = ElfProcessSectionResultNotFound; + FuriString* section_name = furi_string_alloc(); + Elf32_Shdr section_header; + + // find section + // TODO FL-3526: why we start from 1? + for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) { + furi_string_reset(section_name); + if(!elf_read_section(elf, section_idx, §ion_header, section_name)) { + break; + } + + if(furi_string_cmp(section_name, name) == 0) { + result = ElfProcessSectionResultCannotProcess; + break; + } + } + + if(result != ElfProcessSectionResultNotFound) { //-V547 + if(process_section(elf->fd, section_header.sh_offset, section_header.sh_size, context)) { + result = ElfProcessSectionResultSuccess; + } else { + result = ElfProcessSectionResultCannotProcess; //-V1048 + } + } + + furi_string_free(section_name); + + return result; +} + +ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) { + furi_check(elf->fd != NULL); + ELFFileLoadStatus status = ELFFileLoadStatusSuccess; + ELFSectionDict_it_t it; + + AddressCache_init(elf->relocation_cache); + + for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) { + ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it); + FURI_LOG_D(TAG, "Relocating section '%s'", itref->key); + if(!elf_relocate_section(elf, &itref->value)) { + FURI_LOG_E(TAG, "Error relocating section '%s'", itref->key); + status = ELFFileLoadStatusMissingImports; + } + } + + /* Fixing up entry point */ + if(status == ELFFileLoadStatusSuccess) { + ELFSection* text_section = elf_file_get_section(elf, ".text"); + + if(text_section == NULL) { + FURI_LOG_E(TAG, "No .text section found"); + status = ELFFileLoadStatusUnspecifiedError; + } else { + elf->entry += (uint32_t)text_section->data; + } + } + + FURI_LOG_D(TAG, "Relocation cache size: %u", AddressCache_size(elf->relocation_cache)); + FURI_LOG_D(TAG, "Trampoline cache size: %u", AddressCache_size(elf->trampoline_cache)); + AddressCache_clear(elf->relocation_cache); + + { + size_t total_size = 0; + for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); + ELFSectionDict_next(it)) { + ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it); + total_size += itref->value.size; + } + FURI_LOG_I(TAG, "Total size of loaded sections: %zu", total_size); + } + + elf_file_maybe_release_fd(elf); + return status; +} + +void elf_file_call_init(ELFFile* elf) { + furi_check(!elf->init_array_called); + elf_file_call_section_list(elf->preinit_array, false); + elf_file_call_section_list(elf->init_array, false); + elf->init_array_called = true; +} + +bool elf_file_is_init_complete(ELFFile* elf) { + return elf->init_array_called; +} + +void* elf_file_get_entry_point(ELFFile* elf) { + furi_check(elf->init_array_called); + return (void*)elf->entry; +} + +void elf_file_call_fini(ELFFile* elf) { + furi_check(elf->init_array_called); + elf_file_call_section_list(elf->fini_array, true); + elf->init_array_called = false; +} + +const ElfApiInterface* elf_file_get_api_interface(ELFFile* elf_file) { + return elf_file->api_interface; +} + +void elf_file_init_debug_info(ELFFile* elf, ELFDebugInfo* debug_info) { + // set entry + debug_info->entry = elf->entry; + + // copy debug info + memcpy(&debug_info->debug_link_info, &elf->debug_link_info, sizeof(ELFDebugLinkInfo)); + + // init mmap + debug_info->mmap_entry_count = ELFSectionDict_size(elf->sections); + debug_info->mmap_entries = malloc(sizeof(ELFMemoryMapEntry) * debug_info->mmap_entry_count); + uint32_t mmap_entry_idx = 0; + + ELFSectionDict_it_t it; + for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) { + const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it); + + const void* data_ptr = itref->value.data; + if(data_ptr) { + ELFMemoryMapEntry* entry = &debug_info->mmap_entries[mmap_entry_idx]; + entry->address = (uint32_t)data_ptr; + entry->name = itref->key; + mmap_entry_idx++; + } + } +} + +void elf_file_clear_debug_info(ELFDebugInfo* debug_info) { + // clear debug info + memset(&debug_info->debug_link_info, 0, sizeof(ELFDebugLinkInfo)); + + // clear mmap + if(debug_info->mmap_entries) { + free(debug_info->mmap_entries); + debug_info->mmap_entries = NULL; + } + + debug_info->mmap_entry_count = 0; +} diff --git a/lib/flipper_application/elf/elf_file.h b/lib/flipper_application/elf/elf_file.h new file mode 100644 index 00000000000..631fe122f93 --- /dev/null +++ b/lib/flipper_application/elf/elf_file.h @@ -0,0 +1,151 @@ +/** + * @file elf_file.h + * ELF file loader + */ +#pragma once +#include +#include "../application_manifest.h" +#include "elf_api_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ELFFile ELFFile; + +typedef struct { + const char* name; + uint32_t address; +} ELFMemoryMapEntry; + +typedef struct { + uint32_t debug_link_size; + uint8_t* debug_link; +} ELFDebugLinkInfo; + +typedef struct { + uint32_t mmap_entry_count; + ELFMemoryMapEntry* mmap_entries; + ELFDebugLinkInfo debug_link_info; + off_t entry; +} ELFDebugInfo; + +typedef enum { + ELFFileLoadStatusSuccess = 0, + ELFFileLoadStatusUnspecifiedError, + ELFFileLoadStatusNoFreeMemory, + ELFFileLoadStatusMissingImports, +} ELFFileLoadStatus; + +typedef enum { + ElfProcessSectionResultNotFound, + ElfProcessSectionResultCannotProcess, + ElfProcessSectionResultSuccess, +} ElfProcessSectionResult; + +typedef bool(ElfProcessSection)(File* file, size_t offset, size_t size, void* context); + +/** + * @brief Allocate ELFFile instance + * @param storage + * @param api_interface + * @return ELFFile* + */ +ELFFile* elf_file_alloc(Storage* storage, const ElfApiInterface* api_interface); + +/** + * @brief Free ELFFile instance + * @param elf_file + */ +void elf_file_free(ELFFile* elf_file); + +/** + * @brief Open ELF file + * @param elf_file + * @param path + * @return bool + */ +bool elf_file_open(ELFFile* elf_file, const char* path); + +/** + * @brief Load ELF file section table (load stage #1) + * @param elf_file + * @return bool + */ +bool elf_file_load_section_table(ELFFile* elf_file); + +/** + * @brief Load and relocate ELF file sections (load stage #2) + * @param elf_file + * @return ELFFileLoadStatus + */ +ELFFileLoadStatus elf_file_load_sections(ELFFile* elf_file); + +/** + * @brief Execute ELF file pre-run stage, + * call static constructors for example (load stage #3) + * Must be done before invoking any code from the ELF file + * @param elf + */ +void elf_file_call_init(ELFFile* elf); + +/** + * @brief Check if ELF file pre-run stage was executed and its code is runnable + * @param elf + */ +bool elf_file_is_init_complete(ELFFile* elf); + +/** + * @brief Get actual entry point for ELF file + * @param elf_file + * @param args + * @return int32_t + */ +void* elf_file_get_entry_point(ELFFile* elf_file); + +/** + * @brief Execute ELF file post-run stage, + * call static destructors for example (load stage #5) + * Must be done if any code from the ELF file was executed + * @param elf + */ +void elf_file_call_fini(ELFFile* elf); + +/** + * @brief Get ELF file API interface + * @param elf_file + * @return const ElfApiInterface* + */ +const ElfApiInterface* elf_file_get_api_interface(ELFFile* elf_file); + +/** + * @brief Get ELF file debug info + * @param elf_file + * @param debug_info + */ +void elf_file_init_debug_info(ELFFile* elf_file, ELFDebugInfo* debug_info); + +/** + * @brief Clear ELF file debug info generated by elf_file_init_debug_info + * @param debug_info + */ +void elf_file_clear_debug_info(ELFDebugInfo* debug_info); + +/** + * @brief Process ELF file section + * + * @param elf_file + * @param name + * @param process_section + * @param context + * @return ElfProcessSectionResult + */ +ElfProcessSectionResult elf_process_section( + ELFFile* elf_file, + const char* name, + ElfProcessSection* process_section, + void* context); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/flipper_application/elf/elf_file_i.h b/lib/flipper_application/elf/elf_file_i.h new file mode 100644 index 00000000000..e1b97b631cb --- /dev/null +++ b/lib/flipper_application/elf/elf_file_i.h @@ -0,0 +1,58 @@ +#pragma once +#include "elf_file.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +DICT_DEF2(AddressCache, int, M_DEFAULT_OPLIST, Elf32_Addr, M_DEFAULT_OPLIST) //-V1048 + +/** + * Callable elf entry type + */ +typedef int32_t(entry_t)(void*); + +typedef struct ELFSection ELFSection; + +struct ELFSection { + void* data; + Elf32_Word size; + + size_t rel_count; + Elf32_Off rel_offset; + ELFSection* fast_rel; + + uint16_t sec_idx; +}; + +DICT_DEF2(ELFSectionDict, const char*, M_CSTR_OPLIST, ELFSection, M_POD_OPLIST) + +struct ELFFile { + size_t sections_count; + off_t section_table; + off_t section_table_strings; + + size_t symbol_count; + off_t symbol_table; + off_t symbol_table_strings; + off_t entry; + ELFSectionDict_t sections; + + AddressCache_t relocation_cache; + AddressCache_t trampoline_cache; + + File* fd; + const ElfApiInterface* api_interface; + ELFDebugLinkInfo debug_link_info; + + ELFSection* preinit_array; + ELFSection* init_array; + ELFSection* fini_array; + + bool init_array_called; +}; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/flipper_application/flipper_application.c b/lib/flipper_application/flipper_application.c new file mode 100644 index 00000000000..d56a8a7ef06 --- /dev/null +++ b/lib/flipper_application/flipper_application.c @@ -0,0 +1,342 @@ +#include "flipper_application.h" +#include "elf/elf_file.h" +#include +#include "application_assets.h" +#include + +#include + +#define TAG "Fap" + +struct FlipperApplication { + ELFDebugInfo state; + FlipperApplicationManifest manifest; + ELFFile* elf; + FuriThread* thread; + void* ep_thread_args; +}; + +/********************** Debugger access to loader state **********************/ + +LIST_DEF(FlipperApplicationList, const FlipperApplication*, M_POD_OPLIST); + +FlipperApplicationList_t flipper_application_loaded_app_list = {0}; +static bool flipper_application_loaded_app_list_initialized = false; + +static void flipper_application_list_add_app(const FlipperApplication* app) { + furi_assert(app); + + if(!flipper_application_loaded_app_list_initialized) { + FlipperApplicationList_init(flipper_application_loaded_app_list); + flipper_application_loaded_app_list_initialized = true; + } + FlipperApplicationList_push_back(flipper_application_loaded_app_list, app); +} + +static void flipper_application_list_remove_app(const FlipperApplication* app) { + furi_assert(flipper_application_loaded_app_list_initialized); + furi_assert(app); + + FlipperApplicationList_it_t it; + for(FlipperApplicationList_it(it, flipper_application_loaded_app_list); + !FlipperApplicationList_end_p(it); + FlipperApplicationList_next(it)) { + if(*FlipperApplicationList_ref(it) == app) { + FlipperApplicationList_remove(flipper_application_loaded_app_list, it); + break; + } + } +} + +/*****************************************************************************/ + +FlipperApplication* + flipper_application_alloc(Storage* storage, const ElfApiInterface* api_interface) { + FlipperApplication* app = malloc(sizeof(FlipperApplication)); + app->elf = elf_file_alloc(storage, api_interface); + app->thread = NULL; + app->ep_thread_args = NULL; + return app; +} + +bool flipper_application_is_plugin(FlipperApplication* app) { + return app->manifest.stack_size == 0; +} + +void flipper_application_free(FlipperApplication* app) { + furi_assert(app); + + if(app->thread) { + furi_thread_join(app->thread); + furi_thread_free(app->thread); + } + + if(app->state.entry) { + flipper_application_list_remove_app(app); + } + + elf_file_clear_debug_info(&app->state); + + if(elf_file_is_init_complete(app->elf)) { + elf_file_call_fini(app->elf); + } + + elf_file_free(app->elf); + + if(app->ep_thread_args) { + free(app->ep_thread_args); + app->ep_thread_args = NULL; + } + + free(app); +} + +static FlipperApplicationPreloadStatus + flipper_application_validate_manifest(FlipperApplication* app) { + if(!flipper_application_manifest_is_valid(&app->manifest)) { + return FlipperApplicationPreloadStatusInvalidManifest; + } + + if(!flipper_application_manifest_is_target_compatible(&app->manifest)) { + return FlipperApplicationPreloadStatusTargetMismatch; + } + + if(!flipper_application_manifest_is_too_old( + &app->manifest, elf_file_get_api_interface(app->elf))) { + return FlipperApplicationPreloadStatusApiTooOld; + } + + if(!flipper_application_manifest_is_too_new( + &app->manifest, elf_file_get_api_interface(app->elf))) { + return FlipperApplicationPreloadStatusApiTooNew; + } + + return FlipperApplicationPreloadStatusSuccess; +} + +static bool flipper_application_process_manifest_section( + File* file, + size_t offset, + size_t size, + void* context) { + FlipperApplicationManifest* manifest = context; + + if(size < sizeof(FlipperApplicationManifest)) { + return false; + } + + if(manifest == NULL) { + return true; + } + + return storage_file_seek(file, offset, true) && + storage_file_read(file, manifest, size) == size; +} + +// we can't use const char* as context because we will lose the const qualifier +typedef struct { + const char* path; +} FlipperApplicationPreloadAssetsContext; + +static bool flipper_application_process_assets_section( + File* file, + size_t offset, + size_t size, + void* context) { + FlipperApplicationPreloadAssetsContext* preload_context = context; + return flipper_application_assets_load(file, preload_context->path, offset, size); +} + +static FlipperApplicationPreloadStatus + flipper_application_load(FlipperApplication* app, const char* path, bool load_full) { + if(!elf_file_open(app->elf, path)) { + return FlipperApplicationPreloadStatusInvalidFile; + } + + // if we are loading full file + if(load_full) { + // load section table + if(!elf_file_load_section_table(app->elf)) { + return FlipperApplicationPreloadStatusInvalidFile; + } + + // load assets section + FlipperApplicationPreloadAssetsContext preload_context = {.path = path}; + if(elf_process_section( + app->elf, + ".fapassets", + flipper_application_process_assets_section, + &preload_context) == ElfProcessSectionResultCannotProcess) { + return FlipperApplicationPreloadStatusInvalidFile; + } + } + + // load manifest section + if(elf_process_section( + app->elf, ".fapmeta", flipper_application_process_manifest_section, &app->manifest) != + ElfProcessSectionResultSuccess) { + return FlipperApplicationPreloadStatusInvalidFile; + } + + return flipper_application_validate_manifest(app); +} + +/* Parse headers, load manifest */ +FlipperApplicationPreloadStatus + flipper_application_preload_manifest(FlipperApplication* app, const char* path) { + return flipper_application_load(app, path, false); +} + +/* Parse headers, load full file */ +FlipperApplicationPreloadStatus + flipper_application_preload(FlipperApplication* app, const char* path) { + return flipper_application_load(app, path, true); +} + +const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplication* app) { + return &app->manifest; +} + +FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app) { + ELFFileLoadStatus status = elf_file_load_sections(app->elf); + + switch(status) { + case ELFFileLoadStatusSuccess: + elf_file_init_debug_info(app->elf, &app->state); + flipper_application_list_add_app(app); + return FlipperApplicationLoadStatusSuccess; + case ELFFileLoadStatusNoFreeMemory: + return FlipperApplicationLoadStatusNoFreeMemory; + case ELFFileLoadStatusMissingImports: + return FlipperApplicationLoadStatusMissingImports; + default: + return FlipperApplicationLoadStatusUnspecifiedError; + } +} + +static int32_t flipper_application_thread(void* context) { + furi_assert(context); + FlipperApplication* app = (FlipperApplication*)context; + + elf_file_call_init(app->elf); + + FlipperApplicationEntryPoint entry_point = elf_file_get_entry_point(app->elf); + int32_t ret_code = entry_point(app->ep_thread_args); + + elf_file_call_fini(app->elf); + + // wait until all notifications from RAM are completed + NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION); + const NotificationSequence sequence_empty = { + NULL, + }; + notification_message_block(notifications, &sequence_empty); + furi_record_close(RECORD_NOTIFICATION); + + return ret_code; +} + +FuriThread* flipper_application_alloc_thread(FlipperApplication* app, const char* args) { + furi_check(app->thread == NULL); + furi_check(!flipper_application_is_plugin(app)); + + if(app->ep_thread_args) { + free(app->ep_thread_args); + } + + if(args) { + app->ep_thread_args = strdup(args); + } else { + app->ep_thread_args = NULL; + } + + const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); + app->thread = furi_thread_alloc_ex( + manifest->name, manifest->stack_size, flipper_application_thread, app); + + return app->thread; +} + +static const char* preload_status_strings[] = { + [FlipperApplicationPreloadStatusSuccess] = "Success", + [FlipperApplicationPreloadStatusUnspecifiedError] = "Unknown error", + [FlipperApplicationPreloadStatusInvalidFile] = "Invalid file", + [FlipperApplicationPreloadStatusInvalidManifest] = "Invalid file manifest", + [FlipperApplicationPreloadStatusApiTooOld] = "Update Application to use with this Firmware (ApiTooOld)", + [FlipperApplicationPreloadStatusApiTooNew] = "Update Firmware to use with this Application (ApiTooNew)", + [FlipperApplicationPreloadStatusTargetMismatch] = "Hardware target mismatch", +}; + +static const char* load_status_strings[] = { + [FlipperApplicationLoadStatusSuccess] = "Success", + [FlipperApplicationLoadStatusUnspecifiedError] = "Unknown error", + [FlipperApplicationLoadStatusNoFreeMemory] = "Out of memory", + [FlipperApplicationLoadStatusMissingImports] = "Update Firmware to use with this Application (MissingImports)", +}; + +const char* flipper_application_preload_status_to_string(FlipperApplicationPreloadStatus status) { + if(status >= COUNT_OF(preload_status_strings) || preload_status_strings[status] == NULL) { + return "Unknown error"; + } + return preload_status_strings[status]; +} + +const char* flipper_application_load_status_to_string(FlipperApplicationLoadStatus status) { + if(status >= COUNT_OF(load_status_strings) || load_status_strings[status] == NULL) { + return "Unknown error"; + } + return load_status_strings[status]; +} + +const FlipperAppPluginDescriptor* + flipper_application_plugin_get_descriptor(FlipperApplication* app) { + if(!flipper_application_is_plugin(app)) { + return NULL; + } + + if(!elf_file_is_init_complete(app->elf)) { + elf_file_call_init(app->elf); + } + + typedef const FlipperAppPluginDescriptor* (*get_lib_descriptor_t)(void); + get_lib_descriptor_t lib_ep = elf_file_get_entry_point(app->elf); + furi_check(lib_ep); + + const FlipperAppPluginDescriptor* lib_descriptor = lib_ep(); + + FURI_LOG_D( + TAG, + "Library for %s, API v. %lu loaded", + lib_descriptor->appid, + lib_descriptor->ep_api_version); + + return lib_descriptor; +} + +bool flipper_application_load_name_and_icon( + FuriString* path, + Storage* storage, + uint8_t** icon_ptr, + FuriString* item_name) { + FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface); + + FlipperApplicationPreloadStatus preload_res = + flipper_application_preload_manifest(app, furi_string_get_cstr(path)); + + bool load_success = false; + + if(preload_res == FlipperApplicationPreloadStatusSuccess) { + const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); + if(manifest->has_icon) { + memcpy(*icon_ptr, manifest->icon, FAP_MANIFEST_MAX_ICON_SIZE); + } + furi_string_set(item_name, manifest->name); + load_success = true; + } else { + FURI_LOG_E(TAG, "Failed to preload %s", furi_string_get_cstr(path)); + load_success = false; + } + + flipper_application_free(app); + return load_success; +} \ No newline at end of file diff --git a/lib/flipper_application/flipper_application.h b/lib/flipper_application/flipper_application.h new file mode 100644 index 00000000000..a119cf530de --- /dev/null +++ b/lib/flipper_application/flipper_application.h @@ -0,0 +1,170 @@ +/** + * @file flipper_application.h + * Flipper application + */ +#pragma once + +#include "application_manifest.h" +#include "elf/elf_api_interface.h" + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + FlipperApplicationPreloadStatusSuccess = 0, + FlipperApplicationPreloadStatusUnspecifiedError, + FlipperApplicationPreloadStatusInvalidFile, + FlipperApplicationPreloadStatusInvalidManifest, + FlipperApplicationPreloadStatusApiTooOld, + FlipperApplicationPreloadStatusApiTooNew, + FlipperApplicationPreloadStatusTargetMismatch, +} FlipperApplicationPreloadStatus; + +typedef enum { + FlipperApplicationLoadStatusSuccess = 0, + FlipperApplicationLoadStatusUnspecifiedError, + FlipperApplicationLoadStatusNoFreeMemory, + FlipperApplicationLoadStatusMissingImports, +} FlipperApplicationLoadStatus; + +/** + * @brief Get text description of preload status + * @param status Status code + * @return String pointer to description + */ +const char* flipper_application_preload_status_to_string(FlipperApplicationPreloadStatus status); + +/** + * @brief Get text description of load status + * @param status Status code + * @return String pointer to description + */ +const char* flipper_application_load_status_to_string(FlipperApplicationLoadStatus status); + +typedef struct FlipperApplication FlipperApplication; + +typedef struct { + const char* name; + uint32_t address; +} FlipperApplicationMemoryMapEntry; + +typedef struct { + uint32_t mmap_entry_count; + FlipperApplicationMemoryMapEntry* mmap_entries; + uint32_t debug_link_size; + uint8_t* debug_link; +} FlipperApplicationState; + +/** + * @brief Initialize FlipperApplication object + * @param storage Storage instance + * @param api_interface ELF API interface to use for pre-loading and symbol resolving + * @return Application instance + */ +FlipperApplication* + flipper_application_alloc(Storage* storage, const ElfApiInterface* api_interface); + +/** + * @brief Destroy FlipperApplication object + * @param app Application pointer + */ +void flipper_application_free(FlipperApplication* app); + +/** + * @brief Validate elf file and load application metadata + * @param app Application pointer + * @return Preload result code + */ +FlipperApplicationPreloadStatus + flipper_application_preload(FlipperApplication* app, const char* path); + +/** + * @brief Validate elf file and load application manifest + * @param app Application pointer + * @return Preload result code + */ +FlipperApplicationPreloadStatus + flipper_application_preload_manifest(FlipperApplication* app, const char* path); + +/** + * @brief Get pointer to application manifest for preloaded application + * @param app Application pointer + * @return Pointer to application manifest + */ +const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplication* app); + +/** + * @brief Load sections and process relocations for already pre-loaded application + * @param app Application pointer + * @return Load result code + */ +FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app); + +/** + * @brief Allocate application thread at entry point address, using app name and + * stack size from metadata. Returned thread isn't started yet. + * Can be only called once for application instance. + * @param app Applicaiton pointer + * @param args Args to pass to app's entry point + * @return Created thread + */ +FuriThread* flipper_application_alloc_thread(FlipperApplication* app, const char* args); + +/** + * @brief Check if application is a plugin (not a runnable standalone app) + * @param app Application pointer + * @return true if application is a plugin, false otherwise + */ +bool flipper_application_is_plugin(FlipperApplication* app); + +/** + * @brief Entry point prototype for standalone applications + */ +typedef int32_t (*FlipperApplicationEntryPoint)(void*); + +/** + * @brief An object that describes a plugin - must be returned by plugin's entry point + */ +typedef struct { + const char* appid; + const uint32_t ep_api_version; + const void* entry_point; +} FlipperAppPluginDescriptor; + +/** + * @brief Entry point prototype for plugins + */ +typedef const FlipperAppPluginDescriptor* (*FlipperApplicationPluginEntryPoint)(void); + +/** + * @brief Get plugin descriptor for preloaded plugin + * @param app Application pointer + * @return Pointer to plugin descriptor + */ +const FlipperAppPluginDescriptor* + flipper_application_plugin_get_descriptor(FlipperApplication* app); + +/** + * @brief Load name and icon from FAP file. + * + * @param path Path to FAP file. + * @param storage Storage instance. + * @param icon_ptr Icon pointer. + * @param item_name Application name. + * @return true if icon and name were loaded successfully. + */ +bool flipper_application_load_name_and_icon( + FuriString* path, + Storage* storage, + uint8_t** icon_ptr, + FuriString* item_name); + +#ifdef __cplusplus +} +#endif diff --git a/lib/flipper_application/plugins/composite_resolver.c b/lib/flipper_application/plugins/composite_resolver.c new file mode 100644 index 00000000000..7cc2b340a96 --- /dev/null +++ b/lib/flipper_application/plugins/composite_resolver.c @@ -0,0 +1,52 @@ +#include "composite_resolver.h" + +#include +#include + +LIST_DEF(ElfApiInterfaceList, const ElfApiInterface*, M_POD_OPLIST) +#define M_OPL_ElfApiInterfaceList_t() LIST_OPLIST(ElfApiInterfaceList, M_POD_OPLIST) + +struct CompositeApiResolver { + ElfApiInterface api_interface; + ElfApiInterfaceList_t interfaces; +}; + +static bool composite_api_resolver_callback( + const ElfApiInterface* interface, + uint32_t hash, + Elf32_Addr* address) { + CompositeApiResolver* resolver = (CompositeApiResolver*)interface; + for + M_EACH(interface, resolver->interfaces, ElfApiInterfaceList_t) { + if((*interface)->resolver_callback(*interface, hash, address)) { + return true; + } + } + return false; +} + +CompositeApiResolver* composite_api_resolver_alloc() { + CompositeApiResolver* resolver = malloc(sizeof(CompositeApiResolver)); + resolver->api_interface.api_version_major = 0; + resolver->api_interface.api_version_minor = 0; + resolver->api_interface.resolver_callback = &composite_api_resolver_callback; + ElfApiInterfaceList_init(resolver->interfaces); + return resolver; +} + +void composite_api_resolver_free(CompositeApiResolver* resolver) { + ElfApiInterfaceList_clear(resolver->interfaces); + free(resolver); +} + +void composite_api_resolver_add(CompositeApiResolver* resolver, const ElfApiInterface* interface) { + if(ElfApiInterfaceList_empty_p(resolver->interfaces)) { + resolver->api_interface.api_version_major = interface->api_version_major; + resolver->api_interface.api_version_minor = interface->api_version_minor; + } + ElfApiInterfaceList_push_back(resolver->interfaces, interface); +} + +const ElfApiInterface* composite_api_resolver_get(CompositeApiResolver* resolver) { + return &resolver->api_interface; +} diff --git a/lib/flipper_application/plugins/composite_resolver.h b/lib/flipper_application/plugins/composite_resolver.h new file mode 100644 index 00000000000..a2d4bab25e8 --- /dev/null +++ b/lib/flipper_application/plugins/composite_resolver.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Composite API resolver + * Resolves API interface by calling all resolvers in order + * Uses API version from first resolver + * Note: when using hashtable resolvers, collisions between tables are not detected + * Can be cast to ElfApiInterface* + */ +typedef struct CompositeApiResolver CompositeApiResolver; + +/** + * @brief Allocate composite API resolver + * @return CompositeApiResolver* instance + */ +CompositeApiResolver* composite_api_resolver_alloc(); + +/** + * @brief Free composite API resolver + * @param resolver Instance + */ +void composite_api_resolver_free(CompositeApiResolver* resolver); + +/** + * @brief Add API resolver to composite resolver + * @param resolver Instance + * @param interface API resolver + */ +void composite_api_resolver_add(CompositeApiResolver* resolver, const ElfApiInterface* interface); + +/** + * @brief Get API interface from composite resolver + * @param resolver Instance + * @return API interface + */ +const ElfApiInterface* composite_api_resolver_get(CompositeApiResolver* resolver); + +#ifdef __cplusplus +} +#endif diff --git a/lib/flipper_application/plugins/plugin_manager.c b/lib/flipper_application/plugins/plugin_manager.c new file mode 100644 index 00000000000..8f30ed13ec5 --- /dev/null +++ b/lib/flipper_application/plugins/plugin_manager.c @@ -0,0 +1,154 @@ +#include "plugin_manager.h" + +#include +#include +#include + +#include +#include + +#include + +#define TAG "PluginManager" + +ARRAY_DEF(FlipperApplicationList, FlipperApplication*, M_PTR_OPLIST) +#define M_OPL_FlipperApplicationList_t() ARRAY_OPLIST(FlipperApplicationList, M_PTR_OPLIST) + +struct PluginManager { + const char* application_id; + uint32_t api_version; + Storage* storage; + FlipperApplicationList_t libs; + const ElfApiInterface* api_interface; +}; + +PluginManager* plugin_manager_alloc( + const char* application_id, + uint32_t api_version, + const ElfApiInterface* api_interface) { + PluginManager* manager = malloc(sizeof(PluginManager)); + manager->application_id = application_id; + manager->api_version = api_version; + manager->api_interface = api_interface ? api_interface : firmware_api_interface; + manager->storage = furi_record_open(RECORD_STORAGE); + FlipperApplicationList_init(manager->libs); + return manager; +} + +void plugin_manager_free(PluginManager* manager) { + for + M_EACH(loaded_lib, manager->libs, FlipperApplicationList_t) { + flipper_application_free(*loaded_lib); + } + FlipperApplicationList_clear(manager->libs); + furi_record_close(RECORD_STORAGE); + free(manager); +} + +PluginManagerError plugin_manager_load_single(PluginManager* manager, const char* path) { + FlipperApplication* lib = flipper_application_alloc(manager->storage, manager->api_interface); + + PluginManagerError error = PluginManagerErrorNone; + do { + FlipperApplicationPreloadStatus preload_res = flipper_application_preload(lib, path); + + if(preload_res != FlipperApplicationPreloadStatusSuccess) { + FURI_LOG_E(TAG, "Failed to preload %s", path); + error = PluginManagerErrorLoaderError; + break; + } + + if(!flipper_application_is_plugin(lib)) { + FURI_LOG_E(TAG, "Not a plugin %s", path); + error = PluginManagerErrorLoaderError; + break; + } + + FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(lib); + if(load_status != FlipperApplicationLoadStatusSuccess) { + FURI_LOG_E(TAG, "Failed to load %s", path); + error = PluginManagerErrorLoaderError; + break; + } + + const FlipperAppPluginDescriptor* app_descriptor = + flipper_application_plugin_get_descriptor(lib); + + if(!app_descriptor) { + FURI_LOG_E(TAG, "Failed to get descriptor %s", path); + error = PluginManagerErrorLoaderError; + break; + } + + if(strcmp(app_descriptor->appid, manager->application_id) != 0) { + FURI_LOG_E(TAG, "Application id mismatch %s", path); + error = PluginManagerErrorApplicationIdMismatch; + break; + } + + if(app_descriptor->ep_api_version != manager->api_version) { + FURI_LOG_E(TAG, "API version mismatch %s", path); + error = PluginManagerErrorAPIVersionMismatch; + break; + } + + FlipperApplicationList_push_back(manager->libs, lib); + } while(false); + + if(error != PluginManagerErrorNone) { + flipper_application_free(lib); + } + + return error; +} + +PluginManagerError plugin_manager_load_all(PluginManager* manager, const char* path) { + File* directory = storage_file_alloc(manager->storage); + char file_name_buffer[256]; + FuriString* file_name = furi_string_alloc(); + do { + if(!storage_dir_open(directory, path)) { + FURI_LOG_E(TAG, "Failed to open directory %s", path); + break; + } + while(true) { + if(!storage_dir_read(directory, NULL, file_name_buffer, sizeof(file_name_buffer))) { + break; + } + + furi_string_set(file_name, file_name_buffer); + if(!furi_string_end_with_str(file_name, ".fal")) { + continue; + } + + path_concat(path, file_name_buffer, file_name); + FURI_LOG_D(TAG, "Loading %s", furi_string_get_cstr(file_name)); + PluginManagerError error = + plugin_manager_load_single(manager, furi_string_get_cstr(file_name)); + + if(error != PluginManagerErrorNone) { + FURI_LOG_E(TAG, "Failed to load %s", furi_string_get_cstr(file_name)); + break; + } + } + } while(false); + storage_dir_close(directory); + storage_file_free(directory); + furi_string_free(file_name); + return PluginManagerErrorNone; +} + +uint32_t plugin_manager_get_count(PluginManager* manager) { + return FlipperApplicationList_size(manager->libs); +} + +const FlipperAppPluginDescriptor* plugin_manager_get(PluginManager* manager, uint32_t index) { + FlipperApplication* app = *FlipperApplicationList_get(manager->libs, index); + return flipper_application_plugin_get_descriptor(app); +} + +const void* plugin_manager_get_ep(PluginManager* manager, uint32_t index) { + const FlipperAppPluginDescriptor* lib_descr = plugin_manager_get(manager, index); + furi_check(lib_descr); + return lib_descr->entry_point; +} diff --git a/lib/flipper_application/plugins/plugin_manager.h b/lib/flipper_application/plugins/plugin_manager.h new file mode 100644 index 00000000000..d94c25db971 --- /dev/null +++ b/lib/flipper_application/plugins/plugin_manager.h @@ -0,0 +1,82 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Object that manages plugins for an application + * Implements mass loading of plugins and provides access to their descriptors + */ +typedef struct PluginManager PluginManager; + +typedef enum { + PluginManagerErrorNone = 0, + PluginManagerErrorLoaderError, + PluginManagerErrorApplicationIdMismatch, + PluginManagerErrorAPIVersionMismatch, +} PluginManagerError; + +/** + * @brief Allocates new PluginManager + * @param application_id Application ID filter - only plugins with matching ID will be loaded + * @param api_version Application API version filter - only plugins with matching API version + * @param api_interface Application API interface - used to resolve plugins' API imports + * If plugin uses private application's API, use CompoundApiInterface + * @return new PluginManager instance + */ +PluginManager* plugin_manager_alloc( + const char* application_id, + uint32_t api_version, + const ElfApiInterface* api_interface); + +/** + * @brief Frees PluginManager + * @param manager PluginManager instance + */ +void plugin_manager_free(PluginManager* manager); + +/** + * @brief Loads single plugin by full path + * @param manager PluginManager instance + * @param path Path to plugin + * @return Error code + */ +PluginManagerError plugin_manager_load_single(PluginManager* manager, const char* path); + +/** + * @brief Loads all plugins from specified directory + * @param manager PluginManager instance + * @param path Path to directory + * @return Error code + */ +PluginManagerError plugin_manager_load_all(PluginManager* manager, const char* path); + +/** + * @brief Returns number of loaded plugins + * @param manager PluginManager instance + * @return Number of loaded plugins + */ +uint32_t plugin_manager_get_count(PluginManager* manager); + +/** + * @brief Returns plugin descriptor by index + * @param manager PluginManager instance + * @param index Plugin index + * @return Plugin descriptor + */ +const FlipperAppPluginDescriptor* plugin_manager_get(PluginManager* manager, uint32_t index); + +/** + * @brief Returns plugin entry point by index + * @param manager PluginManager instance + * @param index Plugin index + * @return Plugin entry point + */ +const void* plugin_manager_get_ep(PluginManager* manager, uint32_t index); + +#ifdef __cplusplus +} +#endif diff --git a/lib/flipper_format/SConscript b/lib/flipper_format/SConscript index e5d61a075aa..f16cd4f391c 100644 --- a/lib/flipper_format/SConscript +++ b/lib/flipper_format/SConscript @@ -4,6 +4,14 @@ env.Append( CPPPATH=[ "#/lib/flipper_format", ], + SDK_HEADERS=[ + File("flipper_format.h"), + File("flipper_format_i.h"), + File("flipper_format_stream.h"), + ], + LINT_SOURCES=[ + Dir("."), + ], ) diff --git a/lib/flipper_format/flipper_format.c b/lib/flipper_format/flipper_format.c index 620051276f4..bb1aa59f586 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); @@ -137,7 +143,7 @@ bool flipper_format_key_exist(FlipperFormat* flipper_format, const char* key) { bool flipper_format_read_header( FlipperFormat* flipper_format, - string_t filetype, + FuriString* filetype, uint32_t* version) { furi_assert(flipper_format); return flipper_format_read_string(flipper_format, flipper_format_filetype_key, filetype) && @@ -146,10 +152,11 @@ bool flipper_format_read_header( bool flipper_format_write_header( FlipperFormat* flipper_format, - string_t filetype, + FuriString* filetype, const uint32_t version) { furi_assert(flipper_format); - return flipper_format_write_header_cstr(flipper_format, string_get_cstr(filetype), version); + return flipper_format_write_header_cstr( + flipper_format, furi_string_get_cstr(filetype), version); } bool flipper_format_write_header_cstr( @@ -171,18 +178,18 @@ bool flipper_format_get_value_count( flipper_format->stream, key, count, flipper_format->strict_mode); } -bool flipper_format_read_string(FlipperFormat* flipper_format, const char* key, string_t data) { +bool flipper_format_read_string(FlipperFormat* flipper_format, const char* key, FuriString* data) { furi_assert(flipper_format); return flipper_format_stream_read_value_line( flipper_format->stream, key, FlipperStreamValueStr, data, 1, flipper_format->strict_mode); } -bool flipper_format_write_string(FlipperFormat* flipper_format, const char* key, string_t data) { +bool flipper_format_write_string(FlipperFormat* flipper_format, const char* key, FuriString* data) { furi_assert(flipper_format); FlipperStreamWriteData write_data = { .key = key, .type = FlipperStreamValueStr, - .data = string_get_cstr(data), + .data = furi_string_get_cstr(data), .data_size = 1, }; bool result = flipper_format_stream_write_value_line(flipper_format->stream, &write_data); @@ -386,9 +393,9 @@ bool flipper_format_write_hex( return result; } -bool flipper_format_write_comment(FlipperFormat* flipper_format, string_t data) { +bool flipper_format_write_comment(FlipperFormat* flipper_format, FuriString* data) { furi_assert(flipper_format); - return flipper_format_write_comment_cstr(flipper_format, string_get_cstr(data)); + return flipper_format_write_comment_cstr(flipper_format, furi_string_get_cstr(data)); } bool flipper_format_write_comment_cstr(FlipperFormat* flipper_format, const char* data) { @@ -409,12 +416,12 @@ bool flipper_format_delete_key(FlipperFormat* flipper_format, const char* key) { return result; } -bool flipper_format_update_string(FlipperFormat* flipper_format, const char* key, string_t data) { +bool flipper_format_update_string(FlipperFormat* flipper_format, const char* key, FuriString* data) { furi_assert(flipper_format); FlipperStreamWriteData write_data = { .key = key, .type = FlipperStreamValueStr, - .data = string_get_cstr(data), + .data = furi_string_get_cstr(data), .data_size = 1, }; bool result = flipper_format_stream_delete_key_and_write( @@ -522,7 +529,7 @@ bool flipper_format_update_hex( bool flipper_format_insert_or_update_string( FlipperFormat* flipper_format, const char* key, - string_t data) { + FuriString* data) { bool result = false; if(!flipper_format_key_exist(flipper_format, key)) { diff --git a/lib/flipper_format/flipper_format.h b/lib/flipper_format/flipper_format.h index 6163dee0e6d..671ff6fa0cc 100644 --- a/lib/flipper_format/flipper_format.h +++ b/lib/flipper_format/flipper_format.h @@ -41,7 +41,7 @@ * Writing: * * ~~~~~~~~~~~~~~~~~~~~~ - * FlipperFormat format = flipper_format_file_alloc(storage); + * FlipperFormat* format = flipper_format_file_alloc(storage); * * do { * const uint32_t version = 1; @@ -66,17 +66,17 @@ * Reading: * * ~~~~~~~~~~~~~~~~~~~~~ - * FlipperFormat file = flipper_format_file_alloc(storage); + * FlipperFormat* file = flipper_format_file_alloc(storage); * * do { * uint32_t version = 1; - * string_t file_type; - * string_t string_value; + * FuriString* file_type; + * FuriString* string_value; * uint32_t uint32_value = 1; * uint16_t array_size = 4; * uint8_t* array[array_size] = {0}; - * string_init(file_type); - * string_init(string_value); + * file_type = furi_string_alloc(); + * string_value = furi_string_alloc(); * * if(!flipper_format_file_open_existing(file, EXT_PATH("flipper_format_test"))) break; * if(!flipper_format_read_header(file, file_type, &version)) break; @@ -94,7 +94,6 @@ #pragma once #include -#include #include #ifdef __cplusplus @@ -132,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 @@ -157,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. @@ -227,7 +235,7 @@ bool flipper_format_key_exist(FlipperFormat* flipper_format, const char* key); */ bool flipper_format_read_header( FlipperFormat* flipper_format, - string_t filetype, + FuriString* filetype, uint32_t* version); /** @@ -239,7 +247,7 @@ bool flipper_format_read_header( */ bool flipper_format_write_header( FlipperFormat* flipper_format, - string_t filetype, + FuriString* filetype, const uint32_t version); /** @@ -273,7 +281,7 @@ bool flipper_format_get_value_count( * @param data Value * @return True on success */ -bool flipper_format_read_string(FlipperFormat* flipper_format, const char* key, string_t data); +bool flipper_format_read_string(FlipperFormat* flipper_format, const char* key, FuriString* data); /** * Write key and string @@ -282,7 +290,7 @@ bool flipper_format_read_string(FlipperFormat* flipper_format, const char* key, * @param data Value * @return True on success */ -bool flipper_format_write_string(FlipperFormat* flipper_format, const char* key, string_t data); +bool flipper_format_write_string(FlipperFormat* flipper_format, const char* key, FuriString* data); /** * Write key and string. Plain C string version. @@ -470,7 +478,7 @@ bool flipper_format_write_hex( * @param data Comment text * @return True on success */ -bool flipper_format_write_comment(FlipperFormat* flipper_format, string_t data); +bool flipper_format_write_comment(FlipperFormat* flipper_format, FuriString* data); /** * Write comment. Plain C string version. @@ -495,7 +503,7 @@ bool flipper_format_delete_key(FlipperFormat* flipper_format, const char* key); * @param data Value * @return True on success */ -bool flipper_format_update_string(FlipperFormat* flipper_format, const char* key, string_t data); +bool flipper_format_update_string(FlipperFormat* flipper_format, const char* key, FuriString* data); /** * Updates the value of the first matching key to a string value. Plain C version. Sets the RW pointer to a position at the end of inserted data. @@ -585,7 +593,7 @@ bool flipper_format_update_hex( bool flipper_format_insert_or_update_string( FlipperFormat* flipper_format, const char* key, - string_t data); + FuriString* data); /** * Updates the value of the first matching key to a string value, or adds the key and value if the key did not exist. diff --git a/lib/flipper_format/flipper_format_stream.c b/lib/flipper_format/flipper_format_stream.c index 81189b69b87..405b819a760 100644 --- a/lib/flipper_format/flipper_format_stream.c +++ b/lib/flipper_format/flipper_format_stream.c @@ -4,6 +4,10 @@ #include "flipper_format_stream.h" #include "flipper_format_stream_i.h" +static inline bool flipper_format_stream_is_space(char c) { + return c == ' ' || c == '\t' || c == flipper_format_eolr; +} + static bool flipper_format_stream_write(Stream* stream, const void* data, size_t data_size) { size_t bytes_written = stream_write(stream, data, data_size); return bytes_written == data_size; @@ -26,8 +30,8 @@ bool flipper_format_stream_write_eol(Stream* stream) { return flipper_format_stream_write(stream, &flipper_format_eoln, 1); } -static bool flipper_format_stream_read_valid_key(Stream* stream, string_t key) { - string_reset(key); +static bool flipper_format_stream_read_valid_key(Stream* stream, FuriString* key) { + furi_string_reset(key); const size_t buffer_size = 32; uint8_t buffer[buffer_size]; @@ -44,7 +48,7 @@ static bool flipper_format_stream_read_valid_key(Stream* stream, string_t key) { uint8_t data = buffer[i]; if(data == flipper_format_eoln) { // EOL found, clean data, start accumulating data and set the new_line flag - string_reset(key); + furi_string_reset(key); accumulate = true; new_line = true; } else if(data == flipper_format_eolr) { @@ -60,7 +64,7 @@ static bool flipper_format_stream_read_valid_key(Stream* stream, string_t key) { // this can only be if we have previously found some kind of key, so // clear the data, set the flag that we no longer want to accumulate data // and reset the new_line flag - string_reset(key); + furi_string_reset(key); accumulate = false; new_line = false; } else { @@ -82,7 +86,7 @@ static bool flipper_format_stream_read_valid_key(Stream* stream, string_t key) { new_line = false; if(accumulate) { // and accumulate data if we want - string_push_back(key, data); + furi_string_push_back(key, data); } } } @@ -95,13 +99,13 @@ static bool flipper_format_stream_read_valid_key(Stream* stream, string_t key) { bool flipper_format_stream_seek_to_key(Stream* stream, const char* key, bool strict_mode) { bool found = false; - string_t read_key; + FuriString* read_key; - string_init(read_key); + read_key = furi_string_alloc(); while(!stream_eof(stream)) { if(flipper_format_stream_read_valid_key(stream, read_key)) { - if(string_cmp_str(read_key, key) == 0) { + if(furi_string_cmp_str(read_key, key) == 0) { if(!stream_seek(stream, 2, StreamOffsetFromCurrent)) break; found = true; @@ -112,61 +116,70 @@ bool flipper_format_stream_seek_to_key(Stream* stream, const char* key, bool str } } } - string_clear(read_key); + furi_string_free(read_key); return found; } -static bool flipper_format_stream_read_value(Stream* stream, string_t value, bool* last) { - string_reset(value); +static bool flipper_format_stream_read_value(Stream* stream, FuriString* value, bool* last) { + enum { LeadingSpace, ReadValue, TrailingSpace } state = LeadingSpace; const size_t buffer_size = 32; uint8_t buffer[buffer_size]; bool result = false; bool error = false; + furi_string_reset(value); + while(true) { size_t was_read = stream_read(stream, buffer, buffer_size); if(was_read == 0) { - // check EOF - if(stream_eof(stream) && string_size(value) > 0) { + if(state != LeadingSpace && stream_eof(stream)) { result = true; *last = true; - break; + } else { + error = true; } } for(uint16_t i = 0; i < was_read; i++) { - uint8_t data = buffer[i]; - if(data == flipper_format_eoln) { - if(string_size(value) > 0) { - if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) { - error = true; - break; - } + const uint8_t data = buffer[i]; - result = true; - *last = true; + if(state == LeadingSpace) { + if(flipper_format_stream_is_space(data)) { + continue; + } else if(data == flipper_format_eoln) { + stream_seek(stream, i - was_read, StreamOffsetFromCurrent); + error = true; break; } else { - error = true; + state = ReadValue; + furi_string_push_back(value, data); } - } else if(data == ' ') { - if(string_size(value) > 0) { + } else if(state == ReadValue) { + if(flipper_format_stream_is_space(data)) { + state = TrailingSpace; + } else if(data == flipper_format_eoln) { if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) { error = true; - break; + } else { + result = true; + *last = true; } - - result = true; - *last = false; break; + } else { + furi_string_push_back(value, data); } - - } else if(data == flipper_format_eolr) { - // Ignore - } else { - string_push_back(value, data); + } else if(state == TrailingSpace) { + if(flipper_format_stream_is_space(data)) { + continue; + } else if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) { + error = true; + } else { + *last = (data == flipper_format_eoln); + result = true; + } + break; } } @@ -176,8 +189,8 @@ static bool flipper_format_stream_read_value(Stream* stream, string_t value, boo return result; } -static bool flipper_format_stream_read_line(Stream* stream, string_t str_result) { - string_reset(str_result); +static bool flipper_format_stream_read_line(Stream* stream, FuriString* str_result) { + furi_string_reset(str_result); const size_t buffer_size = 32; uint8_t buffer[buffer_size]; @@ -201,7 +214,7 @@ static bool flipper_format_stream_read_line(Stream* stream, string_t str_result) } else if(data == flipper_format_eolr) { // Ignore } else { - string_push_back(str_result, data); + furi_string_push_back(str_result, data); } } @@ -210,7 +223,7 @@ static bool flipper_format_stream_read_line(Stream* stream, string_t str_result) } } while(true); - return string_size(str_result) != 0; + return furi_string_size(str_result) != 0; } static bool flipper_format_stream_seek_to_next_line(Stream* stream) { @@ -254,8 +267,8 @@ bool flipper_format_stream_write_value_line(Stream* stream, FlipperStreamWriteDa if(write_data->type == FlipperStreamValueIgnore) { result = true; } else { - string_t value; - string_init(value); + FuriString* value; + value = furi_string_alloc(); do { if(!flipper_format_stream_write_key(stream, write_data->key)) break; @@ -267,45 +280,45 @@ bool flipper_format_stream_write_value_line(Stream* stream, FlipperStreamWriteDa switch(write_data->type) { case FlipperStreamValueStr: { const char* data = write_data->data; - string_printf(value, "%s", data); + furi_string_printf(value, "%s", data); }; break; case FlipperStreamValueHex: { const uint8_t* data = write_data->data; - string_printf(value, "%02X", data[i]); + furi_string_printf(value, "%02X", data[i]); }; break; #ifndef FLIPPER_STREAM_LITE case FlipperStreamValueFloat: { const float* data = write_data->data; - string_printf(value, "%f", (double)data[i]); + furi_string_printf(value, "%f", (double)data[i]); }; break; #endif case FlipperStreamValueInt32: { const int32_t* data = write_data->data; - string_printf(value, "%" PRIi32, data[i]); + furi_string_printf(value, "%" PRIi32, data[i]); }; break; case FlipperStreamValueUint32: { const uint32_t* data = write_data->data; - string_printf(value, "%" PRId32, data[i]); + furi_string_printf(value, "%" PRIu32, data[i]); }; break; case FlipperStreamValueHexUint64: { const uint64_t* data = write_data->data; - string_printf( + furi_string_printf( value, "%08lX%08lX", (uint32_t)(data[i] >> 32), (uint32_t)data[i]); }; break; case FlipperStreamValueBool: { const bool* data = write_data->data; - string_printf(value, data[i] ? "true" : "false"); + furi_string_printf(value, data[i] ? "true" : "false"); }; break; default: furi_crash("Unknown FF type"); } - if((size_t)(i + 1) < write_data->data_size) { - string_cat(value, " "); + if(((size_t)i + 1) < write_data->data_size) { + furi_string_cat(value, " "); } if(!flipper_format_stream_write( - stream, string_get_cstr(value), string_size(value))) { + stream, furi_string_get_cstr(value), furi_string_size(value))) { cycle_error = true; break; } @@ -316,7 +329,7 @@ bool flipper_format_stream_write_value_line(Stream* stream, FlipperStreamWriteDa result = true; } while(false); - string_clear(value); + furi_string_free(value); } return result; @@ -335,15 +348,15 @@ bool flipper_format_stream_read_value_line( if(!flipper_format_stream_seek_to_key(stream, key, strict_mode)) break; if(type == FlipperStreamValueStr) { - string_ptr data = (string_ptr)_data; + FuriString* data = (FuriString*)_data; if(flipper_format_stream_read_line(stream, data)) { result = true; break; } } else { result = true; - string_t value; - string_init(value); + FuriString* value; + value = furi_string_alloc(); for(size_t i = 0; i < data_size; i++) { bool last = false; @@ -354,11 +367,11 @@ bool flipper_format_stream_read_value_line( switch(type) { case FlipperStreamValueHex: { uint8_t* data = _data; - if(string_size(value) >= 2) { + if(furi_string_size(value) >= 2) { // sscanf "%02X" does not work here - if(hex_chars_to_uint8( - string_get_char(value, 0), - string_get_char(value, 1), + if(hex_char_to_uint8( + furi_string_get_char(value, 0), + furi_string_get_char(value, 1), &data[i])) { scan_values = 1; } @@ -368,9 +381,9 @@ bool flipper_format_stream_read_value_line( case FlipperStreamValueFloat: { float* data = _data; // newlib-nano does not have sscanf for floats - // scan_values = sscanf(string_get_cstr(value), "%f", &data[i]); + // scan_values = sscanf(furi_string_get_cstr(value), "%f", &data[i]); char* end_char; - data[i] = strtof(string_get_cstr(value), &end_char); + data[i] = strtof(furi_string_get_cstr(value), &end_char); if(*end_char == 0) { // most likely ok scan_values = 1; @@ -379,23 +392,23 @@ bool flipper_format_stream_read_value_line( #endif case FlipperStreamValueInt32: { int32_t* data = _data; - scan_values = sscanf(string_get_cstr(value), "%" PRIi32, &data[i]); + scan_values = sscanf(furi_string_get_cstr(value), "%" PRIi32, &data[i]); }; break; case FlipperStreamValueUint32: { uint32_t* data = _data; - scan_values = sscanf(string_get_cstr(value), "%" PRId32, &data[i]); + scan_values = sscanf(furi_string_get_cstr(value), "%" PRIu32, &data[i]); }; break; case FlipperStreamValueHexUint64: { uint64_t* data = _data; - if(string_size(value) >= 16) { - if(hex_chars_to_uint64(string_get_cstr(value), &data[i])) { + if(furi_string_size(value) >= 16) { + if(hex_chars_to_uint64(furi_string_get_cstr(value), &data[i])) { scan_values = 1; } } }; break; case FlipperStreamValueBool: { bool* data = _data; - data[i] = !string_cmpi_str(value, "true"); + data[i] = !furi_string_cmpi(value, "true"); scan_values = 1; }; break; default: @@ -416,7 +429,7 @@ bool flipper_format_stream_read_value_line( } } - string_clear(value); + furi_string_free(value); } } while(false); @@ -431,8 +444,8 @@ bool flipper_format_stream_get_value_count( bool result = false; bool last = false; - string_t value; - string_init(value); + FuriString* value; + value = furi_string_alloc(); uint32_t position = stream_tell(stream); do { @@ -456,7 +469,7 @@ bool flipper_format_stream_get_value_count( result = false; } - string_clear(value); + furi_string_free(value); return result; } diff --git a/lib/flipper_format/flipper_format_stream.h b/lib/flipper_format/flipper_format_stream.h index 75eaef20e93..88b096b2233 100644 --- a/lib/flipper_format/flipper_format_stream.h +++ b/lib/flipper_format/flipper_format_stream.h @@ -2,7 +2,6 @@ #include #include #include -#include #ifdef __cplusplus extern "C" { diff --git a/lib/fnv1a-hash/fnv1a-hash.c b/lib/fnv1a-hash/fnv1a-hash.c deleted file mode 100644 index 69c675f310a..00000000000 --- a/lib/fnv1a-hash/fnv1a-hash.c +++ /dev/null @@ -1,10 +0,0 @@ -#include "fnv1a-hash.h" - -// FNV-1a hash, 32-bit -uint32_t fnv1a_buffer_hash(const uint8_t* buffer, uint32_t length, uint32_t hash) -{ - for (uint32_t i = 0; i < length; i++) { - hash = (hash ^ buffer[i]) * 16777619ULL; - } - return hash; -} diff --git a/lib/fnv1a-hash/fnv1a-hash.h b/lib/fnv1a-hash/fnv1a-hash.h deleted file mode 100644 index 3218cc27c75..00000000000 --- a/lib/fnv1a-hash/fnv1a-hash.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define FNV_1A_INIT 2166136261UL - -// FNV-1a hash, 32-bit -uint32_t fnv1a_buffer_hash(const uint8_t* buffer, uint32_t length, uint32_t hash); - -#ifdef __cplusplus -} -#endif - -#ifdef __cplusplus -// constexpr FNV-1a hash for strings, 32-bit -inline constexpr uint32_t fnv1a_string_hash(const char* str) { - uint32_t hash = FNV_1A_INIT; - - while(*str) { - hash = (hash ^ *str) * 16777619ULL; - str += 1; - } - return hash; -} -#else -// FNV-1a hash for strings, 32-bit -inline uint32_t fnv1a_string_hash(const char* str) { - uint32_t hash = FNV_1A_INIT; - - while(*str) { - hash = (hash ^ *str) * 16777619ULL; - str += 1; - } - return hash; -} -#endif diff --git a/lib/freertos.scons b/lib/freertos.scons index 1c5a5bf54fb..cb0006e560f 100644 --- a/lib/freertos.scons +++ b/lib/freertos.scons @@ -7,9 +7,6 @@ env.Append( "#/lib/FreeRTOS-Kernel/portable/GCC/ARM_CM4F", "#/lib/FreeRTOS-glue", ], - CPPDEFINES=[ - "HAVE_FREERTOS", - ], ) diff --git a/lib/heatshrink b/lib/heatshrink new file mode 160000 index 00000000000..7398ccc9165 --- /dev/null +++ b/lib/heatshrink @@ -0,0 +1 @@ +Subproject commit 7398ccc91652a33483245200cfa1a83b073bc206 diff --git a/lib/heatshrink.scons b/lib/heatshrink.scons new file mode 100644 index 00000000000..241b5a34e36 --- /dev/null +++ b/lib/heatshrink.scons @@ -0,0 +1,23 @@ +from fbt.util import GLOB_FILE_EXCLUSION + +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/heatshrink", + ], +) + + +libenv = env.Clone(FW_LIB_NAME="heatshrink") +libenv.ApplyLibFlags() + +sources = Glob( + "heatshrink/heatshrink_*.c*", + exclude=GLOB_FILE_EXCLUSION, + source=True, +) + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/heatshrink/heatshrink_common.h b/lib/heatshrink/heatshrink_common.h deleted file mode 100644 index 243f447029d..00000000000 --- a/lib/heatshrink/heatshrink_common.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef HEATSHRINK_H -#define HEATSHRINK_H - -#define HEATSHRINK_AUTHOR "Scott Vokes " -#define HEATSHRINK_URL "https://github.com/atomicobject/heatshrink" - -/* Version 0.4.1 */ -#define HEATSHRINK_VERSION_MAJOR 0 -#define HEATSHRINK_VERSION_MINOR 4 -#define HEATSHRINK_VERSION_PATCH 1 - -#define HEATSHRINK_MIN_WINDOW_BITS 4 -#define HEATSHRINK_MAX_WINDOW_BITS 15 - -#define HEATSHRINK_MIN_LOOKAHEAD_BITS 3 - -#define HEATSHRINK_LITERAL_MARKER 0x01 -#define HEATSHRINK_BACKREF_MARKER 0x00 - -#endif diff --git a/lib/heatshrink/heatshrink_config.h b/lib/heatshrink/heatshrink_config.h deleted file mode 100644 index 7f2373c0d47..00000000000 --- a/lib/heatshrink/heatshrink_config.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef HEATSHRINK_CONFIG_H -#define HEATSHRINK_CONFIG_H - -#include - -/* Should functionality assuming dynamic allocation be used? */ -#ifndef HEATSHRINK_DYNAMIC_ALLOC -#define HEATSHRINK_DYNAMIC_ALLOC 1 -#endif - -#if HEATSHRINK_DYNAMIC_ALLOC - /* Optional replacement of malloc/free */ - #define HEATSHRINK_MALLOC(SZ) malloc(SZ) - #define HEATSHRINK_FREE(P, SZ) free(P) -#else - /* Required parameters for static configuration */ - #define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 1024 - #define HEATSHRINK_STATIC_WINDOW_BITS 8 - #define HEATSHRINK_STATIC_LOOKAHEAD_BITS 4 -#endif - -/* Turn on logging for debugging. */ -#define HEATSHRINK_DEBUGGING_LOGS 0 - -/* Use indexing for faster compression. (This requires additional space.) */ -#define HEATSHRINK_USE_INDEX 1 - -#endif diff --git a/lib/heatshrink/heatshrink_decoder.c b/lib/heatshrink/heatshrink_decoder.c deleted file mode 100644 index 28782836757..00000000000 --- a/lib/heatshrink/heatshrink_decoder.c +++ /dev/null @@ -1,364 +0,0 @@ -#include -#include -#include "heatshrink_decoder.h" - -/* States for the polling state machine. */ -typedef enum { - HSDS_TAG_BIT, /* tag bit */ - HSDS_YIELD_LITERAL, /* ready to yield literal byte */ - HSDS_BACKREF_INDEX_MSB, /* most significant byte of index */ - HSDS_BACKREF_INDEX_LSB, /* least significant byte of index */ - HSDS_BACKREF_COUNT_MSB, /* most significant byte of count */ - HSDS_BACKREF_COUNT_LSB, /* least significant byte of count */ - HSDS_YIELD_BACKREF, /* ready to yield back-reference */ -} HSD_state; - -#if HEATSHRINK_DEBUGGING_LOGS -#include -#include -#include -#define LOG(...) fprintf(stderr, __VA_ARGS__) -#define ASSERT(X) assert(X) -static const char *state_names[] = { - "tag_bit", - "yield_literal", - "backref_index_msb", - "backref_index_lsb", - "backref_count_msb", - "backref_count_lsb", - "yield_backref", -}; -#else -#define LOG(...) /* no-op */ -#define ASSERT(X) /* no-op */ -#endif - -typedef struct { - uint8_t *buf; /* output buffer */ - size_t buf_size; /* buffer size */ - size_t *output_size; /* bytes pushed to buffer, so far */ -} output_info; - -#define NO_BITS ((uint16_t)-1) - -/* Forward references. */ -static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count); -static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte); - -#if HEATSHRINK_DYNAMIC_ALLOC -heatshrink_decoder *heatshrink_decoder_alloc(uint8_t* buffer, - uint16_t input_buffer_size, - uint8_t window_sz2, - uint8_t lookahead_sz2) { - if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) || - (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) || - (input_buffer_size == 0) || - (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) || - (lookahead_sz2 >= window_sz2)) { - return NULL; - } - size_t sz = sizeof(heatshrink_decoder); - heatshrink_decoder *hsd = HEATSHRINK_MALLOC(sz); - if (hsd == NULL) { return NULL; } - hsd->input_buffer_size = input_buffer_size; - hsd->window_sz2 = window_sz2; - hsd->lookahead_sz2 = lookahead_sz2; - hsd->buffers = buffer; - heatshrink_decoder_reset(hsd); - LOG("-- allocated decoder with buffer size of %zu (%zu + %u + %u)\n", - sz, sizeof(heatshrink_decoder), (1 << window_sz2), input_buffer_size); - return hsd; -} - -void heatshrink_decoder_free(heatshrink_decoder *hsd) { - size_t sz = sizeof(heatshrink_decoder); - HEATSHRINK_FREE(hsd, sz); - (void)sz; /* may not be used by free */ -} -#endif - -void heatshrink_decoder_reset(heatshrink_decoder *hsd) { - hsd->state = HSDS_TAG_BIT; - hsd->input_size = 0; - hsd->input_index = 0; - hsd->bit_index = 0x00; - hsd->current_byte = 0x00; - hsd->output_count = 0; - hsd->output_index = 0; - hsd->head_index = 0; -} - -/* Copy SIZE bytes into the decoder's input buffer, if it will fit. */ -HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, - uint8_t *in_buf, size_t size, size_t *input_size) { - if ((hsd == NULL) || (in_buf == NULL) || (input_size == NULL)) { - return HSDR_SINK_ERROR_NULL; - } - - size_t rem = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd) - hsd->input_size; - if (rem == 0) { - *input_size = 0; - return HSDR_SINK_FULL; - } - - size = rem < size ? rem : size; - LOG("-- sinking %zd bytes\n", size); - /* copy into input buffer (at head of buffers) */ - memcpy(&hsd->buffers[hsd->input_size], in_buf, size); - hsd->input_size += size; - *input_size = size; - return HSDR_SINK_OK; -} - - -/***************** - * Decompression * - *****************/ - -#define BACKREF_COUNT_BITS(HSD) (HEATSHRINK_DECODER_LOOKAHEAD_BITS(HSD)) -#define BACKREF_INDEX_BITS(HSD) (HEATSHRINK_DECODER_WINDOW_BITS(HSD)) - -// States -static HSD_state st_tag_bit(heatshrink_decoder *hsd); -static HSD_state st_yield_literal(heatshrink_decoder *hsd, - output_info *oi); -static HSD_state st_backref_index_msb(heatshrink_decoder *hsd); -static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd); -static HSD_state st_backref_count_msb(heatshrink_decoder *hsd); -static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd); -static HSD_state st_yield_backref(heatshrink_decoder *hsd, - output_info *oi); - -HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size) { - if ((hsd == NULL) || (out_buf == NULL) || (output_size == NULL)) { - return HSDR_POLL_ERROR_NULL; - } - *output_size = 0; - - output_info oi; - oi.buf = out_buf; - oi.buf_size = out_buf_size; - oi.output_size = output_size; - - while (1) { - LOG("-- poll, state is %d (%s), input_size %d\n", - hsd->state, state_names[hsd->state], hsd->input_size); - uint8_t in_state = hsd->state; - switch (in_state) { - case HSDS_TAG_BIT: - hsd->state = st_tag_bit(hsd); - break; - case HSDS_YIELD_LITERAL: - hsd->state = st_yield_literal(hsd, &oi); - break; - case HSDS_BACKREF_INDEX_MSB: - hsd->state = st_backref_index_msb(hsd); - break; - case HSDS_BACKREF_INDEX_LSB: - hsd->state = st_backref_index_lsb(hsd); - break; - case HSDS_BACKREF_COUNT_MSB: - hsd->state = st_backref_count_msb(hsd); - break; - case HSDS_BACKREF_COUNT_LSB: - hsd->state = st_backref_count_lsb(hsd); - break; - case HSDS_YIELD_BACKREF: - hsd->state = st_yield_backref(hsd, &oi); - break; - default: - return HSDR_POLL_ERROR_UNKNOWN; - } - - /* If the current state cannot advance, check if input or output - * buffer are exhausted. */ - if (hsd->state == in_state) { - if (*output_size == out_buf_size) { return HSDR_POLL_MORE; } - return HSDR_POLL_EMPTY; - } - } -} - -static HSD_state st_tag_bit(heatshrink_decoder *hsd) { - uint32_t bits = get_bits(hsd, 1); // get tag bit - if (bits == NO_BITS) { - return HSDS_TAG_BIT; - } else if (bits) { - return HSDS_YIELD_LITERAL; - } else if (HEATSHRINK_DECODER_WINDOW_BITS(hsd) > 8) { - return HSDS_BACKREF_INDEX_MSB; - } else { - hsd->output_index = 0; - return HSDS_BACKREF_INDEX_LSB; - } -} - -static HSD_state st_yield_literal(heatshrink_decoder *hsd, - output_info *oi) { - /* Emit a repeated section from the window buffer, and add it (again) - * to the window buffer. (Note that the repetition can include - * itself.)*/ - if (*oi->output_size < oi->buf_size) { - uint16_t byte = get_bits(hsd, 8); - if (byte == NO_BITS) { return HSDS_YIELD_LITERAL; } /* out of input */ - uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)]; - uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1; - uint8_t c = byte & 0xFF; - LOG("-- emitting literal byte 0x%02x ('%c')\n", c, isprint(c) ? c : '.'); - buf[hsd->head_index++ & mask] = c; - push_byte(hsd, oi, c); - return HSDS_TAG_BIT; - } else { - return HSDS_YIELD_LITERAL; - } -} - -static HSD_state st_backref_index_msb(heatshrink_decoder *hsd) { - uint8_t bit_ct = BACKREF_INDEX_BITS(hsd); - ASSERT(bit_ct > 8); - uint16_t bits = get_bits(hsd, bit_ct - 8); - LOG("-- backref index (msb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_MSB; } - hsd->output_index = bits << 8; - return HSDS_BACKREF_INDEX_LSB; -} - -static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd) { - uint8_t bit_ct = BACKREF_INDEX_BITS(hsd); - uint16_t bits = get_bits(hsd, bit_ct < 8 ? bit_ct : 8); - LOG("-- backref index (lsb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_LSB; } - hsd->output_index |= bits; - hsd->output_index++; - uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); - hsd->output_count = 0; - return (br_bit_ct > 8) ? HSDS_BACKREF_COUNT_MSB : HSDS_BACKREF_COUNT_LSB; -} - -static HSD_state st_backref_count_msb(heatshrink_decoder *hsd) { - uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); - ASSERT(br_bit_ct > 8); - uint16_t bits = get_bits(hsd, br_bit_ct - 8); - LOG("-- backref count (msb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_MSB; } - hsd->output_count = bits << 8; - return HSDS_BACKREF_COUNT_LSB; -} - -static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd) { - uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); - uint16_t bits = get_bits(hsd, br_bit_ct < 8 ? br_bit_ct : 8); - LOG("-- backref count (lsb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_LSB; } - hsd->output_count |= bits; - hsd->output_count++; - return HSDS_YIELD_BACKREF; -} - -static HSD_state st_yield_backref(heatshrink_decoder *hsd, - output_info *oi) { - size_t count = oi->buf_size - *oi->output_size; - if (count > 0) { - size_t i = 0; - if (hsd->output_count < count) count = hsd->output_count; - uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)]; - uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1; - uint16_t neg_offset = hsd->output_index; - LOG("-- emitting %zu bytes from -%u bytes back\n", count, neg_offset); - ASSERT(neg_offset <= mask + 1); - ASSERT(count <= (size_t)(1 << BACKREF_COUNT_BITS(hsd))); - - for (i=0; ihead_index - neg_offset) & mask]; - push_byte(hsd, oi, c); - buf[hsd->head_index & mask] = c; - hsd->head_index++; - LOG(" -- ++ 0x%02x\n", c); - } - hsd->output_count -= count; - if (hsd->output_count == 0) { return HSDS_TAG_BIT; } - } - return HSDS_YIELD_BACKREF; -} - -/* Get the next COUNT bits from the input buffer, saving incremental progress. - * Returns NO_BITS on end of input, or if more than 15 bits are requested. */ -static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count) { - uint16_t accumulator = 0; - int i = 0; - if (count > 15) { return NO_BITS; } - LOG("-- popping %u bit(s)\n", count); - - /* If we aren't able to get COUNT bits, suspend immediately, because we - * don't track how many bits of COUNT we've accumulated before suspend. */ - if (hsd->input_size == 0) { - if (hsd->bit_index < (1 << (count - 1))) { return NO_BITS; } - } - - for (i = 0; i < count; i++) { - if (hsd->bit_index == 0x00) { - if (hsd->input_size == 0) { - LOG(" -- out of bits, suspending w/ accumulator of %u (0x%02x)\n", - accumulator, accumulator); - return NO_BITS; - } - hsd->current_byte = hsd->buffers[hsd->input_index++]; - LOG(" -- pulled byte 0x%02x\n", hsd->current_byte); - if (hsd->input_index == hsd->input_size) { - hsd->input_index = 0; /* input is exhausted */ - hsd->input_size = 0; - } - hsd->bit_index = 0x80; - } - accumulator <<= 1; - if (hsd->current_byte & hsd->bit_index) { - accumulator |= 0x01; - if (0) { - LOG(" -- got 1, accumulator 0x%04x, bit_index 0x%02x\n", - accumulator, hsd->bit_index); - } - } else { - if (0) { - LOG(" -- got 0, accumulator 0x%04x, bit_index 0x%02x\n", - accumulator, hsd->bit_index); - } - } - hsd->bit_index >>= 1; - } - - if (count > 1) { LOG(" -- accumulated %08x\n", accumulator); } - return accumulator; -} - -HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd) { - if (hsd == NULL) { return HSDR_FINISH_ERROR_NULL; } - switch (hsd->state) { - case HSDS_TAG_BIT: - return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; - - /* If we want to finish with no input, but are in these states, it's - * because the 0-bit padding to the last byte looks like a backref - * marker bit followed by all 0s for index and count bits. */ - case HSDS_BACKREF_INDEX_LSB: - case HSDS_BACKREF_INDEX_MSB: - case HSDS_BACKREF_COUNT_LSB: - case HSDS_BACKREF_COUNT_MSB: - return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; - - /* If the output stream is padded with 0xFFs (possibly due to being in - * flash memory), also explicitly check the input size rather than - * uselessly returning MORE but yielding 0 bytes when polling. */ - case HSDS_YIELD_LITERAL: - return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; - - default: - return HSDR_FINISH_MORE; - } -} - -static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte) { - LOG(" -- pushing byte: 0x%02x ('%c')\n", byte, isprint(byte) ? byte : '.'); - oi->buf[(*oi->output_size)++] = byte; - (void)hsd; -} diff --git a/lib/heatshrink/heatshrink_decoder.h b/lib/heatshrink/heatshrink_decoder.h deleted file mode 100644 index 687b0806b4b..00000000000 --- a/lib/heatshrink/heatshrink_decoder.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef HEATSHRINK_DECODER_H -#define HEATSHRINK_DECODER_H - -#include -#include -#include "heatshrink_common.h" -#include "heatshrink_config.h" - -typedef enum { - HSDR_SINK_OK, /* data sunk, ready to poll */ - HSDR_SINK_FULL, /* out of space in internal buffer */ - HSDR_SINK_ERROR_NULL=-1, /* NULL argument */ -} HSD_sink_res; - -typedef enum { - HSDR_POLL_EMPTY, /* input exhausted */ - HSDR_POLL_MORE, /* more data remaining, call again w/ fresh output buffer */ - HSDR_POLL_ERROR_NULL=-1, /* NULL arguments */ - HSDR_POLL_ERROR_UNKNOWN=-2, -} HSD_poll_res; - -typedef enum { - HSDR_FINISH_DONE, /* output is done */ - HSDR_FINISH_MORE, /* more output remains */ - HSDR_FINISH_ERROR_NULL=-1, /* NULL arguments */ -} HSD_finish_res; - -#if HEATSHRINK_DYNAMIC_ALLOC -#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(BUF) \ - ((BUF)->input_buffer_size) -#define HEATSHRINK_DECODER_WINDOW_BITS(BUF) \ - ((BUF)->window_sz2) -#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \ - ((BUF)->lookahead_sz2) -#else -#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_) \ - HEATSHRINK_STATIC_INPUT_BUFFER_SIZE -#define HEATSHRINK_DECODER_WINDOW_BITS(_) \ - (HEATSHRINK_STATIC_WINDOW_BITS) -#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \ - (HEATSHRINK_STATIC_LOOKAHEAD_BITS) -#endif - -typedef struct { - uint16_t input_size; /* bytes in input buffer */ - uint16_t input_index; /* offset to next unprocessed input byte */ - uint16_t output_count; /* how many bytes to output */ - uint16_t output_index; /* index for bytes to output */ - uint16_t head_index; /* head of window buffer */ - uint8_t state; /* current state machine node */ - uint8_t current_byte; /* current byte of input */ - uint8_t bit_index; /* current bit index */ - -#if HEATSHRINK_DYNAMIC_ALLOC - /* Fields that are only used if dynamically allocated. */ - uint8_t window_sz2; /* window buffer bits */ - uint8_t lookahead_sz2; /* lookahead bits */ - uint16_t input_buffer_size; /* input buffer size */ - - /* Input buffer, then expansion window buffer */ - uint8_t* buffers; -#else - /* Input buffer, then expansion window buffer */ - uint8_t buffers[(1 << HEATSHRINK_DECODER_WINDOW_BITS(_)) - + HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_)]; -#endif -} heatshrink_decoder; - -#if HEATSHRINK_DYNAMIC_ALLOC -/* Allocate a decoder with an input buffer of INPUT_BUFFER_SIZE bytes, - * an expansion buffer size of 2^WINDOW_SZ2, and a lookahead - * size of 2^lookahead_sz2. (The window buffer and lookahead sizes - * must match the settings used when the data was compressed.) - * Returns NULL on error. */ -heatshrink_decoder *heatshrink_decoder_alloc(uint8_t* buffer, uint16_t input_buffer_size, - uint8_t expansion_buffer_sz2, uint8_t lookahead_sz2); - -/* Free a decoder. */ -void heatshrink_decoder_free(heatshrink_decoder *hsd); -#endif - -/* Reset a decoder. */ -void heatshrink_decoder_reset(heatshrink_decoder *hsd); - -/* Sink at most SIZE bytes from IN_BUF into the decoder. *INPUT_SIZE is set to - * indicate how many bytes were actually sunk (in case a buffer was filled). */ -HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, - uint8_t *in_buf, size_t size, size_t *input_size); - -/* Poll for output from the decoder, copying at most OUT_BUF_SIZE bytes into - * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ -HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size); - -/* Notify the dencoder that the input stream is finished. - * If the return value is HSDR_FINISH_MORE, there is still more output, so - * call heatshrink_decoder_poll and repeat. */ -HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd); - -#endif diff --git a/lib/heatshrink/heatshrink_encoder.c b/lib/heatshrink/heatshrink_encoder.c deleted file mode 100644 index 98f27dff872..00000000000 --- a/lib/heatshrink/heatshrink_encoder.c +++ /dev/null @@ -1,602 +0,0 @@ -#include -#include -#include -#include "heatshrink_encoder.h" - -typedef enum { - HSES_NOT_FULL, /* input buffer not full enough */ - HSES_FILLED, /* buffer is full */ - HSES_SEARCH, /* searching for patterns */ - HSES_YIELD_TAG_BIT, /* yield tag bit */ - HSES_YIELD_LITERAL, /* emit literal byte */ - HSES_YIELD_BR_INDEX, /* yielding backref index */ - HSES_YIELD_BR_LENGTH, /* yielding backref length */ - HSES_SAVE_BACKLOG, /* copying buffer to backlog */ - HSES_FLUSH_BITS, /* flush bit buffer */ - HSES_DONE, /* done */ -} HSE_state; - -#if HEATSHRINK_DEBUGGING_LOGS -#include -#include -#include -#define LOG(...) fprintf(stderr, __VA_ARGS__) -#define ASSERT(X) assert(X) -static const char *state_names[] = { - "not_full", - "filled", - "search", - "yield_tag_bit", - "yield_literal", - "yield_br_index", - "yield_br_length", - "save_backlog", - "flush_bits", - "done", -}; -#else -#define LOG(...) /* no-op */ -#define ASSERT(X) /* no-op */ -#endif - -// Encoder flags -enum { - FLAG_IS_FINISHING = 0x01, -}; - -typedef struct { - uint8_t *buf; /* output buffer */ - size_t buf_size; /* buffer size */ - size_t *output_size; /* bytes pushed to buffer, so far */ -} output_info; - -#define MATCH_NOT_FOUND ((uint16_t)-1) - -static uint16_t get_input_offset(heatshrink_encoder *hse); -static uint16_t get_input_buffer_size(heatshrink_encoder *hse); -static uint16_t get_lookahead_size(heatshrink_encoder *hse); -static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag); -static int can_take_byte(output_info *oi); -static int is_finishing(heatshrink_encoder *hse); -static void save_backlog(heatshrink_encoder *hse); - -/* Push COUNT (max 8) bits to the output buffer, which has room. */ -static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits, - output_info *oi); -static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi); -static void push_literal_byte(heatshrink_encoder *hse, output_info *oi); - -#if HEATSHRINK_DYNAMIC_ALLOC -heatshrink_encoder *heatshrink_encoder_alloc(uint8_t* buffer, uint8_t window_sz2, - uint8_t lookahead_sz2) { - if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) || - (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) || - (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) || - (lookahead_sz2 >= window_sz2)) { - return NULL; - } - - /* Note: 2 * the window size is used because the buffer needs to fit - * (1 << window_sz2) bytes for the current input, and an additional - * (1 << window_sz2) bytes for the previous buffer of input, which - * will be scanned for useful backreferences. */ - size_t buf_sz = (2 << window_sz2); - - heatshrink_encoder *hse = HEATSHRINK_MALLOC(sizeof(*hse)); - if (hse == NULL) { return NULL; } - hse->window_sz2 = window_sz2; - hse->lookahead_sz2 = lookahead_sz2; - hse->buffer = buffer; - heatshrink_encoder_reset(hse); - -#if HEATSHRINK_USE_INDEX - size_t index_sz = buf_sz*sizeof(uint16_t); - hse->search_index = HEATSHRINK_MALLOC(index_sz + sizeof(struct hs_index)); - if (hse->search_index == NULL) { - HEATSHRINK_FREE(hse, sizeof(*hse) + buf_sz); - return NULL; - } - hse->search_index->size = index_sz; -#endif - - LOG("-- allocated encoder with buffer size of %zu (%u byte input size)\n", - buf_sz, get_input_buffer_size(hse)); - return hse; -} - -void heatshrink_encoder_free(heatshrink_encoder *hse) { -#if HEATSHRINK_USE_INDEX - size_t index_sz = sizeof(struct hs_index) + hse->search_index->size; - HEATSHRINK_FREE(hse->search_index, index_sz); - (void)index_sz; -#endif - HEATSHRINK_FREE(hse, sizeof(heatshrink_encoder)); -} -#endif - -void heatshrink_encoder_reset(heatshrink_encoder *hse) { - hse->input_size = 0; - hse->state = HSES_NOT_FULL; - hse->match_scan_index = 0; - hse->flags = 0; - hse->bit_index = 0x80; - hse->current_byte = 0x00; - hse->match_length = 0; - - hse->outgoing_bits = 0x0000; - hse->outgoing_bits_count = 0; - - #ifdef LOOP_DETECT - hse->loop_detect = (uint32_t)-1; - #endif -} - -HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, - uint8_t *in_buf, size_t size, size_t *input_size) { - if ((hse == NULL) || (in_buf == NULL) || (input_size == NULL)) { - return HSER_SINK_ERROR_NULL; - } - - /* Sinking more content after saying the content is done, tsk tsk */ - if (is_finishing(hse)) { return HSER_SINK_ERROR_MISUSE; } - - /* Sinking more content before processing is done */ - if (hse->state != HSES_NOT_FULL) { return HSER_SINK_ERROR_MISUSE; } - - uint16_t write_offset = get_input_offset(hse) + hse->input_size; - uint16_t ibs = get_input_buffer_size(hse); - uint16_t rem = ibs - hse->input_size; - uint16_t cp_sz = rem < size ? rem : size; - - memcpy(&hse->buffer[write_offset], in_buf, cp_sz); - *input_size = cp_sz; - hse->input_size += cp_sz; - - LOG("-- sunk %u bytes (of %zu) into encoder at %d, input buffer now has %u\n", - cp_sz, size, write_offset, hse->input_size); - if (cp_sz == rem) { - LOG("-- internal buffer is now full\n"); - hse->state = HSES_FILLED; - } - - return HSER_SINK_OK; -} - - -/*************** - * Compression * - ***************/ - -static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start, - uint16_t end, const uint16_t maxlen, uint16_t *match_length); -static void do_indexing(heatshrink_encoder *hse); - -static HSE_state st_step_search(heatshrink_encoder *hse); -static HSE_state st_yield_tag_bit(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_yield_literal(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_yield_br_index(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_yield_br_length(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_save_backlog(heatshrink_encoder *hse); -static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse, - output_info *oi); - -HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size) { - if ((hse == NULL) || (out_buf == NULL) || (output_size == NULL)) { - return HSER_POLL_ERROR_NULL; - } - if (out_buf_size == 0) { - LOG("-- MISUSE: output buffer size is 0\n"); - return HSER_POLL_ERROR_MISUSE; - } - *output_size = 0; - - output_info oi; - oi.buf = out_buf; - oi.buf_size = out_buf_size; - oi.output_size = output_size; - - while (1) { - LOG("-- polling, state %u (%s), flags 0x%02x\n", - hse->state, state_names[hse->state], hse->flags); - - uint8_t in_state = hse->state; - switch (in_state) { - case HSES_NOT_FULL: - return HSER_POLL_EMPTY; - case HSES_FILLED: - do_indexing(hse); - hse->state = HSES_SEARCH; - break; - case HSES_SEARCH: - hse->state = st_step_search(hse); - break; - case HSES_YIELD_TAG_BIT: - hse->state = st_yield_tag_bit(hse, &oi); - break; - case HSES_YIELD_LITERAL: - hse->state = st_yield_literal(hse, &oi); - break; - case HSES_YIELD_BR_INDEX: - hse->state = st_yield_br_index(hse, &oi); - break; - case HSES_YIELD_BR_LENGTH: - hse->state = st_yield_br_length(hse, &oi); - break; - case HSES_SAVE_BACKLOG: - hse->state = st_save_backlog(hse); - break; - case HSES_FLUSH_BITS: - hse->state = st_flush_bit_buffer(hse, &oi); - /* fall through */ - case HSES_DONE: - return HSER_POLL_EMPTY; - default: - LOG("-- bad state %s\n", state_names[hse->state]); - return HSER_POLL_ERROR_MISUSE; - } - - if (hse->state == in_state) { - /* Check if output buffer is exhausted. */ - if (*output_size == out_buf_size) return HSER_POLL_MORE; - } - } -} - -HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse) { - if (hse == NULL) { return HSER_FINISH_ERROR_NULL; } - LOG("-- setting is_finishing flag\n"); - hse->flags |= FLAG_IS_FINISHING; - if (hse->state == HSES_NOT_FULL) { hse->state = HSES_FILLED; } - return hse->state == HSES_DONE ? HSER_FINISH_DONE : HSER_FINISH_MORE; -} - -static HSE_state st_step_search(heatshrink_encoder *hse) { - uint16_t window_length = get_input_buffer_size(hse); - uint16_t lookahead_sz = get_lookahead_size(hse); - uint16_t msi = hse->match_scan_index; - LOG("## step_search, scan @ +%d (%d/%d), input size %d\n", - msi, hse->input_size + msi, 2*window_length, hse->input_size); - - bool fin = is_finishing(hse); - if (msi > hse->input_size - (fin ? 1 : lookahead_sz)) { - /* Current search buffer is exhausted, copy it into the - * backlog and await more input. */ - LOG("-- end of search @ %d\n", msi); - return fin ? HSES_FLUSH_BITS : HSES_SAVE_BACKLOG; - } - - uint16_t input_offset = get_input_offset(hse); - uint16_t end = input_offset + msi; - uint16_t start = end - window_length; - - uint16_t max_possible = lookahead_sz; - if (hse->input_size - msi < lookahead_sz) { - max_possible = hse->input_size - msi; - } - - uint16_t match_length = 0; - uint16_t match_pos = find_longest_match(hse, - start, end, max_possible, &match_length); - - if (match_pos == MATCH_NOT_FOUND) { - LOG("ss Match not found\n"); - hse->match_scan_index++; - hse->match_length = 0; - return HSES_YIELD_TAG_BIT; - } else { - LOG("ss Found match of %d bytes at %d\n", match_length, match_pos); - hse->match_pos = match_pos; - hse->match_length = match_length; - ASSERT(match_pos <= 1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse) /*window_length*/); - - return HSES_YIELD_TAG_BIT; - } -} - -static HSE_state st_yield_tag_bit(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - if (hse->match_length == 0) { - add_tag_bit(hse, oi, HEATSHRINK_LITERAL_MARKER); - return HSES_YIELD_LITERAL; - } else { - add_tag_bit(hse, oi, HEATSHRINK_BACKREF_MARKER); - hse->outgoing_bits = hse->match_pos - 1; - hse->outgoing_bits_count = HEATSHRINK_ENCODER_WINDOW_BITS(hse); - return HSES_YIELD_BR_INDEX; - } - } else { - return HSES_YIELD_TAG_BIT; /* output is full, continue */ - } -} - -static HSE_state st_yield_literal(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - push_literal_byte(hse, oi); - return HSES_SEARCH; - } else { - return HSES_YIELD_LITERAL; - } -} - -static HSE_state st_yield_br_index(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - LOG("-- yielding backref index %u\n", hse->match_pos); - if (push_outgoing_bits(hse, oi) > 0) { - return HSES_YIELD_BR_INDEX; /* continue */ - } else { - hse->outgoing_bits = hse->match_length - 1; - hse->outgoing_bits_count = HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse); - return HSES_YIELD_BR_LENGTH; /* done */ - } - } else { - return HSES_YIELD_BR_INDEX; /* continue */ - } -} - -static HSE_state st_yield_br_length(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - LOG("-- yielding backref length %u\n", hse->match_length); - if (push_outgoing_bits(hse, oi) > 0) { - return HSES_YIELD_BR_LENGTH; - } else { - hse->match_scan_index += hse->match_length; - hse->match_length = 0; - return HSES_SEARCH; - } - } else { - return HSES_YIELD_BR_LENGTH; - } -} - -static HSE_state st_save_backlog(heatshrink_encoder *hse) { - LOG("-- saving backlog\n"); - save_backlog(hse); - return HSES_NOT_FULL; -} - -static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse, - output_info *oi) { - if (hse->bit_index == 0x80) { - LOG("-- done!\n"); - return HSES_DONE; - } else if (can_take_byte(oi)) { - LOG("-- flushing remaining byte (bit_index == 0x%02x)\n", hse->bit_index); - oi->buf[(*oi->output_size)++] = hse->current_byte; - LOG("-- done!\n"); - return HSES_DONE; - } else { - return HSES_FLUSH_BITS; - } -} - -static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag) { - LOG("-- adding tag bit: %d\n", tag); - push_bits(hse, 1, tag, oi); -} - -static uint16_t get_input_offset(heatshrink_encoder *hse) { - return get_input_buffer_size(hse); -} - -static uint16_t get_input_buffer_size(heatshrink_encoder *hse) { - return (1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); - (void)hse; -} - -static uint16_t get_lookahead_size(heatshrink_encoder *hse) { - return (1 << HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse)); - (void)hse; -} - -static void do_indexing(heatshrink_encoder *hse) { -#if HEATSHRINK_USE_INDEX - /* Build an index array I that contains flattened linked lists - * for the previous instances of every byte in the buffer. - * - * For example, if buf[200] == 'x', then index[200] will either - * be an offset i such that buf[i] == 'x', or a negative offset - * to indicate end-of-list. This significantly speeds up matching, - * while only using sizeof(uint16_t)*sizeof(buffer) bytes of RAM. - * - * Future optimization options: - * 1. Since any negative value represents end-of-list, the other - * 15 bits could be used to improve the index dynamically. - * - * 2. Likewise, the last lookahead_sz bytes of the index will - * not be usable, so temporary data could be stored there to - * dynamically improve the index. - * */ - struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); - int16_t last[256]; - memset(last, 0xFF, sizeof(last)); - - uint8_t * const data = hse->buffer; - int16_t * const index = hsi->index; - - const uint16_t input_offset = get_input_offset(hse); - const uint16_t end = input_offset + hse->input_size; - - for (uint16_t i=0; iflags & FLAG_IS_FINISHING; -} - -static int can_take_byte(output_info *oi) { - return *oi->output_size < oi->buf_size; -} - -/* Return the longest match for the bytes at buf[end:end+maxlen] between - * buf[start] and buf[end-1]. If no match is found, return -1. */ -static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start, - uint16_t end, const uint16_t maxlen, uint16_t *match_length) { - LOG("-- scanning for match of buf[%u:%u] between buf[%u:%u] (max %u bytes)\n", - end, end + maxlen, start, end + maxlen - 1, maxlen); - uint8_t *buf = hse->buffer; - - uint16_t match_maxlen = 0; - uint16_t match_index = MATCH_NOT_FOUND; - - uint16_t len = 0; - uint8_t * const needlepoint = &buf[end]; -#if HEATSHRINK_USE_INDEX - struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); - int16_t pos = hsi->index[end]; - - while (pos - (int16_t)start >= 0) { - uint8_t * const pospoint = &buf[pos]; - len = 0; - - /* Only check matches that will potentially beat the current maxlen. - * This is redundant with the index if match_maxlen is 0, but the - * added branch overhead to check if it == 0 seems to be worse. */ - if (pospoint[match_maxlen] != needlepoint[match_maxlen]) { - pos = hsi->index[pos]; - continue; - } - - for (len = 1; len < maxlen; len++) { - if (pospoint[len] != needlepoint[len]) break; - } - - if (len > match_maxlen) { - match_maxlen = len; - match_index = pos; - if (len == maxlen) { break; } /* won't find better */ - } - pos = hsi->index[pos]; - } -#else - for (int16_t pos=end - 1; pos - (int16_t)start >= 0; pos--) { - uint8_t * const pospoint = &buf[pos]; - if ((pospoint[match_maxlen] == needlepoint[match_maxlen]) - && (*pospoint == *needlepoint)) { - for (len=1; len cmp buf[%d] == 0x%02x against %02x (start %u)\n", - pos + len, pospoint[len], needlepoint[len], start); - } - if (pospoint[len] != needlepoint[len]) { break; } - } - if (len > match_maxlen) { - match_maxlen = len; - match_index = pos; - if (len == maxlen) { break; } /* don't keep searching */ - } - } - } -#endif - - const size_t break_even_point = - (1 + HEATSHRINK_ENCODER_WINDOW_BITS(hse) + - HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse)); - - /* Instead of comparing break_even_point against 8*match_maxlen, - * compare match_maxlen against break_even_point/8 to avoid - * overflow. Since MIN_WINDOW_BITS and MIN_LOOKAHEAD_BITS are 4 and - * 3, respectively, break_even_point/8 will always be at least 1. */ - if (match_maxlen > (break_even_point / 8)) { - LOG("-- best match: %u bytes at -%u\n", - match_maxlen, end - match_index); - *match_length = match_maxlen; - return end - match_index; - } - LOG("-- none found\n"); - return MATCH_NOT_FOUND; -} - -static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi) { - uint8_t count = 0; - uint8_t bits = 0; - if (hse->outgoing_bits_count > 8) { - count = 8; - bits = hse->outgoing_bits >> (hse->outgoing_bits_count - 8); - } else { - count = hse->outgoing_bits_count; - bits = hse->outgoing_bits; - } - - if (count > 0) { - LOG("-- pushing %d outgoing bits: 0x%02x\n", count, bits); - push_bits(hse, count, bits, oi); - hse->outgoing_bits_count -= count; - } - return count; -} - -/* Push COUNT (max 8) bits to the output buffer, which has room. - * Bytes are set from the lowest bits, up. */ -static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits, - output_info *oi) { - ASSERT(count <= 8); - LOG("++ push_bits: %d bits, input of 0x%02x\n", count, bits); - - /* If adding a whole byte and at the start of a new output byte, - * just push it through whole and skip the bit IO loop. */ - if (count == 8 && hse->bit_index == 0x80) { - oi->buf[(*oi->output_size)++] = bits; - } else { - for (int i=count - 1; i>=0; i--) { - bool bit = bits & (1 << i); - if (bit) { hse->current_byte |= hse->bit_index; } - if (0) { - LOG(" -- setting bit %d at bit index 0x%02x, byte => 0x%02x\n", - bit ? 1 : 0, hse->bit_index, hse->current_byte); - } - hse->bit_index >>= 1; - if (hse->bit_index == 0x00) { - hse->bit_index = 0x80; - LOG(" > pushing byte 0x%02x\n", hse->current_byte); - oi->buf[(*oi->output_size)++] = hse->current_byte; - hse->current_byte = 0x00; - } - } - } -} - -static void push_literal_byte(heatshrink_encoder *hse, output_info *oi) { - uint16_t processed_offset = hse->match_scan_index - 1; - uint16_t input_offset = get_input_offset(hse) + processed_offset; - uint8_t c = hse->buffer[input_offset]; - LOG("-- yielded literal byte 0x%02x ('%c') from +%d\n", - c, isprint(c) ? c : '.', input_offset); - push_bits(hse, 8, c, oi); -} - -static void save_backlog(heatshrink_encoder *hse) { - size_t input_buf_sz = get_input_buffer_size(hse); - - uint16_t msi = hse->match_scan_index; - - /* Copy processed data to beginning of buffer, so it can be - * used for future matches. Don't bother checking whether the - * input is less than the maximum size, because if it isn't, - * we're done anyway. */ - uint16_t rem = input_buf_sz - msi; // unprocessed bytes - uint16_t shift_sz = input_buf_sz + rem; - - memmove(&hse->buffer[0], - &hse->buffer[input_buf_sz - rem], - shift_sz); - - hse->match_scan_index = 0; - hse->input_size -= input_buf_sz - rem; -} diff --git a/lib/heatshrink/heatshrink_encoder.h b/lib/heatshrink/heatshrink_encoder.h deleted file mode 100644 index e2ccb44c7c2..00000000000 --- a/lib/heatshrink/heatshrink_encoder.h +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef HEATSHRINK_ENCODER_H -#define HEATSHRINK_ENCODER_H - -#include -#include -#include "heatshrink_common.h" -#include "heatshrink_config.h" - -typedef enum { - HSER_SINK_OK, /* data sunk into input buffer */ - HSER_SINK_ERROR_NULL=-1, /* NULL argument */ - HSER_SINK_ERROR_MISUSE=-2, /* API misuse */ -} HSE_sink_res; - -typedef enum { - HSER_POLL_EMPTY, /* input exhausted */ - HSER_POLL_MORE, /* poll again for more output */ - HSER_POLL_ERROR_NULL=-1, /* NULL argument */ - HSER_POLL_ERROR_MISUSE=-2, /* API misuse */ -} HSE_poll_res; - -typedef enum { - HSER_FINISH_DONE, /* encoding is complete */ - HSER_FINISH_MORE, /* more output remaining; use poll */ - HSER_FINISH_ERROR_NULL=-1, /* NULL argument */ -} HSE_finish_res; - -#if HEATSHRINK_DYNAMIC_ALLOC -#define HEATSHRINK_ENCODER_WINDOW_BITS(HSE) \ - ((HSE)->window_sz2) -#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(HSE) \ - ((HSE)->lookahead_sz2) -#define HEATSHRINK_ENCODER_INDEX(HSE) \ - ((HSE)->search_index) -struct hs_index { - uint16_t size; - int16_t index[]; -}; -#else -#define HEATSHRINK_ENCODER_WINDOW_BITS(_) \ - (HEATSHRINK_STATIC_WINDOW_BITS) -#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(_) \ - (HEATSHRINK_STATIC_LOOKAHEAD_BITS) -#define HEATSHRINK_ENCODER_INDEX(HSE) \ - (&(HSE)->search_index) -struct hs_index { - uint16_t size; - int16_t index[2 << HEATSHRINK_STATIC_WINDOW_BITS]; -}; -#endif - -typedef struct { - uint16_t input_size; /* bytes in input buffer */ - uint16_t match_scan_index; - uint16_t match_length; - uint16_t match_pos; - uint16_t outgoing_bits; /* enqueued outgoing bits */ - uint8_t outgoing_bits_count; - uint8_t flags; - uint8_t state; /* current state machine node */ - uint8_t current_byte; /* current byte of output */ - uint8_t bit_index; /* current bit index */ -#if HEATSHRINK_DYNAMIC_ALLOC - uint8_t window_sz2; /* 2^n size of window */ - uint8_t lookahead_sz2; /* 2^n size of lookahead */ -#if HEATSHRINK_USE_INDEX - struct hs_index *search_index; -#endif - /* input buffer and / sliding window for expansion */ - uint8_t* buffer; -#else - #if HEATSHRINK_USE_INDEX - struct hs_index search_index; - #endif - /* input buffer and / sliding window for expansion */ - uint8_t buffer[2 << HEATSHRINK_ENCODER_WINDOW_BITS(_)]; -#endif -} heatshrink_encoder; - -#if HEATSHRINK_DYNAMIC_ALLOC -/* Allocate a new encoder struct and its buffers. - * Returns NULL on error. */ -heatshrink_encoder *heatshrink_encoder_alloc(uint8_t* buffer, uint8_t window_sz2, - uint8_t lookahead_sz2); - -/* Free an encoder. */ -void heatshrink_encoder_free(heatshrink_encoder *hse); -#endif - -/* Reset an encoder. */ -void heatshrink_encoder_reset(heatshrink_encoder *hse); - -/* Sink up to SIZE bytes from IN_BUF into the encoder. - * INPUT_SIZE is set to the number of bytes actually sunk (in case a - * buffer was filled.). */ -HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, - uint8_t *in_buf, size_t size, size_t *input_size); - -/* Poll for output from the encoder, copying at most OUT_BUF_SIZE bytes into - * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ -HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size); - -/* Notify the encoder that the input stream is finished. - * If the return value is HSER_FINISH_MORE, there is still more output, so - * call heatshrink_encoder_poll and repeat. */ -HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse); - -#endif diff --git a/lib/ibutton/SConscript b/lib/ibutton/SConscript new file mode 100644 index 00000000000..238d65f7d16 --- /dev/null +++ b/lib/ibutton/SConscript @@ -0,0 +1,24 @@ +Import("env") + +env.Append( + LINT_SOURCES=[ + Dir("."), + ], + CPPPATH=[ + "#/lib/ibutton", + ], + SDK_HEADERS=[ + File("ibutton_key.h"), + File("ibutton_worker.h"), + File("ibutton_protocols.h"), + ], +) + +libenv = env.Clone(FW_LIB_NAME="ibutton") +libenv.ApplyLibFlags() + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/ibutton/ibutton_key.c b/lib/ibutton/ibutton_key.c new file mode 100644 index 00000000000..926a826a26e --- /dev/null +++ b/lib/ibutton/ibutton_key.c @@ -0,0 +1,43 @@ +#include "ibutton_key_i.h" + +struct iButtonKey { + iButtonProtocolId protocol_id; + iButtonProtocolData* protocol_data; + size_t protocol_data_size; +}; + +iButtonKey* ibutton_key_alloc(size_t data_size) { + iButtonKey* key = malloc(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_reset(iButtonKey* key) { + key->protocol_id = iButtonProtocolIdInvalid; + memset(key->protocol_data, 0, key->protocol_data_size); +} + +iButtonProtocolId ibutton_key_get_protocol_id(const iButtonKey* key) { + return key->protocol_id; +} + +void ibutton_key_set_protocol_id(iButtonKey* key, iButtonProtocolId protocol_id) { + key->protocol_id = protocol_id; +} + +iButtonProtocolData* ibutton_key_get_protocol_data(const iButtonKey* key) { + return key->protocol_data; +} + +size_t ibutton_key_get_protocol_data_size(const iButtonKey* key) { + return key->protocol_data_size; +} diff --git a/lib/ibutton/ibutton_key.h b/lib/ibutton/ibutton_key.h new file mode 100644 index 00000000000..1848cd67212 --- /dev/null +++ b/lib/ibutton/ibutton_key.h @@ -0,0 +1,54 @@ +/** + * @file ibutton_key.h + * + * iButton key data holder + */ + +#pragma once + +#include + +#include "protocols/protocol_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct iButtonKey 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(size_t data_size); + +/** + * Destroy the key object, free resources + * @param [in] key pointer to the key object + */ +void ibutton_key_free(iButtonKey* key); + +/** + * Get the protocol id held by the key + * @param [in] key pointer to the key object + * @return protocol id held by the key + */ +iButtonProtocolId ibutton_key_get_protocol_id(const iButtonKey* key); + +/** + * 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_protocol_id(iButtonKey* key, iButtonProtocolId protocol_id); + +/** + * Reset the protocol id and data held by the key + * @param [in] key pointer to the key object + */ +void ibutton_key_reset(iButtonKey* key); + +#ifdef __cplusplus +} +#endif diff --git a/lib/ibutton/ibutton_key_i.h b/lib/ibutton/ibutton_key_i.h new file mode 100644 index 00000000000..b527c65b444 --- /dev/null +++ b/lib/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/ibutton/ibutton_protocols.c b/lib/ibutton/ibutton_protocols.c new file mode 100644 index 00000000000..df74126708c --- /dev/null +++ b/lib/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(); +} + +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/ibutton/ibutton_protocols.h b/lib/ibutton/ibutton_protocols.h new file mode 100644 index 00000000000..0e7ed0a804a --- /dev/null +++ b/lib/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/ibutton/ibutton_worker.c b/lib/ibutton/ibutton_worker.c new file mode 100644 index 00000000000..d40dba71aeb --- /dev/null +++ b/lib/ibutton/ibutton_worker.c @@ -0,0 +1,182 @@ +#include "ibutton_worker_i.h" +#include "ibutton_protocols.h" + +#include + +typedef enum { + iButtonMessageEnd, + iButtonMessageStop, + iButtonMessageRead, + iButtonMessageWriteBlank, + iButtonMessageWriteCopy, + iButtonMessageEmulate, + iButtonMessageNotifyEmulate, +} iButtonMessageType; + +typedef struct { + iButtonMessageType type; + union { + iButtonKey* key; + } data; +} iButtonMessage; + +static int32_t ibutton_worker_thread(void* thread_context); + +iButtonWorker* ibutton_worker_alloc(iButtonProtocols* protocols) { + iButtonWorker* worker = malloc(sizeof(iButtonWorker)); + + worker->protocols = protocols; + worker->messages = furi_message_queue_alloc(1, sizeof(iButtonMessage)); + + worker->mode_index = iButtonWorkerModeIdle; + worker->thread = furi_thread_alloc_ex("iButtonWorker", 2048, ibutton_worker_thread, worker); + + return worker; +} + +void ibutton_worker_read_set_callback( + iButtonWorker* worker, + iButtonWorkerReadCallback callback, + void* context) { + furi_check(worker->mode_index == iButtonWorkerModeIdle); + worker->read_cb = callback; + worker->cb_ctx = context; +} + +void ibutton_worker_write_set_callback( + iButtonWorker* worker, + iButtonWorkerWriteCallback callback, + void* context) { + furi_check(worker->mode_index == iButtonWorkerModeIdle); + worker->write_cb = callback; + worker->cb_ctx = context; +} + +void ibutton_worker_emulate_set_callback( + iButtonWorker* worker, + iButtonWorkerEmulateCallback callback, + void* context) { + furi_check(worker->mode_index == iButtonWorkerModeIdle); + worker->emulate_cb = callback; + worker->cb_ctx = context; +} + +void ibutton_worker_read_start(iButtonWorker* worker, iButtonKey* key) { + iButtonMessage message = {.type = iButtonMessageRead, .data.key = key}; + furi_check( + furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk); +} + +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); +} + +void ibutton_worker_emulate_start(iButtonWorker* worker, iButtonKey* key) { + iButtonMessage message = {.type = iButtonMessageEmulate, .data.key = key}; + furi_check( + furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk); +} + +void ibutton_worker_stop(iButtonWorker* worker) { + iButtonMessage message = {.type = iButtonMessageStop}; + furi_check( + furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk); +} + +void ibutton_worker_free(iButtonWorker* worker) { + furi_message_queue_free(worker->messages); + furi_thread_free(worker->thread); + free(worker); +} + +void ibutton_worker_start_thread(iButtonWorker* worker) { + furi_thread_start(worker->thread); +} + +void ibutton_worker_stop_thread(iButtonWorker* worker) { + iButtonMessage message = {.type = iButtonMessageEnd}; + furi_check( + furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk); + furi_thread_join(worker->thread); +} + +void ibutton_worker_switch_mode(iButtonWorker* worker, iButtonWorkerMode mode) { + ibutton_worker_modes[worker->mode_index].stop(worker); + worker->mode_index = mode; + ibutton_worker_modes[worker->mode_index].start(worker); +} + +void ibutton_worker_notify_emulate(iButtonWorker* worker) { + iButtonMessage message = {.type = iButtonMessageNotifyEmulate}; + // we're running in an interrupt context, so we can't wait + // and we can drop message if queue is full, that's ok for that message + furi_message_queue_put(worker->messages, &message, 0); +} + +void ibutton_worker_set_key_p(iButtonWorker* worker, iButtonKey* key) { + worker->key = key; +} + +static int32_t ibutton_worker_thread(void* thread_context) { + iButtonWorker* worker = thread_context; + bool running = true; + iButtonMessage message; + FuriStatus status; + + ibutton_worker_modes[worker->mode_index].start(worker); + + while(running) { + status = furi_message_queue_get( + worker->messages, &message, ibutton_worker_modes[worker->mode_index].quant); + if(status == FuriStatusOk) { + switch(message.type) { + case iButtonMessageEnd: + ibutton_worker_switch_mode(worker, iButtonWorkerModeIdle); + ibutton_worker_set_key_p(worker, NULL); + running = false; + break; + case iButtonMessageStop: + 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, iButtonWorkerModeRead); + break; + case iButtonMessageWriteBlank: + ibutton_worker_set_key_p(worker, message.data.key); + 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, iButtonWorkerModeEmulate); + break; + case iButtonMessageNotifyEmulate: + if(worker->emulate_cb) { + worker->emulate_cb(worker->cb_ctx, true); + } + break; + } + } else if(status == FuriStatusErrorTimeout) { + ibutton_worker_modes[worker->mode_index].tick(worker); + } else { + furi_crash("iButton worker error"); + } + } + + ibutton_worker_modes[worker->mode_index].stop(worker); + + return 0; +} diff --git a/lib/one_wire/ibutton/ibutton_worker.h b/lib/ibutton/ibutton_worker.h similarity index 85% rename from lib/one_wire/ibutton/ibutton_worker.h rename to lib/ibutton/ibutton_worker.h index 5c8b1fc399b..2a12a3194de 100644 --- a/lib/one_wire/ibutton/ibutton_worker.h +++ b/lib/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/ibutton/ibutton_worker_i.h b/lib/ibutton/ibutton_worker_i.h new file mode 100644 index 00000000000..5f259a38af3 --- /dev/null +++ b/lib/ibutton/ibutton_worker_i.h @@ -0,0 +1,54 @@ +/** + * @file ibutton_worker_i.h + * + * iButton worker, internal definitions + */ + +#pragma once + +#include +#include + +#include "ibutton_worker.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + const uint32_t quant; + void (*const start)(iButtonWorker* worker); + void (*const tick)(iButtonWorker* worker); + void (*const stop)(iButtonWorker* worker); +} iButtonWorkerModeType; + +typedef enum { + iButtonWorkerModeIdle, + iButtonWorkerModeRead, + iButtonWorkerModeWriteBlank, + iButtonWorkerModeWriteCopy, + iButtonWorkerModeEmulate, +} iButtonWorkerMode; + +struct iButtonWorker { + iButtonKey* key; + iButtonProtocols* protocols; + iButtonWorkerMode mode_index; + FuriMessageQueue* messages; + FuriThread* thread; + + iButtonWorkerReadCallback read_cb; + iButtonWorkerWriteCallback write_cb; + iButtonWorkerEmulateCallback emulate_cb; + + void* cb_ctx; +}; + +extern const iButtonWorkerModeType ibutton_worker_modes[]; + +void ibutton_worker_switch_mode(iButtonWorker* worker, iButtonWorkerMode mode); +void ibutton_worker_notify_emulate(iButtonWorker* worker); + +#ifdef __cplusplus +} +#endif diff --git a/lib/ibutton/ibutton_worker_modes.c b/lib/ibutton/ibutton_worker_modes.c new file mode 100644 index 00000000000..83e207de931 --- /dev/null +++ b/lib/ibutton/ibutton_worker_modes.c @@ -0,0 +1,153 @@ +#include "ibutton_worker_i.h" + +#include + +#include +#include + +#include "ibutton_protocols.h" + +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[] = { + { + .quant = FuriWaitForever, + .start = ibutton_worker_mode_idle_start, + .tick = ibutton_worker_mode_idle_tick, + .stop = ibutton_worker_mode_idle_stop, + }, + { + .quant = 100, + .start = ibutton_worker_mode_read_start, + .tick = ibutton_worker_mode_read_tick, + .stop = ibutton_worker_mode_read_stop, + }, + { + .quant = 1000, + .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, + .start = ibutton_worker_mode_emulate_start, + .tick = ibutton_worker_mode_emulate_tick, + .stop = ibutton_worker_mode_emulate_stop, + }, +}; + +/*********************** IDLE ***********************/ + +void ibutton_worker_mode_idle_start(iButtonWorker* worker) { + UNUSED(worker); +} + +void ibutton_worker_mode_idle_tick(iButtonWorker* worker) { + UNUSED(worker); +} + +void ibutton_worker_mode_idle_stop(iButtonWorker* worker) { + UNUSED(worker); +} + +/*********************** READ ***********************/ + +void ibutton_worker_mode_read_start(iButtonWorker* worker) { + UNUSED(worker); + furi_hal_power_enable_otg(); +} + +void ibutton_worker_mode_read_tick(iButtonWorker* worker) { + if(ibutton_protocols_read(worker->protocols, worker->key)) { + if(worker->read_cb != NULL) { + worker->read_cb(worker->cb_ctx); + } + + ibutton_worker_switch_mode(worker, iButtonWorkerModeIdle); + } +} + +void ibutton_worker_mode_read_stop(iButtonWorker* worker) { + UNUSED(worker); + furi_hal_power_disable_otg(); +} + +/*********************** EMULATE ***********************/ + +void ibutton_worker_mode_emulate_start(iButtonWorker* worker) { + furi_assert(worker->key); + + furi_hal_rfid_pins_reset(); + furi_hal_rfid_pin_pull_pulldown(); + + ibutton_protocols_emulate_start(worker->protocols, worker->key); +} + +void ibutton_worker_mode_emulate_tick(iButtonWorker* worker) { + UNUSED(worker); +} + +void ibutton_worker_mode_emulate_stop(iButtonWorker* worker) { + furi_assert(worker->key); + + ibutton_protocols_emulate_stop(worker->protocols, worker->key); + + furi_hal_rfid_pins_reset(); +} + +/*********************** WRITE ***********************/ + +void ibutton_worker_mode_write_common_start(iButtonWorker* worker) { //-V524 + UNUSED(worker); + furi_hal_power_enable_otg(); +} + +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 FL-3527: 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_copy_tick(iButtonWorker* worker) { + furi_assert(worker->key); + + const bool success = ibutton_protocols_write_copy(worker->protocols, worker->key); + // TODO FL-3527: 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/ibutton/protocols/blanks/rw1990.c b/lib/ibutton/protocols/blanks/rw1990.c new file mode 100644 index 00000000000..f86e43d99c0 --- /dev/null +++ b/lib/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 FL-3528: 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 Fl-3528: Better error handling + return rw1990_read_and_compare(host, data, data_size); +} diff --git a/lib/ibutton/protocols/blanks/rw1990.h b/lib/ibutton/protocols/blanks/rw1990.h new file mode 100644 index 00000000000..bdd27f6bfbc --- /dev/null +++ b/lib/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/ibutton/protocols/blanks/tm2004.c b/lib/ibutton/protocols/blanks/tm2004.c new file mode 100644 index 00000000000..b020a218d7e --- /dev/null +++ b/lib/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 FL-3529: 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 FL-3529: Better error handling + return i == data_size; +} diff --git a/lib/ibutton/protocols/blanks/tm2004.h b/lib/ibutton/protocols/blanks/tm2004.h new file mode 100644 index 00000000000..15713af565f --- /dev/null +++ b/lib/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/ibutton/protocols/dallas/dallas_common.c b/lib/ibutton/protocols/dallas/dallas_common.c new file mode 100644 index 00000000000..6e99a3be239 --- /dev/null +++ b/lib/ibutton/protocols/dallas/dallas_common.c @@ -0,0 +1,261 @@ +#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 +#define BITS_IN_MBIT (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 FL-3530: 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* mem_data, + size_t mem_size, + const char* mem_name) { + for(size_t i = 0; i < sizeof(rom_data->bytes); ++i) { + furi_string_cat_printf(result, "%02X ", rom_data->bytes[i]); + } + + const char* size_prefix = ""; + size_t mem_size_bits = mem_size * BITS_IN_BYTE; + + if(mem_size_bits >= BITS_IN_MBIT) { + size_prefix = "M"; + mem_size_bits /= BITS_IN_MBIT; + } else if(mem_size_bits >= BITS_IN_KBIT) { + size_prefix = "K"; + mem_size_bits /= BITS_IN_KBIT; + } + + furi_string_cat_printf( + result, "\nInternal %s: %zu %sbit\n", mem_name, mem_size_bits, size_prefix); + + for(size_t i = 0; i < DALLAS_COMMON_BRIEF_HEAD_COUNT; ++i) { + furi_string_cat_printf(result, "%02X ", mem_data[i]); + } + + furi_string_cat_printf(result, "[ . . . ]"); + + for(size_t i = mem_size - DALLAS_COMMON_BRIEF_TAIL_COUNT; i < mem_size; ++i) { + furi_string_cat_printf(result, " %02X", mem_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/ibutton/protocols/dallas/dallas_common.h b/lib/ibutton/protocols/dallas/dallas_common.h new file mode 100644 index 00000000000..6f5ff7cc010 --- /dev/null +++ b/lib/ibutton/protocols/dallas/dallas_common.h @@ -0,0 +1,108 @@ +#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* mem_data, + size_t mem_size, + const char* mem_name); + +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/ibutton/protocols/dallas/protocol_dallas_base.h b/lib/ibutton/protocols/dallas/protocol_dallas_base.h new file mode 100644 index 00000000000..55e10993605 --- /dev/null +++ b/lib/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/ibutton/protocols/dallas/protocol_ds1971.c b/lib/ibutton/protocols/dallas/protocol_ds1971.c new file mode 100644 index 00000000000..b65e645846c --- /dev/null +++ b/lib/ibutton/protocols/dallas/protocol_ds1971.c @@ -0,0 +1,276 @@ +#include "protocol_ds1971.h" + +#include +#include + +#include "dallas_common.h" + +#define DS1971_FAMILY_CODE 0x14U +#define DS1971_FAMILY_NAME "DS1971" + +#define DS1971_EEPROM_DATA_SIZE 32U +#define DS1971_SRAM_PAGE_SIZE 32U +#define DS1971_COPY_SCRATCH_DELAY_US 250U + +#define DS1971_DATA_BYTE_COUNT 4U + +#define DS1971_EEPROM_DATA_KEY "Eeprom Data" +#define DS1971_MEMORY_TYPE "EEPROM" + +#define DS1971_CMD_FINALIZATION 0xA5 + +typedef struct { + OneWireSlave* bus; + DallasCommonCommandState command_state; +} DS1971ProtocolState; + +typedef struct { + DallasCommonRomData rom_data; + uint8_t eeprom_data[DS1971_EEPROM_DATA_SIZE]; + DS1971ProtocolState state; +} DS1971ProtocolData; + +static bool dallas_ds1971_read(OneWireHost*, void*); +static bool dallas_ds1971_write_copy(OneWireHost*, iButtonProtocolData*); +static void dallas_ds1971_emulate(OneWireSlave*, iButtonProtocolData*); +static bool dallas_ds1971_load(FlipperFormat*, uint32_t, iButtonProtocolData*); +static bool dallas_ds1971_save(FlipperFormat*, const iButtonProtocolData*); +static void dallas_ds1971_render_data(FuriString*, const iButtonProtocolData*); +static void dallas_ds1971_render_brief_data(FuriString*, const iButtonProtocolData*); +static void dallas_ds1971_render_error(FuriString*, const iButtonProtocolData*); +static bool dallas_ds1971_is_data_valid(const iButtonProtocolData*); +static void dallas_ds1971_get_editable_data(iButtonEditableData*, iButtonProtocolData*); +static void dallas_ds1971_apply_edits(iButtonProtocolData*); +static bool + dallas_ds1971_read_mem(OneWireHost* host, uint8_t address, uint8_t* data, size_t data_size); +static bool ds1971_emulate_read_mem(OneWireSlave* bus, const uint8_t* data, size_t data_size); + +const iButtonProtocolDallasBase ibutton_protocol_ds1971 = { + .family_code = DS1971_FAMILY_CODE, + .features = iButtonProtocolFeatureExtData | iButtonProtocolFeatureWriteCopy, + .data_size = sizeof(DS1971ProtocolData), + .manufacturer = DALLAS_COMMON_MANUFACTURER_NAME, + .name = DS1971_FAMILY_NAME, + + .read = dallas_ds1971_read, + .write_blank = NULL, // TODO FL-3531: Implement writing to blank + .write_copy = dallas_ds1971_write_copy, + .emulate = dallas_ds1971_emulate, + .save = dallas_ds1971_save, + .load = dallas_ds1971_load, + .render_data = dallas_ds1971_render_data, + .render_brief_data = dallas_ds1971_render_brief_data, + .render_error = dallas_ds1971_render_error, + .is_valid = dallas_ds1971_is_data_valid, + .get_editable_data = dallas_ds1971_get_editable_data, + .apply_edits = dallas_ds1971_apply_edits, +}; + +bool dallas_ds1971_read(OneWireHost* host, iButtonProtocolData* protocol_data) { + DS1971ProtocolData* data = protocol_data; + return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data) && + dallas_ds1971_read_mem(host, 0, data->eeprom_data, DS1971_EEPROM_DATA_SIZE); +} + +bool dallas_ds1971_write_copy(OneWireHost* host, iButtonProtocolData* protocol_data) { + DS1971ProtocolData* data = protocol_data; + + onewire_host_reset(host); + onewire_host_write(host, DALLAS_COMMON_CMD_SKIP_ROM); + // Starting writing from address 0x0000 + onewire_host_write(host, DALLAS_COMMON_CMD_WRITE_SCRATCH); + onewire_host_write(host, 0x00); + // Write data to scratchpad + onewire_host_write_bytes(host, data->eeprom_data, DS1971_EEPROM_DATA_SIZE); + + // Read data from scratchpad and verify + bool pad_valid = false; + if(onewire_host_reset(host)) { + pad_valid = true; + onewire_host_write(host, DALLAS_COMMON_CMD_SKIP_ROM); + onewire_host_write(host, DALLAS_COMMON_CMD_READ_SCRATCH); + onewire_host_write(host, 0x00); + + for(size_t i = 0; i < DS1971_EEPROM_DATA_SIZE; ++i) { + uint8_t scratch = onewire_host_read(host); + if(data->eeprom_data[i] != scratch) { + pad_valid = false; + break; + } + } + } + + // Copy scratchpad to memory and confirm + if(pad_valid) { + if(onewire_host_reset(host)) { + onewire_host_write(host, DALLAS_COMMON_CMD_SKIP_ROM); + onewire_host_write(host, DALLAS_COMMON_CMD_COPY_SCRATCH); + onewire_host_write(host, DS1971_CMD_FINALIZATION); + + furi_delay_us(DS1971_COPY_SCRATCH_DELAY_US); + } + } + + return pad_valid; +} + +static bool dallas_ds1971_reset_callback(bool is_short, void* context) { + furi_assert(context); + DS1971ProtocolData* data = context; + + if(!is_short) { + data->state.command_state = DallasCommonCommandStateIdle; + onewire_slave_set_overdrive(data->state.bus, is_short); + } + + return !is_short; +} + +static bool dallas_ds1971_command_callback(uint8_t command, void* context) { + furi_assert(context); + DS1971ProtocolData* 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; + ds1971_emulate_read_mem(bus, data->eeprom_data, DS1971_EEPROM_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_ds1971_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) { + DS1971ProtocolData* data = protocol_data; + data->state.bus = bus; + + onewire_slave_set_reset_callback(bus, dallas_ds1971_reset_callback, protocol_data); + onewire_slave_set_command_callback(bus, dallas_ds1971_command_callback, protocol_data); +} + +bool dallas_ds1971_load( + FlipperFormat* ff, + uint32_t format_version, + iButtonProtocolData* protocol_data) { + DS1971ProtocolData* 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, DS1971_EEPROM_DATA_KEY, data->eeprom_data, DS1971_EEPROM_DATA_SIZE)) + break; + success = true; + } while(false); + + return success; +} + +bool dallas_ds1971_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) { + const DS1971ProtocolData* data = protocol_data; + bool success = false; + + do { + if(!dallas_common_save_rom_data(ff, &data->rom_data)) break; + if(!flipper_format_write_hex( + ff, DS1971_EEPROM_DATA_KEY, data->eeprom_data, DS1971_EEPROM_DATA_SIZE)) + break; + success = true; + } while(false); + + return success; +} + +void dallas_ds1971_render_data(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1971ProtocolData* data = protocol_data; + pretty_format_bytes_hex_canonical( + result, + DS1971_DATA_BYTE_COUNT, + PRETTY_FORMAT_FONT_MONOSPACE, + data->eeprom_data, + DS1971_EEPROM_DATA_SIZE); +} + +void dallas_ds1971_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1971ProtocolData* data = protocol_data; + dallas_common_render_brief_data( + result, &data->rom_data, data->eeprom_data, DS1971_EEPROM_DATA_SIZE, DS1971_MEMORY_TYPE); +} + +void dallas_ds1971_render_error(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1971ProtocolData* data = protocol_data; + + if(!dallas_common_is_valid_crc(&data->rom_data)) { + dallas_common_render_crc_error(result, &data->rom_data); + } +} + +bool dallas_ds1971_is_data_valid(const iButtonProtocolData* protocol_data) { + const DS1971ProtocolData* data = protocol_data; + return dallas_common_is_valid_crc(&data->rom_data); +} + +void dallas_ds1971_get_editable_data( + iButtonEditableData* editable_data, + iButtonProtocolData* protocol_data) { + DS1971ProtocolData* data = protocol_data; + editable_data->ptr = data->rom_data.bytes; + editable_data->size = sizeof(DallasCommonRomData); +} + +void dallas_ds1971_apply_edits(iButtonProtocolData* protocol_data) { + DS1971ProtocolData* data = protocol_data; + dallas_common_apply_edits(&data->rom_data, DS1971_FAMILY_CODE); +} + +bool dallas_ds1971_read_mem(OneWireHost* host, uint8_t address, uint8_t* data, size_t data_size) { + onewire_host_write(host, DALLAS_COMMON_CMD_READ_MEM); + + onewire_host_write(host, address); + onewire_host_read_bytes(host, data, (uint8_t)data_size); + + return true; +} + +bool ds1971_emulate_read_mem(OneWireSlave* bus, const uint8_t* data, size_t data_size) { + bool success = false; + + do { + uint8_t address; + if(!onewire_slave_receive(bus, &address, sizeof(address))) break; + if(address >= data_size) break; + if(!onewire_slave_send(bus, data + address, data_size - address)) break; + + success = true; + } while(false); + + return success; +} diff --git a/lib/ibutton/protocols/dallas/protocol_ds1971.h b/lib/ibutton/protocols/dallas/protocol_ds1971.h new file mode 100644 index 00000000000..522b612dabf --- /dev/null +++ b/lib/ibutton/protocols/dallas/protocol_ds1971.h @@ -0,0 +1,5 @@ +#pragma once + +#include "protocol_dallas_base.h" + +extern const iButtonProtocolDallasBase ibutton_protocol_ds1971; diff --git a/lib/ibutton/protocols/dallas/protocol_ds1990.c b/lib/ibutton/protocols/dallas/protocol_ds1990.c new file mode 100644 index 00000000000..86d39f1bd87 --- /dev/null +++ b/lib/ibutton/protocols/dallas/protocol_ds1990.c @@ -0,0 +1,152 @@ +#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_reset_callback(bool is_short, void* context) { + DS1990ProtocolData* data = context; + if(!is_short) { + onewire_slave_set_overdrive(data->state.bus, is_short); + } + return !is_short; +} + +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, dallas_ds1990_reset_callback, protocol_data); + 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/ibutton/protocols/dallas/protocol_ds1990.h b/lib/ibutton/protocols/dallas/protocol_ds1990.h new file mode 100644 index 00000000000..de3da0e4cb9 --- /dev/null +++ b/lib/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/ibutton/protocols/dallas/protocol_ds1992.c b/lib/ibutton/protocols/dallas/protocol_ds1992.c new file mode 100644 index 00000000000..7440882ea0d --- /dev/null +++ b/lib/ibutton/protocols/dallas/protocol_ds1992.c @@ -0,0 +1,225 @@ +#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" +#define DS1992_MEMORY_TYPE "SRAM" + +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 FL-3532: 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 bool dallas_ds1992_reset_callback(bool is_short, void* context) { + furi_assert(context); + DS1992ProtocolData* data = context; + + if(!is_short) { + data->state.command_state = DallasCommonCommandStateIdle; + onewire_slave_set_overdrive(data->state.bus, is_short); + } + + return !is_short; +} + +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, DS1992_MEMORY_TYPE); +} + +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/ibutton/protocols/dallas/protocol_ds1992.h b/lib/ibutton/protocols/dallas/protocol_ds1992.h new file mode 100644 index 00000000000..5a3c45f3be3 --- /dev/null +++ b/lib/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/ibutton/protocols/dallas/protocol_ds1996.c b/lib/ibutton/protocols/dallas/protocol_ds1996.c new file mode 100644 index 00000000000..5970a67bbfe --- /dev/null +++ b/lib/ibutton/protocols/dallas/protocol_ds1996.c @@ -0,0 +1,251 @@ +#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" +#define DS1996_MEMORY_TYPE "SRAM" + +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; + bool success = false; + + do { + if(!onewire_host_reset(host)) break; + if(!dallas_common_read_rom(host, &data->rom_data)) break; + if(!onewire_host_reset(host)) break; + + onewire_host_write(host, DALLAS_COMMON_CMD_OVERDRIVE_SKIP_ROM); + onewire_host_set_overdrive(host, true); + + if(!dallas_common_read_mem(host, 0, data->sram_data, DS1996_SRAM_DATA_SIZE)) break; + success = true; + } while(false); + + onewire_host_set_overdrive(host, false); + return success; +} + +bool dallas_ds1996_write_copy(OneWireHost* host, iButtonProtocolData* protocol_data) { + DS1996ProtocolData* data = protocol_data; + bool success = false; + + do { + if(!onewire_host_reset(host)) break; + + onewire_host_write(host, DALLAS_COMMON_CMD_OVERDRIVE_SKIP_ROM); + onewire_host_set_overdrive(host, true); + + if(!dallas_common_write_mem( + host, + DS1996_COPY_SCRATCH_TIMEOUT_US, + DS1996_SRAM_PAGE_SIZE, + data->sram_data, + DS1996_SRAM_DATA_SIZE)) + break; + success = true; + } while(false); + + onewire_host_set_overdrive(host, false); + return success; +} + +static bool dallas_ds1996_reset_callback(bool is_short, void* context) { + furi_assert(context); + DS1996ProtocolData* data = context; + data->state.command_state = DallasCommonCommandStateIdle; + onewire_slave_set_overdrive(data->state.bus, is_short); + return true; +} + +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; + return dallas_common_emulate_read_mem(bus, data->sram_data, DS1996_SRAM_DATA_SIZE); + + } 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: + if(data->state.command_state == DallasCommonCommandStateIdle) { + data->state.command_state = DallasCommonCommandStateRomCmd; + onewire_slave_set_overdrive(bus, true); + return true; + } else { + return false; + } + + case DALLAS_COMMON_CMD_MATCH_ROM: + case DALLAS_COMMON_CMD_OVERDRIVE_MATCH_ROM: + /* TODO FL-3533: Match ROM command 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, DS1996_MEMORY_TYPE); +} + +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/ibutton/protocols/dallas/protocol_ds1996.h b/lib/ibutton/protocols/dallas/protocol_ds1996.h new file mode 100644 index 00000000000..34546985b41 --- /dev/null +++ b/lib/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/ibutton/protocols/dallas/protocol_ds_generic.c b/lib/ibutton/protocols/dallas/protocol_ds_generic.c new file mode 100644 index 00000000000..6c698bb8924 --- /dev/null +++ b/lib/ibutton/protocols/dallas/protocol_ds_generic.c @@ -0,0 +1,142 @@ +#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_reset_callback(bool is_short, void* context) { + furi_assert(context); + DallasGenericProtocolData* data = context; + if(!is_short) { + onewire_slave_set_overdrive(data->state.bus, is_short); + } + return !is_short; +} + +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, ds_generic_reset_callback, protocol_data); + 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/ibutton/protocols/dallas/protocol_ds_generic.h b/lib/ibutton/protocols/dallas/protocol_ds_generic.h new file mode 100644 index 00000000000..008f8a67b0d --- /dev/null +++ b/lib/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/ibutton/protocols/dallas/protocol_group_dallas.c b/lib/ibutton/protocols/dallas/protocol_group_dallas.c new file mode 100644 index 00000000000..63ec97855ab --- /dev/null +++ b/lib/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(&gpio_ibutton); + group->bus = onewire_slave_alloc(&gpio_ibutton); + + 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/ibutton/protocols/dallas/protocol_group_dallas.h b/lib/ibutton/protocols/dallas/protocol_group_dallas.h new file mode 100644 index 00000000000..d1293c71b81 --- /dev/null +++ b/lib/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/ibutton/protocols/dallas/protocol_group_dallas_defs.c b/lib/ibutton/protocols/dallas/protocol_group_dallas_defs.c new file mode 100644 index 00000000000..b4dd51ce717 --- /dev/null +++ b/lib/ibutton/protocols/dallas/protocol_group_dallas_defs.c @@ -0,0 +1,18 @@ +#include "protocol_group_dallas_defs.h" + +#include "protocol_ds1990.h" +#include "protocol_ds1992.h" +#include "protocol_ds1996.h" +#include "protocol_ds1971.h" +#include "protocol_ds_generic.h" + +const iButtonProtocolDallasBase* ibutton_protocols_dallas[] = { + [iButtonProtocolDS1990] = &ibutton_protocol_ds1990, + [iButtonProtocolDS1992] = &ibutton_protocol_ds1992, + [iButtonProtocolDS1996] = &ibutton_protocol_ds1996, + [iButtonProtocolDS1971] = &ibutton_protocol_ds1971, + /* Add new 1-Wire protocols here */ + + /* Default catch-all 1-Wire protocol */ + [iButtonProtocolDSGeneric] = &ibutton_protocol_ds_generic, +}; diff --git a/lib/ibutton/protocols/dallas/protocol_group_dallas_defs.h b/lib/ibutton/protocols/dallas/protocol_group_dallas_defs.h new file mode 100644 index 00000000000..2ba1dd39acb --- /dev/null +++ b/lib/ibutton/protocols/dallas/protocol_group_dallas_defs.h @@ -0,0 +1,17 @@ +#pragma once + +#include "protocol_dallas_base.h" + +typedef enum { + iButtonProtocolDS1990, + iButtonProtocolDS1992, + iButtonProtocolDS1996, + iButtonProtocolDS1971, + /* 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/ibutton/protocols/misc/protocol_cyfral.c b/lib/ibutton/protocols/misc/protocol_cyfral.c new file mode 100644 index 00000000000..0cdb8766b00 --- /dev/null +++ b/lib/ibutton/protocols/misc/protocol_cyfral.c @@ -0,0 +1,352 @@ +#include +#include + +#include "protocol_cyfral.h" + +#define CYFRAL_DATA_SIZE sizeof(uint16_t) +#define CYFRAL_PERIOD (125 * furi_hal_cortex_instructions_per_microsecond()) +#define CYFRAL_0_LOW (CYFRAL_PERIOD * 0.66f) +#define CYFRAL_0_HI (CYFRAL_PERIOD * 0.33f) +#define CYFRAL_1_LOW (CYFRAL_PERIOD * 0.33f) +#define CYFRAL_1_HI (CYFRAL_PERIOD * 0.66f) + +#define CYFRAL_MAX_PERIOD_US 230 + +typedef enum { + CYFRAL_BIT_WAIT_FRONT_HIGH, + CYFRAL_BIT_WAIT_FRONT_LOW, +} CyfralBitState; + +typedef enum { + CYFRAL_WAIT_START_NIBBLE, + CYFRAL_READ_NIBBLE, + CYFRAL_READ_STOP_NIBBLE, +} CyfralState; + +typedef struct { + CyfralState state; + CyfralBitState bit_state; + + // high + low period time + uint32_t period_time; + // temporary nibble storage + uint8_t nibble; + // data valid flag + // MUST be checked only in READ_STOP_NIBBLE state + bool data_valid; + // nibble index, we expect 8 nibbles + uint8_t index; + // bit index in nibble, 4 bit per nibble + uint8_t bit_index; + // max period, 230us x clock per us + uint32_t max_period; +} ProtocolCyfralDecoder; + +typedef struct { + uint32_t data; + uint32_t index; +} ProtocolCyfralEncoder; + +typedef struct { + uint16_t data; + + ProtocolCyfralDecoder decoder; + ProtocolCyfralEncoder encoder; +} ProtocolCyfral; + +static void* protocol_cyfral_alloc(void) { + ProtocolCyfral* proto = malloc(sizeof(ProtocolCyfral)); + return (void*)proto; +} + +static void protocol_cyfral_free(ProtocolCyfral* proto) { + free(proto); +} + +static uint8_t* protocol_cyfral_get_data(ProtocolCyfral* proto) { + return (uint8_t*)&proto->data; +} + +static void protocol_cyfral_decoder_start(ProtocolCyfral* proto) { + ProtocolCyfralDecoder* cyfral = &proto->decoder; + + cyfral->state = CYFRAL_WAIT_START_NIBBLE; + cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW; + cyfral->period_time = 0; + cyfral->bit_index = 0; + cyfral->index = 0; + cyfral->nibble = 0; + cyfral->data_valid = true; + cyfral->max_period = CYFRAL_MAX_PERIOD_US * furi_hal_cortex_instructions_per_microsecond(); + + proto->data = 0; +} + +static bool protocol_cyfral_decoder_process_bit( + ProtocolCyfralDecoder* cyfral, + bool polarity, + uint32_t length, + bool* bit_ready, + bool* bit_value) { + bool result = true; + *bit_ready = false; + + // bit start from low + switch(cyfral->bit_state) { + case CYFRAL_BIT_WAIT_FRONT_LOW: + if(polarity == true) { + cyfral->period_time += length; + + *bit_ready = true; + if(cyfral->period_time <= cyfral->max_period) { + if((cyfral->period_time / 2) > length) { + *bit_value = false; + } else { + *bit_value = true; + } + } else { + result = false; + } + + cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_HIGH; + } else { + result = false; + } + break; + case CYFRAL_BIT_WAIT_FRONT_HIGH: + if(polarity == false) { + cyfral->period_time = length; + cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW; + } else { + result = false; + } + break; + } + + return result; +} + +static bool protocol_cyfral_decoder_feed(ProtocolCyfral* proto, bool level, uint32_t duration) { + ProtocolCyfralDecoder* cyfral = &proto->decoder; + + bool bit_ready; + bool bit_value; + bool decoded = false; + + switch(cyfral->state) { + case CYFRAL_WAIT_START_NIBBLE: + // wait for start word + if(protocol_cyfral_decoder_process_bit(cyfral, level, duration, &bit_ready, &bit_value)) { + if(bit_ready) { + cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F; + if(cyfral->nibble == 0b0001) { + cyfral->nibble = 0; + cyfral->state = CYFRAL_READ_NIBBLE; + } + } + } else { + protocol_cyfral_decoder_start(proto); + } + + break; + case CYFRAL_READ_NIBBLE: + // read nibbles + if(protocol_cyfral_decoder_process_bit(cyfral, level, duration, &bit_ready, &bit_value)) { + if(bit_ready) { + cyfral->nibble = (cyfral->nibble << 1) | bit_value; + + cyfral->bit_index++; + + //convert every nibble to 2-bit index + if(cyfral->bit_index == 4) { + switch(cyfral->nibble) { + case 0b1110: + proto->data = (proto->data << 2) | 0b11; + break; + case 0b1101: + proto->data = (proto->data << 2) | 0b10; + break; + case 0b1011: + proto->data = (proto->data << 2) | 0b01; + break; + case 0b0111: + proto->data = (proto->data << 2) | 0b00; + break; + default: + cyfral->data_valid = false; + break; + } + + cyfral->nibble = 0; + cyfral->bit_index = 0; + cyfral->index++; + } + + // successfully read 8 nibbles + if(cyfral->index == 8) { + cyfral->state = CYFRAL_READ_STOP_NIBBLE; + } + } + } else { + protocol_cyfral_decoder_start(proto); + } + break; + case CYFRAL_READ_STOP_NIBBLE: + // read stop nibble + if(protocol_cyfral_decoder_process_bit(cyfral, level, duration, &bit_ready, &bit_value)) { + if(bit_ready) { + cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F; + cyfral->bit_index++; + + switch(cyfral->bit_index) { + case 0: + case 1: + case 2: + case 3: + break; + case 4: + if(cyfral->nibble == 0b0001) { + // validate data + if(cyfral->data_valid) { + decoded = true; + } else { + protocol_cyfral_decoder_start(proto); + } + } else { + protocol_cyfral_decoder_start(proto); + } + break; + default: + protocol_cyfral_decoder_start(proto); + break; + } + } + } else { + protocol_cyfral_decoder_start(proto); + } + break; + } + + return decoded; +} + +static uint32_t protocol_cyfral_encoder_encode(const uint16_t data) { + uint32_t value = 0; + for(int8_t i = 0; i <= 7; i++) { + switch((data >> (i * 2)) & 0b00000011) { + case 0b11: + value = value << 4; + value += 0b00000111; + break; + case 0b10: + value = value << 4; + value += 0b00001011; + break; + case 0b01: + value = value << 4; + value += 0b00001101; + break; + case 0b00: + value = value << 4; + value += 0b00001110; + break; + default: + break; + } + } + + return value; +} + +static bool protocol_cyfral_encoder_start(ProtocolCyfral* proto) { + proto->encoder.index = 0; + proto->encoder.data = protocol_cyfral_encoder_encode(proto->data); + return true; +} + +static LevelDuration protocol_cyfral_encoder_yield(ProtocolCyfral* proto) { + LevelDuration result; + + if(proto->encoder.index < 8) { + // start word (0b0001) + switch(proto->encoder.index) { + case 0: + result = level_duration_make(false, CYFRAL_0_LOW); //-V1037 + break; + case 1: + result = level_duration_make(true, CYFRAL_0_HI); //-V1037 + break; + case 2: + result = level_duration_make(false, CYFRAL_0_LOW); + break; + case 3: + result = level_duration_make(true, CYFRAL_0_HI); + break; + case 4: + result = level_duration_make(false, CYFRAL_0_LOW); + break; + case 5: + result = level_duration_make(true, CYFRAL_0_HI); + break; + case 6: + result = level_duration_make(false, CYFRAL_1_LOW); + break; + case 7: + result = level_duration_make(true, CYFRAL_1_HI); + break; + } + } else { + // data + uint8_t data_start_index = proto->encoder.index - 8; + bool clock_polarity = (data_start_index) % 2; + uint8_t bit_index = (data_start_index) / 2; + bool bit_value = ((proto->encoder.data >> bit_index) & 1); + + if(!clock_polarity) { + if(bit_value) { + result = level_duration_make(false, CYFRAL_1_LOW); + } else { + result = level_duration_make(false, CYFRAL_0_LOW); + } + } else { + if(bit_value) { + result = level_duration_make(true, CYFRAL_1_HI); + } else { + result = level_duration_make(true, CYFRAL_0_HI); + } + } + } + + proto->encoder.index++; + if(proto->encoder.index >= (9 * 4 * 2)) { + proto->encoder.index = 0; + } + + return result; +} + +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, + .alloc = (ProtocolAlloc)protocol_cyfral_alloc, + .free = (ProtocolFree)protocol_cyfral_free, + .get_data = (ProtocolGetData)protocol_cyfral_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_cyfral_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_cyfral_decoder_feed, + }, + .encoder = + { + .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/ibutton/protocols/misc/protocol_cyfral.h b/lib/ibutton/protocols/misc/protocol_cyfral.h new file mode 100644 index 00000000000..08b8eef8163 --- /dev/null +++ b/lib/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/ibutton/protocols/misc/protocol_group_misc.c b/lib/ibutton/protocols/misc/protocol_group_misc.c new file mode 100644 index 00000000000..dad1ef3cfee --- /dev/null +++ b/lib/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/ibutton/protocols/misc/protocol_group_misc.h b/lib/ibutton/protocols/misc/protocol_group_misc.h new file mode 100644 index 00000000000..7fde6e73763 --- /dev/null +++ b/lib/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/ibutton/protocols/misc/protocol_group_misc_defs.c b/lib/ibutton/protocols/misc/protocol_group_misc_defs.c new file mode 100644 index 00000000000..09ae0bdc7cb --- /dev/null +++ b/lib/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/ibutton/protocols/misc/protocol_group_misc_defs.h b/lib/ibutton/protocols/misc/protocol_group_misc_defs.h new file mode 100644 index 00000000000..0a7f92847ec --- /dev/null +++ b/lib/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/ibutton/protocols/misc/protocol_metakom.c b/lib/ibutton/protocols/misc/protocol_metakom.c new file mode 100644 index 00000000000..a2bd2cf7ca4 --- /dev/null +++ b/lib/ibutton/protocols/misc/protocol_metakom.c @@ -0,0 +1,328 @@ +#include +#include + +#include "protocol_metakom.h" + +#define METAKOM_DATA_SIZE sizeof(uint32_t) +#define METAKOM_PERIOD (125 * furi_hal_cortex_instructions_per_microsecond()) +#define METAKOM_0_LOW (METAKOM_PERIOD * 0.33f) +#define METAKOM_0_HI (METAKOM_PERIOD * 0.66f) +#define METAKOM_1_LOW (METAKOM_PERIOD * 0.66f) +#define METAKOM_1_HI (METAKOM_PERIOD * 0.33f) + +#define METAKOM_PERIOD_SAMPLE_COUNT 10 + +typedef enum { + METAKOM_WAIT_PERIOD_SYNC, + METAKOM_WAIT_START_BIT, + METAKOM_WAIT_START_WORD, + METAKOM_READ_WORD, + METAKOM_READ_STOP_WORD, +} MetakomState; + +typedef enum { + METAKOM_BIT_WAIT_FRONT_HIGH, + METAKOM_BIT_WAIT_FRONT_LOW, +} MetakomBitState; + +typedef struct { + // high + low period time + uint32_t period_time; + uint32_t low_time_storage; + uint8_t period_sample_index; + uint32_t period_sample_data[METAKOM_PERIOD_SAMPLE_COUNT]; + + uint8_t tmp_data; + uint8_t tmp_counter; + + uint8_t key_data_index; + + MetakomBitState bit_state; + MetakomState state; +} ProtocolMetakomDecoder; + +typedef struct { + uint32_t index; +} ProtocolMetakomEncoder; + +typedef struct { + uint32_t data; + + ProtocolMetakomDecoder decoder; + ProtocolMetakomEncoder encoder; +} ProtocolMetakom; + +static ProtocolMetakom* protocol_metakom_alloc(void) { + ProtocolMetakom* proto = malloc(sizeof(ProtocolMetakom)); + return (void*)proto; +} + +static void protocol_metakom_free(ProtocolMetakom* proto) { + free(proto); +} + +static uint8_t* protocol_metakom_get_data(ProtocolMetakom* proto) { + return (uint8_t*)&proto->data; +} + +static void protocol_metakom_decoder_start(ProtocolMetakom* proto) { + ProtocolMetakomDecoder* metakom = &proto->decoder; + + metakom->period_sample_index = 0; + metakom->period_time = 0; + metakom->tmp_counter = 0; + metakom->tmp_data = 0; + for(uint8_t i = 0; i < METAKOM_PERIOD_SAMPLE_COUNT; i++) { + metakom->period_sample_data[i] = 0; + }; + metakom->state = METAKOM_WAIT_PERIOD_SYNC; + metakom->bit_state = METAKOM_BIT_WAIT_FRONT_LOW; + metakom->key_data_index = 0; + metakom->low_time_storage = 0; + + proto->data = 0; +} + +static bool metakom_parity_check(uint8_t data) { + uint8_t ones_count = 0; + bool result; + + for(uint8_t i = 0; i < 8; i++) { + if((data >> i) & 0b00000001) { + ones_count++; + } + } + + result = (ones_count % 2 == 0); + + return result; +} + +static bool metakom_process_bit( + ProtocolMetakomDecoder* metakom, + bool polarity, + uint32_t time, + uint32_t* high_time, + uint32_t* low_time) { + bool result = false; + + switch(metakom->bit_state) { + case METAKOM_BIT_WAIT_FRONT_LOW: + if(polarity == false) { + *low_time = metakom->low_time_storage; + *high_time = time; + result = true; + metakom->bit_state = METAKOM_BIT_WAIT_FRONT_HIGH; + } + break; + case METAKOM_BIT_WAIT_FRONT_HIGH: + if(polarity == true) { + metakom->low_time_storage = time; + metakom->bit_state = METAKOM_BIT_WAIT_FRONT_LOW; + } + break; + } + + return result; +} + +static bool protocol_metakom_decoder_feed(ProtocolMetakom* proto, bool level, uint32_t duration) { + ProtocolMetakomDecoder* metakom = &proto->decoder; + + bool ready = false; + + uint32_t high_time = 0; + uint32_t low_time = 0; + + switch(metakom->state) { + case METAKOM_WAIT_PERIOD_SYNC: + if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) { + metakom->period_sample_data[metakom->period_sample_index] = high_time + low_time; + metakom->period_sample_index++; + + if(metakom->period_sample_index == METAKOM_PERIOD_SAMPLE_COUNT) { + for(uint8_t i = 0; i < METAKOM_PERIOD_SAMPLE_COUNT; i++) { + metakom->period_time += metakom->period_sample_data[i]; + }; + metakom->period_time /= METAKOM_PERIOD_SAMPLE_COUNT; + + metakom->state = METAKOM_WAIT_START_BIT; + } + } + + break; + case METAKOM_WAIT_START_BIT: + if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) { + metakom->tmp_counter++; + if(high_time > metakom->period_time) { + metakom->tmp_counter = 0; + metakom->state = METAKOM_WAIT_START_WORD; + } + + if(metakom->tmp_counter > 40) { + protocol_metakom_decoder_start(proto); + } + } + + break; + case METAKOM_WAIT_START_WORD: + if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) { + if(low_time < (metakom->period_time / 2)) { + metakom->tmp_data = (metakom->tmp_data << 1) | 0b0; + } else { + metakom->tmp_data = (metakom->tmp_data << 1) | 0b1; + } + metakom->tmp_counter++; + + if(metakom->tmp_counter == 3) { + if(metakom->tmp_data == 0b010) { + metakom->tmp_counter = 0; + metakom->tmp_data = 0; + metakom->state = METAKOM_READ_WORD; + } else { + protocol_metakom_decoder_start(proto); + } + } + } + break; + case METAKOM_READ_WORD: + if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) { + if(low_time < (metakom->period_time / 2)) { + metakom->tmp_data = (metakom->tmp_data << 1) | 0b0; + } else { + metakom->tmp_data = (metakom->tmp_data << 1) | 0b1; + } + metakom->tmp_counter++; + + if(metakom->tmp_counter == 8) { + if(metakom_parity_check(metakom->tmp_data)) { + proto->data = (proto->data << 8) | metakom->tmp_data; + metakom->key_data_index++; + metakom->tmp_data = 0; + metakom->tmp_counter = 0; + + if(metakom->key_data_index == 4) { + // check for stop bit + if(high_time > metakom->period_time) { + metakom->state = METAKOM_READ_STOP_WORD; + } else { + protocol_metakom_decoder_start(proto); + } + } + } else { + protocol_metakom_decoder_start(proto); + } + } + } + break; + case METAKOM_READ_STOP_WORD: + if(metakom_process_bit(metakom, level, duration, &high_time, &low_time)) { + if(low_time < (metakom->period_time / 2)) { + metakom->tmp_data = (metakom->tmp_data << 1) | 0b0; + } else { + metakom->tmp_data = (metakom->tmp_data << 1) | 0b1; + } + metakom->tmp_counter++; + + if(metakom->tmp_counter == 3) { + if(metakom->tmp_data == 0b010) { + ready = true; + } else { + protocol_metakom_decoder_start(proto); + } + } + } + break; + } + + return ready; +} + +static bool protocol_metakom_encoder_start(ProtocolMetakom* proto) { + proto->encoder.index = 0; + return true; +} + +static LevelDuration protocol_metakom_encoder_yield(ProtocolMetakom* proto) { + LevelDuration result; + + if(proto->encoder.index == 0) { + // sync bit + result = level_duration_make(false, METAKOM_PERIOD); + } else if(proto->encoder.index <= 6) { + // start word (0b010) + switch(proto->encoder.index) { + case 1: + result = level_duration_make(true, METAKOM_0_LOW); //-V1037 + break; + case 2: + result = level_duration_make(false, METAKOM_0_HI); //-V1037 + break; + case 3: + result = level_duration_make(true, METAKOM_1_LOW); + break; + case 4: + result = level_duration_make(false, METAKOM_1_HI); + break; + case 5: + result = level_duration_make(true, METAKOM_0_LOW); + break; + case 6: + result = level_duration_make(false, METAKOM_0_HI); + break; + } + } else { + // data + uint8_t data_start_index = proto->encoder.index - 7; + bool clock_polarity = (data_start_index) % 2; + uint8_t bit_index = (data_start_index) / 2; + bool bit_value = (proto->data >> (32 - 1 - bit_index)) & 1; + + if(!clock_polarity) { + if(bit_value) { + result = level_duration_make(true, METAKOM_1_LOW); + } else { + result = level_duration_make(true, METAKOM_0_LOW); + } + } else { + if(bit_value) { + result = level_duration_make(false, METAKOM_1_HI); + } else { + result = level_duration_make(false, METAKOM_0_HI); + } + } + } + + proto->encoder.index++; + if(proto->encoder.index >= (1 + 3 * 2 + 32 * 2)) { + proto->encoder.index = 0; + } + + return result; +} + +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, + .alloc = (ProtocolAlloc)protocol_metakom_alloc, + .free = (ProtocolFree)protocol_metakom_free, + .get_data = (ProtocolGetData)protocol_metakom_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_metakom_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_metakom_decoder_feed, + }, + .encoder = + { + .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/ibutton/protocols/misc/protocol_metakom.h b/lib/ibutton/protocols/misc/protocol_metakom.h new file mode 100644 index 00000000000..317619e3fb1 --- /dev/null +++ b/lib/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/ibutton/protocols/protocol_common.h b/lib/ibutton/protocols/protocol_common.h new file mode 100644 index 00000000000..5383158e46b --- /dev/null +++ b/lib/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/ibutton/protocols/protocol_common_i.h b/lib/ibutton/protocols/protocol_common_i.h new file mode 100644 index 00000000000..b80c92887ff --- /dev/null +++ b/lib/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/ibutton/protocols/protocol_group_base.h b/lib/ibutton/protocols/protocol_group_base.h new file mode 100644 index 00000000000..c8fec70fe78 --- /dev/null +++ b/lib/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/ibutton/protocols/protocol_group_defs.c b/lib/ibutton/protocols/protocol_group_defs.c new file mode 100644 index 00000000000..40a360f0eb4 --- /dev/null +++ b/lib/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/ibutton/protocols/protocol_group_defs.h b/lib/ibutton/protocols/protocol_group_defs.h new file mode 100644 index 00000000000..2d41e3cb86e --- /dev/null +++ b/lib/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/infrared/SConscript b/lib/infrared/SConscript index 35db75f879d..a32248a645b 100644 --- a/lib/infrared/SConscript +++ b/lib/infrared/SConscript @@ -5,6 +5,14 @@ env.Append( "#/lib/infrared/encoder_decoder", "#/lib/infrared/worker", ], + SDK_HEADERS=[ + File("encoder_decoder/infrared.h"), + File("worker/infrared_worker.h"), + File("worker/infrared_transmit.h"), + ], + LINT_SOURCES=[ + Dir("."), + ], ) diff --git a/lib/infrared/encoder_decoder/common/infrared_common_decoder.c b/lib/infrared/encoder_decoder/common/infrared_common_decoder.c index bff4c73db75..d67b204f52d 100644 --- a/lib/infrared/encoder_decoder/common/infrared_common_decoder.c +++ b/lib/infrared/encoder_decoder/common/infrared_common_decoder.c @@ -1,11 +1,8 @@ +#include "infrared_common_i.h" + +#include #include #include -#include "infrared.h" -#include "infrared_common_i.h" -#include -#include -#include "infrared_i.h" -#include static void infrared_common_decoder_reset_state(InfraredCommonDecoder* decoder); @@ -85,8 +82,8 @@ static InfraredStatus infrared_common_decode_bits(InfraredCommonDecoder* decoder if(timings->min_split_time && !level) { if(timing > timings->min_split_time) { /* long low timing - check if we're ready for any of protocol modification */ - for(size_t i = 0; decoder->protocol->databit_len[i] && - (i < COUNT_OF(decoder->protocol->databit_len)); + for(size_t i = 0; i < COUNT_OF(decoder->protocol->databit_len) && + decoder->protocol->databit_len[i]; ++i) { if(decoder->protocol->databit_len[i] == decoder->databit_cnt) { return InfraredStatusReady; @@ -107,7 +104,7 @@ static InfraredStatus infrared_common_decode_bits(InfraredCommonDecoder* decoder decoder->timings_cnt = consume_samples(decoder->timings, decoder->timings_cnt, 1); /* check if largest protocol version can be decoded */ - if(level && (decoder->protocol->databit_len[0] == decoder->databit_cnt) && + if(level && (decoder->protocol->databit_len[0] == decoder->databit_cnt) && //-V1051 !timings->min_split_time) { status = InfraredStatusReady; break; @@ -199,7 +196,7 @@ InfraredMessage* infrared_common_decoder_check_ready(InfraredCommonDecoder* deco bool found_length = false; for(size_t i = 0; - decoder->protocol->databit_len[i] && (i < COUNT_OF(decoder->protocol->databit_len)); + i < COUNT_OF(decoder->protocol->databit_len) && decoder->protocol->databit_len[i]; ++i) { if(decoder->protocol->databit_len[i] == decoder->databit_cnt) { found_length = true; diff --git a/lib/infrared/encoder_decoder/common/infrared_common_encoder.c b/lib/infrared/encoder_decoder/common/infrared_common_encoder.c index dcf63acc858..f145a585a95 100644 --- a/lib/infrared/encoder_decoder/common/infrared_common_encoder.c +++ b/lib/infrared/encoder_decoder/common/infrared_common_encoder.c @@ -1,10 +1,9 @@ -#include -#include "infrared.h" #include "infrared_common_i.h" -#include -#include -#include "infrared_i.h" -#include + +#include +#include +#include +#include static InfraredStatus infrared_common_encode_bits(InfraredCommonEncoder* encoder, uint32_t* duration, bool* level) { @@ -94,7 +93,6 @@ InfraredStatus case InfraredCommonEncoderStateSilence: *duration = encoder->protocol->timings.silence_time; *level = false; - status = InfraredStatusOk; encoder->state = InfraredCommonEncoderStatePreamble; ++encoder->timings_encoded; encoder->timings_sum = 0; diff --git a/lib/infrared/encoder_decoder/common/infrared_common_i.h b/lib/infrared/encoder_decoder/common/infrared_common_i.h index 20dbeb79dc5..f34e81edaef 100644 --- a/lib/infrared/encoder_decoder/common/infrared_common_i.h +++ b/lib/infrared/encoder_decoder/common/infrared_common_i.h @@ -4,7 +4,7 @@ #include "infrared.h" #include "infrared_i.h" -#define MATCH_TIMING(x, v, delta) (((x) < (v + delta)) && ((x) > (v - delta))) +#define MATCH_TIMING(x, v, delta) (((x) < ((v) + (delta))) && ((x) > ((v) - (delta)))) typedef struct InfraredCommonDecoder InfraredCommonDecoder; typedef struct InfraredCommonEncoder InfraredCommonEncoder; diff --git a/lib/infrared/encoder_decoder/common/infrared_common_protocol_defs.c b/lib/infrared/encoder_decoder/common/infrared_common_protocol_defs.c deleted file mode 100644 index e8f7664a7cd..00000000000 --- a/lib/infrared/encoder_decoder/common/infrared_common_protocol_defs.c +++ /dev/null @@ -1,117 +0,0 @@ -#include "infrared_common_i.h" -#include "infrared_protocol_defs_i.h" - -const InfraredCommonProtocolSpec protocol_nec = { - .timings = - { - .preamble_mark = INFRARED_NEC_PREAMBLE_MARK, - .preamble_space = INFRARED_NEC_PREAMBLE_SPACE, - .bit1_mark = INFRARED_NEC_BIT1_MARK, - .bit1_space = INFRARED_NEC_BIT1_SPACE, - .bit0_mark = INFRARED_NEC_BIT0_MARK, - .bit0_space = INFRARED_NEC_BIT0_SPACE, - .preamble_tolerance = INFRARED_NEC_PREAMBLE_TOLERANCE, - .bit_tolerance = INFRARED_NEC_BIT_TOLERANCE, - .silence_time = INFRARED_NEC_SILENCE, - .min_split_time = INFRARED_NEC_MIN_SPLIT_TIME, - }, - .databit_len[0] = 42, - .databit_len[1] = 32, - .no_stop_bit = false, - .decode = infrared_common_decode_pdwm, - .encode = infrared_common_encode_pdwm, - .interpret = infrared_decoder_nec_interpret, - .decode_repeat = infrared_decoder_nec_decode_repeat, - .encode_repeat = infrared_encoder_nec_encode_repeat, -}; - -const InfraredCommonProtocolSpec protocol_samsung32 = { - .timings = - { - .preamble_mark = INFRARED_SAMSUNG_PREAMBLE_MARK, - .preamble_space = INFRARED_SAMSUNG_PREAMBLE_SPACE, - .bit1_mark = INFRARED_SAMSUNG_BIT1_MARK, - .bit1_space = INFRARED_SAMSUNG_BIT1_SPACE, - .bit0_mark = INFRARED_SAMSUNG_BIT0_MARK, - .bit0_space = INFRARED_SAMSUNG_BIT0_SPACE, - .preamble_tolerance = INFRARED_SAMSUNG_PREAMBLE_TOLERANCE, - .bit_tolerance = INFRARED_SAMSUNG_BIT_TOLERANCE, - .silence_time = INFRARED_SAMSUNG_SILENCE, - .min_split_time = INFRARED_SAMSUNG_MIN_SPLIT_TIME, - }, - .databit_len[0] = 32, - .no_stop_bit = false, - .decode = infrared_common_decode_pdwm, - .encode = infrared_common_encode_pdwm, - .interpret = infrared_decoder_samsung32_interpret, - .decode_repeat = infrared_decoder_samsung32_decode_repeat, - .encode_repeat = infrared_encoder_samsung32_encode_repeat, -}; - -const InfraredCommonProtocolSpec protocol_rc6 = { - .timings = - { - .preamble_mark = INFRARED_RC6_PREAMBLE_MARK, - .preamble_space = INFRARED_RC6_PREAMBLE_SPACE, - .bit1_mark = INFRARED_RC6_BIT, - .preamble_tolerance = INFRARED_RC6_PREAMBLE_TOLERANCE, - .bit_tolerance = INFRARED_RC6_BIT_TOLERANCE, - .silence_time = INFRARED_RC6_SILENCE, - .min_split_time = INFRARED_RC6_MIN_SPLIT_TIME, - }, - .databit_len[0] = - 1 + 3 + 1 + 8 + - 8, // start_bit + 3 mode bits, + 1 toggle bit (x2 timing) + 8 address + 8 command - .manchester_start_from_space = false, - .decode = infrared_decoder_rc6_decode_manchester, - .encode = infrared_encoder_rc6_encode_manchester, - .interpret = infrared_decoder_rc6_interpret, - .decode_repeat = NULL, - .encode_repeat = NULL, -}; - -const InfraredCommonProtocolSpec protocol_rc5 = { - .timings = - { - .preamble_mark = 0, - .preamble_space = 0, - .bit1_mark = INFRARED_RC5_BIT, - .preamble_tolerance = 0, - .bit_tolerance = INFRARED_RC5_BIT_TOLERANCE, - .silence_time = INFRARED_RC5_SILENCE, - .min_split_time = INFRARED_RC5_MIN_SPLIT_TIME, - }, - .databit_len[0] = 1 + 1 + 1 + 5 + - 6, // start_bit + start_bit/command_bit + toggle_bit + 5 address + 6 command - .manchester_start_from_space = true, - .decode = infrared_common_decode_manchester, - .encode = infrared_common_encode_manchester, - .interpret = infrared_decoder_rc5_interpret, - .decode_repeat = NULL, - .encode_repeat = NULL, -}; - -const InfraredCommonProtocolSpec protocol_sirc = { - .timings = - { - .preamble_mark = INFRARED_SIRC_PREAMBLE_MARK, - .preamble_space = INFRARED_SIRC_PREAMBLE_SPACE, - .bit1_mark = INFRARED_SIRC_BIT1_MARK, - .bit1_space = INFRARED_SIRC_BIT1_SPACE, - .bit0_mark = INFRARED_SIRC_BIT0_MARK, - .bit0_space = INFRARED_SIRC_BIT0_SPACE, - .preamble_tolerance = INFRARED_SIRC_PREAMBLE_TOLERANCE, - .bit_tolerance = INFRARED_SIRC_BIT_TOLERANCE, - .silence_time = INFRARED_SIRC_SILENCE, - .min_split_time = INFRARED_SIRC_MIN_SPLIT_TIME, - }, - .databit_len[0] = 20, - .databit_len[1] = 15, - .databit_len[2] = 12, - .no_stop_bit = true, - .decode = infrared_common_decode_pdwm, - .encode = infrared_common_encode_pdwm, - .interpret = infrared_decoder_sirc_interpret, - .decode_repeat = NULL, - .encode_repeat = infrared_encoder_sirc_encode_repeat, -}; diff --git a/lib/infrared/encoder_decoder/infrared.c b/lib/infrared/encoder_decoder/infrared.c index 71bd6bb6d17..56f2c3f9ee6 100644 --- a/lib/infrared/encoder_decoder/infrared.c +++ b/lib/infrared/encoder_decoder/infrared.c @@ -1,13 +1,17 @@ #include "infrared.h" -#include -#include "common/infrared_common_i.h" -#include "infrared_protocol_defs_i.h" -#include -#include + #include -#include -#include "infrared_i.h" -#include +#include +#include +#include + +#include "nec/infrared_protocol_nec.h" +#include "samsung/infrared_protocol_samsung.h" +#include "rc5/infrared_protocol_rc5.h" +#include "rc6/infrared_protocol_rc6.h" +#include "sirc/infrared_protocol_sirc.h" +#include "kaseikyo/infrared_protocol_kaseikyo.h" +#include "rca/infrared_protocol_rca.h" typedef struct { InfraredAlloc alloc; @@ -36,7 +40,7 @@ struct InfraredEncoderHandler { typedef struct { InfraredEncoders encoder; InfraredDecoders decoder; - InfraredGetProtocolSpec get_protocol_spec; + InfraredGetProtocolVariant get_protocol_variant; } InfraredEncoderDecoder; static const InfraredEncoderDecoder infrared_encoder_decoder[] = { @@ -52,7 +56,7 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { .encode = infrared_encoder_nec_encode, .reset = infrared_encoder_nec_reset, .free = infrared_encoder_nec_free}, - .get_protocol_spec = infrared_nec_get_spec, + .get_protocol_variant = infrared_protocol_nec_get_variant, }, { .decoder = @@ -66,7 +70,7 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { .encode = infrared_encoder_samsung32_encode, .reset = infrared_encoder_samsung32_reset, .free = infrared_encoder_samsung32_free}, - .get_protocol_spec = infrared_samsung32_get_spec, + .get_protocol_variant = infrared_protocol_samsung32_get_variant, }, { .decoder = @@ -80,7 +84,7 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { .encode = infrared_encoder_rc5_encode, .reset = infrared_encoder_rc5_reset, .free = infrared_encoder_rc5_free}, - .get_protocol_spec = infrared_rc5_get_spec, + .get_protocol_variant = infrared_protocol_rc5_get_variant, }, { .decoder = @@ -94,7 +98,7 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { .encode = infrared_encoder_rc6_encode, .reset = infrared_encoder_rc6_reset, .free = infrared_encoder_rc6_free}, - .get_protocol_spec = infrared_rc6_get_spec, + .get_protocol_variant = infrared_protocol_rc6_get_variant, }, { .decoder = @@ -108,13 +112,40 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { .encode = infrared_encoder_sirc_encode, .reset = infrared_encoder_sirc_reset, .free = infrared_encoder_sirc_free}, - .get_protocol_spec = infrared_sirc_get_spec, + .get_protocol_variant = infrared_protocol_sirc_get_variant, + }, + { + .decoder = + {.alloc = infrared_decoder_kaseikyo_alloc, + .decode = infrared_decoder_kaseikyo_decode, + .reset = infrared_decoder_kaseikyo_reset, + .check_ready = infrared_decoder_kaseikyo_check_ready, + .free = infrared_decoder_kaseikyo_free}, + .encoder = + {.alloc = infrared_encoder_kaseikyo_alloc, + .encode = infrared_encoder_kaseikyo_encode, + .reset = infrared_encoder_kaseikyo_reset, + .free = infrared_encoder_kaseikyo_free}, + .get_protocol_variant = infrared_protocol_kaseikyo_get_variant, + }, + { + .decoder = + {.alloc = infrared_decoder_rca_alloc, + .decode = infrared_decoder_rca_decode, + .reset = infrared_decoder_rca_reset, + .check_ready = infrared_decoder_rca_check_ready, + .free = infrared_decoder_rca_free}, + .encoder = + {.alloc = infrared_encoder_rca_alloc, + .encode = infrared_encoder_rca_encode, + .reset = infrared_encoder_rca_reset, + .free = infrared_encoder_rca_free}, + .get_protocol_variant = infrared_protocol_rca_get_variant, }, }; static int infrared_find_index_by_protocol(InfraredProtocol protocol); -static const InfraredProtocolSpecification* - infrared_get_spec_by_protocol(InfraredProtocol protocol); +static const InfraredProtocolVariant* infrared_get_variant_by_protocol(InfraredProtocol protocol); const InfraredMessage* infrared_decode(InfraredDecoderHandler* handler, bool level, uint32_t duration) { @@ -210,7 +241,7 @@ void infrared_free_encoder(InfraredEncoderHandler* handler) { static int infrared_find_index_by_protocol(InfraredProtocol protocol) { for(size_t i = 0; i < COUNT_OF(infrared_encoder_decoder); ++i) { - if(infrared_encoder_decoder[i].get_protocol_spec(protocol)) { + if(infrared_encoder_decoder[i].get_protocol_variant(protocol)) { return i; } } @@ -268,34 +299,37 @@ InfraredProtocol infrared_get_protocol_by_name(const char* protocol_name) { return InfraredProtocolUnknown; } -static const InfraredProtocolSpecification* - infrared_get_spec_by_protocol(InfraredProtocol protocol) { +static const InfraredProtocolVariant* infrared_get_variant_by_protocol(InfraredProtocol protocol) { int index = infrared_find_index_by_protocol(protocol); - const InfraredProtocolSpecification* spec = NULL; + const InfraredProtocolVariant* variant = NULL; if(index >= 0) { - spec = infrared_encoder_decoder[index].get_protocol_spec(protocol); + variant = infrared_encoder_decoder[index].get_protocol_variant(protocol); } - furi_assert(spec); - return spec; + furi_assert(variant); + return variant; } const char* infrared_get_protocol_name(InfraredProtocol protocol) { - return infrared_get_spec_by_protocol(protocol)->name; + return infrared_get_variant_by_protocol(protocol)->name; } uint8_t infrared_get_protocol_address_length(InfraredProtocol protocol) { - return infrared_get_spec_by_protocol(protocol)->address_length; + return infrared_get_variant_by_protocol(protocol)->address_length; } uint8_t infrared_get_protocol_command_length(InfraredProtocol protocol) { - return infrared_get_spec_by_protocol(protocol)->command_length; + return infrared_get_variant_by_protocol(protocol)->command_length; } uint32_t infrared_get_protocol_frequency(InfraredProtocol protocol) { - return infrared_get_spec_by_protocol(protocol)->frequency; + return infrared_get_variant_by_protocol(protocol)->frequency; } float infrared_get_protocol_duty_cycle(InfraredProtocol protocol) { - return infrared_get_spec_by_protocol(protocol)->duty_cycle; + return infrared_get_variant_by_protocol(protocol)->duty_cycle; +} + +size_t infrared_get_protocol_min_repeat_count(InfraredProtocol protocol) { + return infrared_get_variant_by_protocol(protocol)->repeat_count; } diff --git a/lib/infrared/encoder_decoder/infrared.h b/lib/infrared/encoder_decoder/infrared.h index 945913000be..ada449b9831 100644 --- a/lib/infrared/encoder_decoder/infrared.h +++ b/lib/infrared/encoder_decoder/infrared.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #ifdef __cplusplus @@ -10,7 +11,7 @@ extern "C" { #define INFRARED_COMMON_CARRIER_FREQUENCY ((uint32_t)38000) #define INFRARED_COMMON_DUTY_CYCLE ((float)0.33) -/* if we want to see splitted raw signals during brutforce, +/* if we want to see split raw signals during bruteforce, * we have to have RX raw timing delay less than TX */ #define INFRARED_RAW_RX_TIMING_DELAY_US 150000 #define INFRARED_RAW_TX_TIMING_DELAY_US 180000 @@ -31,6 +32,8 @@ typedef enum { InfraredProtocolSIRC, InfraredProtocolSIRC15, InfraredProtocolSIRC20, + InfraredProtocolKaseikyo, + InfraredProtocolRCA, InfraredProtocolMAX, } InfraredProtocol; @@ -200,6 +203,15 @@ uint32_t infrared_get_protocol_frequency(InfraredProtocol protocol); */ float infrared_get_protocol_duty_cycle(InfraredProtocol protocol); +/** + * Get the minimum count of signal repeats for the selected protocol + * + * \param[in] protocol - protocol to get the repeat count from + * + * \return repeat count + */ +size_t infrared_get_protocol_min_repeat_count(InfraredProtocol protocol); + #ifdef __cplusplus } #endif diff --git a/lib/infrared/encoder_decoder/infrared_i.h b/lib/infrared/encoder_decoder/infrared_i.h index 3a645cede82..3efc41cd67a 100644 --- a/lib/infrared/encoder_decoder/infrared_i.h +++ b/lib/infrared/encoder_decoder/infrared_i.h @@ -22,9 +22,10 @@ typedef struct { uint8_t command_length; uint32_t frequency; float duty_cycle; -} InfraredProtocolSpecification; + size_t repeat_count; +} InfraredProtocolVariant; -typedef const InfraredProtocolSpecification* (*InfraredGetProtocolSpec)(InfraredProtocol protocol); +typedef const InfraredProtocolVariant* (*InfraredGetProtocolVariant)(InfraredProtocol protocol); typedef void* (*InfraredAlloc)(void); typedef void (*InfraredFree)(void*); diff --git a/lib/infrared/encoder_decoder/infrared_protocol_defs_i.h b/lib/infrared/encoder_decoder/infrared_protocol_defs_i.h deleted file mode 100644 index 575961f1357..00000000000 --- a/lib/infrared/encoder_decoder/infrared_protocol_defs_i.h +++ /dev/null @@ -1,269 +0,0 @@ -#pragma once - -#include -#include -#include -#include "infrared.h" -#include "common/infrared_common_i.h" - -/*************************************************************************************************** -* NEC protocol description -* https://radioparty.ru/manuals/encyclopedia/213-ircontrol?start=1 -**************************************************************************************************** -* Preamble Preamble Pulse Distance/Width Pause Preamble Preamble Stop -* mark space Modulation up to period repeat repeat bit -* mark space -* -* 9000 4500 32 bit + stop bit ...110000 9000 2250 -* __________ _ _ _ _ _ _ _ _ _ _ _ _ _ ___________ _ -* ____ __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________ ____________ ___ -* -***************************************************************************************************/ - -#define INFRARED_NEC_PREAMBLE_MARK 9000 -#define INFRARED_NEC_PREAMBLE_SPACE 4500 -#define INFRARED_NEC_BIT1_MARK 560 -#define INFRARED_NEC_BIT1_SPACE 1690 -#define INFRARED_NEC_BIT0_MARK 560 -#define INFRARED_NEC_BIT0_SPACE 560 -#define INFRARED_NEC_REPEAT_PERIOD 110000 -#define INFRARED_NEC_SILENCE INFRARED_NEC_REPEAT_PERIOD -#define INFRARED_NEC_MIN_SPLIT_TIME INFRARED_NEC_REPEAT_PAUSE_MIN -#define INFRARED_NEC_REPEAT_PAUSE_MIN 4000 -#define INFRARED_NEC_REPEAT_PAUSE_MAX 150000 -#define INFRARED_NEC_REPEAT_MARK 9000 -#define INFRARED_NEC_REPEAT_SPACE 2250 -#define INFRARED_NEC_PREAMBLE_TOLERANCE 200 // us -#define INFRARED_NEC_BIT_TOLERANCE 120 // us - -void* infrared_decoder_nec_alloc(void); -void infrared_decoder_nec_reset(void* decoder); -void infrared_decoder_nec_free(void* decoder); -InfraredMessage* infrared_decoder_nec_check_ready(void* decoder); -InfraredMessage* infrared_decoder_nec_decode(void* decoder, bool level, uint32_t duration); -void* infrared_encoder_nec_alloc(void); -InfraredStatus infrared_encoder_nec_encode(void* encoder_ptr, uint32_t* duration, bool* level); -void infrared_encoder_nec_reset(void* encoder_ptr, const InfraredMessage* message); -void infrared_encoder_nec_free(void* encoder_ptr); -bool infrared_decoder_nec_interpret(InfraredCommonDecoder* decoder); -InfraredStatus infrared_decoder_nec_decode_repeat(InfraredCommonDecoder* decoder); -InfraredStatus infrared_encoder_nec_encode_repeat( - InfraredCommonEncoder* encoder, - uint32_t* duration, - bool* level); -const InfraredProtocolSpecification* infrared_nec_get_spec(InfraredProtocol protocol); - -extern const InfraredCommonProtocolSpec protocol_nec; - -/*************************************************************************************************** -* SAMSUNG32 protocol description -* https://www.mikrocontroller.net/articles/IRMP_-_english#SAMSUNG -**************************************************************************************************** -* Preamble Preamble Pulse Distance/Width Pause Preamble Preamble Bit1 Stop -* mark space Modulation repeat repeat bit -* mark space -* -* 4500 4500 32 bit + stop bit 40000/100000 4500 4500 -* __________ _ _ _ _ _ _ _ _ _ _ _ ___________ _ _ -* _ __________ __ _ __ __ __ _ _ __ __ _ ________________ ____________ ____ ___ -* -***************************************************************************************************/ - -#define INFRARED_SAMSUNG_PREAMBLE_MARK 4500 -#define INFRARED_SAMSUNG_PREAMBLE_SPACE 4500 -#define INFRARED_SAMSUNG_BIT1_MARK 550 -#define INFRARED_SAMSUNG_BIT1_SPACE 1650 -#define INFRARED_SAMSUNG_BIT0_MARK 550 -#define INFRARED_SAMSUNG_BIT0_SPACE 550 -#define INFRARED_SAMSUNG_REPEAT_PAUSE_MIN 30000 -#define INFRARED_SAMSUNG_REPEAT_PAUSE1 46000 -#define INFRARED_SAMSUNG_REPEAT_PAUSE2 97000 -/* Samsung silence have to be greater than REPEAT MAX - * otherwise there can be problems during unit tests parsing - * of some data. Real tolerances we don't know, but in real life - * silence time should be greater than max repeat time. This is - * because of similar preambule timings for repeat and first messages. */ -#define INFRARED_SAMSUNG_MIN_SPLIT_TIME 5000 -#define INFRARED_SAMSUNG_SILENCE 145000 -#define INFRARED_SAMSUNG_REPEAT_PAUSE_MAX 140000 -#define INFRARED_SAMSUNG_REPEAT_MARK 4500 -#define INFRARED_SAMSUNG_REPEAT_SPACE 4500 -#define INFRARED_SAMSUNG_PREAMBLE_TOLERANCE 200 // us -#define INFRARED_SAMSUNG_BIT_TOLERANCE 120 // us - -void* infrared_decoder_samsung32_alloc(void); -void infrared_decoder_samsung32_reset(void* decoder); -void infrared_decoder_samsung32_free(void* decoder); -InfraredMessage* infrared_decoder_samsung32_check_ready(void* ctx); -InfraredMessage* infrared_decoder_samsung32_decode(void* decoder, bool level, uint32_t duration); -InfraredStatus - infrared_encoder_samsung32_encode(void* encoder_ptr, uint32_t* duration, bool* level); -void infrared_encoder_samsung32_reset(void* encoder_ptr, const InfraredMessage* message); -void* infrared_encoder_samsung32_alloc(void); -void infrared_encoder_samsung32_free(void* encoder_ptr); -bool infrared_decoder_samsung32_interpret(InfraredCommonDecoder* decoder); -InfraredStatus infrared_decoder_samsung32_decode_repeat(InfraredCommonDecoder* decoder); -InfraredStatus infrared_encoder_samsung32_encode_repeat( - InfraredCommonEncoder* encoder, - uint32_t* duration, - bool* level); -const InfraredProtocolSpecification* infrared_samsung32_get_spec(InfraredProtocol protocol); - -extern const InfraredCommonProtocolSpec protocol_samsung32; - -/*************************************************************************************************** -* RC6 protocol description -* https://www.mikrocontroller.net/articles/IRMP_-_english#RC6_.2B_RC6A -**************************************************************************************************** -* Preamble Manchester/biphase Silence -* mark/space Modulation -* -* 2666 889 444/888 - bit (x2 for toggle bit) 2666 -* -* ________ __ __ __ __ ____ __ __ __ __ __ __ __ __ -* _ _________ ____ __ __ ____ __ __ __ __ __ __ __ __ _______________ -* | 1 | 0 | 0 | 0 | 0 | ... | ... | | -* s m2 m1 m0 T address (MSB) command (MSB) -* -* s - start bit (always 1) -* m0-2 - mode (000 for RC6) -* T - toggle bit, twice longer -* address - 8 bit -* command - 8 bit -***************************************************************************************************/ - -#define INFRARED_RC6_CARRIER_FREQUENCY 36000 -#define INFRARED_RC6_DUTY_CYCLE 0.33 - -#define INFRARED_RC6_PREAMBLE_MARK 2666 -#define INFRARED_RC6_PREAMBLE_SPACE 889 -#define INFRARED_RC6_BIT 444 // half of time-quant for 1 bit -#define INFRARED_RC6_PREAMBLE_TOLERANCE 200 // us -#define INFRARED_RC6_BIT_TOLERANCE 120 // us -/* protocol allows 2700 silence, but it is hard to send 1 message without repeat */ -#define INFRARED_RC6_SILENCE (2700 * 10) -#define INFRARED_RC6_MIN_SPLIT_TIME 2700 - -void* infrared_decoder_rc6_alloc(void); -void infrared_decoder_rc6_reset(void* decoder); -void infrared_decoder_rc6_free(void* decoder); -InfraredMessage* infrared_decoder_rc6_check_ready(void* ctx); -InfraredMessage* infrared_decoder_rc6_decode(void* decoder, bool level, uint32_t duration); -void* infrared_encoder_rc6_alloc(void); -void infrared_encoder_rc6_reset(void* encoder_ptr, const InfraredMessage* message); -void infrared_encoder_rc6_free(void* decoder); -InfraredStatus infrared_encoder_rc6_encode(void* encoder_ptr, uint32_t* duration, bool* polarity); -bool infrared_decoder_rc6_interpret(InfraredCommonDecoder* decoder); -InfraredStatus infrared_decoder_rc6_decode_manchester( - InfraredCommonDecoder* decoder, - bool level, - uint32_t timing); -InfraredStatus infrared_encoder_rc6_encode_manchester( - InfraredCommonEncoder* encoder_ptr, - uint32_t* duration, - bool* polarity); -const InfraredProtocolSpecification* infrared_rc6_get_spec(InfraredProtocol protocol); - -extern const InfraredCommonProtocolSpec protocol_rc6; - -/*************************************************************************************************** -* RC5 protocol description -* https://www.mikrocontroller.net/articles/IRMP_-_english#RC5_.2B_RC5X -**************************************************************************************************** -* Manchester/biphase -* Modulation -* -* 888/1776 - bit (x2 for toggle bit) -* -* __ ____ __ __ __ __ __ __ __ __ -* __ __ ____ __ __ __ __ __ __ __ _ -* | 1 | 1 | 0 | ... | ... | -* s si T address (MSB) command (MSB) -* -* Note: manchester starts from space timing, so it have to be handled properly -* s - start bit (always 1) -* si - RC5: start bit (always 1), RC5X - 7-th bit of address (in our case always 0) -* T - toggle bit, change it's value every button press -* address - 5 bit -* command - 6/7 bit -***************************************************************************************************/ - -#define INFRARED_RC5_CARRIER_FREQUENCY 36000 -#define INFRARED_RC5_DUTY_CYCLE 0.33 - -#define INFRARED_RC5_PREAMBLE_MARK 0 -#define INFRARED_RC5_PREAMBLE_SPACE 0 -#define INFRARED_RC5_BIT 888 // half of time-quant for 1 bit -#define INFRARED_RC5_PREAMBLE_TOLERANCE 200 // us -#define INFRARED_RC5_BIT_TOLERANCE 120 // us -/* protocol allows 2700 silence, but it is hard to send 1 message without repeat */ -#define INFRARED_RC5_SILENCE (2700 * 10) -#define INFRARED_RC5_MIN_SPLIT_TIME 2700 - -void* infrared_decoder_rc5_alloc(void); -void infrared_decoder_rc5_reset(void* decoder); -void infrared_decoder_rc5_free(void* decoder); -InfraredMessage* infrared_decoder_rc5_check_ready(void* ctx); -InfraredMessage* infrared_decoder_rc5_decode(void* decoder, bool level, uint32_t duration); -void* infrared_encoder_rc5_alloc(void); -void infrared_encoder_rc5_reset(void* encoder_ptr, const InfraredMessage* message); -void infrared_encoder_rc5_free(void* decoder); -InfraredStatus infrared_encoder_rc5_encode(void* encoder_ptr, uint32_t* duration, bool* polarity); -bool infrared_decoder_rc5_interpret(InfraredCommonDecoder* decoder); -const InfraredProtocolSpecification* infrared_rc5_get_spec(InfraredProtocol protocol); - -extern const InfraredCommonProtocolSpec protocol_rc5; - -/*************************************************************************************************** -* Sony SIRC protocol description -* https://www.sbprojects.net/knowledge/ir/sirc.php -* http://picprojects.org.uk/ -**************************************************************************************************** -* Preamble Preamble Pulse Width Modulation Pause Entirely repeat -* mark space up to period message.. -* -* 2400 600 12/15/20 bits (600,1200) ...45000 2400 600 -* __________ _ _ _ _ _ _ _ _ _ _ _ _ _ __________ _ _ -* ____ __________ _ _ _ __ __ __ _ _ __ __ _ _ ____________________ __________ _ -* | command | address | -* SIRC | 7b LSB | 5b LSB | -* SIRC15 | 7b LSB | 8b LSB | -* SIRC20 | 7b LSB | 13b LSB | -* -* No way to determine either next message is repeat or not, -* so recognize only fact message received. Sony remotes always send at least 3 messages. -* Assume 8 last extended bits for SIRC20 are address bits. -***************************************************************************************************/ - -#define INFRARED_SIRC_CARRIER_FREQUENCY 40000 -#define INFRARED_SIRC_DUTY_CYCLE 0.33 -#define INFRARED_SIRC_PREAMBLE_MARK 2400 -#define INFRARED_SIRC_PREAMBLE_SPACE 600 -#define INFRARED_SIRC_BIT1_MARK 1200 -#define INFRARED_SIRC_BIT1_SPACE 600 -#define INFRARED_SIRC_BIT0_MARK 600 -#define INFRARED_SIRC_BIT0_SPACE 600 -#define INFRARED_SIRC_PREAMBLE_TOLERANCE 200 // us -#define INFRARED_SIRC_BIT_TOLERANCE 120 // us -#define INFRARED_SIRC_SILENCE 10000 -#define INFRARED_SIRC_MIN_SPLIT_TIME (INFRARED_SIRC_SILENCE - 1000) -#define INFRARED_SIRC_REPEAT_PERIOD 45000 - -void* infrared_decoder_sirc_alloc(void); -void infrared_decoder_sirc_reset(void* decoder); -InfraredMessage* infrared_decoder_sirc_check_ready(void* decoder); -uint32_t infrared_decoder_sirc_get_timeout(void* decoder); -void infrared_decoder_sirc_free(void* decoder); -InfraredMessage* infrared_decoder_sirc_decode(void* decoder, bool level, uint32_t duration); -void* infrared_encoder_sirc_alloc(void); -void infrared_encoder_sirc_reset(void* encoder_ptr, const InfraredMessage* message); -void infrared_encoder_sirc_free(void* decoder); -InfraredStatus infrared_encoder_sirc_encode(void* encoder_ptr, uint32_t* duration, bool* polarity); -bool infrared_decoder_sirc_interpret(InfraredCommonDecoder* decoder); -const InfraredProtocolSpecification* infrared_sirc_get_spec(InfraredProtocol protocol); -InfraredStatus infrared_encoder_sirc_encode_repeat( - InfraredCommonEncoder* encoder, - uint32_t* duration, - bool* level); - -extern const InfraredCommonProtocolSpec protocol_sirc; diff --git a/lib/infrared/encoder_decoder/kaseikyo/infrared_decoder_kaseikyo.c b/lib/infrared/encoder_decoder/kaseikyo/infrared_decoder_kaseikyo.c new file mode 100644 index 00000000000..e85a8965259 --- /dev/null +++ b/lib/infrared/encoder_decoder/kaseikyo/infrared_decoder_kaseikyo.c @@ -0,0 +1,50 @@ +#include "infrared_protocol_kaseikyo_i.h" +#include + +InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* ctx) { + return infrared_common_decoder_check_ready(ctx); +} + +bool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder) { + furi_assert(decoder); + + bool result = false; + uint16_t vendor_id = ((uint16_t)(decoder->data[1]) << 8) | (uint16_t)decoder->data[0]; + uint8_t vendor_parity = decoder->data[2] & 0x0f; + uint8_t genre1 = decoder->data[2] >> 4; + uint8_t genre2 = decoder->data[3] & 0x0f; + uint16_t data = (uint16_t)(decoder->data[3] >> 4) | ((uint16_t)(decoder->data[4] & 0x3f) << 4); + uint8_t id = decoder->data[4] >> 6; + uint8_t parity = decoder->data[5]; + + uint8_t vendor_parity_check = decoder->data[0] ^ decoder->data[1]; + vendor_parity_check = (vendor_parity_check & 0xf) ^ (vendor_parity_check >> 4); + uint8_t parity_check = decoder->data[2] ^ decoder->data[3] ^ decoder->data[4]; + + if(vendor_parity == vendor_parity_check && parity == parity_check) { + decoder->message.command = (uint32_t)data; + decoder->message.address = ((uint32_t)id << 24) | ((uint32_t)vendor_id << 8) | + ((uint32_t)genre1 << 4) | (uint32_t)genre2; + decoder->message.protocol = InfraredProtocolKaseikyo; + decoder->message.repeat = false; + result = true; + } + + return result; +} + +void* infrared_decoder_kaseikyo_alloc(void) { + return infrared_common_decoder_alloc(&infrared_protocol_kaseikyo); +} + +InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration) { + return infrared_common_decode(decoder, level, duration); +} + +void infrared_decoder_kaseikyo_free(void* decoder) { + infrared_common_decoder_free(decoder); +} + +void infrared_decoder_kaseikyo_reset(void* decoder) { + infrared_common_decoder_reset(decoder); +} diff --git a/lib/infrared/encoder_decoder/kaseikyo/infrared_encoder_kaseikyo.c b/lib/infrared/encoder_decoder/kaseikyo/infrared_encoder_kaseikyo.c new file mode 100644 index 00000000000..618fc3babdc --- /dev/null +++ b/lib/infrared/encoder_decoder/kaseikyo/infrared_encoder_kaseikyo.c @@ -0,0 +1,41 @@ +#include "infrared_protocol_kaseikyo_i.h" +#include + +void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message) { + furi_assert(encoder_ptr); + + InfraredCommonEncoder* encoder = encoder_ptr; + infrared_common_encoder_reset(encoder); + + uint32_t address = message->address; + uint16_t command = message->command; + + uint8_t id = (address >> 24) & 3; + uint16_t vendor_id = (address >> 8) & 0xffff; + uint8_t genre1 = (address >> 4) & 0xf; + uint8_t genre2 = address & 0xf; + + encoder->data[0] = (uint8_t)(vendor_id & 0xff); + encoder->data[1] = (uint8_t)(vendor_id >> 8); + uint8_t vendor_parity = encoder->data[0] ^ encoder->data[1]; + vendor_parity = (vendor_parity & 0xf) ^ (vendor_parity >> 4); + encoder->data[2] = (vendor_parity & 0xf) | (genre1 << 4); + encoder->data[3] = (genre2 & 0xf) | ((uint8_t)(command & 0xf) << 4); + encoder->data[4] = (id << 6) | (uint8_t)(command >> 4); + encoder->data[5] = encoder->data[2] ^ encoder->data[3] ^ encoder->data[4]; + + encoder->bits_to_encode = encoder->protocol->databit_len[0]; +} + +void* infrared_encoder_kaseikyo_alloc(void) { + return infrared_common_encoder_alloc(&infrared_protocol_kaseikyo); +} + +void infrared_encoder_kaseikyo_free(void* encoder_ptr) { + infrared_common_encoder_free(encoder_ptr); +} + +InfraredStatus + infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level) { + return infrared_common_encode(encoder_ptr, duration, level); +} diff --git a/lib/infrared/encoder_decoder/kaseikyo/infrared_protocol_kaseikyo.c b/lib/infrared/encoder_decoder/kaseikyo/infrared_protocol_kaseikyo.c new file mode 100644 index 00000000000..0c61be3bf8c --- /dev/null +++ b/lib/infrared/encoder_decoder/kaseikyo/infrared_protocol_kaseikyo.c @@ -0,0 +1,40 @@ +#include "infrared_protocol_kaseikyo_i.h" + +const InfraredCommonProtocolSpec infrared_protocol_kaseikyo = { + .timings = + { + .preamble_mark = INFRARED_KASEIKYO_PREAMBLE_MARK, + .preamble_space = INFRARED_KASEIKYO_PREAMBLE_SPACE, + .bit1_mark = INFRARED_KASEIKYO_BIT1_MARK, + .bit1_space = INFRARED_KASEIKYO_BIT1_SPACE, + .bit0_mark = INFRARED_KASEIKYO_BIT0_MARK, + .bit0_space = INFRARED_KASEIKYO_BIT0_SPACE, + .preamble_tolerance = INFRARED_KASEIKYO_PREAMBLE_TOLERANCE, + .bit_tolerance = INFRARED_KASEIKYO_BIT_TOLERANCE, + .silence_time = INFRARED_KASEIKYO_SILENCE, + .min_split_time = INFRARED_KASEIKYO_MIN_SPLIT_TIME, + }, + .databit_len[0] = 48, + .no_stop_bit = false, + .decode = infrared_common_decode_pdwm, + .encode = infrared_common_encode_pdwm, + .interpret = infrared_decoder_kaseikyo_interpret, + .decode_repeat = NULL, + .encode_repeat = NULL, +}; + +static const InfraredProtocolVariant infrared_protocol_variant_kaseikyo = { + .name = "Kaseikyo", + .address_length = 26, + .command_length = 10, + .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, + .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, + .repeat_count = INFRARED_KASEIKYO_REPEAT_COUNT_MIN, +}; + +const InfraredProtocolVariant* infrared_protocol_kaseikyo_get_variant(InfraredProtocol protocol) { + if(protocol == InfraredProtocolKaseikyo) + return &infrared_protocol_variant_kaseikyo; + else + return NULL; +} diff --git a/lib/infrared/encoder_decoder/kaseikyo/infrared_protocol_kaseikyo.h b/lib/infrared/encoder_decoder/kaseikyo/infrared_protocol_kaseikyo.h new file mode 100644 index 00000000000..61ff0ca1cf7 --- /dev/null +++ b/lib/infrared/encoder_decoder/kaseikyo/infrared_protocol_kaseikyo.h @@ -0,0 +1,31 @@ +#pragma once + +#include "../infrared_i.h" + +/*************************************************************************************************** +* Kaseikyo protocol description +* https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_Kaseikyo.hpp +**************************************************************************************************** +* Preamble Preamble Pulse Distance/Width Pause Preamble Preamble +* mark space Modulation up to period repeat repeat +* mark space +* +* 3360 1665 48 bit ...130000 3456 1728 +* __________ _ _ _ _ _ _ _ _ _ _ _ _ _ ___________ +* ____ __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________ ___________ +* +***************************************************************************************************/ + +void* infrared_decoder_kaseikyo_alloc(void); +void infrared_decoder_kaseikyo_reset(void* decoder); +void infrared_decoder_kaseikyo_free(void* decoder); +InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* decoder); +InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration); + +void* infrared_encoder_kaseikyo_alloc(void); +InfraredStatus + infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level); +void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message); +void infrared_encoder_kaseikyo_free(void* encoder_ptr); + +const InfraredProtocolVariant* infrared_protocol_kaseikyo_get_variant(InfraredProtocol protocol); diff --git a/lib/infrared/encoder_decoder/kaseikyo/infrared_protocol_kaseikyo_i.h b/lib/infrared/encoder_decoder/kaseikyo/infrared_protocol_kaseikyo_i.h new file mode 100644 index 00000000000..bee116c4d15 --- /dev/null +++ b/lib/infrared/encoder_decoder/kaseikyo/infrared_protocol_kaseikyo_i.h @@ -0,0 +1,30 @@ +#pragma once + +#include "../common/infrared_common_i.h" + +#define INFRARED_KASEIKYO_UNIT 432 +#define INFRARED_KASEIKYO_PREAMBLE_MARK (8 * INFRARED_KASEIKYO_UNIT) +#define INFRARED_KASEIKYO_PREAMBLE_SPACE (4 * INFRARED_KASEIKYO_UNIT) +#define INFRARED_KASEIKYO_BIT1_MARK INFRARED_KASEIKYO_UNIT +#define INFRARED_KASEIKYO_BIT1_SPACE (3 * INFRARED_KASEIKYO_UNIT) +#define INFRARED_KASEIKYO_BIT0_MARK INFRARED_KASEIKYO_UNIT +#define INFRARED_KASEIKYO_BIT0_SPACE INFRARED_KASEIKYO_UNIT +#define INFRARED_KASEIKYO_REPEAT_PERIOD 130000 +#define INFRARED_KASEIKYO_SILENCE INFRARED_KASEIKYO_REPEAT_PERIOD +#define INFRARED_KASEIKYO_MIN_SPLIT_TIME INFRARED_KASEIKYO_REPEAT_PAUSE_MIN +#define INFRARED_KASEIKYO_REPEAT_PAUSE_MIN 4000 +#define INFRARED_KASEIKYO_REPEAT_PAUSE_MAX 150000 +#define INFRARED_KASEIKYO_REPEAT_COUNT_MIN 1 +#define INFRARED_KASEIKYO_REPEAT_MARK INFRARED_KASEIKYO_PREAMBLE_MARK +#define INFRARED_KASEIKYO_REPEAT_SPACE (INFRARED_KASEIKYO_REPEAT_PERIOD - 56000) +#define INFRARED_KASEIKYO_PREAMBLE_TOLERANCE 200 // us +#define INFRARED_KASEIKYO_BIT_TOLERANCE 120 // us + +extern const InfraredCommonProtocolSpec infrared_protocol_kaseikyo; + +bool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder); +InfraredStatus infrared_decoder_kaseikyo_decode_repeat(InfraredCommonDecoder* decoder); +InfraredStatus infrared_encoder_kaseikyo_encode_repeat( + InfraredCommonEncoder* encoder, + uint32_t* duration, + bool* level); diff --git a/lib/infrared/encoder_decoder/nec/infrared_decoder_nec.c b/lib/infrared/encoder_decoder/nec/infrared_decoder_nec.c index 3ad14a7ab1a..91384d702a2 100644 --- a/lib/infrared/encoder_decoder/nec/infrared_decoder_nec.c +++ b/lib/infrared/encoder_decoder/nec/infrared_decoder_nec.c @@ -1,10 +1,5 @@ -#include "common/infrared_common_i.h" -#include "infrared.h" -#include "infrared_protocol_defs_i.h" -#include -#include -#include -#include "../infrared_i.h" +#include "infrared_protocol_nec_i.h" +#include InfraredMessage* infrared_decoder_nec_check_ready(void* ctx) { return infrared_common_decoder_check_ready(ctx); @@ -86,7 +81,7 @@ InfraredStatus infrared_decoder_nec_decode_repeat(InfraredCommonDecoder* decoder } void* infrared_decoder_nec_alloc(void) { - return infrared_common_decoder_alloc(&protocol_nec); + return infrared_common_decoder_alloc(&infrared_protocol_nec); } InfraredMessage* infrared_decoder_nec_decode(void* decoder, bool level, uint32_t duration) { diff --git a/lib/infrared/encoder_decoder/nec/infrared_encoder_nec.c b/lib/infrared/encoder_decoder/nec/infrared_encoder_nec.c index d0039c330b0..47d8c3c6496 100644 --- a/lib/infrared/encoder_decoder/nec/infrared_encoder_nec.c +++ b/lib/infrared/encoder_decoder/nec/infrared_encoder_nec.c @@ -1,10 +1,7 @@ +#include "infrared_protocol_nec_i.h" + +#include #include -#include "infrared.h" -#include "common/infrared_common_i.h" -#include -#include "../infrared_i.h" -#include "infrared_protocol_defs_i.h" -#include static const uint32_t repeat_timings[] = { INFRARED_NEC_REPEAT_PERIOD - INFRARED_NEC_REPEAT_MARK - INFRARED_NEC_REPEAT_SPACE - @@ -51,7 +48,7 @@ void infrared_encoder_nec_reset(void* encoder_ptr, const InfraredMessage* messag *data2 = (message->command & 0xFFC0) >> 6; encoder->bits_to_encode = 42; } else { - furi_assert(0); + furi_crash(); } } @@ -81,7 +78,7 @@ InfraredStatus infrared_encoder_nec_encode_repeat( } void* infrared_encoder_nec_alloc(void) { - return infrared_common_encoder_alloc(&protocol_nec); + return infrared_common_encoder_alloc(&infrared_protocol_nec); } void infrared_encoder_nec_free(void* encoder_ptr) { diff --git a/lib/infrared/encoder_decoder/nec/infrared_nec_spec.c b/lib/infrared/encoder_decoder/nec/infrared_nec_spec.c deleted file mode 100644 index 16cab8b5f50..00000000000 --- a/lib/infrared/encoder_decoder/nec/infrared_nec_spec.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "../infrared_i.h" -#include "infrared_protocol_defs_i.h" - -static const InfraredProtocolSpecification infrared_nec_protocol_specification = { - .name = "NEC", - .address_length = 8, - .command_length = 8, - .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, - .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, -}; - -static const InfraredProtocolSpecification infrared_necext_protocol_specification = { - .name = "NECext", - .address_length = 16, - .command_length = 16, - .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, - .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, -}; - -static const InfraredProtocolSpecification infrared_nec42_protocol_specification = { - .name = "NEC42", - .address_length = 13, - .command_length = 8, - .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, - .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, -}; - -static const InfraredProtocolSpecification infrared_nec42ext_protocol_specification = { - .name = "NEC42ext", - .address_length = 26, - .command_length = 16, - .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, - .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, -}; - -const InfraredProtocolSpecification* infrared_nec_get_spec(InfraredProtocol protocol) { - if(protocol == InfraredProtocolNEC) - return &infrared_nec_protocol_specification; - else if(protocol == InfraredProtocolNECext) - return &infrared_necext_protocol_specification; - else if(protocol == InfraredProtocolNEC42) - return &infrared_nec42_protocol_specification; - else if(protocol == InfraredProtocolNEC42ext) - return &infrared_nec42ext_protocol_specification; - else - return NULL; -} diff --git a/lib/infrared/encoder_decoder/nec/infrared_protocol_nec.c b/lib/infrared/encoder_decoder/nec/infrared_protocol_nec.c new file mode 100644 index 00000000000..3444f78b612 --- /dev/null +++ b/lib/infrared/encoder_decoder/nec/infrared_protocol_nec.c @@ -0,0 +1,74 @@ +#include "infrared_protocol_nec_i.h" + +const InfraredCommonProtocolSpec infrared_protocol_nec = { + .timings = + { + .preamble_mark = INFRARED_NEC_PREAMBLE_MARK, + .preamble_space = INFRARED_NEC_PREAMBLE_SPACE, + .bit1_mark = INFRARED_NEC_BIT1_MARK, + .bit1_space = INFRARED_NEC_BIT1_SPACE, + .bit0_mark = INFRARED_NEC_BIT0_MARK, + .bit0_space = INFRARED_NEC_BIT0_SPACE, + .preamble_tolerance = INFRARED_NEC_PREAMBLE_TOLERANCE, + .bit_tolerance = INFRARED_NEC_BIT_TOLERANCE, + .silence_time = INFRARED_NEC_SILENCE, + .min_split_time = INFRARED_NEC_MIN_SPLIT_TIME, + }, + .databit_len[0] = 42, + .databit_len[1] = 32, + .no_stop_bit = false, + .decode = infrared_common_decode_pdwm, + .encode = infrared_common_encode_pdwm, + .interpret = infrared_decoder_nec_interpret, + .decode_repeat = infrared_decoder_nec_decode_repeat, + .encode_repeat = infrared_encoder_nec_encode_repeat, +}; + +static const InfraredProtocolVariant infrared_protocol_variant_nec = { + .name = "NEC", + .address_length = 8, + .command_length = 8, + .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, + .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, + .repeat_count = INFRARED_NEC_REPEAT_COUNT_MIN, +}; + +static const InfraredProtocolVariant infrared_protocol_variant_necext = { + .name = "NECext", + .address_length = 16, + .command_length = 16, + .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, + .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, + .repeat_count = INFRARED_NEC_REPEAT_COUNT_MIN, +}; + +static const InfraredProtocolVariant infrared_protocol_variant_nec42 = { + .name = "NEC42", + .address_length = 13, + .command_length = 8, + .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, + .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, + .repeat_count = INFRARED_NEC_REPEAT_COUNT_MIN, +}; + +static const InfraredProtocolVariant infrared_protocol_variant_nec42ext = { + .name = "NEC42ext", + .address_length = 26, + .command_length = 16, + .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, + .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, + .repeat_count = INFRARED_NEC_REPEAT_COUNT_MIN, +}; + +const InfraredProtocolVariant* infrared_protocol_nec_get_variant(InfraredProtocol protocol) { + if(protocol == InfraredProtocolNEC) + return &infrared_protocol_variant_nec; + else if(protocol == InfraredProtocolNECext) + return &infrared_protocol_variant_necext; + else if(protocol == InfraredProtocolNEC42) + return &infrared_protocol_variant_nec42; + else if(protocol == InfraredProtocolNEC42ext) + return &infrared_protocol_variant_nec42ext; + else + return NULL; +} diff --git a/lib/infrared/encoder_decoder/nec/infrared_protocol_nec.h b/lib/infrared/encoder_decoder/nec/infrared_protocol_nec.h new file mode 100644 index 00000000000..559e31fda13 --- /dev/null +++ b/lib/infrared/encoder_decoder/nec/infrared_protocol_nec.h @@ -0,0 +1,30 @@ +#pragma once + +#include "../infrared_i.h" + +/*************************************************************************************************** +* NEC protocol description +* https://radioparty.ru/manuals/encyclopedia/213-ircontrol?start=1 +**************************************************************************************************** +* Preamble Preamble Pulse Distance/Width Pause Preamble Preamble Stop +* mark space Modulation up to period repeat repeat bit +* mark space +* +* 9000 4500 32 bit + stop bit ...110000 9000 2250 +* __________ _ _ _ _ _ _ _ _ _ _ _ _ _ ___________ _ +* ____ __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________ ____________ ___ +* +***************************************************************************************************/ + +void* infrared_decoder_nec_alloc(void); +void infrared_decoder_nec_reset(void* decoder); +void infrared_decoder_nec_free(void* decoder); +InfraredMessage* infrared_decoder_nec_check_ready(void* decoder); +InfraredMessage* infrared_decoder_nec_decode(void* decoder, bool level, uint32_t duration); + +void* infrared_encoder_nec_alloc(void); +InfraredStatus infrared_encoder_nec_encode(void* encoder_ptr, uint32_t* duration, bool* level); +void infrared_encoder_nec_reset(void* encoder_ptr, const InfraredMessage* message); +void infrared_encoder_nec_free(void* encoder_ptr); + +const InfraredProtocolVariant* infrared_protocol_nec_get_variant(InfraredProtocol protocol); diff --git a/lib/infrared/encoder_decoder/nec/infrared_protocol_nec_i.h b/lib/infrared/encoder_decoder/nec/infrared_protocol_nec_i.h new file mode 100644 index 00000000000..05df1f474ea --- /dev/null +++ b/lib/infrared/encoder_decoder/nec/infrared_protocol_nec_i.h @@ -0,0 +1,29 @@ +#pragma once + +#include "../common/infrared_common_i.h" + +#define INFRARED_NEC_PREAMBLE_MARK 9000 +#define INFRARED_NEC_PREAMBLE_SPACE 4500 +#define INFRARED_NEC_BIT1_MARK 560 +#define INFRARED_NEC_BIT1_SPACE 1690 +#define INFRARED_NEC_BIT0_MARK 560 +#define INFRARED_NEC_BIT0_SPACE 560 +#define INFRARED_NEC_REPEAT_PERIOD 110000 +#define INFRARED_NEC_SILENCE INFRARED_NEC_REPEAT_PERIOD +#define INFRARED_NEC_MIN_SPLIT_TIME INFRARED_NEC_REPEAT_PAUSE_MIN +#define INFRARED_NEC_REPEAT_PAUSE_MIN 4000 +#define INFRARED_NEC_REPEAT_PAUSE_MAX 150000 +#define INFRARED_NEC_REPEAT_COUNT_MIN 1 +#define INFRARED_NEC_REPEAT_MARK 9000 +#define INFRARED_NEC_REPEAT_SPACE 2250 +#define INFRARED_NEC_PREAMBLE_TOLERANCE 200 // us +#define INFRARED_NEC_BIT_TOLERANCE 120 // us + +extern const InfraredCommonProtocolSpec infrared_protocol_nec; + +bool infrared_decoder_nec_interpret(InfraredCommonDecoder* decoder); +InfraredStatus infrared_decoder_nec_decode_repeat(InfraredCommonDecoder* decoder); +InfraredStatus infrared_encoder_nec_encode_repeat( + InfraredCommonEncoder* encoder, + uint32_t* duration, + bool* level); diff --git a/lib/infrared/encoder_decoder/rc5/infrared_decoder_rc5.c b/lib/infrared/encoder_decoder/rc5/infrared_decoder_rc5.c index 6b4a7c2e35f..1b2f2f3019f 100644 --- a/lib/infrared/encoder_decoder/rc5/infrared_decoder_rc5.c +++ b/lib/infrared/encoder_decoder/rc5/infrared_decoder_rc5.c @@ -1,10 +1,7 @@ -#include "infrared.h" -#include -#include -#include -#include -#include "../infrared_i.h" -#include "../infrared_protocol_defs_i.h" +#include "infrared_protocol_rc5_i.h" + +#include +#include typedef struct { InfraredCommonDecoder* common_decoder; @@ -60,7 +57,7 @@ bool infrared_decoder_rc5_interpret(InfraredCommonDecoder* decoder) { void* infrared_decoder_rc5_alloc(void) { InfraredRc5Decoder* decoder = malloc(sizeof(InfraredRc5Decoder)); decoder->toggle = false; - decoder->common_decoder = infrared_common_decoder_alloc(&protocol_rc5); + decoder->common_decoder = infrared_common_decoder_alloc(&infrared_protocol_rc5); decoder->common_decoder->context = decoder; return decoder; } diff --git a/lib/infrared/encoder_decoder/rc5/infrared_encoder_rc5.c b/lib/infrared/encoder_decoder/rc5/infrared_encoder_rc5.c index 7b55cdc4479..df47fb7c433 100644 --- a/lib/infrared/encoder_decoder/rc5/infrared_encoder_rc5.c +++ b/lib/infrared/encoder_decoder/rc5/infrared_encoder_rc5.c @@ -1,9 +1,7 @@ -#include -#include "infrared.h" -#include "common/infrared_common_i.h" -#include "infrared_protocol_defs_i.h" -#include -#include "../infrared_i.h" +#include "infrared_protocol_rc5_i.h" + +#include +#include typedef struct InfraredEncoderRC5 { InfraredCommonEncoder* common_encoder; @@ -41,7 +39,7 @@ InfraredStatus infrared_encoder_rc5_encode(void* encoder_ptr, uint32_t* duration void* infrared_encoder_rc5_alloc(void) { InfraredEncoderRC5* encoder = malloc(sizeof(InfraredEncoderRC5)); - encoder->common_encoder = infrared_common_encoder_alloc(&protocol_rc5); + encoder->common_encoder = infrared_common_encoder_alloc(&infrared_protocol_rc5); encoder->toggle_bit = false; return encoder; } diff --git a/lib/infrared/encoder_decoder/rc5/infrared_protocol_rc5.c b/lib/infrared/encoder_decoder/rc5/infrared_protocol_rc5.c new file mode 100644 index 00000000000..bc7e299fda2 --- /dev/null +++ b/lib/infrared/encoder_decoder/rc5/infrared_protocol_rc5.c @@ -0,0 +1,49 @@ +#include "infrared_protocol_rc5_i.h" + +const InfraredCommonProtocolSpec infrared_protocol_rc5 = { + .timings = + { + .preamble_mark = 0, + .preamble_space = 0, + .bit1_mark = INFRARED_RC5_BIT, + .preamble_tolerance = 0, + .bit_tolerance = INFRARED_RC5_BIT_TOLERANCE, + .silence_time = INFRARED_RC5_SILENCE, + .min_split_time = INFRARED_RC5_MIN_SPLIT_TIME, + }, + .databit_len[0] = 1 + 1 + 1 + 5 + + 6, // start_bit + start_bit/command_bit + toggle_bit + 5 address + 6 command + .manchester_start_from_space = true, + .decode = infrared_common_decode_manchester, + .encode = infrared_common_encode_manchester, + .interpret = infrared_decoder_rc5_interpret, + .decode_repeat = NULL, + .encode_repeat = NULL, +}; + +static const InfraredProtocolVariant infrared_protocol_variant_rc5 = { + .name = "RC5", + .address_length = 5, + .command_length = 6, + .frequency = INFRARED_RC5_CARRIER_FREQUENCY, + .duty_cycle = INFRARED_RC5_DUTY_CYCLE, + .repeat_count = INFRARED_RC5_REPEAT_COUNT_MIN, +}; + +static const InfraredProtocolVariant infrared_protocol_variant_rc5x = { + .name = "RC5X", + .address_length = 5, + .command_length = 7, + .frequency = INFRARED_RC5_CARRIER_FREQUENCY, + .duty_cycle = INFRARED_RC5_DUTY_CYCLE, + .repeat_count = INFRARED_RC5_REPEAT_COUNT_MIN, +}; + +const InfraredProtocolVariant* infrared_protocol_rc5_get_variant(InfraredProtocol protocol) { + if(protocol == InfraredProtocolRC5) + return &infrared_protocol_variant_rc5; + else if(protocol == InfraredProtocolRC5X) + return &infrared_protocol_variant_rc5x; + else + return NULL; +} diff --git a/lib/infrared/encoder_decoder/rc5/infrared_protocol_rc5.h b/lib/infrared/encoder_decoder/rc5/infrared_protocol_rc5.h new file mode 100644 index 00000000000..9dd5802a93c --- /dev/null +++ b/lib/infrared/encoder_decoder/rc5/infrared_protocol_rc5.h @@ -0,0 +1,38 @@ +#pragma once + +#include "../infrared_i.h" + +/*************************************************************************************************** +* RC5 protocol description +* https://www.mikrocontroller.net/articles/IRMP_-_english#RC5_.2B_RC5X +**************************************************************************************************** +* Manchester/biphase +* Modulation +* +* 888/1776 - bit (x2 for toggle bit) +* +* __ ____ __ __ __ __ __ __ __ __ +* __ __ ____ __ __ __ __ __ __ __ _ +* | 1 | 1 | 0 | ... | ... | +* s si T address (MSB) command (MSB) +* +* Note: manchester starts from space timing, so it have to be handled properly +* s - start bit (always 1) +* si - RC5: start bit (always 1), RC5X - 7-th bit of address (in our case always 0) +* T - toggle bit, change it's value every button press +* address - 5 bit +* command - 6/7 bit +***************************************************************************************************/ + +void* infrared_decoder_rc5_alloc(void); +void infrared_decoder_rc5_reset(void* decoder); +void infrared_decoder_rc5_free(void* decoder); +InfraredMessage* infrared_decoder_rc5_check_ready(void* ctx); +InfraredMessage* infrared_decoder_rc5_decode(void* decoder, bool level, uint32_t duration); + +void* infrared_encoder_rc5_alloc(void); +void infrared_encoder_rc5_reset(void* encoder_ptr, const InfraredMessage* message); +void infrared_encoder_rc5_free(void* decoder); +InfraredStatus infrared_encoder_rc5_encode(void* encoder_ptr, uint32_t* duration, bool* polarity); + +const InfraredProtocolVariant* infrared_protocol_rc5_get_variant(InfraredProtocol protocol); diff --git a/lib/infrared/encoder_decoder/rc5/infrared_protocol_rc5_i.h b/lib/infrared/encoder_decoder/rc5/infrared_protocol_rc5_i.h new file mode 100644 index 00000000000..b906c369e00 --- /dev/null +++ b/lib/infrared/encoder_decoder/rc5/infrared_protocol_rc5_i.h @@ -0,0 +1,20 @@ +#pragma once + +#include "../common/infrared_common_i.h" + +#define INFRARED_RC5_CARRIER_FREQUENCY 36000 +#define INFRARED_RC5_DUTY_CYCLE 0.33 + +#define INFRARED_RC5_PREAMBLE_MARK 0 +#define INFRARED_RC5_PREAMBLE_SPACE 0 +#define INFRARED_RC5_BIT 888 // half of time-quant for 1 bit +#define INFRARED_RC5_PREAMBLE_TOLERANCE 200 // us +#define INFRARED_RC5_BIT_TOLERANCE 120 // us +/* protocol allows 2700 silence, but it is hard to send 1 message without repeat */ +#define INFRARED_RC5_SILENCE (2700 * 10) +#define INFRARED_RC5_MIN_SPLIT_TIME 2700 +#define INFRARED_RC5_REPEAT_COUNT_MIN 1 + +extern const InfraredCommonProtocolSpec infrared_protocol_rc5; + +bool infrared_decoder_rc5_interpret(InfraredCommonDecoder* decoder); diff --git a/lib/infrared/encoder_decoder/rc5/infrared_rc5_spec.c b/lib/infrared/encoder_decoder/rc5/infrared_rc5_spec.c deleted file mode 100644 index 25ea230ef34..00000000000 --- a/lib/infrared/encoder_decoder/rc5/infrared_rc5_spec.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "../infrared_i.h" -#include "infrared_protocol_defs_i.h" - -static const InfraredProtocolSpecification infrared_rc5_protocol_specification = { - .name = "RC5", - .address_length = 5, - .command_length = 6, - .frequency = INFRARED_RC5_CARRIER_FREQUENCY, - .duty_cycle = INFRARED_RC5_DUTY_CYCLE, -}; - -static const InfraredProtocolSpecification infrared_rc5x_protocol_specification = { - .name = "RC5X", - .address_length = 5, - .command_length = 7, - .frequency = INFRARED_RC5_CARRIER_FREQUENCY, - .duty_cycle = INFRARED_RC5_DUTY_CYCLE, -}; - -const InfraredProtocolSpecification* infrared_rc5_get_spec(InfraredProtocol protocol) { - if(protocol == InfraredProtocolRC5) - return &infrared_rc5_protocol_specification; - else if(protocol == InfraredProtocolRC5X) - return &infrared_rc5x_protocol_specification; - else - return NULL; -} diff --git a/lib/infrared/encoder_decoder/rc6/infrared_decoder_rc6.c b/lib/infrared/encoder_decoder/rc6/infrared_decoder_rc6.c index 13d3e5364dc..b70f7ceb854 100644 --- a/lib/infrared/encoder_decoder/rc6/infrared_decoder_rc6.c +++ b/lib/infrared/encoder_decoder/rc6/infrared_decoder_rc6.c @@ -1,10 +1,7 @@ -#include "infrared.h" -#include -#include -#include -#include -#include "../infrared_i.h" -#include "../infrared_protocol_defs_i.h" +#include "infrared_protocol_rc6_i.h" + +#include +#include typedef struct { InfraredCommonDecoder* common_decoder; @@ -93,7 +90,7 @@ InfraredStatus infrared_decoder_rc6_decode_manchester( void* infrared_decoder_rc6_alloc(void) { InfraredRc6Decoder* decoder = malloc(sizeof(InfraredRc6Decoder)); decoder->toggle = false; - decoder->common_decoder = infrared_common_decoder_alloc(&protocol_rc6); + decoder->common_decoder = infrared_common_decoder_alloc(&infrared_protocol_rc6); decoder->common_decoder->context = decoder; return decoder; } diff --git a/lib/infrared/encoder_decoder/rc6/infrared_encoder_rc6.c b/lib/infrared/encoder_decoder/rc6/infrared_encoder_rc6.c index f1240b17a79..d13a204ea0e 100644 --- a/lib/infrared/encoder_decoder/rc6/infrared_encoder_rc6.c +++ b/lib/infrared/encoder_decoder/rc6/infrared_encoder_rc6.c @@ -1,9 +1,7 @@ -#include -#include "infrared.h" -#include "common/infrared_common_i.h" -#include "infrared_protocol_defs_i.h" -#include -#include "../infrared_i.h" +#include "infrared_protocol_rc6_i.h" + +#include +#include typedef struct InfraredEncoderRC6 { InfraredCommonEncoder* common_encoder; @@ -35,7 +33,7 @@ InfraredStatus infrared_encoder_rc6_encode(void* encoder_ptr, uint32_t* duration void* infrared_encoder_rc6_alloc(void) { InfraredEncoderRC6* encoder = malloc(sizeof(InfraredEncoderRC6)); - encoder->common_encoder = infrared_common_encoder_alloc(&protocol_rc6); + encoder->common_encoder = infrared_common_encoder_alloc(&infrared_protocol_rc6); encoder->toggle_bit = false; return encoder; } diff --git a/lib/infrared/encoder_decoder/rc6/infrared_protocol_rc6.c b/lib/infrared/encoder_decoder/rc6/infrared_protocol_rc6.c new file mode 100644 index 00000000000..40a187d85cb --- /dev/null +++ b/lib/infrared/encoder_decoder/rc6/infrared_protocol_rc6.c @@ -0,0 +1,39 @@ +#include "infrared_protocol_rc6_i.h" + +const InfraredCommonProtocolSpec infrared_protocol_rc6 = { + .timings = + { + .preamble_mark = INFRARED_RC6_PREAMBLE_MARK, + .preamble_space = INFRARED_RC6_PREAMBLE_SPACE, + .bit1_mark = INFRARED_RC6_BIT, + .preamble_tolerance = INFRARED_RC6_PREAMBLE_TOLERANCE, + .bit_tolerance = INFRARED_RC6_BIT_TOLERANCE, + .silence_time = INFRARED_RC6_SILENCE, + .min_split_time = INFRARED_RC6_MIN_SPLIT_TIME, + }, + .databit_len[0] = + 1 + 3 + 1 + 8 + + 8, // start_bit + 3 mode bits, + 1 toggle bit (x2 timing) + 8 address + 8 command + .manchester_start_from_space = false, + .decode = infrared_decoder_rc6_decode_manchester, + .encode = infrared_encoder_rc6_encode_manchester, + .interpret = infrared_decoder_rc6_interpret, + .decode_repeat = NULL, + .encode_repeat = NULL, +}; + +static const InfraredProtocolVariant infrared_protocol_variant_rc6 = { + .name = "RC6", + .address_length = 8, + .command_length = 8, + .frequency = INFRARED_RC6_CARRIER_FREQUENCY, + .duty_cycle = INFRARED_RC6_DUTY_CYCLE, + .repeat_count = INFRARED_RC6_REPEAT_COUNT_MIN, +}; + +const InfraredProtocolVariant* infrared_protocol_rc6_get_variant(InfraredProtocol protocol) { + if(protocol == InfraredProtocolRC6) + return &infrared_protocol_variant_rc6; + else + return NULL; +} diff --git a/lib/infrared/encoder_decoder/rc6/infrared_protocol_rc6.h b/lib/infrared/encoder_decoder/rc6/infrared_protocol_rc6.h new file mode 100644 index 00000000000..f0b1634111c --- /dev/null +++ b/lib/infrared/encoder_decoder/rc6/infrared_protocol_rc6.h @@ -0,0 +1,37 @@ +#pragma once + +#include "../infrared_i.h" + +/*************************************************************************************************** +* RC6 protocol description +* https://www.mikrocontroller.net/articles/IRMP_-_english#RC6_.2B_RC6A +**************************************************************************************************** +* Preamble Manchester/biphase Silence +* mark/space Modulation +* +* 2666 889 444/888 - bit (x2 for toggle bit) 2666 +* +* ________ __ __ __ __ ____ __ __ __ __ __ __ __ __ +* _ _________ ____ __ __ ____ __ __ __ __ __ __ __ __ _______________ +* | 1 | 0 | 0 | 0 | 0 | ... | ... | | +* s m2 m1 m0 T address (MSB) command (MSB) +* +* s - start bit (always 1) +* m0-2 - mode (000 for RC6) +* T - toggle bit, twice longer +* address - 8 bit +* command - 8 bit +***************************************************************************************************/ + +void* infrared_decoder_rc6_alloc(void); +void infrared_decoder_rc6_reset(void* decoder); +void infrared_decoder_rc6_free(void* decoder); +InfraredMessage* infrared_decoder_rc6_check_ready(void* ctx); +InfraredMessage* infrared_decoder_rc6_decode(void* decoder, bool level, uint32_t duration); + +void* infrared_encoder_rc6_alloc(void); +void infrared_encoder_rc6_reset(void* encoder_ptr, const InfraredMessage* message); +void infrared_encoder_rc6_free(void* decoder); +InfraredStatus infrared_encoder_rc6_encode(void* encoder_ptr, uint32_t* duration, bool* polarity); + +const InfraredProtocolVariant* infrared_protocol_rc6_get_variant(InfraredProtocol protocol); diff --git a/lib/infrared/encoder_decoder/rc6/infrared_protocol_rc6_i.h b/lib/infrared/encoder_decoder/rc6/infrared_protocol_rc6_i.h new file mode 100644 index 00000000000..06aa2a6a519 --- /dev/null +++ b/lib/infrared/encoder_decoder/rc6/infrared_protocol_rc6_i.h @@ -0,0 +1,28 @@ +#pragma once + +#include "../common/infrared_common_i.h" + +#define INFRARED_RC6_CARRIER_FREQUENCY 36000 +#define INFRARED_RC6_DUTY_CYCLE 0.33 + +#define INFRARED_RC6_PREAMBLE_MARK 2666 +#define INFRARED_RC6_PREAMBLE_SPACE 889 +#define INFRARED_RC6_BIT 444 // half of time-quant for 1 bit +#define INFRARED_RC6_PREAMBLE_TOLERANCE 200 // us +#define INFRARED_RC6_BIT_TOLERANCE 120 // us +/* protocol allows 2700 silence, but it is hard to send 1 message without repeat */ +#define INFRARED_RC6_SILENCE (2700 * 10) +#define INFRARED_RC6_MIN_SPLIT_TIME 2700 +#define INFRARED_RC6_REPEAT_COUNT_MIN 1 + +extern const InfraredCommonProtocolSpec infrared_protocol_rc6; + +bool infrared_decoder_rc6_interpret(InfraredCommonDecoder* decoder); +InfraredStatus infrared_decoder_rc6_decode_manchester( + InfraredCommonDecoder* decoder, + bool level, + uint32_t timing); +InfraredStatus infrared_encoder_rc6_encode_manchester( + InfraredCommonEncoder* encoder_ptr, + uint32_t* duration, + bool* polarity); diff --git a/lib/infrared/encoder_decoder/rc6/infrared_rc6_spec.c b/lib/infrared/encoder_decoder/rc6/infrared_rc6_spec.c deleted file mode 100644 index 9e0ba746251..00000000000 --- a/lib/infrared/encoder_decoder/rc6/infrared_rc6_spec.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "../infrared_i.h" -#include "infrared_protocol_defs_i.h" - -static const InfraredProtocolSpecification infrared_rc6_protocol_specification = { - .name = "RC6", - .address_length = 8, - .command_length = 8, - .frequency = INFRARED_RC6_CARRIER_FREQUENCY, - .duty_cycle = INFRARED_RC6_DUTY_CYCLE, -}; - -const InfraredProtocolSpecification* infrared_rc6_get_spec(InfraredProtocol protocol) { - if(protocol == InfraredProtocolRC6) - return &infrared_rc6_protocol_specification; - else - return NULL; -} diff --git a/lib/infrared/encoder_decoder/rca/infrared_decoder_rca.c b/lib/infrared/encoder_decoder/rca/infrared_decoder_rca.c new file mode 100644 index 00000000000..b6d02a38c77 --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_decoder_rca.c @@ -0,0 +1,45 @@ +#include "infrared_protocol_rca_i.h" +#include + +InfraredMessage* infrared_decoder_rca_check_ready(void* ctx) { + return infrared_common_decoder_check_ready(ctx); +} + +bool infrared_decoder_rca_interpret(InfraredCommonDecoder* decoder) { + furi_assert(decoder); + + uint32_t* data = (void*)&decoder->data; + + uint8_t address = (*data & 0xF); + uint8_t command = (*data >> 4) & 0xFF; + uint8_t address_inverse = (*data >> 12) & 0xF; + uint8_t command_inverse = (*data >> 16) & 0xFF; + uint8_t inverse_address_inverse = (uint8_t)~address_inverse & 0xF; + uint8_t inverse_command_inverse = (uint8_t)~command_inverse; + + if((command == inverse_command_inverse) && (address == inverse_address_inverse)) { + decoder->message.protocol = InfraredProtocolRCA; + decoder->message.address = address; + decoder->message.command = command; + decoder->message.repeat = false; + return true; + } + + return false; +} + +void* infrared_decoder_rca_alloc(void) { + return infrared_common_decoder_alloc(&infrared_protocol_rca); +} + +InfraredMessage* infrared_decoder_rca_decode(void* decoder, bool level, uint32_t duration) { + return infrared_common_decode(decoder, level, duration); +} + +void infrared_decoder_rca_free(void* decoder) { + infrared_common_decoder_free(decoder); +} + +void infrared_decoder_rca_reset(void* decoder) { + infrared_common_decoder_reset(decoder); +} diff --git a/lib/infrared/encoder_decoder/rca/infrared_encoder_rca.c b/lib/infrared/encoder_decoder/rca/infrared_encoder_rca.c new file mode 100644 index 00000000000..f0be4a6a9e2 --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_encoder_rca.c @@ -0,0 +1,37 @@ +#include "infrared_protocol_rca_i.h" + +#include + +void infrared_encoder_rca_reset(void* encoder_ptr, const InfraredMessage* message) { + furi_assert(encoder_ptr); + furi_assert(message); + + InfraredCommonEncoder* encoder = encoder_ptr; + infrared_common_encoder_reset(encoder); + + uint32_t* data = (void*)encoder->data; + + uint8_t address = message->address; + uint8_t address_inverse = ~address; + uint8_t command = message->command; + uint8_t command_inverse = ~command; + + *data = address & 0xF; + *data |= command << 4; + *data |= (address_inverse & 0xF) << 12; + *data |= command_inverse << 16; + + encoder->bits_to_encode = encoder->protocol->databit_len[0]; +} + +void* infrared_encoder_rca_alloc(void) { + return infrared_common_encoder_alloc(&infrared_protocol_rca); +} + +void infrared_encoder_rca_free(void* encoder_ptr) { + infrared_common_encoder_free(encoder_ptr); +} + +InfraredStatus infrared_encoder_rca_encode(void* encoder_ptr, uint32_t* duration, bool* level) { + return infrared_common_encode(encoder_ptr, duration, level); +} diff --git a/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.c b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.c new file mode 100644 index 00000000000..8e1e76dbd37 --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.c @@ -0,0 +1,40 @@ +#include "infrared_protocol_rca_i.h" + +const InfraredCommonProtocolSpec infrared_protocol_rca = { + .timings = + { + .preamble_mark = INFRARED_RCA_PREAMBLE_MARK, + .preamble_space = INFRARED_RCA_PREAMBLE_SPACE, + .bit1_mark = INFRARED_RCA_BIT1_MARK, + .bit1_space = INFRARED_RCA_BIT1_SPACE, + .bit0_mark = INFRARED_RCA_BIT0_MARK, + .bit0_space = INFRARED_RCA_BIT0_SPACE, + .preamble_tolerance = INFRARED_RCA_PREAMBLE_TOLERANCE, + .bit_tolerance = INFRARED_RCA_BIT_TOLERANCE, + .silence_time = INFRARED_RCA_SILENCE, + .min_split_time = INFRARED_RCA_MIN_SPLIT_TIME, + }, + .databit_len[0] = 24, + .no_stop_bit = false, + .decode = infrared_common_decode_pdwm, + .encode = infrared_common_encode_pdwm, + .interpret = infrared_decoder_rca_interpret, + .decode_repeat = NULL, + .encode_repeat = NULL, +}; + +static const InfraredProtocolVariant infrared_protocol_variant_rca = { + .name = "RCA", + .address_length = 4, + .command_length = 8, + .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, + .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, + .repeat_count = INFRARED_RCA_REPEAT_COUNT_MIN, +}; + +const InfraredProtocolVariant* infrared_protocol_rca_get_variant(InfraredProtocol protocol) { + if(protocol == InfraredProtocolRCA) + return &infrared_protocol_variant_rca; + else + return NULL; +} diff --git a/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.h b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.h new file mode 100644 index 00000000000..d9cae48e48d --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.h @@ -0,0 +1,30 @@ +#pragma once + +#include "../infrared_i.h" + +/*************************************************************************************************** +* RCA protocol description +* https://www.sbprojects.net/knowledge/ir/rca.php +**************************************************************************************************** +* Preamble Preamble Pulse Distance/Width Pause Preamble Preamble +* mark space Modulation up to period repeat repeat +* mark space +* +* 4000 4000 24 bit ...8000 4000 4000 +* __________ _ _ _ _ _ _ _ _ _ _ _ _ _ ___________ +* ____ __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________ ___________ +* +***************************************************************************************************/ + +void* infrared_decoder_rca_alloc(void); +void infrared_decoder_rca_reset(void* decoder); +void infrared_decoder_rca_free(void* decoder); +InfraredMessage* infrared_decoder_rca_check_ready(void* decoder); +InfraredMessage* infrared_decoder_rca_decode(void* decoder, bool level, uint32_t duration); + +void* infrared_encoder_rca_alloc(void); +InfraredStatus infrared_encoder_rca_encode(void* encoder_ptr, uint32_t* duration, bool* level); +void infrared_encoder_rca_reset(void* encoder_ptr, const InfraredMessage* message); +void infrared_encoder_rca_free(void* encoder_ptr); + +const InfraredProtocolVariant* infrared_protocol_rca_get_variant(InfraredProtocol protocol); diff --git a/lib/infrared/encoder_decoder/rca/infrared_protocol_rca_i.h b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca_i.h new file mode 100644 index 00000000000..9ec4fe3b179 --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca_i.h @@ -0,0 +1,30 @@ +#pragma once + +#include "../common/infrared_common_i.h" + +#define INFRARED_RCA_PREAMBLE_MARK 4000 +#define INFRARED_RCA_PREAMBLE_SPACE 4000 +#define INFRARED_RCA_BIT1_MARK 500 +#define INFRARED_RCA_BIT1_SPACE 2000 +#define INFRARED_RCA_BIT0_MARK 500 +#define INFRARED_RCA_BIT0_SPACE 1000 +#define INFRARED_RCA_REPEAT_PERIOD 8000 +#define INFRARED_RCA_SILENCE INFRARED_RCA_REPEAT_PERIOD + +#define INFRARED_RCA_MIN_SPLIT_TIME INFRARED_RCA_REPEAT_PAUSE_MIN +#define INFRARED_RCA_REPEAT_PAUSE_MIN 4000 +#define INFRARED_RCA_REPEAT_PAUSE_MAX 150000 +#define INFRARED_RCA_REPEAT_COUNT_MIN 1 +#define INFRARED_RCA_REPEAT_MARK INFRARED_RCA_PREAMBLE_MARK +#define INFRARED_RCA_REPEAT_SPACE INFRARED_RCA_PREAMBLE_SPACE +#define INFRARED_RCA_PREAMBLE_TOLERANCE 200 // us +#define INFRARED_RCA_BIT_TOLERANCE 120 // us + +extern const InfraredCommonProtocolSpec infrared_protocol_rca; + +bool infrared_decoder_rca_interpret(InfraredCommonDecoder* decoder); +InfraredStatus infrared_decoder_rca_decode_repeat(InfraredCommonDecoder* decoder); +InfraredStatus infrared_encoder_rca_encode_repeat( + InfraredCommonEncoder* encoder, + uint32_t* duration, + bool* level); diff --git a/lib/infrared/encoder_decoder/samsung/infrared_decoder_samsung.c b/lib/infrared/encoder_decoder/samsung/infrared_decoder_samsung.c index e8cd3b05cb5..32881d3c959 100644 --- a/lib/infrared/encoder_decoder/samsung/infrared_decoder_samsung.c +++ b/lib/infrared/encoder_decoder/samsung/infrared_decoder_samsung.c @@ -1,9 +1,5 @@ -#include "infrared.h" -#include "infrared_protocol_defs_i.h" -#include -#include -#include -#include "../infrared_i.h" +#include "infrared_protocol_samsung_i.h" +#include InfraredMessage* infrared_decoder_samsung32_check_ready(void* ctx) { return infrared_common_decoder_check_ready(ctx); @@ -57,7 +53,7 @@ InfraredStatus infrared_decoder_samsung32_decode_repeat(InfraredCommonDecoder* d } void* infrared_decoder_samsung32_alloc(void) { - return infrared_common_decoder_alloc(&protocol_samsung32); + return infrared_common_decoder_alloc(&infrared_protocol_samsung32); } InfraredMessage* infrared_decoder_samsung32_decode(void* decoder, bool level, uint32_t duration) { diff --git a/lib/infrared/encoder_decoder/samsung/infrared_encoder_samsung.c b/lib/infrared/encoder_decoder/samsung/infrared_encoder_samsung.c index 75b037f0076..3aed50656e2 100644 --- a/lib/infrared/encoder_decoder/samsung/infrared_encoder_samsung.c +++ b/lib/infrared/encoder_decoder/samsung/infrared_encoder_samsung.c @@ -1,9 +1,7 @@ +#include "infrared_protocol_samsung_i.h" + #include -#include "common/infrared_common_i.h" -#include -#include "../infrared_i.h" -#include "infrared_protocol_defs_i.h" -#include +#include static const uint32_t repeat_timings[] = { INFRARED_SAMSUNG_REPEAT_PAUSE2, @@ -58,7 +56,7 @@ InfraredStatus infrared_encoder_samsung32_encode_repeat( } void* infrared_encoder_samsung32_alloc(void) { - return infrared_common_encoder_alloc(&protocol_samsung32); + return infrared_common_encoder_alloc(&infrared_protocol_samsung32); } void infrared_encoder_samsung32_free(void* encoder_ptr) { diff --git a/lib/infrared/encoder_decoder/samsung/infrared_protocol_samsung.c b/lib/infrared/encoder_decoder/samsung/infrared_protocol_samsung.c new file mode 100644 index 00000000000..ca78726acf9 --- /dev/null +++ b/lib/infrared/encoder_decoder/samsung/infrared_protocol_samsung.c @@ -0,0 +1,40 @@ +#include "infrared_protocol_samsung_i.h" + +const InfraredCommonProtocolSpec infrared_protocol_samsung32 = { + .timings = + { + .preamble_mark = INFRARED_SAMSUNG_PREAMBLE_MARK, + .preamble_space = INFRARED_SAMSUNG_PREAMBLE_SPACE, + .bit1_mark = INFRARED_SAMSUNG_BIT1_MARK, + .bit1_space = INFRARED_SAMSUNG_BIT1_SPACE, + .bit0_mark = INFRARED_SAMSUNG_BIT0_MARK, + .bit0_space = INFRARED_SAMSUNG_BIT0_SPACE, + .preamble_tolerance = INFRARED_SAMSUNG_PREAMBLE_TOLERANCE, + .bit_tolerance = INFRARED_SAMSUNG_BIT_TOLERANCE, + .silence_time = INFRARED_SAMSUNG_SILENCE, + .min_split_time = INFRARED_SAMSUNG_MIN_SPLIT_TIME, + }, + .databit_len[0] = 32, + .no_stop_bit = false, + .decode = infrared_common_decode_pdwm, + .encode = infrared_common_encode_pdwm, + .interpret = infrared_decoder_samsung32_interpret, + .decode_repeat = infrared_decoder_samsung32_decode_repeat, + .encode_repeat = infrared_encoder_samsung32_encode_repeat, +}; + +static const InfraredProtocolVariant infrared_protocol_variant_samsung32 = { + .name = "Samsung32", + .address_length = 8, + .command_length = 8, + .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, + .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, + .repeat_count = INFRARED_SAMSUNG_REPEAT_COUNT_MIN, +}; + +const InfraredProtocolVariant* infrared_protocol_samsung32_get_variant(InfraredProtocol protocol) { + if(protocol == InfraredProtocolSamsung32) + return &infrared_protocol_variant_samsung32; + else + return NULL; +} diff --git a/lib/infrared/encoder_decoder/samsung/infrared_protocol_samsung.h b/lib/infrared/encoder_decoder/samsung/infrared_protocol_samsung.h new file mode 100644 index 00000000000..9abcb2e3e65 --- /dev/null +++ b/lib/infrared/encoder_decoder/samsung/infrared_protocol_samsung.h @@ -0,0 +1,31 @@ +#pragma once + +#include "../infrared_i.h" + +/*************************************************************************************************** +* SAMSUNG32 protocol description +* https://www.mikrocontroller.net/articles/IRMP_-_english#SAMSUNG +**************************************************************************************************** +* Preamble Preamble Pulse Distance/Width Pause Preamble Preamble Bit1 Stop +* mark space Modulation repeat repeat bit +* mark space +* +* 4500 4500 32 bit + stop bit 40000/100000 4500 4500 +* __________ _ _ _ _ _ _ _ _ _ _ _ ___________ _ _ +* _ __________ __ _ __ __ __ _ _ __ __ _ ________________ ____________ ____ ___ +* +***************************************************************************************************/ + +void* infrared_decoder_samsung32_alloc(void); +void infrared_decoder_samsung32_reset(void* decoder); +void infrared_decoder_samsung32_free(void* decoder); +InfraredMessage* infrared_decoder_samsung32_check_ready(void* ctx); +InfraredMessage* infrared_decoder_samsung32_decode(void* decoder, bool level, uint32_t duration); + +InfraredStatus + infrared_encoder_samsung32_encode(void* encoder_ptr, uint32_t* duration, bool* level); +void infrared_encoder_samsung32_reset(void* encoder_ptr, const InfraredMessage* message); +void* infrared_encoder_samsung32_alloc(void); +void infrared_encoder_samsung32_free(void* encoder_ptr); + +const InfraredProtocolVariant* infrared_protocol_samsung32_get_variant(InfraredProtocol protocol); diff --git a/lib/infrared/encoder_decoder/samsung/infrared_protocol_samsung_i.h b/lib/infrared/encoder_decoder/samsung/infrared_protocol_samsung_i.h new file mode 100644 index 00000000000..b8538494296 --- /dev/null +++ b/lib/infrared/encoder_decoder/samsung/infrared_protocol_samsung_i.h @@ -0,0 +1,35 @@ +#pragma once + +#include "../common/infrared_common_i.h" + +#define INFRARED_SAMSUNG_PREAMBLE_MARK 4500 +#define INFRARED_SAMSUNG_PREAMBLE_SPACE 4500 +#define INFRARED_SAMSUNG_BIT1_MARK 550 +#define INFRARED_SAMSUNG_BIT1_SPACE 1650 +#define INFRARED_SAMSUNG_BIT0_MARK 550 +#define INFRARED_SAMSUNG_BIT0_SPACE 550 +#define INFRARED_SAMSUNG_REPEAT_PAUSE_MIN 30000 +#define INFRARED_SAMSUNG_REPEAT_PAUSE_MAX 140000 +#define INFRARED_SAMSUNG_REPEAT_PAUSE1 46000 +#define INFRARED_SAMSUNG_REPEAT_PAUSE2 97000 +#define INFRARED_SAMSUNG_REPEAT_COUNT_MIN 1 +/* Samsung silence have to be greater than REPEAT MAX + * otherwise there can be problems during unit tests parsing + * of some data. Real tolerances we don't know, but in real life + * silence time should be greater than max repeat time. This is + * because of similar preambule timings for repeat and first messages. */ +#define INFRARED_SAMSUNG_MIN_SPLIT_TIME 5000 +#define INFRARED_SAMSUNG_SILENCE 145000 +#define INFRARED_SAMSUNG_REPEAT_MARK 4500 +#define INFRARED_SAMSUNG_REPEAT_SPACE 4500 +#define INFRARED_SAMSUNG_PREAMBLE_TOLERANCE 200 // us +#define INFRARED_SAMSUNG_BIT_TOLERANCE 120 // us + +bool infrared_decoder_samsung32_interpret(InfraredCommonDecoder* decoder); +InfraredStatus infrared_decoder_samsung32_decode_repeat(InfraredCommonDecoder* decoder); +InfraredStatus infrared_encoder_samsung32_encode_repeat( + InfraredCommonEncoder* encoder, + uint32_t* duration, + bool* level); + +extern const InfraredCommonProtocolSpec infrared_protocol_samsung32; diff --git a/lib/infrared/encoder_decoder/samsung/infrared_samsung_spec.c b/lib/infrared/encoder_decoder/samsung/infrared_samsung_spec.c deleted file mode 100644 index f4cbf699ec8..00000000000 --- a/lib/infrared/encoder_decoder/samsung/infrared_samsung_spec.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "../infrared_i.h" -#include "infrared_protocol_defs_i.h" - -static const InfraredProtocolSpecification infrared_samsung32_protocol_specification = { - .name = "Samsung32", - .address_length = 8, - .command_length = 8, - .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, - .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, -}; - -const InfraredProtocolSpecification* infrared_samsung32_get_spec(InfraredProtocol protocol) { - if(protocol == InfraredProtocolSamsung32) - return &infrared_samsung32_protocol_specification; - else - return NULL; -} diff --git a/lib/infrared/encoder_decoder/sirc/infrared_decoder_sirc.c b/lib/infrared/encoder_decoder/sirc/infrared_decoder_sirc.c index 45f06c9422a..fc18a183ca7 100644 --- a/lib/infrared/encoder_decoder/sirc/infrared_decoder_sirc.c +++ b/lib/infrared/encoder_decoder/sirc/infrared_decoder_sirc.c @@ -1,10 +1,5 @@ -#include "common/infrared_common_i.h" -#include "infrared.h" -#include "infrared_protocol_defs_i.h" -#include -#include -#include -#include "../infrared_i.h" +#include "infrared_protocol_sirc_i.h" +#include InfraredMessage* infrared_decoder_sirc_check_ready(void* ctx) { return infrared_common_decoder_check_ready(ctx); @@ -44,7 +39,7 @@ bool infrared_decoder_sirc_interpret(InfraredCommonDecoder* decoder) { } void* infrared_decoder_sirc_alloc(void) { - return infrared_common_decoder_alloc(&protocol_sirc); + return infrared_common_decoder_alloc(&infrared_protocol_sirc); } InfraredMessage* infrared_decoder_sirc_decode(void* decoder, bool level, uint32_t duration) { diff --git a/lib/infrared/encoder_decoder/sirc/infrared_encoder_sirc.c b/lib/infrared/encoder_decoder/sirc/infrared_encoder_sirc.c index 2c2bda1afd8..39c2eb16678 100644 --- a/lib/infrared/encoder_decoder/sirc/infrared_encoder_sirc.c +++ b/lib/infrared/encoder_decoder/sirc/infrared_encoder_sirc.c @@ -1,10 +1,5 @@ +#include "infrared_protocol_sirc_i.h" #include -#include "infrared.h" -#include "common/infrared_common_i.h" -#include -#include "../infrared_i.h" -#include "infrared_protocol_defs_i.h" -#include void infrared_encoder_sirc_reset(void* encoder_ptr, const InfraredMessage* message) { furi_assert(encoder_ptr); @@ -28,7 +23,7 @@ void infrared_encoder_sirc_reset(void* encoder_ptr, const InfraredMessage* messa *data |= (message->address & 0x1FFF) << 7; encoder->bits_to_encode = 20; } else { - furi_assert(0); + furi_crash(); } } @@ -53,7 +48,7 @@ InfraredStatus infrared_encoder_sirc_encode_repeat( } void* infrared_encoder_sirc_alloc(void) { - return infrared_common_encoder_alloc(&protocol_sirc); + return infrared_common_encoder_alloc(&infrared_protocol_sirc); } void infrared_encoder_sirc_free(void* encoder_ptr) { diff --git a/lib/infrared/encoder_decoder/sirc/infrared_protocol_sirc.c b/lib/infrared/encoder_decoder/sirc/infrared_protocol_sirc.c new file mode 100644 index 00000000000..b527fba980c --- /dev/null +++ b/lib/infrared/encoder_decoder/sirc/infrared_protocol_sirc.c @@ -0,0 +1,64 @@ +#include "infrared_protocol_sirc_i.h" + +const InfraredCommonProtocolSpec infrared_protocol_sirc = { + .timings = + { + .preamble_mark = INFRARED_SIRC_PREAMBLE_MARK, + .preamble_space = INFRARED_SIRC_PREAMBLE_SPACE, + .bit1_mark = INFRARED_SIRC_BIT1_MARK, + .bit1_space = INFRARED_SIRC_BIT1_SPACE, + .bit0_mark = INFRARED_SIRC_BIT0_MARK, + .bit0_space = INFRARED_SIRC_BIT0_SPACE, + .preamble_tolerance = INFRARED_SIRC_PREAMBLE_TOLERANCE, + .bit_tolerance = INFRARED_SIRC_BIT_TOLERANCE, + .silence_time = INFRARED_SIRC_SILENCE, + .min_split_time = INFRARED_SIRC_MIN_SPLIT_TIME, + }, + .databit_len[0] = 20, + .databit_len[1] = 15, + .databit_len[2] = 12, + .no_stop_bit = true, + .decode = infrared_common_decode_pdwm, + .encode = infrared_common_encode_pdwm, + .interpret = infrared_decoder_sirc_interpret, + .decode_repeat = NULL, + .encode_repeat = infrared_encoder_sirc_encode_repeat, +}; + +static const InfraredProtocolVariant infrared_protocol_variant_sirc = { + .name = "SIRC", + .address_length = 5, + .command_length = 7, + .frequency = INFRARED_SIRC_CARRIER_FREQUENCY, + .duty_cycle = INFRARED_SIRC_DUTY_CYCLE, + .repeat_count = INFRARED_SIRC_REPEAT_COUNT_MIN, +}; + +static const InfraredProtocolVariant infrared_protocol_variant_sirc15 = { + .name = "SIRC15", + .address_length = 8, + .command_length = 7, + .frequency = INFRARED_SIRC_CARRIER_FREQUENCY, + .duty_cycle = INFRARED_SIRC_DUTY_CYCLE, + .repeat_count = INFRARED_SIRC_REPEAT_COUNT_MIN, +}; + +static const InfraredProtocolVariant infrared_protocol_variant_sirc20 = { + .name = "SIRC20", + .address_length = 13, + .command_length = 7, + .frequency = INFRARED_SIRC_CARRIER_FREQUENCY, + .duty_cycle = INFRARED_SIRC_DUTY_CYCLE, + .repeat_count = INFRARED_SIRC_REPEAT_COUNT_MIN, +}; + +const InfraredProtocolVariant* infrared_protocol_sirc_get_variant(InfraredProtocol protocol) { + if(protocol == InfraredProtocolSIRC) + return &infrared_protocol_variant_sirc; + else if(protocol == InfraredProtocolSIRC15) + return &infrared_protocol_variant_sirc15; + else if(protocol == InfraredProtocolSIRC20) + return &infrared_protocol_variant_sirc20; + else + return NULL; +} diff --git a/lib/infrared/encoder_decoder/sirc/infrared_protocol_sirc.h b/lib/infrared/encoder_decoder/sirc/infrared_protocol_sirc.h new file mode 100644 index 00000000000..0c3bcd8a2af --- /dev/null +++ b/lib/infrared/encoder_decoder/sirc/infrared_protocol_sirc.h @@ -0,0 +1,37 @@ +#pragma once + +#include "../infrared_i.h" + +/*************************************************************************************************** +* Sony SIRC protocol description +* https://www.sbprojects.net/knowledge/ir/sirc.php +* http://picprojects.org.uk/ +**************************************************************************************************** +* Preamble Preamble Pulse Width Modulation Pause Entirely repeat +* mark space up to period message.. +* +* 2400 600 12/15/20 bits (600,1200) ...45000 2400 600 +* __________ _ _ _ _ _ _ _ _ _ _ _ _ _ __________ _ _ +* ____ __________ _ _ _ __ __ __ _ _ __ __ _ _ ____________________ __________ _ +* | command | address | +* SIRC | 7b LSB | 5b LSB | +* SIRC15 | 7b LSB | 8b LSB | +* SIRC20 | 7b LSB | 13b LSB | +* +* No way to determine either next message is repeat or not, +* so recognize only fact message received. Sony remotes always send at least 3 messages. +* Assume 8 last extended bits for SIRC20 are address bits. +***************************************************************************************************/ + +void* infrared_decoder_sirc_alloc(void); +void infrared_decoder_sirc_reset(void* decoder); +InfraredMessage* infrared_decoder_sirc_check_ready(void* decoder); +void infrared_decoder_sirc_free(void* decoder); +InfraredMessage* infrared_decoder_sirc_decode(void* decoder, bool level, uint32_t duration); + +void* infrared_encoder_sirc_alloc(void); +void infrared_encoder_sirc_reset(void* encoder_ptr, const InfraredMessage* message); +void infrared_encoder_sirc_free(void* decoder); +InfraredStatus infrared_encoder_sirc_encode(void* encoder_ptr, uint32_t* duration, bool* polarity); + +const InfraredProtocolVariant* infrared_protocol_sirc_get_variant(InfraredProtocol protocol); diff --git a/lib/infrared/encoder_decoder/sirc/infrared_protocol_sirc_i.h b/lib/infrared/encoder_decoder/sirc/infrared_protocol_sirc_i.h new file mode 100644 index 00000000000..e38be9bc868 --- /dev/null +++ b/lib/infrared/encoder_decoder/sirc/infrared_protocol_sirc_i.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../common/infrared_common_i.h" + +#define INFRARED_SIRC_CARRIER_FREQUENCY 40000 +#define INFRARED_SIRC_DUTY_CYCLE 0.33 +#define INFRARED_SIRC_PREAMBLE_MARK 2400 +#define INFRARED_SIRC_PREAMBLE_SPACE 600 +#define INFRARED_SIRC_BIT1_MARK 1200 +#define INFRARED_SIRC_BIT1_SPACE 600 +#define INFRARED_SIRC_BIT0_MARK 600 +#define INFRARED_SIRC_BIT0_SPACE 600 +#define INFRARED_SIRC_PREAMBLE_TOLERANCE 200 // us +#define INFRARED_SIRC_BIT_TOLERANCE 120 // us +#define INFRARED_SIRC_SILENCE 10000 +#define INFRARED_SIRC_MIN_SPLIT_TIME (INFRARED_SIRC_SILENCE - 1000) +#define INFRARED_SIRC_REPEAT_PERIOD 45000 +#define INFRARED_SIRC_REPEAT_COUNT_MIN 3 + +extern const InfraredCommonProtocolSpec infrared_protocol_sirc; + +bool infrared_decoder_sirc_interpret(InfraredCommonDecoder* decoder); +InfraredStatus infrared_encoder_sirc_encode_repeat( + InfraredCommonEncoder* encoder, + uint32_t* duration, + bool* level); diff --git a/lib/infrared/encoder_decoder/sirc/infrared_sirc_spec.c b/lib/infrared/encoder_decoder/sirc/infrared_sirc_spec.c deleted file mode 100644 index 9bf35908e98..00000000000 --- a/lib/infrared/encoder_decoder/sirc/infrared_sirc_spec.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "../infrared_i.h" -#include "infrared_protocol_defs_i.h" - -static const InfraredProtocolSpecification infrared_sirc_protocol_specification = { - .name = "SIRC", - .address_length = 5, - .command_length = 7, - .frequency = INFRARED_SIRC_CARRIER_FREQUENCY, - .duty_cycle = INFRARED_SIRC_DUTY_CYCLE, -}; - -static const InfraredProtocolSpecification infrared_sirc15_protocol_specification = { - .name = "SIRC15", - .address_length = 8, - .command_length = 7, - .frequency = INFRARED_SIRC_CARRIER_FREQUENCY, - .duty_cycle = INFRARED_SIRC_DUTY_CYCLE, -}; - -static const InfraredProtocolSpecification infrared_sirc20_protocol_specification = { - .name = "SIRC20", - .address_length = 13, - .command_length = 7, - .frequency = INFRARED_SIRC_CARRIER_FREQUENCY, - .duty_cycle = INFRARED_SIRC_DUTY_CYCLE, -}; - -const InfraredProtocolSpecification* infrared_sirc_get_spec(InfraredProtocol protocol) { - if(protocol == InfraredProtocolSIRC) - return &infrared_sirc_protocol_specification; - else if(protocol == InfraredProtocolSIRC15) - return &infrared_sirc15_protocol_specification; - else if(protocol == InfraredProtocolSIRC20) - return &infrared_sirc20_protocol_specification; - else - return NULL; -} diff --git a/lib/infrared/worker/infrared_transmit.c b/lib/infrared/worker/infrared_transmit.c index 695be8d1f31..8f99c006621 100644 --- a/lib/infrared/worker/infrared_transmit.c +++ b/lib/infrared/worker/infrared_transmit.c @@ -67,7 +67,7 @@ void infrared_send_raw(const uint32_t timings[], uint32_t timings_cnt, bool star FuriHalInfraredTxGetDataState infrared_get_data_callback(void* context, uint32_t* duration, bool* level) { - FuriHalInfraredTxGetDataState state = FuriHalInfraredTxGetDataStateLastDone; + FuriHalInfraredTxGetDataState state; InfraredEncoderHandler* handler = context; InfraredStatus status = InfraredStatusError; @@ -82,12 +82,13 @@ FuriHalInfraredTxGetDataState } else if(status == InfraredStatusOk) { state = FuriHalInfraredTxGetDataStateOk; } else if(status == InfraredStatusDone) { - state = FuriHalInfraredTxGetDataStateDone; if(--infrared_tx_number_of_transmissions == 0) { state = FuriHalInfraredTxGetDataStateLastDone; + } else { + state = FuriHalInfraredTxGetDataStateDone; } } else { - furi_crash(NULL); + furi_crash(); } return state; @@ -100,7 +101,8 @@ void infrared_send(const InfraredMessage* message, int times) { InfraredEncoderHandler* handler = infrared_alloc_encoder(); infrared_reset_encoder(handler, message); - infrared_tx_number_of_transmissions = times; + infrared_tx_number_of_transmissions = + MAX((int)infrared_get_protocol_min_repeat_count(message->protocol), times); uint32_t frequency = infrared_get_protocol_frequency(message->protocol); float duty_cycle = infrared_get_protocol_duty_cycle(message->protocol); diff --git a/lib/infrared/worker/infrared_worker.c b/lib/infrared/worker/infrared_worker.c index 2b4e3cdbbf1..38392fc06ed 100644 --- a/lib/infrared/worker/infrared_worker.c +++ b/lib/infrared/worker/infrared_worker.c @@ -1,15 +1,12 @@ -#include -#include -#include "sys/_stdint.h" #include "infrared_worker.h" -#include + #include -#include -#include -#include +#include + +#include +#include #include -#include #define INFRARED_WORKER_RX_TIMEOUT INFRARED_RAW_RX_TIMING_DELAY_US @@ -43,14 +40,18 @@ struct InfraredWorkerSignal { size_t timings_cnt; union { InfraredMessage message; - /* +1 is for pause we add at the beginning */ - uint32_t timings[MAX_TIMINGS_AMOUNT + 1]; + struct { + /* +1 is for pause we add at the beginning */ + uint32_t timings[MAX_TIMINGS_AMOUNT + 1]; + uint32_t frequency; + float duty_cycle; + } raw; }; }; struct InfraredWorker { FuriThread* thread; - StreamBufferHandle_t stream; + FuriStreamBuffer* stream; InfraredWorkerSignal signal; InfraredWorkerState state; @@ -58,6 +59,7 @@ struct InfraredWorker { InfraredDecoderHandler* infrared_decoder; NotificationApp* notification; bool blink_enable; + bool decode_enable; union { struct { @@ -100,15 +102,13 @@ static void infrared_worker_rx_timeout_callback(void* context) { static void infrared_worker_rx_callback(void* context, bool level, uint32_t duration) { InfraredWorker* instance = context; - BaseType_t xHigherPriorityTaskWoken = pdFALSE; furi_assert(duration != 0); LevelDuration level_duration = level_duration_make(level, duration); - size_t ret = xStreamBufferSendFromISR( - instance->stream, &level_duration, sizeof(LevelDuration), &xHigherPriorityTaskWoken); + size_t ret = + furi_stream_buffer_send(instance->stream, &level_duration, sizeof(LevelDuration), 0); uint32_t events = (ret == sizeof(LevelDuration)) ? INFRARED_WORKER_RX_RECEIVED : INFRARED_WORKER_OVERRUN; - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); uint32_t flags_set = furi_thread_flags_set(furi_thread_get_id(instance->thread), events); furi_check(flags_set & events); @@ -134,7 +134,8 @@ static void infrared_worker_process_timeout(InfraredWorker* instance) { static void infrared_worker_process_timings(InfraredWorker* instance, uint32_t duration, bool level) { const InfraredMessage* message_decoded = - infrared_decode(instance->infrared_decoder, level, duration); + instance->decode_enable ? infrared_decode(instance->infrared_decoder, level, duration) : + NULL; if(message_decoded) { instance->signal.message = *message_decoded; instance->signal.timings_cnt = 0; @@ -149,7 +150,7 @@ static void } if(instance->signal.timings_cnt < MAX_TIMINGS_AMOUNT) { - instance->signal.timings[instance->signal.timings_cnt] = duration; + instance->signal.raw.timings[instance->signal.timings_cnt] = duration; ++instance->signal.timings_cnt; } else { uint32_t flags_set = furi_thread_flags_set( @@ -164,7 +165,7 @@ static int32_t infrared_worker_rx_thread(void* thread_context) { InfraredWorker* instance = thread_context; uint32_t events = 0; LevelDuration level_duration; - TickType_t last_blink_time = 0; + uint32_t last_blink_time = 0; while(1) { events = furi_thread_flags_wait(INFRARED_WORKER_ALL_RX_EVENTS, 0, FuriWaitForever); @@ -172,14 +173,14 @@ static int32_t infrared_worker_rx_thread(void* thread_context) { if(events & INFRARED_WORKER_RX_RECEIVED) { if(!instance->rx.overrun && instance->blink_enable && - ((xTaskGetTickCount() - last_blink_time) > 80)) { - last_blink_time = xTaskGetTickCount(); + ((furi_get_tick() - last_blink_time) > 80)) { + last_blink_time = furi_get_tick(); notification_message(instance->notification, &sequence_blink_blue_10); } if(instance->signal.timings_cnt == 0) notification_message(instance->notification, &sequence_display_backlight_on); while(sizeof(LevelDuration) == - xStreamBufferReceive( + furi_stream_buffer_receive( instance->stream, &level_duration, sizeof(LevelDuration), 0)) { if(!instance->rx.overrun) { bool level = level_duration_get_level(level_duration); @@ -224,18 +225,16 @@ void infrared_worker_rx_set_received_signal_callback( InfraredWorker* infrared_worker_alloc() { InfraredWorker* instance = malloc(sizeof(InfraredWorker)); - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "InfraredWorker"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_context(instance->thread, instance); + instance->thread = furi_thread_alloc_ex("InfraredWorker", 2048, NULL, instance); size_t buffer_size = MAX(sizeof(InfraredWorkerTiming) * (MAX_TIMINGS_AMOUNT + 1), sizeof(LevelDuration) * MAX_TIMINGS_AMOUNT); - instance->stream = xStreamBufferCreate(buffer_size, sizeof(InfraredWorkerTiming)); + instance->stream = furi_stream_buffer_alloc(buffer_size, sizeof(InfraredWorkerTiming)); instance->infrared_decoder = infrared_alloc_decoder(); instance->infrared_encoder = infrared_alloc_encoder(); instance->blink_enable = false; + instance->decode_enable = true; instance->notification = furi_record_open(RECORD_NOTIFICATION); instance->state = InfraredWorkerStateIdle; @@ -249,7 +248,7 @@ void infrared_worker_free(InfraredWorker* instance) { furi_record_close(RECORD_NOTIFICATION); infrared_free_decoder(instance->infrared_decoder); infrared_free_encoder(instance->infrared_encoder); - vStreamBufferDelete(instance->stream); + furi_stream_buffer_free(instance->stream); furi_thread_free(instance->thread); free(instance); @@ -259,7 +258,7 @@ void infrared_worker_rx_start(InfraredWorker* instance) { furi_assert(instance); furi_assert(instance->state == InfraredWorkerStateIdle); - xStreamBufferSetTriggerLevel(instance->stream, sizeof(LevelDuration)); + furi_stream_set_trigger_level(instance->stream, sizeof(LevelDuration)); furi_thread_set_callback(instance->thread, infrared_worker_rx_thread); furi_thread_start(instance->thread); @@ -285,9 +284,9 @@ void infrared_worker_rx_stop(InfraredWorker* instance) { furi_thread_flags_set(furi_thread_get_id(instance->thread), INFRARED_WORKER_EXIT); furi_thread_join(instance->thread); - BaseType_t xReturn = xStreamBufferReset(instance->stream); - furi_assert(xReturn == pdPASS); - (void)xReturn; + FuriStatus status = furi_stream_buffer_reset(instance->stream); + furi_assert(status == FuriStatusOk); + (void)status; instance->state = InfraredWorkerStateIdle; } @@ -305,7 +304,7 @@ void infrared_worker_get_raw_signal( furi_assert(timings); furi_assert(timings_cnt); - *timings = signal->timings; + *timings = signal->raw.timings; *timings_cnt = signal->timings_cnt; } @@ -319,13 +318,18 @@ void infrared_worker_rx_enable_blink_on_receiving(InfraredWorker* instance, bool instance->blink_enable = enable; } +void infrared_worker_rx_enable_signal_decoding(InfraredWorker* instance, bool enable) { + furi_assert(instance); + instance->decode_enable = enable; +} + void infrared_worker_tx_start(InfraredWorker* instance) { furi_assert(instance); furi_assert(instance->state == InfraredWorkerStateIdle); furi_assert(instance->tx.get_signal_callback); // size have to be greater than api hal infrared async tx buffer size - xStreamBufferSetTriggerLevel(instance->stream, sizeof(InfraredWorkerTiming)); + furi_stream_set_trigger_level(instance->stream, sizeof(InfraredWorkerTiming)); furi_thread_set_callback(instance->thread, infrared_worker_tx_thread); @@ -358,15 +362,16 @@ static FuriHalInfraredTxGetDataState FuriHalInfraredTxGetDataState state; if(sizeof(InfraredWorkerTiming) == - xStreamBufferReceiveFromISR(instance->stream, &timing, sizeof(InfraredWorkerTiming), 0)) { + furi_stream_buffer_receive(instance->stream, &timing, sizeof(InfraredWorkerTiming), 0)) { *level = timing.level; *duration = timing.duration; state = timing.state; } else { - furi_assert(0); + // Why bother if we crash anyway?.. *level = 0; *duration = 100; state = FuriHalInfraredTxGetDataStateDone; + furi_crash(); } uint32_t flags_set = furi_thread_flags_set( @@ -390,13 +395,14 @@ static bool infrared_get_new_signal(InfraredWorker* instance) { infrared_get_protocol_duty_cycle(instance->signal.message.protocol); } else { furi_assert(instance->signal.timings_cnt > 1); - new_tx_frequency = INFRARED_COMMON_CARRIER_FREQUENCY; - new_tx_duty_cycle = INFRARED_COMMON_DUTY_CYCLE; + new_tx_frequency = instance->signal.raw.frequency; + new_tx_duty_cycle = instance->signal.raw.duty_cycle; } instance->tx.tx_raw_cnt = 0; - instance->tx.need_reinitialization = (new_tx_frequency != instance->tx.frequency) || - (new_tx_duty_cycle != instance->tx.duty_cycle); + instance->tx.need_reinitialization = + (new_tx_frequency != instance->tx.frequency) || + !float_is_equal(new_tx_duty_cycle, instance->tx.duty_cycle); instance->tx.frequency = new_tx_frequency; instance->tx.duty_cycle = new_tx_duty_cycle; if(instance->signal.decoded) { @@ -409,7 +415,7 @@ static bool infrared_get_new_signal(InfraredWorker* instance) { } else if(response == InfraredWorkerGetSignalResponseStop) { new_signal_obtained = false; } else { - furi_assert(0); + furi_crash(); } return new_signal_obtained; @@ -420,12 +426,12 @@ static bool infrared_worker_tx_fill_buffer(InfraredWorker* instance) { InfraredWorkerTiming timing; InfraredStatus status = InfraredStatusError; - while(!xStreamBufferIsFull(instance->stream) && !instance->tx.need_reinitialization && + while(!furi_stream_buffer_is_full(instance->stream) && !instance->tx.need_reinitialization && new_data_available) { if(instance->signal.decoded) { status = infrared_encode(instance->infrared_encoder, &timing.duration, &timing.level); } else { - timing.duration = instance->signal.timings[instance->tx.tx_raw_cnt]; + timing.duration = instance->signal.raw.timings[instance->tx.tx_raw_cnt]; /* raw always starts from Mark, but we fill it with space delay at start */ timing.level = (instance->tx.tx_raw_cnt % 2); ++instance->tx.tx_raw_cnt; @@ -438,9 +444,8 @@ static bool infrared_worker_tx_fill_buffer(InfraredWorker* instance) { } if(status == InfraredStatusError) { - furi_assert(0); new_data_available = false; - break; + furi_crash(); } else if(status == InfraredStatusOk) { timing.state = FuriHalInfraredTxGetDataStateOk; } else if(status == InfraredStatusDone) { @@ -451,10 +456,10 @@ static bool infrared_worker_tx_fill_buffer(InfraredWorker* instance) { timing.state = FuriHalInfraredTxGetDataStateLastDone; } } else { - furi_assert(0); + furi_crash(); } uint32_t written_size = - xStreamBufferSend(instance->stream, &timing, sizeof(InfraredWorkerTiming), 0); + furi_stream_buffer_send(instance->stream, &timing, sizeof(InfraredWorkerTiming), 0); furi_assert(sizeof(InfraredWorkerTiming) == written_size); (void)written_size; } @@ -467,18 +472,23 @@ static int32_t infrared_worker_tx_thread(void* thread_context) { furi_assert(instance->state == InfraredWorkerStateStartTx); furi_assert(thread_context); + size_t repeats_left = + instance->signal.decoded ? + infrared_get_protocol_min_repeat_count(instance->signal.message.protocol) : + 1; uint32_t events = 0; - bool new_data_available = true; - bool exit = false; - exit = !infrared_get_new_signal(instance); - furi_assert(!exit); + bool exit_pending = false; + + bool running = infrared_get_new_signal(instance); + furi_assert(running); - while(!exit) { + while(running) { switch(instance->state) { case InfraredWorkerStateStartTx: + --repeats_left; /* The first message does not result in TX_MESSAGE_SENT event for some reason */ instance->tx.need_reinitialization = false; - new_data_available = infrared_worker_tx_fill_buffer(instance); + const bool new_data_available = infrared_worker_tx_fill_buffer(instance); furi_hal_infrared_async_tx_start(instance->tx.frequency, instance->tx.duty_cycle); if(!new_data_available) { @@ -492,7 +502,7 @@ static int32_t infrared_worker_tx_thread(void* thread_context) { break; case InfraredWorkerStateStopTx: furi_hal_infrared_async_tx_stop(); - exit = true; + running = false; break; case InfraredWorkerStateWaitTxEnd: furi_hal_infrared_async_tx_wait_termination(); @@ -500,18 +510,18 @@ static int32_t infrared_worker_tx_thread(void* thread_context) { events = furi_thread_flags_get(); if(events & INFRARED_WORKER_EXIT) { - exit = true; + running = false; break; } break; case InfraredWorkerStateRunTx: - events = furi_thread_flags_wait(INFRARED_WORKER_ALL_TX_EVENTS, 0, FuriWaitForever); + events = furi_thread_flags_wait( + INFRARED_WORKER_ALL_TX_EVENTS, FuriFlagWaitAny, FuriWaitForever); furi_check(events & INFRARED_WORKER_ALL_TX_EVENTS); /* at least one caught */ if(events & INFRARED_WORKER_EXIT) { - instance->state = InfraredWorkerStateStopTx; - break; + exit_pending = true; } if(events & INFRARED_WORKER_TX_FILL_BUFFER) { @@ -523,12 +533,22 @@ static int32_t infrared_worker_tx_thread(void* thread_context) { } if(events & INFRARED_WORKER_TX_MESSAGE_SENT) { - if(instance->tx.message_sent_callback) + if(repeats_left > 0) { + --repeats_left; + } + + if(instance->tx.message_sent_callback) { instance->tx.message_sent_callback(instance->tx.message_sent_context); + } + } + + if(exit_pending && repeats_left == 0) { + instance->state = InfraredWorkerStateStopTx; } + break; default: - furi_assert(0); + furi_crash(); break; } } @@ -564,10 +584,9 @@ void infrared_worker_tx_stop(InfraredWorker* instance) { furi_hal_infrared_async_tx_set_signal_sent_isr_callback(NULL, NULL); instance->signal.timings_cnt = 0; - BaseType_t xReturn = pdFAIL; - xReturn = xStreamBufferReset(instance->stream); - furi_assert(xReturn == pdPASS); - (void)xReturn; + FuriStatus status = furi_stream_buffer_reset(instance->stream); + furi_assert(status == FuriStatusOk); + (void)status; instance->state = InfraredWorkerStateIdle; } @@ -582,15 +601,21 @@ void infrared_worker_set_decoded_signal(InfraredWorker* instance, const Infrared void infrared_worker_set_raw_signal( InfraredWorker* instance, const uint32_t* timings, - size_t timings_cnt) { + size_t timings_cnt, + uint32_t frequency, + float duty_cycle) { furi_assert(instance); furi_assert(timings); furi_assert(timings_cnt > 0); - size_t max_copy_num = COUNT_OF(instance->signal.timings) - 1; + furi_assert((frequency <= INFRARED_MAX_FREQUENCY) && (frequency >= INFRARED_MIN_FREQUENCY)); + furi_assert((duty_cycle < 1.0f) && (duty_cycle > 0.0f)); + size_t max_copy_num = COUNT_OF(instance->signal.raw.timings) - 1; furi_check(timings_cnt <= max_copy_num); - instance->signal.timings[0] = INFRARED_RAW_TX_TIMING_DELAY_US; - memcpy(&instance->signal.timings[1], timings, timings_cnt * sizeof(uint32_t)); + instance->signal.raw.frequency = frequency; + instance->signal.raw.duty_cycle = duty_cycle; + instance->signal.raw.timings[0] = INFRARED_RAW_TX_TIMING_DELAY_US; + memcpy(&instance->signal.raw.timings[1], timings, timings_cnt * sizeof(uint32_t)); instance->signal.decoded = false; instance->signal.timings_cnt = timings_cnt + 1; } diff --git a/lib/infrared/worker/infrared_worker.h b/lib/infrared/worker/infrared_worker.h index c6617e501e3..e0e86198309 100644 --- a/lib/infrared/worker/infrared_worker.h +++ b/lib/infrared/worker/infrared_worker.h @@ -7,7 +7,7 @@ extern "C" { #endif -#define MAX_TIMINGS_AMOUNT 1024 +#define MAX_TIMINGS_AMOUNT 1024U /** Interface struct of infrared worker */ typedef struct InfraredWorker InfraredWorker; @@ -76,6 +76,14 @@ void infrared_worker_rx_set_received_signal_callback( */ void infrared_worker_rx_enable_blink_on_receiving(InfraredWorker* instance, bool enable); +/** Enable decoding of received infrared signals. + * + * @param[in] instance - instance of InfraredWorker + * @param[in] enable - true if you want to enable decoding + * false otherwise + */ +void infrared_worker_rx_enable_signal_decoding(InfraredWorker* instance, bool enable); + /** Clarify is received signal either decoded or raw * * @param[in] signal - received signal @@ -122,9 +130,9 @@ void infrared_worker_tx_set_signal_sent_callback( /** Callback to pass to infrared_worker_tx_set_get_signal_callback() if signal * is steady and will not be changed between infrared_worker start and stop. * Before starting transmission, desired steady signal must be set with - * infrared_worker_make_decoded_signal() or infrared_worker_make_raw_signal(). + * infrared_worker_set_decoded_signal() or infrared_worker_set_raw_signal(). * - * This function should not be implicitly called. + * This function should not be called directly. * * @param[in] context - context * @param[out] instance - InfraredWorker instance @@ -164,11 +172,15 @@ void infrared_worker_set_decoded_signal(InfraredWorker* instance, const Infrared * @param[out] instance - InfraredWorker instance * @param[in] timings - array of raw timings * @param[in] timings_cnt - size of array of raw timings + * @param[in] frequency - carrier frequency in Hertz + * @param[in] duty_cycle - carrier duty cycle (0.0 - 1.0) */ void infrared_worker_set_raw_signal( InfraredWorker* instance, const uint32_t* timings, - size_t timings_cnt); + size_t timings_cnt, + uint32_t frequency, + float duty_cycle); #ifdef __cplusplus } diff --git a/lib/lfrfid/SConscript b/lib/lfrfid/SConscript new file mode 100644 index 00000000000..f9431ca75f2 --- /dev/null +++ b/lib/lfrfid/SConscript @@ -0,0 +1,27 @@ +Import("env") + +env.Append( + LINT_SOURCES=[ + Dir("."), + ], + CPPPATH=[ + "#/lib/lfrfid", + ], + SDK_HEADERS=[ + File("lfrfid_worker.h"), + File("lfrfid_raw_worker.h"), + File("lfrfid_raw_file.h"), + File("lfrfid_dict_file.h"), + File("tools/bit_lib.h"), + File("protocols/lfrfid_protocols.h"), + ], +) + +libenv = env.Clone(FW_LIB_NAME="lfrfid") +libenv.ApplyLibFlags() + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/lfrfid/lfrfid_dict_file.c b/lib/lfrfid/lfrfid_dict_file.c new file mode 100644 index 00000000000..18bf505f0f5 --- /dev/null +++ b/lib/lfrfid/lfrfid_dict_file.c @@ -0,0 +1,182 @@ +#include "lfrfid_dict_file.h" +#include +#include +#include + +#define LFRFID_DICT_FILETYPE "Flipper RFID key" + +bool lfrfid_dict_file_save(ProtocolDict* dict, ProtocolId protocol, const char* filename) { + furi_check(protocol != PROTOCOL_NO); + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + size_t data_size = protocol_dict_get_data_size(dict, protocol); + uint8_t* data = malloc(data_size); + bool result = false; + + do { + if(!flipper_format_file_open_always(file, filename)) break; + if(!flipper_format_write_header_cstr(file, LFRFID_DICT_FILETYPE, 1)) break; + + // TODO FL-3517: write comment about protocol types into file + + if(!flipper_format_write_string_cstr( + file, "Key type", protocol_dict_get_name(dict, protocol))) + break; + + // TODO FL-3517: write comment about protocol sizes into file + + protocol_dict_get_data(dict, protocol, data, data_size); + + if(!flipper_format_write_hex(file, "Data", data, data_size)) break; + result = true; + } while(false); + + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); + free(data); + + return result; +} + +static void lfrfid_dict_protocol_indala_data( + uint8_t* data, + size_t data_size, + uint8_t* protocol_data, + size_t protocol_data_size) { + UNUSED(data_size); + memset(protocol_data, 0, protocol_data_size); + + // fc + bit_lib_set_bit(protocol_data, 24, bit_lib_get_bit(data, 0)); + bit_lib_set_bit(protocol_data, 16, bit_lib_get_bit(data, 1)); + bit_lib_set_bit(protocol_data, 11, bit_lib_get_bit(data, 2)); + bit_lib_set_bit(protocol_data, 14, bit_lib_get_bit(data, 3)); + bit_lib_set_bit(protocol_data, 15, bit_lib_get_bit(data, 4)); + bit_lib_set_bit(protocol_data, 20, bit_lib_get_bit(data, 5)); + bit_lib_set_bit(protocol_data, 6, bit_lib_get_bit(data, 6)); + bit_lib_set_bit(protocol_data, 25, bit_lib_get_bit(data, 7)); + + // cn + bit_lib_set_bit(protocol_data, 9, bit_lib_get_bit(data, 8 + 0)); + bit_lib_set_bit(protocol_data, 12, bit_lib_get_bit(data, 8 + 1)); + bit_lib_set_bit(protocol_data, 10, bit_lib_get_bit(data, 8 + 2)); + bit_lib_set_bit(protocol_data, 7, bit_lib_get_bit(data, 8 + 3)); + bit_lib_set_bit(protocol_data, 19, bit_lib_get_bit(data, 8 + 4)); + bit_lib_set_bit(protocol_data, 3, bit_lib_get_bit(data, 8 + 5)); + bit_lib_set_bit(protocol_data, 2, bit_lib_get_bit(data, 8 + 6)); + bit_lib_set_bit(protocol_data, 18, bit_lib_get_bit(data, 8 + 7)); + bit_lib_set_bit(protocol_data, 13, bit_lib_get_bit(data, 8 + 8)); + bit_lib_set_bit(protocol_data, 0, bit_lib_get_bit(data, 8 + 9)); + bit_lib_set_bit(protocol_data, 4, bit_lib_get_bit(data, 8 + 10)); + bit_lib_set_bit(protocol_data, 21, bit_lib_get_bit(data, 8 + 11)); + bit_lib_set_bit(protocol_data, 23, bit_lib_get_bit(data, 8 + 12)); + bit_lib_set_bit(protocol_data, 26, bit_lib_get_bit(data, 8 + 13)); + bit_lib_set_bit(protocol_data, 17, bit_lib_get_bit(data, 8 + 14)); + bit_lib_set_bit(protocol_data, 8, bit_lib_get_bit(data, 8 + 15)); + + const uint32_t fc_and_card = data[0] << 16 | data[1] << 8 | data[2]; + + // indala checksum + uint8_t checksum_sum = 0; + checksum_sum += ((fc_and_card >> 14) & 1); + checksum_sum += ((fc_and_card >> 12) & 1); + checksum_sum += ((fc_and_card >> 9) & 1); + checksum_sum += ((fc_and_card >> 8) & 1); + checksum_sum += ((fc_and_card >> 6) & 1); + checksum_sum += ((fc_and_card >> 5) & 1); + checksum_sum += ((fc_and_card >> 2) & 1); + checksum_sum += ((fc_and_card >> 0) & 1); + checksum_sum = checksum_sum & 0b1; + + if(checksum_sum) { + bit_lib_set_bit(protocol_data, 27, 0); + bit_lib_set_bit(protocol_data, 28, 1); + } else { + bit_lib_set_bit(protocol_data, 27, 1); + bit_lib_set_bit(protocol_data, 28, 0); + } + + // wiegand parity + uint8_t even_parity_sum = 0; + for(int8_t i = 12; i < 24; i++) { + if(((fc_and_card >> i) & 1) == 1) { + even_parity_sum++; + } + } + bit_lib_set_bit(protocol_data, 1, even_parity_sum % 2); + + uint8_t odd_parity_sum = 1; + for(int8_t i = 0; i < 12; i++) { + if(((fc_and_card >> i) & 1) == 1) { + odd_parity_sum++; + } + } + bit_lib_set_bit(protocol_data, 5, odd_parity_sum % 2); +} + +static ProtocolId lfrfid_dict_protocol_fallback( + ProtocolDict* dict, + const char* protocol_name, + FlipperFormat* file) { + ProtocolId result = PROTOCOL_NO; + if(strcmp(protocol_name, "I40134") == 0) { + ProtocolId protocol = LFRFIDProtocolIndala26; + + size_t data_size = 3; + size_t protocol_data_size = protocol_dict_get_data_size(dict, protocol); + uint8_t* data = malloc(data_size); + uint8_t* protocol_data = malloc(protocol_data_size); + if(flipper_format_read_hex(file, "Data", data, data_size)) { + lfrfid_dict_protocol_indala_data(data, data_size, protocol_data, protocol_data_size); + protocol_dict_set_data(dict, protocol, protocol_data, protocol_data_size); + result = protocol; + } + free(protocol_data); + free(data); + } + + return result; +} + +ProtocolId lfrfid_dict_file_load(ProtocolDict* dict, const char* filename) { + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + ProtocolId result = PROTOCOL_NO; + uint8_t* data = malloc(protocol_dict_get_max_data_size(dict)); + FuriString* str_result; + str_result = furi_string_alloc(); + + do { + if(!flipper_format_file_open_existing(file, filename)) break; + + // header + uint32_t version; + if(!flipper_format_read_header(file, str_result, &version)) break; + if(furi_string_cmp_str(str_result, LFRFID_DICT_FILETYPE) != 0) break; + if(version != 1) break; + + // type + if(!flipper_format_read_string(file, "Key type", str_result)) break; + ProtocolId protocol; + protocol = protocol_dict_get_protocol_by_name(dict, furi_string_get_cstr(str_result)); + + if(protocol == PROTOCOL_NO) { + protocol = lfrfid_dict_protocol_fallback(dict, furi_string_get_cstr(str_result), file); + if(protocol == PROTOCOL_NO) break; + } else { + // data + size_t data_size = protocol_dict_get_data_size(dict, protocol); + if(!flipper_format_read_hex(file, "Data", data, data_size)) break; + protocol_dict_set_data(dict, protocol, data, data_size); + } + + result = protocol; + } while(false); + + free(data); + furi_string_free(str_result); + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); + + return result; +} \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_dict_file.h b/lib/lfrfid/lfrfid_dict_file.h new file mode 100644 index 00000000000..077bb0ba138 --- /dev/null +++ b/lib/lfrfid/lfrfid_dict_file.h @@ -0,0 +1,31 @@ +#pragma once +#include +#include "protocols/lfrfid_protocols.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Save protocol from dictionary to file + * + * @param dict + * @param protocol + * @param filename + * @return true + * @return false + */ +bool lfrfid_dict_file_save(ProtocolDict* dict, ProtocolId protocol, const char* filename); + +/** + * @brief Load protocol from file to dictionary + * + * @param dict + * @param filename + * @return ProtocolId + */ +ProtocolId lfrfid_dict_file_load(ProtocolDict* dict, const char* filename); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_raw_file.c b/lib/lfrfid/lfrfid_raw_file.c new file mode 100644 index 00000000000..ca29770f189 --- /dev/null +++ b/lib/lfrfid/lfrfid_raw_file.c @@ -0,0 +1,145 @@ +#include "lfrfid_raw_file.h" +#include "tools/varint_pair.h" +#include +#include + +#define LFRFID_RAW_FILE_MAGIC 0x4C464952 +#define LFRFID_RAW_FILE_VERSION 1 + +#define TAG "LfRfidRawFile" + +typedef struct { + uint32_t magic; + uint32_t version; + float frequency; + float duty_cycle; + uint32_t max_buffer_size; +} LFRFIDRawFileHeader; + +struct LFRFIDRawFile { + Stream* stream; + uint32_t max_buffer_size; + + uint8_t* buffer; + uint32_t buffer_size; + size_t buffer_counter; +}; + +LFRFIDRawFile* lfrfid_raw_file_alloc(Storage* storage) { + LFRFIDRawFile* file = malloc(sizeof(LFRFIDRawFile)); + file->stream = file_stream_alloc(storage); + file->buffer = NULL; + return file; +} + +void lfrfid_raw_file_free(LFRFIDRawFile* file) { + if(file->buffer) free(file->buffer); + stream_free(file->stream); + free(file); +} + +bool lfrfid_raw_file_open_write(LFRFIDRawFile* file, const char* file_path) { + return file_stream_open(file->stream, file_path, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS); +} + +bool lfrfid_raw_file_open_read(LFRFIDRawFile* file, const char* file_path) { + return file_stream_open(file->stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING); +} + +bool lfrfid_raw_file_write_header( + LFRFIDRawFile* file, + float frequency, + float duty_cycle, + uint32_t max_buffer_size) { + LFRFIDRawFileHeader header = { + .magic = LFRFID_RAW_FILE_MAGIC, + .version = LFRFID_RAW_FILE_VERSION, + .frequency = frequency, + .duty_cycle = duty_cycle, + .max_buffer_size = max_buffer_size}; + + size_t size = stream_write(file->stream, (uint8_t*)&header, sizeof(LFRFIDRawFileHeader)); + return (size == sizeof(LFRFIDRawFileHeader)); +} + +bool lfrfid_raw_file_write_buffer(LFRFIDRawFile* file, uint8_t* buffer_data, size_t buffer_size) { + size_t size; + size = stream_write(file->stream, (uint8_t*)&buffer_size, sizeof(size_t)); + if(size != sizeof(size_t)) return false; + + size = stream_write(file->stream, buffer_data, buffer_size); + if(size != buffer_size) return false; + + return true; +} + +bool lfrfid_raw_file_read_header(LFRFIDRawFile* file, float* frequency, float* duty_cycle) { + LFRFIDRawFileHeader header; + size_t size = stream_read(file->stream, (uint8_t*)&header, sizeof(LFRFIDRawFileHeader)); + if(size == sizeof(LFRFIDRawFileHeader)) { + if(header.magic == LFRFID_RAW_FILE_MAGIC && header.version == LFRFID_RAW_FILE_VERSION) { + *frequency = header.frequency; + *duty_cycle = header.duty_cycle; + file->max_buffer_size = header.max_buffer_size; + file->buffer = malloc(file->max_buffer_size); + file->buffer_size = 0; + file->buffer_counter = 0; + return true; + } else { + return false; + } + } else { + return false; + } +} + +bool lfrfid_raw_file_read_pair( + LFRFIDRawFile* file, + uint32_t* duration, + uint32_t* pulse, + bool* pass_end) { + size_t length = 0; + if(file->buffer_counter >= file->buffer_size) { + if(stream_eof(file->stream)) { + // rewind stream and pass header + stream_seek(file->stream, sizeof(LFRFIDRawFileHeader), StreamOffsetFromStart); + if(pass_end) *pass_end = true; + } + + length = stream_read(file->stream, (uint8_t*)&file->buffer_size, sizeof(size_t)); + if(length != sizeof(size_t)) { + FURI_LOG_E(TAG, "read pair: failed to read size"); + return false; + } + + if(file->buffer_size > file->max_buffer_size) { + FURI_LOG_E(TAG, "read pair: buffer size is too big"); + return false; + } + + length = stream_read(file->stream, file->buffer, file->buffer_size); + if(length != file->buffer_size) { + FURI_LOG_E(TAG, "read pair: failed to read data"); + return false; + } + + file->buffer_counter = 0; + } + + size_t size = 0; + bool result = varint_pair_unpack( + &file->buffer[file->buffer_counter], + (size_t)(file->buffer_size - file->buffer_counter), + pulse, + duration, + &size); + + if(result) { + file->buffer_counter += size; + } else { + FURI_LOG_E(TAG, "read pair: buffer is too small"); + return false; + } + + return true; +} \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_raw_file.h b/lib/lfrfid/lfrfid_raw_file.h new file mode 100644 index 00000000000..3f2f14e09f4 --- /dev/null +++ b/lib/lfrfid/lfrfid_raw_file.h @@ -0,0 +1,95 @@ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct LFRFIDRawFile LFRFIDRawFile; + +/** + * @brief Allocate a new LFRFIDRawFile instance + * + * @param storage + * @return LFRFIDRawFile* + */ +LFRFIDRawFile* lfrfid_raw_file_alloc(Storage* storage); + +/** + * @brief Free a LFRFIDRawFile instance + * + * @param file + */ +void lfrfid_raw_file_free(LFRFIDRawFile* file); + +/** + * @brief Open RAW file for writing + * + * @param file + * @param file_path + * @return bool + */ +bool lfrfid_raw_file_open_write(LFRFIDRawFile* file, const char* file_path); + +/** + * @brief Open RAW file for reading + * @param file + * @param file_path + * @return bool + */ +bool lfrfid_raw_file_open_read(LFRFIDRawFile* file, const char* file_path); + +/** + * @brief Write RAW file header + * + * @param file + * @param frequency + * @param duty_cycle + * @param max_buffer_size + * @return bool + */ +bool lfrfid_raw_file_write_header( + LFRFIDRawFile* file, + float frequency, + float duty_cycle, + uint32_t max_buffer_size); + +/** + * @brief Write data to RAW file + * + * @param file + * @param buffer_data + * @param buffer_size + * @return bool + */ +bool lfrfid_raw_file_write_buffer(LFRFIDRawFile* file, uint8_t* buffer_data, size_t buffer_size); + +/** + * @brief Read RAW file header + * + * @param file + * @param frequency + * @param duty_cycle + * @return bool + */ +bool lfrfid_raw_file_read_header(LFRFIDRawFile* file, float* frequency, float* duty_cycle); + +/** + * @brief Read varint-encoded pair from RAW file + * + * @param file + * @param duration + * @param pulse + * @param pass_end file was wrapped around, can be NULL + * @return bool + */ +bool lfrfid_raw_file_read_pair( + LFRFIDRawFile* file, + uint32_t* duration, + uint32_t* pulse, + bool* pass_end); + +#ifdef __cplusplus +} +#endif diff --git a/lib/lfrfid/lfrfid_raw_worker.c b/lib/lfrfid/lfrfid_raw_worker.c new file mode 100644 index 00000000000..344c2afa240 --- /dev/null +++ b/lib/lfrfid/lfrfid_raw_worker.c @@ -0,0 +1,344 @@ +#include +#include +#include +#include +#include "lfrfid_raw_worker.h" +#include "lfrfid_raw_file.h" +#include "tools/varint_pair.h" + +#define EMULATE_BUFFER_SIZE 1024 +#define RFID_DATA_BUFFER_SIZE 2048 +#define READ_DATA_BUFFER_COUNT 4 + +#define TAG_EMULATE "RawEmulate" + +// emulate mode +typedef struct { + size_t overrun_count; + FuriStreamBuffer* stream; +} RfidEmulateCtx; + +typedef struct { + uint32_t emulate_buffer_arr[EMULATE_BUFFER_SIZE]; + uint32_t emulate_buffer_ccr[EMULATE_BUFFER_SIZE]; + RfidEmulateCtx ctx; +} LFRFIDRawWorkerEmulateData; + +typedef enum { + HalfTransfer, + TransferComplete, +} LFRFIDRawEmulateDMAEvent; + +// read mode +#define READ_TEMP_DATA_SIZE 10 + +typedef struct { + BufferStream* stream; + VarintPair* pair; +} LFRFIDRawWorkerReadData; + +// main worker +struct LFRFIDRawWorker { + FuriString* file_path; + FuriThread* thread; + FuriEventFlag* events; + + LFRFIDWorkerEmulateRawCallback emulate_callback; + LFRFIDWorkerReadRawCallback read_callback; + void* context; + + float frequency; + float duty_cycle; +}; + +typedef enum { + LFRFIDRawWorkerEventStop, +} LFRFIDRawWorkerEvent; + +static int32_t lfrfid_raw_read_worker_thread(void* thread_context); +static int32_t lfrfid_raw_emulate_worker_thread(void* thread_context); + +LFRFIDRawWorker* lfrfid_raw_worker_alloc() { + LFRFIDRawWorker* worker = malloc(sizeof(LFRFIDRawWorker)); + + worker->thread = furi_thread_alloc_ex("LfrfidRawWorker", 2048, NULL, worker); + + worker->events = furi_event_flag_alloc(NULL); + + worker->file_path = furi_string_alloc(); + return worker; +} + +void lfrfid_raw_worker_free(LFRFIDRawWorker* worker) { + furi_thread_free(worker->thread); + furi_event_flag_free(worker->events); + furi_string_free(worker->file_path); + free(worker); +} + +void lfrfid_raw_worker_start_read( + LFRFIDRawWorker* worker, + const char* file_path, + float freq, + float duty_cycle, + LFRFIDWorkerReadRawCallback callback, + void* context) { + furi_check(furi_thread_get_state(worker->thread) == FuriThreadStateStopped); + + furi_string_set(worker->file_path, file_path); + + worker->frequency = freq; + worker->duty_cycle = duty_cycle; + worker->read_callback = callback; + worker->context = context; + + furi_thread_set_callback(worker->thread, lfrfid_raw_read_worker_thread); + + furi_thread_start(worker->thread); +} + +void lfrfid_raw_worker_start_emulate( + LFRFIDRawWorker* worker, + const char* file_path, + LFRFIDWorkerEmulateRawCallback callback, + void* context) { + furi_check(furi_thread_get_state(worker->thread) == FuriThreadStateStopped); + furi_string_set(worker->file_path, file_path); + worker->emulate_callback = callback; + worker->context = context; + furi_thread_set_callback(worker->thread, lfrfid_raw_emulate_worker_thread); + furi_thread_start(worker->thread); +} + +void lfrfid_raw_worker_stop(LFRFIDRawWorker* worker) { + worker->emulate_callback = NULL; + worker->context = NULL; + worker->read_callback = NULL; + furi_event_flag_set(worker->events, 1 << LFRFIDRawWorkerEventStop); + furi_thread_join(worker->thread); +} + +static void lfrfid_raw_worker_capture(bool level, uint32_t duration, void* context) { + LFRFIDRawWorkerReadData* ctx = context; + + bool need_to_send = varint_pair_pack(ctx->pair, level, duration); + + if(need_to_send) { + buffer_stream_send_from_isr( + ctx->stream, varint_pair_get_data(ctx->pair), varint_pair_get_size(ctx->pair)); + varint_pair_reset(ctx->pair); + } +} + +static int32_t lfrfid_raw_read_worker_thread(void* thread_context) { + LFRFIDRawWorker* worker = (LFRFIDRawWorker*)thread_context; + + Storage* storage = furi_record_open(RECORD_STORAGE); + LFRFIDRawFile* file = lfrfid_raw_file_alloc(storage); + const char* filename = furi_string_get_cstr(worker->file_path); + bool file_valid = lfrfid_raw_file_open_write(file, filename); + + LFRFIDRawWorkerReadData* data = malloc(sizeof(LFRFIDRawWorkerReadData)); + + data->stream = buffer_stream_alloc(RFID_DATA_BUFFER_SIZE, READ_DATA_BUFFER_COUNT); + data->pair = varint_pair_alloc(); + + if(file_valid) { + // write header + file_valid = lfrfid_raw_file_write_header( + file, worker->frequency, worker->duty_cycle, RFID_DATA_BUFFER_SIZE); + } + + if(file_valid) { + // setup carrier + furi_hal_rfid_tim_read_start(worker->frequency, worker->duty_cycle); + + // stabilize detector + furi_delay_ms(1500); + + // start capture + furi_hal_rfid_tim_read_capture_start(lfrfid_raw_worker_capture, data); + + while(1) { + Buffer* buffer = buffer_stream_receive(data->stream, 100); + + if(buffer != NULL) { + file_valid = lfrfid_raw_file_write_buffer( + file, buffer_get_data(buffer), buffer_get_size(buffer)); + buffer_reset(buffer); + } + + if(!file_valid) { + if(worker->read_callback != NULL) { + // message file_error to worker + worker->read_callback(LFRFIDWorkerReadRawFileError, worker->context); + } + break; + } + + if(buffer_stream_get_overrun_count(data->stream) > 0 && + worker->read_callback != NULL) { + // message overrun to worker + worker->read_callback(LFRFIDWorkerReadRawOverrun, worker->context); + } + + uint32_t flags = furi_event_flag_get(worker->events); + if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) { + break; + } + } + + furi_hal_rfid_tim_read_capture_stop(); + furi_hal_rfid_tim_read_stop(); + } else { + if(worker->read_callback != NULL) { + // message file_error to worker + worker->read_callback(LFRFIDWorkerReadRawFileError, worker->context); + } + } + + if(!file_valid) { + const uint32_t available_flags = (1 << LFRFIDRawWorkerEventStop); + while(true) { + uint32_t flags = furi_event_flag_wait( + worker->events, available_flags, FuriFlagWaitAny, FuriWaitForever); + + if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) { + break; + } + } + } + + varint_pair_free(data->pair); + buffer_stream_free(data->stream); + lfrfid_raw_file_free(file); + furi_record_close(RECORD_STORAGE); + free(data); + + return 0; +} + +static void rfid_emulate_dma_isr(bool half, void* context) { + RfidEmulateCtx* ctx = context; + + uint32_t flag = half ? HalfTransfer : TransferComplete; + size_t len = furi_stream_buffer_send(ctx->stream, &flag, sizeof(uint32_t), 0); + if(len != sizeof(uint32_t)) { + ctx->overrun_count++; + } +} + +static int32_t lfrfid_raw_emulate_worker_thread(void* thread_context) { + LFRFIDRawWorker* worker = thread_context; + + bool file_valid = true; + + LFRFIDRawWorkerEmulateData* data = malloc(sizeof(LFRFIDRawWorkerEmulateData)); + + Storage* storage = furi_record_open(RECORD_STORAGE); + data->ctx.overrun_count = 0; + data->ctx.stream = furi_stream_buffer_alloc(sizeof(uint32_t), sizeof(uint32_t)); + + LFRFIDRawFile* file = lfrfid_raw_file_alloc(storage); + + do { + file_valid = lfrfid_raw_file_open_read(file, furi_string_get_cstr(worker->file_path)); + if(!file_valid) break; + file_valid = lfrfid_raw_file_read_header(file, &worker->frequency, &worker->duty_cycle); + if(!file_valid) break; + + for(size_t i = 0; i < EMULATE_BUFFER_SIZE; i++) { + file_valid = lfrfid_raw_file_read_pair( + file, &data->emulate_buffer_arr[i], &data->emulate_buffer_ccr[i], NULL); + if(!file_valid) break; + data->emulate_buffer_arr[i] /= 8; + data->emulate_buffer_arr[i] -= 1; + data->emulate_buffer_ccr[i] /= 8; + } + } while(false); + + furi_hal_rfid_tim_emulate_dma_start( + data->emulate_buffer_arr, + data->emulate_buffer_ccr, + EMULATE_BUFFER_SIZE, + rfid_emulate_dma_isr, + &data->ctx); + + if(!file_valid && worker->emulate_callback != NULL) { + // message file_error to worker + worker->emulate_callback(LFRFIDWorkerEmulateRawFileError, worker->context); + } + + if(file_valid) { + uint32_t flag = 0; + + while(true) { + size_t size = + furi_stream_buffer_receive(data->ctx.stream, &flag, sizeof(uint32_t), 100); + + if(size == sizeof(uint32_t)) { + size_t start = 0; + if(flag == TransferComplete) { + start = (EMULATE_BUFFER_SIZE / 2); + } + + for(size_t i = 0; i < (EMULATE_BUFFER_SIZE / 2); i++) { + file_valid = lfrfid_raw_file_read_pair( + file, + &data->emulate_buffer_arr[start + i], + &data->emulate_buffer_ccr[start + i], + NULL); + if(!file_valid) break; + data->emulate_buffer_arr[i] /= 8; + data->emulate_buffer_arr[i] -= 1; + data->emulate_buffer_ccr[i] /= 8; + } + } else if(size != 0) { + data->ctx.overrun_count++; + } + + if(!file_valid) { + if(worker->emulate_callback != NULL) { + // message file_error to worker + worker->emulate_callback(LFRFIDWorkerEmulateRawFileError, worker->context); + } + break; + } + + if(data->ctx.overrun_count > 0 && worker->emulate_callback != NULL) { + // message overrun to worker + worker->emulate_callback(LFRFIDWorkerEmulateRawOverrun, worker->context); + } + + uint32_t flags = furi_event_flag_get(worker->events); + if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) { + break; + }; + } + } + + furi_hal_rfid_tim_emulate_dma_stop(); + + if(!file_valid) { + const uint32_t available_flags = (1 << LFRFIDRawWorkerEventStop); + while(true) { + uint32_t flags = furi_event_flag_wait( + worker->events, available_flags, FuriFlagWaitAny, FuriWaitForever); + + if(FURI_BIT(flags, LFRFIDRawWorkerEventStop)) { + break; + }; + } + } + + if(data->ctx.overrun_count) { + FURI_LOG_E(TAG_EMULATE, "overruns: %zu", data->ctx.overrun_count); + } + + furi_stream_buffer_free(data->ctx.stream); + lfrfid_raw_file_free(file); + furi_record_close(RECORD_STORAGE); + free(data); + + return 0; +} diff --git a/lib/lfrfid/lfrfid_raw_worker.h b/lib/lfrfid/lfrfid_raw_worker.h new file mode 100644 index 00000000000..1195dd58790 --- /dev/null +++ b/lib/lfrfid/lfrfid_raw_worker.h @@ -0,0 +1,68 @@ +#pragma once +#include +#include "lfrfid_worker.h" +#include +#include "protocols/lfrfid_protocols.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct LFRFIDRawWorker LFRFIDRawWorker; + +/** + * @brief Allocate a new LFRFIDRawWorker instance + * + * @return LFRFIDRawWorker* + */ +LFRFIDRawWorker* lfrfid_raw_worker_alloc(); + +/** + * @brief Free a LFRFIDRawWorker instance + * + * @param worker LFRFIDRawWorker instance + */ +void lfrfid_raw_worker_free(LFRFIDRawWorker* worker); + +/** + * @brief Start reading + * + * @param worker LFRFIDRawWorker instance + * @param file_path path where file will be saved + * @param frequency HW frequency + * @param duty_cycle HW duty cycle + * @param callback callback for read event + * @param context context for callback + */ +void lfrfid_raw_worker_start_read( + LFRFIDRawWorker* worker, + const char* file_path, + float frequency, + float duty_cycle, + LFRFIDWorkerReadRawCallback callback, + void* context); + +/** + * @brief Start emulate + * + * @param worker LFRFIDRawWorker instance + * @param file_path path to file that will be emulated + * @param callback callback for emulate event + * @param context context for callback + */ +void lfrfid_raw_worker_start_emulate( + LFRFIDRawWorker* worker, + const char* file_path, + LFRFIDWorkerEmulateRawCallback callback, + void* context); + +/** + * @brief Stop worker + * + * @param worker + */ +void lfrfid_raw_worker_stop(LFRFIDRawWorker* worker); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/lfrfid/lfrfid_worker.c b/lib/lfrfid/lfrfid_worker.c new file mode 100644 index 00000000000..ffaa8ee925f --- /dev/null +++ b/lib/lfrfid/lfrfid_worker.c @@ -0,0 +1,163 @@ +#include "lfrfid_worker_i.h" + +#include +#include + +typedef enum { + LFRFIDEventStopThread = (1 << 0), + LFRFIDEventStopMode = (1 << 1), + LFRFIDEventRead = (1 << 2), + LFRFIDEventWrite = (1 << 3), + LFRFIDEventEmulate = (1 << 4), + LFRFIDEventReadRaw = (1 << 5), + LFRFIDEventEmulateRaw = (1 << 6), + LFRFIDEventAll = + (LFRFIDEventStopThread | LFRFIDEventStopMode | LFRFIDEventRead | LFRFIDEventWrite | + LFRFIDEventEmulate | LFRFIDEventReadRaw | LFRFIDEventEmulateRaw), +} LFRFIDEventType; + +static int32_t lfrfid_worker_thread(void* thread_context); + +LFRFIDWorker* lfrfid_worker_alloc(ProtocolDict* dict) { + furi_assert(dict); + + LFRFIDWorker* worker = malloc(sizeof(LFRFIDWorker)); + worker->mode_index = LFRFIDWorkerIdle; + worker->read_cb = NULL; + worker->write_cb = NULL; + worker->cb_ctx = NULL; + worker->raw_filename = NULL; + worker->mode_storage = NULL; + + worker->thread = furi_thread_alloc_ex("LfrfidWorker", 2048, lfrfid_worker_thread, worker); + + worker->protocols = dict; + + return worker; +} + +void lfrfid_worker_free(LFRFIDWorker* worker) { + if(worker->raw_filename) { + free(worker->raw_filename); + } + + furi_thread_free(worker->thread); + free(worker); +} + +void lfrfid_worker_read_start( + LFRFIDWorker* worker, + LFRFIDWorkerReadType type, + LFRFIDWorkerReadCallback callback, + void* context) { + furi_assert(worker->mode_index == LFRFIDWorkerIdle); + worker->read_type = type; + worker->read_cb = callback; + worker->cb_ctx = context; + furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventRead); +} + +void lfrfid_worker_write_start( + LFRFIDWorker* worker, + LFRFIDProtocol protocol, + LFRFIDWorkerWriteCallback callback, + void* context) { + furi_assert(worker->mode_index == LFRFIDWorkerIdle); + worker->protocol = protocol; + worker->write_cb = callback; + worker->cb_ctx = context; + furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventWrite); +} + +void lfrfid_worker_emulate_start(LFRFIDWorker* worker, LFRFIDProtocol protocol) { + furi_assert(worker->mode_index == LFRFIDWorkerIdle); + worker->protocol = protocol; + furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventEmulate); +} + +void lfrfid_worker_set_filename(LFRFIDWorker* worker, const char* filename) { + if(worker->raw_filename) { + free(worker->raw_filename); + } + + worker->raw_filename = strdup(filename); +} + +void lfrfid_worker_read_raw_start( + LFRFIDWorker* worker, + const char* filename, + LFRFIDWorkerReadType type, + LFRFIDWorkerReadRawCallback callback, + void* context) { + furi_assert(worker->mode_index == LFRFIDWorkerIdle); + worker->read_type = type; + worker->read_raw_cb = callback; + worker->cb_ctx = context; + lfrfid_worker_set_filename(worker, filename); + furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventReadRaw); +} + +void lfrfid_worker_emulate_raw_start( + LFRFIDWorker* worker, + const char* filename, + LFRFIDWorkerEmulateRawCallback callback, + void* context) { + furi_assert(worker->mode_index == LFRFIDWorkerIdle); + lfrfid_worker_set_filename(worker, filename); + worker->emulate_raw_cb = callback; + worker->cb_ctx = context; + furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventEmulateRaw); +} + +void lfrfid_worker_stop(LFRFIDWorker* worker) { + furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventStopMode); +} + +void lfrfid_worker_start_thread(LFRFIDWorker* worker) { + furi_thread_start(worker->thread); +} + +void lfrfid_worker_stop_thread(LFRFIDWorker* worker) { + furi_thread_flags_set(furi_thread_get_id(worker->thread), LFRFIDEventStopThread); + furi_thread_join(worker->thread); +} + +bool lfrfid_worker_check_for_stop(LFRFIDWorker* worker) { + UNUSED(worker); + uint32_t flags = furi_thread_flags_get(); + return (flags & LFRFIDEventStopMode); +} + +size_t lfrfid_worker_dict_get_data_size(LFRFIDWorker* worker, LFRFIDProtocol protocol) { + furi_assert(worker->mode_index == LFRFIDWorkerIdle); + return protocol_dict_get_data_size(worker->protocols, protocol); +} + +static int32_t lfrfid_worker_thread(void* thread_context) { + LFRFIDWorker* worker = thread_context; + + while(true) { + uint32_t flags = furi_thread_flags_wait(LFRFIDEventAll, FuriFlagWaitAny, FuriWaitForever); + if(flags != (unsigned)FuriFlagErrorTimeout) { + // stop thread + if(flags & LFRFIDEventStopThread) break; + + // switch mode + if(flags & LFRFIDEventRead) worker->mode_index = LFRFIDWorkerRead; + if(flags & LFRFIDEventWrite) worker->mode_index = LFRFIDWorkerWrite; + if(flags & LFRFIDEventEmulate) worker->mode_index = LFRFIDWorkerEmulate; + if(flags & LFRFIDEventReadRaw) worker->mode_index = LFRFIDWorkerReadRaw; + if(flags & LFRFIDEventEmulateRaw) worker->mode_index = LFRFIDWorkerEmulateRaw; + + // do mode, if it exists + if(lfrfid_worker_modes[worker->mode_index].process) { + lfrfid_worker_modes[worker->mode_index].process(worker); + } + + // reset mode + worker->mode_index = LFRFIDWorkerIdle; + } + } + + return 0; +} diff --git a/lib/lfrfid/lfrfid_worker.h b/lib/lfrfid/lfrfid_worker.h new file mode 100644 index 00000000000..22135097e26 --- /dev/null +++ b/lib/lfrfid/lfrfid_worker.h @@ -0,0 +1,152 @@ +/** + * @file lfrfid_worker.h + * + * LFRFID worker + */ + +#pragma once +#include +#include "protocols/lfrfid_protocols.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + LFRFIDWorkerWriteOK, + LFRFIDWorkerWriteProtocolCannotBeWritten, + LFRFIDWorkerWriteFobCannotBeWritten, + LFRFIDWorkerWriteTooLongToWrite, +} LFRFIDWorkerWriteResult; + +typedef enum { + LFRFIDWorkerReadTypeAuto, + LFRFIDWorkerReadTypeASKOnly, + LFRFIDWorkerReadTypePSKOnly, +} LFRFIDWorkerReadType; + +typedef enum { + LFRFIDWorkerReadSenseStart, // TODO FL-3516: not implemented + LFRFIDWorkerReadSenseEnd, // TODO FL-3516: not implemented + LFRFIDWorkerReadSenseCardStart, + LFRFIDWorkerReadSenseCardEnd, + LFRFIDWorkerReadStartASK, + LFRFIDWorkerReadStartPSK, + LFRFIDWorkerReadDone, +} LFRFIDWorkerReadResult; + +typedef enum { + LFRFIDWorkerReadRawFileError, + LFRFIDWorkerReadRawOverrun, +} LFRFIDWorkerReadRawResult; + +typedef enum { + LFRFIDWorkerEmulateRawFileError, + LFRFIDWorkerEmulateRawOverrun, +} LFRFIDWorkerEmulateRawResult; + +typedef void ( + *LFRFIDWorkerReadCallback)(LFRFIDWorkerReadResult result, ProtocolId protocol, void* context); +typedef void (*LFRFIDWorkerWriteCallback)(LFRFIDWorkerWriteResult result, void* context); + +typedef void (*LFRFIDWorkerReadRawCallback)(LFRFIDWorkerReadRawResult result, void* context); +typedef void (*LFRFIDWorkerEmulateRawCallback)(LFRFIDWorkerEmulateRawResult result, void* context); + +typedef struct LFRFIDWorker LFRFIDWorker; + +/** + * Allocate LF-RFID worker + * @return LFRFIDWorker* + */ +LFRFIDWorker* lfrfid_worker_alloc(ProtocolDict* dict); + +/** + * Free LF-RFID worker + * @param worker + */ +void lfrfid_worker_free(LFRFIDWorker* worker); + +/** + * Start LF-RFID worker thread + * @param worker + */ +void lfrfid_worker_start_thread(LFRFIDWorker* worker); + +/** + * Stop LF-RFID worker thread + * @param worker + */ +void lfrfid_worker_stop_thread(LFRFIDWorker* worker); + +/** + * @brief Start read mode + * + * @param worker + * @param type + * @param callback + * @param context + */ +void lfrfid_worker_read_start( + LFRFIDWorker* worker, + LFRFIDWorkerReadType type, + LFRFIDWorkerReadCallback callback, + void* context); + +/** + * @brief Start write mode + * + * @param worker + * @param protocol + * @param callback + * @param context + */ +void lfrfid_worker_write_start( + LFRFIDWorker* worker, + LFRFIDProtocol protocol, + LFRFIDWorkerWriteCallback callback, + void* context); + +/** + * Start emulate mode + * @param worker + */ +void lfrfid_worker_emulate_start(LFRFIDWorker* worker, LFRFIDProtocol protocol); + +/** + * @brief Start raw read mode + * + * @param worker + * @param filename + * @param type + * @param callback + * @param context + */ +void lfrfid_worker_read_raw_start( + LFRFIDWorker* worker, + const char* filename, + LFRFIDWorkerReadType type, + LFRFIDWorkerReadRawCallback callback, + void* context); + +/** + * Emulate raw read mode + * @param worker + * @param filename + * @param callback + * @param context + */ +void lfrfid_worker_emulate_raw_start( + LFRFIDWorker* worker, + const char* filename, + LFRFIDWorkerEmulateRawCallback callback, + void* context); + +/** + * Stop all modes + * @param worker + */ +void lfrfid_worker_stop(LFRFIDWorker* worker); + +#ifdef __cplusplus +} +#endif diff --git a/lib/lfrfid/lfrfid_worker_i.h b/lib/lfrfid/lfrfid_worker_i.h new file mode 100644 index 00000000000..33c0bff0851 --- /dev/null +++ b/lib/lfrfid/lfrfid_worker_i.h @@ -0,0 +1,64 @@ +/** + * @file lfrfid_worker_i.h + * + * lfrfid worker, internal definitions + */ + +#pragma once +#include +#include "lfrfid_worker.h" +#include "lfrfid_raw_worker.h" +#include "protocols/lfrfid_protocols.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + void (*const process)(LFRFIDWorker* worker); +} LFRFIDWorkerModeType; + +typedef enum { + LFRFIDWorkerIdle, + LFRFIDWorkerRead, + LFRFIDWorkerWrite, + LFRFIDWorkerEmulate, + LFRFIDWorkerReadRaw, + LFRFIDWorkerEmulateRaw, +} LFRFIDWorkerMode; + +struct LFRFIDWorker { + char* raw_filename; + + LFRFIDWorkerMode mode_index; + void* mode_storage; + + FuriEventFlag* events; + FuriThread* thread; + + LFRFIDWorkerReadType read_type; + + LFRFIDWorkerReadCallback read_cb; + LFRFIDWorkerWriteCallback write_cb; + LFRFIDWorkerReadRawCallback read_raw_cb; + LFRFIDWorkerEmulateRawCallback emulate_raw_cb; + + void* cb_ctx; + + ProtocolDict* protocols; + LFRFIDProtocol protocol; +}; + +extern const LFRFIDWorkerModeType lfrfid_worker_modes[]; + +/** + * @brief Check for stop flag + * + * @param worker + * @return bool + */ +bool lfrfid_worker_check_for_stop(LFRFIDWorker* worker); + +#ifdef __cplusplus +} +#endif diff --git a/lib/lfrfid/lfrfid_worker_modes.c b/lib/lfrfid/lfrfid_worker_modes.c new file mode 100644 index 00000000000..32e2532590c --- /dev/null +++ b/lib/lfrfid/lfrfid_worker_modes.c @@ -0,0 +1,635 @@ +#include +#include +#include "lfrfid_worker_i.h" +#include "tools/t5577.h" +#include +#include +#include "tools/varint_pair.h" +#include "tools/bit_lib.h" + +#define TAG "LfRfidWorker" + +/** + * if READ_DEBUG_GPIO is defined: + * gpio_ext_pa7 will repeat signal coming from the comparator + * gpio_ext_pa6 will show load on the decoder + */ +// #define LFRFID_WORKER_READ_DEBUG_GPIO 1 + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO +#define LFRFID_WORKER_READ_DEBUG_GPIO_VALUE &gpio_ext_pa7 +#define LFRFID_WORKER_READ_DEBUG_GPIO_LOAD &gpio_ext_pa6 +#endif + +#define LFRFID_WORKER_READ_AVERAGE_COUNT 64 +#define LFRFID_WORKER_READ_MIN_TIME_US 16 + +#define LFRFID_WORKER_READ_DROP_TIME_MS 50 +#define LFRFID_WORKER_READ_STABILIZE_TIME_MS 450 +#define LFRFID_WORKER_READ_SWITCH_TIME_MS 2000 + +#define LFRFID_WORKER_WRITE_VERIFY_TIME_MS 2000 +#define LFRFID_WORKER_WRITE_DROP_TIME_MS 50 +#define LFRFID_WORKER_WRITE_TOO_LONG_TIME_MS 10000 + +#define LFRFID_WORKER_WRITE_MAX_UNSUCCESSFUL_READS 5 + +#define LFRFID_WORKER_READ_BUFFER_SIZE 512 +#define LFRFID_WORKER_READ_BUFFER_COUNT 16 + +#define LFRFID_WORKER_EMULATE_BUFFER_SIZE 1024 + +#define LFRFID_WORKER_DELAY_QUANT 50 + +void lfrfid_worker_delay(LFRFIDWorker* worker, uint32_t milliseconds) { + for(uint32_t i = 0; i < (milliseconds / LFRFID_WORKER_DELAY_QUANT); i++) { + if(lfrfid_worker_check_for_stop(worker)) break; + furi_delay_ms(LFRFID_WORKER_DELAY_QUANT); + } +} + +/**************************************************************************************************/ +/********************************************** READ **********************************************/ +/**************************************************************************************************/ + +typedef struct { + BufferStream* stream; + VarintPair* pair; + bool ignore_next_pulse; +} LFRFIDWorkerReadContext; + +static void lfrfid_worker_read_capture(bool level, uint32_t duration, void* context) { + LFRFIDWorkerReadContext* ctx = context; + + // ignore pulse if last pulse was noise + if(ctx->ignore_next_pulse) { + ctx->ignore_next_pulse = false; + return; + } + + // ignore noise spikes + if(duration <= LFRFID_WORKER_READ_MIN_TIME_US) { + if(level) { + ctx->ignore_next_pulse = true; + } + varint_pair_reset(ctx->pair); + return; + } + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_VALUE, level); +#endif + + bool need_to_send = varint_pair_pack(ctx->pair, level, duration); + if(need_to_send) { + buffer_stream_send_from_isr( + ctx->stream, varint_pair_get_data(ctx->pair), varint_pair_get_size(ctx->pair)); + varint_pair_reset(ctx->pair); + } +} + +typedef enum { + LFRFIDWorkerReadOK, + LFRFIDWorkerReadExit, + LFRFIDWorkerReadTimeout, +} LFRFIDWorkerReadState; + +static LFRFIDWorkerReadState lfrfid_worker_read_internal( + LFRFIDWorker* worker, + LFRFIDFeature feature, + uint32_t timeout, + ProtocolId* result_protocol) { + LFRFIDWorkerReadState state = LFRFIDWorkerReadTimeout; + + if(feature & LFRFIDFeatureASK) { + furi_hal_rfid_tim_read_start(125000, 0.5); + FURI_LOG_D(TAG, "Start ASK"); + if(worker->read_cb) { + worker->read_cb(LFRFIDWorkerReadStartASK, PROTOCOL_NO, worker->cb_ctx); + } + } else { + furi_hal_rfid_tim_read_start(62500, 0.25); + FURI_LOG_D(TAG, "Start PSK"); + if(worker->read_cb) { + worker->read_cb(LFRFIDWorkerReadStartPSK, PROTOCOL_NO, worker->cb_ctx); + } + } + + // stabilize detector + lfrfid_worker_delay(worker, LFRFID_WORKER_READ_STABILIZE_TIME_MS); + + protocol_dict_decoders_start(worker->protocols); + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_VALUE, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, GpioModeOutputPushPull); + furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_VALUE, false); + furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, false); +#endif + + LFRFIDWorkerReadContext ctx; + ctx.pair = varint_pair_alloc(); + ctx.stream = + buffer_stream_alloc(LFRFID_WORKER_READ_BUFFER_SIZE, LFRFID_WORKER_READ_BUFFER_COUNT); + + furi_hal_rfid_tim_read_capture_start(lfrfid_worker_read_capture, &ctx); + + *result_protocol = PROTOCOL_NO; + ProtocolId last_protocol = PROTOCOL_NO; + size_t last_size = protocol_dict_get_max_data_size(worker->protocols); + uint8_t* last_data = malloc(last_size); + uint8_t* protocol_data = malloc(last_size); + size_t last_read_count = 0; + + uint32_t switch_os_tick_last = furi_get_tick(); + + uint32_t average_duration = 0; + uint32_t average_pulse = 0; + size_t average_index = 0; + bool card_detected = false; + + FURI_LOG_D(TAG, "Read started"); + while(true) { + if(lfrfid_worker_check_for_stop(worker)) { + state = LFRFIDWorkerReadExit; + break; + } + + Buffer* buffer = buffer_stream_receive(ctx.stream, 100); + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, true); +#endif + + if(buffer_stream_get_overrun_count(ctx.stream) > 0) { + FURI_LOG_E(TAG, "Read overrun, recovering"); + buffer_stream_reset(ctx.stream); +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, false); +#endif + continue; + } + + if(buffer == NULL) { +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, false); +#endif + continue; + } + + size_t size = buffer_get_size(buffer); + uint8_t* data = buffer_get_data(buffer); + size_t index = 0; + + while(index < size) { + uint32_t duration; + uint32_t pulse; + size_t tmp_size; + + if(!varint_pair_unpack(&data[index], size - index, &pulse, &duration, &tmp_size)) { + FURI_LOG_E(TAG, "can't unpack varint pair"); + break; + } else { + index += tmp_size; + + average_duration += duration; + average_pulse += pulse; + average_index++; + if(average_index >= LFRFID_WORKER_READ_AVERAGE_COUNT) { + float average = (float)average_pulse / (float)average_duration; + average_pulse = 0; + average_duration = 0; + average_index = 0; + + if(worker->read_cb) { + if(average > 0.2f && average < 0.8f) { + if(!card_detected) { + card_detected = true; + worker->read_cb( + LFRFIDWorkerReadSenseStart, PROTOCOL_NO, worker->cb_ctx); + } + } else { + if(card_detected) { + card_detected = false; + worker->read_cb( + LFRFIDWorkerReadSenseEnd, PROTOCOL_NO, worker->cb_ctx); + } + } + } + } + + ProtocolId protocol = PROTOCOL_NO; + + protocol = protocol_dict_decoders_feed_by_feature( + worker->protocols, feature, true, pulse); + if(protocol == PROTOCOL_NO) { + protocol = protocol_dict_decoders_feed_by_feature( + worker->protocols, feature, false, duration - pulse); + } + + if(protocol != PROTOCOL_NO) { + // reset switch timer + switch_os_tick_last = furi_get_tick(); + + size_t protocol_data_size = + protocol_dict_get_data_size(worker->protocols, protocol); + protocol_dict_get_data( + worker->protocols, protocol, protocol_data, protocol_data_size); + + // validate protocol + if(protocol == last_protocol && + memcmp(last_data, protocol_data, protocol_data_size) == 0) { + last_read_count = last_read_count + 1; + + size_t validation_count = + protocol_dict_get_validate_count(worker->protocols, protocol); + + if(last_read_count >= validation_count) { + state = LFRFIDWorkerReadOK; + *result_protocol = protocol; + break; + } + } else { + if(last_protocol == PROTOCOL_NO && worker->read_cb) { + worker->read_cb( + LFRFIDWorkerReadSenseCardStart, protocol, worker->cb_ctx); + } + + last_protocol = protocol; + memcpy(last_data, protocol_data, protocol_data_size); + last_read_count = 0; + } + + if(furi_log_get_level() >= FuriLogLevelDebug) { + FuriString* string_info; + string_info = furi_string_alloc(); + for(uint8_t i = 0; i < protocol_data_size; i++) { + if(i != 0) { + furi_string_cat_printf(string_info, " "); + } + + furi_string_cat_printf(string_info, "%02X", protocol_data[i]); + } + + FURI_LOG_D( + TAG, + "%s, %zu, [%s]", + protocol_dict_get_name(worker->protocols, protocol), + last_read_count, + furi_string_get_cstr(string_info)); + furi_string_free(string_info); + } + + protocol_dict_decoders_start(worker->protocols); + } + } + } + + buffer_reset(buffer); + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, false); +#endif + + if(*result_protocol != PROTOCOL_NO) { + break; + } + + if((furi_get_tick() - switch_os_tick_last) > timeout) { + state = LFRFIDWorkerReadTimeout; + break; + } + } + + FURI_LOG_D(TAG, "Read stopped"); + + if(last_protocol != PROTOCOL_NO && worker->read_cb) { + worker->read_cb(LFRFIDWorkerReadSenseCardEnd, last_protocol, worker->cb_ctx); + } + + if(card_detected && worker->read_cb) { + worker->read_cb(LFRFIDWorkerReadSenseEnd, last_protocol, worker->cb_ctx); + } + + furi_hal_rfid_tim_read_capture_stop(); + furi_hal_rfid_tim_read_stop(); + furi_hal_rfid_pins_reset(); + + varint_pair_free(ctx.pair); + buffer_stream_free(ctx.stream); + + free(protocol_data); + free(last_data); + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_VALUE, false); + furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, false); + furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_VALUE, GpioModeAnalog); + furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, GpioModeAnalog); +#endif + + return state; +} + +static void lfrfid_worker_mode_read_process(LFRFIDWorker* worker) { + ProtocolId read_result = PROTOCOL_NO; + LFRFIDWorkerReadState state; + LFRFIDFeature feature; + + if(worker->read_type == LFRFIDWorkerReadTypePSKOnly) { + feature = LFRFIDFeaturePSK; + } else { + feature = LFRFIDFeatureASK; + } + + if(worker->read_type == LFRFIDWorkerReadTypeAuto) { + while(1) { + // read for a while + state = lfrfid_worker_read_internal( + worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); + + if(state == LFRFIDWorkerReadOK || state == LFRFIDWorkerReadExit) { + break; + } + + // switch to next feature + if(feature == LFRFIDFeatureASK) { + feature = LFRFIDFeaturePSK; + } else { + feature = LFRFIDFeatureASK; + } + + lfrfid_worker_delay(worker, LFRFID_WORKER_READ_DROP_TIME_MS); + } + } else { + while(1) { + if(worker->read_type == LFRFIDWorkerReadTypeASKOnly) { + state = lfrfid_worker_read_internal(worker, feature, UINT32_MAX, &read_result); + } else { + state = lfrfid_worker_read_internal( + worker, feature, LFRFID_WORKER_READ_SWITCH_TIME_MS, &read_result); + } + + if(state == LFRFIDWorkerReadOK || state == LFRFIDWorkerReadExit) { + break; + } + + lfrfid_worker_delay(worker, LFRFID_WORKER_READ_DROP_TIME_MS); + } + } + + if(state == LFRFIDWorkerReadOK && worker->read_cb) { + worker->read_cb(LFRFIDWorkerReadDone, read_result, worker->cb_ctx); + } +} + +/**************************************************************************************************/ +/******************************************** EMULATE *********************************************/ +/**************************************************************************************************/ + +typedef struct { + uint32_t duration[LFRFID_WORKER_EMULATE_BUFFER_SIZE]; + uint32_t pulse[LFRFID_WORKER_EMULATE_BUFFER_SIZE]; +} LFRFIDWorkerEmulateBuffer; + +typedef enum { + HalfTransfer, + TransferComplete, +} LFRFIDWorkerEmulateDMAEvent; + +static void lfrfid_worker_emulate_dma_isr(bool half, void* context) { + FuriStreamBuffer* stream = context; + uint32_t flag = half ? HalfTransfer : TransferComplete; + furi_stream_buffer_send(stream, &flag, sizeof(uint32_t), 0); +} + +static void lfrfid_worker_mode_emulate_process(LFRFIDWorker* worker) { + LFRFIDWorkerEmulateBuffer* buffer = malloc(sizeof(LFRFIDWorkerEmulateBuffer)); + FuriStreamBuffer* stream = furi_stream_buffer_alloc(sizeof(uint32_t), sizeof(uint32_t)); + LFRFIDProtocol protocol = worker->protocol; + PulseGlue* pulse_glue = pulse_glue_alloc(); + + protocol_dict_encoder_start(worker->protocols, protocol); + + for(size_t i = 0; i < LFRFID_WORKER_EMULATE_BUFFER_SIZE; i++) { + bool pulse_pop = false; + while(!pulse_pop) { + LevelDuration level_duration = + protocol_dict_encoder_yield(worker->protocols, protocol); + pulse_pop = pulse_glue_push( + pulse_glue, + level_duration_get_level(level_duration), + level_duration_get_duration(level_duration)); + } + uint32_t duration, pulse; + pulse_glue_pop(pulse_glue, &duration, &pulse); + buffer->duration[i] = duration - 1; + buffer->pulse[i] = pulse; + } + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, GpioModeOutputPushPull); +#endif + + furi_hal_rfid_tim_emulate_dma_start( + buffer->duration, + buffer->pulse, + LFRFID_WORKER_EMULATE_BUFFER_SIZE, + lfrfid_worker_emulate_dma_isr, + stream); + + while(true) { + uint32_t flag = 0; + size_t size = furi_stream_buffer_receive(stream, &flag, sizeof(uint32_t), 100); + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, true); +#endif + + if(size == sizeof(uint32_t)) { + size_t start = 0; + + if(flag == HalfTransfer) { + start = 0; + } else if(flag == TransferComplete) { + start = (LFRFID_WORKER_EMULATE_BUFFER_SIZE / 2); + } + + for(size_t i = 0; i < (LFRFID_WORKER_EMULATE_BUFFER_SIZE / 2); i++) { + bool pulse_pop = false; + while(!pulse_pop) { + LevelDuration level_duration = + protocol_dict_encoder_yield(worker->protocols, protocol); + pulse_pop = pulse_glue_push( + pulse_glue, + level_duration_get_level(level_duration), + level_duration_get_duration(level_duration)); + } + uint32_t duration, pulse; + pulse_glue_pop(pulse_glue, &duration, &pulse); + buffer->duration[start + i] = duration - 1; + buffer->pulse[start + i] = pulse; + } + } + + if(lfrfid_worker_check_for_stop(worker)) { + break; + } + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_write(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, false); +#endif + } + + furi_hal_rfid_tim_emulate_dma_stop(); + +#ifdef LFRFID_WORKER_READ_DEBUG_GPIO + furi_hal_gpio_init_simple(LFRFID_WORKER_READ_DEBUG_GPIO_LOAD, GpioModeAnalog); +#endif + + free(buffer); + furi_stream_buffer_free(stream); + pulse_glue_free(pulse_glue); +} + +/**************************************************************************************************/ +/********************************************* WRITE **********************************************/ +/**************************************************************************************************/ + +static void lfrfid_worker_mode_write_process(LFRFIDWorker* worker) { + LFRFIDProtocol protocol = worker->protocol; + LFRFIDWriteRequest* request = malloc(sizeof(LFRFIDWriteRequest)); + request->write_type = LFRFIDWriteTypeT5577; + + bool can_be_written = protocol_dict_get_write_data(worker->protocols, protocol, request); + + uint32_t write_start_time = furi_get_tick(); + bool too_long = false; + size_t unsuccessful_reads = 0; + + size_t data_size = protocol_dict_get_data_size(worker->protocols, protocol); + uint8_t* verify_data = malloc(data_size); + uint8_t* read_data = malloc(data_size); + protocol_dict_get_data(worker->protocols, protocol, verify_data, data_size); + + if(can_be_written) { + while(!lfrfid_worker_check_for_stop(worker)) { + FURI_LOG_D(TAG, "Data write"); + t5577_write(&request->t5577); + + ProtocolId read_result = PROTOCOL_NO; + LFRFIDWorkerReadState state = lfrfid_worker_read_internal( + worker, + protocol_dict_get_features(worker->protocols, protocol), + LFRFID_WORKER_WRITE_VERIFY_TIME_MS, + &read_result); + + if(state == LFRFIDWorkerReadOK) { + bool read_success = false; + + if(read_result == protocol) { + protocol_dict_get_data(worker->protocols, protocol, read_data, data_size); + + if(memcmp(read_data, verify_data, data_size) == 0) { + read_success = true; + } + } + + if(read_success) { + if(worker->write_cb) { + worker->write_cb(LFRFIDWorkerWriteOK, worker->cb_ctx); + } + break; + } else { + unsuccessful_reads++; + + if(unsuccessful_reads == LFRFID_WORKER_WRITE_MAX_UNSUCCESSFUL_READS) { + if(worker->write_cb) { + worker->write_cb(LFRFIDWorkerWriteFobCannotBeWritten, worker->cb_ctx); + } + } + } + } else if(state == LFRFIDWorkerReadExit) { + break; + } + + if(!too_long && + (furi_get_tick() - write_start_time) > LFRFID_WORKER_WRITE_TOO_LONG_TIME_MS) { + too_long = true; + if(worker->write_cb) { + worker->write_cb(LFRFIDWorkerWriteTooLongToWrite, worker->cb_ctx); + } + } + + lfrfid_worker_delay(worker, LFRFID_WORKER_WRITE_DROP_TIME_MS); + } + } else { + if(worker->write_cb) { + worker->write_cb(LFRFIDWorkerWriteProtocolCannotBeWritten, worker->cb_ctx); + } + } + + free(request); + free(verify_data); + free(read_data); +} + +/**************************************************************************************************/ +/******************************************* READ RAW *********************************************/ +/**************************************************************************************************/ + +static void lfrfid_worker_mode_read_raw_process(LFRFIDWorker* worker) { + LFRFIDRawWorker* raw_worker = lfrfid_raw_worker_alloc(); + + switch(worker->read_type) { + case LFRFIDWorkerReadTypePSKOnly: + lfrfid_raw_worker_start_read( + raw_worker, worker->raw_filename, 62500, 0.25, worker->read_raw_cb, worker->cb_ctx); + break; + case LFRFIDWorkerReadTypeASKOnly: + lfrfid_raw_worker_start_read( + raw_worker, worker->raw_filename, 125000, 0.5, worker->read_raw_cb, worker->cb_ctx); + break; + default: + furi_crash("RAW can be only PSK or ASK"); + break; + } + + while(!lfrfid_worker_check_for_stop(worker)) { + furi_delay_ms(100); + } + + lfrfid_raw_worker_stop(raw_worker); + lfrfid_raw_worker_free(raw_worker); +} + +/**************************************************************************************************/ +/***************************************** EMULATE RAW ********************************************/ +/**************************************************************************************************/ + +static void lfrfid_worker_mode_emulate_raw_process(LFRFIDWorker* worker) { + LFRFIDRawWorker* raw_worker = lfrfid_raw_worker_alloc(); + + lfrfid_raw_worker_start_emulate( + raw_worker, worker->raw_filename, worker->emulate_raw_cb, worker->cb_ctx); + + while(!lfrfid_worker_check_for_stop(worker)) { + furi_delay_ms(100); + } + + lfrfid_raw_worker_stop(raw_worker); + lfrfid_raw_worker_free(raw_worker); +} + +/**************************************************************************************************/ +/******************************************** MODES ***********************************************/ +/**************************************************************************************************/ + +const LFRFIDWorkerModeType lfrfid_worker_modes[] = { + [LFRFIDWorkerIdle] = {.process = NULL}, + [LFRFIDWorkerRead] = {.process = lfrfid_worker_mode_read_process}, + [LFRFIDWorkerWrite] = {.process = lfrfid_worker_mode_write_process}, + [LFRFIDWorkerEmulate] = {.process = lfrfid_worker_mode_emulate_process}, + [LFRFIDWorkerReadRaw] = {.process = lfrfid_worker_mode_read_raw_process}, + [LFRFIDWorkerEmulateRaw] = {.process = lfrfid_worker_mode_emulate_raw_process}, +}; diff --git a/lib/lfrfid/protocols/lfrfid_protocols.c b/lib/lfrfid/protocols/lfrfid_protocols.c new file mode 100644 index 00000000000..f07218d7f30 --- /dev/null +++ b/lib/lfrfid/protocols/lfrfid_protocols.c @@ -0,0 +1,40 @@ +#include "lfrfid_protocols.h" +#include "protocol_em4100.h" +#include "protocol_h10301.h" +#include "protocol_idteck.h" +#include "protocol_indala26.h" +#include "protocol_io_prox_xsf.h" +#include "protocol_awid.h" +#include "protocol_fdx_a.h" +#include "protocol_fdx_b.h" +#include "protocol_hid_generic.h" +#include "protocol_hid_ex_generic.h" +#include "protocol_pyramid.h" +#include "protocol_viking.h" +#include "protocol_jablotron.h" +#include "protocol_paradox.h" +#include "protocol_pac_stanley.h" +#include "protocol_keri.h" +#include "protocol_gallagher.h" +#include "protocol_nexwatch.h" + +const ProtocolBase* lfrfid_protocols[] = { + [LFRFIDProtocolEM4100] = &protocol_em4100, + [LFRFIDProtocolH10301] = &protocol_h10301, + [LFRFIDProtocolIdteck] = &protocol_idteck, + [LFRFIDProtocolIndala26] = &protocol_indala26, + [LFRFIDProtocolIOProxXSF] = &protocol_io_prox_xsf, + [LFRFIDProtocolAwid] = &protocol_awid, + [LFRFIDProtocolFDXA] = &protocol_fdx_a, + [LFRFIDProtocolFDXB] = &protocol_fdx_b, + [LFRFIDProtocolHidGeneric] = &protocol_hid_generic, + [LFRFIDProtocolHidExGeneric] = &protocol_hid_ex_generic, + [LFRFIDProtocolPyramid] = &protocol_pyramid, + [LFRFIDProtocolViking] = &protocol_viking, + [LFRFIDProtocolJablotron] = &protocol_jablotron, + [LFRFIDProtocolParadox] = &protocol_paradox, + [LFRFIDProtocolPACStanley] = &protocol_pac_stanley, + [LFRFIDProtocolKeri] = &protocol_keri, + [LFRFIDProtocolGallagher] = &protocol_gallagher, + [LFRFIDProtocolNexwatch] = &protocol_nexwatch, +}; diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h new file mode 100644 index 00000000000..0cb7cbc8440 --- /dev/null +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -0,0 +1,43 @@ +#pragma once +#include +#include "../tools/t5577.h" + +typedef enum { + LFRFIDFeatureASK = 1 << 0, /** ASK Demodulation */ + LFRFIDFeaturePSK = 1 << 1, /** PSK Demodulation */ +} LFRFIDFeature; + +typedef enum { + LFRFIDProtocolEM4100, + LFRFIDProtocolH10301, + LFRFIDProtocolIdteck, + LFRFIDProtocolIndala26, + LFRFIDProtocolIOProxXSF, + LFRFIDProtocolAwid, + LFRFIDProtocolFDXA, + LFRFIDProtocolFDXB, + LFRFIDProtocolHidGeneric, + LFRFIDProtocolHidExGeneric, + LFRFIDProtocolPyramid, + LFRFIDProtocolViking, + LFRFIDProtocolJablotron, + LFRFIDProtocolParadox, + LFRFIDProtocolPACStanley, + LFRFIDProtocolKeri, + LFRFIDProtocolGallagher, + LFRFIDProtocolNexwatch, + LFRFIDProtocolMax, +} LFRFIDProtocol; + +extern const ProtocolBase* lfrfid_protocols[]; + +typedef enum { + LFRFIDWriteTypeT5577, +} LFRFIDWriteType; + +typedef struct { + LFRFIDWriteType write_type; + union { + LFRFIDT5577 t5577; + }; +} LFRFIDWriteRequest; diff --git a/lib/lfrfid/protocols/protocol_awid.c b/lib/lfrfid/protocols/protocol_awid.c new file mode 100644 index 00000000000..9396277236e --- /dev/null +++ b/lib/lfrfid/protocols/protocol_awid.c @@ -0,0 +1,255 @@ +#include +#include +#include +#include +#include +#include "lfrfid_protocols.h" + +#define JITTER_TIME (20) +#define MIN_TIME (64 - JITTER_TIME) +#define MAX_TIME (80 + JITTER_TIME) + +#define AWID_DECODED_DATA_SIZE (9) + +#define AWID_ENCODED_BIT_SIZE (96) +#define AWID_ENCODED_DATA_SIZE (((AWID_ENCODED_BIT_SIZE) / 8) + 1) +#define AWID_ENCODED_DATA_LAST (AWID_ENCODED_DATA_SIZE - 1) + +typedef struct { + FSKDemod* fsk_demod; +} ProtocolAwidDecoder; + +typedef struct { + FSKOsc* fsk_osc; + uint8_t encoded_index; +} ProtocolAwidEncoder; + +typedef struct { + ProtocolAwidDecoder decoder; + ProtocolAwidEncoder encoder; + uint8_t encoded_data[AWID_ENCODED_DATA_SIZE]; + uint8_t data[AWID_DECODED_DATA_SIZE]; +} ProtocolAwid; + +ProtocolAwid* protocol_awid_alloc(void) { + ProtocolAwid* protocol = malloc(sizeof(ProtocolAwid)); + protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5); + protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50); + + return protocol; +}; + +void protocol_awid_free(ProtocolAwid* protocol) { + fsk_demod_free(protocol->decoder.fsk_demod); + fsk_osc_free(protocol->encoder.fsk_osc); + free(protocol); +}; + +uint8_t* protocol_awid_get_data(ProtocolAwid* protocol) { + return protocol->data; +}; + +void protocol_awid_decoder_start(ProtocolAwid* protocol) { + memset(protocol->encoded_data, 0, AWID_ENCODED_DATA_SIZE); +}; + +static bool protocol_awid_can_be_decoded(uint8_t* data) { + bool result = false; + + // Index map + // 0 10 20 30 40 50 60 + // | | | | | | | + // 01234567 890 1 234 5 678 9 012 3 456 7 890 1 234 5 678 9 012 3 456 7 890 1 234 5 678 9 012 3 - to 96 + // ----------------------------------------------------------------------------- + // 00000001 000 1 110 1 101 1 011 1 101 1 010 0 000 1 000 1 010 0 001 0 110 1 100 0 000 1 000 1 + // preamble bbb o bbb o bbw o fff o fff o ffc o ccc o ccc o ccc o ccc o ccc o wxx o xxx o xxx o - to 96 + // |---26 bit---| |-----117----||-------------142-------------| + // b = format bit len, o = odd parity of last 3 bits + // f = facility code, c = card number + // w = wiegand parity + // (26 bit format shown) + + do { + // check preamble and spacing + if(data[0] != 0b00000001 || data[AWID_ENCODED_DATA_LAST] != 0b00000001) break; + + // check odd parity for every 4 bits starting from the second byte + bool parity_error = bit_lib_test_parity(data, 8, 88, BitLibParityOdd, 4); + if(parity_error) break; + + bit_lib_remove_bit_every_nth(data, 8, 88, 4); + + // Avoid detection for invalid formats + uint8_t len = bit_lib_get_bits(data, 8, 8); + if(len != 26 && len != 50 && len != 37 && len != 34 && len != 36) break; + + result = true; + } while(false); + + return result; +} + +static void protocol_awid_decode(uint8_t* encoded_data, uint8_t* decoded_data) { + bit_lib_copy_bits(decoded_data, 0, 66, encoded_data, 8); +} + +bool protocol_awid_decoder_feed(ProtocolAwid* protocol, bool level, uint32_t duration) { + bool value; + uint32_t count; + bool result = false; + + fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count); + if(count > 0) { + for(size_t i = 0; i < count; i++) { + bit_lib_push_bit(protocol->encoded_data, AWID_ENCODED_DATA_SIZE, value); + if(protocol_awid_can_be_decoded(protocol->encoded_data)) { + protocol_awid_decode(protocol->encoded_data, protocol->data); + + result = true; + break; + } + } + } + + return result; +}; + +static void protocol_awid_encode(const uint8_t* decoded_data, uint8_t* encoded_data) { + memset(encoded_data, 0, AWID_ENCODED_DATA_SIZE); + + // preamble + bit_lib_set_bits(encoded_data, 0, 0b00000001, 8); + + for(size_t i = 0; i < 88 / 4; i++) { + uint8_t value = bit_lib_get_bits(decoded_data, i * 3, 3) << 1; + value |= bit_lib_test_parity_32(value, BitLibParityOdd); + bit_lib_set_bits(encoded_data, 8 + i * 4, value, 4); + } +}; + +bool protocol_awid_encoder_start(ProtocolAwid* protocol) { + protocol_awid_encode(protocol->data, (uint8_t*)protocol->encoded_data); + protocol->encoder.encoded_index = 0; + fsk_osc_reset(protocol->encoder.fsk_osc); + return true; +}; + +LevelDuration protocol_awid_encoder_yield(ProtocolAwid* protocol) { + bool level; + uint32_t duration; + + bool bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index); + bool advance = fsk_osc_next_half(protocol->encoder.fsk_osc, bit, &level, &duration); + + if(advance) { + bit_lib_increment_index(protocol->encoder.encoded_index, AWID_ENCODED_BIT_SIZE); + } + return level_duration_make(level, duration); +}; + +void protocol_awid_render_data(ProtocolAwid* protocol, FuriString* result) { + // Index map + // 0 10 20 30 40 50 60 + // | | | | | | | + // 01234567 8 90123456 7890123456789012 3 456789012345678901234567890123456 + // ------------------------------------------------------------------------ + // 00011010 1 01110101 0000000010001110 1 000000000000000000000000000000000 + // bbbbbbbb w ffffffff cccccccccccccccc w xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + // |26 bit| |-117--| |-----142------| + // b = format bit len, o = odd parity of last 3 bits + // f = facility code, c = card number + // w = wiegand parity + // (26 bit format shown) + + uint8_t* decoded_data = protocol->data; + uint8_t format_length = decoded_data[0]; + + furi_string_cat_printf(result, "Format: %d\r\n", format_length); + if(format_length == 26) { + uint8_t facility; + bit_lib_copy_bits(&facility, 0, 8, decoded_data, 9); + + uint16_t card_id; + bit_lib_copy_bits((uint8_t*)&card_id, 8, 8, decoded_data, 17); + bit_lib_copy_bits((uint8_t*)&card_id, 0, 8, decoded_data, 25); + furi_string_cat_printf(result, "Facility: %d\r\n", facility); + furi_string_cat_printf(result, "Card: %d", card_id); + } else { + // print 66 bits as hex + furi_string_cat_printf(result, "Data: "); + for(size_t i = 0; i < AWID_DECODED_DATA_SIZE; i++) { + furi_string_cat_printf(result, "%02X", decoded_data[i]); + } + } +}; + +void protocol_awid_render_brief_data(ProtocolAwid* protocol, FuriString* result) { + uint8_t* decoded_data = protocol->data; + uint8_t format_length = decoded_data[0]; + + furi_string_cat_printf(result, "Format: %d\r\n", format_length); + if(format_length == 26) { + uint8_t facility; + bit_lib_copy_bits(&facility, 0, 8, decoded_data, 9); + + uint16_t card_id; + bit_lib_copy_bits((uint8_t*)&card_id, 8, 8, decoded_data, 17); + bit_lib_copy_bits((uint8_t*)&card_id, 0, 8, decoded_data, 25); + furi_string_cat_printf(result, "ID: %03u,%05u", facility, card_id); + } else { + furi_string_cat_printf(result, "Data: unknown"); + } +}; + +bool protocol_awid_write_data(ProtocolAwid* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + // Fix incorrect length byte + if(protocol->data[0] != 26 && protocol->data[0] != 50 && protocol->data[0] != 37 && + protocol->data[0] != 34 && protocol->data[0] != 36) { + protocol->data[0] = 26; + } + + // Correct protocol data by redecoding + protocol_awid_encode(protocol->data, (uint8_t*)protocol->encoded_data); + bit_lib_remove_bit_every_nth((uint8_t*)protocol->encoded_data, 8, 88, 4); + protocol_awid_decode(protocol->encoded_data, protocol->data); + + protocol_awid_encode(protocol->data, (uint8_t*)protocol->encoded_data); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 | + (3 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.blocks_to_write = 4; + result = true; + } + return result; +}; + +const ProtocolBase protocol_awid = { + .name = "AWID", + .manufacturer = "AWID", + .data_size = AWID_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_awid_alloc, + .free = (ProtocolFree)protocol_awid_free, + .get_data = (ProtocolGetData)protocol_awid_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_awid_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_awid_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_awid_encoder_start, + .yield = (ProtocolEncoderYield)protocol_awid_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_awid_render_data, + .render_brief_data = (ProtocolRenderData)protocol_awid_render_brief_data, + .write_data = (ProtocolWriteData)protocol_awid_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_awid.h b/lib/lfrfid/protocols/protocol_awid.h new file mode 100644 index 00000000000..51a4ea52f59 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_awid.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_awid; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_em4100.c b/lib/lfrfid/protocols/protocol_em4100.c new file mode 100644 index 00000000000..4b720dffdab --- /dev/null +++ b/lib/lfrfid/protocols/protocol_em4100.c @@ -0,0 +1,301 @@ +#include +#include +#include +#include "lfrfid_protocols.h" + +typedef uint64_t EM4100DecodedData; + +#define EM_HEADER_POS (55) +#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS) + +#define EM_FIRST_ROW_POS (50) + +#define EM_ROW_COUNT (10) +#define EM_COLUMN_COUNT (4) +#define EM_BITS_PER_ROW_COUNT (EM_COLUMN_COUNT + 1) + +#define EM_COLUMN_POS (4) +#define EM_STOP_POS (0) +#define EM_STOP_MASK (0x1LLU << EM_STOP_POS) + +#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | EM_STOP_MASK) +#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK) + +#define EM4100_DECODED_DATA_SIZE (5) +#define EM4100_ENCODED_DATA_SIZE (sizeof(EM4100DecodedData)) + +#define EM4100_CLOCK_PER_BIT (64) + +#define EM_READ_SHORT_TIME (256) +#define EM_READ_LONG_TIME (512) +#define EM_READ_JITTER_TIME (100) + +#define EM_READ_SHORT_TIME_LOW (EM_READ_SHORT_TIME - EM_READ_JITTER_TIME) +#define EM_READ_SHORT_TIME_HIGH (EM_READ_SHORT_TIME + EM_READ_JITTER_TIME) +#define EM_READ_LONG_TIME_LOW (EM_READ_LONG_TIME - EM_READ_JITTER_TIME) +#define EM_READ_LONG_TIME_HIGH (EM_READ_LONG_TIME + EM_READ_JITTER_TIME) + +typedef struct { + uint8_t data[EM4100_DECODED_DATA_SIZE]; + + EM4100DecodedData encoded_data; + uint8_t encoded_data_index; + bool encoded_polarity; + + ManchesterState decoder_manchester_state; +} ProtocolEM4100; + +ProtocolEM4100* protocol_em4100_alloc(void) { + ProtocolEM4100* proto = malloc(sizeof(ProtocolEM4100)); + return (void*)proto; +}; + +void protocol_em4100_free(ProtocolEM4100* proto) { + free(proto); +}; + +uint8_t* protocol_em4100_get_data(ProtocolEM4100* proto) { + return proto->data; +}; + +static void em4100_decode( + const uint8_t* encoded_data, + const uint8_t encoded_data_size, + uint8_t* decoded_data, + const uint8_t decoded_data_size) { + furi_check(decoded_data_size >= EM4100_DECODED_DATA_SIZE); + furi_check(encoded_data_size >= EM4100_ENCODED_DATA_SIZE); + + uint8_t decoded_data_index = 0; + EM4100DecodedData card_data = *((EM4100DecodedData*)(encoded_data)); + + // clean result + memset(decoded_data, 0, decoded_data_size); + + // header + for(uint8_t i = 0; i < 9; i++) { + card_data = card_data << 1; + } + + // nibbles + uint8_t value = 0; + for(uint8_t r = 0; r < EM_ROW_COUNT; r++) { + uint8_t nibble = 0; + for(uint8_t i = 0; i < 5; i++) { + if(i < 4) nibble = (nibble << 1) | (card_data & (1LLU << 63) ? 1 : 0); + card_data = card_data << 1; + } + value = (value << 4) | nibble; + if(r % 2) { + decoded_data[decoded_data_index] |= value; + decoded_data_index++; + value = 0; + } + } +} + +static bool em4100_can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) { + furi_check(encoded_data_size >= EM4100_ENCODED_DATA_SIZE); + const EM4100DecodedData* card_data = (EM4100DecodedData*)encoded_data; + + // check header and stop bit + if((*card_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return false; + + // check row parity + for(uint8_t i = 0; i < EM_ROW_COUNT; i++) { + uint8_t parity_sum = 0; + + for(uint8_t j = 0; j < EM_BITS_PER_ROW_COUNT; j++) { + parity_sum += (*card_data >> (EM_FIRST_ROW_POS - i * EM_BITS_PER_ROW_COUNT + j)) & 1; + } + + if((parity_sum % 2)) { + return false; + } + } + + // check columns parity + for(uint8_t i = 0; i < EM_COLUMN_COUNT; i++) { + uint8_t parity_sum = 0; + + for(uint8_t j = 0; j < EM_ROW_COUNT + 1; j++) { + parity_sum += (*card_data >> (EM_COLUMN_POS - i + j * EM_BITS_PER_ROW_COUNT)) & 1; + } + + if((parity_sum % 2)) { + return false; + } + } + + return true; +} + +void protocol_em4100_decoder_start(ProtocolEM4100* proto) { + memset(proto->data, 0, EM4100_DECODED_DATA_SIZE); + proto->encoded_data = 0; + manchester_advance( + proto->decoder_manchester_state, + ManchesterEventReset, + &proto->decoder_manchester_state, + NULL); +}; + +bool protocol_em4100_decoder_feed(ProtocolEM4100* proto, bool level, uint32_t duration) { + bool result = false; + + ManchesterEvent event = ManchesterEventReset; + + if(duration > EM_READ_SHORT_TIME_LOW && duration < EM_READ_SHORT_TIME_HIGH) { + if(!level) { + event = ManchesterEventShortHigh; + } else { + event = ManchesterEventShortLow; + } + } else if(duration > EM_READ_LONG_TIME_LOW && duration < EM_READ_LONG_TIME_HIGH) { + if(!level) { + event = ManchesterEventLongHigh; + } else { + event = ManchesterEventLongLow; + } + } + + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + proto->decoder_manchester_state, event, &proto->decoder_manchester_state, &data); + + if(data_ok) { + proto->encoded_data = (proto->encoded_data << 1) | data; + + if(em4100_can_be_decoded((uint8_t*)&proto->encoded_data, sizeof(EM4100DecodedData))) { + em4100_decode( + (uint8_t*)&proto->encoded_data, + sizeof(EM4100DecodedData), + proto->data, + EM4100_DECODED_DATA_SIZE); + result = true; + } + } + } + + return result; +}; + +static void em4100_write_nibble(bool low_nibble, uint8_t data, EM4100DecodedData* encoded_data) { + uint8_t parity_sum = 0; + uint8_t start = 0; + if(!low_nibble) start = 4; + + for(int8_t i = (start + 3); i >= start; i--) { + parity_sum += (data >> i) & 1; + *encoded_data = (*encoded_data << 1) | ((data >> i) & 1); + } + + *encoded_data = (*encoded_data << 1) | ((parity_sum % 2) & 1); +} + +bool protocol_em4100_encoder_start(ProtocolEM4100* proto) { + // header + proto->encoded_data = 0b111111111; + + // data + for(uint8_t i = 0; i < EM4100_DECODED_DATA_SIZE; i++) { + em4100_write_nibble(false, proto->data[i], &proto->encoded_data); + em4100_write_nibble(true, proto->data[i], &proto->encoded_data); + } + + // column parity and stop bit + uint8_t parity_sum; + + for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) { + parity_sum = 0; + for(uint8_t i = 1; i <= EM_ROW_COUNT; i++) { + uint8_t parity_bit = (proto->encoded_data >> (i * EM_BITS_PER_ROW_COUNT - 1)) & 1; + parity_sum += parity_bit; + } + proto->encoded_data = (proto->encoded_data << 1) | ((parity_sum % 2) & 1); + } + + // stop bit + proto->encoded_data = (proto->encoded_data << 1) | 0; + + proto->encoded_data_index = 0; + proto->encoded_polarity = true; + + return true; +}; + +LevelDuration protocol_em4100_encoder_yield(ProtocolEM4100* proto) { + bool level = (proto->encoded_data >> (63 - proto->encoded_data_index)) & 1; + uint32_t duration = EM4100_CLOCK_PER_BIT / 2; + + if(proto->encoded_polarity) { + proto->encoded_polarity = false; + } else { + level = !level; + + proto->encoded_polarity = true; + proto->encoded_data_index++; + if(proto->encoded_data_index >= 64) { + proto->encoded_data_index = 0; + } + } + + return level_duration_make(level, duration); +}; + +bool protocol_em4100_write_data(ProtocolEM4100* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + // Correct protocol data by redecoding + protocol_em4100_encoder_start(protocol); + em4100_decode( + (uint8_t*)&protocol->encoded_data, + sizeof(EM4100DecodedData), + protocol->data, + EM4100_DECODED_DATA_SIZE); + + protocol_em4100_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = + (LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_64 | + (2 << LFRFID_T5577_MAXBLOCK_SHIFT)); + request->t5577.block[1] = protocol->encoded_data; + request->t5577.block[2] = protocol->encoded_data >> 32; + request->t5577.blocks_to_write = 3; + result = true; + } + return result; +}; + +void protocol_em4100_render_data(ProtocolEM4100* protocol, FuriString* result) { + uint8_t* data = protocol->data; + furi_string_printf( + result, "FC: %03u, Card: %05u", data[2], (uint16_t)((data[3] << 8) | (data[4]))); +}; + +const ProtocolBase protocol_em4100 = { + .name = "EM4100", + .manufacturer = "EM-Micro", + .data_size = EM4100_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK | LFRFIDFeaturePSK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_em4100_alloc, + .free = (ProtocolFree)protocol_em4100_free, + .get_data = (ProtocolGetData)protocol_em4100_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_em4100_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_em4100_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_em4100_encoder_start, + .yield = (ProtocolEncoderYield)protocol_em4100_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_em4100_render_data, + .render_brief_data = (ProtocolRenderData)protocol_em4100_render_data, + .write_data = (ProtocolWriteData)protocol_em4100_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_em4100.h b/lib/lfrfid/protocols/protocol_em4100.h new file mode 100644 index 00000000000..6e1e25b9371 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_em4100.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_em4100; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_fdx_a.c b/lib/lfrfid/protocols/protocol_fdx_a.c new file mode 100644 index 00000000000..87daa0eb642 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_fdx_a.c @@ -0,0 +1,252 @@ +#include +#include +#include +#include +#include "lfrfid_protocols.h" +#include + +#define JITTER_TIME (20) +#define MIN_TIME (64 - JITTER_TIME) +#define MAX_TIME (80 + JITTER_TIME) + +#define FDXA_DATA_SIZE 10 +#define FDXA_PREAMBLE_SIZE 2 + +#define FDXA_ENCODED_DATA_SIZE (FDXA_PREAMBLE_SIZE + FDXA_DATA_SIZE + FDXA_PREAMBLE_SIZE) +#define FDXA_ENCODED_BIT_SIZE ((FDXA_PREAMBLE_SIZE + FDXA_DATA_SIZE) * 8) +#define FDXA_DECODED_DATA_SIZE (5) +#define FDXA_DECODED_BIT_SIZE ((FDXA_ENCODED_BIT_SIZE - FDXA_PREAMBLE_SIZE * 8) / 2) + +#define FDXA_PREAMBLE_0 0x55 +#define FDXA_PREAMBLE_1 0x1D + +typedef struct { + FSKDemod* fsk_demod; +} ProtocolFDXADecoder; + +typedef struct { + FSKOsc* fsk_osc; + uint8_t encoded_index; + uint32_t pulse; +} ProtocolFDXAEncoder; + +typedef struct { + ProtocolFDXADecoder decoder; + ProtocolFDXAEncoder encoder; + uint8_t encoded_data[FDXA_ENCODED_DATA_SIZE]; + uint8_t data[FDXA_DECODED_DATA_SIZE]; + size_t protocol_size; +} ProtocolFDXA; + +ProtocolFDXA* protocol_fdx_a_alloc(void) { + ProtocolFDXA* protocol = malloc(sizeof(ProtocolFDXA)); + protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5); + protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50); + + return protocol; +}; + +void protocol_fdx_a_free(ProtocolFDXA* protocol) { + fsk_demod_free(protocol->decoder.fsk_demod); + fsk_osc_free(protocol->encoder.fsk_osc); + free(protocol); +}; + +uint8_t* protocol_fdx_a_get_data(ProtocolFDXA* protocol) { + return protocol->data; +}; + +void protocol_fdx_a_decoder_start(ProtocolFDXA* protocol) { + memset(protocol->encoded_data, 0, FDXA_ENCODED_DATA_SIZE); +}; + +static bool protocol_fdx_a_decode(const uint8_t* from, uint8_t* to) { + size_t bit_index = 0; + for(size_t i = FDXA_PREAMBLE_SIZE; i < (FDXA_PREAMBLE_SIZE + FDXA_DATA_SIZE); i++) { + for(size_t n = 0; n < 4; n++) { + uint8_t bit_pair = (from[i] >> (6 - (n * 2))) & 0b11; + if(bit_pair == 0b01) { + bit_lib_set_bit(to, bit_index, 0); + } else if(bit_pair == 0b10) { + bit_lib_set_bit(to, bit_index, 1); + } else { + return false; + } + bit_index++; + } + } + + return true; +} + +static void protocol_fdx_a_fix_parity(ProtocolFDXA* protocol) { + for(size_t i = 0; i < FDXA_DECODED_DATA_SIZE; i++) { + if(bit_lib_test_parity_32(protocol->data[i], BitLibParityOdd)) { + protocol->data[i] ^= (1 << 7); + } + } +} + +static bool protocol_fdx_a_can_be_decoded(const uint8_t* data) { + // check preamble + if(data[0] != FDXA_PREAMBLE_0 || data[1] != FDXA_PREAMBLE_1 || data[12] != FDXA_PREAMBLE_0 || + data[13] != FDXA_PREAMBLE_1) { + return false; + } + + // check for manchester encoding + uint8_t decoded_data[FDXA_DECODED_DATA_SIZE]; + if(!protocol_fdx_a_decode(data, decoded_data)) return false; + + uint8_t parity_sum = 0; + for(size_t i = 0; i < FDXA_DECODED_DATA_SIZE; i++) { + parity_sum += bit_lib_test_parity_32(decoded_data[i], BitLibParityOdd); + decoded_data[i] &= 0x7F; + } + + return (parity_sum == 0); +} + +bool protocol_fdx_a_decoder_feed(ProtocolFDXA* protocol, bool level, uint32_t duration) { + bool value; + uint32_t count; + bool result = false; + + fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count); + if(count > 0) { + for(size_t i = 0; i < count; i++) { + bit_lib_push_bit(protocol->encoded_data, FDXA_ENCODED_DATA_SIZE, value); + if(protocol_fdx_a_can_be_decoded(protocol->encoded_data)) { + protocol_fdx_a_decode(protocol->encoded_data, protocol->data); + result = true; + } + } + } + + return result; +}; + +static void protocol_fdx_a_encode(ProtocolFDXA* protocol) { + protocol->encoded_data[0] = FDXA_PREAMBLE_0; + protocol->encoded_data[1] = FDXA_PREAMBLE_1; + + size_t bit_index = 0; + for(size_t i = 0; i < FDXA_DECODED_BIT_SIZE; i++) { + bool bit = bit_lib_get_bit(protocol->data, i); + if(bit) { + bit_lib_set_bit(protocol->encoded_data, 16 + bit_index, 1); + bit_lib_set_bit(protocol->encoded_data, 16 + bit_index + 1, 0); + } else { + bit_lib_set_bit(protocol->encoded_data, 16 + bit_index, 0); + bit_lib_set_bit(protocol->encoded_data, 16 + bit_index + 1, 1); + } + bit_index += 2; + } +} + +bool protocol_fdx_a_encoder_start(ProtocolFDXA* protocol) { + protocol->encoder.encoded_index = 0; + protocol->encoder.pulse = 0; + protocol_fdx_a_encode(protocol); + + return true; +}; + +LevelDuration protocol_fdx_a_encoder_yield(ProtocolFDXA* protocol) { + bool level = 0; + uint32_t duration = 0; + + // if pulse is zero, we need to output high, otherwise we need to output low + if(protocol->encoder.pulse == 0) { + // get bit + uint8_t bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index); + + // get pulse from oscillator + bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration); + + if(advance) { + bit_lib_increment_index(protocol->encoder.encoded_index, FDXA_ENCODED_BIT_SIZE); + } + + // duration diveded by 2 because we need to output high and low + duration = duration / 2; + protocol->encoder.pulse = duration; + level = true; + } else { + // output low half and reset pulse + duration = protocol->encoder.pulse; + protocol->encoder.pulse = 0; + level = false; + } + + return level_duration_make(level, duration); +}; + +bool protocol_fdx_a_write_data(ProtocolFDXA* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + // Correct protocol data by redecoding + protocol_fdx_a_fix_parity(protocol); + protocol_fdx_a_encoder_start(protocol); + protocol_fdx_a_decode(protocol->encoded_data, protocol->data); + + protocol_fdx_a_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 | + (3 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.blocks_to_write = 4; + result = true; + } + return result; +}; + +void protocol_fdx_a_render_data(ProtocolFDXA* protocol, FuriString* result) { + uint8_t data[FDXA_DECODED_DATA_SIZE]; + memcpy(data, protocol->data, FDXA_DECODED_DATA_SIZE); + + uint8_t parity_sum = 0; + for(size_t i = 0; i < FDXA_DECODED_DATA_SIZE; i++) { + parity_sum += bit_lib_test_parity_32(data[i], BitLibParityOdd); + data[i] &= 0x7F; + } + + furi_string_printf( + result, + "ID: %02X%02X%02X%02X%02X\r\n" + "Parity: %s", + data[0], + data[1], + data[2], + data[3], + data[4], + parity_sum == 0 ? "+" : "-"); +}; + +const ProtocolBase protocol_fdx_a = { + .name = "FDX-A", + .manufacturer = "FECAVA", + .data_size = FDXA_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_fdx_a_alloc, + .free = (ProtocolFree)protocol_fdx_a_free, + .get_data = (ProtocolGetData)protocol_fdx_a_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_fdx_a_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_fdx_a_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_fdx_a_encoder_start, + .yield = (ProtocolEncoderYield)protocol_fdx_a_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_fdx_a_render_data, + .render_brief_data = (ProtocolRenderData)protocol_fdx_a_render_data, + .write_data = (ProtocolWriteData)protocol_fdx_a_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_fdx_a.h b/lib/lfrfid/protocols/protocol_fdx_a.h new file mode 100644 index 00000000000..355544881c0 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_fdx_a.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_fdx_a; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_fdx_b.c b/lib/lfrfid/protocols/protocol_fdx_b.c new file mode 100644 index 00000000000..04386a67528 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_fdx_b.c @@ -0,0 +1,383 @@ +#include +#include "toolbox/level_duration.h" +#include "protocol_fdx_b.h" +#include +#include +#include "lfrfid_protocols.h" +#include + +#define FDX_B_ENCODED_BIT_SIZE (128) +#define FDX_B_ENCODED_BYTE_SIZE (((FDX_B_ENCODED_BIT_SIZE) / 8)) +#define FDX_B_PREAMBLE_BIT_SIZE (11) +#define FDX_B_PREAMBLE_BYTE_SIZE (2) +#define FDX_B_ENCODED_BYTE_FULL_SIZE (FDX_B_ENCODED_BYTE_SIZE + FDX_B_PREAMBLE_BYTE_SIZE) + +#define FDXB_DECODED_DATA_SIZE (11) + +#define FDX_B_SHORT_TIME (128) +#define FDX_B_LONG_TIME (256) +#define FDX_B_JITTER_TIME (60) + +#define FDX_B_SHORT_TIME_LOW (FDX_B_SHORT_TIME - FDX_B_JITTER_TIME) +#define FDX_B_SHORT_TIME_HIGH (FDX_B_SHORT_TIME + FDX_B_JITTER_TIME) +#define FDX_B_LONG_TIME_LOW (FDX_B_LONG_TIME - FDX_B_JITTER_TIME) +#define FDX_B_LONG_TIME_HIGH (FDX_B_LONG_TIME + FDX_B_JITTER_TIME) + +typedef struct { + bool last_short; + bool last_level; + size_t encoded_index; + uint8_t encoded_data[FDX_B_ENCODED_BYTE_FULL_SIZE]; + uint8_t data[FDXB_DECODED_DATA_SIZE]; +} ProtocolFDXB; + +ProtocolFDXB* protocol_fdx_b_alloc(void) { + ProtocolFDXB* protocol = malloc(sizeof(ProtocolFDXB)); + return protocol; +}; + +void protocol_fdx_b_free(ProtocolFDXB* protocol) { + free(protocol); +}; + +uint8_t* protocol_fdx_b_get_data(ProtocolFDXB* proto) { + return proto->data; +}; + +void protocol_fdx_b_decoder_start(ProtocolFDXB* protocol) { + memset(protocol->encoded_data, 0, FDX_B_ENCODED_BYTE_FULL_SIZE); + protocol->last_short = false; +}; + +static bool protocol_fdx_b_can_be_decoded(ProtocolFDXB* protocol) { + bool result = false; + + /* + msb lsb + 0 10000000000 Header pattern. 11 bits. + 11 1nnnnnnnn + 20 1nnnnnnnn 38 bit (12 digit) National code. + 29 1nnnnnnnn eg. 000000001008 (decimal). + 38 1nnnnnnnn + 47 1nnnnnncc 10 bit (3 digit) Country code. + 56 1cccccccc eg. 999 (decimal). + 65 1s------- 1 bit data block status flag. + 74 1-------a 1 bit animal application indicator. + 83 1xxxxxxxx 16 bit checksum. + 92 1xxxxxxxx + 101 1eeeeeeee 24 bits of extra data if present. + 110 1eeeeeeee eg. $123456. + 119 1eeeeeeee + */ + + do { + // check 11 bits preamble + if(bit_lib_get_bits_16(protocol->encoded_data, 0, 11) != 0b10000000000) break; + // check next 11 bits preamble + if(bit_lib_get_bits_16(protocol->encoded_data, 128, 11) != 0b10000000000) break; + // check control bits + if(!bit_lib_test_parity(protocol->encoded_data, 3, 13 * 9, BitLibParityAlways1, 9)) break; + + // compute checksum + uint8_t crc_data[8]; + for(size_t i = 0; i < 8; i++) { + bit_lib_copy_bits(crc_data, i * 8, 8, protocol->encoded_data, 12 + 9 * i); + } + uint16_t crc_res = bit_lib_crc16(crc_data, 8, 0x1021, 0x0000, false, false, 0x0000); + + // read checksum + uint16_t crc_ex = 0; + bit_lib_copy_bits((uint8_t*)&crc_ex, 8, 8, protocol->encoded_data, 84); + bit_lib_copy_bits((uint8_t*)&crc_ex, 0, 8, protocol->encoded_data, 93); + + // compare checksum + if(crc_res != crc_ex) break; + + result = true; + } while(false); + + return result; +} + +void protocol_fdx_b_decode(ProtocolFDXB* protocol) { + // remove parity + bit_lib_remove_bit_every_nth(protocol->encoded_data, 3, 13 * 9, 9); + + // remove header pattern + for(size_t i = 0; i < 11; i++) + bit_lib_push_bit(protocol->encoded_data, FDX_B_ENCODED_BYTE_FULL_SIZE, 0); + + // 0 nnnnnnnn + // 8 nnnnnnnn 38 bit (12 digit) National code. + // 16 nnnnnnnn eg. 000000001008 (decimal). + // 24 nnnnnnnn + // 32 nnnnnncc 10 bit (3 digit) Country code. + // 40 cccccccc eg. 999 (decimal). + // 48 s------- 1 bit data block status flag. + // 56 -------a 1 bit animal application indicator. + // 64 xxxxxxxx 16 bit checksum. + // 72 xxxxxxxx + // 80 eeeeeeee 24 bits of extra data if present. + // 88 eeeeeeee eg. $123456. + // 92 eeeeeeee + + // copy data without checksum + bit_lib_copy_bits(protocol->data, 0, 64, protocol->encoded_data, 0); + bit_lib_copy_bits(protocol->data, 64, 24, protocol->encoded_data, 80); + + // const BitLibRegion regions_encoded[] = { + // {'n', 0, 38}, + // {'c', 38, 10}, + // {'b', 48, 16}, + // {'x', 64, 16}, + // {'e', 80, 24}, + // }; + + // bit_lib_print_regions(regions_encoded, 5, protocol->encoded_data, FDX_B_ENCODED_BIT_SIZE); + + // const BitLibRegion regions_decoded[] = { + // {'n', 0, 38}, + // {'c', 38, 10}, + // {'b', 48, 16}, + // {'e', 64, 24}, + // }; + + // bit_lib_print_regions(regions_decoded, 4, protocol->data, FDXB_DECODED_DATA_SIZE * 8); +} + +bool protocol_fdx_b_decoder_feed(ProtocolFDXB* protocol, bool level, uint32_t duration) { + bool result = false; + UNUSED(level); + + bool pushed = false; + + // Bi-Phase Manchester decoding + if(duration >= FDX_B_SHORT_TIME_LOW && duration <= FDX_B_SHORT_TIME_HIGH) { + if(protocol->last_short == false) { + protocol->last_short = true; + } else { + pushed = true; + bit_lib_push_bit(protocol->encoded_data, FDX_B_ENCODED_BYTE_FULL_SIZE, false); + protocol->last_short = false; + } + } else if(duration >= FDX_B_LONG_TIME_LOW && duration <= FDX_B_LONG_TIME_HIGH) { + if(protocol->last_short == false) { + pushed = true; + bit_lib_push_bit(protocol->encoded_data, FDX_B_ENCODED_BYTE_FULL_SIZE, true); + } else { + // reset + protocol->last_short = false; + } + } else { + // reset + protocol->last_short = false; + } + + if(pushed && protocol_fdx_b_can_be_decoded(protocol)) { + protocol_fdx_b_decode(protocol); + result = true; + } + + return result; +}; + +bool protocol_fdx_b_encoder_start(ProtocolFDXB* protocol) { + memset(protocol->encoded_data, 0, FDX_B_ENCODED_BYTE_FULL_SIZE); + bit_lib_set_bit(protocol->encoded_data, 0, 1); + for(size_t i = 0; i < 13; i++) { + bit_lib_set_bit(protocol->encoded_data, 11 + 9 * i, 1); + if(i == 8 || i == 9) continue; + + if(i < 8) { + bit_lib_copy_bits(protocol->encoded_data, 12 + 9 * i, 8, protocol->data, i * 8); + } else { + bit_lib_copy_bits(protocol->encoded_data, 12 + 9 * i, 8, protocol->data, (i - 2) * 8); + } + } + + uint16_t crc_res = bit_lib_crc16(protocol->data, 8, 0x1021, 0x0000, false, false, 0x0000); + bit_lib_copy_bits(protocol->encoded_data, 84, 8, (uint8_t*)&crc_res, 8); + bit_lib_copy_bits(protocol->encoded_data, 93, 8, (uint8_t*)&crc_res, 0); + + protocol->encoded_index = 0; + protocol->last_short = false; + protocol->last_level = false; + return true; +}; + +LevelDuration protocol_fdx_b_encoder_yield(ProtocolFDXB* protocol) { + uint32_t duration; + protocol->last_level = !protocol->last_level; + + bool bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_index); + + // Bi-Phase Manchester encoder + if(bit) { + // one long pulse for 1 + duration = FDX_B_LONG_TIME / 8; + bit_lib_increment_index(protocol->encoded_index, FDX_B_ENCODED_BIT_SIZE); + } else { + // two short pulses for 0 + duration = FDX_B_SHORT_TIME / 8; + if(protocol->last_short) { + bit_lib_increment_index(protocol->encoded_index, FDX_B_ENCODED_BIT_SIZE); + protocol->last_short = false; + } else { + protocol->last_short = true; + } + } + + return level_duration_make(protocol->last_level, duration); +}; + +// 0 nnnnnnnn +// 8 nnnnnnnn 38 bit (12 digit) National code. +// 16 nnnnnnnn eg. 000000001008 (decimal). +// 24 nnnnnnnn +// 32 nnnnnnnn 10 bit (3 digit) Country code. +// 40 cccccccc eg. 999 (decimal). +// 48 s------- 1 bit data block status flag. +// 56 -------a 1 bit animal application indicator. +// 64 eeeeeeee 24 bits of extra data if present. +// 72 eeeeeeee eg. $123456. +// 80 eeeeeeee + +static uint64_t protocol_fdx_b_get_national_code(const uint8_t* data) { + uint64_t national_code = bit_lib_get_bits_32(data, 0, 32); + national_code = national_code << 32; + national_code |= (uint64_t)bit_lib_get_bits_32(data, 32, 6) << (32 - 6); + bit_lib_reverse_bits((uint8_t*)&national_code, 0, 64); + return national_code; +} + +static uint16_t protocol_fdx_b_get_country_code(const uint8_t* data) { + uint16_t country_code = bit_lib_get_bits_16(data, 38, 10) << 6; + bit_lib_reverse_bits((uint8_t*)&country_code, 0, 16); + return country_code; +} + +static bool protocol_fdx_b_get_temp(const uint8_t* data, float* temp) { + uint32_t extended = bit_lib_get_bits_32(data, 64, 24) << 8; + bit_lib_reverse_bits((uint8_t*)&extended, 0, 32); + + uint8_t ex_parity = (extended & 0x100) >> 8; + uint8_t ex_temperature = extended & 0xff; + uint8_t ex_calc_parity = bit_lib_test_parity_32(ex_temperature, BitLibParityOdd); + bool ex_temperature_present = (ex_calc_parity == ex_parity) && !(extended & 0xe00); + + if(ex_temperature_present) { + float temperature_f = 74 + ex_temperature * 0.2; + *temp = temperature_f; + return true; + } else { + return false; + } +} + +void protocol_fdx_b_render_data(ProtocolFDXB* protocol, FuriString* result) { + // 38 bits of national code + uint64_t national_code = protocol_fdx_b_get_national_code(protocol->data); + + // 10 bit of country code + uint16_t country_code = protocol_fdx_b_get_country_code(protocol->data); + + bool block_status = bit_lib_get_bit(protocol->data, 48); + bool rudi_bit = bit_lib_get_bit(protocol->data, 49); + uint8_t reserved = bit_lib_get_bits(protocol->data, 50, 5); + uint8_t user_info = bit_lib_get_bits(protocol->data, 55, 5); + uint8_t replacement_number = bit_lib_get_bits(protocol->data, 60, 3); + bool animal_flag = bit_lib_get_bit(protocol->data, 63); + + furi_string_printf(result, "ID: %03u-%012llu\r\n", country_code, national_code); + furi_string_cat_printf(result, "Animal: %s, ", animal_flag ? "Yes" : "No"); + + float temperature; + if(protocol_fdx_b_get_temp(protocol->data, &temperature)) { + float temperature_c = (temperature - 32) / 1.8; + furi_string_cat_printf( + result, "T: %.2fF, %.2fC\r\n", (double)temperature, (double)temperature_c); + } else { + furi_string_cat_printf(result, "T: ---\r\n"); + } + + furi_string_cat_printf( + result, + "Bits: %X-%X-%X-%X-%X", + block_status, + rudi_bit, + reserved, + user_info, + replacement_number); +}; + +void protocol_fdx_b_render_brief_data(ProtocolFDXB* protocol, FuriString* result) { + // 38 bits of national code + uint64_t national_code = protocol_fdx_b_get_national_code(protocol->data); + + // 10 bit of country code + uint16_t country_code = protocol_fdx_b_get_country_code(protocol->data); + + bool animal_flag = bit_lib_get_bit(protocol->data, 63); + + furi_string_printf(result, "ID: %03u-%012llu\r\n", country_code, national_code); + furi_string_cat_printf(result, "Animal: %s, ", animal_flag ? "Yes" : "No"); + + float temperature; + if(protocol_fdx_b_get_temp(protocol->data, &temperature)) { + if(furi_hal_rtc_get_locale_units() == FuriHalRtcLocaleUnitsMetric) { + float temperature_c = (temperature - 32.0f) / 1.8f; + furi_string_cat_printf(result, "T: %.2fC", (double)temperature_c); + } else { + furi_string_cat_printf(result, "T: %.2fF", (double)temperature); + } + } else { + furi_string_cat_printf(result, "T: ---"); + } +}; + +bool protocol_fdx_b_write_data(ProtocolFDXB* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + // Correct protocol data by redecoding + protocol_fdx_b_encoder_start(protocol); + protocol_fdx_b_decode(protocol); + + protocol_fdx_b_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_DIPHASE | LFRFID_T5577_BITRATE_RF_32 | + (4 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.block[4] = bit_lib_get_bits_32(protocol->encoded_data, 96, 32); + request->t5577.blocks_to_write = 5; + result = true; + } + return result; +}; + +const ProtocolBase protocol_fdx_b = { + .name = "FDX-B", + .manufacturer = "ISO", + .data_size = FDXB_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_fdx_b_alloc, + .free = (ProtocolFree)protocol_fdx_b_free, + .get_data = (ProtocolGetData)protocol_fdx_b_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_fdx_b_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_fdx_b_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_fdx_b_encoder_start, + .yield = (ProtocolEncoderYield)protocol_fdx_b_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_fdx_b_render_data, + .render_brief_data = (ProtocolRenderData)protocol_fdx_b_render_brief_data, + .write_data = (ProtocolWriteData)protocol_fdx_b_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_fdx_b.h b/lib/lfrfid/protocols/protocol_fdx_b.h new file mode 100644 index 00000000000..549c862e380 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_fdx_b.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_fdx_b; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_gallagher.c b/lib/lfrfid/protocols/protocol_gallagher.c new file mode 100644 index 00000000000..4720d3a4df0 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_gallagher.c @@ -0,0 +1,304 @@ +#include +#include +#include +#include +#include "lfrfid_protocols.h" + +#define GALLAGHER_CLOCK_PER_BIT (32) + +#define GALLAGHER_ENCODED_BIT_SIZE (96) +#define GALLAGHER_ENCODED_BYTE_SIZE ((GALLAGHER_ENCODED_BIT_SIZE) / 8) +#define GALLAGHER_PREAMBLE_BIT_SIZE (16) +#define GALLAGHER_PREAMBLE_BYTE_SIZE ((GALLAGHER_PREAMBLE_BIT_SIZE) / 8) +#define GALLAGHER_ENCODED_BYTE_FULL_SIZE \ + (GALLAGHER_ENCODED_BYTE_SIZE + GALLAGHER_PREAMBLE_BYTE_SIZE) +#define GALLAGHER_DECODED_DATA_SIZE 8 + +#define GALLAGHER_READ_SHORT_TIME (128) +#define GALLAGHER_READ_LONG_TIME (256) +#define GALLAGHER_READ_JITTER_TIME (60) + +#define GALLAGHER_READ_SHORT_TIME_LOW (GALLAGHER_READ_SHORT_TIME - GALLAGHER_READ_JITTER_TIME) +#define GALLAGHER_READ_SHORT_TIME_HIGH (GALLAGHER_READ_SHORT_TIME + GALLAGHER_READ_JITTER_TIME) +#define GALLAGHER_READ_LONG_TIME_LOW (GALLAGHER_READ_LONG_TIME - GALLAGHER_READ_JITTER_TIME) +#define GALLAGHER_READ_LONG_TIME_HIGH (GALLAGHER_READ_LONG_TIME + GALLAGHER_READ_JITTER_TIME) + +typedef struct { + uint8_t data[GALLAGHER_DECODED_DATA_SIZE]; + uint8_t encoded_data[GALLAGHER_ENCODED_BYTE_FULL_SIZE]; + + uint8_t encoded_data_index; + bool encoded_polarity; + + ManchesterState decoder_manchester_state; +} ProtocolGallagher; + +ProtocolGallagher* protocol_gallagher_alloc(void) { + ProtocolGallagher* proto = malloc(sizeof(ProtocolGallagher)); + return (void*)proto; +}; + +void protocol_gallagher_free(ProtocolGallagher* protocol) { + free(protocol); +}; + +uint8_t* protocol_gallagher_get_data(ProtocolGallagher* protocol) { + return protocol->data; +}; + +static void protocol_gallagher_scramble(uint8_t* data, size_t length) { + const uint8_t lut[] = { + 0xa3, 0xb0, 0x80, 0xc6, 0xb2, 0xf4, 0x5c, 0x6c, 0x81, 0xf1, 0xbb, 0xeb, 0x55, 0x67, 0x3c, + 0x05, 0x1a, 0x0e, 0x61, 0xf6, 0x22, 0xce, 0xaa, 0x8f, 0xbd, 0x3b, 0x1f, 0x5e, 0x44, 0x04, + 0x51, 0x2e, 0x4d, 0x9a, 0x84, 0xea, 0xf8, 0x66, 0x74, 0x29, 0x7f, 0x70, 0xd8, 0x31, 0x7a, + 0x6d, 0xa4, 0x00, 0x82, 0xb9, 0x5f, 0xb4, 0x16, 0xab, 0xff, 0xc2, 0x39, 0xdc, 0x19, 0x65, + 0x57, 0x7c, 0x20, 0xfa, 0x5a, 0x49, 0x13, 0xd0, 0xfb, 0xa8, 0x91, 0x73, 0xb1, 0x33, 0x18, + 0xbe, 0x21, 0x72, 0x48, 0xb6, 0xdb, 0xa0, 0x5d, 0xcc, 0xe6, 0x17, 0x27, 0xe5, 0xd4, 0x53, + 0x42, 0xf3, 0xdd, 0x7b, 0x24, 0xac, 0x2b, 0x58, 0x1e, 0xa7, 0xe7, 0x86, 0x40, 0xd3, 0x98, + 0x97, 0x71, 0xcb, 0x3a, 0x0f, 0x01, 0x9b, 0x6e, 0x1b, 0xfc, 0x34, 0xa6, 0xda, 0x07, 0x0c, + 0xae, 0x37, 0xca, 0x54, 0xfd, 0x26, 0xfe, 0x0a, 0x45, 0xa2, 0x2a, 0xc4, 0x12, 0x0d, 0xf5, + 0x4f, 0x69, 0xe0, 0x8a, 0x77, 0x60, 0x3f, 0x99, 0x95, 0xd2, 0x38, 0x36, 0x62, 0xb7, 0x32, + 0x7e, 0x79, 0xc0, 0x46, 0x93, 0x2f, 0xa5, 0xba, 0x5b, 0xaf, 0x52, 0x1d, 0xc3, 0x75, 0xcf, + 0xd6, 0x4c, 0x83, 0xe8, 0x3d, 0x30, 0x4e, 0xbc, 0x08, 0x2d, 0x09, 0x06, 0xd9, 0x25, 0x9e, + 0x89, 0xf2, 0x96, 0x88, 0xc1, 0x8c, 0x94, 0x0b, 0x28, 0xf0, 0x47, 0x63, 0xd5, 0xb3, 0x68, + 0x56, 0x9c, 0xf9, 0x6f, 0x41, 0x50, 0x85, 0x8b, 0x9d, 0x59, 0xbf, 0x9f, 0xe2, 0x8e, 0x6a, + 0x11, 0x23, 0xa1, 0xcd, 0xb5, 0x7d, 0xc7, 0xa9, 0xc8, 0xef, 0xdf, 0x02, 0xb8, 0x03, 0x6b, + 0x35, 0x3e, 0x2c, 0x76, 0xc9, 0xde, 0x1c, 0x4b, 0xd1, 0xed, 0x14, 0xc5, 0xad, 0xe9, 0x64, + 0x4a, 0xec, 0x8d, 0xf7, 0x10, 0x43, 0x78, 0x15, 0x87, 0xe4, 0xd7, 0x92, 0xe1, 0xee, 0xe3, + 0x90}; + for(size_t i = 0; i < length; i++) { + data[i] = lut[data[i]]; + } +} + +static void protocol_gallagher_descramble(uint8_t* data, size_t length) { + const uint8_t lut[] = { + 0x2f, 0x6e, 0xdd, 0xdf, 0x1d, 0x0f, 0xb0, 0x76, 0xad, 0xaf, 0x7f, 0xbb, 0x77, 0x85, 0x11, + 0x6d, 0xf4, 0xd2, 0x84, 0x42, 0xeb, 0xf7, 0x34, 0x55, 0x4a, 0x3a, 0x10, 0x71, 0xe7, 0xa1, + 0x62, 0x1a, 0x3e, 0x4c, 0x14, 0xd3, 0x5e, 0xb2, 0x7d, 0x56, 0xbc, 0x27, 0x82, 0x60, 0xe3, + 0xae, 0x1f, 0x9b, 0xaa, 0x2b, 0x95, 0x49, 0x73, 0xe1, 0x92, 0x79, 0x91, 0x38, 0x6c, 0x19, + 0x0e, 0xa9, 0xe2, 0x8d, 0x66, 0xc7, 0x5a, 0xf5, 0x1c, 0x80, 0x99, 0xbe, 0x4e, 0x41, 0xf0, + 0xe8, 0xa6, 0x20, 0xab, 0x87, 0xc8, 0x1e, 0xa0, 0x59, 0x7b, 0x0c, 0xc3, 0x3c, 0x61, 0xcc, + 0x40, 0x9e, 0x06, 0x52, 0x1b, 0x32, 0x8c, 0x12, 0x93, 0xbf, 0xef, 0x3b, 0x25, 0x0d, 0xc2, + 0x88, 0xd1, 0xe0, 0x07, 0x2d, 0x70, 0xc6, 0x29, 0x6a, 0x4d, 0x47, 0x26, 0xa3, 0xe4, 0x8b, + 0xf6, 0x97, 0x2c, 0x5d, 0x3d, 0xd7, 0x96, 0x28, 0x02, 0x08, 0x30, 0xa7, 0x22, 0xc9, 0x65, + 0xf8, 0xb7, 0xb4, 0x8a, 0xca, 0xb9, 0xf2, 0xd0, 0x17, 0xff, 0x46, 0xfb, 0x9a, 0xba, 0x8f, + 0xb6, 0x69, 0x68, 0x8e, 0x21, 0x6f, 0xc4, 0xcb, 0xb3, 0xce, 0x51, 0xd4, 0x81, 0x00, 0x2e, + 0x9c, 0x74, 0x63, 0x45, 0xd9, 0x16, 0x35, 0x5f, 0xed, 0x78, 0x9f, 0x01, 0x48, 0x04, 0xc1, + 0x33, 0xd6, 0x4f, 0x94, 0xde, 0x31, 0x9d, 0x0a, 0xac, 0x18, 0x4b, 0xcd, 0x98, 0xb8, 0x37, + 0xa2, 0x83, 0xec, 0x03, 0xd8, 0xda, 0xe5, 0x7a, 0x6b, 0x53, 0xd5, 0x15, 0xa4, 0x43, 0xe9, + 0x90, 0x67, 0x58, 0xc0, 0xa5, 0xfa, 0x2a, 0xb1, 0x75, 0x50, 0x39, 0x5c, 0xe6, 0xdc, 0x89, + 0xfc, 0xcf, 0xfe, 0xf9, 0x57, 0x54, 0x64, 0xa8, 0xee, 0x23, 0x0b, 0xf1, 0xea, 0xfd, 0xdb, + 0xbd, 0x09, 0xb5, 0x5b, 0x05, 0x86, 0x13, 0xf3, 0x24, 0xc5, 0x3f, 0x44, 0x72, 0x7c, 0x7e, + 0x36}; + + for(size_t i = 0; i < length; i++) { + data[i] = lut[data[i]]; + } +} + +static void protocol_gallagher_decode(ProtocolGallagher* protocol) { + bit_lib_remove_bit_every_nth(protocol->encoded_data, 16, 9 * 8, 9); + protocol_gallagher_descramble(protocol->encoded_data + 2, 8); + + // Region code + bit_lib_set_bits(protocol->data, 0, (protocol->encoded_data[5] & 0x1E) >> 1, 4); + + // Issue Level + bit_lib_set_bits(protocol->data, 4, (protocol->encoded_data[9] & 0x0F), 4); + + // Facility Code + uint32_t fc = (protocol->encoded_data[7] & 0x0F) << 12 | protocol->encoded_data[3] << 4 | + ((protocol->encoded_data[9] >> 4) & 0x0F); + protocol->data[3] = (uint8_t)fc; + protocol->data[2] = (uint8_t)(fc >>= 8); + protocol->data[1] = (uint8_t)(fc >>= 8); + + // Card Number + uint32_t card = protocol->encoded_data[2] << 16 | (protocol->encoded_data[6] & 0x1F) << 11 | + protocol->encoded_data[4] << 3 | (protocol->encoded_data[5] & 0xE0) >> 5; + protocol->data[7] = (uint8_t)card; + protocol->data[6] = (uint8_t)(card >>= 8); + protocol->data[5] = (uint8_t)(card >>= 8); + protocol->data[4] = (uint8_t)(card >>= 8); +} + +static bool protocol_gallagher_can_be_decoded(ProtocolGallagher* protocol) { + // check 16 bits preamble + if(bit_lib_get_bits_16(protocol->encoded_data, 0, 16) != 0b0111111111101010) return false; + + // check next 16 bits preamble + if(bit_lib_get_bits_16(protocol->encoded_data, 96, 16) != 0b0111111111101010) return false; + + uint8_t checksum_arr[8] = {0}; + for(int i = 0, pos = 0; i < 8; i++) { + // Following the preamble, every 9th bit is a checksum-bit for the preceding byte + pos = 16 + (9 * i); + checksum_arr[i] = bit_lib_get_bits(protocol->encoded_data, pos, 8); + } + uint8_t crc = bit_lib_get_bits(protocol->encoded_data, 16 + (9 * 8), 8); + uint8_t calc_crc = bit_lib_crc8(checksum_arr, 8, 0x7, 0x2c, false, false, 0x00); + + // crc + if(crc != calc_crc) return false; + + return true; +} + +void protocol_gallagher_decoder_start(ProtocolGallagher* protocol) { + memset(protocol->encoded_data, 0, GALLAGHER_ENCODED_BYTE_FULL_SIZE); + manchester_advance( + protocol->decoder_manchester_state, + ManchesterEventReset, + &protocol->decoder_manchester_state, + NULL); +}; + +bool protocol_gallagher_decoder_feed(ProtocolGallagher* protocol, bool level, uint32_t duration) { + bool result = false; + + ManchesterEvent event = ManchesterEventReset; + + if(duration > GALLAGHER_READ_SHORT_TIME_LOW && duration < GALLAGHER_READ_SHORT_TIME_HIGH) { + if(!level) { + event = ManchesterEventShortHigh; + } else { + event = ManchesterEventShortLow; + } + } else if(duration > GALLAGHER_READ_LONG_TIME_LOW && duration < GALLAGHER_READ_LONG_TIME_HIGH) { + if(!level) { + event = ManchesterEventLongHigh; + } else { + event = ManchesterEventLongLow; + } + } + + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + protocol->decoder_manchester_state, event, &protocol->decoder_manchester_state, &data); + + if(data_ok) { + bit_lib_push_bit(protocol->encoded_data, GALLAGHER_ENCODED_BYTE_FULL_SIZE, data); + + if(protocol_gallagher_can_be_decoded(protocol)) { + protocol_gallagher_decode(protocol); + result = true; + } + } + } + + return result; +}; + +bool protocol_gallagher_encoder_start(ProtocolGallagher* protocol) { + // Preamble + bit_lib_set_bits(protocol->encoded_data, 0, 0b01111111, 8); + bit_lib_set_bits(protocol->encoded_data, 8, 0b11101010, 8); + + uint8_t rc = bit_lib_get_bits(protocol->data, 0, 4); + uint8_t il = bit_lib_get_bits(protocol->data, 4, 4); + uint32_t fc = bit_lib_get_bits_32(protocol->data, 8, 24); + uint32_t cn = bit_lib_get_bits_32(protocol->data, 32, 32); + + uint8_t payload[8] = {0}; + payload[0] = (cn & 0xffffff) >> 16; + payload[1] = (fc & 0xfff) >> 4; + payload[2] = (cn & 0x7ff) >> 3; + payload[3] = (cn & 0x7) << 5 | (rc & 0xf) << 1; + payload[4] = (cn & 0xffff) >> 11; + payload[5] = (fc & 0xffff) >> 12; + payload[6] = 0; + payload[7] = (fc & 0xf) << 4 | (il & 0xf); + + // Gallagher scramble + protocol_gallagher_scramble(payload, 8); + + for(int i = 0; i < 8; i++) { + // data byte + bit_lib_set_bits(protocol->encoded_data, 16 + (i * 9), payload[i], 8); + + // every byte is followed by a bit which is the inverse of the last bit + bit_lib_set_bit(protocol->encoded_data, 16 + (i * 9) + 8, !(payload[i] & 0x1)); + } + + // checksum + uint8_t crc = bit_lib_crc8(payload, 8, 0x7, 0x2c, false, false, 0x00); + bit_lib_set_bits(protocol->encoded_data, 16 + (9 * 8), crc, 8); + + return true; +}; + +LevelDuration protocol_gallagher_encoder_yield(ProtocolGallagher* protocol) { + bool level = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_data_index); + uint32_t duration = GALLAGHER_CLOCK_PER_BIT / 2; + + if(protocol->encoded_polarity) { + protocol->encoded_polarity = false; + } else { + level = !level; + + protocol->encoded_polarity = true; + bit_lib_increment_index(protocol->encoded_data_index, GALLAGHER_ENCODED_BIT_SIZE); + } + + return level_duration_make(level, duration); +}; + +bool protocol_gallagher_write_data(ProtocolGallagher* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + // Correct protocol data by redecoding + protocol_gallagher_encoder_start(protocol); + protocol_gallagher_decode(protocol); + + protocol_gallagher_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = + (LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_32 | + (3 << LFRFID_T5577_MAXBLOCK_SHIFT)); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.blocks_to_write = 4; + result = true; + } + return result; +}; + +void protocol_gallagher_render_data(ProtocolGallagher* protocol, FuriString* result) { + UNUSED(protocol); + uint8_t rc = bit_lib_get_bits(protocol->data, 0, 4); + uint8_t il = bit_lib_get_bits(protocol->data, 4, 4); + uint32_t fc = bit_lib_get_bits_32(protocol->data, 8, 24); + uint32_t card_id = bit_lib_get_bits_32(protocol->data, 32, 32); + + furi_string_cat_printf(result, "Region: %u, Issue Level: %u\r\n", rc, il); + furi_string_cat_printf(result, "FC: %lu, C: %lu\r\n", fc, card_id); +}; + +const ProtocolBase protocol_gallagher = { + .name = "Gallagher", + .manufacturer = "Gallagher", + .data_size = GALLAGHER_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_gallagher_alloc, + .free = (ProtocolFree)protocol_gallagher_free, + .get_data = (ProtocolGetData)protocol_gallagher_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_gallagher_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_gallagher_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_gallagher_encoder_start, + .yield = (ProtocolEncoderYield)protocol_gallagher_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_gallagher_render_data, + .render_brief_data = (ProtocolRenderData)protocol_gallagher_render_data, + .write_data = (ProtocolWriteData)protocol_gallagher_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_gallagher.h b/lib/lfrfid/protocols/protocol_gallagher.h new file mode 100644 index 00000000000..2d922f605e4 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_gallagher.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_gallagher; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_h10301.c b/lib/lfrfid/protocols/protocol_h10301.c new file mode 100644 index 00000000000..2d7a3e669e2 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_h10301.c @@ -0,0 +1,390 @@ +#include +#include +#include +#include +#include "lfrfid_protocols.h" + +#define JITTER_TIME (20) +#define MIN_TIME (64 - JITTER_TIME) +#define MAX_TIME (80 + JITTER_TIME) + +#define H10301_DECODED_DATA_SIZE (3) +#define H10301_ENCODED_DATA_SIZE_U32 (3) +#define H10301_ENCODED_DATA_SIZE (sizeof(uint32_t) * H10301_ENCODED_DATA_SIZE_U32) + +#define H10301_BIT_SIZE (sizeof(uint32_t) * 8) +#define H10301_BIT_MAX_SIZE (H10301_BIT_SIZE * H10301_DECODED_DATA_SIZE) + +typedef struct { + FSKDemod* fsk_demod; +} ProtocolH10301Decoder; + +typedef struct { + FSKOsc* fsk_osc; + uint8_t encoded_index; + uint32_t pulse; +} ProtocolH10301Encoder; + +typedef struct { + ProtocolH10301Decoder decoder; + ProtocolH10301Encoder encoder; + uint32_t encoded_data[H10301_ENCODED_DATA_SIZE_U32]; + uint8_t data[H10301_DECODED_DATA_SIZE]; +} ProtocolH10301; + +ProtocolH10301* protocol_h10301_alloc(void) { + ProtocolH10301* protocol = malloc(sizeof(ProtocolH10301)); + protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5); + protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50); + + return protocol; +}; + +void protocol_h10301_free(ProtocolH10301* protocol) { + fsk_demod_free(protocol->decoder.fsk_demod); + fsk_osc_free(protocol->encoder.fsk_osc); + free(protocol); +}; + +uint8_t* protocol_h10301_get_data(ProtocolH10301* protocol) { + return protocol->data; +}; + +void protocol_h10301_decoder_start(ProtocolH10301* protocol) { + memset(protocol->encoded_data, 0, sizeof(uint32_t) * 3); +}; + +static void protocol_h10301_decoder_store_data(ProtocolH10301* protocol, bool data) { + protocol->encoded_data[0] = (protocol->encoded_data[0] << 1) | + ((protocol->encoded_data[1] >> 31) & 1); + protocol->encoded_data[1] = (protocol->encoded_data[1] << 1) | + ((protocol->encoded_data[2] >> 31) & 1); + protocol->encoded_data[2] = (protocol->encoded_data[2] << 1) | data; +} + +static bool protocol_h10301_can_be_decoded(const uint32_t* card_data) { + const uint8_t* encoded_data = (const uint8_t*)card_data; + + // packet preamble + // raw data + if(*(encoded_data + 3) != 0x1D) { + return false; + } + + // encoded company/oem + // coded with 01 = 0, 10 = 1 transitions + // stored in word 0 + if((*card_data >> 10 & 0x3FFF) != 0x1556) { + return false; + } + + // encoded format/length + // coded with 01 = 0, 10 = 1 transitions + // stored in word 0 and word 1 + if((((*card_data & 0x3FF) << 12) | ((*(card_data + 1) >> 20) & 0xFFF)) != 0x155556) { + return false; + } + + // data decoding + uint32_t result = 0; + + // decode from word 1 + // coded with 01 = 0, 10 = 1 transitions + for(int8_t i = 9; i >= 0; i--) { + switch((*(card_data + 1) >> (2 * i)) & 0b11) { + case 0b01: + result = (result << 1) | 0; + break; + case 0b10: + result = (result << 1) | 1; + break; + default: + return false; + break; + } + } + + // decode from word 2 + // coded with 01 = 0, 10 = 1 transitions + for(int8_t i = 15; i >= 0; i--) { + switch((*(card_data + 2) >> (2 * i)) & 0b11) { + case 0b01: + result = (result << 1) | 0; + break; + case 0b10: + result = (result << 1) | 1; + break; + default: + return false; + break; + } + } + + // trailing parity (odd) test + uint8_t parity_sum = 0; + for(int8_t i = 0; i < 13; i++) { + if(((result >> i) & 1) == 1) { + parity_sum++; + } + } + + if((parity_sum % 2) != 1) { + return false; + } + + // leading parity (even) test + parity_sum = 0; + for(int8_t i = 13; i < 26; i++) { + if(((result >> i) & 1) == 1) { + parity_sum++; + } + } + + if((parity_sum % 2) == 1) { + return false; + } + + return true; +} + +static void protocol_h10301_decode(const uint32_t* card_data, uint8_t* decoded_data) { + // data decoding + uint32_t result = 0; + + // decode from word 1 + // coded with 01 = 0, 10 = 1 transitions + for(int8_t i = 9; i >= 0; i--) { + switch((*(card_data + 1) >> (2 * i)) & 0b11) { + case 0b01: + result = (result << 1) | 0; + break; + case 0b10: + result = (result << 1) | 1; + break; + default: + break; + } + } + + // decode from word 2 + // coded with 01 = 0, 10 = 1 transitions + for(int8_t i = 15; i >= 0; i--) { + switch((*(card_data + 2) >> (2 * i)) & 0b11) { + case 0b01: + result = (result << 1) | 0; + break; + case 0b10: + result = (result << 1) | 1; + break; + default: + break; + } + } + + uint8_t data[H10301_DECODED_DATA_SIZE] = { + (uint8_t)(result >> 17), (uint8_t)(result >> 9), (uint8_t)(result >> 1)}; + + memcpy(decoded_data, &data, H10301_DECODED_DATA_SIZE); +} + +bool protocol_h10301_decoder_feed(ProtocolH10301* protocol, bool level, uint32_t duration) { + bool value; + uint32_t count; + bool result = false; + + fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count); + if(count > 0) { + for(size_t i = 0; i < count; i++) { + protocol_h10301_decoder_store_data(protocol, value); + if(protocol_h10301_can_be_decoded(protocol->encoded_data)) { + protocol_h10301_decode(protocol->encoded_data, protocol->data); + result = true; + break; + } + } + } + + return result; +}; + +static void protocol_h10301_write_raw_bit(bool bit, uint8_t position, uint32_t* card_data) { + if(bit) { + card_data[position / H10301_BIT_SIZE] |= + 1UL << (H10301_BIT_SIZE - (position % H10301_BIT_SIZE) - 1); + } else { + card_data[position / H10301_BIT_SIZE] &= + ~(1UL << (H10301_BIT_SIZE - (position % H10301_BIT_SIZE) - 1)); + } +} + +static void protocol_h10301_write_bit(bool bit, uint8_t position, uint32_t* card_data) { + protocol_h10301_write_raw_bit(bit, position + 0, card_data); + protocol_h10301_write_raw_bit(!bit, position + 1, card_data); +} + +void protocol_h10301_encode(const uint8_t* decoded_data, uint8_t* encoded_data) { + uint32_t card_data[H10301_DECODED_DATA_SIZE] = {0, 0, 0}; + + uint32_t fc_cn = (decoded_data[0] << 16) | (decoded_data[1] << 8) | decoded_data[2]; + + // even parity sum calculation (high 12 bits of data) + uint8_t even_parity_sum = 0; + for(int8_t i = 12; i < 24; i++) { + if(((fc_cn >> i) & 1) == 1) { + even_parity_sum++; + } + } + + // odd parity sum calculation (low 12 bits of data) + uint8_t odd_parity_sum = 1; + for(int8_t i = 0; i < 12; i++) { + if(((fc_cn >> i) & 1) == 1) { + odd_parity_sum++; + } + } + + // 0x1D preamble + protocol_h10301_write_raw_bit(0, 0, card_data); + protocol_h10301_write_raw_bit(0, 1, card_data); + protocol_h10301_write_raw_bit(0, 2, card_data); + protocol_h10301_write_raw_bit(1, 3, card_data); + protocol_h10301_write_raw_bit(1, 4, card_data); + protocol_h10301_write_raw_bit(1, 5, card_data); + protocol_h10301_write_raw_bit(0, 6, card_data); + protocol_h10301_write_raw_bit(1, 7, card_data); + + // company / OEM code 1 + protocol_h10301_write_bit(0, 8, card_data); + protocol_h10301_write_bit(0, 10, card_data); + protocol_h10301_write_bit(0, 12, card_data); + protocol_h10301_write_bit(0, 14, card_data); + protocol_h10301_write_bit(0, 16, card_data); + protocol_h10301_write_bit(0, 18, card_data); + protocol_h10301_write_bit(1, 20, card_data); + + // card format / length 1 + protocol_h10301_write_bit(0, 22, card_data); + protocol_h10301_write_bit(0, 24, card_data); + protocol_h10301_write_bit(0, 26, card_data); + protocol_h10301_write_bit(0, 28, card_data); + protocol_h10301_write_bit(0, 30, card_data); + protocol_h10301_write_bit(0, 32, card_data); + protocol_h10301_write_bit(0, 34, card_data); + protocol_h10301_write_bit(0, 36, card_data); + protocol_h10301_write_bit(0, 38, card_data); + protocol_h10301_write_bit(0, 40, card_data); + protocol_h10301_write_bit(1, 42, card_data); + + // even parity bit + protocol_h10301_write_bit((even_parity_sum % 2), 44, card_data); + + // data + for(uint8_t i = 0; i < 24; i++) { + protocol_h10301_write_bit((fc_cn >> (23 - i)) & 1, 46 + (i * 2), card_data); + } + + // odd parity bit + protocol_h10301_write_bit((odd_parity_sum % 2), 94, card_data); + + memcpy(encoded_data, &card_data, H10301_ENCODED_DATA_SIZE); +} + +bool protocol_h10301_encoder_start(ProtocolH10301* protocol) { + protocol_h10301_encode(protocol->data, (uint8_t*)protocol->encoded_data); + protocol->encoder.encoded_index = 0; + protocol->encoder.pulse = 0; + + return true; +}; + +LevelDuration protocol_h10301_encoder_yield(ProtocolH10301* protocol) { + bool level = 0; + uint32_t duration = 0; + + // if pulse is zero, we need to output high, otherwise we need to output low + if(protocol->encoder.pulse == 0) { + // get bit + uint8_t bit = + (protocol->encoded_data[protocol->encoder.encoded_index / H10301_BIT_SIZE] >> + ((H10301_BIT_SIZE - 1) - (protocol->encoder.encoded_index % H10301_BIT_SIZE))) & + 1; + + // get pulse from oscillator + bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration); + + if(advance) { + protocol->encoder.encoded_index++; + if(protocol->encoder.encoded_index >= (H10301_BIT_MAX_SIZE)) { + protocol->encoder.encoded_index = 0; + } + } + + // duration diveded by 2 because we need to output high and low + duration = duration / 2; + protocol->encoder.pulse = duration; + level = true; + } else { + // output low half and reset pulse + duration = protocol->encoder.pulse; + protocol->encoder.pulse = 0; + level = false; + } + + return level_duration_make(level, duration); +}; + +bool protocol_h10301_write_data(ProtocolH10301* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + // Correct protocol data by redecoding + protocol_h10301_encoder_start(protocol); + protocol_h10301_decode(protocol->encoded_data, protocol->data); + + protocol_h10301_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 | + (3 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = protocol->encoded_data[0]; + request->t5577.block[2] = protocol->encoded_data[1]; + request->t5577.block[3] = protocol->encoded_data[2]; + request->t5577.blocks_to_write = 4; + result = true; + } + return result; +}; + +void protocol_h10301_render_data(ProtocolH10301* protocol, FuriString* result) { + uint8_t* data = protocol->data; + furi_string_printf( + result, + "FC: %u\r\n" + "Card: %u", + data[0], + (uint16_t)((data[1] << 8) | (data[2]))); +}; + +const ProtocolBase protocol_h10301 = { + .name = "H10301", + .manufacturer = "HID", + .data_size = H10301_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_h10301_alloc, + .free = (ProtocolFree)protocol_h10301_free, + .get_data = (ProtocolGetData)protocol_h10301_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_h10301_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_h10301_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_h10301_encoder_start, + .yield = (ProtocolEncoderYield)protocol_h10301_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_h10301_render_data, + .render_brief_data = (ProtocolRenderData)protocol_h10301_render_data, + .write_data = (ProtocolWriteData)protocol_h10301_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_h10301.h b/lib/lfrfid/protocols/protocol_h10301.h new file mode 100644 index 00000000000..b7ee5ad57d9 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_h10301.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_h10301; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_hid_ex_generic.c b/lib/lfrfid/protocols/protocol_hid_ex_generic.c new file mode 100644 index 00000000000..35500ab595b --- /dev/null +++ b/lib/lfrfid/protocols/protocol_hid_ex_generic.c @@ -0,0 +1,223 @@ +#include +#include +#include +#include +#include "lfrfid_protocols.h" +#include + +#define JITTER_TIME (20) +#define MIN_TIME (64 - JITTER_TIME) +#define MAX_TIME (80 + JITTER_TIME) + +#define HID_DATA_SIZE 23 +#define HID_PREAMBLE_SIZE 1 + +#define HID_ENCODED_DATA_SIZE (HID_PREAMBLE_SIZE + HID_DATA_SIZE + HID_PREAMBLE_SIZE) +#define HID_ENCODED_BIT_SIZE ((HID_PREAMBLE_SIZE + HID_DATA_SIZE) * 8) +#define HID_DECODED_DATA_SIZE (12) +#define HID_DECODED_BIT_SIZE ((HID_ENCODED_BIT_SIZE - HID_PREAMBLE_SIZE * 8) / 2) + +#define HID_PREAMBLE 0x1D + +typedef struct { + FSKDemod* fsk_demod; +} ProtocolHIDExDecoder; + +typedef struct { + FSKOsc* fsk_osc; + uint8_t encoded_index; + uint32_t pulse; +} ProtocolHIDExEncoder; + +typedef struct { + ProtocolHIDExDecoder decoder; + ProtocolHIDExEncoder encoder; + uint8_t encoded_data[HID_ENCODED_DATA_SIZE]; + uint8_t data[HID_DECODED_DATA_SIZE]; + size_t protocol_size; +} ProtocolHIDEx; + +ProtocolHIDEx* protocol_hid_ex_generic_alloc(void) { + ProtocolHIDEx* protocol = malloc(sizeof(ProtocolHIDEx)); + protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5); + protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50); + + return protocol; +}; + +void protocol_hid_ex_generic_free(ProtocolHIDEx* protocol) { + fsk_demod_free(protocol->decoder.fsk_demod); + fsk_osc_free(protocol->encoder.fsk_osc); + free(protocol); +}; + +uint8_t* protocol_hid_ex_generic_get_data(ProtocolHIDEx* protocol) { + return protocol->data; +}; + +void protocol_hid_ex_generic_decoder_start(ProtocolHIDEx* protocol) { + memset(protocol->encoded_data, 0, HID_ENCODED_DATA_SIZE); +}; + +static bool protocol_hid_ex_generic_can_be_decoded(const uint8_t* data) { + // check preamble + if(data[0] != HID_PREAMBLE || data[HID_PREAMBLE_SIZE + HID_DATA_SIZE] != HID_PREAMBLE) { + return false; + } + + // check for manchester encoding + for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) { + for(size_t n = 0; n < 4; n++) { + uint8_t bit_pair = (data[i] >> (n * 2)) & 0b11; + if(bit_pair == 0b11 || bit_pair == 0b00) { + return false; + } + } + } + + return true; +} + +static void protocol_hid_ex_generic_decode(const uint8_t* from, uint8_t* to) { + size_t bit_index = 0; + for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) { + for(size_t n = 0; n < 4; n++) { + uint8_t bit_pair = (from[i] >> (6 - (n * 2))) & 0b11; + if(bit_pair == 0b01) { + bit_lib_set_bit(to, bit_index, 0); + } else if(bit_pair == 0b10) { + bit_lib_set_bit(to, bit_index, 1); + } + bit_index++; + } + } +} + +bool protocol_hid_ex_generic_decoder_feed(ProtocolHIDEx* protocol, bool level, uint32_t duration) { + bool value; + uint32_t count; + bool result = false; + + fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count); + if(count > 0) { + for(size_t i = 0; i < count; i++) { + bit_lib_push_bit(protocol->encoded_data, HID_ENCODED_DATA_SIZE, value); + if(protocol_hid_ex_generic_can_be_decoded(protocol->encoded_data)) { + protocol_hid_ex_generic_decode(protocol->encoded_data, protocol->data); + result = true; + } + } + } + + return result; +}; + +static void protocol_hid_ex_generic_encode(ProtocolHIDEx* protocol) { + protocol->encoded_data[0] = HID_PREAMBLE; + + size_t bit_index = 0; + for(size_t i = 0; i < HID_DECODED_BIT_SIZE; i++) { + bool bit = bit_lib_get_bit(protocol->data, i); + if(bit) { + bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 1); + bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 0); + } else { + bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 0); + bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 1); + } + bit_index += 2; + } +} + +bool protocol_hid_ex_generic_encoder_start(ProtocolHIDEx* protocol) { + protocol->encoder.encoded_index = 0; + protocol->encoder.pulse = 0; + protocol_hid_ex_generic_encode(protocol); + + return true; +}; + +LevelDuration protocol_hid_ex_generic_encoder_yield(ProtocolHIDEx* protocol) { + bool level = 0; + uint32_t duration = 0; + + // if pulse is zero, we need to output high, otherwise we need to output low + if(protocol->encoder.pulse == 0) { + // get bit + uint8_t bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index); + + // get pulse from oscillator + bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration); + + if(advance) { + bit_lib_increment_index(protocol->encoder.encoded_index, HID_ENCODED_BIT_SIZE); + } + + // duration diveded by 2 because we need to output high and low + duration = duration / 2; + protocol->encoder.pulse = duration; + level = true; + } else { + // output low half and reset pulse + duration = protocol->encoder.pulse; + protocol->encoder.pulse = 0; + level = false; + } + + return level_duration_make(level, duration); +}; + +bool protocol_hid_ex_generic_write_data(ProtocolHIDEx* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + // Correct protocol data by redecoding + protocol_hid_ex_generic_encoder_start(protocol); + protocol_hid_ex_generic_decode(protocol->encoded_data, protocol->data); + + protocol_hid_ex_generic_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 | + (6 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.block[4] = bit_lib_get_bits_32(protocol->encoded_data, 96, 32); + request->t5577.block[5] = bit_lib_get_bits_32(protocol->encoded_data, 128, 32); + request->t5577.block[6] = bit_lib_get_bits_32(protocol->encoded_data, 160, 32); + request->t5577.blocks_to_write = 7; + result = true; + } + return result; +}; + +void protocol_hid_ex_generic_render_data(ProtocolHIDEx* protocol, FuriString* result) { + // TODO FL-3518: parser and render functions + UNUSED(protocol); + furi_string_printf(result, "Generic HID Extended\r\nData: Unknown"); +}; + +const ProtocolBase protocol_hid_ex_generic = { + .name = "HIDExt", + .manufacturer = "Generic", + .data_size = HID_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_hid_ex_generic_alloc, + .free = (ProtocolFree)protocol_hid_ex_generic_free, + .get_data = (ProtocolGetData)protocol_hid_ex_generic_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_hid_ex_generic_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_hid_ex_generic_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_hid_ex_generic_encoder_start, + .yield = (ProtocolEncoderYield)protocol_hid_ex_generic_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_hid_ex_generic_render_data, + .render_brief_data = (ProtocolRenderData)protocol_hid_ex_generic_render_data, + .write_data = (ProtocolWriteData)protocol_hid_ex_generic_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_hid_ex_generic.h b/lib/lfrfid/protocols/protocol_hid_ex_generic.h new file mode 100644 index 00000000000..9c4ddffff3e --- /dev/null +++ b/lib/lfrfid/protocols/protocol_hid_ex_generic.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_hid_ex_generic; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_hid_generic.c b/lib/lfrfid/protocols/protocol_hid_generic.c new file mode 100644 index 00000000000..e07021403de --- /dev/null +++ b/lib/lfrfid/protocols/protocol_hid_generic.c @@ -0,0 +1,288 @@ +#include +#include +#include +#include +#include "lfrfid_protocols.h" +#include + +#define JITTER_TIME (20) +#define MIN_TIME (64 - JITTER_TIME) +#define MAX_TIME (80 + JITTER_TIME) + +#define HID_DATA_SIZE 11 +#define HID_PREAMBLE_SIZE 1 +#define HID_PROTOCOL_SIZE_UNKNOWN 0 + +#define HID_ENCODED_DATA_SIZE (HID_PREAMBLE_SIZE + HID_DATA_SIZE + HID_PREAMBLE_SIZE) +#define HID_ENCODED_BIT_SIZE ((HID_PREAMBLE_SIZE + HID_DATA_SIZE) * 8) +#define HID_DECODED_DATA_SIZE (6) +#define HID_DECODED_BIT_SIZE ((HID_ENCODED_BIT_SIZE - HID_PREAMBLE_SIZE * 8) / 2) + +#define HID_PREAMBLE 0x1D + +typedef struct { + FSKDemod* fsk_demod; +} ProtocolHIDDecoder; + +typedef struct { + FSKOsc* fsk_osc; + uint8_t encoded_index; + uint32_t pulse; +} ProtocolHIDEncoder; + +typedef struct { + ProtocolHIDDecoder decoder; + ProtocolHIDEncoder encoder; + uint8_t encoded_data[HID_ENCODED_DATA_SIZE]; + uint8_t data[HID_DECODED_DATA_SIZE]; +} ProtocolHID; + +ProtocolHID* protocol_hid_generic_alloc(void) { + ProtocolHID* protocol = malloc(sizeof(ProtocolHID)); + protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5); + protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50); + + return protocol; +}; + +void protocol_hid_generic_free(ProtocolHID* protocol) { + fsk_demod_free(protocol->decoder.fsk_demod); + fsk_osc_free(protocol->encoder.fsk_osc); + free(protocol); +}; + +uint8_t* protocol_hid_generic_get_data(ProtocolHID* protocol) { + return protocol->data; +}; + +void protocol_hid_generic_decoder_start(ProtocolHID* protocol) { + memset(protocol->encoded_data, 0, HID_ENCODED_DATA_SIZE); +}; + +static bool protocol_hid_generic_can_be_decoded(const uint8_t* data) { + // check preamble + if(data[0] != HID_PREAMBLE || data[HID_PREAMBLE_SIZE + HID_DATA_SIZE] != HID_PREAMBLE) { + return false; + } + + // check for manchester encoding + for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) { + for(size_t n = 0; n < 4; n++) { + uint8_t bit_pair = (data[i] >> (n * 2)) & 0b11; + if(bit_pair == 0b11 || bit_pair == 0b00) { + return false; + } + } + } + + return true; +} + +static void protocol_hid_generic_decode(const uint8_t* from, uint8_t* to) { + size_t bit_index = 0; + for(size_t i = HID_PREAMBLE_SIZE; i < (HID_PREAMBLE_SIZE + HID_DATA_SIZE); i++) { + for(size_t n = 0; n < 4; n++) { + uint8_t bit_pair = (from[i] >> (6 - (n * 2))) & 0b11; + if(bit_pair == 0b01) { + bit_lib_set_bit(to, bit_index, 0); + } else if(bit_pair == 0b10) { + bit_lib_set_bit(to, bit_index, 1); + } + bit_index++; + } + } +} + +/** + * Decodes size from the HID Proximity header: + * - If any of the first six bits is 1, the key is composed of the bits + * following the first 1 + * - Otherwise, if the first six bits are 0: + * - If the seventh bit is 0, the key is composed of the remaining 37 bits. + * - If the seventh bit is 1, the size header continues until the next 1 bit, + * and the key is composed of however many bits remain. + * + * HID Proximity keys are 26 bits at minimum. If the header implies a key size + * under 26 bits, this function returns HID_PROTOCOL_SIZE_UNKNOWN. + */ +static uint8_t protocol_hid_generic_decode_protocol_size(ProtocolHID* protocol) { + for(size_t bit_index = 0; bit_index < 6; bit_index++) { + if(bit_lib_get_bit(protocol->data, bit_index)) { + return HID_DECODED_BIT_SIZE - bit_index - 1; + } + } + + if(!bit_lib_get_bit(protocol->data, 6)) { + return 37; + } + + size_t bit_index = 7; + uint8_t size = 36; + while(!bit_lib_get_bit(protocol->data, bit_index) && size >= 26) { + size--; + bit_index++; + } + return size < 26 ? HID_PROTOCOL_SIZE_UNKNOWN : size; +} + +bool protocol_hid_generic_decoder_feed(ProtocolHID* protocol, bool level, uint32_t duration) { + bool value; + uint32_t count; + bool result = false; + + fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count); + if(count > 0) { + for(size_t i = 0; i < count; i++) { + bit_lib_push_bit(protocol->encoded_data, HID_ENCODED_DATA_SIZE, value); + if(protocol_hid_generic_can_be_decoded(protocol->encoded_data)) { + protocol_hid_generic_decode(protocol->encoded_data, protocol->data); + result = true; + } + } + } + + return result; +}; + +static void protocol_hid_generic_encode(ProtocolHID* protocol) { + protocol->encoded_data[0] = HID_PREAMBLE; + + size_t bit_index = 0; + for(size_t i = 0; i < HID_DECODED_BIT_SIZE; i++) { + bool bit = bit_lib_get_bit(protocol->data, i); + if(bit) { + bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 1); + bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 0); + } else { + bit_lib_set_bit(protocol->encoded_data, 8 + bit_index, 0); + bit_lib_set_bit(protocol->encoded_data, 8 + bit_index + 1, 1); + } + bit_index += 2; + } +} + +bool protocol_hid_generic_encoder_start(ProtocolHID* protocol) { + protocol->encoder.encoded_index = 0; + protocol->encoder.pulse = 0; + protocol_hid_generic_encode(protocol); + + return true; +}; + +LevelDuration protocol_hid_generic_encoder_yield(ProtocolHID* protocol) { + bool level = 0; + uint32_t duration = 0; + + // if pulse is zero, we need to output high, otherwise we need to output low + if(protocol->encoder.pulse == 0) { + // get bit + uint8_t bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index); + + // get pulse from oscillator + bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration); + + if(advance) { + bit_lib_increment_index(protocol->encoder.encoded_index, HID_ENCODED_BIT_SIZE); + } + + // duration diveded by 2 because we need to output high and low + duration = duration / 2; + protocol->encoder.pulse = duration; + level = true; + } else { + // output low half and reset pulse + duration = protocol->encoder.pulse; + protocol->encoder.pulse = 0; + level = false; + } + + return level_duration_make(level, duration); +}; + +bool protocol_hid_generic_write_data(ProtocolHID* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + // Correct protocol data by redecoding + protocol_hid_generic_encoder_start(protocol); + protocol_hid_generic_decode(protocol->encoded_data, protocol->data); + + protocol_hid_generic_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 | + (3 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.blocks_to_write = 4; + result = true; + } + return result; +}; + +static void protocol_hid_generic_string_cat_protocol_bits( + ProtocolHID* protocol, + uint8_t protocol_size, + FuriString* result) { + // round up to the nearest nibble + const uint8_t hex_character_count = (protocol_size + 3) / 4; + const uint8_t protocol_bit_index = HID_DECODED_BIT_SIZE - protocol_size; + + for(size_t i = 0; i < hex_character_count; i++) { + uint8_t nibble = i == 0 ? bit_lib_get_bits( + protocol->data, + protocol_bit_index, + protocol_size % 4 == 0 ? 4 : protocol_size % 4) : + bit_lib_get_bits(protocol->data, protocol_bit_index + i * 4, 4); + furi_string_cat_printf(result, "%X", nibble & 0xF); + } +} + +void protocol_hid_generic_render_data(ProtocolHID* protocol, FuriString* result) { + const uint8_t protocol_size = protocol_hid_generic_decode_protocol_size(protocol); + + if(protocol_size == HID_PROTOCOL_SIZE_UNKNOWN) { + furi_string_printf( + result, + "Generic HID Proximity\r\n" + "Data: %02X%02X%02X%02X%02X%X", + protocol->data[0], + protocol->data[1], + protocol->data[2], + protocol->data[3], + protocol->data[4], + protocol->data[5] >> 4); + } else { + furi_string_printf( + result, + "%hhu-bit HID Proximity\r\n" + "Data: ", + protocol_size); + protocol_hid_generic_string_cat_protocol_bits(protocol, protocol_size, result); + } +}; + +const ProtocolBase protocol_hid_generic = { + .name = "HIDProx", + .manufacturer = "Generic", + .data_size = HID_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 6, + .alloc = (ProtocolAlloc)protocol_hid_generic_alloc, + .free = (ProtocolFree)protocol_hid_generic_free, + .get_data = (ProtocolGetData)protocol_hid_generic_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_hid_generic_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_hid_generic_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_hid_generic_encoder_start, + .yield = (ProtocolEncoderYield)protocol_hid_generic_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_hid_generic_render_data, + .render_brief_data = (ProtocolRenderData)protocol_hid_generic_render_data, + .write_data = (ProtocolWriteData)protocol_hid_generic_write_data, +}; diff --git a/lib/lfrfid/protocols/protocol_hid_generic.h b/lib/lfrfid/protocols/protocol_hid_generic.h new file mode 100644 index 00000000000..22e78a4d34f --- /dev/null +++ b/lib/lfrfid/protocols/protocol_hid_generic.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_hid_generic; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_idteck.c b/lib/lfrfid/protocols/protocol_idteck.c new file mode 100644 index 00000000000..033fcd28c09 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_idteck.c @@ -0,0 +1,269 @@ +#include +#include +#include +#include "lfrfid_protocols.h" + +// Example: 4944544B 351FBE4B +// 01001001 01000100 01010100 01001011 00110101 00011111 10111110 01001011 +// 4 9 4 4 5 4 4 B 3 5 1 F B E 4 B +// 0100 1001 0100 0100 0101 0100 0100 1011 0011 0101 0001 1111 1011 1110 0100 1011 + +#define IDTECK_PREAMBLE_BIT_SIZE (32) +#define IDTECK_PREAMBLE_DATA_SIZE (8) + +#define IDTECK_ENCODED_BIT_SIZE (64) +#define IDTECK_ENCODED_DATA_SIZE (((IDTECK_ENCODED_BIT_SIZE) / 8) + IDTECK_PREAMBLE_DATA_SIZE) +#define IDTECK_ENCODED_DATA_LAST ((IDTECK_ENCODED_BIT_SIZE) / 8) + +#define IDTECK_DECODED_BIT_SIZE (64) +#define IDTECK_DECODED_DATA_SIZE (8) + +#define IDTECK_US_PER_BIT (255) +#define IDTECK_ENCODER_PULSES_PER_BIT (16) + +typedef struct { + uint8_t data_index; + uint8_t bit_clock_index; + bool last_bit; + bool current_polarity; + bool pulse_phase; +} ProtocolIdteckEncoder; + +typedef struct { + uint8_t encoded_data[IDTECK_ENCODED_DATA_SIZE]; + uint8_t negative_encoded_data[IDTECK_ENCODED_DATA_SIZE]; + uint8_t corrupted_encoded_data[IDTECK_ENCODED_DATA_SIZE]; + uint8_t corrupted_negative_encoded_data[IDTECK_ENCODED_DATA_SIZE]; + + uint8_t data[IDTECK_DECODED_DATA_SIZE]; + ProtocolIdteckEncoder encoder; +} ProtocolIdteck; + +ProtocolIdteck* protocol_idteck_alloc(void) { + ProtocolIdteck* protocol = malloc(sizeof(ProtocolIdteck)); + return protocol; +}; + +void protocol_idteck_free(ProtocolIdteck* protocol) { + free(protocol); +}; + +uint8_t* protocol_idteck_get_data(ProtocolIdteck* protocol) { + return protocol->data; +}; + +void protocol_idteck_decoder_start(ProtocolIdteck* protocol) { + memset(protocol->encoded_data, 0, IDTECK_ENCODED_DATA_SIZE); + memset(protocol->negative_encoded_data, 0, IDTECK_ENCODED_DATA_SIZE); + memset(protocol->corrupted_encoded_data, 0, IDTECK_ENCODED_DATA_SIZE); + memset(protocol->corrupted_negative_encoded_data, 0, IDTECK_ENCODED_DATA_SIZE); +}; + +static bool protocol_idteck_check_preamble(uint8_t* data, size_t bit_index) { + // Preamble 01001001 01000100 01010100 01001011 + if(*(uint32_t*)&data[bit_index / 8] != 0b01001011010101000100010001001001) return false; + return true; +} + +static bool protocol_idteck_can_be_decoded(uint8_t* data) { + if(!protocol_idteck_check_preamble(data, 0)) return false; + return true; +} + +static bool protocol_idteck_decoder_feed_internal(bool polarity, uint32_t time, uint8_t* data) { + time += (IDTECK_US_PER_BIT / 2); + + size_t bit_count = (time / IDTECK_US_PER_BIT); + bool result = false; + + if(bit_count < IDTECK_ENCODED_BIT_SIZE) { + for(size_t i = 0; i < bit_count; i++) { + bit_lib_push_bit(data, IDTECK_ENCODED_DATA_SIZE, polarity); + if(protocol_idteck_can_be_decoded(data)) { + result = true; + break; + } + } + } + + return result; +} + +static void protocol_idteck_decoder_save(uint8_t* data_to, const uint8_t* data_from) { + bit_lib_copy_bits(data_to, 0, 64, data_from, 0); +} + +bool protocol_idteck_decoder_feed(ProtocolIdteck* protocol, bool level, uint32_t duration) { + bool result = false; + + if(duration > (IDTECK_US_PER_BIT / 2)) { + if(protocol_idteck_decoder_feed_internal(level, duration, protocol->encoded_data)) { + protocol_idteck_decoder_save(protocol->data, protocol->encoded_data); + FURI_LOG_D("Idteck", "Positive"); + result = true; + return result; + } + + if(protocol_idteck_decoder_feed_internal( + !level, duration, protocol->negative_encoded_data)) { + protocol_idteck_decoder_save(protocol->data, protocol->negative_encoded_data); + FURI_LOG_D("Idteck", "Negative"); + result = true; + return result; + } + } + + if(duration > (IDTECK_US_PER_BIT / 4)) { + // Try to decode wrong phase synced data + if(level) { + duration += 120; + } else { + if(duration > 120) { + duration -= 120; + } + } + + if(protocol_idteck_decoder_feed_internal( + level, duration, protocol->corrupted_encoded_data)) { + protocol_idteck_decoder_save(protocol->data, protocol->corrupted_encoded_data); + FURI_LOG_D("Idteck", "Positive Corrupted"); + + result = true; + return result; + } + + if(protocol_idteck_decoder_feed_internal( + !level, duration, protocol->corrupted_negative_encoded_data)) { + protocol_idteck_decoder_save( + protocol->data, protocol->corrupted_negative_encoded_data); + FURI_LOG_D("Idteck", "Negative Corrupted"); + + result = true; + return result; + } + } + + return result; +}; + +bool protocol_idteck_encoder_start(ProtocolIdteck* protocol) { + memset(protocol->encoded_data, 0, IDTECK_ENCODED_DATA_SIZE); + *(uint32_t*)&protocol->encoded_data[0] = 0b01001011010101000100010001001001; + bit_lib_copy_bits(protocol->encoded_data, 32, 32, protocol->data, 32); + + protocol->encoder.last_bit = + bit_lib_get_bit(protocol->encoded_data, IDTECK_ENCODED_BIT_SIZE - 1); + protocol->encoder.data_index = 0; + protocol->encoder.current_polarity = true; + protocol->encoder.pulse_phase = true; + protocol->encoder.bit_clock_index = 0; + + return true; +}; + +LevelDuration protocol_idteck_encoder_yield(ProtocolIdteck* protocol) { + LevelDuration level_duration; + ProtocolIdteckEncoder* encoder = &protocol->encoder; + + if(encoder->pulse_phase) { + level_duration = level_duration_make(encoder->current_polarity, 1); + encoder->pulse_phase = false; + } else { + level_duration = level_duration_make(!encoder->current_polarity, 1); + encoder->pulse_phase = true; + + encoder->bit_clock_index++; + if(encoder->bit_clock_index >= IDTECK_ENCODER_PULSES_PER_BIT) { + encoder->bit_clock_index = 0; + + bool current_bit = bit_lib_get_bit(protocol->encoded_data, encoder->data_index); + + if(current_bit != encoder->last_bit) { + encoder->current_polarity = !encoder->current_polarity; + } + + encoder->last_bit = current_bit; + + bit_lib_increment_index(encoder->data_index, IDTECK_ENCODED_BIT_SIZE); + } + } + + return level_duration; +}; + +// factory code +static uint32_t get_fc(const uint8_t* data) { + uint32_t fc = 0; + fc = bit_lib_get_bits_32(data, 0, 32); + return fc; +} + +// card number +static uint32_t get_card(const uint8_t* data) { + uint32_t cn = 0; + cn = bit_lib_get_bits_32(data, 32, 32); + return cn; +} + +void protocol_idteck_render_data_internal(ProtocolIdteck* protocol, FuriString* result, bool brief) { + const uint32_t fc = get_fc(protocol->data); + const uint32_t card = get_card(protocol->data); + + if(brief) { + furi_string_printf(result, "FC: %08lX\r\nCard: %08lX", fc, card); + } else { + furi_string_printf( + result, + "FC: %08lX\r\n" + "Card: %08lX\r\n", + fc, + card); + } +} +void protocol_idteck_render_data(ProtocolIdteck* protocol, FuriString* result) { + protocol_idteck_render_data_internal(protocol, result, false); +} +void protocol_idteck_render_brief_data(ProtocolIdteck* protocol, FuriString* result) { + protocol_idteck_render_data_internal(protocol, result, true); +} + +bool protocol_idteck_write_data(ProtocolIdteck* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + protocol_idteck_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_BITRATE_RF_32 | LFRFID_T5577_MODULATION_PSK1 | + (2 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.blocks_to_write = 3; + result = true; + } + return result; +}; + +const ProtocolBase protocol_idteck = { + .name = "Idteck", + .manufacturer = "IDTECK", + .data_size = IDTECK_DECODED_DATA_SIZE, + .features = LFRFIDFeaturePSK, + .validate_count = 6, + .alloc = (ProtocolAlloc)protocol_idteck_alloc, + .free = (ProtocolFree)protocol_idteck_free, + .get_data = (ProtocolGetData)protocol_idteck_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_idteck_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_idteck_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_idteck_encoder_start, + .yield = (ProtocolEncoderYield)protocol_idteck_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_idteck_render_data, + .render_brief_data = (ProtocolRenderData)protocol_idteck_render_brief_data, + .write_data = (ProtocolWriteData)protocol_idteck_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_idteck.h b/lib/lfrfid/protocols/protocol_idteck.h new file mode 100644 index 00000000000..b7a5ade4722 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_idteck.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_idteck; diff --git a/lib/lfrfid/protocols/protocol_indala26.c b/lib/lfrfid/protocols/protocol_indala26.c new file mode 100644 index 00000000000..8319f0a93df --- /dev/null +++ b/lib/lfrfid/protocols/protocol_indala26.c @@ -0,0 +1,356 @@ +#include +#include +#include +#include "lfrfid_protocols.h" + +#define INDALA26_PREAMBLE_BIT_SIZE (33) +#define INDALA26_PREAMBLE_DATA_SIZE (5) + +#define INDALA26_ENCODED_BIT_SIZE (64) +#define INDALA26_ENCODED_DATA_SIZE \ + (((INDALA26_ENCODED_BIT_SIZE) / 8) + INDALA26_PREAMBLE_DATA_SIZE) +#define INDALA26_ENCODED_DATA_LAST ((INDALA26_ENCODED_BIT_SIZE) / 8) + +#define INDALA26_DECODED_BIT_SIZE (28) +#define INDALA26_DECODED_DATA_SIZE (4) + +#define INDALA26_US_PER_BIT (255) +#define INDALA26_ENCODER_PULSES_PER_BIT (16) + +typedef struct { + uint8_t data_index; + uint8_t bit_clock_index; + bool last_bit; + bool current_polarity; + bool pulse_phase; +} ProtocolIndalaEncoder; + +typedef struct { + uint8_t encoded_data[INDALA26_ENCODED_DATA_SIZE]; + uint8_t negative_encoded_data[INDALA26_ENCODED_DATA_SIZE]; + uint8_t corrupted_encoded_data[INDALA26_ENCODED_DATA_SIZE]; + uint8_t corrupted_negative_encoded_data[INDALA26_ENCODED_DATA_SIZE]; + + uint8_t data[INDALA26_DECODED_DATA_SIZE]; + ProtocolIndalaEncoder encoder; +} ProtocolIndala; + +ProtocolIndala* protocol_indala26_alloc(void) { + ProtocolIndala* protocol = malloc(sizeof(ProtocolIndala)); + return protocol; +}; + +void protocol_indala26_free(ProtocolIndala* protocol) { + free(protocol); +}; + +uint8_t* protocol_indala26_get_data(ProtocolIndala* protocol) { + return protocol->data; +}; + +void protocol_indala26_decoder_start(ProtocolIndala* protocol) { + memset(protocol->encoded_data, 0, INDALA26_ENCODED_DATA_SIZE); + memset(protocol->negative_encoded_data, 0, INDALA26_ENCODED_DATA_SIZE); + memset(protocol->corrupted_encoded_data, 0, INDALA26_ENCODED_DATA_SIZE); + memset(protocol->corrupted_negative_encoded_data, 0, INDALA26_ENCODED_DATA_SIZE); +}; + +static bool protocol_indala26_check_preamble(uint8_t* data, size_t bit_index) { + // Preamble 10100000 00000000 00000000 00000000 1 + if(*(uint32_t*)&data[bit_index / 8] != 0b00000000000000000000000010100000) return false; + if(bit_lib_get_bit(data, bit_index + 32) != 1) return false; + return true; +} + +static bool protocol_indala26_can_be_decoded(uint8_t* data) { + if(!protocol_indala26_check_preamble(data, 0)) return false; + if(!protocol_indala26_check_preamble(data, 64)) return false; + if(bit_lib_get_bit(data, 61) != 0) return false; + if(bit_lib_get_bit(data, 60) != 0) return false; + return true; +} + +static bool protocol_indala26_decoder_feed_internal(bool polarity, uint32_t time, uint8_t* data) { + time += (INDALA26_US_PER_BIT / 2); + + size_t bit_count = (time / INDALA26_US_PER_BIT); + bool result = false; + + if(bit_count < INDALA26_ENCODED_BIT_SIZE) { + for(size_t i = 0; i < bit_count; i++) { + bit_lib_push_bit(data, INDALA26_ENCODED_DATA_SIZE, polarity); + if(protocol_indala26_can_be_decoded(data)) { + result = true; + break; + } + } + } + + return result; +} + +static void protocol_indala26_decoder_save(uint8_t* data_to, const uint8_t* data_from) { + bit_lib_copy_bits(data_to, 0, 22, data_from, 33); + bit_lib_copy_bits(data_to, 22, 5, data_from, 55); + bit_lib_copy_bits(data_to, 27, 2, data_from, 62); +} + +bool protocol_indala26_decoder_feed(ProtocolIndala* protocol, bool level, uint32_t duration) { + bool result = false; + + if(duration > (INDALA26_US_PER_BIT / 2)) { + if(protocol_indala26_decoder_feed_internal(level, duration, protocol->encoded_data)) { + protocol_indala26_decoder_save(protocol->data, protocol->encoded_data); + FURI_LOG_D("Indala26", "Positive"); + result = true; + return result; + } + + if(protocol_indala26_decoder_feed_internal( + !level, duration, protocol->negative_encoded_data)) { + protocol_indala26_decoder_save(protocol->data, protocol->negative_encoded_data); + FURI_LOG_D("Indala26", "Negative"); + result = true; + return result; + } + } + + if(duration > (INDALA26_US_PER_BIT / 4)) { + // Try to decode wrong phase synced data + if(level) { + duration += 120; + } else { + if(duration > 120) { + duration -= 120; + } + } + + if(protocol_indala26_decoder_feed_internal( + level, duration, protocol->corrupted_encoded_data)) { + protocol_indala26_decoder_save(protocol->data, protocol->corrupted_encoded_data); + FURI_LOG_D("Indala26", "Positive Corrupted"); + + result = true; + return result; + } + + if(protocol_indala26_decoder_feed_internal( + !level, duration, protocol->corrupted_negative_encoded_data)) { + protocol_indala26_decoder_save( + protocol->data, protocol->corrupted_negative_encoded_data); + FURI_LOG_D("Indala26", "Negative Corrupted"); + + result = true; + return result; + } + } + + return result; +}; + +bool protocol_indala26_encoder_start(ProtocolIndala* protocol) { + memset(protocol->encoded_data, 0, INDALA26_ENCODED_DATA_SIZE); + *(uint32_t*)&protocol->encoded_data[0] = 0b00000000000000000000000010100000; + bit_lib_set_bit(protocol->encoded_data, 32, 1); + bit_lib_copy_bits(protocol->encoded_data, 33, 22, protocol->data, 0); + bit_lib_copy_bits(protocol->encoded_data, 55, 5, protocol->data, 22); + bit_lib_copy_bits(protocol->encoded_data, 62, 2, protocol->data, 27); + + protocol->encoder.last_bit = + bit_lib_get_bit(protocol->encoded_data, INDALA26_ENCODED_BIT_SIZE - 1); + protocol->encoder.data_index = 0; + protocol->encoder.current_polarity = true; + protocol->encoder.pulse_phase = true; + protocol->encoder.bit_clock_index = 0; + + return true; +}; + +LevelDuration protocol_indala26_encoder_yield(ProtocolIndala* protocol) { + LevelDuration level_duration; + ProtocolIndalaEncoder* encoder = &protocol->encoder; + + if(encoder->pulse_phase) { + level_duration = level_duration_make(encoder->current_polarity, 1); + encoder->pulse_phase = false; + } else { + level_duration = level_duration_make(!encoder->current_polarity, 1); + encoder->pulse_phase = true; + + encoder->bit_clock_index++; + if(encoder->bit_clock_index >= INDALA26_ENCODER_PULSES_PER_BIT) { + encoder->bit_clock_index = 0; + + bool current_bit = bit_lib_get_bit(protocol->encoded_data, encoder->data_index); + + if(current_bit != encoder->last_bit) { + encoder->current_polarity = !encoder->current_polarity; + } + + encoder->last_bit = current_bit; + + bit_lib_increment_index(encoder->data_index, INDALA26_ENCODED_BIT_SIZE); + } + } + + return level_duration; +}; + +// factory code +static uint8_t get_fc(const uint8_t* data) { + uint8_t fc = 0; + + fc = fc << 1 | bit_lib_get_bit(data, 24); + fc = fc << 1 | bit_lib_get_bit(data, 16); + fc = fc << 1 | bit_lib_get_bit(data, 11); + fc = fc << 1 | bit_lib_get_bit(data, 14); + fc = fc << 1 | bit_lib_get_bit(data, 15); + fc = fc << 1 | bit_lib_get_bit(data, 20); + fc = fc << 1 | bit_lib_get_bit(data, 6); + fc = fc << 1 | bit_lib_get_bit(data, 25); + + return fc; +} + +// card number +static uint16_t get_cn(const uint8_t* data) { + uint16_t cn = 0; + + cn = cn << 1 | bit_lib_get_bit(data, 9); + cn = cn << 1 | bit_lib_get_bit(data, 12); + cn = cn << 1 | bit_lib_get_bit(data, 10); + cn = cn << 1 | bit_lib_get_bit(data, 7); + cn = cn << 1 | bit_lib_get_bit(data, 19); + cn = cn << 1 | bit_lib_get_bit(data, 3); + cn = cn << 1 | bit_lib_get_bit(data, 2); + cn = cn << 1 | bit_lib_get_bit(data, 18); + cn = cn << 1 | bit_lib_get_bit(data, 13); + cn = cn << 1 | bit_lib_get_bit(data, 0); + cn = cn << 1 | bit_lib_get_bit(data, 4); + cn = cn << 1 | bit_lib_get_bit(data, 21); + cn = cn << 1 | bit_lib_get_bit(data, 23); + cn = cn << 1 | bit_lib_get_bit(data, 26); + cn = cn << 1 | bit_lib_get_bit(data, 17); + cn = cn << 1 | bit_lib_get_bit(data, 8); + + return cn; +} + +void protocol_indala26_render_data_internal( + ProtocolIndala* protocol, + FuriString* result, + bool brief) { + bool wiegand_correct = true; + bool checksum_correct = true; + + const uint8_t fc = get_fc(protocol->data); + const uint16_t card = get_cn(protocol->data); + const uint32_t fc_and_card = fc << 16 | card; + const uint8_t checksum = bit_lib_get_bit(protocol->data, 27) << 1 | + bit_lib_get_bit(protocol->data, 28); + const bool even_parity = bit_lib_get_bit(protocol->data, 1); + const bool odd_parity = bit_lib_get_bit(protocol->data, 5); + + // indala checksum + uint8_t checksum_sum = 0; + checksum_sum += ((fc_and_card >> 14) & 1); + checksum_sum += ((fc_and_card >> 12) & 1); + checksum_sum += ((fc_and_card >> 9) & 1); + checksum_sum += ((fc_and_card >> 8) & 1); + checksum_sum += ((fc_and_card >> 6) & 1); + checksum_sum += ((fc_and_card >> 5) & 1); + checksum_sum += ((fc_and_card >> 2) & 1); + checksum_sum += ((fc_and_card >> 0) & 1); + checksum_sum = checksum_sum & 0b1; + + if(checksum_sum == 1 && checksum == 0b01) { + } else if(checksum_sum == 0 && checksum == 0b10) { + } else { + checksum_correct = false; + } + + // wiegand parity + uint8_t even_parity_sum = 0; + for(int8_t i = 12; i < 24; i++) { + if(((fc_and_card >> i) & 1) == 1) { + even_parity_sum++; + } + } + if(even_parity_sum % 2 != even_parity) wiegand_correct = false; + + uint8_t odd_parity_sum = 1; + for(int8_t i = 0; i < 12; i++) { + if(((fc_and_card >> i) & 1) == 1) { + odd_parity_sum++; + } + } + if(odd_parity_sum % 2 != odd_parity) wiegand_correct = false; + + if(brief) { + furi_string_printf( + result, + "FC: %u\r\nCard: %u, Parity:%s%s", + fc, + card, + (checksum_correct ? "+" : "-"), + (wiegand_correct ? "+" : "-")); + } else { + furi_string_printf( + result, + "FC: %u\r\n" + "Card: %u\r\n" + "Checksum: %s\r\n" + "W26 Parity: %s", + fc, + card, + (checksum_correct ? "+" : "-"), + (wiegand_correct ? "+" : "-")); + } +} +void protocol_indala26_render_data(ProtocolIndala* protocol, FuriString* result) { + protocol_indala26_render_data_internal(protocol, result, false); +} +void protocol_indala26_render_brief_data(ProtocolIndala* protocol, FuriString* result) { + protocol_indala26_render_data_internal(protocol, result, true); +} + +bool protocol_indala26_write_data(ProtocolIndala* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + protocol_indala26_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_BITRATE_RF_32 | LFRFID_T5577_MODULATION_PSK1 | + (2 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.blocks_to_write = 3; + result = true; + } + return result; +}; + +const ProtocolBase protocol_indala26 = { + .name = "Indala26", + .manufacturer = "Motorola", + .data_size = INDALA26_DECODED_DATA_SIZE, + .features = LFRFIDFeaturePSK, + .validate_count = 6, + .alloc = (ProtocolAlloc)protocol_indala26_alloc, + .free = (ProtocolFree)protocol_indala26_free, + .get_data = (ProtocolGetData)protocol_indala26_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_indala26_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_indala26_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_indala26_encoder_start, + .yield = (ProtocolEncoderYield)protocol_indala26_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_indala26_render_data, + .render_brief_data = (ProtocolRenderData)protocol_indala26_render_brief_data, + .write_data = (ProtocolWriteData)protocol_indala26_write_data, +}; diff --git a/lib/lfrfid/protocols/protocol_indala26.h b/lib/lfrfid/protocols/protocol_indala26.h new file mode 100644 index 00000000000..c0c61784b7f --- /dev/null +++ b/lib/lfrfid/protocols/protocol_indala26.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_indala26; diff --git a/lib/lfrfid/protocols/protocol_io_prox_xsf.c b/lib/lfrfid/protocols/protocol_io_prox_xsf.c new file mode 100644 index 00000000000..71314856840 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_io_prox_xsf.c @@ -0,0 +1,301 @@ +#include +#include +#include +#include +#include +#include "lfrfid_protocols.h" + +#define JITTER_TIME (20) +#define MIN_TIME (64 - JITTER_TIME) +#define MAX_TIME (80 + JITTER_TIME) + +#define IOPROXXSF_DECODED_DATA_SIZE (4) +#define IOPROXXSF_ENCODED_DATA_SIZE (8) + +#define IOPROXXSF_BIT_SIZE (8) +#define IOPROXXSF_BIT_MAX_SIZE (IOPROXXSF_BIT_SIZE * IOPROXXSF_ENCODED_DATA_SIZE) + +typedef struct { + FSKDemod* fsk_demod; +} ProtocolIOProxXSFDecoder; + +typedef struct { + FSKOsc* fsk_osc; + uint8_t encoded_index; +} ProtocolIOProxXSFEncoder; + +typedef struct { + ProtocolIOProxXSFEncoder encoder; + ProtocolIOProxXSFDecoder decoder; + uint8_t encoded_data[IOPROXXSF_ENCODED_DATA_SIZE]; + uint8_t data[IOPROXXSF_DECODED_DATA_SIZE]; +} ProtocolIOProxXSF; + +ProtocolIOProxXSF* protocol_io_prox_xsf_alloc(void) { + ProtocolIOProxXSF* protocol = malloc(sizeof(ProtocolIOProxXSF)); + protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 8, MAX_TIME, 6); + protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 64); + return protocol; +}; + +void protocol_io_prox_xsf_free(ProtocolIOProxXSF* protocol) { + fsk_demod_free(protocol->decoder.fsk_demod); + fsk_osc_free(protocol->encoder.fsk_osc); + free(protocol); +}; + +uint8_t* protocol_io_prox_xsf_get_data(ProtocolIOProxXSF* protocol) { + return protocol->data; +}; + +void protocol_io_prox_xsf_decoder_start(ProtocolIOProxXSF* protocol) { + memset(protocol->encoded_data, 0, IOPROXXSF_ENCODED_DATA_SIZE); +}; + +static uint8_t protocol_io_prox_xsf_compute_checksum(const uint8_t* data) { + // Packet structure: + // + //0 1 2 3 4 5 6 7 + //v v v v v v v v + //01234567 8 9ABCDEF0 1 23456789 A BCDEF012 3 456789AB C DEF01234 5 6789ABCD EF + //00000000 0 VVVVVVVV 1 WWWWWWWW 1 XXXXXXXX 1 YYYYYYYY 1 ZZZZZZZZ 1 CHECKSUM 11 + // + // algorithm as observed by the proxmark3 folks + // CHECKSUM == 0xFF - (V + W + X + Y + Z) + + uint8_t checksum = 0; + + for(size_t i = 1; i <= 5; i++) { + checksum += bit_lib_get_bits(data, 9 * i, 8); + } + + return 0xFF - checksum; +} + +static bool protocol_io_prox_xsf_can_be_decoded(const uint8_t* encoded_data) { + // Packet framing + // + //0 1 2 3 4 5 6 7 + //v v v v v v v v + //01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF + //----------------------------------------------------------------------- + //00000000 01______ _1______ __1_____ ___1____ ____1___ _____1XX XXXXXX11 + // + // _ = variable data + // 0 = preamble 0 + // 1 = framing 1 + // X = checksum + + // Validate the packet preamble is there... + if(encoded_data[0] != 0b00000000) { + return false; + } + if((encoded_data[1] >> 6) != 0b01) { + return false; + } + + // ... check for known ones... + if(bit_lib_bit_is_not_set(encoded_data[2], 6)) { + return false; + } + if(bit_lib_bit_is_not_set(encoded_data[3], 5)) { + return false; + } + if(bit_lib_bit_is_not_set(encoded_data[4], 4)) { + return false; + } + if(bit_lib_bit_is_not_set(encoded_data[5], 3)) { + return false; + } + if(bit_lib_bit_is_not_set(encoded_data[6], 2)) { + return false; + } + if(bit_lib_bit_is_not_set(encoded_data[7], 1)) { + return false; + } + if(bit_lib_bit_is_not_set(encoded_data[7], 0)) { + return false; + } + + // ... and validate our checksums. + uint8_t checksum = protocol_io_prox_xsf_compute_checksum(encoded_data); + uint8_t checkval = bit_lib_get_bits(encoded_data, 54, 8); + + if(checksum != checkval) { + return false; + } + + return true; +} + +void protocol_io_prox_xsf_decode(const uint8_t* encoded_data, uint8_t* decoded_data) { + // Packet structure: + // (Note: the second word seems fixed; but this may not be a guarantee; + // it currently has no meaning.) + // + //0 1 2 3 4 5 6 7 + //v v v v v v v v + //01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF 01234567 89ABCDEF + //----------------------------------------------------------------------- + //00000000 01111000 01FFFFFF FF1VVVVV VVV1CCCC CCCC1CCC CCCCC1XX XXXXXX11 + // + // F = facility code + // V = version + // C = code + // X = checksum + + // Facility code + decoded_data[0] = bit_lib_get_bits(encoded_data, 18, 8); + + // Version code. + decoded_data[1] = bit_lib_get_bits(encoded_data, 27, 8); + + // Code bytes. + decoded_data[2] = bit_lib_get_bits(encoded_data, 36, 8); + decoded_data[3] = bit_lib_get_bits(encoded_data, 45, 8); +} + +bool protocol_io_prox_xsf_decoder_feed(ProtocolIOProxXSF* protocol, bool level, uint32_t duration) { + bool result = false; + + uint32_t count; + bool value; + + fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count); + for(size_t i = 0; i < count; i++) { + bit_lib_push_bit(protocol->encoded_data, IOPROXXSF_ENCODED_DATA_SIZE, value); + if(protocol_io_prox_xsf_can_be_decoded(protocol->encoded_data)) { + protocol_io_prox_xsf_decode(protocol->encoded_data, protocol->data); + result = true; + break; + } + } + + return result; +}; + +static void protocol_io_prox_xsf_encode(const uint8_t* decoded_data, uint8_t* encoded_data) { + // Packet to transmit: + // + // 0 10 20 30 40 50 60 + // v v v v v v v + // 01234567 8 90123456 7 89012345 6 78901234 5 67890123 4 56789012 3 45678901 23 + // ----------------------------------------------------------------------------- + // 00000000 0 11110000 1 facility 1 version_ 1 code-one 1 code-two 1 checksum 11 + + // Preamble. + bit_lib_set_bits(encoded_data, 0, 0b00000000, 8); + bit_lib_set_bit(encoded_data, 8, 0); + + bit_lib_set_bits(encoded_data, 9, 0b11110000, 8); + bit_lib_set_bit(encoded_data, 17, 1); + + // Facility code. + bit_lib_set_bits(encoded_data, 18, decoded_data[0], 8); + bit_lib_set_bit(encoded_data, 26, 1); + + // Version + bit_lib_set_bits(encoded_data, 27, decoded_data[1], 8); + bit_lib_set_bit(encoded_data, 35, 1); + + // Code one + bit_lib_set_bits(encoded_data, 36, decoded_data[2], 8); + bit_lib_set_bit(encoded_data, 44, 1); + + // Code two + bit_lib_set_bits(encoded_data, 45, decoded_data[3], 8); + bit_lib_set_bit(encoded_data, 53, 1); + + // Checksum + bit_lib_set_bits(encoded_data, 54, protocol_io_prox_xsf_compute_checksum(encoded_data), 8); + bit_lib_set_bit(encoded_data, 62, 1); + bit_lib_set_bit(encoded_data, 63, 1); +} + +bool protocol_io_prox_xsf_encoder_start(ProtocolIOProxXSF* protocol) { + protocol_io_prox_xsf_encode(protocol->data, protocol->encoded_data); + protocol->encoder.encoded_index = 0; + fsk_osc_reset(protocol->encoder.fsk_osc); + return true; +}; + +LevelDuration protocol_io_prox_xsf_encoder_yield(ProtocolIOProxXSF* protocol) { + bool level; + uint32_t duration; + + bool bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index); + bool advance = fsk_osc_next_half(protocol->encoder.fsk_osc, bit, &level, &duration); + + if(advance) { + bit_lib_increment_index(protocol->encoder.encoded_index, IOPROXXSF_BIT_MAX_SIZE); + } + return level_duration_make(level, duration); +}; + +void protocol_io_prox_xsf_render_data(ProtocolIOProxXSF* protocol, FuriString* result) { + uint8_t* data = protocol->data; + furi_string_printf( + result, + "FC: %u\r\n" + "VС: %u\r\n" + "Card: %u", + data[0], + data[1], + (uint16_t)((data[2] << 8) | (data[3]))); +} + +void protocol_io_prox_xsf_render_brief_data(ProtocolIOProxXSF* protocol, FuriString* result) { + uint8_t* data = protocol->data; + furi_string_printf( + result, + "FC: %u, VС: %u\r\n" + "Card: %u", + data[0], + data[1], + (uint16_t)((data[2] << 8) | (data[3]))); +} + +bool protocol_io_prox_xsf_write_data(ProtocolIOProxXSF* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + // Correct protocol data by redecoding + protocol_io_prox_xsf_encode(protocol->data, protocol->encoded_data); + protocol_io_prox_xsf_decode(protocol->encoded_data, protocol->data); + + protocol_io_prox_xsf_encode(protocol->data, protocol->encoded_data); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_64 | + (2 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.blocks_to_write = 3; + result = true; + } + return result; +}; + +const ProtocolBase protocol_io_prox_xsf = { + .name = "IoProxXSF", + .manufacturer = "Kantech", + .data_size = IOPROXXSF_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_io_prox_xsf_alloc, + .free = (ProtocolFree)protocol_io_prox_xsf_free, + .get_data = (ProtocolGetData)protocol_io_prox_xsf_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_io_prox_xsf_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_io_prox_xsf_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_io_prox_xsf_encoder_start, + .yield = (ProtocolEncoderYield)protocol_io_prox_xsf_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_io_prox_xsf_render_data, + .render_brief_data = (ProtocolRenderData)protocol_io_prox_xsf_render_brief_data, + .write_data = (ProtocolWriteData)protocol_io_prox_xsf_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_io_prox_xsf.h b/lib/lfrfid/protocols/protocol_io_prox_xsf.h new file mode 100644 index 00000000000..af94fb4636c --- /dev/null +++ b/lib/lfrfid/protocols/protocol_io_prox_xsf.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_io_prox_xsf; diff --git a/lib/lfrfid/protocols/protocol_jablotron.c b/lib/lfrfid/protocols/protocol_jablotron.c new file mode 100644 index 00000000000..d2c7ea79ff8 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_jablotron.c @@ -0,0 +1,211 @@ +#include +#include "toolbox/level_duration.h" +#include "protocol_jablotron.h" +#include +#include +#include "lfrfid_protocols.h" + +#define JABLOTRON_ENCODED_BIT_SIZE (64) +#define JABLOTRON_ENCODED_BYTE_SIZE (((JABLOTRON_ENCODED_BIT_SIZE) / 8)) +#define JABLOTRON_PREAMBLE_BIT_SIZE (16) +#define JABLOTRON_PREAMBLE_BYTE_SIZE (2) +#define JABLOTRON_ENCODED_BYTE_FULL_SIZE \ + (JABLOTRON_ENCODED_BYTE_SIZE + JABLOTRON_PREAMBLE_BYTE_SIZE) + +#define JABLOTRON_DECODED_DATA_SIZE (5) + +#define JABLOTRON_SHORT_TIME (256) +#define JABLOTRON_LONG_TIME (512) +#define JABLOTRON_JITTER_TIME (120) + +#define JABLOTRON_SHORT_TIME_LOW (JABLOTRON_SHORT_TIME - JABLOTRON_JITTER_TIME) +#define JABLOTRON_SHORT_TIME_HIGH (JABLOTRON_SHORT_TIME + JABLOTRON_JITTER_TIME) +#define JABLOTRON_LONG_TIME_LOW (JABLOTRON_LONG_TIME - JABLOTRON_JITTER_TIME) +#define JABLOTRON_LONG_TIME_HIGH (JABLOTRON_LONG_TIME + JABLOTRON_JITTER_TIME) + +typedef struct { + bool last_short; + bool last_level; + size_t encoded_index; + uint8_t encoded_data[JABLOTRON_ENCODED_BYTE_FULL_SIZE]; + uint8_t data[JABLOTRON_DECODED_DATA_SIZE]; +} ProtocolJablotron; + +ProtocolJablotron* protocol_jablotron_alloc(void) { + ProtocolJablotron* protocol = malloc(sizeof(ProtocolJablotron)); + return protocol; +}; + +void protocol_jablotron_free(ProtocolJablotron* protocol) { + free(protocol); +}; + +uint8_t* protocol_jablotron_get_data(ProtocolJablotron* proto) { + return proto->data; +}; + +void protocol_jablotron_decoder_start(ProtocolJablotron* protocol) { + memset(protocol->encoded_data, 0, JABLOTRON_ENCODED_BYTE_FULL_SIZE); + protocol->last_short = false; +}; + +uint8_t protocol_jablotron_checksum(uint8_t* bits) { + uint8_t chksum = 0; + for(uint8_t i = 16; i < 56; i += 8) { + chksum += bit_lib_get_bits(bits, i, 8); + } + chksum ^= 0x3A; + return chksum; +} + +uint64_t protocol_jablotron_card_id(uint8_t* bytes) { + uint64_t id = 0; + for(int i = 0; i < 5; i++) { + id *= 100; + id += ((bytes[i] & 0xF0) >> 4) * 10 + (bytes[i] & 0x0F); + } + return id; +} + +static bool protocol_jablotron_can_be_decoded(ProtocolJablotron* protocol) { + // check 11 bits preamble + if(bit_lib_get_bits_16(protocol->encoded_data, 0, 16) != 0b1111111111111111) return false; + // check next 11 bits preamble + if(bit_lib_get_bits_16(protocol->encoded_data, 64, 16) != 0b1111111111111111) return false; + + uint8_t checksum = bit_lib_get_bits(protocol->encoded_data, 56, 8); + if(checksum != protocol_jablotron_checksum(protocol->encoded_data)) return false; + + return true; +} + +void protocol_jablotron_decode(ProtocolJablotron* protocol) { + bit_lib_copy_bits(protocol->data, 0, 40, protocol->encoded_data, 16); +} + +bool protocol_jablotron_decoder_feed(ProtocolJablotron* protocol, bool level, uint32_t duration) { + UNUSED(level); + bool pushed = false; + + // Bi-Phase Manchester decoding + if(duration >= JABLOTRON_SHORT_TIME_LOW && duration <= JABLOTRON_SHORT_TIME_HIGH) { + if(protocol->last_short == false) { + protocol->last_short = true; + } else { + pushed = true; + bit_lib_push_bit(protocol->encoded_data, JABLOTRON_ENCODED_BYTE_FULL_SIZE, false); + protocol->last_short = false; + } + } else if(duration >= JABLOTRON_LONG_TIME_LOW && duration <= JABLOTRON_LONG_TIME_HIGH) { + if(protocol->last_short == false) { + pushed = true; + bit_lib_push_bit(protocol->encoded_data, JABLOTRON_ENCODED_BYTE_FULL_SIZE, true); + } else { + // reset + protocol->last_short = false; + } + } else { + // reset + protocol->last_short = false; + } + + if(pushed && protocol_jablotron_can_be_decoded(protocol)) { + protocol_jablotron_decode(protocol); + return true; + } + + return false; +}; + +bool protocol_jablotron_encoder_start(ProtocolJablotron* protocol) { + // preamble + bit_lib_set_bits(protocol->encoded_data, 0, 0b11111111, 8); + bit_lib_set_bits(protocol->encoded_data, 8, 0b11111111, 8); + + // Full code + bit_lib_copy_bits(protocol->encoded_data, 16, 40, protocol->data, 0); + + // Checksum + bit_lib_set_bits( + protocol->encoded_data, 56, protocol_jablotron_checksum(protocol->encoded_data), 8); + + protocol->encoded_index = 0; + protocol->last_short = false; + protocol->last_level = false; + return true; +}; + +LevelDuration protocol_jablotron_encoder_yield(ProtocolJablotron* protocol) { + uint32_t duration; + protocol->last_level = !protocol->last_level; + + bool bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_index); + + // Bi-Phase Manchester encoder + if(bit) { + // one long pulse for 1 + duration = JABLOTRON_LONG_TIME / 8; + bit_lib_increment_index(protocol->encoded_index, JABLOTRON_ENCODED_BIT_SIZE); + } else { + // two short pulses for 0 + duration = JABLOTRON_SHORT_TIME / 8; + if(protocol->last_short) { + bit_lib_increment_index(protocol->encoded_index, JABLOTRON_ENCODED_BIT_SIZE); + protocol->last_short = false; + } else { + protocol->last_short = true; + } + } + + return level_duration_make(protocol->last_level, duration); +}; + +void protocol_jablotron_render_data(ProtocolJablotron* protocol, FuriString* result) { + uint64_t id = protocol_jablotron_card_id(protocol->data); + furi_string_printf(result, "ID: %llX\r\n", id); +}; + +bool protocol_jablotron_write_data(ProtocolJablotron* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + // Correct protocol data by redecoding + protocol_jablotron_encoder_start(protocol); + protocol_jablotron_decode(protocol); + + protocol_jablotron_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_DIPHASE | LFRFID_T5577_BITRATE_RF_64 | + (2 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.blocks_to_write = 3; + result = true; + } + return result; +}; + +const ProtocolBase protocol_jablotron = { + .name = "Jablotron", + .manufacturer = "Jablotron", + .data_size = JABLOTRON_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_jablotron_alloc, + .free = (ProtocolFree)protocol_jablotron_free, + .get_data = (ProtocolGetData)protocol_jablotron_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_jablotron_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_jablotron_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_jablotron_encoder_start, + .yield = (ProtocolEncoderYield)protocol_jablotron_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_jablotron_render_data, + .render_brief_data = (ProtocolRenderData)protocol_jablotron_render_data, + .write_data = (ProtocolWriteData)protocol_jablotron_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_jablotron.h b/lib/lfrfid/protocols/protocol_jablotron.h new file mode 100644 index 00000000000..4de57de42a5 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_jablotron.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_jablotron; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_keri.c b/lib/lfrfid/protocols/protocol_keri.c new file mode 100644 index 00000000000..f0a12863e3b --- /dev/null +++ b/lib/lfrfid/protocols/protocol_keri.c @@ -0,0 +1,267 @@ +#include +#include +#include +#include "lfrfid_protocols.h" + +#define KERI_PREAMBLE_BIT_SIZE (33) +#define KERI_PREAMBLE_DATA_SIZE (5) + +#define KERI_ENCODED_BIT_SIZE (64) +#define KERI_ENCODED_DATA_SIZE (((KERI_ENCODED_BIT_SIZE) / 8) + KERI_PREAMBLE_DATA_SIZE) +#define KERI_ENCODED_DATA_LAST ((KERI_ENCODED_BIT_SIZE) / 8) + +#define KERI_DECODED_BIT_SIZE (28) +#define KERI_DECODED_DATA_SIZE (4) + +#define KERI_US_PER_BIT (255) +#define KERI_ENCODER_PULSES_PER_BIT (16) + +typedef struct { + uint8_t data_index; + uint8_t bit_clock_index; + bool last_bit; + bool current_polarity; + bool pulse_phase; +} ProtocolKeriEncoder; + +typedef struct { + uint8_t encoded_data[KERI_ENCODED_DATA_SIZE]; + uint8_t negative_encoded_data[KERI_ENCODED_DATA_SIZE]; + uint8_t corrupted_encoded_data[KERI_ENCODED_DATA_SIZE]; + uint8_t corrupted_negative_encoded_data[KERI_ENCODED_DATA_SIZE]; + + uint8_t data[KERI_DECODED_DATA_SIZE]; + ProtocolKeriEncoder encoder; +} ProtocolKeri; + +ProtocolKeri* protocol_keri_alloc(void) { + ProtocolKeri* protocol = malloc(sizeof(ProtocolKeri)); + return protocol; +}; + +void protocol_keri_free(ProtocolKeri* protocol) { + free(protocol); +}; + +uint8_t* protocol_keri_get_data(ProtocolKeri* protocol) { + return protocol->data; +}; + +void protocol_keri_decoder_start(ProtocolKeri* protocol) { + memset(protocol->encoded_data, 0, KERI_ENCODED_DATA_SIZE); + memset(protocol->negative_encoded_data, 0, KERI_ENCODED_DATA_SIZE); + memset(protocol->corrupted_encoded_data, 0, KERI_ENCODED_DATA_SIZE); + memset(protocol->corrupted_negative_encoded_data, 0, KERI_ENCODED_DATA_SIZE); +}; + +static bool protocol_keri_check_preamble(uint8_t* data, size_t bit_index) { + // Preamble 11100000 00000000 00000000 00000000 1 + if(*(uint32_t*)&data[bit_index / 8] != 0b00000000000000000000000011100000) return false; + if(bit_lib_get_bit(data, bit_index + 32) != 1) return false; + return true; +} + +static bool protocol_keri_can_be_decoded(uint8_t* data) { + if(!protocol_keri_check_preamble(data, 0)) return false; + if(!protocol_keri_check_preamble(data, 64)) return false; + ///if(bit_lib_get_bit(data, 61) != 0) return false; + //if(bit_lib_get_bit(data, 60) != 0) return false; + return true; +} + +static bool protocol_keri_decoder_feed_internal(bool polarity, uint32_t time, uint8_t* data) { + time += (KERI_US_PER_BIT / 2); + + size_t bit_count = (time / KERI_US_PER_BIT); + bool result = false; + + if(bit_count < KERI_ENCODED_BIT_SIZE) { + for(size_t i = 0; i < bit_count; i++) { + bit_lib_push_bit(data, KERI_ENCODED_DATA_SIZE, polarity); + if(protocol_keri_can_be_decoded(data)) { + result = true; + break; + } + } + } + + return result; +} + +static void protocol_keri_descramble(uint32_t* fc, uint32_t* cn, uint32_t* internal_id) { + const uint8_t card_to_id[] = {255, 255, 255, 255, 13, 12, 20, 5, 16, 6, 21, + 17, 8, 255, 0, 7, 10, 15, 255, 11, 4, 1, + 255, 18, 255, 19, 2, 14, 3, 9, 255, 255}; + + const uint8_t card_to_fc[] = {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 2, 255, 255, 255, + 3, 255, 4, 255, 255, 255, 255, 255, 1, 255}; + + *fc = 0; + *cn = 0; + for(uint8_t card_idx = 0; card_idx < 32; card_idx++) { + bool bit = (*internal_id >> card_idx) & 1; + // Card ID + if(card_to_id[card_idx] < 32) { + *cn = *cn | (bit << card_to_id[card_idx]); + } + // Card FC + if(card_to_fc[card_idx] < 32) { + *fc = *fc | (bit << card_to_fc[card_idx]); + } + } +} + +static void protocol_keri_decoder_save(uint8_t* data_to, const uint8_t* data_from) { + uint32_t id = bit_lib_get_bits_32(data_from, 32, 32); + data_to[3] = (uint8_t)id; + data_to[2] = (uint8_t)(id >>= 8); + data_to[1] = (uint8_t)(id >>= 8); + data_to[0] = (uint8_t)(id >>= 8); +} + +bool protocol_keri_decoder_feed(ProtocolKeri* protocol, bool level, uint32_t duration) { + bool result = false; + + if(duration > (KERI_US_PER_BIT / 2)) { + if(protocol_keri_decoder_feed_internal(level, duration, protocol->encoded_data)) { + protocol_keri_decoder_save(protocol->data, protocol->encoded_data); + result = true; + return result; + } + + if(protocol_keri_decoder_feed_internal(!level, duration, protocol->negative_encoded_data)) { + protocol_keri_decoder_save(protocol->data, protocol->negative_encoded_data); + result = true; + return result; + } + } + + if(duration > (KERI_US_PER_BIT / 4)) { + // Try to decode wrong phase synced data + if(level) { + duration += 120; + } else { + if(duration > 120) { + duration -= 120; + } + } + + if(protocol_keri_decoder_feed_internal(level, duration, protocol->corrupted_encoded_data)) { + protocol_keri_decoder_save(protocol->data, protocol->corrupted_encoded_data); + + result = true; + return result; + } + + if(protocol_keri_decoder_feed_internal( + !level, duration, protocol->corrupted_negative_encoded_data)) { + protocol_keri_decoder_save(protocol->data, protocol->corrupted_negative_encoded_data); + + result = true; + return result; + } + } + + return result; +}; + +bool protocol_keri_encoder_start(ProtocolKeri* protocol) { + memset(protocol->encoded_data, 0, KERI_ENCODED_DATA_SIZE); + *(uint32_t*)&protocol->encoded_data[0] = 0b00000000000000000000000011100000; + bit_lib_copy_bits(protocol->encoded_data, 32, 32, protocol->data, 0); + bit_lib_set_bits(protocol->encoded_data, 32, 1, 1); + + protocol->encoder.last_bit = + bit_lib_get_bit(protocol->encoded_data, KERI_ENCODED_BIT_SIZE - 1); + protocol->encoder.data_index = 0; + protocol->encoder.current_polarity = true; + protocol->encoder.pulse_phase = true; + protocol->encoder.bit_clock_index = 0; + + return true; +}; + +LevelDuration protocol_keri_encoder_yield(ProtocolKeri* protocol) { + LevelDuration level_duration; + ProtocolKeriEncoder* encoder = &protocol->encoder; + + if(encoder->pulse_phase) { + level_duration = level_duration_make(encoder->current_polarity, 1); + encoder->pulse_phase = false; + } else { + level_duration = level_duration_make(!encoder->current_polarity, 1); + encoder->pulse_phase = true; + + encoder->bit_clock_index++; + if(encoder->bit_clock_index >= KERI_ENCODER_PULSES_PER_BIT) { + encoder->bit_clock_index = 0; + + bool current_bit = bit_lib_get_bit(protocol->encoded_data, encoder->data_index); + + if(current_bit != encoder->last_bit) { + encoder->current_polarity = !encoder->current_polarity; + } + + encoder->last_bit = current_bit; + + bit_lib_increment_index(encoder->data_index, KERI_ENCODED_BIT_SIZE); + } + } + + return level_duration; +}; + +void protocol_keri_render_data(ProtocolKeri* protocol, FuriString* result) { + uint32_t data = bit_lib_get_bits_32(protocol->data, 0, 32); + uint32_t internal_id = data & 0x7FFFFFFF; + uint32_t fc = 0; + uint32_t cn = 0; + protocol_keri_descramble(&fc, &cn, &data); + furi_string_printf(result, "Internal ID: %lu\r\nFC: %lu, Card: %lu\r\n", internal_id, fc, cn); +} + +bool protocol_keri_write_data(ProtocolKeri* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + // Start bit should be always set + protocol->data[0] |= (1 << 7); + protocol_keri_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_TESTMODE_DISABLED | LFRFID_T5577_X_MODE | + LFRFID_T5577_MODULATION_PSK1 | LFRFID_T5577_PSKCF_RF_2 | + (2 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[0] |= 0xF << 18; + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.blocks_to_write = 3; + result = true; + } + return result; +}; + +const ProtocolBase protocol_keri = { + .name = "Keri", + .manufacturer = "Keri", + .data_size = KERI_DECODED_DATA_SIZE, + .features = LFRFIDFeaturePSK, + .validate_count = 6, + .alloc = (ProtocolAlloc)protocol_keri_alloc, + .free = (ProtocolFree)protocol_keri_free, + .get_data = (ProtocolGetData)protocol_keri_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_keri_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_keri_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_keri_encoder_start, + .yield = (ProtocolEncoderYield)protocol_keri_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_keri_render_data, + .render_brief_data = (ProtocolRenderData)protocol_keri_render_data, + .write_data = (ProtocolWriteData)protocol_keri_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_keri.h b/lib/lfrfid/protocols/protocol_keri.h new file mode 100644 index 00000000000..4cf5f370c9c --- /dev/null +++ b/lib/lfrfid/protocols/protocol_keri.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_keri; diff --git a/lib/lfrfid/protocols/protocol_nexwatch.c b/lib/lfrfid/protocols/protocol_nexwatch.c new file mode 100644 index 00000000000..3bbbb42f503 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_nexwatch.c @@ -0,0 +1,323 @@ +#include +#include +#include +#include "lfrfid_protocols.h" + +#define NEXWATCH_PREAMBLE_BIT_SIZE (8) +#define NEXWATCH_PREAMBLE_DATA_SIZE (1) + +#define NEXWATCH_ENCODED_BIT_SIZE (96) +#define NEXWATCH_ENCODED_DATA_SIZE ((NEXWATCH_ENCODED_BIT_SIZE) / 8) + +#define NEXWATCH_DECODED_BIT_SIZE (NEXWATCH_DECODED_DATA_SIZE * 8) +#define NEXWATCH_DECODED_DATA_SIZE (8) + +#define NEXWATCH_US_PER_BIT (255) +#define NEXWATCH_ENCODER_PULSES_PER_BIT (16) + +typedef struct { + uint8_t magic; + char desc[13]; + uint8_t chk; +} ProtocolNexwatchMagic; + +ProtocolNexwatchMagic magic_items[] = { + {0xBE, "Quadrakey", 0}, + {0x88, "Nexkey", 0}, + {0x86, "Honeywell", 0}}; + +typedef struct { + uint8_t data_index; + uint8_t bit_clock_index; + bool last_bit; + bool current_polarity; + bool pulse_phase; +} ProtocolNexwatchEncoder; + +typedef struct { + uint8_t encoded_data[NEXWATCH_ENCODED_DATA_SIZE]; + uint8_t negative_encoded_data[NEXWATCH_ENCODED_DATA_SIZE]; + uint8_t corrupted_encoded_data[NEXWATCH_ENCODED_DATA_SIZE]; + uint8_t corrupted_negative_encoded_data[NEXWATCH_ENCODED_DATA_SIZE]; + + uint8_t data[NEXWATCH_DECODED_DATA_SIZE]; + ProtocolNexwatchEncoder encoder; +} ProtocolNexwatch; + +ProtocolNexwatch* protocol_nexwatch_alloc(void) { + ProtocolNexwatch* protocol = malloc(sizeof(ProtocolNexwatch)); + return protocol; +}; + +void protocol_nexwatch_free(ProtocolNexwatch* protocol) { + free(protocol); +}; + +uint8_t* protocol_nexwatch_get_data(ProtocolNexwatch* protocol) { + return protocol->data; +}; + +void protocol_nexwatch_decoder_start(ProtocolNexwatch* protocol) { + memset(protocol->encoded_data, 0, NEXWATCH_ENCODED_DATA_SIZE); + memset(protocol->negative_encoded_data, 0, NEXWATCH_ENCODED_DATA_SIZE); + memset(protocol->corrupted_encoded_data, 0, NEXWATCH_ENCODED_DATA_SIZE); + memset(protocol->corrupted_negative_encoded_data, 0, NEXWATCH_ENCODED_DATA_SIZE); +}; + +static bool protocol_nexwatch_check_preamble(uint8_t* data, size_t bit_index) { + // 01010110 + if(bit_lib_get_bits(data, bit_index, 8) != 0b01010110) return false; + return true; +} + +static uint8_t protocol_nexwatch_parity_swap(uint8_t parity) { + uint8_t a = (((parity >> 3) & 1)); + a |= (((parity >> 1) & 1) << 1); + a |= (((parity >> 2) & 1) << 2); + a |= ((parity & 1) << 3); + return a; +} + +static uint8_t protocol_nexwatch_parity(const uint8_t hexid[5]) { + uint8_t p = 0; + for(uint8_t i = 0; i < 5; i++) { + p ^= ((hexid[i]) & 0xF0) >> 4; + p ^= ((hexid[i]) & 0x0F); + } + return protocol_nexwatch_parity_swap(p); +} + +static uint8_t protocol_nexwatch_checksum(uint8_t magic, uint32_t id, uint8_t parity) { + uint8_t a = ((id >> 24) & 0xFF); + a -= ((id >> 16) & 0xFF); + a -= ((id >> 8) & 0xFF); + a -= (id & 0xFF); + a -= magic; + a -= (bit_lib_reverse_8_fast(parity) >> 4); + return bit_lib_reverse_8_fast(a); +} + +static bool protocol_nexwatch_can_be_decoded(uint8_t* data) { + if(!protocol_nexwatch_check_preamble(data, 0)) return false; + + // Check for reserved word (32-bit) + if(bit_lib_get_bits_32(data, 8, 32) != 0) { + return false; + } + + uint8_t parity = bit_lib_get_bits(data, 76, 4); + + // parity check + // from 32b hex id, 4b mode + uint8_t hex[5] = {0}; + for(uint8_t i = 0; i < 5; i++) { + hex[i] = bit_lib_get_bits(data, 40 + (i * 8), 8); + } + //mode is only 4 bits. + hex[4] &= 0xf0; + uint8_t calc_parity = protocol_nexwatch_parity(hex); + + if(calc_parity != parity) { + return false; + } + + return true; +} + +static bool protocol_nexwatch_decoder_feed_internal(bool polarity, uint32_t time, uint8_t* data) { + time += (NEXWATCH_US_PER_BIT / 2); + + size_t bit_count = (time / NEXWATCH_US_PER_BIT); + bool result = false; + + if(bit_count < NEXWATCH_ENCODED_BIT_SIZE) { + for(size_t i = 0; i < bit_count; i++) { + bit_lib_push_bit(data, NEXWATCH_ENCODED_DATA_SIZE, polarity); + if(protocol_nexwatch_can_be_decoded(data)) { + result = true; + break; + } + } + } + + return result; +} + +static void protocol_nexwatch_descramble(uint32_t* id, uint32_t* scrambled) { + // 255 = Not used/Unknown other values are the bit offset in the ID/FC values + const uint8_t hex_2_id[] = {31, 27, 23, 19, 15, 11, 7, 3, 30, 26, 22, 18, 14, 10, 6, 2, + 29, 25, 21, 17, 13, 9, 5, 1, 28, 24, 20, 16, 12, 8, 4, 0}; + + *id = 0; + for(uint8_t idx = 0; idx < 32; idx++) { + bool bit_state = (*scrambled >> hex_2_id[idx]) & 1; + *id |= (bit_state << (31 - idx)); + } +} + +static void protocol_nexwatch_decoder_save(uint8_t* data_to, const uint8_t* data_from) { + uint32_t id = bit_lib_get_bits_32(data_from, 40, 32); + data_to[4] = (uint8_t)id; + data_to[3] = (uint8_t)(id >>= 8); + data_to[2] = (uint8_t)(id >>= 8); + data_to[1] = (uint8_t)(id >>= 8); + data_to[0] = (uint8_t)(id >>= 8); + uint32_t check = bit_lib_get_bits_32(data_from, 72, 24); + data_to[7] = (uint8_t)check; + data_to[6] = (uint8_t)(check >>= 8); + data_to[5] = (uint8_t)(check >>= 8); +} + +bool protocol_nexwatch_decoder_feed(ProtocolNexwatch* protocol, bool level, uint32_t duration) { + bool result = false; + + if(duration > (NEXWATCH_US_PER_BIT / 2)) { + if(protocol_nexwatch_decoder_feed_internal(level, duration, protocol->encoded_data)) { + protocol_nexwatch_decoder_save(protocol->data, protocol->encoded_data); + result = true; + return result; + } + + if(protocol_nexwatch_decoder_feed_internal( + !level, duration, protocol->negative_encoded_data)) { + protocol_nexwatch_decoder_save(protocol->data, protocol->negative_encoded_data); + result = true; + return result; + } + } + + if(duration > (NEXWATCH_US_PER_BIT / 4)) { + // Try to decode wrong phase synced data + if(level) { + duration += 120; + } else { + if(duration > 120) { + duration -= 120; + } + } + + if(protocol_nexwatch_decoder_feed_internal( + level, duration, protocol->corrupted_encoded_data)) { + protocol_nexwatch_decoder_save(protocol->data, protocol->corrupted_encoded_data); + + result = true; + return result; + } + + if(protocol_nexwatch_decoder_feed_internal( + !level, duration, protocol->corrupted_negative_encoded_data)) { + protocol_nexwatch_decoder_save( + protocol->data, protocol->corrupted_negative_encoded_data); + + result = true; + return result; + } + } + + return result; +}; + +bool protocol_nexwatch_encoder_start(ProtocolNexwatch* protocol) { + memset(protocol->encoded_data, 0, NEXWATCH_ENCODED_DATA_SIZE); + *(uint32_t*)&protocol->encoded_data[0] = 0b00000000000000000000000001010110; + bit_lib_copy_bits(protocol->encoded_data, 32, 32, protocol->data, 0); + bit_lib_copy_bits(protocol->encoded_data, 64, 32, protocol->data, 32); + + protocol->encoder.last_bit = + bit_lib_get_bit(protocol->encoded_data, NEXWATCH_ENCODED_BIT_SIZE - 1); + protocol->encoder.data_index = 0; + protocol->encoder.current_polarity = true; + protocol->encoder.pulse_phase = true; + protocol->encoder.bit_clock_index = 0; + + return true; +}; + +LevelDuration protocol_nexwatch_encoder_yield(ProtocolNexwatch* protocol) { + LevelDuration level_duration; + ProtocolNexwatchEncoder* encoder = &protocol->encoder; + + if(encoder->pulse_phase) { + level_duration = level_duration_make(encoder->current_polarity, 1); + encoder->pulse_phase = false; + } else { + level_duration = level_duration_make(!encoder->current_polarity, 1); + encoder->pulse_phase = true; + + encoder->bit_clock_index++; + if(encoder->bit_clock_index >= NEXWATCH_ENCODER_PULSES_PER_BIT) { + encoder->bit_clock_index = 0; + + bool current_bit = bit_lib_get_bit(protocol->encoded_data, encoder->data_index); + + if(current_bit != encoder->last_bit) { + encoder->current_polarity = !encoder->current_polarity; + } + + encoder->last_bit = current_bit; + + bit_lib_increment_index(encoder->data_index, NEXWATCH_ENCODED_BIT_SIZE); + } + } + + return level_duration; +}; + +void protocol_nexwatch_render_data(ProtocolNexwatch* protocol, FuriString* result) { + uint32_t id = 0; + uint32_t scrambled = bit_lib_get_bits_32(protocol->data, 8, 32); + protocol_nexwatch_descramble(&id, &scrambled); + + uint8_t m_idx; + uint8_t mode = bit_lib_get_bits(protocol->data, 40, 4); + uint8_t parity = bit_lib_get_bits(protocol->data, 44, 4); + uint8_t chk = bit_lib_get_bits(protocol->data, 48, 8); + for(m_idx = 0; m_idx < 3; m_idx++) { + magic_items[m_idx].chk = protocol_nexwatch_checksum(magic_items[m_idx].magic, id, parity); + if(magic_items[m_idx].chk == chk) { + break; + } + } + furi_string_printf(result, "ID: %lu, M:%u\r\nType: %s\r\n", id, mode, magic_items[m_idx].desc); +} + +bool protocol_nexwatch_write_data(ProtocolNexwatch* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + protocol_nexwatch_encoder_start(protocol); + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_PSK1 | LFRFID_T5577_BITRATE_RF_32 | + (3 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.blocks_to_write = 4; + result = true; + } + return result; +}; + +const ProtocolBase protocol_nexwatch = { + .name = "Nexwatch", + .manufacturer = "Honeywell", + .data_size = NEXWATCH_DECODED_DATA_SIZE, + .features = LFRFIDFeaturePSK, + .validate_count = 6, + .alloc = (ProtocolAlloc)protocol_nexwatch_alloc, + .free = (ProtocolFree)protocol_nexwatch_free, + .get_data = (ProtocolGetData)protocol_nexwatch_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_nexwatch_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_nexwatch_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_nexwatch_encoder_start, + .yield = (ProtocolEncoderYield)protocol_nexwatch_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_nexwatch_render_data, + .render_brief_data = (ProtocolRenderData)protocol_nexwatch_render_data, + .write_data = (ProtocolWriteData)protocol_nexwatch_write_data, +}; diff --git a/lib/lfrfid/protocols/protocol_nexwatch.h b/lib/lfrfid/protocols/protocol_nexwatch.h new file mode 100644 index 00000000000..0872ca7dcdd --- /dev/null +++ b/lib/lfrfid/protocols/protocol_nexwatch.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_nexwatch; diff --git a/lib/lfrfid/protocols/protocol_pac_stanley.c b/lib/lfrfid/protocols/protocol_pac_stanley.c new file mode 100644 index 00000000000..11c642402a1 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_pac_stanley.c @@ -0,0 +1,231 @@ +#include +#include +#include +#include +#include +#include "lfrfid_protocols.h" + +#define PAC_STANLEY_ENCODED_BIT_SIZE (128) +#define PAC_STANLEY_ENCODED_BYTE_SIZE (((PAC_STANLEY_ENCODED_BIT_SIZE) / 8)) +#define PAC_STANLEY_PREAMBLE_BIT_SIZE (8) +#define PAC_STANLEY_PREAMBLE_BYTE_SIZE (1) +#define PAC_STANLEY_ENCODED_BYTE_FULL_SIZE \ + (PAC_STANLEY_ENCODED_BYTE_SIZE + PAC_STANLEY_PREAMBLE_BYTE_SIZE) +#define PAC_STANLEY_BYTE_LENGTH (10) // start bit, 7 data bits, parity bit, stop bit +#define PAC_STANLEY_DATA_START_INDEX (8 + (3 * PAC_STANLEY_BYTE_LENGTH) + 1) + +#define PAC_STANLEY_DECODED_DATA_SIZE (4) +#define PAC_STANLEY_ENCODED_DATA_SIZE (sizeof(ProtocolPACStanley)) + +#define PAC_STANLEY_CLOCKS_IN_US (32) +#define PAC_STANLEY_CYCLE_LENGTH (256) +#define PAC_STANLEY_MIN_TIME (60) +#define PAC_STANLEY_MAX_TIME (4000) + +typedef struct { + bool inverted; + bool got_preamble; + size_t encoded_index; + uint8_t encoded_data[PAC_STANLEY_ENCODED_BYTE_FULL_SIZE]; + uint8_t data[PAC_STANLEY_DECODED_DATA_SIZE]; +} ProtocolPACStanley; + +ProtocolPACStanley* protocol_pac_stanley_alloc(void) { + ProtocolPACStanley* protocol = malloc(sizeof(ProtocolPACStanley)); + return (void*)protocol; +} + +void protocol_pac_stanley_free(ProtocolPACStanley* protocol) { + free(protocol); +} + +uint8_t* protocol_pac_stanley_get_data(ProtocolPACStanley* protocol) { + return protocol->data; +} + +static void protocol_pac_stanley_decode(ProtocolPACStanley* protocol) { + uint8_t asciiCardId[8]; + for(size_t idx = 0; idx < 8; idx++) { + uint8_t byte = bit_lib_reverse_8_fast(bit_lib_get_bits( + protocol->encoded_data, + PAC_STANLEY_DATA_START_INDEX + (PAC_STANLEY_BYTE_LENGTH * idx), + 8)); + asciiCardId[idx] = byte & 0x7F; // discard the parity bit + } + + hex_chars_to_uint8((char*)asciiCardId, protocol->data); +} + +static bool protocol_pac_stanley_can_be_decoded(ProtocolPACStanley* protocol) { + // Check preamble + if(bit_lib_get_bits(protocol->encoded_data, 0, 8) != 0b11111111) return false; + if(bit_lib_get_bit(protocol->encoded_data, 8) != 0) return false; + if(bit_lib_get_bit(protocol->encoded_data, 9) != 0) return false; + if(bit_lib_get_bit(protocol->encoded_data, 10) != 1) return false; + if(bit_lib_get_bits(protocol->encoded_data, 11, 8) != 0b00000010) return false; + + // Check next preamble + if(bit_lib_get_bits(protocol->encoded_data, 128, 8) != 0b11111111) return false; + + // Checksum + uint8_t checksum = 0; + uint8_t stripped_byte; + for(size_t idx = 0; idx < 9; idx++) { + uint8_t byte = bit_lib_reverse_8_fast(bit_lib_get_bits( + protocol->encoded_data, + PAC_STANLEY_DATA_START_INDEX + (PAC_STANLEY_BYTE_LENGTH * idx), + 8)); + stripped_byte = byte & 0x7F; // discard the parity bit + if(bit_lib_test_parity_32(stripped_byte, BitLibParityOdd) != (byte & 0x80) >> 7) { + return false; + } + if(idx < 8) checksum ^= stripped_byte; + } + if(stripped_byte != checksum) return false; + return true; +} + +void protocol_pac_stanley_decoder_start(ProtocolPACStanley* protocol) { + memset(protocol->data, 0, PAC_STANLEY_DECODED_DATA_SIZE); + protocol->inverted = false; + protocol->got_preamble = false; +} + +bool protocol_pac_stanley_decoder_feed(ProtocolPACStanley* protocol, bool level, uint32_t duration) { + bool pushed = false; + + if(duration > PAC_STANLEY_MAX_TIME) return false; + + uint8_t pulses = (uint8_t)round((float)duration / PAC_STANLEY_CYCLE_LENGTH); + + // Handle last stopbit & preamble (1 sb, 8 bit preamble) + if(pulses >= 9 && !protocol->got_preamble) { + pulses = 8; + protocol->got_preamble = true; + protocol->inverted = !level; + } else if(pulses >= 9 && protocol->got_preamble) { + protocol->got_preamble = false; + } else if(pulses == 0 && duration > PAC_STANLEY_MIN_TIME) { + pulses = 1; + } + + if(pulses) { + for(uint8_t i = 0; i < pulses; i++) { + bit_lib_push_bit( + protocol->encoded_data, + PAC_STANLEY_ENCODED_BYTE_FULL_SIZE, + level ^ protocol->inverted); + } + pushed = true; + } + + if(pushed && protocol_pac_stanley_can_be_decoded(protocol)) { + protocol_pac_stanley_decode(protocol); + return true; + } + + return false; +} + +bool protocol_pac_stanley_encoder_start(ProtocolPACStanley* protocol) { + memset(protocol->encoded_data, 0, sizeof(protocol->encoded_data)); + + uint8_t idbytes[10]; + idbytes[0] = '2'; + idbytes[1] = '0'; + + uint8_to_hex_chars(protocol->data, &idbytes[2], 8); + + // insert start and stop bits + for(size_t i = 0; i < 16; i++) protocol->encoded_data[i] = 0x40 >> ((i + 3) % 5 * 2); + + protocol->encoded_data[0] = 0xFF; // mark + stop + protocol->encoded_data[1] = 0x20; // start + reflect8(STX) + + uint8_t checksum = 0; + for(size_t i = 2; i < 13; i++) { + uint8_t shift = 7 - (i + 3) % 4 * 2; + uint8_t index = i + (i - 1) / 4; + + uint16_t pattern; + if(i < 12) { + pattern = bit_lib_reverse_8_fast(idbytes[i - 2]); + pattern |= bit_lib_test_parity_32(pattern, BitLibParityOdd); + if(i > 3) checksum ^= idbytes[i - 2]; + } else { + pattern = (bit_lib_reverse_8_fast(checksum) & 0xFE) | + (bit_lib_test_parity_32(checksum, BitLibParityOdd)); + } + pattern <<= shift; + + protocol->encoded_data[index] |= pattern >> 8 & 0xFF; + protocol->encoded_data[index + 1] |= pattern & 0xFF; + } + + protocol->encoded_index = 0; + return true; +} + +LevelDuration protocol_pac_stanley_encoder_yield(ProtocolPACStanley* protocol) { + uint16_t length = PAC_STANLEY_CLOCKS_IN_US; + bool bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_index); + bit_lib_increment_index(protocol->encoded_index, PAC_STANLEY_ENCODED_BIT_SIZE); + while(bit_lib_get_bit(protocol->encoded_data, protocol->encoded_index) == bit) { + length += PAC_STANLEY_CLOCKS_IN_US; + bit_lib_increment_index(protocol->encoded_index, PAC_STANLEY_ENCODED_BIT_SIZE); + } + + return level_duration_make(bit, length); +} + +bool protocol_pac_stanley_write_data(ProtocolPACStanley* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + // Correct protocol data by redecoding + protocol_pac_stanley_encoder_start(protocol); + protocol_pac_stanley_decode(protocol); + + protocol_pac_stanley_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_DIRECT | LFRFID_T5577_BITRATE_RF_32 | + (4 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.block[4] = bit_lib_get_bits_32(protocol->encoded_data, 96, 32); + request->t5577.blocks_to_write = 5; + result = true; + } + return result; +} + +void protocol_pac_stanley_render_data(ProtocolPACStanley* protocol, FuriString* result) { + uint8_t* data = protocol->data; + furi_string_printf(result, "CIN: %02X%02X%02X%02X", data[0], data[1], data[2], data[3]); +} + +const ProtocolBase protocol_pac_stanley = { + .name = "PAC/Stanley", + .manufacturer = "N/A", + .data_size = PAC_STANLEY_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_pac_stanley_alloc, + .free = (ProtocolFree)protocol_pac_stanley_free, + .get_data = (ProtocolGetData)protocol_pac_stanley_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_pac_stanley_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_pac_stanley_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_pac_stanley_encoder_start, + .yield = (ProtocolEncoderYield)protocol_pac_stanley_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_pac_stanley_render_data, + .render_brief_data = (ProtocolRenderData)protocol_pac_stanley_render_data, + .write_data = (ProtocolWriteData)protocol_pac_stanley_write_data, +}; diff --git a/lib/lfrfid/protocols/protocol_pac_stanley.h b/lib/lfrfid/protocols/protocol_pac_stanley.h new file mode 100644 index 00000000000..3ca329cf797 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_pac_stanley.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_pac_stanley; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_paradox.c b/lib/lfrfid/protocols/protocol_paradox.c new file mode 100644 index 00000000000..26c9b55dcd1 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_paradox.c @@ -0,0 +1,240 @@ +#include +#include +#include +#include +#include +#include "lfrfid_protocols.h" + +#define JITTER_TIME (20) +#define MIN_TIME (64 - JITTER_TIME) +#define MAX_TIME (80 + JITTER_TIME) + +#define PARADOX_DECODED_DATA_SIZE (6) + +#define PARADOX_PREAMBLE_LENGTH (8) +#define PARADOX_ENCODED_BIT_SIZE (96) +#define PARADOX_ENCODED_DATA_SIZE (((PARADOX_ENCODED_BIT_SIZE) / 8) + 1) +#define PARADOX_ENCODED_DATA_LAST (PARADOX_ENCODED_DATA_SIZE - 1) + +typedef struct { + FSKDemod* fsk_demod; +} ProtocolParadoxDecoder; + +typedef struct { + FSKOsc* fsk_osc; + uint8_t encoded_index; +} ProtocolParadoxEncoder; + +typedef struct { + ProtocolParadoxDecoder decoder; + ProtocolParadoxEncoder encoder; + uint8_t encoded_data[PARADOX_ENCODED_DATA_SIZE]; + uint8_t data[PARADOX_DECODED_DATA_SIZE]; +} ProtocolParadox; + +ProtocolParadox* protocol_paradox_alloc(void) { + ProtocolParadox* protocol = malloc(sizeof(ProtocolParadox)); + protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5); + protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50); + + return protocol; +}; + +void protocol_paradox_free(ProtocolParadox* protocol) { + fsk_demod_free(protocol->decoder.fsk_demod); + fsk_osc_free(protocol->encoder.fsk_osc); + free(protocol); +}; + +uint8_t* protocol_paradox_get_data(ProtocolParadox* protocol) { + return protocol->data; +}; + +void protocol_paradox_decoder_start(ProtocolParadox* protocol) { + memset(protocol->encoded_data, 0, PARADOX_ENCODED_DATA_SIZE); +}; + +static bool protocol_paradox_can_be_decoded(ProtocolParadox* protocol) { + // check preamble + if(protocol->encoded_data[0] != 0b00001111 || + protocol->encoded_data[PARADOX_ENCODED_DATA_LAST] != 0b00001111) + return false; + + for(uint32_t i = PARADOX_PREAMBLE_LENGTH; i < 96; i += 2) { + if(bit_lib_get_bit(protocol->encoded_data, i) == + bit_lib_get_bit(protocol->encoded_data, i + 1)) { + return false; + } + } + + return true; +} + +static void protocol_paradox_decode(uint8_t* encoded_data, uint8_t* decoded_data) { + for(uint32_t i = PARADOX_PREAMBLE_LENGTH; i < 96; i += 2) { + if(bit_lib_get_bits(encoded_data, i, 2) == 0b01) { + bit_lib_push_bit(decoded_data, PARADOX_DECODED_DATA_SIZE, 0); + } else if(bit_lib_get_bits(encoded_data, i, 2) == 0b10) { + bit_lib_push_bit(decoded_data, PARADOX_DECODED_DATA_SIZE, 1); + } + } + bit_lib_push_bit(decoded_data, PARADOX_DECODED_DATA_SIZE, 0); + bit_lib_push_bit(decoded_data, PARADOX_DECODED_DATA_SIZE, 0); + bit_lib_push_bit(decoded_data, PARADOX_DECODED_DATA_SIZE, 0); + bit_lib_push_bit(decoded_data, PARADOX_DECODED_DATA_SIZE, 0); +} + +bool protocol_paradox_decoder_feed(ProtocolParadox* protocol, bool level, uint32_t duration) { + bool value; + uint32_t count; + + fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count); + if(count > 0) { + for(size_t i = 0; i < count; i++) { + bit_lib_push_bit(protocol->encoded_data, PARADOX_ENCODED_DATA_SIZE, value); + if(protocol_paradox_can_be_decoded(protocol)) { + protocol_paradox_decode(protocol->encoded_data, protocol->data); + + return true; + } + } + } + + return false; +}; + +static void protocol_paradox_encode(const uint8_t* decoded_data, uint8_t* encoded_data) { + // preamble + bit_lib_set_bits(encoded_data, 0, 0b00001111, 8); + + for(size_t i = 0; i < 44; i++) { + if(bit_lib_get_bit(decoded_data, i)) { + bit_lib_set_bits(encoded_data, PARADOX_PREAMBLE_LENGTH + i * 2, 0b10, 2); + } else { + bit_lib_set_bits(encoded_data, PARADOX_PREAMBLE_LENGTH + i * 2, 0b01, 2); + } + } +}; + +bool protocol_paradox_encoder_start(ProtocolParadox* protocol) { + protocol_paradox_encode(protocol->data, (uint8_t*)protocol->encoded_data); + protocol->encoder.encoded_index = 0; + fsk_osc_reset(protocol->encoder.fsk_osc); + return true; +}; + +LevelDuration protocol_paradox_encoder_yield(ProtocolParadox* protocol) { + bool level; + uint32_t duration; + + bool bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index); + bool advance = fsk_osc_next_half(protocol->encoder.fsk_osc, bit, &level, &duration); + + if(advance) { + bit_lib_increment_index(protocol->encoder.encoded_index, PARADOX_ENCODED_BIT_SIZE); + } + return level_duration_make(level, duration); +}; + +static uint8_t protocol_paradox_calculate_checksum(uint8_t fc, uint16_t card_id) { + uint8_t card_hi = (card_id >> 8) & 0xff; + uint8_t card_lo = card_id & 0xff; + + uint8_t arr[5] = {0, 0, fc, card_hi, card_lo}; + + uint8_t manchester[9]; + + bit_lib_push_bit(manchester, 9, false); + bit_lib_push_bit(manchester, 9, false); + bit_lib_push_bit(manchester, 9, false); + bit_lib_push_bit(manchester, 9, false); + + for(uint8_t i = 6; i < 40; i += 1) { + if(bit_lib_get_bit(arr, i) == 0b1) { + bit_lib_push_bit(manchester, 9, true); + bit_lib_push_bit(manchester, 9, false); + } else { + bit_lib_push_bit(manchester, 9, false); + bit_lib_push_bit(manchester, 9, true); + } + } + + uint8_t output = bit_lib_crc8(manchester, 9, 0x31, 0x00, true, true, 0x06); + + return output; +} + +void protocol_paradox_render_data(ProtocolParadox* protocol, FuriString* result) { + uint8_t* decoded_data = protocol->data; + uint8_t fc = bit_lib_get_bits(decoded_data, 10, 8); + uint16_t card_id = bit_lib_get_bits_16(decoded_data, 18, 16); + uint8_t card_crc = bit_lib_get_bits_16(decoded_data, 34, 8); + uint8_t calc_crc = protocol_paradox_calculate_checksum(fc, card_id); + + furi_string_cat_printf(result, "Facility: %u\r\n", fc); + furi_string_cat_printf(result, "Card: %u\r\n", card_id); + furi_string_cat_printf(result, "CRC: %u Calc CRC: %u\r\n", card_crc, calc_crc); + if(card_crc != calc_crc) furi_string_cat_printf(result, "CRC Mismatch, Invalid Card!\r\n"); +}; + +void protocol_paradox_render_brief_data(ProtocolParadox* protocol, FuriString* result) { + uint8_t* decoded_data = protocol->data; + + uint8_t fc = bit_lib_get_bits(decoded_data, 10, 8); + uint16_t card_id = bit_lib_get_bits_16(decoded_data, 18, 16); + uint8_t card_crc = bit_lib_get_bits_16(decoded_data, 34, 8); + uint8_t calc_crc = protocol_paradox_calculate_checksum(fc, card_id); + + furi_string_cat_printf(result, "FC: %03u, Card: %05u\r\n", fc, card_id); + if(calc_crc == card_crc) { + furi_string_cat_printf(result, "CRC : %03u", card_crc); + } else { + furi_string_cat_printf(result, "Card is Invalid!"); + } +}; + +bool protocol_paradox_write_data(ProtocolParadox* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + // Correct protocol data by redecoding + protocol_paradox_encode(protocol->data, (uint8_t*)protocol->encoded_data); + protocol_paradox_decode(protocol->encoded_data, protocol->data); + + protocol_paradox_encode(protocol->data, (uint8_t*)protocol->encoded_data); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 | + (3 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.blocks_to_write = 4; + result = true; + } + return result; +}; + +const ProtocolBase protocol_paradox = { + .name = "Paradox", + .manufacturer = "Paradox", + .data_size = PARADOX_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_paradox_alloc, + .free = (ProtocolFree)protocol_paradox_free, + .get_data = (ProtocolGetData)protocol_paradox_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_paradox_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_paradox_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_paradox_encoder_start, + .yield = (ProtocolEncoderYield)protocol_paradox_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_paradox_render_data, + .render_brief_data = (ProtocolRenderData)protocol_paradox_render_brief_data, + .write_data = (ProtocolWriteData)protocol_paradox_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_paradox.h b/lib/lfrfid/protocols/protocol_paradox.h new file mode 100644 index 00000000000..a80696c35ab --- /dev/null +++ b/lib/lfrfid/protocols/protocol_paradox.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_paradox; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_pyramid.c b/lib/lfrfid/protocols/protocol_pyramid.c new file mode 100644 index 00000000000..d794bb46ead --- /dev/null +++ b/lib/lfrfid/protocols/protocol_pyramid.c @@ -0,0 +1,282 @@ +#include +#include +#include +#include +#include "lfrfid_protocols.h" +#include + +#define JITTER_TIME (20) +#define MIN_TIME (64 - JITTER_TIME) +#define MAX_TIME (80 + JITTER_TIME) + +#define PYRAMID_DATA_SIZE 13 +#define PYRAMID_PREAMBLE_SIZE 3 + +#define PYRAMID_ENCODED_DATA_SIZE \ + (PYRAMID_PREAMBLE_SIZE + PYRAMID_DATA_SIZE + PYRAMID_PREAMBLE_SIZE) +#define PYRAMID_ENCODED_BIT_SIZE ((PYRAMID_PREAMBLE_SIZE + PYRAMID_DATA_SIZE) * 8) +#define PYRAMID_DECODED_DATA_SIZE (4) +#define PYRAMID_DECODED_BIT_SIZE ((PYRAMID_ENCODED_BIT_SIZE - PYRAMID_PREAMBLE_SIZE * 8) / 2) + +typedef struct { + FSKDemod* fsk_demod; +} ProtocolPyramidDecoder; + +typedef struct { + FSKOsc* fsk_osc; + uint8_t encoded_index; + uint32_t pulse; +} ProtocolPyramidEncoder; + +typedef struct { + ProtocolPyramidDecoder decoder; + ProtocolPyramidEncoder encoder; + uint8_t encoded_data[PYRAMID_ENCODED_DATA_SIZE]; + uint8_t data[PYRAMID_DECODED_DATA_SIZE]; +} ProtocolPyramid; + +ProtocolPyramid* protocol_pyramid_alloc(void) { + ProtocolPyramid* protocol = malloc(sizeof(ProtocolPyramid)); + protocol->decoder.fsk_demod = fsk_demod_alloc(MIN_TIME, 6, MAX_TIME, 5); + protocol->encoder.fsk_osc = fsk_osc_alloc(8, 10, 50); + + return protocol; +}; + +void protocol_pyramid_free(ProtocolPyramid* protocol) { + fsk_demod_free(protocol->decoder.fsk_demod); + fsk_osc_free(protocol->encoder.fsk_osc); + free(protocol); +}; + +uint8_t* protocol_pyramid_get_data(ProtocolPyramid* protocol) { + return protocol->data; +}; + +void protocol_pyramid_decoder_start(ProtocolPyramid* protocol) { + memset(protocol->encoded_data, 0, PYRAMID_ENCODED_DATA_SIZE); +}; + +static bool protocol_pyramid_can_be_decoded(uint8_t* data) { + // check preamble + if(bit_lib_get_bits_16(data, 0, 16) != 0b0000000000000001 || + bit_lib_get_bits(data, 16, 8) != 0b00000001) { + return false; + } + + if(bit_lib_get_bits_16(data, 128, 16) != 0b0000000000000001 || + bit_lib_get_bits(data, 136, 8) != 0b00000001) { + return false; + } + + uint8_t checksum = bit_lib_get_bits(data, 120, 8); + uint8_t checksum_data[13] = {0x00}; + for(uint8_t i = 0; i < 13; i++) { + checksum_data[i] = bit_lib_get_bits(data, 16 + (i * 8), 8); + } + + uint8_t calc_checksum = bit_lib_crc8(checksum_data, 13, 0x31, 0x00, true, true, 0x00); + if(checksum != calc_checksum) return false; + + // Remove parity + bit_lib_remove_bit_every_nth(data, 8, 15 * 8, 8); + + // Determine Startbit and format + int j; + for(j = 0; j < 105; ++j) { + if(bit_lib_get_bit(data, j)) break; + } + uint8_t fmt_len = 105 - j; + + // Only support 26bit format for now + if(fmt_len != 26) return false; + + return true; +} + +static void protocol_pyramid_decode(ProtocolPyramid* protocol) { + // Format + bit_lib_set_bits(protocol->data, 0, 26, 8); + + // Facility Code + bit_lib_copy_bits(protocol->data, 8, 8, protocol->encoded_data, 73 + 8); + + // Card Number + bit_lib_copy_bits(protocol->data, 16, 16, protocol->encoded_data, 81 + 8); +} + +bool protocol_pyramid_decoder_feed(ProtocolPyramid* protocol, bool level, uint32_t duration) { + bool value; + uint32_t count; + bool result = false; + + fsk_demod_feed(protocol->decoder.fsk_demod, level, duration, &value, &count); + if(count > 0) { + for(size_t i = 0; i < count; i++) { + bit_lib_push_bit(protocol->encoded_data, PYRAMID_ENCODED_DATA_SIZE, value); + if(protocol_pyramid_can_be_decoded(protocol->encoded_data)) { + protocol_pyramid_decode(protocol); + result = true; + } + } + } + + return result; +}; + +bool protocol_pyramid_get_parity(const uint8_t* bits, uint8_t type, int length) { + int x; + for(x = 0; length > 0; --length) x += bit_lib_get_bit(bits, length - 1); + x %= 2; + return x ^ type; +} + +void protocol_pyramid_add_wiegand_parity( + uint8_t* target, + uint8_t target_position, + uint8_t* source, + uint8_t length) { + bit_lib_set_bit( + target, target_position, protocol_pyramid_get_parity(source, 0 /* even */, length / 2)); + bit_lib_copy_bits(target, target_position + 1, length, source, 0); + bit_lib_set_bit( + target, + target_position + length + 1, + protocol_pyramid_get_parity(source + length / 2, 1 /* odd */, length / 2)); +} + +static void protocol_pyramid_encode(ProtocolPyramid* protocol) { + memset(protocol->encoded_data, 0, sizeof(protocol->encoded_data)); + + uint8_t pre[16]; + memset(pre, 0, sizeof(pre)); + + // Format start bit + bit_lib_set_bit(pre, 79, 1); + + uint8_t wiegand[3]; + memset(wiegand, 0, sizeof(wiegand)); + + // FC + bit_lib_copy_bits(wiegand, 0, 8, protocol->data, 8); + + // CardNum + bit_lib_copy_bits(wiegand, 8, 16, protocol->data, 16); + + // Wiegand parity + protocol_pyramid_add_wiegand_parity(pre, 80, wiegand, 24); + + bit_lib_add_parity(pre, 8, protocol->encoded_data, 8, 102, 8, 1); + + // Add checksum + uint8_t checksum_buffer[13]; + for(uint8_t i = 0; i < 13; i++) + checksum_buffer[i] = bit_lib_get_bits(protocol->encoded_data, 16 + (i * 8), 8); + + uint8_t crc = bit_lib_crc8(checksum_buffer, 13, 0x31, 0x00, true, true, 0x00); + bit_lib_set_bits(protocol->encoded_data, 120, crc, 8); +} + +bool protocol_pyramid_encoder_start(ProtocolPyramid* protocol) { + protocol->encoder.encoded_index = 0; + protocol->encoder.pulse = 0; + protocol_pyramid_encode(protocol); + + return true; +}; + +LevelDuration protocol_pyramid_encoder_yield(ProtocolPyramid* protocol) { + bool level = 0; + uint32_t duration = 0; + + // if pulse is zero, we need to output high, otherwise we need to output low + if(protocol->encoder.pulse == 0) { + // get bit + uint8_t bit = bit_lib_get_bit(protocol->encoded_data, protocol->encoder.encoded_index); + + // get pulse from oscillator + bool advance = fsk_osc_next(protocol->encoder.fsk_osc, bit, &duration); + + if(advance) { + bit_lib_increment_index(protocol->encoder.encoded_index, PYRAMID_ENCODED_BIT_SIZE); + } + + // duration diveded by 2 because we need to output high and low + duration = duration / 2; + protocol->encoder.pulse = duration; + level = true; + } else { + // output low half and reset pulse + duration = protocol->encoder.pulse; + protocol->encoder.pulse = 0; + level = false; + } + + return level_duration_make(level, duration); +}; + +bool protocol_pyramid_write_data(ProtocolPyramid* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + // Correct protocol data by redecoding + protocol_pyramid_encode(protocol); + bit_lib_remove_bit_every_nth(protocol->encoded_data, 8, 15 * 8, 8); + protocol_pyramid_decode(protocol); + + protocol_pyramid_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_MODULATION_FSK2a | LFRFID_T5577_BITRATE_RF_50 | + (4 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.block[4] = bit_lib_get_bits_32(protocol->encoded_data, 96, 32); + request->t5577.blocks_to_write = 5; + result = true; + } + return result; +}; + +void protocol_pyramid_render_data(ProtocolPyramid* protocol, FuriString* result) { + uint8_t* decoded_data = protocol->data; + uint8_t format_length = decoded_data[0]; + + furi_string_cat_printf(result, "Format: %d\r\n", format_length); + if(format_length == 26) { + uint8_t facility; + bit_lib_copy_bits(&facility, 0, 8, decoded_data, 8); + + uint16_t card_id; + bit_lib_copy_bits((uint8_t*)&card_id, 8, 8, decoded_data, 16); + bit_lib_copy_bits((uint8_t*)&card_id, 0, 8, decoded_data, 24); + furi_string_cat_printf(result, "FC: %03u, Card: %05u", facility, card_id); + } else { + furi_string_cat_printf(result, "Data: unknown"); + } +}; + +const ProtocolBase protocol_pyramid = { + .name = "Pyramid", + .manufacturer = "Farpointe", + .data_size = PYRAMID_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_pyramid_alloc, + .free = (ProtocolFree)protocol_pyramid_free, + .get_data = (ProtocolGetData)protocol_pyramid_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_pyramid_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_pyramid_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_pyramid_encoder_start, + .yield = (ProtocolEncoderYield)protocol_pyramid_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_pyramid_render_data, + .render_brief_data = (ProtocolRenderData)protocol_pyramid_render_data, + .write_data = (ProtocolWriteData)protocol_pyramid_write_data, +}; diff --git a/lib/lfrfid/protocols/protocol_pyramid.h b/lib/lfrfid/protocols/protocol_pyramid.h new file mode 100644 index 00000000000..940ecaec203 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_pyramid.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_pyramid; diff --git a/lib/lfrfid/protocols/protocol_viking.c b/lib/lfrfid/protocols/protocol_viking.c new file mode 100644 index 00000000000..8083f6d915f --- /dev/null +++ b/lib/lfrfid/protocols/protocol_viking.c @@ -0,0 +1,205 @@ +#include +#include +#include +#include +#include "lfrfid_protocols.h" + +#define VIKING_CLOCK_PER_BIT (32) + +#define VIKING_ENCODED_BIT_SIZE (64) +#define VIKING_ENCODED_BYTE_SIZE (((VIKING_ENCODED_BIT_SIZE) / 8)) +#define VIKING_PREAMBLE_BIT_SIZE (24) +#define VIKING_PREAMBLE_BYTE_SIZE (3) +#define VIKING_ENCODED_BYTE_FULL_SIZE (VIKING_ENCODED_BYTE_SIZE + VIKING_PREAMBLE_BYTE_SIZE) +#define VIKING_DECODED_DATA_SIZE 4 + +#define VIKING_READ_SHORT_TIME (128) +#define VIKING_READ_LONG_TIME (256) +#define VIKING_READ_JITTER_TIME (60) + +#define VIKING_READ_SHORT_TIME_LOW (VIKING_READ_SHORT_TIME - VIKING_READ_JITTER_TIME) +#define VIKING_READ_SHORT_TIME_HIGH (VIKING_READ_SHORT_TIME + VIKING_READ_JITTER_TIME) +#define VIKING_READ_LONG_TIME_LOW (VIKING_READ_LONG_TIME - VIKING_READ_JITTER_TIME) +#define VIKING_READ_LONG_TIME_HIGH (VIKING_READ_LONG_TIME + VIKING_READ_JITTER_TIME) + +typedef struct { + uint8_t data[VIKING_DECODED_DATA_SIZE]; + uint8_t encoded_data[VIKING_ENCODED_BYTE_FULL_SIZE]; + + uint8_t encoded_data_index; + bool encoded_polarity; + + ManchesterState decoder_manchester_state; +} ProtocolViking; + +ProtocolViking* protocol_viking_alloc(void) { + ProtocolViking* proto = malloc(sizeof(ProtocolViking)); + return (void*)proto; +}; + +void protocol_viking_free(ProtocolViking* protocol) { + free(protocol); +}; + +uint8_t* protocol_viking_get_data(ProtocolViking* protocol) { + return protocol->data; +}; + +static void protocol_viking_decode(ProtocolViking* protocol) { + // Copy Card ID + bit_lib_copy_bits(protocol->data, 0, 32, protocol->encoded_data, 24); +} + +static bool protocol_viking_can_be_decoded(ProtocolViking* protocol) { + // check 24 bits preamble + if(bit_lib_get_bits_16(protocol->encoded_data, 0, 16) != 0b1111001000000000) return false; + if(bit_lib_get_bits(protocol->encoded_data, 16, 8) != 0b00000000) return false; + + // check next 24 bits preamble + if(bit_lib_get_bits_16(protocol->encoded_data, 64, 16) != 0b1111001000000000) return false; + if(bit_lib_get_bits(protocol->encoded_data, 80, 8) != 0b00000000) return false; + + // Checksum + uint32_t checksum = bit_lib_get_bits(protocol->encoded_data, 0, 8) ^ + bit_lib_get_bits(protocol->encoded_data, 8, 8) ^ + bit_lib_get_bits(protocol->encoded_data, 16, 8) ^ + bit_lib_get_bits(protocol->encoded_data, 24, 8) ^ + bit_lib_get_bits(protocol->encoded_data, 32, 8) ^ + bit_lib_get_bits(protocol->encoded_data, 40, 8) ^ + bit_lib_get_bits(protocol->encoded_data, 48, 8) ^ + bit_lib_get_bits(protocol->encoded_data, 56, 8) ^ 0xA8; + if(checksum != 0) return false; + + return true; +} + +void protocol_viking_decoder_start(ProtocolViking* protocol) { + memset(protocol->encoded_data, 0, VIKING_ENCODED_BYTE_FULL_SIZE); + manchester_advance( + protocol->decoder_manchester_state, + ManchesterEventReset, + &protocol->decoder_manchester_state, + NULL); +}; + +bool protocol_viking_decoder_feed(ProtocolViking* protocol, bool level, uint32_t duration) { + bool result = false; + + ManchesterEvent event = ManchesterEventReset; + + if(duration > VIKING_READ_SHORT_TIME_LOW && duration < VIKING_READ_SHORT_TIME_HIGH) { + if(!level) { + event = ManchesterEventShortHigh; + } else { + event = ManchesterEventShortLow; + } + } else if(duration > VIKING_READ_LONG_TIME_LOW && duration < VIKING_READ_LONG_TIME_HIGH) { + if(!level) { + event = ManchesterEventLongHigh; + } else { + event = ManchesterEventLongLow; + } + } + + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + protocol->decoder_manchester_state, event, &protocol->decoder_manchester_state, &data); + + if(data_ok) { + bit_lib_push_bit(protocol->encoded_data, VIKING_ENCODED_BYTE_FULL_SIZE, data); + + if(protocol_viking_can_be_decoded(protocol)) { + protocol_viking_decode(protocol); + result = true; + } + } + } + + return result; +}; + +bool protocol_viking_encoder_start(ProtocolViking* protocol) { + // Preamble + bit_lib_set_bits(protocol->encoded_data, 0, 0b11110010, 8); + bit_lib_set_bits(protocol->encoded_data, 8, 0b00000000, 8); + bit_lib_set_bits(protocol->encoded_data, 16, 0b00000000, 8); + + // Card Id + bit_lib_copy_bits(protocol->encoded_data, 24, 32, protocol->data, 0); + + // Checksum + uint32_t id = bit_lib_get_bits_32(protocol->data, 0, 32); + uint8_t checksum = ((id >> 24) & 0xFF) ^ ((id >> 16) & 0xFF) ^ ((id >> 8) & 0xFF) ^ + (id & 0xFF) ^ 0xF2 ^ 0xA8; + bit_lib_set_bits(protocol->encoded_data, 56, checksum, 8); + + return true; +}; + +LevelDuration protocol_viking_encoder_yield(ProtocolViking* protocol) { + bool level = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_data_index); + uint32_t duration = VIKING_CLOCK_PER_BIT / 2; + + if(protocol->encoded_polarity) { + protocol->encoded_polarity = false; + } else { + level = !level; + + protocol->encoded_polarity = true; + bit_lib_increment_index(protocol->encoded_data_index, VIKING_ENCODED_BIT_SIZE); + } + + return level_duration_make(level, duration); +}; + +bool protocol_viking_write_data(ProtocolViking* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + // Correct protocol data by redecoding + protocol_viking_encoder_start(protocol); + protocol_viking_decode(protocol); + + protocol_viking_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = + (LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_32 | + (2 << LFRFID_T5577_MAXBLOCK_SHIFT)); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.blocks_to_write = 3; + result = true; + } + return result; +}; + +void protocol_viking_render_data(ProtocolViking* protocol, FuriString* result) { + uint32_t id = bit_lib_get_bits_32(protocol->data, 0, 32); + furi_string_printf(result, "ID: %08lX\r\n", id); +}; + +const ProtocolBase protocol_viking = { + .name = "Viking", + .manufacturer = "Viking", + .data_size = VIKING_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_viking_alloc, + .free = (ProtocolFree)protocol_viking_free, + .get_data = (ProtocolGetData)protocol_viking_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_viking_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_viking_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_viking_encoder_start, + .yield = (ProtocolEncoderYield)protocol_viking_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_viking_render_data, + .render_brief_data = (ProtocolRenderData)protocol_viking_render_data, + .write_data = (ProtocolWriteData)protocol_viking_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_viking.h b/lib/lfrfid/protocols/protocol_viking.h new file mode 100644 index 00000000000..3286e03a7da --- /dev/null +++ b/lib/lfrfid/protocols/protocol_viking.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_viking; diff --git a/lib/lfrfid/tools/bit_lib.c b/lib/lfrfid/tools/bit_lib.c new file mode 100644 index 00000000000..e0d0ff40213 --- /dev/null +++ b/lib/lfrfid/tools/bit_lib.c @@ -0,0 +1,367 @@ +#include "bit_lib.h" +#include +#include + +void bit_lib_push_bit(uint8_t* data, size_t data_size, bool bit) { + size_t last_index = data_size - 1; + + for(size_t i = 0; i < last_index; ++i) { + data[i] = (data[i] << 1) | ((data[i + 1] >> 7) & 1); + } + data[last_index] = (data[last_index] << 1) | bit; +} + +void bit_lib_set_bit(uint8_t* data, size_t position, bool bit) { + if(bit) { + data[position / 8] |= 1UL << (7 - (position % 8)); + } else { + data[position / 8] &= ~(1UL << (7 - (position % 8))); + } +} + +void bit_lib_set_bits(uint8_t* data, size_t position, uint8_t byte, uint8_t length) { + furi_check(length <= 8); + furi_check(length > 0); + + for(uint8_t i = 0; i < length; ++i) { + uint8_t shift = (length - 1) - i; + bit_lib_set_bit(data, position + i, (byte >> shift) & 1); //-V610 + } +} + +bool bit_lib_get_bit(const uint8_t* data, size_t position) { + return (data[position / 8] >> (7 - (position % 8))) & 1; +} + +uint8_t bit_lib_get_bits(const uint8_t* data, size_t position, uint8_t length) { + uint8_t shift = position % 8; + if(shift == 0) { + return data[position / 8] >> (8 - length); + } else { + // TODO FL-3534: fix read out of bounds + uint8_t value = (data[position / 8] << (shift)); + value |= data[position / 8 + 1] >> (8 - shift); + value = value >> (8 - length); + return value; + } +} + +uint16_t bit_lib_get_bits_16(const uint8_t* data, size_t position, uint8_t length) { + uint16_t value = 0; + if(length <= 8) { + value = bit_lib_get_bits(data, position, length); + } else { + value = bit_lib_get_bits(data, position, 8) << (length - 8); + value |= bit_lib_get_bits(data, position + 8, length - 8); + } + return value; +} + +uint32_t bit_lib_get_bits_32(const uint8_t* data, size_t position, uint8_t length) { + uint32_t value = 0; + if(length <= 8) { + value = bit_lib_get_bits(data, position, length); + } else if(length <= 16) { + value = bit_lib_get_bits(data, position, 8) << (length - 8); + value |= bit_lib_get_bits(data, position + 8, length - 8); + } else if(length <= 24) { + value = bit_lib_get_bits(data, position, 8) << (length - 8); + value |= bit_lib_get_bits(data, position + 8, 8) << (length - 16); + value |= bit_lib_get_bits(data, position + 16, length - 16); + } else { + value = (uint32_t)bit_lib_get_bits(data, position, 8) << (length - 8); + value |= (uint32_t)bit_lib_get_bits(data, position + 8, 8) << (length - 16); + value |= (uint32_t)bit_lib_get_bits(data, position + 16, 8) << (length - 24); + value |= bit_lib_get_bits(data, position + 24, length - 24); + } + + return value; +} + +bool bit_lib_test_parity_32(uint32_t bits, BitLibParity parity) { +#if !defined __GNUC__ +#error Please, implement parity test for non-GCC compilers +#else + switch(parity) { + case BitLibParityEven: + return __builtin_parity(bits); + case BitLibParityOdd: + return !__builtin_parity(bits); + default: + furi_crash("Unknown parity"); + } +#endif +} + +bool bit_lib_test_parity( + const uint8_t* bits, + size_t position, + uint8_t length, + BitLibParity parity, + uint8_t parity_length) { + uint32_t parity_block; + bool result = true; + const size_t parity_blocks_count = length / parity_length; + + for(size_t i = 0; i < parity_blocks_count; ++i) { + switch(parity) { + case BitLibParityEven: + case BitLibParityOdd: + parity_block = bit_lib_get_bits_32(bits, position + i * parity_length, parity_length); + if(!bit_lib_test_parity_32(parity_block, parity)) { + result = false; + } + break; + case BitLibParityAlways0: + if(bit_lib_get_bit(bits, position + i * parity_length + parity_length - 1)) { + result = false; + } + break; + case BitLibParityAlways1: + if(!bit_lib_get_bit(bits, position + i * parity_length + parity_length - 1)) { + result = false; + } + break; + } + + if(!result) break; + } + return result; +} + +size_t bit_lib_add_parity( + const uint8_t* data, + size_t position, + uint8_t* dest, + size_t dest_position, + uint8_t source_length, + uint8_t parity_length, + BitLibParity parity) { + uint32_t parity_word = 0; + size_t j = 0, bit_count = 0; + for(int word = 0; word < source_length; word += parity_length - 1) { + for(int bit = 0; bit < parity_length - 1; bit++) { + parity_word = (parity_word << 1) | bit_lib_get_bit(data, position + word + bit); + bit_lib_set_bit( + dest, dest_position + j++, bit_lib_get_bit(data, position + word + bit)); + } + // if parity fails then return 0 + switch(parity) { + case BitLibParityAlways0: + bit_lib_set_bit(dest, dest_position + j++, 0); + break; // marker bit which should be a 0 + case BitLibParityAlways1: + bit_lib_set_bit(dest, dest_position + j++, 1); + break; // marker bit which should be a 1 + default: + bit_lib_set_bit( + dest, + dest_position + j++, + (bit_lib_test_parity_32(parity_word, BitLibParityOdd) ^ parity) ^ 1); + break; + } + bit_count += parity_length; + parity_word = 0; + } + // if we got here then all the parities passed + // return bit count + return bit_count; +} + +size_t bit_lib_remove_bit_every_nth(uint8_t* data, size_t position, uint8_t length, uint8_t n) { + size_t counter = 0; + size_t result_counter = 0; + uint8_t bit_buffer = 0; + uint8_t bit_counter = 0; + + while(counter < length) { + if((counter + 1) % n != 0) { + bit_buffer = (bit_buffer << 1) | bit_lib_get_bit(data, position + counter); + bit_counter++; + } + + if(bit_counter == 8) { + bit_lib_set_bits(data, position + result_counter, bit_buffer, 8); + bit_counter = 0; + bit_buffer = 0; + result_counter += 8; + } + counter++; + } + + if(bit_counter != 0) { + bit_lib_set_bits(data, position + result_counter, bit_buffer, bit_counter); + result_counter += bit_counter; + } + return result_counter; +} + +void bit_lib_copy_bits( + uint8_t* data, + size_t position, + size_t length, + const uint8_t* source, + size_t source_position) { + for(size_t i = 0; i < length; ++i) { + bit_lib_set_bit(data, position + i, bit_lib_get_bit(source, source_position + i)); + } +} + +void bit_lib_reverse_bits(uint8_t* data, size_t position, uint8_t length) { + size_t i = 0; + size_t j = length - 1; + + while(i < j) { + bool tmp = bit_lib_get_bit(data, position + i); + bit_lib_set_bit(data, position + i, bit_lib_get_bit(data, position + j)); + bit_lib_set_bit(data, position + j, tmp); + i++; + j--; + } +} + +uint8_t bit_lib_get_bit_count(uint32_t data) { +#if defined __GNUC__ + return __builtin_popcountl(data); +#else +#error Please, implement popcount for non-GCC compilers +#endif +} + +void bit_lib_print_bits(const uint8_t* data, size_t length) { + for(size_t i = 0; i < length; ++i) { + printf("%u", bit_lib_get_bit(data, i)); + } +} + +void bit_lib_print_regions( + const BitLibRegion* regions, + size_t region_count, + const uint8_t* data, + size_t length) { + // print data + bit_lib_print_bits(data, length); + printf("\r\n"); + + // print regions + for(size_t c = 0; c < length; ++c) { + bool print = false; + + for(size_t i = 0; i < region_count; i++) { + if(regions[i].start <= c && c < regions[i].start + regions[i].length) { + print = true; + printf("%c", regions[i].mark); + break; + } + } + + if(!print) { + printf(" "); + } + } + printf("\r\n"); + + // print regions data + for(size_t c = 0; c < length; ++c) { + bool print = false; + + for(size_t i = 0; i < region_count; i++) { + if(regions[i].start <= c && c < regions[i].start + regions[i].length) { + print = true; + printf("%u", bit_lib_get_bit(data, c)); + break; + } + } + + if(!print) { + printf(" "); + } + } + printf("\r\n"); +} + +uint16_t bit_lib_reverse_16_fast(uint16_t data) { + uint16_t result = 0; + result |= (data & 0x8000) >> 15; + result |= (data & 0x4000) >> 13; + result |= (data & 0x2000) >> 11; + result |= (data & 0x1000) >> 9; + result |= (data & 0x0800) >> 7; + result |= (data & 0x0400) >> 5; + result |= (data & 0x0200) >> 3; + result |= (data & 0x0100) >> 1; + result |= (data & 0x0080) << 1; + result |= (data & 0x0040) << 3; + result |= (data & 0x0020) << 5; + result |= (data & 0x0010) << 7; + result |= (data & 0x0008) << 9; + result |= (data & 0x0004) << 11; + result |= (data & 0x0002) << 13; + result |= (data & 0x0001) << 15; + return result; +} + +uint8_t bit_lib_reverse_8_fast(uint8_t byte) { + byte = (byte & 0xF0) >> 4 | (byte & 0x0F) << 4; + byte = (byte & 0xCC) >> 2 | (byte & 0x33) << 2; + byte = (byte & 0xAA) >> 1 | (byte & 0x55) << 1; + return byte; +} + +uint16_t bit_lib_crc8( + uint8_t const* data, + size_t data_size, + uint8_t polynom, + uint8_t init, + bool ref_in, + bool ref_out, + uint8_t xor_out) { + uint8_t crc = init; + + for(size_t i = 0; i < data_size; ++i) { + uint8_t byte = data[i]; + if(ref_in) bit_lib_reverse_bits(&byte, 0, 8); + crc ^= byte; + + for(size_t j = 8; j > 0; --j) { + if(crc & TOPBIT(8)) { + crc = (crc << 1) ^ polynom; + } else { + crc = (crc << 1); + } + } + } + + if(ref_out) bit_lib_reverse_bits(&crc, 0, 8); + crc ^= xor_out; + + return crc; +} + +uint16_t bit_lib_crc16( + uint8_t const* data, + size_t data_size, + uint16_t polynom, + uint16_t init, + bool ref_in, + bool ref_out, + uint16_t xor_out) { + uint16_t crc = init; + + for(size_t i = 0; i < data_size; ++i) { + uint8_t byte = data[i]; + if(ref_in) byte = bit_lib_reverse_16_fast(byte) >> 8; + + for(size_t j = 0; j < 8; ++j) { + bool c15 = (crc >> 15 & 1); + bool bit = (byte >> (7 - j) & 1); + crc <<= 1; + if(c15 ^ bit) crc ^= polynom; + } + } + + if(ref_out) crc = bit_lib_reverse_16_fast(crc); + crc ^= xor_out; + + return crc; +} diff --git a/lib/lfrfid/tools/bit_lib.h b/lib/lfrfid/tools/bit_lib.h new file mode 100644 index 00000000000..bae95462d5b --- /dev/null +++ b/lib/lfrfid/tools/bit_lib.h @@ -0,0 +1,272 @@ +#pragma once +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define TOPBIT(X) (1 << ((X)-1)) + +typedef enum { + BitLibParityEven, + BitLibParityOdd, + BitLibParityAlways0, + BitLibParityAlways1, +} BitLibParity; + +/** @brief Increment and wrap around a value. + * @param index value to increment + * @param length wrap-around range + */ +#define bit_lib_increment_index(index, length) (index = (((index) + 1) % (length))) + +/** @brief Test if a bit is set. + * @param data value to test + * @param index bit index to test + */ +#define bit_lib_bit_is_set(data, index) (((data) & (1 << (index))) != 0) + +/** @brief Test if a bit is not set. + * @param data value to test + * @param index bit index to test + */ +#define bit_lib_bit_is_not_set(data, index) (((data) & (1 << (index))) == 0) + +/** @brief Push a bit into a byte array. + * @param data array to push bit into + * @param data_size array size + * @param bit bit to push + */ +void bit_lib_push_bit(uint8_t* data, size_t data_size, bool bit); + +/** @brief Set a bit in a byte array. + * @param data array to set bit in + * @param position The position of the bit to set. + * @param bit bit value to set + */ +void bit_lib_set_bit(uint8_t* data, size_t position, bool bit); + +/** @brief Set the bit at the given position to the given value. + * @param data The data to set the bit in. + * @param position The position of the bit to set. + * @param byte The data to set the bit to. + * @param length The length of the data. + */ +void bit_lib_set_bits(uint8_t* data, size_t position, uint8_t byte, uint8_t length); + +/** @brief Get the bit of a byte. + * @param data The byte to get the bits from. + * @param position The position of the bit. + * @return The bit. + */ +bool bit_lib_get_bit(const uint8_t* data, size_t position); + +/** + * @brief Get the bits of a data, as uint8_t. + * @param data The data to get the bits from. + * @param position The position of the first bit. + * @param length The length of the bits. + * @return The bits. + */ +uint8_t bit_lib_get_bits(const uint8_t* data, size_t position, uint8_t length); + +/** + * @brief Get the bits of a data, as uint16_t. + * @param data The data to get the bits from. + * @param position The position of the first bit. + * @param length The length of the bits. + * @return The bits. + */ +uint16_t bit_lib_get_bits_16(const uint8_t* data, size_t position, uint8_t length); + +/** + * @brief Get the bits of a data, as uint32_t. + * @param data The data to get the bits from. + * @param position The position of the first bit. + * @param length The length of the bits. + * @return The bits. + */ +uint32_t bit_lib_get_bits_32(const uint8_t* data, size_t position, uint8_t length); + +/** + * @brief Test parity of given bits + * @param bits Bits to test parity of + * @param parity Parity to test against + * @return true if parity is correct, false otherwise + */ +bool bit_lib_test_parity_32(uint32_t bits, BitLibParity parity); + +/** + * @brief Test parity of bit array, check parity for every parity_length block from start + * + * @param data Bit array + * @param position Start position + * @param length Bit count + * @param parity Parity to test against + * @param parity_length Parity block length + * @return true + * @return false + */ +bool bit_lib_test_parity( + const uint8_t* data, + size_t position, + uint8_t length, + BitLibParity parity, + uint8_t parity_length); + +/** + * @brief Add parity to bit array + * + * @param data Source bit array + * @param position Start position + * @param dest Destination bit array + * @param dest_position Destination position + * @param source_length Source bit count + * @param parity_length Parity block length + * @param parity Parity to test against + * @return size_t + */ +size_t bit_lib_add_parity( + const uint8_t* data, + size_t position, + uint8_t* dest, + size_t dest_position, + uint8_t source_length, + uint8_t parity_length, + BitLibParity parity); + +/** + * @brief Remove bit every n in array and shift array left. Useful to remove parity. + * + * @param data Bit array + * @param position Start position + * @param length Bit count + * @param n every n bit will be removed + * @return size_t + */ +size_t bit_lib_remove_bit_every_nth(uint8_t* data, size_t position, uint8_t length, uint8_t n); + +/** + * @brief Copy bits from source to destination. + * + * @param data destination array + * @param position position in destination array + * @param length length of bits to copy + * @param source source array + * @param source_position position in source array + */ +void bit_lib_copy_bits( + uint8_t* data, + size_t position, + size_t length, + const uint8_t* source, + size_t source_position); + +/** + * @brief Reverse bits in bit array + * + * @param data Bit array + * @param position start position + * @param length length of bits to reverse + */ +void bit_lib_reverse_bits(uint8_t* data, size_t position, uint8_t length); + +/** + * @brief Count 1 bits in data + * + * @param data + * @return uint8_t set bit count + */ +uint8_t bit_lib_get_bit_count(uint32_t data); + +/** + * @brief Print data as bit array + * + * @param data + * @param length + */ +void bit_lib_print_bits(const uint8_t* data, size_t length); + +typedef struct { + const char mark; + const size_t start; + const size_t length; +} BitLibRegion; + +/** + * @brief Print data as bit array and mark regions. Regions needs to be sorted by start position. + * + * @param regions + * @param region_count + * @param data + * @param length + */ +void bit_lib_print_regions( + const BitLibRegion* regions, + size_t region_count, + const uint8_t* data, + size_t length); + +/** + * @brief Reverse bits in uint16_t, faster than generic bit_lib_reverse_bits. + * + * @param data + * @return uint16_t + */ +uint16_t bit_lib_reverse_16_fast(uint16_t data); + +/** + * @brief Reverse bits in uint8_t, faster than generic bit_lib_reverse_bits. + * + * @param byte Byte + * @return uint8_t the reversed byte + */ +uint8_t bit_lib_reverse_8_fast(uint8_t byte); + +/** + * @brief Slow, but generic CRC8 implementation + * + * @param data + * @param data_size + * @param polynom CRC polynom + * @param init init value + * @param ref_in true if the right bit is older + * @param ref_out true to reverse output + * @param xor_out xor output with this value + * @return uint8_t + */ +uint16_t bit_lib_crc8( + uint8_t const* data, + size_t data_size, + uint8_t polynom, + uint8_t init, + bool ref_in, + bool ref_out, + uint8_t xor_out); + +/** + * @brief Slow, but generic CRC16 implementation + * + * @param data + * @param data_size + * @param polynom CRC polynom + * @param init init value + * @param ref_in true if the right bit is older + * @param ref_out true to reverse output + * @param xor_out xor output with this value + * @return uint16_t + */ +uint16_t bit_lib_crc16( + uint8_t const* data, + size_t data_size, + uint16_t polynom, + uint16_t init, + bool ref_in, + bool ref_out, + uint16_t xor_out); + +#ifdef __cplusplus +} +#endif diff --git a/lib/lfrfid/tools/fsk_demod.c b/lib/lfrfid/tools/fsk_demod.c new file mode 100644 index 00000000000..a5155d88e51 --- /dev/null +++ b/lib/lfrfid/tools/fsk_demod.c @@ -0,0 +1,93 @@ +#include +#include "fsk_demod.h" + +struct FSKDemod { + uint32_t low_time; + uint32_t low_pulses; + uint32_t hi_time; + uint32_t hi_pulses; + + bool invert; + uint32_t mid_time; + uint32_t time; + uint32_t count; + bool last_pulse; +}; + +FSKDemod* + fsk_demod_alloc(uint32_t low_time, uint32_t low_pulses, uint32_t hi_time, uint32_t hi_pulses) { + FSKDemod* demod = malloc(sizeof(FSKDemod)); + demod->invert = false; + + if(low_time > hi_time) { + uint32_t tmp; + tmp = hi_time; + hi_time = low_time; + low_time = tmp; + + tmp = hi_pulses; + hi_pulses = low_pulses; + low_pulses = tmp; + + demod->invert = true; + } + + demod->low_time = low_time; + demod->low_pulses = low_pulses; + demod->hi_time = hi_time; + demod->hi_pulses = hi_pulses; + + demod->mid_time = (hi_time - low_time) / 2 + low_time; + demod->time = 0; + demod->count = 0; + demod->last_pulse = false; + + return demod; +} + +void fsk_demod_free(FSKDemod* demod) { + free(demod); +} + +void fsk_demod_feed(FSKDemod* demod, bool polarity, uint32_t time, bool* value, uint32_t* count) { + *count = 0; + + if(polarity) { + // accumulate time + demod->time = time; + } else { + demod->time += time; + + // check for valid pulse + if(demod->time >= demod->low_time && demod->time < demod->hi_time) { + bool pulse; + + if(demod->time < demod->mid_time) { + pulse = false; + } else { + pulse = true; + } + + demod->count++; + + // check for edge transition + if(demod->last_pulse != pulse) { + uint32_t data_count = demod->count + 1; + + if(demod->last_pulse) { + data_count /= demod->hi_pulses; + *value = !demod->invert; + } else { + data_count /= demod->low_pulses; + *value = demod->invert; + } + + *count = data_count; + demod->count = 0; + demod->last_pulse = pulse; + } + } else { + demod->count = 0; + } + } +} diff --git a/lib/lfrfid/tools/fsk_demod.h b/lib/lfrfid/tools/fsk_demod.h new file mode 100644 index 00000000000..d816b0dac1e --- /dev/null +++ b/lib/lfrfid/tools/fsk_demod.h @@ -0,0 +1,44 @@ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct FSKDemod FSKDemod; + +/** + * @brief Allocate a new FSKDemod instance + * FSKDemod is a demodulator that can decode FSK encoded data + * + * @param low_time time between rising edges for the 0 bit + * @param low_pulses rising edges count for the 0 bit + * @param hi_time time between rising edges for the 1 bit + * @param hi_pulses rising edges count for the 1 bit + * @return FSKDemod* + */ +FSKDemod* + fsk_demod_alloc(uint32_t low_time, uint32_t low_pulses, uint32_t hi_time, uint32_t hi_pulses); + +/** + * @brief Free a FSKDemod instance + * + * @param fsk_demod + */ +void fsk_demod_free(FSKDemod* fsk_demod); + +/** + * @brief Feed sample to demodulator + * + * @param demod FSKDemod instance + * @param polarity sample polarity + * @param time sample time + * @param value demodulated bit value + * @param count demodulated bit count + */ +void fsk_demod_feed(FSKDemod* demod, bool polarity, uint32_t time, bool* value, uint32_t* count); + +#ifdef __cplusplus +} +#endif diff --git a/lib/lfrfid/tools/fsk_ocs.c b/lib/lfrfid/tools/fsk_ocs.c new file mode 100644 index 00000000000..1fd46cf1793 --- /dev/null +++ b/lib/lfrfid/tools/fsk_ocs.c @@ -0,0 +1,62 @@ +#include "fsk_osc.h" +#include + +struct FSKOsc { + uint16_t freq[2]; + uint16_t osc_phase_max; + int32_t osc_phase_current; + + uint32_t pulse; +}; + +FSKOsc* fsk_osc_alloc(uint32_t freq_low, uint32_t freq_hi, uint32_t osc_phase_max) { + FSKOsc* osc = malloc(sizeof(FSKOsc)); + osc->freq[0] = freq_low; + osc->freq[1] = freq_hi; + osc->osc_phase_max = osc_phase_max; + osc->osc_phase_current = 0; + osc->pulse = 0; + return osc; +} + +void fsk_osc_free(FSKOsc* osc) { + free(osc); +} + +void fsk_osc_reset(FSKOsc* osc) { + osc->osc_phase_current = 0; + osc->pulse = 0; +} + +bool fsk_osc_next(FSKOsc* osc, bool bit, uint32_t* period) { + bool advance = false; + *period = osc->freq[bit]; + osc->osc_phase_current += *period; + + if(osc->osc_phase_current > osc->osc_phase_max) { + advance = true; + osc->osc_phase_current -= osc->osc_phase_max; + } + + return advance; +} + +bool fsk_osc_next_half(FSKOsc* osc, bool bit, bool* level, uint32_t* duration) { + bool advance = false; + + // if pulse is zero, we need to output high, otherwise we need to output low + if(osc->pulse == 0) { + uint32_t length; + advance = fsk_osc_next(osc, bit, &length); + *duration = length / 2; + osc->pulse = *duration; + *level = true; + } else { + // output low half and reset pulse + *duration = osc->pulse; + osc->pulse = 0; + *level = false; + } + + return advance; +} \ No newline at end of file diff --git a/lib/lfrfid/tools/fsk_osc.h b/lib/lfrfid/tools/fsk_osc.h new file mode 100644 index 00000000000..ed7d436c855 --- /dev/null +++ b/lib/lfrfid/tools/fsk_osc.h @@ -0,0 +1,60 @@ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct FSKOsc FSKOsc; + +/** + * @brief Allocate a new FSKOsc instance + * FSKOsc is a oscillator that can provide FSK encoding + * + * @param freq_low + * @param freq_hi + * @param osc_phase_max + * @return FSKOsc* + */ +FSKOsc* fsk_osc_alloc(uint32_t freq_low, uint32_t freq_hi, uint32_t osc_phase_max); + +/** + * @brief Free a FSKOsc instance + * + * @param osc + */ +void fsk_osc_free(FSKOsc* osc); + +/** + * @brief Reset ocillator + * + * @param osc + */ +void fsk_osc_reset(FSKOsc* osc); + +/** + * @brief Get next duration sample from oscillator + * + * @param osc + * @param bit + * @param period + * @return bool + */ +bool fsk_osc_next(FSKOsc* osc, bool bit, uint32_t* period); + +/** + * @brief Get next half of sample from oscillator + * Useful when encoding high and low levels separately. + * + * @param osc + * @param bit + * @param level + * @param duration + * @return bool + */ +bool fsk_osc_next_half(FSKOsc* osc, bool bit, bool* level, uint32_t* duration); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/lfrfid/tools/t5577.c b/lib/lfrfid/tools/t5577.c new file mode 100644 index 00000000000..00c9ddfb627 --- /dev/null +++ b/lib/lfrfid/tools/t5577.c @@ -0,0 +1,91 @@ +#include "t5577.h" +#include +#include + +#define T5577_TIMING_WAIT_TIME 400 +#define T5577_TIMING_START_GAP 30 +#define T5577_TIMING_WRITE_GAP 18 +#define T5577_TIMING_DATA_0 24 +#define T5577_TIMING_DATA_1 56 +#define T5577_TIMING_PROGRAM 700 + +#define T5577_OPCODE_PAGE_0 0b10 +#define T5577_OPCODE_PAGE_1 0b11 +#define T5577_OPCODE_RESET 0b00 + +static void t5577_start() { + furi_hal_rfid_tim_read_start(125000, 0.5); + + // do not ground the antenna + furi_hal_rfid_pin_pull_release(); +} + +static void t5577_stop() { + furi_hal_rfid_tim_read_stop(); + furi_hal_rfid_pins_reset(); +} + +static void t5577_write_gap(uint32_t gap_time) { + furi_hal_rfid_tim_read_pause(); + furi_delay_us(gap_time * 8); + furi_hal_rfid_tim_read_continue(); +} + +static void t5577_write_bit(bool value) { + if(value) { + furi_delay_us(T5577_TIMING_DATA_1 * 8); + } else { + furi_delay_us(T5577_TIMING_DATA_0 * 8); + } + t5577_write_gap(T5577_TIMING_WRITE_GAP); +} + +static void t5577_write_opcode(uint8_t value) { + t5577_write_bit((value >> 1) & 1); + t5577_write_bit((value >> 0) & 1); +} + +static void t5577_write_reset() { + t5577_write_gap(T5577_TIMING_START_GAP); + t5577_write_bit(1); + t5577_write_bit(0); +} + +static void t5577_write_block(uint8_t block, bool lock_bit, uint32_t data) { + furi_delay_us(T5577_TIMING_WAIT_TIME * 8); + + // start gap + t5577_write_gap(T5577_TIMING_START_GAP); + + // opcode for page 0 + t5577_write_opcode(T5577_OPCODE_PAGE_0); + + // lock bit + t5577_write_bit(lock_bit); + + // data + for(uint8_t i = 0; i < 32; i++) { + t5577_write_bit((data >> (31 - i)) & 1); + } + + // block address + t5577_write_bit((block >> 2) & 1); + t5577_write_bit((block >> 1) & 1); + t5577_write_bit((block >> 0) & 1); + + furi_delay_us(T5577_TIMING_PROGRAM * 8); + + furi_delay_us(T5577_TIMING_WAIT_TIME * 8); + t5577_write_reset(); +} + +void t5577_write(LFRFIDT5577* data) { + t5577_start(); + FURI_CRITICAL_ENTER(); + for(size_t i = 0; i < data->blocks_to_write; i++) { + t5577_write_block(i, false, data->block[i]); + } + t5577_write_reset(); + FURI_CRITICAL_EXIT(); + t5577_stop(); +} \ No newline at end of file diff --git a/lib/lfrfid/tools/t5577.h b/lib/lfrfid/tools/t5577.h new file mode 100644 index 00000000000..6d53b5dc74b --- /dev/null +++ b/lib/lfrfid/tools/t5577.h @@ -0,0 +1,56 @@ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define LFRFID_T5577_BLOCK_COUNT 8 + +// T5577 block 0 definitions, thanks proxmark3! +#define LFRFID_T5577_POR_DELAY 0x00000001 +#define LFRFID_T5577_ST_TERMINATOR 0x00000008 +#define LFRFID_T5577_PWD 0x00000010 +#define LFRFID_T5577_MAXBLOCK_SHIFT 5 +#define LFRFID_T5577_AOR 0x00000200 +#define LFRFID_T5577_PSKCF_RF_2 0 +#define LFRFID_T5577_PSKCF_RF_4 0x00000400 +#define LFRFID_T5577_PSKCF_RF_8 0x00000800 +#define LFRFID_T5577_MODULATION_DIRECT 0 +#define LFRFID_T5577_MODULATION_PSK1 0x00001000 +#define LFRFID_T5577_MODULATION_PSK2 0x00002000 +#define LFRFID_T5577_MODULATION_PSK3 0x00003000 +#define LFRFID_T5577_MODULATION_FSK1 0x00004000 +#define LFRFID_T5577_MODULATION_FSK2 0x00005000 +#define LFRFID_T5577_MODULATION_FSK1a 0x00006000 +#define LFRFID_T5577_MODULATION_FSK2a 0x00007000 +#define LFRFID_T5577_MODULATION_MANCHESTER 0x00008000 +#define LFRFID_T5577_MODULATION_BIPHASE 0x00010000 +#define LFRFID_T5577_MODULATION_DIPHASE 0x00018000 +#define LFRFID_T5577_X_MODE 0x00020000 +#define LFRFID_T5577_BITRATE_RF_8 0 +#define LFRFID_T5577_BITRATE_RF_16 0x00040000 +#define LFRFID_T5577_BITRATE_RF_32 0x00080000 +#define LFRFID_T5577_BITRATE_RF_40 0x000C0000 +#define LFRFID_T5577_BITRATE_RF_50 0x00100000 +#define LFRFID_T5577_BITRATE_RF_64 0x00140000 +#define LFRFID_T5577_BITRATE_RF_100 0x00180000 +#define LFRFID_T5577_BITRATE_RF_128 0x001C0000 +#define LFRFID_T5577_TESTMODE_DISABLED 0x60000000 + +typedef struct { + uint32_t block[LFRFID_T5577_BLOCK_COUNT]; + uint32_t blocks_to_write; +} LFRFIDT5577; + +/** + * @brief Write T5577 tag data to tag + * + * @param data + */ +void t5577_write(LFRFIDT5577* data); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/lfrfid/tools/varint_pair.c b/lib/lfrfid/tools/varint_pair.c new file mode 100644 index 00000000000..1e6c82eeea4 --- /dev/null +++ b/lib/lfrfid/tools/varint_pair.c @@ -0,0 +1,75 @@ +#include "varint_pair.h" +#include + +#define VARINT_PAIR_SIZE 10 + +struct VarintPair { + size_t data_length; + uint8_t data[VARINT_PAIR_SIZE]; +}; + +VarintPair* varint_pair_alloc() { + VarintPair* pair = malloc(sizeof(VarintPair)); + pair->data_length = 0; + return pair; +} + +void varint_pair_free(VarintPair* pair) { + free(pair); +} + +bool varint_pair_pack(VarintPair* pair, bool first, uint32_t value) { + bool result = false; + + if(first) { + if(pair->data_length == 0) { + pair->data_length = varint_uint32_pack(value, pair->data); + } else { + pair->data_length = 0; + } + } else { + if(pair->data_length != 0) { + pair->data_length += varint_uint32_pack(value, pair->data + pair->data_length); + result = true; + } + } + + return result; +} + +bool varint_pair_unpack( + uint8_t* data, + size_t data_length, + uint32_t* value_1, + uint32_t* value_2, + size_t* length) { + size_t size = 0; + uint32_t tmp_value_1; + uint32_t tmp_value_2; + + size += varint_uint32_unpack(&tmp_value_1, &data[size], data_length); + + if(size >= data_length) { + return false; + } + + size += varint_uint32_unpack(&tmp_value_2, &data[size], (size_t)(data_length - size)); + + *value_1 = tmp_value_1; + *value_2 = tmp_value_2; + *length = size; + + return true; +} + +uint8_t* varint_pair_get_data(VarintPair* pair) { + return pair->data; +} + +size_t varint_pair_get_size(VarintPair* pair) { + return pair->data_length; +} + +void varint_pair_reset(VarintPair* pair) { + pair->data_length = 0; +} diff --git a/lib/lfrfid/tools/varint_pair.h b/lib/lfrfid/tools/varint_pair.h new file mode 100644 index 00000000000..3c8386423e4 --- /dev/null +++ b/lib/lfrfid/tools/varint_pair.h @@ -0,0 +1,79 @@ +#pragma once +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct VarintPair VarintPair; + +/** + * @brief Allocate a new VarintPair instance + * + * VarintPair is a buffer that holds pair of varint values + * @return VarintPair* + */ +VarintPair* varint_pair_alloc(); + +/** + * @brief Free a VarintPair instance + * + * @param pair + */ +void varint_pair_free(VarintPair* pair); + +/** + * @brief Write varint pair to buffer + * + * @param pair + * @param first + * @param value + * @return bool pair complete and needs to be written + */ +bool varint_pair_pack(VarintPair* pair, bool first, uint32_t value); + +/** + * @brief Get pointer to varint pair buffer + * + * @param pair + * @return uint8_t* + */ +uint8_t* varint_pair_get_data(VarintPair* pair); + +/** + * @brief Get size of varint pair buffer + * + * @param pair + * @return size_t + */ +size_t varint_pair_get_size(VarintPair* pair); + +/** + * @brief Reset varint pair buffer + * + * @param pair + */ +void varint_pair_reset(VarintPair* pair); + +/** + * @brief Unpack varint pair to uint32_t pair from buffer + * + * @param data + * @param data_length + * @param value_1 + * @param value_2 + * @param length + * @return bool + */ +bool varint_pair_unpack( + uint8_t* data, + size_t data_length, + uint32_t* value_1, + uint32_t* value_2, + size_t* length); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/lfs_config.h b/lib/lfs_config.h index 59b3c486e33..ff8bc4b235f 100644 --- a/lib/lfs_config.h +++ b/lib/lfs_config.h @@ -5,21 +5,26 @@ #ifdef FURI_NDEBUG #define LFS_NO_ASSERT #define LFS_ASSERT(x) -#else +#else #define LFS_ASSERT furi_assert #endif #define LFS_TAG "Lfs" +#ifdef FURI_LFS_DEBUG #define LFS_TRACE(...) FURI_LOG_T(LFS_TAG, __VA_ARGS__); #define LFS_DEBUG(...) FURI_LOG_D(LFS_TAG, __VA_ARGS__); +#else +#define LFS_TRACE(...) + +#define LFS_DEBUG(...) +#endif // FURI_LFS_DEBUG #define LFS_WARN(...) FURI_LOG_W(LFS_TAG, __VA_ARGS__); #define LFS_ERROR(...) FURI_LOG_E(LFS_TAG, __VA_ARGS__); - // Because crc #undef LFS_CONFIG @@ -35,16 +40,13 @@ #ifndef LFS_NO_ASSERT #include #endif -#if !defined(LFS_NO_DEBUG) || \ - !defined(LFS_NO_WARN) || \ - !defined(LFS_NO_ERROR) || \ - defined(LFS_YES_TRACE) +#if !defined(LFS_NO_DEBUG) || !defined(LFS_NO_WARN) || !defined(LFS_NO_ERROR) || \ + defined(LFS_YES_TRACE) #include #endif #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif // Builtin functions, these may be replaced by more efficient @@ -66,21 +68,29 @@ static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) { } static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) { - return lfs_aligndown(a + alignment-1, alignment); + return lfs_aligndown(a + alignment - 1, alignment); } // Find the smallest power of 2 greater than or equal to a static inline uint32_t lfs_npw2(uint32_t a) { #if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) - return 32 - __builtin_clz(a-1); + return 32 - __builtin_clz(a - 1); #else uint32_t r = 0; uint32_t s; a -= 1; - s = (a > 0xffff) << 4; a >>= s; r |= s; - s = (a > 0xff ) << 3; a >>= s; r |= s; - s = (a > 0xf ) << 2; a >>= s; r |= s; - s = (a > 0x3 ) << 1; a >>= s; r |= s; + s = (a > 0xffff) << 4; + a >>= s; + r |= s; + s = (a > 0xff) << 3; + a >>= s; + r |= s; + s = (a > 0xf) << 2; + a >>= s; + r |= s; + s = (a > 0x3) << 1; + a >>= s; + r |= s; return (r | (a >> 1)) + 1; #endif } @@ -114,20 +124,23 @@ static inline int lfs_scmp(uint32_t a, uint32_t b) { // Convert between 32-bit little-endian and native order static inline uint32_t lfs_fromle32(uint32_t a) { -#if !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) +#if !defined(LFS_NO_INTRINSICS) && \ + ((defined(BYTE_ORDER) && defined(ORDER_LITTLE_ENDIAN) && \ + BYTE_ORDER == ORDER_LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER) && defined(__ORDER_LITTLE_ENDIAN) && \ + __BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) return a; -#elif !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) +#elif !defined(LFS_NO_INTRINSICS) && \ + ((defined(BYTE_ORDER) && defined(ORDER_BIG_ENDIAN) && BYTE_ORDER == ORDER_BIG_ENDIAN) || \ + (defined(__BYTE_ORDER) && defined(__ORDER_BIG_ENDIAN) && \ + __BYTE_ORDER == __ORDER_BIG_ENDIAN) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) return __builtin_bswap32(a); #else - return (((uint8_t*)&a)[0] << 0) | - (((uint8_t*)&a)[1] << 8) | - (((uint8_t*)&a)[2] << 16) | + return (((uint8_t*)&a)[0] << 0) | (((uint8_t*)&a)[1] << 8) | (((uint8_t*)&a)[2] << 16) | (((uint8_t*)&a)[3] << 24); #endif } @@ -138,21 +151,24 @@ static inline uint32_t lfs_tole32(uint32_t a) { // Convert between 32-bit big-endian and native order static inline uint32_t lfs_frombe32(uint32_t a) { -#if !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) +#if !defined(LFS_NO_INTRINSICS) && \ + ((defined(BYTE_ORDER) && defined(ORDER_LITTLE_ENDIAN) && \ + BYTE_ORDER == ORDER_LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER) && defined(__ORDER_LITTLE_ENDIAN) && \ + __BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) return __builtin_bswap32(a); -#elif !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) +#elif !defined(LFS_NO_INTRINSICS) && \ + ((defined(BYTE_ORDER) && defined(ORDER_BIG_ENDIAN) && BYTE_ORDER == ORDER_BIG_ENDIAN) || \ + (defined(__BYTE_ORDER) && defined(__ORDER_BIG_ENDIAN) && \ + __BYTE_ORDER == __ORDER_BIG_ENDIAN) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) return a; #else - return (((uint8_t*)&a)[0] << 24) | - (((uint8_t*)&a)[1] << 16) | - (((uint8_t*)&a)[2] << 8) | - (((uint8_t*)&a)[3] << 0); + return (((uint8_t*)&a)[0] << 24) | (((uint8_t*)&a)[1] << 16) | (((uint8_t*)&a)[2] << 8) | + (((uint8_t*)&a)[3] << 0); #endif } @@ -161,11 +177,11 @@ static inline uint32_t lfs_tobe32(uint32_t a) { } // Calculate CRC-32 with polynomial = 0x04c11db7 -uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size); +uint32_t lfs_crc(uint32_t crc, const void* buffer, size_t size); // Allocate memory, only used if buffers are not provided to littlefs // Note, memory must be 64-bit aligned -static inline void *lfs_malloc(size_t size) { +static inline void* lfs_malloc(size_t size) { #ifndef LFS_NO_MALLOC return malloc(size); #else @@ -175,7 +191,7 @@ static inline void *lfs_malloc(size_t size) { } // Deallocate memory, only used if buffers are not provided to littlefs -static inline void lfs_free(void *p) { +static inline void lfs_free(void* p) { #ifndef LFS_NO_MALLOC free(p); #else @@ -183,7 +199,6 @@ static inline void lfs_free(void *p) { #endif } - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/lib/libusb_stm32 b/lib/libusb_stm32 index 6a88ec4d770..6ca2857519f 160000 --- a/lib/libusb_stm32 +++ b/lib/libusb_stm32 @@ -1 +1 @@ -Subproject commit 6a88ec4d7709ca8605b5ec3e609057c330ca2a70 +Subproject commit 6ca2857519f996244f7b324dd227fdf0a075fffb diff --git a/lib/libusb_stm32.scons b/lib/libusb_stm32.scons index cb867fdbc9c..ccc5de24f09 100644 --- a/lib/libusb_stm32.scons +++ b/lib/libusb_stm32.scons @@ -4,14 +4,20 @@ env.Append( CPPPATH=[ "#/lib/libusb_stm32/inc", ], - CPPDEFINES=[ - ("USB_PMASIZE", "0x400"), - ], + SDK_HEADERS=env.GlobRecursive( + "*.h", + Dir("libusb_stm32/inc"), + ), ) libenv = env.Clone(FW_LIB_NAME="usb_stm32") libenv.ApplyLibFlags() +libenv.Append( + CPPDEFINES=[ + ("USB_PMASIZE", "0x400"), + ], +) sources = [ diff --git a/lib/littlefs b/lib/littlefs index 1863dc7883d..611c9b20db2 160000 --- a/lib/littlefs +++ b/lib/littlefs @@ -1 +1 @@ -Subproject commit 1863dc7883d82bd6ca79faa164b65341064d1c16 +Subproject commit 611c9b20db2b99faee261daa7cc9bbe175d3eaca diff --git a/lib/littlefs.scons b/lib/littlefs.scons index 792142c32f0..3d68e07bae8 100644 --- a/lib/littlefs.scons +++ b/lib/littlefs.scons @@ -4,14 +4,16 @@ env.Append( CPPPATH=[ "#/lib/littlefs", ], - CPPDEFINES=[ - ("LFS_CONFIG", "lfs_config.h"), - ], ) libenv = env.Clone(FW_LIB_NAME="littlefs") libenv.ApplyLibFlags() +libenv.Append( + CPPDEFINES=[ + ("LFS_CONFIG", "lfs_config.h"), + ], +) sources = Glob("littlefs/*.c", source=True) diff --git a/lib/loclass.scons b/lib/loclass.scons deleted file mode 100644 index a657f0424c2..00000000000 --- a/lib/loclass.scons +++ /dev/null @@ -1,17 +0,0 @@ -Import("env") - -env.Append( - CPPPATH=[ - "#/lib/loclass", - ], -) - - -libenv = env.Clone(FW_LIB_NAME="loclass") -libenv.ApplyLibFlags() - -sources = Glob("loclass/*.c", source=True) - -lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) -libenv.Install("${LIB_DIST_DIR}", lib) -Return("lib") diff --git a/lib/loclass/optimized_cipher.c b/lib/loclass/optimized_cipher.c deleted file mode 100644 index e4f6a58c355..00000000000 --- a/lib/loclass/optimized_cipher.c +++ /dev/null @@ -1,313 +0,0 @@ -//----------------------------------------------------------------------------- -// Borrowed initially from https://github.com/holiman/loclass -// Copyright (C) 2014 Martin Holst Swende -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// 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. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// WARNING -// -// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. -// -// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL -// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, -// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. -// -// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. -//----------------------------------------------------------------------------- -// It is a reconstruction of the cipher engine used in iClass, and RFID techology. -// -// The implementation is based on the work performed by -// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and -// Milosch Meriac in the paper "Dismantling IClass". -//----------------------------------------------------------------------------- -/* - This file contains an optimized version of the MAC-calculation algorithm. Some measurements on - a std laptop showed it runs in about 1/3 of the time: - - Std: 0.428962 - Opt: 0.151609 - - Additionally, it is self-reliant, not requiring e.g. bitstreams from the cipherutils, thus can - be easily dropped into a code base. - - The optimizations have been performed in the following steps: - * Parameters passed by reference instead of by value. - * Iteration instead of recursion, un-nesting recursive loops into for-loops. - * Handling of bytes instead of individual bits, for less shuffling and masking - * Less creation of "objects", structs, and instead reuse of alloc:ed memory - * Inlining some functions via #define:s - - As a consequence, this implementation is less generic. Also, I haven't bothered documenting this. - For a thorough documentation, check out the MAC-calculation within cipher.c instead. - - -- MHS 2015 -**/ - -/** - - The runtime of opt_doTagMAC_2() with the MHS optimized version was 403 microseconds on Proxmark3. - This was still to slow for some newer readers which didn't want to wait that long. - - Further optimizations to speedup the MAC calculations: - * Optimized opt_Tt logic - * Look up table for opt_select - * Removing many unnecessary bit maskings (& 0x1) - * updating state in place instead of alternating use of a second state structure - * remove the necessity to reverse bits of input and output bytes - - opt_doTagMAC_2() now completes in 270 microseconds. - - -- piwi 2019 -**/ - -/** - add the possibility to do iCLASS on device only - -- iceman 2020 -**/ - -#include "optimized_cipher.h" -#include "optimized_elite.h" -#include "optimized_ikeys.h" -#include "optimized_cipherutils.h" - -static const uint8_t loclass_opt_select_LUT[256] = { - 00, 03, 02, 01, 02, 03, 00, 01, 04, 07, 07, 04, 06, 07, 05, 04, - 01, 02, 03, 00, 02, 03, 00, 01, 05, 06, 06, 05, 06, 07, 05, 04, - 06, 05, 04, 07, 04, 05, 06, 07, 06, 05, 05, 06, 04, 05, 07, 06, - 07, 04, 05, 06, 04, 05, 06, 07, 07, 04, 04, 07, 04, 05, 07, 06, - 06, 05, 04, 07, 04, 05, 06, 07, 02, 01, 01, 02, 00, 01, 03, 02, - 03, 00, 01, 02, 00, 01, 02, 03, 07, 04, 04, 07, 04, 05, 07, 06, - 00, 03, 02, 01, 02, 03, 00, 01, 00, 03, 03, 00, 02, 03, 01, 00, - 05, 06, 07, 04, 06, 07, 04, 05, 05, 06, 06, 05, 06, 07, 05, 04, - 02, 01, 00, 03, 00, 01, 02, 03, 06, 05, 05, 06, 04, 05, 07, 06, - 03, 00, 01, 02, 00, 01, 02, 03, 07, 04, 04, 07, 04, 05, 07, 06, - 02, 01, 00, 03, 00, 01, 02, 03, 02, 01, 01, 02, 00, 01, 03, 02, - 03, 00, 01, 02, 00, 01, 02, 03, 03, 00, 00, 03, 00, 01, 03, 02, - 04, 07, 06, 05, 06, 07, 04, 05, 00, 03, 03, 00, 02, 03, 01, 00, - 01, 02, 03, 00, 02, 03, 00, 01, 05, 06, 06, 05, 06, 07, 05, 04, - 04, 07, 06, 05, 06, 07, 04, 05, 04, 07, 07, 04, 06, 07, 05, 04, - 01, 02, 03, 00, 02, 03, 00, 01, 01, 02, 02, 01, 02, 03, 01, 00 -}; - -/********************** the table above has been generated with this code: ******** -#include "util.h" -static void init_opt_select_LUT(void) { - for (int r = 0; r < 256; r++) { - uint8_t r_ls2 = r << 2; - uint8_t r_and_ls2 = r & r_ls2; - uint8_t r_or_ls2 = r | r_ls2; - uint8_t z0 = (r_and_ls2 >> 5) ^ ((r & ~r_ls2) >> 4) ^ ( r_or_ls2 >> 3); - uint8_t z1 = (r_or_ls2 >> 6) ^ ( r_or_ls2 >> 1) ^ (r >> 5) ^ r; - uint8_t z2 = ((r & ~r_ls2) >> 4) ^ (r_and_ls2 >> 3) ^ r; - loclass_opt_select_LUT[r] = (z0 & 4) | (z1 & 2) | (z2 & 1); - } - print_result("", loclass_opt_select_LUT, 256); -} -***********************************************************************************/ - -#define loclass_opt__select(x,y,r) (4 & (((r & (r << 2)) >> 5) ^ ((r & ~(r << 2)) >> 4) ^ ( (r | r << 2) >> 3)))\ - |(2 & (((r | r << 2) >> 6) ^ ( (r | r << 2) >> 1) ^ (r >> 5) ^ r ^ ((x^y) << 1)))\ - |(1 & (((r & ~(r << 2)) >> 4) ^ ((r & (r << 2)) >> 3) ^ r ^ x)) - - -static void loclass_opt_successor(const uint8_t *k, LoclassState_t *s, uint8_t y) { - uint16_t Tt = s->t & 0xc533; - Tt = Tt ^ (Tt >> 1); - Tt = Tt ^ (Tt >> 4); - Tt = Tt ^ (Tt >> 10); - Tt = Tt ^ (Tt >> 8); - - s->t = (s->t >> 1); - s->t |= (Tt ^ (s->r >> 7) ^ (s->r >> 3)) << 15; - - uint8_t opt_B = s->b; - opt_B ^= s->b >> 6; - opt_B ^= s->b >> 5; - opt_B ^= s->b >> 4; - - s->b = s->b >> 1; - s->b |= (opt_B ^ s->r) << 7; - - uint8_t opt_select = loclass_opt_select_LUT[s->r] & 0x04; - opt_select |= (loclass_opt_select_LUT[s->r] ^ ((Tt ^ y) << 1)) & 0x02; - opt_select |= (loclass_opt_select_LUT[s->r] ^ Tt) & 0x01; - - uint8_t r = s->r; - s->r = (k[opt_select] ^ s->b) + s->l ; - s->l = s->r + r; -} - -static void loclass_opt_suc(const uint8_t *k, LoclassState_t *s, const uint8_t *in, uint8_t length, bool add32Zeroes) { - for (int i = 0; i < length; i++) { - uint8_t head; - head = in[i]; - loclass_opt_successor(k, s, head); - - head >>= 1; - loclass_opt_successor(k, s, head); - - head >>= 1; - loclass_opt_successor(k, s, head); - - head >>= 1; - loclass_opt_successor(k, s, head); - - head >>= 1; - loclass_opt_successor(k, s, head); - - head >>= 1; - loclass_opt_successor(k, s, head); - - head >>= 1; - loclass_opt_successor(k, s, head); - - head >>= 1; - loclass_opt_successor(k, s, head); - } - //For tag MAC, an additional 32 zeroes - if (add32Zeroes) { - for (int i = 0; i < 16; i++) { - loclass_opt_successor(k, s, 0); - loclass_opt_successor(k, s, 0); - } - } -} - -static void loclass_opt_output(const uint8_t *k, LoclassState_t *s, uint8_t *buffer) { - for (uint8_t times = 0; times < 4; times++) { - uint8_t bout = 0; - bout |= (s->r & 0x4) >> 2; - loclass_opt_successor(k, s, 0); - bout |= (s->r & 0x4) >> 1; - loclass_opt_successor(k, s, 0); - bout |= (s->r & 0x4); - loclass_opt_successor(k, s, 0); - bout |= (s->r & 0x4) << 1; - loclass_opt_successor(k, s, 0); - bout |= (s->r & 0x4) << 2; - loclass_opt_successor(k, s, 0); - bout |= (s->r & 0x4) << 3; - loclass_opt_successor(k, s, 0); - bout |= (s->r & 0x4) << 4; - loclass_opt_successor(k, s, 0); - bout |= (s->r & 0x4) << 5; - loclass_opt_successor(k, s, 0); - buffer[times] = bout; - } -} - -static void loclass_opt_MAC(uint8_t *k, uint8_t *input, uint8_t *out) { - LoclassState_t _init = { - ((k[0] ^ 0x4c) + 0xEC) & 0xFF,// l - ((k[0] ^ 0x4c) + 0x21) & 0xFF,// r - 0x4c, // b - 0xE012 // t - }; - - loclass_opt_suc(k, &_init, input, 12, false); - loclass_opt_output(k, &_init, out); -} - -static void loclass_opt_MAC_N(uint8_t *k, uint8_t *input, uint8_t in_size, uint8_t *out) { - LoclassState_t _init = { - ((k[0] ^ 0x4c) + 0xEC) & 0xFF,// l - ((k[0] ^ 0x4c) + 0x21) & 0xFF,// r - 0x4c, // b - 0xE012 // t - }; - - loclass_opt_suc(k, &_init, input, in_size, false); - loclass_opt_output(k, &_init, out); -} - -void loclass_opt_doReaderMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]) { - uint8_t dest [] = {0, 0, 0, 0, 0, 0, 0, 0}; - loclass_opt_MAC(div_key_p, cc_nr_p, dest); - memcpy(mac, dest, 4); -} - -void loclass_opt_doReaderMAC_2(LoclassState_t _init, uint8_t *nr, uint8_t mac[4], const uint8_t *div_key_p) { - loclass_opt_suc(div_key_p, &_init, nr, 4, false); - loclass_opt_output(div_key_p, &_init, mac); -} - - -void loclass_doMAC_N(uint8_t *in_p, uint8_t in_size, uint8_t *div_key_p, uint8_t mac[4]) { - uint8_t dest [] = {0, 0, 0, 0, 0, 0, 0, 0}; - loclass_opt_MAC_N(div_key_p, in_p, in_size, dest); - memcpy(mac, dest, 4); -} - -void loclass_opt_doTagMAC(uint8_t *cc_p, const uint8_t *div_key_p, uint8_t mac[4]) { - LoclassState_t _init = { - ((div_key_p[0] ^ 0x4c) + 0xEC) & 0xFF,// l - ((div_key_p[0] ^ 0x4c) + 0x21) & 0xFF,// r - 0x4c, // b - 0xE012 // t - }; - loclass_opt_suc(div_key_p, &_init, cc_p, 12, true); - loclass_opt_output(div_key_p, &_init, mac); -} - -/** - * The tag MAC can be divided (both can, but no point in dividing the reader mac) into - * two functions, since the first 8 bytes are known, we can pre-calculate the state - * reached after feeding CC to the cipher. - * @param cc_p - * @param div_key_p - * @return the cipher state - */ -LoclassState_t loclass_opt_doTagMAC_1(uint8_t *cc_p, const uint8_t *div_key_p) { - LoclassState_t _init = { - ((div_key_p[0] ^ 0x4c) + 0xEC) & 0xFF,// l - ((div_key_p[0] ^ 0x4c) + 0x21) & 0xFF,// r - 0x4c, // b - 0xE012 // t - }; - loclass_opt_suc(div_key_p, &_init, cc_p, 8, false); - return _init; -} - -/** - * The second part of the tag MAC calculation, since the CC is already calculated into the state, - * this function is fed only the NR, and internally feeds the remaining 32 0-bits to generate the tag - * MAC response. - * @param _init - precalculated cipher state - * @param nr - the reader challenge - * @param mac - where to store the MAC - * @param div_key_p - the key to use - */ -void loclass_opt_doTagMAC_2(LoclassState_t _init, uint8_t *nr, uint8_t mac[4], const uint8_t *div_key_p) { - loclass_opt_suc(div_key_p, &_init, nr, 4, true); - loclass_opt_output(div_key_p, &_init, mac); -} - -void loclass_iclass_calc_div_key(uint8_t *csn, uint8_t *key, uint8_t *div_key, bool elite) { - if (elite) { - uint8_t keytable[128] = {0}; - uint8_t key_index[8] = {0}; - uint8_t key_sel[8] = { 0 }; - uint8_t key_sel_p[8] = { 0 }; - loclass_hash2(key, keytable); - loclass_hash1(csn, key_index); - for (uint8_t i = 0; i < 8 ; i++) - key_sel[i] = keytable[key_index[i]]; - - //Permute from iclass format to standard format - loclass_permutekey_rev(key_sel, key_sel_p); - loclass_diversifyKey(csn, key_sel_p, div_key); - } else { - loclass_diversifyKey(csn, key, div_key); - } -} diff --git a/lib/loclass/optimized_cipher.h b/lib/loclass/optimized_cipher.h deleted file mode 100644 index e7b8cbd67ff..00000000000 --- a/lib/loclass/optimized_cipher.h +++ /dev/null @@ -1,90 +0,0 @@ -//----------------------------------------------------------------------------- -// Borrowed initially from https://github.com/holiman/loclass -// More recently from https://github.com/RfidResearchGroup/proxmark3 -// Copyright (C) 2014 Martin Holst Swende -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// 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. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// WARNING -// -// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. -// -// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL -// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, -// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. -// -// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. -//----------------------------------------------------------------------------- -// It is a reconstruction of the cipher engine used in iClass, and RFID techology. -// -// The implementation is based on the work performed by -// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and -// Milosch Meriac in the paper "Dismantling IClass". -//----------------------------------------------------------------------------- -#ifndef OPTIMIZED_CIPHER_H -#define OPTIMIZED_CIPHER_H -#include -#include -#include -#include - -/** -* Definition 1 (Cipher state). A cipher state of iClass s is an element of F 40/2 -* consisting of the following four components: -* 1. the left register l = (l 0 . . . l 7 ) ∈ F 8/2 ; -* 2. the right register r = (r 0 . . . r 7 ) ∈ F 8/2 ; -* 3. the top register t = (t 0 . . . t 15 ) ∈ F 16/2 . -* 4. the bottom register b = (b 0 . . . b 7 ) ∈ F 8/2 . -**/ -typedef struct { - uint8_t l; - uint8_t r; - uint8_t b; - uint16_t t; -} LoclassState_t; - -/** The reader MAC is MAC(key, CC * NR ) - **/ -void loclass_opt_doReaderMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]); - -void loclass_opt_doReaderMAC_2(LoclassState_t _init, uint8_t *nr, uint8_t mac[4], const uint8_t *div_key_p); - -/** - * The tag MAC is MAC(key, CC * NR * 32x0)) - */ -void loclass_opt_doTagMAC(uint8_t *cc_p, const uint8_t *div_key_p, uint8_t mac[4]); - -/** - * The tag MAC can be divided (both can, but no point in dividing the reader mac) into - * two functions, since the first 8 bytes are known, we can pre-calculate the state - * reached after feeding CC to the cipher. - * @param cc_p - * @param div_key_p - * @return the cipher state - */ -LoclassState_t loclass_opt_doTagMAC_1(uint8_t *cc_p, const uint8_t *div_key_p); -/** - * The second part of the tag MAC calculation, since the CC is already calculated into the state, - * this function is fed only the NR, and internally feeds the remaining 32 0-bits to generate the tag - * MAC response. - * @param _init - precalculated cipher state - * @param nr - the reader challenge - * @param mac - where to store the MAC - * @param div_key_p - the key to use - */ -void loclass_opt_doTagMAC_2(LoclassState_t _init, uint8_t *nr, uint8_t mac[4], const uint8_t *div_key_p); - -void loclass_doMAC_N(uint8_t *in_p, uint8_t in_size, uint8_t *div_key_p, uint8_t mac[4]); -void loclass_iclass_calc_div_key(uint8_t *csn, uint8_t *key, uint8_t *div_key, bool elite); -#endif // OPTIMIZED_CIPHER_H diff --git a/lib/loclass/optimized_cipherutils.c b/lib/loclass/optimized_cipherutils.c deleted file mode 100644 index c5bcbaccdad..00000000000 --- a/lib/loclass/optimized_cipherutils.c +++ /dev/null @@ -1,137 +0,0 @@ -//----------------------------------------------------------------------------- -// Borrowed initially from https://github.com/holiman/loclass -// Copyright (C) 2014 Martin Holst Swende -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// 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. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// WARNING -// -// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. -// -// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL -// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, -// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. -// -// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. -//----------------------------------------------------------------------------- -// It is a reconstruction of the cipher engine used in iClass, and RFID techology. -// -// The implementation is based on the work performed by -// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and -// Milosch Meriac in the paper "Dismantling IClass". -//----------------------------------------------------------------------------- -#include "optimized_cipherutils.h" -#include - -/** - * - * @brief Return and remove the first bit (x0) in the stream : - * @param stream - * @return - */ -bool loclass_headBit(LoclassBitstreamIn_t *stream) { - int bytepos = stream->position >> 3; // divide by 8 - int bitpos = (stream->position++) & 7; // mask out 00000111 - return (*(stream->buffer + bytepos) >> (7 - bitpos)) & 1; -} -/** - * @brief Return and remove the last bit (xn) in the stream: - * @param stream - * @return - */ -bool loclass_tailBit(LoclassBitstreamIn_t *stream) { - int bitpos = stream->numbits - 1 - (stream->position++); - - int bytepos = bitpos >> 3; - bitpos &= 7; - return (*(stream->buffer + bytepos) >> (7 - bitpos)) & 1; -} -/** - * @brief Pushes bit onto the stream - * @param stream - * @param bit - */ -void loclass_pushBit(LoclassBitstreamOut_t *stream, bool bit) { - int bytepos = stream->position >> 3; // divide by 8 - int bitpos = stream->position & 7; - *(stream->buffer + bytepos) |= (bit) << (7 - bitpos); - stream->position++; - stream->numbits++; -} - -/** - * @brief Pushes the lower six bits onto the stream - * as b0 b1 b2 b3 b4 b5 b6 - * @param stream - * @param bits - */ -void loclass_push6bits(LoclassBitstreamOut_t *stream, uint8_t bits) { - loclass_pushBit(stream, bits & 0x20); - loclass_pushBit(stream, bits & 0x10); - loclass_pushBit(stream, bits & 0x08); - loclass_pushBit(stream, bits & 0x04); - loclass_pushBit(stream, bits & 0x02); - loclass_pushBit(stream, bits & 0x01); -} - -/** - * @brief loclass_bitsLeft - * @param stream - * @return number of bits left in stream - */ -int loclass_bitsLeft(LoclassBitstreamIn_t *stream) { - return stream->numbits - stream->position; -} -/** - * @brief numBits - * @param stream - * @return Number of bits stored in stream - */ -void loclass_x_num_to_bytes(uint64_t n, size_t len, uint8_t *dest) { - while (len--) { - dest[len] = (uint8_t) n; - n >>= 8; - } -} - -uint64_t loclass_x_bytes_to_num(uint8_t *src, size_t len) { - uint64_t num = 0; - while (len--) { - num = (num << 8) | (*src); - src++; - } - return num; -} - -uint8_t loclass_reversebytes(uint8_t b) { - b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; - b = (b & 0xCC) >> 2 | (b & 0x33) << 2; - b = (b & 0xAA) >> 1 | (b & 0x55) << 1; - return b; -} - -void loclass_reverse_arraybytes(uint8_t *arr, size_t len) { - uint8_t i; - for (i = 0; i < len ; i++) { - arr[i] = loclass_reversebytes(arr[i]); - } -} - -void loclass_reverse_arraycopy(uint8_t *arr, uint8_t *dest, size_t len) { - uint8_t i; - for (i = 0; i < len ; i++) { - dest[i] = loclass_reversebytes(arr[i]); - } -} - diff --git a/lib/loclass/optimized_cipherutils.h b/lib/loclass/optimized_cipherutils.h deleted file mode 100644 index cb9d2724ae0..00000000000 --- a/lib/loclass/optimized_cipherutils.h +++ /dev/null @@ -1,64 +0,0 @@ -//----------------------------------------------------------------------------- -// Borrowed initially from https://github.com/holiman/loclass -// More recently from https://github.com/RfidResearchGroup/proxmark3 -// Copyright (C) 2014 Martin Holst Swende -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// 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. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// WARNING -// -// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. -// -// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL -// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, -// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. -// -// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. -//----------------------------------------------------------------------------- -// It is a reconstruction of the cipher engine used in iClass, and RFID techology. -// -// The implementation is based on the work performed by -// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and -// Milosch Meriac in the paper "Dismantling IClass". -//----------------------------------------------------------------------------- -#ifndef CIPHERUTILS_H -#define CIPHERUTILS_H -#include -#include -#include - -typedef struct { - uint8_t *buffer; - uint8_t numbits; - uint8_t position; -} LoclassBitstreamIn_t; - -typedef struct { - uint8_t *buffer; - uint8_t numbits; - uint8_t position; -} LoclassBitstreamOut_t; - -bool loclass_headBit(LoclassBitstreamIn_t *stream); -bool loclass_tailBit(LoclassBitstreamIn_t *stream); -void loclass_pushBit(LoclassBitstreamOut_t *stream, bool bit); -int loclass_bitsLeft(LoclassBitstreamIn_t *stream); - -void loclass_push6bits(LoclassBitstreamOut_t *stream, uint8_t bits); -void loclass_x_num_to_bytes(uint64_t n, size_t len, uint8_t *dest); -uint64_t loclass_x_bytes_to_num(uint8_t *src, size_t len); -uint8_t loclass_reversebytes(uint8_t b); -void loclass_reverse_arraybytes(uint8_t *arr, size_t len); -void loclass_reverse_arraycopy(uint8_t *arr, uint8_t *dest, size_t len); -#endif // CIPHERUTILS_H diff --git a/lib/loclass/optimized_elite.c b/lib/loclass/optimized_elite.c deleted file mode 100644 index fc1e5d7484e..00000000000 --- a/lib/loclass/optimized_elite.c +++ /dev/null @@ -1,234 +0,0 @@ -//----------------------------------------------------------------------------- -// Borrowed initially from https://github.com/holiman/loclass -// Copyright (C) 2014 Martin Holst Swende -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// 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. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// WARNING -// -// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. -// -// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL -// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, -// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. -// -// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. -//----------------------------------------------------------------------------- -// It is a reconstruction of the cipher engine used in iClass, and RFID techology. -// -// The implementation is based on the work performed by -// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and -// Milosch Meriac in the paper "Dismantling IClass". -//----------------------------------------------------------------------------- -#include "optimized_elite.h" - -#include -#include -#include -#include -#include "optimized_ikeys.h" - -/** - * @brief Permutes a key from standard NIST format to Iclass specific format - * from http://www.proxmark.org/forum/viewtopic.php?pid=11220#p11220 - * - * If you loclass_permute [6c 8d 44 f9 2a 2d 01 bf] you get [8a 0d b9 88 bb a7 90 ea] as shown below. - * - * 1 0 1 1 1 1 1 1 bf - * 0 0 0 0 0 0 0 1 01 - * 0 0 1 0 1 1 0 1 2d - * 0 0 1 0 1 0 1 0 2a - * 1 1 1 1 1 0 0 1 f9 - * 0 1 0 0 0 1 0 0 44 - * 1 0 0 0 1 1 0 1 8d - * 0 1 1 0 1 1 0 0 6c - * - * 8 0 b 8 b a 9 e - * a d 9 8 b 7 0 a - * - * @param key - * @param dest - */ -void loclass_permutekey(const uint8_t key[8], uint8_t dest[8]) { - int i; - for (i = 0 ; i < 8 ; i++) { - dest[i] = (((key[7] & (0x80 >> i)) >> (7 - i)) << 7) | - (((key[6] & (0x80 >> i)) >> (7 - i)) << 6) | - (((key[5] & (0x80 >> i)) >> (7 - i)) << 5) | - (((key[4] & (0x80 >> i)) >> (7 - i)) << 4) | - (((key[3] & (0x80 >> i)) >> (7 - i)) << 3) | - (((key[2] & (0x80 >> i)) >> (7 - i)) << 2) | - (((key[1] & (0x80 >> i)) >> (7 - i)) << 1) | - (((key[0] & (0x80 >> i)) >> (7 - i)) << 0); - } -} -/** - * Permutes a key from iclass specific format to NIST format - * @brief loclass_permutekey_rev - * @param key - * @param dest - */ -void loclass_permutekey_rev(const uint8_t key[8], uint8_t dest[8]) { - int i; - for (i = 0 ; i < 8 ; i++) { - dest[7 - i] = (((key[0] & (0x80 >> i)) >> (7 - i)) << 7) | - (((key[1] & (0x80 >> i)) >> (7 - i)) << 6) | - (((key[2] & (0x80 >> i)) >> (7 - i)) << 5) | - (((key[3] & (0x80 >> i)) >> (7 - i)) << 4) | - (((key[4] & (0x80 >> i)) >> (7 - i)) << 3) | - (((key[5] & (0x80 >> i)) >> (7 - i)) << 2) | - (((key[6] & (0x80 >> i)) >> (7 - i)) << 1) | - (((key[7] & (0x80 >> i)) >> (7 - i)) << 0); - } -} - -/** - * Helper function for loclass_hash1 - * @brief loclass_rr - * @param val - * @return - */ -static uint8_t loclass_rr(uint8_t val) { - return val >> 1 | ((val & 1) << 7); -} - -/** - * Helper function for loclass_hash1 - * @brief rl - * @param val - * @return - */ -static uint8_t loclass_rl(uint8_t val) { - return val << 1 | ((val & 0x80) >> 7); -} - -/** - * Helper function for loclass_hash1 - * @brief loclass_swap - * @param val - * @return - */ -static uint8_t loclass_swap(uint8_t val) { - return ((val >> 4) & 0xFF) | ((val & 0xFF) << 4); -} - -/** - * Hash1 takes CSN as input, and determines what bytes in the keytable will be used - * when constructing the K_sel. - * @param csn the CSN used - * @param k output - */ -void loclass_hash1(const uint8_t csn[], uint8_t k[]) { - k[0] = csn[0] ^ csn[1] ^ csn[2] ^ csn[3] ^ csn[4] ^ csn[5] ^ csn[6] ^ csn[7]; - k[1] = csn[0] + csn[1] + csn[2] + csn[3] + csn[4] + csn[5] + csn[6] + csn[7]; - k[2] = loclass_rr(loclass_swap(csn[2] + k[1])); - k[3] = loclass_rl(loclass_swap(csn[3] + k[0])); - k[4] = ~loclass_rr(csn[4] + k[2]) + 1; - k[5] = ~loclass_rl(csn[5] + k[3]) + 1; - k[6] = loclass_rr(csn[6] + (k[4] ^ 0x3c)); - k[7] = loclass_rl(csn[7] + (k[5] ^ 0xc3)); - - k[7] &= 0x7F; - k[6] &= 0x7F; - k[5] &= 0x7F; - k[4] &= 0x7F; - k[3] &= 0x7F; - k[2] &= 0x7F; - k[1] &= 0x7F; - k[0] &= 0x7F; -} -/** -Definition 14. Define the rotate key function loclass_rk : (F 82 ) 8 × N → (F 82 ) 8 as -loclass_rk(x [0] . . . x [7] , 0) = x [0] . . . x [7] -loclass_rk(x [0] . . . x [7] , n + 1) = loclass_rk(loclass_rl(x [0] ) . . . loclass_rl(x [7] ), n) -**/ -static void loclass_rk(uint8_t *key, uint8_t n, uint8_t *outp_key) { - memcpy(outp_key, key, 8); - uint8_t j; - while (n-- > 0) { - for (j = 0; j < 8 ; j++) - outp_key[j] = loclass_rl(outp_key[j]); - } - return; -} - -static mbedtls_des_context loclass_ctx_enc; -static mbedtls_des_context loclass_ctx_dec; - -static void loclass_desdecrypt_iclass(uint8_t *iclass_key, uint8_t *input, uint8_t *output) { - uint8_t key_std_format[8] = {0}; - loclass_permutekey_rev(iclass_key, key_std_format); - mbedtls_des_setkey_dec(&loclass_ctx_dec, key_std_format); - mbedtls_des_crypt_ecb(&loclass_ctx_dec, input, output); -} - -static void loclass_desencrypt_iclass(uint8_t *iclass_key, uint8_t *input, uint8_t *output) { - uint8_t key_std_format[8] = {0}; - loclass_permutekey_rev(iclass_key, key_std_format); - mbedtls_des_setkey_enc(&loclass_ctx_enc, key_std_format); - mbedtls_des_crypt_ecb(&loclass_ctx_enc, input, output); -} - -/** - * @brief Insert uint8_t[8] custom master key to calculate hash2 and return key_select. - * @param key unpermuted custom key - * @param loclass_hash1 loclass_hash1 - * @param key_sel output key_sel=h[loclass_hash1[i]] - */ -void hash2(uint8_t *key64, uint8_t *outp_keytable) { - /** - *Expected: - * High Security Key Table - - 00 F1 35 59 A1 0D 5A 26 7F 18 60 0B 96 8A C0 25 C1 - 10 BF A1 3B B0 FF 85 28 75 F2 1F C6 8F 0E 74 8F 21 - 20 14 7A 55 16 C8 A9 7D B3 13 0C 5D C9 31 8D A9 B2 - 30 A3 56 83 0F 55 7E DE 45 71 21 D2 6D C1 57 1C 9C - 40 78 2F 64 51 42 7B 64 30 FA 26 51 76 D3 E0 FB B6 - 50 31 9F BF 2F 7E 4F 94 B4 BD 4F 75 91 E3 1B EB 42 - 60 3F 88 6F B8 6C 2C 93 0D 69 2C D5 20 3C C1 61 95 - 70 43 08 A0 2F FE B3 26 D7 98 0B 34 7B 47 70 A0 AB - - **** The 64-bit HS Custom Key Value = 5B7C62C491C11B39 ******/ - uint8_t key64_negated[8] = {0}; - uint8_t z[8][8] = {{0}, {0}}; - uint8_t temp_output[8] = {0}; - - //calculate complement of key - int i; - for (i = 0; i < 8; i++) - key64_negated[i] = ~key64[i]; - - // Once again, key is on iclass-format - loclass_desencrypt_iclass(key64, key64_negated, z[0]); - - uint8_t y[8][8] = {{0}, {0}}; - - // y[0]=DES_dec(z[0],~key) - // Once again, key is on iclass-format - loclass_desdecrypt_iclass(z[0], key64_negated, y[0]); - - for (i = 1; i < 8; i++) { - loclass_rk(key64, i, temp_output); - loclass_desdecrypt_iclass(temp_output, z[i - 1], z[i]); - loclass_desencrypt_iclass(temp_output, y[i - 1], y[i]); - } - - if (outp_keytable != NULL) { - for (i = 0 ; i < 8 ; i++) { - memcpy(outp_keytable + i * 16, y[i], 8); - memcpy(outp_keytable + 8 + i * 16, z[i], 8); - } - } -} diff --git a/lib/loclass/optimized_elite.h b/lib/loclass/optimized_elite.h deleted file mode 100644 index 9bc30e575f8..00000000000 --- a/lib/loclass/optimized_elite.h +++ /dev/null @@ -1,58 +0,0 @@ -//----------------------------------------------------------------------------- -// Borrowed initially from https://github.com/holiman/loclass -// More recently from https://github.com/RfidResearchGroup/proxmark3 -// Copyright (C) 2014 Martin Holst Swende -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// 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. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// WARNING -// -// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. -// -// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL -// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, -// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. -// -// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. -//----------------------------------------------------------------------------- -// It is a reconstruction of the cipher engine used in iClass, and RFID techology. -// -// The implementation is based on the work performed by -// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and -// Milosch Meriac in the paper "Dismantling IClass". -//----------------------------------------------------------------------------- -#ifndef ELITE_CRACK_H -#define ELITE_CRACK_H - -#include -#include - -void loclass_permutekey(const uint8_t key[8], uint8_t dest[8]); -/** - * Permutes a key from iclass specific format to NIST format - * @brief loclass_permutekey_rev - * @param key - * @param dest - */ -void loclass_permutekey_rev(const uint8_t key[8], uint8_t dest[8]); -/** - * Hash1 takes CSN as input, and determines what bytes in the keytable will be used - * when constructing the K_sel. - * @param csn the CSN used - * @param k output - */ -void loclass_hash1(const uint8_t *csn, uint8_t *k); -void loclass_hash2(uint8_t *key64, uint8_t *outp_keytable); - -#endif diff --git a/lib/loclass/optimized_ikeys.c b/lib/loclass/optimized_ikeys.c deleted file mode 100644 index 9531c16c281..00000000000 --- a/lib/loclass/optimized_ikeys.c +++ /dev/null @@ -1,321 +0,0 @@ -//----------------------------------------------------------------------------- -// Borrowed initially from https://github.com/holiman/loclass -// Copyright (C) 2014 Martin Holst Swende -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// 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. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// WARNING -// -// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. -// -// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL -// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, -// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. -// -// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. -//----------------------------------------------------------------------------- -// It is a reconstruction of the cipher engine used in iClass, and RFID techology. -// -// The implementation is based on the work performed by -// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and -// Milosch Meriac in the paper "Dismantling IClass". -//----------------------------------------------------------------------------- - -/** -From "Dismantling iclass": - This section describes in detail the built-in key diversification algorithm of iClass. - Besides the obvious purpose of deriving a card key from a master key, this - algorithm intends to circumvent weaknesses in the cipher by preventing the - usage of certain ‘weak’ keys. In order to compute a diversified key, the iClass - reader first encrypts the card identity id with the master key K, using single - DES. The resulting ciphertext is then input to a function called loclass_hash0 which - outputs the diversified key k. - - k = loclass_hash0(DES enc (id, K)) - - Here the DES encryption of id with master key K outputs a cryptogram c - of 64 bits. These 64 bits are divided as c = x, y, z [0] , . . . , z [7] ∈ F 82 × F 82 × (F 62 ) 8 - which is used as input to the loclass_hash0 function. This function introduces some - obfuscation by performing a number of permutations, complement and modulo - operations, see Figure 2.5. Besides that, it checks for and removes patterns like - similar key bytes, which could produce a strong bias in the cipher. Finally, the - output of loclass_hash0 is the diversified card key k = k [0] , . . . , k [7] ∈ (F 82 ) 8 . - -**/ -#include "optimized_ikeys.h" - -#include -#include -#include -#include -#include "optimized_cipherutils.h" - -static const uint8_t loclass_pi[35] = { - 0x0F, 0x17, 0x1B, 0x1D, 0x1E, 0x27, 0x2B, 0x2D, - 0x2E, 0x33, 0x35, 0x39, 0x36, 0x3A, 0x3C, 0x47, - 0x4B, 0x4D, 0x4E, 0x53, 0x55, 0x56, 0x59, 0x5A, - 0x5C, 0x63, 0x65, 0x66, 0x69, 0x6A, 0x6C, 0x71, - 0x72, 0x74, 0x78 -}; - -/** - * @brief The key diversification algorithm uses 6-bit bytes. - * This implementation uses 64 bit uint to pack seven of them into one - * variable. When they are there, they are placed as follows: - * XXXX XXXX N0 .... N7, occupying the last 48 bits. - * - * This function picks out one from such a collection - * @param all - * @param n bitnumber - * @return - */ -static uint8_t loclass_getSixBitByte(uint64_t c, int n) { - return (c >> (42 - 6 * n)) & 0x3F; -} - -/** - * @brief Puts back a six-bit 'byte' into a uint64_t. - * @param c buffer - * @param z the value to place there - * @param n bitnumber. - */ -static void loclass_pushbackSixBitByte(uint64_t *c, uint8_t z, int n) { - //0x XXXX YYYY ZZZZ ZZZZ ZZZZ - // ^z0 ^z7 - //z0: 1111 1100 0000 0000 - - uint64_t masked = z & 0x3F; - uint64_t eraser = 0x3F; - masked <<= 42 - 6 * n; - eraser <<= 42 - 6 * n; - - //masked <<= 6*n; - //eraser <<= 6*n; - - eraser = ~eraser; - (*c) &= eraser; - (*c) |= masked; - -} -/** - * @brief Swaps the z-values. - * If the input value has format XYZ0Z1...Z7, the output will have the format - * XYZ7Z6...Z0 instead - * @param c - * @return - */ -static uint64_t loclass_swapZvalues(uint64_t c) { - uint64_t newz = 0; - loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 0), 7); - loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 1), 6); - loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 2), 5); - loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 3), 4); - loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 4), 3); - loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 5), 2); - loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 6), 1); - loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 7), 0); - newz |= (c & 0xFFFF000000000000); - return newz; -} - -/** -* @return 4 six-bit bytes chunked into a uint64_t,as 00..00a0a1a2a3 -*/ -static uint64_t loclass_ck(int i, int j, uint64_t z) { - if (i == 1 && j == -1) { - // loclass_ck(1, −1, z [0] . . . z [3] ) = z [0] . . . z [3] - return z; - } else if (j == -1) { - // loclass_ck(i, −1, z [0] . . . z [3] ) = loclass_ck(i − 1, i − 2, z [0] . . . z [3] ) - return loclass_ck(i - 1, i - 2, z); - } - - if (loclass_getSixBitByte(z, i) == loclass_getSixBitByte(z, j)) { - //loclass_ck(i, j − 1, z [0] . . . z [i] ← j . . . z [3] ) - uint64_t newz = 0; - int c; - for (c = 0; c < 4; c++) { - uint8_t val = loclass_getSixBitByte(z, c); - if (c == i) - loclass_pushbackSixBitByte(&newz, j, c); - else - loclass_pushbackSixBitByte(&newz, val, c); - } - return loclass_ck(i, j - 1, newz); - } else { - return loclass_ck(i, j - 1, z); - } -} -/** - - Definition 8. - Let the function check : (F 62 ) 8 → (F 62 ) 8 be defined as - check(z [0] . . . z [7] ) = loclass_ck(3, 2, z [0] . . . z [3] ) · loclass_ck(3, 2, z [4] . . . z [7] ) - - where loclass_ck : N × N × (F 62 ) 4 → (F 62 ) 4 is defined as - - loclass_ck(1, −1, z [0] . . . z [3] ) = z [0] . . . z [3] - loclass_ck(i, −1, z [0] . . . z [3] ) = loclass_ck(i − 1, i − 2, z [0] . . . z [3] ) - loclass_ck(i, j, z [0] . . . z [3] ) = - loclass_ck(i, j − 1, z [0] . . . z [i] ← j . . . z [3] ), if z [i] = z [j] ; - loclass_ck(i, j − 1, z [0] . . . z [3] ), otherwise - - otherwise. -**/ - -static uint64_t loclass_check(uint64_t z) { - //These 64 bits are divided as c = x, y, z [0] , . . . , z [7] - - // loclass_ck(3, 2, z [0] . . . z [3] ) - uint64_t ck1 = loclass_ck(3, 2, z); - - // loclass_ck(3, 2, z [4] . . . z [7] ) - uint64_t ck2 = loclass_ck(3, 2, z << 24); - - //The loclass_ck function will place the values - // in the middle of z. - ck1 &= 0x00000000FFFFFF000000; - ck2 &= 0x00000000FFFFFF000000; - - return ck1 | ck2 >> 24; -} - -static void loclass_permute(LoclassBitstreamIn_t *p_in, uint64_t z, int l, int r, LoclassBitstreamOut_t *out) { - if (loclass_bitsLeft(p_in) == 0) - return; - - bool pn = loclass_tailBit(p_in); - if (pn) { // pn = 1 - uint8_t zl = loclass_getSixBitByte(z, l); - - loclass_push6bits(out, zl + 1); - loclass_permute(p_in, z, l + 1, r, out); - } else { // otherwise - uint8_t zr = loclass_getSixBitByte(z, r); - - loclass_push6bits(out, zr); - loclass_permute(p_in, z, l, r + 1, out); - } -} - -/** - * @brief - *Definition 11. Let the function loclass_hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as - * loclass_hash0(x, y, z [0] . . . z [7] ) = k [0] . . . k [7] where - * z'[i] = (z[i] mod (63-i)) + i i = 0...3 - * z'[i+4] = (z[i+4] mod (64-i)) + i i = 0...3 - * ẑ = check(z'); - * @param c - * @param k this is where the diversified key is put (should be 8 bytes) - * @return - */ -void loclass_hash0(uint64_t c, uint8_t k[8]) { - c = loclass_swapZvalues(c); - - //These 64 bits are divided as c = x, y, z [0] , . . . , z [7] - // x = 8 bits - // y = 8 bits - // z0-z7 6 bits each : 48 bits - uint8_t x = (c & 0xFF00000000000000) >> 56; - uint8_t y = (c & 0x00FF000000000000) >> 48; - uint64_t zP = 0; - - for (int n = 0; n < 4 ; n++) { - uint8_t zn = loclass_getSixBitByte(c, n); - uint8_t zn4 = loclass_getSixBitByte(c, n + 4); - uint8_t _zn = (zn % (63 - n)) + n; - uint8_t _zn4 = (zn4 % (64 - n)) + n; - loclass_pushbackSixBitByte(&zP, _zn, n); - loclass_pushbackSixBitByte(&zP, _zn4, n + 4); - } - - uint64_t zCaret = loclass_check(zP); - uint8_t p = loclass_pi[x % 35]; - - if (x & 1) //Check if x7 is 1 - p = ~p; - - LoclassBitstreamIn_t p_in = { &p, 8, 0 }; - uint8_t outbuffer[] = {0, 0, 0, 0, 0, 0, 0, 0}; - LoclassBitstreamOut_t out = {outbuffer, 0, 0}; - loclass_permute(&p_in, zCaret, 0, 4, &out); //returns 48 bits? or 6 8-bytes - - //Out is now a buffer containing six-bit bytes, should be 48 bits - // if all went well - //Shift z-values down onto the lower segment - - uint64_t zTilde = loclass_x_bytes_to_num(outbuffer, sizeof(outbuffer)); - - zTilde >>= 16; - - for (int i = 0; i < 8; i++) { - // the key on index i is first a bit from y - // then six bits from z, - // then a bit from p - - // Init with zeroes - k[i] = 0; - // First, place yi leftmost in k - //k[i] |= (y << i) & 0x80 ; - - // First, place y(7-i) leftmost in k - k[i] |= (y << (7 - i)) & 0x80 ; - - uint8_t zTilde_i = loclass_getSixBitByte(zTilde, i); - // zTildeI is now on the form 00XXXXXX - // with one leftshift, it'll be - // 0XXXXXX0 - // So after leftshift, we can OR it into k - // However, when doing complement, we need to - // again MASK 0XXXXXX0 (0x7E) - zTilde_i <<= 1; - - //Finally, add bit from p or p-mod - //Shift bit i into rightmost location (mask only after complement) - uint8_t p_i = p >> i & 0x1; - - if (k[i]) { // yi = 1 - k[i] |= ~zTilde_i & 0x7E; - k[i] |= p_i & 1; - k[i] += 1; - - } else { // otherwise - k[i] |= zTilde_i & 0x7E; - k[i] |= (~p_i) & 1; - } - } -} -/** - * @brief Performs Elite-class key diversification - * @param csn - * @param key - * @param div_key - */ -void loclass_diversifyKey(uint8_t *csn, const uint8_t *key, uint8_t *div_key) { - mbedtls_des_context loclass_ctx_enc; - - // Prepare the DES key - mbedtls_des_setkey_enc(&loclass_ctx_enc, key); - - uint8_t crypted_csn[8] = {0}; - - // Calculate DES(CSN, KEY) - mbedtls_des_crypt_ecb(&loclass_ctx_enc, csn, crypted_csn); - - //Calculate HASH0(DES)) - uint64_t c_csn = loclass_x_bytes_to_num(crypted_csn, sizeof(crypted_csn)); - - loclass_hash0(c_csn, div_key); -} - diff --git a/lib/loclass/optimized_ikeys.h b/lib/loclass/optimized_ikeys.h deleted file mode 100644 index e960b8be279..00000000000 --- a/lib/loclass/optimized_ikeys.h +++ /dev/null @@ -1,66 +0,0 @@ -//----------------------------------------------------------------------------- -// Borrowed initially from https://github.com/holiman/loclass -// More recently from https://github.com/RfidResearchGroup/proxmark3 -// Copyright (C) 2014 Martin Holst Swende -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// 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. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// WARNING -// -// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. -// -// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL -// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, -// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. -// -// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. -//----------------------------------------------------------------------------- -// It is a reconstruction of the cipher engine used in iClass, and RFID techology. -// -// The implementation is based on the work performed by -// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and -// Milosch Meriac in the paper "Dismantling IClass". -//----------------------------------------------------------------------------- -#ifndef IKEYS_H -#define IKEYS_H - -#include - -/** - * @brief - *Definition 11. Let the function loclass_hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as - * loclass_hash0(x, y, z [0] . . . z [7] ) = k [0] . . . k [7] where - * z'[i] = (z[i] mod (63-i)) + i i = 0...3 - * z'[i+4] = (z[i+4] mod (64-i)) + i i = 0...3 - * ẑ = check(z'); - * @param c - * @param k this is where the diversified key is put (should be 8 bytes) - * @return - */ -void loclass_hash0(uint64_t c, uint8_t k[8]); -/** - * @brief Performs Elite-class key diversification - * @param csn - * @param key - * @param div_key - */ - -void loclass_diversifyKey(uint8_t *csn, const uint8_t *key, uint8_t *div_key); -/** - * @brief Permutes a key from standard NIST format to Iclass specific format - * @param key - * @param dest - */ - -#endif // IKEYS_H diff --git a/lib/mbedtls b/lib/mbedtls index d65aeb37349..edb8fec9882 160000 --- a/lib/mbedtls +++ b/lib/mbedtls @@ -1 +1 @@ -Subproject commit d65aeb37349ad1a50e0f6c9b694d4b5290d60e49 +Subproject commit edb8fec9882084344a314368ac7fd957a187519c diff --git a/lib/mbedtls.scons b/lib/mbedtls.scons index e39d9dae852..77add769668 100644 --- a/lib/mbedtls.scons +++ b/lib/mbedtls.scons @@ -2,20 +2,56 @@ Import("env") env.Append( CPPPATH=[ - "#/lib/mbedtls", + # "#/lib/mbedtls", "#/lib/mbedtls/include", ], + SDK_HEADERS=[ + File("mbedtls/include/mbedtls/des.h"), + File("mbedtls/include/mbedtls/sha1.h"), + File("mbedtls/include/mbedtls/sha256.h"), + File("mbedtls/include/mbedtls/md5.h"), + File("mbedtls/include/mbedtls/md.h"), + File("mbedtls/include/mbedtls/ecdsa.h"), + File("mbedtls/include/mbedtls/ecdh.h"), + File("mbedtls/include/mbedtls/ecp.h"), + # File("mbedtls/include/mbedtls/sha1.h"), + ], + CPPDEFINES=[("MBEDTLS_CONFIG_FILE", '\\"mbedtls_cfg.h\\"')], ) libenv = env.Clone(FW_LIB_NAME="mbedtls") libenv.ApplyLibFlags() +libenv.AppendUnique( + CCFLAGS=[ + # Required for lib to be linkable with .faps + "-mword-relocations", + "-mlong-calls", + # Crappy code :) + "-Wno-redundant-decls", + ], +) + +# If we were to build full mbedtls, we would need to use this: +# sources = libenv.GlobRecursive("*.c*", "mbedtls/library") +# Otherwise, we can just use the files we need: sources = [ - "mbedtls/library/des.c", - "mbedtls/library/sha1.c", - "mbedtls/library/platform_util.c", + File("mbedtls/library/bignum.c"), + File("mbedtls/library/bignum_core.c"), + File("mbedtls/library/ecdsa.c"), + File("mbedtls/library/ecp.c"), + File("mbedtls/library/ecp_curves.c"), + File("mbedtls/library/md.c"), + File("mbedtls/library/md5.c"), + File("mbedtls/library/platform_util.c"), + File("mbedtls/library/ripemd160.c"), + File("mbedtls/library/sha1.c"), + File("mbedtls/library/sha256.c"), + File("mbedtls/library/des.c"), ] +Depends(sources, File("mbedtls_cfg.h")) + lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) libenv.Install("${LIB_DIST_DIR}", lib) diff --git a/lib/mbedtls_cfg.h b/lib/mbedtls_cfg.h new file mode 100644 index 00000000000..5af98e96575 --- /dev/null +++ b/lib/mbedtls_cfg.h @@ -0,0 +1,92 @@ +#pragma once + +/** +* A subset of the mbedTLS configuration options that are relevant to the +* Flipper Zero firmware and apps. They are built to "mbedtls" library you can +* link your apps with. +* +* If you need more features, either bring the full mbedtls library into your +* app using "fap_private_libs" or open an issue on GitHub to add them to the +* default configuration. +**/ + +#define MBEDTLS_HAVE_ASM + +#define MBEDTLS_NO_UDBL_DIVISION +#define MBEDTLS_NO_64BIT_MULTIPLICATION + +#define MBEDTLS_DEPRECATED_WARNING + +#define MBEDTLS_AES_FEWER_TABLES +// #define MBEDTLS_CHECK_RETURN_WARNING + +#define MBEDTLS_CIPHER_MODE_CBC +#define MBEDTLS_CIPHER_MODE_CFB +#define MBEDTLS_CIPHER_MODE_CTR +#define MBEDTLS_CIPHER_MODE_OFB +#define MBEDTLS_CIPHER_MODE_XTS + +#define MBEDTLS_CIPHER_PADDING_PKCS7 +#define MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS +#define MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN +#define MBEDTLS_CIPHER_PADDING_ZEROS + +/* Short Weierstrass curves (supporting ECP, ECDH, ECDSA) */ +// #define MBEDTLS_ECP_DP_SECP192R1_ENABLED +// #define MBEDTLS_ECP_DP_SECP224R1_ENABLED +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +// #define MBEDTLS_ECP_DP_SECP384R1_ENABLED +// #define MBEDTLS_ECP_DP_SECP521R1_ENABLED +// #define MBEDTLS_ECP_DP_SECP192K1_ENABLED +// #define MBEDTLS_ECP_DP_SECP224K1_ENABLED +// #define MBEDTLS_ECP_DP_SECP256K1_ENABLED +// #define MBEDTLS_ECP_DP_BP256R1_ENABLED +// #define MBEDTLS_ECP_DP_BP384R1_ENABLED +// #define MBEDTLS_ECP_DP_BP512R1_ENABLED +/* Montgomery curves (supporting ECP) */ +// #define MBEDTLS_ECP_DP_CURVE25519_ENABLED +// #define MBEDTLS_ECP_DP_CURVE448_ENABLED + +#define MBEDTLS_ECP_NIST_OPTIM + +#define MBEDTLS_GENPRIME +// #define MBEDTLS_PKCS1_V15 +// #define MBEDTLS_PKCS1_V21 + +#define MBEDTLS_MD_C + +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_ASN1_WRITE_C +#define MBEDTLS_BASE64_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_OID_C + +// #define MBEDTLS_CHACHA20_C +// #define MBEDTLS_CHACHAPOLY_C +#define MBEDTLS_CIPHER_C +#define MBEDTLS_DES_C +#define MBEDTLS_DHM_C + +#define MBEDTLS_ECDH_C + +#define MBEDTLS_ECDSA_C +#define MBEDTLS_ECP_C + +#define MBEDTLS_GCM_C + +#define MBEDTLS_AES_C +#define MBEDTLS_MD5_C + +// #define MBEDTLS_PEM_PARSE_C +// #define MBEDTLS_PEM_WRITE_C + +// #define MBEDTLS_PLATFORM_MEMORY +// #define MBEDTLS_PLATFORM_C + +// #define MBEDTLS_RIPEMD160_C +// #define MBEDTLS_RSA_C +#define MBEDTLS_SHA224_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SHA1_C + +#define MBEDTLS_ERROR_C \ No newline at end of file diff --git a/lib/micro-ecc/LICENSE.txt b/lib/micro-ecc/LICENSE.txt deleted file mode 100644 index ab099ae5a51..00000000000 --- a/lib/micro-ecc/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -Copyright (c) 2014, Kenneth MacKay -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 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/lib/micro-ecc/README.md b/lib/micro-ecc/README.md deleted file mode 100644 index 111321bf765..00000000000 --- a/lib/micro-ecc/README.md +++ /dev/null @@ -1,41 +0,0 @@ -micro-ecc -========== - -A small and fast ECDH and ECDSA implementation for 8-bit, 32-bit, and 64-bit processors. - -The static version of micro-ecc (ie, where the curve was selected at compile-time) can be found in the "static" branch. - -Features --------- - - * Resistant to known side-channel attacks. - * Written in C, with optional GCC inline assembly for AVR, ARM and Thumb platforms. - * Supports 8, 32, and 64-bit architectures. - * Small code size. - * No dynamic memory allocation. - * Support for 5 standard curves: secp160r1, secp192r1, secp224r1, secp256r1, and secp256k1. - * BSD 2-clause license. - -Usage Notes ------------ -### Point Representation ### -Compressed points are represented in the standard format as defined in http://www.secg.org/sec1-v2.pdf; uncompressed points are represented in standard format, but without the `0x04` prefix. All functions except `uECC_decompress()` only accept uncompressed points; use `uECC_compress()` and `uECC_decompress()` to convert between compressed and uncompressed point representations. - -Private keys are represented in the standard format. - -### Using the Code ### - -I recommend just copying (or symlink) the uECC files into your project. Then just `#include "uECC.h"` to use the micro-ecc functions. - -For use with Arduino, you can use the Library Manager to download micro-ecc (**Sketch**=>**Include Library**=>**Manage Libraries**). You can then use uECC just like any other Arduino library (uECC should show up in the **Sketch**=>**Import Library** submenu). - -See uECC.h for documentation for each function. - -### Compilation Notes ### - - * Should compile with any C/C++ compiler that supports stdint.h (this includes Visual Studio 2013). - * If you want to change the defaults for any of the uECC compile-time options (such as `uECC_OPTIMIZATION_LEVEL`), you must change them in your Makefile or similar so that uECC.c is compiled with the desired values (ie, compile uECC.c with `-DuECC_OPTIMIZATION_LEVEL=3` or whatever). - * When compiling for a Thumb-1 platform, you must use the `-fomit-frame-pointer` GCC option (this is enabled by default when compiling with `-O1` or higher). - * When compiling for an ARM/Thumb-2 platform with `uECC_OPTIMIZATION_LEVEL` >= 3, you must use the `-fomit-frame-pointer` GCC option (this is enabled by default when compiling with `-O1` or higher). - * When compiling for AVR, you must have optimizations enabled (compile with `-O1` or higher). - * When building for Windows, you will need to link in the `advapi32.lib` system library. diff --git a/lib/micro-ecc/asm_arm.inc b/lib/micro-ecc/asm_arm.inc deleted file mode 100644 index 43844da6a56..00000000000 --- a/lib/micro-ecc/asm_arm.inc +++ /dev/null @@ -1,821 +0,0 @@ -/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_ASM_ARM_H_ -#define _UECC_ASM_ARM_H_ - -#if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) - #define uECC_MIN_WORDS 8 -#endif -#if uECC_SUPPORTS_secp224r1 - #undef uECC_MIN_WORDS - #define uECC_MIN_WORDS 7 -#endif -#if uECC_SUPPORTS_secp192r1 - #undef uECC_MIN_WORDS - #define uECC_MIN_WORDS 6 -#endif -#if uECC_SUPPORTS_secp160r1 - #undef uECC_MIN_WORDS - #define uECC_MIN_WORDS 5 -#endif - -#if (uECC_PLATFORM == uECC_arm_thumb) - #define REG_RW "+l" - #define REG_WRITE "=l" -#else - #define REG_RW "+r" - #define REG_WRITE "=r" -#endif - -#if (uECC_PLATFORM == uECC_arm_thumb || uECC_PLATFORM == uECC_arm_thumb2) - #define REG_RW_LO "+l" - #define REG_WRITE_LO "=l" -#else - #define REG_RW_LO "+r" - #define REG_WRITE_LO "=r" -#endif - -#if (uECC_PLATFORM == uECC_arm_thumb2) - #define RESUME_SYNTAX -#else - #define RESUME_SYNTAX ".syntax divided \n\t" -#endif - -#if (uECC_OPTIMIZATION_LEVEL >= 2) - -uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - wordcount_t num_words) { -#if (uECC_MAX_WORDS != uECC_MIN_WORDS) - #if (uECC_PLATFORM == uECC_arm_thumb) || (uECC_PLATFORM == uECC_arm_thumb2) - uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 2 + 1; - #else /* ARM */ - uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 4; - #endif -#endif - uint32_t carry; - uint32_t left_word; - uint32_t right_word; - - __asm__ volatile ( - ".syntax unified \n\t" - "movs %[carry], #0 \n\t" - #if (uECC_MAX_WORDS != uECC_MIN_WORDS) - "adr %[left], 1f \n\t" - ".align 4 \n\t" - "adds %[jump], %[left] \n\t" - #endif - - "ldmia %[lptr]!, {%[left]} \n\t" - "ldmia %[rptr]!, {%[right]} \n\t" - "adds %[left], %[right] \n\t" - "stmia %[dptr]!, {%[left]} \n\t" - - #if (uECC_MAX_WORDS != uECC_MIN_WORDS) - "bx %[jump] \n\t" - #endif - "1: \n\t" - REPEAT(DEC(uECC_MAX_WORDS), - "ldmia %[lptr]!, {%[left]} \n\t" - "ldmia %[rptr]!, {%[right]} \n\t" - "adcs %[left], %[right] \n\t" - "stmia %[dptr]!, {%[left]} \n\t") - - "adcs %[carry], %[carry] \n\t" - RESUME_SYNTAX - : [dptr] REG_RW_LO (result), [lptr] REG_RW_LO (left), [rptr] REG_RW_LO (right), - #if (uECC_MAX_WORDS != uECC_MIN_WORDS) - [jump] REG_RW_LO (jump), - #endif - [carry] REG_WRITE_LO (carry), [left] REG_WRITE_LO (left_word), - [right] REG_WRITE_LO (right_word) - : - : "cc", "memory" - ); - return carry; -} -#define asm_add 1 - -#pragma GCC diagnostic ignored "-Wredundant-decls" -uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - wordcount_t num_words) { -#if (uECC_MAX_WORDS != uECC_MIN_WORDS) - #if (uECC_PLATFORM == uECC_arm_thumb) || (uECC_PLATFORM == uECC_arm_thumb2) - uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 2 + 1; - #else /* ARM */ - uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 4; - #endif -#endif - uint32_t carry; - uint32_t left_word; - uint32_t right_word; - - __asm__ volatile ( - ".syntax unified \n\t" - "movs %[carry], #0 \n\t" - #if (uECC_MAX_WORDS != uECC_MIN_WORDS) - "adr %[left], 1f \n\t" - ".align 4 \n\t" - "adds %[jump], %[left] \n\t" - #endif - - "ldmia %[lptr]!, {%[left]} \n\t" - "ldmia %[rptr]!, {%[right]} \n\t" - "subs %[left], %[right] \n\t" - "stmia %[dptr]!, {%[left]} \n\t" - - #if (uECC_MAX_WORDS != uECC_MIN_WORDS) - "bx %[jump] \n\t" - #endif - "1: \n\t" - REPEAT(DEC(uECC_MAX_WORDS), - "ldmia %[lptr]!, {%[left]} \n\t" - "ldmia %[rptr]!, {%[right]} \n\t" - "sbcs %[left], %[right] \n\t" - "stmia %[dptr]!, {%[left]} \n\t") - - "adcs %[carry], %[carry] \n\t" - RESUME_SYNTAX - : [dptr] REG_RW_LO (result), [lptr] REG_RW_LO (left), [rptr] REG_RW_LO (right), - #if (uECC_MAX_WORDS != uECC_MIN_WORDS) - [jump] REG_RW_LO (jump), - #endif - [carry] REG_WRITE_LO (carry), [left] REG_WRITE_LO (left_word), - [right] REG_WRITE_LO (right_word) - : - : "cc", "memory" - ); - return !carry; /* Note that on ARM, carry flag set means "no borrow" when subtracting - (for some reason...) */ -} -#define asm_sub 1 - -#endif /* (uECC_OPTIMIZATION_LEVEL >= 2) */ - -#if (uECC_OPTIMIZATION_LEVEL >= 3) - -#if (uECC_PLATFORM != uECC_arm_thumb) - -#if uECC_ARM_USE_UMAAL - #include "asm_arm_mult_square_umaal.inc" -#else - #include "asm_arm_mult_square.inc" -#endif - -#if (uECC_OPTIMIZATION_LEVEL == 3) - -uECC_VLI_API void uECC_vli_mult(uint32_t *result, - const uint32_t *left, - const uint32_t *right, - wordcount_t num_words) { - register uint32_t *r0 __asm__("r0") = result; - register const uint32_t *r1 __asm__("r1") = left; - register const uint32_t *r2 __asm__("r2") = right; - register uint32_t r3 __asm__("r3") = num_words; - - __asm__ volatile ( - ".syntax unified \n\t" -#if (uECC_MIN_WORDS == 5) - FAST_MULT_ASM_5 - #if (uECC_MAX_WORDS > 5) - FAST_MULT_ASM_5_TO_6 - #endif - #if (uECC_MAX_WORDS > 6) - FAST_MULT_ASM_6_TO_7 - #endif - #if (uECC_MAX_WORDS > 7) - FAST_MULT_ASM_7_TO_8 - #endif -#elif (uECC_MIN_WORDS == 6) - FAST_MULT_ASM_6 - #if (uECC_MAX_WORDS > 6) - FAST_MULT_ASM_6_TO_7 - #endif - #if (uECC_MAX_WORDS > 7) - FAST_MULT_ASM_7_TO_8 - #endif -#elif (uECC_MIN_WORDS == 7) - FAST_MULT_ASM_7 - #if (uECC_MAX_WORDS > 7) - FAST_MULT_ASM_7_TO_8 - #endif -#elif (uECC_MIN_WORDS == 8) - FAST_MULT_ASM_8 -#endif - "1: \n\t" - RESUME_SYNTAX - : "+r" (r0), "+r" (r1), "+r" (r2) - : "r" (r3) - : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" - ); -} -#define asm_mult 1 - -#if uECC_SQUARE_FUNC -uECC_VLI_API void uECC_vli_square(uECC_word_t *result, - const uECC_word_t *left, - wordcount_t num_words) { - register uint32_t *r0 __asm__("r0") = result; - register const uint32_t *r1 __asm__("r1") = left; - register uint32_t r2 __asm__("r2") = num_words; - - __asm__ volatile ( - ".syntax unified \n\t" -#if (uECC_MIN_WORDS == 5) - FAST_SQUARE_ASM_5 - #if (uECC_MAX_WORDS > 5) - FAST_SQUARE_ASM_5_TO_6 - #endif - #if (uECC_MAX_WORDS > 6) - FAST_SQUARE_ASM_6_TO_7 - #endif - #if (uECC_MAX_WORDS > 7) - FAST_SQUARE_ASM_7_TO_8 - #endif -#elif (uECC_MIN_WORDS == 6) - FAST_SQUARE_ASM_6 - #if (uECC_MAX_WORDS > 6) - FAST_SQUARE_ASM_6_TO_7 - #endif - #if (uECC_MAX_WORDS > 7) - FAST_SQUARE_ASM_7_TO_8 - #endif -#elif (uECC_MIN_WORDS == 7) - FAST_SQUARE_ASM_7 - #if (uECC_MAX_WORDS > 7) - FAST_SQUARE_ASM_7_TO_8 - #endif -#elif (uECC_MIN_WORDS == 8) - FAST_SQUARE_ASM_8 -#endif - - "1: \n\t" - RESUME_SYNTAX - : "+r" (r0), "+r" (r1) - : "r" (r2) - : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" - ); -} -#define asm_square 1 -#endif /* uECC_SQUARE_FUNC */ - -#else /* (uECC_OPTIMIZATION_LEVEL > 3) */ - -uECC_VLI_API void uECC_vli_mult(uint32_t *result, - const uint32_t *left, - const uint32_t *right, - wordcount_t num_words) { - register uint32_t *r0 __asm__("r0") = result; - register const uint32_t *r1 __asm__("r1") = left; - register const uint32_t *r2 __asm__("r2") = right; - register uint32_t r3 __asm__("r3") = num_words; - -#if uECC_SUPPORTS_secp160r1 - if (num_words == 5) { - __asm__ volatile ( - ".syntax unified \n\t" - FAST_MULT_ASM_5 - RESUME_SYNTAX - : "+r" (r0), "+r" (r1), "+r" (r2) - : "r" (r3) - : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" - ); - return; - } -#endif -#if uECC_SUPPORTS_secp192r1 - if (num_words == 6) { - __asm__ volatile ( - ".syntax unified \n\t" - FAST_MULT_ASM_6 - RESUME_SYNTAX - : "+r" (r0), "+r" (r1), "+r" (r2) - : "r" (r3) - : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" - ); - return; - } -#endif -#if uECC_SUPPORTS_secp224r1 - if (num_words == 7) { - __asm__ volatile ( - ".syntax unified \n\t" - FAST_MULT_ASM_7 - RESUME_SYNTAX - : "+r" (r0), "+r" (r1), "+r" (r2) - : "r" (r3) - : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" - ); - return; - } -#endif -#if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) - if (num_words == 8) { - __asm__ volatile ( - ".syntax unified \n\t" - FAST_MULT_ASM_8 - RESUME_SYNTAX - : "+r" (r0), "+r" (r1), "+r" (r2) - : "r" (r3) - : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" - ); - return; - } -#endif -} -#define asm_mult 1 - -#if uECC_SQUARE_FUNC -uECC_VLI_API void uECC_vli_square(uECC_word_t *result, - const uECC_word_t *left, - wordcount_t num_words) { - register uint32_t *r0 __asm__("r0") = result; - register const uint32_t *r1 __asm__("r1") = left; - register uint32_t r2 __asm__("r2") = num_words; - -#if uECC_SUPPORTS_secp160r1 - if (num_words == 5) { - __asm__ volatile ( - ".syntax unified \n\t" - FAST_SQUARE_ASM_5 - RESUME_SYNTAX - : "+r" (r0), "+r" (r1) - : "r" (r2) - : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" - ); - return; - } -#endif -#if uECC_SUPPORTS_secp192r1 - if (num_words == 6) { - __asm__ volatile ( - ".syntax unified \n\t" - FAST_SQUARE_ASM_6 - RESUME_SYNTAX - : "+r" (r0), "+r" (r1) - : "r" (r2) - : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" - ); - return; - } -#endif -#if uECC_SUPPORTS_secp224r1 - if (num_words == 7) { - __asm__ volatile ( - ".syntax unified \n\t" - FAST_SQUARE_ASM_7 - RESUME_SYNTAX - : "+r" (r0), "+r" (r1) - : "r" (r2) - : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" - ); - return; - } -#endif -#if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) - if (num_words == 8) { - __asm__ volatile ( - ".syntax unified \n\t" - FAST_SQUARE_ASM_8 - RESUME_SYNTAX - : "+r" (r0), "+r" (r1) - : "r" (r2) - : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" - ); - return; - } -#endif -} -#define asm_square 1 -#endif /* uECC_SQUARE_FUNC */ - -#endif /* (uECC_OPTIMIZATION_LEVEL > 3) */ - -#endif /* uECC_PLATFORM != uECC_arm_thumb */ - -#endif /* (uECC_OPTIMIZATION_LEVEL >= 3) */ - -/* ---- "Small" implementations ---- */ - -#if !asm_add -uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - wordcount_t num_words) { - uint32_t carry = 0; - uint32_t left_word; - uint32_t right_word; - - __asm__ volatile ( - ".syntax unified \n\t" - "1: \n\t" - "ldmia %[lptr]!, {%[left]} \n\t" /* Load left word. */ - "ldmia %[rptr]!, {%[right]} \n\t" /* Load right word. */ - "lsrs %[carry], #1 \n\t" /* Set up carry flag (carry = 0 after this). */ - "adcs %[left], %[left], %[right] \n\t" /* Add with carry. */ - "adcs %[carry], %[carry], %[carry] \n\t" /* Store carry bit. */ - "stmia %[dptr]!, {%[left]} \n\t" /* Store result word. */ - "subs %[ctr], #1 \n\t" /* Decrement counter. */ - "bne 1b \n\t" /* Loop until counter == 0. */ - RESUME_SYNTAX - : [dptr] REG_RW (result), [lptr] REG_RW (left), [rptr] REG_RW (right), - [ctr] REG_RW (num_words), [carry] REG_RW (carry), - [left] REG_WRITE (left_word), [right] REG_WRITE (right_word) - : - : "cc", "memory" - ); - return carry; -} -#define asm_add 1 -#endif - -#if !asm_sub -uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - wordcount_t num_words) { - uint32_t carry = 1; /* carry = 1 initially (means don't borrow) */ - uint32_t left_word; - uint32_t right_word; - - __asm__ volatile ( - ".syntax unified \n\t" - "1: \n\t" - "ldmia %[lptr]!, {%[left]} \n\t" /* Load left word. */ - "ldmia %[rptr]!, {%[right]} \n\t" /* Load right word. */ - "lsrs %[carry], #1 \n\t" /* Set up carry flag (carry = 0 after this). */ - "sbcs %[left], %[left], %[right] \n\t" /* Subtract with borrow. */ - "adcs %[carry], %[carry], %[carry] \n\t" /* Store carry bit. */ - "stmia %[dptr]!, {%[left]} \n\t" /* Store result word. */ - "subs %[ctr], #1 \n\t" /* Decrement counter. */ - "bne 1b \n\t" /* Loop until counter == 0. */ - RESUME_SYNTAX - : [dptr] REG_RW (result), [lptr] REG_RW (left), [rptr] REG_RW (right), - [ctr] REG_RW (num_words), [carry] REG_RW (carry), - [left] REG_WRITE (left_word), [right] REG_WRITE (right_word) - : - : "cc", "memory" - ); - return !carry; -} -#define asm_sub 1 -#endif - -#if !asm_mult -uECC_VLI_API void uECC_vli_mult(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - wordcount_t num_words) { -#if (uECC_PLATFORM != uECC_arm_thumb) - uint32_t c0 = 0; - uint32_t c1 = 0; - uint32_t c2 = 0; - uint32_t k = 0; - uint32_t i; - uint32_t t0, t1; - - __asm__ volatile ( - ".syntax unified \n\t" - - "1: \n\t" /* outer loop (k < num_words) */ - "movs %[i], #0 \n\t" /* i = 0 */ - "b 3f \n\t" - - "2: \n\t" /* outer loop (k >= num_words) */ - "movs %[i], %[k] \n\t" /* i = k */ - "subs %[i], %[last_word] \n\t" /* i = k - (num_words - 1) (times 4) */ - - "3: \n\t" /* inner loop */ - "subs %[t0], %[k], %[i] \n\t" /* t0 = k-i */ - - "ldr %[t1], [%[right], %[t0]] \n\t" /* t1 = right[k - i] */ - "ldr %[t0], [%[left], %[i]] \n\t" /* t0 = left[i] */ - - "umull %[t0], %[t1], %[t0], %[t1] \n\t" /* (t0, t1) = left[i] * right[k - i] */ - - "adds %[c0], %[c0], %[t0] \n\t" /* add low word to c0 */ - "adcs %[c1], %[c1], %[t1] \n\t" /* add high word to c1, including carry */ - "adcs %[c2], %[c2], #0 \n\t" /* add carry to c2 */ - - "adds %[i], #4 \n\t" /* i += 4 */ - "cmp %[i], %[last_word] \n\t" /* i > (num_words - 1) (times 4)? */ - "bgt 4f \n\t" /* if so, exit the loop */ - "cmp %[i], %[k] \n\t" /* i <= k? */ - "ble 3b \n\t" /* if so, continue looping */ - - "4: \n\t" /* end inner loop */ - - "str %[c0], [%[result], %[k]] \n\t" /* result[k] = c0 */ - "mov %[c0], %[c1] \n\t" /* c0 = c1 */ - "mov %[c1], %[c2] \n\t" /* c1 = c2 */ - "movs %[c2], #0 \n\t" /* c2 = 0 */ - "adds %[k], #4 \n\t" /* k += 4 */ - "cmp %[k], %[last_word] \n\t" /* k <= (num_words - 1) (times 4) ? */ - "ble 1b \n\t" /* if so, loop back, start with i = 0 */ - "cmp %[k], %[last_word], lsl #1 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */ - "ble 2b \n\t" /* if so, loop back, start with i = (k + 1) - num_words */ - /* end outer loop */ - - "str %[c0], [%[result], %[k]] \n\t" /* result[num_words * 2 - 1] = c0 */ - RESUME_SYNTAX - : [c0] "+r" (c0), [c1] "+r" (c1), [c2] "+r" (c2), - [k] "+r" (k), [i] "=&r" (i), [t0] "=&r" (t0), [t1] "=&r" (t1) - : [result] "r" (result), [left] "r" (left), [right] "r" (right), - [last_word] "r" ((num_words - 1) * 4) - : "cc", "memory" - ); - -#else /* Thumb-1 */ - uint32_t r4, r5, r6, r7; - - __asm__ volatile ( - ".syntax unified \n\t" - "subs %[r3], #1 \n\t" /* r3 = num_words - 1 */ - "lsls %[r3], #2 \n\t" /* r3 = (num_words - 1) * 4 */ - "mov r8, %[r3] \n\t" /* r8 = (num_words - 1) * 4 */ - "lsls %[r3], #1 \n\t" /* r3 = (num_words - 1) * 8 */ - "mov r9, %[r3] \n\t" /* r9 = (num_words - 1) * 8 */ - "movs %[r3], #0 \n\t" /* c0 = 0 */ - "movs %[r4], #0 \n\t" /* c1 = 0 */ - "movs %[r5], #0 \n\t" /* c2 = 0 */ - "movs %[r6], #0 \n\t" /* k = 0 */ - - "push {%[r0]} \n\t" /* keep result on the stack */ - - "1: \n\t" /* outer loop (k < num_words) */ - "movs %[r7], #0 \n\t" /* r7 = i = 0 */ - "b 3f \n\t" - - "2: \n\t" /* outer loop (k >= num_words) */ - "movs %[r7], %[r6] \n\t" /* r7 = k */ - "mov %[r0], r8 \n\t" /* r0 = (num_words - 1) * 4 */ - "subs %[r7], %[r0] \n\t" /* r7 = i = k - (num_words - 1) (times 4) */ - - "3: \n\t" /* inner loop */ - "mov r10, %[r3] \n\t" - "mov r11, %[r4] \n\t" - "mov r12, %[r5] \n\t" - "mov r14, %[r6] \n\t" - "subs %[r0], %[r6], %[r7] \n\t" /* r0 = k - i */ - - "ldr %[r4], [%[r2], %[r0]] \n\t" /* r4 = right[k - i] */ - "ldr %[r0], [%[r1], %[r7]] \n\t" /* r0 = left[i] */ - - "lsrs %[r3], %[r0], #16 \n\t" /* r3 = a1 */ - "uxth %[r0], %[r0] \n\t" /* r0 = a0 */ - - "lsrs %[r5], %[r4], #16 \n\t" /* r5 = b1 */ - "uxth %[r4], %[r4] \n\t" /* r4 = b0 */ - - "movs %[r6], %[r3] \n\t" /* r6 = a1 */ - "muls %[r6], %[r5], %[r6] \n\t" /* r6 = a1 * b1 */ - "muls %[r3], %[r4], %[r3] \n\t" /* r3 = b0 * a1 */ - "muls %[r5], %[r0], %[r5] \n\t" /* r5 = a0 * b1 */ - "muls %[r0], %[r4], %[r0] \n\t" /* r0 = a0 * b0 */ - - /* Add middle terms */ - "lsls %[r4], %[r3], #16 \n\t" - "lsrs %[r3], %[r3], #16 \n\t" - "adds %[r0], %[r4] \n\t" - "adcs %[r6], %[r3] \n\t" - - "lsls %[r4], %[r5], #16 \n\t" - "lsrs %[r5], %[r5], #16 \n\t" - "adds %[r0], %[r4] \n\t" - "adcs %[r6], %[r5] \n\t" - - "mov %[r3], r10\n\t" - "mov %[r4], r11\n\t" - "mov %[r5], r12\n\t" - "adds %[r3], %[r0] \n\t" /* add low word to c0 */ - "adcs %[r4], %[r6] \n\t" /* add high word to c1, including carry */ - "movs %[r0], #0 \n\t" /* r0 = 0 (does not affect carry bit) */ - "adcs %[r5], %[r0] \n\t" /* add carry to c2 */ - - "mov %[r6], r14\n\t" /* r6 = k */ - - "adds %[r7], #4 \n\t" /* i += 4 */ - "cmp %[r7], r8 \n\t" /* i > (num_words - 1) (times 4)? */ - "bgt 4f \n\t" /* if so, exit the loop */ - "cmp %[r7], %[r6] \n\t" /* i <= k? */ - "ble 3b \n\t" /* if so, continue looping */ - - "4: \n\t" /* end inner loop */ - - "ldr %[r0], [sp, #0] \n\t" /* r0 = result */ - - "str %[r3], [%[r0], %[r6]] \n\t" /* result[k] = c0 */ - "mov %[r3], %[r4] \n\t" /* c0 = c1 */ - "mov %[r4], %[r5] \n\t" /* c1 = c2 */ - "movs %[r5], #0 \n\t" /* c2 = 0 */ - "adds %[r6], #4 \n\t" /* k += 4 */ - "cmp %[r6], r8 \n\t" /* k <= (num_words - 1) (times 4) ? */ - "ble 1b \n\t" /* if so, loop back, start with i = 0 */ - "cmp %[r6], r9 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */ - "ble 2b \n\t" /* if so, loop back, with i = (k + 1) - num_words */ - /* end outer loop */ - - "str %[r3], [%[r0], %[r6]] \n\t" /* result[num_words * 2 - 1] = c0 */ - "pop {%[r0]} \n\t" /* pop result off the stack */ - - ".syntax divided \n\t" - : [r3] "+l" (num_words), [r4] "=&l" (r4), - [r5] "=&l" (r5), [r6] "=&l" (r6), [r7] "=&l" (r7) - : [r0] "l" (result), [r1] "l" (left), [r2] "l" (right) - : "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" - ); -#endif -} -#define asm_mult 1 -#endif - -#if uECC_SQUARE_FUNC -#if !asm_square -uECC_VLI_API void uECC_vli_square(uECC_word_t *result, - const uECC_word_t *left, - wordcount_t num_words) { -#if (uECC_PLATFORM != uECC_arm_thumb) - uint32_t c0 = 0; - uint32_t c1 = 0; - uint32_t c2 = 0; - uint32_t k = 0; - uint32_t i, tt; - uint32_t t0, t1; - - __asm__ volatile ( - ".syntax unified \n\t" - - "1: \n\t" /* outer loop (k < num_words) */ - "movs %[i], #0 \n\t" /* i = 0 */ - "b 3f \n\t" - - "2: \n\t" /* outer loop (k >= num_words) */ - "movs %[i], %[k] \n\t" /* i = k */ - "subs %[i], %[last_word] \n\t" /* i = k - (num_words - 1) (times 4) */ - - "3: \n\t" /* inner loop */ - "subs %[tt], %[k], %[i] \n\t" /* tt = k-i */ - - "ldr %[t1], [%[left], %[tt]] \n\t" /* t1 = left[k - i] */ - "ldr %[t0], [%[left], %[i]] \n\t" /* t0 = left[i] */ - - "umull %[t0], %[t1], %[t0], %[t1] \n\t" /* (t0, t1) = left[i] * right[k - i] */ - - "cmp %[i], %[tt] \n\t" /* (i < k - i) ? */ - "bge 4f \n\t" /* if i >= k - i, skip */ - "adds %[c0], %[c0], %[t0] \n\t" /* add low word to c0 */ - "adcs %[c1], %[c1], %[t1] \n\t" /* add high word to c1, including carry */ - "adcs %[c2], %[c2], #0 \n\t" /* add carry to c2 */ - - "4: \n\t" - "adds %[c0], %[c0], %[t0] \n\t" /* add low word to c0 */ - "adcs %[c1], %[c1], %[t1] \n\t" /* add high word to c1, including carry */ - "adcs %[c2], %[c2], #0 \n\t" /* add carry to c2 */ - - "adds %[i], #4 \n\t" /* i += 4 */ - "cmp %[i], %[k] \n\t" /* i >= k? */ - "bge 5f \n\t" /* if so, exit the loop */ - "subs %[tt], %[k], %[i] \n\t" /* tt = k - i */ - "cmp %[i], %[tt] \n\t" /* i <= k - i? */ - "ble 3b \n\t" /* if so, continue looping */ - - "5: \n\t" /* end inner loop */ - - "str %[c0], [%[result], %[k]] \n\t" /* result[k] = c0 */ - "mov %[c0], %[c1] \n\t" /* c0 = c1 */ - "mov %[c1], %[c2] \n\t" /* c1 = c2 */ - "movs %[c2], #0 \n\t" /* c2 = 0 */ - "adds %[k], #4 \n\t" /* k += 4 */ - "cmp %[k], %[last_word] \n\t" /* k <= (num_words - 1) (times 4) ? */ - "ble 1b \n\t" /* if so, loop back, start with i = 0 */ - "cmp %[k], %[last_word], lsl #1 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */ - "ble 2b \n\t" /* if so, loop back, start with i = (k + 1) - num_words */ - /* end outer loop */ - - "str %[c0], [%[result], %[k]] \n\t" /* result[num_words * 2 - 1] = c0 */ - RESUME_SYNTAX - : [c0] "+r" (c0), [c1] "+r" (c1), [c2] "+r" (c2), - [k] "+r" (k), [i] "=&r" (i), [tt] "=&r" (tt), [t0] "=&r" (t0), [t1] "=&r" (t1) - : [result] "r" (result), [left] "r" (left), [last_word] "r" ((num_words - 1) * 4) - : "cc", "memory" - ); - -#else - uint32_t r3, r4, r5, r6, r7; - - __asm__ volatile ( - ".syntax unified \n\t" - "subs %[r2], #1 \n\t" /* r2 = num_words - 1 */ - "lsls %[r2], #2 \n\t" /* r2 = (num_words - 1) * 4 */ - "mov r8, %[r2] \n\t" /* r8 = (num_words - 1) * 4 */ - "lsls %[r2], #1 \n\t" /* r2 = (num_words - 1) * 8 */ - "mov r9, %[r2] \n\t" /* r9 = (num_words - 1) * 8 */ - "movs %[r2], #0 \n\t" /* c0 = 0 */ - "movs %[r3], #0 \n\t" /* c1 = 0 */ - "movs %[r4], #0 \n\t" /* c2 = 0 */ - "movs %[r5], #0 \n\t" /* k = 0 */ - - "push {%[r0]} \n\t" /* keep result on the stack */ - - "1: \n\t" /* outer loop (k < num_words) */ - "movs %[r6], #0 \n\t" /* r6 = i = 0 */ - "b 3f \n\t" - - "2: \n\t" /* outer loop (k >= num_words) */ - "movs %[r6], %[r5] \n\t" /* r6 = k */ - "mov %[r0], r8 \n\t" /* r0 = (num_words - 1) * 4 */ - "subs %[r6], %[r0] \n\t" /* r6 = i = k - (num_words - 1) (times 4) */ - - "3: \n\t" /* inner loop */ - "mov r10, %[r2] \n\t" - "mov r11, %[r3] \n\t" - "mov r12, %[r4] \n\t" - "mov r14, %[r5] \n\t" - "subs %[r7], %[r5], %[r6] \n\t" /* r7 = k - i */ - - "ldr %[r3], [%[r1], %[r7]] \n\t" /* r3 = left[k - i] */ - "ldr %[r0], [%[r1], %[r6]] \n\t" /* r0 = left[i] */ - - "lsrs %[r2], %[r0], #16 \n\t" /* r2 = a1 */ - "uxth %[r0], %[r0] \n\t" /* r0 = a0 */ - - "lsrs %[r4], %[r3], #16 \n\t" /* r4 = b1 */ - "uxth %[r3], %[r3] \n\t" /* r3 = b0 */ - - "movs %[r5], %[r2] \n\t" /* r5 = a1 */ - "muls %[r5], %[r4], %[r5] \n\t" /* r5 = a1 * b1 */ - "muls %[r2], %[r3], %[r2] \n\t" /* r2 = b0 * a1 */ - "muls %[r4], %[r0], %[r4] \n\t" /* r4 = a0 * b1 */ - "muls %[r0], %[r3], %[r0] \n\t" /* r0 = a0 * b0 */ - - /* Add middle terms */ - "lsls %[r3], %[r2], #16 \n\t" - "lsrs %[r2], %[r2], #16 \n\t" - "adds %[r0], %[r3] \n\t" - "adcs %[r5], %[r2] \n\t" - - "lsls %[r3], %[r4], #16 \n\t" - "lsrs %[r4], %[r4], #16 \n\t" - "adds %[r0], %[r3] \n\t" - "adcs %[r5], %[r4] \n\t" - - /* Add to acc, doubling if necessary */ - "mov %[r2], r10\n\t" - "mov %[r3], r11\n\t" - "mov %[r4], r12\n\t" - - "cmp %[r6], %[r7] \n\t" /* (i < k - i) ? */ - "bge 4f \n\t" /* if i >= k - i, skip */ - "movs %[r7], #0 \n\t" /* r7 = 0 */ - "adds %[r2], %[r0] \n\t" /* add low word to c0 */ - "adcs %[r3], %[r5] \n\t" /* add high word to c1, including carry */ - "adcs %[r4], %[r7] \n\t" /* add carry to c2 */ - "4: \n\t" - "movs %[r7], #0 \n\t" /* r7 = 0 */ - "adds %[r2], %[r0] \n\t" /* add low word to c0 */ - "adcs %[r3], %[r5] \n\t" /* add high word to c1, including carry */ - "adcs %[r4], %[r7] \n\t" /* add carry to c2 */ - - "mov %[r5], r14\n\t" /* r5 = k */ - - "adds %[r6], #4 \n\t" /* i += 4 */ - "cmp %[r6], %[r5] \n\t" /* i >= k? */ - "bge 5f \n\t" /* if so, exit the loop */ - "subs %[r7], %[r5], %[r6] \n\t" /* r7 = k - i */ - "cmp %[r6], %[r7] \n\t" /* i <= k - i? */ - "ble 3b \n\t" /* if so, continue looping */ - - "5: \n\t" /* end inner loop */ - - "ldr %[r0], [sp, #0] \n\t" /* r0 = result */ - - "str %[r2], [%[r0], %[r5]] \n\t" /* result[k] = c0 */ - "mov %[r2], %[r3] \n\t" /* c0 = c1 */ - "mov %[r3], %[r4] \n\t" /* c1 = c2 */ - "movs %[r4], #0 \n\t" /* c2 = 0 */ - "adds %[r5], #4 \n\t" /* k += 4 */ - "cmp %[r5], r8 \n\t" /* k <= (num_words - 1) (times 4) ? */ - "ble 1b \n\t" /* if so, loop back, start with i = 0 */ - "cmp %[r5], r9 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */ - "ble 2b \n\t" /* if so, loop back, with i = (k + 1) - num_words */ - /* end outer loop */ - - "str %[r2], [%[r0], %[r5]] \n\t" /* result[num_words * 2 - 1] = c0 */ - "pop {%[r0]} \n\t" /* pop result off the stack */ - - ".syntax divided \n\t" - : [r2] "+l" (num_words), [r3] "=&l" (r3), [r4] "=&l" (r4), - [r5] "=&l" (r5), [r6] "=&l" (r6), [r7] "=&l" (r7) - : [r0] "l" (result), [r1] "l" (left) - : "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" - ); -#endif -} -#define asm_square 1 -#endif -#endif /* uECC_SQUARE_FUNC */ - -#endif /* _UECC_ASM_ARM_H_ */ diff --git a/lib/micro-ecc/asm_arm_mult_square.inc b/lib/micro-ecc/asm_arm_mult_square.inc deleted file mode 100644 index 8907fc1858d..00000000000 --- a/lib/micro-ecc/asm_arm_mult_square.inc +++ /dev/null @@ -1,2311 +0,0 @@ -/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_ASM_ARM_MULT_SQUARE_H_ -#define _UECC_ASM_ARM_MULT_SQUARE_H_ - -#define FAST_MULT_ASM_5 \ - "push {r3} \n\t" \ - "add r0, 12 \n\t" \ - "add r2, 12 \n\t" \ - "ldmia r1!, {r3,r4} \n\t" \ - "ldmia r2!, {r6,r7} \n\t" \ - \ - "umull r11, r12, r3, r6 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umull r11, r9, r3, r7 \n\t" \ - "adds r12, r12, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r11, r14, r4, r6 \n\t" \ - "adds r12, r12, r11 \n\t" \ - "adcs r9, r9, r14 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "umull r12, r14, r4, r7 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adc r10, r10, r14 \n\t" \ - "stmia r0!, {r9, r10} \n\t" \ - \ - "sub r0, 28 \n\t" \ - "sub r2, 20 \n\t" \ - "ldmia r2!, {r6,r7,r8} \n\t" \ - "ldmia r1!, {r5} \n\t" \ - \ - "umull r11, r12, r3, r6 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umull r11, r9, r3, r7 \n\t" \ - "adds r12, r12, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r11, r14, r4, r6 \n\t" \ - "adds r12, r12, r11 \n\t" \ - "adcs r9, r9, r14 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "mov r11, #0 \n\t" \ - "umull r12, r14, r3, r8 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r4, r7 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r5, r6 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "stmia r0!, {r9} \n\t" \ - \ - "ldmia r1!, {r3} \n\t" \ - "mov r12, #0 \n\t" \ - "umull r14, r9, r4, r8 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r14, r9, r5, r7 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r14, r9, r3, r6 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "ldr r14, [r0] \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, #0 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r10} \n\t" \ - \ - "ldmia r1!, {r4} \n\t" \ - "mov r14, #0 \n\t" \ - "umull r9, r10, r5, r8 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "umull r9, r10, r3, r7 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "umull r9, r10, r4, r6 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "ldr r9, [r0] \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, #0 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "ldmia r2!, {r6} \n\t" \ - "mov r9, #0 \n\t" \ - "umull r10, r11, r5, r6 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r10, r11, r3, r8 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r10, r11, r4, r7 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "ldr r10, [r0] \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, #0 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "ldmia r2!, {r7} \n\t" \ - "mov r10, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r3, r6 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "ldr r11, [r0] \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r14} \n\t" \ - \ - "mov r11, #0 \n\t" \ - "umull r12, r14, r3, r7 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r4, r6 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "stmia r0!, {r9} \n\t" \ - \ - "umull r14, r9, r4, r7 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adc r11, r11, r9 \n\t" \ - "stmia r0!, {r10, r11} \n\t" \ - "pop {r3} \n\t" - -#define FAST_MULT_ASM_5_TO_6 \ - "cmp r3, #5 \n\t" \ - "beq 1f \n\t" \ - \ - /* r4 = left high, r5 = right high */ \ - "ldr r4, [r1] \n\t" \ - "ldr r5, [r2] \n\t" \ - \ - "sub r0, #20 \n\t" \ - "sub r1, #20 \n\t" \ - "sub r2, #20 \n\t" \ - \ - "ldr r6, [r0] \n\t" \ - "ldr r7, [r1], #4 \n\t" \ - "ldr r8, [r2], #4 \n\t" \ - "mov r14, #0 \n\t" \ - "umull r9, r10, r4, r8 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r9, r9, r6 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "adds r9, r9, r11 \n\t" \ - "adcs r10, r10, r12 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "str r9, [r0], #4 \n\t" \ - \ - "ldr r6, [r0] \n\t" \ - "adds r10, r10, r6 \n\t" \ - "adcs r14, r14, #0 \n\t" \ - "ldr r7, [r1], #4 \n\t" \ - "ldr r8, [r2], #4 \n\t" \ - "mov r9, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r10, r10, r11 \n\t" \ - "adcs r14, r14, r12 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r10, r10, r11 \n\t" \ - "adcs r14, r14, r12 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "str r10, [r0], #4 \n\t" \ - \ - "ldr r6, [r0] \n\t" \ - "adds r14, r14, r6 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - "ldr r7, [r1], #4 \n\t" \ - "ldr r8, [r2], #4 \n\t" \ - "mov r10, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "str r14, [r0], #4 \n\t" \ - \ - "ldr r6, [r0] \n\t" \ - "adds r9, r9, r6 \n\t" \ - "adcs r10, r10, #0 \n\t" \ - "ldr r7, [r1], #4 \n\t" \ - "ldr r8, [r2], #4 \n\t" \ - "mov r14, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r9, r9, r11 \n\t" \ - "adcs r10, r10, r12 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r9, r9, r11 \n\t" \ - "adcs r10, r10, r12 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "str r9, [r0], #4 \n\t" \ - \ - "ldr r6, [r0] \n\t" \ - "adds r10, r10, r6 \n\t" \ - "adcs r14, r14, #0 \n\t" \ - /* skip past already-loaded (r4, r5) */ \ - "ldr r7, [r1], #8 \n\t" \ - "ldr r8, [r2], #8 \n\t" \ - "mov r9, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r10, r10, r11 \n\t" \ - "adcs r14, r14, r12 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r10, r10, r11 \n\t" \ - "adcs r14, r14, r12 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "str r10, [r0], #4 \n\t" \ - \ - "umull r11, r12, r4, r5 \n\t" \ - "adds r11, r11, r14 \n\t" \ - "adc r12, r12, r9 \n\t" \ - "stmia r0!, {r11, r12} \n\t" - -#define FAST_MULT_ASM_6 \ - "push {r3} \n\t" \ - "add r0, 12 \n\t" \ - "add r2, 12 \n\t" \ - "ldmia r1!, {r3,r4,r5} \n\t" \ - "ldmia r2!, {r6,r7,r8} \n\t" \ - \ - "umull r11, r12, r3, r6 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umull r11, r9, r3, r7 \n\t" \ - "adds r12, r12, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r11, r14, r4, r6 \n\t" \ - "adds r12, r12, r11 \n\t" \ - "adcs r9, r9, r14 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "mov r11, #0 \n\t" \ - "umull r12, r14, r3, r8 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r4, r7 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r5, r6 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "stmia r0!, {r9} \n\t" \ - \ - "mov r12, #0 \n\t" \ - "umull r14, r9, r4, r8 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r14, r9, r5, r7 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r10} \n\t" \ - \ - "umull r9, r10, r5, r8 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adc r12, r12, r10 \n\t" \ - "stmia r0!, {r11, r12} \n\t" \ - \ - "sub r0, 36 \n\t" \ - "sub r2, 24 \n\t" \ - "ldmia r2!, {r6,r7,r8} \n\t" \ - \ - "umull r11, r12, r3, r6 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umull r11, r9, r3, r7 \n\t" \ - "adds r12, r12, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r11, r14, r4, r6 \n\t" \ - "adds r12, r12, r11 \n\t" \ - "adcs r9, r9, r14 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "mov r11, #0 \n\t" \ - "umull r12, r14, r3, r8 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r4, r7 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r5, r6 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "stmia r0!, {r9} \n\t" \ - \ - "ldmia r1!, {r3} \n\t" \ - "mov r12, #0 \n\t" \ - "umull r14, r9, r4, r8 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r14, r9, r5, r7 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r14, r9, r3, r6 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "ldr r14, [r0] \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, #0 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r10} \n\t" \ - \ - "ldmia r1!, {r4} \n\t" \ - "mov r14, #0 \n\t" \ - "umull r9, r10, r5, r8 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "umull r9, r10, r3, r7 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "umull r9, r10, r4, r6 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "ldr r9, [r0] \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, #0 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "ldmia r1!, {r5} \n\t" \ - "mov r9, #0 \n\t" \ - "umull r10, r11, r3, r8 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r10, r11, r4, r7 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r10, r11, r5, r6 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "ldr r10, [r0] \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, #0 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "ldmia r2!, {r6} \n\t" \ - "mov r10, #0 \n\t" \ - "umull r11, r12, r3, r6 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "ldr r11, [r0] \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r14} \n\t" \ - \ - "ldmia r2!, {r7} \n\t" \ - "mov r11, #0 \n\t" \ - "umull r12, r14, r3, r7 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r4, r6 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r5, r8 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "ldr r12, [r0] \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, #0 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "stmia r0!, {r9} \n\t" \ - \ - "ldmia r2!, {r8} \n\t" \ - "mov r12, #0 \n\t" \ - "umull r14, r9, r3, r8 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r14, r9, r4, r7 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r14, r9, r5, r6 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "ldr r14, [r0] \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, #0 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r10} \n\t" \ - \ - "mov r14, #0 \n\t" \ - "umull r9, r10, r4, r8 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "umull r9, r10, r5, r7 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "umull r10, r11, r5, r8 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adc r14, r14, r11 \n\t" \ - "stmia r0!, {r12, r14} \n\t" \ - "pop {r3} \n\t" - -#define FAST_MULT_ASM_6_TO_7 \ - "cmp r3, #6 \n\t" \ - "beq 1f \n\t" \ - \ - /* r4 = left high, r5 = right high */ \ - "ldr r4, [r1] \n\t" \ - "ldr r5, [r2] \n\t" \ - \ - "sub r0, #24 \n\t" \ - "sub r1, #24 \n\t" \ - "sub r2, #24 \n\t" \ - \ - "ldr r6, [r0] \n\t" \ - "ldr r7, [r1], #4 \n\t" \ - "ldr r8, [r2], #4 \n\t" \ - "mov r14, #0 \n\t" \ - "umull r9, r10, r4, r8 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r9, r9, r6 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "adds r9, r9, r11 \n\t" \ - "adcs r10, r10, r12 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "str r9, [r0], #4 \n\t" \ - \ - "ldr r6, [r0] \n\t" \ - "adds r10, r10, r6 \n\t" \ - "adcs r14, r14, #0 \n\t" \ - "ldr r7, [r1], #4 \n\t" \ - "ldr r8, [r2], #4 \n\t" \ - "mov r9, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r10, r10, r11 \n\t" \ - "adcs r14, r14, r12 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r10, r10, r11 \n\t" \ - "adcs r14, r14, r12 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "str r10, [r0], #4 \n\t" \ - \ - "ldr r6, [r0] \n\t" \ - "adds r14, r14, r6 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - "ldr r7, [r1], #4 \n\t" \ - "ldr r8, [r2], #4 \n\t" \ - "mov r10, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "str r14, [r0], #4 \n\t" \ - \ - "ldr r6, [r0] \n\t" \ - "adds r9, r9, r6 \n\t" \ - "adcs r10, r10, #0 \n\t" \ - "ldr r7, [r1], #4 \n\t" \ - "ldr r8, [r2], #4 \n\t" \ - "mov r14, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r9, r9, r11 \n\t" \ - "adcs r10, r10, r12 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r9, r9, r11 \n\t" \ - "adcs r10, r10, r12 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "str r9, [r0], #4 \n\t" \ - \ - "ldr r6, [r0] \n\t" \ - "adds r10, r10, r6 \n\t" \ - "adcs r14, r14, #0 \n\t" \ - "ldr r7, [r1], #4 \n\t" \ - "ldr r8, [r2], #4 \n\t" \ - "mov r9, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r10, r10, r11 \n\t" \ - "adcs r14, r14, r12 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r10, r10, r11 \n\t" \ - "adcs r14, r14, r12 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "str r10, [r0], #4 \n\t" \ - \ - "ldr r6, [r0] \n\t" \ - "adds r14, r14, r6 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - /* skip past already-loaded (r4, r5) */ \ - "ldr r7, [r1], #8 \n\t" \ - "ldr r8, [r2], #8 \n\t" \ - "mov r10, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "str r14, [r0], #4 \n\t" \ - \ - "umull r11, r12, r4, r5 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adc r12, r12, r10 \n\t" \ - "stmia r0!, {r11, r12} \n\t" - -#define FAST_MULT_ASM_7 \ - "push {r3} \n\t" \ - "add r0, 24 \n\t" \ - "add r2, 24 \n\t" \ - "ldmia r1!, {r3} \n\t" \ - "ldmia r2!, {r6} \n\t" \ - \ - "umull r9, r10, r3, r6 \n\t" \ - "stmia r0!, {r9, r10} \n\t" \ - \ - "sub r0, 20 \n\t" \ - "sub r2, 16 \n\t" \ - "ldmia r2!, {r6, r7, r8} \n\t" \ - "ldmia r1!, {r4, r5} \n\t" \ - \ - "umull r9, r10, r3, r6 \n\t" \ - "stmia r0!, {r9} \n\t" \ - \ - "mov r14, #0 \n\t" \ - "umull r9, r12, r3, r7 \n\t" \ - "adds r10, r10, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r9, r11, r4, r6 \n\t" \ - "adds r10, r10, r9 \n\t" \ - "adcs r12, r12, r11 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "stmia r0!, {r10} \n\t" \ - \ - "mov r9, #0 \n\t" \ - "umull r10, r11, r3, r8 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r10, r11, r4, r7 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r10, r11, r5, r6 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "ldmia r1!, {r3} \n\t" \ - "mov r10, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r3, r6 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "ldr r11, [r0] \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r14} \n\t" \ - \ - "ldmia r2!, {r6} \n\t" \ - "mov r11, #0 \n\t" \ - "umull r12, r14, r4, r6 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r5, r8 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r3, r7 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "ldr r12, [r0] \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, #0 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "stmia r0!, {r9} \n\t" \ - \ - "mov r12, #0 \n\t" \ - "umull r14, r9, r5, r6 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r14, r9, r3, r8 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r10} \n\t" \ - \ - "umull r9, r10, r3, r6 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adc r12, r12, r10 \n\t" \ - "stmia r0!, {r11, r12} \n\t" \ - \ - "sub r0, 44 \n\t" \ - "sub r1, 16 \n\t" \ - "sub r2, 28 \n\t" \ - "ldmia r1!, {r3,r4,r5} \n\t" \ - "ldmia r2!, {r6,r7,r8} \n\t" \ - \ - "umull r9, r10, r3, r6 \n\t" \ - "stmia r0!, {r9} \n\t" \ - \ - "mov r14, #0 \n\t" \ - "umull r9, r12, r3, r7 \n\t" \ - "adds r10, r10, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r9, r11, r4, r6 \n\t" \ - "adds r10, r10, r9 \n\t" \ - "adcs r12, r12, r11 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "stmia r0!, {r10} \n\t" \ - \ - "mov r9, #0 \n\t" \ - "umull r10, r11, r3, r8 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r10, r11, r4, r7 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r10, r11, r5, r6 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "ldmia r1!, {r3} \n\t" \ - "mov r10, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r3, r6 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "ldr r11, [r0] \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r14} \n\t" \ - \ - "ldmia r1!, {r4} \n\t" \ - "mov r11, #0 \n\t" \ - "umull r12, r14, r5, r8 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r3, r7 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r4, r6 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "ldr r12, [r0] \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, #0 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "stmia r0!, {r9} \n\t" \ - \ - "ldmia r1!, {r5} \n\t" \ - "mov r12, #0 \n\t" \ - "umull r14, r9, r3, r8 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r14, r9, r4, r7 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r14, r9, r5, r6 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "ldr r14, [r0] \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, #0 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r10} \n\t" \ - \ - "ldmia r1!, {r3} \n\t" \ - "mov r14, #0 \n\t" \ - "umull r9, r10, r4, r8 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "umull r9, r10, r5, r7 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "umull r9, r10, r3, r6 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "ldr r9, [r0] \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, #0 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "ldmia r2!, {r6} \n\t" \ - "mov r9, #0 \n\t" \ - "umull r10, r11, r4, r6 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r10, r11, r5, r8 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r10, r11, r3, r7 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "ldr r10, [r0] \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, #0 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "ldmia r2!, {r7} \n\t" \ - "mov r10, #0 \n\t" \ - "umull r11, r12, r4, r7 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r5, r6 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r3, r8 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "ldr r11, [r0] \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r14} \n\t" \ - \ - "ldmia r2!, {r8} \n\t" \ - "mov r11, #0 \n\t" \ - "umull r12, r14, r4, r8 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r5, r7 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r3, r6 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "ldr r12, [r0] \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, #0 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "stmia r0!, {r9} \n\t" \ - \ - "ldmia r2!, {r6} \n\t" \ - "mov r12, #0 \n\t" \ - "umull r14, r9, r4, r6 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r14, r9, r5, r8 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r14, r9, r3, r7 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "ldr r14, [r0] \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, #0 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r10} \n\t" \ - \ - "mov r14, #0 \n\t" \ - "umull r9, r10, r5, r6 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "umull r9, r10, r3, r8 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "umull r10, r11, r3, r6 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adc r14, r14, r11 \n\t" \ - "stmia r0!, {r12, r14} \n\t" \ - "pop {r3} \n\t" - -#define FAST_MULT_ASM_7_TO_8 \ - "cmp r3, #7 \n\t" \ - "beq 1f \n\t" \ - \ - /* r4 = left high, r5 = right high */ \ - "ldr r4, [r1] \n\t" \ - "ldr r5, [r2] \n\t" \ - \ - "sub r0, #28 \n\t" \ - "sub r1, #28 \n\t" \ - "sub r2, #28 \n\t" \ - \ - "ldr r6, [r0] \n\t" \ - "ldr r7, [r1], #4 \n\t" \ - "ldr r8, [r2], #4 \n\t" \ - "mov r14, #0 \n\t" \ - "umull r9, r10, r4, r8 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r9, r9, r6 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "adds r9, r9, r11 \n\t" \ - "adcs r10, r10, r12 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "str r9, [r0], #4 \n\t" \ - \ - "ldr r6, [r0] \n\t" \ - "adds r10, r10, r6 \n\t" \ - "adcs r14, r14, #0 \n\t" \ - "ldr r7, [r1], #4 \n\t" \ - "ldr r8, [r2], #4 \n\t" \ - "mov r9, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r10, r10, r11 \n\t" \ - "adcs r14, r14, r12 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r10, r10, r11 \n\t" \ - "adcs r14, r14, r12 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "str r10, [r0], #4 \n\t" \ - \ - "ldr r6, [r0] \n\t" \ - "adds r14, r14, r6 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - "ldr r7, [r1], #4 \n\t" \ - "ldr r8, [r2], #4 \n\t" \ - "mov r10, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "str r14, [r0], #4 \n\t" \ - \ - "ldr r6, [r0] \n\t" \ - "adds r9, r9, r6 \n\t" \ - "adcs r10, r10, #0 \n\t" \ - "ldr r7, [r1], #4 \n\t" \ - "ldr r8, [r2], #4 \n\t" \ - "mov r14, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r9, r9, r11 \n\t" \ - "adcs r10, r10, r12 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r9, r9, r11 \n\t" \ - "adcs r10, r10, r12 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "str r9, [r0], #4 \n\t" \ - \ - "ldr r6, [r0] \n\t" \ - "adds r10, r10, r6 \n\t" \ - "adcs r14, r14, #0 \n\t" \ - "ldr r7, [r1], #4 \n\t" \ - "ldr r8, [r2], #4 \n\t" \ - "mov r9, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r10, r10, r11 \n\t" \ - "adcs r14, r14, r12 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r10, r10, r11 \n\t" \ - "adcs r14, r14, r12 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "str r10, [r0], #4 \n\t" \ - \ - "ldr r6, [r0] \n\t" \ - "adds r14, r14, r6 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - "ldr r7, [r1], #4 \n\t" \ - "ldr r8, [r2], #4 \n\t" \ - "mov r10, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "str r14, [r0], #4 \n\t" \ - \ - "ldr r6, [r0] \n\t" \ - "adds r9, r9, r6 \n\t" \ - "adcs r10, r10, #0 \n\t" \ - /* skip past already-loaded (r4, r5) */ \ - "ldr r7, [r1], #8 \n\t" \ - "ldr r8, [r2], #8 \n\t" \ - "mov r14, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r9, r9, r11 \n\t" \ - "adcs r10, r10, r12 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r9, r9, r11 \n\t" \ - "adcs r10, r10, r12 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "str r9, [r0], #4 \n\t" \ - \ - "umull r11, r12, r4, r5 \n\t" \ - "adds r11, r11, r10 \n\t" \ - "adc r12, r12, r14 \n\t" \ - "stmia r0!, {r11, r12} \n\t" - -#define FAST_MULT_ASM_8 \ - "push {r3} \n\t" \ - "add r0, 24 \n\t" \ - "add r2, 24 \n\t" \ - "ldmia r1!, {r3,r4} \n\t" \ - "ldmia r2!, {r6,r7} \n\t" \ - \ - "umull r11, r12, r3, r6 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umull r11, r9, r3, r7 \n\t" \ - "adds r12, r12, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r11, r14, r4, r6 \n\t" \ - "adds r12, r12, r11 \n\t" \ - "adcs r9, r9, r14 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "umull r12, r14, r4, r7 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adc r10, r10, r14 \n\t" \ - "stmia r0!, {r9, r10} \n\t" \ - \ - "sub r0, 28 \n\t" \ - "sub r2, 20 \n\t" \ - "ldmia r2!, {r6,r7,r8} \n\t" \ - "ldmia r1!, {r5} \n\t" \ - \ - "umull r11, r12, r3, r6 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umull r11, r9, r3, r7 \n\t" \ - "adds r12, r12, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r11, r14, r4, r6 \n\t" \ - "adds r12, r12, r11 \n\t" \ - "adcs r9, r9, r14 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "mov r11, #0 \n\t" \ - "umull r12, r14, r3, r8 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r4, r7 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r5, r6 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "stmia r0!, {r9} \n\t" \ - \ - "ldmia r1!, {r3} \n\t" \ - "mov r12, #0 \n\t" \ - "umull r14, r9, r4, r8 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r14, r9, r5, r7 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r14, r9, r3, r6 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "ldr r14, [r0] \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, #0 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r10} \n\t" \ - \ - "ldmia r1!, {r4} \n\t" \ - "mov r14, #0 \n\t" \ - "umull r9, r10, r5, r8 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "umull r9, r10, r3, r7 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "umull r9, r10, r4, r6 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "ldr r9, [r0] \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, #0 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "ldmia r2!, {r6} \n\t" \ - "mov r9, #0 \n\t" \ - "umull r10, r11, r5, r6 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r10, r11, r3, r8 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r10, r11, r4, r7 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "ldr r10, [r0] \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, #0 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "ldmia r2!, {r7} \n\t" \ - "mov r10, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r3, r6 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "ldr r11, [r0] \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r14} \n\t" \ - \ - "mov r11, #0 \n\t" \ - "umull r12, r14, r3, r7 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r4, r6 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "stmia r0!, {r9} \n\t" \ - \ - "umull r14, r9, r4, r7 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adc r11, r11, r9 \n\t" \ - "stmia r0!, {r10, r11} \n\t" \ - \ - "sub r0, 52 \n\t" \ - "sub r1, 20 \n\t" \ - "sub r2, 32 \n\t" \ - "ldmia r1!, {r3,r4,r5} \n\t" \ - "ldmia r2!, {r6,r7,r8} \n\t" \ - \ - "umull r11, r12, r3, r6 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umull r11, r9, r3, r7 \n\t" \ - "adds r12, r12, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r11, r14, r4, r6 \n\t" \ - "adds r12, r12, r11 \n\t" \ - "adcs r9, r9, r14 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "mov r11, #0 \n\t" \ - "umull r12, r14, r3, r8 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r4, r7 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r5, r6 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "stmia r0!, {r9} \n\t" \ - \ - "ldmia r1!, {r3} \n\t" \ - "mov r12, #0 \n\t" \ - "umull r14, r9, r4, r8 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r14, r9, r5, r7 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r14, r9, r3, r6 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "ldr r14, [r0] \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, #0 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r10} \n\t" \ - \ - "ldmia r1!, {r4} \n\t" \ - "mov r14, #0 \n\t" \ - "umull r9, r10, r5, r8 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "umull r9, r10, r3, r7 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "umull r9, r10, r4, r6 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "ldr r9, [r0] \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, #0 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "ldmia r1!, {r5} \n\t" \ - "mov r9, #0 \n\t" \ - "umull r10, r11, r3, r8 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r10, r11, r4, r7 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r10, r11, r5, r6 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "ldr r10, [r0] \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, #0 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "ldmia r1!, {r3} \n\t" \ - "mov r10, #0 \n\t" \ - "umull r11, r12, r4, r8 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r5, r7 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r3, r6 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "ldr r11, [r0] \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r14} \n\t" \ - \ - "ldmia r1!, {r4} \n\t" \ - "mov r11, #0 \n\t" \ - "umull r12, r14, r5, r8 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r3, r7 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r4, r6 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "ldr r12, [r0] \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, #0 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "stmia r0!, {r9} \n\t" \ - \ - "ldmia r2!, {r6} \n\t" \ - "mov r12, #0 \n\t" \ - "umull r14, r9, r5, r6 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r14, r9, r3, r8 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r14, r9, r4, r7 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "ldr r14, [r0] \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, #0 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r10} \n\t" \ - \ - "ldmia r2!, {r7} \n\t" \ - "mov r14, #0 \n\t" \ - "umull r9, r10, r5, r7 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "umull r9, r10, r3, r6 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "umull r9, r10, r4, r8 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "ldr r9, [r0] \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adcs r12, r12, #0 \n\t" \ - "adc r14, r14, #0 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "ldmia r2!, {r8} \n\t" \ - "mov r9, #0 \n\t" \ - "umull r10, r11, r5, r8 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r10, r11, r3, r7 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "umull r10, r11, r4, r6 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "ldr r10, [r0] \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r14, r14, #0 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "ldmia r2!, {r6} \n\t" \ - "mov r10, #0 \n\t" \ - "umull r11, r12, r5, r6 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r3, r8 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r4, r7 \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "ldr r11, [r0] \n\t" \ - "adds r14, r14, r11 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r14} \n\t" \ - \ - "ldmia r2!, {r7} \n\t" \ - "mov r11, #0 \n\t" \ - "umull r12, r14, r5, r7 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r3, r6 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "umull r12, r14, r4, r8 \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, r14 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "ldr r12, [r0] \n\t" \ - "adds r9, r9, r12 \n\t" \ - "adcs r10, r10, #0 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "stmia r0!, {r9} \n\t" \ - \ - "mov r12, #0 \n\t" \ - "umull r14, r9, r3, r7 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r14, r9, r4, r6 \n\t" \ - "adds r10, r10, r14 \n\t" \ - "adcs r11, r11, r9 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r10} \n\t" \ - \ - "umull r9, r10, r4, r7 \n\t" \ - "adds r11, r11, r9 \n\t" \ - "adc r12, r12, r10 \n\t" \ - "stmia r0!, {r11, r12} \n\t" \ - "pop {r3} \n\t" - -#define FAST_SQUARE_ASM_5 \ - "push {r2} \n\t" \ - "ldmia r1!, {r2,r3,r4,r5,r6} \n\t" \ - "push {r1} \n\t" \ - \ - "umull r11, r12, r2, r2 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "mov r9, #0 \n\t" \ - "umull r10, r11, r2, r3 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r8, r11, #0 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r8, r8, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umull r11, r12, r2, r4 \n\t" \ - "adds r11, r11, r11 \n\t" \ - "adcs r12, r12, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "adds r8, r8, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r3, r3 \n\t" \ - "adds r8, r8, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r12, #0 \n\t" \ - "umull r8, r11, r2, r5 \n\t" \ - "umull r1, r14, r3, r4 \n\t" \ - "adds r8, r8, r1 \n\t" \ - "adcs r11, r11, r14 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r11, r11, r11 \n\t" \ - "adc r12, r12, r12 \n\t" \ - "adds r8, r8, r9 \n\t" \ - "adcs r11, r11, r10 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umull r8, r9, r2, r6 \n\t" \ - "umull r1, r14, r3, r5 \n\t" \ - "adds r8, r8, r1 \n\t" \ - "adcs r9, r9, r14 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r9, r9, r9 \n\t" \ - "adc r10, r10, r10 \n\t" \ - "umull r1, r14, r4, r4 \n\t" \ - "adds r8, r8, r1 \n\t" \ - "adcs r9, r9, r14 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "adds r8, r8, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r12, #0 \n\t" \ - "umull r8, r11, r3, r6 \n\t" \ - "umull r1, r14, r4, r5 \n\t" \ - "adds r8, r8, r1 \n\t" \ - "adcs r11, r11, r14 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r11, r11, r11 \n\t" \ - "adc r12, r12, r12 \n\t" \ - "adds r8, r8, r9 \n\t" \ - "adcs r11, r11, r10 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r8, #0 \n\t" \ - "umull r1, r10, r4, r6 \n\t" \ - "adds r1, r1, r1 \n\t" \ - "adcs r10, r10, r10 \n\t" \ - "adc r8, r8, #0 \n\t" \ - "adds r11, r11, r1 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r8, r8, #0 \n\t" \ - "umull r1, r10, r5, r5 \n\t" \ - "adds r11, r11, r1 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r8, r8, #0 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "mov r11, #0 \n\t" \ - "umull r1, r10, r5, r6 \n\t" \ - "adds r1, r1, r1 \n\t" \ - "adcs r10, r10, r10 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "adds r12, r12, r1 \n\t" \ - "adcs r8, r8, r10 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "umull r1, r10, r6, r6 \n\t" \ - "adds r8, r8, r1 \n\t" \ - "adcs r11, r11, r10 \n\t" \ - "stmia r0!, {r8, r11} \n\t" \ - "pop {r1, r2} \n\t" - -#define FAST_SQUARE_ASM_5_TO_6 \ - "cmp r2, #5 \n\t" \ - "beq 1f \n\t" \ - \ - "sub r0, #20 \n\t" \ - "sub r1, #20 \n\t" \ - \ - /* Do off-center multiplication */ \ - "ldmia r1!, {r6,r7,r8,r9,r10,r11} \n\t" \ - "umull r3, r4, r6, r11 \n\t" \ - "umull r6, r5, r7, r11 \n\t" \ - "adds r4, r4, r6 \n\t" \ - "umull r7, r6, r8, r11 \n\t" \ - "adcs r5, r5, r7 \n\t" \ - "umull r8, r7, r9, r11 \n\t" \ - "adcs r6, r6, r8 \n\t" \ - "umull r9, r8, r10, r11 \n\t" \ - "adcs r7, r7, r9 \n\t" \ - "adcs r8, r8, #0 \n\t" \ - \ - /* Multiply by 2 */ \ - "mov r9, #0 \n\t" \ - "adds r3, r3, r3 \n\t" \ - "adcs r4, r4, r4 \n\t" \ - "adcs r5, r5, r5 \n\t" \ - "adcs r6, r6, r6 \n\t" \ - "adcs r7, r7, r7 \n\t" \ - "adcs r8, r8, r8 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - \ - /* Add into previous */ \ - "ldr r14, [r0], #4 \n\t" \ - "adds r3, r3, r14 \n\t" \ - "ldr r14, [r0], #4 \n\t" \ - "adcs r4, r4, r14 \n\t" \ - "ldr r14, [r0], #4 \n\t" \ - "adcs r5, r5, r14 \n\t" \ - "ldr r14, [r0], #4 \n\t" \ - "adcs r6, r6, r14 \n\t" \ - "ldr r14, [r0], #4 \n\t" \ - "adcs r7, r7, r14 \n\t" \ - "adcs r8, r8, #0 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - "sub r0, #20 \n\t" \ - \ - /* Perform center multiplication */ \ - "umlal r8, r9, r11, r11 \n\t" \ - "stmia r0!, {r3,r4,r5,r6,r7,r8,r9} \n\t" - -#define FAST_SQUARE_ASM_6 \ - "push {r2} \n\t" \ - "ldmia r1!, {r2,r3,r4,r5,r6,r7} \n\t" \ - "push {r1} \n\t" \ - \ - "umull r11, r12, r2, r2 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "mov r9, #0 \n\t" \ - "umull r10, r11, r2, r3 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r8, r11, #0 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r8, r8, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umull r11, r12, r2, r4 \n\t" \ - "adds r11, r11, r11 \n\t" \ - "adcs r12, r12, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "adds r8, r8, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r3, r3 \n\t" \ - "adds r8, r8, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r12, #0 \n\t" \ - "umull r8, r11, r2, r5 \n\t" \ - "umull r1, r14, r3, r4 \n\t" \ - "adds r8, r8, r1 \n\t" \ - "adcs r11, r11, r14 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r11, r11, r11 \n\t" \ - "adc r12, r12, r12 \n\t" \ - "adds r8, r8, r9 \n\t" \ - "adcs r11, r11, r10 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umull r8, r9, r2, r6 \n\t" \ - "umull r1, r14, r3, r5 \n\t" \ - "adds r8, r8, r1 \n\t" \ - "adcs r9, r9, r14 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r9, r9, r9 \n\t" \ - "adc r10, r10, r10 \n\t" \ - "umull r1, r14, r4, r4 \n\t" \ - "adds r8, r8, r1 \n\t" \ - "adcs r9, r9, r14 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "adds r8, r8, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r12, #0 \n\t" \ - "umull r8, r11, r2, r7 \n\t" \ - "umull r1, r14, r3, r6 \n\t" \ - "adds r8, r8, r1 \n\t" \ - "adcs r11, r11, r14 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "umull r1, r14, r4, r5 \n\t" \ - "adds r8, r8, r1 \n\t" \ - "adcs r11, r11, r14 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r11, r11, r11 \n\t" \ - "adc r12, r12, r12 \n\t" \ - "adds r8, r8, r9 \n\t" \ - "adcs r11, r11, r10 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umull r8, r9, r3, r7 \n\t" \ - "umull r1, r14, r4, r6 \n\t" \ - "adds r8, r8, r1 \n\t" \ - "adcs r9, r9, r14 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r9, r9, r9 \n\t" \ - "adc r10, r10, r10 \n\t" \ - "umull r1, r14, r5, r5 \n\t" \ - "adds r8, r8, r1 \n\t" \ - "adcs r9, r9, r14 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "adds r8, r8, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r12, #0 \n\t" \ - "umull r8, r11, r4, r7 \n\t" \ - "umull r1, r14, r5, r6 \n\t" \ - "adds r8, r8, r1 \n\t" \ - "adcs r11, r11, r14 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r11, r11, r11 \n\t" \ - "adc r12, r12, r12 \n\t" \ - "adds r8, r8, r9 \n\t" \ - "adcs r11, r11, r10 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r8, #0 \n\t" \ - "umull r1, r10, r5, r7 \n\t" \ - "adds r1, r1, r1 \n\t" \ - "adcs r10, r10, r10 \n\t" \ - "adc r8, r8, #0 \n\t" \ - "adds r11, r11, r1 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r8, r8, #0 \n\t" \ - "umull r1, r10, r6, r6 \n\t" \ - "adds r11, r11, r1 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r8, r8, #0 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "mov r11, #0 \n\t" \ - "umull r1, r10, r6, r7 \n\t" \ - "adds r1, r1, r1 \n\t" \ - "adcs r10, r10, r10 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "adds r12, r12, r1 \n\t" \ - "adcs r8, r8, r10 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "umull r1, r10, r7, r7 \n\t" \ - "adds r8, r8, r1 \n\t" \ - "adcs r11, r11, r10 \n\t" \ - "stmia r0!, {r8, r11} \n\t" \ - "pop {r1, r2} \n\t" - -#define FAST_SQUARE_ASM_6_TO_7 \ - "cmp r2, #6 \n\t" \ - "beq 1f \n\t" \ - \ - "sub r0, #24 \n\t" \ - "sub r1, #24 \n\t" \ - \ - /* Do off-center multiplication */ \ - "ldmia r1!, {r6,r7,r8,r9,r10,r11,r12} \n\t" \ - "umull r3, r4, r6, r12 \n\t" \ - "umull r6, r5, r7, r12 \n\t" \ - "adds r4, r4, r6 \n\t" \ - "umull r7, r6, r8, r12 \n\t" \ - "adcs r5, r5, r7 \n\t" \ - "umull r8, r7, r9, r12 \n\t" \ - "adcs r6, r6, r8 \n\t" \ - "umull r9, r8, r10, r12 \n\t" \ - "adcs r7, r7, r9 \n\t" \ - "umull r10, r9, r11, r12 \n\t" \ - "adcs r8, r8, r10 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - \ - /* Multiply by 2 */ \ - "mov r10, #0 \n\t" \ - "adds r3, r3, r3 \n\t" \ - "adcs r4, r4, r4 \n\t" \ - "adcs r5, r5, r5 \n\t" \ - "adcs r6, r6, r6 \n\t" \ - "adcs r7, r7, r7 \n\t" \ - "adcs r8, r8, r8 \n\t" \ - "adcs r9, r9, r9 \n\t" \ - "adcs r10, r10, #0 \n\t" \ - \ - /* Add into previous */ \ - "ldr r14, [r0], #4 \n\t" \ - "adds r3, r3, r14 \n\t" \ - "ldr r14, [r0], #4 \n\t" \ - "adcs r4, r4, r14 \n\t" \ - "ldr r14, [r0], #4 \n\t" \ - "adcs r5, r5, r14 \n\t" \ - "ldr r14, [r0], #4 \n\t" \ - "adcs r6, r6, r14 \n\t" \ - "ldr r14, [r0], #4 \n\t" \ - "adcs r7, r7, r14 \n\t" \ - "ldr r14, [r0], #4 \n\t" \ - "adcs r8, r8, r14 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - "adcs r10, r10, #0 \n\t" \ - "sub r0, #24 \n\t" \ - \ - /* Perform center multiplication */ \ - "umlal r9, r10, r12, r12 \n\t" \ - "stmia r0!, {r3,r4,r5,r6,r7,r8,r9,r10} \n\t" - -#define FAST_SQUARE_ASM_7 \ - "push {r2} \n\t" \ - "ldmia r1!, {r2, r3, r4, r5, r6, r7, r8} \n\t" \ - "push {r1} \n\t" \ - "sub r1, 4 \n\t" \ - \ - "add r0, 24 \n\t" \ - "umull r9, r10, r2, r8 \n\t" \ - "stmia r0!, {r9, r10} \n\t" \ - "sub r0, 32 \n\t" \ - \ - "umull r11, r12, r2, r2 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "mov r9, #0 \n\t" \ - "umull r10, r11, r2, r3 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r8, r11, #0 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r8, r8, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umull r11, r12, r2, r4 \n\t" \ - "adds r11, r11, r11 \n\t" \ - "adcs r12, r12, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "adds r8, r8, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r3, r3 \n\t" \ - "adds r8, r8, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r12, #0 \n\t" \ - "umull r8, r11, r2, r5 \n\t" \ - "mov r14, r11 \n\t" \ - "umlal r8, r11, r3, r4 \n\t" \ - "cmp r14, r11 \n\t" \ - "it hi \n\t" \ - "adchi r12, r12, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r11, r11, r11 \n\t" \ - "adc r12, r12, r12 \n\t" \ - "adds r8, r8, r9 \n\t" \ - "adcs r11, r11, r10 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umull r8, r9, r2, r6 \n\t" \ - "mov r14, r9 \n\t" \ - "umlal r8, r9, r3, r5 \n\t" \ - "cmp r14, r9 \n\t" \ - "it hi \n\t" \ - "adchi r10, r10, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r9, r9, r9 \n\t" \ - "adc r10, r10, r10 \n\t" \ - "mov r14, r9 \n\t" \ - "umlal r8, r9, r4, r4 \n\t" \ - "cmp r14, r9 \n\t" \ - "it hi \n\t" \ - "adchi r10, r10, #0 \n\t" \ - "adds r8, r8, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r12, #0 \n\t" \ - "umull r8, r11, r2, r7 \n\t" \ - "mov r14, r11 \n\t" \ - "umlal r8, r11, r3, r6 \n\t" \ - "cmp r14, r11 \n\t" \ - "it hi \n\t" \ - "adchi r12, r12, #0 \n\t" \ - "mov r14, r11 \n\t" \ - "umlal r8, r11, r4, r5 \n\t" \ - "cmp r14, r11 \n\t" \ - "it hi \n\t" \ - "adchi r12, r12, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r11, r11, r11 \n\t" \ - "adc r12, r12, r12 \n\t" \ - "adds r8, r8, r9 \n\t" \ - "adcs r11, r11, r10 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "ldmia r1!, {r2} \n\t" \ - "mov r10, #0 \n\t" \ - "umull r8, r9, r3, r7 \n\t" \ - "mov r14, r9 \n\t" \ - "umlal r8, r9, r4, r6 \n\t" \ - "cmp r14, r9 \n\t" \ - "it hi \n\t" \ - "adchi r10, r10, #0 \n\t" \ - "ldr r14, [r0] \n\t" \ - "adds r8, r8, r14 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r9, r9, r9 \n\t" \ - "adc r10, r10, r10 \n\t" \ - "mov r14, r9 \n\t" \ - "umlal r8, r9, r5, r5 \n\t" \ - "cmp r14, r9 \n\t" \ - "it hi \n\t" \ - "adchi r10, r10, #0 \n\t" \ - "adds r8, r8, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r12, #0 \n\t" \ - "umull r8, r11, r3, r2 \n\t" \ - "mov r14, r11 \n\t" \ - "umlal r8, r11, r4, r7 \n\t" \ - "cmp r14, r11 \n\t" \ - "it hi \n\t" \ - "adchi r12, r12, #0 \n\t" \ - "mov r14, r11 \n\t" \ - "umlal r8, r11, r5, r6 \n\t" \ - "cmp r14, r11 \n\t" \ - "it hi \n\t" \ - "adchi r12, r12, #0 \n\t" \ - "ldr r14, [r0] \n\t" \ - "adds r8, r8, r14 \n\t" \ - "adcs r11, r11, #0 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r11, r11, r11 \n\t" \ - "adc r12, r12, r12 \n\t" \ - "adds r8, r8, r9 \n\t" \ - "adcs r11, r11, r10 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umull r8, r9, r4, r2 \n\t" \ - "mov r14, r9 \n\t" \ - "umlal r8, r9, r5, r7 \n\t" \ - "cmp r14, r9 \n\t" \ - "it hi \n\t" \ - "adchi r10, r10, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r9, r9, r9 \n\t" \ - "adc r10, r10, r10 \n\t" \ - "mov r14, r9 \n\t" \ - "umlal r8, r9, r6, r6 \n\t" \ - "cmp r14, r9 \n\t" \ - "it hi \n\t" \ - "adchi r10, r10, #0 \n\t" \ - "adds r8, r8, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r12, #0 \n\t" \ - "umull r8, r11, r5, r2 \n\t" \ - "mov r14, r11 \n\t" \ - "umlal r8, r11, r6, r7 \n\t" \ - "cmp r14, r11 \n\t" \ - "it hi \n\t" \ - "adchi r12, r12, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r11, r11, r11 \n\t" \ - "adc r12, r12, r12 \n\t" \ - "adds r8, r8, r9 \n\t" \ - "adcs r11, r11, r10 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r8, #0 \n\t" \ - "umull r1, r10, r6, r2 \n\t" \ - "adds r1, r1, r1 \n\t" \ - "adcs r10, r10, r10 \n\t" \ - "adc r8, r8, #0 \n\t" \ - "adds r11, r11, r1 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r8, r8, #0 \n\t" \ - "umull r1, r10, r7, r7 \n\t" \ - "adds r11, r11, r1 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r8, r8, #0 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "mov r11, #0 \n\t" \ - "umull r1, r10, r7, r2 \n\t" \ - "adds r1, r1, r1 \n\t" \ - "adcs r10, r10, r10 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "adds r12, r12, r1 \n\t" \ - "adcs r8, r8, r10 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "umull r1, r10, r2, r2 \n\t" \ - "adds r8, r8, r1 \n\t" \ - "adcs r11, r11, r10 \n\t" \ - "stmia r0!, {r8, r11} \n\t" \ - "pop {r1, r2} \n\t" - -#define FAST_SQUARE_ASM_7_TO_8 \ - "cmp r2, #7 \n\t" \ - "beq 1f \n\t" \ - \ - "sub r0, #28 \n\t" \ - "sub r1, #28 \n\t" \ - \ - /* Do off-center multiplication */ \ - "ldmia r1!, {r6,r7,r8,r9,r10,r11,r12,r14} \n\t" \ - "umull r3, r4, r6, r14 \n\t" \ - "umull r6, r5, r7, r14 \n\t" \ - "adds r4, r4, r6 \n\t" \ - "umull r7, r6, r8, r14 \n\t" \ - "adcs r5, r5, r7 \n\t" \ - "umull r8, r7, r9, r14 \n\t" \ - "adcs r6, r6, r8 \n\t" \ - "umull r9, r8, r10, r14 \n\t" \ - "adcs r7, r7, r9 \n\t" \ - "umull r10, r9, r11, r14 \n\t" \ - "adcs r8, r8, r10 \n\t" \ - "umull r11, r10, r12, r14 \n\t" \ - "adcs r9, r9, r11 \n\t" \ - "adcs r10, r10, #0 \n\t" \ - \ - /* Multiply by 2 */ \ - "mov r11, #0 \n\t" \ - "adds r3, r3, r3 \n\t" \ - "adcs r4, r4, r4 \n\t" \ - "adcs r5, r5, r5 \n\t" \ - "adcs r6, r6, r6 \n\t" \ - "adcs r7, r7, r7 \n\t" \ - "adcs r8, r8, r8 \n\t" \ - "adcs r9, r9, r9 \n\t" \ - "adcs r10, r10, r10 \n\t" \ - "adcs r11, r11, #0 \n\t" \ - \ - /* Add into previous */ \ - "ldr r12, [r0], #4 \n\t" \ - "adds r3, r3, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r4, r4, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r5, r5, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r6, r6, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r7, r7, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r8, r8, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adcs r10, r10, #0 \n\t" \ - "adcs r11, r11, #0 \n\t" \ - "sub r0, #28 \n\t" \ - \ - /* Perform center multiplication */ \ - "umlal r10, r11, r14, r14 \n\t" \ - "stmia r0!, {r3,r4,r5,r6,r7,r8,r9,r10,r11} \n\t" - -#define FAST_SQUARE_ASM_8 \ - "push {r2} \n\t" \ - "ldmia r1!, {r2,r3,r4,r5,r6,r7,r8,r9} \n\t" \ - "push {r1} \n\t" \ - "sub r1, 8 \n\t" \ - \ - "add r0, 24 \n\t" \ - "umull r10, r11, r2, r8 \n\t" \ - "umull r12, r14, r2, r9 \n\t" \ - "umull r8, r9, r3, r9 \n\t" \ - "adds r11, r11, r12 \n\t" \ - "adcs r12, r14, r8 \n\t" \ - "adcs r14, r9, #0 \n\t" \ - "stmia r0!, {r10, r11, r12, r14} \n\t" \ - "sub r0, 40 \n\t" \ - \ - "umull r11, r12, r2, r2 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "mov r9, #0 \n\t" \ - "umull r10, r11, r2, r3 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r8, r11, #0 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "adds r12, r12, r10 \n\t" \ - "adcs r8, r8, r11 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umull r11, r12, r2, r4 \n\t" \ - "adds r11, r11, r11 \n\t" \ - "adcs r12, r12, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "adds r8, r8, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "umull r11, r12, r3, r3 \n\t" \ - "adds r8, r8, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r12, #0 \n\t" \ - "umull r8, r11, r2, r5 \n\t" \ - "mov r14, r11 \n\t" \ - "umlal r8, r11, r3, r4 \n\t" \ - "cmp r14, r11 \n\t" \ - "it hi \n\t" \ - "adchi r12, r12, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r11, r11, r11 \n\t" \ - "adc r12, r12, r12 \n\t" \ - "adds r8, r8, r9 \n\t" \ - "adcs r11, r11, r10 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umull r8, r9, r2, r6 \n\t" \ - "mov r14, r9 \n\t" \ - "umlal r8, r9, r3, r5 \n\t" \ - "cmp r14, r9 \n\t" \ - "it hi \n\t" \ - "adchi r10, r10, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r9, r9, r9 \n\t" \ - "adc r10, r10, r10 \n\t" \ - "mov r14, r9 \n\t" \ - "umlal r8, r9, r4, r4 \n\t" \ - "cmp r14, r9 \n\t" \ - "it hi \n\t" \ - "adchi r10, r10, #0 \n\t" \ - "adds r8, r8, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r12, #0 \n\t" \ - "umull r8, r11, r2, r7 \n\t" \ - "mov r14, r11 \n\t" \ - "umlal r8, r11, r3, r6 \n\t" \ - "cmp r14, r11 \n\t" \ - "it hi \n\t" \ - "adchi r12, r12, #0 \n\t" \ - "mov r14, r11 \n\t" \ - "umlal r8, r11, r4, r5 \n\t" \ - "cmp r14, r11 \n\t" \ - "it hi \n\t" \ - "adchi r12, r12, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r11, r11, r11 \n\t" \ - "adc r12, r12, r12 \n\t" \ - "adds r8, r8, r9 \n\t" \ - "adcs r11, r11, r10 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "ldmia r1!, {r2} \n\t" \ - "mov r10, #0 \n\t" \ - "umull r8, r9, r3, r7 \n\t" \ - "mov r14, r9 \n\t" \ - "umlal r8, r9, r4, r6 \n\t" \ - "cmp r14, r9 \n\t" \ - "it hi \n\t" \ - "adchi r10, r10, #0 \n\t" \ - "ldr r14, [r0] \n\t" \ - "adds r8, r8, r14 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r9, r9, r9 \n\t" \ - "adc r10, r10, r10 \n\t" \ - "mov r14, r9 \n\t" \ - "umlal r8, r9, r5, r5 \n\t" \ - "cmp r14, r9 \n\t" \ - "it hi \n\t" \ - "adchi r10, r10, #0 \n\t" \ - "adds r8, r8, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r12, #0 \n\t" \ - "umull r8, r11, r3, r2 \n\t" \ - "mov r14, r11 \n\t" \ - "umlal r8, r11, r4, r7 \n\t" \ - "cmp r14, r11 \n\t" \ - "it hi \n\t" \ - "adchi r12, r12, #0 \n\t" \ - "mov r14, r11 \n\t" \ - "umlal r8, r11, r5, r6 \n\t" \ - "cmp r14, r11 \n\t" \ - "it hi \n\t" \ - "adchi r12, r12, #0 \n\t" \ - "ldr r14, [r0] \n\t" \ - "adds r8, r8, r14 \n\t" \ - "adcs r11, r11, #0 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r11, r11, r11 \n\t" \ - "adc r12, r12, r12 \n\t" \ - "adds r8, r8, r9 \n\t" \ - "adcs r11, r11, r10 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "ldmia r1!, {r3} \n\t" \ - "mov r10, #0 \n\t" \ - "umull r8, r9, r4, r2 \n\t" \ - "mov r14, r9 \n\t" \ - "umlal r8, r9, r5, r7 \n\t" \ - "cmp r14, r9 \n\t" \ - "it hi \n\t" \ - "adchi r10, r10, #0 \n\t" \ - "ldr r14, [r0] \n\t" \ - "adds r8, r8, r14 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r9, r9, r9 \n\t" \ - "adc r10, r10, r10 \n\t" \ - "mov r14, r9 \n\t" \ - "umlal r8, r9, r6, r6 \n\t" \ - "cmp r14, r9 \n\t" \ - "it hi \n\t" \ - "adchi r10, r10, #0 \n\t" \ - "adds r8, r8, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r12, #0 \n\t" \ - "umull r8, r11, r4, r3 \n\t" \ - "mov r14, r11 \n\t" \ - "umlal r8, r11, r5, r2 \n\t" \ - "cmp r14, r11 \n\t" \ - "it hi \n\t" \ - "adchi r12, r12, #0 \n\t" \ - "mov r14, r11 \n\t" \ - "umlal r8, r11, r6, r7 \n\t" \ - "cmp r14, r11 \n\t" \ - "it hi \n\t" \ - "adchi r12, r12, #0 \n\t" \ - "ldr r14, [r0] \n\t" \ - "adds r8, r8, r14 \n\t" \ - "adcs r11, r11, #0 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r11, r11, r11 \n\t" \ - "adc r12, r12, r12 \n\t" \ - "adds r8, r8, r9 \n\t" \ - "adcs r11, r11, r10 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umull r8, r9, r5, r3 \n\t" \ - "mov r14, r9 \n\t" \ - "umlal r8, r9, r6, r2 \n\t" \ - "cmp r14, r9 \n\t" \ - "it hi \n\t" \ - "adchi r10, r10, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r9, r9, r9 \n\t" \ - "adc r10, r10, r10 \n\t" \ - "mov r14, r9 \n\t" \ - "umlal r8, r9, r7, r7 \n\t" \ - "cmp r14, r9 \n\t" \ - "it hi \n\t" \ - "adchi r10, r10, #0 \n\t" \ - "adds r8, r8, r11 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r12, #0 \n\t" \ - "umull r8, r11, r6, r3 \n\t" \ - "mov r14, r11 \n\t" \ - "umlal r8, r11, r7, r2 \n\t" \ - "cmp r14, r11 \n\t" \ - "it hi \n\t" \ - "adchi r12, r12, #0 \n\t" \ - "adds r8, r8, r8 \n\t" \ - "adcs r11, r11, r11 \n\t" \ - "adc r12, r12, r12 \n\t" \ - "adds r8, r8, r9 \n\t" \ - "adcs r11, r11, r10 \n\t" \ - "adc r12, r12, #0 \n\t" \ - "stmia r0!, {r8} \n\t" \ - \ - "mov r8, #0 \n\t" \ - "umull r1, r10, r7, r3 \n\t" \ - "adds r1, r1, r1 \n\t" \ - "adcs r10, r10, r10 \n\t" \ - "adc r8, r8, #0 \n\t" \ - "adds r11, r11, r1 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r8, r8, #0 \n\t" \ - "umull r1, r10, r2, r2 \n\t" \ - "adds r11, r11, r1 \n\t" \ - "adcs r12, r12, r10 \n\t" \ - "adc r8, r8, #0 \n\t" \ - "stmia r0!, {r11} \n\t" \ - \ - "mov r11, #0 \n\t" \ - "umull r1, r10, r2, r3 \n\t" \ - "adds r1, r1, r1 \n\t" \ - "adcs r10, r10, r10 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "adds r12, r12, r1 \n\t" \ - "adcs r8, r8, r10 \n\t" \ - "adc r11, r11, #0 \n\t" \ - "stmia r0!, {r12} \n\t" \ - \ - "umull r1, r10, r3, r3 \n\t" \ - "adds r8, r8, r1 \n\t" \ - "adcs r11, r11, r10 \n\t" \ - "stmia r0!, {r8, r11} \n\t" \ - "pop {r1, r2} \n\t" - -#endif /* _UECC_ASM_ARM_MULT_SQUARE_H_ */ diff --git a/lib/micro-ecc/asm_arm_mult_square_umaal.inc b/lib/micro-ecc/asm_arm_mult_square_umaal.inc deleted file mode 100644 index c554d20e385..00000000000 --- a/lib/micro-ecc/asm_arm_mult_square_umaal.inc +++ /dev/null @@ -1,1202 +0,0 @@ -/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_ASM_ARM_MULT_SQUARE_H_ -#define _UECC_ASM_ARM_MULT_SQUARE_H_ - -#define FAST_MULT_ASM_5 \ - "push {r3} \n\t" \ - "ldmia r2!, {r3, r4, r5, r6, r7} \n\t" \ - "push {r2} \n\t" \ - \ - "ldr r2, [r1], #4 \n\t" \ - "umull r8, r9, r3, r2 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "mov r10, #0 \n\t" \ - "umaal r9, r10, r4, r2 \n\t" \ - "mov r11, #0 \n\t" \ - "umaal r10, r11, r5, r2 \n\t" \ - "mov r12, #0 \n\t" \ - "umaal r11, r12, r6, r2 \n\t" \ - "mov r14, #0 \n\t" \ - "umaal r12, r14, r7, r2 \n\t" \ - \ - "ldr r2, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r3, r2 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r4, r2 \n\t" \ - "umaal r10, r11, r5, r2 \n\t" \ - "umaal r11, r12, r6, r2 \n\t" \ - "umaal r12, r14, r7, r2 \n\t" \ - \ - "ldr r2, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r3, r2 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r4, r2 \n\t" \ - "umaal r10, r11, r5, r2 \n\t" \ - "umaal r11, r12, r6, r2 \n\t" \ - "umaal r12, r14, r7, r2 \n\t" \ - \ - "ldr r2, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r3, r2 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r4, r2 \n\t" \ - "umaal r10, r11, r5, r2 \n\t" \ - "umaal r11, r12, r6, r2 \n\t" \ - "umaal r12, r14, r7, r2 \n\t" \ - \ - "ldr r2, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r3, r2 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r4, r2 \n\t" \ - "umaal r10, r11, r5, r2 \n\t" \ - "umaal r11, r12, r6, r2 \n\t" \ - "umaal r12, r14, r7, r2 \n\t" \ - \ - "str r9, [r0], #4 \n\t" \ - "str r10, [r0], #4 \n\t" \ - "str r11, [r0], #4 \n\t" \ - "str r12, [r0], #4 \n\t" \ - "str r14, [r0], #4 \n\t" \ - \ - "pop {r2, r3} \n\t" - -#define FAST_MULT_ASM_5_TO_6 \ - "cmp r3, #5 \n\t" \ - "beq 1f \n\t" \ - \ - /* r4 = left high */ \ - "ldr r4, [r1] \n\t" \ - \ - "sub r0, #20 \n\t" \ - "sub r1, #20 \n\t" \ - "sub r2, #20 \n\t" \ - \ - /* Do right side */ \ - "ldr r14, [r2], #4 \n\t" \ - "mov r5, #0 \n\t" \ - "ldr r6, [r0], #4 \n\t" \ - "umaal r5, r6, r4, r14 \n\t" \ - "ldr r14, [r2], #4 \n\t" \ - "ldr r7, [r0], #4 \n\t" \ - "umaal r6, r7, r4, r14 \n\t" \ - "ldr r14, [r2], #4 \n\t" \ - "ldr r8, [r0], #4 \n\t" \ - "umaal r7, r8, r4, r14 \n\t" \ - "ldr r14, [r2], #4 \n\t" \ - "ldr r9, [r0], #4 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "ldr r14, [r2], #4 \n\t" \ - "ldr r10, [r0], #4 \n\t" \ - "umaal r9, r10, r4, r14 \n\t" \ - "sub r0, #20 \n\t" \ - \ - /* r4 = right high */ \ - "ldr r4, [r2], #4 \n\t" \ - \ - /* Do left side */ \ - "ldr r14, [r1], #4 \n\t" \ - "mov r12, #0 \n\t" \ - "umaal r12, r5, r4, r14 \n\t" \ - "str r12, [r0], #4 \n\t" \ - "ldr r14, [r1], #4 \n\t" \ - "umaal r5, r6, r4, r14 \n\t" \ - "str r5, [r0], #4 \n\t" \ - "ldr r14, [r1], #4 \n\t" \ - "umaal r6, r7, r4, r14 \n\t" \ - "str r6, [r0], #4 \n\t" \ - "ldr r14, [r1], #4 \n\t" \ - "umaal r7, r8, r4, r14 \n\t" \ - "str r7, [r0], #4 \n\t" \ - "ldr r14, [r1], #4 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "umaal r9, r10, r4, r14 \n\t" \ - "stmia r0!, {r9, r10} \n\t" - -#define FAST_MULT_ASM_6 \ - "ldmia r2!, {r4, r5, r6} \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "umull r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "mov r10, #0 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "mov r11, #0 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - \ - "str r9, [r0], #4 \n\t" \ - "str r10, [r0], #4 \n\t" \ - "str r11, [r0], #4 \n\t" \ - \ - "sub r0, #24 \n\t" \ - "sub r1, #24 \n\t" \ - "ldmia r2!, {r4, r5, r6} \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "mov r9, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "mov r10, #0 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "mov r11, #0 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - \ - "str r9, [r0], #4 \n\t" \ - "str r10, [r0], #4 \n\t" \ - "str r11, [r0], #4 \n\t" - -#define FAST_MULT_ASM_6_TO_7 \ - "cmp r3, #6 \n\t" \ - "beq 1f \n\t" \ - \ - /* r4 = left high */ \ - "ldr r4, [r1] \n\t" \ - \ - "sub r0, #24 \n\t" \ - "sub r1, #24 \n\t" \ - "sub r2, #24 \n\t" \ - \ - /* Do right side */ \ - "ldr r14, [r2], #4 \n\t" \ - "mov r5, #0 \n\t" \ - "ldr r6, [r0], #4 \n\t" \ - "umaal r5, r6, r4, r14 \n\t" \ - "ldr r14, [r2], #4 \n\t" \ - "ldr r7, [r0], #4 \n\t" \ - "umaal r6, r7, r4, r14 \n\t" \ - "ldr r14, [r2], #4 \n\t" \ - "ldr r8, [r0], #4 \n\t" \ - "umaal r7, r8, r4, r14 \n\t" \ - "ldr r14, [r2], #4 \n\t" \ - "ldr r9, [r0], #4 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "ldr r14, [r2], #4 \n\t" \ - "ldr r10, [r0], #4 \n\t" \ - "umaal r9, r10, r4, r14 \n\t" \ - "ldr r14, [r2], #4 \n\t" \ - "ldr r11, [r0], #4 \n\t" \ - "umaal r10, r11, r4, r14 \n\t" \ - "sub r0, #24 \n\t" \ - \ - /* r4 = right high */ \ - "ldr r4, [r2], #4 \n\t" \ - \ - /* Do left side */ \ - "ldr r14, [r1], #4 \n\t" \ - "mov r12, #0 \n\t" \ - "umaal r12, r5, r4, r14 \n\t" \ - "str r12, [r0], #4 \n\t" \ - "ldr r14, [r1], #4 \n\t" \ - "umaal r5, r6, r4, r14 \n\t" \ - "str r5, [r0], #4 \n\t" \ - "ldr r14, [r1], #4 \n\t" \ - "umaal r6, r7, r4, r14 \n\t" \ - "str r6, [r0], #4 \n\t" \ - "ldr r14, [r1], #4 \n\t" \ - "umaal r7, r8, r4, r14 \n\t" \ - "str r7, [r0], #4 \n\t" \ - "ldr r14, [r1], #4 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "ldr r14, [r1], #4 \n\t" \ - "umaal r9, r10, r4, r14 \n\t" \ - "str r9, [r0], #4 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "umaal r10, r11, r4, r14 \n\t" \ - "stmia r0!, {r10, r11} \n\t" - -#define FAST_MULT_ASM_7 \ - "ldmia r2!, {r4, r5, r6, r7} \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "umull r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "mov r10, #0 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "mov r11, #0 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "mov r12, #0 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "str r9, [r0], #4 \n\t" \ - "str r10, [r0], #4 \n\t" \ - "str r11, [r0], #4 \n\t" \ - "str r12, [r0], #4 \n\t" \ - \ - "sub r0, #28 \n\t" \ - "sub r1, #28 \n\t" \ - "ldmia r2!, {r4, r5, r6} \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "mov r9, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "mov r10, #0 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "mov r11, #0 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - \ - "str r9, [r0], #4 \n\t" \ - "str r10, [r0], #4 \n\t" \ - "str r11, [r0], #4 \n\t" - -#define FAST_MULT_ASM_7_TO_8 \ - "cmp r3, #7 \n\t" \ - "beq 1f \n\t" \ - "push {r3} \n\t" \ - \ - /* r4 = left high */ \ - "ldr r4, [r1] \n\t" \ - \ - "sub r0, #28 \n\t" \ - "sub r1, #28 \n\t" \ - "sub r2, #28 \n\t" \ - \ - /* Do right side */ \ - "ldr r14, [r2], #4 \n\t" \ - "mov r5, #0 \n\t" \ - "ldr r6, [r0], #4 \n\t" \ - "umaal r5, r6, r4, r14 \n\t" \ - "ldr r14, [r2], #4 \n\t" \ - "ldr r7, [r0], #4 \n\t" \ - "umaal r6, r7, r4, r14 \n\t" \ - "ldr r14, [r2], #4 \n\t" \ - "ldr r8, [r0], #4 \n\t" \ - "umaal r7, r8, r4, r14 \n\t" \ - "ldr r14, [r2], #4 \n\t" \ - "ldr r9, [r0], #4 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "ldr r14, [r2], #4 \n\t" \ - "ldr r10, [r0], #4 \n\t" \ - "umaal r9, r10, r4, r14 \n\t" \ - "ldr r14, [r2], #4 \n\t" \ - "ldr r11, [r0], #4 \n\t" \ - "umaal r10, r11, r4, r14 \n\t" \ - "ldr r14, [r2], #4 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "umaal r11, r12, r4, r14 \n\t" \ - "sub r0, #28 \n\t" \ - \ - /* r4 = right high */ \ - "ldr r4, [r2], #4 \n\t" \ - \ - /* Do left side */ \ - "ldr r14, [r1], #4 \n\t" \ - "mov r3, #0 \n\t" \ - "umaal r3, r5, r4, r14 \n\t" \ - "str r3, [r0], #4 \n\t" \ - "ldr r14, [r1], #4 \n\t" \ - "umaal r5, r6, r4, r14 \n\t" \ - "str r5, [r0], #4 \n\t" \ - "ldr r14, [r1], #4 \n\t" \ - "umaal r6, r7, r4, r14 \n\t" \ - "str r6, [r0], #4 \n\t" \ - "ldr r14, [r1], #4 \n\t" \ - "umaal r7, r8, r4, r14 \n\t" \ - "str r7, [r0], #4 \n\t" \ - "ldr r14, [r1], #4 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "ldr r14, [r1], #4 \n\t" \ - "umaal r9, r10, r4, r14 \n\t" \ - "str r9, [r0], #4 \n\t" \ - "ldr r14, [r1], #4 \n\t" \ - "umaal r10, r11, r4, r14 \n\t" \ - "str r10, [r0], #4 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "umaal r11, r12, r4, r14 \n\t" \ - "stmia r0!, {r11, r12} \n\t" \ - "pop {r3} \n\t" - -#define FAST_MULT_ASM_8 \ - "ldmia r2!, {r4, r5, r6, r7} \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "umull r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "mov r10, #0 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "mov r11, #0 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "mov r12, #0 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "str r9, [r0], #4 \n\t" \ - "str r10, [r0], #4 \n\t" \ - "str r11, [r0], #4 \n\t" \ - "str r12, [r0], #4 \n\t" \ - \ - "sub r0, #32 \n\t" \ - "sub r1, #32 \n\t" \ - "ldmia r2!, {r4, r5, r6, r7} \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "mov r9, #0 \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "mov r10, #0 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "mov r11, #0 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "mov r12, #0 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "ldr r14, [r1], #4 \n\t" \ - "ldr r8, [r0] \n\t" \ - "umaal r8, r9, r4, r14 \n\t" \ - "str r8, [r0], #4 \n\t" \ - "umaal r9, r10, r5, r14 \n\t" \ - "umaal r10, r11, r6, r14 \n\t" \ - "umaal r11, r12, r7, r14 \n\t" \ - \ - "str r9, [r0], #4 \n\t" \ - "str r10, [r0], #4 \n\t" \ - "str r11, [r0], #4 \n\t" \ - "str r12, [r0], #4 \n\t" - -#define FAST_SQUARE_ASM_5 \ - "ldmia r1!, {r9,r10,r11,r12,r14} \n\t" \ - "push {r1, r2} \n\t" \ - \ - "umull r1, r2, r10, r9 \n\t" \ - "mov r3, #0 \n\t" \ - "umaal r2, r3, r11, r9 \n\t" \ - "mov r4, #0 \n\t" \ - "umaal r3, r4, r12, r9 \n\t" \ - "mov r5, #0 \n\t" \ - "umaal r4, r5, r14, r9 \n\t" \ - \ - "mov r6, #0 \n\t" \ - "umaal r6, r3, r11, r10 \n\t" \ - "umaal r3, r4, r12, r10 \n\t" \ - "adds r1, r1, r1 \n\t" \ - "adcs r2, r2, r2 \n\t" \ - "adcs r6, r6, r6 \n\t" \ - "adcs r3, r3, r3 \n\t" \ - \ - "umull r7, r8, r9, r9 \n\t" \ - /* Store carry in r9 */ \ - "mov r9, #0 \n\t" \ - "adc r9, r9, #0 \n\t" \ - "adds r8, r8, r1 \n\t" \ - "stmia r0!, {r7,r8} \n\t" \ - \ - "umull r7, r8, r10, r10 \n\t" \ - "adcs r7, r7, r2 \n\t" \ - "adcs r8, r8, r6 \n\t" \ - "stmia r0!, {r7,r8} \n\t" \ - \ - "umaal r4, r5, r14, r10 \n\t" \ - /* Store carry in r10 */ \ - "mov r10, #0 \n\t" \ - "adc r10, r10, #0 \n\t" \ - \ - "mov r1, #0 \n\t" \ - "umaal r1, r4, r12, r11 \n\t" \ - "umaal r4, r5, r14, r11 \n\t" \ - \ - "mov r2, #0 \n\t" \ - "umaal r2, r5, r14, r12 \n\t" \ - /* Load carry from r9 */ \ - "lsrs r9, #1 \n\t" \ - "adcs r1, r1, r1 \n\t" \ - "adcs r4, r4, r4 \n\t" \ - "adcs r2, r2, r2 \n\t" \ - "adcs r5, r5, r5 \n\t" \ - /* r9 is 0 now */ \ - "adc r9, r9, #0 \n\t" \ - \ - /* Use carry from r10 */ \ - "umaal r3, r10, r11, r11 \n\t" \ - "adds r10, r10, r1 \n\t" \ - "stmia r0!, {r3,r10} \n\t" \ - \ - "umull r6, r10, r12, r12 \n\t" \ - "adcs r6, r6, r4 \n\t" \ - "adcs r10, r10, r2 \n\t" \ - "stmia r0!, {r6,r10} \n\t" \ - \ - "umull r6, r10, r14, r14 \n\t" \ - "adcs r6, r6, r5 \n\t" \ - "adcs r10, r10, r9 \n\t" \ - "stmia r0!, {r6,r10} \n\t" \ - "pop {r1, r2} \n\t" - -#define FAST_SQUARE_ASM_5_TO_6 \ - "cmp r2, #5 \n\t" \ - "beq 1f \n\t" \ - \ - "sub r0, #20 \n\t" \ - "sub r1, #20 \n\t" \ - \ - /* Do off-center multiplication */ \ - "ldmia r1!, {r5,r6,r7,r8,r9,r14} \n\t" \ - "umull r3, r4, r5, r14 \n\t" \ - "mov r5, #0 \n\t" \ - "umaal r4, r5, r6, r14 \n\t" \ - "mov r6, #0 \n\t" \ - "umaal r5, r6, r7, r14 \n\t" \ - "mov r7, #0 \n\t" \ - "umaal r6, r7, r8, r14 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r7, r8, r9, r14 \n\t" \ - \ - /* Multiply by 2 */ \ - "mov r9, #0 \n\t" \ - "adds r3, r3, r3 \n\t" \ - "adcs r4, r4, r4 \n\t" \ - "adcs r5, r5, r5 \n\t" \ - "adcs r6, r6, r6 \n\t" \ - "adcs r7, r7, r7 \n\t" \ - "adcs r8, r8, r8 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - \ - /* Add into previous */ \ - "ldr r12, [r0], #4 \n\t" \ - "adds r3, r3, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r4, r4, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r5, r5, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r6, r6, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r7, r7, r12 \n\t" \ - "adcs r8, r8, #0 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - "sub r0, #20 \n\t" \ - \ - /* Perform center multiplication */ \ - "umlal r8, r9, r14, r14 \n\t" \ - "stmia r0!, {r3,r4,r5,r6,r7,r8,r9} \n\t" - -#define FAST_SQUARE_ASM_6 \ - "ldmia r1!, {r8,r9,r10,r11,r12,r14} \n\t" \ - "push {r1, r2} \n\t" \ - \ - "umull r1, r2, r9, r8 \n\t" \ - "mov r3, #0 \n\t" \ - "umaal r2, r3, r10, r8 \n\t" \ - "mov r4, #0 \n\t" \ - "umaal r3, r4, r11, r8 \n\t" \ - "mov r5, #0 \n\t" \ - "umaal r4, r5, r12, r8 \n\t" \ - "mov r6, #0 \n\t" \ - "umaal r5, r6, r14, r8 \n\t" \ - \ - "mov r7, #0 \n\t" \ - "umaal r7, r3, r10, r9 \n\t" \ - "umaal r3, r4, r11, r9 \n\t" \ - "umaal r4, r5, r12, r9 \n\t" \ - "push {r4, r5} \n\t" \ - "adds r1, r1, r1 \n\t" \ - "adcs r2, r2, r2 \n\t" \ - "adcs r7, r7, r7 \n\t" \ - "adcs r3, r3, r3 \n\t" \ - \ - "umull r4, r5, r8, r8 \n\t" \ - /* Store carry in r8 */ \ - "mov r8, #0 \n\t" \ - "adc r8, r8, #0 \n\t" \ - "adds r5, r5, r1 \n\t" \ - "stmia r0!, {r4,r5} \n\t" \ - \ - "umull r4, r5, r9, r9 \n\t" \ - "adcs r4, r4, r2 \n\t" \ - "adcs r5, r5, r7 \n\t" \ - "stmia r0!, {r4,r5} \n\t" \ - \ - "pop {r4, r5} \n\t" \ - "umaal r5, r6, r14, r9 \n\t" \ - /* Store carry in r9 */ \ - "mov r9, #0 \n\t" \ - "adc r9, r9, #0 \n\t" \ - \ - "mov r1, #0 \n\t" \ - "umaal r1, r4, r11, r10 \n\t" \ - "umaal r4, r5, r12, r10 \n\t" \ - "umaal r5, r6, r14, r10 \n\t" \ - \ - "mov r2, #0 \n\t" \ - "umaal r2, r5, r12, r11 \n\t" \ - "umaal r5, r6, r14, r11 \n\t" \ - \ - "mov r7, #0 \n\t" \ - "umaal r7, r6, r14, r12 \n\t" \ - \ - /* Load carry from r8 */ \ - "lsrs r8, #1 \n\t" \ - "adcs r1, r1, r1 \n\t" \ - "adcs r4, r4, r4 \n\t" \ - "adcs r2, r2, r2 \n\t" \ - "adcs r5, r5, r5 \n\t" \ - "adcs r7, r7, r7 \n\t" \ - "adcs r6, r6, r6 \n\t" \ - "adc r8, r8, #0 \n\t" \ - \ - /* Use carry from r9 */ \ - "umaal r3, r9, r10, r10 \n\t" \ - "adds r9, r9, r1 \n\t" \ - "stmia r0!, {r3,r9} \n\t" \ - \ - "umull r9, r10, r11, r11 \n\t" \ - "adcs r9, r9, r4 \n\t" \ - "adcs r10, r10, r2 \n\t" \ - "stmia r0!, {r9,r10} \n\t" \ - \ - "umull r9, r10, r12, r12 \n\t" \ - "adcs r9, r9, r5 \n\t" \ - "adcs r10, r10, r7 \n\t" \ - "stmia r0!, {r9,r10} \n\t" \ - \ - "umull r9, r10, r14, r14 \n\t" \ - "adcs r9, r9, r6 \n\t" \ - "adcs r10, r10, r8 \n\t" \ - "stmia r0!, {r9,r10} \n\t" \ - "pop {r1, r2} \n\t" - -#define FAST_SQUARE_ASM_6_TO_7 \ - "cmp r2, #6 \n\t" \ - "beq 1f \n\t" \ - \ - "sub r0, #24 \n\t" \ - "sub r1, #24 \n\t" \ - \ - /* Do off-center multiplication */ \ - "ldmia r1!, {r5,r6,r7,r8,r9,r10,r14} \n\t" \ - "umull r3, r4, r5, r14 \n\t" \ - "mov r5, #0 \n\t" \ - "umaal r4, r5, r6, r14 \n\t" \ - "mov r6, #0 \n\t" \ - "umaal r5, r6, r7, r14 \n\t" \ - "mov r7, #0 \n\t" \ - "umaal r6, r7, r8, r14 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r7, r8, r9, r14 \n\t" \ - "mov r9, #0 \n\t" \ - "umaal r8, r9, r10, r14 \n\t" \ - \ - /* Multiply by 2 */ \ - "mov r10, #0 \n\t" \ - "adds r3, r3, r3 \n\t" \ - "adcs r4, r4, r4 \n\t" \ - "adcs r5, r5, r5 \n\t" \ - "adcs r6, r6, r6 \n\t" \ - "adcs r7, r7, r7 \n\t" \ - "adcs r8, r8, r8 \n\t" \ - "adcs r9, r9, r9 \n\t" \ - "adcs r10, r10, #0 \n\t" \ - \ - /* Add into previous */ \ - "ldr r12, [r0], #4 \n\t" \ - "adds r3, r3, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r4, r4, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r5, r5, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r6, r6, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r7, r7, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r8, r8, r12 \n\t" \ - "adcs r9, r9, #0 \n\t" \ - "adcs r10, r10, #0 \n\t" \ - "sub r0, #24 \n\t" \ - \ - /* Perform center multiplication */ \ - "umlal r9, r10, r14, r14 \n\t" \ - "stmia r0!, {r3,r4,r5,r6,r7,r8,r9,r10} \n\t" - -#define FAST_SQUARE_ASM_7 \ - "ldmia r1!, {r9,r10,r11,r12} \n\t" \ - "push {r2} \n\t" \ - \ - "umull r14, r2, r10, r9 \n\t" \ - "mov r3, #0 \n\t" \ - "umaal r2, r3, r11, r9 \n\t" \ - "mov r4, #0 \n\t" \ - "umaal r3, r4, r12, r9 \n\t" \ - \ - "mov r5, #0 \n\t" \ - "umaal r5, r3, r11, r10 \n\t" \ - "adds r14, r14, r14 \n\t" \ - "adcs r2, r2, r2 \n\t" \ - "adcs r5, r5, r5 \n\t" \ - /* Store carry in r7 */ \ - "mov r7, #0 \n\t" \ - "adc r7, r7, #0 \n\t" \ - \ - "umull r6, r8, r9, r9 \n\t" \ - "adds r8, r8, r14 \n\t" \ - "stmia r0!, {r6,r8} \n\t" \ - \ - "umull r6, r8, r10, r10 \n\t" \ - "adcs r6, r6, r2 \n\t" \ - "adcs r8, r8, r5 \n\t" \ - "stmia r0!, {r6,r8} \n\t" \ - /* Store carry in r8 */ \ - "mov r8, #0 \n\t" \ - "adc r8, r8, #0 \n\t" \ - \ - "ldmia r1!, {r2, r6, r14} \n\t" \ - "push {r1} \n\t" \ - "umaal r3, r4, r2, r9 \n\t" \ - "mov r5, #0 \n\t" \ - "umaal r4, r5, r6, r9 \n\t" \ - "mov r1, #0 \n\t" \ - "umaal r5, r1, r14, r9 \n\t" \ - \ - "mov r9, #0 \n\t" \ - "umaal r3, r9, r12, r10 \n\t" \ - "umaal r9, r4, r2, r10 \n\t" \ - "umaal r4, r5, r6, r10 \n\t" \ - "umaal r5, r1, r14, r10 \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umaal r10, r9, r12, r11 \n\t" \ - "umaal r9, r4, r2, r11 \n\t" \ - "umaal r4, r5, r6, r11 \n\t" \ - "umaal r5, r1, r14, r11 \n\t" \ - \ - /* Load carry from r7 */ \ - "lsrs r7, #1 \n\t" \ - "adcs r3, r3, r3 \n\t" \ - "adcs r10, r10, r10 \n\t" \ - "adcs r9, r9, r9 \n\t" \ - /* Store carry back in r7 */ \ - "adc r7, r7, #0 \n\t" \ - \ - /* Use carry from r8 */ \ - "umaal r3, r8, r11, r11 \n\t" \ - "adds r8, r8, r10 \n\t" \ - "stmia r0!, {r3,r8} \n\t" \ - /* Store carry back in r8 */ \ - "mov r8, #0 \n\t" \ - "adc r8, r8, #0 \n\t" \ - \ - "mov r3, #0 \n\t" \ - "umaal r3, r4, r2, r12 \n\t" \ - "umaal r4, r5, r6, r12 \n\t" \ - "umaal r5, r1, r14, r12 \n\t" \ - \ - "mov r10, #0 \n\t" \ - "umaal r10, r5, r6, r2 \n\t" \ - "umaal r5, r1, r14, r2 \n\t" \ - \ - "mov r11, #0 \n\t" \ - "umaal r11, r1, r14, r6 \n\t" \ - \ - /* Load carry from r7 */ \ - "lsrs r7, #1 \n\t" \ - "adcs r3, r3, r3 \n\t" \ - "adcs r4, r4, r4 \n\t" \ - "adcs r10, r10, r10 \n\t" \ - "adcs r5, r5, r5 \n\t" \ - "adcs r11, r11, r11 \n\t" \ - "adcs r1, r1, r1 \n\t" \ - "adc r7, r7, #0 \n\t" \ - \ - /* Use carry from r8 */ \ - "umaal r8, r9, r12, r12 \n\t" \ - "adds r9, r9, r3 \n\t" \ - "stmia r0!, {r8,r9} \n\t" \ - \ - "umull r8, r9, r2, r2 \n\t" \ - "adcs r8, r8, r4 \n\t" \ - "adcs r9, r9, r10 \n\t" \ - "stmia r0!, {r8,r9} \n\t" \ - \ - "umull r8, r9, r6, r6 \n\t" \ - "adcs r8, r8, r5 \n\t" \ - "adcs r9, r9, r11 \n\t" \ - "stmia r0!, {r8,r9} \n\t" \ - \ - "umull r8, r9, r14, r14 \n\t" \ - "adcs r8, r8, r1 \n\t" \ - "adcs r9, r9, r7 \n\t" \ - "stmia r0!, {r8,r9} \n\t" \ - "pop {r1, r2} \n\t" - -#define FAST_SQUARE_ASM_7_TO_8 \ - "cmp r2, #7 \n\t" \ - "beq 1f \n\t" \ - \ - "sub r0, #28 \n\t" \ - "sub r1, #28 \n\t" \ - \ - /* Do off-center multiplication */ \ - "ldmia r1!, {r5,r6,r7,r8,r9,r10,r11,r14} \n\t" \ - "umull r3, r4, r5, r14 \n\t" \ - "mov r5, #0 \n\t" \ - "umaal r4, r5, r6, r14 \n\t" \ - "mov r6, #0 \n\t" \ - "umaal r5, r6, r7, r14 \n\t" \ - "mov r7, #0 \n\t" \ - "umaal r6, r7, r8, r14 \n\t" \ - "mov r8, #0 \n\t" \ - "umaal r7, r8, r9, r14 \n\t" \ - "mov r9, #0 \n\t" \ - "umaal r8, r9, r10, r14 \n\t" \ - "mov r10, #0 \n\t" \ - "umaal r9, r10, r11, r14 \n\t" \ - \ - /* Multiply by 2 */ \ - "mov r11, #0 \n\t" \ - "adds r3, r3, r3 \n\t" \ - "adcs r4, r4, r4 \n\t" \ - "adcs r5, r5, r5 \n\t" \ - "adcs r6, r6, r6 \n\t" \ - "adcs r7, r7, r7 \n\t" \ - "adcs r8, r8, r8 \n\t" \ - "adcs r9, r9, r9 \n\t" \ - "adcs r10, r10, r10 \n\t" \ - "adcs r11, r11, #0 \n\t" \ - \ - /* Add into previous */ \ - "ldr r12, [r0], #4 \n\t" \ - "adds r3, r3, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r4, r4, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r5, r5, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r6, r6, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r7, r7, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r8, r8, r12 \n\t" \ - "ldr r12, [r0], #4 \n\t" \ - "adcs r9, r9, r12 \n\t" \ - "adcs r10, r10, #0 \n\t" \ - "adcs r11, r11, #0 \n\t" \ - "sub r0, #28 \n\t" \ - \ - /* Perform center multiplication */ \ - "umlal r10, r11, r14, r14 \n\t" \ - "stmia r0!, {r3,r4,r5,r6,r7,r8,r9,r10,r11} \n\t" - -#define FAST_SQUARE_ASM_8 \ - "ldmia r1!, {r10,r11,r12,r14} \n\t" \ - "push {r2} \n\t" \ - \ - "umull r2, r3, r11, r10 \n\t" \ - "mov r4, #0 \n\t" \ - "umaal r3, r4, r12, r10 \n\t" \ - "mov r5, #0 \n\t" \ - "umaal r4, r5, r14, r10 \n\t" \ - \ - "mov r6, #0 \n\t" \ - "umaal r6, r4, r12, r11 \n\t" \ - "adds r2, r2, r2 \n\t" \ - "adcs r3, r3, r3 \n\t" \ - "adcs r6, r6, r6 \n\t" \ - /* Store carry in r7 */ \ - "mov r7, #0 \n\t" \ - "adc r7, r7, #0 \n\t" \ - \ - "umull r8, r9, r10, r10 \n\t" \ - "adds r9, r9, r2 \n\t" \ - "stmia r0!, {r8,r9} \n\t" \ - \ - "umull r8, r9, r11, r11 \n\t" \ - "adcs r8, r8, r3 \n\t" \ - "adcs r9, r9, r6 \n\t" \ - "stmia r0!, {r8,r9} \n\t" \ - /* Store carry in r8 */ \ - "mov r8, #0 \n\t" \ - "adc r8, r8, #0 \n\t" \ - \ - "ldmia r1!, {r2, r3} \n\t" \ - "push {r1} \n\t" \ - "umaal r4, r5, r2, r10 \n\t" \ - "mov r6, #0 \n\t" \ - "umaal r5, r6, r3, r10 \n\t" \ - \ - "mov r9, #0 \n\t" \ - "umaal r9, r4, r14, r11 \n\t" \ - "umaal r4, r5, r2, r11 \n\t" \ - \ - "mov r1, #0 \n\t" \ - "umaal r1, r4, r14, r12 \n\t" \ - \ - /* Load carry from r7 */ \ - "lsrs r7, #1 \n\t" \ - "adcs r9, r9, r9 \n\t" \ - "adcs r1, r1, r1 \n\t" \ - /* Store carry back in r7 */ \ - "adc r7, r7, #0 \n\t" \ - \ - /* Use carry from r8 */ \ - "umaal r8, r9, r12, r12 \n\t" \ - "adds r9, r9, r1 \n\t" \ - "stmia r0!, {r8,r9} \n\t" \ - /* Store carry back in r8 */ \ - "mov r8, #0 \n\t" \ - "adc r8, r8, #0 \n\t" \ - \ - "pop {r1} \n\t" \ - /* TODO could fix up r1 value on stack here */ \ - /* and leave the value on the stack (rather */ \ - /* than popping) if supporting curves > 256 bits */ \ - "ldr r9, [r1], #4 \n\t" \ - "ldr r1, [r1] \n\t" \ - \ - "push {r7} \n\t" \ - "umaal r5, r6, r9, r10 \n\t" \ - "mov r7, #0 \n\t" \ - "umaal r6, r7, r1, r10 \n\t" \ - /* Carry now stored in r10 */ \ - "pop {r10} \n\t" \ - \ - "umaal r4, r5, r3, r11 \n\t" \ - "umaal r5, r6, r9, r11 \n\t" \ - "umaal r6, r7, r1, r11 \n\t" \ - \ - "mov r11, #0 \n\t" \ - "umaal r11, r4, r2, r12 \n\t" \ - "umaal r4, r5, r3, r12 \n\t" \ - "umaal r5, r6, r9, r12 \n\t" \ - "umaal r6, r7, r1, r12 \n\t" \ - \ - "mov r12, #0 \n\t" \ - "umaal r12, r4, r2, r14 \n\t" \ - "umaal r4, r5, r3, r14 \n\t" \ - "umaal r5, r6, r9, r14 \n\t" \ - "umaal r6, r7, r1, r14 \n\t" \ - \ - /* Load carry from r10 */ \ - "lsrs r10, #1 \n\t" \ - "adcs r11, r11, r11 \n\t" \ - "adcs r12, r12, r12 \n\t" \ - "adc r10, r10, #0 \n\t" \ - \ - /* Use carry from r8 */ \ - "umaal r8, r11, r14, r14 \n\t" \ - "adds r11, r11, r12 \n\t" \ - "stmia r0!, {r8,r11} \n\t" \ - /* Store carry back in r8 */ \ - "mov r8, #0 \n\t" \ - "adc r8, r8, #0 \n\t" \ - \ - "mov r11, #0 \n\t" \ - "umaal r11, r5, r3, r2 \n\t" \ - "umaal r5, r6, r9, r2 \n\t" \ - "umaal r6, r7, r1, r2 \n\t" \ - \ - "mov r12, #0 \n\t" \ - "umaal r12, r6, r9, r3 \n\t" \ - "umaal r6, r7, r1, r3 \n\t" \ - \ - "mov r14, #0 \n\t" \ - "umaal r14, r7, r1, r9 \n\t" \ - \ - /* Load carry from r10 */ \ - "lsrs r10, #1 \n\t" \ - "adcs r4, r4, r4 \n\t" \ - "adcs r11, r11, r11 \n\t" \ - "adcs r5, r5, r5 \n\t" \ - "adcs r12, r12, r12 \n\t" \ - "adcs r6, r6, r6 \n\t" \ - "adcs r14, r14, r14 \n\t" \ - "adcs r7, r7, r7 \n\t" \ - "adc r10, r10, #0 \n\t" \ - \ - /* Use carry from r8 */ \ - "umaal r4, r8, r2, r2 \n\t" \ - "adds r8, r8, r11 \n\t" \ - "stmia r0!, {r4,r8} \n\t" \ - \ - "umull r4, r8, r3, r3 \n\t" \ - "adcs r4, r4, r5 \n\t" \ - "adcs r8, r8, r12 \n\t" \ - "stmia r0!, {r4,r8} \n\t" \ - \ - "umull r4, r8, r9, r9 \n\t" \ - "adcs r4, r4, r6 \n\t" \ - "adcs r8, r8, r14 \n\t" \ - "stmia r0!, {r4,r8} \n\t" \ - \ - "umull r4, r8, r1, r1 \n\t" \ - "adcs r4, r4, r7 \n\t" \ - "adcs r8, r8, r10 \n\t" \ - "stmia r0!, {r4,r8} \n\t" \ - /* TODO pop {r1, r2} if supporting curves > 256 bits */ \ - "pop {r2} \n\t" - -#endif /* _UECC_ASM_ARM_MULT_SQUARE_H_ */ diff --git a/lib/micro-ecc/curve-specific.inc b/lib/micro-ecc/curve-specific.inc deleted file mode 100644 index f5d3da842c8..00000000000 --- a/lib/micro-ecc/curve-specific.inc +++ /dev/null @@ -1,1249 +0,0 @@ -/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_CURVE_SPECIFIC_H_ -#define _UECC_CURVE_SPECIFIC_H_ - -#define num_bytes_secp160r1 20 -#define num_bytes_secp192r1 24 -#define num_bytes_secp224r1 28 -#define num_bytes_secp256r1 32 -#define num_bytes_secp256k1 32 - -#if (uECC_WORD_SIZE == 1) - -#define num_words_secp160r1 20 -#define num_words_secp192r1 24 -#define num_words_secp224r1 28 -#define num_words_secp256r1 32 -#define num_words_secp256k1 32 - -#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) \ - 0x##a, 0x##b, 0x##c, 0x##d, 0x##e, 0x##f, 0x##g, 0x##h -#define BYTES_TO_WORDS_4(a, b, c, d) 0x##a, 0x##b, 0x##c, 0x##d - -#elif (uECC_WORD_SIZE == 4) - -#define num_words_secp160r1 5 -#define num_words_secp192r1 6 -#define num_words_secp224r1 7 -#define num_words_secp256r1 8 -#define num_words_secp256k1 8 - -#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) 0x##d##c##b##a, 0x##h##g##f##e -#define BYTES_TO_WORDS_4(a, b, c, d) 0x##d##c##b##a - -#elif (uECC_WORD_SIZE == 8) - -#define num_words_secp160r1 3 -#define num_words_secp192r1 3 -#define num_words_secp224r1 4 -#define num_words_secp256r1 4 -#define num_words_secp256k1 4 - -#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) 0x##h##g##f##e##d##c##b##a##ull -#define BYTES_TO_WORDS_4(a, b, c, d) 0x##d##c##b##a##ull - -#endif /* uECC_WORD_SIZE */ - -#if uECC_SUPPORTS_secp160r1 || uECC_SUPPORTS_secp192r1 || \ - uECC_SUPPORTS_secp224r1 || uECC_SUPPORTS_secp256r1 -static void double_jacobian_default(uECC_word_t * X1, - uECC_word_t * Y1, - uECC_word_t * Z1, - uECC_Curve curve) { - /* t1 = X, t2 = Y, t3 = Z */ - uECC_word_t t4[uECC_MAX_WORDS]; - uECC_word_t t5[uECC_MAX_WORDS]; - wordcount_t num_words = curve->num_words; - - if (uECC_vli_isZero(Z1, num_words)) { - return; - } - - uECC_vli_modSquare_fast(t4, Y1, curve); /* t4 = y1^2 */ - uECC_vli_modMult_fast(t5, X1, t4, curve); /* t5 = x1*y1^2 = A */ - uECC_vli_modSquare_fast(t4, t4, curve); /* t4 = y1^4 */ - uECC_vli_modMult_fast(Y1, Y1, Z1, curve); /* t2 = y1*z1 = z3 */ - uECC_vli_modSquare_fast(Z1, Z1, curve); /* t3 = z1^2 */ - - uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = x1 + z1^2 */ - uECC_vli_modAdd(Z1, Z1, Z1, curve->p, num_words); /* t3 = 2*z1^2 */ - uECC_vli_modSub(Z1, X1, Z1, curve->p, num_words); /* t3 = x1 - z1^2 */ - uECC_vli_modMult_fast(X1, X1, Z1, curve); /* t1 = x1^2 - z1^4 */ - - uECC_vli_modAdd(Z1, X1, X1, curve->p, num_words); /* t3 = 2*(x1^2 - z1^4) */ - uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = 3*(x1^2 - z1^4) */ - if (uECC_vli_testBit(X1, 0)) { - uECC_word_t l_carry = uECC_vli_add(X1, X1, curve->p, num_words); - uECC_vli_rshift1(X1, num_words); - X1[num_words - 1] |= l_carry << (uECC_WORD_BITS - 1); - } else { - uECC_vli_rshift1(X1, num_words); - } - /* t1 = 3/2*(x1^2 - z1^4) = B */ - - uECC_vli_modSquare_fast(Z1, X1, curve); /* t3 = B^2 */ - uECC_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - A */ - uECC_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - 2A = x3 */ - uECC_vli_modSub(t5, t5, Z1, curve->p, num_words); /* t5 = A - x3 */ - uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = B * (A - x3) */ - uECC_vli_modSub(t4, X1, t4, curve->p, num_words); /* t4 = B * (A - x3) - y1^4 = y3 */ - - uECC_vli_set(X1, Z1, num_words); - uECC_vli_set(Z1, Y1, num_words); - uECC_vli_set(Y1, t4, num_words); -} - -/* Computes result = x^3 + ax + b. result must not overlap x. */ -static void x_side_default(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve) { - uECC_word_t _3[uECC_MAX_WORDS] = {3}; /* -a = 3 */ - wordcount_t num_words = curve->num_words; - - uECC_vli_modSquare_fast(result, x, curve); /* r = x^2 */ - uECC_vli_modSub(result, result, _3, curve->p, num_words); /* r = x^2 - 3 */ - uECC_vli_modMult_fast(result, result, x, curve); /* r = x^3 - 3x */ - uECC_vli_modAdd(result, result, curve->b, curve->p, num_words); /* r = x^3 - 3x + b */ -} -#endif /* uECC_SUPPORTS_secp... */ - -#if uECC_SUPPORT_COMPRESSED_POINT -#if uECC_SUPPORTS_secp160r1 || uECC_SUPPORTS_secp192r1 || \ - uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1 -/* Compute a = sqrt(a) (mod curve_p). */ -static void mod_sqrt_default(uECC_word_t *a, uECC_Curve curve) { - bitcount_t i; - uECC_word_t p1[uECC_MAX_WORDS] = {1}; - uECC_word_t l_result[uECC_MAX_WORDS] = {1}; - wordcount_t num_words = curve->num_words; - - /* When curve->p == 3 (mod 4), we can compute - sqrt(a) = a^((curve->p + 1) / 4) (mod curve->p). */ - uECC_vli_add(p1, curve->p, p1, num_words); /* p1 = curve_p + 1 */ - for (i = uECC_vli_numBits(p1, num_words) - 1; i > 1; --i) { - uECC_vli_modSquare_fast(l_result, l_result, curve); - if (uECC_vli_testBit(p1, i)) { - uECC_vli_modMult_fast(l_result, l_result, a, curve); - } - } - uECC_vli_set(a, l_result, num_words); -} -#endif /* uECC_SUPPORTS_secp... */ -#endif /* uECC_SUPPORT_COMPRESSED_POINT */ - -#if uECC_SUPPORTS_secp160r1 - -#if (uECC_OPTIMIZATION_LEVEL > 0) -static void vli_mmod_fast_secp160r1(uECC_word_t *result, uECC_word_t *product); -#endif - -static const struct uECC_Curve_t curve_secp160r1 = { - num_words_secp160r1, - num_bytes_secp160r1, - 161, /* num_n_bits */ - { BYTES_TO_WORDS_8(FF, FF, FF, 7F, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_4(FF, FF, FF, FF) }, - { BYTES_TO_WORDS_8(57, 22, 75, CA, D3, AE, 27, F9), - BYTES_TO_WORDS_8(C8, F4, 01, 00, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, 01, 00, 00, 00) }, - { BYTES_TO_WORDS_8(82, FC, CB, 13, B9, 8B, C3, 68), - BYTES_TO_WORDS_8(89, 69, 64, 46, 28, 73, F5, 8E), - BYTES_TO_WORDS_4(68, B5, 96, 4A), - - BYTES_TO_WORDS_8(32, FB, C5, 7A, 37, 51, 23, 04), - BYTES_TO_WORDS_8(12, C9, DC, 59, 7D, 94, 68, 31), - BYTES_TO_WORDS_4(55, 28, A6, 23) }, - { BYTES_TO_WORDS_8(45, FA, 65, C5, AD, D4, D4, 81), - BYTES_TO_WORDS_8(9F, F8, AC, 65, 8B, 7A, BD, 54), - BYTES_TO_WORDS_4(FC, BE, 97, 1C) }, - &double_jacobian_default, -#if uECC_SUPPORT_COMPRESSED_POINT - &mod_sqrt_default, -#endif - &x_side_default, -#if (uECC_OPTIMIZATION_LEVEL > 0) - &vli_mmod_fast_secp160r1 -#endif -}; - -uECC_Curve uECC_secp160r1(void) { return &curve_secp160r1; } - -#if (uECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp160r1) -/* Computes result = product % curve_p - see http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf page 354 - - Note that this only works if log2(omega) < log2(p) / 2 */ -static void omega_mult_secp160r1(uECC_word_t *result, const uECC_word_t *right); -#if uECC_WORD_SIZE == 8 -static void vli_mmod_fast_secp160r1(uECC_word_t *result, uECC_word_t *product) { - uECC_word_t tmp[2 * num_words_secp160r1]; - uECC_word_t copy; - - uECC_vli_clear(tmp, num_words_secp160r1); - uECC_vli_clear(tmp + num_words_secp160r1, num_words_secp160r1); - - omega_mult_secp160r1(tmp, product + num_words_secp160r1 - 1); /* (Rq, q) = q * c */ - - product[num_words_secp160r1 - 1] &= 0xffffffff; - copy = tmp[num_words_secp160r1 - 1]; - tmp[num_words_secp160r1 - 1] &= 0xffffffff; - uECC_vli_add(result, product, tmp, num_words_secp160r1); /* (C, r) = r + q */ - uECC_vli_clear(product, num_words_secp160r1); - tmp[num_words_secp160r1 - 1] = copy; - omega_mult_secp160r1(product, tmp + num_words_secp160r1 - 1); /* Rq*c */ - uECC_vli_add(result, result, product, num_words_secp160r1); /* (C1, r) = r + Rq*c */ - - while (uECC_vli_cmp_unsafe(result, curve_secp160r1.p, num_words_secp160r1) > 0) { - uECC_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1); - } -} - -static void omega_mult_secp160r1(uint64_t *result, const uint64_t *right) { - uint32_t carry; - unsigned i; - - /* Multiply by (2^31 + 1). */ - carry = 0; - for (i = 0; i < num_words_secp160r1; ++i) { - uint64_t tmp = (right[i] >> 32) | (right[i + 1] << 32); - result[i] = (tmp << 31) + tmp + carry; - carry = (tmp >> 33) + (result[i] < tmp || (carry && result[i] == tmp)); - } - result[i] = carry; -} -#else -static void vli_mmod_fast_secp160r1(uECC_word_t *result, uECC_word_t *product) { - uECC_word_t tmp[2 * num_words_secp160r1]; - uECC_word_t carry; - - uECC_vli_clear(tmp, num_words_secp160r1); - uECC_vli_clear(tmp + num_words_secp160r1, num_words_secp160r1); - - omega_mult_secp160r1(tmp, product + num_words_secp160r1); /* (Rq, q) = q * c */ - - carry = uECC_vli_add(result, product, tmp, num_words_secp160r1); /* (C, r) = r + q */ - uECC_vli_clear(product, num_words_secp160r1); - omega_mult_secp160r1(product, tmp + num_words_secp160r1); /* Rq*c */ - carry += uECC_vli_add(result, result, product, num_words_secp160r1); /* (C1, r) = r + Rq*c */ - - while (carry > 0) { - --carry; - uECC_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1); - } - if (uECC_vli_cmp_unsafe(result, curve_secp160r1.p, num_words_secp160r1) > 0) { - uECC_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1); - } -} -#endif - -#if uECC_WORD_SIZE == 1 -static void omega_mult_secp160r1(uint8_t *result, const uint8_t *right) { - uint8_t carry; - uint8_t i; - - /* Multiply by (2^31 + 1). */ - uECC_vli_set(result + 4, right, num_words_secp160r1); /* 2^32 */ - uECC_vli_rshift1(result + 4, num_words_secp160r1); /* 2^31 */ - result[3] = right[0] << 7; /* get last bit from shift */ - - carry = uECC_vli_add(result, result, right, num_words_secp160r1); /* 2^31 + 1 */ - for (i = num_words_secp160r1; carry; ++i) { - uint16_t sum = (uint16_t)result[i] + carry; - result[i] = (uint8_t)sum; - carry = sum >> 8; - } -} -#elif uECC_WORD_SIZE == 4 -static void omega_mult_secp160r1(uint32_t *result, const uint32_t *right) { - uint32_t carry; - unsigned i; - - /* Multiply by (2^31 + 1). */ - uECC_vli_set(result + 1, right, num_words_secp160r1); /* 2^32 */ - uECC_vli_rshift1(result + 1, num_words_secp160r1); /* 2^31 */ - result[0] = right[0] << 31; /* get last bit from shift */ - - carry = uECC_vli_add(result, result, right, num_words_secp160r1); /* 2^31 + 1 */ - for (i = num_words_secp160r1; carry; ++i) { - uint64_t sum = (uint64_t)result[i] + carry; - result[i] = (uint32_t)sum; - carry = sum >> 32; - } -} -#endif /* uECC_WORD_SIZE */ -#endif /* (uECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp160r1) */ - -#endif /* uECC_SUPPORTS_secp160r1 */ - -#if uECC_SUPPORTS_secp192r1 - -#if (uECC_OPTIMIZATION_LEVEL > 0) -static void vli_mmod_fast_secp192r1(uECC_word_t *result, uECC_word_t *product); -#endif - -static const struct uECC_Curve_t curve_secp192r1 = { - num_words_secp192r1, - num_bytes_secp192r1, - 192, /* num_n_bits */ - { BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FE, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF) }, - { BYTES_TO_WORDS_8(31, 28, D2, B4, B1, C9, 6B, 14), - BYTES_TO_WORDS_8(36, F8, DE, 99, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF) }, - { BYTES_TO_WORDS_8(12, 10, FF, 82, FD, 0A, FF, F4), - BYTES_TO_WORDS_8(00, 88, A1, 43, EB, 20, BF, 7C), - BYTES_TO_WORDS_8(F6, 90, 30, B0, 0E, A8, 8D, 18), - - BYTES_TO_WORDS_8(11, 48, 79, 1E, A1, 77, F9, 73), - BYTES_TO_WORDS_8(D5, CD, 24, 6B, ED, 11, 10, 63), - BYTES_TO_WORDS_8(78, DA, C8, FF, 95, 2B, 19, 07) }, - { BYTES_TO_WORDS_8(B1, B9, 46, C1, EC, DE, B8, FE), - BYTES_TO_WORDS_8(49, 30, 24, 72, AB, E9, A7, 0F), - BYTES_TO_WORDS_8(E7, 80, 9C, E5, 19, 05, 21, 64) }, - &double_jacobian_default, -#if uECC_SUPPORT_COMPRESSED_POINT - &mod_sqrt_default, -#endif - &x_side_default, -#if (uECC_OPTIMIZATION_LEVEL > 0) - &vli_mmod_fast_secp192r1 -#endif -}; - -uECC_Curve uECC_secp192r1(void) { return &curve_secp192r1; } - -#if (uECC_OPTIMIZATION_LEVEL > 0) -/* Computes result = product % curve_p. - See algorithm 5 and 6 from http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf */ -#if uECC_WORD_SIZE == 1 -static void vli_mmod_fast_secp192r1(uint8_t *result, uint8_t *product) { - uint8_t tmp[num_words_secp192r1]; - uint8_t carry; - - uECC_vli_set(result, product, num_words_secp192r1); - - uECC_vli_set(tmp, &product[24], num_words_secp192r1); - carry = uECC_vli_add(result, result, tmp, num_words_secp192r1); - - tmp[0] = tmp[1] = tmp[2] = tmp[3] = tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0; - tmp[8] = product[24]; tmp[9] = product[25]; tmp[10] = product[26]; tmp[11] = product[27]; - tmp[12] = product[28]; tmp[13] = product[29]; tmp[14] = product[30]; tmp[15] = product[31]; - tmp[16] = product[32]; tmp[17] = product[33]; tmp[18] = product[34]; tmp[19] = product[35]; - tmp[20] = product[36]; tmp[21] = product[37]; tmp[22] = product[38]; tmp[23] = product[39]; - carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); - - tmp[0] = tmp[8] = product[40]; - tmp[1] = tmp[9] = product[41]; - tmp[2] = tmp[10] = product[42]; - tmp[3] = tmp[11] = product[43]; - tmp[4] = tmp[12] = product[44]; - tmp[5] = tmp[13] = product[45]; - tmp[6] = tmp[14] = product[46]; - tmp[7] = tmp[15] = product[47]; - tmp[16] = tmp[17] = tmp[18] = tmp[19] = tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; - carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); - - while (carry || uECC_vli_cmp_unsafe(curve_secp192r1.p, result, num_words_secp192r1) != 1) { - carry -= uECC_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1); - } -} -#elif uECC_WORD_SIZE == 4 -static void vli_mmod_fast_secp192r1(uint32_t *result, uint32_t *product) { - uint32_t tmp[num_words_secp192r1]; - int carry; - - uECC_vli_set(result, product, num_words_secp192r1); - - uECC_vli_set(tmp, &product[6], num_words_secp192r1); - carry = uECC_vli_add(result, result, tmp, num_words_secp192r1); - - tmp[0] = tmp[1] = 0; - tmp[2] = product[6]; - tmp[3] = product[7]; - tmp[4] = product[8]; - tmp[5] = product[9]; - carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); - - tmp[0] = tmp[2] = product[10]; - tmp[1] = tmp[3] = product[11]; - tmp[4] = tmp[5] = 0; - carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); - - while (carry || uECC_vli_cmp_unsafe(curve_secp192r1.p, result, num_words_secp192r1) != 1) { - carry -= uECC_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1); - } -} -#else -static void vli_mmod_fast_secp192r1(uint64_t *result, uint64_t *product) { - uint64_t tmp[num_words_secp192r1]; - int carry; - - uECC_vli_set(result, product, num_words_secp192r1); - - uECC_vli_set(tmp, &product[3], num_words_secp192r1); - carry = (int)uECC_vli_add(result, result, tmp, num_words_secp192r1); - - tmp[0] = 0; - tmp[1] = product[3]; - tmp[2] = product[4]; - carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); - - tmp[0] = tmp[1] = product[5]; - tmp[2] = 0; - carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); - - while (carry || uECC_vli_cmp_unsafe(curve_secp192r1.p, result, num_words_secp192r1) != 1) { - carry -= uECC_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1); - } -} -#endif /* uECC_WORD_SIZE */ -#endif /* (uECC_OPTIMIZATION_LEVEL > 0) */ - -#endif /* uECC_SUPPORTS_secp192r1 */ - -#if uECC_SUPPORTS_secp224r1 - -#if uECC_SUPPORT_COMPRESSED_POINT -static void mod_sqrt_secp224r1(uECC_word_t *a, uECC_Curve curve); -#endif -#if (uECC_OPTIMIZATION_LEVEL > 0) -static void vli_mmod_fast_secp224r1(uECC_word_t *result, uECC_word_t *product); -#endif - -static const struct uECC_Curve_t curve_secp224r1 = { - num_words_secp224r1, - num_bytes_secp224r1, - 224, /* num_n_bits */ - { BYTES_TO_WORDS_8(01, 00, 00, 00, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_4(FF, FF, FF, FF) }, - { BYTES_TO_WORDS_8(3D, 2A, 5C, 5C, 45, 29, DD, 13), - BYTES_TO_WORDS_8(3E, F0, B8, E0, A2, 16, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_4(FF, FF, FF, FF) }, - { BYTES_TO_WORDS_8(21, 1D, 5C, 11, D6, 80, 32, 34), - BYTES_TO_WORDS_8(22, 11, C2, 56, D3, C1, 03, 4A), - BYTES_TO_WORDS_8(B9, 90, 13, 32, 7F, BF, B4, 6B), - BYTES_TO_WORDS_4(BD, 0C, 0E, B7), - - BYTES_TO_WORDS_8(34, 7E, 00, 85, 99, 81, D5, 44), - BYTES_TO_WORDS_8(64, 47, 07, 5A, A0, 75, 43, CD), - BYTES_TO_WORDS_8(E6, DF, 22, 4C, FB, 23, F7, B5), - BYTES_TO_WORDS_4(88, 63, 37, BD) }, - { BYTES_TO_WORDS_8(B4, FF, 55, 23, 43, 39, 0B, 27), - BYTES_TO_WORDS_8(BA, D8, BF, D7, B7, B0, 44, 50), - BYTES_TO_WORDS_8(56, 32, 41, F5, AB, B3, 04, 0C), - BYTES_TO_WORDS_4(85, 0A, 05, B4) }, - &double_jacobian_default, -#if uECC_SUPPORT_COMPRESSED_POINT - &mod_sqrt_secp224r1, -#endif - &x_side_default, -#if (uECC_OPTIMIZATION_LEVEL > 0) - &vli_mmod_fast_secp224r1 -#endif -}; - -uECC_Curve uECC_secp224r1(void) { return &curve_secp224r1; } - - -#if uECC_SUPPORT_COMPRESSED_POINT -/* Routine 3.2.4 RS; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -static void mod_sqrt_secp224r1_rs(uECC_word_t *d1, - uECC_word_t *e1, - uECC_word_t *f1, - const uECC_word_t *d0, - const uECC_word_t *e0, - const uECC_word_t *f0) { - uECC_word_t t[num_words_secp224r1]; - - uECC_vli_modSquare_fast(t, d0, &curve_secp224r1); /* t <-- d0 ^ 2 */ - uECC_vli_modMult_fast(e1, d0, e0, &curve_secp224r1); /* e1 <-- d0 * e0 */ - uECC_vli_modAdd(d1, t, f0, curve_secp224r1.p, num_words_secp224r1); /* d1 <-- t + f0 */ - uECC_vli_modAdd(e1, e1, e1, curve_secp224r1.p, num_words_secp224r1); /* e1 <-- e1 + e1 */ - uECC_vli_modMult_fast(f1, t, f0, &curve_secp224r1); /* f1 <-- t * f0 */ - uECC_vli_modAdd(f1, f1, f1, curve_secp224r1.p, num_words_secp224r1); /* f1 <-- f1 + f1 */ - uECC_vli_modAdd(f1, f1, f1, curve_secp224r1.p, num_words_secp224r1); /* f1 <-- f1 + f1 */ -} - -/* Routine 3.2.5 RSS; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -static void mod_sqrt_secp224r1_rss(uECC_word_t *d1, - uECC_word_t *e1, - uECC_word_t *f1, - const uECC_word_t *d0, - const uECC_word_t *e0, - const uECC_word_t *f0, - const bitcount_t j) { - bitcount_t i; - - uECC_vli_set(d1, d0, num_words_secp224r1); /* d1 <-- d0 */ - uECC_vli_set(e1, e0, num_words_secp224r1); /* e1 <-- e0 */ - uECC_vli_set(f1, f0, num_words_secp224r1); /* f1 <-- f0 */ - for (i = 1; i <= j; i++) { - mod_sqrt_secp224r1_rs(d1, e1, f1, d1, e1, f1); /* RS (d1,e1,f1,d1,e1,f1) */ - } -} - -/* Routine 3.2.6 RM; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -static void mod_sqrt_secp224r1_rm(uECC_word_t *d2, - uECC_word_t *e2, - uECC_word_t *f2, - const uECC_word_t *c, - const uECC_word_t *d0, - const uECC_word_t *e0, - const uECC_word_t *d1, - const uECC_word_t *e1) { - uECC_word_t t1[num_words_secp224r1]; - uECC_word_t t2[num_words_secp224r1]; - - uECC_vli_modMult_fast(t1, e0, e1, &curve_secp224r1); /* t1 <-- e0 * e1 */ - uECC_vli_modMult_fast(t1, t1, c, &curve_secp224r1); /* t1 <-- t1 * c */ - /* t1 <-- p - t1 */ - uECC_vli_modSub(t1, curve_secp224r1.p, t1, curve_secp224r1.p, num_words_secp224r1); - uECC_vli_modMult_fast(t2, d0, d1, &curve_secp224r1); /* t2 <-- d0 * d1 */ - uECC_vli_modAdd(t2, t2, t1, curve_secp224r1.p, num_words_secp224r1); /* t2 <-- t2 + t1 */ - uECC_vli_modMult_fast(t1, d0, e1, &curve_secp224r1); /* t1 <-- d0 * e1 */ - uECC_vli_modMult_fast(e2, d1, e0, &curve_secp224r1); /* e2 <-- d1 * e0 */ - uECC_vli_modAdd(e2, e2, t1, curve_secp224r1.p, num_words_secp224r1); /* e2 <-- e2 + t1 */ - uECC_vli_modSquare_fast(f2, e2, &curve_secp224r1); /* f2 <-- e2^2 */ - uECC_vli_modMult_fast(f2, f2, c, &curve_secp224r1); /* f2 <-- f2 * c */ - /* f2 <-- p - f2 */ - uECC_vli_modSub(f2, curve_secp224r1.p, f2, curve_secp224r1.p, num_words_secp224r1); - uECC_vli_set(d2, t2, num_words_secp224r1); /* d2 <-- t2 */ -} - -/* Routine 3.2.7 RP; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -static void mod_sqrt_secp224r1_rp(uECC_word_t *d1, - uECC_word_t *e1, - uECC_word_t *f1, - const uECC_word_t *c, - const uECC_word_t *r) { - wordcount_t i; - wordcount_t pow2i = 1; - uECC_word_t d0[num_words_secp224r1]; - uECC_word_t e0[num_words_secp224r1] = {1}; /* e0 <-- 1 */ - uECC_word_t f0[num_words_secp224r1]; - - uECC_vli_set(d0, r, num_words_secp224r1); /* d0 <-- r */ - /* f0 <-- p - c */ - uECC_vli_modSub(f0, curve_secp224r1.p, c, curve_secp224r1.p, num_words_secp224r1); - for (i = 0; i <= 6; i++) { - mod_sqrt_secp224r1_rss(d1, e1, f1, d0, e0, f0, pow2i); /* RSS (d1,e1,f1,d0,e0,f0,2^i) */ - mod_sqrt_secp224r1_rm(d1, e1, f1, c, d1, e1, d0, e0); /* RM (d1,e1,f1,c,d1,e1,d0,e0) */ - uECC_vli_set(d0, d1, num_words_secp224r1); /* d0 <-- d1 */ - uECC_vli_set(e0, e1, num_words_secp224r1); /* e0 <-- e1 */ - uECC_vli_set(f0, f1, num_words_secp224r1); /* f0 <-- f1 */ - pow2i *= 2; - } -} - -/* Compute a = sqrt(a) (mod curve_p). */ -/* Routine 3.2.8 mp_mod_sqrt_224; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -static void mod_sqrt_secp224r1(uECC_word_t *a, uECC_Curve curve) { - (void)curve; - bitcount_t i; - uECC_word_t e1[num_words_secp224r1]; - uECC_word_t f1[num_words_secp224r1]; - uECC_word_t d0[num_words_secp224r1]; - uECC_word_t e0[num_words_secp224r1]; - uECC_word_t f0[num_words_secp224r1]; - uECC_word_t d1[num_words_secp224r1]; - - /* s = a; using constant instead of random value */ - mod_sqrt_secp224r1_rp(d0, e0, f0, a, a); /* RP (d0, e0, f0, c, s) */ - mod_sqrt_secp224r1_rs(d1, e1, f1, d0, e0, f0); /* RS (d1, e1, f1, d0, e0, f0) */ - for (i = 1; i <= 95; i++) { - uECC_vli_set(d0, d1, num_words_secp224r1); /* d0 <-- d1 */ - uECC_vli_set(e0, e1, num_words_secp224r1); /* e0 <-- e1 */ - uECC_vli_set(f0, f1, num_words_secp224r1); /* f0 <-- f1 */ - mod_sqrt_secp224r1_rs(d1, e1, f1, d0, e0, f0); /* RS (d1, e1, f1, d0, e0, f0) */ - if (uECC_vli_isZero(d1, num_words_secp224r1)) { /* if d1 == 0 */ - break; - } - } - uECC_vli_modInv(f1, e0, curve_secp224r1.p, num_words_secp224r1); /* f1 <-- 1 / e0 */ - uECC_vli_modMult_fast(a, d0, f1, &curve_secp224r1); /* a <-- d0 / e0 */ -} -#endif /* uECC_SUPPORT_COMPRESSED_POINT */ - -#if (uECC_OPTIMIZATION_LEVEL > 0) -/* Computes result = product % curve_p - from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -#if uECC_WORD_SIZE == 1 -static void vli_mmod_fast_secp224r1(uint8_t *result, uint8_t *product) { - uint8_t tmp[num_words_secp224r1]; - int8_t carry; - - /* t */ - uECC_vli_set(result, product, num_words_secp224r1); - - /* s1 */ - tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0; - tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0; - tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0; - tmp[12] = product[28]; tmp[13] = product[29]; tmp[14] = product[30]; tmp[15] = product[31]; - tmp[16] = product[32]; tmp[17] = product[33]; tmp[18] = product[34]; tmp[19] = product[35]; - tmp[20] = product[36]; tmp[21] = product[37]; tmp[22] = product[38]; tmp[23] = product[39]; - tmp[24] = product[40]; tmp[25] = product[41]; tmp[26] = product[42]; tmp[27] = product[43]; - carry = uECC_vli_add(result, result, tmp, num_words_secp224r1); - - /* s2 */ - tmp[12] = product[44]; tmp[13] = product[45]; tmp[14] = product[46]; tmp[15] = product[47]; - tmp[16] = product[48]; tmp[17] = product[49]; tmp[18] = product[50]; tmp[19] = product[51]; - tmp[20] = product[52]; tmp[21] = product[53]; tmp[22] = product[54]; tmp[23] = product[55]; - tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; - carry += uECC_vli_add(result, result, tmp, num_words_secp224r1); - - /* d1 */ - tmp[0] = product[28]; tmp[1] = product[29]; tmp[2] = product[30]; tmp[3] = product[31]; - tmp[4] = product[32]; tmp[5] = product[33]; tmp[6] = product[34]; tmp[7] = product[35]; - tmp[8] = product[36]; tmp[9] = product[37]; tmp[10] = product[38]; tmp[11] = product[39]; - tmp[12] = product[40]; tmp[13] = product[41]; tmp[14] = product[42]; tmp[15] = product[43]; - tmp[16] = product[44]; tmp[17] = product[45]; tmp[18] = product[46]; tmp[19] = product[47]; - tmp[20] = product[48]; tmp[21] = product[49]; tmp[22] = product[50]; tmp[23] = product[51]; - tmp[24] = product[52]; tmp[25] = product[53]; tmp[26] = product[54]; tmp[27] = product[55]; - carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); - - /* d2 */ - tmp[0] = product[44]; tmp[1] = product[45]; tmp[2] = product[46]; tmp[3] = product[47]; - tmp[4] = product[48]; tmp[5] = product[49]; tmp[6] = product[50]; tmp[7] = product[51]; - tmp[8] = product[52]; tmp[9] = product[53]; tmp[10] = product[54]; tmp[11] = product[55]; - tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0; - tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; - tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; - tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; - carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); - - if (carry < 0) { - do { - carry += uECC_vli_add(result, result, curve_secp224r1.p, num_words_secp224r1); - } while (carry < 0); - } else { - while (carry || uECC_vli_cmp_unsafe(curve_secp224r1.p, result, num_words_secp224r1) != 1) { - carry -= uECC_vli_sub(result, result, curve_secp224r1.p, num_words_secp224r1); - } - } -} -#elif uECC_WORD_SIZE == 4 -static void vli_mmod_fast_secp224r1(uint32_t *result, uint32_t *product) -{ - uint32_t tmp[num_words_secp224r1]; - int carry; - - /* t */ - uECC_vli_set(result, product, num_words_secp224r1); - - /* s1 */ - tmp[0] = tmp[1] = tmp[2] = 0; - tmp[3] = product[7]; - tmp[4] = product[8]; - tmp[5] = product[9]; - tmp[6] = product[10]; - carry = uECC_vli_add(result, result, tmp, num_words_secp224r1); - - /* s2 */ - tmp[3] = product[11]; - tmp[4] = product[12]; - tmp[5] = product[13]; - tmp[6] = 0; - carry += uECC_vli_add(result, result, tmp, num_words_secp224r1); - - /* d1 */ - tmp[0] = product[7]; - tmp[1] = product[8]; - tmp[2] = product[9]; - tmp[3] = product[10]; - tmp[4] = product[11]; - tmp[5] = product[12]; - tmp[6] = product[13]; - carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); - - /* d2 */ - tmp[0] = product[11]; - tmp[1] = product[12]; - tmp[2] = product[13]; - tmp[3] = tmp[4] = tmp[5] = tmp[6] = 0; - carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); - - if (carry < 0) { - do { - carry += uECC_vli_add(result, result, curve_secp224r1.p, num_words_secp224r1); - } while (carry < 0); - } else { - while (carry || uECC_vli_cmp_unsafe(curve_secp224r1.p, result, num_words_secp224r1) != 1) { - carry -= uECC_vli_sub(result, result, curve_secp224r1.p, num_words_secp224r1); - } - } -} -#else -static void vli_mmod_fast_secp224r1(uint64_t *result, uint64_t *product) -{ - uint64_t tmp[num_words_secp224r1]; - int carry = 0; - - /* t */ - uECC_vli_set(result, product, num_words_secp224r1); - result[num_words_secp224r1 - 1] &= 0xffffffff; - - /* s1 */ - tmp[0] = 0; - tmp[1] = product[3] & 0xffffffff00000000ull; - tmp[2] = product[4]; - tmp[3] = product[5] & 0xffffffff; - uECC_vli_add(result, result, tmp, num_words_secp224r1); - - /* s2 */ - tmp[1] = product[5] & 0xffffffff00000000ull; - tmp[2] = product[6]; - tmp[3] = 0; - uECC_vli_add(result, result, tmp, num_words_secp224r1); - - /* d1 */ - tmp[0] = (product[3] >> 32) | (product[4] << 32); - tmp[1] = (product[4] >> 32) | (product[5] << 32); - tmp[2] = (product[5] >> 32) | (product[6] << 32); - tmp[3] = product[6] >> 32; - carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); - - /* d2 */ - tmp[0] = (product[5] >> 32) | (product[6] << 32); - tmp[1] = product[6] >> 32; - tmp[2] = tmp[3] = 0; - carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); - - if (carry < 0) { - do { - carry += uECC_vli_add(result, result, curve_secp224r1.p, num_words_secp224r1); - } while (carry < 0); - } else { - while (uECC_vli_cmp_unsafe(curve_secp224r1.p, result, num_words_secp224r1) != 1) { - uECC_vli_sub(result, result, curve_secp224r1.p, num_words_secp224r1); - } - } -} -#endif /* uECC_WORD_SIZE */ -#endif /* (uECC_OPTIMIZATION_LEVEL > 0) */ - -#endif /* uECC_SUPPORTS_secp224r1 */ - -#if uECC_SUPPORTS_secp256r1 - -#if (uECC_OPTIMIZATION_LEVEL > 0) -static void vli_mmod_fast_secp256r1(uECC_word_t *result, uECC_word_t *product); -#endif - -static const struct uECC_Curve_t curve_secp256r1 = { - num_words_secp256r1, - num_bytes_secp256r1, - 256, /* num_n_bits */ - { BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), - BYTES_TO_WORDS_8(01, 00, 00, 00, FF, FF, FF, FF) }, - { BYTES_TO_WORDS_8(51, 25, 63, FC, C2, CA, B9, F3), - BYTES_TO_WORDS_8(84, 9E, 17, A7, AD, FA, E6, BC), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF) }, - { BYTES_TO_WORDS_8(96, C2, 98, D8, 45, 39, A1, F4), - BYTES_TO_WORDS_8(A0, 33, EB, 2D, 81, 7D, 03, 77), - BYTES_TO_WORDS_8(F2, 40, A4, 63, E5, E6, BC, F8), - BYTES_TO_WORDS_8(47, 42, 2C, E1, F2, D1, 17, 6B), - - BYTES_TO_WORDS_8(F5, 51, BF, 37, 68, 40, B6, CB), - BYTES_TO_WORDS_8(CE, 5E, 31, 6B, 57, 33, CE, 2B), - BYTES_TO_WORDS_8(16, 9E, 0F, 7C, 4A, EB, E7, 8E), - BYTES_TO_WORDS_8(9B, 7F, 1A, FE, E2, 42, E3, 4F) }, - { BYTES_TO_WORDS_8(4B, 60, D2, 27, 3E, 3C, CE, 3B), - BYTES_TO_WORDS_8(F6, B0, 53, CC, B0, 06, 1D, 65), - BYTES_TO_WORDS_8(BC, 86, 98, 76, 55, BD, EB, B3), - BYTES_TO_WORDS_8(E7, 93, 3A, AA, D8, 35, C6, 5A) }, - &double_jacobian_default, -#if uECC_SUPPORT_COMPRESSED_POINT - &mod_sqrt_default, -#endif - &x_side_default, -#if (uECC_OPTIMIZATION_LEVEL > 0) - &vli_mmod_fast_secp256r1 -#endif -}; - -uECC_Curve uECC_secp256r1(void) { return &curve_secp256r1; } - - -#if (uECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256r1) -/* Computes result = product % curve_p - from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -#if uECC_WORD_SIZE == 1 -static void vli_mmod_fast_secp256r1(uint8_t *result, uint8_t *product) { - uint8_t tmp[num_words_secp256r1]; - int8_t carry; - - /* t */ - uECC_vli_set(result, product, num_words_secp256r1); - - /* s1 */ - tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0; - tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0; - tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0; - tmp[12] = product[44]; tmp[13] = product[45]; tmp[14] = product[46]; tmp[15] = product[47]; - tmp[16] = product[48]; tmp[17] = product[49]; tmp[18] = product[50]; tmp[19] = product[51]; - tmp[20] = product[52]; tmp[21] = product[53]; tmp[22] = product[54]; tmp[23] = product[55]; - tmp[24] = product[56]; tmp[25] = product[57]; tmp[26] = product[58]; tmp[27] = product[59]; - tmp[28] = product[60]; tmp[29] = product[61]; tmp[30] = product[62]; tmp[31] = product[63]; - carry = uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); - carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); - - /* s2 */ - tmp[12] = product[48]; tmp[13] = product[49]; tmp[14] = product[50]; tmp[15] = product[51]; - tmp[16] = product[52]; tmp[17] = product[53]; tmp[18] = product[54]; tmp[19] = product[55]; - tmp[20] = product[56]; tmp[21] = product[57]; tmp[22] = product[58]; tmp[23] = product[59]; - tmp[24] = product[60]; tmp[25] = product[61]; tmp[26] = product[62]; tmp[27] = product[63]; - tmp[28] = tmp[29] = tmp[30] = tmp[31] = 0; - carry += uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); - carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); - - /* s3 */ - tmp[0] = product[32]; tmp[1] = product[33]; tmp[2] = product[34]; tmp[3] = product[35]; - tmp[4] = product[36]; tmp[5] = product[37]; tmp[6] = product[38]; tmp[7] = product[39]; - tmp[8] = product[40]; tmp[9] = product[41]; tmp[10] = product[42]; tmp[11] = product[43]; - tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0; - tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; - tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; - tmp[24] = product[56]; tmp[25] = product[57]; tmp[26] = product[58]; tmp[27] = product[59]; - tmp[28] = product[60]; tmp[29] = product[61]; tmp[30] = product[62]; tmp[31] = product[63]; - carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); - - /* s4 */ - tmp[0] = product[36]; tmp[1] = product[37]; tmp[2] = product[38]; tmp[3] = product[39]; - tmp[4] = product[40]; tmp[5] = product[41]; tmp[6] = product[42]; tmp[7] = product[43]; - tmp[8] = product[44]; tmp[9] = product[45]; tmp[10] = product[46]; tmp[11] = product[47]; - tmp[12] = product[52]; tmp[13] = product[53]; tmp[14] = product[54]; tmp[15] = product[55]; - tmp[16] = product[56]; tmp[17] = product[57]; tmp[18] = product[58]; tmp[19] = product[59]; - tmp[20] = product[60]; tmp[21] = product[61]; tmp[22] = product[62]; tmp[23] = product[63]; - tmp[24] = product[52]; tmp[25] = product[53]; tmp[26] = product[54]; tmp[27] = product[55]; - tmp[28] = product[32]; tmp[29] = product[33]; tmp[30] = product[34]; tmp[31] = product[35]; - carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); - - /* d1 */ - tmp[0] = product[44]; tmp[1] = product[45]; tmp[2] = product[46]; tmp[3] = product[47]; - tmp[4] = product[48]; tmp[5] = product[49]; tmp[6] = product[50]; tmp[7] = product[51]; - tmp[8] = product[52]; tmp[9] = product[53]; tmp[10] = product[54]; tmp[11] = product[55]; - tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0; - tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; - tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; - tmp[24] = product[32]; tmp[25] = product[33]; tmp[26] = product[34]; tmp[27] = product[35]; - tmp[28] = product[40]; tmp[29] = product[41]; tmp[30] = product[42]; tmp[31] = product[43]; - carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d2 */ - tmp[0] = product[48]; tmp[1] = product[49]; tmp[2] = product[50]; tmp[3] = product[51]; - tmp[4] = product[52]; tmp[5] = product[53]; tmp[6] = product[54]; tmp[7] = product[55]; - tmp[8] = product[56]; tmp[9] = product[57]; tmp[10] = product[58]; tmp[11] = product[59]; - tmp[12] = product[60]; tmp[13] = product[61]; tmp[14] = product[62]; tmp[15] = product[63]; - tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; - tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; - tmp[24] = product[36]; tmp[25] = product[37]; tmp[26] = product[38]; tmp[27] = product[39]; - tmp[28] = product[44]; tmp[29] = product[45]; tmp[30] = product[46]; tmp[31] = product[47]; - carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d3 */ - tmp[0] = product[52]; tmp[1] = product[53]; tmp[2] = product[54]; tmp[3] = product[55]; - tmp[4] = product[56]; tmp[5] = product[57]; tmp[6] = product[58]; tmp[7] = product[59]; - tmp[8] = product[60]; tmp[9] = product[61]; tmp[10] = product[62]; tmp[11] = product[63]; - tmp[12] = product[32]; tmp[13] = product[33]; tmp[14] = product[34]; tmp[15] = product[35]; - tmp[16] = product[36]; tmp[17] = product[37]; tmp[18] = product[38]; tmp[19] = product[39]; - tmp[20] = product[40]; tmp[21] = product[41]; tmp[22] = product[42]; tmp[23] = product[43]; - tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; - tmp[28] = product[48]; tmp[29] = product[49]; tmp[30] = product[50]; tmp[31] = product[51]; - carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d4 */ - tmp[0] = product[56]; tmp[1] = product[57]; tmp[2] = product[58]; tmp[3] = product[59]; - tmp[4] = product[60]; tmp[5] = product[61]; tmp[6] = product[62]; tmp[7] = product[63]; - tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0; - tmp[12] = product[36]; tmp[13] = product[37]; tmp[14] = product[38]; tmp[15] = product[39]; - tmp[16] = product[40]; tmp[17] = product[41]; tmp[18] = product[42]; tmp[19] = product[43]; - tmp[20] = product[44]; tmp[21] = product[45]; tmp[22] = product[46]; tmp[23] = product[47]; - tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; - tmp[28] = product[52]; tmp[29] = product[53]; tmp[30] = product[54]; tmp[31] = product[55]; - carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); - - if (carry < 0) { - do { - carry += uECC_vli_add(result, result, curve_secp256r1.p, num_words_secp256r1); - } while (carry < 0); - } else { - while (carry || uECC_vli_cmp_unsafe(curve_secp256r1.p, result, num_words_secp256r1) != 1) { - carry -= uECC_vli_sub(result, result, curve_secp256r1.p, num_words_secp256r1); - } - } -} -#elif uECC_WORD_SIZE == 4 -static void vli_mmod_fast_secp256r1(uint32_t *result, uint32_t *product) { - uint32_t tmp[num_words_secp256r1]; - int carry; - - /* t */ - uECC_vli_set(result, product, num_words_secp256r1); - - /* s1 */ - tmp[0] = tmp[1] = tmp[2] = 0; - tmp[3] = product[11]; - tmp[4] = product[12]; - tmp[5] = product[13]; - tmp[6] = product[14]; - tmp[7] = product[15]; - carry = uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); - carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); - - /* s2 */ - tmp[3] = product[12]; - tmp[4] = product[13]; - tmp[5] = product[14]; - tmp[6] = product[15]; - tmp[7] = 0; - carry += uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); - carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); - - /* s3 */ - tmp[0] = product[8]; - tmp[1] = product[9]; - tmp[2] = product[10]; - tmp[3] = tmp[4] = tmp[5] = 0; - tmp[6] = product[14]; - tmp[7] = product[15]; - carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); - - /* s4 */ - tmp[0] = product[9]; - tmp[1] = product[10]; - tmp[2] = product[11]; - tmp[3] = product[13]; - tmp[4] = product[14]; - tmp[5] = product[15]; - tmp[6] = product[13]; - tmp[7] = product[8]; - carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); - - /* d1 */ - tmp[0] = product[11]; - tmp[1] = product[12]; - tmp[2] = product[13]; - tmp[3] = tmp[4] = tmp[5] = 0; - tmp[6] = product[8]; - tmp[7] = product[10]; - carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d2 */ - tmp[0] = product[12]; - tmp[1] = product[13]; - tmp[2] = product[14]; - tmp[3] = product[15]; - tmp[4] = tmp[5] = 0; - tmp[6] = product[9]; - tmp[7] = product[11]; - carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d3 */ - tmp[0] = product[13]; - tmp[1] = product[14]; - tmp[2] = product[15]; - tmp[3] = product[8]; - tmp[4] = product[9]; - tmp[5] = product[10]; - tmp[6] = 0; - tmp[7] = product[12]; - carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d4 */ - tmp[0] = product[14]; - tmp[1] = product[15]; - tmp[2] = 0; - tmp[3] = product[9]; - tmp[4] = product[10]; - tmp[5] = product[11]; - tmp[6] = 0; - tmp[7] = product[13]; - carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); - - if (carry < 0) { - do { - carry += uECC_vli_add(result, result, curve_secp256r1.p, num_words_secp256r1); - } while (carry < 0); - } else { - while (carry || uECC_vli_cmp_unsafe(curve_secp256r1.p, result, num_words_secp256r1) != 1) { - carry -= uECC_vli_sub(result, result, curve_secp256r1.p, num_words_secp256r1); - } - } -} -#else -static void vli_mmod_fast_secp256r1(uint64_t *result, uint64_t *product) { - uint64_t tmp[num_words_secp256r1]; - int carry; - - /* t */ - uECC_vli_set(result, product, num_words_secp256r1); - - /* s1 */ - tmp[0] = 0; - tmp[1] = product[5] & 0xffffffff00000000ull; - tmp[2] = product[6]; - tmp[3] = product[7]; - carry = (int)uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); - carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); - - /* s2 */ - tmp[1] = product[6] << 32; - tmp[2] = (product[6] >> 32) | (product[7] << 32); - tmp[3] = product[7] >> 32; - carry += uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); - carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); - - /* s3 */ - tmp[0] = product[4]; - tmp[1] = product[5] & 0xffffffff; - tmp[2] = 0; - tmp[3] = product[7]; - carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); - - /* s4 */ - tmp[0] = (product[4] >> 32) | (product[5] << 32); - tmp[1] = (product[5] >> 32) | (product[6] & 0xffffffff00000000ull); - tmp[2] = product[7]; - tmp[3] = (product[6] >> 32) | (product[4] << 32); - carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); - - /* d1 */ - tmp[0] = (product[5] >> 32) | (product[6] << 32); - tmp[1] = (product[6] >> 32); - tmp[2] = 0; - tmp[3] = (product[4] & 0xffffffff) | (product[5] << 32); - carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d2 */ - tmp[0] = product[6]; - tmp[1] = product[7]; - tmp[2] = 0; - tmp[3] = (product[4] >> 32) | (product[5] & 0xffffffff00000000ull); - carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d3 */ - tmp[0] = (product[6] >> 32) | (product[7] << 32); - tmp[1] = (product[7] >> 32) | (product[4] << 32); - tmp[2] = (product[4] >> 32) | (product[5] << 32); - tmp[3] = (product[6] << 32); - carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); - - /* d4 */ - tmp[0] = product[7]; - tmp[1] = product[4] & 0xffffffff00000000ull; - tmp[2] = product[5]; - tmp[3] = product[6] & 0xffffffff00000000ull; - carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); - - if (carry < 0) { - do { - carry += uECC_vli_add(result, result, curve_secp256r1.p, num_words_secp256r1); - } while (carry < 0); - } else { - while (carry || uECC_vli_cmp_unsafe(curve_secp256r1.p, result, num_words_secp256r1) != 1) { - carry -= uECC_vli_sub(result, result, curve_secp256r1.p, num_words_secp256r1); - } - } -} -#endif /* uECC_WORD_SIZE */ -#endif /* (uECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256r1) */ - -#endif /* uECC_SUPPORTS_secp256r1 */ - -#if uECC_SUPPORTS_secp256k1 - -static void double_jacobian_secp256k1(uECC_word_t * X1, - uECC_word_t * Y1, - uECC_word_t * Z1, - uECC_Curve curve); -static void x_side_secp256k1(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve); -#if (uECC_OPTIMIZATION_LEVEL > 0) -static void vli_mmod_fast_secp256k1(uECC_word_t *result, uECC_word_t *product); -#endif - -static const struct uECC_Curve_t curve_secp256k1 = { - num_words_secp256k1, - num_bytes_secp256k1, - 256, /* num_n_bits */ - { BYTES_TO_WORDS_8(2F, FC, FF, FF, FE, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF) }, - { BYTES_TO_WORDS_8(41, 41, 36, D0, 8C, 5E, D2, BF), - BYTES_TO_WORDS_8(3B, A0, 48, AF, E6, DC, AE, BA), - BYTES_TO_WORDS_8(FE, FF, FF, FF, FF, FF, FF, FF), - BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF) }, - { BYTES_TO_WORDS_8(98, 17, F8, 16, 5B, 81, F2, 59), - BYTES_TO_WORDS_8(D9, 28, CE, 2D, DB, FC, 9B, 02), - BYTES_TO_WORDS_8(07, 0B, 87, CE, 95, 62, A0, 55), - BYTES_TO_WORDS_8(AC, BB, DC, F9, 7E, 66, BE, 79), - - BYTES_TO_WORDS_8(B8, D4, 10, FB, 8F, D0, 47, 9C), - BYTES_TO_WORDS_8(19, 54, 85, A6, 48, B4, 17, FD), - BYTES_TO_WORDS_8(A8, 08, 11, 0E, FC, FB, A4, 5D), - BYTES_TO_WORDS_8(65, C4, A3, 26, 77, DA, 3A, 48) }, - { BYTES_TO_WORDS_8(07, 00, 00, 00, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), - BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00) }, - &double_jacobian_secp256k1, -#if uECC_SUPPORT_COMPRESSED_POINT - &mod_sqrt_default, -#endif - &x_side_secp256k1, -#if (uECC_OPTIMIZATION_LEVEL > 0) - &vli_mmod_fast_secp256k1 -#endif -}; - -uECC_Curve uECC_secp256k1(void) { return &curve_secp256k1; } - - -/* Double in place */ -static void double_jacobian_secp256k1(uECC_word_t * X1, - uECC_word_t * Y1, - uECC_word_t * Z1, - uECC_Curve curve) { - /* t1 = X, t2 = Y, t3 = Z */ - uECC_word_t t4[num_words_secp256k1]; - uECC_word_t t5[num_words_secp256k1]; - - if (uECC_vli_isZero(Z1, num_words_secp256k1)) { - return; - } - - uECC_vli_modSquare_fast(t5, Y1, curve); /* t5 = y1^2 */ - uECC_vli_modMult_fast(t4, X1, t5, curve); /* t4 = x1*y1^2 = A */ - uECC_vli_modSquare_fast(X1, X1, curve); /* t1 = x1^2 */ - uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = y1^4 */ - uECC_vli_modMult_fast(Z1, Y1, Z1, curve); /* t3 = y1*z1 = z3 */ - - uECC_vli_modAdd(Y1, X1, X1, curve->p, num_words_secp256k1); /* t2 = 2*x1^2 */ - uECC_vli_modAdd(Y1, Y1, X1, curve->p, num_words_secp256k1); /* t2 = 3*x1^2 */ - if (uECC_vli_testBit(Y1, 0)) { - uECC_word_t carry = uECC_vli_add(Y1, Y1, curve->p, num_words_secp256k1); - uECC_vli_rshift1(Y1, num_words_secp256k1); - Y1[num_words_secp256k1 - 1] |= carry << (uECC_WORD_BITS - 1); - } else { - uECC_vli_rshift1(Y1, num_words_secp256k1); - } - /* t2 = 3/2*(x1^2) = B */ - - uECC_vli_modSquare_fast(X1, Y1, curve); /* t1 = B^2 */ - uECC_vli_modSub(X1, X1, t4, curve->p, num_words_secp256k1); /* t1 = B^2 - A */ - uECC_vli_modSub(X1, X1, t4, curve->p, num_words_secp256k1); /* t1 = B^2 - 2A = x3 */ - - uECC_vli_modSub(t4, t4, X1, curve->p, num_words_secp256k1); /* t4 = A - x3 */ - uECC_vli_modMult_fast(Y1, Y1, t4, curve); /* t2 = B * (A - x3) */ - uECC_vli_modSub(Y1, Y1, t5, curve->p, num_words_secp256k1); /* t2 = B * (A - x3) - y1^4 = y3 */ -} - -/* Computes result = x^3 + b. result must not overlap x. */ -static void x_side_secp256k1(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve) { - uECC_vli_modSquare_fast(result, x, curve); /* r = x^2 */ - uECC_vli_modMult_fast(result, result, x, curve); /* r = x^3 */ - uECC_vli_modAdd(result, result, curve->b, curve->p, num_words_secp256k1); /* r = x^3 + b */ -} - -#if (uECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256k1) -static void omega_mult_secp256k1(uECC_word_t *result, const uECC_word_t *right); -static void vli_mmod_fast_secp256k1(uECC_word_t *result, uECC_word_t *product) { - uECC_word_t tmp[2 * num_words_secp256k1]; - uECC_word_t carry; - - uECC_vli_clear(tmp, num_words_secp256k1); - uECC_vli_clear(tmp + num_words_secp256k1, num_words_secp256k1); - - omega_mult_secp256k1(tmp, product + num_words_secp256k1); /* (Rq, q) = q * c */ - - carry = uECC_vli_add(result, product, tmp, num_words_secp256k1); /* (C, r) = r + q */ - uECC_vli_clear(product, num_words_secp256k1); - omega_mult_secp256k1(product, tmp + num_words_secp256k1); /* Rq*c */ - carry += uECC_vli_add(result, result, product, num_words_secp256k1); /* (C1, r) = r + Rq*c */ - - while (carry > 0) { - --carry; - uECC_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); - } - if (uECC_vli_cmp_unsafe(result, curve_secp256k1.p, num_words_secp256k1) > 0) { - uECC_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); - } -} - -#if uECC_WORD_SIZE == 1 -static void omega_mult_secp256k1(uint8_t * result, const uint8_t * right) { - /* Multiply by (2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ - uECC_word_t r0 = 0; - uECC_word_t r1 = 0; - uECC_word_t r2 = 0; - wordcount_t k; - - /* Multiply by (2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ - muladd(0xD1, right[0], &r0, &r1, &r2); - result[0] = r0; - r0 = r1; - r1 = r2; - /* r2 is still 0 */ - - for (k = 1; k < num_words_secp256k1; ++k) { - muladd(0x03, right[k - 1], &r0, &r1, &r2); - muladd(0xD1, right[k], &r0, &r1, &r2); - result[k] = r0; - r0 = r1; - r1 = r2; - r2 = 0; - } - muladd(0x03, right[num_words_secp256k1 - 1], &r0, &r1, &r2); - result[num_words_secp256k1] = r0; - result[num_words_secp256k1 + 1] = r1; - /* add the 2^32 multiple */ - result[4 + num_words_secp256k1] = - uECC_vli_add(result + 4, result + 4, right, num_words_secp256k1); -} -#elif uECC_WORD_SIZE == 4 -static void omega_mult_secp256k1(uint32_t * result, const uint32_t * right) { - /* Multiply by (2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ - uint32_t carry = 0; - wordcount_t k; - - for (k = 0; k < num_words_secp256k1; ++k) { - uint64_t p = (uint64_t)0x3D1 * right[k] + carry; - result[k] = (uint32_t) p; - carry = p >> 32; - } - result[num_words_secp256k1] = carry; - /* add the 2^32 multiple */ - result[1 + num_words_secp256k1] = - uECC_vli_add(result + 1, result + 1, right, num_words_secp256k1); -} -#else -static void omega_mult_secp256k1(uint64_t * result, const uint64_t * right) { - uECC_word_t r0 = 0; - uECC_word_t r1 = 0; - uECC_word_t r2 = 0; - wordcount_t k; - - /* Multiply by (2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ - for (k = 0; k < num_words_secp256k1; ++k) { - muladd(0x1000003D1ull, right[k], &r0, &r1, &r2); - result[k] = r0; - r0 = r1; - r1 = r2; - r2 = 0; - } - result[num_words_secp256k1] = r0; -} -#endif /* uECC_WORD_SIZE */ -#endif /* (uECC_OPTIMIZATION_LEVEL > 0 && && !asm_mmod_fast_secp256k1) */ - -#endif /* uECC_SUPPORTS_secp256k1 */ - -#endif /* _UECC_CURVE_SPECIFIC_H_ */ diff --git a/lib/micro-ecc/platform-specific.inc b/lib/micro-ecc/platform-specific.inc deleted file mode 100644 index 7e0373f5059..00000000000 --- a/lib/micro-ecc/platform-specific.inc +++ /dev/null @@ -1,94 +0,0 @@ -/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_PLATFORM_SPECIFIC_H_ -#define _UECC_PLATFORM_SPECIFIC_H_ - -#include "types.h" - -#if (defined(_WIN32) || defined(_WIN64)) -/* Windows */ - -// use pragma syntax to prevent tweaking the linker script for getting CryptXYZ function -#pragma comment(lib, "crypt32.lib") -#pragma comment(lib, "advapi32.lib") - -#define WIN32_LEAN_AND_MEAN -#include -#include - -static int default_RNG(uint8_t *dest, unsigned size) { - HCRYPTPROV prov; - if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { - return 0; - } - - CryptGenRandom(prov, size, (BYTE *)dest); - CryptReleaseContext(prov, 0); - return 1; -} -#define default_RNG_defined 1 - -#elif defined(unix) || defined(__linux__) || defined(__unix__) || defined(__unix) || \ - (defined(__APPLE__) && defined(__MACH__)) || defined(uECC_POSIX) - -/* Some POSIX-like system with /dev/urandom or /dev/random. */ -#include -#include -#include - -#ifndef O_CLOEXEC - #define O_CLOEXEC 0 -#endif - -static int default_RNG(uint8_t *dest, unsigned size) { - int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); - if (fd == -1) { - fd = open("/dev/random", O_RDONLY | O_CLOEXEC); - if (fd == -1) { - return 0; - } - } - - char *ptr = (char *)dest; - size_t left = size; - while (left > 0) { - ssize_t bytes_read = read(fd, ptr, left); - if (bytes_read <= 0) { // read failed - close(fd); - return 0; - } - left -= bytes_read; - ptr += bytes_read; - } - - close(fd); - return 1; -} -#define default_RNG_defined 1 - -#elif defined(RIOT_VERSION) - -#include - -static int default_RNG(uint8_t *dest, unsigned size) { - random_bytes(dest, size); - return 1; -} -#define default_RNG_defined 1 - -#elif defined(NRF52_SERIES) - -#include "app_error.h" -#include "nrf_crypto_rng.h" - -static int default_RNG(uint8_t *dest, unsigned size) -{ - // make sure to call nrf_crypto_init and nrf_crypto_rng_init first - ret_code_t ret_code = nrf_crypto_rng_vector_generate(dest, size); - return (ret_code == NRF_SUCCESS) ? 1 : 0; -} -#define default_RNG_defined 1 - -#endif /* platform */ - -#endif /* _UECC_PLATFORM_SPECIFIC_H_ */ diff --git a/lib/micro-ecc/types.h b/lib/micro-ecc/types.h deleted file mode 100644 index 9ee81438fac..00000000000 --- a/lib/micro-ecc/types.h +++ /dev/null @@ -1,108 +0,0 @@ -/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_TYPES_H_ -#define _UECC_TYPES_H_ - -#ifndef uECC_PLATFORM - #if __AVR__ - #define uECC_PLATFORM uECC_avr - #elif defined(__thumb2__) || defined(_M_ARMT) /* I think MSVC only supports Thumb-2 targets */ - #define uECC_PLATFORM uECC_arm_thumb2 - #elif defined(__thumb__) - #define uECC_PLATFORM uECC_arm_thumb - #elif defined(__arm__) || defined(_M_ARM) - #define uECC_PLATFORM uECC_arm - #elif defined(__aarch64__) - #define uECC_PLATFORM uECC_arm64 - #elif defined(__i386__) || defined(_M_IX86) || defined(_X86_) || defined(__I86__) - #define uECC_PLATFORM uECC_x86 - #elif defined(__amd64__) || defined(_M_X64) - #define uECC_PLATFORM uECC_x86_64 - #else - #define uECC_PLATFORM uECC_arch_other - #endif -#endif - -#ifndef uECC_ARM_USE_UMAAL - #if (uECC_PLATFORM == uECC_arm) && (__ARM_ARCH >= 6) - #define uECC_ARM_USE_UMAAL 1 - #elif (uECC_PLATFORM == uECC_arm_thumb2) && (__ARM_ARCH >= 6) && !__ARM_ARCH_7M__ - #define uECC_ARM_USE_UMAAL 1 - #else - #define uECC_ARM_USE_UMAAL 0 - #endif -#endif - -#ifndef uECC_WORD_SIZE - #if uECC_PLATFORM == uECC_avr - #define uECC_WORD_SIZE 1 - #elif (uECC_PLATFORM == uECC_x86_64 || uECC_PLATFORM == uECC_arm64) - #define uECC_WORD_SIZE 8 - #else - #define uECC_WORD_SIZE 4 - #endif -#endif - -#if (uECC_WORD_SIZE != 1) && (uECC_WORD_SIZE != 4) && (uECC_WORD_SIZE != 8) - #error "Unsupported value for uECC_WORD_SIZE" -#endif - -#if ((uECC_PLATFORM == uECC_avr) && (uECC_WORD_SIZE != 1)) - #pragma message ("uECC_WORD_SIZE must be 1 for AVR") - #undef uECC_WORD_SIZE - #define uECC_WORD_SIZE 1 -#endif - -#if ((uECC_PLATFORM == uECC_arm || uECC_PLATFORM == uECC_arm_thumb || \ - uECC_PLATFORM == uECC_arm_thumb2) && \ - (uECC_WORD_SIZE != 4)) - #pragma message ("uECC_WORD_SIZE must be 4 for ARM") - #undef uECC_WORD_SIZE - #define uECC_WORD_SIZE 4 -#endif - -#if defined(__SIZEOF_INT128__) || ((__clang_major__ * 100 + __clang_minor__) >= 302) - #define SUPPORTS_INT128 1 -#else - #define SUPPORTS_INT128 0 -#endif - -typedef int8_t wordcount_t; -typedef int16_t bitcount_t; -typedef int8_t cmpresult_t; - -#if (uECC_WORD_SIZE == 1) - -typedef uint8_t uECC_word_t; -typedef uint16_t uECC_dword_t; - -#define HIGH_BIT_SET 0x80 -#define uECC_WORD_BITS 8 -#define uECC_WORD_BITS_SHIFT 3 -#define uECC_WORD_BITS_MASK 0x07 - -#elif (uECC_WORD_SIZE == 4) - -typedef uint32_t uECC_word_t; -typedef uint64_t uECC_dword_t; - -#define HIGH_BIT_SET 0x80000000 -#define uECC_WORD_BITS 32 -#define uECC_WORD_BITS_SHIFT 5 -#define uECC_WORD_BITS_MASK 0x01F - -#elif (uECC_WORD_SIZE == 8) - -typedef uint64_t uECC_word_t; -#if SUPPORTS_INT128 -typedef unsigned __int128 uECC_dword_t; -#endif - -#define HIGH_BIT_SET 0x8000000000000000ull -#define uECC_WORD_BITS 64 -#define uECC_WORD_BITS_SHIFT 6 -#define uECC_WORD_BITS_MASK 0x03F - -#endif /* uECC_WORD_SIZE */ - -#endif /* _UECC_TYPES_H_ */ diff --git a/lib/micro-ecc/uECC.c b/lib/micro-ecc/uECC.c deleted file mode 100644 index a3d502cf21b..00000000000 --- a/lib/micro-ecc/uECC.c +++ /dev/null @@ -1,1669 +0,0 @@ -/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#include "uECC.h" -#include "uECC_vli.h" - -#ifndef uECC_RNG_MAX_TRIES - #define uECC_RNG_MAX_TRIES 64 -#endif - -#if uECC_ENABLE_VLI_API - #define uECC_VLI_API -#else - #define uECC_VLI_API static -#endif - -#if (uECC_PLATFORM == uECC_avr) || \ - (uECC_PLATFORM == uECC_arm) || \ - (uECC_PLATFORM == uECC_arm_thumb) || \ - (uECC_PLATFORM == uECC_arm_thumb2) - #define CONCATX(a, ...) a ## __VA_ARGS__ - #define CONCAT(a, ...) CONCATX(a, __VA_ARGS__) - - #define STRX(a) #a - #define STR(a) STRX(a) - - #define EVAL(...) EVAL1(EVAL1(EVAL1(EVAL1(__VA_ARGS__)))) - #define EVAL1(...) EVAL2(EVAL2(EVAL2(EVAL2(__VA_ARGS__)))) - #define EVAL2(...) EVAL3(EVAL3(EVAL3(EVAL3(__VA_ARGS__)))) - #define EVAL3(...) EVAL4(EVAL4(EVAL4(EVAL4(__VA_ARGS__)))) - #define EVAL4(...) __VA_ARGS__ - - #define DEC_1 0 - #define DEC_2 1 - #define DEC_3 2 - #define DEC_4 3 - #define DEC_5 4 - #define DEC_6 5 - #define DEC_7 6 - #define DEC_8 7 - #define DEC_9 8 - #define DEC_10 9 - #define DEC_11 10 - #define DEC_12 11 - #define DEC_13 12 - #define DEC_14 13 - #define DEC_15 14 - #define DEC_16 15 - #define DEC_17 16 - #define DEC_18 17 - #define DEC_19 18 - #define DEC_20 19 - #define DEC_21 20 - #define DEC_22 21 - #define DEC_23 22 - #define DEC_24 23 - #define DEC_25 24 - #define DEC_26 25 - #define DEC_27 26 - #define DEC_28 27 - #define DEC_29 28 - #define DEC_30 29 - #define DEC_31 30 - #define DEC_32 31 - - #define DEC(N) CONCAT(DEC_, N) - - #define SECOND_ARG(_, val, ...) val - #define SOME_CHECK_0 ~, 0 - #define GET_SECOND_ARG(...) SECOND_ARG(__VA_ARGS__, SOME,) - #define SOME_OR_0(N) GET_SECOND_ARG(CONCAT(SOME_CHECK_, N)) - - #define EMPTY(...) - #define DEFER(...) __VA_ARGS__ EMPTY() - - #define REPEAT_NAME_0() REPEAT_0 - #define REPEAT_NAME_SOME() REPEAT_SOME - #define REPEAT_0(...) - #define REPEAT_SOME(N, stuff) DEFER(CONCAT(REPEAT_NAME_, SOME_OR_0(DEC(N))))()(DEC(N), stuff) stuff - #define REPEAT(N, stuff) EVAL(REPEAT_SOME(N, stuff)) - - #define REPEATM_NAME_0() REPEATM_0 - #define REPEATM_NAME_SOME() REPEATM_SOME - #define REPEATM_0(...) - #define REPEATM_SOME(N, macro) macro(N) \ - DEFER(CONCAT(REPEATM_NAME_, SOME_OR_0(DEC(N))))()(DEC(N), macro) - #define REPEATM(N, macro) EVAL(REPEATM_SOME(N, macro)) -#endif - -#include "platform-specific.inc" - -#if (uECC_WORD_SIZE == 1) - #if uECC_SUPPORTS_secp160r1 - #define uECC_MAX_WORDS 21 /* Due to the size of curve_n. */ - #endif - #if uECC_SUPPORTS_secp192r1 - #undef uECC_MAX_WORDS - #define uECC_MAX_WORDS 24 - #endif - #if uECC_SUPPORTS_secp224r1 - #undef uECC_MAX_WORDS - #define uECC_MAX_WORDS 28 - #endif - #if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) - #undef uECC_MAX_WORDS - #define uECC_MAX_WORDS 32 - #endif -#elif (uECC_WORD_SIZE == 4) - #if uECC_SUPPORTS_secp160r1 - #define uECC_MAX_WORDS 6 /* Due to the size of curve_n. */ - #endif - #if uECC_SUPPORTS_secp192r1 - #undef uECC_MAX_WORDS - #define uECC_MAX_WORDS 6 - #endif - #if uECC_SUPPORTS_secp224r1 - #undef uECC_MAX_WORDS - #define uECC_MAX_WORDS 7 - #endif - #if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) - #undef uECC_MAX_WORDS - #define uECC_MAX_WORDS 8 - #endif -#elif (uECC_WORD_SIZE == 8) - #if uECC_SUPPORTS_secp160r1 - #define uECC_MAX_WORDS 3 - #endif - #if uECC_SUPPORTS_secp192r1 - #undef uECC_MAX_WORDS - #define uECC_MAX_WORDS 3 - #endif - #if uECC_SUPPORTS_secp224r1 - #undef uECC_MAX_WORDS - #define uECC_MAX_WORDS 4 - #endif - #if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) - #undef uECC_MAX_WORDS - #define uECC_MAX_WORDS 4 - #endif -#endif /* uECC_WORD_SIZE */ - -#define BITS_TO_WORDS(num_bits) ((num_bits + ((uECC_WORD_SIZE * 8) - 1)) / (uECC_WORD_SIZE * 8)) -#define BITS_TO_BYTES(num_bits) ((num_bits + 7) / 8) - -struct uECC_Curve_t { - wordcount_t num_words; - wordcount_t num_bytes; - bitcount_t num_n_bits; - uECC_word_t p[uECC_MAX_WORDS]; - uECC_word_t n[uECC_MAX_WORDS]; - uECC_word_t G[uECC_MAX_WORDS * 2]; - uECC_word_t b[uECC_MAX_WORDS]; - void (*double_jacobian)(uECC_word_t * X1, - uECC_word_t * Y1, - uECC_word_t * Z1, - uECC_Curve curve); -#if uECC_SUPPORT_COMPRESSED_POINT - void (*mod_sqrt)(uECC_word_t *a, uECC_Curve curve); -#endif - void (*x_side)(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve); -#if (uECC_OPTIMIZATION_LEVEL > 0) - void (*mmod_fast)(uECC_word_t *result, uECC_word_t *product); -#endif -}; - -#if uECC_VLI_NATIVE_LITTLE_ENDIAN -static void bcopy(uint8_t *dst, - const uint8_t *src, - unsigned num_bytes) { - while (0 != num_bytes) { - num_bytes--; - dst[num_bytes] = src[num_bytes]; - } -} -#endif - -static cmpresult_t uECC_vli_cmp_unsafe(const uECC_word_t *left, - const uECC_word_t *right, - wordcount_t num_words); - -#if (uECC_PLATFORM == uECC_arm || uECC_PLATFORM == uECC_arm_thumb || \ - uECC_PLATFORM == uECC_arm_thumb2) - #include "asm_arm.inc" -#endif - -#if (uECC_PLATFORM == uECC_avr) - #include "asm_avr.inc" -#endif - -#if default_RNG_defined -static uECC_RNG_Function g_rng_function = &default_RNG; -#else -static uECC_RNG_Function g_rng_function = 0; -#endif - -void uECC_set_rng(uECC_RNG_Function rng_function) { - g_rng_function = rng_function; -} - -uECC_RNG_Function uECC_get_rng(void) { - return g_rng_function; -} - -int uECC_curve_private_key_size(uECC_Curve curve) { - return BITS_TO_BYTES(curve->num_n_bits); -} - -int uECC_curve_public_key_size(uECC_Curve curve) { - return 2 * curve->num_bytes; -} - -#if !asm_clear -uECC_VLI_API void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words) { - wordcount_t i; - for (i = 0; i < num_words; ++i) { - vli[i] = 0; - } -} -#endif /* !asm_clear */ - -/* Constant-time comparison to zero - secure way to compare long integers */ -/* Returns 1 if vli == 0, 0 otherwise. */ -uECC_VLI_API uECC_word_t uECC_vli_isZero(const uECC_word_t *vli, wordcount_t num_words) { - uECC_word_t bits = 0; - wordcount_t i; - for (i = 0; i < num_words; ++i) { - bits |= vli[i]; - } - return (bits == 0); -} - -/* Returns nonzero if bit 'bit' of vli is set. */ -uECC_VLI_API uECC_word_t uECC_vli_testBit(const uECC_word_t *vli, bitcount_t bit) { - return (vli[bit >> uECC_WORD_BITS_SHIFT] & ((uECC_word_t)1 << (bit & uECC_WORD_BITS_MASK))); -} - -/* Counts the number of words in vli. */ -static wordcount_t vli_numDigits(const uECC_word_t *vli, const wordcount_t max_words) { - wordcount_t i; - /* Search from the end until we find a non-zero digit. - We do it in reverse because we expect that most digits will be nonzero. */ - for (i = max_words - 1; i >= 0 && vli[i] == 0; --i) { - } - - return (i + 1); -} - -/* Counts the number of bits required to represent vli. */ -uECC_VLI_API bitcount_t uECC_vli_numBits(const uECC_word_t *vli, const wordcount_t max_words) { - uECC_word_t i; - uECC_word_t digit; - - wordcount_t num_digits = vli_numDigits(vli, max_words); - if (num_digits == 0) { - return 0; - } - - digit = vli[num_digits - 1]; - for (i = 0; digit; ++i) { - digit >>= 1; - } - - return (((bitcount_t)(num_digits - 1) << uECC_WORD_BITS_SHIFT) + i); -} - -/* Sets dest = src. */ -#if !asm_set -uECC_VLI_API void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, wordcount_t num_words) { - wordcount_t i; - for (i = 0; i < num_words; ++i) { - dest[i] = src[i]; - } -} -#endif /* !asm_set */ - -/* Returns sign of left - right. */ -static cmpresult_t uECC_vli_cmp_unsafe(const uECC_word_t *left, - const uECC_word_t *right, - wordcount_t num_words) { - wordcount_t i; - for (i = num_words - 1; i >= 0; --i) { - if (left[i] > right[i]) { - return 1; - } else if (left[i] < right[i]) { - return -1; - } - } - return 0; -} - -/* Constant-time comparison function - secure way to compare long integers */ -/* Returns one if left == right, zero otherwise. */ -uECC_VLI_API uECC_word_t uECC_vli_equal(const uECC_word_t *left, - const uECC_word_t *right, - wordcount_t num_words) { - uECC_word_t diff = 0; - wordcount_t i; - for (i = num_words - 1; i >= 0; --i) { - diff |= (left[i] ^ right[i]); - } - return (diff == 0); -} - -uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - wordcount_t num_words); - -/* Returns sign of left - right, in constant time. */ -uECC_VLI_API cmpresult_t uECC_vli_cmp(const uECC_word_t *left, - const uECC_word_t *right, - wordcount_t num_words) { - uECC_word_t tmp[uECC_MAX_WORDS]; - uECC_word_t neg = !!uECC_vli_sub(tmp, left, right, num_words); - uECC_word_t equal = uECC_vli_isZero(tmp, num_words); - return (!equal - 2 * neg); -} - -/* Computes vli = vli >> 1. */ -#if !asm_rshift1 -uECC_VLI_API void uECC_vli_rshift1(uECC_word_t *vli, wordcount_t num_words) { - uECC_word_t *end = vli; - uECC_word_t carry = 0; - - vli += num_words; - while (vli-- > end) { - uECC_word_t temp = *vli; - *vli = (temp >> 1) | carry; - carry = temp << (uECC_WORD_BITS - 1); - } -} -#endif /* !asm_rshift1 */ - -/* Computes result = left + right, returning carry. Can modify in place. */ -#if !asm_add -uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - wordcount_t num_words) { - uECC_word_t carry = 0; - wordcount_t i; - for (i = 0; i < num_words; ++i) { - uECC_word_t sum = left[i] + right[i] + carry; - if (sum != left[i]) { - carry = (sum < left[i]); - } - result[i] = sum; - } - return carry; -} -#endif /* !asm_add */ - -/* Computes result = left - right, returning borrow. Can modify in place. */ -#if !asm_sub -uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - wordcount_t num_words) { - uECC_word_t borrow = 0; - wordcount_t i; - for (i = 0; i < num_words; ++i) { - uECC_word_t diff = left[i] - right[i] - borrow; - if (diff != left[i]) { - borrow = (diff > left[i]); - } - result[i] = diff; - } - return borrow; -} -#endif /* !asm_sub */ - -#if !asm_mult || (uECC_SQUARE_FUNC && !asm_square) || \ - (uECC_SUPPORTS_secp256k1 && (uECC_OPTIMIZATION_LEVEL > 0) && \ - ((uECC_WORD_SIZE == 1) || (uECC_WORD_SIZE == 8))) -static void muladd(uECC_word_t a, - uECC_word_t b, - uECC_word_t *r0, - uECC_word_t *r1, - uECC_word_t *r2) { -#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128 - uint64_t a0 = a & 0xffffffffull; - uint64_t a1 = a >> 32; - uint64_t b0 = b & 0xffffffffull; - uint64_t b1 = b >> 32; - - uint64_t i0 = a0 * b0; - uint64_t i1 = a0 * b1; - uint64_t i2 = a1 * b0; - uint64_t i3 = a1 * b1; - - uint64_t p0, p1; - - i2 += (i0 >> 32); - i2 += i1; - if (i2 < i1) { /* overflow */ - i3 += 0x100000000ull; - } - - p0 = (i0 & 0xffffffffull) | (i2 << 32); - p1 = i3 + (i2 >> 32); - - *r0 += p0; - *r1 += (p1 + (*r0 < p0)); - *r2 += ((*r1 < p1) || (*r1 == p1 && *r0 < p0)); -#else - uECC_dword_t p = (uECC_dword_t)a * b; - uECC_dword_t r01 = ((uECC_dword_t)(*r1) << uECC_WORD_BITS) | *r0; - r01 += p; - *r2 += (r01 < p); - *r1 = r01 >> uECC_WORD_BITS; - *r0 = (uECC_word_t)r01; -#endif -} -#endif /* muladd needed */ - -#if !asm_mult -uECC_VLI_API void uECC_vli_mult(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - wordcount_t num_words) { - uECC_word_t r0 = 0; - uECC_word_t r1 = 0; - uECC_word_t r2 = 0; - wordcount_t i, k; - - /* Compute each digit of result in sequence, maintaining the carries. */ - for (k = 0; k < num_words; ++k) { - for (i = 0; i <= k; ++i) { - muladd(left[i], right[k - i], &r0, &r1, &r2); - } - result[k] = r0; - r0 = r1; - r1 = r2; - r2 = 0; - } - for (k = num_words; k < num_words * 2 - 1; ++k) { - for (i = (k + 1) - num_words; i < num_words; ++i) { - muladd(left[i], right[k - i], &r0, &r1, &r2); - } - result[k] = r0; - r0 = r1; - r1 = r2; - r2 = 0; - } - result[num_words * 2 - 1] = r0; -} -#endif /* !asm_mult */ - -#if uECC_SQUARE_FUNC - -#if !asm_square -static void mul2add(uECC_word_t a, - uECC_word_t b, - uECC_word_t *r0, - uECC_word_t *r1, - uECC_word_t *r2) { -#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128 - uint64_t a0 = a & 0xffffffffull; - uint64_t a1 = a >> 32; - uint64_t b0 = b & 0xffffffffull; - uint64_t b1 = b >> 32; - - uint64_t i0 = a0 * b0; - uint64_t i1 = a0 * b1; - uint64_t i2 = a1 * b0; - uint64_t i3 = a1 * b1; - - uint64_t p0, p1; - - i2 += (i0 >> 32); - i2 += i1; - if (i2 < i1) - { /* overflow */ - i3 += 0x100000000ull; - } - - p0 = (i0 & 0xffffffffull) | (i2 << 32); - p1 = i3 + (i2 >> 32); - - *r2 += (p1 >> 63); - p1 = (p1 << 1) | (p0 >> 63); - p0 <<= 1; - - *r0 += p0; - *r1 += (p1 + (*r0 < p0)); - *r2 += ((*r1 < p1) || (*r1 == p1 && *r0 < p0)); -#else - uECC_dword_t p = (uECC_dword_t)a * b; - uECC_dword_t r01 = ((uECC_dword_t)(*r1) << uECC_WORD_BITS) | *r0; - *r2 += (p >> (uECC_WORD_BITS * 2 - 1)); - p *= 2; - r01 += p; - *r2 += (r01 < p); - *r1 = r01 >> uECC_WORD_BITS; - *r0 = (uECC_word_t)r01; -#endif -} - -uECC_VLI_API void uECC_vli_square(uECC_word_t *result, - const uECC_word_t *left, - wordcount_t num_words) { - uECC_word_t r0 = 0; - uECC_word_t r1 = 0; - uECC_word_t r2 = 0; - - wordcount_t i, k; - - for (k = 0; k < num_words * 2 - 1; ++k) { - uECC_word_t min = (k < num_words ? 0 : (k + 1) - num_words); - for (i = min; i <= k && i <= k - i; ++i) { - if (i < k-i) { - mul2add(left[i], left[k - i], &r0, &r1, &r2); - } else { - muladd(left[i], left[k - i], &r0, &r1, &r2); - } - } - result[k] = r0; - r0 = r1; - r1 = r2; - r2 = 0; - } - - result[num_words * 2 - 1] = r0; -} -#endif /* !asm_square */ - -#else /* uECC_SQUARE_FUNC */ - -#if uECC_ENABLE_VLI_API -uECC_VLI_API void uECC_vli_square(uECC_word_t *result, - const uECC_word_t *left, - wordcount_t num_words) { - uECC_vli_mult(result, left, left, num_words); -} -#endif /* uECC_ENABLE_VLI_API */ - -#endif /* uECC_SQUARE_FUNC */ - -/* Computes result = (left + right) % mod. - Assumes that left < mod and right < mod, and that result does not overlap mod. */ -uECC_VLI_API void uECC_vli_modAdd(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - const uECC_word_t *mod, - wordcount_t num_words) { - uECC_word_t carry = uECC_vli_add(result, left, right, num_words); - if (carry || uECC_vli_cmp_unsafe(mod, result, num_words) != 1) { - /* result > mod (result = mod + remainder), so subtract mod to get remainder. */ - uECC_vli_sub(result, result, mod, num_words); - } -} - -/* Computes result = (left - right) % mod. - Assumes that left < mod and right < mod, and that result does not overlap mod. */ -uECC_VLI_API void uECC_vli_modSub(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - const uECC_word_t *mod, - wordcount_t num_words) { - uECC_word_t l_borrow = uECC_vli_sub(result, left, right, num_words); - if (l_borrow) { - /* In this case, result == -diff == (max int) - diff. Since -x % d == d - x, - we can get the correct result from result + mod (with overflow). */ - uECC_vli_add(result, result, mod, num_words); - } -} - -/* Computes result = product % mod, where product is 2N words long. */ -/* Currently only designed to work for curve_p or curve_n. */ -uECC_VLI_API void uECC_vli_mmod(uECC_word_t *result, - uECC_word_t *product, - const uECC_word_t *mod, - wordcount_t num_words) { - uECC_word_t mod_multiple[2 * uECC_MAX_WORDS]; - uECC_word_t tmp[2 * uECC_MAX_WORDS]; - uECC_word_t *v[2] = {tmp, product}; - uECC_word_t index; - - /* Shift mod so its highest set bit is at the maximum position. */ - bitcount_t shift = (num_words * 2 * uECC_WORD_BITS) - uECC_vli_numBits(mod, num_words); - wordcount_t word_shift = shift / uECC_WORD_BITS; - wordcount_t bit_shift = shift % uECC_WORD_BITS; - uECC_word_t carry = 0; - uECC_vli_clear(mod_multiple, word_shift); - if (bit_shift > 0) { - for(index = 0; index < (uECC_word_t)num_words; ++index) { - mod_multiple[word_shift + index] = (mod[index] << bit_shift) | carry; - carry = mod[index] >> (uECC_WORD_BITS - bit_shift); - } - } else { - uECC_vli_set(mod_multiple + word_shift, mod, num_words); - } - - for (index = 1; shift >= 0; --shift) { - uECC_word_t borrow = 0; - wordcount_t i; - for (i = 0; i < num_words * 2; ++i) { - uECC_word_t diff = v[index][i] - mod_multiple[i] - borrow; - if (diff != v[index][i]) { - borrow = (diff > v[index][i]); - } - v[1 - index][i] = diff; - } - index = !(index ^ borrow); /* Swap the index if there was no borrow */ - uECC_vli_rshift1(mod_multiple, num_words); - mod_multiple[num_words - 1] |= mod_multiple[num_words] << (uECC_WORD_BITS - 1); - uECC_vli_rshift1(mod_multiple + num_words, num_words); - } - uECC_vli_set(result, v[index], num_words); -} - -/* Computes result = (left * right) % mod. */ -uECC_VLI_API void uECC_vli_modMult(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - const uECC_word_t *mod, - wordcount_t num_words) { - uECC_word_t product[2 * uECC_MAX_WORDS]; - uECC_vli_mult(product, left, right, num_words); - uECC_vli_mmod(result, product, mod, num_words); -} - -uECC_VLI_API void uECC_vli_modMult_fast(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - uECC_Curve curve) { - uECC_word_t product[2 * uECC_MAX_WORDS]; - uECC_vli_mult(product, left, right, curve->num_words); -#if (uECC_OPTIMIZATION_LEVEL > 0) - curve->mmod_fast(result, product); -#else - uECC_vli_mmod(result, product, curve->p, curve->num_words); -#endif -} - -#if uECC_SQUARE_FUNC - -#if uECC_ENABLE_VLI_API -/* Computes result = left^2 % mod. */ -uECC_VLI_API void uECC_vli_modSquare(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *mod, - wordcount_t num_words) { - uECC_word_t product[2 * uECC_MAX_WORDS]; - uECC_vli_square(product, left, num_words); - uECC_vli_mmod(result, product, mod, num_words); -} -#endif /* uECC_ENABLE_VLI_API */ - -uECC_VLI_API void uECC_vli_modSquare_fast(uECC_word_t *result, - const uECC_word_t *left, - uECC_Curve curve) { - uECC_word_t product[2 * uECC_MAX_WORDS]; - uECC_vli_square(product, left, curve->num_words); -#if (uECC_OPTIMIZATION_LEVEL > 0) - curve->mmod_fast(result, product); -#else - uECC_vli_mmod(result, product, curve->p, curve->num_words); -#endif -} - -#else /* uECC_SQUARE_FUNC */ - -#if uECC_ENABLE_VLI_API -uECC_VLI_API void uECC_vli_modSquare(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *mod, - wordcount_t num_words) { - uECC_vli_modMult(result, left, left, mod, num_words); -} -#endif /* uECC_ENABLE_VLI_API */ - -uECC_VLI_API void uECC_vli_modSquare_fast(uECC_word_t *result, - const uECC_word_t *left, - uECC_Curve curve) { - uECC_vli_modMult_fast(result, left, left, curve); -} - -#endif /* uECC_SQUARE_FUNC */ - -#define EVEN(vli) (!(vli[0] & 1)) -static void vli_modInv_update(uECC_word_t *uv, - const uECC_word_t *mod, - wordcount_t num_words) { - uECC_word_t carry = 0; - if (!EVEN(uv)) { - carry = uECC_vli_add(uv, uv, mod, num_words); - } - uECC_vli_rshift1(uv, num_words); - if (carry) { - uv[num_words - 1] |= HIGH_BIT_SET; - } -} - -/* Computes result = (1 / input) % mod. All VLIs are the same size. - See "From Euclid's GCD to Montgomery Multiplication to the Great Divide" */ -uECC_VLI_API void uECC_vli_modInv(uECC_word_t *result, - const uECC_word_t *input, - const uECC_word_t *mod, - wordcount_t num_words) { - uECC_word_t a[uECC_MAX_WORDS], b[uECC_MAX_WORDS], u[uECC_MAX_WORDS], v[uECC_MAX_WORDS]; - cmpresult_t cmpResult; - - if (uECC_vli_isZero(input, num_words)) { - uECC_vli_clear(result, num_words); - return; - } - - uECC_vli_set(a, input, num_words); - uECC_vli_set(b, mod, num_words); - uECC_vli_clear(u, num_words); - u[0] = 1; - uECC_vli_clear(v, num_words); - while ((cmpResult = uECC_vli_cmp_unsafe(a, b, num_words)) != 0) { - if (EVEN(a)) { - uECC_vli_rshift1(a, num_words); - vli_modInv_update(u, mod, num_words); - } else if (EVEN(b)) { - uECC_vli_rshift1(b, num_words); - vli_modInv_update(v, mod, num_words); - } else if (cmpResult > 0) { - uECC_vli_sub(a, a, b, num_words); - uECC_vli_rshift1(a, num_words); - if (uECC_vli_cmp_unsafe(u, v, num_words) < 0) { - uECC_vli_add(u, u, mod, num_words); - } - uECC_vli_sub(u, u, v, num_words); - vli_modInv_update(u, mod, num_words); - } else { - uECC_vli_sub(b, b, a, num_words); - uECC_vli_rshift1(b, num_words); - if (uECC_vli_cmp_unsafe(v, u, num_words) < 0) { - uECC_vli_add(v, v, mod, num_words); - } - uECC_vli_sub(v, v, u, num_words); - vli_modInv_update(v, mod, num_words); - } - } - uECC_vli_set(result, u, num_words); -} - -/* ------ Point operations ------ */ - -#include "curve-specific.inc" - -/* Returns 1 if 'point' is the point at infinity, 0 otherwise. */ -#define EccPoint_isZero(point, curve) uECC_vli_isZero((point), (curve)->num_words * 2) - -/* Point multiplication algorithm using Montgomery's ladder with co-Z coordinates. -From http://eprint.iacr.org/2011/338.pdf -*/ - -/* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */ -static void apply_z(uECC_word_t * X1, - uECC_word_t * Y1, - const uECC_word_t * const Z, - uECC_Curve curve) { - uECC_word_t t1[uECC_MAX_WORDS]; - - uECC_vli_modSquare_fast(t1, Z, curve); /* z^2 */ - uECC_vli_modMult_fast(X1, X1, t1, curve); /* x1 * z^2 */ - uECC_vli_modMult_fast(t1, t1, Z, curve); /* z^3 */ - uECC_vli_modMult_fast(Y1, Y1, t1, curve); /* y1 * z^3 */ -} - -/* P = (x1, y1) => 2P, (x2, y2) => P' */ -static void XYcZ_initial_double(uECC_word_t * X1, - uECC_word_t * Y1, - uECC_word_t * X2, - uECC_word_t * Y2, - const uECC_word_t * const initial_Z, - uECC_Curve curve) { - uECC_word_t z[uECC_MAX_WORDS]; - wordcount_t num_words = curve->num_words; - if (initial_Z) { - uECC_vli_set(z, initial_Z, num_words); - } else { - uECC_vli_clear(z, num_words); - z[0] = 1; - } - - uECC_vli_set(X2, X1, num_words); - uECC_vli_set(Y2, Y1, num_words); - - apply_z(X1, Y1, z, curve); - curve->double_jacobian(X1, Y1, z, curve); - apply_z(X2, Y2, z, curve); -} - -/* Input P = (x1, y1, Z), Q = (x2, y2, Z) - Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3) - or P => P', Q => P + Q -*/ -static void XYcZ_add(uECC_word_t * X1, - uECC_word_t * Y1, - uECC_word_t * X2, - uECC_word_t * Y2, - uECC_Curve curve) { - /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ - uECC_word_t t5[uECC_MAX_WORDS]; - wordcount_t num_words = curve->num_words; - - uECC_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ - uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ - uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ - uECC_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ - uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ - uECC_vli_modSquare_fast(t5, Y2, curve); /* t5 = (y2 - y1)^2 = D */ - - uECC_vli_modSub(t5, t5, X1, curve->p, num_words); /* t5 = D - B */ - uECC_vli_modSub(t5, t5, X2, curve->p, num_words); /* t5 = D - B - C = x3 */ - uECC_vli_modSub(X2, X2, X1, curve->p, num_words); /* t3 = C - B */ - uECC_vli_modMult_fast(Y1, Y1, X2, curve); /* t2 = y1*(C - B) */ - uECC_vli_modSub(X2, X1, t5, curve->p, num_words); /* t3 = B - x3 */ - uECC_vli_modMult_fast(Y2, Y2, X2, curve); /* t4 = (y2 - y1)*(B - x3) */ - uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y3 */ - - uECC_vli_set(X2, t5, num_words); -} - -/* Input P = (x1, y1, Z), Q = (x2, y2, Z) - Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3) - or P => P - Q, Q => P + Q -*/ -static void XYcZ_addC(uECC_word_t * X1, - uECC_word_t * Y1, - uECC_word_t * X2, - uECC_word_t * Y2, - uECC_Curve curve) { - /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ - uECC_word_t t5[uECC_MAX_WORDS]; - uECC_word_t t6[uECC_MAX_WORDS]; - uECC_word_t t7[uECC_MAX_WORDS]; - wordcount_t num_words = curve->num_words; - - uECC_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ - uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ - uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ - uECC_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ - uECC_vli_modAdd(t5, Y2, Y1, curve->p, num_words); /* t5 = y2 + y1 */ - uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ - - uECC_vli_modSub(t6, X2, X1, curve->p, num_words); /* t6 = C - B */ - uECC_vli_modMult_fast(Y1, Y1, t6, curve); /* t2 = y1 * (C - B) = E */ - uECC_vli_modAdd(t6, X1, X2, curve->p, num_words); /* t6 = B + C */ - uECC_vli_modSquare_fast(X2, Y2, curve); /* t3 = (y2 - y1)^2 = D */ - uECC_vli_modSub(X2, X2, t6, curve->p, num_words); /* t3 = D - (B + C) = x3 */ - - uECC_vli_modSub(t7, X1, X2, curve->p, num_words); /* t7 = B - x3 */ - uECC_vli_modMult_fast(Y2, Y2, t7, curve); /* t4 = (y2 - y1)*(B - x3) */ - uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = (y2 - y1)*(B - x3) - E = y3 */ - - uECC_vli_modSquare_fast(t7, t5, curve); /* t7 = (y2 + y1)^2 = F */ - uECC_vli_modSub(t7, t7, t6, curve->p, num_words); /* t7 = F - (B + C) = x3' */ - uECC_vli_modSub(t6, t7, X1, curve->p, num_words); /* t6 = x3' - B */ - uECC_vli_modMult_fast(t6, t6, t5, curve); /* t6 = (y2+y1)*(x3' - B) */ - uECC_vli_modSub(Y1, t6, Y1, curve->p, num_words); /* t2 = (y2+y1)*(x3' - B) - E = y3' */ - - uECC_vli_set(X1, t7, num_words); -} - -/* result may overlap point. */ -static void EccPoint_mult(uECC_word_t * result, - const uECC_word_t * point, - const uECC_word_t * scalar, - const uECC_word_t * initial_Z, - bitcount_t num_bits, - uECC_Curve curve) { - /* R0 and R1 */ - uECC_word_t Rx[2][uECC_MAX_WORDS]; - uECC_word_t Ry[2][uECC_MAX_WORDS]; - uECC_word_t z[uECC_MAX_WORDS]; - bitcount_t i; - uECC_word_t nb; - wordcount_t num_words = curve->num_words; - - uECC_vli_set(Rx[1], point, num_words); - uECC_vli_set(Ry[1], point + num_words, num_words); - - XYcZ_initial_double(Rx[1], Ry[1], Rx[0], Ry[0], initial_Z, curve); - - for (i = num_bits - 2; i > 0; --i) { - nb = !uECC_vli_testBit(scalar, i); - XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); - XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); - } - - nb = !uECC_vli_testBit(scalar, 0); - XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); - - /* Find final 1/Z value. */ - uECC_vli_modSub(z, Rx[1], Rx[0], curve->p, num_words); /* X1 - X0 */ - uECC_vli_modMult_fast(z, z, Ry[1 - nb], curve); /* Yb * (X1 - X0) */ - uECC_vli_modMult_fast(z, z, point, curve); /* xP * Yb * (X1 - X0) */ - uECC_vli_modInv(z, z, curve->p, num_words); /* 1 / (xP * Yb * (X1 - X0)) */ - /* yP / (xP * Yb * (X1 - X0)) */ - uECC_vli_modMult_fast(z, z, point + num_words, curve); - uECC_vli_modMult_fast(z, z, Rx[1 - nb], curve); /* Xb * yP / (xP * Yb * (X1 - X0)) */ - /* End 1/Z calculation */ - - XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); - apply_z(Rx[0], Ry[0], z, curve); - - uECC_vli_set(result, Rx[0], num_words); - uECC_vli_set(result + num_words, Ry[0], num_words); -} - -static uECC_word_t regularize_k(const uECC_word_t * const k, - uECC_word_t *k0, - uECC_word_t *k1, - uECC_Curve curve) { - wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); - bitcount_t num_n_bits = curve->num_n_bits; - uECC_word_t carry = uECC_vli_add(k0, k, curve->n, num_n_words) || - (num_n_bits < ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8) && - uECC_vli_testBit(k0, num_n_bits)); - uECC_vli_add(k1, k0, curve->n, num_n_words); - return carry; -} - -/* Generates a random integer in the range 0 < random < top. - Both random and top have num_words words. */ -uECC_VLI_API int uECC_generate_random_int(uECC_word_t *random, - const uECC_word_t *top, - wordcount_t num_words) { - uECC_word_t mask = (uECC_word_t)-1; - uECC_word_t tries; - bitcount_t num_bits = uECC_vli_numBits(top, num_words); - - if (!g_rng_function) { - return 0; - } - - for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { - if (!g_rng_function((uint8_t *)random, num_words * uECC_WORD_SIZE)) { - return 0; - } - random[num_words - 1] &= mask >> ((bitcount_t)(num_words * uECC_WORD_SIZE * 8 - num_bits)); - if (!uECC_vli_isZero(random, num_words) && - uECC_vli_cmp(top, random, num_words) == 1) { - return 1; - } - } - return 0; -} - -static uECC_word_t EccPoint_compute_public_key(uECC_word_t *result, - uECC_word_t *private_key, - uECC_Curve curve) { - uECC_word_t tmp1[uECC_MAX_WORDS]; - uECC_word_t tmp2[uECC_MAX_WORDS]; - uECC_word_t *p2[2] = {tmp1, tmp2}; - uECC_word_t *initial_Z = 0; - uECC_word_t carry; - - /* Regularize the bitcount for the private key so that attackers cannot use a side channel - attack to learn the number of leading zeros. */ - carry = regularize_k(private_key, tmp1, tmp2, curve); - - /* If an RNG function was specified, try to get a random initial Z value to improve - protection against side-channel attacks. */ - if (g_rng_function) { - if (!uECC_generate_random_int(p2[carry], curve->p, curve->num_words)) { - return 0; - } - initial_Z = p2[carry]; - } - EccPoint_mult(result, curve->G, p2[!carry], initial_Z, curve->num_n_bits + 1, curve); - - if (EccPoint_isZero(result, curve)) { - return 0; - } - return 1; -} - -#if uECC_WORD_SIZE == 1 - -uECC_VLI_API void uECC_vli_nativeToBytes(uint8_t *bytes, - int num_bytes, - const uint8_t *native) { - wordcount_t i; - for (i = 0; i < num_bytes; ++i) { - bytes[i] = native[(num_bytes - 1) - i]; - } -} - -uECC_VLI_API void uECC_vli_bytesToNative(uint8_t *native, - const uint8_t *bytes, - int num_bytes) { - uECC_vli_nativeToBytes(native, num_bytes, bytes); -} - -#else - -uECC_VLI_API void uECC_vli_nativeToBytes(uint8_t *bytes, - int num_bytes, - const uECC_word_t *native) { - int i; - for (i = 0; i < num_bytes; ++i) { - unsigned b = num_bytes - 1 - i; - bytes[i] = native[b / uECC_WORD_SIZE] >> (8 * (b % uECC_WORD_SIZE)); - } -} - -uECC_VLI_API void uECC_vli_bytesToNative(uECC_word_t *native, - const uint8_t *bytes, - int num_bytes) { - int i; - uECC_vli_clear(native, (num_bytes + (uECC_WORD_SIZE - 1)) / uECC_WORD_SIZE); - for (i = 0; i < num_bytes; ++i) { - unsigned b = num_bytes - 1 - i; - native[b / uECC_WORD_SIZE] |= - (uECC_word_t)bytes[i] << (8 * (b % uECC_WORD_SIZE)); - } -} - -#endif /* uECC_WORD_SIZE */ - -int uECC_make_key(uint8_t *public_key, - uint8_t *private_key, - uECC_Curve curve) { -#if uECC_VLI_NATIVE_LITTLE_ENDIAN - uECC_word_t *_private = (uECC_word_t *)private_key; - uECC_word_t *_public = (uECC_word_t *)public_key; -#else - uECC_word_t _private[uECC_MAX_WORDS]; - uECC_word_t _public[uECC_MAX_WORDS * 2]; -#endif - uECC_word_t tries; - - for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { - if (!uECC_generate_random_int(_private, curve->n, BITS_TO_WORDS(curve->num_n_bits))) { - return 0; - } - - if (EccPoint_compute_public_key(_public, _private, curve)) { -#if uECC_VLI_NATIVE_LITTLE_ENDIAN == 0 - uECC_vli_nativeToBytes(private_key, BITS_TO_BYTES(curve->num_n_bits), _private); - uECC_vli_nativeToBytes(public_key, curve->num_bytes, _public); - uECC_vli_nativeToBytes( - public_key + curve->num_bytes, curve->num_bytes, _public + curve->num_words); -#endif - return 1; - } - } - return 0; -} - -int uECC_shared_secret(const uint8_t *public_key, - const uint8_t *private_key, - uint8_t *secret, - uECC_Curve curve) { - uECC_word_t _public[uECC_MAX_WORDS * 2]; - uECC_word_t _private[uECC_MAX_WORDS]; - - uECC_word_t tmp[uECC_MAX_WORDS]; - uECC_word_t *p2[2] = {_private, tmp}; - uECC_word_t *initial_Z = 0; - uECC_word_t carry; - wordcount_t num_words = curve->num_words; - wordcount_t num_bytes = curve->num_bytes; - -#if uECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy((uint8_t *) _private, private_key, num_bytes); - bcopy((uint8_t *) _public, public_key, num_bytes*2); -#else - uECC_vli_bytesToNative(_private, private_key, BITS_TO_BYTES(curve->num_n_bits)); - uECC_vli_bytesToNative(_public, public_key, num_bytes); - uECC_vli_bytesToNative(_public + num_words, public_key + num_bytes, num_bytes); -#endif - - /* Regularize the bitcount for the private key so that attackers cannot use a side channel - attack to learn the number of leading zeros. */ - carry = regularize_k(_private, _private, tmp, curve); - - /* If an RNG function was specified, try to get a random initial Z value to improve - protection against side-channel attacks. */ - if (g_rng_function) { - if (!uECC_generate_random_int(p2[carry], curve->p, num_words)) { - return 0; - } - initial_Z = p2[carry]; - } - - EccPoint_mult(_public, _public, p2[!carry], initial_Z, curve->num_n_bits + 1, curve); -#if uECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy((uint8_t *) secret, (uint8_t *) _public, num_bytes); -#else - uECC_vli_nativeToBytes(secret, num_bytes, _public); -#endif - return !EccPoint_isZero(_public, curve); -} - -#if uECC_SUPPORT_COMPRESSED_POINT -void uECC_compress(const uint8_t *public_key, uint8_t *compressed, uECC_Curve curve) { - wordcount_t i; - for (i = 0; i < curve->num_bytes; ++i) { - compressed[i+1] = public_key[i]; - } -#if uECC_VLI_NATIVE_LITTLE_ENDIAN - compressed[0] = 2 + (public_key[curve->num_bytes] & 0x01); -#else - compressed[0] = 2 + (public_key[curve->num_bytes * 2 - 1] & 0x01); -#endif -} - -void uECC_decompress(const uint8_t *compressed, uint8_t *public_key, uECC_Curve curve) { -#if uECC_VLI_NATIVE_LITTLE_ENDIAN - uECC_word_t *point = (uECC_word_t *)public_key; -#else - uECC_word_t point[uECC_MAX_WORDS * 2]; -#endif - uECC_word_t *y = point + curve->num_words; -#if uECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy(public_key, compressed+1, curve->num_bytes); -#else - uECC_vli_bytesToNative(point, compressed + 1, curve->num_bytes); -#endif - curve->x_side(y, point, curve); - curve->mod_sqrt(y, curve); - - if ((y[0] & 0x01) != (compressed[0] & 0x01)) { - uECC_vli_sub(y, curve->p, y, curve->num_words); - } - -#if uECC_VLI_NATIVE_LITTLE_ENDIAN == 0 - uECC_vli_nativeToBytes(public_key, curve->num_bytes, point); - uECC_vli_nativeToBytes(public_key + curve->num_bytes, curve->num_bytes, y); -#endif -} -#endif /* uECC_SUPPORT_COMPRESSED_POINT */ - -uECC_VLI_API int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve) { - uECC_word_t tmp1[uECC_MAX_WORDS]; - uECC_word_t tmp2[uECC_MAX_WORDS]; - wordcount_t num_words = curve->num_words; - - /* The point at infinity is invalid. */ - if (EccPoint_isZero(point, curve)) { - return 0; - } - - /* x and y must be smaller than p. */ - if (uECC_vli_cmp_unsafe(curve->p, point, num_words) != 1 || - uECC_vli_cmp_unsafe(curve->p, point + num_words, num_words) != 1) { - return 0; - } - - uECC_vli_modSquare_fast(tmp1, point + num_words, curve); - curve->x_side(tmp2, point, curve); /* tmp2 = x^3 + ax + b */ - - /* Make sure that y^2 == x^3 + ax + b */ - return (int)(uECC_vli_equal(tmp1, tmp2, num_words)); -} - -int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve) { -#if uECC_VLI_NATIVE_LITTLE_ENDIAN - uECC_word_t *_public = (uECC_word_t *)public_key; -#else - uECC_word_t _public[uECC_MAX_WORDS * 2]; -#endif - -#if uECC_VLI_NATIVE_LITTLE_ENDIAN == 0 - uECC_vli_bytesToNative(_public, public_key, curve->num_bytes); - uECC_vli_bytesToNative( - _public + curve->num_words, public_key + curve->num_bytes, curve->num_bytes); -#endif - return uECC_valid_point(_public, curve); -} - -int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve) { -#if uECC_VLI_NATIVE_LITTLE_ENDIAN - uECC_word_t *_private = (uECC_word_t *)private_key; - uECC_word_t *_public = (uECC_word_t *)public_key; -#else - uECC_word_t _private[uECC_MAX_WORDS]; - uECC_word_t _public[uECC_MAX_WORDS * 2]; -#endif - -#if uECC_VLI_NATIVE_LITTLE_ENDIAN == 0 - uECC_vli_bytesToNative(_private, private_key, BITS_TO_BYTES(curve->num_n_bits)); -#endif - - /* Make sure the private key is in the range [1, n-1]. */ - if (uECC_vli_isZero(_private, BITS_TO_WORDS(curve->num_n_bits))) { - return 0; - } - - if (uECC_vli_cmp(curve->n, _private, BITS_TO_WORDS(curve->num_n_bits)) != 1) { - return 0; - } - - /* Compute public key. */ - if (!EccPoint_compute_public_key(_public, _private, curve)) { - return 0; - } - -#if uECC_VLI_NATIVE_LITTLE_ENDIAN == 0 - uECC_vli_nativeToBytes(public_key, curve->num_bytes, _public); - uECC_vli_nativeToBytes( - public_key + curve->num_bytes, curve->num_bytes, _public + curve->num_words); -#endif - return 1; -} - - -/* -------- ECDSA code -------- */ - -static void bits2int(uECC_word_t *native, - const uint8_t *bits, - unsigned bits_size, - uECC_Curve curve) { - unsigned num_n_bytes = BITS_TO_BYTES(curve->num_n_bits); - unsigned num_n_words = BITS_TO_WORDS(curve->num_n_bits); - int shift; - uECC_word_t carry; - uECC_word_t *ptr; - - if (bits_size > num_n_bytes) { - bits_size = num_n_bytes; - } - - uECC_vli_clear(native, num_n_words); -#if uECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy((uint8_t *) native, bits, bits_size); -#else - uECC_vli_bytesToNative(native, bits, bits_size); -#endif - if (bits_size * 8 <= (unsigned)curve->num_n_bits) { - return; - } - shift = bits_size * 8 - curve->num_n_bits; - carry = 0; - ptr = native + num_n_words; - while (ptr-- > native) { - uECC_word_t temp = *ptr; - *ptr = (temp >> shift) | carry; - carry = temp << (uECC_WORD_BITS - shift); - } - - /* Reduce mod curve_n */ - if (uECC_vli_cmp_unsafe(curve->n, native, num_n_words) != 1) { - uECC_vli_sub(native, native, curve->n, num_n_words); - } -} - -static int uECC_sign_with_k_internal(const uint8_t *private_key, - const uint8_t *message_hash, - unsigned hash_size, - uECC_word_t *k, - uint8_t *signature, - uECC_Curve curve) { - - uECC_word_t tmp[uECC_MAX_WORDS]; - uECC_word_t s[uECC_MAX_WORDS]; - uECC_word_t *k2[2] = {tmp, s}; - uECC_word_t *initial_Z = 0; -#if uECC_VLI_NATIVE_LITTLE_ENDIAN - uECC_word_t *p = (uECC_word_t *)signature; -#else - uECC_word_t p[uECC_MAX_WORDS * 2]; -#endif - uECC_word_t carry; - wordcount_t num_words = curve->num_words; - wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); - bitcount_t num_n_bits = curve->num_n_bits; - - /* Make sure 0 < k < curve_n */ - if (uECC_vli_isZero(k, num_words) || uECC_vli_cmp(curve->n, k, num_n_words) != 1) { - return 0; - } - - carry = regularize_k(k, tmp, s, curve); - /* If an RNG function was specified, try to get a random initial Z value to improve - protection against side-channel attacks. */ - if (g_rng_function) { - if (!uECC_generate_random_int(k2[carry], curve->p, num_words)) { - return 0; - } - initial_Z = k2[carry]; - } - EccPoint_mult(p, curve->G, k2[!carry], initial_Z, num_n_bits + 1, curve); - if (uECC_vli_isZero(p, num_words)) { - return 0; - } - - /* If an RNG function was specified, get a random number - to prevent side channel analysis of k. */ - if (!g_rng_function) { - uECC_vli_clear(tmp, num_n_words); - tmp[0] = 1; - } else if (!uECC_generate_random_int(tmp, curve->n, num_n_words)) { - return 0; - } - - /* Prevent side channel analysis of uECC_vli_modInv() to determine - bits of k / the private key by premultiplying by a random number */ - uECC_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k' = rand * k */ - uECC_vli_modInv(k, k, curve->n, num_n_words); /* k = 1 / k' */ - uECC_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k = 1 / k */ - -#if uECC_VLI_NATIVE_LITTLE_ENDIAN == 0 - uECC_vli_nativeToBytes(signature, curve->num_bytes, p); /* store r */ -#endif - -#if uECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy((uint8_t *) tmp, private_key, BITS_TO_BYTES(curve->num_n_bits)); -#else - uECC_vli_bytesToNative(tmp, private_key, BITS_TO_BYTES(curve->num_n_bits)); /* tmp = d */ -#endif - - s[num_n_words - 1] = 0; - uECC_vli_set(s, p, num_words); - uECC_vli_modMult(s, tmp, s, curve->n, num_n_words); /* s = r*d */ - - bits2int(tmp, message_hash, hash_size, curve); - uECC_vli_modAdd(s, tmp, s, curve->n, num_n_words); /* s = e + r*d */ - uECC_vli_modMult(s, s, k, curve->n, num_n_words); /* s = (e + r*d) / k */ - if (uECC_vli_numBits(s, num_n_words) > (bitcount_t)curve->num_bytes * 8) { - return 0; - } -#if uECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy((uint8_t *) signature + curve->num_bytes, (uint8_t *) s, curve->num_bytes); -#else - uECC_vli_nativeToBytes(signature + curve->num_bytes, curve->num_bytes, s); -#endif - return 1; -} - -/* For testing - sign with an explicitly specified k value */ -int uECC_sign_with_k(const uint8_t *private_key, - const uint8_t *message_hash, - unsigned hash_size, - const uint8_t *k, - uint8_t *signature, - uECC_Curve curve) { - uECC_word_t k2[uECC_MAX_WORDS]; - bits2int(k2, k, BITS_TO_BYTES(curve->num_n_bits), curve); - return uECC_sign_with_k_internal(private_key, message_hash, hash_size, k2, signature, curve); -} - -int uECC_sign(const uint8_t *private_key, - const uint8_t *message_hash, - unsigned hash_size, - uint8_t *signature, - uECC_Curve curve) { - uECC_word_t k[uECC_MAX_WORDS]; - uECC_word_t tries; - - for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { - if (!uECC_generate_random_int(k, curve->n, BITS_TO_WORDS(curve->num_n_bits))) { - return 0; - } - - if (uECC_sign_with_k_internal(private_key, message_hash, hash_size, k, signature, curve)) { - return 1; - } - } - return 0; -} - -/* Compute an HMAC using K as a key (as in RFC 6979). Note that K is always - the same size as the hash result size. */ -static void HMAC_init(const uECC_HashContext *hash_context, const uint8_t *K) { - uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; - unsigned i; - for (i = 0; i < hash_context->result_size; ++i) - pad[i] = K[i] ^ 0x36; - for (; i < hash_context->block_size; ++i) - pad[i] = 0x36; - - hash_context->init_hash(hash_context); - hash_context->update_hash(hash_context, pad, hash_context->block_size); -} - -static void HMAC_update(const uECC_HashContext *hash_context, - const uint8_t *message, - unsigned message_size) { - hash_context->update_hash(hash_context, message, message_size); -} - -static void HMAC_finish(const uECC_HashContext *hash_context, - const uint8_t *K, - uint8_t *result) { - uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; - unsigned i; - for (i = 0; i < hash_context->result_size; ++i) - pad[i] = K[i] ^ 0x5c; - for (; i < hash_context->block_size; ++i) - pad[i] = 0x5c; - - hash_context->finish_hash(hash_context, result); - - hash_context->init_hash(hash_context); - hash_context->update_hash(hash_context, pad, hash_context->block_size); - hash_context->update_hash(hash_context, result, hash_context->result_size); - hash_context->finish_hash(hash_context, result); -} - -/* V = HMAC_K(V) */ -static void update_V(const uECC_HashContext *hash_context, uint8_t *K, uint8_t *V) { - HMAC_init(hash_context, K); - HMAC_update(hash_context, V, hash_context->result_size); - HMAC_finish(hash_context, K, V); -} - -/* Deterministic signing, similar to RFC 6979. Differences are: - * We just use H(m) directly rather than bits2octets(H(m)) - (it is not reduced modulo curve_n). - * We generate a value for k (aka T) directly rather than converting endianness. - - Layout of hash_context->tmp: | | (1 byte overlapped 0x00 or 0x01) / */ -int uECC_sign_deterministic(const uint8_t *private_key, - const uint8_t *message_hash, - unsigned hash_size, - const uECC_HashContext *hash_context, - uint8_t *signature, - uECC_Curve curve) { - uint8_t *K = hash_context->tmp; - uint8_t *V = K + hash_context->result_size; - wordcount_t num_bytes = curve->num_bytes; - wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); - bitcount_t num_n_bits = curve->num_n_bits; - uECC_word_t tries; - unsigned i; - for (i = 0; i < hash_context->result_size; ++i) { - V[i] = 0x01; - K[i] = 0; - } - - /* K = HMAC_K(V || 0x00 || int2octets(x) || h(m)) */ - HMAC_init(hash_context, K); - V[hash_context->result_size] = 0x00; - HMAC_update(hash_context, V, hash_context->result_size + 1); - HMAC_update(hash_context, private_key, num_bytes); - HMAC_update(hash_context, message_hash, hash_size); - HMAC_finish(hash_context, K, K); - - update_V(hash_context, K, V); - - /* K = HMAC_K(V || 0x01 || int2octets(x) || h(m)) */ - HMAC_init(hash_context, K); - V[hash_context->result_size] = 0x01; - HMAC_update(hash_context, V, hash_context->result_size + 1); - HMAC_update(hash_context, private_key, num_bytes); - HMAC_update(hash_context, message_hash, hash_size); - HMAC_finish(hash_context, K, K); - - update_V(hash_context, K, V); - - for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { - uECC_word_t T[uECC_MAX_WORDS]; - uint8_t *T_ptr = (uint8_t *)T; - wordcount_t T_bytes = 0; - for (;;) { - update_V(hash_context, K, V); - for (i = 0; i < hash_context->result_size; ++i) { - T_ptr[T_bytes++] = V[i]; - if (T_bytes >= num_n_words * uECC_WORD_SIZE) { - goto filled; - } - } - } - filled: - if ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8 > num_n_bits) { - uECC_word_t mask = (uECC_word_t)-1; - T[num_n_words - 1] &= - mask >> ((bitcount_t)(num_n_words * uECC_WORD_SIZE * 8 - num_n_bits)); - } - - if (uECC_sign_with_k_internal(private_key, message_hash, hash_size, T, signature, curve)) { - return 1; - } - - /* K = HMAC_K(V || 0x00) */ - HMAC_init(hash_context, K); - V[hash_context->result_size] = 0x00; - HMAC_update(hash_context, V, hash_context->result_size + 1); - HMAC_finish(hash_context, K, K); - - update_V(hash_context, K, V); - } - return 0; -} - -static bitcount_t smax(bitcount_t a, bitcount_t b) { - return (a > b ? a : b); -} - -int uECC_verify(const uint8_t *public_key, - const uint8_t *message_hash, - unsigned hash_size, - const uint8_t *signature, - uECC_Curve curve) { - uECC_word_t u1[uECC_MAX_WORDS], u2[uECC_MAX_WORDS]; - uECC_word_t z[uECC_MAX_WORDS]; - uECC_word_t sum[uECC_MAX_WORDS * 2]; - uECC_word_t rx[uECC_MAX_WORDS]; - uECC_word_t ry[uECC_MAX_WORDS]; - uECC_word_t tx[uECC_MAX_WORDS]; - uECC_word_t ty[uECC_MAX_WORDS]; - uECC_word_t tz[uECC_MAX_WORDS]; - const uECC_word_t *points[4]; - const uECC_word_t *point; - bitcount_t num_bits; - bitcount_t i; -#if uECC_VLI_NATIVE_LITTLE_ENDIAN - uECC_word_t *_public = (uECC_word_t *)public_key; -#else - uECC_word_t _public[uECC_MAX_WORDS * 2]; -#endif - uECC_word_t r[uECC_MAX_WORDS], s[uECC_MAX_WORDS]; - wordcount_t num_words = curve->num_words; - wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); - - rx[num_n_words - 1] = 0; - r[num_n_words - 1] = 0; - s[num_n_words - 1] = 0; - -#if uECC_VLI_NATIVE_LITTLE_ENDIAN - bcopy((uint8_t *) r, signature, curve->num_bytes); - bcopy((uint8_t *) s, signature + curve->num_bytes, curve->num_bytes); -#else - uECC_vli_bytesToNative(_public, public_key, curve->num_bytes); - uECC_vli_bytesToNative( - _public + num_words, public_key + curve->num_bytes, curve->num_bytes); - uECC_vli_bytesToNative(r, signature, curve->num_bytes); - uECC_vli_bytesToNative(s, signature + curve->num_bytes, curve->num_bytes); -#endif - - /* r, s must not be 0. */ - if (uECC_vli_isZero(r, num_words) || uECC_vli_isZero(s, num_words)) { - return 0; - } - - /* r, s must be < n. */ - if (uECC_vli_cmp_unsafe(curve->n, r, num_n_words) != 1 || - uECC_vli_cmp_unsafe(curve->n, s, num_n_words) != 1) { - return 0; - } - - /* Calculate u1 and u2. */ - uECC_vli_modInv(z, s, curve->n, num_n_words); /* z = 1/s */ - u1[num_n_words - 1] = 0; - bits2int(u1, message_hash, hash_size, curve); - uECC_vli_modMult(u1, u1, z, curve->n, num_n_words); /* u1 = e/s */ - uECC_vli_modMult(u2, r, z, curve->n, num_n_words); /* u2 = r/s */ - - /* Calculate sum = G + Q. */ - uECC_vli_set(sum, _public, num_words); - uECC_vli_set(sum + num_words, _public + num_words, num_words); - uECC_vli_set(tx, curve->G, num_words); - uECC_vli_set(ty, curve->G + num_words, num_words); - uECC_vli_modSub(z, sum, tx, curve->p, num_words); /* z = x2 - x1 */ - XYcZ_add(tx, ty, sum, sum + num_words, curve); - uECC_vli_modInv(z, z, curve->p, num_words); /* z = 1/z */ - apply_z(sum, sum + num_words, z, curve); - - /* Use Shamir's trick to calculate u1*G + u2*Q */ - points[0] = 0; - points[1] = curve->G; - points[2] = _public; - points[3] = sum; - num_bits = smax(uECC_vli_numBits(u1, num_n_words), - uECC_vli_numBits(u2, num_n_words)); - - point = points[(!!uECC_vli_testBit(u1, num_bits - 1)) | - ((!!uECC_vli_testBit(u2, num_bits - 1)) << 1)]; - uECC_vli_set(rx, point, num_words); - uECC_vli_set(ry, point + num_words, num_words); - uECC_vli_clear(z, num_words); - z[0] = 1; - - for (i = num_bits - 2; i >= 0; --i) { - uECC_word_t index; - curve->double_jacobian(rx, ry, z, curve); - - index = (!!uECC_vli_testBit(u1, i)) | ((!!uECC_vli_testBit(u2, i)) << 1); - point = points[index]; - if (point) { - uECC_vli_set(tx, point, num_words); - uECC_vli_set(ty, point + num_words, num_words); - apply_z(tx, ty, z, curve); - uECC_vli_modSub(tz, rx, tx, curve->p, num_words); /* Z = x2 - x1 */ - XYcZ_add(tx, ty, rx, ry, curve); - uECC_vli_modMult_fast(z, z, tz, curve); - } - } - - uECC_vli_modInv(z, z, curve->p, num_words); /* Z = 1/Z */ - apply_z(rx, ry, z, curve); - - /* v = x1 (mod n) */ - if (uECC_vli_cmp_unsafe(curve->n, rx, num_n_words) != 1) { - uECC_vli_sub(rx, rx, curve->n, num_n_words); - } - - /* Accept only if v == r. */ - return (int)(uECC_vli_equal(rx, r, num_words)); -} - -#if uECC_ENABLE_VLI_API - -unsigned uECC_curve_num_words(uECC_Curve curve) { - return curve->num_words; -} - -unsigned uECC_curve_num_bytes(uECC_Curve curve) { - return curve->num_bytes; -} - -unsigned uECC_curve_num_bits(uECC_Curve curve) { - return curve->num_bytes * 8; -} - -unsigned uECC_curve_num_n_words(uECC_Curve curve) { - return BITS_TO_WORDS(curve->num_n_bits); -} - -unsigned uECC_curve_num_n_bytes(uECC_Curve curve) { - return BITS_TO_BYTES(curve->num_n_bits); -} - -unsigned uECC_curve_num_n_bits(uECC_Curve curve) { - return curve->num_n_bits; -} - -const uECC_word_t *uECC_curve_p(uECC_Curve curve) { - return curve->p; -} - -const uECC_word_t *uECC_curve_n(uECC_Curve curve) { - return curve->n; -} - -const uECC_word_t *uECC_curve_G(uECC_Curve curve) { - return curve->G; -} - -const uECC_word_t *uECC_curve_b(uECC_Curve curve) { - return curve->b; -} - -#if uECC_SUPPORT_COMPRESSED_POINT -void uECC_vli_mod_sqrt(uECC_word_t *a, uECC_Curve curve) { - curve->mod_sqrt(a, curve); -} -#endif - -void uECC_vli_mmod_fast(uECC_word_t *result, uECC_word_t *product, uECC_Curve curve) { -#if (uECC_OPTIMIZATION_LEVEL > 0) - curve->mmod_fast(result, product); -#else - uECC_vli_mmod(result, product, curve->p, curve->num_words); -#endif -} - -void uECC_point_mult(uECC_word_t *result, - const uECC_word_t *point, - const uECC_word_t *scalar, - uECC_Curve curve) { - uECC_word_t tmp1[uECC_MAX_WORDS]; - uECC_word_t tmp2[uECC_MAX_WORDS]; - uECC_word_t *p2[2] = {tmp1, tmp2}; - uECC_word_t carry = regularize_k(scalar, tmp1, tmp2, curve); - - EccPoint_mult(result, point, p2[!carry], 0, curve->num_n_bits + 1, curve); -} - -#endif /* uECC_ENABLE_VLI_API */ diff --git a/lib/micro-ecc/uECC.h b/lib/micro-ecc/uECC.h deleted file mode 100644 index dcbdbfa8b42..00000000000 --- a/lib/micro-ecc/uECC.h +++ /dev/null @@ -1,367 +0,0 @@ -/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_H_ -#define _UECC_H_ - -#include - -/* Platform selection options. -If uECC_PLATFORM is not defined, the code will try to guess it based on compiler macros. -Possible values for uECC_PLATFORM are defined below: */ -#define uECC_arch_other 0 -#define uECC_x86 1 -#define uECC_x86_64 2 -#define uECC_arm 3 -#define uECC_arm_thumb 4 -#define uECC_arm_thumb2 5 -#define uECC_arm64 6 -#define uECC_avr 7 - -/* If desired, you can define uECC_WORD_SIZE as appropriate for your platform (1, 4, or 8 bytes). -If uECC_WORD_SIZE is not explicitly defined then it will be automatically set based on your -platform. */ - -/* Optimization level; trade speed for code size. - Larger values produce code that is faster but larger. - Currently supported values are 0 - 4; 0 is unusably slow for most applications. - Optimization level 4 currently only has an effect ARM platforms where more than one - curve is enabled. */ -#ifndef uECC_OPTIMIZATION_LEVEL - #define uECC_OPTIMIZATION_LEVEL 2 -#endif - -/* uECC_SQUARE_FUNC - If enabled (defined as nonzero), this will cause a specific function to be -used for (scalar) squaring instead of the generic multiplication function. This can make things -faster somewhat faster, but increases the code size. */ -#ifndef uECC_SQUARE_FUNC - #define uECC_SQUARE_FUNC 0 -#endif - -/* uECC_VLI_NATIVE_LITTLE_ENDIAN - If enabled (defined as nonzero), this will switch to native -little-endian format for *all* arrays passed in and out of the public API. This includes public -and private keys, shared secrets, signatures and message hashes. -Using this switch reduces the amount of call stack memory used by uECC, since less intermediate -translations are required. -Note that this will *only* work on native little-endian processors and it will treat the uint8_t -arrays passed into the public API as word arrays, therefore requiring the provided byte arrays -to be word aligned on architectures that do not support unaligned accesses. -IMPORTANT: Keys and signatures generated with uECC_VLI_NATIVE_LITTLE_ENDIAN=1 are incompatible -with keys and signatures generated with uECC_VLI_NATIVE_LITTLE_ENDIAN=0; all parties must use -the same endianness. */ -#ifndef uECC_VLI_NATIVE_LITTLE_ENDIAN - #define uECC_VLI_NATIVE_LITTLE_ENDIAN 0 -#endif - -/* Curve support selection. Set to 0 to remove that curve. */ -#ifndef uECC_SUPPORTS_secp160r1 - #define uECC_SUPPORTS_secp160r1 1 -#endif -#ifndef uECC_SUPPORTS_secp192r1 - #define uECC_SUPPORTS_secp192r1 1 -#endif -#ifndef uECC_SUPPORTS_secp224r1 - #define uECC_SUPPORTS_secp224r1 1 -#endif -#ifndef uECC_SUPPORTS_secp256r1 - #define uECC_SUPPORTS_secp256r1 1 -#endif -#ifndef uECC_SUPPORTS_secp256k1 - #define uECC_SUPPORTS_secp256k1 1 -#endif - -/* Specifies whether compressed point format is supported. - Set to 0 to disable point compression/decompression functions. */ -#ifndef uECC_SUPPORT_COMPRESSED_POINT - #define uECC_SUPPORT_COMPRESSED_POINT 1 -#endif - -struct uECC_Curve_t; -typedef const struct uECC_Curve_t * uECC_Curve; - -#ifdef __cplusplus -extern "C" -{ -#endif - -#if uECC_SUPPORTS_secp160r1 -uECC_Curve uECC_secp160r1(void); -#endif -#if uECC_SUPPORTS_secp192r1 -uECC_Curve uECC_secp192r1(void); -#endif -#if uECC_SUPPORTS_secp224r1 -uECC_Curve uECC_secp224r1(void); -#endif -#if uECC_SUPPORTS_secp256r1 -uECC_Curve uECC_secp256r1(void); -#endif -#if uECC_SUPPORTS_secp256k1 -uECC_Curve uECC_secp256k1(void); -#endif - -/* uECC_RNG_Function type -The RNG function should fill 'size' random bytes into 'dest'. It should return 1 if -'dest' was filled with random data, or 0 if the random data could not be generated. -The filled-in values should be either truly random, or from a cryptographically-secure PRNG. - -A correctly functioning RNG function must be set (using uECC_set_rng()) before calling -uECC_make_key() or uECC_sign(). - -Setting a correctly functioning RNG function improves the resistance to side-channel attacks -for uECC_shared_secret() and uECC_sign_deterministic(). - -A correct RNG function is set by default when building for Windows, Linux, or OS X. -If you are building on another POSIX-compliant system that supports /dev/random or /dev/urandom, -you can define uECC_POSIX to use the predefined RNG. For embedded platforms there is no predefined -RNG function; you must provide your own. -*/ -typedef int (*uECC_RNG_Function)(uint8_t *dest, unsigned size); - -/* uECC_set_rng() function. -Set the function that will be used to generate random bytes. The RNG function should -return 1 if the random data was generated, or 0 if the random data could not be generated. - -On platforms where there is no predefined RNG function (eg embedded platforms), this must -be called before uECC_make_key() or uECC_sign() are used. - -Inputs: - rng_function - The function that will be used to generate random bytes. -*/ -void uECC_set_rng(uECC_RNG_Function rng_function); - -/* uECC_get_rng() function. - -Returns the function that will be used to generate random bytes. -*/ -uECC_RNG_Function uECC_get_rng(void); - -/* uECC_curve_private_key_size() function. - -Returns the size of a private key for the curve in bytes. -*/ -int uECC_curve_private_key_size(uECC_Curve curve); - -/* uECC_curve_public_key_size() function. - -Returns the size of a public key for the curve in bytes. -*/ -int uECC_curve_public_key_size(uECC_Curve curve); - -/* uECC_make_key() function. -Create a public/private key pair. - -Outputs: - public_key - Will be filled in with the public key. Must be at least 2 * the curve size - (in bytes) long. For example, if the curve is secp256r1, public_key must be 64 - bytes long. - private_key - Will be filled in with the private key. Must be as long as the curve order; this - is typically the same as the curve size, except for secp160r1. For example, if the - curve is secp256r1, private_key must be 32 bytes long. - - For secp160r1, private_key must be 21 bytes long! Note that the first byte will - almost always be 0 (there is about a 1 in 2^80 chance of it being non-zero). - -Returns 1 if the key pair was generated successfully, 0 if an error occurred. -*/ -int uECC_make_key(uint8_t *public_key, uint8_t *private_key, uECC_Curve curve); - -/* uECC_shared_secret() function. -Compute a shared secret given your secret key and someone else's public key. If the public key -is not from a trusted source and has not been previously verified, you should verify it first -using uECC_valid_public_key(). -Note: It is recommended that you hash the result of uECC_shared_secret() before using it for -symmetric encryption or HMAC. - -Inputs: - public_key - The public key of the remote party. - private_key - Your private key. - -Outputs: - secret - Will be filled in with the shared secret value. Must be the same size as the - curve size; for example, if the curve is secp256r1, secret must be 32 bytes long. - -Returns 1 if the shared secret was generated successfully, 0 if an error occurred. -*/ -int uECC_shared_secret(const uint8_t *public_key, - const uint8_t *private_key, - uint8_t *secret, - uECC_Curve curve); - -#if uECC_SUPPORT_COMPRESSED_POINT -/* uECC_compress() function. -Compress a public key. - -Inputs: - public_key - The public key to compress. - -Outputs: - compressed - Will be filled in with the compressed public key. Must be at least - (curve size + 1) bytes long; for example, if the curve is secp256r1, - compressed must be 33 bytes long. -*/ -void uECC_compress(const uint8_t *public_key, uint8_t *compressed, uECC_Curve curve); - -/* uECC_decompress() function. -Decompress a compressed public key. - -Inputs: - compressed - The compressed public key. - -Outputs: - public_key - Will be filled in with the decompressed public key. -*/ -void uECC_decompress(const uint8_t *compressed, uint8_t *public_key, uECC_Curve curve); -#endif /* uECC_SUPPORT_COMPRESSED_POINT */ - -/* uECC_valid_public_key() function. -Check to see if a public key is valid. - -Note that you are not required to check for a valid public key before using any other uECC -functions. However, you may wish to avoid spending CPU time computing a shared secret or -verifying a signature using an invalid public key. - -Inputs: - public_key - The public key to check. - -Returns 1 if the public key is valid, 0 if it is invalid. -*/ -int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve); - -/* uECC_compute_public_key() function. -Compute the corresponding public key for a private key. - -Inputs: - private_key - The private key to compute the public key for - -Outputs: - public_key - Will be filled in with the corresponding public key - -Returns 1 if the key was computed successfully, 0 if an error occurred. -*/ -int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve); - -/* uECC_sign() function. -Generate an ECDSA signature for a given hash value. - -Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and pass it in to -this function along with your private key. - -Inputs: - private_key - Your private key. - message_hash - The hash of the message to sign. - hash_size - The size of message_hash in bytes. - -Outputs: - signature - Will be filled in with the signature value. Must be at least 2 * curve size long. - For example, if the curve is secp256r1, signature must be 64 bytes long. - -Returns 1 if the signature generated successfully, 0 if an error occurred. -*/ -int uECC_sign(const uint8_t *private_key, - const uint8_t *message_hash, - unsigned hash_size, - uint8_t *signature, - uECC_Curve curve); - -/* uECC_HashContext structure. -This is used to pass in an arbitrary hash function to uECC_sign_deterministic(). -The structure will be used for multiple hash computations; each time a new hash -is computed, init_hash() will be called, followed by one or more calls to -update_hash(), and finally a call to finish_hash() to produce the resulting hash. - -The intention is that you will create a structure that includes uECC_HashContext -followed by any hash-specific data. For example: - -typedef struct SHA256_HashContext { - uECC_HashContext uECC; - SHA256_CTX ctx; -} SHA256_HashContext; - -void init_SHA256(uECC_HashContext *base) { - SHA256_HashContext *context = (SHA256_HashContext *)base; - SHA256_Init(&context->ctx); -} - -void update_SHA256(uECC_HashContext *base, - const uint8_t *message, - unsigned message_size) { - SHA256_HashContext *context = (SHA256_HashContext *)base; - SHA256_Update(&context->ctx, message, message_size); -} - -void finish_SHA256(uECC_HashContext *base, uint8_t *hash_result) { - SHA256_HashContext *context = (SHA256_HashContext *)base; - SHA256_Final(hash_result, &context->ctx); -} - -... when signing ... -{ - uint8_t tmp[32 + 32 + 64]; - SHA256_HashContext ctx = {{&init_SHA256, &update_SHA256, &finish_SHA256, 64, 32, tmp}}; - uECC_sign_deterministic(key, message_hash, &ctx.uECC, signature); -} -*/ -typedef struct uECC_HashContext { - void (*init_hash)(const struct uECC_HashContext *context); - void (*update_hash)(const struct uECC_HashContext *context, - const uint8_t *message, - unsigned message_size); - void (*finish_hash)(const struct uECC_HashContext *context, uint8_t *hash_result); - unsigned block_size; /* Hash function block size in bytes, eg 64 for SHA-256. */ - unsigned result_size; /* Hash function result size in bytes, eg 32 for SHA-256. */ - uint8_t *tmp; /* Must point to a buffer of at least (2 * result_size + block_size) bytes. */ -} uECC_HashContext; - -/* uECC_sign_deterministic() function. -Generate an ECDSA signature for a given hash value, using a deterministic algorithm -(see RFC 6979). You do not need to set the RNG using uECC_set_rng() before calling -this function; however, if the RNG is defined it will improve resistance to side-channel -attacks. - -Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and pass it to -this function along with your private key and a hash context. Note that the message_hash -does not need to be computed with the same hash function used by hash_context. - -Inputs: - private_key - Your private key. - message_hash - The hash of the message to sign. - hash_size - The size of message_hash in bytes. - hash_context - A hash context to use. - -Outputs: - signature - Will be filled in with the signature value. - -Returns 1 if the signature generated successfully, 0 if an error occurred. -*/ -int uECC_sign_deterministic(const uint8_t *private_key, - const uint8_t *message_hash, - unsigned hash_size, - const uECC_HashContext *hash_context, - uint8_t *signature, - uECC_Curve curve); - -/* uECC_verify() function. -Verify an ECDSA signature. - -Usage: Compute the hash of the signed data using the same hash as the signer and -pass it to this function along with the signer's public key and the signature values (r and s). - -Inputs: - public_key - The signer's public key. - message_hash - The hash of the signed data. - hash_size - The size of message_hash in bytes. - signature - The signature value. - -Returns 1 if the signature is valid, 0 if it is invalid. -*/ -int uECC_verify(const uint8_t *public_key, - const uint8_t *message_hash, - unsigned hash_size, - const uint8_t *signature, - uECC_Curve curve); - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* _UECC_H_ */ diff --git a/lib/micro-ecc/uECC_vli.h b/lib/micro-ecc/uECC_vli.h deleted file mode 100644 index 864cc333569..00000000000 --- a/lib/micro-ecc/uECC_vli.h +++ /dev/null @@ -1,172 +0,0 @@ -/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ - -#ifndef _UECC_VLI_H_ -#define _UECC_VLI_H_ - -#include "uECC.h" -#include "types.h" - -/* Functions for raw large-integer manipulation. These are only available - if uECC.c is compiled with uECC_ENABLE_VLI_API defined to 1. */ -#ifndef uECC_ENABLE_VLI_API - #define uECC_ENABLE_VLI_API 0 -#endif - -#ifdef __cplusplus -extern "C" -{ -#endif - -#if uECC_ENABLE_VLI_API - -void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words); - -/* Constant-time comparison to zero - secure way to compare long integers */ -/* Returns 1 if vli == 0, 0 otherwise. */ -uECC_word_t uECC_vli_isZero(const uECC_word_t *vli, wordcount_t num_words); - -/* Returns nonzero if bit 'bit' of vli is set. */ -uECC_word_t uECC_vli_testBit(const uECC_word_t *vli, bitcount_t bit); - -/* Counts the number of bits required to represent vli. */ -bitcount_t uECC_vli_numBits(const uECC_word_t *vli, const wordcount_t max_words); - -/* Sets dest = src. */ -void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, wordcount_t num_words); - -/* Constant-time comparison function - secure way to compare long integers */ -/* Returns one if left == right, zero otherwise */ -uECC_word_t uECC_vli_equal(const uECC_word_t *left, - const uECC_word_t *right, - wordcount_t num_words); - -/* Constant-time comparison function - secure way to compare long integers */ -/* Returns sign of left - right, in constant time. */ -cmpresult_t uECC_vli_cmp(const uECC_word_t *left, const uECC_word_t *right, wordcount_t num_words); - -/* Computes vli = vli >> 1. */ -void uECC_vli_rshift1(uECC_word_t *vli, wordcount_t num_words); - -/* Computes result = left + right, returning carry. Can modify in place. */ -uECC_word_t uECC_vli_add(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - wordcount_t num_words); - -/* Computes result = left - right, returning borrow. Can modify in place. */ -uECC_word_t uECC_vli_sub(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - wordcount_t num_words); - -/* Computes result = left * right. Result must be 2 * num_words long. */ -void uECC_vli_mult(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - wordcount_t num_words); - -/* Computes result = left^2. Result must be 2 * num_words long. */ -void uECC_vli_square(uECC_word_t *result, const uECC_word_t *left, wordcount_t num_words); - -/* Computes result = (left + right) % mod. - Assumes that left < mod and right < mod, and that result does not overlap mod. */ -void uECC_vli_modAdd(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - const uECC_word_t *mod, - wordcount_t num_words); - -/* Computes result = (left - right) % mod. - Assumes that left < mod and right < mod, and that result does not overlap mod. */ -void uECC_vli_modSub(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - const uECC_word_t *mod, - wordcount_t num_words); - -/* Computes result = product % mod, where product is 2N words long. - Currently only designed to work for mod == curve->p or curve_n. */ -void uECC_vli_mmod(uECC_word_t *result, - uECC_word_t *product, - const uECC_word_t *mod, - wordcount_t num_words); - -/* Calculates result = product (mod curve->p), where product is up to - 2 * curve->num_words long. */ -void uECC_vli_mmod_fast(uECC_word_t *result, uECC_word_t *product, uECC_Curve curve); - -/* Computes result = (left * right) % mod. - Currently only designed to work for mod == curve->p or curve_n. */ -void uECC_vli_modMult(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - const uECC_word_t *mod, - wordcount_t num_words); - -/* Computes result = (left * right) % curve->p. */ -void uECC_vli_modMult_fast(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *right, - uECC_Curve curve); - -/* Computes result = left^2 % mod. - Currently only designed to work for mod == curve->p or curve_n. */ -void uECC_vli_modSquare(uECC_word_t *result, - const uECC_word_t *left, - const uECC_word_t *mod, - wordcount_t num_words); - -/* Computes result = left^2 % curve->p. */ -void uECC_vli_modSquare_fast(uECC_word_t *result, const uECC_word_t *left, uECC_Curve curve); - -/* Computes result = (1 / input) % mod.*/ -void uECC_vli_modInv(uECC_word_t *result, - const uECC_word_t *input, - const uECC_word_t *mod, - wordcount_t num_words); - -#if uECC_SUPPORT_COMPRESSED_POINT -/* Calculates a = sqrt(a) (mod curve->p) */ -void uECC_vli_mod_sqrt(uECC_word_t *a, uECC_Curve curve); -#endif - -/* Converts an integer in uECC native format to big-endian bytes. */ -void uECC_vli_nativeToBytes(uint8_t *bytes, int num_bytes, const uECC_word_t *native); -/* Converts big-endian bytes to an integer in uECC native format. */ -void uECC_vli_bytesToNative(uECC_word_t *native, const uint8_t *bytes, int num_bytes); - -unsigned uECC_curve_num_words(uECC_Curve curve); -unsigned uECC_curve_num_bytes(uECC_Curve curve); -unsigned uECC_curve_num_bits(uECC_Curve curve); -unsigned uECC_curve_num_n_words(uECC_Curve curve); -unsigned uECC_curve_num_n_bytes(uECC_Curve curve); -unsigned uECC_curve_num_n_bits(uECC_Curve curve); - -const uECC_word_t *uECC_curve_p(uECC_Curve curve); -const uECC_word_t *uECC_curve_n(uECC_Curve curve); -const uECC_word_t *uECC_curve_G(uECC_Curve curve); -const uECC_word_t *uECC_curve_b(uECC_Curve curve); - -int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve); - -/* Multiplies a point by a scalar. Points are represented by the X coordinate followed by - the Y coordinate in the same array, both coordinates are curve->num_words long. Note - that scalar must be curve->num_n_words long (NOT curve->num_words). */ -void uECC_point_mult(uECC_word_t *result, - const uECC_word_t *point, - const uECC_word_t *scalar, - uECC_Curve curve); - -/* Generates a random integer in the range 0 < random < top. - Both random and top have num_words words. */ -int uECC_generate_random_int(uECC_word_t *random, - const uECC_word_t *top, - wordcount_t num_words); - -#endif /* uECC_ENABLE_VLI_API */ - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* _UECC_VLI_H_ */ diff --git a/lib/microtar.scons b/lib/microtar.scons index 6ee36d403cd..54949fb42b6 100644 --- a/lib/microtar.scons +++ b/lib/microtar.scons @@ -14,7 +14,7 @@ libenv.Append( CPPDEFINES=["MICROTAR_DISABLE_API_CHECKS"], ) -sources = libenv.GlobRecursive("*.c", "microtar/src") +sources = [File("microtar/src/microtar.c")] lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) libenv.Install("${LIB_DIST_DIR}", lib) diff --git a/lib/misc.scons b/lib/misc.scons deleted file mode 100644 index 5a826b18d29..00000000000 --- a/lib/misc.scons +++ /dev/null @@ -1,44 +0,0 @@ -Import("env") - -env.Append( - CPPPATH=[ - "#/lib/digital_signal", - "#/lib/fnv1a_hash", - "#/lib/heatshrink", - "#/lib/micro-ecc", - "#/lib/nanopb", - "#/lib/u8g2", - ], - CPPDEFINES=[ - "PB_ENABLE_MALLOC", - ], -) - - -libenv = env.Clone(FW_LIB_NAME="misc") -libenv.ApplyLibFlags() - -sources = [] - -libs_recurse = [ - "digital_signal", - "micro-ecc", - "one_wire", - "u8g2", - "update_util", -] - -for lib in libs_recurse: - sources += libenv.GlobRecursive("*.c*", lib) - -libs_plain = [ - "heatshrink", - "nanopb", -] - -for lib in libs_plain: - sources += Glob(lib + "/*.c*", source=True) - -lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) -libenv.Install("${LIB_DIST_DIR}", lib) -Return("lib") diff --git a/lib/mlib.scons b/lib/mlib.scons new file mode 100644 index 00000000000..2bdd3728955 --- /dev/null +++ b/lib/mlib.scons @@ -0,0 +1,27 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/mlib", + ], + SDK_HEADERS=[ + *( + File(f"#/lib/mlib/m-{name}.h") + for name in ( + "algo", + "array", + "bptree", + "core", + "deque", + "dict", + "list", + "rbtree", + "tuple", + "variant", + ) + ), + ], + CPPDEFINES=[ + '"M_MEMORY_FULL(x)=abort()"', + ], +) diff --git a/lib/music_worker/SConscript b/lib/music_worker/SConscript new file mode 100644 index 00000000000..0439286cebf --- /dev/null +++ b/lib/music_worker/SConscript @@ -0,0 +1,30 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/music_worker", + ], + SDK_HEADERS=[ + File("music_worker.h"), + ], + LINT_SOURCES=[ + Dir("."), + ], +) + +libenv = env.Clone(FW_LIB_NAME="music_worker") +libenv.ApplyLibFlags() + +libenv.AppendUnique( + CCFLAGS=[ + # Required for lib to be linkable with .faps + "-mword-relocations", + "-mlong-calls", + ], +) + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/music_worker/music_worker.c b/lib/music_worker/music_worker.c new file mode 100644 index 00000000000..279d1267376 --- /dev/null +++ b/lib/music_worker/music_worker.c @@ -0,0 +1,507 @@ +#include "music_worker.h" + +#include +#include + +#include +#include + +#include +#include + +#define TAG "MusicWorker" + +#define MUSIC_PLAYER_FILETYPE "Flipper Music Format" +#define MUSIC_PLAYER_VERSION 0 + +#define SEMITONE_PAUSE 0xFF + +#define NOTE_C4 261.63f +#define NOTE_C4_SEMITONE (4.0f * 12.0f) +#define TWO_POW_TWELTH_ROOT 1.059463094359f + +typedef struct { + uint8_t semitone; + uint8_t duration; + uint8_t dots; +} NoteBlock; + +ARRAY_DEF(NoteBlockArray, NoteBlock, M_POD_OPLIST); + +struct MusicWorker { + FuriThread* thread; + bool should_work; + + MusicWorkerCallback callback; + void* callback_context; + + float volume; + uint32_t bpm; + uint32_t duration; + uint32_t octave; + NoteBlockArray_t notes; +}; + +static int32_t music_worker_thread_callback(void* context) { + furi_assert(context); + MusicWorker* instance = context; + + NoteBlockArray_it_t it; + NoteBlockArray_it(it, instance->notes); + if(furi_hal_speaker_acquire(1000)) { + while(instance->should_work) { + if(NoteBlockArray_end_p(it)) { + NoteBlockArray_it(it, instance->notes); + furi_delay_ms(10); + } else { + NoteBlock* note_block = NoteBlockArray_ref(it); + + float note_from_a4 = (float)note_block->semitone - NOTE_C4_SEMITONE; + float frequency = NOTE_C4 * powf(TWO_POW_TWELTH_ROOT, note_from_a4); + float duration = 60.0 * furi_kernel_get_tick_frequency() * 4 / instance->bpm / + note_block->duration; + uint32_t dots = note_block->dots; + while(dots > 0) { + duration += duration / 2; + dots--; + } + uint32_t next_tick = furi_get_tick() + duration; + float volume = instance->volume; + + if(instance->callback) { + instance->callback( + note_block->semitone, + note_block->dots, + note_block->duration, + 0.0, + instance->callback_context); + } + + furi_hal_speaker_stop(); + furi_hal_speaker_start(frequency, volume); + while(instance->should_work && furi_get_tick() < next_tick) { + volume *= 0.9945679; + furi_hal_speaker_set_volume(volume); + furi_delay_ms(2); + } + NoteBlockArray_next(it); + } + } + + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } else { + FURI_LOG_E(TAG, "Speaker system is busy with another process."); + } + + return 0; +} + +MusicWorker* music_worker_alloc() { + MusicWorker* instance = malloc(sizeof(MusicWorker)); + + NoteBlockArray_init(instance->notes); + + instance->thread = + furi_thread_alloc_ex("MusicWorker", 1024, music_worker_thread_callback, instance); + + instance->volume = 1.0f; + + return instance; +} + +void music_worker_clear(MusicWorker* instance) { + NoteBlockArray_reset(instance->notes); +} + +void music_worker_free(MusicWorker* instance) { + furi_assert(instance); + furi_thread_free(instance->thread); + NoteBlockArray_clear(instance->notes); + free(instance); +} + +static bool is_digit(const char c) { + return isdigit(c) != 0; +} + +static bool is_letter(const char c) { + return islower(c) != 0 || isupper(c) != 0; +} + +static bool is_space(const char c) { + return c == ' ' || c == '\t'; +} + +static size_t extract_number(const char* string, uint32_t* number) { + size_t ret = 0; + *number = 0; + while(is_digit(*string)) { + *number *= 10; + *number += (*string - '0'); + string++; + ret++; + } + return ret; +} + +static size_t extract_dots(const char* string, uint32_t* number) { + size_t ret = 0; + *number = 0; + while(*string == '.') { + *number += 1; + string++; + ret++; + } + return ret; +} + +static size_t extract_char(const char* string, char* symbol) { + if(is_letter(*string)) { + *symbol = *string; + return 1; + } else { + return 0; + } +} + +static size_t extract_sharp(const char* string, char* symbol) { + if(*string == '#' || *string == '_') { + *symbol = '#'; + return 1; + } else { + return 0; + } +} + +static size_t skip_till(const char* string, const char symbol) { + size_t ret = 0; + while(*string != '\0' && *string != symbol) { + string++; + ret++; + } + if(*string != symbol) { + ret = 0; + } + return ret; +} + +static bool + music_worker_add_note(MusicWorker* instance, uint8_t semitone, uint8_t duration, uint8_t dots) { + NoteBlock note_block; + + note_block.semitone = semitone; + note_block.duration = duration; + note_block.dots = dots; + + NoteBlockArray_push_back(instance->notes, note_block); + + return true; +} + +static int8_t note_to_semitone(const char note) { + switch(note) { + case 'C': + return 0; + // C# + case 'D': + return 2; + // D# + case 'E': + return 4; + case 'F': + return 5; + // F# + case 'G': + return 7; + // G# + case 'A': + return 9; + // A# + case 'B': + return 11; + default: + return 0; + } +} + +static bool music_worker_parse_notes(MusicWorker* instance, const char* string) { + const char* cursor = string; + bool result = true; + + while(*cursor != '\0') { + if(!is_space(*cursor)) { + uint32_t duration = 0; + char note_char = '\0'; + char sharp_char = '\0'; + uint32_t octave = 0; + uint32_t dots = 0; + + // Parsing + cursor += extract_number(cursor, &duration); + cursor += extract_char(cursor, ¬e_char); + cursor += extract_sharp(cursor, &sharp_char); + cursor += extract_number(cursor, &octave); + cursor += extract_dots(cursor, &dots); + + // Post processing + note_char = toupper(note_char); + if(!duration) { + duration = instance->duration; + } + if(!octave) { + octave = instance->octave; + } + + // Validation + bool is_valid = true; + is_valid &= (duration >= 1 && duration <= 128); + is_valid &= ((note_char >= 'A' && note_char <= 'G') || note_char == 'P'); + is_valid &= (sharp_char == '#' || sharp_char == '\0'); + is_valid &= (octave <= 16); + is_valid &= (dots <= 16); + if(!is_valid) { + FURI_LOG_E( + TAG, + "Invalid note: %lu%c%c%lu.%lu", + duration, + note_char == '\0' ? '_' : note_char, + sharp_char == '\0' ? '_' : sharp_char, + octave, + dots); + result = false; + break; + } + + // Note to semitones + uint8_t semitone = 0; + if(note_char == 'P') { + semitone = SEMITONE_PAUSE; + } else { + semitone += octave * 12; + semitone += note_to_semitone(note_char); + semitone += sharp_char == '#' ? 1 : 0; + } + + if(music_worker_add_note(instance, semitone, duration, dots)) { + FURI_LOG_D( + TAG, + "Added note: %c%c%lu.%lu = %u %lu", + note_char == '\0' ? '_' : note_char, + sharp_char == '\0' ? '_' : sharp_char, + octave, + dots, + semitone, + duration); + } else { + FURI_LOG_E( + TAG, + "Invalid note: %c%c%lu.%lu = %u %lu", + note_char == '\0' ? '_' : note_char, + sharp_char == '\0' ? '_' : sharp_char, + octave, + dots, + semitone, + duration); + } + cursor += skip_till(cursor, ','); + } + + if(*cursor != '\0') cursor++; + } + + return result; +} + +bool music_worker_load(MusicWorker* instance, const char* file_path) { + furi_assert(instance); + furi_assert(file_path); + + bool ret = false; + if(strcasestr(file_path, ".fmf")) { + ret = music_worker_load_fmf_from_file(instance, file_path); + } else { + ret = music_worker_load_rtttl_from_file(instance, file_path); + } + return ret; +} + +bool music_worker_load_fmf_from_file(MusicWorker* instance, const char* file_path) { + furi_assert(instance); + furi_assert(file_path); + + bool result = false; + FuriString* temp_str; + temp_str = furi_string_alloc(); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + + do { + if(!flipper_format_file_open_existing(file, file_path)) break; + + uint32_t version = 0; + if(!flipper_format_read_header(file, temp_str, &version)) break; + if(furi_string_cmp_str(temp_str, MUSIC_PLAYER_FILETYPE) || + (version != MUSIC_PLAYER_VERSION)) { + FURI_LOG_E(TAG, "Incorrect file format or version"); + break; + } + + if(!flipper_format_read_uint32(file, "BPM", &instance->bpm, 1)) { + FURI_LOG_E(TAG, "BPM is missing"); + break; + } + if(!flipper_format_read_uint32(file, "Duration", &instance->duration, 1)) { + FURI_LOG_E(TAG, "Duration is missing"); + break; + } + if(!flipper_format_read_uint32(file, "Octave", &instance->octave, 1)) { + FURI_LOG_E(TAG, "Octave is missing"); + break; + } + + if(!flipper_format_read_string(file, "Notes", temp_str)) { + FURI_LOG_E(TAG, "Notes is missing"); + break; + } + + if(!music_worker_parse_notes(instance, furi_string_get_cstr(temp_str))) { + break; + } + + result = true; + } while(false); + + furi_record_close(RECORD_STORAGE); + flipper_format_free(file); + furi_string_free(temp_str); + + return result; +} + +bool music_worker_load_rtttl_from_file(MusicWorker* instance, const char* file_path) { + furi_assert(instance); + furi_assert(file_path); + + bool result = false; + FuriString* content; + content = furi_string_alloc(); + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + do { + if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { + FURI_LOG_E(TAG, "Unable to open file"); + break; + }; + + size_t ret = 0; + do { + uint8_t buffer[65] = {0}; + ret = storage_file_read(file, buffer, sizeof(buffer) - 1); + for(size_t i = 0; i < ret; i++) { + furi_string_push_back(content, buffer[i]); + } + } while(ret > 0); + + furi_string_trim(content); + if(!furi_string_size(content)) { + FURI_LOG_E(TAG, "Empty file"); + break; + } + + if(!music_worker_load_rtttl_from_string(instance, furi_string_get_cstr(content))) { + FURI_LOG_E(TAG, "Invalid file content"); + break; + } + + result = true; + } while(0); + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + furi_string_free(content); + + return result; +} + +bool music_worker_load_rtttl_from_string(MusicWorker* instance, const char* string) { + furi_assert(instance); + + const char* cursor = string; + + // Skip name + cursor += skip_till(cursor, ':'); + if(*cursor != ':') { + return false; + } + + // Duration + cursor += skip_till(cursor, '='); + if(*cursor != '=') { + return false; + } + cursor++; + cursor += extract_number(cursor, &instance->duration); + + // Octave + cursor += skip_till(cursor, '='); + if(*cursor != '=') { + return false; + } + cursor++; + cursor += extract_number(cursor, &instance->octave); + + // BPM + cursor += skip_till(cursor, '='); + if(*cursor != '=') { + return false; + } + cursor++; + cursor += extract_number(cursor, &instance->bpm); + + // Notes + cursor += skip_till(cursor, ':'); + if(*cursor != ':') { + return false; + } + cursor++; + if(!music_worker_parse_notes(instance, cursor)) { + return false; + } + + return true; +} + +void music_worker_set_callback(MusicWorker* instance, MusicWorkerCallback callback, void* context) { + furi_assert(instance); + instance->callback = callback; + instance->callback_context = context; +} + +void music_worker_set_volume(MusicWorker* instance, float volume) { + furi_assert(instance); + instance->volume = volume; +} + +void music_worker_start(MusicWorker* instance) { + furi_assert(instance); + furi_assert(instance->should_work == false); + + instance->should_work = true; + furi_thread_start(instance->thread); +} + +void music_worker_stop(MusicWorker* instance) { + furi_assert(instance); + furi_assert(instance->should_work == true); + + instance->should_work = false; + furi_thread_join(instance->thread); +} + +bool music_worker_is_playing(MusicWorker* instance) { + furi_assert(instance); + return instance->should_work; +} diff --git a/lib/music_worker/music_worker.h b/lib/music_worker/music_worker.h new file mode 100644 index 00000000000..5a7cb4936a9 --- /dev/null +++ b/lib/music_worker/music_worker.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +typedef void (*MusicWorkerCallback)( + uint8_t semitone, + uint8_t dots, + uint8_t duration, + float position, + void* context); + +typedef struct MusicWorker MusicWorker; + +MusicWorker* music_worker_alloc(); + +void music_worker_clear(MusicWorker* instance); + +void music_worker_free(MusicWorker* instance); + +bool music_worker_load(MusicWorker* instance, const char* file_path); + +bool music_worker_load_fmf_from_file(MusicWorker* instance, const char* file_path); + +bool music_worker_load_rtttl_from_file(MusicWorker* instance, const char* file_path); + +bool music_worker_load_rtttl_from_string(MusicWorker* instance, const char* string); + +void music_worker_set_callback(MusicWorker* instance, MusicWorkerCallback callback, void* context); + +void music_worker_set_volume(MusicWorker* instance, float volume); + +void music_worker_start(MusicWorker* instance); + +void music_worker_stop(MusicWorker* instance); + +bool music_worker_is_playing(MusicWorker* instance); diff --git a/lib/nanopb.scons b/lib/nanopb.scons new file mode 100644 index 00000000000..43e828b85e6 --- /dev/null +++ b/lib/nanopb.scons @@ -0,0 +1,31 @@ +from fbt.util import GLOB_FILE_EXCLUSION + +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/nanopb", + ], + CPPDEFINES=[ + "PB_ENABLE_MALLOC", + ], + SDK_HEADERS=[ + File("nanopb/pb.h"), + File("nanopb/pb_decode.h"), + File("nanopb/pb_encode.h"), + ], +) + + +libenv = env.Clone(FW_LIB_NAME="nanopb") +libenv.ApplyLibFlags() + +sources = Glob( + "nanopb/*.c*", + exclude=GLOB_FILE_EXCLUSION, + source=True, +) + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index 657f3a9e5b2..d2cfbe2fb61 100644 --- a/lib/nfc/SConscript +++ b/lib/nfc/SConscript @@ -4,6 +4,52 @@ env.Append( CPPPATH=[ "#/lib/nfc", ], + LINT_SOURCES=[ + Dir("."), + ], + SDK_HEADERS=[ + # Main + File("nfc.h"), + File("nfc_device.h"), + File("nfc_listener.h"), + File("nfc_poller.h"), + File("nfc_scanner.h"), + # Protocols + File("protocols/iso14443_3a/iso14443_3a.h"), + File("protocols/iso14443_3b/iso14443_3b.h"), + File("protocols/iso14443_4a/iso14443_4a.h"), + File("protocols/iso14443_4b/iso14443_4b.h"), + File("protocols/mf_ultralight/mf_ultralight.h"), + File("protocols/mf_classic/mf_classic.h"), + File("protocols/mf_desfire/mf_desfire.h"), + File("protocols/slix/slix.h"), + File("protocols/st25tb/st25tb.h"), + # Pollers + File("protocols/iso14443_3a/iso14443_3a_poller.h"), + File("protocols/iso14443_3b/iso14443_3b_poller.h"), + File("protocols/iso14443_4a/iso14443_4a_poller.h"), + File("protocols/iso14443_4b/iso14443_4b_poller.h"), + File("protocols/mf_ultralight/mf_ultralight_poller.h"), + File("protocols/mf_classic/mf_classic_poller.h"), + File("protocols/mf_desfire/mf_desfire_poller.h"), + File("protocols/st25tb/st25tb_poller.h"), + # Listeners + File("protocols/iso14443_3a/iso14443_3a_listener.h"), + File("protocols/iso14443_4a/iso14443_4a_listener.h"), + File("protocols/mf_ultralight/mf_ultralight_listener.h"), + File("protocols/mf_classic/mf_classic_listener.h"), + # Sync API + File("protocols/iso14443_3a/iso14443_3a_poller_sync.h"), + File("protocols/mf_ultralight/mf_ultralight_poller_sync.h"), + File("protocols/mf_classic/mf_classic_poller_sync.h"), + File("protocols/st25tb/st25tb_poller_sync.h"), + # Misc + File("helpers/nfc_util.h"), + File("helpers/iso14443_crc.h"), + File("helpers/iso13239_crc.h"), + File("helpers/nfc_data_generator.h"), + File("helpers/nfc_dict.h"), + ], ) libenv = env.Clone(FW_LIB_NAME="nfc") diff --git a/lib/nfc/helpers/felica_crc.c b/lib/nfc/helpers/felica_crc.c new file mode 100644 index 00000000000..d5e0853a109 --- /dev/null +++ b/lib/nfc/helpers/felica_crc.c @@ -0,0 +1,52 @@ +#include "felica_crc.h" + +#include + +#define FELICA_CRC_POLY (0x1021U) // Polynomial: x^16 + x^12 + x^5 + 1 +#define FELICA_CRC_INIT (0x0000U) + +uint16_t felica_crc_calculate(const uint8_t* data, size_t length) { + uint16_t crc = FELICA_CRC_INIT; + + for(size_t i = 0; i < length; i++) { + crc ^= ((uint16_t)data[i] << 8); + for(size_t j = 0; j < 8; j++) { + if(crc & 0x8000) { + crc <<= 1; + crc ^= FELICA_CRC_POLY; + } else { + crc <<= 1; + } + } + } + + return (crc << 8) | (crc >> 8); +} + +void felica_crc_append(BitBuffer* buf) { + const uint8_t* data = bit_buffer_get_data(buf); + const size_t data_size = bit_buffer_get_size_bytes(buf); + + const uint16_t crc = felica_crc_calculate(data, data_size); + bit_buffer_append_bytes(buf, (const uint8_t*)&crc, FELICA_CRC_SIZE); +} + +bool felica_crc_check(const BitBuffer* buf) { + const size_t data_size = bit_buffer_get_size_bytes(buf); + if(data_size <= FELICA_CRC_SIZE) return false; + + uint16_t crc_received; + bit_buffer_write_bytes_mid(buf, &crc_received, data_size - FELICA_CRC_SIZE, FELICA_CRC_SIZE); + + const uint8_t* data = bit_buffer_get_data(buf); + const uint16_t crc_calc = felica_crc_calculate(data, data_size - FELICA_CRC_SIZE); + + return (crc_calc == crc_received); +} + +void felica_crc_trim(BitBuffer* buf) { + const size_t data_size = bit_buffer_get_size_bytes(buf); + furi_assert(data_size > FELICA_CRC_SIZE); + + bit_buffer_set_size_bytes(buf, data_size - FELICA_CRC_SIZE); +} diff --git a/lib/nfc/helpers/felica_crc.h b/lib/nfc/helpers/felica_crc.h new file mode 100644 index 00000000000..d1dc29e74db --- /dev/null +++ b/lib/nfc/helpers/felica_crc.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +#include "bit_buffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define FELICA_CRC_SIZE sizeof(uint16_t) + +void felica_crc_append(BitBuffer* buf); + +bool felica_crc_check(const BitBuffer* buf); + +void felica_crc_trim(BitBuffer* buf); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/helpers/iso13239_crc.c b/lib/nfc/helpers/iso13239_crc.c new file mode 100644 index 00000000000..c54fbcfb21c --- /dev/null +++ b/lib/nfc/helpers/iso13239_crc.c @@ -0,0 +1,62 @@ +#include "iso13239_crc.h" + +#include + +#define ISO13239_CRC_INIT_DEFAULT (0xFFFFU) +#define ISO13239_CRC_INIT_PICOPASS (0xE012U) +#define ISO13239_CRC_POLY (0x8408U) + +static uint16_t + iso13239_crc_calculate(Iso13239CrcType type, const uint8_t* data, size_t data_size) { + uint16_t crc; + + if(type == Iso13239CrcTypeDefault) { + crc = ISO13239_CRC_INIT_DEFAULT; + } else if(type == Iso13239CrcTypePicopass) { + crc = ISO13239_CRC_INIT_PICOPASS; + } else { + furi_crash("Wrong ISO13239 CRC type"); + } + + for(size_t i = 0; i < data_size; ++i) { + crc ^= (uint16_t)data[i]; + for(size_t j = 0; j < 8; ++j) { + if(crc & 1U) { + crc = (crc >> 1) ^ ISO13239_CRC_POLY; + } else { + crc >>= 1; + } + } + } + + return type == Iso13239CrcTypePicopass ? crc : ~crc; +} + +void iso13239_crc_append(Iso13239CrcType type, BitBuffer* buf) { + const uint8_t* data = bit_buffer_get_data(buf); + const size_t data_size = bit_buffer_get_size_bytes(buf); + + const uint16_t crc = iso13239_crc_calculate(type, data, data_size); + bit_buffer_append_bytes(buf, (const uint8_t*)&crc, ISO13239_CRC_SIZE); +} + +bool iso13239_crc_check(Iso13239CrcType type, const BitBuffer* buf) { + const size_t data_size = bit_buffer_get_size_bytes(buf); + if(data_size <= ISO13239_CRC_SIZE) return false; + + uint16_t crc_received; + bit_buffer_write_bytes_mid( + buf, &crc_received, data_size - ISO13239_CRC_SIZE, ISO13239_CRC_SIZE); + + const uint8_t* data = bit_buffer_get_data(buf); + const uint16_t crc_calc = iso13239_crc_calculate(type, data, data_size - ISO13239_CRC_SIZE); + + return (crc_calc == crc_received); +} + +void iso13239_crc_trim(BitBuffer* buf) { + const size_t data_size = bit_buffer_get_size_bytes(buf); + furi_assert(data_size > ISO13239_CRC_SIZE); + + bit_buffer_set_size_bytes(buf, data_size - ISO13239_CRC_SIZE); +} diff --git a/lib/nfc/helpers/iso13239_crc.h b/lib/nfc/helpers/iso13239_crc.h new file mode 100644 index 00000000000..c71ec6befe4 --- /dev/null +++ b/lib/nfc/helpers/iso13239_crc.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISO13239_CRC_SIZE sizeof(uint16_t) + +typedef enum { + Iso13239CrcTypeDefault, + Iso13239CrcTypePicopass, +} Iso13239CrcType; + +void iso13239_crc_append(Iso13239CrcType type, BitBuffer* buf); + +bool iso13239_crc_check(Iso13239CrcType type, const BitBuffer* buf); + +void iso13239_crc_trim(BitBuffer* buf); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/helpers/iso14443_4_layer.c b/lib/nfc/helpers/iso14443_4_layer.c new file mode 100644 index 00000000000..26f4dc3b7b4 --- /dev/null +++ b/lib/nfc/helpers/iso14443_4_layer.c @@ -0,0 +1,64 @@ +#include "iso14443_4_layer.h" + +#include + +#define ISO14443_4_BLOCK_PCB (1U << 1) +#define ISO14443_4_BLOCK_PCB_I (0U) +#define ISO14443_4_BLOCK_PCB_R (5U << 5) +#define ISO14443_4_BLOCK_PCB_S (3U << 6) + +struct Iso14443_4Layer { + uint8_t pcb; + uint8_t pcb_prev; +}; + +static inline void iso14443_4_layer_update_pcb(Iso14443_4Layer* instance) { + instance->pcb_prev = instance->pcb; + instance->pcb ^= (uint8_t)0x01; +} + +Iso14443_4Layer* iso14443_4_layer_alloc() { + Iso14443_4Layer* instance = malloc(sizeof(Iso14443_4Layer)); + + iso14443_4_layer_reset(instance); + return instance; +} + +void iso14443_4_layer_free(Iso14443_4Layer* instance) { + furi_assert(instance); + free(instance); +} + +void iso14443_4_layer_reset(Iso14443_4Layer* instance) { + furi_assert(instance); + instance->pcb = ISO14443_4_BLOCK_PCB_I | ISO14443_4_BLOCK_PCB; +} + +void iso14443_4_layer_encode_block( + Iso14443_4Layer* instance, + const BitBuffer* input_data, + BitBuffer* block_data) { + furi_assert(instance); + + bit_buffer_append_byte(block_data, instance->pcb); + bit_buffer_append(block_data, input_data); + + iso14443_4_layer_update_pcb(instance); +} + +bool iso14443_4_layer_decode_block( + Iso14443_4Layer* instance, + BitBuffer* output_data, + const BitBuffer* block_data) { + furi_assert(instance); + + bool ret = false; + + do { + if(!bit_buffer_starts_with_byte(block_data, instance->pcb_prev)) break; + bit_buffer_copy_right(output_data, block_data, 1); + ret = true; + } while(false); + + return ret; +} diff --git a/lib/nfc/helpers/iso14443_4_layer.h b/lib/nfc/helpers/iso14443_4_layer.h new file mode 100644 index 00000000000..712173ce1b1 --- /dev/null +++ b/lib/nfc/helpers/iso14443_4_layer.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Iso14443_4Layer Iso14443_4Layer; + +Iso14443_4Layer* iso14443_4_layer_alloc(); + +void iso14443_4_layer_free(Iso14443_4Layer* instance); + +void iso14443_4_layer_reset(Iso14443_4Layer* instance); + +void iso14443_4_layer_encode_block( + Iso14443_4Layer* instance, + const BitBuffer* input_data, + BitBuffer* block_data); + +bool iso14443_4_layer_decode_block( + Iso14443_4Layer* instance, + BitBuffer* output_data, + const BitBuffer* block_data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/helpers/iso14443_crc.c b/lib/nfc/helpers/iso14443_crc.c new file mode 100644 index 00000000000..fda9871aa99 --- /dev/null +++ b/lib/nfc/helpers/iso14443_crc.c @@ -0,0 +1,57 @@ +#include "iso14443_crc.h" + +#include + +#define ISO14443_3A_CRC_INIT (0x6363U) +#define ISO14443_3B_CRC_INIT (0xFFFFU) + +static uint16_t + iso14443_crc_calculate(Iso14443CrcType type, const uint8_t* data, size_t data_size) { + uint16_t crc; + + if(type == Iso14443CrcTypeA) { + crc = ISO14443_3A_CRC_INIT; + } else if(type == Iso14443CrcTypeB) { + crc = ISO14443_3B_CRC_INIT; + } else { + furi_crash("Wrong ISO14443 CRC type"); + } + + for(size_t i = 0; i < data_size; i++) { + uint8_t byte = data[i]; + byte ^= (uint8_t)(crc & 0xff); + byte ^= byte << 4; + crc = (crc >> 8) ^ (((uint16_t)byte) << 8) ^ (((uint16_t)byte) << 3) ^ (byte >> 4); + } + + return type == Iso14443CrcTypeA ? crc : ~crc; +} + +void iso14443_crc_append(Iso14443CrcType type, BitBuffer* buf) { + const uint8_t* data = bit_buffer_get_data(buf); + const size_t data_size = bit_buffer_get_size_bytes(buf); + + const uint16_t crc = iso14443_crc_calculate(type, data, data_size); + bit_buffer_append_bytes(buf, (const uint8_t*)&crc, ISO14443_CRC_SIZE); +} + +bool iso14443_crc_check(Iso14443CrcType type, const BitBuffer* buf) { + const size_t data_size = bit_buffer_get_size_bytes(buf); + if(data_size <= ISO14443_CRC_SIZE) return false; + + uint16_t crc_received; + bit_buffer_write_bytes_mid( + buf, &crc_received, data_size - ISO14443_CRC_SIZE, ISO14443_CRC_SIZE); + + const uint8_t* data = bit_buffer_get_data(buf); + const uint16_t crc_calc = iso14443_crc_calculate(type, data, data_size - ISO14443_CRC_SIZE); + + return (crc_calc == crc_received); +} + +void iso14443_crc_trim(BitBuffer* buf) { + const size_t data_size = bit_buffer_get_size_bytes(buf); + furi_assert(data_size > ISO14443_CRC_SIZE); + + bit_buffer_set_size_bytes(buf, data_size - ISO14443_CRC_SIZE); +} diff --git a/lib/nfc/helpers/iso14443_crc.h b/lib/nfc/helpers/iso14443_crc.h new file mode 100644 index 00000000000..14a63841e71 --- /dev/null +++ b/lib/nfc/helpers/iso14443_crc.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISO14443_CRC_SIZE sizeof(uint16_t) + +typedef enum { + Iso14443CrcTypeA, + Iso14443CrcTypeB, +} Iso14443CrcType; + +void iso14443_crc_append(Iso14443CrcType type, BitBuffer* buf); + +bool iso14443_crc_check(Iso14443CrcType type, const BitBuffer* buf); + +void iso14443_crc_trim(BitBuffer* buf); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/helpers/mf_classic_dict.c b/lib/nfc/helpers/mf_classic_dict.c deleted file mode 100644 index 410ddbd8baf..00000000000 --- a/lib/nfc/helpers/mf_classic_dict.c +++ /dev/null @@ -1,148 +0,0 @@ -#include "mf_classic_dict.h" - -#include -#include - -#define MF_CLASSIC_DICT_FLIPPER_PATH EXT_PATH("nfc/assets/mf_classic_dict.nfc") -#define MF_CLASSIC_DICT_USER_PATH EXT_PATH("nfc/assets/mf_classic_dict_user.nfc") - -#define TAG "MfClassicDict" - -#define NFC_MF_CLASSIC_KEY_LEN (13) - -struct MfClassicDict { - Stream* stream; - uint32_t total_keys; -}; - -bool mf_classic_dict_check_presence(MfClassicDictType dict_type) { - Storage* storage = furi_record_open(RECORD_STORAGE); - - bool dict_present = false; - if(dict_type == MfClassicDictTypeFlipper) { - dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_FLIPPER_PATH, NULL) == FSE_OK; - } else if(dict_type == MfClassicDictTypeUser) { - dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_USER_PATH, NULL) == FSE_OK; - } - - furi_record_close(RECORD_STORAGE); - - return dict_present; -} - -MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) { - MfClassicDict* dict = malloc(sizeof(MfClassicDict)); - Storage* storage = furi_record_open(RECORD_STORAGE); - dict->stream = buffered_file_stream_alloc(storage); - furi_record_close(RECORD_STORAGE); - - bool dict_loaded = false; - do { - if(dict_type == MfClassicDictTypeFlipper) { - if(!buffered_file_stream_open( - dict->stream, MF_CLASSIC_DICT_FLIPPER_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { - buffered_file_stream_close(dict->stream); - break; - } - } else if(dict_type == MfClassicDictTypeUser) { - if(!buffered_file_stream_open( - dict->stream, MF_CLASSIC_DICT_USER_PATH, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) { - buffered_file_stream_close(dict->stream); - break; - } - } - - // Read total amount of keys - string_t next_line; - string_init(next_line); - while(true) { - if(!stream_read_line(dict->stream, next_line)) break; - if(string_get_char(next_line, 0) == '#') continue; - if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; - dict->total_keys++; - } - string_clear(next_line); - stream_rewind(dict->stream); - - dict_loaded = true; - FURI_LOG_I(TAG, "Loaded dictionary with %d keys", dict->total_keys); - } while(false); - - if(!dict_loaded) { - buffered_file_stream_close(dict->stream); - free(dict); - dict = NULL; - } - - return dict; -} - -void mf_classic_dict_free(MfClassicDict* dict) { - furi_assert(dict); - furi_assert(dict->stream); - - buffered_file_stream_close(dict->stream); - stream_free(dict->stream); - free(dict); -} - -uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict) { - furi_assert(dict); - - return dict->total_keys; -} - -bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key) { - furi_assert(dict); - furi_assert(dict->stream); - - uint8_t key_byte_tmp = 0; - string_t next_line; - string_init(next_line); - - bool key_read = false; - *key = 0ULL; - while(!key_read) { - if(!stream_read_line(dict->stream, next_line)) break; - if(string_get_char(next_line, 0) == '#') continue; - if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; - for(uint8_t i = 0; i < 12; i += 2) { - args_char_to_hex( - string_get_char(next_line, i), string_get_char(next_line, i + 1), &key_byte_tmp); - *key |= (uint64_t)key_byte_tmp << 8 * (5 - i / 2); - } - key_read = true; - } - - string_clear(next_line); - return key_read; -} - -bool mf_classic_dict_rewind(MfClassicDict* dict) { - furi_assert(dict); - furi_assert(dict->stream); - - return stream_rewind(dict->stream); -} - -bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key) { - furi_assert(dict); - furi_assert(dict->stream); - - string_t key_str; - string_init(key_str); - for(size_t i = 0; i < 6; i++) { - string_cat_printf(key_str, "%02X", key[i]); - } - string_cat_printf(key_str, "\n"); - - bool key_added = false; - do { - if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break; - if(!stream_insert_string(dict->stream, key_str)) break; - key_added = true; - } while(false); - - string_clear(key_str); - return key_added; -} diff --git a/lib/nfc/helpers/mf_classic_dict.h b/lib/nfc/helpers/mf_classic_dict.h deleted file mode 100644 index 2654e668c54..00000000000 --- a/lib/nfc/helpers/mf_classic_dict.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -typedef enum { - MfClassicDictTypeUser, - MfClassicDictTypeFlipper, -} MfClassicDictType; - -typedef struct MfClassicDict MfClassicDict; - -bool mf_classic_dict_check_presence(MfClassicDictType dict_type); - -MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type); - -void mf_classic_dict_free(MfClassicDict* dict); - -uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict); - -bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key); - -bool mf_classic_dict_rewind(MfClassicDict* dict); - -bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key); diff --git a/lib/nfc/helpers/nfc_data_generator.c b/lib/nfc/helpers/nfc_data_generator.c new file mode 100644 index 00000000000..21f062605b6 --- /dev/null +++ b/lib/nfc/helpers/nfc_data_generator.c @@ -0,0 +1,581 @@ +#include "nfc_data_generator.h" + +#include +#include +#include +#include +#include + +#define NXP_MANUFACTURER_ID (0x04) + +typedef void (*NfcDataGeneratorHandler)(NfcDevice* nfc_device); + +typedef struct { + const char* name; + NfcDataGeneratorHandler handler; +} NfcDataGenerator; + +static const uint8_t version_bytes_mf0ulx1[] = {0x00, 0x04, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03}; +static const uint8_t version_bytes_ntag21x[] = {0x00, 0x04, 0x04, 0x02, 0x01, 0x00, 0x00, 0x03}; +static const uint8_t version_bytes_ntag_i2c[] = {0x00, 0x04, 0x04, 0x05, 0x02, 0x00, 0x00, 0x03}; +static const uint8_t default_data_ntag203[] = + {0xE1, 0x10, 0x12, 0x00, 0x01, 0x03, 0xA0, 0x10, 0x44, 0x03, 0x00, 0xFE}; +static const uint8_t default_data_ntag213[] = {0x01, 0x03, 0xA0, 0x0C, 0x34, 0x03, 0x00, 0xFE}; +static const uint8_t default_data_ntag215_216[] = {0x03, 0x00, 0xFE}; +static const uint8_t default_data_ntag_i2c[] = {0xE1, 0x10, 0x00, 0x00, 0x03, 0x00, 0xFE}; +static const uint8_t default_config_ntag_i2c[] = {0x01, 0x00, 0xF8, 0x48, 0x08, 0x01, 0x00, 0x00}; + +static void nfc_generate_mf_ul_uid(uint8_t* uid) { + uid[0] = NXP_MANUFACTURER_ID; + furi_hal_random_fill_buf(&uid[1], 6); + uid[3] |= 0x01; // To avoid forbidden 0x88 value + // I'm not sure how this is generated, but the upper nybble always seems to be 8 + uid[6] &= 0x0F; + uid[6] |= 0x80; +} + +static void nfc_generate_mf_ul_common(MfUltralightData* mfu_data) { + mfu_data->iso14443_3a_data->uid_len = 7; + nfc_generate_mf_ul_uid(mfu_data->iso14443_3a_data->uid); + mfu_data->iso14443_3a_data->atqa[0] = 0x44; + mfu_data->iso14443_3a_data->atqa[1] = 0x00; + mfu_data->iso14443_3a_data->sak = 0x00; +} + +static void nfc_generate_calc_bcc(uint8_t* uid, uint8_t* bcc0, uint8_t* bcc1) { + *bcc0 = 0x88 ^ uid[0] ^ uid[1] ^ uid[2]; + *bcc1 = uid[3] ^ uid[4] ^ uid[5] ^ uid[6]; +} + +static void nfc_generate_mf_ul_copy_uid_with_bcc(MfUltralightData* mfu_data) { + memcpy(mfu_data->page[0].data, mfu_data->iso14443_3a_data->uid, 3); + memcpy(mfu_data->page[1].data, &mfu_data->iso14443_3a_data->uid[3], 4); + + nfc_generate_calc_bcc( + mfu_data->iso14443_3a_data->uid, &mfu_data->page[0].data[3], &mfu_data->page[2].data[0]); +} + +static void nfc_generate_mf_ul_orig(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + nfc_generate_mf_ul_common(mfu_data); + + mfu_data->type = MfUltralightTypeUnknown; + mfu_data->pages_total = 16; + mfu_data->pages_read = 16; + nfc_generate_mf_ul_copy_uid_with_bcc(mfu_data); + memset(&mfu_data->page[4], 0xff, sizeof(MfUltralightPage)); + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_mf_ul_with_config_common(MfUltralightData* mfu_data, uint8_t num_pages) { + nfc_generate_mf_ul_common(mfu_data); + + mfu_data->pages_total = num_pages; + mfu_data->pages_read = num_pages; + nfc_generate_mf_ul_copy_uid_with_bcc(mfu_data); + uint16_t config_index = (num_pages - 4); + mfu_data->page[config_index].data[0] = 0x04; // STRG_MOD_EN + mfu_data->page[config_index].data[3] = 0xff; // AUTH0 + mfu_data->page[config_index + 1].data[1] = 0x05; // VCTID + memset(&mfu_data->page[config_index + 2], 0xff, sizeof(MfUltralightPage)); // Default PWD + if(num_pages > 20) { + mfu_data->page[config_index - 1].data[3] = MF_ULTRALIGHT_TEARING_FLAG_DEFAULT; + } +} + +static void nfc_generate_mf_ul_ev1_common(MfUltralightData* mfu_data, uint8_t num_pages) { + nfc_generate_mf_ul_with_config_common(mfu_data, num_pages); + memcpy(&mfu_data->version, version_bytes_mf0ulx1, sizeof(MfUltralightVersion)); + for(size_t i = 0; i < 3; ++i) { + mfu_data->tearing_flag[i].data = MF_ULTRALIGHT_TEARING_FLAG_DEFAULT; + } +} + +static void nfc_generate_mf_ul_11(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_mf_ul_ev1_common(mfu_data, 20); + mfu_data->type = MfUltralightTypeUL11; + mfu_data->version.prod_subtype = 0x01; + mfu_data->version.storage_size = 0x0B; + mfu_data->page[16].data[0] = 0x00; // Low capacitance version does not have STRG_MOD_EN + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_mf_ul_h11(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_mf_ul_ev1_common(mfu_data, 20); + mfu_data->type = MfUltralightTypeUL11; + mfu_data->version.prod_subtype = 0x02; + mfu_data->version.storage_size = 0x0B; + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_mf_ul_21(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_mf_ul_ev1_common(mfu_data, 41); + mfu_data->type = MfUltralightTypeUL21; + mfu_data->version.prod_subtype = 0x01; + mfu_data->version.storage_size = 0x0E; + mfu_data->page[37].data[0] = 0x00; // Low capacitance version does not have STRG_MOD_EN + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_mf_ul_h21(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_mf_ul_ev1_common(mfu_data, 41); + mfu_data->type = MfUltralightTypeUL21; + mfu_data->version.prod_subtype = 0x02; + mfu_data->version.storage_size = 0x0E; + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_ntag203(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_mf_ul_common(mfu_data); + mfu_data->type = MfUltralightTypeNTAG203; + mfu_data->pages_total = 42; + mfu_data->pages_read = 42; + nfc_generate_mf_ul_copy_uid_with_bcc(mfu_data); + mfu_data->page[2].data[1] = 0x48; // Internal byte + memcpy(&mfu_data->page[3], default_data_ntag203, sizeof(MfUltralightPage)); //-V1086 + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_ntag21x_common(MfUltralightData* mfu_data, uint8_t num_pages) { + nfc_generate_mf_ul_with_config_common(mfu_data, num_pages); + memcpy(&mfu_data->version, version_bytes_ntag21x, sizeof(MfUltralightVersion)); + mfu_data->page[2].data[1] = 0x48; // Internal byte + // Capability container + mfu_data->page[3].data[0] = 0xE1; + mfu_data->page[3].data[1] = 0x10; +} + +static void nfc_generate_ntag213(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_ntag21x_common(mfu_data, 45); + mfu_data->type = MfUltralightTypeNTAG213; + mfu_data->version.storage_size = 0x0F; + mfu_data->page[3].data[2] = 0x12; + // Default contents + memcpy(&mfu_data->page[4], default_data_ntag213, sizeof(default_data_ntag213)); + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_ntag215(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_ntag21x_common(mfu_data, 135); + mfu_data->type = MfUltralightTypeNTAG215; + mfu_data->version.storage_size = 0x11; + mfu_data->page[3].data[2] = 0x3E; + // Default contents + memcpy(&mfu_data->page[4], default_data_ntag215_216, sizeof(default_data_ntag215_216)); + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_ntag216(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_ntag21x_common(mfu_data, 231); + mfu_data->type = MfUltralightTypeNTAG216; + mfu_data->version.storage_size = 0x13; + mfu_data->page[3].data[2] = 0x6D; + // Default contents + memcpy(&mfu_data->page[4], default_data_ntag215_216, sizeof(default_data_ntag215_216)); + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_ntag_i2c_common( + MfUltralightData* mfu_data, + MfUltralightType type, + uint16_t num_pages) { + nfc_generate_mf_ul_common(mfu_data); + + mfu_data->type = type; + memcpy(&mfu_data->version, version_bytes_ntag_i2c, sizeof(version_bytes_ntag_i2c)); + mfu_data->pages_total = num_pages; + mfu_data->pages_read = num_pages; + memcpy( + mfu_data->page[0].data, + mfu_data->iso14443_3a_data->uid, + mfu_data->iso14443_3a_data->uid_len); + mfu_data->page[1].data[3] = mfu_data->iso14443_3a_data->sak; + mfu_data->page[2].data[0] = mfu_data->iso14443_3a_data->atqa[0]; + mfu_data->page[2].data[1] = mfu_data->iso14443_3a_data->atqa[1]; + + uint16_t config_register_page = 0; + uint16_t session_register_page = 0; + + // Sync with mifare_ultralight.c + switch(type) { + case MfUltralightTypeNTAGI2C1K: + config_register_page = 227; + session_register_page = 229; + break; + case MfUltralightTypeNTAGI2C2K: + config_register_page = 481; + session_register_page = 483; + break; + case MfUltralightTypeNTAGI2CPlus1K: + case MfUltralightTypeNTAGI2CPlus2K: + config_register_page = 232; + session_register_page = 234; + break; + default: + furi_crash("Unknown MFUL"); + break; + } + + memcpy( + &mfu_data->page[config_register_page], + default_config_ntag_i2c, + sizeof(default_config_ntag_i2c)); + memcpy( + &mfu_data->page[session_register_page], + default_config_ntag_i2c, + sizeof(default_config_ntag_i2c)); +} + +static void nfc_generate_ntag_i2c_1k(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_ntag_i2c_common(mfu_data, MfUltralightTypeNTAGI2C1K, 231); + mfu_data->version.prod_ver_minor = 0x01; + mfu_data->version.storage_size = 0x13; + memcpy(&mfu_data->page[3], default_data_ntag_i2c, sizeof(default_data_ntag_i2c)); + mfu_data->page[3].data[2] = 0x6D; // Size of tag in CC + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_ntag_i2c_2k(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_ntag_i2c_common(mfu_data, MfUltralightTypeNTAGI2C2K, 485); + mfu_data->version.prod_ver_minor = 0x01; + mfu_data->version.storage_size = 0x15; + memcpy(&mfu_data->page[3], default_data_ntag_i2c, sizeof(default_data_ntag_i2c)); + mfu_data->page[3].data[2] = 0xEA; // Size of tag in CC + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_ntag_i2c_plus_common( + MfUltralightData* mfu_data, + MfUltralightType type, + uint16_t num_pages) { + nfc_generate_ntag_i2c_common(mfu_data, type, num_pages); + + uint16_t config_index = 227; + mfu_data->page[config_index].data[3] = 0xff; // AUTH0 + + memset(&mfu_data->page[config_index + 2], 0xFF, sizeof(MfUltralightPage)); // Default PWD +} + +static void nfc_generate_ntag_i2c_plus_1k(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_ntag_i2c_plus_common(mfu_data, MfUltralightTypeNTAGI2CPlus1K, 236); + mfu_data->version.prod_ver_minor = 0x02; + mfu_data->version.storage_size = 0x13; + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_ntag_i2c_plus_2k(NfcDevice* nfc_device) { + MfUltralightData* mfu_data = mf_ultralight_alloc(); + + nfc_generate_ntag_i2c_plus_common(mfu_data, MfUltralightTypeNTAGI2CPlus2K, 492); + mfu_data->version.prod_ver_minor = 0x02; + mfu_data->version.storage_size = 0x15; + + nfc_device_set_data(nfc_device, NfcProtocolMfUltralight, mfu_data); + mf_ultralight_free(mfu_data); +} + +static void nfc_generate_mf_classic_uid(uint8_t* uid, uint8_t length) { + uid[0] = NXP_MANUFACTURER_ID; + furi_hal_random_fill_buf(&uid[1], length - 1); + uid[3] |= 0x01; // To avoid forbidden 0x88 value +} + +static void + nfc_generate_mf_classic_common(MfClassicData* data, uint8_t uid_len, MfClassicType type) { + data->iso14443_3a_data->uid_len = uid_len; + data->iso14443_3a_data->atqa[0] = 0x00; + data->iso14443_3a_data->atqa[1] = 0x00; + data->iso14443_3a_data->sak = 0x00; + // Calculate the proper ATQA and SAK + if(uid_len == 7) { + data->iso14443_3a_data->atqa[0] |= 0x40; + } + if(type == MfClassicType1k) { + data->iso14443_3a_data->atqa[0] |= 0x04; + data->iso14443_3a_data->sak = 0x08; + } else if(type == MfClassicType4k) { + data->iso14443_3a_data->atqa[0] |= 0x02; + data->iso14443_3a_data->sak = 0x18; + } else if(type == MfClassicTypeMini) { + data->iso14443_3a_data->atqa[0] |= 0x08; + data->iso14443_3a_data->sak = 0x09; + } + data->type = type; +} + +static void nfc_generate_mf_classic_sector_trailer(MfClassicData* data, uint8_t block) { + // All keys are set to FFFF FFFF FFFFh at chip delivery and the bytes 6, 7 and 8 are set to FF0780h. + MfClassicSectorTrailer* sec_tr = (MfClassicSectorTrailer*)data->block[block].data; + sec_tr->access_bits.data[0] = 0xFF; + sec_tr->access_bits.data[1] = 0x07; + sec_tr->access_bits.data[2] = 0x80; + sec_tr->access_bits.data[3] = 0x69; // Nice + + for(int i = 0; i < 6; i++) { + sec_tr->key_a.data[i] = 0xFF; + sec_tr->key_b.data[i] = 0xFF; + } + + mf_classic_set_block_read(data, block, &data->block[block]); + mf_classic_set_key_found( + data, mf_classic_get_sector_by_block(block), MfClassicKeyTypeA, 0xFFFFFFFFFFFF); + mf_classic_set_key_found( + data, mf_classic_get_sector_by_block(block), MfClassicKeyTypeB, 0xFFFFFFFFFFFF); +} + +static void nfc_generate_mf_classic_block_0( + uint8_t* block, + uint8_t uid_len, + uint8_t sak, + uint8_t atqa0, + uint8_t atqa1) { + // Block length is always 16 bytes, and the UID can be either 4 or 7 bytes + furi_assert(uid_len == 4 || uid_len == 7); + furi_assert(block); + + if(uid_len == 4) { + // Calculate BCC + block[uid_len] = 0; + + for(int i = 0; i < uid_len; i++) { + block[uid_len] ^= block[i]; + } + } else { + uid_len -= 1; + } + + block[uid_len + 1] = sak; + block[uid_len + 2] = atqa0; + block[uid_len + 3] = atqa1; + + for(int i = uid_len + 4; i < 16; i++) { + block[i] = 0xFF; + } +} + +static void nfc_generate_mf_classic(NfcDevice* nfc_device, uint8_t uid_len, MfClassicType type) { + MfClassicData* mfc_data = mf_classic_alloc(); + + nfc_generate_mf_classic_uid(mfc_data->block[0].data, uid_len); + nfc_generate_mf_classic_common(mfc_data, uid_len, type); + + // Set the UID + mfc_data->iso14443_3a_data->uid[0] = NXP_MANUFACTURER_ID; + for(int i = 1; i < uid_len; i++) { + mfc_data->iso14443_3a_data->uid[i] = mfc_data->block[0].data[i]; + } + + mf_classic_set_block_read(mfc_data, 0, &mfc_data->block[0]); + + uint16_t block_num = mf_classic_get_total_block_num(type); + if(type == MfClassicType4k) { + // Set every block to 0x00 + for(uint16_t i = 1; i < block_num; i++) { + if(mf_classic_is_sector_trailer(i)) { + nfc_generate_mf_classic_sector_trailer(mfc_data, i); + } else { + memset(&mfc_data->block[i].data, 0x00, 16); + } + mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]); + } + } else if(type == MfClassicType1k) { + // Set every block to 0x00 + for(uint16_t i = 1; i < block_num; i++) { + if(mf_classic_is_sector_trailer(i)) { + nfc_generate_mf_classic_sector_trailer(mfc_data, i); + } else { + memset(&mfc_data->block[i].data, 0x00, 16); + } + mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]); + } + } else if(type == MfClassicTypeMini) { + // Set every block to 0x00 + for(uint16_t i = 1; i < block_num; i++) { + if(mf_classic_is_sector_trailer(i)) { + nfc_generate_mf_classic_sector_trailer(mfc_data, i); + } else { + memset(&mfc_data->block[i].data, 0x00, 16); + } + mf_classic_set_block_read(mfc_data, i, &mfc_data->block[i]); + } + } + + nfc_generate_mf_classic_block_0( + mfc_data->block[0].data, + uid_len, + mfc_data->iso14443_3a_data->sak, + mfc_data->iso14443_3a_data->atqa[0], + mfc_data->iso14443_3a_data->atqa[1]); + + mfc_data->type = type; + + nfc_device_set_data(nfc_device, NfcProtocolMfClassic, mfc_data); + mf_classic_free(mfc_data); +} + +static void nfc_generate_mf_classic_mini(NfcDevice* nfc_device) { + nfc_generate_mf_classic(nfc_device, 4, MfClassicTypeMini); +} + +static void nfc_generate_mf_classic_1k_4b_uid(NfcDevice* nfc_device) { + nfc_generate_mf_classic(nfc_device, 4, MfClassicType1k); +} + +static void nfc_generate_mf_classic_1k_7b_uid(NfcDevice* nfc_device) { + nfc_generate_mf_classic(nfc_device, 7, MfClassicType1k); +} + +static void nfc_generate_mf_classic_4k_4b_uid(NfcDevice* nfc_device) { + nfc_generate_mf_classic(nfc_device, 4, MfClassicType4k); +} + +static void nfc_generate_mf_classic_4k_7b_uid(NfcDevice* nfc_device) { + nfc_generate_mf_classic(nfc_device, 7, MfClassicType4k); +} + +static const NfcDataGenerator nfc_data_generator[NfcDataGeneratorTypeNum] = { + [NfcDataGeneratorTypeMfUltralight] = + { + .name = "Mifare Ultralight", + .handler = nfc_generate_mf_ul_orig, + }, + [NfcDataGeneratorTypeMfUltralightEV1_11] = + { + .name = "Mifare Ultralight EV1 11", + .handler = nfc_generate_mf_ul_11, + }, + [NfcDataGeneratorTypeMfUltralightEV1_H11] = + { + .name = "Mifare Ultralight EV1 H11", + .handler = nfc_generate_mf_ul_h11, + }, + [NfcDataGeneratorTypeMfUltralightEV1_21] = + { + .name = "Mifare Ultralight EV1 21", + .handler = nfc_generate_mf_ul_21, + }, + [NfcDataGeneratorTypeMfUltralightEV1_H21] = + { + .name = "Mifare Ultralight EV1 H21", + .handler = nfc_generate_mf_ul_h21, + }, + [NfcDataGeneratorTypeNTAG203] = + { + .name = "NTAG203", + .handler = nfc_generate_ntag203, + }, + [NfcDataGeneratorTypeNTAG213] = + { + .name = "NTAG213", + .handler = nfc_generate_ntag213, + }, + [NfcDataGeneratorTypeNTAG215] = + { + .name = "NTAG215", + .handler = nfc_generate_ntag215, + }, + [NfcDataGeneratorTypeNTAG216] = + { + .name = "NTAG216", + .handler = nfc_generate_ntag216, + }, + [NfcDataGeneratorTypeNTAGI2C1k] = + { + .name = "NTAG I2C 1k", + .handler = nfc_generate_ntag_i2c_1k, + }, + [NfcDataGeneratorTypeNTAGI2C2k] = + { + .name = "NTAG I2C 2k", + .handler = nfc_generate_ntag_i2c_2k, + }, + [NfcDataGeneratorTypeNTAGI2CPlus1k] = + { + .name = "NTAG I2C Plus 1k", + .handler = nfc_generate_ntag_i2c_plus_1k, + }, + [NfcDataGeneratorTypeNTAGI2CPlus2k] = + { + .name = "NTAG I2C Plus 2k", + .handler = nfc_generate_ntag_i2c_plus_2k, + }, + [NfcDataGeneratorTypeMfClassicMini] = + { + .name = "Mifare Mini", + .handler = nfc_generate_mf_classic_mini, + }, + [NfcDataGeneratorTypeMfClassic1k_4b] = + { + .name = "Mifare Classic 1k 4byte UID", + .handler = nfc_generate_mf_classic_1k_4b_uid, + }, + [NfcDataGeneratorTypeMfClassic1k_7b] = + { + .name = "Mifare Classic 1k 7byte UID", + .handler = nfc_generate_mf_classic_1k_7b_uid, + }, + [NfcDataGeneratorTypeMfClassic4k_4b] = + { + .name = "Mifare Classic 4k 4byte UID", + .handler = nfc_generate_mf_classic_4k_4b_uid, + }, + [NfcDataGeneratorTypeMfClassic4k_7b] = + { + .name = "Mifare Classic 4k 7byte UID", + .handler = nfc_generate_mf_classic_4k_7b_uid, + }, +}; + +const char* nfc_data_generator_get_name(NfcDataGeneratorType type) { + return nfc_data_generator[type].name; +} + +void nfc_data_generator_fill_data(NfcDataGeneratorType type, NfcDevice* nfc_device) { + nfc_data_generator[type].handler(nfc_device); +} diff --git a/lib/nfc/helpers/nfc_data_generator.h b/lib/nfc/helpers/nfc_data_generator.h new file mode 100644 index 00000000000..01f5dac16e5 --- /dev/null +++ b/lib/nfc/helpers/nfc_data_generator.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + NfcDataGeneratorTypeMfUltralight, + NfcDataGeneratorTypeMfUltralightEV1_11, + NfcDataGeneratorTypeMfUltralightEV1_H11, + NfcDataGeneratorTypeMfUltralightEV1_21, + NfcDataGeneratorTypeMfUltralightEV1_H21, + NfcDataGeneratorTypeNTAG203, + NfcDataGeneratorTypeNTAG213, + NfcDataGeneratorTypeNTAG215, + NfcDataGeneratorTypeNTAG216, + NfcDataGeneratorTypeNTAGI2C1k, + NfcDataGeneratorTypeNTAGI2C2k, + NfcDataGeneratorTypeNTAGI2CPlus1k, + NfcDataGeneratorTypeNTAGI2CPlus2k, + + NfcDataGeneratorTypeMfClassicMini, + NfcDataGeneratorTypeMfClassic1k_4b, + NfcDataGeneratorTypeMfClassic1k_7b, + NfcDataGeneratorTypeMfClassic4k_4b, + NfcDataGeneratorTypeMfClassic4k_7b, + + NfcDataGeneratorTypeNum, + +} NfcDataGeneratorType; + +const char* nfc_data_generator_get_name(NfcDataGeneratorType type); + +void nfc_data_generator_fill_data(NfcDataGeneratorType type, NfcDevice* nfc_device); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/helpers/nfc_debug_pcap.c b/lib/nfc/helpers/nfc_debug_pcap.c deleted file mode 100644 index 48d72bfbfaf..00000000000 --- a/lib/nfc/helpers/nfc_debug_pcap.c +++ /dev/null @@ -1,166 +0,0 @@ -#include "nfc_debug_pcap.h" - -#include -#include - -#define TAG "NfcDebugPcap" - -#define PCAP_MAGIC 0xa1b2c3d4 -#define PCAP_MAJOR 2 -#define PCAP_MINOR 4 -#define DLT_ISO_14443 264 - -#define DATA_PICC_TO_PCD 0xFF -#define DATA_PCD_TO_PICC 0xFE -#define DATA_PICC_TO_PCD_CRC_DROPPED 0xFB -#define DATA_PCD_TO_PICC_CRC_DROPPED 0xFA - -#define NFC_DEBUG_PCAP_FILENAME EXT_PATH("nfc/debug.pcap") -#define NFC_DEBUG_PCAP_BUFFER_SIZE 64 - -struct NfcDebugPcapWorker { - bool alive; - Storage* storage; - File* file; - StreamBufferHandle_t stream; - FuriThread* thread; -}; - -static File* nfc_debug_pcap_open(Storage* storage) { - File* file = storage_file_alloc(storage); - if(!storage_file_open(file, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) { - storage_file_free(file); - return NULL; - } - if(!storage_file_tell(file)) { - struct { - uint32_t magic; - uint16_t major, minor; - uint32_t reserved[2]; - uint32_t snaplen; - uint32_t link_type; - } __attribute__((__packed__)) pcap_hdr = { - .magic = PCAP_MAGIC, - .major = PCAP_MAJOR, - .minor = PCAP_MINOR, - .snaplen = FURI_HAL_NFC_DATA_BUFF_SIZE, - .link_type = DLT_ISO_14443, - }; - if(storage_file_write(file, &pcap_hdr, sizeof(pcap_hdr)) != sizeof(pcap_hdr)) { - FURI_LOG_E(TAG, "Failed to write pcap header"); - } - } - return file; -} - -static void - nfc_debug_pcap_write(NfcDebugPcapWorker* instance, uint8_t event, uint8_t* data, uint16_t len) { - FuriHalRtcDateTime datetime; - furi_hal_rtc_get_datetime(&datetime); - - struct { - // https://wiki.wireshark.org/Development/LibpcapFileFormat#record-packet-header - uint32_t ts_sec; - uint32_t ts_usec; - uint32_t incl_len; - uint32_t orig_len; - // https://www.kaiser.cx/posts/pcap-iso14443/#_packet_data - uint8_t version; - uint8_t event; - uint16_t len; - } __attribute__((__packed__)) pkt_hdr = { - .ts_sec = furi_hal_rtc_datetime_to_timestamp(&datetime), - .ts_usec = 0, - .incl_len = len + 4, - .orig_len = len + 4, - .version = 0, - .event = event, - .len = len << 8 | len >> 8, - }; - xStreamBufferSend(instance->stream, &pkt_hdr, sizeof(pkt_hdr), FuriWaitForever); - xStreamBufferSend(instance->stream, data, len, FuriWaitForever); -} - -static void - nfc_debug_pcap_write_tx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) { - NfcDebugPcapWorker* instance = context; - uint8_t event = crc_dropped ? DATA_PCD_TO_PICC_CRC_DROPPED : DATA_PCD_TO_PICC; - nfc_debug_pcap_write(instance, event, data, bits / 8); -} - -static void - nfc_debug_pcap_write_rx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) { - NfcDebugPcapWorker* instance = context; - uint8_t event = crc_dropped ? DATA_PICC_TO_PCD_CRC_DROPPED : DATA_PICC_TO_PCD; - nfc_debug_pcap_write(instance, event, data, bits / 8); -} - -int32_t nfc_debug_pcap_thread(void* context) { - NfcDebugPcapWorker* instance = context; - uint8_t buffer[NFC_DEBUG_PCAP_BUFFER_SIZE]; - - while(instance->alive) { - size_t ret = - xStreamBufferReceive(instance->stream, buffer, NFC_DEBUG_PCAP_BUFFER_SIZE, 50); - if(storage_file_write(instance->file, buffer, ret) != ret) { - FURI_LOG_E(TAG, "Failed to write pcap data"); - } - } - - return 0; -} - -NfcDebugPcapWorker* nfc_debug_pcap_alloc(Storage* storage) { - NfcDebugPcapWorker* instance = malloc(sizeof(NfcDebugPcapWorker)); - - instance->alive = true; - - instance->storage = storage; - - instance->file = nfc_debug_pcap_open(storage); - - instance->stream = xStreamBufferCreate(4096, 1); - - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "PcapWorker"); - furi_thread_set_stack_size(instance->thread, 1024); - furi_thread_set_callback(instance->thread, nfc_debug_pcap_thread); - furi_thread_set_context(instance->thread, instance); - furi_thread_start(instance->thread); - - return instance; -} - -void nfc_debug_pcap_free(NfcDebugPcapWorker* instance) { - furi_assert(instance); - - instance->alive = false; - - furi_thread_join(instance->thread); - furi_thread_free(instance->thread); - - vStreamBufferDelete(instance->stream); - - if(instance->file) storage_file_free(instance->file); - - instance->storage = NULL; - - free(instance); -} - -void nfc_debug_pcap_prepare_tx_rx( - NfcDebugPcapWorker* instance, - FuriHalNfcTxRxContext* tx_rx, - bool is_picc) { - if(!instance || !instance->file) return; - - if(is_picc) { - tx_rx->sniff_tx = nfc_debug_pcap_write_rx; - tx_rx->sniff_rx = nfc_debug_pcap_write_tx; - } else { - tx_rx->sniff_tx = nfc_debug_pcap_write_tx; - tx_rx->sniff_rx = nfc_debug_pcap_write_rx; - } - - tx_rx->sniff_context = instance; -} diff --git a/lib/nfc/helpers/nfc_debug_pcap.h b/lib/nfc/helpers/nfc_debug_pcap.h deleted file mode 100644 index 6d2a449ae27..00000000000 --- a/lib/nfc/helpers/nfc_debug_pcap.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include - -typedef struct NfcDebugPcapWorker NfcDebugPcapWorker; - -NfcDebugPcapWorker* nfc_debug_pcap_alloc(Storage* storage); - -void nfc_debug_pcap_free(NfcDebugPcapWorker* instance); - -/** Prepare tx/rx context for debug pcap logging, if enabled. - * - * @param instance NfcDebugPcapWorker* instance, can be NULL - * @param tx_rx TX/RX context to log - * @param is_picc if true, record Flipper as PICC, else PCD. - */ -void nfc_debug_pcap_prepare_tx_rx( - NfcDebugPcapWorker* instance, - FuriHalNfcTxRxContext* tx_rx, - bool is_picc); diff --git a/lib/nfc/helpers/nfc_dict.c b/lib/nfc/helpers/nfc_dict.c new file mode 100644 index 00000000000..d4572a3d623 --- /dev/null +++ b/lib/nfc/helpers/nfc_dict.c @@ -0,0 +1,270 @@ +#include "nfc_dict.h" + +#include +#include +#include +#include +#include + +#include + +#define TAG "NfcDict" + +struct NfcDict { + Stream* stream; + size_t key_size; + size_t key_size_symbols; + uint32_t total_keys; +}; + +typedef struct { + const char* path; + FS_OpenMode open_mode; +} NfcDictFile; + +bool nfc_dict_check_presence(const char* path) { + furi_assert(path); + + Storage* storage = furi_record_open(RECORD_STORAGE); + + bool dict_present = storage_common_stat(storage, path, NULL) == FSE_OK; + + furi_record_close(RECORD_STORAGE); + + return dict_present; +} + +NfcDict* nfc_dict_alloc(const char* path, NfcDictMode mode, size_t key_size) { + furi_assert(path); + + NfcDict* instance = malloc(sizeof(NfcDict)); + Storage* storage = furi_record_open(RECORD_STORAGE); + instance->stream = buffered_file_stream_alloc(storage); + furi_record_close(RECORD_STORAGE); + + FS_OpenMode open_mode = FSOM_OPEN_EXISTING; + if(mode == NfcDictModeOpenAlways) { + open_mode = FSOM_OPEN_ALWAYS; + } + instance->key_size = key_size; + // Byte = 2 symbols + 1 end of line + instance->key_size_symbols = key_size * 2 + 1; + + bool dict_loaded = false; + do { + if(!buffered_file_stream_open(instance->stream, path, FSAM_READ_WRITE, open_mode)) { + buffered_file_stream_close(instance->stream); + break; + } + + // Check for new line ending + if(!stream_eof(instance->stream)) { + if(!stream_seek(instance->stream, -1, StreamOffsetFromEnd)) break; + uint8_t last_char = 0; + if(stream_read(instance->stream, &last_char, 1) != 1) break; + if(last_char != '\n') { + FURI_LOG_D(TAG, "Adding new line ending"); + if(stream_write_char(instance->stream, '\n') != 1) break; + } + if(!stream_rewind(instance->stream)) break; + } + + // Read total amount of keys + FuriString* next_line; + next_line = furi_string_alloc(); + while(true) { + if(!stream_read_line(instance->stream, next_line)) { + FURI_LOG_T(TAG, "No keys left in dict"); + break; + } + FURI_LOG_T( + TAG, + "Read line: %s, len: %zu", + furi_string_get_cstr(next_line), + furi_string_size(next_line)); + if(furi_string_get_char(next_line, 0) == '#') continue; + if(furi_string_size(next_line) != instance->key_size_symbols) continue; + instance->total_keys++; + } + furi_string_free(next_line); + stream_rewind(instance->stream); + + dict_loaded = true; + FURI_LOG_I(TAG, "Loaded dictionary with %lu keys", instance->total_keys); + } while(false); + + if(!dict_loaded) { + buffered_file_stream_close(instance->stream); + free(instance); + instance = NULL; + } + + return instance; +} + +void nfc_dict_free(NfcDict* instance) { + furi_assert(instance); + furi_assert(instance->stream); + + buffered_file_stream_close(instance->stream); + stream_free(instance->stream); + free(instance); +} + +static void nfc_dict_int_to_str(NfcDict* instance, const uint8_t* key_int, FuriString* key_str) { + furi_string_reset(key_str); + for(size_t i = 0; i < instance->key_size; i++) { + furi_string_cat_printf(key_str, "%02X", key_int[i]); + } +} + +static void nfc_dict_str_to_int(NfcDict* instance, FuriString* key_str, uint64_t* key_int) { + uint8_t key_byte_tmp; + + *key_int = 0ULL; + for(uint8_t i = 0; i < instance->key_size * 2; i += 2) { + args_char_to_hex( + furi_string_get_char(key_str, i), furi_string_get_char(key_str, i + 1), &key_byte_tmp); + *key_int |= (uint64_t)key_byte_tmp << (8 * (instance->key_size - 1 - i / 2)); + } +} + +uint32_t nfc_dict_get_total_keys(NfcDict* instance) { + furi_assert(instance); + + return instance->total_keys; +} + +bool nfc_dict_rewind(NfcDict* instance) { + furi_assert(instance); + furi_assert(instance->stream); + + return stream_rewind(instance->stream); +} + +static bool nfc_dict_get_next_key_str(NfcDict* instance, FuriString* key) { + furi_assert(instance); + furi_assert(instance->stream); + + bool key_read = false; + furi_string_reset(key); + while(!key_read) { + if(!stream_read_line(instance->stream, key)) break; + if(furi_string_get_char(key, 0) == '#') continue; + if(furi_string_size(key) != instance->key_size_symbols) continue; + furi_string_left(key, instance->key_size_symbols - 1); + key_read = true; + } + + return key_read; +} + +bool nfc_dict_get_next_key(NfcDict* instance, uint8_t* key, size_t key_size) { + furi_assert(instance); + furi_assert(instance->stream); + furi_assert(instance->key_size == key_size); + + FuriString* temp_key = furi_string_alloc(); + uint64_t key_int = 0; + bool key_read = nfc_dict_get_next_key_str(instance, temp_key); + if(key_read) { + nfc_dict_str_to_int(instance, temp_key, &key_int); + nfc_util_num2bytes(key_int, key_size, key); + } + furi_string_free(temp_key); + return key_read; +} + +static bool nfc_dict_is_key_present_str(NfcDict* instance, FuriString* key) { + furi_assert(instance); + furi_assert(instance->stream); + + FuriString* next_line; + next_line = furi_string_alloc(); + + bool key_found = false; + stream_rewind(instance->stream); + while(!key_found) { //-V654 + if(!stream_read_line(instance->stream, next_line)) break; + if(furi_string_get_char(next_line, 0) == '#') continue; + if(furi_string_size(next_line) != instance->key_size_symbols) continue; + furi_string_left(next_line, instance->key_size_symbols - 1); + if(!furi_string_equal(key, next_line)) continue; + key_found = true; + } + + furi_string_free(next_line); + return key_found; +} + +bool nfc_dict_is_key_present(NfcDict* instance, const uint8_t* key, size_t key_size) { + furi_assert(instance); + furi_assert(key); + furi_assert(instance->stream); + furi_assert(instance->key_size == key_size); + + FuriString* temp_key = furi_string_alloc(); + nfc_dict_int_to_str(instance, key, temp_key); + bool key_found = nfc_dict_is_key_present_str(instance, temp_key); + furi_string_free(temp_key); + + return key_found; +} + +static bool nfc_dict_add_key_str(NfcDict* instance, FuriString* key) { + furi_assert(instance); + furi_assert(instance->stream); + + furi_string_cat_printf(key, "\n"); + + bool key_added = false; + do { + if(!stream_seek(instance->stream, 0, StreamOffsetFromEnd)) break; + if(!stream_insert_string(instance->stream, key)) break; + instance->total_keys++; + key_added = true; + } while(false); + + furi_string_left(key, instance->key_size_symbols - 1); + return key_added; +} + +bool nfc_dict_add_key(NfcDict* instance, const uint8_t* key, size_t key_size) { + furi_assert(instance); + furi_assert(key); + furi_assert(instance->stream); + furi_assert(instance->key_size == key_size); + + FuriString* temp_key = furi_string_alloc(); + nfc_dict_int_to_str(instance, key, temp_key); + bool key_added = nfc_dict_add_key_str(instance, temp_key); + furi_string_free(temp_key); + + return key_added; +} + +bool nfc_dict_delete_key(NfcDict* instance, const uint8_t* key, size_t key_size) { + furi_assert(instance); + furi_assert(instance->stream); + furi_assert(key); + furi_assert(instance->key_size == key_size); + + bool key_removed = false; + uint8_t* temp_key = malloc(key_size); + + nfc_dict_rewind(instance); + while(!key_removed) { + if(!nfc_dict_get_next_key(instance, temp_key, key_size)) break; + if(memcmp(temp_key, key, key_size) == 0) { + int32_t offset = (-1) * (instance->key_size_symbols); + stream_seek(instance->stream, offset, StreamOffsetFromCurrent); + if(!stream_delete(instance->stream, instance->key_size_symbols)) break; + instance->total_keys--; + key_removed = true; + } + } + nfc_dict_rewind(instance); + free(temp_key); + + return key_removed; +} diff --git a/lib/nfc/helpers/nfc_dict.h b/lib/nfc/helpers/nfc_dict.h new file mode 100644 index 00000000000..80f3ff68084 --- /dev/null +++ b/lib/nfc/helpers/nfc_dict.h @@ -0,0 +1,103 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + NfcDictModeOpenExisting, + NfcDictModeOpenAlways, +} NfcDictMode; + +typedef struct NfcDict NfcDict; + +/** Check dictionary presence + * + * @param path - dictionary path + * + * @return true if dictionary exists, false otherwise +*/ +bool nfc_dict_check_presence(const char* path); + +/** Open or create dictionary + * Depending on mode, dictionary will be opened or created. + * + * @param path - dictionary path + * @param mode - NfcDictMode value + * @param key_size - size of dictionary keys in bytes + * + * @return NfcDict dictionary instance +*/ +NfcDict* nfc_dict_alloc(const char* path, NfcDictMode mode, size_t key_size); + +/** Close dictionary + * + * @param instance - NfcDict dictionary instance +*/ +void nfc_dict_free(NfcDict* instance); + +/** Get total number of keys in dictionary + * + * @param instance - NfcDict dictionary instance + * + * @return total number of keys in dictionary +*/ +uint32_t nfc_dict_get_total_keys(NfcDict* instance); + +/** Rewind dictionary + * + * @param instance - NfcDict dictionary instance + * + * @return true if rewind was successful, false otherwise +*/ +bool nfc_dict_rewind(NfcDict* instance); + +/** Check if key is present in dictionary + * + * @param instance - NfcDict dictionary instance + * @param key - key to check + * @param key_size - size of key in bytes + * + * @return true if key is present, false otherwise +*/ +bool nfc_dict_is_key_present(NfcDict* instance, const uint8_t* key, size_t key_size); + +/** Get next key from dictionary + * This function will return next key from dictionary. If there are no more + * keys, it will return false, and nfc_dict_rewind() should be called. + * + * @param instance - NfcDict dictionary instance + * @param key - buffer to store key + * @param key_size - size of key in bytes + * + * @return true if key was successfully retrieved, false otherwise +*/ +bool nfc_dict_get_next_key(NfcDict* instance, uint8_t* key, size_t key_size); + +/** Add key to dictionary + * + * @param instance - NfcDict dictionary instance + * @param key - key to add + * @param key_size - size of key in bytes + * + * @return true if key was successfully added, false otherwise +*/ +bool nfc_dict_add_key(NfcDict* instance, const uint8_t* key, size_t key_size); + +/** Delete key from dictionary + * + * @param instance - NfcDict dictionary instance + * @param key - key to delete + * @param key_size - size of key in bytes + * + * @return true if key was successfully deleted, false otherwise +*/ +bool nfc_dict_delete_key(NfcDict* instance, const uint8_t* key, size_t key_size); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/helpers/nfc_util.c b/lib/nfc/helpers/nfc_util.c new file mode 100644 index 00000000000..8cb6d57f218 --- /dev/null +++ b/lib/nfc/helpers/nfc_util.c @@ -0,0 +1,70 @@ +#include "nfc_util.h" + +#include + +static const uint8_t nfc_util_odd_byte_parity[256] = { + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, + 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, + 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, + 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, + 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, + 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, + 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1}; + +void nfc_util_num2bytes(uint64_t src, uint8_t len, uint8_t* dest) { + furi_assert(dest); + furi_assert(len <= 8); + + while(len--) { + dest[len] = (uint8_t)src; + src >>= 8; + } +} + +uint64_t nfc_util_bytes2num(const uint8_t* src, uint8_t len) { + furi_assert(src); + furi_assert(len <= 8); + + uint64_t res = 0; + while(len--) { + res = (res << 8) | (*src); + src++; + } + return res; +} + +uint8_t nfc_util_even_parity32(uint32_t data) { + // data ^= data >> 16; + // data ^= data >> 8; + // return !nfc_util_odd_byte_parity[data]; + return (__builtin_parity(data) & 0xFF); +} + +uint8_t nfc_util_odd_parity8(uint8_t data) { + return nfc_util_odd_byte_parity[data]; +} + +void nfc_util_odd_parity(const uint8_t* src, uint8_t* dst, uint8_t len) { + furi_assert(src); + furi_assert(dst); + + uint8_t parity = 0; + uint8_t bit = 0; + while(len--) { + parity |= nfc_util_odd_parity8(*src) << (7 - bit); // parity is MSB first + bit++; + if(bit == 8) { + *dst = parity; + dst++; + parity = 0; + bit = 0; + } + src++; + } + + if(bit) { + *dst = parity; + } +} \ No newline at end of file diff --git a/lib/nfc/helpers/nfc_util.h b/lib/nfc/helpers/nfc_util.h new file mode 100644 index 00000000000..a9d5a3f8ab0 --- /dev/null +++ b/lib/nfc/helpers/nfc_util.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void nfc_util_num2bytes(uint64_t src, uint8_t len, uint8_t* dest); + +uint64_t nfc_util_bytes2num(const uint8_t* src, uint8_t len); + +uint8_t nfc_util_even_parity32(uint32_t data); + +uint8_t nfc_util_odd_parity8(uint8_t data); + +void nfc_util_odd_parity(const uint8_t* src, uint8_t* dst, uint8_t len); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/nfc.c b/lib/nfc/nfc.c new file mode 100644 index 00000000000..6475cce435a --- /dev/null +++ b/lib/nfc/nfc.c @@ -0,0 +1,665 @@ +#ifndef FW_CFG_unit_tests + +#include "nfc.h" + +#include +#include + +#define TAG "Nfc" + +#define NFC_MAX_BUFFER_SIZE (256) + +typedef enum { + NfcStateIdle, + NfcStateRunning, +} NfcState; + +typedef enum { + NfcPollerStateStart, + NfcPollerStateReady, + NfcPollerStateReset, + NfcPollerStateStop, + + NfcPollerStateNum, +} NfcPollerState; + +typedef enum { + NfcCommStateIdle, + NfcCommStateWaitBlockTxTimer, + NfcCommStateReadyTx, + NfcCommStateWaitTxEnd, + NfcCommStateWaitRxStart, + NfcCommStateWaitRxEnd, + NfcCommStateFailed, +} NfcCommState; + +typedef enum { + NfcConfigurationStateIdle, + NfcConfigurationStateDone, +} NfcConfigurationState; + +struct Nfc { + NfcState state; + NfcPollerState poller_state; + NfcCommState comm_state; + NfcConfigurationState config_state; + NfcMode mode; + + uint32_t fdt_listen_fc; + uint32_t mask_rx_time_fc; + uint32_t fdt_poll_fc; + uint32_t fdt_poll_poll_us; + uint32_t guard_time_us; + NfcEventCallback callback; + void* context; + + uint8_t tx_buffer[NFC_MAX_BUFFER_SIZE]; + size_t tx_bits; + uint8_t rx_buffer[NFC_MAX_BUFFER_SIZE]; + size_t rx_bits; + + FuriThread* worker_thread; +}; + +typedef bool (*NfcWorkerPollerStateHandler)(Nfc* instance); + +static const FuriHalNfcTech nfc_tech_table[NfcModeNum][NfcTechNum] = { + [NfcModePoller] = + { + [NfcTechIso14443a] = FuriHalNfcTechIso14443a, + [NfcTechIso14443b] = FuriHalNfcTechIso14443b, + [NfcTechIso15693] = FuriHalNfcTechIso15693, + [NfcTechFelica] = FuriHalNfcTechFelica, + }, + [NfcModeListener] = + { + [NfcTechIso14443a] = FuriHalNfcTechIso14443a, + [NfcTechIso14443b] = FuriHalNfcTechInvalid, + [NfcTechIso15693] = FuriHalNfcTechIso15693, + [NfcTechFelica] = FuriHalNfcTechFelica, + }, +}; + +static NfcError nfc_process_hal_error(FuriHalNfcError error) { + NfcError ret = NfcErrorNone; + + switch(error) { + case FuriHalNfcErrorNone: + ret = NfcErrorNone; + break; + case FuriHalNfcErrorIncompleteFrame: + ret = NfcErrorIncompleteFrame; + break; + case FuriHalNfcErrorDataFormat: + ret = NfcErrorDataFormat; + break; + + default: + ret = NfcErrorInternal; + } + + return ret; +} + +static int32_t nfc_worker_listener(void* context) { + furi_assert(context); + + Nfc* instance = context; + furi_assert(instance->callback); + furi_assert(instance->config_state == NfcConfigurationStateDone); + + instance->state = NfcStateRunning; + + furi_hal_nfc_event_start(); + + NfcEventData event_data = {}; + event_data.buffer = bit_buffer_alloc(NFC_MAX_BUFFER_SIZE); + NfcEvent nfc_event = {.data = event_data}; + NfcCommand command = NfcCommandContinue; + + while(true) { + FuriHalNfcEvent event = furi_hal_nfc_listener_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER); + if(event & FuriHalNfcEventAbortRequest) { + nfc_event.type = NfcEventTypeUserAbort; + instance->callback(nfc_event, instance->context); + break; + } + if(event & FuriHalNfcEventFieldOn) { + nfc_event.type = NfcEventTypeFieldOn; + instance->callback(nfc_event, instance->context); + } + if(event & FuriHalNfcEventFieldOff) { + nfc_event.type = NfcEventTypeFieldOff; + instance->callback(nfc_event, instance->context); + furi_hal_nfc_listener_idle(); + } + if(event & FuriHalNfcEventListenerActive) { + nfc_event.type = NfcEventTypeListenerActivated; + instance->callback(nfc_event, instance->context); + } + if(event & FuriHalNfcEventRxEnd) { + furi_hal_nfc_timer_block_tx_start(instance->fdt_listen_fc); + + nfc_event.type = NfcEventTypeRxEnd; + furi_hal_nfc_listener_rx( + instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits); + bit_buffer_copy_bits(event_data.buffer, instance->rx_buffer, instance->rx_bits); + command = instance->callback(nfc_event, instance->context); + if(command == NfcCommandStop) { + break; + } else if(command == NfcCommandReset) { + furi_hal_nfc_listener_enable_rx(); + } else if(command == NfcCommandSleep) { + furi_hal_nfc_listener_sleep(); + } + } + } + + furi_hal_nfc_reset_mode(); + instance->config_state = NfcConfigurationStateIdle; + + bit_buffer_free(event_data.buffer); + furi_hal_nfc_low_power_mode_start(); + return 0; +} + +bool nfc_worker_poller_start_handler(Nfc* instance) { + furi_hal_nfc_poller_field_on(); + if(instance->guard_time_us) { + furi_hal_nfc_timer_block_tx_start_us(instance->guard_time_us); + FuriHalNfcEvent event = furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER); + furi_assert(event & FuriHalNfcEventTimerBlockTxExpired); + } + instance->poller_state = NfcPollerStateReady; + + return false; +} + +bool nfc_worker_poller_ready_handler(Nfc* instance) { + NfcCommand command = NfcCommandContinue; + + NfcEvent event = {.type = NfcEventTypePollerReady}; + command = instance->callback(event, instance->context); + if(command == NfcCommandReset) { + instance->poller_state = NfcPollerStateReset; + } else if(command == NfcCommandStop) { + instance->poller_state = NfcPollerStateStop; + } + + return false; +} + +bool nfc_worker_poller_reset_handler(Nfc* instance) { + furi_hal_nfc_low_power_mode_start(); + furi_delay_ms(100); + furi_hal_nfc_low_power_mode_stop(); + instance->poller_state = NfcPollerStateStart; + + return false; +} + +bool nfc_worker_poller_stop_handler(Nfc* instance) { + furi_hal_nfc_reset_mode(); + instance->config_state = NfcConfigurationStateIdle; + + furi_hal_nfc_low_power_mode_start(); + // Wait after field is off some time to reset tag power + furi_delay_ms(10); + instance->poller_state = NfcPollerStateStart; + + return true; +} + +static const NfcWorkerPollerStateHandler nfc_worker_poller_state_handlers[NfcPollerStateNum] = { + [NfcPollerStateStart] = nfc_worker_poller_start_handler, + [NfcPollerStateReady] = nfc_worker_poller_ready_handler, + [NfcPollerStateReset] = nfc_worker_poller_reset_handler, + [NfcPollerStateStop] = nfc_worker_poller_stop_handler, +}; + +static int32_t nfc_worker_poller(void* context) { + furi_assert(context); + + Nfc* instance = context; + furi_assert(instance->callback); + instance->state = NfcStateRunning; + instance->poller_state = NfcPollerStateStart; + + furi_hal_nfc_event_start(); + + bool exit = false; + while(!exit) { + exit = nfc_worker_poller_state_handlers[instance->poller_state](instance); + } + + return 0; +} + +Nfc* nfc_alloc() { + furi_check(furi_hal_nfc_acquire() == FuriHalNfcErrorNone); + + Nfc* instance = malloc(sizeof(Nfc)); + instance->state = NfcStateIdle; + instance->comm_state = NfcCommStateIdle; + instance->config_state = NfcConfigurationStateIdle; + + instance->worker_thread = furi_thread_alloc(); + furi_thread_set_name(instance->worker_thread, "NfcWorker"); + furi_thread_set_context(instance->worker_thread, instance); + furi_thread_set_priority(instance->worker_thread, FuriThreadPriorityHighest); + furi_thread_set_stack_size(instance->worker_thread, 8 * 1024); + + return instance; +} + +void nfc_free(Nfc* instance) { + furi_assert(instance); + furi_assert(instance->state == NfcStateIdle); + + furi_thread_free(instance->worker_thread); + free(instance); + + furi_hal_nfc_release(); +} + +void nfc_config(Nfc* instance, NfcMode mode, NfcTech tech) { + furi_assert(instance); + furi_assert(mode < NfcModeNum); + furi_assert(tech < NfcTechNum); + furi_assert(instance->config_state == NfcConfigurationStateIdle); + + FuriHalNfcTech hal_tech = nfc_tech_table[mode][tech]; + if(hal_tech == FuriHalNfcTechInvalid) { + furi_crash("Unsupported mode for given tech"); + } + FuriHalNfcMode hal_mode = (mode == NfcModePoller) ? FuriHalNfcModePoller : + FuriHalNfcModeListener; + furi_hal_nfc_low_power_mode_stop(); + furi_hal_nfc_set_mode(hal_mode, hal_tech); + + instance->mode = mode; + instance->config_state = NfcConfigurationStateDone; +} + +void nfc_set_fdt_poll_fc(Nfc* instance, uint32_t fdt_poll_fc) { + furi_assert(instance); + instance->fdt_poll_fc = fdt_poll_fc; +} + +void nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc) { + furi_assert(instance); + instance->fdt_listen_fc = fdt_listen_fc; +} + +void nfc_set_fdt_poll_poll_us(Nfc* instance, uint32_t fdt_poll_poll_us) { + furi_assert(instance); + instance->fdt_poll_poll_us = fdt_poll_poll_us; +} + +void nfc_set_guard_time_us(Nfc* instance, uint32_t guard_time_us) { + furi_assert(instance); + instance->guard_time_us = guard_time_us; +} + +void nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc) { + furi_assert(instance); + instance->mask_rx_time_fc = mask_rx_time_fc; +} + +void nfc_start(Nfc* instance, NfcEventCallback callback, void* context) { + furi_assert(instance); + furi_assert(instance->worker_thread); + furi_assert(callback); + furi_assert(instance->config_state == NfcConfigurationStateDone); + + instance->callback = callback; + instance->context = context; + if(instance->mode == NfcModePoller) { + furi_thread_set_callback(instance->worker_thread, nfc_worker_poller); + } else { + furi_thread_set_callback(instance->worker_thread, nfc_worker_listener); + } + instance->comm_state = NfcCommStateIdle; + furi_thread_start(instance->worker_thread); +} + +void nfc_stop(Nfc* instance) { + furi_assert(instance); + furi_assert(instance->state == NfcStateRunning); + + if(instance->mode == NfcModeListener) { + furi_hal_nfc_abort(); + } + furi_thread_join(instance->worker_thread); + + instance->state = NfcStateIdle; +} + +NfcError nfc_listener_tx(Nfc* instance, const BitBuffer* tx_buffer) { + furi_assert(instance); + furi_assert(tx_buffer); + + NfcError ret = NfcErrorNone; + + while(furi_hal_nfc_timer_block_tx_is_running()) { + } + + FuriHalNfcError error = + furi_hal_nfc_listener_tx(bit_buffer_get_data(tx_buffer), bit_buffer_get_size(tx_buffer)); + if(error != FuriHalNfcErrorNone) { + FURI_LOG_D(TAG, "Failed in listener TX"); + ret = nfc_process_hal_error(error); + } + + return ret; +} + +static NfcError nfc_poller_trx_state_machine(Nfc* instance, uint32_t fwt_fc) { + FuriHalNfcEvent event = 0; + NfcError error = NfcErrorNone; + + while(true) { + event = furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER); + if(event & FuriHalNfcEventTimerBlockTxExpired) { + if(instance->comm_state == NfcCommStateWaitBlockTxTimer) { + instance->comm_state = NfcCommStateReadyTx; + } + } + if(event & FuriHalNfcEventTxEnd) { + if(instance->comm_state == NfcCommStateWaitTxEnd) { + if(fwt_fc) { + furi_hal_nfc_timer_fwt_start(fwt_fc); + } + furi_hal_nfc_timer_block_tx_start_us(instance->fdt_poll_poll_us); + instance->comm_state = NfcCommStateWaitRxStart; + } + } + if(event & FuriHalNfcEventRxStart) { + if(instance->comm_state == NfcCommStateWaitRxStart) { + furi_hal_nfc_timer_block_tx_stop(); + furi_hal_nfc_timer_fwt_stop(); + instance->comm_state = NfcCommStateWaitRxEnd; + } + } + if(event & FuriHalNfcEventRxEnd) { + furi_hal_nfc_timer_block_tx_start(instance->fdt_poll_fc); + furi_hal_nfc_timer_fwt_stop(); + instance->comm_state = NfcCommStateWaitBlockTxTimer; + break; + } + if(event & FuriHalNfcEventTimerFwtExpired) { + if(instance->comm_state == NfcCommStateWaitRxStart) { + error = NfcErrorTimeout; + FURI_LOG_D(TAG, "FWT Timeout"); + if(furi_hal_nfc_timer_block_tx_is_running()) { + instance->comm_state = NfcCommStateWaitBlockTxTimer; + } else { + instance->comm_state = NfcCommStateReadyTx; + } + break; + } + } + } + + return error; +} + +NfcError nfc_iso14443a_poller_trx_custom_parity( + Nfc* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + furi_assert(tx_buffer); + furi_assert(rx_buffer); + + furi_assert(instance->poller_state == NfcPollerStateReady); + + NfcError ret = NfcErrorNone; + FuriHalNfcError error = FuriHalNfcErrorNone; + do { + furi_hal_nfc_trx_reset(); + while(furi_hal_nfc_timer_block_tx_is_running()) { + FuriHalNfcEvent event = + furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER); + if(event & FuriHalNfcEventTimerBlockTxExpired) break; + } + bit_buffer_write_bytes_with_parity( + tx_buffer, instance->tx_buffer, sizeof(instance->tx_buffer), &instance->tx_bits); + error = + furi_hal_nfc_iso14443a_poller_tx_custom_parity(instance->tx_buffer, instance->tx_bits); + if(error != FuriHalNfcErrorNone) { + FURI_LOG_D(TAG, "Failed in poller TX"); + ret = nfc_process_hal_error(error); + break; + } + instance->comm_state = NfcCommStateWaitTxEnd; + ret = nfc_poller_trx_state_machine(instance, fwt); + if(ret != NfcErrorNone) { + FURI_LOG_T(TAG, "Failed TRX state machine"); + break; + } + + error = furi_hal_nfc_poller_rx( + instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits); + if(error != FuriHalNfcErrorNone) { + FURI_LOG_D(TAG, "Failed in poller RX"); + ret = nfc_process_hal_error(error); + break; + } + if(instance->rx_bits >= 9) { + if((instance->rx_bits % 9) != 0) { + ret = NfcErrorDataFormat; + break; + } + } + + bit_buffer_copy_bytes_with_parity(rx_buffer, instance->rx_buffer, instance->rx_bits); + } while(false); + + return ret; +} + +NfcError + nfc_poller_trx(Nfc* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, uint32_t fwt) { + furi_assert(instance); + furi_assert(tx_buffer); + furi_assert(rx_buffer); + + furi_assert(instance->poller_state == NfcPollerStateReady); + + NfcError ret = NfcErrorNone; + FuriHalNfcError error = FuriHalNfcErrorNone; + do { + furi_hal_nfc_trx_reset(); + while(furi_hal_nfc_timer_block_tx_is_running()) { + FuriHalNfcEvent event = + furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER); + if(event & FuriHalNfcEventTimerBlockTxExpired) break; + } + error = + furi_hal_nfc_poller_tx(bit_buffer_get_data(tx_buffer), bit_buffer_get_size(tx_buffer)); + if(error != FuriHalNfcErrorNone) { + FURI_LOG_D(TAG, "Failed in poller TX"); + ret = nfc_process_hal_error(error); + break; + } + instance->comm_state = NfcCommStateWaitTxEnd; + ret = nfc_poller_trx_state_machine(instance, fwt); + if(ret != NfcErrorNone) { + FURI_LOG_T(TAG, "Failed TRX state machine"); + break; + } + + error = furi_hal_nfc_poller_rx( + instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits); + if(error != FuriHalNfcErrorNone) { + FURI_LOG_D(TAG, "Failed in poller RX"); + ret = nfc_process_hal_error(error); + break; + } + + bit_buffer_copy_bits(rx_buffer, instance->rx_buffer, instance->rx_bits); + } while(false); + + return ret; +} + +NfcError nfc_iso14443a_listener_set_col_res_data( + Nfc* instance, + uint8_t* uid, + uint8_t uid_len, + uint8_t* atqa, + uint8_t sak) { + furi_assert(instance); + + FuriHalNfcError error = + furi_hal_nfc_iso14443a_listener_set_col_res_data(uid, uid_len, atqa, sak); + instance->comm_state = NfcCommStateIdle; + return nfc_process_hal_error(error); +} + +NfcError nfc_iso14443a_poller_trx_short_frame( + Nfc* instance, + NfcIso14443aShortFrame frame, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + furi_assert(rx_buffer); + + FuriHalNfcaShortFrame short_frame = (frame == NfcIso14443aShortFrameAllReqa) ? + FuriHalNfcaShortFrameAllReq : + FuriHalNfcaShortFrameSensReq; + + furi_assert(instance->poller_state == NfcPollerStateReady); + + NfcError ret = NfcErrorNone; + FuriHalNfcError error = FuriHalNfcErrorNone; + do { + furi_hal_nfc_trx_reset(); + while(furi_hal_nfc_timer_block_tx_is_running()) { + FuriHalNfcEvent event = + furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER); + if(event & FuriHalNfcEventTimerBlockTxExpired) break; + } + error = furi_hal_nfc_iso14443a_poller_trx_short_frame(short_frame); + if(error != FuriHalNfcErrorNone) { + FURI_LOG_D(TAG, "Failed in poller TX"); + ret = nfc_process_hal_error(error); + break; + } + instance->comm_state = NfcCommStateWaitTxEnd; + ret = nfc_poller_trx_state_machine(instance, fwt); + if(ret != NfcErrorNone) { + FURI_LOG_T(TAG, "Failed TRX state machine"); + break; + } + + error = furi_hal_nfc_poller_rx( + instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits); + if(error != FuriHalNfcErrorNone) { + FURI_LOG_D(TAG, "Failed in poller RX"); + ret = nfc_process_hal_error(error); + break; + } + + bit_buffer_copy_bits(rx_buffer, instance->rx_buffer, instance->rx_bits); + } while(false); + + return ret; +} + +NfcError nfc_iso14443a_poller_trx_sdd_frame( + Nfc* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + furi_assert(tx_buffer); + furi_assert(rx_buffer); + + furi_assert(instance->poller_state == NfcPollerStateReady); + + NfcError ret = NfcErrorNone; + FuriHalNfcError error = FuriHalNfcErrorNone; + do { + furi_hal_nfc_trx_reset(); + while(furi_hal_nfc_timer_block_tx_is_running()) { + FuriHalNfcEvent event = + furi_hal_nfc_poller_wait_event(FURI_HAL_NFC_EVENT_WAIT_FOREVER); + if(event & FuriHalNfcEventTimerBlockTxExpired) break; + } + error = furi_hal_nfc_iso14443a_tx_sdd_frame( + bit_buffer_get_data(tx_buffer), bit_buffer_get_size(tx_buffer)); + if(error != FuriHalNfcErrorNone) { + FURI_LOG_D(TAG, "Failed in poller TX"); + ret = nfc_process_hal_error(error); + break; + } + instance->comm_state = NfcCommStateWaitTxEnd; + ret = nfc_poller_trx_state_machine(instance, fwt); + if(ret != NfcErrorNone) { + FURI_LOG_T(TAG, "Failed TRX state machine"); + break; + } + + error = furi_hal_nfc_poller_rx( + instance->rx_buffer, sizeof(instance->rx_buffer), &instance->rx_bits); + if(error != FuriHalNfcErrorNone) { + FURI_LOG_D(TAG, "Failed in poller RX"); + ret = nfc_process_hal_error(error); + break; + } + + bit_buffer_copy_bits(rx_buffer, instance->rx_buffer, instance->rx_bits); + } while(false); + + return ret; +} + +NfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer* tx_buffer) { + furi_assert(instance); + furi_assert(tx_buffer); + + NfcError ret = NfcErrorNone; + FuriHalNfcError error = FuriHalNfcErrorNone; + + const uint8_t* tx_data = bit_buffer_get_data(tx_buffer); + const uint8_t* tx_parity = bit_buffer_get_parity(tx_buffer); + size_t tx_bits = bit_buffer_get_size(tx_buffer); + + error = furi_hal_nfc_iso14443a_listener_tx_custom_parity(tx_data, tx_parity, tx_bits); + ret = nfc_process_hal_error(error); + + return ret; +} + +NfcError nfc_iso15693_listener_tx_sof(Nfc* instance) { + furi_assert(instance); + + while(furi_hal_nfc_timer_block_tx_is_running()) { + } + + FuriHalNfcError error = furi_hal_nfc_iso15693_listener_tx_sof(); + NfcError ret = nfc_process_hal_error(error); + + return ret; +} + +NfcError nfc_felica_listener_set_sensf_res_data( + Nfc* instance, + const uint8_t* idm, + const uint8_t idm_len, + const uint8_t* pmm, + const uint8_t pmm_len) { + furi_assert(instance); + furi_assert(idm); + furi_assert(pmm); + + FuriHalNfcError error = + furi_hal_nfc_felica_listener_set_sensf_res_data(idm, idm_len, pmm, pmm_len); + instance->comm_state = NfcCommStateIdle; + return nfc_process_hal_error(error); +} + +#endif // APP_UNIT_TESTS diff --git a/lib/nfc/nfc.h b/lib/nfc/nfc.h new file mode 100644 index 00000000000..4f7980b0260 --- /dev/null +++ b/lib/nfc/nfc.h @@ -0,0 +1,383 @@ +/** + * @file nfc.h + * @brief Transport layer Nfc library. + * + * The Nfc layer is responsible for setting the operating mode (poller or listener) + * and technology (ISO14443-3A/B, ISO15693, ...), data exchange between higher + * protocol-specific levels and underlying NFC hardware, as well as timings handling. + * + * In applications using the NFC protocol system there is no need to neiter explicitly + * create an Nfc instance nor call any of its functions, as it is all handled + * automatically under the hood. + * + * If the NFC protocol system is not suitable for the application's intended purpose + * or there is need of having direct access to the NFC transport layer, then an Nfc + * instance must be allocated and the below functions shall be used to implement + * the required logic. + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Nfc opaque type definition. + */ +typedef struct Nfc Nfc; + +/** + * @brief Enumeration of possible Nfc event types. + * + * Not all technologies implement all events (this is due to hardware limitations). + */ +typedef enum { + NfcEventTypeUserAbort, /**< User code explicitly aborted the current operation. */ + NfcEventTypeFieldOn, /**< Reader's field was detected by the NFC hardware. */ + NfcEventTypeFieldOff, /**< Reader's field was lost. */ + NfcEventTypeTxStart, /**< Data transmission has started. */ + NfcEventTypeTxEnd, /**< Data transmission has ended. */ + NfcEventTypeRxStart, /**< Data reception has started. */ + NfcEventTypeRxEnd, /**< Data reception has ended. */ + + NfcEventTypeListenerActivated, /**< The listener has been activated by the reader. */ + NfcEventTypePollerReady, /**< The card has been activated by the poller. */ +} NfcEventType; + +/** + * @brief Nfc event data structure. + */ +typedef struct { + BitBuffer* buffer; /**< Pointer to the received data buffer. */ +} NfcEventData; + +/** + * @brief Nfc event structure. + * + * Upon emission of an event, an instance of this struct will be passed to the callback. + */ +typedef struct { + NfcEventType type; /**< Type of the emitted event. */ + NfcEventData data; /**< Event-specific data. */ +} NfcEvent; + +/** + * @brief Enumeration of possible Nfc commands. + * + * The event callback must return one of these to determine the next action. + */ +typedef enum { + NfcCommandContinue, /**< Continue operation normally. */ + NfcCommandReset, /**< Reset the current state. */ + NfcCommandStop, /**< Stop the current operation. */ + NfcCommandSleep, /**< Switch Nfc hardware to low-power mode. */ +} NfcCommand; + +/** + * @brief Nfc event callback type. + * + * A function of this type must be passed as the callback parameter upon start of a an Nfc instance. + * + * @param [in] event Nfc event, passed by value, complete with protocol type and data. + * @param [in,out] context pointer to the user-specific context (set when starting an Nfc instance). + * @returns command which the event producer must execute. + */ +typedef NfcCommand (*NfcEventCallback)(NfcEvent event, void* context); + +/** + * @brief Enumeration of possible operating modes. + * + * Not all technologies implement the listener operating mode. + */ +typedef enum { + NfcModePoller, /**< Configure the Nfc instance as a poller. */ + NfcModeListener, /**< Configure the Nfc instance as a listener. */ + + NfcModeNum, /**< Operating mode count. Internal use. */ +} NfcMode; + +/** + * @brief Enumeration of available technologies. + */ +typedef enum { + NfcTechIso14443a, /**< Configure the Nfc instance to use the ISO14443-3A technology. */ + NfcTechIso14443b, /**< Configure the Nfc instance to use the ISO14443-3B technology. */ + NfcTechIso15693, /**< Configure the Nfc instance to use the ISO15693 technology. */ + NfcTechFelica, /**< Configure the Nfc instance to use the FeliCa technology. */ + + NfcTechNum, /**< Technologies count. Internal use. */ +} NfcTech; + +/** + * @brief Enumeration of possible Nfc error codes. + */ +typedef enum { + NfcErrorNone, /**< No error has occurred. */ + NfcErrorInternal, /**< An unknown error has occured on the lower level. */ + NfcErrorTimeout, /**< Operation is taking too long (e.g. card does not respond). */ + NfcErrorIncompleteFrame, /**< An incomplete data frame has been received. */ + NfcErrorDataFormat, /**< Data has not been parsed due to wrong/unknown format. */ +} NfcError; + +/** + * @brief Allocate an Nfc instance. + * + * Will exclusively take over the NFC HAL until deleted. + * + * @returns pointer to the allocated Nfc instance. + */ +Nfc* nfc_alloc(); + +/** + * @brief Delete an Nfc instance. + * + * Will release the NFC HAL lock, making it available for use by others. + * + * @param[in,out] instance pointer to the instance to be deleted. + */ +void nfc_free(Nfc* instance); + +/** + * @brief Configure the Nfc instance to work in a particular mode. + * + * Not all technologies implement the listener operating mode. + * + * @param[in,out] instance pointer to the instance to be configured. + * @param[in] mode required operating mode. + * @param[in] tech required technology configuration. + */ +void nfc_config(Nfc* instance, NfcMode mode, NfcTech tech); + +/** + * @brief Set poller frame delay time. + * + * @param[in,out] instance pointer to the instance to be modified. + * @param[in] fdt_poll_fc frame delay time, in carrier cycles. +*/ +void nfc_set_fdt_poll_fc(Nfc* instance, uint32_t fdt_poll_fc); + +/** + * @brief Set listener frame delay time. + * + * @param[in,out] instance pointer to the instance to be modified. + * @param[in] fdt_listen_fc frame delay time, in carrier cycles. + */ +void nfc_set_fdt_listen_fc(Nfc* instance, uint32_t fdt_listen_fc); + +/** + * @brief Set mask receive time. + * + * @param[in,out] instance pointer to the instance to be modified. + * @param[in] mask_rx_time mask receive time, in carrier cycles. + */ +void nfc_set_mask_receive_time_fc(Nfc* instance, uint32_t mask_rx_time_fc); + +/** + * @brief Set frame delay time. + * + * Frame delay time is the minimum time between two consecutive poll frames. + * + * @param[in,out] instance pointer to the instance to be modified. + * @param[in] fdt_poll_poll_us frame delay time, in microseconds. + */ +void nfc_set_fdt_poll_poll_us(Nfc* instance, uint32_t fdt_poll_poll_us); + +/** + * @brief Set guard time. + * + * @param[in,out] instance pointer to the instance to be modified. + * @param[in] guard_time_us guard time, in microseconds. + */ +void nfc_set_guard_time_us(Nfc* instance, uint32_t guard_time_us); + +/** + * @brief Start the Nfc instance. + * + * The instance must be configured to work with a specific technology + * in a specific operating mode with a nfc_config() call before starting. + * + * Once started, the user code will be receiving events through the provided + * callback which must handle them according to the logic required. + * + * @param[in,out] instance pointer to the instance to be started. + * @param[in] callback pointer to a user-defined callback function which will receive events. + * @param[in] context pointer to a user-specific context (will be passed to the callback). + */ +void nfc_start(Nfc* instance, NfcEventCallback callback, void* context); + +/** + * @brief Stop Nfc instance. + * + * The instance can only be stopped if it is running. + * + * @param[in,out] instance pointer to the instance to be stopped. + */ +void nfc_stop(Nfc* instance); + +/** + * @brief Transmit and receive a data frame in poller mode. + * + * The rx_buffer will be filled with any data received as a response to data + * sent from tx_buffer, with a timeout defined by the fwt parameter. + * + * The data being transmitted and received may be either bit- or byte-oriented. + * It shall not contain any technology-specific sequences as start or stop bits + * and/or other special symbols, as this is handled on the underlying HAL level. + * + * Must ONLY be used inside the callback function. + * + * @param[in,out] instance pointer to the instance to be used in the transaction. + * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted. + * @param[out] rx_buffer pointer to the buffer to be filled with received data. + * @param[in] fwt frame wait time (response timeout), in carrier cycles. + * @returns NfcErrorNone on success, any other error code on failure. + */ +NfcError + nfc_poller_trx(Nfc* instance, const BitBuffer* tx_buffer, BitBuffer* rx_buffer, uint32_t fwt); + +/** + * @brief Transmit a data frame in listener mode. + * + * Used to transmit a response to the reader request in listener mode. + * + * The data being transmitted may be either bit- or byte-oriented. + * It shall not contain any technology-specific sequences as start or stop bits + * and/or other special symbols, as this is handled on the underlying HAL level. + * + * Must ONLY be used inside the callback function. + * + * @param[in,out] instance pointer to the instance to be used in the transaction. + * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted. + * @returns NfcErrorNone on success, any other error code on failure. + */ +NfcError nfc_listener_tx(Nfc* instance, const BitBuffer* tx_buffer); + +/* + * Technology-specific functions. + * + * In a perfect world, this would not be necessary. + * However, the current implementation employs NFC hardware that partially implements + * certain protocols (e.g. ISO14443-3A), thus requiring methods to access such features. + */ + +/******************* ISO14443-3A specific API *******************/ + +/** + * @brief Enumeration of possible ISO14443-3A short frame types. + */ +typedef enum { + NfcIso14443aShortFrameSensReq, + NfcIso14443aShortFrameAllReqa, +} NfcIso14443aShortFrame; + +/** + * @brief Transmit an ISO14443-3A short frame and receive the response in poller mode. + * + * @param[in,out] instance pointer to the instance to be used in the transaction. + * @param[in] frame type of short frame to be sent. + * @param[out] rx_buffer pointer to the buffer to be filled with received data. + * @param[in] fwt frame wait time (response timeout), in carrier cycles. + * @returns NfcErrorNone on success, any other error code on failure. + */ +NfcError nfc_iso14443a_poller_trx_short_frame( + Nfc* instance, + NfcIso14443aShortFrame frame, + BitBuffer* rx_buffer, + uint32_t fwt); + +/** + * @brief Transmit an ISO14443-3A SDD frame and receive the response in poller mode. + * + * @param[in,out] instance pointer to the instance to be used in the transaction. + * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted. + * @param[out] rx_buffer pointer to the buffer to be filled with received data. + * @param[in] fwt frame wait time (response timeout), in carrier cycles. + * @returns NfcErrorNone on success, any other error code on failure. + */ +NfcError nfc_iso14443a_poller_trx_sdd_frame( + Nfc* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt); + +/** + * @brief Transmit an ISO14443-3A data frame with custom parity bits and receive the response in poller mode. + * + * Same as nfc_poller_trx(), but uses the parity bits provided by the user code + * instead of calculating them automatically. + * + * @param[in,out] instance pointer to the instance to be used in the transaction. + * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted. + * @param[out] rx_buffer pointer to the buffer to be filled with received data. + * @param[in] fwt frame wait time (response timeout), in carrier cycles. + * @returns NfcErrorNone on success, any other error code on failure. + */ +NfcError nfc_iso14443a_poller_trx_custom_parity( + Nfc* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt); + +/** + * @brief Transmit an ISO14443-3A frame with custom parity bits in listener mode. + * + * Same as nfc_listener_tx(), but uses the parity bits provided by the user code + * instead of calculating them automatically. + * + * @param[in,out] instance pointer to the instance to be used in the transaction. + * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted. + * @returns NfcErrorNone on success, any other error code on failure. + */ +NfcError nfc_iso14443a_listener_tx_custom_parity(Nfc* instance, const BitBuffer* tx_buffer); + +/** + * @brief Set ISO14443-3A collision resolution parameters in listener mode. + * + * Configures the NFC hardware for automatic collision resolution. + * + * @param[in,out] instance pointer to the instance to be configured. + * @param[in] uid pointer to a byte array containing the UID. + * @param[in] uid_len UID length in bytes (must be supported by the protocol). + * @param[in] atqa ATQA byte value. + * @param[in] sak SAK byte value. + * @returns NfcErrorNone on success, any other error code on failure. + */ +NfcError nfc_iso14443a_listener_set_col_res_data( + Nfc* instance, + uint8_t* uid, + uint8_t uid_len, + uint8_t* atqa, + uint8_t sak); + +/** + * @brief Set FeliCa collision resolution parameters in listener mode. + * + * Configures the NFC hardware for automatic collision resolution. + * + * @param[in,out] instance pointer to the instance to be configured. + * @param[in] idm pointer to a byte array containing the IDm. + * @param[in] idm_len IDm length in bytes. + * @param[in] pmm pointer to a byte array containing the PMm. + * @param[in] pmm_len PMm length in bytes. + * @returns NfcErrorNone on success, any other error code on failure. +*/ +NfcError nfc_felica_listener_set_sensf_res_data( + Nfc* instance, + const uint8_t* idm, + const uint8_t idm_len, + const uint8_t* pmm, + const uint8_t pmm_len); + +/** + * @brief Send ISO15693 Start of Frame pattern in listener mode + * + * @param[in,out] instance pointer to the instance to be configured. + * @returns NfcErrorNone on success, any other error code on failure. + */ +NfcError nfc_iso15693_listener_tx_sof(Nfc* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/nfc_common.h b/lib/nfc/nfc_common.h new file mode 100644 index 00000000000..c3cccf697d3 --- /dev/null +++ b/lib/nfc/nfc_common.h @@ -0,0 +1,22 @@ +/** + * @file nfc_common.h + * @brief Various common NFC-related macros. + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/* NFC file format version which changed ATQA format. Deprecated. */ +#define NFC_LSB_ATQA_FORMAT_VERSION (2) +/* NFC file format version which is still supported as backwards compatible. */ +#define NFC_MINIMUM_SUPPORTED_FORMAT_VERSION NFC_LSB_ATQA_FORMAT_VERSION +/* NFC file format version which implemented the unified loading process. */ +#define NFC_UNIFIED_FORMAT_VERSION (4) +/* Current NFC file format version. */ +#define NFC_CURRENT_FORMAT_VERSION NFC_UNIFIED_FORMAT_VERSION + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 0bfdb3dacae..267824d16ec 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -1,1298 +1,356 @@ -#include "nfc_device.h" -#include "assets_icons.h" -#include "m-string.h" -#include "nfc_types.h" - -#include -#include -#include +#include "nfc_device_i.h" + +#include #include -#define NFC_DEVICE_KEYS_FOLDER EXT_PATH("nfc/cache") -#define NFC_DEVICE_KEYS_EXTENSION ".keys" +#include "nfc_common.h" +#include "protocols/nfc_device_defs.h" -static const char* nfc_file_header = "Flipper NFC device"; -static const uint32_t nfc_file_version = 2; +#define NFC_FILE_HEADER "Flipper NFC device" +#define NFC_DEV_TYPE_ERROR "Protocol type mismatch" -static const char* nfc_keys_file_header = "Flipper NFC keys"; -static const uint32_t nfc_keys_file_version = 1; +#define NFC_DEVICE_UID_KEY "UID" +#define NFC_DEVICE_TYPE_KEY "Device type" -// Protocols format versions -static const uint32_t nfc_mifare_classic_data_format_version = 2; -static const uint32_t nfc_mifare_ultralight_data_format_version = 1; +#define NFC_DEVICE_UID_MAX_LEN (10U) NfcDevice* nfc_device_alloc() { - NfcDevice* nfc_dev = malloc(sizeof(NfcDevice)); - nfc_dev->storage = furi_record_open(RECORD_STORAGE); - nfc_dev->dialogs = furi_record_open(RECORD_DIALOGS); - string_init(nfc_dev->load_path); - string_init(nfc_dev->dev_data.parsed_data); - return nfc_dev; -} + NfcDevice* instance = malloc(sizeof(NfcDevice)); + instance->protocol = NfcProtocolInvalid; -void nfc_device_free(NfcDevice* nfc_dev) { - furi_assert(nfc_dev); - nfc_device_clear(nfc_dev); - furi_record_close(RECORD_STORAGE); - furi_record_close(RECORD_DIALOGS); - string_clear(nfc_dev->load_path); - string_clear(nfc_dev->dev_data.parsed_data); - free(nfc_dev); + return instance; } -static void nfc_device_prepare_format_string(NfcDevice* dev, string_t format_string) { - if(dev->format == NfcDeviceSaveFormatUid) { - string_set_str(format_string, "UID"); - } else if(dev->format == NfcDeviceSaveFormatBankCard) { - string_set_str(format_string, "Bank card"); - } else if(dev->format == NfcDeviceSaveFormatMifareUl) { - string_set_str(format_string, nfc_mf_ul_type(dev->dev_data.mf_ul_data.type, true)); - } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { - string_set_str(format_string, "Mifare Classic"); - } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { - string_set_str(format_string, "Mifare DESFire"); - } else { - string_set_str(format_string, "Unknown"); - } +void nfc_device_free(NfcDevice* instance) { + furi_assert(instance); + + nfc_device_clear(instance); + free(instance); } -static bool nfc_device_parse_format_string(NfcDevice* dev, string_t format_string) { - if(string_start_with_str_p(format_string, "UID")) { - dev->format = NfcDeviceSaveFormatUid; - dev->dev_data.protocol = NfcDeviceProtocolUnknown; - return true; - } - if(string_start_with_str_p(format_string, "Bank card")) { - dev->format = NfcDeviceSaveFormatBankCard; - dev->dev_data.protocol = NfcDeviceProtocolEMV; - return true; - } - // Check Mifare Ultralight types - for(MfUltralightType type = MfUltralightTypeUnknown; type < MfUltralightTypeNum; type++) { - if(string_equal_str_p(format_string, nfc_mf_ul_type(type, true))) { - dev->format = NfcDeviceSaveFormatMifareUl; - dev->dev_data.protocol = NfcDeviceProtocolMifareUl; - dev->dev_data.mf_ul_data.type = type; - return true; +void nfc_device_clear(NfcDevice* instance) { + furi_assert(instance); + + if(instance->protocol == NfcProtocolInvalid) { + furi_assert(instance->protocol_data == NULL); + } else if(instance->protocol < NfcProtocolNum) { + if(instance->protocol_data) { + nfc_devices[instance->protocol]->free(instance->protocol_data); + instance->protocol_data = NULL; } + instance->protocol = NfcProtocolInvalid; } - if(string_start_with_str_p(format_string, "Mifare Classic")) { - dev->format = NfcDeviceSaveFormatMifareClassic; - dev->dev_data.protocol = NfcDeviceProtocolMifareClassic; - return true; - } - if(string_start_with_str_p(format_string, "Mifare DESFire")) { - dev->format = NfcDeviceSaveFormatMifareDesfire; - dev->dev_data.protocol = NfcDeviceProtocolMifareDesfire; - return true; - } - return false; } -static bool nfc_device_save_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { - bool saved = false; - MfUltralightData* data = &dev->dev_data.mf_ul_data; - string_t temp_str; - string_init(temp_str); +void nfc_device_reset(NfcDevice* instance) { + furi_assert(instance); + furi_assert(instance->protocol < NfcProtocolNum); - // Save Mifare Ultralight specific data - do { - if(!flipper_format_write_comment_cstr(file, "Mifare Ultralight specific data")) break; - if(!flipper_format_write_uint32( - file, "Data format version", &nfc_mifare_ultralight_data_format_version, 1)) - break; - if(!flipper_format_write_hex(file, "Signature", data->signature, sizeof(data->signature))) - break; - if(!flipper_format_write_hex( - file, "Mifare version", (uint8_t*)&data->version, sizeof(data->version))) - break; - // Write conters and tearing flags data - bool counters_saved = true; - for(uint8_t i = 0; i < 3; i++) { - string_printf(temp_str, "Counter %d", i); - if(!flipper_format_write_uint32( - file, string_get_cstr(temp_str), &data->counter[i], 1)) { - counters_saved = false; - break; - } - string_printf(temp_str, "Tearing %d", i); - if(!flipper_format_write_hex(file, string_get_cstr(temp_str), &data->tearing[i], 1)) { - counters_saved = false; - break; - } - } - if(!counters_saved) break; - // Write pages data - uint32_t pages_total = data->data_size / 4; - if(!flipper_format_write_uint32(file, "Pages total", &pages_total, 1)) break; - uint32_t pages_read = data->data_read / 4; - if(!flipper_format_write_uint32(file, "Pages read", &pages_read, 1)) break; - bool pages_saved = true; - for(uint16_t i = 0; i < data->data_size; i += 4) { - string_printf(temp_str, "Page %d", i / 4); - if(!flipper_format_write_hex(file, string_get_cstr(temp_str), &data->data[i], 4)) { - pages_saved = false; - break; - } - } - if(!pages_saved) break; - - // Write authentication counter - uint32_t auth_counter = data->curr_authlim; - if(!flipper_format_write_uint32(file, "Failed authentication attempts", &auth_counter, 1)) - break; + if(instance->protocol_data) { + nfc_devices[instance->protocol]->reset(instance->protocol_data); + } +} - saved = true; - } while(false); +NfcProtocol nfc_device_get_protocol(const NfcDevice* instance) { + furi_assert(instance); + return instance->protocol; +} - string_clear(temp_str); - return saved; +const NfcDeviceData* nfc_device_get_data(const NfcDevice* instance, NfcProtocol protocol) { + return nfc_device_get_data_ptr(instance, protocol); } -bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { - bool parsed = false; - MfUltralightData* data = &dev->dev_data.mf_ul_data; - string_t temp_str; - string_init(temp_str); - uint32_t data_format_version = 0; +const char* nfc_device_get_protocol_name(NfcProtocol protocol) { + furi_assert(protocol < NfcProtocolNum); - do { - // Read Mifare Ultralight format version - if(!flipper_format_read_uint32(file, "Data format version", &data_format_version, 1)) { - if(!flipper_format_rewind(file)) break; - } + return nfc_devices[protocol]->protocol_name; +} - // Read signature - if(!flipper_format_read_hex(file, "Signature", data->signature, sizeof(data->signature))) - break; - // Read Mifare version - if(!flipper_format_read_hex( - file, "Mifare version", (uint8_t*)&data->version, sizeof(data->version))) - break; - // Read counters and tearing flags - bool counters_parsed = true; - for(uint8_t i = 0; i < 3; i++) { - string_printf(temp_str, "Counter %d", i); - if(!flipper_format_read_uint32(file, string_get_cstr(temp_str), &data->counter[i], 1)) { - counters_parsed = false; - break; - } - string_printf(temp_str, "Tearing %d", i); - if(!flipper_format_read_hex(file, string_get_cstr(temp_str), &data->tearing[i], 1)) { - counters_parsed = false; - break; - } - } - if(!counters_parsed) break; - // Read pages - uint32_t pages_total = 0; - if(!flipper_format_read_uint32(file, "Pages total", &pages_total, 1)) break; - uint32_t pages_read = 0; - if(data_format_version < nfc_mifare_ultralight_data_format_version) { - pages_read = pages_total; - } else { - if(!flipper_format_read_uint32(file, "Pages read", &pages_read, 1)) break; - } - data->data_size = pages_total * 4; - data->data_read = pages_read * 4; - bool pages_parsed = true; - for(uint16_t i = 0; i < pages_total; i++) { - string_printf(temp_str, "Page %d", i); - if(!flipper_format_read_hex(file, string_get_cstr(temp_str), &data->data[i * 4], 4)) { - pages_parsed = false; - break; - } - } - if(!pages_parsed) break; +const char* nfc_device_get_name(const NfcDevice* instance, NfcDeviceNameType name_type) { + furi_assert(instance); + furi_assert(instance->protocol < NfcProtocolNum); - // Read authentication counter - uint32_t auth_counter; - if(!flipper_format_read_uint32(file, "Failed authentication attempts", &auth_counter, 1)) - auth_counter = 0; + return nfc_devices[instance->protocol]->get_name(instance->protocol_data, name_type); +} - parsed = true; - } while(false); +const uint8_t* nfc_device_get_uid(const NfcDevice* instance, size_t* uid_len) { + furi_assert(instance); + furi_assert(instance->protocol < NfcProtocolNum); - string_clear(temp_str); - return parsed; + return nfc_devices[instance->protocol]->get_uid(instance->protocol_data, uid_len); } -static bool nfc_device_save_mifare_df_key_settings( - FlipperFormat* file, - MifareDesfireKeySettings* ks, - const char* prefix) { - bool saved = false; - string_t key; - string_init(key); +bool nfc_device_set_uid(NfcDevice* instance, const uint8_t* uid, size_t uid_len) { + furi_assert(instance); + furi_assert(instance->protocol < NfcProtocolNum); - do { - string_printf(key, "%s Change Key ID", prefix); - if(!flipper_format_write_hex(file, string_get_cstr(key), &ks->change_key_id, 1)) break; - string_printf(key, "%s Config Changeable", prefix); - if(!flipper_format_write_bool(file, string_get_cstr(key), &ks->config_changeable, 1)) - break; - string_printf(key, "%s Free Create Delete", prefix); - if(!flipper_format_write_bool(file, string_get_cstr(key), &ks->free_create_delete, 1)) - break; - string_printf(key, "%s Free Directory List", prefix); - if(!flipper_format_write_bool(file, string_get_cstr(key), &ks->free_directory_list, 1)) - break; - string_printf(key, "%s Key Changeable", prefix); - if(!flipper_format_write_bool(file, string_get_cstr(key), &ks->master_key_changeable, 1)) - break; - if(ks->flags) { - string_printf(key, "%s Flags", prefix); - if(!flipper_format_write_hex(file, string_get_cstr(key), &ks->flags, 1)) break; - } - string_printf(key, "%s Max Keys", prefix); - if(!flipper_format_write_hex(file, string_get_cstr(key), &ks->max_keys, 1)) break; - for(MifareDesfireKeyVersion* kv = ks->key_version_head; kv; kv = kv->next) { - string_printf(key, "%s Key %d Version", prefix, kv->id); - if(!flipper_format_write_hex(file, string_get_cstr(key), &kv->version, 1)) break; - } - saved = true; - } while(false); - - string_clear(key); - return saved; + return nfc_devices[instance->protocol]->set_uid(instance->protocol_data, uid, uid_len); } -bool nfc_device_load_mifare_df_key_settings( - FlipperFormat* file, - MifareDesfireKeySettings* ks, - const char* prefix) { - bool parsed = false; - string_t key; - string_init(key); +void nfc_device_set_data( + NfcDevice* instance, + NfcProtocol protocol, + const NfcDeviceData* protocol_data) { + furi_assert(instance); + furi_assert(protocol < NfcProtocolNum); - do { - string_printf(key, "%s Change Key ID", prefix); - if(!flipper_format_read_hex(file, string_get_cstr(key), &ks->change_key_id, 1)) break; - string_printf(key, "%s Config Changeable", prefix); - if(!flipper_format_read_bool(file, string_get_cstr(key), &ks->config_changeable, 1)) break; - string_printf(key, "%s Free Create Delete", prefix); - if(!flipper_format_read_bool(file, string_get_cstr(key), &ks->free_create_delete, 1)) - break; - string_printf(key, "%s Free Directory List", prefix); - if(!flipper_format_read_bool(file, string_get_cstr(key), &ks->free_directory_list, 1)) - break; - string_printf(key, "%s Key Changeable", prefix); - if(!flipper_format_read_bool(file, string_get_cstr(key), &ks->master_key_changeable, 1)) - break; - string_printf(key, "%s Flags", prefix); - if(flipper_format_key_exist(file, string_get_cstr(key))) { - if(!flipper_format_read_hex(file, string_get_cstr(key), &ks->flags, 1)) break; - } - string_printf(key, "%s Max Keys", prefix); - if(!flipper_format_read_hex(file, string_get_cstr(key), &ks->max_keys, 1)) break; - ks->flags |= ks->max_keys >> 4; - ks->max_keys &= 0xF; - MifareDesfireKeyVersion** kv_head = &ks->key_version_head; - for(int key_id = 0; key_id < ks->max_keys; key_id++) { - string_printf(key, "%s Key %d Version", prefix, key_id); - uint8_t version; - if(flipper_format_read_hex(file, string_get_cstr(key), &version, 1)) { - MifareDesfireKeyVersion* kv = malloc(sizeof(MifareDesfireKeyVersion)); - memset(kv, 0, sizeof(MifareDesfireKeyVersion)); - kv->id = key_id; - kv->version = version; - *kv_head = kv; - kv_head = &kv->next; - } - } - parsed = true; - } while(false); + nfc_device_clear(instance); + + instance->protocol = protocol; + instance->protocol_data = nfc_devices[protocol]->alloc(); - string_clear(key); - return parsed; + nfc_devices[protocol]->copy(instance->protocol_data, protocol_data); } -static bool nfc_device_save_mifare_df_app(FlipperFormat* file, MifareDesfireApplication* app) { - bool saved = false; - string_t prefix, key; - string_init_printf(prefix, "Application %02x%02x%02x", app->id[0], app->id[1], app->id[2]); - string_init(key); - uint8_t* tmp = NULL; +void nfc_device_copy_data( + const NfcDevice* instance, + NfcProtocol protocol, + NfcDeviceData* protocol_data) { + furi_assert(instance); + furi_assert(protocol < NfcProtocolNum); + furi_assert(protocol_data); - do { - if(app->key_settings) { - if(!nfc_device_save_mifare_df_key_settings( - file, app->key_settings, string_get_cstr(prefix))) - break; - } - if(!app->file_head) break; - uint32_t n_files = 0; - for(MifareDesfireFile* f = app->file_head; f; f = f->next) { - n_files++; - } - tmp = malloc(n_files); - int i = 0; - for(MifareDesfireFile* f = app->file_head; f; f = f->next) { - tmp[i++] = f->id; - } - string_printf(key, "%s File IDs", string_get_cstr(prefix)); - if(!flipper_format_write_hex(file, string_get_cstr(key), tmp, n_files)) break; - bool saved_files = true; - for(MifareDesfireFile* f = app->file_head; f; f = f->next) { - saved_files = false; - string_printf(key, "%s File %d Type", string_get_cstr(prefix), f->id); - if(!flipper_format_write_hex(file, string_get_cstr(key), &f->type, 1)) break; - string_printf( - key, "%s File %d Communication Settings", string_get_cstr(prefix), f->id); - if(!flipper_format_write_hex(file, string_get_cstr(key), &f->comm, 1)) break; - string_printf(key, "%s File %d Access Rights", string_get_cstr(prefix), f->id); - if(!flipper_format_write_hex( - file, string_get_cstr(key), (uint8_t*)&f->access_rights, 2)) - break; - uint16_t size = 0; - if(f->type == MifareDesfireFileTypeStandard || - f->type == MifareDesfireFileTypeBackup) { - size = f->settings.data.size; - string_printf(key, "%s File %d Size", string_get_cstr(prefix), f->id); - if(!flipper_format_write_uint32( - file, string_get_cstr(key), &f->settings.data.size, 1)) - break; - } else if(f->type == MifareDesfireFileTypeValue) { - string_printf(key, "%s File %d Hi Limit", string_get_cstr(prefix), f->id); - if(!flipper_format_write_uint32( - file, string_get_cstr(key), &f->settings.value.hi_limit, 1)) - break; - string_printf(key, "%s File %d Lo Limit", string_get_cstr(prefix), f->id); - if(!flipper_format_write_uint32( - file, string_get_cstr(key), &f->settings.value.lo_limit, 1)) - break; - string_printf( - key, "%s File %d Limited Credit Value", string_get_cstr(prefix), f->id); - if(!flipper_format_write_uint32( - file, string_get_cstr(key), &f->settings.value.limited_credit_value, 1)) - break; - string_printf( - key, "%s File %d Limited Credit Enabled", string_get_cstr(prefix), f->id); - if(!flipper_format_write_bool( - file, string_get_cstr(key), &f->settings.value.limited_credit_enabled, 1)) - break; - size = 4; - } else if( - f->type == MifareDesfireFileTypeLinearRecord || - f->type == MifareDesfireFileTypeCyclicRecord) { - string_printf(key, "%s File %d Size", string_get_cstr(prefix), f->id); - if(!flipper_format_write_uint32( - file, string_get_cstr(key), &f->settings.record.size, 1)) - break; - string_printf(key, "%s File %d Max", string_get_cstr(prefix), f->id); - if(!flipper_format_write_uint32( - file, string_get_cstr(key), &f->settings.record.max, 1)) - break; - string_printf(key, "%s File %d Cur", string_get_cstr(prefix), f->id); - if(!flipper_format_write_uint32( - file, string_get_cstr(key), &f->settings.record.cur, 1)) - break; - size = f->settings.record.size * f->settings.record.cur; - } - if(f->contents) { - string_printf(key, "%s File %d", string_get_cstr(prefix), f->id); - if(!flipper_format_write_hex(file, string_get_cstr(key), f->contents, size)) break; - } - saved_files = true; - } - if(!saved_files) { - break; - } - saved = true; - } while(false); + if(instance->protocol != protocol) { + furi_crash(NFC_DEV_TYPE_ERROR); + } - free(tmp); - string_clear(prefix); - string_clear(key); - return saved; + nfc_devices[protocol]->copy(protocol_data, instance->protocol_data); } -bool nfc_device_load_mifare_df_app(FlipperFormat* file, MifareDesfireApplication* app) { - bool parsed = false; - string_t prefix, key; - string_init_printf(prefix, "Application %02x%02x%02x", app->id[0], app->id[1], app->id[2]); - string_init(key); - uint8_t* tmp = NULL; - MifareDesfireFile* f = NULL; - - do { - app->key_settings = malloc(sizeof(MifareDesfireKeySettings)); - memset(app->key_settings, 0, sizeof(MifareDesfireKeySettings)); - if(!nfc_device_load_mifare_df_key_settings( - file, app->key_settings, string_get_cstr(prefix))) { - free(app->key_settings); - app->key_settings = NULL; - break; - } - string_printf(key, "%s File IDs", string_get_cstr(prefix)); - uint32_t n_files; - if(!flipper_format_get_value_count(file, string_get_cstr(key), &n_files)) break; - tmp = malloc(n_files); - if(!flipper_format_read_hex(file, string_get_cstr(key), tmp, n_files)) break; - MifareDesfireFile** file_head = &app->file_head; - bool parsed_files = true; - for(uint32_t i = 0; i < n_files; i++) { - parsed_files = false; - f = malloc(sizeof(MifareDesfireFile)); - memset(f, 0, sizeof(MifareDesfireFile)); - f->id = tmp[i]; - string_printf(key, "%s File %d Type", string_get_cstr(prefix), f->id); - if(!flipper_format_read_hex(file, string_get_cstr(key), &f->type, 1)) break; - string_printf( - key, "%s File %d Communication Settings", string_get_cstr(prefix), f->id); - if(!flipper_format_read_hex(file, string_get_cstr(key), &f->comm, 1)) break; - string_printf(key, "%s File %d Access Rights", string_get_cstr(prefix), f->id); - if(!flipper_format_read_hex(file, string_get_cstr(key), (uint8_t*)&f->access_rights, 2)) - break; - if(f->type == MifareDesfireFileTypeStandard || - f->type == MifareDesfireFileTypeBackup) { - string_printf(key, "%s File %d Size", string_get_cstr(prefix), f->id); - if(!flipper_format_read_uint32( - file, string_get_cstr(key), &f->settings.data.size, 1)) - break; - } else if(f->type == MifareDesfireFileTypeValue) { - string_printf(key, "%s File %d Hi Limit", string_get_cstr(prefix), f->id); - if(!flipper_format_read_uint32( - file, string_get_cstr(key), &f->settings.value.hi_limit, 1)) - break; - string_printf(key, "%s File %d Lo Limit", string_get_cstr(prefix), f->id); - if(!flipper_format_read_uint32( - file, string_get_cstr(key), &f->settings.value.lo_limit, 1)) - break; - string_printf( - key, "%s File %d Limited Credit Value", string_get_cstr(prefix), f->id); - if(!flipper_format_read_uint32( - file, string_get_cstr(key), &f->settings.value.limited_credit_value, 1)) - break; - string_printf( - key, "%s File %d Limited Credit Enabled", string_get_cstr(prefix), f->id); - if(!flipper_format_read_bool( - file, string_get_cstr(key), &f->settings.value.limited_credit_enabled, 1)) - break; - } else if( - f->type == MifareDesfireFileTypeLinearRecord || - f->type == MifareDesfireFileTypeCyclicRecord) { - string_printf(key, "%s File %d Size", string_get_cstr(prefix), f->id); - if(!flipper_format_read_uint32( - file, string_get_cstr(key), &f->settings.record.size, 1)) - break; - string_printf(key, "%s File %d Max", string_get_cstr(prefix), f->id); - if(!flipper_format_read_uint32( - file, string_get_cstr(key), &f->settings.record.max, 1)) - break; - string_printf(key, "%s File %d Cur", string_get_cstr(prefix), f->id); - if(!flipper_format_read_uint32( - file, string_get_cstr(key), &f->settings.record.cur, 1)) - break; - } - string_printf(key, "%s File %d", string_get_cstr(prefix), f->id); - if(flipper_format_key_exist(file, string_get_cstr(key))) { - uint32_t size; - if(!flipper_format_get_value_count(file, string_get_cstr(key), &size)) break; - f->contents = malloc(size); - if(!flipper_format_read_hex(file, string_get_cstr(key), f->contents, size)) break; - } - *file_head = f; - file_head = &f->next; - f = NULL; - parsed_files = true; - } - if(!parsed_files) { - break; - } - parsed = true; - } while(false); +bool nfc_device_is_equal_data( + const NfcDevice* instance, + NfcProtocol protocol, + const NfcDeviceData* protocol_data) { + furi_assert(instance); + furi_assert(protocol < NfcProtocolNum); + furi_assert(protocol_data); - if(f) { - free(f->contents); - free(f); - } - free(tmp); - string_clear(prefix); - string_clear(key); - return parsed; + return instance->protocol == protocol && + nfc_devices[protocol]->is_equal(instance->protocol_data, protocol_data); } -static bool nfc_device_save_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { - bool saved = false; - MifareDesfireData* data = &dev->dev_data.mf_df_data; - uint8_t* tmp = NULL; +bool nfc_device_is_equal(const NfcDevice* instance, const NfcDevice* other) { + furi_assert(instance); + furi_assert(other); - do { - if(!flipper_format_write_comment_cstr(file, "Mifare DESFire specific data")) break; - if(!flipper_format_write_hex( - file, "PICC Version", (uint8_t*)&data->version, sizeof(data->version))) - break; - if(data->free_memory) { - if(!flipper_format_write_uint32(file, "PICC Free Memory", &data->free_memory->bytes, 1)) - break; - } - if(data->master_key_settings) { - if(!nfc_device_save_mifare_df_key_settings(file, data->master_key_settings, "PICC")) - break; - } - uint32_t n_apps = 0; - for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { - n_apps++; - } - if(!flipper_format_write_uint32(file, "Application Count", &n_apps, 1)) break; - if(n_apps) { - tmp = malloc(n_apps * 3); - int i = 0; - for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { - memcpy(tmp + i, app->id, 3); - i += 3; - } - if(!flipper_format_write_hex(file, "Application IDs", tmp, n_apps * 3)) break; - for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { - if(!nfc_device_save_mifare_df_app(file, app)) break; - } - } - saved = true; - } while(false); - - free(tmp); - return saved; + return nfc_device_is_equal_data(instance, other->protocol, other->protocol_data); } -bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { - bool parsed = false; - MifareDesfireData* data = &dev->dev_data.mf_df_data; - memset(data, 0, sizeof(MifareDesfireData)); - uint8_t* tmp = NULL; - - do { - if(!flipper_format_read_hex( - file, "PICC Version", (uint8_t*)&data->version, sizeof(data->version))) - break; - if(flipper_format_key_exist(file, "PICC Free Memory")) { - data->free_memory = malloc(sizeof(MifareDesfireFreeMemory)); - memset(data->free_memory, 0, sizeof(MifareDesfireFreeMemory)); - if(!flipper_format_read_uint32( - file, "PICC Free Memory", &data->free_memory->bytes, 1)) { - free(data->free_memory); - break; - } - } - if(flipper_format_key_exist(file, "PICC Change Key ID")) { - data->master_key_settings = malloc(sizeof(MifareDesfireKeySettings)); - memset(data->master_key_settings, 0, sizeof(MifareDesfireKeySettings)); - if(!nfc_device_load_mifare_df_key_settings(file, data->master_key_settings, "PICC")) { - free(data->master_key_settings); - data->master_key_settings = NULL; - break; - } - } - uint32_t n_apps; - if(!flipper_format_read_uint32(file, "Application Count", &n_apps, 1)) break; - if(n_apps) { - tmp = malloc(n_apps * 3); - if(!flipper_format_read_hex(file, "Application IDs", tmp, n_apps * 3)) break; - bool parsed_apps = true; - MifareDesfireApplication** app_head = &data->app_head; - for(uint32_t i = 0; i < n_apps; i++) { - MifareDesfireApplication* app = malloc(sizeof(MifareDesfireApplication)); - memset(app, 0, sizeof(MifareDesfireApplication)); - memcpy(app->id, &tmp[i * 3], 3); - if(!nfc_device_load_mifare_df_app(file, app)) { - free(app); - parsed_apps = false; - break; - } - *app_head = app; - app_head = &app->next; - } - if(!parsed_apps) break; - } - parsed = true; - } while(false); +void nfc_device_set_loading_callback( + NfcDevice* instance, + NfcLoadingCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); - free(tmp); - return parsed; + instance->loading_callback = callback; + instance->loading_callback_context = context; } -static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) { - bool saved = false; - EmvData* data = &dev->dev_data.emv_data; - uint32_t data_temp = 0; - - do { - // Write Bank card specific data - if(!flipper_format_write_comment_cstr(file, "Bank card specific data")) break; - if(!flipper_format_write_hex(file, "AID", data->aid, data->aid_len)) break; - if(!flipper_format_write_string_cstr(file, "Name", data->name)) break; - if(!flipper_format_write_hex(file, "Number", data->number, data->number_len)) break; - if(data->exp_mon) { - uint8_t exp_data[2] = {data->exp_mon, data->exp_year}; - if(!flipper_format_write_hex(file, "Exp data", exp_data, sizeof(exp_data))) break; - } - if(data->country_code) { - data_temp = data->country_code; - if(!flipper_format_write_uint32(file, "Country code", &data_temp, 1)) break; - } - if(data->currency_code) { - data_temp = data->currency_code; - if(!flipper_format_write_uint32(file, "Currency code", &data_temp, 1)) break; - } - saved = true; - } while(false); +bool nfc_device_save(NfcDevice* instance, const char* path) { + furi_assert(instance); + furi_assert(instance->protocol < NfcProtocolNum); + furi_assert(path); - return saved; -} + bool saved = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); + FuriString* temp_str = furi_string_alloc(); -bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) { - bool parsed = false; - EmvData* data = &dev->dev_data.emv_data; - memset(data, 0, sizeof(EmvData)); - uint32_t data_cnt = 0; - string_t temp_str; - string_init(temp_str); + if(instance->loading_callback) { + instance->loading_callback(instance->loading_callback_context, true); + } do { - // Load essential data - if(!flipper_format_get_value_count(file, "AID", &data_cnt)) break; - data->aid_len = data_cnt; - if(!flipper_format_read_hex(file, "AID", data->aid, data->aid_len)) break; - if(!flipper_format_read_string(file, "Name", temp_str)) break; - strlcpy(data->name, string_get_cstr(temp_str), sizeof(data->name)); - if(!flipper_format_get_value_count(file, "Number", &data_cnt)) break; - data->number_len = data_cnt; - if(!flipper_format_read_hex(file, "Number", data->number, data->number_len)) break; - parsed = true; - // Load optional data - uint8_t exp_data[2] = {}; - if(flipper_format_read_hex(file, "Exp data", exp_data, 2)) { - data->exp_mon = exp_data[0]; - data->exp_year = exp_data[1]; - } - if(flipper_format_read_uint32(file, "Country code", &data_cnt, 1)) { - data->country_code = data_cnt; - } - if(flipper_format_read_uint32(file, "Currency code", &data_cnt, 1)) { - data->currency_code = data_cnt; - } - } while(false); + // Open file + if(!flipper_format_buffered_file_open_always(ff, path)) break; - string_clear(temp_str); - return parsed; -} + // Write header + if(!flipper_format_write_header_cstr(ff, NFC_FILE_HEADER, NFC_CURRENT_FORMAT_VERSION)) + break; -static void nfc_device_write_mifare_classic_block( - string_t block_str, - MfClassicData* data, - uint8_t block_num) { - string_reset(block_str); - 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); - // Write key A - for(size_t i = 0; i < sizeof(sec_tr->key_a); i++) { - if(mf_classic_is_key_found(data, sector_num, MfClassicKeyA)) { - string_cat_printf(block_str, "%02X ", sec_tr->key_a[i]); - } else { - string_cat_printf(block_str, "?? "); - } - } - // Write Access bytes - for(size_t i = 0; i < MF_CLASSIC_ACCESS_BYTES_SIZE; i++) { - if(mf_classic_is_block_read(data, block_num)) { - string_cat_printf(block_str, "%02X ", sec_tr->access_bits[i]); - } else { - string_cat_printf(block_str, "?? "); - } - } - // Write key B - for(size_t i = 0; i < sizeof(sec_tr->key_b); i++) { - if(mf_classic_is_key_found(data, sector_num, MfClassicKeyB)) { - string_cat_printf(block_str, "%02X ", sec_tr->key_b[i]); - } else { - string_cat_printf(block_str, "?? "); + // Write allowed device types + furi_string_printf(temp_str, "%s can be ", NFC_DEVICE_TYPE_KEY); + for(NfcProtocol protocol = 0; protocol < NfcProtocolNum; ++protocol) { + furi_string_cat(temp_str, nfc_devices[protocol]->protocol_name); + if(protocol < NfcProtocolNum - 1) { + furi_string_cat(temp_str, ", "); } } - } else { - // Write data block - for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) { - if(mf_classic_is_block_read(data, block_num)) { - string_cat_printf(block_str, "%02X ", data->block[block_num].value[i]); - } else { - string_cat_printf(block_str, "?? "); - } - } - } - string_strim(block_str); -} -static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice* dev) { - bool saved = false; - MfClassicData* data = &dev->dev_data.mf_classic_data; - string_t temp_str; - string_init(temp_str); - uint16_t blocks = 0; + if(!flipper_format_write_comment(ff, temp_str)) break; - // Save Mifare Classic specific data - do { - if(!flipper_format_write_comment_cstr(file, "Mifare Classic specific data")) break; - - if(data->type == MfClassicType1k) { - if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "1K")) break; - blocks = 64; - } else if(data->type == MfClassicType4k) { - if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "4K")) break; - blocks = 256; - } - if(!flipper_format_write_uint32( - file, "Data format version", &nfc_mifare_classic_data_format_version, 1)) + // Write device type + if(!flipper_format_write_string_cstr( + ff, NFC_DEVICE_TYPE_KEY, nfc_devices[instance->protocol]->protocol_name)) break; - if(!flipper_format_write_comment_cstr( - file, "Mifare Classic blocks, \'??\' means unknown data")) - break; - bool block_saved = true; - string_t block_str; - string_init(block_str); - for(size_t i = 0; i < blocks; i++) { - string_printf(temp_str, "Block %d", i); - nfc_device_write_mifare_classic_block(block_str, data, i); - if(!flipper_format_write_string(file, string_get_cstr(temp_str), block_str)) { - block_saved = false; - break; - } - } - string_clear(block_str); - if(!block_saved) break; + + // Write UID + furi_string_printf(temp_str, "%s is common for all formats", NFC_DEVICE_UID_KEY); + if(!flipper_format_write_comment(ff, temp_str)) break; + + size_t uid_len; + const uint8_t* uid = nfc_device_get_uid(instance, &uid_len); + if(!flipper_format_write_hex(ff, NFC_DEVICE_UID_KEY, uid, uid_len)) break; + + // Write protocol-dependent data + if(!nfc_devices[instance->protocol]->save(instance->protocol_data, ff)) break; + saved = true; } while(false); - string_clear(temp_str); - return saved; -} - -static void nfc_device_load_mifare_classic_block( - string_t block_str, - MfClassicData* data, - uint8_t block_num) { - string_strim(block_str); - MfClassicBlock block_tmp = {}; - bool is_sector_trailer = mf_classic_is_sector_trailer(block_num); - uint8_t sector_num = mf_classic_get_sector_by_block(block_num); - uint16_t block_unknown_bytes_mask = 0; - - string_strim(block_str); - for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) { - char hi = string_get_char(block_str, 3 * i); - char low = string_get_char(block_str, 3 * i + 1); - uint8_t byte = 0; - if(hex_chars_to_uint8(hi, low, &byte)) { - block_tmp.value[i] = byte; - } else { - FURI_BIT_SET(block_unknown_bytes_mask, i); - } + if(instance->loading_callback) { + instance->loading_callback(instance->loading_callback_context, false); } - if(block_unknown_bytes_mask == 0xffff) { - // All data is unknown, exit - return; - } + furi_string_free(temp_str); + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); - if(is_sector_trailer) { - MfClassicSectorTrailer* sec_tr_tmp = (MfClassicSectorTrailer*)&block_tmp; - // Load Key A - // Key A mask 0b0000000000111111 = 0x003f - if((block_unknown_bytes_mask & 0x003f) == 0) { - uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_a, sizeof(sec_tr_tmp->key_a)); - mf_classic_set_key_found(data, sector_num, MfClassicKeyA, key); - } - // Load Access Bits - // Access bits mask 0b0000001111000000 = 0x03c0 - if((block_unknown_bytes_mask & 0x03c0) == 0) { - mf_classic_set_block_read(data, block_num, &block_tmp); - } - // Load Key B - // Key B mask 0b1111110000000000 = 0xfc00 - if((block_unknown_bytes_mask & 0xfc00) == 0) { - uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_b, sizeof(sec_tr_tmp->key_b)); - mf_classic_set_key_found(data, sector_num, MfClassicKeyB, key); - } - } else { - if(block_unknown_bytes_mask == 0) { - mf_classic_set_block_read(data, block_num, &block_tmp); - } - } + return saved; } -static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice* dev) { - bool parsed = false; - MfClassicData* data = &dev->dev_data.mf_classic_data; - string_t temp_str; - uint32_t data_format_version = 0; - string_init(temp_str); - uint16_t data_blocks = 0; - memset(data, 0, sizeof(MfClassicData)); +static bool nfc_device_load_uid( + FlipperFormat* ff, + uint8_t* uid, + uint32_t* uid_len, + const uint32_t uid_maxlen) { + bool loaded = false; do { - // Read Mifare Classic type - if(!flipper_format_read_string(file, "Mifare Classic type", temp_str)) break; - if(!string_cmp_str(temp_str, "1K")) { - data->type = MfClassicType1k; - data_blocks = 64; - } else if(!string_cmp_str(temp_str, "4K")) { - data->type = MfClassicType4k; - data_blocks = 256; - } else { - break; - } - - bool old_format = false; - // Read Mifare Classic format version - if(!flipper_format_read_uint32(file, "Data format version", &data_format_version, 1)) { - // Load unread sectors with zero keys access for backward compatability - if(!flipper_format_rewind(file)) break; - old_format = true; - } else { - if(data_format_version < nfc_mifare_classic_data_format_version) { - old_format = true; - } - } + uint32_t uid_len_current; + if(!flipper_format_get_value_count(ff, NFC_DEVICE_UID_KEY, &uid_len_current)) break; + if(uid_len_current > uid_maxlen) break; + if(!flipper_format_read_hex(ff, NFC_DEVICE_UID_KEY, uid, uid_len_current)) break; - // Read Mifare Classic blocks - bool block_read = true; - string_t block_str; - string_init(block_str); - for(size_t i = 0; i < data_blocks; i++) { - string_printf(temp_str, "Block %d", i); - if(!flipper_format_read_string(file, string_get_cstr(temp_str), block_str)) { - block_read = false; - break; - } - nfc_device_load_mifare_classic_block(block_str, data, i); - } - string_clear(block_str); - if(!block_read) break; - - // Set keys and blocks as unknown for backward compatibility - if(old_format) { - data->key_a_mask = 0ULL; - data->key_b_mask = 0ULL; - memset(data->block_read_mask, 0, sizeof(data->block_read_mask)); - } - - parsed = true; + *uid_len = uid_len_current; + loaded = true; } while(false); - string_clear(temp_str); - return parsed; + return loaded; } -static void nfc_device_get_key_cache_file_path(NfcDevice* dev, string_t file_path) { - uint8_t* uid = dev->dev_data.nfc_data.uid; - uint8_t uid_len = dev->dev_data.nfc_data.uid_len; - string_set_str(file_path, NFC_DEVICE_KEYS_FOLDER "/"); - for(size_t i = 0; i < uid_len; i++) { - string_cat_printf(file_path, "%02X", uid[i]); - } - string_cat_printf(file_path, NFC_DEVICE_KEYS_EXTENSION); -} +static bool nfc_device_load_unified(NfcDevice* instance, FlipperFormat* ff, uint32_t version) { + bool loaded = false; -static bool nfc_device_save_mifare_classic_keys(NfcDevice* dev) { - FlipperFormat* file = flipper_format_file_alloc(dev->storage); - MfClassicData* data = &dev->dev_data.mf_classic_data; - string_t temp_str; - string_init(temp_str); + FuriString* temp_str = furi_string_alloc(); - nfc_device_get_key_cache_file_path(dev, temp_str); - bool save_success = false; do { - if(!storage_simply_mkdir(dev->storage, NFC_DEVICE_KEYS_FOLDER)) break; - if(!storage_simply_remove(dev->storage, string_get_cstr(temp_str))) break; - if(!flipper_format_file_open_always(file, string_get_cstr(temp_str))) break; - if(!flipper_format_write_header_cstr(file, nfc_keys_file_header, nfc_keys_file_version)) - break; - if(data->type == MfClassicType1k) { - if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "1K")) break; - } else if(data->type == MfClassicType4k) { - if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "4K")) break; - } - if(!flipper_format_write_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break; - if(!flipper_format_write_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break; - uint8_t sector_num = mf_classic_get_total_sectors_num(data->type); - bool key_save_success = true; - for(size_t i = 0; (i < sector_num) && (key_save_success); i++) { - MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); - if(FURI_BIT(data->key_a_mask, i)) { - string_printf(temp_str, "Key A sector %d", i); - key_save_success = - flipper_format_write_hex(file, string_get_cstr(temp_str), sec_tr->key_a, 6); - } - if(!key_save_success) break; - if(FURI_BIT(data->key_a_mask, i)) { - string_printf(temp_str, "Key B sector %d", i); - key_save_success = - flipper_format_write_hex(file, string_get_cstr(temp_str), sec_tr->key_b, 6); - } - } - save_success = key_save_success; - } while(false); - - flipper_format_free(file); - string_clear(temp_str); - return save_success; -} - -bool nfc_device_load_key_cache(NfcDevice* dev) { - furi_assert(dev); - string_t temp_str; - string_init(temp_str); - - MfClassicData* data = &dev->dev_data.mf_classic_data; - nfc_device_get_key_cache_file_path(dev, temp_str); - FlipperFormat* file = flipper_format_file_alloc(dev->storage); + // Read Nfc device type + if(!flipper_format_read_string(ff, NFC_DEVICE_TYPE_KEY, temp_str)) break; - bool load_success = false; - do { - if(storage_common_stat(dev->storage, string_get_cstr(temp_str), NULL) != FSE_OK) break; - if(!flipper_format_file_open_existing(file, string_get_cstr(temp_str))) break; - uint32_t version = 0; - if(!flipper_format_read_header(file, temp_str, &version)) break; - if(string_cmp_str(temp_str, nfc_keys_file_header)) break; - if(version != nfc_keys_file_version) break; - if(!flipper_format_read_string(file, "Mifare Classic type", temp_str)) break; - if(!string_cmp_str(temp_str, "1K")) { - data->type = MfClassicType1k; - } else if(!string_cmp_str(temp_str, "4K")) { - data->type = MfClassicType4k; - } else { - break; - } - if(!flipper_format_read_hex_uint64(file, "Key A map", &data->key_a_mask, 1)) break; - if(!flipper_format_read_hex_uint64(file, "Key B map", &data->key_b_mask, 1)) break; - uint8_t sectors = mf_classic_get_total_sectors_num(data->type); - bool key_read_success = true; - for(size_t i = 0; (i < sectors) && (key_read_success); i++) { - MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); - if(FURI_BIT(data->key_a_mask, i)) { - string_printf(temp_str, "Key A sector %d", i); - key_read_success = - flipper_format_read_hex(file, string_get_cstr(temp_str), sec_tr->key_a, 6); - } - if(!key_read_success) break; - if(FURI_BIT(data->key_b_mask, i)) { - string_printf(temp_str, "Key B sector %d", i); - key_read_success = - flipper_format_read_hex(file, string_get_cstr(temp_str), sec_tr->key_b, 6); + // Detect protocol + NfcProtocol protocol; + for(protocol = 0; protocol < NfcProtocolNum; ++protocol) { + if(furi_string_equal(temp_str, nfc_devices[protocol]->protocol_name)) { + break; } } - load_success = key_read_success; - } while(false); - string_clear(temp_str); - flipper_format_free(file); + if(protocol == NfcProtocolNum) break; - return load_success; -} + nfc_device_clear(instance); -void nfc_device_set_name(NfcDevice* dev, const char* name) { - furi_assert(dev); + instance->protocol = protocol; + instance->protocol_data = nfc_devices[protocol]->alloc(); - strlcpy(dev->dev_name, name, NFC_DEV_NAME_MAX_LEN); -} + // Load UID + uint8_t uid[NFC_DEVICE_UID_MAX_LEN]; + uint32_t uid_len; -static void nfc_device_get_path_without_ext(string_t orig_path, string_t shadow_path) { - // TODO: this won't work if there is ".nfc" anywhere in the path other than - // at the end - size_t ext_start = string_search_str(orig_path, NFC_APP_EXTENSION); - string_set_n(shadow_path, orig_path, 0, ext_start); -} - -static void nfc_device_get_shadow_path(string_t orig_path, string_t shadow_path) { - nfc_device_get_path_without_ext(orig_path, shadow_path); - string_cat_printf(shadow_path, "%s", NFC_APP_SHADOW_EXTENSION); -} - -static bool nfc_device_save_file( - NfcDevice* dev, - const char* dev_name, - const char* folder, - const char* extension, - bool use_load_path) { - furi_assert(dev); + if(!nfc_device_load_uid(ff, uid, &uid_len, NFC_DEVICE_UID_MAX_LEN)) break; + if(!nfc_device_set_uid(instance, uid, uid_len)) break; - bool saved = false; - FlipperFormat* file = flipper_format_file_alloc(dev->storage); - FuriHalNfcDevData* data = &dev->dev_data.nfc_data; - string_t temp_str; - string_init(temp_str); + // Load data + if(!nfc_devices[protocol]->load(instance->protocol_data, ff, version)) break; - do { - if(use_load_path && !string_empty_p(dev->load_path)) { - // Get directory name - path_extract_dirname(string_get_cstr(dev->load_path), temp_str); - // Create nfc directory if necessary - if(!storage_simply_mkdir(dev->storage, string_get_cstr(temp_str))) break; - // Make path to file to save - string_cat_printf(temp_str, "/%s%s", dev_name, extension); - } else { - // Create nfc directory if necessary - if(!storage_simply_mkdir(dev->storage, NFC_APP_FOLDER)) break; - // First remove nfc device file if it was saved - string_printf(temp_str, "%s/%s%s", folder, dev_name, extension); - } - // Open file - if(!flipper_format_file_open_always(file, string_get_cstr(temp_str))) break; - // Write header - if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break; - // Write nfc device type - if(!flipper_format_write_comment_cstr( - file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic, Bank card")) - break; - nfc_device_prepare_format_string(dev, temp_str); - if(!flipper_format_write_string(file, "Device type", temp_str)) break; - // Write UID, ATQA, SAK - if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats")) - break; - if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break; - if(!flipper_format_write_hex(file, "ATQA", data->atqa, 2)) break; - if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; - // Save more data if necessary - if(dev->format == NfcDeviceSaveFormatMifareUl) { - if(!nfc_device_save_mifare_ul_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { - if(!nfc_device_save_mifare_df_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatBankCard) { - if(!nfc_device_save_bank_card_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { - // Save data - if(!nfc_device_save_mifare_classic_data(file, dev)) break; - // Save keys cache - if(!nfc_device_save_mifare_classic_keys(dev)) break; - } - saved = true; - } while(0); + loaded = true; + } while(false); - if(!saved) { - dialog_message_show_storage_error(dev->dialogs, "Can not save\nkey file"); + if(!loaded) { + nfc_device_clear(instance); } - string_clear(temp_str); - flipper_format_free(file); - return saved; -} -bool nfc_device_save(NfcDevice* dev, const char* dev_name) { - return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_EXTENSION, true); + furi_string_free(temp_str); + return loaded; } -bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name) { - dev->shadow_file_exist = true; - return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_SHADOW_EXTENSION, true); -} +static bool nfc_device_load_legacy(NfcDevice* instance, FlipperFormat* ff, uint32_t version) { + bool loaded = false; -static bool nfc_device_load_data(NfcDevice* dev, string_t path, bool show_dialog) { - bool parsed = false; - FlipperFormat* file = flipper_format_file_alloc(dev->storage); - FuriHalNfcDevData* data = &dev->dev_data.nfc_data; - uint32_t data_cnt = 0; - string_t temp_str; - string_init(temp_str); - bool deprecated_version = false; - - if(dev->loading_cb) { - dev->loading_cb(dev->loading_cb_ctx, true); - } + FuriString* temp_str = furi_string_alloc(); do { - // Check existance of shadow file - nfc_device_get_shadow_path(path, temp_str); - dev->shadow_file_exist = - storage_common_stat(dev->storage, string_get_cstr(temp_str), NULL) == FSE_OK; - // Open shadow file if it exists. If not - open original - if(dev->shadow_file_exist) { - if(!flipper_format_file_open_existing(file, string_get_cstr(temp_str))) break; - } else { - if(!flipper_format_file_open_existing(file, string_get_cstr(path))) break; - } - // Read and verify file header - uint32_t version = 0; - if(!flipper_format_read_header(file, temp_str, &version)) break; - if(string_cmp_str(temp_str, nfc_file_header) || (version != nfc_file_version)) { - deprecated_version = true; - break; - } // Read Nfc device type - if(!flipper_format_read_string(file, "Device type", temp_str)) break; - if(!nfc_device_parse_format_string(dev, temp_str)) break; - // Read and parse UID, ATQA and SAK - if(!flipper_format_get_value_count(file, "UID", &data_cnt)) break; - if(!(data_cnt == 4 || data_cnt == 7)) break; - data->uid_len = data_cnt; - if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; - if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; - if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; - // Parse other data - if(dev->format == NfcDeviceSaveFormatMifareUl) { - if(!nfc_device_load_mifare_ul_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { - if(!nfc_device_load_mifare_classic_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { - if(!nfc_device_load_mifare_df_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatBankCard) { - if(!nfc_device_load_bank_card_data(file, dev)) break; - } - parsed = true; - } while(false); - - if(dev->loading_cb) { - dev->loading_cb(dev->loading_cb_ctx, false); - } + if(!flipper_format_read_string(ff, NFC_DEVICE_TYPE_KEY, temp_str)) break; - if((!parsed) && (show_dialog)) { - if(deprecated_version) { - dialog_message_show_storage_error(dev->dialogs, "File format deprecated"); - } else { - dialog_message_show_storage_error(dev->dialogs, "Can not parse\nfile"); - } - } + nfc_device_clear(instance); - string_clear(temp_str); - flipper_format_free(file); - return parsed; -} + // Detect protocol + for(NfcProtocol protocol = 0; protocol < NfcProtocolNum; protocol++) { + instance->protocol = protocol; + instance->protocol_data = nfc_devices[protocol]->alloc(); -bool nfc_device_load(NfcDevice* dev, const char* file_path, bool show_dialog) { - furi_assert(dev); - furi_assert(file_path); - - // Load device data - string_set_str(dev->load_path, file_path); - bool dev_load = nfc_device_load_data(dev, dev->load_path, show_dialog); - if(dev_load) { - // Set device name - string_t filename; - string_init(filename); - path_extract_filename_no_ext(file_path, filename); - nfc_device_set_name(dev, string_get_cstr(filename)); - string_clear(filename); - } + // Verify protocol + if(nfc_devices[protocol]->verify(instance->protocol_data, temp_str)) { + uint8_t uid[NFC_DEVICE_UID_MAX_LEN]; + uint32_t uid_len; - return dev_load; -} + // Load data + loaded = nfc_device_load_uid(ff, uid, &uid_len, NFC_DEVICE_UID_MAX_LEN) && + nfc_device_set_uid(instance, uid, uid_len) && + nfc_devices[protocol]->load(instance->protocol_data, ff, version); + break; + } -bool nfc_file_select(NfcDevice* dev) { - furi_assert(dev); - - // Input events and views are managed by file_browser - string_t nfc_app_folder; - string_init_set_str(nfc_app_folder, NFC_APP_FOLDER); - bool res = dialog_file_browser_show( - dev->dialogs, dev->load_path, nfc_app_folder, NFC_APP_EXTENSION, true, &I_Nfc_10px, true); - string_clear(nfc_app_folder); - if(res) { - string_t filename; - string_init(filename); - path_extract_filename(dev->load_path, filename, true); - strncpy(dev->dev_name, string_get_cstr(filename), NFC_DEV_NAME_MAX_LEN); - res = nfc_device_load_data(dev, dev->load_path, true); - if(res) { - nfc_device_set_name(dev, dev->dev_name); + nfc_device_clear(instance); } - string_clear(filename); - } - - return res; -} - -void nfc_device_data_clear(NfcDeviceData* dev_data) { - if(dev_data->protocol == NfcDeviceProtocolMifareDesfire) { - mf_df_clear(&dev_data->mf_df_data); - } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { - memset(&dev_data->mf_classic_data, 0, sizeof(MfClassicData)); - } else if(dev_data->protocol == NfcDeviceProtocolMifareUl) { - mf_ul_reset(&dev_data->mf_ul_data); - } else if(dev_data->protocol == NfcDeviceProtocolEMV) { - memset(&dev_data->emv_data, 0, sizeof(EmvData)); - } - memset(&dev_data->nfc_data, 0, sizeof(FuriHalNfcDevData)); - dev_data->protocol = NfcDeviceProtocolUnknown; - string_reset(dev_data->parsed_data); -} -void nfc_device_clear(NfcDevice* dev) { - furi_assert(dev); + } while(false); - nfc_device_set_name(dev, ""); - nfc_device_data_clear(&dev->dev_data); - dev->format = NfcDeviceSaveFormatUid; - string_reset(dev->load_path); + furi_string_free(temp_str); + return loaded; } -bool nfc_device_delete(NfcDevice* dev, bool use_load_path) { - furi_assert(dev); +bool nfc_device_load(NfcDevice* instance, const char* path) { + furi_assert(instance); + furi_assert(path); - bool deleted = false; - string_t file_path; - string_init(file_path); + bool loaded = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_buffered_file_alloc(storage); - do { - // Delete original file - if(use_load_path && !string_empty_p(dev->load_path)) { - string_set(file_path, dev->load_path); - } else { - string_printf(file_path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_EXTENSION); - } - if(!storage_simply_remove(dev->storage, string_get_cstr(file_path))) break; - // Delete shadow file if it exists - if(dev->shadow_file_exist) { - if(use_load_path && !string_empty_p(dev->load_path)) { - nfc_device_get_shadow_path(dev->load_path, file_path); - } else { - string_printf( - file_path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_SHADOW_EXTENSION); - } - if(!storage_simply_remove(dev->storage, string_get_cstr(file_path))) break; - } - deleted = true; - } while(0); + FuriString* temp_str; + temp_str = furi_string_alloc(); - if(!deleted) { - dialog_message_show_storage_error(dev->dialogs, "Can not remove file"); + if(instance->loading_callback) { + instance->loading_callback(instance->loading_callback_context, true); } - string_clear(file_path); - return deleted; -} + do { + if(!flipper_format_buffered_file_open_existing(ff, path)) break; -bool nfc_device_restore(NfcDevice* dev, bool use_load_path) { - furi_assert(dev); - furi_assert(dev->shadow_file_exist); + // Read and verify file header + uint32_t version = 0; + if(!flipper_format_read_header(ff, temp_str, &version)) break; - bool restored = false; - string_t path; + if(furi_string_cmp_str(temp_str, NFC_FILE_HEADER)) break; + if(version < NFC_MINIMUM_SUPPORTED_FORMAT_VERSION) break; - string_init(path); + // Select loading method + loaded = (version < NFC_UNIFIED_FORMAT_VERSION) ? + nfc_device_load_legacy(instance, ff, version) : + nfc_device_load_unified(instance, ff, version); - do { - if(use_load_path && !string_empty_p(dev->load_path)) { - nfc_device_get_shadow_path(dev->load_path, path); - } else { - string_printf( - path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_SHADOW_EXTENSION); - } - if(!storage_simply_remove(dev->storage, string_get_cstr(path))) break; - dev->shadow_file_exist = false; - if(use_load_path && !string_empty_p(dev->load_path)) { - string_set(path, dev->load_path); - } else { - string_printf(path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_EXTENSION); - } - if(!nfc_device_load_data(dev, path, true)) break; - restored = true; - } while(0); + } while(false); - string_clear(path); - return restored; -} + if(instance->loading_callback) { + instance->loading_callback(instance->loading_callback_context, false); + } -void nfc_device_set_loading_callback(NfcDevice* dev, NfcLoadingCallback callback, void* context) { - furi_assert(dev); + furi_string_free(temp_str); + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); - dev->loading_cb = callback; - dev->loading_cb_ctx = context; + return loaded; } diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index f9a0b24baa8..6636e7c7683 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -1,103 +1,266 @@ +/** + * @file nfc_device.h + * @brief Abstract interface for managing NFC device data. + * + * Under the hood, it makes use of the protocol-specific functions that each one of them provides + * and abstracts it with a protocol-independent API. + * + * It does not perform any signal processing, but merely serves as a container with some handy + * operations such as loading and saving from and to a file. + */ #pragma once +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include "protocols/nfc_device_base.h" +#include "protocols/nfc_protocol.h" -#define NFC_DEV_NAME_MAX_LEN 22 -#define NFC_READER_DATA_MAX_SIZE 64 -#define NFC_DICT_KEY_BATCH_SIZE 50 +#ifdef __cplusplus +extern "C" { +#endif -#define NFC_APP_FOLDER ANY_PATH("nfc") -#define NFC_APP_EXTENSION ".nfc" -#define NFC_APP_SHADOW_EXTENSION ".shd" +/** + * @brief NfcDevice opaque type definition. + */ +typedef struct NfcDevice NfcDevice; +/** + * @brief Loading callback function signature. + * + * A function with such signature can be set as a callback to indicate + * the completion (or a failure) of nfc_device_load() and nfc_device_save() functions. + * + * This facility is commonly used to control GUI elements, such as progress dialogs. + * + * @param[in] context user-defined context that was passed in nfc_device_set_loading_callback(). + * @param[in] state true if the data was loaded successfully, false otherwise. + */ typedef void (*NfcLoadingCallback)(void* context, bool state); -typedef enum { - NfcDeviceProtocolUnknown, - NfcDeviceProtocolEMV, - NfcDeviceProtocolMifareUl, - NfcDeviceProtocolMifareClassic, - NfcDeviceProtocolMifareDesfire, -} NfcProtocol; - -typedef enum { - NfcDeviceSaveFormatUid, - NfcDeviceSaveFormatBankCard, - NfcDeviceSaveFormatMifareUl, - NfcDeviceSaveFormatMifareClassic, - NfcDeviceSaveFormatMifareDesfire, -} NfcDeviceSaveFormat; - -typedef struct { - uint8_t data[NFC_READER_DATA_MAX_SIZE]; - uint16_t size; -} NfcReaderRequestData; - -typedef struct { - MfClassicDict* dict; -} NfcMfClassicDictAttackData; - -typedef struct { - FuriHalNfcDevData nfc_data; - NfcProtocol protocol; - union { - NfcReaderRequestData reader_data; - NfcMfClassicDictAttackData mf_classic_dict_attack_data; - }; - union { - EmvData emv_data; - MfUltralightData mf_ul_data; - MfClassicData mf_classic_data; - MifareDesfireData mf_df_data; - }; - string_t parsed_data; -} NfcDeviceData; - -typedef struct { - Storage* storage; - DialogsApp* dialogs; - NfcDeviceData dev_data; - char dev_name[NFC_DEV_NAME_MAX_LEN + 1]; - string_t load_path; - NfcDeviceSaveFormat format; - bool shadow_file_exist; - - NfcLoadingCallback loading_cb; - void* loading_cb_ctx; -} NfcDevice; - +/** + * @brief Allocate an NfcDevice instance. + * + * A newly created instance does not hold any data and thus is considered invalid. The most common + * use case would be to set its data by calling nfc_device_set_data() right afterwards. + * + * @returns pointer to the allocated instance. + */ NfcDevice* nfc_device_alloc(); -void nfc_device_free(NfcDevice* nfc_dev); +/** + * @brief Delete an NfcDevice instance. + * + * @param[in,out] instance pointer to the instance to be deleted. + */ +void nfc_device_free(NfcDevice* instance); + +/** + * @brief Clear an NfcDevice instance. + * + * All data contained in the instance will be deleted and the instance itself will become invalid + * as if it was just allocated. + * + * @param[in,out] instance pointer to the instance to be cleared. + */ +void nfc_device_clear(NfcDevice* instance); + +/** + * @brief Reset an NfcDevice instance. + * + * The data contained in the instance will be reset according to the protocol-defined procedure. + * Unlike the nfc_device_clear() function, the instance will remain valid. + * + * @param[in,out] instance pointer to the instance to be reset. + */ +void nfc_device_reset(NfcDevice* instance); + +/** + * @brief Get the protocol identifier from an NfcDevice instance. + * + * If the instance is invalid, the return value will be NfcProtocolInvalid. + * + * @param[in] instance pointer to the instance to be queried. + * @returns protocol identifier contained in the instance. + */ +NfcProtocol nfc_device_get_protocol(const NfcDevice* instance); + +/** + * @brief Get the protocol-specific data from an NfcDevice instance. + * + * The protocol parameter's behaviour is a bit tricky. The function will check + * whether there is such a protocol somewhere in the protocol hierarchy and return + * the data exactly from that level. + * + * Example: Call nfc_device_get_data() on an instance with Mf DESFire protocol. + * The protocol hierarchy will look like the following: + * + * `Mf DESFire --> ISO14443-4A --> ISO14443-3A` + * + * Thus, the following values of the protocol parameter are valid: + * + * * NfcProtocolIso14443_3a + * * NfcProtocolIso14443_4a + * * NfcProtocolMfDesfire + * + * and passing them to the call would result in the respective data being returned. + * + * However, supplying a protocol identifier which is not in the hierarchy will + * result in a crash. This is to improve type safety. + * + * @param instance pointer to the instance to be queried + * @param protocol protocol identifier of the data to be retrieved. + * @returns pointer to the instance's data. + */ +const NfcDeviceData* nfc_device_get_data(const NfcDevice* instance, NfcProtocol protocol); + +/** + * @brief Get the protocol name by its identifier. + * + * This function does not require an instance as its return result depends only + * the protocol identifier. + * + * @param[in] protocol numeric identifier of the protocol in question. + * @returns pointer to a statically allocated string containing the protocol name. + */ +const char* nfc_device_get_protocol_name(NfcProtocol protocol); + +/** + * @brief Get the name of an NfcDevice instance. + * + * The return value may change depending on the instance's internal state and the name_type parameter. + * + * @param[in] instance pointer to the instance to be queried. + * @param[in] name_type type of the name to be displayed. + * @returns pointer to a statically allocated string containing the device name. + */ +const char* nfc_device_get_name(const NfcDevice* instance, NfcDeviceNameType name_type); -void nfc_device_set_name(NfcDevice* dev, const char* name); +/** + * @brief Get the unique identifier (UID) of an NfcDevice instance. + * + * The UID length is protocol-dependent. Additionally, a particular protocol might support + * several UID lengths. + * + * @param[in] instance pointer to the instance to be queried. + * @param[out] uid_len pointer to the variable to contain the UID length. + * @returns pointer to the byte array containing the instance's UID. + */ +const uint8_t* nfc_device_get_uid(const NfcDevice* instance, size_t* uid_len); -bool nfc_device_save(NfcDevice* dev, const char* dev_name); +/** + * @brief Set the unique identifier (UID) of an NfcDevice instance. + * + * The UID length must be supported by the instance's protocol. + * + * @param[in,out] instance pointer to the instance to be modified. + * @param[in] uid pointer to the byte array containing the new UID. + * @param[in] uid_len length of the UID. + * @return true if the UID was valid and set, false otherwise. + */ +bool nfc_device_set_uid(NfcDevice* instance, const uint8_t* uid, size_t uid_len); -bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name); +/** + * @brief Set the data and protocol of an NfcDevice instance. + * + * Any data previously contained in the instance will be deleted. + * + * @param[in,out] instance pointer to the instance to be modified. + * @param[in] protocol numeric identifier of the data's protocol. + * @param[in] protocol_data pointer to the protocol-specific data. + */ +void nfc_device_set_data( + NfcDevice* instance, + NfcProtocol protocol, + const NfcDeviceData* protocol_data); -bool nfc_device_load(NfcDevice* dev, const char* file_path, bool show_dialog); +/** + * @brief Copy (export) the data contained in an NfcDevice instance to an outside NfcDeviceData instance. + * + * This function does the inverse of nfc_device_set_data(). -bool nfc_device_load_key_cache(NfcDevice* dev); + * The protocol identifier passed as the protocol parameter MUST match the one + * stored in the instance, otherwise a crash will occur. + * This is to improve type safety. + * + * @param[in] instance pointer to the instance to be copied from. + * @param[in] protocol numeric identifier of the instance's protocol. + * @param[out] protocol_data pointer to the destination data. + */ +void nfc_device_copy_data( + const NfcDevice* instance, + NfcProtocol protocol, + NfcDeviceData* protocol_data); -bool nfc_file_select(NfcDevice* dev); +/** + * @brief Check whether an NfcDevice instance holds certain data. + * + * This function's behaviour is similar to nfc_device_is_equal(), with the difference + * that it takes NfcProtocol and NfcDeviceData* instead of the second NfcDevice*. + * + * The following code snippets [1] and [2] are equivalent: + * + * [1] + * ```c + * bool is_equal = nfc_device_is_equal(device1, device2); + * ``` + * [2] + * ```c + * NfcProtocol protocol = nfc_device_get_protocol(device2); + * const NfcDeviceData* data = nfc_device_get_data(device2, protocol); + * bool is_equal = nfc_device_is_equal_data(device1, protocol, data); + * ``` + * + * @param[in] instance pointer to the instance to be compared. + * @param[in] protocol protocol identifier of the data to be compared. + * @param[in] protocol_data pointer to the NFC device data to be compared. + * @returns true if the instance is of the right type and the data matches, false otherwise. + */ +bool nfc_device_is_equal_data( + const NfcDevice* instance, + NfcProtocol protocol, + const NfcDeviceData* protocol_data); -void nfc_device_data_clear(NfcDeviceData* dev); +/** + * @brief Compare two NfcDevice instances to determine whether they are equal. + * + * @param[in] instance pointer to the first instance to be compared. + * @param[in] other pointer to the second instance to be compared. + * @returns true if both instances are considered equal, false otherwise. + */ +bool nfc_device_is_equal(const NfcDevice* instance, const NfcDevice* other); -void nfc_device_clear(NfcDevice* dev); +/** + * @brief Set the loading callback function. + * + * @param[in,out] instance pointer to the instance to be modified. + * @param[in] callback pointer to a function to be called when the load operation completes. + * @param[in] context pointer to a user-specific context (will be passed to the callback). + */ +void nfc_device_set_loading_callback( + NfcDevice* instance, + NfcLoadingCallback callback, + void* context); -bool nfc_device_delete(NfcDevice* dev, bool use_load_path); +/** + * @brief Save NFC device data form an NfcDevice instance to a file. + * + * @param[in] instance pointer to the instance to be saved. + * @param[in] path pointer to a character string with a full file path. + * @returns true if the data was successfully saved, false otherwise. + */ +bool nfc_device_save(NfcDevice* instance, const char* path); -bool nfc_device_restore(NfcDevice* dev, bool use_load_path); +/** + * @brief Load NFC device data to an NfcDevice instance from a file. + * + * @param[in,out] instance pointer to the instance to be loaded into. + * @param[in] path pointer to a character string with a full file path. + * @returns true if the data was successfully loaded, false otherwise. + */ +bool nfc_device_load(NfcDevice* instance, const char* path); -void nfc_device_set_loading_callback(NfcDevice* dev, NfcLoadingCallback callback, void* context); +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/nfc_device_i.c b/lib/nfc/nfc_device_i.c new file mode 100644 index 00000000000..e98b04251e3 --- /dev/null +++ b/lib/nfc/nfc_device_i.c @@ -0,0 +1,37 @@ +#include "nfc_device_i.h" +#include "protocols/nfc_device_defs.h" + +#include + +static NfcDeviceData* + nfc_device_search_base_protocol_data(const NfcDevice* instance, NfcProtocol protocol) { + NfcProtocol protocol_tmp = instance->protocol; + NfcDeviceData* dev_data_tmp = instance->protocol_data; + + while(true) { + dev_data_tmp = nfc_devices[protocol_tmp]->get_base_data(dev_data_tmp); + protocol_tmp = nfc_protocol_get_parent(protocol_tmp); + if(protocol_tmp == protocol) { + break; + } + } + + return dev_data_tmp; +} + +NfcDeviceData* nfc_device_get_data_ptr(const NfcDevice* instance, NfcProtocol protocol) { + furi_assert(instance); + furi_assert(protocol < NfcProtocolNum); + + NfcDeviceData* dev_data = NULL; + + if(instance->protocol == protocol) { + dev_data = instance->protocol_data; + } else if(nfc_protocol_has_parent(instance->protocol, protocol)) { + dev_data = nfc_device_search_base_protocol_data(instance, protocol); + } else { + furi_crash("Incorrect protocol"); + } + + return dev_data; +} diff --git a/lib/nfc/nfc_device_i.h b/lib/nfc/nfc_device_i.h new file mode 100644 index 00000000000..1a9017d03f6 --- /dev/null +++ b/lib/nfc/nfc_device_i.h @@ -0,0 +1,46 @@ +/** + * @file nfc_device_i.h + * @brief NfcDevice private types and definitions. + * + * This file is an implementation detail. It must not be included in + * any public API-related headers. + */ +#pragma once + +#include "nfc_device.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief NfcDevice structure definition. + */ +struct NfcDevice { + NfcProtocol protocol; /**< Numeric identifier of the data's protocol*/ + NfcDeviceData* protocol_data; /**< Pointer to the NFC device data. */ + + NfcLoadingCallback + loading_callback; /**< Pointer to the function to be called upon loading completion. */ + void* loading_callback_context; /**< Pointer to the context to be passed to the loading callback. */ +}; + +/** + * @brief Get the mutable (non-const) data from an NfcDevice instance. + * + * The behaviour is the same as with nfc_device_get_data(), but the + * return pointer is non-const, allowing for changing data it is pointing to. + * + * @see nfc_device.h + * + * Under the hood, nfc_device_get_data() calls this and then adds const-ness to the return value. + * + * @param instance pointer to the instance to be queried + * @param protocol protocol identifier of the data to be retrieved. + * @returns pointer to the instance's (mutable) data. + */ +NfcDeviceData* nfc_device_get_data_ptr(const NfcDevice* instance, NfcProtocol protocol); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/nfc_listener.c b/lib/nfc/nfc_listener.c new file mode 100644 index 00000000000..57b00a8755a --- /dev/null +++ b/lib/nfc/nfc_listener.c @@ -0,0 +1,144 @@ +#include "nfc_listener.h" + +#include +#include + +#include + +typedef struct NfcListenerListElement { + NfcProtocol protocol; + NfcGenericInstance* listener; + const NfcListenerBase* listener_api; + struct NfcListenerListElement* child; +} NfcListenerListElement; + +typedef struct { + NfcListenerListElement* head; + NfcListenerListElement* tail; +} NfcListenerList; + +struct NfcListener { + NfcProtocol protocol; + Nfc* nfc; + NfcListenerList list; + NfcDevice* nfc_dev; +}; + +static void nfc_listener_list_alloc(NfcListener* instance) { + instance->list.head = malloc(sizeof(NfcListenerListElement)); + instance->list.head->protocol = instance->protocol; + + instance->list.head->listener_api = nfc_listeners_api[instance->protocol]; + instance->list.head->child = NULL; + instance->list.tail = instance->list.head; + + // Build linked list + do { + NfcProtocol parent_protocol = nfc_protocol_get_parent(instance->list.head->protocol); + if(parent_protocol == NfcProtocolInvalid) break; + + NfcListenerListElement* parent = malloc(sizeof(NfcListenerListElement)); + parent->protocol = parent_protocol; + parent->listener_api = nfc_listeners_api[parent_protocol]; + parent->child = instance->list.head; + + instance->list.head = parent; + } while(true); + + // Allocate listener instances + NfcListenerListElement* iter = instance->list.head; + NfcDeviceData* data_tmp = nfc_device_get_data_ptr(instance->nfc_dev, iter->protocol); + iter->listener = iter->listener_api->alloc(instance->nfc, data_tmp); + + do { + if(iter->child == NULL) break; + data_tmp = nfc_device_get_data_ptr(instance->nfc_dev, iter->child->protocol); + iter->child->listener = iter->child->listener_api->alloc(iter->listener, data_tmp); + iter->listener_api->set_callback( + iter->listener, iter->child->listener_api->run, iter->child->listener); + + iter = iter->child; + } while(true); +} + +static void nfc_listener_list_free(NfcListener* instance) { + // Free listener instances + do { + instance->list.head->listener_api->free(instance->list.head->listener); + NfcListenerListElement* child = instance->list.head->child; + free(instance->list.head); + if(child == NULL) break; + instance->list.head = child; + } while(true); +} + +NfcListener* nfc_listener_alloc(Nfc* nfc, NfcProtocol protocol, const NfcDeviceData* data) { + furi_assert(nfc); + furi_assert(protocol < NfcProtocolNum); + furi_assert(data); + furi_assert(nfc_listeners_api[protocol]); + + NfcListener* instance = malloc(sizeof(NfcListener)); + instance->nfc = nfc; + instance->protocol = protocol; + instance->nfc_dev = nfc_device_alloc(); + nfc_device_set_data(instance->nfc_dev, protocol, data); + nfc_listener_list_alloc(instance); + + return instance; +} + +void nfc_listener_free(NfcListener* instance) { + furi_assert(instance); + + nfc_listener_list_free(instance); + nfc_device_free(instance->nfc_dev); + free(instance); +} + +NfcCommand nfc_listener_start_callback(NfcEvent event, void* context) { + furi_assert(context); + + NfcListener* instance = context; + furi_assert(instance->list.head); + + NfcCommand command = NfcCommandContinue; + NfcGenericEvent generic_event = { + .protocol = NfcProtocolInvalid, + .instance = instance->nfc, + .event_data = &event, + }; + + NfcListenerListElement* head_listener = instance->list.head; + command = head_listener->listener_api->run(generic_event, head_listener->listener); + + return command; +} + +void nfc_listener_start(NfcListener* instance, NfcGenericCallback callback, void* context) { + furi_assert(instance); + + NfcListenerListElement* tail_element = instance->list.tail; + tail_element->listener_api->set_callback(tail_element->listener, callback, context); + nfc_start(instance->nfc, nfc_listener_start_callback, instance); +} + +void nfc_listener_stop(NfcListener* instance) { + furi_assert(instance); + + nfc_stop(instance->nfc); +} + +NfcProtocol nfc_listener_get_protocol(const NfcListener* instance) { + furi_assert(instance); + + return instance->protocol; +} + +const NfcDeviceData* nfc_listener_get_data(const NfcListener* instance, NfcProtocol protocol) { + furi_assert(instance); + furi_assert(instance->protocol == protocol); + + NfcListenerListElement* tail_element = instance->list.tail; + return tail_element->listener_api->get_data(tail_element->listener); +} diff --git a/lib/nfc/nfc_listener.h b/lib/nfc/nfc_listener.h new file mode 100644 index 00000000000..dbfeb23bff9 --- /dev/null +++ b/lib/nfc/nfc_listener.h @@ -0,0 +1,94 @@ +/** + * @file nfc_listener.h + * @brief NFC card emulation library. + * + * Once started, it will respond to supported commands from an NFC reader, thus imitating + * (or emulating) an NFC card. The responses will depend on the data that was supplied to + * the listener, so various card types and different cards of the same type can be emulated. + * + * It will also make any changes necessary to the emulated data in response to the + * reader commands if the protocol supports it. + * + * When running, NfcListener will generate events that the calling code must handle + * by providing a callback function. The events passed to the callback are protocol-specific + * and may include errors, state changes, data reception, special function requests and more. + */ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief NfcListener opaque type definition. + */ +typedef struct NfcListener NfcListener; + +/** + * @brief Allocate an NfcListener instance. + * + * @param[in] nfc pointer to an Nfc instance. + * @param[in] protocol identifier of the protocol to be used. + * @param[in] data pointer to the data to use during emulation. + * @returns pointer to an allocated instance. + * + * @see nfc.h + */ +NfcListener* nfc_listener_alloc(Nfc* nfc, NfcProtocol protocol, const NfcDeviceData* data); + +/** + * @brief Delete an NfcListener instance. + * + * @param[in,out] instance pointer to the instance to be deleted. + */ +void nfc_listener_free(NfcListener* instance); + +/** + * @brief Start an NfcListener instance. + * + * The callback logic is protocol-specific, so it cannot be described here in detail. + * However, the callback return value ALWAYS determines what the listener should do next: + * to continue whatever it was doing prior to the callback run or to stop. + * + * @param[in,out] instance pointer to the instance to be started. + * @param[in] callback pointer to a user-defined callback function which will receive events. + * @param[in] context pointer to a user-specific context (will be passed to the callback). + */ +void nfc_listener_start(NfcListener* instance, NfcGenericCallback callback, void* context); + +/** + * @brief Stop an NfcListener instance. + * + * The emulation process can be stopped explicitly (the other way is via the callback return value). + * + * @param[in,out] instance pointer to the instance to be stopped. + */ +void nfc_listener_stop(NfcListener* instance); + +/** + * @brief Get the protocol identifier an NfcListener instance was created with. + * + * @param[in] instance pointer to the instance to be queried. + * @returns identifier of the protocol used by the instance. + */ +NfcProtocol nfc_listener_get_protocol(const NfcListener* instance); + +/** + * @brief Get the data that was that was provided for emulation. + * + * The protocol identifier passed as the protocol parameter MUST match the one + * stored in the instance, otherwise a crash will occur. + * This is to improve type safety. + * + * @param[in] instance pointer to the instance to be queried. + * @param[in] protocol assumed protocol identifier of the data to be retrieved. + * @returns pointer to the NFC device data. + */ +const NfcDeviceData* nfc_listener_get_data(const NfcListener* instance, NfcProtocol protocol); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/nfc_poller.c b/lib/nfc/nfc_poller.c new file mode 100644 index 00000000000..48be37d9259 --- /dev/null +++ b/lib/nfc/nfc_poller.c @@ -0,0 +1,283 @@ +#include "nfc_poller.h" + +#include + +#include + +typedef enum { + NfcPollerSessionStateIdle, + NfcPollerSessionStateActive, + NfcPollerSessionStateStopRequest, +} NfcPollerSessionState; + +typedef struct NfcPollerListElement { + NfcProtocol protocol; + NfcGenericInstance* poller; + const NfcPollerBase* poller_api; + struct NfcPollerListElement* child; +} NfcPollerListElement; + +typedef struct { + NfcPollerListElement* head; + NfcPollerListElement* tail; +} NfcPollerList; + +struct NfcPoller { + NfcProtocol protocol; + Nfc* nfc; + NfcPollerList list; + NfcPollerSessionState session_state; + bool protocol_detected; + + NfcGenericCallbackEx callback; + void* context; +}; + +static void nfc_poller_list_alloc(NfcPoller* instance) { + instance->list.head = malloc(sizeof(NfcPollerListElement)); + instance->list.head->protocol = instance->protocol; + instance->list.head->poller_api = nfc_pollers_api[instance->protocol]; + instance->list.head->child = NULL; + instance->list.tail = instance->list.head; + + do { + NfcProtocol parent_protocol = nfc_protocol_get_parent(instance->list.head->protocol); + if(parent_protocol == NfcProtocolInvalid) break; + + NfcPollerListElement* parent = malloc(sizeof(NfcPollerListElement)); + parent->protocol = parent_protocol; + parent->poller_api = nfc_pollers_api[parent_protocol]; + parent->child = instance->list.head; + instance->list.head = parent; + } while(true); + + NfcPollerListElement* iter = instance->list.head; + iter->poller = iter->poller_api->alloc(instance->nfc); + + do { + if(iter->child == NULL) break; + iter->child->poller = iter->child->poller_api->alloc(iter->poller); + iter->poller_api->set_callback( + iter->poller, iter->child->poller_api->run, iter->child->poller); + + iter = iter->child; + } while(true); +} + +static void nfc_poller_list_free(NfcPoller* instance) { + do { + instance->list.head->poller_api->free(instance->list.head->poller); + NfcPollerListElement* child = instance->list.head->child; + free(instance->list.head); + if(child == NULL) break; + instance->list.head = child; + } while(true); +} + +NfcPoller* nfc_poller_alloc(Nfc* nfc, NfcProtocol protocol) { + furi_assert(nfc); + furi_assert(protocol < NfcProtocolNum); + + NfcPoller* instance = malloc(sizeof(NfcPoller)); + instance->session_state = NfcPollerSessionStateIdle; + instance->nfc = nfc; + instance->protocol = protocol; + nfc_poller_list_alloc(instance); + + return instance; +} + +void nfc_poller_free(NfcPoller* instance) { + furi_assert(instance); + + nfc_poller_list_free(instance); + free(instance); +} + +static NfcCommand nfc_poller_start_callback(NfcEvent event, void* context) { + furi_assert(context); + + NfcPoller* instance = context; + + NfcCommand command = NfcCommandContinue; + NfcGenericEvent poller_event = { + .protocol = NfcProtocolInvalid, + .instance = instance->nfc, + .event_data = &event, + }; + + if(event.type == NfcEventTypePollerReady) { + NfcPollerListElement* head_poller = instance->list.head; + command = head_poller->poller_api->run(poller_event, head_poller->poller); + } + + if(instance->session_state == NfcPollerSessionStateStopRequest) { + command = NfcCommandStop; + } + + return command; +} + +void nfc_poller_start(NfcPoller* instance, NfcGenericCallback callback, void* context) { + furi_assert(instance); + furi_assert(callback); + furi_assert(instance->session_state == NfcPollerSessionStateIdle); + + NfcPollerListElement* tail_poller = instance->list.tail; + tail_poller->poller_api->set_callback(tail_poller->poller, callback, context); + + instance->session_state = NfcPollerSessionStateActive; + nfc_start(instance->nfc, nfc_poller_start_callback, instance); +} + +static NfcCommand nfc_poller_start_ex_tail_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol != NfcProtocolInvalid); + + NfcPoller* instance = context; + NfcCommand command = NfcCommandContinue; + + NfcGenericEventEx poller_event = { + .poller = instance->list.tail->poller, + .parent_event_data = event.event_data, + }; + + command = instance->callback(poller_event, instance->context); + + return command; +} + +static NfcCommand nfc_poller_start_ex_head_callback(NfcEvent event, void* context) { + furi_assert(context); + + NfcCommand command = NfcCommandContinue; + NfcPoller* instance = context; + + NfcProtocol parent_protocol = nfc_protocol_get_parent(instance->protocol); + + if(parent_protocol == NfcProtocolInvalid) { + NfcGenericEventEx poller_event = { + .poller = instance->list.tail->poller, + .parent_event_data = &event, + }; + + command = instance->callback(poller_event, instance->context); + } else { + NfcGenericEvent poller_event = { + .protocol = NfcProtocolInvalid, + .instance = instance->nfc, + .event_data = &event, + }; + NfcPollerListElement* head_poller = instance->list.head; + command = head_poller->poller_api->run(poller_event, head_poller->poller); + } + + if(instance->session_state == NfcPollerSessionStateStopRequest) { + command = NfcCommandStop; + } + + return command; +} + +void nfc_poller_start_ex(NfcPoller* instance, NfcGenericCallbackEx callback, void* context) { + furi_assert(instance); + furi_assert(callback); + furi_assert(instance->session_state == NfcPollerSessionStateIdle); + + instance->callback = callback; + instance->context = context; + + NfcProtocol parent_protocol = nfc_protocol_get_parent(instance->protocol); + if(parent_protocol != NfcProtocolInvalid) { + NfcPollerListElement* iter = instance->list.head; + while(iter->protocol != parent_protocol) iter = iter->child; + + iter->poller_api->set_callback(iter->poller, nfc_poller_start_ex_tail_callback, instance); + } + + instance->session_state = NfcPollerSessionStateActive; + nfc_start(instance->nfc, nfc_poller_start_ex_head_callback, instance); +} + +void nfc_poller_stop(NfcPoller* instance) { + furi_assert(instance); + furi_assert(instance->nfc); + + instance->session_state = NfcPollerSessionStateStopRequest; + nfc_stop(instance->nfc); + instance->session_state = NfcPollerSessionStateIdle; +} + +static NfcCommand nfc_poller_detect_tail_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + + NfcPoller* instance = context; + NfcPollerListElement* tail_poller = instance->list.tail; + instance->protocol_detected = tail_poller->poller_api->detect(event, tail_poller->poller); + + return NfcCommandStop; +} + +static NfcCommand nfc_poller_detect_head_callback(NfcEvent event, void* context) { + furi_assert(context); + + NfcPoller* instance = context; + NfcPollerListElement* tail_poller = instance->list.tail; + NfcPollerListElement* head_poller = instance->list.head; + + NfcCommand command = NfcCommandContinue; + NfcGenericEvent poller_event = { + .protocol = NfcProtocolInvalid, + .instance = instance->nfc, + .event_data = &event, + }; + + if(event.type == NfcEventTypePollerReady) { + if(tail_poller == head_poller) { + instance->protocol_detected = + tail_poller->poller_api->detect(poller_event, tail_poller->poller); + command = NfcCommandStop; + } else { + command = head_poller->poller_api->run(poller_event, head_poller->poller); + } + } + + return command; +} + +bool nfc_poller_detect(NfcPoller* instance) { + furi_assert(instance); + furi_assert(instance->session_state == NfcPollerSessionStateIdle); + + instance->session_state = NfcPollerSessionStateActive; + NfcPollerListElement* tail_poller = instance->list.tail; + NfcPollerListElement* iter = instance->list.head; + + if(tail_poller != instance->list.head) { + while(iter->child != tail_poller) iter = iter->child; + iter->poller_api->set_callback(iter->poller, nfc_poller_detect_tail_callback, instance); + } + + nfc_start(instance->nfc, nfc_poller_detect_head_callback, instance); + nfc_stop(instance->nfc); + + if(tail_poller != instance->list.head) { + iter->poller_api->set_callback( + iter->poller, tail_poller->poller_api->run, tail_poller->poller); + } + + return instance->protocol_detected; +} + +NfcProtocol nfc_poller_get_protocol(const NfcPoller* instance) { + furi_assert(instance); + + return instance->protocol; +} + +const NfcDeviceData* nfc_poller_get_data(const NfcPoller* instance) { + furi_assert(instance); + + NfcPollerListElement* tail_poller = instance->list.tail; + return tail_poller->poller_api->get_data(tail_poller->poller); +} diff --git a/lib/nfc/nfc_poller.h b/lib/nfc/nfc_poller.h new file mode 100644 index 00000000000..18fbfb388ad --- /dev/null +++ b/lib/nfc/nfc_poller.h @@ -0,0 +1,141 @@ +/** + * @file nfc_poller.h + * @brief NFC card reading library. + * + * Once started, it will try to activate and read a card using the designated protocol, + * which is usually obtained by creating and starting an NfcScanner first. + * + * @see nfc_scanner.h + * + * When running, NfcPoller will generate events that the calling code must handle + * by providing a callback function. The events passed to the callback are protocol-specific + * and may include errors, state changes, data reception, special function requests and more. + * + */ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief NfcPoller opaque type definition. + */ +typedef struct NfcPoller NfcPoller; + +/** + * @brief Extended generic Nfc event type. + * + * An extended generic Nfc event contains protocol poller and it's parent protocol event data. + * If protocol has no parent, then events are produced by Nfc instance. + * + * The parent_event_data field is protocol-specific and should be cast to the appropriate type before use. + */ +typedef struct { + NfcGenericInstance* poller; /**< Pointer to the protocol poller. */ + NfcGenericEventData* + parent_event_data /**< Pointer to the protocol's parent poller event data. */; +} NfcGenericEventEx; + +/** + * @brief Extended generic Nfc event callback type. + * + * A function of this type must be passed as the callback parameter upon extended start of a poller. + * + * @param [in] event Nfc extended generic event, passed by value, complete with protocol type and data. + * @param [in,out] context pointer to the user-specific context (set when starting a poller/listener instance). + * @returns the command which the event producer must execute. + */ +typedef NfcCommand (*NfcGenericCallbackEx)(NfcGenericEventEx event, void* context); + +/** + * @brief Allocate an NfcPoller instance. + * + * @param[in] nfc pointer to an Nfc instance. + * @param[in] protocol identifier of the protocol to be used. + * @returns pointer to an allocated instance. + * + * @see nfc.h + */ +NfcPoller* nfc_poller_alloc(Nfc* nfc, NfcProtocol protocol); + +/** + * @brief Delete an NfcPoller instance. + * + * @param[in,out] instance pointer to the instance to be deleted. + */ +void nfc_poller_free(NfcPoller* instance); + +/** + * @brief Start an NfcPoller instance. + * + * The callback logic is protocol-specific, so it cannot be described here in detail. + * However, the callback return value ALWAYS determines what the poller should do next: + * to continue whatever it was doing prior to the callback run or to stop. + * + * @param[in,out] instance pointer to the instance to be started. + * @param[in] callback pointer to a user-defined callback function which will receive events. + * @param[in] context pointer to a user-specific context (will be passed to the callback). + */ +void nfc_poller_start(NfcPoller* instance, NfcGenericCallback callback, void* context); + +/** + * @brief Start an NfcPoller instance in extended mode. + * + * When nfc poller is started in extended mode, callback will be called with parent protocol events + * and protocol instance. This mode enables to make custom poller state machines. + * + * @param[in,out] instance pointer to the instance to be started. + * @param[in] callback pointer to a user-defined callback function which will receive events. + * @param[in] context pointer to a user-specific context (will be passed to the callback). + */ +void nfc_poller_start_ex(NfcPoller* instance, NfcGenericCallbackEx callback, void* context); + +/** + * @brief Stop an NfcPoller instance. + * + * The reading process can be stopped explicitly (the other way is via the callback return value). + * + * @param[in,out] instance pointer to the instance to be stopped. + */ +void nfc_poller_stop(NfcPoller* instance); + +/** + * @brief Detect whether there is a card supporting a particular protocol in the vicinity. + * + * The behaviour of this function is protocol-defined, in general, it will do whatever is + * necessary to determine whether a card supporting the current protocol is in the vicinity + * and whether it is functioning normally. + * + * It is used automatically inside NfcScanner, so there is usually no need + * to call it explicitly. + * + * @see nfc_scanner.h + * + * @param[in,out] instance pointer to the instance to perform the detection with. + * @returns true if a supported card was detected, false otherwise. + */ +bool nfc_poller_detect(NfcPoller* instance); + +/** + * @brief Get the protocol identifier an NfcPoller instance was created with. + * + * @param[in] instance pointer to the instance to be queried. + * @returns identifier of the protocol used by the instance. + */ +NfcProtocol nfc_poller_get_protocol(const NfcPoller* instance); + +/** + * @brief Get the data that was that was gathered during the reading process. + * + * @param[in] instance pointer to the instance to be queried. + * @returns pointer to the NFC device data. + */ +const NfcDeviceData* nfc_poller_get_data(const NfcPoller* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/nfc_scanner.c b/lib/nfc/nfc_scanner.c new file mode 100644 index 00000000000..0056dfb1ccc --- /dev/null +++ b/lib/nfc/nfc_scanner.c @@ -0,0 +1,267 @@ +#include "nfc_scanner.h" +#include "nfc_poller.h" + +#include + +#include + +#define TAG "NfcScanner" + +typedef enum { + NfcScannerStateIdle, + NfcScannerStateTryBasePollers, + NfcScannerStateFindChildrenProtocols, + NfcScannerStateDetectChildrenProtocols, + NfcScannerStateComplete, + + NfcScannerStateNum, +} NfcScannerState; + +typedef enum { + NfcScannerSessionStateIdle, + NfcScannerSessionStateActive, + NfcScannerSessionStateStopRequest, +} NfcScannerSessionState; + +struct NfcScanner { + Nfc* nfc; + NfcScannerState state; + NfcScannerSessionState session_state; + + NfcScannerCallback callback; + void* context; + + NfcEvent nfc_event; + + NfcProtocol first_detected_protocol; + + size_t base_protocols_num; + size_t base_protocols_idx; + NfcProtocol base_protocols[NfcProtocolNum]; + + size_t detected_base_protocols_num; + NfcProtocol detected_base_protocols[NfcProtocolNum]; + + size_t children_protocols_num; + size_t children_protocols_idx; + NfcProtocol children_protocols[NfcProtocolNum]; + + size_t detected_protocols_num; + NfcProtocol detected_protocols[NfcProtocolNum]; + + NfcProtocol current_protocol; + + FuriThread* scan_worker; +}; + +static void nfc_scanner_reset(NfcScanner* instance) { + instance->base_protocols_idx = 0; + instance->base_protocols_num = 0; + + instance->children_protocols_idx = 0; + instance->children_protocols_num = 0; + + instance->detected_protocols_num = 0; + instance->detected_base_protocols_num = 0; + + instance->current_protocol = 0; +} + +typedef void (*NfcScannerStateHandler)(NfcScanner* instance); + +void nfc_scanner_state_handler_idle(NfcScanner* instance) { + for(size_t i = 0; i < NfcProtocolNum; i++) { + NfcProtocol parent_protocol = nfc_protocol_get_parent(i); + if(parent_protocol == NfcProtocolInvalid) { + instance->base_protocols[instance->base_protocols_num] = i; + instance->base_protocols_num++; + } + } + FURI_LOG_D(TAG, "Found %zu base protocols", instance->base_protocols_num); + + instance->first_detected_protocol = NfcProtocolInvalid; + instance->state = NfcScannerStateTryBasePollers; +} + +void nfc_scanner_state_handler_try_base_pollers(NfcScanner* instance) { + do { + instance->current_protocol = instance->base_protocols[instance->base_protocols_idx]; + + if(instance->first_detected_protocol == instance->current_protocol) { + instance->state = NfcScannerStateFindChildrenProtocols; + break; + } + + NfcPoller* poller = nfc_poller_alloc(instance->nfc, instance->current_protocol); + bool protocol_detected = nfc_poller_detect(poller); + nfc_poller_free(poller); + + if(protocol_detected) { + instance->detected_protocols[instance->detected_protocols_num] = + instance->current_protocol; + instance->detected_protocols_num++; + + instance->detected_base_protocols[instance->detected_base_protocols_num] = + instance->current_protocol; + instance->detected_base_protocols_num++; + + if(instance->first_detected_protocol == NfcProtocolInvalid) { + instance->first_detected_protocol = instance->current_protocol; + instance->current_protocol = NfcProtocolInvalid; + } + } + + instance->base_protocols_idx = + (instance->base_protocols_idx + 1) % instance->base_protocols_num; + } while(false); +} + +void nfc_scanner_state_handler_find_children_protocols(NfcScanner* instance) { + for(size_t i = 0; i < NfcProtocolNum; i++) { + for(size_t j = 0; j < instance->detected_base_protocols_num; j++) { + if(nfc_protocol_has_parent(i, instance->detected_base_protocols[j])) { + instance->children_protocols[instance->children_protocols_num] = i; + instance->children_protocols_num++; + } + } + } + + if(instance->children_protocols_num > 0) { + instance->state = NfcScannerStateDetectChildrenProtocols; + } else { + instance->state = NfcScannerStateComplete; + } + FURI_LOG_D(TAG, "Found %zu children", instance->children_protocols_num); +} + +void nfc_scanner_state_handler_detect_children_protocols(NfcScanner* instance) { + furi_assert(instance->children_protocols_num); + + instance->current_protocol = instance->children_protocols[instance->children_protocols_idx]; + + NfcPoller* poller = nfc_poller_alloc(instance->nfc, instance->current_protocol); + bool protocol_detected = nfc_poller_detect(poller); + nfc_poller_free(poller); + + if(protocol_detected) { + instance->detected_protocols[instance->detected_protocols_num] = + instance->current_protocol; + instance->detected_protocols_num++; + } + + instance->children_protocols_idx++; + if(instance->children_protocols_idx == instance->children_protocols_num) { + instance->state = NfcScannerStateComplete; + } +} + +static void nfc_scanner_filter_detected_protocols(NfcScanner* instance) { + size_t filtered_protocols_num = 0; + NfcProtocol filtered_protocols[NfcProtocolNum] = {}; + + for(size_t i = 0; i < instance->detected_protocols_num; i++) { + bool is_parent = false; + for(size_t j = i; j < instance->detected_protocols_num; j++) { + is_parent = nfc_protocol_has_parent( + instance->detected_protocols[j], instance->detected_protocols[i]); + if(is_parent) break; + } + if(!is_parent) { + filtered_protocols[filtered_protocols_num] = instance->detected_protocols[i]; + filtered_protocols_num++; + } + } + + instance->detected_protocols_num = filtered_protocols_num; + memcpy(instance->detected_protocols, filtered_protocols, filtered_protocols_num); +} + +void nfc_scanner_state_handler_complete(NfcScanner* instance) { + if(instance->detected_protocols_num > 1) { + nfc_scanner_filter_detected_protocols(instance); + } + FURI_LOG_I(TAG, "Detected %zu protocols", instance->detected_protocols_num); + + NfcScannerEvent event = { + .type = NfcScannerEventTypeDetected, + .data = + { + .protocol_num = instance->detected_protocols_num, + .protocols = instance->detected_protocols, + }, + }; + + instance->callback(event, instance->context); + furi_delay_ms(100); +} + +static NfcScannerStateHandler nfc_scanner_state_handlers[NfcScannerStateNum] = { + [NfcScannerStateIdle] = nfc_scanner_state_handler_idle, + [NfcScannerStateTryBasePollers] = nfc_scanner_state_handler_try_base_pollers, + [NfcScannerStateFindChildrenProtocols] = nfc_scanner_state_handler_find_children_protocols, + [NfcScannerStateDetectChildrenProtocols] = nfc_scanner_state_handler_detect_children_protocols, + [NfcScannerStateComplete] = nfc_scanner_state_handler_complete, +}; + +static int32_t nfc_scanner_worker(void* context) { + furi_assert(context); + + NfcScanner* instance = context; + + while(instance->session_state == NfcScannerSessionStateActive) { + nfc_scanner_state_handlers[instance->state](instance); + } + + nfc_scanner_reset(instance); + + return 0; +} + +NfcScanner* nfc_scanner_alloc(Nfc* nfc) { + furi_assert(nfc); + + NfcScanner* instance = malloc(sizeof(NfcScanner)); + instance->nfc = nfc; + + return instance; +} + +void nfc_scanner_free(NfcScanner* instance) { + furi_assert(instance); + + free(instance); +} + +void nfc_scanner_start(NfcScanner* instance, NfcScannerCallback callback, void* context) { + furi_assert(instance); + furi_assert(callback); + furi_assert(instance->session_state == NfcScannerSessionStateIdle); + furi_assert(instance->scan_worker == NULL); + + instance->callback = callback; + instance->context = context; + instance->session_state = NfcScannerSessionStateActive; + + instance->scan_worker = furi_thread_alloc(); + furi_thread_set_name(instance->scan_worker, "NfcScanWorker"); + furi_thread_set_context(instance->scan_worker, instance); + furi_thread_set_stack_size(instance->scan_worker, 4 * 1024); + furi_thread_set_callback(instance->scan_worker, nfc_scanner_worker); + + furi_thread_start(instance->scan_worker); +} + +void nfc_scanner_stop(NfcScanner* instance) { + furi_assert(instance); + furi_assert(instance->scan_worker); + + instance->session_state = NfcScannerSessionStateStopRequest; + furi_thread_join(instance->scan_worker); + instance->session_state = NfcScannerSessionStateIdle; + + furi_thread_free(instance->scan_worker); + instance->scan_worker = NULL; + instance->callback = NULL; + instance->context = NULL; + instance->state = NfcScannerStateIdle; +} diff --git a/lib/nfc/nfc_scanner.h b/lib/nfc/nfc_scanner.h new file mode 100644 index 00000000000..a1b4aabcda3 --- /dev/null +++ b/lib/nfc/nfc_scanner.h @@ -0,0 +1,97 @@ +/** + * @file nfc_scanner.h + * @brief NFC card detection library. + * + * Once started, a NfcScanner instance will iterate over all available protocols + * and return a list of one or more detected protocol identifiers via a user-provided callback. + * + * The NfcScanner behaviour is greedy, i.e. it will not stop scanning upon detection of + * a just one protocol and will try others as well until all possibilities are exhausted. + * This is to allow for multi-protocol card support. + * + * If no supported cards are in the vicinity, the scanning process will continue + * until stopped explicitly. + */ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief NfcScanner opaque type definition. + */ +typedef struct NfcScanner NfcScanner; + +/** + * @brief Event type passed to the user callback. + */ +typedef enum { + NfcScannerEventTypeDetected, /**< One or more protocols have been detected. */ +} NfcScannerEventType; + +/** + * @brief Event data passed to the user callback. + */ +typedef struct { + size_t protocol_num; /**< Number of detected protocols (one or more). */ + NfcProtocol* protocols; /**< Pointer to the array of detected protocol identifiers. */ +} NfcScannerEventData; + +/** + * @brief Event passed to the user callback. + */ +typedef struct { + NfcScannerEventType type; /**< Type of event. Determines how the data must be handled. */ + NfcScannerEventData data; /**< Event-specific data. Handled accordingly to the even type. */ +} NfcScannerEvent; + +/** + * @brief User callback function signature. + * + * A function with such signature must be provided by the user upon calling nfc_scanner_start(). + * + * @param[in] event occurred event, complete with type and data. + * @param[in] context pointer to the context data provided in nfc_scanner_start() call. + */ +typedef void (*NfcScannerCallback)(NfcScannerEvent event, void* context); + +/** + * @brief Allocate an NfcScanner instance. + * + * @param[in] nfc pointer to an Nfc instance. + * @returns pointer to the allocated NfcScanner instance. + * + * @see nfc.h + */ +NfcScanner* nfc_scanner_alloc(Nfc* nfc); + +/** + * @brief Delete an NfcScanner instance. + * + * @param[in,out] pointer to the instance to be deleted. + */ +void nfc_scanner_free(NfcScanner* instance); + +/** + * @brief Start an NfcScanner. + * + * @param[in,out] pointer to the instance to be started. + * @param[in] callback pointer to the callback function (will be called upon a detection event). + * @param[in] context pointer to the caller-specific context (will be passed to the callback). + */ +void nfc_scanner_start(NfcScanner* instance, NfcScannerCallback callback, void* context); + +/** + * @brief Stop an NfcScanner. + * + * @param[in,out] pointer to the instance to be stopped. + */ +void nfc_scanner_stop(NfcScanner* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/nfc_types.c b/lib/nfc/nfc_types.c deleted file mode 100644 index 42762876993..00000000000 --- a/lib/nfc/nfc_types.c +++ /dev/null @@ -1,65 +0,0 @@ -#include "nfc_types.h" - -const char* nfc_get_dev_type(FuriHalNfcType type) { - if(type == FuriHalNfcTypeA) { - return "NFC-A"; - } else if(type == FuriHalNfcTypeB) { - return "NFC-B"; - } else if(type == FuriHalNfcTypeF) { - return "NFC-F"; - } else if(type == FuriHalNfcTypeV) { - return "NFC-V"; - } else { - return "Unknown"; - } -} - -const char* nfc_guess_protocol(NfcProtocol protocol) { - if(protocol == NfcDeviceProtocolEMV) { - return "EMV bank card"; - } else if(protocol == NfcDeviceProtocolMifareUl) { - return "Mifare Ultral/NTAG"; - } else if(protocol == NfcDeviceProtocolMifareClassic) { - return "Mifare Classic"; - } else if(protocol == NfcDeviceProtocolMifareDesfire) { - return "Mifare DESFire"; - } else { - return "Unrecognized"; - } -} - -const char* nfc_mf_ul_type(MfUltralightType type, bool full_name) { - if(type == MfUltralightTypeNTAG213) { - return "NTAG213"; - } else if(type == MfUltralightTypeNTAG215) { - return "NTAG215"; - } else if(type == MfUltralightTypeNTAG216) { - return "NTAG216"; - } else if(type == MfUltralightTypeNTAGI2C1K) { - return "NTAG I2C 1K"; - } else if(type == MfUltralightTypeNTAGI2C2K) { - return "NTAG I2C 2K"; - } else if(type == MfUltralightTypeNTAGI2CPlus1K) { - return "NTAG I2C Plus 1K"; - } else if(type == MfUltralightTypeNTAGI2CPlus2K) { - return "NTAG I2C Plus 2K"; - } else if(type == MfUltralightTypeNTAG203) { - return "NTAG203"; - } else if(type == MfUltralightTypeUL11 && full_name) { - return "Mifare Ultralight 11"; - } else if(type == MfUltralightTypeUL21 && full_name) { - return "Mifare Ultralight 21"; - } else { - return "Mifare Ultralight"; - } -} - -const char* nfc_mf_classic_type(MfClassicType type) { - if(type == MfClassicType1k) { - return "Mifare Classic 1K"; - } else if(type == MfClassicType4k) { - return "Mifare Classic 4K"; - } else { - return "Mifare Classic"; - } -} diff --git a/lib/nfc/nfc_types.h b/lib/nfc/nfc_types.h deleted file mode 100644 index fb53ce7c25b..00000000000 --- a/lib/nfc/nfc_types.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "nfc_device.h" - -const char* nfc_get_dev_type(FuriHalNfcType type); - -const char* nfc_guess_protocol(NfcProtocol protocol); - -const char* nfc_mf_ul_type(MfUltralightType type, bool full_name); - -const char* nfc_mf_classic_type(MfClassicType type); diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c deleted file mode 100644 index 45bbc5f41a4..00000000000 --- a/lib/nfc/nfc_worker.c +++ /dev/null @@ -1,570 +0,0 @@ -#include "nfc_worker_i.h" -#include - -#include -#include "parsers/nfc_supported_card.h" - -#define TAG "NfcWorker" - -/***************************** NFC Worker API *******************************/ - -NfcWorker* nfc_worker_alloc() { - NfcWorker* nfc_worker = malloc(sizeof(NfcWorker)); - - // Worker thread attributes - nfc_worker->thread = furi_thread_alloc(); - furi_thread_set_name(nfc_worker->thread, "NfcWorker"); - furi_thread_set_stack_size(nfc_worker->thread, 8192); - furi_thread_set_callback(nfc_worker->thread, nfc_worker_task); - furi_thread_set_context(nfc_worker->thread, nfc_worker); - - nfc_worker->callback = NULL; - nfc_worker->context = NULL; - nfc_worker->storage = furi_record_open(RECORD_STORAGE); - - // Initialize rfal - while(furi_hal_nfc_is_busy()) { - furi_delay_ms(10); - } - nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); - - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - nfc_worker->debug_pcap_worker = nfc_debug_pcap_alloc(nfc_worker->storage); - } - - return nfc_worker; -} - -void nfc_worker_free(NfcWorker* nfc_worker) { - furi_assert(nfc_worker); - - furi_thread_free(nfc_worker->thread); - - furi_record_close(RECORD_STORAGE); - - if(nfc_worker->debug_pcap_worker) nfc_debug_pcap_free(nfc_worker->debug_pcap_worker); - - free(nfc_worker); -} - -NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker) { - return nfc_worker->state; -} - -void nfc_worker_start( - NfcWorker* nfc_worker, - NfcWorkerState state, - NfcDeviceData* dev_data, - NfcWorkerCallback callback, - void* context) { - furi_assert(nfc_worker); - furi_assert(dev_data); - while(furi_hal_nfc_is_busy()) { - furi_delay_ms(10); - } - - nfc_worker->callback = callback; - nfc_worker->context = context; - nfc_worker->dev_data = dev_data; - nfc_worker_change_state(nfc_worker, state); - furi_thread_start(nfc_worker->thread); -} - -void nfc_worker_stop(NfcWorker* nfc_worker) { - furi_assert(nfc_worker); - if(nfc_worker->state == NfcWorkerStateBroken || nfc_worker->state == NfcWorkerStateReady) { - return; - } - furi_hal_nfc_stop(); - nfc_worker_change_state(nfc_worker, NfcWorkerStateStop); - furi_thread_join(nfc_worker->thread); -} - -void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) { - nfc_worker->state = state; -} - -/***************************** NFC Worker Thread *******************************/ - -int32_t nfc_worker_task(void* context) { - NfcWorker* nfc_worker = context; - - furi_hal_nfc_exit_sleep(); - - if(nfc_worker->state == NfcWorkerStateRead) { - nfc_worker_read(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateUidEmulate) { - nfc_worker_emulate_uid(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateEmulateApdu) { - nfc_worker_emulate_apdu(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) { - nfc_worker_emulate_mf_ultralight(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateMfClassicEmulate) { - nfc_worker_emulate_mf_classic(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) { - nfc_worker_mf_ultralight_read_auth(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { - nfc_worker_mf_classic_dict_attack(nfc_worker); - } - furi_hal_nfc_sleep(); - nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); - - return 0; -} - -static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { - bool read_success = false; - MfUltralightReader reader = {}; - MfUltralightData data = {}; - - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); - do { - // Read card - if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; - if(!mf_ul_read_card(tx_rx, &reader, &data)) break; - // Copy data - nfc_worker->dev_data->mf_ul_data = data; - read_success = true; - } while(false); - - return read_success; -} - -static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { - furi_assert(nfc_worker->callback); - bool read_success = false; - - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); - do { - // Try to read supported card - FURI_LOG_I(TAG, "Try read supported card ..."); - for(size_t i = 0; i < NfcSupportedCardTypeEnd; i++) { - if(nfc_supported_card[i].protocol == NfcDeviceProtocolMifareClassic) { - if(nfc_supported_card[i].verify(nfc_worker, tx_rx)) { - if(nfc_supported_card[i].read(nfc_worker, tx_rx)) { - read_success = true; - nfc_supported_card[i].parse(nfc_worker->dev_data); - } - } - } - } - if(read_success) break; - // Try to read card with key cache - FURI_LOG_I(TAG, "Search for key cache ..."); - if(nfc_worker->callback(NfcWorkerEventReadMfClassicLoadKeyCache, nfc_worker->context)) { - FURI_LOG_I(TAG, "Load keys cache success. Start reading"); - uint8_t sectors_read = - mf_classic_update_card(tx_rx, &nfc_worker->dev_data->mf_classic_data); - uint8_t sectors_total = - mf_classic_get_total_sectors_num(nfc_worker->dev_data->mf_classic_data.type); - FURI_LOG_I(TAG, "Read %d sectors out of %d total", sectors_read, sectors_total); - read_success = (sectors_read == sectors_total); - } - } while(false); - - return read_success; -} - -static bool nfc_worker_read_mf_desfire(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { - bool read_success = false; - MifareDesfireData* data = &nfc_worker->dev_data->mf_df_data; - - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); - do { - if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; - if(!mf_df_read_card(tx_rx, data)) break; - read_success = true; - } while(false); - - return read_success; -} - -static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { - bool read_success = false; - EmvApplication emv_app = {}; - EmvData* result = &nfc_worker->dev_data->emv_data; - - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); - do { - // Read card - if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; - if(!emv_read_bank_card(tx_rx, &emv_app)) break; - // Copy data - // TODO Set EmvData to reader or like in mifare ultralight! - result->number_len = emv_app.card_number_len; - memcpy(result->number, emv_app.card_number, result->number_len); - result->aid_len = emv_app.aid_len; - memcpy(result->aid, emv_app.aid, result->aid_len); - if(emv_app.name_found) { - memcpy(result->name, emv_app.name, sizeof(emv_app.name)); - } - if(emv_app.exp_month) { - result->exp_mon = emv_app.exp_month; - result->exp_year = emv_app.exp_year; - } - if(emv_app.country_code) { - result->country_code = emv_app.country_code; - } - if(emv_app.currency_code) { - result->currency_code = emv_app.currency_code; - } - read_success = true; - } while(false); - - return read_success; -} - -static bool nfc_worker_read_nfca(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { - FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - - bool card_read = false; - furi_hal_nfc_sleep(); - if(mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { - FURI_LOG_I(TAG, "Mifare Ultralight / NTAG detected"); - nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareUl; - card_read = nfc_worker_read_mf_ultralight(nfc_worker, tx_rx); - } else if(mf_classic_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { - FURI_LOG_I(TAG, "Mifare Classic detected"); - nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareClassic; - nfc_worker->dev_data->mf_classic_data.type = - mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); - card_read = nfc_worker_read_mf_classic(nfc_worker, tx_rx); - } else if(mf_df_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { - FURI_LOG_I(TAG, "Mifare DESFire detected"); - nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareDesfire; - if(!nfc_worker_read_mf_desfire(nfc_worker, tx_rx)) { - FURI_LOG_I(TAG, "Unknown card. Save UID"); - nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; - } - card_read = true; - } else if(nfc_data->interface == FuriHalNfcInterfaceIsoDep) { - FURI_LOG_I(TAG, "ISO14443-4 card detected"); - nfc_worker->dev_data->protocol = NfcDeviceProtocolEMV; - if(!nfc_worker_read_bank_card(nfc_worker, tx_rx)) { - FURI_LOG_I(TAG, "Unknown card. Save UID"); - nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; - } - card_read = true; - } else { - nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown; - card_read = true; - } - - return card_read; -} - -void nfc_worker_read(NfcWorker* nfc_worker) { - furi_assert(nfc_worker); - furi_assert(nfc_worker->callback); - - nfc_device_data_clear(nfc_worker->dev_data); - NfcDeviceData* dev_data = nfc_worker->dev_data; - FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - FuriHalNfcTxRxContext tx_rx = {}; - NfcWorkerEvent event = 0; - bool card_not_detected_notified = false; - - while(nfc_worker->state == NfcWorkerStateRead) { - if(furi_hal_nfc_detect(nfc_data, 300)) { - // Process first found device - nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); - card_not_detected_notified = false; - if(nfc_data->type == FuriHalNfcTypeA) { - if(nfc_worker_read_nfca(nfc_worker, &tx_rx)) { - if(dev_data->protocol == NfcDeviceProtocolMifareUl) { - event = NfcWorkerEventReadMfUltralight; - break; - } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { - event = NfcWorkerEventReadMfClassicDone; - break; - } else if(dev_data->protocol == NfcDeviceProtocolMifareDesfire) { - event = NfcWorkerEventReadMfDesfire; - break; - } else if(dev_data->protocol == NfcDeviceProtocolEMV) { - event = NfcWorkerEventReadBankCard; - break; - } else if(dev_data->protocol == NfcDeviceProtocolUnknown) { - event = NfcWorkerEventReadUidNfcA; - break; - } - } else { - if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { - event = NfcWorkerEventReadMfClassicDictAttackRequired; - break; - } - } - } else if(nfc_data->type == FuriHalNfcTypeB) { - event = NfcWorkerEventReadUidNfcB; - break; - } else if(nfc_data->type == FuriHalNfcTypeF) { - event = NfcWorkerEventReadUidNfcF; - break; - } else if(nfc_data->type == FuriHalNfcTypeV) { - event = NfcWorkerEventReadUidNfcV; - break; - } - } else { - if(!card_not_detected_notified) { - nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); - card_not_detected_notified = true; - } - } - furi_hal_nfc_sleep(); - furi_delay_ms(100); - } - // Notify caller and exit - if(event > NfcWorkerEventReserved) { - nfc_worker->callback(event, nfc_worker->context); - } -} - -void nfc_worker_emulate_uid(NfcWorker* nfc_worker) { - FuriHalNfcTxRxContext tx_rx = {}; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); - FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data; - NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data; - - // TODO add support for RATS - // Now remove bit 6 in SAK to support ISO-14443A-3 emulation - // Need to save ATS to support ISO-14443A-4 emulation - uint8_t sak = data->sak; - FURI_BIT_CLEAR(sak, 5); - - while(nfc_worker->state == NfcWorkerStateUidEmulate) { - if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, sak, true, 100)) { - if(furi_hal_nfc_tx_rx(&tx_rx, 100)) { - reader_data->size = tx_rx.rx_bits / 8; - if(reader_data->size > 0) { - memcpy(reader_data->data, tx_rx.rx_data, reader_data->size); - if(nfc_worker->callback) { - nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); - } - } - } else { - FURI_LOG_E(TAG, "Failed to get reader commands"); - } - } - } -} - -void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { - FuriHalNfcTxRxContext tx_rx = {}; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); - FuriHalNfcDevData params = { - .uid = {0xCF, 0x72, 0xd4, 0x40}, - .uid_len = 4, - .atqa = {0x00, 0x04}, - .sak = 0x20, - .type = FuriHalNfcTypeA, - }; - - while(nfc_worker->state == NfcWorkerStateEmulateApdu) { - if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) { - FURI_LOG_D(TAG, "POS terminal detected"); - if(emv_card_emulation(&tx_rx)) { - FURI_LOG_D(TAG, "EMV card emulated"); - } - } else { - FURI_LOG_D(TAG, "Can't find reader"); - } - furi_hal_nfc_sleep(); - furi_delay_ms(20); - } -} - -void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) { - FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - MfUltralightEmulator emulator = {}; - mf_ul_prepare_emulation(&emulator, &nfc_worker->dev_data->mf_ul_data); - while(nfc_worker->state == NfcWorkerStateMfUltralightEmulate) { - mf_ul_reset_emulation(&emulator, true); - furi_hal_nfc_emulate_nfca( - nfc_data->uid, - nfc_data->uid_len, - nfc_data->atqa, - nfc_data->sak, - mf_ul_prepare_emulation_response, - &emulator, - 5000); - // Check if data was modified - if(emulator.data_changed) { - nfc_worker->dev_data->mf_ul_data = emulator.data; - if(nfc_worker->callback) { - nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); - } - emulator.data_changed = false; - } - } -} - -void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { - furi_assert(nfc_worker); - furi_assert(nfc_worker->callback); - - MfClassicData* data = &nfc_worker->dev_data->mf_classic_data; - NfcMfClassicDictAttackData* dict_attack_data = - &nfc_worker->dev_data->mf_classic_dict_attack_data; - uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type); - uint64_t key = 0; - FuriHalNfcTxRxContext tx_rx = {}; - bool card_found_notified = true; - bool card_removed_notified = false; - - // Load dictionary - MfClassicDict* dict = dict_attack_data->dict; - if(!dict) { - FURI_LOG_E(TAG, "Dictionary not found"); - nfc_worker->callback(NfcWorkerEventNoDictFound, nfc_worker->context); - return; - } - - FURI_LOG_D(TAG, "Start Dictionary attack, Key Count %d", mf_classic_dict_get_total_keys(dict)); - for(size_t i = 0; i < total_sectors; i++) { - FURI_LOG_I(TAG, "Sector %d", i); - nfc_worker->callback(NfcWorkerEventNewSector, nfc_worker->context); - uint8_t block_num = mf_classic_get_sector_trailer_block_num_by_sector(i); - if(mf_classic_is_sector_read(data, i)) continue; - bool is_key_a_found = mf_classic_is_key_found(data, i, MfClassicKeyA); - bool is_key_b_found = mf_classic_is_key_found(data, i, MfClassicKeyB); - uint16_t key_index = 0; - while(mf_classic_dict_get_next_key(dict, &key)) { - if(++key_index % NFC_DICT_KEY_BATCH_SIZE == 0) { - nfc_worker->callback(NfcWorkerEventNewDictKeyBatch, nfc_worker->context); - } - furi_hal_nfc_sleep(); - if(furi_hal_nfc_activate_nfca(200, NULL)) { - furi_hal_nfc_sleep(); - if(!card_found_notified) { - nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); - card_found_notified = true; - card_removed_notified = false; - } - FURI_LOG_D( - TAG, - "Try to auth to sector %d with key %04lx%08lx", - i, - (uint32_t)(key >> 32), - (uint32_t)key); - if(!is_key_a_found) { - is_key_a_found = mf_classic_is_key_found(data, i, MfClassicKeyA); - if(mf_classic_authenticate(&tx_rx, block_num, key, MfClassicKeyA)) { - mf_classic_set_key_found(data, i, MfClassicKeyA, key); - nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context); - } - furi_hal_nfc_sleep(); - } - if(!is_key_b_found) { - is_key_b_found = mf_classic_is_key_found(data, i, MfClassicKeyB); - if(mf_classic_authenticate(&tx_rx, block_num, key, MfClassicKeyB)) { - mf_classic_set_key_found(data, i, MfClassicKeyB, key); - nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context); - } - } - if(is_key_a_found && is_key_b_found) break; - if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; - } else { - if(!card_removed_notified) { - nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); - card_removed_notified = true; - card_found_notified = false; - } - if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; - } - } - if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; - mf_classic_read_sector(&tx_rx, data, i); - mf_classic_dict_rewind(dict); - } - if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { - nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); - } else { - nfc_worker->callback(NfcWorkerEventAborted, nfc_worker->context); - } -} - -void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { - FuriHalNfcTxRxContext tx_rx = {}; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); - FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - MfClassicEmulator emulator = { - .cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4), - .data = nfc_worker->dev_data->mf_classic_data, - .data_changed = false, - }; - NfcaSignal* nfca_signal = nfca_signal_alloc(); - tx_rx.nfca_signal = nfca_signal; - - rfal_platform_spi_acquire(); - - furi_hal_nfc_listen_start(nfc_data); - while(nfc_worker->state == NfcWorkerStateMfClassicEmulate) { - if(furi_hal_nfc_listen_rx(&tx_rx, 300)) { - mf_classic_emulator(&emulator, &tx_rx); - } - } - if(emulator.data_changed) { - nfc_worker->dev_data->mf_classic_data = emulator.data; - if(nfc_worker->callback) { - nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); - } - emulator.data_changed = false; - } - - nfca_signal_free(nfca_signal); - - rfal_platform_spi_release(); -} - -void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { - furi_assert(nfc_worker); - furi_assert(nfc_worker->callback); - - MfUltralightData* data = &nfc_worker->dev_data->mf_ul_data; - FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - FuriHalNfcTxRxContext tx_rx = {}; - MfUltralightReader reader = {}; - mf_ul_reset(data); - - uint32_t key = 0; - uint16_t pack = 0; - while(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) { - furi_hal_nfc_sleep(); - if(furi_hal_nfc_detect(nfc_data, 300) && nfc_data->type == FuriHalNfcTypeA) { - if(mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { - nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); - if(data->auth_method == MfUltralightAuthMethodManual) { - nfc_worker->callback(NfcWorkerEventMfUltralightPassKey, nfc_worker->context); - key = nfc_util_bytes2num(data->auth_key, 4); - } else if(data->auth_method == MfUltralightAuthMethodAmeebo) { - key = mf_ul_pwdgen_amiibo(nfc_data); - } else if(data->auth_method == MfUltralightAuthMethodXiaomi) { - key = mf_ul_pwdgen_xiaomi(nfc_data); - } else { - FURI_LOG_E(TAG, "Incorrect auth method"); - break; - } - - data->auth_success = mf_ultralight_authenticate(&tx_rx, key, &pack); - mf_ul_read_card(&tx_rx, &reader, data); - if(data->auth_success) { - MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(data); - if(config_pages != NULL) { - config_pages->auth_data.pwd.value = REVERSE_BYTES_U32(key); - config_pages->auth_data.pack.value = pack; - } - nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); - break; - } else { - nfc_worker->callback(NfcWorkerEventFail, nfc_worker->context); - break; - } - } else { - nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); - furi_delay_ms(10); - } - } else { - nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); - furi_delay_ms(10); - } - } -} diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h deleted file mode 100755 index 22cbc3dcc99..00000000000 --- a/lib/nfc/nfc_worker.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include "nfc_device.h" - -typedef struct NfcWorker NfcWorker; - -typedef enum { - // Init states - NfcWorkerStateNone, - NfcWorkerStateBroken, - NfcWorkerStateReady, - // Main worker states - NfcWorkerStateRead, - NfcWorkerStateUidEmulate, - NfcWorkerStateMfUltralightEmulate, - NfcWorkerStateMfClassicEmulate, - NfcWorkerStateReadMfUltralightReadAuth, - NfcWorkerStateMfClassicDictAttack, - // Debug - NfcWorkerStateEmulateApdu, - NfcWorkerStateField, - // Transition - NfcWorkerStateStop, -} NfcWorkerState; - -typedef enum { - // Reserve first 50 events for application events - NfcWorkerEventReserved = 50, - - // Nfc read events - NfcWorkerEventReadUidNfcB, - NfcWorkerEventReadUidNfcV, - NfcWorkerEventReadUidNfcF, - NfcWorkerEventReadUidNfcA, - NfcWorkerEventReadMfUltralight, - NfcWorkerEventReadMfDesfire, - NfcWorkerEventReadMfClassicDone, - NfcWorkerEventReadMfClassicLoadKeyCache, - NfcWorkerEventReadMfClassicDictAttackRequired, - NfcWorkerEventReadBankCard, - - // Nfc worker common events - NfcWorkerEventSuccess, - NfcWorkerEventFail, - NfcWorkerEventAborted, - NfcWorkerEventCardDetected, - NfcWorkerEventNoCardDetected, - NfcWorkerEventWrongCardDetected, - - // Mifare Classic events - NfcWorkerEventNoDictFound, - NfcWorkerEventNewSector, - NfcWorkerEventNewDictKeyBatch, - NfcWorkerEventFoundKeyA, - NfcWorkerEventFoundKeyB, - - // Mifare Ultralight events - NfcWorkerEventMfUltralightPassKey, -} NfcWorkerEvent; - -typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); - -NfcWorker* nfc_worker_alloc(); - -NfcWorkerState nfc_worker_get_state(NfcWorker* nfc_worker); - -void nfc_worker_free(NfcWorker* nfc_worker); - -void nfc_worker_start( - NfcWorker* nfc_worker, - NfcWorkerState state, - NfcDeviceData* dev_data, - NfcWorkerCallback callback, - void* context); - -void nfc_worker_stop(NfcWorker* nfc_worker); diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h deleted file mode 100644 index 1e98879a7da..00000000000 --- a/lib/nfc/nfc_worker_i.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include "nfc_worker.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "helpers/nfc_debug_pcap.h" - -struct NfcWorker { - FuriThread* thread; - Storage* storage; - Stream* dict_stream; - - NfcDeviceData* dev_data; - - NfcWorkerCallback callback; - void* context; - - NfcWorkerState state; - - NfcDebugPcapWorker* debug_pcap_worker; -}; - -void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state); - -int32_t nfc_worker_task(void* context); - -void nfc_worker_read(NfcWorker* nfc_worker); - -void nfc_worker_emulate_uid(NfcWorker* nfc_worker); - -void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker); - -void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker); - -void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker); - -void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker); - -void nfc_worker_mf_ul_auth_attack(NfcWorker* nfc_worker); - -void nfc_worker_emulate_apdu(NfcWorker* nfc_worker); diff --git a/lib/nfc/parsers/nfc_supported_card.c b/lib/nfc/parsers/nfc_supported_card.c deleted file mode 100644 index 480c970e7ec..00000000000 --- a/lib/nfc/parsers/nfc_supported_card.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "nfc_supported_card.h" - -#include "troyka_parser.h" - -NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd] = { - [NfcSupportedCardTypeTroyka] = - { - .protocol = NfcDeviceProtocolMifareClassic, - .verify = troyka_parser_verify, - .read = troyka_parser_read, - .parse = troyka_parser_parse, - }, -}; - -bool nfc_supported_card_verify_and_parse(NfcDeviceData* dev_data) { - furi_assert(dev_data); - - bool card_parsed = false; - for(size_t i = 0; i < COUNT_OF(nfc_supported_card); i++) { - if(nfc_supported_card[i].parse(dev_data)) { - card_parsed = true; - break; - } - } - - return card_parsed; -} diff --git a/lib/nfc/parsers/nfc_supported_card.h b/lib/nfc/parsers/nfc_supported_card.h deleted file mode 100644 index 9b5d1c05326..00000000000 --- a/lib/nfc/parsers/nfc_supported_card.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include -#include "../nfc_worker.h" -#include "../nfc_device.h" - -#include - -typedef enum { - NfcSupportedCardTypeTroyka, - - NfcSupportedCardTypeEnd, -} NfcSupportedCardType; - -typedef bool (*NfcSupportedCardVerify)(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); - -typedef bool (*NfcSupportedCardRead)(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); - -typedef bool (*NfcSupportedCardParse)(NfcDeviceData* dev_data); - -typedef struct { - NfcProtocol protocol; - NfcSupportedCardVerify verify; - NfcSupportedCardRead read; - NfcSupportedCardParse parse; -} NfcSupportedCard; - -extern NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd]; - -bool nfc_supported_card_verify_and_parse(NfcDeviceData* dev_data); diff --git a/lib/nfc/parsers/troyka_parser.c b/lib/nfc/parsers/troyka_parser.c deleted file mode 100644 index 51ffa42e14f..00000000000 --- a/lib/nfc/parsers/troyka_parser.c +++ /dev/null @@ -1,79 +0,0 @@ -#include "nfc_supported_card.h" - -#include -#include - -static const MfClassicAuthContext troyka_keys[] = { - {.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0xfbf225dc5d58}, - {.sector = 1, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880}, - {.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, - {.sector = 3, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, - {.sector = 4, .key_a = 0x73068f118c13, .key_b = 0x2b7f3253fac5}, - {.sector = 5, .key_a = 0xfbc2793d540b, .key_b = 0xd3a297dc2698}, - {.sector = 6, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, - {.sector = 7, .key_a = 0xae3d65a3dad4, .key_b = 0x0f1c63013dba}, - {.sector = 8, .key_a = 0xa73f5dc1d333, .key_b = 0xe35173494a81}, - {.sector = 9, .key_a = 0x69a32f1c2f19, .key_b = 0x6b8bd9860763}, - {.sector = 10, .key_a = 0x9becdf3d9273, .key_b = 0xf8493407799d}, - {.sector = 11, .key_a = 0x08b386463229, .key_b = 0x5efbaecef46b}, - {.sector = 12, .key_a = 0xcd4c61c26e3d, .key_b = 0x31c7610de3b0}, - {.sector = 13, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880}, - {.sector = 14, .key_a = 0x0e8f64340ba4, .key_b = 0x4acec1205d75}, - {.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, -}; - -bool troyka_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { - furi_assert(nfc_worker); - UNUSED(nfc_worker); - - MfClassicAuthContext auth_ctx = { - .key_a = MF_CLASSIC_NO_KEY, - .key_b = MF_CLASSIC_NO_KEY, - .sector = 8, - }; - return mf_classic_auth_attempt(tx_rx, &auth_ctx, 0xa73f5dc1d333); -} - -bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { - furi_assert(nfc_worker); - - MfClassicReader reader = {}; - FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); - - for(size_t i = 0; i < COUNT_OF(troyka_keys); i++) { - mf_classic_reader_add_sector( - &reader, troyka_keys[i].sector, troyka_keys[i].key_a, troyka_keys[i].key_b); - } - - return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16; -} - -bool troyka_parser_parse(NfcDeviceData* dev_data) { - MfClassicData* data = &dev_data->mf_classic_data; - bool troyka_parsed = false; - - do { - // Verify key - MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8); - uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); - if(key != troyka_keys[8].key_a) break; - - // Parse data - uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5]; - uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25; - temp_ptr = &data->block[8 * 4].value[3]; - uint32_t number = 0; - for(size_t i = 0; i < 4; i++) { - number <<= 8; - number |= temp_ptr[i]; - } - number >>= 4; - - string_printf( - dev_data->parsed_data, "\e#Troyka\nNum: %ld\nBalance: %d rur.", number, balance); - troyka_parsed = true; - } while(false); - - return troyka_parsed; -} diff --git a/lib/nfc/parsers/troyka_parser.h b/lib/nfc/parsers/troyka_parser.h deleted file mode 100644 index 445fe40e55a..00000000000 --- a/lib/nfc/parsers/troyka_parser.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "nfc_supported_card.h" - -bool troyka_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); - -bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); - -bool troyka_parser_parse(NfcDeviceData* dev_data); diff --git a/lib/nfc/protocols/crypto1.c b/lib/nfc/protocols/crypto1.c deleted file mode 100644 index f08164ba9f2..00000000000 --- a/lib/nfc/protocols/crypto1.c +++ /dev/null @@ -1,75 +0,0 @@ -#include "crypto1.h" -#include "nfc_util.h" -#include - -// Algorithm from https://github.com/RfidResearchGroup/proxmark3.git - -#define SWAPENDIAN(x) (x = (x >> 8 & 0xff00ff) | (x & 0xff00ff) << 8, x = x >> 16 | x << 16) -#define LF_POLY_ODD (0x29CE5C) -#define LF_POLY_EVEN (0x870804) - -#define BEBIT(x, n) FURI_BIT(x, (n) ^ 24) - -void crypto1_reset(Crypto1* crypto1) { - furi_assert(crypto1); - crypto1->even = 0; - crypto1->odd = 0; -} - -void crypto1_init(Crypto1* crypto1, uint64_t key) { - furi_assert(crypto1); - crypto1->even = 0; - crypto1->odd = 0; - for(int8_t i = 47; i > 0; i -= 2) { - crypto1->odd = crypto1->odd << 1 | FURI_BIT(key, (i - 1) ^ 7); - crypto1->even = crypto1->even << 1 | FURI_BIT(key, i ^ 7); - } -} - -uint32_t crypto1_filter(uint32_t in) { - uint32_t out = 0; - out = 0xf22c0 >> (in & 0xf) & 16; - out |= 0x6c9c0 >> (in >> 4 & 0xf) & 8; - out |= 0x3c8b0 >> (in >> 8 & 0xf) & 4; - out |= 0x1e458 >> (in >> 12 & 0xf) & 2; - out |= 0x0d938 >> (in >> 16 & 0xf) & 1; - return FURI_BIT(0xEC57E80A, out); -} - -uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted) { - furi_assert(crypto1); - uint8_t out = crypto1_filter(crypto1->odd); - uint32_t feed = out & (!!is_encrypted); - feed ^= !!in; - feed ^= LF_POLY_ODD & crypto1->odd; - feed ^= LF_POLY_EVEN & crypto1->even; - crypto1->even = crypto1->even << 1 | (nfc_util_even_parity32(feed)); - - FURI_SWAP(crypto1->odd, crypto1->even); - return out; -} - -uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted) { - furi_assert(crypto1); - uint8_t out = 0; - for(uint8_t i = 0; i < 8; i++) { - out |= crypto1_bit(crypto1, FURI_BIT(in, i), is_encrypted) << i; - } - return out; -} - -uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted) { - furi_assert(crypto1); - uint32_t out = 0; - for(uint8_t i = 0; i < 32; i++) { - out |= crypto1_bit(crypto1, BEBIT(in, i), is_encrypted) << (24 ^ i); - } - return out; -} - -uint32_t prng_successor(uint32_t x, uint32_t n) { - SWAPENDIAN(x); - while(n--) x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31; - - return SWAPENDIAN(x); -} diff --git a/lib/nfc/protocols/crypto1.h b/lib/nfc/protocols/crypto1.h deleted file mode 100644 index 07b39c22ce7..00000000000 --- a/lib/nfc/protocols/crypto1.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include - -typedef struct { - uint32_t odd; - uint32_t even; -} Crypto1; - -void crypto1_reset(Crypto1* crypto1); - -void crypto1_init(Crypto1* crypto1, uint64_t key); - -uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted); - -uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted); - -uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted); - -uint32_t crypto1_filter(uint32_t in); - -uint32_t prng_successor(uint32_t x, uint32_t n); diff --git a/lib/nfc/protocols/emv.c b/lib/nfc/protocols/emv.c deleted file mode 100644 index 935c9f6306d..00000000000 --- a/lib/nfc/protocols/emv.c +++ /dev/null @@ -1,429 +0,0 @@ -#include "emv.h" - -#include - -#define TAG "Emv" - -const PDOLValue pdol_term_info = {0x9F59, {0xC8, 0x80, 0x00}}; // Terminal transaction information -const PDOLValue pdol_term_type = {0x9F5A, {0x00}}; // Terminal transaction type -const PDOLValue pdol_merchant_type = {0x9F58, {0x01}}; // Merchant type indicator -const PDOLValue pdol_term_trans_qualifies = { - 0x9F66, - {0x79, 0x00, 0x40, 0x80}}; // Terminal transaction qualifiers -const PDOLValue pdol_addtnl_term_qualifies = { - 0x9F40, - {0x79, 0x00, 0x40, 0x80}}; // Terminal transaction qualifiers -const PDOLValue pdol_amount_authorise = { - 0x9F02, - {0x00, 0x00, 0x00, 0x10, 0x00, 0x00}}; // Amount, authorised -const PDOLValue pdol_amount = {0x9F03, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; // Amount -const PDOLValue pdol_country_code = {0x9F1A, {0x01, 0x24}}; // Terminal country code -const PDOLValue pdol_currency_code = {0x5F2A, {0x01, 0x24}}; // Transaction currency code -const PDOLValue pdol_term_verification = { - 0x95, - {0x00, 0x00, 0x00, 0x00, 0x00}}; // Terminal verification results -const PDOLValue pdol_transaction_date = {0x9A, {0x19, 0x01, 0x01}}; // Transaction date -const PDOLValue pdol_transaction_type = {0x9C, {0x00}}; // Transaction type -const PDOLValue pdol_transaction_cert = {0x98, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; // Transaction cert -const PDOLValue pdol_unpredict_number = {0x9F37, {0x82, 0x3D, 0xDE, 0x7A}}; // Unpredictable number - -const PDOLValue* const pdol_values[] = { - &pdol_term_info, - &pdol_term_type, - &pdol_merchant_type, - &pdol_term_trans_qualifies, - &pdol_addtnl_term_qualifies, - &pdol_amount_authorise, - &pdol_amount, - &pdol_country_code, - &pdol_currency_code, - &pdol_term_verification, - &pdol_transaction_date, - &pdol_transaction_type, - &pdol_transaction_cert, - &pdol_unpredict_number, -}; - -static const uint8_t select_ppse_ans[] = {0x6F, 0x29, 0x84, 0x0E, 0x32, 0x50, 0x41, 0x59, 0x2E, - 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31, - 0xA5, 0x17, 0xBF, 0x0C, 0x14, 0x61, 0x12, 0x4F, 0x07, - 0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x50, 0x04, - 0x56, 0x49, 0x53, 0x41, 0x87, 0x01, 0x01, 0x90, 0x00}; -static const uint8_t select_app_ans[] = {0x6F, 0x20, 0x84, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03, - 0x10, 0x10, 0xA5, 0x15, 0x50, 0x04, 0x56, 0x49, 0x53, - 0x41, 0x9F, 0x38, 0x0C, 0x9F, 0x66, 0x04, 0x9F, 0x02, - 0x06, 0x9F, 0x37, 0x04, 0x5F, 0x2A, 0x02, 0x90, 0x00}; -static const uint8_t pdol_ans[] = {0x77, 0x40, 0x82, 0x02, 0x20, 0x00, 0x57, 0x13, 0x55, 0x70, - 0x73, 0x83, 0x85, 0x87, 0x73, 0x31, 0xD1, 0x80, 0x22, 0x01, - 0x38, 0x84, 0x77, 0x94, 0x00, 0x00, 0x1F, 0x5F, 0x34, 0x01, - 0x00, 0x9F, 0x10, 0x07, 0x06, 0x01, 0x11, 0x03, 0x80, 0x00, - 0x00, 0x9F, 0x26, 0x08, 0x7A, 0x65, 0x7F, 0xD3, 0x52, 0x96, - 0xC9, 0x85, 0x9F, 0x27, 0x01, 0x00, 0x9F, 0x36, 0x02, 0x06, - 0x0C, 0x9F, 0x6C, 0x02, 0x10, 0x00, 0x90, 0x00}; - -static void emv_trace(FuriHalNfcTxRxContext* tx_rx, const char* message) { - if(furi_log_get_level() == FuriLogLevelTrace) { - FURI_LOG_T(TAG, "%s", message); - printf("TX: "); - for(size_t i = 0; i < tx_rx->tx_bits / 8; i++) { - printf("%02X ", tx_rx->tx_data[i]); - } - printf("\r\nRX: "); - for(size_t i = 0; i < tx_rx->rx_bits / 8; i++) { - printf("%02X ", tx_rx->rx_data[i]); - } - printf("\r\n"); - } -} - -static bool emv_decode_response(uint8_t* buff, uint16_t len, EmvApplication* app) { - uint16_t i = 0; - uint16_t tag = 0, first_byte = 0; - uint16_t tlen = 0; - bool success = false; - - while(i < len) { - first_byte = buff[i]; - if((first_byte & 31) == 31) { // 2-byte tag - tag = buff[i] << 8 | buff[i + 1]; - i++; - FURI_LOG_T(TAG, " 2-byte TLV EMV tag: %x", tag); - } else { - tag = buff[i]; - FURI_LOG_T(TAG, " 1-byte TLV EMV tag: %x", tag); - } - i++; - tlen = buff[i]; - if((tlen & 128) == 128) { // long length value - i++; - tlen = buff[i]; - FURI_LOG_T(TAG, " 2-byte TLV length: %d", tlen); - } else { - FURI_LOG_T(TAG, " 1-byte TLV length: %d", tlen); - } - i++; - if((first_byte & 32) == 32) { // "Constructed" -- contains more TLV data to parse - FURI_LOG_T(TAG, "Constructed TLV %x", tag); - if(!emv_decode_response(&buff[i], tlen, app)) { - FURI_LOG_T(TAG, "Failed to decode response for %x", tag); - // return false; - } else { - success = true; - } - } else { - switch(tag) { - case EMV_TAG_AID: - app->aid_len = tlen; - memcpy(app->aid, &buff[i], tlen); - success = true; - FURI_LOG_T(TAG, "found EMV_TAG_AID %x", tag); - break; - case EMV_TAG_PRIORITY: - memcpy(&app->priority, &buff[i], tlen); - success = true; - break; - case EMV_TAG_CARD_NAME: - memcpy(app->name, &buff[i], tlen); - app->name[tlen] = '\0'; - app->name_found = true; - success = true; - FURI_LOG_T(TAG, "found EMV_TAG_CARD_NAME %x : %s", tag, app->name); - break; - case EMV_TAG_PDOL: - memcpy(app->pdol.data, &buff[i], tlen); - app->pdol.size = tlen; - success = true; - FURI_LOG_T(TAG, "found EMV_TAG_PDOL %x (len=%d)", tag, tlen); - break; - case EMV_TAG_AFL: - memcpy(app->afl.data, &buff[i], tlen); - app->afl.size = tlen; - success = true; - FURI_LOG_T(TAG, "found EMV_TAG_AFL %x (len=%d)", tag, tlen); - break; - case EMV_TAG_CARD_NUM: // Track 2 Equivalent Data. 0xD0 delimits PAN from expiry (YYMM) - for(int x = 1; x < tlen; x++) { - if(buff[i + x + 1] > 0xD0) { - memcpy(app->card_number, &buff[i], x + 1); - app->card_number_len = x + 1; - break; - } - } - success = true; - FURI_LOG_T( - TAG, - "found EMV_TAG_CARD_NUM %x (len=%d)", - EMV_TAG_CARD_NUM, - app->card_number_len); - break; - case EMV_TAG_PAN: - memcpy(app->card_number, &buff[i], tlen); - app->card_number_len = tlen; - success = true; - break; - case EMV_TAG_EXP_DATE: - app->exp_year = buff[i]; - app->exp_month = buff[i + 1]; - success = true; - break; - case EMV_TAG_CURRENCY_CODE: - app->currency_code = (buff[i] << 8 | buff[i + 1]); - success = true; - break; - case EMV_TAG_COUNTRY_CODE: - app->country_code = (buff[i] << 8 | buff[i + 1]); - success = true; - break; - } - } - i += tlen; - } - return success; -} - -bool emv_select_ppse(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) { - bool app_aid_found = false; - const uint8_t emv_select_ppse_cmd[] = { - 0x00, 0xA4, // SELECT ppse - 0x04, 0x00, // P1:By name, P2: empty - 0x0e, // Lc: Data length - 0x32, 0x50, 0x41, 0x59, 0x2e, 0x53, 0x59, // Data string: - 0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, // 2PAY.SYS.DDF01 (PPSE) - 0x00 // Le - }; - - memcpy(tx_rx->tx_data, emv_select_ppse_cmd, sizeof(emv_select_ppse_cmd)); - tx_rx->tx_bits = sizeof(emv_select_ppse_cmd) * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - - FURI_LOG_D(TAG, "Send select PPSE"); - if(furi_hal_nfc_tx_rx(tx_rx, 300)) { - emv_trace(tx_rx, "Select PPSE answer:"); - if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) { - app_aid_found = true; - } else { - FURI_LOG_E(TAG, "Failed to parse application"); - } - } else { - FURI_LOG_E(TAG, "Failed select PPSE"); - } - - return app_aid_found; -} - -bool emv_select_app(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) { - bool select_app_success = false; - const uint8_t emv_select_header[] = { - 0x00, - 0xA4, // SELECT application - 0x04, - 0x00 // P1:By name, P2:First or only occurence - }; - uint16_t size = sizeof(emv_select_header); - - // Copy header - memcpy(tx_rx->tx_data, emv_select_header, size); - // Copy AID - tx_rx->tx_data[size++] = app->aid_len; - memcpy(&tx_rx->tx_data[size], app->aid, app->aid_len); - size += app->aid_len; - tx_rx->tx_data[size++] = 0x00; - tx_rx->tx_bits = size * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - - FURI_LOG_D(TAG, "Start application"); - if(furi_hal_nfc_tx_rx(tx_rx, 300)) { - emv_trace(tx_rx, "Start application answer:"); - if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) { - select_app_success = true; - } else { - FURI_LOG_E(TAG, "Failed to read PAN or PDOL"); - } - } else { - FURI_LOG_E(TAG, "Failed to start application"); - } - - return select_app_success; -} - -static uint16_t emv_prepare_pdol(APDU* dest, APDU* src) { - bool tag_found; - for(uint16_t i = 0; i < src->size; i++) { - tag_found = false; - for(uint8_t j = 0; j < sizeof(pdol_values) / sizeof(PDOLValue*); j++) { - if(src->data[i] == pdol_values[j]->tag) { - // Found tag with 1 byte length - uint8_t len = src->data[++i]; - memcpy(dest->data + dest->size, pdol_values[j]->data, len); - dest->size += len; - tag_found = true; - break; - } else if(((src->data[i] << 8) | src->data[i + 1]) == pdol_values[j]->tag) { - // Found tag with 2 byte length - i += 2; - uint8_t len = src->data[i]; - memcpy(dest->data + dest->size, pdol_values[j]->data, len); - dest->size += len; - tag_found = true; - break; - } - } - if(!tag_found) { - // Unknown tag, fill zeros - i += 2; - uint8_t len = src->data[i]; - memset(dest->data + dest->size, 0, len); - dest->size += len; - } - } - return dest->size; -} - -static bool emv_get_processing_options(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) { - bool card_num_read = false; - const uint8_t emv_gpo_header[] = {0x80, 0xA8, 0x00, 0x00}; - uint16_t size = sizeof(emv_gpo_header); - - // Copy header - memcpy(tx_rx->tx_data, emv_gpo_header, size); - APDU pdol_data = {0, {0}}; - // Prepare and copy pdol parameters - emv_prepare_pdol(&pdol_data, &app->pdol); - tx_rx->tx_data[size++] = 0x02 + pdol_data.size; - tx_rx->tx_data[size++] = 0x83; - tx_rx->tx_data[size++] = pdol_data.size; - memcpy(tx_rx->tx_data + size, pdol_data.data, pdol_data.size); - size += pdol_data.size; - tx_rx->tx_data[size++] = 0; - tx_rx->tx_bits = size * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - - FURI_LOG_D(TAG, "Get proccessing options"); - if(furi_hal_nfc_tx_rx(tx_rx, 300)) { - emv_trace(tx_rx, "Get processing options answer:"); - if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) { - if(app->card_number_len > 0) { - card_num_read = true; - } - } - } else { - FURI_LOG_E(TAG, "Failed to get processing options"); - } - - return card_num_read; -} - -static bool emv_read_sfi_record( - FuriHalNfcTxRxContext* tx_rx, - EmvApplication* app, - uint8_t sfi, - uint8_t record_num) { - bool card_num_read = false; - uint8_t sfi_param = (sfi << 3) | (1 << 2); - uint8_t emv_sfi_header[] = { - 0x00, - 0xB2, // READ RECORD - record_num, // P1:record_number - sfi_param, // P2:SFI - 0x00 // Le - }; - - memcpy(tx_rx->tx_data, emv_sfi_header, sizeof(emv_sfi_header)); - tx_rx->tx_bits = sizeof(emv_sfi_header) * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - - if(furi_hal_nfc_tx_rx(tx_rx, 300)) { - emv_trace(tx_rx, "SFI record:"); - if(emv_decode_response(tx_rx->rx_data, tx_rx->rx_bits / 8, app)) { - card_num_read = true; - } - } else { - FURI_LOG_E(TAG, "Failed to read SFI record %d", record_num); - } - - return card_num_read; -} - -static bool emv_read_files(FuriHalNfcTxRxContext* tx_rx, EmvApplication* app) { - bool card_num_read = false; - - if(app->afl.size == 0) { - return false; - } - - FURI_LOG_D(TAG, "Search PAN in SFI"); - // Iterate through all files - for(size_t i = 0; i < app->afl.size; i += 4) { - uint8_t sfi = app->afl.data[i] >> 3; - uint8_t record_start = app->afl.data[i + 1]; - uint8_t record_end = app->afl.data[i + 2]; - // Iterate through all records in file - for(uint8_t record = record_start; record <= record_end; ++record) { - card_num_read |= emv_read_sfi_record(tx_rx, app, sfi, record); - } - } - - return card_num_read; -} - -bool emv_search_application(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app) { - furi_assert(tx_rx); - furi_assert(emv_app); - memset(emv_app, 0, sizeof(EmvApplication)); - - return emv_select_ppse(tx_rx, emv_app); -} - -bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app) { - furi_assert(tx_rx); - furi_assert(emv_app); - bool card_num_read = false; - memset(emv_app, 0, sizeof(EmvApplication)); - - do { - if(!emv_select_ppse(tx_rx, emv_app)) break; - if(!emv_select_app(tx_rx, emv_app)) break; - if(emv_get_processing_options(tx_rx, emv_app)) { - card_num_read = true; - } else { - card_num_read = emv_read_files(tx_rx, emv_app); - } - } while(false); - - return card_num_read; -} - -bool emv_card_emulation(FuriHalNfcTxRxContext* tx_rx) { - furi_assert(tx_rx); - bool emulation_complete = false; - tx_rx->tx_bits = 0; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - - do { - FURI_LOG_D(TAG, "Read select PPSE command"); - if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break; - - memcpy(tx_rx->tx_data, select_ppse_ans, sizeof(select_ppse_ans)); - tx_rx->tx_bits = sizeof(select_ppse_ans) * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - FURI_LOG_D(TAG, "Send select PPSE answer and read select App command"); - if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break; - - memcpy(tx_rx->tx_data, select_app_ans, sizeof(select_app_ans)); - tx_rx->tx_bits = sizeof(select_app_ans) * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - FURI_LOG_D(TAG, "Send select App answer and read get PDOL command"); - if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break; - - memcpy(tx_rx->tx_data, pdol_ans, sizeof(pdol_ans)); - tx_rx->tx_bits = sizeof(pdol_ans) * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - FURI_LOG_D(TAG, "Send get PDOL answer"); - if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break; - - emulation_complete = true; - } while(false); - - return emulation_complete; -} diff --git a/lib/nfc/protocols/emv.h b/lib/nfc/protocols/emv.h deleted file mode 100755 index b5a0c574f07..00000000000 --- a/lib/nfc/protocols/emv.h +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -#include - -#define MAX_APDU_LEN 255 - -#define EMV_TAG_APP_TEMPLATE 0x61 -#define EMV_TAG_AID 0x4F -#define EMV_TAG_PRIORITY 0x87 -#define EMV_TAG_PDOL 0x9F38 -#define EMV_TAG_CARD_NAME 0x50 -#define EMV_TAG_FCI 0xBF0C -#define EMV_TAG_LOG_CTRL 0x9F4D -#define EMV_TAG_CARD_NUM 0x57 -#define EMV_TAG_PAN 0x5A -#define EMV_TAG_AFL 0x94 -#define EMV_TAG_EXP_DATE 0x5F24 -#define EMV_TAG_COUNTRY_CODE 0x5F28 -#define EMV_TAG_CURRENCY_CODE 0x9F42 -#define EMV_TAG_CARDHOLDER_NAME 0x5F20 - -typedef struct { - char name[32]; - uint8_t aid[16]; - uint16_t aid_len; - uint8_t number[10]; - uint8_t number_len; - uint8_t exp_mon; - uint8_t exp_year; - uint16_t country_code; - uint16_t currency_code; -} EmvData; - -typedef struct { - uint16_t tag; - uint8_t data[]; -} PDOLValue; - -typedef struct { - uint8_t size; - uint8_t data[MAX_APDU_LEN]; -} APDU; - -typedef struct { - uint8_t priority; - uint8_t aid[16]; - uint8_t aid_len; - char name[32]; - bool name_found; - uint8_t card_number[10]; - uint8_t card_number_len; - uint8_t exp_month; - uint8_t exp_year; - uint16_t country_code; - uint16_t currency_code; - APDU pdol; - APDU afl; -} EmvApplication; - -/** Read bank card data - * @note Search EMV Application, start it, try to read AID, PAN, card name, - * expiration date, currency and country codes - * - * @param tx_rx FuriHalNfcTxRxContext instance - * @param emv_app EmvApplication instance - * - * @return true on success - */ -bool emv_read_bank_card(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app); - -/** Search for EMV Application - * - * @param tx_rx FuriHalNfcTxRxContext instance - * @param emv_app EmvApplication instance - * - * @return true on success - */ -bool emv_search_application(FuriHalNfcTxRxContext* tx_rx, EmvApplication* emv_app); - -/** Emulate bank card - * @note Answer to application selection and PDOL - * - * @param tx_rx FuriHalNfcTxRxContext instance - * - * @return true on success - */ -bool emv_card_emulation(FuriHalNfcTxRxContext* tx_rx); diff --git a/lib/nfc/protocols/felica/felica.c b/lib/nfc/protocols/felica/felica.c new file mode 100644 index 00000000000..4b7c91fb64e --- /dev/null +++ b/lib/nfc/protocols/felica/felica.c @@ -0,0 +1,147 @@ +#include "felica.h" + +#include + +#include + +#define FELICA_PROTOCOL_NAME "FeliCa" +#define FELICA_DEVICE_NAME "FeliCa" + +#define FELICA_DATA_FORMAT_VERSION "Data format version" +#define FELICA_MANUFACTURE_ID "Manufacture id" +#define FELICA_MANUFACTURE_PARAMETER "Manufacture parameter" + +static const uint32_t felica_data_format_version = 1; + +const NfcDeviceBase nfc_device_felica = { + .protocol_name = FELICA_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)felica_alloc, + .free = (NfcDeviceFree)felica_free, + .reset = (NfcDeviceReset)felica_reset, + .copy = (NfcDeviceCopy)felica_copy, + .verify = (NfcDeviceVerify)felica_verify, + .load = (NfcDeviceLoad)felica_load, + .save = (NfcDeviceSave)felica_save, + .is_equal = (NfcDeviceEqual)felica_is_equal, + .get_name = (NfcDeviceGetName)felica_get_device_name, + .get_uid = (NfcDeviceGetUid)felica_get_uid, + .set_uid = (NfcDeviceSetUid)felica_set_uid, + .get_base_data = (NfcDeviceGetBaseData)felica_get_base_data, +}; + +FelicaData* felica_alloc() { + FelicaData* data = malloc(sizeof(FelicaData)); + return data; +} + +void felica_free(FelicaData* data) { + furi_assert(data); + + free(data); +} + +void felica_reset(FelicaData* data) { + memset(data, 0, sizeof(FelicaData)); +} + +void felica_copy(FelicaData* data, const FelicaData* other) { + furi_assert(data); + furi_assert(other); + + *data = *other; +} + +bool felica_verify(FelicaData* data, const FuriString* device_type) { + UNUSED(data); + UNUSED(device_type); + + return false; +} + +bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + + bool parsed = false; + + do { + if(version < NFC_UNIFIED_FORMAT_VERSION) break; + + uint32_t data_format_version = 0; + if(!flipper_format_read_uint32(ff, FELICA_DATA_FORMAT_VERSION, &data_format_version, 1)) + break; + if(data_format_version != felica_data_format_version) break; + if(!flipper_format_read_hex(ff, FELICA_MANUFACTURE_ID, data->idm.data, FELICA_IDM_SIZE)) + break; + if(!flipper_format_read_hex( + ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE)) + break; + + parsed = true; + } while(false); + + return parsed; +} + +bool felica_save(const FelicaData* data, FlipperFormat* ff) { + furi_assert(data); + + bool saved = false; + + do { + if(!flipper_format_write_comment_cstr(ff, FELICA_PROTOCOL_NAME " specific data")) break; + if(!flipper_format_write_uint32( + ff, FELICA_DATA_FORMAT_VERSION, &felica_data_format_version, 1)) + break; + if(!flipper_format_write_hex(ff, FELICA_MANUFACTURE_ID, data->idm.data, FELICA_IDM_SIZE)) + break; + if(!flipper_format_write_hex( + ff, FELICA_MANUFACTURE_PARAMETER, data->pmm.data, FELICA_PMM_SIZE)) + break; + + saved = true; + } while(false); + + return saved; +} + +bool felica_is_equal(const FelicaData* data, const FelicaData* other) { + furi_assert(data); + furi_assert(other); + + return memcmp(data, other, sizeof(FelicaData)) == 0; +} + +const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType name_type) { + UNUSED(data); + UNUSED(name_type); + + return FELICA_DEVICE_NAME; +} + +const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len) { + furi_assert(data); + + // Consider Manufacturer ID as UID + if(uid_len) { + *uid_len = FELICA_IDM_SIZE; + } + + return data->idm.data; +} + +bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + // Consider Manufacturer ID as UID + const bool uid_valid = uid_len == FELICA_IDM_SIZE; + if(uid_valid) { + memcpy(data->idm.data, uid, uid_len); + } + + return uid_valid; +} + +FelicaData* felica_get_base_data(const FelicaData* data) { + UNUSED(data); + furi_crash("No base data"); +} diff --git a/lib/nfc/protocols/felica/felica.h b/lib/nfc/protocols/felica/felica.h new file mode 100644 index 00000000000..31e040b8a1e --- /dev/null +++ b/lib/nfc/protocols/felica/felica.h @@ -0,0 +1,79 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define FELICA_IDM_SIZE (8U) +#define FELICA_PMM_SIZE (8U) + +#define FELICA_GUARD_TIME_US (20000U) +#define FELICA_FDT_POLL_FC (10000U) +#define FELICA_POLL_POLL_MIN_US (1280U) + +#define FELICA_FDT_LISTEN_FC (1172) + +#define FELICA_SYSTEM_CODE_CODE (0xFFFFU) +#define FELICA_TIME_SLOT_1 (0x00U) +#define FELICA_TIME_SLOT_2 (0x01U) +#define FELICA_TIME_SLOT_4 (0x03U) +#define FELICA_TIME_SLOT_8 (0x07U) +#define FELICA_TIME_SLOT_16 (0x0FU) + +typedef enum { + FelicaErrorNone, + FelicaErrorNotPresent, + FelicaErrorColResFailed, + FelicaErrorBufferOverflow, + FelicaErrorCommunication, + FelicaErrorFieldOff, + FelicaErrorWrongCrc, + FelicaErrorProtocol, + FelicaErrorTimeout, +} FelicaError; + +typedef struct { + uint8_t data[FELICA_IDM_SIZE]; +} FelicaIDm; + +typedef struct { + uint8_t data[FELICA_PMM_SIZE]; +} FelicaPMm; + +typedef struct { + FelicaIDm idm; + FelicaPMm pmm; +} FelicaData; + +extern const NfcDeviceBase nfc_device_felica; + +FelicaData* felica_alloc(); + +void felica_free(FelicaData* data); + +void felica_reset(FelicaData* data); + +void felica_copy(FelicaData* data, const FelicaData* other); + +bool felica_verify(FelicaData* data, const FuriString* device_type); + +bool felica_load(FelicaData* data, FlipperFormat* ff, uint32_t version); + +bool felica_save(const FelicaData* data, FlipperFormat* ff); + +bool felica_is_equal(const FelicaData* data, const FelicaData* other); + +const char* felica_get_device_name(const FelicaData* data, NfcDeviceNameType name_type); + +const uint8_t* felica_get_uid(const FelicaData* data, size_t* uid_len); + +bool felica_set_uid(FelicaData* data, const uint8_t* uid, size_t uid_len); + +FelicaData* felica_get_base_data(const FelicaData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/felica/felica_listener.c b/lib/nfc/protocols/felica/felica_listener.c new file mode 100644 index 00000000000..4e6c0578547 --- /dev/null +++ b/lib/nfc/protocols/felica/felica_listener.c @@ -0,0 +1,79 @@ +#include "felica_listener_i.h" + +#include "nfc/protocols/nfc_listener_base.h" + +#define FELICA_LISTENER_MAX_BUFFER_SIZE (64) +#define TAG "Felica" + +FelicaListener* felica_listener_alloc(Nfc* nfc, FelicaData* data) { + furi_assert(nfc); + furi_assert(data); + + FelicaListener* instance = malloc(sizeof(FelicaListener)); + instance->nfc = nfc; + instance->data = data; + instance->tx_buffer = bit_buffer_alloc(FELICA_LISTENER_MAX_BUFFER_SIZE); + instance->rx_buffer = bit_buffer_alloc(FELICA_LISTENER_MAX_BUFFER_SIZE); + + nfc_set_fdt_listen_fc(instance->nfc, FELICA_FDT_LISTEN_FC); + + nfc_config(instance->nfc, NfcModeListener, NfcTechFelica); + nfc_felica_listener_set_sensf_res_data( + nfc, data->idm.data, sizeof(data->idm), data->pmm.data, sizeof(data->pmm)); + + return instance; +} + +void felica_listener_free(FelicaListener* instance) { + furi_assert(instance); + furi_assert(instance->tx_buffer); + + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + free(instance); +} + +void felica_listener_set_callback( + FelicaListener* listener, + NfcGenericCallback callback, + void* context) { + UNUSED(listener); + UNUSED(callback); + UNUSED(context); +} + +const FelicaData* felica_listener_get_data(const FelicaListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +NfcCommand felica_listener_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolInvalid); + furi_assert(event.event_data); + + FelicaListener* instance = context; + NfcEvent* nfc_event = event.event_data; + NfcCommand command = NfcCommandContinue; + + if(nfc_event->type == NfcEventTypeListenerActivated) { + instance->state = Felica_ListenerStateActivated; + FURI_LOG_D(TAG, "Activated"); + } else if(nfc_event->type == NfcEventTypeFieldOff) { + instance->state = Felica_ListenerStateIdle; + FURI_LOG_D(TAG, "Field Off"); + } else if(nfc_event->type == NfcEventTypeRxEnd) { + FURI_LOG_D(TAG, "Rx Done"); + } + return command; +} + +const NfcListenerBase nfc_listener_felica = { + .alloc = (NfcListenerAlloc)felica_listener_alloc, + .free = (NfcListenerFree)felica_listener_free, + .set_callback = (NfcListenerSetCallback)felica_listener_set_callback, + .get_data = (NfcListenerGetData)felica_listener_get_data, + .run = (NfcListenerRun)felica_listener_run, +}; \ No newline at end of file diff --git a/lib/nfc/protocols/felica/felica_listener.h b/lib/nfc/protocols/felica/felica_listener.h new file mode 100644 index 00000000000..d210befa574 --- /dev/null +++ b/lib/nfc/protocols/felica/felica_listener.h @@ -0,0 +1,14 @@ +#pragma once + +#include "felica.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct FelicaListener FelicaListener; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/nfc/protocols/felica/felica_listener_defs.h b/lib/nfc/protocols/felica/felica_listener_defs.h new file mode 100644 index 00000000000..19b252be59e --- /dev/null +++ b/lib/nfc/protocols/felica/felica_listener_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcListenerBase nfc_listener_felica; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/felica/felica_listener_i.h b/lib/nfc/protocols/felica/felica_listener_i.h new file mode 100644 index 00000000000..4fa25a16209 --- /dev/null +++ b/lib/nfc/protocols/felica/felica_listener_i.h @@ -0,0 +1,21 @@ +#include "felica_listener.h" + +#include + +typedef enum { + Felica_ListenerStateIdle, + Felica_ListenerStateActivated, +} FelicaListenerState; + +struct FelicaListener { + Nfc* nfc; + FelicaData* data; + FelicaListenerState state; + + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + + NfcGenericEvent generic_event; + NfcGenericCallback callback; + void* context; +}; \ No newline at end of file diff --git a/lib/nfc/protocols/felica/felica_poller.c b/lib/nfc/protocols/felica/felica_poller.c new file mode 100644 index 00000000000..23b1604e19b --- /dev/null +++ b/lib/nfc/protocols/felica/felica_poller.c @@ -0,0 +1,117 @@ +#include "felica_poller_i.h" + +#include + +#include + +const FelicaData* felica_poller_get_data(FelicaPoller* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +static FelicaPoller* felica_poller_alloc(Nfc* nfc) { + furi_assert(nfc); + + FelicaPoller* instance = malloc(sizeof(FelicaPoller)); + instance->nfc = nfc; + instance->tx_buffer = bit_buffer_alloc(FELICA_POLLER_MAX_BUFFER_SIZE); + instance->rx_buffer = bit_buffer_alloc(FELICA_POLLER_MAX_BUFFER_SIZE); + + nfc_config(instance->nfc, NfcModePoller, NfcTechFelica); + nfc_set_guard_time_us(instance->nfc, FELICA_GUARD_TIME_US); + nfc_set_fdt_poll_fc(instance->nfc, FELICA_FDT_POLL_FC); + nfc_set_fdt_poll_poll_us(instance->nfc, FELICA_POLL_POLL_MIN_US); + instance->data = felica_alloc(); + + instance->felica_event.data = &instance->felica_event_data; + instance->general_event.protocol = NfcProtocolFelica; + instance->general_event.event_data = &instance->felica_event; + instance->general_event.instance = instance; + + return instance; +} + +static void felica_poller_free(FelicaPoller* instance) { + furi_assert(instance); + + furi_assert(instance->tx_buffer); + furi_assert(instance->rx_buffer); + furi_assert(instance->data); + + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + felica_free(instance->data); + free(instance); +} + +static void + felica_poller_set_callback(FelicaPoller* instance, NfcGenericCallback callback, void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand felica_poller_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolInvalid); + furi_assert(event.event_data); + + FelicaPoller* instance = context; + NfcEvent* nfc_event = event.event_data; + NfcCommand command = NfcCommandContinue; + + if(nfc_event->type == NfcEventTypePollerReady) { + if(instance->state != FelicaPollerStateActivated) { + FelicaError error = felica_poller_activate(instance, instance->data); + if(error == FelicaErrorNone) { + instance->felica_event.type = FelicaPollerEventTypeReady; + instance->felica_event_data.error = error; + command = instance->callback(instance->general_event, instance->context); + } else { + instance->felica_event.type = FelicaPollerEventTypeError; + instance->felica_event_data.error = error; + command = instance->callback(instance->general_event, instance->context); + // Add delay to switch context + furi_delay_ms(100); + } + } else { + instance->felica_event.type = FelicaPollerEventTypeReady; + instance->felica_event_data.error = FelicaErrorNone; + command = instance->callback(instance->general_event, instance->context); + } + } + + return command; +} + +static bool felica_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.instance); + furi_assert(event.protocol == NfcProtocolInvalid); + + bool protocol_detected = false; + FelicaPoller* instance = context; + NfcEvent* nfc_event = event.event_data; + furi_assert(instance->state == FelicaPollerStateIdle); + + if(nfc_event->type == NfcEventTypePollerReady) { + FelicaError error = felica_poller_activate(instance, instance->data); + protocol_detected = (error == FelicaErrorNone); + } + + return protocol_detected; +} + +const NfcPollerBase nfc_poller_felica = { + .alloc = (NfcPollerAlloc)felica_poller_alloc, + .free = (NfcPollerFree)felica_poller_free, + .set_callback = (NfcPollerSetCallback)felica_poller_set_callback, + .run = (NfcPollerRun)felica_poller_run, + .detect = (NfcPollerDetect)felica_poller_detect, + .get_data = (NfcPollerGetData)felica_poller_get_data, +}; diff --git a/lib/nfc/protocols/felica/felica_poller.h b/lib/nfc/protocols/felica/felica_poller.h new file mode 100644 index 00000000000..45fd9a9a1fc --- /dev/null +++ b/lib/nfc/protocols/felica/felica_poller.h @@ -0,0 +1,58 @@ +#pragma once + +#include "felica.h" +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief FelicaPoller opaque type definition. + */ +typedef struct FelicaPoller FelicaPoller; + +/** + * @brief Enumeration of possible Felica poller event types. + */ +typedef enum { + FelicaPollerEventTypeError, /**< The card was activated by the poller. */ + FelicaPollerEventTypeReady, /**< An error occured during activation procedure. */ +} FelicaPollerEventType; + +/** + * @brief Felica poller event data. + */ +typedef union { + FelicaError error; /**< Error code indicating card activation fail reason. */ +} FelicaPollerEventData; + +/** + * @brief FelicaPoller poller event structure. + * + * Upon emission of an event, an instance of this struct will be passed to the callback. + */ +typedef struct { + FelicaPollerEventType type; /**< Type of emmitted event. */ + FelicaPollerEventData* data; /**< Pointer to event specific data. */ +} FelicaPollerEvent; + +/** + * @brief Perform collision resolution procedure. + * + * Must ONLY be used inside the callback function. + * + * Perfoms the collision resolution procedure as defined in FeliCa standars. The data + * field will be filled with Felica data on success. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the Felica data structure to be filled. + * @return FelicaErrorNone on success, an error code on failure. + */ +FelicaError felica_poller_activate(FelicaPoller* instance, FelicaData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/felica/felica_poller_defs.h b/lib/nfc/protocols/felica/felica_poller_defs.h new file mode 100644 index 00000000000..fc99dc75219 --- /dev/null +++ b/lib/nfc/protocols/felica/felica_poller_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcPollerBase nfc_poller_felica; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/felica/felica_poller_i.c b/lib/nfc/protocols/felica/felica_poller_i.c new file mode 100644 index 00000000000..bfbf150ef98 --- /dev/null +++ b/lib/nfc/protocols/felica/felica_poller_i.c @@ -0,0 +1,128 @@ +#include "felica_poller_i.h" + +#include + +#define TAG "FelicaPoller" + +static FelicaError felica_poller_process_error(NfcError error) { + switch(error) { + case NfcErrorNone: + return FelicaErrorNone; + case NfcErrorTimeout: + return FelicaErrorTimeout; + default: + return FelicaErrorNotPresent; + } +} + +static FelicaError felica_poller_frame_exchange( + FelicaPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + + const size_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer); + furi_assert(tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - FELICA_CRC_SIZE); + + felica_crc_append(instance->tx_buffer); + + FelicaError ret = FelicaErrorNone; + + do { + NfcError error = + nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt); + if(error != NfcErrorNone) { + ret = felica_poller_process_error(error); + break; + } + + bit_buffer_copy(rx_buffer, instance->rx_buffer); + if(!felica_crc_check(instance->rx_buffer)) { + ret = FelicaErrorWrongCrc; + break; + } + + felica_crc_trim(rx_buffer); + } while(false); + + return ret; +} + +FelicaError felica_poller_polling( + FelicaPoller* instance, + const FelicaPollerPollingCommand* cmd, + FelicaPollerPollingResponse* resp) { + furi_assert(instance); + furi_assert(cmd); + furi_assert(resp); + + FelicaError error = FelicaErrorNone; + + do { + bit_buffer_set_size_bytes(instance->tx_buffer, 2); + // Set frame len + bit_buffer_set_byte( + instance->tx_buffer, 0, sizeof(FelicaPollerPollingCommand) + FELICA_CRC_SIZE); + // Set command code + bit_buffer_set_byte(instance->tx_buffer, 1, FELICA_POLLER_CMD_POLLING_REQ_CODE); + // Set other data + bit_buffer_append_bytes( + instance->tx_buffer, (uint8_t*)cmd, sizeof(FelicaPollerPollingCommand)); + + error = felica_poller_frame_exchange( + instance, instance->tx_buffer, instance->rx_buffer, FELICA_POLLER_POLLING_FWT); + + if(error != FelicaErrorNone) break; + if(bit_buffer_get_byte(instance->rx_buffer, 1) != FELICA_POLLER_CMD_POLLING_RESP_CODE) { + error = FelicaErrorProtocol; + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) < + sizeof(FelicaIDm) + sizeof(FelicaPMm) + 1) { + error = FelicaErrorProtocol; + break; + } + + bit_buffer_write_bytes_mid(instance->rx_buffer, resp->idm.data, 2, sizeof(FelicaIDm)); + bit_buffer_write_bytes_mid( + instance->rx_buffer, resp->pmm.data, sizeof(FelicaIDm) + 2, sizeof(FelicaPMm)); + + } while(false); + + return error; +} + +FelicaError felica_poller_activate(FelicaPoller* instance, FelicaData* data) { + furi_assert(instance); + + felica_reset(data); + + FelicaError ret; + + do { + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + // Send Polling command + const FelicaPollerPollingCommand polling_cmd = { + .system_code = FELICA_SYSTEM_CODE_CODE, + .request_code = 0, + .time_slot = FELICA_TIME_SLOT_1, + }; + FelicaPollerPollingResponse polling_resp = {}; + + ret = felica_poller_polling(instance, &polling_cmd, &polling_resp); + + if(ret != FelicaErrorNone) { + FURI_LOG_T(TAG, "Activation failed error: %d", ret); + break; + } + + data->idm = polling_resp.idm; + data->pmm = polling_resp.pmm; + instance->state = FelicaPollerStateActivated; + } while(false); + + return ret; +} diff --git a/lib/nfc/protocols/felica/felica_poller_i.h b/lib/nfc/protocols/felica/felica_poller_i.h new file mode 100644 index 00000000000..3bd4d91f9f9 --- /dev/null +++ b/lib/nfc/protocols/felica/felica_poller_i.h @@ -0,0 +1,58 @@ +#pragma once + +#include "felica_poller.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define FELICA_POLLER_MAX_BUFFER_SIZE (256U) + +#define FELICA_POLLER_POLLING_FWT (200000U) + +#define FELICA_POLLER_CMD_POLLING_REQ_CODE (0x00U) +#define FELICA_POLLER_CMD_POLLING_RESP_CODE (0x01U) + +typedef enum { + FelicaPollerStateIdle, + FelicaPollerStateActivated, +} FelicaPollerState; + +struct FelicaPoller { + Nfc* nfc; + FelicaPollerState state; + FelicaData* data; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + + NfcGenericEvent general_event; + FelicaPollerEvent felica_event; + FelicaPollerEventData felica_event_data; + NfcGenericCallback callback; + void* context; +}; + +typedef struct { + uint16_t system_code; + uint8_t request_code; + uint8_t time_slot; +} FelicaPollerPollingCommand; + +typedef struct { + FelicaIDm idm; + FelicaPMm pmm; + uint8_t request_data[2]; +} FelicaPollerPollingResponse; + +const FelicaData* felica_poller_get_data(FelicaPoller* instance); + +FelicaError felica_poller_polling( + FelicaPoller* instance, + const FelicaPollerPollingCommand* cmd, + FelicaPollerPollingResponse* resp); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a.c b/lib/nfc/protocols/iso14443_3a/iso14443_3a.c new file mode 100644 index 00000000000..96d0b39537e --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a.c @@ -0,0 +1,186 @@ +#include "iso14443_3a.h" + +#include +#include + +#define ISO14443A_ATS_BIT (1U << 5) + +#define ISO14443_3A_PROTOCOL_NAME_LEGACY "UID" +#define ISO14443_3A_PROTOCOL_NAME "ISO14443-3A" +#define ISO14443_3A_DEVICE_NAME "ISO14443-3A (Unknown)" + +#define ISO14443_3A_ATQA_KEY "ATQA" +#define ISO14443_3A_SAK_KEY "SAK" + +const NfcDeviceBase nfc_device_iso14443_3a = { + .protocol_name = ISO14443_3A_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)iso14443_3a_alloc, + .free = (NfcDeviceFree)iso14443_3a_free, + .reset = (NfcDeviceReset)iso14443_3a_reset, + .copy = (NfcDeviceCopy)iso14443_3a_copy, + .verify = (NfcDeviceVerify)iso14443_3a_verify, + .load = (NfcDeviceLoad)iso14443_3a_load, + .save = (NfcDeviceSave)iso14443_3a_save, + .is_equal = (NfcDeviceEqual)iso14443_3a_is_equal, + .get_name = (NfcDeviceGetName)iso14443_3a_get_device_name, + .get_uid = (NfcDeviceGetUid)iso14443_3a_get_uid, + .set_uid = (NfcDeviceSetUid)iso14443_3a_set_uid, + .get_base_data = (NfcDeviceGetBaseData)iso14443_3a_get_base_data, +}; + +Iso14443_3aData* iso14443_3a_alloc() { + Iso14443_3aData* data = malloc(sizeof(Iso14443_3aData)); + return data; +} + +void iso14443_3a_free(Iso14443_3aData* data) { + furi_assert(data); + + free(data); +} + +void iso14443_3a_reset(Iso14443_3aData* data) { + furi_assert(data); + memset(data, 0, sizeof(Iso14443_3aData)); +} + +void iso14443_3a_copy(Iso14443_3aData* data, const Iso14443_3aData* other) { + furi_assert(data); + furi_assert(other); + + *data = *other; +} + +bool iso14443_3a_verify(Iso14443_3aData* data, const FuriString* device_type) { + UNUSED(data); + return furi_string_equal(device_type, ISO14443_3A_PROTOCOL_NAME_LEGACY); +} + +bool iso14443_3a_load(Iso14443_3aData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + + bool parsed = false; + + do { + // Common to all format versions + if(!flipper_format_read_hex(ff, ISO14443_3A_ATQA_KEY, data->atqa, 2)) break; + if(!flipper_format_read_hex(ff, ISO14443_3A_SAK_KEY, &data->sak, 1)) break; + + if(version > NFC_LSB_ATQA_FORMAT_VERSION) { + // Swap ATQA bytes for newer versions + FURI_SWAP(data->atqa[0], data->atqa[1]); + } + + parsed = true; + } while(false); + + return parsed; +} + +bool iso14443_3a_save(const Iso14443_3aData* data, FlipperFormat* ff) { + furi_assert(data); + + bool saved = false; + + do { + // Save ATQA in MSB order for correct companion apps display + const uint8_t atqa[2] = {data->atqa[1], data->atqa[0]}; + if(!flipper_format_write_comment_cstr(ff, ISO14443_3A_PROTOCOL_NAME " specific data")) + break; + + // Write ATQA and SAK + if(!flipper_format_write_hex(ff, ISO14443_3A_ATQA_KEY, atqa, 2)) break; + if(!flipper_format_write_hex(ff, ISO14443_3A_SAK_KEY, &data->sak, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool iso14443_3a_is_equal(const Iso14443_3aData* data, const Iso14443_3aData* other) { + furi_assert(data); + furi_assert(other); + + return memcmp(data, other, sizeof(Iso14443_3aData)) == 0; +} + +const char* iso14443_3a_get_device_name(const Iso14443_3aData* data, NfcDeviceNameType name_type) { + UNUSED(data); + UNUSED(name_type); + return ISO14443_3A_DEVICE_NAME; +} + +const uint8_t* iso14443_3a_get_uid(const Iso14443_3aData* data, size_t* uid_len) { + furi_assert(data); + + if(uid_len) { + *uid_len = data->uid_len; + } + + return data->uid; +} + +bool iso14443_3a_set_uid(Iso14443_3aData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + const bool uid_valid = uid_len == ISO14443_3A_UID_4_BYTES || + uid_len == ISO14443_3A_UID_7_BYTES || + uid_len == ISO14443_3A_UID_10_BYTES; + + if(uid_valid) { + memcpy(data->uid, uid, uid_len); + data->uid_len = uid_len; + } + + return uid_valid; +} + +Iso14443_3aData* iso14443_3a_get_base_data(const Iso14443_3aData* data) { + UNUSED(data); + furi_crash("No base data"); +} + +uint32_t iso14443_3a_get_cuid(const Iso14443_3aData* data) { + furi_assert(data); + + uint32_t cuid = 0; + const uint8_t* cuid_start = data->uid; + if(data->uid_len == ISO14443_3A_UID_7_BYTES) { + cuid_start = &data->uid[3]; + } + cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) | (cuid_start[3]); + + return cuid; +} + +bool iso14443_3a_supports_iso14443_4(const Iso14443_3aData* data) { + furi_assert(data); + + return data->sak & ISO14443A_ATS_BIT; +} + +uint8_t iso14443_3a_get_sak(const Iso14443_3aData* data) { + furi_assert(data); + + return data->sak; +} + +void iso14443_3a_get_atqa(const Iso14443_3aData* data, uint8_t atqa[2]) { + furi_assert(data); + furi_assert(atqa); + + memcpy(atqa, data->atqa, sizeof(data->atqa)); +} + +void iso14443_3a_set_sak(Iso14443_3aData* data, uint8_t sak) { + furi_assert(data); + + data->sak = sak; +} + +void iso14443_3a_set_atqa(Iso14443_3aData* data, const uint8_t atqa[2]) { + furi_assert(data); + furi_assert(atqa); + + memcpy(data->atqa, atqa, sizeof(data->atqa)); +} diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a.h new file mode 100644 index 00000000000..005626e6285 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a.h @@ -0,0 +1,107 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISO14443_3A_UID_4_BYTES (4U) +#define ISO14443_3A_UID_7_BYTES (7U) +#define ISO14443_3A_UID_10_BYTES (10U) +#define ISO14443_3A_MAX_UID_SIZE ISO14443_3A_UID_10_BYTES + +#define ISO14443_3A_GUARD_TIME_US (5000) +#define ISO14443_3A_FDT_POLL_FC (1620) +#define ISO14443_3A_FDT_LISTEN_FC (1172) +#define ISO14443_3A_POLLER_MASK_RX_FS ((ISO14443_3A_FDT_LISTEN_FC) / 2) +#define ISO14443_3A_POLL_POLL_MIN_US (1100) + +typedef enum { + Iso14443_3aErrorNone, + Iso14443_3aErrorNotPresent, + Iso14443_3aErrorColResFailed, + Iso14443_3aErrorBufferOverflow, + Iso14443_3aErrorCommunication, + Iso14443_3aErrorFieldOff, + Iso14443_3aErrorWrongCrc, + Iso14443_3aErrorTimeout, +} Iso14443_3aError; + +typedef struct { + uint8_t sens_resp[2]; +} Iso14443_3aSensResp; + +typedef struct { + uint8_t sel_cmd; + uint8_t sel_par; + uint8_t data[4]; // max data bit is 32 +} Iso14443_3aSddReq; + +typedef struct { + uint8_t nfcid[4]; + uint8_t bss; +} Iso14443_3aSddResp; + +typedef struct { + uint8_t sel_cmd; + uint8_t sel_par; + uint8_t nfcid[4]; + uint8_t bcc; +} Iso14443_3aSelReq; + +typedef struct { + uint8_t sak; +} Iso14443_3aSelResp; + +typedef struct { + uint8_t uid[ISO14443_3A_MAX_UID_SIZE]; + uint8_t uid_len; + uint8_t atqa[2]; + uint8_t sak; +} Iso14443_3aData; + +Iso14443_3aData* iso14443_3a_alloc(); + +void iso14443_3a_free(Iso14443_3aData* data); + +void iso14443_3a_reset(Iso14443_3aData* data); + +void iso14443_3a_copy(Iso14443_3aData* data, const Iso14443_3aData* other); + +bool iso14443_3a_verify(Iso14443_3aData* data, const FuriString* device_type); + +bool iso14443_3a_load(Iso14443_3aData* data, FlipperFormat* ff, uint32_t version); + +bool iso14443_3a_save(const Iso14443_3aData* data, FlipperFormat* ff); + +bool iso14443_3a_is_equal(const Iso14443_3aData* data, const Iso14443_3aData* other); + +const char* iso14443_3a_get_device_name(const Iso14443_3aData* data, NfcDeviceNameType name_type); + +const uint8_t* iso14443_3a_get_uid(const Iso14443_3aData* data, size_t* uid_len); + +bool iso14443_3a_set_uid(Iso14443_3aData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_3aData* iso14443_3a_get_base_data(const Iso14443_3aData* data); + +uint32_t iso14443_3a_get_cuid(const Iso14443_3aData* data); + +// Getters and tests + +bool iso14443_3a_supports_iso14443_4(const Iso14443_3aData* data); + +uint8_t iso14443_3a_get_sak(const Iso14443_3aData* data); + +void iso14443_3a_get_atqa(const Iso14443_3aData* data, uint8_t atqa[2]); + +// Setters + +void iso14443_3a_set_sak(Iso14443_3aData* data, uint8_t sak); + +void iso14443_3a_set_atqa(Iso14443_3aData* data, const uint8_t atqa[2]); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_device_defs.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_device_defs.h new file mode 100644 index 00000000000..8a736dfa7ff --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_device_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcDeviceBase nfc_device_iso14443_3a; diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.c b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.c new file mode 100644 index 00000000000..89d96f6fc53 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.c @@ -0,0 +1,127 @@ +#include "iso14443_3a_listener_i.h" + +#include "nfc/protocols/nfc_listener_base.h" +#include "nfc/helpers/iso14443_crc.h" + +#include +#include + +#define TAG "Iso14443_3aListener" + +#define ISO14443_3A_LISTENER_MAX_BUFFER_SIZE (256) + +static bool iso14443_3a_listener_halt_received(BitBuffer* buf) { + bool halt_cmd_received = false; + + do { + if(bit_buffer_get_size_bytes(buf) != 4) break; + if(!iso14443_crc_check(Iso14443CrcTypeA, buf)) break; + if(bit_buffer_get_byte(buf, 0) != 0x50) break; + if(bit_buffer_get_byte(buf, 1) != 0x00) break; + halt_cmd_received = true; + } while(false); + + return halt_cmd_received; +} + +Iso14443_3aListener* iso14443_3a_listener_alloc(Nfc* nfc, Iso14443_3aData* data) { + furi_assert(nfc); + + Iso14443_3aListener* instance = malloc(sizeof(Iso14443_3aListener)); + instance->nfc = nfc; + instance->data = data; + instance->tx_buffer = bit_buffer_alloc(ISO14443_3A_LISTENER_MAX_BUFFER_SIZE); + + instance->iso14443_3a_event.data = &instance->iso14443_3a_event_data; + instance->generic_event.protocol = NfcProtocolIso14443_3a; + instance->generic_event.instance = instance; + instance->generic_event.event_data = &instance->iso14443_3a_event; + + nfc_set_fdt_listen_fc(instance->nfc, ISO14443_3A_FDT_LISTEN_FC); + nfc_config(instance->nfc, NfcModeListener, NfcTechIso14443a); + nfc_iso14443a_listener_set_col_res_data( + instance->nfc, + instance->data->uid, + instance->data->uid_len, + instance->data->atqa, + instance->data->sak); + + return instance; +} + +void iso14443_3a_listener_free(Iso14443_3aListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + furi_assert(instance->tx_buffer); + + bit_buffer_free(instance->tx_buffer); + free(instance); +} + +void iso14443_3a_listener_set_callback( + Iso14443_3aListener* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + + instance->callback = callback; + instance->context = context; +} + +const Iso14443_3aData* iso14443_3a_listener_get_data(Iso14443_3aListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +NfcCommand iso14443_3a_listener_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolInvalid); + furi_assert(event.event_data); + + Iso14443_3aListener* instance = context; + NfcEvent* nfc_event = event.event_data; + NfcCommand command = NfcCommandContinue; + + if(nfc_event->type == NfcEventTypeListenerActivated) { + instance->state = Iso14443_3aListenerStateActive; + } else if(nfc_event->type == NfcEventTypeFieldOff) { + instance->state = Iso14443_3aListenerStateIdle; + if(instance->callback) { + instance->iso14443_3a_event.type = Iso14443_3aListenerEventTypeFieldOff; + instance->callback(instance->generic_event, instance->context); + } + command = NfcCommandSleep; + } else if(nfc_event->type == NfcEventTypeRxEnd) { + if(iso14443_3a_listener_halt_received(nfc_event->data.buffer)) { + if(instance->callback) { + instance->iso14443_3a_event.type = Iso14443_3aListenerEventTypeHalted; + instance->callback(instance->generic_event, instance->context); + } + command = NfcCommandSleep; + } else { + if(iso14443_crc_check(Iso14443CrcTypeA, nfc_event->data.buffer)) { + instance->iso14443_3a_event.type = + Iso14443_3aListenerEventTypeReceivedStandardFrame; + iso14443_crc_trim(nfc_event->data.buffer); + } else { + instance->iso14443_3a_event.type = Iso14443_3aListenerEventTypeReceivedData; + } + instance->iso14443_3a_event_data.buffer = nfc_event->data.buffer; + if(instance->callback) { + command = instance->callback(instance->generic_event, instance->context); + } + } + } + + return command; +} + +const NfcListenerBase nfc_listener_iso14443_3a = { + .alloc = (NfcListenerAlloc)iso14443_3a_listener_alloc, + .free = (NfcListenerFree)iso14443_3a_listener_free, + .set_callback = (NfcListenerSetCallback)iso14443_3a_listener_set_callback, + .get_data = (NfcListenerGetData)iso14443_3a_listener_get_data, + .run = (NfcListenerRun)iso14443_3a_listener_run, +}; diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h new file mode 100644 index 00000000000..8a550ca0a89 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h @@ -0,0 +1,31 @@ +#pragma once + +#include "iso14443_3a.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Iso14443_3aListener Iso14443_3aListener; + +typedef enum { + Iso14443_3aListenerEventTypeFieldOff, + Iso14443_3aListenerEventTypeHalted, + + Iso14443_3aListenerEventTypeReceivedStandardFrame, + Iso14443_3aListenerEventTypeReceivedData, +} Iso14443_3aListenerEventType; + +typedef struct { + BitBuffer* buffer; +} Iso14443_3aListenerEventData; + +typedef struct { + Iso14443_3aListenerEventType type; + Iso14443_3aListenerEventData* data; +} Iso14443_3aListenerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_defs.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_defs.h new file mode 100644 index 00000000000..b4fccec74c2 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcListenerBase nfc_listener_iso14443_3a; diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.c b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.c new file mode 100644 index 00000000000..46501503cee --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.c @@ -0,0 +1,73 @@ +#include "iso14443_3a_listener_i.h" + +#include "nfc/helpers/iso14443_crc.h" + +#define TAG "Iso14443_3aListener" + +static Iso14443_3aError iso14443_3a_listener_process_nfc_error(NfcError error) { + Iso14443_3aError ret = Iso14443_3aErrorNone; + + if(error == NfcErrorNone) { + ret = Iso14443_3aErrorNone; + } else if(error == NfcErrorTimeout) { + ret = Iso14443_3aErrorTimeout; + } else { + ret = Iso14443_3aErrorFieldOff; + } + + return ret; +} + +Iso14443_3aError + iso14443_3a_listener_tx(Iso14443_3aListener* instance, const BitBuffer* tx_buffer) { + furi_assert(instance); + furi_assert(tx_buffer); + + Iso14443_3aError ret = Iso14443_3aErrorNone; + NfcError error = nfc_listener_tx(instance->nfc, tx_buffer); + if(error != NfcErrorNone) { + FURI_LOG_W(TAG, "Tx error: %d", error); + ret = iso14443_3a_listener_process_nfc_error(error); + } + + return ret; +} + +Iso14443_3aError iso14443_3a_listener_tx_with_custom_parity( + Iso14443_3aListener* instance, + const BitBuffer* tx_buffer) { + furi_assert(instance); + furi_assert(tx_buffer); + + Iso14443_3aError ret = Iso14443_3aErrorNone; + NfcError error = nfc_iso14443a_listener_tx_custom_parity(instance->nfc, tx_buffer); + if(error != NfcErrorNone) { + FURI_LOG_W(TAG, "Tx error: %d", error); + ret = iso14443_3a_listener_process_nfc_error(error); + } + + return ret; +}; + +Iso14443_3aError iso14443_3a_listener_send_standard_frame( + Iso14443_3aListener* instance, + const BitBuffer* tx_buffer) { + furi_assert(instance); + furi_assert(tx_buffer); + furi_assert(instance->tx_buffer); + + Iso14443_3aError ret = Iso14443_3aErrorNone; + do { + bit_buffer_copy(instance->tx_buffer, tx_buffer); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_buffer); + + NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer); + if(error != NfcErrorNone) { + FURI_LOG_W(TAG, "Tx error: %d", error); + ret = iso14443_3a_listener_process_nfc_error(error); + break; + } + } while(false); + + return ret; +} diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.h new file mode 100644 index 00000000000..0113a1cb892 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_listener_i.h @@ -0,0 +1,42 @@ +#pragma once + +#include "iso14443_3a_listener.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + Iso14443_3aListenerStateIdle, + Iso14443_3aListenerStateActive, +} Iso14443_3aListenerState; + +struct Iso14443_3aListener { + Nfc* nfc; + Iso14443_3aData* data; + Iso14443_3aListenerState state; + + BitBuffer* tx_buffer; + + NfcGenericEvent generic_event; + Iso14443_3aListenerEvent iso14443_3a_event; + Iso14443_3aListenerEventData iso14443_3a_event_data; + NfcGenericCallback callback; + void* context; +}; + +Iso14443_3aError + iso14443_3a_listener_tx(Iso14443_3aListener* instance, const BitBuffer* tx_buffer); + +Iso14443_3aError iso14443_3a_listener_tx_with_custom_parity( + Iso14443_3aListener* instance, + const BitBuffer* tx_buffer); + +Iso14443_3aError iso14443_3a_listener_send_standard_frame( + Iso14443_3aListener* instance, + const BitBuffer* tx_buffer); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.c b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.c new file mode 100644 index 00000000000..158250acdf7 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.c @@ -0,0 +1,128 @@ +#include "iso14443_3a_poller_i.h" + +#include + +#include + +#define TAG "ISO14443_3A" + +const Iso14443_3aData* iso14443_3a_poller_get_data(Iso14443_3aPoller* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +static Iso14443_3aPoller* iso14443_3a_poller_alloc(Nfc* nfc) { + furi_assert(nfc); + + Iso14443_3aPoller* instance = malloc(sizeof(Iso14443_3aPoller)); + instance->nfc = nfc; + instance->tx_buffer = bit_buffer_alloc(ISO14443_3A_POLLER_MAX_BUFFER_SIZE); + instance->rx_buffer = bit_buffer_alloc(ISO14443_3A_POLLER_MAX_BUFFER_SIZE); + + nfc_config(instance->nfc, NfcModePoller, NfcTechIso14443a); + nfc_set_guard_time_us(instance->nfc, ISO14443_3A_GUARD_TIME_US); + nfc_set_fdt_poll_fc(instance->nfc, ISO14443_3A_FDT_POLL_FC); + nfc_set_fdt_poll_poll_us(instance->nfc, ISO14443_3A_POLL_POLL_MIN_US); + instance->data = iso14443_3a_alloc(); + + instance->iso14443_3a_event.data = &instance->iso14443_3a_event_data; + instance->general_event.protocol = NfcProtocolIso14443_3a; + instance->general_event.event_data = &instance->iso14443_3a_event; + instance->general_event.instance = instance; + + return instance; +} + +static void iso14443_3a_poller_free_new(Iso14443_3aPoller* iso14443_3a_poller) { + furi_assert(iso14443_3a_poller); + + Iso14443_3aPoller* instance = iso14443_3a_poller; + furi_assert(instance->tx_buffer); + furi_assert(instance->rx_buffer); + furi_assert(instance->data); + + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + iso14443_3a_free(instance->data); + free(instance); +} + +static void iso14443_3a_poller_set_callback( + Iso14443_3aPoller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand iso14443_3a_poller_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolInvalid); + furi_assert(event.event_data); + + Iso14443_3aPoller* instance = context; + NfcEvent* nfc_event = event.event_data; + NfcCommand command = NfcCommandContinue; + + if(nfc_event->type == NfcEventTypePollerReady) { + if(instance->state != Iso14443_3aPollerStateActivated) { + Iso14443_3aData data = {}; + Iso14443_3aError error = iso14443_3a_poller_activate(instance, &data); + if(error == Iso14443_3aErrorNone) { + instance->state = Iso14443_3aPollerStateActivated; + instance->iso14443_3a_event.type = Iso14443_3aPollerEventTypeReady; + instance->iso14443_3a_event_data.error = error; + command = instance->callback(instance->general_event, instance->context); + } else { + instance->iso14443_3a_event.type = Iso14443_3aPollerEventTypeError; + instance->iso14443_3a_event_data.error = error; + command = instance->callback(instance->general_event, instance->context); + // Add delay to switch context + furi_delay_ms(100); + } + } else { + instance->iso14443_3a_event.type = Iso14443_3aPollerEventTypeReady; + instance->iso14443_3a_event_data.error = Iso14443_3aErrorNone; + command = instance->callback(instance->general_event, instance->context); + } + } + + if(command == NfcCommandReset) { + instance->state = Iso14443_3aPollerStateIdle; + } + + return command; +} + +static bool iso14443_3a_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.instance); + furi_assert(event.protocol == NfcProtocolInvalid); + + bool protocol_detected = false; + Iso14443_3aPoller* instance = context; + NfcEvent* nfc_event = event.event_data; + furi_assert(instance->state == Iso14443_3aPollerStateIdle); + + if(nfc_event->type == NfcEventTypePollerReady) { + Iso14443_3aError error = iso14443_3a_poller_activate(instance, NULL); + protocol_detected = (error == Iso14443_3aErrorNone); + } + + return protocol_detected; +} + +const NfcPollerBase nfc_poller_iso14443_3a = { + .alloc = (NfcPollerAlloc)iso14443_3a_poller_alloc, + .free = (NfcPollerFree)iso14443_3a_poller_free_new, + .set_callback = (NfcPollerSetCallback)iso14443_3a_poller_set_callback, + .run = (NfcPollerRun)iso14443_3a_poller_run, + .detect = (NfcPollerDetect)iso14443_3a_poller_detect, + .get_data = (NfcPollerGetData)iso14443_3a_poller_get_data, +}; diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h new file mode 100644 index 00000000000..42e4b4bf524 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h @@ -0,0 +1,145 @@ +#pragma once + +#include "iso14443_3a.h" +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Iso14443_3aPoller opaque type definition. + */ +typedef struct Iso14443_3aPoller Iso14443_3aPoller; + +/** + * @brief Enumeration of possible Iso14443_3a poller event types. + */ +typedef enum { + Iso14443_3aPollerEventTypeError, /**< The card was activated by the poller. */ + Iso14443_3aPollerEventTypeReady, /**< An error occured during activation procedure. */ +} Iso14443_3aPollerEventType; + +/** + * @brief Iso14443_3a poller event data. + */ +typedef union { + Iso14443_3aError error; /**< Error code indicating card activation fail reason. */ +} Iso14443_3aPollerEventData; + +/** + * @brief Iso14443_3a poller event structure. + * + * Upon emission of an event, an instance of this struct will be passed to the callback. + */ +typedef struct { + Iso14443_3aPollerEventType type; /**< Type of emmitted event. */ + Iso14443_3aPollerEventData* data; /**< Pointer to event specific data. */ +} Iso14443_3aPollerEvent; + +/** + * @brief Transmit and receive Iso14443_3a frames in poller mode. + * + * Must ONLY be used inside the callback function. + * + * The rx_buffer will be filled with any data received as a response to data + * sent from tx_buffer, with a timeout defined by the fwt parameter. + * + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted. + * @param[out] rx_buffer pointer to the buffer to be filled with received data. + * @param[in] fwt frame wait time (response timeout), in carrier cycles. + * @return Iso14443_3aErrorNone on success, an error code on failure. + */ +Iso14443_3aError iso14443_3a_poller_txrx( + Iso14443_3aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt); + +/** + * @brief Transmit and receive Iso14443_3a standard frames in poller mode. + * + * Must ONLY be used inside the callback function. + * + * The rx_buffer will be filled with any data received as a response to data + * sent from tx_buffer, with a timeout defined by the fwt parameter. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted. + * @param[out] rx_buffer pointer to the buffer to be filled with received data. + * @param[in] fwt frame wait time (response timeout), in carrier cycles. + * @return Iso14443_3aErrorNone on success, an error code on failure. + */ +Iso14443_3aError iso14443_3a_poller_send_standard_frame( + Iso14443_3aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt); + +/** + * @brief Transmit and receive Iso14443_3a frames with custom parity bits in poller mode. + * + * Must ONLY be used inside the callback function. + * + * The rx_buffer will be filled with any data received as a response to data + * sent from tx_buffer, with a timeout defined by the fwt parameter. + * + * Custom parity bits must be set in the tx_buffer. The rx_buffer will contain + * the received data with the parity bits. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted. + * @param[out] rx_buffer pointer to the buffer to be filled with received data. + * @param[in] fwt frame wait time (response timeout), in carrier cycles. + * @return Iso14443_3aErrorNone on success, an error code on failure. + */ +Iso14443_3aError iso14443_3a_poller_txrx_custom_parity( + Iso14443_3aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt); + +/** + * @brief Checks presence of Iso14443_3a complient card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @return Iso14443_3aErrorNone if card is present, an error code otherwise. + */ +Iso14443_3aError iso14443_3a_poller_check_presence(Iso14443_3aPoller* instance); + +/** + * @brief Perform collision resolution procedure. + * + * Must ONLY be used inside the callback function. + * + * Perfoms the collision resolution procedure as defined in Iso14443-3a. The iso14443_3a_data + * field will be filled with Iso14443-3a data on success. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] iso14443_3a_data pointer to the Iso14443_3a data structure to be filled. + * @return Iso14443_3aErrorNone on success, an error code on failure. + */ +Iso14443_3aError + iso14443_3a_poller_activate(Iso14443_3aPoller* instance, Iso14443_3aData* iso14443_3a_data); + +/** + * @brief Send HALT command to the card. + * + * Must ONLY be used inside the callback function. + * + * Halts card and changes internal Iso14443_3aPoller state to Idle. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @return Iso14443_3aErrorNone on success, an error code on failure. + */ +Iso14443_3aError iso14443_3a_poller_halt(Iso14443_3aPoller* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_defs.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_defs.h new file mode 100644 index 00000000000..1bcc684b405 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcPollerBase nfc_poller_iso14443_3a; diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.c b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.c new file mode 100644 index 00000000000..2be88bc515d --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.c @@ -0,0 +1,292 @@ +#include "iso14443_3a_poller_i.h" + +#include + +#include "nfc/helpers/iso14443_crc.h" + +#define TAG "ISO14443_3A" + +static Iso14443_3aError iso14443_3a_poller_process_error(NfcError error) { + Iso14443_3aError ret = Iso14443_3aErrorNone; + if(error == NfcErrorNone) { + ret = Iso14443_3aErrorNone; + } else if(error == NfcErrorTimeout) { + ret = Iso14443_3aErrorTimeout; + } else { + ret = Iso14443_3aErrorNotPresent; + } + return ret; +} + +static Iso14443_3aError iso14443_3a_poller_standard_frame_exchange( + Iso14443_3aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + furi_assert(tx_buffer); + furi_assert(rx_buffer); + + uint16_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer); + furi_assert(tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - 2); + + bit_buffer_copy(instance->tx_buffer, tx_buffer); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_buffer); + Iso14443_3aError ret = Iso14443_3aErrorNone; + + do { + NfcError error = + nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt); + if(error != NfcErrorNone) { + ret = iso14443_3a_poller_process_error(error); + break; + } + + bit_buffer_copy(rx_buffer, instance->rx_buffer); + if(!iso14443_crc_check(Iso14443CrcTypeA, instance->rx_buffer)) { + ret = Iso14443_3aErrorWrongCrc; + break; + } + + iso14443_crc_trim(rx_buffer); + } while(false); + + return ret; +} + +Iso14443_3aError iso14443_3a_poller_check_presence(Iso14443_3aPoller* instance) { + furi_assert(instance); + furi_assert(instance->nfc); + + NfcError error = NfcErrorNone; + Iso14443_3aError ret = Iso14443_3aErrorNone; + do { + error = nfc_iso14443a_poller_trx_short_frame( + instance->nfc, + NfcIso14443aShortFrameSensReq, + instance->rx_buffer, + ISO14443_3A_FDT_LISTEN_FC); + if(error != NfcErrorNone) { + ret = iso14443_3a_poller_process_error(error); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(instance->col_res.sens_resp)) { + ret = Iso14443_3aErrorCommunication; + break; + } + } while(false); + + return ret; +} + +Iso14443_3aError iso14443_3a_poller_halt(Iso14443_3aPoller* instance) { + furi_assert(instance); + furi_assert(instance->nfc); + furi_assert(instance->tx_buffer); + + uint8_t halt_cmd[2] = {0x50, 0x00}; + bit_buffer_copy_bytes(instance->tx_buffer, halt_cmd, sizeof(halt_cmd)); + + iso14443_3a_poller_standard_frame_exchange( + instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3A_FDT_LISTEN_FC); + + instance->state = Iso14443_3aPollerStateIdle; + return Iso14443_3aErrorNone; +} + +Iso14443_3aError + iso14443_3a_poller_activate(Iso14443_3aPoller* instance, Iso14443_3aData* iso14443_3a_data) { + furi_assert(instance); + furi_assert(instance->nfc); + furi_assert(instance->tx_buffer); + furi_assert(instance->rx_buffer); + + // Reset Iso14443_3a poller state + memset(&instance->col_res, 0, sizeof(instance->col_res)); + memset(instance->data, 0, sizeof(Iso14443_3aData)); + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + // Halt if necessary + if(instance->state != Iso14443_3aPollerStateIdle) { + iso14443_3a_poller_halt(instance); + instance->state = Iso14443_3aPollerStateIdle; + } + + NfcError error = NfcErrorNone; + Iso14443_3aError ret = Iso14443_3aErrorNone; + + bool activated = false; + do { + error = nfc_iso14443a_poller_trx_short_frame( + instance->nfc, + NfcIso14443aShortFrameSensReq, + instance->rx_buffer, + ISO14443_3A_FDT_LISTEN_FC); + if(error != NfcErrorNone) { + ret = Iso14443_3aErrorNotPresent; + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(instance->col_res.sens_resp)) { + FURI_LOG_W(TAG, "Wrong sens response size"); + ret = Iso14443_3aErrorCommunication; + break; + } + bit_buffer_write_bytes( + instance->rx_buffer, + &instance->col_res.sens_resp, + sizeof(instance->col_res.sens_resp)); + memcpy( + instance->data->atqa, + &instance->col_res.sens_resp, + sizeof(instance->col_res.sens_resp)); + + instance->state = Iso14443_3aPollerStateColResInProgress; + instance->col_res.cascade_level = 0; + instance->col_res.state = Iso14443_3aPollerColResStateStateNewCascade; + + while(instance->state == Iso14443_3aPollerStateColResInProgress) { + if(instance->col_res.state == Iso14443_3aPollerColResStateStateNewCascade) { + bit_buffer_set_size_bytes(instance->tx_buffer, 2); + bit_buffer_set_byte( + instance->tx_buffer, + 0, + ISO14443_3A_POLLER_SEL_CMD(instance->col_res.cascade_level)); + bit_buffer_set_byte(instance->tx_buffer, 1, ISO14443_3A_POLLER_SEL_PAR(2, 0)); + error = nfc_iso14443a_poller_trx_sdd_frame( + instance->nfc, + instance->tx_buffer, + instance->rx_buffer, + ISO14443_3A_FDT_LISTEN_FC); + if(error != NfcErrorNone) { + FURI_LOG_E(TAG, "Sdd request failed: %d", error); + instance->state = Iso14443_3aPollerStateColResFailed; + ret = Iso14443_3aErrorColResFailed; + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != 5) { + FURI_LOG_E(TAG, "Sdd response wrong length"); + instance->state = Iso14443_3aPollerStateColResFailed; + ret = Iso14443_3aErrorColResFailed; + break; + } + bit_buffer_write_bytes( + instance->rx_buffer, &instance->col_res.sdd_resp, sizeof(Iso14443_3aSddResp)); + instance->col_res.state = Iso14443_3aPollerColResStateStateSelectCascade; + } else if(instance->col_res.state == Iso14443_3aPollerColResStateStateSelectCascade) { + instance->col_res.sel_req.sel_cmd = + ISO14443_3A_POLLER_SEL_CMD(instance->col_res.cascade_level); + instance->col_res.sel_req.sel_par = ISO14443_3A_POLLER_SEL_PAR(7, 0); + memcpy( + instance->col_res.sel_req.nfcid, + instance->col_res.sdd_resp.nfcid, + sizeof(instance->col_res.sdd_resp.nfcid)); + instance->col_res.sel_req.bcc = instance->col_res.sdd_resp.bss; + bit_buffer_copy_bytes( + instance->tx_buffer, + (uint8_t*)&instance->col_res.sel_req, + sizeof(instance->col_res.sel_req)); + ret = iso14443_3a_poller_send_standard_frame( + instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3A_FDT_LISTEN_FC); + if(ret != Iso14443_3aErrorNone) { + FURI_LOG_E(TAG, "Sel request failed: %d", ret); + instance->state = Iso14443_3aPollerStateColResFailed; + ret = Iso14443_3aErrorColResFailed; + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != + sizeof(instance->col_res.sel_resp)) { + FURI_LOG_E(TAG, "Sel response wrong length"); + instance->state = Iso14443_3aPollerStateColResFailed; + ret = Iso14443_3aErrorColResFailed; + break; + } + bit_buffer_write_bytes( + instance->rx_buffer, + &instance->col_res.sel_resp, + sizeof(instance->col_res.sel_resp)); + FURI_LOG_T(TAG, "Sel resp: %02X", instance->col_res.sel_resp.sak); + if(instance->col_res.sel_req.nfcid[0] == ISO14443_3A_POLLER_SDD_CL) { + // Copy part of UID + memcpy( + &instance->data->uid[instance->data->uid_len], + &instance->col_res.sel_req.nfcid[1], + 3); + instance->data->uid_len += 3; + instance->col_res.cascade_level++; + instance->col_res.state = Iso14443_3aPollerColResStateStateNewCascade; + } else { + FURI_LOG_T(TAG, "Col resolution complete"); + instance->data->sak = instance->col_res.sel_resp.sak; + memcpy( + &instance->data->uid[instance->data->uid_len], + &instance->col_res.sel_req.nfcid[0], + 4); + instance->data->uid_len += 4; + instance->col_res.state = Iso14443_3aPollerColResStateStateSuccess; + instance->state = Iso14443_3aPollerStateActivated; + } + } + } + + activated = (instance->state == Iso14443_3aPollerStateActivated); + } while(false); + + if(activated && iso14443_3a_data) { + *iso14443_3a_data = *instance->data; + } + + return ret; +} + +Iso14443_3aError iso14443_3a_poller_txrx_custom_parity( + Iso14443_3aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + furi_assert(tx_buffer); + furi_assert(rx_buffer); + + Iso14443_3aError ret = Iso14443_3aErrorNone; + NfcError error = + nfc_iso14443a_poller_trx_custom_parity(instance->nfc, tx_buffer, rx_buffer, fwt); + if(error != NfcErrorNone) { + ret = iso14443_3a_poller_process_error(error); + } + + return ret; +} + +Iso14443_3aError iso14443_3a_poller_txrx( + Iso14443_3aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + furi_assert(tx_buffer); + furi_assert(rx_buffer); + + Iso14443_3aError ret = Iso14443_3aErrorNone; + NfcError error = nfc_poller_trx(instance->nfc, tx_buffer, rx_buffer, fwt); + if(error != NfcErrorNone) { + ret = iso14443_3a_poller_process_error(error); + } + + return ret; +} + +Iso14443_3aError iso14443_3a_poller_send_standard_frame( + Iso14443_3aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + furi_assert(tx_buffer); + furi_assert(rx_buffer); + + Iso14443_3aError ret = + iso14443_3a_poller_standard_frame_exchange(instance, tx_buffer, rx_buffer, fwt); + + return ret; +} diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h new file mode 100644 index 00000000000..764f1a6b593 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h @@ -0,0 +1,61 @@ +#pragma once + +#include "iso14443_3a_poller.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISO14443_3A_POLLER_MAX_BUFFER_SIZE (512U) + +#define ISO14443_3A_POLLER_SEL_CMD(cascade_lvl) (0x93 + 2 * (cascade_lvl)) +#define ISO14443_3A_POLLER_SEL_PAR(bytes, bits) (((bytes) << 4 & 0xf0U) | ((bits)&0x0fU)) +#define ISO14443_3A_POLLER_SDD_CL (0x88U) + +typedef enum { + Iso14443_3aPollerColResStateStateIdle, + Iso14443_3aPollerColResStateStateNewCascade, + Iso14443_3aPollerColResStateStateSelectCascade, + Iso14443_3aPollerColResStateStateSuccess, + Iso14443_3aPollerColResStateStateFail, +} Iso14443_3aPollerColResState; + +typedef struct { + Iso14443_3aPollerColResState state; + Iso14443_3aSensResp sens_resp; + Iso14443_3aSddReq sdd_req; + Iso14443_3aSddResp sdd_resp; + Iso14443_3aSelReq sel_req; + Iso14443_3aSelResp sel_resp; + uint8_t cascade_level; +} Iso14443_3aPollerColRes; + +typedef enum { + Iso14443_3aPollerStateIdle, + Iso14443_3aPollerStateColResInProgress, + Iso14443_3aPollerStateColResFailed, + Iso14443_3aPollerStateActivated, +} Iso14443_3aPollerState; + +struct Iso14443_3aPoller { + Nfc* nfc; + Iso14443_3aPollerState state; + Iso14443_3aPollerColRes col_res; + Iso14443_3aData* data; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + + NfcGenericEvent general_event; + Iso14443_3aPollerEvent iso14443_3a_event; + Iso14443_3aPollerEventData iso14443_3a_event_data; + NfcGenericCallback callback; + void* context; +}; + +const Iso14443_3aData* iso14443_3a_poller_get_data(Iso14443_3aPoller* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.c b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.c new file mode 100644 index 00000000000..ea7a6ae156a --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.c @@ -0,0 +1,58 @@ +#include "iso14443_3a_poller_sync.h" + +#include "iso14443_3a_poller_i.h" +#include + +#include + +#define ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE (1UL << 0) + +typedef struct { + Iso14443_3aPoller* instance; + FuriThreadId thread_id; + Iso14443_3aError error; + Iso14443_3aData data; +} Iso14443_3aPollerContext; + +NfcCommand iso14443_3a_poller_read_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.instance); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + + Iso14443_3aPollerContext* poller_context = context; + Iso14443_3aPoller* iso14443_3a_poller = event.instance; + Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data; + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + iso14443_3a_copy(&poller_context->data, iso14443_3a_poller->data); + } + poller_context->error = iso14443_3a_event->data->error; + + furi_thread_flags_set(poller_context->thread_id, ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE); + + return NfcCommandStop; +} + +Iso14443_3aError iso14443_3a_poller_sync_read(Nfc* nfc, Iso14443_3aData* iso14443_3a_data) { + furi_assert(nfc); + furi_assert(iso14443_3a_data); + + Iso14443_3aPollerContext poller_context = {}; + poller_context.thread_id = furi_thread_get_current_id(); + + NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolIso14443_3a); + nfc_poller_start(poller, iso14443_3a_poller_read_callback, &poller_context); + furi_thread_flags_wait( + ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE, FuriFlagWaitAny, FuriWaitForever); + furi_thread_flags_clear(ISO14443_3A_POLLER_FLAG_COMMAND_COMPLETE); + + nfc_poller_stop(poller); + nfc_poller_free(poller); + + if(poller_context.error == Iso14443_3aErrorNone) { + *iso14443_3a_data = poller_context.data; + } + + return poller_context.error; +} \ No newline at end of file diff --git a/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.h b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.h new file mode 100644 index 00000000000..72f084d1b06 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.h @@ -0,0 +1,14 @@ +#pragma once + +#include "iso14443_3a.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +Iso14443_3aError iso14443_3a_poller_sync_read(Nfc* nfc, Iso14443_3aData* iso14443_3a_data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b.c b/lib/nfc/protocols/iso14443_3b/iso14443_3b.c new file mode 100644 index 00000000000..fd81e390da7 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b.c @@ -0,0 +1,223 @@ +#include "iso14443_3b_i.h" + +#include +#include + +#include +#include + +#define ISO14443_3B_PROTOCOL_NAME "ISO14443-3B" +#define ISO14443_3B_DEVICE_NAME "ISO14443-3B (Unknown)" + +#define ISO14443_3B_APP_DATA_KEY "Application data" +#define ISO14443_3B_PROTOCOL_INFO_KEY "Protocol info" + +#define ISO14443_3B_FDT_POLL_DEFAULT_FC (ISO14443_3B_FDT_POLL_FC) + +const NfcDeviceBase nfc_device_iso14443_3b = { + .protocol_name = ISO14443_3B_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)iso14443_3b_alloc, + .free = (NfcDeviceFree)iso14443_3b_free, + .reset = (NfcDeviceReset)iso14443_3b_reset, + .copy = (NfcDeviceCopy)iso14443_3b_copy, + .verify = (NfcDeviceVerify)iso14443_3b_verify, + .load = (NfcDeviceLoad)iso14443_3b_load, + .save = (NfcDeviceSave)iso14443_3b_save, + .is_equal = (NfcDeviceEqual)iso14443_3b_is_equal, + .get_name = (NfcDeviceGetName)iso14443_3b_get_device_name, + .get_uid = (NfcDeviceGetUid)iso14443_3b_get_uid, + .set_uid = (NfcDeviceSetUid)iso14443_3b_set_uid, + .get_base_data = (NfcDeviceGetBaseData)iso14443_3b_get_base_data, +}; + +Iso14443_3bData* iso14443_3b_alloc() { + Iso14443_3bData* data = malloc(sizeof(Iso14443_3bData)); + return data; +} + +void iso14443_3b_free(Iso14443_3bData* data) { + furi_assert(data); + + free(data); +} + +void iso14443_3b_reset(Iso14443_3bData* data) { + memset(data, 0, sizeof(Iso14443_3bData)); +} + +void iso14443_3b_copy(Iso14443_3bData* data, const Iso14443_3bData* other) { + furi_assert(data); + furi_assert(other); + + *data = *other; +} + +bool iso14443_3b_verify(Iso14443_3bData* data, const FuriString* device_type) { + UNUSED(data); + UNUSED(device_type); + // No support for old ISO14443-3B + return false; +} + +bool iso14443_3b_load(Iso14443_3bData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + + bool parsed = false; + + do { + if(version < NFC_UNIFIED_FORMAT_VERSION) break; + + if(!flipper_format_read_hex( + ff, ISO14443_3B_APP_DATA_KEY, data->app_data, ISO14443_3B_APP_DATA_SIZE)) + break; + if(!flipper_format_read_hex( + ff, + ISO14443_3B_PROTOCOL_INFO_KEY, + (uint8_t*)&data->protocol_info, + sizeof(Iso14443_3bProtocolInfo))) + break; + + parsed = true; + } while(false); + + return parsed; +} + +bool iso14443_3b_save(const Iso14443_3bData* data, FlipperFormat* ff) { + furi_assert(data); + + bool saved = false; + + do { + if(!flipper_format_write_comment_cstr(ff, ISO14443_3B_PROTOCOL_NAME " specific data")) + break; + if(!flipper_format_write_hex( + ff, ISO14443_3B_APP_DATA_KEY, data->app_data, ISO14443_3B_APP_DATA_SIZE)) + break; + if(!flipper_format_write_hex( + ff, + ISO14443_3B_PROTOCOL_INFO_KEY, + (uint8_t*)&data->protocol_info, + sizeof(Iso14443_3bProtocolInfo))) + break; + saved = true; + } while(false); + + return saved; +} + +bool iso14443_3b_is_equal(const Iso14443_3bData* data, const Iso14443_3bData* other) { + furi_assert(data); + furi_assert(other); + + return memcmp(data, other, sizeof(Iso14443_3bData)) == 0; +} + +const char* iso14443_3b_get_device_name(const Iso14443_3bData* data, NfcDeviceNameType name_type) { + UNUSED(data); + UNUSED(name_type); + + return ISO14443_3B_DEVICE_NAME; +} + +const uint8_t* iso14443_3b_get_uid(const Iso14443_3bData* data, size_t* uid_len) { + furi_assert(data); + furi_assert(uid_len); + + *uid_len = ISO14443_3B_UID_SIZE; + return data->uid; +} + +bool iso14443_3b_set_uid(Iso14443_3bData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + const bool uid_valid = uid_len == ISO14443_3B_UID_SIZE; + + if(uid_valid) { + memcpy(data->uid, uid, uid_len); + } + + return uid_valid; +} + +Iso14443_3bData* iso14443_3b_get_base_data(const Iso14443_3bData* data) { + UNUSED(data); + furi_crash("No base data"); +} + +bool iso14443_3b_supports_iso14443_4(const Iso14443_3bData* data) { + furi_assert(data); + + return data->protocol_info.protocol_type == 0x01; +} + +bool iso14443_3b_supports_bit_rate(const Iso14443_3bData* data, Iso14443_3bBitRate bit_rate) { + furi_assert(data); + + const uint8_t capability = data->protocol_info.bit_rate_capability; + + switch(bit_rate) { + case Iso14443_3bBitRateBoth106Kbit: + return capability == ISO14443_3B_BIT_RATE_BOTH_106KBIT; + case Iso14443_3bBitRatePiccToPcd212Kbit: + return capability & ISO14443_3B_BIT_RATE_PICC_TO_PCD_212KBIT; + case Iso14443_3bBitRatePiccToPcd424Kbit: + return capability & ISO14443_3B_BIT_RATE_PICC_TO_PCD_424KBIT; + case Iso14443_3bBitRatePiccToPcd848Kbit: + return capability & ISO14443_3B_BIT_RATE_PICC_TO_PCD_848KBIT; + case Iso14443_3bBitRatePcdToPicc212Kbit: + return capability & ISO14443_3B_BIT_RATE_PCD_TO_PICC_212KBIT; + case Iso14443_3bBitRatePcdToPicc424Kbit: + return capability & ISO14443_3B_BIT_RATE_PCD_TO_PICC_424KBIT; + case Iso14443_3bBitRatePcdToPicc848Kbit: + return capability & ISO14443_3B_BIT_RATE_PCD_TO_PICC_848KBIT; + default: + return false; + } +} + +bool iso14443_3b_supports_frame_option(const Iso14443_3bData* data, Iso14443_3bFrameOption option) { + furi_assert(data); + + switch(option) { + case Iso14443_3bFrameOptionNad: + return data->protocol_info.fo & ISO14443_3B_FRAME_OPTION_NAD; + case Iso14443_3bFrameOptionCid: + return data->protocol_info.fo & ISO14443_3B_FRAME_OPTION_CID; + default: + return false; + } +} + +const uint8_t* iso14443_3b_get_application_data(const Iso14443_3bData* data, size_t* data_size) { + furi_assert(data); + furi_assert(data_size); + + *data_size = ISO14443_3B_APP_DATA_SIZE; + return data->app_data; +} + +uint16_t iso14443_3b_get_frame_size_max(const Iso14443_3bData* data) { + furi_assert(data); + + const uint8_t fs_bits = data->protocol_info.max_frame_size; + + if(fs_bits < 5) { + return fs_bits * 8 + 16; + } else if(fs_bits == 5) { + return 64; + } else if(fs_bits == 6) { + return 96; + } else if(fs_bits < 13) { + return 128U << (fs_bits - 7); + } else { + return 0; + } +} + +uint32_t iso14443_3b_get_fwt_fc_max(const Iso14443_3bData* data) { + furi_assert(data); + + const uint8_t fwi = data->protocol_info.fwi; + return fwi < 0x0F ? 4096UL << fwi : ISO14443_3B_FDT_POLL_DEFAULT_FC; +} diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b.h b/lib/nfc/protocols/iso14443_3b/iso14443_3b.h new file mode 100644 index 00000000000..848e610c3e8 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b.h @@ -0,0 +1,83 @@ +#pragma once + +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + Iso14443_3bErrorNone, + Iso14443_3bErrorNotPresent, + Iso14443_3bErrorColResFailed, + Iso14443_3bErrorBufferOverflow, + Iso14443_3bErrorCommunication, + Iso14443_3bErrorFieldOff, + Iso14443_3bErrorWrongCrc, + Iso14443_3bErrorTimeout, +} Iso14443_3bError; + +typedef enum { + Iso14443_3bBitRateBoth106Kbit, + Iso14443_3bBitRatePiccToPcd212Kbit, + Iso14443_3bBitRatePiccToPcd424Kbit, + Iso14443_3bBitRatePiccToPcd848Kbit, + Iso14443_3bBitRatePcdToPicc212Kbit, + Iso14443_3bBitRatePcdToPicc424Kbit, + Iso14443_3bBitRatePcdToPicc848Kbit, +} Iso14443_3bBitRate; + +typedef enum { + Iso14443_3bFrameOptionNad, + Iso14443_3bFrameOptionCid, +} Iso14443_3bFrameOption; + +typedef struct Iso14443_3bData Iso14443_3bData; + +// Virtual methods + +Iso14443_3bData* iso14443_3b_alloc(); + +void iso14443_3b_free(Iso14443_3bData* data); + +void iso14443_3b_reset(Iso14443_3bData* data); + +void iso14443_3b_copy(Iso14443_3bData* data, const Iso14443_3bData* other); + +bool iso14443_3b_verify(Iso14443_3bData* data, const FuriString* device_type); + +bool iso14443_3b_load(Iso14443_3bData* data, FlipperFormat* ff, uint32_t version); + +bool iso14443_3b_save(const Iso14443_3bData* data, FlipperFormat* ff); + +bool iso14443_3b_is_equal(const Iso14443_3bData* data, const Iso14443_3bData* other); + +const char* iso14443_3b_get_device_name(const Iso14443_3bData* data, NfcDeviceNameType name_type); + +const uint8_t* iso14443_3b_get_uid(const Iso14443_3bData* data, size_t* uid_len); + +bool iso14443_3b_set_uid(Iso14443_3bData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_3bData* iso14443_3b_get_base_data(const Iso14443_3bData* data); + +// Getters and tests + +bool iso14443_3b_supports_iso14443_4(const Iso14443_3bData* data); + +bool iso14443_3b_supports_bit_rate(const Iso14443_3bData* data, Iso14443_3bBitRate bit_rate); + +bool iso14443_3b_supports_frame_option(const Iso14443_3bData* data, Iso14443_3bFrameOption option); + +const uint8_t* iso14443_3b_get_application_data(const Iso14443_3bData* data, size_t* data_size); + +uint16_t iso14443_3b_get_frame_size_max(const Iso14443_3bData* data); + +uint32_t iso14443_3b_get_fwt_fc_max(const Iso14443_3bData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b_device_defs.h b/lib/nfc/protocols/iso14443_3b/iso14443_3b_device_defs.h new file mode 100644 index 00000000000..6c33900da23 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b_device_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcDeviceBase nfc_device_iso14443_3b; diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b_i.c b/lib/nfc/protocols/iso14443_3b/iso14443_3b_i.c new file mode 100644 index 00000000000..cac7ed82665 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b_i.c @@ -0,0 +1 @@ +#include "iso14443_3b_i.h" diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b_i.h b/lib/nfc/protocols/iso14443_3b/iso14443_3b_i.h new file mode 100644 index 00000000000..f4ff36bd7e3 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b_i.h @@ -0,0 +1,37 @@ +#pragma once + +#include "iso14443_3b.h" + +#define ISO14443_3B_UID_SIZE (4U) +#define ISO14443_3B_APP_DATA_SIZE (4U) + +#define ISO14443_3B_GUARD_TIME_US (5000U) +#define ISO14443_3B_FDT_POLL_FC (9000U) +#define ISO14443_3B_POLL_POLL_MIN_US (1280U) + +#define ISO14443_3B_BIT_RATE_BOTH_106KBIT (0U << 0) +#define ISO14443_3B_BIT_RATE_PCD_TO_PICC_212KBIT (1U << 0) +#define ISO14443_3B_BIT_RATE_PCD_TO_PICC_424KBIT (1U << 1) +#define ISO14443_3B_BIT_RATE_PCD_TO_PICC_848KBIT (1U << 2) +#define ISO14443_3B_BIT_RATE_PICC_TO_PCD_212KBIT (1U << 4) +#define ISO14443_3B_BIT_RATE_PICC_TO_PCD_424KBIT (1U << 5) +#define ISO14443_3B_BIT_RATE_PICC_TO_PCD_848KBIT (1U << 6) +#define ISO14443_3B_BIT_RATE_BOTH_SAME_COMPULSORY (1U << 7) + +#define ISO14443_3B_FRAME_OPTION_NAD (1U << 1) +#define ISO14443_3B_FRAME_OPTION_CID (1U << 0) + +typedef struct { + uint8_t bit_rate_capability; + uint8_t protocol_type : 4; + uint8_t max_frame_size : 4; + uint8_t fo : 2; + uint8_t adc : 2; + uint8_t fwi : 4; +} Iso14443_3bProtocolInfo; + +struct Iso14443_3bData { + uint8_t uid[ISO14443_3B_UID_SIZE]; + uint8_t app_data[ISO14443_3B_APP_DATA_SIZE]; + Iso14443_3bProtocolInfo protocol_info; +}; diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.c b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.c new file mode 100644 index 00000000000..f0c9b67ad1c --- /dev/null +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.c @@ -0,0 +1,121 @@ +#include "iso14443_3b_poller_i.h" + +#include + +#include + +#define TAG "ISO14443_3bPoller" + +const Iso14443_3bData* iso14443_3b_poller_get_data(Iso14443_3bPoller* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +static Iso14443_3bPoller* iso14443_3b_poller_alloc(Nfc* nfc) { + furi_assert(nfc); + + Iso14443_3bPoller* instance = malloc(sizeof(Iso14443_3bPoller)); + instance->nfc = nfc; + instance->tx_buffer = bit_buffer_alloc(ISO14443_3B_POLLER_MAX_BUFFER_SIZE); + instance->rx_buffer = bit_buffer_alloc(ISO14443_3B_POLLER_MAX_BUFFER_SIZE); + + nfc_config(instance->nfc, NfcModePoller, NfcTechIso14443b); + nfc_set_guard_time_us(instance->nfc, ISO14443_3B_GUARD_TIME_US); + nfc_set_fdt_poll_fc(instance->nfc, ISO14443_3B_FDT_POLL_FC); + nfc_set_fdt_poll_poll_us(instance->nfc, ISO14443_3B_POLL_POLL_MIN_US); + instance->data = iso14443_3b_alloc(); + + instance->iso14443_3b_event.data = &instance->iso14443_3b_event_data; + instance->general_event.protocol = NfcProtocolIso14443_3b; + instance->general_event.event_data = &instance->iso14443_3b_event; + instance->general_event.instance = instance; + + return instance; +} + +static void iso14443_3b_poller_free(Iso14443_3bPoller* instance) { + furi_assert(instance); + + furi_assert(instance->tx_buffer); + furi_assert(instance->rx_buffer); + furi_assert(instance->data); + + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + iso14443_3b_free(instance->data); + free(instance); +} + +static void iso14443_3b_poller_set_callback( + Iso14443_3bPoller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand iso14443_3b_poller_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolInvalid); + furi_assert(event.event_data); + + Iso14443_3bPoller* instance = context; + NfcEvent* nfc_event = event.event_data; + NfcCommand command = NfcCommandContinue; + + if(nfc_event->type == NfcEventTypePollerReady) { + if(instance->state != Iso14443_3bPollerStateActivated) { + Iso14443_3bError error = iso14443_3b_poller_activate(instance, instance->data); + if(error == Iso14443_3bErrorNone) { + instance->iso14443_3b_event.type = Iso14443_3bPollerEventTypeReady; + instance->iso14443_3b_event_data.error = error; + command = instance->callback(instance->general_event, instance->context); + } else { + instance->iso14443_3b_event.type = Iso14443_3bPollerEventTypeError; + instance->iso14443_3b_event_data.error = error; + command = instance->callback(instance->general_event, instance->context); + // Add delay to switch context + furi_delay_ms(100); + } + } else { + instance->iso14443_3b_event.type = Iso14443_3bPollerEventTypeReady; + instance->iso14443_3b_event_data.error = Iso14443_3bErrorNone; + command = instance->callback(instance->general_event, instance->context); + } + } + + return command; +} + +static bool iso14443_3b_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.instance); + furi_assert(event.protocol == NfcProtocolInvalid); + + bool protocol_detected = false; + Iso14443_3bPoller* instance = context; + NfcEvent* nfc_event = event.event_data; + furi_assert(instance->state == Iso14443_3bPollerStateIdle); + + if(nfc_event->type == NfcEventTypePollerReady) { + Iso14443_3bError error = iso14443_3b_poller_activate(instance, instance->data); + protocol_detected = (error == Iso14443_3bErrorNone); + } + + return protocol_detected; +} + +const NfcPollerBase nfc_poller_iso14443_3b = { + .alloc = (NfcPollerAlloc)iso14443_3b_poller_alloc, + .free = (NfcPollerFree)iso14443_3b_poller_free, + .set_callback = (NfcPollerSetCallback)iso14443_3b_poller_set_callback, + .run = (NfcPollerRun)iso14443_3b_poller_run, + .detect = (NfcPollerDetect)iso14443_3b_poller_detect, + .get_data = (NfcPollerGetData)iso14443_3b_poller_get_data, +}; diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.h b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.h new file mode 100644 index 00000000000..940903c1dcc --- /dev/null +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.h @@ -0,0 +1,89 @@ +#pragma once + +#include "iso14443_3b.h" +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Iso14443_3bPoller opaque type definition. + */ +typedef struct Iso14443_3bPoller Iso14443_3bPoller; + +/** + * @brief Enumeration of possible Iso14443_3b poller event types. + */ +typedef enum { + Iso14443_3bPollerEventTypeError, /**< The card was activated by the poller. */ + Iso14443_3bPollerEventTypeReady, /**< An error occured during activation procedure. */ +} Iso14443_3bPollerEventType; + +/** + * @brief Iso14443_3b poller event data. + */ +typedef union { + Iso14443_3bError error; /**< Error code indicating card activation fail reason. */ +} Iso14443_3bPollerEventData; + +/** + * @brief Iso14443_3b poller event structure. + * + * Upon emission of an event, an instance of this struct will be passed to the callback. + */ +typedef struct { + Iso14443_3bPollerEventType type; /**< Type of emmitted event. */ + Iso14443_3bPollerEventData* data; /**< Pointer to event specific data. */ +} Iso14443_3bPollerEvent; + +/** + * @brief Transmit and receive Iso14443_3b frames in poller mode. + * + * Must ONLY be used inside the callback function. + * + * The rx_buffer will be filled with any data received as a response to data + * sent from tx_buffer, with a timeout defined by the fwt parameter. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted. + * @param[out] rx_buffer pointer to the buffer to be filled with received data. + * @param[in] fwt frame wait time (response timeout), in carrier cycles. + * @return Iso14443_3bErrorNone on success, an error code on failure. + */ +Iso14443_3bError iso14443_3b_poller_send_frame( + Iso14443_3bPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer); + +/** + * @brief Perform collision resolution procedure. + * + * Must ONLY be used inside the callback function. + * + * Perfoms the collision resolution procedure as defined in Iso14443-3b. The data + * field will be filled with Iso14443-3b data on success. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the Iso14443_3b data structure to be filled. + * @return Iso14443_3bErrorNone on success, an error code on failure. + */ +Iso14443_3bError iso14443_3b_poller_activate(Iso14443_3bPoller* instance, Iso14443_3bData* data); + +/** + * @brief Send HALT command to the card. + * + * Must ONLY be used inside the callback function. + * + * Halts card and changes internal Iso14443_3bPoller state to Idle. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @return Iso14443_3bErrorNone on success, an error code on failure. + */ +Iso14443_3bError iso14443_3b_poller_halt(Iso14443_3bPoller* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_defs.h b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_defs.h new file mode 100644 index 00000000000..60225310abe --- /dev/null +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcPollerBase nfc_poller_iso14443_3b; diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.c b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.c new file mode 100644 index 00000000000..1ee5237c641 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.c @@ -0,0 +1,193 @@ +#include "iso14443_3b_poller_i.h" + +#include + +#define TAG "Iso14443_3bPoller" + +#define ISO14443_3B_ATTRIB_FRAME_SIZE_256 (0x08) + +static Iso14443_3bError iso14443_3b_poller_process_error(NfcError error) { + switch(error) { + case NfcErrorNone: + return Iso14443_3bErrorNone; + case NfcErrorTimeout: + return Iso14443_3bErrorTimeout; + default: + return Iso14443_3bErrorNotPresent; + } +} + +static Iso14443_3bError iso14443_3b_poller_prepare_trx(Iso14443_3bPoller* instance) { + furi_assert(instance); + + if(instance->state == Iso14443_3bPollerStateIdle) { + return iso14443_3b_poller_activate(instance, NULL); + } + + return Iso14443_3bErrorNone; +} + +static Iso14443_3bError iso14443_3b_poller_frame_exchange( + Iso14443_3bPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + + const size_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer); + furi_assert( + tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - ISO14443_CRC_SIZE); + + bit_buffer_copy(instance->tx_buffer, tx_buffer); + iso14443_crc_append(Iso14443CrcTypeB, instance->tx_buffer); + + Iso14443_3bError ret = Iso14443_3bErrorNone; + + do { + NfcError error = + nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt); + if(error != NfcErrorNone) { + ret = iso14443_3b_poller_process_error(error); + break; + } + + bit_buffer_copy(rx_buffer, instance->rx_buffer); + if(!iso14443_crc_check(Iso14443CrcTypeB, instance->rx_buffer)) { + ret = Iso14443_3bErrorWrongCrc; + break; + } + + iso14443_crc_trim(rx_buffer); + } while(false); + + return ret; +} + +Iso14443_3bError iso14443_3b_poller_activate(Iso14443_3bPoller* instance, Iso14443_3bData* data) { + furi_assert(instance); + furi_assert(instance->nfc); + + iso14443_3b_reset(data); + + Iso14443_3bError ret; + + do { + instance->state = Iso14443_3bPollerStateColResInProgress; + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + // Send REQB + bit_buffer_append_byte(instance->tx_buffer, 0x05); + bit_buffer_append_byte(instance->tx_buffer, 0x00); + bit_buffer_append_byte(instance->tx_buffer, 0x08); + + ret = iso14443_3b_poller_frame_exchange( + instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3B_FDT_POLL_FC); + if(ret != Iso14443_3bErrorNone) { + instance->state = Iso14443_3bPollerStateColResFailed; + break; + } + + typedef struct { + uint8_t flag; + uint8_t uid[ISO14443_3B_UID_SIZE]; + uint8_t app_data[ISO14443_3B_APP_DATA_SIZE]; + Iso14443_3bProtocolInfo protocol_info; + } Iso14443_3bAtqBLayout; + + if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(Iso14443_3bAtqBLayout)) { + FURI_LOG_D(TAG, "Unexpected REQB response"); + instance->state = Iso14443_3bPollerStateColResFailed; + ret = Iso14443_3bErrorCommunication; + break; + } + + instance->state = Iso14443_3bPollerStateActivationInProgress; + + const Iso14443_3bAtqBLayout* atqb = + (const Iso14443_3bAtqBLayout*)bit_buffer_get_data(instance->rx_buffer); + + memcpy(data->uid, atqb->uid, ISO14443_3B_UID_SIZE); + memcpy(data->app_data, atqb->app_data, ISO14443_3B_APP_DATA_SIZE); + + data->protocol_info = atqb->protocol_info; + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + // Send ATTRIB + bit_buffer_append_byte(instance->tx_buffer, 0x1d); + bit_buffer_append_bytes(instance->tx_buffer, data->uid, ISO14443_3B_UID_SIZE); + bit_buffer_append_byte(instance->tx_buffer, 0x00); + bit_buffer_append_byte(instance->tx_buffer, ISO14443_3B_ATTRIB_FRAME_SIZE_256); + bit_buffer_append_byte(instance->tx_buffer, 0x01); + bit_buffer_append_byte(instance->tx_buffer, 0x00); + + ret = iso14443_3b_poller_frame_exchange( + instance, instance->tx_buffer, instance->rx_buffer, iso14443_3b_get_fwt_fc_max(data)); + if(ret != Iso14443_3bErrorNone) { + instance->state = Iso14443_3bPollerStateActivationFailed; + break; + } + + if(bit_buffer_get_size_bytes(instance->rx_buffer) != 1 || + bit_buffer_get_byte(instance->rx_buffer, 0) != 0) { + FURI_LOG_D(TAG, "Unexpected ATTRIB response"); + instance->state = Iso14443_3bPollerStateActivationFailed; + ret = Iso14443_3bErrorCommunication; + break; + } + + instance->state = Iso14443_3bPollerStateActivated; + } while(false); + + return ret; +} + +Iso14443_3bError iso14443_3b_poller_halt(Iso14443_3bPoller* instance) { + furi_assert(instance); + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + bit_buffer_append_byte(instance->tx_buffer, 0x50); + bit_buffer_append_bytes(instance->tx_buffer, instance->data->uid, ISO14443_3B_UID_SIZE); + + Iso14443_3bError ret; + + do { + ret = iso14443_3b_poller_frame_exchange( + instance, instance->tx_buffer, instance->rx_buffer, ISO14443_3B_FDT_POLL_FC); + if(ret != Iso14443_3bErrorNone) { + break; + } + + if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(uint8_t) || + bit_buffer_get_byte(instance->rx_buffer, 0) != 0) { + ret = Iso14443_3bErrorCommunication; + break; + } + + instance->state = Iso14443_3bPollerStateIdle; + } while(false); + + return ret; +} + +Iso14443_3bError iso14443_3b_poller_send_frame( + Iso14443_3bPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer) { + Iso14443_3bError ret; + + do { + ret = iso14443_3b_poller_prepare_trx(instance); + if(ret != Iso14443_3bErrorNone) break; + + ret = iso14443_3b_poller_frame_exchange( + instance, tx_buffer, rx_buffer, iso14443_3b_get_fwt_fc_max(instance->data)); + } while(false); + + return ret; +} diff --git a/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.h b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.h new file mode 100644 index 00000000000..2503c2e41e8 --- /dev/null +++ b/lib/nfc/protocols/iso14443_3b/iso14443_3b_poller_i.h @@ -0,0 +1,39 @@ +#pragma once + +#include "iso14443_3b_poller.h" +#include "iso14443_3b_i.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISO14443_3B_POLLER_MAX_BUFFER_SIZE (256U) + +typedef enum { + Iso14443_3bPollerStateIdle, + Iso14443_3bPollerStateColResInProgress, + Iso14443_3bPollerStateColResFailed, + Iso14443_3bPollerStateActivationInProgress, + Iso14443_3bPollerStateActivationFailed, + Iso14443_3bPollerStateActivated, +} Iso14443_3bPollerState; + +struct Iso14443_3bPoller { + Nfc* nfc; + Iso14443_3bPollerState state; + Iso14443_3bData* data; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + + NfcGenericEvent general_event; + Iso14443_3bPollerEvent iso14443_3b_event; + Iso14443_3bPollerEventData iso14443_3b_event_data; + NfcGenericCallback callback; + void* context; +}; + +const Iso14443_3bData* iso14443_3b_poller_get_data(Iso14443_3bPoller* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a.c new file mode 100644 index 00000000000..9c2a530d53c --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a.c @@ -0,0 +1,300 @@ +#include "iso14443_4a_i.h" + +#include + +#define ISO14443_4A_PROTOCOL_NAME "ISO14443-4A" +#define ISO14443_4A_DEVICE_NAME "ISO14443-4A (Unknown)" + +#define ISO14443_4A_T0_KEY "T0" +#define ISO14443_4A_TA1_KEY "TA(1)" +#define ISO14443_4A_TB1_KEY "TB(1)" +#define ISO14443_4A_TC1_KEY "TC(1)" +#define ISO14443_4A_T1_TK_KEY "T1...Tk" + +#define ISO14443_4A_FDT_DEFAULT_FC ISO14443_3A_FDT_POLL_FC + +typedef enum { + Iso14443_4aInterfaceByteTA1, + Iso14443_4aInterfaceByteTB1, + Iso14443_4aInterfaceByteTC1, +} Iso14443_4aInterfaceByte; + +const NfcDeviceBase nfc_device_iso14443_4a = { + .protocol_name = ISO14443_4A_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)iso14443_4a_alloc, + .free = (NfcDeviceFree)iso14443_4a_free, + .reset = (NfcDeviceReset)iso14443_4a_reset, + .copy = (NfcDeviceCopy)iso14443_4a_copy, + .verify = (NfcDeviceVerify)iso14443_4a_verify, + .load = (NfcDeviceLoad)iso14443_4a_load, + .save = (NfcDeviceSave)iso14443_4a_save, + .is_equal = (NfcDeviceEqual)iso14443_4a_is_equal, + .get_name = (NfcDeviceGetName)iso14443_4a_get_device_name, + .get_uid = (NfcDeviceGetUid)iso14443_4a_get_uid, + .set_uid = (NfcDeviceSetUid)iso14443_4a_set_uid, + .get_base_data = (NfcDeviceGetBaseData)iso14443_4a_get_base_data, +}; + +Iso14443_4aData* iso14443_4a_alloc() { + Iso14443_4aData* data = malloc(sizeof(Iso14443_4aData)); + + data->iso14443_3a_data = iso14443_3a_alloc(); + data->ats_data.t1_tk = simple_array_alloc(&simple_array_config_uint8_t); + + return data; +} + +void iso14443_4a_free(Iso14443_4aData* data) { + furi_assert(data); + + simple_array_free(data->ats_data.t1_tk); + iso14443_3a_free(data->iso14443_3a_data); + + free(data); +} + +void iso14443_4a_reset(Iso14443_4aData* data) { + furi_assert(data); + + iso14443_3a_reset(data->iso14443_3a_data); + + data->ats_data.tl = 1; + data->ats_data.t0 = 0; + data->ats_data.ta_1 = 0; + data->ats_data.tb_1 = 0; + data->ats_data.tc_1 = 0; + + simple_array_reset(data->ats_data.t1_tk); +} + +void iso14443_4a_copy(Iso14443_4aData* data, const Iso14443_4aData* other) { + furi_assert(data); + furi_assert(other); + + iso14443_3a_copy(data->iso14443_3a_data, other->iso14443_3a_data); + + data->ats_data.tl = other->ats_data.tl; + data->ats_data.t0 = other->ats_data.t0; + data->ats_data.ta_1 = other->ats_data.ta_1; + data->ats_data.tb_1 = other->ats_data.tb_1; + data->ats_data.tc_1 = other->ats_data.tc_1; + + simple_array_copy(data->ats_data.t1_tk, other->ats_data.t1_tk); +} + +bool iso14443_4a_verify(Iso14443_4aData* data, const FuriString* device_type) { + UNUSED(data); + UNUSED(device_type); + + // Empty, unified file format only + return false; +} + +bool iso14443_4a_load(Iso14443_4aData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + + bool parsed = false; + + do { + if(!iso14443_3a_load(data->iso14443_3a_data, ff, version)) break; + + Iso14443_4aAtsData* ats_data = &data->ats_data; + + ats_data->tl = 1; + + if(flipper_format_key_exist(ff, ISO14443_4A_T0_KEY)) { + if(!flipper_format_read_hex(ff, ISO14443_4A_T0_KEY, &ats_data->t0, 1)) break; + ++ats_data->tl; + } + + if(ats_data->t0 & ISO14443_4A_ATS_T0_TA1) { + if(!flipper_format_key_exist(ff, ISO14443_4A_TA1_KEY)) break; + if(!flipper_format_read_hex(ff, ISO14443_4A_TA1_KEY, &ats_data->ta_1, 1)) break; + ++ats_data->tl; + } + if(ats_data->t0 & ISO14443_4A_ATS_T0_TB1) { + if(!flipper_format_key_exist(ff, ISO14443_4A_TB1_KEY)) break; + if(!flipper_format_read_hex(ff, ISO14443_4A_TB1_KEY, &ats_data->tb_1, 1)) break; + ++ats_data->tl; + } + if(ats_data->t0 & ISO14443_4A_ATS_T0_TC1) { + if(!flipper_format_key_exist(ff, ISO14443_4A_TC1_KEY)) break; + if(!flipper_format_read_hex(ff, ISO14443_4A_TC1_KEY, &ats_data->tc_1, 1)) break; + ++ats_data->tl; + } + + if(flipper_format_key_exist(ff, ISO14443_4A_T1_TK_KEY)) { + uint32_t t1_tk_size; + if(!flipper_format_get_value_count(ff, ISO14443_4A_T1_TK_KEY, &t1_tk_size)) break; + + if(t1_tk_size > 0) { + simple_array_init(ats_data->t1_tk, t1_tk_size); + if(!flipper_format_read_hex( + ff, + ISO14443_4A_T1_TK_KEY, + simple_array_get_data(ats_data->t1_tk), + t1_tk_size)) + break; + ats_data->tl += t1_tk_size; + } + } + parsed = true; + } while(false); + + return parsed; +} + +bool iso14443_4a_save(const Iso14443_4aData* data, FlipperFormat* ff) { + furi_assert(data); + + bool saved = false; + + do { + if(!iso14443_3a_save(data->iso14443_3a_data, ff)) break; + if(!flipper_format_write_comment_cstr(ff, ISO14443_4A_PROTOCOL_NAME " specific data")) + break; + + const Iso14443_4aAtsData* ats_data = &data->ats_data; + + if(ats_data->tl > 1) { + if(!flipper_format_write_hex(ff, ISO14443_4A_T0_KEY, &ats_data->t0, 1)) break; + + if(ats_data->t0 & ISO14443_4A_ATS_T0_TA1) { + if(!flipper_format_write_hex(ff, ISO14443_4A_TA1_KEY, &ats_data->ta_1, 1)) break; + } + if(ats_data->t0 & ISO14443_4A_ATS_T0_TB1) { + if(!flipper_format_write_hex(ff, ISO14443_4A_TB1_KEY, &ats_data->tb_1, 1)) break; + } + if(ats_data->t0 & ISO14443_4A_ATS_T0_TC1) { + if(!flipper_format_write_hex(ff, ISO14443_4A_TC1_KEY, &ats_data->tc_1, 1)) break; + } + + const uint32_t t1_tk_size = simple_array_get_count(ats_data->t1_tk); + if(t1_tk_size > 0) { + if(!flipper_format_write_hex( + ff, + ISO14443_4A_T1_TK_KEY, + simple_array_cget_data(ats_data->t1_tk), + t1_tk_size)) + break; + } + } + saved = true; + } while(false); + + return saved; +} + +bool iso14443_4a_is_equal(const Iso14443_4aData* data, const Iso14443_4aData* other) { + return iso14443_3a_is_equal(data->iso14443_3a_data, other->iso14443_3a_data); +} + +const char* iso14443_4a_get_device_name(const Iso14443_4aData* data, NfcDeviceNameType name_type) { + UNUSED(data); + UNUSED(name_type); + return ISO14443_4A_DEVICE_NAME; +} + +const uint8_t* iso14443_4a_get_uid(const Iso14443_4aData* data, size_t* uid_len) { + return iso14443_3a_get_uid(data->iso14443_3a_data, uid_len); +} + +bool iso14443_4a_set_uid(Iso14443_4aData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + return iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len); +} + +Iso14443_3aData* iso14443_4a_get_base_data(const Iso14443_4aData* data) { + furi_assert(data); + + return data->iso14443_3a_data; +} + +uint16_t iso14443_4a_get_frame_size_max(const Iso14443_4aData* data) { + furi_assert(data); + + const uint8_t fsci = data->ats_data.t0 & 0x0F; + + if(fsci < 5) { + return fsci * 8 + 16; + } else if(fsci == 5) { + return 64; + } else if(fsci == 6) { + return 96; + } else if(fsci < 13) { + return 128U << (fsci - 7); + } else { + return 0; + } +} + +uint32_t iso14443_4a_get_fwt_fc_max(const Iso14443_4aData* data) { + furi_assert(data); + + uint32_t fwt_fc_max = ISO14443_4A_FDT_DEFAULT_FC; + + do { + if(!(data->ats_data.tl > 1)) break; + if(!(data->ats_data.t0 & ISO14443_4A_ATS_T0_TB1)) break; + + const uint8_t fwi = data->ats_data.tb_1 >> 4; + if(fwi == 0x0F) break; + + fwt_fc_max = 4096UL << fwi; + } while(false); + + return fwt_fc_max; +} + +const uint8_t* iso14443_4a_get_historical_bytes(const Iso14443_4aData* data, uint32_t* count) { + furi_assert(data); + furi_assert(count); + + *count = simple_array_get_count(data->ats_data.t1_tk); + return simple_array_cget_data(data->ats_data.t1_tk); +} + +bool iso14443_4a_supports_bit_rate(const Iso14443_4aData* data, Iso14443_4aBitRate bit_rate) { + furi_assert(data); + + if(!(data->ats_data.t0 & ISO14443_4A_ATS_T0_TA1)) + return bit_rate == Iso14443_4aBitRateBoth106Kbit; + + const uint8_t ta_1 = data->ats_data.ta_1; + + switch(bit_rate) { + case Iso14443_4aBitRateBoth106Kbit: + return ta_1 == ISO14443_4A_ATS_TA1_BOTH_SAME_COMPULSORY; + case Iso14443_4aBitRatePiccToPcd212Kbit: + return ta_1 & ISO14443_4A_ATS_TA1_PCD_TO_PICC_212KBIT; + case Iso14443_4aBitRatePiccToPcd424Kbit: + return ta_1 & ISO14443_4A_ATS_TA1_PCD_TO_PICC_424KBIT; + case Iso14443_4aBitRatePiccToPcd848Kbit: + return ta_1 & ISO14443_4A_ATS_TA1_PCD_TO_PICC_848KBIT; + case Iso14443_4aBitRatePcdToPicc212Kbit: + return ta_1 & ISO14443_4A_ATS_TA1_PICC_TO_PCD_212KBIT; + case Iso14443_4aBitRatePcdToPicc424Kbit: + return ta_1 & ISO14443_4A_ATS_TA1_PICC_TO_PCD_424KBIT; + case Iso14443_4aBitRatePcdToPicc848Kbit: + return ta_1 & ISO14443_4A_ATS_TA1_PICC_TO_PCD_848KBIT; + default: + return false; + } +} + +bool iso14443_4a_supports_frame_option(const Iso14443_4aData* data, Iso14443_4aFrameOption option) { + furi_assert(data); + + const Iso14443_4aAtsData* ats_data = &data->ats_data; + if(!(ats_data->t0 & ISO14443_4A_ATS_T0_TC1)) return false; + + switch(option) { + case Iso14443_4aFrameOptionNad: + return ats_data->tc_1 & ISO14443_4A_ATS_TC1_NAD; + case Iso14443_4aFrameOptionCid: + return ats_data->tc_1 & ISO14443_4A_ATS_TC1_CID; + default: + return false; + } +} diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a.h new file mode 100644 index 00000000000..df212152de1 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a.h @@ -0,0 +1,87 @@ +#pragma once + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + Iso14443_4aErrorNone, + Iso14443_4aErrorNotPresent, + Iso14443_4aErrorProtocol, + Iso14443_4aErrorTimeout, +} Iso14443_4aError; + +typedef enum { + Iso14443_4aBitRateBoth106Kbit, + Iso14443_4aBitRatePiccToPcd212Kbit, + Iso14443_4aBitRatePiccToPcd424Kbit, + Iso14443_4aBitRatePiccToPcd848Kbit, + Iso14443_4aBitRatePcdToPicc212Kbit, + Iso14443_4aBitRatePcdToPicc424Kbit, + Iso14443_4aBitRatePcdToPicc848Kbit, +} Iso14443_4aBitRate; + +typedef enum { + Iso14443_4aFrameOptionNad, + Iso14443_4aFrameOptionCid, +} Iso14443_4aFrameOption; + +typedef struct { + uint8_t tl; + uint8_t t0; + uint8_t ta_1; + uint8_t tb_1; + uint8_t tc_1; + SimpleArray* t1_tk; +} Iso14443_4aAtsData; + +typedef struct { + Iso14443_3aData* iso14443_3a_data; + Iso14443_4aAtsData ats_data; +} Iso14443_4aData; + +// Virtual methods + +Iso14443_4aData* iso14443_4a_alloc(); + +void iso14443_4a_free(Iso14443_4aData* data); + +void iso14443_4a_reset(Iso14443_4aData* data); + +void iso14443_4a_copy(Iso14443_4aData* data, const Iso14443_4aData* other); + +bool iso14443_4a_verify(Iso14443_4aData* data, const FuriString* device_type); + +bool iso14443_4a_load(Iso14443_4aData* data, FlipperFormat* ff, uint32_t version); + +bool iso14443_4a_save(const Iso14443_4aData* data, FlipperFormat* ff); + +bool iso14443_4a_is_equal(const Iso14443_4aData* data, const Iso14443_4aData* other); + +const char* iso14443_4a_get_device_name(const Iso14443_4aData* data, NfcDeviceNameType name_type); + +const uint8_t* iso14443_4a_get_uid(const Iso14443_4aData* data, size_t* uid_len); + +bool iso14443_4a_set_uid(Iso14443_4aData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_3aData* iso14443_4a_get_base_data(const Iso14443_4aData* data); + +// Getters & Tests + +uint16_t iso14443_4a_get_frame_size_max(const Iso14443_4aData* data); + +uint32_t iso14443_4a_get_fwt_fc_max(const Iso14443_4aData* data); + +const uint8_t* iso14443_4a_get_historical_bytes(const Iso14443_4aData* data, uint32_t* count); + +bool iso14443_4a_supports_bit_rate(const Iso14443_4aData* data, Iso14443_4aBitRate bit_rate); + +bool iso14443_4a_supports_frame_option(const Iso14443_4aData* data, Iso14443_4aFrameOption option); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_device_defs.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_device_defs.h new file mode 100644 index 00000000000..db372f81030 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_device_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcDeviceBase nfc_device_iso14443_4a; diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_i.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_i.c new file mode 100644 index 00000000000..f6e3acc5c1f --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_i.c @@ -0,0 +1,71 @@ +#include "iso14443_4a_i.h" + +bool iso14443_4a_ats_parse(Iso14443_4aAtsData* data, const BitBuffer* buf) { + bool can_parse = false; + + do { + const size_t buf_size = bit_buffer_get_size_bytes(buf); + if(buf_size == 0) break; + + size_t current_index = 0; + + const uint8_t tl = bit_buffer_get_byte(buf, current_index++); + if(tl != buf_size) break; + + data->tl = tl; + + if(tl > 1) { + const uint8_t t0 = bit_buffer_get_byte(buf, current_index++); + + const bool has_ta_1 = t0 & ISO14443_4A_ATS_T0_TA1; + const bool has_tb_1 = t0 & ISO14443_4A_ATS_T0_TB1; + const bool has_tc_1 = t0 & ISO14443_4A_ATS_T0_TC1; + + const uint8_t buf_size_min = + 2 + (has_ta_1 ? 1 : 0) + (has_tb_1 ? 1 : 0) + (has_tc_1 ? 1 : 0); + + if(buf_size < buf_size_min) break; + + data->t0 = t0; + + if(has_ta_1) { + data->ta_1 = bit_buffer_get_byte(buf, current_index++); + } + if(has_tb_1) { + data->tb_1 = bit_buffer_get_byte(buf, current_index++); + } + if(has_tc_1) { + data->tc_1 = bit_buffer_get_byte(buf, current_index++); + } + + const uint8_t t1_tk_size = buf_size - buf_size_min; + + if(t1_tk_size > 0) { + simple_array_init(data->t1_tk, t1_tk_size); + bit_buffer_write_bytes_mid( + buf, simple_array_get_data(data->t1_tk), current_index, t1_tk_size); + } + } + + can_parse = true; + } while(false); + + return can_parse; +} + +Iso14443_4aError iso14443_4a_process_error(Iso14443_3aError error) { + switch(error) { + case Iso14443_3aErrorNone: + return Iso14443_4aErrorNone; + case Iso14443_3aErrorNotPresent: + return Iso14443_4aErrorNotPresent; + case Iso14443_3aErrorColResFailed: + case Iso14443_3aErrorCommunication: + case Iso14443_3aErrorWrongCrc: + return Iso14443_4aErrorProtocol; + case Iso14443_3aErrorTimeout: + return Iso14443_4aErrorTimeout; + default: + return Iso14443_4aErrorProtocol; + } +} diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_i.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_i.h new file mode 100644 index 00000000000..e5483a6ba1f --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_i.h @@ -0,0 +1,26 @@ +#pragma once + +#include "iso14443_4a.h" + +#define ISO14443_4A_CMD_READ_ATS (0xE0) + +// ATS bit definitions +#define ISO14443_4A_ATS_T0_TA1 (1U << 4) +#define ISO14443_4A_ATS_T0_TB1 (1U << 5) +#define ISO14443_4A_ATS_T0_TC1 (1U << 6) + +#define ISO14443_4A_ATS_TA1_BOTH_106KBIT (0U << 0) +#define ISO14443_4A_ATS_TA1_PCD_TO_PICC_212KBIT (1U << 0) +#define ISO14443_4A_ATS_TA1_PCD_TO_PICC_424KBIT (1U << 1) +#define ISO14443_4A_ATS_TA1_PCD_TO_PICC_848KBIT (1U << 2) +#define ISO14443_4A_ATS_TA1_PICC_TO_PCD_212KBIT (1U << 4) +#define ISO14443_4A_ATS_TA1_PICC_TO_PCD_424KBIT (1U << 5) +#define ISO14443_4A_ATS_TA1_PICC_TO_PCD_848KBIT (1U << 6) +#define ISO14443_4A_ATS_TA1_BOTH_SAME_COMPULSORY (1U << 7) + +#define ISO14443_4A_ATS_TC1_NAD (1U << 0) +#define ISO14443_4A_ATS_TC1_CID (1U << 1) + +bool iso14443_4a_ats_parse(Iso14443_4aAtsData* data, const BitBuffer* buf); + +Iso14443_4aError iso14443_4a_process_error(Iso14443_3aError error); diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c new file mode 100644 index 00000000000..95612bf54d0 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.c @@ -0,0 +1,99 @@ +#include "iso14443_4a_listener_i.h" + +#include +#include + +#define TAG "Iso14443_4aListener" + +#define ISO14443_4A_LISTENER_BUF_SIZE (256U) + +static Iso14443_4aListener* + iso14443_4a_listener_alloc(Iso14443_3aListener* iso14443_3a_listener, Iso14443_4aData* data) { + furi_assert(iso14443_3a_listener); + + Iso14443_4aListener* instance = malloc(sizeof(Iso14443_4aListener)); + instance->iso14443_3a_listener = iso14443_3a_listener; + instance->data = data; + + instance->tx_buffer = bit_buffer_alloc(ISO14443_4A_LISTENER_BUF_SIZE); + + instance->iso14443_4a_event.data = &instance->iso14443_4a_event_data; + instance->generic_event.protocol = NfcProtocolIso14443_4a; + instance->generic_event.instance = instance; + instance->generic_event.event_data = &instance->iso14443_4a_event; + + return instance; +} + +static void iso14443_4a_listener_free(Iso14443_4aListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + furi_assert(instance->tx_buffer); + + bit_buffer_free(instance->tx_buffer); + free(instance); +} + +static void iso14443_4a_listener_set_callback( + Iso14443_4aListener* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + + instance->callback = callback; + instance->context = context; +} + +static const Iso14443_4aData* iso14443_4a_listener_get_data(Iso14443_4aListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +static NfcCommand iso14443_4a_listener_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + furi_assert(event.event_data); + + Iso14443_4aListener* instance = context; + Iso14443_3aListenerEvent* iso14443_3a_event = event.event_data; + BitBuffer* rx_buffer = iso14443_3a_event->data->buffer; + NfcCommand command = NfcCommandContinue; + + if(iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame) { + if(instance->state == Iso14443_4aListenerStateIdle) { + if(bit_buffer_get_size_bytes(rx_buffer) == 2 && + bit_buffer_get_byte(rx_buffer, 0) == ISO14443_4A_CMD_READ_ATS) { + if(iso14443_4a_listener_send_ats(instance, &instance->data->ats_data) != + Iso14443_4aErrorNone) { + command = NfcCommandContinue; + } else { + instance->state = Iso14443_4aListenerStateActive; + } + } + } else { + instance->iso14443_4a_event.type = Iso14443_4aListenerEventTypeReceivedData; + instance->iso14443_4a_event.data->buffer = rx_buffer; + + if(instance->callback) { + command = instance->callback(instance->generic_event, instance->context); + } + } + } else if( + iso14443_3a_event->type == Iso14443_3aListenerEventTypeHalted || + iso14443_3a_event->type == Iso14443_3aListenerEventTypeFieldOff) { + instance->state = Iso14443_4aListenerStateIdle; + command = NfcCommandContinue; + } + + return command; +} + +const NfcListenerBase nfc_listener_iso14443_4a = { + .alloc = (NfcListenerAlloc)iso14443_4a_listener_alloc, + .free = (NfcListenerFree)iso14443_4a_listener_free, + .set_callback = (NfcListenerSetCallback)iso14443_4a_listener_set_callback, + .get_data = (NfcListenerGetData)iso14443_4a_listener_get_data, + .run = (NfcListenerRun)iso14443_4a_listener_run, +}; diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h new file mode 100644 index 00000000000..ba649847b27 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "iso14443_4a.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Iso14443_4aListener Iso14443_4aListener; + +typedef enum { + Iso14443_4aListenerEventTypeHalted, + Iso14443_4aListenerEventTypeReceivedData, +} Iso14443_4aListenerEventType; + +typedef struct { + BitBuffer* buffer; +} Iso14443_4aListenerEventData; + +typedef struct { + Iso14443_4aListenerEventType type; + Iso14443_4aListenerEventData* data; +} Iso14443_4aListenerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_defs.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_defs.h new file mode 100644 index 00000000000..ef70a5e1415 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcListenerBase nfc_listener_iso14443_4a; diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.c new file mode 100644 index 00000000000..8590c22ade5 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.c @@ -0,0 +1,32 @@ +#include "iso14443_4a_listener_i.h" + +#include + +Iso14443_4aError + iso14443_4a_listener_send_ats(Iso14443_4aListener* instance, const Iso14443_4aAtsData* data) { + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, data->tl); + + if(data->tl > 1) { + bit_buffer_append_byte(instance->tx_buffer, data->t0); + if(data->t0 & ISO14443_4A_ATS_T0_TA1) { + bit_buffer_append_byte(instance->tx_buffer, data->ta_1); + } + if(data->t0 & ISO14443_4A_ATS_T0_TB1) { + bit_buffer_append_byte(instance->tx_buffer, data->tb_1); + } + if(data->t0 & ISO14443_4A_ATS_T0_TC1) { + bit_buffer_append_byte(instance->tx_buffer, data->tc_1); + } + + const uint32_t t1_tk_size = simple_array_get_count(data->t1_tk); + if(t1_tk_size != 0) { + bit_buffer_append_bytes( + instance->tx_buffer, simple_array_cget_data(data->t1_tk), t1_tk_size); + } + } + + const Iso14443_3aError error = iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, instance->tx_buffer); + return iso14443_4a_process_error(error); +} diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h new file mode 100644 index 00000000000..d4e884f6f05 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_listener_i.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include "iso14443_4a_listener.h" +#include "iso14443_4a_i.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + Iso14443_4aListenerStateIdle, + Iso14443_4aListenerStateActive, +} Iso14443_4aListenerState; + +struct Iso14443_4aListener { + Iso14443_3aListener* iso14443_3a_listener; + Iso14443_4aData* data; + Iso14443_4aListenerState state; + + BitBuffer* tx_buffer; + + NfcGenericEvent generic_event; + Iso14443_4aListenerEvent iso14443_4a_event; + Iso14443_4aListenerEventData iso14443_4a_event_data; + NfcGenericCallback callback; + void* context; +}; + +Iso14443_4aError + iso14443_4a_listener_send_ats(Iso14443_4aListener* instance, const Iso14443_4aAtsData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.c new file mode 100644 index 00000000000..e20048b509d --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.c @@ -0,0 +1,153 @@ +#include "iso14443_4a_poller_i.h" + +#include + +#include + +#define TAG "Iso14443_4aPoller" + +#define ISO14443_4A_POLLER_BUF_SIZE (256U) + +typedef NfcCommand (*Iso14443_4aPollerStateHandler)(Iso14443_4aPoller* instance); + +const Iso14443_4aData* iso14443_4a_poller_get_data(Iso14443_4aPoller* instance) { + furi_assert(instance); + + return instance->data; +} + +static Iso14443_4aPoller* iso14443_4a_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller) { + Iso14443_4aPoller* instance = malloc(sizeof(Iso14443_4aPoller)); + instance->iso14443_3a_poller = iso14443_3a_poller; + instance->data = iso14443_4a_alloc(); + instance->iso14443_4_layer = iso14443_4_layer_alloc(); + instance->tx_buffer = bit_buffer_alloc(ISO14443_4A_POLLER_BUF_SIZE); + instance->rx_buffer = bit_buffer_alloc(ISO14443_4A_POLLER_BUF_SIZE); + + instance->iso14443_4a_event.data = &instance->iso14443_4a_event_data; + + instance->general_event.protocol = NfcProtocolIso14443_4a; + instance->general_event.event_data = &instance->iso14443_4a_event; + instance->general_event.instance = instance; + + return instance; +} + +static void iso14443_4a_poller_free(Iso14443_4aPoller* instance) { + furi_assert(instance); + + iso14443_4a_free(instance->data); + iso14443_4_layer_free(instance->iso14443_4_layer); + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + free(instance); +} + +static NfcCommand iso14443_4a_poller_handler_idle(Iso14443_4aPoller* instance) { + iso14443_3a_copy( + instance->data->iso14443_3a_data, + iso14443_3a_poller_get_data(instance->iso14443_3a_poller)); + + iso14443_4_layer_reset(instance->iso14443_4_layer); + + instance->poller_state = Iso14443_4aPollerStateReadAts; + return NfcCommandContinue; +} + +static NfcCommand iso14443_4a_poller_handler_read_ats(Iso14443_4aPoller* instance) { + Iso14443_4aError error = iso14443_4a_poller_read_ats(instance, &instance->data->ats_data); + if(error == Iso14443_4aErrorNone) { + FURI_LOG_D(TAG, "Read ATS success"); + instance->poller_state = Iso14443_4aPollerStateReady; + } else { + FURI_LOG_D(TAG, "Failed to read ATS"); + instance->poller_state = Iso14443_4aPollerStateError; + } + + return NfcCommandContinue; +} + +static NfcCommand iso14443_4a_poller_handler_error(Iso14443_4aPoller* instance) { + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + instance->iso14443_4a_event_data.error = instance->error; + NfcCommand command = instance->callback(instance->general_event, instance->context); + instance->poller_state = Iso14443_4aPollerStateIdle; + return command; +} + +static NfcCommand iso14443_4a_poller_handler_ready(Iso14443_4aPoller* instance) { + instance->iso14443_4a_event.type = Iso14443_4aPollerEventTypeReady; + NfcCommand command = instance->callback(instance->general_event, instance->context); + return command; +} + +static const Iso14443_4aPollerStateHandler + iso14443_4a_poller_state_handler[Iso14443_4aPollerStateNum] = { + [Iso14443_4aPollerStateIdle] = iso14443_4a_poller_handler_idle, + [Iso14443_4aPollerStateReadAts] = iso14443_4a_poller_handler_read_ats, + [Iso14443_4aPollerStateError] = iso14443_4a_poller_handler_error, + [Iso14443_4aPollerStateReady] = iso14443_4a_poller_handler_ready, +}; + +static void iso14443_4a_poller_set_callback( + Iso14443_4aPoller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand iso14443_4a_poller_run(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_3a); + + Iso14443_4aPoller* instance = context; + furi_assert(instance); + furi_assert(instance->callback); + + Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data; + furi_assert(iso14443_3a_event); + + NfcCommand command = NfcCommandContinue; + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + command = iso14443_4a_poller_state_handler[instance->poller_state](instance); + } else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) { + instance->iso14443_4a_event.type = Iso14443_4aPollerEventTypeError; + command = instance->callback(instance->general_event, instance->context); + } + + return command; +} + +static bool iso14443_4a_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_3a); + + const Iso14443_4aPoller* instance = context; + furi_assert(instance); + + const Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data; + furi_assert(iso14443_3a_event); + iso14443_3a_copy( + instance->data->iso14443_3a_data, + iso14443_3a_poller_get_data(instance->iso14443_3a_poller)); + + bool protocol_detected = false; + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + protocol_detected = iso14443_3a_supports_iso14443_4(instance->data->iso14443_3a_data); + } + + return protocol_detected; +} + +const NfcPollerBase nfc_poller_iso14443_4a = { + .alloc = (NfcPollerAlloc)iso14443_4a_poller_alloc, + .free = (NfcPollerFree)iso14443_4a_poller_free, + .set_callback = (NfcPollerSetCallback)iso14443_4a_poller_set_callback, + .run = (NfcPollerRun)iso14443_4a_poller_run, + .detect = (NfcPollerDetect)iso14443_4a_poller_detect, + .get_data = (NfcPollerGetData)iso14443_4a_poller_get_data, +}; diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h new file mode 100644 index 00000000000..73eb6ef74da --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h @@ -0,0 +1,87 @@ +#pragma once + +#include + +#include "iso14443_4a.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Iso14443_4aPoller opaque type definition. + */ +typedef struct Iso14443_4aPoller Iso14443_4aPoller; + +/** + * @brief Enumeration of possible Iso14443_4a poller event types. + */ +typedef enum { + Iso14443_4aPollerEventTypeError, /**< The card was activated by the poller. */ + Iso14443_4aPollerEventTypeReady, /**< An error occured during activation procedure. */ +} Iso14443_4aPollerEventType; + +/** + * @brief Iso14443_4a poller event data. + */ +typedef union { + Iso14443_4aError error; /**< Error code indicating card activation fail reason. */ +} Iso14443_4aPollerEventData; + +/** + * @brief Iso14443_4a poller event structure. + * + * Upon emission of an event, an instance of this struct will be passed to the callback. + */ +typedef struct { + Iso14443_4aPollerEventType type; /**< Type of emmitted event. */ + Iso14443_4aPollerEventData* data; /**< Pointer to event specific data. */ +} Iso14443_4aPollerEvent; + +/** + * @brief Transmit and receive Iso14443_4a blocks in poller mode. + * + * Must ONLY be used inside the callback function. + * + * The rx_buffer will be filled with any data received as a response to data + * sent from tx_buffer. The fwt parameter is calculated during activation procedure. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted. + * @param[out] rx_buffer pointer to the buffer to be filled with received data. + * @return Iso14443_4aErrorNone on success, an error code on failure. + */ +Iso14443_4aError iso14443_4a_poller_send_block( + Iso14443_4aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer); + +/** + * @brief Send HALT command to the card. + * + * Must ONLY be used inside the callback function. + * + * Halts card and changes internal Iso14443_4aPoller state to Idle. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @return Iso14443_4aErrorNone on success, an error code on failure. + */ +Iso14443_4aError iso14443_4a_poller_halt(Iso14443_4aPoller* instance); + +/** + * @brief Read Answer To Select (ATS) from the card. + * + * Must ONLY be used inside the callback function. + * + * Send Request Answer To Select (RATS) command to the card and parse the response. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the buffer to be filled with ATS data. + * @return Iso14443_4aErrorNone on success, an error code on failure. + */ +Iso14443_4aError + iso14443_4a_poller_read_ats(Iso14443_4aPoller* instance, Iso14443_4aAtsData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_defs.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_defs.h new file mode 100644 index 00000000000..aa62166742b --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcPollerBase nfc_poller_iso14443_4a; diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c new file mode 100644 index 00000000000..938e4e715fd --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.c @@ -0,0 +1,83 @@ +#include "iso14443_4a_poller_i.h" + +#include + +#include "iso14443_4a_i.h" + +#define TAG "Iso14443_4aPoller" + +#define ISO14443_4A_FSDI_256 (0x8U) + +Iso14443_4aError iso14443_4a_poller_halt(Iso14443_4aPoller* instance) { + furi_assert(instance); + + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + instance->poller_state = Iso14443_4aPollerStateIdle; + + return Iso14443_4aErrorNone; +} + +Iso14443_4aError + iso14443_4a_poller_read_ats(Iso14443_4aPoller* instance, Iso14443_4aAtsData* data) { + furi_assert(instance); + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, ISO14443_4A_CMD_READ_ATS); + bit_buffer_append_byte(instance->tx_buffer, ISO14443_4A_FSDI_256 << 4); + + Iso14443_4aError error = Iso14443_4aErrorNone; + + do { + const Iso14443_3aError iso14443_3a_error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + ISO14443_4A_POLLER_ATS_FWT_FC); + + if(iso14443_3a_error != Iso14443_3aErrorNone) { + FURI_LOG_E(TAG, "ATS request failed"); + error = iso14443_4a_process_error(iso14443_3a_error); + break; + + } else if(!iso14443_4a_ats_parse(data, instance->rx_buffer)) { + FURI_LOG_E(TAG, "Failed to parse ATS response"); + error = Iso14443_4aErrorProtocol; + break; + } + + } while(false); + + return error; +} + +Iso14443_4aError iso14443_4a_poller_send_block( + Iso14443_4aPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer) { + furi_assert(instance); + + bit_buffer_reset(instance->tx_buffer); + iso14443_4_layer_encode_block(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer); + + Iso14443_4aError error = Iso14443_4aErrorNone; + + do { + Iso14443_3aError iso14443_3a_error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + iso14443_4a_get_fwt_fc_max(instance->data)); + + if(iso14443_3a_error != Iso14443_3aErrorNone) { + error = iso14443_4a_process_error(iso14443_3a_error); + break; + + } else if(!iso14443_4_layer_decode_block( + instance->iso14443_4_layer, rx_buffer, instance->rx_buffer)) { + error = Iso14443_4aErrorProtocol; + break; + } + } while(false); + + return error; +} diff --git a/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.h b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.h new file mode 100644 index 00000000000..7aae852e437 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4a/iso14443_4a_poller_i.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include + +#include "iso14443_4a_poller.h" +#include "iso14443_4a_i.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISO14443_4A_POLLER_ATS_FWT_FC (40000) + +typedef enum { + Iso14443_4aPollerStateIdle, + Iso14443_4aPollerStateReadAts, + Iso14443_4aPollerStateError, + Iso14443_4aPollerStateReady, + + Iso14443_4aPollerStateNum, +} Iso14443_4aPollerState; + +typedef enum { + Iso14443_4aPollerSessionStateIdle, + Iso14443_4aPollerSessionStateActive, + Iso14443_4aPollerSessionStateStopRequest, +} Iso14443_4aPollerSessionState; + +struct Iso14443_4aPoller { + Iso14443_3aPoller* iso14443_3a_poller; + Iso14443_4aPollerState poller_state; + Iso14443_4aPollerSessionState session_state; + Iso14443_4aError error; + Iso14443_4aData* data; + Iso14443_4Layer* iso14443_4_layer; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + Iso14443_4aPollerEventData iso14443_4a_event_data; + Iso14443_4aPollerEvent iso14443_4a_event; + NfcGenericEvent general_event; + NfcGenericCallback callback; + void* context; +}; + +const Iso14443_4aData* iso14443_4a_poller_get_data(Iso14443_4aPoller* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_4b/iso14443_4b.c b/lib/nfc/protocols/iso14443_4b/iso14443_4b.c new file mode 100644 index 00000000000..19f2939da06 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4b/iso14443_4b.c @@ -0,0 +1,94 @@ +#include "iso14443_4b_i.h" + +#include +#include + +#define ISO14443_4B_PROTOCOL_NAME "ISO14443-4B" +#define ISO14443_4B_DEVICE_NAME "ISO14443-4B (Unknown)" + +const NfcDeviceBase nfc_device_iso14443_4b = { + .protocol_name = ISO14443_4B_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)iso14443_4b_alloc, + .free = (NfcDeviceFree)iso14443_4b_free, + .reset = (NfcDeviceReset)iso14443_4b_reset, + .copy = (NfcDeviceCopy)iso14443_4b_copy, + .verify = (NfcDeviceVerify)iso14443_4b_verify, + .load = (NfcDeviceLoad)iso14443_4b_load, + .save = (NfcDeviceSave)iso14443_4b_save, + .is_equal = (NfcDeviceEqual)iso14443_4b_is_equal, + .get_name = (NfcDeviceGetName)iso14443_4b_get_device_name, + .get_uid = (NfcDeviceGetUid)iso14443_4b_get_uid, + .set_uid = (NfcDeviceSetUid)iso14443_4b_set_uid, + .get_base_data = (NfcDeviceGetBaseData)iso14443_4b_get_base_data, +}; + +Iso14443_4bData* iso14443_4b_alloc() { + Iso14443_4bData* data = malloc(sizeof(Iso14443_4bData)); + + data->iso14443_3b_data = iso14443_3b_alloc(); + return data; +} + +void iso14443_4b_free(Iso14443_4bData* data) { + furi_assert(data); + + iso14443_3b_free(data->iso14443_3b_data); + free(data); +} + +void iso14443_4b_reset(Iso14443_4bData* data) { + furi_assert(data); + + iso14443_3b_reset(data->iso14443_3b_data); +} + +void iso14443_4b_copy(Iso14443_4bData* data, const Iso14443_4bData* other) { + furi_assert(data); + furi_assert(other); + + iso14443_3b_copy(data->iso14443_3b_data, other->iso14443_3b_data); +} + +bool iso14443_4b_verify(Iso14443_4bData* data, const FuriString* device_type) { + UNUSED(data); + UNUSED(device_type); + + // Empty, unified file format only + return false; +} + +bool iso14443_4b_load(Iso14443_4bData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + return iso14443_3b_load(data->iso14443_3b_data, ff, version); +} + +bool iso14443_4b_save(const Iso14443_4bData* data, FlipperFormat* ff) { + furi_assert(data); + return iso14443_3b_save(data->iso14443_3b_data, ff); +} + +bool iso14443_4b_is_equal(const Iso14443_4bData* data, const Iso14443_4bData* other) { + return iso14443_3b_is_equal(data->iso14443_3b_data, other->iso14443_3b_data); +} + +const char* iso14443_4b_get_device_name(const Iso14443_4bData* data, NfcDeviceNameType name_type) { + UNUSED(data); + UNUSED(name_type); + return ISO14443_4B_DEVICE_NAME; +} + +const uint8_t* iso14443_4b_get_uid(const Iso14443_4bData* data, size_t* uid_len) { + return iso14443_3b_get_uid(data->iso14443_3b_data, uid_len); +} + +bool iso14443_4b_set_uid(Iso14443_4bData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + return iso14443_3b_set_uid(data->iso14443_3b_data, uid, uid_len); +} + +Iso14443_3bData* iso14443_4b_get_base_data(const Iso14443_4bData* data) { + furi_assert(data); + + return data->iso14443_3b_data; +} diff --git a/lib/nfc/protocols/iso14443_4b/iso14443_4b.h b/lib/nfc/protocols/iso14443_4b/iso14443_4b.h new file mode 100644 index 00000000000..1f269ed9144 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4b/iso14443_4b.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + Iso14443_4bErrorNone, + Iso14443_4bErrorNotPresent, + Iso14443_4bErrorProtocol, + Iso14443_4bErrorTimeout, +} Iso14443_4bError; + +typedef struct Iso14443_4bData Iso14443_4bData; + +// Virtual methods + +Iso14443_4bData* iso14443_4b_alloc(); + +void iso14443_4b_free(Iso14443_4bData* data); + +void iso14443_4b_reset(Iso14443_4bData* data); + +void iso14443_4b_copy(Iso14443_4bData* data, const Iso14443_4bData* other); + +bool iso14443_4b_verify(Iso14443_4bData* data, const FuriString* device_type); + +bool iso14443_4b_load(Iso14443_4bData* data, FlipperFormat* ff, uint32_t version); + +bool iso14443_4b_save(const Iso14443_4bData* data, FlipperFormat* ff); + +bool iso14443_4b_is_equal(const Iso14443_4bData* data, const Iso14443_4bData* other); + +const char* iso14443_4b_get_device_name(const Iso14443_4bData* data, NfcDeviceNameType name_type); + +const uint8_t* iso14443_4b_get_uid(const Iso14443_4bData* data, size_t* uid_len); + +bool iso14443_4b_set_uid(Iso14443_4bData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_3bData* iso14443_4b_get_base_data(const Iso14443_4bData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_4b/iso14443_4b_device_defs.h b/lib/nfc/protocols/iso14443_4b/iso14443_4b_device_defs.h new file mode 100644 index 00000000000..8185c9aeebc --- /dev/null +++ b/lib/nfc/protocols/iso14443_4b/iso14443_4b_device_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcDeviceBase nfc_device_iso14443_4b; diff --git a/lib/nfc/protocols/iso14443_4b/iso14443_4b_i.c b/lib/nfc/protocols/iso14443_4b/iso14443_4b_i.c new file mode 100644 index 00000000000..1df8806408d --- /dev/null +++ b/lib/nfc/protocols/iso14443_4b/iso14443_4b_i.c @@ -0,0 +1,18 @@ +#include "iso14443_4b_i.h" + +Iso14443_4bError iso14443_4b_process_error(Iso14443_3bError error) { + switch(error) { + case Iso14443_3bErrorNone: + return Iso14443_4bErrorNone; + case Iso14443_3bErrorNotPresent: + return Iso14443_4bErrorNotPresent; + case Iso14443_3bErrorColResFailed: + case Iso14443_3bErrorCommunication: + case Iso14443_3bErrorWrongCrc: + return Iso14443_4bErrorProtocol; + case Iso14443_3bErrorTimeout: + return Iso14443_4bErrorTimeout; + default: + return Iso14443_4bErrorProtocol; + } +} diff --git a/lib/nfc/protocols/iso14443_4b/iso14443_4b_i.h b/lib/nfc/protocols/iso14443_4b/iso14443_4b_i.h new file mode 100644 index 00000000000..6090c03936e --- /dev/null +++ b/lib/nfc/protocols/iso14443_4b/iso14443_4b_i.h @@ -0,0 +1,9 @@ +#pragma once + +#include "iso14443_4b.h" + +struct Iso14443_4bData { + Iso14443_3bData* iso14443_3b_data; +}; + +Iso14443_4bError iso14443_4b_process_error(Iso14443_3bError error); diff --git a/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.c b/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.c new file mode 100644 index 00000000000..1030ebfb6f9 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.c @@ -0,0 +1,138 @@ +#include "iso14443_4b_poller_i.h" + +#include + +#include + +#define TAG "Iso14443_4bPoller" + +#define ISO14443_4A_POLLER_BUF_SIZE (256U) + +typedef NfcCommand (*Iso14443_4bPollerStateHandler)(Iso14443_4bPoller* instance); + +const Iso14443_4bData* iso14443_4b_poller_get_data(Iso14443_4bPoller* instance) { + furi_assert(instance); + + return instance->data; +} + +static Iso14443_4bPoller* iso14443_4b_poller_alloc(Iso14443_3bPoller* iso14443_3b_poller) { + Iso14443_4bPoller* instance = malloc(sizeof(Iso14443_4bPoller)); + instance->iso14443_3b_poller = iso14443_3b_poller; + instance->data = iso14443_4b_alloc(); + instance->iso14443_4_layer = iso14443_4_layer_alloc(); + instance->tx_buffer = bit_buffer_alloc(ISO14443_4A_POLLER_BUF_SIZE); + instance->rx_buffer = bit_buffer_alloc(ISO14443_4A_POLLER_BUF_SIZE); + + instance->iso14443_4b_event.data = &instance->iso14443_4b_event_data; + + instance->general_event.protocol = NfcProtocolIso14443_4b; + instance->general_event.event_data = &instance->iso14443_4b_event; + instance->general_event.instance = instance; + + return instance; +} + +static void iso14443_4b_poller_free(Iso14443_4bPoller* instance) { + furi_assert(instance); + + iso14443_4b_free(instance->data); + iso14443_4_layer_free(instance->iso14443_4_layer); + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + free(instance); +} + +static NfcCommand iso14443_4b_poller_handler_idle(Iso14443_4bPoller* instance) { + iso14443_3b_copy( + instance->data->iso14443_3b_data, + iso14443_3b_poller_get_data(instance->iso14443_3b_poller)); + + iso14443_4_layer_reset(instance->iso14443_4_layer); + instance->poller_state = Iso14443_4bPollerStateReady; + return NfcCommandContinue; +} + +static NfcCommand iso14443_4b_poller_handler_error(Iso14443_4bPoller* instance) { + iso14443_3b_poller_halt(instance->iso14443_3b_poller); + instance->iso14443_4b_event_data.error = instance->error; + NfcCommand command = instance->callback(instance->general_event, instance->context); + instance->poller_state = Iso14443_4bPollerStateIdle; + return command; +} + +static NfcCommand iso14443_4b_poller_handler_ready(Iso14443_4bPoller* instance) { + instance->iso14443_4b_event.type = Iso14443_4bPollerEventTypeReady; + NfcCommand command = instance->callback(instance->general_event, instance->context); + return command; +} + +static const Iso14443_4bPollerStateHandler + iso14443_4b_poller_state_handler[Iso14443_4bPollerStateNum] = { + [Iso14443_4bPollerStateIdle] = iso14443_4b_poller_handler_idle, + [Iso14443_4bPollerStateError] = iso14443_4b_poller_handler_error, + [Iso14443_4bPollerStateReady] = iso14443_4b_poller_handler_ready, +}; + +static void iso14443_4b_poller_set_callback( + Iso14443_4bPoller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand iso14443_4b_poller_run(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_3b); + + Iso14443_4bPoller* instance = context; + furi_assert(instance); + furi_assert(instance->callback); + + Iso14443_3bPollerEvent* iso14443_3b_event = event.event_data; + furi_assert(iso14443_3b_event); + + NfcCommand command = NfcCommandContinue; + + if(iso14443_3b_event->type == Iso14443_3bPollerEventTypeReady) { + command = iso14443_4b_poller_state_handler[instance->poller_state](instance); + } else if(iso14443_3b_event->type == Iso14443_3bPollerEventTypeError) { + instance->iso14443_4b_event.type = Iso14443_4bPollerEventTypeError; + command = instance->callback(instance->general_event, instance->context); + } + + return command; +} + +static bool iso14443_4b_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_3b); + + const Iso14443_4bPoller* instance = context; + furi_assert(instance); + + const Iso14443_3bPollerEvent* iso14443_3b_event = event.event_data; + furi_assert(iso14443_3b_event); + iso14443_3b_copy( + instance->data->iso14443_3b_data, + iso14443_3b_poller_get_data(instance->iso14443_3b_poller)); + + bool protocol_detected = false; + + if(iso14443_3b_event->type == Iso14443_3bPollerEventTypeReady) { + protocol_detected = iso14443_3b_supports_iso14443_4(instance->data->iso14443_3b_data); + } + + return protocol_detected; +} + +const NfcPollerBase nfc_poller_iso14443_4b = { + .alloc = (NfcPollerAlloc)iso14443_4b_poller_alloc, + .free = (NfcPollerFree)iso14443_4b_poller_free, + .set_callback = (NfcPollerSetCallback)iso14443_4b_poller_set_callback, + .run = (NfcPollerRun)iso14443_4b_poller_run, + .detect = (NfcPollerDetect)iso14443_4b_poller_detect, + .get_data = (NfcPollerGetData)iso14443_4b_poller_get_data, +}; diff --git a/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h b/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h new file mode 100644 index 00000000000..03b288c079c --- /dev/null +++ b/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h @@ -0,0 +1,73 @@ +#pragma once + +#include + +#include "iso14443_4b.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Iso14443_4bPoller opaque type definition. + */ +typedef struct Iso14443_4bPoller Iso14443_4bPoller; + +/** + * @brief Enumeration of possible Iso14443_4b poller event types. + */ +typedef enum { + Iso14443_4bPollerEventTypeError, /**< The card was activated by the poller. */ + Iso14443_4bPollerEventTypeReady, /**< An error occured during activation procedure. */ +} Iso14443_4bPollerEventType; + +/** + * @brief Iso14443_4b poller event data. + */ +typedef union { + Iso14443_4bError error; /**< Error code indicating card activation fail reason. */ +} Iso14443_4bPollerEventData; + +/** + * @brief Iso14443_4b poller event structure. + * + * Upon emission of an event, an instance of this struct will be passed to the callback. + */ +typedef struct { + Iso14443_4bPollerEventType type; /**< Type of emmitted event. */ + Iso14443_4bPollerEventData* data; /**< Pointer to event specific data. */ +} Iso14443_4bPollerEvent; + +/** + * @brief Transmit and receive Iso14443_4b blocks in poller mode. + * + * Must ONLY be used inside the callback function. + * + * The rx_buffer will be filled with any data received as a response to data + * sent from tx_buffer. The fwt parameter is calculated during activation procedure. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted. + * @param[out] rx_buffer pointer to the buffer to be filled with received data. + * @return Iso14443_4bErrorNone on success, an error code on failure. + */ +Iso14443_4bError iso14443_4b_poller_send_block( + Iso14443_4bPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer); + +/** + * @brief Send HALT command to the card. + * + * Must ONLY be used inside the callback function. + * + * Halts card and changes internal Iso14443_4aPoller state to Idle. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @return Iso14443_4bErrorNone on success, an error code on failure. + */ +Iso14443_4bError iso14443_4b_poller_halt(Iso14443_4bPoller* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller_defs.h b/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller_defs.h new file mode 100644 index 00000000000..4388f2cf8f3 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcPollerBase nfc_poller_iso14443_4b; diff --git a/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller_i.c b/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller_i.c new file mode 100644 index 00000000000..82de1d538a3 --- /dev/null +++ b/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller_i.c @@ -0,0 +1,45 @@ +#include "iso14443_4b_poller_i.h" + +#include + +#include "iso14443_4b_i.h" + +#define TAG "Iso14443_4bPoller" + +Iso14443_4bError iso14443_4b_poller_halt(Iso14443_4bPoller* instance) { + furi_assert(instance); + + iso14443_3b_poller_halt(instance->iso14443_3b_poller); + instance->poller_state = Iso14443_4bPollerStateIdle; + + return Iso14443_4bErrorNone; +} + +Iso14443_4bError iso14443_4b_poller_send_block( + Iso14443_4bPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer) { + furi_assert(instance); + + bit_buffer_reset(instance->tx_buffer); + iso14443_4_layer_encode_block(instance->iso14443_4_layer, tx_buffer, instance->tx_buffer); + + Iso14443_4bError error = Iso14443_4bErrorNone; + + do { + Iso14443_3bError iso14443_3b_error = iso14443_3b_poller_send_frame( + instance->iso14443_3b_poller, instance->tx_buffer, instance->rx_buffer); + + if(iso14443_3b_error != Iso14443_3bErrorNone) { + error = iso14443_4b_process_error(iso14443_3b_error); + break; + + } else if(!iso14443_4_layer_decode_block( + instance->iso14443_4_layer, rx_buffer, instance->rx_buffer)) { + error = Iso14443_4bErrorProtocol; + break; + } + } while(false); + + return error; +} diff --git a/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller_i.h b/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller_i.h new file mode 100644 index 00000000000..fc9c2632b2b --- /dev/null +++ b/lib/nfc/protocols/iso14443_4b/iso14443_4b_poller_i.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include + +#include "iso14443_4b_poller.h" +#include "iso14443_4b_i.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + Iso14443_4bPollerStateIdle, + Iso14443_4bPollerStateError, + Iso14443_4bPollerStateReady, + + Iso14443_4bPollerStateNum, +} Iso14443_4bPollerState; + +typedef enum { + Iso14443_4bPollerSessionStateIdle, + Iso14443_4bPollerSessionStateActive, + Iso14443_4bPollerSessionStateStopRequest, +} Iso14443_4bPollerSessionState; + +struct Iso14443_4bPoller { + Iso14443_3bPoller* iso14443_3b_poller; + Iso14443_4bPollerState poller_state; + Iso14443_4bPollerSessionState session_state; + Iso14443_4bError error; + Iso14443_4bData* data; + Iso14443_4Layer* iso14443_4_layer; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + Iso14443_4bPollerEventData iso14443_4b_event_data; + Iso14443_4bPollerEvent iso14443_4b_event; + NfcGenericEvent general_event; + NfcGenericCallback callback; + void* context; +}; + +const Iso14443_4bData* iso14443_4b_poller_get_data(Iso14443_4bPoller* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3.c b/lib/nfc/protocols/iso15693_3/iso15693_3.c new file mode 100644 index 00000000000..3203cbad000 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3.c @@ -0,0 +1,358 @@ +#include "iso15693_3.h" +#include "iso15693_3_device_defs.h" + +#include + +#define ISO15693_3_PROTOCOL_NAME "ISO15693-3" +#define ISO15693_3_PROTOCOL_NAME_LEGACY "ISO15693" +#define ISO15693_3_DEVICE_NAME "ISO15693-3 (Unknown)" + +#define ISO15693_3_LOCK_DSFID_LEGACY (1U << 0) +#define ISO15693_3_LOCK_AFI_LEGACY (1U << 1) + +#define ISO15693_3_DSFID_KEY "DSFID" +#define ISO15693_3_AFI_KEY "AFI" +#define ISO15693_3_IC_REF_KEY "IC Reference" +#define ISO15693_3_BLOCK_COUNT_KEY "Block Count" +#define ISO15693_3_BLOCK_SIZE_KEY "Block Size" +#define ISO15693_3_DATA_CONTENT_KEY "Data Content" +#define ISO15693_3_LOCK_DSFID_KEY "Lock DSFID" +#define ISO15693_3_LOCK_AFI_KEY "Lock AFI" +#define ISO15693_3_SECURITY_STATUS_KEY "Security Status" + +const NfcDeviceBase nfc_device_iso15693_3 = { + .protocol_name = ISO15693_3_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)iso15693_3_alloc, + .free = (NfcDeviceFree)iso15693_3_free, + .reset = (NfcDeviceReset)iso15693_3_reset, + .copy = (NfcDeviceCopy)iso15693_3_copy, + .verify = (NfcDeviceVerify)iso15693_3_verify, + .load = (NfcDeviceLoad)iso15693_3_load, + .save = (NfcDeviceSave)iso15693_3_save, + .is_equal = (NfcDeviceEqual)iso15693_3_is_equal, + .get_name = (NfcDeviceGetName)iso15693_3_get_device_name, + .get_uid = (NfcDeviceGetUid)iso15693_3_get_uid, + .set_uid = (NfcDeviceSetUid)iso15693_3_set_uid, + .get_base_data = (NfcDeviceGetBaseData)iso15693_3_get_base_data, +}; + +Iso15693_3Data* iso15693_3_alloc() { + Iso15693_3Data* data = malloc(sizeof(Iso15693_3Data)); + + data->block_data = simple_array_alloc(&simple_array_config_uint8_t); + data->block_security = simple_array_alloc(&simple_array_config_uint8_t); + + return data; +} + +void iso15693_3_free(Iso15693_3Data* data) { + furi_assert(data); + + simple_array_free(data->block_data); + simple_array_free(data->block_security); + free(data); +} + +void iso15693_3_reset(Iso15693_3Data* data) { + furi_assert(data); + + memset(data->uid, 0, ISO15693_3_UID_SIZE); + memset(&data->system_info, 0, sizeof(Iso15693_3SystemInfo)); + memset(&data->settings, 0, sizeof(Iso15693_3Settings)); + + simple_array_reset(data->block_data); + simple_array_reset(data->block_security); +} + +void iso15693_3_copy(Iso15693_3Data* data, const Iso15693_3Data* other) { + furi_assert(data); + furi_assert(other); + + memcpy(data->uid, other->uid, ISO15693_3_UID_SIZE); + + data->system_info = other->system_info; + data->settings = other->settings; + + simple_array_copy(data->block_data, other->block_data); + simple_array_copy(data->block_security, other->block_security); +} + +bool iso15693_3_verify(Iso15693_3Data* data, const FuriString* device_type) { + UNUSED(data); + return furi_string_equal(device_type, ISO15693_3_PROTOCOL_NAME_LEGACY); +} + +static inline bool iso15693_3_load_security_legacy(Iso15693_3Data* data, FlipperFormat* ff) { + bool loaded = false; + uint8_t* legacy_data = NULL; + + do { + uint32_t value_count; + if(!flipper_format_get_value_count(ff, ISO15693_3_SECURITY_STATUS_KEY, &value_count)) + break; + if(simple_array_get_count(data->block_security) + 1 != value_count) break; + + legacy_data = malloc(value_count); + if(!flipper_format_read_hex(ff, ISO15693_3_SECURITY_STATUS_KEY, legacy_data, value_count)) + break; + + // First legacy data byte is lock bits + data->settings.lock_bits.dsfid = legacy_data[0] & ISO15693_3_LOCK_DSFID_LEGACY; + data->settings.lock_bits.afi = legacy_data[0] & ISO15693_3_LOCK_AFI_LEGACY; + + // The rest are block security + memcpy( + &legacy_data[1], + simple_array_get_data(data->block_security), + simple_array_get_count(data->block_security)); + + loaded = true; + } while(false); + + if(legacy_data) free(legacy_data); + + return loaded; +} + +static inline bool iso15693_3_load_security(Iso15693_3Data* data, FlipperFormat* ff) { + bool loaded = false; + + do { + uint32_t value_count; + if(!flipper_format_get_value_count(ff, ISO15693_3_SECURITY_STATUS_KEY, &value_count)) + break; + if(simple_array_get_count(data->block_security) != value_count) break; + if(!flipper_format_read_hex( + ff, + ISO15693_3_SECURITY_STATUS_KEY, + simple_array_get_data(data->block_security), + simple_array_get_count(data->block_security))) + break; + + loaded = true; + } while(false); + + return loaded; +} + +bool iso15693_3_load(Iso15693_3Data* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + UNUSED(version); + + bool loaded = false; + + do { + if(flipper_format_key_exist(ff, ISO15693_3_DSFID_KEY)) { + if(!flipper_format_read_hex(ff, ISO15693_3_DSFID_KEY, &data->system_info.dsfid, 1)) + break; + data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_DSFID; + } + + if(flipper_format_key_exist(ff, ISO15693_3_AFI_KEY)) { + if(!flipper_format_read_hex(ff, ISO15693_3_AFI_KEY, &data->system_info.afi, 1)) break; + data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_AFI; + } + + if(flipper_format_key_exist(ff, ISO15693_3_IC_REF_KEY)) { + if(!flipper_format_read_hex(ff, ISO15693_3_IC_REF_KEY, &data->system_info.ic_ref, 1)) + break; + data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_IC_REF; + } + + const bool has_lock_bits = flipper_format_key_exist(ff, ISO15693_3_LOCK_DSFID_KEY) && + flipper_format_key_exist(ff, ISO15693_3_LOCK_AFI_KEY); + if(has_lock_bits) { + Iso15693_3LockBits* lock_bits = &data->settings.lock_bits; + if(!flipper_format_read_bool(ff, ISO15693_3_LOCK_DSFID_KEY, &lock_bits->dsfid, 1)) + break; + if(!flipper_format_read_bool(ff, ISO15693_3_LOCK_AFI_KEY, &lock_bits->afi, 1)) break; + } + + if(flipper_format_key_exist(ff, ISO15693_3_BLOCK_COUNT_KEY) && + flipper_format_key_exist(ff, ISO15693_3_BLOCK_SIZE_KEY)) { + uint32_t block_count; + if(!flipper_format_read_uint32(ff, ISO15693_3_BLOCK_COUNT_KEY, &block_count, 1)) break; + + data->system_info.block_count = block_count; + data->system_info.flags |= ISO15693_3_SYSINFO_FLAG_MEMORY; + + if(!flipper_format_read_hex( + ff, ISO15693_3_BLOCK_SIZE_KEY, &(data->system_info.block_size), 1)) + break; + + simple_array_init( + data->block_data, data->system_info.block_size * data->system_info.block_count); + + if(!flipper_format_read_hex( + ff, + ISO15693_3_DATA_CONTENT_KEY, + simple_array_get_data(data->block_data), + simple_array_get_count(data->block_data))) + break; + + if(flipper_format_key_exist(ff, ISO15693_3_SECURITY_STATUS_KEY)) { + simple_array_init(data->block_security, data->system_info.block_count); + + const bool security_loaded = has_lock_bits ? + iso15693_3_load_security(data, ff) : + iso15693_3_load_security_legacy(data, ff); + if(!security_loaded) break; + } + } + + loaded = true; + } while(false); + + return loaded; +} + +bool iso15693_3_save(const Iso15693_3Data* data, FlipperFormat* ff) { + furi_assert(data); + + bool saved = false; + + do { + if(!flipper_format_write_comment_cstr(ff, ISO15693_3_PROTOCOL_NAME " specific data")) + break; + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_DSFID) { + if(!flipper_format_write_comment_cstr(ff, "Data Storage Format Identifier")) break; + if(!flipper_format_write_hex(ff, ISO15693_3_DSFID_KEY, &data->system_info.dsfid, 1)) + break; + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_AFI) { + if(!flipper_format_write_comment_cstr(ff, "Application Family Identifier")) break; + if(!flipper_format_write_hex(ff, ISO15693_3_AFI_KEY, &data->system_info.afi, 1)) break; + } + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_IC_REF) { + if(!flipper_format_write_comment_cstr(ff, "IC Reference - Vendor specific meaning")) + break; + if(!flipper_format_write_hex(ff, ISO15693_3_IC_REF_KEY, &data->system_info.ic_ref, 1)) + break; + } + + if(!flipper_format_write_comment_cstr(ff, "Lock Bits")) break; + if(!flipper_format_write_bool( + ff, ISO15693_3_LOCK_DSFID_KEY, &data->settings.lock_bits.dsfid, 1)) + break; + if(!flipper_format_write_bool( + ff, ISO15693_3_LOCK_AFI_KEY, &data->settings.lock_bits.afi, 1)) + break; + + if(data->system_info.flags & ISO15693_3_SYSINFO_FLAG_MEMORY) { + const uint32_t block_count = data->system_info.block_count; + if(!flipper_format_write_comment_cstr( + ff, "Number of memory blocks, valid range = 1..256")) + break; + if(!flipper_format_write_uint32(ff, ISO15693_3_BLOCK_COUNT_KEY, &block_count, 1)) + break; + + if(!flipper_format_write_comment_cstr( + ff, "Size of a single memory block, valid range = 01...20 (hex)")) + break; + if(!flipper_format_write_hex( + ff, ISO15693_3_BLOCK_SIZE_KEY, &data->system_info.block_size, 1)) + break; + + if(!flipper_format_write_hex( + ff, + ISO15693_3_DATA_CONTENT_KEY, + simple_array_cget_data(data->block_data), + simple_array_get_count(data->block_data))) + break; + + if(!flipper_format_write_comment_cstr( + ff, "Block Security Status: 01 = locked, 00 = not locked")) + break; + if(!flipper_format_write_hex( + ff, + ISO15693_3_SECURITY_STATUS_KEY, + simple_array_cget_data(data->block_security), + simple_array_get_count(data->block_security))) + break; + } + saved = true; + } while(false); + + return saved; +} + +bool iso15693_3_is_equal(const Iso15693_3Data* data, const Iso15693_3Data* other) { + furi_assert(data); + furi_assert(other); + + return memcmp(data->uid, other->uid, ISO15693_3_UID_SIZE) == 0 && + memcmp(&data->settings, &other->settings, sizeof(Iso15693_3Settings)) == 0 && + memcmp(&data->system_info, &other->system_info, sizeof(Iso15693_3SystemInfo)) == 0 && + simple_array_is_equal(data->block_data, other->block_data) && + simple_array_is_equal(data->block_security, other->block_security); +} + +const char* iso15693_3_get_device_name(const Iso15693_3Data* data, NfcDeviceNameType name_type) { + UNUSED(data); + UNUSED(name_type); + + return ISO15693_3_DEVICE_NAME; +} + +const uint8_t* iso15693_3_get_uid(const Iso15693_3Data* data, size_t* uid_len) { + furi_assert(data); + + if(uid_len) *uid_len = ISO15693_3_UID_SIZE; + return data->uid; +} + +bool iso15693_3_set_uid(Iso15693_3Data* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + furi_assert(uid); + + bool uid_valid = uid_len == ISO15693_3_UID_SIZE; + + if(uid_valid) { + memcpy(data->uid, uid, uid_len); + // All ISO15693-3 cards must have this as first UID byte + data->uid[0] = 0xe0; + } + + return uid_valid; +} + +Iso15693_3Data* iso15693_3_get_base_data(const Iso15693_3Data* data) { + UNUSED(data); + furi_crash("No base data"); +} + +bool iso15693_3_is_block_locked(const Iso15693_3Data* data, uint8_t block_index) { + furi_assert(data); + furi_assert(block_index < data->system_info.block_count); + + return *(const uint8_t*)simple_array_cget(data->block_security, block_index); +} + +uint8_t iso15693_3_get_manufacturer_id(const Iso15693_3Data* data) { + furi_assert(data); + + return data->uid[1]; +} + +uint16_t iso15693_3_get_block_count(const Iso15693_3Data* data) { + furi_assert(data); + + return data->system_info.block_count; +} + +uint8_t iso15693_3_get_block_size(const Iso15693_3Data* data) { + furi_assert(data); + + return data->system_info.block_size; +} + +const uint8_t* iso15693_3_get_block_data(const Iso15693_3Data* data, uint8_t block_index) { + furi_assert(data); + furi_assert(data->system_info.block_count > block_index); + + return (const uint8_t*)simple_array_cget( + data->block_data, block_index * data->system_info.block_size); +} diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3.h b/lib/nfc/protocols/iso15693_3/iso15693_3.h new file mode 100644 index 00000000000..5d4158ab9e3 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3.h @@ -0,0 +1,163 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISO15693_3_UID_SIZE (8U) + +#define ISO15693_3_GUARD_TIME_US (5000U) +#define ISO15693_3_FDT_POLL_FC (4202U) +#define ISO15693_3_FDT_LISTEN_FC (4320U) +#define ISO15693_3_POLL_POLL_MIN_US (1500U) + +#define ISO15693_3_REQ_FLAG_SUBCARRIER_1 (0U << 0) +#define ISO15693_3_REQ_FLAG_SUBCARRIER_2 (1U << 0) +#define ISO15693_3_REQ_FLAG_DATA_RATE_LO (0U << 1) +#define ISO15693_3_REQ_FLAG_DATA_RATE_HI (1U << 1) +#define ISO15693_3_REQ_FLAG_INVENTORY_T4 (0U << 2) +#define ISO15693_3_REQ_FLAG_INVENTORY_T5 (1U << 2) +#define ISO15693_3_REQ_FLAG_EXTENSION (1U << 3) + +#define ISO15693_3_REQ_FLAG_T4_SELECTED (1U << 4) +#define ISO15693_3_REQ_FLAG_T4_ADDRESSED (1U << 5) +#define ISO15693_3_REQ_FLAG_T4_OPTION (1U << 6) + +#define ISO15693_3_REQ_FLAG_T5_AFI_PRESENT (1U << 4) +#define ISO15693_3_REQ_FLAG_T5_N_SLOTS_16 (0U << 5) +#define ISO15693_3_REQ_FLAG_T5_N_SLOTS_1 (1U << 5) +#define ISO15693_3_REQ_FLAG_T5_OPTION (1U << 6) + +#define ISO15693_3_RESP_FLAG_NONE (0U) +#define ISO15693_3_RESP_FLAG_ERROR (1U << 0) +#define ISO15693_3_RESP_FLAG_EXTENSION (1U << 3) + +#define ISO15693_3_RESP_ERROR_NOT_SUPPORTED (0x01U) +#define ISO15693_3_RESP_ERROR_FORMAT (0x02U) +#define ISO15693_3_RESP_ERROR_OPTION (0x03U) +#define ISO15693_3_RESP_ERROR_UNKNOWN (0x0FU) +#define ISO15693_3_RESP_ERROR_BLOCK_UNAVAILABLE (0x10U) +#define ISO15693_3_RESP_ERROR_BLOCK_ALREADY_LOCKED (0x11U) +#define ISO15693_3_RESP_ERROR_BLOCK_LOCKED (0x12U) +#define ISO15693_3_RESP_ERROR_BLOCK_WRITE (0x13U) +#define ISO15693_3_RESP_ERROR_BLOCK_LOCK (0x14U) +#define ISO15693_3_RESP_ERROR_CUSTOM_START (0xA0U) +#define ISO15693_3_RESP_ERROR_CUSTOM_END (0xDFU) + +#define ISO15693_3_CMD_MANDATORY_START (0x01U) +#define ISO15693_3_CMD_INVENTORY (0x01U) +#define ISO15693_3_CMD_STAY_QUIET (0x02U) +#define ISO15693_3_CMD_MANDATORY_RFU (0x03U) +#define ISO15693_3_CMD_OPTIONAL_START (0x20U) +#define ISO15693_3_CMD_READ_BLOCK (0x20U) +#define ISO15693_3_CMD_WRITE_BLOCK (0x21U) +#define ISO15693_3_CMD_LOCK_BLOCK (0x22U) +#define ISO15693_3_CMD_READ_MULTI_BLOCKS (0x23U) +#define ISO15693_3_CMD_WRITE_MULTI_BLOCKS (0x24U) +#define ISO15693_3_CMD_SELECT (0x25U) +#define ISO15693_3_CMD_RESET_TO_READY (0x26U) +#define ISO15693_3_CMD_WRITE_AFI (0x27U) +#define ISO15693_3_CMD_LOCK_AFI (0x28U) +#define ISO15693_3_CMD_WRITE_DSFID (0x29U) +#define ISO15693_3_CMD_LOCK_DSFID (0x2AU) +#define ISO15693_3_CMD_GET_SYS_INFO (0x2BU) +#define ISO15693_3_CMD_GET_BLOCKS_SECURITY (0x2CU) +#define ISO15693_3_CMD_OPTIONAL_RFU (0x2DU) +#define ISO15693_3_CMD_CUSTOM_START (0xA0U) + +#define ISO15693_3_MANDATORY_COUNT (ISO15693_3_CMD_MANDATORY_RFU - ISO15693_3_CMD_MANDATORY_START) +#define ISO15693_3_OPTIONAL_COUNT (ISO15693_3_CMD_OPTIONAL_RFU - ISO15693_3_CMD_OPTIONAL_START) + +#define ISO15693_3_SYSINFO_FLAG_DSFID (1U << 0) +#define ISO15693_3_SYSINFO_FLAG_AFI (1U << 1) +#define ISO15693_3_SYSINFO_FLAG_MEMORY (1U << 2) +#define ISO15693_3_SYSINFO_FLAG_IC_REF (1U << 3) + +typedef enum { + Iso15693_3ErrorNone, + Iso15693_3ErrorNotPresent, + Iso15693_3ErrorBufferEmpty, + Iso15693_3ErrorBufferOverflow, + Iso15693_3ErrorFieldOff, + Iso15693_3ErrorWrongCrc, + Iso15693_3ErrorTimeout, + Iso15693_3ErrorFormat, + Iso15693_3ErrorIgnore, + Iso15693_3ErrorNotSupported, + Iso15693_3ErrorUidMismatch, + Iso15693_3ErrorFullyHandled, + Iso15693_3ErrorUnexpectedResponse, + Iso15693_3ErrorInternal, + Iso15693_3ErrorCustom, + Iso15693_3ErrorUnknown, +} Iso15693_3Error; + +typedef struct { + uint8_t flags; + uint8_t dsfid; + uint8_t afi; + uint8_t ic_ref; + uint16_t block_count; + uint8_t block_size; +} Iso15693_3SystemInfo; + +typedef struct { + bool dsfid; + bool afi; +} Iso15693_3LockBits; + +typedef struct { + Iso15693_3LockBits lock_bits; +} Iso15693_3Settings; + +typedef struct { + uint8_t uid[ISO15693_3_UID_SIZE]; + Iso15693_3SystemInfo system_info; + Iso15693_3Settings settings; + SimpleArray* block_data; + SimpleArray* block_security; +} Iso15693_3Data; + +Iso15693_3Data* iso15693_3_alloc(); + +void iso15693_3_free(Iso15693_3Data* data); + +void iso15693_3_reset(Iso15693_3Data* data); + +void iso15693_3_copy(Iso15693_3Data* data, const Iso15693_3Data* other); + +bool iso15693_3_verify(Iso15693_3Data* data, const FuriString* device_type); + +bool iso15693_3_load(Iso15693_3Data* data, FlipperFormat* ff, uint32_t version); + +bool iso15693_3_save(const Iso15693_3Data* data, FlipperFormat* ff); + +bool iso15693_3_is_equal(const Iso15693_3Data* data, const Iso15693_3Data* other); + +const char* iso15693_3_get_device_name(const Iso15693_3Data* data, NfcDeviceNameType name_type); + +const uint8_t* iso15693_3_get_uid(const Iso15693_3Data* data, size_t* uid_len); + +bool iso15693_3_set_uid(Iso15693_3Data* data, const uint8_t* uid, size_t uid_len); + +Iso15693_3Data* iso15693_3_get_base_data(const Iso15693_3Data* data); + +// Getters and tests + +bool iso15693_3_is_block_locked(const Iso15693_3Data* data, uint8_t block_index); + +uint8_t iso15693_3_get_manufacturer_id(const Iso15693_3Data* data); + +uint16_t iso15693_3_get_block_count(const Iso15693_3Data* data); + +uint8_t iso15693_3_get_block_size(const Iso15693_3Data* data); + +const uint8_t* iso15693_3_get_block_data(const Iso15693_3Data* data, uint8_t block_index); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_device_defs.h b/lib/nfc/protocols/iso15693_3/iso15693_3_device_defs.h new file mode 100644 index 00000000000..69a73476e61 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_device_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcDeviceBase nfc_device_iso15693_3; diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_i.c b/lib/nfc/protocols/iso15693_3/iso15693_3_i.c new file mode 100644 index 00000000000..3d8d95c3a1d --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_i.c @@ -0,0 +1,263 @@ +#include "iso15693_3_i.h" + +bool iso15693_3_error_response_parse(Iso15693_3Error* error, const BitBuffer* buf) { + furi_assert(error); + + if(bit_buffer_get_size_bytes(buf) == 0) { + // YEET! + *error = Iso15693_3ErrorBufferEmpty; + return true; + } + + typedef struct { + uint8_t flags; + uint8_t error; + } ErrorResponseLayout; + + const ErrorResponseLayout* resp = (const ErrorResponseLayout*)bit_buffer_get_data(buf); + + if((resp->flags & ISO15693_3_RESP_FLAG_ERROR) == 0) { + // No error flag is set, the data does not contain an error frame + return false; + } else if(bit_buffer_get_size_bytes(buf) < sizeof(ErrorResponseLayout)) { + // Error bit is set, but not enough data to determine the error + *error = Iso15693_3ErrorUnexpectedResponse; + return true; + } else if( + resp->error >= ISO15693_3_RESP_ERROR_CUSTOM_START && + resp->error <= ISO15693_3_RESP_ERROR_CUSTOM_END) { + // Custom vendor-specific error, must be checked in the respective protocol implementation + *error = Iso15693_3ErrorCustom; + return true; + } + + switch(resp->error) { + case ISO15693_3_RESP_ERROR_NOT_SUPPORTED: + case ISO15693_3_RESP_ERROR_OPTION: + *error = Iso15693_3ErrorNotSupported; + break; + case ISO15693_3_RESP_ERROR_FORMAT: + *error = Iso15693_3ErrorFormat; + break; + case ISO15693_3_RESP_ERROR_BLOCK_UNAVAILABLE: + case ISO15693_3_RESP_ERROR_BLOCK_ALREADY_LOCKED: + case ISO15693_3_RESP_ERROR_BLOCK_LOCKED: + case ISO15693_3_RESP_ERROR_BLOCK_WRITE: + case ISO15693_3_RESP_ERROR_BLOCK_LOCK: + *error = Iso15693_3ErrorInternal; + break; + case ISO15693_3_RESP_ERROR_UNKNOWN: + default: + *error = Iso15693_3ErrorUnknown; + } + + return true; +} + +Iso15693_3Error iso15693_3_inventory_response_parse(uint8_t* data, const BitBuffer* buf) { + furi_assert(data); + + Iso15693_3Error ret = Iso15693_3ErrorNone; + + do { + if(iso15693_3_error_response_parse(&ret, buf)) break; + + typedef struct { + uint8_t flags; + uint8_t dsfid; + uint8_t uid[ISO15693_3_UID_SIZE]; + } InventoryResponseLayout; + + if(bit_buffer_get_size_bytes(buf) != sizeof(InventoryResponseLayout)) { + ret = Iso15693_3ErrorUnexpectedResponse; + break; + } + + const InventoryResponseLayout* resp = + (const InventoryResponseLayout*)bit_buffer_get_data(buf); + // Reverse UID for backward compatibility + for(uint32_t i = 0; i < ISO15693_3_UID_SIZE; ++i) { + data[i] = resp->uid[ISO15693_3_UID_SIZE - i - 1]; + } + + } while(false); + + return ret; +} + +Iso15693_3Error + iso15693_3_system_info_response_parse(Iso15693_3SystemInfo* data, const BitBuffer* buf) { + furi_assert(data); + + Iso15693_3Error ret = Iso15693_3ErrorNone; + + do { + if(iso15693_3_error_response_parse(&ret, buf)) break; + + typedef struct { + uint8_t flags; + uint8_t info_flags; + uint8_t uid[ISO15693_3_UID_SIZE]; + uint8_t extra[]; + } SystemInfoResponseLayout; + + if(bit_buffer_get_size_bytes(buf) < sizeof(SystemInfoResponseLayout)) { + ret = Iso15693_3ErrorUnexpectedResponse; + break; + } + + const SystemInfoResponseLayout* resp = + (const SystemInfoResponseLayout*)bit_buffer_get_data(buf); + + const uint8_t* extra = resp->extra; + const size_t extra_size = (resp->info_flags & ISO15693_3_SYSINFO_FLAG_DSFID ? 1 : 0) + + (resp->info_flags & ISO15693_3_SYSINFO_FLAG_AFI ? 1 : 0) + + (resp->info_flags & ISO15693_3_SYSINFO_FLAG_MEMORY ? 2 : 0) + + (resp->info_flags & ISO15693_3_SYSINFO_FLAG_IC_REF ? 1 : 0); + + if(extra_size != bit_buffer_get_size_bytes(buf) - sizeof(SystemInfoResponseLayout)) { + ret = Iso15693_3ErrorUnexpectedResponse; + break; + } + + data->flags = resp->info_flags; + + if(data->flags & ISO15693_3_SYSINFO_FLAG_DSFID) { + data->dsfid = *extra++; + } + + if(data->flags & ISO15693_3_SYSINFO_FLAG_AFI) { + data->afi = *extra++; + } + + if(data->flags & ISO15693_3_SYSINFO_FLAG_MEMORY) { + // Add 1 to get actual values + data->block_count = *extra++ + 1; + data->block_size = (*extra++ & 0x1F) + 1; + } + + if(data->flags & ISO15693_3_SYSINFO_FLAG_IC_REF) { + data->ic_ref = *extra; + } + + } while(false); + + return ret; +} + +Iso15693_3Error + iso15693_3_read_block_response_parse(uint8_t* data, uint8_t block_size, const BitBuffer* buf) { + furi_assert(data); + + Iso15693_3Error ret = Iso15693_3ErrorNone; + + do { + if(iso15693_3_error_response_parse(&ret, buf)) break; + + typedef struct { + uint8_t flags; + uint8_t block_data[]; + } ReadBlockResponseLayout; + + const size_t buf_size = bit_buffer_get_size_bytes(buf); + const size_t received_block_size = buf_size - sizeof(ReadBlockResponseLayout); + + if(buf_size <= sizeof(ReadBlockResponseLayout) || received_block_size != block_size) { + ret = Iso15693_3ErrorUnexpectedResponse; + break; + } + + const ReadBlockResponseLayout* resp = + (const ReadBlockResponseLayout*)bit_buffer_get_data(buf); + memcpy(data, resp->block_data, received_block_size); + + } while(false); + + return ret; +} + +Iso15693_3Error iso15693_3_get_block_security_response_parse( + uint8_t* data, + uint16_t block_count, + const BitBuffer* buf) { + furi_assert(data); + furi_assert(block_count); + Iso15693_3Error ret = Iso15693_3ErrorNone; + + do { + if(iso15693_3_error_response_parse(&ret, buf)) break; + + typedef struct { + uint8_t flags; + uint8_t block_security[]; + } GetBlockSecurityResponseLayout; + + const size_t buf_size = bit_buffer_get_size_bytes(buf); + const size_t received_block_count = buf_size - sizeof(GetBlockSecurityResponseLayout); + + if(buf_size <= sizeof(GetBlockSecurityResponseLayout) || + received_block_count != block_count) { + ret = Iso15693_3ErrorUnexpectedResponse; + break; + } + + const GetBlockSecurityResponseLayout* resp = + (const GetBlockSecurityResponseLayout*)bit_buffer_get_data(buf); + + memcpy(data, resp->block_security, received_block_count); + + } while(false); + + return ret; +} + +void iso15693_3_append_uid(const Iso15693_3Data* data, BitBuffer* buf) { + for(size_t i = 0; i < ISO15693_3_UID_SIZE; ++i) { + // Reverse the UID + bit_buffer_append_byte(buf, data->uid[ISO15693_3_UID_SIZE - i - 1]); + } +} + +void iso15693_3_append_block(const Iso15693_3Data* data, uint8_t block_num, BitBuffer* buf) { + furi_assert(block_num < data->system_info.block_count); + + const uint32_t block_offset = block_num * data->system_info.block_size; + const uint8_t* block_data = simple_array_cget(data->block_data, block_offset); + + bit_buffer_append_bytes(buf, block_data, data->system_info.block_size); +} + +void iso15693_3_set_block_locked(Iso15693_3Data* data, uint8_t block_index, bool locked) { + furi_assert(data); + furi_assert(block_index < data->system_info.block_count); + + *(uint8_t*)simple_array_get(data->block_security, block_index) = locked ? 1 : 0; +} + +void iso15693_3_set_block_data( + Iso15693_3Data* data, + uint8_t block_num, + const uint8_t* block_data, + size_t block_data_size) { + furi_assert(block_num < data->system_info.block_count); + furi_assert(block_data_size == data->system_info.block_size); + + const uint32_t block_offset = block_num * data->system_info.block_size; + uint8_t* block = simple_array_get(data->block_data, block_offset); + + memcpy(block, block_data, block_data_size); +} + +void iso15693_3_append_block_security( + const Iso15693_3Data* data, + uint8_t block_num, + BitBuffer* buf) { + bit_buffer_append_byte(buf, *(uint8_t*)simple_array_cget(data->block_security, block_num)); +} + +bool iso15693_3_is_equal_uid(const Iso15693_3Data* data, const uint8_t* uid) { + for(size_t i = 0; i < ISO15693_3_UID_SIZE; ++i) { + if(data->uid[i] != uid[ISO15693_3_UID_SIZE - i - 1]) return false; + } + return true; +} diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_i.h b/lib/nfc/protocols/iso15693_3/iso15693_3_i.h new file mode 100644 index 00000000000..253bda7f598 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_i.h @@ -0,0 +1,58 @@ +#pragma once + +#include "iso15693_3.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Check if the buffer contains an error frame and if it does, determine + * the error type. + * NOTE: No changes are done to the result if no error is present. + * + * @param [out] data Pointer to the resulting error value. + * @param [in] buf Data buffer to be checked + * + * @return True if data contains an error frame or is empty, false otherwise + */ +bool iso15693_3_error_response_parse(Iso15693_3Error* error, const BitBuffer* buf); + +Iso15693_3Error iso15693_3_inventory_response_parse(uint8_t* data, const BitBuffer* buf); + +Iso15693_3Error + iso15693_3_system_info_response_parse(Iso15693_3SystemInfo* data, const BitBuffer* buf); + +Iso15693_3Error + iso15693_3_read_block_response_parse(uint8_t* data, uint8_t block_size, const BitBuffer* buf); + +Iso15693_3Error iso15693_3_get_block_security_response_parse( + uint8_t* data, + uint16_t block_count, + const BitBuffer* buf); + +void iso15693_3_append_uid(const Iso15693_3Data* data, BitBuffer* buf); + +void iso15693_3_append_block(const Iso15693_3Data* data, uint8_t block_num, BitBuffer* buf); + +void iso15693_3_set_block_locked(Iso15693_3Data* data, uint8_t block_index, bool locked); + +void iso15693_3_set_block_data( + Iso15693_3Data* data, + uint8_t block_num, + const uint8_t* block_data, + size_t block_data_size); + +void iso15693_3_append_block_security( + const Iso15693_3Data* data, + uint8_t block_num, + BitBuffer* buf); + +// NOTE: the uid parameter has reversed byte order with respect to data +bool iso15693_3_is_equal_uid(const Iso15693_3Data* data, const uint8_t* uid); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_listener.c b/lib/nfc/protocols/iso15693_3/iso15693_3_listener.c new file mode 100644 index 00000000000..84e75085855 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_listener.c @@ -0,0 +1,110 @@ +#include "iso15693_3_listener_i.h" + +#include + +#include +#include +#include + +#define TAG "Iso15693_3Listener" + +#define ISO15693_3_LISTENER_BUFFER_SIZE (64U) + +Iso15693_3Listener* iso15693_3_listener_alloc(Nfc* nfc, Iso15693_3Data* data) { + furi_assert(nfc); + + Iso15693_3Listener* instance = malloc(sizeof(Iso15693_3Listener)); + instance->nfc = nfc; + instance->data = data; + + instance->tx_buffer = bit_buffer_alloc(ISO15693_3_LISTENER_BUFFER_SIZE); + + instance->iso15693_3_event.data = &instance->iso15693_3_event_data; + instance->generic_event.protocol = NfcProtocolIso15693_3; + instance->generic_event.instance = instance; + instance->generic_event.event_data = &instance->iso15693_3_event; + + nfc_set_fdt_listen_fc(instance->nfc, ISO15693_3_FDT_LISTEN_FC); + nfc_config(instance->nfc, NfcModeListener, NfcTechIso15693); + + return instance; +} + +void iso15693_3_listener_free(Iso15693_3Listener* instance) { + furi_assert(instance); + + bit_buffer_free(instance->tx_buffer); + + free(instance); +} + +void iso15693_3_listener_set_callback( + Iso15693_3Listener* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + + instance->callback = callback; + instance->context = context; +} + +const Iso15693_3Data* iso15693_3_listener_get_data(Iso15693_3Listener* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +NfcCommand iso15693_3_listener_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolInvalid); + furi_assert(event.event_data); + + Iso15693_3Listener* instance = context; + NfcEvent* nfc_event = event.event_data; + NfcCommand command = NfcCommandContinue; + + if(nfc_event->type == NfcEventTypeRxEnd) { + BitBuffer* rx_buffer = nfc_event->data.buffer; + + if(iso13239_crc_check(Iso13239CrcTypeDefault, rx_buffer)) { + iso13239_crc_trim(rx_buffer); + + const Iso15693_3Error error = iso15693_3_listener_process_request(instance, rx_buffer); + + if(error == Iso15693_3ErrorNotSupported) { + if(instance->callback) { + instance->iso15693_3_event.type = Iso15693_3ListenerEventTypeCustomCommand; + instance->iso15693_3_event.data->buffer = rx_buffer; + command = instance->callback(instance->generic_event, instance->context); + } + + } else if(error == Iso15693_3ErrorUidMismatch) { + iso15693_3_listener_process_uid_mismatch(instance, rx_buffer); + } + + } else if(bit_buffer_get_size(rx_buffer) == 0) { + // Special case: Single EOF + const Iso15693_3Error error = iso15693_3_listener_process_single_eof(instance); + if(error == Iso15693_3ErrorUnexpectedResponse) { + if(instance->callback) { + instance->iso15693_3_event.type = Iso15693_3ListenerEventTypeSingleEof; + command = instance->callback(instance->generic_event, instance->context); + } + } + } else { + FURI_LOG_D( + TAG, "Wrong CRC, buffer size: %zu", bit_buffer_get_size(nfc_event->data.buffer)); + } + } + + return command; +} + +const NfcListenerBase nfc_listener_iso15693_3 = { + .alloc = (NfcListenerAlloc)iso15693_3_listener_alloc, + .free = (NfcListenerFree)iso15693_3_listener_free, + .set_callback = (NfcListenerSetCallback)iso15693_3_listener_set_callback, + .get_data = (NfcListenerGetData)iso15693_3_listener_get_data, + .run = (NfcListenerRun)iso15693_3_listener_run, +}; diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_listener.h b/lib/nfc/protocols/iso15693_3/iso15693_3_listener.h new file mode 100644 index 00000000000..c69d18db4e7 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_listener.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include "iso15693_3.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Iso15693_3Listener Iso15693_3Listener; + +typedef enum { + Iso15693_3ListenerEventTypeFieldOff, + Iso15693_3ListenerEventTypeCustomCommand, + Iso15693_3ListenerEventTypeSingleEof, +} Iso15693_3ListenerEventType; + +typedef struct { + BitBuffer* buffer; +} Iso15693_3ListenerEventData; + +typedef struct { + Iso15693_3ListenerEventType type; + Iso15693_3ListenerEventData* data; +} Iso15693_3ListenerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_listener_defs.h b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_defs.h new file mode 100644 index 00000000000..93497512eb4 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcListenerBase nfc_listener_iso15693_3; diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.c b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.c new file mode 100644 index 00000000000..a8dec7ae33f --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.c @@ -0,0 +1,883 @@ +#include "iso15693_3_listener_i.h" + +#include + +#define TAG "Iso15693_3Listener" + +typedef Iso15693_3Error (*Iso15693_3RequestHandler)( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags); + +typedef struct { + Iso15693_3RequestHandler mandatory[ISO15693_3_MANDATORY_COUNT]; + Iso15693_3RequestHandler optional[ISO15693_3_OPTIONAL_COUNT]; +} Iso15693_3ListenerHandlerTable; + +static Iso15693_3Error + iso15693_3_listener_extension_handler(Iso15693_3Listener* instance, uint32_t command, ...) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + if(instance->extension_table == NULL) break; + + Iso15693_3ExtensionHandler handler = NULL; + + if(command < ISO15693_3_CMD_MANDATORY_RFU) { + const Iso15693_3ExtensionHandler* mandatory = instance->extension_table->mandatory; + handler = mandatory[command - ISO15693_3_CMD_MANDATORY_START]; + } else if(command >= ISO15693_3_CMD_OPTIONAL_START && command < ISO15693_3_CMD_OPTIONAL_RFU) { + const Iso15693_3ExtensionHandler* optional = instance->extension_table->optional; + handler = optional[command - ISO15693_3_CMD_OPTIONAL_START]; + } + + if(handler == NULL) break; + + va_list args; + va_start(args, command); + + error = handler(instance->extension_context, args); + + va_end(args); + + } while(false); + return error; +} + +static Iso15693_3Error iso15693_3_listener_inventory_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + const bool afi_flag = flags & ISO15693_3_REQ_FLAG_T5_AFI_PRESENT; + const size_t data_size_min = sizeof(uint8_t) * (afi_flag ? 2 : 1); + + if(data_size < data_size_min) { + error = Iso15693_3ErrorFormat; + break; + } + + if(afi_flag) { + const uint8_t afi = *data++; + // When AFI flag is set, ignore non-matching requests + if(afi != instance->data->system_info.afi) break; + } + + const uint8_t mask_len = *data++; + const size_t data_size_required = data_size_min + mask_len; + + if(data_size != data_size_required) { + error = Iso15693_3ErrorFormat; + break; + } + + if(mask_len != 0) { + // TODO FL-3633: Take mask_len and mask_value into account (if present) + } + + error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_INVENTORY); + if(error != Iso15693_3ErrorNone) break; + + bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.dsfid); // DSFID + iso15693_3_append_uid(instance->data, instance->tx_buffer); // UID + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_stay_quiet_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + UNUSED(flags); + + instance->state = Iso15693_3ListenerStateQuiet; + return Iso15693_3ErrorIgnore; +} + +static Iso15693_3Error iso15693_3_listener_read_block_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t block_num; + } Iso15693_3ReadBlockRequestLayout; + + const Iso15693_3ReadBlockRequestLayout* request = + (const Iso15693_3ReadBlockRequestLayout*)data; + + if(data_size != sizeof(Iso15693_3ReadBlockRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const uint32_t block_index = request->block_num; + const uint32_t block_count_max = instance->data->system_info.block_count; + + if(block_index >= block_count_max) { + error = Iso15693_3ErrorInternal; + break; + } + + error = iso15693_3_listener_extension_handler( + instance, ISO15693_3_CMD_READ_BLOCK, block_index); + if(error != Iso15693_3ErrorNone) break; + + if(flags & ISO15693_3_REQ_FLAG_T4_OPTION) { + iso15693_3_append_block_security( + instance->data, block_index, instance->tx_buffer); // Block security (optional) + } + + iso15693_3_append_block(instance->data, block_index, instance->tx_buffer); // Block data + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_write_block_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t block_num; + uint8_t block_data[]; + } Iso15693_3WriteBlockRequestLayout; + + const Iso15693_3WriteBlockRequestLayout* request = + (const Iso15693_3WriteBlockRequestLayout*)data; + + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + if(data_size <= sizeof(Iso15693_3WriteBlockRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const uint32_t block_index = request->block_num; + const uint32_t block_count_max = instance->data->system_info.block_count; + const uint32_t block_size_max = instance->data->system_info.block_size; + const size_t block_size_received = data_size - sizeof(Iso15693_3WriteBlockRequestLayout); + + if(block_index >= block_count_max) { + error = Iso15693_3ErrorInternal; + break; + } else if(block_size_received != block_size_max) { + error = Iso15693_3ErrorInternal; + break; + } else if(iso15693_3_is_block_locked(instance->data, block_index)) { + error = Iso15693_3ErrorInternal; + break; + } + + error = iso15693_3_listener_extension_handler( + instance, ISO15693_3_CMD_WRITE_BLOCK, block_index, request->block_data); + if(error != Iso15693_3ErrorNone) break; + + iso15693_3_set_block_data( + instance->data, block_index, request->block_data, block_size_received); + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_lock_block_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t block_num; + } Iso15693_3LockBlockRequestLayout; + + const Iso15693_3LockBlockRequestLayout* request = + (const Iso15693_3LockBlockRequestLayout*)data; + + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + if(data_size != sizeof(Iso15693_3LockBlockRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const uint32_t block_index = request->block_num; + const uint32_t block_count_max = instance->data->system_info.block_count; + + if(block_index >= block_count_max) { + error = Iso15693_3ErrorInternal; + break; + } else if(iso15693_3_is_block_locked(instance->data, block_index)) { + error = Iso15693_3ErrorInternal; + break; + } + + error = iso15693_3_listener_extension_handler( + instance, ISO15693_3_CMD_LOCK_BLOCK, block_index); + if(error != Iso15693_3ErrorNone) break; + + iso15693_3_set_block_locked(instance->data, block_index, true); + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_read_multi_blocks_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t first_block_num; + uint8_t block_count; + } Iso15693_3ReadMultiBlocksRequestLayout; + + const Iso15693_3ReadMultiBlocksRequestLayout* request = + (const Iso15693_3ReadMultiBlocksRequestLayout*)data; + + if(data_size != sizeof(Iso15693_3ReadMultiBlocksRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const uint32_t block_index_start = request->first_block_num; + const uint32_t block_index_end = block_index_start + request->block_count; + + const uint32_t block_count = request->block_count + 1; + const uint32_t block_count_max = instance->data->system_info.block_count; + const uint32_t block_count_available = block_count_max - block_index_start; + + if(block_count > block_count_available) { + error = Iso15693_3ErrorInternal; + break; + } + + error = iso15693_3_listener_extension_handler( + instance, + ISO15693_3_CMD_READ_MULTI_BLOCKS, + (uint32_t)block_index_start, + (uint32_t)block_index_end); + if(error != Iso15693_3ErrorNone) break; + + for(uint32_t i = block_index_start; i <= block_index_end; ++i) { + if(flags & ISO15693_3_REQ_FLAG_T4_OPTION) { + iso15693_3_append_block_security( + instance->data, i, instance->tx_buffer); // Block security (optional) + } + iso15693_3_append_block(instance->data, i, instance->tx_buffer); // Block data + } + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_write_multi_blocks_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t first_block_num; + uint8_t block_count; + uint8_t block_data[]; + } Iso15693_3WriteMultiBlocksRequestLayout; + + const Iso15693_3WriteMultiBlocksRequestLayout* request = + (const Iso15693_3WriteMultiBlocksRequestLayout*)data; + + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + if(data_size <= sizeof(Iso15693_3WriteMultiBlocksRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const uint32_t block_index_start = request->first_block_num; + const uint32_t block_index_end = block_index_start + request->block_count; + + const uint32_t block_count = request->block_count + 1; + const uint32_t block_count_max = instance->data->system_info.block_count; + const uint32_t block_count_available = block_count_max - block_index_start; + + const size_t block_data_size = data_size - sizeof(Iso15693_3WriteMultiBlocksRequestLayout); + const size_t block_size = block_data_size / block_count; + const size_t block_size_max = instance->data->system_info.block_size; + + if(block_count > block_count_available) { + error = Iso15693_3ErrorInternal; + break; + } else if(block_size != block_size_max) { + error = Iso15693_3ErrorInternal; + break; + } + + error = iso15693_3_listener_extension_handler( + instance, ISO15693_3_CMD_WRITE_MULTI_BLOCKS, block_index_start, block_index_end); + if(error != Iso15693_3ErrorNone) break; + + for(uint32_t i = block_index_start; i <= block_index_end; ++i) { + if(iso15693_3_is_block_locked(instance->data, i)) { + error = Iso15693_3ErrorInternal; + break; + } + } + + if(error != Iso15693_3ErrorNone) break; + + for(uint32_t i = block_index_start; i < block_count + request->first_block_num; ++i) { + const uint8_t* block_data = &request->block_data[block_size * i]; + iso15693_3_set_block_data(instance->data, i, block_data, block_size); + } + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_select_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + if(!(flags & ISO15693_3_REQ_FLAG_T4_ADDRESSED)) { + error = Iso15693_3ErrorFormat; + break; + } + + instance->state = Iso15693_3ListenerStateSelected; + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_reset_to_ready_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + UNUSED(flags); + + instance->state = Iso15693_3ListenerStateReady; + return Iso15693_3ErrorNone; +} + +static Iso15693_3Error iso15693_3_listener_write_afi_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t afi; + } Iso15693_3WriteAfiRequestLayout; + + const Iso15693_3WriteAfiRequestLayout* request = + (const Iso15693_3WriteAfiRequestLayout*)data; + + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + if(data_size <= sizeof(Iso15693_3WriteAfiRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } else if(instance->data->settings.lock_bits.afi) { + error = Iso15693_3ErrorInternal; + break; + } + + error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_WRITE_AFI); + if(error != Iso15693_3ErrorNone) break; + + instance->data->system_info.afi = request->afi; + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_lock_afi_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + Iso15693_3LockBits* lock_bits = &instance->data->settings.lock_bits; + + if(lock_bits->afi) { + error = Iso15693_3ErrorInternal; + break; + } + + error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_LOCK_AFI); + if(error != Iso15693_3ErrorNone) break; + + lock_bits->afi = true; + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_write_dsfid_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t dsfid; + } Iso15693_3WriteDsfidRequestLayout; + + const Iso15693_3WriteDsfidRequestLayout* request = + (const Iso15693_3WriteDsfidRequestLayout*)data; + + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + if(data_size <= sizeof(Iso15693_3WriteDsfidRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } else if(instance->data->settings.lock_bits.dsfid) { + error = Iso15693_3ErrorInternal; + break; + } + + error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_WRITE_DSFID); + if(error != Iso15693_3ErrorNone) break; + + instance->data->system_info.dsfid = request->dsfid; + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_lock_dsfid_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + instance->session_state.wait_for_eof = flags & ISO15693_3_REQ_FLAG_T4_OPTION; + + Iso15693_3LockBits* lock_bits = &instance->data->settings.lock_bits; + + if(lock_bits->dsfid) { + error = Iso15693_3ErrorInternal; + break; + } + + error = iso15693_3_listener_extension_handler(instance, ISO15693_3_CMD_LOCK_DSFID); + if(error != Iso15693_3ErrorNone) break; + + lock_bits->dsfid = true; + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_get_system_info_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + UNUSED(flags); + + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + const uint8_t system_flags = instance->data->system_info.flags; + bit_buffer_append_byte(instance->tx_buffer, system_flags); // System info flags + + iso15693_3_append_uid(instance->data, instance->tx_buffer); // UID + + if(system_flags & ISO15693_3_SYSINFO_FLAG_DSFID) { + bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.dsfid); + } + if(system_flags & ISO15693_3_SYSINFO_FLAG_AFI) { + bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.afi); + } + if(system_flags & ISO15693_3_SYSINFO_FLAG_MEMORY) { + const uint8_t memory_info[2] = { + instance->data->system_info.block_count - 1, + instance->data->system_info.block_size - 1, + }; + bit_buffer_append_bytes(instance->tx_buffer, memory_info, COUNT_OF(memory_info)); + } + if(system_flags & ISO15693_3_SYSINFO_FLAG_IC_REF) { + bit_buffer_append_byte(instance->tx_buffer, instance->data->system_info.ic_ref); + } + + } while(false); + + return error; +} + +static Iso15693_3Error iso15693_3_listener_get_multi_blocks_security_handler( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(flags); + + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t first_block_num; + uint8_t block_count; + } Iso15693_3GetMultiBlocksSecurityRequestLayout; + + const Iso15693_3GetMultiBlocksSecurityRequestLayout* request = + (const Iso15693_3GetMultiBlocksSecurityRequestLayout*)data; + + if(data_size < sizeof(Iso15693_3GetMultiBlocksSecurityRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const uint32_t block_index_start = request->first_block_num; + const uint32_t block_index_end = block_index_start + request->block_count; + + const uint32_t block_count_max = instance->data->system_info.block_count; + + if(block_index_end >= block_count_max) { + error = Iso15693_3ErrorInternal; + break; + } + + for(uint32_t i = block_index_start; i <= block_index_end; ++i) { + bit_buffer_append_byte( + instance->tx_buffer, iso15693_3_is_block_locked(instance->data, i) ? 1 : 0); + } + } while(false); + + return error; +} + +const Iso15693_3ListenerHandlerTable iso15693_3_handler_table = { + .mandatory = + { + iso15693_3_listener_inventory_handler, + iso15693_3_listener_stay_quiet_handler, + }, + .optional = + { + iso15693_3_listener_read_block_handler, + iso15693_3_listener_write_block_handler, + iso15693_3_listener_lock_block_handler, + iso15693_3_listener_read_multi_blocks_handler, + iso15693_3_listener_write_multi_blocks_handler, + iso15693_3_listener_select_handler, + iso15693_3_listener_reset_to_ready_handler, + iso15693_3_listener_write_afi_handler, + iso15693_3_listener_lock_afi_handler, + iso15693_3_listener_write_dsfid_handler, + iso15693_3_listener_lock_dsfid_handler, + iso15693_3_listener_get_system_info_handler, + iso15693_3_listener_get_multi_blocks_security_handler, + }, +}; + +static Iso15693_3Error iso15693_3_listener_handle_standard_request( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size, + uint8_t command, + uint8_t flags) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + Iso15693_3RequestHandler handler = NULL; + + if(command < ISO15693_3_CMD_MANDATORY_RFU) { + handler = iso15693_3_handler_table.mandatory[command - ISO15693_3_CMD_MANDATORY_START]; + } else if(command >= ISO15693_3_CMD_OPTIONAL_START && command < ISO15693_3_CMD_OPTIONAL_RFU) { + handler = iso15693_3_handler_table.optional[command - ISO15693_3_CMD_OPTIONAL_START]; + } + + if(handler == NULL) { + error = Iso15693_3ErrorNotSupported; + break; + } + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_NONE); + + error = handler(instance, data, data_size, flags); + + // The request was fully handled in the protocol extension, no further action necessary + if(error == Iso15693_3ErrorFullyHandled) { + error = Iso15693_3ErrorNone; + } + + // Several commands may not require an answer + if(error == Iso15693_3ErrorFormat || error == Iso15693_3ErrorIgnore) break; + + if(error != Iso15693_3ErrorNone) { + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_ERROR); + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_ERROR_UNKNOWN); + } + + Iso15693_3ListenerSessionState* session_state = &instance->session_state; + + if(!session_state->wait_for_eof) { + error = iso15693_3_listener_send_frame(instance, instance->tx_buffer); + } + + } while(false); + + return error; +} + +static inline Iso15693_3Error iso15693_3_listener_handle_custom_request( + Iso15693_3Listener* instance, + const uint8_t* data, + size_t data_size) { + Iso15693_3Error error; + + do { + typedef struct { + uint8_t manufacturer; + uint8_t extra[]; + } Iso15693_3CustomRequestLayout; + + if(data_size < sizeof(Iso15693_3CustomRequestLayout)) { + error = Iso15693_3ErrorFormat; + break; + } + + const Iso15693_3CustomRequestLayout* request = (const Iso15693_3CustomRequestLayout*)data; + + if(request->manufacturer != iso15693_3_get_manufacturer_id(instance->data)) { + error = Iso15693_3ErrorIgnore; + break; + } + + // This error code will trigger the CustomCommand listener event + error = Iso15693_3ErrorNotSupported; + } while(false); + + return error; +} + +Iso15693_3Error iso15693_3_listener_set_extension_handler_table( + Iso15693_3Listener* instance, + const Iso15693_3ExtensionHandlerTable* table, + void* context) { + furi_assert(instance); + furi_assert(context); + + instance->extension_table = table; + instance->extension_context = context; + return Iso15693_3ErrorNone; +} + +Iso15693_3Error iso15693_3_listener_ready(Iso15693_3Listener* instance) { + furi_assert(instance); + instance->state = Iso15693_3ListenerStateReady; + return Iso15693_3ErrorNone; +} + +static Iso15693_3Error iso15693_3_listener_process_nfc_error(NfcError error) { + Iso15693_3Error ret = Iso15693_3ErrorNone; + + if(error == NfcErrorNone) { + ret = Iso15693_3ErrorNone; + } else if(error == NfcErrorTimeout) { + ret = Iso15693_3ErrorTimeout; + } else { + ret = Iso15693_3ErrorFieldOff; + } + + return ret; +} + +Iso15693_3Error + iso15693_3_listener_send_frame(Iso15693_3Listener* instance, const BitBuffer* tx_buffer) { + furi_assert(instance); + furi_assert(tx_buffer); + + bit_buffer_copy(instance->tx_buffer, tx_buffer); + iso13239_crc_append(Iso13239CrcTypeDefault, instance->tx_buffer); + + NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer); + return iso15693_3_listener_process_nfc_error(error); +} + +Iso15693_3Error + iso15693_3_listener_process_request(Iso15693_3Listener* instance, const BitBuffer* rx_buffer) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + typedef struct { + uint8_t flags; + uint8_t command; + uint8_t data[]; + } Iso15693_3RequestLayout; + + const size_t buf_size = bit_buffer_get_size_bytes(rx_buffer); + const size_t buf_size_min = sizeof(Iso15693_3RequestLayout); + + if(buf_size < buf_size_min) { + error = Iso15693_3ErrorFormat; + break; + } + + const Iso15693_3RequestLayout* request = + (const Iso15693_3RequestLayout*)bit_buffer_get_data(rx_buffer); + + Iso15693_3ListenerSessionState* session_state = &instance->session_state; + + if((request->flags & ISO15693_3_REQ_FLAG_INVENTORY_T5) == 0) { + session_state->selected = request->flags & ISO15693_3_REQ_FLAG_T4_SELECTED; + session_state->addressed = request->flags & ISO15693_3_REQ_FLAG_T4_ADDRESSED; + + if(session_state->selected && session_state->addressed) { + // A request mode can be either addressed or selected, but not both + error = Iso15693_3ErrorUnknown; + break; + } else if(instance->state == Iso15693_3ListenerStateQuiet) { + // If the card is quiet, ignore non-addressed commands + if(session_state->addressed) { + error = Iso15693_3ErrorIgnore; + break; + } + } else if(instance->state != Iso15693_3ListenerStateSelected) { + // If the card is not selected, ignore selected commands + if(session_state->selected) { + error = Iso15693_3ErrorIgnore; + break; + } + } + } else { + // If the card is quiet, ignore inventory commands + if(instance->state == Iso15693_3ListenerStateQuiet) { + error = Iso15693_3ErrorIgnore; + break; + } + + session_state->selected = false; + session_state->addressed = false; + } + + if(request->command >= ISO15693_3_CMD_CUSTOM_START) { + // Custom commands are properly handled in the protocol-specific top-level poller + error = iso15693_3_listener_handle_custom_request( + instance, request->data, buf_size - buf_size_min); + break; + } + + const uint8_t* data; + size_t data_size; + + if(session_state->addressed) { + // In addressed mode, UID must be included in each command + const size_t buf_size_min_addr = buf_size_min + ISO15693_3_UID_SIZE; + + if(buf_size < buf_size_min_addr) { + error = Iso15693_3ErrorFormat; + break; + } else if(!iso15693_3_is_equal_uid(instance->data, request->data)) { + error = Iso15693_3ErrorUidMismatch; + break; + } + + data = &request->data[ISO15693_3_UID_SIZE]; + data_size = buf_size - buf_size_min_addr; + + } else { + data = request->data; + data_size = buf_size - buf_size_min; + } + + error = iso15693_3_listener_handle_standard_request( + instance, data, data_size, request->command, request->flags); + + } while(false); + + return error; +} + +Iso15693_3Error iso15693_3_listener_process_single_eof(Iso15693_3Listener* instance) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + if(!instance->session_state.wait_for_eof) { + error = Iso15693_3ErrorUnexpectedResponse; + break; + } + + instance->session_state.wait_for_eof = false; + + error = iso15693_3_listener_send_frame(instance, instance->tx_buffer); + } while(false); + + return error; +} + +Iso15693_3Error iso15693_3_listener_process_uid_mismatch( + Iso15693_3Listener* instance, + const BitBuffer* rx_buffer) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + // No checks, assuming they have been made beforehand + typedef struct { + uint8_t flags; + uint8_t command; + } Iso15693_3RequestLayout; + + const Iso15693_3RequestLayout* request = + (const Iso15693_3RequestLayout*)bit_buffer_get_data(rx_buffer); + + if(request->command == ISO15693_3_CMD_SELECT) { + if(instance->state == Iso15693_3ListenerStateSelected) { + error = iso15693_3_listener_ready(instance); + } + } + + return error; +} diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.h b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.h new file mode 100644 index 00000000000..a9e0822bff9 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_listener_i.h @@ -0,0 +1,70 @@ +#pragma once + +#include + +#include "iso15693_3_listener.h" + +#include "iso15693_3_i.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + Iso15693_3ListenerStateReady, + Iso15693_3ListenerStateSelected, + Iso15693_3ListenerStateQuiet, +} Iso15693_3ListenerState; + +typedef struct { + bool selected; + bool addressed; + bool wait_for_eof; +} Iso15693_3ListenerSessionState; + +typedef Iso15693_3Error (*Iso15693_3ExtensionHandler)(void* context, va_list args); + +typedef struct { + Iso15693_3ExtensionHandler mandatory[ISO15693_3_MANDATORY_COUNT]; + Iso15693_3ExtensionHandler optional[ISO15693_3_OPTIONAL_COUNT]; +} Iso15693_3ExtensionHandlerTable; + +struct Iso15693_3Listener { + Nfc* nfc; + Iso15693_3Data* data; + Iso15693_3ListenerState state; + Iso15693_3ListenerSessionState session_state; + BitBuffer* tx_buffer; + + NfcGenericEvent generic_event; + Iso15693_3ListenerEvent iso15693_3_event; + Iso15693_3ListenerEventData iso15693_3_event_data; + NfcGenericCallback callback; + void* context; + + const Iso15693_3ExtensionHandlerTable* extension_table; + void* extension_context; +}; + +Iso15693_3Error iso15693_3_listener_set_extension_handler_table( + Iso15693_3Listener* instance, + const Iso15693_3ExtensionHandlerTable* table, + void* context); + +Iso15693_3Error iso15693_3_listener_ready(Iso15693_3Listener* instance); + +Iso15693_3Error + iso15693_3_listener_send_frame(Iso15693_3Listener* instance, const BitBuffer* tx_buffer); + +Iso15693_3Error + iso15693_3_listener_process_request(Iso15693_3Listener* instance, const BitBuffer* rx_buffer); + +Iso15693_3Error iso15693_3_listener_process_single_eof(Iso15693_3Listener* instance); + +Iso15693_3Error iso15693_3_listener_process_uid_mismatch( + Iso15693_3Listener* instance, + const BitBuffer* rx_buffer); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_poller.c b/lib/nfc/protocols/iso15693_3/iso15693_3_poller.c new file mode 100644 index 00000000000..cf27b1f3fd5 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_poller.c @@ -0,0 +1,122 @@ +#include "iso15693_3_poller_i.h" + +#include + +#include + +#define TAG "ISO15693_3Poller" + +const Iso15693_3Data* iso15693_3_poller_get_data(Iso15693_3Poller* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +static Iso15693_3Poller* iso15693_3_poller_alloc(Nfc* nfc) { + furi_assert(nfc); + + Iso15693_3Poller* instance = malloc(sizeof(Iso15693_3Poller)); + instance->nfc = nfc; + instance->tx_buffer = bit_buffer_alloc(ISO15693_3_POLLER_MAX_BUFFER_SIZE); + instance->rx_buffer = bit_buffer_alloc(ISO15693_3_POLLER_MAX_BUFFER_SIZE); + + nfc_config(instance->nfc, NfcModePoller, NfcTechIso15693); + nfc_set_guard_time_us(instance->nfc, ISO15693_3_GUARD_TIME_US); + nfc_set_fdt_poll_fc(instance->nfc, ISO15693_3_FDT_POLL_FC); + nfc_set_fdt_poll_poll_us(instance->nfc, ISO15693_3_POLL_POLL_MIN_US); + instance->data = iso15693_3_alloc(); + + instance->iso15693_3_event.data = &instance->iso15693_3_event_data; + instance->general_event.protocol = NfcProtocolIso15693_3; + instance->general_event.event_data = &instance->iso15693_3_event; + instance->general_event.instance = instance; + + return instance; +} + +static void iso15693_3_poller_free(Iso15693_3Poller* instance) { + furi_assert(instance); + + furi_assert(instance->tx_buffer); + furi_assert(instance->rx_buffer); + furi_assert(instance->data); + + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + iso15693_3_free(instance->data); + free(instance); +} + +static void iso15693_3_poller_set_callback( + Iso15693_3Poller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand iso15693_3_poller_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolInvalid); + furi_assert(event.event_data); + + Iso15693_3Poller* instance = context; + NfcEvent* nfc_event = event.event_data; + NfcCommand command = NfcCommandContinue; + + if(nfc_event->type == NfcEventTypePollerReady) { + if(instance->state != Iso15693_3PollerStateActivated) { + Iso15693_3Error error = iso15693_3_poller_activate(instance, instance->data); + if(error == Iso15693_3ErrorNone) { + instance->iso15693_3_event.type = Iso15693_3PollerEventTypeReady; + instance->iso15693_3_event_data.error = error; + command = instance->callback(instance->general_event, instance->context); + } else { + instance->iso15693_3_event.type = Iso15693_3PollerEventTypeError; + instance->iso15693_3_event_data.error = error; + command = instance->callback(instance->general_event, instance->context); + // Add delay to switch context + furi_delay_ms(100); + } + } else { + instance->iso15693_3_event.type = Iso15693_3PollerEventTypeReady; + instance->iso15693_3_event_data.error = Iso15693_3ErrorNone; + command = instance->callback(instance->general_event, instance->context); + } + } + + return command; +} + +static bool iso15693_3_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.instance); + furi_assert(event.protocol == NfcProtocolInvalid); + + bool protocol_detected = false; + Iso15693_3Poller* instance = context; + NfcEvent* nfc_event = event.event_data; + furi_assert(instance->state == Iso15693_3PollerStateIdle); + + if(nfc_event->type == NfcEventTypePollerReady) { + uint8_t uid[ISO15693_3_UID_SIZE]; + Iso15693_3Error error = iso15693_3_poller_inventory(instance, uid); + protocol_detected = (error == Iso15693_3ErrorNone); + } + + return protocol_detected; +} + +const NfcPollerBase nfc_poller_iso15693_3 = { + .alloc = (NfcPollerAlloc)iso15693_3_poller_alloc, + .free = (NfcPollerFree)iso15693_3_poller_free, + .set_callback = (NfcPollerSetCallback)iso15693_3_poller_set_callback, + .run = (NfcPollerRun)iso15693_3_poller_run, + .detect = (NfcPollerDetect)iso15693_3_poller_detect, + .get_data = (NfcPollerGetData)iso15693_3_poller_get_data, +}; diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_poller.h b/lib/nfc/protocols/iso15693_3/iso15693_3_poller.h new file mode 100644 index 00000000000..a187ceace14 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_poller.h @@ -0,0 +1,149 @@ +#pragma once + +#include "iso15693_3.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Iso15693_3Poller opaque type definition. + */ +typedef struct Iso15693_3Poller Iso15693_3Poller; + +/** + * @brief Enumeration of possible Iso15693_3 poller event types. + */ +typedef enum { + Iso15693_3PollerEventTypeError, /**< The card was activated by the poller. */ + Iso15693_3PollerEventTypeReady, /**< An error occured during activation procedure. */ +} Iso15693_3PollerEventType; + +/** + * @brief Iso15693_3 poller event data. + */ +typedef union { + Iso15693_3Error error; /**< Error code indicating card activation fail reason. */ +} Iso15693_3PollerEventData; + +/** + * @brief Iso15693_3 poller event structure. + * + * Upon emission of an event, an instance of this struct will be passed to the callback. + */ +typedef struct { + Iso15693_3PollerEventType type; /**< Type of emmitted event. */ + Iso15693_3PollerEventData* data; /**< Pointer to event specific data. */ +} Iso15693_3PollerEvent; + +/** + * @brief Transmit and receive Iso15693_3 frames in poller mode. + * + * Must ONLY be used inside the callback function. + * + * The rx_buffer will be filled with any data received as a response to data + * sent from tx_buffer, with a timeout defined by the fwt parameter. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted. + * @param[out] rx_buffer pointer to the buffer to be filled with received data. + * @param[in] fwt frame wait time (response timeout), in carrier cycles. + * @return Iso15693_3ErrorNone on success, an error code on failure. + */ +Iso15693_3Error iso15693_3_poller_send_frame( + Iso15693_3Poller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt); + +/** + * @brief Perform activation procedure. + * + * Must ONLY be used inside the callback function. + * + * Perfoms the activation procedure as defined in Iso15693_3. The data + * field will be filled with Iso15693_3Data data on success. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the Iso15693_3 data structure to be filled. + * @return Iso15693_3ErrorNone on success, an error code on failure. + */ +Iso15693_3Error iso15693_3_poller_activate(Iso15693_3Poller* instance, Iso15693_3Data* data); + +/** + * @brief Send invertory command and parse response. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] uid pointer to the buffer to be filled with the UID. + * @return Iso15693_3ErrorNone on success, an error code on failure. + */ +Iso15693_3Error iso15693_3_poller_inventory(Iso15693_3Poller* instance, uint8_t* uid); + +/** + * @brief Send get system info command and parse response. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the Iso15693_3SystemInfo structure to be filled. + * @return Iso15693_3ErrorNone on success, an error code on failure. + */ +Iso15693_3Error + iso15693_3_poller_get_system_info(Iso15693_3Poller* instance, Iso15693_3SystemInfo* data); + +/** + * @brief Read Iso15693_3 block. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the buffer to be filled with the block data. + * @param[in] block_number block number to be read. + * @param[in] block_size size of the block to be read. + * @return Iso15693_3ErrorNone on success, an error code on failure. + */ +Iso15693_3Error iso15693_3_poller_read_block( + Iso15693_3Poller* instance, + uint8_t* data, + uint8_t block_number, + uint8_t block_size); + +/** + * @brief Read multiple Iso15693_3 blocks. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the buffer to be filled with the block data. + * @param[in] block_count number of blocks to be read. + * @param[in] block_size size of the blocks to be read. + * @return Iso15693_3ErrorNone on success, an error code on failure. + */ +Iso15693_3Error iso15693_3_poller_read_blocks( + Iso15693_3Poller* instance, + uint8_t* data, + uint16_t block_count, + uint8_t block_size); + +/** + * @brief Get Iso15693_3 block security status. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the buffer to be filled with the block security status. + * @param[in] block_count block security number to be read. + * @return Iso15693_3ErrorNone on success, an error code on failure. + */ +Iso15693_3Error iso15693_3_poller_get_blocks_security( + Iso15693_3Poller* instance, + uint8_t* data, + uint16_t block_count); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_poller_defs.h b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_defs.h new file mode 100644 index 00000000000..f0447c28ba3 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcPollerBase nfc_poller_iso15693_3; diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c new file mode 100644 index 00000000000..ca6f5435e3e --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.c @@ -0,0 +1,303 @@ +#include "iso15693_3_poller_i.h" + +#include + +#define TAG "Iso15693_3Poller" + +#define BITS_IN_BYTE (8) + +#define ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY (32U) + +static Iso15693_3Error iso15693_3_poller_process_nfc_error(NfcError error) { + switch(error) { + case NfcErrorNone: + return Iso15693_3ErrorNone; + case NfcErrorTimeout: + return Iso15693_3ErrorTimeout; + default: + return Iso15693_3ErrorNotPresent; + } +} + +static Iso15693_3Error iso15693_3_poller_filter_error(Iso15693_3Error error) { + switch(error) { + /* If a particular optional command is not supported, the card might + * respond with a "Not supported" error or not respond at all. + * Therefore, treat these errors as non-critical ones. */ + case Iso15693_3ErrorNotSupported: + case Iso15693_3ErrorTimeout: + return Iso15693_3ErrorNone; + default: + return error; + } +} + +static Iso15693_3Error iso15693_3_poller_prepare_trx(Iso15693_3Poller* instance) { + furi_assert(instance); + + if(instance->state == Iso15693_3PollerStateIdle) { + return iso15693_3_poller_activate(instance, NULL); + } + + return Iso15693_3ErrorNone; +} + +static Iso15693_3Error iso15693_3_poller_frame_exchange( + Iso15693_3Poller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + + Iso15693_3Error ret = Iso15693_3ErrorNone; + + do { + if(bit_buffer_get_size_bytes(tx_buffer) > + bit_buffer_get_capacity_bytes(instance->tx_buffer) - ISO13239_CRC_SIZE) { + ret = Iso15693_3ErrorBufferOverflow; + break; + } + + bit_buffer_copy(instance->tx_buffer, tx_buffer); + iso13239_crc_append(Iso13239CrcTypeDefault, instance->tx_buffer); + + NfcError error = + nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt); + if(error != NfcErrorNone) { + ret = iso15693_3_poller_process_nfc_error(error); + break; + } + + if(!iso13239_crc_check(Iso13239CrcTypeDefault, instance->rx_buffer)) { + ret = Iso15693_3ErrorWrongCrc; + break; + } + + iso13239_crc_trim(instance->rx_buffer); + bit_buffer_copy(rx_buffer, instance->rx_buffer); + } while(false); + + return ret; +} + +Iso15693_3Error iso15693_3_poller_activate(Iso15693_3Poller* instance, Iso15693_3Data* data) { + furi_assert(instance); + furi_assert(instance->nfc); + + iso15693_3_reset(data); + + Iso15693_3Error ret; + + do { + instance->state = Iso15693_3PollerStateColResInProgress; + + // Inventory: Mandatory command + ret = iso15693_3_poller_inventory(instance, data->uid); + if(ret != Iso15693_3ErrorNone) { + instance->state = Iso15693_3PollerStateColResFailed; + break; + } + + instance->state = Iso15693_3PollerStateActivated; + + // Get system info: Optional command + Iso15693_3SystemInfo* system_info = &data->system_info; + ret = iso15693_3_poller_get_system_info(instance, system_info); + if(ret != Iso15693_3ErrorNone) { + ret = iso15693_3_poller_filter_error(ret); + break; + } + + if(system_info->block_count > 0) { + // Read blocks: Optional command + simple_array_init( + data->block_data, system_info->block_count * system_info->block_size); + ret = iso15693_3_poller_read_blocks( + instance, + simple_array_get_data(data->block_data), + system_info->block_count, + system_info->block_size); + if(ret != Iso15693_3ErrorNone) { + ret = iso15693_3_poller_filter_error(ret); + break; + } + + // Get block security status: Optional command + simple_array_init(data->block_security, system_info->block_count); + + ret = iso15693_3_poller_get_blocks_security( + instance, simple_array_get_data(data->block_security), system_info->block_count); + if(ret != Iso15693_3ErrorNone) { + ret = iso15693_3_poller_filter_error(ret); + break; + } + } + } while(false); + + return ret; +} + +Iso15693_3Error iso15693_3_poller_inventory(Iso15693_3Poller* instance, uint8_t* uid) { + furi_assert(instance); + furi_assert(instance->nfc); + furi_assert(uid); + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + // Send INVENTORY + bit_buffer_append_byte( + instance->tx_buffer, + ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI | + ISO15693_3_REQ_FLAG_INVENTORY_T5 | ISO15693_3_REQ_FLAG_T5_N_SLOTS_1); + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_INVENTORY); + bit_buffer_append_byte(instance->tx_buffer, 0x00); + + Iso15693_3Error ret; + + do { + ret = iso15693_3_poller_frame_exchange( + instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC); + if(ret != Iso15693_3ErrorNone) break; + + ret = iso15693_3_inventory_response_parse(uid, instance->rx_buffer); + } while(false); + + return ret; +} + +Iso15693_3Error + iso15693_3_poller_get_system_info(Iso15693_3Poller* instance, Iso15693_3SystemInfo* data) { + furi_assert(instance); + furi_assert(data); + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + // Send GET SYSTEM INFO + bit_buffer_append_byte( + instance->tx_buffer, ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI); + + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_GET_SYS_INFO); + + Iso15693_3Error ret; + + do { + ret = iso15693_3_poller_frame_exchange( + instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC); + if(ret != Iso15693_3ErrorNone) break; + + ret = iso15693_3_system_info_response_parse(data, instance->rx_buffer); + } while(false); + + return ret; +} + +Iso15693_3Error iso15693_3_poller_read_block( + Iso15693_3Poller* instance, + uint8_t* data, + uint8_t block_number, + uint8_t block_size) { + furi_assert(instance); + furi_assert(data); + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + bit_buffer_append_byte( + instance->tx_buffer, ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI); + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_READ_BLOCK); + bit_buffer_append_byte(instance->tx_buffer, block_number); + + Iso15693_3Error ret; + + do { + ret = iso15693_3_poller_send_frame( + instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC); + if(ret != Iso15693_3ErrorNone) break; + + ret = iso15693_3_read_block_response_parse(data, block_size, instance->rx_buffer); + } while(false); + + return ret; +} + +Iso15693_3Error iso15693_3_poller_read_blocks( + Iso15693_3Poller* instance, + uint8_t* data, + uint16_t block_count, + uint8_t block_size) { + furi_assert(instance); + furi_assert(data); + furi_assert(block_count); + furi_assert(block_size); + + Iso15693_3Error ret = Iso15693_3ErrorNone; + + for(uint32_t i = 0; i < block_count; ++i) { + ret = iso15693_3_poller_read_block(instance, &data[block_size * i], i, block_size); + if(ret != Iso15693_3ErrorNone) break; + } + + return ret; +} + +Iso15693_3Error iso15693_3_poller_get_blocks_security( + Iso15693_3Poller* instance, + uint8_t* data, + uint16_t block_count) { + furi_assert(instance); + furi_assert(data); + + // Limit the number of blocks to 32 in a single query + const uint32_t num_queries = block_count / ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY + + (block_count % ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY ? 1 : 0); + + Iso15693_3Error ret = Iso15693_3ErrorNone; + + for(uint32_t i = 0; i < num_queries; ++i) { + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + bit_buffer_append_byte( + instance->tx_buffer, + ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI); + + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_CMD_GET_BLOCKS_SECURITY); + + const uint8_t start_block_num = i * ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY; + bit_buffer_append_byte(instance->tx_buffer, start_block_num); + + const uint8_t block_count_per_query = + MIN(block_count - start_block_num, (uint16_t)ISO15693_3_POLLER_NUM_BLOCKS_PER_QUERY); + // Block count byte must be 1 less than the desired count + bit_buffer_append_byte(instance->tx_buffer, block_count_per_query - 1); + + ret = iso15693_3_poller_send_frame( + instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC); + if(ret != Iso15693_3ErrorNone) break; + + ret = iso15693_3_get_block_security_response_parse( + &data[start_block_num], block_count_per_query, instance->rx_buffer); + if(ret != Iso15693_3ErrorNone) break; + } + + return ret; +} + +Iso15693_3Error iso15693_3_poller_send_frame( + Iso15693_3Poller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + Iso15693_3Error ret; + + do { + ret = iso15693_3_poller_prepare_trx(instance); + if(ret != Iso15693_3ErrorNone) break; + + ret = iso15693_3_poller_frame_exchange(instance, tx_buffer, rx_buffer, fwt); + } while(false); + + return ret; +} diff --git a/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.h b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.h new file mode 100644 index 00000000000..346d0d724c5 --- /dev/null +++ b/lib/nfc/protocols/iso15693_3/iso15693_3_poller_i.h @@ -0,0 +1,40 @@ +#pragma once + +#include "iso15693_3_poller.h" + +#include "iso15693_3_i.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ISO15693_3_POLLER_MAX_BUFFER_SIZE (64U) + +typedef enum { + Iso15693_3PollerStateIdle, + Iso15693_3PollerStateColResInProgress, + Iso15693_3PollerStateColResFailed, + Iso15693_3PollerStateActivated, +} Iso15693_3PollerState; + +struct Iso15693_3Poller { + Nfc* nfc; + Iso15693_3PollerState state; + Iso15693_3Data* data; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + + NfcGenericEvent general_event; + Iso15693_3PollerEvent iso15693_3_event; + Iso15693_3PollerEventData iso15693_3_event_data; + NfcGenericCallback callback; + void* context; +}; + +const Iso15693_3Data* iso15693_3_poller_get_data(Iso15693_3Poller* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_classic/crypto1.c b/lib/nfc/protocols/mf_classic/crypto1.c new file mode 100644 index 00000000000..02bc677ba69 --- /dev/null +++ b/lib/nfc/protocols/mf_classic/crypto1.c @@ -0,0 +1,177 @@ +#include "crypto1.h" + +#include +#include + +// Algorithm from https://github.com/RfidResearchGroup/proxmark3.git + +#define SWAPENDIAN(x) \ + ((x) = ((x) >> 8 & 0xff00ff) | ((x)&0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16) +#define LF_POLY_ODD (0x29CE5C) +#define LF_POLY_EVEN (0x870804) + +#define BEBIT(x, n) FURI_BIT(x, (n) ^ 24) + +Crypto1* crypto1_alloc() { + Crypto1* instance = malloc(sizeof(Crypto1)); + + return instance; +} + +void crypto1_free(Crypto1* instance) { + furi_assert(instance); + + free(instance); +} + +void crypto1_reset(Crypto1* crypto1) { + furi_assert(crypto1); + crypto1->even = 0; + crypto1->odd = 0; +} + +void crypto1_init(Crypto1* crypto1, uint64_t key) { + furi_assert(crypto1); + crypto1->even = 0; + crypto1->odd = 0; + for(int8_t i = 47; i > 0; i -= 2) { + crypto1->odd = crypto1->odd << 1 | FURI_BIT(key, (i - 1) ^ 7); + crypto1->even = crypto1->even << 1 | FURI_BIT(key, i ^ 7); + } +} + +static uint32_t crypto1_filter(uint32_t in) { + uint32_t out = 0; + out = 0xf22c0 >> (in & 0xf) & 16; + out |= 0x6c9c0 >> (in >> 4 & 0xf) & 8; + out |= 0x3c8b0 >> (in >> 8 & 0xf) & 4; + out |= 0x1e458 >> (in >> 12 & 0xf) & 2; + out |= 0x0d938 >> (in >> 16 & 0xf) & 1; + return FURI_BIT(0xEC57E80A, out); +} + +uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted) { + furi_assert(crypto1); + uint8_t out = crypto1_filter(crypto1->odd); + uint32_t feed = out & (!!is_encrypted); + feed ^= !!in; + feed ^= LF_POLY_ODD & crypto1->odd; + feed ^= LF_POLY_EVEN & crypto1->even; + crypto1->even = crypto1->even << 1 | (nfc_util_even_parity32(feed)); + + FURI_SWAP(crypto1->odd, crypto1->even); + return out; +} + +uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted) { + furi_assert(crypto1); + uint8_t out = 0; + for(uint8_t i = 0; i < 8; i++) { + out |= crypto1_bit(crypto1, FURI_BIT(in, i), is_encrypted) << i; + } + return out; +} + +uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted) { + furi_assert(crypto1); + uint32_t out = 0; + for(uint8_t i = 0; i < 32; i++) { + out |= (uint32_t)crypto1_bit(crypto1, BEBIT(in, i), is_encrypted) << (24 ^ i); + } + return out; +} + +uint32_t prng_successor(uint32_t x, uint32_t n) { + SWAPENDIAN(x); + while(n--) x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31; + + return SWAPENDIAN(x); +} + +void crypto1_decrypt(Crypto1* crypto, const BitBuffer* buff, BitBuffer* out) { + furi_assert(crypto); + furi_assert(buff); + furi_assert(out); + + size_t bits = bit_buffer_get_size(buff); + bit_buffer_set_size(out, bits); + const uint8_t* encrypted_data = bit_buffer_get_data(buff); + if(bits < 8) { + uint8_t decrypted_byte = 0; + uint8_t encrypted_byte = encrypted_data[0]; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 0)) << 0; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 1)) << 1; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 2)) << 2; + decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_byte, 3)) << 3; + bit_buffer_set_byte(out, 0, decrypted_byte); + } else { + for(size_t i = 0; i < bits / 8; i++) { + uint8_t decrypted_byte = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i]; + bit_buffer_set_byte(out, i, decrypted_byte); + } + } +} + +void crypto1_encrypt(Crypto1* crypto, uint8_t* keystream, const BitBuffer* buff, BitBuffer* out) { + furi_assert(crypto); + furi_assert(buff); + furi_assert(out); + + size_t bits = bit_buffer_get_size(buff); + bit_buffer_set_size(out, bits); + const uint8_t* plain_data = bit_buffer_get_data(buff); + if(bits < 8) { + uint8_t encrypted_byte = 0; + for(size_t i = 0; i < bits; i++) { + encrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i; + } + bit_buffer_set_byte(out, 0, encrypted_byte); + } else { + for(size_t i = 0; i < bits / 8; i++) { + uint8_t encrypted_byte = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^ + plain_data[i]; + bool parity_bit = + ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01); + bit_buffer_set_byte_with_parity(out, i, encrypted_byte, parity_bit); + } + } +} + +void crypto1_encrypt_reader_nonce( + Crypto1* crypto, + uint64_t key, + uint32_t cuid, + uint8_t* nt, + uint8_t* nr, + BitBuffer* out, + bool is_nested) { + furi_assert(crypto); + furi_assert(nt); + furi_assert(nr); + furi_assert(out); + + bit_buffer_set_size_bytes(out, 8); + uint32_t nt_num = nfc_util_bytes2num(nt, sizeof(uint32_t)); + + crypto1_init(crypto, key); + if(is_nested) { + nt_num = crypto1_word(crypto, nt_num ^ cuid, 1) ^ nt_num; + } else { + crypto1_word(crypto, nt_num ^ cuid, 0); + } + + for(size_t i = 0; i < 4; i++) { + uint8_t byte = crypto1_byte(crypto, nr[i], 0) ^ nr[i]; + bool parity_bit = ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nr[i])) & 0x01); + bit_buffer_set_byte_with_parity(out, i, byte, parity_bit); + nr[i] = byte; + } + + nt_num = prng_successor(nt_num, 32); + for(size_t i = 4; i < 8; i++) { + nt_num = prng_successor(nt_num, 8); + uint8_t byte = crypto1_byte(crypto, 0, 0) ^ (uint8_t)(nt_num); + bool parity_bit = ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nt_num)) & 0x01); + bit_buffer_set_byte_with_parity(out, i, byte, parity_bit); + } +} diff --git a/lib/nfc/protocols/mf_classic/crypto1.h b/lib/nfc/protocols/mf_classic/crypto1.h new file mode 100644 index 00000000000..7cc16fcffde --- /dev/null +++ b/lib/nfc/protocols/mf_classic/crypto1.h @@ -0,0 +1,45 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint32_t odd; + uint32_t even; +} Crypto1; + +Crypto1* crypto1_alloc(); + +void crypto1_free(Crypto1* instance); + +void crypto1_reset(Crypto1* crypto1); + +void crypto1_init(Crypto1* crypto1, uint64_t key); + +uint8_t crypto1_bit(Crypto1* crypto1, uint8_t in, int is_encrypted); + +uint8_t crypto1_byte(Crypto1* crypto1, uint8_t in, int is_encrypted); + +uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted); + +void crypto1_decrypt(Crypto1* crypto, const BitBuffer* buff, BitBuffer* out); + +void crypto1_encrypt(Crypto1* crypto, uint8_t* keystream, const BitBuffer* buff, BitBuffer* out); + +void crypto1_encrypt_reader_nonce( + Crypto1* crypto, + uint64_t key, + uint32_t cuid, + uint8_t* nt, + uint8_t* nr, + BitBuffer* out, + bool is_nested); + +uint32_t prng_successor(uint32_t x, uint32_t n); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_classic/mf_classic.c b/lib/nfc/protocols/mf_classic/mf_classic.c new file mode 100644 index 00000000000..e68e8c71872 --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic.c @@ -0,0 +1,741 @@ +#include "mf_classic.h" + +#include +#include + +#include + +#define MF_CLASSIC_PROTOCOL_NAME "Mifare Classic" + +typedef struct { + uint8_t sectors_total; + uint16_t blocks_total; + const char* full_name; + const char* type_name; +} MfClassicFeatures; + +static const uint32_t mf_classic_data_format_version = 2; + +static const MfClassicFeatures mf_classic_features[MfClassicTypeNum] = { + [MfClassicTypeMini] = + { + .sectors_total = 5, + .blocks_total = 20, + .full_name = "Mifare Classic Mini 0.3K", + .type_name = "MINI", + }, + [MfClassicType1k] = + { + .sectors_total = 16, + .blocks_total = 64, + .full_name = "Mifare Classic 1K", + .type_name = "1K", + }, + [MfClassicType4k] = + { + .sectors_total = 40, + .blocks_total = 256, + .full_name = "Mifare Classic 4K", + .type_name = "4K", + }, +}; + +const NfcDeviceBase nfc_device_mf_classic = { + .protocol_name = MF_CLASSIC_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)mf_classic_alloc, + .free = (NfcDeviceFree)mf_classic_free, + .reset = (NfcDeviceReset)mf_classic_reset, + .copy = (NfcDeviceCopy)mf_classic_copy, + .verify = (NfcDeviceVerify)mf_classic_verify, + .load = (NfcDeviceLoad)mf_classic_load, + .save = (NfcDeviceSave)mf_classic_save, + .is_equal = (NfcDeviceEqual)mf_classic_is_equal, + .get_name = (NfcDeviceGetName)mf_classic_get_device_name, + .get_uid = (NfcDeviceGetUid)mf_classic_get_uid, + .set_uid = (NfcDeviceSetUid)mf_classic_set_uid, + .get_base_data = (NfcDeviceGetBaseData)mf_classic_get_base_data, +}; + +MfClassicData* mf_classic_alloc() { + MfClassicData* data = malloc(sizeof(MfClassicData)); + data->iso14443_3a_data = iso14443_3a_alloc(); + return data; +} + +void mf_classic_free(MfClassicData* data) { + furi_assert(data); + + iso14443_3a_free(data->iso14443_3a_data); + free(data); +} + +void mf_classic_reset(MfClassicData* data) { + furi_assert(data); + + iso14443_3a_reset(data->iso14443_3a_data); +} + +void mf_classic_copy(MfClassicData* data, const MfClassicData* other) { + furi_assert(data); + furi_assert(other); + + iso14443_3a_copy(data->iso14443_3a_data, other->iso14443_3a_data); + for(size_t i = 0; i < COUNT_OF(data->block); i++) { + data->block[i] = other->block[i]; + } + for(size_t i = 0; i < COUNT_OF(data->block_read_mask); i++) { + data->block_read_mask[i] = other->block_read_mask[i]; + } + data->type = other->type; + data->key_a_mask = other->key_a_mask; + data->key_b_mask = other->key_b_mask; +} + +bool mf_classic_verify(MfClassicData* data, const FuriString* device_type) { + UNUSED(data); + return furi_string_equal_str(device_type, "Mifare Classic"); +} + +static void mf_classic_parse_block(FuriString* block_str, MfClassicData* data, uint8_t block_num) { + furi_string_trim(block_str); + MfClassicBlock block_tmp = {}; + bool is_sector_trailer = mf_classic_is_sector_trailer(block_num); + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + uint16_t block_unknown_bytes_mask = 0; + + furi_string_trim(block_str); + for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) { + char hi = furi_string_get_char(block_str, 3 * i); + char low = furi_string_get_char(block_str, 3 * i + 1); + uint8_t byte = 0; + if(hex_char_to_uint8(hi, low, &byte)) { + block_tmp.data[i] = byte; + } else { + FURI_BIT_SET(block_unknown_bytes_mask, i); + } + } + + if(block_unknown_bytes_mask != 0xffff) { + if(is_sector_trailer) { + MfClassicSectorTrailer* sec_tr_tmp = (MfClassicSectorTrailer*)&block_tmp; + // Load Key A + // Key A mask 0b0000000000111111 = 0x003f + if((block_unknown_bytes_mask & 0x003f) == 0) { + uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_a.data, sizeof(MfClassicKey)); + mf_classic_set_key_found(data, sector_num, MfClassicKeyTypeA, key); + } + // Load Access Bits + // Access bits mask 0b0000001111000000 = 0x03c0 + if((block_unknown_bytes_mask & 0x03c0) == 0) { + mf_classic_set_block_read(data, block_num, &block_tmp); + } + // Load Key B + // Key B mask 0b1111110000000000 = 0xfc00 + if((block_unknown_bytes_mask & 0xfc00) == 0) { + uint64_t key = nfc_util_bytes2num(sec_tr_tmp->key_b.data, sizeof(MfClassicKey)); + mf_classic_set_key_found(data, sector_num, MfClassicKeyTypeB, key); + } + } else { + if(block_unknown_bytes_mask == 0) { + mf_classic_set_block_read(data, block_num, &block_tmp); + } + } + } +} + +bool mf_classic_load(MfClassicData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + + FuriString* temp_str = furi_string_alloc(); + bool parsed = false; + + do { + // Read ISO14443_3A data + if(!iso14443_3a_load(data->iso14443_3a_data, ff, version)) break; + + // Read Mifare Classic type + if(!flipper_format_read_string(ff, "Mifare Classic type", temp_str)) break; + bool type_parsed = false; + for(size_t i = 0; i < MfClassicTypeNum; i++) { + if(furi_string_equal_str(temp_str, mf_classic_features[i].type_name)) { + data->type = i; + type_parsed = true; + } + } + if(!type_parsed) break; + + // Read format version + uint32_t data_format_version = 0; + bool old_format = false; + // Read Mifare Classic format version + if(!flipper_format_read_uint32(ff, "Data format version", &data_format_version, 1)) { + // Load unread sectors with zero keys access for backward compatibility + if(!flipper_format_rewind(ff)) break; + old_format = true; + } else { + if(data_format_version < mf_classic_data_format_version) { + old_format = true; + } + } + + // Read Mifare Classic blocks + bool block_read = true; + FuriString* block_str = furi_string_alloc(); + uint16_t blocks_total = mf_classic_get_total_block_num(data->type); + for(size_t i = 0; i < blocks_total; i++) { + furi_string_printf(temp_str, "Block %d", i); + if(!flipper_format_read_string(ff, furi_string_get_cstr(temp_str), block_str)) { + block_read = false; + break; + } + mf_classic_parse_block(block_str, data, i); + } + furi_string_free(block_str); + if(!block_read) break; + + // Set keys and blocks as unknown for backward compatibility + if(old_format) { + data->key_a_mask = 0ULL; + data->key_b_mask = 0ULL; + memset(data->block_read_mask, 0, sizeof(data->block_read_mask)); + } + + parsed = true; + } while(false); + + furi_string_free(temp_str); + + return parsed; +} + +static void + mf_classic_set_block_str(FuriString* block_str, const MfClassicData* data, uint8_t block_num) { + furi_string_reset(block_str); + 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); + // Write key A + for(size_t i = 0; i < sizeof(sec_tr->key_a); i++) { + if(mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeA)) { + furi_string_cat_printf(block_str, "%02X ", sec_tr->key_a.data[i]); + } else { + furi_string_cat_printf(block_str, "?? "); + } + } + // Write Access bytes + for(size_t i = 0; i < MF_CLASSIC_ACCESS_BYTES_SIZE; i++) { + if(mf_classic_is_block_read(data, block_num)) { + furi_string_cat_printf(block_str, "%02X ", sec_tr->access_bits.data[i]); + } else { + furi_string_cat_printf(block_str, "?? "); + } + } + // Write key B + for(size_t i = 0; i < sizeof(sec_tr->key_b); i++) { + if(mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeB)) { + furi_string_cat_printf(block_str, "%02X ", sec_tr->key_b.data[i]); + } else { + furi_string_cat_printf(block_str, "?? "); + } + } + } else { + // Write data block + for(size_t i = 0; i < MF_CLASSIC_BLOCK_SIZE; i++) { + if(mf_classic_is_block_read(data, block_num)) { + furi_string_cat_printf(block_str, "%02X ", data->block[block_num].data[i]); + } else { + furi_string_cat_printf(block_str, "?? "); + } + } + } + furi_string_trim(block_str); +} + +bool mf_classic_save(const MfClassicData* data, FlipperFormat* ff) { + furi_assert(data); + + FuriString* temp_str = furi_string_alloc(); + bool saved = false; + + do { + if(!iso14443_3a_save(data->iso14443_3a_data, ff)) break; + + if(!flipper_format_write_comment_cstr(ff, "Mifare Classic specific data")) break; + if(!flipper_format_write_string_cstr( + ff, "Mifare Classic type", mf_classic_features[data->type].type_name)) + break; + if(!flipper_format_write_uint32( + ff, "Data format version", &mf_classic_data_format_version, 1)) + break; + if(!flipper_format_write_comment_cstr( + ff, "Mifare Classic blocks, \'??\' means unknown data")) + break; + + uint16_t blocks_total = mf_classic_get_total_block_num(data->type); + FuriString* block_str = furi_string_alloc(); + bool block_saved = true; + for(size_t i = 0; i < blocks_total; i++) { + furi_string_printf(temp_str, "Block %d", i); + mf_classic_set_block_str(block_str, data, i); + if(!flipper_format_write_string(ff, furi_string_get_cstr(temp_str), block_str)) { + block_saved = false; + break; + } + } + furi_string_free(block_str); + if(!block_saved) break; + + saved = true; + } while(false); + + furi_string_free(temp_str); + + return saved; +} + +bool mf_classic_is_equal(const MfClassicData* data, const MfClassicData* other) { + bool is_equal = false; + bool data_array_is_equal = true; + + do { + if(!iso14443_3a_is_equal(data->iso14443_3a_data, other->iso14443_3a_data)) break; + if(data->type != other->type) break; + if(data->key_a_mask != other->key_a_mask) break; + if(data->key_b_mask != other->key_b_mask) break; + + for(size_t i = 0; i < COUNT_OF(data->block_read_mask); i++) { + if(data->block_read_mask[i] != other->block_read_mask[i]) { + data_array_is_equal = false; + break; + } + } + if(!data_array_is_equal) break; + + for(size_t i = 0; i < COUNT_OF(data->block); i++) { + if(memcmp(&data->block[i], &other->block[i], sizeof(data->block[i])) != 0) { + data_array_is_equal = false; + break; + } + } + if(!data_array_is_equal) break; + + is_equal = true; + } while(false); + + return is_equal; +} + +const char* mf_classic_get_device_name(const MfClassicData* data, NfcDeviceNameType name_type) { + furi_assert(data); + furi_assert(data->type < MfClassicTypeNum); + + if(name_type == NfcDeviceNameTypeFull) { + return mf_classic_features[data->type].full_name; + } else { + return mf_classic_features[data->type].type_name; + } +} + +const uint8_t* mf_classic_get_uid(const MfClassicData* data, size_t* uid_len) { + furi_assert(data); + + return iso14443_3a_get_uid(data->iso14443_3a_data, uid_len); +} + +bool mf_classic_set_uid(MfClassicData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + return iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len); +} + +Iso14443_3aData* mf_classic_get_base_data(const MfClassicData* data) { + furi_assert(data); + + return data->iso14443_3a_data; +} + +uint8_t mf_classic_get_total_sectors_num(MfClassicType type) { + return mf_classic_features[type].sectors_total; +} + +uint16_t mf_classic_get_total_block_num(MfClassicType type) { + return mf_classic_features[type].blocks_total; +} + +uint8_t mf_classic_get_sector_trailer_num_by_sector(uint8_t sector) { + uint8_t block_num = 0; + + if(sector < 32) { + block_num = sector * 4 + 3; + } else if(sector < 40) { + block_num = 32 * 4 + (sector - 32) * 16 + 15; + } else { + furi_crash("Wrong sector num"); + } + + return block_num; +} + +uint8_t mf_classic_get_sector_trailer_num_by_block(uint8_t block) { + uint8_t sec_tr_block_num = 0; + + if(block < 128) { + sec_tr_block_num = block | 0x03; + } else { + sec_tr_block_num = block | 0x0f; + } + + return sec_tr_block_num; +} + +MfClassicSectorTrailer* + mf_classic_get_sector_trailer_by_sector(const MfClassicData* data, uint8_t sector_num) { + furi_assert(data); + + uint8_t sec_tr_block = mf_classic_get_sector_trailer_num_by_sector(sector_num); + MfClassicSectorTrailer* sec_trailer = (MfClassicSectorTrailer*)&data->block[sec_tr_block]; + + return sec_trailer; +} + +bool mf_classic_is_sector_trailer(uint8_t block) { + return block == mf_classic_get_sector_trailer_num_by_block(block); +} + +uint8_t mf_classic_get_sector_by_block(uint8_t block) { + uint8_t sector = 0; + + if(block < 128) { + sector = (block | 0x03) / 4; + } else { + sector = 32 + ((block | 0x0f) - 32 * 4) / 16; + } + + return sector; +} + +bool mf_classic_block_to_value(const MfClassicBlock* block, int32_t* value, uint8_t* addr) { + furi_assert(block); + + uint32_t v = *(uint32_t*)&block->data[0]; + uint32_t v_inv = *(uint32_t*)&block->data[sizeof(uint32_t)]; + uint32_t v1 = *(uint32_t*)&block->data[sizeof(uint32_t) * 2]; + + bool val_checks = + ((v == v1) && (v == ~v_inv) && (block->data[12] == (~block->data[13] & 0xFF)) && + (block->data[14] == (~block->data[15] & 0xFF)) && (block->data[12] == block->data[14])); + if(value) { + *value = (int32_t)v; + } + if(addr) { + *addr = block->data[12]; + } + return val_checks; +} + +void mf_classic_value_to_block(int32_t value, uint8_t addr, MfClassicBlock* block) { + furi_assert(block); + + uint32_t v_inv = ~((uint32_t)value); + + memcpy(&block->data[0], &value, 4); //-V1086 + memcpy(&block->data[4], &v_inv, 4); //-V1086 + memcpy(&block->data[8], &value, 4); //-V1086 + + block->data[12] = addr; + block->data[13] = ~addr & 0xFF; + block->data[14] = addr; + block->data[15] = ~addr & 0xFF; +} + +bool mf_classic_is_key_found( + const MfClassicData* data, + uint8_t sector_num, + MfClassicKeyType key_type) { + furi_assert(data); + + bool key_found = false; + if(key_type == MfClassicKeyTypeA) { + key_found = (FURI_BIT(data->key_a_mask, sector_num) == 1); + } else if(key_type == MfClassicKeyTypeB) { + key_found = (FURI_BIT(data->key_b_mask, sector_num) == 1); + } + + return key_found; +} + +void mf_classic_set_key_found( + MfClassicData* data, + uint8_t sector_num, + MfClassicKeyType key_type, + uint64_t key) { + furi_assert(data); + + uint8_t key_arr[6] = {}; + MfClassicSectorTrailer* sec_trailer = + mf_classic_get_sector_trailer_by_sector(data, sector_num); + nfc_util_num2bytes(key, 6, key_arr); + if(key_type == MfClassicKeyTypeA) { + memcpy(sec_trailer->key_a.data, key_arr, sizeof(MfClassicKey)); + FURI_BIT_SET(data->key_a_mask, sector_num); + } else if(key_type == MfClassicKeyTypeB) { + memcpy(sec_trailer->key_b.data, key_arr, sizeof(MfClassicKey)); + FURI_BIT_SET(data->key_b_mask, sector_num); + } +} + +void mf_classic_set_key_not_found( + MfClassicData* data, + uint8_t sector_num, + MfClassicKeyType key_type) { + furi_assert(data); + + if(key_type == MfClassicKeyTypeA) { + FURI_BIT_CLEAR(data->key_a_mask, sector_num); + } else if(key_type == MfClassicKeyTypeB) { + FURI_BIT_CLEAR(data->key_b_mask, sector_num); + } +} + +bool mf_classic_is_block_read(const MfClassicData* data, uint8_t block_num) { + furi_assert(data); + + return (FURI_BIT(data->block_read_mask[block_num / 32], block_num % 32) == 1); +} + +void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data) { + furi_assert(data); + + if(mf_classic_is_sector_trailer(block_num)) { + memcpy(&data->block[block_num].data[6], &block_data->data[6], 4); + } else { + memcpy(data->block[block_num].data, block_data->data, MF_CLASSIC_BLOCK_SIZE); + } + FURI_BIT_SET(data->block_read_mask[block_num / 32], block_num % 32); +} + +uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector) { + furi_assert(sector < 40); + + uint8_t block = 0; + if(sector < 32) { + block = sector * 4; + } else { + block = 32 * 4 + (sector - 32) * 16; + } + + return block; +} + +uint8_t mf_classic_get_blocks_num_in_sector(uint8_t sector) { + furi_assert(sector < 40); + return sector < 32 ? 4 : 16; +} + +void mf_classic_get_read_sectors_and_keys( + const MfClassicData* data, + uint8_t* sectors_read, + uint8_t* keys_found) { + furi_assert(data); + furi_assert(sectors_read); + furi_assert(keys_found); + + *sectors_read = 0; + *keys_found = 0; + uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); + for(size_t i = 0; i < sectors_total; i++) { + if(mf_classic_is_key_found(data, i, MfClassicKeyTypeA)) { + *keys_found += 1; + } + if(mf_classic_is_key_found(data, i, MfClassicKeyTypeB)) { + *keys_found += 1; + } + uint8_t first_block = mf_classic_get_first_block_num_of_sector(i); + uint8_t total_blocks_in_sec = mf_classic_get_blocks_num_in_sector(i); + bool blocks_read = true; + for(size_t j = first_block; j < first_block + total_blocks_in_sec; j++) { + blocks_read = mf_classic_is_block_read(data, j); + if(!blocks_read) break; + } + if(blocks_read) { + *sectors_read += 1; + } + } +} + +bool mf_classic_is_card_read(const MfClassicData* data) { + furi_assert(data); + + uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); + uint8_t sectors_read = 0; + uint8_t keys_found = 0; + mf_classic_get_read_sectors_and_keys(data, §ors_read, &keys_found); + bool card_read = (sectors_read == sectors_total) && (keys_found == sectors_total * 2); + + return card_read; +} + +bool mf_classic_is_sector_read(const MfClassicData* data, uint8_t sector_num) { + furi_assert(data); + + bool sector_read = false; + do { + if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeA)) break; + if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyTypeB)) break; + uint8_t start_block = mf_classic_get_first_block_num_of_sector(sector_num); + uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sector_num); + uint8_t block_read = true; + for(size_t i = start_block; i < start_block + total_blocks; i++) { + block_read = mf_classic_is_block_read(data, i); + if(!block_read) break; + } + sector_read = block_read; + } while(false); + + return sector_read; +} + +static bool mf_classic_is_allowed_access_sector_trailer( + MfClassicData* data, + uint8_t block_num, + MfClassicKeyType key_type, + MfClassicAction action) { + 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); + uint8_t* access_bits_arr = sec_tr->access_bits.data; + uint8_t AC = ((access_bits_arr[1] >> 5) & 0x04) | ((access_bits_arr[2] >> 2) & 0x02) | + ((access_bits_arr[2] >> 7) & 0x01); + FURI_LOG_T("NFC", "AC: %02X", AC); + + switch(action) { + case MfClassicActionKeyARead: { + return false; + } + case MfClassicActionKeyAWrite: + case MfClassicActionKeyBWrite: { + return ( + (key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x01)) || + (key_type == MfClassicKeyTypeB && + (AC == 0x00 || AC == 0x04 || AC == 0x03 || AC == 0x01))); + } + case MfClassicActionKeyBRead: { + return (key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x02 || AC == 0x01)) || + (key_type == MfClassicKeyTypeB && (AC == 0x00 || AC == 0x02 || AC == 0x01)); + } + case MfClassicActionACRead: { + return ((key_type == MfClassicKeyTypeA) || (key_type == MfClassicKeyTypeB)); + } + case MfClassicActionACWrite: { + return ( + (key_type == MfClassicKeyTypeA && (AC == 0x01)) || + (key_type == MfClassicKeyTypeB && (AC == 0x01 || AC == 0x03 || AC == 0x05))); + } + default: + return false; + } + return true; +} + +bool mf_classic_is_allowed_access_data_block( + MfClassicSectorTrailer* sec_tr, + uint8_t block_num, + MfClassicKeyType key_type, + MfClassicAction action) { + furi_assert(sec_tr); + + uint8_t* access_bits_arr = sec_tr->access_bits.data; + + if(block_num == 0 && action == MfClassicActionDataWrite) { + return false; + } + + uint8_t sector_block = 0; + if(block_num <= 128) { + sector_block = block_num & 0x03; + } else { + sector_block = (block_num & 0x0f) / 5; + } + + uint8_t AC; + switch(sector_block) { + case 0x00: { + AC = ((access_bits_arr[1] >> 2) & 0x04) | ((access_bits_arr[2] << 1) & 0x02) | + ((access_bits_arr[2] >> 4) & 0x01); + break; + } + case 0x01: { + AC = ((access_bits_arr[1] >> 3) & 0x04) | ((access_bits_arr[2] >> 0) & 0x02) | + ((access_bits_arr[2] >> 5) & 0x01); + break; + } + case 0x02: { + AC = ((access_bits_arr[1] >> 4) & 0x04) | ((access_bits_arr[2] >> 1) & 0x02) | + ((access_bits_arr[2] >> 6) & 0x01); + break; + } + default: + return false; + } + + switch(action) { + case MfClassicActionDataRead: { + return ( + (key_type == MfClassicKeyTypeA && !(AC == 0x03 || AC == 0x05 || AC == 0x07)) || + (key_type == MfClassicKeyTypeB && !(AC == 0x07))); + } + case MfClassicActionDataWrite: { + return ( + (key_type == MfClassicKeyTypeA && (AC == 0x00)) || + (key_type == MfClassicKeyTypeB && + (AC == 0x00 || AC == 0x04 || AC == 0x06 || AC == 0x03))); + } + case MfClassicActionDataInc: { + return ( + (key_type == MfClassicKeyTypeA && (AC == 0x00)) || + (key_type == MfClassicKeyTypeB && (AC == 0x00 || AC == 0x06))); + } + case MfClassicActionDataDec: { + return ( + (key_type == MfClassicKeyTypeA && (AC == 0x00 || AC == 0x06 || AC == 0x01)) || + (key_type == MfClassicKeyTypeB && (AC == 0x00 || AC == 0x06 || AC == 0x01))); + } + default: + return false; + } + + return false; +} + +bool mf_classic_is_allowed_access( + MfClassicData* data, + uint8_t block_num, + MfClassicKeyType key_type, + MfClassicAction action) { + furi_assert(data); + + bool access_allowed = false; + if(mf_classic_is_sector_trailer(block_num)) { + access_allowed = + mf_classic_is_allowed_access_sector_trailer(data, block_num, key_type, action); + } else { + 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); + access_allowed = + mf_classic_is_allowed_access_data_block(sec_tr, block_num, key_type, action); + } + + return access_allowed; +} + +bool mf_classic_is_value_block(MfClassicSectorTrailer* sec_tr, uint8_t block_num) { + furi_assert(sec_tr); + + // Check if key A can write, if it can, it's transport configuration, not data block + return !mf_classic_is_allowed_access_data_block( + sec_tr, block_num, MfClassicKeyTypeA, MfClassicActionDataWrite) && + (mf_classic_is_allowed_access_data_block( + sec_tr, block_num, MfClassicKeyTypeB, MfClassicActionDataInc) || + mf_classic_is_allowed_access_data_block( + sec_tr, block_num, MfClassicKeyTypeB, MfClassicActionDataDec)); +} diff --git a/lib/nfc/protocols/mf_classic/mf_classic.h b/lib/nfc/protocols/mf_classic/mf_classic.h new file mode 100644 index 00000000000..146e6a6f157 --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic.h @@ -0,0 +1,237 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MF_CLASSIC_CMD_AUTH_KEY_A (0x60U) +#define MF_CLASSIC_CMD_AUTH_KEY_B (0x61U) +#define MF_CLASSIC_CMD_READ_BLOCK (0x30U) +#define MF_CLASSIC_CMD_WRITE_BLOCK (0xA0U) +#define MF_CLASSIC_CMD_VALUE_DEC (0xC0U) +#define MF_CLASSIC_CMD_VALUE_INC (0xC1U) +#define MF_CLASSIC_CMD_VALUE_RESTORE (0xC2U) +#define MF_CLASSIC_CMD_VALUE_TRANSFER (0xB0U) + +#define MF_CLASSIC_CMD_HALT_MSB (0x50) +#define MF_CLASSIC_CMD_HALT_LSB (0x00) +#define MF_CLASSIC_CMD_ACK (0x0A) +#define MF_CLASSIC_CMD_NACK (0x00) +#define MF_CLASSIC_CMD_NACK_TRANSFER_INVALID (0x04) +#define MF_CLASSIC_CMD_NACK_TRANSFER_CRC_ERROR (0x01) + +#define MF_CLASSIC_TOTAL_SECTORS_MAX (40) +#define MF_CLASSIC_TOTAL_BLOCKS_MAX (256) +#define MF_CLASSIC_READ_MASK_SIZE (MF_CLASSIC_TOTAL_BLOCKS_MAX / 32) +#define MF_CLASSIC_BLOCK_SIZE (16) +#define MF_CLASSIC_KEY_SIZE (6) +#define MF_CLASSIC_ACCESS_BYTES_SIZE (4) + +#define MF_CLASSIC_NT_SIZE (4) +#define MF_CLASSIC_NR_SIZE (4) +#define MF_CLASSIC_AR_SIZE (4) +#define MF_CLASSIC_AT_SIZE (4) + +typedef enum { + MfClassicErrorNone, + MfClassicErrorNotPresent, + MfClassicErrorProtocol, + MfClassicErrorAuth, + MfClassicErrorTimeout, +} MfClassicError; + +typedef enum { + MfClassicTypeMini, + MfClassicType1k, + MfClassicType4k, + + MfClassicTypeNum, +} MfClassicType; + +typedef enum { + MfClassicActionDataRead, + MfClassicActionDataWrite, + MfClassicActionDataInc, + MfClassicActionDataDec, + + MfClassicActionKeyARead, + MfClassicActionKeyAWrite, + MfClassicActionKeyBRead, + MfClassicActionKeyBWrite, + MfClassicActionACRead, + MfClassicActionACWrite, +} MfClassicAction; + +typedef enum { + MfClassicValueCommandIncrement, + MfClassicValueCommandDecrement, + MfClassicValueCommandRestore, + + MfClassicValueCommandInvalid, +} MfClassicValueCommand; + +typedef struct { + uint8_t data[MF_CLASSIC_BLOCK_SIZE]; +} MfClassicBlock; + +typedef enum { + MfClassicKeyTypeA, + MfClassicKeyTypeB, +} MfClassicKeyType; + +typedef struct { + uint8_t data[MF_CLASSIC_KEY_SIZE]; +} MfClassicKey; + +typedef struct { + uint8_t data[MF_CLASSIC_ACCESS_BYTES_SIZE]; +} MfClassicAccessBits; + +typedef struct { + uint8_t data[MF_CLASSIC_NT_SIZE]; +} MfClassicNt; + +typedef struct { + uint8_t data[MF_CLASSIC_AT_SIZE]; +} MfClassicAt; + +typedef struct { + uint8_t data[MF_CLASSIC_NR_SIZE]; +} MfClassicNr; + +typedef struct { + uint8_t data[MF_CLASSIC_AR_SIZE]; +} MfClassicAr; + +typedef struct { + uint8_t block_num; + MfClassicKey key; + MfClassicKeyType key_type; + MfClassicNt nt; + MfClassicNr nr; + MfClassicAr ar; + MfClassicAt at; +} MfClassicAuthContext; + +typedef union { + MfClassicBlock block; + struct { + MfClassicKey key_a; + MfClassicAccessBits access_bits; + MfClassicKey key_b; + }; +} MfClassicSectorTrailer; + +typedef struct { + uint64_t key_a_mask; + MfClassicKey key_a[MF_CLASSIC_TOTAL_SECTORS_MAX]; + uint64_t key_b_mask; + MfClassicKey key_b[MF_CLASSIC_TOTAL_SECTORS_MAX]; +} MfClassicDeviceKeys; + +typedef struct { + Iso14443_3aData* iso14443_3a_data; + MfClassicType type; + uint32_t block_read_mask[MF_CLASSIC_READ_MASK_SIZE]; + uint64_t key_a_mask; + uint64_t key_b_mask; + MfClassicBlock block[MF_CLASSIC_TOTAL_BLOCKS_MAX]; +} MfClassicData; + +extern const NfcDeviceBase nfc_device_mf_classic; + +MfClassicData* mf_classic_alloc(); + +void mf_classic_free(MfClassicData* data); + +void mf_classic_reset(MfClassicData* data); + +void mf_classic_copy(MfClassicData* data, const MfClassicData* other); + +bool mf_classic_verify(MfClassicData* data, const FuriString* device_type); + +bool mf_classic_load(MfClassicData* data, FlipperFormat* ff, uint32_t version); + +bool mf_classic_save(const MfClassicData* data, FlipperFormat* ff); + +bool mf_classic_is_equal(const MfClassicData* data, const MfClassicData* other); + +const char* mf_classic_get_device_name(const MfClassicData* data, NfcDeviceNameType name_type); + +const uint8_t* mf_classic_get_uid(const MfClassicData* data, size_t* uid_len); + +bool mf_classic_set_uid(MfClassicData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_3aData* mf_classic_get_base_data(const MfClassicData* data); + +uint8_t mf_classic_get_total_sectors_num(MfClassicType type); + +uint16_t mf_classic_get_total_block_num(MfClassicType type); + +uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector); + +uint8_t mf_classic_get_blocks_num_in_sector(uint8_t sector); + +uint8_t mf_classic_get_sector_trailer_num_by_sector(uint8_t sector); + +uint8_t mf_classic_get_sector_trailer_num_by_block(uint8_t block); + +MfClassicSectorTrailer* + mf_classic_get_sector_trailer_by_sector(const MfClassicData* data, uint8_t sector_num); + +bool mf_classic_is_sector_trailer(uint8_t block); + +uint8_t mf_classic_get_sector_by_block(uint8_t block); + +bool mf_classic_block_to_value(const MfClassicBlock* block, int32_t* value, uint8_t* addr); + +void mf_classic_value_to_block(int32_t value, uint8_t addr, MfClassicBlock* block); + +bool mf_classic_is_key_found( + const MfClassicData* data, + uint8_t sector_num, + MfClassicKeyType key_type); + +void mf_classic_set_key_found( + MfClassicData* data, + uint8_t sector_num, + MfClassicKeyType key_type, + uint64_t key); + +void mf_classic_set_key_not_found( + MfClassicData* data, + uint8_t sector_num, + MfClassicKeyType key_type); + +bool mf_classic_is_block_read(const MfClassicData* data, uint8_t block_num); + +void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data); + +bool mf_classic_is_sector_read(const MfClassicData* data, uint8_t sector_num); + +void mf_classic_get_read_sectors_and_keys( + const MfClassicData* data, + uint8_t* sectors_read, + uint8_t* keys_found); + +bool mf_classic_is_card_read(const MfClassicData* data); + +bool mf_classic_is_value_block(MfClassicSectorTrailer* sec_tr, uint8_t block_num); + +bool mf_classic_is_allowed_access_data_block( + MfClassicSectorTrailer* sec_tr, + uint8_t block_num, + MfClassicKeyType key_type, + MfClassicAction action); + +bool mf_classic_is_allowed_access( + MfClassicData* data, + uint8_t block_num, + MfClassicKeyType key_type, + MfClassicAction action); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_classic/mf_classic_listener.c b/lib/nfc/protocols/mf_classic/mf_classic_listener.c new file mode 100644 index 00000000000..fb12ba8a95c --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_listener.c @@ -0,0 +1,677 @@ +#include "mf_classic_listener_i.h" + +#include + +#include +#include + +#include +#include + +#define TAG "MfClassicListener" + +#define MF_CLASSIC_MAX_BUFF_SIZE (64) + +typedef MfClassicListenerCommand ( + *MfClassicListenerCommandHandler)(MfClassicListener* instance, BitBuffer* buf); + +typedef struct { + uint8_t cmd_start_byte; + size_t cmd_len_bits; + size_t command_num; + MfClassicListenerCommandHandler* handler; +} MfClassicListenerCmd; + +static void mf_classic_listener_prepare_emulation(MfClassicListener* instance) { + instance->total_block_num = mf_classic_get_total_block_num(instance->data->type); +} + +static void mf_classic_listener_reset_state(MfClassicListener* instance) { + crypto1_reset(instance->crypto); + memset(&instance->auth_context, 0, sizeof(MfClassicAuthContext)); + instance->comm_state = MfClassicListenerCommStatePlain; + instance->state = MfClassicListenerStateIdle; + instance->cmd_in_progress = false; + instance->current_cmd_handler_idx = 0; + instance->transfer_value = 0; + instance->transfer_valid = false; + instance->value_cmd = MfClassicValueCommandInvalid; +} + +static MfClassicListenerCommand + mf_classic_listener_halt_handler(MfClassicListener* instance, BitBuffer* buff) { + MfClassicListenerCommand command = MfClassicListenerCommandNack; + + if(bit_buffer_get_byte(buff, 1) == MF_CLASSIC_CMD_HALT_LSB) { + mf_classic_listener_reset_state(instance); + command = MfClassicListenerCommandSleep; + } + + return command; +} + +static MfClassicListenerCommand mf_classic_listener_auth_first_part_handler( + MfClassicListener* instance, + MfClassicKeyType key_type, + uint8_t block_num) { + MfClassicListenerCommand command = MfClassicListenerCommandNack; + + do { + instance->state = MfClassicListenerStateIdle; + + if(block_num >= instance->total_block_num) { + mf_classic_listener_reset_state(instance); + break; + } + + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + + MfClassicSectorTrailer* sec_tr = + mf_classic_get_sector_trailer_by_sector(instance->data, sector_num); + MfClassicKey* key = (key_type == MfClassicKeyTypeA) ? &sec_tr->key_a : &sec_tr->key_b; + uint64_t key_num = nfc_util_bytes2num(key->data, sizeof(MfClassicKey)); + uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data); + + instance->auth_context.key_type = key_type; + instance->auth_context.block_num = block_num; + + furi_hal_random_fill_buf(instance->auth_context.nt.data, sizeof(MfClassicNt)); + uint32_t nt_num = nfc_util_bytes2num(instance->auth_context.nt.data, sizeof(MfClassicNt)); + + crypto1_init(instance->crypto, key_num); + if(instance->comm_state == MfClassicListenerCommStatePlain) { + crypto1_word(instance->crypto, nt_num ^ cuid, 0); + bit_buffer_copy_bytes( + instance->tx_encrypted_buffer, + instance->auth_context.nt.data, + sizeof(MfClassicNt)); + iso14443_3a_listener_tx(instance->iso14443_3a_listener, instance->tx_encrypted_buffer); + command = MfClassicListenerCommandProcessed; + } else { + uint8_t key_stream[4] = {}; + nfc_util_num2bytes(nt_num ^ cuid, sizeof(uint32_t), key_stream); + bit_buffer_copy_bytes( + instance->tx_plain_buffer, instance->auth_context.nt.data, sizeof(MfClassicNt)); + crypto1_encrypt( + instance->crypto, + key_stream, + instance->tx_plain_buffer, + instance->tx_encrypted_buffer); + + iso14443_3a_listener_tx_with_custom_parity( + instance->iso14443_3a_listener, instance->tx_encrypted_buffer); + + command = MfClassicListenerCommandProcessed; + } + + instance->cmd_in_progress = true; + instance->current_cmd_handler_idx++; + } while(false); + + return command; +} + +static MfClassicListenerCommand + mf_classic_listener_auth_key_a_handler(MfClassicListener* instance, BitBuffer* buff) { + MfClassicListenerCommand command = mf_classic_listener_auth_first_part_handler( + instance, MfClassicKeyTypeA, bit_buffer_get_byte(buff, 1)); + + return command; +} + +static MfClassicListenerCommand + mf_classic_listener_auth_key_b_handler(MfClassicListener* instance, BitBuffer* buff) { + MfClassicListenerCommand command = mf_classic_listener_auth_first_part_handler( + instance, MfClassicKeyTypeB, bit_buffer_get_byte(buff, 1)); + + return command; +} + +static MfClassicListenerCommand + mf_classic_listener_auth_second_part_handler(MfClassicListener* instance, BitBuffer* buff) { + MfClassicListenerCommand command = MfClassicListenerCommandSilent; + + do { + instance->cmd_in_progress = false; + + if(bit_buffer_get_size_bytes(buff) != (sizeof(MfClassicNr) + sizeof(MfClassicAr))) { + mf_classic_listener_reset_state(instance); + break; + } + bit_buffer_write_bytes_mid(buff, instance->auth_context.nr.data, 0, sizeof(MfClassicNr)); + bit_buffer_write_bytes_mid( + buff, instance->auth_context.ar.data, sizeof(MfClassicNr), sizeof(MfClassicAr)); + + if(instance->callback) { + instance->mfc_event.type = MfClassicListenerEventTypeAuthContextPartCollected, + instance->mfc_event_data.auth_context = instance->auth_context; + instance->callback(instance->generic_event, instance->context); + } + + uint32_t nr_num = nfc_util_bytes2num(instance->auth_context.nr.data, sizeof(MfClassicNr)); + uint32_t ar_num = nfc_util_bytes2num(instance->auth_context.ar.data, sizeof(MfClassicAr)); + + crypto1_word(instance->crypto, nr_num, 1); + uint32_t nt_num = nfc_util_bytes2num(instance->auth_context.nt.data, sizeof(MfClassicNt)); + uint32_t secret_poller = ar_num ^ crypto1_word(instance->crypto, 0, 0); + if(secret_poller != prng_successor(nt_num, 64)) { + FURI_LOG_T( + TAG, "Wrong reader key: %08lX != %08lX", secret_poller, prng_successor(nt_num, 64)); + mf_classic_listener_reset_state(instance); + break; + } + + uint32_t at_num = prng_successor(nt_num, 96); + nfc_util_num2bytes(at_num, sizeof(uint32_t), instance->auth_context.at.data); + bit_buffer_copy_bytes( + instance->tx_plain_buffer, instance->auth_context.at.data, sizeof(MfClassicAr)); + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + iso14443_3a_listener_tx_with_custom_parity( + instance->iso14443_3a_listener, instance->tx_encrypted_buffer); + + instance->state = MfClassicListenerStateAuthComplete; + instance->comm_state = MfClassicListenerCommStateEncrypted; + command = MfClassicListenerCommandProcessed; + + if(instance->callback) { + instance->mfc_event.type = MfClassicListenerEventTypeAuthContextFullCollected, + instance->mfc_event_data.auth_context = instance->auth_context; + instance->callback(instance->generic_event, instance->context); + } + } while(false); + + return command; +} + +static MfClassicListenerCommand + mf_classic_listener_read_block_handler(MfClassicListener* instance, BitBuffer* buff) { + MfClassicListenerCommand command = MfClassicListenerCommandNack; + MfClassicAuthContext* auth_ctx = &instance->auth_context; + + do { + if(instance->state != MfClassicListenerStateAuthComplete) break; + + uint8_t block_num = bit_buffer_get_byte(buff, 1); + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + uint8_t auth_sector_num = mf_classic_get_sector_by_block(auth_ctx->block_num); + if(sector_num != auth_sector_num) break; + + MfClassicBlock access_block = instance->data->block[block_num]; + + if(mf_classic_is_sector_trailer(block_num)) { + MfClassicSectorTrailer* access_sec_tr = (MfClassicSectorTrailer*)&access_block; + if(!mf_classic_is_allowed_access( + instance->data, block_num, auth_ctx->key_type, MfClassicActionKeyARead)) { + memset(access_sec_tr->key_a.data, 0, sizeof(MfClassicKey)); + } + if(!mf_classic_is_allowed_access( + instance->data, block_num, auth_ctx->key_type, MfClassicActionKeyBRead)) { + memset(access_sec_tr->key_b.data, 0, sizeof(MfClassicKey)); + } + if(!mf_classic_is_allowed_access( + instance->data, block_num, auth_ctx->key_type, MfClassicActionACRead)) { + memset(access_sec_tr->access_bits.data, 0, sizeof(MfClassicAccessBits)); + } + } else if(!mf_classic_is_allowed_access( + instance->data, block_num, auth_ctx->key_type, MfClassicActionDataRead)) { + break; + } + + bit_buffer_copy_bytes( + instance->tx_plain_buffer, access_block.data, sizeof(MfClassicBlock)); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer); + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + iso14443_3a_listener_tx_with_custom_parity( + instance->iso14443_3a_listener, instance->tx_encrypted_buffer); + command = MfClassicListenerCommandProcessed; + } while(false); + + return command; +} + +static MfClassicListenerCommand mf_classic_listener_write_block_first_part_handler( + MfClassicListener* instance, + BitBuffer* buff) { + MfClassicListenerCommand command = MfClassicListenerCommandNack; + MfClassicAuthContext* auth_ctx = &instance->auth_context; + + do { + if(instance->state != MfClassicListenerStateAuthComplete) break; + + uint8_t block_num = bit_buffer_get_byte(buff, 1); + if(block_num >= instance->total_block_num) break; + + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + uint8_t auth_sector_num = mf_classic_get_sector_by_block(auth_ctx->block_num); + if(sector_num != auth_sector_num) break; + + instance->cmd_in_progress = true; + instance->current_cmd_handler_idx++; + command = MfClassicListenerCommandAck; + } while(false); + + return command; +} + +static MfClassicListenerCommand mf_classic_listener_write_block_second_part_handler( + MfClassicListener* instance, + BitBuffer* buff) { + MfClassicListenerCommand command = MfClassicListenerCommandNack; + MfClassicAuthContext* auth_ctx = &instance->auth_context; + + do { + instance->cmd_in_progress = false; + + size_t buff_size = bit_buffer_get_size_bytes(buff); + if(buff_size != sizeof(MfClassicBlock)) break; + + uint8_t block_num = auth_ctx->block_num; + MfClassicKeyType key_type = auth_ctx->key_type; + MfClassicBlock block = instance->data->block[block_num]; + + if(mf_classic_is_sector_trailer(block_num)) { + MfClassicSectorTrailer* sec_tr = (MfClassicSectorTrailer*)█ + + // Check if any writing is allowed + if(!mf_classic_is_allowed_access( + instance->data, block_num, key_type, MfClassicActionKeyAWrite) && + !mf_classic_is_allowed_access( + instance->data, block_num, key_type, MfClassicActionKeyBWrite) && + !mf_classic_is_allowed_access( + instance->data, block_num, key_type, MfClassicActionACWrite)) { + break; + } + + if(mf_classic_is_allowed_access( + instance->data, block_num, key_type, MfClassicActionKeyAWrite)) { + bit_buffer_write_bytes_mid(buff, sec_tr->key_a.data, 0, sizeof(MfClassicKey)); + } + if(mf_classic_is_allowed_access( + instance->data, block_num, key_type, MfClassicActionKeyBWrite)) { + bit_buffer_write_bytes_mid(buff, sec_tr->key_b.data, 10, sizeof(MfClassicKey)); + } + if(mf_classic_is_allowed_access( + instance->data, block_num, key_type, MfClassicActionACWrite)) { + bit_buffer_write_bytes_mid( + buff, sec_tr->access_bits.data, 6, sizeof(MfClassicAccessBits)); + } + } else { + if(mf_classic_is_allowed_access( + instance->data, block_num, key_type, MfClassicActionDataWrite)) { + bit_buffer_write_bytes_mid(buff, block.data, 0, sizeof(MfClassicBlock)); + } else { + break; + } + } + + instance->data->block[block_num] = block; + command = MfClassicListenerCommandAck; + } while(false); + + return command; +} + +static MfClassicListenerCommand + mf_classic_listener_value_cmd_handler(MfClassicListener* instance, BitBuffer* buff) { + MfClassicListenerCommand command = MfClassicListenerCommandNack; + MfClassicAuthContext* auth_ctx = &instance->auth_context; + + do { + if(instance->state != MfClassicListenerStateAuthComplete) break; + + uint8_t block_num = bit_buffer_get_byte(buff, 1); + if(block_num >= instance->total_block_num) break; + + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + uint8_t auth_sector_num = mf_classic_get_sector_by_block(auth_ctx->block_num); + if(sector_num != auth_sector_num) break; + + uint8_t cmd = bit_buffer_get_byte(buff, 0); + MfClassicAction action = MfClassicActionDataDec; + if(cmd == MF_CLASSIC_CMD_VALUE_DEC) { + instance->value_cmd = MfClassicValueCommandDecrement; + } else if(cmd == MF_CLASSIC_CMD_VALUE_INC) { + instance->value_cmd = MfClassicValueCommandIncrement; + action = MfClassicActionDataInc; + } else if(cmd == MF_CLASSIC_CMD_VALUE_RESTORE) { + instance->value_cmd = MfClassicValueCommandRestore; + } else { + break; + } + + if(!mf_classic_is_allowed_access(instance->data, block_num, auth_ctx->key_type, action)) { + break; + } + + if(!mf_classic_block_to_value( + &instance->data->block[block_num], &instance->transfer_value, NULL)) { + break; + } + + instance->transfer_valid = true; + instance->cmd_in_progress = true; + instance->current_cmd_handler_idx++; + command = MfClassicListenerCommandAck; + } while(false); + + return command; +} + +static MfClassicListenerCommand + mf_classic_listener_value_dec_handler(MfClassicListener* instance, BitBuffer* buff) { + return mf_classic_listener_value_cmd_handler(instance, buff); +} + +static MfClassicListenerCommand + mf_classic_listener_value_inc_handler(MfClassicListener* instance, BitBuffer* buff) { + return mf_classic_listener_value_cmd_handler(instance, buff); +} + +static MfClassicListenerCommand + mf_classic_listener_value_restore_handler(MfClassicListener* instance, BitBuffer* buff) { + return mf_classic_listener_value_cmd_handler(instance, buff); +} + +static MfClassicListenerCommand + mf_classic_listener_value_data_receive_handler(MfClassicListener* instance, BitBuffer* buff) { + MfClassicListenerCommand command = MfClassicListenerCommandNack; + + do { + if(bit_buffer_get_size_bytes(buff) != 4) break; + + int32_t data; + bit_buffer_write_bytes_mid(buff, &data, 0, sizeof(data)); + + if(data < 0) { + data = -data; + } + + if(instance->value_cmd == MfClassicValueCommandDecrement) { + data = -data; + } else if(instance->value_cmd == MfClassicValueCommandRestore) { + data = 0; + } + + instance->transfer_value += data; + instance->transfer_valid = true; + + instance->cmd_in_progress = true; + instance->current_cmd_handler_idx++; + command = MfClassicListenerCommandSilent; + } while(false); + + return command; +} + +static MfClassicListenerCommand + mf_classic_listener_value_transfer_handler(MfClassicListener* instance, BitBuffer* buff) { + MfClassicListenerCommand command = MfClassicListenerCommandNack; + MfClassicAuthContext* auth_ctx = &instance->auth_context; + + do { + instance->cmd_in_progress = false; + + if(bit_buffer_get_size_bytes(buff) != 2) break; + if(bit_buffer_get_byte(buff, 0) != MF_CLASSIC_CMD_VALUE_TRANSFER) break; + + uint8_t block_num = bit_buffer_get_byte(buff, 1); + if(!mf_classic_is_allowed_access( + instance->data, block_num, auth_ctx->key_type, MfClassicActionDataDec)) { + break; + } + + mf_classic_value_to_block( + instance->transfer_value, block_num, &instance->data->block[block_num]); + instance->transfer_value = 0; + instance->transfer_valid = false; + + command = MfClassicListenerCommandAck; + } while(false); + + return command; +} + +static MfClassicListenerCommandHandler mf_classic_listener_halt_handlers[] = { + mf_classic_listener_halt_handler, +}; + +static MfClassicListenerCommandHandler mf_classic_listener_auth_key_a_handlers[] = { + mf_classic_listener_auth_key_a_handler, + mf_classic_listener_auth_second_part_handler, +}; + +static MfClassicListenerCommandHandler mf_classic_listener_auth_key_b_handlers[] = { + mf_classic_listener_auth_key_b_handler, + mf_classic_listener_auth_second_part_handler, +}; + +static MfClassicListenerCommandHandler mf_classic_listener_read_block_handlers[] = { + mf_classic_listener_read_block_handler, +}; + +static MfClassicListenerCommandHandler mf_classic_listener_write_block_handlers[] = { + mf_classic_listener_write_block_first_part_handler, + mf_classic_listener_write_block_second_part_handler, +}; + +static MfClassicListenerCommandHandler mf_classic_listener_value_dec_handlers[] = { + mf_classic_listener_value_dec_handler, + mf_classic_listener_value_data_receive_handler, + mf_classic_listener_value_transfer_handler, +}; + +static MfClassicListenerCommandHandler mf_classic_listener_value_inc_handlers[] = { + mf_classic_listener_value_inc_handler, + mf_classic_listener_value_data_receive_handler, + mf_classic_listener_value_transfer_handler, +}; + +static MfClassicListenerCommandHandler mf_classic_listener_value_restore_handlers[] = { + mf_classic_listener_value_restore_handler, + mf_classic_listener_value_data_receive_handler, + mf_classic_listener_value_transfer_handler, +}; + +static const MfClassicListenerCmd mf_classic_listener_cmd_handlers[] = { + { + .cmd_start_byte = MF_CLASSIC_CMD_HALT_MSB, + .cmd_len_bits = 2 * 8, + .command_num = COUNT_OF(mf_classic_listener_halt_handlers), + .handler = mf_classic_listener_halt_handlers, + }, + { + .cmd_start_byte = MF_CLASSIC_CMD_AUTH_KEY_A, + .cmd_len_bits = 2 * 8, + .command_num = COUNT_OF(mf_classic_listener_auth_key_a_handlers), + .handler = mf_classic_listener_auth_key_a_handlers, + }, + { + .cmd_start_byte = MF_CLASSIC_CMD_AUTH_KEY_B, + .cmd_len_bits = 2 * 8, + .command_num = COUNT_OF(mf_classic_listener_auth_key_b_handlers), + .handler = mf_classic_listener_auth_key_b_handlers, + }, + { + .cmd_start_byte = MF_CLASSIC_CMD_READ_BLOCK, + .cmd_len_bits = 2 * 8, + .command_num = COUNT_OF(mf_classic_listener_read_block_handlers), + .handler = mf_classic_listener_read_block_handlers, + }, + { + .cmd_start_byte = MF_CLASSIC_CMD_WRITE_BLOCK, + .cmd_len_bits = 2 * 8, + .command_num = COUNT_OF(mf_classic_listener_write_block_handlers), + .handler = mf_classic_listener_write_block_handlers, + }, + { + .cmd_start_byte = MF_CLASSIC_CMD_VALUE_DEC, + .cmd_len_bits = 2 * 8, + .command_num = COUNT_OF(mf_classic_listener_value_dec_handlers), + .handler = mf_classic_listener_value_dec_handlers, + }, + { + .cmd_start_byte = MF_CLASSIC_CMD_VALUE_INC, + .cmd_len_bits = 2 * 8, + .command_num = COUNT_OF(mf_classic_listener_value_inc_handlers), + .handler = mf_classic_listener_value_inc_handlers, + }, + { + .cmd_start_byte = MF_CLASSIC_CMD_VALUE_RESTORE, + .cmd_len_bits = 2 * 8, + .command_num = COUNT_OF(mf_classic_listener_value_restore_handlers), + .handler = mf_classic_listener_value_restore_handlers, + }, +}; + +static void mf_classic_listener_send_short_frame(MfClassicListener* instance, uint8_t data) { + BitBuffer* tx_buffer = instance->tx_plain_buffer; + + bit_buffer_set_size(instance->tx_plain_buffer, 4); + bit_buffer_set_byte(instance->tx_plain_buffer, 0, data); + if(instance->comm_state == MfClassicListenerCommStateEncrypted) { + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + tx_buffer = instance->tx_encrypted_buffer; + } + + iso14443_3a_listener_tx_with_custom_parity(instance->iso14443_3a_listener, tx_buffer); +} + +NfcCommand mf_classic_listener_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + + NfcCommand command = NfcCommandContinue; + MfClassicListener* instance = context; + Iso14443_3aListenerEvent* iso3_event = event.event_data; + BitBuffer* rx_buffer_plain; + + if(iso3_event->type == Iso14443_3aListenerEventTypeFieldOff) { + mf_classic_listener_reset_state(instance); + command = NfcCommandSleep; + } else if( + (iso3_event->type == Iso14443_3aListenerEventTypeReceivedData) || + (iso3_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame)) { + if(instance->comm_state == MfClassicListenerCommStateEncrypted) { + if(instance->state == MfClassicListenerStateAuthComplete) { + crypto1_decrypt( + instance->crypto, iso3_event->data->buffer, instance->rx_plain_buffer); + rx_buffer_plain = instance->rx_plain_buffer; + if(iso14443_crc_check(Iso14443CrcTypeA, rx_buffer_plain)) { + iso14443_crc_trim(rx_buffer_plain); + } + } else { + rx_buffer_plain = iso3_event->data->buffer; + } + } else { + rx_buffer_plain = iso3_event->data->buffer; + } + + MfClassicListenerCommand mfc_command = MfClassicListenerCommandNack; + if(instance->cmd_in_progress) { + mfc_command = + mf_classic_listener_cmd_handlers[instance->current_cmd_idx] + .handler[instance->current_cmd_handler_idx](instance, rx_buffer_plain); + } else { + for(size_t i = 0; i < COUNT_OF(mf_classic_listener_cmd_handlers); i++) { + if(bit_buffer_get_size(rx_buffer_plain) != + mf_classic_listener_cmd_handlers[i].cmd_len_bits) { + continue; + } + if(bit_buffer_get_byte(rx_buffer_plain, 0) != + mf_classic_listener_cmd_handlers[i].cmd_start_byte) { + continue; + } + instance->current_cmd_idx = i; + instance->current_cmd_handler_idx = 0; + mfc_command = + mf_classic_listener_cmd_handlers[i].handler[0](instance, rx_buffer_plain); + break; + } + } + + if(mfc_command == MfClassicListenerCommandAck) { + mf_classic_listener_send_short_frame(instance, MF_CLASSIC_CMD_ACK); + } else if(mfc_command == MfClassicListenerCommandNack) { + // Calculate nack based on the transfer buffer validity + uint8_t nack = MF_CLASSIC_CMD_NACK; + if(!instance->transfer_valid) { + nack += MF_CLASSIC_CMD_NACK_TRANSFER_INVALID; + } + + mf_classic_listener_send_short_frame(instance, nack); + } else if(mfc_command == MfClassicListenerCommandSilent) { + command = NfcCommandReset; + } else if(mfc_command == MfClassicListenerCommandSleep) { + command = NfcCommandSleep; + } + } else if(iso3_event->type == Iso14443_3aListenerEventTypeHalted) { + mf_classic_listener_reset_state(instance); + } + + return command; +} + +MfClassicListener* + mf_classic_listener_alloc(Iso14443_3aListener* iso14443_3a_listener, MfClassicData* data) { + MfClassicListener* instance = malloc(sizeof(MfClassicListener)); + instance->iso14443_3a_listener = iso14443_3a_listener; + instance->data = data; + mf_classic_listener_prepare_emulation(instance); + + instance->crypto = crypto1_alloc(); + instance->tx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE); + instance->tx_encrypted_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE); + instance->rx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE); + + instance->mfc_event.data = &instance->mfc_event_data; + instance->generic_event.protocol = NfcProtocolMfClassic; + instance->generic_event.event_data = &instance->mfc_event; + instance->generic_event.instance = instance; + + return instance; +} + +void mf_classic_listener_free(MfClassicListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + furi_assert(instance->crypto); + furi_assert(instance->rx_plain_buffer); + furi_assert(instance->tx_encrypted_buffer); + furi_assert(instance->tx_plain_buffer); + + crypto1_free(instance->crypto); + bit_buffer_free(instance->rx_plain_buffer); + bit_buffer_free(instance->tx_encrypted_buffer); + bit_buffer_free(instance->tx_plain_buffer); + + free(instance); +} + +void mf_classic_listener_set_callback( + MfClassicListener* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + + instance->callback = callback; + instance->context = context; +} + +const MfClassicData* mf_classic_listener_get_data(const MfClassicListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +const NfcListenerBase mf_classic_listener = { + .alloc = (NfcListenerAlloc)mf_classic_listener_alloc, + .free = (NfcListenerFree)mf_classic_listener_free, + .set_callback = (NfcListenerSetCallback)mf_classic_listener_set_callback, + .get_data = (NfcListenerGetData)mf_classic_listener_get_data, + .run = (NfcListenerRun)mf_classic_listener_run, +}; diff --git a/lib/nfc/protocols/mf_classic/mf_classic_listener.h b/lib/nfc/protocols/mf_classic/mf_classic_listener.h new file mode 100644 index 00000000000..5169de62d81 --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_listener.h @@ -0,0 +1,27 @@ +#pragma once + +#include "mf_classic.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MfClassicListener MfClassicListener; + +typedef enum { + MfClassicListenerEventTypeAuthContextPartCollected, + MfClassicListenerEventTypeAuthContextFullCollected, +} MfClassicListenerEventType; + +typedef union { + MfClassicAuthContext auth_context; +} MfClassicListenerEventData; + +typedef struct { + MfClassicListenerEventType type; + MfClassicListenerEventData* data; +} MfClassicListenerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_classic/mf_classic_listener_defs.h b/lib/nfc/protocols/mf_classic/mf_classic_listener_defs.h new file mode 100644 index 00000000000..6c7a1dd6a74 --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_listener_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcListenerBase mf_classic_listener; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_classic/mf_classic_listener_i.h b/lib/nfc/protocols/mf_classic/mf_classic_listener_i.h new file mode 100644 index 00000000000..52273be9c22 --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_listener_i.h @@ -0,0 +1,63 @@ +#pragma once + +#include "mf_classic_listener.h" +#include +#include +#include "crypto1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + MfClassicListenerCommandProcessed, + MfClassicListenerCommandAck, + MfClassicListenerCommandNack, + MfClassicListenerCommandSilent, + MfClassicListenerCommandSleep, +} MfClassicListenerCommand; + +typedef enum { + MfClassicListenerStateIdle, + MfClassicListenerStateAuthComplete, +} MfClassicListenerState; + +typedef enum { + MfClassicListenerCommStatePlain, + MfClassicListenerCommStateEncrypted, +} MfClassicListenerCommState; + +struct MfClassicListener { + Iso14443_3aListener* iso14443_3a_listener; + MfClassicListenerState state; + MfClassicListenerCommState comm_state; + + MfClassicData* data; + BitBuffer* tx_plain_buffer; + BitBuffer* tx_encrypted_buffer; + BitBuffer* rx_plain_buffer; + + Crypto1* crypto; + MfClassicAuthContext auth_context; + + // Value operation data + int32_t transfer_value; + bool transfer_valid; + MfClassicValueCommand value_cmd; + + NfcGenericEvent generic_event; + MfClassicListenerEvent mfc_event; + MfClassicListenerEventData mfc_event_data; + NfcGenericCallback callback; + void* context; + + bool cmd_in_progress; + size_t current_cmd_idx; + size_t current_cmd_handler_idx; + + size_t total_block_num; +}; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller.c b/lib/nfc/protocols/mf_classic/mf_classic_poller.c new file mode 100644 index 00000000000..dbc32a1b51c --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller.c @@ -0,0 +1,951 @@ +#include "mf_classic_poller_i.h" + +#include + +#include + +#define TAG "MfClassicPoller" + +#define MF_CLASSIC_MAX_BUFF_SIZE (64) + +typedef NfcCommand (*MfClassicPollerReadHandler)(MfClassicPoller* instance); + +MfClassicPoller* mf_classic_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller) { + furi_assert(iso14443_3a_poller); + + MfClassicPoller* instance = malloc(sizeof(MfClassicPoller)); + instance->iso14443_3a_poller = iso14443_3a_poller; + instance->data = mf_classic_alloc(); + instance->crypto = crypto1_alloc(); + instance->tx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE); + instance->tx_encrypted_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE); + instance->rx_plain_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE); + instance->rx_encrypted_buffer = bit_buffer_alloc(MF_CLASSIC_MAX_BUFF_SIZE); + instance->current_type_check = MfClassicType4k; + + instance->mfc_event.data = &instance->mfc_event_data; + + instance->general_event.protocol = NfcProtocolMfClassic; + instance->general_event.event_data = &instance->mfc_event; + instance->general_event.instance = instance; + + return instance; +} + +void mf_classic_poller_free(MfClassicPoller* instance) { + furi_assert(instance); + furi_assert(instance->data); + furi_assert(instance->crypto); + furi_assert(instance->tx_plain_buffer); + furi_assert(instance->rx_plain_buffer); + furi_assert(instance->tx_encrypted_buffer); + furi_assert(instance->rx_encrypted_buffer); + + mf_classic_free(instance->data); + crypto1_free(instance->crypto); + bit_buffer_free(instance->tx_plain_buffer); + bit_buffer_free(instance->rx_plain_buffer); + bit_buffer_free(instance->tx_encrypted_buffer); + bit_buffer_free(instance->rx_encrypted_buffer); + + free(instance); +} + +static NfcCommand mf_classic_poller_handle_data_update(MfClassicPoller* instance) { + MfClassicPollerEventDataUpdate* data_update = &instance->mfc_event_data.data_update; + + mf_classic_get_read_sectors_and_keys( + instance->data, &data_update->sectors_read, &data_update->keys_found); + data_update->current_sector = instance->mode_ctx.dict_attack_ctx.current_sector; + instance->mfc_event.type = MfClassicPollerEventTypeDataUpdate; + return instance->callback(instance->general_event, instance->context); +} + +static void mf_classic_poller_check_key_b_is_readable( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicBlock* data) { + do { + if(!mf_classic_is_sector_trailer(block_num)) break; + if(!mf_classic_is_allowed_access( + instance->data, block_num, MfClassicKeyTypeA, MfClassicActionKeyBRead)) + break; + + MfClassicSectorTrailer* sec_tr = (MfClassicSectorTrailer*)data; + uint64_t key_b = nfc_util_bytes2num(sec_tr->key_b.data, sizeof(MfClassicKey)); + uint8_t sector_num = mf_classic_get_sector_by_block(block_num); + mf_classic_set_key_found(instance->data, sector_num, MfClassicKeyTypeB, key_b); + } while(false); +} + +NfcCommand mf_classic_poller_handler_detect_type(MfClassicPoller* instance) { + NfcCommand command = NfcCommandReset; + + if(instance->current_type_check == MfClassicType4k) { + iso14443_3a_copy( + instance->data->iso14443_3a_data, + iso14443_3a_poller_get_data(instance->iso14443_3a_poller)); + MfClassicError error = mf_classic_poller_get_nt(instance, 254, MfClassicKeyTypeA, NULL); + if(error == MfClassicErrorNone) { + instance->data->type = MfClassicType4k; + instance->state = MfClassicPollerStateStart; + instance->current_type_check = MfClassicType4k; + FURI_LOG_D(TAG, "4K detected"); + } else { + instance->current_type_check = MfClassicType1k; + } + } else if(instance->current_type_check == MfClassicType1k) { + MfClassicError error = mf_classic_poller_get_nt(instance, 62, MfClassicKeyTypeA, NULL); + if(error == MfClassicErrorNone) { + instance->data->type = MfClassicType1k; + FURI_LOG_D(TAG, "1K detected"); + } else { + instance->data->type = MfClassicTypeMini; + FURI_LOG_D(TAG, "Mini detected"); + } + instance->current_type_check = MfClassicType4k; + instance->state = MfClassicPollerStateStart; + } + + return command; +} + +NfcCommand mf_classic_poller_handler_start(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + + instance->sectors_total = mf_classic_get_total_sectors_num(instance->data->type); + memset(&instance->mode_ctx, 0, sizeof(MfClassicPollerModeContext)); + + instance->mfc_event.type = MfClassicPollerEventTypeRequestMode; + command = instance->callback(instance->general_event, instance->context); + + if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeDictAttack) { + mf_classic_copy(instance->data, instance->mfc_event_data.poller_mode.data); + instance->state = MfClassicPollerStateRequestKey; + } else if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeRead) { + instance->state = MfClassicPollerStateRequestReadSector; + } else if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeWrite) { + instance->state = MfClassicPollerStateRequestSectorTrailer; + } else { + furi_crash("Invalid mode selected"); + } + + return command; +} + +NfcCommand mf_classic_poller_handler_request_sector_trailer(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx; + + if(write_ctx->current_sector == instance->sectors_total) { + instance->state = MfClassicPollerStateSuccess; + } else { + instance->mfc_event.type = MfClassicPollerEventTypeRequestSectorTrailer; + instance->mfc_event_data.sec_tr_data.sector_num = write_ctx->current_sector; + command = instance->callback(instance->general_event, instance->context); + if(instance->mfc_event_data.sec_tr_data.sector_trailer_provided) { + instance->state = MfClassicPollerStateCheckWriteConditions; + memcpy( + &write_ctx->sec_tr, + &instance->mfc_event_data.sec_tr_data.sector_trailer, + sizeof(MfClassicSectorTrailer)); + write_ctx->current_block = + MAX(1, mf_classic_get_first_block_num_of_sector(write_ctx->current_sector)); + + } else { + write_ctx->current_sector++; + } + } + + return command; +} + +NfcCommand mf_classic_handler_check_write_conditions(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + + MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx; + MfClassicSectorTrailer* sec_tr = &write_ctx->sec_tr; + + do { + // Check last block in sector to write + uint8_t sec_tr_block_num = + mf_classic_get_sector_trailer_num_by_sector(write_ctx->current_sector); + if(write_ctx->current_block == sec_tr_block_num) { + write_ctx->current_sector++; + instance->state = MfClassicPollerStateRequestSectorTrailer; + break; + } + + // Check write and read access + if(mf_classic_is_allowed_access_data_block( + sec_tr, write_ctx->current_block, MfClassicKeyTypeA, MfClassicActionDataWrite)) { + write_ctx->key_type_write = MfClassicKeyTypeA; + } else if(mf_classic_is_allowed_access_data_block( + sec_tr, + write_ctx->current_block, + MfClassicKeyTypeB, + MfClassicActionDataWrite)) { + write_ctx->key_type_write = MfClassicKeyTypeB; + } else if(mf_classic_is_value_block(sec_tr, write_ctx->current_block)) { + write_ctx->is_value_block = true; + } else { + FURI_LOG_D(TAG, "Not allowed to write block %d", write_ctx->current_block); + write_ctx->current_block++; + break; + } + + if(mf_classic_is_allowed_access_data_block( + sec_tr, + write_ctx->current_block, + write_ctx->key_type_write, + MfClassicActionDataRead)) { + write_ctx->key_type_read = write_ctx->key_type_write; + } else { + write_ctx->key_type_read = write_ctx->key_type_write == MfClassicKeyTypeA ? + MfClassicKeyTypeB : + MfClassicKeyTypeA; + if(!mf_classic_is_allowed_access_data_block( + sec_tr, + write_ctx->current_block, + write_ctx->key_type_read, + MfClassicActionDataRead)) { + FURI_LOG_D(TAG, "Not allowed to read block %d", write_ctx->current_block); + write_ctx->current_block++; + break; + } + } + + write_ctx->need_halt_before_write = + (write_ctx->key_type_read != write_ctx->key_type_write); + instance->state = MfClassicPollerStateReadBlock; + } while(false); + + return command; +} + +NfcCommand mf_classic_poller_handler_read_block(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx; + + MfClassicKey* auth_key = write_ctx->key_type_read == MfClassicKeyTypeA ? + &write_ctx->sec_tr.key_a : + &write_ctx->sec_tr.key_b; + MfClassicError error = MfClassicErrorNone; + + do { + // Authenticate to sector + error = mf_classic_poller_auth( + instance, write_ctx->current_block, auth_key, write_ctx->key_type_read, NULL); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to auth to block %d", write_ctx->current_block); + instance->state = MfClassicPollerStateFail; + break; + } + + // Read block from tag + error = mf_classic_poller_read_block( + instance, write_ctx->current_block, &write_ctx->tag_block); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to read block %d", write_ctx->current_block); + instance->state = MfClassicPollerStateFail; + break; + } + + if(write_ctx->is_value_block) { + mf_classic_poller_halt(instance); + instance->state = MfClassicPollerStateWriteValueBlock; + } else { + if(write_ctx->need_halt_before_write) { + mf_classic_poller_halt(instance); + } + instance->state = MfClassicPollerStateWriteBlock; + } + } while(false); + + return command; +} + +NfcCommand mf_classic_poller_handler_write_block(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + + MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx; + MfClassicKey* auth_key = write_ctx->key_type_write == MfClassicKeyTypeA ? + &write_ctx->sec_tr.key_a : + &write_ctx->sec_tr.key_b; + MfClassicError error = MfClassicErrorNone; + + do { + // Request block to write + instance->mfc_event.type = MfClassicPollerEventTypeRequestWriteBlock; + instance->mfc_event_data.write_block_data.block_num = write_ctx->current_block; + command = instance->callback(instance->general_event, instance->context); + if(!instance->mfc_event_data.write_block_data.write_block_provided) break; + + // Compare tag and saved block + if(memcmp( + write_ctx->tag_block.data, + instance->mfc_event_data.write_block_data.write_block.data, + sizeof(MfClassicBlock)) == 0) { + FURI_LOG_D(TAG, "Block %d is equal. Skip writing", write_ctx->current_block); + break; + } + + // Reauth if necessary + if(write_ctx->need_halt_before_write) { + error = mf_classic_poller_auth( + instance, write_ctx->current_block, auth_key, write_ctx->key_type_write, NULL); + if(error != MfClassicErrorNone) { + FURI_LOG_D( + TAG, "Failed to auth to block %d for writing", write_ctx->current_block); + instance->state = MfClassicPollerStateFail; + break; + } + } + + // Write block + error = mf_classic_poller_write_block( + instance, + write_ctx->current_block, + &instance->mfc_event_data.write_block_data.write_block); + if(error != MfClassicErrorNone) { + FURI_LOG_D(TAG, "Failed to write block %d", write_ctx->current_block); + instance->state = MfClassicPollerStateFail; + break; + } + + } while(false); + + mf_classic_poller_halt(instance); + write_ctx->current_block++; + instance->state = MfClassicPollerStateCheckWriteConditions; + + return command; +} + +NfcCommand mf_classic_poller_handler_write_value_block(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerWriteContext* write_ctx = &instance->mode_ctx.write_ctx; + + do { + // Request block to write + instance->mfc_event.type = MfClassicPollerEventTypeRequestWriteBlock; + instance->mfc_event_data.write_block_data.block_num = write_ctx->current_block; + command = instance->callback(instance->general_event, instance->context); + if(!instance->mfc_event_data.write_block_data.write_block_provided) break; + + // Compare tag and saved block + if(memcmp( + write_ctx->tag_block.data, + instance->mfc_event_data.write_block_data.write_block.data, + sizeof(MfClassicBlock)) == 0) { + FURI_LOG_D(TAG, "Block %d is equal. Skip writing", write_ctx->current_block); + break; + } + + bool key_a_inc_allowed = mf_classic_is_allowed_access_data_block( + &write_ctx->sec_tr, + write_ctx->current_block, + MfClassicKeyTypeA, + MfClassicActionDataInc); + bool key_b_inc_allowed = mf_classic_is_allowed_access_data_block( + &write_ctx->sec_tr, + write_ctx->current_block, + MfClassicKeyTypeB, + MfClassicActionDataInc); + bool key_a_dec_allowed = mf_classic_is_allowed_access_data_block( + &write_ctx->sec_tr, + write_ctx->current_block, + MfClassicKeyTypeA, + MfClassicActionDataDec); + bool key_b_dec_allowed = mf_classic_is_allowed_access_data_block( + &write_ctx->sec_tr, + write_ctx->current_block, + MfClassicKeyTypeB, + MfClassicActionDataDec); + + int32_t source_value = 0; + int32_t target_value = 0; + if(!mf_classic_block_to_value( + &instance->mfc_event_data.write_block_data.write_block, &source_value, NULL)) + break; + if(!mf_classic_block_to_value(&write_ctx->tag_block, &target_value, NULL)) break; + + MfClassicKeyType auth_key_type = MfClassicKeyTypeA; + MfClassicValueCommand value_cmd = MfClassicValueCommandIncrement; + int32_t diff = source_value - target_value; + if(diff > 0) { + if(key_a_inc_allowed) { + auth_key_type = MfClassicKeyTypeA; + value_cmd = MfClassicValueCommandIncrement; + } else if(key_b_inc_allowed) { + auth_key_type = MfClassicKeyTypeB; + value_cmd = MfClassicValueCommandIncrement; + } else { + FURI_LOG_D(TAG, "Unable to increment value block"); + break; + } + } else { + if(key_a_dec_allowed) { + auth_key_type = MfClassicKeyTypeA; + value_cmd = MfClassicValueCommandDecrement; + diff *= -1; + } else if(key_b_dec_allowed) { + auth_key_type = MfClassicKeyTypeB; + value_cmd = MfClassicValueCommandDecrement; + diff *= -1; + } else { + FURI_LOG_D(TAG, "Unable to decrement value block"); + break; + } + } + + MfClassicKey* key = (auth_key_type == MfClassicKeyTypeA) ? &write_ctx->sec_tr.key_a : + &write_ctx->sec_tr.key_b; + + MfClassicError error = + mf_classic_poller_auth(instance, write_ctx->current_block, key, auth_key_type, NULL); + if(error != MfClassicErrorNone) break; + + error = mf_classic_poller_value_cmd(instance, write_ctx->current_block, value_cmd, diff); + if(error != MfClassicErrorNone) break; + + error = mf_classic_poller_value_transfer(instance, write_ctx->current_block); + if(error != MfClassicErrorNone) break; + + } while(false); + + mf_classic_poller_halt(instance); + write_ctx->is_value_block = false; + write_ctx->current_block++; + instance->state = MfClassicPollerStateCheckWriteConditions; + + return command; +} + +NfcCommand mf_classic_poller_handler_request_read_sector(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + + MfClassicPollerReadContext* sec_read_ctx = &instance->mode_ctx.read_ctx; + MfClassicPollerEventDataReadSectorRequest* sec_read = + &instance->mfc_event_data.read_sector_request_data; + instance->mfc_event.type = MfClassicPollerEventTypeRequestReadSector; + command = instance->callback(instance->general_event, instance->context); + + if(!sec_read->key_provided) { + instance->state = MfClassicPollerStateSuccess; + } else { + sec_read_ctx->current_sector = sec_read->sector_num; + sec_read_ctx->key = sec_read->key; + sec_read_ctx->key_type = sec_read->key_type; + sec_read_ctx->current_block = + mf_classic_get_first_block_num_of_sector(sec_read->sector_num); + sec_read_ctx->auth_passed = false; + instance->state = MfClassicPollerStateReadSectorBlocks; + } + + return command; +} + +NfcCommand mf_classic_poller_handler_request_read_sector_blocks(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + + MfClassicPollerReadContext* sec_read_ctx = &instance->mode_ctx.read_ctx; + + do { + MfClassicError error = MfClassicErrorNone; + + if(!sec_read_ctx->auth_passed) { + uint64_t key = nfc_util_bytes2num(sec_read_ctx->key.data, sizeof(MfClassicKey)); + FURI_LOG_D( + TAG, + "Auth to block %d with key %c: %06llx", + sec_read_ctx->current_block, + sec_read_ctx->key_type == MfClassicKeyTypeA ? 'A' : 'B', + key); + error = mf_classic_poller_auth( + instance, + sec_read_ctx->current_block, + &sec_read_ctx->key, + sec_read_ctx->key_type, + NULL); + if(error != MfClassicErrorNone) break; + + sec_read_ctx->auth_passed = true; + if(!mf_classic_is_key_found( + instance->data, sec_read_ctx->current_sector, sec_read_ctx->key_type)) { + mf_classic_set_key_found( + instance->data, sec_read_ctx->current_sector, sec_read_ctx->key_type, key); + } + } + if(mf_classic_is_block_read(instance->data, sec_read_ctx->current_block)) break; + + FURI_LOG_D(TAG, "Reading block %d", sec_read_ctx->current_block); + MfClassicBlock read_block = {}; + error = mf_classic_poller_read_block(instance, sec_read_ctx->current_block, &read_block); + if(error == MfClassicErrorNone) { + mf_classic_set_block_read(instance->data, sec_read_ctx->current_block, &read_block); + if(sec_read_ctx->key_type == MfClassicKeyTypeA) { + mf_classic_poller_check_key_b_is_readable( + instance, sec_read_ctx->current_block, &read_block); + } + } else { + mf_classic_poller_halt(instance); + sec_read_ctx->auth_passed = false; + } + } while(false); + + uint8_t sec_tr_num = mf_classic_get_sector_trailer_num_by_sector(sec_read_ctx->current_sector); + sec_read_ctx->current_block++; + if(sec_read_ctx->current_block > sec_tr_num) { + mf_classic_poller_halt(instance); + instance->state = MfClassicPollerStateRequestReadSector; + } + + return command; +} + +NfcCommand mf_classic_poller_handler_request_key(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + instance->mfc_event.type = MfClassicPollerEventTypeRequestKey; + command = instance->callback(instance->general_event, instance->context); + if(instance->mfc_event_data.key_request_data.key_provided) { + dict_attack_ctx->current_key = instance->mfc_event_data.key_request_data.key; + instance->state = MfClassicPollerStateAuthKeyA; + } else { + instance->state = MfClassicPollerStateNextSector; + } + + return command; +} + +NfcCommand mf_classic_poller_handler_auth_a(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + if(mf_classic_is_key_found( + instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA)) { + instance->state = MfClassicPollerStateAuthKeyB; + } else { + uint8_t block = mf_classic_get_first_block_num_of_sector(dict_attack_ctx->current_sector); + uint64_t key = nfc_util_bytes2num(dict_attack_ctx->current_key.data, sizeof(MfClassicKey)); + FURI_LOG_D(TAG, "Auth to block %d with key A: %06llx", block, key); + + MfClassicError error = mf_classic_poller_auth( + instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL); + if(error == MfClassicErrorNone) { + FURI_LOG_I(TAG, "Key A found"); + mf_classic_set_key_found( + instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA, key); + + command = mf_classic_poller_handle_data_update(instance); + dict_attack_ctx->current_key_type = MfClassicKeyTypeA; + dict_attack_ctx->current_block = block; + dict_attack_ctx->auth_passed = true; + instance->state = MfClassicPollerStateReadSector; + } else { + mf_classic_poller_halt(instance); + instance->state = MfClassicPollerStateAuthKeyB; + } + } + + return command; +} + +NfcCommand mf_classic_poller_handler_auth_b(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + if(mf_classic_is_key_found( + instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeB)) { + if(mf_classic_is_key_found( + instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeA)) { + instance->state = MfClassicPollerStateNextSector; + } else { + instance->state = MfClassicPollerStateRequestKey; + } + } else { + uint8_t block = mf_classic_get_first_block_num_of_sector(dict_attack_ctx->current_sector); + uint64_t key = nfc_util_bytes2num(dict_attack_ctx->current_key.data, sizeof(MfClassicKey)); + FURI_LOG_D(TAG, "Auth to block %d with key B: %06llx", block, key); + + MfClassicError error = mf_classic_poller_auth( + instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL); + if(error == MfClassicErrorNone) { + FURI_LOG_I(TAG, "Key B found"); + mf_classic_set_key_found( + instance->data, dict_attack_ctx->current_sector, MfClassicKeyTypeB, key); + + command = mf_classic_poller_handle_data_update(instance); + dict_attack_ctx->current_key_type = MfClassicKeyTypeB; + dict_attack_ctx->current_block = block; + + dict_attack_ctx->auth_passed = true; + instance->state = MfClassicPollerStateReadSector; + } else { + mf_classic_poller_halt(instance); + instance->state = MfClassicPollerStateRequestKey; + } + } + + return command; +} + +NfcCommand mf_classic_poller_handler_next_sector(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + dict_attack_ctx->current_sector++; + if(dict_attack_ctx->current_sector == instance->sectors_total) { + instance->state = MfClassicPollerStateSuccess; + } else { + instance->mfc_event.type = MfClassicPollerEventTypeNextSector; + instance->mfc_event_data.next_sector_data.current_sector = dict_attack_ctx->current_sector; + command = instance->callback(instance->general_event, instance->context); + instance->state = MfClassicPollerStateRequestKey; + } + + return command; +} + +NfcCommand mf_classic_poller_handler_read_sector(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + MfClassicError error = MfClassicErrorNone; + uint8_t block_num = dict_attack_ctx->current_block; + MfClassicBlock block = {}; + + do { + if(mf_classic_is_block_read(instance->data, block_num)) break; + + if(!dict_attack_ctx->auth_passed) { + error = mf_classic_poller_auth( + instance, + block_num, + &dict_attack_ctx->current_key, + dict_attack_ctx->current_key_type, + NULL); + if(error != MfClassicErrorNone) { + instance->state = MfClassicPollerStateNextSector; + FURI_LOG_W(TAG, "Failed to re-auth. Go to next sector"); + break; + } + } + + FURI_LOG_D(TAG, "Reading block %d", block_num); + error = mf_classic_poller_read_block(instance, block_num, &block); + + if(error != MfClassicErrorNone) { + mf_classic_poller_halt(instance); + dict_attack_ctx->auth_passed = false; + FURI_LOG_D(TAG, "Failed to read block %d", block_num); + } else { + mf_classic_set_block_read(instance->data, block_num, &block); + if(dict_attack_ctx->current_key_type == MfClassicKeyTypeA) { + mf_classic_poller_check_key_b_is_readable(instance, block_num, &block); + } + } + } while(false); + + uint8_t sec_tr_block_num = + mf_classic_get_sector_trailer_num_by_sector(dict_attack_ctx->current_sector); + dict_attack_ctx->current_block++; + if(dict_attack_ctx->current_block > sec_tr_block_num) { + mf_classic_poller_handle_data_update(instance); + + mf_classic_poller_halt(instance); + dict_attack_ctx->auth_passed = false; + + if(dict_attack_ctx->current_sector == instance->sectors_total) { + instance->state = MfClassicPollerStateNextSector; + } else { + dict_attack_ctx->reuse_key_sector = dict_attack_ctx->current_sector; + instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStart; + instance->mfc_event_data.key_attack_data.current_sector = + dict_attack_ctx->reuse_key_sector; + command = instance->callback(instance->general_event, instance->context); + instance->state = MfClassicPollerStateKeyReuseStart; + } + } + + return command; +} + +NfcCommand mf_classic_poller_handler_key_reuse_start(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + if(dict_attack_ctx->current_key_type == MfClassicKeyTypeA) { + dict_attack_ctx->current_key_type = MfClassicKeyTypeB; + instance->state = MfClassicPollerStateKeyReuseAuthKeyB; + } else { + dict_attack_ctx->reuse_key_sector++; + if(dict_attack_ctx->reuse_key_sector == instance->sectors_total) { + instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStop; + command = instance->callback(instance->general_event, instance->context); + instance->state = MfClassicPollerStateRequestKey; + } else { + instance->mfc_event.type = MfClassicPollerEventTypeKeyAttackStart; + instance->mfc_event_data.key_attack_data.current_sector = + dict_attack_ctx->reuse_key_sector; + command = instance->callback(instance->general_event, instance->context); + + dict_attack_ctx->current_key_type = MfClassicKeyTypeA; + instance->state = MfClassicPollerStateKeyReuseAuthKeyA; + } + } + + return command; +} + +NfcCommand mf_classic_poller_handler_key_reuse_auth_key_a(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + if(mf_classic_is_key_found( + instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeA)) { + instance->state = MfClassicPollerStateKeyReuseStart; + } else { + uint8_t block = + mf_classic_get_first_block_num_of_sector(dict_attack_ctx->reuse_key_sector); + uint64_t key = nfc_util_bytes2num(dict_attack_ctx->current_key.data, sizeof(MfClassicKey)); + FURI_LOG_D(TAG, "Key attack auth to block %d with key A: %06llx", block, key); + + MfClassicError error = mf_classic_poller_auth( + instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL); + if(error == MfClassicErrorNone) { + FURI_LOG_I(TAG, "Key A found"); + mf_classic_set_key_found( + instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeA, key); + + command = mf_classic_poller_handle_data_update(instance); + dict_attack_ctx->current_key_type = MfClassicKeyTypeA; + dict_attack_ctx->current_block = block; + dict_attack_ctx->auth_passed = true; + instance->state = MfClassicPollerStateKeyReuseReadSector; + } else { + mf_classic_poller_halt(instance); + dict_attack_ctx->auth_passed = false; + instance->state = MfClassicPollerStateKeyReuseStart; + } + } + + return command; +} + +NfcCommand mf_classic_poller_handler_key_reuse_auth_key_b(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + if(mf_classic_is_key_found( + instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeB)) { + instance->state = MfClassicPollerStateKeyReuseStart; + } else { + uint8_t block = + mf_classic_get_first_block_num_of_sector(dict_attack_ctx->reuse_key_sector); + uint64_t key = nfc_util_bytes2num(dict_attack_ctx->current_key.data, sizeof(MfClassicKey)); + FURI_LOG_D(TAG, "Key attack auth to block %d with key B: %06llx", block, key); + + MfClassicError error = mf_classic_poller_auth( + instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL); + if(error == MfClassicErrorNone) { + FURI_LOG_I(TAG, "Key B found"); + mf_classic_set_key_found( + instance->data, dict_attack_ctx->reuse_key_sector, MfClassicKeyTypeB, key); + + command = mf_classic_poller_handle_data_update(instance); + dict_attack_ctx->current_key_type = MfClassicKeyTypeB; + dict_attack_ctx->current_block = block; + + dict_attack_ctx->auth_passed = true; + instance->state = MfClassicPollerStateKeyReuseReadSector; + } else { + mf_classic_poller_halt(instance); + dict_attack_ctx->auth_passed = false; + instance->state = MfClassicPollerStateKeyReuseStart; + } + } + + return command; +} + +NfcCommand mf_classic_poller_handler_key_reuse_read_sector(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; + + MfClassicError error = MfClassicErrorNone; + uint8_t block_num = dict_attack_ctx->current_block; + MfClassicBlock block = {}; + + do { + if(mf_classic_is_block_read(instance->data, block_num)) break; + + if(!dict_attack_ctx->auth_passed) { + error = mf_classic_poller_auth( + instance, + block_num, + &dict_attack_ctx->current_key, + dict_attack_ctx->current_key_type, + NULL); + if(error != MfClassicErrorNone) { + instance->state = MfClassicPollerStateKeyReuseStart; + break; + } + } + + FURI_LOG_D(TAG, "Reading block %d", block_num); + error = mf_classic_poller_read_block(instance, block_num, &block); + + if(error != MfClassicErrorNone) { + mf_classic_poller_halt(instance); + dict_attack_ctx->auth_passed = false; + FURI_LOG_D(TAG, "Failed to read block %d", block_num); + } else { + mf_classic_set_block_read(instance->data, block_num, &block); + if(dict_attack_ctx->current_key_type == MfClassicKeyTypeA) { + mf_classic_poller_check_key_b_is_readable(instance, block_num, &block); + } + } + } while(false); + + uint16_t sec_tr_block_num = + mf_classic_get_sector_trailer_num_by_sector(dict_attack_ctx->reuse_key_sector); + dict_attack_ctx->current_block++; + if(dict_attack_ctx->current_block > sec_tr_block_num) { + mf_classic_poller_halt(instance); + dict_attack_ctx->auth_passed = false; + + mf_classic_poller_handle_data_update(instance); + instance->state = MfClassicPollerStateKeyReuseStart; + } + + return command; +} + +NfcCommand mf_classic_poller_handler_success(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + instance->mfc_event.type = MfClassicPollerEventTypeSuccess; + command = instance->callback(instance->general_event, instance->context); + + return command; +} + +NfcCommand mf_classic_poller_handler_fail(MfClassicPoller* instance) { + NfcCommand command = NfcCommandContinue; + instance->mfc_event.type = MfClassicPollerEventTypeFail; + command = instance->callback(instance->general_event, instance->context); + instance->state = MfClassicPollerStateDetectType; + + return command; +} + +static const MfClassicPollerReadHandler + mf_classic_poller_dict_attack_handler[MfClassicPollerStateNum] = { + [MfClassicPollerStateDetectType] = mf_classic_poller_handler_detect_type, + [MfClassicPollerStateStart] = mf_classic_poller_handler_start, + [MfClassicPollerStateRequestSectorTrailer] = + mf_classic_poller_handler_request_sector_trailer, + [MfClassicPollerStateCheckWriteConditions] = mf_classic_handler_check_write_conditions, + [MfClassicPollerStateReadBlock] = mf_classic_poller_handler_read_block, + [MfClassicPollerStateWriteBlock] = mf_classic_poller_handler_write_block, + [MfClassicPollerStateWriteValueBlock] = mf_classic_poller_handler_write_value_block, + [MfClassicPollerStateNextSector] = mf_classic_poller_handler_next_sector, + [MfClassicPollerStateRequestKey] = mf_classic_poller_handler_request_key, + [MfClassicPollerStateRequestReadSector] = mf_classic_poller_handler_request_read_sector, + [MfClassicPollerStateReadSectorBlocks] = + mf_classic_poller_handler_request_read_sector_blocks, + [MfClassicPollerStateAuthKeyA] = mf_classic_poller_handler_auth_a, + [MfClassicPollerStateAuthKeyB] = mf_classic_poller_handler_auth_b, + [MfClassicPollerStateReadSector] = mf_classic_poller_handler_read_sector, + [MfClassicPollerStateKeyReuseStart] = mf_classic_poller_handler_key_reuse_start, + [MfClassicPollerStateKeyReuseAuthKeyA] = mf_classic_poller_handler_key_reuse_auth_key_a, + [MfClassicPollerStateKeyReuseAuthKeyB] = mf_classic_poller_handler_key_reuse_auth_key_b, + [MfClassicPollerStateKeyReuseReadSector] = mf_classic_poller_handler_key_reuse_read_sector, + [MfClassicPollerStateSuccess] = mf_classic_poller_handler_success, + [MfClassicPollerStateFail] = mf_classic_poller_handler_fail, +}; + +NfcCommand mf_classic_poller_run(NfcGenericEvent event, void* context) { + furi_assert(event.event_data); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + furi_assert(context); + + MfClassicPoller* instance = context; + Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data; + NfcCommand command = NfcCommandContinue; + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + if(instance->card_state == MfClassicCardStateLost) { + instance->card_state = MfClassicCardStateDetected; + instance->mfc_event.type = MfClassicPollerEventTypeCardDetected; + instance->callback(instance->general_event, instance->context); + } + command = mf_classic_poller_dict_attack_handler[instance->state](instance); + } else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) { + if(instance->card_state == MfClassicCardStateDetected) { + instance->card_state = MfClassicCardStateLost; + instance->mfc_event.type = MfClassicPollerEventTypeCardLost; + command = instance->callback(instance->general_event, instance->context); + } + } + + return command; +} + +bool mf_classic_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(event.event_data); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + furi_assert(context); + + Iso14443_3aPoller* iso3_poller = event.instance; + Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data; + bool detected = false; + const uint8_t auth_cmd[] = {MF_CLASSIC_CMD_AUTH_KEY_A, 0}; + BitBuffer* tx_buffer = bit_buffer_alloc(COUNT_OF(auth_cmd)); + bit_buffer_copy_bytes(tx_buffer, auth_cmd, COUNT_OF(auth_cmd)); + BitBuffer* rx_buffer = bit_buffer_alloc(sizeof(MfClassicNt)); + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + Iso14443_3aError error = iso14443_3a_poller_send_standard_frame( + iso3_poller, tx_buffer, rx_buffer, MF_CLASSIC_FWT_FC); + if(error == Iso14443_3aErrorWrongCrc) { + if(bit_buffer_get_size_bytes(rx_buffer) == sizeof(MfClassicNt)) { + detected = true; + } + } + } + + bit_buffer_free(tx_buffer); + bit_buffer_free(rx_buffer); + + return detected; +} + +void mf_classic_poller_set_callback( + MfClassicPoller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +const MfClassicData* mf_classic_poller_get_data(const MfClassicPoller* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +const NfcPollerBase mf_classic_poller = { + .alloc = (NfcPollerAlloc)mf_classic_poller_alloc, + .free = (NfcPollerFree)mf_classic_poller_free, + .set_callback = (NfcPollerSetCallback)mf_classic_poller_set_callback, + .run = (NfcPollerRun)mf_classic_poller_run, + .detect = (NfcPollerDetect)mf_classic_poller_detect, + .get_data = (NfcPollerGetData)mf_classic_poller_get_data, +}; diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller.h b/lib/nfc/protocols/mf_classic/mf_classic_poller.h new file mode 100644 index 00000000000..19e52570175 --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller.h @@ -0,0 +1,320 @@ +#pragma once + +#include "mf_classic.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief MfClassicPoller opaque type definition. + */ +typedef struct MfClassicPoller MfClassicPoller; + +/** + * @brief Enumeration of possible MfClassic poller event types. + */ +typedef enum { + MfClassicPollerEventTypeRequestMode, /**< Poller requests to fill the mode. */ + + MfClassicPollerEventTypeRequestReadSector, /**< Poller requests data to read sector. */ + + MfClassicPollerEventTypeRequestSectorTrailer, /**< Poller requests sector trailer for writing block. */ + MfClassicPollerEventTypeRequestWriteBlock, /**< Poller requests data to write block. */ + + MfClassicPollerEventTypeRequestKey, /**< Poller requests key for sector authentication. */ + MfClassicPollerEventTypeNextSector, /**< Poller switches to next sector during dictionary attack. */ + MfClassicPollerEventTypeDataUpdate, /**< Poller updates data. */ + MfClassicPollerEventTypeFoundKeyA, /**< Poller found key A. */ + MfClassicPollerEventTypeFoundKeyB, /**< Poller found key B. */ + MfClassicPollerEventTypeKeyAttackStart, /**< Poller starts key attack. */ + MfClassicPollerEventTypeKeyAttackStop, /**< Poller stops key attack. */ + MfClassicPollerEventTypeKeyAttackNextSector, /**< Poller switches to next sector during key attack. */ + + MfClassicPollerEventTypeCardDetected, /**< Poller detected card. */ + MfClassicPollerEventTypeCardLost, /**< Poller lost card. */ + MfClassicPollerEventTypeSuccess, /**< Poller succeeded. */ + MfClassicPollerEventTypeFail, /**< Poller failed. */ +} MfClassicPollerEventType; + +/** + * @brief MfClassic poller mode. + */ +typedef enum { + MfClassicPollerModeRead, /**< Poller reading mode. */ + MfClassicPollerModeWrite, /**< Poller writing mode. */ + MfClassicPollerModeDictAttack, /**< Poller dictionary attack mode. */ +} MfClassicPollerMode; + +/** + * @brief MfClassic poller request mode event data. + * + * This instance of this structure must be filled on MfClassicPollerEventTypeRequestMode event. + */ +typedef struct { + MfClassicPollerMode mode; /**< Mode to be used by poller. */ + const MfClassicData* data; /**< Data to be used by poller. */ +} MfClassicPollerEventDataRequestMode; + +/** + * @brief MfClassic poller next sector event data. + * + * The instance of this structure is filled by poller and passed with + * MfClassicPollerEventTypeNextSector event. + */ +typedef struct { + uint8_t current_sector; /**< Current sector number. */ +} MfClassicPollerEventDataDictAttackNextSector; + +/** + * @brief MfClassic poller update event data. + * + * The instance of this structure is filled by poller and passed with + * MfClassicPollerEventTypeDataUpdate event. + */ +typedef struct { + uint8_t sectors_read; /**< Number of sectors read. */ + uint8_t keys_found; /**< Number of keys found. */ + uint8_t current_sector; /**< Current sector number. */ +} MfClassicPollerEventDataUpdate; + +/** + * @brief MfClassic poller key request event data. + * + * The instance of this structure must be filled on MfClassicPollerEventTypeRequestKey event. + */ +typedef struct { + MfClassicKey key; /**< Key to be used by poller. */ + bool key_provided; /**< Flag indicating if key is provided. */ +} MfClassicPollerEventDataKeyRequest; + +/** + * @brief MfClassic poller read sector request event data. + * + * The instance of this structure must be filled on MfClassicPollerEventTypeRequestReadSector event. + */ +typedef struct { + uint8_t sector_num; /**< Sector number to be read. */ + MfClassicKey key; /**< Key to be used by poller. */ + MfClassicKeyType key_type; /**< Key type to be used by poller. */ + bool key_provided; /**< Flag indicating if key is provided. */ +} MfClassicPollerEventDataReadSectorRequest; + +/** + * @brief MfClassic poller sector trailer request event data. + * + * The instance of this structure must be filled on MfClassicPollerEventTypeRequestSectorTrailer event. + */ +typedef struct { + uint8_t sector_num; /**< Sector number to be read. */ + MfClassicBlock sector_trailer; /**< Sector trailer to be used by poller. */ + bool sector_trailer_provided; /**< Flag indicating if sector trailer is provided. */ +} MfClassicPollerEventDataSectorTrailerRequest; + +/** + * @brief MfClassic poller write block request event data. + * + * The instance of this structure must be filled on MfClassicPollerEventTypeRequestWriteBlock event. + */ +typedef struct { + uint8_t block_num; /**< Block number to be written. */ + MfClassicBlock write_block; /**< Block to be written. */ + bool write_block_provided; /**< Flag indicating if block is provided. */ +} MfClassicPollerEventDataWriteBlockRequest; + +/** + * @brief MfClassic poller key attack event data. + * + * The instance of this structure is filled by poller and passed with + * MfClassicPollerEventTypeKeyAttackNextSector event. + */ +typedef struct { + uint8_t current_sector; /**< Current sector number. */ +} MfClassicPollerEventKeyAttackData; + +/** + * @brief MfClassic poller event data. + */ +typedef union { + MfClassicError error; /**< Error code on MfClassicPollerEventTypeFail event. */ + MfClassicPollerEventDataRequestMode poller_mode; /**< Poller mode context. */ + MfClassicPollerEventDataDictAttackNextSector next_sector_data; /**< Next sector context. */ + MfClassicPollerEventDataKeyRequest key_request_data; /**< Key request context. */ + MfClassicPollerEventDataUpdate data_update; /**< Data update context. */ + MfClassicPollerEventDataReadSectorRequest + read_sector_request_data; /**< Read sector request context. */ + MfClassicPollerEventKeyAttackData key_attack_data; /**< Key attack context. */ + MfClassicPollerEventDataSectorTrailerRequest sec_tr_data; /**< Sector trailer request context. */ + MfClassicPollerEventDataWriteBlockRequest write_block_data; /**< Write block request context. */ +} MfClassicPollerEventData; + +/** + * @brief MfClassic poller event. + * + * Upon emission of an event, an instance of this struct will be passed to the callback. + */ +typedef struct { + MfClassicPollerEventType type; /**< Event type. */ + MfClassicPollerEventData* data; /**< Pointer to event specific data. */ +} MfClassicPollerEvent; + +/** + * @brief Collect tag nonce during authentication. + * + * Must ONLY be used inside the callback function. + * + * Starts authentication procedure and collects tag nonce. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] block_num block number for authentication. + * @param[in] key_type key type to be used for authentication. + * @param[out] nt pointer to the MfClassicNt structure to be filled with nonce data. + * @return MfClassicErrorNone on success, an error code on failure. + */ +MfClassicError mf_classic_poller_get_nt( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicKeyType key_type, + MfClassicNt* nt); + +/** + * @brief Collect tag nonce during nested authentication. + * + * Must ONLY be used inside the callback function. + * + * Starts nested authentication procedure and collects tag nonce. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] block_num block number for authentication. + * @param[in] key_type key type to be used for authentication. + * @param[out] nt pointer to the MfClassicNt structure to be filled with nonce data. + * @return MfClassicErrorNone on success, an error code on failure. + */ +MfClassicError mf_classic_poller_get_nt_nested( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicKeyType key_type, + MfClassicNt* nt); + +/** + * @brief Perform authentication. + * + * Must ONLY be used inside the callback function. + * + * Perform authentication as specified in Mf Classic protocol. Initialize crypto state for futher + * communication with the tag. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] block_num block number for authentication. + * @param[in] key key to be used for authentication. + * @param[in] key_type key type to be used for authentication. + * @param[out] data pointer to MfClassicAuthContext structure to be filled with authentication data. + * @return MfClassicErrorNone on success, an error code on failure. + */ +MfClassicError mf_classic_poller_auth( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + MfClassicAuthContext* data); + +/** + * @brief Perform nested authentication. + * + * Must ONLY be used inside the callback function. + * + * Perform nested authentication as specified in Mf Classic protocol. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] block_num block number for authentication. + * @param[in] key key to be used for authentication. + * @param[in] key_type key type to be used for authentication. + * @param[out] data pointer to MfClassicAuthContext structure to be filled with authentication data. + * @return MfClassicErrorNone on success, an error code on failure. + */ +MfClassicError mf_classic_poller_auth_nested( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + MfClassicAuthContext* data); + +/** + * @brief Halt the tag. + * + * Must ONLY be used inside the callback function. + * + * Halt the tag and reset crypto state of the poller. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @return MfClassicErrorNone on success, an error code on failure. + */ +MfClassicError mf_classic_poller_halt(MfClassicPoller* instance); + +/** + * @brief Read block from tag. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] block_num block number to be read. + * @param[out] data pointer to the MfClassicBlock structure to be filled with block data. + * @return MfClassicErrorNone on success, an error code on failure. + */ +MfClassicError mf_classic_poller_read_block( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicBlock* data); + +/** + * @brief Write block to tag. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] block_num block number to be written. + * @param[in] data pointer to the MfClassicBlock structure to be written. + * @return MfClassicErrorNone on success, an error code on failure. + */ +MfClassicError mf_classic_poller_write_block( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicBlock* data); + +/** + * @brief Perform value command on tag. + * + * Must ONLY be used inside the callback function. + * + * Perform Increment, Decrement or Restore command on tag. The result is stored in internal transfer + * block of the tag. Use mf_classic_poller_value_transfer to transfer the result to the tag. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] block_num block number to be used for value command. + * @param[in] cmd value command to be performed. + * @param[in] data value to be used for value command. + * @return MfClassicErrorNone on success, an error code on failure. + */ +MfClassicError mf_classic_poller_value_cmd( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicValueCommand cmd, + int32_t data); + +/** + * @brief Transfer internal transfer block to tag. + * + * Must ONLY be used inside the callback function. + * + * Transfer internal transfer block to tag. The block is filled by mf_classic_poller_value_cmd. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] block_num block number to be used for value command. + * @return MfClassicErrorNone on success, an error code on failure. + */ +MfClassicError mf_classic_poller_value_transfer(MfClassicPoller* instance, uint8_t block_num); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller_defs.h b/lib/nfc/protocols/mf_classic/mf_classic_poller_defs.h new file mode 100644 index 00000000000..d0c0b18fcfc --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcPollerBase mf_classic_poller; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller_i.c b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.c new file mode 100644 index 00000000000..16bfb3f7282 --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.c @@ -0,0 +1,447 @@ +#include "mf_classic_poller_i.h" + +#include +#include + +#include + +#define TAG "MfCLassicPoller" + +MfClassicError mf_classic_process_error(Iso14443_3aError error) { + MfClassicError ret = MfClassicErrorNone; + + switch(error) { + case Iso14443_3aErrorNone: + ret = MfClassicErrorNone; + break; + case Iso14443_3aErrorNotPresent: + ret = MfClassicErrorNotPresent; + break; + case Iso14443_3aErrorColResFailed: + case Iso14443_3aErrorCommunication: + case Iso14443_3aErrorWrongCrc: + ret = MfClassicErrorProtocol; + break; + case Iso14443_3aErrorTimeout: + ret = MfClassicErrorTimeout; + break; + default: + ret = MfClassicErrorProtocol; + break; + } + + return ret; +} + +static MfClassicError mf_classic_poller_get_nt_common( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicKeyType key_type, + MfClassicNt* nt, + bool is_nested) { + MfClassicError ret = MfClassicErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + uint8_t auth_type = (key_type == MfClassicKeyTypeB) ? MF_CLASSIC_CMD_AUTH_KEY_B : + MF_CLASSIC_CMD_AUTH_KEY_A; + uint8_t auth_cmd[2] = {auth_type, block_num}; + bit_buffer_copy_bytes(instance->tx_plain_buffer, auth_cmd, sizeof(auth_cmd)); + + if(is_nested) { + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer); + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + error = iso14443_3a_poller_txrx_custom_parity( + instance->iso14443_3a_poller, + instance->tx_encrypted_buffer, + instance->rx_plain_buffer, // NT gets decrypted by mf_classic_async_auth + MF_CLASSIC_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_classic_process_error(error); + break; + } + } else { + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_plain_buffer, + instance->rx_plain_buffer, + MF_CLASSIC_FWT_FC); + if(error != Iso14443_3aErrorWrongCrc) { + ret = mf_classic_process_error(error); + break; + } + } + if(bit_buffer_get_size_bytes(instance->rx_plain_buffer) != sizeof(MfClassicNt)) { + ret = MfClassicErrorProtocol; + break; + } + + if(nt) { + bit_buffer_write_bytes(instance->rx_plain_buffer, nt->data, sizeof(MfClassicNt)); + } + } while(false); + + return ret; +} + +MfClassicError mf_classic_poller_get_nt( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicKeyType key_type, + MfClassicNt* nt) { + return mf_classic_poller_get_nt_common(instance, block_num, key_type, nt, false); +} + +MfClassicError mf_classic_poller_get_nt_nested( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicKeyType key_type, + MfClassicNt* nt) { + return mf_classic_poller_get_nt_common(instance, block_num, key_type, nt, true); +} + +static MfClassicError mf_classic_poller_auth_common( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + MfClassicAuthContext* data, + bool is_nested) { + MfClassicError ret = MfClassicErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + iso14443_3a_copy( + instance->data->iso14443_3a_data, + iso14443_3a_poller_get_data(instance->iso14443_3a_poller)); + + MfClassicNt nt = {}; + if(is_nested) { + ret = mf_classic_poller_get_nt_nested(instance, block_num, key_type, &nt); + } else { + ret = mf_classic_poller_get_nt(instance, block_num, key_type, &nt); + } + if(ret != MfClassicErrorNone) break; + if(data) { + data->nt = nt; + } + + uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data); + uint64_t key_num = nfc_util_bytes2num(key->data, sizeof(MfClassicKey)); + MfClassicNr nr = {}; + furi_hal_random_fill_buf(nr.data, sizeof(MfClassicNr)); + + crypto1_encrypt_reader_nonce( + instance->crypto, + key_num, + cuid, + nt.data, + nr.data, + instance->tx_encrypted_buffer, + is_nested); + error = iso14443_3a_poller_txrx_custom_parity( + instance->iso14443_3a_poller, + instance->tx_encrypted_buffer, + instance->rx_encrypted_buffer, + MF_CLASSIC_FWT_FC); + + if(error != Iso14443_3aErrorNone) { + ret = mf_classic_process_error(error); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_encrypted_buffer) != 4) { + ret = MfClassicErrorAuth; + } + + crypto1_word(instance->crypto, 0, 0); + instance->auth_state = MfClassicAuthStatePassed; + + if(data) { + data->nr = nr; + const uint8_t* nr_ar = bit_buffer_get_data(instance->tx_encrypted_buffer); + memcpy(data->ar.data, &nr_ar[4], sizeof(MfClassicAr)); + bit_buffer_write_bytes( + instance->rx_encrypted_buffer, data->at.data, sizeof(MfClassicAt)); + } + } while(false); + + if(ret != MfClassicErrorNone) { + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + } + + return ret; +} + +MfClassicError mf_classic_poller_auth( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + MfClassicAuthContext* data) { + return mf_classic_poller_auth_common(instance, block_num, key, key_type, data, false); +} + +MfClassicError mf_classic_poller_auth_nested( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + MfClassicAuthContext* data) { + return mf_classic_poller_auth_common(instance, block_num, key, key_type, data, true); +} + +MfClassicError mf_classic_poller_halt(MfClassicPoller* instance) { + MfClassicError ret = MfClassicErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + uint8_t halt_cmd[2] = {MF_CLASSIC_CMD_HALT_MSB, MF_CLASSIC_CMD_HALT_LSB}; + bit_buffer_copy_bytes(instance->tx_plain_buffer, halt_cmd, sizeof(halt_cmd)); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer); + + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + + error = iso14443_3a_poller_txrx_custom_parity( + instance->iso14443_3a_poller, + instance->tx_encrypted_buffer, + instance->rx_encrypted_buffer, + MF_CLASSIC_FWT_FC); + if(error != Iso14443_3aErrorTimeout) { + ret = mf_classic_process_error(error); + break; + } + instance->auth_state = MfClassicAuthStateIdle; + instance->iso14443_3a_poller->state = Iso14443_3aPollerStateIdle; + } while(false); + + return ret; +} + +MfClassicError mf_classic_poller_read_block( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicBlock* data) { + MfClassicError ret = MfClassicErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + uint8_t read_block_cmd[2] = {MF_CLASSIC_CMD_READ_BLOCK, block_num}; + bit_buffer_copy_bytes(instance->tx_plain_buffer, read_block_cmd, sizeof(read_block_cmd)); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer); + + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + + error = iso14443_3a_poller_txrx_custom_parity( + instance->iso14443_3a_poller, + instance->tx_encrypted_buffer, + instance->rx_encrypted_buffer, + MF_CLASSIC_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_classic_process_error(error); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_encrypted_buffer) != + (sizeof(MfClassicBlock) + 2)) { + ret = MfClassicErrorProtocol; + break; + } + + crypto1_decrypt( + instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer); + + if(!iso14443_crc_check(Iso14443CrcTypeA, instance->rx_plain_buffer)) { + FURI_LOG_D(TAG, "CRC error"); + ret = MfClassicErrorProtocol; + break; + } + + iso14443_crc_trim(instance->rx_plain_buffer); + bit_buffer_write_bytes(instance->rx_plain_buffer, data->data, sizeof(MfClassicBlock)); + } while(false); + + return ret; +} + +MfClassicError mf_classic_poller_write_block( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicBlock* data) { + MfClassicError ret = MfClassicErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + uint8_t write_block_cmd[2] = {MF_CLASSIC_CMD_WRITE_BLOCK, block_num}; + bit_buffer_copy_bytes(instance->tx_plain_buffer, write_block_cmd, sizeof(write_block_cmd)); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer); + + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + + error = iso14443_3a_poller_txrx_custom_parity( + instance->iso14443_3a_poller, + instance->tx_encrypted_buffer, + instance->rx_encrypted_buffer, + MF_CLASSIC_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_classic_process_error(error); + break; + } + if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) { + ret = MfClassicErrorProtocol; + break; + } + + crypto1_decrypt( + instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer); + + if(bit_buffer_get_byte(instance->rx_plain_buffer, 0) != MF_CLASSIC_CMD_ACK) { + FURI_LOG_D(TAG, "Not ACK received"); + ret = MfClassicErrorProtocol; + break; + } + + bit_buffer_copy_bytes(instance->tx_plain_buffer, data->data, sizeof(MfClassicBlock)); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer); + + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + + error = iso14443_3a_poller_txrx_custom_parity( + instance->iso14443_3a_poller, + instance->tx_encrypted_buffer, + instance->rx_encrypted_buffer, + MF_CLASSIC_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_classic_process_error(error); + break; + } + if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) { + ret = MfClassicErrorProtocol; + break; + } + + crypto1_decrypt( + instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer); + + if(bit_buffer_get_byte(instance->rx_plain_buffer, 0) != MF_CLASSIC_CMD_ACK) { + FURI_LOG_D(TAG, "Not ACK received"); + ret = MfClassicErrorProtocol; + break; + } + } while(false); + + return ret; +} + +MfClassicError mf_classic_poller_value_cmd( + MfClassicPoller* instance, + uint8_t block_num, + MfClassicValueCommand cmd, + int32_t data) { + MfClassicError ret = MfClassicErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + uint8_t cmd_value = 0; + if(cmd == MfClassicValueCommandDecrement) { + cmd_value = MF_CLASSIC_CMD_VALUE_DEC; + } else if(cmd == MfClassicValueCommandIncrement) { + cmd_value = MF_CLASSIC_CMD_VALUE_INC; + } else { + cmd_value = MF_CLASSIC_CMD_VALUE_RESTORE; + } + uint8_t value_cmd[2] = {cmd_value, block_num}; + bit_buffer_copy_bytes(instance->tx_plain_buffer, value_cmd, sizeof(value_cmd)); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer); + + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + + error = iso14443_3a_poller_txrx_custom_parity( + instance->iso14443_3a_poller, + instance->tx_encrypted_buffer, + instance->rx_encrypted_buffer, + MF_CLASSIC_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_classic_process_error(error); + break; + } + if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) { + ret = MfClassicErrorProtocol; + break; + } + + crypto1_decrypt( + instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer); + + if(bit_buffer_get_byte(instance->rx_plain_buffer, 0) != MF_CLASSIC_CMD_ACK) { + FURI_LOG_D(TAG, "Not ACK received"); + ret = MfClassicErrorProtocol; + break; + } + + bit_buffer_copy_bytes(instance->tx_plain_buffer, (uint8_t*)&data, sizeof(data)); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer); + + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + + error = iso14443_3a_poller_txrx_custom_parity( + instance->iso14443_3a_poller, + instance->tx_encrypted_buffer, + instance->rx_encrypted_buffer, + MF_CLASSIC_FWT_FC); + + // Command processed if tag doesn't respond + if(error != Iso14443_3aErrorTimeout) { + ret = MfClassicErrorProtocol; + break; + } + ret = MfClassicErrorNone; + } while(false); + + return ret; +} + +MfClassicError mf_classic_poller_value_transfer(MfClassicPoller* instance, uint8_t block_num) { + MfClassicError ret = MfClassicErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + uint8_t transfer_cmd[2] = {MF_CLASSIC_CMD_VALUE_TRANSFER, block_num}; + bit_buffer_copy_bytes(instance->tx_plain_buffer, transfer_cmd, sizeof(transfer_cmd)); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer); + + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + + error = iso14443_3a_poller_txrx_custom_parity( + instance->iso14443_3a_poller, + instance->tx_encrypted_buffer, + instance->rx_encrypted_buffer, + MF_CLASSIC_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_classic_process_error(error); + break; + } + if(bit_buffer_get_size(instance->rx_encrypted_buffer) != 4) { + ret = MfClassicErrorProtocol; + break; + } + + crypto1_decrypt( + instance->crypto, instance->rx_encrypted_buffer, instance->rx_plain_buffer); + + if(bit_buffer_get_byte(instance->rx_plain_buffer, 0) != MF_CLASSIC_CMD_ACK) { + FURI_LOG_D(TAG, "Not ACK received"); + ret = MfClassicErrorProtocol; + break; + } + + } while(false); + + return ret; +} diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h new file mode 100644 index 00000000000..0be42196f1d --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h @@ -0,0 +1,174 @@ +#pragma once + +#include "mf_classic_poller.h" +#include +#include +#include "crypto1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MF_CLASSIC_FWT_FC (60000) + +typedef enum { + MfClassicAuthStateIdle, + MfClassicAuthStatePassed, +} MfClassicAuthState; + +typedef enum { + MfClassicCardStateDetected, + MfClassicCardStateLost, +} MfClassicCardState; + +typedef enum { + MfClassicPollerStateDetectType, + MfClassicPollerStateStart, + + // Write states + MfClassicPollerStateRequestSectorTrailer, + MfClassicPollerStateCheckWriteConditions, + MfClassicPollerStateReadBlock, + MfClassicPollerStateWriteBlock, + MfClassicPollerStateWriteValueBlock, + + // Read states + MfClassicPollerStateRequestReadSector, + MfClassicPollerStateReadSectorBlocks, + + // Dict attack states + MfClassicPollerStateNextSector, + MfClassicPollerStateRequestKey, + MfClassicPollerStateReadSector, + MfClassicPollerStateAuthKeyA, + MfClassicPollerStateAuthKeyB, + MfClassicPollerStateKeyReuseStart, + MfClassicPollerStateKeyReuseAuthKeyA, + MfClassicPollerStateKeyReuseAuthKeyB, + MfClassicPollerStateKeyReuseReadSector, + MfClassicPollerStateSuccess, + MfClassicPollerStateFail, + + MfClassicPollerStateNum, +} MfClassicPollerState; + +typedef struct { + uint8_t current_sector; + MfClassicSectorTrailer sec_tr; + uint16_t current_block; + bool is_value_block; + MfClassicKeyType key_type_read; + MfClassicKeyType key_type_write; + bool need_halt_before_write; + MfClassicBlock tag_block; +} MfClassicPollerWriteContext; + +typedef struct { + uint8_t current_sector; + MfClassicKey current_key; + MfClassicKeyType current_key_type; + bool auth_passed; + uint16_t current_block; + uint8_t reuse_key_sector; +} MfClassicPollerDictAttackContext; + +typedef struct { + uint8_t current_sector; + uint16_t current_block; + MfClassicKeyType key_type; + MfClassicKey key; + bool auth_passed; +} MfClassicPollerReadContext; + +typedef union { + MfClassicPollerWriteContext write_ctx; + MfClassicPollerDictAttackContext dict_attack_ctx; + MfClassicPollerReadContext read_ctx; + +} MfClassicPollerModeContext; + +struct MfClassicPoller { + Iso14443_3aPoller* iso14443_3a_poller; + + MfClassicPollerState state; + MfClassicAuthState auth_state; + MfClassicCardState card_state; + + MfClassicType current_type_check; + uint8_t sectors_total; + MfClassicPollerModeContext mode_ctx; + + Crypto1* crypto; + BitBuffer* tx_plain_buffer; + BitBuffer* tx_encrypted_buffer; + BitBuffer* rx_plain_buffer; + BitBuffer* rx_encrypted_buffer; + MfClassicData* data; + + NfcGenericEvent general_event; + MfClassicPollerEvent mfc_event; + MfClassicPollerEventData mfc_event_data; + NfcGenericCallback callback; + void* context; +}; + +typedef struct { + uint8_t block; + MfClassicKeyType key_type; + MfClassicNt nt; +} MfClassicCollectNtContext; + +typedef struct { + uint8_t block_num; + MfClassicKey key; + MfClassicKeyType key_type; + MfClassicBlock block; +} MfClassicReadBlockContext; + +typedef struct { + uint8_t block_num; + MfClassicKey key; + MfClassicKeyType key_type; + MfClassicBlock block; +} MfClassicWriteBlockContext; + +typedef struct { + uint8_t block_num; + MfClassicKey key; + MfClassicKeyType key_type; + int32_t value; +} MfClassicReadValueContext; + +typedef struct { + uint8_t block_num; + MfClassicKey key; + MfClassicKeyType key_type; + MfClassicValueCommand value_cmd; + int32_t data; + int32_t new_value; +} MfClassicChangeValueContext; + +typedef struct { + MfClassicDeviceKeys keys; + uint8_t current_sector; +} MfClassicReadContext; + +typedef union { + MfClassicCollectNtContext collect_nt_context; + MfClassicAuthContext auth_context; + MfClassicReadBlockContext read_block_context; + MfClassicWriteBlockContext write_block_context; + MfClassicReadValueContext read_value_context; + MfClassicChangeValueContext change_value_context; + MfClassicReadContext read_context; +} MfClassicPollerContextData; + +MfClassicError mf_classic_process_error(Iso14443_3aError error); + +MfClassicPoller* mf_classic_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller); + +void mf_classic_poller_free(MfClassicPoller* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller_sync.c b/lib/nfc/protocols/mf_classic/mf_classic_poller_sync.c new file mode 100644 index 00000000000..69954452aa9 --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller_sync.c @@ -0,0 +1,521 @@ +#include "mf_classic_poller_sync.h" +#include "mf_classic_poller_i.h" + +#include + +#include + +#define TAG "MfClassicPoller" + +#define MF_CLASSIC_POLLER_COMPLETE_EVENT (1UL << 0) + +typedef enum { + MfClassicPollerCmdTypeCollectNt, + MfClassicPollerCmdTypeAuth, + MfClassicPollerCmdTypeReadBlock, + MfClassicPollerCmdTypeWriteBlock, + MfClassicPollerCmdTypeReadValue, + MfClassicPollerCmdTypeChangeValue, + + MfClassicPollerCmdTypeNum, +} MfClassicPollerCmdType; + +typedef struct { + MfClassicPollerCmdType cmd_type; + FuriThreadId thread_id; + MfClassicError error; + MfClassicPollerContextData data; +} MfClassicPollerContext; + +typedef MfClassicError ( + *MfClassicPollerCmdHandler)(MfClassicPoller* poller, MfClassicPollerContextData* data); + +static MfClassicError mf_classic_poller_collect_nt_handler( + MfClassicPoller* poller, + MfClassicPollerContextData* data) { + return mf_classic_poller_get_nt( + poller, + data->collect_nt_context.block, + data->collect_nt_context.key_type, + &data->collect_nt_context.nt); +} + +static MfClassicError + mf_classic_poller_auth_handler(MfClassicPoller* poller, MfClassicPollerContextData* data) { + return mf_classic_poller_auth( + poller, + data->auth_context.block_num, + &data->auth_context.key, + data->auth_context.key_type, + &data->auth_context); +} + +static MfClassicError mf_classic_poller_read_block_handler( + MfClassicPoller* poller, + MfClassicPollerContextData* data) { + MfClassicError error = MfClassicErrorNone; + + do { + error = mf_classic_poller_auth( + poller, + data->read_block_context.block_num, + &data->read_block_context.key, + data->read_block_context.key_type, + NULL); + if(error != MfClassicErrorNone) break; + + error = mf_classic_poller_read_block( + poller, data->read_block_context.block_num, &data->read_block_context.block); + if(error != MfClassicErrorNone) break; + + error = mf_classic_poller_halt(poller); + if(error != MfClassicErrorNone) break; + + } while(false); + + return error; +} + +static MfClassicError mf_classic_poller_write_block_handler( + MfClassicPoller* poller, + MfClassicPollerContextData* data) { + MfClassicError error = MfClassicErrorNone; + + do { + error = mf_classic_poller_auth( + poller, + data->read_block_context.block_num, + &data->read_block_context.key, + data->read_block_context.key_type, + NULL); + if(error != MfClassicErrorNone) break; + + error = mf_classic_poller_write_block( + poller, data->write_block_context.block_num, &data->write_block_context.block); + if(error != MfClassicErrorNone) break; + + error = mf_classic_poller_halt(poller); + if(error != MfClassicErrorNone) break; + + } while(false); + + return error; +} + +static MfClassicError mf_classic_poller_read_value_handler( + MfClassicPoller* poller, + MfClassicPollerContextData* data) { + MfClassicError error = MfClassicErrorNone; + + do { + error = mf_classic_poller_auth( + poller, + data->read_value_context.block_num, + &data->read_value_context.key, + data->read_value_context.key_type, + NULL); + if(error != MfClassicErrorNone) break; + + MfClassicBlock block = {}; + error = mf_classic_poller_read_block(poller, data->read_value_context.block_num, &block); + if(error != MfClassicErrorNone) break; + + if(!mf_classic_block_to_value(&block, &data->read_value_context.value, NULL)) { + error = MfClassicErrorProtocol; + break; + } + + error = mf_classic_poller_halt(poller); + if(error != MfClassicErrorNone) break; + + } while(false); + + return error; +} + +static MfClassicError mf_classic_poller_change_value_handler( + MfClassicPoller* poller, + MfClassicPollerContextData* data) { + MfClassicError error = MfClassicErrorNone; + + do { + error = mf_classic_poller_auth( + poller, + data->change_value_context.block_num, + &data->change_value_context.key, + data->change_value_context.key_type, + NULL); + if(error != MfClassicErrorNone) break; + + error = mf_classic_poller_value_cmd( + poller, + data->change_value_context.block_num, + data->change_value_context.value_cmd, + data->change_value_context.data); + if(error != MfClassicErrorNone) break; + + error = mf_classic_poller_value_transfer(poller, data->change_value_context.block_num); + if(error != MfClassicErrorNone) break; + + MfClassicBlock block = {}; + error = mf_classic_poller_read_block(poller, data->change_value_context.block_num, &block); + if(error != MfClassicErrorNone) break; + + error = mf_classic_poller_halt(poller); + if(error != MfClassicErrorNone) break; + + if(!mf_classic_block_to_value(&block, &data->change_value_context.new_value, NULL)) { + error = MfClassicErrorProtocol; + break; + } + + } while(false); + + return error; +} + +static const MfClassicPollerCmdHandler mf_classic_poller_cmd_handlers[MfClassicPollerCmdTypeNum] = { + [MfClassicPollerCmdTypeCollectNt] = mf_classic_poller_collect_nt_handler, + [MfClassicPollerCmdTypeAuth] = mf_classic_poller_auth_handler, + [MfClassicPollerCmdTypeReadBlock] = mf_classic_poller_read_block_handler, + [MfClassicPollerCmdTypeWriteBlock] = mf_classic_poller_write_block_handler, + [MfClassicPollerCmdTypeReadValue] = mf_classic_poller_read_value_handler, + [MfClassicPollerCmdTypeChangeValue] = mf_classic_poller_change_value_handler, +}; + +static NfcCommand mf_classic_poller_cmd_callback(NfcGenericEventEx event, void* context) { + furi_assert(event.poller); + furi_assert(event.parent_event_data); + furi_assert(context); + + MfClassicPollerContext* poller_context = context; + Iso14443_3aPollerEvent* iso14443_3a_event = event.parent_event_data; + MfClassicPoller* mfc_poller = event.poller; + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + poller_context->error = mf_classic_poller_cmd_handlers[poller_context->cmd_type]( + mfc_poller, &poller_context->data); + } else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) { + poller_context->error = mf_classic_process_error(iso14443_3a_event->data->error); + } + + furi_thread_flags_set(poller_context->thread_id, MF_CLASSIC_POLLER_COMPLETE_EVENT); + + return NfcCommandStop; +} + +static MfClassicError mf_classic_poller_cmd_execute(Nfc* nfc, MfClassicPollerContext* poller_ctx) { + furi_assert(poller_ctx->cmd_type < MfClassicPollerCmdTypeNum); + + poller_ctx->thread_id = furi_thread_get_current_id(); + + NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolMfClassic); + nfc_poller_start_ex(poller, mf_classic_poller_cmd_callback, poller_ctx); + furi_thread_flags_wait(MF_CLASSIC_POLLER_COMPLETE_EVENT, FuriFlagWaitAny, FuriWaitForever); + furi_thread_flags_clear(MF_CLASSIC_POLLER_COMPLETE_EVENT); + + nfc_poller_stop(poller); + nfc_poller_free(poller); + + return poller_ctx->error; +} + +MfClassicError mf_classic_poller_sync_collect_nt( + Nfc* nfc, + uint8_t block_num, + MfClassicKeyType key_type, + MfClassicNt* nt) { + furi_assert(nfc); + + MfClassicPollerContext poller_context = { + .cmd_type = MfClassicPollerCmdTypeCollectNt, + .data.collect_nt_context.block = block_num, + .data.collect_nt_context.key_type = key_type, + }; + + MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context); + + if(error == MfClassicErrorNone) { + if(nt) { + *nt = poller_context.data.collect_nt_context.nt; + } + } + + return error; +} + +MfClassicError mf_classic_poller_sync_auth( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + MfClassicAuthContext* data) { + furi_assert(nfc); + furi_assert(key); + + MfClassicPollerContext poller_context = { + .cmd_type = MfClassicPollerCmdTypeAuth, + .data.auth_context.block_num = block_num, + .data.auth_context.key = *key, + .data.auth_context.key_type = key_type, + }; + + MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context); + + if(error == MfClassicErrorNone) { + if(data) { + *data = poller_context.data.auth_context; + } + } + + return error; +} + +MfClassicError mf_classic_poller_sync_read_block( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + MfClassicBlock* data) { + furi_assert(nfc); + furi_assert(key); + furi_assert(data); + + MfClassicPollerContext poller_context = { + .cmd_type = MfClassicPollerCmdTypeReadBlock, + .data.read_block_context.block_num = block_num, + .data.read_block_context.key = *key, + .data.read_block_context.key_type = key_type, + }; + + MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context); + + if(error == MfClassicErrorNone) { + *data = poller_context.data.read_block_context.block; + } + + return error; +} + +MfClassicError mf_classic_poller_sync_write_block( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + MfClassicBlock* data) { + furi_assert(nfc); + furi_assert(key); + furi_assert(data); + + MfClassicPollerContext poller_context = { + .cmd_type = MfClassicPollerCmdTypeWriteBlock, + .data.write_block_context.block_num = block_num, + .data.write_block_context.key = *key, + .data.write_block_context.key_type = key_type, + .data.write_block_context.block = *data, + }; + + MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context); + + return error; +} + +MfClassicError mf_classic_poller_sync_read_value( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + int32_t* value) { + furi_assert(nfc); + furi_assert(key); + furi_assert(value); + + MfClassicPollerContext poller_context = { + .cmd_type = MfClassicPollerCmdTypeReadValue, + .data.write_block_context.block_num = block_num, + .data.write_block_context.key = *key, + .data.write_block_context.key_type = key_type, + }; + + MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context); + + if(error == MfClassicErrorNone) { + *value = poller_context.data.read_value_context.value; + } + + return error; +} + +MfClassicError mf_classic_poller_sync_change_value( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + int32_t data, + int32_t* new_value) { + furi_assert(nfc); + furi_assert(key); + furi_assert(new_value); + + MfClassicValueCommand command = MfClassicValueCommandRestore; + int32_t command_data = 0; + if(data > 0) { + command = MfClassicValueCommandIncrement; + command_data = data; + } else if(data < 0) { + command = MfClassicValueCommandDecrement; + command_data = -data; + } + + MfClassicPollerContext poller_context = { + .cmd_type = MfClassicPollerCmdTypeChangeValue, + .data.change_value_context.block_num = block_num, + .data.change_value_context.key = *key, + .data.change_value_context.key_type = key_type, + .data.change_value_context.value_cmd = command, + .data.change_value_context.data = command_data, + }; + + MfClassicError error = mf_classic_poller_cmd_execute(nfc, &poller_context); + + if(error == MfClassicErrorNone) { + *new_value = poller_context.data.change_value_context.new_value; + } + + return error; +} + +static bool mf_classic_poller_read_get_next_key( + MfClassicReadContext* read_ctx, + uint8_t* sector_num, + MfClassicKey* key, + MfClassicKeyType* key_type) { + bool next_key_found = false; + + for(uint8_t i = read_ctx->current_sector; i < MF_CLASSIC_TOTAL_SECTORS_MAX; i++) { + if(FURI_BIT(read_ctx->keys.key_a_mask, i)) { + FURI_BIT_CLEAR(read_ctx->keys.key_a_mask, i); + *key = read_ctx->keys.key_a[i]; + *key_type = MfClassicKeyTypeA; + *sector_num = i; + + next_key_found = true; + break; + } + if(FURI_BIT(read_ctx->keys.key_b_mask, i)) { + FURI_BIT_CLEAR(read_ctx->keys.key_b_mask, i); + *key = read_ctx->keys.key_b[i]; + *key_type = MfClassicKeyTypeB; + *sector_num = i; + + next_key_found = true; + read_ctx->current_sector = i; + break; + } + } + + return next_key_found; +} + +NfcCommand mf_classic_poller_read_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.protocol == NfcProtocolMfClassic); + + NfcCommand command = NfcCommandContinue; + MfClassicPollerContext* poller_context = context; + MfClassicPollerEvent* mfc_event = event.event_data; + + if(mfc_event->type == MfClassicPollerEventTypeCardLost) { + poller_context->error = MfClassicErrorNotPresent; + command = NfcCommandStop; + } else if(mfc_event->type == MfClassicPollerEventTypeRequestMode) { + mfc_event->data->poller_mode.mode = MfClassicPollerModeRead; + } else if(mfc_event->type == MfClassicPollerEventTypeRequestReadSector) { + MfClassicPollerEventDataReadSectorRequest* req_data = + &mfc_event->data->read_sector_request_data; + MfClassicKey key = {}; + MfClassicKeyType key_type = MfClassicKeyTypeA; + uint8_t sector_num = 0; + if(mf_classic_poller_read_get_next_key( + &poller_context->data.read_context, §or_num, &key, &key_type)) { + req_data->sector_num = sector_num; + req_data->key = key; + req_data->key_type = key_type; + req_data->key_provided = true; + } else { + req_data->key_provided = false; + } + } else if(mfc_event->type == MfClassicPollerEventTypeSuccess) { + command = NfcCommandStop; + } + + if(command == NfcCommandStop) { + furi_thread_flags_set(poller_context->thread_id, MF_CLASSIC_POLLER_COMPLETE_EVENT); + } + + return command; +} + +MfClassicError + mf_classic_poller_sync_read(Nfc* nfc, const MfClassicDeviceKeys* keys, MfClassicData* data) { + furi_assert(nfc); + furi_assert(keys); + furi_assert(data); + + MfClassicError error = MfClassicErrorNone; + MfClassicPollerContext poller_context = {}; + poller_context.thread_id = furi_thread_get_current_id(); + poller_context.data.read_context.keys = *keys; + + NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolMfClassic); + nfc_poller_start(poller, mf_classic_poller_read_callback, &poller_context); + furi_thread_flags_wait(MF_CLASSIC_POLLER_COMPLETE_EVENT, FuriFlagWaitAny, FuriWaitForever); + furi_thread_flags_clear(MF_CLASSIC_POLLER_COMPLETE_EVENT); + + nfc_poller_stop(poller); + + if(poller_context.error != MfClassicErrorNone) { + error = poller_context.error; + } else { + const MfClassicData* mfc_data = nfc_poller_get_data(poller); + uint8_t sectors_read = 0; + uint8_t keys_found = 0; + + mf_classic_get_read_sectors_and_keys(mfc_data, §ors_read, &keys_found); + if((sectors_read > 0) || (keys_found > 0)) { + mf_classic_copy(data, mfc_data); + } else { + error = MfClassicErrorNotPresent; + } + } + + nfc_poller_free(poller); + + return error; +} + +MfClassicError mf_classic_poller_sync_detect_type(Nfc* nfc, MfClassicType* type) { + furi_assert(nfc); + furi_assert(type); + + MfClassicError error = MfClassicErrorNone; + + const uint8_t mf_classic_verify_block[MfClassicTypeNum] = { + [MfClassicTypeMini] = 0, + [MfClassicType1k] = 62, + [MfClassicType4k] = 254, + }; + + size_t i = 0; + for(i = 0; i < COUNT_OF(mf_classic_verify_block); i++) { + error = mf_classic_poller_sync_collect_nt( + nfc, mf_classic_verify_block[MfClassicTypeNum - i - 1], MfClassicKeyTypeA, NULL); + if(error == MfClassicErrorNone) { + *type = MfClassicTypeNum - i - 1; + break; + } + } + + return error; +} diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller_sync.h b/lib/nfc/protocols/mf_classic/mf_classic_poller_sync.h new file mode 100644 index 00000000000..d384e46e4e8 --- /dev/null +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller_sync.h @@ -0,0 +1,59 @@ +#pragma once + +#include "mf_classic.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +MfClassicError mf_classic_poller_sync_collect_nt( + Nfc* nfc, + uint8_t block_num, + MfClassicKeyType key_type, + MfClassicNt* nt); + +MfClassicError mf_classic_poller_sync_auth( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + MfClassicAuthContext* data); + +MfClassicError mf_classic_poller_sync_read_block( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + MfClassicBlock* data); + +MfClassicError mf_classic_poller_sync_write_block( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + MfClassicBlock* data); + +MfClassicError mf_classic_poller_sync_read_value( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + int32_t* value); + +MfClassicError mf_classic_poller_sync_change_value( + Nfc* nfc, + uint8_t block_num, + MfClassicKey* key, + MfClassicKeyType key_type, + int32_t data, + int32_t* new_value); + +MfClassicError mf_classic_poller_sync_detect_type(Nfc* nfc, MfClassicType* type); + +MfClassicError + mf_classic_poller_sync_read(Nfc* nfc, const MfClassicDeviceKeys* keys, MfClassicData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire.c b/lib/nfc/protocols/mf_desfire/mf_desfire.c new file mode 100644 index 00000000000..b83198a25e6 --- /dev/null +++ b/lib/nfc/protocols/mf_desfire/mf_desfire.c @@ -0,0 +1,290 @@ +#include "mf_desfire_i.h" + +#include + +#define MF_DESFIRE_PROTOCOL_NAME "Mifare DESFire" + +const NfcDeviceBase nfc_device_mf_desfire = { + .protocol_name = MF_DESFIRE_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)mf_desfire_alloc, + .free = (NfcDeviceFree)mf_desfire_free, + .reset = (NfcDeviceReset)mf_desfire_reset, + .copy = (NfcDeviceCopy)mf_desfire_copy, + .verify = (NfcDeviceVerify)mf_desfire_verify, + .load = (NfcDeviceLoad)mf_desfire_load, + .save = (NfcDeviceSave)mf_desfire_save, + .is_equal = (NfcDeviceEqual)mf_desfire_is_equal, + .get_name = (NfcDeviceGetName)mf_desfire_get_device_name, + .get_uid = (NfcDeviceGetUid)mf_desfire_get_uid, + .set_uid = (NfcDeviceSetUid)mf_desfire_set_uid, + .get_base_data = (NfcDeviceGetBaseData)mf_desfire_get_base_data, +}; + +MfDesfireData* mf_desfire_alloc() { + MfDesfireData* data = malloc(sizeof(MfDesfireData)); + data->iso14443_4a_data = iso14443_4a_alloc(); + data->master_key_versions = simple_array_alloc(&mf_desfire_key_version_array_config); + data->application_ids = simple_array_alloc(&mf_desfire_app_id_array_config); + data->applications = simple_array_alloc(&mf_desfire_application_array_config); + + return data; +} + +void mf_desfire_free(MfDesfireData* data) { + furi_assert(data); + + mf_desfire_reset(data); + simple_array_free(data->applications); + simple_array_free(data->application_ids); + simple_array_free(data->master_key_versions); + iso14443_4a_free(data->iso14443_4a_data); + free(data); +} + +void mf_desfire_reset(MfDesfireData* data) { + furi_assert(data); + + iso14443_4a_reset(data->iso14443_4a_data); + + memset(&data->version, 0, sizeof(MfDesfireVersion)); + memset(&data->free_memory, 0, sizeof(MfDesfireFreeMemory)); + + simple_array_reset(data->master_key_versions); + simple_array_reset(data->application_ids); + simple_array_reset(data->applications); +} + +void mf_desfire_copy(MfDesfireData* data, const MfDesfireData* other) { + furi_assert(data); + furi_assert(other); + + mf_desfire_reset(data); + + iso14443_4a_copy(data->iso14443_4a_data, other->iso14443_4a_data); + + data->version = other->version; + data->free_memory = other->free_memory; + data->master_key_settings = other->master_key_settings; + + simple_array_copy(data->master_key_versions, other->master_key_versions); + simple_array_copy(data->application_ids, other->application_ids); + simple_array_copy(data->applications, other->applications); +} + +bool mf_desfire_verify(MfDesfireData* data, const FuriString* device_type) { + UNUSED(data); + return furi_string_equal_str(device_type, MF_DESFIRE_PROTOCOL_NAME); +} + +bool mf_desfire_load(MfDesfireData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + + FuriString* prefix = furi_string_alloc(); + + bool success = false; + + do { + if(!iso14443_4a_load(data->iso14443_4a_data, ff, version)) break; + + if(!mf_desfire_version_load(&data->version, ff)) break; + if(!mf_desfire_free_memory_load(&data->free_memory, ff)) break; + if(!mf_desfire_key_settings_load( + &data->master_key_settings, MF_DESFIRE_FFF_PICC_PREFIX, ff)) + break; + + const uint32_t master_key_version_count = data->master_key_settings.max_keys; + simple_array_init(data->master_key_versions, master_key_version_count); + + uint32_t i; + for(i = 0; i < master_key_version_count; ++i) { + if(!mf_desfire_key_version_load( + simple_array_get(data->master_key_versions, i), + MF_DESFIRE_FFF_PICC_PREFIX, + i, + ff)) + break; + } + + if(i != master_key_version_count) break; + + uint32_t application_count; + if(!mf_desfire_application_count_load(&application_count, ff)) break; + + if(application_count > 0) { + simple_array_init(data->application_ids, application_count); + if(!mf_desfire_application_ids_load( + simple_array_get_data(data->application_ids), application_count, ff)) + break; + + simple_array_init(data->applications, application_count); + for(i = 0; i < application_count; ++i) { + const MfDesfireApplicationId* app_id = simple_array_cget(data->application_ids, i); + furi_string_printf( + prefix, + "%s %02x%02x%02x", + MF_DESFIRE_FFF_APP_PREFIX, + app_id->data[0], + app_id->data[1], + app_id->data[2]); + + if(!mf_desfire_application_load( + simple_array_get(data->applications, i), furi_string_get_cstr(prefix), ff)) + break; + } + + if(i != application_count) break; + } + + success = true; + } while(false); + + furi_string_free(prefix); + return success; +} + +bool mf_desfire_save(const MfDesfireData* data, FlipperFormat* ff) { + furi_assert(data); + + FuriString* prefix = furi_string_alloc(); + + bool success = false; + + do { + if(!iso14443_4a_save(data->iso14443_4a_data, ff)) break; + + if(!flipper_format_write_comment_cstr(ff, MF_DESFIRE_PROTOCOL_NAME " specific data")) + break; + if(!mf_desfire_version_save(&data->version, ff)) break; + if(!mf_desfire_free_memory_save(&data->free_memory, ff)) break; + if(!mf_desfire_key_settings_save( + &data->master_key_settings, MF_DESFIRE_FFF_PICC_PREFIX, ff)) + break; + + const uint32_t master_key_version_count = + simple_array_get_count(data->master_key_versions); + + uint32_t i; + for(i = 0; i < master_key_version_count; ++i) { + if(!mf_desfire_key_version_save( + simple_array_cget(data->master_key_versions, i), + MF_DESFIRE_FFF_PICC_PREFIX, + i, + ff)) + break; + } + + if(i != master_key_version_count) break; + + const uint32_t application_count = simple_array_get_count(data->application_ids); + if(!mf_desfire_application_count_save(&application_count, ff)) break; + + if(application_count > 0) { + if(!mf_desfire_application_ids_save( + simple_array_cget_data(data->application_ids), application_count, ff)) + break; + + for(i = 0; i < application_count; ++i) { + const MfDesfireApplicationId* app_id = simple_array_cget(data->application_ids, i); + furi_string_printf( + prefix, + "%s %02x%02x%02x", + MF_DESFIRE_FFF_APP_PREFIX, + app_id->data[0], + app_id->data[1], + app_id->data[2]); + + const MfDesfireApplication* app = simple_array_cget(data->applications, i); + if(!mf_desfire_application_save(app, furi_string_get_cstr(prefix), ff)) break; + } + + if(i != application_count) break; + } + + success = true; + } while(false); + + furi_string_free(prefix); + return success; +} + +bool mf_desfire_is_equal(const MfDesfireData* data, const MfDesfireData* other) { + furi_assert(data); + furi_assert(other); + + return iso14443_4a_is_equal(data->iso14443_4a_data, other->iso14443_4a_data) && + memcmp(&data->version, &other->version, sizeof(MfDesfireVersion)) == 0 && + memcmp(&data->free_memory, &other->free_memory, sizeof(MfDesfireFreeMemory)) == 0 && + memcmp( + &data->master_key_settings, + &other->master_key_settings, + sizeof(MfDesfireKeySettings)) == 0 && + simple_array_is_equal(data->master_key_versions, other->master_key_versions) && + simple_array_is_equal(data->application_ids, other->application_ids) && + simple_array_is_equal(data->applications, other->applications); +} + +const char* mf_desfire_get_device_name(const MfDesfireData* data, NfcDeviceNameType name_type) { + UNUSED(data); + UNUSED(name_type); + return MF_DESFIRE_PROTOCOL_NAME; +} + +const uint8_t* mf_desfire_get_uid(const MfDesfireData* data, size_t* uid_len) { + furi_assert(data); + + return iso14443_4a_get_uid(data->iso14443_4a_data, uid_len); +} + +bool mf_desfire_set_uid(MfDesfireData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + return iso14443_4a_set_uid(data->iso14443_4a_data, uid, uid_len); +} + +Iso14443_4aData* mf_desfire_get_base_data(const MfDesfireData* data) { + furi_assert(data); + + return data->iso14443_4a_data; +} + +const MfDesfireApplication* + mf_desfire_get_application(const MfDesfireData* data, const MfDesfireApplicationId* app_id) { + MfDesfireApplication* app = NULL; + + for(uint32_t i = 0; i < simple_array_get_count(data->application_ids); ++i) { + const MfDesfireApplicationId* current_app_id = simple_array_cget(data->application_ids, i); + if(memcmp(app_id, current_app_id, sizeof(MfDesfireApplicationId)) == 0) { + app = simple_array_get(data->applications, i); + } + } + + return app; +} + +const MfDesfireFileSettings* + mf_desfire_get_file_settings(const MfDesfireApplication* data, const MfDesfireFileId* file_id) { + MfDesfireFileSettings* file_settings = NULL; + + for(uint32_t i = 0; i < simple_array_get_count(data->file_ids); ++i) { + const MfDesfireFileId* current_file_id = simple_array_cget(data->file_ids, i); + if(*file_id == *current_file_id) { + file_settings = simple_array_get(data->file_settings, i); + } + } + + return file_settings; +} + +const MfDesfireFileData* + mf_desfire_get_file_data(const MfDesfireApplication* data, const MfDesfireFileId* file_id) { + MfDesfireFileData* file_data = NULL; + + for(uint32_t i = 0; i < simple_array_get_count(data->file_ids); ++i) { + const MfDesfireFileId* current_file_id = simple_array_cget(data->file_ids, i); + if(*file_id == *current_file_id) { + file_data = simple_array_get(data->file_data, i); + } + } + + return file_data; +} diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire.h b/lib/nfc/protocols/mf_desfire/mf_desfire.h new file mode 100644 index 00000000000..8505b792aa5 --- /dev/null +++ b/lib/nfc/protocols/mf_desfire/mf_desfire.h @@ -0,0 +1,192 @@ +#pragma once + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MF_DESFIRE_CMD_GET_VERSION (0x60) +#define MF_DESFIRE_CMD_GET_FREE_MEMORY (0x6E) +#define MF_DESFIRE_CMD_GET_KEY_SETTINGS (0x45) +#define MF_DESFIRE_CMD_GET_KEY_VERSION (0x64) +#define MF_DESFIRE_CMD_GET_APPLICATION_IDS (0x6A) +#define MF_DESFIRE_CMD_SELECT_APPLICATION (0x5A) +#define MF_DESFIRE_CMD_GET_FILE_IDS (0x6F) +#define MF_DESFIRE_CMD_GET_FILE_SETTINGS (0xF5) + +#define MF_DESFIRE_CMD_READ_DATA (0xBD) +#define MF_DESFIRE_CMD_GET_VALUE (0x6C) +#define MF_DESFIRE_CMD_READ_RECORDS (0xBB) + +#define MF_DESFIRE_FLAG_HAS_NEXT (0xAF) + +#define MF_DESFIRE_MAX_KEYS (14) +#define MF_DESFIRE_MAX_FILES (32) + +#define MF_DESFIRE_UID_SIZE (7) +#define MF_DESFIRE_BATCH_SIZE (5) +#define MF_DESFIRE_APP_ID_SIZE (3) +#define MF_DESFIRE_VALUE_SIZE (4) + +typedef struct { + uint8_t hw_vendor; + uint8_t hw_type; + uint8_t hw_subtype; + uint8_t hw_major; + uint8_t hw_minor; + uint8_t hw_storage; + uint8_t hw_proto; + + uint8_t sw_vendor; + uint8_t sw_type; + uint8_t sw_subtype; + uint8_t sw_major; + uint8_t sw_minor; + uint8_t sw_storage; + uint8_t sw_proto; + + uint8_t uid[MF_DESFIRE_UID_SIZE]; + uint8_t batch[MF_DESFIRE_BATCH_SIZE]; + uint8_t prod_week; + uint8_t prod_year; +} MfDesfireVersion; + +typedef struct { + uint32_t bytes_free; + bool is_present; +} MfDesfireFreeMemory; // EV1+ only + +typedef struct { + bool is_master_key_changeable; + bool is_free_directory_list; + bool is_free_create_delete; + bool is_config_changeable; + uint8_t change_key_id; + uint8_t max_keys; + uint8_t flags; +} MfDesfireKeySettings; + +typedef uint8_t MfDesfireKeyVersion; + +typedef struct { + MfDesfireKeySettings key_settings; + SimpleArray* key_versions; +} MfDesfireKeyConfiguration; + +typedef enum { + MfDesfireFileTypeStandard = 0, + MfDesfireFileTypeBackup = 1, + MfDesfireFileTypeValue = 2, + MfDesfireFileTypeLinearRecord = 3, + MfDesfireFileTypeCyclicRecord = 4, +} MfDesfireFileType; + +typedef enum { + MfDesfireFileCommunicationSettingsPlaintext = 0, + MfDesfireFileCommunicationSettingsAuthenticated = 1, + MfDesfireFileCommunicationSettingsEnciphered = 3, +} MfDesfireFileCommunicationSettings; + +typedef uint8_t MfDesfireFileId; +typedef uint16_t MfDesfireFileAccessRights; + +typedef struct { + MfDesfireFileType type; + MfDesfireFileCommunicationSettings comm; + MfDesfireFileAccessRights access_rights; + union { + struct { + uint32_t size; + } data; + struct { + uint32_t lo_limit; + uint32_t hi_limit; + uint32_t limited_credit_value; + bool limited_credit_enabled; + } value; + struct { + uint32_t size; + uint32_t max; + uint32_t cur; + } record; + }; +} MfDesfireFileSettings; + +typedef struct { + SimpleArray* data; +} MfDesfireFileData; + +typedef struct { + uint8_t data[MF_DESFIRE_APP_ID_SIZE]; +} MfDesfireApplicationId; + +typedef struct MfDesfireApplication { + MfDesfireKeySettings key_settings; + SimpleArray* key_versions; + SimpleArray* file_ids; + SimpleArray* file_settings; + SimpleArray* file_data; +} MfDesfireApplication; + +typedef enum { + MfDesfireErrorNone, + MfDesfireErrorNotPresent, + MfDesfireErrorProtocol, + MfDesfireErrorTimeout, +} MfDesfireError; + +typedef struct { + Iso14443_4aData* iso14443_4a_data; + MfDesfireVersion version; + MfDesfireFreeMemory free_memory; + MfDesfireKeySettings master_key_settings; + SimpleArray* master_key_versions; + SimpleArray* application_ids; + SimpleArray* applications; +} MfDesfireData; + +extern const NfcDeviceBase nfc_device_mf_desfire; + +// Virtual methods + +MfDesfireData* mf_desfire_alloc(); + +void mf_desfire_free(MfDesfireData* data); + +void mf_desfire_reset(MfDesfireData* data); + +void mf_desfire_copy(MfDesfireData* data, const MfDesfireData* other); + +bool mf_desfire_verify(MfDesfireData* data, const FuriString* device_type); + +bool mf_desfire_load(MfDesfireData* data, FlipperFormat* ff, uint32_t version); + +bool mf_desfire_save(const MfDesfireData* data, FlipperFormat* ff); + +bool mf_desfire_is_equal(const MfDesfireData* data, const MfDesfireData* other); + +const char* mf_desfire_get_device_name(const MfDesfireData* data, NfcDeviceNameType name_type); + +const uint8_t* mf_desfire_get_uid(const MfDesfireData* data, size_t* uid_len); + +bool mf_desfire_set_uid(MfDesfireData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_4aData* mf_desfire_get_base_data(const MfDesfireData* data); + +// Getters and tests + +const MfDesfireApplication* + mf_desfire_get_application(const MfDesfireData* data, const MfDesfireApplicationId* app_id); + +const MfDesfireFileSettings* + mf_desfire_get_file_settings(const MfDesfireApplication* data, const MfDesfireFileId* file_id); + +const MfDesfireFileData* + mf_desfire_get_file_data(const MfDesfireApplication* data, const MfDesfireFileId* file_id); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c new file mode 100644 index 00000000000..8e65eca5a54 --- /dev/null +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_i.c @@ -0,0 +1,790 @@ +#include "mf_desfire_i.h" + +#define BITS_IN_BYTE (8U) + +#define MF_DESFIRE_FFF_VERSION_KEY \ + MF_DESFIRE_FFF_PICC_PREFIX " " \ + "Version" +#define MF_DESFIRE_FFF_FREE_MEM_KEY \ + MF_DESFIRE_FFF_PICC_PREFIX " " \ + "Free Memory" + +#define MF_DESFIRE_FFF_CHANGE_KEY_ID_KEY "Change Key ID" +#define MF_DESFIRE_FFF_CONFIG_CHANGEABLE_KEY "Config Changeable" +#define MF_DESFIRE_FFF_FREE_CREATE_DELETE_KEY "Free Create Delete" +#define MF_DESFIRE_FFF_FREE_DIR_LIST_KEY "Free Directory List" +#define MF_DESFIRE_FFF_KEY_CHANGEABLE_KEY "Key Changeable" +#define MF_DESFIRE_FFF_FLAGS_KEY "Flags" +#define MF_DESFIRE_FFF_MAX_KEYS_KEY "Max Keys" + +#define MF_DESFIRE_FFF_KEY_SUB_PREFIX "Key" +#define MF_DESFIRE_FFF_KEY_VERSION_KEY "Version" + +#define MF_DESFIRE_FFF_APPLICATION_COUNT_KEY \ + MF_DESFIRE_FFF_APP_PREFIX " " \ + "Count" +#define MF_DESFIRE_FFF_APPLICATION_IDS_KEY \ + MF_DESFIRE_FFF_APP_PREFIX " " \ + "IDs" + +#define MF_DESFIRE_FFF_FILE_SUB_PREFIX "File" +#define MF_DESFIRE_FFF_FILE_IDS_KEY \ + MF_DESFIRE_FFF_FILE_SUB_PREFIX " " \ + "IDs" +#define MF_DESFIRE_FFF_FILE_TYPE_KEY "Type" +#define MF_DESFIRE_FFF_FILE_COMM_SETTINGS_KEY "Communication Settings" +#define MF_DESFIRE_FFF_FILE_ACCESS_RIGHTS_KEY "Access Rights" + +#define MF_DESFIRE_FFF_FILE_SIZE_KEY "Size" + +#define MF_DESFIRE_FFF_FILE_HI_LIMIT_KEY "Hi Limit" +#define MF_DESFIRE_FFF_FILE_LO_LIMIT_KEY "Lo Limit" +#define MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_VALUE_KEY "Limited Credit Value" +#define MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_ENABLED_KEY "Limited Credit Enabled" + +#define MF_DESFIRE_FFF_FILE_MAX_KEY "Max" +#define MF_DESFIRE_FFF_FILE_CUR_KEY "Cur" + +bool mf_desfire_version_parse(MfDesfireVersion* data, const BitBuffer* buf) { + const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireVersion); + + if(can_parse) { + bit_buffer_write_bytes(buf, data, sizeof(MfDesfireVersion)); + } + + return can_parse; +} + +bool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* buf) { + typedef struct FURI_PACKED { + uint32_t bytes_free : 3 * BITS_IN_BYTE; + } MfDesfireFreeMemoryLayout; + + const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireFreeMemoryLayout); + + if(can_parse) { + MfDesfireFreeMemoryLayout layout; + bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireFreeMemoryLayout)); + data->bytes_free = layout.bytes_free; + } + + data->is_present = can_parse; + + return can_parse; +} + +bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf) { + typedef struct FURI_PACKED { + bool is_master_key_changeable : 1; + bool is_free_directory_list : 1; + bool is_free_create_delete : 1; + bool is_config_changeable : 1; + uint8_t change_key_id : 4; + uint8_t max_keys : 4; + uint8_t flags : 4; + } MfDesfireKeySettingsLayout; + + const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireKeySettingsLayout); + + if(can_parse) { + MfDesfireKeySettingsLayout layout; + bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireKeySettingsLayout)); + + data->is_master_key_changeable = layout.is_master_key_changeable; + data->is_free_directory_list = layout.is_free_directory_list; + data->is_free_create_delete = layout.is_free_create_delete; + data->is_config_changeable = layout.is_config_changeable; + + data->change_key_id = layout.change_key_id; + data->max_keys = layout.max_keys; + data->flags = layout.flags; + } + + return can_parse; +} + +bool mf_desfire_key_version_parse(MfDesfireKeyVersion* data, const BitBuffer* buf) { + const bool can_parse = bit_buffer_get_size_bytes(buf) == sizeof(MfDesfireKeyVersion); + + if(can_parse) { + bit_buffer_write_bytes(buf, data, sizeof(MfDesfireKeyVersion)); + } + + return can_parse; +} + +bool mf_desfire_application_id_parse( + MfDesfireApplicationId* data, + uint32_t index, + const BitBuffer* buf) { + const bool can_parse = + bit_buffer_get_size_bytes(buf) >= + (index * sizeof(MfDesfireApplicationId) + sizeof(MfDesfireApplicationId)); + + if(can_parse) { + bit_buffer_write_bytes_mid( + buf, data, index * sizeof(MfDesfireApplicationId), sizeof(MfDesfireApplicationId)); + } + + return can_parse; +} + +bool mf_desfire_file_id_parse(MfDesfireFileId* data, uint32_t index, const BitBuffer* buf) { + const bool can_parse = bit_buffer_get_size_bytes(buf) >= + (index * sizeof(MfDesfireFileId) + sizeof(MfDesfireFileId)); + if(can_parse) { + bit_buffer_write_bytes_mid( + buf, data, index * sizeof(MfDesfireFileId), sizeof(MfDesfireFileId)); + } + + return can_parse; +} + +bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer* buf) { + bool parsed = false; + + typedef struct FURI_PACKED { + uint8_t type; + uint8_t comm; + uint16_t access_rights; + } MfDesfireFileSettingsHeader; + + typedef struct FURI_PACKED { + uint32_t size : 3 * BITS_IN_BYTE; + } MfDesfireFileSettingsData; + + typedef struct FURI_PACKED { + uint32_t lo_limit; + uint32_t hi_limit; + uint32_t limited_credit_value; + uint8_t limited_credit_enabled; + } MfDesfireFileSettingsValue; + + typedef struct FURI_PACKED { + uint32_t size : 3 * BITS_IN_BYTE; + uint32_t max : 3 * BITS_IN_BYTE; + uint32_t cur : 3 * BITS_IN_BYTE; + } MfDesfireFileSettingsRecord; + + typedef struct FURI_PACKED { + MfDesfireFileSettingsHeader header; + union { + MfDesfireFileSettingsData data; + MfDesfireFileSettingsValue value; + MfDesfireFileSettingsRecord record; + }; + } MfDesfireFileSettingsLayout; + + do { + const size_t data_size = bit_buffer_get_size_bytes(buf); + const size_t min_data_size = + sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsData); + + if(data_size < min_data_size) break; + + MfDesfireFileSettingsLayout layout; + bit_buffer_write_bytes(buf, &layout, sizeof(MfDesfireFileSettingsLayout)); + + data->type = layout.header.type; + data->comm = layout.header.comm; + data->access_rights = layout.header.access_rights; + + if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) { + if(data_size != min_data_size) break; + + data->data.size = layout.data.size; + + } else if(data->type == MfDesfireFileTypeValue) { + if(data_size != + sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsValue)) + break; + + data->value.lo_limit = layout.value.lo_limit; + data->value.hi_limit = layout.value.hi_limit; + data->value.limited_credit_value = layout.value.limited_credit_value; + data->value.limited_credit_enabled = layout.value.limited_credit_enabled; + + } else if( + data->type == MfDesfireFileTypeLinearRecord || + data->type == MfDesfireFileTypeCyclicRecord) { + if(data_size != + sizeof(MfDesfireFileSettingsHeader) + sizeof(MfDesfireFileSettingsRecord)) + break; + + data->record.size = layout.record.size; + data->record.max = layout.record.max; + data->record.cur = layout.record.cur; + + } else { + break; + } + + parsed = true; + } while(false); + + return parsed; +} + +bool mf_desfire_file_data_parse(MfDesfireFileData* data, const BitBuffer* buf) { + const size_t data_size = bit_buffer_get_size_bytes(buf); + + if(data_size > 0) { + simple_array_init(data->data, data_size); + bit_buffer_write_bytes(buf, simple_array_get_data(data->data), data_size); + } + + // Success no matter whether there is data or not + return true; +} + +void mf_desfire_file_data_init(MfDesfireFileData* data) { + data->data = simple_array_alloc(&simple_array_config_uint8_t); +} + +void mf_desfire_application_init(MfDesfireApplication* data) { + data->key_versions = simple_array_alloc(&mf_desfire_key_version_array_config); + data->file_ids = simple_array_alloc(&mf_desfire_file_id_array_config); + data->file_settings = simple_array_alloc(&mf_desfire_file_settings_array_config); + data->file_data = simple_array_alloc(&mf_desfire_file_data_array_config); +} + +void mf_desfire_file_data_reset(MfDesfireFileData* data) { + simple_array_free(data->data); + memset(data, 0, sizeof(MfDesfireFileData)); +} + +void mf_desfire_application_reset(MfDesfireApplication* data) { + simple_array_free(data->key_versions); + simple_array_free(data->file_ids); + simple_array_free(data->file_settings); + simple_array_free(data->file_data); + memset(data, 0, sizeof(MfDesfireApplication)); +} + +void mf_desfire_file_data_copy(MfDesfireFileData* data, const MfDesfireFileData* other) { + simple_array_copy(data->data, other->data); +} + +void mf_desfire_application_copy(MfDesfireApplication* data, const MfDesfireApplication* other) { + data->key_settings = other->key_settings; + simple_array_copy(data->key_versions, other->key_versions); + simple_array_copy(data->file_ids, other->file_ids); + simple_array_copy(data->file_settings, other->file_settings); + simple_array_copy(data->file_data, other->file_data); +} + +bool mf_desfire_version_load(MfDesfireVersion* data, FlipperFormat* ff) { + return flipper_format_read_hex( + ff, MF_DESFIRE_FFF_VERSION_KEY, (uint8_t*)data, sizeof(MfDesfireVersion)); +} + +bool mf_desfire_free_memory_load(MfDesfireFreeMemory* data, FlipperFormat* ff) { + data->is_present = flipper_format_key_exist(ff, MF_DESFIRE_FFF_FREE_MEM_KEY); + return data->is_present ? + flipper_format_read_uint32(ff, MF_DESFIRE_FFF_FREE_MEM_KEY, &data->bytes_free, 1) : + true; +} + +bool mf_desfire_key_settings_load( + MfDesfireKeySettings* data, + const char* prefix, + FlipperFormat* ff) { + bool success = false; + + FuriString* key = furi_string_alloc(); + + do { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_CHANGE_KEY_ID_KEY); + if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), &data->change_key_id, 1)) break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_CONFIG_CHANGEABLE_KEY); + if(!flipper_format_read_bool(ff, furi_string_get_cstr(key), &data->is_config_changeable, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FREE_CREATE_DELETE_KEY); + if(!flipper_format_read_bool( + ff, furi_string_get_cstr(key), &data->is_free_create_delete, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FREE_DIR_LIST_KEY); + if(!flipper_format_read_bool( + ff, furi_string_get_cstr(key), &data->is_free_directory_list, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_KEY_CHANGEABLE_KEY); + if(!flipper_format_read_bool( + ff, furi_string_get_cstr(key), &data->is_master_key_changeable, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FLAGS_KEY); + if(flipper_format_key_exist(ff, furi_string_get_cstr(key))) { + if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), &data->flags, 1)) break; + } + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_MAX_KEYS_KEY); + if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), &data->max_keys, 1)) break; + + success = true; + } while(false); + + furi_string_free(key); + return success; +} + +bool mf_desfire_key_version_load( + MfDesfireKeyVersion* data, + const char* prefix, + uint32_t index, + FlipperFormat* ff) { + FuriString* key = furi_string_alloc_printf( + "%s %s %lu %s", + prefix, + MF_DESFIRE_FFF_KEY_SUB_PREFIX, + index, + MF_DESFIRE_FFF_KEY_VERSION_KEY); + const bool success = flipper_format_read_hex(ff, furi_string_get_cstr(key), data, 1); + furi_string_free(key); + return success; +} + +bool mf_desfire_file_count_load(uint32_t* data, const char* prefix, FlipperFormat* ff) { + FuriString* key = furi_string_alloc_printf("%s %s", prefix, MF_DESFIRE_FFF_FILE_IDS_KEY); + const bool success = flipper_format_get_value_count(ff, furi_string_get_cstr(key), data); + furi_string_free(key); + return success; +} + +bool mf_desfire_file_ids_load( + MfDesfireFileId* data, + uint32_t count, + const char* prefix, + FlipperFormat* ff) { + FuriString* key = furi_string_alloc_printf("%s %s", prefix, MF_DESFIRE_FFF_FILE_IDS_KEY); + const bool success = flipper_format_read_hex(ff, furi_string_get_cstr(key), data, count); + furi_string_free(key); + return success; +} + +bool mf_desfire_file_settings_load( + MfDesfireFileSettings* data, + const char* prefix, + FlipperFormat* ff) { + bool success = false; + + FuriString* key = furi_string_alloc(); + + do { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_TYPE_KEY); + if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), (uint8_t*)&data->type, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_COMM_SETTINGS_KEY); + if(!flipper_format_read_hex(ff, furi_string_get_cstr(key), (uint8_t*)&data->comm, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_ACCESS_RIGHTS_KEY); + if(!flipper_format_read_hex( + ff, + furi_string_get_cstr(key), + (uint8_t*)&data->access_rights, + sizeof(MfDesfireFileAccessRights))) + break; + + if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY); + if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->data.size, 1)) + break; + + } else if(data->type == MfDesfireFileTypeValue) { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_HI_LIMIT_KEY); + if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->value.hi_limit, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LO_LIMIT_KEY); + if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->value.lo_limit, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_VALUE_KEY); + if(!flipper_format_read_uint32( + ff, furi_string_get_cstr(key), &data->value.limited_credit_value, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_ENABLED_KEY); + if(!flipper_format_read_bool( + ff, furi_string_get_cstr(key), &data->value.limited_credit_enabled, 1)) + break; + } else if( + data->type == MfDesfireFileTypeLinearRecord || + data->type == MfDesfireFileTypeCyclicRecord) { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY); + if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->record.size, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_MAX_KEY); + if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->record.max, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_CUR_KEY); + if(!flipper_format_read_uint32(ff, furi_string_get_cstr(key), &data->record.cur, 1)) + break; + } + + success = true; + } while(false); + + furi_string_free(key); + + return success; +} + +bool mf_desfire_file_data_load(MfDesfireFileData* data, const char* prefix, FlipperFormat* ff) { + bool success = false; + do { + if(!flipper_format_key_exist(ff, prefix)) { + success = true; + break; + } + + uint32_t data_size; + if(!flipper_format_get_value_count(ff, prefix, &data_size)) break; + + simple_array_init(data->data, data_size); + + if(!flipper_format_read_hex(ff, prefix, simple_array_get_data(data->data), data_size)) + break; + + success = true; + } while(false); + + return success; +} + +bool mf_desfire_application_count_load(uint32_t* data, FlipperFormat* ff) { + return flipper_format_read_uint32(ff, MF_DESFIRE_FFF_APPLICATION_COUNT_KEY, data, 1); +} + +bool mf_desfire_application_ids_load( + MfDesfireApplicationId* data, + uint32_t count, + FlipperFormat* ff) { + return flipper_format_read_hex( + ff, MF_DESFIRE_FFF_APPLICATION_IDS_KEY, data->data, count * sizeof(MfDesfireApplicationId)); +} + +bool mf_desfire_application_load(MfDesfireApplication* data, const char* prefix, FlipperFormat* ff) { + FuriString* sub_prefix = furi_string_alloc(); + bool success = false; + + do { + if(!mf_desfire_key_settings_load(&data->key_settings, prefix, ff)) break; + + const uint32_t key_version_count = data->key_settings.max_keys; + simple_array_init(data->key_versions, key_version_count); + + uint32_t i; + for(i = 0; i < key_version_count; ++i) { + if(!mf_desfire_key_version_load(simple_array_get(data->key_versions, i), prefix, i, ff)) + break; + } + + if(i != key_version_count) break; + + uint32_t file_count; + if(!mf_desfire_file_count_load(&file_count, prefix, ff)) break; + + simple_array_init(data->file_ids, file_count); + if(!mf_desfire_file_ids_load(simple_array_get_data(data->file_ids), file_count, prefix, ff)) + break; + + simple_array_init(data->file_settings, file_count); + simple_array_init(data->file_data, file_count); + + for(i = 0; i < file_count; ++i) { + const MfDesfireFileId* file_id = simple_array_cget(data->file_ids, i); + furi_string_printf( + sub_prefix, "%s %s %u", prefix, MF_DESFIRE_FFF_FILE_SUB_PREFIX, *file_id); + + MfDesfireFileSettings* file_settings = simple_array_get(data->file_settings, i); + if(!mf_desfire_file_settings_load(file_settings, furi_string_get_cstr(sub_prefix), ff)) + break; + + MfDesfireFileData* file_data = simple_array_get(data->file_data, i); + if(!mf_desfire_file_data_load(file_data, furi_string_get_cstr(sub_prefix), ff)) break; + } + + if(i != file_count) break; + + success = true; + } while(false); + + furi_string_free(sub_prefix); + return success; +} + +bool mf_desfire_version_save(const MfDesfireVersion* data, FlipperFormat* ff) { + return flipper_format_write_hex( + ff, MF_DESFIRE_FFF_VERSION_KEY, (const uint8_t*)data, sizeof(MfDesfireVersion)); +} + +bool mf_desfire_free_memory_save(const MfDesfireFreeMemory* data, FlipperFormat* ff) { + return data->is_present ? + flipper_format_write_uint32(ff, MF_DESFIRE_FFF_FREE_MEM_KEY, &data->bytes_free, 1) : + true; +} + +bool mf_desfire_key_settings_save( + const MfDesfireKeySettings* data, + const char* prefix, + FlipperFormat* ff) { + bool success = false; + + FuriString* key = furi_string_alloc(); + + do { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_CHANGE_KEY_ID_KEY); + if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), &data->change_key_id, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_CONFIG_CHANGEABLE_KEY); + if(!flipper_format_write_bool( + ff, furi_string_get_cstr(key), &data->is_config_changeable, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FREE_CREATE_DELETE_KEY); + if(!flipper_format_write_bool( + ff, furi_string_get_cstr(key), &data->is_free_create_delete, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FREE_DIR_LIST_KEY); + if(!flipper_format_write_bool( + ff, furi_string_get_cstr(key), &data->is_free_directory_list, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_KEY_CHANGEABLE_KEY); + if(!flipper_format_write_bool( + ff, furi_string_get_cstr(key), &data->is_master_key_changeable, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FLAGS_KEY); + if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), &data->flags, 1)) break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_MAX_KEYS_KEY); + if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), &data->max_keys, 1)) break; + + success = true; + } while(false); + + furi_string_free(key); + return success; +} + +bool mf_desfire_key_version_save( + const MfDesfireKeyVersion* data, + const char* prefix, + uint32_t index, + FlipperFormat* ff) { + FuriString* key = furi_string_alloc_printf( + "%s %s %lu %s", + prefix, + MF_DESFIRE_FFF_KEY_SUB_PREFIX, + index, + MF_DESFIRE_FFF_KEY_VERSION_KEY); + const bool success = flipper_format_write_hex(ff, furi_string_get_cstr(key), data, 1); + furi_string_free(key); + return success; +} + +bool mf_desfire_file_ids_save( + const MfDesfireFileId* data, + uint32_t count, + const char* prefix, + FlipperFormat* ff) { + FuriString* key = furi_string_alloc_printf("%s %s", prefix, MF_DESFIRE_FFF_FILE_IDS_KEY); + const bool success = flipper_format_write_hex(ff, furi_string_get_cstr(key), data, count); + furi_string_free(key); + return success; +} + +bool mf_desfire_file_settings_save( + const MfDesfireFileSettings* data, + const char* prefix, + FlipperFormat* ff) { + bool success = false; + + FuriString* key = furi_string_alloc(); + + do { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_TYPE_KEY); + if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), (const uint8_t*)&data->type, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_COMM_SETTINGS_KEY); + if(!flipper_format_write_hex(ff, furi_string_get_cstr(key), (const uint8_t*)&data->comm, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_ACCESS_RIGHTS_KEY); + if(!flipper_format_write_hex( + ff, + furi_string_get_cstr(key), + (const uint8_t*)&data->access_rights, + sizeof(MfDesfireFileAccessRights))) + break; + + if(data->type == MfDesfireFileTypeStandard || data->type == MfDesfireFileTypeBackup) { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY); + if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->data.size, 1)) + break; + + } else if(data->type == MfDesfireFileTypeValue) { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_HI_LIMIT_KEY); + if(!flipper_format_write_uint32( + ff, furi_string_get_cstr(key), &data->value.hi_limit, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LO_LIMIT_KEY); + if(!flipper_format_write_uint32( + ff, furi_string_get_cstr(key), &data->value.lo_limit, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_VALUE_KEY); + if(!flipper_format_write_uint32( + ff, furi_string_get_cstr(key), &data->value.limited_credit_value, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_LIMIT_CREDIT_ENABLED_KEY); + if(!flipper_format_write_bool( + ff, furi_string_get_cstr(key), &data->value.limited_credit_enabled, 1)) + break; + } else if( + data->type == MfDesfireFileTypeLinearRecord || + data->type == MfDesfireFileTypeCyclicRecord) { + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_SIZE_KEY); + if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->record.size, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_MAX_KEY); + if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->record.max, 1)) + break; + + furi_string_printf(key, "%s %s", prefix, MF_DESFIRE_FFF_FILE_CUR_KEY); + if(!flipper_format_write_uint32(ff, furi_string_get_cstr(key), &data->record.cur, 1)) + break; + } + + success = true; + } while(false); + + furi_string_free(key); + return success; +} + +bool mf_desfire_file_data_save( + const MfDesfireFileData* data, + const char* prefix, + FlipperFormat* ff) { + const uint32_t data_size = simple_array_get_count(data->data); + return data_size > 0 ? flipper_format_write_hex( + ff, prefix, simple_array_cget_data(data->data), data_size) : + true; +} + +bool mf_desfire_application_count_save(const uint32_t* data, FlipperFormat* ff) { + return flipper_format_write_uint32(ff, MF_DESFIRE_FFF_APPLICATION_COUNT_KEY, data, 1); +} + +bool mf_desfire_application_ids_save( + const MfDesfireApplicationId* data, + uint32_t count, + FlipperFormat* ff) { + return flipper_format_write_hex( + ff, MF_DESFIRE_FFF_APPLICATION_IDS_KEY, data->data, count * sizeof(MfDesfireApplicationId)); +} + +bool mf_desfire_application_save( + const MfDesfireApplication* data, + const char* prefix, + FlipperFormat* ff) { + FuriString* sub_prefix = furi_string_alloc(); + bool success = false; + + do { + if(!mf_desfire_key_settings_save(&data->key_settings, prefix, ff)) break; + + const uint32_t key_version_count = data->key_settings.max_keys; + + uint32_t i; + for(i = 0; i < key_version_count; ++i) { + if(!mf_desfire_key_version_save( + simple_array_cget(data->key_versions, i), prefix, i, ff)) + break; + } + + if(i != key_version_count) break; + + const uint32_t file_count = simple_array_get_count(data->file_ids); + if(!mf_desfire_file_ids_save(simple_array_get_data(data->file_ids), file_count, prefix, ff)) + break; + + for(i = 0; i < file_count; ++i) { + const MfDesfireFileId* file_id = simple_array_cget(data->file_ids, i); + furi_string_printf( + sub_prefix, "%s %s %u", prefix, MF_DESFIRE_FFF_FILE_SUB_PREFIX, *file_id); + + const MfDesfireFileSettings* file_settings = simple_array_cget(data->file_settings, i); + if(!mf_desfire_file_settings_save(file_settings, furi_string_get_cstr(sub_prefix), ff)) + break; + + const MfDesfireFileData* file_data = simple_array_cget(data->file_data, i); + if(!mf_desfire_file_data_save(file_data, furi_string_get_cstr(sub_prefix), ff)) break; + } + + if(i != file_count) break; + + success = true; + } while(false); + + furi_string_free(sub_prefix); + return success; +} + +const SimpleArrayConfig mf_desfire_key_version_array_config = { + .init = NULL, + .copy = NULL, + .reset = NULL, + .type_size = sizeof(MfDesfireKeyVersion), +}; + +const SimpleArrayConfig mf_desfire_app_id_array_config = { + .init = NULL, + .copy = NULL, + .reset = NULL, + .type_size = sizeof(MfDesfireApplicationId), +}; + +const SimpleArrayConfig mf_desfire_file_id_array_config = { + .init = NULL, + .copy = NULL, + .reset = NULL, + .type_size = sizeof(MfDesfireFileId), +}; + +const SimpleArrayConfig mf_desfire_file_settings_array_config = { + .init = NULL, + .copy = NULL, + .reset = NULL, + .type_size = sizeof(MfDesfireFileSettings), +}; + +const SimpleArrayConfig mf_desfire_file_data_array_config = { + .init = (SimpleArrayInit)mf_desfire_file_data_init, + .copy = (SimpleArrayCopy)mf_desfire_file_data_copy, + .reset = (SimpleArrayReset)mf_desfire_file_data_reset, + .type_size = sizeof(MfDesfireData), +}; + +const SimpleArrayConfig mf_desfire_application_array_config = { + .init = (SimpleArrayInit)mf_desfire_application_init, + .copy = (SimpleArrayCopy)mf_desfire_application_copy, + .reset = (SimpleArrayReset)mf_desfire_application_reset, + .type_size = sizeof(MfDesfireApplication), +}; diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_i.h b/lib/nfc/protocols/mf_desfire/mf_desfire_i.h new file mode 100644 index 00000000000..05381096d16 --- /dev/null +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_i.h @@ -0,0 +1,140 @@ +#pragma once + +#include "mf_desfire.h" + +#define MF_DESFIRE_FFF_PICC_PREFIX "PICC" +#define MF_DESFIRE_FFF_APP_PREFIX "Application" + +// SimpleArray configurations + +extern const SimpleArrayConfig mf_desfire_key_version_array_config; +extern const SimpleArrayConfig mf_desfire_app_id_array_config; +extern const SimpleArrayConfig mf_desfire_file_id_array_config; +extern const SimpleArrayConfig mf_desfire_file_settings_array_config; +extern const SimpleArrayConfig mf_desfire_file_data_array_config; +extern const SimpleArrayConfig mf_desfire_application_array_config; + +// Parse internal MfDesfire structures + +bool mf_desfire_version_parse(MfDesfireVersion* data, const BitBuffer* buf); + +bool mf_desfire_free_memory_parse(MfDesfireFreeMemory* data, const BitBuffer* buf); + +bool mf_desfire_key_settings_parse(MfDesfireKeySettings* data, const BitBuffer* buf); + +bool mf_desfire_key_version_parse(MfDesfireKeyVersion* data, const BitBuffer* buf); + +bool mf_desfire_application_id_parse( + MfDesfireApplicationId* data, + uint32_t index, + const BitBuffer* buf); + +bool mf_desfire_file_id_parse(MfDesfireFileId* data, uint32_t index, const BitBuffer* buf); + +bool mf_desfire_file_settings_parse(MfDesfireFileSettings* data, const BitBuffer* buf); + +bool mf_desfire_file_data_parse(MfDesfireFileData* data, const BitBuffer* buf); + +// Init internal MfDesfire structures + +void mf_desfire_file_data_init(MfDesfireFileData* data); + +void mf_desfire_application_init(MfDesfireApplication* data); + +// Reset internal MfDesfire structures + +void mf_desfire_file_data_reset(MfDesfireFileData* data); + +void mf_desfire_application_reset(MfDesfireApplication* data); + +// Copy internal MfDesfire structures + +void mf_desfire_file_data_copy(MfDesfireFileData* data, const MfDesfireFileData* other); + +void mf_desfire_application_copy(MfDesfireApplication* data, const MfDesfireApplication* other); + +// Load internal MfDesfire structures + +bool mf_desfire_version_load(MfDesfireVersion* data, FlipperFormat* ff); + +bool mf_desfire_free_memory_load(MfDesfireFreeMemory* data, FlipperFormat* ff); + +bool mf_desfire_key_settings_load( + MfDesfireKeySettings* data, + const char* prefix, + FlipperFormat* ff); + +bool mf_desfire_key_version_load( + MfDesfireKeyVersion* data, + const char* prefix, + uint32_t index, + FlipperFormat* ff); + +bool mf_desfire_file_count_load(uint32_t* data, const char* prefix, FlipperFormat* ff); + +bool mf_desfire_file_ids_load( + MfDesfireFileId* data, + uint32_t count, + const char* prefix, + FlipperFormat* ff); + +bool mf_desfire_file_settings_load( + MfDesfireFileSettings* data, + const char* prefix, + FlipperFormat* ff); + +bool mf_desfire_file_data_load(MfDesfireFileData* data, const char* prefix, FlipperFormat* ff); + +bool mf_desfire_application_count_load(uint32_t* data, FlipperFormat* ff); + +bool mf_desfire_application_ids_load( + MfDesfireApplicationId* data, + uint32_t count, + FlipperFormat* ff); + +bool mf_desfire_application_load(MfDesfireApplication* data, const char* prefix, FlipperFormat* ff); + +// Save internal MFDesfire structures + +bool mf_desfire_version_save(const MfDesfireVersion* data, FlipperFormat* ff); + +bool mf_desfire_free_memory_save(const MfDesfireFreeMemory* data, FlipperFormat* ff); + +bool mf_desfire_key_settings_save( + const MfDesfireKeySettings* data, + const char* prefix, + FlipperFormat* ff); + +bool mf_desfire_key_version_save( + const MfDesfireKeyVersion* data, + const char* prefix, + uint32_t index, + FlipperFormat* ff); + +bool mf_desfire_file_ids_save( + const MfDesfireFileId* data, + uint32_t count, + const char* prefix, + FlipperFormat* ff); + +bool mf_desfire_file_settings_save( + const MfDesfireFileSettings* data, + const char* prefix, + FlipperFormat* ff); + +bool mf_desfire_file_data_save( + const MfDesfireFileData* data, + const char* prefix, + FlipperFormat* ff); + +bool mf_desfire_application_count_save(const uint32_t* data, FlipperFormat* ff); + +bool mf_desfire_application_ids_save( + const MfDesfireApplicationId* data, + uint32_t count, + FlipperFormat* ff); + +bool mf_desfire_application_save( + const MfDesfireApplication* data, + const char* prefix, + FlipperFormat* ff); diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.c b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.c new file mode 100644 index 00000000000..5af033d4ca6 --- /dev/null +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.c @@ -0,0 +1,243 @@ +#include "mf_desfire_poller_i.h" + +#include + +#include + +#define TAG "MfDesfirePoller" + +#define MF_DESFIRE_BUF_SIZE (64U) +#define MF_DESFIRE_RESULT_BUF_SIZE (512U) + +typedef NfcCommand (*MfDesfirePollerReadHandler)(MfDesfirePoller* instance); + +const MfDesfireData* mf_desfire_poller_get_data(MfDesfirePoller* instance) { + furi_assert(instance); + + return instance->data; +} + +static MfDesfirePoller* mf_desfire_poller_alloc(Iso14443_4aPoller* iso14443_4a_poller) { + MfDesfirePoller* instance = malloc(sizeof(MfDesfirePoller)); + instance->iso14443_4a_poller = iso14443_4a_poller; + instance->data = mf_desfire_alloc(); + instance->tx_buffer = bit_buffer_alloc(MF_DESFIRE_BUF_SIZE); + instance->rx_buffer = bit_buffer_alloc(MF_DESFIRE_BUF_SIZE); + instance->input_buffer = bit_buffer_alloc(MF_DESFIRE_BUF_SIZE); + instance->result_buffer = bit_buffer_alloc(MF_DESFIRE_RESULT_BUF_SIZE); + + instance->mf_desfire_event.data = &instance->mf_desfire_event_data; + + instance->general_event.protocol = NfcProtocolMfDesfire; + instance->general_event.event_data = &instance->mf_desfire_event; + instance->general_event.instance = instance; + + return instance; +} + +static void mf_desfire_poller_free(MfDesfirePoller* instance) { + furi_assert(instance); + + mf_desfire_free(instance->data); + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + bit_buffer_free(instance->input_buffer); + bit_buffer_free(instance->result_buffer); + free(instance); +} + +static NfcCommand mf_desfire_poller_handler_idle(MfDesfirePoller* instance) { + bit_buffer_reset(instance->input_buffer); + bit_buffer_reset(instance->result_buffer); + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + iso14443_4a_copy( + instance->data->iso14443_4a_data, + iso14443_4a_poller_get_data(instance->iso14443_4a_poller)); + + instance->state = MfDesfirePollerStateReadVersion; + return NfcCommandContinue; +} + +static NfcCommand mf_desfire_poller_handler_read_version(MfDesfirePoller* instance) { + instance->error = mf_desfire_poller_read_version(instance, &instance->data->version); + if(instance->error == MfDesfireErrorNone) { + FURI_LOG_D(TAG, "Read version success"); + instance->state = MfDesfirePollerStateReadFreeMemory; + } else { + FURI_LOG_E(TAG, "Failed to read version"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = MfDesfirePollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_desfire_poller_handler_read_free_memory(MfDesfirePoller* instance) { + instance->error = mf_desfire_poller_read_free_memory(instance, &instance->data->free_memory); + if(instance->error == MfDesfireErrorNone) { + FURI_LOG_D(TAG, "Read free memory success"); + instance->state = MfDesfirePollerStateReadMasterKeySettings; + } else { + FURI_LOG_E(TAG, "Failed to read free memory"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = MfDesfirePollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_desfire_poller_handler_read_master_key_settings(MfDesfirePoller* instance) { + instance->error = + mf_desfire_poller_read_key_settings(instance, &instance->data->master_key_settings); + if(instance->error == MfDesfireErrorNone) { + FURI_LOG_D(TAG, "Read master key settings success"); + instance->state = MfDesfirePollerStateReadMasterKeyVersion; + } else { + FURI_LOG_E(TAG, "Failed to read master key settings"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = MfDesfirePollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_desfire_poller_handler_read_master_key_version(MfDesfirePoller* instance) { + instance->error = mf_desfire_poller_read_key_versions( + instance, + instance->data->master_key_versions, + instance->data->master_key_settings.max_keys); + if(instance->error == MfDesfireErrorNone) { + FURI_LOG_D(TAG, "Read master key version success"); + instance->state = MfDesfirePollerStateReadApplicationIds; + } else { + FURI_LOG_E(TAG, "Failed to read master key version"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = MfDesfirePollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_desfire_poller_handler_read_application_ids(MfDesfirePoller* instance) { + instance->error = + mf_desfire_poller_read_application_ids(instance, instance->data->application_ids); + if(instance->error == MfDesfireErrorNone) { + FURI_LOG_D(TAG, "Read application ids success"); + instance->state = MfDesfirePollerStateReadApplications; + } else { + FURI_LOG_E(TAG, "Failed to read application ids"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = MfDesfirePollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_desfire_poller_handler_read_applications(MfDesfirePoller* instance) { + instance->error = mf_desfire_poller_read_applications( + instance, instance->data->application_ids, instance->data->applications); + if(instance->error == MfDesfireErrorNone) { + FURI_LOG_D(TAG, "Read applications success"); + instance->state = MfDesfirePollerStateReadSuccess; + } else { + FURI_LOG_E(TAG, "Failed to read applications"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->state = MfDesfirePollerStateReadFailed; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_desfire_poller_handler_read_fail(MfDesfirePoller* instance) { + FURI_LOG_D(TAG, "Read Failed"); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->mf_desfire_event.data->error = instance->error; + NfcCommand command = instance->callback(instance->general_event, instance->context); + instance->state = MfDesfirePollerStateIdle; + return command; +} + +static NfcCommand mf_desfire_poller_handler_read_success(MfDesfirePoller* instance) { + FURI_LOG_D(TAG, "Read success."); + iso14443_4a_poller_halt(instance->iso14443_4a_poller); + instance->mf_desfire_event.type = MfDesfirePollerEventTypeReadSuccess; + NfcCommand command = instance->callback(instance->general_event, instance->context); + return command; +} + +static const MfDesfirePollerReadHandler mf_desfire_poller_read_handler[MfDesfirePollerStateNum] = { + [MfDesfirePollerStateIdle] = mf_desfire_poller_handler_idle, + [MfDesfirePollerStateReadVersion] = mf_desfire_poller_handler_read_version, + [MfDesfirePollerStateReadFreeMemory] = mf_desfire_poller_handler_read_free_memory, + [MfDesfirePollerStateReadMasterKeySettings] = + mf_desfire_poller_handler_read_master_key_settings, + [MfDesfirePollerStateReadMasterKeyVersion] = mf_desfire_poller_handler_read_master_key_version, + [MfDesfirePollerStateReadApplicationIds] = mf_desfire_poller_handler_read_application_ids, + [MfDesfirePollerStateReadApplications] = mf_desfire_poller_handler_read_applications, + [MfDesfirePollerStateReadFailed] = mf_desfire_poller_handler_read_fail, + [MfDesfirePollerStateReadSuccess] = mf_desfire_poller_handler_read_success, +}; + +static void mf_desfire_poller_set_callback( + MfDesfirePoller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand mf_desfire_poller_run(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_4a); + + MfDesfirePoller* instance = context; + furi_assert(instance); + furi_assert(instance->callback); + + const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data; + furi_assert(iso14443_4a_event); + + NfcCommand command = NfcCommandContinue; + + if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) { + command = mf_desfire_poller_read_handler[instance->state](instance); + } else if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeError) { + instance->mf_desfire_event.type = MfDesfirePollerEventTypeReadFailed; + command = instance->callback(instance->general_event, instance->context); + } + + return command; +} + +static bool mf_desfire_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso14443_4a); + + MfDesfirePoller* instance = context; + furi_assert(instance); + + const Iso14443_4aPollerEvent* iso14443_4a_event = event.event_data; + furi_assert(iso14443_4a_event); + + bool protocol_detected = false; + + if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) { + MfDesfireVersion version = {}; + const MfDesfireError error = mf_desfire_poller_read_version(instance, &version); + protocol_detected = (error == MfDesfireErrorNone); + } + + return protocol_detected; +} + +const NfcPollerBase mf_desfire_poller = { + .alloc = (NfcPollerAlloc)mf_desfire_poller_alloc, + .free = (NfcPollerFree)mf_desfire_poller_free, + .set_callback = (NfcPollerSetCallback)mf_desfire_poller_set_callback, + .run = (NfcPollerRun)mf_desfire_poller_run, + .detect = (NfcPollerDetect)mf_desfire_poller_detect, + .get_data = (NfcPollerGetData)mf_desfire_poller_get_data, +}; diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h new file mode 100644 index 00000000000..6ef2f3f68d4 --- /dev/null +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.h @@ -0,0 +1,274 @@ +#pragma once + +#include "mf_desfire.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief MfDesfirePoller opaque type definition. + */ +typedef struct MfDesfirePoller MfDesfirePoller; + +/** + * @brief Enumeration of possible MfDesfire poller event types. + */ +typedef enum { + MfDesfirePollerEventTypeReadSuccess, /**< Card was read successfully. */ + MfDesfirePollerEventTypeReadFailed, /**< Poller failed to read card. */ +} MfDesfirePollerEventType; + +/** + * @brief MfDesfire poller event data. + */ +typedef union { + MfDesfireError error; /**< Error code indicating card reading fail reason. */ +} MfDesfirePollerEventData; + +/** + * @brief MfDesfire poller event structure. + * + * Upon emission of an event, an instance of this struct will be passed to the callback. + */ +typedef struct { + MfDesfirePollerEventType type; /**< Type of emmitted event. */ + MfDesfirePollerEventData* data; /**< Pointer to event specific data. */ +} MfDesfirePollerEvent; + +/** + * @brief Transmit and receive MfDesfire chunks in poller mode. + * + * Must ONLY be used inside the callback function. + * + * The rx_buffer will be filled with any data received as a response to data + * sent from tx_buffer, with a timeout defined by the fwt parameter. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted. + * @param[out] rx_buffer pointer to the buffer to be filled with received data. + * @return MfDesfireErrorNone on success, an error code on failure. + */ +MfDesfireError mf_desfire_send_chunks( + MfDesfirePoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer); + +/** + * @brief Read MfDesfire card version. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the MfDesfireVersion structure to be filled with version data. + * @return MfDesfireErrorNone on success, an error code on failure. + */ +MfDesfireError mf_desfire_poller_read_version(MfDesfirePoller* instance, MfDesfireVersion* data); + +/** + * @brief Read free memory available on MfDesfire card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the MfDesfireFreeMemory structure to be filled with free memory data. + * @return MfDesfireErrorNone on success, an error code on failure. + */ +MfDesfireError + mf_desfire_poller_read_free_memory(MfDesfirePoller* instance, MfDesfireFreeMemory* data); + +/** + * @brief Read key settings on MfDesfire card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the MfDesfireKeySettings structure to be filled with key settings data. + * @return MfDesfireErrorNone on success, an error code on failure. + */ +MfDesfireError + mf_desfire_poller_read_key_settings(MfDesfirePoller* instance, MfDesfireKeySettings* data); + +/** + * @brief Read key versions on MfDesfire card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the SimpleArray structure to be filled with key versions data. + * @param[in] count number of key versions to read. + * @return MfDesfireErrorNone on success, an error code on failure. + */ +MfDesfireError mf_desfire_poller_read_key_versions( + MfDesfirePoller* instance, + SimpleArray* data, + uint32_t count); + +/** + * @brief Read applications IDs on MfDesfire card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the SimpleArray structure to be filled with application ids data. + * @return MfDesfireErrorNone on success, an error code on failure. + */ +MfDesfireError + mf_desfire_poller_read_application_ids(MfDesfirePoller* instance, SimpleArray* data); + +/** + * @brief Select application on MfDesfire card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] id pointer to the MfDesfireApplicationId structure with application id to select. + * @return MfDesfireErrorNone on success, an error code on failure. + */ +MfDesfireError mf_desfire_poller_select_application( + MfDesfirePoller* instance, + const MfDesfireApplicationId* id); + +/** + * @brief Read file IDs for selected application on MfDesfire card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the SimpleArray structure to be filled with file ids data. + * @return MfDesfireErrorNone on success, an error code on failure. + */ +MfDesfireError mf_desfire_poller_read_file_ids(MfDesfirePoller* instance, SimpleArray* data); + +/** + * @brief Read file settings on MfDesfire card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] id file id to read settings for. + * @param[out] data pointer to the MfDesfireFileSettings structure to be filled with file settings data. + * @return MfDesfireErrorNone on success, an error code on failure. + */ +MfDesfireError mf_desfire_poller_read_file_settings( + MfDesfirePoller* instance, + MfDesfireFileId id, + MfDesfireFileSettings* data); + +/** + * @brief Read multiple file settings on MfDesfire card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] file_ids pointer to the SimpleArray structure array with file ids to read settings for. + * @param[out] data pointer to the SimpleArray structure array to be filled with file settings data. + * @return MfDesfireErrorNone on success, an error code on failure. + */ +MfDesfireError mf_desfire_poller_read_file_settings_multi( + MfDesfirePoller* instance, + const SimpleArray* file_ids, + SimpleArray* data); + +/** + * @brief Read file data on MfDesfire card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] id file id to read data from. + * @param[in] offset offset in bytes to start reading from. + * @param[in] size number of bytes to read. + * @param[out] data pointer to the MfDesfireFileData structure to be filled with file data. + * @return MfDesfireErrorNone on success, an error code on failure. + */ +MfDesfireError mf_desfire_poller_read_file_data( + MfDesfirePoller* instance, + MfDesfireFileId id, + uint32_t offset, + size_t size, + MfDesfireFileData* data); + +/** + * @brief Read file value on MfDesfire card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] id file id to read value from. + * @param[out] data pointer to the MfDesfireFileData structure to be filled with file value. + * @return MfDesfireErrorNone on success, an error code on failure. + */ +MfDesfireError mf_desfire_poller_read_file_value( + MfDesfirePoller* instance, + MfDesfireFileId id, + MfDesfireFileData* data); + +/** + * @brief Read file records on MfDesfire card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] id file id to read data from. + * @param[in] offset offset in bytes to start reading from. + * @param[in] size number of bytes to read. + * @param[out] data pointer to the MfDesfireFileData structure to be filled with file records data. + * @return MfDesfireErrorNone on success, an error code on failure. + */ +MfDesfireError mf_desfire_poller_read_file_records( + MfDesfirePoller* instance, + MfDesfireFileId id, + uint32_t offset, + size_t size, + MfDesfireFileData* data); + +/** + * @brief Read data from multiple files on MfDesfire card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] file_ids pointer to the SimpleArray structure array with files ids to read data from. + * @param[in] file_settings pointer to the SimpleArray structure array with files settings to read data from. + * @param[out] data pointer to the SimpleArray structure array to be filled with files data. + * @return MfDesfireErrorNone on success, an error code on failure. + */ +MfDesfireError mf_desfire_poller_read_file_data_multi( + MfDesfirePoller* instance, + const SimpleArray* file_ids, + const SimpleArray* file_settings, + SimpleArray* data); + +/** + * @brief Read application data for selected application on MfDesfire card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the MfDesfireApplication structure to be filled with application data. + * @return MfDesfireErrorNone on success, an error code on failure. + */ +MfDesfireError + mf_desfire_poller_read_application(MfDesfirePoller* instance, MfDesfireApplication* data); + +/** + * @brief Read multiple applications data on MfDesfire card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] app_ids pointer to the SimpleArray structure array with application ids to read data from. + * @param[out] data pointer to the SimpleArray structure array to be filled with applications data. + * @return MfDesfireErrorNone on success, an error code on failure. + */ +MfDesfireError mf_desfire_poller_read_applications( + MfDesfirePoller* instance, + const SimpleArray* app_ids, + SimpleArray* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_defs.h b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_defs.h new file mode 100644 index 00000000000..0c14ceee4d8 --- /dev/null +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcPollerBase mf_desfire_poller; diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c new file mode 100644 index 00000000000..0b2d8413828 --- /dev/null +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.c @@ -0,0 +1,476 @@ +#include "mf_desfire_poller_i.h" + +#include + +#include "mf_desfire_i.h" + +#define TAG "MfDesfirePoller" + +MfDesfireError mf_desfire_process_error(Iso14443_4aError error) { + switch(error) { + case Iso14443_4aErrorNone: + return MfDesfireErrorNone; + case Iso14443_4aErrorNotPresent: + return MfDesfireErrorNotPresent; + case Iso14443_4aErrorTimeout: + return MfDesfireErrorTimeout; + default: + return MfDesfireErrorProtocol; + } +} + +MfDesfireError mf_desfire_send_chunks( + MfDesfirePoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer) { + furi_assert(instance); + furi_assert(instance->iso14443_4a_poller); + furi_assert(instance->tx_buffer); + furi_assert(instance->rx_buffer); + furi_assert(tx_buffer); + furi_assert(rx_buffer); + + MfDesfireError error = MfDesfireErrorNone; + + do { + Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( + instance->iso14443_4a_poller, tx_buffer, instance->rx_buffer); + + if(iso14443_4a_error != Iso14443_4aErrorNone) { + error = mf_desfire_process_error(iso14443_4a_error); + break; + } + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, MF_DESFIRE_FLAG_HAS_NEXT); + + if(bit_buffer_get_size_bytes(instance->rx_buffer) > sizeof(uint8_t)) { + bit_buffer_copy_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t)); + } else { + bit_buffer_reset(rx_buffer); + } + + while(bit_buffer_starts_with_byte(instance->rx_buffer, MF_DESFIRE_FLAG_HAS_NEXT)) { + Iso14443_4aError iso14443_4a_error = iso14443_4a_poller_send_block( + instance->iso14443_4a_poller, instance->tx_buffer, instance->rx_buffer); + + if(iso14443_4a_error != Iso14443_4aErrorNone) { + error = mf_desfire_process_error(iso14443_4a_error); + break; + } + + const size_t rx_size = bit_buffer_get_size_bytes(instance->rx_buffer); + const size_t rx_capacity_remaining = + bit_buffer_get_capacity_bytes(rx_buffer) - bit_buffer_get_size_bytes(rx_buffer); + + if(rx_size <= rx_capacity_remaining) { + bit_buffer_append_right(rx_buffer, instance->rx_buffer, sizeof(uint8_t)); + } else { + FURI_LOG_W(TAG, "RX buffer overflow: ignoring %zu bytes", rx_size); + } + } + } while(false); + + return error; +} + +MfDesfireError mf_desfire_poller_read_version(MfDesfirePoller* instance, MfDesfireVersion* data) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_VERSION); + + MfDesfireError error; + + do { + error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + + if(error != MfDesfireErrorNone) break; + + if(!mf_desfire_version_parse(data, instance->result_buffer)) { + error = MfDesfireErrorProtocol; + } + } while(false); + + return error; +} + +MfDesfireError + mf_desfire_poller_read_free_memory(MfDesfirePoller* instance, MfDesfireFreeMemory* data) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FREE_MEMORY); + + MfDesfireError error; + + do { + error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + + if(error != MfDesfireErrorNone) break; + + if(!mf_desfire_free_memory_parse(data, instance->result_buffer)) { + error = MfDesfireErrorProtocol; + } + } while(false); + + return error; +} + +MfDesfireError + mf_desfire_poller_read_key_settings(MfDesfirePoller* instance, MfDesfireKeySettings* data) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_KEY_SETTINGS); + + MfDesfireError error; + + do { + error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + + if(error != MfDesfireErrorNone) break; + + if(!mf_desfire_key_settings_parse(data, instance->result_buffer)) { + error = MfDesfireErrorProtocol; + } + } while(false); + + return error; +} + +MfDesfireError mf_desfire_poller_read_key_versions( + MfDesfirePoller* instance, + SimpleArray* data, + uint32_t count) { + furi_assert(instance); + furi_assert(count > 0); + + simple_array_init(data, count); + + bit_buffer_set_size_bytes(instance->input_buffer, sizeof(uint8_t) * 2); + bit_buffer_set_byte(instance->input_buffer, 0, MF_DESFIRE_CMD_GET_KEY_VERSION); + + MfDesfireError error = MfDesfireErrorNone; + + for(uint32_t i = 0; i < count; ++i) { + bit_buffer_set_byte(instance->input_buffer, 1, i); + + error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + + if(error != MfDesfireErrorNone) break; + + if(!mf_desfire_key_version_parse(simple_array_get(data, i), instance->result_buffer)) { + error = MfDesfireErrorProtocol; + break; + } + } + + return error; +} + +MfDesfireError + mf_desfire_poller_read_application_ids(MfDesfirePoller* instance, SimpleArray* data) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_APPLICATION_IDS); + + MfDesfireError error; + + do { + error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + + if(error != MfDesfireErrorNone) break; + + const uint32_t app_id_count = + bit_buffer_get_size_bytes(instance->result_buffer) / sizeof(MfDesfireApplicationId); + if(app_id_count == 0) break; + + simple_array_init(data, app_id_count); + + for(uint32_t i = 0; i < app_id_count; ++i) { + if(!mf_desfire_application_id_parse( + simple_array_get(data, i), i, instance->result_buffer)) { + error = MfDesfireErrorProtocol; + break; + } + } + } while(false); + + return error; +} + +MfDesfireError mf_desfire_poller_select_application( + MfDesfirePoller* instance, + const MfDesfireApplicationId* id) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_SELECT_APPLICATION); + bit_buffer_append_bytes( + instance->input_buffer, (const uint8_t*)id, sizeof(MfDesfireApplicationId)); + + MfDesfireError error = + mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + + return error; +} + +MfDesfireError mf_desfire_poller_read_file_ids(MfDesfirePoller* instance, SimpleArray* data) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FILE_IDS); + + MfDesfireError error; + + do { + error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + + if(error != MfDesfireErrorNone) break; + + const uint32_t id_count = + bit_buffer_get_size_bytes(instance->result_buffer) / sizeof(MfDesfireFileId); + + if(id_count == 0) break; + simple_array_init(data, id_count); + + for(uint32_t i = 0; i < id_count; ++i) { + if(!mf_desfire_file_id_parse(simple_array_get(data, i), i, instance->result_buffer)) { + error = MfDesfireErrorProtocol; + break; + } + } + } while(false); + + return error; +} + +MfDesfireError mf_desfire_poller_read_file_settings( + MfDesfirePoller* instance, + MfDesfireFileId id, + MfDesfireFileSettings* data) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_FILE_SETTINGS); + bit_buffer_append_byte(instance->input_buffer, id); + + MfDesfireError error; + + do { + error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + + if(error != MfDesfireErrorNone) break; + + if(!mf_desfire_file_settings_parse(data, instance->result_buffer)) { + error = MfDesfireErrorProtocol; + } + } while(false); + + return error; +} + +MfDesfireError mf_desfire_poller_read_file_settings_multi( + MfDesfirePoller* instance, + const SimpleArray* file_ids, + SimpleArray* data) { + furi_assert(instance); + + MfDesfireError error = MfDesfireErrorNone; + + const uint32_t file_id_count = simple_array_get_count(file_ids); + if(file_id_count > 0) { + simple_array_init(data, file_id_count); + } + + for(uint32_t i = 0; i < file_id_count; ++i) { + const MfDesfireFileId file_id = *(const MfDesfireFileId*)simple_array_cget(file_ids, i); + error = mf_desfire_poller_read_file_settings(instance, file_id, simple_array_get(data, i)); + if(error != MfDesfireErrorNone) break; + } + + return error; +} + +MfDesfireError mf_desfire_poller_read_file_data( + MfDesfirePoller* instance, + MfDesfireFileId id, + uint32_t offset, + size_t size, + MfDesfireFileData* data) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_READ_DATA); + bit_buffer_append_byte(instance->input_buffer, id); + bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&offset, 3); + bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&size, 3); + + MfDesfireError error; + + do { + error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + + if(error != MfDesfireErrorNone) break; + + if(!mf_desfire_file_data_parse(data, instance->result_buffer)) { + error = MfDesfireErrorProtocol; + } + } while(false); + + return error; +} + +MfDesfireError mf_desfire_poller_read_file_value( + MfDesfirePoller* instance, + MfDesfireFileId id, + MfDesfireFileData* data) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_GET_VALUE); + bit_buffer_append_byte(instance->input_buffer, id); + + MfDesfireError error; + + do { + error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + + if(error != MfDesfireErrorNone) break; + + if(!mf_desfire_file_data_parse(data, instance->result_buffer)) { + error = MfDesfireErrorProtocol; + } + } while(false); + + return error; +} + +MfDesfireError mf_desfire_poller_read_file_records( + MfDesfirePoller* instance, + MfDesfireFileId id, + uint32_t offset, + size_t size, + MfDesfireFileData* data) { + furi_assert(instance); + + bit_buffer_reset(instance->input_buffer); + bit_buffer_append_byte(instance->input_buffer, MF_DESFIRE_CMD_READ_RECORDS); + bit_buffer_append_byte(instance->input_buffer, id); + bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&offset, 3); + bit_buffer_append_bytes(instance->input_buffer, (const uint8_t*)&size, 3); + + MfDesfireError error; + + do { + error = mf_desfire_send_chunks(instance, instance->input_buffer, instance->result_buffer); + + if(error != MfDesfireErrorNone) break; + + if(!mf_desfire_file_data_parse(data, instance->result_buffer)) { + error = MfDesfireErrorProtocol; + } + } while(false); + + return error; +} + +MfDesfireError mf_desfire_poller_read_file_data_multi( + MfDesfirePoller* instance, + const SimpleArray* file_ids, + const SimpleArray* file_settings, + SimpleArray* data) { + furi_assert(instance); + furi_assert(simple_array_get_count(file_ids) == simple_array_get_count(file_settings)); + + MfDesfireError error = MfDesfireErrorNone; + + const uint32_t file_id_count = simple_array_get_count(file_ids); + if(file_id_count > 0) { + simple_array_init(data, file_id_count); + } + + for(uint32_t i = 0; i < file_id_count; ++i) { + const MfDesfireFileId file_id = *(const MfDesfireFileId*)simple_array_cget(file_ids, i); + const MfDesfireFileSettings* file_settings_cur = simple_array_cget(file_settings, i); + const MfDesfireFileType file_type = file_settings_cur->type; + + MfDesfireFileData* file_data = simple_array_get(data, i); + + if(file_type == MfDesfireFileTypeStandard || file_type == MfDesfireFileTypeBackup) { + error = mf_desfire_poller_read_file_data( + instance, file_id, 0, file_settings_cur->data.size, file_data); + } else if(file_type == MfDesfireFileTypeValue) { + error = mf_desfire_poller_read_file_value(instance, file_id, file_data); + } else if( + file_type == MfDesfireFileTypeLinearRecord || + file_type == MfDesfireFileTypeCyclicRecord) { + error = mf_desfire_poller_read_file_records( + instance, file_id, 0, file_settings_cur->data.size, file_data); + } + + if(error != MfDesfireErrorNone) break; + } + + return error; +} + +MfDesfireError + mf_desfire_poller_read_application(MfDesfirePoller* instance, MfDesfireApplication* data) { + furi_assert(instance); + furi_assert(data); + + MfDesfireError error; + + do { + error = mf_desfire_poller_read_key_settings(instance, &data->key_settings); + if(error != MfDesfireErrorNone) break; + + error = mf_desfire_poller_read_key_versions( + instance, data->key_versions, data->key_settings.max_keys); + if(error != MfDesfireErrorNone) break; + + error = mf_desfire_poller_read_file_ids(instance, data->file_ids); + if(error != MfDesfireErrorNone) break; + + error = mf_desfire_poller_read_file_settings_multi( + instance, data->file_ids, data->file_settings); + if(error != MfDesfireErrorNone) break; + + error = mf_desfire_poller_read_file_data_multi( + instance, data->file_ids, data->file_settings, data->file_data); + if(error != MfDesfireErrorNone) break; + + } while(false); + + return error; +} + +MfDesfireError mf_desfire_poller_read_applications( + MfDesfirePoller* instance, + const SimpleArray* app_ids, + SimpleArray* data) { + furi_assert(instance); + + MfDesfireError error = MfDesfireErrorNone; + + const uint32_t app_id_count = simple_array_get_count(app_ids); + if(app_id_count > 0) { + simple_array_init(data, app_id_count); + } + + for(uint32_t i = 0; i < app_id_count; ++i) { + do { + error = mf_desfire_poller_select_application(instance, simple_array_cget(app_ids, i)); + if(error != MfDesfireErrorNone) break; + + MfDesfireApplication* current_app = simple_array_get(data, i); + error = mf_desfire_poller_read_application(instance, current_app); + + } while(false); + } + + return error; +} diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h new file mode 100644 index 00000000000..1c80af36fb6 --- /dev/null +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller_i.h @@ -0,0 +1,54 @@ +#pragma once + +#include "mf_desfire_poller.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + MfDesfirePollerStateIdle, + MfDesfirePollerStateReadVersion, + MfDesfirePollerStateReadFreeMemory, + MfDesfirePollerStateReadMasterKeySettings, + MfDesfirePollerStateReadMasterKeyVersion, + MfDesfirePollerStateReadApplicationIds, + MfDesfirePollerStateReadApplications, + MfDesfirePollerStateReadFailed, + MfDesfirePollerStateReadSuccess, + + MfDesfirePollerStateNum, +} MfDesfirePollerState; + +typedef enum { + MfDesfirePollerSessionStateIdle, + MfDesfirePollerSessionStateActive, + MfDesfirePollerSessionStateStopRequest, +} MfDesfirePollerSessionState; + +struct MfDesfirePoller { + Iso14443_4aPoller* iso14443_4a_poller; + MfDesfirePollerSessionState session_state; + MfDesfirePollerState state; + MfDesfireError error; + MfDesfireData* data; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + BitBuffer* input_buffer; + BitBuffer* result_buffer; + MfDesfirePollerEventData mf_desfire_event_data; + MfDesfirePollerEvent mf_desfire_event; + NfcGenericEvent general_event; + NfcGenericCallback callback; + void* context; +}; + +MfDesfireError mf_desfire_process_error(Iso14443_4aError error); + +const MfDesfireData* mf_desfire_poller_get_data(MfDesfirePoller* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight.c new file mode 100644 index 00000000000..fd845f3c085 --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight.c @@ -0,0 +1,627 @@ +#include "mf_ultralight.h" + +#include +#include + +#define MF_ULTRALIGHT_PROTOCOL_NAME "NTAG/Ultralight" + +#define MF_ULTRALIGHT_FORMAT_VERSION_KEY "Data format version" +#define MF_ULTRALIGHT_TYPE_KEY MF_ULTRALIGHT_PROTOCOL_NAME " type" +#define MF_ULTRALIGHT_SIGNATURE_KEY "Signature" +#define MF_ULTRALIGHT_MIFARE_VERSION_KEY "Mifare version" +#define MF_ULTRALIGHT_COUNTER_KEY "Counter" +#define MF_ULTRALIGHT_TEARING_KEY "Tearing" +#define MF_ULTRALIGHT_PAGES_TOTAL_KEY "Pages total" +#define MF_ULTRALIGHT_PAGES_READ_KEY "Pages read" +#define MF_ULTRALIGHT_PAGE_KEY "Page" +#define MF_ULTRALIGHT_FAILED_ATTEMPTS_KEY "Failed authentication attempts" + +typedef struct { + const char* device_name; + uint16_t total_pages; + uint16_t config_page; + uint32_t feature_set; +} MfUltralightFeatures; + +static const uint32_t mf_ultralight_data_format_version = 2; + +static const MfUltralightFeatures mf_ultralight_features[MfUltralightTypeNum] = { + [MfUltralightTypeUnknown] = + { + .device_name = "Mifare Ultralight", + .total_pages = 16, + .config_page = 0, + .feature_set = MfUltralightFeatureSupportCompatibleWrite, + }, + [MfUltralightTypeMfulC] = + { + .device_name = "Mifare Ultralight C", + .total_pages = 48, + .config_page = 0, + .feature_set = MfUltralightFeatureSupportCompatibleWrite | + MfUltralightFeatureSupportAuthenticate, + }, + [MfUltralightTypeNTAG203] = + { + .device_name = "NTAG203", + .total_pages = 42, + .config_page = 0, + .feature_set = MfUltralightFeatureSupportCompatibleWrite | + MfUltralightFeatureSupportCounterInMemory, + }, + [MfUltralightTypeUL11] = + { + .device_name = "Mifare Ultralight 11", + .total_pages = 20, + .config_page = 16, + .feature_set = + MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature | + MfUltralightFeatureSupportReadCounter | + MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportFastRead | + MfUltralightFeatureSupportIncCounter | MfUltralightFeatureSupportCompatibleWrite | + MfUltralightFeatureSupportPasswordAuth | MfUltralightFeatureSupportVcsl, + }, + [MfUltralightTypeUL21] = + { + .device_name = "Mifare Ultralight 21", + .total_pages = 41, + .config_page = 37, + .feature_set = + MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature | + MfUltralightFeatureSupportReadCounter | + MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportFastRead | + MfUltralightFeatureSupportIncCounter | MfUltralightFeatureSupportCompatibleWrite | + MfUltralightFeatureSupportPasswordAuth | MfUltralightFeatureSupportVcsl | + MfUltralightFeatureSupportDynamicLock, + }, + [MfUltralightTypeNTAG213] = + { + .device_name = "NTAG213", + .total_pages = 45, + .config_page = 41, + .feature_set = + MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature | + MfUltralightFeatureSupportReadCounter | MfUltralightFeatureSupportFastRead | + MfUltralightFeatureSupportCompatibleWrite | + MfUltralightFeatureSupportPasswordAuth | MfUltralightFeatureSupportSingleCounter | + MfUltralightFeatureSupportAsciiMirror | MfUltralightFeatureSupportDynamicLock, + }, + [MfUltralightTypeNTAG215] = + { + .device_name = "NTAG215", + .total_pages = 135, + .config_page = 131, + .feature_set = + MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature | + MfUltralightFeatureSupportReadCounter | MfUltralightFeatureSupportFastRead | + MfUltralightFeatureSupportCompatibleWrite | + MfUltralightFeatureSupportPasswordAuth | MfUltralightFeatureSupportSingleCounter | + MfUltralightFeatureSupportAsciiMirror | MfUltralightFeatureSupportDynamicLock, + }, + [MfUltralightTypeNTAG216] = + { + .device_name = "NTAG216", + .total_pages = 231, + .config_page = 227, + .feature_set = + MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature | + MfUltralightFeatureSupportReadCounter | MfUltralightFeatureSupportFastRead | + MfUltralightFeatureSupportCompatibleWrite | + MfUltralightFeatureSupportPasswordAuth | MfUltralightFeatureSupportSingleCounter | + MfUltralightFeatureSupportAsciiMirror | MfUltralightFeatureSupportDynamicLock, + }, + [MfUltralightTypeNTAGI2C1K] = + { + .device_name = "NTAG I2C 1K", + .total_pages = 231, + .config_page = 0, + .feature_set = + MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportFastRead | + MfUltralightFeatureSupportSectorSelect | MfUltralightFeatureSupportDynamicLock, + }, + [MfUltralightTypeNTAGI2C2K] = + { + .device_name = "NTAG I2C 2K", + .total_pages = 485, + .config_page = 0, + .feature_set = + MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportFastRead | + MfUltralightFeatureSupportSectorSelect | MfUltralightFeatureSupportDynamicLock, + }, + [MfUltralightTypeNTAGI2CPlus1K] = + { + .device_name = "NTAG I2C Plus 1K", + .total_pages = 236, + .config_page = 227, + .feature_set = + MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature | + MfUltralightFeatureSupportFastRead | MfUltralightFeatureSupportPasswordAuth | + MfUltralightFeatureSupportSectorSelect | MfUltralightFeatureSupportFastWrite | + MfUltralightFeatureSupportDynamicLock, + }, + [MfUltralightTypeNTAGI2CPlus2K] = + { + .device_name = "NTAG I2C Plus 2K", + .total_pages = 492, + .config_page = 227, + .feature_set = + MfUltralightFeatureSupportReadVersion | MfUltralightFeatureSupportReadSignature | + MfUltralightFeatureSupportFastRead | MfUltralightFeatureSupportPasswordAuth | + MfUltralightFeatureSupportSectorSelect | MfUltralightFeatureSupportFastWrite | + MfUltralightFeatureSupportDynamicLock, + }, +}; + +const NfcDeviceBase nfc_device_mf_ultralight = { + .protocol_name = MF_ULTRALIGHT_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)mf_ultralight_alloc, + .free = (NfcDeviceFree)mf_ultralight_free, + .reset = (NfcDeviceReset)mf_ultralight_reset, + .copy = (NfcDeviceCopy)mf_ultralight_copy, + .verify = (NfcDeviceVerify)mf_ultralight_verify, + .load = (NfcDeviceLoad)mf_ultralight_load, + .save = (NfcDeviceSave)mf_ultralight_save, + .is_equal = (NfcDeviceEqual)mf_ultralight_is_equal, + .get_name = (NfcDeviceGetName)mf_ultralight_get_device_name, + .get_uid = (NfcDeviceGetUid)mf_ultralight_get_uid, + .set_uid = (NfcDeviceSetUid)mf_ultralight_set_uid, + .get_base_data = (NfcDeviceGetBaseData)mf_ultralight_get_base_data, +}; + +MfUltralightData* mf_ultralight_alloc() { + MfUltralightData* data = malloc(sizeof(MfUltralightData)); + data->iso14443_3a_data = iso14443_3a_alloc(); + return data; +} + +void mf_ultralight_free(MfUltralightData* data) { + furi_assert(data); + + iso14443_3a_free(data->iso14443_3a_data); + free(data); +} + +void mf_ultralight_reset(MfUltralightData* data) { + furi_assert(data); + + iso14443_3a_reset(data->iso14443_3a_data); +} + +void mf_ultralight_copy(MfUltralightData* data, const MfUltralightData* other) { + furi_assert(data); + furi_assert(other); + + iso14443_3a_copy(data->iso14443_3a_data, other->iso14443_3a_data); + for(size_t i = 0; i < COUNT_OF(data->counter); i++) { + data->counter[i] = other->counter[i]; + } + for(size_t i = 0; i < COUNT_OF(data->tearing_flag); i++) { + data->tearing_flag[i] = other->tearing_flag[i]; + } + for(size_t i = 0; i < COUNT_OF(data->page); i++) { + data->page[i] = other->page[i]; + } + + data->type = other->type; + data->version = other->version; + data->signature = other->signature; + + data->pages_read = other->pages_read; + data->pages_total = other->pages_total; + data->auth_attempts = other->auth_attempts; +} + +static const char* + mf_ultralight_get_device_name_by_type(MfUltralightType type, NfcDeviceNameType name_type) { + if(name_type == NfcDeviceNameTypeShort && + (type == MfUltralightTypeUL11 || type == MfUltralightTypeUL21)) { + type = MfUltralightTypeUnknown; + } + + return mf_ultralight_features[type].device_name; +} + +bool mf_ultralight_verify(MfUltralightData* data, const FuriString* device_type) { + furi_assert(data); + + bool verified = false; + + for(MfUltralightType i = 0; i < MfUltralightTypeNum; i++) { + const char* name = mf_ultralight_get_device_name_by_type(i, NfcDeviceNameTypeFull); + verified = furi_string_equal(device_type, name); + if(verified) { + data->type = i; + break; + } + } + + return verified; +} + +bool mf_ultralight_load(MfUltralightData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + + FuriString* temp_str = furi_string_alloc(); + bool parsed = false; + + do { + // Read ISO14443_3A data + if(!iso14443_3a_load(data->iso14443_3a_data, ff, version)) break; + + // Read Ultralight specific data + // Read Mifare Ultralight format version + uint32_t data_format_version = 0; + if(!flipper_format_read_uint32( + ff, MF_ULTRALIGHT_FORMAT_VERSION_KEY, &data_format_version, 1)) { + if(!flipper_format_rewind(ff)) break; + } + + // Read Mifare Ultralight type + if(data_format_version > 1) { + if(!flipper_format_read_string(ff, MF_ULTRALIGHT_TYPE_KEY, temp_str)) break; + if(!mf_ultralight_verify(data, temp_str)) break; + } + + // Read signature + if(!flipper_format_read_hex( + ff, + MF_ULTRALIGHT_SIGNATURE_KEY, + data->signature.data, + sizeof(MfUltralightSignature))) + break; + // Read Mifare version + if(!flipper_format_read_hex( + ff, + MF_ULTRALIGHT_MIFARE_VERSION_KEY, + (uint8_t*)&data->version, + sizeof(MfUltralightVersion))) + break; + // Read counters and tearing flags + bool counters_parsed = true; + for(size_t i = 0; i < 3; i++) { + furi_string_printf(temp_str, "%s %d", MF_ULTRALIGHT_COUNTER_KEY, i); + if(!flipper_format_read_uint32( + ff, furi_string_get_cstr(temp_str), &data->counter[i].counter, 1)) { + counters_parsed = false; + break; + } + furi_string_printf(temp_str, "%s %d", MF_ULTRALIGHT_TEARING_KEY, i); + if(!flipper_format_read_hex( + ff, furi_string_get_cstr(temp_str), &data->tearing_flag[i].data, 1)) { + counters_parsed = false; + break; + } + } + if(!counters_parsed) break; + // Read pages + uint32_t pages_total = 0; + if(!flipper_format_read_uint32(ff, MF_ULTRALIGHT_PAGES_TOTAL_KEY, &pages_total, 1)) break; + uint32_t pages_read = 0; + if(data_format_version < mf_ultralight_data_format_version) { //-V547 + pages_read = pages_total; + } else { + if(!flipper_format_read_uint32(ff, MF_ULTRALIGHT_PAGES_READ_KEY, &pages_read, 1)) + break; + } + data->pages_total = pages_total; + data->pages_read = pages_read; + + if((pages_read > MF_ULTRALIGHT_MAX_PAGE_NUM) || (pages_total > MF_ULTRALIGHT_MAX_PAGE_NUM)) + break; + + bool pages_parsed = true; + for(size_t i = 0; i < pages_total; i++) { + furi_string_printf(temp_str, "%s %d", MF_ULTRALIGHT_PAGE_KEY, i); + if(!flipper_format_read_hex( + ff, + furi_string_get_cstr(temp_str), + data->page[i].data, + sizeof(MfUltralightPage))) { + pages_parsed = false; + break; + } + } + if(!pages_parsed) break; + + // Read authentication counter + if(!flipper_format_read_uint32( + ff, MF_ULTRALIGHT_FAILED_ATTEMPTS_KEY, &data->auth_attempts, 1)) { + data->auth_attempts = 0; + } + + parsed = true; + } while(false); + + furi_string_free(temp_str); + + return parsed; +} + +bool mf_ultralight_save(const MfUltralightData* data, FlipperFormat* ff) { + furi_assert(data); + + FuriString* temp_str = furi_string_alloc(); + bool saved = false; + + do { + if(!iso14443_3a_save(data->iso14443_3a_data, ff)) break; + + if(!flipper_format_write_comment_cstr(ff, MF_ULTRALIGHT_PROTOCOL_NAME " specific data")) + break; + if(!flipper_format_write_uint32( + ff, MF_ULTRALIGHT_FORMAT_VERSION_KEY, &mf_ultralight_data_format_version, 1)) + break; + + const char* device_type_name = + mf_ultralight_get_device_name_by_type(data->type, NfcDeviceNameTypeFull); + if(!flipper_format_write_string_cstr(ff, MF_ULTRALIGHT_TYPE_KEY, device_type_name)) break; + if(!flipper_format_write_hex( + ff, + MF_ULTRALIGHT_SIGNATURE_KEY, + data->signature.data, + sizeof(MfUltralightSignature))) + break; + if(!flipper_format_write_hex( + ff, + MF_ULTRALIGHT_MIFARE_VERSION_KEY, + (uint8_t*)&data->version, + sizeof(MfUltralightVersion))) + break; + + // Write conters and tearing flags data + bool counters_saved = true; + for(size_t i = 0; i < 3; i++) { + furi_string_printf(temp_str, "%s %d", MF_ULTRALIGHT_COUNTER_KEY, i); + if(!flipper_format_write_uint32( + ff, furi_string_get_cstr(temp_str), &data->counter[i].counter, 1)) { + counters_saved = false; + break; + } + furi_string_printf(temp_str, "%s %d", MF_ULTRALIGHT_TEARING_KEY, i); + if(!flipper_format_write_hex( + ff, furi_string_get_cstr(temp_str), &data->tearing_flag[i].data, 1)) { + counters_saved = false; + break; + } + } + if(!counters_saved) break; + + // Write pages data + uint32_t pages_total = data->pages_total; + uint32_t pages_read = data->pages_read; + if(!flipper_format_write_uint32(ff, MF_ULTRALIGHT_PAGES_TOTAL_KEY, &pages_total, 1)) break; + if(!flipper_format_write_uint32(ff, MF_ULTRALIGHT_PAGES_READ_KEY, &pages_read, 1)) break; + bool pages_saved = true; + for(size_t i = 0; i < data->pages_total; i++) { + furi_string_printf(temp_str, "%s %d", MF_ULTRALIGHT_PAGE_KEY, i); + if(!flipper_format_write_hex( + ff, + furi_string_get_cstr(temp_str), + data->page[i].data, + sizeof(MfUltralightPage))) { + pages_saved = false; + break; + } + } + if(!pages_saved) break; + + // Write authentication counter + if(!flipper_format_write_uint32( + ff, MF_ULTRALIGHT_FAILED_ATTEMPTS_KEY, &data->auth_attempts, 1)) + break; + + saved = true; + } while(false); + + furi_string_free(temp_str); + + return saved; +} + +bool mf_ultralight_is_equal(const MfUltralightData* data, const MfUltralightData* other) { + bool is_equal = false; + bool data_array_is_equal = true; + + do { + if(!iso14443_3a_is_equal(data->iso14443_3a_data, other->iso14443_3a_data)) break; + if(data->type != other->type) break; + if(data->pages_read != other->pages_read) break; + if(data->pages_total != other->pages_total) break; + if(data->auth_attempts != other->auth_attempts) break; + + if(memcmp(&data->version, &other->version, sizeof(data->version)) != 0) break; + if(memcmp(&data->signature, &other->signature, sizeof(data->signature)) != 0) break; + + for(size_t i = 0; i < COUNT_OF(data->counter); i++) { + if(memcmp(&data->counter[i], &other->counter[i], sizeof(data->counter[i])) != 0) { + data_array_is_equal = false; + break; + } + } + if(!data_array_is_equal) break; + + for(size_t i = 0; i < COUNT_OF(data->tearing_flag); i++) { + if(memcmp( + &data->tearing_flag[i], + &other->tearing_flag[i], + sizeof(data->tearing_flag[i])) != 0) { + data_array_is_equal = false; + break; + } + } + if(!data_array_is_equal) break; + + for(size_t i = 0; i < COUNT_OF(data->page); i++) { + if(memcmp(&data->page[i], &other->page[i], sizeof(data->page[i])) != 0) { + data_array_is_equal = false; + break; + } + } + if(!data_array_is_equal) break; + + is_equal = true; + } while(false); + + return is_equal; +} + +const char* + mf_ultralight_get_device_name(const MfUltralightData* data, NfcDeviceNameType name_type) { + furi_assert(data); + furi_assert(data->type < MfUltralightTypeNum); + + return mf_ultralight_get_device_name_by_type(data->type, name_type); +} + +const uint8_t* mf_ultralight_get_uid(const MfUltralightData* data, size_t* uid_len) { + furi_assert(data); + + return iso14443_3a_get_uid(data->iso14443_3a_data, uid_len); +} + +bool mf_ultralight_set_uid(MfUltralightData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + return iso14443_3a_set_uid(data->iso14443_3a_data, uid, uid_len); +} + +Iso14443_3aData* mf_ultralight_get_base_data(const MfUltralightData* data) { + furi_assert(data); + + return data->iso14443_3a_data; +} + +MfUltralightType mf_ultralight_get_type_by_version(MfUltralightVersion* version) { + furi_assert(version); + + MfUltralightType type = MfUltralightTypeUnknown; + + if(version->storage_size == 0x0B || version->storage_size == 0x00) { + type = MfUltralightTypeUL11; + } else if(version->storage_size == 0x0E) { + type = MfUltralightTypeUL21; + } else if(version->storage_size == 0x0F) { + type = MfUltralightTypeNTAG213; + } else if(version->storage_size == 0x11) { + type = MfUltralightTypeNTAG215; + } else if(version->prod_subtype == 5 && version->prod_ver_major == 2) { + if(version->prod_ver_minor == 1) { + if(version->storage_size == 0x13) { + type = MfUltralightTypeNTAGI2C1K; + } else if(version->storage_size == 0x15) { + type = MfUltralightTypeNTAGI2C2K; + } + } else if(version->prod_ver_minor == 2) { + if(version->storage_size == 0x13) { + type = MfUltralightTypeNTAGI2CPlus1K; + } else if(version->storage_size == 0x15) { + type = MfUltralightTypeNTAGI2CPlus2K; + } + } + } else if(version->storage_size == 0x13) { + type = MfUltralightTypeNTAG216; + } + + return type; +} + +uint16_t mf_ultralight_get_pages_total(MfUltralightType type) { + return mf_ultralight_features[type].total_pages; +} + +uint32_t mf_ultralight_get_feature_support_set(MfUltralightType type) { + return mf_ultralight_features[type].feature_set; +} + +bool mf_ultralight_detect_protocol(const Iso14443_3aData* iso14443_3a_data) { + furi_assert(iso14443_3a_data); + + bool mfu_detected = (iso14443_3a_data->atqa[0] == 0x44) && + (iso14443_3a_data->atqa[1] == 0x00) && (iso14443_3a_data->sak == 0x00); + + return mfu_detected; +} + +uint16_t mf_ultralight_get_config_page_num(MfUltralightType type) { + return mf_ultralight_features[type].config_page; +} + +uint8_t mf_ultralight_get_pwd_page_num(MfUltralightType type) { + uint8_t config_page = mf_ultralight_features[type].config_page; + return (config_page != 0) ? config_page + 2 : 0; +} + +bool mf_ultralight_is_page_pwd_or_pack(MfUltralightType type, uint16_t page) { + uint8_t pwd_page = mf_ultralight_get_pwd_page_num(type); + uint8_t pack_page = pwd_page + 1; + return ((pwd_page != 0) && (page == pwd_page || page == pack_page)); +} + +bool mf_ultralight_support_feature(const uint32_t feature_set, const uint32_t features_to_check) { + return (feature_set & features_to_check) != 0; +} + +bool mf_ultralight_get_config_page(const MfUltralightData* data, MfUltralightConfigPages** config) { + furi_assert(data); + furi_assert(config); + + bool config_pages_found = false; + + uint16_t config_page = mf_ultralight_features[data->type].config_page; + if(config_page != 0) { + *config = (MfUltralightConfigPages*)&data->page[config_page]; //-V1027 + config_pages_found = true; + } + + return config_pages_found; +} + +bool mf_ultralight_is_all_data_read(const MfUltralightData* data) { + furi_assert(data); + + bool all_read = false; + if(data->pages_read == data->pages_total || + (data->type == MfUltralightTypeMfulC && data->pages_read == data->pages_total - 4)) { + // Having read all the pages doesn't mean that we've got everything. + // By default PWD is 0xFFFFFFFF, but if read back it is always 0x00000000, + // so a default read on an auth-supported NTAG is never complete. + uint32_t feature_set = mf_ultralight_get_feature_support_set(data->type); + if(!mf_ultralight_support_feature(feature_set, MfUltralightFeatureSupportPasswordAuth)) { + all_read = true; + } else { + MfUltralightConfigPages* config = NULL; + if(mf_ultralight_get_config_page(data, &config)) { + uint32_t pass = + nfc_util_bytes2num(config->password.data, sizeof(MfUltralightAuthPassword)); + uint16_t pack = + nfc_util_bytes2num(config->pack.data, sizeof(MfUltralightAuthPack)); + all_read = ((pass != 0) || (pack != 0)); + } + } + } + + return all_read; +} + +bool mf_ultralight_is_counter_configured(const MfUltralightData* data) { + furi_assert(data); + + MfUltralightConfigPages* config = NULL; + bool configured = false; + + switch(data->type) { + case MfUltralightTypeNTAG213: + case MfUltralightTypeNTAG215: + case MfUltralightTypeNTAG216: + if(mf_ultralight_get_config_page(data, &config)) { + configured = config->access.nfc_cnt_en; + } + break; + + default: + configured = true; + break; + } + + return configured; +} diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight.h new file mode 100644 index 00000000000..4786b18253c --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight.h @@ -0,0 +1,229 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MF_ULTRALIGHT_TEARING_FLAG_DEFAULT (0xBD) + +#define MF_ULTRALIGHT_CMD_GET_VERSION (0x60) +#define MF_ULTRALIGHT_CMD_READ_PAGE (0x30) +#define MF_ULTRALIGHT_CMD_FAST_READ (0x3A) +#define MF_ULTRALIGHT_CMD_SECTOR_SELECT (0xC2) +#define MF_ULTRALIGHT_CMD_COMP_WRITE (0xA0) +#define MF_ULTRALIGHT_CMD_WRITE_PAGE (0xA2) +#define MF_ULTRALIGHT_CMD_FAST_WRITE (0xA6) +#define MF_ULTRALIGHT_CMD_READ_SIG (0x3C) +#define MF_ULTRALIGHT_CMD_READ_CNT (0x39) +#define MF_ULTRALIGHT_CMD_INCR_CNT (0xA5) +#define MF_ULTRALIGHT_CMD_CHECK_TEARING (0x3E) +#define MF_ULTRALIGHT_CMD_PWD_AUTH (0x1B) +#define MF_ULTRALIGHT_CMD_AUTH (0x1A) +#define MF_ULTRALIGHT_CMD_VCSL (0x4B) + +#define MF_ULTRALIGHT_CMD_ACK (0x0A) +#define MF_ULTRALIGHT_CMD_NACK (0x00) +#define MF_ULTRALIGHT_CMD_AUTH_NAK (0x04) + +#define MF_ULTRALIGHT_MAX_CNTR_VAL (0x00FFFFFF) +#define MF_ULTRALIGHT_MAX_PAGE_NUM (510) +#define MF_ULTRALIGHT_PAGE_SIZE (4U) +#define MF_ULTRALIGHT_SIGNATURE_SIZE (32) +#define MF_ULTRALIGHT_COUNTER_SIZE (3) +#define MF_ULTRALIGHT_COUNTER_NUM (3) +#define MF_ULTRALIGHT_TEARING_FLAG_SIZE (1) +#define MF_ULTRALIGHT_TEARING_FLAG_NUM (3) +#define MF_ULTRALIGHT_AUTH_PASSWORD_SIZE (4) +#define MF_ULTRALIGHT_AUTH_PACK_SIZE (2) +#define MF_ULTRALIGHT_AUTH_RESPONSE_SIZE (9) + +typedef enum { + MfUltralightErrorNone, + MfUltralightErrorNotPresent, + MfUltralightErrorProtocol, + MfUltralightErrorAuth, + MfUltralightErrorTimeout, +} MfUltralightError; + +typedef enum { + MfUltralightTypeUnknown, + MfUltralightTypeNTAG203, + MfUltralightTypeMfulC, + MfUltralightTypeUL11, + MfUltralightTypeUL21, + MfUltralightTypeNTAG213, + MfUltralightTypeNTAG215, + MfUltralightTypeNTAG216, + MfUltralightTypeNTAGI2C1K, + MfUltralightTypeNTAGI2C2K, + MfUltralightTypeNTAGI2CPlus1K, + MfUltralightTypeNTAGI2CPlus2K, + + MfUltralightTypeNum, +} MfUltralightType; + +typedef enum { + MfUltralightFeatureSupportReadVersion = (1U << 0), + MfUltralightFeatureSupportReadSignature = (1U << 1), + MfUltralightFeatureSupportReadCounter = (1U << 2), + MfUltralightFeatureSupportCheckTearingFlag = (1U << 3), + MfUltralightFeatureSupportFastRead = (1U << 4), + MfUltralightFeatureSupportIncCounter = (1U << 5), + MfUltralightFeatureSupportFastWrite = (1U << 6), + MfUltralightFeatureSupportCompatibleWrite = (1U << 7), + MfUltralightFeatureSupportPasswordAuth = (1U << 8), + MfUltralightFeatureSupportVcsl = (1U << 9), + MfUltralightFeatureSupportSectorSelect = (1U << 10), + MfUltralightFeatureSupportSingleCounter = (1U << 11), + MfUltralightFeatureSupportAsciiMirror = (1U << 12), + MfUltralightFeatureSupportCounterInMemory = (1U << 13), + MfUltralightFeatureSupportDynamicLock = (1U << 14), + MfUltralightFeatureSupportAuthenticate = (1U << 15), +} MfUltralightFeatureSupport; + +typedef struct { + uint8_t data[MF_ULTRALIGHT_PAGE_SIZE]; +} MfUltralightPage; + +typedef struct { + MfUltralightPage page[4]; +} MfUltralightPageReadCommandData; + +typedef struct { + uint8_t header; + uint8_t vendor_id; + uint8_t prod_type; + uint8_t prod_subtype; + uint8_t prod_ver_major; + uint8_t prod_ver_minor; + uint8_t storage_size; + uint8_t protocol_type; +} MfUltralightVersion; + +typedef struct { + uint8_t data[MF_ULTRALIGHT_SIGNATURE_SIZE]; +} MfUltralightSignature; + +typedef union { + uint32_t counter; + uint8_t data[MF_ULTRALIGHT_COUNTER_SIZE]; +} MfUltralightCounter; + +typedef struct { + uint8_t data; +} MfUltralightTearingFlag; + +typedef struct { + uint8_t data[MF_ULTRALIGHT_AUTH_PASSWORD_SIZE]; +} MfUltralightAuthPassword; + +typedef struct { + uint8_t data[MF_ULTRALIGHT_AUTH_PACK_SIZE]; +} MfUltralightAuthPack; + +typedef enum { + MfUltralightMirrorNone, + MfUltralightMirrorUid, + MfUltralightMirrorCounter, + MfUltralightMirrorUidCounter, +} MfUltralightMirrorConf; + +typedef struct FURI_PACKED { + union { + uint8_t value; + struct { + uint8_t rfui1 : 2; + bool strg_mod_en : 1; + bool rfui2 : 1; + uint8_t mirror_byte : 2; + MfUltralightMirrorConf mirror_conf : 2; + }; + } mirror; + uint8_t rfui1; + uint8_t mirror_page; + uint8_t auth0; + union { + uint8_t value; + struct { + uint8_t authlim : 3; + bool nfc_cnt_pwd_prot : 1; + bool nfc_cnt_en : 1; + bool nfc_dis_sec1 : 1; // NTAG I2C Plus only + bool cfglck : 1; + bool prot : 1; + }; + } access; + uint8_t vctid; + uint8_t rfui2[2]; + MfUltralightAuthPassword password; + MfUltralightAuthPack pack; + uint8_t rfui3[2]; +} MfUltralightConfigPages; + +typedef struct { + Iso14443_3aData* iso14443_3a_data; + MfUltralightType type; + MfUltralightVersion version; + MfUltralightSignature signature; + MfUltralightCounter counter[MF_ULTRALIGHT_COUNTER_NUM]; + MfUltralightTearingFlag tearing_flag[MF_ULTRALIGHT_TEARING_FLAG_NUM]; + MfUltralightPage page[MF_ULTRALIGHT_MAX_PAGE_NUM]; + uint16_t pages_read; + uint16_t pages_total; + uint32_t auth_attempts; +} MfUltralightData; + +extern const NfcDeviceBase nfc_device_mf_ultralight; + +MfUltralightData* mf_ultralight_alloc(); + +void mf_ultralight_free(MfUltralightData* data); + +void mf_ultralight_reset(MfUltralightData* data); + +void mf_ultralight_copy(MfUltralightData* data, const MfUltralightData* other); + +bool mf_ultralight_verify(MfUltralightData* data, const FuriString* device_type); + +bool mf_ultralight_load(MfUltralightData* data, FlipperFormat* ff, uint32_t version); + +bool mf_ultralight_save(const MfUltralightData* data, FlipperFormat* ff); + +bool mf_ultralight_is_equal(const MfUltralightData* data, const MfUltralightData* other); + +const char* + mf_ultralight_get_device_name(const MfUltralightData* data, NfcDeviceNameType name_type); + +const uint8_t* mf_ultralight_get_uid(const MfUltralightData* data, size_t* uid_len); + +bool mf_ultralight_set_uid(MfUltralightData* data, const uint8_t* uid, size_t uid_len); + +Iso14443_3aData* mf_ultralight_get_base_data(const MfUltralightData* data); + +MfUltralightType mf_ultralight_get_type_by_version(MfUltralightVersion* version); + +uint16_t mf_ultralight_get_pages_total(MfUltralightType type); + +uint32_t mf_ultralight_get_feature_support_set(MfUltralightType type); + +uint16_t mf_ultralight_get_config_page_num(MfUltralightType type); + +uint8_t mf_ultralight_get_pwd_page_num(MfUltralightType type); + +bool mf_ultralight_is_page_pwd_or_pack(MfUltralightType type, uint16_t page_num); + +bool mf_ultralight_support_feature(const uint32_t feature_set, const uint32_t features_to_check); + +bool mf_ultralight_get_config_page(const MfUltralightData* data, MfUltralightConfigPages** config); + +bool mf_ultralight_is_all_data_read(const MfUltralightData* data); + +bool mf_ultralight_detect_protocol(const Iso14443_3aData* iso14443_3a_data); + +bool mf_ultralight_is_counter_configured(const MfUltralightData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.c new file mode 100644 index 00000000000..5bef2a354c9 --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.c @@ -0,0 +1,772 @@ +#include "mf_ultralight_listener_i.h" +#include "mf_ultralight_listener_defs.h" + +#include + +#include + +#define TAG "MfUltralightListener" + +#define MF_ULTRALIGHT_LISTENER_MAX_TX_BUFF_SIZE (256) + +typedef enum { + MfUltralightListenerAccessTypeRead, + MfUltralightListenerAccessTypeWrite, +} MfUltralightListenerAccessType; + +typedef struct { + uint8_t cmd; + size_t cmd_len_bits; + MfUltralightListenerCommandCallback callback; +} MfUltralightListenerCmdHandler; + +static bool mf_ultralight_listener_check_access( + MfUltralightListener* instance, + uint16_t start_page, + MfUltralightListenerAccessType access_type) { + bool access_success = false; + bool is_write_op = (access_type == MfUltralightListenerAccessTypeWrite); + + do { + if(!mf_ultralight_support_feature( + instance->features, MfUltralightFeatureSupportPasswordAuth)) { + access_success = true; + break; + } + if(instance->auth_state != MfUltralightListenerAuthStateSuccess) { + if((instance->config->auth0 <= start_page) && + (instance->config->access.prot || is_write_op)) { + break; + } + } + if(instance->config->access.cfglck && is_write_op) { + uint16_t config_page_start = instance->data->pages_total - 4; + if((start_page == config_page_start) || (start_page == config_page_start + 1)) { + break; + } + } + + access_success = true; + } while(false); + + return access_success; +} + +static void mf_ultralight_listener_send_short_resp(MfUltralightListener* instance, uint8_t data) { + furi_assert(instance->tx_buffer); + + bit_buffer_set_size(instance->tx_buffer, 4); + bit_buffer_set_byte(instance->tx_buffer, 0, data); + iso14443_3a_listener_tx(instance->iso14443_3a_listener, instance->tx_buffer); +}; + +static void mf_ultralight_listener_perform_read( + MfUltralightPage* pages, + MfUltralightListener* instance, + uint16_t start_page, + uint8_t page_cnt, + bool do_i2c_page_check) { + uint16_t pages_total = instance->data->pages_total; + mf_ultralight_mirror_read_prepare(start_page, instance); + for(uint8_t i = 0, rollover = 0; i < page_cnt; i++) { + uint16_t page = start_page + i; + + bool page_restricted = !mf_ultralight_listener_check_access( + instance, page, MfUltralightListenerAccessTypeRead); + + if(do_i2c_page_check && !mf_ultralight_i2c_validate_pages(page, page, instance)) + memset(pages[i].data, 0, sizeof(MfUltralightPage)); + else if(mf_ultralight_is_page_pwd_or_pack(instance->data->type, page)) + memset(pages[i].data, 0, sizeof(MfUltralightPage)); + else { + if(do_i2c_page_check) + page = mf_ultralight_i2c_provide_page_by_requested(page, instance); + + page = page_restricted ? rollover++ : page % pages_total; + pages[i] = instance->data->page[page]; + + mf_ultralight_mirror_read_handler(page, pages[i].data, instance); + } + } + mf_ultralight_single_counter_try_increase(instance); +} + +static MfUltralightCommand mf_ultralight_listener_perform_write( + MfUltralightListener* instance, + const uint8_t* const rx_data, + uint16_t start_page, + bool do_i2c_check) { + MfUltralightCommand command = MfUltralightCommandProcessedACK; + + if(start_page < 2 && instance->sector == 0) + command = MfUltralightCommandNotProcessedNAK; + else if(start_page == 2 && instance->sector == 0) + mf_ultralight_static_lock_bytes_write(instance->static_lock, *((uint16_t*)&rx_data[2])); + else if(start_page == 3 && instance->sector == 0) + mf_ultralight_capability_container_write(&instance->data->page[start_page], rx_data); + else if(mf_ultralight_is_page_dynamic_lock(instance, start_page)) + mf_ultralight_dynamic_lock_bytes_write(instance->dynamic_lock, *((uint32_t*)rx_data)); + else { + uint16_t page = start_page; + if(do_i2c_check) page = mf_ultralight_i2c_provide_page_by_requested(start_page, instance); + + memcpy(instance->data->page[page].data, rx_data, sizeof(MfUltralightPage)); + } + + return command; +} + +static MfUltralightCommand + mf_ultralight_listener_read_page_handler(MfUltralightListener* instance, BitBuffer* buffer) { + uint16_t start_page = bit_buffer_get_byte(buffer, 1); + uint16_t pages_total = instance->data->pages_total; + MfUltralightCommand command = MfUltralightCommandNotProcessedNAK; + + FURI_LOG_T(TAG, "CMD_READ: %d", start_page); + + do { + bool do_i2c_check = mf_ultralight_is_i2c_tag(instance->data->type); + + if(do_i2c_check) { + if(!mf_ultralight_i2c_validate_pages(start_page, start_page, instance)) break; + } else if(pages_total < start_page) { + break; + } + + if(!mf_ultralight_listener_check_access( + instance, start_page, MfUltralightListenerAccessTypeRead)) { + break; + } + + MfUltralightPage pages[4] = {}; + mf_ultralight_listener_perform_read(pages, instance, start_page, 4, do_i2c_check); + + bit_buffer_copy_bytes(instance->tx_buffer, (uint8_t*)pages, sizeof(pages)); + iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, instance->tx_buffer); + command = MfUltralightCommandProcessed; + + } while(false); + + return command; +} + +static MfUltralightCommand + mf_ultralight_listener_fast_read_handler(MfUltralightListener* instance, BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedSilent; + FURI_LOG_T(TAG, "CMD_FAST_READ"); + + do { + if(!mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportFastRead)) + break; + uint16_t pages_total = instance->data->pages_total; + uint16_t start_page = bit_buffer_get_byte(buffer, 1); + uint16_t end_page = bit_buffer_get_byte(buffer, 2); + bool do_i2c_check = mf_ultralight_is_i2c_tag(instance->data->type); + + if(do_i2c_check) { + if(!mf_ultralight_i2c_validate_pages(start_page, end_page, instance)) { + command = MfUltralightCommandNotProcessedNAK; + break; + } + } else if(end_page > pages_total - 1) { + command = MfUltralightCommandNotProcessedNAK; + break; + } + + if(end_page < start_page) { + command = MfUltralightCommandNotProcessedNAK; + break; + } + + if(!mf_ultralight_listener_check_access( + instance, start_page, MfUltralightListenerAccessTypeRead) || + !mf_ultralight_listener_check_access( + instance, end_page, MfUltralightListenerAccessTypeRead)) { + command = MfUltralightCommandNotProcessedNAK; + break; + } + + MfUltralightPage pages[64] = {}; + uint8_t page_cnt = (end_page - start_page) + 1; + mf_ultralight_listener_perform_read(pages, instance, start_page, page_cnt, do_i2c_check); + + bit_buffer_copy_bytes(instance->tx_buffer, (uint8_t*)pages, page_cnt * 4); + iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, instance->tx_buffer); + command = MfUltralightCommandProcessed; + } while(false); + + return command; +} + +static MfUltralightCommand + mf_ultralight_listener_write_page_handler(MfUltralightListener* instance, BitBuffer* buffer) { + uint8_t start_page = bit_buffer_get_byte(buffer, 1); + uint16_t pages_total = instance->data->pages_total; + MfUltralightCommand command = MfUltralightCommandNotProcessedNAK; + + FURI_LOG_T(TAG, "CMD_WRITE"); + + do { + bool do_i2c_check = mf_ultralight_is_i2c_tag(instance->data->type); + + if(do_i2c_check) { + if(!mf_ultralight_i2c_validate_pages(start_page, start_page, instance)) break; + } else if(pages_total < start_page) { + break; + } + + if(!mf_ultralight_listener_check_access( + instance, start_page, MfUltralightListenerAccessTypeWrite)) + break; + + if(mf_ultralight_static_lock_check_page(instance->static_lock, start_page)) break; + if(mf_ultralight_dynamic_lock_check_page(instance, start_page)) break; + + const uint8_t* rx_data = bit_buffer_get_data(buffer); + command = + mf_ultralight_listener_perform_write(instance, &rx_data[2], start_page, do_i2c_check); + } while(false); + + return command; +} + +static MfUltralightCommand + mf_ultralight_listener_fast_write_handler(MfUltralightListener* instance, BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedSilent; + FURI_LOG_T(TAG, "CMD_FAST_WRITE"); + + do { + if(!mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportFastWrite)) + break; + + uint8_t start_page = bit_buffer_get_byte(buffer, 1); + uint8_t end_page = bit_buffer_get_byte(buffer, 2); + if(start_page != 0xF0 || end_page != 0xFF) { + command = MfUltralightCommandNotProcessedNAK; + break; + } + + // No SRAM emulation support + + command = MfUltralightCommandProcessedACK; + } while(false); + + return command; +} + +static MfUltralightCommand + mf_ultralight_listener_read_version_handler(MfUltralightListener* instance, BitBuffer* buffer) { + UNUSED(buffer); + MfUltralightCommand command = MfUltralightCommandNotProcessedSilent; + + FURI_LOG_T(TAG, "CMD_GET_VERSION"); + + if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportReadVersion)) { + bit_buffer_copy_bytes( + instance->tx_buffer, (uint8_t*)&instance->data->version, sizeof(MfUltralightVersion)); + iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, instance->tx_buffer); + command = MfUltralightCommandProcessed; + } + + return command; +} + +static MfUltralightCommand mf_ultralight_listener_read_signature_handler( + MfUltralightListener* instance, + BitBuffer* buffer) { + UNUSED(buffer); + MfUltralightCommand command = MfUltralightCommandNotProcessedSilent; + + FURI_LOG_T(TAG, "CMD_READ_SIG"); + + if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportReadSignature)) { + bit_buffer_copy_bytes( + instance->tx_buffer, instance->data->signature.data, sizeof(MfUltralightSignature)); + iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, instance->tx_buffer); + command = MfUltralightCommandProcessed; + } + + return command; +} + +static MfUltralightCommand + mf_ultralight_listener_read_counter_handler(MfUltralightListener* instance, BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedNAK; + + FURI_LOG_T(TAG, "CMD_READ_CNT"); + + do { + uint8_t counter_num = bit_buffer_get_byte(buffer, 1); + if(!mf_ultralight_support_feature( + instance->features, MfUltralightFeatureSupportReadCounter)) + break; + + if(mf_ultralight_support_feature( + instance->features, MfUltralightFeatureSupportSingleCounter)) { + if(instance->config == NULL) break; + + if(!instance->config->access.nfc_cnt_en || counter_num != 2) break; + + if(instance->config->access.nfc_cnt_pwd_prot) { + if(instance->auth_state != MfUltralightListenerAuthStateSuccess) { + break; + } + } + } + + if(counter_num > 2) break; + uint8_t cnt_value[3] = { + (instance->data->counter[counter_num].counter >> 0) & 0xff, + (instance->data->counter[counter_num].counter >> 8) & 0xff, + (instance->data->counter[counter_num].counter >> 16) & 0xff, + }; + bit_buffer_copy_bytes(instance->tx_buffer, cnt_value, sizeof(cnt_value)); + iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, instance->tx_buffer); + command = MfUltralightCommandProcessed; + } while(false); + + return command; +} + +static MfUltralightCommand mf_ultralight_listener_increase_counter_handler( + MfUltralightListener* instance, + BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedNAK; + + FURI_LOG_T(TAG, "CMD_INCR_CNT"); + + do { + if(!mf_ultralight_support_feature( + instance->features, MfUltralightFeatureSupportIncCounter)) { + command = MfUltralightCommandNotProcessedSilent; + break; + } + + uint8_t counter_num = bit_buffer_get_byte(buffer, 1); + if(counter_num > 2) break; + + if(instance->data->counter[counter_num].counter == MF_ULTRALIGHT_MAX_CNTR_VAL) { + command = MfUltralightCommandProcessed; + break; + } + + MfUltralightCounter buf_counter = {}; + bit_buffer_write_bytes_mid(buffer, buf_counter.data, 2, sizeof(buf_counter.data)); + uint32_t incr_value = buf_counter.counter; + + if(instance->data->counter[counter_num].counter + incr_value > MF_ULTRALIGHT_MAX_CNTR_VAL) + break; + + instance->data->counter[counter_num].counter += incr_value; + command = MfUltralightCommandProcessedACK; + } while(false); + + return command; +} + +static MfUltralightCommand mf_ultralight_listener_check_tearing_handler( + MfUltralightListener* instance, + BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedNAK; + + FURI_LOG_T(TAG, "CMD_CHECK_TEARING"); + + do { + uint8_t tearing_flag_num = bit_buffer_get_byte(buffer, 1); + if(!mf_ultralight_support_feature( + instance->features, + MfUltralightFeatureSupportCheckTearingFlag | + MfUltralightFeatureSupportSingleCounter)) { + break; + } + if(mf_ultralight_support_feature( + instance->features, MfUltralightFeatureSupportSingleCounter) && + (tearing_flag_num != 2)) { + break; + } + if(tearing_flag_num >= MF_ULTRALIGHT_TEARING_FLAG_NUM) { + break; + } + + bit_buffer_set_size_bytes(instance->tx_buffer, 1); + bit_buffer_set_byte( + instance->tx_buffer, 0, instance->data->tearing_flag[tearing_flag_num].data); + iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, instance->tx_buffer); + command = MfUltralightCommandProcessed; + + } while(false); + + return command; +} + +static MfUltralightCommand + mf_ultralight_listener_vcsl_handler(MfUltralightListener* instance, BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedSilent; + UNUSED(instance); + UNUSED(buffer); + FURI_LOG_T(TAG, "CMD_VCSL"); + do { + if(!mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportVcsl)) + break; + + MfUltralightConfigPages* config; + if(!mf_ultralight_get_config_page(instance->data, &config)) break; + + bit_buffer_set_size_bytes(instance->tx_buffer, 1); + bit_buffer_set_byte(instance->tx_buffer, 0, config->vctid); + iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, instance->tx_buffer); + command = MfUltralightCommandProcessed; + } while(false); + + return command; +} + +static MfUltralightCommand + mf_ultralight_listener_auth_handler(MfUltralightListener* instance, BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedNAK; + + FURI_LOG_T(TAG, "CMD_AUTH"); + + do { + if(!mf_ultralight_support_feature( + instance->features, MfUltralightFeatureSupportPasswordAuth)) + break; + + MfUltralightAuthPassword password; + bit_buffer_write_bytes_mid(buffer, password.data, 1, sizeof(password.data)); + + if(instance->callback) { + instance->mfu_event_data.password = password; + instance->mfu_event.type = MfUltralightListenerEventTypeAuth; + instance->callback(instance->generic_event, instance->context); + } + + bool auth_success = + mf_ultralight_auth_check_password(&instance->config->password, &password); + bool card_locked = mf_ultralight_auth_limit_check_and_update(instance, auth_success); + + if(card_locked) { + command = MfUltralightCommandNotProcessedAuthNAK; + break; + } + + if(!auth_success) break; + + bit_buffer_copy_bytes( + instance->tx_buffer, instance->config->pack.data, sizeof(MfUltralightAuthPack)); + instance->auth_state = MfUltralightListenerAuthStateSuccess; + iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, instance->tx_buffer); + + command = MfUltralightCommandProcessed; + } while(false); + + return command; +} + +static MfUltralightCommand + mf_ultralight_comp_write_handler_p2(MfUltralightListener* instance, BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedNAK; + FURI_LOG_T(TAG, "CMD_CM_WR_2"); + + do { + if(bit_buffer_get_size_bytes(buffer) != 16) break; + + const uint8_t* rx_data = bit_buffer_get_data(buffer); + uint8_t start_page = instance->composite_cmd.data; + + command = mf_ultralight_listener_perform_write(instance, rx_data, start_page, false); + } while(false); + + return command; +} + +static MfUltralightCommand + mf_ultralight_comp_write_handler_p1(MfUltralightListener* instance, BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedSilent; + + FURI_LOG_T(TAG, "CMD_CM_WR_1"); + + do { + if(!mf_ultralight_support_feature( + instance->features, MfUltralightFeatureSupportCompatibleWrite)) + break; + + uint8_t start_page = bit_buffer_get_byte(buffer, 1); + uint16_t last_page = instance->data->pages_total - 1; + + if(start_page < 2 || start_page > last_page) { + command = MfUltralightCommandNotProcessedNAK; + break; + } + + if(!mf_ultralight_listener_check_access( + instance, start_page, MfUltralightListenerAccessTypeWrite)) { + command = MfUltralightCommandNotProcessedNAK; + break; + } + + if(mf_ultralight_static_lock_check_page(instance->static_lock, start_page) || + mf_ultralight_dynamic_lock_check_page(instance, start_page)) { + command = MfUltralightCommandNotProcessedNAK; + break; + } + + instance->composite_cmd.data = start_page; + command = MfUltralightCommandProcessedACK; + mf_ultralight_composite_command_set_next(instance, mf_ultralight_comp_write_handler_p2); + } while(false); + + return command; +} + +static MfUltralightCommand + mf_ultralight_sector_select_handler_p2(MfUltralightListener* instance, BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedNAK; + UNUSED(instance); + UNUSED(buffer); + FURI_LOG_T(TAG, "CMD_SEC_SEL_2"); + + do { + if(bit_buffer_get_size_bytes(buffer) != 4) break; + uint8_t sector = bit_buffer_get_byte(buffer, 0); + if(sector == 0xFF) break; + + instance->sector = sector; + command = MfUltralightCommandProcessedSilent; + } while(false); + + return command; +} + +static MfUltralightCommand + mf_ultralight_sector_select_handler_p1(MfUltralightListener* instance, BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedNAK; + UNUSED(buffer); + FURI_LOG_T(TAG, "CMD_SEC_SEL_1"); + + do { + if(!mf_ultralight_support_feature( + instance->features, MfUltralightFeatureSupportSectorSelect) && + bit_buffer_get_byte(buffer, 1) == 0xFF) + break; + + command = MfUltralightCommandProcessedACK; + mf_ultralight_composite_command_set_next(instance, mf_ultralight_sector_select_handler_p2); + } while(false); + + return command; +} + +static const MfUltralightListenerCmdHandler mf_ultralight_command[] = { + { + .cmd = MF_ULTRALIGHT_CMD_READ_PAGE, + .cmd_len_bits = 2 * 8, + .callback = mf_ultralight_listener_read_page_handler, + }, + { + .cmd = MF_ULTRALIGHT_CMD_FAST_READ, + .cmd_len_bits = 3 * 8, + .callback = mf_ultralight_listener_fast_read_handler, + }, + { + .cmd = MF_ULTRALIGHT_CMD_WRITE_PAGE, + .cmd_len_bits = 6 * 8, + .callback = mf_ultralight_listener_write_page_handler, + }, + { + .cmd = MF_ULTRALIGHT_CMD_FAST_WRITE, + .cmd_len_bits = 67 * 8, + .callback = mf_ultralight_listener_fast_write_handler, + }, + { + .cmd = MF_ULTRALIGHT_CMD_GET_VERSION, + .cmd_len_bits = 8, + .callback = mf_ultralight_listener_read_version_handler, + }, + { + .cmd = MF_ULTRALIGHT_CMD_READ_SIG, + .cmd_len_bits = 2 * 8, + .callback = mf_ultralight_listener_read_signature_handler, + }, + { + .cmd = MF_ULTRALIGHT_CMD_READ_CNT, + .cmd_len_bits = 2 * 8, + .callback = mf_ultralight_listener_read_counter_handler, + }, + { + .cmd = MF_ULTRALIGHT_CMD_CHECK_TEARING, + .cmd_len_bits = 2 * 8, + .callback = mf_ultralight_listener_check_tearing_handler, + }, + { + .cmd = MF_ULTRALIGHT_CMD_PWD_AUTH, + .cmd_len_bits = 5 * 8, + .callback = mf_ultralight_listener_auth_handler, + }, + { + .cmd = MF_ULTRALIGHT_CMD_INCR_CNT, + .cmd_len_bits = 6 * 8, + .callback = mf_ultralight_listener_increase_counter_handler, + }, + { + .cmd = MF_ULTRALIGHT_CMD_SECTOR_SELECT, + .cmd_len_bits = 2 * 8, + .callback = mf_ultralight_sector_select_handler_p1, + }, + { + .cmd = MF_ULTRALIGHT_CMD_COMP_WRITE, + .cmd_len_bits = 2 * 8, + .callback = mf_ultralight_comp_write_handler_p1, + }, + { + .cmd = MF_ULTRALIGHT_CMD_VCSL, + .cmd_len_bits = 21 * 8, + .callback = mf_ultralight_listener_vcsl_handler, + }, +}; + +static void mf_ultralight_listener_prepare_emulation(MfUltralightListener* instance) { + MfUltralightData* data = instance->data; + instance->features = mf_ultralight_get_feature_support_set(data->type); + mf_ultralight_get_config_page(data, &instance->config); + mf_ultralight_mirror_prepare_emulation(instance); + mf_ultralight_static_lock_bytes_prepare(instance); + mf_ultralight_dynamic_lock_bytes_prepare(instance); +} + +static NfcCommand mf_ultralight_command_postprocess( + MfUltralightCommand mfu_command, + MfUltralightListener* instance) { + NfcCommand command = NfcCommandContinue; + + if(mfu_command == MfUltralightCommandProcessedACK) { + mf_ultralight_listener_send_short_resp(instance, MF_ULTRALIGHT_CMD_ACK); + command = NfcCommandContinue; + } else if(mfu_command == MfUltralightCommandProcessedSilent) { + command = NfcCommandReset; + } else if(mfu_command != MfUltralightCommandProcessed) { + instance->auth_state = MfUltralightListenerAuthStateIdle; + command = NfcCommandSleep; + + if(mfu_command == MfUltralightCommandNotProcessedNAK) { + mf_ultralight_listener_send_short_resp(instance, MF_ULTRALIGHT_CMD_NACK); + } else if(mfu_command == MfUltralightCommandNotProcessedAuthNAK) { + mf_ultralight_listener_send_short_resp(instance, MF_ULTRALIGHT_CMD_AUTH_NAK); + } + } + + return command; +} + +static NfcCommand mf_ultralight_reset_listener_state( + MfUltralightListener* instance, + Iso14443_3aListenerEventType event_type) { + mf_ultralight_composite_command_reset(instance); + mf_ultralight_single_counter_try_to_unlock(instance, event_type); + instance->sector = 0; + instance->auth_state = MfUltralightListenerAuthStateIdle; + return NfcCommandSleep; +} + +MfUltralightListener* mf_ultralight_listener_alloc( + Iso14443_3aListener* iso14443_3a_listener, + MfUltralightData* data) { + furi_assert(iso14443_3a_listener); + + MfUltralightListener* instance = malloc(sizeof(MfUltralightListener)); + instance->mirror.ascii_mirror_data = furi_string_alloc(); + instance->iso14443_3a_listener = iso14443_3a_listener; + instance->data = data; + mf_ultralight_static_lock_bytes_prepare(instance); + mf_ultralight_listener_prepare_emulation(instance); + mf_ultralight_composite_command_reset(instance); + instance->sector = 0; + instance->tx_buffer = bit_buffer_alloc(MF_ULTRALIGHT_LISTENER_MAX_TX_BUFF_SIZE); + + instance->mfu_event.data = &instance->mfu_event_data; + instance->generic_event.protocol = NfcProtocolMfUltralight; + instance->generic_event.instance = instance; + instance->generic_event.event_data = &instance->mfu_event; + + return instance; +} + +void mf_ultralight_listener_free(MfUltralightListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + furi_assert(instance->tx_buffer); + + bit_buffer_free(instance->tx_buffer); + furi_string_free(instance->mirror.ascii_mirror_data); + free(instance); +} + +const MfUltralightData* mf_ultralight_listener_get_data(MfUltralightListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +void mf_ultralight_listener_set_callback( + MfUltralightListener* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + + instance->callback = callback; + instance->context = context; +} + +NfcCommand mf_ultralight_listener_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + furi_assert(event.event_data); + + MfUltralightListener* instance = context; + Iso14443_3aListenerEvent* iso14443_3a_event = event.event_data; + BitBuffer* rx_buffer = iso14443_3a_event->data->buffer; + NfcCommand command = NfcCommandContinue; + + if(iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedStandardFrame) { + MfUltralightCommand mfu_command = MfUltralightCommandNotFound; + size_t size = bit_buffer_get_size(rx_buffer); + uint8_t cmd = bit_buffer_get_byte(rx_buffer, 0); + + if(mf_ultralight_composite_command_in_progress(instance)) { + mfu_command = mf_ultralight_composite_command_run(instance, rx_buffer); + } else { + for(size_t i = 0; i < COUNT_OF(mf_ultralight_command); i++) { + if(size != mf_ultralight_command[i].cmd_len_bits) continue; + if(cmd != mf_ultralight_command[i].cmd) continue; + mfu_command = mf_ultralight_command[i].callback(instance, rx_buffer); + + if(mfu_command != MfUltralightCommandNotFound) break; + } + } + command = mf_ultralight_command_postprocess(mfu_command, instance); + } else if( + iso14443_3a_event->type == Iso14443_3aListenerEventTypeReceivedData || + iso14443_3a_event->type == Iso14443_3aListenerEventTypeFieldOff || + iso14443_3a_event->type == Iso14443_3aListenerEventTypeHalted) { + command = mf_ultralight_reset_listener_state(instance, iso14443_3a_event->type); + } + + return command; +} + +const NfcListenerBase mf_ultralight_listener = { + .alloc = (NfcListenerAlloc)mf_ultralight_listener_alloc, + .free = (NfcListenerFree)mf_ultralight_listener_free, + .get_data = (NfcListenerGetData)mf_ultralight_listener_get_data, + .set_callback = (NfcListenerSetCallback)mf_ultralight_listener_set_callback, + .run = (NfcListenerRun)mf_ultralight_listener_run, +}; diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h new file mode 100644 index 00000000000..fa6a6bd7e70 --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h @@ -0,0 +1,28 @@ +#pragma once + +#include "mf_ultralight.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MfUltralightListener MfUltralightListener; + +typedef enum { + MfUltralightListenerEventTypeAuth, +} MfUltralightListenerEventType; + +typedef struct { + union { + MfUltralightAuthPassword password; + }; +} MfUltralightListenerEventData; + +typedef struct { + MfUltralightListenerEventType type; + MfUltralightListenerEventData* data; +} MfUltralightListenerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_defs.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_defs.h new file mode 100644 index 00000000000..aa3e11b8c4d --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcListenerBase mf_ultralight_listener; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.c new file mode 100644 index 00000000000..64647492de1 --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.c @@ -0,0 +1,579 @@ +#include "mf_ultralight_listener_i.h" + +#include + +#define MF_ULTRALIGHT_STATIC_BIT_LOCK_OTP_CC 0 +#define MF_ULTRALIGHT_STATIC_BIT_LOCK_BL_9_4 1 +#define MF_ULTRALIGHT_STATIC_BIT_LOCK_BL_15_10 2 + +#define MF_ULTRALIGHT_BIT_ACTIVE(lock_bits, bit) (((lock_bits) & (1U << (bit))) != 0) +#define MF_ULTRALIGHT_BITS_SET(lock_bits, mask) ((lock_bits) |= (mask)) +#define MF_ULTRALIGHT_BITS_CLR(lock_bits, mask) ((lock_bits) &= ~(mask)) + +#define MF_ULTRALIGHT_PAGE_LOCKED(lock_bits, page) MF_ULTRALIGHT_BIT_ACTIVE(lock_bits, page) + +#define MF_ULTRALIGHT_STATIC_BIT_OTP_CC_LOCKED(lock_bits) \ + MF_ULTRALIGHT_BIT_ACTIVE(lock_bits, MF_ULTRALIGHT_STATIC_BIT_LOCK_OTP_CC) + +#define MF_ULTRALIGHT_STATIC_BITS_9_4_LOCKED(lock_bits) \ + MF_ULTRALIGHT_BIT_ACTIVE(lock_bits, MF_ULTRALIGHT_STATIC_BIT_LOCK_BL_9_4) + +#define MF_ULTRALIGHT_STATIC_BITS_15_10_LOCKED(lock_bits) \ + MF_ULTRALIGHT_BIT_ACTIVE(lock_bits, MF_ULTRALIGHT_STATIC_BIT_LOCK_BL_15_10) + +#define MF_ULTRALIGHT_STATIC_LOCK_L_OTP_CC_MASK (1U << 3) +#define MF_ULTRALIGHT_STATIC_LOCK_L_9_4_MASK \ + ((1U << 9) | (1U << 8) | (1U << 7) | (1U << 6) | (1U << 5) | (1U << 4)) +#define MF_ULTRALIGHT_STATIC_LOCK_L_15_10_MASK \ + ((1U << 15) | (1U << 14) | (1U << 13) | (1U << 12) | (1U << 11) | (1U << 10)) + +#define MF_ULTRALIGHT_PAGE_IN_BOUNDS(page, start, end) (((page) >= (start)) && ((page) <= (end))) + +#define MF_ULTRALIGHT_I2C_PAGE_ON_SESSION_REG(page) \ + MF_ULTRALIGHT_PAGE_IN_BOUNDS(page, 0x00EC, 0x00ED) + +#define MF_ULTRALIGHT_I2C_PAGE_ON_MIRRORED_SESSION_REG(page) \ + MF_ULTRALIGHT_PAGE_IN_BOUNDS(page, 0x00F8, 0x00F9) + +#define MF_ULTRALIGHT_AUTH_RESET_ATTEMPTS(instance) (instance->data->auth_attempts = 0) +#define MF_ULTRALIGHT_AUTH_INCREASE_ATTEMPTS(instance) (instance->data->auth_attempts++) + +static MfUltralightMirrorConf mf_ultralight_mirror_check_mode( + const MfUltralightConfigPages* const config, + const MfUltralightListenerAuthState auth_state) { + MfUltralightMirrorConf mirror_mode = config->mirror.mirror_conf; + + if(mirror_mode == MfUltralightMirrorNone || mirror_mode == MfUltralightMirrorUid) + return mirror_mode; + + if(!config->access.nfc_cnt_en || + (config->access.nfc_cnt_pwd_prot && auth_state != MfUltralightListenerAuthStateSuccess)) { + mirror_mode = mirror_mode == MfUltralightMirrorCounter ? MfUltralightMirrorNone : + MfUltralightMirrorUid; + } + return mirror_mode; +} + +static bool mf_ultralight_mirror_check_boundaries(MfUltralightListener* instance) { + const MfUltralightConfigPages* const conf = instance->config; + + uint8_t last_user_page = mf_ultralight_get_config_page_num(instance->data->type) - 2; + + uint8_t max_page_offset = 0; + uint8_t max_byte_offset = 2; + + MfUltralightMirrorConf mode = mf_ultralight_mirror_check_mode(conf, instance->auth_state); + + bool result = false; + bool again = false; + do { + if(mode == MfUltralightMirrorNone) { + break; + } else if(mode == MfUltralightMirrorUid) { + max_page_offset = 3; + } else if(mode == MfUltralightMirrorCounter) { + max_page_offset = 1; + } else if(mode == MfUltralightMirrorUidCounter) { + max_page_offset = 5; + max_byte_offset = 3; + } + + instance->mirror.actual_mode = mode; + + if(conf->mirror_page <= 3) break; + if(conf->mirror_page < last_user_page - max_page_offset) { + result = true; + break; + } + if(conf->mirror_page == last_user_page - max_page_offset) { + result = (conf->mirror.mirror_byte <= max_byte_offset); + break; + } + + if(conf->mirror_page > last_user_page - max_page_offset && + mode == MfUltralightMirrorUidCounter) { + mode = MfUltralightMirrorUid; + again = true; + } + } while(again); + + return result; +} + +static bool mf_ultralight_mirror_enabled(MfUltralightListener* instance) { + bool mirror_enabled = false; + if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportAsciiMirror) && + (instance->config != NULL) && mf_ultralight_mirror_check_boundaries(instance)) { + mirror_enabled = true; + } + instance->mirror.enabled = mirror_enabled; + return instance->mirror.enabled; +} + +static uint8_t mf_ultralight_get_mirror_data_size(MfUltralightMirrorConf mode) { + switch(mode) { + case MfUltralightMirrorUid: + return 14; + case MfUltralightMirrorCounter: + return 6; + case MfUltralightMirrorUidCounter: + return 21; + default: + return 0; + } +} + +static uint8_t mf_ultralight_get_mirror_last_page(MfUltralightListener* instance) { + uint8_t strSize = mf_ultralight_get_mirror_data_size(instance->mirror.actual_mode); + return (instance->config->mirror_page + 1U + strSize / 4); +} + +static uint8_t mf_ultralight_get_ascii_offset(uint8_t start_page, MfUltralightListener* instance) { + uint8_t start_offset = 0; + if(instance->config->mirror.mirror_conf == MfUltralightMirrorCounter) start_offset = 15; + + uint8_t ascii_offset = start_offset; + + if(start_page > instance->config->mirror_page) + ascii_offset = (start_page - instance->config->mirror_page) * 4 - + instance->config->mirror.mirror_byte + start_offset; + + return ascii_offset; +} + +static uint8_t mf_ultralight_get_ascii_end(MfUltralightMirrorConf mode) { + return (mode == MfUltralightMirrorUid) ? 14 : 21; +} + +static uint8_t mf_ultralight_get_byte_offset( + uint8_t current_page, + const MfUltralightConfigPages* const config) { + return (current_page > config->mirror_page) ? 0 : config->mirror.mirror_byte; +} + +static void mf_ultralight_format_mirror_data( + FuriString* str, + const uint8_t* const data, + const uint8_t data_len) { + for(uint8_t i = 0; i < data_len; i++) furi_string_cat_printf(str, "%02X", data[i]); +} + +void mf_ultralight_mirror_read_prepare(uint8_t start_page, MfUltralightListener* instance) { + if(mf_ultralight_mirror_enabled(instance)) { + instance->mirror.ascii_offset = mf_ultralight_get_ascii_offset(start_page, instance); + instance->mirror.ascii_end = mf_ultralight_get_ascii_end(instance->mirror.actual_mode); + + instance->mirror.mirror_last_page = mf_ultralight_get_mirror_last_page(instance); + } +} + +void mf_ultralight_mirror_read_handler( + uint8_t mirror_page_num, + uint8_t* dest, + MfUltralightListener* instance) { + if(instance->mirror.enabled && mirror_page_num >= instance->config->mirror_page && + mirror_page_num <= instance->mirror.mirror_last_page) { + uint8_t byte_offset = mf_ultralight_get_byte_offset(mirror_page_num, instance->config); + + uint8_t ascii_offset = instance->mirror.ascii_offset; + uint8_t ascii_end = instance->mirror.ascii_end; + uint8_t* source = (uint8_t*)furi_string_get_cstr(instance->mirror.ascii_mirror_data); + for(uint8_t j = byte_offset; (j < 4) && (ascii_offset < ascii_end); j++) { + dest[j] = source[ascii_offset]; + ascii_offset++; + } + instance->mirror.ascii_offset = ascii_offset; + } +} + +void mf_ultralight_mirror_prepare_emulation(MfUltralightListener* instance) { + mf_ultralight_format_mirror_data( + instance->mirror.ascii_mirror_data, + instance->data->iso14443_3a_data->uid, + instance->data->iso14443_3a_data->uid_len); + + furi_string_push_back(instance->mirror.ascii_mirror_data, 'x'); + + mf_ultraligt_mirror_format_counter(instance); +} + +void mf_ultraligt_mirror_format_counter(MfUltralightListener* instance) { + furi_string_left( + instance->mirror.ascii_mirror_data, instance->data->iso14443_3a_data->uid_len * 2 + 1); + + uint8_t* c = instance->data->counter[2].data; + furi_string_cat_printf(instance->mirror.ascii_mirror_data, "%02X%02X%02X", c[2], c[1], c[0]); +} + +bool mf_ultralight_composite_command_in_progress(MfUltralightListener* instance) { + return (instance->composite_cmd.callback != NULL); +} + +MfUltralightCommand + mf_ultralight_composite_command_run(MfUltralightListener* instance, BitBuffer* buffer) { + MfUltralightCommand command = (instance->composite_cmd.callback)(instance, buffer); + mf_ultralight_composite_command_reset(instance); + return command; +} + +void mf_ultralight_composite_command_reset(MfUltralightListener* instance) { + instance->composite_cmd.callback = NULL; + instance->composite_cmd.data = 0; +} + +void mf_ultralight_composite_command_set_next( + MfUltralightListener* instance, + const MfUltralightListenerCommandCallback handler) { + instance->composite_cmd.callback = handler; +} + +void mf_ultralight_single_counter_try_increase(MfUltralightListener* instance) { + if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportSingleCounter) && + instance->config->access.nfc_cnt_en && !instance->single_counter_increased) { + if(instance->data->counter[2].counter < MF_ULTRALIGHT_MAX_CNTR_VAL) { + instance->data->counter[2].counter++; + mf_ultraligt_mirror_format_counter(instance); + } + instance->single_counter_increased = true; + } +} + +void mf_ultralight_single_counter_try_to_unlock( + MfUltralightListener* instance, + Iso14443_3aListenerEventType type) { + if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportSingleCounter) && + type == Iso14443_3aListenerEventTypeFieldOff) { + instance->single_counter_increased = false; + } +} + +static bool mf_ultralight_i2c_page_validator_for_sector0( + uint16_t start_page, + uint16_t end_page, + MfUltralightType type) { + UNUSED(type); + bool valid = false; + if(type == MfUltralightTypeNTAGI2CPlus1K || type == MfUltralightTypeNTAGI2CPlus2K) { + if(start_page <= 0xE9 && end_page <= 0xE9) { + valid = true; + } else if( + MF_ULTRALIGHT_I2C_PAGE_ON_SESSION_REG(start_page) && + MF_ULTRALIGHT_I2C_PAGE_ON_SESSION_REG(end_page)) { + valid = true; + } + } else if(type == MfUltralightTypeNTAGI2C1K) { + if((start_page <= 0xE2) || MF_ULTRALIGHT_PAGE_IN_BOUNDS(start_page, 0x00E8, 0x00E9)) { + valid = true; + } + } else if(type == MfUltralightTypeNTAGI2C2K) { + valid = (start_page <= 0xFF && end_page <= 0xFF); + } + + return valid; +} + +static bool mf_ultralight_i2c_page_validator_for_sector1( + uint16_t start_page, + uint16_t end_page, + MfUltralightType type) { + bool valid = false; + if(type == MfUltralightTypeNTAGI2CPlus2K) { + valid = (start_page <= 0xFF && end_page <= 0xFF); + } else if(type == MfUltralightTypeNTAGI2C2K) { + valid = (MF_ULTRALIGHT_PAGE_IN_BOUNDS(start_page, 0x00E8, 0x00E9) || (start_page <= 0xE0)); + } else if(type == MfUltralightTypeNTAGI2C1K || type == MfUltralightTypeNTAGI2CPlus1K) { + valid = false; + } + + return valid; +} + +static bool mf_ultralight_i2c_page_validator_for_sector2( + uint16_t start_page, + uint16_t end_page, + MfUltralightType type) { + UNUSED(start_page); + UNUSED(end_page); + UNUSED(type); + return false; +} + +static bool mf_ultralight_i2c_page_validator_for_sector3( + uint16_t start_page, + uint16_t end_page, + MfUltralightType type) { + UNUSED(type); + UNUSED(end_page); + return MF_ULTRALIGHT_I2C_PAGE_ON_MIRRORED_SESSION_REG(start_page); +} + +typedef bool ( + *MfUltralightI2CValidator)(uint16_t start_page, uint16_t end_page, MfUltralightType type); + +typedef uint16_t (*MfUltralightI2CPageProvider)(uint16_t page, MfUltralightType type); + +const MfUltralightI2CValidator validation_methods[] = { + mf_ultralight_i2c_page_validator_for_sector0, + mf_ultralight_i2c_page_validator_for_sector1, + mf_ultralight_i2c_page_validator_for_sector2, + mf_ultralight_i2c_page_validator_for_sector3, +}; + +bool mf_ultralight_i2c_validate_pages( + uint16_t start_page, + uint16_t end_page, + MfUltralightListener* instance) { + bool valid = false; + if(instance->sector < COUNT_OF(validation_methods)) { + MfUltralightI2CValidator validate = validation_methods[instance->sector]; + valid = validate(start_page, end_page, instance->data->type); + } + return valid; +} + +bool mf_ultralight_is_i2c_tag(MfUltralightType type) { + return type == MfUltralightTypeNTAGI2C1K || type == MfUltralightTypeNTAGI2C2K || + type == MfUltralightTypeNTAGI2CPlus1K || type == MfUltralightTypeNTAGI2CPlus2K; +} + +static uint16_t mf_ultralight_i2c_page_provider_for_sector0(uint16_t page, MfUltralightType type) { + uint8_t new_page = page; + if(type == MfUltralightTypeNTAGI2CPlus1K || type == MfUltralightTypeNTAGI2CPlus2K) { + if(page == 0x00EC) { + new_page = 234; + } else if(page == 0x00ED) { + new_page = 235; + } + } else if(type == MfUltralightTypeNTAGI2C1K) { + if(page == 0x00E8) { + new_page = 232; + } else if(page == 0x00E9) { + new_page = 233; + } + } else if(type == MfUltralightTypeNTAGI2C2K) { + new_page = page; + } + return new_page; +} + +static uint16_t mf_ultralight_i2c_page_provider_for_sector1(uint16_t page, MfUltralightType type) { + UNUSED(type); + uint16_t new_page = page; + if(type == MfUltralightTypeNTAGI2CPlus2K) new_page = page + 236; + if(type == MfUltralightTypeNTAGI2C2K) new_page = page + 256; + return new_page; +} + +static uint16_t mf_ultralight_i2c_page_provider_for_sector2(uint16_t page, MfUltralightType type) { + UNUSED(type); + return page; +} + +static uint16_t mf_ultralight_i2c_page_provider_for_sector3(uint16_t page, MfUltralightType type) { + uint16_t new_page = page; + if(type == MfUltralightTypeNTAGI2CPlus1K || type == MfUltralightTypeNTAGI2CPlus2K) { + if(page == 0x00F8) + new_page = 234; + else if(page == 0x00F9) + new_page = 235; + } else if(type == MfUltralightTypeNTAGI2C1K || type == MfUltralightTypeNTAGI2C2K) { + if(page == 0x00F8) + new_page = (type == MfUltralightTypeNTAGI2C1K) ? 227 : 481; + else if(page == 0x00F9) + new_page = (type == MfUltralightTypeNTAGI2C1K) ? 228 : 482; + } + return new_page; +} + +const MfUltralightI2CPageProvider provider_methods[] = { + mf_ultralight_i2c_page_provider_for_sector0, + mf_ultralight_i2c_page_provider_for_sector1, + mf_ultralight_i2c_page_provider_for_sector2, + mf_ultralight_i2c_page_provider_for_sector3, +}; + +uint16_t + mf_ultralight_i2c_provide_page_by_requested(uint16_t page, MfUltralightListener* instance) { + uint16_t result = page; + if(instance->sector < COUNT_OF(provider_methods)) { + MfUltralightI2CPageProvider provider = provider_methods[instance->sector]; + result = provider(page, instance->data->type); + } + return result; +} + +void mf_ultralight_static_lock_bytes_prepare(MfUltralightListener* instance) { + instance->static_lock = (uint16_t*)&instance->data->page[2].data[2]; +} + +void mf_ultralight_static_lock_bytes_write( + MfUltralightStaticLockData* const lock_bits, + uint16_t new_bits) { + uint16_t current_locks = *lock_bits; + + if(MF_ULTRALIGHT_STATIC_BIT_OTP_CC_LOCKED(current_locks)) + MF_ULTRALIGHT_BITS_CLR(new_bits, MF_ULTRALIGHT_STATIC_LOCK_L_OTP_CC_MASK); + + if(MF_ULTRALIGHT_STATIC_BITS_9_4_LOCKED(current_locks)) + MF_ULTRALIGHT_BITS_CLR(new_bits, MF_ULTRALIGHT_STATIC_LOCK_L_9_4_MASK); + + if(MF_ULTRALIGHT_STATIC_BITS_15_10_LOCKED(current_locks)) + MF_ULTRALIGHT_BITS_CLR(new_bits, MF_ULTRALIGHT_STATIC_LOCK_L_15_10_MASK); + + MF_ULTRALIGHT_BITS_SET(current_locks, new_bits); + *lock_bits = current_locks; +} + +bool mf_ultralight_static_lock_check_page( + const MfUltralightStaticLockData* const lock_bits, + uint16_t page) { + bool locked = false; + if(MF_ULTRALIGHT_PAGE_IN_BOUNDS(page, 0x0003, 0x000F)) { + uint16_t current_locks = *lock_bits; + locked = MF_ULTRALIGHT_PAGE_LOCKED(current_locks, page); + } + return locked; +} + +void mf_ultralight_capability_container_write( + MfUltralightPage* const current_page, + const uint8_t* const new_data) { + for(uint8_t i = 0; i < MF_ULTRALIGHT_PAGE_SIZE; i++) { + current_page->data[i] |= new_data[i]; + } +} + +static uint16_t mf_ultralight_dynamic_lock_page_num(const MfUltralightData* data) { + uint16_t lock_page; + if(data->type == MfUltralightTypeNTAGI2C1K) + lock_page = 226; + else if(data->type == MfUltralightTypeNTAGI2C2K) + lock_page = 480; + else + lock_page = mf_ultralight_get_config_page_num(data->type) - 1; + return lock_page; +} + +void mf_ultralight_dynamic_lock_bytes_prepare(MfUltralightListener* instance) { + if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportDynamicLock)) { + uint16_t lock_page = mf_ultralight_dynamic_lock_page_num(instance->data); + instance->dynamic_lock = (uint32_t*)instance->data->page[lock_page].data; + } else { + instance->dynamic_lock = NULL; + } +} + +bool mf_ultralight_is_page_dynamic_lock(const MfUltralightListener* instance, uint16_t page) { + bool is_lock = false; + if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportDynamicLock)) { + uint16_t linear_page = page + instance->sector * 256; + + uint16_t lock_page = mf_ultralight_dynamic_lock_page_num(instance->data); + is_lock = linear_page == lock_page; + } + return is_lock; +} + +void mf_ultralight_dynamic_lock_bytes_write( + MfUltralightDynamicLockData* const lock_bits, + uint32_t new_bits) { + furi_assert(lock_bits != NULL); + new_bits &= 0x00FFFFFF; + uint32_t current_lock = *lock_bits; + for(uint8_t i = 0; i < 8; i++) { + uint8_t bl_bit = i + 16; + + if(MF_ULTRALIGHT_BIT_ACTIVE(current_lock, bl_bit)) { + uint8_t lock_bit = i * 2; + uint32_t mask = (1U << lock_bit) | (1U << (lock_bit + 1)); + MF_ULTRALIGHT_BITS_CLR(new_bits, mask); + } + } + MF_ULTRALIGHT_BITS_SET(current_lock, new_bits); + *lock_bits = current_lock; +} + +static uint8_t mf_ultralight_dynamic_lock_granularity(MfUltralightType type) { + switch(type) { + case MfUltralightTypeUL21: + case MfUltralightTypeNTAG213: + return 2; + case MfUltralightTypeNTAG215: + case MfUltralightTypeNTAG216: + case MfUltralightTypeNTAGI2C1K: + case MfUltralightTypeNTAGI2CPlus1K: + return 16; + case MfUltralightTypeNTAGI2C2K: + case MfUltralightTypeNTAGI2CPlus2K: + return 32; + default: + return 1; + } +} + +static uint16_t mf_ultralight_get_upper_page_bound(MfUltralightType type) { + uint16_t upper_page_bound; + + if(type == MfUltralightTypeNTAGI2CPlus2K) + upper_page_bound = 511; + else if(type == MfUltralightTypeNTAGI2C2K) + upper_page_bound = 479; + else if(type == MfUltralightTypeNTAGI2C1K) + upper_page_bound = 225; + else { + upper_page_bound = mf_ultralight_get_config_page_num(type) - 2; + } + + return upper_page_bound; +} + +bool mf_ultralight_dynamic_lock_check_page(const MfUltralightListener* instance, uint16_t page) { + UNUSED(page); + bool locked = false; + uint16_t upper_page_bound = mf_ultralight_get_upper_page_bound(instance->data->type); + uint16_t linear_page = page + instance->sector * 256; + + if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportDynamicLock) && + MF_ULTRALIGHT_PAGE_IN_BOUNDS(linear_page, 0x0010, upper_page_bound)) { + uint8_t granularity = mf_ultralight_dynamic_lock_granularity(instance->data->type); + uint8_t bit = (linear_page - 16) / granularity; + uint16_t current_locks = *instance->dynamic_lock; + locked = MF_ULTRALIGHT_PAGE_LOCKED(current_locks, bit); + } + return locked; +} + +static bool mf_ultralight_auth_check_attempts(const MfUltralightListener* instance) { + uint8_t authlim = ((instance->data->type == MfUltralightTypeNTAGI2CPlus1K) || + (instance->data->type == MfUltralightTypeNTAGI2CPlus2K)) ? + (1U << instance->config->access.authlim) : + instance->config->access.authlim; + + return (instance->data->auth_attempts >= authlim); +} + +bool mf_ultralight_auth_limit_check_and_update(MfUltralightListener* instance, bool auth_success) { + bool card_locked = false; + + do { + if(instance->config->access.authlim == 0) break; + card_locked = mf_ultralight_auth_check_attempts(instance); + if(card_locked) break; + + if(auth_success) { + MF_ULTRALIGHT_AUTH_RESET_ATTEMPTS(instance); + } else { + MF_ULTRALIGHT_AUTH_INCREASE_ATTEMPTS(instance); + } + + card_locked = mf_ultralight_auth_check_attempts(instance); + } while(false); + + return card_locked; +} + +bool mf_ultralight_auth_check_password( + const MfUltralightAuthPassword* config_pass, + const MfUltralightAuthPassword* auth_pass) { + return memcmp(config_pass->data, auth_pass->data, sizeof(MfUltralightAuthPassword)) == 0; +} \ No newline at end of file diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.h new file mode 100644 index 00000000000..ba448d0879c --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.h @@ -0,0 +1,123 @@ +#pragma once + +#include "mf_ultralight_listener.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + MfUltralightListenerAuthStateIdle, + MfUltralightListenerAuthStateSuccess, +} MfUltralightListenerAuthState; + +typedef enum { + MfUltralightCommandNotFound, + MfUltralightCommandProcessed, + MfUltralightCommandProcessedACK, + MfUltralightCommandProcessedSilent, + MfUltralightCommandNotProcessedNAK, + MfUltralightCommandNotProcessedSilent, + MfUltralightCommandNotProcessedAuthNAK, +} MfUltralightCommand; + +typedef MfUltralightCommand ( + *MfUltralightListenerCommandCallback)(MfUltralightListener* instance, BitBuffer* buf); + +typedef uint8_t MfUltralightListenerCompositeCommandData; + +typedef struct { + MfUltralightListenerCompositeCommandData data; + MfUltralightListenerCommandCallback callback; +} MfUltralightListenerCompositeCommandContext; + +typedef struct { + uint8_t enabled; + uint8_t ascii_offset; + uint8_t ascii_end; + uint8_t mirror_last_page; + MfUltralightMirrorConf actual_mode; + FuriString* ascii_mirror_data; +} MfUltralightMirrorMode; + +typedef uint16_t MfUltralightStaticLockData; +typedef uint32_t MfUltralightDynamicLockData; + +struct MfUltralightListener { + Iso14443_3aListener* iso14443_3a_listener; + MfUltralightListenerAuthState auth_state; + MfUltralightData* data; + BitBuffer* tx_buffer; + MfUltralightFeatureSupport features; + MfUltralightConfigPages* config; + MfUltralightStaticLockData* static_lock; + MfUltralightDynamicLockData* dynamic_lock; + + NfcGenericEvent generic_event; + MfUltralightListenerEvent mfu_event; + MfUltralightListenerEventData mfu_event_data; + NfcGenericCallback callback; + uint8_t sector; + bool single_counter_increased; + MfUltralightMirrorMode mirror; + MfUltralightListenerCompositeCommandContext composite_cmd; + void* context; +}; + +void mf_ultralight_single_counter_try_increase(MfUltralightListener* instance); +void mf_ultralight_single_counter_try_to_unlock( + MfUltralightListener* instance, + Iso14443_3aListenerEventType type); + +void mf_ultralight_mirror_prepare_emulation(MfUltralightListener* instance); +void mf_ultraligt_mirror_format_counter(MfUltralightListener* instance); +void mf_ultralight_mirror_read_prepare(uint8_t start_page, MfUltralightListener* instance); +void mf_ultralight_mirror_read_handler( + uint8_t mirror_page_num, + uint8_t* dest, + MfUltralightListener* instance); + +void mf_ultralight_composite_command_set_next( + MfUltralightListener* instance, + const MfUltralightListenerCommandCallback handler); +void mf_ultralight_composite_command_reset(MfUltralightListener* instance); +bool mf_ultralight_composite_command_in_progress(MfUltralightListener* instance); +MfUltralightCommand + mf_ultralight_composite_command_run(MfUltralightListener* instance, BitBuffer* buffer); + +bool mf_ultralight_is_i2c_tag(MfUltralightType type); +bool mf_ultralight_i2c_validate_pages( + uint16_t start_page, + uint16_t end_page, + MfUltralightListener* instance); + +uint16_t + mf_ultralight_i2c_provide_page_by_requested(uint16_t page, MfUltralightListener* instance); + +void mf_ultralight_static_lock_bytes_prepare(MfUltralightListener* instance); +void mf_ultralight_static_lock_bytes_write( + MfUltralightStaticLockData* const lock_bits, + uint16_t new_bits); +bool mf_ultralight_static_lock_check_page( + const MfUltralightStaticLockData* const lock_bits, + uint16_t page); + +void mf_ultralight_capability_container_write( + MfUltralightPage* const current_page, + const uint8_t* const new_data); + +void mf_ultralight_dynamic_lock_bytes_prepare(MfUltralightListener* instance); +bool mf_ultralight_is_page_dynamic_lock(const MfUltralightListener* instance, uint16_t start_page); +void mf_ultralight_dynamic_lock_bytes_write( + MfUltralightDynamicLockData* const lock_bits, + uint32_t new_bits); +bool mf_ultralight_dynamic_lock_check_page(const MfUltralightListener* instance, uint16_t page); +bool mf_ultralight_auth_limit_check_and_update(MfUltralightListener* instance, bool auth_success); +bool mf_ultralight_auth_check_password( + const MfUltralightAuthPassword* config_pass, + const MfUltralightAuthPassword* auth_pass); +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c new file mode 100644 index 00000000000..619cd8c5fbc --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c @@ -0,0 +1,723 @@ +#include "mf_ultralight_poller_i.h" + +#include + +#include + +#define TAG "MfUltralightPoller" + +typedef NfcCommand (*MfUltralightPollerReadHandler)(MfUltralightPoller* instance); + +static bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_1k( + uint16_t lin_addr, + uint8_t* sector, + uint8_t* tag, + uint8_t* pages_left) { + bool tag_calculated = false; + // 0 - 226: sector 0 + // 227 - 228: config registers + // 229 - 230: session registers + + if(lin_addr > 230) { + *pages_left = 0; + } else if(lin_addr >= 229) { + *sector = 3; + *pages_left = 2 - (lin_addr - 229); + *tag = lin_addr - 229 + 248; + tag_calculated = true; + } else if(lin_addr >= 227) { + *sector = 0; + *pages_left = 2 - (lin_addr - 227); + *tag = lin_addr - 227 + 232; + tag_calculated = true; + } else { + *sector = 0; + *pages_left = 227 - lin_addr; + *tag = lin_addr; + tag_calculated = true; + } + + return tag_calculated; +} + +static bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_2k( + uint16_t lin_addr, + uint8_t* sector, + uint8_t* tag, + uint8_t* pages_left) { + bool tag_calculated = false; + // 0 - 255: sector 0 + // 256 - 480: sector 1 + // 481 - 482: config registers + // 483 - 484: session registers + + if(lin_addr > 484) { + *pages_left = 0; + } else if(lin_addr >= 483) { + *sector = 3; + *pages_left = 2 - (lin_addr - 483); + *tag = lin_addr - 483 + 248; + tag_calculated = true; + } else if(lin_addr >= 481) { + *sector = 1; + *pages_left = 2 - (lin_addr - 481); + *tag = lin_addr - 481 + 232; + tag_calculated = true; + } else if(lin_addr >= 256) { + *sector = 1; + *pages_left = 225 - (lin_addr - 256); + *tag = lin_addr - 256; + tag_calculated = true; + } else { + *sector = 0; + *pages_left = 256 - lin_addr; + *tag = lin_addr; + tag_calculated = true; + } + + return tag_calculated; +} + +static bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_1k( + uint16_t lin_addr, + uint8_t* sector, + uint8_t* tag, + uint8_t* pages_left) { + bool tag_calculated = false; + // 0 - 233: sector 0 + registers + // 234 - 235: session registers + + if(lin_addr > 235) { + *pages_left = 0; + } else if(lin_addr >= 234) { + *sector = 0; + *pages_left = 2 - (lin_addr - 234); + *tag = lin_addr - 234 + 236; + tag_calculated = true; + } else { + *sector = 0; + *pages_left = 234 - lin_addr; + *tag = lin_addr; + tag_calculated = true; + } + + return tag_calculated; +} + +static bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_2k( + uint16_t lin_addr, + uint8_t* sector, + uint8_t* tag, + uint8_t* pages_left) { + bool tag_calculated = false; + // 0 - 233: sector 0 + registers + // 234 - 235: session registers + // 236 - 491: sector 1 + + if(lin_addr > 491) { + *pages_left = 0; + } else if(lin_addr >= 236) { + *sector = 1; + *pages_left = 256 - (lin_addr - 236); + *tag = lin_addr - 236; + tag_calculated = true; + } else if(lin_addr >= 234) { + *sector = 0; + *pages_left = 2 - (lin_addr - 234); + *tag = lin_addr - 234 + 236; + tag_calculated = true; + } else { + *sector = 0; + *pages_left = 234 - lin_addr; + *tag = lin_addr; + tag_calculated = true; + } + + return tag_calculated; +} + +bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag( + MfUltralightPoller* instance, + uint16_t lin_addr, + uint8_t* sector, + uint8_t* tag, + uint8_t* pages_left) { + furi_assert(instance); + furi_assert(sector); + furi_assert(tag); + furi_assert(pages_left); + + bool tag_calculated = false; + + if(instance->data->type == MfUltralightTypeNTAGI2C1K) { + tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_1k( + lin_addr, sector, tag, pages_left); + } else if(instance->data->type == MfUltralightTypeNTAGI2C2K) { + tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_2k( + lin_addr, sector, tag, pages_left); + } else if(instance->data->type == MfUltralightTypeNTAGI2CPlus1K) { + tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_1k( + lin_addr, sector, tag, pages_left); + } else if(instance->data->type == MfUltralightTypeNTAGI2CPlus2K) { + tag_calculated = mf_ultralight_poller_ntag_i2c_addr_lin_to_tag_ntag_i2c_plus_2k( + lin_addr, sector, tag, pages_left); + } + + return tag_calculated; +} + +MfUltralightPoller* mf_ultralight_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller) { + furi_assert(iso14443_3a_poller); + + MfUltralightPoller* instance = malloc(sizeof(MfUltralightPoller)); + instance->iso14443_3a_poller = iso14443_3a_poller; + instance->tx_buffer = bit_buffer_alloc(MF_ULTRALIGHT_MAX_BUFF_SIZE); + instance->rx_buffer = bit_buffer_alloc(MF_ULTRALIGHT_MAX_BUFF_SIZE); + instance->data = mf_ultralight_alloc(); + + instance->mfu_event.data = &instance->mfu_event_data; + + instance->general_event.protocol = NfcProtocolMfUltralight; + instance->general_event.event_data = &instance->mfu_event; + instance->general_event.instance = instance; + + return instance; +} + +void mf_ultralight_poller_free(MfUltralightPoller* instance) { + furi_assert(instance); + furi_assert(instance->data); + furi_assert(instance->tx_buffer); + furi_assert(instance->rx_buffer); + + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + mf_ultralight_free(instance->data); + free(instance); +} + +static void mf_ultralight_poller_set_callback( + MfUltralightPoller* instance, + NfcGenericCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +const MfUltralightData* mf_ultralight_poller_get_data(MfUltralightPoller* instance) { + furi_assert(instance); + + return instance->data; +} + +static NfcCommand mf_ultralight_poller_handler_idle(MfUltralightPoller* instance) { + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + iso14443_3a_copy( + instance->data->iso14443_3a_data, + iso14443_3a_poller_get_data(instance->iso14443_3a_poller)); + instance->counters_read = 0; + instance->counters_total = 3; + instance->tearing_flag_read = 0; + instance->tearing_flag_total = 3; + instance->pages_read = 0; + instance->state = MfUltralightPollerStateRequestMode; + instance->current_page = 0; + return NfcCommandContinue; +} + +static NfcCommand mf_ultralight_poller_handler_request_mode(MfUltralightPoller* instance) { + NfcCommand command = NfcCommandContinue; + + instance->mfu_event.type = MfUltralightPollerEventTypeRequestMode; + instance->mfu_event.data->poller_mode = MfUltralightPollerModeRead; + + command = instance->callback(instance->general_event, instance->context); + instance->mode = instance->mfu_event.data->poller_mode; + + instance->state = MfUltralightPollerStateReadVersion; + return command; +} + +static NfcCommand mf_ultralight_poller_handler_read_version(MfUltralightPoller* instance) { + instance->error = mf_ultralight_poller_read_version(instance, &instance->data->version); + if(instance->error == MfUltralightErrorNone) { + FURI_LOG_D(TAG, "Read version success"); + instance->data->type = mf_ultralight_get_type_by_version(&instance->data->version); + instance->state = MfUltralightPollerStateGetFeatureSet; + } else { + FURI_LOG_D(TAG, "Didn't response. Check Ultralight C"); + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + instance->state = MfUltralightPollerStateDetectMfulC; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_ultralight_poller_handler_check_ultralight_c(MfUltralightPoller* instance) { + instance->error = mf_ultralight_poller_authenticate(instance); + if(instance->error == MfUltralightErrorNone) { + FURI_LOG_D(TAG, "Ultralight C detected"); + instance->data->type = MfUltralightTypeMfulC; + instance->state = MfUltralightPollerStateGetFeatureSet; + } else { + FURI_LOG_D(TAG, "Didn't response. Check NTAG 203"); + instance->state = MfUltralightPollerStateDetectNtag203; + } + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + return NfcCommandContinue; +} + +static NfcCommand mf_ultralight_poller_handler_check_ntag_203(MfUltralightPoller* instance) { + MfUltralightPollerState next_state = MfUltralightPollerStateGetFeatureSet; + MfUltralightPageReadCommandData data = {}; + instance->error = mf_ultralight_poller_read_page(instance, 41, &data); + if(instance->error == MfUltralightErrorNone) { + FURI_LOG_D(TAG, "NTAG203 detected"); + instance->data->type = MfUltralightTypeNTAG203; + } else { + FURI_LOG_D(TAG, "Original Ultralight detected"); + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + instance->data->type = MfUltralightTypeUnknown; + if(instance->mode == MfUltralightPollerModeWrite) { + instance->mfu_event.type = MfUltralightPollerEventTypeCardMismatch; + instance->callback(instance->general_event, instance->context); + next_state = MfUltralightPollerStateWriteFail; + } + } + instance->state = next_state; + + return NfcCommandContinue; +} + +static NfcCommand mf_ultralight_poller_handler_get_feature_set(MfUltralightPoller* instance) { + instance->feature_set = mf_ultralight_get_feature_support_set(instance->data->type); + instance->pages_total = mf_ultralight_get_pages_total(instance->data->type); + instance->data->pages_total = instance->pages_total; + FURI_LOG_D( + TAG, + "%s detected. Total pages: %d", + mf_ultralight_get_device_name(instance->data, NfcDeviceNameTypeFull), + instance->pages_total); + + instance->state = MfUltralightPollerStateReadSignature; + return NfcCommandContinue; +} + +static NfcCommand mf_ultralight_poller_handler_read_signature(MfUltralightPoller* instance) { + MfUltralightPollerState next_state = MfUltralightPollerStateAuth; + if(mf_ultralight_support_feature( + instance->feature_set, MfUltralightFeatureSupportReadSignature)) { + FURI_LOG_D(TAG, "Reading signature"); + instance->error = + mf_ultralight_poller_read_signature(instance, &instance->data->signature); + if(instance->error != MfUltralightErrorNone) { + FURI_LOG_D(TAG, "Read signature failed"); + next_state = MfUltralightPollerStateReadFailed; + } + } else { + FURI_LOG_D(TAG, "Skip reading signature"); + } + instance->state = next_state; + + return NfcCommandContinue; +} + +static NfcCommand mf_ultralight_poller_handler_read_counters(MfUltralightPoller* instance) { + do { + if(!mf_ultralight_support_feature( + instance->feature_set, MfUltralightFeatureSupportReadCounter) || + !mf_ultralight_is_counter_configured(instance->data)) { + FURI_LOG_D(TAG, "Skip reading counters"); + instance->state = MfUltralightPollerStateReadTearingFlags; + break; + } + + MfUltralightConfigPages* config = NULL; + mf_ultralight_get_config_page(instance->data, &config); + + if(config->access.nfc_cnt_pwd_prot && !instance->auth_context.auth_success) { + FURI_LOG_D(TAG, "Counter reading is protected with password"); + instance->state = MfUltralightPollerStateReadTearingFlags; + break; + } + + if(instance->counters_read == instance->counters_total) { + instance->state = MfUltralightPollerStateReadTearingFlags; + break; + } + + if(mf_ultralight_support_feature( + instance->feature_set, MfUltralightFeatureSupportSingleCounter)) { + instance->counters_read = 2; + } + + FURI_LOG_D(TAG, "Reading counter %d", instance->counters_read); + instance->error = mf_ultralight_poller_read_counter( + instance, instance->counters_read, &instance->data->counter[instance->counters_read]); + if(instance->error != MfUltralightErrorNone) { + FURI_LOG_D(TAG, "Failed to read %d counter", instance->counters_read); + instance->state = MfUltralightPollerStateReadTearingFlags; + } else { + instance->counters_read++; + } + + } while(false); + + return NfcCommandContinue; +} + +static NfcCommand mf_ultralight_poller_handler_read_tearing_flags(MfUltralightPoller* instance) { + if(mf_ultralight_support_feature( + instance->feature_set, + MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportSingleCounter)) { + if(instance->tearing_flag_read == instance->tearing_flag_total) { + instance->state = MfUltralightPollerStateTryDefaultPass; + } else { + bool single_counter = mf_ultralight_support_feature( + instance->feature_set, MfUltralightFeatureSupportSingleCounter); + if(single_counter) instance->tearing_flag_read = 2; + + FURI_LOG_D(TAG, "Reading tearing flag %d", instance->tearing_flag_read); + instance->error = mf_ultralight_poller_read_tearing_flag( + instance, + instance->tearing_flag_read, + &instance->data->tearing_flag[instance->tearing_flag_read]); + if((instance->error == MfUltralightErrorProtocol) && single_counter) { + instance->tearing_flag_read++; + } else if(instance->error != MfUltralightErrorNone) { + FURI_LOG_D(TAG, "Reading tearing flag %d failed", instance->tearing_flag_read); + instance->state = MfUltralightPollerStateTryDefaultPass; + } else { + instance->tearing_flag_read++; + } + } + } else { + FURI_LOG_D(TAG, "Skip reading tearing flags"); + instance->state = MfUltralightPollerStateTryDefaultPass; + } + + return NfcCommandContinue; +} + +static NfcCommand mf_ultralight_poller_handler_auth(MfUltralightPoller* instance) { + NfcCommand command = NfcCommandContinue; + if(mf_ultralight_support_feature( + instance->feature_set, MfUltralightFeatureSupportPasswordAuth)) { + instance->mfu_event.type = MfUltralightPollerEventTypeAuthRequest; + + command = instance->callback(instance->general_event, instance->context); + if(!instance->mfu_event.data->auth_context.skip_auth) { + instance->auth_context.password = instance->mfu_event.data->auth_context.password; + uint32_t pass = nfc_util_bytes2num( + instance->auth_context.password.data, sizeof(MfUltralightAuthPassword)); + FURI_LOG_D(TAG, "Trying to authenticate with password %08lX", pass); + instance->error = mf_ultralight_poller_auth_pwd(instance, &instance->auth_context); + if(instance->error == MfUltralightErrorNone) { + FURI_LOG_D(TAG, "Auth success"); + instance->auth_context.auth_success = true; + instance->mfu_event.data->auth_context = instance->auth_context; + instance->mfu_event.type = MfUltralightPollerEventTypeAuthSuccess; + command = instance->callback(instance->general_event, instance->context); + } else { + FURI_LOG_D(TAG, "Auth failed"); + instance->auth_context.auth_success = false; + instance->mfu_event.type = MfUltralightPollerEventTypeAuthFailed; + command = instance->callback(instance->general_event, instance->context); + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + } + } + } + instance->state = MfUltralightPollerStateReadPages; + + return command; +} + +static NfcCommand mf_ultralight_poller_handler_read_pages(MfUltralightPoller* instance) { + MfUltralightPageReadCommandData data = {}; + uint16_t start_page = instance->pages_read; + if(MF_ULTRALIGHT_IS_NTAG_I2C(instance->data->type)) { + uint8_t tag = 0; + uint8_t sector = 0; + uint8_t pages_left = 0; + if(mf_ultralight_poller_ntag_i2c_addr_lin_to_tag( + instance, start_page, §or, &tag, &pages_left)) { + instance->error = + mf_ultralight_poller_read_page_from_sector(instance, sector, tag, &data); + } else { + FURI_LOG_D(TAG, "Failed to calculate sector and tag from %d page", start_page); + instance->error = MfUltralightErrorProtocol; + } + } else { + instance->error = mf_ultralight_poller_read_page(instance, start_page, &data); + } + + if(instance->error == MfUltralightErrorNone) { + for(size_t i = 0; i < 4; i++) { + if(start_page + i < instance->pages_total) { + FURI_LOG_D(TAG, "Read page %d success", start_page + i); + instance->data->page[start_page + i] = data.page[i]; + instance->pages_read++; + instance->data->pages_read = instance->pages_read; + } + } + if(instance->pages_read == instance->pages_total) { + instance->state = MfUltralightPollerStateReadCounters; + } + } else { + FURI_LOG_D(TAG, "Read page %d failed", instance->pages_read); + if(instance->pages_read) { + instance->state = MfUltralightPollerStateReadCounters; + } else { + instance->state = MfUltralightPollerStateReadFailed; + } + } + + return NfcCommandContinue; +} + +static NfcCommand mf_ultralight_poller_handler_try_default_pass(MfUltralightPoller* instance) { + do { + if(!mf_ultralight_support_feature( + instance->feature_set, MfUltralightFeatureSupportPasswordAuth)) + break; + + MfUltralightConfigPages* config = NULL; + mf_ultralight_get_config_page(instance->data, &config); + if(instance->auth_context.auth_success) { + config->password = instance->auth_context.password; + config->pack = instance->auth_context.pack; + } else if(config->access.authlim == 0) { + FURI_LOG_D(TAG, "No limits in authentication. Trying default password"); + nfc_util_num2bytes( + MF_ULTRALIGHT_DEFAULT_PASSWORD, + sizeof(MfUltralightAuthPassword), + instance->auth_context.password.data); + instance->error = mf_ultralight_poller_auth_pwd(instance, &instance->auth_context); + if(instance->error == MfUltralightErrorNone) { + FURI_LOG_D(TAG, "Default password detected"); + nfc_util_num2bytes( + MF_ULTRALIGHT_DEFAULT_PASSWORD, + sizeof(MfUltralightAuthPassword), + config->password.data); + config->pack = instance->auth_context.pack; + instance->auth_context.auth_success = true; + } + } + + if(instance->pages_read != instance->pages_total) { + // Probably password protected, fix AUTH0 and PROT so before AUTH0 + // can be written and since AUTH0 won't be readable, like on the + // original card + config->auth0 = instance->pages_read; + config->access.prot = true; + } else if(!instance->auth_context.auth_success) { + instance->pages_read -= 2; + instance->data->pages_read -= 2; + } + } while(false); + + instance->state = MfUltralightPollerStateReadSuccess; + return NfcCommandContinue; +} + +static NfcCommand mf_ultralight_poller_handler_read_fail(MfUltralightPoller* instance) { + FURI_LOG_D(TAG, "Read Failed"); + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + instance->mfu_event.type = MfUltralightPollerEventTypeReadFailed; + instance->mfu_event.data->error = instance->error; + NfcCommand command = instance->callback(instance->general_event, instance->context); + instance->state = MfUltralightPollerStateIdle; + return command; +} + +static NfcCommand mf_ultralight_poller_handler_read_success(MfUltralightPoller* instance) { + FURI_LOG_D(TAG, "Read success"); + instance->mfu_event.type = MfUltralightPollerEventTypeReadSuccess; + NfcCommand command = instance->callback(instance->general_event, instance->context); + + if(instance->mode == MfUltralightPollerModeRead) { + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + instance->state = MfUltralightPollerStateIdle; + } else { + instance->state = MfUltralightPollerStateRequestWriteData; + } + + return command; +} + +static NfcCommand mf_ultralight_poller_handler_request_write_data(MfUltralightPoller* instance) { + FURI_LOG_D(TAG, "Check writing capability"); + NfcCommand command = NfcCommandContinue; + MfUltralightPollerState next_state = MfUltralightPollerStateWritePages; + instance->current_page = 4; + + instance->mfu_event.type = MfUltralightPollerEventTypeRequestWriteData; + instance->callback(instance->general_event, instance->context); + + const MfUltralightData* write_data = instance->mfu_event.data->write_data; + const MfUltralightData* tag_data = instance->data; + uint32_t features = mf_ultralight_get_feature_support_set(tag_data->type); + + bool check_passed = false; + do { + if(write_data->type != tag_data->type) { + FURI_LOG_D(TAG, "Incorrect tag type"); + instance->mfu_event.type = MfUltralightPollerEventTypeCardMismatch; + break; + } + + if(!instance->auth_context.auth_success) { + FURI_LOG_D(TAG, "Unknown password"); + instance->mfu_event.type = MfUltralightPollerEventTypeCardLocked; + break; + } + + const MfUltralightPage staticlock_page = tag_data->page[2]; + if(staticlock_page.data[2] != 0 || staticlock_page.data[3] != 0) { + FURI_LOG_D(TAG, "Static lock bits are set"); + instance->mfu_event.type = MfUltralightPollerEventTypeCardLocked; + break; + } + + if(mf_ultralight_support_feature(features, MfUltralightFeatureSupportDynamicLock)) { + uint8_t dynlock_num = mf_ultralight_get_config_page_num(tag_data->type) - 1; + const MfUltralightPage dynlock_page = tag_data->page[dynlock_num]; + if(dynlock_page.data[0] != 0 || dynlock_page.data[1] != 0) { + FURI_LOG_D(TAG, "Dynamic lock bits are set"); + instance->mfu_event.type = MfUltralightPollerEventTypeCardLocked; + break; + } + } + + check_passed = true; + } while(false); + + if(!check_passed) { + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + command = instance->callback(instance->general_event, instance->context); + next_state = MfUltralightPollerStateWriteFail; + } + + instance->state = next_state; + return command; +} + +static NfcCommand mf_ultralight_poller_handler_write_pages(MfUltralightPoller* instance) { + NfcCommand command = NfcCommandContinue; + + do { + const MfUltralightData* write_data = instance->mfu_event.data->write_data; + uint8_t end_page = mf_ultralight_get_config_page_num(write_data->type) - 1; + if(instance->current_page == end_page) { + instance->state = MfUltralightPollerStateWriteSuccess; + break; + } + FURI_LOG_D(TAG, "Writing page %d", instance->current_page); + MfUltralightError error = mf_ultralight_poller_write_page( + instance, instance->current_page, &write_data->page[instance->current_page]); + if(error != MfUltralightErrorNone) { + instance->state = MfUltralightPollerStateWriteFail; + instance->error = error; + break; + } + instance->current_page++; + } while(false); + + return command; +} + +static NfcCommand mf_ultralight_poller_handler_write_fail(MfUltralightPoller* instance) { + FURI_LOG_D(TAG, "Write failed"); + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + instance->mfu_event.data->error = instance->error; + instance->mfu_event.type = MfUltralightPollerEventTypeWriteFail; + NfcCommand command = instance->callback(instance->general_event, instance->context); + return command; +} + +static NfcCommand mf_ultralight_poller_handler_write_success(MfUltralightPoller* instance) { + FURI_LOG_D(TAG, "Write success"); + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + instance->mfu_event.type = MfUltralightPollerEventTypeWriteSuccess; + NfcCommand command = instance->callback(instance->general_event, instance->context); + return command; +} + +static const MfUltralightPollerReadHandler + mf_ultralight_poller_read_handler[MfUltralightPollerStateNum] = { + [MfUltralightPollerStateIdle] = mf_ultralight_poller_handler_idle, + [MfUltralightPollerStateRequestMode] = mf_ultralight_poller_handler_request_mode, + [MfUltralightPollerStateReadVersion] = mf_ultralight_poller_handler_read_version, + [MfUltralightPollerStateDetectMfulC] = mf_ultralight_poller_handler_check_ultralight_c, + [MfUltralightPollerStateDetectNtag203] = mf_ultralight_poller_handler_check_ntag_203, + [MfUltralightPollerStateGetFeatureSet] = mf_ultralight_poller_handler_get_feature_set, + [MfUltralightPollerStateReadSignature] = mf_ultralight_poller_handler_read_signature, + [MfUltralightPollerStateReadCounters] = mf_ultralight_poller_handler_read_counters, + [MfUltralightPollerStateReadTearingFlags] = + mf_ultralight_poller_handler_read_tearing_flags, + [MfUltralightPollerStateAuth] = mf_ultralight_poller_handler_auth, + [MfUltralightPollerStateTryDefaultPass] = mf_ultralight_poller_handler_try_default_pass, + [MfUltralightPollerStateReadPages] = mf_ultralight_poller_handler_read_pages, + [MfUltralightPollerStateReadFailed] = mf_ultralight_poller_handler_read_fail, + [MfUltralightPollerStateReadSuccess] = mf_ultralight_poller_handler_read_success, + [MfUltralightPollerStateRequestWriteData] = + mf_ultralight_poller_handler_request_write_data, + [MfUltralightPollerStateWritePages] = mf_ultralight_poller_handler_write_pages, + [MfUltralightPollerStateWriteFail] = mf_ultralight_poller_handler_write_fail, + [MfUltralightPollerStateWriteSuccess] = mf_ultralight_poller_handler_write_success, + +}; + +static NfcCommand mf_ultralight_poller_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + + MfUltralightPoller* instance = context; + furi_assert(instance->callback); + + const Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data; + + NfcCommand command = NfcCommandContinue; + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + command = mf_ultralight_poller_read_handler[instance->state](instance); + } else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) { + instance->mfu_event.type = MfUltralightPollerEventTypeReadFailed; + command = instance->callback(instance->general_event, instance->context); + } + + return command; +} + +static bool mf_ultralight_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.protocol == NfcProtocolIso14443_3a); + + bool protocol_detected = false; + MfUltralightPoller* instance = context; + const Iso14443_3aPollerEvent* iso14443_3a_event = event.event_data; + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + MfUltralightPageReadCommandData read_page_cmd_data = {}; + MfUltralightError error = mf_ultralight_poller_read_page(instance, 0, &read_page_cmd_data); + protocol_detected = (error == MfUltralightErrorNone); + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + } + + return protocol_detected; +} + +const NfcPollerBase mf_ultralight_poller = { + .alloc = (NfcPollerAlloc)mf_ultralight_poller_alloc, + .free = (NfcPollerFree)mf_ultralight_poller_free, + .set_callback = (NfcPollerSetCallback)mf_ultralight_poller_set_callback, + .run = (NfcPollerRun)mf_ultralight_poller_run, + .detect = (NfcPollerDetect)mf_ultralight_poller_detect, + .get_data = (NfcPollerGetData)mf_ultralight_poller_get_data, +}; diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h new file mode 100644 index 00000000000..2343be089bf --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h @@ -0,0 +1,203 @@ +#pragma once + +#include "mf_ultralight.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief MfUltralightPoller opaque type definition. + */ +typedef struct MfUltralightPoller MfUltralightPoller; + +/** + * @brief Enumeration of possible MfUltralight poller event types. + */ +typedef enum { + MfUltralightPollerEventTypeRequestMode, /**< Poller requests for operating mode. */ + MfUltralightPollerEventTypeAuthRequest, /**< Poller requests to fill authentication context. */ + MfUltralightPollerEventTypeAuthSuccess, /**< Authentication succeeded. */ + MfUltralightPollerEventTypeAuthFailed, /**< Authentication failed. */ + MfUltralightPollerEventTypeReadSuccess, /**< Poller read card successfully. */ + MfUltralightPollerEventTypeReadFailed, /**< Poller failed to read card. */ + MfUltralightPollerEventTypeRequestWriteData, /**< Poller request card data for write operation. */ + MfUltralightPollerEventTypeCardMismatch, /**< Type of card for writing differs from presented one. */ + MfUltralightPollerEventTypeCardLocked, /**< Presented card is locked by password, AUTH0 or lock bytes. */ + MfUltralightPollerEventTypeWriteSuccess, /**< Poller wrote card successfully. */ + MfUltralightPollerEventTypeWriteFail, /**< Poller failed to write card. */ +} MfUltralightPollerEventType; + +/** + * @brief Enumeration of possible MfUltralight poller operating modes. + */ +typedef enum { + MfUltralightPollerModeRead, /**< Poller will only read card. It's a default mode. */ + MfUltralightPollerModeWrite, /**< Poller will write already saved card to another presented card. */ +} MfUltralightPollerMode; + +/** + * @brief MfUltralight poller authentication context. + */ +typedef struct { + MfUltralightAuthPassword password; /**< Password to be used for authentication. */ + MfUltralightAuthPack pack; /**< Pack received on successfull authentication. */ + bool auth_success; /**< Set to true if authentication succeeded, false otherwise. */ + bool skip_auth; /**< Set to true if authentication should be skipped, false otherwise. */ +} MfUltralightPollerAuthContext; + +/** + * @brief MfUltralight poller event data. + */ +typedef union { + MfUltralightPollerAuthContext auth_context; /**< Authentication context. */ + MfUltralightError error; /**< Error code indicating reading fail reason. */ + const MfUltralightData* write_data; + MfUltralightPollerMode poller_mode; +} MfUltralightPollerEventData; + +/** + * @brief MfUltralight poller event structure. + * + * Upon emission of an event, an instance of this struct will be passed to the callback. + */ +typedef struct { + MfUltralightPollerEventType type; /**< Type of emmitted event. */ + MfUltralightPollerEventData* data; /**< Pointer to event specific data. */ +} MfUltralightPollerEvent; + +/** + * @brief Perform authentication with password. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in, out] data pointer to the authentication context. + * @return MfUltralightErrorNone on success, an error code on failure. + */ +MfUltralightError mf_ultralight_poller_auth_pwd( + MfUltralightPoller* instance, + MfUltralightPollerAuthContext* data); + +/** + * @brief Start authentication procedure. + * + * Must ONLY be used inside the callback function. + * + * This function now is used only to identify Mf Ultralight C cards. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @return MfUltralightErrorNone if card supports authentication command, an error code on otherwise. + */ +MfUltralightError mf_ultralight_poller_authenticate(MfUltralightPoller* instance); + +/** + * @brief Read page from card. + * + * Must ONLY be used inside the callback function. + * + * Send read command and parse response. The response on this command is data of 4 pages starting + * from the page specified in the command. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] start_page page number to be read. + * @param[out] data pointer to the MfUltralightPageReadCommandData structure to be filled with page data. + * @return MfUltralightErrorNone on success, an error code on failure. + */ +MfUltralightError mf_ultralight_poller_read_page( + MfUltralightPoller* instance, + uint8_t start_page, + MfUltralightPageReadCommandData* data); + +/** + * @brief Read page from sector. + * + * Must ONLY be used inside the callback function. + * + * This command should be used for NTAGI2C tags. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] sector sector number to be read. + * @param[in] tag tag number to be read. + * @param[out] data pointer to the MfUltralightPageReadCommandData structure to be filled with page data. + * @return MfUltralightErrorNone on success, an error code on failure. + */ +MfUltralightError mf_ultralight_poller_read_page_from_sector( + MfUltralightPoller* instance, + uint8_t sector, + uint8_t tag, + MfUltralightPageReadCommandData* data); + +/** + * @brief Write page to card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] page page number to be written. + * @param[in] data pointer to the MfUltralightPage structure to be written. + * @return MfUltralightErrorNone on success, an error code on failure. + */ +MfUltralightError mf_ultralight_poller_write_page( + MfUltralightPoller* instance, + uint8_t page, + const MfUltralightPage* data); + +/** + * @brief Read version from card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the MfUltralightVersion structure to be filled. + * @return MfUltralightErrorNone on success, an error code on failure. + */ +MfUltralightError + mf_ultralight_poller_read_version(MfUltralightPoller* instance, MfUltralightVersion* data); + +/** + * @brief Read signature from card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the MfUltralightSignature structure to be filled. + * @return MfUltralightErrorNone on success, an error code on failure. + */ +MfUltralightError + mf_ultralight_poller_read_signature(MfUltralightPoller* instance, MfUltralightSignature* data); + +/** + * @brief Read counter from card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] counter_num counter number to be read. + * @param[out] data pointer to the MfUltralightCounter structure to be filled. + * @return MfUltralightErrorNone on success, an error code on failure. + */ +MfUltralightError mf_ultralight_poller_read_counter( + MfUltralightPoller* instance, + uint8_t counter_num, + MfUltralightCounter* data); + +/** + * @brief Read tearing flag from card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] tearing_falg_num tearing flag number to be read. + * @param[out] data pointer to the MfUltralightTearingFlag structure to be filled. + * @return MfUltralightErrorNone on success, an error code on failure. + */ +MfUltralightError mf_ultralight_poller_read_tearing_flag( + MfUltralightPoller* instance, + uint8_t tearing_falg_num, + MfUltralightTearingFlag* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_defs.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_defs.h new file mode 100644 index 00000000000..80b0d7b6eaa --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcPollerBase mf_ultralight_poller; diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c new file mode 100644 index 00000000000..2d88db3e598 --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c @@ -0,0 +1,306 @@ +#include "mf_ultralight_poller_i.h" + +#include + +#define TAG "MfUltralightPoller" + +MfUltralightError mf_ultralight_process_error(Iso14443_3aError error) { + MfUltralightError ret = MfUltralightErrorNone; + + switch(error) { + case Iso14443_3aErrorNone: + ret = MfUltralightErrorNone; + break; + case Iso14443_3aErrorNotPresent: + ret = MfUltralightErrorNotPresent; + break; + case Iso14443_3aErrorColResFailed: + case Iso14443_3aErrorCommunication: + case Iso14443_3aErrorWrongCrc: + ret = MfUltralightErrorProtocol; + break; + case Iso14443_3aErrorTimeout: + ret = MfUltralightErrorTimeout; + break; + default: + ret = MfUltralightErrorProtocol; + break; + } + + return ret; +} + +MfUltralightError mf_ultralight_poller_auth_pwd( + MfUltralightPoller* instance, + MfUltralightPollerAuthContext* data) { + uint8_t auth_cmd[5] = {MF_ULTRALIGHT_CMD_PWD_AUTH}; //-V1009 + memccpy(&auth_cmd[1], data->password.data, 0, MF_ULTRALIGHT_AUTH_PASSWORD_SIZE); + bit_buffer_copy_bytes(instance->tx_buffer, auth_cmd, sizeof(auth_cmd)); + + MfUltralightError ret = MfUltralightErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + do { + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_ultralight_process_error(error); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != MF_ULTRALIGHT_AUTH_PACK_SIZE) { + ret = MfUltralightErrorAuth; + break; + } + bit_buffer_write_bytes(instance->rx_buffer, data->pack.data, MF_ULTRALIGHT_AUTH_PACK_SIZE); + } while(false); + + return ret; +} + +MfUltralightError mf_ultralight_poller_authenticate(MfUltralightPoller* instance) { + uint8_t auth_cmd[2] = {MF_ULTRALIGHT_CMD_AUTH, 0x00}; + bit_buffer_copy_bytes(instance->tx_buffer, auth_cmd, sizeof(auth_cmd)); + + MfUltralightError ret = MfUltralightErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + do { + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_ultralight_process_error(error); + break; + } + if((bit_buffer_get_size_bytes(instance->rx_buffer) != MF_ULTRALIGHT_AUTH_RESPONSE_SIZE) && + (bit_buffer_get_byte(instance->rx_buffer, 0) != 0xAF)) { + ret = MfUltralightErrorAuth; + break; + } + //Save encrypted PICC random number RndB here if needed + } while(false); + + return ret; +} + +MfUltralightError mf_ultralight_poller_read_page_from_sector( + MfUltralightPoller* instance, + uint8_t sector, + uint8_t tag, + MfUltralightPageReadCommandData* data) { + MfUltralightError ret = MfUltralightErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + const uint8_t select_sector_cmd[2] = {MF_ULTRALIGHT_CMD_SECTOR_SELECT, 0xff}; + bit_buffer_copy_bytes(instance->tx_buffer, select_sector_cmd, sizeof(select_sector_cmd)); + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC); + if(error != Iso14443_3aErrorWrongCrc) { + FURI_LOG_D(TAG, "Failed to issue sector select command"); + ret = mf_ultralight_process_error(error); + break; + } + + const uint8_t read_sector_cmd[4] = {sector, 0x00, 0x00, 0x00}; + bit_buffer_copy_bytes(instance->tx_buffer, read_sector_cmd, sizeof(read_sector_cmd)); + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC); + if(error != Iso14443_3aErrorTimeout) { + // This is NOT a typo! The tag ACKs by not sending a response within 1ms. + FURI_LOG_D(TAG, "Sector %u select NAK'd", sector); + ret = MfUltralightErrorProtocol; + break; + } + + ret = mf_ultralight_poller_read_page(instance, tag, data); + } while(false); + + return ret; +} + +MfUltralightError mf_ultralight_poller_read_page( + MfUltralightPoller* instance, + uint8_t start_page, + MfUltralightPageReadCommandData* data) { + MfUltralightError ret = MfUltralightErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + uint8_t read_page_cmd[2] = {MF_ULTRALIGHT_CMD_READ_PAGE, start_page}; + bit_buffer_copy_bytes(instance->tx_buffer, read_page_cmd, sizeof(read_page_cmd)); + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_ultralight_process_error(error); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != + sizeof(MfUltralightPageReadCommandData)) { + ret = MfUltralightErrorProtocol; + break; + } + bit_buffer_write_bytes(instance->rx_buffer, data, sizeof(MfUltralightPageReadCommandData)); + } while(false); + + return ret; +} + +MfUltralightError mf_ultralight_poller_write_page( + MfUltralightPoller* instance, + uint8_t page, + const MfUltralightPage* data) { + MfUltralightError ret = MfUltralightErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + uint8_t write_page_cmd[MF_ULTRALIGHT_PAGE_SIZE + 2] = {MF_ULTRALIGHT_CMD_WRITE_PAGE, page}; + memcpy(&write_page_cmd[2], data->data, MF_ULTRALIGHT_PAGE_SIZE); + bit_buffer_copy_bytes(instance->tx_buffer, write_page_cmd, sizeof(write_page_cmd)); + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC); + if(error != Iso14443_3aErrorWrongCrc) { + ret = mf_ultralight_process_error(error); + break; + } + if(bit_buffer_get_size(instance->rx_buffer) != 4) { + ret = MfUltralightErrorProtocol; + break; + } + if(!bit_buffer_starts_with_byte(instance->rx_buffer, MF_ULTRALIGHT_CMD_ACK)) { + ret = MfUltralightErrorProtocol; + break; + } + } while(false); + + return ret; +} + +MfUltralightError + mf_ultralight_poller_read_version(MfUltralightPoller* instance, MfUltralightVersion* data) { + MfUltralightError ret = MfUltralightErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + const uint8_t get_version_cmd = MF_ULTRALIGHT_CMD_GET_VERSION; + bit_buffer_copy_bytes(instance->tx_buffer, &get_version_cmd, sizeof(get_version_cmd)); + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_ultralight_process_error(error); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(MfUltralightVersion)) { + FURI_LOG_I( + TAG, "Read Version failed: %zu", bit_buffer_get_size_bytes(instance->rx_buffer)); + ret = MfUltralightErrorProtocol; + break; + } + bit_buffer_write_bytes(instance->rx_buffer, data, sizeof(MfUltralightVersion)); + } while(false); + + return ret; +} + +MfUltralightError + mf_ultralight_poller_read_signature(MfUltralightPoller* instance, MfUltralightSignature* data) { + MfUltralightError ret = MfUltralightErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + const uint8_t read_signature_cmd[2] = {MF_ULTRALIGHT_CMD_READ_SIG, 0x00}; + bit_buffer_copy_bytes(instance->tx_buffer, read_signature_cmd, sizeof(read_signature_cmd)); + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_ultralight_process_error(error); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(MfUltralightSignature)) { + ret = MfUltralightErrorProtocol; + break; + } + bit_buffer_write_bytes(instance->rx_buffer, data, sizeof(MfUltralightSignature)); + } while(false); + + return ret; +} + +MfUltralightError mf_ultralight_poller_read_counter( + MfUltralightPoller* instance, + uint8_t counter_num, + MfUltralightCounter* data) { + MfUltralightError ret = MfUltralightErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + uint8_t read_counter_cmd[2] = {MF_ULTRALIGHT_CMD_READ_CNT, counter_num}; + bit_buffer_copy_bytes(instance->tx_buffer, read_counter_cmd, sizeof(read_counter_cmd)); + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_ultralight_process_error(error); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != MF_ULTRALIGHT_COUNTER_SIZE) { + ret = MfUltralightErrorProtocol; + break; + } + bit_buffer_write_bytes(instance->rx_buffer, data->data, MF_ULTRALIGHT_COUNTER_SIZE); + } while(false); + + return ret; +} + +MfUltralightError mf_ultralight_poller_read_tearing_flag( + MfUltralightPoller* instance, + uint8_t tearing_falg_num, + MfUltralightTearingFlag* data) { + MfUltralightError ret = MfUltralightErrorNone; + Iso14443_3aError error = Iso14443_3aErrorNone; + + do { + uint8_t check_tearing_cmd[2] = {MF_ULTRALIGHT_CMD_CHECK_TEARING, tearing_falg_num}; + bit_buffer_copy_bytes(instance->tx_buffer, check_tearing_cmd, sizeof(check_tearing_cmd)); + error = iso14443_3a_poller_send_standard_frame( + instance->iso14443_3a_poller, + instance->tx_buffer, + instance->rx_buffer, + MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC); + if(error != Iso14443_3aErrorNone) { + ret = mf_ultralight_process_error(error); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(MfUltralightTearingFlag)) { + ret = MfUltralightErrorProtocol; + break; + } + bit_buffer_write_bytes(instance->rx_buffer, data, sizeof(MfUltralightTearingFlag)); + } while(false); + + return ret; +} diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h new file mode 100644 index 00000000000..7c7354b1c6d --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h @@ -0,0 +1,115 @@ +#pragma once + +#include "mf_ultralight_poller.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MF_ULTRALIGHT_POLLER_STANDARD_FWT_FC (60000) +#define MF_ULTRALIGHT_MAX_BUFF_SIZE (64) + +#define MF_ULTRALIGHT_DEFAULT_PASSWORD (0xffffffffUL) + +#define MF_ULTRALIGHT_IS_NTAG_I2C(type) \ + (((type) == MfUltralightTypeNTAGI2C1K) || ((type) == MfUltralightTypeNTAGI2C2K) || \ + ((type) == MfUltralightTypeNTAGI2CPlus1K) || ((type) == MfUltralightTypeNTAGI2CPlus2K)) + +typedef struct { + MfUltralightPage page; + uint8_t page_to_write; +} MfUltralightPollerWritePageCommand; + +typedef struct { + MfUltralightPageReadCommandData data; + uint8_t start_page; +} MfUltralightPollerReadPageCommand; + +typedef struct { + MfUltralightCounter data; + uint8_t counter_num; +} MfUltralightPollerReadCounterCommand; + +typedef struct { + MfUltralightTearingFlag data; + uint8_t tearing_flag_num; +} MfUltralightPollerReadTearingFlagCommand; + +typedef union { + MfUltralightPollerWritePageCommand write_cmd; + MfUltralightPollerReadPageCommand read_cmd; + MfUltralightVersion version; + MfUltralightSignature signature; + MfUltralightPollerReadCounterCommand counter_cmd; + MfUltralightPollerReadTearingFlagCommand tearing_flag_cmd; + MfUltralightData* data; +} MfUltralightPollerContextData; + +typedef enum { + MfUltralightPollerStateIdle, + MfUltralightPollerStateRequestMode, + MfUltralightPollerStateReadVersion, + MfUltralightPollerStateDetectMfulC, + MfUltralightPollerStateDetectNtag203, + MfUltralightPollerStateGetFeatureSet, + MfUltralightPollerStateReadSignature, + MfUltralightPollerStateReadCounters, + MfUltralightPollerStateReadTearingFlags, + MfUltralightPollerStateAuth, + MfUltralightPollerStateReadPages, + MfUltralightPollerStateTryDefaultPass, + MfUltralightPollerStateReadFailed, + MfUltralightPollerStateReadSuccess, + MfUltralightPollerStateRequestWriteData, + MfUltralightPollerStateWritePages, + MfUltralightPollerStateWriteFail, + MfUltralightPollerStateWriteSuccess, + + MfUltralightPollerStateNum, +} MfUltralightPollerState; + +struct MfUltralightPoller { + Iso14443_3aPoller* iso14443_3a_poller; + MfUltralightPollerState state; + MfUltralightPollerMode mode; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + MfUltralightData* data; + MfUltralightPollerAuthContext auth_context; + uint32_t feature_set; + uint16_t pages_read; + uint16_t pages_total; + uint8_t counters_read; + uint8_t counters_total; + uint8_t tearing_flag_read; + uint8_t tearing_flag_total; + uint16_t current_page; + MfUltralightError error; + + NfcGenericEvent general_event; + MfUltralightPollerEvent mfu_event; + MfUltralightPollerEventData mfu_event_data; + NfcGenericCallback callback; + void* context; +}; + +MfUltralightError mf_ultralight_process_error(Iso14443_3aError error); + +MfUltralightPoller* mf_ultralight_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller); + +void mf_ultralight_poller_free(MfUltralightPoller* instance); + +const MfUltralightData* mf_ultralight_poller_get_data(MfUltralightPoller* instance); + +bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag( + MfUltralightPoller* instance, + uint16_t lin_addr, + uint8_t* sector, + uint8_t* tag, + uint8_t* pages_left); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.c new file mode 100644 index 00000000000..c4833facf0f --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.c @@ -0,0 +1,286 @@ +#include "mf_ultralight_poller_sync.h" +#include "mf_ultralight_poller_i.h" + +#include + +#include + +#define MF_ULTRALIGHT_POLLER_COMPLETE_EVENT (1UL << 0) + +typedef enum { + MfUltralightPollerCmdTypeReadPage, + MfUltralightPollerCmdTypeWritePage, + MfUltralightPollerCmdTypeReadVersion, + MfUltralightPollerCmdTypeReadSignature, + MfUltralightPollerCmdTypeReadCounter, + MfUltralightPollerCmdTypeReadTearingFlag, + + MfUltralightPollerCmdTypeNum, +} MfUltralightPollerCmdType; + +typedef struct { + MfUltralightPollerCmdType cmd_type; + FuriThreadId thread_id; + MfUltralightError error; + MfUltralightPollerContextData data; +} MfUltralightPollerContext; + +typedef MfUltralightError (*MfUltralightPollerCmdHandler)( + MfUltralightPoller* poller, + MfUltralightPollerContextData* data); + +MfUltralightError mf_ultralight_poller_read_page_handler( + MfUltralightPoller* poller, + MfUltralightPollerContextData* data) { + return mf_ultralight_poller_read_page(poller, data->read_cmd.start_page, &data->read_cmd.data); +} + +MfUltralightError mf_ultralight_poller_write_page_handler( + MfUltralightPoller* poller, + MfUltralightPollerContextData* data) { + return mf_ultralight_poller_write_page( + poller, data->write_cmd.page_to_write, &data->write_cmd.page); +} + +MfUltralightError mf_ultralight_poller_read_version_handler( + MfUltralightPoller* poller, + MfUltralightPollerContextData* data) { + return mf_ultralight_poller_read_version(poller, &data->version); +} + +MfUltralightError mf_ultralight_poller_read_signature_handler( + MfUltralightPoller* poller, + MfUltralightPollerContextData* data) { + return mf_ultralight_poller_read_signature(poller, &data->signature); +} + +MfUltralightError mf_ultralight_poller_read_counter_handler( + MfUltralightPoller* poller, + MfUltralightPollerContextData* data) { + return mf_ultralight_poller_read_counter( + poller, data->counter_cmd.counter_num, &data->counter_cmd.data); +} + +MfUltralightError mf_ultralight_poller_read_tearing_flag_handler( + MfUltralightPoller* poller, + MfUltralightPollerContextData* data) { + return mf_ultralight_poller_read_tearing_flag( + poller, data->tearing_flag_cmd.tearing_flag_num, &data->tearing_flag_cmd.data); +} + +static const MfUltralightPollerCmdHandler + mf_ultralight_poller_cmd_handlers[MfUltralightPollerCmdTypeNum] = { + [MfUltralightPollerCmdTypeReadPage] = mf_ultralight_poller_read_page_handler, + [MfUltralightPollerCmdTypeWritePage] = mf_ultralight_poller_write_page_handler, + [MfUltralightPollerCmdTypeReadVersion] = mf_ultralight_poller_read_version_handler, + [MfUltralightPollerCmdTypeReadSignature] = mf_ultralight_poller_read_signature_handler, + [MfUltralightPollerCmdTypeReadCounter] = mf_ultralight_poller_read_counter_handler, + [MfUltralightPollerCmdTypeReadTearingFlag] = + mf_ultralight_poller_read_tearing_flag_handler, +}; + +static NfcCommand mf_ultralight_poller_cmd_callback(NfcGenericEventEx event, void* context) { + furi_assert(event.poller); + furi_assert(event.parent_event_data); + furi_assert(context); + + MfUltralightPollerContext* poller_context = context; + Iso14443_3aPollerEvent* iso14443_3a_event = event.parent_event_data; + MfUltralightPoller* mfu_poller = event.poller; + + if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeReady) { + poller_context->error = mf_ultralight_poller_cmd_handlers[poller_context->cmd_type]( + mfu_poller, &poller_context->data); + } else if(iso14443_3a_event->type == Iso14443_3aPollerEventTypeError) { + poller_context->error = mf_ultralight_process_error(iso14443_3a_event->data->error); + } + + furi_thread_flags_set(poller_context->thread_id, MF_ULTRALIGHT_POLLER_COMPLETE_EVENT); + + return NfcCommandStop; +} + +static MfUltralightError + mf_ultralight_poller_cmd_execute(Nfc* nfc, MfUltralightPollerContext* poller_ctx) { + furi_assert(poller_ctx->cmd_type < MfUltralightPollerCmdTypeNum); + + poller_ctx->thread_id = furi_thread_get_current_id(); + + NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolMfUltralight); + nfc_poller_start_ex(poller, mf_ultralight_poller_cmd_callback, poller_ctx); + furi_thread_flags_wait(MF_ULTRALIGHT_POLLER_COMPLETE_EVENT, FuriFlagWaitAny, FuriWaitForever); + furi_thread_flags_clear(MF_ULTRALIGHT_POLLER_COMPLETE_EVENT); + + nfc_poller_stop(poller); + nfc_poller_free(poller); + + return poller_ctx->error; +} + +MfUltralightError + mf_ultralight_poller_sync_read_page(Nfc* nfc, uint16_t page, MfUltralightPage* data) { + furi_assert(nfc); + furi_assert(data); + + MfUltralightPollerContext poller_context = { + .cmd_type = MfUltralightPollerCmdTypeReadPage, + .data.read_cmd.start_page = page, + }; + + MfUltralightError error = mf_ultralight_poller_cmd_execute(nfc, &poller_context); + + if(error == MfUltralightErrorNone) { + *data = poller_context.data.read_cmd.data.page[0]; + } + + return error; +} + +MfUltralightError + mf_ultralight_poller_sync_write_page(Nfc* nfc, uint16_t page, MfUltralightPage* data) { + furi_assert(nfc); + furi_assert(data); + + MfUltralightPollerContext poller_context = { + .cmd_type = MfUltralightPollerCmdTypeWritePage, + .data.write_cmd = + { + .page_to_write = page, + .page = *data, + }, + }; + + MfUltralightError error = mf_ultralight_poller_cmd_execute(nfc, &poller_context); + + return error; +} + +MfUltralightError mf_ultralight_poller_sync_read_version(Nfc* nfc, MfUltralightVersion* data) { + furi_assert(nfc); + furi_assert(data); + + MfUltralightPollerContext poller_context = { + .cmd_type = MfUltralightPollerCmdTypeReadVersion, + }; + + MfUltralightError error = mf_ultralight_poller_cmd_execute(nfc, &poller_context); + + if(error == MfUltralightErrorNone) { + *data = poller_context.data.version; + } + + return error; +} + +MfUltralightError mf_ultralight_poller_sync_read_signature(Nfc* nfc, MfUltralightSignature* data) { + furi_assert(nfc); + furi_assert(data); + + MfUltralightPollerContext poller_context = { + .cmd_type = MfUltralightPollerCmdTypeReadSignature, + }; + + MfUltralightError error = mf_ultralight_poller_cmd_execute(nfc, &poller_context); + + if(error == MfUltralightErrorNone) { + *data = poller_context.data.signature; + } + + return error; +} + +MfUltralightError mf_ultralight_poller_sync_read_counter( + Nfc* nfc, + uint8_t counter_num, + MfUltralightCounter* data) { + furi_assert(nfc); + furi_assert(data); + + MfUltralightPollerContext poller_context = { + .cmd_type = MfUltralightPollerCmdTypeReadCounter, + .data.counter_cmd.counter_num = counter_num, + }; + + MfUltralightError error = mf_ultralight_poller_cmd_execute(nfc, &poller_context); + + if(error == MfUltralightErrorNone) { + *data = poller_context.data.counter_cmd.data; + } + + return error; +} + +MfUltralightError mf_ultralight_poller_sync_read_tearing_flag( + Nfc* nfc, + uint8_t flag_num, + MfUltralightTearingFlag* data) { + furi_assert(nfc); + furi_assert(data); + + MfUltralightPollerContext poller_context = { + .cmd_type = MfUltralightPollerCmdTypeReadTearingFlag, + .data.tearing_flag_cmd.tearing_flag_num = flag_num, + }; + + MfUltralightError error = mf_ultralight_poller_cmd_execute(nfc, &poller_context); + + if(error == MfUltralightErrorNone) { + *data = poller_context.data.tearing_flag_cmd.data; + } + + return error; +} + +static NfcCommand mf_ultralight_poller_read_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.instance); + furi_assert(event.event_data); + furi_assert(event.protocol == NfcProtocolMfUltralight); + + NfcCommand command = NfcCommandContinue; + MfUltralightPollerContext* poller_context = context; + MfUltralightPoller* mfu_poller = event.instance; + MfUltralightPollerEvent* mfu_event = event.event_data; + + if(mfu_event->type == MfUltralightPollerEventTypeReadSuccess) { + mf_ultralight_copy(poller_context->data.data, mf_ultralight_poller_get_data(mfu_poller)); + poller_context->error = MfUltralightErrorNone; + command = NfcCommandStop; + } else if(mfu_event->type == MfUltralightPollerEventTypeReadFailed) { + poller_context->error = mfu_event->data->error; + command = NfcCommandStop; + } else if(mfu_event->type == MfUltralightPollerEventTypeAuthRequest) { + mfu_event->data->auth_context.skip_auth = true; + } + + if(command == NfcCommandStop) { + furi_thread_flags_set(poller_context->thread_id, MF_ULTRALIGHT_POLLER_COMPLETE_EVENT); + } + + return command; +} + +MfUltralightError mf_ultralight_poller_sync_read_card(Nfc* nfc, MfUltralightData* data) { + furi_assert(nfc); + furi_assert(data); + + MfUltralightPollerContext poller_context = {}; + poller_context.thread_id = furi_thread_get_current_id(); + poller_context.data.data = mf_ultralight_alloc(); + + NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolMfUltralight); + nfc_poller_start(poller, mf_ultralight_poller_read_callback, &poller_context); + furi_thread_flags_wait(MF_ULTRALIGHT_POLLER_COMPLETE_EVENT, FuriFlagWaitAny, FuriWaitForever); + furi_thread_flags_clear(MF_ULTRALIGHT_POLLER_COMPLETE_EVENT); + + nfc_poller_stop(poller); + nfc_poller_free(poller); + + if(poller_context.error == MfUltralightErrorNone) { + mf_ultralight_copy(data, poller_context.data.data); + } + + mf_ultralight_free(poller_context.data.data); + + return poller_context.error; +} diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h new file mode 100644 index 00000000000..ac585aad7e9 --- /dev/null +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h @@ -0,0 +1,34 @@ +#pragma once + +#include "mf_ultralight.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +MfUltralightError + mf_ultralight_poller_sync_read_page(Nfc* nfc, uint16_t page, MfUltralightPage* data); + +MfUltralightError + mf_ultralight_poller_sync_write_page(Nfc* nfc, uint16_t page, MfUltralightPage* data); + +MfUltralightError mf_ultralight_poller_sync_read_version(Nfc* nfc, MfUltralightVersion* data); + +MfUltralightError mf_ultralight_poller_sync_read_signature(Nfc* nfc, MfUltralightSignature* data); + +MfUltralightError mf_ultralight_poller_sync_read_counter( + Nfc* nfc, + uint8_t counter_num, + MfUltralightCounter* data); + +MfUltralightError mf_ultralight_poller_sync_read_tearing_flag( + Nfc* nfc, + uint8_t flag_num, + MfUltralightTearingFlag* data); + +MfUltralightError mf_ultralight_poller_sync_read_card(Nfc* nfc, MfUltralightData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c deleted file mode 100644 index 3b53439329a..00000000000 --- a/lib/nfc/protocols/mifare_classic.c +++ /dev/null @@ -1,992 +0,0 @@ -#include "mifare_classic.h" -#include "nfca.h" -#include "nfc_util.h" -#include - -// Algorithm from https://github.com/RfidResearchGroup/proxmark3.git - -#define TAG "MfClassic" - -#define MF_CLASSIC_AUTH_KEY_A_CMD (0x60U) -#define MF_CLASSIC_AUTH_KEY_B_CMD (0x61U) -#define MF_CLASSIC_READ_SECT_CMD (0x30) - -typedef enum { - MfClassicActionDataRead, - MfClassicActionDataWrite, - MfClassicActionDataInc, - MfClassicActionDataDec, - - MfClassicActionKeyARead, - MfClassicActionKeyAWrite, - MfClassicActionKeyBRead, - MfClassicActionKeyBWrite, - MfClassicActionACRead, - MfClassicActionACWrite, -} MfClassicAction; - -const char* mf_classic_get_type_str(MfClassicType type) { - if(type == MfClassicType1k) { - return "MIFARE Classic 1K"; - } else if(type == MfClassicType4k) { - return "MIFARE Classic 4K"; - } else { - return "Unknown"; - } -} - -static uint8_t mf_classic_get_first_block_num_of_sector(uint8_t sector) { - furi_assert(sector < 40); - if(sector < 32) { - return sector * 4; - } else { - return 32 * 4 + (sector - 32) * 16; - } -} - -uint8_t mf_classic_get_sector_trailer_block_num_by_sector(uint8_t sector) { - furi_assert(sector < 40); - if(sector < 32) { - return sector * 4 + 3; - } else { - return 32 * 4 + (sector - 32) * 16 + 15; - } -} - -uint8_t mf_classic_get_sector_by_block(uint8_t block) { - if(block < 128) { - return (block | 0x03) / 4; - } else { - return 32 + ((block | 0xf) - 32 * 4) / 16; - } -} - -static uint8_t mf_classic_get_blocks_num_in_sector(uint8_t sector) { - furi_assert(sector < 40); - return sector < 32 ? 4 : 16; -} - -uint8_t mf_classic_get_sector_trailer_num_by_block(uint8_t block) { - if(block < 128) { - return block | 0x03; - } else { - return block | 0x0f; - } -} - -bool mf_classic_is_sector_trailer(uint8_t block) { - return block == mf_classic_get_sector_trailer_num_by_block(block); -} - -MfClassicSectorTrailer* - mf_classic_get_sector_trailer_by_sector(MfClassicData* data, uint8_t sector) { - furi_assert(data); - uint8_t sec_tr_block_num = mf_classic_get_sector_trailer_block_num_by_sector(sector); - return (MfClassicSectorTrailer*)data->block[sec_tr_block_num].value; -} - -uint8_t mf_classic_get_total_sectors_num(MfClassicType type) { - if(type == MfClassicType1k) { - return MF_CLASSIC_1K_TOTAL_SECTORS_NUM; - } else if(type == MfClassicType4k) { - return MF_CLASSIC_4K_TOTAL_SECTORS_NUM; - } else { - return 0; - } -} - -static uint16_t mf_classic_get_total_block_num(MfClassicType type) { - if(type == MfClassicType1k) { - return 64; - } else if(type == MfClassicType4k) { - return 256; - } else { - return 0; - } -} - -bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num) { - furi_assert(data); - - return (FURI_BIT(data->block_read_mask[block_num / 32], block_num % 32) == 1); -} - -void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data) { - furi_assert(data); - - if(mf_classic_is_sector_trailer(block_num)) { - memcpy(&data->block[block_num].value[6], &block_data->value[6], 4); - } else { - memcpy(data->block[block_num].value, block_data->value, MF_CLASSIC_BLOCK_SIZE); - } - FURI_BIT_SET(data->block_read_mask[block_num / 32], block_num % 32); -} - -bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type) { - furi_assert(data); - - bool key_found = false; - if(key_type == MfClassicKeyA) { - key_found = (FURI_BIT(data->key_a_mask, sector_num) == 1); - } else if(key_type == MfClassicKeyB) { - key_found = (FURI_BIT(data->key_b_mask, sector_num) == 1); - } - - return key_found; -} - -void mf_classic_set_key_found( - MfClassicData* data, - uint8_t sector_num, - MfClassicKey key_type, - uint64_t key) { - furi_assert(data); - - uint8_t key_arr[6] = {}; - MfClassicSectorTrailer* sec_trailer = - mf_classic_get_sector_trailer_by_sector(data, sector_num); - nfc_util_num2bytes(key, 6, key_arr); - if(key_type == MfClassicKeyA) { - memcpy(sec_trailer->key_a, key_arr, sizeof(sec_trailer->key_a)); - FURI_BIT_SET(data->key_a_mask, sector_num); - } else if(key_type == MfClassicKeyB) { - memcpy(sec_trailer->key_b, key_arr, sizeof(sec_trailer->key_b)); - FURI_BIT_SET(data->key_b_mask, sector_num); - } -} - -bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num) { - furi_assert(data); - - bool sector_read = false; - do { - if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyA)) break; - if(!mf_classic_is_key_found(data, sector_num, MfClassicKeyB)) break; - uint8_t start_block = mf_classic_get_first_block_num_of_sector(sector_num); - uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sector_num); - uint8_t block_read = true; - for(size_t i = start_block; i < start_block + total_blocks; i++) { - block_read = mf_classic_is_block_read(data, i); - if(!block_read) break; - } - sector_read = block_read; - } while(false); - - return sector_read; -} - -void mf_classic_get_read_sectors_and_keys( - MfClassicData* data, - uint8_t* sectors_read, - uint8_t* keys_found) { - furi_assert(data); - *sectors_read = 0; - *keys_found = 0; - uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type); - for(size_t i = 0; i < sectors_total; i++) { - if(mf_classic_is_key_found(data, i, MfClassicKeyA)) { - *keys_found += 1; - } - if(mf_classic_is_key_found(data, i, MfClassicKeyB)) { - *keys_found += 1; - } - uint8_t first_block = mf_classic_get_first_block_num_of_sector(i); - uint8_t total_blocks_in_sec = mf_classic_get_blocks_num_in_sector(i); - bool blocks_read = true; - for(size_t i = first_block; i < first_block + total_blocks_in_sec; i++) { - blocks_read = mf_classic_is_block_read(data, i); - if(!blocks_read) break; - } - if(blocks_read) { - *sectors_read += 1; - } - } -} - -static bool mf_classic_is_allowed_access_sector_trailer( - MfClassicEmulator* emulator, - uint8_t block_num, - MfClassicKey key, - MfClassicAction action) { - uint8_t* sector_trailer = emulator->data.block[block_num].value; - uint8_t AC = ((sector_trailer[7] >> 5) & 0x04) | ((sector_trailer[8] >> 2) & 0x02) | - ((sector_trailer[8] >> 7) & 0x01); - switch(action) { - case MfClassicActionKeyARead: { - return false; - } - case MfClassicActionKeyAWrite: { - return ( - (key == MfClassicKeyA && (AC == 0x00 || AC == 0x01)) || - (key == MfClassicKeyB && (AC == 0x04 || AC == 0x03))); - } - case MfClassicActionKeyBRead: { - return (key == MfClassicKeyA && (AC == 0x00 || AC == 0x02 || AC == 0x01)); - } - case MfClassicActionKeyBWrite: { - return ( - (key == MfClassicKeyA && (AC == 0x00 || AC == 0x01)) || - (key == MfClassicKeyB && (AC == 0x04 || AC == 0x03))); - } - case MfClassicActionACRead: { - return ( - (key == MfClassicKeyA) || - (key == MfClassicKeyB && !(AC == 0x00 || AC == 0x02 || AC == 0x01))); - } - case MfClassicActionACWrite: { - return ( - (key == MfClassicKeyA && (AC == 0x01)) || - (key == MfClassicKeyB && (AC == 0x03 || AC == 0x05))); - } - default: - return false; - } - return true; -} - -static bool mf_classic_is_allowed_access_data_block( - MfClassicEmulator* emulator, - uint8_t block_num, - MfClassicKey key, - MfClassicAction action) { - uint8_t* sector_trailer = - emulator->data.block[mf_classic_get_sector_trailer_num_by_block(block_num)].value; - - uint8_t sector_block; - if(block_num <= 128) { - sector_block = block_num & 0x03; - } else { - sector_block = (block_num & 0x0f) / 5; - } - - uint8_t AC; - switch(sector_block) { - case 0x00: { - AC = ((sector_trailer[7] >> 2) & 0x04) | ((sector_trailer[8] << 1) & 0x02) | - ((sector_trailer[8] >> 4) & 0x01); - break; - } - case 0x01: { - AC = ((sector_trailer[7] >> 3) & 0x04) | ((sector_trailer[8] >> 0) & 0x02) | - ((sector_trailer[8] >> 5) & 0x01); - break; - } - case 0x02: { - AC = ((sector_trailer[7] >> 4) & 0x04) | ((sector_trailer[8] >> 1) & 0x02) | - ((sector_trailer[8] >> 6) & 0x01); - break; - } - default: - return false; - } - - switch(action) { - case MfClassicActionDataRead: { - return ( - (key == MfClassicKeyA && !(AC == 0x03 || AC == 0x05 || AC == 0x07)) || - (key == MfClassicKeyB && !(AC == 0x07))); - } - case MfClassicActionDataWrite: { - return ( - (key == MfClassicKeyA && (AC == 0x00)) || - (key == MfClassicKeyB && (AC == 0x00 || AC == 0x04 || AC == 0x06 || AC == 0x03))); - } - case MfClassicActionDataInc: { - return ( - (key == MfClassicKeyA && (AC == 0x00)) || - (key == MfClassicKeyB && (AC == 0x00 || AC == 0x06))); - } - case MfClassicActionDataDec: { - return ( - (key == MfClassicKeyA && (AC == 0x00 || AC == 0x06 || AC == 0x01)) || - (key == MfClassicKeyB && (AC == 0x00 || AC == 0x06 || AC == 0x01))); - } - default: - return false; - } - - return false; -} - -static bool mf_classic_is_allowed_access( - MfClassicEmulator* emulator, - uint8_t block_num, - MfClassicKey key, - MfClassicAction action) { - if(mf_classic_is_sector_trailer(block_num)) { - return mf_classic_is_allowed_access_sector_trailer(emulator, block_num, key, action); - } else { - return mf_classic_is_allowed_access_data_block(emulator, block_num, key, action); - } -} - -bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { - UNUSED(ATQA1); - if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) { - return true; - } else if((ATQA0 == 0x01) && (ATQA1 == 0x0F) && (SAK == 0x01)) { - //skylanders support - return true; - } else if((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18)) { - return true; - } else { - return false; - } -} - -MfClassicType mf_classic_get_classic_type(int8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { - UNUSED(ATQA1); - if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) { - return MfClassicType1k; - } else if((ATQA0 == 0x01) && (ATQA1 == 0x0F) && (SAK == 0x01)) { - //skylanders support - return MfClassicType1k; - } else if((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18)) { - return MfClassicType4k; - } - return MfClassicType1k; -} - -void mf_classic_reader_add_sector( - MfClassicReader* reader, - uint8_t sector, - uint64_t key_a, - uint64_t key_b) { - furi_assert(reader); - furi_assert(sector < MF_CLASSIC_SECTORS_MAX); - furi_assert((key_a != MF_CLASSIC_NO_KEY) || (key_b != MF_CLASSIC_NO_KEY)); - - if(reader->sectors_to_read < MF_CLASSIC_SECTORS_MAX) { - reader->sector_reader[reader->sectors_to_read].key_a = key_a; - reader->sector_reader[reader->sectors_to_read].key_b = key_b; - reader->sector_reader[reader->sectors_to_read].sector_num = sector; - reader->sectors_to_read++; - } -} - -void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint8_t sector) { - furi_assert(auth_ctx); - auth_ctx->sector = sector; - auth_ctx->key_a = MF_CLASSIC_NO_KEY; - auth_ctx->key_b = MF_CLASSIC_NO_KEY; -} - -static bool mf_classic_auth( - FuriHalNfcTxRxContext* tx_rx, - uint32_t block, - uint64_t key, - MfClassicKey key_type, - Crypto1* crypto) { - bool auth_success = false; - uint32_t cuid = 0; - memset(tx_rx->tx_data, 0, sizeof(tx_rx->tx_data)); - memset(tx_rx->tx_parity, 0, sizeof(tx_rx->tx_parity)); - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - - do { - if(!furi_hal_nfc_activate_nfca(200, &cuid)) break; - if(key_type == MfClassicKeyA) { - tx_rx->tx_data[0] = MF_CLASSIC_AUTH_KEY_A_CMD; - } else { - tx_rx->tx_data[0] = MF_CLASSIC_AUTH_KEY_B_CMD; - } - tx_rx->tx_data[1] = block; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRxNoCrc; - tx_rx->tx_bits = 2 * 8; - if(!furi_hal_nfc_tx_rx(tx_rx, 6)) break; - - uint32_t nt = (uint32_t)nfc_util_bytes2num(tx_rx->rx_data, 4); - crypto1_init(crypto, key); - crypto1_word(crypto, nt ^ cuid, 0); - uint8_t nr[4] = {}; - nfc_util_num2bytes(prng_successor(DWT->CYCCNT, 32), 4, nr); - for(uint8_t i = 0; i < 4; i++) { - tx_rx->tx_data[i] = crypto1_byte(crypto, nr[i], 0) ^ nr[i]; - tx_rx->tx_parity[0] |= - (((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nr[i])) & 0x01) << (7 - i)); - } - nt = prng_successor(nt, 32); - for(uint8_t i = 4; i < 8; i++) { - nt = prng_successor(nt, 8); - tx_rx->tx_data[i] = crypto1_byte(crypto, 0x00, 0) ^ (nt & 0xff); - tx_rx->tx_parity[0] |= - (((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nt & 0xff)) & 0x01) - << (7 - i)); - } - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; - tx_rx->tx_bits = 8 * 8; - if(!furi_hal_nfc_tx_rx(tx_rx, 6)) break; - if(tx_rx->rx_bits == 32) { - crypto1_word(crypto, 0, 0); - auth_success = true; - } - } while(false); - - return auth_success; -} - -bool mf_classic_authenticate( - FuriHalNfcTxRxContext* tx_rx, - uint8_t block_num, - uint64_t key, - MfClassicKey key_type) { - furi_assert(tx_rx); - - Crypto1 crypto = {}; - bool key_found = mf_classic_auth(tx_rx, block_num, key, key_type, &crypto); - furi_hal_nfc_sleep(); - return key_found; -} - -bool mf_classic_auth_attempt( - FuriHalNfcTxRxContext* tx_rx, - MfClassicAuthContext* auth_ctx, - uint64_t key) { - furi_assert(tx_rx); - furi_assert(auth_ctx); - bool found_key = false; - 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), - key, - MfClassicKeyA, - &crypto)) { - auth_ctx->key_a = key; - found_key = true; - } - } - - if(need_halt) { - furi_hal_nfc_sleep(); - } - - if(auth_ctx->key_b == MF_CLASSIC_NO_KEY) { - // Try AUTH with key B - if(mf_classic_auth( - tx_rx, - mf_classic_get_first_block_num_of_sector(auth_ctx->sector), - key, - MfClassicKeyB, - &crypto)) { - auth_ctx->key_b = key; - found_key = true; - } - } - - return found_key; -} - -bool mf_classic_read_block( - FuriHalNfcTxRxContext* tx_rx, - Crypto1* crypto, - uint8_t block_num, - MfClassicBlock* block) { - furi_assert(tx_rx); - furi_assert(crypto); - furi_assert(block); - - bool read_block_success = false; - uint8_t plain_cmd[4] = {MF_CLASSIC_READ_SECT_CMD, block_num, 0x00, 0x00}; - nfca_append_crc16(plain_cmd, 2); - memset(tx_rx->tx_data, 0, sizeof(tx_rx->tx_data)); - memset(tx_rx->tx_parity, 0, sizeof(tx_rx->tx_parity)); - - for(uint8_t i = 0; i < 4; i++) { - tx_rx->tx_data[i] = crypto1_byte(crypto, 0x00, 0) ^ plain_cmd[i]; - tx_rx->tx_parity[0] |= - ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_cmd[i])) & 0x01) << (7 - i); - } - tx_rx->tx_bits = 4 * 9; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; - - if(furi_hal_nfc_tx_rx(tx_rx, 50)) { - if(tx_rx->rx_bits == 8 * (MF_CLASSIC_BLOCK_SIZE + 2)) { - uint8_t block_received[MF_CLASSIC_BLOCK_SIZE + 2]; - for(uint8_t i = 0; i < MF_CLASSIC_BLOCK_SIZE + 2; i++) { - block_received[i] = crypto1_byte(crypto, 0, 0) ^ tx_rx->rx_data[i]; - } - uint16_t crc_calc = nfca_get_crc16(block_received, MF_CLASSIC_BLOCK_SIZE); - uint16_t crc_received = (block_received[MF_CLASSIC_BLOCK_SIZE + 1] << 8) | - block_received[MF_CLASSIC_BLOCK_SIZE]; - if(crc_received != crc_calc) { - FURI_LOG_E( - TAG, - "Incorrect CRC while reading block %d. Expected %04X, Received %04X", - block_num, - crc_received, - crc_calc); - } else { - memcpy(block->value, block_received, MF_CLASSIC_BLOCK_SIZE); - read_block_success = true; - } - } - } - return read_block_success; -} - -void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, uint8_t sec_num) { - furi_assert(tx_rx); - furi_assert(data); - - furi_hal_nfc_sleep(); - bool key_a_found = mf_classic_is_key_found(data, sec_num, MfClassicKeyA); - bool key_b_found = mf_classic_is_key_found(data, sec_num, MfClassicKeyB); - uint8_t start_block = mf_classic_get_first_block_num_of_sector(sec_num); - uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sec_num); - MfClassicBlock block_tmp = {}; - uint64_t key = 0; - MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, sec_num); - Crypto1 crypto = {}; - - uint8_t blocks_read = 0; - do { - if(!key_a_found) break; - FURI_LOG_D(TAG, "Try to read blocks with key A"); - key = nfc_util_bytes2num(sec_tr->key_a, sizeof(sec_tr->key_a)); - if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyA, &crypto)) break; - for(size_t i = start_block; i < start_block + total_blocks; i++) { - if(!mf_classic_is_block_read(data, i)) { - if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { - mf_classic_set_block_read(data, i, &block_tmp); - blocks_read++; - } - } else { - blocks_read++; - } - } - FURI_LOG_D(TAG, "Read %d blocks out of %d", blocks_read, total_blocks); - } while(false); - do { - if(blocks_read == total_blocks) break; - if(!key_b_found) break; - FURI_LOG_D(TAG, "Try to read blocks with key B"); - key = nfc_util_bytes2num(sec_tr->key_b, sizeof(sec_tr->key_b)); - furi_hal_nfc_sleep(); - if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyB, &crypto)) break; - for(size_t i = start_block; i < start_block + total_blocks; i++) { - if(!mf_classic_is_block_read(data, i)) { - if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { - mf_classic_set_block_read(data, i, &block_tmp); - blocks_read++; - } - } else { - blocks_read++; - } - } - FURI_LOG_D(TAG, "Read %d blocks out of %d", blocks_read, total_blocks); - } while(false); -} - -static bool mf_classic_read_sector_with_reader( - FuriHalNfcTxRxContext* tx_rx, - Crypto1* crypto, - MfClassicSectorReader* sector_reader, - MfClassicSector* sector) { - furi_assert(tx_rx); - furi_assert(sector_reader); - furi_assert(sector); - - uint64_t key; - MfClassicKey key_type; - uint8_t first_block; - bool sector_read = false; - - furi_hal_nfc_sleep(); - do { - // Activate card - first_block = mf_classic_get_first_block_num_of_sector(sector_reader->sector_num); - if(sector_reader->key_a != MF_CLASSIC_NO_KEY) { - key = sector_reader->key_a; - key_type = MfClassicKeyA; - } else if(sector_reader->key_b != MF_CLASSIC_NO_KEY) { - key = sector_reader->key_b; - key_type = MfClassicKeyB; - } else { - break; - } - - // Auth to first block in sector - if(!mf_classic_auth(tx_rx, first_block, key, key_type, crypto)) break; - sector->total_blocks = mf_classic_get_blocks_num_in_sector(sector_reader->sector_num); - - // Read blocks - for(uint8_t i = 0; i < sector->total_blocks; i++) { - mf_classic_read_block(tx_rx, crypto, first_block + i, §or->block[i]); - } - // Save sector keys in last block - if(sector_reader->key_a != MF_CLASSIC_NO_KEY) { - nfc_util_num2bytes( - sector_reader->key_a, 6, §or->block[sector->total_blocks - 1].value[0]); - } - if(sector_reader->key_b != MF_CLASSIC_NO_KEY) { - nfc_util_num2bytes( - sector_reader->key_b, 6, §or->block[sector->total_blocks - 1].value[10]); - } - - sector_read = true; - } while(false); - - return sector_read; -} - -uint8_t mf_classic_read_card( - FuriHalNfcTxRxContext* tx_rx, - MfClassicReader* reader, - MfClassicData* data) { - furi_assert(tx_rx); - furi_assert(reader); - furi_assert(data); - - uint8_t sectors_read = 0; - data->type = reader->type; - data->key_a_mask = 0; - data->key_b_mask = 0; - MfClassicSector temp_sector = {}; - for(uint8_t i = 0; i < reader->sectors_to_read; i++) { - if(mf_classic_read_sector_with_reader( - tx_rx, &reader->crypto, &reader->sector_reader[i], &temp_sector)) { - uint8_t first_block = - mf_classic_get_first_block_num_of_sector(reader->sector_reader[i].sector_num); - for(uint8_t j = 0; j < temp_sector.total_blocks; j++) { - mf_classic_set_block_read(data, first_block + j, &temp_sector.block[j]); - } - if(reader->sector_reader[i].key_a != MF_CLASSIC_NO_KEY) { - mf_classic_set_key_found( - data, - reader->sector_reader[i].sector_num, - MfClassicKeyA, - reader->sector_reader[i].key_a); - } - if(reader->sector_reader[i].key_b != MF_CLASSIC_NO_KEY) { - mf_classic_set_key_found( - data, - reader->sector_reader[i].sector_num, - MfClassicKeyB, - reader->sector_reader[i].key_b); - } - sectors_read++; - } - } - - return sectors_read; -} - -uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data) { - furi_assert(tx_rx); - furi_assert(data); - - uint8_t sectors_read = 0; - Crypto1 crypto = {}; - uint8_t total_sectors = mf_classic_get_total_sectors_num(data->type); - uint64_t key_a = 0; - uint64_t key_b = 0; - MfClassicSectorReader sec_reader = {}; - MfClassicSector temp_sector = {}; - - for(size_t i = 0; i < total_sectors; i++) { - MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, i); - // Load key A - if(mf_classic_is_key_found(data, i, MfClassicKeyA)) { - sec_reader.key_a = nfc_util_bytes2num(sec_tr->key_a, 6); - } else { - sec_reader.key_a = MF_CLASSIC_NO_KEY; - } - // Load key B - if(mf_classic_is_key_found(data, i, MfClassicKeyB)) { - sec_reader.key_b = nfc_util_bytes2num(sec_tr->key_b, 6); - } else { - sec_reader.key_b = MF_CLASSIC_NO_KEY; - } - if((key_a != MF_CLASSIC_NO_KEY) || (key_b != MF_CLASSIC_NO_KEY)) { - sec_reader.sector_num = i; - if(mf_classic_read_sector_with_reader(tx_rx, &crypto, &sec_reader, &temp_sector)) { - uint8_t first_block = mf_classic_get_first_block_num_of_sector(i); - for(uint8_t j = 0; j < temp_sector.total_blocks; j++) { - mf_classic_set_block_read(data, first_block + j, &temp_sector.block[j]); - } - sectors_read++; - } - } - } - return sectors_read; -} - -void mf_crypto1_decrypt( - Crypto1* crypto, - uint8_t* encrypted_data, - uint16_t encrypted_data_bits, - uint8_t* decrypted_data) { - if(encrypted_data_bits < 8) { - uint8_t decrypted_byte = 0; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 0)) << 0; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 1)) << 1; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 2)) << 2; - decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 3)) << 3; - decrypted_data[0] = decrypted_byte; - } else { - for(size_t i = 0; i < encrypted_data_bits / 8; i++) { - decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i]; - } - } -} - -void mf_crypto1_encrypt( - Crypto1* crypto, - uint8_t* keystream, - uint8_t* plain_data, - uint16_t plain_data_bits, - uint8_t* encrypted_data, - uint8_t* encrypted_parity) { - if(plain_data_bits < 8) { - encrypted_data[0] = 0; - for(size_t i = 0; i < plain_data_bits; i++) { - encrypted_data[0] |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i; - } - } else { - memset(encrypted_parity, 0, plain_data_bits / 8 + 1); - for(uint8_t i = 0; i < plain_data_bits / 8; i++) { - encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^ - plain_data[i]; - encrypted_parity[i / 8] |= - (((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01) - << (7 - (i & 0x0007))); - } - } -} - -bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx) { - furi_assert(emulator); - furi_assert(tx_rx); - bool command_processed = false; - bool is_encrypted = false; - uint8_t plain_data[MF_CLASSIC_MAX_DATA_SIZE]; - MfClassicKey access_key = MfClassicKeyA; - - // Read command - while(!command_processed) { - if(!is_encrypted) { - crypto1_reset(&emulator->crypto); - memcpy(plain_data, tx_rx->rx_data, tx_rx->rx_bits / 8); - } else { - if(!furi_hal_nfc_tx_rx(tx_rx, 300)) { - FURI_LOG_D( - TAG, - "Error in tx rx. Tx :%d bits, Rx: %d bits", - tx_rx->tx_bits, - tx_rx->rx_bits); - break; - } - mf_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); - } - - if(plain_data[0] == 0x50 && plain_data[1] == 0x00) { - FURI_LOG_T(TAG, "Halt received"); - furi_hal_nfc_listen_sleep(); - command_processed = true; - break; - } else if(plain_data[0] == 0x60 || plain_data[0] == 0x61) { - uint8_t block = plain_data[1]; - uint64_t key = 0; - uint8_t sector_trailer_block = mf_classic_get_sector_trailer_num_by_block(block); - MfClassicSectorTrailer* sector_trailer = - (MfClassicSectorTrailer*)emulator->data.block[sector_trailer_block].value; - if(plain_data[0] == 0x60) { - key = nfc_util_bytes2num(sector_trailer->key_a, 6); - access_key = MfClassicKeyA; - } else { - key = nfc_util_bytes2num(sector_trailer->key_b, 6); - access_key = MfClassicKeyB; - } - - uint32_t nonce = prng_successor(DWT->CYCCNT, 32) ^ 0xAA; - uint8_t nt[4]; - uint8_t nt_keystream[4]; - nfc_util_num2bytes(nonce, 4, nt); - nfc_util_num2bytes(nonce ^ emulator->cuid, 4, nt_keystream); - crypto1_init(&emulator->crypto, key); - if(!is_encrypted) { - crypto1_word(&emulator->crypto, emulator->cuid ^ nonce, 0); - memcpy(tx_rx->tx_data, nt, sizeof(nt)); - tx_rx->tx_parity[0] = 0; - for(size_t i = 0; i < sizeof(nt); i++) { - tx_rx->tx_parity[0] |= nfc_util_odd_parity8(nt[i]) << (7 - i); - } - tx_rx->tx_bits = sizeof(nt) * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; - } else { - mf_crypto1_encrypt( - &emulator->crypto, - nt_keystream, - nt, - sizeof(nt) * 8, - tx_rx->tx_data, - tx_rx->tx_parity); - tx_rx->tx_bits = sizeof(nt) * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; - } - if(!furi_hal_nfc_tx_rx(tx_rx, 500)) { - FURI_LOG_E(TAG, "Error in NT exchange"); - command_processed = true; - break; - } - - if(tx_rx->rx_bits != 64) { - FURI_LOG_W(TAG, "Incorrect nr + ar"); - command_processed = true; - break; - } - - uint32_t nr = nfc_util_bytes2num(tx_rx->rx_data, 4); - uint32_t ar = nfc_util_bytes2num(&tx_rx->rx_data[4], 4); - - FURI_LOG_D( - TAG, - "%08x key%c block %d nt/nr/ar: %08x %08x %08x", - emulator->cuid, - access_key == MfClassicKeyA ? 'A' : 'B', - sector_trailer_block, - nonce, - nr, - ar); - - crypto1_word(&emulator->crypto, nr, 1); - uint32_t cardRr = ar ^ crypto1_word(&emulator->crypto, 0, 0); - if(cardRr != prng_successor(nonce, 64)) { - FURI_LOG_T(TAG, "Wrong AUTH! %08X != %08X", cardRr, prng_successor(nonce, 64)); - // Don't send NACK, as the tag doesn't send it - command_processed = true; - break; - } - - uint32_t ans = prng_successor(nonce, 96); - uint8_t responce[4] = {}; - nfc_util_num2bytes(ans, 4, responce); - mf_crypto1_encrypt( - &emulator->crypto, - NULL, - responce, - sizeof(responce) * 8, - tx_rx->tx_data, - tx_rx->tx_parity); - tx_rx->tx_bits = sizeof(responce) * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; - - is_encrypted = true; - } else if(is_encrypted && plain_data[0] == 0x30) { - uint8_t block = plain_data[1]; - uint8_t block_data[18] = {}; - memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE); - if(mf_classic_is_sector_trailer(block)) { - if(!mf_classic_is_allowed_access( - emulator, block, access_key, MfClassicActionKeyARead)) { - memset(block_data, 0, 6); - } - if(!mf_classic_is_allowed_access( - emulator, block, access_key, MfClassicActionKeyBRead)) { - memset(&block_data[10], 0, 6); - } - if(!mf_classic_is_allowed_access( - emulator, block, access_key, MfClassicActionACRead)) { - memset(&block_data[6], 0, 4); - } - } else { - if(!mf_classic_is_allowed_access( - emulator, block, access_key, MfClassicActionDataRead)) { - // Send NACK - uint8_t nack = 0x04; - if(is_encrypted) { - mf_crypto1_encrypt( - &emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); - } else { - tx_rx->tx_data[0] = nack; - } - tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; - tx_rx->tx_bits = 4; - furi_hal_nfc_tx_rx(tx_rx, 300); - break; - } - } - nfca_append_crc16(block_data, 16); - - mf_crypto1_encrypt( - &emulator->crypto, - NULL, - block_data, - sizeof(block_data) * 8, - tx_rx->tx_data, - tx_rx->tx_parity); - tx_rx->tx_bits = 18 * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; - } else if(is_encrypted && plain_data[0] == 0xA0) { - uint8_t block = plain_data[1]; - if(block > mf_classic_get_total_block_num(emulator->data.type)) { - break; - } - // Send ACK - uint8_t ack = 0x0A; - mf_crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); - tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; - tx_rx->tx_bits = 4; - - if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break; - if(tx_rx->rx_bits != 18 * 8) break; - - mf_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); - uint8_t block_data[16] = {}; - memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE); - if(mf_classic_is_sector_trailer(block)) { - if(mf_classic_is_allowed_access( - emulator, block, access_key, MfClassicActionKeyAWrite)) { - memcpy(block_data, plain_data, 6); - } - if(mf_classic_is_allowed_access( - emulator, block, access_key, MfClassicActionKeyBWrite)) { - memcpy(&block_data[10], &plain_data[10], 6); - } - if(mf_classic_is_allowed_access( - emulator, block, access_key, MfClassicActionACWrite)) { - memcpy(&block_data[6], &plain_data[6], 4); - } - } else { - if(mf_classic_is_allowed_access( - emulator, block, access_key, MfClassicActionDataWrite)) { - memcpy(block_data, plain_data, MF_CLASSIC_BLOCK_SIZE); - } - } - if(memcmp(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE)) { - memcpy(emulator->data.block[block].value, block_data, MF_CLASSIC_BLOCK_SIZE); - emulator->data_changed = true; - } - // Send ACK - ack = 0x0A; - mf_crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); - tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; - tx_rx->tx_bits = 4; - } else { - // Unknown command - break; - } - } - - if(!command_processed) { - // Send NACK - uint8_t nack = 0x04; - if(is_encrypted) { - mf_crypto1_encrypt( - &emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); - } else { - tx_rx->tx_data[0] = nack; - } - tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; - tx_rx->tx_bits = 4; - furi_hal_nfc_tx_rx(tx_rx, 300); - } - - return true; -} diff --git a/lib/nfc/protocols/mifare_classic.h b/lib/nfc/protocols/mifare_classic.h deleted file mode 100644 index b9921fb1ca5..00000000000 --- a/lib/nfc/protocols/mifare_classic.h +++ /dev/null @@ -1,143 +0,0 @@ -#pragma once - -#include - -#include "crypto1.h" - -#define MF_CLASSIC_BLOCK_SIZE (16) -#define MF_CLASSIC_TOTAL_BLOCKS_MAX (256) -#define MF_CLASSIC_1K_TOTAL_SECTORS_NUM (16) -#define MF_CLASSIC_4K_TOTAL_SECTORS_NUM (40) - -#define MF_CLASSIC_SECTORS_MAX (40) -#define MF_CLASSIC_BLOCKS_IN_SECTOR_MAX (16) - -#define MF_CLASSIC_NO_KEY (0xFFFFFFFFFFFFFFFF) -#define MF_CLASSIC_MAX_DATA_SIZE (16) -#define MF_CLASSIC_KEY_SIZE (6) -#define MF_CLASSIC_ACCESS_BYTES_SIZE (4) - -typedef enum { - MfClassicType1k, - MfClassicType4k, -} MfClassicType; - -typedef enum { - MfClassicKeyA, - MfClassicKeyB, -} MfClassicKey; - -typedef struct { - uint8_t value[MF_CLASSIC_BLOCK_SIZE]; -} MfClassicBlock; - -typedef struct { - uint8_t key_a[MF_CLASSIC_KEY_SIZE]; - uint8_t access_bits[MF_CLASSIC_ACCESS_BYTES_SIZE]; - uint8_t key_b[MF_CLASSIC_KEY_SIZE]; -} MfClassicSectorTrailer; - -typedef struct { - uint8_t total_blocks; - MfClassicBlock block[MF_CLASSIC_BLOCKS_IN_SECTOR_MAX]; -} MfClassicSector; - -typedef struct { - MfClassicType type; - uint32_t block_read_mask[MF_CLASSIC_TOTAL_BLOCKS_MAX / 32]; - uint64_t key_a_mask; - uint64_t key_b_mask; - MfClassicBlock block[MF_CLASSIC_TOTAL_BLOCKS_MAX]; -} MfClassicData; - -typedef struct { - uint8_t sector; - uint64_t key_a; - uint64_t key_b; -} MfClassicAuthContext; - -typedef struct { - uint8_t sector_num; - uint64_t key_a; - uint64_t key_b; -} MfClassicSectorReader; - -typedef struct { - MfClassicType type; - Crypto1 crypto; - uint8_t sectors_to_read; - MfClassicSectorReader sector_reader[MF_CLASSIC_SECTORS_MAX]; -} MfClassicReader; - -typedef struct { - uint32_t cuid; - Crypto1 crypto; - MfClassicData data; - bool data_changed; -} MfClassicEmulator; - -const char* mf_classic_get_type_str(MfClassicType type); - -bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); - -MfClassicType mf_classic_get_classic_type(int8_t ATQA0, uint8_t ATQA1, uint8_t SAK); - -uint8_t mf_classic_get_total_sectors_num(MfClassicType type); - -uint8_t mf_classic_get_sector_trailer_block_num_by_sector(uint8_t sector); - -bool mf_classic_is_sector_trailer(uint8_t block); - -uint8_t mf_classic_get_sector_by_block(uint8_t block); - -bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type); - -void mf_classic_set_key_found( - MfClassicData* data, - uint8_t sector_num, - MfClassicKey key_type, - uint64_t key); - -bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num); - -void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data); - -bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num); - -void mf_classic_get_read_sectors_and_keys( - MfClassicData* data, - uint8_t* sectors_read, - uint8_t* keys_found); - -MfClassicSectorTrailer* - mf_classic_get_sector_trailer_by_sector(MfClassicData* data, uint8_t sector); - -void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint8_t sector); - -bool mf_classic_authenticate( - FuriHalNfcTxRxContext* tx_rx, - uint8_t block_num, - uint64_t key, - MfClassicKey key_type); - -bool mf_classic_auth_attempt( - FuriHalNfcTxRxContext* tx_rx, - MfClassicAuthContext* auth_ctx, - uint64_t key); - -void mf_classic_reader_add_sector( - MfClassicReader* reader, - uint8_t sector, - uint64_t key_a, - uint64_t key_b); - -void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, uint8_t sec_num); - -uint8_t mf_classic_read_card( - FuriHalNfcTxRxContext* tx_rx, - MfClassicReader* reader, - MfClassicData* data); - -uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data); - -bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx); diff --git a/lib/nfc/protocols/mifare_common.c b/lib/nfc/protocols/mifare_common.c deleted file mode 100644 index 90b57e1f031..00000000000 --- a/lib/nfc/protocols/mifare_common.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "mifare_common.h" - -MifareType mifare_common_get_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { - MifareType type = MifareTypeUnknown; - - if((ATQA0 == 0x44) && (ATQA1 == 0x00) && (SAK == 0x00)) { - type = MifareTypeUltralight; - } else if( - ((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) || - ((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18)) || - ((ATQA0 == 0x01) && (ATQA1 == 0x0F) && (SAK == 0x01))) { - type = MifareTypeClassic; - } else if(ATQA0 == 0x44 && ATQA1 == 0x03 && SAK == 0x20) { - type = MifareTypeDesfire; - } - - return type; -} diff --git a/lib/nfc/protocols/mifare_common.h b/lib/nfc/protocols/mifare_common.h deleted file mode 100644 index 2b694d9068a..00000000000 --- a/lib/nfc/protocols/mifare_common.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -typedef enum { - MifareTypeUnknown, - MifareTypeUltralight, - MifareTypeClassic, - MifareTypeDesfire, -} MifareType; - -MifareType mifare_common_get_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); diff --git a/lib/nfc/protocols/mifare_desfire.c b/lib/nfc/protocols/mifare_desfire.c deleted file mode 100644 index 1822d5c1ab6..00000000000 --- a/lib/nfc/protocols/mifare_desfire.c +++ /dev/null @@ -1,623 +0,0 @@ -#include "mifare_desfire.h" -#include -#include - -#define TAG "MifareDESFire" - -void mf_df_clear(MifareDesfireData* data) { - free(data->free_memory); - if(data->master_key_settings) { - MifareDesfireKeyVersion* key_version = data->master_key_settings->key_version_head; - while(key_version) { - MifareDesfireKeyVersion* next_key_version = key_version->next; - free(key_version); - key_version = next_key_version; - } - } - free(data->master_key_settings); - MifareDesfireApplication* app = data->app_head; - while(app) { - MifareDesfireApplication* next_app = app->next; - if(app->key_settings) { - MifareDesfireKeyVersion* key_version = app->key_settings->key_version_head; - while(key_version) { - MifareDesfireKeyVersion* next_key_version = key_version->next; - free(key_version); - key_version = next_key_version; - } - } - free(app->key_settings); - MifareDesfireFile* file = app->file_head; - while(file) { - MifareDesfireFile* next_file = file->next; - free(file->contents); - free(file); - file = next_file; - } - free(app); - app = next_app; - } - data->free_memory = NULL; - data->master_key_settings = NULL; - data->app_head = NULL; -} - -void mf_df_cat_data(MifareDesfireData* data, string_t out) { - mf_df_cat_card_info(data, out); - for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { - mf_df_cat_application(app, out); - } -} - -void mf_df_cat_card_info(MifareDesfireData* data, string_t out) { - mf_df_cat_version(&data->version, out); - if(data->free_memory) { - mf_df_cat_free_mem(data->free_memory, out); - } - if(data->master_key_settings) { - mf_df_cat_key_settings(data->master_key_settings, out); - } -} - -void mf_df_cat_version(MifareDesfireVersion* version, string_t out) { - string_cat_printf( - out, - "%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", - version->uid[0], - version->uid[1], - version->uid[2], - version->uid[3], - version->uid[4], - version->uid[5], - version->uid[6]); - string_cat_printf( - out, - "hw %02x type %02x sub %02x\n" - " maj %02x min %02x\n" - " size %02x proto %02x\n", - version->hw_vendor, - version->hw_type, - version->hw_subtype, - version->hw_major, - version->hw_minor, - version->hw_storage, - version->hw_proto); - string_cat_printf( - out, - "sw %02x type %02x sub %02x\n" - " maj %02x min %02x\n" - " size %02x proto %02x\n", - version->sw_vendor, - version->sw_type, - version->sw_subtype, - version->sw_major, - version->sw_minor, - version->sw_storage, - version->sw_proto); - string_cat_printf( - out, - "batch %02x:%02x:%02x:%02x:%02x\n" - "week %d year %d\n", - version->batch[0], - version->batch[1], - version->batch[2], - version->batch[3], - version->batch[4], - version->prod_week, - version->prod_year); -} - -void mf_df_cat_free_mem(MifareDesfireFreeMemory* free_mem, string_t out) { - string_cat_printf(out, "freeMem %d\n", free_mem->bytes); -} - -void mf_df_cat_key_settings(MifareDesfireKeySettings* ks, string_t out) { - string_cat_printf(out, "changeKeyID %d\n", ks->change_key_id); - string_cat_printf(out, "configChangeable %d\n", ks->config_changeable); - string_cat_printf(out, "freeCreateDelete %d\n", ks->free_create_delete); - string_cat_printf(out, "freeDirectoryList %d\n", ks->free_directory_list); - string_cat_printf(out, "masterChangeable %d\n", ks->master_key_changeable); - if(ks->flags) { - string_cat_printf(out, "flags %d\n", ks->flags); - } - string_cat_printf(out, "maxKeys %d\n", ks->max_keys); - for(MifareDesfireKeyVersion* kv = ks->key_version_head; kv; kv = kv->next) { - string_cat_printf(out, "key %d version %d\n", kv->id, kv->version); - } -} - -void mf_df_cat_application_info(MifareDesfireApplication* app, string_t out) { - string_cat_printf(out, "Application %02x%02x%02x\n", app->id[0], app->id[1], app->id[2]); - if(app->key_settings) { - mf_df_cat_key_settings(app->key_settings, out); - } -} - -void mf_df_cat_application(MifareDesfireApplication* app, string_t out) { - mf_df_cat_application_info(app, out); - for(MifareDesfireFile* file = app->file_head; file; file = file->next) { - mf_df_cat_file(file, out); - } -} - -void mf_df_cat_file(MifareDesfireFile* file, string_t out) { - char* type = "unknown"; - switch(file->type) { - case MifareDesfireFileTypeStandard: - type = "standard"; - break; - case MifareDesfireFileTypeBackup: - type = "backup"; - break; - case MifareDesfireFileTypeValue: - type = "value"; - break; - case MifareDesfireFileTypeLinearRecord: - type = "linear"; - break; - case MifareDesfireFileTypeCyclicRecord: - type = "cyclic"; - break; - } - char* comm = "unknown"; - switch(file->comm) { - case MifareDesfireFileCommunicationSettingsPlaintext: - comm = "plain"; - break; - case MifareDesfireFileCommunicationSettingsAuthenticated: - comm = "auth"; - break; - case MifareDesfireFileCommunicationSettingsEnciphered: - comm = "enciphered"; - break; - } - string_cat_printf(out, "File %d\n", file->id); - string_cat_printf(out, "%s %s\n", type, comm); - string_cat_printf( - out, - "r %d w %d rw %d c %d\n", - file->access_rights >> 12 & 0xF, - file->access_rights >> 8 & 0xF, - file->access_rights >> 4 & 0xF, - file->access_rights & 0xF); - uint16_t size = 0; - uint16_t num = 1; - switch(file->type) { - case MifareDesfireFileTypeStandard: - case MifareDesfireFileTypeBackup: - size = file->settings.data.size; - string_cat_printf(out, "size %d\n", size); - break; - case MifareDesfireFileTypeValue: - size = 4; - string_cat_printf( - out, "lo %d hi %d\n", file->settings.value.lo_limit, file->settings.value.hi_limit); - string_cat_printf( - out, - "limit %d enabled %d\n", - file->settings.value.limited_credit_value, - file->settings.value.limited_credit_enabled); - break; - case MifareDesfireFileTypeLinearRecord: - case MifareDesfireFileTypeCyclicRecord: - size = file->settings.record.size; - num = file->settings.record.cur; - string_cat_printf(out, "size %d\n", size); - string_cat_printf(out, "num %d max %d\n", num, file->settings.record.max); - break; - } - uint8_t* data = file->contents; - if(data) { - for(int rec = 0; rec < num; rec++) { - for(int ch = 0; ch < size; ch++) { - string_cat_printf(out, "%02x", data[rec * size + ch]); - } - string_cat_printf(out, " \n"); - } - } -} - -bool mf_df_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { - return ATQA0 == 0x44 && ATQA1 == 0x03 && SAK == 0x20; -} - -uint16_t mf_df_prepare_get_version(uint8_t* dest) { - dest[0] = MF_DF_GET_VERSION; - return 1; -} - -bool mf_df_parse_get_version_response(uint8_t* buf, uint16_t len, MifareDesfireVersion* out) { - if(len < 1 || *buf) { - return false; - } - len--; - buf++; - if(len < sizeof(MifareDesfireVersion)) { - return false; - } - memcpy(out, buf, sizeof(MifareDesfireVersion)); - return true; -} - -uint16_t mf_df_prepare_get_free_memory(uint8_t* dest) { - dest[0] = MF_DF_GET_FREE_MEMORY; - return 1; -} - -bool mf_df_parse_get_free_memory_response(uint8_t* buf, uint16_t len, MifareDesfireFreeMemory* out) { - if(len < 1 || *buf) { - return false; - } - len--; - buf++; - if(len != 3) { - return false; - } - out->bytes = buf[0] | (buf[1] << 8) | (buf[2] << 16); - return true; -} - -uint16_t mf_df_prepare_get_key_settings(uint8_t* dest) { - dest[0] = MF_DF_GET_KEY_SETTINGS; - return 1; -} - -bool mf_df_parse_get_key_settings_response( - uint8_t* buf, - uint16_t len, - MifareDesfireKeySettings* out) { - if(len < 1 || *buf) { - return false; - } - len--; - buf++; - if(len < 2) { - return false; - } - out->change_key_id = buf[0] >> 4; - out->config_changeable = (buf[0] & 0x8) != 0; - out->free_create_delete = (buf[0] & 0x4) != 0; - out->free_directory_list = (buf[0] & 0x2) != 0; - out->master_key_changeable = (buf[0] & 0x1) != 0; - out->flags = buf[1] >> 4; - out->max_keys = buf[1] & 0xF; - return true; -} - -uint16_t mf_df_prepare_get_key_version(uint8_t* dest, uint8_t key_id) { - dest[0] = MF_DF_GET_KEY_VERSION; - dest[1] = key_id; - return 2; -} - -bool mf_df_parse_get_key_version_response(uint8_t* buf, uint16_t len, MifareDesfireKeyVersion* out) { - if(len != 2 || *buf) { - return false; - } - out->version = buf[1]; - return true; -} - -uint16_t mf_df_prepare_get_application_ids(uint8_t* dest) { - dest[0] = MF_DF_GET_APPLICATION_IDS; - return 1; -} - -bool mf_df_parse_get_application_ids_response( - uint8_t* buf, - uint16_t len, - MifareDesfireApplication** app_head) { - if(len < 1 || *buf) { - return false; - } - len--; - buf++; - if(len % 3 != 0) { - return false; - } - while(len) { - MifareDesfireApplication* app = malloc(sizeof(MifareDesfireApplication)); - memset(app, 0, sizeof(MifareDesfireApplication)); - memcpy(app->id, buf, 3); - len -= 3; - buf += 3; - *app_head = app; - app_head = &app->next; - } - return true; -} - -uint16_t mf_df_prepare_select_application(uint8_t* dest, uint8_t id[3]) { - dest[0] = MF_DF_SELECT_APPLICATION; - dest[1] = id[0]; - dest[2] = id[1]; - dest[3] = id[2]; - return 4; -} - -bool mf_df_parse_select_application_response(uint8_t* buf, uint16_t len) { - return len == 1 && !*buf; -} - -uint16_t mf_df_prepare_get_file_ids(uint8_t* dest) { - dest[0] = MF_DF_GET_FILE_IDS; - return 1; -} - -bool mf_df_parse_get_file_ids_response(uint8_t* buf, uint16_t len, MifareDesfireFile** file_head) { - if(len < 1 || *buf) { - return false; - } - len--; - buf++; - while(len) { - MifareDesfireFile* file = malloc(sizeof(MifareDesfireFile)); - memset(file, 0, sizeof(MifareDesfireFile)); - file->id = *buf; - len--; - buf++; - *file_head = file; - file_head = &file->next; - } - return true; -} - -uint16_t mf_df_prepare_get_file_settings(uint8_t* dest, uint8_t file_id) { - dest[0] = MF_DF_GET_FILE_SETTINGS; - dest[1] = file_id; - return 2; -} - -bool mf_df_parse_get_file_settings_response(uint8_t* buf, uint16_t len, MifareDesfireFile* out) { - if(len < 5 || *buf) { - return false; - } - len--; - buf++; - out->type = buf[0]; - out->comm = buf[1]; - out->access_rights = buf[2] | (buf[3] << 8); - switch(out->type) { - case MifareDesfireFileTypeStandard: - case MifareDesfireFileTypeBackup: - if(len != 7) { - return false; - } - out->settings.data.size = buf[4] | (buf[5] << 8) | (buf[6] << 16); - break; - case MifareDesfireFileTypeValue: - if(len != 17) { - return false; - } - out->settings.value.lo_limit = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24); - out->settings.value.hi_limit = buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24); - out->settings.value.limited_credit_value = buf[12] | (buf[13] << 8) | (buf[14] << 16) | - (buf[15] << 24); - out->settings.value.limited_credit_enabled = buf[16]; - break; - case MifareDesfireFileTypeLinearRecord: - case MifareDesfireFileTypeCyclicRecord: - if(len != 13) { - return false; - } - out->settings.record.size = buf[4] | (buf[5] << 8) | (buf[6] << 16); - out->settings.record.max = buf[7] | (buf[8] << 8) | (buf[9] << 16); - out->settings.record.cur = buf[10] | (buf[11] << 8) | (buf[12] << 16); - break; - default: - return false; - } - return true; -} - -uint16_t mf_df_prepare_read_data(uint8_t* dest, uint8_t file_id, uint32_t offset, uint32_t len) { - dest[0] = MF_DF_READ_DATA; - dest[1] = file_id; - dest[2] = offset; - dest[3] = offset >> 8; - dest[4] = offset >> 16; - dest[5] = len; - dest[6] = len >> 8; - dest[7] = len >> 16; - return 8; -} - -uint16_t mf_df_prepare_get_value(uint8_t* dest, uint8_t file_id) { - dest[0] = MF_DF_GET_VALUE; - dest[1] = file_id; - return 2; -} - -uint16_t - mf_df_prepare_read_records(uint8_t* dest, uint8_t file_id, uint32_t offset, uint32_t len) { - dest[0] = MF_DF_READ_RECORDS; - dest[1] = file_id; - dest[2] = offset; - dest[3] = offset >> 8; - dest[4] = offset >> 16; - dest[5] = len; - dest[6] = len >> 8; - dest[7] = len >> 16; - return 8; -} - -bool mf_df_parse_read_data_response(uint8_t* buf, uint16_t len, MifareDesfireFile* out) { - if(len < 1 || *buf) { - return false; - } - len--; - buf++; - out->contents = malloc(len); - memcpy(out->contents, buf, len); - return true; -} - -bool mf_df_read_card(FuriHalNfcTxRxContext* tx_rx, MifareDesfireData* data) { - furi_assert(tx_rx); - furi_assert(data); - - bool card_read = false; - do { - // Get version - tx_rx->tx_bits = 8 * mf_df_prepare_get_version(tx_rx->tx_data); - if(!furi_hal_nfc_tx_rx_full(tx_rx)) { - FURI_LOG_W(TAG, "Bad exchange getting version"); - break; - } - if(!mf_df_parse_get_version_response(tx_rx->rx_data, tx_rx->rx_bits / 8, &data->version)) { - FURI_LOG_W(TAG, "Bad DESFire GET_VERSION responce"); - } - - // Get free memory - tx_rx->tx_bits = 8 * mf_df_prepare_get_free_memory(tx_rx->tx_data); - if(furi_hal_nfc_tx_rx_full(tx_rx)) { - data->free_memory = malloc(sizeof(MifareDesfireFreeMemory)); - if(!mf_df_parse_get_free_memory_response( - tx_rx->rx_data, tx_rx->rx_bits / 8, data->free_memory)) { - FURI_LOG_D(TAG, "Bad DESFire GET_FREE_MEMORY response (normal for pre-EV1 cards)"); - free(data->free_memory); - data->free_memory = NULL; - } - } - - // Get key settings - tx_rx->tx_bits = 8 * mf_df_prepare_get_key_settings(tx_rx->tx_data); - if(!furi_hal_nfc_tx_rx_full(tx_rx)) { - FURI_LOG_D(TAG, "Bad exchange getting key settings"); - } else { - data->master_key_settings = malloc(sizeof(MifareDesfireKeySettings)); - if(!mf_df_parse_get_key_settings_response( - tx_rx->rx_data, tx_rx->rx_bits / 8, data->master_key_settings)) { - FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response"); - free(data->master_key_settings); - data->master_key_settings = NULL; - } else { - MifareDesfireKeyVersion** key_version_head = - &data->master_key_settings->key_version_head; - for(uint8_t key_id = 0; key_id < data->master_key_settings->max_keys; key_id++) { - tx_rx->tx_bits = 8 * mf_df_prepare_get_key_version(tx_rx->tx_data, key_id); - if(!furi_hal_nfc_tx_rx_full(tx_rx)) { - FURI_LOG_W(TAG, "Bad exchange getting key version"); - continue; - } - MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion)); - memset(key_version, 0, sizeof(MifareDesfireKeyVersion)); - key_version->id = key_id; - if(!mf_df_parse_get_key_version_response( - tx_rx->rx_data, tx_rx->rx_bits / 8, key_version)) { - FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response"); - free(key_version); - continue; - } - *key_version_head = key_version; - key_version_head = &key_version->next; - } - } - } - - // Get application IDs - tx_rx->tx_bits = 8 * mf_df_prepare_get_application_ids(tx_rx->tx_data); - if(!furi_hal_nfc_tx_rx_full(tx_rx)) { - FURI_LOG_W(TAG, "Bad exchange getting application IDs"); - break; - } else { - if(!mf_df_parse_get_application_ids_response( - tx_rx->rx_data, tx_rx->rx_bits / 8, &data->app_head)) { - FURI_LOG_W(TAG, "Bad DESFire GET_APPLICATION_IDS response"); - break; - } - } - - for(MifareDesfireApplication* app = data->app_head; app; app = app->next) { - tx_rx->tx_bits = 8 * mf_df_prepare_select_application(tx_rx->tx_data, app->id); - if(!furi_hal_nfc_tx_rx_full(tx_rx) || - !mf_df_parse_select_application_response(tx_rx->rx_data, tx_rx->rx_bits / 8)) { - FURI_LOG_W(TAG, "Bad exchange selecting application"); - continue; - } - tx_rx->tx_bits = 8 * mf_df_prepare_get_key_settings(tx_rx->tx_data); - if(!furi_hal_nfc_tx_rx_full(tx_rx)) { - FURI_LOG_W(TAG, "Bad exchange getting key settings"); - } else { - app->key_settings = malloc(sizeof(MifareDesfireKeySettings)); - memset(app->key_settings, 0, sizeof(MifareDesfireKeySettings)); - if(!mf_df_parse_get_key_settings_response( - tx_rx->rx_data, tx_rx->rx_bits / 8, app->key_settings)) { - FURI_LOG_W(TAG, "Bad DESFire GET_KEY_SETTINGS response"); - free(app->key_settings); - app->key_settings = NULL; - continue; - } - - MifareDesfireKeyVersion** key_version_head = &app->key_settings->key_version_head; - for(uint8_t key_id = 0; key_id < app->key_settings->max_keys; key_id++) { - tx_rx->tx_bits = 8 * mf_df_prepare_get_key_version(tx_rx->tx_data, key_id); - if(!furi_hal_nfc_tx_rx_full(tx_rx)) { - FURI_LOG_W(TAG, "Bad exchange getting key version"); - continue; - } - MifareDesfireKeyVersion* key_version = malloc(sizeof(MifareDesfireKeyVersion)); - memset(key_version, 0, sizeof(MifareDesfireKeyVersion)); - key_version->id = key_id; - if(!mf_df_parse_get_key_version_response( - tx_rx->rx_data, tx_rx->rx_bits / 8, key_version)) { - FURI_LOG_W(TAG, "Bad DESFire GET_KEY_VERSION response"); - free(key_version); - continue; - } - *key_version_head = key_version; - key_version_head = &key_version->next; - } - } - - tx_rx->tx_bits = 8 * mf_df_prepare_get_file_ids(tx_rx->tx_data); - if(!furi_hal_nfc_tx_rx_full(tx_rx)) { - FURI_LOG_W(TAG, "Bad exchange getting file IDs"); - } else { - if(!mf_df_parse_get_file_ids_response( - tx_rx->rx_data, tx_rx->rx_bits / 8, &app->file_head)) { - FURI_LOG_W(TAG, "Bad DESFire GET_FILE_IDS response"); - } - } - - for(MifareDesfireFile* file = app->file_head; file; file = file->next) { - tx_rx->tx_bits = 8 * mf_df_prepare_get_file_settings(tx_rx->tx_data, file->id); - if(!furi_hal_nfc_tx_rx_full(tx_rx)) { - FURI_LOG_W(TAG, "Bad exchange getting file settings"); - continue; - } - if(!mf_df_parse_get_file_settings_response( - tx_rx->rx_data, tx_rx->rx_bits / 8, file)) { - FURI_LOG_W(TAG, "Bad DESFire GET_FILE_SETTINGS response"); - continue; - } - switch(file->type) { - case MifareDesfireFileTypeStandard: - case MifareDesfireFileTypeBackup: - tx_rx->tx_bits = 8 * mf_df_prepare_read_data(tx_rx->tx_data, file->id, 0, 0); - break; - case MifareDesfireFileTypeValue: - tx_rx->tx_bits = 8 * mf_df_prepare_get_value(tx_rx->tx_data, file->id); - break; - case MifareDesfireFileTypeLinearRecord: - case MifareDesfireFileTypeCyclicRecord: - tx_rx->tx_bits = - 8 * mf_df_prepare_read_records(tx_rx->tx_data, file->id, 0, 0); - break; - } - if(!furi_hal_nfc_tx_rx_full(tx_rx)) { - FURI_LOG_W(TAG, "Bad exchange reading file %d", file->id); - continue; - } - if(!mf_df_parse_read_data_response(tx_rx->rx_data, tx_rx->rx_bits / 8, file)) { - FURI_LOG_W(TAG, "Bad response reading file %d", file->id); - continue; - } - } - } - - card_read = true; - } while(false); - - return card_read; -} diff --git a/lib/nfc/protocols/mifare_desfire.h b/lib/nfc/protocols/mifare_desfire.h deleted file mode 100644 index e59743a2fed..00000000000 --- a/lib/nfc/protocols/mifare_desfire.h +++ /dev/null @@ -1,169 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -#define MF_DF_GET_VERSION (0x60) -#define MF_DF_GET_FREE_MEMORY (0x6E) -#define MF_DF_GET_KEY_SETTINGS (0x45) -#define MF_DF_GET_KEY_VERSION (0x64) -#define MF_DF_GET_APPLICATION_IDS (0x6A) -#define MF_DF_SELECT_APPLICATION (0x5A) -#define MF_DF_GET_FILE_IDS (0x6F) -#define MF_DF_GET_FILE_SETTINGS (0xF5) - -#define MF_DF_READ_DATA (0xBD) -#define MF_DF_GET_VALUE (0x6C) -#define MF_DF_READ_RECORDS (0xBB) - -typedef struct { - uint8_t hw_vendor; - uint8_t hw_type; - uint8_t hw_subtype; - uint8_t hw_major; - uint8_t hw_minor; - uint8_t hw_storage; - uint8_t hw_proto; - - uint8_t sw_vendor; - uint8_t sw_type; - uint8_t sw_subtype; - uint8_t sw_major; - uint8_t sw_minor; - uint8_t sw_storage; - uint8_t sw_proto; - - uint8_t uid[7]; - uint8_t batch[5]; - uint8_t prod_week; - uint8_t prod_year; -} MifareDesfireVersion; - -typedef struct { - uint32_t bytes; -} MifareDesfireFreeMemory; // EV1+ only - -typedef struct MifareDesfireKeyVersion { - uint8_t id; - uint8_t version; - struct MifareDesfireKeyVersion* next; -} MifareDesfireKeyVersion; - -typedef struct { - uint8_t change_key_id; - bool config_changeable; - bool free_create_delete; - bool free_directory_list; - bool master_key_changeable; - uint8_t flags; - uint8_t max_keys; - MifareDesfireKeyVersion* key_version_head; -} MifareDesfireKeySettings; - -typedef enum { - MifareDesfireFileTypeStandard = 0, - MifareDesfireFileTypeBackup = 1, - MifareDesfireFileTypeValue = 2, - MifareDesfireFileTypeLinearRecord = 3, - MifareDesfireFileTypeCyclicRecord = 4, -} MifareDesfireFileType; - -typedef enum { - MifareDesfireFileCommunicationSettingsPlaintext = 0, - MifareDesfireFileCommunicationSettingsAuthenticated = 1, - MifareDesfireFileCommunicationSettingsEnciphered = 3, -} MifareDesfireFileCommunicationSettings; - -typedef struct MifareDesfireFile { - uint8_t id; - MifareDesfireFileType type; - MifareDesfireFileCommunicationSettings comm; - uint16_t access_rights; - union { - struct { - uint32_t size; - } data; - struct { - uint32_t lo_limit; - uint32_t hi_limit; - uint32_t limited_credit_value; - bool limited_credit_enabled; - } value; - struct { - uint32_t size; - uint32_t max; - uint32_t cur; - } record; - } settings; - uint8_t* contents; - - struct MifareDesfireFile* next; -} MifareDesfireFile; - -typedef struct MifareDesfireApplication { - uint8_t id[3]; - MifareDesfireKeySettings* key_settings; - MifareDesfireFile* file_head; - - struct MifareDesfireApplication* next; -} MifareDesfireApplication; - -typedef struct { - MifareDesfireVersion version; - MifareDesfireFreeMemory* free_memory; - MifareDesfireKeySettings* master_key_settings; - MifareDesfireApplication* app_head; -} MifareDesfireData; - -void mf_df_clear(MifareDesfireData* data); - -void mf_df_cat_data(MifareDesfireData* data, string_t out); -void mf_df_cat_card_info(MifareDesfireData* data, string_t out); -void mf_df_cat_version(MifareDesfireVersion* version, string_t out); -void mf_df_cat_free_mem(MifareDesfireFreeMemory* free_mem, string_t out); -void mf_df_cat_key_settings(MifareDesfireKeySettings* ks, string_t out); -void mf_df_cat_application_info(MifareDesfireApplication* app, string_t out); -void mf_df_cat_application(MifareDesfireApplication* app, string_t out); -void mf_df_cat_file(MifareDesfireFile* file, string_t out); - -bool mf_df_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); - -uint16_t mf_df_prepare_get_version(uint8_t* dest); -bool mf_df_parse_get_version_response(uint8_t* buf, uint16_t len, MifareDesfireVersion* out); - -uint16_t mf_df_prepare_get_free_memory(uint8_t* dest); -bool mf_df_parse_get_free_memory_response(uint8_t* buf, uint16_t len, MifareDesfireFreeMemory* out); - -uint16_t mf_df_prepare_get_key_settings(uint8_t* dest); -bool mf_df_parse_get_key_settings_response( - uint8_t* buf, - uint16_t len, - MifareDesfireKeySettings* out); - -uint16_t mf_df_prepare_get_key_version(uint8_t* dest, uint8_t key_id); -bool mf_df_parse_get_key_version_response(uint8_t* buf, uint16_t len, MifareDesfireKeyVersion* out); - -uint16_t mf_df_prepare_get_application_ids(uint8_t* dest); -bool mf_df_parse_get_application_ids_response( - uint8_t* buf, - uint16_t len, - MifareDesfireApplication** app_head); - -uint16_t mf_df_prepare_select_application(uint8_t* dest, uint8_t id[3]); -bool mf_df_parse_select_application_response(uint8_t* buf, uint16_t len); - -uint16_t mf_df_prepare_get_file_ids(uint8_t* dest); -bool mf_df_parse_get_file_ids_response(uint8_t* buf, uint16_t len, MifareDesfireFile** file_head); - -uint16_t mf_df_prepare_get_file_settings(uint8_t* dest, uint8_t file_id); -bool mf_df_parse_get_file_settings_response(uint8_t* buf, uint16_t len, MifareDesfireFile* out); - -uint16_t mf_df_prepare_read_data(uint8_t* dest, uint8_t file_id, uint32_t offset, uint32_t len); -uint16_t mf_df_prepare_get_value(uint8_t* dest, uint8_t file_id); -uint16_t mf_df_prepare_read_records(uint8_t* dest, uint8_t file_id, uint32_t offset, uint32_t len); -bool mf_df_parse_read_data_response(uint8_t* buf, uint16_t len, MifareDesfireFile* out); - -bool mf_df_read_card(FuriHalNfcTxRxContext* tx_rx, MifareDesfireData* data); diff --git a/lib/nfc/protocols/mifare_ultralight.c b/lib/nfc/protocols/mifare_ultralight.c deleted file mode 100644 index f637d378a70..00000000000 --- a/lib/nfc/protocols/mifare_ultralight.c +++ /dev/null @@ -1,1868 +0,0 @@ -#include -#include -#include "mifare_ultralight.h" -#include "nfc_util.h" -#include -#include "furi_hal_nfc.h" -#include - -#define TAG "MfUltralight" - -// Algorithms from: https://github.com/RfidResearchGroup/proxmark3/blob/0f6061c16f072372b7d4d381911f1542afbc3a69/common/generator.c#L110 -uint32_t mf_ul_pwdgen_xiaomi(FuriHalNfcDevData* data) { - uint8_t hash[20]; - mbedtls_sha1(data->uid, data->uid_len, hash); - - uint32_t pwd = 0; - pwd |= (hash[hash[0] % 20]) << 24; - pwd |= (hash[(hash[0] + 5) % 20]) << 16; - pwd |= (hash[(hash[0] + 13) % 20]) << 8; - pwd |= (hash[(hash[0] + 17) % 20]); - - return pwd; -} - -uint32_t mf_ul_pwdgen_amiibo(FuriHalNfcDevData* data) { - uint8_t* uid = data->uid; - - uint32_t pwd = 0; - pwd |= (uid[1] ^ uid[3] ^ 0xAA) << 24; - pwd |= (uid[2] ^ uid[4] ^ 0x55) << 16; - pwd |= (uid[3] ^ uid[5] ^ 0xAA) << 8; - pwd |= uid[4] ^ uid[6] ^ 0x55; - - return pwd; -} - -bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { - if((ATQA0 == 0x44) && (ATQA1 == 0x00) && (SAK == 0x00)) { - return true; - } - return false; -} - -void mf_ul_reset(MfUltralightData* data) { - furi_assert(data); - data->type = MfUltralightTypeUnknown; - memset(&data->version, 0, sizeof(MfUltralightVersion)); - memset(data->signature, 0, sizeof(data->signature)); - memset(data->counter, 0, sizeof(data->counter)); - memset(data->tearing, 0, sizeof(data->tearing)); - memset(data->data, 0, sizeof(data->data)); - data->data_size = 0; - data->data_read = 0; - data->curr_authlim = 0; - data->has_auth = false; -} - -static MfUltralightFeatures mf_ul_get_features(MfUltralightType type) { - switch(type) { - case MfUltralightTypeUL11: - case MfUltralightTypeUL21: - return MfUltralightSupportFastRead | MfUltralightSupportCompatWrite | - MfUltralightSupportReadCounter | MfUltralightSupportIncrCounter | - MfUltralightSupportAuth | MfUltralightSupportSignature | - MfUltralightSupportTearingFlags | MfUltralightSupportVcsl; - case MfUltralightTypeNTAG213: - case MfUltralightTypeNTAG215: - case MfUltralightTypeNTAG216: - return MfUltralightSupportFastRead | MfUltralightSupportCompatWrite | - MfUltralightSupportReadCounter | MfUltralightSupportAuth | - MfUltralightSupportSignature | MfUltralightSupportSingleCounter | - MfUltralightSupportAsciiMirror; - case MfUltralightTypeNTAGI2C1K: - case MfUltralightTypeNTAGI2C2K: - return MfUltralightSupportFastRead | MfUltralightSupportSectorSelect; - case MfUltralightTypeNTAGI2CPlus1K: - case MfUltralightTypeNTAGI2CPlus2K: - return MfUltralightSupportFastRead | MfUltralightSupportAuth | - MfUltralightSupportFastWrite | MfUltralightSupportSignature | - MfUltralightSupportSectorSelect; - case MfUltralightTypeNTAG203: - return MfUltralightSupportCompatWrite | MfUltralightSupportCounterInMemory; - default: - // Assumed original MFUL 512-bit - return MfUltralightSupportCompatWrite; - } -} - -static void mf_ul_set_default_version(MfUltralightReader* reader, MfUltralightData* data) { - data->type = MfUltralightTypeUnknown; - reader->pages_to_read = 16; -} - -static void mf_ul_set_version_ntag203(MfUltralightReader* reader, MfUltralightData* data) { - data->type = MfUltralightTypeNTAG203; - reader->pages_to_read = 42; -} - -bool mf_ultralight_read_version( - FuriHalNfcTxRxContext* tx_rx, - MfUltralightReader* reader, - MfUltralightData* data) { - bool version_read = false; - - do { - FURI_LOG_D(TAG, "Reading version"); - tx_rx->tx_data[0] = MF_UL_GET_VERSION_CMD; - tx_rx->tx_bits = 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - if(!furi_hal_nfc_tx_rx(tx_rx, 50) || tx_rx->rx_bits != 64) { - FURI_LOG_D(TAG, "Failed reading version"); - mf_ul_set_default_version(reader, data); - furi_hal_nfc_sleep(); - furi_hal_nfc_activate_nfca(300, NULL); - break; - } - MfUltralightVersion* version = (MfUltralightVersion*)tx_rx->rx_data; - data->version = *version; - if(version->storage_size == 0x0B || version->storage_size == 0x00) { - data->type = MfUltralightTypeUL11; - reader->pages_to_read = 20; - } else if(version->storage_size == 0x0E) { - data->type = MfUltralightTypeUL21; - reader->pages_to_read = 41; - } else if(version->storage_size == 0x0F) { - data->type = MfUltralightTypeNTAG213; - reader->pages_to_read = 45; - } else if(version->storage_size == 0x11) { - data->type = MfUltralightTypeNTAG215; - reader->pages_to_read = 135; - } else if(version->prod_subtype == 5 && version->prod_ver_major == 2) { - // NTAG I2C - bool known = false; - if(version->prod_ver_minor == 1) { - if(version->storage_size == 0x13) { - data->type = MfUltralightTypeNTAGI2C1K; - reader->pages_to_read = 231; - known = true; - } else if(version->storage_size == 0x15) { - data->type = MfUltralightTypeNTAGI2C2K; - reader->pages_to_read = 485; - known = true; - } - } else if(version->prod_ver_minor == 2) { - if(version->storage_size == 0x13) { - data->type = MfUltralightTypeNTAGI2CPlus1K; - reader->pages_to_read = 236; - known = true; - } else if(version->storage_size == 0x15) { - data->type = MfUltralightTypeNTAGI2CPlus2K; - reader->pages_to_read = 492; - known = true; - } - } - - if(!known) { - mf_ul_set_default_version(reader, data); - } - } else if(version->storage_size == 0x13) { - data->type = MfUltralightTypeNTAG216; - reader->pages_to_read = 231; - } else { - mf_ul_set_default_version(reader, data); - break; - } - version_read = true; - } while(false); - - reader->supported_features = mf_ul_get_features(data->type); - return version_read; -} - -bool mf_ultralight_authenticate(FuriHalNfcTxRxContext* tx_rx, uint32_t key, uint16_t* pack) { - bool authenticated = false; - - do { - FURI_LOG_D(TAG, "Authenticating"); - tx_rx->tx_data[0] = MF_UL_AUTH; - nfc_util_num2bytes(key, 4, &tx_rx->tx_data[1]); - tx_rx->tx_bits = 40; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - if(!furi_hal_nfc_tx_rx(tx_rx, 50)) { - FURI_LOG_D(TAG, "Tag did not respond to authentication"); - break; - } - - // PACK - if(tx_rx->rx_bits < 2 * 8) { - FURI_LOG_D(TAG, "Authentication failed"); - break; - } - - if(pack != NULL) { - *pack = (tx_rx->rx_data[1] << 8) | tx_rx->rx_data[0]; - } - - FURI_LOG_I(TAG, "Auth success. Password: %08X. PACK: %04X", key, *pack); - authenticated = true; - } while(false); - - return authenticated; -} - -static int16_t mf_ultralight_page_addr_to_tag_addr(uint8_t sector, uint8_t page) { - return sector * 256 + page; -} - -static int16_t mf_ultralight_ntag_i2c_addr_lin_to_tag_1k( - int16_t linear_address, - uint8_t* sector, - int16_t* valid_pages) { - // 0 - 226: sector 0 - // 227 - 228: config registers - // 229 - 230: session registers - - if(linear_address > 230) { - *valid_pages = 0; - return -1; - } else if(linear_address >= 229) { - *sector = 3; - *valid_pages = 2 - (linear_address - 229); - return linear_address - 229 + 248; - } else if(linear_address >= 227) { - *sector = 0; - *valid_pages = 2 - (linear_address - 227); - return linear_address - 227 + 232; - } else { - *sector = 0; - *valid_pages = 227 - linear_address; - return linear_address; - } -} - -static int16_t mf_ultralight_ntag_i2c_addr_lin_to_tag_2k( - int16_t linear_address, - uint8_t* sector, - int16_t* valid_pages) { - // 0 - 255: sector 0 - // 256 - 480: sector 1 - // 481 - 482: config registers - // 483 - 484: session registers - - if(linear_address > 484) { - *valid_pages = 0; - return -1; - } else if(linear_address >= 483) { - *sector = 3; - *valid_pages = 2 - (linear_address - 483); - return linear_address - 483 + 248; - } else if(linear_address >= 481) { - *sector = 1; - *valid_pages = 2 - (linear_address - 481); - return linear_address - 481 + 232; - } else if(linear_address >= 256) { - *sector = 1; - *valid_pages = 225 - (linear_address - 256); - return linear_address - 256; - } else { - *sector = 0; - *valid_pages = 256 - linear_address; - return linear_address; - } -} - -static int16_t mf_ultralight_ntag_i2c_addr_lin_to_tag_plus_1k( - int16_t linear_address, - uint8_t* sector, - int16_t* valid_pages) { - // 0 - 233: sector 0 + registers - // 234 - 235: session registers - - if(linear_address > 235) { - *valid_pages = 0; - return -1; - } else if(linear_address >= 234) { - *sector = 0; - *valid_pages = 2 - (linear_address - 234); - return linear_address - 234 + 236; - } else { - *sector = 0; - *valid_pages = 234 - linear_address; - return linear_address; - } -} - -static int16_t mf_ultralight_ntag_i2c_addr_lin_to_tag_plus_2k( - int16_t linear_address, - uint8_t* sector, - int16_t* valid_pages) { - // 0 - 233: sector 0 + registers - // 234 - 235: session registers - // 236 - 491: sector 1 - - if(linear_address > 491) { - *valid_pages = 0; - return -1; - } else if(linear_address >= 236) { - *sector = 1; - *valid_pages = 256 - (linear_address - 236); - return linear_address - 236; - } else if(linear_address >= 234) { - *sector = 0; - *valid_pages = 2 - (linear_address - 234); - return linear_address - 234 + 236; - } else { - *sector = 0; - *valid_pages = 234 - linear_address; - return linear_address; - } -} - -static int16_t mf_ultralight_ntag_i2c_addr_lin_to_tag( - MfUltralightData* data, - MfUltralightReader* reader, - int16_t linear_address, - uint8_t* sector, - int16_t* valid_pages) { - switch(data->type) { - case MfUltralightTypeNTAGI2C1K: - return mf_ultralight_ntag_i2c_addr_lin_to_tag_1k(linear_address, sector, valid_pages); - - case MfUltralightTypeNTAGI2C2K: - return mf_ultralight_ntag_i2c_addr_lin_to_tag_2k(linear_address, sector, valid_pages); - - case MfUltralightTypeNTAGI2CPlus1K: - return mf_ultralight_ntag_i2c_addr_lin_to_tag_plus_1k(linear_address, sector, valid_pages); - - case MfUltralightTypeNTAGI2CPlus2K: - return mf_ultralight_ntag_i2c_addr_lin_to_tag_plus_2k(linear_address, sector, valid_pages); - - default: - *sector = 0xff; - *valid_pages = reader->pages_to_read - linear_address; - return linear_address; - } -} - -static int16_t - mf_ultralight_ntag_i2c_addr_tag_to_lin_1k(uint8_t page, uint8_t sector, uint16_t* valid_pages) { - bool valid = false; - int16_t translated_page; - if(sector == 0) { - if(page <= 226) { - *valid_pages = 227 - page; - translated_page = page; - valid = true; - } else if(page >= 232 && page <= 233) { - *valid_pages = 2 - (page - 232); - translated_page = page - 232 + 227; - valid = true; - } - } else if(sector == 3) { - if(page >= 248 && page <= 249) { - *valid_pages = 2 - (page - 248); - translated_page = page - 248 + 229; - valid = true; - } - } - - if(!valid) { - *valid_pages = 0; - translated_page = -1; - } - return translated_page; -} - -static int16_t - mf_ultralight_ntag_i2c_addr_tag_to_lin_2k(uint8_t page, uint8_t sector, uint16_t* valid_pages) { - bool valid = false; - int16_t translated_page; - if(sector == 0) { - *valid_pages = 256 - page; - translated_page = page; - valid = true; - } else if(sector == 1) { - if(page <= 224) { - *valid_pages = 225 - page; - translated_page = 256 + page; - valid = true; - } else if(page >= 232 && page <= 233) { - *valid_pages = 2 - (page - 232); - translated_page = page - 232 + 481; - valid = true; - } - } else if(sector == 3) { - if(page >= 248 && page <= 249) { - *valid_pages = 2 - (page - 248); - translated_page = page - 248 + 483; - valid = true; - } - } - - if(!valid) { - *valid_pages = 0; - translated_page = -1; - } - return translated_page; -} - -static int16_t mf_ultralight_ntag_i2c_addr_tag_to_lin_plus_1k( - uint8_t page, - uint8_t sector, - uint16_t* valid_pages) { - bool valid = false; - int16_t translated_page; - if(sector == 0) { - if(page <= 233) { - *valid_pages = 234 - page; - translated_page = page; - valid = true; - } else if(page >= 236 && page <= 237) { - *valid_pages = 2 - (page - 236); - translated_page = page - 236 + 234; - valid = true; - } - } else if(sector == 3) { - if(page >= 248 && page <= 249) { - *valid_pages = 2 - (page - 248); - translated_page = page - 248 + 234; - valid = true; - } - } - - if(!valid) { - *valid_pages = 0; - translated_page = -1; - } - return translated_page; -} - -static int16_t mf_ultralight_ntag_i2c_addr_tag_to_lin_plus_2k( - uint8_t page, - uint8_t sector, - uint16_t* valid_pages) { - bool valid = false; - int16_t translated_page; - if(sector == 0) { - if(page <= 233) { - *valid_pages = 234 - page; - translated_page = page; - valid = true; - } else if(page >= 236 && page <= 237) { - *valid_pages = 2 - (page - 236); - translated_page = page - 236 + 234; - valid = true; - } - } else if(sector == 1) { - *valid_pages = 256 - page; - translated_page = page + 236; - valid = true; - } else if(sector == 3) { - if(page >= 248 && page <= 249) { - *valid_pages = 2 - (page - 248); - translated_page = page - 248 + 234; - valid = true; - } - } - - if(!valid) { - *valid_pages = 0; - translated_page = -1; - } - return translated_page; -} - -static int16_t mf_ultralight_ntag_i2c_addr_tag_to_lin( - MfUltralightData* data, - uint8_t page, - uint8_t sector, - uint16_t* valid_pages) { - switch(data->type) { - case MfUltralightTypeNTAGI2C1K: - return mf_ultralight_ntag_i2c_addr_tag_to_lin_1k(page, sector, valid_pages); - - case MfUltralightTypeNTAGI2C2K: - return mf_ultralight_ntag_i2c_addr_tag_to_lin_2k(page, sector, valid_pages); - - case MfUltralightTypeNTAGI2CPlus1K: - return mf_ultralight_ntag_i2c_addr_tag_to_lin_plus_1k(page, sector, valid_pages); - - case MfUltralightTypeNTAGI2CPlus2K: - return mf_ultralight_ntag_i2c_addr_tag_to_lin_plus_2k(page, sector, valid_pages); - - default: - *valid_pages = data->data_size / 4 - page; - return page; - } -} - -MfUltralightConfigPages* mf_ultralight_get_config_pages(MfUltralightData* data) { - if(data->type >= MfUltralightTypeUL11 && data->type <= MfUltralightTypeNTAG216) { - return (MfUltralightConfigPages*)&data->data[data->data_size - 4 * 4]; - } else if( - data->type >= MfUltralightTypeNTAGI2CPlus1K && - data->type <= MfUltralightTypeNTAGI2CPlus2K) { - return (MfUltralightConfigPages*)&data->data[0xe3 * 4]; - } else { - return NULL; - } -} - -static uint16_t mf_ultralight_calc_auth_count(MfUltralightData* data) { - if(mf_ul_get_features(data->type) & MfUltralightSupportAuth) { - MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data); - uint16_t scaled_authlim = config->access.authlim; - // NTAG I2C Plus uses 2^AUTHLIM attempts rather than the direct number - if(scaled_authlim > 0 && data->type >= MfUltralightTypeNTAGI2CPlus1K && - data->type <= MfUltralightTypeNTAGI2CPlus2K) { - scaled_authlim = 1 << scaled_authlim; - } - return scaled_authlim; - } - - return 0; -} - -// NTAG21x will NAK if NFC_CNT_EN unset, so preempt -static bool mf_ultralight_should_read_counters(MfUltralightData* data) { - if(data->type < MfUltralightTypeNTAG213 || data->type > MfUltralightTypeNTAG216) return true; - - MfUltralightConfigPages* config = mf_ultralight_get_config_pages(data); - return config->access.nfc_cnt_en; -} - -static bool mf_ultralight_sector_select(FuriHalNfcTxRxContext* tx_rx, uint8_t sector) { - FURI_LOG_D(TAG, "Selecting sector %u", sector); - tx_rx->tx_data[0] = MF_UL_SECTOR_SELECT; - tx_rx->tx_data[1] = 0xff; - tx_rx->tx_bits = 16; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - if(!furi_hal_nfc_tx_rx(tx_rx, 50)) { - FURI_LOG_D(TAG, "Failed to issue sector select command"); - return false; - } - - tx_rx->tx_data[0] = sector; - tx_rx->tx_data[1] = 0x00; - tx_rx->tx_data[2] = 0x00; - tx_rx->tx_data[3] = 0x00; - tx_rx->tx_bits = 32; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - // This is NOT a typo! The tag ACKs by not sending a response within 1ms. - if(furi_hal_nfc_tx_rx(tx_rx, 20)) { - // TODO: what gets returned when an actual NAK is received? - FURI_LOG_D(TAG, "Sector %u select NAK'd", sector); - return false; - } - - return true; -} - -bool mf_ultralight_read_pages_direct( - FuriHalNfcTxRxContext* tx_rx, - uint8_t start_index, - uint8_t* data) { - FURI_LOG_D(TAG, "Reading pages %d - %d", start_index, start_index + 3); - tx_rx->tx_data[0] = MF_UL_READ_CMD; - tx_rx->tx_data[1] = start_index; - tx_rx->tx_bits = 16; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - if(!furi_hal_nfc_tx_rx(tx_rx, 50) || tx_rx->rx_bits < 16 * 8) { - FURI_LOG_D(TAG, "Failed to read pages %d - %d", start_index, start_index + 3); - return false; - } - memcpy(data, tx_rx->rx_data, 16); - return true; -} - -bool mf_ultralight_read_pages( - FuriHalNfcTxRxContext* tx_rx, - MfUltralightReader* reader, - MfUltralightData* data) { - uint8_t pages_read_cnt = 0; - uint8_t curr_sector_index = 0xff; - reader->pages_read = 0; - for(size_t i = 0; i < reader->pages_to_read; i += pages_read_cnt) { - uint8_t tag_sector; - int16_t valid_pages; - int16_t tag_page = mf_ultralight_ntag_i2c_addr_lin_to_tag( - data, reader, (int16_t)i, &tag_sector, &valid_pages); - - furi_assert(tag_page != -1); - if(curr_sector_index != tag_sector) { - if(!mf_ultralight_sector_select(tx_rx, tag_sector)) return false; - curr_sector_index = tag_sector; - } - - FURI_LOG_D(TAG, "Reading pages %d - %d", i, i + (valid_pages > 4 ? 4 : valid_pages) - 1); - tx_rx->tx_data[0] = MF_UL_READ_CMD; - tx_rx->tx_data[1] = tag_page; - tx_rx->tx_bits = 16; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - - if(!furi_hal_nfc_tx_rx(tx_rx, 50) || tx_rx->rx_bits < 16 * 8) { - FURI_LOG_D( - TAG, - "Failed to read pages %d - %d", - i, - i + (valid_pages > 4 ? 4 : valid_pages) - 1); - break; - } - - if(valid_pages > 4) { - pages_read_cnt = 4; - } else { - pages_read_cnt = valid_pages; - } - reader->pages_read += pages_read_cnt; - memcpy(&data->data[i * 4], tx_rx->rx_data, pages_read_cnt * 4); - } - data->data_size = reader->pages_to_read * 4; - data->data_read = reader->pages_read * 4; - - return reader->pages_read > 0; -} - -bool mf_ultralight_fast_read_pages( - FuriHalNfcTxRxContext* tx_rx, - MfUltralightReader* reader, - MfUltralightData* data) { - uint8_t curr_sector_index = 0xff; - reader->pages_read = 0; - while(reader->pages_read < reader->pages_to_read) { - uint8_t tag_sector; - int16_t valid_pages; - int16_t tag_page = mf_ultralight_ntag_i2c_addr_lin_to_tag( - data, reader, reader->pages_read, &tag_sector, &valid_pages); - - furi_assert(tag_page != -1); - if(curr_sector_index != tag_sector) { - if(!mf_ultralight_sector_select(tx_rx, tag_sector)) return false; - curr_sector_index = tag_sector; - } - - FURI_LOG_D( - TAG, "Reading pages %d - %d", reader->pages_read, reader->pages_read + valid_pages - 1); - tx_rx->tx_data[0] = MF_UL_FAST_READ_CMD; - tx_rx->tx_data[1] = tag_page; - tx_rx->tx_data[2] = valid_pages - 1; - tx_rx->tx_bits = 24; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - if(furi_hal_nfc_tx_rx(tx_rx, 50)) { - memcpy(&data->data[reader->pages_read * 4], tx_rx->rx_data, valid_pages * 4); - reader->pages_read += valid_pages; - data->data_size = reader->pages_read * 4; - } else { - FURI_LOG_D( - TAG, - "Failed to read pages %d - %d", - reader->pages_read, - reader->pages_read + valid_pages - 1); - break; - } - } - - return reader->pages_read == reader->pages_to_read; -} - -bool mf_ultralight_read_signature(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data) { - bool signature_read = false; - - FURI_LOG_D(TAG, "Reading signature"); - tx_rx->tx_data[0] = MF_UL_READ_SIG; - tx_rx->tx_data[1] = 0; - tx_rx->tx_bits = 16; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - if(furi_hal_nfc_tx_rx(tx_rx, 50)) { - memcpy(data->signature, tx_rx->rx_data, sizeof(data->signature)); - signature_read = true; - } else { - FURI_LOG_D(TAG, "Failed redaing signature"); - } - - return signature_read; -} - -bool mf_ultralight_read_counters(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data) { - uint8_t counter_read = 0; - - FURI_LOG_D(TAG, "Reading counters"); - bool is_single_counter = (mf_ul_get_features(data->type) & MfUltralightSupportSingleCounter) != - 0; - for(size_t i = is_single_counter ? 2 : 0; i < 3; i++) { - tx_rx->tx_data[0] = MF_UL_READ_CNT; - tx_rx->tx_data[1] = i; - tx_rx->tx_bits = 16; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - if(!furi_hal_nfc_tx_rx(tx_rx, 50)) { - FURI_LOG_D(TAG, "Failed to read %d counter", i); - break; - } - data->counter[i] = (tx_rx->rx_data[2] << 16) | (tx_rx->rx_data[1] << 8) | - tx_rx->rx_data[0]; - counter_read++; - } - - return counter_read == (is_single_counter ? 1 : 3); -} - -bool mf_ultralight_read_tearing_flags(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data) { - uint8_t flag_read = 0; - - FURI_LOG_D(TAG, "Reading tearing flags"); - for(size_t i = 0; i < 3; i++) { - tx_rx->tx_data[0] = MF_UL_CHECK_TEARING; - tx_rx->rx_data[1] = i; - tx_rx->tx_bits = 16; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; - if(!furi_hal_nfc_tx_rx(tx_rx, 50)) { - FURI_LOG_D(TAG, "Failed to read %d tearing flag", i); - break; - } - data->tearing[i] = tx_rx->rx_data[0]; - flag_read++; - } - - return flag_read == 2; -} - -bool mf_ul_read_card( - FuriHalNfcTxRxContext* tx_rx, - MfUltralightReader* reader, - MfUltralightData* data) { - furi_assert(tx_rx); - furi_assert(reader); - furi_assert(data); - - bool card_read = false; - - // Read Mifare Ultralight version - if(mf_ultralight_read_version(tx_rx, reader, data)) { - if(reader->supported_features & MfUltralightSupportSignature) { - // Read Signature - mf_ultralight_read_signature(tx_rx, data); - } - } else { - // No GET_VERSION command, check for NTAG203 by reading last page (41) - uint8_t dummy[16]; - if(mf_ultralight_read_pages_direct(tx_rx, 41, dummy)) { - mf_ul_set_version_ntag203(reader, data); - reader->supported_features = mf_ul_get_features(data->type); - } else { - // We're really an original Mifare Ultralight, reset tag for safety - furi_hal_nfc_sleep(); - furi_hal_nfc_activate_nfca(300, NULL); - } - } - - card_read = mf_ultralight_read_pages(tx_rx, reader, data); - - if(card_read) { - if(reader->supported_features & MfUltralightSupportReadCounter && - mf_ultralight_should_read_counters(data)) { - mf_ultralight_read_counters(tx_rx, data); - } - if(reader->supported_features & MfUltralightSupportTearingFlags) { - mf_ultralight_read_tearing_flags(tx_rx, data); - } - data->curr_authlim = 0; - } - - return card_read; -} - -static void mf_ul_protect_auth_data_on_read_command_i2c( - uint8_t* tx_buff, - uint8_t start_page, - uint8_t end_page, - MfUltralightEmulator* emulator) { - if(emulator->data.type >= MfUltralightTypeNTAGI2CPlus1K) { - // Blank out PWD and PACK - if(start_page <= 229 && end_page >= 229) { - uint16_t offset = (229 - start_page) * 4; - uint8_t count = 4; - if(end_page >= 230) count += 2; - memset(&tx_buff[offset], 0, count); - } - - // Handle AUTH0 for sector 0 - if(!emulator->auth_success) { - if(emulator->config_cache.access.prot) { - uint8_t auth0 = emulator->config_cache.auth0; - if(auth0 < end_page) { - // start_page is always < auth0; otherwise is NAK'd already - uint8_t page_offset = auth0 - start_page; - uint8_t page_count = end_page - auth0; - memset(&tx_buff[page_offset * 4], 0, page_count * 4); - } - } - } - } -} - -static void mf_ul_ntag_i2c_fill_cross_area_read( - uint8_t* tx_buff, - uint8_t start_page, - uint8_t end_page, - MfUltralightEmulator* emulator) { - // For copying config or session registers in fast read - int16_t tx_page_offset; - int16_t data_page_offset; - uint8_t page_length; - bool apply = false; - MfUltralightType type = emulator->data.type; - if(emulator->curr_sector == 0) { - if(type == MfUltralightTypeNTAGI2C1K) { - if(start_page <= 233 && end_page >= 232) { - tx_page_offset = start_page - 232; - data_page_offset = 227; - page_length = 2; - apply = true; - } - } else if(type == MfUltralightTypeNTAGI2CPlus1K || type == MfUltralightTypeNTAGI2CPlus2K) { - if(start_page <= 237 && end_page >= 236) { - tx_page_offset = start_page - 236; - data_page_offset = 234; - page_length = 2; - apply = true; - } - } - } else if(emulator->curr_sector == 1) { - if(type == MfUltralightTypeNTAGI2C2K) { - if(start_page <= 233 && end_page >= 232) { - tx_page_offset = start_page - 232; - data_page_offset = 483; - page_length = 2; - apply = true; - } - } - } - - if(apply) { - while(tx_page_offset < 0 && page_length > 0) { - ++tx_page_offset; - ++data_page_offset; - --page_length; - } - memcpy( - &tx_buff[tx_page_offset * 4], - &emulator->data.data[data_page_offset * 4], - page_length * 4); - } -} - -static bool mf_ul_check_auth(MfUltralightEmulator* emulator, uint8_t start_page, bool is_write) { - if(!emulator->auth_success) { - if(start_page >= emulator->config_cache.auth0 && - (emulator->config_cache.access.prot || is_write)) - return false; - } - - if(is_write && emulator->config_cache.access.cfglck) { - uint16_t config_start_page = emulator->page_num - 4; - if(start_page == config_start_page || start_page == config_start_page + 1) return false; - } - - return true; -} - -static bool mf_ul_ntag_i2c_plus_check_auth( - MfUltralightEmulator* emulator, - uint8_t start_page, - bool is_write) { - if(!emulator->auth_success) { - // Check NFC_PROT - if(emulator->curr_sector == 0 && (emulator->config_cache.access.prot || is_write)) { - if(start_page >= emulator->config_cache.auth0) return false; - } else if(emulator->curr_sector == 1) { - // We don't have to specifically check for type because this is done - // by address translator - uint8_t pt_i2c = emulator->data.data[231 * 4]; - // Check 2K_PROT - if(pt_i2c & 0x08) return false; - } - } - - if(emulator->curr_sector == 1) { - // Check NFC_DIS_SEC1 - if(emulator->config_cache.access.nfc_dis_sec1) return false; - } - - return true; -} - -static int16_t mf_ul_get_dynamic_lock_page_addr(MfUltralightData* data) { - switch(data->type) { - case MfUltralightTypeNTAG203: - return 0x28; - case MfUltralightTypeUL21: - case MfUltralightTypeNTAG213: - case MfUltralightTypeNTAG215: - case MfUltralightTypeNTAG216: - return data->data_size / 4 - 5; - case MfUltralightTypeNTAGI2C1K: - case MfUltralightTypeNTAGI2CPlus1K: - case MfUltralightTypeNTAGI2CPlus2K: - return 0xe2; - case MfUltralightTypeNTAGI2C2K: - return 0x1e0; - default: - return -1; // No dynamic lock bytes - } -} - -// Returns true if page not locked -// write_page is tag address -static bool mf_ul_check_lock(MfUltralightEmulator* emulator, int16_t write_page) { - if(write_page < 2) return false; // Page 0-1 is always locked - if(write_page == 2) return true; // Page 2 does not have a lock flag - - // Check static lock bytes - if(write_page <= 15) { - uint16_t static_lock_bytes = emulator->data.data[10] | (emulator->data.data[11] << 8); - return (static_lock_bytes & (1 << write_page)) == 0; - } - - // Check dynamic lock bytes - - // Check max page - switch(emulator->data.type) { - case MfUltralightTypeNTAG203: - // Counter page can be locked and is after dynamic locks - if(write_page == 40) return true; - break; - case MfUltralightTypeUL21: - case MfUltralightTypeNTAG213: - case MfUltralightTypeNTAG215: - case MfUltralightTypeNTAG216: - if(write_page >= emulator->page_num - 5) return true; - break; - case MfUltralightTypeNTAGI2C1K: - case MfUltralightTypeNTAGI2CPlus1K: - if(write_page > 225) return true; - break; - case MfUltralightTypeNTAGI2C2K: - if(write_page > 479) return true; - break; - case MfUltralightTypeNTAGI2CPlus2K: - if(write_page >= 226 && write_page <= 255) return true; - if(write_page >= 512) return true; - break; - default: - furi_assert(false); - return true; - } - - int16_t dynamic_lock_index = mf_ul_get_dynamic_lock_page_addr(&emulator->data); - if(dynamic_lock_index == -1) return true; - // Run address through converter because NTAG I2C 2K is special - uint16_t valid_pages; // unused - dynamic_lock_index = - mf_ultralight_ntag_i2c_addr_tag_to_lin( - &emulator->data, dynamic_lock_index & 0xff, dynamic_lock_index >> 8, &valid_pages) * - 4; - - uint16_t dynamic_lock_bytes = emulator->data.data[dynamic_lock_index] | - (emulator->data.data[dynamic_lock_index + 1] << 8); - uint8_t shift; - - switch(emulator->data.type) { - // low byte LSB range, MSB range - case MfUltralightTypeNTAG203: - if(write_page >= 16 && write_page <= 27) - shift = (write_page - 16) / 4 + 1; - else if(write_page >= 28 && write_page <= 39) - shift = (write_page - 28) / 4 + 5; - else if(write_page == 41) - shift = 12; - else { - furi_assert(false); - shift = 0; - } - - break; - case MfUltralightTypeUL21: - case MfUltralightTypeNTAG213: - // 16-17, 30-31 - shift = (write_page - 16) / 2; - break; - case MfUltralightTypeNTAG215: - case MfUltralightTypeNTAG216: - case MfUltralightTypeNTAGI2C1K: - case MfUltralightTypeNTAGI2CPlus1K: - // 16-31, 128-129 - // 16-31, 128-143 - shift = (write_page - 16) / 16; - break; - case MfUltralightTypeNTAGI2C2K: - // 16-47, 240-271 - shift = (write_page - 16) / 32; - break; - case MfUltralightTypeNTAGI2CPlus2K: - // 16-47, 256-271 - if(write_page >= 208 && write_page <= 225) - shift = 6; - else if(write_page >= 256 && write_page <= 271) - shift = 7; - else - shift = (write_page - 16) / 32; - break; - default: - furi_assert(false); - shift = 0; - break; - } - - return (dynamic_lock_bytes & (1 << shift)) == 0; -} - -static void mf_ul_make_ascii_mirror(MfUltralightEmulator* emulator, string_t str) { - // Locals to improve readability - uint8_t mirror_page = emulator->config->mirror_page; - uint8_t mirror_byte = emulator->config->mirror.mirror_byte; - MfUltralightMirrorConf mirror_conf = emulator->config_cache.mirror.mirror_conf; - uint16_t last_user_page_index = emulator->page_num - 6; - bool uid_printed = false; - - if(mirror_conf == MfUltralightMirrorUid || mirror_conf == MfUltralightMirrorUidCounter) { - // UID range check - if(mirror_page < 4 || mirror_page > last_user_page_index - 3 || - (mirror_page == last_user_page_index - 3 && mirror_byte > 2)) { - if(mirror_conf == MfUltralightMirrorUid) return; - // NTAG21x has the peculiar behavior when UID+counter selected, if UID does not fit but - // counter will fit, it will actually mirror the counter - string_cat_str(str, " "); - } else { - for(int i = 0; i < 3; ++i) { - string_cat_printf(str, "%02X", emulator->data.data[i]); - } - // Skip BCC0 - for(int i = 4; i < 8; ++i) { - string_cat_printf(str, "%02X", emulator->data.data[i]); - } - uid_printed = true; - } - - uint16_t next_byte_offset = mirror_page * 4 + mirror_byte + 14; - if(mirror_conf == MfUltralightMirrorUidCounter) ++next_byte_offset; - mirror_page = next_byte_offset / 4; - mirror_byte = next_byte_offset % 4; - } - - if(mirror_conf == MfUltralightMirrorCounter || mirror_conf == MfUltralightMirrorUidCounter) { - // Counter is only printed if counter enabled - if(emulator->config_cache.access.nfc_cnt_en) { - // Counter protection check - if(emulator->config_cache.access.nfc_cnt_pwd_prot && !emulator->auth_success) return; - // Counter range check - if(mirror_page < 4) return; - if(mirror_page > last_user_page_index - 1) return; - if(mirror_page == last_user_page_index - 1 && mirror_byte > 2) return; - - if(mirror_conf == MfUltralightMirrorUidCounter) - string_cat_str(str, uid_printed ? "x" : " "); - - string_cat_printf(str, "%06X", emulator->data.counter[2]); - } - } -} - -static void mf_ul_increment_single_counter(MfUltralightEmulator* emulator) { - if(!emulator->read_counter_incremented && emulator->config_cache.access.nfc_cnt_en) { - if(emulator->data.counter[2] < 0xFFFFFF) { - ++emulator->data.counter[2]; - emulator->data_changed = true; - } - emulator->read_counter_incremented = true; - } -} - -static bool - mf_ul_emulate_ntag203_counter_write(MfUltralightEmulator* emulator, uint8_t* page_buff) { - // We'll reuse the existing counters for other NTAGs as staging - // Counter 0 stores original value, data is new value - uint32_t counter_value; - if(emulator->data.tearing[0] == MF_UL_TEARING_FLAG_DEFAULT) { - counter_value = emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4] | - (emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4 + 1] << 8); - } else { - // We've had a reset here, so load from original value - counter_value = emulator->data.counter[0]; - } - // Although the datasheet says increment by 0 is always possible, this is not the case on - // an actual tag. If the counter is at 0xFFFF, any writes are locked out. - if(counter_value == 0xFFFF) return false; - uint32_t increment = page_buff[0] | (page_buff[1] << 8); - if(counter_value == 0) { - counter_value = increment; - } else { - // Per datasheet specifying > 0x000F is supposed to NAK, but actual tag doesn't - increment &= 0x000F; - if(counter_value + increment > 0xFFFF) return false; - counter_value += increment; - } - // Commit to new value counter - emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4] = (uint8_t)counter_value; - emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4 + 1] = (uint8_t)(counter_value >> 8); - emulator->data.tearing[0] = MF_UL_TEARING_FLAG_DEFAULT; - if(counter_value == 0xFFFF) { - // Tag will lock out counter if final number is 0xFFFF, even if you try to roll it back - emulator->data.counter[1] = 0xFFFF; - } - emulator->data_changed = true; - return true; -} - -static void mf_ul_emulate_write( - MfUltralightEmulator* emulator, - int16_t tag_addr, - int16_t write_page, - uint8_t* page_buff) { - // Assumption: all access checks have been completed - - if(tag_addr == 2) { - // Handle static locks - uint16_t orig_static_locks = emulator->data.data[write_page * 4 + 2] | - (emulator->data.data[write_page * 4 + 3] << 8); - uint16_t new_static_locks = page_buff[2] | (page_buff[3] << 8); - if(orig_static_locks & 1) new_static_locks &= ~0x08; - if(orig_static_locks & 2) new_static_locks &= ~0xF0; - if(orig_static_locks & 4) new_static_locks &= 0xFF; - new_static_locks |= orig_static_locks; - page_buff[0] = emulator->data.data[write_page * 4]; - page_buff[1] = emulator->data.data[write_page * 4 + 1]; - page_buff[2] = new_static_locks & 0xff; - page_buff[3] = new_static_locks >> 8; - } else if(tag_addr == 3) { - // Handle OTP/capability container - *(uint32_t*)page_buff |= *(uint32_t*)&emulator->data.data[write_page * 4]; - } else if(tag_addr == mf_ul_get_dynamic_lock_page_addr(&emulator->data)) { - // Handle dynamic locks - if(emulator->data.type == MfUltralightTypeNTAG203) { - // NTAG203 lock bytes are a bit different from the others - uint8_t orig_page_lock_byte = emulator->data.data[write_page * 4]; - uint8_t orig_cnt_lock_byte = emulator->data.data[write_page * 4 + 1]; - uint8_t new_page_lock_byte = page_buff[0]; - uint8_t new_cnt_lock_byte = page_buff[1]; - - if(orig_page_lock_byte & 0x01) // Block lock bits 1-3 - new_page_lock_byte &= ~0x0E; - if(orig_page_lock_byte & 0x10) // Block lock bits 5-7 - new_page_lock_byte &= ~0xE0; - for(uint8_t i = 0; i < 4; ++i) { - if(orig_cnt_lock_byte & (1 << i)) // Block lock counter bit - new_cnt_lock_byte &= ~(1 << (4 + i)); - } - - new_page_lock_byte |= orig_page_lock_byte; - new_cnt_lock_byte |= orig_cnt_lock_byte; - page_buff[0] = new_page_lock_byte; - page_buff[1] = new_cnt_lock_byte; - } else { - uint16_t orig_locks = emulator->data.data[write_page * 4] | - (emulator->data.data[write_page * 4 + 1] << 8); - uint8_t orig_block_locks = emulator->data.data[write_page * 4 + 2]; - uint16_t new_locks = page_buff[0] | (page_buff[1] << 8); - uint8_t new_block_locks = page_buff[2]; - - int block_lock_count; - switch(emulator->data.type) { - case MfUltralightTypeUL21: - block_lock_count = 5; - break; - case MfUltralightTypeNTAG213: - block_lock_count = 6; - break; - case MfUltralightTypeNTAG215: - block_lock_count = 4; - break; - case MfUltralightTypeNTAG216: - case MfUltralightTypeNTAGI2C1K: - case MfUltralightTypeNTAGI2CPlus1K: - block_lock_count = 7; - break; - case MfUltralightTypeNTAGI2C2K: - case MfUltralightTypeNTAGI2CPlus2K: - block_lock_count = 8; - break; - default: - furi_assert(false); - block_lock_count = 0; - break; - } - - for(int i = 0; i < block_lock_count; ++i) { - if(orig_block_locks & (1 << i)) new_locks &= ~(3 << (2 * i)); - } - - new_locks |= orig_locks; - new_block_locks |= orig_block_locks; - - page_buff[0] = new_locks & 0xff; - page_buff[1] = new_locks >> 8; - page_buff[2] = new_block_locks; - if(emulator->data.type >= MfUltralightTypeUL21 && - emulator->data.type <= MfUltralightTypeNTAG216) - page_buff[3] = MF_UL_TEARING_FLAG_DEFAULT; - else - page_buff[3] = 0; - } - } - - memcpy(&emulator->data.data[write_page * 4], page_buff, 4); - emulator->data_changed = true; -} - -void mf_ul_reset_emulation(MfUltralightEmulator* emulator, bool is_power_cycle) { - emulator->curr_sector = 0; - emulator->ntag_i2c_plus_sector3_lockout = false; - emulator->auth_success = false; - if(is_power_cycle) { - if(emulator->config != NULL) emulator->config_cache = *emulator->config; - - if(emulator->supported_features & MfUltralightSupportSingleCounter) { - emulator->read_counter_incremented = false; - } - - if(emulator->data.type == MfUltralightTypeNTAG203) { - // Apply lockout if counter ever reached 0xFFFF - if(emulator->data.counter[1] == 0xFFFF) { - emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4] = 0xFF; - emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4 + 1] = 0xFF; - } - // Copy original counter value from data - emulator->data.counter[0] = - emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4] | - (emulator->data.data[MF_UL_NTAG203_COUNTER_PAGE * 4 + 1] << 8); - } - } else { - if(emulator->config != NULL) { - // ACCESS (less CFGLCK) and AUTH0 are updated when reactivated - // MIRROR_CONF is not; don't know about STRG_MOD_EN, but we're not using that anyway - emulator->config_cache.access.value = (emulator->config->access.value & 0xBF) | - (emulator->config_cache.access.value & 0x40); - emulator->config_cache.auth0 = emulator->config->auth0; - } - } - if(emulator->data.type == MfUltralightTypeNTAG203) { - // Mark counter as dirty - emulator->data.tearing[0] = 0; - } -} - -void mf_ul_prepare_emulation(MfUltralightEmulator* emulator, MfUltralightData* data) { - FURI_LOG_D(TAG, "Prepare emulation"); - emulator->data = *data; - emulator->supported_features = mf_ul_get_features(data->type); - emulator->config = mf_ultralight_get_config_pages(&emulator->data); - emulator->page_num = emulator->data.data_size / 4; - emulator->data_changed = false; - emulator->comp_write_cmd_started = false; - emulator->sector_select_cmd_started = false; - mf_ul_reset_emulation(emulator, true); -} - -bool mf_ul_prepare_emulation_response( - uint8_t* buff_rx, - uint16_t buff_rx_len, - uint8_t* buff_tx, - uint16_t* buff_tx_len, - uint32_t* data_type, - void* context) { - furi_assert(context); - MfUltralightEmulator* emulator = context; - uint16_t tx_bytes = 0; - uint16_t tx_bits = 0; - bool command_parsed = false; - bool send_ack = false; - bool respond_nothing = false; - bool reset_idle = false; - -#ifdef FURI_DEBUG - string_t debug_buf; - string_init(debug_buf); - for(int i = 0; i < (buff_rx_len + 7) / 8; ++i) { - string_cat_printf(debug_buf, "%02x ", buff_rx[i]); - } - string_strim(debug_buf); - FURI_LOG_T(TAG, "Emu RX (%d): %s", buff_rx_len, string_get_cstr(debug_buf)); - string_reset(debug_buf); -#endif - - // Check composite commands - if(emulator->comp_write_cmd_started) { - if(buff_rx_len == 16 * 8) { - if(emulator->data.type == MfUltralightTypeNTAG203 && - emulator->comp_write_page_addr == MF_UL_NTAG203_COUNTER_PAGE) { - send_ack = mf_ul_emulate_ntag203_counter_write(emulator, buff_rx); - command_parsed = send_ack; - } else { - mf_ul_emulate_write( - emulator, - emulator->comp_write_page_addr, - emulator->comp_write_page_addr, - buff_rx); - send_ack = true; - command_parsed = true; - } - } - emulator->comp_write_cmd_started = false; - } else if(emulator->sector_select_cmd_started) { - if(buff_rx_len == 4 * 8) { - if(buff_rx[0] <= 0xFE) { - emulator->curr_sector = buff_rx[0] > 3 ? 0 : buff_rx[0]; - emulator->ntag_i2c_plus_sector3_lockout = false; - command_parsed = true; - respond_nothing = true; - FURI_LOG_D(TAG, "Changing sector to %d", emulator->curr_sector); - } - } - emulator->sector_select_cmd_started = false; - } else if(buff_rx_len >= 8) { - uint8_t cmd = buff_rx[0]; - if(cmd == MF_UL_GET_VERSION_CMD) { - if(emulator->data.type >= MfUltralightTypeUL11) { - if(buff_rx_len == 1 * 8) { - tx_bytes = sizeof(emulator->data.version); - memcpy(buff_tx, &emulator->data.version, tx_bytes); - *data_type = FURI_HAL_NFC_TXRX_DEFAULT; - command_parsed = true; - } - } - } else if(cmd == MF_UL_READ_CMD) { - if(buff_rx_len == (1 + 1) * 8) { - int16_t start_page = buff_rx[1]; - tx_bytes = 16; - if(emulator->data.type < MfUltralightTypeNTAGI2C1K) { - if(start_page < emulator->page_num) { - do { - uint8_t copied_pages = 0; - uint8_t src_page = start_page; - uint8_t last_page_plus_one = start_page + 4; - uint8_t pwd_page = emulator->page_num - 2; - string_t ascii_mirror; - size_t ascii_mirror_len = 0; - const char* ascii_mirror_cptr = NULL; - uint8_t ascii_mirror_curr_page = 0; - uint8_t ascii_mirror_curr_byte = 0; - if(last_page_plus_one > emulator->page_num) - last_page_plus_one = emulator->page_num; - if(emulator->supported_features & MfUltralightSupportAuth) { - if(!mf_ul_check_auth(emulator, start_page, false)) break; - if(!emulator->auth_success && emulator->config_cache.access.prot && - emulator->config_cache.auth0 < last_page_plus_one) - last_page_plus_one = emulator->config_cache.auth0; - } - if(emulator->supported_features & MfUltralightSupportSingleCounter) - mf_ul_increment_single_counter(emulator); - if(emulator->supported_features & MfUltralightSupportAsciiMirror && - emulator->config_cache.mirror.mirror_conf != - MfUltralightMirrorNone) { - ascii_mirror_curr_byte = emulator->config->mirror.mirror_byte; - ascii_mirror_curr_page = emulator->config->mirror_page; - // Try to avoid wasting time making mirror if we won't copy it - // Conservatively check with UID+counter mirror size - if(last_page_plus_one > ascii_mirror_curr_page && - start_page + 3 >= ascii_mirror_curr_page && - start_page <= ascii_mirror_curr_page + 6) { - string_init(ascii_mirror); - mf_ul_make_ascii_mirror(emulator, ascii_mirror); - ascii_mirror_len = string_length_u(ascii_mirror); - ascii_mirror_cptr = string_get_cstr(ascii_mirror); - // Move pointer to where it should be to start copying - if(ascii_mirror_len > 0 && - ascii_mirror_curr_page < start_page && - ascii_mirror_curr_byte != 0) { - uint8_t diff = 4 - ascii_mirror_curr_byte; - ascii_mirror_len -= diff; - ascii_mirror_cptr += diff; - ascii_mirror_curr_byte = 0; - ++ascii_mirror_curr_page; - } - while(ascii_mirror_len > 0 && - ascii_mirror_curr_page < start_page) { - uint8_t diff = ascii_mirror_len > 4 ? 4 : ascii_mirror_len; - ascii_mirror_len -= diff; - ascii_mirror_cptr += diff; - ++ascii_mirror_curr_page; - } - } - } - - uint8_t* dest_ptr = buff_tx; - while(copied_pages < 4) { - // Copy page - memcpy(dest_ptr, &emulator->data.data[src_page * 4], 4); - - // Note: don't have to worry about roll-over with ASCII mirror because - // lowest valid page for it is 4, while roll-over will at best read - // pages 0-2 - if(ascii_mirror_len > 0 && src_page == ascii_mirror_curr_page) { - // Copy ASCII mirror - size_t copy_len = 4 - ascii_mirror_curr_byte; - if(copy_len > ascii_mirror_len) copy_len = ascii_mirror_len; - for(size_t i = 0; i < copy_len; ++i) { - if(*ascii_mirror_cptr != ' ') - dest_ptr[ascii_mirror_curr_byte] = - (uint8_t)*ascii_mirror_cptr; - ++ascii_mirror_curr_byte; - ++ascii_mirror_cptr; - } - ascii_mirror_len -= copy_len; - // Don't care if this is inaccurate after ascii_mirror_len = 0 - ascii_mirror_curr_byte = 0; - ++ascii_mirror_curr_page; - } - - if(emulator->supported_features & MfUltralightSupportAuth) { - if(src_page == pwd_page || src_page == pwd_page + 1) { - // Blank out PWD and PACK pages - memset(dest_ptr, 0, 4); - } - } - - dest_ptr += 4; - ++copied_pages; - ++src_page; - if(src_page >= last_page_plus_one) src_page = 0; - } - if(ascii_mirror_cptr != NULL) { - string_clear(ascii_mirror); - } - *data_type = FURI_HAL_NFC_TXRX_DEFAULT; - command_parsed = true; - } while(false); - } - } else { - uint16_t valid_pages; - start_page = mf_ultralight_ntag_i2c_addr_tag_to_lin( - &emulator->data, start_page, emulator->curr_sector, &valid_pages); - if(start_page != -1) { - if(emulator->data.type < MfUltralightTypeNTAGI2CPlus1K || - mf_ul_ntag_i2c_plus_check_auth(emulator, buff_rx[1], false)) { - if(emulator->data.type >= MfUltralightTypeNTAGI2CPlus1K && - emulator->curr_sector == 3 && valid_pages == 1) { - // Rewind back a sector to match behavior on a real tag - --start_page; - ++valid_pages; - } - - uint16_t copy_count = (valid_pages > 4 ? 4 : valid_pages) * 4; - FURI_LOG_D( - TAG, - "NTAG I2C Emu: page valid, %02x:%02x -> %d, %d", - emulator->curr_sector, - buff_rx[1], - start_page, - valid_pages); - memcpy(buff_tx, &emulator->data.data[start_page * 4], copy_count); - // For NTAG I2C, there's no roll-over; remainder is filled by null bytes - if(copy_count < tx_bytes) - memset(&buff_tx[copy_count], 0, tx_bytes - copy_count); - // Special case: NTAG I2C Plus sector 0 page 233 read crosses into page 236 - if(start_page == 233) - memcpy( - &buff_tx[12], &emulator->data.data[(start_page + 1) * 4], 4); - mf_ul_protect_auth_data_on_read_command_i2c( - buff_tx, start_page, start_page + copy_count / 4 - 1, emulator); - *data_type = FURI_HAL_NFC_TXRX_DEFAULT; - command_parsed = true; - } - } else { - FURI_LOG_D( - TAG, - "NTAG I2C Emu: page invalid, %02x:%02x", - emulator->curr_sector, - buff_rx[1]); - if(emulator->data.type >= MfUltralightTypeNTAGI2CPlus1K && - emulator->curr_sector == 3 && - !emulator->ntag_i2c_plus_sector3_lockout) { - // NTAG I2C Plus has a weird behavior where if you read sector 3 - // at an invalid address, it responds with zeroes then locks - // the read out, while if you read the mirrored session registers, - // it returns both session registers on either pages - memset(buff_tx, 0, tx_bytes); - *data_type = FURI_HAL_NFC_TXRX_DEFAULT; - command_parsed = true; - emulator->ntag_i2c_plus_sector3_lockout = true; - } - } - } - if(!command_parsed) tx_bytes = 0; - } - } else if(cmd == MF_UL_FAST_READ_CMD) { - if(emulator->supported_features & MfUltralightSupportFastRead) { - if(buff_rx_len == (1 + 2) * 8) { - int16_t start_page = buff_rx[1]; - uint8_t end_page = buff_rx[2]; - if(start_page <= end_page) { - tx_bytes = ((end_page + 1) - start_page) * 4; - if(emulator->data.type < MfUltralightTypeNTAGI2C1K) { - if((start_page < emulator->page_num) && - (end_page < emulator->page_num)) { - do { - if(emulator->supported_features & MfUltralightSupportAuth) { - // NAK if not authenticated and requested pages cross over AUTH0 - if(!emulator->auth_success && - emulator->config_cache.access.prot && - (start_page >= emulator->config_cache.auth0 || - end_page >= emulator->config_cache.auth0)) - break; - } - if(emulator->supported_features & - MfUltralightSupportSingleCounter) - mf_ul_increment_single_counter(emulator); - - // Copy requested pages - memcpy( - buff_tx, &emulator->data.data[start_page * 4], tx_bytes); - - if(emulator->supported_features & - MfUltralightSupportAsciiMirror && - emulator->config_cache.mirror.mirror_conf != - MfUltralightMirrorNone) { - // Copy ASCII mirror - // Less stringent check here, because expecting FAST_READ to - // only be issued once rather than repeatedly - string_t ascii_mirror; - string_init(ascii_mirror); - mf_ul_make_ascii_mirror(emulator, ascii_mirror); - size_t ascii_mirror_len = string_length_u(ascii_mirror); - const char* ascii_mirror_cptr = - string_get_cstr(ascii_mirror); - int16_t mirror_start_offset = - (emulator->config->mirror_page - start_page) * 4 + - emulator->config->mirror.mirror_byte; - if(mirror_start_offset < 0) { - if(mirror_start_offset < -(int16_t)ascii_mirror_len) { - // Past ASCII mirror, don't copy - ascii_mirror_len = 0; - } else { - ascii_mirror_cptr += -mirror_start_offset; - ascii_mirror_len -= -mirror_start_offset; - mirror_start_offset = 0; - } - } - if(ascii_mirror_len > 0) { - int16_t mirror_end_offset = - mirror_start_offset + ascii_mirror_len; - if(mirror_end_offset > (end_page + 1) * 4) { - mirror_end_offset = (end_page + 1) * 4; - ascii_mirror_len = - mirror_end_offset - mirror_start_offset; - } - for(size_t i = 0; i < ascii_mirror_len; ++i) { - if(*ascii_mirror_cptr != ' ') - buff_tx[mirror_start_offset] = - (uint8_t)*ascii_mirror_cptr; - ++mirror_start_offset; - ++ascii_mirror_cptr; - } - } - string_clear(ascii_mirror); - } - - if(emulator->supported_features & MfUltralightSupportAuth) { - // Clear PWD and PACK pages - uint8_t pwd_page = emulator->page_num - 2; - int16_t pwd_page_offset = pwd_page - start_page; - // PWD page - if(pwd_page_offset >= 0 && pwd_page <= end_page) { - memset(&buff_tx[pwd_page_offset * 4], 0, 4); - // PACK page - if(pwd_page + 1 <= end_page) - memset(&buff_tx[(pwd_page_offset + 1) * 4], 0, 4); - } - } - *data_type = FURI_HAL_NFC_TXRX_DEFAULT; - command_parsed = true; - } while(false); - } - } else { - uint16_t valid_pages; - start_page = mf_ultralight_ntag_i2c_addr_tag_to_lin( - &emulator->data, start_page, emulator->curr_sector, &valid_pages); - if(start_page != -1) { - if(emulator->data.type < MfUltralightTypeNTAGI2CPlus1K || - mf_ul_ntag_i2c_plus_check_auth(emulator, buff_rx[1], false)) { - uint16_t copy_count = tx_bytes; - if(copy_count > valid_pages * 4) copy_count = valid_pages * 4; - memcpy( - buff_tx, &emulator->data.data[start_page * 4], copy_count); - if(copy_count < tx_bytes) - memset(&buff_tx[copy_count], 0, tx_bytes - copy_count); - mf_ul_ntag_i2c_fill_cross_area_read( - buff_tx, buff_rx[1], buff_rx[2], emulator); - mf_ul_protect_auth_data_on_read_command_i2c( - buff_tx, - start_page, - start_page + copy_count / 4 - 1, - emulator); - *data_type = FURI_HAL_NFC_TXRX_DEFAULT; - command_parsed = true; - } - } - } - if(!command_parsed) tx_bytes = 0; - } - } - } - } else if(cmd == MF_UL_WRITE) { - if(buff_rx_len == (1 + 5) * 8) { - do { - uint8_t orig_write_page = buff_rx[1]; - int16_t write_page = orig_write_page; - uint16_t valid_pages; // unused - write_page = mf_ultralight_ntag_i2c_addr_tag_to_lin( - &emulator->data, write_page, emulator->curr_sector, &valid_pages); - if(write_page == -1) // NTAG I2C range check - break; - else if(write_page < 2 || write_page >= emulator->page_num) // Other MFUL/NTAG range check - break; - - if(emulator->supported_features & MfUltralightSupportAuth) { - if(emulator->data.type >= MfUltralightTypeNTAGI2CPlus1K) { - if(!mf_ul_ntag_i2c_plus_check_auth(emulator, orig_write_page, true)) - break; - } else { - if(!mf_ul_check_auth(emulator, orig_write_page, true)) break; - } - } - int16_t tag_addr = mf_ultralight_page_addr_to_tag_addr( - emulator->curr_sector, orig_write_page); - if(!mf_ul_check_lock(emulator, tag_addr)) break; - if(emulator->data.type == MfUltralightTypeNTAG203 && - orig_write_page == MF_UL_NTAG203_COUNTER_PAGE) { - send_ack = mf_ul_emulate_ntag203_counter_write(emulator, &buff_rx[2]); - command_parsed = send_ack; - } else { - mf_ul_emulate_write(emulator, tag_addr, write_page, &buff_rx[2]); - send_ack = true; - command_parsed = true; - } - } while(false); - } - } else if(cmd == MF_UL_FAST_WRITE) { - if(emulator->supported_features & MfUltralightSupportFastWrite) { - if(buff_rx_len == (1 + 66) * 8) { - if(buff_rx[1] == 0xF0 && buff_rx[2] == 0xFF) { - // TODO: update when SRAM emulation implemented - send_ack = true; - command_parsed = true; - } - } - } - } else if(cmd == MF_UL_COMP_WRITE) { - if(emulator->supported_features & MfUltralightSupportCompatWrite) { - if(buff_rx_len == (1 + 1) * 8) { - uint8_t write_page = buff_rx[1]; - do { - if(write_page < 2 || write_page >= emulator->page_num) break; - if(emulator->supported_features & MfUltralightSupportAuth && - !mf_ul_check_auth(emulator, write_page, true)) - break; - // Note we don't convert to tag addr here because there's only one sector - if(!mf_ul_check_lock(emulator, write_page)) break; - - emulator->comp_write_cmd_started = true; - emulator->comp_write_page_addr = write_page; - send_ack = true; - command_parsed = true; - } while(false); - } - } - } else if(cmd == MF_UL_READ_CNT) { - if(emulator->supported_features & MfUltralightSupportReadCounter) { - if(buff_rx_len == (1 + 1) * 8) { - do { - uint8_t cnt_num = buff_rx[1]; - - // NTAG21x checks - if(emulator->supported_features & MfUltralightSupportSingleCounter) { - if(cnt_num != 2) break; // Only counter 2 is available - if(!emulator->config_cache.access.nfc_cnt_en) - break; // NAK if counter not enabled - if(emulator->config_cache.access.nfc_cnt_pwd_prot && - !emulator->auth_success) - break; - } - - if(cnt_num < 3) { - buff_tx[0] = emulator->data.counter[cnt_num] & 0xFF; - buff_tx[1] = (emulator->data.counter[cnt_num] >> 8) & 0xFF; - buff_tx[2] = (emulator->data.counter[cnt_num] >> 16) & 0xFF; - tx_bytes = 3; - *data_type = FURI_HAL_NFC_TXRX_DEFAULT; - command_parsed = true; - } - } while(false); - } - } - } else if(cmd == MF_UL_INC_CNT) { - if(emulator->supported_features & MfUltralightSupportIncrCounter) { - if(buff_rx_len == (1 + 5) * 8) { - uint8_t cnt_num = buff_rx[1]; - uint32_t inc = (buff_rx[2] | (buff_rx[3] << 8) | (buff_rx[4] << 16)); - // TODO: can you increment by 0 when counter is at 0xffffff? - if((cnt_num < 3) && (emulator->data.counter[cnt_num] != 0x00FFFFFF) && - (emulator->data.counter[cnt_num] + inc <= 0x00FFFFFF)) { - emulator->data.counter[cnt_num] += inc; - // We're RAM-backed, so tearing never happens - emulator->data.tearing[cnt_num] = MF_UL_TEARING_FLAG_DEFAULT; - emulator->data_changed = true; - send_ack = true; - command_parsed = true; - } - } - } - } else if(cmd == MF_UL_AUTH) { - if(emulator->supported_features & MfUltralightSupportAuth) { - if(buff_rx_len == (1 + 4) * 8) { - uint16_t scaled_authlim = mf_ultralight_calc_auth_count(&emulator->data); - if(scaled_authlim != 0 && emulator->data.curr_authlim >= scaled_authlim) { - if(emulator->data.curr_authlim != UINT16_MAX) { - // Handle case where AUTHLIM has been lowered or changed from 0 - emulator->data.curr_authlim = UINT16_MAX; - emulator->data_changed = true; - } - // AUTHLIM reached, always fail - buff_tx[0] = MF_UL_NAK_AUTHLIM_REACHED; - tx_bits = 4; - *data_type = FURI_HAL_NFC_TX_RAW_RX_DEFAULT; - mf_ul_reset_emulation(emulator, false); - command_parsed = true; - } else { - if(memcmp(&buff_rx[1], emulator->config->auth_data.pwd.raw, 4) == 0) { - // Correct password - buff_tx[0] = emulator->config->auth_data.pack.raw[0]; - buff_tx[1] = emulator->config->auth_data.pack.raw[1]; - tx_bytes = 2; - *data_type = FURI_HAL_NFC_TXRX_DEFAULT; - emulator->auth_success = true; - command_parsed = true; - if(emulator->data.curr_authlim != 0) { - // Reset current AUTHLIM - emulator->data.curr_authlim = 0; - emulator->data_changed = true; - } - } else if(!emulator->config->auth_data.pwd.value) { - // Unknown password, pretend to be an Amiibo - buff_tx[0] = 0x80; - buff_tx[1] = 0x80; - tx_bytes = 2; - *data_type = FURI_HAL_NFC_TXRX_DEFAULT; - emulator->auth_success = true; - command_parsed = true; - } else { - // Wrong password, increase negative verification count - if(emulator->data.curr_authlim < UINT16_MAX) { - ++emulator->data.curr_authlim; - emulator->data_changed = true; - } - if(scaled_authlim != 0 && - emulator->data.curr_authlim >= scaled_authlim) { - emulator->data.curr_authlim = UINT16_MAX; - buff_tx[0] = MF_UL_NAK_AUTHLIM_REACHED; - tx_bits = 4; - *data_type = FURI_HAL_NFC_TX_RAW_RX_DEFAULT; - mf_ul_reset_emulation(emulator, false); - command_parsed = true; - } else { - // Should delay here to slow brute forcing - } - } - } - } - } - } else if(cmd == MF_UL_READ_SIG) { - if(emulator->supported_features & MfUltralightSupportSignature) { - // Check 2nd byte = 0x00 - RFU - if(buff_rx_len == (1 + 1) * 8 && buff_rx[1] == 0x00) { - tx_bytes = sizeof(emulator->data.signature); - memcpy(buff_tx, emulator->data.signature, tx_bytes); - *data_type = FURI_HAL_NFC_TXRX_DEFAULT; - command_parsed = true; - } - } - } else if(cmd == MF_UL_CHECK_TEARING) { - if(emulator->supported_features & MfUltralightSupportTearingFlags) { - if(buff_rx_len == (1 + 1) * 8) { - uint8_t cnt_num = buff_rx[1]; - if(cnt_num < 3) { - buff_tx[0] = emulator->data.tearing[cnt_num]; - tx_bytes = 1; - *data_type = FURI_HAL_NFC_TXRX_DEFAULT; - command_parsed = true; - } - } - } - } else if(cmd == MF_UL_HALT_START) { - reset_idle = true; - FURI_LOG_D(TAG, "Received HLTA"); - } else if(cmd == MF_UL_SECTOR_SELECT) { - if(emulator->supported_features & MfUltralightSupportSectorSelect) { - if(buff_rx_len == (1 + 1) * 8 && buff_rx[1] == 0xFF) { - // Send ACK - emulator->sector_select_cmd_started = true; - send_ack = true; - command_parsed = true; - } - } - } else if(cmd == MF_UL_READ_VCSL) { - if(emulator->supported_features & MfUltralightSupportVcsl) { - if(buff_rx_len == (1 + 20) * 8) { - buff_tx[0] = emulator->config_cache.vctid; - tx_bytes = 1; - *data_type = FURI_HAL_NFC_TXRX_DEFAULT; - command_parsed = true; - } - } - } else { - // NTAG203 appears to NAK instead of just falling off on invalid commands - if(emulator->data.type != MfUltralightTypeNTAG203) reset_idle = true; - FURI_LOG_D(TAG, "Received invalid command"); - } - } else { - reset_idle = true; - FURI_LOG_D(TAG, "Received invalid buffer less than 8 bits in length"); - } - - if(reset_idle) { - mf_ul_reset_emulation(emulator, false); - tx_bits = 0; - command_parsed = true; - } - - if(!command_parsed) { - // Send NACK - buff_tx[0] = MF_UL_NAK_INVALID_ARGUMENT; - tx_bits = 4; - *data_type = FURI_HAL_NFC_TX_RAW_RX_DEFAULT; - // Every NAK should cause reset to IDLE - mf_ul_reset_emulation(emulator, false); - } else if(send_ack) { - buff_tx[0] = MF_UL_ACK; - tx_bits = 4; - *data_type = FURI_HAL_NFC_TX_RAW_RX_DEFAULT; - } - - if(respond_nothing) { - *buff_tx_len = UINT16_MAX; - *data_type = FURI_HAL_NFC_TX_RAW_RX_DEFAULT; - } else { - // Return tx buffer size in bits - if(tx_bytes) { - tx_bits = tx_bytes * 8; - } - *buff_tx_len = tx_bits; - } - -#ifdef FURI_DEBUG - if(*buff_tx_len == UINT16_MAX) { - FURI_LOG_T(TAG, "Emu TX: no reply"); - } else if(*buff_tx_len > 0) { - int count = (*buff_tx_len + 7) / 8; - for(int i = 0; i < count; ++i) { - string_cat_printf(debug_buf, "%02x ", buff_tx[i]); - } - string_strim(debug_buf); - FURI_LOG_T(TAG, "Emu TX (%d): %s", *buff_tx_len, string_get_cstr(debug_buf)); - string_clear(debug_buf); - } else { - FURI_LOG_T(TAG, "Emu TX: HALT"); - } -#endif - - return tx_bits > 0; -} diff --git a/lib/nfc/protocols/mifare_ultralight.h b/lib/nfc/protocols/mifare_ultralight.h deleted file mode 100644 index 9642824f702..00000000000 --- a/lib/nfc/protocols/mifare_ultralight.h +++ /dev/null @@ -1,243 +0,0 @@ -#pragma once - -#include - -// Largest tag is NTAG I2C Plus 2K, both data sectors plus SRAM -#define MF_UL_MAX_DUMP_SIZE ((238 + 256 + 16) * 4) - -#define MF_UL_TEARING_FLAG_DEFAULT (0xBD) - -#define MF_UL_HALT_START (0x50) -#define MF_UL_GET_VERSION_CMD (0x60) -#define MF_UL_READ_CMD (0x30) -#define MF_UL_FAST_READ_CMD (0x3A) -#define MF_UL_WRITE (0xA2) -#define MF_UL_FAST_WRITE (0xA6) -#define MF_UL_COMP_WRITE (0xA0) -#define MF_UL_READ_CNT (0x39) -#define MF_UL_INC_CNT (0xA5) -#define MF_UL_AUTH (0x1B) -#define MF_UL_READ_SIG (0x3C) -#define MF_UL_CHECK_TEARING (0x3E) -#define MF_UL_READ_VCSL (0x4B) -#define MF_UL_SECTOR_SELECT (0xC2) - -#define MF_UL_ACK (0xa) -#define MF_UL_NAK_INVALID_ARGUMENT (0x0) -#define MF_UL_NAK_AUTHLIM_REACHED (0x4) - -#define MF_UL_NTAG203_COUNTER_PAGE (41) - -typedef enum { - MfUltralightAuthMethodManual, - MfUltralightAuthMethodAmeebo, - MfUltralightAuthMethodXiaomi, -} MfUltralightAuthMethod; - -// Important: order matters; some features are based on positioning in this enum -typedef enum { - MfUltralightTypeUnknown, - MfUltralightTypeNTAG203, - // Below have config pages and GET_VERSION support - MfUltralightTypeUL11, - MfUltralightTypeUL21, - MfUltralightTypeNTAG213, - MfUltralightTypeNTAG215, - MfUltralightTypeNTAG216, - // Below also have sector select - // NTAG I2C's *does not* have regular config pages, so it's a bit of an odd duck - MfUltralightTypeNTAGI2C1K, - MfUltralightTypeNTAGI2C2K, - // NTAG I2C Plus has stucture expected from NTAG21x - MfUltralightTypeNTAGI2CPlus1K, - MfUltralightTypeNTAGI2CPlus2K, - - // Keep last for number of types calculation - MfUltralightTypeNum, -} MfUltralightType; - -typedef enum { - MfUltralightSupportNone = 0, - MfUltralightSupportFastRead = 1 << 0, - MfUltralightSupportTearingFlags = 1 << 1, - MfUltralightSupportReadCounter = 1 << 2, - MfUltralightSupportIncrCounter = 1 << 3, - MfUltralightSupportSignature = 1 << 4, - MfUltralightSupportFastWrite = 1 << 5, - MfUltralightSupportCompatWrite = 1 << 6, - MfUltralightSupportAuth = 1 << 7, - MfUltralightSupportVcsl = 1 << 8, - MfUltralightSupportSectorSelect = 1 << 9, - // NTAG21x only has counter 2 - MfUltralightSupportSingleCounter = 1 << 10, - // ASCII mirror is not a command, but handy to have as a flag - MfUltralightSupportAsciiMirror = 1 << 11, - // NTAG203 counter that's in memory rather than through a command - MfUltralightSupportCounterInMemory = 1 << 12, -} MfUltralightFeatures; - -typedef enum { - MfUltralightMirrorNone, - MfUltralightMirrorUid, - MfUltralightMirrorCounter, - MfUltralightMirrorUidCounter, -} MfUltralightMirrorConf; - -typedef struct { - uint8_t header; - uint8_t vendor_id; - uint8_t prod_type; - uint8_t prod_subtype; - uint8_t prod_ver_major; - uint8_t prod_ver_minor; - uint8_t storage_size; - uint8_t protocol_type; -} MfUltralightVersion; - -typedef struct { - uint8_t sn0[3]; - uint8_t btBCC0; - uint8_t sn1[4]; - uint8_t btBCC1; - uint8_t internal; - uint8_t lock[2]; - uint8_t otp[4]; -} MfUltralightManufacturerBlock; - -typedef struct { - MfUltralightType type; - MfUltralightVersion version; - uint8_t signature[32]; - uint32_t counter[3]; - uint8_t tearing[3]; - bool has_auth; - MfUltralightAuthMethod auth_method; - uint8_t auth_key[4]; - bool auth_success; - uint16_t curr_authlim; - uint16_t data_size; - uint8_t data[MF_UL_MAX_DUMP_SIZE]; - uint16_t data_read; -} MfUltralightData; - -typedef struct __attribute__((packed)) { - union { - uint8_t raw[4]; - uint32_t value; - } pwd; - union { - uint8_t raw[2]; - uint16_t value; - } pack; -} MfUltralightAuth; - -// Common configuration pages for MFUL EV1, NTAG21x, and NTAG I2C Plus -typedef struct __attribute__((packed)) { - union { - uint8_t value; - struct { - uint8_t rfui1 : 2; - bool strg_mod_en : 1; - bool rfui2 : 1; - uint8_t mirror_byte : 2; - MfUltralightMirrorConf mirror_conf : 2; - }; - } mirror; - uint8_t rfui1; - uint8_t mirror_page; - uint8_t auth0; - union { - uint8_t value; - struct { - uint8_t authlim : 3; - bool nfc_cnt_pwd_prot : 1; - bool nfc_cnt_en : 1; - bool nfc_dis_sec1 : 1; // NTAG I2C Plus only - bool cfglck : 1; - bool prot : 1; - }; - } access; - uint8_t vctid; - uint8_t rfui2[2]; - MfUltralightAuth auth_data; - uint8_t rfui3[2]; -} MfUltralightConfigPages; - -typedef struct { - uint16_t pages_to_read; - int16_t pages_read; - MfUltralightFeatures supported_features; -} MfUltralightReader; - -typedef struct { - MfUltralightData data; - MfUltralightConfigPages* config; - // Most config values don't apply until power cycle, so cache config pages - // for correct behavior - MfUltralightConfigPages config_cache; - MfUltralightFeatures supported_features; - uint16_t page_num; - bool data_changed; - bool comp_write_cmd_started; - uint8_t comp_write_page_addr; - bool auth_success; - uint8_t curr_sector; - bool sector_select_cmd_started; - bool ntag_i2c_plus_sector3_lockout; - bool read_counter_incremented; -} MfUltralightEmulator; - -void mf_ul_reset(MfUltralightData* data); - -bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); - -bool mf_ultralight_read_version( - FuriHalNfcTxRxContext* tx_rx, - MfUltralightReader* reader, - MfUltralightData* data); - -bool mf_ultralight_read_pages_direct( - FuriHalNfcTxRxContext* tx_rx, - uint8_t start_index, - uint8_t* data); - -bool mf_ultralight_read_pages( - FuriHalNfcTxRxContext* tx_rx, - MfUltralightReader* reader, - MfUltralightData* data); - -bool mf_ultralight_fast_read_pages( - FuriHalNfcTxRxContext* tx_rx, - MfUltralightReader* reader, - MfUltralightData* data); - -bool mf_ultralight_read_signature(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data); - -bool mf_ultralight_read_counters(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data); - -bool mf_ultralight_read_tearing_flags(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data); - -bool mf_ultralight_authenticate(FuriHalNfcTxRxContext* tx_rx, uint32_t key, uint16_t* pack); - -MfUltralightConfigPages* mf_ultralight_get_config_pages(MfUltralightData* data); - -bool mf_ul_read_card( - FuriHalNfcTxRxContext* tx_rx, - MfUltralightReader* reader, - MfUltralightData* data); - -void mf_ul_reset_emulation(MfUltralightEmulator* emulator, bool is_power_cycle); - -void mf_ul_prepare_emulation(MfUltralightEmulator* emulator, MfUltralightData* data); - -bool mf_ul_prepare_emulation_response( - uint8_t* buff_rx, - uint16_t buff_rx_len, - uint8_t* buff_tx, - uint16_t* buff_tx_len, - uint32_t* data_type, - void* context); - -uint32_t mf_ul_pwdgen_amiibo(FuriHalNfcDevData* data); - -uint32_t mf_ul_pwdgen_xiaomi(FuriHalNfcDevData* data); diff --git a/lib/nfc/protocols/nfc_device_base.h b/lib/nfc/protocols/nfc_device_base.h new file mode 100644 index 00000000000..4f3480d455a --- /dev/null +++ b/lib/nfc/protocols/nfc_device_base.h @@ -0,0 +1,26 @@ +/** + * @file nfc_device_base.h + * @brief Common top-level types for the NFC protocol stack. + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Verbosity level of the displayed NFC device name. + */ +typedef enum { + NfcDeviceNameTypeFull, /**< Display full(verbose) name. */ + NfcDeviceNameTypeShort, /**< Display shortened name. */ +} NfcDeviceNameType; + +/** + * @brief Generic opaque type for protocol-specific NFC device data. + */ +typedef void NfcDeviceData; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_device_base_i.h b/lib/nfc/protocols/nfc_device_base_i.h new file mode 100644 index 00000000000..946ae76dcc0 --- /dev/null +++ b/lib/nfc/protocols/nfc_device_base_i.h @@ -0,0 +1,161 @@ +/** + * @file nfc_device_base_i.h + * @brief Abstract interface definitions for the NFC device system. + * + * This file is an implementation detail. It must not be included in + * any public API-related headers. + */ +#pragma once + +#include "nfc_device_base.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Allocate the protocol-specific NFC device data instance. + * + * @returns pointer to the allocated instance. + */ +typedef NfcDeviceData* (*NfcDeviceAlloc)(); + +/** + * @brief Delete the protocol-specific NFC device data instance. + * + * @param[in,out] data pointer to the instance to be deleted. + */ +typedef void (*NfcDeviceFree)(NfcDeviceData* data); + +/** + * @brief Reset the NFC device data instance. + * + * The behaviour is protocol-specific. Usually, required fields are zeroed or + * set to their initial values. + * + * @param[in,out] data pointer to the instance to be reset. + */ +typedef void (*NfcDeviceReset)(NfcDeviceData* data); + +/** + * @brief Copy source instance's data into the destination so that they become equal. + * + * @param[in,out] data pointer to the destination instance. + * @param[in] other pointer to the source instance. + */ +typedef void (*NfcDeviceCopy)(NfcDeviceData* data, const NfcDeviceData* other); + +/** + * @brief Deprecated. Do not use in new protocols. + * @deprecated do not use in new protocols. + * + * @param[in,out] data pointer to the instance to be tested. + * @param[in] device_type pointer to a FuriString containing a device type identifier. + * @returns true if data was verified, false otherwise. + */ +typedef bool (*NfcDeviceVerify)(NfcDeviceData* data, const FuriString* device_type); + +/** + * @brief Load NFC device data from a FlipperFormat file. + * + * The FlipperFormat file structure must be initialised and open by the calling code. + * + * @param[in,out] data pointer to the instance to be loaded into. + * @param[in] ff pointer to the FlipperFormat file instance. + * @param[in] version file format version to use when loading. + * @returns true if loaded successfully, false otherwise. + */ +typedef bool (*NfcDeviceLoad)(NfcDeviceData* data, FlipperFormat* ff, uint32_t version); + +/** + * @brief Save NFC device data to a FlipperFormat file. + * + * The FlipperFormat file structure must be initialised and open by the calling code. + * + * @param[in] data pointer to the instance to be saved. + * @param[in] ff pointer to the FlipperFormat file instance. + * @returns true if saved successfully, false otherwise. + */ +typedef bool (*NfcDeviceSave)(const NfcDeviceData* data, FlipperFormat* ff); + +/** + * @brief Compare two NFC device data instances. + * + * @param[in] data pointer to the first instance to be compared. + * @param[in] other pointer to the second instance to be compared. + * @returns true if instances are equal, false otherwise. + */ +typedef bool (*NfcDeviceEqual)(const NfcDeviceData* data, const NfcDeviceData* other); + +/** + * @brief Get a protocol-specific stateful NFC device name. + * + * The return value may change depending on the instance's internal state and the name_type parameter. + * + * @param[in] data pointer to the instance to be queried. + * @param[in] name_type type of the name to be displayed. + * @returns pointer to a statically allocated character string containing the appropriate name. + */ +typedef const char* (*NfcDeviceGetName)(const NfcDeviceData* data, NfcDeviceNameType name_type); + +/** + * @brief Get the NFC device's unique identifier (UID). + * + * The UID length is protocol-dependent. Additionally, a particular protocol might support + * several UID lengths. + * + * @param[in] data pointer to the instance to be queried. + * @param[out] uid_len pointer to the variable to contain the UID length. + * @returns pointer to the byte array containing the device's UID. + */ +typedef const uint8_t* (*NfcDeviceGetUid)(const NfcDeviceData* data, size_t* uid_len); + +/** + * @brief Set the NFC device's unique identifier (UID). + * + * The UID length must be supported by the protocol in question. + * + * @param[in,out] data pointer to the instance to be modified. + * @param[in] uid pointer to the byte array containing the new UID. + * @param[in] uid_len length of the UID. + * @return true if the UID was valid and set, false otherwise. + */ +typedef bool (*NfcDeviceSetUid)(NfcDeviceData* data, const uint8_t* uid, size_t uid_len); + +/** + * @brief Get the NFC device data associated with the parent protocol. + * + * The protocol the instance's data is associated with must have a parent. + * + * @param[in] data pointer to the instance to be queried. + * @returns pointer to the data instance associated with the parent protocol. + */ +typedef NfcDeviceData* (*NfcDeviceGetBaseData)(const NfcDeviceData* data); + +/** + * @brief Generic NFC device interface. + * + * Each protocol must fill this structure with its own function implementations. + */ +typedef struct { + const char* + protocol_name; /**< Pointer to a statically-allocated string with the protocol name. */ + NfcDeviceAlloc alloc; /**< Pointer to the alloc() function. */ + NfcDeviceFree free; /**< Pointer to the free() function. */ + NfcDeviceReset reset; /**< Pointer to the reset() function. */ + NfcDeviceCopy copy; /**< Pointer to the copy() function. */ + NfcDeviceVerify verify; /**< Deprecated. Set to NULL in new protocols. */ + NfcDeviceLoad load; /**< Pointer to the load() function. */ + NfcDeviceSave save; /**< Pointer to the save() function. */ + NfcDeviceEqual is_equal; /**< Pointer to the is_equal() function. */ + NfcDeviceGetName get_name; /**< Pointer to the get_name() function. */ + NfcDeviceGetUid get_uid; /**< Pointer to the get_uid() function. */ + NfcDeviceSetUid set_uid; /**< Pointer to the set_uid() function. */ + NfcDeviceGetBaseData get_base_data; /**< Pointer to the get_base_data() function. */ +} NfcDeviceBase; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_device_defs.c b/lib/nfc/protocols/nfc_device_defs.c new file mode 100644 index 00000000000..870bcafd9e0 --- /dev/null +++ b/lib/nfc/protocols/nfc_device_defs.c @@ -0,0 +1,46 @@ +/** + * @file nfc_device_defs.c + * @brief Main NFC device implementation definitions. + * + * All NFC device implementations must be registered here in order to be used + * by the NfcDevice library. + * + * @see nfc_device.h + * + * This file is to be modified upon adding a new protocol (see below). + */ +#include "nfc_device_base_i.h" +#include "nfc_protocol.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief List of registered NFC device implementations. + * + * When implementing a new protocol, add its implementation + * here under its own index defined in nfc_protocol.h. + */ +const NfcDeviceBase* nfc_devices[NfcProtocolNum] = { + [NfcProtocolIso14443_3a] = &nfc_device_iso14443_3a, + [NfcProtocolIso14443_3b] = &nfc_device_iso14443_3b, + [NfcProtocolIso14443_4a] = &nfc_device_iso14443_4a, + [NfcProtocolIso14443_4b] = &nfc_device_iso14443_4b, + [NfcProtocolIso15693_3] = &nfc_device_iso15693_3, + [NfcProtocolFelica] = &nfc_device_felica, + [NfcProtocolMfUltralight] = &nfc_device_mf_ultralight, + [NfcProtocolMfClassic] = &nfc_device_mf_classic, + [NfcProtocolMfDesfire] = &nfc_device_mf_desfire, + [NfcProtocolSlix] = &nfc_device_slix, + [NfcProtocolSt25tb] = &nfc_device_st25tb, + /* Add new protocols here */ +}; diff --git a/lib/nfc/protocols/nfc_device_defs.h b/lib/nfc/protocols/nfc_device_defs.h new file mode 100644 index 00000000000..f5ba2563f46 --- /dev/null +++ b/lib/nfc/protocols/nfc_device_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include "nfc_device_base_i.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcDeviceBase* nfc_devices[]; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_generic_event.h b/lib/nfc/protocols/nfc_generic_event.h new file mode 100644 index 00000000000..ec3bd68dc57 --- /dev/null +++ b/lib/nfc/protocols/nfc_generic_event.h @@ -0,0 +1,79 @@ +/** + * @file nfc_generic_event.h + * @brief Generic Nfc stack event definitions. + * + * Events are the main way of passing information about, well, various events + * that occur across the Nfc protocol stack. + * + * In order to subscribe to events from a certain instance, the user code must call + * its corresponding start() function while providing the appropriate callback. + * During this call, an additional context pointer can be provided, which will be passed + * to the context parameter at the time of the callback execution. + * + * For additional information on how events are passed around and processed, see protocol-specific + * poller and listener implementations found in the respectively named subfolders. + * + */ +#pragma once + +#include "nfc_protocol.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Generic Nfc instance type. + * + * Must be cast to a concrete type before use. + * Depending on the context, a pointer of this type + * may point to an object of the following types: + * - Nfc type, + * - Concrete poller type, + * - Concrete listener type. + */ +typedef void NfcGenericInstance; + +/** + * @brief Generic Nfc event data type. + * + * Must be cast to a concrete type before use. + * Usually, it will be the protocol-specific event type. + */ +typedef void NfcGenericEventData; + +/** + * @brief Generic Nfc event type. + * + * A generic Nfc event contains a protocol identifier, can be used to determine + * the remaing fields' type. + * + * If the value of the protocol field is NfcProtocolInvalid, then it means that + * the event was emitted from an Nfc instance, otherwise it originated from + * a concrete poller or listener instance. + * + * The event_data field is protocol-specific and should be cast to the appropriate type before use. + */ +typedef struct { + NfcProtocol protocol; /**< Protocol identifier of the instance that produced the event. */ + NfcGenericInstance* + instance; /**< Pointer to the protocol-specific instance that produced the event. */ + NfcGenericEventData* event_data; /**< Pointer to the protocol-specific event. */ +} NfcGenericEvent; + +/** + * @brief Generic Nfc event callback type. + * + * A function of this type must be passed as the callback parameter upon start + * of a poller, listener or Nfc instance. + * + * @param [in] event Nfc generic event, passed by value, complete with protocol type and data. + * @param [in,out] context pointer to the user-specific context (set when starting a poller/listener instance). + * @returns the command which the event producer must execute. + */ +typedef NfcCommand (*NfcGenericCallback)(NfcGenericEvent event, void* context); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_listener_base.h b/lib/nfc/protocols/nfc_listener_base.h new file mode 100644 index 00000000000..6d0cdcd094c --- /dev/null +++ b/lib/nfc/protocols/nfc_listener_base.h @@ -0,0 +1,98 @@ +/** + * @file nfc_listener_base.h + * @brief Abstract interface definitions for the NFC listener system. + * + * This file is an implementation detail. It must not be included in + * any public API-related headers. + * + * @see nfc_listener.h + * + */ +#pragma once + +#include "nfc_generic_event.h" +#include "nfc_device_base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Allocate a protocol-specific listener instance. + * + * For base listeners pass a pointer to an instance of type Nfc + * as the base_listener parameter, otherwise it must be a pointer to another listener instance + * (compare iso14443_3a/iso14443_3a_listener.c and iso14443_4a/iso14443_4a_listener.c). + * + * @see nfc_protocol.c + * + * The NFC device data passed as the data parameter is copied to the instance and may + * change during the emulation in response to reader commands. + * + * To retrieve the modified data, NfcListenerGetData gets called by the NfcListener + * implementation when the user code calls nfc_listener_get_data(). + * + * @param[in] base_listener pointer to the parent listener instance. + * @param[in] data pointer to the protocol-specific data to use during emulation. + * @returns pointer to the allocated listener instance. + */ +typedef NfcGenericInstance* ( + *NfcListenerAlloc)(NfcGenericInstance* base_listener, NfcDeviceData* data); + +/** + * @brief Delete a protocol-specific listener instance. + * + * @param[in,out] instance pointer to the instance to be deleted. + */ +typedef void (*NfcListenerFree)(NfcGenericInstance* instance); + +/** + * @brief Set the callback function to handle events emitted by the listener instance. + * + * @see nfc_generic_event.h + * + * @param[in,out] listener + * @param[in] callback pointer to the user-defined callback function which will receive events. + * @param[in] context pointer to the user-specific context (will be passed to the callback). + */ +typedef void (*NfcListenerSetCallback)( + NfcGenericInstance* listener, + NfcGenericCallback callback, + void* context); + +/** + * @brief Emulate a supported NFC card with given device data. + * + * @param[in] event protocol-specific event passed by the parent listener instance. + * @param[in,out] context pointer to the protocol-specific listener instance. + * @returns command to be executed by the parent listener instance. + */ +typedef NfcCommand (*NfcListenerRun)(NfcGenericEvent event, void* context); + +/** + * @brief Get the protocol-specific data that was that was provided for emulation. + * + * @param[in] instance pointer to the protocol-specific listener instance. + * @returns pointer to the NFC device data. + */ +typedef const NfcDeviceData* (*NfcListenerGetData)(const NfcGenericInstance* instance); + +/** + * @brief Generic NFC listener interface. + * + * Each protocol must fill this structure with its own function implementations. + * See above for the function signatures and descriptions. + * + * Additionally, see ${PROTOCOL_NAME}/${PROTOCOL_NAME}_listener.c for usage examples. + */ +typedef struct { + NfcListenerAlloc alloc; /**< Pointer to the alloc() function. */ + NfcListenerFree free; /**< Pointer to the free() function. */ + NfcListenerSetCallback set_callback; /**< Pointer to the set_callback() function. */ + NfcListenerRun run; /**< Pointer to the run() function. */ + NfcListenerGetData get_data; /**< Pointer to the get_data() function. */ +} NfcListenerBase; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_listener_defs.c b/lib/nfc/protocols/nfc_listener_defs.c new file mode 100644 index 00000000000..2a6167e9cb3 --- /dev/null +++ b/lib/nfc/protocols/nfc_listener_defs.c @@ -0,0 +1,23 @@ +#include "nfc_listener_defs.h" + +#include +#include +#include +#include +#include +#include +#include + +const NfcListenerBase* nfc_listeners_api[NfcProtocolNum] = { + [NfcProtocolIso14443_3a] = &nfc_listener_iso14443_3a, + [NfcProtocolIso14443_3b] = NULL, + [NfcProtocolIso14443_4a] = &nfc_listener_iso14443_4a, + [NfcProtocolIso14443_4b] = NULL, + [NfcProtocolIso15693_3] = &nfc_listener_iso15693_3, + [NfcProtocolMfUltralight] = &mf_ultralight_listener, + [NfcProtocolMfClassic] = &mf_classic_listener, + [NfcProtocolMfDesfire] = NULL, + [NfcProtocolSlix] = &nfc_listener_slix, + [NfcProtocolSt25tb] = NULL, + [NfcProtocolFelica] = &nfc_listener_felica, +}; diff --git a/lib/nfc/protocols/nfc_listener_defs.h b/lib/nfc/protocols/nfc_listener_defs.h new file mode 100644 index 00000000000..4d88cc09830 --- /dev/null +++ b/lib/nfc/protocols/nfc_listener_defs.h @@ -0,0 +1,14 @@ +#pragma once + +#include "nfc_listener_base.h" +#include "nfc_protocol.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcListenerBase* nfc_listeners_api[NfcProtocolNum]; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_poller_base.h b/lib/nfc/protocols/nfc_poller_base.h new file mode 100644 index 00000000000..9c4a2b65208 --- /dev/null +++ b/lib/nfc/protocols/nfc_poller_base.h @@ -0,0 +1,132 @@ +/** + * @file nfc_poller_base.h + * @brief Abstract interface definitions for the NFC poller system. + * + * This file is an implementation detail. It must not be included in + * any public API-related headers. + * + * @see nfc_poller.h + * + */ +#pragma once + +#include "nfc_generic_event.h" +#include "nfc_device_base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Allocate a protocol-specific poller instance. + * + * For base pollers pass a pointer to an instance of type Nfc + * as the base_poller parameter, otherwise it must be a pointer to another poller instance + * (compare iso14443_3a/iso14443_3a_poller.c and iso14443_4a/iso14443_4a_poller.c). + * + * @see nfc_protocol.c + * + * @param[in] base_poller pointer to the parent poller instance. + * @returns pointer to the allocated poller instance. + */ +typedef NfcGenericInstance* (*NfcPollerAlloc)(NfcGenericInstance* base_poller); + +/** + * @brief Delete a protocol-specific poller instance. + * + * @param[in,out] instance pointer to the instance to be deleted. + */ +typedef void (*NfcPollerFree)(NfcGenericInstance* instance); + +/** + * @brief Set the callback function to handle events emitted by the poller instance. + * + * @see nfc_generic_event.h + * + * @param[in,out] poller pointer to the protocol-specific poller instance. + * @param[in] callback pointer to the user-defined callback function which will receive events. + * @param[in] context pointer to the user-specific context (will be passed to the callback). + */ +typedef void ( + *NfcPollerSetCallback)(NfcGenericInstance* poller, NfcGenericCallback callback, void* context); + +/** + * @brief Activate and read a supported NFC card. + * + * Ths function is passed to the parent poller's ${POLLER_NAME}_set_callback function as + * the callback parameter. This is done automatically by the NfcPoller implementation based + * on the protocol hierarchy defined in nfc_protocol.c, so there is no need to call it explicitly. + * + * Thus, it will be called each time the parent poller emits an event. Usually it happens + * only after the parent poller has successfully completed its job. + * + * Example for an application reading a card with a compound (non-base) protocol (simplified): + * + * ``` + * start() <-- set_callback() <-- set_callback() <-- nfc_poller_start() + * | | | + * Nfc | Base Poller | Child Poller | Application + * | | | + * worker() --> run() --> run() ---> handle_event() + * ``` + * + * The base poller receives events directly from an Nfc instance, from which they are + * propagated as needed to however many other pollers there are in the current hierarchy. + * + * This function can be thought of as the poller's "main loop" function. Depending + * on the particular poller implementation, it may perform actions such as reading + * and writing to an NFC card, state changes and control of the parent poller. + * + * @see nfc_generic_event.h + * + * @param[in] event protocol-specific event passed by the parent poller instance. + * @param[in,out] context pointer to the protocol-specific poller instance. + * @returns command to be executed by the parent poller instance. + */ +typedef NfcCommand (*NfcPollerRun)(NfcGenericEvent event, void* context); + +/** + * @brief Determine whether there is a supported card in the vicinity. + * + * The behaviour is mostly the same as of NfcPollerRun, with the difference in the + * procedure and return value. + * The procedure implemented in this function must do whatever it needs to unambigiously + * determine whether a supported and valid NFC card is in the vicinity. + * + * Like the previously described NfcPollerRun, it is called automatically by the NfcPoller + * implementation, so there is no need to call it explicitly. + * + * @param[in] event protocol-specific event passed by the parent poller instance. + * @param[in,out] context pointer to the protocol-specific poller instance. + * @returns true if a supported card was detected, false otherwise. + */ +typedef bool (*NfcPollerDetect)(NfcGenericEvent event, void* context); + +/** + * @brief Get the data that was that was gathered during the reading process. + * + * @param[in] instance pointer to the protocol-specific poller instance. + * @returns pointer to the NFC device data. + */ +typedef const NfcDeviceData* (*NfcPollerGetData)(const NfcGenericInstance* instance); + +/** + * @brief Generic NFC poller interface. + * + * Each protocol must fill this structure with its own function implementations. + * See above for the function signatures and descriptions. + * + * Additionally, see ${PROTOCOL_NAME}/${PROTOCOL_NAME}_poller.c for usage examples. + */ +typedef struct { + NfcPollerAlloc alloc; /**< Pointer to the alloc() function. */ + NfcPollerFree free; /**< Pointer to the free() function. */ + NfcPollerSetCallback set_callback; /**< Pointer to the set_callback() function. */ + NfcPollerRun run; /**< Pointer to the run() function. */ + NfcPollerDetect detect; /**< Pointer to the detect() function. */ + NfcPollerGetData get_data; /**< Pointer to the get_data() function. */ +} NfcPollerBase; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_poller_defs.c b/lib/nfc/protocols/nfc_poller_defs.c new file mode 100644 index 00000000000..7553c74de1b --- /dev/null +++ b/lib/nfc/protocols/nfc_poller_defs.c @@ -0,0 +1,28 @@ +#include "nfc_poller_defs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const NfcPollerBase* nfc_pollers_api[NfcProtocolNum] = { + [NfcProtocolIso14443_3a] = &nfc_poller_iso14443_3a, + [NfcProtocolIso14443_3b] = &nfc_poller_iso14443_3b, + [NfcProtocolIso14443_4a] = &nfc_poller_iso14443_4a, + [NfcProtocolIso14443_4b] = &nfc_poller_iso14443_4b, + [NfcProtocolIso15693_3] = &nfc_poller_iso15693_3, + [NfcProtocolFelica] = &nfc_poller_felica, + [NfcProtocolMfUltralight] = &mf_ultralight_poller, + [NfcProtocolMfClassic] = &mf_classic_poller, + [NfcProtocolMfDesfire] = &mf_desfire_poller, + [NfcProtocolSlix] = &nfc_poller_slix, + /* Add new pollers here */ + [NfcProtocolSt25tb] = &nfc_poller_st25tb, +}; diff --git a/lib/nfc/protocols/nfc_poller_defs.h b/lib/nfc/protocols/nfc_poller_defs.h new file mode 100644 index 00000000000..a406a5f08b4 --- /dev/null +++ b/lib/nfc/protocols/nfc_poller_defs.h @@ -0,0 +1,14 @@ +#pragma once + +#include "nfc_poller_base.h" +#include "nfc_protocol.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcPollerBase* nfc_pollers_api[NfcProtocolNum]; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_protocol.c b/lib/nfc/protocols/nfc_protocol.c new file mode 100644 index 00000000000..2ea9b39820a --- /dev/null +++ b/lib/nfc/protocols/nfc_protocol.c @@ -0,0 +1,174 @@ +/** + * @file nfc_protocol.c + * @brief Main protocol hierarchy definitions. + * + * To reduce code duplication, all NFC protocols are described as a tree, whose + * structure is shown in the diagram below. The (Start) node is actually + * nonexistent and is there only for clarity. + * + * All its child protocols are considered base protocols, which in turn serve + * as parents to other, usually vendor-specific ones. + * + * ``` + * **************************** Protocol tree structure *************************** + * + * (Start) + * | + * +------------------------+-----------+---------+------------+ + * | | | | | + * ISO14443-3A ISO14443-3B Felica ISO15693-3 ST25TB + * | | | + * +---------------+-------------+ ISO14443-4B SLIX + * | | | + * ISO14443-4A Mf Ultralight Mf Classic + * | + * Mf Desfire + * ``` + * + * When implementing a new protocol, its place in the tree must be determined first. + * If no appropriate base protocols exists, then it must be a base protocol itself. + * + * This file is to be modified upon adding a new protocol (see below). + * + */ +#include "nfc_protocol.h" + +#include + +/** + * @brief Tree node describing a protocol. + * + * All base protocols (see above) have NfcProtocolInvalid + * in the parent_protocol field. + */ +typedef struct { + NfcProtocol parent_protocol; /**< Parent protocol identifier. */ + size_t children_num; /** < Number of the child protocols. */ + const NfcProtocol* children_protocol; /**< Pointer to an array of child protocol identifiers. */ +} NfcProtocolTreeNode; + +/** List of ISO14443-3A child protocols. */ +static const NfcProtocol nfc_protocol_iso14443_3a_children_protocol[] = { + NfcProtocolIso14443_4a, + NfcProtocolMfUltralight, +}; + +/** List of ISO14443-3B child protocols. */ +static const NfcProtocol nfc_protocol_iso14443_3b_children_protocol[] = { + NfcProtocolIso14443_4b, +}; + +/** List of ISO14443-4A child protocols. */ +static const NfcProtocol nfc_protocol_iso14443_4a_children_protocol[] = { + NfcProtocolMfDesfire, +}; + +/** List of ISO115693-3 child protocols. */ +static const NfcProtocol nfc_protocol_iso15693_3_children_protocol[] = { + NfcProtocolSlix, +}; + +/* Add new child protocol lists here (if necessary) */ + +/** + * @brief Flattened description of the NFC protocol tree. + * + * When implementing a new protocol, add the node here under its + * own index defined in nfc_protocol.h. + * + * Additionally, if it has an already implemented protocol as a parent, + * add its identifier to its respective list of child protocols (see above). + */ +static const NfcProtocolTreeNode nfc_protocol_nodes[NfcProtocolNum] = { + [NfcProtocolIso14443_3a] = + { + .parent_protocol = NfcProtocolInvalid, + .children_num = COUNT_OF(nfc_protocol_iso14443_3a_children_protocol), + .children_protocol = nfc_protocol_iso14443_3a_children_protocol, + }, + [NfcProtocolIso14443_3b] = + { + .parent_protocol = NfcProtocolInvalid, + .children_num = COUNT_OF(nfc_protocol_iso14443_3b_children_protocol), + .children_protocol = nfc_protocol_iso14443_3b_children_protocol, + }, + [NfcProtocolIso14443_4a] = + { + .parent_protocol = NfcProtocolIso14443_3a, + .children_num = COUNT_OF(nfc_protocol_iso14443_4a_children_protocol), + .children_protocol = nfc_protocol_iso14443_4a_children_protocol, + }, + [NfcProtocolIso14443_4b] = + { + .parent_protocol = NfcProtocolIso14443_3b, + .children_num = 0, + .children_protocol = NULL, + }, + [NfcProtocolIso15693_3] = + { + .parent_protocol = NfcProtocolInvalid, + .children_num = COUNT_OF(nfc_protocol_iso15693_3_children_protocol), + .children_protocol = nfc_protocol_iso15693_3_children_protocol, + }, + [NfcProtocolFelica] = + { + .parent_protocol = NfcProtocolInvalid, + .children_num = 0, + .children_protocol = NULL, + }, + [NfcProtocolMfUltralight] = + { + .parent_protocol = NfcProtocolIso14443_3a, + .children_num = 0, + .children_protocol = NULL, + }, + [NfcProtocolMfClassic] = + { + .parent_protocol = NfcProtocolIso14443_3a, + .children_num = 0, + .children_protocol = NULL, + }, + [NfcProtocolMfDesfire] = + { + .parent_protocol = NfcProtocolIso14443_4a, + .children_num = 0, + .children_protocol = NULL, + }, + [NfcProtocolSlix] = + { + .parent_protocol = NfcProtocolIso15693_3, + .children_num = 0, + .children_protocol = NULL, + }, + [NfcProtocolSt25tb] = + { + .parent_protocol = NfcProtocolInvalid, + .children_num = 0, + .children_protocol = NULL, + }, + /* Add new protocols here */ +}; + +NfcProtocol nfc_protocol_get_parent(NfcProtocol protocol) { + furi_assert(protocol < NfcProtocolNum); + + return nfc_protocol_nodes[protocol].parent_protocol; +} + +bool nfc_protocol_has_parent(NfcProtocol protocol, NfcProtocol parent_protocol) { + furi_assert(protocol < NfcProtocolNum); + furi_assert(parent_protocol < NfcProtocolNum); + + bool parent_found = false; + const NfcProtocolTreeNode* iter = &nfc_protocol_nodes[protocol]; + + while(iter->parent_protocol != NfcProtocolInvalid) { + if(iter->parent_protocol == parent_protocol) { + parent_found = true; + break; + } + iter = &nfc_protocol_nodes[iter->parent_protocol]; + } + + return parent_found; +} diff --git a/lib/nfc/protocols/nfc_protocol.h b/lib/nfc/protocols/nfc_protocol.h new file mode 100644 index 00000000000..ee634533356 --- /dev/null +++ b/lib/nfc/protocols/nfc_protocol.h @@ -0,0 +1,219 @@ +/** + * @file nfc_protocol.h + * @brief Top-level NFC protocol definitions. + * + * This file is to be modified upon adding a new protocol (see below). + * + * # How to add a new NFC protocol + * + * ## 1. Gather information + * + * Having proper protocol documentation would be ideal, although they are often available only for a fee, or given under an NDA. + * As an alternative, reading code from relevant open-source projects or notes gathered by means of reverse engineering will do. + * + * ### 1.1 Technology + * + * Check whether the NFC technology required for the protocol is supported. If no support exists, adding the protocol may + * be problematic, since it is highly hardware-dependent. + * + * @see NfcTech for the enumeration of supported NFC technologies. + * + * ### 1.2 Base protocols + * + * Check whether the protocol to be implemented is built on top of some already supported protocol. + * + * @see NfcProtocol for the enumeration of supported NFC protocols. + * + * If the answer is "yes", then the protocol to be implemented is a child protocol. If no, then it will become a base protocol. + * Sometimes it will be necessary to implement both, e.g. when the target protocol is built on top of a base one, + * but the latter is currently not supported. + * + * ## 2. Create the files + * + * ### 2.1 Recommended file structure + * + * The recommended file structure for a protocol is as follows: + * + * ```text + * protocols + * | + * +- protocol_name + * | + * +- protocol_name.h + * | + * +- protocol_name.c + * | + * +- protocol_name_device_defs.h + * | + * +- protocol_name_poller.h + * | + * +- protocol_name_poller.c + * | + * +- protocol_name_poller_defs.h + * | + * . + * . (files below are optional) + * . + * | + * +- protocol_name_listener.h | + * | | + * +- protocol_name_listener.c |- add for emulation support + * | | + * +- protocol_name_listener_defs.h | + * | + * +- protocol_name_sync.h | + * | |- add for synchronous API support + * +- protocol_name_sync.c | + * | + * ``` + * + * Additionally, an arbitrary amount of private `protocol_name_*_i.h` header files may be created. Do not put implementation + * details in the regular header files, as they will be exposed in the public firmware API later on. + * + * ### 2.2 File structure explanation + * + * | Filename | Explanation | + * |:------------------------------|:------------| + * | protocol_name.h | Main protocol data structure and associated functions declarations. It is recommended to keep the former as opaque pointer. | + * | protocol_name.c | Implementations of functions declared in `protocol_name.h`. | + * | protocol_name_device_defs.h | Declarations for use by the NfcDevice library. See nfc_device_base_i.h for more info. | + * | protocol_name_poller.h | Protocol-specific poller and associated functions declarations. | + * | protocol_name_poller.c | Implementation of functions declared in `protocol_name_poller.h`. | + * | protocol_name_poller_defs.h | Declarations for use by the NfcPoller library. See nfc_poller_base.h for more info. | + * | protocol_name_listener.h | Protocol-specific listener and associated functions declarations. Optional, needed for emulation support. | + * | protocol_name_listener.c | Implementation of functions declared in `protocol_name_listener.h`. Optional, needed for emulation support. | + * | protocol_name_listener_defs.h | Declarations for use by the NfcListener library. See nfc_listener_base.h for more info. Optional, needed for emulation support. | + * | protocol_name_sync.h | Synchronous API declarations. (See below for sync API explanation). Optional.| + * | protocol_name_sync.c | Synchronous API implementation. Optional. | + * + * ## 3 Implement the code + * + * ### 3.1 Protocol data structure + * + * A protocol data structure is what holds all data that can be possibly read from a card of a certain type. It may include a unique identifier (UID), + * configuration bytes and flags, built-in memory, and so on. + * Additionally, it must implement the NfcDevice interface so that it could be used by the firmware. + * + * @see nfc_device_base_i.h for the device interface description. + * + * @note It is strongly recommended to implement such a structure as an opaque type and access it via specialised methods only. + * + * If the protocol being implemented is a child protocol, then its data must include a pointer to the parent protocol data structure. + * It is the protocol's responsibility to correctly create and delete the instance the pointer is pointing to. + * + * ### 3.2 Poller (reading functionality) + * + * A poller contains the functions necessary to successfully read a card of supported type. It must also implement a specific interface, + * namely described by the NfcPollerBase type. + * + * Upon creation, a poller instance will receive either a pointer to the Nfc instance (if it's a base protocol), or a pointer to another poller + * instance (if it is a child protocol) as the `alloc()` parameter. + * + * @see nfc_poller_base.h for the poller interface description. + * + * ### 3.3 Listener (emulation functionality) + * + * A listener implementation is optional, needed only when emulation is required/possible. + * + * Same considerations apply to the listener as for the poller. Additionally, upon creation it will receive an additional parameter + * in the form of a pointer to the matching protocol data structure, which will be used during emulation. The listener instance + * does not own this data and does not need to worry about its deletion. + * + * @see nfc_listener_base.h for the listener interface description. + * + * ### 3.4 Synchronous API + * + * Synchronous API does exaclty what it says -- it provides a set of blocking functions for easier use of pollers. + * Instead of creating all necessary instances and setting up callbacks manually, it does it automatically, at the + * expense of some flexibility. + * + * The most notable use of sync API is in the supported card plugins, so it's a good idea to implement it if such a plugin + * is to be implemented afterwards. + * + * ### 3.5 Registering the protocol + * + * After completing the protocol, it must be registered within the NfcProtocol system in order for it to be usable. + * + * 1. In nfc_protocol.h, add a new entry in the NfcProtocol enum in the form of NfcProtocolProtocolName. + * 2. In nfc_protocol.c, add a new entry in the `nfc_protocol_nodes[]` array under the appropriate index. + * 1. If it is a child protocol, register it as a child in the respective `nfc_base_protocol_name_children_protocol[]` array. + * 2. If the protocol has children on its own, create a `nfc_protocol_name_children_protocol[]` array + * with respective identifiers and register it in the protocol entry added in step 2. + * 3. In nfc_device_defs.c, include `protocol_name_device_defs.h` and add a pointer to the + * `protocol_name_device_defs` structure under the appropriate index. + * 4. In nfc_poller_defs.c, include `protocol_name_poller_defs.h` and add a pointer to the + * `protocol_name_poller_defs` structure under the appropriate index. + * 5. (Optional) If emulation support was implemented, do the step 4, but for the listener. + * 6. Add `protocol_name.h`, `protocol_name_poller.h`, and optionally, `protocol_name_listener.h` + * and `protocol_name_sync.h` into the `SDK_HEADERS` list in the SConscript file. + * This will export the protocol's types and functions for use by the applications. + * 7. Done! + * + * ## What's next? + * + * It's about time to integrate the newly implemented protocol into the main NFC application. Without that, reading a card + * of this type would crash it. + * + * @see nfc_protocol_support.h for more information on protocol integration. + * + * After having done that, a supported card plugin may be implemented to take further advantage of the new protocol. + * + * @see nfc_supported_card_plugin.h for more information on supported card plugins. + * + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Enumeration of all available NFC protocols. + * + * When implementing a new protocol, add its identifier before the + * NfcProtocolNum entry. + */ +typedef enum { + NfcProtocolIso14443_3a, + NfcProtocolIso14443_3b, + NfcProtocolIso14443_4a, + NfcProtocolIso14443_4b, + NfcProtocolIso15693_3, + NfcProtocolFelica, + NfcProtocolMfUltralight, + NfcProtocolMfClassic, + NfcProtocolMfDesfire, + NfcProtocolSlix, + NfcProtocolSt25tb, + /* Add new protocols here */ + + NfcProtocolNum, /**< Special value representing the number of available protocols. */ + + NfcProtocolInvalid, /**< Special value representing an invalid state. */ +} NfcProtocol; + +/** + * @brief Get the immediate parent of a specific protocol. + * + * @param[in] protocol identifier of the protocol in question. + * @returns parent protocol identifier if it has one, or NfcProtocolInvalid otherwise. + */ +NfcProtocol nfc_protocol_get_parent(NfcProtocol protocol); + +/** + * @brief Determine if a specific protocol has a parent on an arbitrary level. + * + * Unlike nfc_protocol_get_parent(), this function will traverse the full protocol hierarchy + * and check each parent node for the matching protocol type. + * + * @param[in] protocol identifier of the protocol in question. + * @param[in] parent_protocol identifier of the parent protocol in question. + * @returns true if the parent of given type exists, false otherwise. + */ +bool nfc_protocol_has_parent(NfcProtocol protocol, NfcProtocol parent_protocol); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/nfc_util.c b/lib/nfc/protocols/nfc_util.c deleted file mode 100644 index 9de6f982b63..00000000000 --- a/lib/nfc/protocols/nfc_util.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "nfc_util.h" - -#include - -static const uint8_t nfc_util_odd_byte_parity[256] = { - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, - 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, - 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, - 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, - 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, - 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, - 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1}; - -void nfc_util_num2bytes(uint64_t src, uint8_t len, uint8_t* dest) { - furi_assert(dest); - furi_assert(len <= 8); - - while(len--) { - dest[len] = (uint8_t)src; - src >>= 8; - } -} - -uint64_t nfc_util_bytes2num(uint8_t* src, uint8_t len) { - furi_assert(src); - furi_assert(len <= 8); - - uint64_t res = 0; - while(len--) { - res = (res << 8) | (*src); - src++; - } - return res; -} - -uint8_t nfc_util_even_parity32(uint32_t data) { - // data ^= data >> 16; - // data ^= data >> 8; - // return !nfc_util_odd_byte_parity[data]; - return (__builtin_parity(data) & 0xFF); -} - -uint8_t nfc_util_odd_parity8(uint8_t data) { - return nfc_util_odd_byte_parity[data]; -} diff --git a/lib/nfc/protocols/nfc_util.h b/lib/nfc/protocols/nfc_util.h deleted file mode 100644 index d530badc3d7..00000000000 --- a/lib/nfc/protocols/nfc_util.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -void nfc_util_num2bytes(uint64_t src, uint8_t len, uint8_t* dest); - -uint64_t nfc_util_bytes2num(uint8_t* src, uint8_t len); - -uint8_t nfc_util_even_parity32(uint32_t data); - -uint8_t nfc_util_odd_parity8(uint8_t data); diff --git a/lib/nfc/protocols/nfca.c b/lib/nfc/protocols/nfca.c deleted file mode 100755 index c401f8cc587..00000000000 --- a/lib/nfc/protocols/nfca.c +++ /dev/null @@ -1,142 +0,0 @@ -#include "nfca.h" -#include -#include -#include - -#define NFCA_CMD_RATS (0xE0U) - -#define NFCA_CRC_INIT (0x6363) - -#define NFCA_F_SIG (13560000.0) -#define T_SIG 7374 //73.746ns*100 -#define T_SIG_x8 58992 //T_SIG*8 -#define T_SIG_x8_x8 471936 //T_SIG*8*8 -#define T_SIG_x8_x9 530928 //T_SIG*8*9 - -#define NFCA_SIGNAL_MAX_EDGES (1350) - -typedef struct { - uint8_t cmd; - uint8_t param; -} nfca_cmd_rats; - -static uint8_t nfca_default_ats[] = {0x05, 0x78, 0x80, 0x80, 0x00}; - -static uint8_t nfca_sleep_req[] = {0x50, 0x00}; - -uint16_t nfca_get_crc16(uint8_t* buff, uint16_t len) { - uint16_t crc = NFCA_CRC_INIT; - uint8_t byte = 0; - - for(uint8_t i = 0; i < len; i++) { - byte = buff[i]; - byte ^= (uint8_t)(crc & 0xff); - byte ^= byte << 4; - crc = (crc >> 8) ^ (((uint16_t)byte) << 8) ^ (((uint16_t)byte) << 3) ^ - (((uint16_t)byte) >> 4); - } - - return crc; -} - -void nfca_append_crc16(uint8_t* buff, uint16_t len) { - uint16_t crc = nfca_get_crc16(buff, len); - buff[len] = (uint8_t)crc; - buff[len + 1] = (uint8_t)(crc >> 8); -} - -bool nfca_emulation_handler( - uint8_t* buff_rx, - uint16_t buff_rx_len, - uint8_t* buff_tx, - uint16_t* buff_tx_len) { - bool sleep = false; - uint8_t rx_bytes = buff_rx_len / 8; - - if(rx_bytes == sizeof(nfca_sleep_req) && !memcmp(buff_rx, nfca_sleep_req, rx_bytes)) { - sleep = true; - } else if(rx_bytes == sizeof(nfca_cmd_rats) && buff_rx[0] == NFCA_CMD_RATS) { - memcpy(buff_tx, nfca_default_ats, sizeof(nfca_default_ats)); - *buff_tx_len = sizeof(nfca_default_ats) * 8; - } - - return sleep; -} - -static void nfca_add_bit(DigitalSignal* signal, bool bit) { - if(bit) { - signal->start_level = true; - for(size_t i = 0; i < 7; i++) { - signal->edge_timings[i] = T_SIG_x8; - } - signal->edge_timings[7] = T_SIG_x8_x9; - signal->edge_cnt = 8; - } else { - signal->start_level = false; - signal->edge_timings[0] = T_SIG_x8_x8; - for(size_t i = 1; i < 9; i++) { - signal->edge_timings[i] = T_SIG_x8; - } - signal->edge_cnt = 9; - } -} - -static void nfca_add_byte(NfcaSignal* nfca_signal, uint8_t byte, bool parity) { - for(uint8_t i = 0; i < 8; i++) { - if(byte & (1 << i)) { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->one); - } else { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->zero); - } - } - if(parity) { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->one); - } else { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->zero); - } -} - -NfcaSignal* nfca_signal_alloc() { - NfcaSignal* nfca_signal = malloc(sizeof(NfcaSignal)); - nfca_signal->one = digital_signal_alloc(10); - nfca_signal->zero = digital_signal_alloc(10); - nfca_add_bit(nfca_signal->one, true); - nfca_add_bit(nfca_signal->zero, false); - nfca_signal->tx_signal = digital_signal_alloc(NFCA_SIGNAL_MAX_EDGES); - - return nfca_signal; -} - -void nfca_signal_free(NfcaSignal* nfca_signal) { - furi_assert(nfca_signal); - - digital_signal_free(nfca_signal->one); - digital_signal_free(nfca_signal->zero); - digital_signal_free(nfca_signal->tx_signal); - free(nfca_signal); -} - -void nfca_signal_encode(NfcaSignal* nfca_signal, uint8_t* data, uint16_t bits, uint8_t* parity) { - furi_assert(nfca_signal); - furi_assert(data); - furi_assert(parity); - - nfca_signal->tx_signal->edge_cnt = 0; - nfca_signal->tx_signal->start_level = true; - // Start of frame - digital_signal_append(nfca_signal->tx_signal, nfca_signal->one); - - if(bits < 8) { - for(size_t i = 0; i < bits; i++) { - if(FURI_BIT(data[0], i)) { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->one); - } else { - digital_signal_append(nfca_signal->tx_signal, nfca_signal->zero); - } - } - } else { - for(size_t i = 0; i < bits / 8; i++) { - nfca_add_byte(nfca_signal, data[i], parity[i / 8] & (1 << (7 - (i & 0x07)))); - } - } -} diff --git a/lib/nfc/protocols/nfca.h b/lib/nfc/protocols/nfca.h deleted file mode 100644 index 498ef28431c..00000000000 --- a/lib/nfc/protocols/nfca.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include - -#include - -typedef struct { - DigitalSignal* one; - DigitalSignal* zero; - DigitalSignal* tx_signal; -} NfcaSignal; - -uint16_t nfca_get_crc16(uint8_t* buff, uint16_t len); - -void nfca_append_crc16(uint8_t* buff, uint16_t len); - -bool nfca_emulation_handler( - uint8_t* buff_rx, - uint16_t buff_rx_len, - uint8_t* buff_tx, - uint16_t* buff_tx_len); - -NfcaSignal* nfca_signal_alloc(); - -void nfca_signal_free(NfcaSignal* nfca_signal); - -void nfca_signal_encode(NfcaSignal* nfca_signal, uint8_t* data, uint16_t bits, uint8_t* parity); diff --git a/lib/nfc/protocols/slix/slix.c b/lib/nfc/protocols/slix/slix.c new file mode 100644 index 00000000000..12dc6750dd1 --- /dev/null +++ b/lib/nfc/protocols/slix/slix.c @@ -0,0 +1,433 @@ +#include "slix_i.h" +#include "slix_device_defs.h" + +#include +#include + +#define SLIX_PROTOCOL_NAME "SLIX" +#define SLIX_DEVICE_NAME "SLIX" + +#define SLIX_TYPE_SLIX_SLIX2 (0x01U) +#define SLIX_TYPE_SLIX_S (0x02U) +#define SLIX_TYPE_SLIX_L (0x03U) + +#define SLIX_TYPE_INDICATOR_SLIX (0x02U) +#define SLIX_TYPE_INDICATOR_SLIX2 (0x01U) + +#define SLIX_PASSWORD_READ_KEY "Password Read" +#define SLIX_PASSWORD_WRITE_KEY "Password Write" +#define SLIX_PASSWORD_PRIVACY_KEY "Password Privacy" +#define SLIX_PASSWORD_DESTROY_KEY "Password Destroy" +#define SLIX_PASSWORD_EAS_KEY "Password EAS" +#define SLIX_SIGNATURE_KEY "Signature" +#define SLIX_PRIVACY_MODE_KEY "Privacy Mode" +#define SLIX_PROTECTION_POINTER_KEY "Protection Pointer" +#define SLIX_PROTECTION_CONDITION_KEY "Protection Condition" +#define SLIX_LOCK_EAS_KEY "Lock EAS" +#define SLIX_LOCK_PPL_KEY "Lock PPL" + +typedef struct { + uint8_t iso15693_3[2]; + uint8_t icode_type; + union { + struct { + uint8_t unused_1 : 3; + uint8_t type_indicator : 2; + uint8_t unused_2 : 3; + }; + uint8_t serial_num[5]; + }; +} SlixUidLayout; + +const NfcDeviceBase nfc_device_slix = { + .protocol_name = SLIX_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)slix_alloc, + .free = (NfcDeviceFree)slix_free, + .reset = (NfcDeviceReset)slix_reset, + .copy = (NfcDeviceCopy)slix_copy, + .verify = (NfcDeviceVerify)slix_verify, + .load = (NfcDeviceLoad)slix_load, + .save = (NfcDeviceSave)slix_save, + .is_equal = (NfcDeviceEqual)slix_is_equal, + .get_name = (NfcDeviceGetName)slix_get_device_name, + .get_uid = (NfcDeviceGetUid)slix_get_uid, + .set_uid = (NfcDeviceSetUid)slix_set_uid, + .get_base_data = (NfcDeviceGetBaseData)slix_get_base_data, +}; + +static const char* slix_nfc_device_name[] = { + [SlixTypeSlix] = SLIX_DEVICE_NAME, + [SlixTypeSlixS] = SLIX_DEVICE_NAME "-S", + [SlixTypeSlixL] = SLIX_DEVICE_NAME "-L", + [SlixTypeSlix2] = SLIX_DEVICE_NAME "2", +}; + +static const SlixTypeFeatures slix_type_features[] = { + [SlixTypeSlix] = SLIX_TYPE_FEATURES_SLIX, + [SlixTypeSlixS] = SLIX_TYPE_FEATURES_SLIX_S, + [SlixTypeSlixL] = SLIX_TYPE_FEATURES_SLIX_L, + [SlixTypeSlix2] = SLIX_TYPE_FEATURES_SLIX2, +}; + +typedef struct { + const char* key; + SlixTypeFeatures feature_flag; + SlixPassword default_value; +} SlixPasswordConfig; + +static const SlixPasswordConfig slix_password_configs[] = { + [SlixPasswordTypeRead] = {SLIX_PASSWORD_READ_KEY, SLIX_TYPE_FEATURE_READ, 0x00000000U}, + [SlixPasswordTypeWrite] = {SLIX_PASSWORD_WRITE_KEY, SLIX_TYPE_FEATURE_WRITE, 0x00000000U}, + [SlixPasswordTypePrivacy] = {SLIX_PASSWORD_PRIVACY_KEY, SLIX_TYPE_FEATURE_PRIVACY, 0xFFFFFFFFU}, + [SlixPasswordTypeDestroy] = {SLIX_PASSWORD_DESTROY_KEY, SLIX_TYPE_FEATURE_DESTROY, 0xFFFFFFFFU}, + [SlixPasswordTypeEasAfi] = {SLIX_PASSWORD_EAS_KEY, SLIX_TYPE_FEATURE_EAS, 0x00000000U}, +}; + +static void slix_password_set_defaults(SlixPassword* passwords) { + for(uint32_t i = 0; i < COUNT_OF(slix_password_configs); ++i) { + passwords[i] = slix_password_configs[i].default_value; + } +} + +SlixData* slix_alloc() { + SlixData* data = malloc(sizeof(SlixData)); + + data->iso15693_3_data = iso15693_3_alloc(); + slix_password_set_defaults(data->passwords); + + return data; +} + +void slix_free(SlixData* data) { + furi_assert(data); + + iso15693_3_free(data->iso15693_3_data); + + free(data); +} + +void slix_reset(SlixData* data) { + furi_assert(data); + + iso15693_3_reset(data->iso15693_3_data); + slix_password_set_defaults(data->passwords); + + memset(&data->system_info, 0, sizeof(SlixSystemInfo)); + memset(data->signature, 0, sizeof(SlixSignature)); + + data->privacy = false; +} + +void slix_copy(SlixData* data, const SlixData* other) { + furi_assert(data); + furi_assert(other); + + iso15693_3_copy(data->iso15693_3_data, other->iso15693_3_data); + + memcpy(data->passwords, other->passwords, sizeof(SlixPassword) * SlixPasswordTypeCount); + memcpy(data->signature, other->signature, sizeof(SlixSignature)); + + data->system_info = other->system_info; + data->privacy = other->privacy; +} + +bool slix_verify(SlixData* data, const FuriString* device_type) { + UNUSED(data); + UNUSED(device_type); + // No backward compatibility, unified format only + return false; +} + +static bool slix_load_passwords(SlixPassword* passwords, SlixType slix_type, FlipperFormat* ff) { + bool ret = true; + + for(uint32_t i = 0; i < COUNT_OF(slix_password_configs); ++i) { + const SlixPasswordConfig* password_config = &slix_password_configs[i]; + + if(!slix_type_has_features(slix_type, password_config->feature_flag)) continue; + if(!flipper_format_key_exist(ff, password_config->key)) { + passwords[i] = password_config->default_value; + continue; + } + if(!flipper_format_read_hex( + ff, password_config->key, (uint8_t*)&passwords[i], sizeof(SlixPassword))) { + ret = false; + break; + } + } + + return ret; +} + +bool slix_load(SlixData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + + bool loaded = false; + + do { + if(!iso15693_3_load(data->iso15693_3_data, ff, version)) break; + + const SlixType slix_type = slix_get_type(data); + if(slix_type >= SlixTypeCount) break; + + if(!slix_load_passwords(data->passwords, slix_type, ff)) break; + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_SIGNATURE)) { + if(flipper_format_key_exist(ff, SLIX_SIGNATURE_KEY)) { + if(!flipper_format_read_hex( + ff, SLIX_SIGNATURE_KEY, data->signature, SLIX_SIGNATURE_SIZE)) + break; + } + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PRIVACY)) { + if(flipper_format_key_exist(ff, SLIX_PRIVACY_MODE_KEY)) { + if(!flipper_format_read_bool(ff, SLIX_PRIVACY_MODE_KEY, &data->privacy, 1)) break; + } + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) { + SlixProtection* protection = &data->system_info.protection; + if(flipper_format_key_exist(ff, SLIX_PROTECTION_POINTER_KEY) && + flipper_format_key_exist(ff, SLIX_PROTECTION_CONDITION_KEY)) { + if(!flipper_format_read_hex( + ff, SLIX_PROTECTION_POINTER_KEY, &protection->pointer, 1)) + break; + if(!flipper_format_read_hex( + ff, SLIX_PROTECTION_CONDITION_KEY, &protection->condition, 1)) + break; + } + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_EAS)) { + if(flipper_format_key_exist(ff, SLIX_LOCK_EAS_KEY)) { + SlixLockBits* lock_bits = &data->system_info.lock_bits; + if(!flipper_format_read_bool(ff, SLIX_LOCK_EAS_KEY, &lock_bits->eas, 1)) break; + } + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) { + if(flipper_format_key_exist(ff, SLIX_LOCK_PPL_KEY)) { + SlixLockBits* lock_bits = &data->system_info.lock_bits; + if(!flipper_format_read_bool(ff, SLIX_LOCK_PPL_KEY, &lock_bits->ppl, 1)) break; + } + } + + loaded = true; + } while(false); + + return loaded; +} + +static bool + slix_save_passwords(const SlixPassword* passwords, SlixType slix_type, FlipperFormat* ff) { + bool ret = true; + + for(uint32_t i = 0; i < COUNT_OF(slix_password_configs); ++i) { + const SlixPasswordConfig* password_config = &slix_password_configs[i]; + + if(!slix_type_has_features(slix_type, password_config->feature_flag)) continue; + if(!flipper_format_write_hex( + ff, password_config->key, (uint8_t*)&passwords[i], sizeof(SlixPassword))) { + ret = false; + break; + } + } + + return ret; +} + +bool slix_save(const SlixData* data, FlipperFormat* ff) { + furi_assert(data); + + bool saved = false; + + do { + const SlixType slix_type = slix_get_type(data); + if(slix_type >= SlixTypeCount) break; + + if(!iso15693_3_save(data->iso15693_3_data, ff)) break; + if(!flipper_format_write_comment_cstr(ff, SLIX_PROTOCOL_NAME " specific data")) break; + + if(!flipper_format_write_comment_cstr( + ff, + "Passwords are optional. If a password is omitted, a default value will be used")) + break; + + if(!slix_save_passwords(data->passwords, slix_type, ff)) break; + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_SIGNATURE)) { + if(!flipper_format_write_comment_cstr( + ff, + "This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key.")) + break; + if(!flipper_format_write_hex( + ff, SLIX_SIGNATURE_KEY, data->signature, SLIX_SIGNATURE_SIZE)) + break; + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PRIVACY)) { + if(!flipper_format_write_bool(ff, SLIX_PRIVACY_MODE_KEY, &data->privacy, 1)) break; + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) { + const SlixProtection* protection = &data->system_info.protection; + if(!flipper_format_write_comment_cstr(ff, "Protection pointer configuration")) break; + if(!flipper_format_write_hex( + ff, SLIX_PROTECTION_POINTER_KEY, &protection->pointer, sizeof(uint8_t))) + break; + if(!flipper_format_write_hex( + ff, SLIX_PROTECTION_CONDITION_KEY, &protection->condition, sizeof(uint8_t))) + break; + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_EAS) || + slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) { + if(!flipper_format_write_comment_cstr(ff, "SLIX Lock Bits")) break; + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_EAS)) { + const SlixLockBits* lock_bits = &data->system_info.lock_bits; + if(!flipper_format_write_bool(ff, SLIX_LOCK_EAS_KEY, &lock_bits->eas, 1)) break; + } + + if(slix_type_has_features(slix_type, SLIX_TYPE_FEATURE_PROTECTION)) { + const SlixLockBits* lock_bits = &data->system_info.lock_bits; + if(!flipper_format_write_bool(ff, SLIX_LOCK_PPL_KEY, &lock_bits->ppl, 1)) break; + } + + saved = true; + } while(false); + + return saved; +} + +bool slix_is_equal(const SlixData* data, const SlixData* other) { + return iso15693_3_is_equal(data->iso15693_3_data, other->iso15693_3_data) && + memcmp(&data->system_info, &other->system_info, sizeof(SlixSystemInfo)) == 0 && + memcmp( + data->passwords, other->passwords, sizeof(SlixPassword) * SlixPasswordTypeCount) == + 0 && + memcmp(&data->signature, &other->signature, sizeof(SlixSignature)) == 0 && + data->privacy == other->privacy; +} + +const char* slix_get_device_name(const SlixData* data, NfcDeviceNameType name_type) { + UNUSED(name_type); + + const SlixType slix_type = slix_get_type(data); + furi_assert(slix_type < SlixTypeCount); + + return slix_nfc_device_name[slix_type]; +} + +const uint8_t* slix_get_uid(const SlixData* data, size_t* uid_len) { + return iso15693_3_get_uid(data->iso15693_3_data, uid_len); +} + +bool slix_set_uid(SlixData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + return iso15693_3_set_uid(data->iso15693_3_data, uid, uid_len); +} + +const Iso15693_3Data* slix_get_base_data(const SlixData* data) { + furi_assert(data); + + return data->iso15693_3_data; +} + +SlixType slix_get_type(const SlixData* data) { + SlixType type = SlixTypeCount; + + do { + if(iso15693_3_get_manufacturer_id(data->iso15693_3_data) != SLIX_NXP_MANUFACTURER_CODE) + break; + + const SlixUidLayout* uid = (const SlixUidLayout*)data->iso15693_3_data->uid; + + if(uid->icode_type == SLIX_TYPE_SLIX_SLIX2) { + if(uid->type_indicator == SLIX_TYPE_INDICATOR_SLIX) { + type = SlixTypeSlix; + } else if(uid->type_indicator == SLIX_TYPE_INDICATOR_SLIX2) { + type = SlixTypeSlix2; + } + } else if(uid->icode_type == SLIX_TYPE_SLIX_S) { + type = SlixTypeSlixS; + } else if(uid->icode_type == SLIX_TYPE_SLIX_L) { + type = SlixTypeSlixL; + } + + } while(false); + + return type; +} + +SlixPassword slix_get_password(const SlixData* data, SlixPasswordType password_type) { + furi_assert(data); + furi_assert(password_type < SlixPasswordTypeCount); + + return data->passwords[password_type]; +} + +uint16_t slix_get_counter(const SlixData* data) { + furi_assert(data); + const SlixCounter* counter = (const SlixCounter*)iso15693_3_get_block_data( + data->iso15693_3_data, SLIX_COUNTER_BLOCK_NUM); + + return counter->value; +} + +bool slix_is_privacy_mode(const SlixData* data) { + furi_assert(data); + + return data->privacy; +} + +bool slix_is_block_protected( + const SlixData* data, + SlixPasswordType password_type, + uint8_t block_num) { + furi_assert(data); + furi_assert(password_type < SlixPasswordTypeCount); + + bool ret = false; + + do { + if(password_type != SlixPasswordTypeRead && password_type != SlixPasswordTypeWrite) break; + if(block_num >= iso15693_3_get_block_count(data->iso15693_3_data)) break; + if(block_num == SLIX_COUNTER_BLOCK_NUM) break; + + const bool high = block_num >= data->system_info.protection.pointer; + const bool read = password_type == SlixPasswordTypeRead; + + const uint8_t condition = high ? (read ? SLIX_PP_CONDITION_RH : SLIX_PP_CONDITION_WH) : + (read ? SLIX_PP_CONDITION_RL : SLIX_PP_CONDITION_WL); + + ret = data->system_info.protection.condition & condition; + } while(false); + + return ret; +} + +bool slix_is_counter_increment_protected(const SlixData* data) { + furi_assert(data); + + const SlixCounter* counter = (const SlixCounter*)iso15693_3_get_block_data( + data->iso15693_3_data, SLIX_COUNTER_BLOCK_NUM); + + return counter->protection != 0; +} + +bool slix_type_has_features(SlixType slix_type, SlixTypeFeatures features) { + furi_assert(slix_type < SlixTypeCount); + + return (slix_type_features[slix_type] & features) == features; +} + +bool slix_type_supports_password(SlixType slix_type, SlixPasswordType password_type) { + furi_assert(slix_type < SlixTypeCount); + furi_assert(password_type < SlixPasswordTypeCount); + + return slix_type_features[slix_type] & slix_password_configs[password_type].feature_flag; +} diff --git a/lib/nfc/protocols/slix/slix.h b/lib/nfc/protocols/slix/slix.h new file mode 100644 index 00000000000..f6c1453c5dd --- /dev/null +++ b/lib/nfc/protocols/slix/slix.h @@ -0,0 +1,146 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SLIX_BLOCK_SIZE (4U) +#define SLIX_SIGNATURE_SIZE (32U) + +#define SLIX_COUNTER_BLOCK_NUM (79U) + +#define SLIX_PP_CONDITION_RL (1U << 0) +#define SLIX_PP_CONDITION_WL (1U << 1) +#define SLIX_PP_CONDITION_RH (1U << 4) +#define SLIX_PP_CONDITION_WH (1U << 5) + +#define SLIX_FEATURE_FLAG_UM_PP (1UL << 0) +#define SLIX_FEATURE_FLAG_COUNTER (1UL << 1) +#define SLIX_FEATURE_FLAG_EAS_ID (1UL << 2) +#define SLIX_FEATURE_FLAG_EAS_PP (1UL << 3) +#define SLIX_FEATURE_FLAG_AFI_PP (1UL << 4) +#define SLIX_FEATURE_FLAG_INVENTORY_READ_EXT (1UL << 5) +#define SLIX_FEATURE_FLAG_EAS_IR (1UL << 6) +#define SLIX_FEATURE_FLAG_ORIGINALITY_SIG (1UL << 8) +#define SLIX_FEATURE_FLAG_ORIGINALITY_SIG_PP (1UL << 9) +#define SLIX_FEATURE_FLAG_PERSISTENT_QUIET (1UL << 10) +#define SLIX_FEATURE_FLAG_PRIVACY (1UL << 12) +#define SLIX_FEATURE_FLAG_DESTROY (1UL << 13) +#define SLIX_FEATURE_EXT (1UL << 31) + +#define SLIX_TYPE_FEATURE_READ (1U << 0) +#define SLIX_TYPE_FEATURE_WRITE (1U << 1) +#define SLIX_TYPE_FEATURE_PRIVACY (1U << 2) +#define SLIX_TYPE_FEATURE_DESTROY (1U << 3) +#define SLIX_TYPE_FEATURE_EAS (1U << 4) +#define SLIX_TYPE_FEATURE_SIGNATURE (1U << 5) +#define SLIX_TYPE_FEATURE_PROTECTION (1U << 6) + +typedef uint32_t SlixTypeFeatures; + +typedef enum { + SlixErrorNone, + SlixErrorTimeout, + SlixErrorFormat, + SlixErrorNotSupported, + SlixErrorInternal, + SlixErrorWrongPassword, + SlixErrorUidMismatch, + SlixErrorUnknown, +} SlixError; + +typedef enum { + SlixTypeSlix, + SlixTypeSlixS, + SlixTypeSlixL, + SlixTypeSlix2, + SlixTypeCount, +} SlixType; + +typedef enum { + SlixPasswordTypeRead, + SlixPasswordTypeWrite, + SlixPasswordTypePrivacy, + SlixPasswordTypeDestroy, + SlixPasswordTypeEasAfi, + SlixPasswordTypeCount, +} SlixPasswordType; + +typedef uint32_t SlixPassword; +typedef uint8_t SlixSignature[SLIX_SIGNATURE_SIZE]; +typedef bool SlixPrivacy; + +typedef struct { + uint8_t pointer; + uint8_t condition; +} SlixProtection; + +typedef struct { + bool eas; + bool ppl; +} SlixLockBits; + +typedef struct { + SlixProtection protection; + SlixLockBits lock_bits; +} SlixSystemInfo; + +typedef struct { + Iso15693_3Data* iso15693_3_data; + SlixSystemInfo system_info; + SlixSignature signature; + SlixPassword passwords[SlixPasswordTypeCount]; + SlixPrivacy privacy; +} SlixData; + +SlixData* slix_alloc(); + +void slix_free(SlixData* data); + +void slix_reset(SlixData* data); + +void slix_copy(SlixData* data, const SlixData* other); + +bool slix_verify(SlixData* data, const FuriString* device_type); + +bool slix_load(SlixData* data, FlipperFormat* ff, uint32_t version); + +bool slix_save(const SlixData* data, FlipperFormat* ff); + +bool slix_is_equal(const SlixData* data, const SlixData* other); + +const char* slix_get_device_name(const SlixData* data, NfcDeviceNameType name_type); + +const uint8_t* slix_get_uid(const SlixData* data, size_t* uid_len); + +bool slix_set_uid(SlixData* data, const uint8_t* uid, size_t uid_len); + +const Iso15693_3Data* slix_get_base_data(const SlixData* data); + +// Getters and tests + +SlixType slix_get_type(const SlixData* data); + +SlixPassword slix_get_password(const SlixData* data, SlixPasswordType password_type); + +uint16_t slix_get_counter(const SlixData* data); + +bool slix_is_privacy_mode(const SlixData* data); + +bool slix_is_block_protected( + const SlixData* data, + SlixPasswordType password_type, + uint8_t block_num); + +bool slix_is_counter_increment_protected(const SlixData* data); + +// Static methods +bool slix_type_has_features(SlixType slix_type, SlixTypeFeatures features); + +bool slix_type_supports_password(SlixType slix_type, SlixPasswordType password_type); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/slix/slix_device_defs.h b/lib/nfc/protocols/slix/slix_device_defs.h new file mode 100644 index 00000000000..cadd40eecd7 --- /dev/null +++ b/lib/nfc/protocols/slix/slix_device_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcDeviceBase nfc_device_slix; diff --git a/lib/nfc/protocols/slix/slix_i.c b/lib/nfc/protocols/slix/slix_i.c new file mode 100644 index 00000000000..97d66484c01 --- /dev/null +++ b/lib/nfc/protocols/slix/slix_i.c @@ -0,0 +1,127 @@ +#include "slix_i.h" + +#include + +bool slix_error_response_parse(SlixError* error, const BitBuffer* buf) { + Iso15693_3Error iso15693_3_error; + const bool ret = iso15693_3_error_response_parse(&iso15693_3_error, buf); + + if(ret) { + *error = slix_process_iso15693_3_error(iso15693_3_error); + } + + return ret; +} + +SlixError slix_process_iso15693_3_error(Iso15693_3Error iso15693_3_error) { + switch(iso15693_3_error) { + case Iso15693_3ErrorNone: + return SlixErrorNone; + case Iso15693_3ErrorTimeout: + return SlixErrorTimeout; + case Iso15693_3ErrorFormat: + return SlixErrorFormat; + case Iso15693_3ErrorInternal: + return SlixErrorInternal; + default: + return SlixErrorUnknown; + } +} + +SlixError slix_get_nxp_system_info_response_parse(SlixData* data, const BitBuffer* buf) { + furi_assert(data); + SlixError error = SlixErrorNone; + + do { + if(slix_error_response_parse(&error, buf)) break; + + typedef struct { + uint8_t flags; + uint8_t pp_pointer; + uint8_t pp_condition; + uint8_t lock_bits; + uint32_t feature_flags; + } SlixGetNxpSystemInfoResponseLayout; + + const size_t size_received = bit_buffer_get_size_bytes(buf); + const size_t size_required = sizeof(SlixGetNxpSystemInfoResponseLayout); + + if(size_received != size_required) { + error = SlixErrorFormat; + break; + } + + const SlixGetNxpSystemInfoResponseLayout* response = + (const SlixGetNxpSystemInfoResponseLayout*)bit_buffer_get_data(buf); + + SlixProtection* protection = &data->system_info.protection; + protection->pointer = response->pp_pointer; + protection->condition = response->pp_condition; + + Iso15693_3LockBits* iso15693_3_lock_bits = &data->iso15693_3_data->settings.lock_bits; + iso15693_3_lock_bits->dsfid = response->lock_bits & SLIX_LOCK_BITS_DSFID; + iso15693_3_lock_bits->afi = response->lock_bits & SLIX_LOCK_BITS_AFI; + + SlixLockBits* slix_lock_bits = &data->system_info.lock_bits; + slix_lock_bits->eas = response->lock_bits & SLIX_LOCK_BITS_EAS; + slix_lock_bits->ppl = response->lock_bits & SLIX_LOCK_BITS_PPL; + + } while(false); + + return error; +} + +SlixError slix_read_signature_response_parse(SlixSignature data, const BitBuffer* buf) { + SlixError error = SlixErrorNone; + + do { + if(slix_error_response_parse(&error, buf)) break; + + typedef struct { + uint8_t flags; + uint8_t signature[SLIX_SIGNATURE_SIZE]; + } SlixReadSignatureResponseLayout; + + const size_t size_received = bit_buffer_get_size_bytes(buf); + const size_t size_required = sizeof(SlixReadSignatureResponseLayout); + + if(size_received != size_required) { + error = SlixErrorFormat; + break; + } + + const SlixReadSignatureResponseLayout* response = + (const SlixReadSignatureResponseLayout*)bit_buffer_get_data(buf); + + memcpy(data, response->signature, sizeof(SlixSignature)); + } while(false); + + return error; +} + +void slix_set_password(SlixData* data, SlixPasswordType password_type, SlixPassword password) { + furi_assert(data); + furi_assert(password_type < SlixPasswordTypeCount); + + data->passwords[password_type] = password; +} + +void slix_set_privacy_mode(SlixData* data, bool set) { + furi_assert(data); + + data->privacy = set; +} + +void slix_increment_counter(SlixData* data) { + furi_assert(data); + + const uint8_t* block_data = + iso15693_3_get_block_data(data->iso15693_3_data, SLIX_COUNTER_BLOCK_NUM); + + SlixCounter counter; + memcpy(counter.bytes, block_data, SLIX_BLOCK_SIZE); + counter.value += 1; + + iso15693_3_set_block_data( + data->iso15693_3_data, SLIX_COUNTER_BLOCK_NUM, counter.bytes, sizeof(SlixCounter)); +} diff --git a/lib/nfc/protocols/slix/slix_i.h b/lib/nfc/protocols/slix/slix_i.h new file mode 100644 index 00000000000..b5e445f31d0 --- /dev/null +++ b/lib/nfc/protocols/slix/slix_i.h @@ -0,0 +1,86 @@ +#pragma once + +#include "slix.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SLIX_NXP_MANUFACTURER_CODE (0x04U) + +#define SLIX_LOCK_BITS_AFI (1U << 0) +#define SLIX_LOCK_BITS_EAS (1U << 1) +#define SLIX_LOCK_BITS_DSFID (1U << 2) +#define SLIX_LOCK_BITS_PPL (1U << 3) + +#define SLIX_CMD_CUSTOM_START (0xA2U) +#define SLIX_CMD_SET_EAS (0xA2U) +#define SLIX_CMD_RESET_EAS (0xA3U) +#define SLIX_CMD_LOCK_EAS (0xA4U) +#define SLIX_CMD_EAS_ALARM (0xA5U) +#define SLIX_CMD_PASSWORD_PROTECT_EAS_AFI (0xA6U) +#define SLIX_CMD_WRITE_EAS_ID (0xA7U) +#define SLIX_CMD_GET_NXP_SYSTEM_INFORMATION (0xABU) +#define SLIX_CMD_INVENTORY_PAGE_READ (0xB0U) +#define SLIX_CMD_INVENTORY_PAGE_READ_FAST (0xB1U) +#define SLIX_CMD_GET_RANDOM_NUMBER (0xB2U) +#define SLIX_CMD_SET_PASSWORD (0xB3U) +#define SLIX_CMD_WRITE_PASSWORD (0xB4U) +#define SLIX_CMD_64_BIT_PASSWORD_PROTECTION (0xB5U) +#define SLIX_CMD_PROTECT_PAGE (0xB6U) +#define SLIX_CMD_LOCK_PAGE_PROTECTION_CONDITION (0xB7U) +#define SLIX_CMD_DESTROY (0xB9U) +#define SLIX_CMD_ENABLE_PRIVACY (0xBAU) +#define SLIX_CMD_STAY_QUIET_PERSISTENT (0xBCU) +#define SLIX_CMD_READ_SIGNATURE (0xBDU) +#define SLIX_CMD_CUSTOM_END (0xBEU) +#define SLIX_CMD_CUSTOM_COUNT (SLIX_CMD_CUSTOM_END - SLIX_CMD_CUSTOM_START) + +#define SLIX_TYPE_FEATURES_SLIX (SLIX_TYPE_FEATURE_EAS) +#define SLIX_TYPE_FEATURES_SLIX_S \ + (SLIX_TYPE_FEATURE_READ | SLIX_TYPE_FEATURE_WRITE | SLIX_TYPE_FEATURE_PRIVACY | \ + SLIX_TYPE_FEATURE_DESTROY | SLIX_TYPE_FEATURE_EAS) +#define SLIX_TYPE_FEATURES_SLIX_L \ + (SLIX_TYPE_FEATURE_PRIVACY | SLIX_TYPE_FEATURE_DESTROY | SLIX_TYPE_FEATURE_EAS) +#define SLIX_TYPE_FEATURES_SLIX2 \ + (SLIX_TYPE_FEATURE_READ | SLIX_TYPE_FEATURE_WRITE | SLIX_TYPE_FEATURE_PRIVACY | \ + SLIX_TYPE_FEATURE_DESTROY | SLIX_TYPE_FEATURE_EAS | SLIX_TYPE_FEATURE_SIGNATURE | \ + SLIX_TYPE_FEATURE_PROTECTION) + +#define SLIX2_FEATURE_FLAGS \ + (SLIX_FEATURE_FLAG_UM_PP | SLIX_FEATURE_FLAG_COUNTER | SLIX_FEATURE_FLAG_EAS_ID | \ + SLIX_FEATURE_FLAG_EAS_PP | SLIX_FEATURE_FLAG_AFI_PP | SLIX_FEATURE_FLAG_INVENTORY_READ_EXT | \ + SLIX_FEATURE_FLAG_EAS_IR | SLIX_FEATURE_FLAG_ORIGINALITY_SIG | \ + SLIX_FEATURE_FLAG_PERSISTENT_QUIET | SLIX_FEATURE_FLAG_PRIVACY | SLIX_FEATURE_FLAG_DESTROY) + +typedef union { + struct { + uint16_t value; + uint8_t reserved; + uint8_t protection; + }; + uint8_t bytes[SLIX_BLOCK_SIZE]; +} SlixCounter; + +// Same behaviour as iso15693_3_error_response_parse +bool slix_error_response_parse(SlixError* error, const BitBuffer* buf); + +SlixError slix_process_iso15693_3_error(Iso15693_3Error iso15693_3_error); + +SlixError slix_get_nxp_system_info_response_parse(SlixData* data, const BitBuffer* buf); + +SlixError slix_read_signature_response_parse(SlixSignature data, const BitBuffer* buf); + +// Setters +void slix_set_password(SlixData* data, SlixPasswordType password_type, SlixPassword password); + +void slix_set_privacy_mode(SlixData* data, bool set); + +void slix_increment_counter(SlixData* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/slix/slix_listener.c b/lib/nfc/protocols/slix/slix_listener.c new file mode 100644 index 00000000000..204be5ab91a --- /dev/null +++ b/lib/nfc/protocols/slix/slix_listener.c @@ -0,0 +1,79 @@ +#include "slix_listener_i.h" + +#include +#include + +#define TAG "SlixListener" + +#define SLIX_LISTENER_BUF_SIZE (64U) + +static SlixListener* slix_listener_alloc(Iso15693_3Listener* iso15693_3_listener, SlixData* data) { + furi_assert(iso15693_3_listener); + + SlixListener* instance = malloc(sizeof(SlixListener)); + instance->iso15693_3_listener = iso15693_3_listener; + instance->data = data; + + instance->tx_buffer = bit_buffer_alloc(SLIX_LISTENER_BUF_SIZE); + + instance->slix_event.data = &instance->slix_event_data; + instance->generic_event.protocol = NfcProtocolSlix; + instance->generic_event.instance = instance; + instance->generic_event.event_data = &instance->slix_event; + + slix_listener_init_iso15693_3_extensions(instance); + + return instance; +} + +static void slix_listener_free(SlixListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + furi_assert(instance->tx_buffer); + + bit_buffer_free(instance->tx_buffer); + free(instance); +} + +static void + slix_listener_set_callback(SlixListener* instance, NfcGenericCallback callback, void* context) { + furi_assert(instance); + + instance->callback = callback; + instance->context = context; +} + +static const SlixData* slix_listener_get_data(SlixListener* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +static NfcCommand slix_listener_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolIso15693_3); + furi_assert(event.event_data); + + SlixListener* instance = context; + Iso15693_3ListenerEvent* iso15693_3_event = event.event_data; + BitBuffer* rx_buffer = iso15693_3_event->data->buffer; + NfcCommand command = NfcCommandContinue; + + if(iso15693_3_event->type == Iso15693_3ListenerEventTypeCustomCommand) { + const SlixError error = slix_listener_process_request(instance, rx_buffer); + if(error == SlixErrorWrongPassword) { + command = NfcCommandStop; + } + } + + return command; +} + +const NfcListenerBase nfc_listener_slix = { + .alloc = (NfcListenerAlloc)slix_listener_alloc, + .free = (NfcListenerFree)slix_listener_free, + .set_callback = (NfcListenerSetCallback)slix_listener_set_callback, + .get_data = (NfcListenerGetData)slix_listener_get_data, + .run = (NfcListenerRun)slix_listener_run, +}; diff --git a/lib/nfc/protocols/slix/slix_listener.h b/lib/nfc/protocols/slix/slix_listener.h new file mode 100644 index 00000000000..7b8518a5bb7 --- /dev/null +++ b/lib/nfc/protocols/slix/slix_listener.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "slix.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SlixListener SlixListener; + +typedef enum { + SlixListenerEventTypeFieldOff, + SlixListenerEventTypeCustomCommand, +} SlixListenerEventType; + +typedef struct { + BitBuffer* buffer; +} SlixListenerEventData; + +typedef struct { + SlixListenerEventType type; + SlixListenerEventData* data; +} SlixListenerEvent; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/slix/slix_listener_defs.h b/lib/nfc/protocols/slix/slix_listener_defs.h new file mode 100644 index 00000000000..0598090c473 --- /dev/null +++ b/lib/nfc/protocols/slix/slix_listener_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcListenerBase nfc_listener_slix; diff --git a/lib/nfc/protocols/slix/slix_listener_i.c b/lib/nfc/protocols/slix/slix_listener_i.c new file mode 100644 index 00000000000..dfcb6c88017 --- /dev/null +++ b/lib/nfc/protocols/slix/slix_listener_i.c @@ -0,0 +1,617 @@ +#include "slix_listener_i.h" + +#include + +#include + +#define TAG "SlixListener" + +typedef SlixError (*SlixRequestHandler)( + SlixListener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags); + +// Helper functions + +static bool + slix_listener_is_password_lock_enabled(SlixListener* instance, SlixPasswordType password_type) { + return !instance->session_state.password_match[password_type]; +} + +static SlixPasswordType slix_listener_get_password_type_by_id(uint8_t id) { + uint32_t type; + + for(type = 0; type < SlixPasswordTypeCount; ++type) { + if(id >> type == 0x01U) break; + } + + return type; +} + +static SlixPassword + slix_listener_unxor_password(const SlixPassword password_xored, uint16_t random) { + return password_xored ^ ((SlixPassword)random << 16 | random); +} + +static SlixError slix_listener_set_password( + SlixListener* instance, + SlixPasswordType password_type, + SlixPassword password) { + SlixError error = SlixErrorNone; + + do { + if(password_type >= SlixPasswordTypeCount) { + error = SlixErrorInternal; + break; + } + + SlixData* slix_data = instance->data; + + if(!slix_type_supports_password(slix_get_type(slix_data), password_type)) { + error = SlixErrorNotSupported; + break; + } + + SlixListenerSessionState* session_state = &instance->session_state; + session_state->password_match[password_type] = + (password == slix_get_password(slix_data, password_type)); + + if(!session_state->password_match[password_type]) { + error = SlixErrorWrongPassword; + break; + } + } while(false); + + return error; +} + +static SlixError slix_listener_write_password( + SlixListener* instance, + SlixPasswordType password_type, + SlixPassword password) { + SlixError error = SlixErrorNone; + + do { + if(password_type >= SlixPasswordTypeCount) { + error = SlixErrorInternal; + break; + } + + SlixData* slix_data = instance->data; + + if(!slix_type_supports_password(slix_get_type(slix_data), password_type)) { + error = SlixErrorNotSupported; + break; + } + + SlixListenerSessionState* session_state = &instance->session_state; + + if(session_state->password_match[password_type]) { + // TODO FL-3634: check for password lock + slix_set_password(slix_data, password_type, password); + // Require another SET_PASSWORD command with the new password + session_state->password_match[password_type] = false; + } else { + error = SlixErrorWrongPassword; + break; + } + } while(false); + + return error; +} + +// Custom SLIX request handlers +static SlixError slix_listener_default_handler( + SlixListener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(instance); + UNUSED(data); + UNUSED(data_size); + UNUSED(flags); + + // Empty placeholder handler + return SlixErrorNotSupported; +} + +static SlixError slix_listener_get_nxp_system_info_handler( + SlixListener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + UNUSED(flags); + + const SlixData* slix_data = instance->data; + const Iso15693_3Data* iso15693_data = instance->data->iso15693_3_data; + + const SlixProtection* protection = &slix_data->system_info.protection; + bit_buffer_append_byte(instance->tx_buffer, protection->pointer); + bit_buffer_append_byte(instance->tx_buffer, protection->condition); + + uint8_t lock_bits = 0; + if(iso15693_data->settings.lock_bits.dsfid) { + lock_bits |= SLIX_LOCK_BITS_DSFID; + } + if(iso15693_data->settings.lock_bits.afi) { + lock_bits |= SLIX_LOCK_BITS_AFI; + } + if(slix_data->system_info.lock_bits.eas) { + lock_bits |= SLIX_LOCK_BITS_EAS; + } + if(slix_data->system_info.lock_bits.ppl) { + lock_bits |= SLIX_LOCK_BITS_PPL; + } + bit_buffer_append_byte(instance->tx_buffer, lock_bits); + + const uint32_t feature_flags = SLIX2_FEATURE_FLAGS; + bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&feature_flags, sizeof(uint32_t)); + + return SlixErrorNone; +} + +static SlixError slix_listener_get_random_number_handler( + SlixListener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + UNUSED(flags); + + SlixListenerSessionState* session_state = &instance->session_state; + session_state->random = furi_hal_random_get(); + bit_buffer_append_bytes( + instance->tx_buffer, (uint8_t*)&session_state->random, sizeof(uint16_t)); + + return SlixErrorNone; +} + +static SlixError slix_listener_set_password_handler( + SlixListener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(flags); + SlixError error = SlixErrorNone; + + do { +#pragma pack(push, 1) + typedef struct { + uint8_t password_id; + SlixPassword password_xored; + } SlixSetPasswordRequestLayout; +#pragma pack(pop) + + if(data_size != sizeof(SlixSetPasswordRequestLayout)) { + error = SlixErrorFormat; + break; + } + + const SlixSetPasswordRequestLayout* request = (const SlixSetPasswordRequestLayout*)data; + const SlixPasswordType password_type = + slix_listener_get_password_type_by_id(request->password_id); + const SlixPassword password_received = + slix_listener_unxor_password(request->password_xored, instance->session_state.random); + + error = slix_listener_set_password(instance, password_type, password_received); + if(error != SlixErrorNone) break; + + if(password_type == SlixPasswordTypePrivacy) { + slix_set_privacy_mode(instance->data, false); + } + } while(false); + + return error; +} + +static SlixError slix_listener_write_password_handler( + SlixListener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(flags); + SlixError error = SlixErrorNone; + + do { +#pragma pack(push, 1) + typedef struct { + uint8_t password_id; + SlixPassword password; + } SlixWritePasswordRequestLayout; +#pragma pack(pop) + + if(data_size != sizeof(SlixWritePasswordRequestLayout)) { + error = SlixErrorFormat; + break; + } + + const SlixWritePasswordRequestLayout* request = + (const SlixWritePasswordRequestLayout*)data; + const SlixPasswordType password_type = + slix_listener_get_password_type_by_id(request->password_id); + + error = slix_listener_write_password(instance, password_type, request->password); + if(error != SlixErrorNone) break; + + } while(false); + + return error; +} + +static SlixError slix_listener_protect_page_handler( + SlixListener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(flags); + SlixError error = SlixErrorNone; + + do { + typedef struct { + uint8_t pointer; + uint8_t condition; + } SlixProtectPageRequestLayout; + + if(data_size != sizeof(SlixProtectPageRequestLayout)) { + error = SlixErrorFormat; + break; + } + + SlixData* slix_data = instance->data; + + if(slix_data->system_info.lock_bits.ppl) { + error = SlixErrorInternal; + break; + } + + const SlixListenerSessionState* session_state = &instance->session_state; + if(!session_state->password_match[SlixPasswordTypeRead] || + !session_state->password_match[SlixPasswordTypeWrite]) { + error = SlixErrorInternal; + break; + } + + const SlixProtectPageRequestLayout* request = (const SlixProtectPageRequestLayout*)data; + + if(request->pointer >= SLIX_COUNTER_BLOCK_NUM) { + error = SlixErrorInternal; + break; + } + + SlixProtection* protection = &slix_data->system_info.protection; + + protection->pointer = request->pointer; + protection->condition = request->condition; + } while(false); + + return error; +} + +static SlixError slix_listener_enable_privacy_handler( + SlixListener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(flags); + SlixError error = SlixErrorNone; + + do { + typedef struct { + SlixPassword password_xored; + } SlixEnablePrivacyRequestLayout; + + if(data_size != sizeof(SlixEnablePrivacyRequestLayout)) { + error = SlixErrorFormat; + break; + } + + const SlixEnablePrivacyRequestLayout* request = + (const SlixEnablePrivacyRequestLayout*)data; + + const SlixPassword password_received = + slix_listener_unxor_password(request->password_xored, instance->session_state.random); + + error = slix_listener_set_password(instance, SlixPasswordTypePrivacy, password_received); + if(error != SlixErrorNone) break; + + slix_set_privacy_mode(instance->data, true); + } while(false); + + return error; +} + +static SlixError slix_listener_read_signature_handler( + SlixListener* instance, + const uint8_t* data, + size_t data_size, + uint8_t flags) { + UNUSED(data); + UNUSED(data_size); + UNUSED(flags); + + bit_buffer_append_bytes(instance->tx_buffer, instance->data->signature, sizeof(SlixSignature)); + return SlixErrorNone; +} + +// Custom SLIX commands handler table +static const SlixRequestHandler slix_request_handler_table[SLIX_CMD_CUSTOM_COUNT] = { + slix_listener_default_handler, // SLIX_CMD_SET_EAS (0xA2U) + slix_listener_default_handler, // SLIX_CMD_RESET_EAS (0xA3U) + slix_listener_default_handler, // SLIX_CMD_LOCK_EAS (0xA4U) + slix_listener_default_handler, // SLIX_CMD_EAS_ALARM (0xA5U) + slix_listener_default_handler, // SLIX_CMD_PASSWORD_PROTECT_EAS_AFI (0xA6U) + slix_listener_default_handler, // SLIX_CMD_WRITE_EAS_ID (0xA7U) + slix_listener_default_handler, // UNUSED (0xA8U) + slix_listener_default_handler, // UNUSED (0xA9U) + slix_listener_default_handler, // UNUSED (0xAAU) + slix_listener_get_nxp_system_info_handler, + slix_listener_default_handler, // UNUSED (0xACU) + slix_listener_default_handler, // UNUSED (0xADU) + slix_listener_default_handler, // UNUSED (0xAEU) + slix_listener_default_handler, // UNUSED (0xAFU) + slix_listener_default_handler, // SLIX_CMD_INVENTORY_PAGE_READ (0xB0U) + slix_listener_default_handler, // SLIX_CMD_INVENTORY_PAGE_READ_FAST (0xB1U) + slix_listener_get_random_number_handler, + slix_listener_set_password_handler, + slix_listener_write_password_handler, + slix_listener_default_handler, // SLIX_CMD_64_BIT_PASSWORD_PROTECTION (0xB5U) + slix_listener_protect_page_handler, + slix_listener_default_handler, // SLIX_CMD_LOCK_PAGE_PROTECTION_CONDITION (0xB7U) + slix_listener_default_handler, // UNUSED (0xB8U) + slix_listener_default_handler, // SLIX_CMD_DESTROY (0xB9U) + slix_listener_enable_privacy_handler, + slix_listener_default_handler, // UNUSED (0xBBU) + slix_listener_default_handler, // SLIX_CMD_STAY_QUIET_PERSISTENT (0xBCU) + slix_listener_read_signature_handler, +}; + +// ISO15693-3 Protocol extension handlers + +static Iso15693_3Error + slix_listener_iso15693_3_inventory_extension_handler(SlixListener* instance, va_list args) { + UNUSED(args); + + return instance->data->privacy ? Iso15693_3ErrorIgnore : Iso15693_3ErrorNone; +} + +static Iso15693_3Error + slix_iso15693_3_read_block_extension_handler(SlixListener* instance, va_list args) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + const uint32_t block_num = va_arg(args, uint32_t); + // SLIX Counter has no read protection + if(block_num == SLIX_COUNTER_BLOCK_NUM) break; + + if(slix_is_block_protected(instance->data, SlixPasswordTypeRead, block_num)) { + if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) { + error = Iso15693_3ErrorInternal; + break; + } + } + } while(false); + + return error; +} + +static Iso15693_3Error + slix_listener_iso15693_3_write_block_extension_handler(SlixListener* instance, va_list args) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + const uint32_t block_num = va_arg(args, uint32_t); + + if(block_num == SLIX_COUNTER_BLOCK_NUM) { + const uint32_t counter = *(va_arg(args, uint32_t*)); + if(counter == 0x00000001U) { + if(slix_is_counter_increment_protected(instance->data) && + slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) { + error = Iso15693_3ErrorInternal; + break; + } + slix_increment_counter(instance->data); + error = Iso15693_3ErrorFullyHandled; + break; + } + } else if(slix_is_block_protected(instance->data, SlixPasswordTypeRead, block_num)) { + if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) { + error = Iso15693_3ErrorInternal; + break; + } + } + + if(slix_is_block_protected(instance->data, SlixPasswordTypeWrite, block_num)) { + if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeWrite)) { + error = Iso15693_3ErrorInternal; + break; + } + } + + } while(false); + + return error; +} + +static Iso15693_3Error + slix_listener_iso15693_3_lock_block_extension_handler(SlixListener* instance, va_list args) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + do { + const uint32_t block_num = va_arg(args, uint32_t); + + // SLIX counter cannot be locked + if(block_num == SLIX_COUNTER_BLOCK_NUM) { + error = Iso15693_3ErrorInternal; + break; + } + + if(slix_is_block_protected(instance->data, SlixPasswordTypeRead, block_num)) { + if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) { + error = Iso15693_3ErrorInternal; + break; + } + } + + if(slix_is_block_protected(instance->data, SlixPasswordTypeWrite, block_num)) { + if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeWrite)) { + error = Iso15693_3ErrorInternal; + break; + } + } + + } while(false); + + return error; +} + +static Iso15693_3Error slix_listener_iso15693_3_read_multi_block_extension_handler( + SlixListener* instance, + va_list args) { + Iso15693_3Error error = Iso15693_3ErrorNone; + + const uint32_t block_index_start = va_arg(args, uint32_t); + const uint32_t block_index_end = va_arg(args, uint32_t); + + for(uint32_t i = block_index_start; i <= block_index_end; ++i) { + // SLIX Counter has no read protection + if(i == SLIX_COUNTER_BLOCK_NUM) continue; + + if(slix_is_block_protected(instance->data, SlixPasswordTypeRead, i)) { + if(slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeRead)) { + error = Iso15693_3ErrorInternal; + break; + } + } + } + + return error; +} + +static Iso15693_3Error slix_listener_iso15693_3_write_multi_block_extension_handler( + SlixListener* instance, + va_list args) { + UNUSED(instance); + UNUSED(args); + // No mention of this command in SLIX docs, assuming not supported + return Iso15693_3ErrorNotSupported; +} + +static Iso15693_3Error slix_listener_iso15693_3_write_lock_afi_extension_handler( + SlixListener* instance, + va_list args) { + UNUSED(args); + + return slix_listener_is_password_lock_enabled(instance, SlixPasswordTypeEasAfi) ? + Iso15693_3ErrorInternal : + Iso15693_3ErrorNone; +} + +// Extended ISO15693-3 standard commands handler table (NULL = no extension) +static const Iso15693_3ExtensionHandlerTable slix_iso15693_extension_handler_table = { + .mandatory = + { + (Iso15693_3ExtensionHandler)slix_listener_iso15693_3_inventory_extension_handler, + (Iso15693_3ExtensionHandler)NULL // ISO15693_3_CMD_STAY_QUIET (0x02U) + }, + .optional = + { + (Iso15693_3ExtensionHandler)slix_iso15693_3_read_block_extension_handler, + (Iso15693_3ExtensionHandler)slix_listener_iso15693_3_write_block_extension_handler, + (Iso15693_3ExtensionHandler)slix_listener_iso15693_3_lock_block_extension_handler, + (Iso15693_3ExtensionHandler)slix_listener_iso15693_3_read_multi_block_extension_handler, + (Iso15693_3ExtensionHandler) + slix_listener_iso15693_3_write_multi_block_extension_handler, + (Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_SELECT (0x25U) + (Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_RESET_TO_READY (0x26U) + (Iso15693_3ExtensionHandler)slix_listener_iso15693_3_write_lock_afi_extension_handler, + (Iso15693_3ExtensionHandler)slix_listener_iso15693_3_write_lock_afi_extension_handler, + (Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_WRITE_DSFID (0x29U) + (Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_LOCK_DSFID (0x2AU) + (Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_GET_SYS_INFO (0x2BU) + (Iso15693_3ExtensionHandler)NULL, // ISO15693_3_CMD_GET_BLOCKS_SECURITY (0x2CU) + }, +}; + +SlixError slix_listener_init_iso15693_3_extensions(SlixListener* instance) { + iso15693_3_listener_set_extension_handler_table( + instance->iso15693_3_listener, &slix_iso15693_extension_handler_table, instance); + return SlixErrorNone; +} + +SlixError slix_listener_process_request(SlixListener* instance, const BitBuffer* rx_buffer) { + SlixError error = SlixErrorNone; + + do { + typedef struct { + uint8_t flags; + uint8_t command; + uint8_t manufacturer; + uint8_t data[]; + } SlixRequestLayout; + + const size_t buf_size = bit_buffer_get_size_bytes(rx_buffer); + + if(buf_size < sizeof(SlixRequestLayout)) { + error = SlixErrorFormat; + break; + } + + const SlixRequestLayout* request = + (const SlixRequestLayout*)bit_buffer_get_data(rx_buffer); + + const bool addressed_mode = instance->iso15693_3_listener->session_state.addressed; + + const size_t uid_field_size = addressed_mode ? ISO15693_3_UID_SIZE : 0; + const size_t buf_size_min = sizeof(SlixRequestLayout) + uid_field_size; + + if(buf_size < buf_size_min) { + error = SlixErrorFormat; + break; + } + + if(addressed_mode) { + if(!iso15693_3_is_equal_uid(instance->data->iso15693_3_data, request->data)) { + error = SlixErrorUidMismatch; + break; + } + } + + const uint8_t command = request->command; + const bool is_valid_slix_command = command >= SLIX_CMD_CUSTOM_START && + command < SLIX_CMD_CUSTOM_END; + if(!is_valid_slix_command) { + error = SlixErrorNotSupported; + break; + } + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_NONE); + + const uint8_t* request_data = &request->data[uid_field_size]; + const size_t request_data_size = buf_size - buf_size_min; + + SlixRequestHandler handler = slix_request_handler_table[command - SLIX_CMD_CUSTOM_START]; + error = handler(instance, request_data, request_data_size, request->flags); + + // It's a trick! Send no reply. + if(error == SlixErrorFormat || error == SlixErrorWrongPassword || + error == SlixErrorNotSupported) + break; + + if(error != SlixErrorNone) { + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_FLAG_ERROR); + bit_buffer_append_byte(instance->tx_buffer, ISO15693_3_RESP_ERROR_UNKNOWN); + } + + const Iso15693_3Error iso15693_error = + iso15693_3_listener_send_frame(instance->iso15693_3_listener, instance->tx_buffer); + error = slix_process_iso15693_3_error(iso15693_error); + } while(false); + + return error; +} diff --git a/lib/nfc/protocols/slix/slix_listener_i.h b/lib/nfc/protocols/slix/slix_listener_i.h new file mode 100644 index 00000000000..ce059c5a675 --- /dev/null +++ b/lib/nfc/protocols/slix/slix_listener_i.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include "slix_listener.h" +#include "slix_i.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint16_t random; + bool password_match[SlixPasswordTypeCount]; +} SlixListenerSessionState; + +struct SlixListener { + Iso15693_3Listener* iso15693_3_listener; + SlixData* data; + SlixListenerSessionState session_state; + + BitBuffer* tx_buffer; + + NfcGenericEvent generic_event; + SlixListenerEvent slix_event; + SlixListenerEventData slix_event_data; + NfcGenericCallback callback; + void* context; +}; + +SlixError slix_listener_init_iso15693_3_extensions(SlixListener* instance); + +SlixError slix_listener_process_request(SlixListener* instance, const BitBuffer* rx_buffer); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/slix/slix_poller.c b/lib/nfc/protocols/slix/slix_poller.c new file mode 100644 index 00000000000..46a17119436 --- /dev/null +++ b/lib/nfc/protocols/slix/slix_poller.c @@ -0,0 +1,158 @@ +#include "slix_poller_i.h" + +#include + +#include + +#define TAG "SlixPoller" + +#define SLIX_POLLER_BUF_SIZE (64U) + +typedef NfcCommand (*SlixPollerStateHandler)(SlixPoller* instance); + +const SlixData* slix_poller_get_data(SlixPoller* instance) { + furi_assert(instance); + + return instance->data; +} + +static SlixPoller* slix_poller_alloc(Iso15693_3Poller* iso15693_3_poller) { + SlixPoller* instance = malloc(sizeof(SlixPoller)); + instance->iso15693_3_poller = iso15693_3_poller; + instance->data = slix_alloc(); + instance->tx_buffer = bit_buffer_alloc(SLIX_POLLER_BUF_SIZE); + instance->rx_buffer = bit_buffer_alloc(SLIX_POLLER_BUF_SIZE); + + instance->slix_event.data = &instance->slix_event_data; + + instance->general_event.protocol = NfcProtocolSlix; + instance->general_event.event_data = &instance->slix_event; + instance->general_event.instance = instance; + + return instance; +} + +static void slix_poller_free(SlixPoller* instance) { + furi_assert(instance); + + slix_free(instance->data); + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + free(instance); +} + +static NfcCommand slix_poller_handler_idle(SlixPoller* instance) { + iso15693_3_copy( + instance->data->iso15693_3_data, iso15693_3_poller_get_data(instance->iso15693_3_poller)); + + instance->poller_state = SlixPollerStateGetNxpSysInfo; + return NfcCommandContinue; +} + +static NfcCommand slix_poller_handler_get_nfc_system_info(SlixPoller* instance) { + instance->error = slix_poller_get_nxp_system_info(instance, &instance->data->system_info); + if(instance->error == SlixErrorNone) { + instance->poller_state = SlixPollerStateReadSignature; + } else { + instance->poller_state = SlixPollerStateError; + } + + return NfcCommandContinue; +} + +static NfcCommand slix_poller_handler_read_signature(SlixPoller* instance) { + instance->error = slix_poller_read_signature(instance, &instance->data->signature); + if(instance->error == SlixErrorNone) { + instance->poller_state = SlixPollerStateReady; + } else { + instance->poller_state = SlixPollerStateError; + } + + return NfcCommandContinue; +} + +static NfcCommand slix_poller_handler_error(SlixPoller* instance) { + instance->slix_event_data.error = instance->error; + instance->slix_event.type = SlixPollerEventTypeError; + NfcCommand command = instance->callback(instance->general_event, instance->context); + instance->poller_state = SlixPollerStateIdle; + return command; +} + +static NfcCommand slix_poller_handler_ready(SlixPoller* instance) { + instance->slix_event.type = SlixPollerEventTypeReady; + NfcCommand command = instance->callback(instance->general_event, instance->context); + return command; +} + +static const SlixPollerStateHandler slix_poller_state_handler[SlixPollerStateNum] = { + [SlixPollerStateIdle] = slix_poller_handler_idle, + [SlixPollerStateError] = slix_poller_handler_error, + [SlixPollerStateGetNxpSysInfo] = slix_poller_handler_get_nfc_system_info, + [SlixPollerStateReadSignature] = slix_poller_handler_read_signature, + [SlixPollerStateReady] = slix_poller_handler_ready, +}; + +static void + slix_poller_set_callback(SlixPoller* instance, NfcGenericCallback callback, void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand slix_poller_run(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso15693_3); + + SlixPoller* instance = context; + furi_assert(instance); + furi_assert(instance->callback); + + Iso15693_3PollerEvent* iso15693_3_event = event.event_data; + furi_assert(iso15693_3_event); + + NfcCommand command = NfcCommandContinue; + + if(iso15693_3_event->type == Iso15693_3PollerEventTypeReady) { + command = slix_poller_state_handler[instance->poller_state](instance); + } else if(iso15693_3_event->type == Iso15693_3PollerEventTypeError) { + instance->slix_event.type = SlixPollerEventTypeError; + command = instance->callback(instance->general_event, instance->context); + } + + return command; +} + +static bool slix_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(event.protocol == NfcProtocolIso15693_3); + + SlixPoller* instance = context; + furi_assert(instance); + + const Iso15693_3PollerEvent* iso15693_3_event = event.event_data; + furi_assert(iso15693_3_event); + iso15693_3_copy( + instance->data->iso15693_3_data, iso15693_3_poller_get_data(instance->iso15693_3_poller)); + + bool protocol_detected = false; + + if(iso15693_3_event->type == Iso15693_3PollerEventTypeReady) { + if(slix_get_type(instance->data) < SlixTypeCount) { + SlixSystemInfo system_info = {}; + SlixError error = slix_poller_get_nxp_system_info(instance, &system_info); + protocol_detected = (error == SlixErrorNone); + } + } + + return protocol_detected; +} + +const NfcPollerBase nfc_poller_slix = { + .alloc = (NfcPollerAlloc)slix_poller_alloc, + .free = (NfcPollerFree)slix_poller_free, + .set_callback = (NfcPollerSetCallback)slix_poller_set_callback, + .run = (NfcPollerRun)slix_poller_run, + .detect = (NfcPollerDetect)slix_poller_detect, + .get_data = (NfcPollerGetData)slix_poller_get_data, +}; diff --git a/lib/nfc/protocols/slix/slix_poller.h b/lib/nfc/protocols/slix/slix_poller.h new file mode 100644 index 00000000000..62d60be5fe2 --- /dev/null +++ b/lib/nfc/protocols/slix/slix_poller.h @@ -0,0 +1,85 @@ +#pragma once + +#include "slix.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief SlixPoller opaque type definition. + */ +typedef struct SlixPoller SlixPoller; + +/** + * @brief Enumeration of possible Slix poller event types. + */ +typedef enum { + SlixPollerEventTypeError, /**< An error occured while reading card. */ + SlixPollerEventTypeReady, /**< The card was successfully read by the poller. */ +} SlixPollerEventType; + +/** + * @brief Slixs poller event data. + */ +typedef union { + SlixError error; /**< Error code indicating card reaing fail reason. */ +} SlixPollerEventData; + +/** + * @brief Slix poller event structure. + * + * Upon emission of an event, an instance of this struct will be passed to the callback. + */ +typedef struct { + SlixPollerEventType type; /**< Type of emmitted event. */ + SlixPollerEventData* data; /**< Pointer to event specific data. */ +} SlixPollerEvent; + +/** + * @brief Transmit and receive Slix frames in poller mode. + * + * Must ONLY be used inside the callback function. + * + * The rx_buffer will be filled with any data received as a response to data + * sent from tx_buffer, with a timeout defined by the fwt parameter. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] tx_buffer pointer to the buffer containing the data to be transmitted. + * @param[out] rx_buffer pointer to the buffer to be filled with received data. + * @param[in] fwt frame wait time (response timeout), in carrier cycles. + * @return SlixErrorNone on success, an error code on failure. + */ +SlixError slix_poller_send_frame( + SlixPoller* instance, + const BitBuffer* tx_data, + BitBuffer* rx_data, + uint32_t fwt); + +/** + * @brief Send get nxp system info command and parse response. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the SlixSystemInfo structure to be filled. + * @return SlixErrorNone on success, an error code on failure. + */ +SlixError slix_poller_get_nxp_system_info(SlixPoller* instance, SlixSystemInfo* data); + +/** + * @brief Read signature from card. + * + * Must ONLY be used inside the callback function. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[out] data pointer to the SlixSignature structure to be filled. + * @return SlixErrorNone on success, an error code on failure. + */ +SlixError slix_poller_read_signature(SlixPoller* instance, SlixSignature* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/slix/slix_poller_defs.h b/lib/nfc/protocols/slix/slix_poller_defs.h new file mode 100644 index 00000000000..df8f298f0a3 --- /dev/null +++ b/lib/nfc/protocols/slix/slix_poller_defs.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const NfcPollerBase nfc_poller_slix; diff --git a/lib/nfc/protocols/slix/slix_poller_i.c b/lib/nfc/protocols/slix/slix_poller_i.c new file mode 100644 index 00000000000..6d7bdf37795 --- /dev/null +++ b/lib/nfc/protocols/slix/slix_poller_i.c @@ -0,0 +1,69 @@ +#include "slix_poller_i.h" + +#include + +#include "slix_i.h" + +#define TAG "SlixPoller" + +static void slix_poller_prepare_request(SlixPoller* instance, uint8_t command) { + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + bit_buffer_append_byte( + instance->tx_buffer, + ISO15693_3_REQ_FLAG_SUBCARRIER_1 | ISO15693_3_REQ_FLAG_DATA_RATE_HI | + ISO15693_3_REQ_FLAG_T4_ADDRESSED); + bit_buffer_append_byte(instance->tx_buffer, command); + bit_buffer_append_byte(instance->tx_buffer, SLIX_NXP_MANUFACTURER_CODE); + + iso15693_3_append_uid(instance->data->iso15693_3_data, instance->tx_buffer); +} + +SlixError slix_poller_send_frame( + SlixPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + + const Iso15693_3Error iso15693_3_error = + iso15693_3_poller_send_frame(instance->iso15693_3_poller, tx_buffer, rx_buffer, fwt); + return slix_process_iso15693_3_error(iso15693_3_error); +} + +SlixError slix_poller_get_nxp_system_info(SlixPoller* instance, SlixSystemInfo* data) { + furi_assert(instance); + furi_assert(data); + + slix_poller_prepare_request(instance, SLIX_CMD_GET_NXP_SYSTEM_INFORMATION); + + SlixError error = SlixErrorNone; + + do { + error = slix_poller_send_frame( + instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC); + if(error != SlixErrorNone) break; + error = slix_get_nxp_system_info_response_parse(instance->data, instance->rx_buffer); + } while(false); + + return error; +} + +SlixError slix_poller_read_signature(SlixPoller* instance, SlixSignature* data) { + furi_assert(instance); + furi_assert(data); + + slix_poller_prepare_request(instance, SLIX_CMD_READ_SIGNATURE); + + SlixError error = SlixErrorNone; + + do { + error = slix_poller_send_frame( + instance, instance->tx_buffer, instance->rx_buffer, ISO15693_3_FDT_POLL_FC * 2); + if(error != SlixErrorNone) break; + error = slix_read_signature_response_parse(instance->data->signature, instance->rx_buffer); + } while(false); + + return error; +} diff --git a/lib/nfc/protocols/slix/slix_poller_i.h b/lib/nfc/protocols/slix/slix_poller_i.h new file mode 100644 index 00000000000..1fda1a7d246 --- /dev/null +++ b/lib/nfc/protocols/slix/slix_poller_i.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include "slix_poller.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + SlixPollerStateIdle, + SlixPollerStateGetNxpSysInfo, + SlixPollerStateReadSignature, + SlixPollerStateReady, + SlixPollerStateError, + SlixPollerStateNum, +} SlixPollerState; + +struct SlixPoller { + Iso15693_3Poller* iso15693_3_poller; + SlixData* data; + SlixPollerState poller_state; + SlixError error; + + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + + SlixPollerEventData slix_event_data; + SlixPollerEvent slix_event; + NfcGenericEvent general_event; + NfcGenericCallback callback; + void* context; +}; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/st25tb/st25tb.c b/lib/nfc/protocols/st25tb/st25tb.c new file mode 100644 index 00000000000..785cf831d93 --- /dev/null +++ b/lib/nfc/protocols/st25tb/st25tb.c @@ -0,0 +1,254 @@ +#include "st25tb.h" + +#include "core/string.h" +#include "flipper_format.h" +#include + +#include +#include + +#define ST25TB_PROTOCOL_NAME "ST25TB" +#define ST25TB_TYPE_KEY "ST25TB Type" +#define ST25TB_BLOCK_KEY "Block %d" +#define ST25TB_SYSTEM_BLOCK_KEY "System OTP Block" + +typedef struct { + uint8_t blocks_total; + bool has_otp; + const char* full_name; + const char* type_name; +} St25tbFeatures; + +static const St25tbFeatures st25tb_features[St25tbTypeNum] = { + [St25tbType512At] = + { + .blocks_total = 16, + .has_otp = false, + .full_name = "ST25TB512-AT/SRI512", + .type_name = "512AT", + }, + [St25tbType512Ac] = + { + .blocks_total = 16, + .has_otp = true, + .full_name = "ST25TB512-AC/SRT512", + .type_name = "512AC", + }, + [St25tbTypeX512] = + { + .blocks_total = 16, + .has_otp = true, + .full_name = "SRIX512", + .type_name = "X512", + }, + [St25tbType02k] = + { + .blocks_total = 64, + .has_otp = true, + .full_name = "ST25TB02K/SRI2K", + .type_name = "2K", + }, + [St25tbType04k] = + { + .blocks_total = 128, + .has_otp = true, + .full_name = "ST25TB04K/SRI4K", + .type_name = "4K", + }, + [St25tbTypeX4k] = + { + .blocks_total = 128, + .has_otp = true, + .full_name = "SRIX4K", + .type_name = "X4K", + }, +}; + +const NfcDeviceBase nfc_device_st25tb = { + .protocol_name = ST25TB_PROTOCOL_NAME, + .alloc = (NfcDeviceAlloc)st25tb_alloc, + .free = (NfcDeviceFree)st25tb_free, + .reset = (NfcDeviceReset)st25tb_reset, + .copy = (NfcDeviceCopy)st25tb_copy, + .verify = (NfcDeviceVerify)st25tb_verify, + .load = (NfcDeviceLoad)st25tb_load, + .save = (NfcDeviceSave)st25tb_save, + .is_equal = (NfcDeviceEqual)st25tb_is_equal, + .get_name = (NfcDeviceGetName)st25tb_get_device_name, + .get_uid = (NfcDeviceGetUid)st25tb_get_uid, + .set_uid = (NfcDeviceSetUid)st25tb_set_uid, + .get_base_data = (NfcDeviceGetBaseData)st25tb_get_base_data, +}; + +St25tbData* st25tb_alloc() { + St25tbData* data = malloc(sizeof(St25tbData)); + return data; +} + +void st25tb_free(St25tbData* data) { + furi_assert(data); + + free(data); +} + +void st25tb_reset(St25tbData* data) { + memset(data, 0, sizeof(St25tbData)); +} + +void st25tb_copy(St25tbData* data, const St25tbData* other) { + furi_assert(data); + furi_assert(other); + + *data = *other; +} + +bool st25tb_verify(St25tbData* data, const FuriString* device_type) { + UNUSED(data); + return furi_string_equal_str(device_type, ST25TB_PROTOCOL_NAME); +} + +bool st25tb_load(St25tbData* data, FlipperFormat* ff, uint32_t version) { + furi_assert(data); + + bool parsed = false; + + FuriString* temp_str = furi_string_alloc(); + + do { + if(version < NFC_UNIFIED_FORMAT_VERSION) break; + if(!flipper_format_read_string(ff, ST25TB_TYPE_KEY, temp_str)) break; + + bool type_parsed = false; + for(size_t i = 0; i < St25tbTypeNum; i++) { + if(furi_string_equal_str(temp_str, st25tb_features[i].type_name)) { + data->type = i; + type_parsed = true; + } + } + if(!type_parsed) break; + + bool blocks_parsed = true; + for(uint8_t block = 0; block < st25tb_features[data->type].blocks_total; block++) { + furi_string_printf(temp_str, ST25TB_BLOCK_KEY, block); + if(!flipper_format_read_hex( + ff, furi_string_get_cstr(temp_str), (uint8_t*)&data->blocks[block], 4)) { + blocks_parsed = false; + break; + } + } + if(!blocks_parsed) break; + + if(!flipper_format_read_hex( + ff, ST25TB_SYSTEM_BLOCK_KEY, (uint8_t*)&data->system_otp_block, 4)) + break; + + parsed = true; + } while(false); + + furi_string_free(temp_str); + + return parsed; +} + +bool st25tb_save(const St25tbData* data, FlipperFormat* ff) { + furi_assert(data); + + FuriString* temp_str = furi_string_alloc(); + bool saved = false; + + do { + if(!flipper_format_write_comment_cstr(ff, ST25TB_PROTOCOL_NAME " specific data")) break; + if(!flipper_format_write_string_cstr( + ff, ST25TB_TYPE_KEY, st25tb_features[data->type].type_name)) + break; + + bool blocks_saved = true; + for(uint8_t block = 0; block < st25tb_features[data->type].blocks_total; block++) { + furi_string_printf(temp_str, ST25TB_BLOCK_KEY, block); + if(!flipper_format_write_hex( + ff, furi_string_get_cstr(temp_str), (uint8_t*)&data->blocks[block], 4)) { + blocks_saved = false; + break; + } + } + if(!blocks_saved) break; + + if(!flipper_format_write_hex( + ff, ST25TB_SYSTEM_BLOCK_KEY, (uint8_t*)&data->system_otp_block, 4)) + break; + + saved = true; + } while(false); + + furi_string_free(temp_str); + return saved; +} + +bool st25tb_is_equal(const St25tbData* data, const St25tbData* other) { + furi_assert(data); + furi_assert(other); + + return memcmp(data, other, sizeof(St25tbData)) == 0; +} + +uint8_t st25tb_get_block_count(St25tbType type) { + return st25tb_features[type].blocks_total; +} + +const char* st25tb_get_device_name(const St25tbData* data, NfcDeviceNameType name_type) { + furi_assert(data); + furi_assert(data->type < St25tbTypeNum); + + if(name_type == NfcDeviceNameTypeFull) { + return st25tb_features[data->type].full_name; + } else { + return st25tb_features[data->type].type_name; + } +} + +const uint8_t* st25tb_get_uid(const St25tbData* data, size_t* uid_len) { + furi_assert(data); + + if(uid_len) { + *uid_len = ST25TB_UID_SIZE; + } + + return data->uid; +} + +bool st25tb_set_uid(St25tbData* data, const uint8_t* uid, size_t uid_len) { + furi_assert(data); + + const bool uid_valid = uid_len == ST25TB_UID_SIZE; + + if(uid_valid) { + memcpy(data->uid, uid, uid_len); + } + + return uid_valid; +} + +St25tbData* st25tb_get_base_data(const St25tbData* data) { + UNUSED(data); + furi_crash("No base data"); +} + +St25tbType st25tb_get_type_from_uid(const uint8_t* uid) { + switch(uid[2] >> 2) { + case 0x0: + case 0x3: + return St25tbTypeX4k; + case 0x4: + return St25tbTypeX512; + case 0x6: + return St25tbType512Ac; + case 0x7: + return St25tbType04k; + case 0xc: + return St25tbType512At; + case 0xf: + return St25tbType02k; + default: + furi_crash("unsupported st25tb type"); + } +} diff --git a/lib/nfc/protocols/st25tb/st25tb.h b/lib/nfc/protocols/st25tb/st25tb.h new file mode 100644 index 00000000000..ed02dc2b207 --- /dev/null +++ b/lib/nfc/protocols/st25tb/st25tb.h @@ -0,0 +1,81 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ST25TB_UID_SIZE (8U) + +//#define ST25TB_FDT_FC (4205U) +#define ST25TB_FDT_FC (8494U) +#define ST25TB_GUARD_TIME_US (5000U) +#define ST25TB_POLL_POLL_MIN_US (1280U) + +#define ST25TB_MAX_BLOCKS (128U) +#define ST25TB_SYSTEM_OTP_BLOCK (0xFFU) +#define ST25TB_BLOCK_SIZE (4U) + +typedef enum { + St25tbErrorNone, + St25tbErrorNotPresent, + St25tbErrorColResFailed, + St25tbErrorBufferOverflow, + St25tbErrorCommunication, + St25tbErrorFieldOff, + St25tbErrorWrongCrc, + St25tbErrorTimeout, + St25tbErrorWriteFailed, +} St25tbError; + +typedef enum { + St25tbType512At, + St25tbType512Ac, + St25tbTypeX512, + St25tbType02k, + St25tbType04k, + St25tbTypeX4k, + St25tbTypeNum, +} St25tbType; + +typedef struct { + uint8_t uid[ST25TB_UID_SIZE]; + St25tbType type; + uint32_t blocks[ST25TB_MAX_BLOCKS]; + uint32_t system_otp_block; +} St25tbData; + +extern const NfcDeviceBase nfc_device_st25tb; + +St25tbData* st25tb_alloc(); + +void st25tb_free(St25tbData* data); + +void st25tb_reset(St25tbData* data); + +void st25tb_copy(St25tbData* data, const St25tbData* other); + +bool st25tb_verify(St25tbData* data, const FuriString* device_type); + +bool st25tb_load(St25tbData* data, FlipperFormat* ff, uint32_t version); + +bool st25tb_save(const St25tbData* data, FlipperFormat* ff); + +bool st25tb_is_equal(const St25tbData* data, const St25tbData* other); + +uint8_t st25tb_get_block_count(St25tbType type); + +const char* st25tb_get_device_name(const St25tbData* data, NfcDeviceNameType name_type); + +const uint8_t* st25tb_get_uid(const St25tbData* data, size_t* uid_len); + +bool st25tb_set_uid(St25tbData* data, const uint8_t* uid, size_t uid_len); + +St25tbData* st25tb_get_base_data(const St25tbData* data); + +St25tbType st25tb_get_type_from_uid(const uint8_t* uid); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/st25tb/st25tb_poller.c b/lib/nfc/protocols/st25tb/st25tb_poller.c new file mode 100644 index 00000000000..fd6dc4f09f0 --- /dev/null +++ b/lib/nfc/protocols/st25tb/st25tb_poller.c @@ -0,0 +1,229 @@ +#include "st25tb_poller.h" +#include "st25tb_poller_i.h" + +#include + +#define TAG "ST25TBPoller" + +typedef NfcCommand (*St25tbPollerStateHandler)(St25tbPoller* instance); + +const St25tbData* st25tb_poller_get_data(St25tbPoller* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +static St25tbPoller* st25tb_poller_alloc(Nfc* nfc) { + furi_assert(nfc); + + St25tbPoller* instance = malloc(sizeof(St25tbPoller)); + instance->nfc = nfc; + instance->state = St25tbPollerStateSelect; + instance->tx_buffer = bit_buffer_alloc(ST25TB_POLLER_MAX_BUFFER_SIZE); + instance->rx_buffer = bit_buffer_alloc(ST25TB_POLLER_MAX_BUFFER_SIZE); + + // RF configuration is the same as 14b + nfc_config(instance->nfc, NfcModePoller, NfcTechIso14443b); + nfc_set_guard_time_us(instance->nfc, ST25TB_GUARD_TIME_US); + nfc_set_fdt_poll_fc(instance->nfc, ST25TB_FDT_FC); + nfc_set_fdt_poll_poll_us(instance->nfc, ST25TB_POLL_POLL_MIN_US); + instance->data = st25tb_alloc(); + + instance->st25tb_event.data = &instance->st25tb_event_data; + instance->general_event.protocol = NfcProtocolSt25tb; + instance->general_event.event_data = &instance->st25tb_event; + instance->general_event.instance = instance; + + return instance; +} + +static void st25tb_poller_free(St25tbPoller* instance) { + furi_assert(instance); + + furi_assert(instance->tx_buffer); + furi_assert(instance->rx_buffer); + furi_assert(instance->data); + + bit_buffer_free(instance->tx_buffer); + bit_buffer_free(instance->rx_buffer); + st25tb_free(instance->data); + free(instance); +} + +static void + st25tb_poller_set_callback(St25tbPoller* instance, NfcGenericCallback callback, void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static NfcCommand st25tb_poller_select_handler(St25tbPoller* instance) { + NfcCommand command = NfcCommandContinue; + + do { + St25tbError error = st25tb_poller_select(instance, NULL); + if(error != St25tbErrorNone) { + instance->state = St25tbPollerStateFailure; + instance->st25tb_event_data.error = error; + break; + } + + instance->st25tb_event.type = St25tbPollerEventTypeReady; + instance->st25tb_event.data->ready.type = instance->data->type; + command = instance->callback(instance->general_event, instance->context); + instance->state = St25tbPollerStateRequestMode; + } while(false); + + return command; +} + +static NfcCommand st25tb_poller_request_mode_handler(St25tbPoller* instance) { + NfcCommand command = NfcCommandContinue; + instance->st25tb_event.type = St25tbPollerEventTypeRequestMode; + command = instance->callback(instance->general_event, instance->context); + + St25tbPollerEventDataModeRequest* mode_request_data = + &instance->st25tb_event_data.mode_request; + + furi_assert(mode_request_data->mode < St25tbPollerModeNum); + + if(mode_request_data->mode == St25tbPollerModeRead) { + instance->state = St25tbPollerStateRead; + instance->poller_ctx.read.current_block = 0; + } else { + instance->state = St25tbPollerStateWrite; + instance->poller_ctx.write.block_number = + mode_request_data->params.write_params.block_number; + instance->poller_ctx.write.block_data = mode_request_data->params.write_params.block_data; + } + + return command; +} + +static NfcCommand st25tb_poller_read_handler(St25tbPoller* instance) { + St25tbError error = St25tbErrorNone; + + do { + uint8_t total_blocks = st25tb_get_block_count(instance->data->type); + uint8_t* current_block = &instance->poller_ctx.read.current_block; + if(*current_block == total_blocks) { + error = st25tb_poller_read_block( + instance, &instance->data->system_otp_block, ST25TB_SYSTEM_OTP_BLOCK); + if(error != St25tbErrorNone) { + FURI_LOG_E(TAG, "Failed to read OTP block"); + instance->state = St25tbPollerStateFailure; + instance->st25tb_event_data.error = error; + break; + } else { + instance->state = St25tbPollerStateSuccess; + break; + } + } else { + error = st25tb_poller_read_block( + instance, &instance->data->blocks[*current_block], *current_block); + if(error != St25tbErrorNone) { + FURI_LOG_E(TAG, "Failed to read block %d", *current_block); + instance->state = St25tbPollerStateFailure; + instance->st25tb_event_data.error = error; + break; + } + + *current_block += 1; + } + } while(false); + + return NfcCommandContinue; +} + +static NfcCommand st25tb_poller_write_handler(St25tbPoller* instance) { + St25tbPollerWriteContext* write_ctx = &instance->poller_ctx.write; + St25tbError error = + st25tb_poller_write_block(instance, write_ctx->block_data, write_ctx->block_number); + + if(error == St25tbErrorNone) { + instance->state = St25tbPollerStateSuccess; + } else { + instance->state = St25tbPollerStateFailure; + instance->st25tb_event_data.error = error; + } + + return NfcCommandContinue; +} + +NfcCommand st25tb_poller_success_handler(St25tbPoller* instance) { + NfcCommand command = NfcCommandContinue; + instance->st25tb_event.type = St25tbPollerEventTypeSuccess; + command = instance->callback(instance->general_event, instance->context); + furi_delay_ms(100); + instance->state = St25tbPollerStateRequestMode; + + return command; +} + +NfcCommand st25tb_poller_failure_handler(St25tbPoller* instance) { + NfcCommand command = NfcCommandContinue; + instance->st25tb_event.type = St25tbPollerEventTypeFailure; + command = instance->callback(instance->general_event, instance->context); + furi_delay_ms(100); + instance->state = St25tbPollerStateSelect; + + return command; +} + +static St25tbPollerStateHandler st25tb_poller_state_handlers[St25tbPollerStateNum] = { + [St25tbPollerStateSelect] = st25tb_poller_select_handler, + [St25tbPollerStateRequestMode] = st25tb_poller_request_mode_handler, + [St25tbPollerStateRead] = st25tb_poller_read_handler, + [St25tbPollerStateWrite] = st25tb_poller_write_handler, + [St25tbPollerStateSuccess] = st25tb_poller_success_handler, + [St25tbPollerStateFailure] = st25tb_poller_failure_handler, +}; + +static NfcCommand st25tb_poller_run(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.protocol == NfcProtocolInvalid); + furi_assert(event.event_data); + + St25tbPoller* instance = context; + NfcEvent* nfc_event = event.event_data; + NfcCommand command = NfcCommandContinue; + + furi_assert(instance->state < St25tbPollerStateNum); + + if(nfc_event->type == NfcEventTypePollerReady) { + command = st25tb_poller_state_handlers[instance->state](instance); + } + + return command; +} + +static bool st25tb_poller_detect(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.instance); + furi_assert(event.protocol == NfcProtocolInvalid); + + bool protocol_detected = false; + St25tbPoller* instance = context; + NfcEvent* nfc_event = event.event_data; + furi_assert(instance->state == St25tbPollerStateSelect); + + if(nfc_event->type == NfcEventTypePollerReady) { + St25tbError error = st25tb_poller_initiate(instance, NULL); + protocol_detected = (error == St25tbErrorNone); + } + + return protocol_detected; +} + +const NfcPollerBase nfc_poller_st25tb = { + .alloc = (NfcPollerAlloc)st25tb_poller_alloc, + .free = (NfcPollerFree)st25tb_poller_free, + .set_callback = (NfcPollerSetCallback)st25tb_poller_set_callback, + .run = (NfcPollerRun)st25tb_poller_run, + .detect = (NfcPollerDetect)st25tb_poller_detect, + .get_data = (NfcPollerGetData)st25tb_poller_get_data, +}; diff --git a/lib/nfc/protocols/st25tb/st25tb_poller.h b/lib/nfc/protocols/st25tb/st25tb_poller.h new file mode 100644 index 00000000000..87687b7eba9 --- /dev/null +++ b/lib/nfc/protocols/st25tb/st25tb_poller.h @@ -0,0 +1,77 @@ +#pragma once + +#include "st25tb.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct St25tbPoller St25tbPoller; + +typedef enum { + St25tbPollerEventTypeReady, + St25tbPollerEventTypeRequestMode, + St25tbPollerEventTypeFailure, + St25tbPollerEventTypeSuccess, +} St25tbPollerEventType; + +typedef struct { + St25tbType type; +} St25tbPollerReadyData; + +typedef enum { + St25tbPollerModeRead, + St25tbPollerModeWrite, + + St25tbPollerModeNum, +} St25tbPollerMode; + +typedef struct { + uint8_t block_number; + uint32_t block_data; +} St25tbPollerEventDataModeRequestWriteParams; + +typedef union { + St25tbPollerEventDataModeRequestWriteParams write_params; +} St25tbPollerEventDataModeRequestParams; + +typedef struct { + St25tbPollerMode mode; + St25tbPollerEventDataModeRequestParams params; +} St25tbPollerEventDataModeRequest; + +typedef union { + St25tbPollerReadyData ready; + St25tbPollerEventDataModeRequest mode_request; + St25tbError error; +} St25tbPollerEventData; + +typedef struct { + St25tbPollerEventType type; + St25tbPollerEventData* data; +} St25tbPollerEvent; + +St25tbError st25tb_poller_send_frame( + St25tbPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt); + +St25tbError st25tb_poller_initiate(St25tbPoller* instance, uint8_t* chip_id_ptr); + +St25tbError st25tb_poller_select(St25tbPoller* instance, uint8_t* chip_id_ptr); + +St25tbError st25tb_poller_get_uid(St25tbPoller* instance, uint8_t* uid); + +St25tbError + st25tb_poller_read_block(St25tbPoller* instance, uint32_t* block, uint8_t block_number); + +St25tbError + st25tb_poller_write_block(St25tbPoller* instance, uint32_t block, uint8_t block_number); + +St25tbError st25tb_poller_halt(St25tbPoller* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/st25tb/st25tb_poller_defs.h b/lib/nfc/protocols/st25tb/st25tb_poller_defs.h new file mode 100644 index 00000000000..86b68024f3a --- /dev/null +++ b/lib/nfc/protocols/st25tb/st25tb_poller_defs.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const NfcPollerBase nfc_poller_st25tb; + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/st25tb/st25tb_poller_i.c b/lib/nfc/protocols/st25tb/st25tb_poller_i.c new file mode 100644 index 00000000000..adb8626a30f --- /dev/null +++ b/lib/nfc/protocols/st25tb/st25tb_poller_i.c @@ -0,0 +1,316 @@ +#include "st25tb_poller_i.h" + +#include + +#define TAG "ST25TBPoller" + +static St25tbError st25tb_poller_process_error(NfcError error) { + switch(error) { + case NfcErrorNone: + return St25tbErrorNone; + case NfcErrorTimeout: + return St25tbErrorTimeout; + default: + return St25tbErrorNotPresent; + } +} + +St25tbError st25tb_poller_send_frame( + St25tbPoller* instance, + const BitBuffer* tx_buffer, + BitBuffer* rx_buffer, + uint32_t fwt) { + furi_assert(instance); + + const size_t tx_bytes = bit_buffer_get_size_bytes(tx_buffer); + furi_assert( + tx_bytes <= bit_buffer_get_capacity_bytes(instance->tx_buffer) - ISO14443_CRC_SIZE); + + bit_buffer_copy(instance->tx_buffer, tx_buffer); + iso14443_crc_append(Iso14443CrcTypeB, instance->tx_buffer); + + St25tbError ret = St25tbErrorNone; + + do { + NfcError error = + nfc_poller_trx(instance->nfc, instance->tx_buffer, instance->rx_buffer, fwt); + if(error != NfcErrorNone) { + FURI_LOG_T(TAG, "error during trx: %d", error); + ret = st25tb_poller_process_error(error); + break; + } + + bit_buffer_copy(rx_buffer, instance->rx_buffer); + if(!iso14443_crc_check(Iso14443CrcTypeB, instance->rx_buffer)) { + ret = St25tbErrorWrongCrc; + break; + } + + iso14443_crc_trim(rx_buffer); + } while(false); + + return ret; +} + +St25tbError st25tb_poller_initiate(St25tbPoller* instance, uint8_t* chip_id_ptr) { + // Send Initiate() + furi_assert(instance); + furi_assert(instance->nfc); + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + bit_buffer_append_byte(instance->tx_buffer, 0x06); + bit_buffer_append_byte(instance->tx_buffer, 0x00); + + St25tbError ret; + do { + ret = st25tb_poller_send_frame( + instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC); + if(ret != St25tbErrorNone) { + break; + } + + if(bit_buffer_get_size_bytes(instance->rx_buffer) != 1) { + FURI_LOG_E(TAG, "Unexpected Initiate response size"); + ret = St25tbErrorCommunication; + break; + } + uint8_t chip_id = bit_buffer_get_byte(instance->rx_buffer, 0); + FURI_LOG_D(TAG, "Got chip_id=0x%02X", chip_id); + if(chip_id_ptr) { + *chip_id_ptr = bit_buffer_get_byte(instance->rx_buffer, 0); + } + } while(false); + + return ret; +} + +St25tbError st25tb_poller_select(St25tbPoller* instance, uint8_t* chip_id_ptr) { + furi_assert(instance); + furi_assert(instance->nfc); + + St25tbError ret; + + do { + uint8_t chip_id; + + if(chip_id_ptr != NULL) { + chip_id = *chip_id_ptr; + } else { + ret = st25tb_poller_initiate(instance, &chip_id); + if(ret != St25tbErrorNone) { + break; + } + } + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + // Send Select(Chip_ID), let's just assume that collisions won't ever happen :D + bit_buffer_append_byte(instance->tx_buffer, 0x0E); + bit_buffer_append_byte(instance->tx_buffer, chip_id); + + ret = st25tb_poller_send_frame( + instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC); + if(ret != St25tbErrorNone) { + break; + } + + if(bit_buffer_get_size_bytes(instance->rx_buffer) != 1) { + FURI_LOG_E(TAG, "Unexpected Select response size"); + ret = St25tbErrorCommunication; + break; + } + + if(bit_buffer_get_byte(instance->rx_buffer, 0) != chip_id) { + FURI_LOG_E(TAG, "ChipID mismatch"); + ret = St25tbErrorColResFailed; + break; + } + + ret = st25tb_poller_get_uid(instance, instance->data->uid); + if(ret != St25tbErrorNone) { + break; + } + + instance->data->type = st25tb_get_type_from_uid(instance->data->uid); + } while(false); + + return ret; +} + +St25tbError st25tb_poller_read(St25tbPoller* instance, St25tbData* data) { + furi_assert(instance); + furi_assert(instance->nfc); + + St25tbError ret; + + memcpy(data, instance->data, sizeof(St25tbData)); + + do { + bool read_blocks = true; + for(uint8_t i = 0; i < st25tb_get_block_count(data->type); i++) { + ret = st25tb_poller_read_block(instance, &data->blocks[i], i); + if(ret != St25tbErrorNone) { + read_blocks = false; + break; + } + } + if(!read_blocks) { + break; + } + ret = st25tb_poller_read_block(instance, &data->system_otp_block, ST25TB_SYSTEM_OTP_BLOCK); + if(ret != St25tbErrorNone) { + break; + } + } while(false); + + return ret; +} + +St25tbError st25tb_poller_get_uid(St25tbPoller* instance, uint8_t* uid) { + furi_assert(instance); + furi_assert(instance->nfc); + + St25tbError ret; + + do { + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + bit_buffer_append_byte(instance->tx_buffer, 0x0B); + + ret = st25tb_poller_send_frame( + instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC); + if(ret != St25tbErrorNone) { + break; + } + + if(bit_buffer_get_size_bytes(instance->rx_buffer) != ST25TB_UID_SIZE) { + FURI_LOG_E(TAG, "Unexpected Get_UID() response size"); + ret = St25tbErrorCommunication; + break; + } + bit_buffer_write_bytes(instance->rx_buffer, uid, ST25TB_UID_SIZE); + FURI_SWAP(uid[0], uid[7]); + FURI_SWAP(uid[1], uid[6]); + FURI_SWAP(uid[2], uid[5]); + FURI_SWAP(uid[3], uid[4]); + FURI_LOG_I( + TAG, + "Got tag with uid: %02X %02X %02X %02X %02X %02X %02X %02X", + uid[0], + uid[1], + uid[2], + uid[3], + uid[4], + uid[5], + uid[6], + uid[7]); + } while(false); + return ret; +} + +St25tbError + st25tb_poller_read_block(St25tbPoller* instance, uint32_t* block, uint8_t block_number) { + furi_assert(instance); + furi_assert(instance->nfc); + furi_assert(block); + furi_assert( + (block_number <= st25tb_get_block_count(instance->data->type)) || + block_number == ST25TB_SYSTEM_OTP_BLOCK); + FURI_LOG_T(TAG, "reading block %d", block_number); + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + // Send Read_block(Addr) + bit_buffer_append_byte(instance->tx_buffer, 0x08); + bit_buffer_append_byte(instance->tx_buffer, block_number); + St25tbError ret; + do { + ret = st25tb_poller_send_frame( + instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC); + if(ret != St25tbErrorNone) { + break; + } + + if(bit_buffer_get_size_bytes(instance->rx_buffer) != ST25TB_BLOCK_SIZE) { + FURI_LOG_E(TAG, "Unexpected Read_block(Addr) response size"); + ret = St25tbErrorCommunication; + break; + } + bit_buffer_write_bytes(instance->rx_buffer, block, ST25TB_BLOCK_SIZE); + FURI_LOG_D(TAG, "Read_block(%d) result: %08lX", block_number, *block); + } while(false); + + return ret; +} + +St25tbError + st25tb_poller_write_block(St25tbPoller* instance, uint32_t block, uint8_t block_number) { + furi_assert(instance); + furi_assert(instance->nfc); + furi_assert( + (block_number <= st25tb_get_block_count(instance->data->type)) || + block_number == ST25TB_SYSTEM_OTP_BLOCK); + FURI_LOG_T(TAG, "writing block %d", block_number); + bit_buffer_reset(instance->tx_buffer); + + // Send Write_block(Addr, Data) + bit_buffer_append_byte(instance->tx_buffer, 0x09); + bit_buffer_append_byte(instance->tx_buffer, block_number); + bit_buffer_append_bytes(instance->tx_buffer, (uint8_t*)&block, ST25TB_BLOCK_SIZE); + St25tbError ret; + do { + ret = st25tb_poller_send_frame( + instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC); + if(ret != St25tbErrorTimeout) { // tag doesn't ack writes so timeout are expected. + break; + } + + furi_delay_ms(7); // 7ms is the max programming time as per datasheet + + uint32_t block_check; + ret = st25tb_poller_read_block(instance, &block_check, block_number); + if(ret != St25tbErrorNone) { + FURI_LOG_E(TAG, "write verification failed: read error"); + break; + } + if(block_check != block) { + FURI_LOG_E( + TAG, + "write verification failed: wrote %08lX but read back %08lX", + block, + block_check); + ret = St25tbErrorWriteFailed; + break; + } + FURI_LOG_D(TAG, "wrote %08lX to block %d", block, block_number); + } while(false); + + return ret; +} + +St25tbError st25tb_poller_halt(St25tbPoller* instance) { + furi_assert(instance); + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_reset(instance->rx_buffer); + + // Send Completion() + bit_buffer_append_byte(instance->tx_buffer, 0x0F); + + St25tbError ret; + + do { + ret = st25tb_poller_send_frame( + instance, instance->tx_buffer, instance->rx_buffer, ST25TB_FDT_FC); + if(ret != St25tbErrorTimeout) { + break; + } + + instance->state = St25tbPollerStateSelect; + } while(false); + + return ret; +} diff --git a/lib/nfc/protocols/st25tb/st25tb_poller_i.h b/lib/nfc/protocols/st25tb/st25tb_poller_i.h new file mode 100644 index 00000000000..e16feb7812c --- /dev/null +++ b/lib/nfc/protocols/st25tb/st25tb_poller_i.h @@ -0,0 +1,58 @@ +#pragma once + +#include "st25tb_poller.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ST25TB_POLLER_MAX_BUFFER_SIZE (16U) + +typedef enum { + St25tbPollerStateSelect, + St25tbPollerStateRequestMode, + St25tbPollerStateRead, + St25tbPollerStateWrite, + St25tbPollerStateSuccess, + St25tbPollerStateFailure, + + St25tbPollerStateNum, +} St25tbPollerState; + +typedef struct { + uint8_t current_block; +} St25tbPollerReadContext; + +typedef struct { + uint8_t block_number; + uint32_t block_data; +} St25tbPollerWriteContext; + +typedef union { + St25tbPollerReadContext read; + St25tbPollerWriteContext write; +} St25tbPollerContext; + +struct St25tbPoller { + Nfc* nfc; + St25tbPollerState state; + St25tbData* data; + BitBuffer* tx_buffer; + BitBuffer* rx_buffer; + + St25tbPollerContext poller_ctx; + + NfcGenericEvent general_event; + St25tbPollerEvent st25tb_event; + St25tbPollerEventData st25tb_event_data; + NfcGenericCallback callback; + void* context; +}; + +const St25tbData* st25tb_poller_get_data(St25tbPoller* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/st25tb/st25tb_poller_sync.c b/lib/nfc/protocols/st25tb/st25tb_poller_sync.c new file mode 100644 index 00000000000..3cd0b379290 --- /dev/null +++ b/lib/nfc/protocols/st25tb/st25tb_poller_sync.c @@ -0,0 +1,211 @@ +#include "st25tb_poller_sync.h" +#include "st25tb_poller_i.h" + +#define ST25TB_POLLER_FLAG_COMMAND_COMPLETE (1UL << 0) + +typedef enum { + St25tbPollerCmdTypeDetectType, + St25tbPollerCmdTypeReadBlock, + St25tbPollerCmdTypeWriteBlock, + + St25tbPollerCmdTypeNum, +} St25tbPollerCmdType; + +typedef struct { + St25tbType* type; +} St25tbPollerCmdDetectTypeData; + +typedef struct { + St25tbData* data; +} St25tbPollerCmdReadData; + +typedef struct { + uint8_t block_num; + uint32_t* block; +} St25tbPollerCmdReadBlockData; + +typedef struct { + uint8_t block_num; + uint32_t block; +} St25tbPollerCmdWriteBlockData; + +typedef union { + St25tbPollerCmdDetectTypeData detect_type; + St25tbPollerCmdReadData read; + St25tbPollerCmdReadBlockData read_block; + St25tbPollerCmdWriteBlockData write_block; +} St25tbPollerCmdData; + +typedef struct { + FuriThreadId thread_id; + St25tbError error; + St25tbPollerCmdType cmd_type; + St25tbPollerCmdData cmd_data; +} St25tbPollerSyncContext; + +typedef St25tbError (*St25tbPollerCmdHandler)(St25tbPoller* poller, St25tbPollerCmdData* data); + +static St25tbError st25tb_poller_detect_handler(St25tbPoller* poller, St25tbPollerCmdData* data) { + uint8_t uid[ST25TB_UID_SIZE]; + St25tbError error = st25tb_poller_get_uid(poller, uid); + if(error == St25tbErrorNone) { + *data->detect_type.type = st25tb_get_type_from_uid(uid); + } + return error; +} + +static St25tbError + st25tb_poller_read_block_handler(St25tbPoller* poller, St25tbPollerCmdData* data) { + return st25tb_poller_read_block(poller, data->read_block.block, data->read_block.block_num); +} + +static St25tbError + st25tb_poller_write_block_handler(St25tbPoller* poller, St25tbPollerCmdData* data) { + return st25tb_poller_write_block(poller, data->write_block.block, data->write_block.block_num); +} + +static St25tbPollerCmdHandler st25tb_poller_cmd_handlers[St25tbPollerCmdTypeNum] = { + [St25tbPollerCmdTypeDetectType] = st25tb_poller_detect_handler, + [St25tbPollerCmdTypeReadBlock] = st25tb_poller_read_block_handler, + [St25tbPollerCmdTypeWriteBlock] = st25tb_poller_write_block_handler, +}; + +static NfcCommand st25tb_poller_cmd_callback(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.instance); + furi_assert(event.protocol == NfcProtocolSt25tb); + + St25tbPollerSyncContext* poller_context = context; + St25tbPoller* st25tb_poller = event.instance; + St25tbPollerEvent* st25tb_event = event.event_data; + + if(st25tb_event->type == St25tbPollerEventTypeReady) { + poller_context->error = st25tb_poller_cmd_handlers[poller_context->cmd_type]( + st25tb_poller, &poller_context->cmd_data); + } else { + poller_context->error = st25tb_event->data->error; + } + + furi_thread_flags_set(poller_context->thread_id, ST25TB_POLLER_FLAG_COMMAND_COMPLETE); + + return NfcCommandStop; +} + +static St25tbError st25tb_poller_cmd_execute(Nfc* nfc, St25tbPollerSyncContext* poller_ctx) { + furi_assert(nfc); + furi_assert(poller_ctx->cmd_type < St25tbPollerCmdTypeNum); + poller_ctx->thread_id = furi_thread_get_current_id(); + + NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolSt25tb); + nfc_poller_start(poller, st25tb_poller_cmd_callback, poller_ctx); + furi_thread_flags_wait(ST25TB_POLLER_FLAG_COMMAND_COMPLETE, FuriFlagWaitAny, FuriWaitForever); + furi_thread_flags_clear(ST25TB_POLLER_FLAG_COMMAND_COMPLETE); + + nfc_poller_stop(poller); + nfc_poller_free(poller); + + return poller_ctx->error; +} + +St25tbError st25tb_poller_sync_read_block(Nfc* nfc, uint8_t block_num, uint32_t* block) { + furi_assert(block); + St25tbPollerSyncContext poller_context = { + .cmd_type = St25tbPollerCmdTypeReadBlock, + .cmd_data = + { + .read_block = + { + .block = block, + .block_num = block_num, + }, + }, + }; + return st25tb_poller_cmd_execute(nfc, &poller_context); +} + +St25tbError st25tb_poller_sync_write_block(Nfc* nfc, uint8_t block_num, uint32_t block) { + St25tbPollerSyncContext poller_context = { + .cmd_type = St25tbPollerCmdTypeWriteBlock, + .cmd_data = + { + .write_block = + { + .block = block, + .block_num = block_num, + }, + }, + }; + return st25tb_poller_cmd_execute(nfc, &poller_context); +} + +St25tbError st25tb_poller_sync_detect_type(Nfc* nfc, St25tbType* type) { + furi_assert(type); + St25tbPollerSyncContext poller_context = { + .cmd_type = St25tbPollerCmdTypeDetectType, + .cmd_data = + { + .detect_type = + { + .type = type, + }, + }, + }; + return st25tb_poller_cmd_execute(nfc, &poller_context); +} + +static NfcCommand nfc_scene_read_poller_callback_st25tb(NfcGenericEvent event, void* context) { + furi_assert(context); + furi_assert(event.event_data); + furi_assert(event.instance); + furi_assert(event.protocol == NfcProtocolSt25tb); + + St25tbPollerSyncContext* poller_context = context; + St25tbPollerEvent* st25tb_event = event.event_data; + + NfcCommand command = NfcCommandContinue; + if(st25tb_event->type == St25tbPollerEventTypeRequestMode) { + st25tb_event->data->mode_request.mode = St25tbPollerModeRead; + } else if( + st25tb_event->type == St25tbPollerEventTypeSuccess || + st25tb_event->type == St25tbPollerEventTypeFailure) { + if(st25tb_event->type == St25tbPollerEventTypeSuccess) { + memcpy( + poller_context->cmd_data.read.data, + st25tb_poller_get_data(event.instance), + sizeof(St25tbData)); + } else { + poller_context->error = st25tb_event->data->error; + } + command = NfcCommandStop; + furi_thread_flags_set(poller_context->thread_id, ST25TB_POLLER_FLAG_COMMAND_COMPLETE); + } + + return command; +} + +St25tbError st25tb_poller_sync_read(Nfc* nfc, St25tbData* data) { + furi_assert(nfc); + furi_assert(data); + + St25tbPollerSyncContext poller_context = { + .thread_id = furi_thread_get_current_id(), + .cmd_data = + { + .read = + { + .data = data, + }, + }, + }; + + NfcPoller* poller = nfc_poller_alloc(nfc, NfcProtocolSt25tb); + nfc_poller_start(poller, nfc_scene_read_poller_callback_st25tb, &poller_context); + furi_thread_flags_wait(ST25TB_POLLER_FLAG_COMMAND_COMPLETE, FuriFlagWaitAny, FuriWaitForever); + furi_thread_flags_clear(ST25TB_POLLER_FLAG_COMMAND_COMPLETE); + + nfc_poller_stop(poller); + nfc_poller_free(poller); + + return poller_context.error; +} \ No newline at end of file diff --git a/lib/nfc/protocols/st25tb/st25tb_poller_sync.h b/lib/nfc/protocols/st25tb/st25tb_poller_sync.h new file mode 100644 index 00000000000..ecd994b3981 --- /dev/null +++ b/lib/nfc/protocols/st25tb/st25tb_poller_sync.h @@ -0,0 +1,20 @@ +#pragma once + +#include "st25tb.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +St25tbError st25tb_poller_sync_read_block(Nfc* nfc, uint8_t block_num, uint32_t* block); + +St25tbError st25tb_poller_sync_write_block(Nfc* nfc, uint8_t block_num, uint32_t block); + +St25tbError st25tb_poller_sync_detect_type(Nfc* nfc, St25tbType* type); + +St25tbError st25tb_poller_sync_read(Nfc* nfc, St25tbData* data); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/one_wire/SConscript b/lib/one_wire/SConscript new file mode 100644 index 00000000000..2dde9153df9 --- /dev/null +++ b/lib/one_wire/SConscript @@ -0,0 +1,24 @@ +Import("env") + +env.Append( + LINT_SOURCES=[ + Dir("."), + ], + CPPPATH=[ + "#/lib/one_wire", + ], + SDK_HEADERS=[ + File("one_wire_host.h"), + File("one_wire_slave.h"), + File("maxim_crc.h"), + ], +) + +libenv = env.Clone(FW_LIB_NAME="one_wire") +libenv.ApplyLibFlags() + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/one_wire/ibutton/encoder/encoder_cyfral.c b/lib/one_wire/ibutton/encoder/encoder_cyfral.c deleted file mode 100644 index 0b506b2b04b..00000000000 --- a/lib/one_wire/ibutton/encoder/encoder_cyfral.c +++ /dev/null @@ -1,126 +0,0 @@ -#include "encoder_cyfral.h" -#include - -#define CYFRAL_DATA_SIZE sizeof(uint16_t) -#define CYFRAL_PERIOD (125 * furi_hal_cortex_instructions_per_microsecond()) -#define CYFRAL_0_LOW (CYFRAL_PERIOD * 0.66f) -#define CYFRAL_0_HI (CYFRAL_PERIOD * 0.33f) -#define CYFRAL_1_LOW (CYFRAL_PERIOD * 0.33f) -#define CYFRAL_1_HI (CYFRAL_PERIOD * 0.66f) - -#define CYFRAL_SET_DATA(level, len) \ - *polarity = level; \ - *length = len; - -struct EncoderCyfral { - uint32_t data; - uint32_t index; -}; - -EncoderCyfral* encoder_cyfral_alloc() { - EncoderCyfral* cyfral = malloc(sizeof(EncoderCyfral)); - encoder_cyfral_reset(cyfral); - return cyfral; -} - -void encoder_cyfral_free(EncoderCyfral* cyfral) { - free(cyfral); -} - -void encoder_cyfral_reset(EncoderCyfral* cyfral) { - cyfral->data = 0; - cyfral->index = 0; -} - -uint32_t cyfral_encoder_encode(const uint16_t data) { - uint32_t value = 0; - for(int8_t i = 0; i <= 7; i++) { - switch((data >> (i * 2)) & 0b00000011) { - case 0b11: - value = value << 4; - value += 0b00000111; - break; - case 0b10: - value = value << 4; - value += 0b00001011; - break; - case 0b01: - value = value << 4; - value += 0b00001101; - break; - case 0b00: - value = value << 4; - value += 0b00001110; - break; - default: - break; - } - } - - return value; -} - -void encoder_cyfral_set_data(EncoderCyfral* cyfral, const uint8_t* data, size_t data_size) { - furi_assert(cyfral); - furi_check(data_size >= CYFRAL_DATA_SIZE); - uint16_t intermediate; - memcpy(&intermediate, data, CYFRAL_DATA_SIZE); - cyfral->data = cyfral_encoder_encode(intermediate); -} - -void encoder_cyfral_get_pulse(EncoderCyfral* cyfral, bool* polarity, uint32_t* length) { - if(cyfral->index < 8) { - // start word (0b0001) - switch(cyfral->index) { - case 0: - CYFRAL_SET_DATA(false, CYFRAL_0_LOW); - break; - case 1: - CYFRAL_SET_DATA(true, CYFRAL_0_HI); - break; - case 2: - CYFRAL_SET_DATA(false, CYFRAL_0_LOW); - break; - case 3: - CYFRAL_SET_DATA(true, CYFRAL_0_HI); - break; - case 4: - CYFRAL_SET_DATA(false, CYFRAL_0_LOW); - break; - case 5: - CYFRAL_SET_DATA(true, CYFRAL_0_HI); - break; - case 6: - CYFRAL_SET_DATA(false, CYFRAL_1_LOW); - break; - case 7: - CYFRAL_SET_DATA(true, CYFRAL_1_HI); - break; - } - } else { - // data - uint8_t data_start_index = cyfral->index - 8; - bool clock_polarity = (data_start_index) % 2; - uint8_t bit_index = (data_start_index) / 2; - bool bit_value = ((cyfral->data >> bit_index) & 1); - - if(!clock_polarity) { - if(bit_value) { - CYFRAL_SET_DATA(false, CYFRAL_1_LOW); - } else { - CYFRAL_SET_DATA(false, CYFRAL_0_LOW); - } - } else { - if(bit_value) { - CYFRAL_SET_DATA(true, CYFRAL_1_HI); - } else { - CYFRAL_SET_DATA(true, CYFRAL_0_HI); - } - } - } - - cyfral->index++; - if(cyfral->index >= (9 * 4 * 2)) { - cyfral->index = 0; - } -} diff --git a/lib/one_wire/ibutton/encoder/encoder_cyfral.h b/lib/one_wire/ibutton/encoder/encoder_cyfral.h deleted file mode 100644 index 4fc7be5ed8d..00000000000 --- a/lib/one_wire/ibutton/encoder/encoder_cyfral.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @file encoder_cyfral.h - * - * Cyfral pulse format encoder - */ - -#pragma once -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct EncoderCyfral EncoderCyfral; - -/** - * Allocate Cyfral encoder - * @return EncoderCyfral* - */ -EncoderCyfral* encoder_cyfral_alloc(); - -/** - * Deallocate Cyfral encoder - * @param cyfral - */ -void encoder_cyfral_free(EncoderCyfral* cyfral); - -/** - * Reset Cyfral encoder - * @param cyfral - */ -void encoder_cyfral_reset(EncoderCyfral* cyfral); - -/** - * Set data to be encoded to Cyfral pulse format, 2 bytes - * @param cyfral - * @param data - * @param data_size - */ -void encoder_cyfral_set_data(EncoderCyfral* cyfral, const uint8_t* data, size_t data_size); - -/** - * Pop pulse from Cyfral encoder - * @param cyfral - * @param polarity - * @param length - */ -void encoder_cyfral_get_pulse(EncoderCyfral* cyfral, bool* polarity, uint32_t* length); - -#ifdef __cplusplus -} -#endif diff --git a/lib/one_wire/ibutton/encoder/encoder_metakom.c b/lib/one_wire/ibutton/encoder/encoder_metakom.c deleted file mode 100644 index 5b978ebe285..00000000000 --- a/lib/one_wire/ibutton/encoder/encoder_metakom.c +++ /dev/null @@ -1,93 +0,0 @@ -#include "encoder_metakom.h" -#include - -#define METAKOM_DATA_SIZE sizeof(uint32_t) -#define METAKOM_PERIOD (125 * furi_hal_cortex_instructions_per_microsecond()) -#define METAKOM_0_LOW (METAKOM_PERIOD * 0.33f) -#define METAKOM_0_HI (METAKOM_PERIOD * 0.66f) -#define METAKOM_1_LOW (METAKOM_PERIOD * 0.66f) -#define METAKOM_1_HI (METAKOM_PERIOD * 0.33f) - -#define METAKOM_SET_DATA(level, len) \ - *polarity = !level; \ - *length = len; - -struct EncoderMetakom { - uint32_t data; - uint32_t index; -}; - -EncoderMetakom* encoder_metakom_alloc() { - EncoderMetakom* metakom = malloc(sizeof(EncoderMetakom)); - encoder_metakom_reset(metakom); - return metakom; -} - -void encoder_metakom_free(EncoderMetakom* metakom) { - free(metakom); -} - -void encoder_metakom_reset(EncoderMetakom* metakom) { - metakom->data = 0; - metakom->index = 0; -} - -void encoder_metakom_set_data(EncoderMetakom* metakom, const uint8_t* data, size_t data_size) { - furi_assert(metakom); - furi_check(data_size >= METAKOM_DATA_SIZE); - memcpy(&metakom->data, data, METAKOM_DATA_SIZE); -} - -void encoder_metakom_get_pulse(EncoderMetakom* metakom, bool* polarity, uint32_t* length) { - if(metakom->index == 0) { - // sync bit - METAKOM_SET_DATA(true, METAKOM_PERIOD); - } else if(metakom->index >= 1 && metakom->index <= 6) { - // start word (0b010) - switch(metakom->index) { - case 1: - METAKOM_SET_DATA(false, METAKOM_0_LOW); - break; - case 2: - METAKOM_SET_DATA(true, METAKOM_0_HI); - break; - case 3: - METAKOM_SET_DATA(false, METAKOM_1_LOW); - break; - case 4: - METAKOM_SET_DATA(true, METAKOM_1_HI); - break; - case 5: - METAKOM_SET_DATA(false, METAKOM_0_LOW); - break; - case 6: - METAKOM_SET_DATA(true, METAKOM_0_HI); - break; - } - } else { - // data - uint8_t data_start_index = metakom->index - 7; - bool clock_polarity = (data_start_index) % 2; - uint8_t bit_index = (data_start_index) / 2; - bool bit_value = (metakom->data >> (32 - 1 - bit_index)) & 1; - - if(!clock_polarity) { - if(bit_value) { - METAKOM_SET_DATA(false, METAKOM_1_LOW); - } else { - METAKOM_SET_DATA(false, METAKOM_0_LOW); - } - } else { - if(bit_value) { - METAKOM_SET_DATA(true, METAKOM_1_HI); - } else { - METAKOM_SET_DATA(true, METAKOM_0_HI); - } - } - } - - metakom->index++; - if(metakom->index >= (1 + 3 * 2 + 32 * 2)) { - metakom->index = 0; - } -} diff --git a/lib/one_wire/ibutton/encoder/encoder_metakom.h b/lib/one_wire/ibutton/encoder/encoder_metakom.h deleted file mode 100644 index 50ff11a243c..00000000000 --- a/lib/one_wire/ibutton/encoder/encoder_metakom.h +++ /dev/null @@ -1,54 +0,0 @@ -/** - * @file encoder_metakom.h - * - * Metakom pulse format encoder - */ - -#pragma once -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct EncoderMetakom EncoderMetakom; - -/** - * Allocate Metakom encoder - * @return EncoderMetakom* - */ -EncoderMetakom* encoder_metakom_alloc(); - -/** - * Deallocate Metakom encoder - * @param metakom - */ -void encoder_metakom_free(EncoderMetakom* metakom); - -/** - * Reset Metakom encoder - * @param metakom - */ -void encoder_metakom_reset(EncoderMetakom* metakom); - -/** - * Set data to be encoded to Metakom pulse format, 4 bytes - * @param metakom - * @param data - * @param data_size - */ -void encoder_metakom_set_data(EncoderMetakom* metakom, const uint8_t* data, size_t data_size); - -/** - * Pop pulse from Metakom encoder - * @param cyfral - * @param polarity - * @param length - */ -void encoder_metakom_get_pulse(EncoderMetakom* metakom, bool* polarity, uint32_t* length); - -#ifdef __cplusplus -} -#endif diff --git a/lib/one_wire/ibutton/ibutton_key.c b/lib/one_wire/ibutton/ibutton_key.c deleted file mode 100644 index 2c0f7fa2698..00000000000 --- a/lib/one_wire/ibutton/ibutton_key.c +++ /dev/null @@ -1,112 +0,0 @@ -#include -#include -#include "ibutton_key.h" - -struct iButtonKey { - uint8_t data[IBUTTON_KEY_DATA_SIZE]; - iButtonKeyType type; -}; - -iButtonKey* ibutton_key_alloc() { - iButtonKey* key = malloc(sizeof(iButtonKey)); - memset(key, 0, sizeof(iButtonKey)); - return key; -} - -void ibutton_key_free(iButtonKey* key) { - free(key); -} - -void ibutton_key_set(iButtonKey* to, const iButtonKey* from) { - memcpy(to, from, sizeof(iButtonKey)); -} - -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); -} - -void ibutton_key_clear_data(iButtonKey* key) { - memset(key->data, 0, IBUTTON_KEY_DATA_SIZE); -} - -const uint8_t* ibutton_key_get_data_p(iButtonKey* key) { - return key->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"); - return ""; - break; - } -} - -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); -} diff --git a/lib/one_wire/ibutton/ibutton_key.h b/lib/one_wire/ibutton/ibutton_key.h deleted file mode 100644 index f66537d7ea9..00000000000 --- a/lib/one_wire/ibutton/ibutton_key.h +++ /dev/null @@ -1,131 +0,0 @@ -/** - * @file ibutton_key.h - * - * iButton key data holder - */ - -#pragma once - -#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* - */ -iButtonKey* ibutton_key_alloc(); - -/** - * Free key - * @param key - */ -void ibutton_key_free(iButtonKey* key); - -/** - * Copy key - * @param to - * @param from - */ -void ibutton_key_set(iButtonKey* to, const iButtonKey* from); - -/** - * Set key data - * @param key - * @param data - * @param data_count - */ -void ibutton_key_set_data(iButtonKey* key, uint8_t* data, uint8_t data_count); - -/** - * Clear key data - * @param key - */ -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); - -#ifdef __cplusplus -} -#endif 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 88049d0643d..00000000000 --- 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_worker.c b/lib/one_wire/ibutton/ibutton_worker.c deleted file mode 100644 index 755ed32f3e9..00000000000 --- a/lib/one_wire/ibutton/ibutton_worker.c +++ /dev/null @@ -1,213 +0,0 @@ -#include -#include -#include -#include "ibutton_worker_i.h" - -typedef enum { - iButtonMessageEnd, - iButtonMessageStop, - iButtonMessageRead, - iButtonMessageWrite, - iButtonMessageEmulate, - iButtonMessageNotifyEmulate, -} iButtonMessageType; - -typedef struct { - iButtonMessageType type; - union { - iButtonKey* key; - } data; -} iButtonMessage; - -static int32_t ibutton_worker_thread(void* thread_context); - -iButtonWorker* ibutton_worker_alloc() { - 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->pulse_decoder = pulse_decoder_alloc(); - worker->protocol_cyfral = protocol_cyfral_alloc(); - worker->protocol_metakom = protocol_metakom_alloc(); - worker->messages = furi_message_queue_alloc(1, sizeof(iButtonMessage)); - worker->mode_index = iButtonWorkerIdle; - worker->last_dwt_value = 0; - worker->read_cb = NULL; - worker->write_cb = NULL; - worker->emulate_cb = NULL; - worker->cb_ctx = NULL; - - worker->encoder_cyfral = encoder_cyfral_alloc(); - worker->encoder_metakom = encoder_metakom_alloc(); - - worker->thread = furi_thread_alloc(); - furi_thread_set_name(worker->thread, "ibutton_worker"); - furi_thread_set_callback(worker->thread, ibutton_worker_thread); - furi_thread_set_context(worker->thread, worker); - furi_thread_set_stack_size(worker->thread, 2048); - - pulse_decoder_add_protocol( - worker->pulse_decoder, - protocol_cyfral_get_protocol(worker->protocol_cyfral), - PulseProtocolCyfral); - pulse_decoder_add_protocol( - worker->pulse_decoder, - protocol_metakom_get_protocol(worker->protocol_metakom), - PulseProtocolMetakom); - - return worker; -} - -void ibutton_worker_read_set_callback( - iButtonWorker* worker, - iButtonWorkerReadCallback callback, - void* context) { - furi_check(worker->mode_index == iButtonWorkerIdle); - worker->read_cb = callback; - worker->cb_ctx = context; -} - -void ibutton_worker_write_set_callback( - iButtonWorker* worker, - iButtonWorkerWriteCallback callback, - void* context) { - furi_check(worker->mode_index == iButtonWorkerIdle); - worker->write_cb = callback; - worker->cb_ctx = context; -} - -void ibutton_worker_emulate_set_callback( - iButtonWorker* worker, - iButtonWorkerEmulateCallback callback, - void* context) { - furi_check(worker->mode_index == iButtonWorkerIdle); - worker->emulate_cb = callback; - worker->cb_ctx = context; -} - -void ibutton_worker_read_start(iButtonWorker* worker, iButtonKey* key) { - iButtonMessage message = {.type = iButtonMessageRead, .data.key = key}; - furi_check( - 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}; - furi_check( - furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk); -} - -void ibutton_worker_emulate_start(iButtonWorker* worker, iButtonKey* key) { - iButtonMessage message = {.type = iButtonMessageEmulate, .data.key = key}; - furi_check( - furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk); -} - -void ibutton_worker_stop(iButtonWorker* worker) { - iButtonMessage message = {.type = iButtonMessageStop}; - furi_check( - furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk); -} - -void ibutton_worker_free(iButtonWorker* worker) { - pulse_decoder_free(worker->pulse_decoder); - protocol_metakom_free(worker->protocol_metakom); - protocol_cyfral_free(worker->protocol_cyfral); - - ibutton_writer_free(worker->writer); - - onewire_slave_free(worker->slave); - - onewire_host_free(worker->host); - onewire_device_free(worker->device); - - encoder_cyfral_free(worker->encoder_cyfral); - encoder_metakom_free(worker->encoder_metakom); - - furi_message_queue_free(worker->messages); - - furi_thread_free(worker->thread); - free(worker->key_data); - free(worker); -} - -void ibutton_worker_start_thread(iButtonWorker* worker) { - furi_thread_start(worker->thread); -} - -void ibutton_worker_stop_thread(iButtonWorker* worker) { - iButtonMessage message = {.type = iButtonMessageEnd}; - furi_check( - furi_message_queue_put(worker->messages, &message, FuriWaitForever) == FuriStatusOk); - furi_thread_join(worker->thread); -} - -void ibutton_worker_switch_mode(iButtonWorker* worker, iButtonWorkerMode mode) { - ibutton_worker_modes[worker->mode_index].stop(worker); - worker->mode_index = mode; - ibutton_worker_modes[worker->mode_index].start(worker); -} - -void ibutton_worker_notify_emulate(iButtonWorker* worker) { - iButtonMessage message = {.type = iButtonMessageNotifyEmulate}; - furi_check(furi_message_queue_put(worker->messages, &message, 0) == FuriStatusOk); -} - -void ibutton_worker_set_key_p(iButtonWorker* worker, iButtonKey* key) { - worker->key_p = key; -} - -static int32_t ibutton_worker_thread(void* thread_context) { - iButtonWorker* worker = thread_context; - bool running = true; - iButtonMessage message; - FuriStatus status; - - ibutton_worker_modes[worker->mode_index].start(worker); - - while(running) { - status = furi_message_queue_get( - worker->messages, &message, ibutton_worker_modes[worker->mode_index].quant); - if(status == FuriStatusOk) { - switch(message.type) { - case iButtonMessageEnd: - ibutton_worker_switch_mode(worker, iButtonWorkerIdle); - ibutton_worker_set_key_p(worker, NULL); - running = false; - break; - case iButtonMessageStop: - ibutton_worker_switch_mode(worker, iButtonWorkerIdle); - 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); - break; - case iButtonMessageWrite: - ibutton_worker_set_key_p(worker, message.data.key); - ibutton_worker_switch_mode(worker, iButtonWorkerWrite); - break; - case iButtonMessageEmulate: - ibutton_worker_set_key_p(worker, message.data.key); - ibutton_worker_switch_mode(worker, iButtonWorkerEmulate); - break; - case iButtonMessageNotifyEmulate: - if(worker->emulate_cb) { - worker->emulate_cb(worker->cb_ctx, true); - } - break; - } - } else if(status == FuriStatusErrorTimeout) { - ibutton_worker_modes[worker->mode_index].tick(worker); - } else { - furi_crash("iButton worker error"); - } - } - - ibutton_worker_modes[worker->mode_index].stop(worker); - - return 0; -} diff --git a/lib/one_wire/ibutton/ibutton_worker_i.h b/lib/one_wire/ibutton/ibutton_worker_i.h deleted file mode 100644 index 28588443943..00000000000 --- a/lib/one_wire/ibutton/ibutton_worker_i.h +++ /dev/null @@ -1,80 +0,0 @@ -/** - * @file ibutton_worker_i.h - * - * iButton worker, internal definitions - */ - -#pragma once -#include "ibutton_worker.h" -#include "ibutton_writer.h" -#include "../one_wire_host.h" -#include "../one_wire_slave.h" -#include "../one_wire_device.h" -#include "../pulse_protocols/pulse_decoder.h" -#include "pulse_protocols/protocol_cyfral.h" -#include "pulse_protocols/protocol_metakom.h" -#include "encoder/encoder_cyfral.h" -#include "encoder/encoder_metakom.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - PulseProtocolCyfral, - PulseProtocolMetakom, -} PulseProtocols; - -typedef struct { - const uint32_t quant; - void (*const start)(iButtonWorker* worker); - void (*const tick)(iButtonWorker* worker); - void (*const stop)(iButtonWorker* worker); -} iButtonWorkerModeType; - -typedef enum { - iButtonWorkerIdle = 0, - iButtonWorkerRead = 1, - iButtonWorkerWrite = 2, - iButtonWorkerEmulate = 3, -} iButtonWorkerMode; - -typedef enum { - iButtonEmulateModeCyfral, - iButtonEmulateModeMetakom, -} iButtonEmulateMode; - -struct iButtonWorker { - iButtonKey* key_p; - uint8_t* key_data; - OneWireHost* host; - OneWireSlave* slave; - OneWireDevice* device; - iButtonWriter* writer; - iButtonWorkerMode mode_index; - FuriMessageQueue* messages; - FuriThread* thread; - - PulseDecoder* pulse_decoder; - ProtocolCyfral* protocol_cyfral; - ProtocolMetakom* protocol_metakom; - uint32_t last_dwt_value; - - iButtonWorkerReadCallback read_cb; - iButtonWorkerWriteCallback write_cb; - iButtonWorkerEmulateCallback emulate_cb; - void* cb_ctx; - - EncoderCyfral* encoder_cyfral; - EncoderMetakom* encoder_metakom; - iButtonEmulateMode emulate_mode; -}; - -extern const iButtonWorkerModeType ibutton_worker_modes[]; - -void ibutton_worker_switch_mode(iButtonWorker* worker, iButtonWorkerMode mode); -void ibutton_worker_notify_emulate(iButtonWorker* worker); - -#ifdef __cplusplus -} -#endif diff --git a/lib/one_wire/ibutton/ibutton_worker_modes.c b/lib/one_wire/ibutton/ibutton_worker_modes.c deleted file mode 100644 index 78e05d0ee31..00000000000 --- a/lib/one_wire/ibutton/ibutton_worker_modes.c +++ /dev/null @@ -1,335 +0,0 @@ -#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); - -void ibutton_worker_mode_emulate_start(iButtonWorker* worker); -void ibutton_worker_mode_emulate_tick(iButtonWorker* worker); -void ibutton_worker_mode_emulate_stop(iButtonWorker* worker); - -void ibutton_worker_mode_read_start(iButtonWorker* worker); -void ibutton_worker_mode_read_tick(iButtonWorker* worker); -void ibutton_worker_mode_read_stop(iButtonWorker* worker); - -void ibutton_worker_mode_write_start(iButtonWorker* worker); -void ibutton_worker_mode_write_tick(iButtonWorker* worker); -void ibutton_worker_mode_write_stop(iButtonWorker* worker); - -const iButtonWorkerModeType ibutton_worker_modes[] = { - { - .quant = FuriWaitForever, - .start = ibutton_worker_mode_idle_start, - .tick = ibutton_worker_mode_idle_tick, - .stop = ibutton_worker_mode_idle_stop, - }, - { - .quant = 100, - .start = ibutton_worker_mode_read_start, - .tick = ibutton_worker_mode_read_tick, - .stop = ibutton_worker_mode_read_stop, - }, - { - .quant = 1000, - .start = ibutton_worker_mode_write_start, - .tick = ibutton_worker_mode_write_tick, - .stop = ibutton_worker_mode_write_stop, - }, - { - .quant = 1000, - .start = ibutton_worker_mode_emulate_start, - .tick = ibutton_worker_mode_emulate_tick, - .stop = ibutton_worker_mode_emulate_stop, - }, -}; - -/*********************** IDLE ***********************/ - -void ibutton_worker_mode_idle_start(iButtonWorker* worker) { - UNUSED(worker); -} - -void ibutton_worker_mode_idle_tick(iButtonWorker* worker) { - UNUSED(worker); -} - -void ibutton_worker_mode_idle_stop(iButtonWorker* worker) { - UNUSED(worker); -} - -/*********************** READ ***********************/ - -void ibutton_worker_comparator_callback(bool level, void* context) { - iButtonWorker* worker = context; - - uint32_t current_dwt_value = DWT->CYCCNT; - - pulse_decoder_process_pulse( - worker->pulse_decoder, level, current_dwt_value - worker->last_dwt_value); - - worker->last_dwt_value = current_dwt_value; -} - -bool ibutton_worker_read_comparator(iButtonWorker* worker) { - bool result = false; - - pulse_decoder_reset(worker->pulse_decoder); - - 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(); - furi_hal_rfid_comp_set_callback(ibutton_worker_comparator_callback, worker); - worker->last_dwt_value = DWT->CYCCNT; - furi_hal_rfid_comp_start(); - - // TODO: rework with thread events, "pulse_decoder_get_decoded_index_with_timeout" - furi_delay_ms(100); - int32_t decoded_index = pulse_decoder_get_decoded_index(worker->pulse_decoder); - if(decoded_index >= 0) { - pulse_decoder_get_data( - worker->pulse_decoder, decoded_index, worker->key_data, ibutton_key_get_max_size()); - } - - switch(decoded_index) { - case PulseProtocolCyfral: - 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 PulseProtocolMetakom: - 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; - break; - default: - break; - } - - furi_hal_rfid_comp_stop(); - furi_hal_rfid_comp_set_callback(NULL, NULL); - furi_hal_rfid_pins_reset(); - - 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(worker->read_cb != NULL) { - worker->read_cb(worker->cb_ctx); - } - - ibutton_worker_switch_mode(worker, iButtonWorkerIdle); - } -} - -void ibutton_worker_mode_read_stop(iButtonWorker* worker) { - UNUSED(worker); - furi_hal_power_disable_otg(); -} - -/*********************** 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_start(worker->slave); - onewire_slave_set_result_callback(worker->slave, onewire_slave_callback, worker); -} - -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; - - bool polarity; - uint32_t length; - - switch(worker->emulate_mode) { - case iButtonEmulateModeCyfral: - encoder_cyfral_get_pulse(worker->encoder_cyfral, &polarity, &length); - break; - case iButtonEmulateModeMetakom: - encoder_metakom_get_pulse(worker->encoder_metakom, &polarity, &length); - break; - } - - furi_hal_ibutton_emulate_set_next(length); - - if(polarity) { - 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->emulate_mode = iButtonEmulateModeCyfral; - encoder_cyfral_reset(worker->encoder_cyfral); - encoder_cyfral_set_data(worker->encoder_cyfral, key_id, key_size); - break; - case iButtonKeyMetakom: - worker->emulate_mode = iButtonEmulateModeMetakom; - encoder_metakom_reset(worker->encoder_metakom); - encoder_metakom_set_data(worker->encoder_metakom, key_id, key_size); - break; - } - - 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_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; - } -} - -void ibutton_worker_mode_emulate_tick(iButtonWorker* worker) { - UNUSED(worker); -} - -void ibutton_worker_mode_emulate_stop(iButtonWorker* worker) { - furi_assert(worker->key_p); - - 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) { - 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; - } - - 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); -} diff --git a/lib/one_wire/ibutton/ibutton_writer.c b/lib/one_wire/ibutton/ibutton_writer.c deleted file mode 100644 index 203c4fc077a..00000000000 --- 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); - - // 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 6dbdbb27a10..00000000000 --- 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/pulse_protocols/protocol_cyfral.c b/lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.c deleted file mode 100644 index 7c2897907be..00000000000 --- a/lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.c +++ /dev/null @@ -1,256 +0,0 @@ -#include "protocol_cyfral.h" -#include -#include -#include -#include - -#define CYFRAL_DATA_SIZE 2 -#define CYFRAL_MAX_PERIOD_US 230 - -typedef enum { - CYFRAL_BIT_WAIT_FRONT_HIGH, - CYFRAL_BIT_WAIT_FRONT_LOW, -} CyfralBitState; - -typedef enum { - CYFRAL_WAIT_START_NIBBLE, - CYFRAL_READ_NIBBLE, - CYFRAL_READ_STOP_NIBBLE, -} CyfralState; - -struct ProtocolCyfral { - PulseProtocol* protocol; - - CyfralState state; - CyfralBitState bit_state; - - // ready flag, key is read and valid - // TODO: atomic access - bool ready; - // key data storage - uint16_t key_data; - // high + low period time - uint32_t period_time; - // temporary nibble storage - uint8_t nibble; - // data valid flag - // MUST be checked only in READ_STOP_NIBBLE state - bool data_valid; - // nibble index, we expect 8 nibbles - uint8_t index; - // bit index in nibble, 4 bit per nibble - uint8_t bit_index; - // max period, 230us x clock per us - uint32_t max_period; -}; - -static void cyfral_pulse(void* context, bool polarity, uint32_t length); -static void cyfral_reset(void* context); -static void cyfral_get_data(void* context, uint8_t* data, size_t length); -static bool cyfral_decoded(void* context); - -ProtocolCyfral* protocol_cyfral_alloc() { - ProtocolCyfral* cyfral = malloc(sizeof(ProtocolCyfral)); - cyfral_reset(cyfral); - - cyfral->protocol = pulse_protocol_alloc(); - - pulse_protocol_set_context(cyfral->protocol, cyfral); - pulse_protocol_set_pulse_cb(cyfral->protocol, cyfral_pulse); - pulse_protocol_set_reset_cb(cyfral->protocol, cyfral_reset); - pulse_protocol_set_get_data_cb(cyfral->protocol, cyfral_get_data); - pulse_protocol_set_decoded_cb(cyfral->protocol, cyfral_decoded); - - return cyfral; -} - -void protocol_cyfral_free(ProtocolCyfral* cyfral) { - furi_assert(cyfral); - pulse_protocol_free(cyfral->protocol); - free(cyfral); -} - -PulseProtocol* protocol_cyfral_get_protocol(ProtocolCyfral* cyfral) { - furi_assert(cyfral); - return cyfral->protocol; -} - -static void cyfral_get_data(void* context, uint8_t* data, size_t length) { - furi_assert(context); - furi_check(length >= CYFRAL_DATA_SIZE); - ProtocolCyfral* cyfral = context; - memcpy(data, &cyfral->key_data, CYFRAL_DATA_SIZE); -} - -static bool cyfral_decoded(void* context) { - furi_assert(context); - ProtocolCyfral* cyfral = context; - bool decoded = cyfral->ready; - return decoded; -} - -static void cyfral_reset(void* context) { - furi_assert(context); - ProtocolCyfral* cyfral = context; - cyfral->state = CYFRAL_WAIT_START_NIBBLE; - cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW; - - cyfral->period_time = 0; - cyfral->bit_index = 0; - cyfral->ready = false; - cyfral->index = 0; - - cyfral->key_data = 0; - cyfral->nibble = 0; - cyfral->data_valid = true; - - cyfral->max_period = CYFRAL_MAX_PERIOD_US * furi_hal_cortex_instructions_per_microsecond(); -} - -static bool cyfral_process_bit( - ProtocolCyfral* cyfral, - bool polarity, - uint32_t length, - bool* bit_ready, - bool* bit_value) { - bool result = true; - *bit_ready = false; - - // bit start from low - switch(cyfral->bit_state) { - case CYFRAL_BIT_WAIT_FRONT_LOW: - if(polarity == true) { - cyfral->period_time += length; - - *bit_ready = true; - if(cyfral->period_time <= cyfral->max_period) { - if((cyfral->period_time / 2) > length) { - *bit_value = false; - } else { - *bit_value = true; - } - } else { - result = false; - } - - cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_HIGH; - } else { - result = false; - } - break; - case CYFRAL_BIT_WAIT_FRONT_HIGH: - if(polarity == false) { - cyfral->period_time = length; - cyfral->bit_state = CYFRAL_BIT_WAIT_FRONT_LOW; - } else { - result = false; - } - break; - } - - return result; -} - -static void cyfral_pulse(void* context, bool polarity, uint32_t length) { - furi_assert(context); - ProtocolCyfral* cyfral = context; - - bool bit_ready; - bool bit_value; - - if(cyfral->ready) return; - - switch(cyfral->state) { - case CYFRAL_WAIT_START_NIBBLE: - // wait for start word - if(cyfral_process_bit(cyfral, polarity, length, &bit_ready, &bit_value)) { - if(bit_ready) { - cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F; - if(cyfral->nibble == 0b0001) { - cyfral->nibble = 0; - cyfral->state = CYFRAL_READ_NIBBLE; - } - } - } else { - cyfral_reset(cyfral); - } - - break; - case CYFRAL_READ_NIBBLE: - // read nibbles - if(cyfral_process_bit(cyfral, polarity, length, &bit_ready, &bit_value)) { - if(bit_ready) { - cyfral->nibble = (cyfral->nibble << 1) | bit_value; - - cyfral->bit_index++; - - //convert every nibble to 2-bit index - if(cyfral->bit_index == 4) { - switch(cyfral->nibble) { - case 0b1110: - cyfral->key_data = (cyfral->key_data << 2) | 0b11; - break; - case 0b1101: - cyfral->key_data = (cyfral->key_data << 2) | 0b10; - break; - case 0b1011: - cyfral->key_data = (cyfral->key_data << 2) | 0b01; - break; - case 0b0111: - cyfral->key_data = (cyfral->key_data << 2) | 0b00; - break; - default: - cyfral->data_valid = false; - break; - } - - cyfral->nibble = 0; - cyfral->bit_index = 0; - cyfral->index++; - } - - // succefully read 8 nibbles - if(cyfral->index == 8) { - cyfral->state = CYFRAL_READ_STOP_NIBBLE; - } - } - } else { - cyfral_reset(cyfral); - } - break; - case CYFRAL_READ_STOP_NIBBLE: - // read stop nibble - if(cyfral_process_bit(cyfral, polarity, length, &bit_ready, &bit_value)) { - if(bit_ready) { - cyfral->nibble = ((cyfral->nibble << 1) | bit_value) & 0x0F; - cyfral->bit_index++; - - switch(cyfral->bit_index) { - case 0: - case 1: - case 2: - case 3: - break; - case 4: - if(cyfral->nibble == 0b0001) { - // validate data - if(cyfral->data_valid) { - cyfral->ready = true; - } else { - cyfral_reset(cyfral); - } - } else { - cyfral_reset(cyfral); - } - break; - default: - cyfral_reset(cyfral); - break; - } - } - } else { - cyfral_reset(cyfral); - } - break; - } -} diff --git a/lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.h b/lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.h deleted file mode 100644 index 10305da12f5..00000000000 --- a/lib/one_wire/ibutton/pulse_protocols/protocol_cyfral.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @file protocol_cyfral.h - * - * Cyfral pulse format decoder - */ - -#pragma once -#include -#include "../../pulse_protocols/pulse_protocol.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct ProtocolCyfral ProtocolCyfral; - -/** - * Allocate decoder - * @return ProtocolCyfral* - */ -ProtocolCyfral* protocol_cyfral_alloc(); - -/** - * Deallocate decoder - * @param cyfral - */ -void protocol_cyfral_free(ProtocolCyfral* cyfral); - -/** - * Get protocol interface - * @param cyfral - * @return PulseProtocol* - */ -PulseProtocol* protocol_cyfral_get_protocol(ProtocolCyfral* cyfral); - -#ifdef __cplusplus -} -#endif diff --git a/lib/one_wire/ibutton/pulse_protocols/protocol_metakom.c b/lib/one_wire/ibutton/pulse_protocols/protocol_metakom.c deleted file mode 100644 index 9df9e20bed1..00000000000 --- a/lib/one_wire/ibutton/pulse_protocols/protocol_metakom.c +++ /dev/null @@ -1,261 +0,0 @@ -#include "protocol_metakom.h" -#include -#include -#include - -#define METAKOM_DATA_SIZE 4 -#define METAKOM_PERIOD_SAMPLE_COUNT 10 - -typedef enum { - METAKOM_WAIT_PERIOD_SYNC, - METAKOM_WAIT_START_BIT, - METAKOM_WAIT_START_WORD, - METAKOM_READ_WORD, - METAKOM_READ_STOP_WORD, -} MetakomState; - -typedef enum { - METAKOM_BIT_WAIT_FRONT_HIGH, - METAKOM_BIT_WAIT_FRONT_LOW, -} MetakomBitState; - -struct ProtocolMetakom { - PulseProtocol* protocol; - - // high + low period time - uint32_t period_time; - uint32_t low_time_storage; - uint8_t period_sample_index; - uint32_t period_sample_data[METAKOM_PERIOD_SAMPLE_COUNT]; - - // ready flag - // TODO: atomic access - bool ready; - - uint8_t tmp_data; - uint8_t tmp_counter; - - uint32_t key_data; - uint8_t key_data_index; - - MetakomBitState bit_state; - MetakomState state; -}; - -static void metakom_pulse(void* context, bool polarity, uint32_t length); -static void metakom_reset(void* context); -static void metakom_get_data(void* context, uint8_t* data, size_t length); -static bool metakom_decoded(void* context); - -ProtocolMetakom* protocol_metakom_alloc() { - ProtocolMetakom* metakom = malloc(sizeof(ProtocolMetakom)); - metakom_reset(metakom); - - metakom->protocol = pulse_protocol_alloc(); - - pulse_protocol_set_context(metakom->protocol, metakom); - pulse_protocol_set_pulse_cb(metakom->protocol, metakom_pulse); - pulse_protocol_set_reset_cb(metakom->protocol, metakom_reset); - pulse_protocol_set_get_data_cb(metakom->protocol, metakom_get_data); - pulse_protocol_set_decoded_cb(metakom->protocol, metakom_decoded); - - return metakom; -} - -void protocol_metakom_free(ProtocolMetakom* metakom) { - furi_assert(metakom); - pulse_protocol_free(metakom->protocol); - free(metakom); -} - -PulseProtocol* protocol_metakom_get_protocol(ProtocolMetakom* metakom) { - furi_assert(metakom); - return metakom->protocol; -} - -static void metakom_get_data(void* context, uint8_t* data, size_t length) { - furi_assert(context); - furi_check(length >= METAKOM_DATA_SIZE); - ProtocolMetakom* metakom = context; - memcpy(data, &metakom->key_data, METAKOM_DATA_SIZE); -} - -static bool metakom_decoded(void* context) { - furi_assert(context); - ProtocolMetakom* metakom = context; - bool decoded = metakom->ready; - return decoded; -} - -static void metakom_reset(void* context) { - furi_assert(context); - ProtocolMetakom* metakom = context; - - metakom->ready = false; - metakom->period_sample_index = 0; - metakom->period_time = 0; - metakom->tmp_counter = 0; - metakom->tmp_data = 0; - for(uint8_t i = 0; i < METAKOM_PERIOD_SAMPLE_COUNT; i++) { - metakom->period_sample_data[i] = 0; - }; - metakom->state = METAKOM_WAIT_PERIOD_SYNC; - metakom->bit_state = METAKOM_BIT_WAIT_FRONT_LOW; - metakom->key_data = 0; - metakom->key_data_index = 0; - metakom->low_time_storage = 0; -} - -static bool metakom_parity_check(uint8_t data) { - uint8_t ones_count = 0; - bool result; - - for(uint8_t i = 0; i < 8; i++) { - if((data >> i) & 0b00000001) { - ones_count++; - } - } - - result = (ones_count % 2 == 0); - - return result; -} - -static bool metakom_process_bit( - ProtocolMetakom* metakom, - bool polarity, - uint32_t time, - uint32_t* high_time, - uint32_t* low_time) { - bool result = false; - - switch(metakom->bit_state) { - case METAKOM_BIT_WAIT_FRONT_LOW: - if(polarity == false) { - *low_time = metakom->low_time_storage; - *high_time = time; - result = true; - metakom->bit_state = METAKOM_BIT_WAIT_FRONT_HIGH; - } - break; - case METAKOM_BIT_WAIT_FRONT_HIGH: - if(polarity == true) { - metakom->low_time_storage = time; - metakom->bit_state = METAKOM_BIT_WAIT_FRONT_LOW; - } - break; - } - - return result; -} - -static void metakom_pulse(void* context, bool polarity, uint32_t time) { - furi_assert(context); - ProtocolMetakom* metakom = context; - - if(metakom->ready) return; - - uint32_t high_time = 0; - uint32_t low_time = 0; - - switch(metakom->state) { - case METAKOM_WAIT_PERIOD_SYNC: - if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) { - metakom->period_sample_data[metakom->period_sample_index] = high_time + low_time; - metakom->period_sample_index++; - - if(metakom->period_sample_index == METAKOM_PERIOD_SAMPLE_COUNT) { - for(uint8_t i = 0; i < METAKOM_PERIOD_SAMPLE_COUNT; i++) { - metakom->period_time += metakom->period_sample_data[i]; - }; - metakom->period_time /= METAKOM_PERIOD_SAMPLE_COUNT; - - metakom->state = METAKOM_WAIT_START_BIT; - } - } - - break; - case METAKOM_WAIT_START_BIT: - if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) { - metakom->tmp_counter++; - if(high_time > metakom->period_time) { - metakom->tmp_counter = 0; - metakom->state = METAKOM_WAIT_START_WORD; - } - - if(metakom->tmp_counter > 40) { - metakom_reset(metakom); - } - } - - break; - case METAKOM_WAIT_START_WORD: - if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) { - if(low_time < (metakom->period_time / 2)) { - metakom->tmp_data = (metakom->tmp_data << 1) | 0b0; - } else { - metakom->tmp_data = (metakom->tmp_data << 1) | 0b1; - } - metakom->tmp_counter++; - - if(metakom->tmp_counter == 3) { - if(metakom->tmp_data == 0b010) { - metakom->tmp_counter = 0; - metakom->tmp_data = 0; - metakom->state = METAKOM_READ_WORD; - } else { - metakom_reset(metakom); - } - } - } - break; - case METAKOM_READ_WORD: - if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) { - if(low_time < (metakom->period_time / 2)) { - metakom->tmp_data = (metakom->tmp_data << 1) | 0b0; - } else { - metakom->tmp_data = (metakom->tmp_data << 1) | 0b1; - } - metakom->tmp_counter++; - - if(metakom->tmp_counter == 8) { - if(metakom_parity_check(metakom->tmp_data)) { - metakom->key_data = (metakom->key_data << 8) | metakom->tmp_data; - metakom->key_data_index++; - metakom->tmp_data = 0; - metakom->tmp_counter = 0; - - if(metakom->key_data_index == 4) { - // check for stop bit - if(high_time > metakom->period_time) { - metakom->state = METAKOM_READ_STOP_WORD; - } else { - metakom_reset(metakom); - } - } - } else { - metakom_reset(metakom); - } - } - } - break; - case METAKOM_READ_STOP_WORD: - if(metakom_process_bit(metakom, polarity, time, &high_time, &low_time)) { - if(low_time < (metakom->period_time / 2)) { - metakom->tmp_data = (metakom->tmp_data << 1) | 0b0; - } else { - metakom->tmp_data = (metakom->tmp_data << 1) | 0b1; - } - metakom->tmp_counter++; - - if(metakom->tmp_counter == 3) { - if(metakom->tmp_data == 0b010) { - metakom->ready = true; - } else { - metakom_reset(metakom); - } - } - } - break; - } -} diff --git a/lib/one_wire/ibutton/pulse_protocols/protocol_metakom.h b/lib/one_wire/ibutton/pulse_protocols/protocol_metakom.h deleted file mode 100644 index fdc45769538..00000000000 --- a/lib/one_wire/ibutton/pulse_protocols/protocol_metakom.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @file protocol_metakom.h - * - * Metakom pulse format decoder - */ - -#pragma once -#include -#include "../../pulse_protocols/pulse_protocol.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct ProtocolMetakom ProtocolMetakom; - -/** - * Allocate decoder - * @return ProtocolMetakom* - */ -ProtocolMetakom* protocol_metakom_alloc(); - -/** - * Free decoder - * @param metakom - */ -void protocol_metakom_free(ProtocolMetakom* metakom); - -/** - * Get protocol interface - * @param metakom - * @return PulseProtocol* - */ -PulseProtocol* protocol_metakom_get_protocol(ProtocolMetakom* metakom); - -#ifdef __cplusplus -} -#endif diff --git a/lib/one_wire/one_wire_device.c b/lib/one_wire/one_wire_device.c deleted file mode 100644 index d9b4955dbe2..00000000000 --- 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 fc687c7badd..00000000000 --- 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 654b9e45d02..678812105c3 100644 --- a/lib/one_wire/one_wire_host.c +++ b/lib/one_wire/one_wire_host.c @@ -1,19 +1,65 @@ #include -#include + +/** + * Timings based on Application Note 126: + * https://www.analog.com/media/en/technical-documentation/tech-articles/1wire-communication-through-software--maxim-integrated.pdf + */ + #include "one_wire_host.h" -#include "one_wire_host_timing.h" + +typedef struct { + uint16_t a; + uint16_t b; + uint16_t c; + uint16_t d; + uint16_t e; + uint16_t f; + uint16_t g; + uint16_t h; + uint16_t i; + uint16_t j; +} OneWireHostTimings; + +static const OneWireHostTimings onewire_host_timings_normal = { + .a = 9, + .b = 64, + .c = 64, + .d = 14, + .e = 9, + .f = 55, + .g = 0, + .h = 480, + .i = 70, + .j = 410, +}; + +static const OneWireHostTimings onewire_host_timings_overdrive = { + .a = 1, + .b = 8, + .c = 8, + .d = 3, + .e = 1, + .f = 7, + .g = 3, + .h = 70, + .i = 9, + .j = 40, +}; struct OneWireHost { - // global search state - unsigned char saved_rom[8]; + const GpioPin* gpio_pin; + const OneWireHostTimings* timings; + 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); + onewire_host_set_overdrive(host, false); return host; } @@ -23,50 +69,52 @@ void onewire_host_free(OneWireHost* host) { } bool onewire_host_reset(OneWireHost* host) { - UNUSED(host); uint8_t r; uint8_t retries = 125; + const OneWireHostTimings* timings = host->timings; + // 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); + furi_delay_us(timings->g); // drive low - furi_hal_ibutton_pin_low(); - furi_delay_us(OWH_RESET_DRIVE); + furi_hal_gpio_write(host->gpio_pin, false); + furi_delay_us(timings->h); // release - furi_hal_ibutton_pin_high(); - furi_delay_us(OWH_RESET_RELEASE); + furi_hal_gpio_write(host->gpio_pin, true); + furi_delay_us(timings->i); // read and post delay - r = !furi_hal_ibutton_pin_get_level(); - furi_delay_us(OWH_RESET_DELAY_POST); + r = !furi_hal_gpio_read(host->gpio_pin); + furi_delay_us(timings->j); return r; } bool onewire_host_read_bit(OneWireHost* host) { - UNUSED(host); bool result; + const OneWireHostTimings* timings = host->timings; + // drive low - furi_hal_ibutton_pin_low(); - furi_delay_us(OWH_READ_DRIVE); + furi_hal_gpio_write(host->gpio_pin, false); + furi_delay_us(timings->a); // release - furi_hal_ibutton_pin_high(); - furi_delay_us(OWH_READ_RELEASE); + furi_hal_gpio_write(host->gpio_pin, true); + furi_delay_us(timings->e); // read and post delay - result = furi_hal_ibutton_pin_get_level(); - furi_delay_us(OWH_READ_DELAY_POST); + result = furi_hal_gpio_read(host->gpio_pin); + furi_delay_us(timings->f); return result; } @@ -90,23 +138,24 @@ void onewire_host_read_bytes(OneWireHost* host, uint8_t* buffer, uint16_t count) } void onewire_host_write_bit(OneWireHost* host, bool value) { - UNUSED(host); + const OneWireHostTimings* timings = host->timings; + if(value) { // drive low - furi_hal_ibutton_pin_low(); - furi_delay_us(OWH_WRITE_1_DRIVE); + furi_hal_gpio_write(host->gpio_pin, false); + furi_delay_us(timings->a); // release - furi_hal_ibutton_pin_high(); - furi_delay_us(OWH_WRITE_1_RELEASE); + furi_hal_gpio_write(host->gpio_pin, true); + furi_delay_us(timings->b); } else { // drive low - furi_hal_ibutton_pin_low(); - furi_delay_us(OWH_WRITE_0_DRIVE); + furi_hal_gpio_write(host->gpio_pin, false); + furi_delay_us(timings->c); // release - furi_hal_ibutton_pin_high(); - furi_delay_us(OWH_WRITE_0_RELEASE); + furi_hal_gpio_write(host->gpio_pin, true); + furi_delay_us(timings->d); } } @@ -118,18 +167,20 @@ void onewire_host_write(OneWireHost* host, uint8_t value) { } } -void onewire_host_skip(OneWireHost* host) { - onewire_host_write(host, 0xCC); +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_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 +201,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) { +bool 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 +228,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,8 +310,12 @@ 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; } + +void onewire_host_set_overdrive(OneWireHost* host, bool set) { + host->timings = set ? &onewire_host_timings_overdrive : &onewire_host_timings_normal; +} diff --git a/lib/one_wire/one_wire_host.h b/lib/one_wire/one_wire_host.h index 5b55ee8dd80..9f9bd4ffd75 100644 --- a/lib/one_wire/one_wire_host.h +++ b/lib/one_wire/one_wire_host.h @@ -14,107 +14,116 @@ 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 for all devices */ } OneWireHostSearchMode; typedef struct OneWireHost OneWireHost; /** - * Allocate onewire host bus - * @param gpio - * @return OneWireHost* + * Allocate OneWireHost instance + * @param [in] gpio_pin connection pin + * @return pointer to OneWireHost instance */ -OneWireHost* onewire_host_alloc(); +OneWireHost* onewire_host_alloc(const GpioPin* gpio_pin); /** - * Deallocate onewire host bus - * @param host + * Destroy OneWireHost instance, free resources + * @param [in] host pointer to OneWireHost instance */ void onewire_host_free(OneWireHost* host); /** - * Reset bus - * @param host - * @return bool + * Reset the 1-Wire bus + * @param [in] host pointer to OneWireHost instance + * @return true if presence was detected, false otherwise */ bool onewire_host_reset(OneWireHost* host); /** * Read one bit - * @param host - * @return bool + * @param [in] host pointer to OneWireHost instance + * @return received bit value */ bool onewire_host_read_bit(OneWireHost* host); /** * Read one byte - * @param host - * @return uint8_t + * @param [in] host pointer to OneWireHost instance + * @return received byte value */ uint8_t onewire_host_read(OneWireHost* host); /** - * Read many bytes - * @param host - * @param buffer - * @param count + * Read one or more bytes + * @param [in] host pointer to OneWireHost instance + * @param [out] buffer received data buffer + * @param [in] count number of bytes to read */ void onewire_host_read_bytes(OneWireHost* host, uint8_t* buffer, uint16_t count); /** * Write one bit - * @param host - * @param value + * @param [in] host pointer to OneWireHost instance + * @param value bit value to write */ void onewire_host_write_bit(OneWireHost* host, bool value); /** * Write one byte - * @param host - * @param value + * @param [in] host pointer to OneWireHost instance + * @param value byte value to write */ void onewire_host_write(OneWireHost* host, uint8_t value); /** - * Skip ROM command - * @param host + * Write one or more bytes + * @param [in] host pointer to OneWireHost instance + * @param [in] buffer pointer to the data to write + * @param [in] count size of the data to write */ -void onewire_host_skip(OneWireHost* host); +void onewire_host_write_bytes(OneWireHost* host, const uint8_t* buffer, uint16_t count); /** * Start working with the bus - * @param host + * @param [in] host pointer to OneWireHost instance */ void onewire_host_start(OneWireHost* host); /** * Stop working with the bus - * @param host + * @param [in] host pointer to OneWireHost instance */ void onewire_host_stop(OneWireHost* host); /** - * - * @param host + * Reset previous search results + * @param [in] host pointer to OneWireHost instance */ void onewire_host_reset_search(OneWireHost* host); /** - * - * @param host - * @param family_code + * Set the family code to search for + * @param [in] host pointer to OneWireHost instance + * @param [in] family_code device family code */ void onewire_host_target_search(OneWireHost* host, uint8_t family_code); /** - * - * @param host - * @param newAddr - * @param mode - * @return uint8_t + * Search for devices on the 1-Wire bus + * @param [in] host pointer to OneWireHost instance + * @param [out] new_addr pointer to the buffer to contain the unique ROM of the found device + * @param [in] mode search mode + * @return true on success, false otherwise + */ +bool onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearchMode mode); + +/** + * Enable overdrive mode + * @param [in] host pointer to OneWireHost instance + * @param [in] set true to turn overdrive on, false to turn it off */ -uint8_t onewire_host_search(OneWireHost* host, uint8_t* newAddr, OneWireHostSearchMode mode); +void onewire_host_set_overdrive(OneWireHost* host, bool set); #ifdef __cplusplus } diff --git a/lib/one_wire/one_wire_host_timing.h b/lib/one_wire/one_wire_host_timing.h deleted file mode 100644 index f95dd3561e1..00000000000 --- a/lib/one_wire/one_wire_host_timing.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @file one_wire_host_timing.h - * - * 1-Wire library, timing list - */ - -#pragma once - -#define OWH_TIMING_A 9 -#define OWH_TIMING_B 64 -#define OWH_TIMING_C 64 -#define OWH_TIMING_D 14 -#define OWH_TIMING_E 9 -#define OWH_TIMING_F 55 -#define OWH_TIMING_G 0 -#define OWH_TIMING_H 480 -#define OWH_TIMING_I 70 -#define OWH_TIMING_J 410 - -#define OWH_WRITE_1_DRIVE OWH_TIMING_A -#define OWH_WRITE_1_RELEASE OWH_TIMING_B -#define OWH_WRITE_0_DRIVE OWH_TIMING_C -#define OWH_WRITE_0_RELEASE OWH_TIMING_D -#define OWH_READ_DRIVE 3 -#define OWH_READ_RELEASE OWH_TIMING_E -#define OWH_READ_DELAY_POST OWH_TIMING_F -#define OWH_RESET_DELAY_PRE OWH_TIMING_G -#define OWH_RESET_DRIVE OWH_TIMING_H -#define OWH_RESET_RELEASE OWH_TIMING_I -#define OWH_RESET_DELAY_POST OWH_TIMING_J diff --git a/lib/one_wire/one_wire_slave.c b/lib/one_wire/one_wire_slave.c index af04cfdabbc..733b36e30e2 100644 --- a/lib/one_wire/one_wire_slave.c +++ b/lib/one_wire/one_wire_slave.c @@ -1,245 +1,216 @@ #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 TH_TIMEOUT_MAX 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 { - OneWireSlaveError error; - OneWireDevice* device; - OneWireSlaveResultCallback result_cb; - void* result_cb_ctx; -}; +typedef struct { + uint16_t trstl_min; /* Minimum Reset Low time */ + uint16_t trstl_max; /* Maximum Reset Low time */ -/*********************** PRIVATE ***********************/ + uint16_t tpdh_typ; /* Typical Presence Detect High time */ + uint16_t tpdl_min; /* Minimum Presence Detect Low time */ + uint16_t tpdl_max; /* Maximum Presence Detect Low time */ -uint32_t onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time, const bool pin_value) { - UNUSED(bus); - uint32_t start = DWT->CYCCNT; - uint32_t time_ticks = time * furi_hal_cortex_instructions_per_microsecond(); - uint32_t time_captured; - - do { - time_captured = DWT->CYCCNT; - if(furi_hal_ibutton_pin_get_level() != pin_value) { - uint32_t remaining_time = time_ticks - (time_captured - start); - remaining_time /= furi_hal_cortex_instructions_per_microsecond(); - return remaining_time; - } - } while((time_captured - start) < time_ticks); + uint16_t tslot_min; /* Minimum Read/Write Slot time */ + uint16_t tslot_max; /* Maximum Read/Write Slot time */ - return 0; -} + uint16_t tw1l_max; /* Maximum Master Write 1 time */ + uint16_t trl_tmsr_max; /* Maximum Master Read Low + Read Sample time */ +} OneWireSlaveTimings; -bool onewire_slave_show_presence(OneWireSlave* bus) { - // wait while master delay presence check - onewire_slave_wait_while_gpio_is(bus, OWS_PRESENCE_TIMEOUT, true); +struct OneWireSlave { + const GpioPin* gpio_pin; + const OneWireSlaveTimings* timings; + OneWireSlaveError error; - // show presence - furi_hal_ibutton_pin_low(); - furi_delay_us(OWS_PRESENCE_MIN); - furi_hal_ibutton_pin_high(); + bool is_first_reset; + bool is_short_reset; - // somebody also can show presence - const uint32_t wait_low_time = OWS_PRESENCE_MAX - OWS_PRESENCE_MIN; + OneWireSlaveResetCallback reset_callback; + OneWireSlaveCommandCallback command_callback; + OneWireSlaveResultCallback result_callback; - // so we will wait - if(onewire_slave_wait_while_gpio_is(bus, wait_low_time, false) == 0) { - bus->error = PRESENCE_LOW_ON_LINE; - return false; - } + void* reset_callback_context; + void* result_callback_context; + void* command_callback_context; +}; - return true; -} +static const OneWireSlaveTimings onewire_slave_timings_normal = { + .trstl_min = 270, + .trstl_max = 1200, -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; - } + .tpdh_typ = 20, + .tpdl_min = 100, + .tpdl_max = 480, - // 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; - } + .tslot_min = 60, + .tslot_max = 135, - // wait a time of zero - time = OWS_READ_MIN; - time = onewire_slave_wait_while_gpio_is(bus, time, false); + .tw1l_max = 20, + .trl_tmsr_max = 30, +}; - return (time > 0); -} +static const OneWireSlaveTimings onewire_slave_timings_overdrive = { + .trstl_min = 48, + .trstl_max = 80, -bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { - const bool write_zero = !value; + .tpdh_typ = 0, + .tpdl_min = 8, + .tpdl_max = 24, - // 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; - } + .tslot_min = 6, + .tslot_max = 16, - // 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; - } + .tw1l_max = 2, + .trl_tmsr_max = 3, +}; - // choose write time - if(write_zero) { - furi_hal_ibutton_pin_low(); - time = OWS_WRITE_ZERO; - } else { - time = OWS_READ_MAX; - } +/*********************** PRIVATE ***********************/ - // hold line for ZERO or ONE time - furi_delay_us(time); - furi_hal_ibutton_pin_high(); +static bool + onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time_us, const bool pin_value) { + const uint32_t time_start = DWT->CYCCNT; + const uint32_t time_ticks = time_us * furi_hal_cortex_instructions_per_microsecond(); - return true; -} + uint32_t time_elapsed; -void onewire_slave_cmd_search_rom(OneWireSlave* bus) { - const uint8_t key_bytes = 8; - uint8_t* key = onewire_device_get_id_p(bus->device); + do { //-V1044 + time_elapsed = DWT->CYCCNT - time_start; + if(furi_hal_gpio_read(bus->gpio_pin) != pin_value) { + return time_ticks >= time_elapsed; + } + } while(time_elapsed < time_ticks); - for(uint8_t i = 0; i < key_bytes; i++) { - uint8_t key_byte = key[i]; + return false; +} - for(uint8_t j = 0; j < 8; j++) { - bool bit = (key_byte >> j) & 0x01; +static inline bool onewire_slave_show_presence(OneWireSlave* bus) { + const OneWireSlaveTimings* timings = bus->timings; + // wait until the bus is high (might return immediately) + onewire_slave_wait_while_gpio_is(bus, timings->trstl_max, false); + // wait while master delay presence check + furi_delay_us(timings->tpdh_typ); - if(!onewire_slave_send_bit(bus, bit)) return; - if(!onewire_slave_send_bit(bus, !bit)) return; + // show presence + furi_hal_gpio_write(bus->gpio_pin, false); + furi_delay_us(timings->tpdl_min); + furi_hal_gpio_write(bus->gpio_pin, true); - onewire_slave_receive_bit(bus); - if(bus->error != NO_ERROR) return; - } + // somebody also can show presence + const uint32_t wait_low_time = timings->tpdl_max - timings->tpdl_min; + + // so we will wait + if(!onewire_slave_wait_while_gpio_is(bus, wait_low_time, false)) { + bus->error = OneWireSlaveErrorPresenceConflict; + return false; } + + return true; } -bool onewire_slave_receive_and_process_cmd(OneWireSlave* bus) { - uint8_t cmd; - onewire_slave_receive(bus, &cmd, 1); +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(!bus->is_first_reset) { + /* Guess the reset type */ + bus->is_short_reset = onewire_slave_wait_while_gpio_is( + bus, + onewire_slave_timings_overdrive.trstl_max - + onewire_slave_timings_overdrive.tslot_max, + false); + } else { + bus->is_first_reset = false; + } - if(bus->error == RESET_IN_PROGRESS) return true; - if(bus->error != NO_ERROR) return false; + furi_assert(bus->reset_callback); - switch(cmd) { - case 0xF0: - // SEARCH ROM - onewire_slave_cmd_search_rom(bus); - return true; + if(bus->reset_callback(bus->is_short_reset, bus->reset_callback_context)) { + if(onewire_slave_show_presence(bus)) { + bus->error = OneWireSlaveErrorNone; + return true; + } + } - case 0x0F: - case 0x33: - // READ ROM - onewire_device_send_id(bus->device); - return true; + } else if(bus->error == OneWireSlaveErrorNone) { + uint8_t command; + if(onewire_slave_receive(bus, &command, sizeof(command))) { + furi_assert(bus->command_callback); + if(bus->command_callback(command, bus->command_callback_context)) { + return true; + } + } - default: // Unknown command - bus->error = INCORRECT_ONEWIRE_CMD; + return (bus->error == OneWireSlaveErrorResetInProgress); } - if(bus->error == RESET_IN_PROGRESS) return true; - return (bus->error == NO_ERROR); + 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_bus_start(OneWireSlave* bus) { + FURI_CRITICAL_ENTER(); + furi_hal_gpio_init(bus->gpio_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); - 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); + while(onewire_slave_receive_and_process_command(bus)) + ; - } else { - result = false; - } + const bool result = (bus->error == OneWireSlaveErrorNone); - furi_hal_ibutton_start_interrupt_in_isr(); - FURI_CRITICAL_EXIT(); - } + 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_slave_timings_overdrive.trstl_min) && + (pulse_length <= onewire_slave_timings_normal.trstl_max)) { + /* Start in reset state in order to send a presence pulse immediately */ + bus->error = OneWireSlaveErrorResetInProgress; + /* Determine reset type (chooses speed mode if supported by the emulated device) */ + bus->is_short_reset = pulse_length <= onewire_slave_timings_overdrive.trstl_max; + /* Initial reset allows going directly into overdrive mode */ + bus->is_first_reset = true; + + 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->timings = &onewire_slave_timings_normal; + bus->error = OneWireSlaveErrorNone; + return bus; } @@ -249,51 +220,102 @@ 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) { + const OneWireSlaveTimings* timings = bus->timings; + // wait while bus is low + if(!onewire_slave_wait_while_gpio_is(bus, timings->tslot_max, false)) { + bus->error = OneWireSlaveErrorResetInProgress; + return false; + } - furi_hal_ibutton_pin_high(); + // wait while bus is high + if(!onewire_slave_wait_while_gpio_is(bus, TH_TIMEOUT_MAX, true)) { + bus->error = OneWireSlaveErrorTimeout; + return false; + } + + // wait a time of zero + return onewire_slave_wait_while_gpio_is(bus, timings->tw1l_max, false); +} + +bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { + const OneWireSlaveTimings* timings = bus->timings; + // wait while bus is low + if(!onewire_slave_wait_while_gpio_is(bus, timings->tslot_max, false)) { + bus->error = OneWireSlaveErrorResetInProgress; + return false; + } + + // wait while bus is high + if(!onewire_slave_wait_while_gpio_is(bus, TH_TIMEOUT_MAX, true)) { + bus->error = OneWireSlaveErrorTimeout; + return false; + } + + // choose write time + uint32_t time; + + if(!value) { + furi_hal_gpio_write(bus->gpio_pin, false); + time = timings->trl_tmsr_max; + } else { + time = timings->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 +323,35 @@ 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; +} + +void onewire_slave_set_overdrive(OneWireSlave* bus, bool set) { + const OneWireSlaveTimings* new_timings = set ? &onewire_slave_timings_overdrive : + &onewire_slave_timings_normal; + if(bus->timings != new_timings) { + /* Prevent erroneous reset by waiting for the previous time slot to finish */ + onewire_slave_wait_while_gpio_is(bus, bus->timings->tslot_max, false); + bus->timings = new_timings; + } } diff --git a/lib/one_wire/one_wire_slave.h b/lib/one_wire/one_wire_slave.h index 82e9f552331..21114b912cc 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,51 +17,111 @@ extern "C" { typedef struct OneWireDevice OneWireDevice; typedef struct OneWireSlave OneWireSlave; + +typedef bool (*OneWireSlaveResetCallback)(bool is_short, void* context); +typedef bool (*OneWireSlaveCommandCallback)(uint8_t command, void* context); typedef void (*OneWireSlaveResultCallback)(void* context); /** - * Allocate onewire slave - * @param pin - * @return OneWireSlave* + * Allocate OneWireSlave instance + * @param [in] gpio_pin connection pin + * @return pointer to OneWireSlave instance */ -OneWireSlave* onewire_slave_alloc(); +OneWireSlave* onewire_slave_alloc(const GpioPin* gpio_pin); /** - * Free onewire slave - * @param bus + * Destroy OneWireSlave instance, free resources + * @param [in] bus pointer to OneWireSlave instance */ void onewire_slave_free(OneWireSlave* bus); /** * Start working with the bus - * @param bus + * @param [in] bus pointer to OneWireSlave instance */ void onewire_slave_start(OneWireSlave* bus); /** * Stop working with the bus - * @param bus + * @param [in] bus pointer to OneWireSlave instance */ void onewire_slave_stop(OneWireSlave* bus); /** - * Attach device for emulation - * @param bus - * @param device + * Receive one bit + * @param [in] bus pointer to OneWireSlave instance + * @return received bit value + */ +bool onewire_slave_receive_bit(OneWireSlave* bus); + +/** + * Send one bit + * @param [in] bus pointer to OneWireSlave instance + * @param [in] value bit value to send + * @return true on success, false on failure + */ +bool onewire_slave_send_bit(OneWireSlave* bus, bool value); + +/** + * Send one or more bytes of data + * @param [in] bus pointer to OneWireSlave instance + * @param [in] data pointer to the data to send + * @param [in] data_size size of the data to send + * @return true on success, false on failure + */ +bool onewire_slave_send(OneWireSlave* bus, const uint8_t* data, size_t data_size); + +/** + * Receive one or more bytes of data + * @param [in] bus pointer to OneWireSlave instance + * @param [out] data pointer to the receive buffer + * @param [in] data_size number of bytes to receive + * @return true on success, false on failure */ -void onewire_slave_attach(OneWireSlave* bus, OneWireDevice* device); +bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, size_t data_size); /** - * Detach device from bus - * @param bus + * Enable overdrive mode + * @param [in] bus pointer to OneWireSlave instance + * @param [in] set true to turn overdrive on, false to turn it off */ -void onewire_slave_detach(OneWireSlave* bus); +void onewire_slave_set_overdrive(OneWireSlave* bus, bool set); + +/** + * Set a callback function to be called on each reset. + * The return value of the callback determines whether the emulated device + * supports the short reset (passed as the is_short parameter). + * In most applications, it should also call onewire_slave_set_overdrive() + * to set the appropriate speed mode. + * + * @param [in] bus pointer to OneWireSlave instance + * @param [in] callback pointer to a callback function + * @param [in] context additional parameter to be passed to the callback + */ +void onewire_slave_set_reset_callback( + OneWireSlave* bus, + OneWireSlaveResetCallback callback, + void* context); + +/** + * Set a callback function to be called on each command. + * The return value of the callback determines whether further operation + * is possible. As a rule of thumb, return true unless a critical error happened. + * + * @param [in] bus pointer to OneWireSlave instance + * @param [in] callback pointer to a callback function + * @param [in] context additional parameter to be passed to the callback + */ +void onewire_slave_set_command_callback( + OneWireSlave* bus, + OneWireSlaveCommandCallback callback, + void* context); /** * Set a callback to report emulation success - * @param bus - * @param result_cb - * @param context + * @param [in] bus pointer to OneWireSlave instance + * @param [in] result_cb pointer to a callback function + * @param [in] context additional parameter to be passed to the callback */ void onewire_slave_set_result_callback( OneWireSlave* bus, 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 55e0762e443..00000000000 --- 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/one_wire/pulse_protocols/pulse_decoder.c b/lib/one_wire/pulse_protocols/pulse_decoder.c deleted file mode 100644 index c7d3b09ecb8..00000000000 --- a/lib/one_wire/pulse_protocols/pulse_decoder.c +++ /dev/null @@ -1,66 +0,0 @@ -#include -#include "pulse_decoder.h" -#include -#include - -#define MAX_PROTOCOL 5 - -struct PulseDecoder { - PulseProtocol* protocols[MAX_PROTOCOL]; -}; - -PulseDecoder* pulse_decoder_alloc() { - PulseDecoder* decoder = malloc(sizeof(PulseDecoder)); - memset(decoder, 0, sizeof(PulseDecoder)); - return decoder; -} - -void pulse_decoder_free(PulseDecoder* reader) { - furi_assert(reader); - free(reader); -} - -void pulse_decoder_add_protocol(PulseDecoder* reader, PulseProtocol* protocol, int32_t index) { - furi_check(index < MAX_PROTOCOL); - furi_check(reader->protocols[index] == NULL); - reader->protocols[index] = protocol; -} - -void pulse_decoder_process_pulse(PulseDecoder* reader, bool polarity, uint32_t length) { - furi_assert(reader); - for(size_t index = 0; index < MAX_PROTOCOL; index++) { - if(reader->protocols[index] != NULL) { - pulse_protocol_process_pulse(reader->protocols[index], polarity, length); - } - } -} - -int32_t pulse_decoder_get_decoded_index(PulseDecoder* reader) { - furi_assert(reader); - int32_t decoded = -1; - for(size_t index = 0; index < MAX_PROTOCOL; index++) { - if(reader->protocols[index] != NULL) { - if(pulse_protocol_decoded(reader->protocols[index])) { - decoded = index; - break; - } - } - } - - return decoded; -} - -void pulse_decoder_reset(PulseDecoder* reader) { - furi_assert(reader); - for(size_t index = 0; index < MAX_PROTOCOL; index++) { - if(reader->protocols[index] != NULL) { - pulse_protocol_reset(reader->protocols[index]); - } - } -} - -void pulse_decoder_get_data(PulseDecoder* reader, int32_t index, uint8_t* data, size_t length) { - furi_assert(reader); - furi_check(reader->protocols[index] != NULL); - pulse_protocol_get_data(reader->protocols[index], data, length); -} diff --git a/lib/one_wire/pulse_protocols/pulse_decoder.h b/lib/one_wire/pulse_protocols/pulse_decoder.h deleted file mode 100644 index dbaef52b5e4..00000000000 --- a/lib/one_wire/pulse_protocols/pulse_decoder.h +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @file pulse_decoder.h - * - * Generic pulse protocol decoder library - */ - -#pragma once -#include -#include -#include "pulse_protocol.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct PulseDecoder PulseDecoder; - -/** - * Allocate decoder - * @return PulseDecoder* - */ -PulseDecoder* pulse_decoder_alloc(); - -/** - * Deallocate decoder - * @param decoder - */ -void pulse_decoder_free(PulseDecoder* decoder); - -/** - * Add protocol to decoder - * @param decoder - * @param protocol protocol implementation - * @param index protocol index, should not be repeated - */ -void pulse_decoder_add_protocol(PulseDecoder* decoder, PulseProtocol* protocol, int32_t index); - -/** - * Push and process pulse with decoder - * @param decoder - * @param polarity - * @param length - */ -void pulse_decoder_process_pulse(PulseDecoder* decoder, bool polarity, uint32_t length); - -/** - * Get indec of decoded protocol - * @param decoder - * @return int32_t, -1 if nothing decoded, or index of decoded protocol - */ -int32_t pulse_decoder_get_decoded_index(PulseDecoder* decoder); - -/** - * Reset all protocols in decoder - * @param decoder - */ -void pulse_decoder_reset(PulseDecoder* decoder); - -/** - * Get decoded data from protocol - * @param decoder - * @param index - * @param data - * @param length - */ -void pulse_decoder_get_data(PulseDecoder* decoder, int32_t index, uint8_t* data, size_t length); - -#ifdef __cplusplus -} -#endif diff --git a/lib/one_wire/pulse_protocols/pulse_protocol.c b/lib/one_wire/pulse_protocols/pulse_protocol.c deleted file mode 100644 index 76feba11358..00000000000 --- a/lib/one_wire/pulse_protocols/pulse_protocol.c +++ /dev/null @@ -1,67 +0,0 @@ -#include "pulse_protocol.h" -#include -#include - -struct PulseProtocol { - void* context; - PulseProtocolPulseCallback pulse_cb; - PulseProtocolResetCallback reset_cb; - PulseProtocolGetDataCallback get_data_cb; - PulseProtocolDecodedCallback decoded_cb; -}; - -PulseProtocol* pulse_protocol_alloc() { - PulseProtocol* protocol = malloc(sizeof(PulseProtocol)); - memset(protocol, 0, sizeof(PulseProtocol)); - return protocol; -} - -void pulse_protocol_set_context(PulseProtocol* protocol, void* context) { - protocol->context = context; -} - -void pulse_protocol_set_pulse_cb(PulseProtocol* protocol, PulseProtocolPulseCallback callback) { - protocol->pulse_cb = callback; -} - -void pulse_protocol_set_reset_cb(PulseProtocol* protocol, PulseProtocolResetCallback callback) { - protocol->reset_cb = callback; -} - -void pulse_protocol_set_get_data_cb(PulseProtocol* protocol, PulseProtocolGetDataCallback callback) { - protocol->get_data_cb = callback; -} - -void pulse_protocol_set_decoded_cb(PulseProtocol* protocol, PulseProtocolDecodedCallback callback) { - protocol->decoded_cb = callback; -} - -void pulse_protocol_free(PulseProtocol* protocol) { - free(protocol); -} - -void pulse_protocol_process_pulse(PulseProtocol* protocol, bool polarity, uint32_t length) { - if(protocol->pulse_cb != NULL) { - protocol->pulse_cb(protocol->context, polarity, length); - } -} - -void pulse_protocol_reset(PulseProtocol* protocol) { - if(protocol->reset_cb != NULL) { - protocol->reset_cb(protocol->context); - } -} - -bool pulse_protocol_decoded(PulseProtocol* protocol) { - bool result = false; - if(protocol->decoded_cb != NULL) { - result = protocol->decoded_cb(protocol->context); - } - return result; -} - -void pulse_protocol_get_data(PulseProtocol* protocol, uint8_t* data, size_t length) { - if(protocol->get_data_cb != NULL) { - protocol->get_data_cb(protocol->context, data, length); - } -} diff --git a/lib/one_wire/pulse_protocols/pulse_protocol.h b/lib/one_wire/pulse_protocols/pulse_protocol.h deleted file mode 100644 index bfce0e76d8b..00000000000 --- a/lib/one_wire/pulse_protocols/pulse_protocol.h +++ /dev/null @@ -1,122 +0,0 @@ -/** - * @file pulse_protocol.h - * - * Generic pulse protocol decoder library, protocol interface - */ - -#pragma once -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Anonymous PulseProtocol struct - */ -typedef struct PulseProtocol PulseProtocol; - -/** - * Process pulse callback - */ -typedef void (*PulseProtocolPulseCallback)(void* context, bool polarity, uint32_t length); - -/** - * Reset protocol callback - */ -typedef void (*PulseProtocolResetCallback)(void* context); - -/** - * Get decoded data callback - */ -typedef void (*PulseProtocolGetDataCallback)(void* context, uint8_t* data, size_t length); - -/** - * Is protocol decoded callback - */ -typedef bool (*PulseProtocolDecodedCallback)(void* context); - -/** - * Allocate protocol - * @return PulseProtocol* - */ -PulseProtocol* pulse_protocol_alloc(); - -/** - * Deallocate protocol - * @param protocol - */ -void pulse_protocol_free(PulseProtocol* protocol); - -/** - * Set context for callbacks - * @param protocol - * @param context - */ -void pulse_protocol_set_context(PulseProtocol* protocol, void* context); - -/** - * Set "Process pulse" callback. Called from the decoder when a new pulse is received. - * @param protocol - * @param callback - */ -void pulse_protocol_set_pulse_cb(PulseProtocol* protocol, PulseProtocolPulseCallback callback); - -/** - * Set "Reset protocol" callback. Called from the decoder when the decoder is reset. - * @param protocol - * @param callback - */ -void pulse_protocol_set_reset_cb(PulseProtocol* protocol, PulseProtocolResetCallback callback); - -/** - * Set "Get decoded data" callback. Called from the decoder when the decoder wants to get decoded data. - * @param protocol - * @param callback - */ -void pulse_protocol_set_get_data_cb(PulseProtocol* protocol, PulseProtocolGetDataCallback callback); - -/** - * Set "Is protocol decoded" callback. Called from the decoder when the decoder wants to know if a protocol has been decoded. - * @param protocol - * @param callback - */ -void pulse_protocol_set_decoded_cb(PulseProtocol* protocol, PulseProtocolDecodedCallback callback); - -/** - * Part of decoder interface. - * @param protocol - * @param polarity - * @param length - */ -void pulse_protocol_process_pulse(PulseProtocol* protocol, bool polarity, uint32_t length); - -/** - * Part of decoder interface. - * @param protocol - * @return true - * @return false - */ -bool pulse_protocol_decoded(PulseProtocol* protocol); - -/** - * Part of decoder interface. - * @param protocol - * @return true - * @return false - */ -void pulse_protocol_get_data(PulseProtocol* protocol, uint8_t* data, size_t length); - -/** - * Part of decoder interface. - * @param protocol - * @return true - * @return false - */ -void pulse_protocol_reset(PulseProtocol* protocol); - -#ifdef __cplusplus -} -#endif diff --git a/lib/print/SConscript b/lib/print/SConscript index 412d17a660b..819e60bf07f 100644 --- a/lib/print/SConscript +++ b/lib/print/SConscript @@ -96,6 +96,15 @@ for wrapped_fn in wrapped_fn_list: ] ) +env.Append( + SDK_HEADERS=[ + File("wrappers.h"), + ], + LINT_SOURCES=[ + Dir("."), + ], +) + libenv = env.Clone(FW_LIB_NAME="print") libenv.ApplyLibFlags() libenv.Append(CCFLAGS=["-Wno-double-promotion"]) diff --git a/lib/print/printf_tiny.c b/lib/print/printf_tiny.c index 0db11922d71..54f192a609b 100644 --- a/lib/print/printf_tiny.c +++ b/lib/print/printf_tiny.c @@ -541,7 +541,7 @@ static size_t _etoa( exp2 = (int)(expval * 3.321928094887362 + 0.5); const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; const double z2 = z * z; - conv.U = (uint64_t)(exp2 + 1023) << 52U; + conv.U = ((uint64_t)exp2 + 1023) << 52U; //-V519 // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); // correct for rounding errors diff --git a/lib/print/printf_tiny.h b/lib/print/printf_tiny.h index 8f292819e0a..58f6a673b7b 100644 --- a/lib/print/printf_tiny.h +++ b/lib/print/printf_tiny.h @@ -34,6 +34,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -54,7 +55,7 @@ void _putchar(char character); * \param format A string that specifies the format of the output * \return The number of characters that are written into the array, not counting the terminating null character */ -int printf_(const char* format, ...); +int printf_(const char* format, ...) _ATTRIBUTE((__format__(__printf__, 1, 2))); /** * Tiny sprintf implementation @@ -63,7 +64,7 @@ int printf_(const char* format, ...); * \param format A string that specifies the format of the output * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character */ -int sprintf_(char* buffer, const char* format, ...); +int sprintf_(char* buffer, const char* format, ...) _ATTRIBUTE((__format__(__printf__, 2, 3))); /** * Tiny snprintf/vsnprintf implementation @@ -75,7 +76,8 @@ int sprintf_(char* buffer, const char* format, ...); * null character. A value equal or larger than count indicates truncation. Only when the returned value * is non-negative and less than count, the string has been completely written. */ -int snprintf_(char* buffer, size_t count, const char* format, ...); +int snprintf_(char* buffer, size_t count, const char* format, ...) + _ATTRIBUTE((__format__(__printf__, 3, 4))); int vsnprintf_(char* buffer, size_t count, const char* format, va_list va); /** @@ -94,7 +96,8 @@ int vprintf_(const char* format, va_list va); * \param format A string that specifies the format of the output * \return The number of characters that are sent to the output function, not counting the terminating null character */ -int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...); +int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) + _ATTRIBUTE((__format__(__printf__, 3, 4))); #ifdef __cplusplus } diff --git a/lib/print/wrappers.c b/lib/print/wrappers.c index 3fe446657c4..b248aeb3dfe 100644 --- a/lib/print/wrappers.c +++ b/lib/print/wrappers.c @@ -1,11 +1,10 @@ -#include -#include +#include "wrappers.h" + #include #include #include #include #include -#include #include "printf_tiny.h" void _putchar(char character) { @@ -60,7 +59,6 @@ int __wrap_fflush(FILE* stream) { __attribute__((__noreturn__)) void __wrap___assert(const char* file, int line, const char* e) { UNUSED(file); UNUSED(line); - // TODO: message file and line number furi_crash(e); } @@ -69,6 +67,5 @@ __attribute__((__noreturn__)) void UNUSED(file); UNUSED(line); UNUSED(func); - // TODO: message file and line number furi_crash(e); } \ No newline at end of file diff --git a/lib/print/wrappers.h b/lib/print/wrappers.h new file mode 100644 index 00000000000..7c0d1f92ebe --- /dev/null +++ b/lib/print/wrappers.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int __wrap_printf(const char* format, ...); +int __wrap_vsnprintf(char* str, size_t size, const char* format, va_list args); +int __wrap_puts(const char* str); +int __wrap_putchar(int ch); +int __wrap_putc(int ch, FILE* stream); +int __wrap_snprintf(char* str, size_t size, const char* format, ...); +int __wrap_fflush(FILE* stream); + +__attribute__((__noreturn__)) void __wrap___assert(const char* file, int line, const char* e); + +__attribute__((__noreturn__)) void + __wrap___assert_func(const char* file, int line, const char* func, const char* e); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/pulse_reader/SConscript b/lib/pulse_reader/SConscript new file mode 100644 index 00000000000..a134783798c --- /dev/null +++ b/lib/pulse_reader/SConscript @@ -0,0 +1,30 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/pulse_reader", + ], + SDK_HEADERS=[ + File("pulse_reader.h"), + ], + LINT_SOURCES=[ + Dir("."), + ], +) + +libenv = env.Clone(FW_LIB_NAME="pulse_reader") +libenv.ApplyLibFlags() + +libenv.AppendUnique( + CCFLAGS=[ + # Required for lib to be linkable with .faps + "-mword-relocations", + "-mlong-calls", + ], +) + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/pulse_reader/pulse_reader.c b/lib/pulse_reader/pulse_reader.c new file mode 100644 index 00000000000..1c3cb4a586f --- /dev/null +++ b/lib/pulse_reader/pulse_reader.c @@ -0,0 +1,236 @@ +#include "pulse_reader.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +struct PulseReader { + uint32_t* timer_buffer; + uint32_t* gpio_buffer; + uint32_t size; + uint32_t pos; + uint32_t timer_value; + uint32_t gpio_value; + uint32_t gpio_mask; + uint32_t unit_multiplier; + uint32_t unit_divider; + uint32_t bit_time; + uint32_t dma_channel; + const GpioPin* gpio; + GpioPull pull; + LL_DMA_InitTypeDef dma_config_timer; + LL_DMA_InitTypeDef dma_config_gpio; +}; + +#define GPIO_PIN_MAP(pin, prefix) \ + (((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \ + ((pin) == (LL_GPIO_PIN_1)) ? prefix##1 : \ + ((pin) == (LL_GPIO_PIN_2)) ? prefix##2 : \ + ((pin) == (LL_GPIO_PIN_3)) ? prefix##3 : \ + ((pin) == (LL_GPIO_PIN_4)) ? prefix##4 : \ + ((pin) == (LL_GPIO_PIN_5)) ? prefix##5 : \ + ((pin) == (LL_GPIO_PIN_6)) ? prefix##6 : \ + ((pin) == (LL_GPIO_PIN_7)) ? prefix##7 : \ + ((pin) == (LL_GPIO_PIN_8)) ? prefix##8 : \ + ((pin) == (LL_GPIO_PIN_9)) ? prefix##9 : \ + ((pin) == (LL_GPIO_PIN_10)) ? prefix##10 : \ + ((pin) == (LL_GPIO_PIN_11)) ? prefix##11 : \ + ((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \ + ((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \ + ((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \ + prefix##15) + +#define GET_DMAMUX_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_DMAMUX_REQ_GEN_EXTI_LINE) + +PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) { + PulseReader* signal = malloc(sizeof(PulseReader)); + signal->timer_buffer = malloc(size * sizeof(uint32_t)); + signal->gpio_buffer = malloc(size * sizeof(uint32_t)); + signal->dma_channel = LL_DMA_CHANNEL_4; + signal->gpio = gpio; + signal->pull = GpioPullNo; + signal->size = size; + signal->timer_value = 0; + signal->pos = 0; + + pulse_reader_set_timebase(signal, PulseReaderUnit64MHz); + pulse_reader_set_bittime(signal, 1); + + signal->dma_config_timer.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; + signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->CNT); + signal->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + signal->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + signal->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->timer_buffer; + signal->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + signal->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + signal->dma_config_timer.Mode = LL_DMA_MODE_CIRCULAR; + signal->dma_config_timer.PeriphRequest = + LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ + signal->dma_config_timer.Priority = LL_DMA_PRIORITY_VERYHIGH; + + signal->dma_config_gpio.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; + signal->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + signal->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + signal->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + signal->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + signal->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR; + signal->dma_config_gpio.PeriphRequest = + LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */ + signal->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH; + + return signal; +} + +void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit) { + switch(unit) { + case PulseReaderUnit64MHz: + signal->unit_multiplier = 1; + signal->unit_divider = 1; + break; + case PulseReaderUnitPicosecond: + signal->unit_multiplier = 15625; + signal->unit_divider = 1; + break; + case PulseReaderUnitNanosecond: + signal->unit_multiplier = 15625; + signal->unit_divider = 1000; + break; + case PulseReaderUnitMicrosecond: + signal->unit_multiplier = 15625; + signal->unit_divider = 1000000; + break; + } +} + +void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time) { + signal->bit_time = bit_time; +} + +void pulse_reader_set_pull(PulseReader* signal, GpioPull pull) { + signal->pull = pull; +} + +void pulse_reader_free(PulseReader* signal) { + furi_assert(signal); + + free(signal->timer_buffer); + free(signal->gpio_buffer); + free(signal); +} + +uint32_t pulse_reader_samples(PulseReader* signal) { + uint32_t dma_pos = signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel); + + return ((signal->pos + signal->size) - dma_pos) % signal->size; +} + +void pulse_reader_stop(PulseReader* signal) { + LL_DMA_DisableChannel(DMA1, signal->dma_channel); + LL_DMA_DisableChannel(DMA1, signal->dma_channel + 1); + LL_DMAMUX_DisableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); + LL_TIM_DisableCounter(TIM2); + furi_hal_bus_disable(FuriHalBusTIM2); + furi_hal_gpio_init_simple(signal->gpio, GpioModeAnalog); +} + +void pulse_reader_start(PulseReader* signal) { + /* configure DMA to read from a timer peripheral */ + signal->dma_config_timer.NbData = signal->size; + + signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) & (signal->gpio->port->IDR); + signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)signal->gpio_buffer; + signal->dma_config_gpio.NbData = signal->size; + + furi_hal_bus_enable(FuriHalBusTIM2); + + /* start counter */ + LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetPrescaler(TIM2, 0); + LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF); + LL_TIM_SetCounter(TIM2, 0); + LL_TIM_EnableCounter(TIM2); + + /* generator 0 gets fed by EXTI_LINEn */ + LL_DMAMUX_SetRequestSignalID( + NULL, LL_DMAMUX_REQ_GEN_0, GET_DMAMUX_EXTI_LINE(signal->gpio->pin)); + /* trigger on rising edge of the interrupt */ + LL_DMAMUX_SetRequestGenPolarity(NULL, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_POL_RISING); + /* now enable request generation again */ + LL_DMAMUX_EnableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0); + + /* we need the EXTI to be configured as interrupt generating line, but no ISR registered */ + furi_hal_gpio_init_ex( + signal->gpio, GpioModeInterruptRiseFall, signal->pull, GpioSpeedVeryHigh, GpioAltFnUnused); + + /* capture current timer */ + signal->pos = 0; + signal->timer_value = TIM2->CNT; + signal->gpio_mask = signal->gpio->pin; + signal->gpio_value = signal->gpio->port->IDR & signal->gpio_mask; + + /* now set up DMA with these settings */ + LL_DMA_Init(DMA1, signal->dma_channel, &signal->dma_config_timer); + LL_DMA_Init(DMA1, signal->dma_channel + 1, &signal->dma_config_gpio); + LL_DMA_EnableChannel(DMA1, signal->dma_channel); + LL_DMA_EnableChannel(DMA1, signal->dma_channel + 1); +} + +uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us) { + uint32_t start_time = DWT->CYCCNT; + uint32_t timeout_ticks = timeout_us * (F_TIM2 / 1000000); + + do { + /* get the DMA's next write position by reading "remaining length" register */ + uint32_t dma_pos = + signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel); + + /* the DMA has advanced in the ringbuffer */ + if(dma_pos != signal->pos) { + uint32_t delta = signal->timer_buffer[signal->pos] - signal->timer_value; + uint32_t last_gpio_value = signal->gpio_value; + + signal->gpio_value = signal->gpio_buffer[signal->pos]; + + /* check if the GPIO really toggled. if not, we lost an edge :( */ + if(((last_gpio_value ^ signal->gpio_value) & signal->gpio_mask) != signal->gpio_mask) { + signal->gpio_value ^= signal->gpio_mask; + return PULSE_READER_LOST_EDGE; + } + signal->timer_value = signal->timer_buffer[signal->pos]; + + signal->pos++; + signal->pos %= signal->size; + + uint32_t delta_unit = 0; + + /* probably larger values, so choose a wider data type */ + if(signal->unit_divider > 1) { + delta_unit = + (uint32_t)((uint64_t)delta * (uint64_t)signal->unit_multiplier / signal->unit_divider); + } else { + delta_unit = delta * signal->unit_multiplier; + } + + /* if to be scaled to bit times, save a few instructions. should be faster */ + if(signal->bit_time > 1) { + return (delta_unit + signal->bit_time / 2) / signal->bit_time; + } + + return delta_unit; + } + + /* check for timeout */ + uint32_t elapsed = DWT->CYCCNT - start_time; + + if(elapsed > timeout_ticks) { + return PULSE_READER_NO_EDGE; + } + } while(true); +} diff --git a/lib/pulse_reader/pulse_reader.h b/lib/pulse_reader/pulse_reader.h new file mode 100644 index 00000000000..62c5f2fa46d --- /dev/null +++ b/lib/pulse_reader/pulse_reader.h @@ -0,0 +1,122 @@ +#pragma once + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PULSE_READER_NO_EDGE (0xFFFFFFFFUL) +#define PULSE_READER_LOST_EDGE (0xFFFFFFFEUL) +#define F_TIM2 (64000000UL) + +/** + * unit of the edge durations to return + */ +typedef enum { + PulseReaderUnit64MHz, + PulseReaderUnitPicosecond, + PulseReaderUnitNanosecond, + PulseReaderUnitMicrosecond, +} PulseReaderUnit; + +/* using an anonymous type */ +typedef struct PulseReader PulseReader; + +/** Allocate a PulseReader object + * + * Allocates memory for a ringbuffer and initalizes the object + * + * @param[in] gpio the GPIO to use. will get configured as input. + * @param[in] size number of edges to buffer + */ +PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size); + +/** Free a PulseReader object + * + * Frees all memory of the given object + * + * @param[in] signal previously allocated PulseReader object. + */ +void pulse_reader_free(PulseReader* signal); + +/** Start signal capturing + * + * Initializes DMA1, TIM2 and DMAMUX_REQ_GEN_0 to automatically capture timer values. + * Ensure that interrupts are always enabled, as the used EXTI line is handled as one. + * + * @param[in] signal previously allocated PulseReader object. + */ +void pulse_reader_start(PulseReader* signal); + +/** Stop signal capturing + * + * Frees DMA1, TIM2 and DMAMUX_REQ_GEN_0 + * + * @param[in] signal previously allocated PulseReader object. + */ +void pulse_reader_stop(PulseReader* signal); + +/** Recevie a sample from ringbuffer + * + * Waits for the specified time until a new edge gets detected. + * If not configured otherwise, the pulse duration will be in picosecond resolution. + * If a bittime was configured, the return value will contain the properly rounded + * number of bit times measured. + * + * @param[in] signal previously allocated PulseReader object. + * @param[in] timeout_us time to wait for a signal [µs] + * + * @returns the scaled value of the pulse duration + */ +uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us); + +/** Get available samples + * + * Get the number of available samples in the ringbuffer + * + * @param[in] signal previously allocated PulseReader object. + * + * @returns the number of samples in buffer + */ +uint32_t pulse_reader_samples(PulseReader* signal); + +/** Set timebase + * + * Set the timebase to be used when returning pulse duration. + * + * @param[in] signal previously allocated PulseReader object. + * @param[in] unit PulseReaderUnit64MHz or PulseReaderUnitPicosecond + */ +void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit); + +/** Set bit time + * + * Set the number of timebase units per bit. + * When set, the pulse_reader_receive() will return an already rounded + * bit count value instead of the raw duration. + * + * Set to 1 to return duration again. + * + * @param[in] signal previously allocated PulseReader object. + * @param[in] bit_time + */ +void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time); + +/** Set GPIO pull direction + * + * Some GPIOs need pulldown, others don't. By default the + * pull direction is GpioPullNo. + * + * @param[in] signal previously allocated PulseReader object. + * @param[in] pull GPIO pull direction + */ +void pulse_reader_set_pull(PulseReader* signal, GpioPull pull); + +#ifdef __cplusplus +} +#endif diff --git a/lib/qrcode/qrcode.c b/lib/qrcode/qrcode.c deleted file mode 100644 index fb5bd8a6e78..00000000000 --- a/lib/qrcode/qrcode.c +++ /dev/null @@ -1,975 +0,0 @@ -/** - * The MIT License (MIT) - * - * This library is written and maintained by Richard Moore. - * Major parts were derived from Project Nayuki's library. - * - * Copyright (c) 2017 Richard Moore (https://github.com/ricmoo/QRCode) - * Copyright (c) 2017 Project Nayuki (https://www.nayuki.io/page/qr-code-generator-library) - * - * 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. - */ - -/** - * Special thanks to Nayuki (https://www.nayuki.io/) from which this library was - * heavily inspired and compared against. - * - * See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp - */ - -#include "qrcode.h" - -#include -#include - -#pragma mark - Error Correction Lookup tables - -#if LOCK_VERSION == 0 - -static const uint16_t NUM_ERROR_CORRECTION_CODEWORDS[4][40] = { - // 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, 37, 38, 39, 40 Error correction level - {10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 150, 176, 198, 216, - 240, 280, 308, 338, 364, 416, 442, 476, 504, 560, 588, 644, 700, 728, - 784, 812, 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372}, // Medium - {7, 10, 15, 20, 26, 36, 40, 48, 60, 72, 80, 96, 104, 120, - 132, 144, 168, 180, 196, 224, 224, 252, 270, 300, 312, 336, 360, 390, - 420, 450, 480, 510, 540, 570, 570, 600, 630, 660, 720, 750}, // Low - {17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 264, 308, 352, 384, - 432, 480, 532, 588, 650, 700, 750, 816, 900, 960, 1050, 1110, 1200, 1260, - 1350, 1440, 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430}, // High - {13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 224, 260, 288, 320, - 360, 408, 448, 504, 546, 600, 644, 690, 750, 810, 870, 952, 1020, 1050, - 1140, 1200, 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040}, // Quartile -}; - -static const uint8_t NUM_ERROR_CORRECTION_BLOCKS[4][40] = { - // Version: (note that index 0 is for padding, and is set to an illegal value) - // 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, 37, 38, 39, 40 Error correction level - {1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, - 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium - {1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, - 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low - {1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, - 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High - {1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, - 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile -}; - -static const uint16_t NUM_RAW_DATA_MODULES[40] = { - // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 208, - 359, - 567, - 807, - 1079, - 1383, - 1568, - 1936, - 2336, - 2768, - 3232, - 3728, - 4256, - 4651, - 5243, - 5867, - 6523, - // 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 7211, - 7931, - 8683, - 9252, - 10068, - 10916, - 11796, - 12708, - 13652, - 14628, - 15371, - 16411, - 17483, - 18587, - // 32, 33, 34, 35, 36, 37, 38, 39, 40 - 19723, - 20891, - 22091, - 23008, - 24272, - 25568, - 26896, - 28256, - 29648}; - -// @TODO: Put other LOCK_VERSIONS here -#elif LOCK_VERSION == 3 - -static const int16_t NUM_ERROR_CORRECTION_CODEWORDS[4] = {26, 15, 44, 36}; - -static const int8_t NUM_ERROR_CORRECTION_BLOCKS[4] = {1, 1, 2, 2}; - -static const uint16_t NUM_RAW_DATA_MODULES = 567; - -#else - -#error Unsupported LOCK_VERSION (add it...) - -#endif - -static int max(int a, int b) { - if(a > b) { - return a; - } - return b; -} - -/* -static int abs(int value) { - if (value < 0) { return -value; } - return value; -} -*/ - -#pragma mark - Mode testing and conversion - -static int8_t getAlphanumeric(char c) { - if(c >= '0' && c <= '9') { - return (c - '0'); - } - if(c >= 'A' && c <= 'Z') { - return (c - 'A' + 10); - } - - switch(c) { - case ' ': - return 36; - case '$': - return 37; - case '%': - return 38; - case '*': - return 39; - case '+': - return 40; - case '-': - return 41; - case '.': - return 42; - case '/': - return 43; - case ':': - return 44; - } - - return -1; -} - -static bool isAlphanumeric(const char* text, uint16_t length) { - while(length != 0) { - if(getAlphanumeric(text[--length]) == -1) { - return false; - } - } - return true; -} - -static bool isNumeric(const char* text, uint16_t length) { - while(length != 0) { - char c = text[--length]; - if(c < '0' || c > '9') { - return false; - } - } - return true; -} - -#pragma mark - Counting - -// We store the following tightly packed (less 8) in modeInfo -// <=9 <=26 <= 40 -// NUMERIC ( 10, 12, 14); -// ALPHANUMERIC ( 9, 11, 13); -// BYTE ( 8, 16, 16); -static char getModeBits(uint8_t version, uint8_t mode) { - // Note: We use 15 instead of 16; since 15 doesn't exist and we cannot store 16 (8 + 8) in 3 bits - // hex(int("".join(reversed([('00' + bin(x - 8)[2:])[-3:] for x in [10, 9, 8, 12, 11, 15, 14, 13, 15]])), 2)) - unsigned int modeInfo = 0x7bbb80a; - -#if LOCK_VERSION == 0 || LOCK_VERSION > 9 - if(version > 9) { - modeInfo >>= 9; - } -#endif - -#if LOCK_VERSION == 0 || LOCK_VERSION > 26 - if(version > 26) { - modeInfo >>= 9; - } -#endif - - char result = 8 + ((modeInfo >> (3 * mode)) & 0x07); - if(result == 15) { - result = 16; - } - - return result; -} - -#pragma mark - BitBucket - -typedef struct BitBucket { - uint32_t bitOffsetOrWidth; - uint16_t capacityBytes; - uint8_t* data; -} BitBucket; - -/* -void bb_dump(BitBucket *bitBuffer) { - printf("Buffer: "); - for (uint32_t i = 0; i < bitBuffer->capacityBytes; i++) { - printf("%02x", bitBuffer->data[i]); - if ((i % 4) == 3) { printf(" "); } - } - printf("\n"); -} -*/ - -static uint16_t bb_getGridSizeBytes(uint8_t size) { - return (((size * size) + 7) / 8); -} - -static uint16_t bb_getBufferSizeBytes(uint32_t bits) { - return ((bits + 7) / 8); -} - -static void bb_initBuffer(BitBucket* bitBuffer, uint8_t* data, int32_t capacityBytes) { - bitBuffer->bitOffsetOrWidth = 0; - bitBuffer->capacityBytes = capacityBytes; - bitBuffer->data = data; - - memset(data, 0, bitBuffer->capacityBytes); -} - -static void bb_initGrid(BitBucket* bitGrid, uint8_t* data, uint8_t size) { - bitGrid->bitOffsetOrWidth = size; - bitGrid->capacityBytes = bb_getGridSizeBytes(size); - bitGrid->data = data; - - memset(data, 0, bitGrid->capacityBytes); -} - -static void bb_appendBits(BitBucket* bitBuffer, uint32_t val, uint8_t length) { - uint32_t offset = bitBuffer->bitOffsetOrWidth; - for(int8_t i = length - 1; i >= 0; i--, offset++) { - bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7)); - } - bitBuffer->bitOffsetOrWidth = offset; -} -/* -void bb_setBits(BitBucket *bitBuffer, uint32_t val, int offset, uint8_t length) { - for (int8_t i = length - 1; i >= 0; i--, offset++) { - bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7)); - } -} -*/ -static void bb_setBit(BitBucket* bitGrid, uint8_t x, uint8_t y, bool on) { - uint32_t offset = y * bitGrid->bitOffsetOrWidth + x; - uint8_t mask = 1 << (7 - (offset & 0x07)); - if(on) { - bitGrid->data[offset >> 3] |= mask; - } else { - bitGrid->data[offset >> 3] &= ~mask; - } -} - -static void bb_invertBit(BitBucket* bitGrid, uint8_t x, uint8_t y, bool invert) { - uint32_t offset = y * bitGrid->bitOffsetOrWidth + x; - uint8_t mask = 1 << (7 - (offset & 0x07)); - bool on = ((bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0); - if(on ^ invert) { - bitGrid->data[offset >> 3] |= mask; - } else { - bitGrid->data[offset >> 3] &= ~mask; - } -} - -static bool bb_getBit(BitBucket* bitGrid, uint8_t x, uint8_t y) { - uint32_t offset = y * bitGrid->bitOffsetOrWidth + x; - return (bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0; -} - -#pragma mark - Drawing Patterns - -// XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical -// properties, calling applyMask(m) twice with the same value is equivalent to no change at all. -// This means it is possible to apply a mask, undo it, and try another mask. Note that a final -// well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). -static void applyMask(BitBucket* modules, BitBucket* isFunction, uint8_t mask) { - uint8_t size = modules->bitOffsetOrWidth; - - for(uint8_t y = 0; y < size; y++) { - for(uint8_t x = 0; x < size; x++) { - if(bb_getBit(isFunction, x, y)) { - continue; - } - - bool invert = 0; - switch(mask) { - case 0: - invert = (x + y) % 2 == 0; - break; - case 1: - invert = y % 2 == 0; - break; - case 2: - invert = x % 3 == 0; - break; - case 3: - invert = (x + y) % 3 == 0; - break; - case 4: - invert = (x / 3 + y / 2) % 2 == 0; - break; - case 5: - invert = x * y % 2 + x * y % 3 == 0; - break; - case 6: - invert = (x * y % 2 + x * y % 3) % 2 == 0; - break; - case 7: - invert = ((x + y) % 2 + x * y % 3) % 2 == 0; - break; - } - bb_invertBit(modules, x, y, invert); - } - } -} - -static void - setFunctionModule(BitBucket* modules, BitBucket* isFunction, uint8_t x, uint8_t y, bool on) { - bb_setBit(modules, x, y, on); - bb_setBit(isFunction, x, y, true); -} - -// Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). -static void drawFinderPattern(BitBucket* modules, BitBucket* isFunction, uint8_t x, uint8_t y) { - uint8_t size = modules->bitOffsetOrWidth; - - for(int8_t i = -4; i <= 4; i++) { - for(int8_t j = -4; j <= 4; j++) { - uint8_t dist = max(abs(i), abs(j)); // Chebyshev/infinity norm - int16_t xx = x + j, yy = y + i; - if(0 <= xx && xx < size && 0 <= yy && yy < size) { - setFunctionModule(modules, isFunction, xx, yy, dist != 2 && dist != 4); - } - } - } -} - -// Draws a 5*5 alignment pattern, with the center module at (x, y). -static void drawAlignmentPattern(BitBucket* modules, BitBucket* isFunction, uint8_t x, uint8_t y) { - for(int8_t i = -2; i <= 2; i++) { - for(int8_t j = -2; j <= 2; j++) { - setFunctionModule(modules, isFunction, x + j, y + i, max(abs(i), abs(j)) != 1); - } - } -} - -// Draws two copies of the format bits (with its own error correction code) -// based on the given mask and this object's error correction level field. -static void drawFormatBits(BitBucket* modules, BitBucket* isFunction, uint8_t ecc, uint8_t mask) { - uint8_t size = modules->bitOffsetOrWidth; - - // Calculate error correction code and pack bits - uint32_t data = ecc << 3 | mask; // errCorrLvl is uint2, mask is uint3 - uint32_t rem = data; - for(int i = 0; i < 10; i++) { - rem = (rem << 1) ^ ((rem >> 9) * 0x537); - } - - data = data << 10 | rem; - data ^= 0x5412; // uint15 - - // Draw first copy - for(uint8_t i = 0; i <= 5; i++) { - setFunctionModule(modules, isFunction, 8, i, ((data >> i) & 1) != 0); - } - - setFunctionModule(modules, isFunction, 8, 7, ((data >> 6) & 1) != 0); - setFunctionModule(modules, isFunction, 8, 8, ((data >> 7) & 1) != 0); - setFunctionModule(modules, isFunction, 7, 8, ((data >> 8) & 1) != 0); - - for(int8_t i = 9; i < 15; i++) { - setFunctionModule(modules, isFunction, 14 - i, 8, ((data >> i) & 1) != 0); - } - - // Draw second copy - for(int8_t i = 0; i <= 7; i++) { - setFunctionModule(modules, isFunction, size - 1 - i, 8, ((data >> i) & 1) != 0); - } - - for(int8_t i = 8; i < 15; i++) { - setFunctionModule(modules, isFunction, 8, size - 15 + i, ((data >> i) & 1) != 0); - } - - setFunctionModule(modules, isFunction, 8, size - 8, true); -} - -// Draws two copies of the version bits (with its own error correction code), -// based on this object's version field (which only has an effect for 7 <= version <= 40). -static void drawVersion(BitBucket* modules, BitBucket* isFunction, uint8_t version) { - int8_t size = modules->bitOffsetOrWidth; - -#if LOCK_VERSION != 0 && LOCK_VERSION < 7 - return; - -#else - if(version < 7) { - return; - } - - // Calculate error correction code and pack bits - uint32_t rem = version; // version is uint6, in the range [7, 40] - for(uint8_t i = 0; i < 12; i++) { - rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); - } - - uint32_t data = version << 12 | rem; // uint18 - - // Draw two copies - for(uint8_t i = 0; i < 18; i++) { - bool bit = ((data >> i) & 1) != 0; - uint8_t a = size - 11 + i % 3, b = i / 3; - setFunctionModule(modules, isFunction, a, b, bit); - setFunctionModule(modules, isFunction, b, a, bit); - } - -#endif -} - -static void - drawFunctionPatterns(BitBucket* modules, BitBucket* isFunction, uint8_t version, uint8_t ecc) { - uint8_t size = modules->bitOffsetOrWidth; - - // Draw the horizontal and vertical timing patterns - for(uint8_t i = 0; i < size; i++) { - setFunctionModule(modules, isFunction, 6, i, i % 2 == 0); - setFunctionModule(modules, isFunction, i, 6, i % 2 == 0); - } - - // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) - drawFinderPattern(modules, isFunction, 3, 3); - drawFinderPattern(modules, isFunction, size - 4, 3); - drawFinderPattern(modules, isFunction, 3, size - 4); - -#if LOCK_VERSION == 0 || LOCK_VERSION > 1 - - if(version > 1) { - // Draw the numerous alignment patterns - - uint8_t alignCount = version / 7 + 2; - uint8_t step; - if(version != 32) { - step = (version * 4 + alignCount * 2 + 1) / (2 * alignCount - 2) * - 2; // ceil((size - 13) / (2*numAlign - 2)) * 2 - } else { // C-C-C-Combo breaker! - step = 26; - } - - uint8_t alignPositionIndex = alignCount - 1; - uint8_t alignPosition[alignCount]; - - alignPosition[0] = 6; - - uint8_t size = version * 4 + 17; - for(uint8_t i = 0, pos = size - 7; i < alignCount - 1; i++, pos -= step) { - alignPosition[alignPositionIndex--] = pos; - } - - for(uint8_t i = 0; i < alignCount; i++) { - for(uint8_t j = 0; j < alignCount; j++) { - if((i == 0 && j == 0) || (i == 0 && j == alignCount - 1) || - (i == alignCount - 1 && j == 0)) { - continue; // Skip the three finder corners - } else { - drawAlignmentPattern(modules, isFunction, alignPosition[i], alignPosition[j]); - } - } - } - } - -#endif - - // Draw configuration data - drawFormatBits( - modules, isFunction, ecc, 0); // Dummy mask value; overwritten later in the constructor - drawVersion(modules, isFunction, version); -} - -// Draws the given sequence of 8-bit codewords (data and error correction) onto the entire -// data area of this QR Code symbol. Function modules need to be marked off before this is called. -static void drawCodewords(BitBucket* modules, BitBucket* isFunction, BitBucket* codewords) { - uint32_t bitLength = codewords->bitOffsetOrWidth; - uint8_t* data = codewords->data; - - uint8_t size = modules->bitOffsetOrWidth; - - // Bit index into the data - uint32_t i = 0; - - // Do the funny zigzag scan - for(int16_t right = size - 1; right >= 1; - right -= 2) { // Index of right column in each column pair - if(right == 6) { - right = 5; - } - - for(uint8_t vert = 0; vert < size; vert++) { // Vertical counter - for(int j = 0; j < 2; j++) { - uint8_t x = right - j; // Actual x coordinate - bool upwards = ((right & 2) == 0) ^ (x < 6); - uint8_t y = upwards ? size - 1 - vert : vert; // Actual y coordinate - if(!bb_getBit(isFunction, x, y) && i < bitLength) { - bb_setBit(modules, x, y, ((data[i >> 3] >> (7 - (i & 7))) & 1) != 0); - i++; - } - // If there are any remainder bits (0 to 7), they are already - // set to 0/false/white when the grid of modules was initialized - } - } - } -} - -#pragma mark - Penalty Calculation - -#define PENALTY_N1 3 -#define PENALTY_N2 3 -#define PENALTY_N3 40 -#define PENALTY_N4 10 - -// Calculates and returns the penalty score based on state of this QR Code's current modules. -// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. -// @TODO: This can be optimized by working with the bytes instead of bits. -static uint32_t getPenaltyScore(BitBucket* modules) { - uint32_t result = 0; - - uint8_t size = modules->bitOffsetOrWidth; - - // Adjacent modules in row having same color - for(uint8_t y = 0; y < size; y++) { - bool colorX = bb_getBit(modules, 0, y); - for(uint8_t x = 1, runX = 1; x < size; x++) { - bool cx = bb_getBit(modules, x, y); - if(cx != colorX) { - colorX = cx; - runX = 1; - - } else { - runX++; - if(runX == 5) { - result += PENALTY_N1; - } else if(runX > 5) { - result++; - } - } - } - } - - // Adjacent modules in column having same color - for(uint8_t x = 0; x < size; x++) { - bool colorY = bb_getBit(modules, x, 0); - for(uint8_t y = 1, runY = 1; y < size; y++) { - bool cy = bb_getBit(modules, x, y); - if(cy != colorY) { - colorY = cy; - runY = 1; - } else { - runY++; - if(runY == 5) { - result += PENALTY_N1; - } else if(runY > 5) { - result++; - } - } - } - } - - uint16_t black = 0; - for(uint8_t y = 0; y < size; y++) { - uint16_t bitsRow = 0, bitsCol = 0; - for(uint8_t x = 0; x < size; x++) { - bool color = bb_getBit(modules, x, y); - - // 2*2 blocks of modules having same color - if(x > 0 && y > 0) { - bool colorUL = bb_getBit(modules, x - 1, y - 1); - bool colorUR = bb_getBit(modules, x, y - 1); - bool colorL = bb_getBit(modules, x - 1, y); - if(color == colorUL && color == colorUR && color == colorL) { - result += PENALTY_N2; - } - } - - // Finder-like pattern in rows and columns - bitsRow = ((bitsRow << 1) & 0x7FF) | color; - bitsCol = ((bitsCol << 1) & 0x7FF) | bb_getBit(modules, y, x); - - // Needs 11 bits accumulated - if(x >= 10) { - if(bitsRow == 0x05D || bitsRow == 0x5D0) { - result += PENALTY_N3; - } - if(bitsCol == 0x05D || bitsCol == 0x5D0) { - result += PENALTY_N3; - } - } - - // Balance of black and white modules - if(color) { - black++; - } - } - } - - // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% - uint16_t total = size * size; - for(uint16_t k = 0; black * 20 < (9 - k) * total || black * 20 > (11 + k) * total; k++) { - result += PENALTY_N4; - } - - return result; -} - -#pragma mark - Reed-Solomon Generator - -static uint8_t rs_multiply(uint8_t x, uint8_t y) { - // Russian peasant multiplication - // See: https://en.wikipedia.org/wiki/Ancient_Egyptian_multiplication - uint16_t z = 0; - for(int8_t i = 7; i >= 0; i--) { - z = (z << 1) ^ ((z >> 7) * 0x11D); - z ^= ((y >> i) & 1) * x; - } - return z; -} - -static void rs_init(uint8_t degree, uint8_t* coeff) { - memset(coeff, 0, degree); - coeff[degree - 1] = 1; - - // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), - // drop the highest term, and store the rest of the coefficients in order of descending powers. - // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). - uint16_t root = 1; - for(uint8_t i = 0; i < degree; i++) { - // Multiply the current product by (x - r^i) - for(uint8_t j = 0; j < degree; j++) { - coeff[j] = rs_multiply(coeff[j], root); - if(j + 1 < degree) { - coeff[j] ^= coeff[j + 1]; - } - } - root = (root << 1) ^ ((root >> 7) * 0x11D); // Multiply by 0x02 mod GF(2^8/0x11D) - } -} - -static void rs_getRemainder( - uint8_t degree, - uint8_t* coeff, - uint8_t* data, - uint8_t length, - uint8_t* result, - uint8_t stride) { - // Compute the remainder by performing polynomial division - - //for (uint8_t i = 0; i < degree; i++) { result[] = 0; } - //memset(result, 0, degree); - - for(uint8_t i = 0; i < length; i++) { - uint8_t factor = data[i] ^ result[0]; - for(uint8_t j = 1; j < degree; j++) { - result[(j - 1) * stride] = result[j * stride]; - } - result[(degree - 1) * stride] = 0; - - for(uint8_t j = 0; j < degree; j++) { - result[j * stride] ^= rs_multiply(coeff[j], factor); - } - } -} - -#pragma mark - QrCode - -static int8_t encodeDataCodewords( - BitBucket* dataCodewords, - const uint8_t* text, - uint16_t length, - uint8_t version) { - int8_t mode = MODE_BYTE; - - if(isNumeric((char*)text, length)) { - mode = MODE_NUMERIC; - bb_appendBits(dataCodewords, 1 << MODE_NUMERIC, 4); - bb_appendBits(dataCodewords, length, getModeBits(version, MODE_NUMERIC)); - - uint16_t accumData = 0; - uint8_t accumCount = 0; - for(uint16_t i = 0; i < length; i++) { - accumData = accumData * 10 + ((char)(text[i]) - '0'); - accumCount++; - if(accumCount == 3) { - bb_appendBits(dataCodewords, accumData, 10); - accumData = 0; - accumCount = 0; - } - } - - // 1 or 2 digits remaining - if(accumCount > 0) { - bb_appendBits(dataCodewords, accumData, accumCount * 3 + 1); - } - - } else if(isAlphanumeric((char*)text, length)) { - mode = MODE_ALPHANUMERIC; - bb_appendBits(dataCodewords, 1 << MODE_ALPHANUMERIC, 4); - bb_appendBits(dataCodewords, length, getModeBits(version, MODE_ALPHANUMERIC)); - - uint16_t accumData = 0; - uint8_t accumCount = 0; - for(uint16_t i = 0; i < length; i++) { - accumData = accumData * 45 + getAlphanumeric((char)(text[i])); - accumCount++; - if(accumCount == 2) { - bb_appendBits(dataCodewords, accumData, 11); - accumData = 0; - accumCount = 0; - } - } - - // 1 character remaining - if(accumCount > 0) { - bb_appendBits(dataCodewords, accumData, 6); - } - - } else { - bb_appendBits(dataCodewords, 1 << MODE_BYTE, 4); - bb_appendBits(dataCodewords, length, getModeBits(version, MODE_BYTE)); - for(uint16_t i = 0; i < length; i++) { - bb_appendBits(dataCodewords, (char)(text[i]), 8); - } - } - - //bb_setBits(dataCodewords, length, 4, getModeBits(version, mode)); - - return mode; -} - -static void performErrorCorrection(uint8_t version, uint8_t ecc, BitBucket* data) { - // See: http://www.thonky.com/qr-code-tutorial/structure-final-message - -#if LOCK_VERSION == 0 - uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc][version - 1]; - uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc][version - 1]; - uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1]; -#else - uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc]; - uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc]; - uint16_t moduleCount = NUM_RAW_DATA_MODULES; -#endif - - uint8_t blockEccLen = totalEcc / numBlocks; - uint8_t numShortBlocks = numBlocks - moduleCount / 8 % numBlocks; - uint8_t shortBlockLen = moduleCount / 8 / numBlocks; - - uint8_t shortDataBlockLen = shortBlockLen - blockEccLen; - - uint8_t result[data->capacityBytes]; - memset(result, 0, sizeof(result)); - - uint8_t coeff[blockEccLen]; - rs_init(blockEccLen, coeff); - - uint16_t offset = 0; - uint8_t* dataBytes = data->data; - - // Interleave all short blocks - for(uint8_t i = 0; i < shortDataBlockLen; i++) { - uint16_t index = i; - uint8_t stride = shortDataBlockLen; - for(uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) { - result[offset++] = dataBytes[index]; - -#if LOCK_VERSION == 0 || LOCK_VERSION >= 5 - if(blockNum == numShortBlocks) { - stride++; - } -#endif - index += stride; - } - } - - // Version less than 5 only have short blocks -#if LOCK_VERSION == 0 || LOCK_VERSION >= 5 - { - // Interleave long blocks - uint16_t index = shortDataBlockLen * (numShortBlocks + 1); - uint8_t stride = shortDataBlockLen; - for(uint8_t blockNum = 0; blockNum < numBlocks - numShortBlocks; blockNum++) { - result[offset++] = dataBytes[index]; - - if(blockNum == 0) { - stride++; - } - index += stride; - } - } -#endif - - // Add all ecc blocks, interleaved - uint8_t blockSize = shortDataBlockLen; - for(uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) { -#if LOCK_VERSION == 0 || LOCK_VERSION >= 5 - if(blockNum == numShortBlocks) { - blockSize++; - } -#endif - rs_getRemainder( - blockEccLen, coeff, dataBytes, blockSize, &result[offset + blockNum], numBlocks); - dataBytes += blockSize; - } - - memcpy(data->data, result, data->capacityBytes); - data->bitOffsetOrWidth = moduleCount; -} - -// We store the Format bits tightly packed into a single byte (each of the 4 modes is 2 bits) -// The format bits can be determined by ECC_FORMAT_BITS >> (2 * ecc) -static const uint8_t ECC_FORMAT_BITS = (0x02 << 6) | (0x03 << 4) | (0x00 << 2) | (0x01 << 0); - -#pragma mark - Public QRCode functions - -uint16_t qrcode_getBufferSize(uint8_t version) { - return bb_getGridSizeBytes(4 * version + 17); -} - -// @TODO: Return error if data is too big. -int8_t qrcode_initBytes( - QRCode* qrcode, - uint8_t* modules, - uint8_t version, - uint8_t ecc, - uint8_t* data, - uint16_t length) { - uint8_t size = version * 4 + 17; - qrcode->version = version; - qrcode->size = size; - qrcode->ecc = ecc; - qrcode->modules = modules; - - uint8_t eccFormatBits = (ECC_FORMAT_BITS >> (2 * ecc)) & 0x03; - -#if LOCK_VERSION == 0 - uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1]; - uint16_t dataCapacity = - moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits][version - 1]; -#else - version = LOCK_VERSION; - uint16_t moduleCount = NUM_RAW_DATA_MODULES; - uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits]; -#endif - - struct BitBucket codewords; - uint8_t codewordBytes[bb_getBufferSizeBytes(moduleCount)]; - bb_initBuffer(&codewords, codewordBytes, (int32_t)sizeof(codewordBytes)); - - // Place the data code words into the buffer - int8_t mode = encodeDataCodewords(&codewords, data, length, version); - - if(mode < 0) { - return -1; - } - qrcode->mode = mode; - - // Add terminator and pad up to a byte if applicable - uint32_t padding = (dataCapacity * 8) - codewords.bitOffsetOrWidth; - if(padding > 4) { - padding = 4; - } - bb_appendBits(&codewords, 0, padding); - bb_appendBits(&codewords, 0, (8 - codewords.bitOffsetOrWidth % 8) % 8); - - // Pad with alternate bytes until data capacity is reached - for(uint8_t padByte = 0xEC; codewords.bitOffsetOrWidth < (dataCapacity * 8); - padByte ^= 0xEC ^ 0x11) { - bb_appendBits(&codewords, padByte, 8); - } - - BitBucket modulesGrid; - bb_initGrid(&modulesGrid, modules, size); - - BitBucket isFunctionGrid; - uint8_t isFunctionGridBytes[bb_getGridSizeBytes(size)]; - bb_initGrid(&isFunctionGrid, isFunctionGridBytes, size); - - // Draw function patterns, draw all codewords, do masking - drawFunctionPatterns(&modulesGrid, &isFunctionGrid, version, eccFormatBits); - performErrorCorrection(version, eccFormatBits, &codewords); - drawCodewords(&modulesGrid, &isFunctionGrid, &codewords); - - // Find the best (lowest penalty) mask - uint8_t mask = 0; - int32_t minPenalty = INT32_MAX; - for(uint8_t i = 0; i < 8; i++) { - drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, i); - applyMask(&modulesGrid, &isFunctionGrid, i); - int penalty = getPenaltyScore(&modulesGrid); - if(penalty < minPenalty) { - mask = i; - minPenalty = penalty; - } - applyMask(&modulesGrid, &isFunctionGrid, i); // Undoes the mask due to XOR - } - - qrcode->mask = mask; - - // Overwrite old format bits - drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, mask); - - // Apply the final choice of mask - applyMask(&modulesGrid, &isFunctionGrid, mask); - - return 0; -} - -int8_t qrcode_initText( - QRCode* qrcode, - uint8_t* modules, - uint8_t version, - uint8_t ecc, - const char* data) { - return qrcode_initBytes(qrcode, modules, version, ecc, (uint8_t*)data, strlen(data)); -} - -bool qrcode_getModule(QRCode* qrcode, uint8_t x, uint8_t y) { - if(x < 0 || x >= qrcode->size || y < 0 || y >= qrcode->size) { - return false; - } - - uint32_t offset = y * qrcode->size + x; - return (qrcode->modules[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0; -} diff --git a/lib/qrcode/qrcode.h b/lib/qrcode/qrcode.h deleted file mode 100644 index 6d637ba04d6..00000000000 --- a/lib/qrcode/qrcode.h +++ /dev/null @@ -1,99 +0,0 @@ -/** - * The MIT License (MIT) - * - * This library is written and maintained by Richard Moore. - * Major parts were derived from Project Nayuki's library. - * - * Copyright (c) 2017 Richard Moore (https://github.com/ricmoo/QRCode) - * Copyright (c) 2017 Project Nayuki (https://www.nayuki.io/page/qr-code-generator-library) - * - * 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. - */ - -/** - * Special thanks to Nayuki (https://www.nayuki.io/) from which this library was - * heavily inspired and compared against. - * - * See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp - */ - -#ifndef __QRCODE_H_ -#define __QRCODE_H_ - -#ifndef __cplusplus -typedef unsigned char bool; -static const bool false = 0; -static const bool true = 1; -#endif - -#include - -// QR Code Format Encoding -#define MODE_NUMERIC 0 -#define MODE_ALPHANUMERIC 1 -#define MODE_BYTE 2 - -// Error Correction Code Levels -#define ECC_LOW 0 -#define ECC_MEDIUM 1 -#define ECC_QUARTILE 2 -#define ECC_HIGH 3 - -// If set to non-zero, this library can ONLY produce QR codes at that version -// This saves a lot of dynamic memory, as the codeword tables are skipped -#ifndef LOCK_VERSION -#define LOCK_VERSION 0 -#endif - -typedef struct QRCode { - uint8_t version; - uint8_t size; - uint8_t ecc; - uint8_t mode; - uint8_t mask; - uint8_t* modules; -} QRCode; - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -uint16_t qrcode_getBufferSize(uint8_t version); - -int8_t qrcode_initText( - QRCode* qrcode, - uint8_t* modules, - uint8_t version, - uint8_t ecc, - const char* data); -int8_t qrcode_initBytes( - QRCode* qrcode, - uint8_t* modules, - uint8_t version, - uint8_t ecc, - uint8_t* data, - uint16_t length); - -bool qrcode_getModule(QRCode* qrcode, uint8_t x, uint8_t y); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* __QRCODE_H_ */ diff --git a/lib/scons b/lib/scons deleted file mode 160000 index c2d1f09f615..00000000000 --- a/lib/scons +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c2d1f09f615a9ef3fb5497a7e8e5ee2c900d21a7 diff --git a/lib/signal_reader/SConscript b/lib/signal_reader/SConscript new file mode 100644 index 00000000000..386e3419d75 --- /dev/null +++ b/lib/signal_reader/SConscript @@ -0,0 +1,23 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/signal_reader", + ], + SDK_HEADERS=[ + File("signal_reader.h"), + ], + LINT_SOURCES=[ + Dir("."), + ], +) + +libenv = env.Clone(FW_LIB_NAME="signal_reader") +libenv.ApplyLibFlags() +libenv.AppendUnique(CCFLAGS=["-O3", "-funroll-loops", "-Ofast"]) + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/signal_reader/parsers/iso15693/iso15693_parser.c b/lib/signal_reader/parsers/iso15693/iso15693_parser.c new file mode 100644 index 00000000000..c5326a354d7 --- /dev/null +++ b/lib/signal_reader/parsers/iso15693/iso15693_parser.c @@ -0,0 +1,300 @@ +#include "iso15693_parser.h" + +#include + +#include + +#define ISO15693_PARSER_SIGNAL_READER_BUFF_SIZE (2) +#define ISO15693_PARSER_BITSTREAM_BUFF_SIZE (32) +#define ISO15693_PARSER_BITRATE_F64MHZ (603U) + +#define TAG "Iso15693Parser" + +typedef enum { + Iso15693ParserStateParseSoF, + Iso15693ParserStateParseFrame, + Iso15693ParserStateFail, +} Iso15693ParserState; + +typedef enum { + Iso15693ParserMode1OutOf4, + Iso15693ParserMode1OutOf256, + + Iso15693ParserModeNum, +} Iso15693ParserMode; + +struct Iso15693Parser { + Iso15693ParserState state; + Iso15693ParserMode mode; + + SignalReader* signal_reader; + + uint8_t bitstream_buff[ISO15693_PARSER_BITSTREAM_BUFF_SIZE]; + size_t bitstream_idx; + uint8_t last_byte; + + bool signal_detected; + bool bit_offset_calculated; + uint8_t bit_offset; + size_t byte_idx; + size_t bytes_to_process; + + uint8_t next_byte; + uint16_t next_byte_part; + bool zero_found; + + BitBuffer* parsed_frame; + bool eof_received; + bool frame_parsed; + + Iso15693ParserCallback callback; + void* context; +}; + +typedef enum { + Iso15693ParserCommandProcessed, + Iso15693ParserCommandWaitData, + Iso15693ParserCommandFail, + Iso15693ParserCommandSuccess, +} Iso15693ParserCommand; + +typedef Iso15693ParserCommand (*Iso15693ParserStateHandler)(Iso15693Parser* instance); + +Iso15693Parser* iso15693_parser_alloc(const GpioPin* pin, size_t max_frame_size) { + Iso15693Parser* instance = malloc(sizeof(Iso15693Parser)); + instance->parsed_frame = bit_buffer_alloc(max_frame_size); + + instance->signal_reader = signal_reader_alloc(pin, ISO15693_PARSER_SIGNAL_READER_BUFF_SIZE); + signal_reader_set_sample_rate( + instance->signal_reader, SignalReaderTimeUnit64Mhz, ISO15693_PARSER_BITRATE_F64MHZ); + signal_reader_set_pull(instance->signal_reader, GpioPullDown); + signal_reader_set_polarity(instance->signal_reader, SignalReaderPolarityNormal); + signal_reader_set_trigger(instance->signal_reader, SignalReaderTriggerRisingFallingEdge); + + return instance; +} + +void iso15693_parser_free(Iso15693Parser* instance) { + furi_assert(instance); + + bit_buffer_free(instance->parsed_frame); + signal_reader_free(instance->signal_reader); + free(instance); +} + +void iso15693_parser_reset(Iso15693Parser* instance) { + furi_assert(instance); + + instance->state = Iso15693ParserStateParseSoF; + instance->mode = Iso15693ParserMode1OutOf4; + memset(instance->bitstream_buff, 0x00, sizeof(instance->bitstream_buff)); + instance->bitstream_idx = 0; + + instance->next_byte = 0; + instance->next_byte_part = 0; + + instance->bit_offset = 0; + instance->byte_idx = 0; + instance->bytes_to_process = 0; + instance->signal_detected = false; + instance->bit_offset_calculated = false; + + instance->last_byte = 0x00; + instance->zero_found = false; + instance->eof_received = false; + + bit_buffer_reset(instance->parsed_frame); + instance->frame_parsed = false; +} + +static void signal_reader_callback(SignalReaderEvent event, void* context) { + furi_assert(context); + furi_assert(event.data->data); + furi_assert(event.data->len == ISO15693_PARSER_SIGNAL_READER_BUFF_SIZE / 2); + + Iso15693Parser* instance = context; + furi_assert(instance->callback); + + const uint8_t sof_1_out_of_4 = 0x21; + const uint8_t sof_1_out_of_256 = 0x81; + const uint8_t eof_single = 0x01; + const uint8_t eof = 0x04; + + if(instance->state == Iso15693ParserStateParseSoF) { + if(event.data->data[0] == sof_1_out_of_4) { + instance->mode = Iso15693ParserMode1OutOf4; + instance->state = Iso15693ParserStateParseFrame; + } else if(event.data->data[0] == sof_1_out_of_256) { + instance->mode = Iso15693ParserMode1OutOf256; + instance->state = Iso15693ParserStateParseFrame; + } else if(event.data->data[0] == eof_single) { + instance->eof_received = true; + instance->callback(Iso15693ParserEventDataReceived, instance->context); + } else { + instance->state = Iso15693ParserStateFail; + instance->callback(Iso15693ParserEventDataReceived, instance->context); + } + } else { + if(instance->mode == Iso15693ParserMode1OutOf4) { + if(event.data->data[0] == eof) { + instance->eof_received = true; + instance->callback(Iso15693ParserEventDataReceived, instance->context); + } else { + instance->bitstream_buff[instance->bytes_to_process] = event.data->data[0]; + instance->bytes_to_process++; + if(instance->bytes_to_process == ISO15693_PARSER_BITSTREAM_BUFF_SIZE) { + instance->callback(Iso15693ParserEventDataReceived, instance->context); + } + } + } else { + instance->bitstream_buff[instance->bytes_to_process] = event.data->data[0]; + instance->bytes_to_process++; + if(instance->bytes_to_process == ISO15693_PARSER_BITSTREAM_BUFF_SIZE) { + instance->callback(Iso15693ParserEventDataReceived, instance->context); + } + } + } +} + +static void iso15693_parser_start_signal_reader(Iso15693Parser* instance) { + iso15693_parser_reset(instance); + signal_reader_start(instance->signal_reader, signal_reader_callback, instance); +} + +void iso15693_parser_start( + Iso15693Parser* instance, + Iso15693ParserCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; + iso15693_parser_start_signal_reader(instance); +} + +void iso15693_parser_stop(Iso15693Parser* instance) { + furi_assert(instance); + + signal_reader_stop(instance->signal_reader); +} + +static Iso15693ParserCommand iso15693_parser_parse_1_out_of_4(Iso15693Parser* instance) { + Iso15693ParserCommand command = Iso15693ParserCommandWaitData; + const uint8_t bit_patterns_1_out_of_4[] = {0x02, 0x08, 0x20, 0x80}; + + for(size_t i = 0; i < instance->bytes_to_process; i++) { + // Check next pattern + size_t j = 0; + for(j = 0; j < COUNT_OF(bit_patterns_1_out_of_4); j++) { + if(instance->bitstream_buff[i] == bit_patterns_1_out_of_4[j]) { + instance->next_byte |= j << (instance->next_byte_part * 2); + instance->next_byte_part++; + if(instance->next_byte_part == 4) { + instance->next_byte_part = 0; + bit_buffer_append_byte(instance->parsed_frame, instance->next_byte); + instance->next_byte = 0; + } + break; + } + } + if(j == COUNT_OF(bit_patterns_1_out_of_4)) { + command = Iso15693ParserCommandFail; + break; + } + } + + if(command != Iso15693ParserCommandFail) { + if(instance->eof_received) { + command = Iso15693ParserCommandSuccess; + instance->frame_parsed = true; + } + } + + instance->bytes_to_process = 0; + + return command; +} + +static Iso15693ParserCommand iso15693_parser_parse_1_out_of_256(Iso15693Parser* instance) { + Iso15693ParserCommand command = Iso15693ParserCommandWaitData; + const uint8_t eof = 0x04; + + for(size_t i = instance->byte_idx; i < instance->bytes_to_process; i++) { + // Check EoF + if(instance->next_byte_part == 0) { + if(instance->bitstream_buff[i] == eof) { + instance->frame_parsed = true; + command = Iso15693ParserCommandSuccess; + break; + } + } + + if(instance->zero_found) { + if(instance->bitstream_buff[i] != 0x00) { + command = Iso15693ParserCommandFail; + break; + } + } else { + if(instance->bitstream_buff[i] != 0x00) { + for(size_t j = 0; j < 8; j++) { + if(FURI_BIT(instance->bitstream_buff[i], j) == 1) { + bit_buffer_append_byte( + instance->parsed_frame, instance->next_byte_part * 4 + j / 2); + } + } + } + } + instance->next_byte_part = (instance->next_byte_part + 1) % 64; + } + instance->bytes_to_process = 0; + instance->byte_idx = 0; + + return command; +} + +static const Iso15693ParserStateHandler iso15693_parser_state_handlers[Iso15693ParserModeNum] = { + [Iso15693ParserMode1OutOf4] = iso15693_parser_parse_1_out_of_4, + [Iso15693ParserMode1OutOf256] = iso15693_parser_parse_1_out_of_256, +}; + +bool iso15693_parser_run(Iso15693Parser* instance) { + if(instance->state == Iso15693ParserStateFail) { + iso15693_parser_stop(instance); + iso15693_parser_start_signal_reader(instance); + } else if((instance->state == Iso15693ParserStateParseSoF) && (instance->eof_received)) { + instance->frame_parsed = true; + } else if(instance->bytes_to_process) { + Iso15693ParserCommand command = Iso15693ParserCommandProcessed; + while(command == Iso15693ParserCommandProcessed) { + command = iso15693_parser_state_handlers[instance->mode](instance); + } + + if(command == Iso15693ParserCommandFail) { + iso15693_parser_stop(instance); + iso15693_parser_start_signal_reader(instance); + FURI_LOG_D(TAG, "Frame parse failed"); + } + } + + return instance->frame_parsed; +} + +size_t iso15693_parser_get_data_size_bytes(Iso15693Parser* instance) { + furi_assert(instance); + + return bit_buffer_get_size_bytes(instance->parsed_frame); +} + +void iso15693_parser_get_data( + Iso15693Parser* instance, + uint8_t* buff, + size_t buff_size, + size_t* data_bits) { + furi_assert(instance); + furi_assert(buff); + furi_assert(data_bits); + + bit_buffer_write_bytes(instance->parsed_frame, buff, buff_size); + *data_bits = bit_buffer_get_size(instance->parsed_frame); +} diff --git a/lib/signal_reader/parsers/iso15693/iso15693_parser.h b/lib/signal_reader/parsers/iso15693/iso15693_parser.h new file mode 100644 index 00000000000..3017a96d79a --- /dev/null +++ b/lib/signal_reader/parsers/iso15693/iso15693_parser.h @@ -0,0 +1,42 @@ +#pragma once + +#include "../../signal_reader.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Iso15693Parser Iso15693Parser; + +typedef enum { + Iso15693ParserEventDataReceived, +} Iso15693ParserEvent; + +typedef void (*Iso15693ParserCallback)(Iso15693ParserEvent event, void* context); + +Iso15693Parser* iso15693_parser_alloc(const GpioPin* pin, size_t max_frame_size); + +void iso15693_parser_free(Iso15693Parser* instance); + +void iso15693_parser_reset(Iso15693Parser* instance); + +void iso15693_parser_start( + Iso15693Parser* instance, + Iso15693ParserCallback callback, + void* context); + +void iso15693_parser_stop(Iso15693Parser* instance); + +bool iso15693_parser_run(Iso15693Parser* instance); + +size_t iso15693_parser_get_data_size_bytes(Iso15693Parser* instance); + +void iso15693_parser_get_data( + Iso15693Parser* instance, + uint8_t* buff, + size_t buff_size, + size_t* data_bits); + +#ifdef __cplusplus +} +#endif diff --git a/lib/signal_reader/signal_reader.c b/lib/signal_reader/signal_reader.c new file mode 100644 index 00000000000..7c4d0bae7ed --- /dev/null +++ b/lib/signal_reader/signal_reader.c @@ -0,0 +1,317 @@ +#include "signal_reader.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define SIGNAL_READER_DMA DMA2 + +#define SIGNAL_READER_CAPTURE_TIM (TIM16) +#define SIGNAL_READER_CAPTURE_TIM_CHANNEL LL_TIM_CHANNEL_CH1 + +#define SIGNAL_READER_DMA_GPIO LL_DMA_CHANNEL_2 +#define SIGNAL_READER_DMA_GPIO_IRQ FuriHalInterruptIdDma2Ch2 +#define SIGNAL_READER_DMA_GPIO_DEF SIGNAL_READER_DMA, SIGNAL_READER_DMA_GPIO + +#define SIGNAL_READER_DMA_TRIGGER LL_DMA_CHANNEL_3 +#define SIGNAL_READER_DMA_TRIGGER_IRQ FuriHalInterruptIdDma2Ch3 +#define SIGNAL_READER_DMA_TRIGGER_DEF SIGNAL_READER_DMA, SIGNAL_READER_DMA_TRIGGER + +#define SIGNAL_READER_DMA_CNT_SYNC LL_DMA_CHANNEL_5 +#define SIGNAL_READER_DMA_CNT_SYNC_IRQ FuriHalInterruptIdDma2Ch5 +#define SIGNAL_READER_DMA_CNT_SYNC_DEF SIGNAL_READER_DMA, SIGNAL_READER_DMA_CNT_SYNC + +struct SignalReader { + size_t buffer_size; + const GpioPin* pin; + GpioPull pull; + SignalReaderPolarity polarity; + SignalReaderTrigger trigger; + + uint16_t* gpio_buffer; + uint8_t* bitstream_buffer; + uint32_t cnt_en; + + uint32_t tim_cnt_compensation; + uint32_t tim_arr; + + SignalReaderEvent event; + SignalReaderEventData event_data; + + SignalReaderCallback callback; + void* context; +}; + +#define GPIO_PIN_MAP(pin, prefix) \ + (((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \ + ((pin) == (LL_GPIO_PIN_1)) ? prefix##1 : \ + ((pin) == (LL_GPIO_PIN_2)) ? prefix##2 : \ + ((pin) == (LL_GPIO_PIN_3)) ? prefix##3 : \ + ((pin) == (LL_GPIO_PIN_4)) ? prefix##4 : \ + ((pin) == (LL_GPIO_PIN_5)) ? prefix##5 : \ + ((pin) == (LL_GPIO_PIN_6)) ? prefix##6 : \ + ((pin) == (LL_GPIO_PIN_7)) ? prefix##7 : \ + ((pin) == (LL_GPIO_PIN_8)) ? prefix##8 : \ + ((pin) == (LL_GPIO_PIN_9)) ? prefix##9 : \ + ((pin) == (LL_GPIO_PIN_10)) ? prefix##10 : \ + ((pin) == (LL_GPIO_PIN_11)) ? prefix##11 : \ + ((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \ + ((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \ + ((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \ + prefix##15) + +#define GET_DMAMUX_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_DMAMUX_REQ_GEN_EXTI_LINE) + +SignalReader* signal_reader_alloc(const GpioPin* gpio_pin, uint32_t size) { + SignalReader* instance = malloc(sizeof(SignalReader)); + + instance->pin = gpio_pin; + instance->pull = GpioPullNo; + + instance->buffer_size = size; + instance->gpio_buffer = malloc(sizeof(uint16_t) * size * 8); + instance->bitstream_buffer = malloc(size); + + instance->event.data = &instance->event_data; + + return instance; +} + +void signal_reader_free(SignalReader* instance) { + furi_assert(instance); + furi_assert(instance->gpio_buffer); + furi_assert(instance->bitstream_buffer); + + free(instance->gpio_buffer); + free(instance->bitstream_buffer); + free(instance); +} + +void signal_reader_set_pull(SignalReader* instance, GpioPull pull) { + furi_assert(instance); + + instance->pull = pull; +} + +void signal_reader_set_polarity(SignalReader* instance, SignalReaderPolarity polarity) { + furi_assert(instance); + + instance->polarity = polarity; +} + +void signal_reader_set_sample_rate( + SignalReader* instance, + SignalReaderTimeUnit time_unit, + uint32_t time) { + furi_assert(instance); + UNUSED(time_unit); + + instance->tim_arr = time; +} + +void signal_reader_set_trigger(SignalReader* instance, SignalReaderTrigger trigger) { + furi_assert(instance); + + instance->trigger = trigger; +} + +static void furi_hal_sw_digital_pin_dma_rx_isr(void* context) { + SignalReader* instance = context; + + uint16_t* gpio_buff_start = NULL; + uint8_t* bitstream_buff_start = NULL; + + if(LL_DMA_IsActiveFlag_HT2(SIGNAL_READER_DMA)) { + LL_DMA_ClearFlag_HT2(SIGNAL_READER_DMA); + instance->event.type = SignalReaderEventTypeHalfBufferFilled; + gpio_buff_start = instance->gpio_buffer; + bitstream_buff_start = instance->bitstream_buffer; + + if(instance->callback) { + furi_assert(gpio_buff_start); + furi_assert(bitstream_buff_start); + + for(size_t i = 0; i < instance->buffer_size * 4; i++) { + if((i % 8) == 0) { + bitstream_buff_start[i / 8] = 0; + } + uint8_t bit = 0; + if(instance->polarity == SignalReaderPolarityNormal) { + bit = (gpio_buff_start[i] & instance->pin->pin) == instance->pin->pin; + } else { + bit = (gpio_buff_start[i] & instance->pin->pin) == 0; + } + bitstream_buff_start[i / 8] |= bit << (i % 8); + } + instance->event_data.data = bitstream_buff_start; + instance->event_data.len = instance->buffer_size / 2; + instance->callback(instance->event, instance->context); + } + } + if(LL_DMA_IsActiveFlag_TC2(SIGNAL_READER_DMA)) { + LL_DMA_ClearFlag_TC2(SIGNAL_READER_DMA); + instance->event.type = SignalReaderEventTypeFullBufferFilled; + gpio_buff_start = &instance->gpio_buffer[instance->buffer_size * 4]; + bitstream_buff_start = &instance->bitstream_buffer[instance->buffer_size / 2]; + + if(instance->callback) { + furi_assert(gpio_buff_start); + furi_assert(bitstream_buff_start); + + for(size_t i = 0; i < instance->buffer_size * 4; i++) { + if((i % 8) == 0) { + bitstream_buff_start[i / 8] = 0; + } + uint8_t bit = 0; + if(instance->polarity == SignalReaderPolarityNormal) { + bit = (gpio_buff_start[i] & instance->pin->pin) == instance->pin->pin; + } else { + bit = (gpio_buff_start[i] & instance->pin->pin) == 0; + } + bitstream_buff_start[i / 8] |= bit << (i % 8); + } + instance->event_data.data = bitstream_buff_start; + instance->event_data.len = instance->buffer_size / 2; + instance->callback(instance->event, instance->context); + } + } +} + +void signal_reader_start(SignalReader* instance, SignalReaderCallback callback, void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; + + // EXTI delay compensation + instance->tim_cnt_compensation = 9; + instance->cnt_en = SIGNAL_READER_CAPTURE_TIM->CR1; + instance->cnt_en |= TIM_CR1_CEN; + + furi_hal_bus_enable(FuriHalBusTIM16); + + // Capture timer config + LL_TIM_SetPrescaler(SIGNAL_READER_CAPTURE_TIM, 0); + LL_TIM_SetCounterMode(SIGNAL_READER_CAPTURE_TIM, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetAutoReload(SIGNAL_READER_CAPTURE_TIM, instance->tim_arr); + LL_TIM_SetClockDivision(SIGNAL_READER_CAPTURE_TIM, LL_TIM_CLOCKDIVISION_DIV1); + + LL_TIM_DisableARRPreload(SIGNAL_READER_CAPTURE_TIM); + LL_TIM_SetClockSource(SIGNAL_READER_CAPTURE_TIM, LL_TIM_CLOCKSOURCE_INTERNAL); + + // Configure TIM channel CC1 + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {}; + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_FROZEN; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE; + TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE; + TIM_OC_InitStruct.CompareValue = (instance->tim_arr / 2); + TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH; + LL_TIM_OC_Init( + SIGNAL_READER_CAPTURE_TIM, SIGNAL_READER_CAPTURE_TIM_CHANNEL, &TIM_OC_InitStruct); + LL_TIM_OC_DisableFast(SIGNAL_READER_CAPTURE_TIM, SIGNAL_READER_CAPTURE_TIM_CHANNEL); + + LL_TIM_SetTriggerOutput(SIGNAL_READER_CAPTURE_TIM, LL_TIM_TRGO_RESET); + LL_TIM_DisableMasterSlaveMode(SIGNAL_READER_CAPTURE_TIM); + + // Start + LL_TIM_GenerateEvent_UPDATE(SIGNAL_READER_CAPTURE_TIM); + + /* We need the EXTI to be configured as interrupt generating line, but no ISR registered */ + furi_hal_gpio_init( + instance->pin, GpioModeInterruptRiseFall, instance->pull, GpioSpeedVeryHigh); + + /* Set DMAMUX request generation signal ID on specified DMAMUX channel */ + LL_DMAMUX_SetRequestSignalID( + DMAMUX1, LL_DMAMUX_REQ_GEN_0, GET_DMAMUX_EXTI_LINE(instance->pin->pin)); + /* Set the polarity of the signal on which the DMA request is generated */ + LL_DMAMUX_SetRequestGenPolarity(DMAMUX1, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_POL_RISING); + /* Set the number of DMA requests that will be authorized after a generation event */ + LL_DMAMUX_SetGenRequestNb(DMAMUX1, LL_DMAMUX_REQ_GEN_0, 1); + + // Configure DMA Sync + LL_DMA_SetMemoryAddress( + SIGNAL_READER_DMA_CNT_SYNC_DEF, (uint32_t)&instance->tim_cnt_compensation); + LL_DMA_SetPeriphAddress( + SIGNAL_READER_DMA_CNT_SYNC_DEF, (uint32_t) & (SIGNAL_READER_CAPTURE_TIM->CNT)); + LL_DMA_ConfigTransfer( + SIGNAL_READER_DMA_CNT_SYNC_DEF, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_NOINCREMENT | LL_DMA_PDATAALIGN_HALFWORD | LL_DMA_MDATAALIGN_HALFWORD | + LL_DMA_PRIORITY_VERYHIGH); + LL_DMA_SetDataLength(SIGNAL_READER_DMA_CNT_SYNC_DEF, 1); + LL_DMA_SetPeriphRequest(SIGNAL_READER_DMA_CNT_SYNC_DEF, LL_DMAMUX_REQ_GENERATOR0); + + // Configure DMA Sync + LL_DMA_SetMemoryAddress(SIGNAL_READER_DMA_TRIGGER_DEF, (uint32_t)&instance->cnt_en); + LL_DMA_SetPeriphAddress( + SIGNAL_READER_DMA_TRIGGER_DEF, (uint32_t) & (SIGNAL_READER_CAPTURE_TIM->CR1)); + LL_DMA_ConfigTransfer( + SIGNAL_READER_DMA_TRIGGER_DEF, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_NOINCREMENT | + LL_DMA_PDATAALIGN_HALFWORD | LL_DMA_MDATAALIGN_HALFWORD | LL_DMA_PRIORITY_VERYHIGH); + LL_DMA_SetDataLength(SIGNAL_READER_DMA_TRIGGER_DEF, 1); + LL_DMA_SetPeriphRequest(SIGNAL_READER_DMA_TRIGGER_DEF, LL_DMAMUX_REQ_GENERATOR0); + + // Configure DMA Rx pin + LL_DMA_SetMemoryAddress(SIGNAL_READER_DMA_GPIO_DEF, (uint32_t)instance->gpio_buffer); + LL_DMA_SetPeriphAddress(SIGNAL_READER_DMA_GPIO_DEF, (uint32_t) & (instance->pin->port->IDR)); + LL_DMA_ConfigTransfer( + SIGNAL_READER_DMA_GPIO_DEF, + LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_HALFWORD | LL_DMA_MDATAALIGN_HALFWORD | + LL_DMA_PRIORITY_HIGH); + LL_DMA_SetDataLength(SIGNAL_READER_DMA_GPIO_DEF, instance->buffer_size * 8); + LL_DMA_SetPeriphRequest(SIGNAL_READER_DMA_GPIO_DEF, LL_DMAMUX_REQ_TIM16_CH1); + + // Configure DMA Channel CC1 + LL_TIM_EnableDMAReq_CC1(SIGNAL_READER_CAPTURE_TIM); + LL_TIM_CC_EnableChannel(SIGNAL_READER_CAPTURE_TIM, SIGNAL_READER_CAPTURE_TIM_CHANNEL); + + // Start DMA irq, higher priority than normal + furi_hal_interrupt_set_isr_ex( + SIGNAL_READER_DMA_GPIO_IRQ, 14, furi_hal_sw_digital_pin_dma_rx_isr, instance); + + // Start DMA Sync timer + LL_DMA_EnableChannel(SIGNAL_READER_DMA_CNT_SYNC_DEF); + + // Start DMA Rx pin + LL_DMA_EnableChannel(SIGNAL_READER_DMA_GPIO_DEF); + // Strat timer + LL_TIM_SetCounter(SIGNAL_READER_CAPTURE_TIM, 0); + if(instance->trigger == SignalReaderTriggerNone) { + LL_TIM_EnableCounter(SIGNAL_READER_CAPTURE_TIM); + } else { + LL_DMA_EnableChannel(SIGNAL_READER_DMA_TRIGGER_DEF); + } + + LL_DMAMUX_EnableRequestGen(DMAMUX1, LL_DMAMUX_REQ_GEN_0); + // Need to clear flags before enabling DMA !!!! + if(LL_DMA_IsActiveFlag_TC2(SIGNAL_READER_DMA)) LL_DMA_ClearFlag_TC1(SIGNAL_READER_DMA); + if(LL_DMA_IsActiveFlag_TE2(SIGNAL_READER_DMA)) LL_DMA_ClearFlag_TE1(SIGNAL_READER_DMA); + LL_DMA_EnableIT_TC(SIGNAL_READER_DMA_GPIO_DEF); + LL_DMA_EnableIT_HT(SIGNAL_READER_DMA_GPIO_DEF); +} + +void signal_reader_stop(SignalReader* instance) { + furi_assert(instance); + + furi_hal_interrupt_set_isr(SIGNAL_READER_DMA_GPIO_IRQ, NULL, NULL); + + // Deinit DMA Rx pin + LL_DMA_DeInit(SIGNAL_READER_DMA_GPIO_DEF); + // Deinit DMA Sync timer + LL_DMA_DeInit(SIGNAL_READER_DMA_CNT_SYNC_DEF); + // Deinit DMA Trigger timer + LL_DMA_DeInit(SIGNAL_READER_DMA_TRIGGER_DEF); + + furi_hal_bus_disable(FuriHalBusTIM16); +} diff --git a/lib/signal_reader/signal_reader.h b/lib/signal_reader/signal_reader.h new file mode 100644 index 00000000000..be18f295a44 --- /dev/null +++ b/lib/signal_reader/signal_reader.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + SignalReaderEventTypeHalfBufferFilled, + SignalReaderEventTypeFullBufferFilled, +} SignalReaderEventType; + +typedef struct { + uint8_t* data; + size_t len; +} SignalReaderEventData; + +typedef struct { + SignalReaderEventType type; + SignalReaderEventData* data; +} SignalReaderEvent; + +typedef enum { + SignalReaderTimeUnit64Mhz, +} SignalReaderTimeUnit; + +typedef enum { + SignalReaderPolarityNormal, + SignalReaderPolarityInverted, +} SignalReaderPolarity; + +typedef enum { + SignalReaderTriggerNone, + SignalReaderTriggerRisingFallingEdge, +} SignalReaderTrigger; + +typedef void (*SignalReaderCallback)(SignalReaderEvent event, void* context); + +typedef struct SignalReader SignalReader; + +SignalReader* signal_reader_alloc(const GpioPin* gpio_pin, uint32_t size); + +void signal_reader_free(SignalReader* instance); + +void signal_reader_set_pull(SignalReader* instance, GpioPull pull); + +void signal_reader_set_polarity(SignalReader* instance, SignalReaderPolarity polarity); + +void signal_reader_set_sample_rate( + SignalReader* instance, + SignalReaderTimeUnit time_unit, + uint32_t time); + +void signal_reader_set_trigger(SignalReader* instance, SignalReaderTrigger trigger); + +void signal_reader_start(SignalReader* instance, SignalReaderCallback callback, void* context); + +void signal_reader_stop(SignalReader* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/stm32wb.scons b/lib/stm32wb.scons new file mode 100644 index 00000000000..8a8ad964496 --- /dev/null +++ b/lib/stm32wb.scons @@ -0,0 +1,73 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/cmsis_core", + "#/lib/stm32wb_cmsis/Include", + "#/lib/stm32wb_hal/Inc", + "#/lib/stm32wb_copro/wpan", + ], + CPPDEFINES=[ + "STM32WB", + "STM32WB55xx", + "USE_FULL_ASSERT", + "USE_FULL_LL_DRIVER", + ], + SDK_HEADERS=env.GlobRecursive( + "*_ll_*.h", + Dir("stm32wb_hal/Inc"), + exclude="*usb.h", + ), +) + +if env["RAM_EXEC"]: + env.Append( + CPPDEFINES=[ + "VECT_TAB_SRAM", + ], + ) + + +libenv = env.Clone(FW_LIB_NAME="stm32wb") +libenv.Append( + CPPPATH=[ + "#/lib/stm32wb_copro/wpan/ble", + "#/lib/stm32wb_copro/wpan/ble/core", + "#/lib/stm32wb_copro/wpan/interface/patterns/ble_thread", + "#/lib/stm32wb_copro/wpan/interface/patterns/ble_thread/shci", + "#/lib/stm32wb_copro/wpan/interface/patterns/ble_thread/tl", + "#/lib/stm32wb_copro/wpan/utilities", + ] +) +libenv.ApplyLibFlags() + +sources = libenv.GlobRecursive("*_ll_*.c", "stm32wb_hal/Src/", exclude="*usb.c") +sources += Glob( + "stm32wb_copro/wpan/interface/patterns/ble_thread/shci/*.c", + source=True, +) +sources += Glob( + "stm32wb_copro/wpan/interface/patterns/ble_thread/tl/*_tl*.c", + exclude=[ + "stm32wb_copro/wpan/interface/patterns/ble_thread/tl/hci_tl_if.c", + "stm32wb_copro/wpan/interface/patterns/ble_thread/tl/shci_tl_if.c", + ], + source=True, +) +sources += [ + "stm32wb_copro/wpan/interface/patterns/ble_thread/tl/tl_mbox.c", + "stm32wb_copro/wpan/ble/svc/Src/svc_ctl.c", + "stm32wb_copro/wpan/ble/core/auto/ble_gap_aci.c", + "stm32wb_copro/wpan/ble/core/auto/ble_gatt_aci.c", + "stm32wb_copro/wpan/ble/core/auto/ble_hal_aci.c", + "stm32wb_copro/wpan/ble/core/auto/ble_hci_le.c", + "stm32wb_copro/wpan/ble/core/auto/ble_l2cap_aci.c", + "stm32wb_copro/wpan/ble/core/template/osal.c", + "stm32wb_copro/wpan/utilities/dbg_trace.c", + "stm32wb_copro/wpan/utilities/stm_list.c", +] + + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/stm32wb_cmsis b/lib/stm32wb_cmsis new file mode 160000 index 00000000000..d1b860584df --- /dev/null +++ b/lib/stm32wb_cmsis @@ -0,0 +1 @@ +Subproject commit d1b860584dfe24d40d455ae624ed14600dfa93c9 diff --git a/lib/stm32wb_copro b/lib/stm32wb_copro new file mode 160000 index 00000000000..d8a6f1feb0e --- /dev/null +++ b/lib/stm32wb_copro @@ -0,0 +1 @@ +Subproject commit d8a6f1feb0ebb6798c44162c6ae5ea743f90f3df diff --git a/lib/stm32wb_hal b/lib/stm32wb_hal new file mode 160000 index 00000000000..cfd0dd258cb --- /dev/null +++ b/lib/stm32wb_hal @@ -0,0 +1 @@ +Subproject commit cfd0dd258cb031c95b2b2d6d04c19f9f625fe3e8 diff --git a/lib/subghz/SConscript b/lib/subghz/SConscript index 3c6728afe67..d0bc2a2543c 100644 --- a/lib/subghz/SConscript +++ b/lib/subghz/SConscript @@ -4,6 +4,28 @@ env.Append( CPPPATH=[ "#/lib/subghz", ], + LINT_SOURCES=[ + Dir("."), + ], + SDK_HEADERS=[ + File("environment.h"), + File("receiver.h"), + File("registry.h"), + File("subghz_worker.h"), + File("subghz_tx_rx_worker.h"), + File("transmitter.h"), + File("protocols/raw.h"), + File("protocols/public_api.h"), + File("blocks/const.h"), + File("blocks/decoder.h"), + File("blocks/encoder.h"), + File("blocks/generic.h"), + File("blocks/math.h"), + File("subghz_setting.h"), + File("subghz_protocol_registry.h"), + File("devices/cc1101_configs.h"), + File("devices/cc1101_int/cc1101_int_interconnect.h"), + ], ) libenv = env.Clone(FW_LIB_NAME="subghz") diff --git a/lib/subghz/blocks/const.h b/lib/subghz/blocks/const.h index 57b47d50349..f32334e2f44 100644 --- a/lib/subghz/blocks/const.h +++ b/lib/subghz/blocks/const.h @@ -4,9 +4,17 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { const uint16_t te_long; const uint16_t te_short; const uint16_t te_delta; const uint8_t min_count_bit_for_found; } SubGhzBlockConst; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/subghz/blocks/decoder.c b/lib/subghz/blocks/decoder.c index d2237f6c4ef..f491c87bfb5 100644 --- a/lib/subghz/blocks/decoder.c +++ b/lib/subghz/blocks/decoder.c @@ -7,6 +7,16 @@ void subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit) { decoder->decode_count_bit++; } +void subghz_protocol_blocks_add_to_128_bit( + SubGhzBlockDecoder* decoder, + uint8_t bit, + uint64_t* head_64_bit) { + if(++decoder->decode_count_bit > 64) { + (*head_64_bit) = ((*head_64_bit) << 1) | (decoder->decode_data >> 63); + } + decoder->decode_data = decoder->decode_data << 1 | bit; +} + uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len) { uint8_t hash = 0; uint8_t* p = (uint8_t*)&decoder->decode_data; diff --git a/lib/subghz/blocks/decoder.h b/lib/subghz/blocks/decoder.h index 339e27c15ef..a5e561e3517 100644 --- a/lib/subghz/blocks/decoder.h +++ b/lib/subghz/blocks/decoder.h @@ -4,6 +4,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct SubGhzBlockDecoder SubGhzBlockDecoder; struct SubGhzBlockDecoder { @@ -20,9 +24,24 @@ struct SubGhzBlockDecoder { */ void subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit); +/** + * Add data to_128 bit when decoding. + * @param decoder Pointer to a SubGhzBlockDecoder instance + * @param head_64_bit Pointer to a head_64_bit + * @param bit data, 1bit + */ +void subghz_protocol_blocks_add_to_128_bit( + SubGhzBlockDecoder* decoder, + uint8_t bit, + uint64_t* head_64_bit); + /** * Getting the hash sum of the last randomly received parcel. * @param decoder Pointer to a SubGhzBlockDecoder instance * @return hash Hash sum */ uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/subghz/blocks/encoder.c b/lib/subghz/blocks/encoder.c index f3349b5fcb9..49ec4f17701 100644 --- a/lib/subghz/blocks/encoder.c +++ b/lib/subghz/blocks/encoder.c @@ -2,6 +2,8 @@ #include "math.h" #include +#include "furi.h" + #define TAG "SubGhzBlockEncoder" void subghz_protocol_blocks_set_bit_array( @@ -17,21 +19,32 @@ bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_inde return bit_read(data_array[read_index_bit >> 3], 7 - (read_index_bit & 0x7)); } -size_t subghz_protocol_blocks_get_upload( +size_t subghz_protocol_blocks_get_upload_from_bit_array( uint8_t data_array[], size_t count_bit_data_array, LevelDuration* upload, size_t max_size_upload, - uint32_t duration_bit) { - size_t index_bit = 0; + uint32_t duration_bit, + SubGhzProtocolBlockAlignBit align_bit) { + size_t bias_bit = 0; size_t size_upload = 0; uint32_t duration = duration_bit; + + if(align_bit == SubGhzProtocolBlockAlignBitRight) { + if(count_bit_data_array & 0x7) { + bias_bit = 8 - (count_bit_data_array & 0x7); + } + } + size_t index_bit = bias_bit; + bool last_bit = subghz_protocol_blocks_get_bit_array(data_array, index_bit++); - for(size_t i = 1; i < count_bit_data_array; i++) { + for(size_t i = 1 + bias_bit; i < count_bit_data_array + bias_bit; i++) { if(last_bit == subghz_protocol_blocks_get_bit_array(data_array, index_bit)) { duration += duration_bit; } else { - furi_assert(max_size_upload > size_upload); + if(size_upload > max_size_upload) { + furi_crash("SubGhz: Encoder buffer overflow"); + } upload[size_upload++] = level_duration_make( subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration); last_bit = !last_bit; diff --git a/lib/subghz/blocks/encoder.h b/lib/subghz/blocks/encoder.h index 6ad734cbdd2..aeaa2add023 100644 --- a/lib/subghz/blocks/encoder.h +++ b/lib/subghz/blocks/encoder.h @@ -6,6 +6,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { bool is_running; size_t repeat; @@ -15,6 +19,11 @@ typedef struct { } SubGhzProtocolBlockEncoder; +typedef enum { + SubGhzProtocolBlockAlignBitLeft, + SubGhzProtocolBlockAlignBitRight, +} SubGhzProtocolBlockAlignBit; + /** * Set data bit when encoding HEX array. * @param bit_value The value of the bit to be set @@ -43,10 +52,16 @@ bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_inde * @param upload Pointer to a LevelDuration * @param max_size_upload upload size, check not to overflow * @param duration_bit duration 1 bit + * @param align_bit alignment of useful bits in an array */ -size_t subghz_protocol_blocks_get_upload( +size_t subghz_protocol_blocks_get_upload_from_bit_array( uint8_t data_array[], size_t count_bit_data_array, LevelDuration* upload, size_t max_size_upload, - uint32_t duration_bit); + uint32_t duration_bit, + SubGhzProtocolBlockAlignBit align_bit); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/subghz/blocks/generic.c b/lib/subghz/blocks/generic.c index 353ff18bfae..f59323da377 100644 --- a/lib/subghz/blocks/generic.c +++ b/lib/subghz/blocks/generic.c @@ -4,7 +4,7 @@ #define TAG "SubGhzBlockGeneric" -void subghz_block_generic_get_preset_name(const char* preset_name, string_t preset_str) { +void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str) { const char* preset_name_temp; if(!strcmp(preset_name, "AM270")) { preset_name_temp = "FuriHalSubGhzPresetOok270Async"; @@ -17,103 +17,135 @@ void subghz_block_generic_get_preset_name(const char* preset_name, string_t pres } else { preset_name_temp = "FuriHalSubGhzPresetCustom"; } - string_set(preset_str, preset_name_temp); + furi_string_set(preset_str, preset_name_temp); } -bool subghz_block_generic_serialize( +SubGhzProtocolStatus subghz_block_generic_serialize( SubGhzBlockGeneric* instance, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(instance); - bool res = false; - string_t temp_str; - string_init(temp_str); + SubGhzProtocolStatus res = SubGhzProtocolStatusError; + FuriString* temp_str; + temp_str = furi_string_alloc(); do { stream_clean(flipper_format_get_raw_stream(flipper_format)); 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; } - subghz_block_generic_get_preset_name(string_get_cstr(preset->name), temp_str); - if(!flipper_format_write_string_cstr(flipper_format, "Preset", string_get_cstr(temp_str))) { + subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); + 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(string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + 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; } uint8_t key_data[sizeof(uint64_t)] = {0}; for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->data >> i * 8) & 0xFF; + key_data[sizeof(uint64_t) - i - 1] = (instance->data >> (i * 8)) & 0xFF; } if(!flipper_format_write_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Unable to add Key"); + res = SubGhzProtocolStatusErrorParserKey; break; } - res = true; + res = SubGhzProtocolStatusOk; } while(false); - string_clear(temp_str); + 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; - string_t temp_str; - string_init(temp_str); + SubGhzProtocolStatus res = SubGhzProtocolStatusError; + FuriString* temp_str; + temp_str = furi_string_alloc(); 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; } - instance->data_count_bit = (uint8_t)temp_data; + instance->data_count_bit = (uint16_t)temp_data; 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); - string_clear(temp_str); + 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 56a7fc2d33b..bc26a54d9f0 100644 --- a/lib/subghz/blocks/generic.h +++ b/lib/subghz/blocks/generic.h @@ -5,17 +5,21 @@ #include #include -#include "furi.h" -#include "furi_hal.h" +#include +#include #include "../types.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct SubGhzBlockGeneric SubGhzBlockGeneric; struct SubGhzBlockGeneric { const char* protocol_name; uint64_t data; uint32_t serial; - uint8_t data_count_bit; + uint16_t data_count_bit; uint8_t btn; uint32_t cnt; }; @@ -25,24 +29,41 @@ struct SubGhzBlockGeneric { * @param preset_name name preset * @param preset_str Output name preset */ -void subghz_block_generic_get_preset_name(const char* preset_name, string_t preset_str); +void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str); /** * Serialize data SubGhzBlockGeneric. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return Status Error */ -bool subghz_block_generic_serialize( +SubGhzProtocolStatus subghz_block_generic_serialize( SubGhzBlockGeneric* instance, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzBlockGeneric. + * @param instance Pointer to a SubGhzBlockGeneric instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return Status Error + */ +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 - * @return true On success + * @param count_bit Count bit protocol + * @return Status Error */ -bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format); +SubGhzProtocolStatus subghz_block_generic_deserialize_check_count_bit( + SubGhzBlockGeneric* instance, + FlipperFormat* flipper_format, + uint16_t count_bit); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/subghz/blocks/math.c b/lib/subghz/blocks/math.c index fca50c8f8df..24202ad1c62 100644 --- a/lib/subghz/blocks/math.c +++ b/lib/subghz/blocks/math.c @@ -1,17 +1,244 @@ #include "math.h" -uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t count_bit) { - uint64_t key_reverse = 0; - for(uint8_t i = 0; i < count_bit; i++) { - key_reverse = key_reverse << 1 | bit_read(key, i); +uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count) { + uint64_t reverse_key = 0; + for(uint8_t i = 0; i < bit_count; i++) { + reverse_key = reverse_key << 1 | bit_read(key, i); } - return key_reverse; + return reverse_key; } -uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit) { +uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count) { uint8_t parity = 0; - for(uint8_t i = 0; i < count_bit; i++) { + for(uint8_t i = 0; i < bit_count; i++) { parity += bit_read(key, i); } return parity & 0x01; +} + +uint8_t subghz_protocol_blocks_crc4( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init << 4; // LSBs are unused + uint8_t poly = polynomial << 4; + uint8_t bit; + + while(size--) { + remainder ^= *message++; + for(bit = 0; bit < 8; bit++) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ poly; + } else { + remainder = (remainder << 1); + } + } + } + return remainder >> 4 & 0x0f; // discard the LSBs +} + +uint8_t subghz_protocol_blocks_crc7( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init << 1; // LSB is unused + uint8_t poly = polynomial << 1; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ poly; + } else { + remainder = (remainder << 1); + } + } + } + return remainder >> 1 & 0x7f; // discard the LSB +} + +uint8_t subghz_protocol_blocks_crc8( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ polynomial; + } else { + remainder = (remainder << 1); + } + } + } + return remainder; +} + +uint8_t subghz_protocol_blocks_crc8le( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = subghz_protocol_blocks_reverse_key(init, 8); + polynomial = subghz_protocol_blocks_reverse_key(polynomial, 8); + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 1) { + remainder = (remainder >> 1) ^ polynomial; + } else { + remainder = (remainder >> 1); + } + } + } + return remainder; +} + +uint16_t subghz_protocol_blocks_crc16lsb( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init) { + uint16_t remainder = init; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 1) { + remainder = (remainder >> 1) ^ polynomial; + } else { + remainder = (remainder >> 1); + } + } + } + return remainder; +} + +uint16_t subghz_protocol_blocks_crc16( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init) { + uint16_t remainder = init; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte] << 8; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 0x8000) { + remainder = (remainder << 1) ^ polynomial; + } else { + remainder = (remainder << 1); + } + } + } + return remainder; +} + +uint8_t subghz_protocol_blocks_lfsr_digest8( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key) { + uint8_t sum = 0; + for(size_t byte = 0; byte < size; ++byte) { + uint8_t data = message[byte]; + for(int i = 7; i >= 0; --i) { + // XOR key into sum if data bit is set + if((data >> i) & 1) sum ^= key; + + // roll the key right (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped LSB as MSB) + if(key & 1) + key = (key >> 1) ^ gen; + else + key = (key >> 1); + } + } + return sum; +} + +uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key) { + uint8_t sum = 0; + // Process message from last byte to first byte (reflected) + for(int byte = size - 1; byte >= 0; --byte) { + uint8_t data = message[byte]; + // Process individual bits of each byte (reflected) + for(uint8_t i = 0; i < 8; ++i) { + // XOR key into sum if data bit is set + if((data >> i) & 1) { + sum ^= key; + } + + // roll the key left (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped lsb as MSB) + if(key & 0x80) + key = (key << 1) ^ gen; + else + key = (key << 1); + } + } + return sum; +} + +uint16_t subghz_protocol_blocks_lfsr_digest16( + uint8_t const message[], + size_t size, + uint16_t gen, + uint16_t key) { + uint16_t sum = 0; + for(size_t byte = 0; byte < size; ++byte) { + uint8_t data = message[byte]; + for(int8_t i = 7; i >= 0; --i) { + // if data bit is set then xor with key + if((data >> i) & 1) sum ^= key; + + // roll the key right (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped LSB as MSB) + if(key & 1) + key = (key >> 1) ^ gen; + else + key = (key >> 1); + } + } + return sum; +} + +uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size) { + uint32_t result = 0; + for(size_t i = 0; i < size; ++i) { + result += message[i]; + } + return (uint8_t)result; +} + +uint8_t subghz_protocol_blocks_parity8(uint8_t byte) { + byte ^= byte >> 4; + byte &= 0xf; + return (0x6996 >> byte) & 1; +} + +uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size) { + uint8_t result = 0; + for(size_t i = 0; i < size; ++i) { + result ^= subghz_protocol_blocks_parity8(message[i]); + } + return result; +} + +uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size) { + uint8_t result = 0; + for(size_t i = 0; i < size; ++i) { + result ^= message[i]; + } + return result; } \ No newline at end of file diff --git a/lib/subghz/blocks/math.h b/lib/subghz/blocks/math.h index 85b146ebc7e..dcea3da5faf 100644 --- a/lib/subghz/blocks/math.h +++ b/lib/subghz/blocks/math.h @@ -5,23 +5,218 @@ #include #define bit_read(value, bit) (((value) >> (bit)) & 0x01) -#define bit_set(value, bit) ((value) |= (1UL << (bit))) -#define bit_clear(value, bit) ((value) &= ~(1UL << (bit))) +#define bit_set(value, bit) \ + ({ \ + __typeof__(value) _one = (1); \ + (value) |= (_one << (bit)); \ + }) +#define bit_clear(value, bit) \ + ({ \ + __typeof__(value) _one = (1); \ + (value) &= ~(_one << (bit)); \ + }) #define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit)) -#define DURATION_DIFF(x, y) ((x < y) ? (y - x) : (x - y)) +#define DURATION_DIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y))) -/** - * Flip the data bitwise. - * @param key In data - * @param count_bit number of data bits - * @return Reverse data +#ifdef __cplusplus +extern "C" { +#endif + +/** Flip the data bitwise + * + * @param key In data + * @param bit_count number of data bits + * + * @return Reverse data + */ +uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count); + +/** Get parity the data bitwise + * + * @param key In data + * @param bit_count number of data bits + * + * @return parity + */ +uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count); + +/** CRC-4 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc4( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** CRC-7 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc7( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** Generic Cyclic Redundancy Check CRC-8. Example polynomial: 0x31 = x8 + x5 + + * x4 + 1 (x8 is implicit) Example polynomial: 0x80 = x8 + x7 (a normal + * bit-by-bit parity XOR) + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial byte is from x^7 to x^0 (x^8 is implicitly one) + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc8( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** "Little-endian" Cyclic Redundancy Check CRC-8 LE Input and output are + * reflected, i.e. least significant bit is shifted in first + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc8le( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** CRC-16 LSB. Input and output are reflected, i.e. least significant bit is + * shifted in first. Note that poly and init already need to be reflected + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint16_t subghz_protocol_blocks_crc16lsb( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init); + +/** CRC-16 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint16_t subghz_protocol_blocks_crc16( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init); + +/** Digest-8 by "LFSR-based Toeplitz hash" + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ +uint8_t subghz_protocol_blocks_lfsr_digest8( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key); + +/** Digest-8 by "LFSR-based Toeplitz hash", byte reflect, bit reflect + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ +uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key); + +/** Digest-16 by "LFSR-based Toeplitz hash" + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value */ -uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t count_bit); +uint16_t subghz_protocol_blocks_lfsr_digest16( + uint8_t const message[], + size_t size, + uint16_t gen, + uint16_t key); -/** - * Get parity the data bitwise. - * @param key In data - * @param count_bit number of data bits - * @return parity +/** Compute Addition of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return summation value */ -uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit); +uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size); + +/** Compute bit parity of a single byte (8 bits) + * + * @param byte single byte to check + * + * @return 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_parity8(uint8_t byte); + +/** Compute bit parity of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size); + +/** Compute XOR (byte-wide parity) of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return summation value, per bit-position 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/devices/cc1101_configs.c b/lib/subghz/devices/cc1101_configs.c new file mode 100644 index 00000000000..35ddccd54f6 --- /dev/null +++ b/lib/subghz/devices/cc1101_configs.c @@ -0,0 +1,431 @@ +#include "cc1101_configs.h" +#include + +const uint8_t subghz_device_cc1101_preset_ook_270khz_async_regs[] = { + // https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration + + /* GPIO GD0 */ + CC1101_IOCFG0, + 0x0D, // GD0 as async serial data output/input + + /* FIFO and internals */ + CC1101_FIFOTHR, + 0x47, // The only important bit is ADC_RETENTION, FIFO Tx=33 Rx=32 + + /* Packet engine */ + CC1101_PKTCTRL0, + 0x32, // Async, continious, no whitening + + /* Frequency Synthesizer Control */ + CC1101_FSCTRL1, + 0x06, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz + + // 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, + 0x32, // Data rate is 3.79372 kBaud + CC1101_MDMCFG4, + 0x67, // Rx BW filter is 270.833333kHz + + /* 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, + 0x18, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off + + /* Automatic Gain Control */ + CC1101_AGCCTRL0, + 0x40, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary + CC1101_AGCCTRL1, + 0x00, // 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, + 0x03, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 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, + 0x11, // Adjusts current TX LO buffer + high is PATABLE[1] + CC1101_FREND1, + 0xB6, // + + /* End load reg */ + 0, + 0, + + //ook_async_patable[8] + 0x00, + 0xC0, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +const uint8_t subghz_device_cc1101_preset_ook_650khz_async_regs[] = { + // https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration + + /* 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 + + // 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, + 0x32, // Data rate is 3.79372 kBaud + CC1101_MDMCFG4, + 0x17, // 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, + 0x18, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off + + /* Automatic Gain Control */ + // CC1101_AGCTRL0,0x40, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary + // CC1101_AGCTRL1,0x00, // 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, 0x03, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + //MAGN_TARGET for RX filter BW =< 100 kHz is 0x3. For higher RX filter BW's MAGN_TARGET is 0x7. + 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, + 0x11, // Adjusts current TX LO buffer + high is PATABLE[1] + CC1101_FREND1, + 0xB6, // + + /* End load reg */ + 0, + 0, + + //ook_async_patable[8] + 0x00, + 0xC0, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +const uint8_t subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs[] = { + + /* 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, + CC1101_MDMCFG2, + 0x04, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) + CC1101_MDMCFG3, + 0x83, // Data rate is 4.79794 kBaud + CC1101_MDMCFG4, + 0x67, //Rx BW filter is 270.833333 kHz + CC1101_DEVIATN, + 0x04, //Deviation 2.380371 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, + 0x91, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + CC1101_AGCCTRL1, + 0x00, // 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, + + /* End load reg */ + 0, + 0, + + // 2fsk_async_patable[8] + 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +const uint8_t subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs[] = { + + /* 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, + CC1101_MDMCFG2, + 0x04, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) + CC1101_MDMCFG3, + 0x83, // Data rate is 4.79794 kBaud + CC1101_MDMCFG4, + 0x67, //Rx BW filter is 270.833333 kHz + CC1101_DEVIATN, + 0x47, //Deviation 47.60742 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, + 0x91, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + CC1101_AGCCTRL1, + 0x00, // 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, + + /* End load reg */ + 0, + 0, + + // 2fsk_async_patable[8] + 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +const uint8_t subghz_device_cc1101_preset_msk_99_97kb_async_regs[] = { + /* GPIO GD0 */ + CC1101_IOCFG0, + 0x06, + + CC1101_FIFOTHR, + 0x07, // The only important bit is ADC_RETENTION + CC1101_SYNC1, + 0x46, + CC1101_SYNC0, + 0x4C, + CC1101_ADDR, + 0x00, + CC1101_PKTLEN, + 0x00, + CC1101_CHANNR, + 0x00, + + CC1101_PKTCTRL0, + 0x05, + + CC1101_FSCTRL0, + 0x23, + CC1101_FSCTRL1, + 0x06, + + CC1101_MDMCFG0, + 0xF8, + CC1101_MDMCFG1, + 0x22, + CC1101_MDMCFG2, + 0x72, + CC1101_MDMCFG3, + 0xF8, + CC1101_MDMCFG4, + 0x5B, + CC1101_DEVIATN, + 0x47, + + CC1101_MCSM0, + 0x18, + CC1101_FOCCFG, + 0x16, + + CC1101_AGCCTRL0, + 0xB2, + CC1101_AGCCTRL1, + 0x00, + CC1101_AGCCTRL2, + 0xC7, + + CC1101_FREND0, + 0x10, + CC1101_FREND1, + 0x56, + + CC1101_BSCFG, + 0x1C, + CC1101_FSTEST, + 0x59, + + /* End load reg */ + 0, + 0, + + // msk_async_patable[8] + 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +const uint8_t subghz_device_cc1101_preset_gfsk_9_99kb_async_regs[] = { + + CC1101_IOCFG0, + 0x06, //GDO0 Output Pin Configuration + CC1101_FIFOTHR, + 0x47, //RX FIFO and TX FIFO Thresholds + + //1 : CRC calculation in TX and CRC check in RX enabled, + //1 : Variable packet length mode. Packet length configured by the first byte after sync word + CC1101_PKTCTRL0, + 0x05, + + CC1101_FSCTRL1, + 0x06, //Frequency Synthesizer Control + + CC1101_SYNC1, + 0x46, + CC1101_SYNC0, + 0x4C, + CC1101_ADDR, + 0x00, + CC1101_PKTLEN, + 0x00, + + CC1101_MDMCFG4, + 0xC8, //Modem Configuration 9.99 + CC1101_MDMCFG3, + 0x93, //Modem Configuration + CC1101_MDMCFG2, + 0x12, // 2: 16/16 sync word bits detected + + CC1101_DEVIATN, + 0x34, //Deviation = 19.042969 + CC1101_MCSM0, + 0x18, //Main Radio Control State Machine Configuration + CC1101_FOCCFG, + 0x16, //Frequency Offset Compensation Configuration + + CC1101_AGCCTRL2, + 0x43, //AGC Control + CC1101_AGCCTRL1, + 0x40, + CC1101_AGCCTRL0, + 0x91, + + CC1101_WORCTRL, + 0xFB, //Wake On Radio Control + + /* End load reg */ + 0, + 0, + + // gfsk_async_patable[8] + 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; diff --git a/lib/subghz/devices/cc1101_configs.h b/lib/subghz/devices/cc1101_configs.h new file mode 100644 index 00000000000..eecab01d999 --- /dev/null +++ b/lib/subghz/devices/cc1101_configs.h @@ -0,0 +1,18 @@ +#pragma once +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const uint8_t subghz_device_cc1101_preset_ook_270khz_async_regs[]; +extern const uint8_t subghz_device_cc1101_preset_ook_650khz_async_regs[]; +extern const uint8_t subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs[]; +extern const uint8_t subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs[]; +extern const uint8_t subghz_device_cc1101_preset_msk_99_97kb_async_regs[]; +extern const uint8_t subghz_device_cc1101_preset_gfsk_9_99kb_async_regs[]; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.c b/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.c new file mode 100644 index 00000000000..284c717fd13 --- /dev/null +++ b/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.c @@ -0,0 +1,96 @@ +#include "cc1101_int_interconnect.h" +#include +#include "../cc1101_configs.h" + +#define TAG "SubGhzDeviceCc1101Int" + +static bool subghz_device_cc1101_int_interconnect_is_frequency_valid(uint32_t frequency) { + bool ret = furi_hal_subghz_is_frequency_valid(frequency); + if(!ret) { + furi_crash("SubGhz: Incorrect frequency."); + } + return ret; +} + +static uint32_t subghz_device_cc1101_int_interconnect_set_frequency(uint32_t frequency) { + subghz_device_cc1101_int_interconnect_is_frequency_valid(frequency); + return furi_hal_subghz_set_frequency_and_path(frequency); +} + +static bool subghz_device_cc1101_int_interconnect_start_async_tx(void* callback, void* context) { + return furi_hal_subghz_start_async_tx((FuriHalSubGhzAsyncTxCallback)callback, context); +} + +static void subghz_device_cc1101_int_interconnect_start_async_rx(void* callback, void* context) { + furi_hal_subghz_start_async_rx((FuriHalSubGhzCaptureCallback)callback, context); +} + +static void subghz_device_cc1101_int_interconnect_load_preset( + FuriHalSubGhzPreset preset, + uint8_t* preset_data) { + switch(preset) { + case FuriHalSubGhzPresetOok650Async: + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); + break; + case FuriHalSubGhzPresetOok270Async: + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_270khz_async_regs); + break; + case FuriHalSubGhzPreset2FSKDev238Async: + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs); + break; + case FuriHalSubGhzPreset2FSKDev476Async: + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs); + break; + case FuriHalSubGhzPresetMSK99_97KbAsync: + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_msk_99_97kb_async_regs); + break; + case FuriHalSubGhzPresetGFSK9_99KbAsync: + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_gfsk_9_99kb_async_regs); + break; + + default: + furi_hal_subghz_load_custom_preset(preset_data); + } +} + +static bool subghz_device_cc1101_int_interconnect_is_connect(void) { + return true; +} + +const SubGhzDeviceInterconnect subghz_device_cc1101_int_interconnect = { + .begin = NULL, + .end = furi_hal_subghz_shutdown, + .is_connect = subghz_device_cc1101_int_interconnect_is_connect, + .reset = furi_hal_subghz_reset, + .sleep = furi_hal_subghz_sleep, + .idle = furi_hal_subghz_idle, + .load_preset = subghz_device_cc1101_int_interconnect_load_preset, + .set_frequency = subghz_device_cc1101_int_interconnect_set_frequency, + .is_frequency_valid = furi_hal_subghz_is_frequency_valid, + .set_async_mirror_pin = furi_hal_subghz_set_async_mirror_pin, + .get_data_gpio = furi_hal_subghz_get_data_gpio, + + .set_tx = furi_hal_subghz_tx, + .flush_tx = furi_hal_subghz_flush_tx, + .start_async_tx = subghz_device_cc1101_int_interconnect_start_async_tx, + .is_async_complete_tx = furi_hal_subghz_is_async_tx_complete, + .stop_async_tx = furi_hal_subghz_stop_async_tx, + + .set_rx = furi_hal_subghz_rx, + .flush_rx = furi_hal_subghz_flush_rx, + .start_async_rx = subghz_device_cc1101_int_interconnect_start_async_rx, + .stop_async_rx = furi_hal_subghz_stop_async_rx, + + .get_rssi = furi_hal_subghz_get_rssi, + .get_lqi = furi_hal_subghz_get_lqi, + + .rx_pipe_not_empty = furi_hal_subghz_rx_pipe_not_empty, + .is_rx_data_crc_valid = furi_hal_subghz_is_rx_data_crc_valid, + .read_packet = furi_hal_subghz_read_packet, + .write_packet = furi_hal_subghz_write_packet, +}; + +const SubGhzDevice subghz_device_cc1101_int = { + .name = SUBGHZ_DEVICE_CC1101_INT_NAME, + .interconnect = &subghz_device_cc1101_int_interconnect, +}; diff --git a/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h b/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h new file mode 100644 index 00000000000..629d1264c09 --- /dev/null +++ b/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h @@ -0,0 +1,8 @@ +#pragma once +#include "../types.h" + +#define SUBGHZ_DEVICE_CC1101_INT_NAME "cc1101_int" + +typedef struct SubGhzDeviceCC1101Int SubGhzDeviceCC1101Int; + +extern const SubGhzDevice subghz_device_cc1101_int; diff --git a/lib/subghz/devices/device_registry.h b/lib/subghz/devices/device_registry.h new file mode 100644 index 00000000000..70a0db4b265 --- /dev/null +++ b/lib/subghz/devices/device_registry.h @@ -0,0 +1,13 @@ +#pragma once + +#include "registry.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const SubGhzDeviceRegistry subghz_device_registry; + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/devices/devices.c b/lib/subghz/devices/devices.c new file mode 100644 index 00000000000..55db61e1188 --- /dev/null +++ b/lib/subghz/devices/devices.c @@ -0,0 +1,236 @@ +#include "devices.h" + +#include "registry.h" + +void subghz_devices_init() { + furi_check(!subghz_device_registry_is_valid()); + subghz_device_registry_init(); +} + +void subghz_devices_deinit(void) { + furi_check(subghz_device_registry_is_valid()); + subghz_device_registry_deinit(); +} + +const SubGhzDevice* subghz_devices_get_by_name(const char* device_name) { + furi_check(subghz_device_registry_is_valid()); + const SubGhzDevice* device = subghz_device_registry_get_by_name(device_name); + return device; +} + +const char* subghz_devices_get_name(const SubGhzDevice* device) { + const char* ret = NULL; + if(device) { + ret = device->name; + } + return ret; +} + +bool subghz_devices_begin(const SubGhzDevice* device) { + bool ret = false; + furi_assert(device); + if(device->interconnect->begin) { + ret = device->interconnect->begin(); + } + return ret; +} + +void subghz_devices_end(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->end) { + device->interconnect->end(); + } +} + +bool subghz_devices_is_connect(const SubGhzDevice* device) { + bool ret = false; + furi_assert(device); + if(device->interconnect->is_connect) { + ret = device->interconnect->is_connect(); + } + return ret; +} + +void subghz_devices_reset(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->reset) { + device->interconnect->reset(); + } +} + +void subghz_devices_sleep(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->sleep) { + device->interconnect->sleep(); + } +} + +void subghz_devices_idle(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->idle) { + device->interconnect->idle(); + } +} + +void subghz_devices_load_preset( + const SubGhzDevice* device, + FuriHalSubGhzPreset preset, + uint8_t* preset_data) { + furi_assert(device); + if(device->interconnect->load_preset) { + device->interconnect->load_preset(preset, preset_data); + } +} + +uint32_t subghz_devices_set_frequency(const SubGhzDevice* device, uint32_t frequency) { + uint32_t ret = 0; + furi_assert(device); + if(device->interconnect->set_frequency) { + ret = device->interconnect->set_frequency(frequency); + } + return ret; +} + +bool subghz_devices_is_frequency_valid(const SubGhzDevice* device, uint32_t frequency) { + bool ret = false; + furi_assert(device); + if(device->interconnect->is_frequency_valid) { + ret = device->interconnect->is_frequency_valid(frequency); + } + return ret; +} + +void subghz_devices_set_async_mirror_pin(const SubGhzDevice* device, const GpioPin* gpio) { + furi_assert(device); + if(device->interconnect->set_async_mirror_pin) { + device->interconnect->set_async_mirror_pin(gpio); + } +} + +const GpioPin* subghz_devices_get_data_gpio(const SubGhzDevice* device) { + const GpioPin* ret = NULL; + furi_assert(device); + if(device->interconnect->get_data_gpio) { + ret = device->interconnect->get_data_gpio(); + } + return ret; +} + +bool subghz_devices_set_tx(const SubGhzDevice* device) { + bool ret = 0; + furi_assert(device); + if(device->interconnect->set_tx) { + ret = device->interconnect->set_tx(); + } + return ret; +} + +void subghz_devices_flush_tx(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->flush_tx) { + device->interconnect->flush_tx(); + } +} + +bool subghz_devices_start_async_tx(const SubGhzDevice* device, void* callback, void* context) { + bool ret = false; + furi_assert(device); + if(device->interconnect->start_async_tx) { + ret = device->interconnect->start_async_tx(callback, context); + } + return ret; +} + +bool subghz_devices_is_async_complete_tx(const SubGhzDevice* device) { + bool ret = false; + furi_assert(device); + if(device->interconnect->is_async_complete_tx) { + ret = device->interconnect->is_async_complete_tx(); + } + return ret; +} + +void subghz_devices_stop_async_tx(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->stop_async_tx) { + device->interconnect->stop_async_tx(); + } +} + +void subghz_devices_set_rx(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->set_rx) { + device->interconnect->set_rx(); + } +} + +void subghz_devices_flush_rx(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->flush_rx) { + device->interconnect->flush_rx(); + } +} + +void subghz_devices_start_async_rx(const SubGhzDevice* device, void* callback, void* context) { + furi_assert(device); + if(device->interconnect->start_async_rx) { + device->interconnect->start_async_rx(callback, context); + } +} + +void subghz_devices_stop_async_rx(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->stop_async_rx) { + device->interconnect->stop_async_rx(); + } +} + +float subghz_devices_get_rssi(const SubGhzDevice* device) { + float ret = 0; + furi_assert(device); + if(device->interconnect->get_rssi) { + ret = device->interconnect->get_rssi(); + } + return ret; +} + +uint8_t subghz_devices_get_lqi(const SubGhzDevice* device) { + uint8_t ret = 0; + furi_assert(device); + if(device->interconnect->get_lqi) { + ret = device->interconnect->get_lqi(); + } + return ret; +} + +bool subghz_devices_rx_pipe_not_empty(const SubGhzDevice* device) { + bool ret = false; + furi_assert(device); + if(device->interconnect->rx_pipe_not_empty) { + ret = device->interconnect->rx_pipe_not_empty(); + } + return ret; +} + +bool subghz_devices_is_rx_data_crc_valid(const SubGhzDevice* device) { + bool ret = false; + furi_assert(device); + if(device->interconnect->is_rx_data_crc_valid) { + ret = device->interconnect->is_rx_data_crc_valid(); + } + return ret; +} + +void subghz_devices_read_packet(const SubGhzDevice* device, uint8_t* data, uint8_t* size) { + furi_assert(device); + if(device->interconnect->read_packet) { + device->interconnect->read_packet(data, size); + } +} + +void subghz_devices_write_packet(const SubGhzDevice* device, const uint8_t* data, uint8_t size) { + furi_assert(device); + if(device->interconnect->write_packet) { + device->interconnect->write_packet(data, size); + } +} diff --git a/lib/subghz/devices/devices.h b/lib/subghz/devices/devices.h new file mode 100644 index 00000000000..dad3c9aeb53 --- /dev/null +++ b/lib/subghz/devices/devices.h @@ -0,0 +1,52 @@ +#pragma once + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzDevice SubGhzDevice; + +void subghz_devices_init(); +void subghz_devices_deinit(void); + +const SubGhzDevice* subghz_devices_get_by_name(const char* device_name); +const char* subghz_devices_get_name(const SubGhzDevice* device); +bool subghz_devices_begin(const SubGhzDevice* device); +void subghz_devices_end(const SubGhzDevice* device); +bool subghz_devices_is_connect(const SubGhzDevice* device); +void subghz_devices_reset(const SubGhzDevice* device); +void subghz_devices_sleep(const SubGhzDevice* device); +void subghz_devices_idle(const SubGhzDevice* device); +void subghz_devices_load_preset( + const SubGhzDevice* device, + FuriHalSubGhzPreset preset, + uint8_t* preset_data); +uint32_t subghz_devices_set_frequency(const SubGhzDevice* device, uint32_t frequency); +bool subghz_devices_is_frequency_valid(const SubGhzDevice* device, uint32_t frequency); +void subghz_devices_set_async_mirror_pin(const SubGhzDevice* device, const GpioPin* gpio); +const GpioPin* subghz_devices_get_data_gpio(const SubGhzDevice* device); + +bool subghz_devices_set_tx(const SubGhzDevice* device); +void subghz_devices_flush_tx(const SubGhzDevice* device); +bool subghz_devices_start_async_tx(const SubGhzDevice* device, void* callback, void* context); +bool subghz_devices_is_async_complete_tx(const SubGhzDevice* device); +void subghz_devices_stop_async_tx(const SubGhzDevice* device); + +void subghz_devices_set_rx(const SubGhzDevice* device); +void subghz_devices_flush_rx(const SubGhzDevice* device); +void subghz_devices_start_async_rx(const SubGhzDevice* device, void* callback, void* context); +void subghz_devices_stop_async_rx(const SubGhzDevice* device); + +float subghz_devices_get_rssi(const SubGhzDevice* device); +uint8_t subghz_devices_get_lqi(const SubGhzDevice* device); + +bool subghz_devices_rx_pipe_not_empty(const SubGhzDevice* device); +bool subghz_devices_is_rx_data_crc_valid(const SubGhzDevice* device); +void subghz_devices_read_packet(const SubGhzDevice* device, uint8_t* data, uint8_t* size); +void subghz_devices_write_packet(const SubGhzDevice* device, const uint8_t* data, uint8_t size); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/devices/preset.h b/lib/subghz/devices/preset.h new file mode 100644 index 00000000000..8716f2e233c --- /dev/null +++ b/lib/subghz/devices/preset.h @@ -0,0 +1,13 @@ +#pragma once + +/** Radio Presets */ +typedef enum { + FuriHalSubGhzPresetIDLE, /**< default configuration */ + FuriHalSubGhzPresetOok270Async, /**< OOK, bandwidth 270kHz, asynchronous */ + FuriHalSubGhzPresetOok650Async, /**< OOK, bandwidth 650kHz, asynchronous */ + FuriHalSubGhzPreset2FSKDev238Async, /**< FM, deviation 2.380371 kHz, asynchronous */ + FuriHalSubGhzPreset2FSKDev476Async, /**< FM, deviation 47.60742 kHz, asynchronous */ + FuriHalSubGhzPresetMSK99_97KbAsync, /**< MSK, deviation 47.60742 kHz, 99.97Kb/s, asynchronous */ + FuriHalSubGhzPresetGFSK9_99KbAsync, /**< GFSK, deviation 19.042969 kHz, 9.996Kb/s, asynchronous */ + FuriHalSubGhzPresetCustom, /**Custom Preset*/ +} FuriHalSubGhzPreset; diff --git a/lib/subghz/devices/registry.c b/lib/subghz/devices/registry.c new file mode 100644 index 00000000000..779ba81d7a6 --- /dev/null +++ b/lib/subghz/devices/registry.c @@ -0,0 +1,76 @@ +#include "registry.h" + +#include "cc1101_int/cc1101_int_interconnect.h" +#include +#include + +#define TAG "SubGhzDeviceRegistry" + +struct SubGhzDeviceRegistry { + const SubGhzDevice** items; + size_t size; + PluginManager* manager; +}; + +static SubGhzDeviceRegistry* subghz_device_registry = NULL; + +void subghz_device_registry_init(void) { + SubGhzDeviceRegistry* subghz_device = + (SubGhzDeviceRegistry*)malloc(sizeof(SubGhzDeviceRegistry)); + subghz_device->manager = plugin_manager_alloc( + SUBGHZ_RADIO_DEVICE_PLUGIN_APP_ID, + SUBGHZ_RADIO_DEVICE_PLUGIN_API_VERSION, + firmware_api_interface); + + //TODO FL-3556: fix path to plugins + if(plugin_manager_load_all(subghz_device->manager, "/any/apps_data/subghz/plugins") != + //if(plugin_manager_load_all(subghz_device->manager, APP_DATA_PATH("plugins")) != + PluginManagerErrorNone) { + FURI_LOG_E(TAG, "Failed to load all libs"); + } + + subghz_device->size = plugin_manager_get_count(subghz_device->manager) + 1; + subghz_device->items = + (const SubGhzDevice**)malloc(sizeof(SubGhzDevice*) * subghz_device->size); + subghz_device->items[0] = &subghz_device_cc1101_int; + for(uint32_t i = 1; i < subghz_device->size; i++) { + const SubGhzDevice* plugin = plugin_manager_get_ep(subghz_device->manager, i - 1); + subghz_device->items[i] = plugin; + } + + FURI_LOG_I(TAG, "Loaded %zu radio device", subghz_device->size); + subghz_device_registry = subghz_device; +} + +void subghz_device_registry_deinit(void) { + plugin_manager_free(subghz_device_registry->manager); + free(subghz_device_registry->items); + free(subghz_device_registry); + subghz_device_registry = NULL; +} + +bool subghz_device_registry_is_valid(void) { + return subghz_device_registry != NULL; +} + +const SubGhzDevice* subghz_device_registry_get_by_name(const char* name) { + furi_assert(subghz_device_registry); + + if(name != NULL) { + for(size_t i = 0; i < subghz_device_registry->size; i++) { + if(strcmp(name, subghz_device_registry->items[i]->name) == 0) { + return subghz_device_registry->items[i]; + } + } + } + return NULL; +} + +const SubGhzDevice* subghz_device_registry_get_by_index(size_t index) { + furi_assert(subghz_device_registry); + if(index < subghz_device_registry->size) { + return subghz_device_registry->items[index]; + } else { + return NULL; + } +} diff --git a/lib/subghz/devices/registry.h b/lib/subghz/devices/registry.h new file mode 100644 index 00000000000..5200589205b --- /dev/null +++ b/lib/subghz/devices/registry.h @@ -0,0 +1,40 @@ +#pragma once + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzDevice SubGhzDevice; + +void subghz_device_registry_init(void); + +void subghz_device_registry_deinit(void); + +bool subghz_device_registry_is_valid(void); + +/** + * Registration by name SubGhzDevice. + * @param name SubGhzDevice name + * @return SubGhzDevice* pointer to a SubGhzDevice instance + */ +const SubGhzDevice* subghz_device_registry_get_by_name(const char* name); + +/** + * Registration subghzdevice by index in array SubGhzDevice. + * @param index SubGhzDevice by index in array + * @return SubGhzDevice* pointer to a SubGhzDevice instance + */ +const SubGhzDevice* subghz_device_registry_get_by_index(size_t index); + +/** + * Getting the number of registered subghzdevices. + * @param subghz_device SubGhzDeviceRegistry + * @return Number of subghzdevices + */ +size_t subghz_device_registry_count(void); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/devices/types.h b/lib/subghz/devices/types.h new file mode 100644 index 00000000000..8a41984263f --- /dev/null +++ b/lib/subghz/devices/types.h @@ -0,0 +1,91 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include "preset.h" + +#include + +#define SUBGHZ_RADIO_DEVICE_PLUGIN_APP_ID "subghz_radio_device" +#define SUBGHZ_RADIO_DEVICE_PLUGIN_API_VERSION 1 + +typedef struct SubGhzDeviceRegistry SubGhzDeviceRegistry; +typedef struct SubGhzDevice SubGhzDevice; + +typedef bool (*SubGhzBegin)(void); +typedef void (*SubGhzEnd)(void); +typedef bool (*SubGhzIsConnect)(void); +typedef void (*SubGhzReset)(void); +typedef void (*SubGhzSleep)(void); +typedef void (*SubGhzIdle)(void); +typedef void (*SubGhzLoadPreset)(FuriHalSubGhzPreset preset, uint8_t* preset_data); +typedef uint32_t (*SubGhzSetFrequency)(uint32_t frequency); +typedef bool (*SubGhzIsFrequencyValid)(uint32_t frequency); + +typedef void (*SubGhzSetAsyncMirrorPin)(const GpioPin* gpio); +typedef const GpioPin* (*SubGhzGetDataGpio)(void); + +typedef bool (*SubGhzSetTx)(void); +typedef void (*SubGhzFlushTx)(void); +typedef bool (*SubGhzStartAsyncTx)(void* callback, void* context); +typedef bool (*SubGhzIsAsyncCompleteTx)(void); +typedef void (*SubGhzStopAsyncTx)(void); + +typedef void (*SubGhzSetRx)(void); +typedef void (*SubGhzFlushRx)(void); +typedef void (*SubGhzStartAsyncRx)(void* callback, void* context); +typedef void (*SubGhzStopAsyncRx)(void); + +typedef float (*SubGhzGetRSSI)(void); +typedef uint8_t (*SubGhzGetLQI)(void); + +typedef bool (*SubGhzRxPipeNotEmpty)(void); +typedef bool (*SubGhzRxIsDataCrcValid)(void); +typedef void (*SubGhzReadPacket)(uint8_t* data, uint8_t* size); +typedef void (*SubGhzWritePacket)(const uint8_t* data, uint8_t size); + +typedef struct { + SubGhzBegin begin; + SubGhzEnd end; + + SubGhzIsConnect is_connect; + SubGhzReset reset; + SubGhzSleep sleep; + SubGhzIdle idle; + + SubGhzLoadPreset load_preset; + SubGhzSetFrequency set_frequency; + SubGhzIsFrequencyValid is_frequency_valid; + SubGhzSetAsyncMirrorPin set_async_mirror_pin; + SubGhzGetDataGpio get_data_gpio; + + SubGhzSetTx set_tx; + SubGhzFlushTx flush_tx; + SubGhzStartAsyncTx start_async_tx; + SubGhzIsAsyncCompleteTx is_async_complete_tx; + SubGhzStopAsyncTx stop_async_tx; + + SubGhzSetRx set_rx; + SubGhzFlushRx flush_rx; + SubGhzStartAsyncRx start_async_rx; + SubGhzStopAsyncRx stop_async_rx; + + SubGhzGetRSSI get_rssi; + SubGhzGetLQI get_lqi; + + SubGhzRxPipeNotEmpty rx_pipe_not_empty; + SubGhzRxIsDataCrcValid is_rx_data_crc_valid; + SubGhzReadPacket read_packet; + SubGhzWritePacket write_packet; + +} SubGhzDeviceInterconnect; + +struct SubGhzDevice { + const char* name; + const SubGhzDeviceInterconnect* interconnect; +}; diff --git a/lib/subghz/environment.c b/lib/subghz/environment.c index 46d0d368c5b..3794dbad841 100644 --- a/lib/subghz/environment.c +++ b/lib/subghz/environment.c @@ -1,17 +1,22 @@ #include "environment.h" +#include "registry.h" struct SubGhzEnvironment { SubGhzKeystore* keystore; + const SubGhzProtocolRegistry* protocol_registry; const char* came_atomo_rainbow_table_file_name; const char* nice_flor_s_rainbow_table_file_name; + const char* alutech_at_4n_rainbow_table_file_name; }; SubGhzEnvironment* subghz_environment_alloc() { SubGhzEnvironment* instance = malloc(sizeof(SubGhzEnvironment)); instance->keystore = subghz_keystore_alloc(); + instance->protocol_registry = NULL; instance->came_atomo_rainbow_table_file_name = NULL; instance->nice_flor_s_rainbow_table_file_name = NULL; + instance->alutech_at_4n_rainbow_table_file_name = NULL; return instance; } @@ -19,6 +24,10 @@ SubGhzEnvironment* subghz_environment_alloc() { void subghz_environment_free(SubGhzEnvironment* instance) { furi_assert(instance); + instance->protocol_registry = NULL; + instance->came_atomo_rainbow_table_file_name = NULL; + instance->nice_flor_s_rainbow_table_file_name = NULL; + instance->alutech_at_4n_rainbow_table_file_name = NULL; subghz_keystore_free(instance->keystore); free(instance); @@ -51,6 +60,21 @@ const char* return instance->came_atomo_rainbow_table_file_name; } +void subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename) { + furi_assert(instance); + + instance->alutech_at_4n_rainbow_table_file_name = filename; +} + +const char* + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance) { + furi_assert(instance); + + return instance->alutech_at_4n_rainbow_table_file_name; +} + void subghz_environment_set_nice_flor_s_rainbow_table_file_name( SubGhzEnvironment* instance, const char* filename) { @@ -65,3 +89,31 @@ const char* return instance->nice_flor_s_rainbow_table_file_name; } + +void subghz_environment_set_protocol_registry( + SubGhzEnvironment* instance, + const SubGhzProtocolRegistry* protocol_registry_items) { + furi_assert(instance); + const SubGhzProtocolRegistry* protocol_registry = protocol_registry_items; + instance->protocol_registry = protocol_registry; +} + +const SubGhzProtocolRegistry* + subghz_environment_get_protocol_registry(SubGhzEnvironment* instance) { + furi_assert(instance); + furi_assert(instance->protocol_registry); + return instance->protocol_registry; +} + +const char* + subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx) { + furi_assert(instance); + furi_assert(instance->protocol_registry); + const SubGhzProtocol* protocol = + subghz_protocol_registry_get_by_index(instance->protocol_registry, idx); + if(protocol != NULL) { + return protocol->name; + } else { + return NULL; + } +} \ No newline at end of file diff --git a/lib/subghz/environment.h b/lib/subghz/environment.h index b8e73e33b5a..c15b8b211b0 100644 --- a/lib/subghz/environment.h +++ b/lib/subghz/environment.h @@ -1,10 +1,16 @@ #pragma once #include +#include "registry.h" #include "subghz_keystore.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct SubGhzEnvironment SubGhzEnvironment; +typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; /** * Allocate SubGhzEnvironment. @@ -22,7 +28,7 @@ void subghz_environment_free(SubGhzEnvironment* instance); * Downloading the manufacture key file. * @param instance Pointer to a SubGhzEnvironment instance * @param filename Full path to the file - * @return true On succes + * @return true On success */ bool subghz_environment_load_keystore(SubGhzEnvironment* instance, const char* filename); @@ -48,6 +54,23 @@ void subghz_environment_set_came_atomo_rainbow_table_file_name( */ const char* subghz_environment_get_came_atomo_rainbow_table_file_name(SubGhzEnvironment* instance); +/** + * Set filename to work with Alutech at-4n. + * @param instance Pointer to a SubGhzEnvironment instance + * @param filename Full path to the file + */ +void subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename); + +/** + * Get filename to work with Alutech at-4n. + * @param instance Pointer to a SubGhzEnvironment instance + * @return Full path to the file + */ +const char* + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance); + /** * Set filename to work with Nice Flor-S. * @param instance Pointer to a SubGhzEnvironment instance @@ -64,3 +87,32 @@ void subghz_environment_set_nice_flor_s_rainbow_table_file_name( */ const char* subghz_environment_get_nice_flor_s_rainbow_table_file_name(SubGhzEnvironment* instance); + +/** + * Set list of protocols to work. + * @param instance Pointer to a SubGhzEnvironment instance + * @param protocol_registry_items Pointer to a SubGhzProtocolRegistry + */ +void subghz_environment_set_protocol_registry( + SubGhzEnvironment* instance, + const SubGhzProtocolRegistry* protocol_registry_items); + +/** + * Get list of protocols to work. + * @param instance Pointer to a SubGhzEnvironment instance + * @return Pointer to a SubGhzProtocolRegistry + */ +const SubGhzProtocolRegistry* + subghz_environment_get_protocol_registry(SubGhzEnvironment* instance); + +/** + * Get list of protocols names. + * @param instance Pointer to a SubGhzEnvironment instance + * @param idx index protocols + * @return Pointer to a SubGhzProtocolRegistry + */ +const char* subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/protocols/alutech_at_4n.c b/lib/subghz/protocols/alutech_at_4n.c new file mode 100644 index 00000000000..16c715f807d --- /dev/null +++ b/lib/subghz/protocols/alutech_at_4n.c @@ -0,0 +1,455 @@ +#include "alutech_at_4n.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocoAlutechAt4n" + +#define SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE 0xFFFFFFFF + +static const SubGhzBlockConst subghz_protocol_alutech_at_4n_const = { + .te_short = 400, + .te_long = 800, + .te_delta = 140, + .min_count_bit_for_found = 72, +}; + +struct SubGhzProtocolDecoderAlutech_at_4n { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint64_t data; + uint32_t crc; + uint16_t header_count; + + const char* alutech_at_4n_rainbow_table_file_name; +}; + +struct SubGhzProtocolEncoderAlutech_at_4n { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + Alutech_at_4nDecoderStepReset = 0, + Alutech_at_4nDecoderStepCheckPreambula, + Alutech_at_4nDecoderStepSaveDuration, + Alutech_at_4nDecoderStepCheckDuration, +} Alutech_at_4nDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder = { + .alloc = subghz_protocol_decoder_alutech_at_4n_alloc, + .free = subghz_protocol_decoder_alutech_at_4n_free, + + .feed = subghz_protocol_decoder_alutech_at_4n_feed, + .reset = subghz_protocol_decoder_alutech_at_4n_reset, + + .get_hash_data = subghz_protocol_decoder_alutech_at_4n_get_hash_data, + .serialize = subghz_protocol_decoder_alutech_at_4n_serialize, + .deserialize = subghz_protocol_decoder_alutech_at_4n_deserialize, + .get_string = subghz_protocol_decoder_alutech_at_4n_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol subghz_protocol_alutech_at_4n = { + .name = SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &subghz_protocol_alutech_at_4n_decoder, + .encoder = &subghz_protocol_alutech_at_4n_encoder, +}; + +/** + * Read bytes from rainbow table + * @param file_name Full path to rainbow table the file + * @param number_alutech_at_4n_magic_data number in the array + * @return alutech_at_4n_magic_data + */ +static uint32_t subghz_protocol_alutech_at_4n_get_magic_data_in_file( + const char* file_name, + uint8_t number_alutech_at_4n_magic_data) { + if(!strcmp(file_name, "")) return SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE; + + uint8_t buffer[sizeof(uint32_t)] = {0}; + uint32_t address = number_alutech_at_4n_magic_data * sizeof(uint32_t); + uint32_t alutech_at_4n_magic_data = 0; + + if(subghz_keystore_raw_get_data(file_name, address, buffer, sizeof(uint32_t))) { + for(size_t i = 0; i < sizeof(uint32_t); i++) { + alutech_at_4n_magic_data = (alutech_at_4n_magic_data << 8) | buffer[i]; + } + } else { + alutech_at_4n_magic_data = SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE; + } + return alutech_at_4n_magic_data; +} + +static uint8_t subghz_protocol_alutech_at_4n_crc(uint64_t data) { + uint8_t* p = (uint8_t*)&data; + uint8_t crc = 0xff; + for(uint8_t y = 0; y < 8; y++) { + crc = crc ^ p[y]; + for(uint8_t i = 0; i < 8; i++) { + if((crc & 0x80) != 0) { + crc <<= 1; + crc ^= 0x31; + } else { + crc <<= 1; + } + } + } + return crc; +} + +static uint8_t subghz_protocol_alutech_at_4n_decrypt_data_crc(uint8_t data) { + uint8_t crc = data; + for(uint8_t i = 0; i < 8; i++) { + if((crc & 0x80) != 0) { + crc <<= 1; + crc ^= 0x31; + } else { + crc <<= 1; + } + } + return ~crc; +} + +static uint64_t subghz_protocol_alutech_at_4n_decrypt(uint64_t data, const char* file_name) { + uint8_t* p = (uint8_t*)&data; + uint32_t data1 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; + uint32_t data2 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; + uint32_t data3 = 0; + uint32_t magic_data[] = { + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 3), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5)}; + + uint32_t i = magic_data[0]; + do { + data2 = data2 - + ((magic_data[1] + (data1 << 4)) ^ ((magic_data[2] + (data1 >> 5)) ^ (data1 + i))); + data3 = data2 + i; + i += magic_data[3]; + data1 = + data1 - ((magic_data[4] + (data2 << 4)) ^ ((magic_data[5] + (data2 >> 5)) ^ data3)); + } while(i != 0); + + p[0] = (uint8_t)(data1 >> 24); + p[1] = (uint8_t)(data1 >> 16); + p[3] = (uint8_t)data1; + p[4] = (uint8_t)(data2 >> 24); + p[5] = (uint8_t)(data2 >> 16); + p[2] = (uint8_t)(data1 >> 8); + p[6] = (uint8_t)(data2 >> 8); + p[7] = (uint8_t)data2; + + return data; +} + +// static uint64_t subghz_protocol_alutech_at_4n_encrypt(uint64_t data, const char* file_name) { +// uint8_t* p = (uint8_t*)&data; +// uint32_t data1 = 0; +// uint32_t data2 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; +// uint32_t data3 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; +// uint32_t magic_data[] = { +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 6), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0)}; + +// do { +// data1 = data1 + magic_data[0]; +// data2 = data2 + ((magic_data[1] + (data3 << 4)) ^ +// ((magic_data[2] + (data3 >> 5)) ^ (data1 + data3))); +// data3 = data3 + ((magic_data[3] + (data2 << 4)) ^ +// ((magic_data[4] + (data2 >> 5)) ^ (data1 + data2))); +// } while(data1 != magic_data[5]); +// p[0] = (uint8_t)(data2 >> 24); +// p[1] = (uint8_t)(data2 >> 16); +// p[3] = (uint8_t)data2; +// p[4] = (uint8_t)(data3 >> 24); +// p[5] = (uint8_t)(data3 >> 16); +// p[2] = (uint8_t)(data2 >> 8); +// p[6] = (uint8_t)(data3 >> 8); +// p[7] = (uint8_t)data3; + +// return data; +// } + +void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderAlutech_at_4n* instance = + malloc(sizeof(SubGhzProtocolDecoderAlutech_at_4n)); + instance->base.protocol = &subghz_protocol_alutech_at_4n; + instance->generic.protocol_name = instance->base.protocol->name; + instance->alutech_at_4n_rainbow_table_file_name = + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(environment); + if(instance->alutech_at_4n_rainbow_table_file_name) { + FURI_LOG_I( + TAG, "Loading rainbow table from %s", instance->alutech_at_4n_rainbow_table_file_name); + } + return instance; +} + +void subghz_protocol_decoder_alutech_at_4n_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + instance->alutech_at_4n_rainbow_table_file_name = NULL; + free(instance); +} + +void subghz_protocol_decoder_alutech_at_4n_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; +} + +void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + + switch(instance->decoder.parser_step) { + case Alutech_at_4nDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) { + instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckPreambula; + instance->header_count++; + } + break; + case Alutech_at_4nDecoderStepCheckPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta)) { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + break; + } + if((instance->header_count > 2) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short * 10) < + subghz_protocol_alutech_at_4n_const.te_delta * 10)) { + // Found header + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + break; + case Alutech_at_4nDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckDuration; + } + break; + case Alutech_at_4nDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_alutech_at_4n_const.te_short * 2 + + subghz_protocol_alutech_at_4n_const.te_delta)) { + //add last bit + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } + + // Found end TX + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + if(instance->decoder.decode_count_bit == + subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) { + if(instance->generic.data != instance->data) { + instance->generic.data = instance->data; + + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + instance->crc = instance->decoder.decode_data; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + } + break; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + if(instance->decoder.decode_count_bit == 64) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + if(instance->decoder.decode_count_bit == 64) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param file_name Full path to rainbow table the file + */ +static void subghz_protocol_alutech_at_4n_remote_controller( + SubGhzBlockGeneric* instance, + uint8_t crc, + const char* file_name) { + /** + * Message format 72bit LSB first + * data crc + * XXXXXXXXXXXXXXXX CC + * + * For analysis, you need to turn the package MSB + * in decoded messages format + * + * crc1 serial cnt key + * cc SSSSSSSS XXxx BB + * + * crc1 is calculated from the lower part of cnt + * key 1=0xff, 2=0x11, 3=0x22, 4=0x33, 5=0x44 + * + */ + + bool status = false; + uint64_t data = subghz_protocol_blocks_reverse_key(instance->data, 64); + crc = subghz_protocol_blocks_reverse_key(crc, 8); + + if(crc == subghz_protocol_alutech_at_4n_crc(data)) { + data = subghz_protocol_alutech_at_4n_decrypt(data, file_name); + status = true; + } + + if(status && ((uint8_t)(data >> 56) == + subghz_protocol_alutech_at_4n_decrypt_data_crc((uint8_t)((data >> 8) & 0xFF)))) { + instance->btn = (uint8_t)data & 0xFF; + instance->cnt = (uint16_t)(data >> 8) & 0xFFFF; + instance->serial = (uint32_t)(data >> 24) & 0xFFFFFFFF; + } + + if(!status) { + instance->btn = 0; + instance->cnt = 0; + instance->serial = 0; + } +} + +uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + return (uint8_t)instance->crc; +} + +SubGhzProtocolStatus subghz_protocol_decoder_alutech_at_4n_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + 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 = SubGhzProtocolStatusErrorParserOthers; + } + return res; +} + +SubGhzProtocolStatus subghz_protocol_decoder_alutech_at_4n_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + 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; + } + } while(false); + return ret; +} + +void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + subghz_protocol_alutech_at_4n_remote_controller( + &instance->generic, instance->crc, instance->alutech_at_4n_rainbow_table_file_name); + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %d\r\n" + "Key:0x%08lX%08lX%02X\r\n" + "Sn:0x%08lX Btn:0x%01X\r\n" + "Cnt:0x%03lX\r\n", + + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + (uint8_t)instance->crc, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt); +} diff --git a/lib/subghz/protocols/alutech_at_4n.h b/lib/subghz/protocols/alutech_at_4n.h new file mode 100644 index 00000000000..189f2f0d8b4 --- /dev/null +++ b/lib/subghz/protocols/alutech_at_4n.h @@ -0,0 +1,73 @@ +#pragma once +#include "base.h" + +#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; + +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; + +/** + * Allocate SubGhzProtocolDecoderAlutech_at_4n. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderAlutech_at_4n* pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void subghz_protocol_decoder_alutech_at_4n_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void subghz_protocol_decoder_alutech_at_4n_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderAlutech_at_4n. + * @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 status + */ +SubGhzProtocolStatus subghz_protocol_decoder_alutech_at_4n_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +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); diff --git a/lib/subghz/protocols/ansonic.c b/lib/subghz/protocols/ansonic.c new file mode 100644 index 00000000000..9a122629be7 --- /dev/null +++ b/lib/subghz/protocols/ansonic.c @@ -0,0 +1,337 @@ +#include "ansonic.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolAnsonic" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c" +#define CNT_TO_DIP(dip) \ + (dip & 0x0800 ? '1' : '0'), (dip & 0x0400 ? '1' : '0'), (dip & 0x0200 ? '1' : '0'), \ + (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), \ + (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), (dip & 0x0001 ? '1' : '0'), \ + (dip & 0x0008 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_ansonic_const = { + .te_short = 555, + .te_long = 1111, + .te_delta = 120, + .min_count_bit_for_found = 12, +}; + +struct SubGhzProtocolDecoderAnsonic { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderAnsonic { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + AnsonicDecoderStepReset = 0, + AnsonicDecoderStepFoundStartBit, + AnsonicDecoderStepSaveDuration, + AnsonicDecoderStepCheckDuration, +} AnsonicDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_ansonic_decoder = { + .alloc = subghz_protocol_decoder_ansonic_alloc, + .free = subghz_protocol_decoder_ansonic_free, + + .feed = subghz_protocol_decoder_ansonic_feed, + .reset = subghz_protocol_decoder_ansonic_reset, + + .get_hash_data = subghz_protocol_decoder_ansonic_get_hash_data, + .serialize = subghz_protocol_decoder_ansonic_serialize, + .deserialize = subghz_protocol_decoder_ansonic_deserialize, + .get_string = subghz_protocol_decoder_ansonic_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_ansonic_encoder = { + .alloc = subghz_protocol_encoder_ansonic_alloc, + .free = subghz_protocol_encoder_ansonic_free, + + .deserialize = subghz_protocol_encoder_ansonic_deserialize, + .stop = subghz_protocol_encoder_ansonic_stop, + .yield = subghz_protocol_encoder_ansonic_yield, +}; + +const SubGhzProtocol subghz_protocol_ansonic = { + .name = SUBGHZ_PROTOCOL_ANSONIC_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_FM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_ansonic_decoder, + .encoder = &subghz_protocol_ansonic_encoder, +}; + +void* subghz_protocol_encoder_ansonic_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderAnsonic* instance = malloc(sizeof(SubGhzProtocolEncoderAnsonic)); + + instance->base.protocol = &subghz_protocol_ansonic; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 52; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_ansonic_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderAnsonic* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderAnsonic instance + * @return true On success + */ +static bool subghz_protocol_encoder_ansonic_get_upload(SubGhzProtocolEncoderAnsonic* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_short * 35); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_short); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_short); + } + } + return true; +} + +SubGhzProtocolStatus + subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderAnsonic* instance = context; + SubGhzProtocolStatus res = SubGhzProtocolStatusError; + do { + 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; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_ansonic_get_upload(instance)) { + res = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } + instance->encoder.is_running = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_ansonic_stop(void* context) { + SubGhzProtocolEncoderAnsonic* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_ansonic_yield(void* context) { + SubGhzProtocolEncoderAnsonic* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_ansonic_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderAnsonic* instance = malloc(sizeof(SubGhzProtocolDecoderAnsonic)); + instance->base.protocol = &subghz_protocol_ansonic; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_ansonic_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + free(instance); +} + +void subghz_protocol_decoder_ansonic_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + instance->decoder.parser_step = AnsonicDecoderStepReset; +} + +void subghz_protocol_decoder_ansonic_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + + switch(instance->decoder.parser_step) { + case AnsonicDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short * 35) < + subghz_protocol_ansonic_const.te_delta * 35)) { + //Found header Ansonic + instance->decoder.parser_step = AnsonicDecoderStepFoundStartBit; + } + break; + case AnsonicDecoderStepFoundStartBit: + if(!level) { + break; + } else if( + DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short) < + subghz_protocol_ansonic_const.te_delta) { + //Found start bit Ansonic + instance->decoder.parser_step = AnsonicDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = AnsonicDecoderStepReset; + } + break; + case AnsonicDecoderStepSaveDuration: + if(!level) { //save interval + if(duration >= (subghz_protocol_ansonic_const.te_short * 4)) { + instance->decoder.parser_step = AnsonicDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit >= + subghz_protocol_ansonic_const.min_count_bit_for_found) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } + instance->decoder.te_last = duration; + instance->decoder.parser_step = AnsonicDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = AnsonicDecoderStepReset; + } + break; + case AnsonicDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ansonic_const.te_short) < + subghz_protocol_ansonic_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_long) < + subghz_protocol_ansonic_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = AnsonicDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ansonic_const.te_long) < + subghz_protocol_ansonic_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short) < + subghz_protocol_ansonic_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = AnsonicDecoderStepSaveDuration; + } else + instance->decoder.parser_step = AnsonicDecoderStepReset; + } else { + instance->decoder.parser_step = AnsonicDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_ansonic_check_remote_controller(SubGhzBlockGeneric* instance) { + /* + * 12345678(10) k 9 + * AAA => 10101010 1 01 0 + * + * 1...10 - DIP + * k- KEY + */ + instance->cnt = instance->data & 0xFFF; + instance->btn = ((instance->data >> 1) & 0x3); +} + +uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus subghz_protocol_decoder_ansonic_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + 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) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + subghz_protocol_ansonic_check_remote_controller(&instance->generic); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%03lX\r\n" + "Btn:%X\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.btn, + CNT_TO_DIP(instance->generic.cnt)); +} diff --git a/lib/subghz/protocols/ansonic.h b/lib/subghz/protocols/ansonic.h new file mode 100644 index 00000000000..9558531877d --- /dev/null +++ b/lib/subghz/protocols/ansonic.h @@ -0,0 +1,109 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_ANSONIC_NAME "Ansonic" + +typedef struct SubGhzProtocolDecoderAnsonic SubGhzProtocolDecoderAnsonic; +typedef struct SubGhzProtocolEncoderAnsonic SubGhzProtocolEncoderAnsonic; + +extern const SubGhzProtocolDecoder subghz_protocol_ansonic_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_ansonic_encoder; +extern const SubGhzProtocol subghz_protocol_ansonic; + +/** + * Allocate SubGhzProtocolEncoderAnsonic. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderAnsonic* pointer to a SubGhzProtocolEncoderAnsonic instance + */ +void* subghz_protocol_encoder_ansonic_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderAnsonic. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + */ +void subghz_protocol_encoder_ansonic_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +SubGhzProtocolStatus + subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + */ +void subghz_protocol_encoder_ansonic_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_ansonic_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderAnsonic. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderAnsonic* pointer to a SubGhzProtocolDecoderAnsonic instance + */ +void* subghz_protocol_decoder_ansonic_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + */ +void subghz_protocol_decoder_ansonic_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + */ +void subghz_protocol_decoder_ansonic_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_ansonic_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +SubGhzProtocolStatus subghz_protocol_decoder_ansonic_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +SubGhzProtocolStatus + subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @param output Resulting text + */ +void subghz_protocol_decoder_ansonic_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/base.c b/lib/subghz/protocols/base.c index 06542f5eb1c..37d1a308f06 100644 --- a/lib/subghz/protocols/base.c +++ b/lib/subghz/protocols/base.c @@ -11,7 +11,7 @@ void subghz_protocol_decoder_base_set_decoder_callback( bool subghz_protocol_decoder_base_get_string( SubGhzProtocolDecoderBase* decoder_base, - string_t output) { + FuriString* output) { bool status = false; if(decoder_base->protocol && decoder_base->protocol->decoder && @@ -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, - SubGhzPresetDefinition* preset) { - bool status = false; + SubGhzRadioPreset* preset) { + 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 a1a7478d945..1d819ab5efb 100644 --- a/lib/subghz/protocols/base.h +++ b/lib/subghz/protocols/base.h @@ -2,13 +2,18 @@ #include "../types.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct SubGhzProtocolDecoderBase SubGhzProtocolDecoderBase; typedef void ( *SubGhzProtocolDecoderBaseRxCallback)(SubGhzProtocolDecoderBase* instance, void* context); -typedef void ( - *SubGhzProtocolDecoderBaseSerialize)(SubGhzProtocolDecoderBase* decoder_base, string_t output); +typedef void (*SubGhzProtocolDecoderBaseSerialize)( + SubGhzProtocolDecoderBase* decoder_base, + FuriString* output); struct SubGhzProtocolDecoderBase { // Decoder general section @@ -37,27 +42,27 @@ void subghz_protocol_decoder_base_set_decoder_callback( */ bool subghz_protocol_decoder_base_get_string( SubGhzProtocolDecoderBase* decoder_base, - string_t output); + FuriString* output); /** * Serialize data SubGhzProtocolDecoderBase. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return Status Error */ -bool subghz_protocol_decoder_base_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_base_serialize( SubGhzProtocolDecoderBase* decoder_base, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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); @@ -77,3 +82,7 @@ struct SubGhzProtocolEncoderBase { // Callback section }; + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/protocols/bett.c b/lib/subghz/protocols/bett.c index aca8b8c4f7e..7fce94448e1 100644 --- a/lib/subghz/protocols/bett.c +++ b/lib/subghz/protocols/bett.c @@ -7,7 +7,7 @@ #include "../blocks/math.h" // protocol BERNER / ELKA / TEDSEN / TELETASTER -#define TAG "SubGhzProtocolBETT" +#define TAG "SubGhzProtocolBett" #define DIP_P 0b11 //(+) #define DIP_O 0b10 //(0) @@ -92,7 +92,7 @@ void* subghz_protocol_encoder_bett_alloc(SubGhzEnvironment* environment) { instance->generic.protocol_name = instance->base.protocol->name; instance->encoder.repeat = 10; - instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop) + instance->encoder.size_upload = 52; instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); instance->encoder.is_running = false; return instance; @@ -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); - subghz_protocol_encoder_bett_get_upload(instance); + 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) { @@ -233,7 +234,8 @@ void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t durat case BETTDecoderStepReset: if((!level) && (DURATION_DIFF(duration, subghz_protocol_bett_const.te_short * 44) < (subghz_protocol_bett_const.te_delta * 15))) { - //Found Preambula + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; instance->decoder.parser_step = BETTDecoderStepCheckDuration; } break; @@ -241,7 +243,6 @@ void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t durat if(!level) { if(DURATION_DIFF(duration, subghz_protocol_bett_const.te_short * 44) < (subghz_protocol_bett_const.te_delta * 15)) { - instance->decoder.parser_step = BETTDecoderStepSaveDuration; if(instance->decoder.decode_count_bit == subghz_protocol_bett_const.min_count_bit_for_found) { instance->generic.data = instance->decoder.decode_data; @@ -288,20 +289,6 @@ void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t durat } } -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_bett_check_remote_controller(SubGhzBlockGeneric* instance) { - uint32_t code_found_reverse = - subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit); - - instance->serial = (code_found_reverse & 0xFF) << 12 | - ((code_found_reverse >> 8) & 0xFF) << 4 | - ((code_found_reverse >> 20) & 0x0F); - instance->btn = ((code_found_reverse >> 16) & 0x0F); -} - uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderBETT* instance = context; @@ -309,39 +296,28 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderBETT* instance = context; 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, string_t output) { +void subghz_protocol_decoder_bett_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderBETT* instance = context; - subghz_protocol_bett_check_remote_controller(&instance->generic); - uint32_t data = (uint32_t)(instance->generic.data & 0xFFFFFF); - string_cat_printf( + uint32_t data = (uint32_t)(instance->generic.data & 0x3FFFF); + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:%05lX\r\n" @@ -350,7 +326,7 @@ void subghz_protocol_decoder_bett_get_string(void* context, string_t output) { " -: " DIP_PATTERN "\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, - (uint32_t)(instance->generic.data & 0xFFFFFF), + data, SHOW_DIP_P(data, DIP_P), SHOW_DIP_P(data, DIP_O), SHOW_DIP_P(data, DIP_N)); diff --git a/lib/subghz/protocols/bett.h b/lib/subghz/protocols/bett.h index 48f32b3e0d3..0a11cbe6973 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. @@ -83,25 +84,26 @@ uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderBETT. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_bett_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_bett_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderBETT instance * @param output Resulting text */ -void subghz_protocol_decoder_bett_get_string(void* context, string_t output); +void subghz_protocol_decoder_bett_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/bin_raw.c b/lib/subghz/protocols/bin_raw.c new file mode 100644 index 00000000000..80a34c55f72 --- /dev/null +++ b/lib/subghz/protocols/bin_raw.c @@ -0,0 +1,1148 @@ +#include "bin_raw.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" +#include +#include +#include + +#include + +#define TAG "SubGhzProtocolBinRaw" + +//change very carefully, RAM ends at the most inopportune moment +#define BIN_RAW_BUF_RAW_SIZE 2048 +#define BIN_RAW_BUF_DATA_SIZE 512 + +#define BIN_RAW_THRESHOLD_RSSI -85.0f +#define BIN_RAW_DELTA_RSSI 7.0f +#define BIN_RAW_SEARCH_CLASSES 20 +#define BIN_RAW_TE_MIN_COUNT 40 +#define BIN_RAW_BUF_MIN_DATA_COUNT 128 +#define BIN_RAW_MAX_MARKUP_COUNT 20 + +//#define BIN_RAW_DEBUG + +#ifdef BIN_RAW_DEBUG +#define bin_raw_debug(...) FURI_LOG_RAW_D(__VA_ARGS__) +#define bin_raw_debug_tag(tag, ...) \ + FURI_LOG_RAW_D("\033[0;32m[" tag "]\033[0m "); \ + FURI_LOG_RAW_D(__VA_ARGS__) +#else +#define bin_raw_debug(...) +#define bin_raw_debug_tag(...) +#endif + +static const SubGhzBlockConst subghz_protocol_bin_raw_const = { + .te_short = 30, + .te_long = 65000, + .te_delta = 0, + .min_count_bit_for_found = 0, +}; + +typedef enum { + BinRAWDecoderStepReset = 0, + BinRAWDecoderStepWrite, + BinRAWDecoderStepBufFull, + BinRAWDecoderStepNoParse, +} BinRAWDecoderStep; + +typedef enum { + BinRAWTypeUnknown = 0, + BinRAWTypeNoGap, + BinRAWTypeGap, + BinRAWTypeGapRecurring, + BinRAWTypeGapRolling, + BinRAWTypeGapUnknown, +} BinRAWType; + +struct BinRAW_Markup { + uint16_t byte_bias; + uint16_t bit_count; +}; +typedef struct BinRAW_Markup BinRAW_Markup; + +struct SubGhzProtocolDecoderBinRAW { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + int32_t* data_raw; + uint8_t* data; + BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT]; + size_t data_raw_ind; + uint32_t te; + float adaptive_threshold_rssi; +}; + +struct SubGhzProtocolEncoderBinRAW { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + uint8_t* data; + BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT]; + uint32_t te; +}; + +const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder = { + .alloc = subghz_protocol_decoder_bin_raw_alloc, + .free = subghz_protocol_decoder_bin_raw_free, + + .feed = subghz_protocol_decoder_bin_raw_feed, + .reset = subghz_protocol_decoder_bin_raw_reset, + + .get_hash_data = subghz_protocol_decoder_bin_raw_get_hash_data, + .serialize = subghz_protocol_decoder_bin_raw_serialize, + .deserialize = subghz_protocol_decoder_bin_raw_deserialize, + .get_string = subghz_protocol_decoder_bin_raw_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder = { + .alloc = subghz_protocol_encoder_bin_raw_alloc, + .free = subghz_protocol_encoder_bin_raw_free, + + .deserialize = subghz_protocol_encoder_bin_raw_deserialize, + .stop = subghz_protocol_encoder_bin_raw_stop, + .yield = subghz_protocol_encoder_bin_raw_yield, +}; + +const SubGhzProtocol subghz_protocol_bin_raw = { + .name = SUBGHZ_PROTOCOL_BIN_RAW_NAME, + .type = SubGhzProtocolTypeStatic, +#ifdef BIN_RAW_DEBUG + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, +#else + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_BinRAW | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, +#endif + .decoder = &subghz_protocol_bin_raw_decoder, + .encoder = &subghz_protocol_bin_raw_encoder, +}; + +static uint16_t subghz_protocol_bin_raw_get_full_byte(uint16_t bit_count) { + if(bit_count & 0x7) { + return (bit_count >> 3) + 1; + } else { + return (bit_count >> 3); + } +} + +void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderBinRAW* instance = malloc(sizeof(SubGhzProtocolEncoderBinRAW)); + + instance->base.protocol = &subghz_protocol_bin_raw; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = BIN_RAW_BUF_DATA_SIZE * 5; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->data = malloc(instance->encoder.size_upload * sizeof(uint8_t)); + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_bin_raw_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderBinRAW* instance = context; + free(instance->encoder.upload); + free(instance->data); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderBinRAW instance + * @return true On success + */ +static bool subghz_protocol_encoder_bin_raw_get_upload(SubGhzProtocolEncoderBinRAW* instance) { + furi_assert(instance); + + //we glue all the pieces of the package into 1 long sequence with left alignment, + //in the uploaded data we have right alignment. + + bin_raw_debug_tag(TAG, "Recovery of offset bits in sequences\r\n"); + uint16_t i = 0; + uint16_t ind = 0; + bin_raw_debug("\tind byte_bias\tbit_count\tbit_bias\r\n"); + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + uint8_t bit_bias = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 - + instance->data_markup[i].bit_count; + bin_raw_debug( + "\t%d\t%d\t%d :\t\t%d\r\n", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count, + bit_bias); + for(uint16_t y = instance->data_markup[i].byte_bias * 8; + y < instance->data_markup[i].byte_bias * 8 + + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 - + bit_bias; + y++) { + subghz_protocol_blocks_set_bit_array( + subghz_protocol_blocks_get_bit_array(instance->data, y + bit_bias), + instance->data, + ind++, + BIN_RAW_BUF_DATA_SIZE); + } + i++; + } + bin_raw_debug("\r\n"); +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Restored Sequence left aligned\r\n"); + for(uint16_t y = 0; y < subghz_protocol_bin_raw_get_full_byte(ind); y++) { + bin_raw_debug("%02X ", instance->data[y]); + } + bin_raw_debug("\r\n\tbin_count_result= %d\r\n\r\n", ind); + + bin_raw_debug_tag( + TAG, "Maximum levels encoded in upload %zu\r\n", instance->encoder.size_upload); +#endif + instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array( + instance->data, + ind, + instance->encoder.upload, + instance->encoder.size_upload, + instance->te, + SubGhzProtocolBlockAlignBitLeft); + + bin_raw_debug_tag(TAG, "The result %zu is levels\r\n", instance->encoder.size_upload); + bin_raw_debug_tag(TAG, "Remaining free memory %zu\r\n", memmgr_get_free_heap()); + return true; +} + +SubGhzProtocolStatus + subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderBinRAW* instance = context; + + 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; + } + + instance->generic.data_count_bit = (uint16_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + res = SubGhzProtocolStatusErrorParserTe; + break; + } + + temp_data = 0; + uint16_t ind = 0; + uint16_t byte_bias = 0; + uint16_t byte_count = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + 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; + } + + instance->data_markup[ind].bit_count = temp_data; + instance->data_markup[ind].byte_bias = byte_bias; + byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data); + + if(!flipper_format_read_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[ind].byte_bias, + 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++; + } + +#ifdef BIN_RAW_DEBUG + uint16_t i = 0; + bin_raw_debug_tag(TAG, "Download data to encoder\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data"); + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count); + for(uint16_t y = instance->data_markup[i].byte_bias; + y < instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + y++) { + bin_raw_debug("%02X ", instance->data[y]); + } + i++; + } + bin_raw_debug("\r\n\r\n"); +#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; + res = SubGhzProtocolStatusErrorEncoderGetUpload; + } + instance->encoder.is_running = true; + + res = SubGhzProtocolStatusOk; + } while(0); + + return res; +} + +void subghz_protocol_encoder_bin_raw_stop(void* context) { + SubGhzProtocolEncoderBinRAW* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context) { + SubGhzProtocolEncoderBinRAW* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_bin_raw_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderBinRAW* instance = malloc(sizeof(SubGhzProtocolDecoderBinRAW)); + instance->base.protocol = &subghz_protocol_bin_raw; + instance->generic.protocol_name = instance->base.protocol->name; + instance->data_raw_ind = 0; + instance->data_raw = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t)); + instance->data = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t)); + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + instance->adaptive_threshold_rssi = BIN_RAW_THRESHOLD_RSSI; + return instance; +} + +void subghz_protocol_decoder_bin_raw_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + free(instance->data_raw); + free(instance->data); + free(instance); +} + +void subghz_protocol_decoder_bin_raw_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; +#ifdef BIN_RAW_DEBUG + UNUSED(instance); +#else + instance->decoder.parser_step = BinRAWDecoderStepNoParse; + instance->data_raw_ind = 0; +#endif +} + +void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + if(instance->decoder.parser_step == BinRAWDecoderStepWrite) { + if(instance->data_raw_ind == BIN_RAW_BUF_RAW_SIZE) { + instance->decoder.parser_step = BinRAWDecoderStepBufFull; + } else { + instance->data_raw[instance->data_raw_ind++] = (level ? duration : -duration); + } + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzProtocolDecoderBinRAW* instance + */ +static bool + subghz_protocol_bin_raw_check_remote_controller(SubGhzProtocolDecoderBinRAW* instance) { + struct { + float data; + uint16_t count; + } classes[BIN_RAW_SEARCH_CLASSES]; + + size_t ind = 0; + + memset(classes, 0x00, sizeof(classes)); + + uint16_t data_markup_ind = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + + if(instance->data_raw_ind < 512) { + ind = + instance->data_raw_ind - + 100; //there is usually garbage at the end of the record, we exclude it from the classification + } else { + ind = 512; + } + + //sort the durations to find the shortest correlated interval + for(size_t i = 0; i < ind; i++) { + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + if(classes[k].count == 0) { + classes[k].data = (float)(abs(instance->data_raw[i])); + classes[k].count++; + break; + } else if( + DURATION_DIFF((float)(abs(instance->data_raw[i])), (classes[k].data)) < + (classes[k].data / 4)) { //if the test value does not differ by more than 25% + classes[k].data += ((float)(abs(instance->data_raw[i])) - classes[k].data) * + 0.05f; //running average k=0.05 + classes[k].count++; + break; + } + } + } + + // if(classes[BIN_RAW_SEARCH_CLASSES - 1].count != 0) { + // //filling the classifier, it means that they received an unclean signal + // return false; + // } + + //looking for the minimum te with an occurrence greater than BIN_RAW_TE_MIN_COUNT + instance->te = subghz_protocol_bin_raw_const.te_long * 2; + + bool te_ok = false; + uint16_t gap_ind = 0; + uint16_t gap_delta = 0; + uint32_t gap = 0; + int data_temp = 0; + BinRAWType bin_raw_type = BinRAWTypeUnknown; + + //sort by number of occurrences + bool swap = true; + while(swap) { + swap = false; + for(size_t i = 1; i < BIN_RAW_SEARCH_CLASSES; i++) { + if(classes[i].count > classes[i - 1].count) { + uint32_t data = classes[i - 1].data; + uint32_t count = classes[i - 1].count; + classes[i - 1].data = classes[i].data; + classes[i - 1].count = classes[i].count; + classes[i].data = data; + classes[i].count = count; + swap = true; + } + } + } +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Sorted durations\r\n"); + bin_raw_debug("\t\tind\tcount\tus\r\n"); + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data); + } + bin_raw_debug("\r\n"); +#endif + if((classes[0].count > BIN_RAW_TE_MIN_COUNT) && (classes[1].count == 0)) { + //adopted only the preamble + instance->te = (uint32_t)classes[0].data; + te_ok = true; + gap = 0; //gap no + } else { + //take the 2 most common durations + //check that there are enough + if((classes[0].count < BIN_RAW_TE_MIN_COUNT) || + (classes[1].count < (BIN_RAW_TE_MIN_COUNT >> 1))) + return false; + //arrange the first 2 date values in ascending order + if(classes[0].data > classes[1].data) { + uint32_t data = classes[1].data; + classes[0].data = classes[1].data; + classes[1].data = data; + } + + //determine the value to be corrected + for(uint8_t k = 1; k < 5; k++) { + float delta = (classes[1].data / (classes[0].data / k)); + bin_raw_debug_tag(TAG, "K_div= %f\r\n", (double)(delta)); + delta -= (uint32_t)delta; + + if((delta < 0.20f) || (delta > 0.80f)) { + instance->te = (uint32_t)classes[0].data / k; + bin_raw_debug_tag(TAG, "K= %d\r\n", k); + te_ok = true; //found a correlated duration + break; + } + } + if(!te_ok) { + //did not find the minimum TE satisfying the condition + return false; + } + bin_raw_debug_tag(TAG, "TE= %lu\r\n\r\n", instance->te); + + //looking for a gap + for(size_t k = 2; k < BIN_RAW_SEARCH_CLASSES; k++) { + if((classes[k].count > 2) && (classes[k].data > gap)) { + gap = (uint32_t)classes[k].data; + gap_delta = gap / 5; //calculate 20% deviation from ideal value + } + } + + if((gap / instance->te) < + 10) { //make an assumption, the longest gap should be more than 10 TE + gap = 0; //check that our signal has a gap greater than 10*TE + bin_raw_type = BinRAWTypeNoGap; + } else { + bin_raw_type = BinRAWTypeGap; + //looking for the last occurrence of gap + ind = instance->data_raw_ind - 1; + while((ind > 0) && (DURATION_DIFF(abs(instance->data_raw[ind]), gap) > gap_delta)) { + ind--; + } + gap_ind = ind; + } + } + + //if we consider that there is a gap, then we divide the signal with respect to this gap + //processing input data from the end + if(bin_raw_type == BinRAWTypeGap) { + bin_raw_debug_tag(TAG, "Tinted sequence\r\n"); + ind = (BIN_RAW_BUF_DATA_SIZE * 8); + uint16_t bit_count = 0; + do { + gap_ind--; + data_temp = (int)(round((float)(instance->data_raw[gap_ind]) / instance->te)); + bin_raw_debug("%d ", data_temp); + if(data_temp == 0) bit_count++; //there is noise in the package + for(size_t i = 0; i < abs(data_temp); i++) { + bit_count++; + if(ind) { + ind--; + } else { + break; + } + if(data_temp > 0) { + subghz_protocol_blocks_set_bit_array( + true, instance->data, ind, BIN_RAW_BUF_DATA_SIZE); + } else { + subghz_protocol_blocks_set_bit_array( + false, instance->data, ind, BIN_RAW_BUF_DATA_SIZE); + } + } + //split into full bytes if gap is caught + if(DURATION_DIFF(abs(instance->data_raw[gap_ind]), gap) < gap_delta) { + instance->data_markup[data_markup_ind].byte_bias = ind >> 3; + instance->data_markup[data_markup_ind++].bit_count = bit_count; + bit_count = 0; + + if(data_markup_ind == BIN_RAW_MAX_MARKUP_COUNT) break; + ind &= 0xFFFFFFF8; //jump to the pre whole byte + } + } while(gap_ind != 0); + if((data_markup_ind != BIN_RAW_MAX_MARKUP_COUNT) && (ind != 0)) { + instance->data_markup[data_markup_ind].byte_bias = ind >> 3; + instance->data_markup[data_markup_ind++].bit_count = bit_count; + } + + bin_raw_debug("\r\n\t count bit= %zu\r\n\r\n", (BIN_RAW_BUF_DATA_SIZE * 8) - ind); + + //reset the classifier and classify the received data + memset(classes, 0x00, sizeof(classes)); + + bin_raw_debug_tag(TAG, "Sort the found pieces by the number of bits in them\r\n"); + for(size_t i = 0; i < data_markup_ind; i++) { + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + if(classes[k].count == 0) { + classes[k].data = instance->data_markup[i].bit_count; + classes[k].count++; + break; + } else if(instance->data_markup[i].bit_count == (uint16_t)classes[k].data) { + classes[k].count++; + break; + } + } + } + +#ifdef BIN_RAW_DEBUG + bin_raw_debug("\t\tind\tcount\tus\r\n"); + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data); + } + bin_raw_debug("\r\n"); +#endif + + //choose the value with the maximum repetition + data_temp = 0; + for(size_t i = 0; i < BIN_RAW_SEARCH_CLASSES; i++) { + if((classes[i].count > 1) && (data_temp < classes[i].count)) + data_temp = (int)classes[i].data; + } + + //if(data_markup_ind == 0) return false; + +#ifdef BIN_RAW_DEBUG + //output in reverse order + bin_raw_debug_tag(TAG, "Found sequences\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data\r\n"); + uint16_t data_markup_ind_temp = data_markup_ind; + if(data_markup_ind) { + data_markup_ind_temp--; + for(size_t i = (ind / 8); i < BIN_RAW_BUF_DATA_SIZE; i++) { + if(instance->data_markup[data_markup_ind_temp].byte_bias == i) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + data_markup_ind_temp, + instance->data_markup[data_markup_ind_temp].byte_bias, + instance->data_markup[data_markup_ind_temp].bit_count); + if(data_markup_ind_temp != 0) data_markup_ind_temp--; + } + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); + } + //compare data in chunks with the same number of bits + bin_raw_debug_tag(TAG, "Analyze sequences of long %d bit\r\n\r\n", data_temp); +#endif + + //if(data_temp == 0) data_temp = (int)classes[0].data; + + if(data_temp != 0) { + //check that data in transmission is repeated every packet + for(uint16_t i = 0; i < data_markup_ind - 1; i++) { + if((instance->data_markup[i].bit_count == data_temp) && + (instance->data_markup[i + 1].bit_count == data_temp)) { + //if the number of bits in adjacent parcels is the same, compare the data + bin_raw_debug_tag( + TAG, + "Comparison of neighboring sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n", + i, + i + 1, + instance->data[instance->data_markup[i].byte_bias], + instance->data[instance->data_markup[i + 1].byte_bias], + instance->data + [instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count) - + 1], + instance->data + [instance->data_markup[i + 1].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i + 1].bit_count) - + 1]); + + uint16_t byte_count = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + if(memcmp( + instance->data + instance->data_markup[i].byte_bias, + instance->data + instance->data_markup[i + 1].byte_bias, + byte_count - 1) == 0) { + bin_raw_debug_tag( + TAG, "Match found bin_raw_type=BinRAWTypeGapRecurring\r\n\r\n"); + + //place in 1 element the offset to valid data + instance->data_markup[0].bit_count = instance->data_markup[i].bit_count; + instance->data_markup[0].byte_bias = instance->data_markup[i].byte_bias; + //markup end sign + instance->data_markup[1].bit_count = 0; + instance->data_markup[1].byte_bias = 0; + + bin_raw_type = BinRAWTypeGapRecurring; + i = data_markup_ind; + break; + } + } + } + } + + if(bin_raw_type == BinRAWTypeGap) { + // check that retry occurs every n packets + for(uint16_t i = 0; i < data_markup_ind - 2; i++) { + uint16_t byte_count = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + for(uint16_t y = i + 1; y < data_markup_ind - 1; y++) { + bin_raw_debug_tag( + TAG, + "Comparison every N sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n", + i, + y, + instance->data[instance->data_markup[i].byte_bias], + instance->data[instance->data_markup[y].byte_bias], + instance->data + [instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count) - + 1], + instance->data + [instance->data_markup[y].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[y].bit_count) - + 1]); + + if(byte_count == + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[y].bit_count)) { //if the length in bytes matches + + if((memcmp( + instance->data + instance->data_markup[i].byte_bias, + instance->data + instance->data_markup[y].byte_bias, + byte_count - 1) == 0) && + (memcmp( + instance->data + instance->data_markup[i + 1].byte_bias, + instance->data + instance->data_markup[y + 1].byte_bias, + byte_count - 1) == 0)) { + uint8_t index = 0; +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag( + TAG, "Match found bin_raw_type=BinRAWTypeGapRolling\r\n\r\n"); + //output in reverse order + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data\r\n"); + index = y - 1; + for(size_t z = instance->data_markup[y].byte_bias + byte_count; + z < instance->data_markup[i].byte_bias + byte_count; + z++) { + if(instance->data_markup[index].byte_bias == z) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + index, + instance->data_markup[index].byte_bias, + instance->data_markup[index].bit_count); + if(index != 0) index--; + } + bin_raw_debug("%02X ", instance->data[z]); + } + + bin_raw_debug("\r\n\r\n"); +#endif + BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT]; + memcpy( + markup_temp, + instance->data_markup, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + memset( + instance->data_markup, + 0x00, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + + for(index = i; index < y; index++) { + instance->data_markup[index - i].bit_count = + markup_temp[y - index - 1].bit_count; + instance->data_markup[index - i].byte_bias = + markup_temp[y - index - 1].byte_bias; + } + + bin_raw_type = BinRAWTypeGapRolling; + i = data_markup_ind; + break; + } + } + } + } + } + if(bin_raw_type == BinRAWTypeGap) { + if(data_temp != 0) { //there are sequences with the same number of bits + + BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT]; + memcpy( + markup_temp, + instance->data_markup, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + memset( + instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(data_temp); + uint16_t index = 0; + uint16_t it = BIN_RAW_MAX_MARKUP_COUNT; + do { + it--; + if(subghz_protocol_bin_raw_get_full_byte(markup_temp[it].bit_count) == + byte_count) { + instance->data_markup[index].bit_count = markup_temp[it].bit_count; + instance->data_markup[index].byte_bias = markup_temp[it].byte_bias; + index++; + bin_raw_type = BinRAWTypeGapUnknown; + } + } while(it != 0); + } + } + + if(bin_raw_type != BinRAWTypeGap) + return true; + else + return false; + + } else { + // if bin_raw_type == BinRAWTypeGap + bin_raw_debug_tag(TAG, "Sequence analysis without gap\r\n"); + ind = 0; + for(size_t i = 0; i < instance->data_raw_ind; i++) { + int data_temp = (int)(round((float)(instance->data_raw[i]) / instance->te)); + if(data_temp == 0) break; //found an interval 2 times shorter than TE, this is noise + bin_raw_debug("%d ", data_temp); + + for(size_t k = 0; k < abs(data_temp); k++) { + if(data_temp > 0) { + subghz_protocol_blocks_set_bit_array( + true, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE); + } else { + subghz_protocol_blocks_set_bit_array( + false, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE); + } + if(ind == BIN_RAW_BUF_DATA_SIZE * 8) { + i = instance->data_raw_ind; + break; + } + } + } + + if(ind != 0) { + bin_raw_type = BinRAWTypeNoGap; + //right alignment + uint8_t bit_bias = (subghz_protocol_bin_raw_get_full_byte(ind) << 3) - ind; +#ifdef BIN_RAW_DEBUG + bin_raw_debug( + "\r\n\t count bit= %zu\tcount full byte= %d\tbias bit= %d\r\n\r\n", + ind, + subghz_protocol_bin_raw_get_full_byte(ind), + bit_bias); + + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); +#endif + //checking that the received sequence contains useful data + bool data_check = false; + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + if(instance->data[i] != 0) { + data_check = true; + break; + } + } + + if(data_check) { + for(size_t i = subghz_protocol_bin_raw_get_full_byte(ind) - 1; i > 0; i--) { + instance->data[i] = (instance->data[i - 1] << (8 - bit_bias)) | + (instance->data[i] >> bit_bias); + } + instance->data[0] = (instance->data[0] >> bit_bias); + +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Data right alignment\r\n"); + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); +#endif + instance->data_markup[0].bit_count = ind; + instance->data_markup[0].byte_bias = 0; + + return true; + } else { + return false; + } + } else { + return false; + } + } + return false; +} + +void subghz_protocol_decoder_bin_raw_data_input_rssi( + SubGhzProtocolDecoderBinRAW* instance, + float rssi) { + furi_assert(instance); + switch(instance->decoder.parser_step) { + case BinRAWDecoderStepReset: + + bin_raw_debug("%ld %ld :", (int32_t)rssi, (int32_t)instance->adaptive_threshold_rssi); + if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) { + instance->data_raw_ind = 0; + memset(instance->data_raw, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t)); + memset(instance->data, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t)); + instance->decoder.parser_step = BinRAWDecoderStepWrite; + bin_raw_debug_tag(TAG, "RSSI\r\n"); + } else { + //adaptive noise level adjustment + instance->adaptive_threshold_rssi += (rssi - instance->adaptive_threshold_rssi) * 0.2f; + } + break; + + case BinRAWDecoderStepBufFull: + case BinRAWDecoderStepWrite: +#ifdef BIN_RAW_DEBUG + if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) { + bin_raw_debug("\033[0;32m%ld \033[0m ", (int32_t)rssi); + } else { + bin_raw_debug("%ld ", (int32_t)rssi); + } +#endif + if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) { +#ifdef BIN_RAW_DEBUG + bin_raw_debug("\r\n\r\n"); + bin_raw_debug_tag(TAG, "Data for analysis, positive high, negative low, us\r\n"); + for(size_t i = 0; i < instance->data_raw_ind; i++) { + bin_raw_debug("%ld ", instance->data_raw[i]); + } + bin_raw_debug("\r\n\t count data= %zu\r\n\r\n", instance->data_raw_ind); +#endif + instance->decoder.parser_step = BinRAWDecoderStepReset; + instance->generic.data_count_bit = 0; + if(instance->data_raw_ind >= BIN_RAW_BUF_MIN_DATA_COUNT) { + if(subghz_protocol_bin_raw_check_remote_controller(instance)) { + bin_raw_debug_tag(TAG, "Sequence found\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data"); + uint16_t i = 0; + while((i < BIN_RAW_MAX_MARKUP_COUNT) && + (instance->data_markup[i].bit_count != 0)) { + instance->generic.data_count_bit += instance->data_markup[i].bit_count; +#ifdef BIN_RAW_DEBUG + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count); + for(uint16_t y = instance->data_markup[i].byte_bias; + y < instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count); + y++) { + bin_raw_debug("%02X ", instance->data[y]); + } +#endif + i++; + } + bin_raw_debug("\r\n"); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + } + } + break; + + default: + //if instance->decoder.parser_step == BinRAWDecoderStepNoParse or others, restore the initial state + if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) { + instance->decoder.parser_step = BinRAWDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + return subghz_protocol_blocks_add_bytes( + instance->data + instance->data_markup[0].byte_bias, + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[0].bit_count)); +} + +SubGhzProtocolStatus subghz_protocol_decoder_bin_raw_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + SubGhzProtocolStatus res = SubGhzProtocolStatusError; + FuriString* temp_str; + temp_str = furi_string_alloc(); + do { + stream_clean(flipper_format_get_raw_stream(flipper_format)); + 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; + } + + subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); + 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; + } + + uint16_t i = 0; + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + 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( + flipper_format, + "Data_RAW", + 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 = SubGhzProtocolStatusOk; + } while(false); + furi_string_free(temp_str); + return res; +} + +SubGhzProtocolStatus + subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + 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; + } + + instance->generic.data_count_bit = (uint16_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + res = SubGhzProtocolStatusErrorParserTe; + break; + } + + temp_data = 0; + uint16_t ind = 0; + uint16_t byte_bias = 0; + uint16_t byte_count = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + 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; + } + + instance->data_markup[ind].bit_count = temp_data; + instance->data_markup[ind].byte_bias = byte_bias; + byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data); + + if(!flipper_format_read_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[ind].byte_bias, + 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 = SubGhzProtocolStatusOk; + } while(0); + + return res; +} + +void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:", + instance->generic.protocol_name, + instance->generic.data_count_bit); + + uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(instance->generic.data_count_bit); + for(size_t i = 0; (byte_count < 36 ? i < byte_count : i < 36); i++) { + furi_string_cat_printf(output, "%02X", instance->data[i]); + } + + furi_string_cat_printf(output, "\r\nTe:%luus\r\n", instance->te); +} diff --git a/lib/subghz/protocols/bin_raw.h b/lib/subghz/protocols/bin_raw.h new file mode 100644 index 00000000000..26cc6ec3a20 --- /dev/null +++ b/lib/subghz/protocols/bin_raw.h @@ -0,0 +1,110 @@ +#pragma once + +#include "base.h" +#include "public_api.h" + +#define SUBGHZ_PROTOCOL_BIN_RAW_NAME "BinRAW" + +typedef struct SubGhzProtocolDecoderBinRAW SubGhzProtocolDecoderBinRAW; +typedef struct SubGhzProtocolEncoderBinRAW SubGhzProtocolEncoderBinRAW; + +extern const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder; +extern const SubGhzProtocol subghz_protocol_bin_raw; + +/** + * Allocate SubGhzProtocolEncoderBinRAW. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderBinRAW* pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderBinRAW. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + */ +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 status + */ +SubGhzProtocolStatus + subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void subghz_protocol_encoder_bin_raw_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderBinRAW. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderBinRAW* pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void* subghz_protocol_decoder_bin_raw_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void subghz_protocol_decoder_bin_raw_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void subghz_protocol_decoder_bin_raw_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderBinRAW. + * @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 status + */ +SubGhzProtocolStatus subghz_protocol_decoder_bin_raw_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param output Resulting text + */ +void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/came.c b/lib/subghz/protocols/came.c index 37048017e3f..40ae05bade3 100644 --- a/lib/subghz/protocols/came.c +++ b/lib/subghz/protocols/came.c @@ -12,7 +12,13 @@ * */ -#define TAG "SubGhzProtocolCAME" +#define TAG "SubGhzProtocolCame" +#define CAME_12_COUNT_BIT 12 +#define CAME_24_COUNT_BIT 24 +#define PRASTEL_COUNT_BIT 25 +#define PRASTEL_NAME "Prastel" +#define AIRFORCE_COUNT_BIT 18 +#define AIRFORCE_NAME "Airforce" static const SubGhzBlockConst subghz_protocol_came_const = { .te_short = 320, @@ -83,7 +89,7 @@ void* subghz_protocol_encoder_came_alloc(SubGhzEnvironment* environment) { instance->generic.protocol_name = instance->base.protocol->name; instance->encoder.repeat = 10; - instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop) + instance->encoder.size_upload = 128; instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); instance->encoder.is_running = false; return instance; @@ -103,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) { @@ -112,8 +119,28 @@ static bool subghz_protocol_encoder_came_get_upload(SubGhzProtocolEncoderCame* i instance->encoder.size_upload = size_upload; } //Send header + + 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 * 36); + 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); @@ -136,33 +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 != - subghz_protocol_came_const.min_count_bit_for_found) && - (instance->generic.data_count_bit != - 2 * subghz_protocol_came_const.min_count_bit_for_found)) { + 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); - subghz_protocol_encoder_came_get_upload(instance); + 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) { @@ -213,8 +240,8 @@ void subghz_protocol_decoder_came_feed(void* context, bool level, uint32_t durat SubGhzProtocolDecoderCame* instance = context; switch(instance->decoder.parser_step) { case CameDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_const.te_short * 51) < - subghz_protocol_came_const.te_delta * 51)) { //Need protocol 36 te_short + if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_const.te_short * 56) < + subghz_protocol_came_const.te_delta * 47)) { //Found header CAME instance->decoder.parser_step = CameDecoderStepFoundStartBit; } @@ -237,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; @@ -287,36 +317,35 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderCame* instance = context; 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 != - subghz_protocol_came_const.min_count_bit_for_found) && - (instance->generic.data_count_bit != - 2 * subghz_protocol_came_const.min_count_bit_for_found)) { + 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; } -void subghz_protocol_decoder_came_get_string(void* context, string_t output) { +void subghz_protocol_decoder_came_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderCame* instance = context; @@ -327,12 +356,16 @@ void subghz_protocol_decoder_came_get_string(void* context, string_t output) { uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - string_cat_printf( + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:0x%08lX\r\n" "Yek:0x%08lX\r\n", - instance->generic.protocol_name, + (instance->generic.data_count_bit == PRASTEL_COUNT_BIT ? + PRASTEL_NAME : + (instance->generic.data_count_bit == AIRFORCE_COUNT_BIT ? + AIRFORCE_NAME : + instance->generic.protocol_name)), instance->generic.data_count_bit, code_found_lo, code_found_reverse_lo); diff --git a/lib/subghz/protocols/came.h b/lib/subghz/protocols/came.h index c2648c057f6..fffa017ff59 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. @@ -83,25 +84,26 @@ uint8_t subghz_protocol_decoder_came_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderCame. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_came_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_came_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderCame instance * @param output Resulting text */ -void subghz_protocol_decoder_came_get_string(void* context, string_t output); +void subghz_protocol_decoder_came_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/came_atomo.c b/lib/subghz/protocols/came_atomo.c index 0c3cdd8adcd..9f411a66a77 100644 --- a/lib/subghz/protocols/came_atomo.c +++ b/lib/subghz/protocols/came_atomo.c @@ -189,7 +189,7 @@ void subghz_protocol_decoder_came_atomo_feed(void* context, bool level, uint32_t /** * Read bytes from rainbow table * @param file_name Full path to rainbow table the file - * @param number_atomo_magic_xor Сell number in the array + * @param number_atomo_magic_xor number in the array * @return atomo_magic_xor */ static uint64_t subghz_protocol_came_atomo_get_magic_xor_in_file( @@ -298,34 +298,26 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderCameAtomo* instance = context; 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, string_t output) { +void subghz_protocol_decoder_came_atomo_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderCameAtomo* instance = context; subghz_protocol_came_atomo_remote_controller( @@ -333,12 +325,12 @@ void subghz_protocol_decoder_came_atomo_get_string(void* context, string_t outpu uint32_t code_found_hi = instance->generic.data >> 32; uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - string_cat_printf( + furi_string_cat_printf( output, "%s %db\r\n" "Key:0x%lX%08lX\r\n" "Sn:0x%08lX Btn:0x%01X\r\n" - "Cnt:0x%03X\r\n", + "Cnt:0x%03lX\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, diff --git a/lib/subghz/protocols/came_atomo.h b/lib/subghz/protocols/came_atomo.h index 70a79eca12c..0faea4f1a7c 100644 --- a/lib/subghz/protocols/came_atomo.h +++ b/lib/subghz/protocols/came_atomo.h @@ -48,25 +48,26 @@ uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderCameAtomo. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_came_atomo_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_came_atomo_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance * @param output Resulting text */ -void subghz_protocol_decoder_came_atomo_get_string(void* context, string_t output); +void subghz_protocol_decoder_came_atomo_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/came_twee.c b/lib/subghz/protocols/came_twee.c index b5b409c5904..1d79d2201e2 100644 --- a/lib/subghz/protocols/came_twee.c +++ b/lib/subghz/protocols/came_twee.c @@ -13,7 +13,7 @@ * */ -#define TAG "SubGhzProtocolCAME_Twee" +#define TAG "SubGhzProtocolCameTwee" #define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c" #define CNT_TO_DIP(dip) \ @@ -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,45 +416,37 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderCameTwee* instance = context; 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, string_t output) { +void subghz_protocol_decoder_came_twee_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderCameTwee* instance = context; subghz_protocol_came_twee_remote_controller(&instance->generic); uint32_t code_found_hi = instance->generic.data >> 32; uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - string_cat_printf( + furi_string_cat_printf( output, "%s %db\r\n" "Key:0x%lX%08lX\r\n" - "Btn:%lX\r\n" + "Btn:%X\r\n" "DIP:" DIP_PATTERN "\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, diff --git a/lib/subghz/protocols/came_twee.h b/lib/subghz/protocols/came_twee.h index 42e6ddaf366..f26f1e8062d 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. @@ -83,25 +84,26 @@ uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderCameTwee. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_came_twee_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_came_twee_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance * @param output Resulting text */ -void subghz_protocol_decoder_came_twee_get_string(void* context, string_t output); +void subghz_protocol_decoder_came_twee_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/chamberlain_code.c b/lib/subghz/protocols/chamberlain_code.c index 6c99d84519e..0dd0d2b0b79 100644 --- a/lib/subghz/protocols/chamberlain_code.c +++ b/lib/subghz/protocols/chamberlain_code.c @@ -6,7 +6,7 @@ #include "../blocks/generic.h" #include "../blocks/math.h" -#define TAG "SubGhzProtocolChamb_Code" +#define TAG "SubGhzProtocolChambCode" #define CHAMBERLAIN_CODE_BIT_STOP 0b0001 #define CHAMBERLAIN_CODE_BIT_1 0b0011 @@ -155,7 +155,7 @@ static bool break; default: - furi_crash(TAG " unknown protocol."); + FURI_LOG_E(TAG, "Invalid bits count"); return false; break; } @@ -196,41 +196,46 @@ static bool break; } - instance->encoder.size_upload = subghz_protocol_blocks_get_upload( + instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array( upload_hex_data, upload_hex_count_bit, instance->encoder.upload, instance->encoder.size_upload, - subghz_protocol_chamb_code_const.te_short); + subghz_protocol_chamb_code_const.te_short, + SubGhzProtocolBlockAlignBitLeft); 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 < + 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); - subghz_protocol_encoder_chamb_code_get_upload(instance); + 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) { @@ -280,9 +285,9 @@ static bool subghz_protocol_chamb_code_to_bit(uint64_t* data, uint8_t size) { uint64_t data_tmp = data[0]; uint64_t data_res = 0; for(uint8_t i = 0; i < size; i++) { - if((data_tmp & 0xF) == CHAMBERLAIN_CODE_BIT_0) { + if((data_tmp & 0xFll) == CHAMBERLAIN_CODE_BIT_0) { bit_write(data_res, i, 0); - } else if((data_tmp & 0xF) == CHAMBERLAIN_CODE_BIT_1) { + } else if((data_tmp & 0xFll) == CHAMBERLAIN_CODE_BIT_1) { bit_write(data_res, i, 1); } else { return false; @@ -424,34 +429,36 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderChamb_Code* instance = context; 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 < + 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; } -void subghz_protocol_decoder_chamb_code_get_string(void* context, string_t output) { +void subghz_protocol_decoder_chamb_code_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderChamb_Code* instance = context; @@ -462,7 +469,7 @@ void subghz_protocol_decoder_chamb_code_get_string(void* context, string_t outpu uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - string_cat_printf( + furi_string_cat_printf( output, "%s %db\r\n" "Key:0x%03lX\r\n" @@ -474,19 +481,19 @@ void subghz_protocol_decoder_chamb_code_get_string(void* context, string_t outpu switch(instance->generic.data_count_bit) { case 7: - string_cat_printf( + furi_string_cat_printf( output, "DIP:" CHAMBERLAIN_7_CODE_DIP_PATTERN "\r\n", CHAMBERLAIN_7_CODE_DATA_TO_DIP(code_found_lo)); break; case 8: - string_cat_printf( + furi_string_cat_printf( output, "DIP:" CHAMBERLAIN_8_CODE_DIP_PATTERN "\r\n", CHAMBERLAIN_8_CODE_DATA_TO_DIP(code_found_lo)); break; case 9: - string_cat_printf( + furi_string_cat_printf( output, "DIP:" CHAMBERLAIN_9_CODE_DIP_PATTERN "\r\n", CHAMBERLAIN_9_CODE_DATA_TO_DIP(code_found_lo)); diff --git a/lib/subghz/protocols/chamberlain_code.h b/lib/subghz/protocols/chamberlain_code.h index 1ac2f9f9624..c8ffed5c506 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. @@ -83,25 +84,26 @@ uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderChamb_Code. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_chamb_code_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_chamb_code_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance * @param output Resulting text */ -void subghz_protocol_decoder_chamb_code_get_string(void* context, string_t output); +void subghz_protocol_decoder_chamb_code_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/clemsa.c b/lib/subghz/protocols/clemsa.c new file mode 100644 index 00000000000..a0547a11372 --- /dev/null +++ b/lib/subghz/protocols/clemsa.c @@ -0,0 +1,356 @@ +#include "clemsa.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +// protocol BERNER / ELKA / TEDSEN / TELETASTER +#define TAG "SubGhzProtocolClemsa" + +#define DIP_P 0b11 //(+) +#define DIP_O 0b10 //(0) +#define DIP_N 0b00 //(-) + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c" +#define SHOW_DIP_P(dip, check_dip) \ + ((((dip >> 0xE) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xC) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xA) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x8) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x6) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x4) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x2) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x0) & 0x3) == check_dip) ? '*' : '_') + +static const SubGhzBlockConst subghz_protocol_clemsa_const = { + .te_short = 385, + .te_long = 2695, + .te_delta = 150, + .min_count_bit_for_found = 18, +}; + +struct SubGhzProtocolDecoderClemsa { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderClemsa { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + ClemsaDecoderStepReset = 0, + ClemsaDecoderStepSaveDuration, + ClemsaDecoderStepCheckDuration, +} ClemsaDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_clemsa_decoder = { + .alloc = subghz_protocol_decoder_clemsa_alloc, + .free = subghz_protocol_decoder_clemsa_free, + + .feed = subghz_protocol_decoder_clemsa_feed, + .reset = subghz_protocol_decoder_clemsa_reset, + + .get_hash_data = subghz_protocol_decoder_clemsa_get_hash_data, + .serialize = subghz_protocol_decoder_clemsa_serialize, + .deserialize = subghz_protocol_decoder_clemsa_deserialize, + .get_string = subghz_protocol_decoder_clemsa_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_clemsa_encoder = { + .alloc = subghz_protocol_encoder_clemsa_alloc, + .free = subghz_protocol_encoder_clemsa_free, + + .deserialize = subghz_protocol_encoder_clemsa_deserialize, + .stop = subghz_protocol_encoder_clemsa_stop, + .yield = subghz_protocol_encoder_clemsa_yield, +}; + +const SubGhzProtocol subghz_protocol_clemsa = { + .name = SUBGHZ_PROTOCOL_CLEMSA_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_clemsa_decoder, + .encoder = &subghz_protocol_clemsa_encoder, +}; + +void* subghz_protocol_encoder_clemsa_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderClemsa* instance = malloc(sizeof(SubGhzProtocolEncoderClemsa)); + + instance->base.protocol = &subghz_protocol_clemsa; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 52; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_clemsa_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderClemsa* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderClemsa instance + * @return true On success + */ +static bool subghz_protocol_encoder_clemsa_get_upload(SubGhzProtocolEncoderClemsa* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_clemsa_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_clemsa_const.te_long); + } + } + if(bit_read(instance->generic.data, 0)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_long); + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_clemsa_const.te_short + + subghz_protocol_clemsa_const.te_long * 7); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_clemsa_const.te_long + + subghz_protocol_clemsa_const.te_long * 7); + } + return true; +} + +SubGhzProtocolStatus + subghz_protocol_encoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderClemsa* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + 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)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } + instance->encoder.is_running = true; + + } while(false); + + return ret; +} + +void subghz_protocol_encoder_clemsa_stop(void* context) { + SubGhzProtocolEncoderClemsa* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_clemsa_yield(void* context) { + SubGhzProtocolEncoderClemsa* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_clemsa_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderClemsa* instance = malloc(sizeof(SubGhzProtocolDecoderClemsa)); + instance->base.protocol = &subghz_protocol_clemsa; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_clemsa_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + free(instance); +} + +void subghz_protocol_decoder_clemsa_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + instance->decoder.parser_step = ClemsaDecoderStepReset; +} + +void subghz_protocol_decoder_clemsa_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + + switch(instance->decoder.parser_step) { + case ClemsaDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short * 51) < + subghz_protocol_clemsa_const.te_delta * 25)) { + instance->decoder.parser_step = ClemsaDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + break; + + case ClemsaDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = ClemsaDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = ClemsaDecoderStepReset; + } + break; + + case ClemsaDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_clemsa_const.te_short) < + subghz_protocol_clemsa_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_long) < + subghz_protocol_clemsa_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = ClemsaDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_clemsa_const.te_long) < + subghz_protocol_clemsa_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short) < + subghz_protocol_clemsa_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = ClemsaDecoderStepSaveDuration; + } else if( + DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short * 51) < + subghz_protocol_clemsa_const.te_delta * 25) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_clemsa_const.te_short) < + subghz_protocol_clemsa_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_clemsa_const.te_long) < + subghz_protocol_clemsa_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.parser_step = ClemsaDecoderStepReset; + } + + if(instance->decoder.decode_count_bit == + subghz_protocol_clemsa_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.parser_step = ClemsaDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + + } else { + instance->decoder.parser_step = ClemsaDecoderStepReset; + } + } else { + instance->decoder.parser_step = ClemsaDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_clemsa_check_remote_controller(SubGhzBlockGeneric* instance) { + instance->serial = (instance->data >> 2) & 0xFFFF; + instance->btn = (instance->data & 0x03); +} + +uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus subghz_protocol_decoder_clemsa_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + subghz_protocol_decoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + 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) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + subghz_protocol_clemsa_check_remote_controller(&instance->generic); + //uint32_t data = (uint32_t)(instance->generic.data & 0xFFFFFF); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%05lX Btn %X\r\n" + " +: " DIP_PATTERN "\r\n" + " o: " DIP_PATTERN "\r\n" + " -: " DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0x3FFFF), + instance->generic.btn, + SHOW_DIP_P(instance->generic.serial, DIP_P), + SHOW_DIP_P(instance->generic.serial, DIP_O), + SHOW_DIP_P(instance->generic.serial, DIP_N)); +} diff --git a/lib/subghz/protocols/clemsa.h b/lib/subghz/protocols/clemsa.h new file mode 100644 index 00000000000..f14cd3dace0 --- /dev/null +++ b/lib/subghz/protocols/clemsa.h @@ -0,0 +1,109 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_CLEMSA_NAME "Clemsa" + +typedef struct SubGhzProtocolDecoderClemsa SubGhzProtocolDecoderClemsa; +typedef struct SubGhzProtocolEncoderClemsa SubGhzProtocolEncoderClemsa; + +extern const SubGhzProtocolDecoder subghz_protocol_clemsa_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_clemsa_encoder; +extern const SubGhzProtocol subghz_protocol_clemsa; + +/** + * Allocate SubGhzProtocolEncoderClemsa. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderClemsa* pointer to a SubGhzProtocolEncoderClemsa instance + */ +void* subghz_protocol_encoder_clemsa_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderClemsa. + * @param context Pointer to a SubGhzProtocolEncoderClemsa instance + */ +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 status + */ +SubGhzProtocolStatus + subghz_protocol_encoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderClemsa instance + */ +void subghz_protocol_encoder_clemsa_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderClemsa instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_clemsa_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderClemsa. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderClemsa* pointer to a SubGhzProtocolDecoderClemsa instance + */ +void* subghz_protocol_decoder_clemsa_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderClemsa. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + */ +void subghz_protocol_decoder_clemsa_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderClemsa. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + */ +void subghz_protocol_decoder_clemsa_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_clemsa_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderClemsa. + * @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 status + */ +SubGhzProtocolStatus subghz_protocol_decoder_clemsa_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderClemsa. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_decoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + * @param output Resulting text + */ +void subghz_protocol_decoder_clemsa_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/doitrand.c b/lib/subghz/protocols/doitrand.c index 9a0a58190df..69b8bba4ae1 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); - subghz_protocol_encoder_doitrand_get_upload(instance); + 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,42 +310,34 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderDoitrand* instance = context; 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, string_t output) { +void subghz_protocol_decoder_doitrand_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderDoitrand* instance = context; subghz_protocol_doitrand_check_remote_controller(&instance->generic); - string_cat_printf( + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:%02lX%08lX\r\n" - "Btn:%lX\r\n" + "Btn:%X\r\n" "DIP:" DIP_PATTERN "\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, diff --git a/lib/subghz/protocols/doitrand.h b/lib/subghz/protocols/doitrand.h index f94d7389903..5dbc6678fdc 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. @@ -83,25 +84,26 @@ uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderDoitrand. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_doitrand_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_doitrand_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance * @param output Resulting text */ -void subghz_protocol_decoder_doitrand_get_string(void* context, string_t output); +void subghz_protocol_decoder_doitrand_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/dooya.c b/lib/subghz/protocols/dooya.c new file mode 100644 index 00000000000..47e95209e54 --- /dev/null +++ b/lib/subghz/protocols/dooya.c @@ -0,0 +1,437 @@ +#include "dooya.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolDooya" + +#define DOYA_SINGLE_CHANNEL 0xFF + +static const SubGhzBlockConst subghz_protocol_dooya_const = { + .te_short = 366, + .te_long = 733, + .te_delta = 120, + .min_count_bit_for_found = 40, +}; + +struct SubGhzProtocolDecoderDooya { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderDooya { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + DooyaDecoderStepReset = 0, + DooyaDecoderStepFoundStartBit, + DooyaDecoderStepSaveDuration, + DooyaDecoderStepCheckDuration, +} DooyaDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_dooya_decoder = { + .alloc = subghz_protocol_decoder_dooya_alloc, + .free = subghz_protocol_decoder_dooya_free, + + .feed = subghz_protocol_decoder_dooya_feed, + .reset = subghz_protocol_decoder_dooya_reset, + + .get_hash_data = subghz_protocol_decoder_dooya_get_hash_data, + .serialize = subghz_protocol_decoder_dooya_serialize, + .deserialize = subghz_protocol_decoder_dooya_deserialize, + .get_string = subghz_protocol_decoder_dooya_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_dooya_encoder = { + .alloc = subghz_protocol_encoder_dooya_alloc, + .free = subghz_protocol_encoder_dooya_free, + + .deserialize = subghz_protocol_encoder_dooya_deserialize, + .stop = subghz_protocol_encoder_dooya_stop, + .yield = subghz_protocol_encoder_dooya_yield, +}; + +const SubGhzProtocol subghz_protocol_dooya = { + .name = SUBGHZ_PROTOCOL_DOOYA_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_dooya_decoder, + .encoder = &subghz_protocol_dooya_encoder, +}; + +void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderDooya* instance = malloc(sizeof(SubGhzProtocolEncoderDooya)); + + instance->base.protocol = &subghz_protocol_dooya; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_dooya_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderDooya* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderDooya instance + * @return true On success + */ +static bool subghz_protocol_encoder_dooya_get_upload(SubGhzProtocolEncoderDooya* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send header + if(bit_read(instance->generic.data, 0)) { + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_dooya_const.te_long * 12 + + subghz_protocol_dooya_const.te_long); + } else { + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_dooya_const.te_long * 12 + + subghz_protocol_dooya_const.te_short); + } + + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short * 13); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long * 2); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long); + } + } + return true; +} + +SubGhzProtocolStatus + subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderDooya* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + 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)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } + instance->encoder.is_running = true; + } while(false); + + return ret; +} + +void subghz_protocol_encoder_dooya_stop(void* context) { + SubGhzProtocolEncoderDooya* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_dooya_yield(void* context) { + SubGhzProtocolEncoderDooya* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_dooya_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderDooya* instance = malloc(sizeof(SubGhzProtocolDecoderDooya)); + instance->base.protocol = &subghz_protocol_dooya; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_dooya_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + free(instance); +} + +void subghz_protocol_decoder_dooya_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + instance->decoder.parser_step = DooyaDecoderStepReset; +} + +void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + + switch(instance->decoder.parser_step) { + case DooyaDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 12) < + subghz_protocol_dooya_const.te_delta * 20)) { + instance->decoder.parser_step = DooyaDecoderStepFoundStartBit; + } + break; + + case DooyaDecoderStepFoundStartBit: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 2) < + subghz_protocol_dooya_const.te_delta * 3) { + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + } else if( + DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short * 13) < + subghz_protocol_dooya_const.te_delta * 5) { + break; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + + case DooyaDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = DooyaDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + + case DooyaDecoderStepCheckDuration: + if(!level) { + if(duration >= (subghz_protocol_dooya_const.te_long * 4)) { + //add last bit + if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else if( + DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + break; + } + instance->decoder.parser_step = DooyaDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit == + subghz_protocol_dooya_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGeneric* instance) { + /* + * serial s/m ch key + * long press down X * E1DC030533, 40b 111000011101110000000011 0000 0101 0011 0011 + * + * short press down 3 * E1DC030533, 40b 111000011101110000000011 0000 0101 0011 0011 + * 3 * E1DC03053C, 40b 111000011101110000000011 0000 0101 0011 1100 + * + * press stop X * E1DC030555, 40b 111000011101110000000011 0000 0101 0101 0101 + * + * long press up X * E1DC030511, 40b 111000011101110000000011 0000 0101 0001 0001 + * + * short press up 3 * E1DC030511, 40b 111000011101110000000011 0000 0101 0001 0001 + * 3 * E1DC03051E, 40b 111000011101110000000011 0000 0101 0001 1110 + * + * serial: 3 byte serial number + * s/m: single (b0000) / multi (b0001) channel console + * ch: channel if single (always b0101) or multi + * key: 0b00010001 - long press up + * 0b00011110 - short press up + * 0b00110011 - long press down + * 0b00111100 - short press down + * 0b01010101 - press stop + * 0b01111001 - press up + down + * 0b10000000 - press up + stop + * 0b10000001 - press down + stop + * 0b11001100 - press P2 + * +*/ + + instance->serial = (instance->data >> 16); + if((instance->data >> 12) & 0x0F) { + instance->cnt = (instance->data >> 8) & 0x0F; + } else { + instance->cnt = DOYA_SINGLE_CHANNEL; + } + instance->btn = instance->data & 0xFF; +} + +uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus subghz_protocol_decoder_dooya_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, subghz_protocol_dooya_const.min_count_bit_for_found); +} + +/** + * Get button name. + * @param btn Button number, 8 bit + */ +static const char* subghz_protocol_dooya_get_name_button(uint8_t btn) { + const char* btn_name; + switch(btn) { + case 0b00010001: + btn_name = "Up_Long"; + break; + case 0b00011110: + btn_name = "Up_Short"; + break; + case 0b00110011: + btn_name = "Down_Long"; + break; + case 0b00111100: + btn_name = "Down_Short"; + break; + case 0b01010101: + btn_name = "Stop"; + break; + case 0b01111001: + btn_name = "Up+Down"; + break; + case 0b10000000: + btn_name = "Up+Stop"; + break; + case 0b10000001: + btn_name = "Down+Stop"; + break; + case 0b11001100: + btn_name = "P2"; + break; + default: + btn_name = "Unknown"; + break; + } + return btn_name; +} + +void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + + subghz_protocol_somfy_telis_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%010llX\r\n" + "Sn:0x%08lX\r\n" + "Btn:%s\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + instance->generic.data, + instance->generic.serial, + subghz_protocol_dooya_get_name_button(instance->generic.btn)); + if(instance->generic.cnt == DOYA_SINGLE_CHANNEL) { + furi_string_cat_printf(output, "Ch:Single\r\n"); + } else { + furi_string_cat_printf(output, "Ch:%lu\r\n", instance->generic.cnt); + } +} diff --git a/lib/subghz/protocols/dooya.h b/lib/subghz/protocols/dooya.h new file mode 100644 index 00000000000..ffe9d41eff7 --- /dev/null +++ b/lib/subghz/protocols/dooya.h @@ -0,0 +1,109 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_DOOYA_NAME "Dooya" + +typedef struct SubGhzProtocolDecoderDooya SubGhzProtocolDecoderDooya; +typedef struct SubGhzProtocolEncoderDooya SubGhzProtocolEncoderDooya; + +extern const SubGhzProtocolDecoder subghz_protocol_dooya_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_dooya_encoder; +extern const SubGhzProtocol subghz_protocol_dooya; + +/** + * Allocate SubGhzProtocolEncoderDooya. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderDooya* pointer to a SubGhzProtocolEncoderDooya instance + */ +void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderDooya. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + */ +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 status + */ +SubGhzProtocolStatus + subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + */ +void subghz_protocol_encoder_dooya_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_dooya_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderDooya. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderDooya* pointer to a SubGhzProtocolDecoderDooya instance + */ +void* subghz_protocol_decoder_dooya_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + */ +void subghz_protocol_decoder_dooya_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + */ +void subghz_protocol_decoder_dooya_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderDooya. + * @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 status + */ +SubGhzProtocolStatus subghz_protocol_decoder_dooya_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param output Resulting text + */ +void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/faac_slh.c b/lib/subghz/protocols/faac_slh.c index 8b90f471c21..cdee7cd9f27 100644 --- a/lib/subghz/protocols/faac_slh.c +++ b/lib/subghz/protocols/faac_slh.c @@ -6,7 +6,7 @@ #include "../blocks/generic.h" #include "../blocks/math.h" -#define TAG "SubGhzProtocolFaacSHL" +#define TAG "SubGhzProtocolFaacShl" static const SubGhzBlockConst subghz_protocol_faac_slh_const = { .te_short = 255, @@ -180,34 +180,26 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderFaacSLH* instance = context; return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); } -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 ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_faac_slh_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_faac_slh_const.min_count_bit_for_found); } -void subghz_protocol_decoder_faac_slh_get_string(void* context, string_t output) { +void subghz_protocol_decoder_faac_slh_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderFaacSLH* instance = context; subghz_protocol_faac_slh_check_remote_controller(&instance->generic); @@ -216,13 +208,13 @@ void subghz_protocol_decoder_faac_slh_get_string(void* context, string_t output) uint32_t code_fix = code_found_reverse & 0xFFFFFFFF; uint32_t code_hop = (code_found_reverse >> 32) & 0xFFFFFFFF; - string_cat_printf( + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:%lX%08lX\r\n" "Fix:%08lX \r\n" "Hop:%08lX \r\n" - "Sn:%07lX Btn:%lX\r\n", + "Sn:%07lX Btn:%X\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), diff --git a/lib/subghz/protocols/faac_slh.h b/lib/subghz/protocols/faac_slh.h index fe7a79265ba..9d9a9e4044e 100644 --- a/lib/subghz/protocols/faac_slh.h +++ b/lib/subghz/protocols/faac_slh.h @@ -49,25 +49,26 @@ uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderFaacSLH. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_faac_slh_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_faac_slh_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance * @param output Resulting text */ -void subghz_protocol_decoder_faac_slh_get_string(void* context, string_t output); +void subghz_protocol_decoder_faac_slh_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/gate_tx.c b/lib/subghz/protocols/gate_tx.c index d7efb3862cb..2ebd6bb03b9 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); - subghz_protocol_encoder_gate_tx_get_upload(instance); + 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) { @@ -227,7 +227,7 @@ void subghz_protocol_decoder_gate_tx_feed(void* context, bool level, uint32_t du if(duration >= ((uint32_t)subghz_protocol_gate_tx_const.te_short * 10 + subghz_protocol_gate_tx_const.te_delta)) { instance->decoder.parser_step = GateTXDecoderStepFoundStartBit; - if(instance->decoder.decode_count_bit >= + if(instance->decoder.decode_count_bit == subghz_protocol_gate_tx_const.min_count_bit_for_found) { instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; @@ -290,42 +290,32 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderGateTx* instance = context; 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, string_t output) { +void subghz_protocol_decoder_gate_tx_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderGateTx* instance = context; subghz_protocol_gate_tx_check_remote_controller(&instance->generic); - string_cat_printf( + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:%06lX\r\n" - "Sn:%05lX Btn:%lX\r\n", + "Sn:%05lX Btn:%X\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data & 0xFFFFFF), diff --git a/lib/subghz/protocols/gate_tx.h b/lib/subghz/protocols/gate_tx.h index 17157681699..a6abede0d7f 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. @@ -83,25 +84,26 @@ uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderGateTx. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_gate_tx_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_gate_tx_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderGateTx instance * @param output Resulting text */ -void subghz_protocol_decoder_gate_tx_get_string(void* context, string_t output); +void subghz_protocol_decoder_gate_tx_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/holtek.c b/lib/subghz/protocols/holtek.c index 137ba85d388..294bd124d3c 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); - subghz_protocol_encoder_holtek_get_upload(instance); + 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) { @@ -240,7 +240,6 @@ void subghz_protocol_decoder_holtek_feed(void* context, bool level, uint32_t dur if(!level) { if(duration >= ((uint32_t)subghz_protocol_holtek_const.te_short * 10 + subghz_protocol_holtek_const.te_delta)) { - instance->decoder.parser_step = HoltekDecoderStepSaveDuration; if(instance->decoder.decode_count_bit == subghz_protocol_holtek_const.min_count_bit_for_found) { if((instance->decoder.decode_data & HOLTEK_HEADER_MASK) == HOLTEK_HEADER) { @@ -323,39 +322,29 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderHoltek* instance = context; 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, string_t output) { +void subghz_protocol_decoder_holtek_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderHoltek* instance = context; subghz_protocol_holtek_check_remote_controller(&instance->generic); - string_cat_printf( + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:0x%lX%08lX\r\n" @@ -368,8 +357,8 @@ void subghz_protocol_decoder_holtek_get_string(void* context, string_t output) { instance->generic.btn >> 4); if((instance->generic.btn & 0xF) == 0xE) { - string_cat_printf(output, "ON\r\n"); + furi_string_cat_printf(output, "ON\r\n"); } else if(((instance->generic.btn & 0xF) == 0xB)) { - string_cat_printf(output, "OFF\r\n"); + furi_string_cat_printf(output, "OFF\r\n"); } } diff --git a/lib/subghz/protocols/holtek.h b/lib/subghz/protocols/holtek.h index df7dd6448b4..19081308d3d 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. @@ -83,25 +84,26 @@ uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderHoltek. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_holtek_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_holtek_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderHoltek instance * @param output Resulting text */ -void subghz_protocol_decoder_holtek_get_string(void* context, string_t output); +void subghz_protocol_decoder_holtek_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/holtek_ht12x.c b/lib/subghz/protocols/holtek_ht12x.c new file mode 100644 index 00000000000..302b78598b9 --- /dev/null +++ b/lib/subghz/protocols/holtek_ht12x.c @@ -0,0 +1,405 @@ +#include "holtek_ht12x.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * Help + * https://www.holtek.com/documents/10179/116711/HT12A_Ev130.pdf + * + */ + +#define TAG "SubGhzProtocolHoltekHt12x" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c" +#define CNT_TO_DIP(dip) \ + (dip & 0x0080 ? '0' : '1'), (dip & 0x0040 ? '0' : '1'), (dip & 0x0020 ? '0' : '1'), \ + (dip & 0x0010 ? '0' : '1'), (dip & 0x0008 ? '0' : '1'), (dip & 0x0004 ? '0' : '1'), \ + (dip & 0x0002 ? '0' : '1'), (dip & 0x0001 ? '0' : '1') + +static const SubGhzBlockConst subghz_protocol_holtek_th12x_const = { + .te_short = 320, + .te_long = 640, + .te_delta = 200, + .min_count_bit_for_found = 12, +}; + +struct SubGhzProtocolDecoderHoltek_HT12X { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint32_t te; + uint32_t last_data; +}; + +struct SubGhzProtocolEncoderHoltek_HT12X { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + uint32_t te; +}; + +typedef enum { + Holtek_HT12XDecoderStepReset = 0, + Holtek_HT12XDecoderStepFoundStartBit, + Holtek_HT12XDecoderStepSaveDuration, + Holtek_HT12XDecoderStepCheckDuration, +} Holtek_HT12XDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_holtek_th12x_decoder = { + .alloc = subghz_protocol_decoder_holtek_th12x_alloc, + .free = subghz_protocol_decoder_holtek_th12x_free, + + .feed = subghz_protocol_decoder_holtek_th12x_feed, + .reset = subghz_protocol_decoder_holtek_th12x_reset, + + .get_hash_data = subghz_protocol_decoder_holtek_th12x_get_hash_data, + .serialize = subghz_protocol_decoder_holtek_th12x_serialize, + .deserialize = subghz_protocol_decoder_holtek_th12x_deserialize, + .get_string = subghz_protocol_decoder_holtek_th12x_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_holtek_th12x_encoder = { + .alloc = subghz_protocol_encoder_holtek_th12x_alloc, + .free = subghz_protocol_encoder_holtek_th12x_free, + + .deserialize = subghz_protocol_encoder_holtek_th12x_deserialize, + .stop = subghz_protocol_encoder_holtek_th12x_stop, + .yield = subghz_protocol_encoder_holtek_th12x_yield, +}; + +const SubGhzProtocol subghz_protocol_holtek_th12x = { + .name = SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_holtek_th12x_decoder, + .encoder = &subghz_protocol_holtek_th12x_encoder, +}; + +void* subghz_protocol_encoder_holtek_th12x_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderHoltek_HT12X* instance = + malloc(sizeof(SubGhzProtocolEncoderHoltek_HT12X)); + + instance->base.protocol = &subghz_protocol_holtek_th12x; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_holtek_th12x_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderHoltek_HT12X* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + * @return true On success + */ +static bool + subghz_protocol_encoder_holtek_th12x_get_upload(SubGhzProtocolEncoderHoltek_HT12X* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send header + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te * 36); + //Send start bit + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)instance->te * 2); + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)instance->te * 2); + } + } + return true; +} + +SubGhzProtocolStatus + subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderHoltek_HT12X* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + 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; + } + //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)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } + instance->encoder.is_running = true; + } while(false); + + return ret; +} + +void subghz_protocol_encoder_holtek_th12x_stop(void* context) { + SubGhzProtocolEncoderHoltek_HT12X* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_holtek_th12x_yield(void* context) { + SubGhzProtocolEncoderHoltek_HT12X* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_holtek_th12x_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderHoltek_HT12X* instance = + malloc(sizeof(SubGhzProtocolDecoderHoltek_HT12X)); + instance->base.protocol = &subghz_protocol_holtek_th12x; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_holtek_th12x_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + free(instance); +} + +void subghz_protocol_decoder_holtek_th12x_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; +} + +void subghz_protocol_decoder_holtek_th12x_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + + switch(instance->decoder.parser_step) { + case Holtek_HT12XDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short * 36) < + subghz_protocol_holtek_th12x_const.te_delta * 36)) { + //Found Preambula + instance->decoder.parser_step = Holtek_HT12XDecoderStepFoundStartBit; + } + break; + case Holtek_HT12XDecoderStepFoundStartBit: + if((level) && (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short) < + subghz_protocol_holtek_th12x_const.te_delta)) { + //Found StartBit + instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = duration; + } else { + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; + } + break; + case Holtek_HT12XDecoderStepSaveDuration: + //save duration + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_holtek_th12x_const.te_short * 10 + + subghz_protocol_holtek_th12x_const.te_delta)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_holtek_th12x_const.min_count_bit_for_found) { + if((instance->last_data == instance->decoder.decode_data) && + instance->last_data) { + instance->te /= (instance->decoder.decode_count_bit * 3 + 1); + + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->last_data = instance->decoder.decode_data; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = 0; + instance->decoder.parser_step = Holtek_HT12XDecoderStepFoundStartBit; + break; + } else { + instance->decoder.te_last = duration; + instance->te += duration; + instance->decoder.parser_step = Holtek_HT12XDecoderStepCheckDuration; + } + } else { + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; + } + break; + case Holtek_HT12XDecoderStepCheckDuration: + if(level) { + instance->te += duration; + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_holtek_th12x_const.te_long) < + subghz_protocol_holtek_th12x_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short) < + subghz_protocol_holtek_th12x_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_holtek_th12x_const.te_short) < + subghz_protocol_holtek_th12x_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_long) < + subghz_protocol_holtek_th12x_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; + } + } else { + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_holtek_th12x_check_remote_controller(SubGhzBlockGeneric* instance) { + instance->btn = instance->data & 0x0F; + instance->cnt = (instance->data >> 4) & 0xFF; +} + +uint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus subghz_protocol_decoder_holtek_th12x_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + 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"); + ret = SubGhzProtocolStatusErrorParserTe; + } + return ret; +} + +SubGhzProtocolStatus + subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + 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; + } + } while(false); + return ret; +} + +static void subghz_protocol_holtek_th12x_event_serialize(uint8_t event, FuriString* output) { + furi_string_cat_printf( + output, + "%s%s%s%s\r\n", + (((event >> 3) & 0x1) == 0x0 ? "B1 " : ""), + (((event >> 2) & 0x1) == 0x0 ? "B2 " : ""), + (((event >> 1) & 0x1) == 0x0 ? "B3 " : ""), + (((event >> 0) & 0x1) == 0x0 ? "B4 " : "")); +} + +void subghz_protocol_decoder_holtek_th12x_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + subghz_protocol_holtek_th12x_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%03lX\r\n" + "Btn: ", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFF)); + subghz_protocol_holtek_th12x_event_serialize(instance->generic.btn, output); + furi_string_cat_printf( + output, + "DIP:" DIP_PATTERN "\r\n" + "Te:%luus\r\n", + CNT_TO_DIP(instance->generic.cnt), + instance->te); +} diff --git a/lib/subghz/protocols/holtek_ht12x.h b/lib/subghz/protocols/holtek_ht12x.h new file mode 100644 index 00000000000..500c061aa3c --- /dev/null +++ b/lib/subghz/protocols/holtek_ht12x.h @@ -0,0 +1,109 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME "Holtek_HT12X" + +typedef struct SubGhzProtocolDecoderHoltek_HT12X SubGhzProtocolDecoderHoltek_HT12X; +typedef struct SubGhzProtocolEncoderHoltek_HT12X SubGhzProtocolEncoderHoltek_HT12X; + +extern const SubGhzProtocolDecoder subghz_protocol_holtek_th12x_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_holtek_th12x_encoder; +extern const SubGhzProtocol subghz_protocol_holtek_th12x; + +/** + * Allocate SubGhzProtocolEncoderHoltek_HT12X. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderHoltek_HT12X* pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + */ +void* subghz_protocol_encoder_holtek_th12x_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + */ +void subghz_protocol_encoder_holtek_th12x_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + */ +void subghz_protocol_encoder_holtek_th12x_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_holtek_th12x_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderHoltek_HT12X. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderHoltek_HT12X* pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + */ +void* subghz_protocol_decoder_holtek_th12x_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + */ +void subghz_protocol_decoder_holtek_th12x_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + */ +void subghz_protocol_decoder_holtek_th12x_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_holtek_th12x_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status + */ +SubGhzProtocolStatus subghz_protocol_decoder_holtek_th12x_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @param output Resulting text + */ +void subghz_protocol_decoder_holtek_th12x_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/honeywell_wdb.c b/lib/subghz/protocols/honeywell_wdb.c index e1e21426d2a..fcf2822011c 100644 --- a/lib/subghz/protocols/honeywell_wdb.c +++ b/lib/subghz/protocols/honeywell_wdb.c @@ -6,7 +6,7 @@ #include "../blocks/generic.h" #include "../blocks/math.h" -#define TAG "SubGhzProtocolHoneywellWDB" +#define TAG "SubGhzProtocolHoneywellWdb" /* * @@ -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); - subghz_protocol_encoder_honeywell_wdb_get_upload(instance); + 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,47 +344,38 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderHoneywell_WDB* instance = context; 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, string_t output) { +void subghz_protocol_decoder_honeywell_wdb_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderHoneywell_WDB* instance = context; subghz_protocol_honeywell_wdb_check_remote_controller(instance); - string_cat_printf( + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:0x%lX%08lX\r\n" "Sn:0x%05lX\r\n" "DT:%s Al:%s\r\n" - "SK:%01lX R:%01lX LBat:%01lX\r\n", + "SK:%01X R:%01X LBat:%01X\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)((instance->generic.data >> 32) & 0xFFFFFFFF), diff --git a/lib/subghz/protocols/honeywell_wdb.h b/lib/subghz/protocols/honeywell_wdb.h index 012b3699674..91728691b05 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. @@ -85,27 +84,26 @@ uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderHoneywell_WDB. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_honeywell_wdb_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_honeywell_wdb_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance * @param output Resulting text */ -void subghz_protocol_decoder_honeywell_wdb_get_string(void* context, string_t output); +void subghz_protocol_decoder_honeywell_wdb_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/hormann.c b/lib/subghz/protocols/hormann.c index 0197f59e6b5..fc490e9d1c7 100644 --- a/lib/subghz/protocols/hormann.c +++ b/lib/subghz/protocols/hormann.c @@ -6,7 +6,9 @@ #include "../blocks/generic.h" #include "../blocks/math.h" -#define TAG "SubGhzProtocolHormannHSM" +#define TAG "SubGhzProtocolHormannHsm" + +#define HORMANN_HSM_PATTERN 0xFF000000003 static const SubGhzBlockConst subghz_protocol_hormann_const = { .te_short = 500, @@ -101,20 +103,13 @@ static bool subghz_protocol_encoder_hormann_get_upload(SubGhzProtocolEncoderHorm furi_assert(instance); size_t index = 0; - size_t size_upload = 3 + (instance->generic.data_count_bit * 2 + 2) * 20 + 1; + size_t size_upload = (instance->generic.data_count_bit * 2 + 2) * 20 + 1; if(size_upload > instance->encoder.size_upload) { FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); return false; } else { instance->encoder.size_upload = size_upload; } - //Send header - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_hormann_const.te_short * 64); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_short * 64); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_hormann_const.te_short * 64); instance->encoder.repeat = 10; //original remote does 10 repeats for(size_t repeat = 0; repeat < 20; repeat++) { @@ -145,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); - subghz_protocol_encoder_hormann_get_upload(instance); + 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) { @@ -209,6 +204,10 @@ void subghz_protocol_decoder_hormann_free(void* context) { free(instance); } +static bool subghz_protocol_decoder_hormann_check_pattern(SubGhzProtocolDecoderHormann* instance) { + return (instance->decoder.decode_data & HORMANN_HSM_PATTERN) == HORMANN_HSM_PATTERN; +} + void subghz_protocol_decoder_hormann_reset(void* context) { furi_assert(context); SubGhzProtocolDecoderHormann* instance = context; @@ -221,25 +220,9 @@ void subghz_protocol_decoder_hormann_feed(void* context, bool level, uint32_t du switch(instance->decoder.parser_step) { case HormannDecoderStepReset: - if((level) && (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_short * 64) < - subghz_protocol_hormann_const.te_delta * 64)) { - instance->decoder.parser_step = HormannDecoderStepFoundStartHeader; - } - break; - case HormannDecoderStepFoundStartHeader: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_short * 64) < - subghz_protocol_hormann_const.te_delta * 64)) { - instance->decoder.parser_step = HormannDecoderStepFoundHeader; - } else { - instance->decoder.parser_step = HormannDecoderStepReset; - } - break; - case HormannDecoderStepFoundHeader: if((level) && (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_short * 24) < subghz_protocol_hormann_const.te_delta * 24)) { instance->decoder.parser_step = HormannDecoderStepFoundStartBit; - } else { - instance->decoder.parser_step = HormannDecoderStepReset; } break; case HormannDecoderStepFoundStartBit: @@ -254,7 +237,8 @@ void subghz_protocol_decoder_hormann_feed(void* context, bool level, uint32_t du break; case HormannDecoderStepSaveDuration: if(level) { //save interval - if(duration >= (subghz_protocol_hormann_const.te_short * 5)) { + if(duration >= (subghz_protocol_hormann_const.te_short * 5) && + subghz_protocol_decoder_hormann_check_pattern(instance)) { instance->decoder.parser_step = HormannDecoderStepFoundStartBit; if(instance->decoder.decode_count_bit >= subghz_protocol_hormann_const.min_count_bit_for_found) { @@ -311,39 +295,29 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderHormann* instance = context; 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, string_t output) { +void subghz_protocol_decoder_hormann_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderHormann* instance = context; subghz_protocol_hormann_check_remote_controller(&instance->generic); - string_cat_printf( + furi_string_cat_printf( output, "%s\r\n" "%dbit\r\n" diff --git a/lib/subghz/protocols/hormann.h b/lib/subghz/protocols/hormann.h index 45d6eb22379..8cb45aec328 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. @@ -83,25 +84,26 @@ uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderHormann. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_hormann_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_hormann_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderHormann instance * @param output Resulting text */ -void subghz_protocol_decoder_hormann_get_string(void* context, string_t output); +void subghz_protocol_decoder_hormann_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/ido.c b/lib/subghz/protocols/ido.c index 91446844582..e96e6566b5f 100644 --- a/lib/subghz/protocols/ido.c +++ b/lib/subghz/protocols/ido.c @@ -6,7 +6,7 @@ #include "../blocks/generic.h" #include "../blocks/math.h" -#define TAG "SubGhzProtocol_iDo_117/111" +#define TAG "SubGhzProtocolIdo117/111" static const SubGhzBlockConst subghz_protocol_ido_const = { .te_short = 450, @@ -179,33 +179,24 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderIDo* instance = context; 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, string_t output) { +void subghz_protocol_decoder_ido_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderIDo* instance = context; @@ -215,13 +206,13 @@ void subghz_protocol_decoder_ido_get_string(void* context, string_t output) { uint32_t code_fix = code_found_reverse & 0xFFFFFF; uint32_t code_hop = (code_found_reverse >> 24) & 0xFFFFFF; - string_cat_printf( + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:0x%lX%08lX\r\n" "Fix:%06lX \r\n" "Hop:%06lX \r\n" - "Sn:%05lX Btn:%lX\r\n", + "Sn:%05lX Btn:%X\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), diff --git a/lib/subghz/protocols/ido.h b/lib/subghz/protocols/ido.h index 4fe4e740ab7..9493202466b 100644 --- a/lib/subghz/protocols/ido.h +++ b/lib/subghz/protocols/ido.h @@ -49,25 +49,26 @@ uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderIDo. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_ido_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_ido_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderIDo instance * @param output Resulting text */ -void subghz_protocol_decoder_ido_get_string(void* context, string_t output); +void subghz_protocol_decoder_ido_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/intertechno_v3.c b/lib/subghz/protocols/intertechno_v3.c new file mode 100644 index 00000000000..7fe95299555 --- /dev/null +++ b/lib/subghz/protocols/intertechno_v3.c @@ -0,0 +1,475 @@ +#include "intertechno_v3.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolIntertechnoV3" + +#define CH_PATTERN "%c%c%c%c" +#define CNT_TO_CH(ch) \ + (ch & 0x8 ? '1' : '0'), (ch & 0x4 ? '1' : '0'), (ch & 0x2 ? '1' : '0'), (ch & 0x1 ? '1' : '0') + +#define INTERTECHNO_V3_DIMMING_COUNT_BIT 36 + +static const SubGhzBlockConst subghz_protocol_intertechno_v3_const = { + .te_short = 275, + .te_long = 1375, + .te_delta = 150, + .min_count_bit_for_found = 32, +}; + +struct SubGhzProtocolDecoderIntertechno_V3 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderIntertechno_V3 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + IntertechnoV3DecoderStepReset = 0, + IntertechnoV3DecoderStepStartSync, + IntertechnoV3DecoderStepFoundSync, + IntertechnoV3DecoderStepStartDuration, + IntertechnoV3DecoderStepSaveDuration, + IntertechnoV3DecoderStepCheckDuration, + IntertechnoV3DecoderStepEndDuration, +} IntertechnoV3DecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_intertechno_v3_decoder = { + .alloc = subghz_protocol_decoder_intertechno_v3_alloc, + .free = subghz_protocol_decoder_intertechno_v3_free, + + .feed = subghz_protocol_decoder_intertechno_v3_feed, + .reset = subghz_protocol_decoder_intertechno_v3_reset, + + .get_hash_data = subghz_protocol_decoder_intertechno_v3_get_hash_data, + .serialize = subghz_protocol_decoder_intertechno_v3_serialize, + .deserialize = subghz_protocol_decoder_intertechno_v3_deserialize, + .get_string = subghz_protocol_decoder_intertechno_v3_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_intertechno_v3_encoder = { + .alloc = subghz_protocol_encoder_intertechno_v3_alloc, + .free = subghz_protocol_encoder_intertechno_v3_free, + + .deserialize = subghz_protocol_encoder_intertechno_v3_deserialize, + .stop = subghz_protocol_encoder_intertechno_v3_stop, + .yield = subghz_protocol_encoder_intertechno_v3_yield, +}; + +const SubGhzProtocol subghz_protocol_intertechno_v3 = { + .name = SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | + SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_intertechno_v3_decoder, + .encoder = &subghz_protocol_intertechno_v3_encoder, +}; + +void* subghz_protocol_encoder_intertechno_v3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderIntertechno_V3* instance = + malloc(sizeof(SubGhzProtocolEncoderIntertechno_V3)); + + instance->base.protocol = &subghz_protocol_intertechno_v3; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_intertechno_v3_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderIntertechno_V3* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance + * @return true On success + */ +static bool subghz_protocol_encoder_intertechno_v3_get_upload( + SubGhzProtocolEncoderIntertechno_V3* instance) { + furi_assert(instance); + size_t index = 0; + + //Send header + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short * 38); + //Send sync + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short * 10); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if((instance->generic.data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) && (i == 9)) { + //send bit dimm + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + } else if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_long); + } + } + instance->encoder.size_upload = index; + return true; +} + +SubGhzProtocolStatus subghz_protocol_encoder_intertechno_v3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderIntertechno_V3* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + 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)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } + instance->encoder.is_running = true; + } while(false); + + return ret; +} + +void subghz_protocol_encoder_intertechno_v3_stop(void* context) { + SubGhzProtocolEncoderIntertechno_V3* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_intertechno_v3_yield(void* context) { + SubGhzProtocolEncoderIntertechno_V3* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_intertechno_v3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderIntertechno_V3* instance = + malloc(sizeof(SubGhzProtocolDecoderIntertechno_V3)); + instance->base.protocol = &subghz_protocol_intertechno_v3; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_intertechno_v3_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + free(instance); +} + +void subghz_protocol_decoder_intertechno_v3_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; +} + +void subghz_protocol_decoder_intertechno_v3_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + switch(instance->decoder.parser_step) { + case IntertechnoV3DecoderStepReset: + if((!level) && + (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short * 37) < + subghz_protocol_intertechno_v3_const.te_delta * 15)) { + instance->decoder.parser_step = IntertechnoV3DecoderStepStartSync; + } + break; + case IntertechnoV3DecoderStepStartSync: + if(level && (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta)) { + instance->decoder.parser_step = IntertechnoV3DecoderStepFoundSync; + } else { + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } + break; + + case IntertechnoV3DecoderStepFoundSync: + if(!level && (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short * 10) < + subghz_protocol_intertechno_v3_const.te_delta * 3)) { + instance->decoder.parser_step = IntertechnoV3DecoderStepStartDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } + break; + + case IntertechnoV3DecoderStepStartDuration: + if(level && (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta)) { + instance->decoder.parser_step = IntertechnoV3DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } + break; + + case IntertechnoV3DecoderStepSaveDuration: + if(!level) { //save interval + if(duration >= (subghz_protocol_intertechno_v3_const.te_short * 11)) { + instance->decoder.parser_step = IntertechnoV3DecoderStepStartSync; + if((instance->decoder.decode_count_bit == + subghz_protocol_intertechno_v3_const.min_count_bit_for_found) || + (instance->decoder.decode_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } + instance->decoder.te_last = duration; + instance->decoder.parser_step = IntertechnoV3DecoderStepCheckDuration; + } else { + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } + break; + case IntertechnoV3DecoderStepCheckDuration: + if(level) { + //Add 0 bit + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = IntertechnoV3DecoderStepEndDuration; + } else if( + //Add 1 bit + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_intertechno_v3_const.te_long) < + subghz_protocol_intertechno_v3_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = IntertechnoV3DecoderStepEndDuration; + + } else if( + //Add dimm_state + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta) && + (instance->decoder.decode_count_bit == 27)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = IntertechnoV3DecoderStepEndDuration; + + } else + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } else { + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } + break; + + case IntertechnoV3DecoderStepEndDuration: + if(!level && ((DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta) || + (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_long) < + subghz_protocol_intertechno_v3_const.te_delta * 2))) { + instance->decoder.parser_step = IntertechnoV3DecoderStepStartDuration; + } else { + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_intertechno_v3_check_remote_controller(SubGhzBlockGeneric* instance) { + /* + * A frame is either 32 or 36 bits: + * + * _ + * start bit: | |__________ (T,10T) + * _ _ + * '0': | |_| |_____ (T,T,T,5T) + * _ _ + * '1': | |_____| |_ (T,5T,T,T) + * _ _ + * dimm: | |_| |_ (T,T,T,T) + * + * _ + * stop bit: | |____...____ (T,38T) + * + * if frame 32 bits + * SSSSSSSSSSSSSSSSSSSSSSSSSS all_ch on/off ~ch + * Key:0x3F86C59F => 00111111100001101100010110 0 1 1111 + * + * if frame 36 bits + * SSSSSSSSSSSSSSSSSSSSSSSSSS all_ch dimm ~ch dimm_level + * Key:0x42D2E8856 => 01000010110100101110100010 0 X 0101 0110 + * + */ + + if(instance->data_count_bit == subghz_protocol_intertechno_v3_const.min_count_bit_for_found) { + instance->serial = (instance->data >> 6) & 0x3FFFFFF; + if((instance->data >> 5) & 0x1) { + instance->cnt = 1 << 5; + } else { + instance->cnt = (~instance->data & 0xF); + } + instance->btn = (instance->data >> 4) & 0x1; + } else if(instance->data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) { + instance->serial = (instance->data >> 10) & 0x3FFFFFF; + if((instance->data >> 9) & 0x1) { + instance->cnt = 1 << 5; + } else { + instance->cnt = (~(instance->data >> 4) & 0xF); + } + instance->btn = (instance->data) & 0xF; + } else { + instance->serial = 0; + instance->cnt = 0; + instance->btn = 0; + } +} + +uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus subghz_protocol_decoder_intertechno_v3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus subghz_protocol_decoder_intertechno_v3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + 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; + } + } while(false); + return ret; +} + +void subghz_protocol_decoder_intertechno_v3_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + + subghz_protocol_intertechno_v3_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%.11s %db\r\n" + "Key:0x%08llX\r\n" + "Sn:%07lX\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + instance->generic.data, + instance->generic.serial); + + if(instance->generic.data_count_bit == + subghz_protocol_intertechno_v3_const.min_count_bit_for_found) { + if(instance->generic.cnt >> 5) { + furi_string_cat_printf( + output, "Ch: All Btn:%s\r\n", (instance->generic.btn ? "On" : "Off")); + } else { + furi_string_cat_printf( + output, + "Ch:" CH_PATTERN " Btn:%s\r\n", + CNT_TO_CH(instance->generic.cnt), + (instance->generic.btn ? "On" : "Off")); + } + } else if(instance->generic.data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) { + furi_string_cat_printf( + output, + "Ch:" CH_PATTERN " Dimm:%d%%\r\n", + CNT_TO_CH(instance->generic.cnt), + (int)(6.67 * (float)instance->generic.btn)); + } +} diff --git a/lib/subghz/protocols/intertechno_v3.h b/lib/subghz/protocols/intertechno_v3.h new file mode 100644 index 00000000000..4d1c24cb6b2 --- /dev/null +++ b/lib/subghz/protocols/intertechno_v3.h @@ -0,0 +1,111 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME "Intertechno_V3" + +typedef struct SubGhzProtocolDecoderIntertechno_V3 SubGhzProtocolDecoderIntertechno_V3; +typedef struct SubGhzProtocolEncoderIntertechno_V3 SubGhzProtocolEncoderIntertechno_V3; + +extern const SubGhzProtocolDecoder subghz_protocol_intertechno_v3_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_intertechno_v3_encoder; +extern const SubGhzProtocol subghz_protocol_intertechno_v3; + +/** + * Allocate SubGhzProtocolEncoderIntertechno_V3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderIntertechno_V3* pointer to a SubGhzProtocolEncoderIntertechno_V3 instance + */ +void* subghz_protocol_encoder_intertechno_v3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderIntertechno_V3. + * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance + */ +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 Starus error + */ +SubGhzProtocolStatus subghz_protocol_encoder_intertechno_v3_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance + */ +void subghz_protocol_encoder_intertechno_v3_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_intertechno_v3_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderIntertechno_V3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderIntertechno_V3* pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + */ +void* subghz_protocol_decoder_intertechno_v3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderIntertechno_V3. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + */ +void subghz_protocol_decoder_intertechno_v3_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderIntertechno_V3. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + */ +void subghz_protocol_decoder_intertechno_v3_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_intertechno_v3_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderIntertechno_V3. + * @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 Starus error + */ +SubGhzProtocolStatus subghz_protocol_decoder_intertechno_v3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderIntertechno_V3. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return Starus error + */ +SubGhzProtocolStatus subghz_protocol_decoder_intertechno_v3_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_intertechno_v3_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 88738f3fb4b..cfd8388799f 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -2,7 +2,6 @@ #include "keeloq_common.h" #include "../subghz_keystore.h" -#include #include #include "../blocks/const.h" @@ -120,10 +119,10 @@ void subghz_protocol_encoder_keeloq_free(void* context) { */ static bool subghz_protocol_keeloq_gen_data(SubGhzProtocolEncoderKeeloq* instance, uint8_t btn) { instance->generic.cnt++; - uint32_t fix = btn << 28 | instance->generic.serial; - uint32_t decrypt = btn << 28 | + uint32_t fix = (uint32_t)btn << 28 | instance->generic.serial; + uint32_t decrypt = (uint32_t)btn << 28 | (instance->generic.serial & 0x3FF) - << 16 | //ToDo in some protocols the discriminator is 0 + << 16 | // In some protocols the discriminator is 0 instance->generic.cnt; uint32_t hop = 0; uint64_t man = 0; @@ -131,7 +130,7 @@ static bool subghz_protocol_keeloq_gen_data(SubGhzProtocolEncoderKeeloq* instanc for M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { - res = strcmp(string_get_cstr(manufacture_code->name), instance->manufacture_name); + res = strcmp(furi_string_get_cstr(manufacture_code->name), instance->manufacture_name); if(res == 0) { switch(manufacture_code->type) { case KEELOQ_LEARNING_SIMPLE: @@ -150,7 +149,8 @@ static bool subghz_protocol_keeloq_gen_data(SubGhzProtocolEncoderKeeloq* instanc hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); break; case KEELOQ_LEARNING_UNKNOWN: - hop = 0; //todo + //Invalid or missing encoding type in keeloq_mfcodes + hop = 0; break; } break; @@ -174,7 +174,7 @@ bool subghz_protocol_keeloq_create_data( uint8_t btn, uint16_t cnt, const char* manufacture_name, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolEncoderKeeloq* instance = context; instance->generic.serial = serial; @@ -183,7 +183,9 @@ bool subghz_protocol_keeloq_create_data( instance->generic.data_count_bit = 64; bool res = subghz_protocol_keeloq_gen_data(instance, btn); if(res) { - res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(subghz_block_generic_serialize(&instance->generic, flipper_format, preset) != + SubGhzProtocolStatusOk) + res = false; } return res; } @@ -198,9 +200,7 @@ static bool 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 - } else { + if(!subghz_protocol_keeloq_gen_data(instance, btn)) { return false; } @@ -255,24 +255,25 @@ 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"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_keeloq_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_keeloq_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { break; } subghz_protocol_keeloq_check_remote_controller( &instance->generic, instance->keystore, &instance->manufacture_name); - if(strcmp(instance->manufacture_name, "DoorHan")) { + if(strcmp(instance->manufacture_name, "DoorHan") != 0) { + FURI_LOG_E(TAG, "Wrong manufacturer name"); + ret = SubGhzProtocolStatusErrorParserOthers; break; } @@ -280,27 +281,29 @@ bool subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* fl flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - subghz_protocol_encoder_keeloq_get_upload(instance, instance->generic.btn); - + 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}; for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF; } if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Unable to add Key"); + ret = SubGhzProtocolStatusErrorParserKey; break; } instance->encoder.is_running = true; - - res = true; } while(false); - return res; + return ret; } void subghz_protocol_encoder_keeloq_stop(void* context) { @@ -390,11 +393,14 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur subghz_protocol_keeloq_const.te_delta)) { // Found end TX instance->decoder.parser_step = KeeloqDecoderStepReset; - if(instance->decoder.decode_count_bit >= - subghz_protocol_keeloq_const.min_count_bit_for_found) { + if((instance->decoder.decode_count_bit >= + subghz_protocol_keeloq_const.min_count_bit_for_found) && + (instance->decoder.decode_count_bit <= + subghz_protocol_keeloq_const.min_count_bit_for_found + 2)) { if(instance->generic.data != instance->decoder.decode_data) { instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; + instance->generic.data_count_bit = + subghz_protocol_keeloq_const.min_count_bit_for_found; if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); } @@ -407,20 +413,24 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_keeloq_const.te_short) < subghz_protocol_keeloq_const.te_delta) && (DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_long) < - subghz_protocol_keeloq_const.te_delta)) { + subghz_protocol_keeloq_const.te_delta * 2)) { if(instance->decoder.decode_count_bit < subghz_protocol_keeloq_const.min_count_bit_for_found) { subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.decode_count_bit++; } instance->decoder.parser_step = KeeloqDecoderStepSaveDuration; } else if( (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_keeloq_const.te_long) < - subghz_protocol_keeloq_const.te_delta) && + subghz_protocol_keeloq_const.te_delta * 2) && (DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_short) < subghz_protocol_keeloq_const.te_delta)) { if(instance->decoder.decode_count_bit < subghz_protocol_keeloq_const.min_count_bit_for_found) { subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else { + instance->decoder.decode_count_bit++; } instance->decoder.parser_step = KeeloqDecoderStepSaveDuration; } else { @@ -456,6 +466,19 @@ static inline bool subghz_protocol_keeloq_check_decrypt( } return false; } +// Centurion specific check +static inline bool subghz_protocol_keeloq_check_decrypt_centurion( + SubGhzBlockGeneric* instance, + uint32_t decrypt, + uint8_t btn) { + furi_assert(instance); + + if((decrypt >> 28 == btn) && (((((uint16_t)(decrypt >> 16)) & 0x3FF) == 0x1CE))) { + instance->cnt = decrypt & 0x0000FFFF; + return true; + } + return false; +} /** * Checking the accepted code against the database manafacture key @@ -489,7 +512,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( // Simple Learning decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = string_get_cstr(manufacture_code->name); + *manufacture_name = furi_string_get_cstr(manufacture_code->name); return 1; } break; @@ -498,9 +521,16 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 man = subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = string_get_cstr(manufacture_code->name); - return 1; + if((strcmp(furi_string_get_cstr(manufacture_code->name), "Centurion") == 0)) { + if(subghz_protocol_keeloq_check_decrypt_centurion(instance, decrypt, btn)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + return 1; + } + } else { + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + return 1; + } } break; case KEELOQ_LEARNING_SECURE: @@ -508,7 +538,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( fix, seed, manufacture_code->key); decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = string_get_cstr(manufacture_code->name); + *manufacture_name = furi_string_get_cstr(manufacture_code->name); return 1; } break; @@ -517,7 +547,34 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( fix, manufacture_code->key); decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = string_get_cstr(manufacture_code->name); + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1: + man = subghz_protocol_keeloq_common_magic_serial_type1_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2: + man = subghz_protocol_keeloq_common_magic_serial_type2_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3: + man = subghz_protocol_keeloq_common_magic_serial_type3_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); return 1; } break; @@ -525,9 +582,10 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( // Simple Learning decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = string_get_cstr(manufacture_code->name); + *manufacture_name = furi_string_get_cstr(manufacture_code->name); return 1; } + // Check for mirrored man uint64_t man_rev = 0; uint64_t man_rev_byte = 0; @@ -535,18 +593,20 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( man_rev_byte = (uint8_t)(manufacture_code->key >> i); man_rev = man_rev | man_rev_byte << (56 - i); } + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = string_get_cstr(manufacture_code->name); + *manufacture_name = furi_string_get_cstr(manufacture_code->name); return 1; } + //########################### // Normal Learning // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 man = subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = string_get_cstr(manufacture_code->name); + *manufacture_name = furi_string_get_cstr(manufacture_code->name); return 1; } @@ -554,7 +614,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( man = subghz_protocol_keeloq_common_normal_learning(fix, man_rev); decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = string_get_cstr(manufacture_code->name); + *manufacture_name = furi_string_get_cstr(manufacture_code->name); return 1; } @@ -563,7 +623,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( fix, seed, manufacture_code->key); decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = string_get_cstr(manufacture_code->name); + *manufacture_name = furi_string_get_cstr(manufacture_code->name); return 1; } @@ -571,7 +631,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( man = subghz_protocol_keeloq_common_secure_learning(fix, seed, man_rev); decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = string_get_cstr(manufacture_code->name); + *manufacture_name = furi_string_get_cstr(manufacture_code->name); return 1; } @@ -580,7 +640,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( fix, manufacture_code->key); decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = string_get_cstr(manufacture_code->name); + *manufacture_name = furi_string_get_cstr(manufacture_code->name); return 1; } @@ -588,7 +648,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( man = subghz_protocol_keeloq_common_magic_xor_type1_learning(fix, man_rev); decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = string_get_cstr(manufacture_code->name); + *manufacture_name = furi_string_get_cstr(manufacture_code->name); return 1; } break; @@ -632,46 +692,36 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderKeeloq* instance = context; subghz_protocol_keeloq_check_remote_controller( &instance->generic, instance->keystore, &instance->manufacture_name); - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + SubGhzProtocolStatus res = + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - 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, "Unable to add manufacture name"); - res = false; + res = SubGhzProtocolStatusErrorParserOthers; } 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; - 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_keeloq_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - res = true; - } while(false); - - return res; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, subghz_protocol_keeloq_const.min_count_bit_for_found); } -void subghz_protocol_decoder_keeloq_get_string(void* context, string_t output) { +void subghz_protocol_decoder_keeloq_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderKeeloq* instance = context; subghz_protocol_keeloq_check_remote_controller( @@ -685,12 +735,12 @@ void subghz_protocol_decoder_keeloq_get_string(void* context, string_t output) { uint32_t code_found_reverse_hi = code_found_reverse >> 32; uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - string_cat_printf( + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:%08lX%08lX\r\n" - "Fix:0x%08lX Cnt:%04X\r\n" - "Hop:0x%08lX Btn:%01lX\r\n" + "Fix:0x%08lX Cnt:%04lX\r\n" + "Hop:0x%08lX Btn:%01X\r\n" "MF:%s\r\n" "Sn:0x%07lX \r\n", instance->generic.protocol_name, diff --git a/lib/subghz/protocols/keeloq.h b/lib/subghz/protocols/keeloq.h index e1485e5eff4..4abd14413be 100644 --- a/lib/subghz/protocols/keeloq.h +++ b/lib/subghz/protocols/keeloq.h @@ -1,6 +1,7 @@ #pragma once #include "base.h" +#include "public_api.h" #define SUBGHZ_PROTOCOL_KEELOQ_NAME "KeeLoq" @@ -24,33 +25,14 @@ void* subghz_protocol_encoder_keeloq_alloc(SubGhzEnvironment* environment); */ void subghz_protocol_encoder_keeloq_free(void* context); -/** - * Key generation from simple data. - * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param serial Serial number, 28 bit - * @param btn Button number, 4 bit - * @param cnt Container value, 16 bit - * @param manufacture_name Name of manufacturer's key - * @param preset Modulation, SubGhzPresetDefinition - * @return true On success - */ -bool subghz_protocol_keeloq_create_data( - void* context, - FlipperFormat* flipper_format, - uint32_t serial, - uint8_t btn, - uint16_t cnt, - const char* manufacture_name, - SubGhzPresetDefinition* preset); - /** * 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. @@ -103,25 +85,26 @@ uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderKeeloq. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return SubGhzProtocolStatus */ -bool subghz_protocol_decoder_keeloq_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_keeloq_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance * @param output Resulting text */ -void subghz_protocol_decoder_keeloq_get_string(void* context, string_t output); +void subghz_protocol_decoder_keeloq_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/keeloq_common.c b/lib/subghz/protocols/keeloq_common.c index 7e864a325bd..041494f90a3 100644 --- a/lib/subghz/protocols/keeloq_common.c +++ b/lib/subghz/protocols/keeloq_common.c @@ -2,9 +2,12 @@ #include -#include #include +#define bit(x, n) (((x) >> (n)) & 1) +#define g5(x, a, b, c, d, e) \ + (bit(x, a) + bit(x, b) * 2 + bit(x, c) * 4 + bit(x, d) * 8 + bit(x, e) * 16) + /** Simple Learning Encrypt * @param data - 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter * @param key - manufacture (64bit) @@ -20,7 +23,7 @@ inline uint32_t subghz_protocol_keeloq_common_encrypt(const uint32_t data, const } /** Simple Learning Decrypt - * @param data - keelog encrypt data + * @param data - keeloq encrypt data * @param key - manufacture (64bit) * @return 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter */ @@ -82,3 +85,43 @@ inline uint64_t data &= 0x0FFFFFFF; return (((uint64_t)data << 32) | data) ^ xor; } + +/** Magic_serial_type1 Learning + * @param data - serial number (28bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +inline uint64_t + subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man) { + return (man & 0xFFFFFFFF) | ((uint64_t)data << 40) | + ((uint64_t)(((data & 0xff) + ((data >> 8) & 0xFF)) & 0xFF) << 32); +} + +/** Magic_serial_type2 Learning + * @param data - btn+serial number (32bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +inline uint64_t + subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man) { + uint8_t* p = (uint8_t*)&data; + uint8_t* m = (uint8_t*)&man; + m[7] = p[0]; + m[6] = p[1]; + m[5] = p[2]; + m[4] = p[3]; + return man; +} + +/** Magic_serial_type3 Learning + * @param data - serial number (24bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +inline uint64_t + subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man) { + return (man & 0xFFFFFFFFFF000000) | (data & 0xFFFFFF); +} diff --git a/lib/subghz/protocols/keeloq_common.h b/lib/subghz/protocols/keeloq_common.h index 50d447ed725..df3d0dbf367 100644 --- a/lib/subghz/protocols/keeloq_common.h +++ b/lib/subghz/protocols/keeloq_common.h @@ -11,9 +11,6 @@ * */ #define KEELOQ_NLF 0x3A5C742E -#define bit(x, n) (((x) >> (n)) & 1) -#define g5(x, a, b, c, d, e) \ - (bit(x, a) + bit(x, b) * 2 + bit(x, c) * 4 + bit(x, d) * 8 + bit(x, e) * 16) /* * KeeLoq learning types @@ -24,6 +21,9 @@ #define KEELOQ_LEARNING_NORMAL 2u #define KEELOQ_LEARNING_SECURE 3u #define KEELOQ_LEARNING_MAGIC_XOR_TYPE_1 4u +#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1 5u +#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2 6u +#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3 7u /** * Simple Learning Encrypt @@ -66,3 +66,27 @@ uint64_t * @return manufacture for this serial number (64bit) */ uint64_t subghz_protocol_keeloq_common_magic_xor_type1_learning(uint32_t data, uint64_t xor); + +/** Magic_serial_type1 Learning + * @param data - serial number (28bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +uint64_t subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man); + +/** Magic_serial_type2 Learning + * @param data - btn+serial number (32bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +uint64_t subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man); + +/** Magic_serial_type3 Learning + * @param data - btn+serial number (32bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +uint64_t subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man); diff --git a/lib/subghz/protocols/kia.c b/lib/subghz/protocols/kia.c index 6fc1061704e..9b63271b49d 100644 --- a/lib/subghz/protocols/kia.c +++ b/lib/subghz/protocols/kia.c @@ -6,7 +6,7 @@ #include "../blocks/generic.h" #include "../blocks/math.h" -#define TAG "SubGhzProtocoKIA" +#define TAG "SubGhzProtocoKia" static const SubGhzBlockConst subghz_protocol_kia_const = { .te_short = 250, @@ -142,7 +142,7 @@ void subghz_protocol_decoder_kia_feed(void* context, bool level, uint32_t durati case KIADecoderStepSaveDuration: if(level) { if(duration >= - (uint32_t)(subghz_protocol_kia_const.te_long + subghz_protocol_kia_const.te_delta * 2)) { + (subghz_protocol_kia_const.te_long + subghz_protocol_kia_const.te_delta * 2UL)) { //Found stop bit instance->decoder.parser_step = KIADecoderStepReset; if(instance->decoder.decode_count_bit == @@ -230,33 +230,24 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderKIA* instance = context; 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, string_t output) { +void subghz_protocol_decoder_kia_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderKIA* instance = context; @@ -264,11 +255,11 @@ void subghz_protocol_decoder_kia_get_string(void* context, string_t output) { uint32_t code_found_hi = instance->generic.data >> 32; uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - string_cat_printf( + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:%08lX%08lX\r\n" - "Sn:%07lX Btn:%lX Cnt:%04X\r\n", + "Sn:%07lX Btn:%X Cnt:%04lX\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, code_found_hi, diff --git a/lib/subghz/protocols/kia.h b/lib/subghz/protocols/kia.h index 743ab7cbdc3..749ff8afd27 100644 --- a/lib/subghz/protocols/kia.h +++ b/lib/subghz/protocols/kia.h @@ -49,25 +49,26 @@ uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderKIA. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_kia_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_kia_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderKIA instance * @param output Resulting text */ -void subghz_protocol_decoder_kia_get_string(void* context, string_t output); +void subghz_protocol_decoder_kia_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/kinggates_stylo_4k.c b/lib/subghz/protocols/kinggates_stylo_4k.c new file mode 100644 index 00000000000..0b2a102c49a --- /dev/null +++ b/lib/subghz/protocols/kinggates_stylo_4k.c @@ -0,0 +1,336 @@ +#include "kinggates_stylo_4k.h" +#include "keeloq_common.h" + +#include "../subghz_keystore.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocoKingGatesStylo4k" + +static const SubGhzBlockConst subghz_protocol_kinggates_stylo_4k_const = { + .te_short = 400, + .te_long = 1100, + .te_delta = 140, + .min_count_bit_for_found = 89, +}; + +struct SubGhzProtocolDecoderKingGates_stylo_4k { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint64_t data; + uint16_t header_count; + SubGhzKeystore* keystore; +}; + +struct SubGhzProtocolEncoderKingGates_stylo_4k { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + KingGates_stylo_4kDecoderStepReset = 0, + KingGates_stylo_4kDecoderStepCheckPreambula, + KingGates_stylo_4kDecoderStepCheckStartBit, + KingGates_stylo_4kDecoderStepSaveDuration, + KingGates_stylo_4kDecoderStepCheckDuration, +} KingGates_stylo_4kDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder = { + .alloc = subghz_protocol_decoder_kinggates_stylo_4k_alloc, + .free = subghz_protocol_decoder_kinggates_stylo_4k_free, + + .feed = subghz_protocol_decoder_kinggates_stylo_4k_feed, + .reset = subghz_protocol_decoder_kinggates_stylo_4k_reset, + + .get_hash_data = subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data, + .serialize = subghz_protocol_decoder_kinggates_stylo_4k_serialize, + .deserialize = subghz_protocol_decoder_kinggates_stylo_4k_deserialize, + .get_string = subghz_protocol_decoder_kinggates_stylo_4k_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol subghz_protocol_kinggates_stylo_4k = { + .name = SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &subghz_protocol_kinggates_stylo_4k_decoder, + .encoder = &subghz_protocol_kinggates_stylo_4k_encoder, +}; + +void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderKingGates_stylo_4k* instance = + malloc(sizeof(SubGhzProtocolDecoderKingGates_stylo_4k)); + instance->base.protocol = &subghz_protocol_kinggates_stylo_4k; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + return instance; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + free(instance); +} + +void subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + + switch(instance->decoder.parser_step) { + case KingGates_stylo_4kDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckPreambula; + instance->header_count++; + } + break; + case KingGates_stylo_4kDecoderStepCheckPreambula: + if((!level) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta)) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + break; + } + if((instance->header_count > 2) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long * 2) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) { + // Found header + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckStartBit; + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepCheckStartBit: + if((level) && + DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short * 2) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepSaveDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long * 3)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) { + instance->generic.data = instance->data; + instance->data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckDuration; + } + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_long) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + if(instance->decoder.decode_count_bit == 53) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param file_name Full path to rainbow table the file + */ +static void subghz_protocol_kinggates_stylo_4k_remote_controller( + SubGhzBlockGeneric* instance, + uint64_t data, + SubGhzKeystore* keystore) { + /** + * 9500us 12*(400/400) 2200/800|1-bit|0-bit| + * _ _ _ __ ___ _ + * ________| |_| |_..._| |_____| |_| |___| |..... + * + * 1-bit 400/1100 us + * 0-bit 1100/400 us + * + * The package consists of 89 bits of data, LSB first + * Data - 1C9037F0C80000 CE280BA00 + * S[3] S[2] 1 key S[1] S[0] 2 byte always 0 Hop[3] Hop[2] Hop[1] Hop[0] 0 + * 11100100 10000001 1 0111 11110000 11001000 00000000 00000000 11001110 00101000 00001011 10100000 0000 + * + * Encryption - keeloq Simple Learning + * key C S[3] CNT + * Decrypt - 0xEC270B9C => 0x E C 27 0B9C + * + * + * +*/ + + uint32_t hop = subghz_protocol_blocks_reverse_key(data >> 4, 32); + uint64_t fix = subghz_protocol_blocks_reverse_key(instance->data, 53); + bool ret = false; + uint32_t decrypt = 0; + instance->btn = (fix >> 17) & 0x0F; + instance->serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF); + + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + if(manufacture_code->type == KEELOQ_LEARNING_SIMPLE) { + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(((decrypt >> 28) == instance->btn) && (((decrypt >> 24) & 0x0F) == 0x0C) && + (((decrypt >> 16) & 0xFF) == (instance->serial & 0xFF))) { + ret = true; + break; + } + } + } + if(ret) { + instance->cnt = decrypt & 0xFFFF; + } else { + instance->btn = 0; + instance->serial = 0; + instance->cnt = 0; + } +} + +uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus subghz_protocol_decoder_kinggates_stylo_4k_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + 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->data >> (i * 8)) & 0xFF; + } + + if((ret == SubGhzProtocolStatusOk) && + !flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Data"); + ret = SubGhzProtocolStatusErrorParserOthers; + } + return ret; +} + +SubGhzProtocolStatus subghz_protocol_decoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + 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->data = instance->data << 8 | key_data[i]; + } + } while(false); + return ret; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + subghz_protocol_kinggates_stylo_4k_remote_controller( + &instance->generic, instance->data, instance->keystore); + + furi_string_cat_printf( + output, + "%s\r\n" + "Key:0x%llX%07llX %dbit\r\n" + "Sn:0x%08lX Btn:0x%01X\r\n" + "Cnt:0x%04lX\r\n", + instance->generic.protocol_name, + instance->generic.data, + instance->data, + instance->generic.data_count_bit, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt); +} diff --git a/lib/subghz/protocols/kinggates_stylo_4k.h b/lib/subghz/protocols/kinggates_stylo_4k.h new file mode 100644 index 00000000000..64cbb904636 --- /dev/null +++ b/lib/subghz/protocols/kinggates_stylo_4k.h @@ -0,0 +1,74 @@ +#pragma once +#include "base.h" + +#define SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME "KingGates Stylo4k" + +typedef struct SubGhzProtocolDecoderKingGates_stylo_4k SubGhzProtocolDecoderKingGates_stylo_4k; +typedef struct SubGhzProtocolEncoderKingGates_stylo_4k SubGhzProtocolEncoderKingGates_stylo_4k; + +extern const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder; +extern const SubGhzProtocol subghz_protocol_kinggates_stylo_4k; + +/** + * Allocate SubGhzProtocolDecoderKingGates_stylo_4k. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderKingGates_stylo_4k* pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void subghz_protocol_decoder_kinggates_stylo_4k_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderKingGates_stylo_4k. + * @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 status + */ +SubGhzProtocolStatus subghz_protocol_decoder_kinggates_stylo_4k_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus subghz_protocol_decoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param output Resulting text + */ +void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/linear.c b/lib/subghz/protocols/linear.c index 92ba02a8fbd..8d37357966b 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); - subghz_protocol_encoder_linear_get_upload(instance); + 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,34 +300,24 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderLinear* instance = context; 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, string_t output) { +void subghz_protocol_decoder_linear_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderLinear* instance = context; @@ -338,7 +328,7 @@ void subghz_protocol_decoder_linear_get_string(void* context, string_t output) { uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - string_cat_printf( + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:0x%08lX\r\n" diff --git a/lib/subghz/protocols/linear.h b/lib/subghz/protocols/linear.h index 035b130c44d..b692b911c1e 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. @@ -83,25 +84,26 @@ uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderLinear. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_linear_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_linear_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderLinear instance * @param output Resulting text */ -void subghz_protocol_decoder_linear_get_string(void* context, string_t output); +void subghz_protocol_decoder_linear_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/linear_delta3.c b/lib/subghz/protocols/linear_delta3.c new file mode 100644 index 00000000000..97ac5cc5a65 --- /dev/null +++ b/lib/subghz/protocols/linear_delta3.c @@ -0,0 +1,349 @@ +#include "linear_delta3.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolLinearDelta3" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c" +#define DATA_TO_DIP(dip) \ + (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), \ + (dip & 0x0010 ? '1' : '0'), (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), \ + (dip & 0x0002 ? '1' : '0'), (dip & 0x0001 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_linear_delta3_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 8, +}; + +struct SubGhzProtocolDecoderLinearDelta3 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint32_t last_data; +}; + +struct SubGhzProtocolEncoderLinearDelta3 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + LinearDecoderStepReset = 0, + LinearDecoderStepSaveDuration, + LinearDecoderStepCheckDuration, +} LinearDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder = { + .alloc = subghz_protocol_decoder_linear_delta3_alloc, + .free = subghz_protocol_decoder_linear_delta3_free, + + .feed = subghz_protocol_decoder_linear_delta3_feed, + .reset = subghz_protocol_decoder_linear_delta3_reset, + + .get_hash_data = subghz_protocol_decoder_linear_delta3_get_hash_data, + .serialize = subghz_protocol_decoder_linear_delta3_serialize, + .deserialize = subghz_protocol_decoder_linear_delta3_deserialize, + .get_string = subghz_protocol_decoder_linear_delta3_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder = { + .alloc = subghz_protocol_encoder_linear_delta3_alloc, + .free = subghz_protocol_encoder_linear_delta3_free, + + .deserialize = subghz_protocol_encoder_linear_delta3_deserialize, + .stop = subghz_protocol_encoder_linear_delta3_stop, + .yield = subghz_protocol_encoder_linear_delta3_yield, +}; + +const SubGhzProtocol subghz_protocol_linear_delta3 = { + .name = SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_linear_delta3_decoder, + .encoder = &subghz_protocol_linear_delta3_encoder, +}; + +void* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderLinearDelta3* instance = + malloc(sizeof(SubGhzProtocolEncoderLinearDelta3)); + + instance->base.protocol = &subghz_protocol_linear_delta3; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 16; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_linear_delta3_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderLinearDelta3* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @return true On success + */ +static bool + subghz_protocol_encoder_linear_delta3_get_upload(SubGhzProtocolEncoderLinearDelta3* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 7); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + } + } + //Send end bit + if(bit_read(instance->generic.data, 0)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_short); + //Send PT_GUARD + instance->encoder.upload[index] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 73); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + //Send PT_GUARD + instance->encoder.upload[index] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 70); + } + + return true; +} + +SubGhzProtocolStatus subghz_protocol_encoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderLinearDelta3* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + 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)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } + instance->encoder.is_running = true; + } while(false); + + return ret; +} + +void subghz_protocol_encoder_linear_delta3_stop(void* context) { + SubGhzProtocolEncoderLinearDelta3* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context) { + SubGhzProtocolEncoderLinearDelta3* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_linear_delta3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderLinearDelta3* instance = + malloc(sizeof(SubGhzProtocolDecoderLinearDelta3)); + instance->base.protocol = &subghz_protocol_linear_delta3; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_linear_delta3_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + free(instance); +} + +void subghz_protocol_decoder_linear_delta3_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + instance->decoder.parser_step = LinearDecoderStepReset; + instance->last_data = 0; +} + +void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + switch(instance->decoder.parser_step) { + case LinearDecoderStepReset: + if((!level) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 70) < + subghz_protocol_linear_delta3_const.te_delta * 24)) { + //Found header Linear + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } + break; + case LinearDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = LinearDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + break; + case LinearDecoderStepCheckDuration: + if(!level) { + if(duration >= (subghz_protocol_linear_delta3_const.te_short * 10)) { + instance->decoder.parser_step = LinearDecoderStepReset; + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) < + subghz_protocol_linear_delta3_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } + if(instance->decoder.decode_count_bit == + subghz_protocol_linear_delta3_const.min_count_bit_for_found) { + if((instance->last_data == instance->decoder.decode_data) && + instance->last_data) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + instance->last_data = instance->decoder.decode_data; + } + break; + } + + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) < + subghz_protocol_linear_delta3_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 7) < + subghz_protocol_linear_delta3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8)); +} + +SubGhzProtocolStatus subghz_protocol_decoder_linear_delta3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus subghz_protocol_decoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + 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) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + + uint32_t data = instance->generic.data & 0xFF; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + data, + DATA_TO_DIP(data)); +} diff --git a/lib/subghz/protocols/linear_delta3.h b/lib/subghz/protocols/linear_delta3.h new file mode 100644 index 00000000000..22f6730f441 --- /dev/null +++ b/lib/subghz/protocols/linear_delta3.h @@ -0,0 +1,109 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME "LinearDelta3" + +typedef struct SubGhzProtocolDecoderLinearDelta3 SubGhzProtocolDecoderLinearDelta3; +typedef struct SubGhzProtocolEncoderLinearDelta3 SubGhzProtocolEncoderLinearDelta3; + +extern const SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder; +extern const SubGhzProtocol subghz_protocol_linear_delta3; + +/** + * Allocate SubGhzProtocolEncoderLinearDelta3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderLinearDelta3* pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +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 status + */ +SubGhzProtocolStatus + subghz_protocol_encoder_linear_delta3_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void subghz_protocol_encoder_linear_delta3_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderLinearDelta3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderLinearDelta3* pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void* subghz_protocol_decoder_linear_delta3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void subghz_protocol_decoder_linear_delta3_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void subghz_protocol_decoder_linear_delta3_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderLinearDelta3. + * @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 status + */ +SubGhzProtocolStatus subghz_protocol_decoder_linear_delta3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_decoder_linear_delta3_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_linear_delta3_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/magellan.c b/lib/subghz/protocols/magellan.c new file mode 100644 index 00000000000..1b8eccc36e3 --- /dev/null +++ b/lib/subghz/protocols/magellan.c @@ -0,0 +1,437 @@ +#include "magellan.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolMagellan" + +static const SubGhzBlockConst subghz_protocol_magellan_const = { + .te_short = 200, + .te_long = 400, + .te_delta = 100, + .min_count_bit_for_found = 32, +}; + +struct SubGhzProtocolDecoderMagellan { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + uint16_t header_count; +}; + +struct SubGhzProtocolEncoderMagellan { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + MagellanDecoderStepReset = 0, + MagellanDecoderStepCheckPreambula, + MagellanDecoderStepFoundPreambula, + MagellanDecoderStepSaveDuration, + MagellanDecoderStepCheckDuration, +} MagellanDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_magellan_decoder = { + .alloc = subghz_protocol_decoder_magellan_alloc, + .free = subghz_protocol_decoder_magellan_free, + + .feed = subghz_protocol_decoder_magellan_feed, + .reset = subghz_protocol_decoder_magellan_reset, + + .get_hash_data = subghz_protocol_decoder_magellan_get_hash_data, + .serialize = subghz_protocol_decoder_magellan_serialize, + .deserialize = subghz_protocol_decoder_magellan_deserialize, + .get_string = subghz_protocol_decoder_magellan_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_magellan_encoder = { + .alloc = subghz_protocol_encoder_magellan_alloc, + .free = subghz_protocol_encoder_magellan_free, + + .deserialize = subghz_protocol_encoder_magellan_deserialize, + .stop = subghz_protocol_encoder_magellan_stop, + .yield = subghz_protocol_encoder_magellan_yield, +}; + +const SubGhzProtocol subghz_protocol_magellan = { + .name = SUBGHZ_PROTOCOL_MAGELLAN_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_magellan_decoder, + .encoder = &subghz_protocol_magellan_encoder, +}; + +void* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderMagellan* instance = malloc(sizeof(SubGhzProtocolEncoderMagellan)); + + instance->base.protocol = &subghz_protocol_magellan; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_magellan_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderMagellan* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderMagellan instance + * @return true On success + */ +static bool subghz_protocol_encoder_magellan_get_upload(SubGhzProtocolEncoderMagellan* instance) { + furi_assert(instance); + + size_t index = 0; + + //Send header + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short * 4); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); + for(uint8_t i = 0; i < 12; i++) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); + } + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); + + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long * 3); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); + } + } + + //Send stop bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long * 100); + + instance->encoder.size_upload = index; + return true; +} + +SubGhzProtocolStatus + subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderMagellan* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + 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)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } + instance->encoder.is_running = true; + } while(false); + + return ret; +} + +void subghz_protocol_encoder_magellan_stop(void* context) { + SubGhzProtocolEncoderMagellan* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_magellan_yield(void* context) { + SubGhzProtocolEncoderMagellan* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_magellan_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderMagellan* instance = malloc(sizeof(SubGhzProtocolDecoderMagellan)); + instance->base.protocol = &subghz_protocol_magellan; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_magellan_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + free(instance); +} + +void subghz_protocol_decoder_magellan_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + instance->decoder.parser_step = MagellanDecoderStepReset; +} + +uint8_t subghz_protocol_magellan_crc8(uint8_t* data, size_t len) { + uint8_t crc = 0x00; + size_t i, j; + for(i = 0; i < len; i++) { + crc ^= data[i]; + for(j = 0; j < 8; j++) { + if((crc & 0x80) != 0) + crc = (uint8_t)((crc << 1) ^ 0x31); + else + crc <<= 1; + } + } + return crc; +} + +static bool subghz_protocol_magellan_check_crc(SubGhzProtocolDecoderMagellan* instance) { + uint8_t data[3] = { + instance->decoder.decode_data >> 24, + instance->decoder.decode_data >> 16, + instance->decoder.decode_data >> 8}; + return (instance->decoder.decode_data & 0xFF) == + subghz_protocol_magellan_crc8(data, sizeof(data)); +} + +void subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + + switch(instance->decoder.parser_step) { + case MagellanDecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta)) { + instance->decoder.parser_step = MagellanDecoderStepCheckPreambula; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + + case MagellanDecoderStepCheckPreambula: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta)) { + // Found header + instance->header_count++; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta * 2) && + (instance->header_count > 10)) { + instance->decoder.parser_step = MagellanDecoderStepFoundPreambula; + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + } + break; + + case MagellanDecoderStepFoundPreambula: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_magellan_const.te_short * 6) < + subghz_protocol_magellan_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta * 2)) { + instance->decoder.parser_step = MagellanDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + } + break; + + case MagellanDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = MagellanDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + break; + + case MagellanDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = MagellanDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = MagellanDecoderStepSaveDuration; + } else if(duration >= (subghz_protocol_magellan_const.te_long * 3)) { + //Found stop bit + if((instance->decoder.decode_count_bit == + subghz_protocol_magellan_const.min_count_bit_for_found) && + subghz_protocol_magellan_check_crc(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = MagellanDecoderStepReset; + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_magellan_check_remote_controller(SubGhzBlockGeneric* instance) { + /* +* package 32b data 24b CRC8 +* 0x037AE4828 => 001101111010111001001000 00101000 +* +* 0x037AE48 (flipped in reverse bit sequence) => 0x1275EC +* +* 0x1275EC => 0x12-event codes, 0x75EC-serial (dec 117236) +* +* event codes +* bit_0: 1-Open/Motion, 0-close/ok +* bit_1: 1-Tamper On (alarm), 0-Tamper Off (ok) +* bit_2: ? +* bit_3: 1-power on +* bit_4: model type - wireless reed +* bit_5: model type - motion sensor +* bit_6: ? +* bit_7: ? +* +*/ + uint64_t data_rev = subghz_protocol_blocks_reverse_key(instance->data >> 8, 24); + instance->serial = data_rev & 0xFFFF; + instance->btn = (data_rev >> 16) & 0xFF; +} + +static void subghz_protocol_magellan_get_event_serialize(uint8_t event, FuriString* output) { + furi_string_cat_printf( + output, + "%s%s%s%s%s%s%s%s", + ((event >> 4) & 0x1 ? (event & 0x1 ? " Open" : " Close") : + (event & 0x1 ? " Motion" : " Ok")), + ((event >> 1) & 0x1 ? ", Tamper On\n(Alarm)" : ""), + ((event >> 2) & 0x1 ? ", ?" : ""), + ((event >> 3) & 0x1 ? ", Power On" : ""), + ((event >> 4) & 0x1 ? ", MT:Wireless_Reed" : ""), + ((event >> 5) & 0x1 ? ", MT:Motion_Sensor" : ""), + ((event >> 6) & 0x1 ? ", ?" : ""), + ((event >> 7) & 0x1 ? ", ?" : "")); +} + +uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus subghz_protocol_decoder_magellan_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + 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) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + subghz_protocol_magellan_check_remote_controller(&instance->generic); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%08lX\r\n" + "Sn:%03ld%03ld, Event:0x%02X\r\n" + "Stat:", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + (instance->generic.serial >> 8) & 0xFF, + instance->generic.serial & 0xFF, + instance->generic.btn); + + subghz_protocol_magellan_get_event_serialize(instance->generic.btn, output); +} diff --git a/lib/subghz/protocols/magellan.h b/lib/subghz/protocols/magellan.h new file mode 100644 index 00000000000..e0fb7ca52a0 --- /dev/null +++ b/lib/subghz/protocols/magellan.h @@ -0,0 +1,109 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_MAGELLAN_NAME "Magellan" + +typedef struct SubGhzProtocolDecoderMagellan SubGhzProtocolDecoderMagellan; +typedef struct SubGhzProtocolEncoderMagellan SubGhzProtocolEncoderMagellan; + +extern const SubGhzProtocolDecoder subghz_protocol_magellan_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_magellan_encoder; +extern const SubGhzProtocol subghz_protocol_magellan; + +/** + * Allocate SubGhzProtocolEncoderMagellan. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderMagellan* pointer to a SubGhzProtocolEncoderMagellan instance + */ +void* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderMagellan. + * @param context Pointer to a SubGhzProtocolEncoderMagellan instance + */ +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 status + */ +SubGhzProtocolStatus + subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderMagellan instance + */ +void subghz_protocol_encoder_magellan_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderMagellan instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_magellan_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderMagellan. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderMagellan* pointer to a SubGhzProtocolDecoderMagellan instance + */ +void* subghz_protocol_decoder_magellan_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderMagellan. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + */ +void subghz_protocol_decoder_magellan_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderMagellan. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + */ +void subghz_protocol_decoder_magellan_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderMagellan. + * @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 status + */ +SubGhzProtocolStatus subghz_protocol_decoder_magellan_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderMagellan. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @param output Resulting text + */ +void subghz_protocol_decoder_magellan_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/marantec.c b/lib/subghz/protocols/marantec.c index bdce6593dbd..fc4aa0dca4e 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,44 +343,36 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderMarantec* instance = context; 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, string_t output) { +void subghz_protocol_decoder_marantec_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderMarantec* instance = context; subghz_protocol_marantec_remote_controller(&instance->generic); - string_cat_printf( + furi_string_cat_printf( output, "%s %db\r\n" "Key:0x%lX%08lX\r\n" "Sn:0x%07lX \r\n" - "Btn:%lX\r\n", + "Btn:%X\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), diff --git a/lib/subghz/protocols/marantec.h b/lib/subghz/protocols/marantec.h index 2da3c88a570..485c563b2fb 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. @@ -83,25 +84,26 @@ uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderMarantec. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_marantec_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_marantec_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderMarantec instance * @param output Resulting text */ -void subghz_protocol_decoder_marantec_get_string(void* context, string_t output); +void subghz_protocol_decoder_marantec_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/mastercode.c b/lib/subghz/protocols/mastercode.c new file mode 100644 index 00000000000..54ad5bfaa44 --- /dev/null +++ b/lib/subghz/protocols/mastercode.c @@ -0,0 +1,360 @@ +#include "mastercode.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +// protocol MASTERCODE Clemsa MV1/MV12 +#define TAG "SubGhzProtocolMastercode" + +#define DIP_P 0b11 //(+) +#define DIP_O 0b10 //(0) +#define DIP_N 0b00 //(-) + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c" + +#define SHOW_DIP_P(dip, check_dip) \ + ((((dip >> 0x0) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x2) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x4) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x6) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x8) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xA) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xC) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xE) & 0x3) == check_dip) ? '*' : '_') + +static const SubGhzBlockConst subghz_protocol_mastercode_const = { + .te_short = 1072, + .te_long = 2145, + .te_delta = 150, + .min_count_bit_for_found = 36, +}; + +struct SubGhzProtocolDecoderMastercode { + SubGhzProtocolDecoderBase base; + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderMastercode { + SubGhzProtocolEncoderBase base; + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + MastercodeDecoderStepReset = 0, + MastercodeDecoderStepSaveDuration, + MastercodeDecoderStepCheckDuration, +} MastercodeDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_mastercode_decoder = { + .alloc = subghz_protocol_decoder_mastercode_alloc, + .free = subghz_protocol_decoder_mastercode_free, + + .feed = subghz_protocol_decoder_mastercode_feed, + .reset = subghz_protocol_decoder_mastercode_reset, + + .get_hash_data = subghz_protocol_decoder_mastercode_get_hash_data, + .serialize = subghz_protocol_decoder_mastercode_serialize, + .deserialize = subghz_protocol_decoder_mastercode_deserialize, + .get_string = subghz_protocol_decoder_mastercode_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_mastercode_encoder = { + .alloc = subghz_protocol_encoder_mastercode_alloc, + .free = subghz_protocol_encoder_mastercode_free, + + .deserialize = subghz_protocol_encoder_mastercode_deserialize, + .stop = subghz_protocol_encoder_mastercode_stop, + .yield = subghz_protocol_encoder_mastercode_yield, +}; + +const SubGhzProtocol subghz_protocol_mastercode = { + .name = SUBGHZ_PROTOCOL_MASTERCODE_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_mastercode_decoder, + .encoder = &subghz_protocol_mastercode_encoder, +}; + +void* subghz_protocol_encoder_mastercode_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderMastercode* instance = malloc(sizeof(SubGhzProtocolEncoderMastercode)); + + instance->base.protocol = &subghz_protocol_mastercode; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 72; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_mastercode_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderMastercode* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderMastercode instance + * @return true On success + */ +static bool + subghz_protocol_encoder_mastercode_get_upload(SubGhzProtocolEncoderMastercode* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_mastercode_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_mastercode_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_mastercode_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_mastercode_const.te_long); + } + } + if(bit_read(instance->generic.data, 0)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_mastercode_const.te_long); + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_mastercode_const.te_short + + subghz_protocol_mastercode_const.te_short * 13); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_mastercode_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_mastercode_const.te_long + + subghz_protocol_mastercode_const.te_short * 13); + } + return true; +} + +SubGhzProtocolStatus + subghz_protocol_encoder_mastercode_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderMastercode* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_mastercode_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_mastercode_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } + instance->encoder.is_running = true; + + } while(false); + + return ret; +} + +void subghz_protocol_encoder_mastercode_stop(void* context) { + SubGhzProtocolEncoderMastercode* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_mastercode_yield(void* context) { + SubGhzProtocolEncoderMastercode* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_mastercode_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderMastercode* instance = malloc(sizeof(SubGhzProtocolDecoderMastercode)); + instance->base.protocol = &subghz_protocol_mastercode; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_mastercode_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMastercode* instance = context; + free(instance); +} + +void subghz_protocol_decoder_mastercode_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMastercode* instance = context; + instance->decoder.parser_step = MastercodeDecoderStepReset; +} + +void subghz_protocol_decoder_mastercode_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderMastercode* instance = context; + + switch(instance->decoder.parser_step) { + case MastercodeDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_mastercode_const.te_short * 15) < + subghz_protocol_mastercode_const.te_delta * 15)) { + instance->decoder.parser_step = MastercodeDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + break; + + case MastercodeDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = MastercodeDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = MastercodeDecoderStepReset; + } + break; + + case MastercodeDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_mastercode_const.te_short) < + subghz_protocol_mastercode_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_mastercode_const.te_long) < + subghz_protocol_mastercode_const.te_delta * 8)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = MastercodeDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_mastercode_const.te_long) < + subghz_protocol_mastercode_const.te_delta * 8) && + (DURATION_DIFF(duration, subghz_protocol_mastercode_const.te_short) < + subghz_protocol_mastercode_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = MastercodeDecoderStepSaveDuration; + } else if( + DURATION_DIFF(duration, subghz_protocol_mastercode_const.te_short * 15) < + subghz_protocol_mastercode_const.te_delta * 15) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_mastercode_const.te_short) < + subghz_protocol_mastercode_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else if((DURATION_DIFF( + instance->decoder.te_last, + subghz_protocol_mastercode_const.te_long) < + subghz_protocol_mastercode_const.te_delta * 8)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.parser_step = MastercodeDecoderStepReset; + } + + if(instance->decoder.decode_count_bit == + subghz_protocol_mastercode_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.parser_step = MastercodeDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + + } else { + instance->decoder.parser_step = MastercodeDecoderStepReset; + } + } else { + instance->decoder.parser_step = MastercodeDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_mastercode_check_remote_controller(SubGhzBlockGeneric* instance) { + instance->serial = (instance->data >> 4) & 0xFFFF; + instance->btn = (instance->data >> 2 & 0x03); +} + +uint8_t subghz_protocol_decoder_mastercode_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMastercode* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus subghz_protocol_decoder_mastercode_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderMastercode* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + subghz_protocol_decoder_mastercode_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderMastercode* instance = context; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_mastercode_const.min_count_bit_for_found); +} + +void subghz_protocol_decoder_mastercode_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderMastercode* instance = context; + subghz_protocol_mastercode_check_remote_controller(&instance->generic); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%llX Btn %X\r\n" + " +: " DIP_PATTERN "\r\n" + " o: " DIP_PATTERN "\r\n" + " -: " DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint64_t)(instance->generic.data), + instance->generic.btn, + SHOW_DIP_P(instance->generic.serial, DIP_P), + SHOW_DIP_P(instance->generic.serial, DIP_O), + SHOW_DIP_P(instance->generic.serial, DIP_N)); +} diff --git a/lib/subghz/protocols/mastercode.h b/lib/subghz/protocols/mastercode.h new file mode 100644 index 00000000000..c5c73db989a --- /dev/null +++ b/lib/subghz/protocols/mastercode.h @@ -0,0 +1,109 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_MASTERCODE_NAME "Mastercode" + +typedef struct SubGhzProtocolDecoderMastercode SubGhzProtocolDecoderMastercode; +typedef struct SubGhzProtocolEncoderMastercode SubGhzProtocolEncoderMastercode; + +extern const SubGhzProtocolDecoder subghz_protocol_mastercode_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_mastercode_encoder; +extern const SubGhzProtocol subghz_protocol_mastercode; + +/** + * Allocate SubGhzProtocolEncoderMastercode. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderMastercode* pointer to a SubGhzProtocolEncoderMastercode instance + */ +void* subghz_protocol_encoder_mastercode_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderMastercode. + * @param context Pointer to a SubGhzProtocolEncoderMastercode instance + */ +void subghz_protocol_encoder_mastercode_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderMastercode instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_encoder_mastercode_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderMastercode instance + */ +void subghz_protocol_encoder_mastercode_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderMastercode instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_mastercode_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderMastercode. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderMastercode* pointer to a SubGhzProtocolDecoderMastercode instance + */ +void* subghz_protocol_decoder_mastercode_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderMastercode. + * @param context Pointer to a SubGhzProtocolDecoderMastercode instance + */ +void subghz_protocol_decoder_mastercode_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderMastercode. + * @param context Pointer to a SubGhzProtocolDecoderMastercode instance + */ +void subghz_protocol_decoder_mastercode_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderMastercode instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_mastercode_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderMastercode instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_mastercode_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderMastercode. + * @param context Pointer to a SubGhzProtocolDecoderMastercode instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status + */ +SubGhzProtocolStatus subghz_protocol_decoder_mastercode_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderMastercode. + * @param context Pointer to a SubGhzProtocolDecoderMastercode instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_decoder_mastercode_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderMastercode instance + * @param output Resulting text + */ +void subghz_protocol_decoder_mastercode_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/megacode.c b/lib/subghz/protocols/megacode.c index 909e721714e..ba58bc445b7 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); - subghz_protocol_encoder_megacode_get_upload(instance); + 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,44 +381,36 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderMegaCode* instance = context; 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, string_t output) { +void subghz_protocol_decoder_megacode_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderMegaCode* instance = context; subghz_protocol_megacode_check_remote_controller(&instance->generic); - string_cat_printf( + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:0x%06lX\r\n" - "Sn:0x%04lX - %d\r\n" - "Facility:%X Btn:%X\r\n", + "Sn:0x%04lX - %lu\r\n" + "Facility:%lX Btn:%X\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)instance->generic.data, diff --git a/lib/subghz/protocols/megacode.h b/lib/subghz/protocols/megacode.h index c8010665bef..616ecdf64ef 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. @@ -83,25 +84,26 @@ uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderMegaCode. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_megacode_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_megacode_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance * @param output Resulting text */ -void subghz_protocol_decoder_megacode_get_string(void* context, string_t output); +void subghz_protocol_decoder_megacode_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/nero_radio.c b/lib/subghz/protocols/nero_radio.c index 69326f5a088..d7731dca67b 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); - subghz_protocol_encoder_nero_radio_get_upload(instance); + 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) { @@ -308,7 +308,7 @@ void subghz_protocol_decoder_nero_radio_feed(void* context, bool level, uint32_t } instance->decoder.decode_data = 0; instance->decoder.decode_count_bit = 0; - instance->decoder.parser_step = NeroRadioDecoderStepReset; + instance->decoder.parser_step = NeroRadioDecoderStepReset; //-V1048 break; } else if( (DURATION_DIFF( @@ -343,34 +343,26 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderNeroRadio* instance = context; 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, string_t output) { +void subghz_protocol_decoder_nero_radio_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderNeroRadio* instance = context; @@ -383,7 +375,7 @@ void subghz_protocol_decoder_nero_radio_get_string(void* context, string_t outpu uint32_t code_found_reverse_hi = code_found_reverse >> 32; uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - string_cat_printf( + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:0x%lX%08lX\r\n" diff --git a/lib/subghz/protocols/nero_radio.h b/lib/subghz/protocols/nero_radio.h index f04dc2b3c24..0598aee6ccd 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. @@ -83,25 +84,26 @@ uint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderNeroRadio. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_nero_radio_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_nero_radio_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance * @param output Resulting text */ -void subghz_protocol_decoder_nero_radio_get_string(void* context, string_t output); +void subghz_protocol_decoder_nero_radio_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/nero_sketch.c b/lib/subghz/protocols/nero_sketch.c index c93b36a53a5..09cd0255ae9 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); - subghz_protocol_encoder_nero_sketch_get_upload(instance); + 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,34 +328,26 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderNeroSketch* instance = context; 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, string_t output) { +void subghz_protocol_decoder_nero_sketch_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderNeroSketch* instance = context; @@ -368,7 +360,7 @@ void subghz_protocol_decoder_nero_sketch_get_string(void* context, string_t outp uint32_t code_found_reverse_hi = code_found_reverse >> 32; uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - string_cat_printf( + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:0x%lX%08lX\r\n" diff --git a/lib/subghz/protocols/nero_sketch.h b/lib/subghz/protocols/nero_sketch.h index ab592b48e63..b557772d20d 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. @@ -83,25 +84,26 @@ uint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderNeroSketch. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_nero_sketch_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_nero_sketch_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance * @param output Resulting text */ -void subghz_protocol_decoder_nero_sketch_get_string(void* context, string_t output); +void subghz_protocol_decoder_nero_sketch_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/nice_flo.c b/lib/subghz/protocols/nice_flo.c index 07b18e3ea37..f60e07fb846 100644 --- a/lib/subghz/protocols/nice_flo.c +++ b/lib/subghz/protocols/nice_flo.c @@ -5,7 +5,7 @@ #include "../blocks/generic.h" #include "../blocks/math.h" -#define TAG "SubGhzProtocolNiceFLO" +#define TAG "SubGhzProtocolNiceFlo" static const SubGhzBlockConst subghz_protocol_nice_flo_const = { .te_short = 700, @@ -129,33 +129,36 @@ 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 != - subghz_protocol_nice_flo_const.min_count_bit_for_found) && - (instance->generic.data_count_bit != + if((instance->generic.data_count_bit < + subghz_protocol_nice_flo_const.min_count_bit_for_found) || + (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); - subghz_protocol_encoder_nice_flo_get_upload(instance); + 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,36 +283,38 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderNiceFlo* instance = context; 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 != - subghz_protocol_nice_flo_const.min_count_bit_for_found) && - (instance->generic.data_count_bit != + if((instance->generic.data_count_bit < + subghz_protocol_nice_flo_const.min_count_bit_for_found) || + (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; } -void subghz_protocol_decoder_nice_flo_get_string(void* context, string_t output) { +void subghz_protocol_decoder_nice_flo_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderNiceFlo* instance = context; @@ -318,7 +323,7 @@ void subghz_protocol_decoder_nice_flo_get_string(void* context, string_t output) instance->generic.data, instance->generic.data_count_bit); uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - string_cat_printf( + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:0x%08lX\r\n" diff --git a/lib/subghz/protocols/nice_flo.h b/lib/subghz/protocols/nice_flo.h index 0873e81b935..9a4b53d1271 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. @@ -83,25 +84,26 @@ uint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderNiceFlo. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_nice_flo_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_nice_flo_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance * @param output Resulting text */ -void subghz_protocol_decoder_nice_flo_get_string(void* context, string_t output); +void subghz_protocol_decoder_nice_flo_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/nice_flor_s.c b/lib/subghz/protocols/nice_flor_s.c index 1d370e85a12..d8f5a070a08 100644 --- a/lib/subghz/protocols/nice_flor_s.c +++ b/lib/subghz/protocols/nice_flor_s.c @@ -14,6 +14,9 @@ #define TAG "SubGhzProtocoNiceFlorS" +#define NICE_ONE_COUNT_BIT 72 +#define NICE_ONE_NAME "Nice One" + static const SubGhzBlockConst subghz_protocol_nice_flor_s_const = { .te_short = 500, .te_long = 1000, @@ -28,6 +31,7 @@ struct SubGhzProtocolDecoderNiceFlorS { SubGhzBlockGeneric generic; const char* nice_flor_s_rainbow_table_file_name; + uint64_t data; }; struct SubGhzProtocolEncoderNiceFlorS { @@ -77,6 +81,64 @@ const SubGhzProtocol subghz_protocol_nice_flor_s = { .encoder = &subghz_protocol_nice_flor_s_encoder, }; +// /** +// * Read bytes from rainbow table +// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-P8-P9-P10 +// * @return crc +// */ +// static uint32_t subghz_protocol_nice_one_crc(uint8_t* p) { +// uint8_t crc = 0; +// uint8_t crc_data = 0xff; +// for(uint8_t i = 4; i < 68; i++) { +// if(subghz_protocol_blocks_get_bit_array(p, i)) { +// crc = crc_data ^ 1; +// } else { +// crc = crc_data; +// } +// crc_data >>= 1; +// if((crc & 0x01)) { +// crc_data ^= 0x97; +// } +// } +// crc = 0; +// for(uint8_t i = 0; i < 8; i++) { +// crc <<= 1; +// if((crc_data >> i) & 0x01) crc = crc | 1; +// } +// return crc; +// } + +// /** +// * Read bytes from rainbow table +// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-XX-XX-XX +// * @param num_parcel parcel number 0..15 +// * @param hold_bit 0 - the button was only pressed, 1 - the button was held down +// */ +// static void subghz_protocol_nice_one_get_data(uint8_t* p, uint8_t num_parcel, uint8_t hold_bit) { +// uint8_t k = 0; +// uint8_t crc = 0; +// p[1] = (p[1] & 0x0f) | ((0x0f ^ (p[0] & 0x0F) ^ num_parcel) << 4); +// if(num_parcel < 4) { +// k = 0x8f; +// } else { +// k = 0x80; +// } + +// if(!hold_bit) { +// hold_bit = 0; +// } else { +// hold_bit = 0x10; +// } +// k = num_parcel ^ k; +// p[7] = k; +// p[8] = hold_bit ^ (k << 4); + +// crc = subghz_protocol_nice_one_crc(p); + +// p[8] |= crc >> 4; +// p[9] = crc << 4; +// } + /** * Read bytes from rainbow table * @param file_name Full path to rainbow table the file @@ -237,10 +299,14 @@ void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_ subghz_protocol_nice_flor_s_const.te_delta) { //Found STOP bit instance->decoder.parser_step = NiceFlorSDecoderStepReset; - if(instance->decoder.decode_count_bit == - subghz_protocol_nice_flor_s_const.min_count_bit_for_found) { - instance->generic.data = instance->decoder.decode_data; + if((instance->decoder.decode_count_bit == + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) || + (instance->decoder.decode_count_bit == NICE_ONE_COUNT_BIT)) { + instance->generic.data = instance->data; + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = instance->generic.data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); } @@ -274,6 +340,11 @@ void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_ } else { instance->decoder.parser_step = NiceFlorSDecoderStepReset; } + if(instance->decoder.decode_count_bit == + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } break; } } @@ -287,6 +358,7 @@ static void subghz_protocol_nice_flor_s_remote_controller( SubGhzBlockGeneric* instance, const char* file_name) { /* + * Protocol Nice Flor-S * Packet format Nice Flor-s: START-P0-P1-P2-P3-P4-P5-P6-P7-STOP * P0 (4-bit) - button positional code - 1:0x1, 2:0x2, 3:0x4, 4:0x8; * P1 (4-bit) - batch repetition number, calculated by the formula: @@ -307,6 +379,24 @@ static void subghz_protocol_nice_flor_s_remote_controller( * data => 0x1c5783607f7b3 key serial cnt * decrypt => 0x10436c6820444 => 0x1 0436c682 0444 * + * Protocol Nice One + * Generally repeats the Nice Flor-S protocol, but there are a few changes + * Packet format first 52 bytes repeat Nice Flor-S protocol + * The additional 20 bytes contain the code of the pressed button, + * the button hold bit and the CRC of the entire message. + * START-P0-P1-P2-P3-P4-P5-P6-P7-P8-P9-P10-STOP + * P7 (byte) - if (n<4) k=0x8f : k=0x80; P7= k^n; + * P8 (byte) - if (hold bit) b=0x00 : b=0x10; P8= b^(k<<4) | 4 hi bit crc + * P10 (4-bit) - 4 lo bit crc + * key+b crc + * data => 0x1724A7D9A522F 899 D6 hold bit = 0 - just pressed the button + * data => 0x1424A7D9A522F 8AB 03 hold bit = 1 - button hold + * + * A small button hold counter (0..15) is stored between each press, + * i.e. if 1 press of the button stops counter 6, then the next press + * of the button will start from the value 7 (hold bit = 0), 8 (hold bit = 1)... + * further up to 15 with overflow + * */ if(!file_name) { instance->cnt = 0; @@ -327,53 +417,92 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderNiceFlorS* instance = context; - return 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((ret == SubGhzProtocolStatusOk) && + !flipper_format_write_uint32(flipper_format, "Data", (uint32_t*)&instance->data, 1)) { + FURI_LOG_E(TAG, "Unable to add Data"); + ret = SubGhzProtocolStatusErrorParserOthers; + } + } + 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) { + 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; } - ret = true; + 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; + } } while(false); return ret; } -void subghz_protocol_decoder_nice_flor_s_get_string(void* context, string_t output) { +void subghz_protocol_decoder_nice_flor_s_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderNiceFlorS* instance = context; subghz_protocol_nice_flor_s_remote_controller( &instance->generic, instance->nice_flor_s_rainbow_table_file_name); - uint32_t code_found_hi = instance->generic.data >> 32; - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - - string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:%05lX\r\n" - "Cnt:%04X Btn:%02lX\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - code_found_hi, - code_found_lo, - instance->generic.serial, - instance->generic.cnt, - instance->generic.btn); + + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%013llX%llX\r\n" + "Sn:%05lX\r\n" + "Cnt:%04lX Btn:%02X\r\n", + NICE_ONE_NAME, + instance->generic.data_count_bit, + instance->generic.data, + instance->data, + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); + } else { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%013llX\r\n" + "Sn:%05lX\r\n" + "Cnt:%04lX Btn:%02X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + instance->generic.data, + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); + } } diff --git a/lib/subghz/protocols/nice_flor_s.h b/lib/subghz/protocols/nice_flor_s.h index 7d98876f68f..1ce61149cd6 100644 --- a/lib/subghz/protocols/nice_flor_s.h +++ b/lib/subghz/protocols/nice_flor_s.h @@ -49,25 +49,26 @@ uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderNiceFlorS. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_nice_flor_s_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_nice_flor_s_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance * @param output Resulting text */ -void subghz_protocol_decoder_nice_flor_s_get_string(void* context, string_t output); +void subghz_protocol_decoder_nice_flor_s_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/phoenix_v2.c b/lib/subghz/protocols/phoenix_v2.c index 3d2796e441b..2416a9d0109 100644 --- a/lib/subghz/protocols/phoenix_v2.c +++ b/lib/subghz/protocols/phoenix_v2.c @@ -6,7 +6,7 @@ #include "../blocks/generic.h" #include "../blocks/math.h" -#define TAG "SubGhzProtocolPhoenix_V2" +#define TAG "SubGhzProtocolPhoenixV2" //transmission only static mode @@ -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); - subghz_protocol_encoder_phoenix_v2_get_upload(instance); + 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,43 +293,35 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderPhoenix_V2* instance = context; 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, string_t output) { +void subghz_protocol_decoder_phoenix_v2_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderPhoenix_V2* instance = context; subghz_protocol_phoenix_v2_check_remote_controller(&instance->generic); - string_cat_printf( + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:%02lX%08lX\r\n" "Sn:0x%07lX \r\n" - "Btn:%lX\r\n", + "Btn:%X\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32) & 0xFFFFFFFF, diff --git a/lib/subghz/protocols/phoenix_v2.h b/lib/subghz/protocols/phoenix_v2.h index 38cadc1a23c..0724de1f095 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. @@ -83,25 +84,26 @@ uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderPhoenix_V2. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_phoenix_v2_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_phoenix_v2_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance * @param output Resulting text */ -void subghz_protocol_decoder_phoenix_v2_get_string(void* context, string_t output); +void subghz_protocol_decoder_phoenix_v2_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/power_smart.c b/lib/subghz/protocols/power_smart.c index bd009d88725..d03282f73a1 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) { @@ -312,7 +309,6 @@ void subghz_protocol_decoder_power_smart_feed( if((instance->decoder.decode_data & POWER_SMART_PACKET_HEADER_MASK) == POWER_SMART_PACKET_HEADER) { if(subghz_protocol_power_smart_chek_valid(instance->decoder.decode_data)) { - instance->decoder.decode_data = instance->decoder.decode_data; instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = subghz_protocol_power_smart_const.min_count_bit_for_found; @@ -346,39 +342,31 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderPowerSmart* instance = context; 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, string_t output) { +void subghz_protocol_decoder_power_smart_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderPowerSmart* instance = context; subghz_protocol_power_smart_remote_controller(&instance->generic); - string_cat_printf( + furi_string_cat_printf( output, "%s %db\r\n" "Key:0x%lX%08lX\r\n" diff --git a/lib/subghz/protocols/power_smart.h b/lib/subghz/protocols/power_smart.h index f6e9571b114..5687cf8b1ba 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. @@ -83,25 +84,26 @@ uint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderPowerSmart. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_power_smart_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_power_smart_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance * @param output Resulting text */ -void subghz_protocol_decoder_power_smart_get_string(void* context, string_t output); +void subghz_protocol_decoder_power_smart_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/princeton.c b/lib/subghz/protocols/princeton.c index 2ddfa2cb683..aa15b8b41c9 100644 --- a/lib/subghz/protocols/princeton.c +++ b/lib/subghz/protocols/princeton.c @@ -15,8 +15,8 @@ #define TAG "SubGhzProtocolPrinceton" static const SubGhzBlockConst subghz_protocol_princeton_const = { - .te_short = 400, - .te_long = 1200, + .te_short = 390, + .te_long = 1170, .te_delta = 300, .min_count_bit_for_found = 24, }; @@ -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); - subghz_protocol_encoder_princeton_get_upload(instance); + 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) { @@ -245,8 +247,7 @@ void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t break; case PrincetonDecoderStepCheckDuration: if(!level) { - if(duration >= ((uint32_t)subghz_protocol_princeton_const.te_short * 10 + - subghz_protocol_princeton_const.te_delta)) { + if(duration >= ((uint32_t)subghz_protocol_princeton_const.te_long * 2)) { instance->decoder.parser_step = PrincetonDecoderStepSaveDuration; if(instance->decoder.decode_count_bit == subghz_protocol_princeton_const.min_count_bit_for_found) { @@ -309,62 +310,64 @@ 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, - SubGhzPresetDefinition* preset) { + 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, string_t output) { +void subghz_protocol_decoder_princeton_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderPrinceton* instance = context; subghz_protocol_princeton_check_remote_controller(&instance->generic); uint32_t data_rev = subghz_protocol_blocks_reverse_key( instance->generic.data, instance->generic.data_count_bit); - string_cat_printf( + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:0x%08lX\r\n" "Yek:0x%08lX\r\n" "Sn:0x%05lX Btn:%01X\r\n" - "Te:%dus\r\n", + "Te:%luus\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data & 0xFFFFFF), diff --git a/lib/subghz/protocols/princeton.h b/lib/subghz/protocols/princeton.h index 9c296c4a1ce..da73b6c30fc 100644 --- a/lib/subghz/protocols/princeton.h +++ b/lib/subghz/protocols/princeton.h @@ -28,9 +28,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. @@ -83,25 +84,26 @@ uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderPrinceton. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_princeton_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_princeton_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance * @param output Resulting text */ -void subghz_protocol_decoder_princeton_get_string(void* context, string_t output); +void subghz_protocol_decoder_princeton_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c new file mode 100644 index 00000000000..472a354e384 --- /dev/null +++ b/lib/subghz/protocols/protocol_items.c @@ -0,0 +1,51 @@ +#include "protocol_items.h" + +const SubGhzProtocol* subghz_protocol_registry_items[] = { + &subghz_protocol_gate_tx, + &subghz_protocol_keeloq, + &subghz_protocol_star_line, + &subghz_protocol_nice_flo, + &subghz_protocol_came, + &subghz_protocol_faac_slh, + &subghz_protocol_nice_flor_s, + &subghz_protocol_came_twee, + &subghz_protocol_came_atomo, + &subghz_protocol_nero_sketch, + &subghz_protocol_ido, + &subghz_protocol_kia, + &subghz_protocol_hormann, + &subghz_protocol_nero_radio, + &subghz_protocol_somfy_telis, + &subghz_protocol_somfy_keytis, + &subghz_protocol_scher_khan, + &subghz_protocol_princeton, + &subghz_protocol_raw, + &subghz_protocol_linear, + &subghz_protocol_secplus_v2, + &subghz_protocol_secplus_v1, + &subghz_protocol_megacode, + &subghz_protocol_holtek, + &subghz_protocol_chamb_code, + &subghz_protocol_power_smart, + &subghz_protocol_marantec, + &subghz_protocol_bett, + &subghz_protocol_doitrand, + &subghz_protocol_phoenix_v2, + &subghz_protocol_honeywell_wdb, + &subghz_protocol_magellan, + &subghz_protocol_intertechno_v3, + &subghz_protocol_clemsa, + &subghz_protocol_ansonic, + &subghz_protocol_smc5326, + &subghz_protocol_holtek_th12x, + &subghz_protocol_linear_delta3, + &subghz_protocol_dooya, + &subghz_protocol_alutech_at_4n, + &subghz_protocol_kinggates_stylo_4k, + &subghz_protocol_bin_raw, + &subghz_protocol_mastercode, +}; + +const SubGhzProtocolRegistry subghz_protocol_registry = { + .items = subghz_protocol_registry_items, + .size = COUNT_OF(subghz_protocol_registry_items)}; diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h new file mode 100644 index 00000000000..c5a090e993d --- /dev/null +++ b/lib/subghz/protocols/protocol_items.h @@ -0,0 +1,47 @@ +#pragma once +#include "../registry.h" +#include "../subghz_protocol_registry.h" + +#include "princeton.h" +#include "keeloq.h" +#include "star_line.h" +#include "nice_flo.h" +#include "came.h" +#include "faac_slh.h" +#include "nice_flor_s.h" +#include "came_twee.h" +#include "came_atomo.h" +#include "nero_sketch.h" +#include "ido.h" +#include "kia.h" +#include "hormann.h" +#include "nero_radio.h" +#include "somfy_telis.h" +#include "somfy_keytis.h" +#include "scher_khan.h" +#include "gate_tx.h" +#include "raw.h" +#include "linear.h" +#include "linear_delta3.h" +#include "secplus_v2.h" +#include "secplus_v1.h" +#include "megacode.h" +#include "holtek.h" +#include "chamberlain_code.h" +#include "power_smart.h" +#include "marantec.h" +#include "bett.h" +#include "doitrand.h" +#include "phoenix_v2.h" +#include "honeywell_wdb.h" +#include "magellan.h" +#include "intertechno_v3.h" +#include "clemsa.h" +#include "ansonic.h" +#include "smc5326.h" +#include "holtek_ht12x.h" +#include "dooya.h" +#include "alutech_at_4n.h" +#include "kinggates_stylo_4k.h" +#include "bin_raw.h" +#include "mastercode.h" diff --git a/lib/subghz/protocols/public_api.h b/lib/subghz/protocols/public_api.h new file mode 100644 index 00000000000..174f175cdfa --- /dev/null +++ b/lib/subghz/protocols/public_api.h @@ -0,0 +1,63 @@ +#pragma once + +#include "../types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 32 bit + * @param btn Button number, 8 bit + * @param cnt Container value, 28 bit + * @param manufacture_name Name of manufacturer's key + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_secplus_v2_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint32_t cnt, + SubGhzRadioPreset* preset); + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 28 bit + * @param btn Button number, 4 bit + * @param cnt Container value, 16 bit + * @param manufacture_name Name of manufacturer's key + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_keeloq_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + const char* manufacture_name, + SubGhzRadioPreset* preset); + +typedef struct SubGhzProtocolDecoderBinRAW SubGhzProtocolDecoderBinRAW; + +void subghz_protocol_decoder_bin_raw_data_input_rssi( + SubGhzProtocolDecoderBinRAW* instance, + float rssi); + +/** + * Validation of fixed parts SubGhzProtocolDecoderSecPlus_v1. + * @param fixed fixed parts + * @return true On success + */ +bool subghz_protocol_secplus_v1_check_fixed(uint32_t fixed); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/protocols/raw.c b/lib/subghz/protocols/raw.c index 0419a39a0b1..95dff309355 100644 --- a/lib/subghz/protocols/raw.c +++ b/lib/subghz/protocols/raw.c @@ -11,7 +11,7 @@ #include #include -#define TAG "SubGhzProtocolRAW" +#define TAG "SubGhzProtocolRaw" #define SUBGHZ_DOWNLOAD_MAX_SIZE 512 static const SubGhzBlockConst subghz_protocol_raw_const = { @@ -29,16 +29,18 @@ struct SubGhzProtocolDecoderRAW { Storage* storage; FlipperFormat* flipper_file; uint32_t file_is_open; - string_t file_name; + FuriString* file_name; size_t sample_write; bool last_level; + bool pause; }; struct SubGhzProtocolEncoderRAW { SubGhzProtocolEncoderBase base; bool is_running; - string_t file_name; + FuriString* file_name; + FuriString* radio_device_name; SubGhzFileEncoderWorker* file_worker_encoder; }; @@ -84,14 +86,14 @@ const SubGhzProtocol subghz_protocol_raw = { bool subghz_protocol_raw_save_to_file_init( SubGhzProtocolDecoderRAW* instance, const char* dev_name, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(instance); instance->storage = furi_record_open(RECORD_STORAGE); instance->flipper_file = flipper_format_file_alloc(instance->storage); - string_t temp_str; - string_init(temp_str); + FuriString* temp_str; + temp_str = furi_string_alloc(); bool init = false; do { @@ -104,17 +106,19 @@ bool subghz_protocol_raw_save_to_file_init( break; } - string_set(instance->file_name, dev_name); + furi_string_set(instance->file_name, dev_name); // First remove subghz device file if it was saved - string_printf(temp_str, "%s/%s%s", SUBGHZ_RAW_FOLDER, dev_name, SUBGHZ_APP_EXTENSION); + furi_string_printf( + temp_str, "%s/%s%s", SUBGHZ_RAW_FOLDER, dev_name, SUBGHZ_APP_FILENAME_EXTENSION); - if(!storage_simply_remove(instance->storage, string_get_cstr(temp_str))) { + if(!storage_simply_remove(instance->storage, furi_string_get_cstr(temp_str))) { break; } // Open file - if(!flipper_format_file_open_always(instance->flipper_file, string_get_cstr(temp_str))) { - FURI_LOG_E(TAG, "Unable to open file for write: %s", temp_str); + if(!flipper_format_file_open_always( + instance->flipper_file, furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Unable to open file for write: %s", furi_string_get_cstr(temp_str)); break; } @@ -130,13 +134,13 @@ bool subghz_protocol_raw_save_to_file_init( break; } - subghz_block_generic_get_preset_name(string_get_cstr(preset->name), temp_str); + subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); if(!flipper_format_write_string_cstr( - instance->flipper_file, "Preset", string_get_cstr(temp_str))) { + instance->flipper_file, "Preset", furi_string_get_cstr(temp_str))) { FURI_LOG_E(TAG, "Unable to add Preset"); break; } - if(!strcmp(string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { if(!flipper_format_write_string_cstr( instance->flipper_file, "Custom_preset_module", "CC1101")) { FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); @@ -157,10 +161,12 @@ bool subghz_protocol_raw_save_to_file_init( instance->upload_raw = malloc(SUBGHZ_DOWNLOAD_MAX_SIZE * sizeof(int32_t)); instance->file_is_open = RAWFileIsOpenWrite; instance->sample_write = 0; + instance->last_level = false; + instance->pause = false; init = true; } while(0); - string_clear(temp_str); + furi_string_free(temp_str); return init; } @@ -198,6 +204,14 @@ void subghz_protocol_raw_save_to_file_stop(SubGhzProtocolDecoderRAW* instance) { instance->file_is_open = RAWFileIsOpenClose; } +void subghz_protocol_raw_save_to_file_pause(SubGhzProtocolDecoderRAW* instance, bool pause) { + furi_assert(instance); + + if(instance->pause != pause) { + instance->pause = pause; + } +} + size_t subghz_protocol_raw_get_sample_write(SubGhzProtocolDecoderRAW* instance) { return instance->sample_write + instance->ind_write; } @@ -210,7 +224,7 @@ void* subghz_protocol_decoder_raw_alloc(SubGhzEnvironment* environment) { instance->ind_write = 0; instance->last_level = false; instance->file_is_open = RAWFileIsOpenClose; - string_init(instance->file_name); + instance->file_name = furi_string_alloc(); return instance; } @@ -218,7 +232,7 @@ void* subghz_protocol_decoder_raw_alloc(SubGhzEnvironment* environment) { void subghz_protocol_decoder_raw_free(void* context) { furi_assert(context); SubGhzProtocolDecoderRAW* instance = context; - string_clear(instance->file_name); + furi_string_free(instance->file_name); free(instance); } @@ -233,7 +247,7 @@ void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t durati furi_assert(context); SubGhzProtocolDecoderRAW* instance = context; - if(instance->upload_raw != NULL) { + if(!instance->pause && (instance->upload_raw != NULL)) { if(duration > subghz_protocol_raw_const.te_short) { if(instance->last_level != level) { instance->last_level = (level ? true : false); @@ -247,20 +261,20 @@ 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; + // stub, for backwards compatibility + return SubGhzProtocolStatusOk; } -void subghz_protocol_decoder_raw_get_string(void* context, string_t output) { +void subghz_protocol_decoder_raw_get_string(void* context, FuriString* output) { furi_assert(context); //SubGhzProtocolDecoderRAW* instance = context; UNUSED(context); - //ToDo no use - string_cat_printf(output, "RAW Date"); + furi_string_cat_printf(output, "RAW Date"); } void* subghz_protocol_encoder_raw_alloc(SubGhzEnvironment* environment) { @@ -268,7 +282,8 @@ void* subghz_protocol_encoder_raw_alloc(SubGhzEnvironment* environment) { SubGhzProtocolEncoderRAW* instance = malloc(sizeof(SubGhzProtocolEncoderRAW)); instance->base.protocol = &subghz_protocol_raw; - string_init(instance->file_name); + instance->file_name = furi_string_alloc(); + instance->radio_device_name = furi_string_alloc(); instance->is_running = false; return instance; } @@ -286,7 +301,8 @@ void subghz_protocol_encoder_raw_free(void* context) { furi_assert(context); SubGhzProtocolEncoderRAW* instance = context; subghz_protocol_encoder_raw_stop(instance); - string_clear(instance->file_name); + furi_string_free(instance->file_name); + furi_string_free(instance->radio_device_name); free(instance); } @@ -305,7 +321,9 @@ static bool subghz_protocol_encoder_raw_worker_init(SubGhzProtocolEncoderRAW* in instance->file_worker_encoder = subghz_file_encoder_worker_alloc(); if(subghz_file_encoder_worker_start( - instance->file_worker_encoder, string_get_cstr(instance->file_name))) { + instance->file_worker_encoder, + furi_string_get_cstr(instance->file_name), + furi_string_get_cstr(instance->radio_device_name))) { //the worker needs a file in order to open and read part of the file furi_delay_ms(100); instance->is_running = true; @@ -315,7 +333,10 @@ static bool subghz_protocol_encoder_raw_worker_init(SubGhzProtocolEncoderRAW* in return instance->is_running; } -void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* file_path) { +void subghz_protocol_raw_gen_fff_data( + FlipperFormat* flipper_format, + const char* file_path, + const char* radio_device_name) { do { stream_clean(flipper_format_get_raw_stream(flipper_format)); if(!flipper_format_write_string_cstr(flipper_format, "Protocol", "RAW")) { @@ -327,30 +348,50 @@ void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* FURI_LOG_E(TAG, "Unable to add File_name"); break; } + + if(!flipper_format_write_string_cstr( + flipper_format, "Radio_device_name", radio_device_name)) { + FURI_LOG_E(TAG, "Unable to add Radio_device_name"); + break; + } } 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; - string_t temp_str; - string_init(temp_str); + 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; } - string_set(instance->file_name, temp_str); + furi_string_set(instance->file_name, temp_str); - res = subghz_protocol_encoder_raw_worker_init(instance); + if(!flipper_format_read_string(flipper_format, "Radio_device_name", temp_str)) { + FURI_LOG_E(TAG, "Missing Radio_device_name"); + res = SubGhzProtocolStatusErrorParserOthers; + break; + } + furi_string_set(instance->radio_device_name, temp_str); + + if(!subghz_protocol_encoder_raw_worker_init(instance)) { + res = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } + res = SubGhzProtocolStatusOk; } while(false); - string_clear(temp_str); + furi_string_free(temp_str); return res; } diff --git a/lib/subghz/protocols/raw.h b/lib/subghz/protocols/raw.h index 00654ad28eb..6d791bb3663 100644 --- a/lib/subghz/protocols/raw.h +++ b/lib/subghz/protocols/raw.h @@ -4,6 +4,10 @@ #define SUBGHZ_PROTOCOL_RAW_NAME "RAW" +#ifdef __cplusplus +extern "C" { +#endif + typedef void (*SubGhzProtocolEncoderRAWCallbackEnd)(void* context); typedef struct SubGhzProtocolDecoderRAW SubGhzProtocolDecoderRAW; @@ -17,13 +21,13 @@ extern const SubGhzProtocol subghz_protocol_raw; * Open file for writing * @param instance Pointer to a SubGhzProtocolDecoderRAW instance * @param dev_name File name - * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @param preset The modulation on which the signal was received, SubGhzRadioPreset * @return true On success */ bool subghz_protocol_raw_save_to_file_init( SubGhzProtocolDecoderRAW* instance, const char* dev_name, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Stop writing file to flash @@ -69,16 +73,17 @@ 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. * @param context Pointer to a SubGhzProtocolDecoderRAW instance * @param output Resulting text */ -void subghz_protocol_decoder_raw_get_string(void* context, string_t output); +void subghz_protocol_decoder_raw_get_string(void* context, FuriString* output); /** * Allocate SubGhzProtocolEncoderRAW. @@ -100,10 +105,11 @@ void subghz_protocol_encoder_raw_free(void* context); void subghz_protocol_encoder_raw_stop(void* context); /** - * Сallback on completion of file transfer. + * pause writing to flash. * @param context Pointer to a SubGhzProtocolEncoderRAW instance + * @param pause pause writing */ -void subghz_protocol_raw_file_encoder_worker_callback_end(void* context); +void subghz_protocol_raw_save_to_file_pause(SubGhzProtocolDecoderRAW* instance, bool pause); /** * Set callback on completion of file transfer. @@ -120,16 +126,21 @@ void subghz_protocol_raw_file_encoder_worker_set_callback_end( * File generation for RAW work. * @param flipper_format Pointer to a FlipperFormat instance * @param file_path File path + * @param radio_dev_name Radio device name */ -void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* file_path); +void subghz_protocol_raw_gen_fff_data( + FlipperFormat* flipper_format, + const char* file_path, + const char* radio_dev_name); /** * 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. @@ -137,3 +148,7 @@ bool subghz_protocol_encoder_raw_deserialize(void* context, FlipperFormat* flipp * @return LevelDuration */ LevelDuration subghz_protocol_encoder_raw_yield(void* context); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/protocols/registry.c b/lib/subghz/protocols/registry.c deleted file mode 100644 index 19b03cebcfd..00000000000 --- a/lib/subghz/protocols/registry.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "registry.h" - -const SubGhzProtocol* subghz_protocol_registry[] = { - &subghz_protocol_gate_tx, &subghz_protocol_keeloq, &subghz_protocol_star_line, - &subghz_protocol_nice_flo, &subghz_protocol_came, &subghz_protocol_faac_slh, - &subghz_protocol_nice_flor_s, &subghz_protocol_came_twee, &subghz_protocol_came_atomo, - &subghz_protocol_nero_sketch, &subghz_protocol_ido, &subghz_protocol_kia, - &subghz_protocol_hormann, &subghz_protocol_nero_radio, &subghz_protocol_somfy_telis, - &subghz_protocol_somfy_keytis, &subghz_protocol_scher_khan, &subghz_protocol_princeton, - &subghz_protocol_raw, &subghz_protocol_linear, &subghz_protocol_secplus_v2, - &subghz_protocol_secplus_v1, &subghz_protocol_megacode, &subghz_protocol_holtek, - &subghz_protocol_chamb_code, &subghz_protocol_power_smart, &subghz_protocol_marantec, - &subghz_protocol_bett, &subghz_protocol_doitrand, &subghz_protocol_phoenix_v2, - &subghz_protocol_honeywell_wdb, - -}; - -const SubGhzProtocol* subghz_protocol_registry_get_by_name(const char* name) { - for(size_t i = 0; i < subghz_protocol_registry_count(); i++) { - if(strcmp(name, subghz_protocol_registry[i]->name) == 0) { - return subghz_protocol_registry[i]; - } - } - return NULL; -} - -const SubGhzProtocol* subghz_protocol_registry_get_by_index(size_t index) { - if(index < subghz_protocol_registry_count()) { - return subghz_protocol_registry[index]; - } else { - return NULL; - } -} - -size_t subghz_protocol_registry_count() { - return COUNT_OF(subghz_protocol_registry); -} diff --git a/lib/subghz/protocols/registry.h b/lib/subghz/protocols/registry.h deleted file mode 100644 index d7569513298..00000000000 --- a/lib/subghz/protocols/registry.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include "../types.h" - -#include "princeton.h" -#include "keeloq.h" -#include "star_line.h" -#include "nice_flo.h" -#include "came.h" -#include "faac_slh.h" -#include "nice_flor_s.h" -#include "came_twee.h" -#include "came_atomo.h" -#include "nero_sketch.h" -#include "ido.h" -#include "kia.h" -#include "hormann.h" -#include "nero_radio.h" -#include "somfy_telis.h" -#include "somfy_keytis.h" -#include "scher_khan.h" -#include "gate_tx.h" -#include "raw.h" -#include "linear.h" -#include "secplus_v2.h" -#include "secplus_v1.h" -#include "megacode.h" -#include "holtek.h" -#include "chamberlain_code.h" -#include "power_smart.h" -#include "marantec.h" -#include "bett.h" -#include "doitrand.h" -#include "phoenix_v2.h" -#include "honeywell_wdb.h" - -/** - * Registration by name SubGhzProtocol. - * @param name Protocol name - * @return SubGhzProtocol* pointer to a SubGhzProtocol instance - */ -const SubGhzProtocol* subghz_protocol_registry_get_by_name(const char* name); - -/** - * Registration protocol by index in array SubGhzProtocol. - * @param index Protocol by index in array - * @return SubGhzProtocol* pointer to a SubGhzProtocol instance - */ -const SubGhzProtocol* subghz_protocol_registry_get_by_index(size_t index); - -/** - * Getting the number of registered protocols. - * @return Number of protocols - */ -size_t subghz_protocol_registry_count(); diff --git a/lib/subghz/protocols/scher_khan.c b/lib/subghz/protocols/scher_khan.c index 1c044e9db3e..bf8de10c96f 100644 --- a/lib/subghz/protocols/scher_khan.c +++ b/lib/subghz/protocols/scher_khan.c @@ -151,8 +151,8 @@ void subghz_protocol_decoder_scher_khan_feed(void* context, bool level, uint32_t break; case ScherKhanDecoderStepSaveDuration: if(level) { - if(duration >= (uint32_t)(subghz_protocol_scher_khan_const.te_long + - subghz_protocol_scher_khan_const.te_delta * 2)) { + if(duration >= (subghz_protocol_scher_khan_const.te_delta * 2UL + + subghz_protocol_scher_khan_const.te_long)) { //Found stop bit instance->decoder.parser_step = ScherKhanDecoderStepReset; if(instance->decoder.decode_count_bit >= @@ -248,33 +248,34 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderScherKhan* instance = context; 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); } -void subghz_protocol_decoder_scher_khan_get_string(void* context, string_t output) { +void subghz_protocol_decoder_scher_khan_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderScherKhan* instance = context; subghz_protocol_scher_khan_check_remote_controller( &instance->generic, &instance->protocol_name); - string_cat_printf( + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:0x%lX%08lX\r\n" - "Sn:%07lX Btn:%lX Cnt:%04X\r\n" + "Sn:%07lX Btn:%X Cnt:%04lX\r\n" "Pt: %s\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, diff --git a/lib/subghz/protocols/scher_khan.h b/lib/subghz/protocols/scher_khan.h index 391ccc414a3..58545069cf6 100644 --- a/lib/subghz/protocols/scher_khan.h +++ b/lib/subghz/protocols/scher_khan.h @@ -49,25 +49,26 @@ uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderScherKhan. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_scher_khan_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_scher_khan_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance * @param output Resulting text */ -void subghz_protocol_decoder_scher_khan_get_string(void* context, string_t output); +void subghz_protocol_decoder_scher_khan_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/secplus_v1.c b/lib/subghz/protocols/secplus_v1.c index 77a0c62a2e9..644b2fba9cf 100644 --- a/lib/subghz/protocols/secplus_v1.c +++ b/lib/subghz/protocols/secplus_v1.c @@ -11,7 +11,7 @@ * https://github.com/merbanan/rtl_433/blob/master/src/devices/secplus_v1.c */ -#define TAG "SubGhzProtocoSecPlus_v1" +#define TAG "SubGhzProtocoSecPlusV1" #define SECPLUS_V1_BIT_ERR -1 //0b0000 #define SECPLUS_V1_BIT_0 0 //0b0001 @@ -224,7 +224,7 @@ static bool subghz_protocol_secplus_v1_encode(SubGhzProtocolEncoderSecPlus_v1* i instance->generic.data &= 0xFFFFFFFF00000000; instance->generic.data |= rolling; - if(rolling > 0xFFFFFFFF) { + if(rolling == 0xFFFFFFFF) { rolling = 0xE6000000; } if(fixed > 0xCFD41B90) { @@ -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,27 +282,29 @@ 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; } uint8_t key_data[sizeof(uint64_t)] = {0}; for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF; } if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Unable to add Key"); + 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,31 +517,23 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderSecPlus_v1* instance = context; 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) { @@ -550,12 +543,12 @@ bool subghz_protocol_secplus_v1_check_fixed(uint32_t fixed) { do { if(id1 == 0) return false; - if(!(btn == 0 || btn == 1 || btn == 2)) return false; + if(!(btn == 0 || btn == 1 || btn == 2)) return false; //-V560 } while(false); return true; } -void subghz_protocol_decoder_secplus_v1_get_string(void* context, string_t output) { +void subghz_protocol_decoder_secplus_v1_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderSecPlus_v1* instance = context; @@ -567,7 +560,7 @@ void subghz_protocol_decoder_secplus_v1_get_string(void* context, string_t outpu uint8_t id1 = (fixed / 9) % 3; uint16_t pin = 0; - string_cat_printf( + furi_string_cat_printf( output, "%s %db\r\n" "Key:0x%lX%08lX\r\n" @@ -587,9 +580,9 @@ void subghz_protocol_decoder_secplus_v1_get_string(void* context, string_t outpu pin = (fixed / 59049) % 19683; if(pin <= 9999) { - string_cat_printf(output, " pin:%d", pin); - } else if(10000 <= pin && pin <= 11029) { - string_cat_printf(output, " pin:enter"); + furi_string_cat_printf(output, " pin:%d", pin); + } else if(pin <= 11029) { + furi_string_cat_printf(output, " pin:enter"); } int pin_suffix = 0; @@ -597,16 +590,16 @@ void subghz_protocol_decoder_secplus_v1_get_string(void* context, string_t outpu pin_suffix = (fixed / 1162261467) % 3; if(pin_suffix == 1) { - string_cat_printf(output, " #\r\n"); + furi_string_cat_printf(output, " #\r\n"); } else if(pin_suffix == 2) { - string_cat_printf(output, " *\r\n"); + furi_string_cat_printf(output, " *\r\n"); } else { - string_cat_printf(output, "\r\n"); + furi_string_cat_printf(output, "\r\n"); } - string_cat_printf( + furi_string_cat_printf( output, "Sn:0x%08lX\r\n" - "Cnt:0x%03X\r\n" + "Cnt:0x%03lX\r\n" "Sw_id:0x%X\r\n", instance->generic.serial, instance->generic.cnt, @@ -615,17 +608,17 @@ void subghz_protocol_decoder_secplus_v1_get_string(void* context, string_t outpu //id = fixed / 27; instance->generic.serial = fixed / 27; if(instance->generic.btn == 1) { - string_cat_printf(output, " Btn:left\r\n"); + furi_string_cat_printf(output, " Btn:left\r\n"); } else if(instance->generic.btn == 0) { - string_cat_printf(output, " Btn:middle\r\n"); - } else if(instance->generic.btn == 2) { - string_cat_printf(output, " Btn:right\r\n"); + furi_string_cat_printf(output, " Btn:middle\r\n"); + } else if(instance->generic.btn == 2) { //-V547 + furi_string_cat_printf(output, " Btn:right\r\n"); } - string_cat_printf( + furi_string_cat_printf( output, "Sn:0x%08lX\r\n" - "Cnt:0x%03X\r\n" + "Cnt:0x%03lX\r\n" "Sw_id:0x%X\r\n", instance->generic.serial, instance->generic.cnt, diff --git a/lib/subghz/protocols/secplus_v1.h b/lib/subghz/protocols/secplus_v1.h index 8ae1c0cb216..e01f8bcdaa4 100644 --- a/lib/subghz/protocols/secplus_v1.h +++ b/lib/subghz/protocols/secplus_v1.h @@ -1,5 +1,7 @@ #pragma once + #include "base.h" +#include "public_api.h" #define SUBGHZ_PROTOCOL_SECPLUS_V1_NAME "Security+ 1.0" @@ -27,9 +29,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. @@ -82,32 +85,26 @@ uint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderSecPlus_v1. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_secplus_v1_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_secplus_v1_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * Deserialize data SubGhzProtocolDecoderSecPlus_v1. * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Validation of fixed parts SubGhzProtocolDecoderSecPlus_v1. - * @param fixed fixed parts - * @return true On success + * @return status */ -bool subghz_protocol_secplus_v1_check_fixed(uint32_t fixed); +SubGhzProtocolStatus + subghz_protocol_decoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format); /** * Getting a textual representation of the received data. * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance * @param output Resulting text */ -void subghz_protocol_decoder_secplus_v1_get_string(void* context, string_t output); +void subghz_protocol_decoder_secplus_v1_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/secplus_v2.c b/lib/subghz/protocols/secplus_v2.c index c242d0b4d90..374c407b034 100644 --- a/lib/subghz/protocols/secplus_v2.c +++ b/lib/subghz/protocols/secplus_v2.c @@ -13,7 +13,7 @@ * https://github.com/merbanan/rtl_433/blob/master/src/devices/secplus_v2.c */ -#define TAG "SubGhzProtocoSecPlus_v2" +#define TAG "SubGhzProtocoSecPlusV2" #define SECPLUS_V2_HEADER 0x3C0000000000 #define SECPLUS_V2_HEADER_MASK 0xFFFF3C0000000000 @@ -151,7 +151,7 @@ static bool subghz_protocol_secplus_v2_mix_order_decode(uint8_t order, uint16_t case 0x06: // 0b0110 2, 1, 0], case 0x09: // 0b1001 2, 1, 0], p[2] = a; - p[1] = b; + // p[1]: no change p[0] = c; break; case 0x08: // 0b1000 1, 2, 0], @@ -166,20 +166,18 @@ static bool subghz_protocol_secplus_v2_mix_order_decode(uint8_t order, uint16_t p[1] = c; break; case 0x00: // 0b0000 0, 2, 1], - p[0] = a; + // p[0]: no change p[2] = b; p[1] = c; break; case 0x05: // 0b0101 1, 0, 2], p[1] = a; p[0] = b; - p[2] = c; + // p[2]: no change break; case 0x02: // 0b0010 0, 1, 2], case 0x0A: // 0b1010 0, 1, 2], - p[0] = a; - p[1] = b; - p[2] = c; + // no reordering break; default: FURI_LOG_E(TAG, "Order FAIL"); @@ -263,16 +261,16 @@ static bool data = order << 4 | invert; int k = 0; for(int i = 6; i >= 0; i -= 2) { - roll_array[k++] = (data >> i) & 0x03; - if(roll_array[k] == 3) { + roll_array[k] = (data >> i) & 0x03; + if(roll_array[k++] == 3) { FURI_LOG_E(TAG, "Roll_Array FAIL"); return false; } } for(int i = 8; i >= 0; i -= 2) { - roll_array[k++] = (p[2] >> i) & 0x03; - if(roll_array[k] == 3) { + roll_array[k] = (p[2] >> i) & 0x03; + if(roll_array[k++] == 3) { FURI_LOG_E(TAG, "Roll_Array FAIL"); return false; } @@ -382,7 +380,6 @@ static void subghz_protocol_secplus_v2_encode(SubGhzProtocolEncoderSecPlus_v2* i uint8_t roll_2[9] = {0}; instance->generic.cnt++; - //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); @@ -505,24 +502,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++) { @@ -539,28 +536,28 @@ bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat //update data for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF; } if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Unable to add Key"); + ret = SubGhzProtocolStatusErrorParserKey; break; } for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> i * 8) & 0xFF; + key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> (i * 8)) & 0xFF; } if(!flipper_format_update_hex( flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { 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) { @@ -592,7 +589,7 @@ bool subghz_protocol_secplus_v2_create_data( uint32_t serial, uint8_t btn, uint32_t cnt, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolEncoderSecPlus_v2* instance = context; instance->generic.serial = serial; @@ -601,19 +598,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; + 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 = SubGhzProtocolStatusErrorParserOthers; } - return res; + return res == SubGhzProtocolStatusOk; } void* subghz_protocol_decoder_secplus_v2_alloc(SubGhzEnvironment* environment) { @@ -691,7 +689,7 @@ void subghz_protocol_decoder_secplus_v2_feed(void* context, bool level, uint32_t subghz_protocol_secplus_v2_const.te_delta) { event = ManchesterEventLongLow; } else if( - duration >= (uint32_t)(subghz_protocol_secplus_v2_const.te_long * 2 + + duration >= (subghz_protocol_secplus_v2_const.te_long * 2UL + subghz_protocol_secplus_v2_const.te_delta)) { if(instance->decoder.decode_count_bit == subghz_protocol_secplus_v2_const.min_count_bit_for_found) { @@ -756,72 +754,73 @@ 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, - SubGhzPresetDefinition* preset) { + 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; + 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, string_t output) { +void subghz_protocol_decoder_secplus_v2_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderSecPlus_v2* instance = context; subghz_protocol_secplus_v2_remote_controller(&instance->generic, instance->secplus_packet_1); - string_cat_printf( + furi_string_cat_printf( output, "%s %db\r\n" "Pk1:0x%lX%08lX\r\n" "Pk2:0x%lX%08lX\r\n" "Sn:0x%08lX Btn:0x%01X\r\n" - "Cnt:0x%03X\r\n", + "Cnt:0x%03lX\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, diff --git a/lib/subghz/protocols/secplus_v2.h b/lib/subghz/protocols/secplus_v2.h index 9e9ae2a7de6..9eb912a27e1 100644 --- a/lib/subghz/protocols/secplus_v2.h +++ b/lib/subghz/protocols/secplus_v2.h @@ -1,5 +1,7 @@ #pragma once + #include "base.h" +#include "public_api.h" #define SUBGHZ_PROTOCOL_SECPLUS_V2_NAME "Security+ 2.0" @@ -27,9 +29,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. @@ -44,25 +47,6 @@ void subghz_protocol_encoder_secplus_v2_stop(void* context); */ LevelDuration subghz_protocol_encoder_secplus_v2_yield(void* context); -/** - * Key generation from simple data. - * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param serial Serial number, 32 bit - * @param btn Button number, 8 bit - * @param cnt Container value, 28 bit - * @param manufacture_name Name of manufacturer's key - * @param preset Modulation, SubGhzPresetDefinition - * @return true On success - */ -bool subghz_protocol_secplus_v2_create_data( - void* context, - FlipperFormat* flipper_format, - uint32_t serial, - uint8_t btn, - uint32_t cnt, - SubGhzPresetDefinition* preset); - /** * Allocate SubGhzProtocolDecoderSecPlus_v2. * @param environment Pointer to a SubGhzEnvironment instance @@ -101,25 +85,26 @@ uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderSecPlus_v2. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_secplus_v2_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_secplus_v2_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance * @param output Resulting text */ -void subghz_protocol_decoder_secplus_v2_get_string(void* context, string_t output); +void subghz_protocol_decoder_secplus_v2_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/smc5326.c b/lib/subghz/protocols/smc5326.c new file mode 100644 index 00000000000..0b9755b8cba --- /dev/null +++ b/lib/subghz/protocols/smc5326.c @@ -0,0 +1,391 @@ +#include "smc5326.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * Help + * https://datasheetspdf.com/pdf-file/532079/Aslic/AX5326-4/1 + * + */ + +#define TAG "SubGhzProtocolSmc5326" + +#define DIP_P 0b11 //(+) +#define DIP_O 0b10 //(0) +#define DIP_N 0b00 //(-) + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c" +#define SHOW_DIP_P(dip, check_dip) \ + ((((dip >> 0xE) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xC) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xA) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x8) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x6) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x4) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x2) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x0) & 0x3) == check_dip) ? '*' : '_') + +static const SubGhzBlockConst subghz_protocol_smc5326_const = { + .te_short = 300, + .te_long = 900, + .te_delta = 200, + .min_count_bit_for_found = 25, +}; + +struct SubGhzProtocolDecoderSMC5326 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint32_t te; + uint32_t last_data; +}; + +struct SubGhzProtocolEncoderSMC5326 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + uint32_t te; +}; + +typedef enum { + SMC5326DecoderStepReset = 0, + SMC5326DecoderStepSaveDuration, + SMC5326DecoderStepCheckDuration, +} SMC5326DecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_smc5326_decoder = { + .alloc = subghz_protocol_decoder_smc5326_alloc, + .free = subghz_protocol_decoder_smc5326_free, + + .feed = subghz_protocol_decoder_smc5326_feed, + .reset = subghz_protocol_decoder_smc5326_reset, + + .get_hash_data = subghz_protocol_decoder_smc5326_get_hash_data, + .serialize = subghz_protocol_decoder_smc5326_serialize, + .deserialize = subghz_protocol_decoder_smc5326_deserialize, + .get_string = subghz_protocol_decoder_smc5326_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_smc5326_encoder = { + .alloc = subghz_protocol_encoder_smc5326_alloc, + .free = subghz_protocol_encoder_smc5326_free, + + .deserialize = subghz_protocol_encoder_smc5326_deserialize, + .stop = subghz_protocol_encoder_smc5326_stop, + .yield = subghz_protocol_encoder_smc5326_yield, +}; + +const SubGhzProtocol subghz_protocol_smc5326 = { + .name = SUBGHZ_PROTOCOL_SMC5326_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | + SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_smc5326_decoder, + .encoder = &subghz_protocol_smc5326_encoder, +}; + +void* subghz_protocol_encoder_smc5326_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderSMC5326* instance = malloc(sizeof(SubGhzProtocolEncoderSMC5326)); + + instance->base.protocol = &subghz_protocol_smc5326; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_smc5326_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderSMC5326* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderSMC5326 instance + * @return true On success + */ +static bool subghz_protocol_encoder_smc5326_get_upload(SubGhzProtocolEncoderSMC5326* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 2; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)instance->te * 3); + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)instance->te * 3); + } + } + + //Send Stop bit + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + //Send PT_GUARD + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te * 25); + + return true; +} + +SubGhzProtocolStatus + subghz_protocol_encoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderSMC5326* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + 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; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_smc5326_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } + instance->encoder.is_running = true; + } while(false); + + return ret; +} + +void subghz_protocol_encoder_smc5326_stop(void* context) { + SubGhzProtocolEncoderSMC5326* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_smc5326_yield(void* context) { + SubGhzProtocolEncoderSMC5326* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_smc5326_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderSMC5326* instance = malloc(sizeof(SubGhzProtocolDecoderSMC5326)); + instance->base.protocol = &subghz_protocol_smc5326; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_smc5326_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + free(instance); +} + +void subghz_protocol_decoder_smc5326_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + instance->decoder.parser_step = SMC5326DecoderStepReset; + instance->last_data = 0; +} + +void subghz_protocol_decoder_smc5326_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + + switch(instance->decoder.parser_step) { + case SMC5326DecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_smc5326_const.te_short * 24) < + subghz_protocol_smc5326_const.te_delta * 12)) { + //Found Preambula + instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = 0; + } + break; + case SMC5326DecoderStepSaveDuration: + //save duration + if(level) { + instance->decoder.te_last = duration; + instance->te += duration; + instance->decoder.parser_step = SMC5326DecoderStepCheckDuration; + } + break; + case SMC5326DecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_smc5326_const.te_long * 2)) { + instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; + if(instance->decoder.decode_count_bit == + subghz_protocol_smc5326_const.min_count_bit_for_found) { + if((instance->last_data == instance->decoder.decode_data) && + instance->last_data) { + instance->te /= (instance->decoder.decode_count_bit * 4 + 1); + + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->last_data = instance->decoder.decode_data; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = 0; + break; + } + + instance->te += duration; + + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_smc5326_const.te_short) < + subghz_protocol_smc5326_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_smc5326_const.te_long) < + subghz_protocol_smc5326_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_smc5326_const.te_long) < + subghz_protocol_smc5326_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_smc5326_const.te_short) < + subghz_protocol_smc5326_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = SMC5326DecoderStepReset; + } + } else { + instance->decoder.parser_step = SMC5326DecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_smc5326_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus subghz_protocol_decoder_smc5326_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + 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"); + ret = SubGhzProtocolStatusErrorParserTe; + } + return ret; +} + +SubGhzProtocolStatus + subghz_protocol_decoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + 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; + } + } while(false); + + return ret; +} + +static void subghz_protocol_smc5326_get_event_serialize(uint8_t event, FuriString* output) { + furi_string_cat_printf( + output, + "%s%s%s%s\r\n", + (((event >> 6) & 0x3) == 0x3 ? "B1 " : ""), + (((event >> 4) & 0x3) == 0x3 ? "B2 " : ""), + (((event >> 2) & 0x3) == 0x3 ? "B3 " : ""), + (((event >> 0) & 0x3) == 0x3 ? "B4 " : "")); +} + +void subghz_protocol_decoder_smc5326_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + uint32_t data = (uint32_t)((instance->generic.data >> 9) & 0xFFFF); + + furi_string_cat_printf( + output, + "%s %ubit\r\n" + "Key:%07lX Te:%luus\r\n" + " +: " DIP_PATTERN "\r\n" + " o: " DIP_PATTERN " ", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0x1FFFFFF), + instance->te, + SHOW_DIP_P(data, DIP_P), + SHOW_DIP_P(data, DIP_O)); + subghz_protocol_smc5326_get_event_serialize(instance->generic.data >> 1, output); + furi_string_cat_printf(output, " -: " DIP_PATTERN "\r\n", SHOW_DIP_P(data, DIP_N)); +} diff --git a/lib/subghz/protocols/smc5326.h b/lib/subghz/protocols/smc5326.h new file mode 100644 index 00000000000..911226cf9bd --- /dev/null +++ b/lib/subghz/protocols/smc5326.h @@ -0,0 +1,109 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_SMC5326_NAME "SMC5326" + +typedef struct SubGhzProtocolDecoderSMC5326 SubGhzProtocolDecoderSMC5326; +typedef struct SubGhzProtocolEncoderSMC5326 SubGhzProtocolEncoderSMC5326; + +extern const SubGhzProtocolDecoder subghz_protocol_smc5326_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_smc5326_encoder; +extern const SubGhzProtocol subghz_protocol_smc5326; + +/** + * Allocate SubGhzProtocolEncoderSMC5326. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderSMC5326* pointer to a SubGhzProtocolEncoderSMC5326 instance + */ +void* subghz_protocol_encoder_smc5326_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderSMC5326. + * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance + */ +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 status + */ +SubGhzProtocolStatus + subghz_protocol_encoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance + */ +void subghz_protocol_encoder_smc5326_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_smc5326_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderSMC5326. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderSMC5326* pointer to a SubGhzProtocolDecoderSMC5326 instance + */ +void* subghz_protocol_decoder_smc5326_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderSMC5326. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + */ +void subghz_protocol_decoder_smc5326_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderSMC5326. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + */ +void subghz_protocol_decoder_smc5326_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_smc5326_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_smc5326_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderSMC5326. + * @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 status + */ +SubGhzProtocolStatus subghz_protocol_decoder_smc5326_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderSMC5326. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_decoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_smc5326_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/somfy_keytis.c b/lib/subghz/protocols/somfy_keytis.c index 7a3b2186f3a..1e3cdc540ef 100644 --- a/lib/subghz/protocols/somfy_keytis.c +++ b/lib/subghz/protocols/somfy_keytis.c @@ -379,37 +379,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, - SubGhzPresetDefinition* preset) { + 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( @@ -418,26 +420,26 @@ 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, string_t output) { +void subghz_protocol_decoder_somfy_keytis_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderSomfyKeytis* instance = context; subghz_protocol_somfy_keytis_check_remote_controller(&instance->generic); - string_cat_printf( + furi_string_cat_printf( output, "%s %db\r\n" "%lX%08lX%06lX\r\n" "Sn:0x%06lX \r\n" - "Cnt:0x%04X\r\n" + "Cnt:0x%04lX\r\n" "Btn:%s\r\n", instance->generic.protocol_name, diff --git a/lib/subghz/protocols/somfy_keytis.h b/lib/subghz/protocols/somfy_keytis.h index 29f96cd6a72..15f63c43586 100644 --- a/lib/subghz/protocols/somfy_keytis.h +++ b/lib/subghz/protocols/somfy_keytis.h @@ -49,25 +49,26 @@ uint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderSomfyKeytis. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_somfy_keytis_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_somfy_keytis_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance * @param output Resulting text */ -void subghz_protocol_decoder_somfy_keytis_get_string(void* context, string_t output); +void subghz_protocol_decoder_somfy_keytis_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/somfy_telis.c b/lib/subghz/protocols/somfy_telis.c index b9aac57775c..523bef6e79f 100644 --- a/lib/subghz/protocols/somfy_telis.c +++ b/lib/subghz/protocols/somfy_telis.c @@ -336,45 +336,37 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderSomfyTelis* instance = context; 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, string_t output) { +void subghz_protocol_decoder_somfy_telis_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderSomfyTelis* instance = context; subghz_protocol_somfy_telis_check_remote_controller(&instance->generic); - string_cat_printf( + furi_string_cat_printf( output, "%s %db\r\n" "Key:0x%lX%08lX\r\n" "Sn:0x%06lX \r\n" - "Cnt:0x%04X\r\n" + "Cnt:0x%04lX\r\n" "Btn:%s\r\n", instance->generic.protocol_name, diff --git a/lib/subghz/protocols/somfy_telis.h b/lib/subghz/protocols/somfy_telis.h index ff5b45e230e..c7ddc3cab98 100644 --- a/lib/subghz/protocols/somfy_telis.h +++ b/lib/subghz/protocols/somfy_telis.h @@ -49,25 +49,26 @@ uint8_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderSomfyTelis. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_somfy_telis_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_somfy_telis_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance * @param output Resulting text */ -void subghz_protocol_decoder_somfy_telis_get_string(void* context, string_t output); +void subghz_protocol_decoder_somfy_telis_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/star_line.c b/lib/subghz/protocols/star_line.c index 757b56622d5..e3b7d4a296c 100644 --- a/lib/subghz/protocols/star_line.c +++ b/lib/subghz/protocols/star_line.c @@ -2,7 +2,6 @@ #include "keeloq_common.h" #include "../subghz_keystore.h" -#include #include #include "../blocks/const.h" @@ -229,7 +228,7 @@ static uint8_t subghz_protocol_star_line_check_remote_controller_selector( //Simple Learning decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = string_get_cstr(manufacture_code->name); + *manufacture_name = furi_string_get_cstr(manufacture_code->name); return 1; } break; @@ -240,7 +239,7 @@ static uint8_t subghz_protocol_star_line_check_remote_controller_selector( subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = string_get_cstr(manufacture_code->name); + *manufacture_name = furi_string_get_cstr(manufacture_code->name); return 1; } break; @@ -248,7 +247,7 @@ static uint8_t subghz_protocol_star_line_check_remote_controller_selector( // Simple Learning decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = string_get_cstr(manufacture_code->name); + *manufacture_name = furi_string_get_cstr(manufacture_code->name); return 1; } // Check for mirrored man @@ -260,7 +259,7 @@ static uint8_t subghz_protocol_star_line_check_remote_controller_selector( } decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = string_get_cstr(manufacture_code->name); + *manufacture_name = furi_string_get_cstr(manufacture_code->name); return 1; } //########################### @@ -270,13 +269,13 @@ static uint8_t subghz_protocol_star_line_check_remote_controller_selector( subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = string_get_cstr(manufacture_code->name); + *manufacture_name = furi_string_get_cstr(manufacture_code->name); return 1; } man_normal_learning = subghz_protocol_keeloq_common_normal_learning(fix, man_rev); decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = string_get_cstr(manufacture_code->name); + *manufacture_name = furi_string_get_cstr(manufacture_code->name); return 1; } break; @@ -317,45 +316,40 @@ 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, - SubGhzPresetDefinition* preset) { + SubGhzRadioPreset* preset) { furi_assert(context); 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; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - res = true; - } while(false); - - return res; + return subghz_block_generic_deserialize(&instance->generic, flipper_format); } -void subghz_protocol_decoder_star_line_get_string(void* context, string_t output) { +void subghz_protocol_decoder_star_line_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderStarLine* instance = context; @@ -370,12 +364,12 @@ void subghz_protocol_decoder_star_line_get_string(void* context, string_t output uint32_t code_found_reverse_hi = code_found_reverse >> 32; uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - string_cat_printf( + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:%08lX%08lX\r\n" - "Fix:0x%08lX Cnt:%04X\r\n" - "Hop:0x%08lX Btn:%02lX\r\n" + "Fix:0x%08lX Cnt:%04lX\r\n" + "Hop:0x%08lX Btn:%02X\r\n" "MF:%s\r\n" "Sn:0x%07lX \r\n", instance->generic.protocol_name, diff --git a/lib/subghz/protocols/star_line.h b/lib/subghz/protocols/star_line.h index e240954b1dc..6f352f240ec 100644 --- a/lib/subghz/protocols/star_line.h +++ b/lib/subghz/protocols/star_line.h @@ -49,25 +49,26 @@ uint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context); * Serialize data SubGhzProtocolDecoderStarLine. * @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, SubGhzPresetDefinition - * @return true On success + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status */ -bool subghz_protocol_decoder_star_line_serialize( +SubGhzProtocolStatus subghz_protocol_decoder_star_line_serialize( void* context, FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); + SubGhzRadioPreset* preset); /** * 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. * @param context Pointer to a SubGhzProtocolDecoderStarLine instance * @param output Resulting text */ -void subghz_protocol_decoder_star_line_get_string(void* context, string_t output); +void subghz_protocol_decoder_star_line_get_string(void* context, FuriString* output); diff --git a/lib/subghz/receiver.c b/lib/subghz/receiver.c index cf4085df447..698fe098ecd 100644 --- a/lib/subghz/receiver.c +++ b/lib/subghz/receiver.c @@ -1,6 +1,7 @@ #include "receiver.h" -#include "protocols/registry.h" +#include "registry.h" +#include "protocols/protocol_items.h" #include @@ -22,9 +23,12 @@ struct SubGhzReceiver { SubGhzReceiver* subghz_receiver_alloc_init(SubGhzEnvironment* environment) { SubGhzReceiver* instance = malloc(sizeof(SubGhzReceiver)); SubGhzReceiverSlotArray_init(instance->slots); + const SubGhzProtocolRegistry* protocol_registry_items = + subghz_environment_get_protocol_registry(environment); - for(size_t i = 0; i < subghz_protocol_registry_count(); ++i) { - const SubGhzProtocol* protocol = subghz_protocol_registry_get_by_index(i); + for(size_t i = 0; i < subghz_protocol_registry_count(protocol_registry_items); ++i) { + const SubGhzProtocol* protocol = + subghz_protocol_registry_get_by_index(protocol_registry_items, i); if(protocol->decoder && protocol->decoder->alloc) { SubGhzReceiverSlot* slot = SubGhzReceiverSlotArray_push_new(instance->slots); @@ -34,7 +38,6 @@ SubGhzReceiver* subghz_receiver_alloc_init(SubGhzEnvironment* environment) { instance->callback = NULL; instance->context = NULL; - return instance; } @@ -61,7 +64,7 @@ void subghz_receiver_decode(SubGhzReceiver* instance, bool level, uint32_t durat for M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) { - if((slot->base->protocol->flag & instance->filter) == instance->filter) { + if((slot->base->protocol->flag & instance->filter) != 0) { slot->base->protocol->decoder->feed(slot->base, level, duration); } } diff --git a/lib/subghz/receiver.h b/lib/subghz/receiver.h index 1357ecbed91..2ef722d1f25 100644 --- a/lib/subghz/receiver.h +++ b/lib/subghz/receiver.h @@ -3,6 +3,10 @@ #include "types.h" #include "protocols/base.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct SubGhzReceiver SubGhzReceiver; typedef void (*SubGhzReceiverCallback)( @@ -63,3 +67,7 @@ void subghz_receiver_set_filter(SubGhzReceiver* instance, SubGhzProtocolFlag fil */ SubGhzProtocolDecoderBase* subghz_receiver_search_decoder_base_by_name(SubGhzReceiver* instance, const char* decoder_name); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/registry.c b/lib/subghz/registry.c new file mode 100644 index 00000000000..d0c22ea8c4a --- /dev/null +++ b/lib/subghz/registry.c @@ -0,0 +1,30 @@ +#include "registry.h" + +const SubGhzProtocol* subghz_protocol_registry_get_by_name( + const SubGhzProtocolRegistry* protocol_registry, + const char* name) { + furi_assert(protocol_registry); + + for(size_t i = 0; i < subghz_protocol_registry_count(protocol_registry); i++) { + if(strcmp(name, protocol_registry->items[i]->name) == 0) { + return protocol_registry->items[i]; + } + } + return NULL; +} + +const SubGhzProtocol* subghz_protocol_registry_get_by_index( + const SubGhzProtocolRegistry* protocol_registry, + size_t index) { + furi_assert(protocol_registry); + if(index < subghz_protocol_registry_count(protocol_registry)) { + return protocol_registry->items[index]; + } else { + return NULL; + } +} + +size_t subghz_protocol_registry_count(const SubGhzProtocolRegistry* protocol_registry) { + furi_assert(protocol_registry); + return protocol_registry->size; +} diff --git a/lib/subghz/registry.h b/lib/subghz/registry.h new file mode 100644 index 00000000000..8529c109705 --- /dev/null +++ b/lib/subghz/registry.h @@ -0,0 +1,48 @@ +#pragma once + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzEnvironment SubGhzEnvironment; + +typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; +typedef struct SubGhzProtocol SubGhzProtocol; + +struct SubGhzProtocolRegistry { + const SubGhzProtocol** items; + const size_t size; +}; + +/** + * Registration by name SubGhzProtocol. + * @param protocol_registry SubGhzProtocolRegistry + * @param name Protocol name + * @return SubGhzProtocol* pointer to a SubGhzProtocol instance + */ +const SubGhzProtocol* subghz_protocol_registry_get_by_name( + const SubGhzProtocolRegistry* protocol_registry, + const char* name); + +/** + * Registration protocol by index in array SubGhzProtocol. + * @param protocol_registry SubGhzProtocolRegistry + * @param index Protocol by index in array + * @return SubGhzProtocol* pointer to a SubGhzProtocol instance + */ +const SubGhzProtocol* subghz_protocol_registry_get_by_index( + const SubGhzProtocolRegistry* protocol_registry, + size_t index); + +/** + * Getting the number of registered protocols. + * @param protocol_registry SubGhzProtocolRegistry + * @return Number of protocols + */ +size_t subghz_protocol_registry_count(const SubGhzProtocolRegistry* protocol_registry); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c index 8f65ea004e7..3c045c26966 100644 --- a/lib/subghz/subghz_file_encoder_worker.c +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -1,9 +1,9 @@ #include "subghz_file_encoder_worker.h" -#include #include #include #include +#include #define TAG "SubGhzFileEncoderWorker" @@ -11,7 +11,7 @@ struct SubGhzFileEncoderWorker { FuriThread* thread; - StreamBufferHandle_t stream; + FuriStreamBuffer* stream; Storage* storage; FlipperFormat* flipper_format; @@ -19,8 +19,10 @@ struct SubGhzFileEncoderWorker { volatile bool worker_running; volatile bool worker_stoping; bool level; - string_t str_data; - string_t file_path; + bool is_storage_slow; + FuriString* str_data; + FuriString* file_path; + const SubGhzDevice* device; SubGhzFileEncoderWorkerCallbackEnd callback_end; void* context_end; @@ -48,7 +50,7 @@ void subghz_file_encoder_worker_add_level_duration( if(res) { instance->level = !instance->level; - xStreamBufferSend(instance->stream, &duration, sizeof(int32_t), 100); + furi_stream_buffer_send(instance->stream, &duration, sizeof(int32_t), 100); } else { FURI_LOG_E(TAG, "Invalid level in the stream"); } @@ -83,24 +85,21 @@ LevelDuration subghz_file_encoder_worker_get_level_duration(void* context) { furi_assert(context); SubGhzFileEncoderWorker* instance = context; int32_t duration; - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - int ret = xStreamBufferReceiveFromISR( - instance->stream, &duration, sizeof(int32_t), &xHigherPriorityTaskWoken); - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + int ret = furi_stream_buffer_receive(instance->stream, &duration, sizeof(int32_t), 0); if(ret == sizeof(int32_t)) { LevelDuration level_duration = {.level = LEVEL_DURATION_RESET}; if(duration < 0) { - level_duration = level_duration_make(false, duration * -1); + level_duration = level_duration_make(false, -duration); } else if(duration > 0) { level_duration = level_duration_make(true, duration); - } else if(duration == 0) { + } else if(duration == 0) { //-V547 level_duration = level_duration_reset(); FURI_LOG_I(TAG, "Stop transmission"); instance->worker_stoping = true; } return level_duration; } else { - FURI_LOG_E(TAG, "Slow flash read"); + instance->is_storage_slow = true; return level_duration_wait(); } } @@ -114,12 +113,15 @@ static int32_t subghz_file_encoder_worker_thread(void* context) { SubGhzFileEncoderWorker* instance = context; FURI_LOG_I(TAG, "Worker start"); bool res = false; + instance->is_storage_slow = false; Stream* stream = flipper_format_get_raw_stream(instance->flipper_format); do { if(!flipper_format_file_open_existing( - instance->flipper_format, string_get_cstr(instance->file_path))) { + instance->flipper_format, furi_string_get_cstr(instance->file_path))) { FURI_LOG_E( - TAG, "Unable to open file for read: %s", string_get_cstr(instance->file_path)); + TAG, + "Unable to open file for read: %s", + furi_string_get_cstr(instance->file_path)); break; } if(!flipper_format_read_string(instance->flipper_format, "Protocol", instance->str_data)) { @@ -135,31 +137,34 @@ static int32_t subghz_file_encoder_worker_thread(void* context) { } while(0); while(res && instance->worker_running) { - size_t stream_free_byte = xStreamBufferSpacesAvailable(instance->stream); + size_t stream_free_byte = furi_stream_buffer_spaces_available(instance->stream); if((stream_free_byte / sizeof(int32_t)) >= SUBGHZ_FILE_ENCODER_LOAD) { if(stream_read_line(stream, instance->str_data)) { - string_strim(instance->str_data); + furi_string_trim(instance->str_data); if(!subghz_file_encoder_worker_data_parse( - instance, string_get_cstr(instance->str_data))) { - //to stop DMA correctly + instance, furi_string_get_cstr(instance->str_data))) { subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); - subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); - break; } } else { - subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); break; } + } else { + furi_delay_ms(1); } - furi_delay_ms(5); } //waiting for the end of the transfer + if(instance->is_storage_slow) { + FURI_LOG_E(TAG, "Storage is slow"); + } + FURI_LOG_I(TAG, "End read file"); - while(!furi_hal_subghz_is_async_tx_complete() && instance->worker_running) { + while(instance->device && !subghz_devices_is_async_complete_tx(instance->device) && + instance->worker_running) { furi_delay_ms(5); } + FURI_LOG_I(TAG, "End transmission"); while(instance->worker_running) { if(instance->worker_stoping) { @@ -176,18 +181,15 @@ static int32_t subghz_file_encoder_worker_thread(void* context) { SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc() { SubGhzFileEncoderWorker* instance = malloc(sizeof(SubGhzFileEncoderWorker)); - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "SubGhzFEWorker"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, subghz_file_encoder_worker_thread); - instance->stream = xStreamBufferCreate(sizeof(int32_t) * 2048, sizeof(int32_t)); + instance->thread = + furi_thread_alloc_ex("SubGhzFEWorker", 2048, subghz_file_encoder_worker_thread, instance); + instance->stream = furi_stream_buffer_alloc(sizeof(int32_t) * 2048, sizeof(int32_t)); instance->storage = furi_record_open(RECORD_STORAGE); instance->flipper_format = flipper_format_file_alloc(instance->storage); - string_init(instance->str_data); - string_init(instance->file_path); + instance->str_data = furi_string_alloc(); + instance->file_path = furi_string_alloc(); instance->level = false; instance->worker_stoping = true; @@ -197,11 +199,11 @@ SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc() { void subghz_file_encoder_worker_free(SubGhzFileEncoderWorker* instance) { furi_assert(instance); - vStreamBufferDelete(instance->stream); + furi_stream_buffer_free(instance->stream); furi_thread_free(instance->thread); - string_clear(instance->str_data); - string_clear(instance->file_path); + furi_string_free(instance->str_data); + furi_string_free(instance->file_path); flipper_format_free(instance->flipper_format); furi_record_close(RECORD_STORAGE); @@ -209,12 +211,18 @@ void subghz_file_encoder_worker_free(SubGhzFileEncoderWorker* instance) { free(instance); } -bool subghz_file_encoder_worker_start(SubGhzFileEncoderWorker* instance, const char* file_path) { +bool subghz_file_encoder_worker_start( + SubGhzFileEncoderWorker* instance, + const char* file_path, + const char* radio_device_name) { furi_assert(instance); furi_assert(!instance->worker_running); - xStreamBufferReset(instance->stream); - string_set(instance->file_path, file_path); + furi_stream_buffer_reset(instance->stream); + furi_string_set(instance->file_path, file_path); + if(radio_device_name) { + instance->device = subghz_devices_get_by_name(radio_device_name); + } instance->worker_running = true; furi_thread_start(instance->thread); diff --git a/lib/subghz/subghz_file_encoder_worker.h b/lib/subghz/subghz_file_encoder_worker.h index a87be5cd6f3..e66c66d76ea 100644 --- a/lib/subghz/subghz_file_encoder_worker.h +++ b/lib/subghz/subghz_file_encoder_worker.h @@ -38,9 +38,14 @@ LevelDuration subghz_file_encoder_worker_get_level_duration(void* context); /** * Start SubGhzFileEncoderWorker. * @param instance Pointer to a SubGhzFileEncoderWorker instance + * @param file_path File path + * @param radio_device_name Radio device name * @return bool - true if ok */ -bool subghz_file_encoder_worker_start(SubGhzFileEncoderWorker* instance, const char* file_path); +bool subghz_file_encoder_worker_start( + SubGhzFileEncoderWorker* instance, + const char* file_path, + const char* radio_device_name); /** * Stop SubGhzFileEncoderWorker diff --git a/lib/subghz/subghz_keystore.c b/lib/subghz/subghz_keystore.c index 0abd2d5e667..4f602d2e276 100644 --- a/lib/subghz/subghz_keystore.c +++ b/lib/subghz/subghz_keystore.c @@ -43,7 +43,7 @@ void subghz_keystore_free(SubGhzKeystore* instance) { for M_EACH(manufacture_code, instance->data, SubGhzKeyArray_t) { - string_clear(manufacture_code->name); + furi_string_free(manufacture_code->name); manufacture_code->key = 0; } SubGhzKeyArray_clear(instance->data); @@ -57,7 +57,7 @@ static void subghz_keystore_add_key( uint64_t key, uint16_t type) { SubGhzKey* manufacture_code = SubGhzKeyArray_push_raw(instance->data); - string_init_set_str(manufacture_code->name, name); + manufacture_code->name = furi_string_alloc_set(name); manufacture_code->key = key; manufacture_code->type = type; } @@ -114,62 +114,69 @@ static bool subghz_keystore_read_file(SubGhzKeystore* instance, Stream* stream, char* encrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); size_t encrypted_line_cursor = 0; - if(iv) furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv); - - size_t ret = 0; do { - ret = stream_read(stream, buffer, FILE_BUFFER_SIZE); - for(uint16_t i = 0; i < ret; i++) { - if(buffer[i] == '\n' && encrypted_line_cursor > 0) { - // Process line - if(iv) { - // Data alignment check, 32 instead of 16 because of hex encoding - size_t len = strlen(encrypted_line); - if(len % 32 == 0) { - // Inplace hex to bin conversion - for(size_t i = 0; i < len; i += 2) { - uint8_t hi_nibble = 0; - uint8_t lo_nibble = 0; - hex_char_to_hex_nibble(encrypted_line[i], &hi_nibble); - hex_char_to_hex_nibble(encrypted_line[i + 1], &lo_nibble); - encrypted_line[i / 2] = (hi_nibble << 4) | lo_nibble; - } - len /= 2; + if(iv) { + if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + FURI_LOG_E(TAG, "Unable to load decryption key"); + break; + } + } - if(furi_hal_crypto_decrypt( - (uint8_t*)encrypted_line, (uint8_t*)decrypted_line, len)) { - subghz_keystore_process_line(instance, decrypted_line); + size_t ret = 0; + do { + ret = stream_read(stream, buffer, FILE_BUFFER_SIZE); + for(uint16_t i = 0; i < ret; i++) { + if(buffer[i] == '\n' && encrypted_line_cursor > 0) { + // Process line + if(iv) { + // Data alignment check, 32 instead of 16 because of hex encoding + size_t len = strlen(encrypted_line); + if(len % 32 == 0) { + // Inplace hex to bin conversion + for(size_t i = 0; i < len; i += 2) { + uint8_t hi_nibble = 0; + uint8_t lo_nibble = 0; + hex_char_to_hex_nibble(encrypted_line[i], &hi_nibble); + hex_char_to_hex_nibble(encrypted_line[i + 1], &lo_nibble); + encrypted_line[i / 2] = (hi_nibble << 4) | lo_nibble; + } + len /= 2; + + if(furi_hal_crypto_decrypt( + (uint8_t*)encrypted_line, (uint8_t*)decrypted_line, len)) { + subghz_keystore_process_line(instance, decrypted_line); + } else { + FURI_LOG_E(TAG, "Decryption failed"); + result = false; + break; + } } else { - FURI_LOG_E(TAG, "Decryption failed"); - result = false; - break; + FURI_LOG_E(TAG, "Invalid encrypted data: %s", encrypted_line); } } else { - FURI_LOG_E(TAG, "Invalid encrypted data: %s", encrypted_line); + subghz_keystore_process_line(instance, encrypted_line); } + // reset line buffer + memset(decrypted_line, 0, SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); + memset(encrypted_line, 0, SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); + encrypted_line_cursor = 0; + } else if(buffer[i] == '\r' || buffer[i] == '\n') { + // do not add line endings to the buffer } else { - subghz_keystore_process_line(instance, encrypted_line); - } - // reset line buffer - memset(decrypted_line, 0, SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); - memset(encrypted_line, 0, SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); - encrypted_line_cursor = 0; - } else if(buffer[i] == '\r' || buffer[i] == '\n') { - // do not add line endings to the buffer - } else { - if(encrypted_line_cursor < SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE) { - encrypted_line[encrypted_line_cursor] = buffer[i]; - encrypted_line_cursor++; - } else { - FURI_LOG_E(TAG, "Malformed file"); - result = false; - break; + if(encrypted_line_cursor < SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE) { + encrypted_line[encrypted_line_cursor] = buffer[i]; + encrypted_line_cursor++; + } else { + FURI_LOG_E(TAG, "Malformed file"); + result = false; + break; + } } } - } - } while(ret > 0 && result); + } while(ret > 0 && result); - if(iv) furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + if(iv) furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + } while(false); free(encrypted_line); free(decrypted_line); @@ -182,10 +189,10 @@ bool subghz_keystore_load(SubGhzKeystore* instance, const char* file_name) { bool result = false; uint8_t iv[16]; uint32_t version; - SubGhzKeystoreEncryption encryption; + uint32_t encryption; - string_t filetype; - string_init(filetype); + FuriString* filetype; + filetype = furi_string_alloc(); FURI_LOG_I(TAG, "Loading keystore %s", file_name); @@ -206,7 +213,7 @@ bool subghz_keystore_load(SubGhzKeystore* instance, const char* file_name) { break; } - if(strcmp(string_get_cstr(filetype), SUBGHZ_KEYSTORE_FILE_TYPE) != 0 || + if(strcmp(furi_string_get_cstr(filetype), SUBGHZ_KEYSTORE_FILE_TYPE) != 0 || version != SUBGHZ_KEYSTORE_FILE_VERSION) { FURI_LOG_E(TAG, "Type or version mismatch"); break; @@ -231,7 +238,7 @@ bool subghz_keystore_load(SubGhzKeystore* instance, const char* file_name) { furi_record_close(RECORD_STORAGE); - string_clear(filetype); + furi_string_free(filetype); return result; } @@ -267,7 +274,7 @@ bool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8 subghz_keystore_mess_with_iv(iv); - if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -287,7 +294,7 @@ bool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8 (uint32_t)(key->key >> 32), (uint32_t)key->key, key->type, - string_get_cstr(key->name)); + furi_string_get_cstr(key->name)); // Verify length and align furi_assert(len > 0); if(len % 16 != 0) { @@ -313,13 +320,13 @@ bool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8 stream_write_char(stream, '\n'); encrypted_line_count++; } - furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); size_t total_keys = SubGhzKeyArray_size(instance->data); result = encrypted_line_count == total_keys; if(result) { - FURI_LOG_I(TAG, "Success. Encrypted: %d of %d", encrypted_line_count, total_keys); + FURI_LOG_I(TAG, "Success. Encrypted: %zu of %zu", encrypted_line_count, total_keys); } else { - FURI_LOG_E(TAG, "Failure. Encrypted: %d of %d", encrypted_line_count, total_keys); + FURI_LOG_E(TAG, "Failure. Encrypted: %zu of %zu", encrypted_line_count, total_keys); } } while(0); flipper_format_free(flipper_format); @@ -342,9 +349,9 @@ bool subghz_keystore_raw_encrypted_save( uint8_t* iv) { bool encrypted = false; uint32_t version; - string_t filetype; - string_init(filetype); - SubGhzKeystoreEncryption encryption; + uint32_t encryption; + FuriString* filetype; + filetype = furi_string_alloc(); Storage* storage = furi_record_open(RECORD_STORAGE); @@ -366,7 +373,7 @@ bool subghz_keystore_raw_encrypted_save( break; } - if(strcmp(string_get_cstr(filetype), SUBGHZ_KEYSTORE_FILE_RAW_TYPE) != 0 || + if(strcmp(furi_string_get_cstr(filetype), SUBGHZ_KEYSTORE_FILE_RAW_TYPE) != 0 || version != SUBGHZ_KEYSTORE_FILE_VERSION) { FURI_LOG_E(TAG, "Type or version mismatch"); break; @@ -385,7 +392,9 @@ bool subghz_keystore_raw_encrypted_save( break; } if(!flipper_format_write_header_cstr( - output_flipper_format, string_get_cstr(filetype), SUBGHZ_KEYSTORE_FILE_VERSION)) { + output_flipper_format, + furi_string_get_cstr(filetype), + SUBGHZ_KEYSTORE_FILE_VERSION)) { FURI_LOG_E(TAG, "Unable to add header"); break; } @@ -406,7 +415,7 @@ bool subghz_keystore_raw_encrypted_save( subghz_keystore_mess_with_iv(iv); - if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -455,11 +464,11 @@ bool subghz_keystore_raw_encrypted_save( } stream_write_cstring(output_stream, encrypted_line); - } while(ret > 0 && result); + } while(true); flipper_format_free(output_flipper_format); - furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); if(!result) break; @@ -479,10 +488,10 @@ bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* bool result = false; uint8_t iv[16]; uint32_t version; - SubGhzKeystoreEncryption encryption; + uint32_t encryption; - string_t str_temp; - string_init(str_temp); + FuriString* str_temp; + str_temp = furi_string_alloc(); Storage* storage = furi_record_open(RECORD_STORAGE); char* decrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); @@ -502,7 +511,7 @@ bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* break; } - if(strcmp(string_get_cstr(str_temp), SUBGHZ_KEYSTORE_FILE_RAW_TYPE) != 0 || + if(strcmp(furi_string_get_cstr(str_temp), SUBGHZ_KEYSTORE_FILE_RAW_TYPE) != 0 || version != SUBGHZ_KEYSTORE_FILE_VERSION) { FURI_LOG_E(TAG, "Type or version mismatch"); break; @@ -561,7 +570,7 @@ bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* } } - if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -589,7 +598,7 @@ bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* memcpy(data, (uint8_t*)decrypted_line + (offset - (offset / 16) * 16), len); } while(0); - furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); if(decrypted) result = true; } while(0); flipper_format_free(flipper_format); @@ -598,7 +607,7 @@ bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* free(decrypted_line); - string_clear(str_temp); + furi_string_free(str_temp); return result; } diff --git a/lib/subghz/subghz_keystore.h b/lib/subghz/subghz_keystore.h index be9a19df61c..06ae8adae4d 100644 --- a/lib/subghz/subghz_keystore.h +++ b/lib/subghz/subghz_keystore.h @@ -1,11 +1,15 @@ #pragma once -#include +#include #include #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { - string_t name; + FuriString* name; uint64_t key; uint16_t type; } SubGhzKey; @@ -70,3 +74,7 @@ bool subghz_keystore_raw_encrypted_save( * @return true On success */ bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* data, size_t len); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/subghz_protocol_registry.h b/lib/subghz/subghz_protocol_registry.h new file mode 100644 index 00000000000..2dfa9ce8b6c --- /dev/null +++ b/lib/subghz/subghz_protocol_registry.h @@ -0,0 +1,15 @@ +#pragma once + +#include "registry.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const SubGhzProtocolRegistry subghz_protocol_registry; + +typedef struct SubGhzProtocolDecoderBinRAW SubGhzProtocolDecoderBinRAW; + +#ifdef __cplusplus +} +#endif diff --git a/applications/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c similarity index 86% rename from applications/subghz/subghz_setting.c rename to lib/subghz/subghz_setting.c index 0a662f589bf..c0216913fbf 100644 --- a/applications/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -1,9 +1,10 @@ #include "subghz_setting.h" -#include "subghz_i.h" +#include "types.h" +//#include "subghz_i.h" #include #include -#include "furi_hal_subghz_configs.h" +#include #define TAG "SubGhzSetting" @@ -158,7 +159,7 @@ static const uint32_t subghz_hopper_frequency_list_region_jp[] = { }; typedef struct { - string_t custom_preset_name; + FuriString* custom_preset_name; uint8_t* custom_preset_data; size_t custom_preset_data_size; } SubGhzSettingCustomPresetItem; @@ -194,7 +195,7 @@ SubGhzSetting* subghz_setting_alloc(void) { static void subghz_setting_preset_reset(SubGhzSetting* instance) { for M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { - string_clear(item->custom_preset_name); + furi_string_free(item->custom_preset_name); free(item->custom_preset_data); } SubGhzSettingCustomPresetItemArray_reset(instance->preset->data); @@ -206,7 +207,7 @@ void subghz_setting_free(SubGhzSetting* instance) { FrequencyList_clear(instance->hopper_frequencies); for M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { - string_clear(item->custom_preset_name); + furi_string_free(item->custom_preset_name); free(item->custom_preset_data); } SubGhzSettingCustomPresetItemArray_clear(instance->preset->data); @@ -217,16 +218,15 @@ void subghz_setting_free(SubGhzSetting* instance) { static void subghz_setting_load_default_preset( SubGhzSetting* instance, const char* preset_name, - const uint8_t* preset_data, - const uint8_t preset_pa_table[8]) { + const uint8_t* preset_data) { furi_assert(instance); furi_assert(preset_data); uint32_t preset_data_count = 0; SubGhzSettingCustomPresetItem* item = SubGhzSettingCustomPresetItemArray_push_raw(instance->preset->data); - string_init(item->custom_preset_name); - string_set(item->custom_preset_name, preset_name); + item->custom_preset_name = furi_string_alloc(); + furi_string_set(item->custom_preset_name, preset_name); while(preset_data[preset_data_count]) { preset_data_count += 2; @@ -234,10 +234,8 @@ static void subghz_setting_load_default_preset( preset_data_count += 2; item->custom_preset_data_size = sizeof(uint8_t) * preset_data_count + sizeof(uint8_t) * 8; item->custom_preset_data = malloc(item->custom_preset_data_size); - //load preset register - memcpy(&item->custom_preset_data[0], &preset_data[0], preset_data_count); - //load pa table - memcpy(&item->custom_preset_data[preset_data_count], &preset_pa_table[0], 8); + //load preset register + pa table + memcpy(&item->custom_preset_data[0], &preset_data[0], item->custom_preset_data_size); } static void subghz_setting_load_default_region( @@ -261,25 +259,13 @@ static void subghz_setting_load_default_region( } subghz_setting_load_default_preset( - instance, - "AM270", - (uint8_t*)furi_hal_subghz_preset_ook_270khz_async_regs, - furi_hal_subghz_preset_ook_async_patable); + instance, "AM270", subghz_device_cc1101_preset_ook_270khz_async_regs); subghz_setting_load_default_preset( - instance, - "AM650", - (uint8_t*)furi_hal_subghz_preset_ook_650khz_async_regs, - furi_hal_subghz_preset_ook_async_patable); + instance, "AM650", subghz_device_cc1101_preset_ook_650khz_async_regs); subghz_setting_load_default_preset( - instance, - "FM238", - (uint8_t*)furi_hal_subghz_preset_2fsk_dev2_38khz_async_regs, - furi_hal_subghz_preset_2fsk_async_patable); + instance, "FM238", subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs); subghz_setting_load_default_preset( - instance, - "FM476", - (uint8_t*)furi_hal_subghz_preset_2fsk_dev47_6khz_async_regs, - furi_hal_subghz_preset_2fsk_async_patable); + instance, "FM476", subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs); } void subghz_setting_load_default(SubGhzSetting* instance) { @@ -314,8 +300,8 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path) { Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - string_t temp_str; - string_init(temp_str); + FuriString* temp_str; + temp_str = furi_string_alloc(); uint32_t temp_data32; bool temp_bool; @@ -324,7 +310,7 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path) { if(file_path) { do { if(!flipper_format_file_open_existing(fff_data_file, file_path)) { - FURI_LOG_E(TAG, "Error open file %s", file_path); + FURI_LOG_I(TAG, "File is not used %s", file_path); break; } @@ -333,7 +319,7 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path) { break; } - if((!strcmp(string_get_cstr(temp_str), SUBGHZ_SETTING_FILE_TYPE)) && + if((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_SETTING_FILE_TYPE)) && temp_data32 == SUBGHZ_SETTING_FILE_VERSION) { } else { FURI_LOG_E(TAG, "Type or version mismatch"); @@ -358,6 +344,7 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path) { } while(flipper_format_read_uint32( fff_data_file, "Frequency", (uint32_t*)&temp_data32, 1)) { + //Todo FL-3535: add a frequency support check depending on the selected radio device if(furi_hal_subghz_is_frequency_valid(temp_data32)) { FURI_LOG_I(TAG, "Frequency loaded %lu", temp_data32); FrequencyList_push_back(instance->frequencies, temp_data32); @@ -402,15 +389,15 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path) { break; } while(flipper_format_read_string(fff_data_file, "Custom_preset_name", temp_str)) { - FURI_LOG_I(TAG, "Custom preset loaded %s", string_get_cstr(temp_str)); + FURI_LOG_I(TAG, "Custom preset loaded %s", furi_string_get_cstr(temp_str)); subghz_setting_load_custom_preset( - instance, string_get_cstr(temp_str), fff_data_file); + instance, furi_string_get_cstr(temp_str), fff_data_file); } } while(false); } - string_clear(temp_str); + furi_string_free(temp_str); flipper_format_free(fff_data_file); furi_record_close(RECORD_STORAGE); @@ -440,21 +427,21 @@ const char* subghz_setting_get_preset_name(SubGhzSetting* instance, size_t idx) furi_assert(instance); SubGhzSettingCustomPresetItem* item = SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx); - return string_get_cstr(item->custom_preset_name); + return furi_string_get_cstr(item->custom_preset_name); } int subghz_setting_get_inx_preset_by_name(SubGhzSetting* instance, const char* preset_name) { furi_assert(instance); size_t idx = 0; - for - M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { - if(strcmp(string_get_cstr(item->custom_preset_name), preset_name) == 0) { - return idx; - } - idx++; - } - furi_crash("SubGhz: No name preset."); - return -1; + for + M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { + if(strcmp(furi_string_get_cstr(item->custom_preset_name), preset_name) == 0) { + return idx; + } + idx++; + } + furi_crash("SubGhz: No name preset."); + return -1; } bool subghz_setting_load_custom_preset( @@ -466,8 +453,8 @@ bool subghz_setting_load_custom_preset( uint32_t temp_data32; SubGhzSettingCustomPresetItem* item = SubGhzSettingCustomPresetItemArray_push_raw(instance->preset->data); - string_init(item->custom_preset_name); - string_set(item->custom_preset_name, preset_name); + item->custom_preset_name = furi_string_alloc(); + furi_string_set(item->custom_preset_name, preset_name); do { if(!flipper_format_get_value_count(fff_data_file, "Custom_preset_data", &temp_data32)) break; @@ -497,8 +484,8 @@ bool subghz_setting_delete_custom_preset(SubGhzSetting* instance, const char* pr SubGhzSettingCustomPresetItemArray_it_last(it, instance->preset->data); while(!SubGhzSettingCustomPresetItemArray_end_p(it)) { SubGhzSettingCustomPresetItem* item = SubGhzSettingCustomPresetItemArray_ref(it); - if(strcmp(string_get_cstr(item->custom_preset_name), preset_name) == 0) { - string_clear(item->custom_preset_name); + if(strcmp(furi_string_get_cstr(item->custom_preset_name), preset_name) == 0) { + furi_string_free(item->custom_preset_name); free(item->custom_preset_data); SubGhzSettingCustomPresetItemArray_remove(instance->preset->data, it); return true; @@ -531,9 +518,8 @@ uint8_t* subghz_setting_get_preset_data_by_name(SubGhzSetting* instance, const c uint32_t subghz_setting_get_frequency(SubGhzSetting* instance, size_t idx) { furi_assert(instance); - uint32_t* ret = FrequencyList_get(instance->frequencies, idx); - if(ret) { - return (*ret) & FREQUENCY_MASK; + if(idx < FrequencyList_size(instance->frequencies)) { + return (*FrequencyList_get(instance->frequencies, idx)) & FREQUENCY_MASK; } else { return 0; } @@ -541,9 +527,8 @@ uint32_t subghz_setting_get_frequency(SubGhzSetting* instance, size_t idx) { uint32_t subghz_setting_get_hopper_frequency(SubGhzSetting* instance, size_t idx) { furi_assert(instance); - uint32_t* ret = FrequencyList_get(instance->hopper_frequencies, idx); - if(ret) { - return *ret; + if(idx < FrequencyList_size(instance->hopper_frequencies)) { + return *FrequencyList_get(instance->hopper_frequencies, idx); } else { return 0; } diff --git a/applications/subghz/subghz_setting.h b/lib/subghz/subghz_setting.h similarity index 95% rename from applications/subghz/subghz_setting.h rename to lib/subghz/subghz_setting.h index 0590cf499fd..cd82b302b00 100644 --- a/applications/subghz/subghz_setting.h +++ b/lib/subghz/subghz_setting.h @@ -6,6 +6,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + #define SUBGHZ_SETTING_DEFAULT_PRESET_COUNT 4 typedef struct SubGhzSetting SubGhzSetting; @@ -46,3 +50,7 @@ uint32_t subghz_setting_get_hopper_frequency(SubGhzSetting* instance, size_t idx uint32_t subghz_setting_get_frequency_default_index(SubGhzSetting* instance); uint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/subghz_tx_rx_worker.c b/lib/subghz/subghz_tx_rx_worker.c index 78a18693155..daf046f4431 100644 --- a/lib/subghz/subghz_tx_rx_worker.c +++ b/lib/subghz/subghz_tx_rx_worker.c @@ -1,6 +1,5 @@ #include "subghz_tx_rx_worker.h" -#include #include #define TAG "SubGhzTxRxWorker" @@ -13,8 +12,8 @@ struct SubGhzTxRxWorker { FuriThread* thread; - StreamBufferHandle_t stream_tx; - StreamBufferHandle_t stream_rx; + FuriStreamBuffer* stream_tx; + FuriStreamBuffer* stream_rx; volatile bool worker_running; volatile bool worker_stoping; @@ -22,6 +21,8 @@ struct SubGhzTxRxWorker { SubGhzTxRxWorkerStatus status; uint32_t frequency; + const SubGhzDevice* device; + const GpioPin* device_data_gpio; SubGhzTxRxWorkerCallbackHaveRead callback_have_read; void* context_have_read; @@ -30,9 +31,9 @@ struct SubGhzTxRxWorker { bool subghz_tx_rx_worker_write(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) { furi_assert(instance); bool ret = false; - size_t stream_tx_free_byte = xStreamBufferSpacesAvailable(instance->stream_tx); + size_t stream_tx_free_byte = furi_stream_buffer_spaces_available(instance->stream_tx); if(size && (stream_tx_free_byte >= size)) { - if(xStreamBufferSend( + if(furi_stream_buffer_send( instance->stream_tx, data, size, SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF) == size) { ret = true; @@ -43,12 +44,12 @@ bool subghz_tx_rx_worker_write(SubGhzTxRxWorker* instance, uint8_t* data, size_t size_t subghz_tx_rx_worker_available(SubGhzTxRxWorker* instance) { furi_assert(instance); - return xStreamBufferBytesAvailable(instance->stream_rx); + return furi_stream_buffer_bytes_available(instance->stream_rx); } size_t subghz_tx_rx_worker_read(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) { furi_assert(instance); - return xStreamBufferReceive(instance->stream_rx, data, size, 0); + return furi_stream_buffer_receive(instance->stream_rx, data, size, 0); } void subghz_tx_rx_worker_set_callback_have_read( @@ -66,33 +67,33 @@ bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* uint8_t timeout = 100; bool ret = false; if(instance->status != SubGhzTxRxWorkerStatusRx) { - furi_hal_subghz_rx(); + subghz_devices_set_rx(instance->device); instance->status = SubGhzTxRxWorkerStatusRx; furi_delay_tick(1); } //waiting for reception to complete - while(furi_hal_gpio_read(&gpio_cc1101_g0)) { + while(furi_hal_gpio_read(instance->device_data_gpio)) { furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "RX cc1101_g0 timeout"); - furi_hal_subghz_flush_rx(); - furi_hal_subghz_rx(); + subghz_devices_flush_rx(instance->device); + subghz_devices_set_rx(instance->device); break; } } - if(furi_hal_subghz_rx_pipe_not_empty()) { + if(subghz_devices_rx_pipe_not_empty(instance->device)) { FURI_LOG_I( TAG, "RSSI: %03.1fdbm LQI: %d", - (double)furi_hal_subghz_get_rssi(), - furi_hal_subghz_get_lqi()); - if(furi_hal_subghz_is_rx_data_crc_valid()) { - furi_hal_subghz_read_packet(data, size); + (double)subghz_devices_get_rssi(instance->device), + subghz_devices_get_lqi(instance->device)); + if(subghz_devices_is_rx_data_crc_valid(instance->device)) { + subghz_devices_read_packet(instance->device, data, size); ret = true; } - furi_hal_subghz_flush_rx(); - furi_hal_subghz_rx(); + subghz_devices_flush_rx(instance->device); + subghz_devices_set_rx(instance->device); } return ret; } @@ -100,26 +101,28 @@ bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* void subghz_tx_rx_worker_tx(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) { uint8_t timeout = 200; if(instance->status != SubGhzTxRxWorkerStatusIDLE) { - furi_hal_subghz_idle(); + subghz_devices_idle(instance->device); } - furi_hal_subghz_write_packet(data, size); - furi_hal_subghz_tx(); //start send + subghz_devices_write_packet(instance->device, data, size); + subghz_devices_set_tx(instance->device); //start send instance->status = SubGhzTxRxWorkerStatusTx; - while(!furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be set -> sync transmitted + while(!furi_hal_gpio_read( + instance->device_data_gpio)) { // Wait for GDO0 to be set -> sync transmitted furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "TX !cc1101_g0 timeout"); break; } } - while(furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be cleared -> end of packet + while(furi_hal_gpio_read( + instance->device_data_gpio)) { // Wait for GDO0 to be cleared -> end of packet furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "TX cc1101_g0 timeout"); break; } } - furi_hal_subghz_idle(); + subghz_devices_idle(instance->device); instance->status = SubGhzTxRxWorkerStatusIDLE; } /** Worker thread @@ -129,16 +132,19 @@ void subghz_tx_rx_worker_tx(SubGhzTxRxWorker* instance, uint8_t* data, size_t si */ static int32_t subghz_tx_rx_worker_thread(void* context) { SubGhzTxRxWorker* instance = context; + furi_assert(instance->device); FURI_LOG_I(TAG, "Worker start"); - furi_hal_subghz_reset(); - furi_hal_subghz_idle(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetGFSK9_99KbAsync); - //furi_hal_subghz_load_preset(FuriHalSubGhzPresetMSK99_97KbAsync); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + subghz_devices_begin(instance->device); + instance->device_data_gpio = subghz_devices_get_data_gpio(instance->device); + subghz_devices_reset(instance->device); + subghz_devices_idle(instance->device); + subghz_devices_load_preset(instance->device, FuriHalSubGhzPresetGFSK9_99KbAsync, NULL); - furi_hal_subghz_set_frequency_and_path(instance->frequency); - furi_hal_subghz_flush_rx(); + furi_hal_gpio_init(instance->device_data_gpio, GpioModeInput, GpioPullNo, GpioSpeedLow); + + subghz_devices_set_frequency(instance->device, instance->frequency); + subghz_devices_flush_rx(instance->device); uint8_t data[SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE + 1] = {0}; size_t size_tx = 0; @@ -148,32 +154,30 @@ static int32_t subghz_tx_rx_worker_thread(void* context) { while(instance->worker_running) { //transmit - size_tx = xStreamBufferBytesAvailable(instance->stream_tx); + size_tx = furi_stream_buffer_bytes_available(instance->stream_tx); if(size_tx > 0 && !timeout_tx) { timeout_tx = 10; //20ms if(size_tx > SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE) { - xStreamBufferReceive( + furi_stream_buffer_receive( instance->stream_tx, &data, SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE, SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF); subghz_tx_rx_worker_tx(instance, data, SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE); } else { - //todo checking that he managed to write all the data to the TX buffer - xStreamBufferReceive( + furi_stream_buffer_receive( instance->stream_tx, &data, size_tx, SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF); subghz_tx_rx_worker_tx(instance, data, size_tx); } } else { //recive if(subghz_tx_rx_worker_rx(instance, data, size_rx)) { - if(xStreamBufferSpacesAvailable(instance->stream_rx) >= size_rx[0]) { + if(furi_stream_buffer_spaces_available(instance->stream_rx) >= size_rx[0]) { if(instance->callback_have_read && - xStreamBufferBytesAvailable(instance->stream_rx) == 0) { + furi_stream_buffer_bytes_available(instance->stream_rx) == 0) { callback_rx = true; } - //todo checking that he managed to write all the data to the RX buffer - xStreamBufferSend( + furi_stream_buffer_send( instance->stream_rx, &data, size_rx[0], @@ -183,7 +187,7 @@ static int32_t subghz_tx_rx_worker_thread(void* context) { callback_rx = false; } } else { - //todo RX buffer overflow + FURI_LOG_E(TAG, "Receive buffer overflow, over-the-air transmission too fast"); } } } @@ -192,8 +196,8 @@ static int32_t subghz_tx_rx_worker_thread(void* context) { furi_delay_tick(1); } - furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); - furi_hal_subghz_sleep(); + subghz_devices_sleep(instance->device); + subghz_devices_end(instance->device); FURI_LOG_I(TAG, "Worker stop"); return 0; @@ -202,15 +206,12 @@ static int32_t subghz_tx_rx_worker_thread(void* context) { SubGhzTxRxWorker* subghz_tx_rx_worker_alloc() { SubGhzTxRxWorker* instance = malloc(sizeof(SubGhzTxRxWorker)); - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "SubGhzTxRxWorker"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, subghz_tx_rx_worker_thread); + instance->thread = + furi_thread_alloc_ex("SubGhzTxRxWorker", 2048, subghz_tx_rx_worker_thread, instance); instance->stream_tx = - xStreamBufferCreate(sizeof(uint8_t) * SUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t)); + furi_stream_buffer_alloc(sizeof(uint8_t) * SUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t)); instance->stream_rx = - xStreamBufferCreate(sizeof(uint8_t) * SUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t)); + furi_stream_buffer_alloc(sizeof(uint8_t) * SUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t)); instance->status = SubGhzTxRxWorkerStatusIDLE; instance->worker_stoping = true; @@ -221,24 +222,28 @@ SubGhzTxRxWorker* subghz_tx_rx_worker_alloc() { void subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance) { furi_assert(instance); furi_assert(!instance->worker_running); - vStreamBufferDelete(instance->stream_tx); - vStreamBufferDelete(instance->stream_rx); + furi_stream_buffer_free(instance->stream_tx); + furi_stream_buffer_free(instance->stream_rx); furi_thread_free(instance->thread); free(instance); } -bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency) { +bool subghz_tx_rx_worker_start( + SubGhzTxRxWorker* instance, + const SubGhzDevice* device, + uint32_t frequency) { furi_assert(instance); furi_assert(!instance->worker_running); bool res = false; - xStreamBufferReset(instance->stream_tx); - xStreamBufferReset(instance->stream_rx); + furi_stream_buffer_reset(instance->stream_tx); + furi_stream_buffer_reset(instance->stream_rx); instance->worker_running = true; if(furi_hal_region_is_frequency_allowed(frequency)) { instance->frequency = frequency; + instance->device = device; res = true; } diff --git a/lib/subghz/subghz_tx_rx_worker.h b/lib/subghz/subghz_tx_rx_worker.h index 3f3f82769ea..56bdb0a1feb 100644 --- a/lib/subghz/subghz_tx_rx_worker.h +++ b/lib/subghz/subghz_tx_rx_worker.h @@ -1,6 +1,11 @@ #pragma once #include +#include + +#ifdef __cplusplus +extern "C" { +#endif typedef void (*SubGhzTxRxWorkerCallbackHaveRead)(void* context); @@ -63,9 +68,13 @@ void subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance); /** * Start SubGhzTxRxWorker * @param instance Pointer to a SubGhzTxRxWorker instance + * @param device Pointer to a SubGhzDevice instance * @return bool - true if ok */ -bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency); +bool subghz_tx_rx_worker_start( + SubGhzTxRxWorker* instance, + const SubGhzDevice* device, + uint32_t frequency); /** * Stop SubGhzTxRxWorker @@ -79,3 +88,7 @@ void subghz_tx_rx_worker_stop(SubGhzTxRxWorker* instance); * @return bool - true if running */ bool subghz_tx_rx_worker_is_running(SubGhzTxRxWorker* instance); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/subghz_worker.c b/lib/subghz/subghz_worker.c index 58db8ea5d95..50b5aba5116 100644 --- a/lib/subghz/subghz_worker.c +++ b/lib/subghz/subghz_worker.c @@ -1,19 +1,17 @@ #include "subghz_worker.h" -#include #include #define TAG "SubGhzWorker" struct SubGhzWorker { FuriThread* thread; - StreamBufferHandle_t stream; + FuriStreamBuffer* stream; volatile bool running; volatile bool overrun; LevelDuration filter_level_duration; - bool filter_running; uint16_t filter_duration; SubGhzWorkerOverrunCallback overrun_callback; @@ -30,16 +28,14 @@ struct SubGhzWorker { void subghz_worker_rx_callback(bool level, uint32_t duration, void* context) { SubGhzWorker* instance = context; - BaseType_t xHigherPriorityTaskWoken = pdFALSE; LevelDuration level_duration = level_duration_make(level, duration); if(instance->overrun) { instance->overrun = false; level_duration = level_duration_reset(); } - size_t ret = xStreamBufferSendFromISR( - instance->stream, &level_duration, sizeof(LevelDuration), &xHigherPriorityTaskWoken); + size_t ret = + furi_stream_buffer_send(instance->stream, &level_duration, sizeof(LevelDuration), 0); if(sizeof(LevelDuration) != ret) instance->overrun = true; - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } /** Worker callback thread @@ -52,8 +48,8 @@ static int32_t subghz_worker_thread_callback(void* context) { LevelDuration level_duration; while(instance->running) { - int ret = - xStreamBufferReceive(instance->stream, &level_duration, sizeof(LevelDuration), 10); + int ret = furi_stream_buffer_receive( + instance->stream, &level_duration, sizeof(LevelDuration), 10); if(ret == sizeof(LevelDuration)) { if(level_duration_is_reset(level_duration)) { FURI_LOG_E(TAG, "Overrun buffer"); @@ -62,24 +58,19 @@ static int32_t subghz_worker_thread_callback(void* context) { bool level = level_duration_get_level(level_duration); uint32_t duration = level_duration_get_duration(level_duration); - if(instance->filter_running) { - if((duration < instance->filter_duration) || - (instance->filter_level_duration.level == level)) { - instance->filter_level_duration.duration += duration; - - } else if(instance->filter_level_duration.level != level) { - if(instance->pair_callback) - instance->pair_callback( - instance->context, - instance->filter_level_duration.level, - instance->filter_level_duration.duration); - - instance->filter_level_duration.duration = duration; - instance->filter_level_duration.level = level; - } - } else { + if((duration < instance->filter_duration) || + (instance->filter_level_duration.level == level)) { + instance->filter_level_duration.duration += duration; + + } else if(instance->filter_level_duration.level != level) { if(instance->pair_callback) - instance->pair_callback(instance->context, level, duration); + instance->pair_callback( + instance->context, + instance->filter_level_duration.level, + instance->filter_level_duration.duration); + + instance->filter_level_duration.duration = duration; + instance->filter_level_duration.level = level; } } } @@ -91,16 +82,13 @@ static int32_t subghz_worker_thread_callback(void* context) { SubGhzWorker* subghz_worker_alloc() { SubGhzWorker* instance = malloc(sizeof(SubGhzWorker)); - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "SubGhzWorker"); - furi_thread_set_stack_size(instance->thread, 2048); - furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, subghz_worker_thread_callback); + instance->thread = + furi_thread_alloc_ex("SubGhzWorker", 2048, subghz_worker_thread_callback, instance); - instance->stream = xStreamBufferCreate(sizeof(LevelDuration) * 4096, sizeof(LevelDuration)); + instance->stream = + furi_stream_buffer_alloc(sizeof(LevelDuration) * 4096, sizeof(LevelDuration)); - //setting filter - instance->filter_running = true; + //setting default filter in us instance->filter_duration = 30; return instance; @@ -109,7 +97,7 @@ SubGhzWorker* subghz_worker_alloc() { void subghz_worker_free(SubGhzWorker* instance) { furi_assert(instance); - vStreamBufferDelete(instance->stream); + furi_stream_buffer_free(instance->stream); furi_thread_free(instance->thread); free(instance); @@ -154,3 +142,8 @@ bool subghz_worker_is_running(SubGhzWorker* instance) { furi_assert(instance); return instance->running; } + +void subghz_worker_set_filter(SubGhzWorker* instance, uint16_t timeout) { + furi_assert(instance); + instance->filter_duration = timeout; +} \ No newline at end of file diff --git a/lib/subghz/subghz_worker.h b/lib/subghz/subghz_worker.h index bb303165c8a..657278f5053 100644 --- a/lib/subghz/subghz_worker.h +++ b/lib/subghz/subghz_worker.h @@ -2,6 +2,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct SubGhzWorker SubGhzWorker; typedef void (*SubGhzWorkerOverrunCallback)(void* context); @@ -62,3 +66,15 @@ void subghz_worker_stop(SubGhzWorker* instance); * @return bool - true if running */ bool subghz_worker_is_running(SubGhzWorker* instance); + +/** + * Short duration filter setting. + * glues short durations into 1. The default setting is 30 us, if set to 0 the filter will be disabled + * @param instance Pointer to a SubGhzWorker instance + * @param timeout time in us + */ +void subghz_worker_set_filter(SubGhzWorker* instance, uint16_t timeout); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/transmitter.c b/lib/subghz/transmitter.c index 0dc2bacde41..81be143b53d 100644 --- a/lib/subghz/transmitter.c +++ b/lib/subghz/transmitter.c @@ -1,7 +1,8 @@ #include "transmitter.h" #include "protocols/base.h" -#include "protocols/registry.h" +#include "registry.h" +#include "protocols/protocol_items.h" struct SubGhzTransmitter { const SubGhzProtocol* protocol; @@ -11,14 +12,17 @@ struct SubGhzTransmitter { SubGhzTransmitter* subghz_transmitter_alloc_init(SubGhzEnvironment* environment, const char* protocol_name) { SubGhzTransmitter* instance = NULL; - const SubGhzProtocol* protocol = subghz_protocol_registry_get_by_name(protocol_name); + const SubGhzProtocolRegistry* protocol_registry_items = + subghz_environment_get_protocol_registry(environment); + + const SubGhzProtocol* protocol = + subghz_protocol_registry_get_by_name(protocol_registry_items, protocol_name); if(protocol && protocol->encoder && protocol->encoder->alloc) { instance = malloc(sizeof(SubGhzTransmitter)); instance->protocol = protocol; instance->protocol_instance = instance->protocol->encoder->alloc(environment); } - return instance; } @@ -43,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 53f10b07565..a1c4cda08f3 100644 --- a/lib/subghz/transmitter.h +++ b/lib/subghz/transmitter.h @@ -4,6 +4,10 @@ #include "environment.h" #include "protocols/base.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct SubGhzTransmitter SubGhzTransmitter; /** @@ -35,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. @@ -45,3 +50,7 @@ bool subghz_transmitter_deserialize(SubGhzTransmitter* instance, FlipperFormat* * @return LevelDuration */ LevelDuration subghz_transmitter_yield(void* context); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/types.h b/lib/subghz/types.h index 32b70b65178..93f94cc7483 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -10,11 +10,11 @@ #include "environment.h" #include #include -#include #define SUBGHZ_APP_FOLDER ANY_PATH("subghz") #define SUBGHZ_RAW_FOLDER EXT_PATH("subghz") -#define SUBGHZ_APP_EXTENSION ".sub" +#define SUBGHZ_APP_FILENAME_PREFIX "SubGHz" +#define SUBGHZ_APP_FILENAME_EXTENSION ".sub" #define SUBGHZ_KEY_FILE_VERSION 1 #define SUBGHZ_KEY_FILE_TYPE "Flipper SubGhz Key File" @@ -22,26 +22,60 @@ #define SUBGHZ_RAW_FILE_VERSION 1 #define SUBGHZ_RAW_FILE_TYPE "Flipper SubGhz RAW File" -// -// Abstract method types -// +#define SUBGHZ_KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes") +#define SUBGHZ_KEYSTORE_DIR_USER_NAME EXT_PATH("subghz/assets/keeloq_mfcodes_user") +#define SUBGHZ_CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo") +#define SUBGHZ_NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") +#define SUBGHZ_ALUTECH_AT_4N_DIR_NAME EXT_PATH("subghz/assets/alutech_at_4n") + +typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; +typedef struct SubGhzEnvironment SubGhzEnvironment; + +// Radio Preset +typedef struct { + FuriString* name; + uint32_t frequency; + uint8_t* data; + 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 + SubGhzProtocolStatusErrorProtocolNotFound = (-13), ///< Protocol not found + 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 (*SubGhzSerialize)( - void* context, - FlipperFormat* flipper_format, - SubGhzPresetDefinition* preset); -typedef bool (*SubGhzDeserialize)(void* context, FlipperFormat* flipper_format); +typedef SubGhzProtocolStatus ( + *SubGhzSerialize)(void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); +typedef SubGhzProtocolStatus (*SubGhzDeserialize)(void* context, FlipperFormat* flipper_format); // Decoder specific typedef void (*SubGhzDecoderFeed)(void* decoder, bool level, uint32_t duration); typedef void (*SubGhzDecoderReset)(void* decoder); typedef uint8_t (*SubGhzGetHashData)(void* decoder); -typedef void (*SubGhzGetString)(void* decoder, string_t output); +typedef void (*SubGhzGetString)(void* decoder, FuriString* output); // Encoder specific typedef void (*SubGhzEncoderStop)(void* encoder); @@ -74,6 +108,8 @@ typedef enum { SubGhzProtocolTypeStatic, SubGhzProtocolTypeDynamic, SubGhzProtocolTypeRAW, + SubGhzProtocolWeatherStation, + SubGhzProtocolCustom, } SubGhzProtocolType; typedef enum { @@ -87,13 +123,14 @@ typedef enum { SubGhzProtocolFlag_Save = (1 << 7), SubGhzProtocolFlag_Load = (1 << 8), SubGhzProtocolFlag_Send = (1 << 9), + SubGhzProtocolFlag_BinRAW = (1 << 10), } SubGhzProtocolFlag; -typedef struct { +struct SubGhzProtocol { const char* name; SubGhzProtocolType type; SubGhzProtocolFlag flag; const SubGhzProtocolEncoder* encoder; const SubGhzProtocolDecoder* decoder; -} SubGhzProtocol; +}; diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index 26c233d98b5..14f8de0646e 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -1,12 +1,40 @@ -Import("env") - from fbt.version import get_fast_git_version_id +Import("env") + env.Append( CPPPATH=[ "#/lib/toolbox", ], + LINT_SOURCES=[ + Dir("."), + ], + SDK_HEADERS=[ + File("api_lock.h"), + File("compress.h"), + File("manchester_decoder.h"), + File("manchester_encoder.h"), + File("path.h"), + File("name_generator.h"), + File("crc32_calc.h"), + File("dir_walk.h"), + File("args.h"), + 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"), + File("hex.h"), + File("simple_array.h"), + File("bit_buffer.h"), + ], ) @@ -29,6 +57,7 @@ build_version = libenv.VersionBuilder( fw_version_json = libenv.InstallAs( "${BUILD_DIR}/${FIRMWARE_BUILD_CFG}.json", "version.json" ) +Alias("version_json", fw_version_json) env.Append(FW_VERSION_JSON=fw_version_json) # Default(fw_version_json) @@ -40,7 +69,7 @@ if not version_depends: sources = libenv.GlobRecursive("*.c") -libenv.Append(CPPPATH=["."]) +libenv.Append(CPPPATH=[libenv.Dir(".")]) lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) libenv.Install("${LIB_DIST_DIR}", lib) diff --git a/lib/toolbox/api_lock.h b/lib/toolbox/api_lock.h new file mode 100644 index 00000000000..5902a4922ab --- /dev/null +++ b/lib/toolbox/api_lock.h @@ -0,0 +1,43 @@ +#pragma once +#include + +/* +Testing 10000 api calls + +No lock + Time diff: 445269.218750 us + Time per call: 44.526920 us + +furi_thread_flags + Time diff: 430279.875000 us // lol wtf? smaller than no lock? + Time per call: 43.027988 us // I tested it many times, it's always smaller + +FuriEventFlag + Time diff: 831523.625000 us + Time per call: 83.152359 us + +FuriSemaphore + Time diff: 999807.125000 us + Time per call: 99.980713 us + +FuriMutex + Time diff: 1071417.500000 us + Time per call: 107.141747 us +*/ + +typedef FuriEventFlag* FuriApiLock; + +#define API_LOCK_EVENT (1U << 0) + +#define api_lock_alloc_locked() furi_event_flag_alloc() + +#define api_lock_wait_unlock(_lock) \ + furi_event_flag_wait(_lock, API_LOCK_EVENT, FuriFlagWaitAny, FuriWaitForever) + +#define api_lock_free(_lock) furi_event_flag_free(_lock) + +#define api_lock_unlock(_lock) furi_event_flag_set(_lock, API_LOCK_EVENT) + +#define api_lock_wait_unlock_and_free(_lock) \ + api_lock_wait_unlock(_lock); \ + api_lock_free(_lock); diff --git a/lib/toolbox/args.c b/lib/toolbox/args.c index 287ca7efc92..aa790ad68c5 100644 --- a/lib/toolbox/args.c +++ b/lib/toolbox/args.c @@ -1,60 +1,60 @@ #include "args.h" #include "hex.h" -size_t args_get_first_word_length(string_t args) { - size_t ws = string_search_char(args, ' '); - if(ws == STRING_FAILURE) { - ws = string_size(args); +size_t args_get_first_word_length(FuriString* args) { + size_t ws = furi_string_search_char(args, ' '); + if(ws == FURI_STRING_FAILURE) { + ws = furi_string_size(args); } return ws; } -size_t args_length(string_t args) { - return string_size(args); +size_t args_length(FuriString* args) { + return furi_string_size(args); } -bool args_read_int_and_trim(string_t args, int* value) { +bool args_read_int_and_trim(FuriString* args, int* value) { size_t cmd_length = args_get_first_word_length(args); if(cmd_length == 0) { return false; } - if(sscanf(string_get_cstr(args), "%d", value) == 1) { - string_right(args, cmd_length); - string_strim(args); + if(sscanf(furi_string_get_cstr(args), "%d", value) == 1) { + furi_string_right(args, cmd_length); + furi_string_trim(args); return true; } return false; } -bool args_read_string_and_trim(string_t args, string_t word) { +bool args_read_string_and_trim(FuriString* args, FuriString* word) { size_t cmd_length = args_get_first_word_length(args); if(cmd_length == 0) { return false; } - string_set_n(word, args, 0, cmd_length); - string_right(args, cmd_length); - string_strim(args); + furi_string_set_n(word, args, 0, cmd_length); + furi_string_right(args, cmd_length); + furi_string_trim(args); return true; } -bool args_read_probably_quoted_string_and_trim(string_t args, string_t word) { - if(string_size(args) > 1 && string_get_char(args, 0) == '\"') { - size_t second_quote_pos = string_search_char(args, '\"', 1); +bool args_read_probably_quoted_string_and_trim(FuriString* args, FuriString* word) { + if(furi_string_size(args) > 1 && furi_string_get_char(args, 0) == '\"') { + size_t second_quote_pos = furi_string_search_char(args, '\"', 1); if(second_quote_pos == 0) { return false; } - string_set_n(word, args, 1, second_quote_pos - 1); - string_right(args, second_quote_pos + 1); - string_strim(args); + furi_string_set_n(word, args, 1, second_quote_pos - 1); + furi_string_right(args, second_quote_pos + 1); + furi_string_trim(args); return true; } else { return args_read_string_and_trim(args, word); @@ -76,9 +76,9 @@ bool args_char_to_hex(char hi_nibble, char low_nibble, uint8_t* byte) { return result; } -bool args_read_hex_bytes(string_t args, uint8_t* bytes, size_t bytes_count) { +bool args_read_hex_bytes(FuriString* args, uint8_t* bytes, size_t bytes_count) { bool result = true; - const char* str_pointer = string_get_cstr(args); + const char* str_pointer = furi_string_get_cstr(args); if(args_get_first_word_length(args) == (bytes_count * 2)) { for(size_t i = 0; i < bytes_count; i++) { diff --git a/lib/toolbox/args.h b/lib/toolbox/args.h index dc72bdafab8..556fd4a72b4 100644 --- a/lib/toolbox/args.h +++ b/lib/toolbox/args.h @@ -2,7 +2,7 @@ #include #include -#include +#include #ifdef __cplusplus extern "C" { @@ -15,7 +15,7 @@ extern "C" { * @return true - success * @return false - arguments string does not contain int */ -bool args_read_int_and_trim(string_t args, int* value); +bool args_read_int_and_trim(FuriString* args, int* value); /** * @brief Extract first argument from arguments string and trim arguments string @@ -25,7 +25,7 @@ bool args_read_int_and_trim(string_t args, int* value); * @return true - success * @return false - arguments string does not contain anything */ -bool args_read_string_and_trim(string_t args, string_t word); +bool args_read_string_and_trim(FuriString* args, FuriString* word); /** * @brief Extract the first quoted argument from the argument string and trim the argument string. If the argument is not quoted, calls args_read_string_and_trim. @@ -35,7 +35,7 @@ bool args_read_string_and_trim(string_t args, string_t word); * @return true - success * @return false - arguments string does not contain anything */ -bool args_read_probably_quoted_string_and_trim(string_t args, string_t word); +bool args_read_probably_quoted_string_and_trim(FuriString* args, FuriString* word); /** * @brief Convert hex ASCII values to byte array @@ -46,7 +46,7 @@ bool args_read_probably_quoted_string_and_trim(string_t args, string_t word); * @return true - success * @return false - arguments string does not contain enough values, or contain non-hex ASCII values */ -bool args_read_hex_bytes(string_t args, uint8_t* bytes, size_t bytes_count); +bool args_read_hex_bytes(FuriString* args, uint8_t* bytes, size_t bytes_count); /************************************ HELPERS ***************************************/ @@ -56,7 +56,7 @@ bool args_read_hex_bytes(string_t args, uint8_t* bytes, size_t bytes_count); * @param args arguments string * @return size_t length of first word */ -size_t args_get_first_word_length(string_t args); +size_t args_get_first_word_length(FuriString* args); /** * @brief Get length of arguments string @@ -64,7 +64,7 @@ size_t args_get_first_word_length(string_t args); * @param args arguments string * @return size_t length of arguments string */ -size_t args_length(string_t args); +size_t args_length(FuriString* args); /** * @brief Convert ASCII hex values to byte diff --git a/lib/toolbox/bit_buffer.c b/lib/toolbox/bit_buffer.c new file mode 100644 index 00000000000..1943ee833dc --- /dev/null +++ b/lib/toolbox/bit_buffer.c @@ -0,0 +1,336 @@ +#include "bit_buffer.h" + +#include + +#define BITS_IN_BYTE (8) + +struct BitBuffer { + uint8_t* data; + uint8_t* parity; + size_t capacity_bytes; + size_t size_bits; +}; + +BitBuffer* bit_buffer_alloc(size_t capacity_bytes) { + furi_assert(capacity_bytes); + + BitBuffer* buf = malloc(sizeof(BitBuffer)); + + buf->data = malloc(capacity_bytes); + size_t parity_buf_size = (capacity_bytes + BITS_IN_BYTE - 1) / BITS_IN_BYTE; + buf->parity = malloc(parity_buf_size); + buf->capacity_bytes = capacity_bytes; + buf->size_bits = 0; + + return buf; +} + +void bit_buffer_free(BitBuffer* buf) { + furi_assert(buf); + + free(buf->data); + free(buf->parity); + free(buf); +} + +void bit_buffer_reset(BitBuffer* buf) { + furi_assert(buf); + + memset(buf->data, 0, buf->capacity_bytes); + size_t parity_buf_size = (buf->capacity_bytes + BITS_IN_BYTE - 1) / BITS_IN_BYTE; + memset(buf->parity, 0, parity_buf_size); + buf->size_bits = 0; +} + +void bit_buffer_copy(BitBuffer* buf, const BitBuffer* other) { + furi_assert(buf); + furi_assert(other); + + if(buf == other) return; + + furi_assert(buf->capacity_bytes * BITS_IN_BYTE >= other->size_bits); + + memcpy(buf->data, other->data, bit_buffer_get_size_bytes(other)); + buf->size_bits = other->size_bits; +} + +void bit_buffer_copy_right(BitBuffer* buf, const BitBuffer* other, size_t start_index) { + furi_assert(buf); + furi_assert(other); + furi_assert(bit_buffer_get_size_bytes(other) > start_index); + furi_assert(buf->capacity_bytes >= bit_buffer_get_size_bytes(other) - start_index); + + memcpy(buf->data, other->data + start_index, bit_buffer_get_size_bytes(other) - start_index); + buf->size_bits = other->size_bits - start_index * BITS_IN_BYTE; +} + +void bit_buffer_copy_left(BitBuffer* buf, const BitBuffer* other, size_t end_index) { + furi_assert(buf); + furi_assert(other); + furi_assert(bit_buffer_get_capacity_bytes(buf) >= end_index); + furi_assert(bit_buffer_get_size_bytes(other) >= end_index); + + memcpy(buf->data, other->data, end_index); + buf->size_bits = end_index * BITS_IN_BYTE; +} + +void bit_buffer_copy_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes) { + furi_assert(buf); + furi_assert(data); + furi_assert(buf->capacity_bytes >= size_bytes); + + memcpy(buf->data, data, size_bytes); + buf->size_bits = size_bytes * BITS_IN_BYTE; +} + +void bit_buffer_copy_bits(BitBuffer* buf, const uint8_t* data, size_t size_bits) { + furi_assert(buf); + furi_assert(data); + furi_assert(buf->capacity_bytes * BITS_IN_BYTE >= size_bits); + + size_t size_bytes = (size_bits + BITS_IN_BYTE - 1) / BITS_IN_BYTE; + memcpy(buf->data, data, size_bytes); + buf->size_bits = size_bits; +} + +void bit_buffer_copy_bytes_with_parity(BitBuffer* buf, const uint8_t* data, size_t size_bits) { + furi_assert(buf); + furi_assert(data); + + size_t bits_processed = 0; + size_t curr_byte = 0; + + if(size_bits < BITS_IN_BYTE + 1) { + buf->size_bits = size_bits; + buf->data[0] = data[0]; + } else { + furi_assert(size_bits % (BITS_IN_BYTE + 1) == 0); + while(bits_processed < size_bits) { + buf->data[curr_byte] = data[bits_processed / BITS_IN_BYTE] >> + (bits_processed % BITS_IN_BYTE); + buf->data[curr_byte] |= data[bits_processed / BITS_IN_BYTE + 1] + << (BITS_IN_BYTE - bits_processed % BITS_IN_BYTE); + uint8_t bit = + FURI_BIT(data[bits_processed / BITS_IN_BYTE + 1], bits_processed % BITS_IN_BYTE); + + if(bits_processed % BITS_IN_BYTE) { + buf->parity[curr_byte / BITS_IN_BYTE] = bit; + } else { + buf->parity[curr_byte / BITS_IN_BYTE] |= bit << (bits_processed % BITS_IN_BYTE); + } + bits_processed += BITS_IN_BYTE + 1; + curr_byte++; + } + buf->size_bits = curr_byte * BITS_IN_BYTE; + } +} + +void bit_buffer_write_bytes(const BitBuffer* buf, void* dest, size_t size_bytes) { + furi_assert(buf); + furi_assert(dest); + furi_assert(bit_buffer_get_size_bytes(buf) <= size_bytes); + + memcpy(dest, buf->data, bit_buffer_get_size_bytes(buf)); +} + +void bit_buffer_write_bytes_with_parity( + const BitBuffer* buf, + void* dest, + size_t size_bytes, + size_t* bits_written) { + furi_assert(buf); + furi_assert(dest); + furi_assert(bits_written); + + size_t buf_size_bytes = bit_buffer_get_size_bytes(buf); + size_t buf_size_with_parity_bytes = + (buf_size_bytes * (BITS_IN_BYTE + 1) + BITS_IN_BYTE) / BITS_IN_BYTE; + furi_assert(buf_size_with_parity_bytes <= size_bytes); + + uint8_t next_par_bit = 0; + uint16_t curr_bit_pos = 0; + uint8_t* bitstream = dest; + + for(size_t i = 0; i < buf_size_bytes; i++) { + next_par_bit = FURI_BIT(buf->parity[i / BITS_IN_BYTE], i % BITS_IN_BYTE); + if(curr_bit_pos % BITS_IN_BYTE == 0) { + bitstream[curr_bit_pos / BITS_IN_BYTE] = buf->data[i]; + curr_bit_pos += BITS_IN_BYTE; + bitstream[curr_bit_pos / BITS_IN_BYTE] = next_par_bit; + curr_bit_pos++; + } else { + bitstream[curr_bit_pos / BITS_IN_BYTE] |= buf->data[i] + << (curr_bit_pos % BITS_IN_BYTE); + bitstream[curr_bit_pos / BITS_IN_BYTE + 1] = + buf->data[i] >> (BITS_IN_BYTE - curr_bit_pos % BITS_IN_BYTE); + bitstream[curr_bit_pos / BITS_IN_BYTE + 1] |= next_par_bit + << (curr_bit_pos % BITS_IN_BYTE); + curr_bit_pos += BITS_IN_BYTE + 1; + } + } + + *bits_written = curr_bit_pos; +} + +void bit_buffer_write_bytes_mid( + const BitBuffer* buf, + void* dest, + size_t start_index, + size_t size_bytes) { + furi_assert(buf); + furi_assert(dest); + furi_assert(start_index + size_bytes <= bit_buffer_get_size_bytes(buf)); + + memcpy(dest, buf->data + start_index, size_bytes); +} + +bool bit_buffer_has_partial_byte(const BitBuffer* buf) { + furi_assert(buf); + + return (buf->size_bits % BITS_IN_BYTE) != 0; +} + +bool bit_buffer_starts_with_byte(const BitBuffer* buf, uint8_t byte) { + furi_assert(buf); + + return bit_buffer_get_size_bytes(buf) && (buf->data[0] == byte); +} + +size_t bit_buffer_get_capacity_bytes(const BitBuffer* buf) { + furi_assert(buf); + + return buf->capacity_bytes; +} + +size_t bit_buffer_get_size(const BitBuffer* buf) { + furi_assert(buf); + + return buf->size_bits; +} + +size_t bit_buffer_get_size_bytes(const BitBuffer* buf) { + furi_assert(buf); + + return (buf->size_bits / BITS_IN_BYTE) + (buf->size_bits % BITS_IN_BYTE ? 1 : 0); +} + +uint8_t bit_buffer_get_byte(const BitBuffer* buf, size_t index) { + furi_assert(buf); + furi_assert(buf->capacity_bytes > index); + + return buf->data[index]; +} + +uint8_t bit_buffer_get_byte_from_bit(const BitBuffer* buf, size_t index_bits) { + furi_assert(buf); + furi_assert(buf->capacity_bytes * BITS_IN_BYTE > index_bits); + + const size_t byte_index = index_bits / BITS_IN_BYTE; + const size_t bit_offset = index_bits % BITS_IN_BYTE; + + const uint8_t lo = buf->data[byte_index] >> bit_offset; + const uint8_t hi = buf->data[byte_index + 1] << (BITS_IN_BYTE - bit_offset); + + return lo | hi; +} + +const uint8_t* bit_buffer_get_data(const BitBuffer* buf) { + furi_assert(buf); + + return buf->data; +} + +const uint8_t* bit_buffer_get_parity(const BitBuffer* buf) { + furi_assert(buf); + + return buf->parity; +} + +void bit_buffer_set_byte(BitBuffer* buf, size_t index, uint8_t byte) { + furi_assert(buf); + + const size_t size_bytes = bit_buffer_get_size_bytes(buf); + furi_assert(size_bytes > index); + + buf->data[index] = byte; +} + +void bit_buffer_set_byte_with_parity(BitBuffer* buff, size_t index, uint8_t byte, bool parity) { + furi_assert(buff); + furi_assert(buff->size_bits / BITS_IN_BYTE > index); + + buff->data[index] = byte; + if((index % BITS_IN_BYTE) == 0) { + buff->parity[index / BITS_IN_BYTE] = parity; + } else { + buff->parity[index / BITS_IN_BYTE] |= parity << (index % BITS_IN_BYTE); + } +} + +void bit_buffer_set_size(BitBuffer* buf, size_t new_size) { + furi_assert(buf); + furi_assert(buf->capacity_bytes * BITS_IN_BYTE >= new_size); + + buf->size_bits = new_size; +} + +void bit_buffer_set_size_bytes(BitBuffer* buf, size_t new_size_bytes) { + furi_assert(buf); + furi_assert(buf->capacity_bytes >= new_size_bytes); + + buf->size_bits = new_size_bytes * BITS_IN_BYTE; +} + +void bit_buffer_append(BitBuffer* buf, const BitBuffer* other) { + bit_buffer_append_right(buf, other, 0); +} + +void bit_buffer_append_right(BitBuffer* buf, const BitBuffer* other, size_t start_index) { + furi_assert(buf); + furi_assert(other); + + const size_t size_bytes = bit_buffer_get_size_bytes(buf); + const size_t other_size_bytes = bit_buffer_get_size_bytes(other) - start_index; + + furi_assert(buf->capacity_bytes >= size_bytes + other_size_bytes); + + memcpy(buf->data + size_bytes, other->data + start_index, other_size_bytes); + buf->size_bits += other->size_bits - start_index * BITS_IN_BYTE; +} + +void bit_buffer_append_byte(BitBuffer* buf, uint8_t byte) { + furi_assert(buf); + + const size_t data_size_bytes = bit_buffer_get_size_bytes(buf); + const size_t new_data_size_bytes = data_size_bytes + 1; + furi_assert(new_data_size_bytes <= buf->capacity_bytes); + + buf->data[data_size_bytes] = byte; + buf->size_bits = new_data_size_bytes * BITS_IN_BYTE; +} + +void bit_buffer_append_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes) { + furi_assert(buf); + furi_assert(data); + + const size_t buf_size_bytes = bit_buffer_get_size_bytes(buf); + furi_assert(buf->capacity_bytes >= buf_size_bytes + size_bytes); + + memcpy(&buf->data[buf_size_bytes], data, size_bytes); + buf->size_bits += size_bytes * BITS_IN_BYTE; +} + +void bit_buffer_append_bit(BitBuffer* buf, bool bit) { + furi_assert(buf); + furi_assert( + bit_buffer_get_size_bytes(buf) <= + (buf->capacity_bytes - (bit_buffer_has_partial_byte(buf) ? 0 : 1))); + + if(bit) { + const size_t byte_index = buf->size_bits / BITS_IN_BYTE; + const size_t bit_offset = (buf->size_bits % BITS_IN_BYTE); + buf->data[byte_index] |= 1U << bit_offset; + } + + buf->size_bits++; +} diff --git a/lib/toolbox/bit_buffer.h b/lib/toolbox/bit_buffer.h new file mode 100644 index 00000000000..5c50e729c60 --- /dev/null +++ b/lib/toolbox/bit_buffer.h @@ -0,0 +1,325 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BitBuffer BitBuffer; + +// Construction, deletion, reset + +/** + * Allocate a BitBuffer instance. + * + * @param [in] capacity_bytes maximum buffer capacity, in bytes + * @return pointer to the allocated BitBuffer instance + */ +BitBuffer* bit_buffer_alloc(size_t capacity_bytes); + +/** + * Delete a BitBuffer instance. + * + * @param [in,out] buf pointer to a BitBuffer instance + */ +void bit_buffer_free(BitBuffer* buf); + +/** + * Clear all data from a BitBuffer instance. + * + * @param [in,out] buf pointer to a BitBuffer instance + */ +void bit_buffer_reset(BitBuffer* buf); + +// Copy and write + +/** + * Copy another BitBuffer instance's contents to this one, replacing + * all of the original data. + * The destination capacity must be no less than the source data size. + * + * @param [in,out] buf pointer to a BitBuffer instance to copy into + * @param [in] other pointer to a BitBuffer instance to copy from + * @note + */ +void bit_buffer_copy(BitBuffer* buf, const BitBuffer* other); + +/** + * Copy all BitBuffer instance's contents to this one, starting from start_index, + * replacing all of the original data. + * The destination capacity must be no less than the source data size + * counting from start_index. + * + * @param [in,out] buf pointer to a BitBuffer instance to copy into + * @param [in] other pointer to a BitBuffer instance to copy from + * @param [in] start_index index to begin copying source data from + */ +void bit_buffer_copy_right(BitBuffer* buf, const BitBuffer* other, size_t start_index); + +/** + * Copy all BitBuffer instance's contents to this one, ending with end_index, + * replacing all of the original data. + * The destination capacity must be no less than the source data size + * counting to end_index. + * + * @param [in,out] buf pointer to a BitBuffer instance to copy into + * @param [in] other pointer to a BitBuffer instance to copy from + * @param [in] end_index index to end copying source data at + */ +void bit_buffer_copy_left(BitBuffer* buf, const BitBuffer* other, size_t end_index); + +/** + * Copy a byte array to a BitBuffer instance, replacing all of the original data. + * The destination capacity must be no less than the source data size. + * + * @param [in,out] buf pointer to a BitBuffer instance to copy into + * @param [in] data pointer to the byte array to be copied + * @param [in] size_bytes size of the data to be copied, in bytes + */ +void bit_buffer_copy_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes); + +/** + * Copy a byte array to a BitBuffer instance, replacing all of the original data. + * The destination capacity must be no less than the source data size. + * + * @param [in,out] buf pointer to a BitBuffer instance to copy into + * @param [in] data pointer to the byte array to be copied + * @param [in] size_bits size of the data to be copied, in bits + */ +void bit_buffer_copy_bits(BitBuffer* buf, const uint8_t* data, size_t size_bits); + +/** + * Copy a byte with parity array to a BitBuffer instance, replacing all of the original data. + * The destination capacity must be no less than the source data size. + * + * @param [in,out] buf pointer to a BitBuffer instance to copy into + * @param [in] data pointer to the byte array to be copied + * @param [in] size_bitss size of the data to be copied, in bits + */ +void bit_buffer_copy_bytes_with_parity(BitBuffer* buf, const uint8_t* data, size_t size_bits); + +/** + * Write a BitBuffer instance's entire contents to an arbitrary memory location. + * The destination memory must be allocated. Additionally, the destination + * capacity must be no less than the source data size. + * + * @param [in] buf pointer to a BitBuffer instance to write from + * @param [out] dest pointer to the destination memory location + * @param [in] size_bytes maximum destination data size, in bytes + */ +void bit_buffer_write_bytes(const BitBuffer* buf, void* dest, size_t size_bytes); + +/** + * Write a BitBuffer instance's entire contents to an arbitrary memory location. + * Additionally, place a parity bit after each byte. + * The destination memory must be allocated. Additionally, the destination + * capacity must be no less than the source data size plus parity. + * + * @param [in] buf pointer to a BitBuffer instance to write from + * @param [out] dest pointer to the destination memory location + * @param [in] size_bytes maximum destination data size, in bytes + * @param [out] bits_written actual number of bits writen, in bits + */ +void bit_buffer_write_bytes_with_parity( + const BitBuffer* buf, + void* dest, + size_t size_bytes, + size_t* bits_written); + +/** + * Write a slice of BitBuffer instance's contents to an arbitrary memory location. + * The destination memory must be allocated. Additionally, the destination + * capacity must be no less than the requested slice size. + * + * @param [in] buf pointer to a BitBuffer instance to write from + * @param [out] dest pointer to the destination memory location + * @param [in] start_index index to begin copying source data from + * @param [in] size_bytes data slice size, in bytes + */ +void bit_buffer_write_bytes_mid( + const BitBuffer* buf, + void* dest, + size_t start_index, + size_t size_bytes); + +// Checks + +/** + * Check whether a BitBuffer instance contains a partial byte (i.e. the bit count + * is not divisible by 8). + * + * @param [in] buf pointer to a BitBuffer instance to be checked + * @return true if the instance contains a partial byte, false otherwise + */ +bool bit_buffer_has_partial_byte(const BitBuffer* buf); + +/** + * Check whether a BitBuffer instance's contents start with the designated byte. + * + * @param [in] buf pointer to a BitBuffer instance to be checked + * @param [in] byte byte value to be checked against + * @return true if data starts with designated byte, false otherwise + */ +bool bit_buffer_starts_with_byte(const BitBuffer* buf, uint8_t byte); + +// Getters + +/** + * Get a BitBuffer instance's capacity (i.e. the maximum possible amount of data), in bytes. + * + * @param [in] buf pointer to a BitBuffer instance to be queried + * @return capacity, in bytes + */ +size_t bit_buffer_get_capacity_bytes(const BitBuffer* buf); + +/** + * Get a BitBuffer instance's data size (i.e. the amount of stored data), in bits. + * Might be not divisible by 8 (see bit_buffer_is_partial_byte). + * + * @param [in] buf pointer to a BitBuffer instance to be queried + * @return data size, in bits. + */ +size_t bit_buffer_get_size(const BitBuffer* buf); + +/** + * Get a BitBuffer instance's data size (i.e. the amount of stored data), in bytes. + * If a partial byte is present, it is also counted. + * + * @param [in] buf pointer to a BitBuffer instance to be queried + * @return data size, in bytes. + */ +size_t bit_buffer_get_size_bytes(const BitBuffer* buf); + +/** + * Get a byte value at a specified index in a BitBuffer instance. + * The index must be valid (i.e. less than the instance's data size in bytes). + * + * @param [in] buf pointer to a BitBuffer instance to be queried + * @param [in] index index of the byte in question + */ +uint8_t bit_buffer_get_byte(const BitBuffer* buf, size_t index); + +/** + * Get a byte value starting from the specified bit index in a BitBuffer instance. + * The resulting byte might correspond to a single byte (if the index is a multiple + * of 8), or two overlapping bytes combined. + * The index must be valid (i.e. less than the instance's data size in bits). + * + * @param [in] buf pointer to a BitBuffer instance to be queried + * @param [in] index bit index of the byte in question + */ +uint8_t bit_buffer_get_byte_from_bit(const BitBuffer* buf, size_t index_bits); + +/** + * Get the pointer to a BitBuffer instance's underlying data. + * + * @param [in] buf pointer to a BitBuffer instance to be queried + * @return pointer to the underlying data + */ +const uint8_t* bit_buffer_get_data(const BitBuffer* buf); + +/** + * Get the pointer to a BitBuffer instance's underlying data. + * + * @param [in] buf pointer to a BitBuffer instance to be queried + * @return pointer to the underlying data + */ +const uint8_t* bit_buffer_get_parity(const BitBuffer* buf); + +// Setters + +/** + * Set byte value at a specified index in a BitBuffer instance. + * The index must be valid (i.e. less than the instance's data size in bytes). + * + * @param [in,out] buf pointer to a BitBuffer instance to be modified + * @param [in] index index of the byte in question + * @param [in] byte byte value to be set at index + */ +void bit_buffer_set_byte(BitBuffer* buf, size_t index, uint8_t byte); + +/** + * Set byte and parity bit value at a specified index in a BitBuffer instance. + * The index must be valid (i.e. less than the instance's data size in bytes). + * + * @param [in,out] buf pointer to a BitBuffer instance to be modified + * @param [in] index index of the byte in question + * @param [in] byte byte value to be set at index + * @param [in] parity parity bit value to be set at index + */ +void bit_buffer_set_byte_with_parity(BitBuffer* buff, size_t index, uint8_t byte, bool parity); + +/** + * Resize a BitBuffer instance to a new size, in bits. + * @warning May cause bugs. Use only if absolutely necessary. + * + * @param [in,out] buf pointer to a BitBuffer instance to be resized + * @param [in] new_size the new size of the buffer, in bits + */ +void bit_buffer_set_size(BitBuffer* buf, size_t new_size); + +/** + * Resize a BitBuffer instance to a new size, in bytes. + * @warning May cause bugs. Use only if absolutely necessary. + * + * @param [in,out] buf pointer to a BitBuffer instance to be resized + * @param [in] new_size_bytes the new size of the buffer, in bytes + */ +void bit_buffer_set_size_bytes(BitBuffer* buf, size_t new_size_bytes); + +// Modification + +/** + * Append all BitBuffer's instance contents to this one. The destination capacity + * must be no less than its original data size plus source data size. + * + * @param [in,out] buf pointer to a BitBuffer instance to be appended to + * @param [in] other pointer to a BitBuffer instance to be appended + */ +void bit_buffer_append(BitBuffer* buf, const BitBuffer* other); + +/** + * Append a BitBuffer's instance contents to this one, starting from start_index. + * The destination capacity must be no less than the source data size + * counting from start_index. + * + * @param [in,out] buf pointer to a BitBuffer instance to be appended to + * @param [in] other pointer to a BitBuffer instance to be appended + * @param [in] start_index index to begin copying source data from + */ +void bit_buffer_append_right(BitBuffer* buf, const BitBuffer* other, size_t start_index); + +/** + * Append a byte to a BitBuffer instance. + * The destination capacity must be no less its original data size plus one. + * + * @param [in,out] buf pointer to a BitBuffer instance to be appended to + * @param [in] byte byte value to be appended + */ +void bit_buffer_append_byte(BitBuffer* buf, uint8_t byte); + +/** + * Append a byte array to a BitBuffer instance. + * The destination capacity must be no less its original data size plus source data size. + * + * @param [in,out] buf pointer to a BitBuffer instance to be appended to + * @param [in] data pointer to the byte array to be appended + * @param [in] size_bytes size of the data to be appended, in bytes + */ +void bit_buffer_append_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes); + +/** + * Append a bit to a BitBuffer instance. + * The destination capacity must be sufficient to accomodate the additional bit. + * + * @param [in,out] buf pointer to a BitBuffer instance to be appended to + * @param [in] bit bit value to be appended + */ +void bit_buffer_append_bit(BitBuffer* buf, bool bit); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/buffer_stream.c b/lib/toolbox/buffer_stream.c new file mode 100644 index 00000000000..e8cb334d51c --- /dev/null +++ b/lib/toolbox/buffer_stream.c @@ -0,0 +1,140 @@ +#include "buffer_stream.h" + +struct Buffer { + volatile bool occupied; + volatile size_t size; + uint8_t* data; + size_t max_data_size; +}; + +struct BufferStream { + size_t stream_overrun_count; + FuriStreamBuffer* stream; + + size_t index; + Buffer* buffers; + size_t max_buffers_count; +}; + +bool buffer_write(Buffer* buffer, const uint8_t* data, size_t size) { + if(buffer->occupied) { + return false; + } + if((buffer->size + size) > buffer->max_data_size) { + return false; + } + memcpy(buffer->data + buffer->size, data, size); + buffer->size += size; + return true; +} + +uint8_t* buffer_get_data(Buffer* buffer) { + return buffer->data; +} + +size_t buffer_get_size(Buffer* buffer) { + return buffer->size; +} + +void buffer_reset(Buffer* buffer) { + buffer->occupied = false; + buffer->size = 0; +} + +BufferStream* buffer_stream_alloc(size_t buffer_size, size_t buffers_count) { + furi_assert(buffer_size > 0); + furi_assert(buffers_count > 0); + BufferStream* buffer_stream = malloc(sizeof(BufferStream)); + buffer_stream->max_buffers_count = buffers_count; + buffer_stream->buffers = malloc(sizeof(Buffer) * buffer_stream->max_buffers_count); + for(size_t i = 0; i < buffer_stream->max_buffers_count; i++) { + buffer_stream->buffers[i].occupied = false; + buffer_stream->buffers[i].size = 0; + buffer_stream->buffers[i].data = malloc(buffer_size); + buffer_stream->buffers[i].max_data_size = buffer_size; + } + buffer_stream->stream = furi_stream_buffer_alloc( + sizeof(BufferStream*) * buffer_stream->max_buffers_count, sizeof(BufferStream*)); + buffer_stream->stream_overrun_count = 0; + buffer_stream->index = 0; + + return buffer_stream; +} + +void buffer_stream_free(BufferStream* buffer_stream) { + for(size_t i = 0; i < buffer_stream->max_buffers_count; i++) { + free(buffer_stream->buffers[i].data); + } + furi_stream_buffer_free(buffer_stream->stream); + free(buffer_stream->buffers); + free(buffer_stream); +} + +static inline int8_t buffer_stream_get_free_buffer(BufferStream* buffer_stream) { + int8_t id = -1; + for(size_t i = 0; i < buffer_stream->max_buffers_count; i++) { + if(buffer_stream->buffers[i].occupied == false) { + id = i; + break; + } + } + + return id; +} + +bool buffer_stream_send_from_isr(BufferStream* buffer_stream, const uint8_t* data, size_t size) { + Buffer* buffer = &buffer_stream->buffers[buffer_stream->index]; + bool result = true; + + // write to buffer + if(!buffer_write(buffer, data, size)) { + // if buffer is full - send it + buffer->occupied = true; + // we always have space for buffer in stream + furi_stream_buffer_send(buffer_stream->stream, &buffer, sizeof(Buffer*), 0); + + // get new buffer from the pool + int8_t index = buffer_stream_get_free_buffer(buffer_stream); + + // check that we have valid buffer + if(index == -1) { + // no free buffer + buffer_stream->stream_overrun_count++; + result = false; + } else { + // write to new buffer + buffer_stream->index = index; + buffer = &buffer_stream->buffers[buffer_stream->index]; + buffer_write(buffer, data, size); + } + } + + return result; +} + +Buffer* buffer_stream_receive(BufferStream* buffer_stream, uint32_t timeout) { + Buffer* buffer; + size_t size = + furi_stream_buffer_receive(buffer_stream->stream, &buffer, sizeof(Buffer*), timeout); + + if(size == sizeof(Buffer*)) { + return buffer; + } else { + return NULL; + } +} + +size_t buffer_stream_get_overrun_count(BufferStream* buffer_stream) { + return buffer_stream->stream_overrun_count; +} + +void buffer_stream_reset(BufferStream* buffer_stream) { + FURI_CRITICAL_ENTER(); + furi_stream_buffer_reset(buffer_stream->stream); + + buffer_stream->stream_overrun_count = 0; + for(size_t i = 0; i < buffer_stream->max_buffers_count; i++) { + buffer_reset(&buffer_stream->buffers[i]); + } + FURI_CRITICAL_EXIT(); +} \ No newline at end of file diff --git a/lib/toolbox/buffer_stream.h b/lib/toolbox/buffer_stream.h new file mode 100644 index 00000000000..5c3dc0a3403 --- /dev/null +++ b/lib/toolbox/buffer_stream.h @@ -0,0 +1,89 @@ +/** + * @file buffer_stream.h + * + * This file implements the concept of a buffer stream. + * Data is written to the buffer until the buffer is full. + * Then the buffer pointer is written to the stream, and the new write buffer is taken from the buffer pool. + * After the buffer has been read by the receiving thread, it is sent to the free buffer pool. + * + * This will speed up sending large chunks of data between threads, compared to using a stream directly. + */ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Buffer Buffer; + +/** + * @brief Get buffer data pointer + * @param buffer + * @return uint8_t* + */ +uint8_t* buffer_get_data(Buffer* buffer); + +/** + * @brief Get buffer size + * @param buffer + * @return size_t + */ +size_t buffer_get_size(Buffer* buffer); + +/** + * @brief Reset buffer and send to free buffer pool + * @param buffer + */ +void buffer_reset(Buffer* buffer); + +typedef struct BufferStream BufferStream; + +/** + * @brief Allocate a new BufferStream instance + * @param buffer_size + * @param buffers_count + * @return BufferStream* + */ +BufferStream* buffer_stream_alloc(size_t buffer_size, size_t buffers_count); + +/** + * @brief Free a BufferStream instance + * @param buffer_stream + */ +void buffer_stream_free(BufferStream* buffer_stream); + +/** + * @brief Write data to buffer stream, from ISR context + * Data will be written to the buffer until the buffer is full, and only then will the buffer be sent. + * @param buffer_stream + * @param data + * @param size + * @return bool + */ +bool buffer_stream_send_from_isr(BufferStream* buffer_stream, const uint8_t* data, size_t size); + +/** + * @brief Receive buffer from stream + * @param buffer_stream + * @param timeout + * @return Buffer* + */ +Buffer* buffer_stream_receive(BufferStream* buffer_stream, uint32_t timeout); + +/** + * @brief Get stream overrun count + * @param buffer_stream + * @return size_t + */ +size_t buffer_stream_get_overrun_count(BufferStream* buffer_stream); + +/** + * @brief Reset stream and buffer pool + * @param buffer_stream + */ +void buffer_stream_reset(BufferStream* buffer_stream); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/compress.c b/lib/toolbox/compress.c new file mode 100644 index 00000000000..0d5e1c654d7 --- /dev/null +++ b/lib/toolbox/compress.c @@ -0,0 +1,261 @@ +#include "compress.h" + +#include +#include +#include + +/** Defines encoder and decoder window size */ +#define COMPRESS_EXP_BUFF_SIZE_LOG (8u) + +/** Defines encoder and decoder lookahead buffer size */ +#define COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG (4u) + +/** Buffer sizes for input and output data */ +#define COMPRESS_ICON_ENCODED_BUFF_SIZE (1024u) +#define COMPRESS_ICON_DECODED_BUFF_SIZE (1024u) + +typedef struct { + uint8_t is_compressed; + uint8_t reserved; + uint16_t compressed_buff_size; +} CompressHeader; + +_Static_assert(sizeof(CompressHeader) == 4, "Incorrect CompressHeader size"); + +struct CompressIcon { + heatshrink_decoder* decoder; + uint8_t decoded_buff[COMPRESS_ICON_DECODED_BUFF_SIZE]; +}; + +CompressIcon* compress_icon_alloc() { + CompressIcon* instance = malloc(sizeof(CompressIcon)); + instance->decoder = heatshrink_decoder_alloc( + COMPRESS_ICON_ENCODED_BUFF_SIZE, + COMPRESS_EXP_BUFF_SIZE_LOG, + COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG); + heatshrink_decoder_reset(instance->decoder); + memset(instance->decoded_buff, 0, sizeof(instance->decoded_buff)); + + return instance; +} + +void compress_icon_free(CompressIcon* instance) { + furi_assert(instance); + heatshrink_decoder_free(instance->decoder); + free(instance); +} + +void compress_icon_decode(CompressIcon* instance, const uint8_t* icon_data, uint8_t** decoded_buff) { + furi_assert(instance); + furi_assert(icon_data); + furi_assert(decoded_buff); + + CompressHeader* header = (CompressHeader*)icon_data; + if(header->is_compressed) { + size_t data_processed = 0; + heatshrink_decoder_sink( + instance->decoder, + (uint8_t*)&icon_data[sizeof(CompressHeader)], + header->compressed_buff_size, + &data_processed); + while(1) { + HSD_poll_res res = heatshrink_decoder_poll( + instance->decoder, + instance->decoded_buff, + sizeof(instance->decoded_buff), + &data_processed); + furi_assert((res == HSDR_POLL_EMPTY) || (res == HSDR_POLL_MORE)); + if(res != HSDR_POLL_MORE) { + break; + } + } + heatshrink_decoder_reset(instance->decoder); + *decoded_buff = instance->decoded_buff; + } else { + *decoded_buff = (uint8_t*)&icon_data[1]; + } +} + +struct Compress { + heatshrink_encoder* encoder; + heatshrink_decoder* decoder; +}; + +static void compress_reset(Compress* compress) { + furi_assert(compress); + heatshrink_encoder_reset(compress->encoder); + heatshrink_decoder_reset(compress->decoder); +} + +Compress* compress_alloc(uint16_t compress_buff_size) { + Compress* compress = malloc(sizeof(Compress)); + compress->encoder = + heatshrink_encoder_alloc(COMPRESS_EXP_BUFF_SIZE_LOG, COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG); + compress->decoder = heatshrink_decoder_alloc( + compress_buff_size, COMPRESS_EXP_BUFF_SIZE_LOG, COMPRESS_LOOKAHEAD_BUFF_SIZE_LOG); + + return compress; +} + +void compress_free(Compress* compress) { + furi_assert(compress); + + heatshrink_encoder_free(compress->encoder); + heatshrink_decoder_free(compress->decoder); + free(compress); +} + +bool compress_encode( + Compress* compress, + uint8_t* data_in, + size_t data_in_size, + uint8_t* data_out, + size_t data_out_size, + size_t* data_res_size) { + furi_assert(compress); + furi_assert(data_in); + furi_assert(data_in_size); + + size_t sink_size = 0; + size_t poll_size = 0; + HSE_sink_res sink_res; + HSE_poll_res poll_res; + HSE_finish_res finish_res; + bool encode_failed = false; + size_t sunk = 0; + size_t res_buff_size = sizeof(CompressHeader); + + // Sink data to encoding buffer + while((sunk < data_in_size) && !encode_failed) { + sink_res = heatshrink_encoder_sink( + compress->encoder, &data_in[sunk], data_in_size - sunk, &sink_size); + if(sink_res != HSER_SINK_OK) { + encode_failed = true; + break; + } + sunk += sink_size; + do { + poll_res = heatshrink_encoder_poll( + compress->encoder, + &data_out[res_buff_size], + data_out_size - res_buff_size, + &poll_size); + if(poll_res < 0) { + encode_failed = true; + break; + } + res_buff_size += poll_size; + } while(poll_res == HSER_POLL_MORE); + } + + // Notify sinking complete and poll encoded data + finish_res = heatshrink_encoder_finish(compress->encoder); + if(finish_res < 0) { + encode_failed = true; + } else { + do { + poll_res = heatshrink_encoder_poll( + compress->encoder, + &data_out[res_buff_size], + data_out_size - 4 - res_buff_size, + &poll_size); + if(poll_res < 0) { + encode_failed = true; + break; + } + res_buff_size += poll_size; + finish_res = heatshrink_encoder_finish(compress->encoder); + } while(finish_res != HSER_FINISH_DONE); + } + + bool result = true; + // Write encoded data to output buffer if compression is efficient. Else - write header and original data + if(!encode_failed && (res_buff_size < data_in_size + 1)) { + CompressHeader header = { + .is_compressed = 0x01, .reserved = 0x00, .compressed_buff_size = res_buff_size}; + memcpy(data_out, &header, sizeof(header)); + *data_res_size = res_buff_size; + } else if(data_out_size > data_in_size) { + data_out[0] = 0x00; + memcpy(&data_out[1], data_in, data_in_size); + *data_res_size = data_in_size + 1; + } else { + *data_res_size = 0; + result = false; + } + compress_reset(compress); + + return result; +} + +bool compress_decode( + Compress* compress, + uint8_t* data_in, + size_t data_in_size, + uint8_t* data_out, + size_t data_out_size, + size_t* data_res_size) { + furi_assert(compress); + furi_assert(data_in); + furi_assert(data_out); + furi_assert(data_res_size); + + bool result = false; + bool decode_failed = false; + HSD_sink_res sink_res; + HSD_poll_res poll_res; + HSD_finish_res finish_res; + size_t sink_size = 0; + size_t res_buff_size = 0; + size_t poll_size = 0; + + CompressHeader* header = (CompressHeader*)data_in; + if(header->is_compressed) { + // Sink data to decoding buffer + size_t compressed_size = header->compressed_buff_size; + size_t sunk = sizeof(CompressHeader); + while(sunk < compressed_size && !decode_failed) { + sink_res = heatshrink_decoder_sink( + compress->decoder, &data_in[sunk], compressed_size - sunk, &sink_size); + if(sink_res < 0) { + decode_failed = true; + break; + } + sunk += sink_size; + do { + poll_res = heatshrink_decoder_poll( + compress->decoder, &data_out[res_buff_size], data_out_size, &poll_size); + if(poll_res < 0) { + decode_failed = true; + break; + } + res_buff_size += poll_size; + } while(poll_res == HSDR_POLL_MORE); + } + // Notify sinking complete and poll decoded data + if(!decode_failed) { + finish_res = heatshrink_decoder_finish(compress->decoder); + if(finish_res < 0) { + decode_failed = true; + } else { + do { + poll_res = heatshrink_decoder_poll( + compress->decoder, &data_out[res_buff_size], data_out_size, &poll_size); + res_buff_size += poll_size; + finish_res = heatshrink_decoder_finish(compress->decoder); + } while(finish_res != HSDR_FINISH_DONE); + } + } + *data_res_size = res_buff_size; + result = !decode_failed; + } else if(data_out_size >= data_in_size - 1) { + memcpy(data_out, &data_in[1], data_in_size); + *data_res_size = data_in_size - 1; + result = true; + } else { + result = false; + } + compress_reset(compress); + + return result; +} diff --git a/lib/toolbox/compress.h b/lib/toolbox/compress.h new file mode 100644 index 00000000000..a18551d7f9a --- /dev/null +++ b/lib/toolbox/compress.h @@ -0,0 +1,96 @@ +/** + * @file compress.h + * LZSS based compression HAL API + */ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Compress Icon control structure */ +typedef struct CompressIcon CompressIcon; + +/** Initialize icon compressor + * + * @return Compress Icon instance + */ +CompressIcon* compress_icon_alloc(); + +/** Free icon compressor + * + * @param instance The Compress Icon instance + */ +void compress_icon_free(CompressIcon* instance); + +/** Decompress icon + * + * @warning decoded_buff pointer set by this function is valid till next + * `compress_icon_decode` or `compress_icon_free` call + * + * @param instance The Compress Icon instance + * @param icon_data pointer to icon data + * @param[in] decoded_buff pointer to decoded buffer pointer + */ +void compress_icon_decode(CompressIcon* instance, const uint8_t* icon_data, uint8_t** decoded_buff); + +/** Compress control structure */ +typedef struct Compress Compress; + +/** Allocate encoder and decoder + * + * @param compress_buff_size size of decoder and encoder buffer to allocate + * + * @return Compress instance + */ +Compress* compress_alloc(uint16_t compress_buff_size); + +/** Free encoder and decoder + * + * @param compress Compress instance + */ +void compress_free(Compress* compress); + +/** Encode data + * + * @param compress Compress instance + * @param data_in pointer to input data + * @param data_in_size size of input data + * @param data_out maximum size of output data + * @param data_res_size pointer to result output data size + * + * @return true on success + */ +bool compress_encode( + Compress* compress, + uint8_t* data_in, + size_t data_in_size, + uint8_t* data_out, + size_t data_out_size, + size_t* data_res_size); + +/** Decode data + * + * @param compress Compress instance + * @param data_in pointer to input data + * @param data_in_size size of input data + * @param data_out maximum size of output data + * @param data_res_size pointer to result output data size + * + * @return true on success + */ +bool compress_decode( + Compress* compress, + uint8_t* data_in, + size_t data_in_size, + uint8_t* data_out, + size_t data_out_size, + size_t* data_res_size); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/crc32_calc.c b/lib/toolbox/crc32_calc.c index c8ae3524a11..78295167f38 100644 --- a/lib/toolbox/crc32_calc.c +++ b/lib/toolbox/crc32_calc.c @@ -4,7 +4,7 @@ #define CRC_DATA_BUFFER_MAX_LEN 512 uint32_t crc32_calc_buffer(uint32_t crc, const void* buffer, size_t size) { - // TODO: consider removing dependency on LFS + // TODO FL-3547: consider removing dependency on LFS return ~lfs_crc(~crc, buffer, size); } @@ -14,7 +14,7 @@ uint32_t crc32_calc_file(File* file, const FileCrcProgressCb progress_cb, void* uint32_t file_crc = 0; uint8_t* data_buffer = malloc(CRC_DATA_BUFFER_MAX_LEN); - uint16_t data_buffer_valid_len; + size_t data_buffer_valid_len; uint32_t file_size = storage_file_size(file); diff --git a/lib/toolbox/dir_walk.c b/lib/toolbox/dir_walk.c index 348bd5442d3..509ceb5b42d 100644 --- a/lib/toolbox/dir_walk.c +++ b/lib/toolbox/dir_walk.c @@ -5,7 +5,7 @@ LIST_DEF(DirIndexList, uint32_t); struct DirWalk { File* file; - string_t path; + FuriString* path; DirIndexList_t index_list; uint32_t current_index; bool recursive; @@ -15,7 +15,7 @@ struct DirWalk { DirWalk* dir_walk_alloc(Storage* storage) { DirWalk* dir_walk = malloc(sizeof(DirWalk)); - string_init(dir_walk->path); + dir_walk->path = furi_string_alloc(); dir_walk->file = storage_file_alloc(storage); DirIndexList_init(dir_walk->index_list); dir_walk->recursive = true; @@ -25,7 +25,7 @@ DirWalk* dir_walk_alloc(Storage* storage) { void dir_walk_free(DirWalk* dir_walk) { storage_file_free(dir_walk->file); - string_clear(dir_walk->path); + furi_string_free(dir_walk->path); DirIndexList_clear(dir_walk->index_list); free(dir_walk); } @@ -40,7 +40,7 @@ void dir_walk_set_filter_cb(DirWalk* dir_walk, DirWalkFilterCb cb, void* context } bool dir_walk_open(DirWalk* dir_walk, const char* path) { - string_set(dir_walk->path, path); + furi_string_set(dir_walk->path, path); dir_walk->current_index = 0; return storage_dir_open(dir_walk->file, path); } @@ -53,7 +53,8 @@ static bool dir_walk_filter(DirWalk* dir_walk, const char* name, FileInfo* filei } } -static DirWalkResult dir_walk_iter(DirWalk* dir_walk, string_t return_path, FileInfo* fileinfo) { +static DirWalkResult + dir_walk_iter(DirWalk* dir_walk, FuriString* return_path, FileInfo* fileinfo) { DirWalkResult result = DirWalkError; char* name = malloc(256); // FIXME: remove magic number FileInfo info; @@ -68,7 +69,11 @@ static DirWalkResult dir_walk_iter(DirWalk* dir_walk, string_t return_path, File if(dir_walk_filter(dir_walk, name, &info)) { if(return_path != NULL) { - string_printf(return_path, "%s/%s", string_get_cstr(dir_walk->path), name); + furi_string_printf( //-V576 + return_path, + "%s/%s", + furi_string_get_cstr(dir_walk->path), + name); } if(fileinfo != NULL) { @@ -78,14 +83,14 @@ static DirWalkResult dir_walk_iter(DirWalk* dir_walk, string_t return_path, File 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; storage_dir_close(dir_walk->file); - string_cat_printf(dir_walk->path, "/%s", name); - storage_dir_open(dir_walk->file, string_get_cstr(dir_walk->path)); + furi_string_cat_printf(dir_walk->path, "/%s", name); + storage_dir_open(dir_walk->file, furi_string_get_cstr(dir_walk->path)); } } else if(storage_file_get_error(dir_walk->file) == FSE_NOT_EXIST) { if(DirIndexList_size(dir_walk->index_list) == 0) { @@ -100,12 +105,12 @@ static DirWalkResult dir_walk_iter(DirWalk* dir_walk, string_t return_path, File storage_dir_close(dir_walk->file); - size_t last_char = string_search_rchar(dir_walk->path, '/'); - if(last_char != STRING_FAILURE) { - string_left(dir_walk->path, last_char); + size_t last_char = furi_string_search_rchar(dir_walk->path, '/'); + if(last_char != FURI_STRING_FAILURE) { + furi_string_left(dir_walk->path, last_char); } - storage_dir_open(dir_walk->file, string_get_cstr(dir_walk->path)); + storage_dir_open(dir_walk->file, furi_string_get_cstr(dir_walk->path)); // rewind while(true) { @@ -137,7 +142,7 @@ FS_Error dir_walk_get_error(DirWalk* dir_walk) { return storage_file_get_error(dir_walk->file); } -DirWalkResult dir_walk_read(DirWalk* dir_walk, string_t return_path, FileInfo* fileinfo) { +DirWalkResult dir_walk_read(DirWalk* dir_walk, FuriString* return_path, FileInfo* fileinfo) { return dir_walk_iter(dir_walk, return_path, fileinfo); } @@ -147,6 +152,6 @@ void dir_walk_close(DirWalk* dir_walk) { } DirIndexList_reset(dir_walk->index_list); - string_reset(dir_walk->path); + furi_string_reset(dir_walk->path); dir_walk->current_index = 0; } diff --git a/lib/toolbox/dir_walk.h b/lib/toolbox/dir_walk.h index fc65104200d..535237fc6e2 100644 --- a/lib/toolbox/dir_walk.h +++ b/lib/toolbox/dir_walk.h @@ -66,7 +66,7 @@ FS_Error dir_walk_get_error(DirWalk* dir_walk); * @param fileinfo * @return DirWalkResult */ -DirWalkResult dir_walk_read(DirWalk* dir_walk, string_t return_path, FileInfo* fileinfo); +DirWalkResult dir_walk_read(DirWalk* dir_walk, FuriString* return_path, FileInfo* fileinfo); /** * Close directory diff --git a/lib/toolbox/float_tools.c b/lib/toolbox/float_tools.c new file mode 100644 index 00000000000..9c0fe871e74 --- /dev/null +++ b/lib/toolbox/float_tools.c @@ -0,0 +1,8 @@ +#include "float_tools.h" + +#include +#include + +bool float_is_equal(float a, float b) { + return fabsf(a - b) <= FLT_EPSILON * fmaxf(fabsf(a), fabsf(b)); +} diff --git a/lib/toolbox/float_tools.h b/lib/toolbox/float_tools.h new file mode 100644 index 00000000000..0b758e9f5ec --- /dev/null +++ b/lib/toolbox/float_tools.h @@ -0,0 +1,19 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** Compare two floating point numbers + * @param a First number to compare + * @param b Second number to compare + * + * @return bool true if a equals b, false otherwise + */ +bool float_is_equal(float a, float b); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/hex.c b/lib/toolbox/hex.c index 41bb24bba74..25dcb09500e 100644 --- a/lib/toolbox/hex.c +++ b/lib/toolbox/hex.c @@ -1,21 +1,21 @@ #include "hex.h" bool hex_char_to_hex_nibble(char c, uint8_t* nibble) { - if((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) { - if((c >= '0' && c <= '9')) { - *nibble = c - '0'; - } else if((c >= 'A' && c <= 'F')) { - *nibble = c - 'A' + 10; - } else { - *nibble = c - 'a' + 10; - } + if((c >= '0' && c <= '9')) { + *nibble = c - '0'; + return true; + } else if((c >= 'A' && c <= 'F')) { + *nibble = c - 'A' + 10; + return true; + } else if(c >= 'a' && c <= 'f') { + *nibble = c - 'a' + 10; return true; } else { return false; } } -bool hex_chars_to_uint8(char hi, char low, uint8_t* value) { +bool hex_char_to_uint8(char hi, char low, uint8_t* value) { uint8_t hi_nibble_value, low_nibble_value; if(hex_char_to_hex_nibble(hi, &hi_nibble_value) && @@ -27,13 +27,29 @@ bool hex_chars_to_uint8(char hi, char low, uint8_t* value) { } } +bool hex_chars_to_uint8(const char* value_str, uint8_t* value) { + bool parse_success = false; + while(*value_str && value_str[1]) { + parse_success = hex_char_to_uint8(*value_str, value_str[1], value++); + if(!parse_success) break; + value_str += 2; + } + return parse_success; +} + bool hex_chars_to_uint64(const char* value_str, uint64_t* value) { uint8_t* _value = (uint8_t*)value; bool parse_success = false; for(uint8_t i = 0; i < 8; i++) { - parse_success = hex_chars_to_uint8(value_str[i * 2], value_str[i * 2 + 1], &_value[7 - i]); + parse_success = hex_char_to_uint8(value_str[i * 2], value_str[i * 2 + 1], &_value[7 - i]); if(!parse_success) break; } return parse_success; } + +void uint8_to_hex_chars(const uint8_t* src, uint8_t* target, int length) { + const char chars[] = "0123456789ABCDEF"; + while(--length >= 0) + target[length] = chars[(src[length >> 1] >> ((1 - (length & 1)) << 2)) & 0xF]; +} diff --git a/lib/toolbox/hex.h b/lib/toolbox/hex.h index c6683a52cdc..740f23b776c 100644 --- a/lib/toolbox/hex.h +++ b/lib/toolbox/hex.h @@ -14,14 +14,22 @@ extern "C" { */ bool hex_char_to_hex_nibble(char c, uint8_t* nibble); -/** Convert ASCII hex values to byte +/** Convert ASCII hex value to byte * @param hi hi nibble text * @param low low nibble text * @param value output value * * @return bool conversion status */ -bool hex_chars_to_uint8(char hi, char low, uint8_t* value); +bool hex_char_to_uint8(char hi, char low, uint8_t* value); + +/** Convert ASCII hex values to uint8_t + * @param value_str ASCII data + * @param value output value + * + * @return bool conversion status + */ +bool hex_chars_to_uint8(const char* value_str, uint8_t* value); /** Convert ASCII hex values to uint64_t * @param value_str ASCII 64 bi data @@ -31,6 +39,14 @@ bool hex_chars_to_uint8(char hi, char low, uint8_t* value); */ bool hex_chars_to_uint64(const char* value_str, uint64_t* value); +/** Convert uint8_t to ASCII hex values + * @param src source data + * @param target output value + * @param length data length + * + */ +void uint8_to_hex_chars(const uint8_t* src, uint8_t* target, int length); + #ifdef __cplusplus } #endif diff --git a/lib/toolbox/hmac_sha256.c b/lib/toolbox/hmac_sha256.c deleted file mode 100644 index 611aa2a6f48..00000000000 --- a/lib/toolbox/hmac_sha256.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * hmac.c - HMAC - * - * Copyright (C) 2017 Sergei Glushchenko - * Author: Sergei Glushchenko - * - * This file is a part of U2F firmware for STM32 - * - * 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 . - * - * As additional permission under GNU GPL version 3 section 7, you may - * distribute non-source form of the Program without the copy of the - * GNU GPL normally required by section 4, provided you inform the - * recipients of GNU GPL by a written offer. - * - */ -#include - -#include "sha256.h" -#include "hmac_sha256.h" - -static void _hmac_sha256_init(const hmac_context* ctx) { - hmac_sha256_context* context = (hmac_sha256_context*)ctx; - sha256_start(&context->sha_ctx); -} - -static void - _hmac_sha256_update(const hmac_context* ctx, const uint8_t* message, unsigned message_size) { - hmac_sha256_context* context = (hmac_sha256_context*)ctx; - sha256_update(&context->sha_ctx, message, message_size); -} - -static void _hmac_sha256_finish(const hmac_context* ctx, uint8_t* hash_result) { - hmac_sha256_context* context = (hmac_sha256_context*)ctx; - sha256_finish(&context->sha_ctx, hash_result); -} - -/* Compute an HMAC using K as a key (as in RFC 6979). Note that K is always - the same size as the hash result size. */ -static void hmac_init(const hmac_context* ctx, const uint8_t* K) { - uint8_t* pad = ctx->tmp + 2 * ctx->result_size; - unsigned i; - for(i = 0; i < ctx->result_size; ++i) pad[i] = K[i] ^ 0x36; - for(; i < ctx->block_size; ++i) pad[i] = 0x36; - - ctx->init_hash(ctx); - ctx->update_hash(ctx, pad, ctx->block_size); -} - -static void hmac_update(const hmac_context* ctx, const uint8_t* message, unsigned message_size) { - ctx->update_hash(ctx, message, message_size); -} - -static void hmac_finish(const hmac_context* ctx, const uint8_t* K, uint8_t* result) { - uint8_t* pad = ctx->tmp + 2 * ctx->result_size; - unsigned i; - for(i = 0; i < ctx->result_size; ++i) pad[i] = K[i] ^ 0x5c; - for(; i < ctx->block_size; ++i) pad[i] = 0x5c; - - ctx->finish_hash(ctx, result); - - ctx->init_hash(ctx); - ctx->update_hash(ctx, pad, ctx->block_size); - ctx->update_hash(ctx, result, ctx->result_size); - ctx->finish_hash(ctx, result); -} - -void hmac_sha256_init(hmac_sha256_context* ctx, const uint8_t* K) { - ctx->hmac_ctx.init_hash = _hmac_sha256_init; - ctx->hmac_ctx.update_hash = _hmac_sha256_update; - ctx->hmac_ctx.finish_hash = _hmac_sha256_finish; - ctx->hmac_ctx.block_size = 64; - ctx->hmac_ctx.result_size = 32; - ctx->hmac_ctx.tmp = ctx->tmp; - hmac_init(&ctx->hmac_ctx, K); -} - -void hmac_sha256_update( - const hmac_sha256_context* ctx, - const uint8_t* message, - unsigned message_size) { - hmac_update(&ctx->hmac_ctx, message, message_size); -} - -void hmac_sha256_finish(const hmac_sha256_context* ctx, const uint8_t* K, uint8_t* hash_result) { - hmac_finish(&ctx->hmac_ctx, K, hash_result); -} diff --git a/lib/toolbox/hmac_sha256.h b/lib/toolbox/hmac_sha256.h deleted file mode 100644 index 01fc0b1465c..00000000000 --- a/lib/toolbox/hmac_sha256.h +++ /dev/null @@ -1,27 +0,0 @@ - -typedef struct hmac_context { - void (*init_hash)(const struct hmac_context* context); - void (*update_hash)( - const struct hmac_context* context, - const uint8_t* message, - unsigned message_size); - void (*finish_hash)(const struct hmac_context* context, uint8_t* hash_result); - unsigned block_size; /* Hash function block size in bytes, eg 64 for SHA-256. */ - unsigned result_size; /* Hash function result size in bytes, eg 32 for SHA-256. */ - uint8_t* tmp; /* Must point to a buffer of at least (2 * result_size + block_size) bytes. */ -} hmac_context; - -typedef struct hmac_sha256_context { - hmac_context hmac_ctx; - sha256_context sha_ctx; - uint8_t tmp[32 * 2 + 64]; -} hmac_sha256_context; - -void hmac_sha256_init(hmac_sha256_context* ctx, const uint8_t* K); - -void hmac_sha256_update( - const hmac_sha256_context* ctx, - const uint8_t* message, - unsigned message_size); - -void hmac_sha256_finish(const hmac_sha256_context* ctx, const uint8_t* K, uint8_t* hash_result); diff --git a/lib/toolbox/level_duration.h b/lib/toolbox/level_duration.h index 9406cef3145..7618344ac3c 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/m_cstr_dup.h b/lib/toolbox/m_cstr_dup.h new file mode 100644 index 00000000000..11b7fe35ada --- /dev/null +++ b/lib/toolbox/m_cstr_dup.h @@ -0,0 +1,17 @@ +#pragma once +#include + +#define M_INIT_DUP(a) ((a) = strdup("")) +#define M_INIT_SET_DUP(a, b) ((a) = strdup(b)) +#define M_SET_DUP(a, b) (free((void*)a), (a) = strdup(b)) +#define M_CLEAR_DUP(a) (free((void*)a)) + +#define M_CSTR_DUP_OPLIST \ + (INIT(M_INIT_DUP), \ + INIT_SET(M_INIT_SET_DUP), \ + SET(M_SET_DUP), \ + CLEAR(M_CLEAR_DUP), \ + HASH(m_core_cstr_hash), \ + EQUAL(M_CSTR_EQUAL), \ + CMP(strcmp), \ + TYPE(const char*)) diff --git a/lib/toolbox/md5.c b/lib/toolbox/md5.c deleted file mode 100644 index 3cf7cf05c07..00000000000 --- a/lib/toolbox/md5.c +++ /dev/null @@ -1,299 +0,0 @@ -/******************************************************************************* -* Portions COPYRIGHT 2015 STMicroelectronics * -* Portions Copyright (C) 2006-2013, Brainspark B.V. * -*******************************************************************************/ - -/* - * RFC 1321 compliant MD5 implementation - * - * Copyright (C) 2006-2013, Brainspark B.V. - * - * This file is part of PolarSSL (http://www.polarssl.org) - * Lead Maintainer: Paul Bakker - * - * All rights reserved. - * - * 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 2 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/* - * The MD5 algorithm was designed by Ron Rivest in 1991. - * - * http://www.ietf.org/rfc/rfc1321.txt - */ - -/** - ****************************************************************************** - * @file md5.c - * @author MCD Application Team - * @brief This file has been modified to support the hardware Cryptographic and - * Hash processors embedded in STM32F415xx/417xx/437xx/439xx/756xx devices. - * This support is activated by defining the "USE_STM32F4XX_HW_CRYPTO" - * or "USE_STM32F7XX_HW_CRYPTO" macro in PolarSSL config.h file. - ****************************************************************************** - * @attention - * - * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/software_license_agreement_liberty_v2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - ****************************************************************************** - */ - -#include "md5.h" - -/* - * 32-bit integer manipulation macros (little endian) - */ -#ifndef GET_UINT32_LE -#define GET_UINT32_LE(n, b, i) \ - { \ - (n) = ((uint32_t)(b)[(i)]) | ((uint32_t)(b)[(i) + 1] << 8) | \ - ((uint32_t)(b)[(i) + 2] << 16) | ((uint32_t)(b)[(i) + 3] << 24); \ - } -#endif - -#ifndef PUT_UINT32_LE -#define PUT_UINT32_LE(n, b, i) \ - { \ - (b)[(i)] = (unsigned char)((n)); \ - (b)[(i) + 1] = (unsigned char)((n) >> 8); \ - (b)[(i) + 2] = (unsigned char)((n) >> 16); \ - (b)[(i) + 3] = (unsigned char)((n) >> 24); \ - } -#endif - -/* - * MD5 context setup - */ -void md5_starts(md5_context* ctx) { - ctx->total[0] = 0; - ctx->total[1] = 0; - - ctx->state[0] = 0x67452301; - ctx->state[1] = 0xEFCDAB89; - ctx->state[2] = 0x98BADCFE; - ctx->state[3] = 0x10325476; -} - -void md5_process(md5_context* ctx, const unsigned char data[64]) { - uint32_t X[16], A, B, C, D; - - GET_UINT32_LE(X[0], data, 0); - GET_UINT32_LE(X[1], data, 4); - GET_UINT32_LE(X[2], data, 8); - GET_UINT32_LE(X[3], data, 12); - GET_UINT32_LE(X[4], data, 16); - GET_UINT32_LE(X[5], data, 20); - GET_UINT32_LE(X[6], data, 24); - GET_UINT32_LE(X[7], data, 28); - GET_UINT32_LE(X[8], data, 32); - GET_UINT32_LE(X[9], data, 36); - GET_UINT32_LE(X[10], data, 40); - GET_UINT32_LE(X[11], data, 44); - GET_UINT32_LE(X[12], data, 48); - GET_UINT32_LE(X[13], data, 52); - GET_UINT32_LE(X[14], data, 56); - GET_UINT32_LE(X[15], data, 60); - -#define S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) - -#define P(a, b, c, d, k, s, t) \ - { \ - a += F(b, c, d) + X[k] + t; \ - a = S(a, s) + b; \ - } - - A = ctx->state[0]; - B = ctx->state[1]; - C = ctx->state[2]; - D = ctx->state[3]; - -#define F(x, y, z) (z ^ (x & (y ^ z))) - - P(A, B, C, D, 0, 7, 0xD76AA478); - P(D, A, B, C, 1, 12, 0xE8C7B756); - P(C, D, A, B, 2, 17, 0x242070DB); - P(B, C, D, A, 3, 22, 0xC1BDCEEE); - P(A, B, C, D, 4, 7, 0xF57C0FAF); - P(D, A, B, C, 5, 12, 0x4787C62A); - P(C, D, A, B, 6, 17, 0xA8304613); - P(B, C, D, A, 7, 22, 0xFD469501); - P(A, B, C, D, 8, 7, 0x698098D8); - P(D, A, B, C, 9, 12, 0x8B44F7AF); - P(C, D, A, B, 10, 17, 0xFFFF5BB1); - P(B, C, D, A, 11, 22, 0x895CD7BE); - P(A, B, C, D, 12, 7, 0x6B901122); - P(D, A, B, C, 13, 12, 0xFD987193); - P(C, D, A, B, 14, 17, 0xA679438E); - P(B, C, D, A, 15, 22, 0x49B40821); - -#undef F - -#define F(x, y, z) (y ^ (z & (x ^ y))) - - P(A, B, C, D, 1, 5, 0xF61E2562); - P(D, A, B, C, 6, 9, 0xC040B340); - P(C, D, A, B, 11, 14, 0x265E5A51); - P(B, C, D, A, 0, 20, 0xE9B6C7AA); - P(A, B, C, D, 5, 5, 0xD62F105D); - P(D, A, B, C, 10, 9, 0x02441453); - P(C, D, A, B, 15, 14, 0xD8A1E681); - P(B, C, D, A, 4, 20, 0xE7D3FBC8); - P(A, B, C, D, 9, 5, 0x21E1CDE6); - P(D, A, B, C, 14, 9, 0xC33707D6); - P(C, D, A, B, 3, 14, 0xF4D50D87); - P(B, C, D, A, 8, 20, 0x455A14ED); - P(A, B, C, D, 13, 5, 0xA9E3E905); - P(D, A, B, C, 2, 9, 0xFCEFA3F8); - P(C, D, A, B, 7, 14, 0x676F02D9); - P(B, C, D, A, 12, 20, 0x8D2A4C8A); - -#undef F - -#define F(x, y, z) (x ^ y ^ z) - - P(A, B, C, D, 5, 4, 0xFFFA3942); - P(D, A, B, C, 8, 11, 0x8771F681); - P(C, D, A, B, 11, 16, 0x6D9D6122); - P(B, C, D, A, 14, 23, 0xFDE5380C); - P(A, B, C, D, 1, 4, 0xA4BEEA44); - P(D, A, B, C, 4, 11, 0x4BDECFA9); - P(C, D, A, B, 7, 16, 0xF6BB4B60); - P(B, C, D, A, 10, 23, 0xBEBFBC70); - P(A, B, C, D, 13, 4, 0x289B7EC6); - P(D, A, B, C, 0, 11, 0xEAA127FA); - P(C, D, A, B, 3, 16, 0xD4EF3085); - P(B, C, D, A, 6, 23, 0x04881D05); - P(A, B, C, D, 9, 4, 0xD9D4D039); - P(D, A, B, C, 12, 11, 0xE6DB99E5); - P(C, D, A, B, 15, 16, 0x1FA27CF8); - P(B, C, D, A, 2, 23, 0xC4AC5665); - -#undef F - -#define F(x, y, z) (y ^ (x | ~z)) - - P(A, B, C, D, 0, 6, 0xF4292244); - P(D, A, B, C, 7, 10, 0x432AFF97); - P(C, D, A, B, 14, 15, 0xAB9423A7); - P(B, C, D, A, 5, 21, 0xFC93A039); - P(A, B, C, D, 12, 6, 0x655B59C3); - P(D, A, B, C, 3, 10, 0x8F0CCC92); - P(C, D, A, B, 10, 15, 0xFFEFF47D); - P(B, C, D, A, 1, 21, 0x85845DD1); - P(A, B, C, D, 8, 6, 0x6FA87E4F); - P(D, A, B, C, 15, 10, 0xFE2CE6E0); - P(C, D, A, B, 6, 15, 0xA3014314); - P(B, C, D, A, 13, 21, 0x4E0811A1); - P(A, B, C, D, 4, 6, 0xF7537E82); - P(D, A, B, C, 11, 10, 0xBD3AF235); - P(C, D, A, B, 2, 15, 0x2AD7D2BB); - P(B, C, D, A, 9, 21, 0xEB86D391); - -#undef F - - ctx->state[0] += A; - ctx->state[1] += B; - ctx->state[2] += C; - ctx->state[3] += D; -} - -/* - * MD5 process buffer - */ -void md5_update(md5_context* ctx, const unsigned char* input, size_t ilen) { - size_t fill; - uint32_t left; - - if(ilen <= 0) return; - - left = ctx->total[0] & 0x3F; - fill = 64 - left; - - ctx->total[0] += (uint32_t)ilen; - ctx->total[0] &= 0xFFFFFFFF; - - if(ctx->total[0] < (uint32_t)ilen) ctx->total[1]++; - - if(left && ilen >= fill) { - memcpy((void*)(ctx->buffer + left), input, fill); - md5_process(ctx, ctx->buffer); - input += fill; - ilen -= fill; - left = 0; - } - - while(ilen >= 64) { - md5_process(ctx, input); - input += 64; - ilen -= 64; - } - - if(ilen > 0) { - memcpy((void*)(ctx->buffer + left), input, ilen); - } -} - -static const unsigned char md5_padding[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - -/* - * MD5 final digest - */ -void md5_finish(md5_context* ctx, unsigned char output[16]) { - uint32_t last, padn; - uint32_t high, low; - unsigned char msglen[8]; - - high = (ctx->total[0] >> 29) | (ctx->total[1] << 3); - low = (ctx->total[0] << 3); - - PUT_UINT32_LE(low, msglen, 0); - PUT_UINT32_LE(high, msglen, 4); - - last = ctx->total[0] & 0x3F; - padn = (last < 56) ? (56 - last) : (120 - last); - - md5_update(ctx, md5_padding, padn); - md5_update(ctx, msglen, 8); - - PUT_UINT32_LE(ctx->state[0], output, 0); - PUT_UINT32_LE(ctx->state[1], output, 4); - PUT_UINT32_LE(ctx->state[2], output, 8); - PUT_UINT32_LE(ctx->state[3], output, 12); -} - -/* - * output = MD5( input buffer ) - */ -void md5(const unsigned char* input, size_t ilen, unsigned char output[16]) { - md5_context ctx; - - md5_starts(&ctx); - md5_update(&ctx, input, ilen); - md5_finish(&ctx, output); - - memset(&ctx, 0, sizeof(md5_context)); -} diff --git a/lib/toolbox/md5.h b/lib/toolbox/md5.h deleted file mode 100644 index fe53db8d3e5..00000000000 --- a/lib/toolbox/md5.h +++ /dev/null @@ -1,83 +0,0 @@ -/** - * \file md5.h - * - * \brief MD5 message digest algorithm (hash function) - * - * Copyright (C) 2006-2013, Brainspark B.V. - * - * This file is part of PolarSSL (http://www.polarssl.org) - * Lead Maintainer: Paul Bakker - * - * All rights reserved. - * - * 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 2 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include - -/** - * \brief MD5 context structure - */ -typedef struct { - uint32_t total[2]; /*!< number of bytes processed */ - uint32_t state[4]; /*!< intermediate digest state */ - unsigned char buffer[64]; /*!< data block being processed */ -} md5_context; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief MD5 context setup - * - * \param ctx context to be initialized - */ -void md5_starts(md5_context* ctx); - -/** - * \brief MD5 process buffer - * - * \param ctx MD5 context - * \param input buffer holding the data - * \param ilen length of the input data - */ -void md5_update(md5_context* ctx, const unsigned char* input, size_t ilen); - -/** - * \brief MD5 final digest - * - * \param ctx MD5 context - * \param output MD5 checksum result - */ -void md5_finish(md5_context* ctx, unsigned char output[16]); - -/* Internal use */ -void md5_process(md5_context* ctx, const unsigned char data[64]); - -/** - * \brief Output = MD5( input buffer ) - * - * \param input buffer holding the data - * \param ilen length of the input data - * \param output MD5 checksum result - */ -void md5(const unsigned char* input, size_t ilen, unsigned char output[16]); - -#ifdef __cplusplus -} -#endif diff --git a/lib/toolbox/md5_calc.c b/lib/toolbox/md5_calc.c new file mode 100644 index 00000000000..59c9403e812 --- /dev/null +++ b/lib/toolbox/md5_calc.c @@ -0,0 +1,58 @@ +#include "md5_calc.h" + +#include +#include +#include + +bool md5_calc_file(File* file, const char* path, unsigned char output[16], FS_Error* file_error) { + if(!storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) { + if(file_error != NULL) { + *file_error = storage_file_get_error(file); + } + return false; + } + + const size_t size_to_read = 512; + uint8_t* data = malloc(size_to_read); + bool result = true; + + mbedtls_md5_context* md5_ctx = malloc(sizeof(mbedtls_md5_context)); + mbedtls_md5_init(md5_ctx); + mbedtls_md5_starts(md5_ctx); + while(true) { + size_t read_size = storage_file_read(file, data, size_to_read); + if(storage_file_get_error(file) != FSE_OK) { + result = false; + break; + } + if(read_size == 0) { + break; + } + mbedtls_md5_update(md5_ctx, data, read_size); + } + mbedtls_md5_finish(md5_ctx, output); + free(md5_ctx); + free(data); + + if(file_error != NULL) { + *file_error = storage_file_get_error(file); + } + + storage_file_close(file); + return result; +} + +bool md5_string_calc_file(File* file, const char* path, FuriString* output, FS_Error* file_error) { + const size_t hash_size = 16; + unsigned char hash[hash_size]; + bool result = md5_calc_file(file, path, hash, file_error); + + if(result) { + furi_string_set(output, ""); + for(size_t i = 0; i < hash_size; i++) { + furi_string_cat_printf(output, "%02x", hash[i]); + } + } + + return result; +} \ No newline at end of file diff --git a/lib/toolbox/md5_calc.h b/lib/toolbox/md5_calc.h new file mode 100644 index 00000000000..cf82e37181c --- /dev/null +++ b/lib/toolbox/md5_calc.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool md5_calc_file(File* file, const char* path, unsigned char output[16], FS_Error* file_error); + +bool md5_string_calc_file(File* file, const char* path, FuriString* output, FS_Error* file_error); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/name_generator.c b/lib/toolbox/name_generator.c new file mode 100644 index 00000000000..36366485acc --- /dev/null +++ b/lib/toolbox/name_generator.c @@ -0,0 +1,75 @@ +#include "name_generator.h" + +#include +#include +#include +#include +#include +#include + +const char* const name_generator_left[] = { + "ancient", "hollow", "strange", "disappeared", "unknown", "unthinkable", "unnameable", + "nameless", "my", "concealed", "forgotten", "hidden", "mysterious", "obscure", + "random", "remote", "uncharted", "undefined", "untraveled", "untold", +}; + +const char* const name_generator_right[] = { + "door", + "entrance", + "doorway", + "entry", + "portal", + "entree", + "opening", + "crack", + "access", + "corridor", + "passage", + "port", +}; + +void name_generator_make_auto(char* name, size_t max_name_size, const char* prefix) { + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDetailedFilename)) { + name_generator_make_detailed(name, max_name_size, prefix); + } else { + name_generator_make_random(name, max_name_size); + } +} + +void name_generator_make_random(char* name, size_t max_name_size) { + furi_assert(name); + furi_assert(max_name_size); + + uint8_t name_generator_left_i = rand() % COUNT_OF(name_generator_left); + uint8_t name_generator_right_i = rand() % COUNT_OF(name_generator_right); + + snprintf( + name, + max_name_size, + "%s_%s", + name_generator_left[name_generator_left_i], + name_generator_right[name_generator_right_i]); + + // Set first symbol to upper case + name[0] = name[0] - 0x20; +} + +void name_generator_make_detailed(char* name, size_t max_name_size, const char* prefix) { + furi_assert(name); + furi_assert(max_name_size); + furi_assert(prefix); + + FuriHalRtcDateTime dateTime; + furi_hal_rtc_get_datetime(&dateTime); + + snprintf( + name, + max_name_size, + "%s-%.4d_%.2d_%.2d-%.2d_%.2d", + prefix, + dateTime.year, + dateTime.month, + dateTime.day, + dateTime.hour, + dateTime.minute); +} diff --git a/lib/toolbox/name_generator.h b/lib/toolbox/name_generator.h new file mode 100644 index 00000000000..bc17d54cd57 --- /dev/null +++ b/lib/toolbox/name_generator.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Generates detailed/random name based on furi_hal flags + * + * @param name buffer to write random name + * @param max_name_size length of given buffer + * @param[in] prefix The prefix of the name + */ +void name_generator_make_auto(char* name, size_t max_name_size, const char* prefix); + +/** Generates random name + * + * @param name buffer to write random name + * @param max_name_size length of given buffer + */ +void name_generator_make_random(char* name, size_t max_name_size); + +/** Generates detailed name + * + * @param name buffer to write random name + * @param max_name_size length of given buffer + * @param[in] prefix The prefix of the name + */ +void name_generator_make_detailed(char* name, size_t max_name_size, const char* prefix); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/toolbox/path.c b/lib/toolbox/path.c index 1262362eff6..3d161a19630 100644 --- a/lib/toolbox/path.c +++ b/lib/toolbox/path.c @@ -1,86 +1,85 @@ #include "path.h" -#include "m-string.h" #include -void path_extract_filename_no_ext(const char* path, string_t filename) { - string_set(filename, path); +void path_extract_filename_no_ext(const char* path, FuriString* filename) { + furi_string_set(filename, path); - size_t start_position = string_search_rchar(filename, '/'); - size_t end_position = string_search_rchar(filename, '.'); + size_t start_position = furi_string_search_rchar(filename, '/'); + size_t end_position = furi_string_search_rchar(filename, '.'); - if(start_position == STRING_FAILURE) { + if(start_position == FURI_STRING_FAILURE) { start_position = 0; } else { start_position += 1; } - if(end_position == STRING_FAILURE) { - end_position = string_size(filename); + if(end_position == FURI_STRING_FAILURE) { + end_position = furi_string_size(filename); } - string_mid(filename, start_position, end_position - start_position); + furi_string_mid(filename, start_position, end_position - start_position); } -void path_extract_filename(string_t path, string_t name, bool trim_ext) { - size_t filename_start = string_search_rchar(path, '/'); +void path_extract_filename(FuriString* path, FuriString* name, bool trim_ext) { + size_t filename_start = furi_string_search_rchar(path, '/'); if(filename_start > 0) { filename_start++; - string_set_n(name, path, filename_start, string_size(path) - filename_start); + furi_string_set_n(name, path, filename_start, furi_string_size(path) - filename_start); } if(trim_ext) { - size_t dot = string_search_rchar(name, '.'); + size_t dot = furi_string_search_rchar(name, '.'); if(dot > 0) { - string_left(name, dot); + furi_string_left(name, dot); } } } -void path_extract_extension(string_t path, char* ext, size_t ext_len_max) { - size_t dot = string_search_rchar(path, '.'); - size_t filename_start = string_search_rchar(path, '/'); +void path_extract_extension(FuriString* path, char* ext, size_t ext_len_max) { + size_t dot = furi_string_search_rchar(path, '.'); + size_t filename_start = furi_string_search_rchar(path, '/'); - if((dot > 0) && (filename_start < dot)) { - strlcpy(ext, &(string_get_cstr(path))[dot], ext_len_max); + if((dot != FURI_STRING_FAILURE) && (filename_start < dot)) { + strlcpy(ext, &(furi_string_get_cstr(path))[dot], ext_len_max); } } -static inline void path_cleanup(string_t path) { - string_strim(path); - while(string_end_with_str_p(path, "/")) { - string_left(path, string_size(path) - 1); +static inline void path_cleanup(FuriString* path) { + furi_string_trim(path); + while(furi_string_end_with(path, "/")) { + furi_string_left(path, furi_string_size(path) - 1); } } -void path_extract_basename(const char* path, string_t basename) { - string_set(basename, path); +void path_extract_basename(const char* path, FuriString* basename) { + furi_string_set(basename, path); path_cleanup(basename); - size_t pos = string_search_rchar(basename, '/'); - if(pos != STRING_FAILURE) { - string_right(basename, pos + 1); + size_t pos = furi_string_search_rchar(basename, '/'); + if(pos != FURI_STRING_FAILURE) { + furi_string_right(basename, pos + 1); } } -void path_extract_dirname(const char* path, string_t dirname) { - string_set(dirname, path); +void path_extract_dirname(const char* path, FuriString* dirname) { + furi_string_set(dirname, path); path_cleanup(dirname); - size_t pos = string_search_rchar(dirname, '/'); - if(pos != STRING_FAILURE) { - string_left(dirname, pos); + size_t pos = furi_string_search_rchar(dirname, '/'); + if(pos != FURI_STRING_FAILURE) { + furi_string_left(dirname, pos); } } -void path_append(string_t path, const char* suffix) { +void path_append(FuriString* path, const char* suffix) { path_cleanup(path); - string_t suffix_str; - string_init_set(suffix_str, suffix); - string_strim(suffix_str); - string_strim(suffix_str, "/"); - string_cat_printf(path, "/%s", string_get_cstr(suffix_str)); - string_clear(suffix_str); + FuriString* suffix_str; + suffix_str = furi_string_alloc_set(suffix); + furi_string_trim(suffix_str); + furi_string_trim(suffix_str, "/"); + furi_string_cat_printf(path, "/%s", furi_string_get_cstr(suffix_str)); + furi_string_free(suffix_str); } -void path_concat(const char* path, const char* suffix, string_t out_path) { - string_set(out_path, path); +void path_concat(const char* path, const char* suffix, FuriString* out_path) { + furi_string_set(out_path, path); path_append(out_path, suffix); } @@ -96,22 +95,17 @@ bool path_contains_only_ascii(const char* path) { name_pos++; } - while(*name_pos != '\0') { - if((*name_pos >= '0') && (*name_pos <= '9')) { - name_pos++; - continue; - } else if((*name_pos >= 'A') && (*name_pos <= 'Z')) { - name_pos++; - continue; - } else if((*name_pos >= 'a') && (*name_pos <= 'z')) { - name_pos++; - continue; - } else if(strchr(" .!#\\$%&'()-@^_`{}~", *name_pos) != NULL) { - name_pos++; - continue; - } + for(; *name_pos; ++name_pos) { + const char c = *name_pos; - return false; + // Regular ASCII characters from 0x20 to 0x7e + const bool is_out_of_range = (c < ' ') || (c > '~'); + // Cross-platform forbidden character set + const bool is_forbidden = strchr("\\<>*|\":?", c); + + if(is_out_of_range || is_forbidden) { + return false; + } } return true; diff --git a/lib/toolbox/path.h b/lib/toolbox/path.h index d46b210f030..e6145b7156a 100644 --- a/lib/toolbox/path.h +++ b/lib/toolbox/path.h @@ -1,6 +1,5 @@ #pragma once - -#include +#include #ifdef __cplusplus extern "C" { @@ -12,7 +11,7 @@ extern "C" { * @param path path string * @param filename output filename string. Must be initialized before. */ -void path_extract_filename_no_ext(const char* path, string_t filename); +void path_extract_filename_no_ext(const char* path, FuriString* filename); /** * @brief Extract filename string from path. @@ -21,7 +20,7 @@ void path_extract_filename_no_ext(const char* path, string_t filename); * @param filename output filename string. Must be initialized before. * @param trim_ext true - get filename without extension */ -void path_extract_filename(string_t path, string_t filename, bool trim_ext); +void path_extract_filename(FuriString* path, FuriString* filename, bool trim_ext); /** * @brief Extract file extension from path. @@ -30,7 +29,7 @@ void path_extract_filename(string_t path, string_t filename, bool trim_ext); * @param ext output extension string * @param ext_len_max maximum extension string length */ -void path_extract_extension(string_t path, char* ext, size_t ext_len_max); +void path_extract_extension(FuriString* path, char* ext, size_t ext_len_max); /** * @brief Extract last path component @@ -38,7 +37,7 @@ void path_extract_extension(string_t path, char* ext, size_t ext_len_max); * @param path path string * @param filename output string. Must be initialized before. */ -void path_extract_basename(const char* path, string_t basename); +void path_extract_basename(const char* path, FuriString* basename); /** * @brief Extract path, except for last component @@ -46,7 +45,7 @@ void path_extract_basename(const char* path, string_t basename); * @param path path string * @param filename output string. Must be initialized before. */ -void path_extract_dirname(const char* path, string_t dirname); +void path_extract_dirname(const char* path, FuriString* dirname); /** * @brief Appends new component to path, adding path delimiter @@ -54,7 +53,7 @@ void path_extract_dirname(const char* path, string_t dirname); * @param path path string * @param suffix path part to apply */ -void path_append(string_t path, const char* suffix); +void path_append(FuriString* path, const char* suffix); /** * @brief Appends new component to path, adding path delimiter @@ -63,7 +62,7 @@ void path_append(string_t path, const char* suffix); * @param suffix second path part * @param out_path output string to combine parts into. Must be initialized */ -void path_concat(const char* path, const char* suffix, string_t out_path); +void path_concat(const char* path, const char* suffix, FuriString* out_path); /** * @brief Check that path contains only ascii characters diff --git a/lib/toolbox/pretty_format.c b/lib/toolbox/pretty_format.c new file mode 100644 index 00000000000..d5ba10381a4 --- /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 00000000000..860528e4cd3 --- /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/profiler.c b/lib/toolbox/profiler.c new file mode 100644 index 00000000000..96f38dce2c5 --- /dev/null +++ b/lib/toolbox/profiler.c @@ -0,0 +1,87 @@ +#include "profiler.h" +#include +#include +#include +#include + +typedef struct { + uint32_t start; + uint32_t length; + uint32_t count; +} ProfilerRecord; + +DICT_DEF2(ProfilerRecordDict, const char*, M_CSTR_OPLIST, ProfilerRecord, M_POD_OPLIST) +#define M_OPL_ProfilerRecord_t() DICT_OPLIST(ProfilerRecord, M_CSTR_OPLIST, M_POD_OPLIST) + +struct Profiler { + ProfilerRecordDict_t records; +}; + +Profiler* profiler_alloc() { + Profiler* profiler = malloc(sizeof(Profiler)); + ProfilerRecordDict_init(profiler->records); + return profiler; +} + +void profiler_free(Profiler* profiler) { + ProfilerRecordDict_clear(profiler->records); + free(profiler); +} + +void profiler_prealloc(Profiler* profiler, const char* key) { + ProfilerRecord record = { + .start = 0, + .length = 0, + .count = 0, + }; + + ProfilerRecordDict_set_at(profiler->records, key, record); +} + +void profiler_start(Profiler* profiler, const char* key) { + ProfilerRecord* record = ProfilerRecordDict_get(profiler->records, key); + if(record == NULL) { + profiler_prealloc(profiler, key); + record = ProfilerRecordDict_get(profiler->records, key); + } + + furi_check(record->start == 0); + record->start = DWT->CYCCNT; +} + +void profiler_stop(Profiler* profiler, const char* key) { + ProfilerRecord* record = ProfilerRecordDict_get(profiler->records, key); + furi_check(record != NULL); + + record->length += DWT->CYCCNT - record->start; + record->start = 0; + record->count++; +} + +void profiler_dump(Profiler* profiler) { + printf("Profiler:\r\n"); + + ProfilerRecordDict_it_t it; + for(ProfilerRecordDict_it(it, profiler->records); !ProfilerRecordDict_end_p(it); + ProfilerRecordDict_next(it)) { + const ProfilerRecordDict_itref_t* itref = ProfilerRecordDict_cref(it); + + uint32_t count = itref->value.count; + + uint32_t clocks = itref->value.length; + double us = (double)clocks / (double)64.0; + double ms = (double)clocks / (double)64000.0; + double s = (double)clocks / (double)64000000.0; + + printf("\t%s[%lu]: %f s, %f ms, %f us, %lu clk\r\n", itref->key, count, s, ms, us, clocks); + + if(count > 1) { + us /= (double)count; + ms /= (double)count; + s /= (double)count; + clocks /= count; + + printf("\t%s[1]: %f s, %f ms, %f us, %lu clk\r\n", itref->key, s, ms, us, clocks); + } + } +} diff --git a/lib/toolbox/profiler.h b/lib/toolbox/profiler.h new file mode 100644 index 00000000000..840146332c5 --- /dev/null +++ b/lib/toolbox/profiler.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Profiler Profiler; + +Profiler* profiler_alloc(); + +void profiler_free(Profiler* profiler); + +void profiler_prealloc(Profiler* profiler, const char* key); + +void profiler_start(Profiler* profiler, const char* key); + +void profiler_stop(Profiler* profiler, const char* key); + +void profiler_dump(Profiler* profiler); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/toolbox/property.c b/lib/toolbox/property.c new file mode 100644 index 00000000000..c258cdd6a0a --- /dev/null +++ b/lib/toolbox/property.c @@ -0,0 +1,33 @@ +#include "property.h" + +#include + +void property_value_out(PropertyValueContext* ctx, const char* fmt, unsigned int nparts, ...) { + furi_assert(ctx); + furi_string_reset(ctx->key); + + va_list args; + va_start(args, nparts); + + for(size_t i = 0; i < nparts; ++i) { + const char* keypart = va_arg(args, const char*); + furi_string_cat(ctx->key, keypart); + if(i < nparts - 1) { + furi_string_push_back(ctx->key, ctx->sep); + } + } + + const char* value_str; + + if(fmt) { + furi_string_vprintf(ctx->value, fmt, args); + value_str = furi_string_get_cstr(ctx->value); + } else { + // C string passthrough (no formatting) + value_str = va_arg(args, const char*); + } + + va_end(args); + + ctx->out(furi_string_get_cstr(ctx->key), value_str, ctx->last, ctx->context); +} diff --git a/lib/toolbox/property.h b/lib/toolbox/property.h new file mode 100644 index 00000000000..524d8ff1605 --- /dev/null +++ b/lib/toolbox/property.h @@ -0,0 +1,39 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** Callback type called every time another key-value pair of device information is ready + * + * @param key[in] device information type identifier + * @param value[in] device information value + * @param last[in] whether the passed key-value pair is the last one + * @param context[in] to pass to callback + */ +typedef void (*PropertyValueCallback)(const char* key, const char* value, bool last, void* context); + +typedef struct { + FuriString* key; /**< key string buffer, must be initialised before use */ + FuriString* value; /**< value string buffer, must be initialised before use */ + PropertyValueCallback out; /**< output callback function */ + char sep; /**< separator character between key parts */ + bool last; /**< flag to indicate last element */ + void* context; /**< user-defined context, passed through to out callback */ +} PropertyValueContext; + +/** Builds key and value strings and outputs them via a callback function + * + * @param ctx[in] local property context + * @param fmt[in] value format, set to NULL to bypass formatting + * @param nparts[in] number of key parts (separated by character) + * @param ...[in] list of key parts followed by value + */ +void property_value_out(PropertyValueContext* ctx, const char* fmt, unsigned int nparts, ...); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/protocols/protocol.h b/lib/toolbox/protocols/protocol.h new file mode 100644 index 00000000000..5a8015b1e1b --- /dev/null +++ b/lib/toolbox/protocols/protocol.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include +#include +#include + +typedef void* (*ProtocolAlloc)(void); +typedef void (*ProtocolFree)(void* protocol); +typedef uint8_t* (*ProtocolGetData)(void* protocol); + +typedef void (*ProtocolDecoderStart)(void* protocol); +typedef bool (*ProtocolDecoderFeed)(void* protocol, bool level, uint32_t duration); + +typedef bool (*ProtocolEncoderStart)(void* protocol); +typedef LevelDuration (*ProtocolEncoderYield)(void* protocol); + +typedef void (*ProtocolRenderData)(void* protocol, FuriString* result); +typedef bool (*ProtocolWriteData)(void* protocol, void* data); + +typedef struct { + ProtocolDecoderStart start; + ProtocolDecoderFeed feed; +} ProtocolDecoder; + +typedef struct { + ProtocolEncoderStart start; + ProtocolEncoderYield yield; +} ProtocolEncoder; + +typedef struct { + const size_t data_size; + const char* name; + const char* manufacturer; + const uint32_t features; + const uint8_t validate_count; + + ProtocolAlloc alloc; + ProtocolFree free; + ProtocolGetData get_data; + ProtocolDecoder decoder; + ProtocolEncoder encoder; + ProtocolRenderData render_data; + ProtocolRenderData render_brief_data; + ProtocolWriteData write_data; +} ProtocolBase; \ No newline at end of file diff --git a/lib/toolbox/protocols/protocol_dict.c b/lib/toolbox/protocols/protocol_dict.c new file mode 100644 index 00000000000..136dc137216 --- /dev/null +++ b/lib/toolbox/protocols/protocol_dict.c @@ -0,0 +1,226 @@ +#include +#include "protocol_dict.h" + +struct ProtocolDict { + const ProtocolBase** base; + size_t count; + void** data; +}; + +ProtocolDict* protocol_dict_alloc(const ProtocolBase** protocols, size_t count) { + ProtocolDict* dict = malloc(sizeof(ProtocolDict)); + dict->base = protocols; + dict->count = count; + dict->data = malloc(sizeof(void*) * dict->count); + + for(size_t i = 0; i < dict->count; i++) { + dict->data[i] = dict->base[i]->alloc(); + } + + return dict; +} + +void protocol_dict_free(ProtocolDict* dict) { + for(size_t i = 0; i < dict->count; i++) { + dict->base[i]->free(dict->data[i]); + } + + free(dict->data); + free(dict); +} + +void protocol_dict_set_data( + ProtocolDict* dict, + size_t protocol_index, + const uint8_t* data, + size_t data_size) { + furi_assert(protocol_index < dict->count); + furi_assert(dict->base[protocol_index]->get_data != NULL); + uint8_t* protocol_data = dict->base[protocol_index]->get_data(dict->data[protocol_index]); + size_t protocol_data_size = dict->base[protocol_index]->data_size; + furi_check(data_size >= protocol_data_size); + memcpy(protocol_data, data, protocol_data_size); +} + +void protocol_dict_get_data( + ProtocolDict* dict, + size_t protocol_index, + uint8_t* data, + size_t data_size) { + furi_assert(protocol_index < dict->count); + furi_assert(dict->base[protocol_index]->get_data != NULL); + uint8_t* protocol_data = dict->base[protocol_index]->get_data(dict->data[protocol_index]); + size_t protocol_data_size = dict->base[protocol_index]->data_size; + furi_check(data_size >= protocol_data_size); + memcpy(data, protocol_data, protocol_data_size); +} + +size_t protocol_dict_get_data_size(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + return dict->base[protocol_index]->data_size; +} + +size_t protocol_dict_get_max_data_size(ProtocolDict* dict) { + size_t max_data_size = 0; + for(size_t i = 0; i < dict->count; i++) { + size_t data_size = dict->base[i]->data_size; + if(data_size > max_data_size) { + max_data_size = data_size; + } + } + + return max_data_size; +} + +const char* protocol_dict_get_name(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + return dict->base[protocol_index]->name; +} + +const char* protocol_dict_get_manufacturer(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + return dict->base[protocol_index]->manufacturer; +} + +void protocol_dict_decoders_start(ProtocolDict* dict) { + for(size_t i = 0; i < dict->count; i++) { + ProtocolDecoderStart fn = dict->base[i]->decoder.start; + + if(fn) { + fn(dict->data[i]); + } + } +} + +uint32_t protocol_dict_get_features(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + return dict->base[protocol_index]->features; +} + +ProtocolId protocol_dict_decoders_feed(ProtocolDict* dict, bool level, uint32_t duration) { + bool done = false; + ProtocolId ready_protocol_id = PROTOCOL_NO; + + for(size_t i = 0; i < dict->count; i++) { + ProtocolDecoderFeed fn = dict->base[i]->decoder.feed; + + if(fn) { + if(fn(dict->data[i], level, duration)) { + if(!done) { + ready_protocol_id = i; + done = true; + } + } + } + } + + return ready_protocol_id; +} + +ProtocolId protocol_dict_decoders_feed_by_feature( + ProtocolDict* dict, + uint32_t feature, + bool level, + uint32_t duration) { + bool done = false; + ProtocolId ready_protocol_id = PROTOCOL_NO; + + for(size_t i = 0; i < dict->count; i++) { + uint32_t features = dict->base[i]->features; + if(features & feature) { + ProtocolDecoderFeed fn = dict->base[i]->decoder.feed; + + if(fn) { + if(fn(dict->data[i], level, duration)) { + if(!done) { + ready_protocol_id = i; + done = true; + } + } + } + } + } + + return ready_protocol_id; +} + +ProtocolId protocol_dict_decoders_feed_by_id( + ProtocolDict* dict, + size_t protocol_index, + bool level, + uint32_t duration) { + furi_assert(protocol_index < dict->count); + + ProtocolId ready_protocol_id = PROTOCOL_NO; + ProtocolDecoderFeed fn = dict->base[protocol_index]->decoder.feed; + + if(fn) { + if(fn(dict->data[protocol_index], level, duration)) { + ready_protocol_id = protocol_index; + } + } + + return ready_protocol_id; +} + +bool protocol_dict_encoder_start(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + ProtocolEncoderStart fn = dict->base[protocol_index]->encoder.start; + + if(fn) { + return fn(dict->data[protocol_index]); + } else { + return false; + } +} + +LevelDuration protocol_dict_encoder_yield(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + ProtocolEncoderYield fn = dict->base[protocol_index]->encoder.yield; + + if(fn) { + return fn(dict->data[protocol_index]); + } else { + return level_duration_reset(); + } +} + +void protocol_dict_render_data(ProtocolDict* dict, FuriString* result, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + ProtocolRenderData fn = dict->base[protocol_index]->render_data; + + if(fn) { + return fn(dict->data[protocol_index], result); + } +} + +void protocol_dict_render_brief_data(ProtocolDict* dict, FuriString* result, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + ProtocolRenderData fn = dict->base[protocol_index]->render_brief_data; + + if(fn) { + return fn(dict->data[protocol_index], result); + } +} + +uint32_t protocol_dict_get_validate_count(ProtocolDict* dict, size_t protocol_index) { + furi_assert(protocol_index < dict->count); + return dict->base[protocol_index]->validate_count; +} + +ProtocolId protocol_dict_get_protocol_by_name(ProtocolDict* dict, const char* name) { + for(size_t i = 0; i < dict->count; i++) { + if(strcmp(name, protocol_dict_get_name(dict, i)) == 0) { + return i; + } + } + return PROTOCOL_NO; +} + +bool protocol_dict_get_write_data(ProtocolDict* dict, size_t protocol_index, void* data) { + furi_assert(protocol_index < dict->count); + ProtocolWriteData fn = dict->base[protocol_index]->write_data; + + furi_assert(fn); + return fn(dict->data[protocol_index], data); +} \ No newline at end of file diff --git a/lib/toolbox/protocols/protocol_dict.h b/lib/toolbox/protocols/protocol_dict.h new file mode 100644 index 00000000000..cd8503952b4 --- /dev/null +++ b/lib/toolbox/protocols/protocol_dict.h @@ -0,0 +1,73 @@ +#pragma once +#include "protocol.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ProtocolDict ProtocolDict; + +typedef int32_t ProtocolId; + +#define PROTOCOL_NO (-1) +#define PROTOCOL_ALL_FEATURES (0xFFFFFFFF) + +ProtocolDict* protocol_dict_alloc(const ProtocolBase** protocols, size_t protocol_count); + +void protocol_dict_free(ProtocolDict* dict); + +void protocol_dict_set_data( + ProtocolDict* dict, + size_t protocol_index, + const uint8_t* data, + size_t data_size); + +void protocol_dict_get_data( + ProtocolDict* dict, + size_t protocol_index, + uint8_t* data, + size_t data_size); + +size_t protocol_dict_get_data_size(ProtocolDict* dict, size_t protocol_index); + +size_t protocol_dict_get_max_data_size(ProtocolDict* dict); + +const char* protocol_dict_get_name(ProtocolDict* dict, size_t protocol_index); + +const char* protocol_dict_get_manufacturer(ProtocolDict* dict, size_t protocol_index); + +void protocol_dict_decoders_start(ProtocolDict* dict); + +uint32_t protocol_dict_get_features(ProtocolDict* dict, size_t protocol_index); + +ProtocolId protocol_dict_decoders_feed(ProtocolDict* dict, bool level, uint32_t duration); + +ProtocolId protocol_dict_decoders_feed_by_feature( + ProtocolDict* dict, + uint32_t feature, + bool level, + uint32_t duration); + +ProtocolId protocol_dict_decoders_feed_by_id( + ProtocolDict* dict, + size_t protocol_index, + bool level, + uint32_t duration); + +bool protocol_dict_encoder_start(ProtocolDict* dict, size_t protocol_index); + +LevelDuration protocol_dict_encoder_yield(ProtocolDict* dict, size_t protocol_index); + +void protocol_dict_render_data(ProtocolDict* dict, FuriString* result, size_t protocol_index); + +void protocol_dict_render_brief_data(ProtocolDict* dict, FuriString* result, size_t protocol_index); + +uint32_t protocol_dict_get_validate_count(ProtocolDict* dict, size_t protocol_index); + +ProtocolId protocol_dict_get_protocol_by_name(ProtocolDict* dict, const char* name); + +bool protocol_dict_get_write_data(ProtocolDict* dict, size_t protocol_index, void* data); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/pulse_joiner.c b/lib/toolbox/pulse_joiner.c new file mode 100644 index 00000000000..b6206486c95 --- /dev/null +++ b/lib/toolbox/pulse_joiner.c @@ -0,0 +1,117 @@ +#include "pulse_joiner.h" +#include + +#define PULSE_MAX_COUNT 6 + +typedef struct { + bool polarity; + uint16_t time; +} Pulse; + +struct PulseJoiner { + size_t pulse_index; + Pulse pulses[PULSE_MAX_COUNT]; +}; + +PulseJoiner* pulse_joiner_alloc() { + PulseJoiner* pulse_joiner = malloc(sizeof(PulseJoiner)); + + pulse_joiner->pulse_index = 0; + for(uint8_t i = 0; i < PULSE_MAX_COUNT; i++) { + pulse_joiner->pulses[i].polarity = false; + pulse_joiner->pulses[i].time = 0; + } + + return pulse_joiner; +} + +void pulse_joiner_free(PulseJoiner* pulse_joiner) { + free(pulse_joiner); +} + +bool pulse_joiner_push_pulse(PulseJoiner* pulse_joiner, bool polarity, size_t period, size_t pulse) { + bool result = false; + furi_check((pulse_joiner->pulse_index + 1) < PULSE_MAX_COUNT); + + if(polarity == false && pulse_joiner->pulse_index == 0) { + // first negative pulse is omitted + + } else { + pulse_joiner->pulses[pulse_joiner->pulse_index].polarity = polarity; + pulse_joiner->pulses[pulse_joiner->pulse_index].time = pulse; + pulse_joiner->pulse_index++; + } + + if(period > pulse) { + pulse_joiner->pulses[pulse_joiner->pulse_index].polarity = !polarity; + pulse_joiner->pulses[pulse_joiner->pulse_index].time = period - pulse; + pulse_joiner->pulse_index++; + } + + if(pulse_joiner->pulse_index >= 4) { + // we know that first pulse is always high + // so we wait 2 edges, hi2low and next low2hi + + uint8_t edges_count = 0; + bool last_polarity = pulse_joiner->pulses[0].polarity; + + for(uint8_t i = 1; i < pulse_joiner->pulse_index; i++) { + if(pulse_joiner->pulses[i].polarity != last_polarity) { + edges_count++; + last_polarity = pulse_joiner->pulses[i].polarity; + } + } + + if(edges_count >= 2) { + result = true; + } + } + + return result; +} + +void pulse_joiner_pop_pulse(PulseJoiner* pulse_joiner, size_t* period, size_t* pulse) { + furi_check(pulse_joiner->pulse_index <= (PULSE_MAX_COUNT + 1)); + + uint16_t tmp_period = 0; + uint16_t tmp_pulse = 0; + uint8_t edges_count = 0; + bool last_polarity = pulse_joiner->pulses[0].polarity; + uint8_t next_fist_pulse = 0; + + for(uint8_t i = 0; i < PULSE_MAX_COUNT; i++) { + // count edges + if(pulse_joiner->pulses[i].polarity != last_polarity) { + edges_count++; + last_polarity = pulse_joiner->pulses[i].polarity; + } + + // wait for 2 edges + if(edges_count == 2) { + next_fist_pulse = i; + break; + } + + // sum pulse time + if(pulse_joiner->pulses[i].polarity) { + tmp_period += pulse_joiner->pulses[i].time; + tmp_pulse += pulse_joiner->pulses[i].time; + } else { + tmp_period += pulse_joiner->pulses[i].time; + } + pulse_joiner->pulse_index--; + } + + *period = tmp_period; + *pulse = tmp_pulse; + + // remove counted periods and shift data + for(uint8_t i = 0; i < PULSE_MAX_COUNT; i++) { + if((next_fist_pulse + i) < PULSE_MAX_COUNT) { + pulse_joiner->pulses[i].polarity = pulse_joiner->pulses[next_fist_pulse + i].polarity; + pulse_joiner->pulses[i].time = pulse_joiner->pulses[next_fist_pulse + i].time; + } else { + break; + } + } +} \ No newline at end of file diff --git a/lib/toolbox/pulse_joiner.h b/lib/toolbox/pulse_joiner.h new file mode 100644 index 00000000000..25f702e7297 --- /dev/null +++ b/lib/toolbox/pulse_joiner.h @@ -0,0 +1,46 @@ +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct PulseJoiner PulseJoiner; + +/** + * @brief Alloc PulseJoiner + * + * @return PulseJoiner* + */ +PulseJoiner* pulse_joiner_alloc(); + +/** + * @brief Free PulseJoiner + * + * @param pulse_joiner + */ +void pulse_joiner_free(PulseJoiner* pulse_joiner); + +/** + * @brief Push timer pulse. First negative pulse is ommited. + * + * @param polarity pulse polarity: true = high2low, false = low2high + * @param period overall period time in timer clicks + * @param pulse pulse time in timer clicks + * + * @return true - next pulse can and must be popped immediatly + */ +bool pulse_joiner_push_pulse(PulseJoiner* pulse_joiner, bool polarity, size_t period, size_t pulse); + +/** + * @brief Get the next timer pulse. Call only if push_pulse returns true. + * + * @param period overall period time in timer clicks + * @param pulse pulse time in timer clicks + */ +void pulse_joiner_pop_pulse(PulseJoiner* pulse_joiner, size_t* period, size_t* pulse); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/one_wire/pulse_protocols/pulse_glue.c b/lib/toolbox/pulse_protocols/pulse_glue.c similarity index 100% rename from lib/one_wire/pulse_protocols/pulse_glue.c rename to lib/toolbox/pulse_protocols/pulse_glue.c diff --git a/lib/one_wire/pulse_protocols/pulse_glue.h b/lib/toolbox/pulse_protocols/pulse_glue.h similarity index 100% rename from lib/one_wire/pulse_protocols/pulse_glue.h rename to lib/toolbox/pulse_protocols/pulse_glue.h diff --git a/lib/toolbox/random_name.c b/lib/toolbox/random_name.c deleted file mode 100644 index 5a53743983f..00000000000 --- a/lib/toolbox/random_name.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "random_name.h" -#include -#include -#include -#include - -void set_random_name(char* name, uint8_t max_name_size) { - static bool rand_generator_inited = false; - - if(!rand_generator_inited) { - srand(DWT->CYCCNT); - rand_generator_inited = true; - } - const char* prefix[] = { - "ancient", "hollow", "strange", "disappeared", "unknown", - "unthinkable", "unnamable", "nameless", "my", "concealed", - "forgotten", "hidden", "mysterious", "obscure", "random", - "remote", "uncharted", "undefined", "untravelled", "untold", - }; - - const char* suffix[] = { - "door", - "entrance", - "doorway", - "entry", - "portal", - "entree", - "opening", - "crack", - "access", - "corridor", - "passage", - "port", - }; - uint8_t prefix_i = rand() % COUNT_OF(prefix); - uint8_t suffix_i = rand() % COUNT_OF(suffix); - - snprintf(name, max_name_size, "%s_%s", prefix[prefix_i], suffix[suffix_i]); - // Set first symbol to upper case - name[0] = name[0] - 0x20; -} diff --git a/lib/toolbox/random_name.h b/lib/toolbox/random_name.h deleted file mode 100644 index 358ea685d8c..00000000000 --- a/lib/toolbox/random_name.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Generates random name - * @param name buffer to write random name - * @param max_name_size length of given buffer - */ -void set_random_name(char* name, uint8_t max_name_size); - -#ifdef __cplusplus -} -#endif diff --git a/lib/toolbox/saved_struct.c b/lib/toolbox/saved_struct.c index 65b761f80ce..2f1c09c8eb5 100644 --- a/lib/toolbox/saved_struct.c +++ b/lib/toolbox/saved_struct.c @@ -46,7 +46,7 @@ bool saved_struct_save(const char* path, void* data, size_t size, uint8_t magic, header.flags = 0; header.timestamp = 0; - uint16_t bytes_count = storage_file_write(file, &header, sizeof(header)); + size_t bytes_count = storage_file_write(file, &header, sizeof(header)); bytes_count += storage_file_write(file, data, size); if(bytes_count != (size + sizeof(header))) { @@ -79,7 +79,7 @@ bool saved_struct_load(const char* path, void* data, size_t size, uint8_t magic, } if(result) { - uint16_t bytes_count = storage_file_read(file, &header, sizeof(SavedStructHeader)); + size_t bytes_count = storage_file_read(file, &header, sizeof(SavedStructHeader)); bytes_count += storage_file_read(file, data_read, size); if(bytes_count != (sizeof(SavedStructHeader) + size)) { @@ -125,3 +125,54 @@ bool saved_struct_load(const char* path, void* data, size_t size, uint8_t magic, return result; } + +bool saved_struct_get_payload_size( + const char* path, + uint8_t magic, + uint8_t version, + size_t* payload_size) { + furi_assert(path); + furi_assert(payload_size); + + SavedStructHeader header; + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + bool result = false; + do { + if(!storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) { + FURI_LOG_E( + TAG, "Failed to read \"%s\". Error: %s", path, storage_file_get_error_desc(file)); + break; + } + + size_t bytes_count = storage_file_read(file, &header, sizeof(SavedStructHeader)); + if(bytes_count != sizeof(SavedStructHeader)) { + FURI_LOG_E(TAG, "Failed to read header"); + break; + } + + if((header.magic != magic) || (header.version != version)) { + FURI_LOG_E( + TAG, + "Magic(%d != %d) or Version(%d != %d) mismatch of file \"%s\"", + header.magic, + magic, + header.version, + version, + path); + break; + } + + uint64_t file_size = storage_file_size(file); + *payload_size = file_size - sizeof(SavedStructHeader); + + result = true; + } while(false); + + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + + return result; +} diff --git a/lib/toolbox/saved_struct.h b/lib/toolbox/saved_struct.h index aae41bd61c6..9ce836564be 100644 --- a/lib/toolbox/saved_struct.h +++ b/lib/toolbox/saved_struct.h @@ -4,6 +4,20 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + bool saved_struct_load(const char* path, void* data, size_t size, uint8_t magic, uint8_t version); bool saved_struct_save(const char* path, void* data, size_t size, uint8_t magic, uint8_t version); + +bool saved_struct_get_payload_size( + const char* path, + uint8_t magic, + uint8_t version, + size_t* payload_size); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/sha256.c b/lib/toolbox/sha256.c deleted file mode 100644 index ece77955d9e..00000000000 --- a/lib/toolbox/sha256.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * sha256.c -- Compute SHA-256 hash - * - * Just for little endian architecture. - * - * Code taken from: - * http://gladman.plushost.co.uk/oldsite/cryptography_technology/sha/index.php - * - * File names are sha2.c, sha2.h, brg_types.h, brg_endian.h - * in the archive sha2-07-01-07.zip. - * - * Code is modified in the style of PolarSSL API. - * - * See original copyright notice below. - */ -/* - --------------------------------------------------------------------------- - Copyright (c) 2002, Dr Brian Gladman, Worcester, UK. All rights reserved. - - LICENSE TERMS - - The free distribution and use of this software in both source and binary - form is allowed (with or without changes) provided that: - - 1. distributions of this source code include the above copyright - notice, this list of conditions and the following disclaimer; - - 2. distributions in binary form include the above copyright - notice, this list of conditions and the following disclaimer - in the documentation and/or other associated materials; - - 3. the copyright holder's name is not used to endorse products - built using this software without specific written permission. - - ALTERNATIVELY, provided that this notice is retained in full, this product - may be distributed under the terms of the GNU General Public License (GPL), - in which case the provisions of the GPL apply INSTEAD OF those given above. - - DISCLAIMER - - This software is provided 'as is' with no explicit or implied warranties - in respect of its properties, including, but not limited to, correctness - and/or fitness for purpose. - --------------------------------------------------------------------------- - Issue Date: 01/08/2005 -*/ - -#include -#include -#include -#include "sha256.h" - -#define SHA256_MASK (SHA256_BLOCK_SIZE - 1) - -static void memcpy_output_bswap32(unsigned char* dst, const uint32_t* p) { - int i; - uint32_t q = 0; - - for(i = 0; i < 32; i++) { - if((i & 3) == 0) q = __builtin_bswap32(p[i >> 2]); /* bswap32 is GCC extention */ - dst[i] = q >> ((i & 3) * 8); - } -} - -#define rotr32(x, n) (((x) >> n) | ((x) << (32 - n))) - -#define ch(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) -#define maj(x, y, z) (((x) & (y)) | ((z) & ((x) ^ (y)))) - -/* round transforms for SHA256 compression functions */ -#define vf(n, i) v[(n - i) & 7] - -#define hf(i) (p[i & 15] += g_1(p[(i + 14) & 15]) + p[(i + 9) & 15] + g_0(p[(i + 1) & 15])) - -#define v_cycle0(i) \ - p[i] = __builtin_bswap32(p[i]); \ - vf(7, i) += p[i] + k_0[i] + s_1(vf(4, i)) + ch(vf(4, i), vf(5, i), vf(6, i)); \ - vf(3, i) += vf(7, i); \ - vf(7, i) += s_0(vf(0, i)) + maj(vf(0, i), vf(1, i), vf(2, i)) - -#define v_cycle(i, j) \ - vf(7, i) += hf(i) + k_0[i + j] + s_1(vf(4, i)) + ch(vf(4, i), vf(5, i), vf(6, i)); \ - vf(3, i) += vf(7, i); \ - vf(7, i) += s_0(vf(0, i)) + maj(vf(0, i), vf(1, i), vf(2, i)) - -#define s_0(x) (rotr32((x), 2) ^ rotr32((x), 13) ^ rotr32((x), 22)) -#define s_1(x) (rotr32((x), 6) ^ rotr32((x), 11) ^ rotr32((x), 25)) -#define g_0(x) (rotr32((x), 7) ^ rotr32((x), 18) ^ ((x) >> 3)) -#define g_1(x) (rotr32((x), 17) ^ rotr32((x), 19) ^ ((x) >> 10)) -#define k_0 k256 - -static const uint32_t k256[64] = { - 0X428A2F98, 0X71374491, 0XB5C0FBCF, 0XE9B5DBA5, 0X3956C25B, 0X59F111F1, 0X923F82A4, 0XAB1C5ED5, - 0XD807AA98, 0X12835B01, 0X243185BE, 0X550C7DC3, 0X72BE5D74, 0X80DEB1FE, 0X9BDC06A7, 0XC19BF174, - 0XE49B69C1, 0XEFBE4786, 0X0FC19DC6, 0X240CA1CC, 0X2DE92C6F, 0X4A7484AA, 0X5CB0A9DC, 0X76F988DA, - 0X983E5152, 0XA831C66D, 0XB00327C8, 0XBF597FC7, 0XC6E00BF3, 0XD5A79147, 0X06CA6351, 0X14292967, - 0X27B70A85, 0X2E1B2138, 0X4D2C6DFC, 0X53380D13, 0X650A7354, 0X766A0ABB, 0X81C2C92E, 0X92722C85, - 0XA2BFE8A1, 0XA81A664B, 0XC24B8B70, 0XC76C51A3, 0XD192E819, 0XD6990624, 0XF40E3585, 0X106AA070, - 0X19A4C116, 0X1E376C08, 0X2748774C, 0X34B0BCB5, 0X391C0CB3, 0X4ED8AA4A, 0X5B9CCA4F, 0X682E6FF3, - 0X748F82EE, 0X78A5636F, 0X84C87814, 0X8CC70208, 0X90BEFFFA, 0XA4506CEB, 0XBEF9A3F7, 0XC67178F2, -}; - -void sha256_process(sha256_context* ctx) { - uint32_t i; - uint32_t* p = ctx->wbuf; - uint32_t v[8]; - - memcpy(v, ctx->state, 8 * sizeof(uint32_t)); - - v_cycle0(0); - v_cycle0(1); - v_cycle0(2); - v_cycle0(3); - v_cycle0(4); - v_cycle0(5); - v_cycle0(6); - v_cycle0(7); - v_cycle0(8); - v_cycle0(9); - v_cycle0(10); - v_cycle0(11); - v_cycle0(12); - v_cycle0(13); - v_cycle0(14); - v_cycle0(15); - - for(i = 16; i < 64; i += 16) { - v_cycle(0, i); - v_cycle(1, i); - v_cycle(2, i); - v_cycle(3, i); - v_cycle(4, i); - v_cycle(5, i); - v_cycle(6, i); - v_cycle(7, i); - v_cycle(8, i); - v_cycle(9, i); - v_cycle(10, i); - v_cycle(11, i); - v_cycle(12, i); - v_cycle(13, i); - v_cycle(14, i); - v_cycle(15, i); - } - - ctx->state[0] += v[0]; - ctx->state[1] += v[1]; - ctx->state[2] += v[2]; - ctx->state[3] += v[3]; - ctx->state[4] += v[4]; - ctx->state[5] += v[5]; - ctx->state[6] += v[6]; - ctx->state[7] += v[7]; -} - -void sha256_update(sha256_context* ctx, const unsigned char* input, unsigned int ilen) { - uint32_t left = (ctx->total[0] & SHA256_MASK); - uint32_t fill = SHA256_BLOCK_SIZE - left; - - ctx->total[0] += ilen; - if(ctx->total[0] < ilen) ctx->total[1]++; - - while(ilen >= fill) { - memcpy(((unsigned char*)ctx->wbuf) + left, input, fill); - sha256_process(ctx); - input += fill; - ilen -= fill; - left = 0; - fill = SHA256_BLOCK_SIZE; - } - - memcpy(((unsigned char*)ctx->wbuf) + left, input, ilen); -} - -void sha256_finish(sha256_context* ctx, unsigned char output[32]) { - uint32_t last = (ctx->total[0] & SHA256_MASK); - - ctx->wbuf[last >> 2] = __builtin_bswap32(ctx->wbuf[last >> 2]); - ctx->wbuf[last >> 2] &= 0xffffff80 << (8 * (~last & 3)); - ctx->wbuf[last >> 2] |= 0x00000080 << (8 * (~last & 3)); - ctx->wbuf[last >> 2] = __builtin_bswap32(ctx->wbuf[last >> 2]); - - if(last > SHA256_BLOCK_SIZE - 9) { - if(last < 60) ctx->wbuf[15] = 0; - sha256_process(ctx); - last = 0; - } else - last = (last >> 2) + 1; - - while(last < 14) ctx->wbuf[last++] = 0; - - ctx->wbuf[14] = __builtin_bswap32((ctx->total[0] >> 29) | (ctx->total[1] << 3)); - ctx->wbuf[15] = __builtin_bswap32(ctx->total[0] << 3); - sha256_process(ctx); - - memcpy_output_bswap32(output, ctx->state); - memset(ctx, 0, sizeof(sha256_context)); -} - -static const uint32_t initial_state[8] = { - 0x6a09e667, - 0xbb67ae85, - 0x3c6ef372, - 0xa54ff53a, - 0x510e527f, - 0x9b05688c, - 0x1f83d9ab, - 0x5be0cd19}; - -void sha256_start(sha256_context* ctx) { - ctx->total[0] = ctx->total[1] = 0; - memcpy(ctx->state, initial_state, 8 * sizeof(uint32_t)); -} - -void sha256(const unsigned char* input, unsigned int ilen, unsigned char output[32]) { - sha256_context ctx; - - sha256_start(&ctx); - sha256_update(&ctx, input, ilen); - sha256_finish(&ctx, output); -} diff --git a/lib/toolbox/sha256.h b/lib/toolbox/sha256.h deleted file mode 100644 index 85b9709dbbd..00000000000 --- a/lib/toolbox/sha256.h +++ /dev/null @@ -1,14 +0,0 @@ -#define SHA256_DIGEST_SIZE 32 -#define SHA256_BLOCK_SIZE 64 - -typedef struct { - uint32_t total[2]; - uint32_t state[8]; - uint32_t wbuf[16]; -} sha256_context; - -void sha256(const unsigned char* input, unsigned int ilen, unsigned char output[32]); -void sha256_start(sha256_context* ctx); -void sha256_finish(sha256_context* ctx, unsigned char output[32]); -void sha256_update(sha256_context* ctx, const unsigned char* input, unsigned int ilen); -void sha256_process(sha256_context* ctx); diff --git a/lib/toolbox/simple_array.c b/lib/toolbox/simple_array.c new file mode 100644 index 00000000000..7aed8e34be3 --- /dev/null +++ b/lib/toolbox/simple_array.c @@ -0,0 +1,127 @@ +#include "simple_array.h" + +#include + +struct SimpleArray { + const SimpleArrayConfig* config; + SimpleArrayElement* data; + uint32_t count; +}; + +SimpleArray* simple_array_alloc(const SimpleArrayConfig* config) { + SimpleArray* instance = malloc(sizeof(SimpleArray)); + instance->config = config; + return instance; +} + +void simple_array_free(SimpleArray* instance) { + furi_assert(instance); + + simple_array_reset(instance); + free(instance); +} + +void simple_array_init(SimpleArray* instance, uint32_t count) { + furi_assert(instance); + furi_assert(count > 0); + + simple_array_reset(instance); + + instance->data = malloc(count * instance->config->type_size); + instance->count = count; + + SimpleArrayInit init = instance->config->init; + if(init) { + for(uint32_t i = 0; i < instance->count; ++i) { + init(simple_array_get(instance, i)); + } + } +} + +void simple_array_reset(SimpleArray* instance) { + furi_assert(instance); + + if(instance->data) { + SimpleArrayReset reset = instance->config->reset; + + if(reset) { + for(uint32_t i = 0; i < instance->count; ++i) { + reset(simple_array_get(instance, i)); + } + } + + free(instance->data); + + instance->count = 0; + instance->data = NULL; + } +} + +void simple_array_copy(SimpleArray* instance, const SimpleArray* other) { + furi_assert(instance); + furi_assert(other); + furi_assert(instance->config == other->config); + + simple_array_reset(instance); + + if(other->count == 0) { + return; + } + + simple_array_init(instance, other->count); + + SimpleArrayCopy copy = instance->config->copy; + if(copy) { + for(uint32_t i = 0; i < other->count; ++i) { + copy(simple_array_get(instance, i), simple_array_cget(other, i)); + } + } else { + memcpy(instance->data, other->data, other->count * instance->config->type_size); + } +} + +bool simple_array_is_equal(const SimpleArray* instance, const SimpleArray* other) { + furi_assert(instance); + furi_assert(other); + + // Equal if the same object + if(instance == other) return true; + + return (instance->config == other->config) && (instance->count == other->count) && + ((instance->data == other->data) || (instance->data == NULL) || (other->data == NULL) || + (memcmp(instance->data, other->data, other->count) == 0)); +} + +uint32_t simple_array_get_count(const SimpleArray* instance) { + furi_assert(instance); + return instance->count; +} + +SimpleArrayElement* simple_array_get(SimpleArray* instance, uint32_t index) { + furi_assert(instance); + furi_assert(index < instance->count); + + return instance->data + index * instance->config->type_size; +} + +const SimpleArrayElement* simple_array_cget(const SimpleArray* instance, uint32_t index) { + return simple_array_get((SimpleArrayElement*)instance, index); +} + +SimpleArrayData* simple_array_get_data(SimpleArray* instance) { + furi_assert(instance); + furi_assert(instance->data); + + return instance->data; +} + +const SimpleArrayData* simple_array_cget_data(const SimpleArray* instance) { + return simple_array_get_data((SimpleArray*)instance); +} + +const SimpleArrayConfig simple_array_config_uint8_t = { + .init = NULL, + .copy = NULL, + .reset = NULL, + .type_size = sizeof(uint8_t), +}; diff --git a/lib/toolbox/simple_array.h b/lib/toolbox/simple_array.h new file mode 100644 index 00000000000..a608db08d87 --- /dev/null +++ b/lib/toolbox/simple_array.h @@ -0,0 +1,148 @@ +/** + * @file simple_array.h + * + * @brief This file provides a simple (non-type safe) array for elements with + * (optional) in-place construction support. + * + * No append, remove or take operations are supported. + * Instead, the user must call simple_array_init() in order to reserve the memory for n elements. + * In case if init() is specified in the configuration, then the elements are constructed in-place. + * + * To clear all elements from the array, call simple_array_reset(), which will also call reset() + * for each element in case if it was specified in the configuration. This is useful if a custom + * destructor is required for a particular type. Calling simple_array_free() will also result in a + * simple_array_reset() call automatically. + * + */ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SimpleArray SimpleArray; + +typedef void SimpleArrayData; +typedef void SimpleArrayElement; + +typedef void (*SimpleArrayInit)(SimpleArrayElement* elem); +typedef void (*SimpleArrayReset)(SimpleArrayElement* elem); +typedef void (*SimpleArrayCopy)(SimpleArrayElement* elem, const SimpleArrayElement* other); + +/** Simple Array configuration structure. Defined per type. */ +typedef struct { + SimpleArrayInit init; /**< Initialisation (in-place constructor) method. */ + SimpleArrayReset reset; /**< Reset (custom destructor) method. */ + SimpleArrayCopy copy; /**< Copy (custom copy-constructor) method. */ + const size_t type_size; /** Type size, in bytes. */ +} SimpleArrayConfig; + +/** + * Allocate a SimpleArray instance with the given configuration. + * + * @param [in] config Pointer to the type-specific configuration + * @return Pointer to the allocated SimpleArray instance + */ +SimpleArray* simple_array_alloc(const SimpleArrayConfig* config); + +/** + * Free a SimpleArray instance and release its contents. + * + * @param [in] instance Pointer to the SimpleArray instance to be freed + */ +void simple_array_free(SimpleArray* instance); + +/** + * Initialise a SimpleArray instance by allocating additional space to contain + * the requested number of elements. + * If init() is specified in the config, then it is called for each element, + * otherwise the data is filled with zeroes. + * + * @param [in] instance Pointer to the SimpleArray instance to be init'd + * @param [in] count Number of elements to be allocated and init'd + */ +void simple_array_init(SimpleArray* instance, uint32_t count); + +/** + * Reset a SimpleArray instance and delete all of its elements. + * If reset() is specified in the config, then it is called for each element, + * otherwise the data is simply free()'d. + * + * @param [in] instance Pointer to the SimpleArray instance to be reset + */ +void simple_array_reset(SimpleArray* instance); + +/** + * Copy (duplicate) another SimpleArray instance to this one. + * If copy() is specified in the config, then it is called for each element, + * otherwise the data is simply memcpy()'d. + * + * @param [in] instance Pointer to the SimpleArray instance to copy to + * @param [in] other Pointer to the SimpleArray instance to copy from + */ +void simple_array_copy(SimpleArray* instance, const SimpleArray* other); + +/** + * Check if another SimpleArray instance is equal (the same object or holds the + * same data) to this one. + * + * @param [in] instance Pointer to the SimpleArray instance to be compared + * @param [in] other Pointer to the SimpleArray instance to be compared + * @return True if instances are considered equal, false otherwise + */ +bool simple_array_is_equal(const SimpleArray* instance, const SimpleArray* other); + +/** + * Get the count of elements currently contained in a SimpleArray instance. + * + * @param [in] instance Pointer to the SimpleArray instance to query the count from + * @return Count of elements contained in the instance + */ +uint32_t simple_array_get_count(const SimpleArray* instance); + +/** + * Get a pointer to an element contained in a SimpleArray instance. + * + * @param [in] instance Pointer to the SimpleArray instance to get an element from + * @param [in] index Index of the element in question. MUST be less than total element count + * @return Pointer to the element specified by index + */ +SimpleArrayElement* simple_array_get(SimpleArray* instance, uint32_t index); + +/** + * Get a const pointer to an element contained in a SimpleArray instance. + * + * @param [in] instance Pointer to the SimpleArray instance to get an element from + * @param [in] index Index of the element in question. MUST be less than total element count + * @return Const pointer to the element specified by index + */ +const SimpleArrayElement* simple_array_cget(const SimpleArray* instance, uint32_t index); + +/** + * Get a pointer to the internal data of a SimpleArray instance. + * + * @param [in] instance Pointer to the SimpleArray instance to get the data of + * @return Pointer to the instance's internal data + */ +SimpleArrayData* simple_array_get_data(SimpleArray* instance); + +/** + * Get a constant pointer to the internal data of a SimpleArray instance. + * + * @param [in] instance Pointer to the SimpleArray instance to get the data of + * @return Constant pointer to the instance's internal data + */ +const SimpleArrayData* simple_array_cget_data(const SimpleArray* instance); + +// Standard preset configurations + +// Preset configuration for a byte(uint8_t) array. +extern const SimpleArrayConfig simple_array_config_uint8_t; + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/stream/buffered_file_stream.c b/lib/toolbox/stream/buffered_file_stream.c index 3b20a391c60..3b485e80df9 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/stream/file_stream.c b/lib/toolbox/stream/file_stream.c index f7363c6be67..2b5348b3e85 100644 --- a/lib/toolbox/stream/file_stream.c +++ b/lib/toolbox/stream/file_stream.c @@ -134,31 +134,11 @@ static size_t file_stream_size(FileStream* stream) { } static size_t file_stream_write(FileStream* stream, const uint8_t* data, size_t size) { - // TODO cache - size_t need_to_write = size; - while(need_to_write > 0) { - uint16_t was_written = - storage_file_write(stream->file, data + (size - need_to_write), need_to_write); - need_to_write -= was_written; - - if(was_written == 0) break; - } - - return size - need_to_write; + return storage_file_write(stream->file, data, size); } static size_t file_stream_read(FileStream* stream, uint8_t* data, size_t size) { - // TODO cache - size_t need_to_read = size; - while(need_to_read > 0) { - uint16_t was_read = - storage_file_read(stream->file, data + (size - need_to_read), need_to_read); - need_to_read -= was_read; - - if(was_read == 0) break; - } - - return size - need_to_read; + return storage_file_read(stream->file, data, size); } static bool file_stream_delete_and_insert( @@ -172,18 +152,21 @@ static bool file_stream_delete_and_insert( // open scratchpad Stream* scratch_stream = file_stream_alloc(_stream->storage); - // TODO: we need something like "storage_open_tmpfile and storage_close_tmpfile" - string_t scratch_name; - string_t tmp_name; - string_init(tmp_name); + // TODO FL-3546: we need something like "storage_open_tmpfile and storage_close_tmpfile" + FuriString* scratch_name; + FuriString* tmp_name; + tmp_name = furi_string_alloc(); storage_get_next_filename( _stream->storage, STORAGE_ANY_PATH_PREFIX, ".scratch", ".pad", tmp_name, 255); - string_init_printf(scratch_name, ANY_PATH("%s.pad"), string_get_cstr(tmp_name)); - string_clear(tmp_name); + scratch_name = furi_string_alloc_printf(ANY_PATH("%s.pad"), furi_string_get_cstr(tmp_name)); + furi_string_free(tmp_name); do { if(!file_stream_open( - scratch_stream, string_get_cstr(scratch_name), FSAM_READ_WRITE, FSOM_CREATE_NEW)) + scratch_stream, + furi_string_get_cstr(scratch_name), + FSAM_READ_WRITE, + FSOM_CREATE_NEW)) break; size_t current_position = stream_tell(stream); @@ -225,8 +208,8 @@ static bool file_stream_delete_and_insert( } while(false); stream_free(scratch_stream); - storage_common_remove(_stream->storage, string_get_cstr(scratch_name)); - string_clear(scratch_name); + storage_common_remove(_stream->storage, furi_string_get_cstr(scratch_name)); + furi_string_free(scratch_name); return result; } diff --git a/lib/toolbox/stream/stream.c b/lib/toolbox/stream/stream.c index 088996f0247..407da0f2c19 100644 --- a/lib/toolbox/stream/stream.c +++ b/lib/toolbox/stream/stream.c @@ -4,6 +4,8 @@ #include #include +#define STREAM_BUFFER_SIZE (32U) + void stream_free(Stream* stream) { furi_assert(stream); stream->vtable->free(stream); @@ -24,6 +26,82 @@ bool stream_seek(Stream* stream, int32_t offset, StreamOffset offset_type) { return stream->vtable->seek(stream, offset, offset_type); } +static bool stream_seek_to_char_forward(Stream* stream, char c) { + // Search is starting from seconds character + if(!stream_seek(stream, 1, StreamOffsetFromCurrent)) { + return false; + } + + // Search character in a stream + bool result = false; + while(!result) { + uint8_t buffer[STREAM_BUFFER_SIZE] = {0}; + size_t ret = stream_read(stream, buffer, STREAM_BUFFER_SIZE); + for(size_t i = 0; i < ret; i++) { + if(buffer[i] == c) { + stream_seek(stream, (int32_t)i - ret, StreamOffsetFromCurrent); + result = true; + break; + } + } + if(ret != STREAM_BUFFER_SIZE) break; + } + return result; +} + +static bool stream_seek_to_char_backward(Stream* stream, char c) { + size_t anchor = stream_tell(stream); + + // Special case, no previous characters + if(anchor == 0) { + return false; + } + + bool result = false; + while(!result) { + // Seek back + uint8_t buffer[STREAM_BUFFER_SIZE] = {0}; + size_t to_read = STREAM_BUFFER_SIZE; + if(to_read > anchor) { + to_read = anchor; + } + + anchor -= to_read; + furi_check(stream_seek(stream, anchor, StreamOffsetFromStart)); + + size_t ret = stream_read(stream, buffer, to_read); + for(size_t i = 0; i < ret; i++) { + size_t cursor = ret - i - 1; + if(buffer[cursor] == c) { + result = true; + furi_check(stream_seek(stream, anchor + cursor, StreamOffsetFromStart)); + break; + } else { + } + } + if(ret != STREAM_BUFFER_SIZE) break; + } + return result; +} + +bool stream_seek_to_char(Stream* stream, char c, StreamDirection direction) { + const size_t old_position = stream_tell(stream); + + bool result = false; + if(direction == StreamDirectionForward) { + result = stream_seek_to_char_forward(stream, c); + } else if(direction == StreamDirectionBackward) { + result = stream_seek_to_char_backward(stream, c); + } + + // Rollback + if(!result) { + stream_seek(stream, old_position, StreamOffsetFromStart); + } + + return result; +} + size_t stream_tell(Stream* stream) { furi_assert(stream); return stream->vtable->tell(stream); @@ -67,13 +145,12 @@ static bool stream_write_struct(Stream* stream, const void* context) { return (stream_write(stream, write_data->data, write_data->size) == write_data->size); } -bool stream_read_line(Stream* stream, string_t str_result) { - string_reset(str_result); - const uint8_t buffer_size = 32; - uint8_t buffer[buffer_size]; +bool stream_read_line(Stream* stream, FuriString* str_result) { + furi_string_reset(str_result); + uint8_t buffer[STREAM_BUFFER_SIZE]; do { - uint16_t bytes_were_read = stream_read(stream, buffer, buffer_size); + uint16_t bytes_were_read = stream_read(stream, buffer, STREAM_BUFFER_SIZE); if(bytes_were_read == 0) break; bool result = false; @@ -84,13 +161,13 @@ bool stream_read_line(Stream* stream, string_t str_result) { error = true; break; } - string_push_back(str_result, buffer[i]); + furi_string_push_back(str_result, buffer[i]); result = true; break; } else if(buffer[i] == '\r') { // Ignore } else { - string_push_back(str_result, buffer[i]); + furi_string_push_back(str_result, buffer[i]); } } @@ -99,7 +176,7 @@ bool stream_read_line(Stream* stream, string_t str_result) { } } while(true); - return string_size(str_result) != 0; + return furi_string_size(str_result) != 0; } bool stream_rewind(Stream* stream) { @@ -112,9 +189,10 @@ size_t stream_write_char(Stream* stream, char c) { return stream_write(stream, (const uint8_t*)&c, 1); } -size_t stream_write_string(Stream* stream, string_t string) { +size_t stream_write_string(Stream* stream, FuriString* string) { furi_assert(stream); - return stream_write(stream, (const uint8_t*)string_get_cstr(string), string_size(string)); + return stream_write( + stream, (const uint8_t*)furi_string_get_cstr(string), furi_string_size(string)); } size_t stream_write_cstring(Stream* stream, const char* string) { @@ -134,10 +212,10 @@ size_t stream_write_format(Stream* stream, const char* format, ...) { size_t stream_write_vaformat(Stream* stream, const char* format, va_list args) { furi_assert(stream); - string_t data; - string_init_vprintf(data, format, args); + FuriString* data; + data = furi_string_alloc_vprintf(format, args); size_t size = stream_write_string(stream, data); - string_clear(data); + furi_string_free(data); return size; } @@ -153,7 +231,7 @@ bool stream_insert_char(Stream* stream, char c) { return stream_delete_and_insert_char(stream, 0, c); } -bool stream_insert_string(Stream* stream, string_t string) { +bool stream_insert_string(Stream* stream, FuriString* string) { furi_assert(stream); return stream_delete_and_insert_string(stream, 0, string); } @@ -184,10 +262,10 @@ bool stream_delete_and_insert_char(Stream* stream, size_t delete_size, char c) { return stream_delete_and_insert(stream, delete_size, stream_write_struct, &write_data); } -bool stream_delete_and_insert_string(Stream* stream, size_t delete_size, string_t string) { +bool stream_delete_and_insert_string(Stream* stream, size_t delete_size, FuriString* string) { furi_assert(stream); StreamWriteData write_data = { - .data = (uint8_t*)string_get_cstr(string), .size = string_size(string)}; + .data = (uint8_t*)furi_string_get_cstr(string), .size = furi_string_size(string)}; return stream_delete_and_insert(stream, delete_size, stream_write_struct, &write_data); } @@ -213,12 +291,12 @@ bool stream_delete_and_insert_vaformat( const char* format, va_list args) { furi_assert(stream); - string_t data; - string_init_vprintf(data, format, args); + FuriString* data; + data = furi_string_alloc_vprintf(format, args); StreamWriteData write_data = { - .data = (uint8_t*)string_get_cstr(data), .size = string_size(data)}; + .data = (uint8_t*)furi_string_get_cstr(data), .size = furi_string_size(data)}; bool result = stream_delete_and_insert(stream, delete_size, stream_write_struct, &write_data); - string_clear(data); + furi_string_free(data); return result; } @@ -314,8 +392,8 @@ void stream_dump_data(Stream* stream) { size_t size = stream_size(stream); size_t tell = stream_tell(stream); printf("stream %p\r\n", stream); - printf("size = %u\r\n", size); - printf("tell = %u\r\n", tell); + printf("size = %zu\r\n", size); + printf("tell = %zu\r\n", tell); printf("DATA START\r\n"); uint8_t* data = malloc(STREAM_CACHE_SIZE); stream_rewind(stream); diff --git a/lib/toolbox/stream/stream.h b/lib/toolbox/stream/stream.h index c10e9f5e5ff..84b4f0eb2f6 100644 --- a/lib/toolbox/stream/stream.h +++ b/lib/toolbox/stream/stream.h @@ -2,7 +2,6 @@ #include #include #include -#include #include #ifdef __cplusplus @@ -17,6 +16,11 @@ typedef enum { StreamOffsetFromEnd, } StreamOffset; +typedef enum { + StreamDirectionForward, + StreamDirectionBackward, +} StreamDirection; + typedef bool (*StreamWriteCB)(Stream* stream, const void* context); /** @@ -32,15 +36,15 @@ void stream_free(Stream* stream); void stream_clean(Stream* stream); /** - * Indicates that the rw pointer is at the end of the stream + * Indicates that the RW pointer is at the end of the stream * @param stream Stream instance - * @return true if rw pointer is at the end of the stream - * @return false if rw pointer is not at the end of the stream + * @return true if RW pointer is at the end of the stream + * @return false if RW pointer is not at the end of the stream */ bool stream_eof(Stream* stream); /** - * Moves the rw pointer. + * Moves the RW pointer. * @param stream Stream instance * @param offset how much to move the pointer * @param offset_type starting from what @@ -49,10 +53,20 @@ bool stream_eof(Stream* stream); */ bool stream_seek(Stream* stream, int32_t offset, StreamOffset offset_type); +/** Seek to next occurrence of the character + * + * @param stream Pointer to the stream instance + * @param[in] c The Character + * @param[in] direction The Direction + * + * @return true on success + */ +bool stream_seek_to_char(Stream* stream, char c, StreamDirection direction); + /** - * Gets the value of the rw pointer + * Gets the value of the RW pointer * @param stream Stream instance - * @return size_t value of the rw pointer + * @return size_t value of the RW pointer */ size_t stream_tell(Stream* stream); @@ -102,13 +116,13 @@ bool stream_delete_and_insert( * Read line from a stream (supports LF and CRLF line endings) * @param stream * @param str_result - * @return true if line lenght is not zero + * @return true if line length is not zero * @return false otherwise */ -bool stream_read_line(Stream* stream, string_t str_result); +bool stream_read_line(Stream* stream, FuriString* str_result); /** - * Moves the rw pointer to the start + * Moves the RW pointer to the start * @param stream Stream instance */ bool stream_rewind(Stream* stream); @@ -127,7 +141,7 @@ size_t stream_write_char(Stream* stream, char c); * @param string string value * @return size_t how many bytes was written */ -size_t stream_write_string(Stream* stream, string_t string); +size_t stream_write_string(Stream* stream, FuriString* string); /** * Write const char* to the stream @@ -144,7 +158,8 @@ size_t stream_write_cstring(Stream* stream, const char* string); * @param ... * @return size_t how many bytes was written */ -size_t stream_write_format(Stream* stream, const char* format, ...); +size_t stream_write_format(Stream* stream, const char* format, ...) + _ATTRIBUTE((__format__(__printf__, 2, 3))); /** * Write formatted string to the stream, va_list version @@ -157,7 +172,7 @@ size_t stream_write_vaformat(Stream* stream, const char* format, va_list args); /** * Insert N chars to the stream, starting at the current pointer. - * Data will be inserted, not overwritteт, so the stream will be increased in size. + * Data will be inserted, not overwritten, so the stream will be increased in size. * @param stream Stream instance * @param data data to be inserted * @param size size of data to be inserted @@ -182,7 +197,7 @@ bool stream_insert_char(Stream* stream, char c); * @return true if the operation was successful * @return false on error */ -bool stream_insert_string(Stream* stream, string_t string); +bool stream_insert_string(Stream* stream, FuriString* string); /** * Insert const char* to the stream @@ -201,7 +216,8 @@ bool stream_insert_cstring(Stream* stream, const char* string); * @return true if the operation was successful * @return false on error */ -bool stream_insert_format(Stream* stream, const char* format, ...); +bool stream_insert_format(Stream* stream, const char* format, ...) + _ATTRIBUTE((__format__(__printf__, 2, 3))); /** * Insert formatted string to the stream, va_list version @@ -231,7 +247,7 @@ bool stream_delete_and_insert_char(Stream* stream, size_t delete_size, char c); * @return true if the operation was successful * @return false on error */ -bool stream_delete_and_insert_string(Stream* stream, size_t delete_size, string_t string); +bool stream_delete_and_insert_string(Stream* stream, size_t delete_size, FuriString* string); /** * Delete N chars from the stream and insert const char* to the stream @@ -252,7 +268,8 @@ bool stream_delete_and_insert_cstring(Stream* stream, size_t delete_size, const * @return true if the operation was successful * @return false on error */ -bool stream_delete_and_insert_format(Stream* stream, size_t delete_size, const char* format, ...); +bool stream_delete_and_insert_format(Stream* stream, size_t delete_size, const char* format, ...) + _ATTRIBUTE((__format__(__printf__, 3, 4))); /** * Delete N chars from the stream and insert formatted string to the stream, va_list version @@ -271,7 +288,7 @@ bool stream_delete_and_insert_vaformat( /** * Remove N chars from the stream, starting at the current pointer. - * The size may be larger than stream size, the stream will be cleared from current rw pointer to the end. + * The size may be larger than stream size, the stream will be cleared from current RW pointer to the end. * @param stream Stream instance * @param size how many chars need to be deleted * @return true if the operation was successful @@ -280,7 +297,7 @@ bool stream_delete_and_insert_vaformat( bool stream_delete(Stream* stream, size_t size); /** - * Copy data from one stream to another. Data will be copied from current rw pointer and to current rw pointer. + * Copy data from one stream to another. Data will be copied from current RW pointer and to current RW pointer. * @param stream_from * @param stream_to * @param size @@ -326,7 +343,7 @@ size_t stream_load_from_file(Stream* stream, Storage* storage, const char* path) size_t stream_save_to_file(Stream* stream, Storage* storage, const char* path, FS_OpenMode mode); /** - * Dump stream inner data (size, RW positiot, content) + * Dump stream inner data (size, RW position, content) * @param stream Stream instance */ void stream_dump_data(Stream* stream); diff --git a/lib/toolbox/stream/string_stream.c b/lib/toolbox/stream/string_stream.c index f3e1364d145..f8a360c03ea 100644 --- a/lib/toolbox/stream/string_stream.c +++ b/lib/toolbox/stream/string_stream.c @@ -5,7 +5,7 @@ typedef struct { Stream stream_base; - string_t string; + FuriString* string; size_t index; } StringStream; @@ -39,14 +39,14 @@ const StreamVTable string_stream_vtable = { Stream* string_stream_alloc() { StringStream* stream = malloc(sizeof(StringStream)); - string_init(stream->string); + stream->string = furi_string_alloc(); stream->index = 0; stream->stream_base.vtable = &string_stream_vtable; return (Stream*)stream; } static void string_stream_free(StringStream* stream) { - string_clear(stream->string); + furi_string_free(stream->string); free(stream); } @@ -56,7 +56,7 @@ static bool string_stream_eof(StringStream* stream) { static void string_stream_clean(StringStream* stream) { stream->index = 0; - string_reset(stream->string); + furi_string_reset(stream->string); } static bool string_stream_seek(StringStream* stream, int32_t offset, StreamOffset offset_type) { @@ -79,8 +79,8 @@ static bool string_stream_seek(StringStream* stream, int32_t offset, StreamOffse } break; case StreamOffsetFromEnd: - if(((int32_t)string_size(stream->string) + offset) >= 0) { - stream->index = string_size(stream->string) + offset; + if(((int32_t)furi_string_size(stream->string) + offset) >= 0) { + stream->index = furi_string_size(stream->string) + offset; } else { result = false; stream->index = 0; @@ -88,7 +88,7 @@ static bool string_stream_seek(StringStream* stream, int32_t offset, StreamOffse break; } - int32_t diff = (stream->index - string_size(stream->string)); + int32_t diff = (stream->index - furi_string_size(stream->string)); if(diff > 0) { stream->index -= diff; result = false; @@ -102,11 +102,11 @@ static size_t string_stream_tell(StringStream* stream) { } static size_t string_stream_size(StringStream* stream) { - return string_size(stream->string); + return furi_string_size(stream->string); } static size_t string_stream_write(StringStream* stream, const char* data, size_t size) { - // TODO: can be optimized for edge cases + // TODO FL-3544: can be optimized for edge cases size_t i; for(i = 0; i < size; i++) { string_stream_write_char(stream, data[i]); @@ -117,7 +117,7 @@ static size_t string_stream_write(StringStream* stream, const char* data, size_t static size_t string_stream_read(StringStream* stream, char* data, size_t size) { size_t write_index = 0; - const char* cstr = string_get_cstr(stream->string); + const char* cstr = furi_string_get_cstr(stream->string); if(!string_stream_eof(stream)) { while(true) { @@ -145,17 +145,17 @@ static bool string_stream_delete_and_insert( remain_size = MIN(delete_size, remain_size); if(remain_size != 0) { - string_replace_at(stream->string, stream->index, remain_size, ""); + furi_string_replace_at(stream->string, stream->index, remain_size, ""); } } if(write_callback) { - string_t right; - string_init_set(right, &string_get_cstr(stream->string)[stream->index]); - string_left(stream->string, string_stream_tell(stream)); + FuriString* right; + right = furi_string_alloc_set(&furi_string_get_cstr(stream->string)[stream->index]); + furi_string_left(stream->string, string_stream_tell(stream)); result &= write_callback((Stream*)stream, ctx); - string_cat(stream->string, right); - string_clear(right); + furi_string_cat(stream->string, right); + furi_string_free(right); } return result; @@ -169,9 +169,9 @@ static bool string_stream_delete_and_insert( */ static size_t string_stream_write_char(StringStream* stream, char c) { if(string_stream_eof(stream)) { - string_push_back(stream->string, c); + furi_string_push_back(stream->string, c); } else { - string_set_char(stream->string, stream->index, c); + furi_string_set_char(stream->string, stream->index, c); } stream->index++; diff --git a/lib/toolbox/stream/string_stream.h b/lib/toolbox/stream/string_stream.h index 6cccfa6ccf8..f882a246b3e 100644 --- a/lib/toolbox/stream/string_stream.h +++ b/lib/toolbox/stream/string_stream.h @@ -1,6 +1,5 @@ #pragma once #include -#include #include "stream.h" #ifdef __cplusplus diff --git a/lib/toolbox/tar/tar_archive.c b/lib/toolbox/tar/tar_archive.c index 5ac89a0fd3c..fcfc22a7035 100644 --- a/lib/toolbox/tar/tar_archive.c +++ b/lib/toolbox/tar/tar_archive.c @@ -106,6 +106,7 @@ void tar_archive_set_file_callback(TarArchive* archive, tar_unpack_file_cb callb static int tar_archive_entry_counter(mtar_t* tar, const mtar_header_t* header, void* param) { UNUSED(tar); UNUSED(header); + furi_assert(param); int32_t* counter = param; (*counter)++; return 0; @@ -168,7 +169,44 @@ typedef struct { Storage_name_converter converter; } TarArchiveDirectoryOpParams; +static bool archive_extract_current_file(TarArchive* archive, const char* dst_path) { + mtar_t* tar = &archive->tar; + File* out_file = storage_file_alloc(archive->storage); + uint8_t* readbuf = malloc(FILE_BLOCK_SIZE); + + bool success = true; + uint8_t n_tries = FILE_OPEN_NTRIES; + do { + while(n_tries-- > 0) { + if(storage_file_open(out_file, dst_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + break; + } + FURI_LOG_W(TAG, "Failed to open '%s', reties: %d", dst_path, n_tries); + storage_file_close(out_file); + furi_delay_ms(FILE_OPEN_RETRY_DELAY); + } + + if(!storage_file_is_open(out_file)) { + success = false; + break; + } + + while(!mtar_eof_data(tar)) { + int32_t readcnt = mtar_read_data(tar, readbuf, FILE_BLOCK_SIZE); + if(!readcnt || !storage_file_write(out_file, readbuf, readcnt)) { + success = false; + break; + } + } + } while(false); + storage_file_free(out_file); + free(readbuf); + + return success; +} + static int archive_extract_foreach_cb(mtar_t* tar, const mtar_header_t* header, void* param) { + UNUSED(tar); TarArchiveDirectoryOpParams* op_params = param; TarArchive* archive = op_params->archive; @@ -183,14 +221,14 @@ static int archive_extract_foreach_cb(mtar_t* tar, const mtar_header_t* header, return 0; } - string_t full_extracted_fname; + FuriString* full_extracted_fname; if(header->type == MTAR_TDIR) { - string_init(full_extracted_fname); + full_extracted_fname = furi_string_alloc(); path_concat(op_params->work_dir, header->name, full_extracted_fname); bool create_res = - storage_simply_mkdir(archive->storage, string_get_cstr(full_extracted_fname)); - string_clear(full_extracted_fname); + storage_simply_mkdir(archive->storage, furi_string_get_cstr(full_extracted_fname)); + furi_string_free(full_extracted_fname); return create_res ? 0 : -1; } @@ -199,58 +237,22 @@ static int archive_extract_foreach_cb(mtar_t* tar, const mtar_header_t* header, return 0; } - string_init(full_extracted_fname); + FURI_LOG_D(TAG, "Extracting %u bytes to '%s'", header->size, header->name); - string_t converted_fname; - string_init_set(converted_fname, header->name); + FuriString* converted_fname = furi_string_alloc_set(header->name); if(op_params->converter) { op_params->converter(converted_fname); } - path_concat(op_params->work_dir, string_get_cstr(converted_fname), full_extracted_fname); - string_clear(converted_fname); - FURI_LOG_I(TAG, "Extracting %d bytes to '%s'", header->size, header->name); - File* out_file = storage_file_alloc(archive->storage); - uint8_t* readbuf = malloc(FILE_BLOCK_SIZE); + full_extracted_fname = furi_string_alloc(); + path_concat(op_params->work_dir, furi_string_get_cstr(converted_fname), full_extracted_fname); - bool failed = false; - uint8_t n_tries = FILE_OPEN_NTRIES; - do { - while(n_tries-- > 0) { - if(storage_file_open( - out_file, - string_get_cstr(full_extracted_fname), - FSAM_WRITE, - FSOM_CREATE_ALWAYS)) { - break; - } - FURI_LOG_W( - TAG, - "Failed to open '%s', reties: %d", - string_get_cstr(full_extracted_fname), - n_tries); - storage_file_close(out_file); - furi_delay_ms(FILE_OPEN_RETRY_DELAY); - } - - if(!storage_file_is_open(out_file)) { - failed = true; - break; - } - - while(!mtar_eof_data(tar)) { - int32_t readcnt = mtar_read_data(tar, readbuf, FILE_BLOCK_SIZE); - if(!readcnt || !storage_file_write(out_file, readbuf, readcnt)) { - failed = true; - break; - } - } - } while(false); + bool success = + archive_extract_current_file(archive, furi_string_get_cstr(full_extracted_fname)); - storage_file_free(out_file); - free(readbuf); - string_clear(full_extracted_fname); - return failed ? -1 : 0; + furi_string_free(converted_fname); + furi_string_free(full_extracted_fname); + return success ? 0 : -1; } bool tar_archive_unpack_to( @@ -294,6 +296,7 @@ bool tar_archive_add_file( break; } + success = true; // if file is empty, that's not an error uint16_t bytes_read = 0; while((bytes_read = storage_file_read(src_file, file_buffer, FILE_BLOCK_SIZE))) { success = tar_archive_file_add_data_block(archive, file_buffer, bytes_read); @@ -331,32 +334,32 @@ bool tar_archive_add_dir(TarArchive* archive, const char* fs_full_path, const ch break; } - string_t element_name, element_fs_abs_path; - string_init(element_name); - string_init(element_fs_abs_path); + FuriString* element_name = furi_string_alloc(); + FuriString* element_fs_abs_path = furi_string_alloc(); path_concat(fs_full_path, name, element_fs_abs_path); if(strlen(path_prefix)) { path_concat(path_prefix, name, element_name); } else { - string_init_set(element_name, name); + furi_string_set(element_name, name); } - if(file_info.flags & FSF_DIRECTORY) { - success = tar_archive_dir_add_element(archive, string_get_cstr(element_name)) && - tar_archive_add_dir( - archive, - string_get_cstr(element_fs_abs_path), - string_get_cstr(element_name)); + if(file_info_is_dir(&file_info)) { + success = + tar_archive_dir_add_element(archive, furi_string_get_cstr(element_name)) && + tar_archive_add_dir( + archive, + furi_string_get_cstr(element_fs_abs_path), + furi_string_get_cstr(element_name)); } else { success = tar_archive_add_file( archive, - string_get_cstr(element_fs_abs_path), - string_get_cstr(element_name), + furi_string_get_cstr(element_fs_abs_path), + furi_string_get_cstr(element_name), file_info.size); } - string_clear(element_name); - string_clear(element_fs_abs_path); + furi_string_free(element_name); + furi_string_free(element_fs_abs_path); if(!success) { break; @@ -368,3 +371,16 @@ bool tar_archive_add_dir(TarArchive* archive, const char* fs_full_path, const ch storage_file_free(directory); return success; } + +bool tar_archive_unpack_file( + TarArchive* archive, + const char* archive_fname, + const char* destination) { + furi_assert(archive); + furi_assert(archive_fname); + furi_assert(destination); + if(mtar_find(&archive->tar, archive_fname) != MTAR_ESUCCESS) { + return false; + } + return archive_extract_current_file(archive, destination); +} diff --git a/lib/toolbox/tar/tar_archive.h b/lib/toolbox/tar/tar_archive.h index 88cb3dd4d96..ba2f7749f09 100644 --- a/lib/toolbox/tar/tar_archive.h +++ b/lib/toolbox/tar/tar_archive.h @@ -2,7 +2,6 @@ #include #include -#include #include #ifdef __cplusplus @@ -41,6 +40,11 @@ bool tar_archive_add_dir(TarArchive* archive, const char* fs_full_path, const ch int32_t tar_archive_get_entries_count(TarArchive* archive); +bool tar_archive_unpack_file( + TarArchive* archive, + const char* archive_fname, + const char* destination); + /* Optional per-entry callback on unpacking - return false to skip entry */ typedef bool (*tar_unpack_file_cb)(const char* name, bool is_directory, void* context); diff --git a/lib/toolbox/value_index.c b/lib/toolbox/value_index.c index e0745e43410..5ec0fb96287 100644 --- a/lib/toolbox/value_index.c +++ b/lib/toolbox/value_index.c @@ -1,5 +1,18 @@ #include "value_index.h" +uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t values_count) { + int64_t last_value = INT64_MIN; + uint8_t index = 0; + for(uint8_t i = 0; i < values_count; i++) { + if((value >= last_value) && (value <= values[i])) { + index = i; + break; + } + last_value = values[i]; + } + return index; +} + uint8_t value_index_uint32(const uint32_t value, const uint32_t values[], uint8_t values_count) { int64_t last_value = INT64_MIN; uint8_t index = 0; diff --git a/lib/toolbox/value_index.h b/lib/toolbox/value_index.h index 9459292a75b..5aa768e3d1d 100644 --- a/lib/toolbox/value_index.h +++ b/lib/toolbox/value_index.h @@ -7,6 +7,19 @@ extern "C" { #endif +/** Get the index of a int32_t array element which is closest to the given value. + * + * Returned index corresponds to the first element found. + * If no suitable elements were found, the function returns 0. + * + * @param value value to be searched. + * @param values pointer to the array to perform the search in. + * @param values_count array size. + * + * @return value's index. + */ +uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t values_count); + /** Get the index of a uint32_t array element which is closest to the given value. * * Returned index corresponds to the first element found. diff --git a/lib/toolbox/varint.c b/lib/toolbox/varint.c new file mode 100644 index 00000000000..79777c4ba58 --- /dev/null +++ b/lib/toolbox/varint.c @@ -0,0 +1,76 @@ +#include "varint.h" + +size_t varint_uint32_pack(uint32_t value, uint8_t* output) { + uint8_t* start = output; + while(value >= 0x80) { + *output++ = (value | 0x80); + value >>= 7; + } + *output++ = value; + return output - start; +} + +size_t varint_uint32_unpack(uint32_t* value, const uint8_t* input, size_t input_size) { + size_t i; + uint32_t parsed = 0; + + for(i = 0; i < input_size; i++) { + parsed |= (input[i] & 0x7FUL) << (7 * i); + + if(!(input[i] & 0x80)) { + break; + } + } + + *value = parsed; + + return i + 1; +} + +size_t varint_uint32_length(uint32_t value) { + size_t size = 0; + while(value >= 0x80) { + value >>= 7; + size++; + } + size++; + + return size; +} + +size_t varint_int32_pack(int32_t value, uint8_t* output) { + uint32_t v; + + if(value >= 0) { + v = value * 2; + } else { + v = (value * -2) - 1; + } + + return varint_uint32_pack(v, output); +} + +size_t varint_int32_unpack(int32_t* value, const uint8_t* input, size_t input_size) { + uint32_t v; + size_t size = varint_uint32_unpack(&v, input, input_size); + + if(v & 1) { + *value = (int32_t)(v + 1) / (-2); + } else { + *value = v / 2; + } + + return size; +} + +size_t varint_int32_length(int32_t value) { + uint32_t v; + + if(value >= 0) { + v = value * 2; + } else { + v = (value * -2) - 1; + } + + return varint_uint32_length(v); +} diff --git a/lib/toolbox/varint.h b/lib/toolbox/varint.h new file mode 100644 index 00000000000..bf4681d4dcb --- /dev/null +++ b/lib/toolbox/varint.h @@ -0,0 +1,35 @@ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Pack uint32 to varint + * @param value value from UINT32_MIN to UINT32_MAX + * @param output output array, need to be at least 5 bytes long + * @return size_t + */ +size_t varint_uint32_pack(uint32_t value, uint8_t* output); + +size_t varint_uint32_unpack(uint32_t* value, const uint8_t* input, size_t input_size); + +size_t varint_uint32_length(uint32_t value); + +/** + * Pack int32 to varint + * @param value value from (INT32_MIN / 2 + 1) to INT32_MAX + * @param output output array, need to be at least 5 bytes long + * @return size_t + */ +size_t varint_int32_pack(int32_t value, uint8_t* output); + +size_t varint_int32_unpack(int32_t* value, const uint8_t* input, size_t input_size); + +size_t varint_int32_length(int32_t value); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/toolbox/version.c b/lib/toolbox/version.c index c6c10b4107e..876695f0785 100644 --- a/lib/toolbox/version.c +++ b/lib/toolbox/version.c @@ -1,23 +1,37 @@ #include "version.h" - +#include /* This header is autogenerated by build system */ #include "version.inc.h" +#define VERSION_MAGIC (0xBE40u) +#define VERSION_MAJOR (0x1u) +#define VERSION_MINOR (0x1u) + struct Version { + // Header + const uint16_t magic; + const uint8_t major; + const uint8_t minor; + // Payload const char* git_hash; const char* git_branch; - const char* git_branch_num; const char* build_date; const char* version; + // Payload bits and pieces const uint8_t target; const bool build_is_dirty; + // v 1.1 + const char* firmware_origin; + const char* git_origin; }; /* version of current running firmware (bootloader/flipper) */ static const Version version = { + .magic = VERSION_MAGIC, + .major = VERSION_MAJOR, + .minor = VERSION_MINOR, .git_hash = GIT_COMMIT, .git_branch = GIT_BRANCH, - .git_branch_num = GIT_BRANCH_NUM, .build_date = BUILD_DATE, .version = VERSION #ifdef FURI_RAM_EXEC @@ -26,6 +40,8 @@ static const Version version = { , .target = TARGET, .build_is_dirty = BUILD_DIRTY, + .firmware_origin = FIRMWARE_ORIGIN, + .git_origin = GIT_ORIGIN, }; const Version* version_get(void) { @@ -41,7 +57,8 @@ const char* version_get_gitbranch(const Version* v) { } const char* version_get_gitbranchnum(const Version* v) { - return v ? v->git_branch_num : version.git_branch_num; + UNUSED(v); + return "0"; } const char* version_get_builddate(const Version* v) { @@ -59,3 +76,11 @@ uint8_t version_get_target(const Version* v) { bool version_get_dirty_flag(const Version* v) { return v ? v->build_is_dirty : version.build_is_dirty; } + +const char* version_get_firmware_origin(const Version* v) { + return v ? v->firmware_origin : version.firmware_origin; +} + +const char* version_get_git_origin(const Version* v) { + return v ? v->git_origin : version.git_origin; +} diff --git a/lib/toolbox/version.h b/lib/toolbox/version.h index 652ff3feace..0c04e5c7596 100644 --- a/lib/toolbox/version.h +++ b/lib/toolbox/version.h @@ -82,6 +82,17 @@ uint8_t version_get_target(const Version* v); */ bool version_get_dirty_flag(const Version* v); +/** + * Get firmware origin. "Official" for mainline firmware, fork name for forks. + * Set by FIRMWARE_ORIGIN fbt argument. +*/ +const char* version_get_firmware_origin(const Version* v); + +/** + * Get git repo origin +*/ +const char* version_get_git_origin(const Version* v); + #ifdef __cplusplus } #endif diff --git a/lib/u8g2/SConscript b/lib/u8g2/SConscript new file mode 100644 index 00000000000..dacdfbacb4b --- /dev/null +++ b/lib/u8g2/SConscript @@ -0,0 +1,20 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/u8g2", + ], + LINT_SOURCES=[ + Dir("."), + ], +) + + +libenv = env.Clone(FW_LIB_NAME="u8g2") +libenv.ApplyLibFlags() + +sources = libenv.GlobRecursive("*.c") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/u8g2/u8g2_glue.c b/lib/u8g2/u8g2_glue.c index 77f37f14ccd..230bd2a1bcf 100644 --- a/lib/u8g2/u8g2_glue.c +++ b/lib/u8g2/u8g2_glue.c @@ -2,6 +2,9 @@ #include +#define CONTRAST_ERC 32 +#define CONTRAST_MGG 28 + uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) { UNUSED(u8x8); UNUSED(arg_ptr); @@ -207,6 +210,19 @@ void u8x8_d_st756x_init(u8x8_t* u8x8, uint8_t contrast, uint8_t regulation_ratio u8x8_cad_EndTransfer(u8x8); } +void u8x8_d_st756x_set_contrast(u8x8_t* u8x8, int8_t contrast_offset) { + uint8_t contrast = (furi_hal_version_get_hw_display() == FuriHalVersionDisplayMgg) ? + CONTRAST_MGG : + CONTRAST_ERC; + contrast += contrast_offset; + contrast = contrast & 0b00111111; + + u8x8_cad_StartTransfer(u8x8); + u8x8_cad_SendCmd(u8x8, ST756X_CMD_SET_EV); + u8x8_cad_SendArg(u8x8, contrast); + u8x8_cad_EndTransfer(u8x8); +} + uint8_t u8x8_d_st756x_flipper(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) { /* call common procedure first and handle messages there */ if(u8x8_d_st756x_common(u8x8, msg, arg_int, arg_ptr) == 0) { @@ -225,7 +241,7 @@ uint8_t u8x8_d_st756x_flipper(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* * RR = 10 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.88 is 6 (0b110) * Bias = 1/9 (false) */ - u8x8_d_st756x_init(u8x8, 32, 0b110, false); + u8x8_d_st756x_init(u8x8, CONTRAST_MGG, 0b110, false); } else { /* ERC v1(ST7565) and v2(ST7567) * EV = 33 @@ -233,7 +249,7 @@ uint8_t u8x8_d_st756x_flipper(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* * RR = 9.3 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.47 is 5.5 (0b101) * Bias = 1/9 (false) */ - u8x8_d_st756x_init(u8x8, 33, 0b101, false); + u8x8_d_st756x_init(u8x8, CONTRAST_ERC, 0b101, false); } break; case U8X8_MSG_DISPLAY_SET_FLIP_MODE: diff --git a/lib/u8g2/u8g2_glue.h b/lib/u8g2/u8g2_glue.h index 91ba2980a5c..af236279ece 100644 --- a/lib/u8g2/u8g2_glue.h +++ b/lib/u8g2/u8g2_glue.h @@ -14,3 +14,5 @@ void u8g2_Setup_st756x_flipper( u8x8_msg_cb gpio_and_delay_cb); void u8x8_d_st756x_init(u8x8_t* u8x8, uint8_t contrast, uint8_t regulation_ratio, bool bias); + +void u8x8_d_st756x_set_contrast(u8x8_t* u8x8, int8_t contrast_offset); diff --git a/lib/update_util/SConscript b/lib/update_util/SConscript new file mode 100644 index 00000000000..c818973d978 --- /dev/null +++ b/lib/update_util/SConscript @@ -0,0 +1,16 @@ +Import("env") + +env.Append( + LINT_SOURCES=[ + Dir("."), + ], +) + +libenv = env.Clone(FW_LIB_NAME="update_util") +libenv.ApplyLibFlags() + +sources = libenv.GlobRecursive("*.c") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/update_util/dfu_file.c b/lib/update_util/dfu_file.c index d6f31b60203..85b661e8e00 100644 --- a/lib/update_util/dfu_file.c +++ b/lib/update_util/dfu_file.c @@ -22,7 +22,7 @@ uint8_t dfu_file_validate_headers(File* dfuf, const DfuValidationParams* referen DfuPrefix dfu_prefix = {0}; DfuSuffix dfu_suffix = {0}; - uint16_t bytes_read = 0; + size_t bytes_read = 0; if(!storage_file_is_open(dfuf) || !storage_file_seek(dfuf, 0, true)) { return 0; @@ -35,7 +35,7 @@ uint8_t dfu_file_validate_headers(File* dfuf, const DfuValidationParams* referen return 0; } - if(memcmp(dfu_prefix.szSignature, DFU_SIGNATURE, sizeof(dfu_prefix.szSignature))) { + if(memcmp(dfu_prefix.szSignature, DFU_SIGNATURE, sizeof(dfu_prefix.szSignature)) != 0) { return 0; } @@ -55,7 +55,7 @@ uint8_t dfu_file_validate_headers(File* dfuf, const DfuValidationParams* referen if((dfu_suffix.bLength != sizeof(DfuSuffix)) || (dfu_suffix.bcdDFU != DFU_SUFFIX_VERSION)) { return 0; } - /* TODO: check DfuSignature?.. */ + /* TODO FL-3561: check DfuSignature?.. */ if((dfu_suffix.idVendor != reference_params->vendor) || (dfu_suffix.idProduct != reference_params->product) || @@ -90,7 +90,7 @@ static DfuUpdateBlockResult dfu_file_perform_task_for_update_pages( } uint8_t* fw_block = malloc(FLASH_PAGE_SIZE); - uint16_t bytes_read = 0; + size_t bytes_read = 0; uint32_t element_offs = 0; while(element_offs < header->dwElementSize) { @@ -125,7 +125,7 @@ static DfuUpdateBlockResult dfu_file_perform_task_for_update_pages( bool dfu_file_process_targets(const DfuUpdateTask* task, File* dfuf, const uint8_t n_targets) { TargetPrefix target_prefix = {0}; ImageElementHeader image_element = {0}; - uint16_t bytes_read = 0; + size_t bytes_read = 0; if(!storage_file_seek(dfuf, sizeof(DfuPrefix), true)) { return UpdateBlockResult_Failed; @@ -137,7 +137,7 @@ bool dfu_file_process_targets(const DfuUpdateTask* task, File* dfuf, const uint8 return UpdateBlockResult_Failed; } - /* TODO: look into TargetPrefix and validate/filter?.. */ + /* TODO FL-3562: look into TargetPrefix and validate/filter?.. */ for(uint32_t i_element = 0; i_element < target_prefix.dwNbElements; ++i_element) { bytes_read = storage_file_read(dfuf, &image_element, sizeof(ImageElementHeader)); if(bytes_read != sizeof(ImageElementHeader)) { diff --git a/lib/update_util/lfs_backup.c b/lib/update_util/lfs_backup.c index 724da3657f7..7786524ef64 100644 --- a/lib/update_util/lfs_backup.c +++ b/lib/update_util/lfs_backup.c @@ -6,13 +6,13 @@ #include #include #include -#include +#include #include #define LFS_BACKUP_DEFAULT_LOCATION EXT_PATH(LFS_BACKUP_DEFAULT_FILENAME) -static void backup_name_converter(string_t filename) { - if(string_empty_p(filename) || (string_get_char(filename, 0) == '.')) { +static void backup_name_converter(FuriString* filename) { + if(furi_string_empty(filename) || (furi_string_get_char(filename, 0) == '.')) { return; } @@ -27,8 +27,8 @@ static void backup_name_converter(string_t filename) { }; for(size_t i = 0; i < COUNT_OF(names); i++) { - if(string_equal_str_p(filename, &names[i][1])) { - string_set_str(filename, names[i]); + if(furi_string_equal(filename, &names[i][1])) { + furi_string_set(filename, names[i]); return; } } @@ -42,8 +42,7 @@ bool lfs_backup_create(Storage* storage, const char* destination) { bool lfs_backup_exists(Storage* storage, const char* source) { const char* final_source = source && strlen(source) ? source : LFS_BACKUP_DEFAULT_LOCATION; - FileInfo fi; - return storage_common_stat(storage, final_source, &fi) == FSE_OK; + return storage_common_stat(storage, final_source, NULL) == FSE_OK; } bool lfs_backup_unpack(Storage* storage, const char* source) { diff --git a/lib/update_util/resources/manifest.c b/lib/update_util/resources/manifest.c new file mode 100644 index 00000000000..5a818a0a4a4 --- /dev/null +++ b/lib/update_util/resources/manifest.c @@ -0,0 +1,163 @@ +#include "manifest.h" + +#include +#include + +struct ResourceManifestReader { + Storage* storage; + Stream* stream; + FuriString* linebuf; + ResourceManifestEntry entry; +}; + +ResourceManifestReader* resource_manifest_reader_alloc(Storage* storage) { + ResourceManifestReader* resource_manifest = + (ResourceManifestReader*)malloc(sizeof(ResourceManifestReader)); + resource_manifest->storage = storage; + resource_manifest->stream = buffered_file_stream_alloc(resource_manifest->storage); + memset(&resource_manifest->entry, 0, sizeof(ResourceManifestEntry)); + resource_manifest->entry.name = furi_string_alloc(); + resource_manifest->linebuf = furi_string_alloc(); + return resource_manifest; +} + +void resource_manifest_reader_free(ResourceManifestReader* resource_manifest) { + furi_assert(resource_manifest); + + furi_string_free(resource_manifest->linebuf); + furi_string_free(resource_manifest->entry.name); + buffered_file_stream_close(resource_manifest->stream); + stream_free(resource_manifest->stream); + free(resource_manifest); +} + +bool resource_manifest_reader_open(ResourceManifestReader* resource_manifest, const char* filename) { + furi_assert(resource_manifest); + + return buffered_file_stream_open( + resource_manifest->stream, filename, FSAM_READ, FSOM_OPEN_EXISTING); +} + +/* Read entries in format of + * F::: + * D: + */ +ResourceManifestEntry* resource_manifest_reader_next(ResourceManifestReader* resource_manifest) { + furi_assert(resource_manifest); + + furi_string_reset(resource_manifest->entry.name); + resource_manifest->entry.type = ResourceManifestEntryTypeUnknown; + resource_manifest->entry.size = 0; + memset(resource_manifest->entry.hash, 0, sizeof(resource_manifest->entry.hash)); + + do { + if(!stream_read_line(resource_manifest->stream, resource_manifest->linebuf)) { + return NULL; + } + + /* Trim end of line */ + furi_string_trim(resource_manifest->linebuf); + + char type_code = furi_string_get_char(resource_manifest->linebuf, 0); + switch(type_code) { + case 'V': + resource_manifest->entry.type = ResourceManifestEntryTypeVersion; + break; + case 'T': + resource_manifest->entry.type = ResourceManifestEntryTypeTimestamp; + break; + case 'F': + resource_manifest->entry.type = ResourceManifestEntryTypeFile; + break; + case 'D': + resource_manifest->entry.type = ResourceManifestEntryTypeDirectory; + break; + default: /* Skip other entries - version, timestamp, etc */ + continue; + }; + + if(resource_manifest->entry.type == ResourceManifestEntryTypeFile) { + /* Parse file entry + F::: */ + + /* Remove entry type code */ + furi_string_right(resource_manifest->linebuf, 2); + + if(furi_string_search_char(resource_manifest->linebuf, ':') != + sizeof(resource_manifest->entry.hash) * 2) { + /* Invalid hash */ + continue; + } + + /* Read hash */ + hex_chars_to_uint8( + furi_string_get_cstr(resource_manifest->linebuf), resource_manifest->entry.hash); + + /* Remove hash */ + furi_string_right( + resource_manifest->linebuf, sizeof(resource_manifest->entry.hash) * 2 + 1); + + resource_manifest->entry.size = atoi(furi_string_get_cstr(resource_manifest->linebuf)); + + /* Remove size */ + size_t offs = furi_string_search_char(resource_manifest->linebuf, ':'); + furi_string_right(resource_manifest->linebuf, offs + 1); + + furi_string_set(resource_manifest->entry.name, resource_manifest->linebuf); + } else { //-V547 + /* Everything else is plain key value. Parse version, timestamp or directory entry + : */ + + /* Remove entry type code */ + furi_string_right(resource_manifest->linebuf, 2); + + furi_string_set(resource_manifest->entry.name, resource_manifest->linebuf); + } + + return &resource_manifest->entry; + } while(true); + + return NULL; +} + +ResourceManifestEntry* + resource_manifest_reader_previous(ResourceManifestReader* resource_manifest) { + furi_assert(resource_manifest); + + // Snapshot position for rollback + const size_t previous_position = stream_tell(resource_manifest->stream); + + // We need to jump 2 lines back + size_t jumps = 2; + // Special case: end of the file. + const bool was_eof = stream_eof(resource_manifest->stream); + if(was_eof) { + jumps = 1; + } + while(jumps) { + if(!stream_seek_to_char(resource_manifest->stream, '\n', StreamDirectionBackward)) { + break; + } + if(stream_tell(resource_manifest->stream) < (previous_position - 1)) { + jumps--; + } + } + + // Special case: first line. Force seek to zero + if(jumps == 1) { + jumps = 0; + stream_seek(resource_manifest->stream, 0, StreamOffsetFromStart); + } + + if(jumps == 0) { + ResourceManifestEntry* entry = resource_manifest_reader_next(resource_manifest); + // Special case: was end of the file, prevent loop + if(was_eof) { + stream_seek(resource_manifest->stream, -1, StreamOffsetFromCurrent); + } + return entry; + } else { + stream_seek(resource_manifest->stream, previous_position, StreamOffsetFromStart); + return NULL; + } +} diff --git a/lib/update_util/resources/manifest.h b/lib/update_util/resources/manifest.h new file mode 100644 index 00000000000..ddceb5ffaa0 --- /dev/null +++ b/lib/update_util/resources/manifest.h @@ -0,0 +1,71 @@ +#pragma once + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + ResourceManifestEntryTypeUnknown = 0, + ResourceManifestEntryTypeVersion, + ResourceManifestEntryTypeTimestamp, + ResourceManifestEntryTypeDirectory, + ResourceManifestEntryTypeFile, +} ResourceManifestEntryType; + +typedef struct { + ResourceManifestEntryType type; + FuriString* name; + uint32_t size; + uint8_t hash[16]; +} ResourceManifestEntry; + +typedef struct ResourceManifestReader ResourceManifestReader; + +/** + * @brief Initialize resource manifest reader + * @param storage Storage API pointer + * @return allocated object + */ +ResourceManifestReader* resource_manifest_reader_alloc(Storage* storage); + +/** + * @brief Release resource manifest reader + * @param resource_manifest allocated object + */ +void resource_manifest_reader_free(ResourceManifestReader* resource_manifest); + +/** + * @brief Initialize resource manifest reader iteration + * @param resource_manifest allocated object + * @param filename manifest file name + * @return true if file opened + */ +bool resource_manifest_reader_open(ResourceManifestReader* resource_manifest, const char* filename); + +/** + * @brief Read next file/dir entry from manifest + * @param resource_manifest allocated object + * @return entry or NULL if end of file + */ +ResourceManifestEntry* resource_manifest_reader_next(ResourceManifestReader* resource_manifest); + +/** Read previous file/dir entry from manifest + * + * You must be at the end of the manifest to use this function. + * Intended to be used after reaching end with resource_manifest_reader_next + * + * @param resource_manifest Pointer to the ResourceManifestReader instance + * + * @return entry or NULL if end of file + */ +ResourceManifestEntry* + resource_manifest_reader_previous(ResourceManifestReader* resource_manifest); + +#ifdef __cplusplus +} // extern "C" +#endif \ No newline at end of file diff --git a/lib/update_util/update_manifest.c b/lib/update_util/update_manifest.c index 1b205d9ce6f..42ab073b04c 100644 --- a/lib/update_util/update_manifest.c +++ b/lib/update_util/update_manifest.c @@ -21,12 +21,12 @@ UpdateManifest* update_manifest_alloc() { UpdateManifest* update_manifest = malloc(sizeof(UpdateManifest)); - string_init(update_manifest->version); - string_init(update_manifest->firmware_dfu_image); - string_init(update_manifest->radio_image); - string_init(update_manifest->staged_loader_file); - string_init(update_manifest->resource_bundle); - string_init(update_manifest->splash_file); + update_manifest->version = furi_string_alloc(); + update_manifest->firmware_dfu_image = furi_string_alloc(); + update_manifest->radio_image = furi_string_alloc(); + update_manifest->staged_loader_file = furi_string_alloc(); + update_manifest->resource_bundle = furi_string_alloc(); + update_manifest->splash_file = furi_string_alloc(); update_manifest->target = 0; update_manifest->manifest_version = 0; memset(update_manifest->ob_reference.bytes, 0, FURI_HAL_FLASH_OB_RAW_SIZE_BYTES); @@ -38,12 +38,12 @@ UpdateManifest* update_manifest_alloc() { void update_manifest_free(UpdateManifest* update_manifest) { furi_assert(update_manifest); - string_clear(update_manifest->version); - string_clear(update_manifest->firmware_dfu_image); - string_clear(update_manifest->radio_image); - string_clear(update_manifest->staged_loader_file); - string_clear(update_manifest->resource_bundle); - string_clear(update_manifest->splash_file); + furi_string_free(update_manifest->version); + furi_string_free(update_manifest->firmware_dfu_image); + furi_string_free(update_manifest->radio_image); + furi_string_free(update_manifest->staged_loader_file); + furi_string_free(update_manifest->resource_bundle); + furi_string_free(update_manifest->splash_file); free(update_manifest); } @@ -52,12 +52,12 @@ static bool furi_assert(update_manifest); furi_assert(flipper_file); - string_t filetype; + FuriString* filetype; - // TODO: compare filetype? - string_init(filetype); + filetype = furi_string_alloc(); update_manifest->valid = flipper_format_read_header(flipper_file, filetype, &update_manifest->manifest_version) && + furi_string_cmp_str(filetype, "Flipper firmware upgrade configuration") == 0 && flipper_format_read_string(flipper_file, MANIFEST_KEY_INFO, update_manifest->version) && flipper_format_read_uint32( flipper_file, MANIFEST_KEY_TARGET, &update_manifest->target, 1) && @@ -68,7 +68,7 @@ static bool MANIFEST_KEY_LOADER_CRC, (uint8_t*)&update_manifest->staged_loader_crc, sizeof(uint32_t)); - string_clear(filetype); + furi_string_free(filetype); if(update_manifest->valid) { /* Optional fields - we can have dfu, radio, resources, or any combination */ @@ -114,9 +114,9 @@ static bool flipper_file, MANIFEST_KEY_SPLASH_FILE, update_manifest->splash_file); update_manifest->valid = - (!string_empty_p(update_manifest->firmware_dfu_image) || - !string_empty_p(update_manifest->radio_image) || - !string_empty_p(update_manifest->resource_bundle)); + (!furi_string_empty(update_manifest->firmware_dfu_image) || + !furi_string_empty(update_manifest->radio_image) || + !furi_string_empty(update_manifest->resource_bundle)); } return update_manifest->valid; diff --git a/lib/update_util/update_manifest.h b/lib/update_util/update_manifest.h index 8f3859471f9..c26e4f87bc1 100644 --- a/lib/update_util/update_manifest.h +++ b/lib/update_util/update_manifest.h @@ -6,7 +6,7 @@ extern "C" { #include #include -#include +#include #include /* Paths don't include /ext -- because at startup SD card is mounted as FS root */ @@ -28,20 +28,20 @@ _Static_assert(sizeof(UpdateManifestRadioVersion) == 6, "UpdateManifestRadioVers typedef struct { uint32_t manifest_version; - string_t version; + FuriString* version; uint32_t target; - string_t staged_loader_file; + FuriString* staged_loader_file; uint32_t staged_loader_crc; - string_t firmware_dfu_image; - string_t radio_image; + FuriString* firmware_dfu_image; + FuriString* radio_image; uint32_t radio_address; UpdateManifestRadioVersion radio_version; uint32_t radio_crc; - string_t resource_bundle; + FuriString* resource_bundle; FuriHalFlashRawOptionByteData ob_reference; FuriHalFlashRawOptionByteData ob_compare_mask; FuriHalFlashRawOptionByteData ob_write_mask; - string_t splash_file; + FuriString* splash_file; bool valid; } UpdateManifest; diff --git a/lib/update_util/update_operation.c b/lib/update_util/update_operation.c index 138828ff0bf..39a7ea07526 100644 --- a/lib/update_util/update_operation.c +++ b/lib/update_util/update_operation.c @@ -4,7 +4,6 @@ #include #include -#include #include #include #include @@ -12,7 +11,7 @@ #define UPDATE_ROOT_DIR EXT_PATH("update") /* Need at least 4 free LFS pages before update */ -#define UPDATE_MIN_INT_FREE_SPACE 4 * 4 * 1024 +#define UPDATE_MIN_INT_FREE_SPACE (2 * 4 * 1024) static const char* update_prepare_result_descr[] = { [UpdatePrepareResultOK] = "OK", @@ -21,9 +20,12 @@ static const char* update_prepare_result_descr[] = { [UpdatePrepareResultManifestInvalid] = "Invalid manifest data", [UpdatePrepareResultStageMissing] = "Missing Stage2 loader", [UpdatePrepareResultStageIntegrityError] = "Corrupted Stage2 loader", - [UpdatePrepareResultManifestPointerError] = "Failed to create update pointer file", + [UpdatePrepareResultManifestPointerCreateError] = "Failed to create update pointer file", + [UpdatePrepareResultManifestPointerCheckError] = "Update pointer file error (corrupted FS?)", + [UpdatePrepareResultTargetMismatch] = "Hardware target mismatch", [UpdatePrepareResultOutdatedManifestVersion] = "Update package is too old", [UpdatePrepareResultIntFull] = "Need more free space in internal storage", + [UpdatePrepareResultUnspecifiedError] = "Unknown error", }; const char* update_operation_describe_preparation_result(const UpdatePrepareResult value) { @@ -34,9 +36,9 @@ const char* update_operation_describe_preparation_result(const UpdatePrepareResu } } -static bool update_operation_get_current_package_path_rtc(Storage* storage, string_t out_path) { +static bool update_operation_get_current_package_path_rtc(Storage* storage, FuriString* out_path) { const uint32_t update_index = furi_hal_rtc_get_register(FuriHalRtcRegisterUpdateFolderFSIndex); - string_set_str(out_path, UPDATE_ROOT_DIR); + furi_string_set(out_path, UPDATE_ROOT_DIR); if(update_index == UPDATE_OPERATION_ROOT_DIR_PACKAGE_MAGIC) { return true; } @@ -63,7 +65,7 @@ static bool update_operation_get_current_package_path_rtc(Storage* storage, stri free(name_buffer); storage_file_free(dir); if(!found) { - string_reset(out_path); + furi_string_reset(out_path); } return found; @@ -72,8 +74,8 @@ static bool update_operation_get_current_package_path_rtc(Storage* storage, stri #define UPDATE_FILE_POINTER_FN EXT_PATH(UPDATE_MANIFEST_POINTER_FILE_NAME) #define UPDATE_MANIFEST_MAX_PATH_LEN 256u -bool update_operation_get_current_package_manifest_path(Storage* storage, string_t out_path) { - string_reset(out_path); +bool update_operation_get_current_package_manifest_path(Storage* storage, FuriString* out_path) { + furi_string_reset(out_path); if(storage_common_stat(storage, UPDATE_FILE_POINTER_FN, NULL) == FSE_OK) { char* manifest_name_buffer = malloc(UPDATE_MANIFEST_MAX_PATH_LEN); File* upd_file = NULL; @@ -83,7 +85,7 @@ bool update_operation_get_current_package_manifest_path(Storage* storage, string upd_file, UPDATE_FILE_POINTER_FN, FSAM_READ, FSOM_OPEN_EXISTING)) { break; } - uint16_t bytes_read = + size_t bytes_read = storage_file_read(upd_file, manifest_name_buffer, UPDATE_MANIFEST_MAX_PATH_LEN); if((bytes_read == 0) || (bytes_read == UPDATE_MANIFEST_MAX_PATH_LEN)) { break; @@ -91,27 +93,27 @@ bool update_operation_get_current_package_manifest_path(Storage* storage, string if(storage_common_stat(storage, manifest_name_buffer, NULL) != FSE_OK) { break; } - string_set_str(out_path, manifest_name_buffer); + furi_string_set(out_path, manifest_name_buffer); } while(0); free(manifest_name_buffer); storage_file_free(upd_file); } else { /* legacy, will be deprecated */ - string_t rtcpath; - string_init(rtcpath); + FuriString* rtcpath; + rtcpath = furi_string_alloc(); do { if(!update_operation_get_current_package_path_rtc(storage, rtcpath)) { break; } - path_concat(string_get_cstr(rtcpath), UPDATE_MANIFEST_DEFAULT_NAME, out_path); + path_concat(furi_string_get_cstr(rtcpath), UPDATE_MANIFEST_DEFAULT_NAME, out_path); } while(0); - string_clear(rtcpath); + furi_string_free(rtcpath); } - return !string_empty_p(out_path); + return !furi_string_empty(out_path); } static bool update_operation_persist_manifest_path(Storage* storage, const char* manifest_path) { - const uint16_t manifest_path_len = strlen(manifest_path); + const size_t manifest_path_len = strlen(manifest_path); furi_check(manifest_path && manifest_path_len); bool success = false; File* file = storage_file_alloc(storage); @@ -141,8 +143,8 @@ UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { File* file = storage_file_alloc(storage); uint64_t free_int_space; - string_t stage_path; - string_init(stage_path); + FuriString* stage_path = furi_string_alloc(); + FuriString* manifest_path_check = furi_string_alloc(); do { if((storage_common_fs_info(storage, STORAGE_INT_PATH_PREFIX, NULL, &free_int_space) != FSE_OK) || @@ -164,16 +166,18 @@ UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { result = UpdatePrepareResultOutdatedManifestVersion; break; } - - if(furi_hal_version_get_hw_target() != manifest->target) { + /* Only compare hardware target if it is set - pre-production devices accept any firmware*/ + if(furi_hal_version_get_hw_target() && + (furi_hal_version_get_hw_target() != manifest->target)) { result = UpdatePrepareResultTargetMismatch; break; } path_extract_dirname(manifest_file_path, stage_path); - path_append(stage_path, string_get_cstr(manifest->staged_loader_file)); + path_append(stage_path, furi_string_get_cstr(manifest->staged_loader_file)); - if(!storage_file_open(file, string_get_cstr(stage_path), FSAM_READ, FSOM_OPEN_EXISTING)) { + if(!storage_file_open( + file, furi_string_get_cstr(stage_path), FSAM_READ, FSOM_OPEN_EXISTING)) { result = UpdatePrepareResultStageMissing; break; } @@ -185,7 +189,18 @@ UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { } if(!update_operation_persist_manifest_path(storage, manifest_file_path)) { - result = UpdatePrepareResultManifestPointerError; + result = UpdatePrepareResultManifestPointerCreateError; + break; + } + + if(!update_operation_get_current_package_manifest_path(storage, manifest_path_check) || + (furi_string_cmpi_str(manifest_path_check, manifest_file_path) != 0)) { + FURI_LOG_E( + "update", + "Manifest pointer check failed: '%s' != '%s'", + furi_string_get_cstr(manifest_path_check), + manifest_file_path); + result = UpdatePrepareResultManifestPointerCheckError; break; } @@ -193,7 +208,8 @@ UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePreUpdate); } while(false); - string_clear(stage_path); + furi_string_free(stage_path); + furi_string_free(manifest_path_check); storage_file_free(file); update_manifest_free(manifest); diff --git a/lib/update_util/update_operation.h b/lib/update_util/update_operation.h index 056520e1147..8e36b5a1370 100644 --- a/lib/update_util/update_operation.h +++ b/lib/update_util/update_operation.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #ifdef __cplusplus @@ -19,7 +18,7 @@ extern "C" { * May be empty if update is in root update directory * @return bool if supplied path is valid and out_manifest_dir contains dir to apply */ -bool update_operation_get_package_dir_name(const char* full_path, string_t out_manifest_dir); +bool update_operation_get_package_dir_name(const char* full_path, FuriString* out_manifest_dir); /* When updating this enum, also update assets/protobuf/system.proto */ typedef enum { @@ -29,7 +28,8 @@ typedef enum { UpdatePrepareResultManifestInvalid, UpdatePrepareResultStageMissing, UpdatePrepareResultStageIntegrityError, - UpdatePrepareResultManifestPointerError, + UpdatePrepareResultManifestPointerCreateError, + UpdatePrepareResultManifestPointerCheckError, UpdatePrepareResultTargetMismatch, UpdatePrepareResultOutdatedManifestVersion, UpdatePrepareResultIntFull, @@ -51,7 +51,7 @@ UpdatePrepareResult update_operation_prepare(const char* manifest_file_path); * @param out_path Path to manifest. Must be initialized * @return true if path was restored successfully */ -bool update_operation_get_current_package_manifest_path(Storage* storage, string_t out_path); +bool update_operation_get_current_package_manifest_path(Storage* storage, FuriString* out_path); /* * Checks if an update operation step is pending after reset diff --git a/scripts/ReadMe.md b/scripts/ReadMe.md index d06303957fe..359ce472ae6 100644 --- a/scripts/ReadMe.md +++ b/scripts/ReadMe.md @@ -26,16 +26,15 @@ Also display type, region and etc... ## Core1 and Core2 firmware flashing -Main flashing sequence can be found in root `Makefile`. Core2 goes first, then Core1. -Never flash FUS or you will loose your job, girlfriend and keys in secure enclave. +Never flash FUS or you will lose your job, girlfriend and keys in secure enclave. ## Option Bytes !!! Setting incorrect Option Bytes may brick your MCU !!! Defaults are mostly OK, but there are couple things that we'd like to tune. -Also OB may be damaged, so we've made couple scripts to check and set option bytes. +Also, OB may be damaged, so we've made couple scripts to check and set option bytes. !!! Setting incorrect Option Bytes may brick your MCU !!! @@ -53,10 +52,10 @@ ob.py set # Assets delivery -Run in the root folder of the repo: +Build the firmware and run in the root folder of the repo: ```bash -python scripts/storage.py -p send assets/resources /ext +python scripts/storage.py -p send build/latest/resources /ext ``` @@ -70,4 +69,4 @@ Then run python scripts/slideshow.py -i assets/slideshow/my_show/ -o assets/slideshow/my_show/.slideshow ``` -Upload generated .slideshow file to Flipper's internal storage and restart it. \ No newline at end of file +Upload generated .slideshow file to Flipper's internal storage and restart it. diff --git a/scripts/assets.py b/scripts/assets.py index b27b29efd05..1099f0c330d 100755 --- a/scripts/assets.py +++ b/scripts/assets.py @@ -1,24 +1,21 @@ #!/usr/bin/env python3 +import os +import shutil + from flipper.app import App from flipper.assets.icon import file2image -import logging -import argparse -import subprocess -import io -import os -import sys - ICONS_SUPPORTED_FORMATS = ["png"] ICONS_TEMPLATE_H_HEADER = """#pragma once + #include """ ICONS_TEMPLATE_H_ICON_NAME = "extern const Icon {name};\n" -ICONS_TEMPLATE_C_HEADER = """#include \"assets_icons.h\" +ICONS_TEMPLATE_C_HEADER = """#include "{assets_filename}.h" #include @@ -37,12 +34,26 @@ def init(self): ) self.parser_icons.add_argument("input_directory", help="Source directory") self.parser_icons.add_argument("output_directory", help="Output directory") + self.parser_icons.add_argument( + "--filename", + help="Base filename for file with icon data", + required=False, + default="assets_icons", + ) + self.parser_icons.set_defaults(func=self.icons) self.parser_manifest = self.subparsers.add_parser( "manifest", help="Create directory Manifest" ) self.parser_manifest.add_argument("local_path", help="local_path") + self.parser_manifest.add_argument( + "--timestamp", + help="timestamp value to embed", + default=0, + type=int, + required=False, + ) self.parser_manifest.set_defaults(func=self.manifest) self.parser_copro = self.subparsers.add_parser( @@ -50,7 +61,6 @@ def init(self): ) self.parser_copro.add_argument("cube_dir", help="Path to Cube folder") self.parser_copro.add_argument("output_dir", help="Path to output folder") - self.parser_copro.add_argument("mcu", help="MCU series as in copro folder") self.parser_copro.add_argument( "--cube_ver", dest="cube_ver", help="Cube version", required=True ) @@ -99,13 +109,15 @@ def _iconIsSupported(self, filename): return extension in ICONS_SUPPORTED_FORMATS def icons(self): - self.logger.debug(f"Converting icons") + self.logger.debug("Converting icons") icons_c = open( - os.path.join(self.args.output_directory, "assets_icons.c"), + os.path.join(self.args.output_directory, f"{self.args.filename}.c"), "w", newline="\n", ) - icons_c.write(ICONS_TEMPLATE_C_HEADER) + icons_c.write( + ICONS_TEMPLATE_C_HEADER.format(assets_filename=self.args.filename) + ) icons = [] # Traverse icons tree, append image data to source file for dirpath, dirnames, filenames in os.walk(self.args.input_directory): @@ -115,7 +127,7 @@ def icons(self): if not filenames: continue if "frame_rate" in filenames: - self.logger.debug(f"Folder contatins animation") + self.logger.debug("Folder contains animation") icon_name = "A_" + os.path.split(dirpath)[1].replace("-", "_") width = height = None frame_count = 0 @@ -174,7 +186,7 @@ def icons(self): icons_c.write("\n") icons.append((icon_name, width, height, 0, 1)) # Create array of images: - self.logger.debug(f"Finalizing source file") + self.logger.debug("Finalizing source file") for name, width, height, frame_rate, frame_count in icons: icons_c.write( ICONS_TEMPLATE_C_ICONS.format( @@ -189,9 +201,9 @@ def icons(self): icons_c.close() # Create Public Header - self.logger.debug(f"Creating header") + self.logger.debug("Creating header") icons_h = open( - os.path.join(self.args.output_directory, "assets_icons.h"), + os.path.join(self.args.output_directory, f"{self.args.filename}.h"), "w", newline="\n", ) @@ -199,7 +211,7 @@ def icons(self): for name, width, height, frame_rate, frame_count in icons: icons_h.write(ICONS_TEMPLATE_H_ICON_NAME.format(name=name)) icons_h.close() - self.logger.debug(f"Done") + self.logger.debug("Done") return 0 def manifest(self): @@ -209,6 +221,7 @@ def manifest(self): if not os.path.isdir(directory_path): self.logger.error(f'"{directory_path}" is not a directory') exit(255) + manifest_file = os.path.join(directory_path, "Manifest") old_manifest = Manifest() if os.path.exists(manifest_file): @@ -217,55 +230,61 @@ def manifest(self): self.logger.info( f'Creating temporary Manifest for directory "{directory_path}"' ) - new_manifest = Manifest() + new_manifest = Manifest(self.args.timestamp) new_manifest.create(directory_path) - self.logger.info(f"Comparing new manifest with existing") + self.logger.info("Comparing new manifest with existing") only_in_old, changed, only_in_new = Manifest.compare(old_manifest, new_manifest) for record in only_in_old: - self.logger.info(f"Only in old: {record}") + self.logger.debug(f"Only in old: {record}") for record in changed: self.logger.info(f"Changed: {record}") for record in only_in_new: - self.logger.info(f"Only in new: {record}") + self.logger.debug(f"Only in new: {record}") if any((only_in_old, changed, only_in_new)): - self.logger.warning("Manifests are different, updating") + self.logger.info( + f"Manifest updated ({len(only_in_new)} new, {len(only_in_old)} removed, {len(changed)} changed)" + ) new_manifest.save(manifest_file) else: self.logger.info("Manifest is up-to-date!") - self.logger.info(f"Complete") + self.logger.info("Complete") return 0 def copro(self): from flipper.assets.copro import Copro - self.logger.info(f"Bundling coprocessor binaries") - copro = Copro(self.args.mcu) - self.logger.info(f"Loading CUBE info") - copro.loadCubeInfo(self.args.cube_dir, self.args.cube_ver) - self.logger.info(f"Bundling") - copro.bundle( - self.args.output_dir, - self.args.stack_file, - self.args.stack_type, - self.args.stack_addr, - ) - self.logger.info(f"Complete") + self.logger.info("Bundling coprocessor binaries") + copro = Copro() + try: + self.logger.info("Loading CUBE info") + copro.loadCubeInfo(self.args.cube_dir, self.args.cube_ver) + self.logger.info("Bundling") + copro.bundle( + self.args.output_dir, + self.args.stack_file, + self.args.stack_type, + self.args.stack_addr, + ) + except Exception as e: + self.logger.error(f"Failed to bundle: {e}") + return 1 + self.logger.info("Complete") return 0 def dolphin(self): from flipper.assets.dolphin import Dolphin - self.logger.info(f"Processing Dolphin sources") + self.logger.info("Processing Dolphin sources") dolphin = Dolphin() - self.logger.info(f"Loading data") + self.logger.info("Loading data") dolphin.load(self.args.input_directory) - self.logger.info(f"Packing") + self.logger.info("Packing") dolphin.pack(self.args.output_directory, self.args.symbol_name) - self.logger.info(f"Complete") + self.logger.info("Complete") return 0 diff --git a/debug/FreeRTOS/FreeRTOS.py b/scripts/debug/FreeRTOS/FreeRTOS.py similarity index 99% rename from debug/FreeRTOS/FreeRTOS.py rename to scripts/debug/FreeRTOS/FreeRTOS.py index 036e18f31db..0eb7e5f8d5b 100644 --- a/debug/FreeRTOS/FreeRTOS.py +++ b/scripts/debug/FreeRTOS/FreeRTOS.py @@ -29,7 +29,6 @@ class Scheduler: def __init__(self): - self._blocked = ListInspector("xSuspendedTaskList") self._delayed1 = ListInspector("xDelayedTaskList1") self._delayed2 = ListInspector("xDelayedTaskList2") diff --git a/debug/FreeRTOS/FreeRTOSgdb/EventGroup.py b/scripts/debug/FreeRTOS/FreeRTOSgdb/EventGroup.py similarity index 100% rename from debug/FreeRTOS/FreeRTOSgdb/EventGroup.py rename to scripts/debug/FreeRTOS/FreeRTOSgdb/EventGroup.py diff --git a/debug/FreeRTOS/FreeRTOSgdb/GDBCommands.py b/scripts/debug/FreeRTOS/FreeRTOSgdb/GDBCommands.py similarity index 99% rename from debug/FreeRTOS/FreeRTOSgdb/GDBCommands.py rename to scripts/debug/FreeRTOS/FreeRTOSgdb/GDBCommands.py index ba811e3e131..5564502ecc9 100644 --- a/debug/FreeRTOS/FreeRTOSgdb/GDBCommands.py +++ b/scripts/debug/FreeRTOS/FreeRTOSgdb/GDBCommands.py @@ -61,7 +61,6 @@ def PrintQueueInfo(self, q): if maxCount == 0: print(outputFmt % (q.GetName(), q.GetQueueMessagesWaiting(), "", "")) else: - for i in range(0, maxCount): txName = "" if i < len(sendList): diff --git a/debug/FreeRTOS/FreeRTOSgdb/HandleRegistry.py b/scripts/debug/FreeRTOS/FreeRTOSgdb/HandleRegistry.py similarity index 99% rename from debug/FreeRTOS/FreeRTOSgdb/HandleRegistry.py rename to scripts/debug/FreeRTOS/FreeRTOSgdb/HandleRegistry.py index 1682c917665..c13457017b3 100644 --- a/debug/FreeRTOS/FreeRTOSgdb/HandleRegistry.py +++ b/scripts/debug/FreeRTOS/FreeRTOSgdb/HandleRegistry.py @@ -48,7 +48,6 @@ def PrintRegistry(self): print("%d: %3s %16s" % (i, h, name)) def FilterBy(self, qMode): - """Retrieve a List of Mutex Queue Handles""" resp = [] for i in range(self._minIndex, self._maxIndex): diff --git a/debug/FreeRTOS/FreeRTOSgdb/List.py b/scripts/debug/FreeRTOS/FreeRTOSgdb/List.py similarity index 99% rename from debug/FreeRTOS/FreeRTOSgdb/List.py rename to scripts/debug/FreeRTOS/FreeRTOSgdb/List.py index 62aa9dc9ab8..575bdc525a3 100644 --- a/debug/FreeRTOS/FreeRTOSgdb/List.py +++ b/scripts/debug/FreeRTOS/FreeRTOSgdb/List.py @@ -56,7 +56,6 @@ def GetElements(self, CastTypeStr=None, startElem=1): of some of the TCB Task lists. """ if self._list != None: - CastType = None if CastTypeStr != None: if type(CastTypeStr) == str: @@ -73,7 +72,6 @@ def GetElements(self, CastTypeStr=None, startElem=1): index = self._list["pxIndex"] if numElems > 0 and numElems < 200: - if startElem == 0: curr = index else: diff --git a/debug/FreeRTOS/FreeRTOSgdb/QueueTools.py b/scripts/debug/FreeRTOS/FreeRTOSgdb/QueueTools.py similarity index 99% rename from debug/FreeRTOS/FreeRTOSgdb/QueueTools.py rename to scripts/debug/FreeRTOS/FreeRTOSgdb/QueueTools.py index 49a780db3e7..a35f0894f1c 100644 --- a/debug/FreeRTOS/FreeRTOSgdb/QueueTools.py +++ b/scripts/debug/FreeRTOS/FreeRTOSgdb/QueueTools.py @@ -47,7 +47,6 @@ def IsValid(qType): class QueueInspector: - QueueType = gdb.lookup_type("Queue_t") def __init__(self, handle): diff --git a/debug/FreeRTOS/FreeRTOSgdb/Task.py b/scripts/debug/FreeRTOS/FreeRTOSgdb/Task.py similarity index 99% rename from debug/FreeRTOS/FreeRTOSgdb/Task.py rename to scripts/debug/FreeRTOS/FreeRTOSgdb/Task.py index 04da3bbcd41..d3078fdc922 100644 --- a/debug/FreeRTOS/FreeRTOSgdb/Task.py +++ b/scripts/debug/FreeRTOS/FreeRTOSgdb/Task.py @@ -11,7 +11,6 @@ class TaskInspector: - TCBType = gdb.lookup_type("TCB_t") def __init__(self, handle): diff --git a/debug/FreeRTOS/FreeRTOSgdb/Types.py b/scripts/debug/FreeRTOS/FreeRTOSgdb/Types.py similarity index 100% rename from debug/FreeRTOS/FreeRTOSgdb/Types.py rename to scripts/debug/FreeRTOS/FreeRTOSgdb/Types.py diff --git a/debug/FreeRTOS/FreeRTOSgdb/__init__.py b/scripts/debug/FreeRTOS/FreeRTOSgdb/__init__.py similarity index 100% rename from debug/FreeRTOS/FreeRTOSgdb/__init__.py rename to scripts/debug/FreeRTOS/FreeRTOSgdb/__init__.py diff --git a/debug/FreeRTOS/LICENSE b/scripts/debug/FreeRTOS/LICENSE similarity index 100% rename from debug/FreeRTOS/LICENSE rename to scripts/debug/FreeRTOS/LICENSE diff --git a/debug/FreeRTOS/README.md b/scripts/debug/FreeRTOS/README.md similarity index 100% rename from debug/FreeRTOS/README.md rename to scripts/debug/FreeRTOS/README.md diff --git a/debug/PyCortexMDebug/LICENSE b/scripts/debug/PyCortexMDebug/LICENSE similarity index 100% rename from debug/PyCortexMDebug/LICENSE rename to scripts/debug/PyCortexMDebug/LICENSE diff --git a/debug/PyCortexMDebug/PyCortexMDebug.py b/scripts/debug/PyCortexMDebug/PyCortexMDebug.py similarity index 96% rename from debug/PyCortexMDebug/PyCortexMDebug.py rename to scripts/debug/PyCortexMDebug/PyCortexMDebug.py index 6533535c32c..fd322b5a62c 100644 --- a/debug/PyCortexMDebug/PyCortexMDebug.py +++ b/scripts/debug/PyCortexMDebug/PyCortexMDebug.py @@ -28,7 +28,5 @@ sys.path.append(directory) from cmdebug.svd_gdb import LoadSVD -from cmdebug.dwt_gdb import DWT -DWT() LoadSVD() diff --git a/scripts/debug/PyCortexMDebug/README.md b/scripts/debug/PyCortexMDebug/README.md new file mode 100644 index 00000000000..0a5764b02de --- /dev/null +++ b/scripts/debug/PyCortexMDebug/README.md @@ -0,0 +1,35 @@ +PyCortexMDebug +============== + +## SVD + +ARM defines an SVD (System View Description) file format in its CMSIS standard as a means for Cortex-M-based chip manufacturers to provide a common description of peripherals, registers, and register fields. You can download SVD files for different manufacturers [here](http://www.arm.com/products/processors/cortex-m/cortex-microcontroller-software-interface-standard.php). + +The implementation consists of two components -- An lxml-based parser module (pysvd) and a GDB file (gdb_svd). I haven't yet worked out a perfect workflow for this, though it's quite easy to use when you already tend to have a GDB initialization file for starting up OpenOCD and the like. However your workflow works, just make sure to, in GDB: + + source gdb_svd.py + svd_load [your_svd_file].svd + +These files can be huge so it might take a second or two. Anyways, after that, you can do + + svd + +to list available peripherals with descriptions. Or you can do + + svd [some_peripheral_name] + +to see all of the registers (with their values) for a given peripheral. For more details, run + + svd [some_peripheral_name] [some_register_name] + +to see all of the field values with descriptions. + +You can add format modifiers like: + +* `svd/x` will display values in hex +* `svd/o` will display values in octal +* `svd/t` or `svd/b` will display values in binary +* `svd/a` will display values in hex and try to resolve symbols from the values + +All field values are displayed at the correct lengths as provided by the SVD files. +Also, tab completion exists for nearly everything! When in doubt, run `svd help`. diff --git a/debug/PyCortexMDebug/cmdebug/__init__.py b/scripts/debug/PyCortexMDebug/cmdebug/__init__.py similarity index 100% rename from debug/PyCortexMDebug/cmdebug/__init__.py rename to scripts/debug/PyCortexMDebug/cmdebug/__init__.py diff --git a/debug/PyCortexMDebug/cmdebug/svd.py b/scripts/debug/PyCortexMDebug/cmdebug/svd.py similarity index 89% rename from debug/PyCortexMDebug/cmdebug/svd.py rename to scripts/debug/PyCortexMDebug/cmdebug/svd.py index f4a884bb4a5..a25e69bfb90 100755 --- a/debug/PyCortexMDebug/cmdebug/svd.py +++ b/scripts/debug/PyCortexMDebug/cmdebug/svd.py @@ -16,15 +16,14 @@ along with PyCortexMDebug. If not, see . """ -from collections import OrderedDict -from . import x2d - -import traceback -import warnings -import pickle +import lxml.objectify as objectify import sys +from collections import OrderedDict import os +import pickle +import traceback import re +import warnings class SmartDict: @@ -127,31 +126,26 @@ class SVDFile: def __init__(self, fname): """ + Args: fname: Filename for the SVD file """ + f = objectify.parse(os.path.expanduser(fname)) + root = f.getroot() + periph = root.peripherals.getchildren() self.peripherals = SmartDict() self.base_address = 0 - xml_file_name = os.path.expanduser(fname) - pickle_file_name = xml_file_name + ".pickle" - root = None - if os.path.exists(pickle_file_name): - print("Loading pickled SVD") - root = pickle.load(open(pickle_file_name, "rb")) - else: - print("Loading XML SVD and pickling it") - root = x2d.parse(open(xml_file_name, "rb")) - pickle.dump(root, open(pickle_file_name, "wb"), pickle.HIGHEST_PROTOCOL) - print("Processing SVD tree") # XML elements - for p in root["device"]["peripherals"]["peripheral"]: + for p in periph: try: - self.peripherals[p["name"]] = SVDPeripheral(p, self) + if p.tag == "peripheral": + self.peripherals[str(p.name)] = SVDPeripheral(p, self) + else: + # This is some other tag + pass except SVDNonFatalError as e: - # print(e) - pass - print("SVD Ready") + print(e) def add_register(parent, node): @@ -271,11 +265,11 @@ def __init__(self, svd_elem, parent): self.parent_base_address = parent.base_address # Look for a base address, as it is required - if "baseAddress" not in svd_elem: + if not hasattr(svd_elem, "baseAddress"): raise SVDNonFatalError("Periph without base address") self.base_address = int(str(svd_elem.baseAddress), 0) - if "@derivedFrom" in svd_elem: - derived_from = svd_elem["@derivedFrom"] + if "derivedFrom" in svd_elem.attrib: + derived_from = svd_elem.attrib["derivedFrom"] try: self.name = str(svd_elem.name) except AttributeError: @@ -301,14 +295,16 @@ def copier(a): self.clusters = SmartDict() if hasattr(svd_elem, "registers"): - if "register" in svd_elem.registers: - for r in svd_elem.registers.register: - if isinstance(r, x2d.ObjectDict): - add_register(self, r) - if "cluster" in svd_elem.registers: - for c in svd_elem.registers.cluster: - if isinstance(c, x2d.ObjectDict): - add_cluster(self, c) + registers = [ + r + for r in svd_elem.registers.getchildren() + if r.tag in ["cluster", "register"] + ] + for r in registers: + if r.tag == "cluster": + add_cluster(self, r) + elif r.tag == "register": + add_register(self, r) def refactor_parent(self, parent): self.parent_base_address = parent.base_address @@ -342,11 +338,11 @@ def __init__(self, svd_elem, parent): else: self.size = 0x20 self.fields = SmartDict() - if "fields" in svd_elem: + if hasattr(svd_elem, "fields"): # Filter fields to only consider those of tag "field" - for f in svd_elem.fields.field: - if isinstance(f, x2d.ObjectDict): - self.fields[str(f.name)] = SVDPeripheralRegisterField(f, self) + fields = [f for f in svd_elem.fields.getchildren() if f.tag == "field"] + for f in fields: + self.fields[str(f.name)] = SVDPeripheralRegisterField(f, self) def refactor_parent(self, parent): self.parent_base_address = parent.base_address diff --git a/debug/PyCortexMDebug/cmdebug/svd_gdb.py b/scripts/debug/PyCortexMDebug/cmdebug/svd_gdb.py similarity index 100% rename from debug/PyCortexMDebug/cmdebug/svd_gdb.py rename to scripts/debug/PyCortexMDebug/cmdebug/svd_gdb.py diff --git a/debug/STM32WB55_CM4.svd b/scripts/debug/STM32WB55_CM4.svd similarity index 100% rename from debug/STM32WB55_CM4.svd rename to scripts/debug/STM32WB55_CM4.svd diff --git a/scripts/debug/flipperapps.py b/scripts/debug/flipperapps.py new file mode 100644 index 00000000000..6dba89a5640 --- /dev/null +++ b/scripts/debug/flipperapps.py @@ -0,0 +1,209 @@ +from dataclasses import dataclass +from typing import Optional, Tuple, Dict, ClassVar +import struct +import posixpath +import zlib + +import gdb + + +def get_file_crc32(filename): + with open(filename, "rb") as f: + return zlib.crc32(f.read()) + + +@dataclass +class AppState: + name: str + text_address: int = 0 + entry_address: int = 0 + other_sections: Dict[str, int] = None + debug_link_elf: str = "" + debug_link_crc: int = 0 + + DEBUG_ELF_ROOT: ClassVar[Optional[str]] = None + + def __post_init__(self): + if self.other_sections is None: + self.other_sections = {} + + def get_original_elf_path(self) -> str: + if self.DEBUG_ELF_ROOT is None: + raise ValueError("DEBUG_ELF_ROOT not set; call fap-set-debug-elf-root") + return ( + posixpath.join(self.DEBUG_ELF_ROOT, self.debug_link_elf) + if self.DEBUG_ELF_ROOT + else self.debug_link_elf + ) + + def is_debug_available(self) -> bool: + have_debug_info = bool(self.debug_link_elf and self.debug_link_crc) + if not have_debug_info: + print("No debug info available for this app") + return False + debug_elf_path = self.get_original_elf_path() + debug_elf_crc32 = get_file_crc32(debug_elf_path) + if self.debug_link_crc != debug_elf_crc32: + print( + f"Debug info ({debug_elf_path}) CRC mismatch: {self.debug_link_crc:08x} != {debug_elf_crc32:08x}, rebuild app" + ) + return False + return True + + def get_gdb_load_command(self) -> str: + load_path = self.get_original_elf_path() + print(f"Loading debug information from {load_path}") + load_command = ( + f"add-symbol-file -readnow {load_path} 0x{self.text_address:08x} " + ) + load_command += " ".join( + f"-s {name} 0x{address:08x}" + for name, address in self.other_sections.items() + ) + return load_command + + def get_gdb_unload_command(self) -> str: + return f"remove-symbol-file -a 0x{self.text_address:08x}" + + @staticmethod + def get_gdb_app_ep(app) -> int: + return int(app["state"]["entry"]) + + @staticmethod + def parse_debug_link_data(section_data: bytes) -> Tuple[str, int]: + # Debug link format: a null-terminated string with debuggable file name + # Padded with 0's to multiple of 4 bytes + # Followed by 4 bytes of CRC32 checksum of that file + elf_name = section_data[:-4].decode("utf-8").split("\x00")[0] + crc32 = struct.unpack(" "AppState": + state = AppState(str(gdb_app["manifest"]["name"].string())) + state.entry_address = cls.get_gdb_app_ep(gdb_app) + + app_state = gdb_app["state"] + if debug_link_size := int(app_state["debug_link_info"]["debug_link_size"]): + debug_link_data = ( + gdb.selected_inferior() + .read_memory( + int(app_state["debug_link_info"]["debug_link"]), debug_link_size + ) + .tobytes() + ) + state.debug_link_elf, state.debug_link_crc = AppState.parse_debug_link_data( + debug_link_data + ) + + for idx in range(app_state["mmap_entry_count"]): + mmap_entry = app_state["mmap_entries"][idx] + section_name = mmap_entry["name"].string() + section_addr = int(mmap_entry["address"]) + if section_name == ".text": + state.text_address = section_addr + else: + state.other_sections[section_name] = section_addr + + return state + + +class SetFapDebugElfRoot(gdb.Command): + """Set path to original ELF files for debug info""" + + def __init__(self): + super().__init__( + "fap-set-debug-elf-root", gdb.COMMAND_FILES, gdb.COMPLETE_FILENAME + ) + self.dont_repeat() + + def invoke(self, arg, from_tty): + AppState.DEBUG_ELF_ROOT = arg + try: + global helper + print(f"Set '{arg}' as debug info lookup path for Flipper external apps") + helper.attach_to_fw() + gdb.events.stop.connect(helper.handle_stop) + gdb.events.exited.connect(helper.handle_exit) + except gdb.error as e: + print(f"Support for Flipper external apps debug is not available: {e}") + + +class FlipperAppStateHelper: + def __init__(self): + self.app_type_ptr = None + self.app_list_ptr = None + self.app_list_entry_type = None + self._current_apps: list[AppState] = [] + self.set_debug_mode(True) + + def _walk_app_list(self, list_head): + while list_head: + if app := list_head["data"]: + yield app.dereference() + list_head = list_head["next"] + + def _exec_gdb_command(self, command: str) -> bool: + try: + gdb.execute(command) + return True + except gdb.error as e: + print(f"Failed to execute GDB command '{command}': {e}") + return False + + def _sync_apps(self) -> None: + self.set_debug_mode(True) + if not (app_list := self.app_list_ptr.value()): + print("Reset app loader state") + for app in self._current_apps: + self._exec_gdb_command(app.get_gdb_unload_command()) + self._current_apps = [] + return + + loaded_apps: dict[int, gdb.Value] = dict( + (AppState.get_gdb_app_ep(app), app) + for app in self._walk_app_list(app_list[0]) + ) + + for app in self._current_apps.copy(): + if app.entry_address not in loaded_apps: + print(f"Application {app.name} is no longer loaded") + if not self._exec_gdb_command(app.get_gdb_unload_command()): + print(f"Failed to unload debug info for {app.name}") + self._current_apps.remove(app) + + for entry_point, app in loaded_apps.items(): + if entry_point not in set(app.entry_address for app in self._current_apps): + new_app_state = AppState.from_gdb(app) + print(f"New application loaded. Adding debug info") + if self._exec_gdb_command(new_app_state.get_gdb_load_command()): + self._current_apps.append(new_app_state) + else: + print(f"Failed to load debug info for {new_app_state}") + + def attach_to_fw(self) -> None: + print("Attaching to Flipper firmware") + self.app_list_ptr = gdb.lookup_global_symbol( + "flipper_application_loaded_app_list" + ) + self.app_type_ptr = gdb.lookup_type("FlipperApplication").pointer() + self.app_list_entry_type = gdb.lookup_type("struct FlipperApplicationList_s") + self._sync_apps() + + def handle_stop(self, event) -> None: + self._sync_apps() + + def handle_exit(self, event) -> None: + self.set_debug_mode(False) + + def set_debug_mode(self, mode: bool) -> None: + try: + gdb.execute(f"set variable furi_hal_debug_gdb_session_active = {int(mode)}") + except gdb.error as e: + print(f"Failed to set debug mode: {e}") + + +# Init additional 'fap-set-debug-elf-root' command and set up hooks +SetFapDebugElfRoot() +helper = FlipperAppStateHelper() +print("Support for Flipper external apps debug is loaded") diff --git a/scripts/debug/flipperversion.py b/scripts/debug/flipperversion.py new file mode 100644 index 00000000000..56915c0f28e --- /dev/null +++ b/scripts/debug/flipperversion.py @@ -0,0 +1,123 @@ +from dataclasses import dataclass, field +from typing import Dict, Optional + +import gdb + + +# Must match FuriHalRtcRegisterVersion index in FuriHalRtcRegister enum +RTC_BACKUP_VERSION_REGISTER_IDX = 0x2 + +RTC_BASE = 0x40002800 +RTC_BACKUP_BASE = RTC_BASE + 0x50 + +VERSION_REGISTER_ADDRESS = RTC_BACKUP_BASE + RTC_BACKUP_VERSION_REGISTER_IDX * 4 + +VERSION_STRUCT_MAGIC = 0xBE40 + + +@dataclass +class VersionData: + git_hash: str + git_branch: str + build_date: str + version: str + target: int + build_is_dirty: bool + # Since version 1.1 + firmware_origin: str = "" + git_origin: str = "" + # More fields may be added in the future + extra: Optional[Dict[str, str]] = field(default_factory=dict) + + +class VersionLoader: + def __init__(self, version_ptr): + self.version_ptr = version_ptr + self._cstr_type = gdb.lookup_type("char").pointer() + self._uint_type = gdb.lookup_type("unsigned int") + + version_signature = version_ptr.dereference().cast(self._uint_type) + is_versioned = (version_signature & (0xFFFF)) == VERSION_STRUCT_MAGIC + if is_versioned: + self._version_data = self.load_versioned( + major=version_signature >> 16 & 0xFF, + minor=version_signature >> 24 & 0xFF, + ) + else: + self._version_data = self.load_unversioned() + + @property + def version(self) -> VersionData: + return self._version_data + + def load_versioned(self, major, minor): + if major != 1: + raise ValueError("Unsupported version struct major version") + + # Struct version 1.0 + extra_data = int(self.version_ptr[5].cast(self._uint_type)) + version_data = VersionData( + git_hash=self.version_ptr[1].cast(self._cstr_type).string(), + git_branch=self.version_ptr[2].cast(self._cstr_type).string(), + build_date=self.version_ptr[3].cast(self._cstr_type).string(), + version=self.version_ptr[4].cast(self._cstr_type).string(), + target=extra_data & 0xF, + build_is_dirty=bool((extra_data >> 8) & 0xF), + ) + if minor >= 1: + version_data.firmware_origin = ( + self.version_ptr[6].cast(self._cstr_type).string() + ) + version_data.git_origin = self.version_ptr[7].cast(self._cstr_type).string() + return version_data + + def load_unversioned(self): + """Parse an early version of the version struct.""" + extra_data = int(self.version_ptr[5].cast(self._uint_type)) + return VersionData( + git_hash=self.version_ptr[0].cast(self._cstr_type).string(), + git_branch=self.version_ptr[1].cast(self._cstr_type).string(), + # branch number is #2, but we don't care about it + build_date=self.version_ptr[3].cast(self._cstr_type).string(), + version=self.version_ptr[4].cast(self._cstr_type).string(), + target=extra_data & 0xF, + build_is_dirty=bool((extra_data >> 8) & 0xF), + ) + + +class FlipperFwVersion(gdb.Command): + """Print the version of Flipper's firmware.""" + + def __init__(self): + super(FlipperFwVersion, self).__init__("fw-version", gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + void_ptr_type = gdb.lookup_type("void").pointer().pointer() + version_ptr_ptr = gdb.Value(VERSION_REGISTER_ADDRESS).cast(void_ptr_type) + + if not version_ptr_ptr: + print("RTC version register is NULL") + return + + version_ptr = version_ptr_ptr.dereference() + if not version_ptr: + print("Pointer to version struct is NULL") + return + + version_struct = version_ptr.cast(void_ptr_type) + + v = VersionLoader(version_struct) + print("Firmware version on attached Flipper:") + print(f"\tVersion: {v.version.version}") + print(f"\tBuilt on: {v.version.build_date}") + print(f"\tGit branch: {v.version.git_branch}") + print(f"\tGit commit: {v.version.git_hash}") + print(f"\tDirty: {v.version.build_is_dirty}") + print(f"\tHW Target: {v.version.target}") + if v.version.firmware_origin: + print(f"\tOrigin: {v.version.firmware_origin}") + if v.version.git_origin: + print(f"\tGit origin: {v.version.git_origin}") + + +FlipperFwVersion() diff --git a/debug/fw.jflash b/scripts/debug/fw.jflash similarity index 100% rename from debug/fw.jflash rename to scripts/debug/fw.jflash diff --git a/scripts/debug/gdbinit b/scripts/debug/gdbinit new file mode 100644 index 00000000000..cba5d6b1a81 --- /dev/null +++ b/scripts/debug/gdbinit @@ -0,0 +1,10 @@ +set confirm off +set pagination off +set print pretty on +set print object on +set print static-members on +set print vtbl on +set print demangle on +set demangle-style gnu-v3 +set print sevenbit-strings off + diff --git a/debug/stm32wbx.cfg b/scripts/debug/stm32wbx.cfg similarity index 98% rename from debug/stm32wbx.cfg rename to scripts/debug/stm32wbx.cfg index f100c3ccda1..ba383831bd4 100644 --- a/debug/stm32wbx.cfg +++ b/scripts/debug/stm32wbx.cfg @@ -101,3 +101,7 @@ $_TARGETNAME configure -event trace-config { # assignment mmw 0xE0042004 0x00000020 0 } + +$_TARGETNAME configure -event gdb-detach { + resume +} \ No newline at end of file diff --git a/scripts/distfap.py b/scripts/distfap.py new file mode 100755 index 00000000000..b1c5587906b --- /dev/null +++ b/scripts/distfap.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 + +import os +import posixpath + +from flipper.app import App +from flipper.storage import FlipperStorage, FlipperStorageOperations +from flipper.utils.cdc import resolve_port + + +class Main(App): + def init(self): + self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") + self.parser.add_argument( + "-n", + "--no-launch", + dest="launch_app", + action="store_false", + help="Don't launch app", + ) + + self.parser.add_argument("fap_src_path", help="App file to upload") + self.parser.add_argument( + "--fap_dst_dir", help="Upload path", default="/ext/apps", required=False + ) + self.parser.set_defaults(func=self.install) + + def install(self): + if not (port := resolve_port(self.logger, self.args.port)): + return 1 + + try: + with FlipperStorage(port) as storage: + storage_ops = FlipperStorageOperations(storage) + fap_local_path = self.args.fap_src_path + self.args.fap_dst_dir = self.args.fap_dst_dir.rstrip("/\\") + + if not os.path.isfile(fap_local_path): + self.logger.error( + f"Error: source .fap ({fap_local_path}) not found" + ) + return 2 + + fap_dst_path = posixpath.join( + self.args.fap_dst_dir, os.path.basename(fap_local_path) + ) + + self.logger.info(f'Installing "{fap_local_path}" to {fap_dst_path}') + + storage_ops.recursive_send(fap_dst_path, fap_local_path, False) + + if not self.args.launch_app: + return 0 + + storage.send_and_wait_eol(f"loader open {fap_dst_path}\r") + + if len(result := storage.read.until(storage.CLI_EOL)): + self.logger.error(f"Unexpected response: {result.decode('ascii')}") + return 3 + return 0 + + except Exception as e: + self.logger.error(f"Error: {e}") + # raise + return 4 + + +if __name__ == "__main__": + Main()() diff --git a/scripts/fastfap.py b/scripts/fastfap.py new file mode 100755 index 00000000000..95e32c37be9 --- /dev/null +++ b/scripts/fastfap.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 +import hashlib +import os +import struct +import subprocess +import tempfile +from collections import defaultdict +from dataclasses import dataclass + +from elftools.elf.elffile import ELFFile +from elftools.elf.relocation import RelocationSection +from elftools.elf.sections import SymbolTableSection +from fbt.sdk.hashes import gnu_sym_hash +from flipper.app import App + +VERSION = 1 + + +@dataclass +class RelData: + section: int + section_value: int + type: int + offset: int + name: str + + +@dataclass(frozen=True) +class UniqueRelData: + section: int + section_value: int + type: int + name: str + + +@dataclass +class RelSection: + name: str + oringinal_name: str + data: dict[UniqueRelData, list[int]] + + +def serialize_relsection_data(data: dict[UniqueRelData, list[int]]) -> bytes: + result = struct.pack(" 0: + result += struct.pack("> 8) & 0xFF, (offset >> 16) & 0xFF + ) + + return result + + +class Main(App): + def init(self): + self.parser.add_argument("fap_src_path", help="App file to upload") + self.parser.add_argument("objcopy_path", help="Objcopy path") + self.parser.set_defaults(func=self.process) + + def process(self): + fap_path = self.args.fap_src_path + objcopy_path = self.args.objcopy_path + + sections: list[RelSection] = [] + + with open(fap_path, "rb") as f: + elf_file = ELFFile(f) + + relocation_sections: list[RelocationSection] = [] + symtab_section: SymbolTableSection | None = None + + for section in elf_file.iter_sections(): + if isinstance(section, RelocationSection): + relocation_sections.append(section) + + if isinstance(section, SymbolTableSection): + symtab_section = section + + if not symtab_section: + self.logger.error("No symbol table found") + return 1 + + if not relocation_sections: + self.logger.info("No relocation sections found") + return 0 + + for section in relocation_sections: + section_relocations: list[RelData] = [] + + for relocation in section.iter_relocations(): + symbol_id: int = relocation.entry["r_info_sym"] + offset: int = relocation.entry["r_offset"] + type: int = relocation.entry["r_info_type"] + symbol = symtab_section.get_symbol(symbol_id) + section_index: int = symbol["st_shndx"] + section_value: int = symbol["st_value"] + if section_index == "SHN_UNDEF": + section_index = 0 + + section_relocations.append( + RelData(section_index, section_value, type, offset, symbol.name) + ) + + unique_relocations: dict[UniqueRelData, list[int]] = defaultdict(list) + for relocation in section_relocations: + unique = UniqueRelData( + relocation.section, + relocation.section_value, + relocation.type, + relocation.name, + ) + + unique_relocations[unique].append(relocation.offset) + + section_name = section.name + if section_name.startswith(".rel"): + section_name = ".fast.rel" + section_name[4:] + else: + self.logger.error( + "Unknown relocation section name: %s", section_name + ) + return 1 + + sections.append( + RelSection(section_name, section.name, unique_relocations) + ) + + with tempfile.TemporaryDirectory() as temp_dir: + for section in sections: + data = serialize_relsection_data(section.data) + hash_name = hashlib.md5(section.name.encode()).hexdigest() + filename = f"{temp_dir}/{hash_name}.bin" + + if os.path.isfile(filename): + self.logger.error(f"File {filename} already exists") + return 1 + + with open(filename, "wb") as f: + f.write(data) + + exit_code = subprocess.run( + [ + objcopy_path, + "--add-section", + f"{section.name}={filename}", + fap_path, + ], + check=True, + ) + + if exit_code.returncode != 0: + self.logger.error("objcopy failed") + return 1 + + return 0 + + +if __name__ == "__main__": + Main()() diff --git a/site_scons/fbt/__init__.py b/scripts/fbt/__init__.py similarity index 100% rename from site_scons/fbt/__init__.py rename to scripts/fbt/__init__.py diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py new file mode 100644 index 00000000000..d32869b106f --- /dev/null +++ b/scripts/fbt/appmanifest.py @@ -0,0 +1,454 @@ +import os +import re +from dataclasses import dataclass, field +from enum import Enum +from typing import Callable, ClassVar, List, Optional, Tuple, Union + +try: + from fbt.util import resolve_real_dir_node +except ImportError: + # When running outside of SCons, we don't have access to SCons.Node + def resolve_real_dir_node(node): + return node + + +class FlipperManifestException(Exception): + pass + + +class FlipperAppType(Enum): + SERVICE = "Service" + SYSTEM = "System" + APP = "App" + DEBUG = "Debug" + ARCHIVE = "Archive" + SETTINGS = "Settings" + STARTUP = "StartupHook" + EXTERNAL = "External" + MENUEXTERNAL = "MenuExternal" + METAPACKAGE = "Package" + PLUGIN = "Plugin" + + +@dataclass +class FlipperApplication: + APP_ID_REGEX: ClassVar[re.Pattern] = re.compile(r"^[a-z0-9_]+$") + PRIVATE_FIELD_PREFIX: ClassVar[str] = "_" + APP_MANIFEST_DEFAULT_NAME: ClassVar[str] = "application.fam" + + @dataclass + class ExternallyBuiltFile: + path: str + command: str + + @dataclass + class Library: + name: str + fap_include_paths: List[str] = field(default_factory=lambda: ["."]) + sources: List[str] = field(default_factory=lambda: ["*.c*"]) + cflags: List[str] = field(default_factory=list) + cdefines: List[str] = field(default_factory=list) + cincludes: List[str] = field(default_factory=list) + + appid: str + apptype: FlipperAppType + name: Optional[str] = "" + entry_point: Optional[str] = None + flags: List[str] = field(default_factory=lambda: ["Default"]) + cdefines: List[str] = field(default_factory=list) + requires: List[str] = field(default_factory=list) + conflicts: List[str] = field(default_factory=list) + provides: List[str] = field(default_factory=list) + stack_size: int = 2048 + icon: Optional[str] = None + order: int = 0 + sdk_headers: List[str] = field(default_factory=list) + targets: List[str] = field(default_factory=lambda: ["all"]) + resources: Optional[str] = None + + # .fap-specific + sources: List[str] = field(default_factory=lambda: ["*.c*"]) + fap_version: Union[str, Tuple[int]] = "0.1" + fap_icon: Optional[str] = None + fap_libs: List[str] = field(default_factory=list) + fap_category: str = "" + fap_description: str = "" + fap_author: str = "" + fap_weburl: str = "" + fap_icon_assets: Optional[str] = None + fap_icon_assets_symbol: Optional[str] = None + fap_extbuild: List[ExternallyBuiltFile] = field(default_factory=list) + fap_private_libs: List[Library] = field(default_factory=list) + fap_file_assets: Optional[str] = None + fal_embedded: bool = False + # Internally used by fbt + _appmanager: Optional["AppManager"] = None + _appdir: Optional[object] = None + _apppath: Optional[str] = None + _plugins: List["FlipperApplication"] = field(default_factory=list) + _assets_dirs: List[object] = field(default_factory=list) + _section_fapmeta: Optional[object] = None + _section_fapfileassets: Optional[object] = None + + @property + def embeds_plugins(self): + return any(plugin.fal_embedded for plugin in self._plugins) + + def supports_hardware_target(self, target: str): + return target in self.targets or "all" in self.targets + + @property + def is_default_deployable(self): + return self.apptype != FlipperAppType.DEBUG and self.fap_category != "Examples" + + @property + def do_strict_import_checks(self): + return self.apptype != FlipperAppType.PLUGIN + + def __post_init__(self): + if self.apptype == FlipperAppType.PLUGIN: + self.stack_size = 0 + if not self.APP_ID_REGEX.match(self.appid): + raise FlipperManifestException( + f"Invalid appid '{self.appid}'. Must match regex '{self.APP_ID_REGEX}'" + ) + if isinstance(self.fap_version, str): + try: + self.fap_version = tuple(int(v) for v in self.fap_version.split(".")) + except ValueError: + raise FlipperManifestException( + f"Invalid version '{self.fap_version}'. Must be in the form 'major.minor'" + ) + if len(self.fap_version) < 2: + raise ValueError("Not enough version components") + + +class AppManager: + def __init__(self): + self.known_apps = {} + + def get(self, appname: str): + try: + return self.known_apps[appname] + except KeyError: + raise FlipperManifestException( + f"Missing application manifest for '{appname}'" + ) + + def find_by_appdir(self, appdir: str): + for app in self.known_apps.values(): + if app._appdir.name == appdir: + return app + return None + + def _validate_app_params(self, *args, **kw): + apptype = kw.get("apptype") + if apptype == FlipperAppType.PLUGIN: + if kw.get("stack_size"): + raise FlipperManifestException( + f"Plugin {kw.get('appid')} cannot have stack (did you mean FlipperAppType.EXTERNAL?)" + ) + if not kw.get("requires"): + raise FlipperManifestException( + f"Plugin {kw.get('appid')} must have 'requires' in manifest" + ) + else: + if kw.get("fal_embedded"): + raise FlipperManifestException( + f"App {kw.get('appid')} cannot have fal_embedded set" + ) + + if apptype in AppBuildset.dist_app_types: + # For distributing .fap's resources, there's "fap_file_assets" + for app_property in ("resources",): + if kw.get(app_property): + raise FlipperManifestException( + f"App {kw.get('appid')} of type {apptype} cannot have '{app_property}' in manifest" + ) + else: + for app_property in ("fap_extbuild", "fap_private_libs", "fap_icon_assets"): + if kw.get(app_property): + raise FlipperManifestException( + f"App {kw.get('appid')} of type {apptype} must not have '{app_property}' in manifest" + ) + + def load_manifest(self, app_manifest_path: str, app_dir_node: object): + if not os.path.exists(app_manifest_path): + raise FlipperManifestException( + f"App manifest not found at path {app_manifest_path}" + ) + # print("Loading", app_manifest_path) + + app_manifests = [] + + def App(*args, **kw): + nonlocal app_manifests + self._validate_app_params(*args, **kw) + app_manifests.append( + FlipperApplication( + *args, + **kw, + _appdir=resolve_real_dir_node(app_dir_node), + _apppath=os.path.dirname(app_manifest_path), + _appmanager=self, + ), + ) + + def ExtFile(*args, **kw): + return FlipperApplication.ExternallyBuiltFile(*args, **kw) + + def Lib(*args, **kw): + return FlipperApplication.Library(*args, **kw) + + try: + with open(app_manifest_path, "rt") as manifest_file: + exec(manifest_file.read()) + except Exception as e: + raise FlipperManifestException( + f"Failed parsing manifest '{app_manifest_path}' : {e}" + ) + + if len(app_manifests) == 0: + raise FlipperManifestException( + f"App manifest '{app_manifest_path}' is malformed" + ) + + # print("Built", app_manifests) + for app in app_manifests: + self._add_known_app(app) + + def _add_known_app(self, app: FlipperApplication): + if self.known_apps.get(app.appid, None): + raise FlipperManifestException(f"Duplicate app declaration: {app.appid}") + self.known_apps[app.appid] = app + + def filter_apps( + self, + *, + applist: List[str], + ext_applist: List[str], + hw_target: str, + ): + return AppBuildset( + self, + hw_target=hw_target, + appnames=applist, + extra_ext_appnames=ext_applist, + ) + + +class AppBuilderException(Exception): + pass + + +class AppBuildset: + BUILTIN_APP_TYPES = ( + FlipperAppType.SERVICE, + FlipperAppType.SYSTEM, + FlipperAppType.APP, + FlipperAppType.DEBUG, + FlipperAppType.ARCHIVE, + FlipperAppType.SETTINGS, + FlipperAppType.STARTUP, + ) + EXTERNAL_APP_TYPES_MAP = { + # AppType -> bool: true if always deploy, false if obey app set + FlipperAppType.EXTERNAL: True, + FlipperAppType.PLUGIN: True, + FlipperAppType.DEBUG: True, + FlipperAppType.MENUEXTERNAL: False, + } + + @classmethod + @property + def dist_app_types(cls): + """Applications that are installed on SD card""" + return list( + entry[0] for entry in cls.EXTERNAL_APP_TYPES_MAP.items() if entry[1] + ) + + @staticmethod + def print_writer(message): + print(message) + + def __init__( + self, + appmgr: AppManager, + hw_target: str, + appnames: List[str], + *, + extra_ext_appnames: List[str], + message_writer: Callable | None = None, + ): + self.appmgr = appmgr + self.appnames = set(appnames) + self.incompatible_extapps, self.extapps = [], [] + self._extra_ext_appnames = extra_ext_appnames + self._orig_appnames = appnames + self.hw_target = hw_target + self._writer = message_writer if message_writer else self.print_writer + self._process_deps() + self._process_ext_apps() + self._check_conflicts() + self._check_unsatisfied() # unneeded? + self._check_target_match() + self._group_plugins() + self._apps = sorted( + list(map(self.appmgr.get, self.appnames)), + key=lambda app: app.appid, + ) + + @property + def apps(self): + return list(self._apps) + + def _is_missing_dep(self, dep_name: str): + return dep_name not in self.appnames + + def _check_if_app_target_supported(self, app_name: str): + return self.appmgr.get(app_name).supports_hardware_target(self.hw_target) + + def _get_app_depends(self, app_name: str) -> List[str]: + app_def = self.appmgr.get(app_name) + # Skip app if its target is not supported by the target we are building for + if not self._check_if_app_target_supported(app_name): + self._writer( + f"Skipping {app_name} due to target mismatch (building for {self.hw_target}, app supports {app_def.targets}" + ) + return [] + + return list( + filter( + self._check_if_app_target_supported, + filter(self._is_missing_dep, app_def.provides + app_def.requires), + ) + ) + + def _process_deps(self): + while True: + provided = [] + for app_name in self.appnames: + provided.extend(self._get_app_depends(app_name)) + + # print("provides round: ", provided) + if len(provided) == 0: + break + self.appnames.update(provided) + + def _process_ext_apps(self): + extapps = [ + app + for (apptype, global_lookup) in self.EXTERNAL_APP_TYPES_MAP.items() + for app in self.get_apps_of_type(apptype, global_lookup) + ] + extapps.extend(map(self.appmgr.get, self._extra_ext_appnames)) + + for app in extapps: + ( + self.extapps + if app.supports_hardware_target(self.hw_target) + else self.incompatible_extapps + ).append(app) + + def get_ext_apps(self): + return self.extapps + + def get_incompatible_ext_apps(self): + return self.incompatible_extapps + + def _check_conflicts(self): + conflicts = [] + for app in self.appnames: + if conflict_app_name := list( + filter( + lambda dep_name: dep_name in self.appnames, + self.appmgr.get(app).conflicts, + ) + ): + conflicts.append((app, conflict_app_name)) + + if len(conflicts): + raise AppBuilderException( + f"App conflicts for {', '.join(f'{conflict_dep[0]}: {conflict_dep[1]}' for conflict_dep in conflicts)}" + ) + + def _check_unsatisfied(self): + unsatisfied = [] + for app in self.appnames: + if missing_dep := list( + filter(self._is_missing_dep, self.appmgr.get(app).requires) + ): + unsatisfied.append((app, missing_dep)) + + if len(unsatisfied): + raise AppBuilderException( + f"Unsatisfied dependencies for {', '.join(f'{missing_dep[0]}: {missing_dep[1]}' for missing_dep in unsatisfied)}" + ) + + def _check_target_match(self): + incompatible = [] + for app in self.appnames: + if not self.appmgr.get(app).supports_hardware_target(self.hw_target): + incompatible.append(app) + + if len(incompatible): + raise AppBuilderException( + f"Apps incompatible with target {self.hw_target}: {', '.join(incompatible)}" + ) + + def _group_plugins(self): + known_extensions = self.get_apps_of_type(FlipperAppType.PLUGIN, all_known=True) + for extension_app in known_extensions: + for parent_app_id in extension_app.requires: + try: + parent_app = self.appmgr.get(parent_app_id) + parent_app._plugins.append(extension_app) + except FlipperManifestException: + self._writer( + f"Module {extension_app.appid} has unknown parent {parent_app_id}" + ) + + def get_apps_cdefs(self): + cdefs = set() + for app in self._apps: + cdefs.update(app.cdefines) + return sorted(list(cdefs)) + + def get_sdk_headers(self): + sdk_headers = [] + for app in self._apps: + sdk_headers.extend( + [ + src._appdir.File(header) + for src in [app, *app._plugins] + for header in src.sdk_headers + ] + ) + return sdk_headers + + def get_apps_of_type(self, apptype: FlipperAppType, all_known: bool = False): + """Looks up apps of given type in current app set. If all_known is true, + ignores app set and checks all loaded apps' manifests.""" + return sorted( + filter( + lambda app: app.apptype == apptype, + self.appmgr.known_apps.values() + if all_known + else map(self.appmgr.get, self.appnames), + ), + key=lambda app: app.order, + ) + + def get_builtin_apps(self): + return list( + filter(lambda app: app.apptype in self.BUILTIN_APP_TYPES, self._apps) + ) + + def get_builtin_app_folders(self): + return sorted( + set( + (app._appdir, source_type) + for app in self.get_builtin_apps() + for source_type in app.sources + ) + ) diff --git a/scripts/fbt/elfmanifest.py b/scripts/fbt/elfmanifest.py new file mode 100644 index 00000000000..333888e140d --- /dev/null +++ b/scripts/fbt/elfmanifest.py @@ -0,0 +1,82 @@ +import os +import struct +from dataclasses import dataclass, field + +from flipper.assets.icon import file2image + +from .appmanifest import FlipperApplication + +_MANIFEST_MAGIC = 0x52474448 + + +@dataclass +class ElfManifestBaseHeader: + manifest_version: int + api_version: int + hardware_target_id: int + + manifest_magic: int = 0x52474448 + + def as_bytes(self): + return struct.pack( + " 32: + raise ValueError( + f"Flipper app icon must be 32 bytes or less, but {len(image.data)} bytes were given" + ) + image_data = image.data + + app_version_as_int = ((app_manifest.fap_version[0] & 0xFFFF) << 16) | ( + app_manifest.fap_version[1] & 0xFFFF + ) + + data = ElfManifestBaseHeader( + manifest_version=1, + api_version=sdk_version, + hardware_target_id=hardware_target, + ).as_bytes() + data += ElfManifestV1( + stack_size=app_manifest.stack_size, + app_version=app_version_as_int, + name=app_manifest.name, + icon=image_data, + ).as_bytes() + + return data diff --git a/scripts/fbt/fapassets.py b/scripts/fbt/fapassets.py new file mode 100644 index 00000000000..cd65ad20fb0 --- /dev/null +++ b/scripts/fbt/fapassets.py @@ -0,0 +1,112 @@ +import hashlib +import os +import struct +from typing import TypedDict, List + + +class File(TypedDict): + path: str + size: int + content_path: str + + +class Dir(TypedDict): + path: str + + +class FileBundler: + """ + u32 magic + u32 version + u32 dirs_count + u32 files_count + u32 signature_size + u8[] signature + Dirs: + u32 dir_name length + u8[] dir_name + Files: + u32 file_name length + u8[] file_name + u32 file_content_size + u8[] file_content + """ + + def __init__(self, assets_dirs: List[object]): + self.src_dirs = list(assets_dirs) + + def _gather(self, directory_path: str): + if not os.path.isdir(directory_path): + raise Exception(f"Assets directory {directory_path} does not exist") + for root, dirs, files in os.walk(directory_path): + for file_info in files: + file_path = os.path.join(root, file_info) + file_size = os.path.getsize(file_path) + self.file_list.append( + { + "path": os.path.relpath(file_path, directory_path), + "size": file_size, + "content_path": file_path, + } + ) + + for dir_info in dirs: + dir_path = os.path.join(root, dir_info) + # dir_size = sum( + # os.path.getsize(os.path.join(dir_path, f)) for f in os.listdir(dir_path) + # ) + self.directory_list.append( + {"path": os.path.relpath(dir_path, directory_path)} + ) + + self.file_list.sort(key=lambda f: f["path"]) + self.directory_list.sort(key=lambda d: d["path"]) + + def _process_src_dirs(self): + self.file_list: list[File] = [] + self.directory_list: list[Dir] = [] + for directory_path in self.src_dirs: + self._gather(directory_path) + + def export(self, target_path: str): + self._process_src_dirs() + self._md5_hash = hashlib.md5() + with open(target_path, "wb") as f: + # Write header magic and version + f.write(struct.pack(" str: + return f"{self.major}.{self.minor}" + + def as_int(self) -> int: + return ((self.major & 0xFFFF) << 16) | (self.minor & 0xFFFF) + + @staticmethod + def from_str(s: str) -> "SdkVersion": + major, minor = s.split(".") + return SdkVersion(int(major), int(minor)) + + def dictify(self) -> dict: + return dict(name=str(self), type=None, params=None) + + +class VersionBump(Enum): + NONE = auto() + MAJOR = auto() + MINOR = auto() + + +class ApiEntryState(Enum): + PENDING = "?" + APPROVED = "+" + DISABLED = "-" + # Special value for API version entry so users have less incentive to edit it + VERSION_PENDING = "v" + + +# Class that stores all known API entries, both enabled and disabled. +# Also keeps track of API versioning +# Allows comparison and update from newly-generated API +class SdkCache: + CSV_FIELD_NAMES = ("entry", "status", "name", "type", "params") + + def __init__(self, cache_file: str, load_version_only=False): + self.cache_file_name = cache_file + self.version = SdkVersion(0, 0) + self.sdk = ApiEntries() + self.disabled_entries = set() + self.new_entries = set() + self.loaded_dirty_version = False + + self.version_action = VersionBump.NONE + self._load_version_only = load_version_only + self.load_cache() + + def is_buildable(self) -> bool: + return ( + self.version != SdkVersion(0, 0) + and self.version_action == VersionBump.NONE + and not self._have_pending_entries() + ) + + def _filter_enabled(self, sdk_entries): + return sorted( + filter(lambda e: e not in self.disabled_entries, sdk_entries), + key=operator.attrgetter("name"), + ) + + def get_valid_names(self): + syms = set(map(lambda e: e.name, self.get_functions())) + syms.update(map(lambda e: e.name, self.get_variables())) + return syms + + def get_disabled_names(self): + return set(map(lambda e: e.name, self.disabled_entries)) + + def get_functions(self): + return self._filter_enabled(self.sdk.functions) + + def get_variables(self): + return self._filter_enabled(self.sdk.variables) + + def get_headers(self): + return self._filter_enabled(self.sdk.headers) + + def _get_entry_status(self, entry) -> str: + if entry in self.disabled_entries: + return ApiEntryState.DISABLED + elif entry in self.new_entries: + if isinstance(entry, SdkVersion): + return ApiEntryState.VERSION_PENDING + return ApiEntryState.PENDING + else: + return ApiEntryState.APPROVED + + def _format_entry(self, obj): + obj_dict = obj.dictify() + obj_dict.update( + dict( + entry=obj.csv_type, + status=self._get_entry_status(obj).value, + ) + ) + return obj_dict + + def save(self) -> None: + if self._load_version_only: + raise Exception("Only SDK version was loaded, cannot save") + + if self.version_action == VersionBump.MINOR: + self.version = SdkVersion(self.version.major, self.version.minor + 1) + elif self.version_action == VersionBump.MAJOR: + self.version = SdkVersion(self.version.major + 1, 0) + + if self._have_pending_entries(): + self.new_entries.add(self.version) + print( + fg.red( + f"API version is still WIP: {self.version}. Review the changes and re-run command." + ) + ) + print("CSV file entries to mark up:") + print( + fg.yellow( + "\n".join( + map( + str, + filter( + lambda e: not isinstance(e, SdkVersion), + self.new_entries, + ), + ) + ) + ) + ) + else: + print(fg.green(f"API version {self.version} is up to date")) + + regenerate_csv = ( + self.loaded_dirty_version + or self._have_pending_entries() + or self.version_action != VersionBump.NONE + ) + + if regenerate_csv: + str_cache_entries = [self.version] + name_getter = operator.attrgetter("name") + str_cache_entries.extend(sorted(self.sdk.headers, key=name_getter)) + str_cache_entries.extend(sorted(self.sdk.functions, key=name_getter)) + str_cache_entries.extend(sorted(self.sdk.variables, key=name_getter)) + + with open(self.cache_file_name, "wt", newline="") as f: + writer = csv.DictWriter(f, fieldnames=SdkCache.CSV_FIELD_NAMES) + writer.writeheader() + + for entry in str_cache_entries: + writer.writerow(self._format_entry(entry)) + + def _process_entry(self, entry_dict: dict) -> None: + entry_class = entry_dict["entry"] + entry_status = entry_dict["status"] + entry_name = entry_dict["name"] + + entry = None + if entry_class == SdkVersion.csv_type: + self.version = SdkVersion.from_str(entry_name) + if entry_status == ApiEntryState.VERSION_PENDING.value: + self.loaded_dirty_version = True + elif entry_class == ApiHeader.csv_type: + self.sdk.headers.add(entry := ApiHeader(entry_name)) + elif entry_class == ApiEntryFunction.csv_type: + self.sdk.functions.add( + entry := ApiEntryFunction( + entry_name, + entry_dict["type"], + entry_dict["params"], + ) + ) + elif entry_class == ApiEntryVariable.csv_type: + self.sdk.variables.add( + entry := ApiEntryVariable(entry_name, entry_dict["type"]) + ) + else: + print(entry_dict) + raise Exception("Unknown entry type: %s" % entry_class) + + if entry is None: + return + + if entry_status == ApiEntryState.DISABLED.value: + self.disabled_entries.add(entry) + elif entry_status == ApiEntryState.PENDING.value: + self.new_entries.add(entry) + + def load_cache(self) -> None: + if not os.path.exists(self.cache_file_name): + raise Exception( + f"Cannot load symbol cache '{self.cache_file_name}'! File does not exist" + ) + + with open(self.cache_file_name, "rt") as f: + reader = csv.DictReader(f) + for row in reader: + self._process_entry(row) + if self._load_version_only and row.get("entry") == SdkVersion.csv_type: + break + + def _have_pending_entries(self) -> bool: + return any( + filter( + lambda e: not isinstance(e, SdkVersion), + self.new_entries, + ) + ) + + def sync_sets( + self, known_set: Set[Any], new_set: Set[Any], update_version: bool = True + ): + new_entries = new_set - known_set + if new_entries: + print(f"New: {new_entries}") + known_set |= new_entries + self.new_entries |= new_entries + if update_version and self.version_action == VersionBump.NONE: + self.version_action = VersionBump.MINOR + removed_entries = known_set - new_set + if removed_entries: + print(f"Removed: {removed_entries}") + self.loaded_dirty_version = True + known_set -= removed_entries + # If any of removed entries was a part of active API, that's a major bump + if update_version and any( + filter( + lambda e: e not in self.disabled_entries + and e not in self.new_entries, + removed_entries, + ) + ): + self.version_action = VersionBump.MAJOR + self.disabled_entries -= removed_entries + self.new_entries -= removed_entries + + def validate_api(self, api: ApiEntries) -> None: + self.sync_sets(self.sdk.headers, api.headers, False) + self.sync_sets(self.sdk.functions, api.functions) + self.sync_sets(self.sdk.variables, api.variables) + + +class LazySdkVersionLoader: + def __init__(self, sdk_path: str): + self.sdk_path = sdk_path + self._version = None + + @property + def version(self) -> SdkVersion: + if self._version is None: + self._version = SdkCache(self.sdk_path, load_version_only=True).version + return self._version + + def __str__(self) -> str: + return str(self.version) diff --git a/scripts/fbt/sdk/collector.py b/scripts/fbt/sdk/collector.py new file mode 100644 index 00000000000..1dd3bc4ebb9 --- /dev/null +++ b/scripts/fbt/sdk/collector.py @@ -0,0 +1,232 @@ +from typing import List +from .hashes import gnu_sym_hash + +from cxxheaderparser.parser import CxxParser +from . import ( + ApiEntries, + ApiEntryFunction, + ApiEntryVariable, + ApiHeader, +) + + +# 'Fixing' complaints about typedefs +CxxParser._fundamentals.discard("wchar_t") + +from cxxheaderparser.types import ( + EnumDecl, + Field, + ForwardDecl, + FriendDecl, + Function, + Method, + Typedef, + UsingAlias, + UsingDecl, + Variable, + Pointer, + Type, + PQName, + NameSpecifier, + FundamentalSpecifier, + Parameter, + Array, + Value, + Token, + FunctionType, +) + +from cxxheaderparser.parserstate import ( + State, + EmptyBlockState, + ClassBlockState, + ExternBlockState, + NamespaceBlockState, +) + + +class SymbolManager: + def __init__(self): + self.api = ApiEntries() + self.name_hashes = set() + + # Calculate hash of name and raise exception if it already is in the set + def _name_check(self, name: str): + name_hash = gnu_sym_hash(name) + if name_hash in self.name_hashes: + raise Exception(f"Hash collision on {name}") + self.name_hashes.add(name_hash) + + def add_function(self, function_def: ApiEntryFunction): + if function_def in self.api.functions: + return + self._name_check(function_def.name) + self.api.functions.add(function_def) + + def add_variable(self, variable_def: ApiEntryVariable): + if variable_def in self.api.variables: + return + self._name_check(variable_def.name) + self.api.variables.add(variable_def) + + def add_header(self, header: str): + self.api.headers.add(ApiHeader(header)) + + +class SdkCollector: + def __init__(self): + self.symbol_manager = SymbolManager() + + def add_header_to_sdk(self, header: str): + self.symbol_manager.add_header(header) + + def process_source_file_for_sdk(self, file_path: str): + visitor = SdkCxxVisitor(self.symbol_manager) + with open(file_path, "rt") as f: + content = f.read() + parser = CxxParser(file_path, content, visitor, None) + parser.parse() + + def get_api(self): + return self.symbol_manager.api + + +def stringify_array_dimension(size_descr): + if not size_descr: + return "" + return stringify_descr(size_descr) + + +def stringify_array_descr(type_descr): + assert isinstance(type_descr, Array) + return ( + stringify_descr(type_descr.array_of), + stringify_array_dimension(type_descr.size), + ) + + +def stringify_descr(type_descr): + if isinstance(type_descr, (NameSpecifier, FundamentalSpecifier)): + return type_descr.name + elif isinstance(type_descr, PQName): + return "::".join(map(stringify_descr, type_descr.segments)) + elif isinstance(type_descr, Pointer): + # Hack + if isinstance(type_descr.ptr_to, FunctionType): + return stringify_descr(type_descr.ptr_to) + return f"{stringify_descr(type_descr.ptr_to)}*" + elif isinstance(type_descr, Type): + return ( + f"{'const ' if type_descr.const else ''}" + f"{'volatile ' if type_descr.volatile else ''}" + f"{stringify_descr(type_descr.typename)}" + ) + elif isinstance(type_descr, Parameter): + return stringify_descr(type_descr.type) + elif isinstance(type_descr, Array): + # Hack for 2d arrays + if isinstance(type_descr.array_of, Array): + argtype, dimension = stringify_array_descr(type_descr.array_of) + return ( + f"{argtype}[{stringify_array_dimension(type_descr.size)}][{dimension}]" + ) + return f"{stringify_descr(type_descr.array_of)}[{stringify_array_dimension(type_descr.size)}]" + elif isinstance(type_descr, Value): + return " ".join(map(stringify_descr, type_descr.tokens)) + elif isinstance(type_descr, FunctionType): + return f"{stringify_descr(type_descr.return_type)} (*)({', '.join(map(stringify_descr, type_descr.parameters))})" + elif isinstance(type_descr, Token): + return type_descr.value + elif type_descr is None: + return "" + else: + raise Exception("unsupported type_descr: %s" % type_descr) + + +class SdkCxxVisitor: + def __init__(self, symbol_manager: SymbolManager): + self.api = symbol_manager + + def on_variable(self, state: State, v: Variable) -> None: + if not v.extern: + return + + self.api.add_variable( + ApiEntryVariable( + stringify_descr(v.name), + stringify_descr(v.type), + ) + ) + + def on_function(self, state: State, fn: Function) -> None: + if fn.inline or fn.has_body: + return + + self.api.add_function( + ApiEntryFunction( + stringify_descr(fn.name), + stringify_descr(fn.return_type), + ", ".join(map(stringify_descr, fn.parameters)) + + (", ..." if fn.vararg else ""), + ) + ) + + def on_define(self, state: State, content: str) -> None: + pass + + def on_pragma(self, state: State, content: str) -> None: + pass + + def on_include(self, state: State, filename: str) -> None: + pass + + def on_empty_block_start(self, state: EmptyBlockState) -> None: + pass + + def on_empty_block_end(self, state: EmptyBlockState) -> None: + pass + + def on_extern_block_start(self, state: ExternBlockState) -> None: + pass + + def on_extern_block_end(self, state: ExternBlockState) -> None: + pass + + def on_namespace_start(self, state: NamespaceBlockState) -> None: + pass + + def on_namespace_end(self, state: NamespaceBlockState) -> None: + pass + + def on_forward_decl(self, state: State, fdecl: ForwardDecl) -> None: + pass + + def on_typedef(self, state: State, typedef: Typedef) -> None: + pass + + def on_using_namespace(self, state: State, namespace: List[str]) -> None: + pass + + def on_using_alias(self, state: State, using: UsingAlias) -> None: + pass + + def on_using_declaration(self, state: State, using: UsingDecl) -> None: + pass + + def on_enum(self, state: State, enum: EnumDecl) -> None: + pass + + def on_class_start(self, state: ClassBlockState) -> None: + pass + + def on_class_field(self, state: State, f: Field) -> None: + pass + + def on_class_method(self, state: ClassBlockState, method: Method) -> None: + pass + + def on_class_friend(self, state: ClassBlockState, friend: FriendDecl) -> None: + pass + + def on_class_end(self, state: ClassBlockState) -> None: + pass diff --git a/scripts/fbt/sdk/hashes.py b/scripts/fbt/sdk/hashes.py new file mode 100644 index 00000000000..fef88ddb5ee --- /dev/null +++ b/scripts/fbt/sdk/hashes.py @@ -0,0 +1,5 @@ +def gnu_sym_hash(name: str) -> int: + h = 0x1505 + for c in name: + h = ((h << 5) + h + ord(c)) & 0xFFFFFFFF + return h diff --git a/scripts/fbt/util.py b/scripts/fbt/util.py new file mode 100644 index 00000000000..fb36ef55ac3 --- /dev/null +++ b/scripts/fbt/util.py @@ -0,0 +1,62 @@ +import os +import re + +import SCons +from SCons.Errors import StopError +from SCons.Subst import quote_spaces + +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) + if SCons.Platform.platform_default() != "win32": + return arg + # GCC requires double Windows slashes, let's use UNIX separator + return WINPATHSEP_RE.sub(r"/\1", arg) + + +def wrap_tempfile(env, command): + env[command] = '${TEMPFILE("' + env[command] + '","$' + command + 'STR")}' + + +def link_dir(target_path, source_path, is_windows): + # print(f"link_dir: {target_path} -> {source_path}") + if os.path.lexists(target_path) or os.path.exists(target_path): + os.unlink(target_path) + if is_windows: + # Crete junction + import _winapi + + if not os.path.isdir(source_path): + raise StopError(f"Source directory {source_path} is not a directory") + + if not os.path.exists(target_path): + _winapi.CreateJunction(source_path, target_path) + else: + os.symlink(source_path, target_path) + + +def single_quote(arg_list): + return " ".join(f"'{arg}'" if " " in arg else str(arg) for arg in arg_list) + + +def resolve_real_dir_node(node): + if isinstance(node, SCons.Node.FS.EntryProxy): + node = node.get() + + for repo_dir in node.get_all_rdirs(): + if os.path.exists(repo_dir.abspath): + return repo_dir + + raise StopError(f"Can't find absolute path for {node.name} ({node})") + + +def path_as_posix(path): + if SCons.Platform.platform_default() == "win32": + return path.replace(os.path.sep, os.path.altsep) + return path diff --git a/site_scons/fbt/version.py b/scripts/fbt/version.py similarity index 77% rename from site_scons/fbt/version.py rename to scripts/fbt/version.py index 4745c7002ca..09f48c8eb8a 100644 --- a/site_scons/fbt/version.py +++ b/scripts/fbt/version.py @@ -1,7 +1,14 @@ -import subprocess import datetime +import subprocess +from functools import cache + + +@cache +def get_git_commit_unix_timestamp(): + return int(subprocess.check_output(["git", "show", "-s", "--format=%ct"])) +@cache def get_fast_git_version_id(): try: version = ( diff --git a/site_scons/site_tools/blackmagic.py b/scripts/fbt_tools/blackmagic.py similarity index 97% rename from site_scons/site_tools/blackmagic.py rename to scripts/fbt_tools/blackmagic.py index ec48c15fd08..0b11426834a 100644 --- a/site_scons/site_tools/blackmagic.py +++ b/scripts/fbt_tools/blackmagic.py @@ -63,7 +63,7 @@ def __str__(self): if probe := self.get_serial() or self.get_networked(): return probe - raise Exception("Please specify BLACKMAGIC=...") + raise StopError("Please specify BLACKMAGIC=...") def generate(env): diff --git a/site_scons/site_tools/ccache.py b/scripts/fbt_tools/ccache.py similarity index 90% rename from site_scons/site_tools/ccache.py rename to scripts/fbt_tools/ccache.py index e88886ade37..63577ab781f 100644 --- a/site_scons/site_tools/ccache.py +++ b/scripts/fbt_tools/ccache.py @@ -3,7 +3,7 @@ def exists(): def generate(env): - if ccache := env.WhereIs("ccache"): + if env.WhereIs("ccache"): env["CCACHE"] = "ccache" env["CC_NOCACHE"] = env["CC"] env["CC"] = "$CCACHE $CC_NOCACHE" diff --git a/scripts/fbt_tools/compilation_db.py b/scripts/fbt_tools/compilation_db.py new file mode 100644 index 00000000000..3d5e469f4e0 --- /dev/null +++ b/scripts/fbt_tools/compilation_db.py @@ -0,0 +1,290 @@ +""" +Implements the ability for SCons to emit a compilation database for the MongoDB project. See +http://clang.llvm.org/docs/JSONCompilationDatabase.html for details on what a compilation +database is, and why you might want one. The only user visible entry point here is +'env.CompilationDatabase'. This method takes an optional 'target' to name the file that +should hold the compilation database, otherwise, the file defaults to compile_commands.json, +which is the name that most clang tools search for by default. +""" + +# Copyright 2020 MongoDB Inc. +# +# 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. +# + +import fnmatch +import itertools +import json +from shlex import quote + +import SCons +from SCons.Tool.asm import ASPPSuffixes, ASSuffixes +from SCons.Tool.cc import CSuffixes +from SCons.Tool.cxx import CXXSuffixes + +# TODO: (-nofl) Is there a better way to do this than this global? Right now this exists so that the +# emitter we add can record all of the things it emits, so that the scanner for the top level +# compilation database can access the complete list, and also so that the writer has easy +# access to write all of the files. But it seems clunky. How can the emitter and the scanner +# communicate more gracefully? +__COMPILATION_DB_ENTRIES = [] + +# We cache the tool path lookups to avoid doing them over and over again. +_TOOL_PATH_CACHE = {} + + +# We make no effort to avoid rebuilding the entries. Someday, perhaps we could and even +# integrate with the cache, but there doesn't seem to be much call for it. +class __CompilationDbNode(SCons.Node.Python.Value): + def __init__(self, value): + SCons.Node.Python.Value.__init__(self, value) + self.Decider(changed_since_last_build_node) + + +def changed_since_last_build_node(child, target, prev_ni, node): + """Dummy decider to force always building""" + return True + + +def make_emit_compilation_DB_entry(comstr): + """ + Effectively this creates a lambda function to capture: + * command line + * source + * target + :param comstr: unevaluated command line + :return: an emitter which has captured the above + """ + user_action = SCons.Action.Action(comstr) + + def emit_compilation_db_entry(target, source, env): + """ + This emitter will be added to each c/c++ object build to capture the info needed + for clang tools + :param target: target node(s) + :param source: source node(s) + :param env: Environment for use building this node + :return: target(s), source(s) + """ + + dbtarget = __CompilationDbNode(source) + + entry = env.__COMPILATIONDB_Entry( + target=dbtarget, + source=[], + __COMPILATIONDB_UOUTPUT=target, + __COMPILATIONDB_USOURCE=source, + __COMPILATIONDB_UACTION=user_action, + __COMPILATIONDB_ENV=env, + ) + + # TODO: (-nofl) Technically, these next two lines should not be required: it should be fine to + # cache the entries. However, they don't seem to update properly. Since they are quick + # to re-generate disable caching and sidestep this problem. + env.AlwaysBuild(entry) + env.NoCache(entry) + + __COMPILATION_DB_ENTRIES.append(dbtarget) + + return target, source + + return emit_compilation_db_entry + + +def compilation_db_entry_action(target, source, env, **kw): + """ + Create a dictionary with evaluated command line, target, source + and store that info as an attribute on the target + (Which has been stored in __COMPILATION_DB_ENTRIES array + :param target: target node(s) + :param source: source node(s) + :param env: Environment for use building this node + :param kw: + :return: None + """ + + command = env["__COMPILATIONDB_UACTION"].strfunction( + target=env["__COMPILATIONDB_UOUTPUT"], + source=env["__COMPILATIONDB_USOURCE"], + env=env["__COMPILATIONDB_ENV"], + ) + + # We assume first non-space character is the executable + executable = command.split(" ", 1)[0] + if not (tool_path := _TOOL_PATH_CACHE.get(executable, None)): + tool_path = env.WhereIs(executable) or executable + _TOOL_PATH_CACHE[executable] = tool_path + # If there are spaces in the executable path, we need to quote it + if " " in tool_path: + tool_path = quote(tool_path) + # Replacing the executable with the full path + command = tool_path + command[len(executable) :] + + entry = { + "directory": env.Dir("#").abspath, + "command": command, + "file": env["__COMPILATIONDB_USOURCE"][0], + "output": env["__COMPILATIONDB_UOUTPUT"][0], + } + + target[0].write(entry) + + +def write_compilation_db(target, source, env): + entries = [] + + use_abspath = env["COMPILATIONDB_USE_ABSPATH"] in [True, 1, "True", "true"] + use_path_filter = env.subst("$COMPILATIONDB_PATH_FILTER") + use_srcpath_filter = env.subst("$COMPILATIONDB_SRCPATH_FILTER") + + for s in __COMPILATION_DB_ENTRIES: + entry = s.read() + source_file = entry["file"] + output_file = entry["output"] + + if source_file.rfile().srcnode().exists(): + source_file = source_file.rfile().srcnode() + + if use_abspath: + source_file = source_file.abspath + output_file = output_file.abspath + else: + source_file = source_file.path + output_file = output_file.path + + # print("output_file, path_filter", output_file, use_path_filter) + if use_path_filter and not fnmatch.fnmatch(output_file, use_path_filter): + continue + + if use_srcpath_filter and not fnmatch.fnmatch(source_file, use_srcpath_filter): + continue + + path_entry = { + "directory": entry["directory"], + "command": entry["command"], + "file": source_file, + "output": output_file, + } + + entries.append(path_entry) + + with open(target[0].path, "w") as output_file: + json.dump( + entries, output_file, sort_keys=True, indent=4, separators=(",", ": ") + ) + + +def scan_compilation_db(node, env, path): + return __COMPILATION_DB_ENTRIES + + +def compilation_db_emitter(target, source, env): + """fix up the source/targets""" + + # Someone called env.CompilationDatabase('my_targetname.json') + if not target and len(source) == 1: + target = source + + # Default target name is compilation_db.json + if not target: + target = [ + "compile_commands.json", + ] + + # No source should have been passed. Drop it. + if source: + source = [] + + return target, source + + +def generate(env, **kwargs): + static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + + env.SetDefault( + COMPILATIONDB_COMSTR=kwargs.get( + "COMPILATIONDB_COMSTR", "Building compilation database $TARGET" + ), + COMPILATIONDB_USE_ABSPATH=False, + COMPILATIONDB_PATH_FILTER="", + COMPILATIONDB_SRCPATH_FILTER="", + ) + + components_by_suffix = itertools.chain( + itertools.product( + CSuffixes, + [ + (static_obj, SCons.Defaults.StaticObjectEmitter, "$CCCOM"), + (shared_obj, SCons.Defaults.SharedObjectEmitter, "$SHCCCOM"), + ], + ), + itertools.product( + CXXSuffixes, + [ + (static_obj, SCons.Defaults.StaticObjectEmitter, "$CXXCOM"), + (shared_obj, SCons.Defaults.SharedObjectEmitter, "$SHCXXCOM"), + ], + ), + itertools.product( + ASSuffixes, + [(static_obj, SCons.Defaults.StaticObjectEmitter, "$ASCOM")], + [(shared_obj, SCons.Defaults.SharedObjectEmitter, "$ASCOM")], + ), + itertools.product( + ASPPSuffixes, + [(static_obj, SCons.Defaults.StaticObjectEmitter, "$ASPPCOM")], + [(shared_obj, SCons.Defaults.SharedObjectEmitter, "$ASPPCOM")], + ), + ) + + for entry in components_by_suffix: + suffix = entry[0] + builder, base_emitter, command = entry[1] + if emitter := builder.emitter.get(suffix, False): + # We may not have tools installed which initialize all or any of + # cxx, cc, or assembly. If not skip resetting the respective emitter. + builder.emitter[suffix] = SCons.Builder.ListEmitter( + [ + emitter, + make_emit_compilation_DB_entry(command), + ] + ) + + env.Append( + BUILDERS={ + "__COMPILATIONDB_Entry": SCons.Builder.Builder( + action=SCons.Action.Action(compilation_db_entry_action, None), + ), + "CompilationDatabase": SCons.Builder.Builder( + action=SCons.Action.Action( + write_compilation_db, "$COMPILATIONDB_COMSTR" + ), + target_scanner=SCons.Scanner.Scanner( + function=scan_compilation_db, node_class=None + ), + emitter=compilation_db_emitter, + suffix="json", + ), + } + ) + + +def exists(env): + return True diff --git a/scripts/fbt_tools/crosscc.py b/scripts/fbt_tools/crosscc.py new file mode 100644 index 00000000000..4890424e6aa --- /dev/null +++ b/scripts/fbt_tools/crosscc.py @@ -0,0 +1,89 @@ +import subprocess + +import gdb +import objdump +import strip +from SCons.Action import _subproc +from SCons.Errors import StopError +from SCons.Tool import ar, asm, gcc, gnulink, gxx + + +def prefix_commands(env, command_prefix, cmd_list): + for command in cmd_list: + if command in env: + prefixed_binary = command_prefix + env[command] + if not env.WhereIs(prefixed_binary): + raise StopError( + f"Toolchain binary {prefixed_binary} not found in PATH." + ) + env.Replace(**{command: prefixed_binary}) + + +def _get_tool_version(env, tool): + verstr = "version unknown" + proc = _subproc( + env, + [env.subst("${%s}" % tool), "--version"], + stdout=subprocess.PIPE, + stderr="devnull", + stdin="devnull", + universal_newlines=True, + error="raise", + shell=False, + ) + if proc: + verstr = proc.stdout.readline() + proc.communicate() + return verstr + + +def generate(env, **kw): + if not env.get("VERBOSE", False): + env.SetDefault( + CCCOMSTR="\tCC\t${SOURCE}", + CXXCOMSTR="\tCPP\t${SOURCE}", + ASCOMSTR="\tASM\t${SOURCE}", + ARCOMSTR="\tAR\t${TARGET}", + RANLIBCOMSTR="\tRANLIB\t${TARGET}", + LINKCOMSTR="\tLINK\t${TARGET}", + INSTALLSTR="\tINSTALL\t${TARGET}", + APPSCOMSTR="\tAPPS\t${TARGET}", + VERSIONCOMSTR="\tVERSION\t${TARGET}", + STRIPCOMSTR="\tSTRIP\t${TARGET}", + OBJDUMPCOMSTR="\tOBJDUMP\t${TARGET}", + ) + + for orig_tool in (asm, gcc, gxx, ar, gnulink, strip, gdb, objdump): + orig_tool.generate(env) + env.SetDefault( + TOOLCHAIN_PREFIX=kw.get("toolchain_prefix"), + ) + prefix_commands( + env, + env.subst("$TOOLCHAIN_PREFIX"), + [ + "AR", + "AS", + "CC", + "CXX", + "OBJCOPY", + "RANLIB", + "STRIP", + "GDB", + "GDBPY", + "OBJDUMP", + ], + ) + # Call CC to check version + if whitelisted_versions := kw.get("versions", ()): + cc_version = _get_tool_version(env, "CC") + # print("CC version =", cc_version) + # print(list(filter(lambda v: v in cc_version, whitelisted_versions))) + if not any(filter(lambda v: v in cc_version, whitelisted_versions)): + raise StopError( + f"Toolchain version is not supported. Allowed: {whitelisted_versions}, toolchain: {cc_version} " + ) + + +def exists(env): + return True diff --git a/scripts/fbt_tools/fbt_apps.py b/scripts/fbt_tools/fbt_apps.py new file mode 100644 index 00000000000..7e0aec5ea60 --- /dev/null +++ b/scripts/fbt_tools/fbt_apps.py @@ -0,0 +1,198 @@ +from ansi.color import fg +from fbt.appmanifest import ( + AppManager, + AppBuildset, + FlipperApplication, + FlipperAppType, + FlipperManifestException, +) +from SCons.Action import Action +from SCons.Builder import Builder +from SCons.Errors import StopError +from SCons.Script import GetOption +from SCons.Warnings import WarningOnByDefault, warn + +# Adding objects for application management to env +# AppManager env["APPMGR"] - loads all manifests; manages list of known apps +# AppBuildset env["APPBUILD"] - contains subset of apps, filtered for current config + + +class ApplicationsCGenerator: + APP_TYPE_MAP = { + FlipperAppType.SERVICE: ("FlipperInternalApplication", "FLIPPER_SERVICES"), + FlipperAppType.SYSTEM: ("FlipperInternalApplication", "FLIPPER_SYSTEM_APPS"), + FlipperAppType.APP: ("FlipperInternalApplication", "FLIPPER_APPS"), + FlipperAppType.DEBUG: ("FlipperInternalApplication", "FLIPPER_DEBUG_APPS"), + FlipperAppType.SETTINGS: ( + "FlipperInternalApplication", + "FLIPPER_SETTINGS_APPS", + ), + FlipperAppType.STARTUP: ( + "FlipperInternalOnStartHook", + "FLIPPER_ON_SYSTEM_START", + ), + } + + APP_EXTERNAL_TYPE = ( + "FlipperExternalApplication", + "FLIPPER_EXTERNAL_APPS", + ) + + def __init__(self, buildset: AppBuildset, autorun_app: str = ""): + self.buildset = buildset + self.autorun = autorun_app + + def get_app_ep_forward(self, app: FlipperApplication): + if app.apptype == FlipperAppType.STARTUP: + return f"extern void {app.entry_point}();" + return f"extern int32_t {app.entry_point}(void* p);" + + def get_app_descr(self, app: FlipperApplication): + if app.apptype == FlipperAppType.STARTUP: + return app.entry_point + 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"}, + .flags = {'|'.join(f"FlipperInternalApplicationFlag{flag}" for flag in app.flags)} }}""" + + def get_external_app_descr(self, app: FlipperApplication): + app_path = "/ext/apps" + if app.fap_category: + app_path += f"/{app.fap_category}" + app_path += f"/{app.appid}.fap" + return f""" + {{ + .name = "{app.name}", + .icon = {f"&{app.icon}" if app.icon else "NULL"}, + .path = "{app_path}" }}""" + + def generate(self): + contents = [ + '#include "applications.h"', + "#include ", + f'const char* FLIPPER_AUTORUN_APP_NAME = "{self.autorun}";', + ] + for apptype in self.APP_TYPE_MAP: + contents.extend( + map(self.get_app_ep_forward, self.buildset.get_apps_of_type(apptype)) + ) + entry_type, entry_block = self.APP_TYPE_MAP[apptype] + contents.append(f"const {entry_type} {entry_block}[] = {{") + contents.append( + ",\n".join( + map(self.get_app_descr, self.buildset.get_apps_of_type(apptype)) + ) + ) + contents.append("};") + contents.append( + f"const size_t {entry_block}_COUNT = COUNT_OF({entry_block});" + ) + + archive_app = self.buildset.get_apps_of_type(FlipperAppType.ARCHIVE) + if archive_app: + contents.extend( + [ + self.get_app_ep_forward(archive_app[0]), + f"const FlipperInternalApplication FLIPPER_ARCHIVE = {self.get_app_descr(archive_app[0])};", + ] + ) + + entry_type, entry_block = self.APP_EXTERNAL_TYPE + external_apps = self.buildset.get_apps_of_type(FlipperAppType.MENUEXTERNAL) + contents.append(f"const {entry_type} {entry_block}[] = {{") + contents.append(",\n".join(map(self.get_external_app_descr, external_apps))) + contents.append("};") + contents.append(f"const size_t {entry_block}_COUNT = COUNT_OF({entry_block});") + + return "\n".join(contents) + + +def LoadAppManifest(env, entry): + try: + manifest_glob = entry.glob(FlipperApplication.APP_MANIFEST_DEFAULT_NAME) + if len(manifest_glob) == 0: + try: + disk_node = next(filter(lambda d: d.exists(), entry.get_all_rdirs())) + except Exception: + disk_node = entry + + raise FlipperManifestException( + f"App folder '{disk_node.abspath}': missing manifest ({FlipperApplication.APP_MANIFEST_DEFAULT_NAME})" + ) + + app_manifest_file_path = manifest_glob[0].rfile().abspath + env["APPMGR"].load_manifest(app_manifest_file_path, entry) + except FlipperManifestException as e: + if not GetOption("silent"): + warn(WarningOnByDefault, str(e)) + + +def PrepareApplicationsBuild(env): + try: + appbuild = env["APPBUILD"] = env["APPMGR"].filter_apps( + applist=env["APPS"], + ext_applist=env["EXTRA_EXT_APPS"], + hw_target=env.subst("f${TARGET_HW}"), + ) + except Exception as e: + raise StopError(e) + + env.Append( + SDK_HEADERS=appbuild.get_sdk_headers(), + ) + + +def DumpApplicationConfig(target, source, env): + print(f"Loaded {len(env['APPMGR'].known_apps)} app definitions.") + print(fg.boldgreen("Firmware modules configuration:")) + for apptype in FlipperAppType: + app_sublist = env["APPBUILD"].get_apps_of_type(apptype) + if app_sublist: + print( + fg.green(f"{apptype.value}:\n\t"), + ", ".join(app.appid for app in app_sublist), + ) + if incompatible_ext_apps := env["APPBUILD"].get_incompatible_ext_apps(): + print( + fg.blue("Incompatible apps (skipped):\n\t"), + ", ".join(app.appid for app in incompatible_ext_apps), + ) + + +def build_apps_c(target, source, env): + target_file_name = target[0].path + + gen = ApplicationsCGenerator(env["APPBUILD"], env.subst("$LOADER_AUTOSTART")) + with open(target_file_name, "w") as file: + file.write(gen.generate()) + + +def generate(env): + env.AddMethod(LoadAppManifest) + env.AddMethod(PrepareApplicationsBuild) + env.SetDefault( + APPMGR=AppManager(), + APPBUILD_DUMP=env.Action( + DumpApplicationConfig, + "\tINFO\t", + ), + ) + + env.Append( + BUILDERS={ + "ApplicationsC": Builder( + action=Action( + build_apps_c, + "${APPSCOMSTR}", + ), + suffix=".c", + ), + } + ) + + +def exists(env): + return True diff --git a/scripts/fbt_tools/fbt_assets.py b/scripts/fbt_tools/fbt_assets.py new file mode 100644 index 00000000000..492a66b6654 --- /dev/null +++ b/scripts/fbt_tools/fbt_assets.py @@ -0,0 +1,228 @@ +import os +import subprocess + +from ansi.color import fg +from SCons.Action import Action +from SCons.Builder import Builder +from SCons.Errors import StopError +from SCons.Node.FS import File + + +def _icons_emitter(target, source, env): + icons_src = env.GlobRecursive("*.png", env["ICON_SRC_DIR"]) + icons_src += env.GlobRecursive("**/frame_rate", env["ICON_SRC_DIR"]) + + target = [ + target[0].File(env.subst("${ICON_FILE_NAME}.c")), + target[0].File(env.subst("${ICON_FILE_NAME}.h")), + ] + return target, icons_src + + +def _proto_emitter(target, source, env): + target = [] + for src in source: + basename = os.path.splitext(src.name)[0] + target.append(env.File(f"compiled/{basename}.pb.c")) + target.append(env.File(f"compiled/{basename}.pb.h")) + return target, source + + +def _dolphin_emitter(target, source, env): + res_root_dir = source[0].Dir(env["DOLPHIN_RES_TYPE"]) + source = list() + source.extend(env.GlobRecursive("*.*", res_root_dir.srcnode())) + + target_base_dir = target[0] + env.Replace(_DOLPHIN_OUT_DIR=target[0]) + env.Replace(_DOLPHIN_SRC_DIR=res_root_dir) + + if env["DOLPHIN_RES_TYPE"] == "external": + target = [target_base_dir.File("manifest.txt")] + ## A detailed list of files to be generated + # Not used ATM, becasuse it inflates the internal dependency graph too much + # Preserve original paths, do .png -> .bm conversion + # target.extend( + # map( + # lambda node: target_base_dir.File( + # res_root_dir.rel_path(node).replace(".png", ".bm") + # ), + # filter(lambda node: isinstance(node, File), source), + # ) + # ) + else: + asset_basename = f"assets_dolphin_{env['DOLPHIN_RES_TYPE']}" + target = [ + target_base_dir.File(asset_basename + ".c"), + target_base_dir.File(asset_basename + ".h"), + ] + + ## Debug output + # print( + # f"Dolphin res type: {env['DOLPHIN_RES_TYPE']},\ntarget files:", + # list(f.path for f in target), + # f"\nsource files:", + # list(f.path for f in source), + # ) + return target, source + + +def __invoke_git(args, source_dir): + cmd = ["git"] + cmd.extend(args) + return ( + subprocess.check_output(cmd, cwd=source_dir, stderr=subprocess.STDOUT) + .strip() + .decode() + ) + + +def _proto_ver_generator(target, source, env): + target_file = target[0] + src_dir = source[0].dir.abspath + + def fetch(unshallow=False): + git_args = ["fetch", "--tags"] + if unshallow: + git_args.append("--unshallow") + + try: + __invoke_git(git_args, source_dir=src_dir) + except (subprocess.CalledProcessError, EnvironmentError): + # Not great, not terrible + print(fg.boldred("Git: fetch failed")) + + def describe(): + try: + return __invoke_git( + ["describe", "--tags", "--abbrev=0"], + source_dir=src_dir, + ) + except (subprocess.CalledProcessError, EnvironmentError): + return None + + fetch() + git_describe = describe() + if not git_describe: + fetch(unshallow=True) + git_describe = describe() + + if not git_describe: + raise StopError("Failed to process git tags for protobuf versioning") + + git_major, git_minor = git_describe.split(".") + version_file_data = ( + "#pragma once", + f"#define PROTOBUF_MAJOR_VERSION {git_major}", + f"#define PROTOBUF_MINOR_VERSION {git_minor}", + "", + ) + with open(str(target_file), "wt") as file: + file.write("\n".join(version_file_data)) + + +def CompileIcons(env, target_dir, source_dir, *, icon_bundle_name="assets_icons"): + return env.IconBuilder( + target_dir, + None, + ICON_SRC_DIR=source_dir, + ICON_FILE_NAME=icon_bundle_name, + ) + + +def generate(env): + env.SetDefault( + ASSETS_COMPILER="${FBT_SCRIPT_DIR}/assets.py", + NANOPB_COMPILER="${ROOT_DIR}/lib/nanopb/generator/nanopb_generator.py", + ) + env.AddMethod(CompileIcons) + + if not env["VERBOSE"]: + env.SetDefault( + ICONSCOMSTR="\tICONS\t${TARGET}", + PROTOCOMSTR="\tPROTO\t${SOURCE}", + DOLPHINCOMSTR="\tDOLPHIN\t${DOLPHIN_RES_TYPE}", + PBVERCOMSTR="\tPBVER\t${TARGET}", + ) + + env.Append( + BUILDERS={ + "IconBuilder": Builder( + action=Action( + [ + [ + "${PYTHON3}", + "${ASSETS_COMPILER}", + "icons", + "${ICON_SRC_DIR}", + "${TARGET.dir}", + "--filename", + "${ICON_FILE_NAME}", + ], + ], + "${ICONSCOMSTR}", + ), + emitter=_icons_emitter, + ), + "ProtoBuilder": Builder( + action=Action( + [ + [ + "${PYTHON3}", + "${NANOPB_COMPILER}", + "-q", + "-I${SOURCE.dir.posix}", + "-D${TARGET.dir.posix}", + "${SOURCES.posix}", + ], + ], + "${PROTOCOMSTR}", + ), + emitter=_proto_emitter, + suffix=".pb.c", + src_suffix=".proto", + ), + "DolphinSymBuilder": Builder( + action=Action( + [ + [ + "${PYTHON3}", + "${ASSETS_COMPILER}", + "dolphin", + "-s", + "dolphin_${DOLPHIN_RES_TYPE}", + "${_DOLPHIN_SRC_DIR}", + "${_DOLPHIN_OUT_DIR}", + ], + ], + "${DOLPHINCOMSTR}", + ), + emitter=_dolphin_emitter, + ), + "DolphinExtBuilder": Builder( + action=Action( + [ + [ + "${PYTHON3}", + "${ASSETS_COMPILER}", + "dolphin", + "${_DOLPHIN_SRC_DIR}", + "${_DOLPHIN_OUT_DIR}", + ], + ], + "${DOLPHINCOMSTR}", + ), + emitter=_dolphin_emitter, + ), + "ProtoVerBuilder": Builder( + action=Action( + _proto_ver_generator, + "${PBVERCOMSTR}", + ), + ), + } + ) + + +def exists(env): + return True diff --git a/scripts/fbt_tools/fbt_debugopts.py b/scripts/fbt_tools/fbt_debugopts.py new file mode 100644 index 00000000000..392465a518f --- /dev/null +++ b/scripts/fbt_tools/fbt_debugopts.py @@ -0,0 +1,81 @@ +from SCons.Errors import UserError + + +def _get_device_serials(search_str="STLink"): + import serial.tools.list_ports as list_ports + + return set([device.serial_number for device in list_ports.grep(search_str)]) + + +def GetDevices(env): + serials = _get_device_serials() + if len(serials) == 0: + raise UserError("No devices found") + + print("\n".join(serials)) + + +def generate(env, **kw): + env.AddMethod(GetDevices) + env.SetDefault( + FBT_DEBUG_DIR="${FBT_SCRIPT_DIR}/debug", + ) + + if (adapter_serial := env.subst("$SWD_TRANSPORT_SERIAL")) != "auto": + env.Append( + OPENOCD_OPTS=[ + "-c", + f"adapter serial {adapter_serial}", + ] + ) + + # Final command is "init", always explicitly added + env.Append( + OPENOCD_OPTS=["-c", "init"], + ) + + env.SetDefault( + OPENOCD_GDB_PIPE=[ + "|openocd -c 'gdb_port pipe; log_output ${FBT_DEBUG_DIR}/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}" + ], + GDBOPTS_BASE=[ + "-ex", + "source ${FBT_DEBUG_DIR}/gdbinit", + "-ex", + "target extended-remote ${GDBREMOTE}", + ], + GDBOPTS_BLACKMAGIC=[ + "-q", + "-ex", + "monitor swdp_scan", + "-ex", + "monitor debug_bmp enable", + "-ex", + "attach 1", + "-ex", + "set mem inaccessible-by-default off", + ], + GDBPYOPTS=[ + "-ex", + "source ${FBT_DEBUG_DIR}/FreeRTOS/FreeRTOS.py", + "-ex", + "source ${FBT_DEBUG_DIR}/flipperapps.py", + "-ex", + "source ${FBT_DEBUG_DIR}/flipperversion.py", + "-ex", + "fap-set-debug-elf-root ${FBT_FAP_DEBUG_ELF_ROOT}", + "-ex", + "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py", + "-ex", + "svd_load ${SVD_FILE}", + "-ex", + "compare-sections", + "-ex", + "fw-version", + ], + JFLASHPROJECT="${FBT_DEBUG_DIR}/fw.jflash", + ) + + +def exists(env): + return True diff --git a/scripts/fbt_tools/fbt_dist.py b/scripts/fbt_tools/fbt_dist.py new file mode 100644 index 00000000000..bf586b8fbba --- /dev/null +++ b/scripts/fbt_tools/fbt_dist.py @@ -0,0 +1,193 @@ +from SCons.Action import Action +from SCons.Builder import Builder +from SCons.Defaults import Touch + + +def GetProjetDirName(env, project=None): + parts = [f"f{env['TARGET_HW']}"] + if project: + parts.append(project) + + suffix = "" + if env["DEBUG"]: + suffix += "D" + if env["COMPACT"]: + suffix += "C" + if suffix: + parts.append(suffix) + + return "-".join(parts) + + +def create_fw_build_targets(env, configuration_name): + flavor = GetProjetDirName(env, configuration_name) + build_dir = env.Dir("build").Dir(flavor) + return env.SConscript( + "firmware.scons", + variant_dir=build_dir, + duplicate=0, + exports={ + "ENV": env, + "fw_build_meta": { + "type": configuration_name, + "flavor": flavor, + "build_dir": build_dir, + }, + }, + ) + + +def AddFwProject(env, base_env, fw_type, fw_env_key): + project_env = env[fw_env_key] = create_fw_build_targets(base_env, fw_type) + env.Append( + DIST_PROJECTS=[ + project_env["FW_FLAVOR"], + ], + DIST_DEPENDS=[ + project_env["FW_ARTIFACTS"], + ], + ) + + env.Replace(DIST_DIR=env.GetProjetDirName()) + return project_env + + +def AddFwFlashTarget(env, targetenv, **kw): + fwflash_target = env.FwFlash( + "#build/flash.flag", + targetenv["FW_ELF"], + **kw, + ) + env.Alias(targetenv.subst("${FIRMWARE_BUILD_CFG}_flash"), fwflash_target) + if env["FORCE"]: + env.AlwaysBuild(fwflash_target) + return fwflash_target + + +def AddJFlashTarget(env, targetenv, **kw): + jflash_target = env.JFlash( + "#build/jflash-${BUILD_CFG}-flash.flag", + targetenv["FW_BIN"], + JFLASHADDR=targetenv.subst("$IMAGE_BASE_ADDRESS"), + BUILD_CFG=targetenv.subst("${FIRMWARE_BUILD_CFG}"), + **kw, + ) + env.Alias(targetenv.subst("${FIRMWARE_BUILD_CFG}_jflash"), jflash_target) + if env["FORCE"]: + env.AlwaysBuild(jflash_target) + return jflash_target + + +def AddUsbFlashTarget(env, file_flag, extra_deps, **kw): + usb_update = env.UsbInstall( + file_flag, + ( + env["DIST_DEPENDS"], + *extra_deps, + ), + ) + if env["FORCE"]: + env.AlwaysBuild(usb_update) + return usb_update + + +def DistCommand(env, name, source, **kw): + target = f"dist_{name}" + command = env.Command( + target, + source, + action=Action( + [ + [ + "${PYTHON3}", + "${DIST_SCRIPT}", + "copy", + "-p", + "${DIST_PROJECTS}", + "-s", + "${DIST_SUFFIX}", + "${DIST_EXTRA}", + ] + ], + "${DISTCOMSTR}", + ), + **kw, + ) + env.Pseudo(target) + env.Alias(name, command) + return command + + +def generate(env): + if not env["VERBOSE"]: + env.SetDefault( + COPROCOMSTR="\tCOPRO\t${TARGET}", + DISTCOMSTR="\tDIST\t${TARGET}", + ) + env.AddMethod(AddFwProject) + env.AddMethod(DistCommand) + env.AddMethod(AddFwFlashTarget) + env.AddMethod(GetProjetDirName) + env.AddMethod(AddJFlashTarget) + env.AddMethod(AddUsbFlashTarget) + + env.SetDefault( + COPRO_MCU_FAMILY="STM32WB5x", + SELFUPDATE_SCRIPT="${FBT_SCRIPT_DIR}/selfupdate.py", + DIST_SCRIPT="${FBT_SCRIPT_DIR}/sconsdist.py", + COPRO_ASSETS_SCRIPT="${FBT_SCRIPT_DIR}/assets.py", + FW_FLASH_SCRIPT="${FBT_SCRIPT_DIR}/fwflash.py", + ) + + env.Append( + BUILDERS={ + "FwFlash": Builder( + action=[ + [ + "${PYTHON3}", + "${FW_FLASH_SCRIPT}", + "-d" if env["VERBOSE"] else "", + "--interface=${SWD_TRANSPORT}", + "--serial=${SWD_TRANSPORT_SERIAL}", + "${SOURCE}", + ], + Touch("${TARGET}"), + ] + ), + "UsbInstall": Builder( + action=[ + [ + "${PYTHON3}", + "${SELFUPDATE_SCRIPT}", + "-p", + "${FLIP_PORT}", + "${UPDATE_BUNDLE_DIR}/update.fuf", + ], + Touch("${TARGET}"), + ] + ), + "CoproBuilder": Builder( + action=Action( + [ + [ + "${PYTHON3}", + "${COPRO_ASSETS_SCRIPT}", + "copro", + "${COPRO_CUBE_DIR}", + "${TARGET}", + "${COPRO_MCU_FAMILY}", + "--cube_ver=${COPRO_CUBE_VERSION}", + "--stack_type=${COPRO_STACK_TYPE}", + "--stack_file=${COPRO_STACK_BIN}", + "--stack_addr=${COPRO_STACK_ADDR}", + ] + ], + "${COPROCOMSTR}", + ) + ), + } + ) + + +def exists(env): + return True diff --git a/scripts/fbt_tools/fbt_envhooks.py b/scripts/fbt_tools/fbt_envhooks.py new file mode 100644 index 00000000000..0538e173c65 --- /dev/null +++ b/scripts/fbt_tools/fbt_envhooks.py @@ -0,0 +1,67 @@ +""" + +To introduce changes to firmware build environment that are specific to your fork: + + create a file "scripts/fbt/fbt_hooks.py" + +With it, you can define functions that will be called at specific points of +firmware build configuration, with environment as an argument. + +For example, you can define a function `PreConfigureFwEnvionment(env)` that +defines that will be a part of SDK build, so applications can +use them for conditional compilation. + +Here is a list of all available hooks: + + PreConfigureFwEnvionment(env): + This function is called on firmware environment (incl. updater) + before any major configuration is done. + + PostConfigureFwEnvionment(env): + This function is called on firmware environment (incl. updater) + after all configuration is done. + + PreConfigureUfbtEnvionment(env): + This function is called on ufbt environment at the beginning of + its configuration, before dist environment is created. + + PostConfigureUfbtEnvionment(env): + This function is called on ufbt dist_env environment after all + configuration and target creation is done. +""" + + +class DefaultFbtHooks: + pass + + +try: + from fbt import fbt_hooks +except ImportError: + fbt_hooks = DefaultFbtHooks() + + +def generate(env): + stub_hook = lambda env: None + control_hooks = [ + "PreConfigureFwEnvionment", + "PostConfigureFwEnvionment", + "PreConfigureUfbtEnvionment", + "PostConfigureUfbtEnvionment", + ] + + if ( + isinstance(fbt_hooks, DefaultFbtHooks) + and env.subst("${FIRMWARE_ORIGIN}") != "Official" + ): + # If fbt_hooks.py is not present, but we are not building official firmware, + # create "scripts/fbt/fbt_hooks.py" to implement changes to firmware build environment. + pass + + for hook_name in control_hooks: + hook_fn = getattr(fbt_hooks, hook_name, stub_hook) + env.AddMethod(hook_fn, hook_name) + + +def exists(): + return True diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py new file mode 100644 index 00000000000..a7914c4f877 --- /dev/null +++ b/scripts/fbt_tools/fbt_extapps.py @@ -0,0 +1,557 @@ +import itertools +import pathlib +from dataclasses import dataclass, field +from typing import Dict, List, Optional + +import SCons.Warnings +from ansi.color import fg +from fbt.appmanifest import FlipperApplication, FlipperAppType, FlipperManifestException +from fbt.elfmanifest import assemble_manifest_data +from fbt.fapassets import FileBundler +from fbt.sdk.cache import SdkCache +from fbt.util import resolve_real_dir_node +from SCons.Action import Action +from SCons.Builder import Builder +from SCons.Errors import UserError +from SCons.Node.FS import Entry, File + +_FAP_META_SECTION = ".fapmeta" +_FAP_FILEASSETS_SECTION = ".fapassets" + + +@dataclass +class FlipperExternalAppInfo: + app: FlipperApplication + compact: Optional[File] = None + debug: Optional[File] = None + validator: Optional[Entry] = None + # List of tuples (dist_to_sd, path) + dist_entries: list[tuple[bool, str]] = field(default_factory=list) + + +class AppBuilder: + @staticmethod + def get_app_work_dir(env, app): + return env["EXT_APPS_WORK_DIR"].Dir(app.appid) + + def __init__(self, env, app): + self.fw_env = env + self.app = app + self.ext_apps_work_dir = env["EXT_APPS_WORK_DIR"] + self.app_work_dir = self.get_app_work_dir(env, app) + self.app_alias = f"fap_{self.app.appid}" + self.icons_src = None + self.externally_built_files = [] + self.private_libs = [] + + def build(self): + self._setup_app_env() + self._build_external_files() + self._compile_assets() + self._build_private_libs() + return self._build_app() + + def _setup_app_env(self): + self.app_env = self.fw_env.Clone( + FAP_SRC_DIR=self.app._appdir, + FAP_WORK_DIR=self.app_work_dir, + ) + self.app_env.Append( + CPPDEFINES=[ + ("FAP_VERSION", f'\\"{".".join(map(str, self.app.fap_version))}\\"'), + *self.app.cdefines, + ], + ) + self.app_env.VariantDir(self.app_work_dir, self.app._appdir, duplicate=False) + + def _build_external_files(self): + if not self.app.fap_extbuild: + return + + for external_file_def in self.app.fap_extbuild: + self.externally_built_files.append(external_file_def.path) + self.app_env.Alias(self.app_alias, external_file_def.path) + self.app_env.AlwaysBuild( + self.app_env.Command( + external_file_def.path, + None, + Action( + external_file_def.command, + "" if self.app_env["VERBOSE"] else "\tEXTCMD\t${TARGET}", + ), + ) + ) + + def _compile_assets(self): + if not self.app.fap_icon_assets: + return + + fap_icons = self.app_env.CompileIcons( + self.app_work_dir, + self.app._appdir.Dir(self.app.fap_icon_assets), + icon_bundle_name=f"{self.app.fap_icon_assets_symbol or self.app.appid }_icons", + ) + self.app_env.Alias("_fap_icons", fap_icons) + self.fw_env.Append(_APP_ICONS=[fap_icons]) + self.icons_src = next(filter(lambda n: n.path.endswith(".c"), fap_icons)) + + def _build_private_libs(self): + for lib_def in self.app.fap_private_libs: + self.private_libs.append(self._build_private_lib(lib_def)) + + def _build_private_lib(self, lib_def): + lib_src_root_path = self.app_work_dir.Dir("lib").Dir(lib_def.name) + self.app_env.AppendUnique( + CPPPATH=list( + self.app_env.Dir(lib_src_root_path) + .Dir(incpath) + .srcnode() + .rfile() + .abspath + for incpath in lib_def.fap_include_paths + ), + ) + + lib_sources = list( + itertools.chain.from_iterable( + self.app_env.GlobRecursive(source_type, lib_src_root_path) + for source_type in lib_def.sources + ) + ) + + if len(lib_sources) == 0: + raise UserError(f"No sources gathered for private library {lib_def}") + + private_lib_env = self.app_env.Clone() + private_lib_env.AppendUnique( + CCFLAGS=lib_def.cflags, + CPPDEFINES=lib_def.cdefines, + CPPPATH=list( + map( + lambda cpath: resolve_real_dir_node(self.app._appdir.Dir(cpath)), + lib_def.cincludes, + ) + ), + ) + + return private_lib_env.StaticLibrary( + self.app_work_dir.File(lib_def.name), + lib_sources, + ) + + def _build_app(self): + if self.app.fap_file_assets: + self.app._assets_dirs = [self.app._appdir.Dir(self.app.fap_file_assets)] + + self.app_env.Append( + LIBS=[*self.app.fap_libs, *self.private_libs, *self.app.fap_libs], + CPPPATH=[self.app_env.Dir(self.app_work_dir), self.app._appdir], + ) + + app_sources = self.app_env.GatherSources( + [self.app.sources, "!lib"], self.app_work_dir + ) + + if not app_sources: + raise UserError(f"No source files found for {self.app.appid}") + + # Ensure that icons are included in the build, regardless of user-configured sources + if self.icons_src and not self.icons_src in app_sources: + app_sources.append(self.icons_src) + + ## Uncomment for debug + # print(f"App sources for {self.app.appid}: {list(f.path for f in app_sources)}") + + app_artifacts = FlipperExternalAppInfo(self.app) + app_artifacts.debug = self.app_env.Program( + self.ext_apps_work_dir.File(f"{self.app.appid}_d.elf"), + app_sources, + APP_ENTRY=self.app.entry_point, + )[0] + + app_artifacts.compact = self.app_env.EmbedAppMetadata( + self.ext_apps_work_dir.File(f"{self.app.appid}.fap"), + app_artifacts.debug, + APP=self.app, + )[0] + + if self.app.embeds_plugins: + self.app._assets_dirs.append(self.app_work_dir.Dir("assets")) + + app_artifacts.validator = self.app_env.ValidateAppImports( + app_artifacts.compact, + _CHECK_APP=self.app.do_strict_import_checks + and self.app_env.get("STRICT_FAP_IMPORT_CHECK"), + )[0] + + if self.app.apptype == FlipperAppType.PLUGIN: + for parent_app_id in self.app.requires: + if self.app.fal_embedded: + parent_app = self.app._appmanager.get(parent_app_id) + if not parent_app: + raise UserError( + f"Embedded plugin {self.app.appid} requires unknown app {parent_app_id}" + ) + self.app_env.Install( + target=self.get_app_work_dir(self.app_env, parent_app) + .Dir("assets") + .Dir("plugins"), + source=app_artifacts.compact, + ) + else: + fal_path = f"apps_data/{parent_app_id}/plugins/{app_artifacts.compact.name}" + deployable = True + # If it's a plugin for a non-deployable app, don't include it in the resources + if parent_app := self.app._appmanager.get(parent_app_id): + if not parent_app.is_default_deployable: + deployable = False + app_artifacts.dist_entries.append((deployable, fal_path)) + else: + fap_path = f"apps/{self.app.fap_category}/{app_artifacts.compact.name}" + app_artifacts.dist_entries.append( + (self.app.is_default_deployable, fap_path) + ) + + self._configure_deps_and_aliases(app_artifacts) + return app_artifacts + + def _configure_deps_and_aliases(self, app_artifacts: FlipperExternalAppInfo): + # Extra things to clean up along with the app + self.app_env.Clean( + app_artifacts.debug, + [*self.externally_built_files, self.app_work_dir], + ) + + # Create listing of the app + app_elf_dump = self.app_env.ObjDump(app_artifacts.debug) + self.app_env.Alias(f"{self.app_alias}_list", app_elf_dump) + + # Extra dependencies for the app - manifest values, icon file + manifest_vals = { + k: v + for k, v in vars(self.app).items() + if not k.startswith(FlipperApplication.PRIVATE_FIELD_PREFIX) + } + + self.app_env.Depends( + app_artifacts.compact, + [self.app_env["SDK_DEFINITION"], self.app_env.Value(manifest_vals)], + ) + if self.app.fap_icon: + self.app_env.Depends( + app_artifacts.compact, + self.app_env.File(f"{self.app._apppath}/{self.app.fap_icon}"), + ) + + # Add dependencies on file assets + for assets_dir in self.app._assets_dirs: + glob_res = self.app_env.GlobRecursive("*", assets_dir) + self.app_env.Depends( + app_artifacts.compact, + (*glob_res, assets_dir), + ) + + # Always run the validator for the app's binary when building the app + self.app_env.AlwaysBuild(app_artifacts.validator) + self.app_env.Alias(self.app_alias, app_artifacts.validator) + + +def BuildAppElf(env, app): + app_builder = AppBuilder(env, app) + env["EXT_APPS"][app.appid] = app_artifacts = app_builder.build() + return app_artifacts + + +def prepare_app_metadata(target, source, env): + metadata_node = next(filter(lambda t: t.name.endswith(_FAP_META_SECTION), target)) + + sdk_cache = SdkCache(env["SDK_DEFINITION"].path, load_version_only=True) + + if not sdk_cache.is_buildable(): + raise UserError( + "SDK version is not finalized, please review changes and re-run operation. See AppsOnSDCard.md for more details." + ) + + app = env["APP"] + with open(metadata_node.abspath, "wb") as f: + f.write( + assemble_manifest_data( + app_manifest=app, + hardware_target=int(env.subst("$TARGET_HW")), + sdk_version=sdk_cache.version.as_int(), + ) + ) + + +def _validate_app_imports(target, source, env): + sdk_cache = SdkCache(env["SDK_DEFINITION"].path, load_version_only=False) + app_syms = set() + with open(target[0].path, "rt") as f: + for line in f: + app_syms.add(line.split()[0]) + unresolved_syms = app_syms - sdk_cache.get_valid_names() + if unresolved_syms: + warning_msg = fg.brightyellow( + f"{source[0].path}: app may not be runnable. Symbols not resolved using firmware's API: " + ) + fg.brightmagenta(f"{unresolved_syms}") + disabled_api_syms = unresolved_syms.intersection(sdk_cache.get_disabled_names()) + if disabled_api_syms: + warning_msg += ( + fg.brightyellow(" (in API, but disabled: ") + + fg.brightmagenta(f"{disabled_api_syms}") + + fg.brightyellow(")") + ) + if env.get("_CHECK_APP"): + raise UserError(warning_msg) + else: + SCons.Warnings.warn(SCons.Warnings.LinkWarning, warning_msg), + + +def GetExtAppByIdOrPath(env, app_dir): + if not app_dir: + raise UserError("APPSRC= not set") + + appmgr = env["APPMGR"] + + app = None + try: + # Maybe user passed an appid? + app = appmgr.get(app_dir) + except FlipperManifestException: + # Look up path components in known app dirs + for dir_part in reversed(pathlib.Path(app_dir).parts): + if app := appmgr.find_by_appdir(dir_part): + break + + if not app: + raise UserError(f"Failed to resolve application for given APPSRC={app_dir}") + + app_artifacts = env["EXT_APPS"].get(app.appid, None) + if not app_artifacts: + raise UserError( + f"Application {app.appid} is not configured to be built as external" + ) + + return app_artifacts + + +def _embed_app_metadata_emitter(target, source, env): + app = env["APP"] + + # Hack: change extension for fap libs + if app.apptype == FlipperAppType.PLUGIN: + target[0].name = target[0].name.replace(".fap", ".fal") + + app_work_dir = AppBuilder.get_app_work_dir(env, app) + app._section_fapmeta = app_work_dir.File(_FAP_META_SECTION) + target.append(app._section_fapmeta) + + # At this point, we haven't added dir with embedded plugins to _assets_dirs yet + if app._assets_dirs or app.embeds_plugins: + app._section_fapfileassets = app_work_dir.File(_FAP_FILEASSETS_SECTION) + target.append(app._section_fapfileassets) + + return (target, source) + + +def prepare_app_file_assets(target, source, env): + files_section_node = next( + filter(lambda t: t.name.endswith(_FAP_FILEASSETS_SECTION), target) + ) + + bundler = FileBundler( + list(env.Dir(asset_dir).abspath for asset_dir in env["APP"]._assets_dirs) + ) + bundler.export(files_section_node.abspath) + + +def generate_embed_app_metadata_actions(source, target, env, for_signature): + app = env["APP"] + + actions = [ + Action(prepare_app_metadata, "$APPMETA_COMSTR"), + ] + + objcopy_args = [ + "${OBJCOPY}", + "--remove-section", + ".ARM.attributes", + "--add-section", + "${_FAP_META_SECTION}=${APP._section_fapmeta}", + "--set-section-flags", + "${_FAP_META_SECTION}=contents,noload,readonly,data", + ] + + if app._section_fapfileassets: + actions.append(Action(prepare_app_file_assets, "$APPFILE_COMSTR")) + objcopy_args.extend( + ( + "--add-section", + "${_FAP_FILEASSETS_SECTION}=${APP._section_fapfileassets}", + "--set-section-flags", + "${_FAP_FILEASSETS_SECTION}=contents,noload,readonly,data", + ) + ) + + objcopy_args.extend( + ( + "--strip-debug", + "--strip-unneeded", + "--add-gnu-debuglink=${SOURCE}", + "${SOURCES}", + "${TARGET}", + ) + ) + + actions.extend( + ( + Action( + [objcopy_args], + "$APPMETAEMBED_COMSTR", + ), + Action( + [ + [ + "${PYTHON3}", + "${FBT_SCRIPT_DIR}/fastfap.py", + "${TARGET}", + "${OBJCOPY}", + ] + ], + "$FASTFAP_COMSTR", + ), + ) + ) + + return Action(actions) + + +@dataclass +class AppDeploymentComponents: + deploy_sources: Dict[str, object] = field(default_factory=dict) + validators: List[object] = field(default_factory=list) + extra_launch_args: str = "" + + def add_app(self, app_artifacts): + for _, ext_path in app_artifacts.dist_entries: + self.deploy_sources[f"/ext/{ext_path}"] = app_artifacts.compact + self.validators.append(app_artifacts.validator) + + +def _gather_app_components(env, appname) -> AppDeploymentComponents: + components = AppDeploymentComponents() + + def _add_host_app_to_targets(host_app): + artifacts_app_to_run = env["EXT_APPS"].get(host_app.appid, None) + components.add_app(artifacts_app_to_run) + for plugin in host_app._plugins: + components.add_app(env["EXT_APPS"].get(plugin.appid, None)) + + artifacts_app_to_run = env.GetExtAppByIdOrPath(appname) + if artifacts_app_to_run.app.apptype == FlipperAppType.PLUGIN: + # We deploy host app instead + host_app = env["APPMGR"].get(artifacts_app_to_run.app.requires[0]) + + if host_app: + if host_app.apptype in [ + FlipperAppType.EXTERNAL, + FlipperAppType.MENUEXTERNAL, + ]: + components.add_app(host_app) + else: + # host app is a built-in app + components.add_app(artifacts_app_to_run) + components.extra_launch_args = f"-a {host_app.name}" + else: + raise UserError("Host app is unknown") + else: + _add_host_app_to_targets(artifacts_app_to_run.app) + return components + + +def AddAppLaunchTarget(env, appname, launch_target_name): + components = _gather_app_components(env, appname) + target = env.PhonyTarget( + launch_target_name, + [ + [ + "${PYTHON3}", + "${APP_RUN_SCRIPT}", + "-p", + "${FLIP_PORT}", + "${EXTRA_ARGS}", + "-s", + "${SOURCES}", + "-t", + "${FLIPPER_FILE_TARGETS}", + ] + ], + source=components.deploy_sources.values(), + FLIPPER_FILE_TARGETS=components.deploy_sources.keys(), + EXTRA_ARGS=components.extra_launch_args, + ) + env.Alias(launch_target_name, components.validators) + return target + + +def AddAppBuildTarget(env, appname, build_target_name): + components = _gather_app_components(env, appname) + env.Alias(build_target_name, components.validators) + env.Alias(build_target_name, components.deploy_sources.values()) + + +def generate(env, **kw): + env.SetDefault( + EXT_APPS_WORK_DIR=env.Dir(env["FBT_FAP_DEBUG_ELF_ROOT"]), + APP_RUN_SCRIPT="${FBT_SCRIPT_DIR}/runfap.py", + ) + if not env["VERBOSE"]: + env.SetDefault( + APPMETA_COMSTR="\tAPPMETA\t${TARGET}", + APPFILE_COMSTR="\tAPPFILE\t${TARGET}", + APPMETAEMBED_COMSTR="\tFAP\t${TARGET}", + FASTFAP_COMSTR="\tFASTFAP\t${TARGET}", + APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}", + ) + + env.SetDefault( + EXT_APPS={}, # appid -> FlipperExternalAppInfo + EXT_LIBS={}, + _APP_ICONS=[], + _FAP_META_SECTION=_FAP_META_SECTION, + _FAP_FILEASSETS_SECTION=_FAP_FILEASSETS_SECTION, + ) + + env.AddMethod(BuildAppElf) + env.AddMethod(GetExtAppByIdOrPath) + env.AddMethod(AddAppLaunchTarget) + env.AddMethod(AddAppBuildTarget) + + env.Append( + BUILDERS={ + "EmbedAppMetadata": Builder( + generator=generate_embed_app_metadata_actions, + suffix=".fap", + src_suffix=".elf", + emitter=_embed_app_metadata_emitter, + ), + "ValidateAppImports": Builder( + action=[ + Action( + "@${NM} -P -u ${SOURCE} > ${TARGET}", + None, # "$APPDUMP_COMSTR", + ), + Action( + _validate_app_imports, + "$APPCHECK_COMSTR", + ), + ], + suffix=".impsyms", + src_suffix=".fap", + ), + } + ) + + +def exists(env): + return True diff --git a/scripts/fbt_tools/fbt_help.py b/scripts/fbt_tools/fbt_help.py new file mode 100644 index 00000000000..dcdce934efe --- /dev/null +++ b/scripts/fbt_tools/fbt_help.py @@ -0,0 +1,52 @@ +targets_help = """Configuration variables: +""" + +tail_help = """ + +TASKS: +Firmware & apps: + firmware_all, fw_dist: + Build firmware; create distribution package + faps, fap_dist: + Build all FAP apps + fap_{APPID}, build APPSRC={APPID}; launch APPSRC={APPID}: + Build FAP app with appid={APPID}; upload & start it over USB + fap_deploy: + Build and upload all FAP apps over USB + + +Flashing & debugging: + flash, jflash: + Flash firmware to target using SWD probe. See also SWD_TRANSPORT, SWD_TRANSPORT_SERIAL + flash_usb, flash_usb_full: + Install firmware using self-update package + debug, debug_other, blackmagic: + Start GDB + +Other: + cli: + Open a Flipper CLI session over USB + firmware_cdb, updater_cdb: + Generate сompilation_database.json + lint, lint_py: + run linters + format, format_py: + run code formatters + firmware_pvs: + generate a PVS-Studio report + +How to open a shell with toolchain environment and other build tools: + In your shell, type "source `./fbt -s env`". You can also use "." instead of "source". + +For more targets & info, see documentation/fbt.md +""" + + +def generate(env, **kw): + vars = kw["vars"] + basic_help = vars.GenerateHelpText(env) + env.Help(targets_help + basic_help + tail_help) + + +def exists(env): + return True diff --git a/scripts/fbt_tools/fbt_hwtarget.py b/scripts/fbt_tools/fbt_hwtarget.py new file mode 100644 index 00000000000..67975ed0f8f --- /dev/null +++ b/scripts/fbt_tools/fbt_hwtarget.py @@ -0,0 +1,133 @@ +import json + + +class HardwareTargetLoader: + def __init__(self, env, root_target_scons_dir, target_id): + self.env = env + self.all_targets_root_dir = root_target_scons_dir + self.target_dir = self._getTargetDir(target_id) + # self.target_id = target_id + self.layered_target_dirs = [] + + self.include_paths = [] + self.sdk_header_paths = [] + self.startup_script = None + self.linker_script_flash = None + self.linker_script_ram = None + self.linker_script_app = None + self.sdk_symbols = None + self.linker_dependencies = [] + self.excluded_sources = [] + self.excluded_headers = [] + self.excluded_modules = [] + self._processTargetDefinitions(target_id) + + def _getTargetDir(self, target_id): + return self.all_targets_root_dir.Dir(f"f{target_id}") + + def _loadDescription(self, target_id): + target_json_file = self._getTargetDir(target_id).File("target.json") + if not target_json_file.exists(): + raise Exception(f"Target file {target_json_file} does not exist") + with open(target_json_file.get_abspath(), "r") as f: + vals = json.load(f) + return vals + + def _processTargetDefinitions(self, target_id): + target_dir = self._getTargetDir(target_id) + self.layered_target_dirs.append(target_dir) + + config = self._loadDescription(target_id) + + for path_list in ("include_paths", "sdk_header_paths"): + getattr(self, path_list).extend( + target_dir.Dir(p) for p in config.get(path_list, []) + ) + + self.excluded_sources.extend(config.get("excluded_sources", [])) + self.excluded_headers.extend(config.get("excluded_headers", [])) + self.excluded_modules.extend(config.get("excluded_modules", [])) + + file_attrs = ( + # (name, use_src_node) + ("startup_script", True), + ("linker_script_flash", True), + ("linker_script_ram", True), + ("linker_script_app", True), + ("sdk_symbols", True), + ) + + for attr_name, use_src_node in file_attrs: + if (val := config.get(attr_name)) and not getattr(self, attr_name): + node = target_dir.File(val) + if use_src_node: + node = node.srcnode() + # print(f"Got node {node}, {node.path} for {attr_name}") + setattr(self, attr_name, node) + + for attr_name in ("linker_dependencies",): + if (val := config.get(attr_name)) and not getattr(self, attr_name): + setattr(self, attr_name, val) + + if inherited_target := config.get("inherit", None): + self._processTargetDefinitions(inherited_target) + + def gatherSources(self): + sources = [self.startup_script] + seen_filenames = set(self.excluded_sources) + # print("Layers: ", self.layered_target_dirs) + for target_dir in self.layered_target_dirs: + accepted_sources = list( + filter( + lambda f: f.name not in seen_filenames, + self.env.GlobRecursive("*.c", target_dir), + ) + ) + seen_filenames.update(f.name for f in accepted_sources) + sources.extend(accepted_sources) + # print(f"Found {len(sources)} sources: {list(f.path for f in sources)}") + return list(f.get_path(self.all_targets_root_dir) for f in sources) + + def gatherSdkHeaders(self): + 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 + # 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: + seen_sdk_headers.add(header.name) + sdk_headers.append(header) + return sdk_headers + + +def ConfigureForTarget(env, target_id): + target_loader = HardwareTargetLoader(env, env["TARGETS_ROOT"], target_id) + env.Replace( + TARGET_CFG=target_loader, + SDK_DEFINITION=target_loader.sdk_symbols, + SKIP_MODULES=target_loader.excluded_modules, + ) + + env.Append( + CPPPATH=target_loader.include_paths, + SDK_HEADERS=target_loader.gatherSdkHeaders(), + ) + + +def ApplyLibFlags(env): + flags_to_apply = env["FW_LIB_OPTS"].get( + env.get("FW_LIB_NAME"), + env["FW_LIB_OPTS"]["Default"], + ) + # print("Flags for ", env.get("FW_LIB_NAME", "Default"), flags_to_apply) + env.MergeFlags(flags_to_apply) + + +def generate(env): + env.AddMethod(ConfigureForTarget) + env.AddMethod(ApplyLibFlags) + + +def exists(env): + return True diff --git a/scripts/fbt_tools/fbt_resources.py b/scripts/fbt_tools/fbt_resources.py new file mode 100644 index 00000000000..4c3146ba45c --- /dev/null +++ b/scripts/fbt_tools/fbt_resources.py @@ -0,0 +1,117 @@ +import os +import shutil + +from SCons.Action import Action +from SCons.Builder import Builder +from SCons.Errors import StopError +from SCons.Node.FS import Dir, File + + +def __generate_resources_dist_entries(env): + src_target_entries = [] + + resources_root = env.Dir(env["RESOURCES_ROOT"]) + + for app_artifacts in env["FW_EXTAPPS"].application_map.values(): + for _, dist_path in filter( + lambda dist_entry: dist_entry[0], app_artifacts.dist_entries + ): + src_target_entries.append( + ( + app_artifacts.compact, + resources_root.File(dist_path), + ) + ) + + # Deploy apps' resources too + for app in env["APPBUILD"].apps: + if not app.resources: + continue + apps_resource_dir = app._appdir.Dir(app.resources) + for res_file in env.GlobRecursive("*", apps_resource_dir): + if not isinstance(res_file, File): + continue + src_target_entries.append( + ( + res_file, + resources_root.File( + res_file.get_path(apps_resource_dir), + ), + ) + ) + + # Deploy other stuff from _EXTRA_DIST + for extra_dist in env["_EXTRA_DIST"]: + if isinstance(extra_dist, Dir): + src_target_entries.append( + ( + extra_dist, + resources_root.Dir(extra_dist.name), + ) + ) + else: + raise StopError(f"Unsupported extra dist type: {type(extra_dist)}") + + return src_target_entries + + +def _resources_dist_emitter(target, source, env): + src_target_entries = __generate_resources_dist_entries(env) + source = list(map(lambda entry: entry[0], src_target_entries)) + return (target, source) + + +def _resources_dist_action(target, source, env): + dist_entries = __generate_resources_dist_entries(env) + assert len(dist_entries) == len(source) + shutil.rmtree(env.Dir(env["RESOURCES_ROOT"]).abspath, ignore_errors=True) + for src, target in dist_entries: + if isinstance(src, File): + os.makedirs(os.path.dirname(target.path), exist_ok=True) + shutil.copy(src.path, target.path) + elif isinstance(src, Dir): + shutil.copytree(src.path, target.path) + else: + raise StopError(f"Unsupported dist entry type: {type(src)}") + + +def generate(env, **kw): + env.SetDefault( + ASSETS_COMPILER="${FBT_SCRIPT_DIR}/assets.py", + ) + + if not env["VERBOSE"]: + env.SetDefault( + RESOURCEDISTCOMSTR="\tRESDIST\t${RESOURCES_ROOT}", + RESMANIFESTCOMSTR="\tMANIFST\t${TARGET}", + ) + + env.Append( + BUILDERS={ + "ManifestBuilder": Builder( + action=[ + Action( + _resources_dist_action, + "${RESOURCEDISTCOMSTR}", + ), + Action( + [ + [ + "${PYTHON3}", + "${ASSETS_COMPILER}", + "manifest", + "${TARGET.dir.posix}", + "--timestamp=${GIT_UNIX_TIMESTAMP}", + ] + ], + "${RESMANIFESTCOMSTR}", + ), + ], + emitter=_resources_dist_emitter, + ), + } + ) + + +def exists(env): + return True diff --git a/scripts/fbt_tools/fbt_sdk.py b/scripts/fbt_tools/fbt_sdk.py new file mode 100644 index 00000000000..17acc8cf1ae --- /dev/null +++ b/scripts/fbt_tools/fbt_sdk.py @@ -0,0 +1,336 @@ +import json +import os.path +import pathlib +import posixpath +import shutil + +from fbt.sdk.cache import SdkCache +from fbt.sdk.collector import SdkCollector +from fbt.util import path_as_posix +from SCons.Action import Action +from SCons.Builder import Builder +from SCons.Errors import UserError + +# from SCons.Scanner import C +from SCons.Script import Entry +from SCons.Util import LogicalLines + + +def ProcessSdkDepends(env, filename): + try: + with open(filename, "r") as fin: + lines = LogicalLines(fin).readlines() + except IOError: + return [] + + _, depends = lines[0].split(":", 1) + depends = depends.split() + depends.pop(0) # remove the .c file + depends = list( + # Don't create dependency on non-existing files + # (e.g. when they were renamed since last build) + filter( + lambda file: file.exists(), + (env.File(f"#{path}") for path in depends), + ) + ) + return depends + + +def _api_amalgam_emitter(target, source, env): + target.append(env.ChangeFileExtension(target[0], ".d")) + target.append(env.ChangeFileExtension(target[0], ".i.c")) + return target, source + + +def _api_amalgam_gen_origin_header(target, source, env): + mega_file = env.subst("${TARGET}.c", target=target[0]) + with open(mega_file, "wt") as sdk_c: + sdk_c.write( + "\n".join(f"#include <{h.srcnode().path}>" for h in env["SDK_HEADERS"]) + ) + + +class SdkMeta: + MAP_FILE_SUBST = "SDK_MAP_FILE_SUBST" + + def __init__(self, env, tree_builder: "SdkTreeBuilder"): + self.env = env + self.treebuilder = tree_builder + + def save_to(self, json_manifest_path: str): + meta_contents = { + "sdk_symbols": self.treebuilder.build_sdk_file_path( + self.env["SDK_DEFINITION"].path + ), + "cc_args": self._wrap_scons_vars("$CCFLAGS $_CCCOMCOM"), + "cpp_args": self._wrap_scons_vars("$CXXFLAGS $CCFLAGS $_CCCOMCOM"), + "linker_args": self._wrap_scons_vars("$LINKFLAGS"), + "linker_libs": self.env.subst("${LIBS}"), + "app_ep_subst": self.env.subst("${APP_ENTRY}"), + "sdk_path_subst": self.env.subst("${SDK_DIR_SUBST}"), + "map_file_subst": self.MAP_FILE_SUBST, + "hardware": self.env.subst("${TARGET_HW}"), + } + with open(json_manifest_path, "wt") as f: + json.dump(meta_contents, f, indent=4) + + def _wrap_scons_vars(self, vars: str): + expanded_vars = self.env.subst( + vars, + target=Entry(self.MAP_FILE_SUBST), + ) + return path_as_posix(expanded_vars) + + +class SdkTreeBuilder: + SDK_DIR_SUBST = "SDK_ROOT_DIR" + SDK_APP_EP_SUBST = "SDK_APP_EP_SUBST" + HEADER_EXTENSIONS = [".h", ".hpp"] + + def __init__(self, env, target, source) -> None: + self.env = env + self.target = target + self.source = source + + self.header_depends = [] + self.header_dirs = [] + + self.target_sdk_dir_name = env.subst("f${TARGET_HW}_sdk") + self.sdk_root_dir = target[0].Dir(".") + self.sdk_deploy_dir = self.sdk_root_dir.Dir(self.target_sdk_dir_name) + + self.sdk_env = self.env.Clone( + APP_ENTRY=self.SDK_APP_EP_SUBST, + SDK_DIR_SUBST=self.SDK_DIR_SUBST, + ) + + def _parse_sdk_depends(self): + deps_file = self.source[0] + with open(deps_file.path, "rt") as deps_f: + lines = LogicalLines(deps_f).readlines() + _, depends = lines[0].split(":", 1) + self.header_depends = list( + filter( + lambda fname: any(map(fname.endswith, self.HEADER_EXTENSIONS)), + depends.split(), + ), + ) + self.header_depends.append(self.sdk_env.subst("${LINKER_SCRIPT_PATH}")) + self.header_depends.append(self.sdk_env.subst("${SDK_DEFINITION}")) + self.header_dirs = sorted( + set(map(os.path.normpath, map(os.path.dirname, self.header_depends))) + ) + + def _generate_sdk_meta(self): + filtered_paths = ["."] + full_fw_paths = list( + map( + os.path.normpath, + ( + self.sdk_env.Dir(inc_dir).relpath + for inc_dir in self.sdk_env["CPPPATH"] + ), + ) + ) + + sdk_dirs = ", ".join(f"'{dir}'" for dir in self.header_dirs) + filtered_paths.extend( + filter(lambda path: path in sdk_dirs, full_fw_paths), + ) + filtered_paths = list(map(self.build_sdk_file_path, filtered_paths)) + + self.sdk_env.Replace( + CPPPATH=filtered_paths, + ORIG_LINKER_SCRIPT_PATH=self.env["LINKER_SCRIPT_PATH"], + LINKER_SCRIPT_PATH=self.build_sdk_file_path("${ORIG_LINKER_SCRIPT_PATH}"), + ) + meta = SdkMeta(self.sdk_env, self) + meta.save_to(self.target[0].path) + + def build_sdk_file_path(self, orig_path: str) -> str: + return path_as_posix( + posixpath.normpath( + posixpath.join( + self.SDK_DIR_SUBST, + self.target_sdk_dir_name, + orig_path, + ) + ) + ) + + def emitter(self, target, source, env): + target_folder = target[0] + target = [target_folder.File("sdk.opts")] + return target, source + + def _run_deploy_commands(self): + dirs_to_create = set( + self.sdk_deploy_dir.Dir(dirpath).path for dirpath in self.header_dirs + ) + + shutil.rmtree(self.sdk_root_dir.path, ignore_errors=False) + + for sdkdir in dirs_to_create: + os.makedirs(sdkdir, exist_ok=True) + + for header in self.header_depends: + shutil.copy2(header, self.sdk_deploy_dir.File(header).path) + + def deploy_action(self): + self._parse_sdk_depends() + self._run_deploy_commands() + self._generate_sdk_meta() + + +def _deploy_sdk_header_tree_action(target, source, env): + sdk_tree = SdkTreeBuilder(env, target, source) + return sdk_tree.deploy_action() + + +def _deploy_sdk_header_tree_emitter(target, source, env): + sdk_tree = SdkTreeBuilder(env, target, source) + return sdk_tree.emitter(target, source, env) + + +def gen_sdk_data(sdk_cache: SdkCache): + api_def = [] + api_def.extend( + (f"#include <{h.name}>" for h in sdk_cache.get_headers()), + ) + + api_def.append(f"const int elf_api_version = {sdk_cache.version.as_int()};") + + api_def.append( + "static constexpr auto elf_api_table = sort(create_array_t(" + ) + + api_lines = [] + for fun_def in sdk_cache.get_functions(): + api_lines.append( + f"API_METHOD({fun_def.name}, {fun_def.returns}, ({fun_def.params}))" + ) + + for var_def in sdk_cache.get_variables(): + api_lines.append(f"API_VARIABLE({var_def.name}, {var_def.var_type })") + + api_def.append(",\n".join(api_lines)) + + api_def.append("));") + return api_def + + +def _check_sdk_is_up2date(sdk_cache: SdkCache): + if not sdk_cache.is_buildable(): + raise UserError( + "SDK version is not finalized, please review changes and re-run operation. See AppsOnSDCard.md for more details" + ) + + +def _validate_api_cache(source, target, env): + # print(f"Generating SDK for {source[0]} to {target[0]}") + current_sdk = SdkCollector() + current_sdk.process_source_file_for_sdk(source[0].path) + for h in env["SDK_HEADERS"]: + current_sdk.add_header_to_sdk(pathlib.Path(h.srcnode().path).as_posix()) + + sdk_cache = SdkCache(target[0].path) + sdk_cache.validate_api(current_sdk.get_api()) + sdk_cache.save() + _check_sdk_is_up2date(sdk_cache) + + +def _generate_api_table(source, target, env): + sdk_cache = SdkCache(source[0].path) + _check_sdk_is_up2date(sdk_cache) + + api_def = gen_sdk_data(sdk_cache) + with open(target[0].path, "wt") as f: + f.write("\n".join(api_def)) + + +def generate(env, **kw): + if not env["VERBOSE"]: + env.SetDefault( + SDK_AMALGAMATE_HEADER_COMSTR="\tAPIPREP\t${TARGET}", + SDK_AMALGAMATE_PP_COMSTR="\tAPIPP\t${TARGET}", + SDKSYM_UPDATER_COMSTR="\tSDKCHK\t${TARGET}", + APITABLE_GENERATOR_COMSTR="\tAPITBL\t${TARGET}", + SDKTREE_COMSTR="\tSDKTREE\t${TARGET}", + ) + + # Filtering out things cxxheaderparser cannot handle + env.SetDefault( + SDK_PP_FLAGS=[ + '-D"_Static_assert(x,y)="', + '-D"__asm__(x)="', + '-D"__attribute__(x)="', + "-Drestrict=", + "-D_Noreturn=", + "-D__restrict=", + "-D__extension__=", + "-D__inline=inline", + "-D__inline__=inline", + ] + ) + + env.AddMethod(ProcessSdkDepends) + env.Append( + BUILDERS={ + "ApiAmalgamator": Builder( + emitter=_api_amalgam_emitter, + action=[ + Action( + _api_amalgam_gen_origin_header, + "$SDK_AMALGAMATE_HEADER_COMSTR", + ), + Action( + [ + [ + "$CC", + "-o", + "$TARGET", + "-E", + "-P", + "$CCFLAGS", + "$_CCCOMCOM", + "$SDK_PP_FLAGS", + "-MMD", + "${TARGET}.c", + ] + ], + "$SDK_AMALGAMATE_PP_COMSTR", + ), + ], + suffix=".i", + ), + "SDKHeaderTreeExtractor": Builder( + action=Action( + _deploy_sdk_header_tree_action, + "$SDKTREE_COMSTR", + ), + emitter=_deploy_sdk_header_tree_emitter, + src_suffix=".d", + ), + "ApiTableValidator": Builder( + action=Action( + _validate_api_cache, + "$SDKSYM_UPDATER_COMSTR", + ), + suffix=".csv", + src_suffix=".i", + ), + "ApiSymbolTable": Builder( + action=Action( + _generate_api_table, + "$APITABLE_GENERATOR_COMSTR", + ), + suffix=".h", + src_suffix=".csv", + ), + } + ) + + +def exists(env): + return True diff --git a/scripts/fbt_tools/fbt_tweaks.py b/scripts/fbt_tools/fbt_tweaks.py new file mode 100644 index 00000000000..68ac9d7d17c --- /dev/null +++ b/scripts/fbt_tools/fbt_tweaks.py @@ -0,0 +1,48 @@ +import os +import sys +import traceback + +import SCons.Warnings as Warnings +from ansi.color import fg +from SCons.Errors import UserError + +# from SCons.Script.Main import find_deepest_user_frame + + +def find_deepest_user_frame(tb): + tb.reverse() + + # find the deepest traceback frame that is not part + # of SCons: + for frame in tb: + filename = frame[0] + if filename.find("fbt_tweaks") != -1: + continue + if filename.find(os.sep + "SCons" + os.sep) == -1: + return frame + return tb[0] + + +def fbt_warning(e): + filename, lineno, routine, dummy = find_deepest_user_frame( + traceback.extract_stack() + ) + fbt_line = "\nfbt: warning: %s\n" % e.args[0] + sys.stderr.write(fg.boldmagenta(fbt_line)) + fbt_line = ( + fg.yellow("%s, line %d, " % (routine, lineno)) + 'in file "%s"\n' % filename + ) + sys.stderr.write(fg.yellow(fbt_line)) + + +def generate(env): + if env.get("UFBT_WORK_DIR"): + raise UserError( + "You're trying to use a new format SDK on a legacy ufbt version. " + "Please update ufbt to a version from PyPI: https://pypi.org/project/ufbt/" + ) + Warnings._warningOut = fbt_warning + + +def exists(): + return True diff --git a/scripts/fbt_tools/fbt_version.py b/scripts/fbt_tools/fbt_version.py new file mode 100644 index 00000000000..0dd5d0feb91 --- /dev/null +++ b/scripts/fbt_tools/fbt_version.py @@ -0,0 +1,46 @@ +from SCons.Action import Action +from SCons.Builder import Builder + + +def _version_emitter(target, source, env): + target_dir = target[0] + target = [ + target_dir.File("version.inc.h"), + target_dir.File("version.json"), + ] + return target, source + + +def generate(env): + env.SetDefault( + VERSION_SCRIPT="${FBT_SCRIPT_DIR}/version.py", + ) + env.Append( + BUILDERS={ + "VersionBuilder": Builder( + action=Action( + [ + [ + "${PYTHON3}", + "${VERSION_SCRIPT}", + "generate", + "-t", + "${TARGET_HW}", + "--fw-origin", + "${FIRMWARE_ORIGIN}", + "-o", + "${TARGET.dir.posix}", + "--dir", + "${ROOT_DIR}", + ] + ], + "${VERSIONCOMSTR}", + ), + emitter=_version_emitter, + ), + } + ) + + +def exists(env): + return True diff --git a/scripts/fbt_tools/fwbin.py b/scripts/fbt_tools/fwbin.py new file mode 100644 index 00000000000..860f83b1b98 --- /dev/null +++ b/scripts/fbt_tools/fwbin.py @@ -0,0 +1,76 @@ +import SCons +from SCons.Action import Action +from SCons.Builder import Builder + +__OBJCOPY_ARM_BIN = "arm-none-eabi-objcopy" +__NM_ARM_BIN = "arm-none-eabi-nm" + + +def generate(env): + env.SetDefault( + BIN2DFU="${FBT_SCRIPT_DIR}/bin2dfu.py", + BIN_SIZE_SCRIPT="${FBT_SCRIPT_DIR}/fwsize.py", + OBJCOPY=__OBJCOPY_ARM_BIN, # FIXME + NM=__NM_ARM_BIN, # FIXME + ) + + if not env["VERBOSE"]: + env.SetDefault( + HEXCOMSTR="\tHEX\t${TARGET}", + BINCOMSTR="\tBIN\t${TARGET}", + DFUCOMSTR="\tDFU\t${TARGET}", + ) + + env.Append( + BUILDERS={ + "HEXBuilder": Builder( + action=Action( + [["${OBJCOPY}", "-O", "ihex", "${SOURCE}", "${TARGET}"]], + "${HEXCOMSTR}", + ), + suffix=".hex", + src_suffix=".elf", + ), + "BINBuilder": Builder( + action=Action( + [["${OBJCOPY}", "-O", "binary", "-S", "${SOURCE}", "${TARGET}"]], + "${BINCOMSTR}", + ), + suffix=".bin", + src_suffix=".elf", + ), + "DFUBuilder": Builder( + action=Action( + [ + [ + "${PYTHON3}", + "${BIN2DFU}", + "-i", + "${SOURCE}", + "-o", + "${TARGET}", + "-a", + "${IMAGE_BASE_ADDRESS}", + "-l", + "Flipper Zero F${TARGET_HW}", + ] + ], + "${DFUCOMSTR}", + ), + suffix=".dfu", + src_suffix=".bin", + ), + } + ) + + +def exists(env): + try: + return env["OBJCOPY"] + except KeyError: + pass + + if objcopy := env.WhereIs(__OBJCOPY_ARM_BIN): + return objcopy + + raise SCons.Errors.StopError("Could not detect objcopy for arm") diff --git a/scripts/fbt_tools/gdb.py b/scripts/fbt_tools/gdb.py new file mode 100644 index 00000000000..ea29e9c92e8 --- /dev/null +++ b/scripts/fbt_tools/gdb.py @@ -0,0 +1,11 @@ +def generate(env): + env.SetDefault( + GDB="gdb", + GDBPY="gdb-py", + GDBCOM="$GDB $GDBOPTS $SOURCES", # no $TARGET + GDBPYCOM="$GDBPY $GDBOPTS $GDBPYOPTS $SOURCES", # no $TARGET + ) + + +def exists(env): + return True diff --git a/scripts/fbt_tools/jflash.py b/scripts/fbt_tools/jflash.py new file mode 100644 index 00000000000..5eb9f2c1932 --- /dev/null +++ b/scripts/fbt_tools/jflash.py @@ -0,0 +1,36 @@ +from SCons.Builder import Builder +from SCons.Defaults import Touch +from SCons.Action import Action + + +def generate(env): + env.SetDefault( + JFLASH="JFlash" if env.subst("$PLATFORM") == "win32" else "JFlashExe", + JFLASHFLAGS=[ + "-auto", + "-exit", + ], + ) + env.Append( + BUILDERS={ + "JFlash": Builder( + action=[ + Action( + [ + [ + "${JFLASH}", + "-openprj${JFLASHPROJECT}", + "-open${SOURCE},${JFLASHADDR}", + "${JFLASHFLAGS}", + ] + ] + ), + Touch("${TARGET}"), + ], + ), + } + ) + + +def exists(env): + return True diff --git a/site_scons/site_tools/objdump.py b/scripts/fbt_tools/objdump.py similarity index 81% rename from site_scons/site_tools/objdump.py rename to scripts/fbt_tools/objdump.py index f5fa938a7bd..e3dbc6d8759 100644 --- a/site_scons/site_tools/objdump.py +++ b/scripts/fbt_tools/objdump.py @@ -1,18 +1,17 @@ -from SCons.Builder import Builder from SCons.Action import Action +from SCons.Builder import Builder def generate(env): env.SetDefault( OBJDUMP="objdump", OBJDUMPFLAGS=[], - OBJDUMPCOM="$OBJDUMP $OBJDUMPFLAGS -S $SOURCES > $TARGET", ) env.Append( BUILDERS={ "ObjDump": Builder( action=Action( - "${OBJDUMPCOM}", + [["$OBJDUMP", "$OBJDUMPFLAGS", "-S", "$SOURCES", ">", "$TARGET"]], "${OBJDUMPCOMSTR}", ), suffix=".lst", diff --git a/scripts/fbt_tools/openocd.py b/scripts/fbt_tools/openocd.py new file mode 100644 index 00000000000..596f5f8a644 --- /dev/null +++ b/scripts/fbt_tools/openocd.py @@ -0,0 +1,47 @@ +import SCons +from SCons.Action import Action +from SCons.Builder import Builder +from SCons.Defaults import Touch + +__OPENOCD_BIN = "openocd" + +# TODO: FL-3663: rework argument passing to lists +_oocd_action = Action( + "${OPENOCD} ${OPENOCD_OPTS} ${OPENOCD_COMMAND}", + "${OPENOCDCOMSTR}", +) + + +def generate(env): + env.SetDefault( + OPENOCD=__OPENOCD_BIN, + OPENOCD_OPTS="", + OPENOCD_COMMAND="", + OPENOCDCOM="${OPENOCD} ${OPENOCD_OPTS} ${OPENOCD_COMMAND}", + OPENOCDCOMSTR="", + ) + + env.Append( + BUILDERS={ + "OpenOCDFlash": Builder( + action=[ + _oocd_action, + Touch("${TARGET}"), + ], + suffix=".flash", + src_suffix=".bin", + ), + } + ) + + +def exists(env): + try: + return env["OPENOCD"] + except KeyError: + pass + + if openocd := env.WhereIs(__OPENOCD_BIN): + return openocd + + raise SCons.Errors.StopError("Could not detect openocd") diff --git a/scripts/fbt_tools/pvsstudio.py b/scripts/fbt_tools/pvsstudio.py new file mode 100644 index 00000000000..ecf9d4b0681 --- /dev/null +++ b/scripts/fbt_tools/pvsstudio.py @@ -0,0 +1,128 @@ +import atexit +import multiprocessing +import subprocess +import sys +import webbrowser + +from SCons.Action import Action +from SCons.Builder import Builder +from SCons.Script import Delete, Flatten, GetBuildFailures, Mkdir + +__no_browser = False + + +def _set_browser_action(target, source, env): + if env["PVSNOBROWSER"]: + global __no_browser + __no_browser = True + + +def _emit_pvsreport(target, source, env): + target_dir = env["REPORT_DIR"] + if env["PLATFORM"] == "win32": + # Report generator on Windows emits to a subfolder of given output folder + target_dir = target_dir.Dir("fullhtml") + return [target_dir.File("index.html")], source + + +def atexist_handler(): + global __no_browser + if __no_browser: + return + + for bf in GetBuildFailures(): + for node in Flatten(bf.node): + if node.exists and node.name.endswith(".html"): + # macOS + if sys.platform == "darwin": + subprocess.run(["open", node.abspath]) + else: + webbrowser.open(node.abspath) + break + + +def generate(env): + env.SetDefault( + PVSNCORES=multiprocessing.cpu_count(), + PVSOPTIONS=[ + "@.pvsoptions", + "-j${PVSNCORES}", + # "--incremental", # kinda broken on PVS side + ], + PVSCONVOPTIONS=[ + "-a", + "GA:1,2,3", + "-t", + "fullhtml", + "--indicate-warnings", + ], + ) + + if env["PLATFORM"] == "win32": + env.SetDefault( + PVSCHECKBIN="CompilerCommandsAnalyzer.exe", + PVSCONVBIN="PlogConverter.exe", + ) + else: + env.SetDefault( + PVSCHECKBIN="pvs-studio-analyzer", + PVSCONVBIN="plog-converter", + ) + + if not env["VERBOSE"]: + env.SetDefault( + PVSCHECKCOMSTR="\tPVS\t${TARGET}", + PVSCONVCOMSTR="\tPVSREP\t${TARGET}", + ) + + env.Append( + BUILDERS={ + "PVSCheck": Builder( + action=Action( + [ + [ + "${PVSCHECKBIN}", + "analyze", + "${PVSOPTIONS}", + "-f", + "${SOURCE}", + "-o", + "${TARGET}", + ] + ], + "${PVSCHECKCOMSTR}", + ), + suffix=".log", + src_suffix=".json", + ), + "PVSReport": Builder( + action=Action( + [ + Delete("${TARGET.dir}"), + # PlogConverter.exe and plog-converter have different behavior + Mkdir("${TARGET.dir}") if env["PLATFORM"] == "win32" else None, + Action(_set_browser_action, None), + Action( + [ + [ + "${PVSCONVBIN}", + "${PVSCONVOPTIONS}", + "${SOURCE}", + "-o", + "${REPORT_DIR}", + ] + ] + ), + ], + "${PVSCONVCOMSTR}", + ), + emitter=_emit_pvsreport, + src_suffix=".log", + ), + } + ) + atexit.register(atexist_handler) + + +def exists(env): + return True diff --git a/site_scons/site_tools/python3.py b/scripts/fbt_tools/python3.py similarity index 100% rename from site_scons/site_tools/python3.py rename to scripts/fbt_tools/python3.py diff --git a/site_scons/site_tools/sconsmodular.py b/scripts/fbt_tools/sconsmodular.py similarity index 86% rename from site_scons/site_tools/sconsmodular.py rename to scripts/fbt_tools/sconsmodular.py index 778c664e47c..4dc2079a6e3 100644 --- a/site_scons/site_tools/sconsmodular.py +++ b/scripts/fbt_tools/sconsmodular.py @@ -1,5 +1,6 @@ -import posixpath import os +import posixpath + from SCons.Errors import UserError @@ -22,6 +23,8 @@ def BuildModule(env, module): def BuildModules(env, modules): result = [] for module in modules: + if module in env.get("SKIP_MODULES", []): + continue build_res = env.BuildModule(module) # print("module ", module, build_res) if build_res is None: @@ -40,10 +43,15 @@ def PhonyTarget(env, name, action, source=None, **kw): return command +def ChangeFileExtension(env, fnode, ext): + return env.File(f"#{os.path.splitext(fnode.path)[0]}{ext}") + + def generate(env): env.AddMethod(BuildModule) env.AddMethod(BuildModules) env.AddMethod(PhonyTarget) + env.AddMethod(ChangeFileExtension) def exists(env): diff --git a/scripts/fbt_tools/sconsrecursiveglob.py b/scripts/fbt_tools/sconsrecursiveglob.py new file mode 100644 index 00000000000..de64c76b7b3 --- /dev/null +++ b/scripts/fbt_tools/sconsrecursiveglob.py @@ -0,0 +1,60 @@ +import itertools + +import SCons +from fbt.util import GLOB_FILE_EXCLUSION +from SCons.Node.FS import has_glob_magic +from SCons.Script import Flatten + + +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) + # Only initiate actual recursion if special symbols can be found in 'pattern' + if has_glob_magic(pattern): + for f in node.glob("*", source=True, exclude=exclude): + if isinstance(f, SCons.Node.FS.Dir): + results += env.GlobRecursive(pattern, f, exclude) + results += node.glob( + pattern, + source=True, + exclude=exclude, + ) + # Otherwise, just assume that file at path exists + else: + results.append(node.File(pattern)) + ## Debug + # print(f"Glob result for {pattern} from {node}: {results}") + return results + + +def GatherSources(env, sources_list, node="."): + sources_list = list(set(Flatten(sources_list))) + include_sources = list(filter(lambda x: not x.startswith("!"), sources_list)) + exclude_sources = list(x[1:] for x in sources_list if x.startswith("!")) + gathered_sources = list( + itertools.chain.from_iterable( + env.GlobRecursive( + source_type, + node, + exclude=exclude_sources, + ) + for source_type in include_sources + ) + ) + ## Debug + # print( + # f"Gathered sources for {sources_list} from {node}: {list(f.path for f in gathered_sources)}" + # ) + return gathered_sources + + +def generate(env): + env.AddMethod(GlobRecursive) + env.AddMethod(GatherSources) + + +def exists(env): + return True diff --git a/site_scons/site_tools/strip.py b/scripts/fbt_tools/strip.py similarity index 82% rename from site_scons/site_tools/strip.py rename to scripts/fbt_tools/strip.py index 053956f22e0..39f3a620cf1 100644 --- a/site_scons/site_tools/strip.py +++ b/scripts/fbt_tools/strip.py @@ -1,18 +1,17 @@ -from SCons.Builder import Builder from SCons.Action import Action +from SCons.Builder import Builder def generate(env): env.SetDefault( STRIP="strip", STRIPFLAGS=[], - STRIPCOM="$STRIP $STRIPFLAGS $SOURCES -o $TARGET", ) env.Append( BUILDERS={ "ELFStripper": Builder( action=Action( - "${STRIPCOM}", + [["$STRIP", "$STRIPFLAGS", "$SOURCES", "-o", "$TARGET"]], "${STRIPCOMSTR}", ), suffix=".elf", diff --git a/scripts/flash.py b/scripts/flash.py index fb05b8b0b85..6189dc1a28e 100755 --- a/scripts/flash.py +++ b/scripts/flash.py @@ -1,13 +1,9 @@ #!/usr/bin/env python3 -import logging -import argparse -import sys -import os from flipper.app import App -from flipper.cube import CubeProgrammer from flipper.assets.coprobin import CoproBinary +from flipper.cube import CubeProgrammer STATEMENT = "AGREE_TO_LOSE_FLIPPER_FEATURES_THAT_USE_CRYPTO_ENCLAVE" @@ -94,59 +90,59 @@ def _getCubeParams(self): } def wipe(self): - self.logger.info(f"Wiping flash") + self.logger.info("Wiping flash") cp = CubeProgrammer(self._getCubeParams()) - self.logger.info(f"Setting RDP to 0xBB") + self.logger.info("Setting RDP to 0xBB") cp.setOptionBytes({"RDP": ("0xBB", "rw")}) - self.logger.info(f"Verifying RDP") + self.logger.info("Verifying RDP") r = cp.checkOptionBytes({"RDP": ("0xBB", "rw")}) - assert r == True + assert r is True self.logger.info(f"Result: {r}") - self.logger.info(f"Setting RDP to 0xAA") + self.logger.info("Setting RDP to 0xAA") cp.setOptionBytes({"RDP": ("0xAA", "rw")}) - self.logger.info(f"Verifying RDP") + self.logger.info("Verifying RDP") r = cp.checkOptionBytes({"RDP": ("0xAA", "rw")}) - assert r == True + assert r is True self.logger.info(f"Result: {r}") - self.logger.info(f"Complete") + self.logger.info("Complete") return 0 def core1bootloader(self): - self.logger.info(f"Flashing bootloader") + self.logger.info("Flashing bootloader") cp = CubeProgrammer(self._getCubeParams()) cp.flashBin("0x08000000", self.args.bootloader) - self.logger.info(f"Complete") + self.logger.info("Complete") cp.resetTarget() return 0 def core1firmware(self): - self.logger.info(f"Flashing firmware") + self.logger.info("Flashing firmware") cp = CubeProgrammer(self._getCubeParams()) cp.flashBin("0x08008000", self.args.firmware) - self.logger.info(f"Complete") + self.logger.info("Complete") cp.resetTarget() return 0 def core1(self): - self.logger.info(f"Flashing bootloader") + self.logger.info("Flashing bootloader") cp = CubeProgrammer(self._getCubeParams()) cp.flashBin("0x08000000", self.args.bootloader) - self.logger.info(f"Flashing firmware") + self.logger.info("Flashing firmware") cp.flashBin("0x08008000", self.args.firmware) cp.resetTarget() - self.logger.info(f"Complete") + self.logger.info("Complete") return 0 def core2fus(self): if self.args.statement != STATEMENT: self.logger.error( - f"PLEASE DON'T. THIS FEATURE INTENDED ONLY FOR FACTORY FLASHING" + "PLEASE DON'T. THIS FEATURE INTENDED ONLY FOR FACTORY FLASHING" ) return 1 - self.logger.info(f"Flashing Firmware Update Service") + self.logger.info("Flashing Firmware Update Service") cp = CubeProgrammer(self._getCubeParams()) cp.flashCore2(self.args.fus_address, self.args.fus) - self.logger.info(f"Complete") + self.logger.info("Complete") return 0 def core2radio(self): @@ -163,15 +159,15 @@ def core2radio(self): f"Radio address not provided, guessed as 0x{radio_address:X}" ) if radio_address > 0x080E0000: - self.logger.error(f"I KNOW WHAT YOU DID LAST SUMMER") + self.logger.error("I KNOW WHAT YOU DID LAST SUMMER") return 1 cp = CubeProgrammer(self._getCubeParams()) - self.logger.info(f"Removing Current Radio Stack") + self.logger.info("Removing Current Radio Stack") cp.deleteCore2RadioStack() - self.logger.info(f"Flashing Radio Stack") + self.logger.info("Flashing Radio Stack") cp.flashCore2(radio_address, self.args.radio) - self.logger.info(f"Complete") + self.logger.info("Complete") return 0 diff --git a/scripts/flipper/app.py b/scripts/flipper/app.py index 9583560212c..da43a1f1151 100644 --- a/scripts/flipper/app.py +++ b/scripts/flipper/app.py @@ -1,6 +1,7 @@ import logging import argparse import sys +import colorlog class App: @@ -10,21 +11,27 @@ def __init__(self, no_exit=False): self.parser = argparse.ArgumentParser() self.parser.add_argument("-d", "--debug", action="store_true", help="Debug") # Logging - self.logger = logging.getLogger() + self.logger = colorlog.getLogger() # Application specific initialization self.init() - def __call__(self, args=None, skip_logger_init=False): + def __call__(self, args=None): self.args, self.other_args = self.parser.parse_known_args(args=args) # configure log output - # if skip_logger_init: self.log_level = logging.DEBUG if self.args.debug else logging.INFO self.logger.setLevel(self.log_level) if not self.logger.hasHandlers(): - self.handler = logging.StreamHandler(sys.stdout) + self.handler = colorlog.StreamHandler(sys.stdout) self.handler.setLevel(self.log_level) - self.formatter = logging.Formatter( - "%(asctime)s [%(levelname)s] %(message)s" + self.formatter = colorlog.ColoredFormatter( + "%(log_color)s%(asctime)s [%(levelname)s] %(message)s", + log_colors={ + "DEBUG": "cyan", + # "INFO": "white", + "WARNING": "yellow", + "ERROR": "red", + "CRITICAL": "red,bg_white", + }, ) self.handler.setFormatter(self.formatter) self.logger.addHandler(self.handler) @@ -36,7 +43,7 @@ def __call__(self, args=None, skip_logger_init=False): if isinstance(return_code, int): return self._exit(return_code) else: - self.logger.error(f"Missing return code") + self.logger.error("Missing return code") return self._exit(255) def _exit(self, code): diff --git a/scripts/flipper/assets/copro.py b/scripts/flipper/assets/copro.py index 0c78e889d2e..25c072899ea 100644 --- a/scripts/flipper/assets/copro.py +++ b/scripts/flipper/assets/copro.py @@ -1,15 +1,14 @@ -import logging -import datetime -import shutil import json -from os.path import basename +import logging +import os +import posixpath +import tarfile +from io import BytesIO -import xml.etree.ElementTree as ET -from flipper.utils import * from flipper.assets.coprobin import CoproBinary, get_stack_type +from flipper.utils import file_sha256, timestamp - -CUBE_COPRO_PATH = "Projects/STM32WB_Copro_Wireless_Binaries" +CUBE_COPRO_PATH = "firmware" MANIFEST_TEMPLATE = { "manifest": {"version": 0, "timestamp": 0}, @@ -24,47 +23,62 @@ class Copro: - def __init__(self, mcu): - self.mcu = mcu + COPRO_TAR_DIR = "core2_firmware" + + def __init__(self): self.version = None self.cube_dir = None self.mcu_copro = None self.logger = logging.getLogger(self.__class__.__name__) - def loadCubeInfo(self, cube_dir, cube_version): + def loadCubeInfo(self, cube_dir, reference_cube_version): if not os.path.isdir(cube_dir): raise Exception(f'"{cube_dir}" doesn\'t exists') self.cube_dir = cube_dir - self.mcu_copro = os.path.join(self.cube_dir, CUBE_COPRO_PATH, self.mcu) + self.mcu_copro = os.path.join(self.cube_dir, CUBE_COPRO_PATH) if not os.path.isdir(self.mcu_copro): raise Exception(f'"{self.mcu_copro}" doesn\'t exists') - cube_manifest_file = os.path.join(self.cube_dir, "package.xml") - cube_manifest = ET.parse(cube_manifest_file) - cube_package = cube_manifest.find("PackDescription") - if not cube_package: - raise Exception(f"Unknown Cube manifest format") - cube_version = cube_package.get("Patch") or cube_package.get("Release") - if not cube_version or not cube_version.startswith("FW.WB"): - raise Exception(f"Incorrect Cube package or version info") - cube_version = cube_version.replace("FW.WB.", "", 1) - if cube_version != cube_version: - raise Exception(f"Unsupported cube version") + try: + cube_manifest_file = os.path.join(self.cube_dir, "VERSION") + with open(cube_manifest_file, "r") as cube_manifest: + cube_version = cube_manifest.read().strip() + except IOError: + raise Exception(f"Failed to read version from {cube_manifest_file}") + + if not cube_version.startswith("v"): + raise Exception(f"Invalid cube version: {cube_version}") + cube_version = cube_version[1:] + + if cube_version != reference_cube_version: + raise Exception( + f"Unsupported cube version: {cube_version}, expecting {reference_cube_version}" + ) self.version = cube_version + def _getFileName(self, name): + return posixpath.join(self.COPRO_TAR_DIR, name) + + def _addFileReadPermission(self, tarinfo): + tarinfo.mode = 0o644 + return tarinfo + def addFile(self, array, filename, **kwargs): source_file = os.path.join(self.mcu_copro, filename) - destination_file = os.path.join(self.output_dir, filename) - shutil.copyfile(source_file, destination_file) - array.append( - {"name": filename, "sha256": file_sha256(destination_file), **kwargs} + self.output_tar.add( + source_file, + arcname=self._getFileName(filename), + filter=self._addFileReadPermission, ) + array.append({"name": filename, "sha256": file_sha256(source_file), **kwargs}) + + def bundle(self, output_file, stack_file_name, stack_type, stack_addr=None): + self.output_tar = tarfile.open(output_file, "w:gz", format=tarfile.USTAR_FORMAT) + fw_directory = tarfile.TarInfo(self.COPRO_TAR_DIR) + fw_directory.mode = 0o755 + fw_directory.type = tarfile.DIRTYPE + self.output_tar.addfile(fw_directory) - def bundle(self, output_dir, stack_file_name, stack_type, stack_addr=None): - if not os.path.isdir(output_dir): - raise Exception(f'"{output_dir}" doesn\'t exists') - self.output_dir = output_dir stack_file = os.path.join(self.mcu_copro, stack_file_name) - manifest_file = os.path.join(self.output_dir, "Manifest.json") # Form Manifest manifest = dict(MANIFEST_TEMPLATE) manifest["manifest"]["timestamp"] = timestamp() @@ -105,6 +119,10 @@ def bundle(self, output_dir, stack_file_name, stack_type, stack_addr=None): stack_file_name, address=f"0x{stack_addr:X}", ) - # Save manifest to - with open(manifest_file, "w", newline="\n") as file: - json.dump(manifest, file) + + # Save manifest + manifest_data = json.dumps(manifest, indent=4).encode("utf-8") + info = tarfile.TarInfo(self._getFileName("Manifest.json")) + info.size = len(manifest_data) + self.output_tar.addfile(info, BytesIO(manifest_data)) + self.output_tar.close() diff --git a/scripts/flipper/assets/coprobin.py b/scripts/flipper/assets/coprobin.py index 64f0b8c8718..84f52fbb3eb 100644 --- a/scripts/flipper/assets/coprobin.py +++ b/scripts/flipper/assets/coprobin.py @@ -1,10 +1,11 @@ import struct import math -import os, os.path +import os +import os.path import sys -# From STM32CubeWB\Middlewares\ST\STM32_WPAN\interface\patterns\ble_thread\shci\shci.h +# From lib/stm32wb_copro/wpan/interface/patterns/ble_thread/shci/shci.h __STACK_TYPE_CODES = { "BLE_FULL": 0x01, "BLE_HCI": 0x02, @@ -45,7 +46,10 @@ class CoproFooterBase: _SIG_BIN_COMMON_SIZE = 2 * 4 def get_version(self): - return f"Version {self.version_major}.{self.version_minor}.{self.version_sub}, branch {self.version_branch}, build {self.version_build} (magic {self.magic:X})" + return ( + f"Version {self.version_major}.{self.version_minor}.{self.version_sub}, " + f"branch {self.version_branch}, build {self.version_build} (magic {self.magic:X})" + ) def get_details(self): raise CoproException("Not implemented") diff --git a/scripts/flipper/assets/dolphin.py b/scripts/flipper/assets/dolphin.py index 096d31629f3..cf98c8253c8 100644 --- a/scripts/flipper/assets/dolphin.py +++ b/scripts/flipper/assets/dolphin.py @@ -1,13 +1,11 @@ import multiprocessing import logging import os -import sys -import shutil from collections import Counter -from flipper.utils.fff import * -from flipper.utils.templite import * -from .icon import * +from flipper.utils.fff import FlipperFormatFile +from flipper.utils.templite import Templite +from .icon import ImageTools, file2image def _convert_image_to_bm(pair: set): @@ -22,7 +20,6 @@ def _convert_image(source_filename: str): class DolphinBubbleAnimation: - FILE_TYPE = "Flipper Animation" FILE_VERSION = 1 @@ -52,13 +49,13 @@ def __init__( def load(self, animation_directory: str): if not os.path.isdir(animation_directory): - raise Exception(f"Animation folder doesn't exists: { animation_directory }") + raise Exception(f"Animation folder doesn't exist: { animation_directory }") meta_filename = os.path.join(animation_directory, "meta.txt") if not os.path.isfile(meta_filename): - raise Exception(f"Animation meta file doesn't exists: { meta_filename }") + raise Exception(f"Animation meta file doesn't exist: { meta_filename }") - self.logger.info(f"Loading meta from {meta_filename}") + self.logger.debug(f"Loading meta from {meta_filename}") file = FlipperFormatFile() file.load(meta_filename) @@ -122,7 +119,7 @@ def load(self, animation_directory: str): self.meta["Passive frames"] + self.meta["Active frames"] == ordered_frames_count ) - except EOFError as e: + except EOFError: raise Exception("Invalid meta file: too short") except AssertionError as e: self.logger.exception(e) @@ -159,7 +156,7 @@ def load(self, animation_directory: str): except AssertionError as e: self.logger.exception(e) self.logger.error( - f"Animation {self.name} bubble slot {bubble_slot} got incorrect data: {bubble}" + f"Animation {self.name} bubble slot {bubble['Slot']} got incorrect data: {bubble}" ) raise Exception("Meta file is invalid: incorrect bubble data") except EOFError: @@ -243,7 +240,6 @@ def process(self): class DolphinManifest: - FILE_TYPE = "Flipper Animation Manifest" FILE_VERSION = 1 @@ -271,7 +267,7 @@ def load(self, source_directory: str): # Load animation data while True: try: - # Read animation spcification + # Read animation specification name = file.readKey("Name") min_butthurt = file.readKeyInt("Min butthurt") max_butthurt = file.readKeyInt("Max butthurt") diff --git a/scripts/flipper/assets/icon.py b/scripts/flipper/assets/icon.py index 235af7b05ec..d5d2a585ee6 100644 --- a/scripts/flipper/assets/icon.py +++ b/scripts/flipper/assets/icon.py @@ -1,9 +1,6 @@ import logging -import argparse import subprocess import io -import os -import sys ICONS_SUPPORTED_FORMATS = ["png"] @@ -36,11 +33,8 @@ class ImageTools: @staticmethod def is_processing_slow(): try: - from PIL import Image, ImageOps - import heatshrink2 - return False - except ImportError as e: + except ImportError: return True def __init__(self): @@ -52,7 +46,7 @@ def png2xbm(self, file): try: from PIL import Image, ImageOps - except ImportError as e: + except ImportError: self.__pil_unavailable = True self.logger.info("pillow module is missing, using convert cli util") return self.png2xbm(file) @@ -72,7 +66,7 @@ def xbm2hs(self, data): try: import heatshrink2 - except ImportError as e: + except ImportError: self.__hs2_unavailable = True self.logger.info("heatshrink2 module is missing, using heatshrink cli util") return self.xbm2hs(data) @@ -104,8 +98,8 @@ def file2image(file): data_enc = bytearray(data_encoded_str) data_enc = bytearray([len(data_enc) & 0xFF, len(data_enc) >> 8]) + data_enc - # Use encoded data only if its lenght less than original, including header - if len(data_enc) < len(data_bin) + 1: + # Use encoded data only if its length less than original, including header + 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/assets/manifest.py b/scripts/flipper/assets/manifest.py index 840761d729d..a9bbb8dacd8 100644 --- a/scripts/flipper/assets/manifest.py +++ b/scripts/flipper/assets/manifest.py @@ -1,11 +1,10 @@ -import datetime import logging import os import posixpath from pathlib import Path -from flipper.utils import * -from flipper.utils.fstree import * +from flipper.utils import timestamp, file_md5 +from flipper.utils.fstree import FsNode, compare_fs_trees MANIFEST_VERSION = 0 @@ -106,11 +105,11 @@ def toLine(self): class Manifest: - def __init__(self): + def __init__(self, timestamp_value=None): self.version = None self.records = [] self.records.append(ManifestRecordVersion(MANIFEST_VERSION)) - self.records.append(ManifestRecordTimestamp(timestamp())) + self.records.append(ManifestRecordTimestamp(timestamp_value or timestamp())) self.logger = logging.getLogger(self.__class__.__name__) def load(self, filename): diff --git a/scripts/flipper/assets/obdata.py b/scripts/flipper/assets/obdata.py index 0f7f5c19281..eb35d0e1745 100644 --- a/scripts/flipper/assets/obdata.py +++ b/scripts/flipper/assets/obdata.py @@ -1,7 +1,5 @@ #!/usr/bin/env python3 -import logging -import struct from enum import Enum from dataclasses import dataclass @@ -181,7 +179,7 @@ def __init__(self, obfname): def gen_values(self): obref = ObReferenceValuesGenerator() - converted_refs = list(obref.apply(ob) for ob in self.obs) + list(obref.apply(ob) for ob in self.obs) return obref diff --git a/scripts/flipper/cube.py b/scripts/flipper/cube.py index 38aa54a8548..e4f9876df11 100644 --- a/scripts/flipper/cube.py +++ b/scripts/flipper/cube.py @@ -14,7 +14,7 @@ def __init__(self, config={}): if "port" in config and config["port"]: connect.append(f"port={config['port']}") else: - connect.append(f"port=swd") + connect.append("port=swd") if "serial" in config and config["serial"]: connect.append(f"sn={config['serial']}") self.params.append("-c " + " ".join(connect)) @@ -43,20 +43,20 @@ def _execute(self, args): return output.decode() def getVersion(self): - output = self._execute(["--version"]) + self._execute(["--version"]) def checkOptionBytes(self, option_bytes): output = self._execute(["-ob displ"]) ob_correct = True for line in output.split("\n"): line = line.strip() - if not ":" in line: + if ":" not in line: self.logger.debug(f"Skipping line: {line}") continue key, data = line.split(":", 1) key = key.strip() data = data.strip() - if not key in option_bytes.keys(): + if key not in option_bytes.keys(): self.logger.debug(f"Skipping key: {key}") continue self.logger.debug(f"Processing key: {key} {data}") diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index 5fa8a2c8101..40af5cebc80 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -1,9 +1,13 @@ +import enum +import hashlib +import logging +import math import os +import posixpath import sys -import serial import time -import hashlib -import math + +import serial def timing(func): @@ -25,12 +29,47 @@ def wrapper(*args, **kwargs): return wrapper +class StorageErrorCode(enum.Enum): + OK = "OK" + NOT_READY = "filesystem not ready" + EXIST = "file/dir already exist" + NOT_EXIST = "file/dir not exist" + INVALID_PARAMETER = "invalid parameter" + DENIED = "access denied" + INVALID_NAME = "invalid name/path" + INTERNAL = "internal error" + NOT_IMPLEMENTED = "function not implemented" + ALREADY_OPEN = "file is already open" + UNKNOWN = "unknown error" + + @property + def is_error(self): + return self != self.OK + + @classmethod + def from_value(cls, s: str | bytes): + if isinstance(s, bytes): + s = s.decode("ascii") + for code in cls: + if code.value == s: + return code + return cls.UNKNOWN + + +class FlipperStorageException(Exception): + @staticmethod + def from_error_code(path: str, error_code: StorageErrorCode): + return FlipperStorageException( + f"Storage error: path '{path}': {error_code.value}" + ) + + class BufferedRead: def __init__(self, stream): self.buffer = bytearray() self.stream = stream - def until(self, eol="\n", cut_eol=True): + def until(self, eol: str = "\n", cut_eol: bool = True): eol = eol.encode("ascii") while True: # search in buffer @@ -59,9 +98,15 @@ def __init__(self, portname: str, chunk_size: int = 8192): self.port.timeout = 2 self.port.baudrate = 115200 # Doesn't matter for VCP self.read = BufferedRead(self.port) - self.last_error = "" self.chunk_size = chunk_size + def __enter__(self): + self.start() + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.stop() + def start(self): self.port.open() self.port.reset_input_buffer() @@ -71,46 +116,43 @@ def start(self): # And read buffer until we get prompt self.read.until(self.CLI_PROMPT) - def stop(self): + def stop(self) -> None: self.port.close() - def send(self, line): + def send(self, line: str) -> None: self.port.write(line.encode("ascii")) - def send_and_wait_eol(self, line): + def send_and_wait_eol(self, line: str): self.send(line) return self.read.until(self.CLI_EOL) - def send_and_wait_prompt(self, line): + def send_and_wait_prompt(self, line: str): self.send(line) return self.read.until(self.CLI_PROMPT) - def has_error(self, data): - """Is data has error""" - if data.find(b"Storage error") != -1: - return True - else: - return False + def has_error(self, data: bytes | str) -> bool: + """Is data an error message""" + return data.find(b"Storage error:") != -1 - def get_error(self, data): + def get_error(self, data: bytes) -> StorageErrorCode: """Extract error text from data and print it""" - error, error_text = data.decode("ascii").split(": ") - return error_text.strip() + _, error_text = data.decode("ascii").split(": ") + return StorageErrorCode.from_value(error_text.strip()) - def list_tree(self, path="/", level=0): + def list_tree(self, path: str = "/", level: int = 0): """List files and dirs on Flipper""" path = path.replace("//", "/") - self.send_and_wait_eol('storage list "' + path + '"\r') + self.send_and_wait_eol(f'storage list "{path}"\r') data = self.read.until(self.CLI_PROMPT) lines = data.split(b"\r\n") for line in lines: try: - # TODO: better decoding, considering non-ascii characters + # TODO FL-3539: better decoding, considering non-ascii characters line = line.decode("ascii") - except: + except Exception: continue line = line.strip() @@ -139,7 +181,7 @@ def list_tree(self, path="/", level=0): # Something wrong, pass pass - def walk(self, path="/"): + def walk(self, path: str = "/"): dirs = [] nondirs = [] walk_dirs = [] @@ -151,9 +193,9 @@ def walk(self, path="/"): for line in lines: try: - # TODO: better decoding, considering non-ascii characters + # TODO FL-3539: better decoding, considering non-ascii characters line = line.decode("ascii") - except: + except Exception: continue line = line.strip() @@ -181,19 +223,21 @@ def walk(self, path="/"): # Something wrong, pass pass - # topdown walk, yield before recursy + # topdown walk, yield before recursing yield path, dirs, nondirs for new_path in walk_dirs: yield from self.walk(new_path) - def send_file(self, filename_from, filename_to): + def send_file(self, filename_from: str, filename_to: str): """Send file from local device to Flipper""" - self.remove(filename_to) + if self.exist_file(filename_to): + self.remove(filename_to) with open(filename_from, "rb") as file: filesize = os.fstat(file.fileno()).st_size buffer_size = self.chunk_size + start_time = time.time() while True: filedata = file.read(buffer_size) size = len(filedata) @@ -203,156 +247,147 @@ def send_file(self, filename_from, filename_to): self.send_and_wait_eol(f'storage write_chunk "{filename_to}" {size}\r') answer = self.read.until(self.CLI_EOL) if self.has_error(answer): - self.last_error = self.get_error(answer) + last_error = self.get_error(answer) self.read.until(self.CLI_PROMPT) - return False + raise FlipperStorageException.from_error_code( + filename_to, last_error + ) self.port.write(filedata) self.read.until(self.CLI_PROMPT) - percent = str(math.ceil(file.tell() / filesize * 100)) - total_chunks = str(math.ceil(filesize / buffer_size)) - current_chunk = str(math.ceil(file.tell() / buffer_size)) + ftell = file.tell() + percent = math.ceil(ftell / filesize * 100) + total_chunks = math.ceil(filesize / buffer_size) + current_chunk = math.ceil(ftell / buffer_size) + approx_speed = ftell / (time.time() - start_time + 0.0001) sys.stdout.write( - f"\r{percent}%, chunk {current_chunk} of {total_chunks}" + f"\r<{percent:3d}%, chunk {current_chunk:2d} of {total_chunks:2d} @ {approx_speed/1024:.2f} kb/s" ) sys.stdout.flush() print() - return True - def read_file(self, filename): + def read_file(self, filename: str): """Receive file from Flipper, and get filedata (bytes)""" buffer_size = self.chunk_size + start_time = time.time() self.send_and_wait_eol( 'storage read_chunks "' + filename + '" ' + str(buffer_size) + "\r" ) answer = self.read.until(self.CLI_EOL) filedata = bytearray() if self.has_error(answer): - self.last_error = self.get_error(answer) + last_error = self.get_error(answer) self.read.until(self.CLI_PROMPT) - return filedata + raise FlipperStorageException(filename, last_error) + # return filedata size = int(answer.split(b": ")[1]) read_size = 0 while read_size < size: self.read.until("Ready?" + self.CLI_EOL) self.send("y") - read_size = min(size - read_size, buffer_size) - filedata.extend(self.port.read(read_size)) - read_size = read_size + read_size - - percent = str(math.ceil(read_size / size * 100)) - total_chunks = str(math.ceil(size / buffer_size)) - current_chunk = str(math.ceil(read_size / buffer_size)) - sys.stdout.write(f"\r{percent}%, chunk {current_chunk} of {total_chunks}") + chunk_size = min(size - read_size, buffer_size) + filedata.extend(self.port.read(chunk_size)) + read_size = read_size + chunk_size + + percent = math.ceil(read_size / size * 100) + total_chunks = math.ceil(size / buffer_size) + current_chunk = math.ceil(read_size / buffer_size) + approx_speed = read_size / (time.time() - start_time + 0.0001) + sys.stdout.write( + f"\r>{percent:3d}%, chunk {current_chunk:2d} of {total_chunks:2d} @ {approx_speed/1024:.2f} kb/s" + ) sys.stdout.flush() print() self.read.until(self.CLI_PROMPT) return filedata - def receive_file(self, filename_from, filename_to): + def receive_file(self, filename_from: str, filename_to: str): """Receive file from Flipper to local storage""" with open(filename_to, "wb") as file: data = self.read_file(filename_from) - if not data: - return False - else: - file.write(data) - return True + file.write(data) - def exist(self, path): - """Is file or dir exist on Flipper""" - self.send_and_wait_eol('storage stat "' + path + '"\r') - answer = self.read.until(self.CLI_EOL) + def exist(self, path: str): + """Does file or dir exist on Flipper""" + self.send_and_wait_eol(f'storage stat "{path}"\r') + response = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - return True + return not self.has_error(response) - def exist_dir(self, path): - """Is dir exist on Flipper""" - self.send_and_wait_eol('storage stat "' + path + '"\r') - answer = self.read.until(self.CLI_EOL) + def exist_dir(self, path: str): + """Does dir exist on Flipper""" + self.send_and_wait_eol(f'storage stat "{path}"\r') + response = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) - - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - if answer.find(b"Directory") != -1: - return True - elif answer.find(b"Storage") != -1: - return True - else: + if self.has_error(response): + error_code = self.get_error(response) + if error_code in ( + StorageErrorCode.NOT_EXIST, + StorageErrorCode.INVALID_NAME, + ): return False + raise FlipperStorageException.from_error_code(path, error_code) - def exist_file(self, path): - """Is file exist on Flipper""" - self.send_and_wait_eol('storage stat "' + path + '"\r') - answer = self.read.until(self.CLI_EOL) + return response == b"Directory" or response.startswith(b"Storage") + + def exist_file(self, path: str): + """Does file exist on Flipper""" + self.send_and_wait_eol(f'storage stat "{path}"\r') + response = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - if answer.find(b"File, size:") != -1: - return True - else: - return False + return response.find(b"File, size:") != -1 + + def _check_no_error(self, response, path=None): + if self.has_error(response): + raise FlipperStorageException.from_error_code( + path, self.get_error(response) + ) - def size(self, path): + def size(self, path: str): """file size on Flipper""" - self.send_and_wait_eol('storage stat "' + path + '"\r') - answer = self.read.until(self.CLI_EOL) + self.send_and_wait_eol(f'storage stat "{path}"\r') + response = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - if answer.find(b"File, size:") != -1: - size = int( - "".join( - ch - for ch in answer.split(b": ")[1].decode("ascii") - if ch.isdigit() - ) + self._check_no_error(response, path) + if response.find(b"File, size:") != -1: + size = int( + "".join( + ch + for ch in response.split(b": ")[1].decode("ascii") + if ch.isdigit() ) - return size - else: - self.last_error = "access denied" - return -1 + ) + return size + raise FlipperStorageException("Not a file") - def mkdir(self, path): + def mkdir(self, path: str): """Create a directory on Flipper""" - self.send_and_wait_eol('storage mkdir "' + path + '"\r') - answer = self.read.until(self.CLI_EOL) + self.send_and_wait_eol(f'storage mkdir "{path}"\r') + response = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) + self._check_no_error(response, path) - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - return True + def format_ext(self): + """Format external storage on Flipper""" + self.send_and_wait_eol("storage format /ext\r") + self.send_and_wait_eol("y\r") + response = self.read.until(self.CLI_EOL) + self.read.until(self.CLI_PROMPT) + self._check_no_error(response, "/ext") - def remove(self, path): + def remove(self, path: str): """Remove file or directory on Flipper""" - self.send_and_wait_eol('storage remove "' + path + '"\r') - answer = self.read.until(self.CLI_EOL) + self.send_and_wait_eol(f'storage remove "{path}"\r') + response = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) + self._check_no_error(response, path) - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - return True - - def hash_local(self, filename): + def hash_local(self, filename: str): """Hash of local file""" hash_md5 = hashlib.md5() with open(filename, "rb") as f: @@ -360,14 +395,112 @@ def hash_local(self, filename): hash_md5.update(chunk) return hash_md5.hexdigest() - def hash_flipper(self, filename): + def hash_flipper(self, filename: str): """Get hash of file on Flipper""" self.send_and_wait_eol('storage md5 "' + filename + '"\r') hash = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) + self._check_no_error(hash, filename) + return hash.decode("ascii") + + +class FlipperStorageOperations: + def __init__(self, storage): + self.storage: FlipperStorage = storage + self.logger = logging.getLogger("FStorageOps") + + def send_file_to_storage( + self, flipper_file_path: str, local_file_path: str, force: bool = False + ): + self.logger.debug( + f"* send_file_to_storage: {local_file_path}->{flipper_file_path}, {force=}" + ) + exists = self.storage.exist_file(flipper_file_path) + do_upload = not exists + if exists: + hash_local = self.storage.hash_local(local_file_path) + hash_flipper = self.storage.hash_flipper(flipper_file_path) + self.logger.debug(f"hash check: local {hash_local}, flipper {hash_flipper}") + do_upload = force or (hash_local != hash_flipper) + + if do_upload: + self.logger.info(f'Sending "{local_file_path}" to "{flipper_file_path}"') + self.storage.send_file(local_file_path, flipper_file_path) + + # make directory with exist check + def mkpath(self, flipper_dir_path: str): + path_components, dirs_to_create = flipper_dir_path.split("/"), [] + while not self.storage.exist_dir(dir_path := "/".join(path_components)): + self.logger.debug(f'"{dir_path}" does not exist, will create') + dirs_to_create.append(path_components.pop()) + for dir_to_create in reversed(dirs_to_create): + path_components.append(dir_to_create) + self.storage.mkdir("/".join(path_components)) + + # send file or folder recursively + def recursive_send(self, flipper_path: str, local_path: str, force: bool = False): + if not os.path.exists(local_path): + raise FlipperStorageException(f'"{local_path}" does not exist') + + if os.path.isdir(local_path): + # create parent dir + self.mkpath(flipper_path) + + for dirpath, dirnames, filenames in os.walk(local_path): + self.logger.debug(f'Processing directory "{os.path.normpath(dirpath)}"') + dirnames.sort() + filenames.sort() + rel_path = os.path.relpath(dirpath, local_path) + + # create subdirs + for dirname in dirnames: + flipper_dir_path = os.path.join(flipper_path, rel_path, dirname) + flipper_dir_path = os.path.normpath(flipper_dir_path).replace( + os.sep, "/" + ) + self.mkpath(flipper_dir_path) + + # send files + for filename in filenames: + flipper_file_path = os.path.join(flipper_path, rel_path, filename) + flipper_file_path = os.path.normpath(flipper_file_path).replace( + os.sep, "/" + ) + local_file_path = os.path.normpath(os.path.join(dirpath, filename)) + self.send_file_to_storage(flipper_file_path, local_file_path, force) + else: + self.mkpath(posixpath.dirname(flipper_path)) + self.send_file_to_storage(flipper_path, local_path, force) + + def recursive_receive(self, flipper_path: str, local_path: str): + if self.storage.exist_dir(flipper_path): + for dirpath, dirnames, filenames in self.storage.walk(flipper_path): + self.logger.debug( + f'Processing directory "{os.path.normpath(dirpath)}"'.replace( + os.sep, "/" + ) + ) + dirnames.sort() + filenames.sort() + + rel_path = os.path.relpath(dirpath, flipper_path) + + for dirname in dirnames: + local_dir_path = os.path.join(local_path, rel_path, dirname) + local_dir_path = os.path.normpath(local_dir_path) + os.makedirs(local_dir_path, exist_ok=True) + + for filename in filenames: + local_file_path = os.path.join(local_path, rel_path, filename) + local_file_path = os.path.normpath(local_file_path) + flipper_file_path = os.path.normpath( + os.path.join(dirpath, filename) + ).replace(os.sep, "/") + self.logger.info( + f'Receiving "{flipper_file_path}" to "{local_file_path}"' + ) + self.storage.receive_file(flipper_file_path, local_file_path) - if self.has_error(hash): - self.last_error = self.get_error(hash) - return "" else: - return hash.decode("ascii") + self.logger.info(f'Receiving "{flipper_path}" to "{local_path}"') + self.storage.receive_file(flipper_path, local_path) diff --git a/scripts/flipper/utils/__init__.py b/scripts/flipper/utils/__init__.py index 62bf98a25e2..6b4ebbd52be 100644 --- a/scripts/flipper/utils/__init__.py +++ b/scripts/flipper/utils/__init__.py @@ -1,6 +1,5 @@ import datetime import hashlib -import os def timestamp(): diff --git a/scripts/flipper/utils/cdc.py b/scripts/flipper/utils/cdc.py index 081705cc257..9564088598e 100644 --- a/scripts/flipper/utils/cdc.py +++ b/scripts/flipper/utils/cdc.py @@ -1,11 +1,12 @@ 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": return portname # Try guessing - flippers = list(list_ports.grep("flip")) + flippers = list(list_ports.grep("flip_")) if len(flippers) == 1: flipper = flippers[0] logger.info(f"Using {flipper.serial_number} on {flipper.device}") @@ -14,4 +15,4 @@ def resolve_port(logger, portname: str = "auto"): logger.error("Failed to find connected Flipper") elif len(flippers) > 1: logger.error("More than one Flipper is attached") - logger.error("Failed to guess which port to use. Specify --port") + logger.error("Failed to guess which port to use") diff --git a/scripts/flipper/utils/fff.py b/scripts/flipper/utils/fff.py index fa689b0168d..3175a1b0041 100644 --- a/scripts/flipper/utils/fff.py +++ b/scripts/flipper/utils/fff.py @@ -67,7 +67,10 @@ def writeEmptyLine(self): self.writeLine("") def writeComment(self, text: str): - self.writeLine(f"# {text}") + if text: + self.writeLine(f"# {text}") + else: + self.writeLine("#") def getHeader(self): if self.cursor != 0 and len(self.lines) == 0: diff --git a/scripts/flipper/utils/openocd.py b/scripts/flipper/utils/openocd.py new file mode 100644 index 00000000000..a43568090c5 --- /dev/null +++ b/scripts/flipper/utils/openocd.py @@ -0,0 +1,173 @@ +import socket +import subprocess +import logging + + +class OpenOCD: + """OpenOCD cli wrapper""" + + COMMAND_TOKEN = "\x1a" + + def __init__(self, config: dict = {}) -> None: + assert isinstance(config, dict) + + # Params base + self.params = [] + + self.gdb_port = 3333 + self.telnet_port = 4444 + self.tcl_port = 6666 + + # Port + if port_base := config.get("port_base", None): + self.gdb_port = port_base + self.tcl_port = port_base + 1 + self.telnet_port = port_base + 2 + + self._add_command(f"gdb_port {self.gdb_port}") + self._add_command(f"tcl_port {self.tcl_port}") + self._add_command(f"telnet_port {self.telnet_port}") + + # Config files + + if interface := config.get("interface", None): + pass + else: + interface = "interface/stlink.cfg" + + if target := config.get("target", None): + pass + else: + target = "target/stm32wbx.cfg" + + self._add_file(interface) + self._add_file(target) + + # Programmer settings + if serial := config.get("serial", None): + self._add_command(f"{serial}") + + # Other params + if "params" in config: + self.params += config["params"] + + # logging + self.logger = logging.getLogger() + + def _add_command(self, command: str): + self.params.append("-c") + self.params.append(command) + + def _add_file(self, file: str): + self.params.append("-f") + self.params.append(file) + + def start(self, args: list[str] = []): + """Start OpenOCD process""" + + params = ["openocd", *self.params, *args] + self.logger.debug(f"_execute: {params}") + self.process = subprocess.Popen( + params, stderr=subprocess.PIPE, stdout=subprocess.PIPE + ) + + self._wait_for_openocd_tcl() + + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.connect(("127.0.0.1", self.tcl_port)) + + def _wait_for_openocd_tcl(self): + """Wait for OpenOCD to start""" + # TODO Fl-3538: timeout + while True: + stderr = self.process.stderr + if not stderr: + break + line = stderr.readline() + if not line: + break + line = line.decode("utf-8").strip() + self.logger.debug(f"OpenOCD: {line}") + if "Listening on port" in line and "for tcl connections" in line: + break + + def stop(self): + self.send_tcl("exit") + self.send_tcl("shutdown") + self.socket.close() + try: + self.process.wait(timeout=10) + except subprocess.TimeoutExpired as e: + self.process.kill() + self.logger.error("Failed to stop OpenOCD") + self.logger.exception(e) + self.postmortem() + + def send_tcl(self, cmd) -> str: + """Send a command string to TCL RPC. Return the result that was read.""" + + try: + data = (cmd + OpenOCD.COMMAND_TOKEN).encode("utf-8") + self.logger.debug(f"<- {data}") + + self.socket.send(data) + except Exception as e: + self.logger.error("Failed to send command to OpenOCD") + self.logger.exception(e) + self.postmortem() + raise + + try: + data = self._recv() + return data + except Exception as e: + self.logger.error("Failed to receive response from OpenOCD") + self.logger.exception(e) + self.postmortem() + raise + + def _recv(self): + """Read from the stream until the token (\x1a) was received.""" + # TODO FL-3538: timeout + data = bytes() + while True: + chunk = self.socket.recv(4096) + data += chunk + if bytes(OpenOCD.COMMAND_TOKEN, encoding="utf-8") in chunk: + break + + self.logger.debug(f"-> {data}") + + data = data.decode("utf-8").strip() + data = data[:-1] # strip trailing \x1a + + return data + + def postmortem(self) -> None: + """Postmortem analysis of the OpenOCD process""" + stdout, stderr = self.process.communicate() + + log = self.logger.error + if self.process.returncode == 0: + log = self.logger.debug + log("OpenOCD exited normally") + else: + log("OpenOCD exited with error") + + log(f"Exit code: {self.process.returncode}") + for line in stdout.decode("utf-8").splitlines(): + log(f"Stdout: {line}") + + for line in stderr.decode("utf-8").splitlines(): + log(f"Stderr: {line}") + + def read_32(self, addr: int) -> int: + """Read 32-bit value from memory""" + data = self.send_tcl(f"mdw {addr}").strip() + data = data.split(": ")[-1] + data = int(data, 16) + return data + + def write_32(self, addr: int, value: int) -> None: + """Write 32-bit value to memory""" + self.send_tcl(f"mww {addr} {value}") diff --git a/scripts/flipper/utils/programmer.py b/scripts/flipper/utils/programmer.py new file mode 100644 index 00000000000..938065f7c8d --- /dev/null +++ b/scripts/flipper/utils/programmer.py @@ -0,0 +1,35 @@ +from abc import ABC, abstractmethod +from enum import Enum + + +class Programmer(ABC): + def __init__(self): + pass + + class RunMode(Enum): + Run = "run" + Stop = "stop" + + @abstractmethod + def reset(self, mode: RunMode = RunMode.Run) -> bool: + pass + + @abstractmethod + def flash(self, address: int, file_path: str, verify: bool = True) -> bool: + pass + + @abstractmethod + def option_bytes_validate(self, file_path: str) -> bool: + pass + + @abstractmethod + def option_bytes_set(self, file_path: str) -> bool: + pass + + @abstractmethod + def option_bytes_recover(self) -> bool: + pass + + @abstractmethod + def otp_write(self, address: int, file_path: str) -> bool: + pass diff --git a/scripts/flipper/utils/programmer_openocd.py b/scripts/flipper/utils/programmer_openocd.py new file mode 100644 index 00000000000..77335ee5bcd --- /dev/null +++ b/scripts/flipper/utils/programmer_openocd.py @@ -0,0 +1,303 @@ +import logging +import os +import typing +from enum import Enum + +from flipper.utils.programmer import Programmer +from flipper.utils.openocd import OpenOCD +from flipper.utils.stm32wb55 import STM32WB55 +from flipper.assets.obdata import OptionBytesData + + +class OpenOCDProgrammerResult(Enum): + Success = 0 + ErrorGeneric = 1 + ErrorAlignment = 2 + ErrorAlreadyWritten = 3 + ErrorValidation = 4 + + +class OpenOCDProgrammer(Programmer): + def __init__( + self, + interface: str = "interface/cmsis-dap.cfg", + port_base: typing.Union[int, None] = None, + serial: typing.Union[str, None] = None, + ): + super().__init__() + + config = {} + + config["interface"] = interface + config["target"] = "target/stm32wbx.cfg" + + if serial is not None: + if interface == "interface/cmsis-dap.cfg": + config["serial"] = f"cmsis_dap_serial {serial}" + elif "stlink" in interface: + config["serial"] = f"stlink_serial {serial}" + + if port_base is not None: + config["port_base"] = port_base + + self.openocd = OpenOCD(config) + self.logger = logging.getLogger() + + def reset(self, mode: Programmer.RunMode = Programmer.RunMode.Run) -> bool: + stm32 = STM32WB55(self.openocd) + if mode == Programmer.RunMode.Run: + stm32.reset(stm32.RunMode.Run) + elif mode == Programmer.RunMode.Stop: + stm32.reset(stm32.RunMode.Init) + else: + raise Exception("Unknown mode") + + return True + + def flash(self, address: int, file_path: str, verify: bool = True) -> bool: + if not os.path.exists(file_path): + raise Exception(f"File {file_path} not found") + + self.openocd.start() + self.openocd.send_tcl("init") + self.openocd.send_tcl( + f"program {file_path} 0x{address:08x}{' verify' if verify else ''} reset exit" + ) + self.openocd.stop() + + return True + + def _ob_print_diff_table(self, ob_reference: bytes, ob_read: bytes, print_fn): + print_fn( + f'{"Reference": <20} {"Device": <20} {"Diff Reference": <20} {"Diff Device": <20}' + ) + + # Split into 8 byte, word + word + for i in range(0, len(ob_reference), 8): + ref = ob_reference[i : i + 8] + read = ob_read[i : i + 8] + + diff_str1 = "" + diff_str2 = "" + for j in range(0, len(ref.hex()), 2): + byte_str_1 = ref.hex()[j : j + 2] + byte_str_2 = read.hex()[j : j + 2] + + if byte_str_1 == byte_str_2: + diff_str1 += "__" + diff_str2 += "__" + else: + diff_str1 += byte_str_1 + diff_str2 += byte_str_2 + + print_fn( + f"{ref.hex(): <20} {read.hex(): <20} {diff_str1: <20} {diff_str2: <20}" + ) + + def option_bytes_validate(self, file_path: str) -> bool: + # Registers + stm32 = STM32WB55(self.openocd) + + # OpenOCD + self.openocd.start() + stm32.reset(stm32.RunMode.Init) + + # Generate Option Bytes data + ob_data = OptionBytesData(file_path) + ob_values = ob_data.gen_values().export() + ob_reference = ob_values.reference + ob_compare_mask = ob_values.compare_mask + ob_length = len(ob_reference) + ob_words = int(ob_length / 4) + + # Read Option Bytes + ob_read = bytes() + for i in range(ob_words): + addr = stm32.OPTION_BYTE_BASE + i * 4 + value = self.openocd.read_32(addr) + ob_read += value.to_bytes(4, "little") + + # Compare Option Bytes with reference by mask + ob_compare = bytes() + for i in range(ob_length): + ob_compare += bytes([ob_read[i] & ob_compare_mask[i]]) + + # Compare Option Bytes + return_code = False + + if ob_reference == ob_compare: + self.logger.info("Option Bytes are valid") + return_code = True + else: + self.logger.error("Option Bytes are invalid") + self._ob_print_diff_table(ob_reference, ob_compare, self.logger.error) + + # Stop OpenOCD + stm32.reset(stm32.RunMode.Run) + self.openocd.stop() + + return return_code + + def _unpack_u32(self, data: bytes, offset: int): + return int.from_bytes(data[offset : offset + 4], "little") + + def option_bytes_set(self, file_path: str) -> bool: + # Registers + stm32 = STM32WB55(self.openocd) + + # OpenOCD + self.openocd.start() + stm32.reset(stm32.RunMode.Init) + + # Generate Option Bytes data + ob_data = OptionBytesData(file_path) + ob_values = ob_data.gen_values().export() + ob_reference_bytes = ob_values.reference + ob_compare_mask_bytes = ob_values.compare_mask + ob_write_mask_bytes = ob_values.write_mask + ob_length = len(ob_reference_bytes) + ob_dwords = int(ob_length / 8) + + # Clear flash errors + stm32.clear_flash_errors() + + # Unlock Flash and Option Bytes + stm32.flash_unlock() + stm32.option_bytes_unlock() + + ob_need_to_apply = False + + for i in range(ob_dwords): + device_addr = stm32.OPTION_BYTE_BASE + i * 8 + device_value = self.openocd.read_32(device_addr) + ob_write_mask = self._unpack_u32(ob_write_mask_bytes, i * 8) + ob_compare_mask = self._unpack_u32(ob_compare_mask_bytes, i * 8) + ob_value_ref = self._unpack_u32(ob_reference_bytes, i * 8) + ob_value_masked = device_value & ob_compare_mask + + need_patch = ((ob_value_masked ^ ob_value_ref) & ob_write_mask) != 0 + if need_patch: + ob_need_to_apply = True + + self.logger.info( + f"Need to patch: {device_addr:08X}: {ob_value_masked:08X} != {ob_value_ref:08X}, REG[{i}]" + ) + + # Check if this option byte (dword) is mapped to a register + device_reg_addr = stm32.option_bytes_id_to_address(i) + + # Construct new value for the OB register + ob_value = device_value & (~ob_write_mask) + ob_value |= ob_value_ref & ob_write_mask + + self.logger.info(f"Writing {ob_value:08X} to {device_reg_addr:08X}") + self.openocd.write_32(device_reg_addr, ob_value) + + if ob_need_to_apply: + stm32.option_bytes_apply() + else: + self.logger.info("Option Bytes are already correct") + + # Load Option Bytes + # That will reset and also lock the Option Bytes and the Flash + stm32.option_bytes_load() + + # Stop OpenOCD + stm32.reset(stm32.RunMode.Run) + self.openocd.stop() + + return True + + def otp_write(self, address: int, file_path: str) -> OpenOCDProgrammerResult: + # Open file, check that it aligned to 8 bytes + with open(file_path, "rb") as f: + data = f.read() + if len(data) % 8 != 0: + self.logger.error(f"File {file_path} is not aligned to 8 bytes") + return OpenOCDProgrammerResult.ErrorAlignment + + # Check that address is aligned to 8 bytes + if address % 8 != 0: + self.logger.error(f"Address {address} is not aligned to 8 bytes") + return OpenOCDProgrammerResult.ErrorAlignment + + # Get size of data + data_size = len(data) + + # Check that data size is aligned to 8 bytes + if data_size % 8 != 0: + self.logger.error(f"Data size {data_size} is not aligned to 8 bytes") + return OpenOCDProgrammerResult.ErrorAlignment + + self.logger.debug(f"Writing {data_size} bytes to OTP at {address:08X}") + self.logger.debug(f"Data: {data.hex().upper()}") + + # Start OpenOCD + self.openocd.start() + + # Registers + stm32 = STM32WB55(self.openocd) + + try: + # Check that OTP is empty for the given address + # Also check that data is already written + already_written = True + for i in range(0, data_size, 4): + file_word = int.from_bytes(data[i : i + 4], "little") + device_word = self.openocd.read_32(address + i) + if device_word != 0xFFFFFFFF and device_word != file_word: + self.logger.error( + f"OTP memory at {address + i:08X} is not empty: {device_word:08X}" + ) + return OpenOCDProgrammerResult.ErrorAlreadyWritten + + if device_word != file_word: + already_written = False + + if already_written: + self.logger.info("OTP memory is already written with the given data") + return OpenOCDProgrammerResult.Success + + self.reset(self.RunMode.Stop) + stm32.clear_flash_errors() + + # Write OTP memory by 8 bytes + for i in range(0, data_size, 8): + word_1 = int.from_bytes(data[i : i + 4], "little") + word_2 = int.from_bytes(data[i + 4 : i + 8], "little") + self.logger.debug( + f"Writing {word_1:08X} {word_2:08X} to {address + i:08X}" + ) + stm32.write_flash_64(address + i, word_1, word_2) + + # Validate OTP memory + validation_result = True + + for i in range(0, data_size, 4): + file_word = int.from_bytes(data[i : i + 4], "little") + device_word = self.openocd.read_32(address + i) + if file_word != device_word: + self.logger.error( + f"Validation failed: {file_word:08X} != {device_word:08X} at {address + i:08X}" + ) + validation_result = False + finally: + # Stop OpenOCD + stm32.reset(stm32.RunMode.Run) + self.openocd.stop() + + return ( + OpenOCDProgrammerResult.Success + if validation_result + else OpenOCDProgrammerResult.ErrorValidation + ) + + def option_bytes_recover(self) -> bool: + try: + self.openocd.start() + stm32 = STM32WB55(self.openocd) + stm32.reset(stm32.RunMode.Halt) + stm32.option_bytes_recover() + return True + finally: + self.openocd.stop() diff --git a/scripts/flipper/utils/register.py b/scripts/flipper/utils/register.py new file mode 100644 index 00000000000..aad75eaca2b --- /dev/null +++ b/scripts/flipper/utils/register.py @@ -0,0 +1,104 @@ +from dataclasses import dataclass +from flipper.utils.openocd import OpenOCD + + +@dataclass +class RegisterBitDefinition: + name: str + offset: int + size: int + value: int = 0 + + +class Register32: + def __init__(self, address: int, definition_list: list[RegisterBitDefinition]): + self.__dict__["names"] = [definition.name for definition in definition_list] + self.names = [definition.name for definition in definition_list] # typecheck + self.address = address + self.definition_list = definition_list + self.openocd = None + + # Validate that the definitions are not overlapping + for i in range(len(definition_list)): + for j in range(i + 1, len(definition_list)): + if self._is_overlapping(definition_list[i], definition_list[j]): + raise ValueError("Register definitions are overlapping") + + self.freezed = True + + def _is_overlapping( + self, a: RegisterBitDefinition, b: RegisterBitDefinition + ) -> bool: + if a.offset + a.size <= b.offset: + return False + if b.offset + b.size <= a.offset: + return False + return True + + def _get_definition(self, name: str) -> RegisterBitDefinition: + for definition in self.definition_list: + if definition.name == name: + return definition + raise ValueError(f"Register definition '{name}' not found") + + def get_definition_list(self) -> list[RegisterBitDefinition]: + return self.definition_list + + def get_address(self) -> int: + return self.address + + def set_reg_value(self, name: str, value: int): + definition = self._get_definition(name) + if value > (1 << definition.size) - 1: + raise ValueError( + f"Value {value} is too large for register definition '{name}'" + ) + definition.value = value + + def get_reg_value(self, name: str) -> int: + definition = self._get_definition(name) + return definition.value + + def __getattr__(self, attr): + if str(attr) in self.names: + return self.get_reg_value(str(attr)) + else: + return self.__dict__[attr] + + def __setattr__(self, attr, value): + if str(attr) in self.names: + self.set_reg_value(str(attr), value) + else: + if attr in self.__dict__ or "freezed" not in self.__dict__: + self.__dict__[attr] = value + else: + raise AttributeError(f"Attribute '{attr}' not found") + + def __dir__(self): + return self.names + + def set_openocd(self, openocd: OpenOCD): + self.openocd = openocd + + def get_openocd(self) -> OpenOCD: + if self.openocd is None: + raise RuntimeError("OpenOCD is not installed") + return self.openocd + + def set(self, value: int): + for definition in self.definition_list: + definition.value = (value >> definition.offset) & ( + (1 << definition.size) - 1 + ) + + def get(self) -> int: + value = 0 + for definition in self.definition_list: + value |= definition.value << definition.offset + return value + + def load(self): + self.set(self.get_openocd().read_32(self.address)) + + def store(self): + self.get_openocd().write_32(self.address, self.get()) diff --git a/scripts/flipper/utils/stm32wb55.py b/scripts/flipper/utils/stm32wb55.py new file mode 100644 index 00000000000..9ea80322055 --- /dev/null +++ b/scripts/flipper/utils/stm32wb55.py @@ -0,0 +1,373 @@ +import logging +from enum import Enum + +from flipper.utils.openocd import OpenOCD +from flipper.utils.register import Register32, RegisterBitDefinition + + +class STM32WB55: + # Address of OTP memory in flash + OTP_BASE = 0x1FFF7000 + + # Address of Option byte in flash + OPTION_BYTE_BASE = 0x1FFF8000 + + # Flash base address + FLASH_BASE = 0x58004000 + + # Flash unlock register + FLASH_KEYR = FLASH_BASE + 0x08 + + # Option byte unlock register + FLASH_OPTKEYR = FLASH_BASE + 0x0C + + # Flash unlock keys + FLASH_UNLOCK_KEY1 = 0x45670123 + FLASH_UNLOCK_KEY2 = 0xCDEF89AB + + # Option byte unlock keys + FLASH_UNLOCK_OPTKEY1 = 0x08192A3B + FLASH_UNLOCK_OPTKEY2 = 0x4C5D6E7F + + # Flash control register + FLASH_CR = Register32( + FLASH_BASE + 0x14, + [ + RegisterBitDefinition("PG", 0, 1), + RegisterBitDefinition("PER", 1, 1), + RegisterBitDefinition("MER", 2, 1), + RegisterBitDefinition("PNB", 3, 8), + RegisterBitDefinition("_", 11, 5), + RegisterBitDefinition("STRT", 16, 1), + RegisterBitDefinition("OPT_STRT", 17, 1), + RegisterBitDefinition("FSTPG", 18, 1), + RegisterBitDefinition("_", 19, 5), + RegisterBitDefinition("EOPIE", 24, 1), + RegisterBitDefinition("ERRIE", 25, 1), + RegisterBitDefinition("RD_ERRIE", 26, 1), + RegisterBitDefinition("OBL_LAUNCH", 27, 1), + RegisterBitDefinition("_", 28, 2), + RegisterBitDefinition("OPT_LOCK", 30, 1), + RegisterBitDefinition("LOCK", 31, 1), + ], + ) + + # Flash status register + FLASH_SR = Register32( + FLASH_BASE + 0x10, + [ + RegisterBitDefinition("EOP", 0, 1), + RegisterBitDefinition("OP_ERR", 1, 1), + RegisterBitDefinition("_", 2, 1), + RegisterBitDefinition("PROG_ERR", 3, 1), + RegisterBitDefinition("WRP_ERR", 4, 1), + RegisterBitDefinition("PGA_ERR", 5, 1), + RegisterBitDefinition("SIZE_ERR", 6, 1), + RegisterBitDefinition("PGS_ERR", 7, 1), + RegisterBitDefinition("MISS_ERR", 8, 1), + RegisterBitDefinition("FAST_ERR", 9, 1), + RegisterBitDefinition("_", 10, 3), + RegisterBitDefinition("OPTNV", 13, 1), + RegisterBitDefinition("RD_ERR", 14, 1), + RegisterBitDefinition("OPTV_ERR", 15, 1), + RegisterBitDefinition("BSY", 16, 1), + RegisterBitDefinition("_", 17, 1), + RegisterBitDefinition("CFGBSY", 18, 1), + RegisterBitDefinition("PESD", 19, 1), + RegisterBitDefinition("_", 20, 12), + ], + ) + + # Option byte registers + FLASH_OPTR = FLASH_BASE + 0x20 + FLASH_PCROP1ASR = FLASH_BASE + 0x24 + FLASH_PCROP1AER = FLASH_BASE + 0x28 + FLASH_WRP1AR = FLASH_BASE + 0x2C + FLASH_WRP1BR = FLASH_BASE + 0x30 + FLASH_PCROP1BSR = FLASH_BASE + 0x34 + FLASH_PCROP1BER = FLASH_BASE + 0x38 + FLASH_IPCCBR = FLASH_BASE + 0x3C + + # Map option byte dword index to register address + OPTION_BYTE_MAP_TO_REGS = { + 0: FLASH_OPTR, + 1: FLASH_PCROP1ASR, + 2: FLASH_PCROP1AER, + 3: FLASH_WRP1AR, + 4: FLASH_WRP1BR, + 5: FLASH_PCROP1BSR, + 6: FLASH_PCROP1BER, + 7: None, # Invalid Options + 8: None, # Invalid Options + 9: None, # Invalid Options + 10: None, # Invalid Options + 11: None, # Invalid Options + 12: None, # Invalid Options + 13: FLASH_IPCCBR, + 14: None, # Secure Flash + 15: None, # Core 2 Options + } + + def __init__(self, openocd: OpenOCD): + self.openocd = openocd + self.logger = logging.getLogger("STM32WB55") + + self.FLASH_CR.set_openocd(self.openocd) + self.FLASH_SR.set_openocd(self.openocd) + + class RunMode(Enum): + Init = "init" + Run = "run" + Halt = "halt" + + def reset(self, mode: RunMode): + self.logger.debug("Resetting device") + self.openocd.send_tcl(f"reset {mode.value}") + + def clear_flash_errors(self): + # Errata 2.2.9: Flash OPTVERR flag is always set after system reset + # And also clear all other flash error flags + self.logger.debug("Resetting flash errors") + self.FLASH_SR.load() + self.FLASH_SR.OP_ERR = 1 + self.FLASH_SR.PROG_ERR = 1 + self.FLASH_SR.WRP_ERR = 1 + self.FLASH_SR.PGA_ERR = 1 + self.FLASH_SR.SIZE_ERR = 1 + self.FLASH_SR.PGS_ERR = 1 + self.FLASH_SR.MISS_ERR = 1 + self.FLASH_SR.FAST_ERR = 1 + self.FLASH_SR.RD_ERR = 1 + self.FLASH_SR.OPTV_ERR = 1 + self.FLASH_SR.store() + + def flash_unlock(self): + # Check if flash is already unlocked + self.FLASH_CR.load() + if self.FLASH_CR.LOCK == 0: + self.logger.debug("Flash is already unlocked") + return + + # Unlock flash + self.logger.debug("Unlocking Flash") + self.openocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY1) + self.openocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY2) + + # Check if flash is unlocked + self.FLASH_CR.load() + if self.FLASH_CR.LOCK == 0: + self.logger.debug("Flash unlocked") + else: + self.logger.error("Flash unlock failed") + raise Exception("Flash unlock failed") + + def option_bytes_unlock(self): + # Check if options is already unlocked + self.FLASH_CR.load() + if self.FLASH_CR.OPT_LOCK == 0: + self.logger.debug("Options is already unlocked") + return + + # Unlock options + self.logger.debug("Unlocking Options") + self.openocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY1) + self.openocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY2) + + # Check if options is unlocked + self.FLASH_CR.load() + if self.FLASH_CR.OPT_LOCK == 0: + self.logger.debug("Options unlocked") + else: + self.logger.error("Options unlock failed") + raise Exception("Options unlock failed") + + def option_bytes_lock(self): + # Check if options is already locked + self.FLASH_CR.load() + if self.FLASH_CR.OPT_LOCK == 1: + self.logger.debug("Options is already locked") + return + + # Lock options + self.logger.debug("Locking Options") + self.FLASH_CR.OPT_LOCK = 1 + self.FLASH_CR.store() + + # Check if options is locked + self.FLASH_CR.load() + if self.FLASH_CR.OPT_LOCK == 1: + self.logger.debug("Options locked") + else: + self.logger.error("Options lock failed") + raise Exception("Options lock failed") + + def flash_lock(self): + # Check if flash is already locked + self.FLASH_CR.load() + if self.FLASH_CR.LOCK == 1: + self.logger.debug("Flash is already locked") + return + + # Lock flash + self.logger.debug("Locking Flash") + self.FLASH_CR.LOCK = 1 + self.FLASH_CR.store() + + # Check if flash is locked + self.FLASH_CR.load() + if self.FLASH_CR.LOCK == 1: + self.logger.debug("Flash locked") + else: + self.logger.error("Flash lock failed") + raise Exception("Flash lock failed") + + def option_bytes_apply(self): + self.logger.debug("Applying Option Bytes") + + self.FLASH_CR.load() + self.FLASH_CR.OPT_STRT = 1 + self.FLASH_CR.store() + + # Wait for Option Bytes to be applied + self.flash_wait_for_operation() + + def option_bytes_load(self): + self.logger.debug("Loading Option Bytes") + self.FLASH_CR.load() + self.FLASH_CR.OBL_LAUNCH = 1 + self.FLASH_CR.store() + + def option_bytes_id_to_address(self, id: int) -> int: + # Check if this option byte (dword) is mapped to a register + device_reg_addr = self.OPTION_BYTE_MAP_TO_REGS.get(id, None) + if device_reg_addr is None: + raise Exception(f"Option Byte {id} is not mapped to a register") + + return device_reg_addr + + def flash_wait_for_operation(self): + # Wait for flash operation to complete + # TODO FL-3537: timeout + while True: + self.FLASH_SR.load() + if self.FLASH_SR.BSY == 0: + break + + def flash_dump_status_register(self): + self.FLASH_SR.load() + self.logger.info(f"FLASH_SR: {self.FLASH_SR.get():08x}") + if self.FLASH_SR.EOP: + self.logger.info(" End of operation") + if self.FLASH_SR.OP_ERR: + self.logger.error(" Operation error") + if self.FLASH_SR.PROG_ERR: + self.logger.error(" Programming error") + if self.FLASH_SR.WRP_ERR: + self.logger.error(" Write protection error") + if self.FLASH_SR.PGA_ERR: + self.logger.error(" Programming alignment error") + if self.FLASH_SR.SIZE_ERR: + self.logger.error(" Size error") + if self.FLASH_SR.PGS_ERR: + self.logger.error(" Programming sequence error") + if self.FLASH_SR.MISS_ERR: + self.logger.error(" Fast programming data miss error") + if self.FLASH_SR.FAST_ERR: + self.logger.error(" Fast programming error") + if self.FLASH_SR.OPTNV: + self.logger.info(" User option OPTVAL indication") + if self.FLASH_SR.RD_ERR: + self.logger.info(" PCROP read error") + if self.FLASH_SR.OPTV_ERR: + self.logger.info(" Option and Engineering bits loading validity error") + if self.FLASH_SR.BSY: + self.logger.info(" Busy") + if self.FLASH_SR.CFGBSY: + self.logger.info(" Programming or erase configuration busy") + if self.FLASH_SR.PESD: + self.logger.info(" Programming / erase operation suspended.") + + def write_flash_64(self, address: int, word_1: int, word_2: int): + self.logger.debug(f"Writing flash at address {address:08x}") + + if address % 8 != 0: + self.logger.error("Address must be aligned to 8 bytes") + raise Exception("Address must be aligned to 8 bytes") + + if word_1 == self.openocd.read_32(address) and word_2 == self.openocd.read_32( + address + 4 + ): + self.logger.debug("Data is already programmed") + return + + self.flash_unlock() + + # Check that no flash main memory operation is ongoing by checking the BSY bit + self.FLASH_SR.load() + if self.FLASH_SR.BSY: + self.logger.error("Flash is busy") + self.flash_dump_status_register() + raise Exception("Flash is busy") + + # Enable end of operation interrupts and error interrupts + self.FLASH_CR.load() + self.FLASH_CR.EOPIE = 1 + self.FLASH_CR.ERRIE = 1 + self.FLASH_CR.store() + + # Check that flash memory program and erase operations are allowed + if self.FLASH_SR.PESD: + self.logger.error("Flash operations are not allowed") + self.flash_dump_status_register() + raise Exception("Flash operations are not allowed") + + # Check and clear all error programming flags due to a previous programming. + self.clear_flash_errors() + + # Set the PG bit in the Flash memory control register (FLASH_CR) + self.FLASH_CR.load() + self.FLASH_CR.PG = 1 + self.FLASH_CR.store() + + # Perform the data write operation at the desired memory address, only double word (64 bits) can be programmed. + # Write the first word + self.openocd.send_tcl(f"mww 0x{address:08x} 0x{word_1:08x}") + # Write the second word + self.openocd.send_tcl(f"mww 0x{(address + 4):08x} 0x{word_2:08x}") + + # Wait for the BSY bit to be cleared + self.flash_wait_for_operation() + + # Check that EOP flag is set in the FLASH_SR register + self.FLASH_SR.load() + if not self.FLASH_SR.EOP: + self.logger.error("Flash operation failed") + self.flash_dump_status_register() + raise Exception("Flash operation failed") + + # Clear the EOP flag + self.FLASH_SR.load() + self.FLASH_SR.EOP = 1 + self.FLASH_SR.store() + + # Clear the PG bit in the FLASH_CR register + self.FLASH_CR.load() + self.FLASH_CR.PG = 0 + self.FLASH_CR.store() + + self.flash_lock() + + def option_bytes_recover(self): + self.openocd.send_tcl("mww 0x58004010 0x8000") # set OPTVERR to reset + # Replace flash_unlock and option_bytes_unlock with the following lines, if this does not work + # self.openocd.send_tcl("mww 0x58004008 0x45670123") # unlock FLASH + # self.openocd.send_tcl("mww 0x58004008 0xCDEF89AB") + # self.openocd.send_tcl("mww 0x5800400c 0x08192A3B") # unlock OB + # self.openocd.send_tcl("mww 0x5800400c 0x4C5D6E7F") + self.flash_unlock() + self.option_bytes_unlock() + self.openocd.send_tcl("mmw 0x58004020 0x3ffff1aa 0xffffffff") # Reset OB + self.openocd.send_tcl("mww 0x5800402c 0xff") # Reset WRP1AR + self.openocd.send_tcl("mww 0x58004030 0xff") # Reset WRP1BR + self.openocd.send_tcl("mmw 0x58004014 0x00020000 0") # OPTSTRT + self.openocd.send_tcl("mmw 0x58004014 0x08000000 0") # OBL_LAUNCH diff --git a/scripts/flipper/utils/templite.py b/scripts/flipper/utils/templite.py index 8af57d7f8f4..1d19585cd8f 100644 --- a/scripts/flipper/utils/templite.py +++ b/scripts/flipper/utils/templite.py @@ -77,8 +77,8 @@ def processControl(self): return lines = self.block.splitlines() - margin = min(len(l) - len(l.lstrip()) for l in lines if l.strip()) - self.block = "\n".join("\t" * self.offset + l[margin:] for l in lines) + margin = min(len(line) - len(line.lstrip()) for line in lines if line.strip()) + self.block = "\n".join("\t" * self.offset + line[margin:] for line in lines) self.blocks.append(self.block) if self.block.endswith(":"): self.offset += 1 @@ -173,12 +173,14 @@ def render(self, **namespace): """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/fwflash.py b/scripts/fwflash.py new file mode 100755 index 00000000000..00a76322d48 --- /dev/null +++ b/scripts/fwflash.py @@ -0,0 +1,498 @@ +#!/usr/bin/env python3 +import logging +import os +import re +import socket +import subprocess +import time +import typing +from abc import ABC, abstractmethod +from dataclasses import dataclass, field + +from flipper.app import App +from serial.tools.list_ports_common import ListPortInfo + +# When adding an interface, also add it to SWD_TRANSPORT in fbt/ufbt options + + +class Programmer(ABC): + root_logger = logging.getLogger("Programmer") + + @abstractmethod + def flash(self, file_path: str, do_verify: bool) -> bool: + pass + + @abstractmethod + def probe(self) -> bool: + pass + + @abstractmethod + def get_name(self) -> str: + pass + + @abstractmethod + def set_serial(self, serial: str): + pass + + @classmethod + def _spawn_and_await(cls, process_params, show_progress: bool = False): + cls.root_logger.debug(f"Launching: {' '.join(process_params)}") + + process = subprocess.Popen( + process_params, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + + if show_progress: + while process.poll() is None: + time.sleep(0.25) + print(".", end="", flush=True) + print() + else: + process.wait() + + return process + + +@dataclass +class OpenOCDInterface: + name: str + config_file: str + serial_cmd: str + additional_args: typing.Optional[list[str]] = field(default_factory=list) + + +class OpenOCDProgrammer(Programmer): + def __init__(self, interface: OpenOCDInterface): + self.interface = interface + self.logger = self.root_logger.getChild("OpenOCD") + self.serial: typing.Optional[str] = None + + def _add_file(self, params: list[str], file: str): + params += ["-f", file] + + def _add_command(self, params: list[str], command: str): + params += ["-c", command] + + def _add_serial(self, params: list[str], serial: str): + self._add_command(params, f"{self.interface.serial_cmd} {serial}") + + def set_serial(self, serial: str): + self.serial = serial + + def flash(self, file_path: str, do_verify: bool) -> bool: + if os.altsep: + file_path = file_path.replace(os.sep, os.altsep) + + openocd_launch_params = ["openocd"] + self._add_file(openocd_launch_params, self.interface.config_file) + if self.serial: + self._add_serial(openocd_launch_params, self.serial) + if self.interface.additional_args: + for additional_arg in self.interface.additional_args: + self._add_command(openocd_launch_params, additional_arg) + self._add_file(openocd_launch_params, "target/stm32wbx.cfg") + self._add_command(openocd_launch_params, "init") + program_params = [ + "program", + f'"{file_path}"', + "verify" if do_verify else "", + "reset", + "exit", + "0x8000000" if file_path.endswith(".bin") else "", + ] + self._add_command(openocd_launch_params, " ".join(program_params)) + + # join the list of parameters into a string, but add quote if there are spaces + openocd_launch_params_string = " ".join( + [f'"{p}"' if " " in p else p for p in openocd_launch_params] + ) + + self.logger.debug(f"Launching: {openocd_launch_params_string}") + + process = self._spawn_and_await(openocd_launch_params, True) + success = process.returncode == 0 + + if not success: + self.logger.error("OpenOCD failed to flash") + if process.stdout: + self.logger.error(process.stdout.read().decode("utf-8").strip()) + + return success + + def probe(self) -> bool: + openocd_launch_params = ["openocd"] + self._add_file(openocd_launch_params, self.interface.config_file) + if self.serial: + self._add_serial(openocd_launch_params, self.serial) + if self.interface.additional_args: + for additional_arg in self.interface.additional_args: + self._add_command(openocd_launch_params, additional_arg) + self._add_file(openocd_launch_params, "target/stm32wbx.cfg") + self._add_command(openocd_launch_params, "init") + self._add_command(openocd_launch_params, "exit") + + process = self._spawn_and_await(openocd_launch_params) + success = process.returncode == 0 + + output = process.stdout.read().decode("utf-8").strip() if process.stdout else "" + self.logger.debug(output) + # Find target voltage using regex + if match := re.search(r"Target voltage: (\d+\.\d+)", output): + voltage = float(match.group(1)) + if not success: + if voltage < 1: + self.logger.warning( + f"Found {self.get_name()}, but device is not connected" + ) + else: + self.logger.warning( + f"Device is connected, but {self.get_name()} failed to attach. Is System>Debug enabled?" + ) + + if "cannot read IDR" in output: + self.logger.warning( + f"Found {self.get_name()}, but failed to attach. Is device connected and is System>Debug enabled?" + ) + success = False + + return success + + def get_name(self) -> str: + return self.interface.name + + +def blackmagic_find_serial(serial: str): + import serial.tools.list_ports as list_ports + + if serial and os.name == "nt": + if not serial.startswith("\\\\.\\"): + serial = f"\\\\.\\{serial}" + + # idk why, but python thinks that list_ports.grep returns tuple[str, str, str] + ports: list[ListPortInfo] = list(list_ports.grep("blackmagic")) # type: ignore + + if len(ports) == 0: + return None + elif len(ports) > 2: + if serial: + ports = list( + filter( + lambda p: p.serial_number == serial + or p.name == serial + or p.device == serial, + ports, + ) + ) + if len(ports) == 0: + return None + + if len(ports) > 2: + raise Exception("More than one Blackmagic probe found") + + # If you're getting any issues with auto lookup, uncomment this + # print("\n".join([f"{p.device} {vars(p)}" for p in ports])) + port = sorted(ports, key=lambda p: f"{p.location}_{p.name}")[0] + + if serial: + if ( + serial != port.serial_number + and serial != port.name + and serial != port.device + ): + return None + + if os.name == "nt": + port.device = f"\\\\.\\{port.device}" + return port.device + + +def _resolve_hostname(hostname): + try: + return socket.gethostbyname(hostname) + except socket.gaierror: + return None + + +def blackmagic_find_networked(serial: str): + if not serial or serial == "auto": + serial = "blackmagic.local" + + # remove the tcp: prefix if it's there + if serial.startswith("tcp:"): + serial = serial[4:] + + # remove the port if it's there + if ":" in serial: + serial = serial.split(":")[0] + + if not (probe := _resolve_hostname(serial)): + return None + + return f"tcp:{probe}:2345" + + +class BlackmagicProgrammer(Programmer): + def __init__( + self, + port_resolver, # typing.Callable[typing.Union[str, None], typing.Optional[str]] + name: str, + ): + self.port_resolver = port_resolver + self.name = name + self.logger = self.root_logger.getChild(f"Blackmagic{name}") + self.port: typing.Optional[str] = None + + def _add_command(self, params: list[str], command: str): + params.append("-ex") + params.append(command) + + def _valid_ip(self, address): + try: + socket.inet_aton(address) + return True + except Exception: + return False + + def set_serial(self, serial: str): + if self._valid_ip(serial): + self.port = f"{serial}:2345" + elif ip := _resolve_hostname(serial): + self.port = f"{ip}:2345" + else: + self.port = serial + + def _get_gdb_core_params(self) -> list[str]: + gdb_launch_params = ["arm-none-eabi-gdb"] + self._add_command(gdb_launch_params, f"target extended-remote {self.port}") + self._add_command(gdb_launch_params, "set pagination off") + self._add_command(gdb_launch_params, "set confirm off") + self._add_command(gdb_launch_params, "monitor swdp_scan") + return gdb_launch_params + + def flash(self, file_path: str, do_verify: bool) -> bool: + if not self.port: + if not self.probe(): + return False + + # We can convert .bin to .elf with objcopy: + # arm-none-eabi-objcopy -I binary -O elf32-littlearm --change-section-address=.data=0x8000000 -B arm -S app.bin app.elf + # But I choose to use the .elf file directly because we are flashing our own firmware and it always has an elf predecessor. + + if file_path.endswith(".bin"): + file_path = file_path[:-4] + ".elf" + if not os.path.exists(file_path): + self.logger.error( + f"Sorry, but Blackmagic can't flash .bin file, and {file_path} doesn't exist" + ) + return False + + # arm-none-eabi-gdb build/f7-firmware-D/firmware.bin + # -ex 'set pagination off' + # -ex 'target extended-remote /dev/cu.usbmodem21201' + # -ex 'set confirm off' + # -ex 'monitor swdp_scan' + # -ex 'attach 1' + # -ex 'set mem inaccessible-by-default off' + # -ex 'load' + # -ex 'compare-sections' + # -ex 'quit' + + gdb_launch_params = self._get_gdb_core_params() + self._add_command(gdb_launch_params, "attach 1") + self._add_command(gdb_launch_params, "set mem inaccessible-by-default off") + self._add_command(gdb_launch_params, "load") + if do_verify: + self._add_command(gdb_launch_params, "compare-sections") + self._add_command(gdb_launch_params, "quit") + gdb_launch_params.append(file_path) + + process = self._spawn_and_await(gdb_launch_params, True) + if not process.stdout: + return False + + output = process.stdout.read().decode("utf-8").strip() + flashed = ( + "Loading section .text," in output + and "MIS-MATCHED!" not in output + and "target image does not match the loaded file" not in output + ) + + if not flashed: + self.logger.error("Blackmagic failed to flash") + self.logger.error(output) + + return flashed + + def probe(self) -> bool: + if not (port := self.port_resolver(self.port)): + return False + + self.port = port + + gdb_launch_params = self._get_gdb_core_params() + self._add_command(gdb_launch_params, "quit") + + process = self._spawn_and_await(gdb_launch_params) + if not process.stdout or process.returncode != 0: + return False + + output = process.stdout.read().decode("utf-8").strip() + if "SW-DP scan failed!" in output: + self.logger.warning( + f"Found {self.get_name()} at {self.port}, but failed to attach. Is device connected and is System>Debug enabled?" + ) + return False + return True + + def get_name(self) -> str: + return self.name + + +#################### + +local_flash_interfaces: list[Programmer] = [ + OpenOCDProgrammer( + OpenOCDInterface( + "cmsis-dap", + "interface/cmsis-dap.cfg", + "cmsis_dap_serial", + ["transport select swd"], + ), + ), + OpenOCDProgrammer( + OpenOCDInterface( + "stlink", + "interface/stlink.cfg", + "hla_serial", + ["transport select hla_swd"], + ), + ), + BlackmagicProgrammer(blackmagic_find_serial, "blackmagic_usb"), +] + +network_flash_interfaces: list[Programmer] = [ + BlackmagicProgrammer(blackmagic_find_networked, "blackmagic_wifi") +] + +all_flash_interfaces = [*local_flash_interfaces, *network_flash_interfaces] + +#################### + + +class Main(App): + AUTO_INTERFACE = "auto" + + def init(self): + Programmer.root_logger = self.logger + + self.parser.add_argument( + "filename", + type=str, + help="File to flash", + ) + self.parser.add_argument( + "--verify", + "-v", + action="store_true", + help="Verify flash after programming", + default=False, + ) + self.parser.add_argument( + "--interface", + choices=( + self.AUTO_INTERFACE, + *[i.get_name() for i in all_flash_interfaces], + ), + type=str, + default=self.AUTO_INTERFACE, + help="Interface to use", + ) + self.parser.add_argument( + "--serial", + type=str, + default=self.AUTO_INTERFACE, + help="Serial number or port of the programmer", + ) + self.parser.set_defaults(func=self.flash) + + def _search_interface(self, interface_list: list[Programmer]) -> list[Programmer]: + found_programmers = [] + + for p in interface_list: + name = p.get_name() + if (serial := self.args.serial) != self.AUTO_INTERFACE: + p.set_serial(serial) + self.logger.debug(f"Trying {name} with {serial}") + else: + self.logger.debug(f"Trying {name}") + + if p.probe(): + self.logger.debug(f"Found {name}") + found_programmers.append(p) + else: + self.logger.debug(f"Failed to probe {name}") + + return found_programmers + + def flash(self): + start_time = time.time() + file_path = os.path.abspath(self.args.filename) + + if not os.path.exists(file_path): + self.logger.error(f"Binary file not found: {file_path}") + return 1 + + if self.args.interface != self.AUTO_INTERFACE: + available_interfaces = list( + filter( + lambda p: p.get_name() == self.args.interface, + all_flash_interfaces, + ) + ) + + else: + self.logger.info("Probing for local interfaces...") + available_interfaces = self._search_interface(local_flash_interfaces) + + if not available_interfaces: + # Probe network blackmagic + self.logger.info("Probing for network interfaces...") + available_interfaces = self._search_interface(network_flash_interfaces) + + if not available_interfaces: + self.logger.error("No available interfaces") + return 1 + elif len(available_interfaces) > 1: + self.logger.error("Multiple interfaces found:") + self.logger.error( + f"Please specify '--interface={[i.get_name() for i in available_interfaces]}'" + ) + return 1 + + interface = available_interfaces.pop(0) + + if self.args.serial != self.AUTO_INTERFACE: + interface.set_serial(self.args.serial) + self.logger.info(f"Using {interface.get_name()} with {self.args.serial}") + else: + self.logger.info(f"Using {interface.get_name()}") + self.logger.info(f"Flashing {file_path}") + + if not interface.flash(file_path, self.args.verify): + self.logger.error(f"Failed to flash via {interface.get_name()}") + return 1 + + flash_time = time.time() - start_time + self.logger.info(f"Flashed successfully in {flash_time:.2f}s") + if file_path.endswith(".bin"): + bin_size = os.path.getsize(file_path) + self.logger.info( + f"Effective speed: {bin_size / flash_time / 1024:.2f} KiB/s" + ) + return 0 + + +if __name__ == "__main__": + Main()() diff --git a/scripts/fwsize.py b/scripts/fwsize.py old mode 100644 new mode 100755 index b381f6e9a57..75a82569226 --- a/scripts/fwsize.py +++ b/scripts/fwsize.py @@ -1,9 +1,11 @@ #!/usr/bin/env python3 -from flipper.app import App -import subprocess -import os import math +import os +import subprocess + +from ansi.color import fg +from flipper.app import App class Main(App): @@ -43,7 +45,9 @@ def process_bin(self): pages = math.ceil(binsize / PAGE_SIZE) last_page_state = (binsize % PAGE_SIZE) * 100 / PAGE_SIZE print( - f"{os.path.basename(self.args.binname):<11}: {pages:>4} flash pages (last page {last_page_state:.02f}% full)" + fg.yellow( + f"{os.path.basename(self.args.binname):<11}: {pages:>4} flash pages (last page {last_page_state:.02f}% full)" + ) ) return 0 diff --git a/scripts/get_env.py b/scripts/get_env.py new file mode 100755 index 00000000000..e8f6b3b77fd --- /dev/null +++ b/scripts/get_env.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 + +import argparse +import datetime +import json +import os +import random +import re +import shlex +import ssl +import string +import urllib.request + + +def id_gen(size=5, chars=string.ascii_uppercase + string.digits): + return "".join(random.choice(chars) for _ in range(size)) + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--event_file", help="Current GitHub event file", required=True) + parser.add_argument( + "--type", + help="Event file type", + required=True, + choices=["pull", "tag", "other"], + ) + args = parser.parse_args() + return args + + +def get_commit_json(event): + context = ssl._create_unverified_context() + commit_url = event["pull_request"]["base"]["repo"]["commits_url"].replace( + "{/sha}", f"/{event['pull_request']['head']['sha']}" + ) + with urllib.request.urlopen(commit_url, context=context) as commit_file: + commit_json = json.loads(commit_file.read().decode("utf-8")) + return commit_json + + +def get_details(event, args): + data = {} + current_time = datetime.datetime.utcnow().date() + if args.type == "pull": + commit_json = get_commit_json(event) + data["commit_comment"] = shlex.quote(commit_json["commit"]["message"]) + data["commit_hash"] = commit_json["sha"] + ref = event["pull_request"]["head"]["ref"] + data["pull_id"] = event["pull_request"]["number"] + data["pull_name"] = shlex.quote(event["pull_request"]["title"]) + elif args.type == "tag": + data["commit_comment"] = shlex.quote(event["head_commit"]["message"]) + data["commit_hash"] = event["head_commit"]["id"] + ref = event["ref"] + else: + data["commit_comment"] = shlex.quote(event["commits"][-1]["message"]) + data["commit_hash"] = event["commits"][-1]["id"] + ref = event["ref"] + data["commit_sha"] = data["commit_hash"][:8] + data["branch_name"] = re.sub("refs/\w+/", "", ref) + data["suffix"] = ( + data["branch_name"].replace("/", "_") + + "-" + + current_time.strftime("%d%m%Y") + + "-" + + data["commit_sha"] + ) + if ref.startswith("refs/tags/"): + data["suffix"] = data["branch_name"].replace("/", "_") + return data + + +def add_env(name, value, file): + delimiter = id_gen() + print(f"{name}<<{delimiter}", file=file) + print(f"{value}", file=file) + print(f"{delimiter}", file=file) + + +def add_set_output_var(name, value, file): + print(f"{name}={value}", file=file) + + +def add_envs(data, gh_env_file, gh_out_file, args): + add_env("COMMIT_MSG", data["commit_comment"], gh_env_file) + add_env("COMMIT_HASH", data["commit_hash"], gh_env_file) + add_env("COMMIT_SHA", data["commit_sha"], gh_env_file) + add_env("SUFFIX", data["suffix"], gh_env_file) + add_env("BRANCH_NAME", data["branch_name"], gh_env_file) + add_env("DIST_SUFFIX", data["suffix"], gh_env_file) + add_env("WORKFLOW_BRANCH_OR_TAG", data["branch_name"], gh_env_file) + add_set_output_var("branch_name", data["branch_name"], gh_out_file) + add_set_output_var("commit_sha", data["commit_sha"], gh_out_file) + add_set_output_var("default_target", os.getenv("DEFAULT_TARGET"), gh_out_file) + add_set_output_var("suffix", data["suffix"], gh_out_file) + if args.type == "pull": + add_env("PULL_ID", data["pull_id"], gh_env_file) + add_env("PULL_NAME", data["pull_name"], gh_env_file) + + +def main(): + args = parse_args() + event_file = open(args.event_file, "r") + event = json.load(event_file) + gh_env_file = open(os.environ["GITHUB_ENV"], "a") + gh_out_file = open(os.environ["GITHUB_OUTPUT"], "a") + data = get_details(event, args) + add_envs(data, gh_env_file, gh_out_file, args) + event_file.close() + gh_env_file.close() + gh_out_file.close() + + +if __name__ == "__main__": + main() diff --git a/scripts/guruguru.py b/scripts/guruguru.py index 3560bcd96fe..c227e17eeb6 100755 --- a/scripts/guruguru.py +++ b/scripts/guruguru.py @@ -17,7 +17,7 @@ def clearConsole(self): async def rebuild(self, line): self.clearConsole() self.logger.info(f"Triggered by: {line}") - proc = await asyncio.create_subprocess_exec("make") + proc = await asyncio.create_subprocess_exec("./fbt") await proc.wait() await asyncio.sleep(1) self.is_building = False diff --git a/scripts/infrared.py b/scripts/infrared.py new file mode 100755 index 00000000000..9fa44a90aa0 --- /dev/null +++ b/scripts/infrared.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 + +from os import path + +from flipper.app import App +from flipper.utils.fff import * + + +class Main(App): + def init(self): + # Subparsers + self.subparsers = self.parser.add_subparsers(help="sub-command help") + + self.parser_cleanup = self.subparsers.add_parser( + "cleanup", help="Cleanup duplicate remotes" + ) + self.parser_cleanup.add_argument("filename", type=str) + self.parser_cleanup.set_defaults(func=self.cleanup) + + def cleanup(self): + f = FlipperFormatFile() + f.load(self.args.filename) + + filetype, version = f.getHeader() + if filetype != "IR library file" or version != 1: + self.logger.error(f"Incorrect file type({filetype}) or version({version})") + return 1 + + data = [] + unique = {} + while True: + try: + d = {} + d["name"] = f.readKey("name") + d["type"] = f.readKey("type") + key = None + if d["type"] == "parsed": + d["protocol"] = f.readKey("protocol") + d["address"] = f.readKey("address") + d["command"] = f.readKey("command") + key = f'{d["protocol"]}{d["address"]}{d["command"]}' + elif d["type"] == "raw": + d["frequency"] = f.readKey("frequency") + d["duty_cycle"] = f.readKey("duty_cycle") + d["data"] = f.readKey("data") + key = f'{d["frequency"]}{d["duty_cycle"]}{d["data"]}' + else: + raise Exception(f'Unknown type: {d["type"]}') + if not key in unique: + unique[key] = d + data.append(d) + else: + self.logger.warn(f"Duplicate key: {key}") + except EOFError: + break + # Form new file + f = FlipperFormatFile() + f.setHeader(filetype, version) + for i in data: + f.writeComment(None) + f.writeKey("name", i["name"]) + f.writeKey("type", i["type"]) + if i["type"] == "parsed": + f.writeKey("protocol", i["protocol"]) + f.writeKey("address", i["address"]) + f.writeKey("command", i["command"]) + elif i["type"] == "raw": + f.writeKey("frequency", i["frequency"]) + f.writeKey("duty_cycle", i["duty_cycle"]) + f.writeKey("data", i["data"]) + else: + raise Exception(f'Unknown type: {i["type"]}') + f.save(self.args.filename) + + return 0 + + +if __name__ == "__main__": + Main()() diff --git a/scripts/lint.py b/scripts/lint.py index b3c3e7da406..8530209bec0 100755 --- a/scripts/lint.py +++ b/scripts/lint.py @@ -1,14 +1,13 @@ #!/usr/bin/env python3 +import multiprocessing import os import re import shutil import subprocess -import multiprocessing from flipper.app import App - SOURCE_CODE_FILE_EXTENSIONS = [".h", ".c", ".cpp", ".cxx", ".hpp"] SOURCE_CODE_FILE_PATTERN = r"^[0-9A-Za-z_]+\.[a-z]+$" SOURCE_CODE_DIR_PATTERN = r"^[0-9A-Za-z_]+$" @@ -35,11 +34,23 @@ def init(self): ) self.parser_format.set_defaults(func=self.format) + @staticmethod + def _filter_lint_directories(dirnames: list[str]): + # Skipping 3rd-party code - usually resides in subfolder "lib" + if "lib" in dirnames: + dirnames.remove("lib") + # Skipping hidden folders + for dirname in dirnames.copy(): + if dirname.startswith("."): + dirnames.remove(dirname) + def _check_folders(self, folders: list): show_message = False pattern = re.compile(SOURCE_CODE_DIR_PATTERN) for folder in folders: for dirpath, dirnames, filenames in os.walk(folder): + self._filter_lint_directories(dirnames) + for dirname in dirnames: if not pattern.match(dirname): to_fix = os.path.join(dirpath, dirname) @@ -47,16 +58,18 @@ def _check_folders(self, folders: list): show_message = True if show_message: self.logger.warning( - f"Folders are not renamed automatically, please fix it by yourself" + "Folders are not renamed automatically, please fix it by yourself" ) def _find_sources(self, folders: list): output = [] for folder in folders: for dirpath, dirnames, filenames in os.walk(folder): + self._filter_lint_directories(dirnames) + for filename in filenames: ext = os.path.splitext(filename.lower())[1] - if not ext in SOURCE_CODE_FILE_EXTENSIONS: + if ext not in SOURCE_CODE_FILE_EXTENSIONS: continue output.append(os.path.join(dirpath, filename)) return output @@ -66,7 +79,7 @@ def _format_source(task): try: subprocess.check_call(task) return True - except subprocess.CalledProcessError as e: + except subprocess.CalledProcessError: return False def _format_sources(self, sources: list, dry_run: bool = False): @@ -88,7 +101,7 @@ def _format_sources(self, sources: list, dry_run: bool = False): def _fix_filename(self, filename: str): return filename.replace("-", "_") - def _replace_occurance(self, sources: list, old: str, new: str): + def _replace_occurrence(self, sources: list, old: str, new: str): old = old.encode() new = new.encode() for source in sources: @@ -102,7 +115,7 @@ def _apply_file_naming_convention(self, sources: list, dry_run: bool = False): pattern = re.compile(SOURCE_CODE_FILE_PATTERN) good = [] bad = [] - # Check sources for invalid filesname + # Check sources for invalid filenames for source in sources: basename = os.path.basename(source) if not pattern.match(basename): @@ -113,40 +126,63 @@ def _apply_file_naming_convention(self, sources: list, dry_run: bool = False): bad.append((source, basename, new_basename)) else: good.append(source) - # Notify about errors or replace all occurances + # Notify about errors or replace all occurrences if dry_run: if len(bad) > 0: self.logger.error(f"Found {len(bad)} incorrectly named files") self.logger.info(bad) return False else: - # Replace occurances in text files + # Replace occurrences in text files for source, old, new in bad: - self._replace_occurance(sources, old, new) + self._replace_occurrence(sources, old, new) # Rename files for source, old, new in bad: shutil.move(source, source.replace(old, new)) return True - def check(self): + def _apply_file_permissions(self, sources: list, dry_run: bool = False): + execute_permissions = 0o111 + re.compile(SOURCE_CODE_FILE_PATTERN) + good = [] + bad = [] + # Check sources for unexpected execute permissions + for source in sources: + st = os.stat(source) + perms_too_many = st.st_mode & execute_permissions + if perms_too_many: + good_perms = st.st_mode & ~perms_too_many + bad.append((source, oct(perms_too_many), good_perms)) + else: + good.append(source) + # Notify or fix + if dry_run: + if len(bad) > 0: + self.logger.error(f"Found {len(bad)} incorrect permissions") + self.logger.info([record[0:2] for record in bad]) + return False + else: + for source, perms_too_many, new_perms in bad: + os.chmod(source, new_perms) + return True + + def _perform(self, dry_run: bool): result = 0 sources = self._find_sources(self.args.input) - if not self._format_sources(sources, dry_run=True): - result |= 0b01 - if not self._apply_file_naming_convention(sources, dry_run=True): - result |= 0b10 + if not self._format_sources(sources, dry_run=dry_run): + result |= 0b001 + if not self._apply_file_naming_convention(sources, dry_run=dry_run): + result |= 0b010 + if not self._apply_file_permissions(sources, dry_run=dry_run): + result |= 0b100 self._check_folders(self.args.input) return result + def check(self): + return self._perform(dry_run=True) + def format(self): - result = 0 - sources = self._find_sources(self.args.input) - if not self._format_sources(sources): - result |= 0b01 - if not self._apply_file_naming_convention(sources): - result |= 0b10 - self._check_folders(self.args.input) - return result + return self._perform(dry_run=False) if __name__ == "__main__": diff --git a/scripts/map_mariadb_insert.py b/scripts/map_mariadb_insert.py new file mode 100755 index 00000000000..a4c9ed5c788 --- /dev/null +++ b/scripts/map_mariadb_insert.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 + +# Requiremets: +# mariadb==1.1.6 + +from datetime import datetime +import argparse +import mariadb +import sys +import os + + +def parseArgs(): + parser = argparse.ArgumentParser() + parser.add_argument("db_user", help="MariaDB user") + parser.add_argument("db_pass", help="MariaDB password") + parser.add_argument("db_host", help="MariaDB hostname") + parser.add_argument("db_port", type=int, help="MariaDB port") + parser.add_argument("db_name", help="MariaDB database") + parser.add_argument("report_file", help="Report file(.map.all)") + args = parser.parse_args() + return args + + +def mariadbConnect(args): + try: + conn = mariadb.connect( + user=args.db_user, + password=args.db_pass, + host=args.db_host, + port=args.db_port, + database=args.db_name, + ) + except mariadb.Error as e: + print(f"Error connecting to MariaDB: {e}") + sys.exit(1) + return conn + + +def parseEnv(): + outArr = [] + outArr.append(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + outArr.append(os.getenv("COMMIT_HASH", default=None)) + outArr.append(os.getenv("COMMIT_MSG", default=None)) + outArr.append(os.getenv("BRANCH_NAME", default=None)) + outArr.append(os.getenv("BSS_SIZE", default=None)) + outArr.append(os.getenv("TEXT_SIZE", default=None)) + outArr.append(os.getenv("RODATA_SIZE", default=None)) + outArr.append(os.getenv("DATA_SIZE", default=None)) + outArr.append(os.getenv("FREE_FLASH_SIZE", default=None)) + outArr.append(os.getenv("PULL_ID", default=None)) + outArr.append(os.getenv("PULL_NAME", default=None)) + return outArr + + +def createTables(cur, conn): + headerTable = "CREATE TABLE IF NOT EXISTS `header` ( \ + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, \ + `datetime` datetime NOT NULL, \ + `commit` varchar(40) NOT NULL, \ + `commit_msg` text NOT NULL, \ + `branch_name` text NOT NULL, \ + `bss_size` int(10) unsigned NOT NULL, \ + `text_size` int(10) unsigned NOT NULL, \ + `rodata_size` int(10) unsigned NOT NULL, \ + `data_size` int(10) unsigned NOT NULL, \ + `free_flash_size` int(10) unsigned NOT NULL, \ + `pullrequest_id` int(10) unsigned DEFAULT NULL, \ + `pullrequest_name` text DEFAULT NULL, \ + PRIMARY KEY (`id`), \ + KEY `header_id_index` (`id`) )" + dataTable = "CREATE TABLE IF NOT EXISTS `data` ( \ + `header_id` int(10) unsigned NOT NULL, \ + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, \ + `section` text NOT NULL, \ + `address` text NOT NULL, \ + `size` int(10) unsigned NOT NULL, \ + `name` text NOT NULL, \ + `lib` text NOT NULL, \ + `obj_name` text NOT NULL, \ + PRIMARY KEY (`id`), \ + KEY `data_id_index` (`id`), \ + KEY `data_header_id_index` (`header_id`), \ + CONSTRAINT `data_header_id_foreign` FOREIGN KEY (`header_id`) REFERENCES `header` (`id`) )" + cur.execute(headerTable) + cur.execute(dataTable) + conn.commit() + + +def insertHeader(data, cur, conn): + query = "INSERT INTO `header` ( \ + datetime, commit, commit_msg, branch_name, bss_size, text_size, \ + rodata_size, data_size, free_flash_size, pullrequest_id, pullrequest_name) \ + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + cur.execute(query, data) + conn.commit() + return cur.lastrowid + + +def parseFile(fileObj, headerID): + arr = [] + fileLines = fileObj.readlines() + for line in fileLines: + lineArr = [] + tempLineArr = line.split("\t") + lineArr.append(headerID) + lineArr.append(tempLineArr[0]) # section + lineArr.append(int(tempLineArr[2], 16)) # address hex + lineArr.append(int(tempLineArr[3])) # size + lineArr.append(tempLineArr[4]) # name + lineArr.append(tempLineArr[5]) # lib + lineArr.append(tempLineArr[6]) # obj_name + arr.append(tuple(lineArr)) + return arr + + +def insertData(data, cur, conn): + query = "INSERT INTO `data` ( \ + header_id, section, address, size, \ + name, lib, obj_name) \ + VALUES (?, ?, ?, ?, ? ,?, ?)" + cur.executemany(query, data) + conn.commit() + + +def main(): + args = parseArgs() + dbConn = mariadbConnect(args) + reportFile = open(args.report_file) + dbCurs = dbConn.cursor() + createTables(dbCurs, dbConn) + headerID = insertHeader(parseEnv(), dbCurs, dbConn) + insertData(parseFile(reportFile, headerID), dbCurs, dbConn) + reportFile.close() + dbCurs.close() + + +if __name__ == "__main__": + main() diff --git a/scripts/map_parser.py b/scripts/map_parser.py new file mode 100755 index 00000000000..1efc4fe82f3 --- /dev/null +++ b/scripts/map_parser.py @@ -0,0 +1,274 @@ +#!/usr/bin/env python3 + +# Requiremets: +# cxxfilt==0.3.0 + +# Most part of this code written by Lars-Dominik Braun https://github.com/PromyLOPh/linkermapviz +# and distributes under MIT licence + +# Copyright (c) 2017 Lars-Dominik Braun +# +# 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. + +import sys +import re +import os +from typing import TextIO +from cxxfilt import demangle + + +class Objectfile: + def __init__(self, section: str, offset: int, size: int, comment: str): + self.section = section.strip() + self.offset = offset + self.size = size + self.path = (None, None) + self.basepath = None + + if comment: + self.path = re.match(r"^(.+?)(?:\(([^\)]+)\))?$", comment).groups() + self.basepath = os.path.basename(self.path[0]) + + self.children = [] + + def __repr__(self) -> str: + return f"" + + +def update_children_size(children: list[list], subsection_size: int) -> list: + # set subsection size to an only child + if len(children) == 1: + children[0][1] = subsection_size + return children + + rest_size = subsection_size + + for index in range(1, len(children)): + if rest_size > 0: + # current size = current address - previous child address + child_size = children[index][0] - children[index - 1][0] + rest_size -= child_size + children[index - 1][1] = child_size + + # if there is rest size, set it to the last child element + if rest_size > 0: + children[-1][1] = rest_size + + return children + + +def parse_sections(file_name: str) -> list: + """ + Quick&Dirty parsing for GNU ld’s linker map output, needs LANG=C, because + some messages are localized. + """ + + sections = [] + with open(file_name, "r") as file: + # skip until memory map is found + found = False + + while True: + line = file.readline() + if not line: + break + if line.strip() == "Memory Configuration": + found = True + break + + if not found: + raise Exception(f"Memory configuration is not found in the{input_file}") + + # long section names result in a linebreak afterwards + sectionre = re.compile( + "(?P
.+?|.{14,}\n)[ ]+0x(?P[0-9a-f]+)[ ]+0x(?P[0-9a-f]+)(?:[ ]+(?P.+))?\n+", + re.I, + ) + subsectionre = re.compile( + "[ ]{16}0x(?P[0-9a-f]+)[ ]+(?P.+)\n+", re.I + ) + s = file.read() + pos = 0 + + while True: + m = sectionre.match(s, pos) + if not m: + # skip that line + try: + nextpos = s.index("\n", pos) + 1 + pos = nextpos + continue + except ValueError: + break + + pos = m.end() + section = m.group("section") + v = m.group("offset") + offset = int(v, 16) if v is not None else None + v = m.group("size") + size = int(v, 16) if v is not None else None + comment = m.group("comment") + + if section != "*default*" and size > 0: + of = Objectfile(section, offset, size, comment) + + if section.startswith(" "): + children = [] + sections[-1].children.append(of) + + while True: + m = subsectionre.match(s, pos) + if not m: + break + pos = m.end() + offset, function = m.groups() + offset = int(offset, 16) + if sections and sections[-1].children: + children.append([offset, 0, function]) + + if children: + children = update_children_size( + children=children, subsection_size=of.size + ) + + sections[-1].children[-1].children.extend(children) + + else: + sections.append(of) + + return sections + + +def get_subsection_name(section_name: str, subsection: Objectfile) -> str: + subsection_split_names = subsection.section.split(".") + if subsection.section.startswith("."): + subsection_split_names = subsection_split_names[1:] + + return ( + f".{subsection_split_names[1]}" + if len(subsection_split_names) > 2 + else section_name + ) + + +def write_subsection( + section_name: str, + subsection_name: str, + address: str, + size: int, + demangled_name: str, + module_name: str, + file_name: str, + mangled_name: str, + write_file_object: TextIO, +) -> None: + write_file_object.write( + f"{section_name}\t" + f"{subsection_name}\t" + f"{address}\t" + f"{size}\t" + f"{demangled_name}\t" + f"{module_name}\t" + f"{file_name}\t" + f"{mangled_name}\n" + ) + + +def save_subsection( + section_name: str, subsection: Objectfile, write_file_object: TextIO +) -> None: + subsection_name = get_subsection_name(section_name, subsection) + module_name = subsection.path[0] + file_name = subsection.path[1] + + if not file_name: + file_name, module_name = module_name, "" + + if not subsection.children: + address = f"{subsection.offset:x}" + size = subsection.size + mangled_name = ( + "" + if subsection.section == section_name + else subsection.section.split(".")[-1] + ) + demangled_name = demangle(mangled_name) if mangled_name else mangled_name + + write_subsection( + section_name=section_name, + subsection_name=subsection_name, + address=address, + size=size, + demangled_name=demangled_name, + module_name=module_name, + file_name=file_name, + mangled_name=mangled_name, + write_file_object=write_file_object, + ) + return + + for subsection_child in subsection.children: + address = f"{subsection_child[0]:x}" + size = subsection_child[1] + mangled_name = subsection_child[2] + demangled_name = demangle(mangled_name) + + write_subsection( + section_name=section_name, + subsection_name=subsection_name, + address=address, + size=size, + demangled_name=demangled_name, + module_name=module_name, + file_name=file_name, + mangled_name=mangled_name, + write_file_object=write_file_object, + ) + + +def save_section(section: Objectfile, write_file_object: TextIO) -> None: + section_name = section.section + for subsection in section.children: + save_subsection( + section_name=section_name, + subsection=subsection, + write_file_object=write_file_object, + ) + + +def save_parsed_data(parsed_data: list[Objectfile], output_file_name: str) -> None: + with open(output_file_name, "w") as write_file_object: + for section in parsed_data: + if section.children: + save_section(section=section, write_file_object=write_file_object) + + +if __name__ == "__main__": + if len(sys.argv) < 3: + raise Exception(f"Usage: {sys.argv[0]} ") + + input_file = sys.argv[1] + output_file = sys.argv[2] + + parsed_sections = parse_sections(input_file) + + if parsed_sections is None: + raise Exception(f"Memory configuration is not {input_file}") + + save_parsed_data(parsed_sections, output_file) diff --git a/scripts/merge_report_qa.py b/scripts/merge_report_qa.py new file mode 100755 index 00000000000..a33327e6b7a --- /dev/null +++ b/scripts/merge_report_qa.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 + +import argparse +import os +import re +import sys + +from slack_sdk import WebClient +from slack_sdk.errors import SlackApiError + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("slack_token") + parser.add_argument("slack_channel") + args = parser.parse_args() + return args + + +def checkCommitMessage(msg): + regex = re.compile(r"^'?\[(FL-\d+,?\s?)+\]") + if regex.match(msg): + return True + return False + + +def reportSlack(commit_hash, slack_token, slack_channel, message): + client = WebClient(token=slack_token) + try: + client.chat_postMessage(channel="#" + slack_channel, text=message) + except SlackApiError as e: + print(e) + sys.exit(1) + + +def main(): + args = parse_args() + commit_msg = os.getenv("COMMIT_MSG") + commit_hash = os.getenv("COMMIT_HASH") + commit_sha = os.getenv("COMMIT_SHA") + commit_link = ( + "" + ) + message = "Commit " + commit_link + " merged to dev without 'FL' ticket!" + if not checkCommitMessage(commit_msg): + reportSlack(commit_hash, args.slack_token, args.slack_channel, message) + + +if __name__ == "__main__": + main() diff --git a/scripts/meta.py b/scripts/meta.py deleted file mode 100755 index ae2f213b760..00000000000 --- a/scripts/meta.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python3 - -from flipper.app import App -import json - - -class Main(App): - def init(self): - self.subparsers = self.parser.add_subparsers(help="sub-command help") - - # generate - self.parser_generate = self.subparsers.add_parser( - "generate", help="Generate JSON meta file" - ) - self.parser_generate.add_argument("-p", dest="project", required=True) - self.parser_generate.add_argument( - "-DBUILD_DATE", dest="build_date", required=True - ) - self.parser_generate.add_argument("-DGIT_COMMIT", dest="commit", required=True) - self.parser_generate.add_argument("-DGIT_BRANCH", dest="branch", required=True) - self.parser_generate.add_argument( - "-DTARGET", dest="target", type=int, required=True - ) - self.parser_generate.set_defaults(func=self.generate) - - # merge - self.parser_merge = self.subparsers.add_parser( - "merge", help="Merge JSON meta files" - ) - self.parser_merge.add_argument( - "-i", dest="input", action="append", nargs="+", required=True - ) - self.parser_merge.set_defaults(func=self.merge) - - def generate(self): - meta = {} - for k, v in vars(self.args).items(): - if k in ["project", "func", "debug"]: - continue - if isinstance(v, str): - v = v.strip('"') - meta[self.args.project + "_" + k] = v - - print(json.dumps(meta, indent=4)) - return 0 - - def merge(self): - full = {} - for path in self.args.input[0]: - with open(path, mode="r") as file: - dict = json.loads(file.read()) - full.update(dict) - - print(json.dumps(full, indent=4)) - return 0 - - -if __name__ == "__main__": - Main()() diff --git a/scripts/ob.data b/scripts/ob.data index 5276a5103bf..9f6f1d08161 100644 --- a/scripts/ob.data +++ b/scripts/ob.data @@ -19,7 +19,7 @@ FSD:0x0:r DDS:0x1:r C2OPT:0x1:r NBRSD:0x0:r -SNBRSA:0xD:r +SNBRSA:0xB:r BRSD:0x0:r SBRSA:0x12:r SBRV:0x35C00:r diff --git a/scripts/ob.py b/scripts/ob.py index 722549d69a7..3480f275e7e 100755 --- a/scripts/ob.py +++ b/scripts/ob.py @@ -1,69 +1,100 @@ #!/usr/bin/env python3 -import logging -import argparse -import subprocess -import sys -import os +from os import path from flipper.app import App -from flipper.cube import CubeProgrammer +from flipper.utils.programmer_openocd import OpenOCDProgrammer class Main(App): def init(self): + # Subparsers self.subparsers = self.parser.add_subparsers(help="sub-command help") + + # Check command self.parser_check = self.subparsers.add_parser( "check", help="Check Option Bytes" ) - self._addArgsSWD(self.parser_check) + self._add_args(self.parser_check) self.parser_check.set_defaults(func=self.check) + # Set command self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes") - self._addArgsSWD(self.parser_set) + self._add_args(self.parser_set) self.parser_set.set_defaults(func=self.set) - # OB - self.ob = {} - def _addArgsSWD(self, parser): + # Recover command + self.parser_recover = self.subparsers.add_parser( + "recover", help="Recover Option Bytes" + ) + self._add_args(self.parser_recover) + self.parser_recover.set_defaults(func=self.recover) + + def _add_args(self, parser): + parser.add_argument( + "--port-base", type=int, help="OpenOCD port base", default=3333 + ) parser.add_argument( - "--port", type=str, help="Port to connect: swd or usb1", default="swd" + "--interface", + type=str, + help="OpenOCD interface", + default="interface/cmsis-dap.cfg", + ) + parser.add_argument( + "--serial", type=str, help="OpenOCD interface serial number" + ) + parser.add_argument( + "--ob-path", + type=str, + help="Option bytes file", + default=path.join(path.dirname(__file__), "ob.data"), ) - parser.add_argument("--serial", type=str, help="ST-Link Serial Number") - - def _getCubeParams(self): - return { - "port": self.args.port, - "serial": self.args.serial, - } - - def before(self): - self.logger.info(f"Loading Option Bytes data") - file_path = os.path.join(os.path.dirname(sys.argv[0]), "ob.data") - with open(file_path, "r") as file: - for line in file.readlines(): - k, v, o = line.split(":") - self.ob[k.strip()] = v.strip(), o.strip() def check(self): - self.logger.info(f"Checking Option Bytes") - cp = CubeProgrammer(self._getCubeParams()) - if cp.checkOptionBytes(self.ob): - self.logger.info(f"OB Check OK") - return 0 - else: - self.logger.error(f"OB Check FAIL") - return 255 + self.logger.info("Checking Option Bytes") + + # OpenOCD + openocd = OpenOCDProgrammer( + self.args.interface, + self.args.port_base, + self.args.serial, + ) + + return_code = 1 + if openocd.option_bytes_validate(self.args.ob_path): + return_code = 0 + + return return_code def set(self): - self.logger.info(f"Setting Option Bytes") - cp = CubeProgrammer(self._getCubeParams()) - if cp.setOptionBytes(self.ob): - self.logger.info(f"OB Set OK") - return 0 - else: - self.logger.error(f"OB Set FAIL") - return 255 + self.logger.info("Setting Option Bytes") + + # OpenOCD + openocd = OpenOCDProgrammer( + self.args.interface, + self.args.port_base, + self.args.serial, + ) + + return_code = 1 + if openocd.option_bytes_set(self.args.ob_path): + return_code = 0 + + return return_code + + def recover(self): + self.logger.info("Setting Option Bytes") + + # OpenOCD + openocd = OpenOCDProgrammer( + self.args.interface, + self.args.port_base, + self.args.serial, + ) + + openocd.option_bytes_recover() + + return 0 if __name__ == "__main__": diff --git a/scripts/otp.py b/scripts/otp.py index 48056fd2ac9..e3f070999a7 100755 --- a/scripts/otp.py +++ b/scripts/otp.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 +import datetime import logging -import argparse -import subprocess import os -import sys import re import struct -import datetime + +from flipper.app import App +from flipper.utils.programmer_openocd import OpenOCDProgrammer, OpenOCDProgrammerResult OTP_MAGIC = 0xBABE OTP_VERSION = 0x02 @@ -17,6 +17,7 @@ "unknown": 0x00, "black": 0x01, "white": 0x02, + "transparent": 0x03, } OTP_REGIONS = { @@ -33,8 +34,14 @@ "mgg": 0x02, } -from flipper.app import App -from flipper.cube import CubeProgrammer + +class OTPException(Exception): + def __init__(self, message: str, result: OpenOCDProgrammerResult): + self.message = message + self.result = result + + def get_exit_code(self) -> int: + return int(self.result.value) class Main(App): @@ -53,21 +60,21 @@ def init(self): self.parser_flash_first = self.subparsers.add_parser( "flash_first", help="Flash first block of OTP to device" ) - self._addArgsSWD(self.parser_flash_first) + self._addArgsOpenOCD(self.parser_flash_first) self._addFirstArgs(self.parser_flash_first) self.parser_flash_first.set_defaults(func=self.flash_first) # Flash Second self.parser_flash_second = self.subparsers.add_parser( "flash_second", help="Flash second block of OTP to device" ) - self._addArgsSWD(self.parser_flash_second) + self._addArgsOpenOCD(self.parser_flash_second) self._addSecondArgs(self.parser_flash_second) self.parser_flash_second.set_defaults(func=self.flash_second) # Flash All self.parser_flash_all = self.subparsers.add_parser( "flash_all", help="Flash OTP to device" ) - self._addArgsSWD(self.parser_flash_all) + self._addArgsOpenOCD(self.parser_flash_all) self._addFirstArgs(self.parser_flash_all) self._addSecondArgs(self.parser_flash_all) self.parser_flash_all.set_defaults(func=self.flash_all) @@ -75,17 +82,19 @@ def init(self): self.logger = logging.getLogger() self.timestamp = datetime.datetime.now().timestamp() - def _addArgsSWD(self, parser): + def _addArgsOpenOCD(self, parser): parser.add_argument( - "--port", type=str, help="Port to connect: swd or usb1", default="swd" + "--port-base", type=int, help="OpenOCD port base", default=3333 + ) + parser.add_argument( + "--interface", + type=str, + help="OpenOCD interface", + default="interface/cmsis-dap.cfg", + ) + parser.add_argument( + "--serial", type=str, help="OpenOCD interface serial number" ) - parser.add_argument("--serial", type=str, help="ST-Link Serial Number") - - def _getCubeParams(self): - return { - "port": self.args.port, - "serial": self.args.serial, - } def _addFirstArgs(self, parser): parser.add_argument("--version", type=int, help="Version", required=True) @@ -147,7 +156,7 @@ def _packSecond(self): ) def generate_all(self): - self.logger.info(f"Generating OTP") + self.logger.info("Generating OTP") self._processFirstArgs() self._processSecondArgs() with open(f"{self.args.file}_first.bin", "wb") as file: @@ -161,55 +170,73 @@ def generate_all(self): return 0 def flash_first(self): - self.logger.info(f"Flashing first block of OTP") + self.logger.info("Flashing first block of OTP") self._processFirstArgs() filename = f"otp_unknown_first_{self.timestamp}.bin" try: - self.logger.info(f"Packing binary data") + self.logger.info("Packing binary data") with open(filename, "wb") as file: file.write(self._packFirst()) - self.logger.info(f"Flashing OTP") - cp = CubeProgrammer(self._getCubeParams()) - cp.flashBin("0x1FFF7000", filename) - cp.resetTarget() - self.logger.info(f"Flashed Successfully") - os.remove(filename) - except Exception as e: + self.logger.info("Flashing OTP") + + openocd = OpenOCDProgrammer( + self.args.interface, + self.args.port_base, + self.args.serial, + ) + + programmer_result = openocd.otp_write(0x1FFF7000, filename) + if programmer_result != OpenOCDProgrammerResult.Success: + raise OTPException("Failed to flash OTP", programmer_result) + + self.logger.info("Flashed Successfully") + except OTPException as e: self.logger.exception(e) - return 1 + return e.get_exit_code() + finally: + os.remove(filename) return 0 def flash_second(self): - self.logger.info(f"Flashing second block of OTP") + self.logger.info("Flashing second block of OTP") self._processSecondArgs() filename = f"otp_{self.args.name}_second_{self.timestamp}.bin" try: - self.logger.info(f"Packing binary data") + self.logger.info("Packing binary data") with open(filename, "wb") as file: file.write(self._packSecond()) - self.logger.info(f"Flashing OTP") - cp = CubeProgrammer(self._getCubeParams()) - cp.flashBin("0x1FFF7010", filename) - cp.resetTarget() - self.logger.info(f"Flashed Successfully") - os.remove(filename) - except Exception as e: + self.logger.info("Flashing OTP") + + openocd = OpenOCDProgrammer( + self.args.interface, + self.args.port_base, + self.args.serial, + ) + + programmer_result = openocd.otp_write(0x1FFF7010, filename) + if programmer_result != OpenOCDProgrammerResult.Success: + raise OTPException("Failed to flash OTP", programmer_result) + + self.logger.info("Flashed Successfully") + except OTPException as e: self.logger.exception(e) - return 1 + return e.get_exit_code() + finally: + os.remove(filename) return 0 def flash_all(self): - self.logger.info(f"Flashing OTP") + self.logger.info("Flashing OTP") self._processFirstArgs() self._processSecondArgs() @@ -217,20 +244,29 @@ def flash_all(self): filename = f"otp_{self.args.name}_whole_{self.timestamp}.bin" try: - self.logger.info(f"Packing binary data") + self.logger.info("Packing binary data") with open(filename, "wb") as file: file.write(self._packFirst()) file.write(self._packSecond()) - self.logger.info(f"Flashing OTP") - cp = CubeProgrammer(self._getCubeParams()) - cp.flashBin("0x1FFF7000", filename) - cp.resetTarget() - self.logger.info(f"Flashed Successfully") - os.remove(filename) - except Exception as e: + self.logger.info("Flashing OTP") + + openocd = OpenOCDProgrammer( + self.args.interface, + self.args.port_base, + self.args.serial, + ) + + programmer_result = openocd.otp_write(0x1FFF7000, filename) + if programmer_result != OpenOCDProgrammerResult.Success: + raise OTPException("Failed to flash OTP", programmer_result) + + self.logger.info("Flashed Successfully") + except OTPException as e: self.logger.exception(e) - return 1 + return e.get_exit_code() + finally: + os.remove(filename) return 0 diff --git a/scripts/power.py b/scripts/power.py new file mode 100755 index 00000000000..50bb2d4f700 --- /dev/null +++ b/scripts/power.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 + +import time +from typing import Optional + +from flipper.app import App +from flipper.storage import FlipperStorage +from flipper.utils.cdc import resolve_port + + +class Main(App): + # this is basic use without sub-commands, simply to reboot flipper / power it off, not meant as a full CLI wrapper + def init(self): + self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") + + self.subparsers = self.parser.add_subparsers(help="sub-command help") + + self.parser_power_off = self.subparsers.add_parser( + "power_off", help="Power off command, won't return to CLI" + ) + self.parser_power_off.set_defaults(func=self.power_off) + + self.parser_reboot = self.subparsers.add_parser( + "reboot", help="Reboot command help" + ) + self.parser_reboot.set_defaults(func=self.reboot) + + self.parser_reboot2dfu = self.subparsers.add_parser( + "reboot2dfu", help="Reboot to DFU, won't return to CLI" + ) + self.parser_reboot2dfu.set_defaults(func=self.reboot2dfu) + + def _get_flipper(self, retry_count: Optional[int] = 1): + port = None + self.logger.info(f"Attempting to find flipper with {retry_count} attempts.") + + for i in range(retry_count): + time.sleep(1) + self.logger.info(f"Attempting to find flipper #{i}.") + + if port := resolve_port(self.logger, self.args.port): + self.logger.info(f"Found flipper at {port}") + break + + if not port: + self.logger.info(f"Failed to find flipper") + return None + + flipper = FlipperStorage(port) + flipper.start() + return flipper + + def power_off(self): + if not (flipper := self._get_flipper(retry_count=10)): + return 1 + + self.logger.info("Powering off") + flipper.send("power off" + "\r") + flipper.stop() + return 0 + + def reboot(self): + if not (flipper := self._get_flipper(retry_count=10)): + return 1 + + self.logger.info("Rebooting") + flipper.send("power reboot" + "\r") + flipper.stop() + return 0 + + def reboot2dfu(self): + if not (flipper := self._get_flipper(retry_count=10)): + return 1 + + self.logger.info("Rebooting to DFU") + flipper.send("power reboot2dfu" + "\r") + flipper.stop() + + return 0 + + +if __name__ == "__main__": + Main()() diff --git a/scripts/requirements.txt b/scripts/requirements.txt deleted file mode 100644 index 4c4b7279c74..00000000000 --- a/scripts/requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -pyserial==3.5 -heatshrink2==0.11.0 -Pillow==9.1.1 -grpcio==1.47.0 -grpcio-tools==1.47.0 -protobuf==3.20.1 -python3-protobuf==2.5.0 diff --git a/scripts/runfap.py b/scripts/runfap.py new file mode 100755 index 00000000000..42141acff65 --- /dev/null +++ b/scripts/runfap.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 + +import operator +from functools import reduce + +from flipper.app import App +from flipper.storage import FlipperStorage, FlipperStorageOperations +from flipper.utils.cdc import resolve_port + + +class Main(App): + def init(self): + self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") + self.parser.add_argument( + "--sources", + "-s", + nargs="+", + action="append", + default=[], + help="Files to send", + ) + self.parser.add_argument( + "--targets", + "-t", + nargs="+", + action="append", + default=[], + help="File destinations (must be same length as -s)", + ) + self.parser.add_argument( + "--host-app", + "-a", + help="Host app to launch", + ) + + self.parser.set_defaults(func=self.install) + + @staticmethod + def flatten(item_list): + return reduce(operator.concat, item_list, []) + + def install(self): + self.args.sources = self.flatten(self.args.sources) + self.args.targets = self.flatten(self.args.targets) + + if len(self.args.sources) != len(self.args.targets): + self.logger.error( + f"Error: sources ({self.args.sources}) and targets ({self.args.targets}) must be same length" + ) + return 1 + + if not (port := resolve_port(self.logger, self.args.port)): + return 2 + + try: + with FlipperStorage(port) as storage: + storage_ops = FlipperStorageOperations(storage) + for fap_local_path, fap_dst_path in zip( + self.args.sources, self.args.targets + ): + self.logger.info(f'Installing "{fap_local_path}" to {fap_dst_path}') + + storage_ops.recursive_send(fap_dst_path, fap_local_path, False) + + fap_host_app = self.args.targets[0] + startup_command = f"{fap_host_app}" + if self.args.host_app: + startup_command = self.args.host_app + + self.logger.info(f"Launching app: {startup_command}") + storage.send_and_wait_eol(f"loader open {startup_command}\r") + + if len(result := storage.read.until(storage.CLI_EOL)): + self.logger.error(f"Unexpected response: {result.decode('ascii')}") + return 3 + return 0 + + except Exception as e: + self.logger.error(f"Error: {e}") + # raise + return 4 + + +if __name__ == "__main__": + Main()() diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py old mode 100644 new mode 100755 index 1e95ee2f23f..2cf43dce02a --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -1,10 +1,15 @@ #!/usr/bin/env python3 +import json +import shutil +import tarfile +import zipfile +from os import makedirs, walk +from os.path import basename, exists, join, relpath + +from ansi.color import fg from flipper.app import App -from os.path import join, exists -from os import makedirs from update import Main as UpdateMain -import shutil class ProjectDir: @@ -17,6 +22,9 @@ def __init__(self, project_dir): class Main(App): + DIST_FILE_PREFIX = "flipper-z-" + DIST_FOLDER_MAX_NAME_LENGTH = 80 + def init(self): self.subparsers = self.parser.add_subparsers(help="sub-command help") @@ -42,27 +50,43 @@ def init(self): ) self.parser_copy.set_defaults(func=self.copy) - def get_project_filename(self, project, filetype): + def get_project_file_name(self, project: ProjectDir, filetype: str) -> str: # Temporary fix project_name = project.project if project_name == "firmware" and filetype != "elf": project_name = "full" - return f"flipper-z-{self.target}-{project_name}-{self.args.suffix}.{filetype}" - def get_dist_filepath(self, filename): + dist_target_path = self.get_dist_file_name(project_name, filetype) + self.note_dist_component( + project_name, filetype, self.get_dist_path(dist_target_path) + ) + return dist_target_path + + def note_dist_component(self, component: str, extension: str, srcpath: str) -> None: + self._dist_components[f"{component}.{extension}"] = srcpath + + def get_dist_file_name(self, dist_artifact_type: str, filetype: str) -> str: + return f"{self.DIST_FILE_PREFIX}{self.target}-{dist_artifact_type}-{self.args.suffix}.{filetype}" + + def get_dist_path(self, filename: str) -> str: return join(self.output_dir_path, filename) - def copy_single_project(self, project): + def copy_single_project(self, project: ProjectDir) -> None: obj_directory = join("build", project.dir) for filetype in ("elf", "bin", "dfu", "json"): - shutil.copyfile( - join(obj_directory, f"{project.project}.{filetype}"), - self.get_dist_filepath(self.get_project_filename(project, filetype)), - ) + if exists(src_file := join(obj_directory, f"{project.project}.{filetype}")): + shutil.copyfile( + src_file, + self.get_dist_path(self.get_project_file_name(project, filetype)), + ) + for foldertype in ("sdk_headers", "lib"): + if exists(sdk_folder := join(obj_directory, foldertype)): + self.note_dist_component(foldertype, "dir", sdk_folder) - def copy(self): - self.projects = dict( + def copy(self) -> int: + self._dist_components: dict[str, str] = dict() + self.projects: dict[str, ProjectDir] = dict( map( lambda pd: (pd.project, pd), map(ProjectDir, self.args.project), @@ -90,53 +114,162 @@ def copy(self): try: shutil.rmtree(self.output_dir_path) except Exception as ex: - pass + self.logger.warn(f"Failed to clean output directory: {ex}") if not exists(self.output_dir_path): + self.logger.debug(f"Creating output directory {self.output_dir_path}") makedirs(self.output_dir_path) + for folder in ("debug", "scripts"): + if exists(folder): + self.note_dist_component(folder, "dir", folder) + for project in self.projects.values(): + self.logger.debug(f"Copying {project.project} for {project.target}") self.copy_single_project(project) self.logger.info( - f"Firmware binaries can be found at:\n\t{self.output_dir_path}" + fg.boldgreen( + f"Firmware binaries can be found at:\n\t{self.output_dir_path}" + ) ) if self.args.version: - bundle_dir = join( - self.output_dir_path, f"{self.target}-update-{self.args.suffix}" + if bundle_result := self.bundle_update_package(): + return bundle_result + + required_components = ("firmware.elf", "full.bin", "update.dir") + if all( + map( + lambda c: c in self._dist_components, + required_components, ) - bundle_args = [ - "generate", - "-d", - bundle_dir, - "-v", - self.args.version, - "-t", - self.target, - "--dfu", - self.get_dist_filepath( - self.get_project_filename(self.projects["firmware"], "dfu") - ), - "--stage", - self.get_dist_filepath( - self.get_project_filename(self.projects["updater"], "bin") + ): + self.bundle_sdk() + + return 0 + + def bundle_sdk(self): + self.logger.info("Bundling SDK") + components_paths = dict() + + sdk_components_keys = ( + "full.bin", + "firmware.elf", + "update.dir", + "sdk_headers.dir", + "lib.dir", + "scripts.dir", + ) + + with zipfile.ZipFile( + self.get_dist_path(self.get_dist_file_name("sdk", "zip")), + "w", + zipfile.ZIP_DEFLATED, + ) as zf: + for component_key in sdk_components_keys: + component_path = self._dist_components.get(component_key) + + if component_key.endswith(".dir"): + components_paths[component_key] = basename(component_path) + for root, dirnames, files in walk(component_path): + if "__pycache__" in dirnames: + dirnames.remove("__pycache__") + for file in files: + zf.write( + join(root, file), + join( + components_paths[component_key], + relpath( + join(root, file), + component_path, + ), + ), + ) + else: + # We use fixed names for files to avoid having to regenerate VSCode project + components_paths[component_key] = component_key + zf.write(component_path, component_key) + + zf.writestr( + "components.json", + json.dumps( + { + "meta": { + "hw_target": self.target, + "flavor": self.flavor, + "version": self.args.version, + }, + "components": components_paths, + } ), - ] - if self.args.resources: - bundle_args.extend( - ( - "-r", - self.args.resources, - ) + ) + + def bundle_update_package(self): + self.logger.debug( + f"Generating update bundle with version {self.args.version} for {self.target}" + ) + bundle_dir_name = f"{self.target}-update-{self.args.suffix}"[ + : self.DIST_FOLDER_MAX_NAME_LENGTH + ] + bundle_dir = self.get_dist_path(bundle_dir_name) + bundle_args = [ + "generate", + "-d", + bundle_dir, + "-v", + self.args.version, + "-t", + self.target, + "--dfu", + self.get_dist_path( + self.get_project_file_name(self.projects["firmware"], "dfu") + ), + "--stage", + self.get_dist_path( + self.get_project_file_name(self.projects["updater"], "bin") + ), + ] + if self.args.resources: + bundle_args.extend( + ( + "-r", + self.args.resources, ) - bundle_args.extend(self.other_args) + ) + bundle_args.extend(self.other_args) + + if (bundle_result := UpdateMain(no_exit=True)(bundle_args)) == 0: + self.note_dist_component("update", "dir", bundle_dir) self.logger.info( - f"Use this directory to self-update your Flipper:\n\t{bundle_dir}" + fg.boldgreen( + f"Use this directory to self-update your Flipper:\n\t{bundle_dir}" + ) ) - return UpdateMain(no_exit=True)(bundle_args) - return 0 + # Create tgz archive + with tarfile.open( + join( + self.output_dir_path, + bundle_tgz := f"{self.DIST_FILE_PREFIX}{bundle_dir_name}.tgz", + ), + "w:gz", + compresslevel=9, + format=tarfile.USTAR_FORMAT, + ) as tar: + self.note_dist_component( + "update", "tgz", self.get_dist_path(bundle_tgz) + ) + + # Strip uid and gid in case of overflow + def tar_filter(tarinfo): + tarinfo.uid = tarinfo.gid = 0 + tarinfo.mtime = 0 + tarinfo.uname = tarinfo.gname = "furippa" + return tarinfo + + tar.add(bundle_dir, arcname=bundle_dir_name, filter=tar_filter) + return bundle_result if __name__ == "__main__": diff --git a/scripts/selfupdate.py b/scripts/selfupdate.py old mode 100644 new mode 100755 index 1c16c5ca6a6..d222bf24916 --- a/scripts/selfupdate.py +++ b/scripts/selfupdate.py @@ -1,14 +1,12 @@ #!/usr/bin/env python3 -from typing import final -from flipper.app import App -from flipper.storage import FlipperStorage -from flipper.utils.cdc import resolve_port - import logging import os import pathlib -import serial.tools.list_ports as list_ports + +from flipper.app import App +from flipper.storage import FlipperStorage, FlipperStorageOperations +from flipper.utils.cdc import resolve_port class Main(App): @@ -17,96 +15,54 @@ def init(self): self.parser.add_argument("manifest_path", help="Manifest path") self.parser.add_argument( - "--pkg_dir_name", help="Update dir name", default="pcbundle", required=False + "--pkg_dir_name", help="Update dir name", default=None, required=False ) self.parser.set_defaults(func=self.install) # logging self.logger = logging.getLogger() - # make directory with exist check - def mkdir_on_storage(self, storage, flipper_dir_path): - if not storage.exist_dir(flipper_dir_path): - self.logger.debug(f'"{flipper_dir_path}" does not exist, creating') - if not storage.mkdir(flipper_dir_path): - self.logger.error(f"Error: {storage.last_error}") - return False - else: - self.logger.debug(f'"{flipper_dir_path}" already exists') - return True - - # send file with exist check and hash check - def send_file_to_storage(self, storage, flipper_file_path, local_file_path, force): - exists = storage.exist_file(flipper_file_path) - do_upload = not exists - if exists: - hash_local = storage.hash_local(local_file_path) - hash_flipper = storage.hash_flipper(flipper_file_path) - self.logger.debug(f"hash check: local {hash_local}, flipper {hash_flipper}") - do_upload = force or (hash_local != hash_flipper) - - if do_upload: - self.logger.info(f'Sending "{local_file_path}" to "{flipper_file_path}"') - if not storage.send_file(local_file_path, flipper_file_path): - self.logger.error(f"Error: {storage.last_error}") - return False - return True - def install(self): if not (port := resolve_port(self.logger, self.args.port)): return 1 - storage = FlipperStorage(port) - storage.start() - - try: - if not os.path.isfile(self.args.manifest_path): - self.logger.error("Error: manifest not found") - return 2 + if not os.path.isfile(self.args.manifest_path): + self.logger.error("Error: manifest not found") + return 2 - manifest_path = pathlib.Path(os.path.abspath(self.args.manifest_path)) - manifest_name, pkg_name = manifest_path.parts[-1], manifest_path.parts[-2] + manifest_path = pathlib.Path(os.path.abspath(self.args.manifest_path)) + manifest_name, pkg_name = manifest_path.parts[-1], manifest_path.parts[-2] - pkg_dir_name = self.args.pkg_dir_name or pkg_name - update_root = "/ext/update" - flipper_update_path = f"{update_root}/{pkg_dir_name}" + pkg_dir_name = self.args.pkg_dir_name or pkg_name + update_root = "/ext/update" + flipper_update_path = f"{update_root}/{pkg_dir_name}" - self.logger.info(f'Installing "{pkg_name}" from {flipper_update_path}') - # if not os.path.exists(self.args.manifest_path): - # self.logger.error("Error: package not found") - if not self.mkdir_on_storage( - storage, update_root - ) or not self.mkdir_on_storage(storage, flipper_update_path): - self.logger.error(f"Error: cannot create {storage.last_error}") - return -2 + self.logger.info(f'Installing "{pkg_name}" from {flipper_update_path}') - for dirpath, dirnames, filenames in os.walk(manifest_path.parents[0]): - for fname in filenames: - self.logger.debug(f"Uploading {fname}") - local_file_path = os.path.join(dirpath, fname) - flipper_file_path = f"{flipper_update_path}/{fname}" - if not self.send_file_to_storage( - storage, flipper_file_path, local_file_path, False - ): - self.logger.error(f"Error: {storage.last_error}") - return -3 + try: + with FlipperStorage(port) as storage: + storage_ops = FlipperStorageOperations(storage) + storage_ops.mkpath(update_root) + storage_ops.mkpath(flipper_update_path) + storage_ops.recursive_send( + flipper_update_path, manifest_path.parents[0] + ) - # return -11 storage.send_and_wait_eol( f"update install {flipper_update_path}/{manifest_name}\r" ) result = storage.read.until(storage.CLI_EOL) - if not b"Verifying" in result: + if b"Verifying" not in result: self.logger.error(f"Unexpected response: {result.decode('ascii')}") - return -4 + return 3 result = storage.read.until(storage.CLI_EOL) if not result.startswith(b"OK"): self.logger.error(result.decode("ascii")) - return -5 - break - return 0 - finally: - storage.stop() + return 4 + return 0 + except Exception as e: + self.logger.error(e) + return 5 if __name__ == "__main__": diff --git a/scripts/serial_cli.py b/scripts/serial_cli.py index e07e6bfb4b8..8e35d57facd 100644 --- a/scripts/serial_cli.py +++ b/scripts/serial_cli.py @@ -1,13 +1,30 @@ +import argparse import logging +import os import subprocess +import sys + from flipper.utils.cdc import resolve_port def main(): logger = logging.getLogger() - if not (port := resolve_port(logger, "auto")): + parser = argparse.ArgumentParser() + parser.add_argument("-p", "--port", help="CDC Port", default="auto") + args = parser.parse_args() + if not (port := resolve_port(logger, args.port)): + logger.error("Is Flipper connected via USB and not in DFU mode?") return 1 - subprocess.call(["python3", "-m", "serial.tools.miniterm", "--raw", port, "230400"]) + subprocess.call( + [ + os.path.basename(sys.executable), + "-m", + "serial.tools.miniterm", + "--raw", + port, + "230400", + ] + ) if __name__ == "__main__": diff --git a/scripts/slideshow.py b/scripts/slideshow.py old mode 100644 new mode 100755 index 7626a6aca05..8a9541a7c17 --- a/scripts/slideshow.py +++ b/scripts/slideshow.py @@ -36,7 +36,7 @@ def pack(self): file_idx += 1 except Exception as e: self.logger.error(e) - break + return 3 widths = set(img.width for img in images) heights = set(img.height for img in images) diff --git a/scripts/storage.py b/scripts/storage.py index 167ba88eb10..e04eaa7e1f0 100755 --- a/scripts/storage.py +++ b/scripts/storage.py @@ -1,15 +1,27 @@ #!/usr/bin/env python3 -from flipper.app import App -from flipper.storage import FlipperStorage -from flipper.utils.cdc import resolve_port - -import logging -import os import binascii import filecmp +import os import tempfile +from flipper.app import App +from flipper.storage import FlipperStorage, FlipperStorageOperations +from flipper.utils.cdc import resolve_port + + +def WrapStorageOp(func): + def wrapper(*args, **kwargs): + try: + func(*args, **kwargs) + return 0 + except Exception as e: + print(f"Error: {e}") + # raise # uncomment to debug + return 1 + + return wrapper + class Main(App): def init(self): @@ -21,6 +33,11 @@ def init(self): self.parser_mkdir.add_argument("flipper_path", help="Flipper path") self.parser_mkdir.set_defaults(func=self.mkdir) + self.parser_format = self.subparsers.add_parser( + "format_ext", help="Format flash card" + ) + self.parser_format.set_defaults(func=self.format_ext) + self.parser_remove = self.subparsers.add_parser( "remove", help="Remove file/directory" ) @@ -66,218 +83,71 @@ def init(self): ) self.parser_stress.set_defaults(func=self.stress) - def _get_storage(self): + def _get_port(self): if not (port := resolve_port(self.logger, self.args.port)): - return None - - storage = FlipperStorage(port) - storage.start() - return storage + raise Exception("Failed to resolve port") + return port + @WrapStorageOp def mkdir(self): - if not (storage := self._get_storage()): - return 1 - self.logger.debug(f'Creating "{self.args.flipper_path}"') - if not storage.mkdir(self.args.flipper_path): - self.logger.error(f"Error: {storage.last_error}") - storage.stop() - return 0 + with FlipperStorage(self._get_port()) as storage: + storage.mkdir(self.args.flipper_path) + @WrapStorageOp def remove(self): - if not (storage := self._get_storage()): - return 1 - self.logger.debug(f'Removing "{self.args.flipper_path}"') - if not storage.remove(self.args.flipper_path): - self.logger.error(f"Error: {storage.last_error}") - storage.stop() - return 0 + with FlipperStorage(self._get_port()) as storage: + storage.remove(self.args.flipper_path) + @WrapStorageOp def receive(self): - if not (storage := self._get_storage()): - return 1 - - if storage.exist_dir(self.args.flipper_path): - for dirpath, dirnames, filenames in storage.walk(self.args.flipper_path): - self.logger.debug( - f'Processing directory "{os.path.normpath(dirpath)}"'.replace( - os.sep, "/" - ) - ) - dirnames.sort() - filenames.sort() - - rel_path = os.path.relpath(dirpath, self.args.flipper_path) - - for dirname in dirnames: - local_dir_path = os.path.join( - self.args.local_path, rel_path, dirname - ) - local_dir_path = os.path.normpath(local_dir_path) - os.makedirs(local_dir_path, exist_ok=True) - - for filename in filenames: - local_file_path = os.path.join( - self.args.local_path, rel_path, filename - ) - local_file_path = os.path.normpath(local_file_path) - flipper_file_path = os.path.normpath( - os.path.join(dirpath, filename) - ).replace(os.sep, "/") - self.logger.info( - f'Receiving "{flipper_file_path}" to "{local_file_path}"' - ) - if not storage.receive_file(flipper_file_path, local_file_path): - self.logger.error(f"Error: {storage.last_error}") - - else: - self.logger.info( - f'Receiving "{self.args.flipper_path}" to "{self.args.local_path}"' + with FlipperStorage(self._get_port()) as storage: + FlipperStorageOperations(storage).recursive_receive( + self.args.flipper_path, self.args.local_path ) - if not storage.receive_file(self.args.flipper_path, self.args.local_path): - self.logger.error(f"Error: {storage.last_error}") - storage.stop() - return 0 + @WrapStorageOp def send(self): - if not (storage := self._get_storage()): - return 1 - - self.send_to_storage( - storage, self.args.flipper_path, self.args.local_path, self.args.force - ) - storage.stop() - return 0 - - # send file or folder recursively - def send_to_storage(self, storage, flipper_path, local_path, force): - if not os.path.exists(local_path): - self.logger.error(f'Error: "{local_path}" is not exist') - - if os.path.isdir(local_path): - # create parent dir - self.mkdir_on_storage(storage, flipper_path) - - for dirpath, dirnames, filenames in os.walk(local_path): - self.logger.debug(f'Processing directory "{os.path.normpath(dirpath)}"') - dirnames.sort() - filenames.sort() - rel_path = os.path.relpath(dirpath, local_path) - - # create subdirs - for dirname in dirnames: - flipper_dir_path = os.path.join(flipper_path, rel_path, dirname) - flipper_dir_path = os.path.normpath(flipper_dir_path).replace( - os.sep, "/" - ) - self.mkdir_on_storage(storage, flipper_dir_path) - - # send files - for filename in filenames: - flipper_file_path = os.path.join(flipper_path, rel_path, filename) - flipper_file_path = os.path.normpath(flipper_file_path).replace( - os.sep, "/" - ) - local_file_path = os.path.normpath(os.path.join(dirpath, filename)) - self.send_file_to_storage( - storage, flipper_file_path, local_file_path, force - ) - else: - self.send_file_to_storage(storage, flipper_path, local_path, force) - - # make directory with exist check - def mkdir_on_storage(self, storage, flipper_dir_path): - if not storage.exist_dir(flipper_dir_path): - self.logger.debug(f'"{flipper_dir_path}" does not exist, creating') - if not storage.mkdir(flipper_dir_path): - self.logger.error(f"Error: {storage.last_error}") - else: - self.logger.debug(f'"{flipper_dir_path}" already exists') - - # send file with exist check and hash check - def send_file_to_storage(self, storage, flipper_file_path, local_file_path, force): - if not storage.exist_file(flipper_file_path): - self.logger.debug( - f'"{flipper_file_path}" does not exist, sending "{local_file_path}"' - ) - self.logger.info(f'Sending "{local_file_path}" to "{flipper_file_path}"') - if not storage.send_file(local_file_path, flipper_file_path): - self.logger.error(f"Error: {storage.last_error}") - elif force: - self.logger.debug( - f'"{flipper_file_path}" exists, but will be overwritten by "{local_file_path}"' - ) - self.logger.info(f'Sending "{local_file_path}" to "{flipper_file_path}"') - if not storage.send_file(local_file_path, flipper_file_path): - self.logger.error(f"Error: {storage.last_error}") - else: - self.logger.debug( - f'"{flipper_file_path}" exists, compare hash with "{local_file_path}"' + with FlipperStorage(self._get_port()) as storage: + FlipperStorageOperations(storage).recursive_send( + self.args.flipper_path, self.args.local_path, self.args.force ) - hash_local = storage.hash_local(local_file_path) - hash_flipper = storage.hash_flipper(flipper_file_path) - - if not hash_flipper: - self.logger.error(f"Error: {storage.last_error}") - - if hash_local == hash_flipper: - self.logger.debug( - f'"{flipper_file_path}" is equal to "{local_file_path}"' - ) - else: - self.logger.debug( - f'"{flipper_file_path}" is NOT equal to "{local_file_path}"' - ) - self.logger.info( - f'Sending "{local_file_path}" to "{flipper_file_path}"' - ) - if not storage.send_file(local_file_path, flipper_file_path): - self.logger.error(f"Error: {storage.last_error}") + @WrapStorageOp def read(self): - if not (storage := self._get_storage()): - return 1 - self.logger.debug(f'Reading "{self.args.flipper_path}"') - data = storage.read_file(self.args.flipper_path) - if not data: - self.logger.error(f"Error: {storage.last_error}") - else: + with FlipperStorage(self._get_port()) as storage: + data = storage.read_file(self.args.flipper_path) try: print("Text data:") print(data.decode()) - except: + except Exception: print("Binary hexadecimal data:") print(binascii.hexlify(data).decode()) - storage.stop() - return 0 + @WrapStorageOp def size(self): - if not (storage := self._get_storage()): - return 1 - self.logger.debug(f'Getting size of "{self.args.flipper_path}"') - size = storage.size(self.args.flipper_path) - if size < 0: - self.logger.error(f"Error: {storage.last_error}") - else: - print(size) - storage.stop() - return 0 + with FlipperStorage(self._get_port()) as storage: + print(storage.size(self.args.flipper_path)) + @WrapStorageOp def list(self): - if not (storage := self._get_storage()): - return 1 - self.logger.debug(f'Listing "{self.args.flipper_path}"') - storage.list_tree(self.args.flipper_path) - storage.stop() - return 0 + with FlipperStorage(self._get_port()) as storage: + storage.list_tree(self.args.flipper_path) + @WrapStorageOp + def format_ext(self): + self.logger.debug("Formatting /ext SD card") + with FlipperStorage(self._get_port()) as storage: + storage.format_ext() + + @WrapStorageOp def stress(self): self.logger.error("This test is wearing out flash memory.") - self.logger.error("Never use it with internal storage(/int)") + self.logger.error("Never use it with internal storage (/int)") if self.args.flipper_path.startswith( "/int" @@ -296,24 +166,19 @@ def stress(self): with open(send_file_name, "w") as fout: fout.write("A" * self.args.file_size) - storage = self._get_storage() - if not storage: - return 1 - - if storage.exist_file(self.args.flipper_path): - self.logger.error("File exists, remove it first") - return - while self.args.count > 0: - storage.send_file(send_file_name, self.args.flipper_path) - storage.receive_file(self.args.flipper_path, receive_file_name) - if not filecmp.cmp(receive_file_name, send_file_name): - self.logger.error("Files mismatch") - break - storage.remove(self.args.flipper_path) - os.unlink(receive_file_name) - self.args.count -= 1 - storage.stop() - return 0 + with FlipperStorage(self._get_port()) as storage: + if storage.exist_file(self.args.flipper_path): + self.logger.error("File exists, remove it first") + return + while self.args.count > 0: + storage.send_file(send_file_name, self.args.flipper_path) + storage.receive_file(self.args.flipper_path, receive_file_name) + if not filecmp.cmp(receive_file_name, send_file_name): + self.logger.error("Files mismatch") + break + storage.remove(self.args.flipper_path) + os.unlink(receive_file_name) + self.args.count -= 1 if __name__ == "__main__": diff --git a/scripts/testing/await_flipper.py b/scripts/testing/await_flipper.py new file mode 100755 index 00000000000..f8dffeb66bf --- /dev/null +++ b/scripts/testing/await_flipper.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +import logging +import os +import sys +import time + + +def flp_serial_by_name(flp_name): + if sys.platform == "darwin": # MacOS + flp_serial = "/dev/cu.usbmodemflip_" + flp_name + "1" + logging.info(f"Darwin, looking for {flp_serial}") + elif sys.platform == "linux": # Linux + flp_serial = ( + "/dev/serial/by-id/usb-Flipper_Devices_Inc._Flipper_" + + flp_name + + "_flip_" + + flp_name + + "-if00" + ) + logging.info(f"linux, looking for {flp_serial}") + + if os.path.exists(flp_serial): + return flp_serial + else: + logging.info(f"Couldn't find {flp_name} on this attempt.") + if os.path.exists(flp_name): + return flp_name + else: + return "" + + +UPDATE_TIMEOUT = 30 * 4 # 4 minutes + + +def main(): + flipper_name = sys.argv[1] + elapsed = 0 + flipper = flp_serial_by_name(flipper_name) + logging.basicConfig( + format="%(asctime)s %(levelname)-8s %(message)s", + level=logging.INFO, + datefmt="%Y-%m-%d %H:%M:%S", + ) + logging.info(f"Waiting for Flipper {flipper_name} to be ready...") + + while flipper == "" and elapsed < UPDATE_TIMEOUT: + elapsed += 1 + time.sleep(1) + flipper = flp_serial_by_name(flipper_name) + + if flipper == "": + logging.error("Flipper not found!") + exit(1) + + logging.info(f"Found Flipper at {flipper}") + + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/scripts/testing/units.py b/scripts/testing/units.py new file mode 100755 index 00000000000..db302e9daeb --- /dev/null +++ b/scripts/testing/units.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +import logging +import re +import sys + +import serial +from await_flipper import flp_serial_by_name + + +def main(): + logging.basicConfig( + format="%(asctime)s %(levelname)-8s %(message)s", + level=logging.INFO, + datefmt="%Y-%m-%d %H:%M:%S", + ) + logging.info("Trying to run units on flipper") + flp_serial = flp_serial_by_name(sys.argv[1]) + + if flp_serial == "": + logging.error("Flipper not found!") + sys.exit(1) + + with serial.Serial(flp_serial, timeout=150) as flipper: + logging.info(f"Found Flipper at {flp_serial}") + flipper.baudrate = 230400 + flipper.flushOutput() + flipper.flushInput() + + flipper.read_until(b">: ").decode("utf-8") + flipper.write(b"unit_tests\r") + data = flipper.read_until(b">: ").decode("utf-8") + + lines = data.split("\r\n") + + tests_re = r"Failed tests: \d{0,}" + time_re = r"Consumed: \d{0,}" + leak_re = r"Leaked: \d{0,}" + status_re = r"Status: \w{3,}" + + tests_pattern = re.compile(tests_re) + time_pattern = re.compile(time_re) + leak_pattern = re.compile(leak_re) + status_pattern = re.compile(status_re) + + tests, time, leak, status = None, None, None, None + total = 0 + + for line in lines: + logging.info(line) + if "()" in line: + total += 1 + + if not tests: + tests = re.match(tests_pattern, line) + if not time: + time = re.match(time_pattern, line) + if not leak: + leak = re.match(leak_pattern, line) + if not status: + status = re.match(status_pattern, line) + + if None in (tests, time, leak, status): + logging.error(f"Failed to parse output: {leak} {time} {leak} {status}") + sys.exit(1) + + leak = int(re.findall(r"[- ]\d+", leak.group(0))[0]) + status = re.findall(r"\w+", status.group(0))[1] + tests = int(re.findall(r"\d+", tests.group(0))[0]) + time = int(re.findall(r"\d+", time.group(0))[0]) + + if tests > 0 or status != "PASSED": + logging.error(f"Got {tests} failed tests.") + logging.error(f"Leaked (not failing on this stat): {leak}") + logging.error(f"Status: {status}") + logging.error(f"Time: {time/1000} seconds") + sys.exit(1) + + logging.info(f"Leaked (not failing on this stat): {leak}") + logging.info( + f"Tests ran successfully! Time elapsed {time/1000} seconds. Passed {total} tests." + ) + + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index aac2a33091a..51708b8c488 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -1,37 +1,51 @@ @echo off -if not [%FBT_ROOT%] == [] ( +if not ["%FBT_ROOT%"] == [""] ( goto already_set ) set "FBT_ROOT=%~dp0\..\..\" -pushd %FBT_ROOT% +pushd "%FBT_ROOT%" set "FBT_ROOT=%cd%" popd -if not [%FBT_NOENV%] == [] ( +if not ["%FBT_NOENV%"] == [""] ( exit /b 0 ) -set "FLIPPER_TOOLCHAIN_VERSION=8" -set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\i686-windows" +set "FLIPPER_TOOLCHAIN_VERSION=23" +if ["%FBT_TOOLCHAIN_PATH%"] == [""] ( + set "FBT_TOOLCHAIN_PATH=%FBT_ROOT%" +) + +set "FBT_TOOLCHAIN_ROOT=%FBT_TOOLCHAIN_PATH%\toolchain\x86_64-windows" + +set "FBT_TOOLCHAIN_VERSION_FILE=%FBT_TOOLCHAIN_ROOT%\VERSION" if not exist "%FBT_TOOLCHAIN_ROOT%" ( - powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" "%flipper_toolchain_version%" + powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" %flipper_toolchain_version% "%FBT_TOOLCHAIN_ROOT%" ) -if not exist "%FBT_TOOLCHAIN_ROOT%\VERSION" ( - powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" "%flipper_toolchain_version%" + +if not exist "%FBT_TOOLCHAIN_VERSION_FILE%" ( + 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_ROOT%\VERSION" + +set /p REAL_TOOLCHAIN_VERSION=<"%FBT_TOOLCHAIN_VERSION_FILE%" if not "%REAL_TOOLCHAIN_VERSION%" == "%FLIPPER_TOOLCHAIN_VERSION%" ( - powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" "%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%" set "PYTHONHOME=%FBT_TOOLCHAIN_ROOT%\python" set "PYTHONPATH=" +set "PYTHONNOUSERSITE=1" set "PATH=%FBT_TOOLCHAIN_ROOT%\python;%FBT_TOOLCHAIN_ROOT%\bin;%FBT_TOOLCHAIN_ROOT%\protoc\bin;%FBT_TOOLCHAIN_ROOT%\openocd\bin;%PATH%" set "PROMPT=(fbt) %PROMPT%" diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index 654b1fe0d7f..990776b27a0 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -4,34 +4,130 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; -SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"8"}"; -FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"23"}"; + +if [ -z ${FBT_TOOLCHAIN_PATH+x} ] ; then + FBT_TOOLCHAIN_PATH_WAS_SET=0; +else + FBT_TOOLCHAIN_PATH_WAS_SET=1; +fi + +FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$DEFAULT_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 "Example:"; + printf "\tsource scripts/toolchain/fbtenv.sh --restore\n"; +} + +fbtenv_curl() +{ + curl --progress-bar -SLo "$1" "$2"; +} + +fbtenv_wget() +{ + wget --show-progress --progress=bar:force -qO "$1" "$2"; +} + +fbtenv_restore_env() +{ + TOOLCHAIN_ARCH_DIR_SED="$(echo "$TOOLCHAIN_ARCH_DIR" | sed 's/\//\\\//g')" + PATH="$(echo "$PATH" | sed "s/$TOOLCHAIN_ARCH_DIR_SED\/python\/bin://g")"; + PATH="$(echo "$PATH" | sed "s/$TOOLCHAIN_ARCH_DIR_SED\/bin://g")"; + PATH="$(echo "$PATH" | sed "s/$TOOLCHAIN_ARCH_DIR_SED\/protobuf\/bin://g")"; + PATH="$(echo "$PATH" | sed "s/$TOOLCHAIN_ARCH_DIR_SED\/openocd\/bin://g")"; + PATH="$(echo "$PATH" | 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 + + 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 + + if [ "$SYS_TYPE" = "Linux" ]; then + if [ -n "$SAVED_TERMINFO_DIRS" ]; then + export TERMINFO_DIRS="$SAVED_TERMINFO_DIRS"; + else + unset TERMINFO_DIRS; + fi + unset SAVED_TERMINFO_DIRS; + 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; + + unset FBT_TOOLCHAIN_VERSION; + unset FBT_TOOLCHAIN_PATH; +} fbtenv_check_sourced() { + if [ -n "${FBT_SKIP_CHECK_SOURCED:-""}" ]; then + return 0; + fi case "${ZSH_EVAL_CONTEXT:-""}" in *:file:*) + setopt +o nomatch; # disabling 'no match found' warning in zsh return 0;; esac - case ${0##*/} in dash|-dash|bash|-bash|ksh|-ksh|sh|-sh) + if [ ${0##*/} = "fbtenv.sh" ]; then # excluding script itself + fbtenv_show_usage; + return 1; + fi + case ${0##*/} in dash|-dash|bash|-bash|ksh|-ksh|sh|-sh|*.sh|fbt|ufbt) return 0;; esac - if [ "$(basename $0)" = "fbt" ]; then - return 0; + fbtenv_show_usage; + return 1; +} + +fbtenv_check_if_sourced_multiple_times() +{ + if ! echo "${PS1:-""}" | grep -qF "[fbt]"; then + if ! echo "${PROMPT:-""}" | grep -qF "[fbt]"; then + return 0; + fi fi - echo "Running this script manually is wrong, please source it"; - echo "Example:"; - printf "\tsource scripts/toolchain/fbtenv.sh\n"; return 1; } -fbtenv_check_script_path() +fbtenv_set_shell_prompt() { - if [ ! -x "$SCRIPT_PATH/fbt" ]; then - echo "Please source this script being into flipperzero-firmware root directory, or specify 'SCRIPT_PATH' manually"; + if [ -n "${PS1:-""}" ]; then + PS1="[fbt]$PS1"; + elif [ -n "${PROMPT:-""}" ]; then + PROMPT="[fbt]$PROMPT"; + fi + return 0; # all other shells +} + +fbtenv_check_env_vars() +{ + # Return error if FBT_TOOLCHAIN_PATH is not set before script is sourced or if fbt executable is not in DEFAULT_SCRIPT_PATH + if [ "$FBT_TOOLCHAIN_PATH_WAS_SET" -eq 0 ] && [ ! -x "$DEFAULT_SCRIPT_PATH/fbt" ] && [ ! -x "$DEFAULT_SCRIPT_PATH/ufbt" ] ; then + echo "Please source this script from [u]fbt root directory, or specify 'FBT_TOOLCHAIN_PATH' variable manually"; echo "Example:"; - printf "\tSCRIPT_PATH=lang/c/flipperzero-firmware source lang/c/flipperzero-firmware/scripts/fbtenv.sh\n"; - echo "If current directory is right, type 'unset SCRIPT_PATH' and try again" + printf "\tFBT_TOOLCHAIN_PATH=lang/c/flipperzero-firmware source lang/c/flipperzero-firmware/scripts/fbtenv.sh\n"; + echo "If current directory is right, type 'unset FBT_TOOLCHAIN_PATH' and try again" return 1; fi return 0; @@ -42,7 +138,7 @@ fbtenv_get_kernel_type() SYS_TYPE="$(uname -s)"; ARCH_TYPE="$(uname -m)"; if [ "$ARCH_TYPE" != "x86_64" ] && [ "$SYS_TYPE" != "Darwin" ]; then - echo "Now we provide toolchain only for x86_64 arhitecture, sorry.."; + echo "We only provide toolchain for x86_64 CPUs, sorry.."; return 1; fi if [ "$SYS_TYPE" = "Darwin" ]; then @@ -53,10 +149,10 @@ fbtenv_get_kernel_type() TOOLCHAIN_ARCH_DIR="$FBT_TOOLCHAIN_PATH/toolchain/x86_64-linux"; TOOLCHAIN_URL="https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-linux-flipper-$FBT_TOOLCHAIN_VERSION.tar.gz"; elif echo "$SYS_TYPE" | grep -q "MINGW"; then - echo "In MinGW shell use \"fbt.cmd\" instead of \"fbt\""; + echo "In MinGW shell, use \"[u]fbt.cmd\" instead of \"[u]fbt\""; return 1; else - echo "Your system is not recognized. Sorry.. Please report us your configuration."; + echo "Your system configuration is not supported. Sorry.. Please report us your configuration."; return 1; fi return 0; @@ -65,9 +161,9 @@ fbtenv_get_kernel_type() fbtenv_check_rosetta() { if [ "$ARCH_TYPE" = "arm64" ]; then - if ! /usr/bin/pgrep -q oahd; then + if ! pgrep -q oahd; then echo "Flipper Zero Toolchain needs Rosetta2 to run under Apple Silicon"; - echo "Please instal it by typing 'softwareupdate --install-rosetta --agree-to-license'"; + echo "Please install it by typing 'softwareupdate --install-rosetta --agree-to-license'"; return 1; fi fi @@ -76,7 +172,7 @@ fbtenv_check_rosetta() fbtenv_check_tar() { - printf "Checking tar.."; + printf "Checking for tar.."; if ! tar --version > /dev/null 2>&1; then echo "no"; return 1; @@ -87,7 +183,7 @@ fbtenv_check_tar() fbtenv_check_downloaded_toolchain() { - printf "Checking downloaded toolchain tgz.."; + printf "Checking if downloaded toolchain tgz exists.."; if [ ! -f "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" ]; then echo "no"; return 1; @@ -100,15 +196,17 @@ fbtenv_download_toolchain_tar() { echo "Downloading toolchain:"; mkdir -p "$FBT_TOOLCHAIN_PATH/toolchain" || return 1; - "$DOWNLOADER" $DOWNLOADER_ARGS "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" "$TOOLCHAIN_URL" || return 1; + "$FBT_DOWNLOADER" "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR.part" "$TOOLCHAIN_URL" || return 1; + # restoring oroginal filename if file downloaded successfully + mv "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR.part" "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" echo "done"; return 0; } -fbtenv_remove_old_tooclhain() +fbtenv_remove_old_toolchain() { - printf "Removing old toolchain (if exist).."; - rm -rf "${TOOLCHAIN_ARCH_DIR}"; + printf "Removing old toolchain.."; + rm -rf "${TOOLCHAIN_ARCH_DIR:?}"; echo "done"; } @@ -126,7 +224,7 @@ fbtenv_show_unpack_percentage() fbtenv_unpack_toolchain() { - echo "Unpacking toolchain:"; + echo "Unpacking toolchain to '$FBT_TOOLCHAIN_PATH/toolchain':"; tar -xvf "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" -C "$FBT_TOOLCHAIN_PATH/toolchain" 2>&1 | fbtenv_show_unpack_percentage; mkdir -p "$FBT_TOOLCHAIN_PATH/toolchain" || return 1; mv "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_DIR" "$TOOLCHAIN_ARCH_DIR" || return 1; @@ -134,11 +232,17 @@ fbtenv_unpack_toolchain() return 0; } -fbtenv_clearing() +fbtenv_cleanup() { - printf "Clearing.."; - rm -rf "${FBT_TOOLCHAIN_PATH:?}/toolchain/$TOOLCHAIN_TAR"; - echo "done"; + if [ -n "${FBT_TOOLCHAIN_PATH:-""}" ]; then + printf "Cleaning up.."; + rm -rf "${FBT_TOOLCHAIN_PATH:?}/toolchain/"*.part; + if [ -z "${FBT_PRESERVE_TAR:-""}" ]; then + rm -rf "${FBT_TOOLCHAIN_PATH:?}/toolchain/"*.tar.gz; + fi + echo "done"; + fi + trap - 2; return 0; } @@ -155,17 +259,15 @@ fbtenv_curl_wget_check() echo; echo "$TOOLCHAIN_URL"; echo; - echo "And place in $FBT_TOOLCHAIN_PATH/toolchain/ dir mannualy"; + echo "And place in $FBT_TOOLCHAIN_PATH/toolchain/ dir manually"; return 1; fi echo "yes" - DOWNLOADER="wget"; - DOWNLOADER_ARGS="--show-progress --progress=bar:force -qO"; + FBT_DOWNLOADER="fbtenv_wget"; return 0; fi echo "yes" - DOWNLOADER="curl"; - DOWNLOADER_ARGS="--progress-bar -SLo"; + FBT_DOWNLOADER="fbtenv_curl"; return 0; } @@ -176,6 +278,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; @@ -186,26 +289,68 @@ fbtenv_download_toolchain() fbtenv_check_tar || return 1; TOOLCHAIN_TAR="$(basename "$TOOLCHAIN_URL")"; TOOLCHAIN_DIR="$(echo "$TOOLCHAIN_TAR" | sed "s/-$FBT_TOOLCHAIN_VERSION.tar.gz//g")"; + trap fbtenv_cleanup 2; # trap will be restored in fbtenv_cleanup if ! fbtenv_check_downloaded_toolchain; then fbtenv_curl_wget_check || return 1; - fbtenv_download_toolchain_tar; + fbtenv_download_toolchain_tar || return 1; fi - fbtenv_remove_old_tooclhain; - fbtenv_unpack_toolchain || { fbtenv_clearing && return 1; }; - fbtenv_clearing; + fbtenv_remove_old_toolchain; + fbtenv_unpack_toolchain || return 1; + fbtenv_cleanup; return 0; } +fbtenv_print_config() +{ + if [ -n "${FBT_VERBOSE:-""}" ]; then + echo "FBT: using toolchain version $(cat "$TOOLCHAIN_ARCH_DIR/VERSION")"; + if [ -n "${FBT_SKIP_CHECK_SOURCED:-""}" ]; then + echo "FBT: fbtenv will not check if it is sourced or not"; + fi + if [ -n "${FBT_PRESERVE_TAR:-""}" ]; then + echo "FBT: toolchain archives will be saved"; + fi + fi +} + fbtenv_main() { fbtenv_check_sourced || return 1; - fbtenv_check_script_path || return 1; fbtenv_get_kernel_type || return 1; + if [ "$1" = "--restore" ]; then + fbtenv_restore_env; + return 0; + fi + if ! fbtenv_check_if_sourced_multiple_times; then + return 0; + fi; + fbtenv_check_env_vars || return 1; fbtenv_check_download_toolchain || return 1; + fbtenv_set_shell_prompt; + fbtenv_print_config; 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; + + 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:-""}"; + + 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=; + + if [ "$SYS_TYPE" = "Linux" ]; then + export SAVED_TERMINFO_DIRS="${TERMINFO_DIRS:-""}"; + export TERMINFO_DIRS="$TOOLCHAIN_ARCH_DIR/ncurses/share/terminfo"; + fi } -fbtenv_main; +fbtenv_main "${1:-""}"; diff --git a/scripts/toolchain/windows-toolchain-download.ps1 b/scripts/toolchain/windows-toolchain-download.ps1 index bcb8f998f25..f30b157dab6 100644 --- a/scripts/toolchain/windows-toolchain-download.ps1 +++ b/scripts/toolchain/windows-toolchain-download.ps1 @@ -1,34 +1,46 @@ Set-StrictMode -Version 2.0 $ErrorActionPreference = "Stop" [Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls" -$repo_root = (Get-Item "$PSScriptRoot\..\..").FullName +# TODO FL-3536: fix path to download_dir +$download_dir = (Get-Item "$PSScriptRoot\..\..").FullName $toolchain_version = $args[0] -$toolchain_url = "https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-i686-windows-flipper-$toolchain_version.zip" -$toolchain_zip = "gcc-arm-none-eabi-10.3-i686-windows-flipper-$toolchain_version.zip" -$toolchain_dir = "gcc-arm-none-eabi-10.3-i686-windows-flipper" +$toolchain_target_path = $args[1] -if (Test-Path -LiteralPath "$repo_root\toolchain\i686-windows") { +$toolchain_url = "https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-windows-flipper-$toolchain_version.zip" +$toolchain_dist_folder = "gcc-arm-none-eabi-10.3-x86_64-windows-flipper" +$toolchain_zip = "$toolchain_dist_folder-$toolchain_version.zip" + +$toolchain_zip_temp_path = "$download_dir\$toolchain_zip" +$toolchain_dist_temp_path = "$download_dir\$toolchain_dist_folder" + +if (Test-Path -LiteralPath "$toolchain_target_path") { Write-Host -NoNewline "Removing old Windows toolchain.." - Remove-Item -LiteralPath "$repo_root\toolchain\i686-windows" -Force -Recurse + Remove-Item -LiteralPath "$toolchain_target_path" -Force -Recurse Write-Host "done!" } -if (!(Test-Path -Path "$repo_root\$toolchain_zip" -PathType Leaf)) { +if (!(Test-Path -Path "$toolchain_zip_temp_path" -PathType Leaf)) { Write-Host -NoNewline "Downloading Windows toolchain.." $wc = New-Object net.webclient - $wc.Downloadfile("$toolchain_url", "$repo_root\$toolchain_zip") + $wc.Downloadfile("$toolchain_url", "$toolchain_zip_temp_path") Write-Host "done!" } -if (!(Test-Path -LiteralPath "$repo_root\toolchain")) { - New-Item "$repo_root\toolchain" -ItemType Directory +if (!(Test-Path -LiteralPath "$toolchain_target_path\..")) { + New-Item "$toolchain_target_path\.." -ItemType Directory -Force } -Write-Host -NoNewline "Unziping Windows toolchain.." +Write-Host -NoNewline "Extracting Windows toolchain.." +# This is faster than Expand-Archive Add-Type -Assembly "System.IO.Compression.Filesystem" -[System.IO.Compression.ZipFile]::ExtractToDirectory("$toolchain_zip", "$repo_root\") -Move-Item -Path "$repo_root\$toolchain_dir" -Destination "$repo_root\toolchain\i686-windows" +[System.IO.Compression.ZipFile]::ExtractToDirectory("$toolchain_zip_temp_path", "$download_dir") +# Expand-Archive -LiteralPath "$toolchain_zip_temp_path" -DestinationPath "$download_dir" + +Write-Host -NoNewline "moving.." +Move-Item -LiteralPath "$toolchain_dist_temp_path" -Destination "$toolchain_target_path" -Force Write-Host "done!" -Write-Host -NoNewline "Clearing temporary files.." -Remove-Item -LiteralPath "$repo_root\$toolchain_zip" -Force +Write-Host -NoNewline "Cleaning up temporary files.." +Remove-Item -LiteralPath "$toolchain_zip_temp_path" -Force Write-Host "done!" + +# dasdasd diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct new file mode 100644 index 00000000000..46d6635788e --- /dev/null +++ b/scripts/ufbt/SConstruct @@ -0,0 +1,485 @@ +import multiprocessing +import os +import pathlib + +from SCons.Errors import UserError +from SCons.Node import FS +from SCons.Platform import TempFileMunge + +SetOption("num_jobs", multiprocessing.cpu_count()) +SetOption("max_drift", 1) +# SetOption("silent", False) + +ufbt_state_dir = Dir(os.environ.get("UFBT_STATE_DIR", "#.ufbt")) +ufbt_script_dir = Dir(os.environ.get("UFBT_SCRIPT_DIR")) +ufbt_build_dir = ufbt_state_dir.Dir("build") + +ufbt_current_sdk_dir = ufbt_state_dir.Dir("current") + +SConsignFile(ufbt_state_dir.File(".sconsign.dblite").abspath) + +ufbt_variables = SConscript("commandline.scons") + +forward_os_env = { + # Import PATH from OS env - scons doesn't do that by default + "PATH": os.environ["PATH"], +} + +# Proxying environment to child processes & scripts +variables_to_forward = [ + # CI/CD variables + "WORKFLOW_BRANCH_OR_TAG", + "DIST_SUFFIX", + # Python & other tools + "HOME", + "APPDATA", + "PYTHONHOME", + "PYTHONNOUSERSITE", + "TMP", + "TEMP", + # Colors for tools + "TERM", +] + +if proxy_env := GetOption("proxy_env"): + variables_to_forward.extend(proxy_env.split(",")) + +for env_value_name in variables_to_forward: + if environ_value := os.environ.get(env_value_name, None): + forward_os_env[env_value_name] = environ_value + +# Core environment init - loads SDK state, sets up paths, etc. +core_env = Environment( + variables=ufbt_variables, + ENV=forward_os_env, + UFBT_STATE_DIR=ufbt_state_dir, + UFBT_CURRENT_SDK_DIR=ufbt_current_sdk_dir, + UFBT_SCRIPT_DIR=ufbt_script_dir, + toolpath=[ufbt_current_sdk_dir.Dir("scripts/ufbt/site_tools")], + tools=[ + "ufbt_state", + ("ufbt_help", {"vars": ufbt_variables}), + ], +) + +core_env.Append(CPPDEFINES=GetOption("extra_defines")) + +# Now we can import stuff bundled with SDK - it was added to sys.path by ufbt_state + +from fbt.appmanifest import FlipperApplication, FlipperAppType +from fbt.sdk.cache import SdkCache +from fbt.util import ( + path_as_posix, + resolve_real_dir_node, + single_quote, + tempfile_arg_esc_func, + wrap_tempfile, +) + +# Base environment with all tools loaded from SDK +env = core_env.Clone( + toolpath=[core_env["FBT_SCRIPT_DIR"].Dir("fbt_tools")], + tools=[ + "fbt_tweaks", + ( + "crosscc", + { + "toolchain_prefix": "arm-none-eabi-", + "versions": (" 10.3",), + }, + ), + "fwbin", + "python3", + "sconsrecursiveglob", + "sconsmodular", + "ccache", + "fbt_apps", + "fbt_extapps", + "fbt_assets", + "fbt_envhooks", + ("compilation_db", {"COMPILATIONDB_COMSTR": "\tCDB\t${TARGET}"}), + ], + FBT_FAP_DEBUG_ELF_ROOT=ufbt_build_dir, + TEMPFILE=TempFileMunge, + MAXLINELENGTH=2048, + PROGSUFFIX=".elf", + TEMPFILEARGESCFUNC=tempfile_arg_esc_func, + SINGLEQUOTEFUNC=single_quote, + ABSPATHGETTERFUNC=resolve_real_dir_node, + APPS=[], + EXTRA_EXT_APPS=[], + UFBT_API_VERSION=SdkCache( + core_env.subst("$SDK_DEFINITION"), load_version_only=True + ).version, + APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}\n\t\tTarget: ${TARGET_HW}, API: ${UFBT_API_VERSION}", +) + +wrap_tempfile(env, "LINKCOM") +wrap_tempfile(env, "ARCOM") + +env.PreConfigureUfbtEnvionment() + +# print(env.Dump()) + +# Dist env + +dist_env = env.Clone( + tools=[ + "fbt_dist", + "fbt_debugopts", + "openocd", + "blackmagic", + "jflash", + "textfile", + ], + ENV=os.environ, + OPENOCD_OPTS=[ + "-f", + "interface/stlink.cfg", + "-c", + "transport select hla_swd", + "-f", + "${FBT_DEBUG_DIR}/stm32wbx.cfg", + "-c", + "stm32wbx.cpu configure -rtos auto", + ], +) + +flash_target = dist_env.FwFlash( + dist_env["UFBT_STATE_DIR"].File("flash"), + dist_env["FW_ELF"], +) +dist_env.Alias("firmware_flash", flash_target) +dist_env.Alias("flash", flash_target) +if env["FORCE"]: + env.AlwaysBuild(flash_target) + + +firmware_jflash = dist_env.JFlash( + dist_env["UFBT_STATE_DIR"].File("jflash"), + dist_env["FW_BIN"], + JFLASHADDR="0x08000000", +) +dist_env.Alias("firmware_jflash", firmware_jflash) +dist_env.Alias("jflash", firmware_jflash) +if env["FORCE"]: + env.AlwaysBuild(firmware_jflash) + + +firmware_debug = dist_env.PhonyTarget( + "debug", + "${GDBPYCOM}", + source=dist_env["FW_ELF"], + GDBOPTS="${GDBOPTS_BASE}", + GDBREMOTE="${OPENOCD_GDB_PIPE}", + FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(dist_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")), +) + +dist_env.PhonyTarget( + "blackmagic", + "${GDBPYCOM}", + source=dist_env["FW_ELF"], + GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", + GDBREMOTE="${BLACKMAGIC_ADDR}", + FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(dist_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")), +) + +# Debug alien elf +debug_other_opts = [ + "-ex", + "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py", + "-ex", + "source ${FBT_DEBUG_DIR}/flipperversion.py", + "-ex", + "fw-version", +] + +dist_env.PhonyTarget( + "debug_other", + "${GDBPYCOM}", + GDBOPTS="${GDBOPTS_BASE}", + GDBREMOTE="${OPENOCD_GDB_PIPE}", + GDBPYOPTS=debug_other_opts, +) + +dist_env.PhonyTarget( + "debug_other_blackmagic", + "${GDBPYCOM}", + GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", + GDBREMOTE="${BLACKMAGIC_ADDR}", + GDBPYOPTS=debug_other_opts, +) + +flash_usb_full = dist_env.UsbInstall( + dist_env["UFBT_STATE_DIR"].File("usbinstall"), + [], +) +dist_env.AlwaysBuild(flash_usb_full) +dist_env.Alias("flash_usb", flash_usb_full) +dist_env.Alias("flash_usb_full", flash_usb_full) + +# App build environment + +appenv = env.Clone( + CCCOM=env["CCCOM"].replace("$CFLAGS", "$CFLAGS_APP $CFLAGS"), + CXXCOM=env["CXXCOM"].replace("$CXXFLAGS", "$CXXFLAGS_APP $CXXFLAGS"), + LINKCOM=env["LINKCOM"].replace("$LINKFLAGS", "$LINKFLAGS_APP $LINKFLAGS"), + COMPILATIONDB_USE_ABSPATH=True, +) + + +original_app_dir = Dir(appenv.subst("$UFBT_APP_DIR")) +app_mount_point = Dir("#/app/") +app_mount_point.addRepository(original_app_dir) + +appenv.LoadAppManifest(app_mount_point) +appenv.PrepareApplicationsBuild() + +####################### + +apps_artifacts = appenv["EXT_APPS"] + +apps_to_build_as_faps = [ + FlipperAppType.PLUGIN, + FlipperAppType.EXTERNAL, + FlipperAppType.MENUEXTERNAL, +] + +known_extapps = [ + app + for apptype in apps_to_build_as_faps + for app in appenv["APPBUILD"].get_apps_of_type(apptype, True) +] +incompatible_apps = [] +for app in known_extapps: + if not app.supports_hardware_target(appenv.subst("f${TARGET_HW}")): + incompatible_apps.append(app) + continue + + app_artifacts = appenv.BuildAppElf(app) + app_src_dir = resolve_real_dir_node(app_artifacts.app._appdir) + app_artifacts.installer = [ + appenv.Install(app_src_dir.Dir("dist"), app_artifacts.compact), + appenv.Install(app_src_dir.Dir("dist").Dir("debug"), app_artifacts.debug), + ] + +if len(incompatible_apps): + print( + "WARNING: The following apps are not compatible with the current target hardware and will not be built: {}".format( + ", ".join([app.name for app in incompatible_apps]) + ) + ) + +if appenv["FORCE"]: + appenv.AlwaysBuild([extapp.compact for extapp in apps_artifacts.values()]) + +# Final steps - target aliases + +install_and_check = [ + (extapp.installer, extapp.validator) for extapp in apps_artifacts.values() +] +Alias( + "faps", + install_and_check, +) +Default(install_and_check) + +# Compilation database + +fwcdb = appenv.CompilationDatabase( + original_app_dir.Dir(".vscode").File("compile_commands.json") +) + +AlwaysBuild(fwcdb) +Precious(fwcdb) +NoClean(fwcdb) +if len(apps_artifacts): + Default(fwcdb) + + +# launch handler +runnable_apps = appenv["APPBUILD"].get_apps_of_type(FlipperAppType.EXTERNAL, True) + +app_to_launch = None +if len(runnable_apps) == 1: + app_to_launch = runnable_apps[0].appid +elif len(runnable_apps) > 1: + # more than 1 app - try to find one with matching id + app_to_launch = appenv.subst("$APPID") + + +def ambiguous_app_call(**kw): + raise UserError( + f"More than one app is runnable: {', '.join(app.appid for app in runnable_apps)}. Please specify an app with APPID=..." + ) + + +if app_to_launch: + appenv.AddAppLaunchTarget(app_to_launch, "launch") + appenv.AddAppBuildTarget(app_to_launch, "build") +else: + dist_env.PhonyTarget("launch", Action(ambiguous_app_call, None)) + dist_env.PhonyTarget("build", Action(ambiguous_app_call, None)) + +# cli handler + +appenv.PhonyTarget( + "cli", + [["${PYTHON3}", "${FBT_SCRIPT_DIR}/serial_cli.py", "-p", "${FLIP_PORT}"]], +) + +# Update WiFi devboard firmware +dist_env.PhonyTarget( + "devboard_flash", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/wifi_board.py"]] +) + +# Linter + +dist_env.PhonyTarget( + "lint", + [["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "check", "${LINT_SOURCES}"]], + source=original_app_dir.File(".clang-format"), + LINT_SOURCES=[original_app_dir], +) + +dist_env.PhonyTarget( + "format", + [["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "format", "${LINT_SOURCES}"]], + source=original_app_dir.File(".clang-format"), + LINT_SOURCES=[original_app_dir], +) + + +# Prepare vscode environment +def _path_as_posix(path): + return pathlib.Path(path).as_posix() + + +vscode_dist = [] +project_template_dir = dist_env["UFBT_SCRIPT_ROOT"].Dir("project_template") +for template_file in project_template_dir.Dir(".vscode").glob("*"): + vscode_dist.append( + dist_env.Substfile( + original_app_dir.Dir(".vscode").File(template_file.name), + template_file, + SUBST_DICT={ + "@UFBT_VSCODE_PATH_SEP@": os.path.pathsep, + "@UFBT_TOOLCHAIN_ARM_TOOLCHAIN_DIR@": pathlib.Path( + dist_env.WhereIs("arm-none-eabi-gcc") + ).parent.as_posix(), + "@UFBT_TOOLCHAIN_GCC@": _path_as_posix( + dist_env.WhereIs("arm-none-eabi-gcc") + ), + "@UFBT_TOOLCHAIN_GDB_PY@": _path_as_posix( + dist_env.WhereIs("arm-none-eabi-gdb-py") + ), + "@UFBT_TOOLCHAIN_OPENOCD@": _path_as_posix(dist_env.WhereIs("openocd")), + "@UFBT_APP_DIR@": _path_as_posix(original_app_dir.abspath), + "@UFBT_ROOT_DIR@": _path_as_posix(Dir("#").abspath), + "@UFBT_DEBUG_DIR@": dist_env["FBT_DEBUG_DIR"], + "@UFBT_DEBUG_ELF_DIR@": _path_as_posix( + dist_env["FBT_FAP_DEBUG_ELF_ROOT"].abspath + ), + "@UFBT_FIRMWARE_ELF@": _path_as_posix(dist_env["FW_ELF"].abspath), + }, + ) + ) + +for config_file in project_template_dir.glob(".*"): + if isinstance(config_file, FS.Dir): + continue + vscode_dist.append(dist_env.Install(original_app_dir, config_file)) + +dist_env.Precious(vscode_dist) +dist_env.NoClean(vscode_dist) +dist_env.Alias("vscode_dist", vscode_dist) + + +# Creating app from base template + +dist_env.SetDefault(FBT_APPID=appenv.subst("$APPID") or "template") +if fbt_appid := dist_env.subst("$FBT_APPID"): + if not FlipperApplication.APP_ID_REGEX.match(fbt_appid): + raise UserError( + f"Invalid app id '{fbt_appid}'. App id must match {FlipperApplication.APP_ID_REGEX.pattern}" + ) + +app_template_dir = project_template_dir.Dir("app_template") +app_template_dist = [] +for template_file in app_template_dir.glob("*"): + dist_file_name = dist_env.subst(template_file.name) + if template_file.name.endswith(".png"): + app_template_dist.append( + dist_env.InstallAs(original_app_dir.File(dist_file_name), template_file) + ) + else: + app_template_dist.append( + dist_env.Substfile( + original_app_dir.File(dist_file_name), + template_file, + SUBST_DICT={ + "@FBT_APPID@": dist_env.subst("$FBT_APPID"), + }, + ) + ) +AddPostAction( + app_template_dist[-1], + [ + Mkdir(original_app_dir.Dir("images")), + Touch(original_app_dir.Dir("images").File(".gitkeep")), + # scons' glob ignores .dot directories, so we need to copy .github manually + Copy(original_app_dir.Dir(".github"), app_template_dir.Dir(".github")), + ], +) +dist_env.Precious(app_template_dist) +dist_env.NoClean(app_template_dist) +dist_env.Alias("create", app_template_dist) + +dist_env.PhonyTarget( + "get_blackmagic", + "@echo $( ${BLACKMAGIC_ADDR} $)", +) + +dist_env.PhonyTarget( + "get_apiversion", + "@echo $( ${UFBT_API_VERSION} $)", +) + +# Dolphin animation builder. Expects "external" directory in current dir +# with animation sources & manifests. Builds & uploads them to connected Flipper +dolphin_src_dir = original_app_dir.Dir("external") +if dolphin_src_dir.exists(): + dolphin_dir = ufbt_build_dir.Dir("dolphin") + dolphin_external = dist_env.DolphinExtBuilder( + ufbt_build_dir.Dir("dolphin"), + original_app_dir, + DOLPHIN_RES_TYPE="external", + ) + dist_env.PhonyTarget( + "dolphin_ext", + [ + [ + "${PYTHON3}", + "${FBT_SCRIPT_DIR}/storage.py", + "-p", + "${FLIP_PORT}", + "send", + "${SOURCE}", + "/ext/dolphin", + ] + ], + source=ufbt_build_dir.Dir("dolphin"), + ) +else: + + def missing_dolphin_folder(**kw): + raise UserError(f"Dolphin folder not found: {dolphin_src_dir}") + + dist_env.PhonyTarget("dolphin_ext", Action(missing_dolphin_folder, None)) + +dist_env.PhonyTarget( + "env", + "@echo $( ${FBT_SCRIPT_DIR.abspath}/toolchain/fbtenv.sh $)", +) + +dist_env.PostConfigureUfbtEnvionment() diff --git a/scripts/ufbt/commandline.scons b/scripts/ufbt/commandline.scons new file mode 100644 index 00000000000..a3ba7e08dae --- /dev/null +++ b/scripts/ufbt/commandline.scons @@ -0,0 +1,98 @@ +AddOption( + "--extra-define", + action="append", + dest="extra_defines", + default=[], + help="Extra global define that will be passed to C/C++ compiler, can be specified multiple times", +) + +AddOption( + "--proxy-env", + action="store", + dest="proxy_env", + default="", + help="Comma-separated list of additional environment variables to pass to " + "child SCons processes", +) + +vars = Variables("ufbt_options.py", ARGUMENTS) + +vars.AddVariables( + BoolVariable( + "VERBOSE", + help="Print full commands", + default=False, + ), + BoolVariable( + "FORCE", + help="Force target action (for supported targets)", + default=False, + ), + # These 2 are inherited from SDK + # BoolVariable( + # "DEBUG", + # help="Enable debug build", + # default=True, + # ), + # BoolVariable( + # "COMPACT", + # help="Optimize for size", + # default=False, + # ), + PathVariable( + "OTHER_ELF", + help="Path to prebuilt ELF file to debug", + validator=PathVariable.PathAccept, + default="", + ), + ( + "OPENOCD_OPTS", + "Options to pass to OpenOCD", + "", + ), + ( + "BLACKMAGIC", + "Blackmagic probe location", + "auto", + ), + EnumVariable( + "SWD_TRANSPORT", + help="SWD interface adapter type", + default="auto", + allowed_values=[ + "auto", + "cmsis-dap", + "stlink", + "blackmagic_usb", + "blackmagic_wifi", + ], + ), + ( + "SWD_TRANSPORT_SERIAL", + "SWD interface adapter serial number", + "auto", + ), + ( + "APPID", + "Application id", + "", + ), + PathVariable( + "UFBT_APP_DIR", + help="Application dir to work with", + validator=PathVariable.PathIsDir, + default="", + ), + ( + "FLIP_PORT", + "CDC Port of Flipper to use, if multiple are connected", + "auto", + ), + BoolVariable( + "STRICT_FAP_IMPORT_CHECK", + help="Enable strict import check for .faps", + default=True, + ), +) + +Return("vars") diff --git a/scripts/ufbt/project_template/.clang-format b/scripts/ufbt/project_template/.clang-format new file mode 100644 index 00000000000..4b76f7fa43b --- /dev/null +++ b/scripts/ufbt/project_template/.clang-format @@ -0,0 +1,191 @@ +--- +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/scripts/ufbt/project_template/.editorconfig b/scripts/ufbt/project_template/.editorconfig new file mode 100644 index 00000000000..a31ef8e753a --- /dev/null +++ b/scripts/ufbt/project_template/.editorconfig @@ -0,0 +1,13 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +[*.{cpp,h,c,py,sh}] +indent_style = space +indent_size = 4 + +[{Makefile,*.mk}] +indent_size = tab diff --git a/scripts/ufbt/project_template/.gitignore b/scripts/ufbt/project_template/.gitignore new file mode 100644 index 00000000000..e2a15a10a84 --- /dev/null +++ b/scripts/ufbt/project_template/.gitignore @@ -0,0 +1,4 @@ +dist/* +.vscode +.clang-format +.editorconfig \ No newline at end of file diff --git a/scripts/ufbt/project_template/.vscode/c_cpp_properties.json b/scripts/ufbt/project_template/.vscode/c_cpp_properties.json new file mode 100644 index 00000000000..922a9091b60 --- /dev/null +++ b/scripts/ufbt/project_template/.vscode/c_cpp_properties.json @@ -0,0 +1,14 @@ +{ + "configurations": [ + { + "name": "main", + "compilerPath": "@UFBT_TOOLCHAIN_GCC@", + "intelliSenseMode": "gcc-arm", + "compileCommands": "${workspaceFolder}/.vscode/compile_commands.json", + "configurationProvider": "ms-vscode.cpptools", + "cStandard": "gnu17", + "cppStandard": "c++17" + }, + ], + "version": 4 +} \ No newline at end of file diff --git a/scripts/ufbt/project_template/.vscode/extensions.json b/scripts/ufbt/project_template/.vscode/extensions.json new file mode 100644 index 00000000000..ead935b08b4 --- /dev/null +++ b/scripts/ufbt/project_template/.vscode/extensions.json @@ -0,0 +1,19 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "ms-python.black-formatter", + "ms-vscode.cpptools", + "amiralizadeh9480.cpp-helper", + "marus25.cortex-debug", + "zxh404.vscode-proto3", + "augustocdias.tasks-shell-input", + "rioj7.command-variable" + ], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [ + "twxs.cmake", + "ms-vscode.cmake-tools" + ] +} \ No newline at end of file diff --git a/scripts/ufbt/project_template/.vscode/launch.json b/scripts/ufbt/project_template/.vscode/launch.json new file mode 100644 index 00000000000..3269bab5754 --- /dev/null +++ b/scripts/ufbt/project_template/.vscode/launch.json @@ -0,0 +1,103 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "inputs": [ + { + "id": "BLACKMAGIC", + "type": "command", + "command": "shellCommand.execute", + "args": { + "description": "Get Blackmagic device", + "useSingleResult": true, + "command": "ufbt -s get_blackmagic", + } + }, + ], + "configurations": [ + { + "name": "Attach FW (ST-Link)", + "cwd": "${workspaceFolder}", + "executable": "@UFBT_FIRMWARE_ELF@", + "request": "attach", + "type": "cortex-debug", + "servertype": "openocd", + "device": "stlink", + "svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + "configFiles": [ + "interface/stlink.cfg", + "@UFBT_DEBUG_DIR@/stm32wbx.cfg" + ], + "postAttachCommands": [ + "source @UFBT_DEBUG_DIR@/flipperversion.py", + "fw-version", + "source @UFBT_DEBUG_DIR@/flipperapps.py", + "fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@" + ], + // "showDevDebugOutput": "raw", + }, + { + "name": "Attach FW (DAP)", + "cwd": "${workspaceFolder}", + "executable": "@UFBT_FIRMWARE_ELF@", + "request": "attach", + "type": "cortex-debug", + "servertype": "openocd", + "device": "cmsis-dap", + "svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + "configFiles": [ + "interface/cmsis-dap.cfg", + "@UFBT_DEBUG_DIR@/stm32wbx.cfg" + ], + "postAttachCommands": [ + "source @UFBT_DEBUG_DIR@/flipperversion.py", + "fw-version", + "source @UFBT_DEBUG_DIR@/flipperapps.py", + "fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@" + ], + // "showDevDebugOutput": "raw", + }, + { + "name": "Attach FW (blackmagic)", + "cwd": "${workspaceFolder}", + "executable": "@UFBT_FIRMWARE_ELF@", + "request": "attach", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "${input:BLACKMAGIC}", + "svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + "postAttachCommands": [ + "monitor swdp_scan", + "attach 1", + "set confirm off", + "set mem inaccessible-by-default off", + "source @UFBT_DEBUG_DIR@/flipperversion.py", + "fw-version", + "source @UFBT_DEBUG_DIR@/flipperapps.py", + "fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@" + ] + // "showDevDebugOutput": "raw", + }, + { + "name": "Attach FW (JLink)", + "cwd": "${workspaceFolder}", + "executable": "@UFBT_FIRMWARE_ELF@", + "request": "attach", + "type": "cortex-debug", + "servertype": "jlink", + "interface": "swd", + "device": "STM32WB55RG", + "svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + "postAttachCommands": [ + "source @UFBT_DEBUG_DIR@/flipperversion.py", + "fw-version", + "source @UFBT_DEBUG_DIR@/flipperapps.py", + "fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@" + ] + // "showDevDebugOutput": "raw", + }, + ] +} \ No newline at end of file diff --git a/scripts/ufbt/project_template/.vscode/settings.json b/scripts/ufbt/project_template/.vscode/settings.json new file mode 100644 index 00000000000..33cd3f035e1 --- /dev/null +++ b/scripts/ufbt/project_template/.vscode/settings.json @@ -0,0 +1,20 @@ +{ + "cortex-debug.enableTelemetry": false, + "cortex-debug.variableUseNaturalFormat": false, + "cortex-debug.showRTOS": true, + "cortex-debug.armToolchainPath": "@UFBT_TOOLCHAIN_ARM_TOOLCHAIN_DIR@", + "cortex-debug.openocdPath": "@UFBT_TOOLCHAIN_OPENOCD@", + "cortex-debug.gdbPath": "@UFBT_TOOLCHAIN_GDB_PY@", + "editor.formatOnSave": true, + "files.associations": { + "*.scons": "python", + "SConscript": "python", + "SConstruct": "python", + "*.fam": "python" + }, + "cortex-debug.registerUseNaturalFormat": false, + "python.analysis.typeCheckingMode": "off", + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter" + } +} \ No newline at end of file diff --git a/scripts/ufbt/project_template/.vscode/tasks.json b/scripts/ufbt/project_template/.vscode/tasks.json new file mode 100644 index 00000000000..4b3f4bda563 --- /dev/null +++ b/scripts/ufbt/project_template/.vscode/tasks.json @@ -0,0 +1,66 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "options": { + "env": { + "PATH": "${workspaceFolder}@UFBT_VSCODE_PATH_SEP@${env:PATH}@UFBT_VSCODE_PATH_SEP@@UFBT_ROOT_DIR@" + } + }, + "tasks": [ + { + "label": "Launch App on Flipper", + "group": "build", + "type": "shell", + "command": "ufbt launch" + }, + { + "label": "Build", + "group": "build", + "type": "shell", + "command": "ufbt" + }, + { + "label": "Clean", + "group": "build", + "type": "shell", + "command": "ufbt -c" + }, + { + "label": "Flash FW (ST-Link)", + "group": "build", + "type": "shell", + "command": "ufbt FORCE=1 flash" + }, + { + "label": "Flash FW (blackmagic)", + "group": "build", + "type": "shell", + "command": "ufbt flash_blackmagic" + }, + { + "label": "Flash FW (JLink)", + "group": "build", + "type": "shell", + "command": "ufbt FORCE=1 jflash" + }, + { + "label": "Flash FW (USB, with resources)", + "group": "build", + "type": "shell", + "command": "ufbt FORCE=1 flash_usb" + }, + { + "label": "Update uFBT SDK", + "group": "build", + "type": "shell", + "command": "ufbt update" + }, + { + "label": "Update VSCode config for current SDK", + "group": "build", + "type": "shell", + "command": "ufbt vscode_dist" + } + ] +} \ No newline at end of file diff --git a/scripts/ufbt/project_template/app_template/${FBT_APPID}.c b/scripts/ufbt/project_template/app_template/${FBT_APPID}.c new file mode 100644 index 00000000000..9b8113cb502 --- /dev/null +++ b/scripts/ufbt/project_template/app_template/${FBT_APPID}.c @@ -0,0 +1,12 @@ +#include + +/* generated by fbt from .png files in images folder */ +#include <@FBT_APPID@_icons.h> + +int32_t @FBT_APPID@_app(void* p) { + UNUSED(p); + FURI_LOG_I("TEST", "Hello world"); + FURI_LOG_I("TEST", "I'm @FBT_APPID@!"); + + return 0; +} diff --git a/scripts/ufbt/project_template/app_template/${FBT_APPID}.png b/scripts/ufbt/project_template/app_template/${FBT_APPID}.png new file mode 100644 index 00000000000..59e6c185f60 Binary files /dev/null and b/scripts/ufbt/project_template/app_template/${FBT_APPID}.png differ diff --git a/scripts/ufbt/project_template/app_template/.github/workflows/build.yml b/scripts/ufbt/project_template/app_template/.github/workflows/build.yml new file mode 100644 index 00000000000..143847c4a2e --- /dev/null +++ b/scripts/ufbt/project_template/app_template/.github/workflows/build.yml @@ -0,0 +1,41 @@ +name: "FAP: Build for multiple SDK sources" +# This will build your app for dev and release channels on GitHub. +# It will also build your app every day to make sure it's up to date with the latest SDK changes. +# See https://github.com/marketplace/actions/build-flipper-application-package-fap for more information + +on: + push: + ## put your main branch name under "branches" + #branches: + # - master + pull_request: + schedule: + # do a build every day + - cron: "1 1 * * *" + +jobs: + ufbt-build: + runs-on: ubuntu-latest + strategy: + matrix: + include: + - name: dev channel + sdk-channel: dev + - name: release channel + sdk-channel: release + # You can add unofficial channels here. See ufbt action docs for more info. + name: 'ufbt: Build for ${{ matrix.name }}' + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Build with ufbt + uses: flipperdevices/flipperzero-ufbt-action@v0.1 + id: build-app + with: + sdk-channel: ${{ matrix.sdk-channel }} + - name: Upload app artifacts + uses: actions/upload-artifact@v3 + with: + # See ufbt action docs for other output variables + name: ${{ github.event.repository.name }}-${{ steps.build-app.outputs.suffix }} + path: ${{ steps.build-app.outputs.fap-artifacts }} diff --git a/scripts/ufbt/project_template/app_template/application.fam b/scripts/ufbt/project_template/app_template/application.fam new file mode 100644 index 00000000000..a2d23ef4660 --- /dev/null +++ b/scripts/ufbt/project_template/app_template/application.fam @@ -0,0 +1,17 @@ +# For details & more options, see documentation/AppManifests.md in firmware repo + +App( + appid="@FBT_APPID@", # Must be unique + name="App @FBT_APPID@", # Displayed in menus + apptype=FlipperAppType.EXTERNAL, + entry_point="@FBT_APPID@_app", + stack_size=2 * 1024, + fap_category="Examples", + # Optional values + # fap_version="0.1", + fap_icon="@FBT_APPID@.png", # 10x10 1-bit PNG + # fap_description="A simple app", + # fap_author="J. Doe", + # fap_weburl="https://github.com/user/@FBT_APPID@", + fap_icon_assets="images", # Image assets to compile for this application +) diff --git a/scripts/ufbt/site_init.py b/scripts/ufbt/site_init.py new file mode 100644 index 00000000000..8e38a36e96a --- /dev/null +++ b/scripts/ufbt/site_init.py @@ -0,0 +1,36 @@ +import atexit + +import SCons.Errors +from ansi.color import fg, fx +from SCons.Script import GetBuildFailures + + +def bf_to_str(bf): + """Convert an element of GetBuildFailures() to a string + in a useful way.""" + + if bf is None: # unknown targets product None in list + return "(unknown tgt)" + elif isinstance(bf, SCons.Errors.StopError): + return fg.yellow(str(bf)) + elif bf.node: + return fg.yellow(str(bf.node)) + ": " + bf.errstr + elif bf.filename: + return fg.yellow(bf.filename) + ": " + bf.errstr + return fg.yellow("unknown failure: ") + bf.errstr + + +def display_build_status(): + """Display the build status. Called by atexit. + Here you could do all kinds of complicated things.""" + bf = GetBuildFailures() + if bf: + # bf is normally a list of build failures; if an element is None, + # it's because of a target that scons doesn't know anything about. + failures_message = "\n".join([bf_to_str(x) for x in bf if x is not None]) + print() + print(fg.brightred(fx.bold("*" * 10 + " FBT ERRORS " + "*" * 10))) + print(failures_message) + + +atexit.register(display_build_status) diff --git a/scripts/ufbt/site_tools/ufbt_help.py b/scripts/ufbt/site_tools/ufbt_help.py new file mode 100644 index 00000000000..1df6a0591ae --- /dev/null +++ b/scripts/ufbt/site_tools/ufbt_help.py @@ -0,0 +1,58 @@ +targets_help = """Configuration variables: +""" + +tail_help = """ + +TASKS: + (* - not supported yet) + + launch: + Upload and start application over USB + vscode_dist: + Configure application in current directory for development in VSCode. + create: + Copy application template to current directory. Set APPID=myapp to create an app with id 'myapp'. + +Building: + faps: + Build all FAP apps + fap_{APPID}, launch APPSRC={APPID}: + Build FAP app with appid={APPID}; upload & start it over USB + +Flashing & debugging: + flash, *jflash: + Flash firmware to target using SWD probe. See also SWD_TRANSPORT, SWD_TRANSPORT_SERIAL + flash_usb, flash_usb_full: + Install firmware using self-update package + debug, debug_other, blackmagic: + Start GDB + devboard_flash: + Update WiFi dev board with the latest firmware + +Other: + cli: + Open a Flipper CLI session over USB + lint: + run linter for C code + format: + reformat C code + +How to create a new application: + 1. Create a new directory for your application and cd into it. + 2. Run `ufbt vscode_dist create APPID=myapp` + 3. In VSCode, open the folder and start editing. + 4. Run `ufbt launch` to build and upload your application. + +How to open a shell with toolchain environment and other build tools: + In your shell, type "source `ufbt -s env`". You can also use "." instead of "source". +""" + + +def generate(env, **kw): + vars = kw["vars"] + basic_help = vars.GenerateHelpText(env) + env.Help(targets_help + basic_help + tail_help) + + +def exists(env): + return True diff --git a/scripts/ufbt/site_tools/ufbt_state.py b/scripts/ufbt/site_tools/ufbt_state.py new file mode 100644 index 00000000000..0038b66a31f --- /dev/null +++ b/scripts/ufbt/site_tools/ufbt_state.py @@ -0,0 +1,108 @@ +import json +import os +import pathlib +import sys +from functools import reduce + +from SCons.Errors import StopError + + +def _load_sdk_data(sdk_root): + split_vars = { + "cc_args", + "cpp_args", + "linker_args", + "linker_libs", + } + subst_vars = split_vars | { + "sdk_symbols", + } + sdk_data = {} + with open(os.path.join(sdk_root, "sdk.opts")) as f: + sdk_json_data = json.load(f) + replacements = { + sdk_json_data["app_ep_subst"]: "${APP_ENTRY}", + sdk_json_data["sdk_path_subst"]: sdk_root.replace("\\", "/"), + sdk_json_data["map_file_subst"]: "${TARGET}", + } + + def do_value_substs(src_value): + if isinstance(src_value, str): + return reduce( + lambda acc, kv: acc.replace(*kv), replacements.items(), src_value + ) + elif isinstance(src_value, list): + return [do_value_substs(v) for v in src_value] + else: + return src_value + + for key, value in sdk_json_data.items(): + if key in split_vars: + value = value.split() + if key in subst_vars: + value = do_value_substs(value) + sdk_data[key] = value + + return sdk_data + + +def _load_state_file(state_dir_node, filename: str) -> dict: + state_path = os.path.join(state_dir_node.abspath, filename) + if not os.path.exists(state_path): + raise StopError(f"State file {state_path} not found") + + with open(state_path, "r") as f: + return json.load(f) + + +def generate(env, **kw): + sdk_current_sdk_dir_node = env["UFBT_CURRENT_SDK_DIR"] + + sdk_components_filename = kw.get("SDK_COMPONENTS", "components.json") + ufbt_state_filename = kw.get("UFBT_STATE", "ufbt_state.json") + + sdk_state = _load_state_file(sdk_current_sdk_dir_node, sdk_components_filename) + ufbt_state = _load_state_file(sdk_current_sdk_dir_node, ufbt_state_filename) + + if not (sdk_components := sdk_state.get("components", {})): + raise StopError("SDK state file doesn't contain components data") + + sdk_data = _load_sdk_data( + sdk_current_sdk_dir_node.Dir(sdk_components["sdk_headers.dir"]).abspath + ) + + if not sdk_state["meta"]["hw_target"].endswith(sdk_data["hardware"]): + raise StopError("SDK state file doesn't match hardware target") + + scripts_dir = sdk_current_sdk_dir_node.Dir(sdk_components["scripts.dir"]) + env.SetDefault( + # Paths + SDK_DEFINITION=env.File(sdk_data["sdk_symbols"]), + FBT_SCRIPT_DIR=scripts_dir, + FBT_DEBUG_DIR=scripts_dir.Dir("debug"), + LIBPATH=sdk_current_sdk_dir_node.Dir(sdk_components["lib.dir"]), + FW_ELF=sdk_current_sdk_dir_node.File(sdk_components["firmware.elf"]), + FW_BIN=sdk_current_sdk_dir_node.File(sdk_components["full.bin"]), + UPDATE_BUNDLE_DIR=sdk_current_sdk_dir_node.Dir(sdk_components["update.dir"]), + SVD_FILE="${FBT_DEBUG_DIR}/STM32WB55_CM4.svd", + # Build variables + ROOT_DIR=env.Dir("#"), + FIRMWARE_BUILD_CFG="firmware", + TARGET_HW=int(sdk_data["hardware"]), + CFLAGS_APP=sdk_data["cc_args"], + CXXFLAGS_APP=sdk_data["cpp_args"], + LINKFLAGS_APP=sdk_data["linker_args"], + LIBS=sdk_data["linker_libs"], + # ufbt state + # UFBT_STATE_DIR=ufbt_state_dir_node, + # UFBT_CURRENT_SDK_DIR=sdk_current_sdk_dir_node, + UFBT_STATE=ufbt_state, + UFBT_BOOTSTRAP_SCRIPT="${UFBT_SCRIPT_DIR}/bootstrap.py", + UFBT_SCRIPT_ROOT=scripts_dir.Dir("ufbt"), + ) + + sys.path.insert(0, env["FBT_SCRIPT_DIR"].abspath) + + +def exists(env): + return True diff --git a/scripts/update.py b/scripts/update.py index ee485f44d2a..c315ef8adef 100755 --- a/scripts/update.py +++ b/scripts/update.py @@ -1,16 +1,16 @@ #!/usr/bin/env python3 -from flipper.app import App -from flipper.utils.fff import FlipperFormatFile -from flipper.assets.coprobin import CoproBinary, get_stack_type -from flipper.assets.obdata import OptionBytesData, ObReferenceValues -from os.path import basename, join, exists +import math import os import shutil -import zlib import tarfile -import math +import zlib +from os.path import exists, join +from flipper.app import App +from flipper.assets.coprobin import CoproBinary, get_stack_type +from flipper.assets.obdata import ObReferenceValues, OptionBytesData +from flipper.utils.fff import FlipperFormatFile from slideshow import Main as SlideshowMain @@ -22,6 +22,7 @@ class Main(App): RESOURCE_TAR_MODE = "w:" RESOURCE_TAR_FORMAT = tarfile.USTAR_FORMAT RESOURCE_FILE_NAME = "resources.tar" + RESOURCE_ENTRY_NAME_MAX_LENGTH = 100 WHITELISTED_STACK_TYPES = set( map( @@ -72,13 +73,20 @@ def init(self): self.parser_generate.add_argument( "--I-understand-what-I-am-doing", dest="disclaimer", required=False ) + self.parser_generate.add_argument( + "--stackversion", dest="stack_version", required=False, default="" + ) self.parser_generate.set_defaults(func=self.generate) def generate(self): - stage_basename = basename(self.args.stage) - dfu_basename = basename(self.args.dfu) - radiobin_basename = basename(self.args.radiobin) + stage_basename = "updater.bin" # used to be basename(self.args.stage) + dfu_basename = ( + "firmware.dfu" if self.args.dfu else "" + ) # used to be basename(self.args.dfu) + radiobin_basename = ( + "radio.bin" if self.args.radiobin else "" + ) # used to be basename(self.args.radiobin) resources_basename = "" radio_version = 0 @@ -88,6 +96,13 @@ def generate(self): if not self.args.radiotype: raise ValueError("Missing --radiotype") radio_meta = CoproBinary(self.args.radiobin) + if self.args.stack_version: + actual_stack_version_str = f"{radio_meta.img_sig.version_major}.{radio_meta.img_sig.version_minor}.{radio_meta.img_sig.version_sub}" + if actual_stack_version_str != self.args.stack_version: + self.logger.error( + f"Stack version mismatch: expected {self.args.stack_version}, actual {actual_stack_version_str}" + ) + return 1 radio_version = self.copro_version_as_int(radio_meta, self.args.radiotype) if ( get_stack_type(self.args.radiotype) not in self.WHITELISTED_STACK_TYPES @@ -120,9 +135,10 @@ def generate(self): ) if self.args.resources: resources_basename = self.RESOURCE_FILE_NAME - self.package_resources( + if not self.package_resources( self.args.resources, join(self.args.directory, resources_basename) - ) + ): + return 3 if not self.layout_check(dfu_size, radio_addr): self.logger.warn("Memory layout looks suspicious") @@ -193,17 +209,37 @@ def layout_check(self, fw_size, radio_addr): 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" ) + def _tar_filter(self, tarinfo: tarfile.TarInfo): + if len(tarinfo.name) > self.RESOURCE_ENTRY_NAME_MAX_LENGTH: + self.logger.error( + f"Cannot package resource: name '{tarinfo.name}' too long" + ) + raise ValueError("Resource name too long") + tarinfo.gid = tarinfo.uid = 0 + tarinfo.mtime = 0 + tarinfo.uname = tarinfo.gname = "furippa" + return tarinfo + def package_resources(self, srcdir: str, dst_name: str): - with tarfile.open( - dst_name, self.RESOURCE_TAR_MODE, format=self.RESOURCE_TAR_FORMAT - ) as tarball: - tarball.add(srcdir, arcname="") + try: + with tarfile.open( + dst_name, self.RESOURCE_TAR_MODE, format=self.RESOURCE_TAR_FORMAT + ) as tarball: + tarball.add( + srcdir, + arcname="", + filter=self._tar_filter, + ) + return True + except ValueError as e: + self.logger.error(f"Cannot package resources: {e}") + return False @staticmethod def copro_version_as_int(coprometa, stacktype): @@ -229,7 +265,7 @@ def bytes2ffhex(value: bytes): @staticmethod def int2ffhex(value: int, n_hex_syms=8): if value: - n_hex_syms = math.ceil(math.ceil(math.log2(value)) / 8) * 2 + n_hex_syms = max(math.ceil(math.ceil(math.log2(value)) / 8) * 2, n_hex_syms) fmtstr = f"%0{n_hex_syms}X" hexstr = fmtstr % value return " ".join(list(Main.batch(hexstr, 2))[::-1]) @@ -244,9 +280,9 @@ def crc(fileName): @staticmethod def batch(iterable, n=1): - l = len(iterable) - for ndx in range(0, l, n): - yield iterable[ndx : min(ndx + n, l)] + iterable_len = len(iterable) + for ndx in range(0, iterable_len, n): + yield iterable[ndx : min(ndx + n, iterable_len)] if __name__ == "__main__": diff --git a/scripts/version.py b/scripts/version.py old mode 100644 new mode 100755 index 896b58a467e..98b1b7e8555 --- a/scripts/version.py +++ b/scripts/version.py @@ -1,19 +1,24 @@ #!/usb/bin/env python3 -from flipper.app import App - -import subprocess -import os import json +import os +import subprocess from datetime import date, datetime +from flipper.app import App + class GitVersion: + REVISION_SUFFIX_LENGTH = 8 + def __init__(self, source_dir): self.source_dir = source_dir def get_version_info(self): - commit = self._exec_git("rev-parse --short HEAD") or "unknown" + commit = ( + self._exec_git(f"rev-parse --short={self.REVISION_SUFFIX_LENGTH} HEAD") + or "unknown" + ) dirty = False try: @@ -30,21 +35,44 @@ def get_version_info(self): or "unknown" ) - branch_num = self._exec_git("rev-list --count HEAD") or "n/a" - try: version = self._exec_git("describe --tags --abbrev=0 --exact-match") except subprocess.CalledProcessError: version = "unknown" + if "SOURCE_DATE_EPOCH" in os.environ: + commit_date = datetime.utcfromtimestamp( + int(os.environ["SOURCE_DATE_EPOCH"]) + ) + else: + commit_date = datetime.strptime( + self._exec_git("log -1 --format=%cd --date=default").strip(), + "%a %b %d %H:%M:%S %Y %z", + ) + return { "GIT_COMMIT": commit, "GIT_BRANCH": branch, - "GIT_BRANCH_NUM": branch_num, "VERSION": version, "BUILD_DIRTY": dirty and 1 or 0, + "GIT_ORIGIN": ",".join(self._get_git_origins()), + "GIT_COMMIT_DATE": commit_date, } + def _get_git_origins(self): + try: + remotes = self._exec_git("remote -v") + except subprocess.CalledProcessError: + return set() + origins = set() + for line in remotes.split("\n"): + if not line: + continue + _, destination = line.split("\t") + url, _ = destination.split(" ") + origins.add(url) + return origins + def _exec_git(self, args): cmd = ["git"] cmd.extend(args.split(" ")) @@ -72,24 +100,35 @@ def init(self): help="hardware target", required=True, ) + self.parser_generate.add_argument( + "--fw-origin", + dest="firmware_origin", + type=str, + help="firmware origin", + required=True, + ) self.parser_generate.add_argument("--dir", dest="sourcedir", required=True) self.parser_generate.set_defaults(func=self.generate) def generate(self): current_info = GitVersion(self.args.sourcedir).get_version_info() - if "SOURCE_DATE_EPOCH" in os.environ: - build_date = datetime.utcfromtimestamp(int(os.environ["SOURCE_DATE_EPOCH"])) - else: - build_date = date.today() + build_date = ( + date.today() + if current_info["BUILD_DIRTY"] + else current_info["GIT_COMMIT_DATE"] + ) current_info.update( { "BUILD_DATE": build_date.strftime("%d-%m-%Y"), "TARGET": self.args.target, + "FIRMWARE_ORIGIN": self.args.firmware_origin, } ) + del current_info["GIT_COMMIT_DATE"] + version_values = [] for key in current_info: val = current_info[key] diff --git a/scripts/wifi_board.py b/scripts/wifi_board.py new file mode 100755 index 00000000000..3f89ebdc656 --- /dev/null +++ b/scripts/wifi_board.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python3 + +from flipper.app import App +from serial.tools.list_ports_common import ListPortInfo + +import logging +import os +import tempfile +import subprocess +import serial.tools.list_ports as list_ports +import json +import requests +import tarfile + + +class UpdateDownloader: + UPDATE_SERVER = "https://update.flipperzero.one" + UPDATE_PROJECT = "/blackmagic-firmware" + UPDATE_INDEX = UPDATE_SERVER + UPDATE_PROJECT + "/directory.json" + UPDATE_TYPE = "full_tgz" + + CHANNEL_ID_ALIAS = { + "dev": "development", + "rc": "release-candidate", + "r": "release", + "rel": "release", + } + + def __init__(self): + self.logger = logging.getLogger() + + def download(self, channel_id: str, dir: str) -> bool: + # Aliases + if channel_id in self.CHANNEL_ID_ALIAS: + channel_id = self.CHANNEL_ID_ALIAS[channel_id] + + # Make directory + if not os.path.exists(dir): + self.logger.info(f"Creating directory {dir}") + os.makedirs(dir) + + # Download json index + self.logger.info(f"Downloading {self.UPDATE_INDEX}") + response = requests.get(self.UPDATE_INDEX) + if response.status_code != 200: + self.logger.error(f"Failed to download {self.UPDATE_INDEX}") + return False + + # Parse json index + try: + index = json.loads(response.content) + except Exception as e: + self.logger.error(f"Failed to parse json index: {e}") + return False + + # Find channel + channel = None + for channel_candidate in index["channels"]: + if channel_candidate["id"] == channel_id: + channel = channel_candidate + break + + # Check if channel found + if channel is None: + self.logger.error( + f"Channel '{channel_id}' not found. Valid channels: {', '.join([c['id'] for c in index['channels']])}" + ) + return False + + self.logger.info(f"Using channel '{channel_id}'") + + # Get latest version + try: + version = channel["versions"][0] + except Exception as e: + self.logger.error(f"Failed to get version: {e}") + return False + + self.logger.info(f"Using version '{version['version']}'") + + # Get changelog + changelog = None + try: + changelog = version["changelog"] + except Exception as e: + self.logger.error(f"Failed to get changelog: {e}") + + # print changelog + if changelog is not None: + self.logger.info(f"Changelog:") + for line in changelog.split("\n"): + if line.strip() == "": + continue + self.logger.info(f" {line}") + + # Find file + file_url = None + for file_candidate in version["files"]: + if file_candidate["type"] == self.UPDATE_TYPE: + file_url = file_candidate["url"] + break + + if file_url is None: + self.logger.error(f"File not found") + return False + + # Make file path + file_name = file_url.split("/")[-1] + file_path = os.path.join(dir, file_name) + + # Download file + self.logger.info(f"Downloading {file_url} to {file_path}") + with open(file_path, "wb") as f: + response = requests.get(file_url) + f.write(response.content) + + # Unzip tgz + self.logger.info(f"Unzipping {file_path}") + with tarfile.open(file_path, "r") as tar: + tar.extractall(dir) + + return True + + +class Main(App): + def init(self): + self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") + self.parser.add_argument( + "-c", "--channel", help="Channel name", default="release" + ) + self.parser.set_defaults(func=self.update) + + # logging + self.logger = logging.getLogger() + + def find_wifi_board(self) -> bool: + # idk why, but python thinks that list_ports.grep returns tuple[str, str, str] + blackmagics: list[ListPortInfo] = list(list_ports.grep("blackmagic")) # type: ignore + daps: list[ListPortInfo] = list(list_ports.grep("CMSIS-DAP")) # type: ignore + + return len(blackmagics) > 0 or len(daps) > 0 + + def find_wifi_board_bootloader(self): + # idk why, but python thinks that list_ports.grep returns tuple[str, str, str] + ports: list[ListPortInfo] = list(list_ports.grep("ESP32-S2")) # type: ignore + + if len(ports) == 0: + # Blackmagic probe serial port not found, will be handled later + pass + elif len(ports) > 1: + raise Exception("More than one WiFi board found") + else: + port = ports[0] + if os.name == "nt": + port.device = f"\\\\.\\{port.device}" + return port.device + + def update(self): + try: + port = self.find_wifi_board_bootloader() + except Exception as e: + self.logger.error(f"{e}") + return 1 + + if self.args.port != "auto": + port = self.args.port + + available_ports = [p[0] for p in list(list_ports.comports())] + if port not in available_ports: + self.logger.error(f"Port {port} not found") + return 1 + + if port is None: + if self.find_wifi_board(): + self.logger.error("WiFi board found, but not in bootloader mode.") + self.logger.info("Please hold down BOOT button and press RESET button") + else: + self.logger.error("WiFi board not found") + self.logger.info( + "Please connect WiFi board to your computer, hold down BOOT button and press RESET button" + ) + return 1 + + # get temporary dir + with tempfile.TemporaryDirectory() as temp_dir: + downloader = UpdateDownloader() + + # download latest channel update + try: + if not downloader.download(self.args.channel, temp_dir): + self.logger.error(f"Cannot download update") + return 1 + except Exception as e: + self.logger.error(f"Cannot download update: {e}") + return 1 + + with open(os.path.join(temp_dir, "flash.command"), "r") as f: + flash_command = f.read() + + flash_command = flash_command.replace("\n", "").replace("\r", "") + flash_command = flash_command.replace("(PORT)", port) + + # We can't reset the board after flashing via usb + flash_command = flash_command.replace( + "--after hard_reset", "--after no_reset_stub" + ) + + args = flash_command.split(" ")[0:] + args = list(filter(None, args)) + + esptool_params = [] + esptool_params.extend(args) + + self.logger.info(f'Running command: "{" ".join(args)}" in "{temp_dir}"') + + process = subprocess.Popen( + esptool_params, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + cwd=temp_dir, + bufsize=1, + universal_newlines=True, + ) + + while process.poll() is None: + if process.stdout is not None: + for line in process.stdout: + self.logger.debug(f"{line.strip()}") + + if process.returncode != 0: + self.logger.error(f"Failed to flash WiFi board") + else: + self.logger.info("WiFi board flashed successfully") + self.logger.info("Press RESET button on WiFi board to start it") + + return process.returncode + + +if __name__ == "__main__": + Main()() diff --git a/site_scons/cc.scons b/site_scons/cc.scons index e71db2ba29d..55ab72ba654 100644 --- a/site_scons/cc.scons +++ b/site_scons/cc.scons @@ -11,6 +11,7 @@ ENV.AppendUnique( "-fno-use-cxa-atexit", "-fno-exceptions", "-fno-threadsafe-statics", + "-ftemplate-depth=4096", ], CCFLAGS=[ "-mcpu=cortex-m4", @@ -29,13 +30,13 @@ ENV.AppendUnique( "-ffunction-sections", "-fsingle-precision-constant", "-fno-math-errno", - "-fstack-usage", + # Generates .su files with stack usage information + # "-fstack-usage", "-g", - # "-Wno-stringop-overread", - # "-Wno-stringop-overflow", ], CPPDEFINES=[ "_GNU_SOURCE", + *GetOption("extra_defines"), ], LINKFLAGS=[ "-mcpu=cortex-m4", diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index 4c96268b693..2d2abd5c676 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -15,7 +15,7 @@ AddOption( nargs=1, action="store", default="fbt_options.py", - help="Enviroment option file", + help="Environment option file", ) AddOption( @@ -23,7 +23,15 @@ AddOption( action="store", dest="extra_int_apps", default="", - help="List of applications to add to firmare's built-ins. Also see FIRMWARE_APP_SET and FIRMWARE_APPS", + help="List of applications to add to firmware's built-ins. Also see FIRMWARE_APP_SET and FIRMWARE_APPS", +) + +AddOption( + "--extra-define", + action="append", + dest="extra_defines", + default=[], + help="Extra global define that will be passed to C/C++ compiler, can be specified multiple times", ) AddOption( @@ -34,6 +42,14 @@ AddOption( help="List of applications to forcefully build as standalone .elf", ) +AddOption( + "--proxy-env", + action="store", + dest="proxy_env", + default="", + help="Comma-separated list of additional environment variables to pass to child SCons processes", +) + # Construction environment variables @@ -55,6 +71,11 @@ vars.AddVariables( help="Enable debug build", default=True, ), + BoolVariable( + "LIB_DEBUG", + help="Enable debug build for libraries", + default=False, + ), BoolVariable( "COMPACT", help="Optimize for size", @@ -66,48 +87,39 @@ vars.AddVariables( default="7", allowed_values=[ "7", + "18", ], ), -) - -vars.Add( - "DIST_SUFFIX", - help="Suffix for binaries in build output for dist targets", - default="local", -) - -vars.Add( - "UPDATE_VERSION_STRING", - help="Version string for updater package", - default="${DIST_SUFFIX}", -) - - -vars.Add( - "COPRO_CUBE_VERSION", - help="Cube version", - default="", -) - -vars.Add( - "COPRO_STACK_ADDR", - help="Core2 Firmware address", - default="0", -) - -vars.Add( - "COPRO_STACK_BIN", - help="Core2 Firmware file name", - default="", -) - -vars.Add( - "COPRO_DISCLAIMER", - help="Value to pass to bundling script to confirm dangerous operations", - default="", -) - -vars.AddVariables( + ( + "DIST_SUFFIX", + "Suffix for binaries in build output for dist targets", + "local", + ), + ( + "UPDATE_VERSION_STRING", + "Version string for updater package", + "${DIST_SUFFIX}", + ), + ( + "COPRO_CUBE_VERSION", + "Cube version", + "", + ), + ( + "COPRO_STACK_ADDR", + "Core2 Firmware address", + "0", + ), + ( + "COPRO_STACK_BIN", + "Core2 Firmware file name", + "", + ), + ( + "COPRO_DISCLAIMER", + "Value to pass to bundling script to confirm dangerous operations", + "", + ), PathVariable( "COPRO_OB_DATA", help="Path to OB reference data", @@ -139,7 +151,7 @@ vars.AddVariables( PathVariable( "SVD_FILE", help="Path to SVD file", - validator=PathVariable.PathIsFile, + validator=PathVariable.PathAccept, default="", ), PathVariable( @@ -148,67 +160,120 @@ vars.AddVariables( validator=PathVariable.PathAccept, default="", ), -) - -vars.Add( - "FBT_TOOLCHAIN_VERSIONS", - help="Whitelisted toolchain versions (leave empty for no check)", - default=tuple(), -) - -vars.Add( - "OPENOCD_OPTS", - help="Options to pass to OpenOCD", - default="", -) - -vars.Add( - "BLACKMAGIC", - help="Blackmagic probe location", - default="auto", -) - -vars.Add( - "UPDATE_SPLASH", - help="Directory name with slideshow frames to render after installing update package", - default="update_default", -) - -vars.Add( - "LOADER_AUTOSTART", - help="Application name to automatically run on Flipper boot", - default="", -) - - -vars.Add( - "FIRMWARE_APPS", - help="Map of (configuration_name->application_list)", - default={ - "default": ( - "crypto_start", - # Svc - "basic_services", - # Apps - "basic_apps", - "updater_app", - "archive", - # Settings - "passport", - "system_settings", - "about", - # Plugins - "basic_plugins", - # Debug - "debug_apps", - ) - }, -) - -vars.Add( - "FIRMWARE_APP_SET", - help="Application set to use from FIRMWARE_APPS", - default="default", + ( + "FBT_TOOLCHAIN_VERSIONS", + "Whitelisted toolchain versions (leave empty for no check)", + tuple(), + ), + ( + "OPENOCD_OPTS", + "Options to pass to OpenOCD", + "", + ), + ( + "BLACKMAGIC", + "Blackmagic probe location", + "auto", + ), + EnumVariable( + "SWD_TRANSPORT", + help="SWD interface adapter type", + default="auto", + allowed_values=[ + "auto", + "cmsis-dap", + "stlink", + "blackmagic_usb", + "blackmagic_wifi", + ], + ), + ( + "SWD_TRANSPORT_SERIAL", + "SWD interface adapter serial number", + "auto", + ), + ( + "UPDATE_SPLASH", + "Directory name with slideshow frames to render after installing update package", + "update_default", + ), + ( + "LOADER_AUTOSTART", + "Application name to automatically run on Flipper boot", + "", + ), + ( + "FIRMWARE_APPS", + "Map of (configuration_name->application_list)", + { + "default": ( + # Svc + "basic_services", + # Apps + "main_apps", + "system_apps", + # Settings + "settings_apps", + ), + }, + ), + ( + "FIRMWARE_APP_SET", + "Application set to use from FIRMWARE_APPS", + "default", + ), + ( + "APPSRC", + "Application source directory for app to build & upload", + "", + ), + # List of tuples (directory, add_to_global_include_path) + ( + "APPDIRS", + "Directories to search for firmware components & external apps", + [ + ("applications", False), + ("applications/services", True), + ("applications/main", True), + ("applications/settings", False), + ("applications/system", False), + ("applications/debug", False), + ("applications/examples", False), + ("applications/drivers", False), + ("applications_user", False), + ], + ), + BoolVariable( + "PVSNOBROWSER", + help="Don't open browser after generating error repots", + default=False, + ), + ( + "FIRMWARE_ORIGIN", + "Firmware origin. 'Official' if follows upstream's API structure, otherwise fork name. " + " This will also create a C define FW_ORIGIN_ so that " + " app can check what version it is being built for.", + "Official", + ), + ( + "FLIP_PORT", + "Full port name of Flipper to use, if multiple Flippers are connected", + "auto", + ), + EnumVariable( + "LANG_SERVER", + help="Language server type for vscode_dist.", + default="cpptools", + allowed_values=[ + "cpptools", + "clangd", + ], + ), + BoolVariable( + "STRICT_FAP_IMPORT_CHECK", + help="Enable strict import check for .faps", + default=True, + ), ) Return("vars") diff --git a/site_scons/environ.scons b/site_scons/environ.scons index 99d4cc0b562..74762cb15bb 100644 --- a/site_scons/environ.scons +++ b/site_scons/environ.scons @@ -1,6 +1,10 @@ -import SCons from SCons.Platform import TempFileMunge -from fbt import util +from fbt.util import ( + tempfile_arg_esc_func, + single_quote, + wrap_tempfile, + resolve_real_dir_node, +) import os import multiprocessing @@ -12,13 +16,33 @@ forward_os_env = { "PATH": os.environ["PATH"], } # Proxying CI environment to child processes & scripts -for env_value_name in ("WORKFLOW_BRANCH_OR_TAG", "DIST_SUFFIX", "HOME", "APPDATA"): +variables_to_forward = [ + # CI/CD variables + "WORKFLOW_BRANCH_OR_TAG", + "DIST_SUFFIX", + # Python & other tools + "HOME", + "APPDATA", + "PYTHONHOME", + "PYTHONNOUSERSITE", + "TMP", + "TEMP", + # ccache + "CCACHE_DISABLE", + # Colors for tools + "TERM", +] +if proxy_env := GetOption("proxy_env"): + variables_to_forward.extend(proxy_env.split(",")) + +for env_value_name in variables_to_forward: if environ_value := os.environ.get(env_value_name, None): forward_os_env[env_value_name] = environ_value coreenv = VAR_ENV.Clone( tools=[ + "fbt_tweaks", ( "crosscc", { @@ -35,6 +59,12 @@ coreenv = VAR_ENV.Clone( MAXLINELENGTH=2048, PROGSUFFIX=".elf", ENV=forward_os_env, + SINGLEQUOTEFUNC=single_quote, + ABSPATHGETTERFUNC=resolve_real_dir_node, + # Setting up temp file parameters - to overcome command line length limits + TEMPFILEARGESCFUNC=tempfile_arg_esc_func, + ROOT_DIR=Dir("#"), + FBT_SCRIPT_DIR=Dir("#/scripts"), ) # If DIST_SUFFIX is set in environment, is has precedence (set by CI) @@ -43,41 +73,19 @@ if os_suffix := os.environ.get("DIST_SUFFIX", None): DIST_SUFFIX=os_suffix, ) -# print(coreenv.Dump()) -if not coreenv["VERBOSE"]: - coreenv.SetDefault( - CCCOMSTR="\tCC\t${SOURCE}", - CXXCOMSTR="\tCPP\t${SOURCE}", - ASCOMSTR="\tASM\t${SOURCE}", - ARCOMSTR="\tAR\t${TARGET}", - RANLIBCOMSTR="\tRANLIB\t${TARGET}", - LINKCOMSTR="\tLINK\t${TARGET}", - INSTALLSTR="\tINSTALL\t${TARGET}", - APPSCOMSTR="\tAPPS\t${TARGET}", - VERSIONCOMSTR="\tVERSION\t${TARGET}", - STRIPCOMSTR="\tSTRIP\t${TARGET}", - OBJDUMPCOMSTR="\tOBJDUMP\t${TARGET}", - # GDBCOMSTR="\tGDB\t${SOURCE}", - # GDBPYCOMSTR="\tGDB-PY\t${SOURCE}", - ) - # Default value for commandline options SetOption("num_jobs", multiprocessing.cpu_count()) +## NB - disabled both caches since they seem to do more harm then good in our case # Avoiding re-scan of all sources on every startup -SetOption("implicit_cache", True) -SetOption("implicit_deps_unchanged", True) +# SetOption("implicit_cache", True) +# SetOption("implicit_deps_unchanged", True) # More aggressive caching SetOption("max_drift", 1) # Random task queue - to discover isses with build logic faster # SetOption("random", 1) - -# Setting up temp file parameters - to overcome command line length limits -coreenv["TEMPFILEARGESCFUNC"] = util.tempfile_arg_esc_func -util.wrap_tempfile(coreenv, "LINKCOM") -util.wrap_tempfile(coreenv, "ARCOM") - -coreenv["SINGLEQUOTEFUNC"] = util.single_quote +wrap_tempfile(coreenv, "LINKCOM") +wrap_tempfile(coreenv, "ARCOM") Return("coreenv") diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons new file mode 100644 index 00000000000..769b3eb1540 --- /dev/null +++ b/site_scons/extapps.scons @@ -0,0 +1,135 @@ +from dataclasses import dataclass, field +from fbt.appmanifest import FlipperAppType + +from SCons.Node import NodeList +from SCons.Warnings import warn, WarningOnByDefault + + +Import("ENV") + + +appenv = ENV["APPENV"] = ENV.Clone( + tools=[ + "fbt_extapps", + "fbt_assets", + "fbt_sdk", + ], +) + +appenv.Replace( + LINKER_SCRIPT_PATH=appenv["APP_LINKER_SCRIPT_PATH"], +) + +appenv.AppendUnique( + CCFLAGS=[ + "-mword-relocations", + "-mlong-calls", + "-fno-common", + "-nostdlib", + ], + LINKFLAGS=[ + "-Ur", + "-Wl,-Ur", + "-nostartfiles", + "-mlong-calls", + "-fno-common", + "-nostdlib", + "-Wl,--gc-sections", + "-Wl,--no-export-dynamic", + "-fvisibility=hidden", + "-Wl,-e${APP_ENTRY}", + "-Xlinker", + "-Map=${TARGET}.map", + "-specs=nano.specs", + "-specs=nosys.specs", + ], + LIBS=[ + "m", + "gcc", + "stdc++", + "supc++", + ], +) + + +@dataclass +class FlipperExtAppBuildArtifacts: + application_map: dict = field(default_factory=dict) + sdk_tree: NodeList = field(default_factory=NodeList) + + +for app in appenv["APPBUILD"].get_ext_apps(): + appenv.BuildAppElf(app) + +extapps = FlipperExtAppBuildArtifacts() +extapps.application_map = appenv["EXT_APPS"] + + +if appenv["FORCE"]: + appenv.AlwaysBuild( + list(app_artifact.compact for app_artifact in extapps.application_map.values()) + ) + + +Alias( + "faps", + list(app_artifact.validator for app_artifact in extapps.application_map.values()), +) + + +if appsrc := appenv.subst("$APPSRC"): + launch_target = appenv.AddAppLaunchTarget(appsrc, "launch") + Alias("launch_app", launch_target) + appenv.PhonyTarget( + "launch_app", + Action( + lambda **kw: warn( + WarningOnByDefault, + "The 'launch_app' target is deprecated. Use 'launch' instead.", + ), + None, + ), + ) + + appenv.AddAppBuildTarget(appsrc, "build") + +# SDK management + +amalgamated_api = "${BUILD_DIR}/sdk_origin" +sdk_source = appenv.ApiAmalgamator( + amalgamated_api, + # Deps on root SDK headers and generated files + (appenv["SDK_HEADERS"], appenv["FW_ASSETS_HEADERS"]), +) +# Extra deps on headers included in deeper levels +# Available on second and subsequent builds +Depends(sdk_source, appenv.ProcessSdkDepends(f"{amalgamated_api}.d")) + +appenv["SDK_DIR"] = appenv.Dir("${BUILD_DIR}/sdk_headers") +sdk_header_tree = appenv.SDKHeaderTreeExtractor(appenv["SDK_DIR"], amalgamated_api) +Depends(sdk_header_tree, appenv["SDK_DEFINITION"]) +# AlwaysBuild(sdk_tree) +Alias("sdk_tree", sdk_header_tree) +extapps.sdk_tree = sdk_header_tree + +api_check = appenv.ApiTableValidator(appenv["SDK_DEFINITION"], amalgamated_api) +Precious(api_check) +NoClean(api_check) +AlwaysBuild(api_check) +Alias("api_check", api_check) + +firmware_apitable = appenv.ApiSymbolTable( + "${BUILD_DIR}/assets/compiled/firmware_api_table.h", appenv["SDK_DEFINITION"] +) +Alias("api_table", firmware_apitable) +ENV.Replace( + FW_API_TABLE=firmware_apitable, + _APP_ICONS=appenv["_APP_ICONS"], +) + + +if appenv["FORCE"]: + appenv.AlwaysBuild(sdk_source, sdk_header_tree, api_check, firmware_apitable) + + +Return("extapps") diff --git a/site_scons/fbt/appmanifest.py b/site_scons/fbt/appmanifest.py deleted file mode 100644 index aacf248eccd..00000000000 --- a/site_scons/fbt/appmanifest.py +++ /dev/null @@ -1,253 +0,0 @@ -from dataclasses import dataclass, field -from typing import List, Optional -from enum import Enum -import os - - -class FlipperManifestException(Exception): - pass - - -class FlipperAppType(Enum): - SERVICE = "Service" - SYSTEM = "System" - APP = "App" - PLUGIN = "Plugin" - DEBUG = "Debug" - ARCHIVE = "Archive" - SETTINGS = "Settings" - STARTUP = "StartupHook" - EXTERNAL = "External" - METAPACKAGE = "Package" - - -@dataclass -class FlipperApplication: - appid: str - apptype: FlipperAppType - name: Optional[str] = None - entry_point: Optional[str] = None - flags: List[str] = field(default_factory=lambda: ["Default"]) - cdefines: List[str] = field(default_factory=list) - requires: List[str] = field(default_factory=list) - conflicts: List[str] = field(default_factory=list) - provides: List[str] = field(default_factory=list) - stack_size: int = 2048 - icon: Optional[str] = None - order: int = 0 - _appdir: Optional[str] = None - - -class AppManager: - def __init__(self): - self.known_apps = {} - - def get(self, appname: str): - try: - return self.known_apps[appname] - except KeyError as _: - raise FlipperManifestException( - f"Missing application manifest for '{appname}'" - ) - - def load_manifest(self, app_manifest_path: str, app_dir_name: str): - if not os.path.exists(app_manifest_path): - raise FlipperManifestException( - f"App manifest not found at path {app_manifest_path}" - ) - # print("Loading", app_manifest_path) - - app_manifests = [] - - def App(*args, **kw): - nonlocal app_manifests - app_manifests.append(FlipperApplication(*args, **kw, _appdir=app_dir_name)) - - try: - with open(app_manifest_path, "rt") as manifest_file: - exec(manifest_file.read()) - except Exception as e: - raise FlipperManifestException( - f"Failed parsing manifest '{app_manifest_path}' : {e}" - ) - - if len(app_manifests) == 0: - raise FlipperManifestException( - f"App manifest '{app_manifest_path}' is malformed" - ) - - # print("Built", app_manifests) - for app in app_manifests: - self._add_known_app(app) - - def _add_known_app(self, app: FlipperApplication): - if self.known_apps.get(app.appid, None): - raise FlipperManifestException(f"Duplicate app declaration: {app.appid}") - self.known_apps[app.appid] = app - - def filter_apps(self, applist: List[str]): - return AppBuildset(self, applist) - - -class AppBuilderException(Exception): - pass - - -class AppBuildset: - BUILTIN_APP_TYPES = ( - FlipperAppType.SERVICE, - FlipperAppType.SYSTEM, - FlipperAppType.APP, - FlipperAppType.PLUGIN, - FlipperAppType.DEBUG, - FlipperAppType.ARCHIVE, - FlipperAppType.SETTINGS, - FlipperAppType.STARTUP, - ) - - def __init__(self, appmgr: AppManager, appnames: List[str]): - self.appmgr = appmgr - self.appnames = set(appnames) - self._orig_appnames = appnames - self._process_deps() - self._check_conflicts() - self._check_unsatisfied() # unneeded? - self.apps = sorted( - list(map(self.appmgr.get, self.appnames)), - key=lambda app: app.appid, - ) - - def _is_missing_dep(self, dep_name: str): - return dep_name not in self.appnames - - def _process_deps(self): - while True: - provided = [] - for app in self.appnames: - # print(app) - provided.extend( - filter( - self._is_missing_dep, - self.appmgr.get(app).provides + self.appmgr.get(app).requires, - ) - ) - # print("provides round", provided) - if len(provided) == 0: - break - self.appnames.update(provided) - - def _check_conflicts(self): - conflicts = [] - for app in self.appnames: - # print(app) - if conflict_app_name := list( - filter( - lambda dep_name: dep_name in self.appnames, - self.appmgr.get(app).conflicts, - ) - ): - conflicts.append((app, conflict_app_name)) - - if len(conflicts): - raise AppBuilderException( - f"App conflicts for {', '.join(f'{conflict_dep[0]}: {conflict_dep[1]}' for conflict_dep in conflicts)}" - ) - - def _check_unsatisfied(self): - unsatisfied = [] - for app in self.appnames: - if missing_dep := list( - filter(self._is_missing_dep, self.appmgr.get(app).requires) - ): - unsatisfied.append((app, missing_dep)) - - if len(unsatisfied): - raise AppBuilderException( - f"Unsatisfied dependencies for {', '.join(f'{missing_dep[0]}: {missing_dep[1]}' for missing_dep in unsatisfied)}" - ) - - def get_apps_cdefs(self): - cdefs = set() - for app in self.apps: - cdefs.update(app.cdefines) - return sorted(list(cdefs)) - - def get_apps_of_type(self, apptype: FlipperAppType): - return sorted( - filter(lambda app: app.apptype == apptype, self.apps), - key=lambda app: app.order, - ) - - def get_builtin_app_folders(self): - return sorted( - set( - app._appdir - for app in filter( - lambda app: app.apptype in self.BUILTIN_APP_TYPES, self.apps - ) - ) - ) - - -class ApplicationsCGenerator: - APP_TYPE_MAP = { - FlipperAppType.SERVICE: ("FlipperApplication", "FLIPPER_SERVICES"), - FlipperAppType.SYSTEM: ("FlipperApplication", "FLIPPER_SYSTEM_APPS"), - FlipperAppType.APP: ("FlipperApplication", "FLIPPER_APPS"), - FlipperAppType.PLUGIN: ("FlipperApplication", "FLIPPER_PLUGINS"), - FlipperAppType.DEBUG: ("FlipperApplication", "FLIPPER_DEBUG_APPS"), - FlipperAppType.SETTINGS: ("FlipperApplication", "FLIPPER_SETTINGS_APPS"), - FlipperAppType.STARTUP: ("FlipperOnStartHook", "FLIPPER_ON_SYSTEM_START"), - } - - def __init__(self, buildset: AppBuildset, autorun_app: str = ""): - self.buildset = buildset - self.autorun = autorun_app - - def get_app_ep_forward(self, app: FlipperApplication): - if app.apptype == FlipperAppType.STARTUP: - return f"extern void {app.entry_point}();" - return f"extern int32_t {app.entry_point}(void* p);" - - def get_app_descr(self, app: FlipperApplication): - if app.apptype == FlipperAppType.STARTUP: - return app.entry_point - return f""" - {{.app = {app.entry_point}, - .name = "{app.name}", - .stack_size = {app.stack_size}, - .icon = {f"&{app.icon}" if app.icon else "NULL"}, - .flags = {'|'.join(f"FlipperApplicationFlag{flag}" for flag in app.flags)} }}""" - - def generate(self): - contents = [ - '#include "applications.h"', - "#include ", - f'const char* FLIPPER_AUTORUN_APP_NAME = "{self.autorun}";', - ] - for apptype in self.APP_TYPE_MAP: - contents.extend( - map(self.get_app_ep_forward, self.buildset.get_apps_of_type(apptype)) - ) - entry_type, entry_block = self.APP_TYPE_MAP[apptype] - contents.append(f"const {entry_type} {entry_block}[] = {{") - contents.append( - ",\n".join( - map(self.get_app_descr, self.buildset.get_apps_of_type(apptype)) - ) - ) - contents.append("};") - contents.append( - f"const size_t {entry_block}_COUNT = COUNT_OF({entry_block});" - ) - - archive_app = self.buildset.get_apps_of_type(FlipperAppType.ARCHIVE) - if archive_app: - contents.extend( - [ - self.get_app_ep_forward(archive_app[0]), - f"const FlipperApplication FLIPPER_ARCHIVE = {self.get_app_descr(archive_app[0])};", - ] - ) - - return "\n".join(contents) diff --git a/site_scons/fbt/util.py b/site_scons/fbt/util.py deleted file mode 100644 index 8d8af518328..00000000000 --- a/site_scons/fbt/util.py +++ /dev/null @@ -1,65 +0,0 @@ -import SCons -from SCons.Subst import quote_spaces -from SCons.Errors import StopError - -import re -import os -import random -import string - -WINPATHSEP_RE = re.compile(r"\\([^\"'\\]|$)") - - -def tempfile_arg_esc_func(arg): - arg = quote_spaces(arg) - if SCons.Platform.platform_default() != "win32": - return arg - # GCC requires double Windows slashes, let's use UNIX separator - return WINPATHSEP_RE.sub(r"/\1", arg) - - -def wrap_tempfile(env, command): - env[command] = '${TEMPFILE("' + env[command] + '","$' + command + 'STR")}' - - -def link_dir(target_path, source_path, is_windows): - # print(f"link_dir: {target_path} -> {source_path}") - if os.path.lexists(target_path) or os.path.exists(target_path): - os.unlink(target_path) - if is_windows: - # Crete junction - import _winapi - - if not os.path.isdir(source_path): - raise StopError(f"Source directory {source_path} is not a directory") - - if not os.path.exists(target_path): - _winapi.CreateJunction(source_path, target_path) - else: - os.symlink(source_path, target_path) - - -def single_quote(arg_list): - return " ".join(f"'{arg}'" if " " in arg else str(arg) for arg in arg_list) - - -def link_elf_dir_as_latest(env, elf_node): - elf_dir = elf_node.Dir(".") - latest_dir = env.Dir("#build/latest") - print(f"Setting {elf_dir} as latest built dir (./build/latest/)") - return link_dir(latest_dir.abspath, elf_dir.abspath, env["PLATFORM"] == "win32") - - -def should_gen_cdb_and_link_dir(env, requested_targets): - explicitly_building_updater = False - # Hacky way to check if updater-related targets were requested - for build_target in requested_targets: - if "updater" in str(build_target): - explicitly_building_updater = True - - is_updater = not env["IS_BASE_FIRMWARE"] - # If updater is explicitly requested, link to the latest updater - # Otherwise, link to firmware - return (is_updater and explicitly_building_updater) or ( - not is_updater and not explicitly_building_updater - ) diff --git a/site_scons/fbt_extra/util.py b/site_scons/fbt_extra/util.py new file mode 100644 index 00000000000..c670c01d4d0 --- /dev/null +++ b/site_scons/fbt_extra/util.py @@ -0,0 +1,24 @@ +from fbt.util import link_dir +from ansi.color import fg + + +def link_elf_dir_as_latest(env, elf_node): + elf_dir = elf_node.Dir(".") + latest_dir = env.Dir("#build/latest") + print(fg.green(f"Linking {elf_dir} as latest built dir (./build/latest/)")) + return link_dir(latest_dir.abspath, elf_dir.abspath, env["PLATFORM"] == "win32") + + +def should_gen_cdb_and_link_dir(env, requested_targets): + explicitly_building_updater = False + # Hacky way to check if updater-related targets were requested + for build_target in requested_targets: + if "updater" in str(build_target) and "package" not in str(build_target): + explicitly_building_updater = True + + is_updater = not env["IS_BASE_FIRMWARE"] + # If updater is explicitly requested, link to the latest updater + # Otherwise, link to firmware + return (is_updater and explicitly_building_updater) or ( + not is_updater and not explicitly_building_updater + ) diff --git a/site_scons/firmwareopts.scons b/site_scons/firmwareopts.scons index f04b55cdd20..e4cc8db5824 100644 --- a/site_scons/firmwareopts.scons +++ b/site_scons/firmwareopts.scons @@ -32,19 +32,32 @@ else: ], ) -ENV.Append( +ENV.AppendUnique( LINKFLAGS=[ - "-Tfirmware/targets/f${TARGET_HW}/${LINKER_SCRIPT}.ld", + "-specs=nano.specs", + "-specs=nosys.specs", + "-Wl,--gc-sections", + "-Wl,--undefined=uxTopUsedPriority", + "-Wl,--wrap,_malloc_r", + "-Wl,--wrap,_free_r", + "-Wl,--wrap,_calloc_r", + "-Wl,--wrap,_realloc_r", + "-n", + "-Xlinker", + "-Map=${TARGET}.map", + "-T${LINKER_SCRIPT_PATH}", ], ) + if ENV["FIRMWARE_BUILD_CFG"] == "updater": ENV.Append( IMAGE_BASE_ADDRESS="0x20000000", - LINKER_SCRIPT="stm32wb55xx_ram_fw", + LINKER_SCRIPT_PATH=ENV["TARGET_CFG"].linker_script_ram, ) else: ENV.Append( IMAGE_BASE_ADDRESS="0x8000000", - LINKER_SCRIPT="stm32wb55xx_flash", + LINKER_SCRIPT_PATH=ENV["TARGET_CFG"].linker_script_flash, + APP_LINKER_SCRIPT_PATH=ENV["TARGET_CFG"].linker_script_app, ) diff --git a/site_scons/site_init.py b/site_scons/site_init.py index 817269e286a..b763a3a7c05 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -3,8 +3,10 @@ import sys import os import atexit +from ansi.color import fg, fx sys.path.insert(0, os.path.join(os.getcwd(), "scripts")) +sys.path.insert(0, os.path.join(os.getcwd(), "lib/cxxheaderparser")) def bf_to_str(bf): @@ -15,12 +17,12 @@ def bf_to_str(bf): if bf is None: # unknown targets product None in list return "(unknown tgt)" elif isinstance(bf, SCons.Errors.StopError): - return str(bf) + return fg.yellow(str(bf)) elif bf.node: - return str(bf.node) + ": " + bf.errstr + return fg.yellow(str(bf.node)) + ": " + bf.errstr elif bf.filename: - return bf.filename + ": " + bf.errstr - return "unknown failure: " + bf.errstr + return fg.yellow(bf.filename) + ": " + bf.errstr + return fg.yellow("unknown failure: ") + bf.errstr def display_build_status(): @@ -30,10 +32,9 @@ def display_build_status(): if bf: # bf is normally a list of build failures; if an element is None, # it's because of a target that scons doesn't know anything about. - failures_message = "\n".join( - ["Failed building %s" % bf_to_str(x) for x in bf if x is not None] - ) - print("*" * 10, "ERRORS", "*" * 10) + failures_message = "\n".join([bf_to_str(x) for x in bf if x is not None]) + print() + print(fg.brightred(fx.bold("*" * 10 + " FBT ERRORS " + "*" * 10))) print(failures_message) diff --git a/site_scons/site_tools/crosscc.py b/site_scons/site_tools/crosscc.py deleted file mode 100644 index aacda58c6b3..00000000000 --- a/site_scons/site_tools/crosscc.py +++ /dev/null @@ -1,73 +0,0 @@ -from SCons.Errors import StopError -from SCons.Tool import asm -from SCons.Tool import gcc -from SCons.Tool import gxx -from SCons.Tool import ar -from SCons.Tool import gnulink -import strip -import gdb -import objdump - -from SCons.Action import _subproc -import subprocess - - -def prefix_commands(env, command_prefix, cmd_list): - for command in cmd_list: - if command in env: - env[command] = command_prefix + env[command] - - -def _get_tool_version(env, tool): - verstr = "version unknown" - proc = _subproc( - env, - env.subst("${%s} --version" % tool), - stdout=subprocess.PIPE, - stderr="devnull", - stdin="devnull", - universal_newlines=True, - error="raise", - shell=True, - ) - if proc: - verstr = proc.stdout.readline() - proc.communicate() - return verstr - - -def generate(env, **kw): - for orig_tool in (asm, gcc, gxx, ar, gnulink, strip, gdb, objdump): - orig_tool.generate(env) - env.SetDefault( - TOOLCHAIN_PREFIX=kw.get("toolchain_prefix"), - ) - prefix_commands( - env, - env.subst("$TOOLCHAIN_PREFIX"), - [ - "AR", - "AS", - "CC", - "CXX", - "OBJCOPY", - "RANLIB", - "STRIP", - "GDB", - "GDBPY", - "OBJDUMP", - ], - ) - # Call CC to check version - if whitelisted_versions := kw.get("versions", ()): - cc_version = _get_tool_version(env, "CC") - # print("CC version =", cc_version) - # print(list(filter(lambda v: v in cc_version, whitelisted_versions))) - if not any(filter(lambda v: v in cc_version, whitelisted_versions)): - raise StopError( - f"Toolchain version is not supported. Allowed: {whitelisted_versions}, toolchain: {cc_version} " - ) - - -def exists(env): - return True diff --git a/site_scons/site_tools/fbt_apps.py b/site_scons/site_tools/fbt_apps.py deleted file mode 100644 index 3dc35049b63..00000000000 --- a/site_scons/site_tools/fbt_apps.py +++ /dev/null @@ -1,77 +0,0 @@ -from SCons.Builder import Builder -from SCons.Action import Action -from SCons.Warnings import warn, WarningOnByDefault -import SCons -import os.path - -from fbt.appmanifest import ( - FlipperAppType, - AppManager, - ApplicationsCGenerator, - FlipperManifestException, -) - -# Adding objects for application management to env -# AppManager env["APPMGR"] - loads all manifests; manages list of known apps -# AppBuildset env["APPBUILD"] - contains subset of apps, filtered for current config - - -def LoadApplicationManifests(env): - appmgr = env["APPMGR"] = AppManager() - for entry in env.Glob("#/applications/*", ondisk=True, source=True): - if isinstance(entry, SCons.Node.FS.Dir) and not str(entry).startswith("."): - try: - app_manifest_file_path = os.path.join(entry.abspath, "application.fam") - appmgr.load_manifest(app_manifest_file_path, entry.name) - env.Append(PY_LINT_SOURCES=[app_manifest_file_path]) - except FlipperManifestException as e: - warn(WarningOnByDefault, str(e)) - - -def PrepareApplicationsBuild(env): - env["APPBUILD"] = env["APPMGR"].filter_apps(env["APPS"]) - env["APPBUILD_DUMP"] = env.Action( - DumpApplicationConfig, - "\tINFO\t", - ) - - -def DumpApplicationConfig(target, source, env): - print(f"Loaded {len(env['APPMGR'].known_apps)} app definitions.") - print("Firmware modules configuration:") - for apptype in FlipperAppType: - app_sublist = env["APPBUILD"].get_apps_of_type(apptype) - if app_sublist: - print( - f"{apptype.value}:\n\t", - ", ".join(app.appid for app in app_sublist), - ) - - -def build_apps_c(target, source, env): - target_file_name = target[0].path - - gen = ApplicationsCGenerator(env["APPBUILD"], env.subst("$LOADER_AUTOSTART")) - with open(target_file_name, "w") as file: - file.write(gen.generate()) - - -def generate(env): - env.AddMethod(LoadApplicationManifests) - env.AddMethod(PrepareApplicationsBuild) - - env.Append( - BUILDERS={ - "ApplicationsC": Builder( - action=Action( - build_apps_c, - "${APPSCOMSTR}", - ), - suffix=".c", - ), - } - ) - - -def exists(env): - return True diff --git a/site_scons/site_tools/fbt_assets.py b/site_scons/site_tools/fbt_assets.py deleted file mode 100644 index c844db36b52..00000000000 --- a/site_scons/site_tools/fbt_assets.py +++ /dev/null @@ -1,151 +0,0 @@ -import SCons - -from SCons.Builder import Builder -from SCons.Action import Action -from SCons.Node.FS import File - -import os -import subprocess - - -def icons_emitter(target, source, env): - target = [ - "compiled/assets_icons.c", - "compiled/assets_icons.h", - ] - return target, source - - -def proto_emitter(target, source, env): - out_path = target[0].path - target = [] - for src in source: - basename = os.path.splitext(src.name)[0] - target.append(env.File(f"compiled/{basename}.pb.c")) - target.append(env.File(f"compiled/{basename}.pb.h")) - return target, source - - -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), - ) - - target_base_dir = target[0] - env.Replace(_DOLPHIN_OUT_DIR=target[0]) - - if env["DOLPHIN_RES_TYPE"] == "external": - target = [] - target.extend( - map( - lambda node: target_base_dir.File( - res_root_dir.rel_path(node).replace(".png", ".bm") - ), - filter(lambda node: isinstance(node, SCons.Node.FS.File), source), - ) - ) - else: - asset_basename = f"assets_dolphin_{env['DOLPHIN_RES_TYPE']}" - target = [ - target_base_dir.File(asset_basename + ".c"), - target_base_dir.File(asset_basename + ".h"), - ] - - return target, source - - -def _invoke_git(args, source_dir): - cmd = ["git"] - cmd.extend(args) - return ( - subprocess.check_output(cmd, cwd=source_dir, stderr=subprocess.STDOUT) - .strip() - .decode() - ) - - -def proto_ver_generator(target, source, env): - target_file = target[0] - src_dir = source[0].dir.abspath - try: - git_fetch = _invoke_git( - ["fetch", "--tags"], - source_dir=src_dir, - ) - except (subprocess.CalledProcessError, EnvironmentError) as e: - # Not great, not terrible - print("Git: fetch failed") - - try: - git_describe = _invoke_git( - ["describe", "--tags", "--abbrev=0"], - source_dir=src_dir, - ) - except (subprocess.CalledProcessError, EnvironmentError) as e: - print("Git: describe failed") - Exit("git error") - - # print("describe=", git_describe) - git_major, git_minor = git_describe.split(".") - version_file_data = ( - "#pragma once", - f"#define PROTOBUF_MAJOR_VERSION {git_major}", - f"#define PROTOBUF_MINOR_VERSION {git_minor}", - "", - ) - with open(str(target_file), "wt") as file: - file.write("\n".join(version_file_data)) - - -def generate(env): - env.SetDefault( - ASSETS_COMPILER="${ROOT_DIR.abspath}/scripts/assets.py", - NANOPB_COMPILER="${ROOT_DIR.abspath}/lib/nanopb/generator/nanopb_generator.py", - ) - - env.Append( - BUILDERS={ - "IconBuilder": Builder( - action=Action( - '${PYTHON3} "${ASSETS_COMPILER}" icons ${SOURCE.posix} ${TARGET.dir.posix}', - "${ICONSCOMSTR}", - ), - emitter=icons_emitter, - ), - "ProtoBuilder": Builder( - action=Action( - '${PYTHON3} "${NANOPB_COMPILER}" -q -I${SOURCE.dir.posix} -D${TARGET.dir.posix} ${SOURCES.posix}', - "${PROTOCOMSTR}", - ), - emitter=proto_emitter, - suffix=".pb.c", - src_suffix=".proto", - ), - "DolphinSymBuilder": Builder( - action=Action( - '${PYTHON3} "${ASSETS_COMPILER}" dolphin -s dolphin_${DOLPHIN_RES_TYPE} "${SOURCE}" "${_DOLPHIN_OUT_DIR}"', - "${DOLPHINCOMSTR}", - ), - emitter=dolphin_emitter, - ), - "DolphinExtBuilder": Builder( - action=Action( - '${PYTHON3} "${ASSETS_COMPILER}" dolphin "${SOURCE}" "${_DOLPHIN_OUT_DIR}"', - "${DOLPHINCOMSTR}", - ), - emitter=dolphin_emitter, - ), - "ProtoVerBuilder": Builder( - action=Action( - proto_ver_generator, - "${PBVERCOMSTR}", - ), - ), - } - ) - - -def exists(env): - return True diff --git a/site_scons/site_tools/fbt_dist.py b/site_scons/site_tools/fbt_dist.py deleted file mode 100644 index 399b7ecdd60..00000000000 --- a/site_scons/site_tools/fbt_dist.py +++ /dev/null @@ -1,155 +0,0 @@ -from SCons.Builder import Builder -from SCons.Action import Action -from SCons.Script import Mkdir -from SCons.Defaults import Touch - - -def get_variant_dirname(env, project=None): - parts = [f"f{env['TARGET_HW']}"] - if project: - parts.append(project) - - suffix = "" - if env["DEBUG"]: - suffix += "D" - if env["COMPACT"]: - suffix += "C" - if suffix: - parts.append(suffix) - - return "-".join(parts) - - -def create_fw_build_targets(env, configuration_name): - flavor = get_variant_dirname(env, configuration_name) - build_dir = env.Dir("build").Dir(flavor).abspath - return env.SConscript( - "firmware.scons", - variant_dir=build_dir, - duplicate=0, - exports={ - "ENV": env, - "fw_build_meta": { - "type": configuration_name, - "flavor": flavor, - "build_dir": build_dir, - }, - }, - ) - - -def AddFwProject(env, base_env, fw_type, fw_env_key): - project_env = env[fw_env_key] = create_fw_build_targets(base_env, fw_type) - env.Append( - DIST_PROJECTS=[ - project_env["FW_FLAVOR"], - ], - DIST_DEPENDS=[ - project_env["FW_ARTIFACTS"], - ], - ) - - env.Replace(DIST_DIR=get_variant_dirname(env)) - return project_env - - -def AddOpenOCDFlashTarget(env, targetenv, **kw): - openocd_target = env.OpenOCDFlash( - "#build/oocd-${BUILD_CFG}-flash.flag", - targetenv["FW_BIN"], - OPENOCD_COMMAND=[ - "-c", - "program ${SOURCE.posix} reset exit ${BASE_ADDRESS}", - ], - BUILD_CFG=targetenv.subst("$FIRMWARE_BUILD_CFG"), - BASE_ADDRESS=targetenv.subst("$IMAGE_BASE_ADDRESS"), - **kw, - ) - env.Alias(targetenv.subst("${FIRMWARE_BUILD_CFG}_flash"), openocd_target) - if env["FORCE"]: - env.AlwaysBuild(openocd_target) - return openocd_target - - -def AddJFlashTarget(env, targetenv, **kw): - jflash_target = env.JFlash( - "#build/jflash-${BUILD_CFG}-flash.flag", - targetenv["FW_BIN"], - JFLASHADDR=targetenv.subst("$IMAGE_BASE_ADDRESS"), - BUILD_CFG=targetenv.subst("${FIRMWARE_BUILD_CFG}"), - **kw, - ) - env.Alias(targetenv.subst("${FIRMWARE_BUILD_CFG}_jflash"), jflash_target) - if env["FORCE"]: - env.AlwaysBuild(jflash_target) - return jflash_target - - -def AddUsbFlashTarget(env, file_flag, extra_deps, **kw): - usb_update = env.UsbInstall( - file_flag, - ( - env["DIST_DEPENDS"], - *extra_deps, - ), - ) - if env["FORCE"]: - env.AlwaysBuild(usb_update) - return usb_update - - -def DistCommand(env, name, source, **kw): - target = f"dist_{name}" - command = env.Command( - target, - source, - '@${PYTHON3} "${ROOT_DIR.abspath}/scripts/sconsdist.py" copy -p ${DIST_PROJECTS} -s "${DIST_SUFFIX}" ${DIST_EXTRA}', - **kw, - ) - env.Pseudo(target) - env.Alias(name, command) - return command - - -def generate(env): - env.AddMethod(AddFwProject) - env.AddMethod(DistCommand) - env.AddMethod(AddOpenOCDFlashTarget) - env.AddMethod(AddJFlashTarget) - env.AddMethod(AddUsbFlashTarget) - - env.SetDefault( - COPRO_MCU_FAMILY="STM32WB5x", - ) - - env.Append( - BUILDERS={ - "UsbInstall": Builder( - action=[ - Action( - '${PYTHON3} "${ROOT_DIR.abspath}/scripts/selfupdate.py" dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}/update.fuf' - ), - Touch("${TARGET}"), - ] - ), - "CoproBuilder": Builder( - action=Action( - [ - Mkdir("$TARGET"), - '${PYTHON3} "${ROOT_DIR.abspath}/scripts/assets.py" ' - "copro ${COPRO_CUBE_DIR} " - "${TARGET} ${COPRO_MCU_FAMILY} " - "--cube_ver=${COPRO_CUBE_VERSION} " - "--stack_type=${COPRO_STACK_TYPE} " - '--stack_file="${COPRO_STACK_BIN}" ' - "--stack_addr=${COPRO_STACK_ADDR} ", - ], - "", - ) - ), - } - ) - - -def exists(env): - return True diff --git a/site_scons/site_tools/fbt_extapps.py b/site_scons/site_tools/fbt_extapps.py deleted file mode 100644 index 17c4bf6c545..00000000000 --- a/site_scons/site_tools/fbt_extapps.py +++ /dev/null @@ -1,30 +0,0 @@ -import os - - -def BuildAppElf(env, app): - work_dir = env.subst("$EXT_APPS_WORK_DIR") - app_target_name = os.path.join(work_dir, app.appid) - app_alias = f"{env['FIRMWARE_BUILD_CFG']}_{app.appid}" - app_elf = env.Program( - app_target_name, - env.GlobRecursive("*.c*", os.path.join(work_dir, app._appdir)), - APP_ENTRY=app.entry_point, - ) - app_elf_dump = env.ObjDump(app_target_name) - env.Alias(f"{app_alias}_list", app_elf_dump) - - app_stripped_elf = env.ELFStripper( - os.path.join(env.subst("$PLUGIN_ELF_DIR"), app.appid), app_elf - ) - env.Alias(app_alias, app_stripped_elf) - return app_stripped_elf - - -def generate(env, **kw): - env.SetDefault(EXT_APPS_WORK_DIR=kw.get("EXT_APPS_WORK_DIR", ".extapps")) - env.VariantDir(env.subst("$EXT_APPS_WORK_DIR"), ".", duplicate=False) - env.AddMethod(BuildAppElf) - - -def exists(env): - return True diff --git a/site_scons/site_tools/fbt_version.py b/site_scons/site_tools/fbt_version.py deleted file mode 100644 index 909eea4f342..00000000000 --- a/site_scons/site_tools/fbt_version.py +++ /dev/null @@ -1,29 +0,0 @@ -from SCons.Builder import Builder -from SCons.Action import Action - - -def version_emitter(target, source, env): - target_dir = target[0] - target = [ - target_dir.File("version.inc.h"), - target_dir.File("version.json"), - ] - return target, source - - -def generate(env): - env.Append( - BUILDERS={ - "VersionBuilder": Builder( - action=Action( - '${PYTHON3} "${ROOT_DIR.abspath}/scripts/version.py" generate -t ${TARGET_HW} -o ${TARGET.dir.posix} --dir "${ROOT_DIR}"', - "${VERSIONCOMSTR}", - ), - emitter=version_emitter, - ), - } - ) - - -def exists(env): - return True diff --git a/site_scons/site_tools/fwbin.py b/site_scons/site_tools/fwbin.py deleted file mode 100644 index 37e64e56da8..00000000000 --- a/site_scons/site_tools/fwbin.py +++ /dev/null @@ -1,52 +0,0 @@ -from SCons.Builder import Builder -from SCons.Action import Action -import SCons - -__OBJCOPY_ARM_BIN = "arm-none-eabi-objcopy" - - -def generate(env): - env.SetDefault( - BIN2DFU="${ROOT_DIR.abspath}/scripts/bin2dfu.py", - OBJCOPY=__OBJCOPY_ARM_BIN, # FIXME - ) - env.Append( - BUILDERS={ - "HEXBuilder": Builder( - action=Action( - '${OBJCOPY} -O ihex "${SOURCE}" "${TARGET}"', - "${HEXCOMSTR}", - ), - suffix=".hex", - src_suffix=".elf", - ), - "BINBuilder": Builder( - action=Action( - '${OBJCOPY} -O binary -S "${SOURCE}" "${TARGET}"', - "${BINCOMSTR}", - ), - suffix=".bin", - src_suffix=".elf", - ), - "DFUBuilder": Builder( - action=Action( - '${PYTHON3} "${BIN2DFU}" -i "${SOURCE}" -o "${TARGET}" -a ${IMAGE_BASE_ADDRESS} -l "Flipper Zero F${TARGET_HW}"', - "${DFUCOMSTR}", - ), - suffix=".dfu", - src_suffix=".bin", - ), - } - ) - - -def exists(env): - try: - return env["OBJCOPY"] - except KeyError: - pass - - if objcopy := env.WhereIs(__OBJCOPY_ARM_BIN): - return objcopy - - raise SCons.Errors.StopError("Could not detect objcopy for arm") diff --git a/site_scons/site_tools/gdb.py b/site_scons/site_tools/gdb.py deleted file mode 100644 index 94ea9fbe9be..00000000000 --- a/site_scons/site_tools/gdb.py +++ /dev/null @@ -1,17 +0,0 @@ -from SCons.Builder import Builder -from SCons.Action import Action - - -def generate(env): - env.SetDefault( - GDB="gdb", - GDBPY="gdb-py", - GDBOPTS="", - GDBPYOPTS="", - GDBCOM="$GDB $GDBOPTS $SOURCES", # no $TARGET - GDBPYCOM="$GDBPY $GDBOPTS $GDBPYOPTS $SOURCES", # no $TARGET - ) - - -def exists(env): - return True diff --git a/site_scons/site_tools/jflash.py b/site_scons/site_tools/jflash.py deleted file mode 100644 index aea7279b636..00000000000 --- a/site_scons/site_tools/jflash.py +++ /dev/null @@ -1,27 +0,0 @@ -from SCons.Builder import Builder -from SCons.Defaults import Touch - - -def generate(env): - env.SetDefault( - JFLASH="JFlash" if env.subst("$PLATFORM") == "win32" else "JFlashExe", - JFLASHFLAGS=[ - "-auto", - "-exit", - ], - JFLASHCOM="${JFLASH} -openprj${JFLASHPROJECT} -open${SOURCE},${JFLASHADDR} ${JFLASHFLAGS}", - ) - env.Append( - BUILDERS={ - "JFlash": Builder( - action=[ - "${JFLASHCOM}", - Touch("${TARGET}"), - ], - ), - } - ) - - -def exists(env): - return True diff --git a/site_scons/site_tools/openocd.py b/site_scons/site_tools/openocd.py deleted file mode 100644 index dcf0bf925aa..00000000000 --- a/site_scons/site_tools/openocd.py +++ /dev/null @@ -1,46 +0,0 @@ -from SCons.Builder import Builder -from SCons.Action import Action -from SCons.Defaults import Touch -import SCons - -__OPENOCD_BIN = "openocd" - -_oocd_action = Action( - "${OPENOCD} ${OPENOCD_OPTS} ${OPENOCD_COMMAND}", - "${OPENOCDCOMSTR}", -) - - -def generate(env): - env.SetDefault( - OPENOCD=__OPENOCD_BIN, - OPENOCD_OPTS="", - OPENOCD_COMMAND="", - OPENOCDCOM="${OPENOCD} ${OPENOCD_OPTS} ${OPENOCD_COMMAND}", - OPENOCDCOMSTR="", - ) - - env.Append( - BUILDERS={ - "OpenOCDFlash": Builder( - action=[ - _oocd_action, - Touch("${TARGET}"), - ], - suffix=".flash", - src_suffix=".bin", - ), - } - ) - - -def exists(env): - try: - return env["OPENOCD"] - except KeyError: - pass - - if openocd := env.WhereIs(__OPENOCD_BIN): - return openocd - - raise SCons.Errors.StopError("Could not detect openocd") diff --git a/site_scons/site_tools/sconsrecursiveglob.py b/site_scons/site_tools/sconsrecursiveglob.py deleted file mode 100644 index 32ff55ea10c..00000000000 --- a/site_scons/site_tools/sconsrecursiveglob.py +++ /dev/null @@ -1,25 +0,0 @@ -import SCons - - -def GlobRecursive(env, pattern, node=".", exclude=None): - results = [] - if isinstance(node, str): - node = env.Dir(node) - for f in node.glob("*", source=True, exclude=exclude): - if isinstance(f, SCons.Node.FS.Dir): - results += env.GlobRecursive(pattern, f, exclude) - results += node.glob( - pattern, - source=True, - exclude=exclude, - ) - # print(f"Glob for {pattern} from {node}: {results}") - return results - - -def generate(env): - env.AddMethod(GlobRecursive) - - -def exists(env): - return True diff --git a/targets/ReadMe.md b/targets/ReadMe.md new file mode 100644 index 00000000000..c39f8c4ab31 --- /dev/null +++ b/targets/ReadMe.md @@ -0,0 +1,22 @@ +# Flipper firmware + +What does it do? + +- [x] RTOS +- [x] FuriHAL +- [x] FuriCore +- [x] Services +- [x] Applications + +# Targets + +| Name | Firmware Address | Reset Combo | DFU Combo | +|-----------|-------------------|-----------------------|-----------------------| +| f7 | 0x08000000 | L+Back, release both | L+Back, release Back | + +Also, there is a "hardware" ST bootloader combo available even on a bricked or empty device: L+Ok+Back, release Back, Left. +Target independent code and headers in `target/include` folders. More details in `documentation/KeyCombo.md` + +# Building + +Check out `documentation/fbt.md` on how to build and flash firmware. diff --git a/targets/SConscript b/targets/SConscript new file mode 100644 index 00000000000..8d8789e7235 --- /dev/null +++ b/targets/SConscript @@ -0,0 +1,21 @@ +Import("env") + +env.Append( + LINT_SOURCES=[Dir(".")], +) + +libenv = env.Clone(FW_LIB_NAME="flipper${TARGET_HW}") +libenv.Append( + CPPPATH=[ + "#/lib/stm32wb_copro/wpan/interface/patterns/ble_thread/tl", + ] +) +libenv.ApplyLibFlags() + + +lib = libenv.StaticLibrary( + "${FW_LIB_NAME}", + env.get("TARGET_CFG").gatherSources(), +) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv new file mode 100644 index 00000000000..cb34f969adc --- /dev/null +++ b/targets/f18/api_symbols.csv @@ -0,0 +1,2805 @@ +entry,status,name,type,params +Version,+,49.1,, +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/gui/elements.h,, +Header,+,applications/services/gui/gui.h,, +Header,+,applications/services/gui/icon_i.h,, +Header,+,applications/services/gui/modules/button_menu.h,, +Header,+,applications/services/gui/modules/button_panel.h,, +Header,+,applications/services/gui/modules/byte_input.h,, +Header,+,applications/services/gui/modules/dialog_ex.h,, +Header,+,applications/services/gui/modules/empty_screen.h,, +Header,+,applications/services/gui/modules/file_browser.h,, +Header,+,applications/services/gui/modules/file_browser_worker.h,, +Header,+,applications/services/gui/modules/loading.h,, +Header,+,applications/services/gui/modules/menu.h,, +Header,+,applications/services/gui/modules/popup.h,, +Header,+,applications/services/gui/modules/submenu.h,, +Header,+,applications/services/gui/modules/text_box.h,, +Header,+,applications/services/gui/modules/text_input.h,, +Header,+,applications/services/gui/modules/validators.h,, +Header,+,applications/services/gui/modules/variable_item_list.h,, +Header,+,applications/services/gui/modules/widget.h,, +Header,+,applications/services/gui/modules/widget_elements/widget_element.h,, +Header,+,applications/services/gui/view_dispatcher.h,, +Header,+,applications/services/gui/view_stack.h,, +Header,+,applications/services/input/input.h,, +Header,+,applications/services/loader/firmware_api/firmware_api.h,, +Header,+,applications/services/loader/loader.h,, +Header,+,applications/services/locale/locale.h,, +Header,+,applications/services/notification/notification.h,, +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,+,lib/digital_signal/digital_sequence.h,, +Header,+,lib/digital_signal/digital_signal.h,, +Header,+,lib/drivers/cc1101_regs.h,, +Header,+,lib/drivers/st25r3916.h,, +Header,+,lib/drivers/st25r3916_reg.h,, +Header,+,lib/flipper_application/api_hashtable/api_hashtable.h,, +Header,+,lib/flipper_application/api_hashtable/compilesort.hpp,, +Header,+,lib/flipper_application/flipper_application.h,, +Header,+,lib/flipper_application/plugins/composite_resolver.h,, +Header,+,lib/flipper_application/plugins/plugin_manager.h,, +Header,+,lib/flipper_format/flipper_format.h,, +Header,+,lib/flipper_format/flipper_format_i.h,, +Header,+,lib/flipper_format/flipper_format_stream.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_button.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_consumer.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_desktop.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_device.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_game.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_keyboard.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_led.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_ordinal.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_power.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_simulation.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_sport.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_telephony.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_vr.h,, +Header,-,lib/libusb_stm32/inc/stm32_compat.h,, +Header,+,lib/libusb_stm32/inc/usb.h,, +Header,+,lib/libusb_stm32/inc/usb_ccid.h,, +Header,+,lib/libusb_stm32/inc/usb_cdc.h,, +Header,+,lib/libusb_stm32/inc/usb_cdca.h,, +Header,+,lib/libusb_stm32/inc/usb_cdce.h,, +Header,+,lib/libusb_stm32/inc/usb_cdci.h,, +Header,+,lib/libusb_stm32/inc/usb_cdcp.h,, +Header,+,lib/libusb_stm32/inc/usb_cdcw.h,, +Header,+,lib/libusb_stm32/inc/usb_dfu.h,, +Header,+,lib/libusb_stm32/inc/usb_hid.h,, +Header,+,lib/libusb_stm32/inc/usb_std.h,, +Header,+,lib/libusb_stm32/inc/usb_tmc.h,, +Header,+,lib/libusb_stm32/inc/usbd_core.h,, +Header,+,lib/mbedtls/include/mbedtls/des.h,, +Header,+,lib/mbedtls/include/mbedtls/ecdh.h,, +Header,+,lib/mbedtls/include/mbedtls/ecdsa.h,, +Header,+,lib/mbedtls/include/mbedtls/ecp.h,, +Header,+,lib/mbedtls/include/mbedtls/md.h,, +Header,+,lib/mbedtls/include/mbedtls/md5.h,, +Header,+,lib/mbedtls/include/mbedtls/sha1.h,, +Header,+,lib/mbedtls/include/mbedtls/sha256.h,, +Header,+,lib/mlib/m-algo.h,, +Header,+,lib/mlib/m-array.h,, +Header,+,lib/mlib/m-bptree.h,, +Header,+,lib/mlib/m-core.h,, +Header,+,lib/mlib/m-deque.h,, +Header,+,lib/mlib/m-dict.h,, +Header,+,lib/mlib/m-list.h,, +Header,+,lib/mlib/m-rbtree.h,, +Header,+,lib/mlib/m-tuple.h,, +Header,+,lib/mlib/m-variant.h,, +Header,+,lib/music_worker/music_worker.h,, +Header,+,lib/nanopb/pb.h,, +Header,+,lib/nanopb/pb_decode.h,, +Header,+,lib/nanopb/pb_encode.h,, +Header,+,lib/one_wire/maxim_crc.h,, +Header,+,lib/one_wire/one_wire_host.h,, +Header,+,lib/one_wire/one_wire_slave.h,, +Header,+,lib/print/wrappers.h,, +Header,+,lib/pulse_reader/pulse_reader.h,, +Header,+,lib/signal_reader/signal_reader.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_adc.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_bus.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_comp.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_cortex.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_crc.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_crs.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_dma.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_dmamux.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_exti.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_gpio.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_hsem.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_i2c.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_ipcc.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_iwdg.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_lptim.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_lpuart.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_pka.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_pwr.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_rcc.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_rng.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_rtc.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_spi.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_system.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_tim.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_usart.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_utils.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_wwdg.h,, +Header,+,lib/toolbox/api_lock.h,, +Header,+,lib/toolbox/args.h,, +Header,+,lib/toolbox/bit_buffer.h,, +Header,+,lib/toolbox/compress.h,, +Header,+,lib/toolbox/crc32_calc.h,, +Header,+,lib/toolbox/dir_walk.h,, +Header,+,lib/toolbox/float_tools.h,, +Header,+,lib/toolbox/hex.h,, +Header,+,lib/toolbox/manchester_decoder.h,, +Header,+,lib/toolbox/manchester_encoder.h,, +Header,+,lib/toolbox/name_generator.h,, +Header,+,lib/toolbox/path.h,, +Header,+,lib/toolbox/pretty_format.h,, +Header,+,lib/toolbox/protocols/protocol_dict.h,, +Header,+,lib/toolbox/saved_struct.h,, +Header,+,lib/toolbox/simple_array.h,, +Header,+,lib/toolbox/stream/buffered_file_stream.h,, +Header,+,lib/toolbox/stream/file_stream.h,, +Header,+,lib/toolbox/stream/stream.h,, +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,+,targets/f18/furi_hal/furi_hal_resources.h,, +Header,+,targets/f18/furi_hal/furi_hal_spi_config.h,, +Header,+,targets/f18/furi_hal/furi_hal_target_hw.h,, +Header,+,targets/f7/furi_hal/furi_hal_bus.h,, +Header,+,targets/f7/furi_hal/furi_hal_clock.h,, +Header,+,targets/f7/furi_hal/furi_hal_console.h,, +Header,+,targets/f7/furi_hal/furi_hal_dma.h,, +Header,+,targets/f7/furi_hal/furi_hal_flash.h,, +Header,+,targets/f7/furi_hal/furi_hal_gpio.h,, +Header,+,targets/f7/furi_hal/furi_hal_i2c_config.h,, +Header,+,targets/f7/furi_hal/furi_hal_i2c_types.h,, +Header,+,targets/f7/furi_hal/furi_hal_idle_timer.h,, +Header,+,targets/f7/furi_hal/furi_hal_interrupt.h,, +Header,+,targets/f7/furi_hal/furi_hal_os.h,, +Header,+,targets/f7/furi_hal/furi_hal_pwm.h,, +Header,+,targets/f7/furi_hal/furi_hal_spi_types.h,, +Header,+,targets/f7/furi_hal/furi_hal_uart.h,, +Header,+,targets/f7/furi_hal/furi_hal_usb_cdc.h,, +Header,+,targets/f7/platform_specific/intrinsic_export.h,, +Header,+,targets/f7/platform_specific/math_wrapper.h,, +Header,+,targets/furi_hal_include/furi_hal.h,, +Header,+,targets/furi_hal_include/furi_hal_bt.h,, +Header,+,targets/furi_hal_include/furi_hal_bt_hid.h,, +Header,+,targets/furi_hal_include/furi_hal_bt_serial.h,, +Header,+,targets/furi_hal_include/furi_hal_cortex.h,, +Header,+,targets/furi_hal_include/furi_hal_crypto.h,, +Header,+,targets/furi_hal_include/furi_hal_debug.h,, +Header,+,targets/furi_hal_include/furi_hal_i2c.h,, +Header,+,targets/furi_hal_include/furi_hal_info.h,, +Header,+,targets/furi_hal_include/furi_hal_light.h,, +Header,+,targets/furi_hal_include/furi_hal_memory.h,, +Header,+,targets/furi_hal_include/furi_hal_mpu.h,, +Header,+,targets/furi_hal_include/furi_hal_power.h,, +Header,+,targets/furi_hal_include/furi_hal_random.h,, +Header,+,targets/furi_hal_include/furi_hal_region.h,, +Header,+,targets/furi_hal_include/furi_hal_rtc.h,, +Header,+,targets/furi_hal_include/furi_hal_sd.h,, +Header,+,targets/furi_hal_include/furi_hal_speaker.h,, +Header,+,targets/furi_hal_include/furi_hal_spi.h,, +Header,+,targets/furi_hal_include/furi_hal_usb.h,, +Header,+,targets/furi_hal_include/furi_hal_usb_ccid.h,, +Header,+,targets/furi_hal_include/furi_hal_usb_hid.h,, +Header,+,targets/furi_hal_include/furi_hal_usb_hid_u2f.h,, +Header,+,targets/furi_hal_include/furi_hal_version.h,, +Header,+,targets/furi_hal_include/furi_hal_vibro.h,, +Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* +Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, const LL_ADC_CommonInitTypeDef*" +Function,-,LL_ADC_CommonStructInit,void,LL_ADC_CommonInitTypeDef* +Function,-,LL_ADC_DeInit,ErrorStatus,ADC_TypeDef* +Function,-,LL_ADC_INJ_Init,ErrorStatus,"ADC_TypeDef*, const LL_ADC_INJ_InitTypeDef*" +Function,-,LL_ADC_INJ_StructInit,void,LL_ADC_INJ_InitTypeDef* +Function,-,LL_ADC_Init,ErrorStatus,"ADC_TypeDef*, const LL_ADC_InitTypeDef*" +Function,-,LL_ADC_REG_Init,ErrorStatus,"ADC_TypeDef*, const LL_ADC_REG_InitTypeDef*" +Function,-,LL_ADC_REG_StructInit,void,LL_ADC_REG_InitTypeDef* +Function,-,LL_ADC_StructInit,void,LL_ADC_InitTypeDef* +Function,-,LL_COMP_DeInit,ErrorStatus,COMP_TypeDef* +Function,+,LL_COMP_Init,ErrorStatus,"COMP_TypeDef*, const LL_COMP_InitTypeDef*" +Function,-,LL_COMP_StructInit,void,LL_COMP_InitTypeDef* +Function,-,LL_CRC_DeInit,ErrorStatus,CRC_TypeDef* +Function,-,LL_CRS_DeInit,ErrorStatus, +Function,+,LL_DMA_DeInit,ErrorStatus,"DMA_TypeDef*, uint32_t" +Function,+,LL_DMA_Init,ErrorStatus,"DMA_TypeDef*, uint32_t, LL_DMA_InitTypeDef*" +Function,-,LL_DMA_StructInit,void,LL_DMA_InitTypeDef* +Function,-,LL_EXTI_DeInit,ErrorStatus, +Function,-,LL_EXTI_Init,ErrorStatus,LL_EXTI_InitTypeDef* +Function,-,LL_EXTI_StructInit,void,LL_EXTI_InitTypeDef* +Function,-,LL_GPIO_DeInit,ErrorStatus,GPIO_TypeDef* +Function,+,LL_GPIO_Init,ErrorStatus,"GPIO_TypeDef*, LL_GPIO_InitTypeDef*" +Function,-,LL_GPIO_StructInit,void,LL_GPIO_InitTypeDef* +Function,-,LL_I2C_DeInit,ErrorStatus,const I2C_TypeDef* +Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, const LL_I2C_InitTypeDef*" +Function,-,LL_I2C_StructInit,void,LL_I2C_InitTypeDef* +Function,-,LL_Init1msTick,void,uint32_t +Function,+,LL_LPTIM_DeInit,ErrorStatus,LPTIM_TypeDef* +Function,-,LL_LPTIM_Disable,void,LPTIM_TypeDef* +Function,+,LL_LPTIM_Init,ErrorStatus,"LPTIM_TypeDef*, const LL_LPTIM_InitTypeDef*" +Function,-,LL_LPTIM_StructInit,void,LL_LPTIM_InitTypeDef* +Function,-,LL_LPUART_DeInit,ErrorStatus,const USART_TypeDef* +Function,+,LL_LPUART_Init,ErrorStatus,"USART_TypeDef*, const LL_LPUART_InitTypeDef*" +Function,-,LL_LPUART_StructInit,void,LL_LPUART_InitTypeDef* +Function,-,LL_PKA_DeInit,ErrorStatus,PKA_TypeDef* +Function,-,LL_PKA_Init,ErrorStatus,"PKA_TypeDef*, LL_PKA_InitTypeDef*" +Function,-,LL_PKA_StructInit,void,LL_PKA_InitTypeDef* +Function,-,LL_PLL_ConfigSystemClock_HSE,ErrorStatus,"uint32_t, LL_UTILS_PLLInitTypeDef*, LL_UTILS_ClkInitTypeDef*" +Function,-,LL_PLL_ConfigSystemClock_HSI,ErrorStatus,"LL_UTILS_PLLInitTypeDef*, LL_UTILS_ClkInitTypeDef*" +Function,-,LL_PLL_ConfigSystemClock_MSI,ErrorStatus,"LL_UTILS_PLLInitTypeDef*, LL_UTILS_ClkInitTypeDef*" +Function,-,LL_PWR_DeInit,ErrorStatus, +Function,-,LL_RCC_DeInit,ErrorStatus, +Function,-,LL_RCC_GetADCClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetCLK48ClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetI2CClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetLPTIMClockFreq,uint32_t,uint32_t +Function,+,LL_RCC_GetLPUARTClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetRFWKPClockFreq,uint32_t, +Function,-,LL_RCC_GetRNGClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetRTCClockFreq,uint32_t, +Function,-,LL_RCC_GetSAIClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetSMPSClockFreq,uint32_t, +Function,-,LL_RCC_GetSystemClocksFreq,void,LL_RCC_ClocksTypeDef* +Function,+,LL_RCC_GetUSARTClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetUSBClockFreq,uint32_t,uint32_t +Function,-,LL_RNG_DeInit,ErrorStatus,RNG_TypeDef* +Function,-,LL_RNG_Init,ErrorStatus,"RNG_TypeDef*, LL_RNG_InitTypeDef*" +Function,-,LL_RNG_StructInit,void,LL_RNG_InitTypeDef* +Function,-,LL_RTC_ALMA_Init,ErrorStatus,"RTC_TypeDef*, uint32_t, LL_RTC_AlarmTypeDef*" +Function,-,LL_RTC_ALMA_StructInit,void,LL_RTC_AlarmTypeDef* +Function,-,LL_RTC_ALMB_Init,ErrorStatus,"RTC_TypeDef*, uint32_t, LL_RTC_AlarmTypeDef*" +Function,-,LL_RTC_ALMB_StructInit,void,LL_RTC_AlarmTypeDef* +Function,-,LL_RTC_DATE_Init,ErrorStatus,"RTC_TypeDef*, uint32_t, LL_RTC_DateTypeDef*" +Function,-,LL_RTC_DATE_StructInit,void,LL_RTC_DateTypeDef* +Function,-,LL_RTC_DeInit,ErrorStatus,RTC_TypeDef* +Function,+,LL_RTC_EnterInitMode,ErrorStatus,RTC_TypeDef* +Function,-,LL_RTC_ExitInitMode,ErrorStatus,RTC_TypeDef* +Function,+,LL_RTC_Init,ErrorStatus,"RTC_TypeDef*, LL_RTC_InitTypeDef*" +Function,-,LL_RTC_StructInit,void,LL_RTC_InitTypeDef* +Function,-,LL_RTC_TIME_Init,ErrorStatus,"RTC_TypeDef*, uint32_t, LL_RTC_TimeTypeDef*" +Function,-,LL_RTC_TIME_StructInit,void,LL_RTC_TimeTypeDef* +Function,-,LL_RTC_WaitForSynchro,ErrorStatus,RTC_TypeDef* +Function,-,LL_SPI_DeInit,ErrorStatus,SPI_TypeDef* +Function,+,LL_SPI_Init,ErrorStatus,"SPI_TypeDef*, LL_SPI_InitTypeDef*" +Function,-,LL_SPI_StructInit,void,LL_SPI_InitTypeDef* +Function,-,LL_SetFlashLatency,ErrorStatus,uint32_t +Function,+,LL_SetSystemCoreClock,void,uint32_t +Function,-,LL_TIM_BDTR_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_BDTR_InitTypeDef*" +Function,-,LL_TIM_BDTR_StructInit,void,LL_TIM_BDTR_InitTypeDef* +Function,-,LL_TIM_DeInit,ErrorStatus,TIM_TypeDef* +Function,-,LL_TIM_ENCODER_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_ENCODER_InitTypeDef*" +Function,-,LL_TIM_ENCODER_StructInit,void,LL_TIM_ENCODER_InitTypeDef* +Function,-,LL_TIM_HALLSENSOR_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_HALLSENSOR_InitTypeDef*" +Function,-,LL_TIM_HALLSENSOR_StructInit,void,LL_TIM_HALLSENSOR_InitTypeDef* +Function,-,LL_TIM_IC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, const LL_TIM_IC_InitTypeDef*" +Function,-,LL_TIM_IC_StructInit,void,LL_TIM_IC_InitTypeDef* +Function,+,LL_TIM_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_InitTypeDef*" +Function,+,LL_TIM_OC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, const LL_TIM_OC_InitTypeDef*" +Function,-,LL_TIM_OC_StructInit,void,LL_TIM_OC_InitTypeDef* +Function,-,LL_TIM_StructInit,void,LL_TIM_InitTypeDef* +Function,-,LL_USART_ClockInit,ErrorStatus,"USART_TypeDef*, const LL_USART_ClockInitTypeDef*" +Function,-,LL_USART_ClockStructInit,void,LL_USART_ClockInitTypeDef* +Function,-,LL_USART_DeInit,ErrorStatus,const USART_TypeDef* +Function,+,LL_USART_Init,ErrorStatus,"USART_TypeDef*, const LL_USART_InitTypeDef*" +Function,-,LL_USART_StructInit,void,LL_USART_InitTypeDef* +Function,-,LL_mDelay,void,uint32_t +Function,-,SystemCoreClockUpdate,void, +Function,-,SystemInit,void, +Function,-,_Exit,void,int +Function,+,__aeabi_uldivmod,void*,"uint64_t, uint64_t" +Function,-,__assert,void,"const char*, int, const char*" +Function,+,__assert_func,void,"const char*, int, const char*, const char*" +Function,+,__clear_cache,void,"void*, void*" +Function,-,__eprintf,void,"const char*, const char*, unsigned int, const char*" +Function,+,__errno,int*, +Function,-,__fpclassifyd,int,double +Function,-,__fpclassifyf,int,float +Function,+,__furi_crash_implementation,void, +Function,+,__furi_critical_enter,__FuriCriticalInfo, +Function,+,__furi_critical_exit,void,__FuriCriticalInfo +Function,+,__furi_halt_implementation,void, +Function,-,__getdelim,ssize_t,"char**, size_t*, int, FILE*" +Function,-,__getline,ssize_t,"char**, size_t*, FILE*" +Function,-,__isinfd,int,double +Function,-,__isinff,int,float +Function,-,__isnand,int,double +Function,-,__isnanf,int,float +Function,-,__itoa,char*,"int, char*, int" +Function,-,__locale_mb_cur_max,int, +Function,+,__retarget_lock_acquire,void,_LOCK_T +Function,+,__retarget_lock_acquire_recursive,void,_LOCK_T +Function,-,__retarget_lock_close,void,_LOCK_T +Function,+,__retarget_lock_close_recursive,void,_LOCK_T +Function,-,__retarget_lock_init,void,_LOCK_T* +Function,+,__retarget_lock_init_recursive,void,_LOCK_T* +Function,+,__retarget_lock_release,void,_LOCK_T +Function,+,__retarget_lock_release_recursive,void,_LOCK_T +Function,-,__retarget_lock_try_acquire,int,_LOCK_T +Function,-,__retarget_lock_try_acquire_recursive,int,_LOCK_T +Function,-,__signbitd,int,double +Function,-,__signbitf,int,float +Function,-,__signgam,int*, +Function,-,__srget_r,int,"_reent*, FILE*" +Function,-,__swbuf_r,int,"_reent*, int, FILE*" +Function,-,__utoa,char*,"unsigned, char*, int" +Function,+,__wrap___assert,void,"const char*, int, const char*" +Function,+,__wrap___assert_func,void,"const char*, int, const char*, const char*" +Function,+,__wrap_fflush,int,FILE* +Function,+,__wrap_printf,int,"const char*, ..." +Function,+,__wrap_putc,int,"int, FILE*" +Function,+,__wrap_putchar,int,int +Function,+,__wrap_puts,int,const char* +Function,+,__wrap_snprintf,int,"char*, size_t, const char*, ..." +Function,+,__wrap_vsnprintf,int,"char*, size_t, const char*, va_list" +Function,-,_asiprintf_r,int,"_reent*, char**, const char*, ..." +Function,-,_asniprintf_r,char*,"_reent*, char*, size_t*, const char*, ..." +Function,-,_asnprintf_r,char*,"_reent*, char*, size_t*, const char*, ..." +Function,-,_asprintf_r,int,"_reent*, char**, const char*, ..." +Function,-,_atoi_r,int,"_reent*, const char*" +Function,-,_atol_r,long,"_reent*, const char*" +Function,-,_atoll_r,long long,"_reent*, const char*" +Function,-,_calloc_r,void*,"_reent*, size_t, size_t" +Function,-,_diprintf_r,int,"_reent*, int, const char*, ..." +Function,-,_dprintf_r,int,"_reent*, int, const char*, ..." +Function,-,_drand48_r,double,_reent* +Function,-,_dtoa_r,char*,"_reent*, double, int, int, int*, int*, char**" +Function,-,_erand48_r,double,"_reent*, unsigned short[3]" +Function,-,_fclose_r,int,"_reent*, FILE*" +Function,-,_fcloseall_r,int,_reent* +Function,-,_fdopen_r,FILE*,"_reent*, int, const char*" +Function,-,_fflush_r,int,"_reent*, FILE*" +Function,-,_fgetc_r,int,"_reent*, FILE*" +Function,-,_fgetc_unlocked_r,int,"_reent*, FILE*" +Function,-,_fgetpos_r,int,"_reent*, FILE*, fpos_t*" +Function,-,_fgets_r,char*,"_reent*, char*, int, FILE*" +Function,-,_fgets_unlocked_r,char*,"_reent*, char*, int, FILE*" +Function,-,_findenv,char*,"const char*, int*" +Function,-,_findenv_r,char*,"_reent*, const char*, int*" +Function,-,_fiprintf_r,int,"_reent*, FILE*, const char*, ..." +Function,-,_fiscanf_r,int,"_reent*, FILE*, const char*, ..." +Function,-,_fmemopen_r,FILE*,"_reent*, void*, size_t, const char*" +Function,-,_fopen_r,FILE*,"_reent*, const char*, const char*" +Function,-,_fopencookie_r,FILE*,"_reent*, void*, const char*, cookie_io_functions_t" +Function,-,_fprintf_r,int,"_reent*, FILE*, const char*, ..." +Function,-,_fpurge_r,int,"_reent*, FILE*" +Function,-,_fputc_r,int,"_reent*, int, FILE*" +Function,-,_fputc_unlocked_r,int,"_reent*, int, FILE*" +Function,-,_fputs_r,int,"_reent*, const char*, FILE*" +Function,-,_fputs_unlocked_r,int,"_reent*, const char*, FILE*" +Function,-,_fread_r,size_t,"_reent*, void*, size_t, size_t, FILE*" +Function,-,_fread_unlocked_r,size_t,"_reent*, void*, size_t, size_t, FILE*" +Function,-,_free_r,void,"_reent*, void*" +Function,-,_freopen_r,FILE*,"_reent*, const char*, const char*, FILE*" +Function,-,_fscanf_r,int,"_reent*, FILE*, const char*, ..." +Function,-,_fseek_r,int,"_reent*, FILE*, long, int" +Function,-,_fseeko_r,int,"_reent*, FILE*, _off_t, int" +Function,-,_fsetpos_r,int,"_reent*, FILE*, const fpos_t*" +Function,-,_ftell_r,long,"_reent*, FILE*" +Function,-,_ftello_r,_off_t,"_reent*, FILE*" +Function,-,_funopen_r,FILE*,"_reent*, const void*, int (*)(void*, char*, int), int (*)(void*, const char*, int), fpos_t (*)(void*, fpos_t, int), int (*)(void*)" +Function,-,_fwrite_r,size_t,"_reent*, const void*, size_t, size_t, FILE*" +Function,-,_fwrite_unlocked_r,size_t,"_reent*, const void*, size_t, size_t, FILE*" +Function,-,_getc_r,int,"_reent*, FILE*" +Function,-,_getc_unlocked_r,int,"_reent*, FILE*" +Function,-,_getchar_r,int,_reent* +Function,-,_getchar_unlocked_r,int,_reent* +Function,-,_getenv_r,char*,"_reent*, const char*" +Function,-,_gets_r,char*,"_reent*, char*" +Function,-,_iprintf_r,int,"_reent*, const char*, ..." +Function,-,_iscanf_r,int,"_reent*, const char*, ..." +Function,-,_jrand48_r,long,"_reent*, unsigned short[3]" +Function,-,_l64a_r,char*,"_reent*, long" +Function,-,_lcong48_r,void,"_reent*, unsigned short[7]" +Function,-,_lrand48_r,long,_reent* +Function,-,_malloc_r,void*,"_reent*, size_t" +Function,-,_mblen_r,int,"_reent*, const char*, size_t, _mbstate_t*" +Function,-,_mbstowcs_r,size_t,"_reent*, wchar_t*, const char*, size_t, _mbstate_t*" +Function,-,_mbtowc_r,int,"_reent*, wchar_t*, const char*, size_t, _mbstate_t*" +Function,-,_mkdtemp_r,char*,"_reent*, char*" +Function,-,_mkostemp_r,int,"_reent*, char*, int" +Function,-,_mkostemps_r,int,"_reent*, char*, int, int" +Function,-,_mkstemp_r,int,"_reent*, char*" +Function,-,_mkstemps_r,int,"_reent*, char*, int" +Function,-,_mktemp_r,char*,"_reent*, char*" +Function,-,_mrand48_r,long,_reent* +Function,-,_mstats_r,void,"_reent*, char*" +Function,-,_nrand48_r,long,"_reent*, unsigned short[3]" +Function,-,_open_memstream_r,FILE*,"_reent*, char**, size_t*" +Function,-,_perror_r,void,"_reent*, const char*" +Function,-,_printf_r,int,"_reent*, const char*, ..." +Function,-,_putc_r,int,"_reent*, int, FILE*" +Function,-,_putc_unlocked_r,int,"_reent*, int, FILE*" +Function,-,_putchar_r,int,"_reent*, int" +Function,-,_putchar_unlocked_r,int,"_reent*, int" +Function,-,_putenv_r,int,"_reent*, char*" +Function,-,_puts_r,int,"_reent*, const char*" +Function,-,_realloc_r,void*,"_reent*, void*, size_t" +Function,-,_reallocf_r,void*,"_reent*, void*, size_t" +Function,-,_reclaim_reent,void,_reent* +Function,-,_remove_r,int,"_reent*, const char*" +Function,-,_rename_r,int,"_reent*, const char*, const char*" +Function,-,_rewind_r,void,"_reent*, FILE*" +Function,-,_scanf_r,int,"_reent*, const char*, ..." +Function,-,_seed48_r,unsigned short*,"_reent*, unsigned short[3]" +Function,-,_setenv_r,int,"_reent*, const char*, const char*, int" +Function,-,_siprintf_r,int,"_reent*, char*, const char*, ..." +Function,-,_siscanf_r,int,"_reent*, const char*, const char*, ..." +Function,-,_sniprintf_r,int,"_reent*, char*, size_t, const char*, ..." +Function,-,_snprintf_r,int,"_reent*, char*, size_t, const char*, ..." +Function,-,_sprintf_r,int,"_reent*, char*, const char*, ..." +Function,-,_srand48_r,void,"_reent*, long" +Function,-,_sscanf_r,int,"_reent*, const char*, const char*, ..." +Function,-,_strdup_r,char*,"_reent*, const char*" +Function,-,_strerror_r,char*,"_reent*, int, int, int*" +Function,-,_strndup_r,char*,"_reent*, const char*, size_t" +Function,-,_strtod_r,double,"_reent*, const char*, char**" +Function,-,_strtol_r,long,"_reent*, const char*, char**, int" +Function,-,_strtold_r,long double,"_reent*, const char*, char**" +Function,-,_strtoll_r,long long,"_reent*, const char*, char**, int" +Function,-,_strtoul_r,unsigned long,"_reent*, const char*, char**, int" +Function,-,_strtoull_r,unsigned long long,"_reent*, const char*, char**, int" +Function,-,_system_r,int,"_reent*, const char*" +Function,-,_tempnam_r,char*,"_reent*, const char*, const char*" +Function,-,_tmpfile_r,FILE*,_reent* +Function,-,_tmpnam_r,char*,"_reent*, char*" +Function,-,_ungetc_r,int,"_reent*, int, FILE*" +Function,-,_unsetenv_r,int,"_reent*, const char*" +Function,-,_vasiprintf_r,int,"_reent*, char**, const char*, __gnuc_va_list" +Function,-,_vasniprintf_r,char*,"_reent*, char*, size_t*, const char*, __gnuc_va_list" +Function,-,_vasnprintf_r,char*,"_reent*, char*, size_t*, const char*, __gnuc_va_list" +Function,-,_vasprintf_r,int,"_reent*, char**, const char*, __gnuc_va_list" +Function,-,_vdiprintf_r,int,"_reent*, int, const char*, __gnuc_va_list" +Function,-,_vdprintf_r,int,"_reent*, int, const char*, __gnuc_va_list" +Function,-,_vfiprintf_r,int,"_reent*, FILE*, const char*, __gnuc_va_list" +Function,-,_vfiscanf_r,int,"_reent*, FILE*, const char*, __gnuc_va_list" +Function,-,_vfprintf_r,int,"_reent*, FILE*, const char*, __gnuc_va_list" +Function,-,_vfscanf_r,int,"_reent*, FILE*, const char*, __gnuc_va_list" +Function,-,_viprintf_r,int,"_reent*, const char*, __gnuc_va_list" +Function,-,_viscanf_r,int,"_reent*, const char*, __gnuc_va_list" +Function,-,_vprintf_r,int,"_reent*, const char*, __gnuc_va_list" +Function,-,_vscanf_r,int,"_reent*, const char*, __gnuc_va_list" +Function,-,_vsiprintf_r,int,"_reent*, char*, const char*, __gnuc_va_list" +Function,-,_vsiscanf_r,int,"_reent*, const char*, const char*, __gnuc_va_list" +Function,-,_vsniprintf_r,int,"_reent*, char*, size_t, const char*, __gnuc_va_list" +Function,-,_vsnprintf_r,int,"_reent*, char*, size_t, const char*, __gnuc_va_list" +Function,-,_vsprintf_r,int,"_reent*, char*, const char*, __gnuc_va_list" +Function,-,_vsscanf_r,int,"_reent*, const char*, const char*, __gnuc_va_list" +Function,-,_wcstombs_r,size_t,"_reent*, char*, const wchar_t*, size_t, _mbstate_t*" +Function,-,_wctomb_r,int,"_reent*, char*, wchar_t, _mbstate_t*" +Function,-,a64l,long,const char* +Function,+,abort,void, +Function,-,abs,int,int +Function,-,acos,double,double +Function,-,acosf,float,float +Function,-,acosh,double,double +Function,-,acoshf,float,float +Function,-,acoshl,long double,long double +Function,-,acosl,long double,long double +Function,-,aligned_alloc,void*,"size_t, size_t" +Function,+,aligned_free,void,void* +Function,+,aligned_malloc,void*,"size_t, size_t" +Function,-,arc4random,__uint32_t, +Function,-,arc4random_buf,void,"void*, size_t" +Function,-,arc4random_uniform,__uint32_t,__uint32_t +Function,+,args_char_to_hex,_Bool,"char, char, uint8_t*" +Function,+,args_get_first_word_length,size_t,FuriString* +Function,+,args_length,size_t,FuriString* +Function,+,args_read_hex_bytes,_Bool,"FuriString*, uint8_t*, size_t" +Function,+,args_read_int_and_trim,_Bool,"FuriString*, int*" +Function,+,args_read_probably_quoted_string_and_trim,_Bool,"FuriString*, FuriString*" +Function,+,args_read_string_and_trim,_Bool,"FuriString*, FuriString*" +Function,-,asin,double,double +Function,-,asinf,float,float +Function,-,asinh,double,double +Function,-,asinhf,float,float +Function,-,asinhl,long double,long double +Function,-,asinl,long double,long double +Function,-,asiprintf,int,"char**, const char*, ..." +Function,-,asniprintf,char*,"char*, size_t*, const char*, ..." +Function,-,asnprintf,char*,"char*, size_t*, const char*, ..." +Function,-,asprintf,int,"char**, const char*, ..." +Function,-,at_quick_exit,int,void (*)() +Function,-,atan,double,double +Function,-,atan2,double,"double, double" +Function,-,atan2f,float,"float, float" +Function,-,atan2l,long double,"long double, long double" +Function,-,atanf,float,float +Function,-,atanh,double,double +Function,-,atanhf,float,float +Function,-,atanhl,long double,long double +Function,-,atanl,long double,long double +Function,-,atexit,int,void (*)() +Function,-,atof,double,const char* +Function,-,atoff,float,const char* +Function,+,atoi,int,const char* +Function,-,atol,long,const char* +Function,-,atoll,long long,const char* +Function,-,basename,char*,const char* +Function,-,bcmp,int,"const void*, const void*, size_t" +Function,-,bcopy,void,"const void*, void*, size_t" +Function,+,bit_buffer_alloc,BitBuffer*,size_t +Function,+,bit_buffer_append,void,"BitBuffer*, const BitBuffer*" +Function,+,bit_buffer_append_bit,void,"BitBuffer*, _Bool" +Function,+,bit_buffer_append_byte,void,"BitBuffer*, uint8_t" +Function,+,bit_buffer_append_bytes,void,"BitBuffer*, const uint8_t*, size_t" +Function,+,bit_buffer_append_right,void,"BitBuffer*, const BitBuffer*, size_t" +Function,+,bit_buffer_copy,void,"BitBuffer*, const BitBuffer*" +Function,+,bit_buffer_copy_bits,void,"BitBuffer*, const uint8_t*, size_t" +Function,+,bit_buffer_copy_bytes,void,"BitBuffer*, const uint8_t*, size_t" +Function,+,bit_buffer_copy_bytes_with_parity,void,"BitBuffer*, const uint8_t*, size_t" +Function,+,bit_buffer_copy_left,void,"BitBuffer*, const BitBuffer*, size_t" +Function,+,bit_buffer_copy_right,void,"BitBuffer*, const BitBuffer*, size_t" +Function,+,bit_buffer_free,void,BitBuffer* +Function,+,bit_buffer_get_byte,uint8_t,"const BitBuffer*, size_t" +Function,+,bit_buffer_get_byte_from_bit,uint8_t,"const BitBuffer*, size_t" +Function,+,bit_buffer_get_capacity_bytes,size_t,const BitBuffer* +Function,+,bit_buffer_get_data,const uint8_t*,const BitBuffer* +Function,+,bit_buffer_get_parity,const uint8_t*,const BitBuffer* +Function,+,bit_buffer_get_size,size_t,const BitBuffer* +Function,+,bit_buffer_get_size_bytes,size_t,const BitBuffer* +Function,+,bit_buffer_has_partial_byte,_Bool,const BitBuffer* +Function,+,bit_buffer_reset,void,BitBuffer* +Function,+,bit_buffer_set_byte,void,"BitBuffer*, size_t, uint8_t" +Function,+,bit_buffer_set_byte_with_parity,void,"BitBuffer*, size_t, uint8_t, _Bool" +Function,+,bit_buffer_set_size,void,"BitBuffer*, size_t" +Function,+,bit_buffer_set_size_bytes,void,"BitBuffer*, size_t" +Function,+,bit_buffer_starts_with_byte,_Bool,"const BitBuffer*, uint8_t" +Function,+,bit_buffer_write_bytes,void,"const BitBuffer*, void*, size_t" +Function,+,bit_buffer_write_bytes_mid,void,"const BitBuffer*, void*, size_t, size_t" +Function,+,bit_buffer_write_bytes_with_parity,void,"const BitBuffer*, void*, size_t, size_t*" +Function,+,ble_app_get_key_storage_buff,void,"uint8_t**, uint16_t*" +Function,+,ble_app_init,_Bool, +Function,+,ble_app_thread_stop,void, +Function,+,ble_glue_force_c2_mode,BleGlueCommandResult,BleGlueC2Mode +Function,-,ble_glue_fus_get_status,BleGlueCommandResult, +Function,-,ble_glue_fus_stack_delete,BleGlueCommandResult, +Function,-,ble_glue_fus_stack_install,BleGlueCommandResult,"uint32_t, uint32_t" +Function,-,ble_glue_fus_wait_operation,BleGlueCommandResult, +Function,+,ble_glue_get_c2_info,const BleGlueC2Info*, +Function,-,ble_glue_get_c2_status,BleGlueStatus, +Function,+,ble_glue_init,void, +Function,+,ble_glue_is_alive,_Bool, +Function,+,ble_glue_is_radio_stack_ready,_Bool, +Function,+,ble_glue_reinit_c2,_Bool, +Function,+,ble_glue_set_key_storage_changed_callback,void,"BleGlueKeyStorageChangedCallback, void*" +Function,+,ble_glue_start,_Bool, +Function,+,ble_glue_thread_stop,void, +Function,+,ble_glue_wait_for_c2_start,_Bool,int32_t +Function,-,bsearch,void*,"const void*, const void*, size_t, size_t, __compar_fn_t" +Function,+,bt_disconnect,void,Bt* +Function,+,bt_forget_bonded_devices,void,Bt* +Function,+,bt_keys_storage_set_default_path,void,Bt* +Function,+,bt_keys_storage_set_storage_path,void,"Bt*, const char*" +Function,+,bt_set_profile,_Bool,"Bt*, BtProfile" +Function,+,bt_set_status_changed_callback,void,"Bt*, BtStatusChangedCallback, void*" +Function,+,buffered_file_stream_alloc,Stream*,Storage* +Function,+,buffered_file_stream_close,_Bool,Stream* +Function,+,buffered_file_stream_get_error,FS_Error,Stream* +Function,+,buffered_file_stream_open,_Bool,"Stream*, const char*, FS_AccessMode, FS_OpenMode" +Function,+,buffered_file_stream_sync,_Bool,Stream* +Function,+,button_menu_add_item,ButtonMenuItem*,"ButtonMenu*, const char*, int32_t, ButtonMenuItemCallback, ButtonMenuItemType, void*" +Function,+,button_menu_alloc,ButtonMenu*, +Function,+,button_menu_free,void,ButtonMenu* +Function,+,button_menu_get_view,View*,ButtonMenu* +Function,+,button_menu_reset,void,ButtonMenu* +Function,+,button_menu_set_header,void,"ButtonMenu*, const char*" +Function,+,button_menu_set_selected_item,void,"ButtonMenu*, uint32_t" +Function,+,button_panel_add_icon,void,"ButtonPanel*, uint16_t, uint16_t, const Icon*" +Function,+,button_panel_add_item,void,"ButtonPanel*, uint32_t, uint16_t, uint16_t, uint16_t, uint16_t, const Icon*, const Icon*, ButtonItemCallback, void*" +Function,+,button_panel_add_label,void,"ButtonPanel*, uint16_t, uint16_t, Font, const char*" +Function,+,button_panel_alloc,ButtonPanel*, +Function,+,button_panel_free,void,ButtonPanel* +Function,+,button_panel_get_view,View*,ButtonPanel* +Function,+,button_panel_reserve,void,"ButtonPanel*, size_t, size_t" +Function,+,button_panel_reset,void,ButtonPanel* +Function,+,byte_input_alloc,ByteInput*, +Function,+,byte_input_free,void,ByteInput* +Function,+,byte_input_get_view,View*,ByteInput* +Function,+,byte_input_set_header_text,void,"ByteInput*, const char*" +Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, ByteChangedCallback, void*, uint8_t*, uint8_t" +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,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" +Function,+,canvas_draw_disc,void,"Canvas*, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_dot,void,"Canvas*, uint8_t, uint8_t" +Function,+,canvas_draw_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_glyph,void,"Canvas*, uint8_t, uint8_t, uint16_t" +Function,+,canvas_draw_icon,void,"Canvas*, uint8_t, uint8_t, const Icon*" +Function,+,canvas_draw_icon_animation,void,"Canvas*, uint8_t, uint8_t, IconAnimation*" +Function,+,canvas_draw_icon_ex,void,"Canvas*, uint8_t, uint8_t, const Icon*, IconRotation" +Function,+,canvas_draw_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_rbox,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_rframe,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" +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,const CanvasFontParameters*,"const Canvas*, Font" +Function,+,canvas_glyph_width,uint8_t,"Canvas*, uint16_t" +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,const Canvas* +Function,-,cbrt,double,double +Function,-,cbrtf,float,float +Function,-,cbrtl,long double,long double +Function,-,ceil,double,double +Function,-,ceilf,float,float +Function,-,ceill,long double,long double +Function,-,cfree,void,void* +Function,-,clearerr,void,FILE* +Function,-,clearerr_unlocked,void,FILE* +Function,+,cli_add_command,void,"Cli*, const char*, CliCommandFlag, CliCallback, void*" +Function,+,cli_cmd_interrupt_received,_Bool,Cli* +Function,+,cli_delete_command,void,"Cli*, const char*" +Function,+,cli_getc,char,Cli* +Function,+,cli_is_connected,_Bool,Cli* +Function,+,cli_nl,void, +Function,+,cli_print_usage,void,"const char*, const char*, const char*" +Function,+,cli_read,size_t,"Cli*, uint8_t*, size_t" +Function,+,cli_read_timeout,size_t,"Cli*, uint8_t*, size_t, uint32_t" +Function,+,cli_session_close,void,Cli* +Function,+,cli_session_open,void,"Cli*, void*" +Function,+,cli_write,void,"Cli*, const uint8_t*, size_t" +Function,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiInterface*" +Function,+,composite_api_resolver_alloc,CompositeApiResolver*, +Function,+,composite_api_resolver_free,void,CompositeApiResolver* +Function,+,composite_api_resolver_get,const ElfApiInterface*,CompositeApiResolver* +Function,+,compress_alloc,Compress*,uint16_t +Function,+,compress_decode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,+,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,+,compress_free,void,Compress* +Function,+,compress_icon_alloc,CompressIcon*, +Function,+,compress_icon_decode,void,"CompressIcon*, const uint8_t*, uint8_t**" +Function,+,compress_icon_free,void,CompressIcon* +Function,-,copysign,double,"double, double" +Function,-,copysignf,float,"float, float" +Function,-,copysignl,long double,"long double, long double" +Function,-,cos,double,double +Function,-,cosf,float,float +Function,-,cosh,double,double +Function,-,coshf,float,float +Function,-,coshl,long double,long double +Function,-,cosl,long double,long double +Function,+,crc32_calc_buffer,uint32_t,"uint32_t, const void*, size_t" +Function,+,crc32_calc_file,uint32_t,"File*, const FileCrcProgressCb, void*" +Function,-,ctermid,char*,char* +Function,-,cuserid,char*,char* +Function,+,dialog_ex_alloc,DialogEx*, +Function,+,dialog_ex_disable_extended_events,void,DialogEx* +Function,+,dialog_ex_enable_extended_events,void,DialogEx* +Function,+,dialog_ex_free,void,DialogEx* +Function,+,dialog_ex_get_view,View*,DialogEx* +Function,+,dialog_ex_reset,void,DialogEx* +Function,+,dialog_ex_set_center_button_text,void,"DialogEx*, const char*" +Function,+,dialog_ex_set_context,void,"DialogEx*, void*" +Function,+,dialog_ex_set_header,void,"DialogEx*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,dialog_ex_set_icon,void,"DialogEx*, uint8_t, uint8_t, const Icon*" +Function,+,dialog_ex_set_left_button_text,void,"DialogEx*, const char*" +Function,+,dialog_ex_set_result_callback,void,"DialogEx*, DialogExResultCallback" +Function,+,dialog_ex_set_right_button_text,void,"DialogEx*, const char*" +Function,+,dialog_ex_set_text,void,"DialogEx*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,dialog_file_browser_set_basic_options,void,"DialogsFileBrowserOptions*, const char*, const Icon*" +Function,+,dialog_file_browser_show,_Bool,"DialogsApp*, FuriString*, FuriString*, const DialogsFileBrowserOptions*" +Function,+,dialog_message_alloc,DialogMessage*, +Function,+,dialog_message_free,void,DialogMessage* +Function,+,dialog_message_set_buttons,void,"DialogMessage*, const char*, const char*, const char*" +Function,+,dialog_message_set_header,void,"DialogMessage*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,dialog_message_set_icon,void,"DialogMessage*, const Icon*, uint8_t, uint8_t" +Function,+,dialog_message_set_text,void,"DialogMessage*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,dialog_message_show,DialogMessageButton,"DialogsApp*, const DialogMessage*" +Function,+,dialog_message_show_storage_error,void,"DialogsApp*, const char*" +Function,+,digital_sequence_add_signal,void,"DigitalSequence*, uint8_t" +Function,-,digital_sequence_alloc,DigitalSequence*,"uint32_t, const GpioPin*" +Function,-,digital_sequence_clear,void,DigitalSequence* +Function,-,digital_sequence_free,void,DigitalSequence* +Function,+,digital_sequence_register_signal,void,"DigitalSequence*, uint8_t, const DigitalSignal*" +Function,+,digital_sequence_transmit,void,DigitalSequence* +Function,+,digital_signal_add_period,void,"DigitalSignal*, uint32_t" +Function,+,digital_signal_add_period_with_level,void,"DigitalSignal*, uint32_t, _Bool" +Function,-,digital_signal_alloc,DigitalSignal*,uint32_t +Function,-,digital_signal_free,void,DigitalSignal* +Function,+,digital_signal_get_size,uint32_t,const DigitalSignal* +Function,+,digital_signal_get_start_level,_Bool,const DigitalSignal* +Function,+,digital_signal_set_start_level,void,"DigitalSignal*, _Bool" +Function,-,diprintf,int,"int, const char*, ..." +Function,+,dir_walk_alloc,DirWalk*,Storage* +Function,+,dir_walk_close,void,DirWalk* +Function,+,dir_walk_free,void,DirWalk* +Function,+,dir_walk_get_error,FS_Error,DirWalk* +Function,+,dir_walk_open,_Bool,"DirWalk*, const char*" +Function,+,dir_walk_read,DirWalkResult,"DirWalk*, FuriString*, FileInfo*" +Function,+,dir_walk_set_filter_cb,void,"DirWalk*, DirWalkFilterCb, void*" +Function,+,dir_walk_set_recursive,void,"DirWalk*, _Bool" +Function,-,div,div_t,"int, int" +Function,+,dolphin_deed,void,DolphinDeed +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_pubsub,FuriPubSub*,Dolphin* +Function,+,dolphin_stats,DolphinStats,Dolphin* +Function,+,dolphin_upgrade_level,void,Dolphin* +Function,-,dprintf,int,"int, const char*, ..." +Function,-,drand48,double, +Function,-,drem,double,"double, double" +Function,-,dremf,float,"float, float" +Function,+,elements_bold_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_bubble,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_bubble_str,void,"Canvas*, uint8_t, uint8_t, const char*, Align, Align" +Function,+,elements_button_center,void,"Canvas*, const char*" +Function,+,elements_button_left,void,"Canvas*, const char*" +Function,+,elements_button_right,void,"Canvas*, const char*" +Function,+,elements_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_multiline_text,void,"Canvas*, uint8_t, uint8_t, const char*" +Function,+,elements_multiline_text_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*" +Function,+,elements_multiline_text_framed,void,"Canvas*, uint8_t, uint8_t, const char*" +Function,+,elements_progress_bar,void,"Canvas*, uint8_t, uint8_t, uint8_t, float" +Function,+,elements_progress_bar_with_text,void,"Canvas*, uint8_t, uint8_t, uint8_t, float, const char*" +Function,+,elements_scrollable_text_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, FuriString*, size_t, _Bool" +Function,+,elements_scrollbar,void,"Canvas*, uint16_t, uint16_t" +Function,+,elements_scrollbar_pos,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint16_t, uint16_t" +Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t" +Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" +Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, uint32_t, Elf32_Addr*" +Function,+,elf_symbolname_hash,uint32_t,const char* +Function,+,empty_screen_alloc,EmptyScreen*, +Function,+,empty_screen_free,void,EmptyScreen* +Function,+,empty_screen_get_view,View*,EmptyScreen* +Function,-,erand48,double,unsigned short[3] +Function,-,erf,double,double +Function,-,erfc,double,double +Function,-,erfcf,float,float +Function,-,erfcl,long double,long double +Function,-,erff,float,float +Function,-,erfl,long double,long double +Function,-,exit,void,int +Function,-,exp,double,double +Function,-,exp10,double,double +Function,-,exp10f,float,float +Function,-,exp2,double,double +Function,-,exp2f,float,float +Function,-,exp2l,long double,long double +Function,-,expf,float,float +Function,-,expl,long double,long double +Function,-,explicit_bzero,void,"void*, size_t" +Function,-,expm1,double,double +Function,-,expm1f,float,float +Function,-,expm1l,long double,long double +Function,-,fabs,double,double +Function,-,fabsf,float,float +Function,-,fabsl,long double,long double +Function,-,fclose,int,FILE* +Function,-,fcloseall,int, +Function,-,fdim,double,"double, double" +Function,-,fdimf,float,"float, float" +Function,-,fdiml,long double,"long double, long double" +Function,-,fdopen,FILE*,"int, const char*" +Function,-,feof,int,FILE* +Function,-,feof_unlocked,int,FILE* +Function,-,ferror,int,FILE* +Function,-,ferror_unlocked,int,FILE* +Function,-,fflush,int,FILE* +Function,-,fflush_unlocked,int,FILE* +Function,-,ffs,int,int +Function,-,ffsl,int,long +Function,-,ffsll,int,long long +Function,-,fgetc,int,FILE* +Function,-,fgetc_unlocked,int,FILE* +Function,-,fgetpos,int,"FILE*, fpos_t*" +Function,-,fgets,char*,"char*, int, FILE*" +Function,-,fgets_unlocked,char*,"char*, int, FILE*" +Function,+,file_browser_alloc,FileBrowser*,FuriString* +Function,+,file_browser_configure,void,"FileBrowser*, const char*, const char*, _Bool, _Bool, const Icon*, _Bool" +Function,+,file_browser_free,void,FileBrowser* +Function,+,file_browser_get_view,View*,FileBrowser* +Function,+,file_browser_set_callback,void,"FileBrowser*, FileBrowserCallback, void*" +Function,+,file_browser_set_item_callback,void,"FileBrowser*, FileBrowserLoadItemCallback, void*" +Function,+,file_browser_start,void,"FileBrowser*, FuriString*" +Function,+,file_browser_stop,void,FileBrowser* +Function,+,file_browser_worker_alloc,BrowserWorker*,"FuriString*, const char*, const char*, _Bool, _Bool" +Function,+,file_browser_worker_folder_enter,void,"BrowserWorker*, FuriString*, int32_t" +Function,+,file_browser_worker_folder_exit,void,BrowserWorker* +Function,+,file_browser_worker_folder_refresh,void,"BrowserWorker*, int32_t" +Function,+,file_browser_worker_free,void,BrowserWorker* +Function,+,file_browser_worker_is_in_start_folder,_Bool,BrowserWorker* +Function,+,file_browser_worker_load,void,"BrowserWorker*, uint32_t, uint32_t" +Function,+,file_browser_worker_set_callback_context,void,"BrowserWorker*, void*" +Function,+,file_browser_worker_set_config,void,"BrowserWorker*, FuriString*, const char*, _Bool, _Bool" +Function,+,file_browser_worker_set_folder_callback,void,"BrowserWorker*, BrowserWorkerFolderOpenCallback" +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* +Function,+,file_stream_open,_Bool,"Stream*, const char*, FS_AccessMode, FS_OpenMode" +Function,-,fileno,int,FILE* +Function,-,fileno_unlocked,int,FILE* +Function,+,filesystem_api_error_get_desc,const char*,FS_Error +Function,-,finite,int,double +Function,-,finitef,int,float +Function,-,finitel,int,long double +Function,-,fiprintf,int,"FILE*, const char*, ..." +Function,-,fiscanf,int,"FILE*, const char*, ..." +Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*" +Function,+,flipper_application_alloc_thread,FuriThread*,"FlipperApplication*, const char*" +Function,+,flipper_application_free,void,FlipperApplication* +Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication* +Function,+,flipper_application_is_plugin,_Bool,FlipperApplication* +Function,+,flipper_application_load_name_and_icon,_Bool,"FuriString*, Storage*, uint8_t**, FuriString*" +Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus +Function,+,flipper_application_manifest_is_target_compatible,_Bool,const FlipperApplicationManifest* +Function,+,flipper_application_manifest_is_too_new,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*" +Function,+,flipper_application_manifest_is_too_old,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*" +Function,+,flipper_application_manifest_is_valid,_Bool,const FlipperApplicationManifest* +Function,+,flipper_application_map_to_memory,FlipperApplicationLoadStatus,FlipperApplication* +Function,+,flipper_application_plugin_get_descriptor,const FlipperAppPluginDescriptor*,FlipperApplication* +Function,+,flipper_application_preload,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" +Function,+,flipper_application_preload_manifest,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" +Function,+,flipper_application_preload_status_to_string,const char*,FlipperApplicationPreloadStatus +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* +Function,+,flipper_format_file_close,_Bool,FlipperFormat* +Function,+,flipper_format_file_open_always,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_file_open_append,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_file_open_existing,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_file_open_new,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_free,void,FlipperFormat* +Function,+,flipper_format_get_raw_stream,Stream*,FlipperFormat* +Function,+,flipper_format_get_value_count,_Bool,"FlipperFormat*, const char*, uint32_t*" +Function,+,flipper_format_insert_or_update_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t" +Function,+,flipper_format_insert_or_update_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t" +Function,+,flipper_format_insert_or_update_hex,_Bool,"FlipperFormat*, const char*, const uint8_t*, const uint16_t" +Function,+,flipper_format_insert_or_update_int32,_Bool,"FlipperFormat*, const char*, const int32_t*, const uint16_t" +Function,+,flipper_format_insert_or_update_string,_Bool,"FlipperFormat*, const char*, FuriString*" +Function,+,flipper_format_insert_or_update_string_cstr,_Bool,"FlipperFormat*, const char*, const char*" +Function,+,flipper_format_insert_or_update_uint32,_Bool,"FlipperFormat*, const char*, const uint32_t*, const uint16_t" +Function,+,flipper_format_key_exist,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_read_bool,_Bool,"FlipperFormat*, const char*, _Bool*, const uint16_t" +Function,+,flipper_format_read_float,_Bool,"FlipperFormat*, const char*, float*, const uint16_t" +Function,+,flipper_format_read_header,_Bool,"FlipperFormat*, FuriString*, uint32_t*" +Function,+,flipper_format_read_hex,_Bool,"FlipperFormat*, const char*, uint8_t*, const uint16_t" +Function,+,flipper_format_read_hex_uint64,_Bool,"FlipperFormat*, const char*, uint64_t*, const uint16_t" +Function,+,flipper_format_read_int32,_Bool,"FlipperFormat*, const char*, int32_t*, const uint16_t" +Function,+,flipper_format_read_string,_Bool,"FlipperFormat*, const char*, FuriString*" +Function,+,flipper_format_read_uint32,_Bool,"FlipperFormat*, const char*, uint32_t*, const uint16_t" +Function,+,flipper_format_rewind,_Bool,FlipperFormat* +Function,+,flipper_format_seek_to_end,_Bool,FlipperFormat* +Function,+,flipper_format_set_strict_mode,void,"FlipperFormat*, _Bool" +Function,+,flipper_format_stream_delete_key_and_write,_Bool,"Stream*, FlipperStreamWriteData*, _Bool" +Function,+,flipper_format_stream_get_value_count,_Bool,"Stream*, const char*, uint32_t*, _Bool" +Function,+,flipper_format_stream_read_value_line,_Bool,"Stream*, const char*, FlipperStreamValue, void*, size_t, _Bool" +Function,+,flipper_format_stream_write_comment_cstr,_Bool,"Stream*, const char*" +Function,+,flipper_format_stream_write_value_line,_Bool,"Stream*, FlipperStreamWriteData*" +Function,+,flipper_format_string_alloc,FlipperFormat*, +Function,+,flipper_format_update_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t" +Function,+,flipper_format_update_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t" +Function,+,flipper_format_update_hex,_Bool,"FlipperFormat*, const char*, const uint8_t*, const uint16_t" +Function,+,flipper_format_update_int32,_Bool,"FlipperFormat*, const char*, const int32_t*, const uint16_t" +Function,+,flipper_format_update_string,_Bool,"FlipperFormat*, const char*, FuriString*" +Function,+,flipper_format_update_string_cstr,_Bool,"FlipperFormat*, const char*, const char*" +Function,+,flipper_format_update_uint32,_Bool,"FlipperFormat*, const char*, const uint32_t*, const uint16_t" +Function,+,flipper_format_write_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t" +Function,+,flipper_format_write_comment,_Bool,"FlipperFormat*, FuriString*" +Function,+,flipper_format_write_comment_cstr,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_write_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t" +Function,+,flipper_format_write_header,_Bool,"FlipperFormat*, FuriString*, const uint32_t" +Function,+,flipper_format_write_header_cstr,_Bool,"FlipperFormat*, const char*, const uint32_t" +Function,+,flipper_format_write_hex,_Bool,"FlipperFormat*, const char*, const uint8_t*, const uint16_t" +Function,+,flipper_format_write_hex_uint64,_Bool,"FlipperFormat*, const char*, const uint64_t*, const uint16_t" +Function,+,flipper_format_write_int32,_Bool,"FlipperFormat*, const char*, const int32_t*, const uint16_t" +Function,+,flipper_format_write_string,_Bool,"FlipperFormat*, const char*, FuriString*" +Function,+,flipper_format_write_string_cstr,_Bool,"FlipperFormat*, const char*, const char*" +Function,+,flipper_format_write_uint32,_Bool,"FlipperFormat*, const char*, const uint32_t*, const uint16_t" +Function,+,float_is_equal,_Bool,"float, float" +Function,-,flockfile,void,FILE* +Function,-,floor,double,double +Function,-,floorf,float,float +Function,-,floorl,long double,long double +Function,-,fls,int,int +Function,-,flsl,int,long +Function,-,flsll,int,long long +Function,-,fma,double,"double, double, double" +Function,-,fmaf,float,"float, float, float" +Function,-,fmal,long double,"long double, long double, long double" +Function,-,fmax,double,"double, double" +Function,-,fmaxf,float,"float, float" +Function,-,fmaxl,long double,"long double, long double" +Function,-,fmemopen,FILE*,"void*, size_t, const char*" +Function,-,fmin,double,"double, double" +Function,-,fminf,float,"float, float" +Function,-,fminl,long double,"long double, long double" +Function,-,fmod,double,"double, double" +Function,-,fmodf,float,"float, float" +Function,-,fmodl,long double,"long double, long double" +Function,-,fopen,FILE*,"const char*, const char*" +Function,-,fopencookie,FILE*,"void*, const char*, cookie_io_functions_t" +Function,-,fprintf,int,"FILE*, const char*, ..." +Function,-,fpurge,int,FILE* +Function,-,fputc,int,"int, FILE*" +Function,-,fputc_unlocked,int,"int, FILE*" +Function,-,fputs,int,"const char*, FILE*" +Function,-,fputs_unlocked,int,"const char*, FILE*" +Function,-,fread,size_t,"void*, size_t, size_t, FILE*" +Function,-,fread_unlocked,size_t,"void*, size_t, size_t, FILE*" +Function,+,free,void,void* +Function,-,freopen,FILE*,"const char*, const char*, FILE*" +Function,-,frexp,double,"double, int*" +Function,-,frexpf,float,"float, int*" +Function,-,frexpl,long double,"long double, int*" +Function,-,fscanf,int,"FILE*, const char*, ..." +Function,-,fseek,int,"FILE*, long, int" +Function,-,fseeko,int,"FILE*, off_t, int" +Function,-,fsetpos,int,"FILE*, const fpos_t*" +Function,-,ftell,long,FILE* +Function,-,ftello,off_t,FILE* +Function,-,ftrylockfile,int,FILE* +Function,-,funlockfile,void,FILE* +Function,-,funopen,FILE*,"const void*, int (*)(void*, char*, int), int (*)(void*, const char*, int), fpos_t (*)(void*, fpos_t, int), int (*)(void*)" +Function,+,furi_delay_ms,void,uint32_t +Function,+,furi_delay_tick,void,uint32_t +Function,+,furi_delay_until_tick,FuriStatus,uint32_t +Function,+,furi_delay_us,void,uint32_t +Function,+,furi_event_flag_alloc,FuriEventFlag*, +Function,+,furi_event_flag_clear,uint32_t,"FuriEventFlag*, uint32_t" +Function,+,furi_event_flag_free,void,FuriEventFlag* +Function,+,furi_event_flag_get,uint32_t,FuriEventFlag* +Function,+,furi_event_flag_set,uint32_t,"FuriEventFlag*, uint32_t" +Function,+,furi_event_flag_wait,uint32_t,"FuriEventFlag*, uint32_t, uint32_t, uint32_t" +Function,+,furi_get_tick,uint32_t, +Function,+,furi_hal_bt_change_app,_Bool,"FuriHalBtProfile, GapEventCallback, void*" +Function,+,furi_hal_bt_clear_white_list,_Bool, +Function,+,furi_hal_bt_dump_state,void,FuriString* +Function,+,furi_hal_bt_ensure_c2_mode,_Bool,BleGlueC2Mode +Function,-,furi_hal_bt_get_hardfault_info,const FuriHalBtHardfaultInfo*, +Function,+,furi_hal_bt_get_key_storage_buff,void,"uint8_t**, uint16_t*" +Function,+,furi_hal_bt_get_radio_stack,FuriHalBtStack, +Function,+,furi_hal_bt_get_rssi,float, +Function,+,furi_hal_bt_get_transmitted_packets,uint32_t, +Function,+,furi_hal_bt_hid_consumer_key_press,_Bool,uint16_t +Function,+,furi_hal_bt_hid_consumer_key_release,_Bool,uint16_t +Function,+,furi_hal_bt_hid_consumer_key_release_all,_Bool, +Function,+,furi_hal_bt_hid_kb_press,_Bool,uint16_t +Function,+,furi_hal_bt_hid_kb_release,_Bool,uint16_t +Function,+,furi_hal_bt_hid_kb_release_all,_Bool, +Function,+,furi_hal_bt_hid_mouse_move,_Bool,"int8_t, int8_t" +Function,+,furi_hal_bt_hid_mouse_press,_Bool,uint8_t +Function,+,furi_hal_bt_hid_mouse_release,_Bool,uint8_t +Function,+,furi_hal_bt_hid_mouse_release_all,_Bool, +Function,+,furi_hal_bt_hid_mouse_scroll,_Bool,int8_t +Function,+,furi_hal_bt_hid_start,void, +Function,+,furi_hal_bt_hid_stop,void, +Function,-,furi_hal_bt_init,void, +Function,+,furi_hal_bt_is_active,_Bool, +Function,+,furi_hal_bt_is_alive,_Bool, +Function,+,furi_hal_bt_is_ble_gatt_gap_supported,_Bool, +Function,+,furi_hal_bt_is_testing_supported,_Bool, +Function,+,furi_hal_bt_lock_core2,void, +Function,+,furi_hal_bt_nvm_sram_sem_acquire,void, +Function,+,furi_hal_bt_nvm_sram_sem_release,void, +Function,+,furi_hal_bt_reinit,void, +Function,+,furi_hal_bt_serial_notify_buffer_is_empty,void, +Function,+,furi_hal_bt_serial_set_event_callback,void,"uint16_t, FuriHalBtSerialCallback, void*" +Function,+,furi_hal_bt_serial_set_rpc_status,void,FuriHalBtSerialRpcStatus +Function,+,furi_hal_bt_serial_start,void, +Function,+,furi_hal_bt_serial_stop,void, +Function,+,furi_hal_bt_serial_tx,_Bool,"uint8_t*, uint16_t" +Function,+,furi_hal_bt_set_key_storage_change_callback,void,"BleGlueKeyStorageChangedCallback, void*" +Function,+,furi_hal_bt_start_advertising,void, +Function,+,furi_hal_bt_start_app,_Bool,"FuriHalBtProfile, GapEventCallback, void*" +Function,+,furi_hal_bt_start_packet_rx,void,"uint8_t, uint8_t" +Function,+,furi_hal_bt_start_packet_tx,void,"uint8_t, uint8_t, uint8_t" +Function,+,furi_hal_bt_start_radio_stack,_Bool, +Function,+,furi_hal_bt_start_rx,void,uint8_t +Function,+,furi_hal_bt_start_tone_tx,void,"uint8_t, uint8_t" +Function,+,furi_hal_bt_stop_advertising,void, +Function,+,furi_hal_bt_stop_packet_test,uint16_t, +Function,+,furi_hal_bt_stop_rx,void, +Function,+,furi_hal_bt_stop_tone_tx,void, +Function,+,furi_hal_bt_unlock_core2,void, +Function,+,furi_hal_bt_update_battery_level,void,uint8_t +Function,+,furi_hal_bt_update_power_state,void, +Function,+,furi_hal_bus_deinit_early,void, +Function,+,furi_hal_bus_disable,void,FuriHalBus +Function,+,furi_hal_bus_enable,void,FuriHalBus +Function,+,furi_hal_bus_init_early,void, +Function,+,furi_hal_bus_is_enabled,_Bool,FuriHalBus +Function,+,furi_hal_bus_reset,void,FuriHalBus +Function,+,furi_hal_ccid_ccid_insert_smartcard,void, +Function,+,furi_hal_ccid_ccid_remove_smartcard,void, +Function,+,furi_hal_ccid_set_callbacks,void,CcidCallbacks* +Function,+,furi_hal_cdc_get_ctrl_line_state,uint8_t,uint8_t +Function,+,furi_hal_cdc_get_port_settings,usb_cdc_line_coding*,uint8_t +Function,+,furi_hal_cdc_receive,int32_t,"uint8_t, uint8_t*, uint16_t" +Function,+,furi_hal_cdc_send,void,"uint8_t, uint8_t*, uint16_t" +Function,+,furi_hal_cdc_set_callbacks,void,"uint8_t, CdcCallbacks*, void*" +Function,-,furi_hal_clock_deinit_early,void, +Function,-,furi_hal_clock_init,void, +Function,-,furi_hal_clock_init_early,void, +Function,+,furi_hal_clock_mco_disable,void, +Function,+,furi_hal_clock_mco_enable,void,"FuriHalClockMcoSourceId, FuriHalClockMcoDivisorId" +Function,-,furi_hal_clock_resume_tick,void, +Function,-,furi_hal_clock_suspend_tick,void, +Function,-,furi_hal_clock_switch_hse2hsi,void, +Function,-,furi_hal_clock_switch_hse2pll,_Bool, +Function,-,furi_hal_clock_switch_hsi2hse,void, +Function,-,furi_hal_clock_switch_pll2hse,_Bool, +Function,+,furi_hal_console_disable,void, +Function,+,furi_hal_console_enable,void, +Function,+,furi_hal_console_init,void, +Function,+,furi_hal_console_printf,void,"const char[], ..." +Function,+,furi_hal_console_puts,void,const char* +Function,+,furi_hal_console_set_tx_callback,void,"FuriHalConsoleTxCallback, void*" +Function,+,furi_hal_console_tx,void,"const uint8_t*, size_t" +Function,+,furi_hal_console_tx_with_new_line,void,"const uint8_t*, size_t" +Function,+,furi_hal_cortex_comp_enable,void,"FuriHalCortexComp, FuriHalCortexCompFunction, uint32_t, uint32_t, FuriHalCortexCompSize" +Function,+,furi_hal_cortex_comp_reset,void,FuriHalCortexComp +Function,+,furi_hal_cortex_delay_us,void,uint32_t +Function,-,furi_hal_cortex_init_early,void, +Function,+,furi_hal_cortex_instructions_per_microsecond,uint32_t, +Function,+,furi_hal_cortex_timer_get,FuriHalCortexTimer,uint32_t +Function,+,furi_hal_cortex_timer_is_expired,_Bool,FuriHalCortexTimer +Function,+,furi_hal_cortex_timer_wait,void,FuriHalCortexTimer +Function,+,furi_hal_crypto_ctr,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, uint8_t*, size_t" +Function,+,furi_hal_crypto_decrypt,_Bool,"const uint8_t*, uint8_t*, size_t" +Function,+,furi_hal_crypto_enclave_ensure_key,_Bool,uint8_t +Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*" +Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*" +Function,+,furi_hal_crypto_enclave_unload_key,_Bool,uint8_t +Function,+,furi_hal_crypto_enclave_verify,_Bool,"uint8_t*, uint8_t*" +Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t" +Function,+,furi_hal_crypto_gcm,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*, _Bool" +Function,+,furi_hal_crypto_gcm_decrypt_and_verify,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, const uint8_t*" +Function,+,furi_hal_crypto_gcm_encrypt_and_tag,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*" +Function,-,furi_hal_crypto_init,void, +Function,+,furi_hal_crypto_load_key,_Bool,"const uint8_t*, const uint8_t*" +Function,+,furi_hal_crypto_unload_key,_Bool, +Function,+,furi_hal_debug_disable,void, +Function,+,furi_hal_debug_enable,void, +Function,+,furi_hal_debug_is_gdb_session_active,_Bool, +Function,-,furi_hal_deinit_early,void, +Function,+,furi_hal_dma_deinit_early,void, +Function,+,furi_hal_dma_init_early,void, +Function,-,furi_hal_flash_erase,void,uint8_t +Function,-,furi_hal_flash_get_base,size_t, +Function,-,furi_hal_flash_get_cycles_count,size_t, +Function,-,furi_hal_flash_get_free_end_address,const void*, +Function,-,furi_hal_flash_get_free_page_count,size_t, +Function,-,furi_hal_flash_get_free_page_start_address,size_t, +Function,-,furi_hal_flash_get_free_start_address,const void*, +Function,-,furi_hal_flash_get_page_number,int16_t,size_t +Function,-,furi_hal_flash_get_page_size,size_t, +Function,-,furi_hal_flash_get_read_block_size,size_t, +Function,-,furi_hal_flash_get_write_block_size,size_t, +Function,-,furi_hal_flash_init,void, +Function,-,furi_hal_flash_ob_apply,void, +Function,-,furi_hal_flash_ob_get_raw_ptr,const FuriHalFlashRawOptionByteData*, +Function,-,furi_hal_flash_ob_set_word,_Bool,"size_t, const uint32_t" +Function,-,furi_hal_flash_program_page,void,"const uint8_t, const uint8_t*, uint16_t" +Function,-,furi_hal_flash_write_dword,void,"size_t, uint64_t" +Function,+,furi_hal_gpio_add_int_callback,void,"const GpioPin*, GpioExtiCallback, void*" +Function,+,furi_hal_gpio_disable_int_callback,void,const GpioPin* +Function,+,furi_hal_gpio_enable_int_callback,void,const GpioPin* +Function,+,furi_hal_gpio_init,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed" +Function,+,furi_hal_gpio_init_ex,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed, const GpioAltFn" +Function,+,furi_hal_gpio_init_simple,void,"const GpioPin*, const GpioMode" +Function,+,furi_hal_gpio_remove_int_callback,void,const GpioPin* +Function,+,furi_hal_hid_consumer_key_press,_Bool,uint16_t +Function,+,furi_hal_hid_consumer_key_release,_Bool,uint16_t +Function,+,furi_hal_hid_get_led_state,uint8_t, +Function,+,furi_hal_hid_is_connected,_Bool, +Function,+,furi_hal_hid_kb_press,_Bool,uint16_t +Function,+,furi_hal_hid_kb_release,_Bool,uint16_t +Function,+,furi_hal_hid_kb_release_all,_Bool, +Function,+,furi_hal_hid_mouse_move,_Bool,"int8_t, int8_t" +Function,+,furi_hal_hid_mouse_press,_Bool,uint8_t +Function,+,furi_hal_hid_mouse_release,_Bool,uint8_t +Function,+,furi_hal_hid_mouse_scroll,_Bool,int8_t +Function,+,furi_hal_hid_set_state_callback,void,"HidStateCallback, void*" +Function,+,furi_hal_hid_u2f_get_request,uint32_t,uint8_t* +Function,+,furi_hal_hid_u2f_is_connected,_Bool, +Function,+,furi_hal_hid_u2f_send_response,void,"uint8_t*, uint8_t" +Function,+,furi_hal_hid_u2f_set_callback,void,"HidU2fCallback, void*" +Function,+,furi_hal_i2c_acquire,void,FuriHalI2cBusHandle* +Function,-,furi_hal_i2c_deinit_early,void, +Function,-,furi_hal_i2c_init,void, +Function,-,furi_hal_i2c_init_early,void, +Function,+,furi_hal_i2c_is_device_ready,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint32_t" +Function,+,furi_hal_i2c_read_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_read_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t*, uint32_t" +Function,+,furi_hal_i2c_read_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint32_t" +Function,+,furi_hal_i2c_release,void,FuriHalI2cBusHandle* +Function,+,furi_hal_i2c_rx,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_rx_ext,_Bool,"FuriHalI2cBusHandle*, uint16_t, _Bool, uint8_t*, size_t, FuriHalI2cBegin, FuriHalI2cEnd, uint32_t" +Function,+,furi_hal_i2c_trx,_Bool,"FuriHalI2cBusHandle*, uint8_t, const uint8_t*, size_t, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_tx,_Bool,"FuriHalI2cBusHandle*, uint8_t, const uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_tx_ext,_Bool,"FuriHalI2cBusHandle*, uint16_t, _Bool, const uint8_t*, size_t, FuriHalI2cBegin, FuriHalI2cEnd, uint32_t" +Function,+,furi_hal_i2c_write_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, const uint8_t*, size_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_info_get,void,"PropertyValueCallback, char, void*" +Function,+,furi_hal_info_get_api_version,void,"uint16_t*, uint16_t*" +Function,-,furi_hal_init,void, +Function,-,furi_hal_init_early,void, +Function,-,furi_hal_interrupt_init,void, +Function,+,furi_hal_interrupt_set_isr,void,"FuriHalInterruptId, FuriHalInterruptISR, void*" +Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, uint16_t, FuriHalInterruptISR, void*" +Function,+,furi_hal_light_blink_set_color,void,Light +Function,+,furi_hal_light_blink_start,void,"Light, uint8_t, uint16_t, uint16_t" +Function,+,furi_hal_light_blink_stop,void, +Function,-,furi_hal_light_init,void, +Function,+,furi_hal_light_sequence,void,const char* +Function,+,furi_hal_light_set,void,"Light, uint8_t" +Function,+,furi_hal_memory_alloc,void*,size_t +Function,+,furi_hal_memory_get_free,size_t, +Function,+,furi_hal_memory_init,void, +Function,+,furi_hal_memory_max_pool_block,size_t, +Function,+,furi_hal_mpu_disable,void, +Function,+,furi_hal_mpu_enable,void, +Function,-,furi_hal_mpu_init,void, +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_os_init,void, +Function,+,furi_hal_os_tick,void, +Function,+,furi_hal_power_check_otg_fault,_Bool, +Function,+,furi_hal_power_check_otg_status,void, +Function,+,furi_hal_power_debug_get,void,"PropertyValueCallback, void*" +Function,+,furi_hal_power_disable_external_3_3v,void, +Function,+,furi_hal_power_disable_otg,void, +Function,+,furi_hal_power_enable_external_3_3v,void, +Function,+,furi_hal_power_enable_otg,_Bool, +Function,+,furi_hal_power_gauge_is_ok,_Bool, +Function,+,furi_hal_power_get_bat_health_pct,uint8_t, +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, +Function,+,furi_hal_power_get_battery_remaining_capacity,uint32_t, +Function,+,furi_hal_power_get_battery_temperature,float,FuriHalPowerIC +Function,+,furi_hal_power_get_battery_voltage,float,FuriHalPowerIC +Function,+,furi_hal_power_get_pct,uint8_t, +Function,+,furi_hal_power_get_usb_voltage,float, +Function,+,furi_hal_power_info_get,void,"PropertyValueCallback, char, void*" +Function,-,furi_hal_power_init,void, +Function,+,furi_hal_power_insomnia_enter,void, +Function,+,furi_hal_power_insomnia_exit,void, +Function,-,furi_hal_power_insomnia_level,uint16_t, +Function,+,furi_hal_power_is_charging,_Bool, +Function,+,furi_hal_power_is_charging_done,_Bool, +Function,+,furi_hal_power_is_otg_enabled,_Bool, +Function,+,furi_hal_power_is_shutdown_requested,_Bool, +Function,+,furi_hal_power_off,void, +Function,+,furi_hal_power_reset,void, +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, +Function,+,furi_hal_power_suppress_charge_enter,void, +Function,+,furi_hal_power_suppress_charge_exit,void, +Function,+,furi_hal_pwm_is_running,_Bool,FuriHalPwmOutputId +Function,+,furi_hal_pwm_set_params,void,"FuriHalPwmOutputId, uint32_t, uint8_t" +Function,+,furi_hal_pwm_start,void,"FuriHalPwmOutputId, uint32_t, uint8_t" +Function,+,furi_hal_pwm_stop,void,FuriHalPwmOutputId +Function,+,furi_hal_random_fill_buf,void,"uint8_t*, uint32_t" +Function,+,furi_hal_random_get,uint32_t, +Function,+,furi_hal_random_init,void, +Function,+,furi_hal_region_get,const FuriHalRegion*, +Function,+,furi_hal_region_get_band,const FuriHalRegionBand*,uint32_t +Function,+,furi_hal_region_get_name,const char*, +Function,-,furi_hal_region_init,void, +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* +Function,-,furi_hal_rtc_deinit_early,void, +Function,+,furi_hal_rtc_get_boot_mode,FuriHalRtcBootMode, +Function,+,furi_hal_rtc_get_datetime,void,FuriHalRtcDateTime* +Function,+,furi_hal_rtc_get_days_per_month,uint8_t,"_Bool, uint8_t" +Function,+,furi_hal_rtc_get_days_per_year,uint16_t,uint16_t +Function,+,furi_hal_rtc_get_fault_data,uint32_t, +Function,+,furi_hal_rtc_get_heap_track_mode,FuriHalRtcHeapTrackMode, +Function,+,furi_hal_rtc_get_locale_dateformat,FuriHalRtcLocaleDateFormat, +Function,+,furi_hal_rtc_get_locale_timeformat,FuriHalRtcLocaleTimeFormat, +Function,+,furi_hal_rtc_get_locale_units,FuriHalRtcLocaleUnits, +Function,+,furi_hal_rtc_get_log_level,uint8_t, +Function,+,furi_hal_rtc_get_pin_fails,uint32_t, +Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister +Function,+,furi_hal_rtc_get_timestamp,uint32_t, +Function,-,furi_hal_rtc_init,void, +Function,-,furi_hal_rtc_init_early,void, +Function,+,furi_hal_rtc_is_flag_set,_Bool,FuriHalRtcFlag +Function,+,furi_hal_rtc_is_leap_year,_Bool,uint16_t +Function,+,furi_hal_rtc_reset_flag,void,FuriHalRtcFlag +Function,+,furi_hal_rtc_set_boot_mode,void,FuriHalRtcBootMode +Function,+,furi_hal_rtc_set_datetime,void,FuriHalRtcDateTime* +Function,+,furi_hal_rtc_set_fault_data,void,uint32_t +Function,+,furi_hal_rtc_set_flag,void,FuriHalRtcFlag +Function,+,furi_hal_rtc_set_heap_track_mode,void,FuriHalRtcHeapTrackMode +Function,+,furi_hal_rtc_set_locale_dateformat,void,FuriHalRtcLocaleDateFormat +Function,+,furi_hal_rtc_set_locale_timeformat,void,FuriHalRtcLocaleTimeFormat +Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits +Function,+,furi_hal_rtc_set_log_level,void,uint8_t +Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t +Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" +Function,+,furi_hal_rtc_sync_shadow,void, +Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* +Function,+,furi_hal_sd_get_card_state,FuriStatus, +Function,+,furi_hal_sd_info,FuriStatus,FuriHalSdInfo* +Function,+,furi_hal_sd_init,FuriStatus,_Bool +Function,+,furi_hal_sd_is_present,_Bool, +Function,+,furi_hal_sd_max_mount_retry_count,uint8_t, +Function,+,furi_hal_sd_presence_init,void, +Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t" +Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t" +Function,+,furi_hal_speaker_acquire,_Bool,uint32_t +Function,-,furi_hal_speaker_deinit,void, +Function,-,furi_hal_speaker_init,void, +Function,+,furi_hal_speaker_is_mine,_Bool, +Function,+,furi_hal_speaker_release,void, +Function,+,furi_hal_speaker_set_volume,void,float +Function,+,furi_hal_speaker_start,void,"float, float" +Function,+,furi_hal_speaker_stop,void, +Function,+,furi_hal_spi_acquire,void,FuriHalSpiBusHandle* +Function,+,furi_hal_spi_bus_deinit,void,FuriHalSpiBus* +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*, 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 +Function,+,furi_hal_uart_init,void,"FuriHalUartId, uint32_t" +Function,+,furi_hal_uart_resume,void,FuriHalUartId +Function,+,furi_hal_uart_set_br,void,"FuriHalUartId, uint32_t" +Function,+,furi_hal_uart_set_irq_cb,void,"FuriHalUartId, void (*)(UartIrqEvent, uint8_t, void*), void*" +Function,+,furi_hal_uart_suspend,void,FuriHalUartId +Function,+,furi_hal_uart_tx,void,"FuriHalUartId, uint8_t*, size_t" +Function,+,furi_hal_usb_disable,void, +Function,+,furi_hal_usb_enable,void, +Function,+,furi_hal_usb_get_config,FuriHalUsbInterface*, +Function,-,furi_hal_usb_init,void, +Function,+,furi_hal_usb_is_locked,_Bool, +Function,+,furi_hal_usb_lock,void, +Function,+,furi_hal_usb_reinit,void, +Function,+,furi_hal_usb_set_config,_Bool,"FuriHalUsbInterface*, void*" +Function,-,furi_hal_usb_set_state_callback,void,"FuriHalUsbStateCallback, void*" +Function,+,furi_hal_usb_unlock,void, +Function,+,furi_hal_version_do_i_belong_here,_Bool, +Function,+,furi_hal_version_get_ble_local_device_name_ptr,const char*, +Function,+,furi_hal_version_get_ble_mac,const uint8_t*, +Function,+,furi_hal_version_get_device_name_ptr,const char*, +Function,+,furi_hal_version_get_fcc_id,const char*, +Function,+,furi_hal_version_get_firmware_version,const Version*, +Function,+,furi_hal_version_get_hw_body,uint8_t, +Function,+,furi_hal_version_get_hw_color,FuriHalVersionColor, +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_target,uint8_t, +Function,+,furi_hal_version_get_hw_timestamp,uint32_t, +Function,+,furi_hal_version_get_hw_version,uint8_t, +Function,+,furi_hal_version_get_ic_id,const char*, +Function,+,furi_hal_version_get_mic_id,const char*, +Function,+,furi_hal_version_get_model_code,const char*, +Function,+,furi_hal_version_get_model_name,const char*, +Function,+,furi_hal_version_get_name_ptr,const char*, +Function,+,furi_hal_version_get_otp_version,FuriHalVersionOtpVersion, +Function,-,furi_hal_version_init,void, +Function,+,furi_hal_version_uid,const uint8_t*, +Function,+,furi_hal_version_uid_size,size_t, +Function,-,furi_hal_vibro_init,void, +Function,+,furi_hal_vibro_on,void,_Bool +Function,-,furi_init,void, +Function,+,furi_kernel_get_tick_frequency,uint32_t, +Function,+,furi_kernel_is_irq_or_masked,_Bool, +Function,+,furi_kernel_is_running,_Bool, +Function,+,furi_kernel_lock,int32_t, +Function,+,furi_kernel_restore_lock,int32_t,int32_t +Function,+,furi_kernel_unlock,int32_t, +Function,+,furi_log_get_level,FuriLogLevel, +Function,-,furi_log_init,void, +Function,+,furi_log_level_from_string,_Bool,"const char*, FuriLogLevel*" +Function,+,furi_log_level_to_string,_Bool,"FuriLogLevel, const char**" +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 +Function,+,furi_message_queue_alloc,FuriMessageQueue*,"uint32_t, uint32_t" +Function,+,furi_message_queue_free,void,FuriMessageQueue* +Function,+,furi_message_queue_get,FuriStatus,"FuriMessageQueue*, void*, uint32_t" +Function,+,furi_message_queue_get_capacity,uint32_t,FuriMessageQueue* +Function,+,furi_message_queue_get_count,uint32_t,FuriMessageQueue* +Function,+,furi_message_queue_get_message_size,uint32_t,FuriMessageQueue* +Function,+,furi_message_queue_get_space,uint32_t,FuriMessageQueue* +Function,+,furi_message_queue_put,FuriStatus,"FuriMessageQueue*, const void*, uint32_t" +Function,+,furi_message_queue_reset,FuriStatus,FuriMessageQueue* +Function,+,furi_ms_to_ticks,uint32_t,uint32_t +Function,+,furi_mutex_acquire,FuriStatus,"FuriMutex*, uint32_t" +Function,+,furi_mutex_alloc,FuriMutex*,FuriMutexType +Function,+,furi_mutex_free,void,FuriMutex* +Function,+,furi_mutex_get_owner,FuriThreadId,FuriMutex* +Function,+,furi_mutex_release,FuriStatus,FuriMutex* +Function,+,furi_pubsub_alloc,FuriPubSub*, +Function,-,furi_pubsub_free,void,FuriPubSub* +Function,+,furi_pubsub_publish,void,"FuriPubSub*, void*" +Function,+,furi_pubsub_subscribe,FuriPubSubSubscription*,"FuriPubSub*, FuriPubSubCallback, void*" +Function,+,furi_pubsub_unsubscribe,void,"FuriPubSub*, FuriPubSubSubscription*" +Function,+,furi_record_close,void,const char* +Function,+,furi_record_create,void,"const char*, void*" +Function,+,furi_record_destroy,_Bool,const char* +Function,+,furi_record_exists,_Bool,const char* +Function,-,furi_record_init,void, +Function,+,furi_record_open,void*,const char* +Function,+,furi_run,void, +Function,+,furi_semaphore_acquire,FuriStatus,"FuriSemaphore*, uint32_t" +Function,+,furi_semaphore_alloc,FuriSemaphore*,"uint32_t, uint32_t" +Function,+,furi_semaphore_free,void,FuriSemaphore* +Function,+,furi_semaphore_get_count,uint32_t,FuriSemaphore* +Function,+,furi_semaphore_release,FuriStatus,FuriSemaphore* +Function,+,furi_stream_buffer_alloc,FuriStreamBuffer*,"size_t, size_t" +Function,+,furi_stream_buffer_bytes_available,size_t,FuriStreamBuffer* +Function,+,furi_stream_buffer_free,void,FuriStreamBuffer* +Function,+,furi_stream_buffer_is_empty,_Bool,FuriStreamBuffer* +Function,+,furi_stream_buffer_is_full,_Bool,FuriStreamBuffer* +Function,+,furi_stream_buffer_receive,size_t,"FuriStreamBuffer*, void*, size_t, uint32_t" +Function,+,furi_stream_buffer_reset,FuriStatus,FuriStreamBuffer* +Function,+,furi_stream_buffer_send,size_t,"FuriStreamBuffer*, const void*, size_t, uint32_t" +Function,+,furi_stream_buffer_spaces_available,size_t,FuriStreamBuffer* +Function,+,furi_stream_set_trigger_level,_Bool,"FuriStreamBuffer*, size_t" +Function,+,furi_string_alloc,FuriString*, +Function,+,furi_string_alloc_move,FuriString*,FuriString* +Function,+,furi_string_alloc_printf,FuriString*,"const char[], ..." +Function,+,furi_string_alloc_set,FuriString*,const FuriString* +Function,+,furi_string_alloc_set_str,FuriString*,const char[] +Function,+,furi_string_alloc_vprintf,FuriString*,"const char[], va_list" +Function,+,furi_string_cat,void,"FuriString*, const FuriString*" +Function,+,furi_string_cat_printf,int,"FuriString*, const char[], ..." +Function,+,furi_string_cat_str,void,"FuriString*, const char[]" +Function,+,furi_string_cat_vprintf,int,"FuriString*, const char[], va_list" +Function,+,furi_string_cmp,int,"const FuriString*, const FuriString*" +Function,+,furi_string_cmp_str,int,"const FuriString*, const char[]" +Function,+,furi_string_cmpi,int,"const FuriString*, const FuriString*" +Function,+,furi_string_cmpi_str,int,"const FuriString*, const char[]" +Function,+,furi_string_empty,_Bool,const FuriString* +Function,+,furi_string_end_with,_Bool,"const FuriString*, const FuriString*" +Function,+,furi_string_end_with_str,_Bool,"const FuriString*, const char[]" +Function,+,furi_string_equal,_Bool,"const FuriString*, const FuriString*" +Function,+,furi_string_equal_str,_Bool,"const FuriString*, const char[]" +Function,+,furi_string_free,void,FuriString* +Function,+,furi_string_get_char,char,"const FuriString*, size_t" +Function,+,furi_string_get_cstr,const char*,const FuriString* +Function,+,furi_string_hash,size_t,const FuriString* +Function,+,furi_string_left,void,"FuriString*, size_t" +Function,+,furi_string_mid,void,"FuriString*, size_t, size_t" +Function,+,furi_string_move,void,"FuriString*, FuriString*" +Function,+,furi_string_printf,int,"FuriString*, const char[], ..." +Function,+,furi_string_push_back,void,"FuriString*, char" +Function,+,furi_string_replace,size_t,"FuriString*, FuriString*, FuriString*, size_t" +Function,+,furi_string_replace_all,void,"FuriString*, const FuriString*, const FuriString*" +Function,+,furi_string_replace_all_str,void,"FuriString*, const char[], const char[]" +Function,+,furi_string_replace_at,void,"FuriString*, size_t, size_t, const char[]" +Function,+,furi_string_replace_str,size_t,"FuriString*, const char[], const char[], size_t" +Function,+,furi_string_reserve,void,"FuriString*, size_t" +Function,+,furi_string_reset,void,FuriString* +Function,+,furi_string_right,void,"FuriString*, size_t" +Function,+,furi_string_search,size_t,"const FuriString*, const FuriString*, size_t" +Function,+,furi_string_search_char,size_t,"const FuriString*, char, size_t" +Function,+,furi_string_search_rchar,size_t,"const FuriString*, char, size_t" +Function,+,furi_string_search_str,size_t,"const FuriString*, const char[], size_t" +Function,+,furi_string_set,void,"FuriString*, FuriString*" +Function,+,furi_string_set_char,void,"FuriString*, size_t, const char" +Function,+,furi_string_set_n,void,"FuriString*, const FuriString*, size_t, size_t" +Function,+,furi_string_set_str,void,"FuriString*, const char[]" +Function,+,furi_string_set_strn,void,"FuriString*, const char[], size_t" +Function,+,furi_string_size,size_t,const FuriString* +Function,+,furi_string_start_with,_Bool,"const FuriString*, const FuriString*" +Function,+,furi_string_start_with_str,_Bool,"const FuriString*, const char[]" +Function,+,furi_string_swap,void,"FuriString*, FuriString*" +Function,+,furi_string_trim,void,"FuriString*, const char[]" +Function,+,furi_string_utf8_decode,void,"char, FuriStringUTF8State*, FuriStringUnicodeValue*" +Function,+,furi_string_utf8_length,size_t,FuriString* +Function,+,furi_string_utf8_push,void,"FuriString*, FuriStringUnicodeValue" +Function,+,furi_string_vprintf,int,"FuriString*, const char[], va_list" +Function,+,furi_thread_alloc,FuriThread*, +Function,+,furi_thread_alloc_ex,FuriThread*,"const char*, uint32_t, FuriThreadCallback, void*" +Function,-,furi_thread_disable_heap_trace,void,FuriThread* +Function,+,furi_thread_enable_heap_trace,void,FuriThread* +Function,+,furi_thread_enumerate,uint32_t,"FuriThreadId*, uint32_t" +Function,+,furi_thread_flags_clear,uint32_t,uint32_t +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 +Function,+,furi_thread_get_return_code,int32_t,FuriThread* +Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId +Function,+,furi_thread_get_state,FuriThreadState,FuriThread* +Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback, +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" +Function,+,furi_thread_set_state_callback,void,"FuriThread*, FuriThreadStateCallback" +Function,+,furi_thread_set_state_context,void,"FuriThread*, void*" +Function,+,furi_thread_set_stdout_callback,void,FuriThreadStdoutWriteCallback +Function,+,furi_thread_start,void,FuriThread* +Function,+,furi_thread_stdout_flush,int32_t, +Function,+,furi_thread_stdout_write,size_t,"const char*, size_t" +Function,+,furi_thread_suspend,void,FuriThreadId +Function,+,furi_thread_yield,void, +Function,+,furi_timer_alloc,FuriTimer*,"FuriTimerCallback, FuriTimerType, void*" +Function,+,furi_timer_free,void,FuriTimer* +Function,+,furi_timer_get_expire_time,uint32_t,FuriTimer* +Function,+,furi_timer_is_running,uint32_t,FuriTimer* +Function,+,furi_timer_pending_callback,void,"FuriTimerPendigCallback, void*, uint32_t" +Function,+,furi_timer_restart,FuriStatus,"FuriTimer*, uint32_t" +Function,+,furi_timer_set_thread_priority,void,FuriTimerThreadPriority +Function,+,furi_timer_start,FuriStatus,"FuriTimer*, uint32_t" +Function,+,furi_timer_stop,FuriStatus,FuriTimer* +Function,-,fwrite,size_t,"const void*, size_t, size_t, FILE*" +Function,-,fwrite_unlocked,size_t,"const void*, size_t, size_t, FILE*" +Function,-,gamma,double,double +Function,-,gamma_r,double,"double, int*" +Function,-,gammaf,float,float +Function,-,gammaf_r,float,"float, int*" +Function,-,gap_get_state,GapState, +Function,-,gap_init,_Bool,"GapConfig*, GapEventCallback, void*" +Function,-,gap_start_advertising,void, +Function,-,gap_stop_advertising,void, +Function,-,gap_thread_stop,void, +Function,-,getc,int,FILE* +Function,-,getc_unlocked,int,FILE* +Function,-,getchar,int, +Function,-,getchar_unlocked,int, +Function,-,getenv,char*,const char* +Function,-,gets,char*,char* +Function,-,getsubopt,int,"char**, char**, char**" +Function,-,getw,int,FILE* +Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" +Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" +Function,+,gui_direct_draw_acquire,Canvas*,Gui* +Function,+,gui_direct_draw_release,void,Gui* +Function,+,gui_get_framebuffer_size,size_t,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" +Function,-,gui_view_port_send_to_back,void,"Gui*, ViewPort*" +Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*" +Function,+,hex_char_to_hex_nibble,_Bool,"char, uint8_t*" +Function,+,hex_char_to_uint8,_Bool,"char, char, uint8_t*" +Function,+,hex_chars_to_uint64,_Bool,"const char*, uint64_t*" +Function,+,hex_chars_to_uint8,_Bool,"const char*, uint8_t*" +Function,-,hypot,double,"double, double" +Function,-,hypotf,float,"float, float" +Function,-,hypotl,long double,"long double, long double" +Function,+,icon_animation_alloc,IconAnimation*,const Icon* +Function,+,icon_animation_free,void,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* +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,-,ilogb,int,double +Function,-,ilogbf,int,float +Function,-,ilogbl,int,long double +Function,-,index,char*,"const char*, int" +Function,-,infinity,double, +Function,-,infinityf,float, +Function,-,initstate,char*,"unsigned, char*, size_t" +Function,+,input_get_key_name,const char*,InputKey +Function,+,input_get_type_name,const char*,InputType +Function,-,iprintf,int,"const char*, ..." +Function,-,isalnum,int,int +Function,-,isalnum_l,int,"int, locale_t" +Function,-,isalpha,int,int +Function,-,isalpha_l,int,"int, locale_t" +Function,-,isascii,int,int +Function,-,isascii_l,int,"int, locale_t" +Function,-,isblank,int,int +Function,-,isblank_l,int,"int, locale_t" +Function,-,iscanf,int,"const char*, ..." +Function,-,iscntrl,int,int +Function,-,iscntrl_l,int,"int, locale_t" +Function,-,isdigit,int,int +Function,-,isdigit_l,int,"int, locale_t" +Function,-,isgraph,int,int +Function,-,isgraph_l,int,"int, locale_t" +Function,-,isinf,int,double +Function,-,isinff,int,float +Function,-,islower,int,int +Function,-,islower_l,int,"int, locale_t" +Function,-,isnan,int,double +Function,-,isnanf,int,float +Function,-,isprint,int,int +Function,-,isprint_l,int,"int, locale_t" +Function,-,ispunct,int,int +Function,-,ispunct_l,int,"int, locale_t" +Function,-,isspace,int,int +Function,-,isspace_l,int,"int, locale_t" +Function,-,isupper,int,int +Function,-,isupper_l,int,"int, locale_t" +Function,-,isxdigit,int,int +Function,-,isxdigit_l,int,"int, locale_t" +Function,-,itoa,char*,"int, char*, int" +Function,-,j0,double,double +Function,-,j0f,float,float +Function,-,j1,double,double +Function,-,j1f,float,float +Function,-,jn,double,"int, double" +Function,-,jnf,float,"int, float" +Function,-,jrand48,long,unsigned short[3] +Function,-,l64a,char*,long +Function,-,labs,long,long +Function,-,lcong48,void,unsigned short[7] +Function,-,ldexp,double,"double, int" +Function,-,ldexpf,float,"float, int" +Function,-,ldexpl,long double,"long double, int" +Function,-,ldiv,ldiv_t,"long, long" +Function,-,lgamma,double,double +Function,-,lgamma_r,double,"double, int*" +Function,-,lgammaf,float,float +Function,-,lgammaf_r,float,"float, int*" +Function,-,lgammal,long double,long double +Function,-,llabs,long long,long long +Function,-,lldiv,lldiv_t,"long long, long long" +Function,-,llrint,long long int,double +Function,-,llrintf,long long int,float +Function,-,llrintl,long long int,long double +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_lock,_Bool,Loader* +Function,+,loader_show_menu,void,Loader* +Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*, FuriString*" +Function,+,loader_start_with_gui_error,LoaderStatus,"Loader*, const char*, const char*" +Function,+,loader_unlock,void,Loader* +Function,+,loading_alloc,Loading*, +Function,+,loading_free,void,Loading* +Function,+,loading_get_view,View*,Loading* +Function,+,locale_celsius_to_fahrenheit,float,float +Function,+,locale_fahrenheit_to_celsius,float,float +Function,+,locale_format_date,void,"FuriString*, const FuriHalRtcDateTime*, const LocaleDateFormat, const char*" +Function,+,locale_format_time,void,"FuriString*, const FuriHalRtcDateTime*, const LocaleTimeFormat, const _Bool" +Function,+,locale_get_date_format,LocaleDateFormat, +Function,+,locale_get_measurement_unit,LocaleMeasurementUnits, +Function,+,locale_get_time_format,LocaleTimeFormat, +Function,+,locale_set_date_format,void,LocaleDateFormat +Function,+,locale_set_measurement_unit,void,LocaleMeasurementUnits +Function,+,locale_set_time_format,void,LocaleTimeFormat +Function,-,log,double,double +Function,-,log10,double,double +Function,-,log10f,float,float +Function,-,log10l,long double,long double +Function,-,log1p,double,double +Function,-,log1pf,float,float +Function,-,log1pl,long double,long double +Function,-,log2,double,double +Function,-,log2f,float,float +Function,-,log2l,long double,long double +Function,-,logb,double,double +Function,-,logbf,float,float +Function,-,logbl,long double,long double +Function,-,logf,float,float +Function,-,logl,long double,long double +Function,-,lrand48,long, +Function,-,lrint,long int,double +Function,-,lrintf,long int,float +Function,-,lrintl,long int,long double +Function,-,lround,long int,double +Function,-,lroundf,long int,float +Function,-,lroundl,long,long double +Function,+,malloc,void*,size_t +Function,+,manchester_advance,_Bool,"ManchesterState, ManchesterEvent, ManchesterState*, _Bool*" +Function,+,manchester_encoder_advance,_Bool,"ManchesterEncoderState*, const _Bool, ManchesterEncoderResult*" +Function,+,manchester_encoder_finish,ManchesterEncoderResult,ManchesterEncoderState* +Function,+,manchester_encoder_reset,void,ManchesterEncoderState* +Function,+,maxim_crc8,uint8_t,"const uint8_t*, const uint8_t, const uint8_t" +Function,-,mbedtls_des3_crypt_cbc,int,"mbedtls_des3_context*, int, size_t, unsigned char[8], const unsigned char*, unsigned char*" +Function,-,mbedtls_des3_crypt_ecb,int,"mbedtls_des3_context*, const unsigned char[8], unsigned char[8]" +Function,-,mbedtls_des3_free,void,mbedtls_des3_context* +Function,-,mbedtls_des3_init,void,mbedtls_des3_context* +Function,-,mbedtls_des3_set2key_dec,int,"mbedtls_des3_context*, const unsigned char[8 * 2]" +Function,-,mbedtls_des3_set2key_enc,int,"mbedtls_des3_context*, const unsigned char[8 * 2]" +Function,-,mbedtls_des3_set3key_dec,int,"mbedtls_des3_context*, const unsigned char[8 * 3]" +Function,-,mbedtls_des3_set3key_enc,int,"mbedtls_des3_context*, const unsigned char[8 * 3]" +Function,-,mbedtls_des_crypt_cbc,int,"mbedtls_des_context*, int, size_t, unsigned char[8], const unsigned char*, unsigned char*" +Function,-,mbedtls_des_crypt_ecb,int,"mbedtls_des_context*, const unsigned char[8], unsigned char[8]" +Function,-,mbedtls_des_free,void,mbedtls_des_context* +Function,-,mbedtls_des_init,void,mbedtls_des_context* +Function,-,mbedtls_des_key_check_key_parity,int,const unsigned char[8] +Function,-,mbedtls_des_key_check_weak,int,const unsigned char[8] +Function,-,mbedtls_des_key_set_parity,void,unsigned char[8] +Function,-,mbedtls_des_setkey,void,"uint32_t[32], const unsigned char[8]" +Function,-,mbedtls_des_setkey_dec,int,"mbedtls_des_context*, const unsigned char[8]" +Function,-,mbedtls_des_setkey_enc,int,"mbedtls_des_context*, const unsigned char[8]" +Function,-,mbedtls_ecdh_calc_secret,int,"mbedtls_ecdh_context*, size_t*, unsigned char*, size_t, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecdh_can_do,int,mbedtls_ecp_group_id +Function,-,mbedtls_ecdh_compute_shared,int,"mbedtls_ecp_group*, mbedtls_mpi*, const mbedtls_ecp_point*, const mbedtls_mpi*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecdh_free,void,mbedtls_ecdh_context* +Function,-,mbedtls_ecdh_gen_public,int,"mbedtls_ecp_group*, mbedtls_mpi*, mbedtls_ecp_point*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecdh_get_params,int,"mbedtls_ecdh_context*, const mbedtls_ecp_keypair*, mbedtls_ecdh_side" +Function,-,mbedtls_ecdh_init,void,mbedtls_ecdh_context* +Function,-,mbedtls_ecdh_make_params,int,"mbedtls_ecdh_context*, size_t*, unsigned char*, size_t, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecdh_make_public,int,"mbedtls_ecdh_context*, size_t*, unsigned char*, size_t, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecdh_read_params,int,"mbedtls_ecdh_context*, const unsigned char**, const unsigned char*" +Function,-,mbedtls_ecdh_read_public,int,"mbedtls_ecdh_context*, const unsigned char*, size_t" +Function,-,mbedtls_ecdh_setup,int,"mbedtls_ecdh_context*, mbedtls_ecp_group_id" +Function,-,mbedtls_ecdsa_can_do,int,mbedtls_ecp_group_id +Function,-,mbedtls_ecdsa_free,void,mbedtls_ecdsa_context* +Function,-,mbedtls_ecdsa_from_keypair,int,"mbedtls_ecdsa_context*, const mbedtls_ecp_keypair*" +Function,-,mbedtls_ecdsa_genkey,int,"mbedtls_ecdsa_context*, mbedtls_ecp_group_id, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecdsa_init,void,mbedtls_ecdsa_context* +Function,-,mbedtls_ecdsa_read_signature,int,"mbedtls_ecdsa_context*, const unsigned char*, size_t, const unsigned char*, size_t" +Function,-,mbedtls_ecdsa_read_signature_restartable,int,"mbedtls_ecdsa_context*, const unsigned char*, size_t, const unsigned char*, size_t, mbedtls_ecdsa_restart_ctx*" +Function,-,mbedtls_ecdsa_sign,int,"mbedtls_ecp_group*, mbedtls_mpi*, mbedtls_mpi*, const mbedtls_mpi*, const unsigned char*, size_t, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecdsa_sign_restartable,int,"mbedtls_ecp_group*, mbedtls_mpi*, mbedtls_mpi*, const mbedtls_mpi*, const unsigned char*, size_t, int (*)(void*, unsigned char*, size_t), void*, int (*)(void*, unsigned char*, size_t), void*, mbedtls_ecdsa_restart_ctx*" +Function,-,mbedtls_ecdsa_verify,int,"mbedtls_ecp_group*, const unsigned char*, size_t, const mbedtls_ecp_point*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_ecdsa_verify_restartable,int,"mbedtls_ecp_group*, const unsigned char*, size_t, const mbedtls_ecp_point*, const mbedtls_mpi*, const mbedtls_mpi*, mbedtls_ecdsa_restart_ctx*" +Function,-,mbedtls_ecdsa_write_signature,int,"mbedtls_ecdsa_context*, mbedtls_md_type_t, const unsigned char*, size_t, unsigned char*, size_t, size_t*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecdsa_write_signature_restartable,int,"mbedtls_ecdsa_context*, mbedtls_md_type_t, const unsigned char*, size_t, unsigned char*, size_t, size_t*, int (*)(void*, unsigned char*, size_t), void*, mbedtls_ecdsa_restart_ctx*" +Function,-,mbedtls_ecp_check_privkey,int,"const mbedtls_ecp_group*, const mbedtls_mpi*" +Function,-,mbedtls_ecp_check_pub_priv,int,"const mbedtls_ecp_keypair*, const mbedtls_ecp_keypair*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecp_check_pubkey,int,"const mbedtls_ecp_group*, const mbedtls_ecp_point*" +Function,-,mbedtls_ecp_copy,int,"mbedtls_ecp_point*, const mbedtls_ecp_point*" +Function,-,mbedtls_ecp_curve_info_from_grp_id,const mbedtls_ecp_curve_info*,mbedtls_ecp_group_id +Function,-,mbedtls_ecp_curve_info_from_name,const mbedtls_ecp_curve_info*,const char* +Function,-,mbedtls_ecp_curve_info_from_tls_id,const mbedtls_ecp_curve_info*,uint16_t +Function,-,mbedtls_ecp_curve_list,const mbedtls_ecp_curve_info*, +Function,-,mbedtls_ecp_export,int,"const mbedtls_ecp_keypair*, mbedtls_ecp_group*, mbedtls_mpi*, mbedtls_ecp_point*" +Function,-,mbedtls_ecp_gen_key,int,"mbedtls_ecp_group_id, mbedtls_ecp_keypair*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecp_gen_keypair,int,"mbedtls_ecp_group*, mbedtls_mpi*, mbedtls_ecp_point*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecp_gen_keypair_base,int,"mbedtls_ecp_group*, const mbedtls_ecp_point*, mbedtls_mpi*, mbedtls_ecp_point*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecp_gen_privkey,int,"const mbedtls_ecp_group*, mbedtls_mpi*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecp_get_type,mbedtls_ecp_curve_type,const mbedtls_ecp_group* +Function,-,mbedtls_ecp_group_copy,int,"mbedtls_ecp_group*, const mbedtls_ecp_group*" +Function,-,mbedtls_ecp_group_free,void,mbedtls_ecp_group* +Function,-,mbedtls_ecp_group_init,void,mbedtls_ecp_group* +Function,-,mbedtls_ecp_group_load,int,"mbedtls_ecp_group*, mbedtls_ecp_group_id" +Function,-,mbedtls_ecp_grp_id_list,const mbedtls_ecp_group_id*, +Function,-,mbedtls_ecp_is_zero,int,mbedtls_ecp_point* +Function,-,mbedtls_ecp_keypair_free,void,mbedtls_ecp_keypair* +Function,-,mbedtls_ecp_keypair_init,void,mbedtls_ecp_keypair* +Function,-,mbedtls_ecp_mul,int,"mbedtls_ecp_group*, mbedtls_ecp_point*, const mbedtls_mpi*, const mbedtls_ecp_point*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecp_mul_restartable,int,"mbedtls_ecp_group*, mbedtls_ecp_point*, const mbedtls_mpi*, const mbedtls_ecp_point*, int (*)(void*, unsigned char*, size_t), void*, mbedtls_ecp_restart_ctx*" +Function,-,mbedtls_ecp_muladd,int,"mbedtls_ecp_group*, mbedtls_ecp_point*, const mbedtls_mpi*, const mbedtls_ecp_point*, const mbedtls_mpi*, const mbedtls_ecp_point*" +Function,-,mbedtls_ecp_muladd_restartable,int,"mbedtls_ecp_group*, mbedtls_ecp_point*, const mbedtls_mpi*, const mbedtls_ecp_point*, const mbedtls_mpi*, const mbedtls_ecp_point*, mbedtls_ecp_restart_ctx*" +Function,-,mbedtls_ecp_point_cmp,int,"const mbedtls_ecp_point*, const mbedtls_ecp_point*" +Function,-,mbedtls_ecp_point_free,void,mbedtls_ecp_point* +Function,-,mbedtls_ecp_point_init,void,mbedtls_ecp_point* +Function,-,mbedtls_ecp_point_read_binary,int,"const mbedtls_ecp_group*, mbedtls_ecp_point*, const unsigned char*, size_t" +Function,-,mbedtls_ecp_point_read_string,int,"mbedtls_ecp_point*, int, const char*, const char*" +Function,-,mbedtls_ecp_point_write_binary,int,"const mbedtls_ecp_group*, const mbedtls_ecp_point*, int, size_t*, unsigned char*, size_t" +Function,-,mbedtls_ecp_read_key,int,"mbedtls_ecp_group_id, mbedtls_ecp_keypair*, const unsigned char*, size_t" +Function,-,mbedtls_ecp_set_zero,int,mbedtls_ecp_point* +Function,-,mbedtls_ecp_tls_read_group,int,"mbedtls_ecp_group*, const unsigned char**, size_t" +Function,-,mbedtls_ecp_tls_read_group_id,int,"mbedtls_ecp_group_id*, const unsigned char**, size_t" +Function,-,mbedtls_ecp_tls_read_point,int,"const mbedtls_ecp_group*, mbedtls_ecp_point*, const unsigned char**, size_t" +Function,-,mbedtls_ecp_tls_write_group,int,"const mbedtls_ecp_group*, size_t*, unsigned char*, size_t" +Function,-,mbedtls_ecp_tls_write_point,int,"const mbedtls_ecp_group*, const mbedtls_ecp_point*, int, size_t*, unsigned char*, size_t" +Function,-,mbedtls_ecp_write_key,int,"mbedtls_ecp_keypair*, unsigned char*, size_t" +Function,-,mbedtls_internal_md5_process,int,"mbedtls_md5_context*, const unsigned char[64]" +Function,-,mbedtls_internal_sha1_process,int,"mbedtls_sha1_context*, const unsigned char[64]" +Function,-,mbedtls_internal_sha256_process,int,"mbedtls_sha256_context*, const unsigned char[64]" +Function,-,mbedtls_md,int,"const mbedtls_md_info_t*, const unsigned char*, size_t, unsigned char*" +Function,-,mbedtls_md5,int,"const unsigned char*, size_t, unsigned char[16]" +Function,-,mbedtls_md5_clone,void,"mbedtls_md5_context*, const mbedtls_md5_context*" +Function,-,mbedtls_md5_finish,int,"mbedtls_md5_context*, unsigned char[16]" +Function,-,mbedtls_md5_free,void,mbedtls_md5_context* +Function,-,mbedtls_md5_init,void,mbedtls_md5_context* +Function,-,mbedtls_md5_starts,int,mbedtls_md5_context* +Function,-,mbedtls_md5_update,int,"mbedtls_md5_context*, const unsigned char*, size_t" +Function,-,mbedtls_md_clone,int,"mbedtls_md_context_t*, const mbedtls_md_context_t*" +Function,-,mbedtls_md_finish,int,"mbedtls_md_context_t*, unsigned char*" +Function,-,mbedtls_md_free,void,mbedtls_md_context_t* +Function,-,mbedtls_md_get_name,const char*,const mbedtls_md_info_t* +Function,-,mbedtls_md_get_size,unsigned char,const mbedtls_md_info_t* +Function,-,mbedtls_md_get_type,mbedtls_md_type_t,const mbedtls_md_info_t* +Function,-,mbedtls_md_hmac,int,"const mbedtls_md_info_t*, const unsigned char*, size_t, const unsigned char*, size_t, unsigned char*" +Function,-,mbedtls_md_hmac_finish,int,"mbedtls_md_context_t*, unsigned char*" +Function,-,mbedtls_md_hmac_reset,int,mbedtls_md_context_t* +Function,-,mbedtls_md_hmac_starts,int,"mbedtls_md_context_t*, const unsigned char*, size_t" +Function,-,mbedtls_md_hmac_update,int,"mbedtls_md_context_t*, const unsigned char*, size_t" +Function,-,mbedtls_md_info_from_ctx,const mbedtls_md_info_t*,const mbedtls_md_context_t* +Function,-,mbedtls_md_info_from_string,const mbedtls_md_info_t*,const char* +Function,-,mbedtls_md_info_from_type,const mbedtls_md_info_t*,mbedtls_md_type_t +Function,-,mbedtls_md_init,void,mbedtls_md_context_t* +Function,-,mbedtls_md_list,const int*, +Function,-,mbedtls_md_setup,int,"mbedtls_md_context_t*, const mbedtls_md_info_t*, int" +Function,-,mbedtls_md_starts,int,mbedtls_md_context_t* +Function,-,mbedtls_md_update,int,"mbedtls_md_context_t*, const unsigned char*, size_t" +Function,-,mbedtls_mpi_add_abs,int,"mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_add_int,int,"mbedtls_mpi*, const mbedtls_mpi*, mbedtls_mpi_sint" +Function,-,mbedtls_mpi_add_mpi,int,"mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_bitlen,size_t,const mbedtls_mpi* +Function,-,mbedtls_mpi_cmp_abs,int,"const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_cmp_int,int,"const mbedtls_mpi*, mbedtls_mpi_sint" +Function,-,mbedtls_mpi_cmp_mpi,int,"const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_copy,int,"mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_div_int,int,"mbedtls_mpi*, mbedtls_mpi*, const mbedtls_mpi*, mbedtls_mpi_sint" +Function,-,mbedtls_mpi_div_mpi,int,"mbedtls_mpi*, mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_exp_mod,int,"mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*, mbedtls_mpi*" +Function,-,mbedtls_mpi_fill_random,int,"mbedtls_mpi*, size_t, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_mpi_free,void,mbedtls_mpi* +Function,-,mbedtls_mpi_gcd,int,"mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_gen_prime,int,"mbedtls_mpi*, size_t, int, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_mpi_get_bit,int,"const mbedtls_mpi*, size_t" +Function,-,mbedtls_mpi_grow,int,"mbedtls_mpi*, size_t" +Function,-,mbedtls_mpi_init,void,mbedtls_mpi* +Function,-,mbedtls_mpi_inv_mod,int,"mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_is_prime_ext,int,"const mbedtls_mpi*, int, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_mpi_lsb,size_t,const mbedtls_mpi* +Function,-,mbedtls_mpi_lset,int,"mbedtls_mpi*, mbedtls_mpi_sint" +Function,-,mbedtls_mpi_lt_mpi_ct,int,"const mbedtls_mpi*, const mbedtls_mpi*, unsigned*" +Function,-,mbedtls_mpi_mod_int,int,"mbedtls_mpi_uint*, const mbedtls_mpi*, mbedtls_mpi_sint" +Function,-,mbedtls_mpi_mod_mpi,int,"mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_mul_int,int,"mbedtls_mpi*, const mbedtls_mpi*, mbedtls_mpi_uint" +Function,-,mbedtls_mpi_mul_mpi,int,"mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_random,int,"mbedtls_mpi*, mbedtls_mpi_sint, const mbedtls_mpi*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_mpi_read_binary,int,"mbedtls_mpi*, const unsigned char*, size_t" +Function,-,mbedtls_mpi_read_binary_le,int,"mbedtls_mpi*, const unsigned char*, size_t" +Function,-,mbedtls_mpi_read_string,int,"mbedtls_mpi*, int, const char*" +Function,-,mbedtls_mpi_safe_cond_assign,int,"mbedtls_mpi*, const mbedtls_mpi*, unsigned char" +Function,-,mbedtls_mpi_safe_cond_swap,int,"mbedtls_mpi*, mbedtls_mpi*, unsigned char" +Function,-,mbedtls_mpi_set_bit,int,"mbedtls_mpi*, size_t, unsigned char" +Function,-,mbedtls_mpi_shift_l,int,"mbedtls_mpi*, size_t" +Function,-,mbedtls_mpi_shift_r,int,"mbedtls_mpi*, size_t" +Function,-,mbedtls_mpi_shrink,int,"mbedtls_mpi*, size_t" +Function,-,mbedtls_mpi_size,size_t,const mbedtls_mpi* +Function,-,mbedtls_mpi_sub_abs,int,"mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_sub_int,int,"mbedtls_mpi*, const mbedtls_mpi*, mbedtls_mpi_sint" +Function,-,mbedtls_mpi_sub_mpi,int,"mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_swap,void,"mbedtls_mpi*, mbedtls_mpi*" +Function,-,mbedtls_mpi_write_binary,int,"const mbedtls_mpi*, unsigned char*, size_t" +Function,-,mbedtls_mpi_write_binary_le,int,"const mbedtls_mpi*, unsigned char*, size_t" +Function,-,mbedtls_mpi_write_string,int,"const mbedtls_mpi*, int, char*, size_t, size_t*" +Function,-,mbedtls_platform_zeroize,void,"void*, size_t" +Function,-,mbedtls_sha1,int,"const unsigned char*, size_t, unsigned char[20]" +Function,-,mbedtls_sha1_clone,void,"mbedtls_sha1_context*, const mbedtls_sha1_context*" +Function,-,mbedtls_sha1_finish,int,"mbedtls_sha1_context*, unsigned char[20]" +Function,-,mbedtls_sha1_free,void,mbedtls_sha1_context* +Function,-,mbedtls_sha1_init,void,mbedtls_sha1_context* +Function,-,mbedtls_sha1_starts,int,mbedtls_sha1_context* +Function,-,mbedtls_sha1_update,int,"mbedtls_sha1_context*, const unsigned char*, size_t" +Function,-,mbedtls_sha256,int,"const unsigned char*, size_t, unsigned char*, int" +Function,-,mbedtls_sha256_clone,void,"mbedtls_sha256_context*, const mbedtls_sha256_context*" +Function,-,mbedtls_sha256_finish,int,"mbedtls_sha256_context*, unsigned char*" +Function,-,mbedtls_sha256_free,void,mbedtls_sha256_context* +Function,-,mbedtls_sha256_init,void,mbedtls_sha256_context* +Function,-,mbedtls_sha256_starts,int,"mbedtls_sha256_context*, int" +Function,-,mbedtls_sha256_update,int,"mbedtls_sha256_context*, const unsigned char*, size_t" +Function,-,mblen,int,"const char*, size_t" +Function,-,mbstowcs,size_t,"wchar_t*, const char*, size_t" +Function,-,mbtowc,int,"wchar_t*, const char*, size_t" +Function,-,memccpy,void*,"void*, const void*, int, size_t" +Function,+,memchr,void*,"const void*, int, size_t" +Function,+,memcmp,int,"const void*, const void*, size_t" +Function,+,memcpy,void*,"void*, const void*, size_t" +Function,-,memmem,void*,"const void*, size_t, const void*, size_t" +Function,-,memmgr_alloc_from_pool,void*,size_t +Function,+,memmgr_get_free_heap,size_t, +Function,+,memmgr_get_minimum_free_heap,size_t, +Function,+,memmgr_get_total_heap,size_t, +Function,+,memmgr_heap_disable_thread_trace,void,FuriThreadId +Function,+,memmgr_heap_enable_thread_trace,void,FuriThreadId +Function,+,memmgr_heap_get_max_free_block,size_t, +Function,+,memmgr_heap_get_thread_memory,size_t,FuriThreadId +Function,+,memmgr_heap_printf_free_blocks,void, +Function,-,memmgr_pool_get_free,size_t, +Function,-,memmgr_pool_get_max_block,size_t, +Function,+,memmove,void*,"void*, const void*, size_t" +Function,-,mempcpy,void*,"void*, const void*, size_t" +Function,-,memrchr,void*,"const void*, int, size_t" +Function,+,memset,void*,"void*, int, size_t" +Function,+,menu_add_item,void,"Menu*, const char*, const Icon*, uint32_t, MenuItemCallback, void*" +Function,+,menu_alloc,Menu*, +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,-,mkdtemp,char*,char* +Function,-,mkostemp,int,"char*, int" +Function,-,mkostemps,int,"char*, int, int" +Function,-,mkstemp,int,char* +Function,-,mkstemps,int,"char*, int" +Function,-,mktemp,char*,char* +Function,-,modf,double,"double, double*" +Function,-,modff,float,"float, float*" +Function,-,modfl,long double,"long double, long double*" +Function,-,mrand48,long, +Function,-,music_worker_alloc,MusicWorker*, +Function,-,music_worker_clear,void,MusicWorker* +Function,-,music_worker_free,void,MusicWorker* +Function,-,music_worker_is_playing,_Bool,MusicWorker* +Function,-,music_worker_load,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_fmf_from_file,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_rtttl_from_file,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_rtttl_from_string,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_set_callback,void,"MusicWorker*, MusicWorkerCallback, void*" +Function,-,music_worker_set_volume,void,"MusicWorker*, float" +Function,-,music_worker_start,void,MusicWorker* +Function,-,music_worker_stop,void,MusicWorker* +Function,+,name_generator_make_auto,void,"char*, size_t, const char*" +Function,+,name_generator_make_detailed,void,"char*, size_t, const char*" +Function,+,name_generator_make_random,void,"char*, size_t" +Function,-,nan,double,const char* +Function,-,nanf,float,const char* +Function,-,nanl,long double,const char* +Function,-,nearbyint,double,double +Function,-,nearbyintf,float,float +Function,-,nearbyintl,long double,long double +Function,-,nextafter,double,"double, double" +Function,-,nextafterf,float,"float, float" +Function,-,nextafterl,long double,"long double, long double" +Function,-,nexttoward,double,"double, long double" +Function,-,nexttowardf,float,"float, long double" +Function,-,nexttowardl,long double,"long double, long double" +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_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* +Function,+,onewire_host_read_bytes,void,"OneWireHost*, uint8_t*, uint16_t" +Function,+,onewire_host_reset,_Bool,OneWireHost* +Function,+,onewire_host_reset_search,void,OneWireHost* +Function,+,onewire_host_search,_Bool,"OneWireHost*, uint8_t*, OneWireHostSearchMode" +Function,+,onewire_host_set_overdrive,void,"OneWireHost*, _Bool" +Function,+,onewire_host_start,void,OneWireHost* +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_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_overdrive,void,"OneWireSlave*, _Bool" +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* +Function,-,open_memstream,FILE*,"char**, size_t*" +Function,+,path_append,void,"FuriString*, const char*" +Function,+,path_concat,void,"const char*, const char*, FuriString*" +Function,+,path_contains_only_ascii,_Bool,const char* +Function,+,path_extract_basename,void,"const char*, FuriString*" +Function,+,path_extract_dirname,void,"const char*, FuriString*" +Function,+,path_extract_extension,void,"FuriString*, char*, size_t" +Function,+,path_extract_filename,void,"FuriString*, FuriString*, _Bool" +Function,+,path_extract_filename_no_ext,void,"const char*, FuriString*" +Function,+,pb_close_string_substream,_Bool,"pb_istream_t*, pb_istream_t*" +Function,+,pb_decode,_Bool,"pb_istream_t*, const pb_msgdesc_t*, void*" +Function,+,pb_decode_bool,_Bool,"pb_istream_t*, _Bool*" +Function,+,pb_decode_ex,_Bool,"pb_istream_t*, const pb_msgdesc_t*, void*, unsigned int" +Function,+,pb_decode_fixed32,_Bool,"pb_istream_t*, void*" +Function,+,pb_decode_fixed64,_Bool,"pb_istream_t*, void*" +Function,+,pb_decode_svarint,_Bool,"pb_istream_t*, int64_t*" +Function,+,pb_decode_tag,_Bool,"pb_istream_t*, pb_wire_type_t*, uint32_t*, _Bool*" +Function,+,pb_decode_varint,_Bool,"pb_istream_t*, uint64_t*" +Function,+,pb_decode_varint32,_Bool,"pb_istream_t*, uint32_t*" +Function,+,pb_default_field_callback,_Bool,"pb_istream_t*, pb_ostream_t*, const pb_field_t*" +Function,+,pb_encode,_Bool,"pb_ostream_t*, const pb_msgdesc_t*, const void*" +Function,+,pb_encode_ex,_Bool,"pb_ostream_t*, const pb_msgdesc_t*, const void*, unsigned int" +Function,+,pb_encode_fixed32,_Bool,"pb_ostream_t*, const void*" +Function,+,pb_encode_fixed64,_Bool,"pb_ostream_t*, const void*" +Function,+,pb_encode_string,_Bool,"pb_ostream_t*, const pb_byte_t*, size_t" +Function,+,pb_encode_submessage,_Bool,"pb_ostream_t*, const pb_msgdesc_t*, const void*" +Function,+,pb_encode_svarint,_Bool,"pb_ostream_t*, int64_t" +Function,+,pb_encode_tag,_Bool,"pb_ostream_t*, pb_wire_type_t, uint32_t" +Function,+,pb_encode_tag_for_field,_Bool,"pb_ostream_t*, const pb_field_iter_t*" +Function,+,pb_encode_varint,_Bool,"pb_ostream_t*, uint64_t" +Function,+,pb_get_encoded_size,_Bool,"size_t*, const pb_msgdesc_t*, const void*" +Function,+,pb_istream_from_buffer,pb_istream_t,"const pb_byte_t*, size_t" +Function,+,pb_make_string_substream,_Bool,"pb_istream_t*, pb_istream_t*" +Function,+,pb_ostream_from_buffer,pb_ostream_t,"pb_byte_t*, size_t" +Function,+,pb_read,_Bool,"pb_istream_t*, pb_byte_t*, size_t" +Function,+,pb_release,void,"const pb_msgdesc_t*, void*" +Function,+,pb_skip_field,_Bool,"pb_istream_t*, pb_wire_type_t" +Function,+,pb_write,_Bool,"pb_ostream_t*, const pb_byte_t*, size_t" +Function,-,pclose,int,FILE* +Function,-,perror,void,const char* +Function,+,plugin_manager_alloc,PluginManager*,"const char*, uint32_t, const ElfApiInterface*" +Function,+,plugin_manager_free,void,PluginManager* +Function,+,plugin_manager_get,const FlipperAppPluginDescriptor*,"PluginManager*, uint32_t" +Function,+,plugin_manager_get_count,uint32_t,PluginManager* +Function,+,plugin_manager_get_ep,const void*,"PluginManager*, uint32_t" +Function,+,plugin_manager_load_all,PluginManagerError,"PluginManager*, const char*" +Function,+,plugin_manager_load_single,PluginManagerError,"PluginManager*, const char*" +Function,-,popen,FILE*,"const char*, const char*" +Function,+,popup_alloc,Popup*, +Function,+,popup_disable_timeout,void,Popup* +Function,+,popup_enable_timeout,void,Popup* +Function,+,popup_free,void,Popup* +Function,+,popup_get_view,View*,Popup* +Function,+,popup_reset,void,Popup* +Function,+,popup_set_callback,void,"Popup*, PopupCallback" +Function,+,popup_set_context,void,"Popup*, void*" +Function,+,popup_set_header,void,"Popup*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,popup_set_icon,void,"Popup*, uint8_t, uint8_t, const Icon*" +Function,+,popup_set_text,void,"Popup*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,popup_set_timeout,void,"Popup*, uint32_t" +Function,-,posix_memalign,int,"void**, size_t, size_t" +Function,-,pow,double,"double, double" +Function,-,pow10,double,double +Function,-,pow10f,float,float +Function,+,power_enable_low_battery_level_notification,void,"Power*, _Bool" +Function,+,power_get_info,void,"Power*, PowerInfo*" +Function,+,power_get_pubsub,FuriPubSub*,Power* +Function,+,power_is_battery_healthy,_Bool,Power* +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,+,property_value_out,void,"PropertyValueContext*, const char*, unsigned int, ..." +Function,+,protocol_dict_alloc,ProtocolDict*,"const ProtocolBase**, size_t" +Function,+,protocol_dict_decoders_feed,ProtocolId,"ProtocolDict*, _Bool, uint32_t" +Function,+,protocol_dict_decoders_feed_by_feature,ProtocolId,"ProtocolDict*, uint32_t, _Bool, uint32_t" +Function,+,protocol_dict_decoders_feed_by_id,ProtocolId,"ProtocolDict*, size_t, _Bool, uint32_t" +Function,+,protocol_dict_decoders_start,void,ProtocolDict* +Function,+,protocol_dict_encoder_start,_Bool,"ProtocolDict*, size_t" +Function,+,protocol_dict_encoder_yield,LevelDuration,"ProtocolDict*, size_t" +Function,+,protocol_dict_free,void,ProtocolDict* +Function,+,protocol_dict_get_data,void,"ProtocolDict*, size_t, uint8_t*, size_t" +Function,+,protocol_dict_get_data_size,size_t,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_features,uint32_t,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_manufacturer,const char*,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_max_data_size,size_t,ProtocolDict* +Function,+,protocol_dict_get_name,const char*,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_protocol_by_name,ProtocolId,"ProtocolDict*, const char*" +Function,+,protocol_dict_get_validate_count,uint32_t,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_write_data,_Bool,"ProtocolDict*, size_t, void*" +Function,+,protocol_dict_render_brief_data,void,"ProtocolDict*, FuriString*, size_t" +Function,+,protocol_dict_render_data,void,"ProtocolDict*, FuriString*, size_t" +Function,+,protocol_dict_set_data,void,"ProtocolDict*, size_t, const uint8_t*, size_t" +Function,-,pselect,int,"int, fd_set*, fd_set*, fd_set*, const timespec*, const sigset_t*" +Function,-,pulse_reader_alloc,PulseReader*,"const GpioPin*, uint32_t" +Function,-,pulse_reader_free,void,PulseReader* +Function,-,pulse_reader_receive,uint32_t,"PulseReader*, int" +Function,-,pulse_reader_samples,uint32_t,PulseReader* +Function,-,pulse_reader_set_bittime,void,"PulseReader*, uint32_t" +Function,-,pulse_reader_set_pull,void,"PulseReader*, GpioPull" +Function,-,pulse_reader_set_timebase,void,"PulseReader*, PulseReaderUnit" +Function,-,pulse_reader_start,void,PulseReader* +Function,-,pulse_reader_stop,void,PulseReader* +Function,-,putc,int,"int, FILE*" +Function,-,putc_unlocked,int,"int, FILE*" +Function,-,putchar,int,int +Function,-,putchar_unlocked,int,int +Function,-,putenv,int,char* +Function,-,puts,int,const char* +Function,-,putw,int,"int, FILE*" +Function,-,qsort,void,"void*, size_t, size_t, __compar_fn_t" +Function,-,qsort_r,void,"void*, size_t, size_t, int (*)(const void*, const void*, void*), void*" +Function,-,quick_exit,void,int +Function,+,rand,int, +Function,-,rand_r,int,unsigned* +Function,+,random,long, +Function,-,rawmemchr,void*,"const void*, int" +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,-,remainder,double,"double, double" +Function,-,remainderf,float,"float, float" +Function,-,remainderl,long double,"long double, long double" +Function,-,remove,int,const char* +Function,-,remquo,double,"double, double, int*" +Function,-,remquof,float,"float, float, int*" +Function,-,remquol,long double,"long double, long double, int*" +Function,-,rename,int,"const char*, const char*" +Function,-,renameat,int,"int, const char*, int, const char*" +Function,-,rewind,void,FILE* +Function,-,rindex,char*,"const char*, int" +Function,-,rint,double,double +Function,-,rintf,float,float +Function,-,rintl,long double,long double +Function,-,round,double,double +Function,+,roundf,float,float +Function,-,roundl,long double,long double +Function,+,rpc_session_close,void,RpcSession* +Function,+,rpc_session_feed,size_t,"RpcSession*, uint8_t*, size_t, uint32_t" +Function,+,rpc_session_get_available_size,size_t,RpcSession* +Function,+,rpc_session_get_owner,RpcOwner,RpcSession* +Function,+,rpc_session_open,RpcSession*,"Rpc*, RpcOwner" +Function,+,rpc_session_set_buffer_is_empty_callback,void,"RpcSession*, RpcBufferIsEmptyCallback" +Function,+,rpc_session_set_close_callback,void,"RpcSession*, RpcSessionClosedCallback" +Function,+,rpc_session_set_context,void,"RpcSession*, void*" +Function,+,rpc_session_set_send_bytes_callback,void,"RpcSession*, RpcSendBytesCallback" +Function,+,rpc_session_set_terminated_callback,void,"RpcSession*, RpcSessionTerminatedCallback" +Function,+,rpc_system_app_confirm,void,"RpcAppSystem*, _Bool" +Function,+,rpc_system_app_error_reset,void,RpcAppSystem* +Function,+,rpc_system_app_exchange_data,void,"RpcAppSystem*, const uint8_t*, size_t" +Function,+,rpc_system_app_send_exited,void,RpcAppSystem* +Function,+,rpc_system_app_send_started,void,RpcAppSystem* +Function,+,rpc_system_app_set_callback,void,"RpcAppSystem*, RpcAppSystemCallback, void*" +Function,+,rpc_system_app_set_error_code,void,"RpcAppSystem*, uint32_t" +Function,+,rpc_system_app_set_error_text,void,"RpcAppSystem*, const char*" +Function,-,rpmatch,int,const char* +Function,+,saved_struct_get_payload_size,_Bool,"const char*, uint8_t, uint8_t, size_t*" +Function,+,saved_struct_load,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" +Function,+,saved_struct_save,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" +Function,-,scalbln,double,"double, long int" +Function,-,scalblnf,float,"float, long int" +Function,-,scalblnl,long double,"long double, long" +Function,-,scalbn,double,"double, int" +Function,+,scalbnf,float,"float, int" +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,"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,"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" +Function,+,scene_manager_search_and_switch_to_previous_scene,_Bool,"SceneManager*, uint32_t" +Function,+,scene_manager_search_and_switch_to_previous_scene_one_of,_Bool,"SceneManager*, const uint32_t*, size_t" +Function,+,scene_manager_set_scene_state,void,"SceneManager*, uint32_t, uint32_t" +Function,+,scene_manager_stop,void,SceneManager* +Function,+,sd_api_get_fs_type_text,const char*,SDFsType +Function,-,secure_getenv,char*,const char* +Function,-,seed48,unsigned short*,unsigned short[3] +Function,-,select,int,"int, fd_set*, fd_set*, fd_set*, timeval*" +Function,-,serial_svc_is_started,_Bool, +Function,-,serial_svc_notify_buffer_is_empty,void, +Function,-,serial_svc_set_callbacks,void,"uint16_t, SerialServiceEventCallback, void*" +Function,-,serial_svc_set_rpc_status,void,SerialServiceRpcStatus +Function,-,serial_svc_start,void, +Function,-,serial_svc_stop,void, +Function,-,serial_svc_update_tx,_Bool,"uint8_t*, uint16_t" +Function,-,setbuf,void,"FILE*, char*" +Function,-,setbuffer,void,"FILE*, char*, int" +Function,-,setenv,int,"const char*, const char*, int" +Function,-,setkey,void,const char* +Function,-,setlinebuf,int,FILE* +Function,-,setstate,char*,char* +Function,-,setvbuf,int,"FILE*, char*, int, size_t" +Function,+,signal_reader_alloc,SignalReader*,"const GpioPin*, uint32_t" +Function,+,signal_reader_free,void,SignalReader* +Function,+,signal_reader_set_polarity,void,"SignalReader*, SignalReaderPolarity" +Function,+,signal_reader_set_pull,void,"SignalReader*, GpioPull" +Function,+,signal_reader_set_sample_rate,void,"SignalReader*, SignalReaderTimeUnit, uint32_t" +Function,+,signal_reader_set_trigger,void,"SignalReader*, SignalReaderTrigger" +Function,+,signal_reader_start,void,"SignalReader*, SignalReaderCallback, void*" +Function,+,signal_reader_stop,void,SignalReader* +Function,+,simple_array_alloc,SimpleArray*,const SimpleArrayConfig* +Function,+,simple_array_cget,const SimpleArrayElement*,"const SimpleArray*, uint32_t" +Function,+,simple_array_cget_data,const SimpleArrayData*,const SimpleArray* +Function,+,simple_array_copy,void,"SimpleArray*, const SimpleArray*" +Function,+,simple_array_free,void,SimpleArray* +Function,+,simple_array_get,SimpleArrayElement*,"SimpleArray*, uint32_t" +Function,+,simple_array_get_count,uint32_t,const SimpleArray* +Function,+,simple_array_get_data,SimpleArrayData*,SimpleArray* +Function,+,simple_array_init,void,"SimpleArray*, uint32_t" +Function,+,simple_array_is_equal,_Bool,"const SimpleArray*, const SimpleArray*" +Function,+,simple_array_reset,void,SimpleArray* +Function,-,sin,double,double +Function,-,sincos,void,"double, double*, double*" +Function,-,sincosf,void,"float, float*, float*" +Function,-,sinf,float,float +Function,-,sinh,double,double +Function,-,sinhf,float,float +Function,-,sinhl,long double,long double +Function,-,sinl,long double,long double +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,-,sprintf,int,"char*, const char*, ..." +Function,-,sqrt,double,double +Function,-,sqrtf,float,float +Function,-,sqrtl,long double,long double +Function,+,srand,void,unsigned +Function,-,srand48,void,long +Function,-,srandom,void,unsigned +Function,+,sscanf,int,"const char*, const char*, ..." +Function,+,st25r3916_change_reg_bits,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" +Function,+,st25r3916_change_test_reg_bits,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" +Function,+,st25r3916_check_reg,_Bool,"FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" +Function,+,st25r3916_clear_reg_bits,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t" +Function,+,st25r3916_direct_cmd,void,"FuriHalSpiBusHandle*, uint8_t" +Function,+,st25r3916_get_irq,uint32_t,FuriHalSpiBusHandle* +Function,+,st25r3916_mask_irq,void,"FuriHalSpiBusHandle*, uint32_t" +Function,+,st25r3916_modify_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" +Function,+,st25r3916_read_burst_regs,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t*, uint8_t" +Function,+,st25r3916_read_fifo,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, size_t*" +Function,+,st25r3916_read_pta_mem,void,"FuriHalSpiBusHandle*, uint8_t*, size_t" +Function,+,st25r3916_read_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t*" +Function,+,st25r3916_read_test_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t*" +Function,+,st25r3916_reg_read_fifo,void,"FuriHalSpiBusHandle*, uint8_t*, size_t" +Function,+,st25r3916_reg_write_fifo,void,"FuriHalSpiBusHandle*, const uint8_t*, size_t" +Function,+,st25r3916_set_reg_bits,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t" +Function,+,st25r3916_write_burst_regs,void,"FuriHalSpiBusHandle*, uint8_t, const uint8_t*, uint8_t" +Function,+,st25r3916_write_fifo,void,"FuriHalSpiBusHandle*, const uint8_t*, size_t" +Function,+,st25r3916_write_pta_mem,void,"FuriHalSpiBusHandle*, const uint8_t*, size_t" +Function,+,st25r3916_write_ptf_mem,void,"FuriHalSpiBusHandle*, const uint8_t*, size_t" +Function,+,st25r3916_write_pttsn_mem,void,"FuriHalSpiBusHandle*, uint8_t*, size_t" +Function,+,st25r3916_write_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t" +Function,+,st25r3916_write_test_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t" +Function,+,storage_common_copy,FS_Error,"Storage*, const char*, const char*" +Function,+,storage_common_equivalent_path,_Bool,"Storage*, const char*, const char*, _Bool" +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* +Function,+,storage_error_get_desc,const char*,FS_Error +Function,+,storage_file_alloc,File*,Storage* +Function,+,storage_file_close,_Bool,File* +Function,+,storage_file_copy_to_file,_Bool,"File*, File*, size_t" +Function,+,storage_file_eof,_Bool,File* +Function,+,storage_file_exists,_Bool,"Storage*, const char*" +Function,+,storage_file_free,void,File* +Function,+,storage_file_get_error,FS_Error,File* +Function,+,storage_file_get_error_desc,const char*,File* +Function,-,storage_file_get_internal_error,int32_t,File* +Function,+,storage_file_is_dir,_Bool,File* +Function,+,storage_file_is_open,_Bool,File* +Function,+,storage_file_open,_Bool,"File*, const char*, FS_AccessMode, FS_OpenMode" +Function,+,storage_file_read,size_t,"File*, void*, size_t" +Function,+,storage_file_seek,_Bool,"File*, uint32_t, _Bool" +Function,+,storage_file_size,uint64_t,File* +Function,+,storage_file_sync,_Bool,File* +Function,+,storage_file_tell,uint64_t,File* +Function,+,storage_file_truncate,_Bool,File* +Function,+,storage_file_write,size_t,"File*, const void*, size_t" +Function,+,storage_get_next_filename,void,"Storage*, const char*, const char*, const char*, FuriString*, uint8_t" +Function,+,storage_get_pubsub,FuriPubSub*,Storage* +Function,+,storage_int_backup,FS_Error,"Storage*, const char*" +Function,+,storage_int_restore,FS_Error,"Storage*, const char*, Storage_name_converter" +Function,+,storage_sd_format,FS_Error,Storage* +Function,+,storage_sd_info,FS_Error,"Storage*, SDInfo*" +Function,+,storage_sd_mount,FS_Error,Storage* +Function,+,storage_sd_status,FS_Error,Storage* +Function,+,storage_sd_unmount,FS_Error,Storage* +Function,+,storage_simply_mkdir,_Bool,"Storage*, const char*" +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_l,int,"const char*, const char*, locale_t" +Function,+,strcasestr,char*,"const char*, const char*" +Function,-,strcat,char*,"char*, const char*" +Function,+,strchr,char*,"const char*, int" +Function,-,strchrnul,char*,"const char*, int" +Function,+,strcmp,int,"const char*, const char*" +Function,-,strcoll,int,"const char*, const char*" +Function,-,strcoll_l,int,"const char*, const char*, locale_t" +Function,+,strcpy,char*,"char*, const char*" +Function,+,strcspn,size_t,"const char*, const char*" +Function,+,strdup,char*,const char* +Function,+,stream_clean,void,Stream* +Function,+,stream_copy,size_t,"Stream*, Stream*, size_t" +Function,+,stream_copy_full,size_t,"Stream*, Stream*" +Function,+,stream_delete,_Bool,"Stream*, size_t" +Function,+,stream_delete_and_insert,_Bool,"Stream*, size_t, StreamWriteCB, const void*" +Function,+,stream_delete_and_insert_char,_Bool,"Stream*, size_t, char" +Function,+,stream_delete_and_insert_cstring,_Bool,"Stream*, size_t, const char*" +Function,+,stream_delete_and_insert_format,_Bool,"Stream*, size_t, const char*, ..." +Function,+,stream_delete_and_insert_string,_Bool,"Stream*, size_t, FuriString*" +Function,+,stream_delete_and_insert_vaformat,_Bool,"Stream*, size_t, const char*, va_list" +Function,+,stream_dump_data,void,Stream* +Function,+,stream_eof,_Bool,Stream* +Function,+,stream_free,void,Stream* +Function,+,stream_insert,_Bool,"Stream*, const uint8_t*, size_t" +Function,+,stream_insert_char,_Bool,"Stream*, char" +Function,+,stream_insert_cstring,_Bool,"Stream*, const char*" +Function,+,stream_insert_format,_Bool,"Stream*, const char*, ..." +Function,+,stream_insert_string,_Bool,"Stream*, FuriString*" +Function,+,stream_insert_vaformat,_Bool,"Stream*, const char*, va_list" +Function,+,stream_load_from_file,size_t,"Stream*, Storage*, const char*" +Function,+,stream_read,size_t,"Stream*, uint8_t*, size_t" +Function,+,stream_read_line,_Bool,"Stream*, FuriString*" +Function,+,stream_rewind,_Bool,Stream* +Function,+,stream_save_to_file,size_t,"Stream*, Storage*, const char*, FS_OpenMode" +Function,+,stream_seek,_Bool,"Stream*, int32_t, StreamOffset" +Function,+,stream_seek_to_char,_Bool,"Stream*, char, StreamDirection" +Function,+,stream_size,size_t,Stream* +Function,+,stream_split,_Bool,"Stream*, Stream*, Stream*" +Function,+,stream_tell,size_t,Stream* +Function,+,stream_write,size_t,"Stream*, const uint8_t*, size_t" +Function,+,stream_write_char,size_t,"Stream*, char" +Function,+,stream_write_cstring,size_t,"Stream*, const char*" +Function,+,stream_write_format,size_t,"Stream*, const char*, ..." +Function,+,stream_write_string,size_t,"Stream*, FuriString*" +Function,+,stream_write_vaformat,size_t,"Stream*, const char*, va_list" +Function,-,strerror,char*,int +Function,-,strerror_l,char*,"int, locale_t" +Function,-,strerror_r,char*,"int, char*, size_t" +Function,+,string_stream_alloc,Stream*, +Function,-,strlcat,size_t,"char*, const char*, size_t" +Function,+,strlcpy,size_t,"char*, const char*, size_t" +Function,+,strlen,size_t,const char* +Function,-,strlwr,char*,char* +Function,+,strncasecmp,int,"const char*, const char*, size_t" +Function,-,strncasecmp_l,int,"const char*, const char*, size_t, locale_t" +Function,-,strncat,char*,"char*, const char*, size_t" +Function,+,strncmp,int,"const char*, const char*, size_t" +Function,+,strncpy,char*,"char*, const char*, size_t" +Function,-,strndup,char*,"const char*, size_t" +Function,-,strnlen,size_t,"const char*, size_t" +Function,-,strnstr,char*,"const char*, const char*, size_t" +Function,-,strpbrk,char*,"const char*, const char*" +Function,+,strrchr,char*,"const char*, int" +Function,-,strsep,char*,"char**, const char*" +Function,-,strsignal,char*,int +Function,+,strspn,size_t,"const char*, const char*" +Function,+,strstr,char*,"const char*, const char*" +Function,-,strtod,double,"const char*, char**" +Function,-,strtod_l,double,"const char*, char**, locale_t" +Function,+,strtof,float,"const char*, char**" +Function,-,strtof_l,float,"const char*, char**, locale_t" +Function,-,strtok,char*,"char*, const char*" +Function,-,strtok_r,char*,"char*, const char*, char**" +Function,+,strtol,long,"const char*, char**, int" +Function,-,strtol_l,long,"const char*, char**, int, locale_t" +Function,-,strtold,long double,"const char*, char**" +Function,-,strtold_l,long double,"const char*, char**, locale_t" +Function,-,strtoll,long long,"const char*, char**, int" +Function,-,strtoll_l,long long,"const char*, char**, int, locale_t" +Function,+,strtoul,unsigned long,"const char*, char**, int" +Function,-,strtoul_l,unsigned long,"const char*, char**, int, locale_t" +Function,+,strtoull,unsigned long long,"const char*, char**, int" +Function,-,strtoull_l,unsigned long long,"const char*, char**, int, locale_t" +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,+,submenu_add_item,void,"Submenu*, const char*, uint32_t, SubmenuItemCallback, void*" +Function,+,submenu_alloc,Submenu*, +Function,+,submenu_free,void,Submenu* +Function,+,submenu_get_view,View*,Submenu* +Function,+,submenu_reset,void,Submenu* +Function,+,submenu_set_header,void,"Submenu*, const char*" +Function,+,submenu_set_selected_item,void,"Submenu*, uint32_t" +Function,-,system,int,const char* +Function,-,tan,double,double +Function,-,tanf,float,float +Function,-,tanh,double,double +Function,-,tanhf,float,float +Function,-,tanhl,long double,long double +Function,-,tanl,long double,long double +Function,+,tar_archive_add_dir,_Bool,"TarArchive*, const char*, const char*" +Function,+,tar_archive_add_file,_Bool,"TarArchive*, const char*, const char*, const int32_t" +Function,+,tar_archive_alloc,TarArchive*,Storage* +Function,+,tar_archive_dir_add_element,_Bool,"TarArchive*, const char*" +Function,+,tar_archive_file_add_data_block,_Bool,"TarArchive*, const uint8_t*, const int32_t" +Function,+,tar_archive_file_add_header,_Bool,"TarArchive*, const char*, const int32_t" +Function,+,tar_archive_file_finalize,_Bool,TarArchive* +Function,+,tar_archive_finalize,_Bool,TarArchive* +Function,+,tar_archive_free,void,TarArchive* +Function,+,tar_archive_get_entries_count,int32_t,TarArchive* +Function,+,tar_archive_open,_Bool,"TarArchive*, const char*, TarOpenMode" +Function,+,tar_archive_set_file_callback,void,"TarArchive*, tar_unpack_file_cb, void*" +Function,+,tar_archive_store_data,_Bool,"TarArchive*, const char*, const uint8_t*, const int32_t" +Function,+,tar_archive_unpack_file,_Bool,"TarArchive*, const char*, const char*" +Function,+,tar_archive_unpack_to,_Bool,"TarArchive*, const char*, Storage_name_converter" +Function,-,tempnam,char*,"const char*, const char*" +Function,+,text_box_alloc,TextBox*, +Function,+,text_box_free,void,TextBox* +Function,+,text_box_get_view,View*,TextBox* +Function,+,text_box_reset,void,TextBox* +Function,+,text_box_set_focus,void,"TextBox*, TextBoxFocus" +Function,+,text_box_set_font,void,"TextBox*, TextBoxFont" +Function,+,text_box_set_text,void,"TextBox*, const char*" +Function,+,text_input_alloc,TextInput*, +Function,+,text_input_free,void,TextInput* +Function,+,text_input_get_validator_callback,TextInputValidatorCallback,TextInput* +Function,+,text_input_get_validator_callback_context,void*,TextInput* +Function,+,text_input_get_view,View*,TextInput* +Function,+,text_input_reset,void,TextInput* +Function,+,text_input_set_header_text,void,"TextInput*, const char*" +Function,+,text_input_set_result_callback,void,"TextInput*, TextInputCallback, void*, char*, size_t, _Bool" +Function,+,text_input_set_validator,void,"TextInput*, TextInputValidatorCallback, void*" +Function,-,tgamma,double,double +Function,-,tgammaf,float,float +Function,-,tgammal,long double,long double +Function,-,timingsafe_bcmp,int,"const void*, const void*, size_t" +Function,-,timingsafe_memcmp,int,"const void*, const void*, size_t" +Function,-,tmpfile,FILE*, +Function,-,tmpnam,char*,char* +Function,-,toascii,int,int +Function,-,toascii_l,int,"int, locale_t" +Function,-,tolower,int,int +Function,-,tolower_l,int,"int, locale_t" +Function,-,toupper,int,int +Function,-,toupper_l,int,"int, locale_t" +Function,-,trunc,double,double +Function,-,truncf,float,float +Function,-,truncl,long double,long double +Function,+,uint8_to_hex_chars,void,"const uint8_t*, uint8_t*, int" +Function,-,ungetc,int,"int, FILE*" +Function,-,unsetenv,int,const char* +Function,-,usbd_poll,void,usbd_device* +Function,-,utoa,char*,"unsigned, char*, int" +Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*" +Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" +Function,+,validator_is_file_free,void,ValidatorIsFile* +Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" +Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" +Function,+,value_index_int32,uint8_t,"const int32_t, const int32_t[], uint8_t" +Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" +Function,+,variable_item_get_context,void*,VariableItem* +Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* +Function,+,variable_item_list_add,VariableItem*,"VariableItemList*, const char*, uint8_t, VariableItemChangeCallback, void*" +Function,+,variable_item_list_alloc,VariableItemList*, +Function,+,variable_item_list_free,void,VariableItemList* +Function,+,variable_item_list_get_selected_item_index,uint8_t,VariableItemList* +Function,+,variable_item_list_get_view,View*,VariableItemList* +Function,+,variable_item_list_reset,void,VariableItemList* +Function,+,variable_item_list_set_enter_callback,void,"VariableItemList*, VariableItemListEnterCallback, void*" +Function,+,variable_item_list_set_selected_item,void,"VariableItemList*, uint8_t" +Function,+,variable_item_set_current_value_index,void,"VariableItem*, uint8_t" +Function,+,variable_item_set_current_value_text,void,"VariableItem*, const char*" +Function,+,variable_item_set_values_count,void,"VariableItem*, uint8_t" +Function,-,vasiprintf,int,"char**, const char*, __gnuc_va_list" +Function,-,vasniprintf,char*,"char*, size_t*, const char*, __gnuc_va_list" +Function,-,vasnprintf,char*,"char*, size_t*, const char*, __gnuc_va_list" +Function,-,vasprintf,int,"char**, const char*, __gnuc_va_list" +Function,-,vdiprintf,int,"int, const char*, __gnuc_va_list" +Function,-,vdprintf,int,"int, const char*, __gnuc_va_list" +Function,+,version_get,const Version*, +Function,+,version_get_builddate,const char*,const Version* +Function,+,version_get_dirty_flag,_Bool,const Version* +Function,+,version_get_firmware_origin,const char*,const Version* +Function,+,version_get_git_origin,const char*,const Version* +Function,+,version_get_gitbranch,const char*,const Version* +Function,+,version_get_gitbranchnum,const char*,const Version* +Function,+,version_get_githash,const char*,const Version* +Function,+,version_get_target,uint8_t,const Version* +Function,+,version_get_version,const char*,const Version* +Function,-,vfiprintf,int,"FILE*, const char*, __gnuc_va_list" +Function,-,vfiscanf,int,"FILE*, const char*, __gnuc_va_list" +Function,-,vfprintf,int,"FILE*, const char*, __gnuc_va_list" +Function,-,vfscanf,int,"FILE*, const char*, __gnuc_va_list" +Function,+,view_alloc,View*, +Function,+,view_allocate_model,void,"View*, ViewModelType, size_t" +Function,+,view_commit_model,void,"View*, _Bool" +Function,+,view_dispatcher_add_view,void,"ViewDispatcher*, uint32_t, View*" +Function,+,view_dispatcher_alloc,ViewDispatcher*, +Function,+,view_dispatcher_attach_to_gui,void,"ViewDispatcher*, Gui*, ViewDispatcherType" +Function,+,view_dispatcher_enable_queue,void,ViewDispatcher* +Function,+,view_dispatcher_free,void,ViewDispatcher* +Function,+,view_dispatcher_remove_view,void,"ViewDispatcher*, uint32_t" +Function,+,view_dispatcher_run,void,ViewDispatcher* +Function,+,view_dispatcher_send_custom_event,void,"ViewDispatcher*, uint32_t" +Function,+,view_dispatcher_send_to_back,void,ViewDispatcher* +Function,+,view_dispatcher_send_to_front,void,ViewDispatcher* +Function,+,view_dispatcher_set_custom_event_callback,void,"ViewDispatcher*, ViewDispatcherCustomEventCallback" +Function,+,view_dispatcher_set_event_callback_context,void,"ViewDispatcher*, void*" +Function,+,view_dispatcher_set_navigation_event_callback,void,"ViewDispatcher*, ViewDispatcherNavigationEventCallback" +Function,+,view_dispatcher_set_tick_event_callback,void,"ViewDispatcher*, ViewDispatcherTickEventCallback, uint32_t" +Function,+,view_dispatcher_stop,void,ViewDispatcher* +Function,+,view_dispatcher_switch_to_view,void,"ViewDispatcher*, uint32_t" +Function,+,view_free,void,View* +Function,+,view_free_model,void,View* +Function,+,view_get_model,void*,View* +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,const ViewPort* +Function,+,view_port_get_orientation,ViewPortOrientation,const 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,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" +Function,+,view_port_update,void,ViewPort* +Function,+,view_set_context,void,"View*, void*" +Function,+,view_set_custom_callback,void,"View*, ViewCustomCallback" +Function,+,view_set_draw_callback,void,"View*, ViewDrawCallback" +Function,+,view_set_enter_callback,void,"View*, ViewCallback" +Function,+,view_set_exit_callback,void,"View*, ViewCallback" +Function,+,view_set_input_callback,void,"View*, ViewInputCallback" +Function,+,view_set_orientation,void,"View*, ViewOrientation" +Function,+,view_set_previous_callback,void,"View*, ViewNavigationCallback" +Function,+,view_set_update_callback,void,"View*, ViewUpdateCallback" +Function,+,view_set_update_callback_context,void,"View*, void*" +Function,+,view_stack_add_view,void,"ViewStack*, View*" +Function,+,view_stack_alloc,ViewStack*, +Function,+,view_stack_free,void,ViewStack* +Function,+,view_stack_get_view,View*,ViewStack* +Function,+,view_stack_remove_view,void,"ViewStack*, View*" +Function,+,view_tie_icon_animation,void,"View*, IconAnimation*" +Function,-,viprintf,int,"const char*, __gnuc_va_list" +Function,-,viscanf,int,"const char*, __gnuc_va_list" +Function,-,vprintf,int,"const char*, __gnuc_va_list" +Function,-,vscanf,int,"const char*, __gnuc_va_list" +Function,-,vsiprintf,int,"char*, const char*, __gnuc_va_list" +Function,-,vsiscanf,int,"const char*, const char*, __gnuc_va_list" +Function,-,vsniprintf,int,"char*, size_t, const char*, __gnuc_va_list" +Function,-,vsnprintf,int,"char*, size_t, const char*, __gnuc_va_list" +Function,-,vsprintf,int,"char*, const char*, __gnuc_va_list" +Function,-,vsscanf,int,"const char*, const char*, __gnuc_va_list" +Function,-,wcstombs,size_t,"char*, const wchar_t*, size_t" +Function,-,wctomb,int,"char*, wchar_t" +Function,+,widget_add_button_element,void,"Widget*, GuiButtonType, const char*, ButtonCallback, void*" +Function,+,widget_add_frame_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,widget_add_icon_element,void,"Widget*, uint8_t, uint8_t, const Icon*" +Function,+,widget_add_string_element,void,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*" +Function,+,widget_add_string_multiline_element,void,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*" +Function,+,widget_add_text_box_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" +Function,+,widget_add_text_scroll_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, const char*" +Function,+,widget_alloc,Widget*, +Function,+,widget_free,void,Widget* +Function,+,widget_get_view,View*,Widget* +Function,+,widget_reset,void,Widget* +Function,-,y0,double,double +Function,-,y0f,float,float +Function,-,y1,double,double +Function,-,y1f,float,float +Function,-,yn,double,"int, double" +Function,-,ynf,float,"int, float" +Variable,-,AHBPrescTable,const uint32_t[16], +Variable,-,APBPrescTable,const uint32_t[8], +Variable,-,ITM_RxBuffer,volatile int32_t, +Variable,-,MSIRangeTable,const uint32_t[16], +Variable,-,SmpsPrescalerTable,const uint32_t[4][6], +Variable,+,SystemCoreClock,uint32_t, +Variable,+,_ctype_,const char[], +Variable,+,_global_impure_ptr,_reent*, +Variable,+,_impure_ptr,_reent*, +Variable,-,_sys_errlist,const char*[], +Variable,-,_sys_nerr,int, +Variable,+,cli_vcp,CliSession, +Variable,+,firmware_api_interface,const ElfApiInterface*, +Variable,+,furi_hal_i2c_bus_external,FuriHalI2cBus, +Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus, +Variable,+,furi_hal_i2c_handle_external,FuriHalI2cBusHandle, +Variable,+,furi_hal_i2c_handle_power,FuriHalI2cBusHandle, +Variable,+,furi_hal_spi_bus_d,FuriHalSpiBus, +Variable,+,furi_hal_spi_bus_handle_display,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_external,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_sd_fast,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_sd_slow,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_r,FuriHalSpiBus, +Variable,+,furi_hal_spi_preset_1edge_low_16m,const LL_SPI_InitTypeDef, +Variable,+,furi_hal_spi_preset_1edge_low_2m,const LL_SPI_InitTypeDef, +Variable,+,furi_hal_spi_preset_1edge_low_4m,const LL_SPI_InitTypeDef, +Variable,+,furi_hal_spi_preset_1edge_low_8m,const LL_SPI_InitTypeDef, +Variable,+,furi_hal_spi_preset_2edge_low_8m,const LL_SPI_InitTypeDef, +Variable,+,gpio_button_back,const GpioPin, +Variable,+,gpio_button_down,const GpioPin, +Variable,+,gpio_button_left,const GpioPin, +Variable,+,gpio_button_ok,const GpioPin, +Variable,+,gpio_button_right,const GpioPin, +Variable,+,gpio_button_up,const GpioPin, +Variable,+,gpio_display_cs,const GpioPin, +Variable,+,gpio_display_di,const GpioPin, +Variable,+,gpio_display_rst_n,const GpioPin, +Variable,+,gpio_ext_pa0,const GpioPin, +Variable,+,gpio_ext_pa1,const GpioPin, +Variable,+,gpio_ext_pa15,const GpioPin, +Variable,+,gpio_ext_pa2,const GpioPin, +Variable,+,gpio_ext_pa4,const GpioPin, +Variable,+,gpio_ext_pa5,const GpioPin, +Variable,+,gpio_ext_pa6,const GpioPin, +Variable,+,gpio_ext_pa7,const GpioPin, +Variable,+,gpio_ext_pb13,const GpioPin, +Variable,+,gpio_ext_pb2,const GpioPin, +Variable,+,gpio_ext_pb3,const GpioPin, +Variable,+,gpio_ext_pb4,const GpioPin, +Variable,+,gpio_ext_pb5,const GpioPin, +Variable,+,gpio_ext_pb9,const GpioPin, +Variable,+,gpio_ext_pc0,const GpioPin, +Variable,+,gpio_ext_pc1,const GpioPin, +Variable,+,gpio_ext_pc3,const GpioPin, +Variable,+,gpio_ext_pc4,const GpioPin, +Variable,+,gpio_ext_pc5,const GpioPin, +Variable,+,gpio_ext_pd0,const GpioPin, +Variable,+,gpio_ext_pe4,const GpioPin, +Variable,+,gpio_i2c_power_scl,const GpioPin, +Variable,+,gpio_i2c_power_sda,const GpioPin, +Variable,+,gpio_ibutton,const GpioPin, +Variable,+,gpio_periph_power,const GpioPin, +Variable,+,gpio_pins,const GpioPinRecord[], +Variable,+,gpio_pins_count,const size_t, +Variable,+,gpio_sdcard_cd,const GpioPin, +Variable,+,gpio_sdcard_cs,const GpioPin, +Variable,+,gpio_speaker,const GpioPin, +Variable,+,gpio_spi_d_miso,const GpioPin, +Variable,+,gpio_spi_d_mosi,const GpioPin, +Variable,+,gpio_spi_d_sck,const GpioPin, +Variable,+,gpio_swclk,const GpioPin, +Variable,+,gpio_swdio,const GpioPin, +Variable,+,gpio_usart_rx,const GpioPin, +Variable,+,gpio_usart_tx,const GpioPin, +Variable,+,gpio_usb_dm,const GpioPin, +Variable,+,gpio_usb_dp,const GpioPin, +Variable,+,gpio_vibro,const GpioPin, +Variable,+,input_pins,const InputPin[], +Variable,+,input_pins_count,const size_t, +Variable,+,message_blink_set_color_blue,const NotificationMessage, +Variable,+,message_blink_set_color_cyan,const NotificationMessage, +Variable,+,message_blink_set_color_green,const NotificationMessage, +Variable,+,message_blink_set_color_magenta,const NotificationMessage, +Variable,+,message_blink_set_color_red,const NotificationMessage, +Variable,+,message_blink_set_color_white,const NotificationMessage, +Variable,+,message_blink_set_color_yellow,const NotificationMessage, +Variable,+,message_blink_start_10,const NotificationMessage, +Variable,+,message_blink_start_100,const NotificationMessage, +Variable,+,message_blink_stop,const NotificationMessage, +Variable,+,message_blue_0,const NotificationMessage, +Variable,+,message_blue_255,const NotificationMessage, +Variable,+,message_click,const NotificationMessage, +Variable,+,message_delay_1,const NotificationMessage, +Variable,+,message_delay_10,const NotificationMessage, +Variable,+,message_delay_100,const NotificationMessage, +Variable,+,message_delay_1000,const NotificationMessage, +Variable,+,message_delay_25,const NotificationMessage, +Variable,+,message_delay_250,const NotificationMessage, +Variable,+,message_delay_50,const NotificationMessage, +Variable,+,message_delay_500,const NotificationMessage, +Variable,+,message_display_backlight_enforce_auto,const NotificationMessage, +Variable,+,message_display_backlight_enforce_on,const NotificationMessage, +Variable,+,message_display_backlight_off,const NotificationMessage, +Variable,+,message_display_backlight_on,const NotificationMessage, +Variable,+,message_do_not_reset,const NotificationMessage, +Variable,+,message_force_display_brightness_setting_1f,const NotificationMessage, +Variable,+,message_force_speaker_volume_setting_1f,const NotificationMessage, +Variable,+,message_force_vibro_setting_off,const NotificationMessage, +Variable,+,message_force_vibro_setting_on,const NotificationMessage, +Variable,+,message_green_0,const NotificationMessage, +Variable,+,message_green_255,const NotificationMessage, +Variable,+,message_lcd_contrast_update,const NotificationMessage, +Variable,+,message_note_a0,const NotificationMessage, +Variable,+,message_note_a1,const NotificationMessage, +Variable,+,message_note_a2,const NotificationMessage, +Variable,+,message_note_a3,const NotificationMessage, +Variable,+,message_note_a4,const NotificationMessage, +Variable,+,message_note_a5,const NotificationMessage, +Variable,+,message_note_a6,const NotificationMessage, +Variable,+,message_note_a7,const NotificationMessage, +Variable,+,message_note_a8,const NotificationMessage, +Variable,+,message_note_as0,const NotificationMessage, +Variable,+,message_note_as1,const NotificationMessage, +Variable,+,message_note_as2,const NotificationMessage, +Variable,+,message_note_as3,const NotificationMessage, +Variable,+,message_note_as4,const NotificationMessage, +Variable,+,message_note_as5,const NotificationMessage, +Variable,+,message_note_as6,const NotificationMessage, +Variable,+,message_note_as7,const NotificationMessage, +Variable,+,message_note_as8,const NotificationMessage, +Variable,+,message_note_b0,const NotificationMessage, +Variable,+,message_note_b1,const NotificationMessage, +Variable,+,message_note_b2,const NotificationMessage, +Variable,+,message_note_b3,const NotificationMessage, +Variable,+,message_note_b4,const NotificationMessage, +Variable,+,message_note_b5,const NotificationMessage, +Variable,+,message_note_b6,const NotificationMessage, +Variable,+,message_note_b7,const NotificationMessage, +Variable,+,message_note_b8,const NotificationMessage, +Variable,+,message_note_c0,const NotificationMessage, +Variable,+,message_note_c1,const NotificationMessage, +Variable,+,message_note_c2,const NotificationMessage, +Variable,+,message_note_c3,const NotificationMessage, +Variable,+,message_note_c4,const NotificationMessage, +Variable,+,message_note_c5,const NotificationMessage, +Variable,+,message_note_c6,const NotificationMessage, +Variable,+,message_note_c7,const NotificationMessage, +Variable,+,message_note_c8,const NotificationMessage, +Variable,+,message_note_cs0,const NotificationMessage, +Variable,+,message_note_cs1,const NotificationMessage, +Variable,+,message_note_cs2,const NotificationMessage, +Variable,+,message_note_cs3,const NotificationMessage, +Variable,+,message_note_cs4,const NotificationMessage, +Variable,+,message_note_cs5,const NotificationMessage, +Variable,+,message_note_cs6,const NotificationMessage, +Variable,+,message_note_cs7,const NotificationMessage, +Variable,+,message_note_cs8,const NotificationMessage, +Variable,+,message_note_d0,const NotificationMessage, +Variable,+,message_note_d1,const NotificationMessage, +Variable,+,message_note_d2,const NotificationMessage, +Variable,+,message_note_d3,const NotificationMessage, +Variable,+,message_note_d4,const NotificationMessage, +Variable,+,message_note_d5,const NotificationMessage, +Variable,+,message_note_d6,const NotificationMessage, +Variable,+,message_note_d7,const NotificationMessage, +Variable,+,message_note_d8,const NotificationMessage, +Variable,+,message_note_ds0,const NotificationMessage, +Variable,+,message_note_ds1,const NotificationMessage, +Variable,+,message_note_ds2,const NotificationMessage, +Variable,+,message_note_ds3,const NotificationMessage, +Variable,+,message_note_ds4,const NotificationMessage, +Variable,+,message_note_ds5,const NotificationMessage, +Variable,+,message_note_ds6,const NotificationMessage, +Variable,+,message_note_ds7,const NotificationMessage, +Variable,+,message_note_ds8,const NotificationMessage, +Variable,+,message_note_e0,const NotificationMessage, +Variable,+,message_note_e1,const NotificationMessage, +Variable,+,message_note_e2,const NotificationMessage, +Variable,+,message_note_e3,const NotificationMessage, +Variable,+,message_note_e4,const NotificationMessage, +Variable,+,message_note_e5,const NotificationMessage, +Variable,+,message_note_e6,const NotificationMessage, +Variable,+,message_note_e7,const NotificationMessage, +Variable,+,message_note_e8,const NotificationMessage, +Variable,+,message_note_f0,const NotificationMessage, +Variable,+,message_note_f1,const NotificationMessage, +Variable,+,message_note_f2,const NotificationMessage, +Variable,+,message_note_f3,const NotificationMessage, +Variable,+,message_note_f4,const NotificationMessage, +Variable,+,message_note_f5,const NotificationMessage, +Variable,+,message_note_f6,const NotificationMessage, +Variable,+,message_note_f7,const NotificationMessage, +Variable,+,message_note_f8,const NotificationMessage, +Variable,+,message_note_fs0,const NotificationMessage, +Variable,+,message_note_fs1,const NotificationMessage, +Variable,+,message_note_fs2,const NotificationMessage, +Variable,+,message_note_fs3,const NotificationMessage, +Variable,+,message_note_fs4,const NotificationMessage, +Variable,+,message_note_fs5,const NotificationMessage, +Variable,+,message_note_fs6,const NotificationMessage, +Variable,+,message_note_fs7,const NotificationMessage, +Variable,+,message_note_fs8,const NotificationMessage, +Variable,+,message_note_g0,const NotificationMessage, +Variable,+,message_note_g1,const NotificationMessage, +Variable,+,message_note_g2,const NotificationMessage, +Variable,+,message_note_g3,const NotificationMessage, +Variable,+,message_note_g4,const NotificationMessage, +Variable,+,message_note_g5,const NotificationMessage, +Variable,+,message_note_g6,const NotificationMessage, +Variable,+,message_note_g7,const NotificationMessage, +Variable,+,message_note_g8,const NotificationMessage, +Variable,+,message_note_gs0,const NotificationMessage, +Variable,+,message_note_gs1,const NotificationMessage, +Variable,+,message_note_gs2,const NotificationMessage, +Variable,+,message_note_gs3,const NotificationMessage, +Variable,+,message_note_gs4,const NotificationMessage, +Variable,+,message_note_gs5,const NotificationMessage, +Variable,+,message_note_gs6,const NotificationMessage, +Variable,+,message_note_gs7,const NotificationMessage, +Variable,+,message_note_gs8,const NotificationMessage, +Variable,+,message_red_0,const NotificationMessage, +Variable,+,message_red_255,const NotificationMessage, +Variable,+,message_sound_off,const NotificationMessage, +Variable,+,message_vibro_off,const NotificationMessage, +Variable,+,message_vibro_on,const NotificationMessage, +Variable,+,sequence_audiovisual_alert,const NotificationSequence, +Variable,+,sequence_blink_blue_10,const NotificationSequence, +Variable,+,sequence_blink_blue_100,const NotificationSequence, +Variable,+,sequence_blink_cyan_10,const NotificationSequence, +Variable,+,sequence_blink_cyan_100,const NotificationSequence, +Variable,+,sequence_blink_green_10,const NotificationSequence, +Variable,+,sequence_blink_green_100,const NotificationSequence, +Variable,+,sequence_blink_magenta_10,const NotificationSequence, +Variable,+,sequence_blink_magenta_100,const NotificationSequence, +Variable,+,sequence_blink_red_10,const NotificationSequence, +Variable,+,sequence_blink_red_100,const NotificationSequence, +Variable,+,sequence_blink_start_blue,const NotificationSequence, +Variable,+,sequence_blink_start_cyan,const NotificationSequence, +Variable,+,sequence_blink_start_green,const NotificationSequence, +Variable,+,sequence_blink_start_magenta,const NotificationSequence, +Variable,+,sequence_blink_start_red,const NotificationSequence, +Variable,+,sequence_blink_start_yellow,const NotificationSequence, +Variable,+,sequence_blink_stop,const NotificationSequence, +Variable,+,sequence_blink_white_100,const NotificationSequence, +Variable,+,sequence_blink_yellow_10,const NotificationSequence, +Variable,+,sequence_blink_yellow_100,const NotificationSequence, +Variable,+,sequence_charged,const NotificationSequence, +Variable,+,sequence_charging,const NotificationSequence, +Variable,+,sequence_display_backlight_enforce_auto,const NotificationSequence, +Variable,+,sequence_display_backlight_enforce_on,const NotificationSequence, +Variable,+,sequence_display_backlight_off,const NotificationSequence, +Variable,+,sequence_display_backlight_off_delay_1000,const NotificationSequence, +Variable,+,sequence_display_backlight_on,const NotificationSequence, +Variable,+,sequence_double_vibro,const NotificationSequence, +Variable,+,sequence_error,const NotificationSequence, +Variable,+,sequence_lcd_contrast_update,const NotificationSequence, +Variable,+,sequence_not_charging,const NotificationSequence, +Variable,+,sequence_reset_blue,const NotificationSequence, +Variable,+,sequence_reset_display,const NotificationSequence, +Variable,+,sequence_reset_green,const NotificationSequence, +Variable,+,sequence_reset_red,const NotificationSequence, +Variable,+,sequence_reset_rgb,const NotificationSequence, +Variable,+,sequence_reset_sound,const NotificationSequence, +Variable,+,sequence_reset_vibro,const NotificationSequence, +Variable,+,sequence_set_blue_255,const NotificationSequence, +Variable,+,sequence_set_green_255,const NotificationSequence, +Variable,+,sequence_set_only_blue_255,const NotificationSequence, +Variable,+,sequence_set_only_green_255,const NotificationSequence, +Variable,+,sequence_set_only_red_255,const NotificationSequence, +Variable,+,sequence_set_red_255,const NotificationSequence, +Variable,+,sequence_set_vibro_on,const NotificationSequence, +Variable,+,sequence_single_vibro,const NotificationSequence, +Variable,+,sequence_solid_yellow,const NotificationSequence, +Variable,+,sequence_success,const NotificationSequence, +Variable,+,simple_array_config_uint8_t,const SimpleArrayConfig, +Variable,-,suboptarg,char*, +Variable,+,usb_ccid,FuriHalUsbInterface, +Variable,+,usb_cdc_dual,FuriHalUsbInterface, +Variable,+,usb_cdc_single,FuriHalUsbInterface, +Variable,+,usb_hid,FuriHalUsbInterface, +Variable,+,usb_hid_u2f,FuriHalUsbInterface, +Variable,+,usbd_devfs,const usbd_driver, diff --git a/targets/f18/furi_hal/furi_hal.c b/targets/f18/furi_hal/furi_hal.c new file mode 100644 index 00000000000..5f4e6165dca --- /dev/null +++ b/targets/f18/furi_hal/furi_hal.c @@ -0,0 +1,67 @@ +#include +#include +#include + +#include + +#define TAG "FuriHal" + +void furi_hal_init_early() { + furi_hal_cortex_init_early(); + furi_hal_clock_init_early(); + furi_hal_bus_init_early(); + furi_hal_dma_init_early(); + furi_hal_resources_init_early(); + furi_hal_os_init(); + furi_hal_spi_config_init_early(); + furi_hal_i2c_init_early(); + furi_hal_light_init(); + furi_hal_rtc_init_early(); +} + +void furi_hal_deinit_early() { + furi_hal_rtc_deinit_early(); + furi_hal_i2c_deinit_early(); + furi_hal_spi_config_deinit_early(); + furi_hal_resources_deinit_early(); + furi_hal_dma_deinit_early(); + furi_hal_bus_deinit_early(); + furi_hal_clock_deinit_early(); +} + +void furi_hal_init() { + furi_hal_mpu_init(); + furi_hal_clock_init(); + furi_hal_random_init(); + furi_hal_console_init(); + furi_hal_rtc_init(); + furi_hal_interrupt_init(); + furi_hal_flash_init(); + furi_hal_resources_init(); + furi_hal_version_init(); + furi_hal_spi_config_init(); + furi_hal_spi_dma_init(); + furi_hal_speaker_init(); + furi_hal_crypto_init(); + furi_hal_i2c_init(); + furi_hal_power_init(); + furi_hal_light_init(); + furi_hal_bt_init(); + furi_hal_memory_init(); + +#ifndef FURI_RAM_EXEC + furi_hal_usb_init(); + furi_hal_vibro_init(); +#endif +} + +void furi_hal_switch(void* address) { + __set_BASEPRI(0); + asm volatile("ldr r3, [%0] \n" + "msr msp, r3 \n" + "ldr r3, [%1] \n" + "mov pc, r3 \n" + : + : "r"(address), "r"(address + 0x4) + : "r3"); +} diff --git a/targets/f18/furi_hal/furi_hal_power_config.c b/targets/f18/furi_hal/furi_hal_power_config.c new file mode 100644 index 00000000000..50efceb7de3 --- /dev/null +++ b/targets/f18/furi_hal/furi_hal_power_config.c @@ -0,0 +1,149 @@ +#include + +const BQ27220DMGaugingConfig furi_hal_power_gauge_data_memory_gauging_config = { + .CCT = 1, + .CSYNC = 0, + .EDV_CMP = 0, + .SC = 1, + .FIXED_EDV0 = 1, + .FCC_LIM = 1, + .FC_FOR_VDQ = 1, + .IGNORE_SD = 1, + .SME0 = 0, +}; + +const BQ27220DMData furi_hal_power_gauge_data_memory[] = { + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1GaugingConfig, + .type = BQ27220DMTypePtr16, + .value.u32 = (uint32_t)&furi_hal_power_gauge_data_memory_gauging_config, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1FullChargeCapacity, + .type = BQ27220DMTypeU16, + .value.u16 = 1300, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1DesignCapacity, + .type = BQ27220DMTypeU16, + .value.u16 = 1300, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1EMF, + .type = BQ27220DMTypeU16, + .value.u16 = 3679, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1C0, + .type = BQ27220DMTypeU16, + .value.u16 = 430, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1R0, + .type = BQ27220DMTypeU16, + .value.u16 = 334, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1T0, + .type = BQ27220DMTypeU16, + .value.u16 = 4626, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1R1, + .type = BQ27220DMTypeU16, + .value.u16 = 408, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1TC, + .type = BQ27220DMTypeU8, + .value.u8 = 11, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1C1, + .type = BQ27220DMTypeU8, + .value.u8 = 0, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD0, + .type = BQ27220DMTypeU16, + .value.u16 = 4044, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD10, + .type = BQ27220DMTypeU16, + .value.u16 = 3905, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD20, + .type = BQ27220DMTypeU16, + .value.u16 = 3807, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD30, + .type = BQ27220DMTypeU16, + .value.u16 = 3718, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD40, + .type = BQ27220DMTypeU16, + .value.u16 = 3642, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD50, + .type = BQ27220DMTypeU16, + .value.u16 = 3585, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD60, + .type = BQ27220DMTypeU16, + .value.u16 = 3546, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD70, + .type = BQ27220DMTypeU16, + .value.u16 = 3514, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD80, + .type = BQ27220DMTypeU16, + .value.u16 = 3477, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD90, + .type = BQ27220DMTypeU16, + .value.u16 = 3411, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD100, + .type = BQ27220DMTypeU16, + .value.u16 = 3299, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1EDV0, + .type = BQ27220DMTypeU16, + .value.u16 = 3300, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1EDV1, + .type = BQ27220DMTypeU16, + .value.u16 = 3321, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1EDV2, + .type = BQ27220DMTypeU16, + .value.u16 = 3355, + }, + { + .address = BQ27220DMAddressCalibrationCurrentDeadband, + .type = BQ27220DMTypeU8, + .value.u8 = 1, + }, + { + .address = BQ27220DMAddressConfigurationPowerSleepCurrent, + .type = BQ27220DMTypeI16, + .value.i16 = 1, + }, + { + .type = BQ27220DMTypeEnd, + }, +}; diff --git a/targets/f18/furi_hal/furi_hal_resources.c b/targets/f18/furi_hal/furi_hal_resources.c new file mode 100644 index 00000000000..11893587485 --- /dev/null +++ b/targets/f18/furi_hal/furi_hal_resources.c @@ -0,0 +1,244 @@ +#include +#include +#include + +#include +#include + +#define TAG "FuriHalResources" + +const GpioPin gpio_swdio = {.port = GPIOA, .pin = LL_GPIO_PIN_13}; +const GpioPin gpio_swclk = {.port = GPIOA, .pin = LL_GPIO_PIN_14}; + +const GpioPin gpio_vibro = {.port = GPIOA, .pin = LL_GPIO_PIN_8}; +const GpioPin gpio_ibutton = {.port = GPIOB, .pin = LL_GPIO_PIN_14}; + +const GpioPin gpio_display_cs = {.port = GPIOC, .pin = LL_GPIO_PIN_11}; +const GpioPin gpio_display_rst_n = {.port = GPIOB, .pin = LL_GPIO_PIN_0}; +const GpioPin gpio_display_di = {.port = GPIOB, .pin = LL_GPIO_PIN_1}; +const GpioPin gpio_sdcard_cs = {.port = GPIOC, .pin = LL_GPIO_PIN_12}; +const GpioPin gpio_sdcard_cd = {.port = GPIOC, .pin = LL_GPIO_PIN_10}; + +const GpioPin gpio_button_up = {.port = GPIOB, .pin = LL_GPIO_PIN_10}; +const GpioPin gpio_button_down = {.port = GPIOC, .pin = LL_GPIO_PIN_6}; +const GpioPin gpio_button_right = {.port = GPIOB, .pin = LL_GPIO_PIN_12}; +const GpioPin gpio_button_left = {.port = GPIOB, .pin = LL_GPIO_PIN_11}; +const GpioPin gpio_button_ok = {.port = GPIOH, .pin = LL_GPIO_PIN_3}; +const GpioPin gpio_button_back = {.port = GPIOC, .pin = LL_GPIO_PIN_13}; + +const GpioPin gpio_spi_d_miso = {.port = GPIOC, .pin = LL_GPIO_PIN_2}; +const GpioPin gpio_spi_d_mosi = {.port = GPIOB, .pin = LL_GPIO_PIN_15}; +const GpioPin gpio_spi_d_sck = {.port = GPIOD, .pin = LL_GPIO_PIN_1}; + +const GpioPin gpio_ext_pc0 = {.port = GPIOC, .pin = LL_GPIO_PIN_0}; +const GpioPin gpio_ext_pc1 = {.port = GPIOC, .pin = LL_GPIO_PIN_1}; +const GpioPin gpio_ext_pc3 = {.port = GPIOC, .pin = LL_GPIO_PIN_3}; +const GpioPin gpio_ext_pb2 = {.port = GPIOB, .pin = LL_GPIO_PIN_2}; +const GpioPin gpio_ext_pb3 = {.port = GPIOB, .pin = LL_GPIO_PIN_3}; +const GpioPin gpio_ext_pa4 = {.port = GPIOA, .pin = LL_GPIO_PIN_4}; +const GpioPin gpio_ext_pa6 = {.port = GPIOA, .pin = LL_GPIO_PIN_6}; +const GpioPin gpio_ext_pa7 = {.port = GPIOA, .pin = LL_GPIO_PIN_7}; + +const GpioPin gpio_ext_pc5 = {.port = GPIOC, .pin = LL_GPIO_PIN_5}; +const GpioPin gpio_ext_pc4 = {.port = GPIOC, .pin = LL_GPIO_PIN_4}; +const GpioPin gpio_ext_pa5 = {.port = GPIOA, .pin = LL_GPIO_PIN_5}; +const GpioPin gpio_ext_pb9 = {.port = GPIOB, .pin = LL_GPIO_PIN_9}; +const GpioPin gpio_ext_pa0 = {.port = GPIOA, .pin = LL_GPIO_PIN_0}; +const GpioPin gpio_ext_pa1 = {.port = GPIOA, .pin = LL_GPIO_PIN_1}; +const GpioPin gpio_ext_pa15 = {.port = GPIOA, .pin = LL_GPIO_PIN_15}; +const GpioPin gpio_ext_pe4 = {.port = GPIOE, .pin = LL_GPIO_PIN_4}; +const GpioPin gpio_ext_pa2 = {.port = GPIOA, .pin = LL_GPIO_PIN_2}; +const GpioPin gpio_ext_pb4 = {.port = GPIOB, .pin = LL_GPIO_PIN_4}; +const GpioPin gpio_ext_pb5 = {.port = GPIOB, .pin = LL_GPIO_PIN_5}; +const GpioPin gpio_ext_pd0 = {.port = GPIOD, .pin = LL_GPIO_PIN_0}; +const GpioPin gpio_ext_pb13 = {.port = GPIOB, .pin = LL_GPIO_PIN_13}; + +const GpioPin gpio_usart_tx = {.port = GPIOB, .pin = LL_GPIO_PIN_6}; +const GpioPin gpio_usart_rx = {.port = GPIOB, .pin = LL_GPIO_PIN_7}; + +const GpioPin gpio_i2c_power_sda = {.port = GPIOA, .pin = LL_GPIO_PIN_10}; +const GpioPin gpio_i2c_power_scl = {.port = GPIOA, .pin = LL_GPIO_PIN_9}; + +const GpioPin gpio_speaker = {.port = GPIOB, .pin = LL_GPIO_PIN_8}; + +const GpioPin gpio_periph_power = {.port = GPIOA, .pin = LL_GPIO_PIN_3}; + +const GpioPin gpio_usb_dm = {.port = GPIOA, .pin = LL_GPIO_PIN_11}; +const GpioPin gpio_usb_dp = {.port = GPIOA, .pin = LL_GPIO_PIN_12}; + +const GpioPinRecord gpio_pins[] = { + // 5V: 1 + {.pin = &gpio_ext_pa7, .name = "PA7", .number = 2, .debug = false}, + {.pin = &gpio_ext_pa6, .name = "PA6", .number = 3, .debug = false}, + {.pin = &gpio_ext_pa4, .name = "PA4", .number = 4, .debug = false}, + {.pin = &gpio_ext_pb3, .name = "PB3", .number = 5, .debug = false}, + {.pin = &gpio_ext_pb2, .name = "PB2", .number = 6, .debug = false}, + {.pin = &gpio_ext_pc3, .name = "PC3", .number = 7, .debug = false}, + // GND: 8 + // Space + // 3v3: 9 + {.pin = &gpio_swclk, .name = "PA14", .number = 10, .debug = true}, + // GND: 11 + {.pin = &gpio_swdio, .name = "PA13", .number = 12, .debug = true}, + {.pin = &gpio_usart_tx, .name = "PB6", .number = 13, .debug = true}, + {.pin = &gpio_usart_rx, .name = "PB7", .number = 14, .debug = true}, + {.pin = &gpio_ext_pc1, .name = "PC1", .number = 15, .debug = false}, + {.pin = &gpio_ext_pc0, .name = "PC0", .number = 16, .debug = false}, + {.pin = &gpio_ibutton, .name = "PB14", .number = 17, .debug = true}, + // GND: 18 + + // 2nd column + // 5V: 19 + {.pin = &gpio_ext_pc5, .name = "PC5", .number = 20, .debug = false}, + {.pin = &gpio_ext_pc4, .name = "PC4", .number = 21, .debug = false}, + {.pin = &gpio_ext_pa5, .name = "PA5", .number = 22, .debug = false}, + {.pin = &gpio_ext_pb9, .name = "PB9", .number = 23, .debug = false}, + {.pin = &gpio_ext_pa0, .name = "PA0", .number = 24, .debug = false}, + {.pin = &gpio_ext_pa1, .name = "PA1", .number = 25, .debug = false}, + // KEY: 26 + // Space + // 3v3: 27 + {.pin = &gpio_ext_pa15, .name = "PA15", .number = 28, .debug = false}, + // GND: 29 + {.pin = &gpio_ext_pe4, .name = "PE4", .number = 30, .debug = false}, + {.pin = &gpio_ext_pa2, .name = "PA2", .number = 31, .debug = false}, + {.pin = &gpio_ext_pb4, .name = "PB4", .number = 32, .debug = false}, + {.pin = &gpio_ext_pb5, .name = "PB5", .number = 33, .debug = false}, + {.pin = &gpio_ext_pd0, .name = "PD0", .number = 34, .debug = false}, + {.pin = &gpio_ext_pb13, .name = "PB13", .number = 35, .debug = false}, + // GND: 36 + + /* Dangerous pins, may damage hardware */ + {.pin = &gpio_usart_rx, .name = "PB7", .number = 0, .debug = true}, + {.pin = &gpio_speaker, .name = "PB8", .number = 0, .debug = true}, +}; + +const size_t gpio_pins_count = COUNT_OF(gpio_pins); + +const InputPin input_pins[] = { + {.gpio = &gpio_button_up, .key = InputKeyUp, .inverted = true, .name = "Up"}, + {.gpio = &gpio_button_down, .key = InputKeyDown, .inverted = true, .name = "Down"}, + {.gpio = &gpio_button_right, .key = InputKeyRight, .inverted = true, .name = "Right"}, + {.gpio = &gpio_button_left, .key = InputKeyLeft, .inverted = true, .name = "Left"}, + {.gpio = &gpio_button_ok, .key = InputKeyOk, .inverted = false, .name = "OK"}, + {.gpio = &gpio_button_back, .key = InputKeyBack, .inverted = true, .name = "Back"}, +}; + +const size_t input_pins_count = COUNT_OF(input_pins); + +static void furi_hal_resources_init_input_pins(GpioMode mode) { + for(size_t i = 0; i < input_pins_count; i++) { + furi_hal_gpio_init( + input_pins[i].gpio, + mode, + (input_pins[i].inverted) ? GpioPullUp : GpioPullDown, + GpioSpeedLow); + } +} + +static void furi_hal_resources_init_gpio_pins(GpioMode mode) { + for(size_t i = 0; i < gpio_pins_count; i++) { + if(!gpio_pins[i].debug) { + furi_hal_gpio_init(gpio_pins[i].pin, mode, GpioPullNo, GpioSpeedLow); + } + } +} + +void furi_hal_resources_init_early() { + furi_hal_bus_enable(FuriHalBusGPIOA); + furi_hal_bus_enable(FuriHalBusGPIOB); + furi_hal_bus_enable(FuriHalBusGPIOC); + furi_hal_bus_enable(FuriHalBusGPIOD); + furi_hal_bus_enable(FuriHalBusGPIOE); + furi_hal_bus_enable(FuriHalBusGPIOH); + + furi_hal_resources_init_input_pins(GpioModeInput); + + // Explicit, surviving reset, pulls + LL_PWR_EnablePUPDCfg(); + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_A, LL_PWR_GPIO_BIT_8); // gpio_vibro + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_8); // gpio_speaker + + // SD Card stepdown control + furi_hal_gpio_write(&gpio_periph_power, 1); + furi_hal_gpio_init(&gpio_periph_power, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); + + // Display pins + furi_hal_gpio_write(&gpio_display_rst_n, 0); + furi_hal_gpio_init_simple(&gpio_display_rst_n, GpioModeOutputPushPull); + LL_PWR_EnableGPIOPullUp(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_0); // gpio_display_rst_n + furi_hal_gpio_write(&gpio_display_di, 0); + furi_hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull); + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_1); // gpio_display_di + + // Hard reset USB + furi_hal_gpio_write(&gpio_usb_dm, 1); + furi_hal_gpio_write(&gpio_usb_dp, 1); + furi_hal_gpio_init_simple(&gpio_usb_dm, GpioModeOutputOpenDrain); + furi_hal_gpio_init_simple(&gpio_usb_dp, GpioModeOutputOpenDrain); + furi_hal_gpio_write(&gpio_usb_dm, 0); + furi_hal_gpio_write(&gpio_usb_dp, 0); + furi_delay_us(5); // Device Driven disconnect: 2.5us + extra to compensate cables + furi_hal_gpio_write(&gpio_usb_dm, 1); + furi_hal_gpio_write(&gpio_usb_dp, 1); + furi_hal_gpio_init_simple(&gpio_usb_dm, GpioModeAnalog); + furi_hal_gpio_init_simple(&gpio_usb_dp, GpioModeAnalog); + furi_hal_gpio_write(&gpio_usb_dm, 0); + furi_hal_gpio_write(&gpio_usb_dp, 0); + + // External header pins + furi_hal_resources_init_gpio_pins(GpioModeAnalog); +} + +void furi_hal_resources_deinit_early() { + furi_hal_resources_init_input_pins(GpioModeAnalog); + furi_hal_bus_disable(FuriHalBusGPIOA); + furi_hal_bus_disable(FuriHalBusGPIOB); + furi_hal_bus_disable(FuriHalBusGPIOC); + furi_hal_bus_disable(FuriHalBusGPIOD); + furi_hal_bus_disable(FuriHalBusGPIOE); + furi_hal_bus_disable(FuriHalBusGPIOH); +} + +void furi_hal_resources_init() { + // Button pins + furi_hal_resources_init_input_pins(GpioModeInterruptRiseFall); + + // SD pins + furi_hal_gpio_init(&gpio_sdcard_cd, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_sdcard_cd, 0); + + furi_hal_gpio_init(&gpio_ibutton, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + NVIC_SetPriority(EXTI0_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI0_IRQn); + + NVIC_SetPriority(EXTI1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI1_IRQn); + + NVIC_SetPriority(EXTI2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI2_IRQn); + + NVIC_SetPriority(EXTI3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI3_IRQn); + + NVIC_SetPriority(EXTI4_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI4_IRQn); + + NVIC_SetPriority(EXTI9_5_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI9_5_IRQn); + + NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI15_10_IRQn); + + FURI_LOG_I(TAG, "Init OK"); +} + +int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) { + for(size_t i = 0; i < gpio_pins_count; i++) { + if(gpio_pins[i].pin == gpio) { + return gpio_pins[i].number; + } + } + return -1; +} diff --git a/targets/f18/furi_hal/furi_hal_resources.h b/targets/f18/furi_hal/furi_hal_resources.h new file mode 100644 index 00000000000..fed7802a585 --- /dev/null +++ b/targets/f18/furi_hal/furi_hal_resources.h @@ -0,0 +1,127 @@ +#pragma once + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Input Related Constants */ +#define INPUT_DEBOUNCE_TICKS 4 + +/* Input Keys */ +typedef enum { + InputKeyUp, + InputKeyDown, + InputKeyRight, + InputKeyLeft, + InputKeyOk, + InputKeyBack, + InputKeyMAX, /**< Special value */ +} InputKey; + +/* Light */ +typedef enum { + LightRed = (1 << 0), + LightGreen = (1 << 1), + LightBlue = (1 << 2), + LightBacklight = (1 << 3), +} Light; + +typedef struct { + const GpioPin* gpio; + const InputKey key; + const bool inverted; + const char* name; +} InputPin; + +typedef struct { + const GpioPin* pin; + const char* name; + const uint8_t number; + const bool debug; +} GpioPinRecord; + +extern const InputPin input_pins[]; +extern const size_t input_pins_count; + +extern const GpioPinRecord gpio_pins[]; +extern const size_t gpio_pins_count; + +extern const GpioPin gpio_swdio; +extern const GpioPin gpio_swclk; + +extern const GpioPin gpio_vibro; +extern const GpioPin gpio_ibutton; + +extern const GpioPin gpio_display_cs; +extern const GpioPin gpio_display_rst_n; +extern const GpioPin gpio_display_di; +extern const GpioPin gpio_sdcard_cs; +extern const GpioPin gpio_sdcard_cd; + +extern const GpioPin gpio_button_up; +extern const GpioPin gpio_button_down; +extern const GpioPin gpio_button_right; +extern const GpioPin gpio_button_left; +extern const GpioPin gpio_button_ok; +extern const GpioPin gpio_button_back; + +extern const GpioPin gpio_spi_d_miso; +extern const GpioPin gpio_spi_d_mosi; +extern const GpioPin gpio_spi_d_sck; + +extern const GpioPin gpio_ext_pc0; +extern const GpioPin gpio_ext_pc1; +extern const GpioPin gpio_ext_pc3; +extern const GpioPin gpio_ext_pb2; +extern const GpioPin gpio_ext_pb3; +extern const GpioPin gpio_ext_pa4; +extern const GpioPin gpio_ext_pa6; +extern const GpioPin gpio_ext_pa7; + +extern const GpioPin gpio_ext_pc5; +extern const GpioPin gpio_ext_pc4; +extern const GpioPin gpio_ext_pa5; +extern const GpioPin gpio_ext_pb9; +extern const GpioPin gpio_ext_pa0; +extern const GpioPin gpio_ext_pa1; +extern const GpioPin gpio_ext_pa15; +extern const GpioPin gpio_ext_pe4; +extern const GpioPin gpio_ext_pa2; +extern const GpioPin gpio_ext_pb4; +extern const GpioPin gpio_ext_pb5; +extern const GpioPin gpio_ext_pd0; +extern const GpioPin gpio_ext_pb13; + +extern const GpioPin gpio_usart_tx; +extern const GpioPin gpio_usart_rx; +extern const GpioPin gpio_i2c_power_sda; +extern const GpioPin gpio_i2c_power_scl; + +extern const GpioPin gpio_speaker; + +extern const GpioPin gpio_periph_power; + +extern const GpioPin gpio_usb_dm; +extern const GpioPin gpio_usb_dp; + +void furi_hal_resources_init_early(); + +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/targets/f18/furi_hal/furi_hal_spi_config.c b/targets/f18/furi_hal/furi_hal_spi_config.c new file mode 100644 index 00000000000..5ac84906f82 --- /dev/null +++ b/targets/f18/furi_hal/furi_hal_spi_config.c @@ -0,0 +1,356 @@ +#include +#include +#include +#include +#include + +#define TAG "FuriHalSpiConfig" + +/* SPI Presets */ + +const LL_SPI_InitTypeDef furi_hal_spi_preset_2edge_low_8m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_2EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_8m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_1EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_4m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_1EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV16, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_16m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_1EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_2m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_1EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV32, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +/* SPI Buses */ + +FuriMutex* furi_hal_spi_bus_r_mutex = NULL; + +void furi_hal_spi_config_init_early() { + furi_hal_spi_bus_init(&furi_hal_spi_bus_d); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_display); +} + +void furi_hal_spi_config_deinit_early() { + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_display); + furi_hal_spi_bus_deinit(&furi_hal_spi_bus_d); +} + +void furi_hal_spi_config_init() { + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_fast); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_slow); + + FURI_LOG_I(TAG, "Init OK"); +} + +static void furi_hal_spi_bus_r_event_callback(FuriHalSpiBus* bus, FuriHalSpiBusEvent event) { + if(event == FuriHalSpiBusEventInit) { + furi_hal_spi_bus_r_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + bus->current_handle = NULL; + } else if(event == FuriHalSpiBusEventDeinit) { + furi_mutex_free(furi_hal_spi_bus_r_mutex); + } else if(event == FuriHalSpiBusEventLock) { + furi_check(furi_mutex_acquire(furi_hal_spi_bus_r_mutex, FuriWaitForever) == FuriStatusOk); + } else if(event == FuriHalSpiBusEventUnlock) { + furi_check(furi_mutex_release(furi_hal_spi_bus_r_mutex) == FuriStatusOk); + } else if(event == FuriHalSpiBusEventActivate) { + furi_hal_bus_enable(FuriHalBusSPI1); + } else if(event == FuriHalSpiBusEventDeactivate) { + furi_hal_bus_disable(FuriHalBusSPI1); + } +} + +FuriHalSpiBus furi_hal_spi_bus_r = { + .spi = SPI1, + .callback = furi_hal_spi_bus_r_event_callback, +}; + +FuriMutex* furi_hal_spi_bus_d_mutex = NULL; + +static void furi_hal_spi_bus_d_event_callback(FuriHalSpiBus* bus, FuriHalSpiBusEvent event) { + if(event == FuriHalSpiBusEventInit) { + furi_hal_spi_bus_d_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + bus->current_handle = NULL; + } else if(event == FuriHalSpiBusEventDeinit) { + furi_mutex_free(furi_hal_spi_bus_d_mutex); + } else if(event == FuriHalSpiBusEventLock) { + furi_check(furi_mutex_acquire(furi_hal_spi_bus_d_mutex, FuriWaitForever) == FuriStatusOk); + } else if(event == FuriHalSpiBusEventUnlock) { + furi_check(furi_mutex_release(furi_hal_spi_bus_d_mutex) == FuriStatusOk); + } else if(event == FuriHalSpiBusEventActivate) { + furi_hal_bus_enable(FuriHalBusSPI2); + } else if(event == FuriHalSpiBusEventDeactivate) { + furi_hal_bus_disable(FuriHalBusSPI2); + } +} + +FuriHalSpiBus furi_hal_spi_bus_d = { + .spi = SPI2, + .callback = furi_hal_spi_bus_d_event_callback, +}; + +/* SPI Bus Handles */ + +inline static void furi_hal_spi_bus_r_handle_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event, + const LL_SPI_InitTypeDef* preset) { + if(event == FuriHalSpiBusHandleEventInit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else if(event == FuriHalSpiBusHandleEventDeinit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } else if(event == FuriHalSpiBusHandleEventActivate) { + LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset); + LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER); + LL_SPI_Enable(handle->bus->spi); + + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + + furi_hal_gpio_write(handle->cs, false); + } else if(event == FuriHalSpiBusHandleEventDeactivate) { + furi_hal_gpio_write(handle->cs, true); + + furi_hal_gpio_init(handle->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + LL_SPI_Disable(handle->bus->spi); + } +} + +inline static void furi_hal_spi_bus_nfc_handle_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event, + const LL_SPI_InitTypeDef* preset) { + if(event == FuriHalSpiBusHandleEventInit) { + // Configure GPIOs in normal SPI mode + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else if(event == FuriHalSpiBusHandleEventDeinit) { + // Configure GPIOs for st25r3916 Transparent mode + furi_hal_gpio_init(handle->sck, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_gpio_init(handle->miso, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_gpio_init(handle->cs, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_gpio_write(handle->mosi, false); + furi_hal_gpio_init(handle->mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else if(event == FuriHalSpiBusHandleEventActivate) { + LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset); + LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER); + LL_SPI_Enable(handle->bus->spi); + + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + + } else if(event == FuriHalSpiBusHandleEventDeactivate) { + furi_hal_gpio_init(handle->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + LL_SPI_Disable(handle->bus->spi); + } +} + +static void furi_hal_spi_bus_handle_external_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event) { + furi_hal_spi_bus_r_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_2m); +} + +FuriHalSpiBusHandle furi_hal_spi_bus_handle_external = { + .bus = &furi_hal_spi_bus_r, + .callback = furi_hal_spi_bus_handle_external_event_callback, + .miso = &gpio_ext_pa6, + .mosi = &gpio_ext_pa7, + .sck = &gpio_ext_pb3, + .cs = &gpio_ext_pa4, +}; + +inline static void furi_hal_spi_bus_d_handle_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event, + const LL_SPI_InitTypeDef* preset) { + if(event == FuriHalSpiBusHandleEventInit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh); + + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + + } else if(event == FuriHalSpiBusHandleEventDeinit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeAnalog, GpioPullUp, GpioSpeedLow); + } else if(event == FuriHalSpiBusHandleEventActivate) { + LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset); + LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER); + LL_SPI_Enable(handle->bus->spi); + furi_hal_gpio_write(handle->cs, false); + } else if(event == FuriHalSpiBusHandleEventDeactivate) { + furi_hal_gpio_write(handle->cs, true); + LL_SPI_Disable(handle->bus->spi); + } +} + +static void furi_hal_spi_bus_handle_display_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event) { + furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_4m); +} + +FuriHalSpiBusHandle furi_hal_spi_bus_handle_display = { + .bus = &furi_hal_spi_bus_d, + .callback = furi_hal_spi_bus_handle_display_event_callback, + .miso = &gpio_spi_d_miso, + .mosi = &gpio_spi_d_mosi, + .sck = &gpio_spi_d_sck, + .cs = &gpio_display_cs, +}; + +static void furi_hal_spi_bus_handle_sd_fast_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event) { + furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_16m); +} + +FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast = { + .bus = &furi_hal_spi_bus_d, + .callback = furi_hal_spi_bus_handle_sd_fast_event_callback, + .miso = &gpio_spi_d_miso, + .mosi = &gpio_spi_d_mosi, + .sck = &gpio_spi_d_sck, + .cs = &gpio_sdcard_cs, +}; + +static void furi_hal_spi_bus_handle_sd_slow_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event) { + furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_2m); +} + +FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_slow = { + .bus = &furi_hal_spi_bus_d, + .callback = furi_hal_spi_bus_handle_sd_slow_event_callback, + .miso = &gpio_spi_d_miso, + .mosi = &gpio_spi_d_mosi, + .sck = &gpio_spi_d_sck, + .cs = &gpio_sdcard_cs, +}; diff --git a/targets/f18/furi_hal/furi_hal_spi_config.h b/targets/f18/furi_hal/furi_hal_spi_config.h new file mode 100644 index 00000000000..da39fbfa6d0 --- /dev/null +++ b/targets/f18/furi_hal/furi_hal_spi_config.h @@ -0,0 +1,55 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Preset for ST25R916 */ +extern const LL_SPI_InitTypeDef furi_hal_spi_preset_2edge_low_8m; + +/** Preset for CC1101 */ +extern const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_8m; + +/** Preset for ST7567 (Display) */ +extern const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_4m; + +/** Preset for SdCard in fast mode */ +extern const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_16m; + +/** Preset for SdCard in slow mode */ +extern const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_2m; + +/** Furi Hal Spi Bus R (External) */ +extern FuriHalSpiBus furi_hal_spi_bus_r; + +/** Furi Hal Spi Bus D (Display, SdCard) */ +extern FuriHalSpiBus furi_hal_spi_bus_d; + +/** External on `furi_hal_spi_bus_r` + * Preset: `furi_hal_spi_preset_1edge_low_2m` + * + * miso: pa6 + * mosi: pa7 + * sck: pb3 + * cs: pa4 (software controlled) + * + * @warning not initialized by default, call `furi_hal_spi_bus_handle_init` to initialize + * Bus pins are floating on inactive state, CS high after initialization + * + */ +extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_external; + +/** ST7567(Display) on `furi_hal_spi_bus_d` */ +extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_display; + +/** SdCard in fast mode on `furi_hal_spi_bus_d` */ +extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast; + +/** SdCard in slow mode on `furi_hal_spi_bus_d` */ +extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_slow; + +#ifdef __cplusplus +} +#endif diff --git a/targets/f18/furi_hal/furi_hal_target_hw.h b/targets/f18/furi_hal/furi_hal_target_hw.h new file mode 100644 index 00000000000..6f70f09beec --- /dev/null +++ b/targets/f18/furi_hal/furi_hal_target_hw.h @@ -0,0 +1 @@ +#pragma once diff --git a/targets/f18/furi_hal/furi_hal_version_device.c b/targets/f18/furi_hal/furi_hal_version_device.c new file mode 100644 index 00000000000..e038b98d737 --- /dev/null +++ b/targets/f18/furi_hal/furi_hal_version_device.c @@ -0,0 +1,25 @@ +#include + +bool furi_hal_version_do_i_belong_here() { + return (furi_hal_version_get_hw_target() == 18) || (furi_hal_version_get_hw_target() == 0); +} + +const char* furi_hal_version_get_model_name() { + return "Flipper Nano"; +} + +const char* furi_hal_version_get_model_code() { + return "FN.1"; +} + +const char* furi_hal_version_get_fcc_id() { + return "Pending"; +} + +const char* furi_hal_version_get_ic_id() { + return "Pending"; +} + +const char* furi_hal_version_get_mic_id() { + return "Pending"; +} diff --git a/targets/f18/target.json b/targets/f18/target.json new file mode 100644 index 00000000000..e021a5b2296 --- /dev/null +++ b/targets/f18/target.json @@ -0,0 +1,68 @@ +{ + "inherit": "7", + "include_paths": [ + "furi_hal" + ], + "sdk_header_paths": [ + "../furi_hal_include", + "furi_hal", + "platform_specific" + ], + "sdk_symbols": "api_symbols.csv", + "linker_dependencies": [ + "print", + "flipper18", + "furi", + "freertos", + "stm32wb", + "hwdrivers", + "fatfs", + "littlefs", + "toolbox", + "digital_signal", + "signal_reader", + "microtar", + "usb_stm32", + "appframe", + "assets", + "one_wire", + "music_worker", + "mbedtls", + "flipper_application", + "toolbox", + "u8g2", + "nanopb", + "update_util", + "heatshrink", + "flipperformat", + "flipper18" + ], + "excluded_sources": [ + "furi_hal_infrared.c", + "furi_hal_nfc.c", + "furi_hal_nfc_timer.c", + "furi_hal_nfc_irq.c", + "furi_hal_nfc_event.c", + "furi_hal_nfc_iso15693.c", + "furi_hal_nfc_iso14443a.c", + "furi_hal_nfc_iso14443b.c", + "furi_hal_nfc_felica.c", + "furi_hal_rfid.c", + "furi_hal_subghz.c" + ], + "excluded_headers": [ + "furi_hal_infrared.h", + "furi_hal_nfc.h", + "furi_hal_rfid.h", + "furi_hal_subghz.h", + "furi_hal_ibutton.h", + "furi_hal_subghz_configs.h" + ], + "excluded_modules": [ + "nfc", + "lfrfid", + "subghz", + "ibutton", + "infrared" + ] +} \ No newline at end of file diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv new file mode 100644 index 00000000000..439fc7bf5a5 --- /dev/null +++ b/targets/f7/api_symbols.csv @@ -0,0 +1,3616 @@ +entry,status,name,type,params +Version,+,49.1,, +Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, +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/gui/elements.h,, +Header,+,applications/services/gui/gui.h,, +Header,+,applications/services/gui/icon_i.h,, +Header,+,applications/services/gui/modules/button_menu.h,, +Header,+,applications/services/gui/modules/button_panel.h,, +Header,+,applications/services/gui/modules/byte_input.h,, +Header,+,applications/services/gui/modules/dialog_ex.h,, +Header,+,applications/services/gui/modules/empty_screen.h,, +Header,+,applications/services/gui/modules/file_browser.h,, +Header,+,applications/services/gui/modules/file_browser_worker.h,, +Header,+,applications/services/gui/modules/loading.h,, +Header,+,applications/services/gui/modules/menu.h,, +Header,+,applications/services/gui/modules/popup.h,, +Header,+,applications/services/gui/modules/submenu.h,, +Header,+,applications/services/gui/modules/text_box.h,, +Header,+,applications/services/gui/modules/text_input.h,, +Header,+,applications/services/gui/modules/validators.h,, +Header,+,applications/services/gui/modules/variable_item_list.h,, +Header,+,applications/services/gui/modules/widget.h,, +Header,+,applications/services/gui/modules/widget_elements/widget_element.h,, +Header,+,applications/services/gui/view_dispatcher.h,, +Header,+,applications/services/gui/view_stack.h,, +Header,+,applications/services/input/input.h,, +Header,+,applications/services/loader/firmware_api/firmware_api.h,, +Header,+,applications/services/loader/loader.h,, +Header,+,applications/services/locale/locale.h,, +Header,+,applications/services/notification/notification.h,, +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,+,lib/digital_signal/digital_sequence.h,, +Header,+,lib/digital_signal/digital_signal.h,, +Header,+,lib/drivers/cc1101_regs.h,, +Header,+,lib/drivers/st25r3916.h,, +Header,+,lib/drivers/st25r3916_reg.h,, +Header,+,lib/flipper_application/api_hashtable/api_hashtable.h,, +Header,+,lib/flipper_application/api_hashtable/compilesort.hpp,, +Header,+,lib/flipper_application/flipper_application.h,, +Header,+,lib/flipper_application/plugins/composite_resolver.h,, +Header,+,lib/flipper_application/plugins/plugin_manager.h,, +Header,+,lib/flipper_format/flipper_format.h,, +Header,+,lib/flipper_format/flipper_format_i.h,, +Header,+,lib/flipper_format/flipper_format_stream.h,, +Header,+,lib/ibutton/ibutton_key.h,, +Header,+,lib/ibutton/ibutton_protocols.h,, +Header,+,lib/ibutton/ibutton_worker.h,, +Header,+,lib/infrared/encoder_decoder/infrared.h,, +Header,+,lib/infrared/worker/infrared_transmit.h,, +Header,+,lib/infrared/worker/infrared_worker.h,, +Header,+,lib/lfrfid/lfrfid_dict_file.h,, +Header,+,lib/lfrfid/lfrfid_raw_file.h,, +Header,+,lib/lfrfid/lfrfid_raw_worker.h,, +Header,+,lib/lfrfid/lfrfid_worker.h,, +Header,+,lib/lfrfid/protocols/lfrfid_protocols.h,, +Header,+,lib/lfrfid/tools/bit_lib.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_button.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_consumer.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_desktop.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_device.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_game.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_keyboard.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_led.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_ordinal.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_power.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_simulation.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_sport.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_telephony.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_vr.h,, +Header,-,lib/libusb_stm32/inc/stm32_compat.h,, +Header,+,lib/libusb_stm32/inc/usb.h,, +Header,+,lib/libusb_stm32/inc/usb_ccid.h,, +Header,+,lib/libusb_stm32/inc/usb_cdc.h,, +Header,+,lib/libusb_stm32/inc/usb_cdca.h,, +Header,+,lib/libusb_stm32/inc/usb_cdce.h,, +Header,+,lib/libusb_stm32/inc/usb_cdci.h,, +Header,+,lib/libusb_stm32/inc/usb_cdcp.h,, +Header,+,lib/libusb_stm32/inc/usb_cdcw.h,, +Header,+,lib/libusb_stm32/inc/usb_dfu.h,, +Header,+,lib/libusb_stm32/inc/usb_hid.h,, +Header,+,lib/libusb_stm32/inc/usb_std.h,, +Header,+,lib/libusb_stm32/inc/usb_tmc.h,, +Header,+,lib/libusb_stm32/inc/usbd_core.h,, +Header,+,lib/mbedtls/include/mbedtls/des.h,, +Header,+,lib/mbedtls/include/mbedtls/ecdh.h,, +Header,+,lib/mbedtls/include/mbedtls/ecdsa.h,, +Header,+,lib/mbedtls/include/mbedtls/ecp.h,, +Header,+,lib/mbedtls/include/mbedtls/md.h,, +Header,+,lib/mbedtls/include/mbedtls/md5.h,, +Header,+,lib/mbedtls/include/mbedtls/sha1.h,, +Header,+,lib/mbedtls/include/mbedtls/sha256.h,, +Header,+,lib/mlib/m-algo.h,, +Header,+,lib/mlib/m-array.h,, +Header,+,lib/mlib/m-bptree.h,, +Header,+,lib/mlib/m-core.h,, +Header,+,lib/mlib/m-deque.h,, +Header,+,lib/mlib/m-dict.h,, +Header,+,lib/mlib/m-list.h,, +Header,+,lib/mlib/m-rbtree.h,, +Header,+,lib/mlib/m-tuple.h,, +Header,+,lib/mlib/m-variant.h,, +Header,+,lib/music_worker/music_worker.h,, +Header,+,lib/nanopb/pb.h,, +Header,+,lib/nanopb/pb_decode.h,, +Header,+,lib/nanopb/pb_encode.h,, +Header,+,lib/nfc/helpers/iso13239_crc.h,, +Header,+,lib/nfc/helpers/iso14443_crc.h,, +Header,+,lib/nfc/helpers/nfc_data_generator.h,, +Header,+,lib/nfc/helpers/nfc_dict.h,, +Header,+,lib/nfc/helpers/nfc_util.h,, +Header,+,lib/nfc/nfc.h,, +Header,+,lib/nfc/nfc_device.h,, +Header,+,lib/nfc/nfc_listener.h,, +Header,+,lib/nfc/nfc_poller.h,, +Header,+,lib/nfc/nfc_scanner.h,, +Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a.h,, +Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_listener.h,, +Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_poller.h,, +Header,+,lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_sync.h,, +Header,+,lib/nfc/protocols/iso14443_3b/iso14443_3b.h,, +Header,+,lib/nfc/protocols/iso14443_3b/iso14443_3b_poller.h,, +Header,+,lib/nfc/protocols/iso14443_4a/iso14443_4a.h,, +Header,+,lib/nfc/protocols/iso14443_4a/iso14443_4a_listener.h,, +Header,+,lib/nfc/protocols/iso14443_4a/iso14443_4a_poller.h,, +Header,+,lib/nfc/protocols/iso14443_4b/iso14443_4b.h,, +Header,+,lib/nfc/protocols/iso14443_4b/iso14443_4b_poller.h,, +Header,+,lib/nfc/protocols/mf_classic/mf_classic.h,, +Header,+,lib/nfc/protocols/mf_classic/mf_classic_listener.h,, +Header,+,lib/nfc/protocols/mf_classic/mf_classic_poller.h,, +Header,+,lib/nfc/protocols/mf_classic/mf_classic_poller_sync.h,, +Header,+,lib/nfc/protocols/mf_desfire/mf_desfire.h,, +Header,+,lib/nfc/protocols/mf_desfire/mf_desfire_poller.h,, +Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight.h,, +Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.h,, +Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h,, +Header,+,lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.h,, +Header,+,lib/nfc/protocols/slix/slix.h,, +Header,+,lib/nfc/protocols/st25tb/st25tb.h,, +Header,+,lib/nfc/protocols/st25tb/st25tb_poller.h,, +Header,+,lib/nfc/protocols/st25tb/st25tb_poller_sync.h,, +Header,+,lib/one_wire/maxim_crc.h,, +Header,+,lib/one_wire/one_wire_host.h,, +Header,+,lib/one_wire/one_wire_slave.h,, +Header,+,lib/print/wrappers.h,, +Header,+,lib/pulse_reader/pulse_reader.h,, +Header,+,lib/signal_reader/signal_reader.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_adc.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_bus.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_comp.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_cortex.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_crc.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_crs.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_dma.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_dmamux.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_exti.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_gpio.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_hsem.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_i2c.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_ipcc.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_iwdg.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_lptim.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_lpuart.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_pka.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_pwr.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_rcc.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_rng.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_rtc.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_spi.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_system.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_tim.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_usart.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_utils.h,, +Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_wwdg.h,, +Header,+,lib/subghz/blocks/const.h,, +Header,+,lib/subghz/blocks/decoder.h,, +Header,+,lib/subghz/blocks/encoder.h,, +Header,+,lib/subghz/blocks/generic.h,, +Header,+,lib/subghz/blocks/math.h,, +Header,+,lib/subghz/devices/cc1101_configs.h,, +Header,+,lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h,, +Header,+,lib/subghz/environment.h,, +Header,+,lib/subghz/protocols/public_api.h,, +Header,+,lib/subghz/protocols/raw.h,, +Header,+,lib/subghz/receiver.h,, +Header,+,lib/subghz/registry.h,, +Header,+,lib/subghz/subghz_protocol_registry.h,, +Header,+,lib/subghz/subghz_setting.h,, +Header,+,lib/subghz/subghz_tx_rx_worker.h,, +Header,+,lib/subghz/subghz_worker.h,, +Header,+,lib/subghz/transmitter.h,, +Header,+,lib/toolbox/api_lock.h,, +Header,+,lib/toolbox/args.h,, +Header,+,lib/toolbox/bit_buffer.h,, +Header,+,lib/toolbox/compress.h,, +Header,+,lib/toolbox/crc32_calc.h,, +Header,+,lib/toolbox/dir_walk.h,, +Header,+,lib/toolbox/float_tools.h,, +Header,+,lib/toolbox/hex.h,, +Header,+,lib/toolbox/manchester_decoder.h,, +Header,+,lib/toolbox/manchester_encoder.h,, +Header,+,lib/toolbox/name_generator.h,, +Header,+,lib/toolbox/path.h,, +Header,+,lib/toolbox/pretty_format.h,, +Header,+,lib/toolbox/protocols/protocol_dict.h,, +Header,+,lib/toolbox/saved_struct.h,, +Header,+,lib/toolbox/simple_array.h,, +Header,+,lib/toolbox/stream/buffered_file_stream.h,, +Header,+,lib/toolbox/stream/file_stream.h,, +Header,+,lib/toolbox/stream/stream.h,, +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,+,targets/f7/furi_hal/furi_hal_bus.h,, +Header,+,targets/f7/furi_hal/furi_hal_clock.h,, +Header,+,targets/f7/furi_hal/furi_hal_console.h,, +Header,+,targets/f7/furi_hal/furi_hal_dma.h,, +Header,+,targets/f7/furi_hal/furi_hal_flash.h,, +Header,+,targets/f7/furi_hal/furi_hal_gpio.h,, +Header,+,targets/f7/furi_hal/furi_hal_i2c_config.h,, +Header,+,targets/f7/furi_hal/furi_hal_i2c_types.h,, +Header,+,targets/f7/furi_hal/furi_hal_ibutton.h,, +Header,+,targets/f7/furi_hal/furi_hal_idle_timer.h,, +Header,+,targets/f7/furi_hal/furi_hal_interrupt.h,, +Header,+,targets/f7/furi_hal/furi_hal_os.h,, +Header,+,targets/f7/furi_hal/furi_hal_pwm.h,, +Header,+,targets/f7/furi_hal/furi_hal_resources.h,, +Header,+,targets/f7/furi_hal/furi_hal_rfid.h,, +Header,+,targets/f7/furi_hal/furi_hal_spi_config.h,, +Header,+,targets/f7/furi_hal/furi_hal_spi_types.h,, +Header,+,targets/f7/furi_hal/furi_hal_subghz.h,, +Header,+,targets/f7/furi_hal/furi_hal_target_hw.h,, +Header,+,targets/f7/furi_hal/furi_hal_uart.h,, +Header,+,targets/f7/furi_hal/furi_hal_usb_cdc.h,, +Header,+,targets/f7/platform_specific/intrinsic_export.h,, +Header,+,targets/f7/platform_specific/math_wrapper.h,, +Header,+,targets/furi_hal_include/furi_hal.h,, +Header,+,targets/furi_hal_include/furi_hal_bt.h,, +Header,+,targets/furi_hal_include/furi_hal_bt_hid.h,, +Header,+,targets/furi_hal_include/furi_hal_bt_serial.h,, +Header,+,targets/furi_hal_include/furi_hal_cortex.h,, +Header,+,targets/furi_hal_include/furi_hal_crypto.h,, +Header,+,targets/furi_hal_include/furi_hal_debug.h,, +Header,+,targets/furi_hal_include/furi_hal_i2c.h,, +Header,+,targets/furi_hal_include/furi_hal_info.h,, +Header,+,targets/furi_hal_include/furi_hal_infrared.h,, +Header,+,targets/furi_hal_include/furi_hal_light.h,, +Header,+,targets/furi_hal_include/furi_hal_memory.h,, +Header,+,targets/furi_hal_include/furi_hal_mpu.h,, +Header,+,targets/furi_hal_include/furi_hal_nfc.h,, +Header,+,targets/furi_hal_include/furi_hal_power.h,, +Header,+,targets/furi_hal_include/furi_hal_random.h,, +Header,+,targets/furi_hal_include/furi_hal_region.h,, +Header,+,targets/furi_hal_include/furi_hal_rtc.h,, +Header,+,targets/furi_hal_include/furi_hal_sd.h,, +Header,+,targets/furi_hal_include/furi_hal_speaker.h,, +Header,+,targets/furi_hal_include/furi_hal_spi.h,, +Header,+,targets/furi_hal_include/furi_hal_usb.h,, +Header,+,targets/furi_hal_include/furi_hal_usb_ccid.h,, +Header,+,targets/furi_hal_include/furi_hal_usb_hid.h,, +Header,+,targets/furi_hal_include/furi_hal_usb_hid_u2f.h,, +Header,+,targets/furi_hal_include/furi_hal_version.h,, +Header,+,targets/furi_hal_include/furi_hal_vibro.h,, +Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* +Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, const LL_ADC_CommonInitTypeDef*" +Function,-,LL_ADC_CommonStructInit,void,LL_ADC_CommonInitTypeDef* +Function,-,LL_ADC_DeInit,ErrorStatus,ADC_TypeDef* +Function,-,LL_ADC_INJ_Init,ErrorStatus,"ADC_TypeDef*, const LL_ADC_INJ_InitTypeDef*" +Function,-,LL_ADC_INJ_StructInit,void,LL_ADC_INJ_InitTypeDef* +Function,-,LL_ADC_Init,ErrorStatus,"ADC_TypeDef*, const LL_ADC_InitTypeDef*" +Function,-,LL_ADC_REG_Init,ErrorStatus,"ADC_TypeDef*, const LL_ADC_REG_InitTypeDef*" +Function,-,LL_ADC_REG_StructInit,void,LL_ADC_REG_InitTypeDef* +Function,-,LL_ADC_StructInit,void,LL_ADC_InitTypeDef* +Function,-,LL_COMP_DeInit,ErrorStatus,COMP_TypeDef* +Function,+,LL_COMP_Init,ErrorStatus,"COMP_TypeDef*, const LL_COMP_InitTypeDef*" +Function,-,LL_COMP_StructInit,void,LL_COMP_InitTypeDef* +Function,-,LL_CRC_DeInit,ErrorStatus,CRC_TypeDef* +Function,-,LL_CRS_DeInit,ErrorStatus, +Function,+,LL_DMA_DeInit,ErrorStatus,"DMA_TypeDef*, uint32_t" +Function,+,LL_DMA_Init,ErrorStatus,"DMA_TypeDef*, uint32_t, LL_DMA_InitTypeDef*" +Function,-,LL_DMA_StructInit,void,LL_DMA_InitTypeDef* +Function,-,LL_EXTI_DeInit,ErrorStatus, +Function,-,LL_EXTI_Init,ErrorStatus,LL_EXTI_InitTypeDef* +Function,-,LL_EXTI_StructInit,void,LL_EXTI_InitTypeDef* +Function,-,LL_GPIO_DeInit,ErrorStatus,GPIO_TypeDef* +Function,+,LL_GPIO_Init,ErrorStatus,"GPIO_TypeDef*, LL_GPIO_InitTypeDef*" +Function,-,LL_GPIO_StructInit,void,LL_GPIO_InitTypeDef* +Function,-,LL_I2C_DeInit,ErrorStatus,const I2C_TypeDef* +Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, const LL_I2C_InitTypeDef*" +Function,-,LL_I2C_StructInit,void,LL_I2C_InitTypeDef* +Function,-,LL_Init1msTick,void,uint32_t +Function,+,LL_LPTIM_DeInit,ErrorStatus,LPTIM_TypeDef* +Function,-,LL_LPTIM_Disable,void,LPTIM_TypeDef* +Function,+,LL_LPTIM_Init,ErrorStatus,"LPTIM_TypeDef*, const LL_LPTIM_InitTypeDef*" +Function,-,LL_LPTIM_StructInit,void,LL_LPTIM_InitTypeDef* +Function,-,LL_LPUART_DeInit,ErrorStatus,const USART_TypeDef* +Function,+,LL_LPUART_Init,ErrorStatus,"USART_TypeDef*, const LL_LPUART_InitTypeDef*" +Function,-,LL_LPUART_StructInit,void,LL_LPUART_InitTypeDef* +Function,-,LL_PKA_DeInit,ErrorStatus,PKA_TypeDef* +Function,-,LL_PKA_Init,ErrorStatus,"PKA_TypeDef*, LL_PKA_InitTypeDef*" +Function,-,LL_PKA_StructInit,void,LL_PKA_InitTypeDef* +Function,-,LL_PLL_ConfigSystemClock_HSE,ErrorStatus,"uint32_t, LL_UTILS_PLLInitTypeDef*, LL_UTILS_ClkInitTypeDef*" +Function,-,LL_PLL_ConfigSystemClock_HSI,ErrorStatus,"LL_UTILS_PLLInitTypeDef*, LL_UTILS_ClkInitTypeDef*" +Function,-,LL_PLL_ConfigSystemClock_MSI,ErrorStatus,"LL_UTILS_PLLInitTypeDef*, LL_UTILS_ClkInitTypeDef*" +Function,-,LL_PWR_DeInit,ErrorStatus, +Function,-,LL_RCC_DeInit,ErrorStatus, +Function,-,LL_RCC_GetADCClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetCLK48ClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetI2CClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetLPTIMClockFreq,uint32_t,uint32_t +Function,+,LL_RCC_GetLPUARTClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetRFWKPClockFreq,uint32_t, +Function,-,LL_RCC_GetRNGClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetRTCClockFreq,uint32_t, +Function,-,LL_RCC_GetSAIClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetSMPSClockFreq,uint32_t, +Function,-,LL_RCC_GetSystemClocksFreq,void,LL_RCC_ClocksTypeDef* +Function,+,LL_RCC_GetUSARTClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetUSBClockFreq,uint32_t,uint32_t +Function,-,LL_RNG_DeInit,ErrorStatus,RNG_TypeDef* +Function,-,LL_RNG_Init,ErrorStatus,"RNG_TypeDef*, LL_RNG_InitTypeDef*" +Function,-,LL_RNG_StructInit,void,LL_RNG_InitTypeDef* +Function,-,LL_RTC_ALMA_Init,ErrorStatus,"RTC_TypeDef*, uint32_t, LL_RTC_AlarmTypeDef*" +Function,-,LL_RTC_ALMA_StructInit,void,LL_RTC_AlarmTypeDef* +Function,-,LL_RTC_ALMB_Init,ErrorStatus,"RTC_TypeDef*, uint32_t, LL_RTC_AlarmTypeDef*" +Function,-,LL_RTC_ALMB_StructInit,void,LL_RTC_AlarmTypeDef* +Function,-,LL_RTC_DATE_Init,ErrorStatus,"RTC_TypeDef*, uint32_t, LL_RTC_DateTypeDef*" +Function,-,LL_RTC_DATE_StructInit,void,LL_RTC_DateTypeDef* +Function,-,LL_RTC_DeInit,ErrorStatus,RTC_TypeDef* +Function,+,LL_RTC_EnterInitMode,ErrorStatus,RTC_TypeDef* +Function,-,LL_RTC_ExitInitMode,ErrorStatus,RTC_TypeDef* +Function,+,LL_RTC_Init,ErrorStatus,"RTC_TypeDef*, LL_RTC_InitTypeDef*" +Function,-,LL_RTC_StructInit,void,LL_RTC_InitTypeDef* +Function,-,LL_RTC_TIME_Init,ErrorStatus,"RTC_TypeDef*, uint32_t, LL_RTC_TimeTypeDef*" +Function,-,LL_RTC_TIME_StructInit,void,LL_RTC_TimeTypeDef* +Function,-,LL_RTC_WaitForSynchro,ErrorStatus,RTC_TypeDef* +Function,-,LL_SPI_DeInit,ErrorStatus,SPI_TypeDef* +Function,+,LL_SPI_Init,ErrorStatus,"SPI_TypeDef*, LL_SPI_InitTypeDef*" +Function,-,LL_SPI_StructInit,void,LL_SPI_InitTypeDef* +Function,-,LL_SetFlashLatency,ErrorStatus,uint32_t +Function,+,LL_SetSystemCoreClock,void,uint32_t +Function,-,LL_TIM_BDTR_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_BDTR_InitTypeDef*" +Function,-,LL_TIM_BDTR_StructInit,void,LL_TIM_BDTR_InitTypeDef* +Function,+,LL_TIM_DeInit,ErrorStatus,TIM_TypeDef* +Function,-,LL_TIM_ENCODER_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_ENCODER_InitTypeDef*" +Function,-,LL_TIM_ENCODER_StructInit,void,LL_TIM_ENCODER_InitTypeDef* +Function,-,LL_TIM_HALLSENSOR_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_HALLSENSOR_InitTypeDef*" +Function,-,LL_TIM_HALLSENSOR_StructInit,void,LL_TIM_HALLSENSOR_InitTypeDef* +Function,-,LL_TIM_IC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, const LL_TIM_IC_InitTypeDef*" +Function,-,LL_TIM_IC_StructInit,void,LL_TIM_IC_InitTypeDef* +Function,+,LL_TIM_Init,ErrorStatus,"TIM_TypeDef*, const LL_TIM_InitTypeDef*" +Function,+,LL_TIM_OC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, const LL_TIM_OC_InitTypeDef*" +Function,-,LL_TIM_OC_StructInit,void,LL_TIM_OC_InitTypeDef* +Function,-,LL_TIM_StructInit,void,LL_TIM_InitTypeDef* +Function,-,LL_USART_ClockInit,ErrorStatus,"USART_TypeDef*, const LL_USART_ClockInitTypeDef*" +Function,-,LL_USART_ClockStructInit,void,LL_USART_ClockInitTypeDef* +Function,-,LL_USART_DeInit,ErrorStatus,const USART_TypeDef* +Function,+,LL_USART_Init,ErrorStatus,"USART_TypeDef*, const LL_USART_InitTypeDef*" +Function,-,LL_USART_StructInit,void,LL_USART_InitTypeDef* +Function,-,LL_mDelay,void,uint32_t +Function,-,SystemCoreClockUpdate,void, +Function,-,SystemInit,void, +Function,-,_Exit,void,int +Function,+,__aeabi_uldivmod,void*,"uint64_t, uint64_t" +Function,-,__assert,void,"const char*, int, const char*" +Function,+,__assert_func,void,"const char*, int, const char*, const char*" +Function,+,__clear_cache,void,"void*, void*" +Function,-,__eprintf,void,"const char*, const char*, unsigned int, const char*" +Function,+,__errno,int*, +Function,-,__fpclassifyd,int,double +Function,-,__fpclassifyf,int,float +Function,+,__furi_crash_implementation,void, +Function,+,__furi_critical_enter,__FuriCriticalInfo, +Function,+,__furi_critical_exit,void,__FuriCriticalInfo +Function,+,__furi_halt_implementation,void, +Function,-,__getdelim,ssize_t,"char**, size_t*, int, FILE*" +Function,-,__getline,ssize_t,"char**, size_t*, FILE*" +Function,-,__isinfd,int,double +Function,-,__isinff,int,float +Function,-,__isnand,int,double +Function,-,__isnanf,int,float +Function,-,__itoa,char*,"int, char*, int" +Function,-,__locale_mb_cur_max,int, +Function,+,__retarget_lock_acquire,void,_LOCK_T +Function,+,__retarget_lock_acquire_recursive,void,_LOCK_T +Function,-,__retarget_lock_close,void,_LOCK_T +Function,+,__retarget_lock_close_recursive,void,_LOCK_T +Function,-,__retarget_lock_init,void,_LOCK_T* +Function,+,__retarget_lock_init_recursive,void,_LOCK_T* +Function,+,__retarget_lock_release,void,_LOCK_T +Function,+,__retarget_lock_release_recursive,void,_LOCK_T +Function,-,__retarget_lock_try_acquire,int,_LOCK_T +Function,-,__retarget_lock_try_acquire_recursive,int,_LOCK_T +Function,-,__signbitd,int,double +Function,-,__signbitf,int,float +Function,-,__signgam,int*, +Function,-,__srget_r,int,"_reent*, FILE*" +Function,-,__swbuf_r,int,"_reent*, int, FILE*" +Function,-,__utoa,char*,"unsigned, char*, int" +Function,+,__wrap___assert,void,"const char*, int, const char*" +Function,+,__wrap___assert_func,void,"const char*, int, const char*, const char*" +Function,+,__wrap_fflush,int,FILE* +Function,+,__wrap_printf,int,"const char*, ..." +Function,+,__wrap_putc,int,"int, FILE*" +Function,+,__wrap_putchar,int,int +Function,+,__wrap_puts,int,const char* +Function,+,__wrap_snprintf,int,"char*, size_t, const char*, ..." +Function,+,__wrap_vsnprintf,int,"char*, size_t, const char*, va_list" +Function,-,_asiprintf_r,int,"_reent*, char**, const char*, ..." +Function,-,_asniprintf_r,char*,"_reent*, char*, size_t*, const char*, ..." +Function,-,_asnprintf_r,char*,"_reent*, char*, size_t*, const char*, ..." +Function,-,_asprintf_r,int,"_reent*, char**, const char*, ..." +Function,-,_atoi_r,int,"_reent*, const char*" +Function,-,_atol_r,long,"_reent*, const char*" +Function,-,_atoll_r,long long,"_reent*, const char*" +Function,-,_calloc_r,void*,"_reent*, size_t, size_t" +Function,-,_diprintf_r,int,"_reent*, int, const char*, ..." +Function,-,_dprintf_r,int,"_reent*, int, const char*, ..." +Function,-,_drand48_r,double,_reent* +Function,-,_dtoa_r,char*,"_reent*, double, int, int, int*, int*, char**" +Function,-,_erand48_r,double,"_reent*, unsigned short[3]" +Function,-,_fclose_r,int,"_reent*, FILE*" +Function,-,_fcloseall_r,int,_reent* +Function,-,_fdopen_r,FILE*,"_reent*, int, const char*" +Function,-,_fflush_r,int,"_reent*, FILE*" +Function,-,_fgetc_r,int,"_reent*, FILE*" +Function,-,_fgetc_unlocked_r,int,"_reent*, FILE*" +Function,-,_fgetpos_r,int,"_reent*, FILE*, fpos_t*" +Function,-,_fgets_r,char*,"_reent*, char*, int, FILE*" +Function,-,_fgets_unlocked_r,char*,"_reent*, char*, int, FILE*" +Function,-,_findenv,char*,"const char*, int*" +Function,-,_findenv_r,char*,"_reent*, const char*, int*" +Function,-,_fiprintf_r,int,"_reent*, FILE*, const char*, ..." +Function,-,_fiscanf_r,int,"_reent*, FILE*, const char*, ..." +Function,-,_fmemopen_r,FILE*,"_reent*, void*, size_t, const char*" +Function,-,_fopen_r,FILE*,"_reent*, const char*, const char*" +Function,-,_fopencookie_r,FILE*,"_reent*, void*, const char*, cookie_io_functions_t" +Function,-,_fprintf_r,int,"_reent*, FILE*, const char*, ..." +Function,-,_fpurge_r,int,"_reent*, FILE*" +Function,-,_fputc_r,int,"_reent*, int, FILE*" +Function,-,_fputc_unlocked_r,int,"_reent*, int, FILE*" +Function,-,_fputs_r,int,"_reent*, const char*, FILE*" +Function,-,_fputs_unlocked_r,int,"_reent*, const char*, FILE*" +Function,-,_fread_r,size_t,"_reent*, void*, size_t, size_t, FILE*" +Function,-,_fread_unlocked_r,size_t,"_reent*, void*, size_t, size_t, FILE*" +Function,-,_free_r,void,"_reent*, void*" +Function,-,_freopen_r,FILE*,"_reent*, const char*, const char*, FILE*" +Function,-,_fscanf_r,int,"_reent*, FILE*, const char*, ..." +Function,-,_fseek_r,int,"_reent*, FILE*, long, int" +Function,-,_fseeko_r,int,"_reent*, FILE*, _off_t, int" +Function,-,_fsetpos_r,int,"_reent*, FILE*, const fpos_t*" +Function,-,_ftell_r,long,"_reent*, FILE*" +Function,-,_ftello_r,_off_t,"_reent*, FILE*" +Function,-,_funopen_r,FILE*,"_reent*, const void*, int (*)(void*, char*, int), int (*)(void*, const char*, int), fpos_t (*)(void*, fpos_t, int), int (*)(void*)" +Function,-,_fwrite_r,size_t,"_reent*, const void*, size_t, size_t, FILE*" +Function,-,_fwrite_unlocked_r,size_t,"_reent*, const void*, size_t, size_t, FILE*" +Function,-,_getc_r,int,"_reent*, FILE*" +Function,-,_getc_unlocked_r,int,"_reent*, FILE*" +Function,-,_getchar_r,int,_reent* +Function,-,_getchar_unlocked_r,int,_reent* +Function,-,_getenv_r,char*,"_reent*, const char*" +Function,-,_gets_r,char*,"_reent*, char*" +Function,-,_iprintf_r,int,"_reent*, const char*, ..." +Function,-,_iscanf_r,int,"_reent*, const char*, ..." +Function,-,_jrand48_r,long,"_reent*, unsigned short[3]" +Function,-,_l64a_r,char*,"_reent*, long" +Function,-,_lcong48_r,void,"_reent*, unsigned short[7]" +Function,-,_lrand48_r,long,_reent* +Function,-,_malloc_r,void*,"_reent*, size_t" +Function,-,_mblen_r,int,"_reent*, const char*, size_t, _mbstate_t*" +Function,-,_mbstowcs_r,size_t,"_reent*, wchar_t*, const char*, size_t, _mbstate_t*" +Function,-,_mbtowc_r,int,"_reent*, wchar_t*, const char*, size_t, _mbstate_t*" +Function,-,_mkdtemp_r,char*,"_reent*, char*" +Function,-,_mkostemp_r,int,"_reent*, char*, int" +Function,-,_mkostemps_r,int,"_reent*, char*, int, int" +Function,-,_mkstemp_r,int,"_reent*, char*" +Function,-,_mkstemps_r,int,"_reent*, char*, int" +Function,-,_mktemp_r,char*,"_reent*, char*" +Function,-,_mrand48_r,long,_reent* +Function,-,_mstats_r,void,"_reent*, char*" +Function,-,_nrand48_r,long,"_reent*, unsigned short[3]" +Function,-,_open_memstream_r,FILE*,"_reent*, char**, size_t*" +Function,-,_perror_r,void,"_reent*, const char*" +Function,-,_printf_r,int,"_reent*, const char*, ..." +Function,-,_putc_r,int,"_reent*, int, FILE*" +Function,-,_putc_unlocked_r,int,"_reent*, int, FILE*" +Function,-,_putchar_r,int,"_reent*, int" +Function,-,_putchar_unlocked_r,int,"_reent*, int" +Function,-,_putenv_r,int,"_reent*, char*" +Function,-,_puts_r,int,"_reent*, const char*" +Function,-,_realloc_r,void*,"_reent*, void*, size_t" +Function,-,_reallocf_r,void*,"_reent*, void*, size_t" +Function,-,_reclaim_reent,void,_reent* +Function,-,_remove_r,int,"_reent*, const char*" +Function,-,_rename_r,int,"_reent*, const char*, const char*" +Function,-,_rewind_r,void,"_reent*, FILE*" +Function,-,_scanf_r,int,"_reent*, const char*, ..." +Function,-,_seed48_r,unsigned short*,"_reent*, unsigned short[3]" +Function,-,_setenv_r,int,"_reent*, const char*, const char*, int" +Function,-,_siprintf_r,int,"_reent*, char*, const char*, ..." +Function,-,_siscanf_r,int,"_reent*, const char*, const char*, ..." +Function,-,_sniprintf_r,int,"_reent*, char*, size_t, const char*, ..." +Function,-,_snprintf_r,int,"_reent*, char*, size_t, const char*, ..." +Function,-,_sprintf_r,int,"_reent*, char*, const char*, ..." +Function,-,_srand48_r,void,"_reent*, long" +Function,-,_sscanf_r,int,"_reent*, const char*, const char*, ..." +Function,-,_strdup_r,char*,"_reent*, const char*" +Function,-,_strerror_r,char*,"_reent*, int, int, int*" +Function,-,_strndup_r,char*,"_reent*, const char*, size_t" +Function,-,_strtod_r,double,"_reent*, const char*, char**" +Function,-,_strtol_r,long,"_reent*, const char*, char**, int" +Function,-,_strtold_r,long double,"_reent*, const char*, char**" +Function,-,_strtoll_r,long long,"_reent*, const char*, char**, int" +Function,-,_strtoul_r,unsigned long,"_reent*, const char*, char**, int" +Function,-,_strtoull_r,unsigned long long,"_reent*, const char*, char**, int" +Function,-,_system_r,int,"_reent*, const char*" +Function,-,_tempnam_r,char*,"_reent*, const char*, const char*" +Function,-,_tmpfile_r,FILE*,_reent* +Function,-,_tmpnam_r,char*,"_reent*, char*" +Function,-,_ungetc_r,int,"_reent*, int, FILE*" +Function,-,_unsetenv_r,int,"_reent*, const char*" +Function,-,_vasiprintf_r,int,"_reent*, char**, const char*, __gnuc_va_list" +Function,-,_vasniprintf_r,char*,"_reent*, char*, size_t*, const char*, __gnuc_va_list" +Function,-,_vasnprintf_r,char*,"_reent*, char*, size_t*, const char*, __gnuc_va_list" +Function,-,_vasprintf_r,int,"_reent*, char**, const char*, __gnuc_va_list" +Function,-,_vdiprintf_r,int,"_reent*, int, const char*, __gnuc_va_list" +Function,-,_vdprintf_r,int,"_reent*, int, const char*, __gnuc_va_list" +Function,-,_vfiprintf_r,int,"_reent*, FILE*, const char*, __gnuc_va_list" +Function,-,_vfiscanf_r,int,"_reent*, FILE*, const char*, __gnuc_va_list" +Function,-,_vfprintf_r,int,"_reent*, FILE*, const char*, __gnuc_va_list" +Function,-,_vfscanf_r,int,"_reent*, FILE*, const char*, __gnuc_va_list" +Function,-,_viprintf_r,int,"_reent*, const char*, __gnuc_va_list" +Function,-,_viscanf_r,int,"_reent*, const char*, __gnuc_va_list" +Function,-,_vprintf_r,int,"_reent*, const char*, __gnuc_va_list" +Function,-,_vscanf_r,int,"_reent*, const char*, __gnuc_va_list" +Function,-,_vsiprintf_r,int,"_reent*, char*, const char*, __gnuc_va_list" +Function,-,_vsiscanf_r,int,"_reent*, const char*, const char*, __gnuc_va_list" +Function,-,_vsniprintf_r,int,"_reent*, char*, size_t, const char*, __gnuc_va_list" +Function,-,_vsnprintf_r,int,"_reent*, char*, size_t, const char*, __gnuc_va_list" +Function,-,_vsprintf_r,int,"_reent*, char*, const char*, __gnuc_va_list" +Function,-,_vsscanf_r,int,"_reent*, const char*, const char*, __gnuc_va_list" +Function,-,_wcstombs_r,size_t,"_reent*, char*, const wchar_t*, size_t, _mbstate_t*" +Function,-,_wctomb_r,int,"_reent*, char*, wchar_t, _mbstate_t*" +Function,-,a64l,long,const char* +Function,+,abort,void, +Function,-,abs,int,int +Function,-,acos,double,double +Function,-,acosf,float,float +Function,-,acosh,double,double +Function,-,acoshf,float,float +Function,-,acoshl,long double,long double +Function,-,acosl,long double,long double +Function,-,aligned_alloc,void*,"size_t, size_t" +Function,+,aligned_free,void,void* +Function,+,aligned_malloc,void*,"size_t, size_t" +Function,-,arc4random,__uint32_t, +Function,-,arc4random_buf,void,"void*, size_t" +Function,-,arc4random_uniform,__uint32_t,__uint32_t +Function,+,args_char_to_hex,_Bool,"char, char, uint8_t*" +Function,+,args_get_first_word_length,size_t,FuriString* +Function,+,args_length,size_t,FuriString* +Function,+,args_read_hex_bytes,_Bool,"FuriString*, uint8_t*, size_t" +Function,+,args_read_int_and_trim,_Bool,"FuriString*, int*" +Function,+,args_read_probably_quoted_string_and_trim,_Bool,"FuriString*, FuriString*" +Function,+,args_read_string_and_trim,_Bool,"FuriString*, FuriString*" +Function,-,asin,double,double +Function,-,asinf,float,float +Function,-,asinh,double,double +Function,-,asinhf,float,float +Function,-,asinhl,long double,long double +Function,-,asinl,long double,long double +Function,-,asiprintf,int,"char**, const char*, ..." +Function,-,asniprintf,char*,"char*, size_t*, const char*, ..." +Function,-,asnprintf,char*,"char*, size_t*, const char*, ..." +Function,-,asprintf,int,"char**, const char*, ..." +Function,-,at_quick_exit,int,void (*)() +Function,-,atan,double,double +Function,-,atan2,double,"double, double" +Function,-,atan2f,float,"float, float" +Function,-,atan2l,long double,"long double, long double" +Function,-,atanf,float,float +Function,-,atanh,double,double +Function,-,atanhf,float,float +Function,-,atanhl,long double,long double +Function,-,atanl,long double,long double +Function,-,atexit,int,void (*)() +Function,-,atof,double,const char* +Function,-,atoff,float,const char* +Function,+,atoi,int,const char* +Function,-,atol,long,const char* +Function,-,atoll,long long,const char* +Function,-,basename,char*,const char* +Function,-,bcmp,int,"const void*, const void*, size_t" +Function,-,bcopy,void,"const void*, void*, size_t" +Function,+,bit_buffer_alloc,BitBuffer*,size_t +Function,+,bit_buffer_append,void,"BitBuffer*, const BitBuffer*" +Function,+,bit_buffer_append_bit,void,"BitBuffer*, _Bool" +Function,+,bit_buffer_append_byte,void,"BitBuffer*, uint8_t" +Function,+,bit_buffer_append_bytes,void,"BitBuffer*, const uint8_t*, size_t" +Function,+,bit_buffer_append_right,void,"BitBuffer*, const BitBuffer*, size_t" +Function,+,bit_buffer_copy,void,"BitBuffer*, const BitBuffer*" +Function,+,bit_buffer_copy_bits,void,"BitBuffer*, const uint8_t*, size_t" +Function,+,bit_buffer_copy_bytes,void,"BitBuffer*, const uint8_t*, size_t" +Function,+,bit_buffer_copy_bytes_with_parity,void,"BitBuffer*, const uint8_t*, size_t" +Function,+,bit_buffer_copy_left,void,"BitBuffer*, const BitBuffer*, size_t" +Function,+,bit_buffer_copy_right,void,"BitBuffer*, const BitBuffer*, size_t" +Function,+,bit_buffer_free,void,BitBuffer* +Function,+,bit_buffer_get_byte,uint8_t,"const BitBuffer*, size_t" +Function,+,bit_buffer_get_byte_from_bit,uint8_t,"const BitBuffer*, size_t" +Function,+,bit_buffer_get_capacity_bytes,size_t,const BitBuffer* +Function,+,bit_buffer_get_data,const uint8_t*,const BitBuffer* +Function,+,bit_buffer_get_parity,const uint8_t*,const BitBuffer* +Function,+,bit_buffer_get_size,size_t,const BitBuffer* +Function,+,bit_buffer_get_size_bytes,size_t,const BitBuffer* +Function,+,bit_buffer_has_partial_byte,_Bool,const BitBuffer* +Function,+,bit_buffer_reset,void,BitBuffer* +Function,+,bit_buffer_set_byte,void,"BitBuffer*, size_t, uint8_t" +Function,+,bit_buffer_set_byte_with_parity,void,"BitBuffer*, size_t, uint8_t, _Bool" +Function,+,bit_buffer_set_size,void,"BitBuffer*, size_t" +Function,+,bit_buffer_set_size_bytes,void,"BitBuffer*, size_t" +Function,+,bit_buffer_starts_with_byte,_Bool,"const BitBuffer*, uint8_t" +Function,+,bit_buffer_write_bytes,void,"const BitBuffer*, void*, size_t" +Function,+,bit_buffer_write_bytes_mid,void,"const BitBuffer*, void*, size_t, size_t" +Function,+,bit_buffer_write_bytes_with_parity,void,"const BitBuffer*, void*, size_t, size_t*" +Function,+,bit_lib_add_parity,size_t,"const uint8_t*, size_t, uint8_t*, size_t, uint8_t, uint8_t, BitLibParity" +Function,+,bit_lib_copy_bits,void,"uint8_t*, size_t, size_t, const uint8_t*, size_t" +Function,+,bit_lib_crc16,uint16_t,"const uint8_t*, size_t, uint16_t, uint16_t, _Bool, _Bool, uint16_t" +Function,+,bit_lib_crc8,uint16_t,"const uint8_t*, size_t, uint8_t, uint8_t, _Bool, _Bool, uint8_t" +Function,+,bit_lib_get_bit,_Bool,"const uint8_t*, size_t" +Function,+,bit_lib_get_bit_count,uint8_t,uint32_t +Function,+,bit_lib_get_bits,uint8_t,"const uint8_t*, size_t, uint8_t" +Function,+,bit_lib_get_bits_16,uint16_t,"const uint8_t*, size_t, uint8_t" +Function,+,bit_lib_get_bits_32,uint32_t,"const uint8_t*, size_t, uint8_t" +Function,+,bit_lib_print_bits,void,"const uint8_t*, size_t" +Function,+,bit_lib_print_regions,void,"const BitLibRegion*, size_t, const uint8_t*, size_t" +Function,+,bit_lib_push_bit,void,"uint8_t*, size_t, _Bool" +Function,+,bit_lib_remove_bit_every_nth,size_t,"uint8_t*, size_t, uint8_t, uint8_t" +Function,+,bit_lib_reverse_16_fast,uint16_t,uint16_t +Function,+,bit_lib_reverse_8_fast,uint8_t,uint8_t +Function,+,bit_lib_reverse_bits,void,"uint8_t*, size_t, uint8_t" +Function,+,bit_lib_set_bit,void,"uint8_t*, size_t, _Bool" +Function,+,bit_lib_set_bits,void,"uint8_t*, size_t, uint8_t, uint8_t" +Function,+,bit_lib_test_parity,_Bool,"const uint8_t*, size_t, uint8_t, BitLibParity, uint8_t" +Function,+,bit_lib_test_parity_32,_Bool,"uint32_t, BitLibParity" +Function,+,ble_app_get_key_storage_buff,void,"uint8_t**, uint16_t*" +Function,+,ble_app_init,_Bool, +Function,+,ble_app_thread_stop,void, +Function,+,ble_glue_force_c2_mode,BleGlueCommandResult,BleGlueC2Mode +Function,-,ble_glue_fus_get_status,BleGlueCommandResult, +Function,-,ble_glue_fus_stack_delete,BleGlueCommandResult, +Function,-,ble_glue_fus_stack_install,BleGlueCommandResult,"uint32_t, uint32_t" +Function,-,ble_glue_fus_wait_operation,BleGlueCommandResult, +Function,+,ble_glue_get_c2_info,const BleGlueC2Info*, +Function,-,ble_glue_get_c2_status,BleGlueStatus, +Function,+,ble_glue_init,void, +Function,+,ble_glue_is_alive,_Bool, +Function,+,ble_glue_is_radio_stack_ready,_Bool, +Function,+,ble_glue_reinit_c2,_Bool, +Function,+,ble_glue_set_key_storage_changed_callback,void,"BleGlueKeyStorageChangedCallback, void*" +Function,+,ble_glue_start,_Bool, +Function,+,ble_glue_thread_stop,void, +Function,+,ble_glue_wait_for_c2_start,_Bool,int32_t +Function,-,bsearch,void*,"const void*, const void*, size_t, size_t, __compar_fn_t" +Function,+,bt_disconnect,void,Bt* +Function,+,bt_forget_bonded_devices,void,Bt* +Function,+,bt_keys_storage_set_default_path,void,Bt* +Function,+,bt_keys_storage_set_storage_path,void,"Bt*, const char*" +Function,+,bt_set_profile,_Bool,"Bt*, BtProfile" +Function,+,bt_set_status_changed_callback,void,"Bt*, BtStatusChangedCallback, void*" +Function,+,buffered_file_stream_alloc,Stream*,Storage* +Function,+,buffered_file_stream_close,_Bool,Stream* +Function,+,buffered_file_stream_get_error,FS_Error,Stream* +Function,+,buffered_file_stream_open,_Bool,"Stream*, const char*, FS_AccessMode, FS_OpenMode" +Function,+,buffered_file_stream_sync,_Bool,Stream* +Function,+,button_menu_add_item,ButtonMenuItem*,"ButtonMenu*, const char*, int32_t, ButtonMenuItemCallback, ButtonMenuItemType, void*" +Function,+,button_menu_alloc,ButtonMenu*, +Function,+,button_menu_free,void,ButtonMenu* +Function,+,button_menu_get_view,View*,ButtonMenu* +Function,+,button_menu_reset,void,ButtonMenu* +Function,+,button_menu_set_header,void,"ButtonMenu*, const char*" +Function,+,button_menu_set_selected_item,void,"ButtonMenu*, uint32_t" +Function,+,button_panel_add_icon,void,"ButtonPanel*, uint16_t, uint16_t, const Icon*" +Function,+,button_panel_add_item,void,"ButtonPanel*, uint32_t, uint16_t, uint16_t, uint16_t, uint16_t, const Icon*, const Icon*, ButtonItemCallback, void*" +Function,+,button_panel_add_label,void,"ButtonPanel*, uint16_t, uint16_t, Font, const char*" +Function,+,button_panel_alloc,ButtonPanel*, +Function,+,button_panel_free,void,ButtonPanel* +Function,+,button_panel_get_view,View*,ButtonPanel* +Function,+,button_panel_reserve,void,"ButtonPanel*, size_t, size_t" +Function,+,button_panel_reset,void,ButtonPanel* +Function,+,byte_input_alloc,ByteInput*, +Function,+,byte_input_free,void,ByteInput* +Function,+,byte_input_get_view,View*,ByteInput* +Function,+,byte_input_set_header_text,void,"ByteInput*, const char*" +Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, ByteChangedCallback, void*, uint8_t*, uint8_t" +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,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" +Function,+,canvas_draw_disc,void,"Canvas*, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_dot,void,"Canvas*, uint8_t, uint8_t" +Function,+,canvas_draw_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_glyph,void,"Canvas*, uint8_t, uint8_t, uint16_t" +Function,+,canvas_draw_icon,void,"Canvas*, uint8_t, uint8_t, const Icon*" +Function,+,canvas_draw_icon_animation,void,"Canvas*, uint8_t, uint8_t, IconAnimation*" +Function,+,canvas_draw_icon_ex,void,"Canvas*, uint8_t, uint8_t, const Icon*, IconRotation" +Function,+,canvas_draw_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_rbox,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_rframe,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" +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,const CanvasFontParameters*,"const Canvas*, Font" +Function,+,canvas_glyph_width,uint8_t,"Canvas*, uint16_t" +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,const Canvas* +Function,-,cbrt,double,double +Function,-,cbrtf,float,float +Function,-,cbrtl,long double,long double +Function,-,ceil,double,double +Function,-,ceilf,float,float +Function,-,ceill,long double,long double +Function,-,cfree,void,void* +Function,-,clearerr,void,FILE* +Function,-,clearerr_unlocked,void,FILE* +Function,+,cli_add_command,void,"Cli*, const char*, CliCommandFlag, CliCallback, void*" +Function,+,cli_cmd_interrupt_received,_Bool,Cli* +Function,+,cli_delete_command,void,"Cli*, const char*" +Function,+,cli_getc,char,Cli* +Function,+,cli_is_connected,_Bool,Cli* +Function,+,cli_nl,void, +Function,+,cli_print_usage,void,"const char*, const char*, const char*" +Function,+,cli_read,size_t,"Cli*, uint8_t*, size_t" +Function,+,cli_read_timeout,size_t,"Cli*, uint8_t*, size_t, uint32_t" +Function,+,cli_session_close,void,Cli* +Function,+,cli_session_open,void,"Cli*, void*" +Function,+,cli_write,void,"Cli*, const uint8_t*, size_t" +Function,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiInterface*" +Function,+,composite_api_resolver_alloc,CompositeApiResolver*, +Function,+,composite_api_resolver_free,void,CompositeApiResolver* +Function,+,composite_api_resolver_get,const ElfApiInterface*,CompositeApiResolver* +Function,+,compress_alloc,Compress*,uint16_t +Function,+,compress_decode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,+,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,+,compress_free,void,Compress* +Function,+,compress_icon_alloc,CompressIcon*, +Function,+,compress_icon_decode,void,"CompressIcon*, const uint8_t*, uint8_t**" +Function,+,compress_icon_free,void,CompressIcon* +Function,-,copysign,double,"double, double" +Function,-,copysignf,float,"float, float" +Function,-,copysignl,long double,"long double, long double" +Function,-,cos,double,double +Function,-,cosf,float,float +Function,-,cosh,double,double +Function,-,coshf,float,float +Function,-,coshl,long double,long double +Function,-,cosl,long double,long double +Function,+,crc32_calc_buffer,uint32_t,"uint32_t, const void*, size_t" +Function,+,crc32_calc_file,uint32_t,"File*, const FileCrcProgressCb, void*" +Function,-,ctermid,char*,char* +Function,-,cuserid,char*,char* +Function,+,dialog_ex_alloc,DialogEx*, +Function,+,dialog_ex_disable_extended_events,void,DialogEx* +Function,+,dialog_ex_enable_extended_events,void,DialogEx* +Function,+,dialog_ex_free,void,DialogEx* +Function,+,dialog_ex_get_view,View*,DialogEx* +Function,+,dialog_ex_reset,void,DialogEx* +Function,+,dialog_ex_set_center_button_text,void,"DialogEx*, const char*" +Function,+,dialog_ex_set_context,void,"DialogEx*, void*" +Function,+,dialog_ex_set_header,void,"DialogEx*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,dialog_ex_set_icon,void,"DialogEx*, uint8_t, uint8_t, const Icon*" +Function,+,dialog_ex_set_left_button_text,void,"DialogEx*, const char*" +Function,+,dialog_ex_set_result_callback,void,"DialogEx*, DialogExResultCallback" +Function,+,dialog_ex_set_right_button_text,void,"DialogEx*, const char*" +Function,+,dialog_ex_set_text,void,"DialogEx*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,dialog_file_browser_set_basic_options,void,"DialogsFileBrowserOptions*, const char*, const Icon*" +Function,+,dialog_file_browser_show,_Bool,"DialogsApp*, FuriString*, FuriString*, const DialogsFileBrowserOptions*" +Function,+,dialog_message_alloc,DialogMessage*, +Function,+,dialog_message_free,void,DialogMessage* +Function,+,dialog_message_set_buttons,void,"DialogMessage*, const char*, const char*, const char*" +Function,+,dialog_message_set_header,void,"DialogMessage*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,dialog_message_set_icon,void,"DialogMessage*, const Icon*, uint8_t, uint8_t" +Function,+,dialog_message_set_text,void,"DialogMessage*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,dialog_message_show,DialogMessageButton,"DialogsApp*, const DialogMessage*" +Function,+,dialog_message_show_storage_error,void,"DialogsApp*, const char*" +Function,+,digital_sequence_add_signal,void,"DigitalSequence*, uint8_t" +Function,-,digital_sequence_alloc,DigitalSequence*,"uint32_t, const GpioPin*" +Function,-,digital_sequence_clear,void,DigitalSequence* +Function,-,digital_sequence_free,void,DigitalSequence* +Function,+,digital_sequence_register_signal,void,"DigitalSequence*, uint8_t, const DigitalSignal*" +Function,+,digital_sequence_transmit,void,DigitalSequence* +Function,+,digital_signal_add_period,void,"DigitalSignal*, uint32_t" +Function,+,digital_signal_add_period_with_level,void,"DigitalSignal*, uint32_t, _Bool" +Function,-,digital_signal_alloc,DigitalSignal*,uint32_t +Function,-,digital_signal_free,void,DigitalSignal* +Function,+,digital_signal_get_size,uint32_t,const DigitalSignal* +Function,+,digital_signal_get_start_level,_Bool,const DigitalSignal* +Function,+,digital_signal_set_start_level,void,"DigitalSignal*, _Bool" +Function,-,diprintf,int,"int, const char*, ..." +Function,+,dir_walk_alloc,DirWalk*,Storage* +Function,+,dir_walk_close,void,DirWalk* +Function,+,dir_walk_free,void,DirWalk* +Function,+,dir_walk_get_error,FS_Error,DirWalk* +Function,+,dir_walk_open,_Bool,"DirWalk*, const char*" +Function,+,dir_walk_read,DirWalkResult,"DirWalk*, FuriString*, FileInfo*" +Function,+,dir_walk_set_filter_cb,void,"DirWalk*, DirWalkFilterCb, void*" +Function,+,dir_walk_set_recursive,void,"DirWalk*, _Bool" +Function,-,div,div_t,"int, int" +Function,+,dolphin_deed,void,DolphinDeed +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_pubsub,FuriPubSub*,Dolphin* +Function,+,dolphin_stats,DolphinStats,Dolphin* +Function,+,dolphin_upgrade_level,void,Dolphin* +Function,-,dprintf,int,"int, const char*, ..." +Function,-,drand48,double, +Function,-,drem,double,"double, double" +Function,-,dremf,float,"float, float" +Function,+,elements_bold_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_bubble,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_bubble_str,void,"Canvas*, uint8_t, uint8_t, const char*, Align, Align" +Function,+,elements_button_center,void,"Canvas*, const char*" +Function,+,elements_button_left,void,"Canvas*, const char*" +Function,+,elements_button_right,void,"Canvas*, const char*" +Function,+,elements_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_multiline_text,void,"Canvas*, uint8_t, uint8_t, const char*" +Function,+,elements_multiline_text_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*" +Function,+,elements_multiline_text_framed,void,"Canvas*, uint8_t, uint8_t, const char*" +Function,+,elements_progress_bar,void,"Canvas*, uint8_t, uint8_t, uint8_t, float" +Function,+,elements_progress_bar_with_text,void,"Canvas*, uint8_t, uint8_t, uint8_t, float, const char*" +Function,+,elements_scrollable_text_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, FuriString*, size_t, _Bool" +Function,+,elements_scrollbar,void,"Canvas*, uint16_t, uint16_t" +Function,+,elements_scrollbar_pos,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint16_t, uint16_t" +Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t" +Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" +Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, uint32_t, Elf32_Addr*" +Function,+,elf_symbolname_hash,uint32_t,const char* +Function,+,empty_screen_alloc,EmptyScreen*, +Function,+,empty_screen_free,void,EmptyScreen* +Function,+,empty_screen_get_view,View*,EmptyScreen* +Function,-,erand48,double,unsigned short[3] +Function,-,erf,double,double +Function,-,erfc,double,double +Function,-,erfcf,float,float +Function,-,erfcl,long double,long double +Function,-,erff,float,float +Function,-,erfl,long double,long double +Function,-,exit,void,int +Function,-,exp,double,double +Function,-,exp10,double,double +Function,-,exp10f,float,float +Function,-,exp2,double,double +Function,-,exp2f,float,float +Function,-,exp2l,long double,long double +Function,-,expf,float,float +Function,-,expl,long double,long double +Function,-,explicit_bzero,void,"void*, size_t" +Function,-,expm1,double,double +Function,-,expm1f,float,float +Function,-,expm1l,long double,long double +Function,-,fabs,double,double +Function,-,fabsf,float,float +Function,-,fabsl,long double,long double +Function,-,fclose,int,FILE* +Function,-,fcloseall,int, +Function,-,fdim,double,"double, double" +Function,-,fdimf,float,"float, float" +Function,-,fdiml,long double,"long double, long double" +Function,-,fdopen,FILE*,"int, const char*" +Function,-,feof,int,FILE* +Function,-,feof_unlocked,int,FILE* +Function,-,ferror,int,FILE* +Function,-,ferror_unlocked,int,FILE* +Function,-,fflush,int,FILE* +Function,-,fflush_unlocked,int,FILE* +Function,-,ffs,int,int +Function,-,ffsl,int,long +Function,-,ffsll,int,long long +Function,-,fgetc,int,FILE* +Function,-,fgetc_unlocked,int,FILE* +Function,-,fgetpos,int,"FILE*, fpos_t*" +Function,-,fgets,char*,"char*, int, FILE*" +Function,-,fgets_unlocked,char*,"char*, int, FILE*" +Function,+,file_browser_alloc,FileBrowser*,FuriString* +Function,+,file_browser_configure,void,"FileBrowser*, const char*, const char*, _Bool, _Bool, const Icon*, _Bool" +Function,+,file_browser_free,void,FileBrowser* +Function,+,file_browser_get_view,View*,FileBrowser* +Function,+,file_browser_set_callback,void,"FileBrowser*, FileBrowserCallback, void*" +Function,+,file_browser_set_item_callback,void,"FileBrowser*, FileBrowserLoadItemCallback, void*" +Function,+,file_browser_start,void,"FileBrowser*, FuriString*" +Function,+,file_browser_stop,void,FileBrowser* +Function,+,file_browser_worker_alloc,BrowserWorker*,"FuriString*, const char*, const char*, _Bool, _Bool" +Function,+,file_browser_worker_folder_enter,void,"BrowserWorker*, FuriString*, int32_t" +Function,+,file_browser_worker_folder_exit,void,BrowserWorker* +Function,+,file_browser_worker_folder_refresh,void,"BrowserWorker*, int32_t" +Function,+,file_browser_worker_free,void,BrowserWorker* +Function,+,file_browser_worker_is_in_start_folder,_Bool,BrowserWorker* +Function,+,file_browser_worker_load,void,"BrowserWorker*, uint32_t, uint32_t" +Function,+,file_browser_worker_set_callback_context,void,"BrowserWorker*, void*" +Function,+,file_browser_worker_set_config,void,"BrowserWorker*, FuriString*, const char*, _Bool, _Bool" +Function,+,file_browser_worker_set_folder_callback,void,"BrowserWorker*, BrowserWorkerFolderOpenCallback" +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* +Function,+,file_stream_open,_Bool,"Stream*, const char*, FS_AccessMode, FS_OpenMode" +Function,-,fileno,int,FILE* +Function,-,fileno_unlocked,int,FILE* +Function,+,filesystem_api_error_get_desc,const char*,FS_Error +Function,-,finite,int,double +Function,-,finitef,int,float +Function,-,finitel,int,long double +Function,-,fiprintf,int,"FILE*, const char*, ..." +Function,-,fiscanf,int,"FILE*, const char*, ..." +Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*" +Function,+,flipper_application_alloc_thread,FuriThread*,"FlipperApplication*, const char*" +Function,+,flipper_application_free,void,FlipperApplication* +Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication* +Function,+,flipper_application_is_plugin,_Bool,FlipperApplication* +Function,+,flipper_application_load_name_and_icon,_Bool,"FuriString*, Storage*, uint8_t**, FuriString*" +Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus +Function,+,flipper_application_manifest_is_target_compatible,_Bool,const FlipperApplicationManifest* +Function,+,flipper_application_manifest_is_too_new,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*" +Function,+,flipper_application_manifest_is_too_old,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*" +Function,+,flipper_application_manifest_is_valid,_Bool,const FlipperApplicationManifest* +Function,+,flipper_application_map_to_memory,FlipperApplicationLoadStatus,FlipperApplication* +Function,+,flipper_application_plugin_get_descriptor,const FlipperAppPluginDescriptor*,FlipperApplication* +Function,+,flipper_application_preload,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" +Function,+,flipper_application_preload_manifest,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" +Function,+,flipper_application_preload_status_to_string,const char*,FlipperApplicationPreloadStatus +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* +Function,+,flipper_format_file_close,_Bool,FlipperFormat* +Function,+,flipper_format_file_open_always,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_file_open_append,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_file_open_existing,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_file_open_new,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_free,void,FlipperFormat* +Function,+,flipper_format_get_raw_stream,Stream*,FlipperFormat* +Function,+,flipper_format_get_value_count,_Bool,"FlipperFormat*, const char*, uint32_t*" +Function,+,flipper_format_insert_or_update_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t" +Function,+,flipper_format_insert_or_update_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t" +Function,+,flipper_format_insert_or_update_hex,_Bool,"FlipperFormat*, const char*, const uint8_t*, const uint16_t" +Function,+,flipper_format_insert_or_update_int32,_Bool,"FlipperFormat*, const char*, const int32_t*, const uint16_t" +Function,+,flipper_format_insert_or_update_string,_Bool,"FlipperFormat*, const char*, FuriString*" +Function,+,flipper_format_insert_or_update_string_cstr,_Bool,"FlipperFormat*, const char*, const char*" +Function,+,flipper_format_insert_or_update_uint32,_Bool,"FlipperFormat*, const char*, const uint32_t*, const uint16_t" +Function,+,flipper_format_key_exist,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_read_bool,_Bool,"FlipperFormat*, const char*, _Bool*, const uint16_t" +Function,+,flipper_format_read_float,_Bool,"FlipperFormat*, const char*, float*, const uint16_t" +Function,+,flipper_format_read_header,_Bool,"FlipperFormat*, FuriString*, uint32_t*" +Function,+,flipper_format_read_hex,_Bool,"FlipperFormat*, const char*, uint8_t*, const uint16_t" +Function,+,flipper_format_read_hex_uint64,_Bool,"FlipperFormat*, const char*, uint64_t*, const uint16_t" +Function,+,flipper_format_read_int32,_Bool,"FlipperFormat*, const char*, int32_t*, const uint16_t" +Function,+,flipper_format_read_string,_Bool,"FlipperFormat*, const char*, FuriString*" +Function,+,flipper_format_read_uint32,_Bool,"FlipperFormat*, const char*, uint32_t*, const uint16_t" +Function,+,flipper_format_rewind,_Bool,FlipperFormat* +Function,+,flipper_format_seek_to_end,_Bool,FlipperFormat* +Function,+,flipper_format_set_strict_mode,void,"FlipperFormat*, _Bool" +Function,+,flipper_format_stream_delete_key_and_write,_Bool,"Stream*, FlipperStreamWriteData*, _Bool" +Function,+,flipper_format_stream_get_value_count,_Bool,"Stream*, const char*, uint32_t*, _Bool" +Function,+,flipper_format_stream_read_value_line,_Bool,"Stream*, const char*, FlipperStreamValue, void*, size_t, _Bool" +Function,+,flipper_format_stream_write_comment_cstr,_Bool,"Stream*, const char*" +Function,+,flipper_format_stream_write_value_line,_Bool,"Stream*, FlipperStreamWriteData*" +Function,+,flipper_format_string_alloc,FlipperFormat*, +Function,+,flipper_format_update_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t" +Function,+,flipper_format_update_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t" +Function,+,flipper_format_update_hex,_Bool,"FlipperFormat*, const char*, const uint8_t*, const uint16_t" +Function,+,flipper_format_update_int32,_Bool,"FlipperFormat*, const char*, const int32_t*, const uint16_t" +Function,+,flipper_format_update_string,_Bool,"FlipperFormat*, const char*, FuriString*" +Function,+,flipper_format_update_string_cstr,_Bool,"FlipperFormat*, const char*, const char*" +Function,+,flipper_format_update_uint32,_Bool,"FlipperFormat*, const char*, const uint32_t*, const uint16_t" +Function,+,flipper_format_write_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t" +Function,+,flipper_format_write_comment,_Bool,"FlipperFormat*, FuriString*" +Function,+,flipper_format_write_comment_cstr,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_write_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t" +Function,+,flipper_format_write_header,_Bool,"FlipperFormat*, FuriString*, const uint32_t" +Function,+,flipper_format_write_header_cstr,_Bool,"FlipperFormat*, const char*, const uint32_t" +Function,+,flipper_format_write_hex,_Bool,"FlipperFormat*, const char*, const uint8_t*, const uint16_t" +Function,+,flipper_format_write_hex_uint64,_Bool,"FlipperFormat*, const char*, const uint64_t*, const uint16_t" +Function,+,flipper_format_write_int32,_Bool,"FlipperFormat*, const char*, const int32_t*, const uint16_t" +Function,+,flipper_format_write_string,_Bool,"FlipperFormat*, const char*, FuriString*" +Function,+,flipper_format_write_string_cstr,_Bool,"FlipperFormat*, const char*, const char*" +Function,+,flipper_format_write_uint32,_Bool,"FlipperFormat*, const char*, const uint32_t*, const uint16_t" +Function,+,float_is_equal,_Bool,"float, float" +Function,-,flockfile,void,FILE* +Function,-,floor,double,double +Function,-,floorf,float,float +Function,-,floorl,long double,long double +Function,-,fls,int,int +Function,-,flsl,int,long +Function,-,flsll,int,long long +Function,-,fma,double,"double, double, double" +Function,-,fmaf,float,"float, float, float" +Function,-,fmal,long double,"long double, long double, long double" +Function,-,fmax,double,"double, double" +Function,-,fmaxf,float,"float, float" +Function,-,fmaxl,long double,"long double, long double" +Function,-,fmemopen,FILE*,"void*, size_t, const char*" +Function,-,fmin,double,"double, double" +Function,-,fminf,float,"float, float" +Function,-,fminl,long double,"long double, long double" +Function,-,fmod,double,"double, double" +Function,-,fmodf,float,"float, float" +Function,-,fmodl,long double,"long double, long double" +Function,-,fopen,FILE*,"const char*, const char*" +Function,-,fopencookie,FILE*,"void*, const char*, cookie_io_functions_t" +Function,-,fprintf,int,"FILE*, const char*, ..." +Function,-,fpurge,int,FILE* +Function,-,fputc,int,"int, FILE*" +Function,-,fputc_unlocked,int,"int, FILE*" +Function,-,fputs,int,"const char*, FILE*" +Function,-,fputs_unlocked,int,"const char*, FILE*" +Function,-,fread,size_t,"void*, size_t, size_t, FILE*" +Function,-,fread_unlocked,size_t,"void*, size_t, size_t, FILE*" +Function,+,free,void,void* +Function,-,freopen,FILE*,"const char*, const char*, FILE*" +Function,-,frexp,double,"double, int*" +Function,-,frexpf,float,"float, int*" +Function,-,frexpl,long double,"long double, int*" +Function,-,fscanf,int,"FILE*, const char*, ..." +Function,-,fseek,int,"FILE*, long, int" +Function,-,fseeko,int,"FILE*, off_t, int" +Function,-,fsetpos,int,"FILE*, const fpos_t*" +Function,-,ftell,long,FILE* +Function,-,ftello,off_t,FILE* +Function,-,ftrylockfile,int,FILE* +Function,-,funlockfile,void,FILE* +Function,-,funopen,FILE*,"const void*, int (*)(void*, char*, int), int (*)(void*, const char*, int), fpos_t (*)(void*, fpos_t, int), int (*)(void*)" +Function,+,furi_delay_ms,void,uint32_t +Function,+,furi_delay_tick,void,uint32_t +Function,+,furi_delay_until_tick,FuriStatus,uint32_t +Function,+,furi_delay_us,void,uint32_t +Function,+,furi_event_flag_alloc,FuriEventFlag*, +Function,+,furi_event_flag_clear,uint32_t,"FuriEventFlag*, uint32_t" +Function,+,furi_event_flag_free,void,FuriEventFlag* +Function,+,furi_event_flag_get,uint32_t,FuriEventFlag* +Function,+,furi_event_flag_set,uint32_t,"FuriEventFlag*, uint32_t" +Function,+,furi_event_flag_wait,uint32_t,"FuriEventFlag*, uint32_t, uint32_t, uint32_t" +Function,+,furi_get_tick,uint32_t, +Function,+,furi_hal_bt_change_app,_Bool,"FuriHalBtProfile, GapEventCallback, void*" +Function,+,furi_hal_bt_clear_white_list,_Bool, +Function,+,furi_hal_bt_dump_state,void,FuriString* +Function,+,furi_hal_bt_ensure_c2_mode,_Bool,BleGlueC2Mode +Function,-,furi_hal_bt_get_hardfault_info,const FuriHalBtHardfaultInfo*, +Function,+,furi_hal_bt_get_key_storage_buff,void,"uint8_t**, uint16_t*" +Function,+,furi_hal_bt_get_radio_stack,FuriHalBtStack, +Function,+,furi_hal_bt_get_rssi,float, +Function,+,furi_hal_bt_get_transmitted_packets,uint32_t, +Function,+,furi_hal_bt_hid_consumer_key_press,_Bool,uint16_t +Function,+,furi_hal_bt_hid_consumer_key_release,_Bool,uint16_t +Function,+,furi_hal_bt_hid_consumer_key_release_all,_Bool, +Function,+,furi_hal_bt_hid_kb_press,_Bool,uint16_t +Function,+,furi_hal_bt_hid_kb_release,_Bool,uint16_t +Function,+,furi_hal_bt_hid_kb_release_all,_Bool, +Function,+,furi_hal_bt_hid_mouse_move,_Bool,"int8_t, int8_t" +Function,+,furi_hal_bt_hid_mouse_press,_Bool,uint8_t +Function,+,furi_hal_bt_hid_mouse_release,_Bool,uint8_t +Function,+,furi_hal_bt_hid_mouse_release_all,_Bool, +Function,+,furi_hal_bt_hid_mouse_scroll,_Bool,int8_t +Function,+,furi_hal_bt_hid_start,void, +Function,+,furi_hal_bt_hid_stop,void, +Function,-,furi_hal_bt_init,void, +Function,+,furi_hal_bt_is_active,_Bool, +Function,+,furi_hal_bt_is_alive,_Bool, +Function,+,furi_hal_bt_is_ble_gatt_gap_supported,_Bool, +Function,+,furi_hal_bt_is_testing_supported,_Bool, +Function,+,furi_hal_bt_lock_core2,void, +Function,+,furi_hal_bt_nvm_sram_sem_acquire,void, +Function,+,furi_hal_bt_nvm_sram_sem_release,void, +Function,+,furi_hal_bt_reinit,void, +Function,+,furi_hal_bt_serial_notify_buffer_is_empty,void, +Function,+,furi_hal_bt_serial_set_event_callback,void,"uint16_t, FuriHalBtSerialCallback, void*" +Function,+,furi_hal_bt_serial_set_rpc_status,void,FuriHalBtSerialRpcStatus +Function,+,furi_hal_bt_serial_start,void, +Function,+,furi_hal_bt_serial_stop,void, +Function,+,furi_hal_bt_serial_tx,_Bool,"uint8_t*, uint16_t" +Function,+,furi_hal_bt_set_key_storage_change_callback,void,"BleGlueKeyStorageChangedCallback, void*" +Function,+,furi_hal_bt_start_advertising,void, +Function,+,furi_hal_bt_start_app,_Bool,"FuriHalBtProfile, GapEventCallback, void*" +Function,+,furi_hal_bt_start_packet_rx,void,"uint8_t, uint8_t" +Function,+,furi_hal_bt_start_packet_tx,void,"uint8_t, uint8_t, uint8_t" +Function,+,furi_hal_bt_start_radio_stack,_Bool, +Function,+,furi_hal_bt_start_rx,void,uint8_t +Function,+,furi_hal_bt_start_tone_tx,void,"uint8_t, uint8_t" +Function,+,furi_hal_bt_stop_advertising,void, +Function,+,furi_hal_bt_stop_packet_test,uint16_t, +Function,+,furi_hal_bt_stop_rx,void, +Function,+,furi_hal_bt_stop_tone_tx,void, +Function,+,furi_hal_bt_unlock_core2,void, +Function,+,furi_hal_bt_update_battery_level,void,uint8_t +Function,+,furi_hal_bt_update_power_state,void, +Function,+,furi_hal_bus_deinit_early,void, +Function,+,furi_hal_bus_disable,void,FuriHalBus +Function,+,furi_hal_bus_enable,void,FuriHalBus +Function,+,furi_hal_bus_init_early,void, +Function,+,furi_hal_bus_is_enabled,_Bool,FuriHalBus +Function,+,furi_hal_bus_reset,void,FuriHalBus +Function,+,furi_hal_ccid_ccid_insert_smartcard,void, +Function,+,furi_hal_ccid_ccid_remove_smartcard,void, +Function,+,furi_hal_ccid_set_callbacks,void,CcidCallbacks* +Function,+,furi_hal_cdc_get_ctrl_line_state,uint8_t,uint8_t +Function,+,furi_hal_cdc_get_port_settings,usb_cdc_line_coding*,uint8_t +Function,+,furi_hal_cdc_receive,int32_t,"uint8_t, uint8_t*, uint16_t" +Function,+,furi_hal_cdc_send,void,"uint8_t, uint8_t*, uint16_t" +Function,+,furi_hal_cdc_set_callbacks,void,"uint8_t, CdcCallbacks*, void*" +Function,-,furi_hal_clock_deinit_early,void, +Function,-,furi_hal_clock_init,void, +Function,-,furi_hal_clock_init_early,void, +Function,+,furi_hal_clock_mco_disable,void, +Function,+,furi_hal_clock_mco_enable,void,"FuriHalClockMcoSourceId, FuriHalClockMcoDivisorId" +Function,-,furi_hal_clock_resume_tick,void, +Function,-,furi_hal_clock_suspend_tick,void, +Function,-,furi_hal_clock_switch_hse2hsi,void, +Function,-,furi_hal_clock_switch_hse2pll,_Bool, +Function,-,furi_hal_clock_switch_hsi2hse,void, +Function,-,furi_hal_clock_switch_pll2hse,_Bool, +Function,+,furi_hal_console_disable,void, +Function,+,furi_hal_console_enable,void, +Function,+,furi_hal_console_init,void, +Function,+,furi_hal_console_printf,void,"const char[], ..." +Function,+,furi_hal_console_puts,void,const char* +Function,+,furi_hal_console_set_tx_callback,void,"FuriHalConsoleTxCallback, void*" +Function,+,furi_hal_console_tx,void,"const uint8_t*, size_t" +Function,+,furi_hal_console_tx_with_new_line,void,"const uint8_t*, size_t" +Function,+,furi_hal_cortex_comp_enable,void,"FuriHalCortexComp, FuriHalCortexCompFunction, uint32_t, uint32_t, FuriHalCortexCompSize" +Function,+,furi_hal_cortex_comp_reset,void,FuriHalCortexComp +Function,+,furi_hal_cortex_delay_us,void,uint32_t +Function,-,furi_hal_cortex_init_early,void, +Function,+,furi_hal_cortex_instructions_per_microsecond,uint32_t, +Function,+,furi_hal_cortex_timer_get,FuriHalCortexTimer,uint32_t +Function,+,furi_hal_cortex_timer_is_expired,_Bool,FuriHalCortexTimer +Function,+,furi_hal_cortex_timer_wait,void,FuriHalCortexTimer +Function,+,furi_hal_crypto_ctr,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, uint8_t*, size_t" +Function,+,furi_hal_crypto_decrypt,_Bool,"const uint8_t*, uint8_t*, size_t" +Function,+,furi_hal_crypto_enclave_ensure_key,_Bool,uint8_t +Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*" +Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*" +Function,+,furi_hal_crypto_enclave_unload_key,_Bool,uint8_t +Function,+,furi_hal_crypto_enclave_verify,_Bool,"uint8_t*, uint8_t*" +Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t" +Function,+,furi_hal_crypto_gcm,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*, _Bool" +Function,+,furi_hal_crypto_gcm_decrypt_and_verify,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, const uint8_t*" +Function,+,furi_hal_crypto_gcm_encrypt_and_tag,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*" +Function,-,furi_hal_crypto_init,void, +Function,+,furi_hal_crypto_load_key,_Bool,"const uint8_t*, const uint8_t*" +Function,+,furi_hal_crypto_unload_key,_Bool, +Function,+,furi_hal_debug_disable,void, +Function,+,furi_hal_debug_enable,void, +Function,+,furi_hal_debug_is_gdb_session_active,_Bool, +Function,-,furi_hal_deinit_early,void, +Function,+,furi_hal_dma_deinit_early,void, +Function,+,furi_hal_dma_init_early,void, +Function,-,furi_hal_flash_erase,void,uint8_t +Function,-,furi_hal_flash_get_base,size_t, +Function,-,furi_hal_flash_get_cycles_count,size_t, +Function,-,furi_hal_flash_get_free_end_address,const void*, +Function,-,furi_hal_flash_get_free_page_count,size_t, +Function,-,furi_hal_flash_get_free_page_start_address,size_t, +Function,-,furi_hal_flash_get_free_start_address,const void*, +Function,-,furi_hal_flash_get_page_number,int16_t,size_t +Function,-,furi_hal_flash_get_page_size,size_t, +Function,-,furi_hal_flash_get_read_block_size,size_t, +Function,-,furi_hal_flash_get_write_block_size,size_t, +Function,-,furi_hal_flash_init,void, +Function,-,furi_hal_flash_ob_apply,void, +Function,-,furi_hal_flash_ob_get_raw_ptr,const FuriHalFlashRawOptionByteData*, +Function,-,furi_hal_flash_ob_set_word,_Bool,"size_t, const uint32_t" +Function,-,furi_hal_flash_program_page,void,"const uint8_t, const uint8_t*, uint16_t" +Function,-,furi_hal_flash_write_dword,void,"size_t, uint64_t" +Function,+,furi_hal_gpio_add_int_callback,void,"const GpioPin*, GpioExtiCallback, void*" +Function,+,furi_hal_gpio_disable_int_callback,void,const GpioPin* +Function,+,furi_hal_gpio_enable_int_callback,void,const GpioPin* +Function,+,furi_hal_gpio_init,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed" +Function,+,furi_hal_gpio_init_ex,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed, const GpioAltFn" +Function,+,furi_hal_gpio_init_simple,void,"const GpioPin*, const GpioMode" +Function,+,furi_hal_gpio_remove_int_callback,void,const GpioPin* +Function,+,furi_hal_hid_consumer_key_press,_Bool,uint16_t +Function,+,furi_hal_hid_consumer_key_release,_Bool,uint16_t +Function,+,furi_hal_hid_get_led_state,uint8_t, +Function,+,furi_hal_hid_is_connected,_Bool, +Function,+,furi_hal_hid_kb_press,_Bool,uint16_t +Function,+,furi_hal_hid_kb_release,_Bool,uint16_t +Function,+,furi_hal_hid_kb_release_all,_Bool, +Function,+,furi_hal_hid_mouse_move,_Bool,"int8_t, int8_t" +Function,+,furi_hal_hid_mouse_press,_Bool,uint8_t +Function,+,furi_hal_hid_mouse_release,_Bool,uint8_t +Function,+,furi_hal_hid_mouse_scroll,_Bool,int8_t +Function,+,furi_hal_hid_set_state_callback,void,"HidStateCallback, void*" +Function,+,furi_hal_hid_u2f_get_request,uint32_t,uint8_t* +Function,+,furi_hal_hid_u2f_is_connected,_Bool, +Function,+,furi_hal_hid_u2f_send_response,void,"uint8_t*, uint8_t" +Function,+,furi_hal_hid_u2f_set_callback,void,"HidU2fCallback, void*" +Function,+,furi_hal_i2c_acquire,void,FuriHalI2cBusHandle* +Function,-,furi_hal_i2c_deinit_early,void, +Function,-,furi_hal_i2c_init,void, +Function,-,furi_hal_i2c_init_early,void, +Function,+,furi_hal_i2c_is_device_ready,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint32_t" +Function,+,furi_hal_i2c_read_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_read_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t*, uint32_t" +Function,+,furi_hal_i2c_read_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint32_t" +Function,+,furi_hal_i2c_release,void,FuriHalI2cBusHandle* +Function,+,furi_hal_i2c_rx,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_rx_ext,_Bool,"FuriHalI2cBusHandle*, uint16_t, _Bool, uint8_t*, size_t, FuriHalI2cBegin, FuriHalI2cEnd, uint32_t" +Function,+,furi_hal_i2c_trx,_Bool,"FuriHalI2cBusHandle*, uint8_t, const uint8_t*, size_t, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_tx,_Bool,"FuriHalI2cBusHandle*, uint8_t, const uint8_t*, size_t, uint32_t" +Function,+,furi_hal_i2c_tx_ext,_Bool,"FuriHalI2cBusHandle*, uint16_t, _Bool, const uint8_t*, size_t, FuriHalI2cBegin, FuriHalI2cEnd, uint32_t" +Function,+,furi_hal_i2c_write_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, const uint8_t*, size_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_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_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_info_get_api_version,void,"uint16_t*, uint16_t*" +Function,+,furi_hal_infrared_async_rx_set_capture_isr_callback,void,"FuriHalInfraredRxCaptureCallback, void*" +Function,+,furi_hal_infrared_async_rx_set_timeout,void,uint32_t +Function,+,furi_hal_infrared_async_rx_set_timeout_isr_callback,void,"FuriHalInfraredRxTimeoutCallback, void*" +Function,+,furi_hal_infrared_async_rx_start,void, +Function,+,furi_hal_infrared_async_rx_stop,void, +Function,+,furi_hal_infrared_async_tx_set_data_isr_callback,void,"FuriHalInfraredTxGetDataISRCallback, void*" +Function,+,furi_hal_infrared_async_tx_set_signal_sent_isr_callback,void,"FuriHalInfraredTxSignalSentISRCallback, void*" +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_is_busy,_Bool, +Function,-,furi_hal_init,void, +Function,-,furi_hal_init_early,void, +Function,-,furi_hal_interrupt_init,void, +Function,+,furi_hal_interrupt_set_isr,void,"FuriHalInterruptId, FuriHalInterruptISR, void*" +Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, uint16_t, FuriHalInterruptISR, void*" +Function,+,furi_hal_light_blink_set_color,void,Light +Function,+,furi_hal_light_blink_start,void,"Light, uint8_t, uint16_t, uint16_t" +Function,+,furi_hal_light_blink_stop,void, +Function,-,furi_hal_light_init,void, +Function,+,furi_hal_light_sequence,void,const char* +Function,+,furi_hal_light_set,void,"Light, uint8_t" +Function,+,furi_hal_memory_alloc,void*,size_t +Function,+,furi_hal_memory_get_free,size_t, +Function,+,furi_hal_memory_init,void, +Function,+,furi_hal_memory_max_pool_block,size_t, +Function,+,furi_hal_mpu_disable,void, +Function,+,furi_hal_mpu_enable,void, +Function,-,furi_hal_mpu_init,void, +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_abort,FuriHalNfcError, +Function,+,furi_hal_nfc_acquire,FuriHalNfcError, +Function,+,furi_hal_nfc_event_start,FuriHalNfcError, +Function,+,furi_hal_nfc_event_stop,FuriHalNfcError, +Function,+,furi_hal_nfc_felica_listener_set_sensf_res_data,FuriHalNfcError,"const uint8_t*, const uint8_t, const uint8_t*, const uint8_t" +Function,+,furi_hal_nfc_field_detect_start,FuriHalNfcError, +Function,+,furi_hal_nfc_field_detect_stop,FuriHalNfcError, +Function,+,furi_hal_nfc_field_is_present,_Bool, +Function,+,furi_hal_nfc_init,FuriHalNfcError, +Function,+,furi_hal_nfc_is_hal_ready,FuriHalNfcError, +Function,+,furi_hal_nfc_iso14443a_listener_set_col_res_data,FuriHalNfcError,"uint8_t*, uint8_t, uint8_t*, uint8_t" +Function,+,furi_hal_nfc_iso14443a_listener_tx_custom_parity,FuriHalNfcError,"const uint8_t*, const uint8_t*, size_t" +Function,+,furi_hal_nfc_iso14443a_poller_trx_short_frame,FuriHalNfcError,FuriHalNfcaShortFrame +Function,+,furi_hal_nfc_iso14443a_poller_tx_custom_parity,FuriHalNfcError,"const uint8_t*, size_t" +Function,+,furi_hal_nfc_iso14443a_rx_sdd_frame,FuriHalNfcError,"uint8_t*, size_t, size_t*" +Function,+,furi_hal_nfc_iso14443a_tx_sdd_frame,FuriHalNfcError,"const uint8_t*, size_t" +Function,+,furi_hal_nfc_iso15693_listener_tx_sof,FuriHalNfcError, +Function,+,furi_hal_nfc_listener_enable_rx,FuriHalNfcError, +Function,+,furi_hal_nfc_listener_idle,FuriHalNfcError, +Function,+,furi_hal_nfc_listener_rx,FuriHalNfcError,"uint8_t*, size_t, size_t*" +Function,+,furi_hal_nfc_listener_sleep,FuriHalNfcError, +Function,+,furi_hal_nfc_listener_tx,FuriHalNfcError,"const uint8_t*, size_t" +Function,+,furi_hal_nfc_listener_wait_event,FuriHalNfcEvent,uint32_t +Function,+,furi_hal_nfc_low_power_mode_start,FuriHalNfcError, +Function,+,furi_hal_nfc_low_power_mode_stop,FuriHalNfcError, +Function,+,furi_hal_nfc_poller_field_on,FuriHalNfcError, +Function,+,furi_hal_nfc_poller_rx,FuriHalNfcError,"uint8_t*, size_t, size_t*" +Function,+,furi_hal_nfc_poller_tx,FuriHalNfcError,"const uint8_t*, size_t" +Function,+,furi_hal_nfc_poller_wait_event,FuriHalNfcEvent,uint32_t +Function,+,furi_hal_nfc_release,FuriHalNfcError, +Function,+,furi_hal_nfc_reset_mode,FuriHalNfcError, +Function,+,furi_hal_nfc_set_mode,FuriHalNfcError,"FuriHalNfcMode, FuriHalNfcTech" +Function,+,furi_hal_nfc_timer_block_tx_is_running,_Bool, +Function,+,furi_hal_nfc_timer_block_tx_start,void,uint32_t +Function,+,furi_hal_nfc_timer_block_tx_start_us,void,uint32_t +Function,+,furi_hal_nfc_timer_block_tx_stop,void, +Function,+,furi_hal_nfc_timer_fwt_start,void,uint32_t +Function,+,furi_hal_nfc_timer_fwt_stop,void, +Function,+,furi_hal_nfc_trx_reset,FuriHalNfcError, +Function,-,furi_hal_os_init,void, +Function,+,furi_hal_os_tick,void, +Function,+,furi_hal_power_check_otg_fault,_Bool, +Function,+,furi_hal_power_check_otg_status,void, +Function,+,furi_hal_power_debug_get,void,"PropertyValueCallback, void*" +Function,+,furi_hal_power_disable_external_3_3v,void, +Function,+,furi_hal_power_disable_otg,void, +Function,+,furi_hal_power_enable_external_3_3v,void, +Function,+,furi_hal_power_enable_otg,_Bool, +Function,+,furi_hal_power_gauge_is_ok,_Bool, +Function,+,furi_hal_power_get_bat_health_pct,uint8_t, +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, +Function,+,furi_hal_power_get_battery_remaining_capacity,uint32_t, +Function,+,furi_hal_power_get_battery_temperature,float,FuriHalPowerIC +Function,+,furi_hal_power_get_battery_voltage,float,FuriHalPowerIC +Function,+,furi_hal_power_get_pct,uint8_t, +Function,+,furi_hal_power_get_usb_voltage,float, +Function,+,furi_hal_power_info_get,void,"PropertyValueCallback, char, void*" +Function,-,furi_hal_power_init,void, +Function,+,furi_hal_power_insomnia_enter,void, +Function,+,furi_hal_power_insomnia_exit,void, +Function,-,furi_hal_power_insomnia_level,uint16_t, +Function,+,furi_hal_power_is_charging,_Bool, +Function,+,furi_hal_power_is_charging_done,_Bool, +Function,+,furi_hal_power_is_otg_enabled,_Bool, +Function,+,furi_hal_power_is_shutdown_requested,_Bool, +Function,+,furi_hal_power_off,void, +Function,+,furi_hal_power_reset,void, +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, +Function,+,furi_hal_power_suppress_charge_enter,void, +Function,+,furi_hal_power_suppress_charge_exit,void, +Function,+,furi_hal_pwm_is_running,_Bool,FuriHalPwmOutputId +Function,+,furi_hal_pwm_set_params,void,"FuriHalPwmOutputId, uint32_t, uint8_t" +Function,+,furi_hal_pwm_start,void,"FuriHalPwmOutputId, uint32_t, uint8_t" +Function,+,furi_hal_pwm_stop,void,FuriHalPwmOutputId +Function,+,furi_hal_random_fill_buf,void,"uint8_t*, uint32_t" +Function,+,furi_hal_random_get,uint32_t, +Function,+,furi_hal_random_init,void, +Function,+,furi_hal_region_get,const FuriHalRegion*, +Function,+,furi_hal_region_get_band,const FuriHalRegionBand*,uint32_t +Function,+,furi_hal_region_get_name,const char*, +Function,-,furi_hal_region_init,void, +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_rfid_comp_set_callback,void,"FuriHalRfidCompCallback, void*" +Function,+,furi_hal_rfid_comp_start,void, +Function,+,furi_hal_rfid_comp_stop,void, +Function,+,furi_hal_rfid_field_detect_start,void, +Function,+,furi_hal_rfid_field_detect_stop,void, +Function,+,furi_hal_rfid_field_is_present,_Bool,uint32_t* +Function,-,furi_hal_rfid_init,void, +Function,+,furi_hal_rfid_pin_pull_pulldown,void, +Function,+,furi_hal_rfid_pin_pull_release,void, +Function,+,furi_hal_rfid_pins_reset,void, +Function,+,furi_hal_rfid_set_read_period,void,uint32_t +Function,+,furi_hal_rfid_set_read_pulse,void,uint32_t +Function,+,furi_hal_rfid_tim_emulate_dma_start,void,"uint32_t*, uint32_t*, size_t, FuriHalRfidDMACallback, void*" +Function,+,furi_hal_rfid_tim_emulate_dma_stop,void, +Function,+,furi_hal_rfid_tim_read_capture_start,void,"FuriHalRfidReadCaptureCallback, void*" +Function,+,furi_hal_rfid_tim_read_capture_stop,void, +Function,+,furi_hal_rfid_tim_read_continue,void, +Function,+,furi_hal_rfid_tim_read_pause,void, +Function,+,furi_hal_rfid_tim_read_start,void,"float, float" +Function,+,furi_hal_rfid_tim_read_stop,void, +Function,+,furi_hal_rtc_datetime_to_timestamp,uint32_t,FuriHalRtcDateTime* +Function,-,furi_hal_rtc_deinit_early,void, +Function,+,furi_hal_rtc_get_boot_mode,FuriHalRtcBootMode, +Function,+,furi_hal_rtc_get_datetime,void,FuriHalRtcDateTime* +Function,+,furi_hal_rtc_get_days_per_month,uint8_t,"_Bool, uint8_t" +Function,+,furi_hal_rtc_get_days_per_year,uint16_t,uint16_t +Function,+,furi_hal_rtc_get_fault_data,uint32_t, +Function,+,furi_hal_rtc_get_heap_track_mode,FuriHalRtcHeapTrackMode, +Function,+,furi_hal_rtc_get_locale_dateformat,FuriHalRtcLocaleDateFormat, +Function,+,furi_hal_rtc_get_locale_timeformat,FuriHalRtcLocaleTimeFormat, +Function,+,furi_hal_rtc_get_locale_units,FuriHalRtcLocaleUnits, +Function,+,furi_hal_rtc_get_log_level,uint8_t, +Function,+,furi_hal_rtc_get_pin_fails,uint32_t, +Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister +Function,+,furi_hal_rtc_get_timestamp,uint32_t, +Function,-,furi_hal_rtc_init,void, +Function,-,furi_hal_rtc_init_early,void, +Function,+,furi_hal_rtc_is_flag_set,_Bool,FuriHalRtcFlag +Function,+,furi_hal_rtc_is_leap_year,_Bool,uint16_t +Function,+,furi_hal_rtc_reset_flag,void,FuriHalRtcFlag +Function,+,furi_hal_rtc_set_boot_mode,void,FuriHalRtcBootMode +Function,+,furi_hal_rtc_set_datetime,void,FuriHalRtcDateTime* +Function,+,furi_hal_rtc_set_fault_data,void,uint32_t +Function,+,furi_hal_rtc_set_flag,void,FuriHalRtcFlag +Function,+,furi_hal_rtc_set_heap_track_mode,void,FuriHalRtcHeapTrackMode +Function,+,furi_hal_rtc_set_locale_dateformat,void,FuriHalRtcLocaleDateFormat +Function,+,furi_hal_rtc_set_locale_timeformat,void,FuriHalRtcLocaleTimeFormat +Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits +Function,+,furi_hal_rtc_set_log_level,void,uint8_t +Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t +Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" +Function,+,furi_hal_rtc_sync_shadow,void, +Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* +Function,+,furi_hal_sd_get_card_state,FuriStatus, +Function,+,furi_hal_sd_info,FuriStatus,FuriHalSdInfo* +Function,+,furi_hal_sd_init,FuriStatus,_Bool +Function,+,furi_hal_sd_is_present,_Bool, +Function,+,furi_hal_sd_max_mount_retry_count,uint8_t, +Function,+,furi_hal_sd_presence_init,void, +Function,+,furi_hal_sd_read_blocks,FuriStatus,"uint32_t*, uint32_t, uint32_t" +Function,+,furi_hal_sd_write_blocks,FuriStatus,"const uint32_t*, uint32_t, uint32_t" +Function,+,furi_hal_speaker_acquire,_Bool,uint32_t +Function,-,furi_hal_speaker_deinit,void, +Function,-,furi_hal_speaker_init,void, +Function,+,furi_hal_speaker_is_mine,_Bool, +Function,+,furi_hal_speaker_release,void, +Function,+,furi_hal_speaker_set_volume,void,float +Function,+,furi_hal_speaker_start,void,"float, float" +Function,+,furi_hal_speaker_stop,void, +Function,+,furi_hal_spi_acquire,void,FuriHalSpiBusHandle* +Function,+,furi_hal_spi_bus_deinit,void,FuriHalSpiBus* +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*, 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_subghz_dump_state,void, +Function,+,furi_hal_subghz_flush_rx,void, +Function,+,furi_hal_subghz_flush_tx,void, +Function,+,furi_hal_subghz_get_data_gpio,const GpioPin*, +Function,+,furi_hal_subghz_get_lqi,uint8_t, +Function,+,furi_hal_subghz_get_rssi,float, +Function,+,furi_hal_subghz_idle,void, +Function,-,furi_hal_subghz_init,void, +Function,+,furi_hal_subghz_is_async_tx_complete,_Bool, +Function,+,furi_hal_subghz_is_frequency_valid,_Bool,uint32_t +Function,+,furi_hal_subghz_is_rx_data_crc_valid,_Bool, +Function,+,furi_hal_subghz_load_custom_preset,void,const uint8_t* +Function,+,furi_hal_subghz_load_patable,void,const uint8_t[8] +Function,+,furi_hal_subghz_load_registers,void,const uint8_t* +Function,+,furi_hal_subghz_read_packet,void,"uint8_t*, uint8_t*" +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_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_shutdown,void, +Function,+,furi_hal_subghz_sleep,void, +Function,+,furi_hal_subghz_start_async_rx,void,"FuriHalSubGhzCaptureCallback, void*" +Function,+,furi_hal_subghz_start_async_tx,_Bool,"FuriHalSubGhzAsyncTxCallback, void*" +Function,+,furi_hal_subghz_stop_async_rx,void, +Function,+,furi_hal_subghz_stop_async_tx,void, +Function,+,furi_hal_subghz_tx,_Bool, +Function,+,furi_hal_subghz_write_packet,void,"const uint8_t*, uint8_t" +Function,+,furi_hal_switch,void,void* +Function,+,furi_hal_uart_deinit,void,FuriHalUartId +Function,+,furi_hal_uart_init,void,"FuriHalUartId, uint32_t" +Function,+,furi_hal_uart_resume,void,FuriHalUartId +Function,+,furi_hal_uart_set_br,void,"FuriHalUartId, uint32_t" +Function,+,furi_hal_uart_set_irq_cb,void,"FuriHalUartId, void (*)(UartIrqEvent, uint8_t, void*), void*" +Function,+,furi_hal_uart_suspend,void,FuriHalUartId +Function,+,furi_hal_uart_tx,void,"FuriHalUartId, uint8_t*, size_t" +Function,+,furi_hal_usb_disable,void, +Function,+,furi_hal_usb_enable,void, +Function,+,furi_hal_usb_get_config,FuriHalUsbInterface*, +Function,-,furi_hal_usb_init,void, +Function,+,furi_hal_usb_is_locked,_Bool, +Function,+,furi_hal_usb_lock,void, +Function,+,furi_hal_usb_reinit,void, +Function,+,furi_hal_usb_set_config,_Bool,"FuriHalUsbInterface*, void*" +Function,-,furi_hal_usb_set_state_callback,void,"FuriHalUsbStateCallback, void*" +Function,+,furi_hal_usb_unlock,void, +Function,+,furi_hal_version_do_i_belong_here,_Bool, +Function,+,furi_hal_version_get_ble_local_device_name_ptr,const char*, +Function,+,furi_hal_version_get_ble_mac,const uint8_t*, +Function,+,furi_hal_version_get_device_name_ptr,const char*, +Function,+,furi_hal_version_get_fcc_id,const char*, +Function,+,furi_hal_version_get_firmware_version,const Version*, +Function,+,furi_hal_version_get_hw_body,uint8_t, +Function,+,furi_hal_version_get_hw_color,FuriHalVersionColor, +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_target,uint8_t, +Function,+,furi_hal_version_get_hw_timestamp,uint32_t, +Function,+,furi_hal_version_get_hw_version,uint8_t, +Function,+,furi_hal_version_get_ic_id,const char*, +Function,+,furi_hal_version_get_mic_id,const char*, +Function,+,furi_hal_version_get_model_code,const char*, +Function,+,furi_hal_version_get_model_name,const char*, +Function,+,furi_hal_version_get_name_ptr,const char*, +Function,+,furi_hal_version_get_otp_version,FuriHalVersionOtpVersion, +Function,-,furi_hal_version_init,void, +Function,+,furi_hal_version_uid,const uint8_t*, +Function,+,furi_hal_version_uid_size,size_t, +Function,-,furi_hal_vibro_init,void, +Function,+,furi_hal_vibro_on,void,_Bool +Function,-,furi_init,void, +Function,+,furi_kernel_get_tick_frequency,uint32_t, +Function,+,furi_kernel_is_irq_or_masked,_Bool, +Function,+,furi_kernel_is_running,_Bool, +Function,+,furi_kernel_lock,int32_t, +Function,+,furi_kernel_restore_lock,int32_t,int32_t +Function,+,furi_kernel_unlock,int32_t, +Function,+,furi_log_get_level,FuriLogLevel, +Function,-,furi_log_init,void, +Function,+,furi_log_level_from_string,_Bool,"const char*, FuriLogLevel*" +Function,+,furi_log_level_to_string,_Bool,"FuriLogLevel, const char**" +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 +Function,+,furi_message_queue_alloc,FuriMessageQueue*,"uint32_t, uint32_t" +Function,+,furi_message_queue_free,void,FuriMessageQueue* +Function,+,furi_message_queue_get,FuriStatus,"FuriMessageQueue*, void*, uint32_t" +Function,+,furi_message_queue_get_capacity,uint32_t,FuriMessageQueue* +Function,+,furi_message_queue_get_count,uint32_t,FuriMessageQueue* +Function,+,furi_message_queue_get_message_size,uint32_t,FuriMessageQueue* +Function,+,furi_message_queue_get_space,uint32_t,FuriMessageQueue* +Function,+,furi_message_queue_put,FuriStatus,"FuriMessageQueue*, const void*, uint32_t" +Function,+,furi_message_queue_reset,FuriStatus,FuriMessageQueue* +Function,+,furi_ms_to_ticks,uint32_t,uint32_t +Function,+,furi_mutex_acquire,FuriStatus,"FuriMutex*, uint32_t" +Function,+,furi_mutex_alloc,FuriMutex*,FuriMutexType +Function,+,furi_mutex_free,void,FuriMutex* +Function,+,furi_mutex_get_owner,FuriThreadId,FuriMutex* +Function,+,furi_mutex_release,FuriStatus,FuriMutex* +Function,+,furi_pubsub_alloc,FuriPubSub*, +Function,-,furi_pubsub_free,void,FuriPubSub* +Function,+,furi_pubsub_publish,void,"FuriPubSub*, void*" +Function,+,furi_pubsub_subscribe,FuriPubSubSubscription*,"FuriPubSub*, FuriPubSubCallback, void*" +Function,+,furi_pubsub_unsubscribe,void,"FuriPubSub*, FuriPubSubSubscription*" +Function,+,furi_record_close,void,const char* +Function,+,furi_record_create,void,"const char*, void*" +Function,+,furi_record_destroy,_Bool,const char* +Function,+,furi_record_exists,_Bool,const char* +Function,-,furi_record_init,void, +Function,+,furi_record_open,void*,const char* +Function,+,furi_run,void, +Function,+,furi_semaphore_acquire,FuriStatus,"FuriSemaphore*, uint32_t" +Function,+,furi_semaphore_alloc,FuriSemaphore*,"uint32_t, uint32_t" +Function,+,furi_semaphore_free,void,FuriSemaphore* +Function,+,furi_semaphore_get_count,uint32_t,FuriSemaphore* +Function,+,furi_semaphore_release,FuriStatus,FuriSemaphore* +Function,+,furi_stream_buffer_alloc,FuriStreamBuffer*,"size_t, size_t" +Function,+,furi_stream_buffer_bytes_available,size_t,FuriStreamBuffer* +Function,+,furi_stream_buffer_free,void,FuriStreamBuffer* +Function,+,furi_stream_buffer_is_empty,_Bool,FuriStreamBuffer* +Function,+,furi_stream_buffer_is_full,_Bool,FuriStreamBuffer* +Function,+,furi_stream_buffer_receive,size_t,"FuriStreamBuffer*, void*, size_t, uint32_t" +Function,+,furi_stream_buffer_reset,FuriStatus,FuriStreamBuffer* +Function,+,furi_stream_buffer_send,size_t,"FuriStreamBuffer*, const void*, size_t, uint32_t" +Function,+,furi_stream_buffer_spaces_available,size_t,FuriStreamBuffer* +Function,+,furi_stream_set_trigger_level,_Bool,"FuriStreamBuffer*, size_t" +Function,+,furi_string_alloc,FuriString*, +Function,+,furi_string_alloc_move,FuriString*,FuriString* +Function,+,furi_string_alloc_printf,FuriString*,"const char[], ..." +Function,+,furi_string_alloc_set,FuriString*,const FuriString* +Function,+,furi_string_alloc_set_str,FuriString*,const char[] +Function,+,furi_string_alloc_vprintf,FuriString*,"const char[], va_list" +Function,+,furi_string_cat,void,"FuriString*, const FuriString*" +Function,+,furi_string_cat_printf,int,"FuriString*, const char[], ..." +Function,+,furi_string_cat_str,void,"FuriString*, const char[]" +Function,+,furi_string_cat_vprintf,int,"FuriString*, const char[], va_list" +Function,+,furi_string_cmp,int,"const FuriString*, const FuriString*" +Function,+,furi_string_cmp_str,int,"const FuriString*, const char[]" +Function,+,furi_string_cmpi,int,"const FuriString*, const FuriString*" +Function,+,furi_string_cmpi_str,int,"const FuriString*, const char[]" +Function,+,furi_string_empty,_Bool,const FuriString* +Function,+,furi_string_end_with,_Bool,"const FuriString*, const FuriString*" +Function,+,furi_string_end_with_str,_Bool,"const FuriString*, const char[]" +Function,+,furi_string_equal,_Bool,"const FuriString*, const FuriString*" +Function,+,furi_string_equal_str,_Bool,"const FuriString*, const char[]" +Function,+,furi_string_free,void,FuriString* +Function,+,furi_string_get_char,char,"const FuriString*, size_t" +Function,+,furi_string_get_cstr,const char*,const FuriString* +Function,+,furi_string_hash,size_t,const FuriString* +Function,+,furi_string_left,void,"FuriString*, size_t" +Function,+,furi_string_mid,void,"FuriString*, size_t, size_t" +Function,+,furi_string_move,void,"FuriString*, FuriString*" +Function,+,furi_string_printf,int,"FuriString*, const char[], ..." +Function,+,furi_string_push_back,void,"FuriString*, char" +Function,+,furi_string_replace,size_t,"FuriString*, FuriString*, FuriString*, size_t" +Function,+,furi_string_replace_all,void,"FuriString*, const FuriString*, const FuriString*" +Function,+,furi_string_replace_all_str,void,"FuriString*, const char[], const char[]" +Function,+,furi_string_replace_at,void,"FuriString*, size_t, size_t, const char[]" +Function,+,furi_string_replace_str,size_t,"FuriString*, const char[], const char[], size_t" +Function,+,furi_string_reserve,void,"FuriString*, size_t" +Function,+,furi_string_reset,void,FuriString* +Function,+,furi_string_right,void,"FuriString*, size_t" +Function,+,furi_string_search,size_t,"const FuriString*, const FuriString*, size_t" +Function,+,furi_string_search_char,size_t,"const FuriString*, char, size_t" +Function,+,furi_string_search_rchar,size_t,"const FuriString*, char, size_t" +Function,+,furi_string_search_str,size_t,"const FuriString*, const char[], size_t" +Function,+,furi_string_set,void,"FuriString*, FuriString*" +Function,+,furi_string_set_char,void,"FuriString*, size_t, const char" +Function,+,furi_string_set_n,void,"FuriString*, const FuriString*, size_t, size_t" +Function,+,furi_string_set_str,void,"FuriString*, const char[]" +Function,+,furi_string_set_strn,void,"FuriString*, const char[], size_t" +Function,+,furi_string_size,size_t,const FuriString* +Function,+,furi_string_start_with,_Bool,"const FuriString*, const FuriString*" +Function,+,furi_string_start_with_str,_Bool,"const FuriString*, const char[]" +Function,+,furi_string_swap,void,"FuriString*, FuriString*" +Function,+,furi_string_trim,void,"FuriString*, const char[]" +Function,+,furi_string_utf8_decode,void,"char, FuriStringUTF8State*, FuriStringUnicodeValue*" +Function,+,furi_string_utf8_length,size_t,FuriString* +Function,+,furi_string_utf8_push,void,"FuriString*, FuriStringUnicodeValue" +Function,+,furi_string_vprintf,int,"FuriString*, const char[], va_list" +Function,+,furi_thread_alloc,FuriThread*, +Function,+,furi_thread_alloc_ex,FuriThread*,"const char*, uint32_t, FuriThreadCallback, void*" +Function,-,furi_thread_disable_heap_trace,void,FuriThread* +Function,+,furi_thread_enable_heap_trace,void,FuriThread* +Function,+,furi_thread_enumerate,uint32_t,"FuriThreadId*, uint32_t" +Function,+,furi_thread_flags_clear,uint32_t,uint32_t +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 +Function,+,furi_thread_get_return_code,int32_t,FuriThread* +Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId +Function,+,furi_thread_get_state,FuriThreadState,FuriThread* +Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback, +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" +Function,+,furi_thread_set_state_callback,void,"FuriThread*, FuriThreadStateCallback" +Function,+,furi_thread_set_state_context,void,"FuriThread*, void*" +Function,+,furi_thread_set_stdout_callback,void,FuriThreadStdoutWriteCallback +Function,+,furi_thread_start,void,FuriThread* +Function,+,furi_thread_stdout_flush,int32_t, +Function,+,furi_thread_stdout_write,size_t,"const char*, size_t" +Function,+,furi_thread_suspend,void,FuriThreadId +Function,+,furi_thread_yield,void, +Function,+,furi_timer_alloc,FuriTimer*,"FuriTimerCallback, FuriTimerType, void*" +Function,+,furi_timer_free,void,FuriTimer* +Function,+,furi_timer_get_expire_time,uint32_t,FuriTimer* +Function,+,furi_timer_is_running,uint32_t,FuriTimer* +Function,+,furi_timer_pending_callback,void,"FuriTimerPendigCallback, void*, uint32_t" +Function,+,furi_timer_restart,FuriStatus,"FuriTimer*, uint32_t" +Function,+,furi_timer_set_thread_priority,void,FuriTimerThreadPriority +Function,+,furi_timer_start,FuriStatus,"FuriTimer*, uint32_t" +Function,+,furi_timer_stop,FuriStatus,FuriTimer* +Function,-,fwrite,size_t,"const void*, size_t, size_t, FILE*" +Function,-,fwrite_unlocked,size_t,"const void*, size_t, size_t, FILE*" +Function,-,gamma,double,double +Function,-,gamma_r,double,"double, int*" +Function,-,gammaf,float,float +Function,-,gammaf_r,float,"float, int*" +Function,-,gap_get_state,GapState, +Function,-,gap_init,_Bool,"GapConfig*, GapEventCallback, void*" +Function,-,gap_start_advertising,void, +Function,-,gap_stop_advertising,void, +Function,-,gap_thread_stop,void, +Function,-,getc,int,FILE* +Function,-,getc_unlocked,int,FILE* +Function,-,getchar,int, +Function,-,getchar_unlocked,int, +Function,-,getenv,char*,const char* +Function,-,gets,char*,char* +Function,-,getsubopt,int,"char**, char**, char**" +Function,-,getw,int,FILE* +Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" +Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" +Function,+,gui_direct_draw_acquire,Canvas*,Gui* +Function,+,gui_direct_draw_release,void,Gui* +Function,+,gui_get_framebuffer_size,size_t,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" +Function,-,gui_view_port_send_to_back,void,"Gui*, ViewPort*" +Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*" +Function,+,hex_char_to_hex_nibble,_Bool,"char, uint8_t*" +Function,+,hex_char_to_uint8,_Bool,"char, char, uint8_t*" +Function,+,hex_chars_to_uint64,_Bool,"const char*, uint64_t*" +Function,+,hex_chars_to_uint8,_Bool,"const char*, 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*,size_t +Function,+,ibutton_key_free,void,iButtonKey* +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* +Function,+,ibutton_worker_read_set_callback,void,"iButtonWorker*, iButtonWorkerReadCallback, void*" +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,+,icon_animation_alloc,IconAnimation*,const Icon* +Function,+,icon_animation_free,void,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* +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,-,ilogb,int,double +Function,-,ilogbf,int,float +Function,-,ilogbl,int,long double +Function,-,index,char*,"const char*, int" +Function,-,infinity,double, +Function,-,infinityf,float, +Function,+,infrared_alloc_decoder,InfraredDecoderHandler*, +Function,+,infrared_alloc_encoder,InfraredEncoderHandler*, +Function,+,infrared_check_decoder_ready,const InfraredMessage*,InfraredDecoderHandler* +Function,+,infrared_decode,const InfraredMessage*,"InfraredDecoderHandler*, _Bool, uint32_t" +Function,+,infrared_encode,InfraredStatus,"InfraredEncoderHandler*, uint32_t*, _Bool*" +Function,+,infrared_free_decoder,void,InfraredDecoderHandler* +Function,+,infrared_free_encoder,void,InfraredEncoderHandler* +Function,+,infrared_get_protocol_address_length,uint8_t,InfraredProtocol +Function,+,infrared_get_protocol_by_name,InfraredProtocol,const char* +Function,+,infrared_get_protocol_command_length,uint8_t,InfraredProtocol +Function,+,infrared_get_protocol_duty_cycle,float,InfraredProtocol +Function,+,infrared_get_protocol_frequency,uint32_t,InfraredProtocol +Function,+,infrared_get_protocol_min_repeat_count,size_t,InfraredProtocol +Function,+,infrared_get_protocol_name,const char*,InfraredProtocol +Function,+,infrared_is_protocol_valid,_Bool,InfraredProtocol +Function,+,infrared_reset_decoder,void,InfraredDecoderHandler* +Function,+,infrared_reset_encoder,void,"InfraredEncoderHandler*, const InfraredMessage*" +Function,+,infrared_send,void,"const InfraredMessage*, int" +Function,+,infrared_send_raw,void,"const uint32_t[], uint32_t, _Bool" +Function,+,infrared_send_raw_ext,void,"const uint32_t[], uint32_t, _Bool, uint32_t, float" +Function,+,infrared_worker_alloc,InfraredWorker*, +Function,+,infrared_worker_free,void,InfraredWorker* +Function,+,infrared_worker_get_decoded_signal,const InfraredMessage*,const InfraredWorkerSignal* +Function,+,infrared_worker_get_raw_signal,void,"const InfraredWorkerSignal*, const uint32_t**, size_t*" +Function,+,infrared_worker_rx_enable_blink_on_receiving,void,"InfraredWorker*, _Bool" +Function,+,infrared_worker_rx_enable_signal_decoding,void,"InfraredWorker*, _Bool" +Function,+,infrared_worker_rx_set_received_signal_callback,void,"InfraredWorker*, InfraredWorkerReceivedSignalCallback, void*" +Function,+,infrared_worker_rx_start,void,InfraredWorker* +Function,+,infrared_worker_rx_stop,void,InfraredWorker* +Function,+,infrared_worker_set_decoded_signal,void,"InfraredWorker*, const InfraredMessage*" +Function,+,infrared_worker_set_raw_signal,void,"InfraredWorker*, const uint32_t*, size_t, uint32_t, float" +Function,+,infrared_worker_signal_is_decoded,_Bool,const InfraredWorkerSignal* +Function,+,infrared_worker_tx_get_signal_steady_callback,InfraredWorkerGetSignalResponse,"void*, InfraredWorker*" +Function,+,infrared_worker_tx_set_get_signal_callback,void,"InfraredWorker*, InfraredWorkerGetSignalCallback, void*" +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,-,initstate,char*,"unsigned, char*, size_t" +Function,+,input_get_key_name,const char*,InputKey +Function,+,input_get_type_name,const char*,InputType +Function,-,iprintf,int,"const char*, ..." +Function,-,isalnum,int,int +Function,-,isalnum_l,int,"int, locale_t" +Function,-,isalpha,int,int +Function,-,isalpha_l,int,"int, locale_t" +Function,-,isascii,int,int +Function,-,isascii_l,int,"int, locale_t" +Function,-,isblank,int,int +Function,-,isblank_l,int,"int, locale_t" +Function,-,iscanf,int,"const char*, ..." +Function,-,iscntrl,int,int +Function,-,iscntrl_l,int,"int, locale_t" +Function,-,isdigit,int,int +Function,-,isdigit_l,int,"int, locale_t" +Function,-,isgraph,int,int +Function,-,isgraph_l,int,"int, locale_t" +Function,-,isinf,int,double +Function,-,isinff,int,float +Function,-,islower,int,int +Function,-,islower_l,int,"int, locale_t" +Function,-,isnan,int,double +Function,-,isnanf,int,float +Function,+,iso13239_crc_append,void,"Iso13239CrcType, BitBuffer*" +Function,+,iso13239_crc_check,_Bool,"Iso13239CrcType, const BitBuffer*" +Function,+,iso13239_crc_trim,void,BitBuffer* +Function,+,iso14443_3a_alloc,Iso14443_3aData*, +Function,+,iso14443_3a_copy,void,"Iso14443_3aData*, const Iso14443_3aData*" +Function,+,iso14443_3a_free,void,Iso14443_3aData* +Function,+,iso14443_3a_get_atqa,void,"const Iso14443_3aData*, uint8_t[2]" +Function,+,iso14443_3a_get_base_data,Iso14443_3aData*,const Iso14443_3aData* +Function,+,iso14443_3a_get_cuid,uint32_t,const Iso14443_3aData* +Function,+,iso14443_3a_get_device_name,const char*,"const Iso14443_3aData*, NfcDeviceNameType" +Function,+,iso14443_3a_get_sak,uint8_t,const Iso14443_3aData* +Function,+,iso14443_3a_get_uid,const uint8_t*,"const Iso14443_3aData*, size_t*" +Function,+,iso14443_3a_is_equal,_Bool,"const Iso14443_3aData*, const Iso14443_3aData*" +Function,+,iso14443_3a_load,_Bool,"Iso14443_3aData*, FlipperFormat*, uint32_t" +Function,+,iso14443_3a_poller_activate,Iso14443_3aError,"Iso14443_3aPoller*, Iso14443_3aData*" +Function,+,iso14443_3a_poller_check_presence,Iso14443_3aError,Iso14443_3aPoller* +Function,+,iso14443_3a_poller_halt,Iso14443_3aError,Iso14443_3aPoller* +Function,+,iso14443_3a_poller_send_standard_frame,Iso14443_3aError,"Iso14443_3aPoller*, const BitBuffer*, BitBuffer*, uint32_t" +Function,+,iso14443_3a_poller_sync_read,Iso14443_3aError,"Nfc*, Iso14443_3aData*" +Function,+,iso14443_3a_poller_txrx,Iso14443_3aError,"Iso14443_3aPoller*, const BitBuffer*, BitBuffer*, uint32_t" +Function,+,iso14443_3a_poller_txrx_custom_parity,Iso14443_3aError,"Iso14443_3aPoller*, const BitBuffer*, BitBuffer*, uint32_t" +Function,+,iso14443_3a_reset,void,Iso14443_3aData* +Function,+,iso14443_3a_save,_Bool,"const Iso14443_3aData*, FlipperFormat*" +Function,+,iso14443_3a_set_atqa,void,"Iso14443_3aData*, const uint8_t[2]" +Function,+,iso14443_3a_set_sak,void,"Iso14443_3aData*, uint8_t" +Function,+,iso14443_3a_set_uid,_Bool,"Iso14443_3aData*, const uint8_t*, size_t" +Function,+,iso14443_3a_supports_iso14443_4,_Bool,const Iso14443_3aData* +Function,+,iso14443_3a_verify,_Bool,"Iso14443_3aData*, const FuriString*" +Function,+,iso14443_3b_alloc,Iso14443_3bData*, +Function,+,iso14443_3b_copy,void,"Iso14443_3bData*, const Iso14443_3bData*" +Function,+,iso14443_3b_free,void,Iso14443_3bData* +Function,+,iso14443_3b_get_application_data,const uint8_t*,"const Iso14443_3bData*, size_t*" +Function,+,iso14443_3b_get_base_data,Iso14443_3bData*,const Iso14443_3bData* +Function,+,iso14443_3b_get_device_name,const char*,"const Iso14443_3bData*, NfcDeviceNameType" +Function,+,iso14443_3b_get_frame_size_max,uint16_t,const Iso14443_3bData* +Function,+,iso14443_3b_get_fwt_fc_max,uint32_t,const Iso14443_3bData* +Function,+,iso14443_3b_get_uid,const uint8_t*,"const Iso14443_3bData*, size_t*" +Function,+,iso14443_3b_is_equal,_Bool,"const Iso14443_3bData*, const Iso14443_3bData*" +Function,+,iso14443_3b_load,_Bool,"Iso14443_3bData*, FlipperFormat*, uint32_t" +Function,+,iso14443_3b_poller_activate,Iso14443_3bError,"Iso14443_3bPoller*, Iso14443_3bData*" +Function,+,iso14443_3b_poller_halt,Iso14443_3bError,Iso14443_3bPoller* +Function,+,iso14443_3b_poller_send_frame,Iso14443_3bError,"Iso14443_3bPoller*, const BitBuffer*, BitBuffer*" +Function,+,iso14443_3b_reset,void,Iso14443_3bData* +Function,+,iso14443_3b_save,_Bool,"const Iso14443_3bData*, FlipperFormat*" +Function,+,iso14443_3b_set_uid,_Bool,"Iso14443_3bData*, const uint8_t*, size_t" +Function,+,iso14443_3b_supports_bit_rate,_Bool,"const Iso14443_3bData*, Iso14443_3bBitRate" +Function,+,iso14443_3b_supports_frame_option,_Bool,"const Iso14443_3bData*, Iso14443_3bFrameOption" +Function,+,iso14443_3b_supports_iso14443_4,_Bool,const Iso14443_3bData* +Function,+,iso14443_3b_verify,_Bool,"Iso14443_3bData*, const FuriString*" +Function,+,iso14443_4a_alloc,Iso14443_4aData*, +Function,+,iso14443_4a_copy,void,"Iso14443_4aData*, const Iso14443_4aData*" +Function,+,iso14443_4a_free,void,Iso14443_4aData* +Function,+,iso14443_4a_get_base_data,Iso14443_3aData*,const Iso14443_4aData* +Function,+,iso14443_4a_get_device_name,const char*,"const Iso14443_4aData*, NfcDeviceNameType" +Function,+,iso14443_4a_get_frame_size_max,uint16_t,const Iso14443_4aData* +Function,+,iso14443_4a_get_fwt_fc_max,uint32_t,const Iso14443_4aData* +Function,+,iso14443_4a_get_historical_bytes,const uint8_t*,"const Iso14443_4aData*, uint32_t*" +Function,+,iso14443_4a_get_uid,const uint8_t*,"const Iso14443_4aData*, size_t*" +Function,+,iso14443_4a_is_equal,_Bool,"const Iso14443_4aData*, const Iso14443_4aData*" +Function,+,iso14443_4a_load,_Bool,"Iso14443_4aData*, FlipperFormat*, uint32_t" +Function,+,iso14443_4a_poller_halt,Iso14443_4aError,Iso14443_4aPoller* +Function,+,iso14443_4a_poller_read_ats,Iso14443_4aError,"Iso14443_4aPoller*, Iso14443_4aAtsData*" +Function,+,iso14443_4a_poller_send_block,Iso14443_4aError,"Iso14443_4aPoller*, const BitBuffer*, BitBuffer*" +Function,+,iso14443_4a_reset,void,Iso14443_4aData* +Function,+,iso14443_4a_save,_Bool,"const Iso14443_4aData*, FlipperFormat*" +Function,+,iso14443_4a_set_uid,_Bool,"Iso14443_4aData*, const uint8_t*, size_t" +Function,+,iso14443_4a_supports_bit_rate,_Bool,"const Iso14443_4aData*, Iso14443_4aBitRate" +Function,+,iso14443_4a_supports_frame_option,_Bool,"const Iso14443_4aData*, Iso14443_4aFrameOption" +Function,+,iso14443_4a_verify,_Bool,"Iso14443_4aData*, const FuriString*" +Function,+,iso14443_4b_alloc,Iso14443_4bData*, +Function,+,iso14443_4b_copy,void,"Iso14443_4bData*, const Iso14443_4bData*" +Function,+,iso14443_4b_free,void,Iso14443_4bData* +Function,+,iso14443_4b_get_base_data,Iso14443_3bData*,const Iso14443_4bData* +Function,+,iso14443_4b_get_device_name,const char*,"const Iso14443_4bData*, NfcDeviceNameType" +Function,+,iso14443_4b_get_uid,const uint8_t*,"const Iso14443_4bData*, size_t*" +Function,+,iso14443_4b_is_equal,_Bool,"const Iso14443_4bData*, const Iso14443_4bData*" +Function,+,iso14443_4b_load,_Bool,"Iso14443_4bData*, FlipperFormat*, uint32_t" +Function,+,iso14443_4b_poller_halt,Iso14443_4bError,Iso14443_4bPoller* +Function,+,iso14443_4b_poller_send_block,Iso14443_4bError,"Iso14443_4bPoller*, const BitBuffer*, BitBuffer*" +Function,+,iso14443_4b_reset,void,Iso14443_4bData* +Function,+,iso14443_4b_save,_Bool,"const Iso14443_4bData*, FlipperFormat*" +Function,+,iso14443_4b_set_uid,_Bool,"Iso14443_4bData*, const uint8_t*, size_t" +Function,+,iso14443_4b_verify,_Bool,"Iso14443_4bData*, const FuriString*" +Function,+,iso14443_crc_append,void,"Iso14443CrcType, BitBuffer*" +Function,+,iso14443_crc_check,_Bool,"Iso14443CrcType, const BitBuffer*" +Function,+,iso14443_crc_trim,void,BitBuffer* +Function,+,iso15693_3_alloc,Iso15693_3Data*, +Function,+,iso15693_3_copy,void,"Iso15693_3Data*, const Iso15693_3Data*" +Function,+,iso15693_3_free,void,Iso15693_3Data* +Function,+,iso15693_3_get_base_data,Iso15693_3Data*,const Iso15693_3Data* +Function,+,iso15693_3_get_block_count,uint16_t,const Iso15693_3Data* +Function,+,iso15693_3_get_block_data,const uint8_t*,"const Iso15693_3Data*, uint8_t" +Function,+,iso15693_3_get_block_size,uint8_t,const Iso15693_3Data* +Function,+,iso15693_3_get_device_name,const char*,"const Iso15693_3Data*, NfcDeviceNameType" +Function,+,iso15693_3_get_manufacturer_id,uint8_t,const Iso15693_3Data* +Function,+,iso15693_3_get_uid,const uint8_t*,"const Iso15693_3Data*, size_t*" +Function,+,iso15693_3_is_block_locked,_Bool,"const Iso15693_3Data*, uint8_t" +Function,+,iso15693_3_is_equal,_Bool,"const Iso15693_3Data*, const Iso15693_3Data*" +Function,+,iso15693_3_load,_Bool,"Iso15693_3Data*, FlipperFormat*, uint32_t" +Function,+,iso15693_3_reset,void,Iso15693_3Data* +Function,+,iso15693_3_save,_Bool,"const Iso15693_3Data*, FlipperFormat*" +Function,+,iso15693_3_set_uid,_Bool,"Iso15693_3Data*, const uint8_t*, size_t" +Function,+,iso15693_3_verify,_Bool,"Iso15693_3Data*, const FuriString*" +Function,-,isprint,int,int +Function,-,isprint_l,int,"int, locale_t" +Function,-,ispunct,int,int +Function,-,ispunct_l,int,"int, locale_t" +Function,-,isspace,int,int +Function,-,isspace_l,int,"int, locale_t" +Function,-,isupper,int,int +Function,-,isupper_l,int,"int, locale_t" +Function,-,isxdigit,int,int +Function,-,isxdigit_l,int,"int, locale_t" +Function,-,itoa,char*,"int, char*, int" +Function,-,j0,double,double +Function,-,j0f,float,float +Function,-,j1,double,double +Function,-,j1f,float,float +Function,-,jn,double,"int, double" +Function,-,jnf,float,"int, float" +Function,-,jrand48,long,unsigned short[3] +Function,-,l64a,char*,long +Function,-,labs,long,long +Function,-,lcong48,void,unsigned short[7] +Function,-,ldexp,double,"double, int" +Function,-,ldexpf,float,"float, int" +Function,-,ldexpl,long double,"long double, int" +Function,-,ldiv,ldiv_t,"long, long" +Function,+,lfrfid_dict_file_load,ProtocolId,"ProtocolDict*, const char*" +Function,+,lfrfid_dict_file_save,_Bool,"ProtocolDict*, ProtocolId, const char*" +Function,+,lfrfid_raw_file_alloc,LFRFIDRawFile*,Storage* +Function,+,lfrfid_raw_file_free,void,LFRFIDRawFile* +Function,+,lfrfid_raw_file_open_read,_Bool,"LFRFIDRawFile*, const char*" +Function,+,lfrfid_raw_file_open_write,_Bool,"LFRFIDRawFile*, const char*" +Function,+,lfrfid_raw_file_read_header,_Bool,"LFRFIDRawFile*, float*, float*" +Function,+,lfrfid_raw_file_read_pair,_Bool,"LFRFIDRawFile*, uint32_t*, uint32_t*, _Bool*" +Function,+,lfrfid_raw_file_write_buffer,_Bool,"LFRFIDRawFile*, uint8_t*, size_t" +Function,+,lfrfid_raw_file_write_header,_Bool,"LFRFIDRawFile*, float, float, uint32_t" +Function,+,lfrfid_raw_worker_alloc,LFRFIDRawWorker*, +Function,+,lfrfid_raw_worker_free,void,LFRFIDRawWorker* +Function,+,lfrfid_raw_worker_start_emulate,void,"LFRFIDRawWorker*, const char*, LFRFIDWorkerEmulateRawCallback, void*" +Function,+,lfrfid_raw_worker_start_read,void,"LFRFIDRawWorker*, const char*, float, float, LFRFIDWorkerReadRawCallback, void*" +Function,+,lfrfid_raw_worker_stop,void,LFRFIDRawWorker* +Function,+,lfrfid_worker_alloc,LFRFIDWorker*,ProtocolDict* +Function,+,lfrfid_worker_emulate_raw_start,void,"LFRFIDWorker*, const char*, LFRFIDWorkerEmulateRawCallback, void*" +Function,+,lfrfid_worker_emulate_start,void,"LFRFIDWorker*, LFRFIDProtocol" +Function,+,lfrfid_worker_free,void,LFRFIDWorker* +Function,+,lfrfid_worker_read_raw_start,void,"LFRFIDWorker*, const char*, LFRFIDWorkerReadType, LFRFIDWorkerReadRawCallback, void*" +Function,+,lfrfid_worker_read_start,void,"LFRFIDWorker*, LFRFIDWorkerReadType, LFRFIDWorkerReadCallback, void*" +Function,+,lfrfid_worker_start_thread,void,LFRFIDWorker* +Function,+,lfrfid_worker_stop,void,LFRFIDWorker* +Function,+,lfrfid_worker_stop_thread,void,LFRFIDWorker* +Function,+,lfrfid_worker_write_start,void,"LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*" +Function,-,lgamma,double,double +Function,-,lgamma_r,double,"double, int*" +Function,-,lgammaf,float,float +Function,-,lgammaf_r,float,"float, int*" +Function,-,lgammal,long double,long double +Function,-,llabs,long long,long long +Function,-,lldiv,lldiv_t,"long long, long long" +Function,-,llrint,long long int,double +Function,-,llrintf,long long int,float +Function,-,llrintl,long long int,long double +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_lock,_Bool,Loader* +Function,+,loader_show_menu,void,Loader* +Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*, FuriString*" +Function,+,loader_start_with_gui_error,LoaderStatus,"Loader*, const char*, const char*" +Function,+,loader_unlock,void,Loader* +Function,+,loading_alloc,Loading*, +Function,+,loading_free,void,Loading* +Function,+,loading_get_view,View*,Loading* +Function,+,locale_celsius_to_fahrenheit,float,float +Function,+,locale_fahrenheit_to_celsius,float,float +Function,+,locale_format_date,void,"FuriString*, const FuriHalRtcDateTime*, const LocaleDateFormat, const char*" +Function,+,locale_format_time,void,"FuriString*, const FuriHalRtcDateTime*, const LocaleTimeFormat, const _Bool" +Function,+,locale_get_date_format,LocaleDateFormat, +Function,+,locale_get_measurement_unit,LocaleMeasurementUnits, +Function,+,locale_get_time_format,LocaleTimeFormat, +Function,+,locale_set_date_format,void,LocaleDateFormat +Function,+,locale_set_measurement_unit,void,LocaleMeasurementUnits +Function,+,locale_set_time_format,void,LocaleTimeFormat +Function,-,log,double,double +Function,-,log10,double,double +Function,-,log10f,float,float +Function,-,log10l,long double,long double +Function,-,log1p,double,double +Function,-,log1pf,float,float +Function,-,log1pl,long double,long double +Function,-,log2,double,double +Function,-,log2f,float,float +Function,-,log2l,long double,long double +Function,-,logb,double,double +Function,-,logbf,float,float +Function,-,logbl,long double,long double +Function,-,logf,float,float +Function,-,logl,long double,long double +Function,-,lrand48,long, +Function,-,lrint,long int,double +Function,-,lrintf,long int,float +Function,-,lrintl,long int,long double +Function,-,lround,long int,double +Function,-,lroundf,long int,float +Function,-,lroundl,long,long double +Function,+,malloc,void*,size_t +Function,+,manchester_advance,_Bool,"ManchesterState, ManchesterEvent, ManchesterState*, _Bool*" +Function,+,manchester_encoder_advance,_Bool,"ManchesterEncoderState*, const _Bool, ManchesterEncoderResult*" +Function,+,manchester_encoder_finish,ManchesterEncoderResult,ManchesterEncoderState* +Function,+,manchester_encoder_reset,void,ManchesterEncoderState* +Function,+,maxim_crc8,uint8_t,"const uint8_t*, const uint8_t, const uint8_t" +Function,-,mbedtls_des3_crypt_cbc,int,"mbedtls_des3_context*, int, size_t, unsigned char[8], const unsigned char*, unsigned char*" +Function,-,mbedtls_des3_crypt_ecb,int,"mbedtls_des3_context*, const unsigned char[8], unsigned char[8]" +Function,-,mbedtls_des3_free,void,mbedtls_des3_context* +Function,-,mbedtls_des3_init,void,mbedtls_des3_context* +Function,-,mbedtls_des3_set2key_dec,int,"mbedtls_des3_context*, const unsigned char[8 * 2]" +Function,-,mbedtls_des3_set2key_enc,int,"mbedtls_des3_context*, const unsigned char[8 * 2]" +Function,-,mbedtls_des3_set3key_dec,int,"mbedtls_des3_context*, const unsigned char[8 * 3]" +Function,-,mbedtls_des3_set3key_enc,int,"mbedtls_des3_context*, const unsigned char[8 * 3]" +Function,-,mbedtls_des_crypt_cbc,int,"mbedtls_des_context*, int, size_t, unsigned char[8], const unsigned char*, unsigned char*" +Function,-,mbedtls_des_crypt_ecb,int,"mbedtls_des_context*, const unsigned char[8], unsigned char[8]" +Function,-,mbedtls_des_free,void,mbedtls_des_context* +Function,-,mbedtls_des_init,void,mbedtls_des_context* +Function,-,mbedtls_des_key_check_key_parity,int,const unsigned char[8] +Function,-,mbedtls_des_key_check_weak,int,const unsigned char[8] +Function,-,mbedtls_des_key_set_parity,void,unsigned char[8] +Function,-,mbedtls_des_setkey,void,"uint32_t[32], const unsigned char[8]" +Function,-,mbedtls_des_setkey_dec,int,"mbedtls_des_context*, const unsigned char[8]" +Function,-,mbedtls_des_setkey_enc,int,"mbedtls_des_context*, const unsigned char[8]" +Function,-,mbedtls_ecdh_calc_secret,int,"mbedtls_ecdh_context*, size_t*, unsigned char*, size_t, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecdh_can_do,int,mbedtls_ecp_group_id +Function,-,mbedtls_ecdh_compute_shared,int,"mbedtls_ecp_group*, mbedtls_mpi*, const mbedtls_ecp_point*, const mbedtls_mpi*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecdh_free,void,mbedtls_ecdh_context* +Function,-,mbedtls_ecdh_gen_public,int,"mbedtls_ecp_group*, mbedtls_mpi*, mbedtls_ecp_point*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecdh_get_params,int,"mbedtls_ecdh_context*, const mbedtls_ecp_keypair*, mbedtls_ecdh_side" +Function,-,mbedtls_ecdh_init,void,mbedtls_ecdh_context* +Function,-,mbedtls_ecdh_make_params,int,"mbedtls_ecdh_context*, size_t*, unsigned char*, size_t, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecdh_make_public,int,"mbedtls_ecdh_context*, size_t*, unsigned char*, size_t, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecdh_read_params,int,"mbedtls_ecdh_context*, const unsigned char**, const unsigned char*" +Function,-,mbedtls_ecdh_read_public,int,"mbedtls_ecdh_context*, const unsigned char*, size_t" +Function,-,mbedtls_ecdh_setup,int,"mbedtls_ecdh_context*, mbedtls_ecp_group_id" +Function,-,mbedtls_ecdsa_can_do,int,mbedtls_ecp_group_id +Function,-,mbedtls_ecdsa_free,void,mbedtls_ecdsa_context* +Function,-,mbedtls_ecdsa_from_keypair,int,"mbedtls_ecdsa_context*, const mbedtls_ecp_keypair*" +Function,-,mbedtls_ecdsa_genkey,int,"mbedtls_ecdsa_context*, mbedtls_ecp_group_id, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecdsa_init,void,mbedtls_ecdsa_context* +Function,-,mbedtls_ecdsa_read_signature,int,"mbedtls_ecdsa_context*, const unsigned char*, size_t, const unsigned char*, size_t" +Function,-,mbedtls_ecdsa_read_signature_restartable,int,"mbedtls_ecdsa_context*, const unsigned char*, size_t, const unsigned char*, size_t, mbedtls_ecdsa_restart_ctx*" +Function,-,mbedtls_ecdsa_sign,int,"mbedtls_ecp_group*, mbedtls_mpi*, mbedtls_mpi*, const mbedtls_mpi*, const unsigned char*, size_t, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecdsa_sign_restartable,int,"mbedtls_ecp_group*, mbedtls_mpi*, mbedtls_mpi*, const mbedtls_mpi*, const unsigned char*, size_t, int (*)(void*, unsigned char*, size_t), void*, int (*)(void*, unsigned char*, size_t), void*, mbedtls_ecdsa_restart_ctx*" +Function,-,mbedtls_ecdsa_verify,int,"mbedtls_ecp_group*, const unsigned char*, size_t, const mbedtls_ecp_point*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_ecdsa_verify_restartable,int,"mbedtls_ecp_group*, const unsigned char*, size_t, const mbedtls_ecp_point*, const mbedtls_mpi*, const mbedtls_mpi*, mbedtls_ecdsa_restart_ctx*" +Function,-,mbedtls_ecdsa_write_signature,int,"mbedtls_ecdsa_context*, mbedtls_md_type_t, const unsigned char*, size_t, unsigned char*, size_t, size_t*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecdsa_write_signature_restartable,int,"mbedtls_ecdsa_context*, mbedtls_md_type_t, const unsigned char*, size_t, unsigned char*, size_t, size_t*, int (*)(void*, unsigned char*, size_t), void*, mbedtls_ecdsa_restart_ctx*" +Function,-,mbedtls_ecp_check_privkey,int,"const mbedtls_ecp_group*, const mbedtls_mpi*" +Function,-,mbedtls_ecp_check_pub_priv,int,"const mbedtls_ecp_keypair*, const mbedtls_ecp_keypair*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecp_check_pubkey,int,"const mbedtls_ecp_group*, const mbedtls_ecp_point*" +Function,-,mbedtls_ecp_copy,int,"mbedtls_ecp_point*, const mbedtls_ecp_point*" +Function,-,mbedtls_ecp_curve_info_from_grp_id,const mbedtls_ecp_curve_info*,mbedtls_ecp_group_id +Function,-,mbedtls_ecp_curve_info_from_name,const mbedtls_ecp_curve_info*,const char* +Function,-,mbedtls_ecp_curve_info_from_tls_id,const mbedtls_ecp_curve_info*,uint16_t +Function,-,mbedtls_ecp_curve_list,const mbedtls_ecp_curve_info*, +Function,-,mbedtls_ecp_export,int,"const mbedtls_ecp_keypair*, mbedtls_ecp_group*, mbedtls_mpi*, mbedtls_ecp_point*" +Function,-,mbedtls_ecp_gen_key,int,"mbedtls_ecp_group_id, mbedtls_ecp_keypair*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecp_gen_keypair,int,"mbedtls_ecp_group*, mbedtls_mpi*, mbedtls_ecp_point*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecp_gen_keypair_base,int,"mbedtls_ecp_group*, const mbedtls_ecp_point*, mbedtls_mpi*, mbedtls_ecp_point*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecp_gen_privkey,int,"const mbedtls_ecp_group*, mbedtls_mpi*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecp_get_type,mbedtls_ecp_curve_type,const mbedtls_ecp_group* +Function,-,mbedtls_ecp_group_copy,int,"mbedtls_ecp_group*, const mbedtls_ecp_group*" +Function,-,mbedtls_ecp_group_free,void,mbedtls_ecp_group* +Function,-,mbedtls_ecp_group_init,void,mbedtls_ecp_group* +Function,-,mbedtls_ecp_group_load,int,"mbedtls_ecp_group*, mbedtls_ecp_group_id" +Function,-,mbedtls_ecp_grp_id_list,const mbedtls_ecp_group_id*, +Function,-,mbedtls_ecp_is_zero,int,mbedtls_ecp_point* +Function,-,mbedtls_ecp_keypair_free,void,mbedtls_ecp_keypair* +Function,-,mbedtls_ecp_keypair_init,void,mbedtls_ecp_keypair* +Function,-,mbedtls_ecp_mul,int,"mbedtls_ecp_group*, mbedtls_ecp_point*, const mbedtls_mpi*, const mbedtls_ecp_point*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_ecp_mul_restartable,int,"mbedtls_ecp_group*, mbedtls_ecp_point*, const mbedtls_mpi*, const mbedtls_ecp_point*, int (*)(void*, unsigned char*, size_t), void*, mbedtls_ecp_restart_ctx*" +Function,-,mbedtls_ecp_muladd,int,"mbedtls_ecp_group*, mbedtls_ecp_point*, const mbedtls_mpi*, const mbedtls_ecp_point*, const mbedtls_mpi*, const mbedtls_ecp_point*" +Function,-,mbedtls_ecp_muladd_restartable,int,"mbedtls_ecp_group*, mbedtls_ecp_point*, const mbedtls_mpi*, const mbedtls_ecp_point*, const mbedtls_mpi*, const mbedtls_ecp_point*, mbedtls_ecp_restart_ctx*" +Function,-,mbedtls_ecp_point_cmp,int,"const mbedtls_ecp_point*, const mbedtls_ecp_point*" +Function,-,mbedtls_ecp_point_free,void,mbedtls_ecp_point* +Function,-,mbedtls_ecp_point_init,void,mbedtls_ecp_point* +Function,-,mbedtls_ecp_point_read_binary,int,"const mbedtls_ecp_group*, mbedtls_ecp_point*, const unsigned char*, size_t" +Function,-,mbedtls_ecp_point_read_string,int,"mbedtls_ecp_point*, int, const char*, const char*" +Function,-,mbedtls_ecp_point_write_binary,int,"const mbedtls_ecp_group*, const mbedtls_ecp_point*, int, size_t*, unsigned char*, size_t" +Function,-,mbedtls_ecp_read_key,int,"mbedtls_ecp_group_id, mbedtls_ecp_keypair*, const unsigned char*, size_t" +Function,-,mbedtls_ecp_set_zero,int,mbedtls_ecp_point* +Function,-,mbedtls_ecp_tls_read_group,int,"mbedtls_ecp_group*, const unsigned char**, size_t" +Function,-,mbedtls_ecp_tls_read_group_id,int,"mbedtls_ecp_group_id*, const unsigned char**, size_t" +Function,-,mbedtls_ecp_tls_read_point,int,"const mbedtls_ecp_group*, mbedtls_ecp_point*, const unsigned char**, size_t" +Function,-,mbedtls_ecp_tls_write_group,int,"const mbedtls_ecp_group*, size_t*, unsigned char*, size_t" +Function,-,mbedtls_ecp_tls_write_point,int,"const mbedtls_ecp_group*, const mbedtls_ecp_point*, int, size_t*, unsigned char*, size_t" +Function,-,mbedtls_ecp_write_key,int,"mbedtls_ecp_keypair*, unsigned char*, size_t" +Function,-,mbedtls_internal_md5_process,int,"mbedtls_md5_context*, const unsigned char[64]" +Function,-,mbedtls_internal_sha1_process,int,"mbedtls_sha1_context*, const unsigned char[64]" +Function,-,mbedtls_internal_sha256_process,int,"mbedtls_sha256_context*, const unsigned char[64]" +Function,-,mbedtls_md,int,"const mbedtls_md_info_t*, const unsigned char*, size_t, unsigned char*" +Function,-,mbedtls_md5,int,"const unsigned char*, size_t, unsigned char[16]" +Function,-,mbedtls_md5_clone,void,"mbedtls_md5_context*, const mbedtls_md5_context*" +Function,-,mbedtls_md5_finish,int,"mbedtls_md5_context*, unsigned char[16]" +Function,-,mbedtls_md5_free,void,mbedtls_md5_context* +Function,-,mbedtls_md5_init,void,mbedtls_md5_context* +Function,-,mbedtls_md5_starts,int,mbedtls_md5_context* +Function,-,mbedtls_md5_update,int,"mbedtls_md5_context*, const unsigned char*, size_t" +Function,-,mbedtls_md_clone,int,"mbedtls_md_context_t*, const mbedtls_md_context_t*" +Function,-,mbedtls_md_finish,int,"mbedtls_md_context_t*, unsigned char*" +Function,-,mbedtls_md_free,void,mbedtls_md_context_t* +Function,-,mbedtls_md_get_name,const char*,const mbedtls_md_info_t* +Function,-,mbedtls_md_get_size,unsigned char,const mbedtls_md_info_t* +Function,-,mbedtls_md_get_type,mbedtls_md_type_t,const mbedtls_md_info_t* +Function,-,mbedtls_md_hmac,int,"const mbedtls_md_info_t*, const unsigned char*, size_t, const unsigned char*, size_t, unsigned char*" +Function,-,mbedtls_md_hmac_finish,int,"mbedtls_md_context_t*, unsigned char*" +Function,-,mbedtls_md_hmac_reset,int,mbedtls_md_context_t* +Function,-,mbedtls_md_hmac_starts,int,"mbedtls_md_context_t*, const unsigned char*, size_t" +Function,-,mbedtls_md_hmac_update,int,"mbedtls_md_context_t*, const unsigned char*, size_t" +Function,-,mbedtls_md_info_from_ctx,const mbedtls_md_info_t*,const mbedtls_md_context_t* +Function,-,mbedtls_md_info_from_string,const mbedtls_md_info_t*,const char* +Function,-,mbedtls_md_info_from_type,const mbedtls_md_info_t*,mbedtls_md_type_t +Function,-,mbedtls_md_init,void,mbedtls_md_context_t* +Function,-,mbedtls_md_list,const int*, +Function,-,mbedtls_md_setup,int,"mbedtls_md_context_t*, const mbedtls_md_info_t*, int" +Function,-,mbedtls_md_starts,int,mbedtls_md_context_t* +Function,-,mbedtls_md_update,int,"mbedtls_md_context_t*, const unsigned char*, size_t" +Function,-,mbedtls_mpi_add_abs,int,"mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_add_int,int,"mbedtls_mpi*, const mbedtls_mpi*, mbedtls_mpi_sint" +Function,-,mbedtls_mpi_add_mpi,int,"mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_bitlen,size_t,const mbedtls_mpi* +Function,-,mbedtls_mpi_cmp_abs,int,"const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_cmp_int,int,"const mbedtls_mpi*, mbedtls_mpi_sint" +Function,-,mbedtls_mpi_cmp_mpi,int,"const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_copy,int,"mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_div_int,int,"mbedtls_mpi*, mbedtls_mpi*, const mbedtls_mpi*, mbedtls_mpi_sint" +Function,-,mbedtls_mpi_div_mpi,int,"mbedtls_mpi*, mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_exp_mod,int,"mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*, mbedtls_mpi*" +Function,-,mbedtls_mpi_fill_random,int,"mbedtls_mpi*, size_t, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_mpi_free,void,mbedtls_mpi* +Function,-,mbedtls_mpi_gcd,int,"mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_gen_prime,int,"mbedtls_mpi*, size_t, int, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_mpi_get_bit,int,"const mbedtls_mpi*, size_t" +Function,-,mbedtls_mpi_grow,int,"mbedtls_mpi*, size_t" +Function,-,mbedtls_mpi_init,void,mbedtls_mpi* +Function,-,mbedtls_mpi_inv_mod,int,"mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_is_prime_ext,int,"const mbedtls_mpi*, int, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_mpi_lsb,size_t,const mbedtls_mpi* +Function,-,mbedtls_mpi_lset,int,"mbedtls_mpi*, mbedtls_mpi_sint" +Function,-,mbedtls_mpi_lt_mpi_ct,int,"const mbedtls_mpi*, const mbedtls_mpi*, unsigned*" +Function,-,mbedtls_mpi_mod_int,int,"mbedtls_mpi_uint*, const mbedtls_mpi*, mbedtls_mpi_sint" +Function,-,mbedtls_mpi_mod_mpi,int,"mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_mul_int,int,"mbedtls_mpi*, const mbedtls_mpi*, mbedtls_mpi_uint" +Function,-,mbedtls_mpi_mul_mpi,int,"mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_random,int,"mbedtls_mpi*, mbedtls_mpi_sint, const mbedtls_mpi*, int (*)(void*, unsigned char*, size_t), void*" +Function,-,mbedtls_mpi_read_binary,int,"mbedtls_mpi*, const unsigned char*, size_t" +Function,-,mbedtls_mpi_read_binary_le,int,"mbedtls_mpi*, const unsigned char*, size_t" +Function,-,mbedtls_mpi_read_string,int,"mbedtls_mpi*, int, const char*" +Function,-,mbedtls_mpi_safe_cond_assign,int,"mbedtls_mpi*, const mbedtls_mpi*, unsigned char" +Function,-,mbedtls_mpi_safe_cond_swap,int,"mbedtls_mpi*, mbedtls_mpi*, unsigned char" +Function,-,mbedtls_mpi_set_bit,int,"mbedtls_mpi*, size_t, unsigned char" +Function,-,mbedtls_mpi_shift_l,int,"mbedtls_mpi*, size_t" +Function,-,mbedtls_mpi_shift_r,int,"mbedtls_mpi*, size_t" +Function,-,mbedtls_mpi_shrink,int,"mbedtls_mpi*, size_t" +Function,-,mbedtls_mpi_size,size_t,const mbedtls_mpi* +Function,-,mbedtls_mpi_sub_abs,int,"mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_sub_int,int,"mbedtls_mpi*, const mbedtls_mpi*, mbedtls_mpi_sint" +Function,-,mbedtls_mpi_sub_mpi,int,"mbedtls_mpi*, const mbedtls_mpi*, const mbedtls_mpi*" +Function,-,mbedtls_mpi_swap,void,"mbedtls_mpi*, mbedtls_mpi*" +Function,-,mbedtls_mpi_write_binary,int,"const mbedtls_mpi*, unsigned char*, size_t" +Function,-,mbedtls_mpi_write_binary_le,int,"const mbedtls_mpi*, unsigned char*, size_t" +Function,-,mbedtls_mpi_write_string,int,"const mbedtls_mpi*, int, char*, size_t, size_t*" +Function,-,mbedtls_platform_zeroize,void,"void*, size_t" +Function,-,mbedtls_sha1,int,"const unsigned char*, size_t, unsigned char[20]" +Function,-,mbedtls_sha1_clone,void,"mbedtls_sha1_context*, const mbedtls_sha1_context*" +Function,-,mbedtls_sha1_finish,int,"mbedtls_sha1_context*, unsigned char[20]" +Function,-,mbedtls_sha1_free,void,mbedtls_sha1_context* +Function,-,mbedtls_sha1_init,void,mbedtls_sha1_context* +Function,-,mbedtls_sha1_starts,int,mbedtls_sha1_context* +Function,-,mbedtls_sha1_update,int,"mbedtls_sha1_context*, const unsigned char*, size_t" +Function,-,mbedtls_sha256,int,"const unsigned char*, size_t, unsigned char*, int" +Function,-,mbedtls_sha256_clone,void,"mbedtls_sha256_context*, const mbedtls_sha256_context*" +Function,-,mbedtls_sha256_finish,int,"mbedtls_sha256_context*, unsigned char*" +Function,-,mbedtls_sha256_free,void,mbedtls_sha256_context* +Function,-,mbedtls_sha256_init,void,mbedtls_sha256_context* +Function,-,mbedtls_sha256_starts,int,"mbedtls_sha256_context*, int" +Function,-,mbedtls_sha256_update,int,"mbedtls_sha256_context*, const unsigned char*, size_t" +Function,-,mblen,int,"const char*, size_t" +Function,-,mbstowcs,size_t,"wchar_t*, const char*, size_t" +Function,-,mbtowc,int,"wchar_t*, const char*, size_t" +Function,-,memccpy,void*,"void*, const void*, int, size_t" +Function,+,memchr,void*,"const void*, int, size_t" +Function,+,memcmp,int,"const void*, const void*, size_t" +Function,+,memcpy,void*,"void*, const void*, size_t" +Function,-,memmem,void*,"const void*, size_t, const void*, size_t" +Function,-,memmgr_alloc_from_pool,void*,size_t +Function,+,memmgr_get_free_heap,size_t, +Function,+,memmgr_get_minimum_free_heap,size_t, +Function,+,memmgr_get_total_heap,size_t, +Function,+,memmgr_heap_disable_thread_trace,void,FuriThreadId +Function,+,memmgr_heap_enable_thread_trace,void,FuriThreadId +Function,+,memmgr_heap_get_max_free_block,size_t, +Function,+,memmgr_heap_get_thread_memory,size_t,FuriThreadId +Function,+,memmgr_heap_printf_free_blocks,void, +Function,-,memmgr_pool_get_free,size_t, +Function,-,memmgr_pool_get_max_block,size_t, +Function,+,memmove,void*,"void*, const void*, size_t" +Function,-,mempcpy,void*,"void*, const void*, size_t" +Function,-,memrchr,void*,"const void*, int, size_t" +Function,+,memset,void*,"void*, int, size_t" +Function,+,menu_add_item,void,"Menu*, const char*, const Icon*, uint32_t, MenuItemCallback, void*" +Function,+,menu_alloc,Menu*, +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_alloc,MfClassicData*, +Function,+,mf_classic_block_to_value,_Bool,"const MfClassicBlock*, int32_t*, uint8_t*" +Function,+,mf_classic_copy,void,"MfClassicData*, const MfClassicData*" +Function,+,mf_classic_free,void,MfClassicData* +Function,+,mf_classic_get_base_data,Iso14443_3aData*,const MfClassicData* +Function,+,mf_classic_get_blocks_num_in_sector,uint8_t,uint8_t +Function,+,mf_classic_get_device_name,const char*,"const MfClassicData*, NfcDeviceNameType" +Function,+,mf_classic_get_first_block_num_of_sector,uint8_t,uint8_t +Function,+,mf_classic_get_read_sectors_and_keys,void,"const MfClassicData*, uint8_t*, uint8_t*" +Function,+,mf_classic_get_sector_by_block,uint8_t,uint8_t +Function,+,mf_classic_get_sector_trailer_by_sector,MfClassicSectorTrailer*,"const MfClassicData*, uint8_t" +Function,+,mf_classic_get_sector_trailer_num_by_block,uint8_t,uint8_t +Function,+,mf_classic_get_sector_trailer_num_by_sector,uint8_t,uint8_t +Function,+,mf_classic_get_total_block_num,uint16_t,MfClassicType +Function,+,mf_classic_get_total_sectors_num,uint8_t,MfClassicType +Function,+,mf_classic_get_uid,const uint8_t*,"const MfClassicData*, size_t*" +Function,+,mf_classic_is_allowed_access,_Bool,"MfClassicData*, uint8_t, MfClassicKeyType, MfClassicAction" +Function,+,mf_classic_is_allowed_access_data_block,_Bool,"MfClassicSectorTrailer*, uint8_t, MfClassicKeyType, MfClassicAction" +Function,+,mf_classic_is_block_read,_Bool,"const MfClassicData*, uint8_t" +Function,+,mf_classic_is_card_read,_Bool,const MfClassicData* +Function,+,mf_classic_is_equal,_Bool,"const MfClassicData*, const MfClassicData*" +Function,+,mf_classic_is_key_found,_Bool,"const MfClassicData*, uint8_t, MfClassicKeyType" +Function,+,mf_classic_is_sector_read,_Bool,"const MfClassicData*, uint8_t" +Function,+,mf_classic_is_sector_trailer,_Bool,uint8_t +Function,+,mf_classic_is_value_block,_Bool,"MfClassicSectorTrailer*, uint8_t" +Function,+,mf_classic_load,_Bool,"MfClassicData*, FlipperFormat*, uint32_t" +Function,+,mf_classic_poller_auth,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*" +Function,+,mf_classic_poller_auth_nested,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*" +Function,+,mf_classic_poller_get_nt_nested,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*" +Function,+,mf_classic_poller_get_nt,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*" +Function,+,mf_classic_poller_halt,MfClassicError,MfClassicPoller* +Function,+,mf_classic_poller_read_block,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicBlock*" +Function,+,mf_classic_poller_sync_auth,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*" +Function,+,mf_classic_poller_sync_change_value,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, int32_t, int32_t*" +Function,+,mf_classic_poller_sync_collect_nt,MfClassicError,"Nfc*, uint8_t, MfClassicKeyType, MfClassicNt*" +Function,+,mf_classic_poller_sync_detect_type,MfClassicError,"Nfc*, MfClassicType*" +Function,+,mf_classic_poller_sync_read,MfClassicError,"Nfc*, const MfClassicDeviceKeys*, MfClassicData*" +Function,+,mf_classic_poller_sync_read_block,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicBlock*" +Function,+,mf_classic_poller_sync_read_value,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, int32_t*" +Function,+,mf_classic_poller_sync_write_block,MfClassicError,"Nfc*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicBlock*" +Function,+,mf_classic_poller_value_cmd,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicValueCommand, int32_t" +Function,+,mf_classic_poller_value_transfer,MfClassicError,"MfClassicPoller*, uint8_t" +Function,+,mf_classic_poller_write_block,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicBlock*" +Function,+,mf_classic_reset,void,MfClassicData* +Function,+,mf_classic_save,_Bool,"const MfClassicData*, FlipperFormat*" +Function,+,mf_classic_set_block_read,void,"MfClassicData*, uint8_t, MfClassicBlock*" +Function,+,mf_classic_set_key_found,void,"MfClassicData*, uint8_t, MfClassicKeyType, uint64_t" +Function,+,mf_classic_set_key_not_found,void,"MfClassicData*, uint8_t, MfClassicKeyType" +Function,+,mf_classic_set_uid,_Bool,"MfClassicData*, const uint8_t*, size_t" +Function,+,mf_classic_value_to_block,void,"int32_t, uint8_t, MfClassicBlock*" +Function,+,mf_classic_verify,_Bool,"MfClassicData*, const FuriString*" +Function,+,mf_desfire_alloc,MfDesfireData*, +Function,+,mf_desfire_copy,void,"MfDesfireData*, const MfDesfireData*" +Function,+,mf_desfire_free,void,MfDesfireData* +Function,+,mf_desfire_get_application,const MfDesfireApplication*,"const MfDesfireData*, const MfDesfireApplicationId*" +Function,+,mf_desfire_get_base_data,Iso14443_4aData*,const MfDesfireData* +Function,+,mf_desfire_get_device_name,const char*,"const MfDesfireData*, NfcDeviceNameType" +Function,+,mf_desfire_get_file_data,const MfDesfireFileData*,"const MfDesfireApplication*, const MfDesfireFileId*" +Function,+,mf_desfire_get_file_settings,const MfDesfireFileSettings*,"const MfDesfireApplication*, const MfDesfireFileId*" +Function,+,mf_desfire_get_uid,const uint8_t*,"const MfDesfireData*, size_t*" +Function,+,mf_desfire_is_equal,_Bool,"const MfDesfireData*, const MfDesfireData*" +Function,+,mf_desfire_load,_Bool,"MfDesfireData*, FlipperFormat*, uint32_t" +Function,+,mf_desfire_poller_read_application,MfDesfireError,"MfDesfirePoller*, MfDesfireApplication*" +Function,+,mf_desfire_poller_read_application_ids,MfDesfireError,"MfDesfirePoller*, SimpleArray*" +Function,+,mf_desfire_poller_read_applications,MfDesfireError,"MfDesfirePoller*, const SimpleArray*, SimpleArray*" +Function,+,mf_desfire_poller_read_file_data,MfDesfireError,"MfDesfirePoller*, MfDesfireFileId, uint32_t, size_t, MfDesfireFileData*" +Function,+,mf_desfire_poller_read_file_data_multi,MfDesfireError,"MfDesfirePoller*, const SimpleArray*, const SimpleArray*, SimpleArray*" +Function,+,mf_desfire_poller_read_file_ids,MfDesfireError,"MfDesfirePoller*, SimpleArray*" +Function,+,mf_desfire_poller_read_file_records,MfDesfireError,"MfDesfirePoller*, MfDesfireFileId, uint32_t, size_t, MfDesfireFileData*" +Function,+,mf_desfire_poller_read_file_settings,MfDesfireError,"MfDesfirePoller*, MfDesfireFileId, MfDesfireFileSettings*" +Function,+,mf_desfire_poller_read_file_settings_multi,MfDesfireError,"MfDesfirePoller*, const SimpleArray*, SimpleArray*" +Function,+,mf_desfire_poller_read_file_value,MfDesfireError,"MfDesfirePoller*, MfDesfireFileId, MfDesfireFileData*" +Function,+,mf_desfire_poller_read_free_memory,MfDesfireError,"MfDesfirePoller*, MfDesfireFreeMemory*" +Function,+,mf_desfire_poller_read_key_settings,MfDesfireError,"MfDesfirePoller*, MfDesfireKeySettings*" +Function,+,mf_desfire_poller_read_key_versions,MfDesfireError,"MfDesfirePoller*, SimpleArray*, uint32_t" +Function,+,mf_desfire_poller_read_version,MfDesfireError,"MfDesfirePoller*, MfDesfireVersion*" +Function,+,mf_desfire_poller_select_application,MfDesfireError,"MfDesfirePoller*, const MfDesfireApplicationId*" +Function,+,mf_desfire_reset,void,MfDesfireData* +Function,+,mf_desfire_save,_Bool,"const MfDesfireData*, FlipperFormat*" +Function,+,mf_desfire_send_chunks,MfDesfireError,"MfDesfirePoller*, const BitBuffer*, BitBuffer*" +Function,+,mf_desfire_set_uid,_Bool,"MfDesfireData*, const uint8_t*, size_t" +Function,+,mf_desfire_verify,_Bool,"MfDesfireData*, const FuriString*" +Function,+,mf_ultralight_alloc,MfUltralightData*, +Function,+,mf_ultralight_copy,void,"MfUltralightData*, const MfUltralightData*" +Function,+,mf_ultralight_detect_protocol,_Bool,const Iso14443_3aData* +Function,+,mf_ultralight_free,void,MfUltralightData* +Function,+,mf_ultralight_get_base_data,Iso14443_3aData*,const MfUltralightData* +Function,+,mf_ultralight_get_config_page,_Bool,"const MfUltralightData*, MfUltralightConfigPages**" +Function,+,mf_ultralight_get_config_page_num,uint16_t,MfUltralightType +Function,+,mf_ultralight_get_device_name,const char*,"const MfUltralightData*, NfcDeviceNameType" +Function,+,mf_ultralight_get_feature_support_set,uint32_t,MfUltralightType +Function,+,mf_ultralight_get_pages_total,uint16_t,MfUltralightType +Function,+,mf_ultralight_get_pwd_page_num,uint8_t,MfUltralightType +Function,+,mf_ultralight_get_type_by_version,MfUltralightType,MfUltralightVersion* +Function,+,mf_ultralight_get_uid,const uint8_t*,"const MfUltralightData*, size_t*" +Function,+,mf_ultralight_is_all_data_read,_Bool,const MfUltralightData* +Function,+,mf_ultralight_is_counter_configured,_Bool,const MfUltralightData* +Function,+,mf_ultralight_is_equal,_Bool,"const MfUltralightData*, const MfUltralightData*" +Function,+,mf_ultralight_is_page_pwd_or_pack,_Bool,"MfUltralightType, uint16_t" +Function,+,mf_ultralight_load,_Bool,"MfUltralightData*, FlipperFormat*, uint32_t" +Function,+,mf_ultralight_poller_auth_pwd,MfUltralightError,"MfUltralightPoller*, MfUltralightPollerAuthContext*" +Function,+,mf_ultralight_poller_authenticate,MfUltralightError,MfUltralightPoller* +Function,+,mf_ultralight_poller_read_counter,MfUltralightError,"MfUltralightPoller*, uint8_t, MfUltralightCounter*" +Function,+,mf_ultralight_poller_read_page,MfUltralightError,"MfUltralightPoller*, uint8_t, MfUltralightPageReadCommandData*" +Function,+,mf_ultralight_poller_read_page_from_sector,MfUltralightError,"MfUltralightPoller*, uint8_t, uint8_t, MfUltralightPageReadCommandData*" +Function,+,mf_ultralight_poller_read_signature,MfUltralightError,"MfUltralightPoller*, MfUltralightSignature*" +Function,+,mf_ultralight_poller_read_tearing_flag,MfUltralightError,"MfUltralightPoller*, uint8_t, MfUltralightTearingFlag*" +Function,+,mf_ultralight_poller_read_version,MfUltralightError,"MfUltralightPoller*, MfUltralightVersion*" +Function,+,mf_ultralight_poller_sync_read_card,MfUltralightError,"Nfc*, MfUltralightData*" +Function,+,mf_ultralight_poller_sync_read_counter,MfUltralightError,"Nfc*, uint8_t, MfUltralightCounter*" +Function,+,mf_ultralight_poller_sync_read_page,MfUltralightError,"Nfc*, uint16_t, MfUltralightPage*" +Function,+,mf_ultralight_poller_sync_read_signature,MfUltralightError,"Nfc*, MfUltralightSignature*" +Function,+,mf_ultralight_poller_sync_read_tearing_flag,MfUltralightError,"Nfc*, uint8_t, MfUltralightTearingFlag*" +Function,+,mf_ultralight_poller_sync_read_version,MfUltralightError,"Nfc*, MfUltralightVersion*" +Function,+,mf_ultralight_poller_sync_write_page,MfUltralightError,"Nfc*, uint16_t, MfUltralightPage*" +Function,+,mf_ultralight_poller_write_page,MfUltralightError,"MfUltralightPoller*, uint8_t, const MfUltralightPage*" +Function,+,mf_ultralight_reset,void,MfUltralightData* +Function,+,mf_ultralight_save,_Bool,"const MfUltralightData*, FlipperFormat*" +Function,+,mf_ultralight_set_uid,_Bool,"MfUltralightData*, const uint8_t*, size_t" +Function,+,mf_ultralight_support_feature,_Bool,"const uint32_t, const uint32_t" +Function,+,mf_ultralight_verify,_Bool,"MfUltralightData*, const FuriString*" +Function,-,mkdtemp,char*,char* +Function,-,mkostemp,int,"char*, int" +Function,-,mkostemps,int,"char*, int, int" +Function,-,mkstemp,int,char* +Function,-,mkstemps,int,"char*, int" +Function,-,mktemp,char*,char* +Function,-,modf,double,"double, double*" +Function,-,modff,float,"float, float*" +Function,-,modfl,long double,"long double, long double*" +Function,-,mrand48,long, +Function,-,music_worker_alloc,MusicWorker*, +Function,-,music_worker_clear,void,MusicWorker* +Function,-,music_worker_free,void,MusicWorker* +Function,-,music_worker_is_playing,_Bool,MusicWorker* +Function,-,music_worker_load,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_fmf_from_file,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_rtttl_from_file,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_rtttl_from_string,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_set_callback,void,"MusicWorker*, MusicWorkerCallback, void*" +Function,-,music_worker_set_volume,void,"MusicWorker*, float" +Function,-,music_worker_start,void,MusicWorker* +Function,-,music_worker_stop,void,MusicWorker* +Function,+,name_generator_make_auto,void,"char*, size_t, const char*" +Function,+,name_generator_make_detailed,void,"char*, size_t, const char*" +Function,+,name_generator_make_random,void,"char*, size_t" +Function,-,nan,double,const char* +Function,-,nanf,float,const char* +Function,-,nanl,long double,const char* +Function,-,nearbyint,double,double +Function,-,nearbyintf,float,float +Function,-,nearbyintl,long double,long double +Function,-,nextafter,double,"double, double" +Function,-,nextafterf,float,"float, float" +Function,-,nextafterl,long double,"long double, long double" +Function,-,nexttoward,double,"double, long double" +Function,-,nexttowardf,float,"float, long double" +Function,-,nexttowardl,long double,"long double, long double" +Function,+,nfc_alloc,Nfc*, +Function,+,nfc_config,void,"Nfc*, NfcMode, NfcTech" +Function,+,nfc_data_generator_fill_data,void,"NfcDataGeneratorType, NfcDevice*" +Function,+,nfc_data_generator_get_name,const char*,NfcDataGeneratorType +Function,+,nfc_device_alloc,NfcDevice*, +Function,+,nfc_device_clear,void,NfcDevice* +Function,+,nfc_device_copy_data,void,"const NfcDevice*, NfcProtocol, NfcDeviceData*" +Function,+,nfc_device_free,void,NfcDevice* +Function,+,nfc_device_get_data,const NfcDeviceData*,"const NfcDevice*, NfcProtocol" +Function,+,nfc_device_get_name,const char*,"const NfcDevice*, NfcDeviceNameType" +Function,+,nfc_device_get_protocol,NfcProtocol,const NfcDevice* +Function,+,nfc_device_get_protocol_name,const char*,NfcProtocol +Function,+,nfc_device_get_uid,const uint8_t*,"const NfcDevice*, size_t*" +Function,+,nfc_device_is_equal,_Bool,"const NfcDevice*, const NfcDevice*" +Function,+,nfc_device_is_equal_data,_Bool,"const NfcDevice*, NfcProtocol, const NfcDeviceData*" +Function,+,nfc_device_load,_Bool,"NfcDevice*, const char*" +Function,+,nfc_device_reset,void,NfcDevice* +Function,+,nfc_device_save,_Bool,"NfcDevice*, const char*" +Function,+,nfc_device_set_data,void,"NfcDevice*, NfcProtocol, const NfcDeviceData*" +Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" +Function,+,nfc_device_set_uid,_Bool,"NfcDevice*, const uint8_t*, size_t" +Function,+,nfc_dict_add_key,_Bool,"NfcDict*, const uint8_t*, size_t" +Function,+,nfc_dict_alloc,NfcDict*,"const char*, NfcDictMode, size_t" +Function,+,nfc_dict_check_presence,_Bool,const char* +Function,+,nfc_dict_delete_key,_Bool,"NfcDict*, const uint8_t*, size_t" +Function,+,nfc_dict_free,void,NfcDict* +Function,+,nfc_dict_get_next_key,_Bool,"NfcDict*, uint8_t*, size_t" +Function,+,nfc_dict_get_total_keys,uint32_t,NfcDict* +Function,+,nfc_dict_is_key_present,_Bool,"NfcDict*, const uint8_t*, size_t" +Function,+,nfc_dict_rewind,_Bool,NfcDict* +Function,+,nfc_felica_listener_set_sensf_res_data,NfcError,"Nfc*, const uint8_t*, const uint8_t, const uint8_t*, const uint8_t" +Function,+,nfc_free,void,Nfc* +Function,+,nfc_iso14443a_listener_set_col_res_data,NfcError,"Nfc*, uint8_t*, uint8_t, uint8_t*, uint8_t" +Function,+,nfc_iso14443a_listener_tx_custom_parity,NfcError,"Nfc*, const BitBuffer*" +Function,+,nfc_iso14443a_poller_trx_custom_parity,NfcError,"Nfc*, const BitBuffer*, BitBuffer*, uint32_t" +Function,+,nfc_iso14443a_poller_trx_sdd_frame,NfcError,"Nfc*, const BitBuffer*, BitBuffer*, uint32_t" +Function,+,nfc_iso14443a_poller_trx_short_frame,NfcError,"Nfc*, NfcIso14443aShortFrame, BitBuffer*, uint32_t" +Function,+,nfc_iso15693_listener_tx_sof,NfcError,Nfc* +Function,+,nfc_listener_alloc,NfcListener*,"Nfc*, NfcProtocol, const NfcDeviceData*" +Function,+,nfc_listener_free,void,NfcListener* +Function,+,nfc_listener_get_data,const NfcDeviceData*,"const NfcListener*, NfcProtocol" +Function,+,nfc_listener_get_protocol,NfcProtocol,const NfcListener* +Function,+,nfc_listener_start,void,"NfcListener*, NfcGenericCallback, void*" +Function,+,nfc_listener_stop,void,NfcListener* +Function,+,nfc_listener_tx,NfcError,"Nfc*, const BitBuffer*" +Function,+,nfc_poller_alloc,NfcPoller*,"Nfc*, NfcProtocol" +Function,+,nfc_poller_detect,_Bool,NfcPoller* +Function,+,nfc_poller_free,void,NfcPoller* +Function,+,nfc_poller_get_data,const NfcDeviceData*,const NfcPoller* +Function,+,nfc_poller_get_protocol,NfcProtocol,const NfcPoller* +Function,+,nfc_poller_start,void,"NfcPoller*, NfcGenericCallback, void*" +Function,+,nfc_poller_start_ex,void,"NfcPoller*, NfcGenericCallbackEx, void*" +Function,+,nfc_poller_stop,void,NfcPoller* +Function,+,nfc_poller_trx,NfcError,"Nfc*, const BitBuffer*, BitBuffer*, uint32_t" +Function,+,nfc_protocol_get_parent,NfcProtocol,NfcProtocol +Function,+,nfc_protocol_has_parent,_Bool,"NfcProtocol, NfcProtocol" +Function,+,nfc_scanner_alloc,NfcScanner*,Nfc* +Function,+,nfc_scanner_free,void,NfcScanner* +Function,+,nfc_scanner_start,void,"NfcScanner*, NfcScannerCallback, void*" +Function,+,nfc_scanner_stop,void,NfcScanner* +Function,+,nfc_set_fdt_listen_fc,void,"Nfc*, uint32_t" +Function,+,nfc_set_fdt_poll_fc,void,"Nfc*, uint32_t" +Function,+,nfc_set_fdt_poll_poll_us,void,"Nfc*, uint32_t" +Function,+,nfc_set_guard_time_us,void,"Nfc*, uint32_t" +Function,+,nfc_set_mask_receive_time_fc,void,"Nfc*, uint32_t" +Function,+,nfc_start,void,"Nfc*, NfcEventCallback, void*" +Function,+,nfc_stop,void,Nfc* +Function,+,nfc_util_bytes2num,uint64_t,"const uint8_t*, uint8_t" +Function,+,nfc_util_even_parity32,uint8_t,uint32_t +Function,+,nfc_util_num2bytes,void,"uint64_t, uint8_t, uint8_t*" +Function,+,nfc_util_odd_parity,void,"const uint8_t*, uint8_t*, uint8_t" +Function,+,nfc_util_odd_parity8,uint8_t,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_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* +Function,+,onewire_host_read_bytes,void,"OneWireHost*, uint8_t*, uint16_t" +Function,+,onewire_host_reset,_Bool,OneWireHost* +Function,+,onewire_host_reset_search,void,OneWireHost* +Function,+,onewire_host_search,_Bool,"OneWireHost*, uint8_t*, OneWireHostSearchMode" +Function,+,onewire_host_set_overdrive,void,"OneWireHost*, _Bool" +Function,+,onewire_host_start,void,OneWireHost* +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_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_overdrive,void,"OneWireSlave*, _Bool" +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* +Function,-,open_memstream,FILE*,"char**, size_t*" +Function,+,path_append,void,"FuriString*, const char*" +Function,+,path_concat,void,"const char*, const char*, FuriString*" +Function,+,path_contains_only_ascii,_Bool,const char* +Function,+,path_extract_basename,void,"const char*, FuriString*" +Function,+,path_extract_dirname,void,"const char*, FuriString*" +Function,+,path_extract_extension,void,"FuriString*, char*, size_t" +Function,+,path_extract_filename,void,"FuriString*, FuriString*, _Bool" +Function,+,path_extract_filename_no_ext,void,"const char*, FuriString*" +Function,+,pb_close_string_substream,_Bool,"pb_istream_t*, pb_istream_t*" +Function,+,pb_decode,_Bool,"pb_istream_t*, const pb_msgdesc_t*, void*" +Function,+,pb_decode_bool,_Bool,"pb_istream_t*, _Bool*" +Function,+,pb_decode_ex,_Bool,"pb_istream_t*, const pb_msgdesc_t*, void*, unsigned int" +Function,+,pb_decode_fixed32,_Bool,"pb_istream_t*, void*" +Function,+,pb_decode_fixed64,_Bool,"pb_istream_t*, void*" +Function,+,pb_decode_svarint,_Bool,"pb_istream_t*, int64_t*" +Function,+,pb_decode_tag,_Bool,"pb_istream_t*, pb_wire_type_t*, uint32_t*, _Bool*" +Function,+,pb_decode_varint,_Bool,"pb_istream_t*, uint64_t*" +Function,+,pb_decode_varint32,_Bool,"pb_istream_t*, uint32_t*" +Function,+,pb_default_field_callback,_Bool,"pb_istream_t*, pb_ostream_t*, const pb_field_t*" +Function,+,pb_encode,_Bool,"pb_ostream_t*, const pb_msgdesc_t*, const void*" +Function,+,pb_encode_ex,_Bool,"pb_ostream_t*, const pb_msgdesc_t*, const void*, unsigned int" +Function,+,pb_encode_fixed32,_Bool,"pb_ostream_t*, const void*" +Function,+,pb_encode_fixed64,_Bool,"pb_ostream_t*, const void*" +Function,+,pb_encode_string,_Bool,"pb_ostream_t*, const pb_byte_t*, size_t" +Function,+,pb_encode_submessage,_Bool,"pb_ostream_t*, const pb_msgdesc_t*, const void*" +Function,+,pb_encode_svarint,_Bool,"pb_ostream_t*, int64_t" +Function,+,pb_encode_tag,_Bool,"pb_ostream_t*, pb_wire_type_t, uint32_t" +Function,+,pb_encode_tag_for_field,_Bool,"pb_ostream_t*, const pb_field_iter_t*" +Function,+,pb_encode_varint,_Bool,"pb_ostream_t*, uint64_t" +Function,+,pb_get_encoded_size,_Bool,"size_t*, const pb_msgdesc_t*, const void*" +Function,+,pb_istream_from_buffer,pb_istream_t,"const pb_byte_t*, size_t" +Function,+,pb_make_string_substream,_Bool,"pb_istream_t*, pb_istream_t*" +Function,+,pb_ostream_from_buffer,pb_ostream_t,"pb_byte_t*, size_t" +Function,+,pb_read,_Bool,"pb_istream_t*, pb_byte_t*, size_t" +Function,+,pb_release,void,"const pb_msgdesc_t*, void*" +Function,+,pb_skip_field,_Bool,"pb_istream_t*, pb_wire_type_t" +Function,+,pb_write,_Bool,"pb_ostream_t*, const pb_byte_t*, size_t" +Function,-,pclose,int,FILE* +Function,-,perror,void,const char* +Function,+,plugin_manager_alloc,PluginManager*,"const char*, uint32_t, const ElfApiInterface*" +Function,+,plugin_manager_free,void,PluginManager* +Function,+,plugin_manager_get,const FlipperAppPluginDescriptor*,"PluginManager*, uint32_t" +Function,+,plugin_manager_get_count,uint32_t,PluginManager* +Function,+,plugin_manager_get_ep,const void*,"PluginManager*, uint32_t" +Function,+,plugin_manager_load_all,PluginManagerError,"PluginManager*, const char*" +Function,+,plugin_manager_load_single,PluginManagerError,"PluginManager*, const char*" +Function,-,popen,FILE*,"const char*, const char*" +Function,+,popup_alloc,Popup*, +Function,+,popup_disable_timeout,void,Popup* +Function,+,popup_enable_timeout,void,Popup* +Function,+,popup_free,void,Popup* +Function,+,popup_get_view,View*,Popup* +Function,+,popup_reset,void,Popup* +Function,+,popup_set_callback,void,"Popup*, PopupCallback" +Function,+,popup_set_context,void,"Popup*, void*" +Function,+,popup_set_header,void,"Popup*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,popup_set_icon,void,"Popup*, uint8_t, uint8_t, const Icon*" +Function,+,popup_set_text,void,"Popup*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,popup_set_timeout,void,"Popup*, uint32_t" +Function,-,posix_memalign,int,"void**, size_t, size_t" +Function,-,pow,double,"double, double" +Function,-,pow10,double,double +Function,-,pow10f,float,float +Function,+,power_enable_low_battery_level_notification,void,"Power*, _Bool" +Function,+,power_get_info,void,"Power*, PowerInfo*" +Function,+,power_get_pubsub,FuriPubSub*,Power* +Function,+,power_is_battery_healthy,_Bool,Power* +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,+,property_value_out,void,"PropertyValueContext*, const char*, unsigned int, ..." +Function,+,protocol_dict_alloc,ProtocolDict*,"const ProtocolBase**, size_t" +Function,+,protocol_dict_decoders_feed,ProtocolId,"ProtocolDict*, _Bool, uint32_t" +Function,+,protocol_dict_decoders_feed_by_feature,ProtocolId,"ProtocolDict*, uint32_t, _Bool, uint32_t" +Function,+,protocol_dict_decoders_feed_by_id,ProtocolId,"ProtocolDict*, size_t, _Bool, uint32_t" +Function,+,protocol_dict_decoders_start,void,ProtocolDict* +Function,+,protocol_dict_encoder_start,_Bool,"ProtocolDict*, size_t" +Function,+,protocol_dict_encoder_yield,LevelDuration,"ProtocolDict*, size_t" +Function,+,protocol_dict_free,void,ProtocolDict* +Function,+,protocol_dict_get_data,void,"ProtocolDict*, size_t, uint8_t*, size_t" +Function,+,protocol_dict_get_data_size,size_t,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_features,uint32_t,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_manufacturer,const char*,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_max_data_size,size_t,ProtocolDict* +Function,+,protocol_dict_get_name,const char*,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_protocol_by_name,ProtocolId,"ProtocolDict*, const char*" +Function,+,protocol_dict_get_validate_count,uint32_t,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_write_data,_Bool,"ProtocolDict*, size_t, void*" +Function,+,protocol_dict_render_brief_data,void,"ProtocolDict*, FuriString*, size_t" +Function,+,protocol_dict_render_data,void,"ProtocolDict*, FuriString*, size_t" +Function,+,protocol_dict_set_data,void,"ProtocolDict*, size_t, const uint8_t*, size_t" +Function,-,pselect,int,"int, fd_set*, fd_set*, fd_set*, const timespec*, const sigset_t*" +Function,-,pulse_reader_alloc,PulseReader*,"const GpioPin*, uint32_t" +Function,-,pulse_reader_free,void,PulseReader* +Function,-,pulse_reader_receive,uint32_t,"PulseReader*, int" +Function,-,pulse_reader_samples,uint32_t,PulseReader* +Function,-,pulse_reader_set_bittime,void,"PulseReader*, uint32_t" +Function,-,pulse_reader_set_pull,void,"PulseReader*, GpioPull" +Function,-,pulse_reader_set_timebase,void,"PulseReader*, PulseReaderUnit" +Function,-,pulse_reader_start,void,PulseReader* +Function,-,pulse_reader_stop,void,PulseReader* +Function,-,putc,int,"int, FILE*" +Function,-,putc_unlocked,int,"int, FILE*" +Function,-,putchar,int,int +Function,-,putchar_unlocked,int,int +Function,-,putenv,int,char* +Function,-,puts,int,const char* +Function,-,putw,int,"int, FILE*" +Function,-,qsort,void,"void*, size_t, size_t, __compar_fn_t" +Function,-,qsort_r,void,"void*, size_t, size_t, int (*)(const void*, const void*, void*), void*" +Function,-,quick_exit,void,int +Function,+,rand,int, +Function,-,rand_r,int,unsigned* +Function,+,random,long, +Function,-,rawmemchr,void*,"const void*, int" +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,-,remainder,double,"double, double" +Function,-,remainderf,float,"float, float" +Function,-,remainderl,long double,"long double, long double" +Function,-,remove,int,const char* +Function,-,remquo,double,"double, double, int*" +Function,-,remquof,float,"float, float, int*" +Function,-,remquol,long double,"long double, long double, int*" +Function,-,rename,int,"const char*, const char*" +Function,-,renameat,int,"int, const char*, int, const char*" +Function,-,rewind,void,FILE* +Function,-,rindex,char*,"const char*, int" +Function,-,rint,double,double +Function,-,rintf,float,float +Function,-,rintl,long double,long double +Function,-,round,double,double +Function,+,roundf,float,float +Function,-,roundl,long double,long double +Function,+,rpc_session_close,void,RpcSession* +Function,+,rpc_session_feed,size_t,"RpcSession*, uint8_t*, size_t, uint32_t" +Function,+,rpc_session_get_available_size,size_t,RpcSession* +Function,+,rpc_session_get_owner,RpcOwner,RpcSession* +Function,+,rpc_session_open,RpcSession*,"Rpc*, RpcOwner" +Function,+,rpc_session_set_buffer_is_empty_callback,void,"RpcSession*, RpcBufferIsEmptyCallback" +Function,+,rpc_session_set_close_callback,void,"RpcSession*, RpcSessionClosedCallback" +Function,+,rpc_session_set_context,void,"RpcSession*, void*" +Function,+,rpc_session_set_send_bytes_callback,void,"RpcSession*, RpcSendBytesCallback" +Function,+,rpc_session_set_terminated_callback,void,"RpcSession*, RpcSessionTerminatedCallback" +Function,+,rpc_system_app_confirm,void,"RpcAppSystem*, _Bool" +Function,+,rpc_system_app_error_reset,void,RpcAppSystem* +Function,+,rpc_system_app_exchange_data,void,"RpcAppSystem*, const uint8_t*, size_t" +Function,+,rpc_system_app_send_exited,void,RpcAppSystem* +Function,+,rpc_system_app_send_started,void,RpcAppSystem* +Function,+,rpc_system_app_set_callback,void,"RpcAppSystem*, RpcAppSystemCallback, void*" +Function,+,rpc_system_app_set_error_code,void,"RpcAppSystem*, uint32_t" +Function,+,rpc_system_app_set_error_text,void,"RpcAppSystem*, const char*" +Function,-,rpmatch,int,const char* +Function,+,saved_struct_get_payload_size,_Bool,"const char*, uint8_t, uint8_t, size_t*" +Function,+,saved_struct_load,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" +Function,+,saved_struct_save,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" +Function,-,scalbln,double,"double, long int" +Function,-,scalblnf,float,"float, long int" +Function,-,scalblnl,long double,"long double, long" +Function,-,scalbn,double,"double, int" +Function,+,scalbnf,float,"float, int" +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,"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,"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" +Function,+,scene_manager_search_and_switch_to_previous_scene,_Bool,"SceneManager*, uint32_t" +Function,+,scene_manager_search_and_switch_to_previous_scene_one_of,_Bool,"SceneManager*, const uint32_t*, size_t" +Function,+,scene_manager_set_scene_state,void,"SceneManager*, uint32_t, uint32_t" +Function,+,scene_manager_stop,void,SceneManager* +Function,+,sd_api_get_fs_type_text,const char*,SDFsType +Function,-,secure_getenv,char*,const char* +Function,-,seed48,unsigned short*,unsigned short[3] +Function,-,select,int,"int, fd_set*, fd_set*, fd_set*, timeval*" +Function,-,serial_svc_is_started,_Bool, +Function,-,serial_svc_notify_buffer_is_empty,void, +Function,-,serial_svc_set_callbacks,void,"uint16_t, SerialServiceEventCallback, void*" +Function,-,serial_svc_set_rpc_status,void,SerialServiceRpcStatus +Function,-,serial_svc_start,void, +Function,-,serial_svc_stop,void, +Function,-,serial_svc_update_tx,_Bool,"uint8_t*, uint16_t" +Function,-,setbuf,void,"FILE*, char*" +Function,-,setbuffer,void,"FILE*, char*, int" +Function,-,setenv,int,"const char*, const char*, int" +Function,-,setkey,void,const char* +Function,-,setlinebuf,int,FILE* +Function,-,setstate,char*,char* +Function,-,setvbuf,int,"FILE*, char*, int, size_t" +Function,+,signal_reader_alloc,SignalReader*,"const GpioPin*, uint32_t" +Function,+,signal_reader_free,void,SignalReader* +Function,+,signal_reader_set_polarity,void,"SignalReader*, SignalReaderPolarity" +Function,+,signal_reader_set_pull,void,"SignalReader*, GpioPull" +Function,+,signal_reader_set_sample_rate,void,"SignalReader*, SignalReaderTimeUnit, uint32_t" +Function,+,signal_reader_set_trigger,void,"SignalReader*, SignalReaderTrigger" +Function,+,signal_reader_start,void,"SignalReader*, SignalReaderCallback, void*" +Function,+,signal_reader_stop,void,SignalReader* +Function,+,simple_array_alloc,SimpleArray*,const SimpleArrayConfig* +Function,+,simple_array_cget,const SimpleArrayElement*,"const SimpleArray*, uint32_t" +Function,+,simple_array_cget_data,const SimpleArrayData*,const SimpleArray* +Function,+,simple_array_copy,void,"SimpleArray*, const SimpleArray*" +Function,+,simple_array_free,void,SimpleArray* +Function,+,simple_array_get,SimpleArrayElement*,"SimpleArray*, uint32_t" +Function,+,simple_array_get_count,uint32_t,const SimpleArray* +Function,+,simple_array_get_data,SimpleArrayData*,SimpleArray* +Function,+,simple_array_init,void,"SimpleArray*, uint32_t" +Function,+,simple_array_is_equal,_Bool,"const SimpleArray*, const SimpleArray*" +Function,+,simple_array_reset,void,SimpleArray* +Function,-,sin,double,double +Function,-,sincos,void,"double, double*, double*" +Function,-,sincosf,void,"float, float*, float*" +Function,-,sinf,float,float +Function,-,sinh,double,double +Function,-,sinhf,float,float +Function,-,sinhl,long double,long double +Function,-,sinl,long double,long double +Function,-,siprintf,int,"char*, const char*, ..." +Function,-,siscanf,int,"const char*, const char*, ..." +Function,+,slix_alloc,SlixData*, +Function,+,slix_copy,void,"SlixData*, const SlixData*" +Function,+,slix_free,void,SlixData* +Function,+,slix_get_base_data,const Iso15693_3Data*,const SlixData* +Function,+,slix_get_counter,uint16_t,const SlixData* +Function,+,slix_get_device_name,const char*,"const SlixData*, NfcDeviceNameType" +Function,+,slix_get_password,SlixPassword,"const SlixData*, SlixPasswordType" +Function,+,slix_get_type,SlixType,const SlixData* +Function,+,slix_get_uid,const uint8_t*,"const SlixData*, size_t*" +Function,+,slix_is_block_protected,_Bool,"const SlixData*, SlixPasswordType, uint8_t" +Function,+,slix_is_counter_increment_protected,_Bool,const SlixData* +Function,+,slix_is_equal,_Bool,"const SlixData*, const SlixData*" +Function,+,slix_is_privacy_mode,_Bool,const SlixData* +Function,+,slix_load,_Bool,"SlixData*, FlipperFormat*, uint32_t" +Function,+,slix_reset,void,SlixData* +Function,+,slix_save,_Bool,"const SlixData*, FlipperFormat*" +Function,+,slix_set_uid,_Bool,"SlixData*, const uint8_t*, size_t" +Function,+,slix_type_has_features,_Bool,"SlixType, SlixTypeFeatures" +Function,+,slix_type_supports_password,_Bool,"SlixType, SlixPasswordType" +Function,+,slix_verify,_Bool,"SlixData*, const FuriString*" +Function,-,sniprintf,int,"char*, size_t, const char*, ..." +Function,+,snprintf,int,"char*, size_t, const char*, ..." +Function,-,sprintf,int,"char*, const char*, ..." +Function,-,sqrt,double,double +Function,-,sqrtf,float,float +Function,-,sqrtl,long double,long double +Function,+,srand,void,unsigned +Function,-,srand48,void,long +Function,-,srandom,void,unsigned +Function,+,sscanf,int,"const char*, const char*, ..." +Function,+,st25r3916_change_reg_bits,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" +Function,+,st25r3916_change_test_reg_bits,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" +Function,+,st25r3916_check_reg,_Bool,"FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" +Function,+,st25r3916_clear_reg_bits,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t" +Function,+,st25r3916_direct_cmd,void,"FuriHalSpiBusHandle*, uint8_t" +Function,+,st25r3916_get_irq,uint32_t,FuriHalSpiBusHandle* +Function,+,st25r3916_mask_irq,void,"FuriHalSpiBusHandle*, uint32_t" +Function,+,st25r3916_modify_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t, uint8_t" +Function,+,st25r3916_read_burst_regs,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t*, uint8_t" +Function,+,st25r3916_read_fifo,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, size_t*" +Function,+,st25r3916_read_pta_mem,void,"FuriHalSpiBusHandle*, uint8_t*, size_t" +Function,+,st25r3916_read_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t*" +Function,+,st25r3916_read_test_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t*" +Function,+,st25r3916_reg_read_fifo,void,"FuriHalSpiBusHandle*, uint8_t*, size_t" +Function,+,st25r3916_reg_write_fifo,void,"FuriHalSpiBusHandle*, const uint8_t*, size_t" +Function,+,st25r3916_set_reg_bits,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t" +Function,+,st25r3916_write_burst_regs,void,"FuriHalSpiBusHandle*, uint8_t, const uint8_t*, uint8_t" +Function,+,st25r3916_write_fifo,void,"FuriHalSpiBusHandle*, const uint8_t*, size_t" +Function,+,st25r3916_write_pta_mem,void,"FuriHalSpiBusHandle*, const uint8_t*, size_t" +Function,+,st25r3916_write_ptf_mem,void,"FuriHalSpiBusHandle*, const uint8_t*, size_t" +Function,+,st25r3916_write_pttsn_mem,void,"FuriHalSpiBusHandle*, uint8_t*, size_t" +Function,+,st25r3916_write_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t" +Function,+,st25r3916_write_test_reg,void,"FuriHalSpiBusHandle*, uint8_t, uint8_t" +Function,+,st25tb_alloc,St25tbData*, +Function,+,st25tb_copy,void,"St25tbData*, const St25tbData*" +Function,+,st25tb_free,void,St25tbData* +Function,+,st25tb_get_base_data,St25tbData*,const St25tbData* +Function,+,st25tb_get_block_count,uint8_t,St25tbType +Function,+,st25tb_get_device_name,const char*,"const St25tbData*, NfcDeviceNameType" +Function,+,st25tb_get_type_from_uid,St25tbType,const uint8_t* +Function,+,st25tb_get_uid,const uint8_t*,"const St25tbData*, size_t*" +Function,+,st25tb_is_equal,_Bool,"const St25tbData*, const St25tbData*" +Function,+,st25tb_load,_Bool,"St25tbData*, FlipperFormat*, uint32_t" +Function,+,st25tb_poller_get_uid,St25tbError,"St25tbPoller*, uint8_t*" +Function,+,st25tb_poller_halt,St25tbError,St25tbPoller* +Function,+,st25tb_poller_initiate,St25tbError,"St25tbPoller*, uint8_t*" +Function,+,st25tb_poller_read_block,St25tbError,"St25tbPoller*, uint32_t*, uint8_t" +Function,+,st25tb_poller_select,St25tbError,"St25tbPoller*, uint8_t*" +Function,+,st25tb_poller_send_frame,St25tbError,"St25tbPoller*, const BitBuffer*, BitBuffer*, uint32_t" +Function,+,st25tb_poller_sync_detect_type,St25tbError,"Nfc*, St25tbType*" +Function,+,st25tb_poller_sync_read,St25tbError,"Nfc*, St25tbData*" +Function,+,st25tb_poller_sync_read_block,St25tbError,"Nfc*, uint8_t, uint32_t*" +Function,+,st25tb_poller_sync_write_block,St25tbError,"Nfc*, uint8_t, uint32_t" +Function,+,st25tb_poller_write_block,St25tbError,"St25tbPoller*, uint32_t, uint8_t" +Function,+,st25tb_reset,void,St25tbData* +Function,+,st25tb_save,_Bool,"const St25tbData*, FlipperFormat*" +Function,+,st25tb_set_uid,_Bool,"St25tbData*, const uint8_t*, size_t" +Function,+,st25tb_verify,_Bool,"St25tbData*, const FuriString*" +Function,+,storage_common_copy,FS_Error,"Storage*, const char*, const char*" +Function,+,storage_common_equivalent_path,_Bool,"Storage*, const char*, const char*, _Bool" +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* +Function,+,storage_error_get_desc,const char*,FS_Error +Function,+,storage_file_alloc,File*,Storage* +Function,+,storage_file_close,_Bool,File* +Function,+,storage_file_copy_to_file,_Bool,"File*, File*, size_t" +Function,+,storage_file_eof,_Bool,File* +Function,+,storage_file_exists,_Bool,"Storage*, const char*" +Function,+,storage_file_free,void,File* +Function,+,storage_file_get_error,FS_Error,File* +Function,+,storage_file_get_error_desc,const char*,File* +Function,-,storage_file_get_internal_error,int32_t,File* +Function,+,storage_file_is_dir,_Bool,File* +Function,+,storage_file_is_open,_Bool,File* +Function,+,storage_file_open,_Bool,"File*, const char*, FS_AccessMode, FS_OpenMode" +Function,+,storage_file_read,size_t,"File*, void*, size_t" +Function,+,storage_file_seek,_Bool,"File*, uint32_t, _Bool" +Function,+,storage_file_size,uint64_t,File* +Function,+,storage_file_sync,_Bool,File* +Function,+,storage_file_tell,uint64_t,File* +Function,+,storage_file_truncate,_Bool,File* +Function,+,storage_file_write,size_t,"File*, const void*, size_t" +Function,+,storage_get_next_filename,void,"Storage*, const char*, const char*, const char*, FuriString*, uint8_t" +Function,+,storage_get_pubsub,FuriPubSub*,Storage* +Function,+,storage_int_backup,FS_Error,"Storage*, const char*" +Function,+,storage_int_restore,FS_Error,"Storage*, const char*, Storage_name_converter" +Function,+,storage_sd_format,FS_Error,Storage* +Function,+,storage_sd_info,FS_Error,"Storage*, SDInfo*" +Function,+,storage_sd_mount,FS_Error,Storage* +Function,+,storage_sd_status,FS_Error,Storage* +Function,+,storage_sd_unmount,FS_Error,Storage* +Function,+,storage_simply_mkdir,_Bool,"Storage*, const char*" +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_l,int,"const char*, const char*, locale_t" +Function,+,strcasestr,char*,"const char*, const char*" +Function,-,strcat,char*,"char*, const char*" +Function,+,strchr,char*,"const char*, int" +Function,-,strchrnul,char*,"const char*, int" +Function,+,strcmp,int,"const char*, const char*" +Function,-,strcoll,int,"const char*, const char*" +Function,-,strcoll_l,int,"const char*, const char*, locale_t" +Function,+,strcpy,char*,"char*, const char*" +Function,+,strcspn,size_t,"const char*, const char*" +Function,+,strdup,char*,const char* +Function,+,stream_clean,void,Stream* +Function,+,stream_copy,size_t,"Stream*, Stream*, size_t" +Function,+,stream_copy_full,size_t,"Stream*, Stream*" +Function,+,stream_delete,_Bool,"Stream*, size_t" +Function,+,stream_delete_and_insert,_Bool,"Stream*, size_t, StreamWriteCB, const void*" +Function,+,stream_delete_and_insert_char,_Bool,"Stream*, size_t, char" +Function,+,stream_delete_and_insert_cstring,_Bool,"Stream*, size_t, const char*" +Function,+,stream_delete_and_insert_format,_Bool,"Stream*, size_t, const char*, ..." +Function,+,stream_delete_and_insert_string,_Bool,"Stream*, size_t, FuriString*" +Function,+,stream_delete_and_insert_vaformat,_Bool,"Stream*, size_t, const char*, va_list" +Function,+,stream_dump_data,void,Stream* +Function,+,stream_eof,_Bool,Stream* +Function,+,stream_free,void,Stream* +Function,+,stream_insert,_Bool,"Stream*, const uint8_t*, size_t" +Function,+,stream_insert_char,_Bool,"Stream*, char" +Function,+,stream_insert_cstring,_Bool,"Stream*, const char*" +Function,+,stream_insert_format,_Bool,"Stream*, const char*, ..." +Function,+,stream_insert_string,_Bool,"Stream*, FuriString*" +Function,+,stream_insert_vaformat,_Bool,"Stream*, const char*, va_list" +Function,+,stream_load_from_file,size_t,"Stream*, Storage*, const char*" +Function,+,stream_read,size_t,"Stream*, uint8_t*, size_t" +Function,+,stream_read_line,_Bool,"Stream*, FuriString*" +Function,+,stream_rewind,_Bool,Stream* +Function,+,stream_save_to_file,size_t,"Stream*, Storage*, const char*, FS_OpenMode" +Function,+,stream_seek,_Bool,"Stream*, int32_t, StreamOffset" +Function,+,stream_seek_to_char,_Bool,"Stream*, char, StreamDirection" +Function,+,stream_size,size_t,Stream* +Function,+,stream_split,_Bool,"Stream*, Stream*, Stream*" +Function,+,stream_tell,size_t,Stream* +Function,+,stream_write,size_t,"Stream*, const uint8_t*, size_t" +Function,+,stream_write_char,size_t,"Stream*, char" +Function,+,stream_write_cstring,size_t,"Stream*, const char*" +Function,+,stream_write_format,size_t,"Stream*, const char*, ..." +Function,+,stream_write_string,size_t,"Stream*, FuriString*" +Function,+,stream_write_vaformat,size_t,"Stream*, const char*, va_list" +Function,-,strerror,char*,int +Function,-,strerror_l,char*,"int, locale_t" +Function,-,strerror_r,char*,"int, char*, size_t" +Function,+,string_stream_alloc,Stream*, +Function,-,strlcat,size_t,"char*, const char*, size_t" +Function,+,strlcpy,size_t,"char*, const char*, size_t" +Function,+,strlen,size_t,const char* +Function,-,strlwr,char*,char* +Function,+,strncasecmp,int,"const char*, const char*, size_t" +Function,-,strncasecmp_l,int,"const char*, const char*, size_t, locale_t" +Function,-,strncat,char*,"char*, const char*, size_t" +Function,+,strncmp,int,"const char*, const char*, size_t" +Function,+,strncpy,char*,"char*, const char*, size_t" +Function,-,strndup,char*,"const char*, size_t" +Function,-,strnlen,size_t,"const char*, size_t" +Function,-,strnstr,char*,"const char*, const char*, size_t" +Function,-,strpbrk,char*,"const char*, const char*" +Function,+,strrchr,char*,"const char*, int" +Function,-,strsep,char*,"char**, const char*" +Function,-,strsignal,char*,int +Function,+,strspn,size_t,"const char*, const char*" +Function,+,strstr,char*,"const char*, const char*" +Function,-,strtod,double,"const char*, char**" +Function,-,strtod_l,double,"const char*, char**, locale_t" +Function,+,strtof,float,"const char*, char**" +Function,-,strtof_l,float,"const char*, char**, locale_t" +Function,-,strtok,char*,"char*, const char*" +Function,-,strtok_r,char*,"char*, const char*, char**" +Function,+,strtol,long,"const char*, char**, int" +Function,-,strtol_l,long,"const char*, char**, int, locale_t" +Function,-,strtold,long double,"const char*, char**" +Function,-,strtold_l,long double,"const char*, char**, locale_t" +Function,-,strtoll,long long,"const char*, char**, int" +Function,-,strtoll_l,long long,"const char*, char**, int, locale_t" +Function,+,strtoul,unsigned long,"const char*, char**, int" +Function,-,strtoul_l,unsigned long,"const char*, char**, int, locale_t" +Function,+,strtoull,unsigned long long,"const char*, char**, int" +Function,-,strtoull_l,unsigned long long,"const char*, char**, int, locale_t" +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,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,SubGhzProtocolStatus,"SubGhzBlockGeneric*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_device_cc1101_ext_ep,const FlipperAppPluginDescriptor*, +Function,+,subghz_devices_begin,_Bool,const SubGhzDevice* +Function,+,subghz_devices_deinit,void, +Function,+,subghz_devices_end,void,const SubGhzDevice* +Function,+,subghz_devices_flush_rx,void,const SubGhzDevice* +Function,+,subghz_devices_flush_tx,void,const SubGhzDevice* +Function,+,subghz_devices_get_by_name,const SubGhzDevice*,const char* +Function,+,subghz_devices_get_data_gpio,const GpioPin*,const SubGhzDevice* +Function,+,subghz_devices_get_lqi,uint8_t,const SubGhzDevice* +Function,+,subghz_devices_get_name,const char*,const SubGhzDevice* +Function,+,subghz_devices_get_rssi,float,const SubGhzDevice* +Function,+,subghz_devices_idle,void,const SubGhzDevice* +Function,+,subghz_devices_init,void, +Function,+,subghz_devices_is_async_complete_tx,_Bool,const SubGhzDevice* +Function,+,subghz_devices_is_connect,_Bool,const SubGhzDevice* +Function,+,subghz_devices_is_frequency_valid,_Bool,"const SubGhzDevice*, uint32_t" +Function,+,subghz_devices_is_rx_data_crc_valid,_Bool,const SubGhzDevice* +Function,+,subghz_devices_load_preset,void,"const SubGhzDevice*, FuriHalSubGhzPreset, uint8_t*" +Function,+,subghz_devices_read_packet,void,"const SubGhzDevice*, uint8_t*, uint8_t*" +Function,+,subghz_devices_reset,void,const SubGhzDevice* +Function,+,subghz_devices_rx_pipe_not_empty,_Bool,const SubGhzDevice* +Function,+,subghz_devices_set_async_mirror_pin,void,"const SubGhzDevice*, const GpioPin*" +Function,+,subghz_devices_set_frequency,uint32_t,"const SubGhzDevice*, uint32_t" +Function,+,subghz_devices_set_rx,void,const SubGhzDevice* +Function,+,subghz_devices_set_tx,_Bool,const SubGhzDevice* +Function,+,subghz_devices_sleep,void,const SubGhzDevice* +Function,+,subghz_devices_start_async_rx,void,"const SubGhzDevice*, void*, void*" +Function,+,subghz_devices_start_async_tx,_Bool,"const SubGhzDevice*, void*, void*" +Function,+,subghz_devices_stop_async_rx,void,const SubGhzDevice* +Function,+,subghz_devices_stop_async_tx,void,const SubGhzDevice* +Function,+,subghz_devices_write_packet,void,"const SubGhzDevice*, const uint8_t*, uint8_t" +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* +Function,+,subghz_environment_get_came_atomo_rainbow_table_file_name,const char*,SubGhzEnvironment* +Function,+,subghz_environment_get_keystore,SubGhzKeystore*,SubGhzEnvironment* +Function,+,subghz_environment_get_nice_flor_s_rainbow_table_file_name,const char*,SubGhzEnvironment* +Function,+,subghz_environment_get_protocol_name_registry,const char*,"SubGhzEnvironment*, size_t" +Function,+,subghz_environment_get_protocol_registry,const SubGhzProtocolRegistry*,SubGhzEnvironment* +Function,+,subghz_environment_load_keystore,_Bool,"SubGhzEnvironment*, const char*" +Function,+,subghz_environment_set_alutech_at_4n_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" +Function,+,subghz_environment_set_came_atomo_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" +Function,+,subghz_environment_set_nice_flor_s_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" +Function,+,subghz_environment_set_protocol_registry,void,"SubGhzEnvironment*, const SubGhzProtocolRegistry*" +Function,-,subghz_keystore_alloc,SubGhzKeystore*, +Function,-,subghz_keystore_free,void,SubGhzKeystore* +Function,-,subghz_keystore_get_data,SubGhzKeyArray_t*,SubGhzKeystore* +Function,-,subghz_keystore_load,_Bool,"SubGhzKeystore*, const char*" +Function,-,subghz_keystore_raw_encrypted_save,_Bool,"const char*, const char*, uint8_t*" +Function,-,subghz_keystore_raw_get_data,_Bool,"const char*, size_t, uint8_t*, size_t" +Function,-,subghz_keystore_save,_Bool,"SubGhzKeystore*, const char*, uint8_t*" +Function,+,subghz_protocol_blocks_add_bit,void,"SubGhzBlockDecoder*, uint8_t" +Function,+,subghz_protocol_blocks_add_bytes,uint8_t,"const uint8_t[], size_t" +Function,+,subghz_protocol_blocks_add_to_128_bit,void,"SubGhzBlockDecoder*, uint8_t, uint64_t*" +Function,+,subghz_protocol_blocks_crc16,uint16_t,"const uint8_t[], size_t, uint16_t, uint16_t" +Function,+,subghz_protocol_blocks_crc16lsb,uint16_t,"const uint8_t[], size_t, uint16_t, uint16_t" +Function,+,subghz_protocol_blocks_crc4,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_crc7,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_crc8,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_crc8le,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_get_bit_array,_Bool,"uint8_t[], size_t" +Function,+,subghz_protocol_blocks_get_hash_data,uint8_t,"SubGhzBlockDecoder*, size_t" +Function,+,subghz_protocol_blocks_get_parity,uint8_t,"uint64_t, uint8_t" +Function,+,subghz_protocol_blocks_get_upload_from_bit_array,size_t,"uint8_t[], size_t, LevelDuration*, size_t, uint32_t, SubGhzProtocolBlockAlignBit" +Function,+,subghz_protocol_blocks_lfsr_digest16,uint16_t,"const uint8_t[], size_t, uint16_t, uint16_t" +Function,+,subghz_protocol_blocks_lfsr_digest8,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_lfsr_digest8_reflect,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" +Function,+,subghz_protocol_blocks_parity8,uint8_t,uint8_t +Function,+,subghz_protocol_blocks_parity_bytes,uint8_t,"const uint8_t[], size_t" +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_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,SubGhzProtocolStatus,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_base_set_decoder_callback,void,"SubGhzProtocolDecoderBase*, SubGhzProtocolDecoderBaseRxCallback, void*" +Function,+,subghz_protocol_decoder_bin_raw_data_input_rssi,void,"SubGhzProtocolDecoderBinRAW*, float" +Function,+,subghz_protocol_decoder_raw_alloc,void*,SubGhzEnvironment* +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_encoder_raw_alloc,void*,SubGhzEnvironment* +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_keeloq_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" +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*, const char*" +Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW* +Function,+,subghz_protocol_raw_save_to_file_init,_Bool,"SubGhzProtocolDecoderRAW*, const char*, SubGhzRadioPreset*" +Function,+,subghz_protocol_raw_save_to_file_pause,void,"SubGhzProtocolDecoderRAW*, _Bool" +Function,+,subghz_protocol_raw_save_to_file_stop,void,SubGhzProtocolDecoderRAW* +Function,+,subghz_protocol_registry_count,size_t,const SubGhzProtocolRegistry* +Function,+,subghz_protocol_registry_get_by_index,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, size_t" +Function,+,subghz_protocol_registry_get_by_name,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, const char*" +Function,+,subghz_protocol_secplus_v1_check_fixed,_Bool,uint32_t +Function,+,subghz_protocol_secplus_v2_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, SubGhzRadioPreset*" +Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment* +Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t" +Function,+,subghz_receiver_free,void,SubGhzReceiver* +Function,+,subghz_receiver_reset,void,SubGhzReceiver* +Function,+,subghz_receiver_search_decoder_base_by_name,SubGhzProtocolDecoderBase*,"SubGhzReceiver*, const char*" +Function,+,subghz_receiver_set_filter,void,"SubGhzReceiver*, SubGhzProtocolFlag" +Function,+,subghz_receiver_set_rx_callback,void,"SubGhzReceiver*, SubGhzReceiverCallback, void*" +Function,+,subghz_setting_alloc,SubGhzSetting*, +Function,+,subghz_setting_delete_custom_preset,_Bool,"SubGhzSetting*, const char*" +Function,+,subghz_setting_free,void,SubGhzSetting* +Function,+,subghz_setting_get_default_frequency,uint32_t,SubGhzSetting* +Function,+,subghz_setting_get_frequency,uint32_t,"SubGhzSetting*, size_t" +Function,+,subghz_setting_get_frequency_count,size_t,SubGhzSetting* +Function,+,subghz_setting_get_frequency_default_index,uint32_t,SubGhzSetting* +Function,+,subghz_setting_get_hopper_frequency,uint32_t,"SubGhzSetting*, size_t" +Function,+,subghz_setting_get_hopper_frequency_count,size_t,SubGhzSetting* +Function,+,subghz_setting_get_inx_preset_by_name,int,"SubGhzSetting*, const char*" +Function,+,subghz_setting_get_preset_count,size_t,SubGhzSetting* +Function,+,subghz_setting_get_preset_data,uint8_t*,"SubGhzSetting*, size_t" +Function,+,subghz_setting_get_preset_data_by_name,uint8_t*,"SubGhzSetting*, const char*" +Function,+,subghz_setting_get_preset_data_size,size_t,"SubGhzSetting*, size_t" +Function,+,subghz_setting_get_preset_name,const char*,"SubGhzSetting*, size_t" +Function,+,subghz_setting_load,void,"SubGhzSetting*, const char*" +Function,+,subghz_setting_load_custom_preset,_Bool,"SubGhzSetting*, const char*, FlipperFormat*" +Function,+,subghz_transmitter_alloc_init,SubGhzTransmitter*,"SubGhzEnvironment*, const char*" +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* +Function,+,subghz_transmitter_yield,LevelDuration,void* +Function,+,subghz_tx_rx_worker_alloc,SubGhzTxRxWorker*, +Function,+,subghz_tx_rx_worker_available,size_t,SubGhzTxRxWorker* +Function,+,subghz_tx_rx_worker_free,void,SubGhzTxRxWorker* +Function,+,subghz_tx_rx_worker_is_running,_Bool,SubGhzTxRxWorker* +Function,+,subghz_tx_rx_worker_read,size_t,"SubGhzTxRxWorker*, uint8_t*, size_t" +Function,+,subghz_tx_rx_worker_set_callback_have_read,void,"SubGhzTxRxWorker*, SubGhzTxRxWorkerCallbackHaveRead, void*" +Function,+,subghz_tx_rx_worker_start,_Bool,"SubGhzTxRxWorker*, const SubGhzDevice*, uint32_t" +Function,+,subghz_tx_rx_worker_stop,void,SubGhzTxRxWorker* +Function,+,subghz_tx_rx_worker_write,_Bool,"SubGhzTxRxWorker*, uint8_t*, size_t" +Function,+,subghz_worker_alloc,SubGhzWorker*, +Function,+,subghz_worker_free,void,SubGhzWorker* +Function,+,subghz_worker_is_running,_Bool,SubGhzWorker* +Function,+,subghz_worker_rx_callback,void,"_Bool, uint32_t, void*" +Function,+,subghz_worker_set_context,void,"SubGhzWorker*, void*" +Function,+,subghz_worker_set_filter,void,"SubGhzWorker*, uint16_t" +Function,+,subghz_worker_set_overrun_callback,void,"SubGhzWorker*, SubGhzWorkerOverrunCallback" +Function,+,subghz_worker_set_pair_callback,void,"SubGhzWorker*, SubGhzWorkerPairCallback" +Function,+,subghz_worker_start,void,SubGhzWorker* +Function,+,subghz_worker_stop,void,SubGhzWorker* +Function,+,submenu_add_item,void,"Submenu*, const char*, uint32_t, SubmenuItemCallback, void*" +Function,+,submenu_alloc,Submenu*, +Function,+,submenu_free,void,Submenu* +Function,+,submenu_get_view,View*,Submenu* +Function,+,submenu_reset,void,Submenu* +Function,+,submenu_set_header,void,"Submenu*, const char*" +Function,+,submenu_set_selected_item,void,"Submenu*, uint32_t" +Function,-,system,int,const char* +Function,+,t5577_write,void,LFRFIDT5577* +Function,-,tan,double,double +Function,-,tanf,float,float +Function,-,tanh,double,double +Function,-,tanhf,float,float +Function,-,tanhl,long double,long double +Function,-,tanl,long double,long double +Function,+,tar_archive_add_dir,_Bool,"TarArchive*, const char*, const char*" +Function,+,tar_archive_add_file,_Bool,"TarArchive*, const char*, const char*, const int32_t" +Function,+,tar_archive_alloc,TarArchive*,Storage* +Function,+,tar_archive_dir_add_element,_Bool,"TarArchive*, const char*" +Function,+,tar_archive_file_add_data_block,_Bool,"TarArchive*, const uint8_t*, const int32_t" +Function,+,tar_archive_file_add_header,_Bool,"TarArchive*, const char*, const int32_t" +Function,+,tar_archive_file_finalize,_Bool,TarArchive* +Function,+,tar_archive_finalize,_Bool,TarArchive* +Function,+,tar_archive_free,void,TarArchive* +Function,+,tar_archive_get_entries_count,int32_t,TarArchive* +Function,+,tar_archive_open,_Bool,"TarArchive*, const char*, TarOpenMode" +Function,+,tar_archive_set_file_callback,void,"TarArchive*, tar_unpack_file_cb, void*" +Function,+,tar_archive_store_data,_Bool,"TarArchive*, const char*, const uint8_t*, const int32_t" +Function,+,tar_archive_unpack_file,_Bool,"TarArchive*, const char*, const char*" +Function,+,tar_archive_unpack_to,_Bool,"TarArchive*, const char*, Storage_name_converter" +Function,-,tempnam,char*,"const char*, const char*" +Function,+,text_box_alloc,TextBox*, +Function,+,text_box_free,void,TextBox* +Function,+,text_box_get_view,View*,TextBox* +Function,+,text_box_reset,void,TextBox* +Function,+,text_box_set_focus,void,"TextBox*, TextBoxFocus" +Function,+,text_box_set_font,void,"TextBox*, TextBoxFont" +Function,+,text_box_set_text,void,"TextBox*, const char*" +Function,+,text_input_alloc,TextInput*, +Function,+,text_input_free,void,TextInput* +Function,+,text_input_get_validator_callback,TextInputValidatorCallback,TextInput* +Function,+,text_input_get_validator_callback_context,void*,TextInput* +Function,+,text_input_get_view,View*,TextInput* +Function,+,text_input_reset,void,TextInput* +Function,+,text_input_set_header_text,void,"TextInput*, const char*" +Function,+,text_input_set_result_callback,void,"TextInput*, TextInputCallback, void*, char*, size_t, _Bool" +Function,+,text_input_set_validator,void,"TextInput*, TextInputValidatorCallback, void*" +Function,-,tgamma,double,double +Function,-,tgammaf,float,float +Function,-,tgammal,long double,long double +Function,-,timingsafe_bcmp,int,"const void*, const void*, size_t" +Function,-,timingsafe_memcmp,int,"const void*, const void*, size_t" +Function,-,tmpfile,FILE*, +Function,-,tmpnam,char*,char* +Function,-,toascii,int,int +Function,-,toascii_l,int,"int, locale_t" +Function,-,tolower,int,int +Function,-,tolower_l,int,"int, locale_t" +Function,-,toupper,int,int +Function,-,toupper_l,int,"int, locale_t" +Function,-,trunc,double,double +Function,-,truncf,float,float +Function,-,truncl,long double,long double +Function,+,uint8_to_hex_chars,void,"const uint8_t*, uint8_t*, int" +Function,-,ungetc,int,"int, FILE*" +Function,-,unsetenv,int,const char* +Function,-,usbd_poll,void,usbd_device* +Function,-,utoa,char*,"unsigned, char*, int" +Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*" +Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" +Function,+,validator_is_file_free,void,ValidatorIsFile* +Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" +Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" +Function,+,value_index_int32,uint8_t,"const int32_t, const int32_t[], uint8_t" +Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" +Function,+,variable_item_get_context,void*,VariableItem* +Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* +Function,+,variable_item_list_add,VariableItem*,"VariableItemList*, const char*, uint8_t, VariableItemChangeCallback, void*" +Function,+,variable_item_list_alloc,VariableItemList*, +Function,+,variable_item_list_free,void,VariableItemList* +Function,+,variable_item_list_get_selected_item_index,uint8_t,VariableItemList* +Function,+,variable_item_list_get_view,View*,VariableItemList* +Function,+,variable_item_list_reset,void,VariableItemList* +Function,+,variable_item_list_set_enter_callback,void,"VariableItemList*, VariableItemListEnterCallback, void*" +Function,+,variable_item_list_set_selected_item,void,"VariableItemList*, uint8_t" +Function,+,variable_item_set_current_value_index,void,"VariableItem*, uint8_t" +Function,+,variable_item_set_current_value_text,void,"VariableItem*, const char*" +Function,+,variable_item_set_values_count,void,"VariableItem*, uint8_t" +Function,-,vasiprintf,int,"char**, const char*, __gnuc_va_list" +Function,-,vasniprintf,char*,"char*, size_t*, const char*, __gnuc_va_list" +Function,-,vasnprintf,char*,"char*, size_t*, const char*, __gnuc_va_list" +Function,-,vasprintf,int,"char**, const char*, __gnuc_va_list" +Function,-,vdiprintf,int,"int, const char*, __gnuc_va_list" +Function,-,vdprintf,int,"int, const char*, __gnuc_va_list" +Function,+,version_get,const Version*, +Function,+,version_get_builddate,const char*,const Version* +Function,+,version_get_dirty_flag,_Bool,const Version* +Function,+,version_get_firmware_origin,const char*,const Version* +Function,+,version_get_git_origin,const char*,const Version* +Function,+,version_get_gitbranch,const char*,const Version* +Function,+,version_get_gitbranchnum,const char*,const Version* +Function,+,version_get_githash,const char*,const Version* +Function,+,version_get_target,uint8_t,const Version* +Function,+,version_get_version,const char*,const Version* +Function,-,vfiprintf,int,"FILE*, const char*, __gnuc_va_list" +Function,-,vfiscanf,int,"FILE*, const char*, __gnuc_va_list" +Function,-,vfprintf,int,"FILE*, const char*, __gnuc_va_list" +Function,-,vfscanf,int,"FILE*, const char*, __gnuc_va_list" +Function,+,view_alloc,View*, +Function,+,view_allocate_model,void,"View*, ViewModelType, size_t" +Function,+,view_commit_model,void,"View*, _Bool" +Function,+,view_dispatcher_add_view,void,"ViewDispatcher*, uint32_t, View*" +Function,+,view_dispatcher_alloc,ViewDispatcher*, +Function,+,view_dispatcher_attach_to_gui,void,"ViewDispatcher*, Gui*, ViewDispatcherType" +Function,+,view_dispatcher_enable_queue,void,ViewDispatcher* +Function,+,view_dispatcher_free,void,ViewDispatcher* +Function,+,view_dispatcher_remove_view,void,"ViewDispatcher*, uint32_t" +Function,+,view_dispatcher_run,void,ViewDispatcher* +Function,+,view_dispatcher_send_custom_event,void,"ViewDispatcher*, uint32_t" +Function,+,view_dispatcher_send_to_back,void,ViewDispatcher* +Function,+,view_dispatcher_send_to_front,void,ViewDispatcher* +Function,+,view_dispatcher_set_custom_event_callback,void,"ViewDispatcher*, ViewDispatcherCustomEventCallback" +Function,+,view_dispatcher_set_event_callback_context,void,"ViewDispatcher*, void*" +Function,+,view_dispatcher_set_navigation_event_callback,void,"ViewDispatcher*, ViewDispatcherNavigationEventCallback" +Function,+,view_dispatcher_set_tick_event_callback,void,"ViewDispatcher*, ViewDispatcherTickEventCallback, uint32_t" +Function,+,view_dispatcher_stop,void,ViewDispatcher* +Function,+,view_dispatcher_switch_to_view,void,"ViewDispatcher*, uint32_t" +Function,+,view_free,void,View* +Function,+,view_free_model,void,View* +Function,+,view_get_model,void*,View* +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,const ViewPort* +Function,+,view_port_get_orientation,ViewPortOrientation,const 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,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" +Function,+,view_port_update,void,ViewPort* +Function,+,view_set_context,void,"View*, void*" +Function,+,view_set_custom_callback,void,"View*, ViewCustomCallback" +Function,+,view_set_draw_callback,void,"View*, ViewDrawCallback" +Function,+,view_set_enter_callback,void,"View*, ViewCallback" +Function,+,view_set_exit_callback,void,"View*, ViewCallback" +Function,+,view_set_input_callback,void,"View*, ViewInputCallback" +Function,+,view_set_orientation,void,"View*, ViewOrientation" +Function,+,view_set_previous_callback,void,"View*, ViewNavigationCallback" +Function,+,view_set_update_callback,void,"View*, ViewUpdateCallback" +Function,+,view_set_update_callback_context,void,"View*, void*" +Function,+,view_stack_add_view,void,"ViewStack*, View*" +Function,+,view_stack_alloc,ViewStack*, +Function,+,view_stack_free,void,ViewStack* +Function,+,view_stack_get_view,View*,ViewStack* +Function,+,view_stack_remove_view,void,"ViewStack*, View*" +Function,+,view_tie_icon_animation,void,"View*, IconAnimation*" +Function,-,viprintf,int,"const char*, __gnuc_va_list" +Function,-,viscanf,int,"const char*, __gnuc_va_list" +Function,-,vprintf,int,"const char*, __gnuc_va_list" +Function,-,vscanf,int,"const char*, __gnuc_va_list" +Function,-,vsiprintf,int,"char*, const char*, __gnuc_va_list" +Function,-,vsiscanf,int,"const char*, const char*, __gnuc_va_list" +Function,-,vsniprintf,int,"char*, size_t, const char*, __gnuc_va_list" +Function,-,vsnprintf,int,"char*, size_t, const char*, __gnuc_va_list" +Function,-,vsprintf,int,"char*, const char*, __gnuc_va_list" +Function,-,vsscanf,int,"const char*, const char*, __gnuc_va_list" +Function,-,wcstombs,size_t,"char*, const wchar_t*, size_t" +Function,-,wctomb,int,"char*, wchar_t" +Function,+,widget_add_button_element,void,"Widget*, GuiButtonType, const char*, ButtonCallback, void*" +Function,+,widget_add_frame_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,widget_add_icon_element,void,"Widget*, uint8_t, uint8_t, const Icon*" +Function,+,widget_add_string_element,void,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*" +Function,+,widget_add_string_multiline_element,void,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*" +Function,+,widget_add_text_box_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" +Function,+,widget_add_text_scroll_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, const char*" +Function,+,widget_alloc,Widget*, +Function,+,widget_free,void,Widget* +Function,+,widget_get_view,View*,Widget* +Function,+,widget_reset,void,Widget* +Function,-,y0,double,double +Function,-,y0f,float,float +Function,-,y1,double,double +Function,-,y1f,float,float +Function,-,yn,double,"int, double" +Function,-,ynf,float,"int, float" +Variable,-,AHBPrescTable,const uint32_t[16], +Variable,-,APBPrescTable,const uint32_t[8], +Variable,-,ITM_RxBuffer,volatile int32_t, +Variable,-,MSIRangeTable,const uint32_t[16], +Variable,-,SmpsPrescalerTable,const uint32_t[4][6], +Variable,+,SystemCoreClock,uint32_t, +Variable,+,_ctype_,const char[], +Variable,+,_global_impure_ptr,_reent*, +Variable,+,_impure_ptr,_reent*, +Variable,-,_sys_errlist,const char*[], +Variable,-,_sys_nerr,int, +Variable,+,cli_vcp,CliSession, +Variable,+,firmware_api_interface,const ElfApiInterface*, +Variable,+,furi_hal_i2c_bus_external,FuriHalI2cBus, +Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus, +Variable,+,furi_hal_i2c_handle_external,FuriHalI2cBusHandle, +Variable,+,furi_hal_i2c_handle_power,FuriHalI2cBusHandle, +Variable,+,furi_hal_spi_bus_d,FuriHalSpiBus, +Variable,+,furi_hal_spi_bus_handle_display,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_external,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_nfc,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_sd_fast,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_sd_slow,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_subghz,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_r,FuriHalSpiBus, +Variable,+,furi_hal_spi_preset_1edge_low_16m,const LL_SPI_InitTypeDef, +Variable,+,furi_hal_spi_preset_1edge_low_2m,const LL_SPI_InitTypeDef, +Variable,+,furi_hal_spi_preset_1edge_low_4m,const LL_SPI_InitTypeDef, +Variable,+,furi_hal_spi_preset_1edge_low_8m,const LL_SPI_InitTypeDef, +Variable,+,furi_hal_spi_preset_2edge_low_8m,const LL_SPI_InitTypeDef, +Variable,+,gpio_button_back,const GpioPin, +Variable,+,gpio_button_down,const GpioPin, +Variable,+,gpio_button_left,const GpioPin, +Variable,+,gpio_button_ok,const GpioPin, +Variable,+,gpio_button_right,const GpioPin, +Variable,+,gpio_button_up,const GpioPin, +Variable,+,gpio_cc1101_g0,const GpioPin, +Variable,+,gpio_display_cs,const GpioPin, +Variable,+,gpio_display_di,const GpioPin, +Variable,+,gpio_display_rst_n,const GpioPin, +Variable,+,gpio_ext_pa4,const GpioPin, +Variable,+,gpio_ext_pa6,const GpioPin, +Variable,+,gpio_ext_pa7,const GpioPin, +Variable,+,gpio_ext_pb2,const GpioPin, +Variable,+,gpio_ext_pb3,const GpioPin, +Variable,+,gpio_ext_pc0,const GpioPin, +Variable,+,gpio_ext_pc1,const GpioPin, +Variable,+,gpio_ext_pc3,const GpioPin, +Variable,+,gpio_i2c_power_scl,const GpioPin, +Variable,+,gpio_i2c_power_sda,const GpioPin, +Variable,+,gpio_ibutton,const GpioPin, +Variable,+,gpio_infrared_rx,const GpioPin, +Variable,+,gpio_infrared_tx,const GpioPin, +Variable,+,gpio_nfc_cs,const GpioPin, +Variable,+,gpio_nfc_irq_rfid_pull,const GpioPin, +Variable,+,gpio_periph_power,const GpioPin, +Variable,+,gpio_pins,const GpioPinRecord[], +Variable,+,gpio_pins_count,const size_t, +Variable,+,gpio_rf_sw_0,const GpioPin, +Variable,+,gpio_rfid_carrier,const GpioPin, +Variable,+,gpio_rfid_carrier_out,const GpioPin, +Variable,+,gpio_rfid_data_in,const GpioPin, +Variable,+,gpio_sdcard_cd,const GpioPin, +Variable,+,gpio_sdcard_cs,const GpioPin, +Variable,+,gpio_speaker,const GpioPin, +Variable,+,gpio_spi_d_miso,const GpioPin, +Variable,+,gpio_spi_d_mosi,const GpioPin, +Variable,+,gpio_spi_d_sck,const GpioPin, +Variable,+,gpio_spi_r_miso,const GpioPin, +Variable,+,gpio_spi_r_mosi,const GpioPin, +Variable,+,gpio_spi_r_sck,const GpioPin, +Variable,+,gpio_subghz_cs,const GpioPin, +Variable,+,gpio_swclk,const GpioPin, +Variable,+,gpio_swdio,const GpioPin, +Variable,+,gpio_usart_rx,const GpioPin, +Variable,+,gpio_usart_tx,const GpioPin, +Variable,+,gpio_usb_dm,const GpioPin, +Variable,+,gpio_usb_dp,const GpioPin, +Variable,+,gpio_vibro,const GpioPin, +Variable,+,input_pins,const InputPin[], +Variable,+,input_pins_count,const size_t, +Variable,+,lfrfid_protocols,const ProtocolBase*[], +Variable,+,message_blink_set_color_blue,const NotificationMessage, +Variable,+,message_blink_set_color_cyan,const NotificationMessage, +Variable,+,message_blink_set_color_green,const NotificationMessage, +Variable,+,message_blink_set_color_magenta,const NotificationMessage, +Variable,+,message_blink_set_color_red,const NotificationMessage, +Variable,+,message_blink_set_color_white,const NotificationMessage, +Variable,+,message_blink_set_color_yellow,const NotificationMessage, +Variable,+,message_blink_start_10,const NotificationMessage, +Variable,+,message_blink_start_100,const NotificationMessage, +Variable,+,message_blink_stop,const NotificationMessage, +Variable,+,message_blue_0,const NotificationMessage, +Variable,+,message_blue_255,const NotificationMessage, +Variable,+,message_click,const NotificationMessage, +Variable,+,message_delay_1,const NotificationMessage, +Variable,+,message_delay_10,const NotificationMessage, +Variable,+,message_delay_100,const NotificationMessage, +Variable,+,message_delay_1000,const NotificationMessage, +Variable,+,message_delay_25,const NotificationMessage, +Variable,+,message_delay_250,const NotificationMessage, +Variable,+,message_delay_50,const NotificationMessage, +Variable,+,message_delay_500,const NotificationMessage, +Variable,+,message_display_backlight_enforce_auto,const NotificationMessage, +Variable,+,message_display_backlight_enforce_on,const NotificationMessage, +Variable,+,message_display_backlight_off,const NotificationMessage, +Variable,+,message_display_backlight_on,const NotificationMessage, +Variable,+,message_do_not_reset,const NotificationMessage, +Variable,+,message_force_display_brightness_setting_1f,const NotificationMessage, +Variable,+,message_force_speaker_volume_setting_1f,const NotificationMessage, +Variable,+,message_force_vibro_setting_off,const NotificationMessage, +Variable,+,message_force_vibro_setting_on,const NotificationMessage, +Variable,+,message_green_0,const NotificationMessage, +Variable,+,message_green_255,const NotificationMessage, +Variable,+,message_lcd_contrast_update,const NotificationMessage, +Variable,+,message_note_a0,const NotificationMessage, +Variable,+,message_note_a1,const NotificationMessage, +Variable,+,message_note_a2,const NotificationMessage, +Variable,+,message_note_a3,const NotificationMessage, +Variable,+,message_note_a4,const NotificationMessage, +Variable,+,message_note_a5,const NotificationMessage, +Variable,+,message_note_a6,const NotificationMessage, +Variable,+,message_note_a7,const NotificationMessage, +Variable,+,message_note_a8,const NotificationMessage, +Variable,+,message_note_as0,const NotificationMessage, +Variable,+,message_note_as1,const NotificationMessage, +Variable,+,message_note_as2,const NotificationMessage, +Variable,+,message_note_as3,const NotificationMessage, +Variable,+,message_note_as4,const NotificationMessage, +Variable,+,message_note_as5,const NotificationMessage, +Variable,+,message_note_as6,const NotificationMessage, +Variable,+,message_note_as7,const NotificationMessage, +Variable,+,message_note_as8,const NotificationMessage, +Variable,+,message_note_b0,const NotificationMessage, +Variable,+,message_note_b1,const NotificationMessage, +Variable,+,message_note_b2,const NotificationMessage, +Variable,+,message_note_b3,const NotificationMessage, +Variable,+,message_note_b4,const NotificationMessage, +Variable,+,message_note_b5,const NotificationMessage, +Variable,+,message_note_b6,const NotificationMessage, +Variable,+,message_note_b7,const NotificationMessage, +Variable,+,message_note_b8,const NotificationMessage, +Variable,+,message_note_c0,const NotificationMessage, +Variable,+,message_note_c1,const NotificationMessage, +Variable,+,message_note_c2,const NotificationMessage, +Variable,+,message_note_c3,const NotificationMessage, +Variable,+,message_note_c4,const NotificationMessage, +Variable,+,message_note_c5,const NotificationMessage, +Variable,+,message_note_c6,const NotificationMessage, +Variable,+,message_note_c7,const NotificationMessage, +Variable,+,message_note_c8,const NotificationMessage, +Variable,+,message_note_cs0,const NotificationMessage, +Variable,+,message_note_cs1,const NotificationMessage, +Variable,+,message_note_cs2,const NotificationMessage, +Variable,+,message_note_cs3,const NotificationMessage, +Variable,+,message_note_cs4,const NotificationMessage, +Variable,+,message_note_cs5,const NotificationMessage, +Variable,+,message_note_cs6,const NotificationMessage, +Variable,+,message_note_cs7,const NotificationMessage, +Variable,+,message_note_cs8,const NotificationMessage, +Variable,+,message_note_d0,const NotificationMessage, +Variable,+,message_note_d1,const NotificationMessage, +Variable,+,message_note_d2,const NotificationMessage, +Variable,+,message_note_d3,const NotificationMessage, +Variable,+,message_note_d4,const NotificationMessage, +Variable,+,message_note_d5,const NotificationMessage, +Variable,+,message_note_d6,const NotificationMessage, +Variable,+,message_note_d7,const NotificationMessage, +Variable,+,message_note_d8,const NotificationMessage, +Variable,+,message_note_ds0,const NotificationMessage, +Variable,+,message_note_ds1,const NotificationMessage, +Variable,+,message_note_ds2,const NotificationMessage, +Variable,+,message_note_ds3,const NotificationMessage, +Variable,+,message_note_ds4,const NotificationMessage, +Variable,+,message_note_ds5,const NotificationMessage, +Variable,+,message_note_ds6,const NotificationMessage, +Variable,+,message_note_ds7,const NotificationMessage, +Variable,+,message_note_ds8,const NotificationMessage, +Variable,+,message_note_e0,const NotificationMessage, +Variable,+,message_note_e1,const NotificationMessage, +Variable,+,message_note_e2,const NotificationMessage, +Variable,+,message_note_e3,const NotificationMessage, +Variable,+,message_note_e4,const NotificationMessage, +Variable,+,message_note_e5,const NotificationMessage, +Variable,+,message_note_e6,const NotificationMessage, +Variable,+,message_note_e7,const NotificationMessage, +Variable,+,message_note_e8,const NotificationMessage, +Variable,+,message_note_f0,const NotificationMessage, +Variable,+,message_note_f1,const NotificationMessage, +Variable,+,message_note_f2,const NotificationMessage, +Variable,+,message_note_f3,const NotificationMessage, +Variable,+,message_note_f4,const NotificationMessage, +Variable,+,message_note_f5,const NotificationMessage, +Variable,+,message_note_f6,const NotificationMessage, +Variable,+,message_note_f7,const NotificationMessage, +Variable,+,message_note_f8,const NotificationMessage, +Variable,+,message_note_fs0,const NotificationMessage, +Variable,+,message_note_fs1,const NotificationMessage, +Variable,+,message_note_fs2,const NotificationMessage, +Variable,+,message_note_fs3,const NotificationMessage, +Variable,+,message_note_fs4,const NotificationMessage, +Variable,+,message_note_fs5,const NotificationMessage, +Variable,+,message_note_fs6,const NotificationMessage, +Variable,+,message_note_fs7,const NotificationMessage, +Variable,+,message_note_fs8,const NotificationMessage, +Variable,+,message_note_g0,const NotificationMessage, +Variable,+,message_note_g1,const NotificationMessage, +Variable,+,message_note_g2,const NotificationMessage, +Variable,+,message_note_g3,const NotificationMessage, +Variable,+,message_note_g4,const NotificationMessage, +Variable,+,message_note_g5,const NotificationMessage, +Variable,+,message_note_g6,const NotificationMessage, +Variable,+,message_note_g7,const NotificationMessage, +Variable,+,message_note_g8,const NotificationMessage, +Variable,+,message_note_gs0,const NotificationMessage, +Variable,+,message_note_gs1,const NotificationMessage, +Variable,+,message_note_gs2,const NotificationMessage, +Variable,+,message_note_gs3,const NotificationMessage, +Variable,+,message_note_gs4,const NotificationMessage, +Variable,+,message_note_gs5,const NotificationMessage, +Variable,+,message_note_gs6,const NotificationMessage, +Variable,+,message_note_gs7,const NotificationMessage, +Variable,+,message_note_gs8,const NotificationMessage, +Variable,+,message_red_0,const NotificationMessage, +Variable,+,message_red_255,const NotificationMessage, +Variable,+,message_sound_off,const NotificationMessage, +Variable,+,message_vibro_off,const NotificationMessage, +Variable,+,message_vibro_on,const NotificationMessage, +Variable,-,nfc_device_mf_classic,const NfcDeviceBase, +Variable,-,nfc_device_mf_desfire,const NfcDeviceBase, +Variable,-,nfc_device_mf_ultralight,const NfcDeviceBase, +Variable,-,nfc_device_st25tb,const NfcDeviceBase, +Variable,+,sequence_audiovisual_alert,const NotificationSequence, +Variable,+,sequence_blink_blue_10,const NotificationSequence, +Variable,+,sequence_blink_blue_100,const NotificationSequence, +Variable,+,sequence_blink_cyan_10,const NotificationSequence, +Variable,+,sequence_blink_cyan_100,const NotificationSequence, +Variable,+,sequence_blink_green_10,const NotificationSequence, +Variable,+,sequence_blink_green_100,const NotificationSequence, +Variable,+,sequence_blink_magenta_10,const NotificationSequence, +Variable,+,sequence_blink_magenta_100,const NotificationSequence, +Variable,+,sequence_blink_red_10,const NotificationSequence, +Variable,+,sequence_blink_red_100,const NotificationSequence, +Variable,+,sequence_blink_start_blue,const NotificationSequence, +Variable,+,sequence_blink_start_cyan,const NotificationSequence, +Variable,+,sequence_blink_start_green,const NotificationSequence, +Variable,+,sequence_blink_start_magenta,const NotificationSequence, +Variable,+,sequence_blink_start_red,const NotificationSequence, +Variable,+,sequence_blink_start_yellow,const NotificationSequence, +Variable,+,sequence_blink_stop,const NotificationSequence, +Variable,+,sequence_blink_white_100,const NotificationSequence, +Variable,+,sequence_blink_yellow_10,const NotificationSequence, +Variable,+,sequence_blink_yellow_100,const NotificationSequence, +Variable,+,sequence_charged,const NotificationSequence, +Variable,+,sequence_charging,const NotificationSequence, +Variable,+,sequence_display_backlight_enforce_auto,const NotificationSequence, +Variable,+,sequence_display_backlight_enforce_on,const NotificationSequence, +Variable,+,sequence_display_backlight_off,const NotificationSequence, +Variable,+,sequence_display_backlight_off_delay_1000,const NotificationSequence, +Variable,+,sequence_display_backlight_on,const NotificationSequence, +Variable,+,sequence_double_vibro,const NotificationSequence, +Variable,+,sequence_error,const NotificationSequence, +Variable,+,sequence_lcd_contrast_update,const NotificationSequence, +Variable,+,sequence_not_charging,const NotificationSequence, +Variable,+,sequence_reset_blue,const NotificationSequence, +Variable,+,sequence_reset_display,const NotificationSequence, +Variable,+,sequence_reset_green,const NotificationSequence, +Variable,+,sequence_reset_red,const NotificationSequence, +Variable,+,sequence_reset_rgb,const NotificationSequence, +Variable,+,sequence_reset_sound,const NotificationSequence, +Variable,+,sequence_reset_vibro,const NotificationSequence, +Variable,+,sequence_set_blue_255,const NotificationSequence, +Variable,+,sequence_set_green_255,const NotificationSequence, +Variable,+,sequence_set_only_blue_255,const NotificationSequence, +Variable,+,sequence_set_only_green_255,const NotificationSequence, +Variable,+,sequence_set_only_red_255,const NotificationSequence, +Variable,+,sequence_set_red_255,const NotificationSequence, +Variable,+,sequence_set_vibro_on,const NotificationSequence, +Variable,+,sequence_single_vibro,const NotificationSequence, +Variable,+,sequence_solid_yellow,const NotificationSequence, +Variable,+,sequence_success,const NotificationSequence, +Variable,+,simple_array_config_uint8_t,const SimpleArrayConfig, +Variable,-,subghz_device_cc1101_int,const SubGhzDevice, +Variable,+,subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs,const uint8_t[], +Variable,+,subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs,const uint8_t[], +Variable,+,subghz_device_cc1101_preset_gfsk_9_99kb_async_regs,const uint8_t[], +Variable,+,subghz_device_cc1101_preset_msk_99_97kb_async_regs,const uint8_t[], +Variable,+,subghz_device_cc1101_preset_ook_270khz_async_regs,const uint8_t[], +Variable,+,subghz_device_cc1101_preset_ook_650khz_async_regs,const uint8_t[], +Variable,+,subghz_protocol_raw,const SubGhzProtocol, +Variable,+,subghz_protocol_raw_decoder,const SubGhzProtocolDecoder, +Variable,+,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder, +Variable,+,subghz_protocol_registry,const SubGhzProtocolRegistry, +Variable,-,suboptarg,char*, +Variable,+,usb_ccid,FuriHalUsbInterface, +Variable,+,usb_cdc_dual,FuriHalUsbInterface, +Variable,+,usb_cdc_single,FuriHalUsbInterface, +Variable,+,usb_hid,FuriHalUsbInterface, +Variable,+,usb_hid_u2f,FuriHalUsbInterface, +Variable,+,usbd_devfs,const usbd_driver, diff --git a/targets/f7/application_ext.ld b/targets/f7/application_ext.ld new file mode 100644 index 00000000000..01bb021b655 --- /dev/null +++ b/targets/f7/application_ext.ld @@ -0,0 +1,54 @@ +FORCE_COMMON_ALLOCATION + +SECTIONS +{ + .text 0x00000000 : ALIGN(4) + { + *(.text) + *(.stub) + *(.text*) + *(.text.*) + *(.text._*) + + KEEP (*(.init)) + KEEP (*(.fini)) + } + + .rodata : + { + *(.rodata) + *(.rodata1) + *(.rodata.*) + } + + .data : + { + *(.data) + *(.data1) + *(.data.*) + } + + + .bss : + { + *(.bss) + *(.bss*) + *(.sbss) + *(.sbss*) + *(COMMON) + } + + .ARM.attributes : + { + *(.ARM.attributes) + *(.ARM.attributes.*) + } + + /DISCARD/ : + { + *(.comment) + *(.comment.*) + *(.llvmbc) + *(.llvmcmd) + } +} diff --git a/targets/f7/ble_glue/app_common.h b/targets/f7/ble_glue/app_common.h new file mode 100644 index 00000000000..e969636d279 --- /dev/null +++ b/targets/f7/ble_glue/app_common.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +#include "app_conf.h" diff --git a/targets/f7/ble_glue/app_conf.h b/targets/f7/ble_glue/app_conf.h new file mode 100644 index 00000000000..25fa688c700 --- /dev/null +++ b/targets/f7/ble_glue/app_conf.h @@ -0,0 +1,202 @@ +#pragma once + +#include + +#define CFG_TX_POWER (0x19) /* +0dBm */ + +#define CFG_IDENTITY_ADDRESS GAP_PUBLIC_ADDR + +/** + * Define IO Authentication + */ +#define CFG_USED_FIXED_PIN USE_FIXED_PIN_FOR_PAIRING_FORBIDDEN +#define CFG_ENCRYPTION_KEY_SIZE_MAX (16) +#define CFG_ENCRYPTION_KEY_SIZE_MIN (8) + +/** + * Define IO capabilities + */ +#define CFG_IO_CAPABILITY IO_CAP_DISPLAY_YES_NO + +/** + * Define MITM modes + */ +#define CFG_MITM_PROTECTION MITM_PROTECTION_REQUIRED + +/** + * Define Secure Connections Support + */ +#define CFG_SC_SUPPORT SC_PAIRING_OPTIONAL + +/** + * Define PHY + */ +#define ALL_PHYS_PREFERENCE 0x00 +#define RX_2M_PREFERRED 0x02 +#define TX_2M_PREFERRED 0x02 +#define TX_1M 0x01 +#define TX_2M 0x02 +#define RX_1M 0x01 +#define RX_2M 0x02 + +/****************************************************************************** + * BLE Stack + ******************************************************************************/ +/** + * Maximum number of simultaneous connections that the device will support. + * Valid values are from 1 to 8 + */ +#define CFG_BLE_NUM_LINK 1 + +/** + * Maximum number of Services that can be stored in the GATT database. + * Note that the GAP and GATT services are automatically added so this parameter should be 2 plus the number of user services + */ +#define CFG_BLE_NUM_GATT_SERVICES 8 + +/** + * Maximum number of Attributes + * (i.e. the number of characteristic + the number of characteristic values + the number of descriptors, excluding the services) + * that can be stored in the GATT database. + * Note that certain characteristics and relative descriptors are added automatically during device initialization + * so this parameters should be 9 plus the number of user Attributes + */ +#define CFG_BLE_NUM_GATT_ATTRIBUTES 68 + +/** + * Maximum supported ATT_MTU size + */ +#define CFG_BLE_MAX_ATT_MTU (256 + 128 + 16 + 8 + 4 + 2) + +/** + * Size of the storage area for Attribute values + * This value depends on the number of attributes used by application. In particular the sum of the following quantities (in octets) should be made for each attribute: + * - attribute value length + * - 5, if UUID is 16 bit; 19, if UUID is 128 bit + * - 2, if server configuration descriptor is used + * - 2*DTM_NUM_LINK, if client configuration descriptor is used + * - 2, if extended properties is used + * The total amount of memory needed is the sum of the above quantities for each attribute. + */ +#define CFG_BLE_ATT_VALUE_ARRAY_SIZE (1344) + +/** + * Prepare Write List size in terms of number of packet + */ +#define CFG_BLE_PREPARE_WRITE_LIST_SIZE BLE_PREP_WRITE_X_ATT(CFG_BLE_MAX_ATT_MTU) + +/** + * Number of allocated memory blocks + */ +#define CFG_BLE_MBLOCK_COUNT \ + (BLE_MBLOCKS_CALC(CFG_BLE_PREPARE_WRITE_LIST_SIZE, CFG_BLE_MAX_ATT_MTU, CFG_BLE_NUM_LINK)) + +/** + * Enable or disable the Extended Packet length feature. Valid values are 0 or 1. + */ +#define CFG_BLE_DATA_LENGTH_EXTENSION 1 + +/** + * Sleep clock accuracy in Slave mode (ppm value) + */ +#define CFG_BLE_SLAVE_SCA 500 + +/** + * Sleep clock accuracy in Master mode + * 0 : 251 ppm to 500 ppm + * 1 : 151 ppm to 250 ppm + * 2 : 101 ppm to 150 ppm + * 3 : 76 ppm to 100 ppm + * 4 : 51 ppm to 75 ppm + * 5 : 31 ppm to 50 ppm + * 6 : 21 ppm to 30 ppm + * 7 : 0 ppm to 20 ppm + */ +#define CFG_BLE_MASTER_SCA 0 + +/** + * Source for the low speed clock for RF wake-up + * 1 : external high speed crystal HSE/32/32 + * 0 : external low speed crystal ( no calibration ) + */ +#define CFG_BLE_LSE_SOURCE \ + SHCI_C2_BLE_INIT_CFG_BLE_LS_CLK_LSE | SHCI_C2_BLE_INIT_CFG_BLE_LS_OTHER_DEV | \ + SHCI_C2_BLE_INIT_CFG_BLE_LS_CALIB + +/** + * Start up time of the high speed (16 or 32 MHz) crystal oscillator in units of 625/256 us (~2.44 us) + */ +#define CFG_BLE_HSE_STARTUP_TIME 0x148 + +/** + * Maximum duration of the connection event when the device is in Slave mode in units of 625/256 us (~2.44 us) + */ +#define CFG_BLE_MAX_CONN_EVENT_LENGTH (0xFFFFFFFF) + +/** + * Viterbi Mode + * 1 : enabled + * 0 : disabled + */ +#define CFG_BLE_VITERBI_MODE 1 + +/** + * BLE stack Options flags to be configured with: + * - SHCI_C2_BLE_INIT_OPTIONS_LL_ONLY + * - SHCI_C2_BLE_INIT_OPTIONS_LL_HOST + * - SHCI_C2_BLE_INIT_OPTIONS_NO_SVC_CHANGE_DESC + * - SHCI_C2_BLE_INIT_OPTIONS_WITH_SVC_CHANGE_DESC + * - SHCI_C2_BLE_INIT_OPTIONS_DEVICE_NAME_RO + * - SHCI_C2_BLE_INIT_OPTIONS_DEVICE_NAME_RW + * - SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV + * - SHCI_C2_BLE_INIT_OPTIONS_NO_EXT_ADV + * - SHCI_C2_BLE_INIT_OPTIONS_CS_ALGO2 + * - SHCI_C2_BLE_INIT_OPTIONS_NO_CS_ALGO2 + * - SHCI_C2_BLE_INIT_OPTIONS_POWER_CLASS_1 + * - SHCI_C2_BLE_INIT_OPTIONS_POWER_CLASS_2_3 + * which are used to set following configuration bits: + * (bit 0): 1: LL only + * 0: LL + host + * (bit 1): 1: no service change desc. + * 0: with service change desc. + * (bit 2): 1: device name Read-Only + * 0: device name R/W + * (bit 3): 1: extended advertizing supported [NOT SUPPORTED] + * 0: extended advertizing not supported [NOT SUPPORTED] + * (bit 4): 1: CS Algo #2 supported + * 0: CS Algo #2 not supported + * (bit 7): 1: LE Power Class 1 + * 0: LE Power Class 2-3 + * other bits: reserved (shall be set to 0) + */ +#define CFG_BLE_OPTIONS \ + (SHCI_C2_BLE_INIT_OPTIONS_LL_HOST | SHCI_C2_BLE_INIT_OPTIONS_WITH_SVC_CHANGE_DESC | \ + SHCI_C2_BLE_INIT_OPTIONS_DEVICE_NAME_RO | SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV | \ + SHCI_C2_BLE_INIT_OPTIONS_CS_ALGO2 | SHCI_C2_BLE_INIT_OPTIONS_POWER_CLASS_2_3) + +/** + * Queue length of BLE Event + * This parameter defines the number of asynchronous events that can be stored in the HCI layer before + * being reported to the application. When a command is sent to the BLE core coprocessor, the HCI layer + * is waiting for the event with the Num_HCI_Command_Packets set to 1. The receive queue shall be large + * enough to store all asynchronous events received in between. + * When CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE is set to 27, this allow to store three 255 bytes long asynchronous events + * between the HCI command and its event. + * This parameter depends on the value given to CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE. When the queue size is to small, + * the system may hang if the queue is full with asynchronous events and the HCI layer is still waiting + * for a CC/CS event, In that case, the notification TL_BLE_HCI_ToNot() is called to indicate + * to the application a HCI command did not receive its command event within 30s (Default HCI Timeout). + */ +#define CFG_TLBLE_EVT_QUEUE_LENGTH 5 +/** + * This parameter should be set to fit most events received by the HCI layer. It defines the buffer size of each element + * allocated in the queue of received events and can be used to optimize the amount of RAM allocated by the Memory Manager. + * It should not exceed 255 which is the maximum HCI packet payload size (a greater value is a lost of memory as it will + * never be used) + * With the current wireless firmware implementation, this parameter shall be kept to 255 + * + */ +#define CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE \ + 255 /**< Set to 255 with the memory manager and the mailbox */ + +#define TL_BLE_EVENT_FRAME_SIZE (TL_EVT_HDR_SIZE + CFG_TLBLE_MOST_EVENT_PAYLOAD_SIZE) diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/targets/f7/ble_glue/app_debug.c similarity index 94% rename from firmware/targets/f7/ble_glue/app_debug.c rename to targets/f7/ble_glue/app_debug.c index e480ea364a7..fe76687b9aa 100644 --- a/firmware/targets/f7/ble_glue/app_debug.c +++ b/targets/f7/ble_glue/app_debug.c @@ -1,10 +1,14 @@ -#include "utilities_common.h" - #include "app_common.h" #include "app_debug.h" -#include "shci.h" -#include "tl.h" -#include "dbg_trace.h" +#include +#include +#include +#include +#include + +#include "stm32wbxx_ll_bus.h" +#include "stm32wbxx_ll_pwr.h" + #include typedef PACKED_STRUCT { @@ -32,7 +36,8 @@ PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig = {0, 0, 0, 0}; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) -static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig = {BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}}; +static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig = + {BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}, 0, 0, 0, 0, 0}; /** * THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT @@ -67,7 +72,7 @@ static const APPD_GpioConfig_t aGpioConfigList[GPIO_CFG_NBR_OF_FEATURES] = { {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* END_OF_CONNECTION_EVENT - Set on Entry / Reset on Exit */ {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* TIMER_SERVER_CALLBACK - Toggle on Entry */ {GPIOA, LL_GPIO_PIN_4, 1, 0}, /* PES_ACTIVITY - Set on Entry / Reset on Exit */ - {GPIOB, LL_GPIO_PIN_2, 1, 0}, /* MB_BLE_SEND_EVT - Set on Entry / Reset on Exit */ + {GPIOC, LL_GPIO_PIN_0, 1, 0}, /* MB_BLE_SEND_EVT - Set on Entry / Reset on Exit */ /* From v1.3.0 */ {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_NO_DELAY - Set on Entry / Reset on Exit */ {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* BLE_STACK_STORE_NVM_CB - Set on Entry / Reset on Exit */ @@ -106,10 +111,6 @@ static void APPD_SetCPU2GpioConfig(void); static void APPD_BleDtbCfg(void); void APPD_Init() { -#if(CFG_DEBUG_TRACE != 0) - DbgTraceInit(); -#endif - APPD_SetCPU2GpioConfig(); APPD_BleDtbCfg(); } @@ -194,14 +195,14 @@ static void APPD_SetCPU2GpioConfig(void) { gpio_config.Pin = gpiob_pin_list; LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOB); LL_GPIO_Init(GPIOB, &gpio_config); - LL_GPIO_ResetOutputPin(GPIOB, gpioa_pin_list); + LL_GPIO_ResetOutputPin(GPIOB, gpiob_pin_list); } if(gpioc_pin_list != 0) { gpio_config.Pin = gpioc_pin_list; LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOC); LL_GPIO_Init(GPIOC, &gpio_config); - LL_GPIO_ResetOutputPin(GPIOC, gpioa_pin_list); + LL_GPIO_ResetOutputPin(GPIOC, gpioc_pin_list); } } @@ -250,13 +251,3 @@ static void APPD_BleDtbCfg(void) { } #endif } - -#if(CFG_DEBUG_TRACE != 0) -void DbgOutputInit(void) { -} - -void DbgOutputTraces(uint8_t* p_data, uint16_t size, void (*cb)(void)) { - furi_hal_console_tx(p_data, size); - cb(); -} -#endif diff --git a/targets/f7/ble_glue/app_debug.h b/targets/f7/ble_glue/app_debug.h new file mode 100644 index 00000000000..213470bed5d --- /dev/null +++ b/targets/f7/ble_glue/app_debug.h @@ -0,0 +1,12 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void APPD_Init(void); +void APPD_EnableCPU2(void); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/ble_glue/ble_app.c b/targets/f7/ble_glue/ble_app.c new file mode 100644 index 00000000000..05dd46e943a --- /dev/null +++ b/targets/f7/ble_glue/ble_app.c @@ -0,0 +1,194 @@ +#include "ble_app.h" + +#include +#include +#include +#include "gap.h" + +#include +#include + +#define TAG "Bt" + +#define BLE_APP_FLAG_HCI_EVENT (1UL << 0) +#define BLE_APP_FLAG_KILL_THREAD (1UL << 1) +#define BLE_APP_FLAG_ALL (BLE_APP_FLAG_HCI_EVENT | BLE_APP_FLAG_KILL_THREAD) + +PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t ble_app_cmd_buffer; +PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint32_t ble_app_nvm[BLE_NVM_SRAM_SIZE]; + +_Static_assert( + sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 58, + "Ble stack config structure size mismatch (check new config options - last updated for v.1.17.3)"); + +typedef struct { + FuriMutex* hci_mtx; + FuriSemaphore* hci_sem; + FuriThread* thread; +} BleApp; + +static BleApp* ble_app = NULL; + +static int32_t ble_app_hci_thread(void* context); +static void ble_app_hci_event_handler(void* pPayload); +static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status); + +static const HCI_TL_HciInitConf_t hci_tl_config = { + .p_cmdbuffer = (uint8_t*)&ble_app_cmd_buffer, + .StatusNotCallBack = ble_app_hci_status_not_handler, +}; + +static const SHCI_C2_CONFIG_Cmd_Param_t config_param = { + .PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE, + .Config1 = SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM, + .BleNvmRamAddress = (uint32_t)ble_app_nvm, + .EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE, +}; + +static const SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { + .Header = {{0, 0, 0}}, // Header unused + .Param = { + .pBleBufferAddress = 0, // pBleBufferAddress not used + .BleBufferSize = 0, // BleBufferSize not used + .NumAttrRecord = CFG_BLE_NUM_GATT_ATTRIBUTES, + .NumAttrServ = CFG_BLE_NUM_GATT_SERVICES, + .AttrValueArrSize = CFG_BLE_ATT_VALUE_ARRAY_SIZE, + .NumOfLinks = CFG_BLE_NUM_LINK, + .ExtendedPacketLengthEnable = CFG_BLE_DATA_LENGTH_EXTENSION, + .PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE, + .MblockCount = CFG_BLE_MBLOCK_COUNT, + .AttMtu = CFG_BLE_MAX_ATT_MTU, + .SlaveSca = CFG_BLE_SLAVE_SCA, + .MasterSca = CFG_BLE_MASTER_SCA, + .LsSource = CFG_BLE_LSE_SOURCE, + .MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH, + .HsStartupTime = CFG_BLE_HSE_STARTUP_TIME, + .ViterbiEnable = CFG_BLE_VITERBI_MODE, + .Options = CFG_BLE_OPTIONS, + .HwVersion = 0, + .max_coc_initiator_nbr = 32, + .min_tx_power = 0, + .max_tx_power = 0, + .rx_model_config = 1, + /* New stack (13.3->15.0) */ + .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set + .max_adv_data_len = 1650, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set + .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB + .rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB + .ble_core_version = SHCI_C2_BLE_INIT_BLE_CORE_5_4, + /*15.0->17.0*/ + .Options_extension = SHCI_C2_BLE_INIT_OPTIONS_ENHANCED_ATT_NOTSUPPORTED | + SHCI_C2_BLE_INIT_OPTIONS_APPEARANCE_READONLY, + }}; + +bool ble_app_init() { + SHCI_CmdStatus_t status; + ble_app = malloc(sizeof(BleApp)); + // Allocate semafore and mutex for ble command buffer access + ble_app->hci_mtx = furi_mutex_alloc(FuriMutexTypeNormal); + ble_app->hci_sem = furi_semaphore_alloc(1, 0); + // HCI transport layer thread to handle user asynch events + ble_app->thread = furi_thread_alloc_ex("BleHciDriver", 1024, ble_app_hci_thread, ble_app); + furi_thread_start(ble_app->thread); + + // Initialize Ble Transport Layer + hci_init(ble_app_hci_event_handler, (void*)&hci_tl_config); + + // Configure NVM store for pairing data + status = SHCI_C2_Config((SHCI_C2_CONFIG_Cmd_Param_t*)&config_param); + if(status) { + FURI_LOG_E(TAG, "Failed to configure 2nd core: %d", status); + } + + // Start ble stack on 2nd core + status = SHCI_C2_BLE_Init((SHCI_C2_Ble_Init_Cmd_Packet_t*)&ble_init_cmd_packet); + if(status) { + FURI_LOG_E(TAG, "Failed to start ble stack: %d", status); + } + return status == SHCI_Success; +} + +void ble_app_get_key_storage_buff(uint8_t** addr, uint16_t* size) { + *addr = (uint8_t*)ble_app_nvm; + *size = sizeof(ble_app_nvm); +} + +void ble_app_thread_stop() { + if(ble_app) { + FuriThreadId thread_id = furi_thread_get_id(ble_app->thread); + furi_assert(thread_id); + furi_thread_flags_set(thread_id, BLE_APP_FLAG_KILL_THREAD); + furi_thread_join(ble_app->thread); + furi_thread_free(ble_app->thread); + // Free resources + furi_mutex_free(ble_app->hci_mtx); + furi_semaphore_free(ble_app->hci_sem); + free(ble_app); + ble_app = NULL; + memset(&ble_app_cmd_buffer, 0, sizeof(ble_app_cmd_buffer)); + } +} + +static int32_t ble_app_hci_thread(void* arg) { + UNUSED(arg); + uint32_t flags = 0; + + while(1) { + flags = furi_thread_flags_wait(BLE_APP_FLAG_ALL, FuriFlagWaitAny, FuriWaitForever); + if(flags & BLE_APP_FLAG_KILL_THREAD) { + break; + } + if(flags & BLE_APP_FLAG_HCI_EVENT) { + hci_user_evt_proc(); + } + } + + return 0; +} + +// Called by WPAN lib +void hci_notify_asynch_evt(void* pdata) { + UNUSED(pdata); + furi_check(ble_app); + FuriThreadId thread_id = furi_thread_get_id(ble_app->thread); + furi_assert(thread_id); + furi_thread_flags_set(thread_id, BLE_APP_FLAG_HCI_EVENT); +} + +void hci_cmd_resp_release(uint32_t flag) { + UNUSED(flag); + furi_check(ble_app); + furi_check(furi_semaphore_release(ble_app->hci_sem) == FuriStatusOk); +} + +void hci_cmd_resp_wait(uint32_t timeout) { + furi_check(ble_app); + furi_check(furi_semaphore_acquire(ble_app->hci_sem, timeout) == FuriStatusOk); +} + +static void ble_app_hci_event_handler(void* pPayload) { + SVCCTL_UserEvtFlowStatus_t svctl_return_status; + tHCI_UserEvtRxParam* pParam = (tHCI_UserEvtRxParam*)pPayload; + + furi_check(ble_app); + svctl_return_status = SVCCTL_UserEvtRx((void*)&(pParam->pckt->evtserial)); + if(svctl_return_status != SVCCTL_UserEvtFlowDisable) { + pParam->status = HCI_TL_UserEventFlow_Enable; + } else { + pParam->status = HCI_TL_UserEventFlow_Disable; + } +} + +static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status) { + if(status == HCI_TL_CmdBusy) { + furi_hal_power_insomnia_enter(); + furi_mutex_acquire(ble_app->hci_mtx, FuriWaitForever); + } else if(status == HCI_TL_CmdAvailable) { + furi_mutex_release(ble_app->hci_mtx); + furi_hal_power_insomnia_exit(); + } +} + +void SVCCTL_ResumeUserEventFlow(void) { + hci_resume_flow(); +} diff --git a/firmware/targets/f7/ble_glue/ble_app.h b/targets/f7/ble_glue/ble_app.h similarity index 100% rename from firmware/targets/f7/ble_glue/ble_app.h rename to targets/f7/ble_glue/ble_app.h index 495005ec484..2e6babab79d 100644 --- a/firmware/targets/f7/ble_glue/ble_app.h +++ b/targets/f7/ble_glue/ble_app.h @@ -1,12 +1,12 @@ #pragma once +#include +#include + #ifdef __cplusplus extern "C" { #endif -#include -#include - bool ble_app_init(); void ble_app_get_key_storage_buff(uint8_t** addr, uint16_t* size); void ble_app_thread_stop(); diff --git a/targets/f7/ble_glue/ble_conf.h b/targets/f7/ble_glue/ble_conf.h new file mode 100644 index 00000000000..2b9c22dfe39 --- /dev/null +++ b/targets/f7/ble_glue/ble_conf.h @@ -0,0 +1,14 @@ +#pragma once + +#include "app_conf.h" + +/** + * There is one handler per service enabled + * Note: There is no handler for the Device Information Service + * + * This shall take into account all registered handlers + * (from either the provided services or the custom services) + */ +#define BLE_CFG_SVC_MAX_NBR_CB 7 + +#define BLE_CFG_CLT_MAX_NBR_CB 0 diff --git a/targets/f7/ble_glue/ble_const.h b/targets/f7/ble_glue/ble_const.h new file mode 100644 index 00000000000..1e6647d90fa --- /dev/null +++ b/targets/f7/ble_glue/ble_const.h @@ -0,0 +1,99 @@ +#pragma once + +#include +#include +#include +#include +#include "osal.h" +#include "compiler.h" + +/* Default BLE variant */ +#ifndef BASIC_FEATURES +#define BASIC_FEATURES 0 +#endif +#ifndef SLAVE_ONLY +#define SLAVE_ONLY 0 +#endif +#ifndef LL_ONLY +#define LL_ONLY 0 +#endif +#ifndef LL_ONLY_BASIC +#define LL_ONLY_BASIC 0 +#endif +#ifndef BEACON_ONLY +#define BEACON_ONLY 0 +#endif + +/* Size of command/events buffers: + * + * To change the size of commands and events parameters used in the + * auto-generated files, you need to update 2 defines: + * + * - BLE_CMD_MAX_PARAM_LEN + * - BLE_EVT_MAX_PARAM_LEN + * + * These 2 defines are set below with default values and can be changed. + * + * To compute the value to support a characteristic of 512 bytes for a specific + * command or an event, you need to look in "ble_types.h". + * + * Here are 2 examples, one with a command and one with an event: + * + * - aci_gatt_update_char_value_ext_cp0 + * ---------------------------------- + * + * we have in the structure: + * + * uint8_t Value[(BLE_CMD_MAX_PARAM_LEN- 12)/sizeof(uint8_t)]; + * + * so to support a 512 byte value, we need to have + * + * BLE_CMD_MAX_PARAM_LEN at least equal to: 512 + 12 = 524 + * + * - aci_gatt_read_handle_value_rp0 + * ------------------------------ + * + * we have in the structure: + * + * uint8_t Value[((BLE_EVT_MAX_PARAM_LEN - 3) - 5)/sizeof(uint8_t)]; + * + * so to support a 512 byte value, we need to have + * + * BLE_EVT_MAX_PARAM_LEN at least equal to: 512 + 3 + 5 = 520 + * + * If you need several events or commands with 512-size values, you need to + * take the maximum values for BLE_EVT_MAX_PARAM_LEN and BLE_CMD_MAX_PARAM_LEN. + * + */ + +/* Maximum parameter size of BLE commands. + * Change this value if needed. */ +#define BLE_CMD_MAX_PARAM_LEN HCI_COMMAND_MAX_PARAM_LEN + +/* Maximum parameter size of BLE responses/events. + * Change this value if needed. */ +#define BLE_EVT_MAX_PARAM_LEN HCI_EVENT_MAX_PARAM_LEN + +/* Callback function to send command and receive response */ +struct hci_request { + uint16_t ogf; + uint16_t ocf; + int event; + void* cparam; + int clen; + void* rparam; + int rlen; +}; +extern int hci_send_req(struct hci_request* req, uint8_t async); + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif diff --git a/targets/f7/ble_glue/ble_dbg_conf.h b/targets/f7/ble_glue/ble_dbg_conf.h new file mode 100644 index 00000000000..6f70f09beec --- /dev/null +++ b/targets/f7/ble_glue/ble_dbg_conf.h @@ -0,0 +1 @@ +#pragma once diff --git a/firmware/targets/f7/ble_glue/ble_glue.c b/targets/f7/ble_glue/ble_glue.c similarity index 91% rename from firmware/targets/f7/ble_glue/ble_glue.c rename to targets/f7/ble_glue/ble_glue.c index 585a8982027..5f129ba8ce3 100644 --- a/firmware/targets/f7/ble_glue/ble_glue.c +++ b/targets/f7/ble_glue/ble_glue.c @@ -1,14 +1,15 @@ #include "ble_glue.h" #include "app_common.h" #include "ble_app.h" -#include "ble.h" -#include "tl.h" -#include "shci.h" -#include "shci_tl.h" +#include +#include + +#include +#include +#include #include "app_debug.h" #include -#include #define TAG "Core2" @@ -31,7 +32,6 @@ static uint8_t ble_glue_ble_spare_event_buff[sizeof(TL_PacketHeader_t) + TL_EVT_ typedef struct { FuriMutex* shci_mtx; - FuriSemaphore* shci_sem; FuriThread* thread; BleGlueStatus status; BleGlueKeyStorageChangedCallback callback; @@ -54,16 +54,44 @@ void ble_glue_set_key_storage_changed_callback( ble_glue->context = context; } +/////////////////////////////////////////////////////////////////////////////// + +/* TL hook to catch hardfaults */ + +int32_t ble_glue_TL_SYS_SendCmd(uint8_t* buffer, uint16_t size) { + if(furi_hal_bt_get_hardfault_info()) { + furi_crash("ST(R) Copro(R) HardFault"); + } + + return TL_SYS_SendCmd(buffer, size); +} + +void shci_register_io_bus(tSHciIO* fops) { + /* Register IO bus services */ + fops->Init = TL_SYS_Init; + fops->Send = ble_glue_TL_SYS_SendCmd; +} + +static int32_t ble_glue_TL_BLE_SendCmd(uint8_t* buffer, uint16_t size) { + if(furi_hal_bt_get_hardfault_info()) { + furi_crash("ST(R) Copro(R) HardFault"); + } + + return TL_BLE_SendCmd(buffer, size); +} + +void hci_register_io_bus(tHciIO* fops) { + /* Register IO bus services */ + fops->Init = TL_BLE_Init; + fops->Send = ble_glue_TL_BLE_SendCmd; +} + +/////////////////////////////////////////////////////////////////////////////// + void ble_glue_init() { ble_glue = malloc(sizeof(BleGlue)); ble_glue->status = BleGlueStatusStartup; - // Configure the system Power Mode - // Select HSI as system clock source after Wake Up from Stop mode - LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI); - /* Initialize the CPU2 reset value before starting CPU2 with C2BOOT */ - LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); - #ifdef BLE_GLUE_DEBUG APPD_Init(); #endif @@ -75,14 +103,9 @@ void ble_glue_init() { TL_Init(); ble_glue->shci_mtx = furi_mutex_alloc(FuriMutexTypeNormal); - ble_glue->shci_sem = furi_semaphore_alloc(1, 0); // FreeRTOS system task creation - ble_glue->thread = furi_thread_alloc(); - furi_thread_set_name(ble_glue->thread, "BleShciDriver"); - furi_thread_set_stack_size(ble_glue->thread, 1024); - furi_thread_set_context(ble_glue->thread, ble_glue); - furi_thread_set_callback(ble_glue->thread, ble_glue_shci_thread); + ble_glue->thread = furi_thread_alloc_ex("BleShciDriver", 1024, ble_glue_shci_thread, ble_glue); furi_thread_start(ble_glue->thread); // System channel initialization @@ -197,7 +220,6 @@ bool ble_glue_wait_for_c2_start(int32_t timeout) { bool started = false; do { - // TODO: use mutex? started = ble_glue->status == BleGlueStatusC2Started; if(!started) { timeout--; @@ -369,7 +391,6 @@ void ble_glue_thread_stop() { furi_thread_free(ble_glue->thread); // Free resources furi_mutex_free(ble_glue->shci_mtx); - furi_semaphore_free(ble_glue->shci_sem); ble_glue_clear_shared_memory(); free(ble_glue); ble_glue = NULL; @@ -403,20 +424,6 @@ void shci_notify_asynch_evt(void* pdata) { } } -void shci_cmd_resp_release(uint32_t flag) { - UNUSED(flag); - if(ble_glue) { - furi_semaphore_release(ble_glue->shci_sem); - } -} - -void shci_cmd_resp_wait(uint32_t timeout) { - UNUSED(timeout); - if(ble_glue) { - furi_semaphore_acquire(ble_glue->shci_sem, FuriWaitForever); - } -} - bool ble_glue_reinit_c2() { return SHCI_C2_Reinit() == SHCI_Success; } @@ -460,17 +467,15 @@ BleGlueCommandResult ble_glue_fus_get_status() { BleGlueCommandResult ble_glue_fus_wait_operation() { furi_check(ble_glue->c2_info.mode == BleGlueC2ModeFUS); - bool wip; - do { + + while(true) { BleGlueCommandResult fus_status = ble_glue_fus_get_status(); - if(fus_status == BleGlueCommandResultError) { - return BleGlueCommandResultError; - } - wip = fus_status == BleGlueCommandResultOperationOngoing; - if(wip) { + if(fus_status == BleGlueCommandResultOperationOngoing) { furi_delay_ms(20); + } else if(fus_status == BleGlueCommandResultError) { + return BleGlueCommandResultError; + } else { + return BleGlueCommandResultOK; } - } while(wip); - - return BleGlueCommandResultOK; + } } diff --git a/firmware/targets/f7/ble_glue/ble_glue.h b/targets/f7/ble_glue/ble_glue.h similarity index 100% rename from firmware/targets/f7/ble_glue/ble_glue.h rename to targets/f7/ble_glue/ble_glue.h diff --git a/targets/f7/ble_glue/compiler.h b/targets/f7/ble_glue/compiler.h new file mode 100644 index 00000000000..1d32c2a2edc --- /dev/null +++ b/targets/f7/ble_glue/compiler.h @@ -0,0 +1,138 @@ +#pragma once + +#ifndef __PACKED_STRUCT +#define __PACKED_STRUCT PACKED(struct) +#endif + +#ifndef __PACKED_UNION +#define __PACKED_UNION PACKED(union) +#endif + +/** + * @brief This is the section dedicated to IAR toolchain + */ +#if defined(__ICCARM__) || defined(__IAR_SYSTEMS_ASM__) + +#ifndef __WEAK +#define __WEAK __weak +#endif + +#define QUOTE_(a) #a + +/** + * @brief PACKED + * Use the PACKED macro for variables that needs to be packed. + * Usage: PACKED(struct) myStruct_s + * PACKED(union) myStruct_s + */ +#define PACKED(decl) __packed decl + +/** + * @brief SECTION + * Use the SECTION macro to assign data or code in a specific section. + * Usage: SECTION(".my_section") + */ +#define SECTION(name) _Pragma(QUOTE_(location = name)) + +/** + * @brief ALIGN_DEF + * Use the ALIGN_DEF macro to specify the alignment of a variable. + * Usage: ALIGN_DEF(4) + */ +#define ALIGN_DEF(v) _Pragma(QUOTE_(data_alignment = v)) + +/** + * @brief NO_INIT + * Use the NO_INIT macro to declare a not initialized variable. + * Usage: NO_INIT(int my_no_init_var) + * Usage: NO_INIT(uint16_t my_no_init_array[10]) + */ +#define NO_INIT(var) __no_init var + +/** + * @brief This is the section dedicated to GNU toolchain + */ +#else +#ifdef __GNUC__ + +#ifndef __WEAK +#define __WEAK __attribute__((weak)) +#endif + +/** + * @brief PACKED + * Use the PACKED macro for variables that needs to be packed. + * Usage: PACKED(struct) myStruct_s + * PACKED(union) myStruct_s + */ +#define PACKED(decl) decl __attribute__((packed)) + +/** + * @brief SECTION + * Use the SECTION macro to assign data or code in a specific section. + * Usage: SECTION(".my_section") + */ +#define SECTION(name) __attribute__((section(name))) + +/** + * @brief ALIGN_DEF + * Use the ALIGN_DEF macro to specify the alignment of a variable. + * Usage: ALIGN_DEF(4) + */ +#define ALIGN_DEF(N) __attribute__((aligned(N))) + +/** + * @brief NO_INIT + * Use the NO_INIT macro to declare a not initialized variable. + * Usage: NO_INIT(int my_no_init_var) + * Usage: NO_INIT(uint16_t my_no_init_array[10]) + */ +#define NO_INIT(var) var __attribute__((section(".noinit"))) + +/** + * @brief This is the section dedicated to Keil toolchain + */ +#else +#ifdef __CC_ARM + +#ifndef __WEAK +#define __WEAK __weak +#endif + +/** + * @brief PACKED + * Use the PACKED macro for variables that needs to be packed. + * Usage: PACKED(struct) myStruct_s + * PACKED(union) myStruct_s + */ +#define PACKED(decl) decl __attribute__((packed)) + +/** + * @brief SECTION + * Use the SECTION macro to assign data or code in a specific section. + * Usage: SECTION(".my_section") + */ +#define SECTION(name) __attribute__((section(name))) + +/** + * @brief ALIGN_DEF + * Use the ALIGN_DEF macro to specify the alignment of a variable. + * Usage: ALIGN_DEF(4) + */ +#define ALIGN_DEF(N) __attribute__((aligned(N))) + +/** + * @brief NO_INIT + * Use the NO_INIT macro to declare a not initialized variable. + * Usage: NO_INIT(int my_no_init_var) + * Usage: NO_INIT(uint16_t my_no_init_array[10]) + */ +#define NO_INIT(var) var __attribute__((section("NoInit"))) + +#else + +#error Neither ICCARM, CC ARM nor GNUC C detected. Define your macros. + +#endif +#endif +#endif diff --git a/firmware/targets/f7/ble_glue/gap.c b/targets/f7/ble_glue/gap.c similarity index 91% rename from firmware/targets/f7/ble_glue/gap.c rename to targets/f7/ble_glue/gap.c index 7154b9b1194..f0533567eae 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/targets/f7/ble_glue/gap.c @@ -1,6 +1,7 @@ #include "gap.h" -#include "ble.h" +#include "app_common.h" +#include #include #include @@ -85,7 +86,7 @@ static void gap_verify_connection_parameters(Gap* gap) { SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { hci_event_pckt* event_pckt; evt_le_meta_event* meta_evt; - evt_blue_aci* blue_evt; + evt_blecore_aci* blue_evt; hci_le_phy_update_complete_event_rp0* evt_le_phy_update_complete; uint8_t tx_phy; uint8_t rx_phy; @@ -97,7 +98,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { furi_mutex_acquire(gap->state_mutex, FuriWaitForever); } switch(event_pckt->evt) { - case EVT_DISCONN_COMPLETE: { + case HCI_DISCONNECTION_COMPLETE_EVT_CODE: { hci_disconnection_complete_event_rp0* disconnection_complete_event = (hci_disconnection_complete_event_rp0*)event_pckt->data; if(disconnection_complete_event->Connection_Handle == gap->service.connection_handle) { @@ -106,6 +107,8 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { FURI_LOG_I( TAG, "Disconnect from client. Reason: %02X", disconnection_complete_event->Reason); } + // Enterprise sleep + furi_delay_us(666 + 666); if(gap->enable_adv) { // Restart advertising gap_advertise_start(GapStateAdvFast); @@ -114,10 +117,10 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->on_event_cb(event, gap->context); } break; - case EVT_LE_META_EVENT: + case HCI_LE_META_EVT_CODE: meta_evt = (evt_le_meta_event*)event_pckt->data; switch(meta_evt->subevent) { - case EVT_LE_CONN_UPDATE_COMPLETE: { + case HCI_LE_CONNECTION_UPDATE_COMPLETE_SUBEVT_CODE: { hci_le_connection_update_complete_event_rp0* event = (hci_le_connection_update_complete_event_rp0*)meta_evt->data; gap->connection_params.conn_interval = event->Conn_Interval; @@ -128,7 +131,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { break; } - case EVT_LE_PHY_UPDATE_COMPLETE: + case HCI_LE_PHY_UPDATE_COMPLETE_SUBEVT_CODE: evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data; if(evt_le_phy_update_complete->Status) { FURI_LOG_E( @@ -144,7 +147,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { } break; - case EVT_LE_CONN_COMPLETE: { + case HCI_LE_CONNECTION_COMPLETE_SUBEVT_CODE: { hci_le_connection_complete_event_rp0* event = (hci_le_connection_complete_event_rp0*)meta_evt->data; gap->connection_params.conn_interval = event->Conn_Interval; @@ -168,29 +171,29 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { } break; - case EVT_VENDOR: - blue_evt = (evt_blue_aci*)event_pckt->data; + case HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE: + blue_evt = (evt_blecore_aci*)event_pckt->data; switch(blue_evt->ecode) { aci_gap_pairing_complete_event_rp0* pairing_complete; - case EVT_BLUE_GAP_LIMITED_DISCOVERABLE: + case ACI_GAP_LIMITED_DISCOVERABLE_VSEVT_CODE: FURI_LOG_I(TAG, "Limited discoverable event"); break; - case EVT_BLUE_GAP_PASS_KEY_REQUEST: { + case ACI_GAP_PASS_KEY_REQ_VSEVT_CODE: { // Generate random PIN code - uint32_t pin = rand() % 999999; + uint32_t pin = rand() % 999999; //-V1064 aci_gap_pass_key_resp(gap->service.connection_handle, pin); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) { FURI_LOG_I(TAG, "Pass key request event. Pin: ******"); } else { - FURI_LOG_I(TAG, "Pass key request event. Pin: %06d", pin); + FURI_LOG_I(TAG, "Pass key request event. Pin: %06ld", pin); } GapEvent event = {.type = GapEventTypePinCodeShow, .data.pin_code = pin}; gap->on_event_cb(event, gap->context); } break; - case EVT_BLUE_ATT_EXCHANGE_MTU_RESP: { + case ACI_ATT_EXCHANGE_MTU_RESP_VSEVT_CODE: { aci_att_exchange_mtu_resp_event_rp0* pr = (void*)blue_evt->data; FURI_LOG_I(TAG, "Rx MTU size: %d", pr->Server_RX_MTU); // Set maximum packet size given header size is 3 bytes @@ -199,42 +202,38 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->on_event_cb(event, gap->context); } break; - case EVT_BLUE_GAP_AUTHORIZATION_REQUEST: + case ACI_GAP_AUTHORIZATION_REQ_VSEVT_CODE: FURI_LOG_D(TAG, "Authorization request event"); break; - case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED: + case ACI_GAP_SLAVE_SECURITY_INITIATED_VSEVT_CODE: FURI_LOG_D(TAG, "Slave security initiated"); break; - case EVT_BLUE_GAP_BOND_LOST: + case ACI_GAP_BOND_LOST_VSEVT_CODE: FURI_LOG_D(TAG, "Bond lost event. Start rebonding"); aci_gap_allow_rebond(gap->service.connection_handle); break; - case EVT_BLUE_GAP_DEVICE_FOUND: - FURI_LOG_D(TAG, "Device found event"); - break; - - case EVT_BLUE_GAP_ADDR_NOT_RESOLVED: + case ACI_GAP_ADDR_NOT_RESOLVED_VSEVT_CODE: FURI_LOG_D(TAG, "Address not resolved event"); break; - case EVT_BLUE_GAP_KEYPRESS_NOTIFICATION: + case ACI_GAP_KEYPRESS_NOTIFICATION_VSEVT_CODE: FURI_LOG_D(TAG, "Key press notification event"); break; - case EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE: { + case ACI_GAP_NUMERIC_COMPARISON_VALUE_VSEVT_CODE: { uint32_t pin = ((aci_gap_numeric_comparison_value_event_rp0*)(blue_evt->data))->Numeric_Value; - FURI_LOG_I(TAG, "Verify numeric comparison: %06d", pin); + FURI_LOG_I(TAG, "Verify numeric comparison: %06lu", pin); GapEvent event = {.type = GapEventTypePinCodeVerify, .data.pin_code = pin}; bool result = gap->on_event_cb(event, gap->context); aci_gap_numeric_comparison_value_confirm_yesno(gap->service.connection_handle, result); break; } - case EVT_BLUE_GAP_PAIRING_CMPLT: + case ACI_GAP_PAIRING_COMPLETE_VSEVT_CODE: pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data; if(pairing_complete->Status) { FURI_LOG_E( @@ -245,15 +244,15 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { } else { FURI_LOG_I(TAG, "Pairing complete"); GapEvent event = {.type = GapEventTypeConnected}; - gap->on_event_cb(event, gap->context); + gap->on_event_cb(event, gap->context); //-V595 } break; - case EVT_BLUE_GAP_PROCEDURE_COMPLETE: + case ACI_L2CAP_CONNECTION_UPDATE_RESP_VSEVT_CODE: FURI_LOG_D(TAG, "Procedure complete event"); break; - case EVT_BLUE_L2CAP_CONNECTION_UPDATE_RESP: { + case ACI_L2CAP_CONNECTION_UPDATE_REQ_VSEVT_CODE: { uint16_t result = ((aci_l2cap_connection_update_resp_event_rp0*)(blue_evt->data))->Result; if(result == 0) { @@ -289,8 +288,6 @@ static void gap_init_svc(Gap* gap) { tBleStatus status; uint32_t srd_bd_addr[2]; - // HCI Reset to synchronise BLE Stack - hci_reset(); // Configure mac address aci_hal_write_config_data( CONFIG_DATA_PUBADDR_OFFSET, CONFIG_DATA_PUBADDR_LEN, gap->config->mac_address); @@ -333,6 +330,7 @@ static void gap_init_svc(Gap* gap) { if(status) { FURI_LOG_E(TAG, "Failed updating name characteristic: %d", status); } + uint8_t gap_appearence_char_uuid[2] = { gap->config->appearance_char & 0xff, gap->config->appearance_char >> 8}; status = aci_gatt_update_char_value( @@ -364,7 +362,7 @@ static void gap_init_svc(Gap* gap) { CFG_ENCRYPTION_KEY_SIZE_MAX, CFG_USED_FIXED_PIN, 0, - PUBLIC_ADDR); + CFG_IDENTITY_ADDRESS); // Configure whitelist aci_gap_configure_whitelist(); } @@ -399,7 +397,7 @@ static void gap_advertise_start(GapState new_state) { ADV_IND, min_interval, max_interval, - PUBLIC_ADDR, + CFG_IDENTITY_ADDRESS, 0, strlen(gap->service.adv_name), (uint8_t*)gap->service.adv_name, @@ -478,7 +476,6 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) { gap = malloc(sizeof(Gap)); gap->config = config; - srand(DWT->CYCCNT); // Create advertising timer gap->advertise_timer = furi_timer_alloc(gap_advetise_timer_callback, FuriTimerTypeOnce, NULL); // Initialization of GATT & GAP layer @@ -493,11 +490,7 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) { gap->enable_adv = true; // Thread configuration - gap->thread = furi_thread_alloc(); - furi_thread_set_name(gap->thread, "BleGapDriver"); - furi_thread_set_stack_size(gap->thread, 1024); - furi_thread_set_context(gap->thread, gap); - furi_thread_set_callback(gap->thread, gap_app); + gap->thread = furi_thread_alloc_ex("BleGapDriver", 1024, gap_app, gap); furi_thread_start(gap->thread); // Command queue allocation @@ -539,8 +532,6 @@ void gap_thread_stop() { // Free resources furi_mutex_free(gap->state_mutex); furi_message_queue_free(gap->command_queue); - furi_timer_stop(gap->advertise_timer); - while(xTimerIsTimerActive(gap->advertise_timer) == pdTRUE) furi_delay_tick(1); furi_timer_free(gap->advertise_timer); free(gap); gap = NULL; diff --git a/firmware/targets/f7/ble_glue/gap.h b/targets/f7/ble_glue/gap.h similarity index 100% rename from firmware/targets/f7/ble_glue/gap.h rename to targets/f7/ble_glue/gap.h diff --git a/targets/f7/ble_glue/hsem_map.h b/targets/f7/ble_glue/hsem_map.h new file mode 100644 index 00000000000..9a5f51d204c --- /dev/null +++ b/targets/f7/ble_glue/hsem_map.h @@ -0,0 +1,81 @@ +#pragma once + +/****************************************************************************** + * Semaphores + * THIS SHALL NO BE CHANGED AS THESE SEMAPHORES ARE USED AS WELL ON THE CM0+ + *****************************************************************************/ +/** +* Index of the semaphore used the prevent conflicts after standby sleep. +* Each CPUs takes this semaphore at standby wakeup until conflicting elements are restored. +*/ +#define CFG_HW_PWR_STANDBY_SEMID 10 +/** +* The CPU2 may be configured to store the Thread persistent data either in internal NVM storage on CPU2 or in +* SRAM2 buffer provided by the user application. This can be configured with the system command SHCI_C2_Config() +* When the CPU2 is requested to store persistent data in SRAM2, it can write data in this buffer at any time when needed. +* In order to read consistent data with the CPU1 from the SRAM2 buffer, the flow should be: +* + CPU1 takes CFG_HW_THREAD_NVM_SRAM_SEMID semaphore +* + CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these data into an NVM managed by CPU1) +* + CPU1 releases CFG_HW_THREAD_NVM_SRAM_SEMID semaphore +* CFG_HW_THREAD_NVM_SRAM_SEMID semaphore makes sure CPU2 does not update the persistent data in SRAM2 at the same time CPU1 is reading them. +* There is no timing constraint on how long this semaphore can be kept. +*/ +#define CFG_HW_THREAD_NVM_SRAM_SEMID 9 + +/** +* The CPU2 may be configured to store the BLE persistent data either in internal NVM storage on CPU2 or in +* SRAM2 buffer provided by the user application. This can be configured with the system command SHCI_C2_Config() +* When the CPU2 is requested to store persistent data in SRAM2, it can write data in this buffer at any time when needed. +* In order to read consistent data with the CPU1 from the SRAM2 buffer, the flow should be: +* + CPU1 takes CFG_HW_BLE_NVM_SRAM_SEMID semaphore +* + CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these data into an NVM managed by CPU1) +* + CPU1 releases CFG_HW_BLE_NVM_SRAM_SEMID semaphore +* CFG_HW_BLE_NVM_SRAM_SEMID semaphore makes sure CPU2 does not update the persistent data in SRAM2 at the same time CPU1 is reading them. +* There is no timing constraint on how long this semaphore can be kept. +*/ +#define CFG_HW_BLE_NVM_SRAM_SEMID 8 + +/** +* Index of the semaphore used by CPU2 to prevent the CPU1 to either write or erase data in flash +* The CPU1 shall not either write or erase in flash when this semaphore is taken by the CPU2 +* When the CPU1 needs to either write or erase in flash, it shall first get the semaphore and release it just +* after writing a raw (64bits data) or erasing one sector. +* Once the Semaphore has been released, there shall be at least 1us before it can be taken again. This is required +* to give the opportunity to CPU2 to take it. +* On v1.4.0 and older CPU2 wireless firmware, this semaphore is unused and CPU2 is using PES bit. +* By default, CPU2 is using the PES bit to protect its timing. The CPU1 may request the CPU2 to use the semaphore +* instead of the PES bit by sending the system command SHCI_C2_SetFlashActivityControl() +*/ +#define CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID 7 + +/** +* Index of the semaphore used by CPU1 to prevent the CPU2 to either write or erase data in flash +* In order to protect its timing, the CPU1 may get this semaphore to prevent the CPU2 to either +* write or erase in flash (as this will stall both CPUs) +* The PES bit shall not be used as this may stall the CPU2 in some cases. +*/ +#define CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID 6 + +/** +* Index of the semaphore used to manage the CLK48 clock configuration +* When the USB is required, this semaphore shall be taken before configuring te CLK48 for USB +* and should be released after the application switch OFF the clock when the USB is not used anymore +* When using the RNG, it is good enough to use CFG_HW_RNG_SEMID to control CLK48. +* More details in AN5289 +*/ +#define CFG_HW_CLK48_CONFIG_SEMID 5 + +/* Index of the semaphore used to manage the entry Stop Mode procedure */ +#define CFG_HW_ENTRY_STOP_MODE_SEMID 4 + +/* Index of the semaphore used to access the RCC */ +#define CFG_HW_RCC_SEMID 3 + +/* Index of the semaphore used to access the FLASH */ +#define CFG_HW_FLASH_SEMID 2 + +/* Index of the semaphore used to access the PKA */ +#define CFG_HW_PKA_SEMID 1 + +/* Index of the semaphore used to access the RNG */ +#define CFG_HW_RNG_SEMID 0 diff --git a/targets/f7/ble_glue/hw_ipcc.c b/targets/f7/ble_glue/hw_ipcc.c new file mode 100644 index 00000000000..c2397f35109 --- /dev/null +++ b/targets/f7/ble_glue/hw_ipcc.c @@ -0,0 +1,164 @@ +#include "app_common.h" +#include +#include +#include + +#include +#include + +#include + +#define HW_IPCC_TX_PENDING(channel) \ + ((!(LL_C1_IPCC_IsActiveFlag_CHx(IPCC, channel))) && \ + LL_C1_IPCC_IsEnabledTransmitChannel(IPCC, channel)) +#define HW_IPCC_RX_PENDING(channel) \ + (LL_C2_IPCC_IsActiveFlag_CHx(IPCC, channel) && \ + LL_C1_IPCC_IsEnabledReceiveChannel(IPCC, channel)) + +static void (*FreeBufCb)(); + +static void HW_IPCC_BLE_EvtHandler(); +static void HW_IPCC_BLE_AclDataEvtHandler(); +static void HW_IPCC_MM_FreeBufHandler(); +static void HW_IPCC_SYS_CmdEvtHandler(); +static void HW_IPCC_SYS_EvtHandler(); +static void HW_IPCC_TRACES_EvtHandler(); + +void HW_IPCC_Rx_Handler() { + if(HW_IPCC_RX_PENDING(HW_IPCC_SYSTEM_EVENT_CHANNEL)) { + HW_IPCC_SYS_EvtHandler(); + } else if(HW_IPCC_RX_PENDING(HW_IPCC_BLE_EVENT_CHANNEL)) { + HW_IPCC_BLE_EvtHandler(); + } else if(HW_IPCC_RX_PENDING(HW_IPCC_TRACES_CHANNEL)) { + HW_IPCC_TRACES_EvtHandler(); + } +} + +void HW_IPCC_Tx_Handler() { + if(HW_IPCC_TX_PENDING(HW_IPCC_SYSTEM_CMD_RSP_CHANNEL)) { + HW_IPCC_SYS_CmdEvtHandler(); + } else if(HW_IPCC_TX_PENDING(HW_IPCC_SYSTEM_CMD_RSP_CHANNEL)) { + HW_IPCC_SYS_CmdEvtHandler(); + } else if(HW_IPCC_TX_PENDING(HW_IPCC_MM_RELEASE_BUFFER_CHANNEL)) { + HW_IPCC_MM_FreeBufHandler(); + } else if(HW_IPCC_TX_PENDING(HW_IPCC_HCI_ACL_DATA_CHANNEL)) { + HW_IPCC_BLE_AclDataEvtHandler(); + } +} + +void HW_IPCC_Enable() { + /** + * Such as IPCC IP available to the CPU2, it is required to keep the IPCC clock running + when FUS is running on CPU2 and CPU1 enters deep sleep mode + */ + /** + * When the device is out of standby, it is required to use the EXTI mechanism to wakeup CPU2 + */ + LL_C2_EXTI_EnableEvent_32_63(LL_EXTI_LINE_41); + LL_EXTI_EnableRisingTrig_32_63(LL_EXTI_LINE_41); + + /** + * In case the SBSFU is implemented, it may have already set the C2BOOT bit to startup the CPU2. + * In that case, to keep the mechanism transparent to the user application, it shall call the system command + * SHCI_C2_Reinit( ) before jumping to the application. + * When the CPU2 receives that command, it waits for its event input to be set to restart the CPU2 firmware. + * This is required because once C2BOOT has been set once, a clear/set on C2BOOT has no effect. + * When SHCI_C2_Reinit( ) is not called, generating an event to the CPU2 does not have any effect + * So, by default, the application shall both set the event flag and set the C2BOOT bit. + */ + __SEV(); /* Set the internal event flag and send an event to the CPU2 */ + __WFE(); /* Clear the internal event flag */ + LL_PWR_EnableBootC2(); +} + +void HW_IPCC_Init() { + LL_C1_IPCC_EnableIT_RXO(IPCC); + LL_C1_IPCC_EnableIT_TXF(IPCC); + + NVIC_SetPriority(IPCC_C1_RX_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 6, 0)); + NVIC_EnableIRQ(IPCC_C1_RX_IRQn); + NVIC_SetPriority(IPCC_C1_TX_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 6, 0)); + NVIC_EnableIRQ(IPCC_C1_TX_IRQn); +} + +void HW_IPCC_BLE_Init() { + LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_BLE_EVENT_CHANNEL); +} + +void HW_IPCC_BLE_SendCmd() { + LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_BLE_CMD_CHANNEL); +} + +static void HW_IPCC_BLE_EvtHandler() { + HW_IPCC_BLE_RxEvtNot(); + + LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_BLE_EVENT_CHANNEL); +} + +void HW_IPCC_BLE_SendAclData() { + LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_HCI_ACL_DATA_CHANNEL); + LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_HCI_ACL_DATA_CHANNEL); +} + +static void HW_IPCC_BLE_AclDataEvtHandler() { + LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_HCI_ACL_DATA_CHANNEL); + + HW_IPCC_BLE_AclDataAckNot(); +} + +void HW_IPCC_SYS_Init() { + LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_SYSTEM_EVENT_CHANNEL); +} + +void HW_IPCC_SYS_SendCmd() { + LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_SYSTEM_CMD_RSP_CHANNEL); + + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(33000000); + + while(LL_C1_IPCC_IsActiveFlag_CHx(IPCC, HW_IPCC_SYSTEM_CMD_RSP_CHANNEL)) { + furi_check(!furi_hal_cortex_timer_is_expired(timer), "HW_IPCC_SYS_SendCmd timeout"); + } + + HW_IPCC_SYS_CmdEvtHandler(); +} + +static void HW_IPCC_SYS_CmdEvtHandler() { + LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_SYSTEM_CMD_RSP_CHANNEL); + + HW_IPCC_SYS_CmdEvtNot(); +} + +static void HW_IPCC_SYS_EvtHandler() { + HW_IPCC_SYS_EvtNot(); + + LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_SYSTEM_EVENT_CHANNEL); +} + +void HW_IPCC_MM_SendFreeBuf(void (*cb)()) { + if(LL_C1_IPCC_IsActiveFlag_CHx(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL)) { + FreeBufCb = cb; + LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL); + } else { + cb(); + + LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL); + } +} + +static void HW_IPCC_MM_FreeBufHandler() { + LL_C1_IPCC_DisableTransmitChannel(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL); + + FreeBufCb(); + + LL_C1_IPCC_SetFlag_CHx(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL); +} + +void HW_IPCC_TRACES_Init() { + LL_C1_IPCC_EnableReceiveChannel(IPCC, HW_IPCC_TRACES_CHANNEL); +} + +static void HW_IPCC_TRACES_EvtHandler() { + HW_IPCC_TRACES_EvtNot(); + + LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_TRACES_CHANNEL); +} diff --git a/targets/f7/ble_glue/osal.h b/targets/f7/ble_glue/osal.h new file mode 100644 index 00000000000..0cde061794f --- /dev/null +++ b/targets/f7/ble_glue/osal.h @@ -0,0 +1,40 @@ +#pragma once + +/** + * This function copies size number of bytes from a + * memory location pointed by src to a destination + * memory location pointed by dest + * + * @param[in] dest Destination address + * @param[in] src Source address + * @param[in] size size in the bytes + * + * @return Address of the destination + */ + +extern void* Osal_MemCpy(void* dest, const void* src, unsigned int size); + +/** + * This function sets first number of bytes, specified + * by size, to the destination memory pointed by ptr + * to the specified value + * + * @param[in] ptr Destination address + * @param[in] value Value to be set + * @param[in] size Size in the bytes + * + * @return Address of the destination + */ + +extern void* Osal_MemSet(void* ptr, int value, unsigned int size); + +/** + * This function compares n bytes of two regions of memory + * + * @param[in] s1 First buffer to compare. + * @param[in] s2 Second buffer to compare. + * @param[in] size Number of bytes to compare. + * + * @return 0 if the two buffers are equal, 1 otherwise + */ +extern int Osal_MemCmp(const void* s1, const void* s2, unsigned int size); diff --git a/targets/f7/ble_glue/services/battery_service.c b/targets/f7/ble_glue/services/battery_service.c new file mode 100644 index 00000000000..63f736b3b7a --- /dev/null +++ b/targets/f7/ble_glue/services/battery_service.c @@ -0,0 +1,150 @@ +#include "battery_service.h" +#include "app_common.h" +#include "gatt_char.h" + +#include + +#include +#include + +#define TAG "BtBatterySvc" + +enum { + // Common states + BatterySvcPowerStateUnknown = 0b00, + BatterySvcPowerStateUnsupported = 0b01, + // Level states + BatterySvcPowerStateGoodLevel = 0b10, + BatterySvcPowerStateCriticallyLowLevel = 0b11, + // Charging states + BatterySvcPowerStateNotCharging = 0b10, + BatterySvcPowerStateCharging = 0b11, + // Discharging states + BatterySvcPowerStateNotDischarging = 0b10, + BatterySvcPowerStateDischarging = 0b11, + // Battery states + BatterySvcPowerStateBatteryNotPresent = 0b10, + BatterySvcPowerStateBatteryPresent = 0b11, +}; + +typedef struct { + uint8_t present : 2; + uint8_t discharging : 2; + uint8_t charging : 2; + uint8_t level : 2; +} BattrySvcPowerState; + +_Static_assert(sizeof(BattrySvcPowerState) == 1, "Incorrect structure size"); + +#define BATTERY_POWER_STATE (0x2A1A) + +static const uint16_t service_uuid = BATTERY_SERVICE_UUID; + +typedef enum { + BatterySvcGattCharacteristicBatteryLevel = 0, + BatterySvcGattCharacteristicPowerState, + BatterySvcGattCharacteristicCount, +} BatterySvcGattCharacteristicId; + +static const FlipperGattCharacteristicParams battery_svc_chars[BatterySvcGattCharacteristicCount] = + {[BatterySvcGattCharacteristicBatteryLevel] = + {.name = "Battery Level", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = 1, + .uuid.Char_UUID_16 = BATTERY_LEVEL_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [BatterySvcGattCharacteristicPowerState] = { + .name = "Power State", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = 1, + .uuid.Char_UUID_16 = BATTERY_POWER_STATE, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}}; + +typedef struct { + uint16_t svc_handle; + FlipperGattCharacteristicInstance chars[BatterySvcGattCharacteristicCount]; +} BatterySvc; + +static BatterySvc* battery_svc = NULL; + +void battery_svc_start() { + battery_svc = malloc(sizeof(BatterySvc)); + tBleStatus status; + + // Add Battery service + status = aci_gatt_add_service( + UUID_TYPE_16, (Service_UUID_t*)&service_uuid, PRIMARY_SERVICE, 8, &battery_svc->svc_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add Battery service: %d", status); + } + for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_init( + battery_svc->svc_handle, &battery_svc_chars[i], &battery_svc->chars[i]); + } + + battery_svc_update_power_state(); +} + +void battery_svc_stop() { + tBleStatus status; + if(battery_svc) { + for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_delete(battery_svc->svc_handle, &battery_svc->chars[i]); + } + // Delete Battery service + status = aci_gatt_del_service(battery_svc->svc_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete Battery service: %d", status); + } + free(battery_svc); + battery_svc = NULL; + } +} + +bool battery_svc_is_started() { + return battery_svc != NULL; +} + +bool battery_svc_update_level(uint8_t battery_charge) { + // Check if service was started + if(battery_svc == NULL) { + return false; + } + // Update battery level characteristic + return flipper_gatt_characteristic_update( + battery_svc->svc_handle, + &battery_svc->chars[BatterySvcGattCharacteristicBatteryLevel], + &battery_charge); +} + +bool battery_svc_update_power_state() { + // Check if service was started + if(battery_svc == NULL) { + return false; + } + // Update power state characteristic + BattrySvcPowerState power_state = { + .level = BatterySvcPowerStateUnsupported, + .present = BatterySvcPowerStateBatteryPresent, + }; + if(furi_hal_power_is_charging()) { + power_state.charging = BatterySvcPowerStateCharging; + power_state.discharging = BatterySvcPowerStateNotDischarging; + } else { + power_state.charging = BatterySvcPowerStateNotCharging; + power_state.discharging = BatterySvcPowerStateDischarging; + } + + return flipper_gatt_characteristic_update( + battery_svc->svc_handle, + &battery_svc->chars[BatterySvcGattCharacteristicPowerState], + &power_state); +} diff --git a/firmware/targets/f7/ble_glue/battery_service.h b/targets/f7/ble_glue/services/battery_service.h similarity index 100% rename from firmware/targets/f7/ble_glue/battery_service.h rename to targets/f7/ble_glue/services/battery_service.h diff --git a/targets/f7/ble_glue/services/dev_info_service.c b/targets/f7/ble_glue/services/dev_info_service.c new file mode 100644 index 00000000000..59af23e5c24 --- /dev/null +++ b/targets/f7/ble_glue/services/dev_info_service.c @@ -0,0 +1,178 @@ +#include "dev_info_service.h" +#include "app_common.h" +#include "gatt_char.h" +#include + +#include +#include +#include + +#include "dev_info_service_uuid.inc" + +#define TAG "BtDevInfoSvc" + +typedef enum { + DevInfoSvcGattCharacteristicMfgName = 0, + DevInfoSvcGattCharacteristicSerial, + DevInfoSvcGattCharacteristicFirmwareRev, + DevInfoSvcGattCharacteristicSoftwareRev, + DevInfoSvcGattCharacteristicRpcVersion, + DevInfoSvcGattCharacteristicCount, +} DevInfoSvcGattCharacteristicId; + +#define DEVICE_INFO_HARDWARE_REV_SIZE 4 +typedef struct { + uint16_t service_handle; + FlipperGattCharacteristicInstance characteristics[DevInfoSvcGattCharacteristicCount]; + FuriString* version_string; + char hardware_revision[DEVICE_INFO_HARDWARE_REV_SIZE]; +} DevInfoSvc; + +static DevInfoSvc* dev_info_svc = NULL; + +static const char dev_info_man_name[] = "Flipper Devices Inc."; +static const char dev_info_serial_num[] = "1.0"; +static const char dev_info_rpc_version[] = TOSTRING(PROTOBUF_MAJOR_VERSION.PROTOBUF_MINOR_VERSION); + +static bool dev_info_char_firmware_rev_callback( + const void* context, + const uint8_t** data, + uint16_t* data_len) { + const DevInfoSvc* dev_info_svc = *(DevInfoSvc**)context; + *data_len = strlen(dev_info_svc->hardware_revision); + if(data) { + *data = (const uint8_t*)&dev_info_svc->hardware_revision; + } + return false; +} + +static bool dev_info_char_software_rev_callback( + const void* context, + const uint8_t** data, + uint16_t* data_len) { + const DevInfoSvc* dev_info_svc = *(DevInfoSvc**)context; + *data_len = furi_string_size(dev_info_svc->version_string); + if(data) { + *data = (const uint8_t*)furi_string_get_cstr(dev_info_svc->version_string); + } + return false; +} + +static const FlipperGattCharacteristicParams dev_info_svc_chars[DevInfoSvcGattCharacteristicCount] = + {[DevInfoSvcGattCharacteristicMfgName] = + {.name = "Manufacturer Name", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = sizeof(dev_info_man_name) - 1, + .data.fixed.ptr = (const uint8_t*)&dev_info_man_name, + .uuid.Char_UUID_16 = MANUFACTURER_NAME_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [DevInfoSvcGattCharacteristicSerial] = + {.name = "Serial Number", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = sizeof(dev_info_serial_num) - 1, + .data.fixed.ptr = (const uint8_t*)&dev_info_serial_num, + .uuid.Char_UUID_16 = SERIAL_NUMBER_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [DevInfoSvcGattCharacteristicFirmwareRev] = + {.name = "Firmware Revision", + .data_prop_type = FlipperGattCharacteristicDataCallback, + .data.callback.context = &dev_info_svc, + .data.callback.fn = dev_info_char_firmware_rev_callback, + .uuid.Char_UUID_16 = FIRMWARE_REVISION_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [DevInfoSvcGattCharacteristicSoftwareRev] = + {.name = "Software Revision", + .data_prop_type = FlipperGattCharacteristicDataCallback, + .data.callback.context = &dev_info_svc, + .data.callback.fn = dev_info_char_software_rev_callback, + .uuid.Char_UUID_16 = SOFTWARE_REVISION_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [DevInfoSvcGattCharacteristicRpcVersion] = { + .name = "RPC Version", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = sizeof(dev_info_rpc_version) - 1, + .data.fixed.ptr = (const uint8_t*)&dev_info_rpc_version, + .uuid.Char_UUID_128 = DEV_INVO_RPC_VERSION_UID, + .uuid_type = UUID_TYPE_128, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}}; + +void dev_info_svc_start() { + dev_info_svc = malloc(sizeof(DevInfoSvc)); + dev_info_svc->version_string = furi_string_alloc_printf( + "%s %s %s %s", + version_get_githash(NULL), + version_get_gitbranch(NULL), + version_get_gitbranchnum(NULL), + version_get_builddate(NULL)); + snprintf( + dev_info_svc->hardware_revision, + sizeof(dev_info_svc->hardware_revision), + "%d", + version_get_target(NULL)); + tBleStatus status; + + // Add Device Information Service + uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; + status = aci_gatt_add_service( + UUID_TYPE_16, + (Service_UUID_t*)&uuid, + PRIMARY_SERVICE, + 1 + 2 * DevInfoSvcGattCharacteristicCount, + &dev_info_svc->service_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add Device Information Service: %d", status); + } + + for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_init( + dev_info_svc->service_handle, + &dev_info_svc_chars[i], + &dev_info_svc->characteristics[i]); + flipper_gatt_characteristic_update( + dev_info_svc->service_handle, &dev_info_svc->characteristics[i], NULL); + } +} + +void dev_info_svc_stop() { + tBleStatus status; + if(dev_info_svc) { + // Delete service characteristics + for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_delete( + dev_info_svc->service_handle, &dev_info_svc->characteristics[i]); + } + + // Delete service + status = aci_gatt_del_service(dev_info_svc->service_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete device info service: %d", status); + } + + furi_string_free(dev_info_svc->version_string); + free(dev_info_svc); + dev_info_svc = NULL; + } +} + +bool dev_info_svc_is_started() { + return dev_info_svc != NULL; +} diff --git a/firmware/targets/f7/ble_glue/dev_info_service.h b/targets/f7/ble_glue/services/dev_info_service.h similarity index 100% rename from firmware/targets/f7/ble_glue/dev_info_service.h rename to targets/f7/ble_glue/services/dev_info_service.h diff --git a/targets/f7/ble_glue/services/dev_info_service_uuid.inc b/targets/f7/ble_glue/services/dev_info_service_uuid.inc new file mode 100644 index 00000000000..ad520f62e5e --- /dev/null +++ b/targets/f7/ble_glue/services/dev_info_service_uuid.inc @@ -0,0 +1,3 @@ +#define DEV_INVO_RPC_VERSION_UID \ + { 0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03 } + diff --git a/targets/f7/ble_glue/services/gatt_char.c b/targets/f7/ble_glue/services/gatt_char.c new file mode 100644 index 00000000000..f6e27f53e68 --- /dev/null +++ b/targets/f7/ble_glue/services/gatt_char.c @@ -0,0 +1,122 @@ +#include "gatt_char.h" + +#include + +#define TAG "GattChar" + +#define GATT_MIN_READ_KEY_SIZE (10) + +void flipper_gatt_characteristic_init( + uint16_t svc_handle, + const FlipperGattCharacteristicParams* char_descriptor, + FlipperGattCharacteristicInstance* char_instance) { + furi_assert(char_descriptor); + furi_assert(char_instance); + + // Copy the descriptor to the instance, since it may point to stack memory + char_instance->characteristic = malloc(sizeof(FlipperGattCharacteristicParams)); + memcpy( + (void*)char_instance->characteristic, + char_descriptor, + sizeof(FlipperGattCharacteristicParams)); + + uint16_t char_data_size = 0; + if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) { + char_data_size = char_descriptor->data.fixed.length; + } else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) { + char_descriptor->data.callback.fn( + char_descriptor->data.callback.context, NULL, &char_data_size); + } + + tBleStatus status = aci_gatt_add_char( + svc_handle, + char_descriptor->uuid_type, + &char_descriptor->uuid, + char_data_size, + char_descriptor->char_properties, + char_descriptor->security_permissions, + char_descriptor->gatt_evt_mask, + GATT_MIN_READ_KEY_SIZE, + char_descriptor->is_variable, + &char_instance->handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add %s char: %d", char_descriptor->name, status); + } + + char_instance->descriptor_handle = 0; + if((status == 0) && char_descriptor->descriptor_params) { + uint8_t const* char_data = NULL; + const FlipperGattCharacteristicDescriptorParams* char_data_descriptor = + char_descriptor->descriptor_params; + bool release_data = char_data_descriptor->data_callback.fn( + char_data_descriptor->data_callback.context, &char_data, &char_data_size); + + status = aci_gatt_add_char_desc( + svc_handle, + char_instance->handle, + char_data_descriptor->uuid_type, + &char_data_descriptor->uuid, + char_data_descriptor->max_length, + char_data_size, + char_data, + char_data_descriptor->security_permissions, + char_data_descriptor->access_permissions, + char_data_descriptor->gatt_evt_mask, + GATT_MIN_READ_KEY_SIZE, + char_data_descriptor->is_variable, + &char_instance->descriptor_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add %s char descriptor: %d", char_descriptor->name, status); + } + if(release_data) { + free((void*)char_data); + } + } +} + +void flipper_gatt_characteristic_delete( + uint16_t svc_handle, + FlipperGattCharacteristicInstance* char_instance) { + tBleStatus status = aci_gatt_del_char(svc_handle, char_instance->handle); + if(status) { + FURI_LOG_E( + TAG, "Failed to delete %s char: %d", char_instance->characteristic->name, status); + } + free((void*)char_instance->characteristic); +} + +bool flipper_gatt_characteristic_update( + uint16_t svc_handle, + FlipperGattCharacteristicInstance* char_instance, + const void* source) { + furi_assert(char_instance); + const FlipperGattCharacteristicParams* char_descriptor = char_instance->characteristic; + FURI_LOG_D(TAG, "Updating %s char", char_descriptor->name); + + const uint8_t* char_data = NULL; + uint16_t char_data_size = 0; + bool release_data = false; + if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) { + char_data = char_descriptor->data.fixed.ptr; + if(source) { + char_data = (uint8_t*)source; + } + char_data_size = char_descriptor->data.fixed.length; + } else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) { + const void* context = char_descriptor->data.callback.context; + if(source) { + context = source; + } + release_data = char_descriptor->data.callback.fn(context, &char_data, &char_data_size); + } + + tBleStatus result = aci_gatt_update_char_value( + svc_handle, char_instance->handle, 0, char_data_size, char_data); + if(result) { + FURI_LOG_E(TAG, "Failed updating %s characteristic: %d", char_descriptor->name, result); + } + if(release_data) { + free((void*)char_data); + } + return result != BLE_STATUS_SUCCESS; +} \ No newline at end of file diff --git a/targets/f7/ble_glue/services/gatt_char.h b/targets/f7/ble_glue/services/gatt_char.h new file mode 100644 index 00000000000..959ab67a49f --- /dev/null +++ b/targets/f7/ble_glue/services/gatt_char.h @@ -0,0 +1,96 @@ +#pragma once + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Callback signature for getting characteristic data +// Is called when characteristic is created to get max data length. Data ptr is NULL in this case +// The result is passed to aci_gatt_add_char as "Char_Value_Length" +// For updates, called with a context - see flipper_gatt_characteristic_update +// Returns true if *data ownership is transferred to the caller and will be freed +typedef bool (*cbFlipperGattCharacteristicData)( + const void* context, + const uint8_t** data, + uint16_t* data_len); + +typedef enum { + FlipperGattCharacteristicDataFixed, + FlipperGattCharacteristicDataCallback, +} FlipperGattCharacteristicDataType; + +typedef struct { + Char_Desc_Uuid_t uuid; + struct { + cbFlipperGattCharacteristicData fn; + const void* context; + } data_callback; + uint8_t uuid_type; + uint8_t max_length; + uint8_t security_permissions; + uint8_t access_permissions; + uint8_t gatt_evt_mask; + uint8_t is_variable; +} FlipperGattCharacteristicDescriptorParams; + +typedef struct { + const char* name; + FlipperGattCharacteristicDescriptorParams* descriptor_params; + union { + struct { + const uint8_t* ptr; + uint16_t length; + } fixed; + struct { + cbFlipperGattCharacteristicData fn; + const void* context; + } callback; + } data; + Char_UUID_t uuid; + // Some packed bitfields to save space + FlipperGattCharacteristicDataType data_prop_type : 2; + uint8_t is_variable : 2; + uint8_t uuid_type : 2; + uint8_t char_properties; + uint8_t security_permissions; + uint8_t gatt_evt_mask; +} FlipperGattCharacteristicParams; + +_Static_assert( + sizeof(FlipperGattCharacteristicParams) == 36, + "FlipperGattCharacteristicParams size must be 36 bytes"); + +typedef struct { + const FlipperGattCharacteristicParams* characteristic; + uint16_t handle; + uint16_t descriptor_handle; +} FlipperGattCharacteristicInstance; + +// Initialize a characteristic instance; copies the characteristic descriptor into the instance +void flipper_gatt_characteristic_init( + uint16_t svc_handle, + const FlipperGattCharacteristicParams* char_descriptor, + FlipperGattCharacteristicInstance* char_instance); + +// Delete a characteristic instance; frees the copied characteristic descriptor from the instance +void flipper_gatt_characteristic_delete( + uint16_t svc_handle, + FlipperGattCharacteristicInstance* char_instance); + +// Update a characteristic instance; if source==NULL, uses the data from the characteristic +// - For fixed data, fixed.ptr is used as the source if source==NULL +// - For callback-based data, collback.context is passed as the context if source==NULL +bool flipper_gatt_characteristic_update( + uint16_t svc_handle, + FlipperGattCharacteristicInstance* char_instance, + const void* source); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/targets/f7/ble_glue/services/hid_service.c b/targets/f7/ble_glue/services/hid_service.c new file mode 100644 index 00000000000..cf2aca24e3f --- /dev/null +++ b/targets/f7/ble_glue/services/hid_service.c @@ -0,0 +1,302 @@ +#include "hid_service.h" +#include "app_common.h" +#include +#include "gatt_char.h" + +#include + +#define TAG "BtHid" + +typedef enum { + HidSvcGattCharacteristicProtocolMode = 0, + HidSvcGattCharacteristicReportMap, + HidSvcGattCharacteristicInfo, + HidSvcGattCharacteristicCtrlPoint, + HidSvcGattCharacteristicCount, +} HidSvcGattCharacteristicId; + +typedef struct { + uint8_t report_idx; + uint8_t report_type; +} HidSvcReportId; + +static_assert(sizeof(HidSvcReportId) == sizeof(uint16_t), "HidSvcReportId must be 2 bytes"); + +static const Service_UUID_t hid_svc_uuid = { + .Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID, +}; + +static bool + hid_svc_char_desc_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { + const HidSvcReportId* report_id = context; + *data_len = sizeof(HidSvcReportId); + if(data) { + *data = (const uint8_t*)report_id; + } + return false; +} + +typedef struct { + const void* data_ptr; + uint16_t data_len; +} HidSvcDataWrapper; + +static bool + hid_svc_report_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { + const HidSvcDataWrapper* report_data = context; + if(data) { + *data = report_data->data_ptr; + *data_len = report_data->data_len; + } else { + *data_len = HID_SVC_REPORT_MAP_MAX_LEN; + } + return false; +} + +static const FlipperGattCharacteristicParams hid_svc_chars[HidSvcGattCharacteristicCount] = { + [HidSvcGattCharacteristicProtocolMode] = + {.name = "Protocol Mode", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = 1, + .uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [HidSvcGattCharacteristicReportMap] = + {.name = "Report Map", + .data_prop_type = FlipperGattCharacteristicDataCallback, + .data.callback.fn = hid_svc_report_data_callback, + .data.callback.context = NULL, + .uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_VARIABLE}, + [HidSvcGattCharacteristicInfo] = + {.name = "HID Information", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = HID_SVC_INFO_LEN, + .data.fixed.ptr = NULL, + .uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [HidSvcGattCharacteristicCtrlPoint] = + {.name = "HID Control Point", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = HID_SVC_CONTROL_POINT_LEN, + .uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, +}; + +static const FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_template = { + .uuid_type = UUID_TYPE_16, + .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, + .max_length = HID_SVC_REPORT_REF_LEN, + .data_callback.fn = hid_svc_char_desc_data_callback, + .security_permissions = ATTR_PERMISSION_NONE, + .access_permissions = ATTR_ACCESS_READ_WRITE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT, +}; + +static const FlipperGattCharacteristicParams hid_svc_report_template = { + .name = "Report", + .data_prop_type = FlipperGattCharacteristicDataCallback, + .data.callback.fn = hid_svc_report_data_callback, + .data.callback.context = NULL, + .uuid.Char_UUID_16 = REPORT_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_VARIABLE, +}; + +typedef struct { + uint16_t svc_handle; + FlipperGattCharacteristicInstance chars[HidSvcGattCharacteristicCount]; + FlipperGattCharacteristicInstance input_report_chars[HID_SVC_INPUT_REPORT_COUNT]; + FlipperGattCharacteristicInstance output_report_chars[HID_SVC_OUTPUT_REPORT_COUNT]; + FlipperGattCharacteristicInstance feature_report_chars[HID_SVC_FEATURE_REPORT_COUNT]; +} HIDSvc; + +static HIDSvc* hid_svc = NULL; + +static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { + SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; + hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); + evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; + // aci_gatt_attribute_modified_event_rp0* attribute_modified; + if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { + if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { + // Process modification events + ret = SVCCTL_EvtAckFlowEnable; + } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { + // Process notification confirmation + ret = SVCCTL_EvtAckFlowEnable; + } + } + return ret; +} + +void hid_svc_start() { + tBleStatus status; + hid_svc = malloc(sizeof(HIDSvc)); + + // Register event handler + SVCCTL_RegisterSvcHandler(hid_svc_event_handler); + /** + * Add Human Interface Device Service + */ + status = aci_gatt_add_service( + UUID_TYPE_16, + &hid_svc_uuid, + PRIMARY_SERVICE, + 2 + /* protocol mode */ + (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + + (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + + 2, /* Service + Report Map + HID Information + HID Control Point */ + &hid_svc->svc_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add HID service: %d", status); + } + + // Maintain previously defined characteristic order + flipper_gatt_characteristic_init( + hid_svc->svc_handle, + &hid_svc_chars[HidSvcGattCharacteristicProtocolMode], + &hid_svc->chars[HidSvcGattCharacteristicProtocolMode]); + + uint8_t protocol_mode = 1; + flipper_gatt_characteristic_update( + hid_svc->svc_handle, + &hid_svc->chars[HidSvcGattCharacteristicProtocolMode], + &protocol_mode); + + // reports + FlipperGattCharacteristicDescriptorParams hid_svc_char_descr; + FlipperGattCharacteristicParams report_char; + HidSvcReportId report_id; + + memcpy(&hid_svc_char_descr, &hid_svc_char_descr_template, sizeof(hid_svc_char_descr)); + memcpy(&report_char, &hid_svc_report_template, sizeof(report_char)); + + hid_svc_char_descr.data_callback.context = &report_id; + report_char.descriptor_params = &hid_svc_char_descr; + + typedef struct { + uint8_t report_type; + uint8_t report_count; + FlipperGattCharacteristicInstance* chars; + } HidSvcReportCharProps; + + HidSvcReportCharProps hid_report_chars[] = { + {0x01, HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, + {0x02, HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, + {0x03, HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, + }; + + for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); + report_type_idx++) { + report_id.report_type = hid_report_chars[report_type_idx].report_type; + for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; + report_idx++) { + report_id.report_idx = report_idx + 1; + flipper_gatt_characteristic_init( + hid_svc->svc_handle, + &report_char, + &hid_report_chars[report_type_idx].chars[report_idx]); + } + } + + // Setup remaining characteristics + for(size_t i = HidSvcGattCharacteristicReportMap; i < HidSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_init( + hid_svc->svc_handle, &hid_svc_chars[i], &hid_svc->chars[i]); + } +} + +bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { + furi_assert(data); + furi_assert(hid_svc); + + HidSvcDataWrapper report_data = { + .data_ptr = data, + .data_len = len, + }; + return flipper_gatt_characteristic_update( + hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicReportMap], &report_data); +} + +bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) { + furi_assert(data); + furi_assert(hid_svc); + furi_assert(input_report_num < HID_SVC_INPUT_REPORT_COUNT); + + HidSvcDataWrapper report_data = { + .data_ptr = data, + .data_len = len, + }; + return flipper_gatt_characteristic_update( + hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data); +} + +bool hid_svc_update_info(uint8_t* data) { + furi_assert(data); + furi_assert(hid_svc); + + return flipper_gatt_characteristic_update( + hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicInfo], &data); +} + +bool hid_svc_is_started() { + return hid_svc != NULL; +} + +void hid_svc_stop() { + tBleStatus status; + if(hid_svc) { + // Delete characteristics + for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_delete(hid_svc->svc_handle, &hid_svc->chars[i]); + } + + typedef struct { + uint8_t report_count; + FlipperGattCharacteristicInstance* chars; + } HidSvcReportCharProps; + + HidSvcReportCharProps hid_report_chars[] = { + {HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, + {HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, + {HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, + }; + + for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); + report_type_idx++) { + for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; + report_idx++) { + flipper_gatt_characteristic_delete( + hid_svc->svc_handle, &hid_report_chars[report_type_idx].chars[report_idx]); + } + } + + // Delete service + status = aci_gatt_del_service(hid_svc->svc_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete HID service: %d", status); + } + free(hid_svc); + hid_svc = NULL; + } +} diff --git a/firmware/targets/f7/ble_glue/hid_service.h b/targets/f7/ble_glue/services/hid_service.h similarity index 87% rename from firmware/targets/f7/ble_glue/hid_service.h rename to targets/f7/ble_glue/services/hid_service.h index 723460d496a..211adcd6c44 100644 --- a/firmware/targets/f7/ble_glue/hid_service.h +++ b/targets/f7/ble_glue/services/hid_service.h @@ -25,4 +25,5 @@ bool hid_svc_update_report_map(const uint8_t* data, uint16_t len); bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len); -bool hid_svc_update_info(uint8_t* data, uint16_t len); +// Expects data to be of length HID_SVC_INFO_LEN (4 bytes) +bool hid_svc_update_info(uint8_t* data); diff --git a/targets/f7/ble_glue/services/serial_service.c b/targets/f7/ble_glue/services/serial_service.c new file mode 100644 index 00000000000..0db25b3d3af --- /dev/null +++ b/targets/f7/ble_glue/services/serial_service.c @@ -0,0 +1,262 @@ +#include "serial_service.h" +#include "app_common.h" +#include +#include "gatt_char.h" + +#include + +#include "serial_service_uuid.inc" + +#define TAG "BtSerialSvc" + +typedef enum { + SerialSvcGattCharacteristicRx = 0, + SerialSvcGattCharacteristicTx, + SerialSvcGattCharacteristicFlowCtrl, + SerialSvcGattCharacteristicStatus, + SerialSvcGattCharacteristicCount, +} SerialSvcGattCharacteristicId; + +static const FlipperGattCharacteristicParams serial_svc_chars[SerialSvcGattCharacteristicCount] = { + [SerialSvcGattCharacteristicRx] = + {.name = "RX", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = SERIAL_SVC_DATA_LEN_MAX, + .uuid.Char_UUID_128 = SERIAL_SVC_RX_CHAR_UUID, + .uuid_type = UUID_TYPE_128, + .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, + .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, + .is_variable = CHAR_VALUE_LEN_VARIABLE}, + [SerialSvcGattCharacteristicTx] = + {.name = "TX", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = SERIAL_SVC_DATA_LEN_MAX, + .uuid.Char_UUID_128 = SERIAL_SVC_TX_CHAR_UUID, + .uuid_type = UUID_TYPE_128, + .char_properties = CHAR_PROP_READ | CHAR_PROP_INDICATE, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_VARIABLE}, + [SerialSvcGattCharacteristicFlowCtrl] = + {.name = "Flow control", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = sizeof(uint32_t), + .uuid.Char_UUID_128 = SERIAL_SVC_FLOW_CONTROL_UUID, + .uuid_type = UUID_TYPE_128, + .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [SerialSvcGattCharacteristicStatus] = { + .name = "RPC status", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = sizeof(SerialServiceRpcStatus), + .uuid.Char_UUID_128 = SERIAL_SVC_RPC_STATUS_UUID, + .uuid_type = UUID_TYPE_128, + .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, + .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, + .is_variable = CHAR_VALUE_LEN_CONSTANT}}; + +typedef struct { + uint16_t svc_handle; + FlipperGattCharacteristicInstance chars[SerialSvcGattCharacteristicCount]; + FuriMutex* buff_size_mtx; + uint32_t buff_size; + uint16_t bytes_ready_to_receive; + SerialServiceEventCallback callback; + void* context; +} SerialSvc; + +static SerialSvc* serial_svc = NULL; + +static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { + SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; + hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); + evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; + aci_gatt_attribute_modified_event_rp0* attribute_modified; + if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { + if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { + attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data; + if(attribute_modified->Attr_Handle == + serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 2) { + // Descriptor handle + ret = SVCCTL_EvtAckFlowEnable; + FURI_LOG_D(TAG, "RX descriptor event"); + } else if( + attribute_modified->Attr_Handle == + serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 1) { + FURI_LOG_D(TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); + if(serial_svc->callback) { + furi_check( + furi_mutex_acquire(serial_svc->buff_size_mtx, FuriWaitForever) == + FuriStatusOk); + if(attribute_modified->Attr_Data_Length > serial_svc->bytes_ready_to_receive) { + FURI_LOG_W( + TAG, + "Received %d, while was ready to receive %d bytes. Can lead to buffer overflow!", + attribute_modified->Attr_Data_Length, + serial_svc->bytes_ready_to_receive); + } + serial_svc->bytes_ready_to_receive -= MIN( + serial_svc->bytes_ready_to_receive, attribute_modified->Attr_Data_Length); + SerialServiceEvent event = { + .event = SerialServiceEventTypeDataReceived, + .data = { + .buffer = attribute_modified->Attr_Data, + .size = attribute_modified->Attr_Data_Length, + }}; + uint32_t buff_free_size = serial_svc->callback(event, serial_svc->context); + FURI_LOG_D(TAG, "Available buff size: %ld", buff_free_size); + furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); + } + ret = SVCCTL_EvtAckFlowEnable; + } else if( + attribute_modified->Attr_Handle == + serial_svc->chars[SerialSvcGattCharacteristicStatus].handle + 1) { + SerialServiceRpcStatus* rpc_status = + (SerialServiceRpcStatus*)attribute_modified->Attr_Data; + if(*rpc_status == SerialServiceRpcStatusNotActive) { + if(serial_svc->callback) { + SerialServiceEvent event = { + .event = SerialServiceEventTypesBleResetRequest, + }; + serial_svc->callback(event, serial_svc->context); + } + } + } + } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { + FURI_LOG_T(TAG, "Ack received"); + if(serial_svc->callback) { + SerialServiceEvent event = { + .event = SerialServiceEventTypeDataSent, + }; + serial_svc->callback(event, serial_svc->context); + } + ret = SVCCTL_EvtAckFlowEnable; + } + } + return ret; +} + +static void serial_svc_update_rpc_char(SerialServiceRpcStatus status) { + flipper_gatt_characteristic_update( + serial_svc->svc_handle, &serial_svc->chars[SerialSvcGattCharacteristicStatus], &status); +} + +void serial_svc_start() { + UNUSED(serial_svc_chars); + tBleStatus status; + serial_svc = malloc(sizeof(SerialSvc)); + // Register event handler + SVCCTL_RegisterSvcHandler(serial_svc_event_handler); + + // Add service + status = aci_gatt_add_service( + UUID_TYPE_128, &service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add Serial service: %d", status); + } + + // Add characteristics + for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_init( + serial_svc->svc_handle, &serial_svc_chars[i], &serial_svc->chars[i]); + } + + serial_svc_update_rpc_char(SerialServiceRpcStatusNotActive); + // Allocate buffer size mutex + serial_svc->buff_size_mtx = furi_mutex_alloc(FuriMutexTypeNormal); +} + +void serial_svc_set_callbacks( + uint16_t buff_size, + SerialServiceEventCallback callback, + void* context) { + furi_assert(serial_svc); + serial_svc->callback = callback; + serial_svc->context = context; + serial_svc->buff_size = buff_size; + serial_svc->bytes_ready_to_receive = buff_size; + + uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); + flipper_gatt_characteristic_update( + serial_svc->svc_handle, + &serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], + &buff_size_reversed); +} + +void serial_svc_notify_buffer_is_empty() { + furi_assert(serial_svc); + furi_assert(serial_svc->buff_size_mtx); + + furi_check(furi_mutex_acquire(serial_svc->buff_size_mtx, FuriWaitForever) == FuriStatusOk); + if(serial_svc->bytes_ready_to_receive == 0) { + FURI_LOG_D(TAG, "Buffer is empty. Notifying client"); + serial_svc->bytes_ready_to_receive = serial_svc->buff_size; + + uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); + flipper_gatt_characteristic_update( + serial_svc->svc_handle, + &serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], + &buff_size_reversed); + } + furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); +} + +void serial_svc_stop() { + tBleStatus status; + if(serial_svc) { + for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_delete(serial_svc->svc_handle, &serial_svc->chars[i]); + } + // Delete service + status = aci_gatt_del_service(serial_svc->svc_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete Serial service: %d", status); + } + // Delete buffer size mutex + furi_mutex_free(serial_svc->buff_size_mtx); + free(serial_svc); + serial_svc = NULL; + } +} + +bool serial_svc_is_started() { + return serial_svc != NULL; +} + +bool serial_svc_update_tx(uint8_t* data, uint16_t data_len) { + if(data_len > SERIAL_SVC_DATA_LEN_MAX) { + return false; + } + + for(uint16_t remained = data_len; remained > 0;) { + uint8_t value_len = MIN(SERIAL_SVC_CHAR_VALUE_LEN_MAX, remained); + uint16_t value_offset = data_len - remained; + remained -= value_len; + + tBleStatus result = aci_gatt_update_char_value_ext( + 0, + serial_svc->svc_handle, + serial_svc->chars[SerialSvcGattCharacteristicTx].handle, + remained ? 0x00 : 0x02, + data_len, + value_offset, + value_len, + data + value_offset); + + if(result) { + FURI_LOG_E(TAG, "Failed updating TX characteristic: %d", result); + return false; + } + } + + return true; +} + +void serial_svc_set_rpc_status(SerialServiceRpcStatus status) { + furi_assert(serial_svc); + serial_svc_update_rpc_char(status); +} diff --git a/firmware/targets/f7/ble_glue/serial_service.h b/targets/f7/ble_glue/services/serial_service.h similarity index 79% rename from firmware/targets/f7/ble_glue/serial_service.h rename to targets/f7/ble_glue/services/serial_service.h index a1e5bc1cc57..7d38066f47f 100644 --- a/firmware/targets/f7/ble_glue/serial_service.h +++ b/targets/f7/ble_glue/services/serial_service.h @@ -10,9 +10,15 @@ extern "C" { #endif +typedef enum { + SerialServiceRpcStatusNotActive = 0UL, + SerialServiceRpcStatusActive = 1UL, +} SerialServiceRpcStatus; + typedef enum { SerialServiceEventTypeDataReceived, SerialServiceEventTypeDataSent, + SerialServiceEventTypesBleResetRequest, } SerialServiceEventType; typedef struct { @@ -34,6 +40,8 @@ void serial_svc_set_callbacks( SerialServiceEventCallback callback, void* context); +void serial_svc_set_rpc_status(SerialServiceRpcStatus status); + void serial_svc_notify_buffer_is_empty(); void serial_svc_stop(); diff --git a/targets/f7/ble_glue/services/serial_service_uuid.inc b/targets/f7/ble_glue/services/serial_service_uuid.inc new file mode 100644 index 00000000000..a297d9ad604 --- /dev/null +++ b/targets/f7/ble_glue/services/serial_service_uuid.inc @@ -0,0 +1,12 @@ + +static const Service_UUID_t service_uuid = { .Service_UUID_128 = \ + { 0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f }}; + +#define SERIAL_SVC_TX_CHAR_UUID \ + { 0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } +#define SERIAL_SVC_RX_CHAR_UUID \ + { 0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } +#define SERIAL_SVC_FLOW_CONTROL_UUID \ + { 0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } +#define SERIAL_SVC_RPC_STATUS_UUID \ + { 0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } diff --git a/targets/f7/ble_glue/tl_dbg_conf.h b/targets/f7/ble_glue/tl_dbg_conf.h new file mode 100644 index 00000000000..daaa9d82ba8 --- /dev/null +++ b/targets/f7/ble_glue/tl_dbg_conf.h @@ -0,0 +1,102 @@ +#pragma once + +#include "app_conf.h" /* required as some configuration used in dbg_trace.h are set there */ +#include "dbg_trace.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Enable or Disable traces + * The raw data output is the hci binary packet format as specified by the BT specification * + */ +#define TL_SHCI_CMD_DBG_EN 1 /* Reports System commands sent to CPU2 and the command response */ +#define TL_SHCI_CMD_DBG_RAW_EN \ + 0 /* Reports raw data System commands sent to CPU2 and the command response */ +#define TL_SHCI_EVT_DBG_EN 1 /* Reports System Asynchronous Events received from CPU2 */ +#define TL_SHCI_EVT_DBG_RAW_EN \ + 0 /* Reports raw data System Asynchronous Events received from CPU2 */ + +#define TL_HCI_CMD_DBG_EN 1 /* Reports BLE command sent to CPU2 and the command response */ +#define TL_HCI_CMD_DBG_RAW_EN \ + 0 /* Reports raw data BLE command sent to CPU2 and the command response */ +#define TL_HCI_EVT_DBG_EN 1 /* Reports BLE Asynchronous Events received from CPU2 */ +#define TL_HCI_EVT_DBG_RAW_EN 0 /* Reports raw data BLE Asynchronous Events received from CPU2 */ + +#define TL_MM_DBG_EN 1 /* Reports the informations of the buffer released to CPU2 */ + +/** + * System Transport Layer + */ +#if(TL_SHCI_CMD_DBG_EN != 0) +#define TL_SHCI_CMD_DBG_MSG PRINT_MESG_DBG +#define TL_SHCI_CMD_DBG_BUF PRINT_LOG_BUFF_DBG +#else +#define TL_SHCI_CMD_DBG_MSG(...) +#define TL_SHCI_CMD_DBG_BUF(...) +#endif + +#if(TL_SHCI_CMD_DBG_RAW_EN != 0) +#define TL_SHCI_CMD_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) +#else +#define TL_SHCI_CMD_DBG_RAW(...) +#endif + +#if(TL_SHCI_EVT_DBG_EN != 0) +#define TL_SHCI_EVT_DBG_MSG PRINT_MESG_DBG +#define TL_SHCI_EVT_DBG_BUF PRINT_LOG_BUFF_DBG +#else +#define TL_SHCI_EVT_DBG_MSG(...) +#define TL_SHCI_EVT_DBG_BUF(...) +#endif + +#if(TL_SHCI_EVT_DBG_RAW_EN != 0) +#define TL_SHCI_EVT_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) +#else +#define TL_SHCI_EVT_DBG_RAW(...) +#endif + +/** + * BLE Transport Layer + */ +#if(TL_HCI_CMD_DBG_EN != 0) +#define TL_HCI_CMD_DBG_MSG PRINT_MESG_DBG +#define TL_HCI_CMD_DBG_BUF PRINT_LOG_BUFF_DBG +#else +#define TL_HCI_CMD_DBG_MSG(...) +#define TL_HCI_CMD_DBG_BUF(...) +#endif + +#if(TL_HCI_CMD_DBG_RAW_EN != 0) +#define TL_HCI_CMD_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) +#else +#define TL_HCI_CMD_DBG_RAW(...) +#endif + +#if(TL_HCI_EVT_DBG_EN != 0) +#define TL_HCI_EVT_DBG_MSG PRINT_MESG_DBG +#define TL_HCI_EVT_DBG_BUF PRINT_LOG_BUFF_DBG +#else +#define TL_HCI_EVT_DBG_MSG(...) +#define TL_HCI_EVT_DBG_BUF(...) +#endif + +#if(TL_HCI_EVT_DBG_RAW_EN != 0) +#define TL_HCI_EVT_DBG_RAW(_PDATA_, _SIZE_) furi_hal_console_tx_with_new_line(_PDATA_, _SIZE_) +#else +#define TL_HCI_EVT_DBG_RAW(...) +#endif + +/** + * Memory Manager - Released buffer tracing + */ +#if(TL_MM_DBG_EN != 0) +#define TL_MM_DBG_MSG PRINT_MESG_DBG +#else +#define TL_MM_DBG_MSG(...) +#endif + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/fatfs/fatfs.c b/targets/f7/fatfs/fatfs.c new file mode 100644 index 00000000000..5a8912cbdb7 --- /dev/null +++ b/targets/f7/fatfs/fatfs.c @@ -0,0 +1,23 @@ +#include "fatfs.h" +#include "furi_hal_rtc.h" + +/** logical drive path */ +char fatfs_path[4]; +/** File system object */ +FATFS fatfs_object; + +void fatfs_init(void) { + FATFS_LinkDriver(&sd_fatfs_driver, fatfs_path); +} + +/** Gets Time from RTC + * + * @return Time in DWORD (toasters per square washing machine) + */ +DWORD get_fattime() { + FuriHalRtcDateTime furi_time; + furi_hal_rtc_get_datetime(&furi_time); + + return ((uint32_t)(furi_time.year - 1980) << 25) | furi_time.month << 21 | + furi_time.day << 16 | furi_time.hour << 11 | furi_time.minute << 5 | furi_time.second; +} diff --git a/targets/f7/fatfs/fatfs.h b/targets/f7/fatfs/fatfs.h new file mode 100644 index 00000000000..8376bf6cc34 --- /dev/null +++ b/targets/f7/fatfs/fatfs.h @@ -0,0 +1,19 @@ +#pragma once + +#include "fatfs/ff.h" +#include "fatfs/ff_gen_drv.h" +#include "user_diskio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** File system object */ +extern FATFS fatfs_object; + +/** Init file system driver */ +void fatfs_init(void); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/firmware/targets/f7/fatfs/ffconf.h b/targets/f7/fatfs/ffconf.h similarity index 98% rename from firmware/targets/f7/fatfs/ffconf.h rename to targets/f7/fatfs/ffconf.h index 9410cedc86e..8408a1ec175 100644 --- a/firmware/targets/f7/fatfs/ffconf.h +++ b/targets/f7/fatfs/ffconf.h @@ -164,7 +164,7 @@ /* USER CODE BEGIN Volumes */ #define _STR_VOLUME_ID 0 /* 0:Use only 0-9 for drive ID, 1:Use strings for drive ID */ -#define _VOLUME_STRS "RAM", "NAND", "CF", "SD1", "SD2", "USB1", "USB2", "USB3" +#define _VOLUME_STRS "SD" /* _STR_VOLUME_ID switches string support of volume ID. / When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive / number in the path name. _VOLUME_STRS defines the drive ID strings for each @@ -219,10 +219,7 @@ / When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1) / Note that enabling exFAT discards C89 compatibility. */ -#define _FS_NORTC 1 -#define _NORTC_MON 7 -#define _NORTC_MDAY 20 -#define _NORTC_YEAR 2021 +#define _FS_NORTC 0 /* The option _FS_NORTC switches timestamp functiton. If the system does not have / any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable / the timestamp function. All objects modified by FatFs will have a fixed timestamp diff --git a/targets/f7/fatfs/sector_cache.c b/targets/f7/fatfs/sector_cache.c new file mode 100644 index 00000000000..efb520ec842 --- /dev/null +++ b/targets/f7/fatfs/sector_cache.c @@ -0,0 +1,56 @@ +#include "sector_cache.h" + +#include +#include +#include +#include +#include + +#define SECTOR_SIZE 512 +#define N_SECTORS 8 + +typedef struct { + uint32_t itr; + uint32_t sectors[N_SECTORS]; + uint8_t sector_data[N_SECTORS][SECTOR_SIZE]; +} SectorCache; + +static SectorCache* cache = NULL; + +void sector_cache_init() { + if(cache == NULL) { + cache = memmgr_alloc_from_pool(sizeof(SectorCache)); + } + + if(cache != NULL) { + memset(cache, 0, sizeof(SectorCache)); + } +} + +uint8_t* sector_cache_get(uint32_t n_sector) { + if(cache != NULL && n_sector != 0) { + for(int sector_i = 0; sector_i < N_SECTORS; ++sector_i) { + if(cache->sectors[sector_i] == n_sector) { + return cache->sector_data[sector_i]; + } + } + } + return NULL; +} + +void sector_cache_put(uint32_t n_sector, uint8_t* data) { + if(cache == NULL) return; + cache->sectors[cache->itr % N_SECTORS] = n_sector; + memcpy(cache->sector_data[cache->itr % N_SECTORS], data, SECTOR_SIZE); + cache->itr++; +} + +void sector_cache_invalidate_range(uint32_t start_sector, uint32_t end_sector) { + if(cache == NULL) return; + for(int sector_i = 0; sector_i < N_SECTORS; ++sector_i) { + if((cache->sectors[sector_i] >= start_sector) && + (cache->sectors[sector_i] <= end_sector)) { + cache->sectors[sector_i] = 0; + } + } +} \ No newline at end of file diff --git a/targets/f7/fatfs/sector_cache.h b/targets/f7/fatfs/sector_cache.h new file mode 100644 index 00000000000..5fe4a2ed86b --- /dev/null +++ b/targets/f7/fatfs/sector_cache.h @@ -0,0 +1,36 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Init sector cache system + */ +void sector_cache_init(); + +/** + * @brief Get sector data from cache + * @param n_sector Sector number + * @return Pointer to sector data or NULL if not found + */ +uint8_t* sector_cache_get(uint32_t n_sector); + +/** + * @brief Put sector data to cache + * @param n_sector Sector number + * @param data Pointer to sector data + */ +void sector_cache_put(uint32_t n_sector, uint8_t* data); + +/** + * @brief Invalidate sector cache for given range + * @param start_sector Start sector number + * @param end_sector End sector number + */ +void sector_cache_invalidate_range(uint32_t start_sector, uint32_t end_sector); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/fatfs/user_diskio.c b/targets/f7/fatfs/user_diskio.c new file mode 100644 index 00000000000..85e5cad4f74 --- /dev/null +++ b/targets/f7/fatfs/user_diskio.c @@ -0,0 +1,119 @@ +#include +#include +#include "user_diskio.h" +#include "sector_cache.h" + +static DSTATUS driver_initialize(BYTE pdrv); +static DSTATUS driver_status(BYTE pdrv); +static DRESULT driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count); +static DRESULT driver_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); +static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff); + +Diskio_drvTypeDef sd_fatfs_driver = { + driver_initialize, + driver_status, + driver_read, + driver_write, + driver_ioctl, +}; + +/** + * @brief Initializes a Drive + * @param pdrv: Physical drive number (0..) + * @retval DSTATUS: Operation status + */ +static DSTATUS driver_initialize(BYTE pdrv) { + UNUSED(pdrv); + return RES_OK; +} + +/** + * @brief Gets Disk Status + * @param pdrv: Physical drive number (0..) + * @retval DSTATUS: Operation status + */ +static DSTATUS driver_status(BYTE pdrv) { + UNUSED(pdrv); + DSTATUS status = 0; + if(furi_hal_sd_get_card_state() != FuriStatusOk) { + status = STA_NOINIT; + } + + return status; +} + +/** + * @brief Reads Sector(s) + * @param pdrv: Physical drive number (0..) + * @param *buff: Data buffer to store read data + * @param sector: Sector address (LBA) + * @param count: Number of sectors to read (1..128) + * @retval DRESULT: Operation result + */ +static DRESULT driver_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) { + UNUSED(pdrv); + FuriStatus status = furi_hal_sd_read_blocks((uint32_t*)buff, (uint32_t)(sector), count); + return status == FuriStatusOk ? RES_OK : RES_ERROR; +} + +/** + * @brief Writes Sector(s) + * @param pdrv: Physical drive number (0..) + * @param *buff: Data to be written + * @param sector: Sector address (LBA) + * @param count: Number of sectors to write (1..128) + * @retval DRESULT: Operation result + */ +static DRESULT driver_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) { + UNUSED(pdrv); + FuriStatus status = furi_hal_sd_write_blocks((uint32_t*)buff, (uint32_t)(sector), count); + return status == FuriStatusOk ? RES_OK : RES_ERROR; +} + +/** + * @brief I/O control operation + * @param pdrv: Physical drive number (0..) + * @param cmd: Control code + * @param *buff: Buffer to send/receive control data + * @retval DRESULT: Operation result + */ +static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) { + DRESULT res = RES_ERROR; + FuriHalSdInfo sd_info; + + DSTATUS status = driver_status(pdrv); + if(status & STA_NOINIT) return RES_NOTRDY; + + switch(cmd) { + /* Make sure that no pending write process */ + case CTRL_SYNC: + res = RES_OK; + break; + + /* Get number of sectors on the disk (DWORD) */ + case GET_SECTOR_COUNT: + furi_hal_sd_info(&sd_info); + *(DWORD*)buff = sd_info.logical_block_count; + res = RES_OK; + break; + + /* Get R/W sector size (WORD) */ + case GET_SECTOR_SIZE: + furi_hal_sd_info(&sd_info); + *(WORD*)buff = sd_info.logical_block_size; + res = RES_OK; + break; + + /* Get erase block size in unit of sector (DWORD) */ + case GET_BLOCK_SIZE: + furi_hal_sd_info(&sd_info); + *(DWORD*)buff = sd_info.logical_block_size; + res = RES_OK; + break; + + default: + res = RES_PARERR; + } + + return res; +} diff --git a/targets/f7/fatfs/user_diskio.h b/targets/f7/fatfs/user_diskio.h new file mode 100644 index 00000000000..db636fbb950 --- /dev/null +++ b/targets/f7/fatfs/user_diskio.h @@ -0,0 +1,13 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "fatfs/ff_gen_drv.h" + +extern Diskio_drvTypeDef sd_fatfs_driver; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/targets/f7/furi_hal/furi_hal.c b/targets/f7/furi_hal/furi_hal.c new file mode 100644 index 00000000000..2062645cdf3 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal.c @@ -0,0 +1,72 @@ +#include +#include +#include + +#include + +#define TAG "FuriHal" + +void furi_hal_init_early() { + furi_hal_cortex_init_early(); + furi_hal_clock_init_early(); + furi_hal_bus_init_early(); + furi_hal_dma_init_early(); + furi_hal_resources_init_early(); + furi_hal_os_init(); + furi_hal_spi_config_init_early(); + furi_hal_i2c_init_early(); + furi_hal_light_init(); + furi_hal_rtc_init_early(); +} + +void furi_hal_deinit_early() { + furi_hal_rtc_deinit_early(); + furi_hal_i2c_deinit_early(); + furi_hal_spi_config_deinit_early(); + furi_hal_resources_deinit_early(); + furi_hal_dma_deinit_early(); + furi_hal_bus_deinit_early(); + furi_hal_clock_deinit_early(); +} + +void furi_hal_init() { + furi_hal_mpu_init(); + furi_hal_clock_init(); + furi_hal_random_init(); + furi_hal_console_init(); + furi_hal_rtc_init(); + furi_hal_interrupt_init(); + furi_hal_flash_init(); + furi_hal_resources_init(); + furi_hal_version_init(); + furi_hal_region_init(); + furi_hal_spi_config_init(); + furi_hal_spi_dma_init(); + furi_hal_ibutton_init(); + furi_hal_speaker_init(); + furi_hal_crypto_init(); + furi_hal_i2c_init(); + furi_hal_power_init(); + furi_hal_light_init(); + furi_hal_bt_init(); + furi_hal_memory_init(); + +#ifndef FURI_RAM_EXEC + furi_hal_usb_init(); + furi_hal_vibro_init(); + furi_hal_subghz_init(); + furi_hal_nfc_init(); + furi_hal_rfid_init(); +#endif +} + +void furi_hal_switch(void* address) { + __set_BASEPRI(0); + asm volatile("ldr r3, [%0] \n" + "msr msp, r3 \n" + "ldr r3, [%1] \n" + "mov pc, r3 \n" + : + : "r"(address), "r"(address + 0x4) + : "r3"); +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/targets/f7/furi_hal/furi_hal_bt.c similarity index 80% rename from firmware/targets/f7/furi_hal/furi_hal_bt.c rename to targets/f7/furi_hal/furi_hal_bt.c index 47ed5992e31..48bce998eea 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/targets/f7/furi_hal/furi_hal_bt.c @@ -1,13 +1,19 @@ #include -#include + +#include +#include + #include -#include +#include + +#include #include +#include #include #include -#include "battery_service.h" - +#include +#include #include #define TAG "FuriHalBt" @@ -18,8 +24,19 @@ /* Time, in ms, to wait for mode transition before crashing */ #define C2_MODE_SWITCH_TIMEOUT 10000 -FuriMutex* furi_hal_bt_core2_mtx = NULL; -static FuriHalBtStack furi_hal_bt_stack = FuriHalBtStackUnknown; +#define FURI_HAL_BT_HARDFAULT_INFO_MAGIC 0x1170FD0F + +typedef struct { + FuriMutex* core2_mtx; + FuriTimer* hardfault_check_timer; + FuriHalBtStack stack; +} FuriHalBt; + +static FuriHalBt furi_hal_bt = { + .core2_mtx = NULL, + .hardfault_check_timer = NULL, + .stack = FuriHalBtStackUnknown, +}; typedef void (*FuriHalBtProfileStart)(void); typedef void (*FuriHalBtProfileStop)(void); @@ -76,29 +93,46 @@ FuriHalBtProfileConfig profile_config[FuriHalBtProfileNumber] = { }; FuriHalBtProfileConfig* current_profile = NULL; +static void furi_hal_bt_hardfault_check(void* context) { + UNUSED(context); + if(furi_hal_bt_get_hardfault_info()) { + furi_crash("ST(R) Copro(R) HardFault"); + } +} + void furi_hal_bt_init() { - if(!furi_hal_bt_core2_mtx) { - furi_hal_bt_core2_mtx = furi_mutex_alloc(FuriMutexTypeNormal); - furi_assert(furi_hal_bt_core2_mtx); + furi_hal_bus_enable(FuriHalBusHSEM); + furi_hal_bus_enable(FuriHalBusIPCC); + furi_hal_bus_enable(FuriHalBusAES2); + furi_hal_bus_enable(FuriHalBusPKA); + furi_hal_bus_enable(FuriHalBusCRC); + + if(!furi_hal_bt.core2_mtx) { + furi_hal_bt.core2_mtx = furi_mutex_alloc(FuriMutexTypeNormal); + furi_assert(furi_hal_bt.core2_mtx); } - // Explicitly tell that we are in charge of CLK48 domain - if(!LL_HSEM_IsSemaphoreLocked(HSEM, CFG_HW_CLK48_CONFIG_SEMID)) { - furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0); + if(!furi_hal_bt.hardfault_check_timer) { + furi_hal_bt.hardfault_check_timer = + furi_timer_alloc(furi_hal_bt_hardfault_check, FuriTimerTypePeriodic, NULL); + furi_timer_start(furi_hal_bt.hardfault_check_timer, 5000); } + // Explicitly tell that we are in charge of CLK48 domain + furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0); + // Start Core2 ble_glue_init(); } void furi_hal_bt_lock_core2() { - furi_assert(furi_hal_bt_core2_mtx); - furi_check(furi_mutex_acquire(furi_hal_bt_core2_mtx, FuriWaitForever) == FuriStatusOk); + furi_assert(furi_hal_bt.core2_mtx); + furi_check(furi_mutex_acquire(furi_hal_bt.core2_mtx, FuriWaitForever) == FuriStatusOk); } void furi_hal_bt_unlock_core2() { - furi_assert(furi_hal_bt_core2_mtx); - furi_check(furi_mutex_release(furi_hal_bt_core2_mtx) == FuriStatusOk); + furi_assert(furi_hal_bt.core2_mtx); + furi_check(furi_mutex_release(furi_hal_bt.core2_mtx) == FuriStatusOk); } static bool furi_hal_bt_radio_stack_is_supported(const BleGlueC2Info* info) { @@ -106,31 +140,29 @@ static bool furi_hal_bt_radio_stack_is_supported(const BleGlueC2Info* info) { if(info->StackType == INFO_STACK_TYPE_BLE_LIGHT) { if(info->VersionMajor >= FURI_HAL_BT_STACK_VERSION_MAJOR && info->VersionMinor >= FURI_HAL_BT_STACK_VERSION_MINOR) { - furi_hal_bt_stack = FuriHalBtStackLight; + furi_hal_bt.stack = FuriHalBtStackLight; supported = true; } } else if(info->StackType == INFO_STACK_TYPE_BLE_FULL) { if(info->VersionMajor >= FURI_HAL_BT_STACK_VERSION_MAJOR && info->VersionMinor >= FURI_HAL_BT_STACK_VERSION_MINOR) { - furi_hal_bt_stack = FuriHalBtStackFull; + furi_hal_bt.stack = FuriHalBtStackFull; supported = true; } } else { - furi_hal_bt_stack = FuriHalBtStackUnknown; + furi_hal_bt.stack = FuriHalBtStackUnknown; } return supported; } bool furi_hal_bt_start_radio_stack() { bool res = false; - furi_assert(furi_hal_bt_core2_mtx); + furi_assert(furi_hal_bt.core2_mtx); - furi_mutex_acquire(furi_hal_bt_core2_mtx, FuriWaitForever); + furi_mutex_acquire(furi_hal_bt.core2_mtx, FuriWaitForever); // Explicitly tell that we are in charge of CLK48 domain - if(!LL_HSEM_IsSemaphoreLocked(HSEM, CFG_HW_CLK48_CONFIG_SEMID)) { - furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0); - } + furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0); do { // Wait until C2 is started or timeout @@ -161,17 +193,17 @@ bool furi_hal_bt_start_radio_stack() { } res = true; } while(false); - furi_mutex_release(furi_hal_bt_core2_mtx); + furi_mutex_release(furi_hal_bt.core2_mtx); return res; } FuriHalBtStack furi_hal_bt_get_radio_stack() { - return furi_hal_bt_stack; + return furi_hal_bt.stack; } bool furi_hal_bt_is_ble_gatt_gap_supported() { - if(furi_hal_bt_stack == FuriHalBtStackLight || furi_hal_bt_stack == FuriHalBtStackFull) { + if(furi_hal_bt.stack == FuriHalBtStackLight || furi_hal_bt.stack == FuriHalBtStackFull) { return true; } else { return false; @@ -179,7 +211,7 @@ bool furi_hal_bt_is_ble_gatt_gap_supported() { } bool furi_hal_bt_is_testing_supported() { - if(furi_hal_bt_stack == FuriHalBtStackFull) { + if(furi_hal_bt.stack == FuriHalBtStackFull) { return true; } else { return false; @@ -238,6 +270,7 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, } void furi_hal_bt_reinit() { + furi_hal_power_insomnia_enter(); FURI_LOG_I(TAG, "Disconnect and stop advertising"); furi_hal_bt_stop_advertising(); @@ -257,10 +290,17 @@ void furi_hal_bt_reinit() { furi_delay_ms(100); ble_glue_thread_stop(); + furi_hal_bus_disable(FuriHalBusHSEM); + furi_hal_bus_disable(FuriHalBusIPCC); + furi_hal_bus_disable(FuriHalBusAES2); + furi_hal_bus_disable(FuriHalBusPKA); + furi_hal_bus_disable(FuriHalBusCRC); + FURI_LOG_I(TAG, "Start BT initialization"); furi_hal_bt_init(); furi_hal_bt_start_radio_stack(); + furi_hal_power_insomnia_exit(); } bool furi_hal_bt_change_app(FuriHalBtProfile profile, GapEventCallback event_cb, void* context) { @@ -339,7 +379,7 @@ bool furi_hal_bt_clear_white_list() { return status != BLE_STATUS_SUCCESS; } -void furi_hal_bt_dump_state(string_t buffer) { +void furi_hal_bt_dump_state(FuriString* buffer) { if(furi_hal_bt_is_alive()) { uint8_t HCI_Version; uint16_t HCI_Revision; @@ -350,7 +390,7 @@ void furi_hal_bt_dump_state(string_t buffer) { tBleStatus ret = hci_read_local_version_information( &HCI_Version, &HCI_Revision, &LMP_PAL_Version, &Manufacturer_Name, &LMP_PAL_Subversion); - string_cat_printf( + furi_string_cat_printf( buffer, "Ret: %d, HCI_Version: %d, HCI_Revision: %d, LMP_PAL_Version: %d, Manufacturer_Name: %d, LMP_PAL_Subversion: %d", ret, @@ -360,7 +400,7 @@ void furi_hal_bt_dump_state(string_t buffer) { Manufacturer_Name, LMP_PAL_Subversion); } else { - string_cat_printf(buffer, "BLE not ready"); + furi_string_cat_printf(buffer, "BLE not ready"); } } @@ -414,7 +454,7 @@ float furi_hal_bt_get_rssi() { val += 6.0; rssi >>= 1; } - val += (417 * rssi + 18080) >> 10; + val += (float)((417 * rssi + 18080) >> 10); } return val; } @@ -443,3 +483,12 @@ bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode) { FURI_LOG_E(TAG, "Failed to switch C2 mode: %d", fw_start_res); return false; } + +const FuriHalBtHardfaultInfo* furi_hal_bt_get_hardfault_info() { + /* AN5289, 4.8.2 */ + const FuriHalBtHardfaultInfo* info = (FuriHalBtHardfaultInfo*)(SRAM2A_BASE); + if(info->magic != FURI_HAL_BT_HARDFAULT_INFO_MAGIC) { + return NULL; + } + return info; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/targets/f7/furi_hal/furi_hal_bt_hid.c similarity index 97% rename from firmware/targets/f7/furi_hal/furi_hal_bt_hid.c rename to targets/f7/furi_hal/furi_hal_bt_hid.c index 22415199cbd..7ec712af4db 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +++ b/targets/f7/furi_hal/furi_hal_bt_hid.c @@ -1,11 +1,11 @@ -#include "furi_hal_bt_hid.h" -#include "furi_hal_usb_hid.h" -#include "usb_hid.h" -#include "dev_info_service.h" -#include "battery_service.h" -#include "hid_service.h" +#include +#include +#include +#include +#include #include +#include #define FURI_HAL_BT_INFO_BASE_USB_SPECIFICATION (0x0101) #define FURI_HAL_BT_INFO_COUNTRY_CODE (0x00) @@ -153,7 +153,7 @@ void furi_hal_bt_hid_start() { FURI_HAL_BT_HID_INFO_FLAG_REMOTE_WAKE_MSK | FURI_HAL_BT_HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK, }; - hid_svc_update_info(hid_info_val, sizeof(hid_info_val)); + hid_svc_update_info(hid_info_val); } void furi_hal_bt_hid_stop() { @@ -216,7 +216,7 @@ bool furi_hal_bt_hid_kb_release_all() { bool furi_hal_bt_hid_consumer_key_press(uint16_t button) { furi_assert(consumer_report); - for(uint8_t i = 0; i < FURI_HAL_BT_HID_CONSUMER_MAX_KEYS; i++) { + for(uint8_t i = 0; i < FURI_HAL_BT_HID_CONSUMER_MAX_KEYS; i++) { //-V1008 if(consumer_report->key[i] == 0) { consumer_report->key[i] = button; break; @@ -228,7 +228,7 @@ bool furi_hal_bt_hid_consumer_key_press(uint16_t button) { bool furi_hal_bt_hid_consumer_key_release(uint16_t button) { furi_assert(consumer_report); - for(uint8_t i = 0; i < FURI_HAL_BT_HID_CONSUMER_MAX_KEYS; i++) { + for(uint8_t i = 0; i < FURI_HAL_BT_HID_CONSUMER_MAX_KEYS; i++) { //-V1008 if(consumer_report->key[i] == button) { consumer_report->key[i] = 0; break; @@ -240,7 +240,7 @@ bool furi_hal_bt_hid_consumer_key_release(uint16_t button) { bool furi_hal_bt_hid_consumer_key_release_all() { furi_assert(consumer_report); - for(uint8_t i = 0; i < FURI_HAL_BT_HID_CONSUMER_MAX_KEYS; i++) { + for(uint8_t i = 0; i < FURI_HAL_BT_HID_CONSUMER_MAX_KEYS; i++) { //-V1008 consumer_report->key[i] = 0; } return hid_svc_update_input_report( diff --git a/targets/f7/furi_hal/furi_hal_bt_serial.c b/targets/f7/furi_hal/furi_hal_bt_serial.c new file mode 100644 index 00000000000..2927d946f98 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_bt_serial.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include + +#include + +void furi_hal_bt_serial_start() { + // Start device info + if(!dev_info_svc_is_started()) { + dev_info_svc_start(); + } + // Start battery service + if(!battery_svc_is_started()) { + battery_svc_start(); + } + // Start Serial service + if(!serial_svc_is_started()) { + serial_svc_start(); + } +} + +void furi_hal_bt_serial_set_event_callback( + uint16_t buff_size, + FuriHalBtSerialCallback callback, + void* context) { + serial_svc_set_callbacks(buff_size, callback, context); +} + +void furi_hal_bt_serial_notify_buffer_is_empty() { + serial_svc_notify_buffer_is_empty(); +} + +void furi_hal_bt_serial_set_rpc_status(FuriHalBtSerialRpcStatus status) { + SerialServiceRpcStatus st; + if(status == FuriHalBtSerialRpcStatusActive) { + st = SerialServiceRpcStatusActive; + } else { + st = SerialServiceRpcStatusNotActive; + } + serial_svc_set_rpc_status(st); +} + +bool furi_hal_bt_serial_tx(uint8_t* data, uint16_t size) { + if(size > FURI_HAL_BT_SERIAL_PACKET_SIZE_MAX) { + return false; + } + return serial_svc_update_tx(data, size); +} + +void furi_hal_bt_serial_stop() { + // Stop all services + if(dev_info_svc_is_started()) { + dev_info_svc_stop(); + } + // Start battery service + if(battery_svc_is_started()) { + battery_svc_stop(); + } + // Start Serial service + if(serial_svc_is_started()) { + serial_svc_stop(); + } +} diff --git a/targets/f7/furi_hal/furi_hal_bus.c b/targets/f7/furi_hal/furi_hal_bus.c new file mode 100644 index 00000000000..2c6f1f1ebc9 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_bus.c @@ -0,0 +1,302 @@ +#include +#include + +#include + +/* Bus bitmask definitions */ +#define FURI_HAL_BUS_IGNORE (0x0U) + +#define FURI_HAL_BUS_AHB1_GRP1 \ + (LL_AHB1_GRP1_PERIPH_DMA1 | LL_AHB1_GRP1_PERIPH_DMA2 | LL_AHB1_GRP1_PERIPH_DMAMUX1 | \ + LL_AHB1_GRP1_PERIPH_CRC | LL_AHB1_GRP1_PERIPH_TSC) + +#if defined(ADC_SUPPORT_5_MSPS) +#define FURI_HAL_BUS_AHB2_GRP1 \ + (LL_AHB2_GRP1_PERIPH_GPIOA | LL_AHB2_GRP1_PERIPH_GPIOB | LL_AHB2_GRP1_PERIPH_GPIOC | \ + LL_AHB2_GRP1_PERIPH_GPIOD | LL_AHB2_GRP1_PERIPH_GPIOE | LL_AHB2_GRP1_PERIPH_GPIOH | \ + LL_AHB2_GRP1_PERIPH_ADC | LL_AHB2_GRP1_PERIPH_AES1) + +#define FURI_HAL_BUS_APB2_GRP1 \ + (LL_APB2_GRP1_PERIPH_TIM1 | LL_APB2_GRP1_PERIPH_SPI1 | LL_APB2_GRP1_PERIPH_USART1 | \ + LL_APB2_GRP1_PERIPH_TIM16 | LL_APB2_GRP1_PERIPH_TIM17 | LL_APB2_GRP1_PERIPH_SAI1) +#else +#define FURI_HAL_BUS_AHB2_GRP1 \ + (LL_AHB2_GRP1_PERIPH_GPIOA | LL_AHB2_GRP1_PERIPH_GPIOB | LL_AHB2_GRP1_PERIPH_GPIOC | \ + LL_AHB2_GRP1_PERIPH_GPIOD | LL_AHB2_GRP1_PERIPH_GPIOE | LL_AHB2_GRP1_PERIPH_GPIOH | \ + LL_AHB2_GRP1_PERIPH_AES1) + +#define FURI_HAL_BUS_APB2_GRP1 \ + (LL_APB2_GRP1_PERIPH_ADC | LL_APB2_GRP1_PERIPH_TIM1 | LL_APB2_GRP1_PERIPH_SPI1 | \ + LL_APB2_GRP1_PERIPH_USART1 | LL_APB2_GRP1_PERIPH_TIM16 | LL_APB2_GRP1_PERIPH_TIM17 | \ + LL_APB2_GRP1_PERIPH_SAI1) +#endif + +#define FURI_HAL_BUS_AHB3_GRP1 \ + (LL_AHB3_GRP1_PERIPH_QUADSPI | LL_AHB3_GRP1_PERIPH_PKA | LL_AHB3_GRP1_PERIPH_AES2 | \ + LL_AHB3_GRP1_PERIPH_RNG | LL_AHB3_GRP1_PERIPH_HSEM | LL_AHB3_GRP1_PERIPH_IPCC) +// LL_AHB3_GRP1_PERIPH_FLASH enabled by default + +#define FURI_HAL_BUS_APB1_GRP1 \ + (LL_APB1_GRP1_PERIPH_TIM2 | LL_APB1_GRP1_PERIPH_LCD | LL_APB1_GRP1_PERIPH_SPI2 | \ + LL_APB1_GRP1_PERIPH_I2C1 | LL_APB1_GRP1_PERIPH_I2C3 | LL_APB1_GRP1_PERIPH_CRS | \ + LL_APB1_GRP1_PERIPH_USB | LL_APB1_GRP1_PERIPH_LPTIM1) + +#define FURI_HAL_BUS_APB1_GRP2 (LL_APB1_GRP2_PERIPH_LPUART1 | LL_APB1_GRP2_PERIPH_LPTIM2) +#define FURI_HAL_BUS_APB3_GRP1 (LL_APB3_GRP1_PERIPH_RF) + +/* Test macro definitions */ +#define FURI_HAL_BUS_IS_ALL_CLEAR(reg, value) (READ_BIT((reg), (value)) == 0UL) +#define FURI_HAL_BUS_IS_ALL_SET(reg, value) (READ_BIT((reg), (value)) == (value)) + +#define FURI_HAL_BUS_IS_CLOCK_ENABLED(bus, value, ...) \ + (FURI_HAL_BUS_IS_ALL_SET(RCC->bus##ENR##__VA_ARGS__, (value))) +#define FURI_HAL_BUS_IS_CLOCK_DISABLED(bus, value, ...) \ + (FURI_HAL_BUS_IS_ALL_CLEAR(RCC->bus##ENR##__VA_ARGS__, (value))) + +#define FURI_HAL_BUS_IS_RESET_ASSERTED(bus, value, ...) \ + (FURI_HAL_BUS_IS_ALL_SET(RCC->bus##RSTR##__VA_ARGS__, (value))) +#define FURI_HAL_BUS_IS_RESET_DEASSERTED(bus, value, ...) \ + (FURI_HAL_BUS_IS_ALL_CLEAR(RCC->bus##RSTR##__VA_ARGS__, (value))) + +#define FURI_HAL_BUS_IS_PERIPH_ENABLED(bus, value, ...) \ + (FURI_HAL_BUS_IS_RESET_DEASSERTED(bus, (value), __VA_ARGS__) && \ + FURI_HAL_BUS_IS_CLOCK_ENABLED(bus, (value), __VA_ARGS__)) + +#define FURI_HAL_BUS_IS_PERIPH_DISABLED(bus, value, ...) \ + (FURI_HAL_BUS_IS_CLOCK_DISABLED(bus, (value), __VA_ARGS__) && \ + FURI_HAL_BUS_IS_RESET_ASSERTED(bus, (value), __VA_ARGS__)) + +/* Control macro definitions */ +#define FURI_HAL_BUS_RESET_ASSERT(bus, value, grp) LL_##bus##_GRP##grp##_ForceReset(value) +#define FURI_HAL_BUS_RESET_DEASSERT(bus, value, grp) LL_##bus##_GRP##grp##_ReleaseReset(value) + +#define FURI_HAL_BUS_CLOCK_ENABLE(bus, value, grp) LL_##bus##_GRP##grp##_EnableClock(value) +#define FURI_HAL_BUS_CLOCK_DISABLE(bus, value, grp) LL_##bus##_GRP##grp##_DisableClock(value) + +#define FURI_HAL_BUS_PERIPH_ENABLE(bus, value, grp) \ + FURI_HAL_BUS_CLOCK_ENABLE(bus, value, grp); \ + FURI_HAL_BUS_RESET_DEASSERT(bus, value, grp) + +#define FURI_HAL_BUS_PERIPH_DISABLE(bus, value, grp) \ + FURI_HAL_BUS_RESET_ASSERT(bus, value, grp); \ + FURI_HAL_BUS_CLOCK_DISABLE(bus, value, grp) + +#define FURI_HAL_BUS_PERIPH_RESET(bus, value, grp) \ + FURI_HAL_BUS_RESET_ASSERT(bus, value, grp); \ + FURI_HAL_BUS_RESET_DEASSERT(bus, value, grp) + +static const uint32_t furi_hal_bus[] = { + [FuriHalBusAHB1_GRP1] = FURI_HAL_BUS_IGNORE, + [FuriHalBusDMA1] = LL_AHB1_GRP1_PERIPH_DMA1, + [FuriHalBusDMA2] = LL_AHB1_GRP1_PERIPH_DMA2, + [FuriHalBusDMAMUX1] = LL_AHB1_GRP1_PERIPH_DMAMUX1, + [FuriHalBusCRC] = LL_AHB1_GRP1_PERIPH_CRC, + [FuriHalBusTSC] = LL_AHB1_GRP1_PERIPH_TSC, + + [FuriHalBusAHB2_GRP1] = FURI_HAL_BUS_IGNORE, + [FuriHalBusGPIOA] = LL_AHB2_GRP1_PERIPH_GPIOA, + [FuriHalBusGPIOB] = LL_AHB2_GRP1_PERIPH_GPIOB, + [FuriHalBusGPIOC] = LL_AHB2_GRP1_PERIPH_GPIOC, + [FuriHalBusGPIOD] = LL_AHB2_GRP1_PERIPH_GPIOD, + [FuriHalBusGPIOE] = LL_AHB2_GRP1_PERIPH_GPIOE, + [FuriHalBusGPIOH] = LL_AHB2_GRP1_PERIPH_GPIOH, +#if defined(ADC_SUPPORT_5_MSPS) + [FuriHalBusADC] = LL_AHB2_GRP1_PERIPH_ADC, +#endif + [FuriHalBusAES1] = LL_AHB2_GRP1_PERIPH_AES1, + + [FuriHalBusAHB3_GRP1] = FURI_HAL_BUS_IGNORE, + [FuriHalBusQUADSPI] = LL_AHB3_GRP1_PERIPH_QUADSPI, + [FuriHalBusPKA] = LL_AHB3_GRP1_PERIPH_PKA, + [FuriHalBusAES2] = LL_AHB3_GRP1_PERIPH_AES2, + [FuriHalBusRNG] = LL_AHB3_GRP1_PERIPH_RNG, + [FuriHalBusHSEM] = LL_AHB3_GRP1_PERIPH_HSEM, + [FuriHalBusIPCC] = LL_AHB3_GRP1_PERIPH_IPCC, + [FuriHalBusFLASH] = LL_AHB3_GRP1_PERIPH_FLASH, + + [FuriHalBusAPB1_GRP1] = FURI_HAL_BUS_APB1_GRP1, + [FuriHalBusTIM2] = LL_APB1_GRP1_PERIPH_TIM2, + [FuriHalBusLCD] = LL_APB1_GRP1_PERIPH_LCD, + [FuriHalBusSPI2] = LL_APB1_GRP1_PERIPH_SPI2, + [FuriHalBusI2C1] = LL_APB1_GRP1_PERIPH_I2C1, + [FuriHalBusI2C3] = LL_APB1_GRP1_PERIPH_I2C3, + [FuriHalBusCRS] = LL_APB1_GRP1_PERIPH_CRS, + [FuriHalBusUSB] = LL_APB1_GRP1_PERIPH_USB, + [FuriHalBusLPTIM1] = LL_APB1_GRP1_PERIPH_LPTIM1, + + [FuriHalBusAPB1_GRP2] = FURI_HAL_BUS_APB1_GRP2, + [FuriHalBusLPUART1] = LL_APB1_GRP2_PERIPH_LPUART1, + [FuriHalBusLPTIM2] = LL_APB1_GRP2_PERIPH_LPTIM2, + + [FuriHalBusAPB2_GRP1] = FURI_HAL_BUS_APB2_GRP1, +#if defined(ADC_SUPPORT_2_5_MSPS) + [FuriHalBusADC] = LL_APB2_GRP1_PERIPH_ADC, +#endif + [FuriHalBusTIM1] = LL_APB2_GRP1_PERIPH_TIM1, + [FuriHalBusSPI1] = LL_APB2_GRP1_PERIPH_SPI1, + [FuriHalBusUSART1] = LL_APB2_GRP1_PERIPH_USART1, + [FuriHalBusTIM16] = LL_APB2_GRP1_PERIPH_TIM16, + [FuriHalBusTIM17] = LL_APB2_GRP1_PERIPH_TIM17, + [FuriHalBusSAI1] = LL_APB2_GRP1_PERIPH_SAI1, + + [FuriHalBusAPB3_GRP1] = FURI_HAL_BUS_IGNORE, // APB3_GRP1 clocking cannot be changed + [FuriHalBusRF] = LL_APB3_GRP1_PERIPH_RF, +}; + +void furi_hal_bus_init_early() { + FURI_CRITICAL_ENTER(); + + // FURI_HAL_BUS_PERIPH_DISABLE(AHB1, FURI_HAL_BUS_AHB1_GRP1, 1); + // FURI_HAL_BUS_PERIPH_DISABLE(AHB2, FURI_HAL_BUS_AHB2_GRP1, 1); + // FURI_HAL_BUS_PERIPH_DISABLE(AHB3, FURI_HAL_BUS_AHB3_GRP1, 1); + FURI_HAL_BUS_PERIPH_DISABLE(APB1, FURI_HAL_BUS_APB1_GRP1, 1); + FURI_HAL_BUS_PERIPH_DISABLE(APB1, FURI_HAL_BUS_APB1_GRP2, 2); + FURI_HAL_BUS_PERIPH_DISABLE(APB2, FURI_HAL_BUS_APB2_GRP1, 1); + + FURI_HAL_BUS_RESET_ASSERT(APB3, FURI_HAL_BUS_APB3_GRP1, 1); + + FURI_CRITICAL_EXIT(); +} + +void furi_hal_bus_deinit_early() { + FURI_CRITICAL_ENTER(); + + // FURI_HAL_BUS_PERIPH_ENABLE(AHB1, FURI_HAL_BUS_AHB1_GRP1, 1); + // FURI_HAL_BUS_PERIPH_ENABLE(AHB2, FURI_HAL_BUS_AHB2_GRP1, 1); + // FURI_HAL_BUS_PERIPH_ENABLE(AHB3, FURI_HAL_BUS_AHB3_GRP1, 1); + FURI_HAL_BUS_PERIPH_ENABLE(APB1, FURI_HAL_BUS_APB1_GRP1, 1); + FURI_HAL_BUS_PERIPH_ENABLE(APB1, FURI_HAL_BUS_APB1_GRP2, 2); + FURI_HAL_BUS_PERIPH_ENABLE(APB2, FURI_HAL_BUS_APB2_GRP1, 1); + + FURI_HAL_BUS_RESET_DEASSERT(APB3, FURI_HAL_BUS_APB3_GRP1, 1); + + FURI_CRITICAL_EXIT(); +} + +void furi_hal_bus_enable(FuriHalBus bus) { + furi_check(bus < FuriHalBusMAX); + const uint32_t value = furi_hal_bus[bus]; + if(!value) { + return; + } + + FURI_CRITICAL_ENTER(); + if(bus < FuriHalBusAHB2_GRP1) { + // furi_check(FURI_HAL_BUS_IS_PERIPH_DISABLED(AHB1, value)); + FURI_HAL_BUS_PERIPH_ENABLE(AHB1, value, 1); + } else if(bus < FuriHalBusAHB3_GRP1) { + // furi_check(FURI_HAL_BUS_IS_PERIPH_DISABLED(AHB2, value)); + FURI_HAL_BUS_PERIPH_ENABLE(AHB2, value, 1); + } else if(bus < FuriHalBusAPB1_GRP1) { + // furi_check(FURI_HAL_BUS_IS_PERIPH_DISABLED(AHB3, value)); + FURI_HAL_BUS_PERIPH_ENABLE(AHB3, value, 1); + } else if(bus < FuriHalBusAPB1_GRP2) { + furi_check(FURI_HAL_BUS_IS_PERIPH_DISABLED(APB1, value, 1)); + FURI_HAL_BUS_PERIPH_ENABLE(APB1, value, 1); + } else if(bus < FuriHalBusAPB2_GRP1) { + furi_check(FURI_HAL_BUS_IS_PERIPH_DISABLED(APB1, value, 2)); + FURI_HAL_BUS_PERIPH_ENABLE(APB1, value, 2); + } else if(bus < FuriHalBusAPB3_GRP1) { + furi_check(FURI_HAL_BUS_IS_PERIPH_DISABLED(APB2, value)); + FURI_HAL_BUS_PERIPH_ENABLE(APB2, value, 1); + } else { + furi_check(FURI_HAL_BUS_IS_RESET_ASSERTED(APB3, value)); + FURI_HAL_BUS_RESET_DEASSERT(APB3, FURI_HAL_BUS_APB3_GRP1, 1); + } + FURI_CRITICAL_EXIT(); +} + +void furi_hal_bus_reset(FuriHalBus bus) { + furi_check(bus < FuriHalBusMAX); + const uint32_t value = furi_hal_bus[bus]; + if(!value) { + return; + } + + FURI_CRITICAL_ENTER(); + if(bus < FuriHalBusAHB2_GRP1) { + // furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(AHB1, value)); + FURI_HAL_BUS_PERIPH_RESET(AHB1, value, 1); + } else if(bus < FuriHalBusAHB3_GRP1) { + // furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(AHB2, value)); + FURI_HAL_BUS_PERIPH_RESET(AHB2, value, 1); + } else if(bus < FuriHalBusAPB1_GRP1) { + // furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(AHB3, value)); + FURI_HAL_BUS_PERIPH_RESET(AHB3, value, 1); + } else if(bus < FuriHalBusAPB1_GRP2) { + furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(APB1, value, 1)); + FURI_HAL_BUS_PERIPH_RESET(APB1, value, 1); + } else if(bus < FuriHalBusAPB2_GRP1) { + furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(APB1, value, 2)); + FURI_HAL_BUS_PERIPH_RESET(APB1, value, 2); + } else if(bus < FuriHalBusAPB3_GRP1) { + furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(APB2, value)); + FURI_HAL_BUS_PERIPH_RESET(APB2, value, 1); + } else { + furi_check(FURI_HAL_BUS_IS_RESET_DEASSERTED(APB3, value)); + FURI_HAL_BUS_PERIPH_RESET(APB3, value, 1); + } + FURI_CRITICAL_EXIT(); +} + +void furi_hal_bus_disable(FuriHalBus bus) { + furi_check(bus < FuriHalBusMAX); + const uint32_t value = furi_hal_bus[bus]; + if(!value) { + return; + } + + FURI_CRITICAL_ENTER(); + if(bus < FuriHalBusAHB2_GRP1) { + // furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(AHB1, value)); + FURI_HAL_BUS_PERIPH_DISABLE(AHB1, value, 1); + } else if(bus < FuriHalBusAHB3_GRP1) { + // furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(AHB2, value)); + FURI_HAL_BUS_PERIPH_DISABLE(AHB2, value, 1); + } else if(bus < FuriHalBusAPB1_GRP1) { + // furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(AHB3, value)); + FURI_HAL_BUS_PERIPH_DISABLE(AHB3, value, 1); + } else if(bus < FuriHalBusAPB1_GRP2) { + furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(APB1, value, 1)); + FURI_HAL_BUS_PERIPH_DISABLE(APB1, value, 1); + } else if(bus < FuriHalBusAPB2_GRP1) { + furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(APB1, value, 2)); + FURI_HAL_BUS_PERIPH_DISABLE(APB1, value, 2); + } else if(bus < FuriHalBusAPB3_GRP1) { + furi_check(FURI_HAL_BUS_IS_PERIPH_ENABLED(APB2, value)); + FURI_HAL_BUS_PERIPH_DISABLE(APB2, value, 1); + } else { + furi_check(FURI_HAL_BUS_IS_RESET_DEASSERTED(APB3, value)); + FURI_HAL_BUS_RESET_ASSERT(APB3, FURI_HAL_BUS_APB3_GRP1, 1); + } + FURI_CRITICAL_EXIT(); +} + +bool furi_hal_bus_is_enabled(FuriHalBus bus) { + furi_check(bus < FuriHalBusMAX); + const uint32_t value = furi_hal_bus[bus]; + if(value == FURI_HAL_BUS_IGNORE) { + return true; + } + + bool ret = false; + FURI_CRITICAL_ENTER(); + if(bus < FuriHalBusAHB2_GRP1) { + ret = FURI_HAL_BUS_IS_PERIPH_ENABLED(AHB1, value); + } else if(bus < FuriHalBusAHB3_GRP1) { + ret = FURI_HAL_BUS_IS_PERIPH_ENABLED(AHB2, value); + } else if(bus < FuriHalBusAPB1_GRP1) { + ret = FURI_HAL_BUS_IS_PERIPH_ENABLED(AHB3, value); + } else if(bus < FuriHalBusAPB1_GRP2) { + ret = FURI_HAL_BUS_IS_PERIPH_ENABLED(APB1, value, 1); + } else if(bus < FuriHalBusAPB2_GRP1) { + ret = FURI_HAL_BUS_IS_PERIPH_ENABLED(APB1, value, 2); + } else if(bus < FuriHalBusAPB3_GRP1) { + ret = FURI_HAL_BUS_IS_PERIPH_ENABLED(APB2, value); + } else { + ret = FURI_HAL_BUS_IS_RESET_DEASSERTED(APB3, value); + } + FURI_CRITICAL_EXIT(); + + return ret; +} diff --git a/targets/f7/furi_hal/furi_hal_bus.h b/targets/f7/furi_hal/furi_hal_bus.h new file mode 100644 index 00000000000..ad4bbec3249 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_bus.h @@ -0,0 +1,112 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "stm32wbxx.h" +#include "stdbool.h" + +typedef enum { + FuriHalBusAHB1_GRP1, + FuriHalBusDMA1, + FuriHalBusDMA2, + FuriHalBusDMAMUX1, + FuriHalBusCRC, + FuriHalBusTSC, + + FuriHalBusAHB2_GRP1, + FuriHalBusGPIOA, + FuriHalBusGPIOB, + FuriHalBusGPIOC, + FuriHalBusGPIOD, + FuriHalBusGPIOE, + FuriHalBusGPIOH, +#if defined(ADC_SUPPORT_5_MSPS) + FuriHalBusADC, +#endif + FuriHalBusAES1, + + FuriHalBusAHB3_GRP1, + FuriHalBusQUADSPI, + FuriHalBusPKA, + FuriHalBusAES2, + FuriHalBusRNG, + FuriHalBusHSEM, + FuriHalBusIPCC, + FuriHalBusFLASH, + + FuriHalBusAPB1_GRP1, + FuriHalBusTIM2, + FuriHalBusLCD, + FuriHalBusSPI2, + FuriHalBusI2C1, + FuriHalBusI2C3, + FuriHalBusCRS, + FuriHalBusUSB, + FuriHalBusLPTIM1, + + FuriHalBusAPB1_GRP2, + FuriHalBusLPUART1, + FuriHalBusLPTIM2, + + FuriHalBusAPB2_GRP1, +#if defined(ADC_SUPPORT_2_5_MSPS) + FuriHalBusADC, +#endif + FuriHalBusTIM1, + FuriHalBusSPI1, + FuriHalBusUSART1, + FuriHalBusTIM16, + FuriHalBusTIM17, + FuriHalBusSAI1, + + FuriHalBusAPB3_GRP1, + FuriHalBusRF, + + FuriHalBusMAX, +} FuriHalBus; + +/** Early initialization */ +void furi_hal_bus_init_early(); + +/** Early de-initialization */ +void furi_hal_bus_deinit_early(); + +/** + * Enable a peripheral by turning the clocking on and deasserting the reset. + * @param [in] bus Peripheral to be enabled. + * @warning Peripheral must be in disabled state in order to be enabled. + */ +void furi_hal_bus_enable(FuriHalBus bus); + +/** + * Reset a peripheral by sequentially asserting and deasserting the reset. + * @param [in] bus Peripheral to be reset. + * @warning Peripheral must be in enabled state in order to be reset. + */ +void furi_hal_bus_reset(FuriHalBus bus); + +/** + * Disable a peripheral by turning the clocking off and asserting the reset. + * @param [in] bus Peripheral to be disabled. + * @warning Peripheral must be in enabled state in order to be disabled. + */ +void furi_hal_bus_disable(FuriHalBus bus); + +/** Check if peripheral is enabled + * + * @warning FuriHalBusAPB3_GRP1 is a special group of shared peripherals, for + * core1 its clock is always on and the only status we can report is + * peripheral reset status. Check code and Reference Manual for + * details. + * + * @param[in] bus The peripheral to check + * + * @return true if enabled or always enabled, false otherwise + */ +bool furi_hal_bus_is_enabled(FuriHalBus bus); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/furi_hal/furi_hal_clock.c b/targets/f7/furi_hal/furi_hal_clock.c new file mode 100644 index 00000000000..945dc323b2f --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_clock.c @@ -0,0 +1,279 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define TAG "FuriHalClock" + +#define CPU_CLOCK_EARLY_HZ 4000000 +#define CPU_CLOCK_HSI16_HZ 16000000 +#define CPU_CLOCK_HSE_HZ 32000000 +#define CPU_CLOCK_PLL_HZ 64000000 + +#define TICK_INT_PRIORITY 15U +#define HS_CLOCK_IS_READY() (LL_RCC_HSE_IsReady() && LL_RCC_HSI_IsReady()) +#define LS_CLOCK_IS_READY() (LL_RCC_LSE_IsReady() && LL_RCC_LSI1_IsReady()) + +void furi_hal_clock_init_early() { + LL_SetSystemCoreClock(CPU_CLOCK_EARLY_HZ); + LL_Init1msTick(SystemCoreClock); +} + +void furi_hal_clock_deinit_early() { +} + +void furi_hal_clock_init() { + /* HSE and HSI configuration and activation */ + LL_RCC_HSE_SetCapacitorTuning(0x26); + LL_RCC_HSE_Enable(); + LL_RCC_HSI_Enable(); + while(!HS_CLOCK_IS_READY()) + ; + /* Select HSI as system clock source after Wake Up from Stop mode + * Must be set before enabling CSS */ + LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI); + + LL_RCC_HSE_EnableCSS(); + + /* LSE and LSI1 configuration and activation */ + LL_PWR_EnableBkUpAccess(); + LL_RCC_LSE_SetDriveCapability(LL_RCC_LSEDRIVE_HIGH); + LL_RCC_LSE_Enable(); + LL_RCC_LSI1_Enable(); + while(!LS_CLOCK_IS_READY()) + ; + + LL_EXTI_EnableIT_0_31( + LL_EXTI_LINE_18); /* Why? Because that's why. See RM0434, Table 61. CPU1 vector table. */ + LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_18); + LL_RCC_EnableIT_LSECSS(); + /* ES0394, extended case of 2.2.2 */ + if(!LL_RCC_IsActiveFlag_BORRST()) { + LL_RCC_LSE_EnableCSS(); + } + + /* Main PLL configuration and activation */ + LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_2, 8, LL_RCC_PLLR_DIV_2); + LL_RCC_PLL_Enable(); + LL_RCC_PLL_EnableDomain_SYS(); + while(LL_RCC_PLL_IsReady() != 1) + ; + + LL_RCC_PLLSAI1_ConfigDomain_48M( + LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_2, 6, LL_RCC_PLLSAI1Q_DIV_2); + LL_RCC_PLLSAI1_ConfigDomain_ADC( + LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_2, 6, LL_RCC_PLLSAI1R_DIV_2); + LL_RCC_PLLSAI1_Enable(); + LL_RCC_PLLSAI1_EnableDomain_48M(); + LL_RCC_PLLSAI1_EnableDomain_ADC(); + while(LL_RCC_PLLSAI1_IsReady() != 1) + ; + + /* Sysclk activation on the main PLL */ + /* Set CPU1 prescaler */ + LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); + + /* Set CPU2 prescaler, from this point we are not allowed to touch it. */ + LL_C2_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_2); + + /* Prepare Flash memory for work on 64MHz system clock */ + LL_FLASH_SetLatency(LL_FLASH_LATENCY_3); + while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_3) + ; + + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); + while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) + ; + + /* Set AHB SHARED prescaler*/ + LL_RCC_SetAHB4Prescaler(LL_RCC_SYSCLK_DIV_1); + + /* Set APB1 prescaler*/ + LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1); + + /* Set APB2 prescaler*/ + LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1); + + /* Disable MSI */ + LL_RCC_MSI_Disable(); + while(LL_RCC_MSI_IsReady() != 0) + ; + + /* Update CMSIS variable (which can be updated also through SystemCoreClockUpdate function) */ + LL_SetSystemCoreClock(CPU_CLOCK_PLL_HZ); + + /* Update the time base */ + LL_Init1msTick(SystemCoreClock); + LL_SYSTICK_EnableIT(); + NVIC_SetPriority( + SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), TICK_INT_PRIORITY, 0)); + NVIC_EnableIRQ(SysTick_IRQn); + + LL_RCC_SetCLK48ClockSource(LL_RCC_CLK48_CLKSOURCE_PLLSAI1); + LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSI); + LL_RCC_SetSMPSPrescaler(LL_RCC_SMPS_DIV_1); + LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSE); + + FURI_LOG_I(TAG, "Init OK"); +} + +void furi_hal_clock_switch_hse2hsi() { + LL_RCC_HSI_Enable(); + + while(!LL_RCC_HSI_IsReady()) + ; + + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI); + furi_assert(LL_RCC_GetSMPSClockSelection() == LL_RCC_SMPS_CLKSOURCE_HSI); + + while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI) + ; + + LL_FLASH_SetLatency(LL_FLASH_LATENCY_0); + while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_0) + ; +} + +void furi_hal_clock_switch_hsi2hse() { +#ifdef FURI_HAL_CLOCK_TRACK_STARTUP + uint32_t clock_start_time = DWT->CYCCNT; +#endif + + LL_RCC_HSE_Enable(); + while(!LL_RCC_HSE_IsReady()) + ; + + LL_FLASH_SetLatency(LL_FLASH_LATENCY_1); + while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_1) + ; + + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSE); + + while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSE) + ; + +#ifdef FURI_HAL_CLOCK_TRACK_STARTUP + uint32_t total = DWT->CYCCNT - clock_start_time; + if(total > (20 * 0x148)) { + furi_crash("Slow HSE/PLL startup"); + } +#endif +} + +bool furi_hal_clock_switch_hse2pll() { + furi_assert(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_HSE); + + LL_RCC_PLL_Enable(); + LL_RCC_PLLSAI1_Enable(); + + while(!LL_RCC_PLL_IsReady()) + ; + while(!LL_RCC_PLLSAI1_IsReady()) + ; + + if(SHCI_C2_SetSystemClock(SET_SYSTEM_CLOCK_HSE_TO_PLL) != SHCI_Success) { + return false; + } + + furi_check(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_PLL); + + LL_SetSystemCoreClock(CPU_CLOCK_PLL_HZ); + SysTick->LOAD = (uint32_t)((SystemCoreClock / 1000) - 1UL); + + return true; +} + +bool furi_hal_clock_switch_pll2hse() { + furi_assert(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_PLL); + + LL_RCC_HSE_Enable(); + while(!LL_RCC_HSE_IsReady()) + ; + + if(SHCI_C2_SetSystemClock(SET_SYSTEM_CLOCK_PLL_ON_TO_HSE) != SHCI_Success) { + return false; + } + + furi_check(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_HSE); + + LL_SetSystemCoreClock(CPU_CLOCK_HSE_HZ); + SysTick->LOAD = (uint32_t)((SystemCoreClock / 1000) - 1UL); + + return true; +} + +void furi_hal_clock_suspend_tick() { + CLEAR_BIT(SysTick->CTRL, SysTick_CTRL_ENABLE_Msk); +} + +void furi_hal_clock_resume_tick() { + SET_BIT(SysTick->CTRL, SysTick_CTRL_ENABLE_Msk); +} + +void furi_hal_clock_mco_enable(FuriHalClockMcoSourceId source, FuriHalClockMcoDivisorId div) { + if(source == FuriHalClockMcoLse) { + LL_RCC_ConfigMCO(LL_RCC_MCO1SOURCE_LSE, div); + } else if(source == FuriHalClockMcoSysclk) { + LL_RCC_ConfigMCO(LL_RCC_MCO1SOURCE_SYSCLK, div); + } else { + LL_RCC_MSI_Enable(); + while(LL_RCC_MSI_IsReady() != 1) + ; + switch(source) { + case FuriHalClockMcoMsi100k: + LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_0); + break; + case FuriHalClockMcoMsi200k: + LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_1); + break; + case FuriHalClockMcoMsi400k: + LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_2); + break; + case FuriHalClockMcoMsi800k: + LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_3); + break; + case FuriHalClockMcoMsi1m: + LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_4); + break; + case FuriHalClockMcoMsi2m: + LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_5); + break; + case FuriHalClockMcoMsi4m: + LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_6); + break; + case FuriHalClockMcoMsi8m: + LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_7); + break; + case FuriHalClockMcoMsi16m: + LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_8); + break; + case FuriHalClockMcoMsi24m: + LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_9); + break; + case FuriHalClockMcoMsi32m: + LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_10); + break; + case FuriHalClockMcoMsi48m: + LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_11); + break; + default: + break; + } + LL_RCC_ConfigMCO(LL_RCC_MCO1SOURCE_MSI, div); + } +} + +void furi_hal_clock_mco_disable() { + LL_RCC_ConfigMCO(LL_RCC_MCO1SOURCE_NOCLOCK, FuriHalClockMcoDiv1); + LL_RCC_MSI_Disable(); + while(LL_RCC_MSI_IsReady() != 0) + ; +} diff --git a/targets/f7/furi_hal/furi_hal_clock.h b/targets/f7/furi_hal/furi_hal_clock.h new file mode 100644 index 00000000000..3100b619f60 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_clock.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + FuriHalClockMcoLse, + FuriHalClockMcoSysclk, + FuriHalClockMcoMsi100k, + FuriHalClockMcoMsi200k, + FuriHalClockMcoMsi400k, + FuriHalClockMcoMsi800k, + FuriHalClockMcoMsi1m, + FuriHalClockMcoMsi2m, + FuriHalClockMcoMsi4m, + FuriHalClockMcoMsi8m, + FuriHalClockMcoMsi16m, + FuriHalClockMcoMsi24m, + FuriHalClockMcoMsi32m, + FuriHalClockMcoMsi48m, +} FuriHalClockMcoSourceId; + +typedef enum { + FuriHalClockMcoDiv1 = LL_RCC_MCO1_DIV_1, + FuriHalClockMcoDiv2 = LL_RCC_MCO1_DIV_2, + FuriHalClockMcoDiv4 = LL_RCC_MCO1_DIV_4, + FuriHalClockMcoDiv8 = LL_RCC_MCO1_DIV_8, + FuriHalClockMcoDiv16 = LL_RCC_MCO1_DIV_16, +} FuriHalClockMcoDivisorId; + +/** Early initialization */ +void furi_hal_clock_init_early(); + +/** Early deinitialization */ +void furi_hal_clock_deinit_early(); + +/** Initialize clocks */ +void furi_hal_clock_init(); + +/** Switch clock from HSE to HSI */ +void furi_hal_clock_switch_hse2hsi(); + +/** Switch clock from HSI to HSE */ +void furi_hal_clock_switch_hsi2hse(); + +/** Switch clock from HSE to PLL + * + * @return true if changed, false if failed or not possible at this moment + */ +bool furi_hal_clock_switch_hse2pll(); + +/** Switch clock from PLL to HSE + * + * @return true if changed, false if failed or not possible at this moment + */ +bool furi_hal_clock_switch_pll2hse(); + +/** Stop SysTick counter without resetting */ +void furi_hal_clock_suspend_tick(); + +/** Continue SysTick counter operation */ +void furi_hal_clock_resume_tick(); + +/** Enable clock output on MCO pin + * + * @param source MCO clock source + * @param div MCO clock division +*/ +void furi_hal_clock_mco_enable(FuriHalClockMcoSourceId source, FuriHalClockMcoDivisorId div); + +/** Disable clock output on MCO pin */ +void furi_hal_clock_mco_disable(); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_console.c b/targets/f7/furi_hal/furi_hal_console.c similarity index 91% rename from firmware/targets/f7/furi_hal/furi_hal_console.c rename to targets/f7/furi_hal/furi_hal_console.c index e5db927bfe1..0b113d2dac5 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_console.c +++ b/targets/f7/furi_hal/furi_hal_console.c @@ -4,9 +4,6 @@ #include #include #include -#include - -#include #include @@ -88,13 +85,13 @@ void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size } void furi_hal_console_printf(const char format[], ...) { - string_t string; + FuriString* string; va_list args; va_start(args, format); - string_init_vprintf(string, format, args); + string = furi_string_alloc_vprintf(format, args); va_end(args); - furi_hal_console_tx((const uint8_t*)string_get_cstr(string), string_size(string)); - string_clear(string); + furi_hal_console_tx((const uint8_t*)furi_string_get_cstr(string), furi_string_size(string)); + furi_string_free(string); } void furi_hal_console_puts(const char* data) { diff --git a/firmware/targets/f7/furi_hal/furi_hal_console.h b/targets/f7/furi_hal/furi_hal_console.h similarity index 86% rename from firmware/targets/f7/furi_hal/furi_hal_console.h rename to targets/f7/furi_hal/furi_hal_console.h index 104515ce9ce..ce31a66b33f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_console.h +++ b/targets/f7/furi_hal/furi_hal_console.h @@ -2,6 +2,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -27,7 +28,7 @@ void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size * @param format * @param ... */ -void furi_hal_console_printf(const char format[], ...); +void furi_hal_console_printf(const char format[], ...) _ATTRIBUTE((__format__(__printf__, 1, 2))); void furi_hal_console_puts(const char* data); diff --git a/targets/f7/furi_hal/furi_hal_cortex.c b/targets/f7/furi_hal/furi_hal_cortex.c new file mode 100644 index 00000000000..6b5efc376c0 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_cortex.c @@ -0,0 +1,114 @@ +#include +#include + +#include + +#define FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND (SystemCoreClock / 1000000) + +void furi_hal_cortex_init_early() { + CoreDebug->DEMCR |= (CoreDebug_DEMCR_TRCENA_Msk | CoreDebug_DEMCR_MON_EN_Msk); + DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; + DWT->CYCCNT = 0U; + + /* Enable instruction prefetch */ + SET_BIT(FLASH->ACR, FLASH_ACR_PRFTEN); +} + +void furi_hal_cortex_delay_us(uint32_t microseconds) { + furi_check(microseconds < (UINT32_MAX / FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND)); + + uint32_t start = DWT->CYCCNT; + uint32_t time_ticks = FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND * microseconds; + + while((DWT->CYCCNT - start) < time_ticks) { + }; +} + +uint32_t furi_hal_cortex_instructions_per_microsecond() { + return FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND; +} + +FuriHalCortexTimer furi_hal_cortex_timer_get(uint32_t timeout_us) { + furi_check(timeout_us < (UINT32_MAX / FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND)); + + FuriHalCortexTimer cortex_timer = {0}; + cortex_timer.start = DWT->CYCCNT; + cortex_timer.value = FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND * timeout_us; + return cortex_timer; +} + +bool furi_hal_cortex_timer_is_expired(FuriHalCortexTimer cortex_timer) { + return !((DWT->CYCCNT - cortex_timer.start) < cortex_timer.value); +} + +void furi_hal_cortex_timer_wait(FuriHalCortexTimer cortex_timer) { + while(!furi_hal_cortex_timer_is_expired(cortex_timer)) + ; +} + +// Duck ST +#undef COMP0 +#undef COMP1 +#undef COMP2 +#undef COMP3 + +void furi_hal_cortex_comp_enable( + FuriHalCortexComp comp, + FuriHalCortexCompFunction function, + uint32_t value, + uint32_t mask, + FuriHalCortexCompSize size) { + uint32_t function_reg = (uint32_t)function | ((uint32_t)size << 10); + + switch(comp) { + case FuriHalCortexComp0: + (DWT->COMP0) = value; + (DWT->MASK0) = mask; + (DWT->FUNCTION0) = function_reg; + break; + case FuriHalCortexComp1: + (DWT->COMP1) = value; + (DWT->MASK1) = mask; + (DWT->FUNCTION1) = function_reg; + break; + case FuriHalCortexComp2: + (DWT->COMP2) = value; + (DWT->MASK2) = mask; + (DWT->FUNCTION2) = function_reg; + break; + case FuriHalCortexComp3: + (DWT->COMP3) = value; + (DWT->MASK3) = mask; + (DWT->FUNCTION3) = function_reg; + break; + default: + furi_crash("Invalid parameter"); + } +} + +void furi_hal_cortex_comp_reset(FuriHalCortexComp comp) { + switch(comp) { + case FuriHalCortexComp0: + (DWT->COMP0) = 0; + (DWT->MASK0) = 0; + (DWT->FUNCTION0) = 0; + break; + case FuriHalCortexComp1: + (DWT->COMP1) = 0; + (DWT->MASK1) = 0; + (DWT->FUNCTION1) = 0; + break; + case FuriHalCortexComp2: + (DWT->COMP2) = 0; + (DWT->MASK2) = 0; + (DWT->FUNCTION2) = 0; + break; + case FuriHalCortexComp3: + (DWT->COMP3) = 0; + (DWT->MASK3) = 0; + (DWT->FUNCTION3) = 0; + break; + default: + furi_crash("Invalid parameter"); + } +} diff --git a/targets/f7/furi_hal/furi_hal_crypto.c b/targets/f7/furi_hal/furi_hal_crypto.c new file mode 100644 index 00000000000..a897648a354 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_crypto.c @@ -0,0 +1,732 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#define TAG "FuriHalCrypto" + +#define ENCLAVE_FACTORY_KEY_SLOTS 10 +#define ENCLAVE_SIGNATURE_SIZE 16 + +#define CRYPTO_BLK_LEN (4 * sizeof(uint32_t)) +#define CRYPTO_TIMEOUT_US (1000000) + +#define CRYPTO_MODE_ENCRYPT 0U +#define CRYPTO_MODE_INIT (AES_CR_MODE_0) +#define CRYPTO_MODE_DECRYPT (AES_CR_MODE_1) +#define CRYPTO_MODE_DECRYPT_INIT (AES_CR_MODE_0 | AES_CR_MODE_1) + +#define CRYPTO_DATATYPE_32B 0U +#define CRYPTO_KEYSIZE_256B (AES_CR_KEYSIZE) +#define CRYPTO_AES_CBC (AES_CR_CHMOD_0) + +#define CRYPTO_AES_CTR (AES_CR_CHMOD_1) +#define CRYPTO_CTR_IV_LEN (12U) +#define CRYPTO_CTR_CTR_LEN (4U) + +#define CRYPTO_AES_GCM (AES_CR_CHMOD_1 | AES_CR_CHMOD_0) +#define CRYPTO_GCM_IV_LEN (12U) +#define CRYPTO_GCM_CTR_LEN (4U) +#define CRYPTO_GCM_TAG_LEN (16U) +#define CRYPTO_GCM_PH_INIT (0x0U << AES_CR_GCMPH_Pos) +#define CRYPTO_GCM_PH_HEADER (AES_CR_GCMPH_0) +#define CRYPTO_GCM_PH_PAYLOAD (AES_CR_GCMPH_1) +#define CRYPTO_GCM_PH_FINAL (AES_CR_GCMPH_1 | AES_CR_GCMPH_0) + +static FuriMutex* furi_hal_crypto_mutex = NULL; +static bool furi_hal_crypto_mode_init_done = false; + +static const uint8_t enclave_signature_iv[ENCLAVE_FACTORY_KEY_SLOTS][16] = { + {0xac, 0x5d, 0x68, 0xb8, 0x79, 0x74, 0xfc, 0x7f, 0x45, 0x02, 0x82, 0xf1, 0x48, 0x7e, 0x75, 0x8a}, + {0x38, 0xe6, 0x6a, 0x90, 0x5e, 0x5b, 0x8a, 0xa6, 0x70, 0x30, 0x04, 0x72, 0xc2, 0x42, 0xea, 0xaf}, + {0x73, 0xd5, 0x8e, 0xfb, 0x0f, 0x4b, 0xa9, 0x79, 0x0f, 0xde, 0x0e, 0x53, 0x44, 0x7d, 0xaa, 0xfd}, + {0x3c, 0x9a, 0xf4, 0x43, 0x2b, 0xfe, 0xea, 0xae, 0x8c, 0xc6, 0xd1, 0x60, 0xd2, 0x96, 0x64, 0xa9}, + {0x10, 0xac, 0x7b, 0x63, 0x03, 0x7f, 0x43, 0x18, 0xec, 0x9d, 0x9c, 0xc4, 0x01, 0xdc, 0x35, 0xa7}, + {0x26, 0x21, 0x64, 0xe6, 0xd0, 0xf2, 0x47, 0x49, 0xdc, 0x36, 0xcd, 0x68, 0x0c, 0x91, 0x03, 0x44}, + {0x7a, 0xbd, 0xce, 0x9c, 0x24, 0x7a, 0x2a, 0xb1, 0x3c, 0x4f, 0x5a, 0x7d, 0x80, 0x3e, 0xfc, 0x0d}, + {0xcd, 0xdd, 0xd3, 0x02, 0x85, 0x65, 0x43, 0x83, 0xf9, 0xac, 0x75, 0x2f, 0x21, 0xef, 0x28, 0x6b}, + {0xab, 0x73, 0x70, 0xe8, 0xe2, 0x56, 0x0f, 0x58, 0xab, 0x29, 0xa5, 0xb1, 0x13, 0x47, 0x5e, 0xe8}, + {0x4f, 0x3c, 0x43, 0x77, 0xde, 0xed, 0x79, 0xa1, 0x8d, 0x4c, 0x1f, 0xfd, 0xdb, 0x96, 0x87, 0x2e}, +}; + +static const uint8_t enclave_signature_input[ENCLAVE_FACTORY_KEY_SLOTS][ENCLAVE_SIGNATURE_SIZE] = { + {0x9f, 0x5c, 0xb1, 0x43, 0x17, 0x53, 0x18, 0x8c, 0x66, 0x3d, 0x39, 0x45, 0x90, 0x13, 0xa9, 0xde}, + {0xc5, 0x98, 0xe9, 0x17, 0xb8, 0x97, 0x9e, 0x03, 0x33, 0x14, 0x13, 0x8f, 0xce, 0x74, 0x0d, 0x54}, + {0x34, 0xba, 0x99, 0x59, 0x9f, 0x70, 0x67, 0xe9, 0x09, 0xee, 0x64, 0x0e, 0xb3, 0xba, 0xfb, 0x75}, + {0xdc, 0xfa, 0x6c, 0x9a, 0x6f, 0x0a, 0x3e, 0xdc, 0x42, 0xf6, 0xae, 0x0d, 0x3c, 0xf7, 0x83, 0xaf}, + {0xea, 0x2d, 0xe3, 0x1f, 0x02, 0x99, 0x1a, 0x7e, 0x6d, 0x93, 0x4c, 0xb5, 0x42, 0xf0, 0x7a, 0x9b}, + {0x53, 0x5e, 0x04, 0xa2, 0x49, 0xa0, 0x73, 0x49, 0x56, 0xb0, 0x88, 0x8c, 0x12, 0xa0, 0xe4, 0x18}, + {0x7d, 0xa7, 0xc5, 0x21, 0x7f, 0x12, 0x95, 0xdd, 0x4d, 0x77, 0x01, 0xfa, 0x71, 0x88, 0x2b, 0x7f}, + {0xdc, 0x9b, 0xc5, 0xa7, 0x6b, 0x84, 0x5c, 0x37, 0x7c, 0xec, 0x05, 0xa1, 0x9f, 0x91, 0x17, 0x3b}, + {0xea, 0xcf, 0xd9, 0x9b, 0x86, 0xcd, 0x2b, 0x43, 0x54, 0x45, 0x82, 0xc6, 0xfe, 0x73, 0x1a, 0x1a}, + {0x77, 0xb8, 0x1b, 0x90, 0xb4, 0xb7, 0x32, 0x76, 0x8f, 0x8a, 0x57, 0x06, 0xc7, 0xdd, 0x08, 0x90}, +}; + +static const uint8_t enclave_signature_expected[ENCLAVE_FACTORY_KEY_SLOTS][ENCLAVE_SIGNATURE_SIZE] = { + {0xe9, 0x9a, 0xce, 0xe9, 0x4d, 0xe1, 0x7f, 0x55, 0xcb, 0x8a, 0xbf, 0xf2, 0x4d, 0x98, 0x27, 0x67}, + {0x34, 0x27, 0xa7, 0xea, 0xa8, 0x98, 0x66, 0x9b, 0xed, 0x43, 0xd3, 0x93, 0xb5, 0xa2, 0x87, 0x8e}, + {0x6c, 0xf3, 0x01, 0x78, 0x53, 0x1b, 0x11, 0x32, 0xf0, 0x27, 0x2f, 0xe3, 0x7d, 0xa6, 0xe2, 0xfd}, + {0xdf, 0x7f, 0x37, 0x65, 0x2f, 0xdb, 0x7c, 0xcf, 0x5b, 0xb6, 0xe4, 0x9c, 0x63, 0xc5, 0x0f, 0xe0}, + {0x9b, 0x5c, 0xee, 0x44, 0x0e, 0xd1, 0xcb, 0x5f, 0x28, 0x9f, 0x12, 0x17, 0x59, 0x64, 0x40, 0xbb}, + {0x94, 0xc2, 0x09, 0x98, 0x62, 0xa7, 0x2b, 0x93, 0xed, 0x36, 0x1f, 0x10, 0xbc, 0x26, 0xbd, 0x41}, + {0x4d, 0xb2, 0x2b, 0xc5, 0x96, 0x47, 0x61, 0xf4, 0x16, 0xe0, 0x81, 0xc3, 0x8e, 0xb9, 0x9c, 0x9b}, + {0xc3, 0x6b, 0x83, 0x55, 0x90, 0x38, 0x0f, 0xea, 0xd1, 0x65, 0xbf, 0x32, 0x4f, 0x8e, 0x62, 0x5b}, + {0x8d, 0x5e, 0x27, 0xbc, 0x14, 0x4f, 0x08, 0xa8, 0x2b, 0x14, 0x89, 0x5e, 0xdf, 0x77, 0x04, 0x31}, + {0xc9, 0xf7, 0x03, 0xf1, 0x6c, 0x65, 0xad, 0x49, 0x74, 0xbe, 0x00, 0x54, 0xfd, 0xa6, 0x9c, 0x32}, +}; + +void furi_hal_crypto_init() { + furi_hal_crypto_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + FURI_LOG_I(TAG, "Init OK"); +} + +static bool furi_hal_crypto_generate_unique_keys(uint8_t start_slot, uint8_t end_slot) { + FuriHalCryptoKey key; + uint8_t key_data[32]; + FURI_LOG_I(TAG, "Generating keys %u..%u", start_slot, end_slot); + for(uint8_t slot = start_slot; slot <= end_slot; slot++) { + key.type = FuriHalCryptoKeyTypeSimple; + key.size = FuriHalCryptoKeySize256; + key.data = key_data; + furi_hal_random_fill_buf(key_data, 32); + if(!furi_hal_crypto_enclave_store_key(&key, &slot)) { + FURI_LOG_E(TAG, "Error writing key to slot %u", slot); + return false; + } + } + return true; +} + +bool furi_hal_crypto_enclave_ensure_key(uint8_t key_slot) { + uint8_t keys_nb = 0; + uint8_t valid_keys_nb = 0; + uint8_t last_valid_slot = ENCLAVE_FACTORY_KEY_SLOTS; + uint8_t empty_iv[16] = {0}; + furi_hal_crypto_enclave_verify(&keys_nb, &valid_keys_nb); + if(key_slot <= ENCLAVE_FACTORY_KEY_SLOTS) { // It's a factory key + if(key_slot > keys_nb) return false; + } else { // Unique key + if(keys_nb < ENCLAVE_FACTORY_KEY_SLOTS) // Some factory keys are missing + return false; + for(uint8_t i = key_slot; i > ENCLAVE_FACTORY_KEY_SLOTS; i--) { + if(furi_hal_crypto_enclave_load_key(i, empty_iv)) { + last_valid_slot = i; + furi_hal_crypto_enclave_unload_key(i); + break; + } + } + if(last_valid_slot == key_slot) + return true; + else // Generate missing unique keys + return furi_hal_crypto_generate_unique_keys(last_valid_slot + 1, key_slot); + } + return true; +} + +bool furi_hal_crypto_enclave_verify(uint8_t* keys_nb, uint8_t* valid_keys_nb) { + furi_assert(keys_nb); + furi_assert(valid_keys_nb); + uint8_t keys = 0; + uint8_t keys_valid = 0; + uint8_t buffer[ENCLAVE_SIGNATURE_SIZE]; + for(size_t key_slot = 0; key_slot < ENCLAVE_FACTORY_KEY_SLOTS; key_slot++) { + if(furi_hal_crypto_enclave_load_key(key_slot + 1, enclave_signature_iv[key_slot])) { + keys++; + if(furi_hal_crypto_encrypt( + enclave_signature_input[key_slot], buffer, ENCLAVE_SIGNATURE_SIZE)) { + keys_valid += + memcmp(buffer, enclave_signature_expected[key_slot], ENCLAVE_SIGNATURE_SIZE) == + 0; + } + furi_hal_crypto_enclave_unload_key(key_slot + 1); + } + } + *keys_nb = keys; + *valid_keys_nb = keys_valid; + if(*valid_keys_nb == ENCLAVE_FACTORY_KEY_SLOTS) + return true; + else + return false; +} + +bool furi_hal_crypto_enclave_store_key(FuriHalCryptoKey* key, uint8_t* slot) { + furi_assert(key); + furi_assert(slot); + + furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk); + + if(!furi_hal_bt_is_alive()) { + return false; + } + + SHCI_C2_FUS_StoreUsrKey_Cmd_Param_t pParam; + size_t key_data_size = 0; + + if(key->type == FuriHalCryptoKeyTypeMaster) { + pParam.KeyType = KEYTYPE_MASTER; + } else if(key->type == FuriHalCryptoKeyTypeSimple) { + pParam.KeyType = KEYTYPE_SIMPLE; + } else if(key->type == FuriHalCryptoKeyTypeEncrypted) { + pParam.KeyType = KEYTYPE_ENCRYPTED; + key_data_size += 12; + } else { + furi_crash("Incorrect key type"); + } + + if(key->size == FuriHalCryptoKeySize128) { + pParam.KeySize = KEYSIZE_16; + key_data_size += 16; + } else if(key->size == FuriHalCryptoKeySize256) { + pParam.KeySize = KEYSIZE_32; + key_data_size += 32; + } else { + furi_crash("Incorrect key size"); + } + + memcpy(pParam.KeyData, key->data, key_data_size); + + SHCI_CmdStatus_t shci_state = SHCI_C2_FUS_StoreUsrKey(&pParam, slot); + furi_check(furi_mutex_release(furi_hal_crypto_mutex) == FuriStatusOk); + return (shci_state == SHCI_Success); +} + +static void crypto_key_init(uint32_t* key, uint32_t* iv) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + MODIFY_REG( + AES1->CR, + AES_CR_DATATYPE | AES_CR_KEYSIZE | AES_CR_CHMOD, + CRYPTO_DATATYPE_32B | CRYPTO_KEYSIZE_256B | CRYPTO_AES_CBC); + + if(key != NULL) { + AES1->KEYR7 = key[0]; + AES1->KEYR6 = key[1]; + AES1->KEYR5 = key[2]; + AES1->KEYR4 = key[3]; + AES1->KEYR3 = key[4]; + AES1->KEYR2 = key[5]; + AES1->KEYR1 = key[6]; + AES1->KEYR0 = key[7]; + } + + AES1->IVR3 = iv[0]; + AES1->IVR2 = iv[1]; + AES1->IVR1 = iv[2]; + AES1->IVR0 = iv[3]; +} + +static bool furi_hal_crypto_wait_flag(uint32_t flag) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(CRYPTO_TIMEOUT_US); + while(!READ_BIT(AES1->SR, flag)) { + if(furi_hal_cortex_timer_is_expired(timer)) { + return false; + } + } + return true; +} + +static bool crypto_process_block(uint32_t* in, uint32_t* out, uint8_t blk_len) { + furi_check((blk_len <= 4) && (blk_len > 0)); + + for(uint8_t i = 0; i < 4; i++) { + if(i < blk_len) { + AES1->DINR = in[i]; + } else { + AES1->DINR = 0; + } + } + + if(!furi_hal_crypto_wait_flag(AES_SR_CCF)) { + return false; + } + + SET_BIT(AES1->CR, AES_CR_CCFC); + + uint32_t out_temp[4]; + for(uint8_t i = 0; i < 4; i++) { + out_temp[i] = AES1->DOUTR; + } + + memcpy(out, out_temp, blk_len * sizeof(uint32_t)); + return true; +} + +bool furi_hal_crypto_enclave_load_key(uint8_t slot, const uint8_t* iv) { + furi_assert(slot > 0 && slot <= 100); + furi_assert(furi_hal_crypto_mutex); + furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk); + + furi_hal_bus_enable(FuriHalBusAES1); + + if(!furi_hal_bt_is_alive()) { + return false; + } + + furi_hal_crypto_mode_init_done = false; + crypto_key_init(NULL, (uint32_t*)iv); + + if(SHCI_C2_FUS_LoadUsrKey(slot) == SHCI_Success) { + return true; + } else { + CLEAR_BIT(AES1->CR, AES_CR_EN); + furi_check(furi_mutex_release(furi_hal_crypto_mutex) == FuriStatusOk); + return false; + } +} + +bool furi_hal_crypto_enclave_unload_key(uint8_t slot) { + if(!furi_hal_bt_is_alive()) { + return false; + } + + CLEAR_BIT(AES1->CR, AES_CR_EN); + + SHCI_CmdStatus_t shci_state = SHCI_C2_FUS_UnloadUsrKey(slot); + furi_assert(shci_state == SHCI_Success); + + furi_hal_bus_disable(FuriHalBusAES1); + + furi_check(furi_mutex_release(furi_hal_crypto_mutex) == FuriStatusOk); + return (shci_state == SHCI_Success); +} + +bool furi_hal_crypto_load_key(const uint8_t* key, const uint8_t* iv) { + furi_assert(furi_hal_crypto_mutex); + furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk); + + furi_hal_bus_enable(FuriHalBusAES1); + + furi_hal_crypto_mode_init_done = false; + crypto_key_init((uint32_t*)key, (uint32_t*)iv); + + return true; +} + +bool furi_hal_crypto_unload_key(void) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + + furi_hal_bus_disable(FuriHalBusAES1); + + furi_check(furi_mutex_release(furi_hal_crypto_mutex) == FuriStatusOk); + return true; +} + +bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size) { + bool state = false; + + SET_BIT(AES1->CR, AES_CR_EN); + + MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_ENCRYPT); + + for(size_t i = 0; i < size; i += CRYPTO_BLK_LEN) { + size_t blk_len = size - i; + if(blk_len > CRYPTO_BLK_LEN) { + blk_len = CRYPTO_BLK_LEN; + } + state = crypto_process_block((uint32_t*)&input[i], (uint32_t*)&output[i], blk_len / 4); + if(state == false) { + break; + } + } + + CLEAR_BIT(AES1->CR, AES_CR_EN); + + return state; +} + +bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size) { + bool state = false; + + if(!furi_hal_crypto_mode_init_done) { + MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_INIT); + + SET_BIT(AES1->CR, AES_CR_EN); + + if(!furi_hal_crypto_wait_flag(AES_SR_CCF)) { + return false; + } + + SET_BIT(AES1->CR, AES_CR_CCFC); + + furi_hal_crypto_mode_init_done = true; + } + + MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_DECRYPT); + SET_BIT(AES1->CR, AES_CR_EN); + + for(size_t i = 0; i < size; i += CRYPTO_BLK_LEN) { + size_t blk_len = size - i; + if(blk_len > CRYPTO_BLK_LEN) { + blk_len = CRYPTO_BLK_LEN; + } + state = crypto_process_block((uint32_t*)&input[i], (uint32_t*)&output[i], blk_len / 4); + if(state == false) { + break; + } + } + + CLEAR_BIT(AES1->CR, AES_CR_EN); + + return state; +} + +static void crypto_key_init_bswap(uint32_t* key, uint32_t* iv, uint32_t chaining_mode) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + MODIFY_REG( + AES1->CR, + AES_CR_DATATYPE | AES_CR_KEYSIZE | AES_CR_CHMOD, + CRYPTO_DATATYPE_32B | CRYPTO_KEYSIZE_256B | chaining_mode); + + if(key != NULL) { + AES1->KEYR7 = __builtin_bswap32(key[0]); + AES1->KEYR6 = __builtin_bswap32(key[1]); + AES1->KEYR5 = __builtin_bswap32(key[2]); + AES1->KEYR4 = __builtin_bswap32(key[3]); + AES1->KEYR3 = __builtin_bswap32(key[4]); + AES1->KEYR2 = __builtin_bswap32(key[5]); + AES1->KEYR1 = __builtin_bswap32(key[6]); + AES1->KEYR0 = __builtin_bswap32(key[7]); + } + + AES1->IVR3 = __builtin_bswap32(iv[0]); + AES1->IVR2 = __builtin_bswap32(iv[1]); + AES1->IVR1 = __builtin_bswap32(iv[2]); + AES1->IVR0 = __builtin_bswap32(iv[3]); +} + +static bool + furi_hal_crypto_load_key_bswap(const uint8_t* key, const uint8_t* iv, uint32_t chaining_mode) { + furi_assert(furi_hal_crypto_mutex); + furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk); + + furi_hal_bus_enable(FuriHalBusAES1); + + crypto_key_init_bswap((uint32_t*)key, (uint32_t*)iv, chaining_mode); + + return true; +} + +static bool wait_for_crypto(void) { + if(!furi_hal_crypto_wait_flag(AES_SR_CCF)) { + return false; + } + + SET_BIT(AES1->CR, AES_CR_CCFC); + + return true; +} + +static bool furi_hal_crypto_process_block_bswap(const uint8_t* in, uint8_t* out, size_t bytes) { + uint32_t block[CRYPTO_BLK_LEN / 4]; + memset(block, 0, sizeof(block)); + + memcpy(block, in, bytes); + + block[0] = __builtin_bswap32(block[0]); + block[1] = __builtin_bswap32(block[1]); + block[2] = __builtin_bswap32(block[2]); + block[3] = __builtin_bswap32(block[3]); + + if(!crypto_process_block(block, block, CRYPTO_BLK_LEN / 4)) { + return false; + } + + block[0] = __builtin_bswap32(block[0]); + block[1] = __builtin_bswap32(block[1]); + block[2] = __builtin_bswap32(block[2]); + block[3] = __builtin_bswap32(block[3]); + + memcpy(out, block, bytes); + + return true; +} + +static bool furi_hal_crypto_process_block_no_read_bswap(const uint8_t* in, size_t bytes) { + uint32_t block[CRYPTO_BLK_LEN / 4]; + memset(block, 0, sizeof(block)); + + memcpy(block, in, bytes); + + AES1->DINR = __builtin_bswap32(block[0]); + AES1->DINR = __builtin_bswap32(block[1]); + AES1->DINR = __builtin_bswap32(block[2]); + AES1->DINR = __builtin_bswap32(block[3]); + + return wait_for_crypto(); +} + +static void furi_hal_crypto_ctr_prep_iv(uint8_t* iv) { + /* append counter to IV */ + iv[CRYPTO_CTR_IV_LEN] = 0; + iv[CRYPTO_CTR_IV_LEN + 1] = 0; + iv[CRYPTO_CTR_IV_LEN + 2] = 0; + iv[CRYPTO_CTR_IV_LEN + 3] = 1; +} + +static bool furi_hal_crypto_ctr_payload(const uint8_t* input, uint8_t* output, size_t length) { + SET_BIT(AES1->CR, AES_CR_EN); + MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_ENCRYPT); + + size_t last_block_bytes = length % CRYPTO_BLK_LEN; + + size_t i; + for(i = 0; i < length - last_block_bytes; i += CRYPTO_BLK_LEN) { + if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], CRYPTO_BLK_LEN)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + if(last_block_bytes > 0) { + if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], last_block_bytes)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + CLEAR_BIT(AES1->CR, AES_CR_EN); + return true; +} + +bool furi_hal_crypto_ctr( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* input, + uint8_t* output, + size_t length) { + /* prepare IV and counter */ + uint8_t iv_and_counter[CRYPTO_CTR_IV_LEN + CRYPTO_CTR_CTR_LEN]; + memcpy(iv_and_counter, iv, CRYPTO_CTR_IV_LEN); //-V1086 + furi_hal_crypto_ctr_prep_iv(iv_and_counter); + + /* load key and IV and set the mode to CTR */ + if(!furi_hal_crypto_load_key_bswap(key, iv_and_counter, CRYPTO_AES_CTR)) { + furi_hal_crypto_unload_key(); + return false; + } + + /* process the input and write to output */ + bool state = furi_hal_crypto_ctr_payload(input, output, length); + + furi_hal_crypto_unload_key(); + + return state; +} + +static void furi_hal_crypto_gcm_prep_iv(uint8_t* iv) { + /* append counter to IV */ + iv[CRYPTO_GCM_IV_LEN] = 0; + iv[CRYPTO_GCM_IV_LEN + 1] = 0; + iv[CRYPTO_GCM_IV_LEN + 2] = 0; + iv[CRYPTO_GCM_IV_LEN + 3] = 2; +} + +static bool furi_hal_crypto_gcm_init(bool decrypt) { + /* GCM init phase */ + + MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_INIT); + if(decrypt) { + MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_DECRYPT); + } else { + MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_ENCRYPT); + } + + SET_BIT(AES1->CR, AES_CR_EN); + + if(!wait_for_crypto()) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + + return true; +} + +static bool furi_hal_crypto_gcm_header(const uint8_t* aad, size_t aad_length) { + /* GCM header phase */ + + MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_HEADER); + SET_BIT(AES1->CR, AES_CR_EN); + + size_t last_block_bytes = aad_length % CRYPTO_BLK_LEN; + + size_t i; + for(i = 0; i < aad_length - last_block_bytes; i += CRYPTO_BLK_LEN) { + if(!furi_hal_crypto_process_block_no_read_bswap(&aad[i], CRYPTO_BLK_LEN)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + if(last_block_bytes > 0) { + if(!furi_hal_crypto_process_block_no_read_bswap(&aad[i], last_block_bytes)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + return true; +} + +static bool furi_hal_crypto_gcm_payload( + const uint8_t* input, + uint8_t* output, + size_t length, + bool decrypt) { + /* GCM payload phase */ + + MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_PAYLOAD); + SET_BIT(AES1->CR, AES_CR_EN); + + size_t last_block_bytes = length % CRYPTO_BLK_LEN; + + size_t i; + for(i = 0; i < length - last_block_bytes; i += CRYPTO_BLK_LEN) { + if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], CRYPTO_BLK_LEN)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + if(last_block_bytes > 0) { + if(!decrypt) { + MODIFY_REG( + AES1->CR, AES_CR_NPBLB, (CRYPTO_BLK_LEN - last_block_bytes) << AES_CR_NPBLB_Pos); + } + if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], last_block_bytes)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + return true; +} + +static bool furi_hal_crypto_gcm_finish(size_t aad_length, size_t payload_length, uint8_t* tag) { + /* GCM final phase */ + + MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_FINAL); + + uint32_t last_block[CRYPTO_BLK_LEN / 4]; + memset(last_block, 0, sizeof(last_block)); + last_block[1] = __builtin_bswap32((uint32_t)(aad_length * 8)); + last_block[3] = __builtin_bswap32((uint32_t)(payload_length * 8)); + + if(!furi_hal_crypto_process_block_bswap((uint8_t*)&last_block[0], tag, CRYPTO_BLK_LEN)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + + return true; +} + +static bool furi_hal_crypto_gcm_compare_tag(const uint8_t* tag1, const uint8_t* tag2) { + uint8_t diff = 0; + + size_t i; + for(i = 0; i < CRYPTO_GCM_TAG_LEN; i++) { + diff |= tag1[i] ^ tag2[i]; + } + + return (diff == 0); +} + +bool furi_hal_crypto_gcm( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + uint8_t* tag, + bool decrypt) { + /* GCM init phase */ + + /* prepare IV and counter */ + uint8_t iv_and_counter[CRYPTO_GCM_IV_LEN + CRYPTO_GCM_CTR_LEN]; + memcpy(iv_and_counter, iv, CRYPTO_GCM_IV_LEN); //-V1086 + furi_hal_crypto_gcm_prep_iv(iv_and_counter); + + /* load key and IV and set the mode to CTR */ + if(!furi_hal_crypto_load_key_bswap(key, iv_and_counter, CRYPTO_AES_GCM)) { + furi_hal_crypto_unload_key(); + return false; + } + + if(!furi_hal_crypto_gcm_init(decrypt)) { + furi_hal_crypto_unload_key(); + return false; + } + + /* GCM header phase */ + + if(aad_length > 0) { + if(!furi_hal_crypto_gcm_header(aad, aad_length)) { + furi_hal_crypto_unload_key(); + return false; + } + } + + /* GCM payload phase */ + + if(!furi_hal_crypto_gcm_payload(input, output, length, decrypt)) { + furi_hal_crypto_unload_key(); + return false; + } + + /* GCM final phase */ + + if(!furi_hal_crypto_gcm_finish(aad_length, length, tag)) { + furi_hal_crypto_unload_key(); + return false; + } + + furi_hal_crypto_unload_key(); + return true; +} + +FuriHalCryptoGCMState furi_hal_crypto_gcm_encrypt_and_tag( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + uint8_t* tag) { + if(!furi_hal_crypto_gcm(key, iv, aad, aad_length, input, output, length, tag, false)) { + memset(output, 0, length); + memset(tag, 0, CRYPTO_GCM_TAG_LEN); + return FuriHalCryptoGCMStateError; + } + + return FuriHalCryptoGCMStateOk; +} + +FuriHalCryptoGCMState furi_hal_crypto_gcm_decrypt_and_verify( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + const uint8_t* tag) { + uint8_t dtag[CRYPTO_GCM_TAG_LEN]; + + if(!furi_hal_crypto_gcm(key, iv, aad, aad_length, input, output, length, dtag, true)) { + memset(output, 0, length); + return FuriHalCryptoGCMStateError; + } + + if(!furi_hal_crypto_gcm_compare_tag(dtag, tag)) { + memset(output, 0, length); + return FuriHalCryptoGCMStateAuthFailure; + } + + return FuriHalCryptoGCMStateOk; +} diff --git a/targets/f7/furi_hal/furi_hal_debug.c b/targets/f7/furi_hal/furi_hal_debug.c new file mode 100644 index 00000000000..3dc03ea69d7 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_debug.c @@ -0,0 +1,41 @@ +#include + +#include +#include + +#include +#include + +volatile bool furi_hal_debug_gdb_session_active = false; + +void furi_hal_debug_enable() { + // Low power mode debug + LL_DBGMCU_EnableDBGSleepMode(); + LL_DBGMCU_EnableDBGStopMode(); + LL_DBGMCU_EnableDBGStandbyMode(); + LL_EXTI_EnableIT_32_63(LL_EXTI_LINE_48); + // SWD GPIO + furi_hal_gpio_init_ex( + &gpio_swdio, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn0JTMS_SWDIO); + furi_hal_gpio_init_ex( + &gpio_swclk, GpioModeAltFunctionPushPull, GpioPullDown, GpioSpeedLow, GpioAltFn0JTCK_SWCLK); +} + +void furi_hal_debug_disable() { + // Low power mode debug + LL_DBGMCU_DisableDBGSleepMode(); + LL_DBGMCU_DisableDBGStopMode(); + LL_DBGMCU_DisableDBGStandbyMode(); + LL_EXTI_DisableIT_32_63(LL_EXTI_LINE_48); + // SWD GPIO + furi_hal_gpio_init_simple(&gpio_swdio, GpioModeAnalog); + furi_hal_gpio_init_simple(&gpio_swclk, GpioModeAnalog); +} + +bool furi_hal_debug_is_gdb_session_active() { + return furi_hal_debug_gdb_session_active; +} \ No newline at end of file diff --git a/targets/f7/furi_hal/furi_hal_dma.c b/targets/f7/furi_hal/furi_hal_dma.c new file mode 100644 index 00000000000..a6a30d906da --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_dma.c @@ -0,0 +1,14 @@ +#include +#include + +void furi_hal_dma_init_early() { + furi_hal_bus_enable(FuriHalBusDMA1); + furi_hal_bus_enable(FuriHalBusDMA2); + furi_hal_bus_enable(FuriHalBusDMAMUX1); +} + +void furi_hal_dma_deinit_early() { + furi_hal_bus_disable(FuriHalBusDMA1); + furi_hal_bus_disable(FuriHalBusDMA2); + furi_hal_bus_disable(FuriHalBusDMAMUX1); +} diff --git a/targets/f7/furi_hal/furi_hal_dma.h b/targets/f7/furi_hal/furi_hal_dma.h new file mode 100644 index 00000000000..cadcc7733e0 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_dma.h @@ -0,0 +1,15 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** Early initialization */ +void furi_hal_dma_init_early(); + +/** Early de-initialization */ +void furi_hal_dma_deinit_early(); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/furi_hal/furi_hal_flash.c b/targets/f7/furi_hal/furi_hal_flash.c new file mode 100644 index 00000000000..7ac7a8bd12e --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_flash.c @@ -0,0 +1,563 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#define TAG "FuriHalFlash" + +#define FURI_HAL_CRITICAL_MSG "Critical flash operation fail" +#define FURI_HAL_FLASH_READ_BLOCK (8U) +#define FURI_HAL_FLASH_WRITE_BLOCK (8U) +#define FURI_HAL_FLASH_PAGE_SIZE (4096U) +#define FURI_HAL_FLASH_CYCLES_COUNT (10000U) +#define FURI_HAL_FLASH_TIMEOUT (1000U) +#define FURI_HAL_FLASH_KEY1 (0x45670123U) +#define FURI_HAL_FLASH_KEY2 (0xCDEF89ABU) +#define FURI_HAL_FLASH_TOTAL_PAGES (256U) +#define FURI_HAL_FLASH_SR_ERRORS \ + (FLASH_SR_OPERR | FLASH_SR_PROGERR | FLASH_SR_WRPERR | FLASH_SR_PGAERR | FLASH_SR_SIZERR | \ + FLASH_SR_PGSERR | FLASH_SR_MISERR | FLASH_SR_FASTERR | FLASH_SR_RDERR | FLASH_SR_OPTVERR) + +#define FURI_HAL_FLASH_OPT_KEY1 (0x08192A3BU) +#define FURI_HAL_FLASH_OPT_KEY2 (0x4C5D6E7FU) +#define FURI_HAL_FLASH_OB_TOTAL_WORDS (0x80 / (sizeof(uint32_t) * 2)) + +/* STM32CubeWB/Projects/P-NUCLEO-WB55.Nucleo/Applications/BLE/BLE_RfWithFlash/Core/Src/flash_driver.c + * ProcessSingleFlashOperation, quote: + > In most BLE application, the flash should not be blocked by the CPU2 longer than FLASH_TIMEOUT_VALUE (1000ms) + > However, it could be that for some marginal application, this time is longer. + > ... there is no other way than waiting the operation to be completed. + > If for any reason this test is never passed, this means there is a failure in the system and there is no other + > way to recover than applying a device reset. + */ +#define FURI_HAL_FLASH_C2_LOCK_TIMEOUT_MS (3000U) /* 3 seconds */ + +#define IS_ADDR_ALIGNED_64BITS(__VALUE__) (((__VALUE__)&0x7U) == (0x00UL)) +#define IS_FLASH_PROGRAM_ADDRESS(__VALUE__) \ + (((__VALUE__) >= FLASH_BASE) && ((__VALUE__) <= (FLASH_BASE + FLASH_SIZE - 8UL)) && \ + (((__VALUE__) % 8UL) == 0UL)) + +/* Free flash space borders, exported by linker */ +extern const void __free_flash_start__; + +size_t furi_hal_flash_get_base() { + return FLASH_BASE; +} + +size_t furi_hal_flash_get_read_block_size() { + return FURI_HAL_FLASH_READ_BLOCK; +} + +size_t furi_hal_flash_get_write_block_size() { + return FURI_HAL_FLASH_WRITE_BLOCK; +} + +size_t furi_hal_flash_get_page_size() { + return FURI_HAL_FLASH_PAGE_SIZE; +} + +size_t furi_hal_flash_get_cycles_count() { + return FURI_HAL_FLASH_CYCLES_COUNT; +} + +const void* furi_hal_flash_get_free_start_address() { + return &__free_flash_start__; +} + +const void* furi_hal_flash_get_free_end_address() { + uint32_t sfr_reg_val = READ_REG(FLASH->SFR); + uint32_t sfsa = (READ_BIT(sfr_reg_val, FLASH_SFR_SFSA) >> FLASH_SFR_SFSA_Pos); + return (const void*)((sfsa * FURI_HAL_FLASH_PAGE_SIZE) + FLASH_BASE); +} + +size_t furi_hal_flash_get_free_page_start_address() { + size_t start = (size_t)furi_hal_flash_get_free_start_address(); + size_t page_start = start - start % FURI_HAL_FLASH_PAGE_SIZE; + if(page_start != start) { + page_start += FURI_HAL_FLASH_PAGE_SIZE; + } + return page_start; +} + +size_t furi_hal_flash_get_free_page_count() { + size_t end = (size_t)furi_hal_flash_get_free_end_address(); + size_t page_start = (size_t)furi_hal_flash_get_free_page_start_address(); + return (end - page_start) / FURI_HAL_FLASH_PAGE_SIZE; +} + +void furi_hal_flash_init() { + /* Errata 2.2.9, Flash OPTVERR flag is always set after system reset */ + // WRITE_REG(FLASH->SR, FLASH_SR_OPTVERR); + /* Actually, reset all error flags on start */ + if(READ_BIT(FLASH->SR, FURI_HAL_FLASH_SR_ERRORS)) { + FURI_LOG_W(TAG, "FLASH->SR 0x%08lX(Known ERRATA)", FLASH->SR); + WRITE_REG(FLASH->SR, FURI_HAL_FLASH_SR_ERRORS); + } +} + +static void furi_hal_flash_unlock() { + /* verify Flash is locked */ + furi_check(READ_BIT(FLASH->CR, FLASH_CR_LOCK) != 0U); + + /* Authorize the FLASH Registers access */ + WRITE_REG(FLASH->KEYR, FURI_HAL_FLASH_KEY1); + __ISB(); + WRITE_REG(FLASH->KEYR, FURI_HAL_FLASH_KEY2); + + /* verify Flash is unlocked */ + furi_check(READ_BIT(FLASH->CR, FLASH_CR_LOCK) == 0U); +} + +static void furi_hal_flash_lock(void) { + /* verify Flash is unlocked */ + furi_check(READ_BIT(FLASH->CR, FLASH_CR_LOCK) == 0U); + + /* Set the LOCK Bit to lock the FLASH Registers access */ + /* @Note The lock and unlock procedure is done only using CR registers even from CPU2 */ + SET_BIT(FLASH->CR, FLASH_CR_LOCK); + + /* verify Flash is locked */ + furi_check(READ_BIT(FLASH->CR, FLASH_CR_LOCK) != 0U); +} + +static void furi_hal_flash_begin_with_core2(bool erase_flag) { + furi_hal_power_insomnia_enter(); + /* Take flash controller ownership */ + while(LL_HSEM_1StepLock(HSEM, CFG_HW_FLASH_SEMID) != 0) { + furi_thread_yield(); + } + + /* Unlock flash operation */ + furi_hal_flash_unlock(); + + /* Erase activity notification */ + if(erase_flag) SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON); + + /* 64mHz 5us core2 flag protection */ + for(volatile uint32_t i = 0; i < 35; i++) + ; + + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(FURI_HAL_FLASH_C2_LOCK_TIMEOUT_MS * 1000); + while(true) { + /* Wait till flash controller become usable */ + while(LL_FLASH_IsActiveFlag_OperationSuspended()) { + furi_check(!furi_hal_cortex_timer_is_expired(timer)); + furi_thread_yield(); + }; + + /* Just a little more love */ + taskENTER_CRITICAL(); + + /* Actually we already have mutex for it, but specification is specification */ + if(LL_HSEM_IsSemaphoreLocked(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID)) { + taskEXIT_CRITICAL(); + furi_check(!furi_hal_cortex_timer_is_expired(timer)); + furi_thread_yield(); + continue; + } + + /* Take sempahopre and prevent core2 from anything funky */ + if(LL_HSEM_1StepLock(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID) != 0) { + taskEXIT_CRITICAL(); + furi_check(!furi_hal_cortex_timer_is_expired(timer)); + furi_thread_yield(); + continue; + } + + break; + } +} + +static void furi_hal_flash_begin(bool erase_flag) { + /* Acquire dangerous ops mutex */ + furi_hal_bt_lock_core2(); + + /* If Core2 is running use IPC locking */ + if(furi_hal_bt_is_alive()) { + furi_hal_flash_begin_with_core2(erase_flag); + } else { + furi_hal_flash_unlock(); + } +} + +static void furi_hal_flash_end_with_core2(bool erase_flag) { + /* Funky ops are ok at this point */ + LL_HSEM_ReleaseLock(HSEM, CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID, 0); + + /* Task switching is ok */ + taskEXIT_CRITICAL(); + + /* Doesn't make much sense, does it? */ + while(READ_BIT(FLASH->SR, FLASH_SR_BSY)) { + furi_thread_yield(); + } + + /* Erase activity over, core2 can continue */ + if(erase_flag) SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_OFF); + + /* Lock flash controller */ + furi_hal_flash_lock(); + + /* Release flash controller ownership */ + LL_HSEM_ReleaseLock(HSEM, CFG_HW_FLASH_SEMID, 0); + furi_hal_power_insomnia_exit(); +} + +static void furi_hal_flash_end(bool erase_flag) { + /* If Core2 is running - use IPC locking */ + if(furi_hal_bt_is_alive()) { + furi_hal_flash_end_with_core2(erase_flag); + } else { + furi_hal_flash_lock(); + } + + /* Release dangerous ops mutex */ + furi_hal_bt_unlock_core2(); +} + +static void furi_hal_flush_cache(void) { + /* Flush instruction cache */ + if(READ_BIT(FLASH->ACR, FLASH_ACR_ICEN) == FLASH_ACR_ICEN) { + /* Disable instruction cache */ + LL_FLASH_DisableInstCache(); + /* Reset instruction cache */ + LL_FLASH_EnableInstCacheReset(); + LL_FLASH_DisableInstCacheReset(); + /* Enable instruction cache */ + LL_FLASH_EnableInstCache(); + } + + /* Flush data cache */ + if(READ_BIT(FLASH->ACR, FLASH_ACR_DCEN) == FLASH_ACR_DCEN) { + /* Disable data cache */ + LL_FLASH_DisableDataCache(); + /* Reset data cache */ + LL_FLASH_EnableDataCacheReset(); + LL_FLASH_DisableDataCacheReset(); + /* Enable data cache */ + LL_FLASH_EnableDataCache(); + } +} + +bool furi_hal_flash_wait_last_operation(uint32_t timeout) { + uint32_t error = 0; + + /* Wait for the FLASH operation to complete by polling on BUSY flag to be reset. + Even if the FLASH operation fails, the BUSY flag will be reset and an error + flag will be set */ + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); + while(READ_BIT(FLASH->SR, FLASH_SR_BSY)) { + if(furi_hal_cortex_timer_is_expired(timer)) { + return false; + } + } + + /* Check FLASH operation error flags */ + error = FLASH->SR; + + /* Check FLASH End of Operation flag */ + if((error & FLASH_SR_EOP) != 0U) { + /* Clear FLASH End of Operation pending bit */ + CLEAR_BIT(FLASH->SR, FLASH_SR_EOP); + } + + /* Now update error variable to only error value */ + error &= FURI_HAL_FLASH_SR_ERRORS; + + furi_check(error == 0); + + /* clear error flags */ + CLEAR_BIT(FLASH->SR, error); + + /* Wait for control register to be written */ + timer = furi_hal_cortex_timer_get(timeout * 1000); + while(READ_BIT(FLASH->SR, FLASH_SR_CFGBSY)) { + if(furi_hal_cortex_timer_is_expired(timer)) { + return false; + } + } + return true; +} + +void furi_hal_flash_erase(uint8_t page) { + furi_hal_flash_begin(true); + + /* Ensure that controller state is valid */ + furi_check(FLASH->SR == 0); + + /* Verify that next operation can be proceed */ + furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); + + /* Select page and start operation */ + MODIFY_REG( + FLASH->CR, FLASH_CR_PNB, ((page << FLASH_CR_PNB_Pos) | FLASH_CR_PER | FLASH_CR_STRT)); + + /* Wait for last operation to be completed */ + furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); + + /* If operation is completed or interrupted, disable the Page Erase Bit */ + CLEAR_BIT(FLASH->CR, (FLASH_CR_PER | FLASH_CR_PNB)); + + /* Flush the caches to be sure of the data consistency */ + furi_hal_flush_cache(); + + furi_hal_flash_end(true); +} + +static inline void furi_hal_flash_write_dword_internal_nowait(size_t address, uint64_t* data) { + /* Program first word */ + *(uint32_t*)address = (uint32_t)*data; + + /* Barrier to ensure programming is performed in 2 steps, in right order + (independently of compiler optimization behavior) */ + __ISB(); + + /* Program second word */ + *(uint32_t*)(address + 4U) = (uint32_t)(*data >> 32U); +} + +static inline void furi_hal_flash_write_dword_internal(size_t address, uint64_t* data) { + furi_hal_flash_write_dword_internal_nowait(address, data); + + /* Wait for last operation to be completed */ + furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); +} + +void furi_hal_flash_write_dword(size_t address, uint64_t data) { + furi_hal_flash_begin(false); + + /* Ensure that controller state is valid */ + furi_check(FLASH->SR == 0); + + /* Check the parameters */ + furi_check(IS_ADDR_ALIGNED_64BITS(address)); + furi_check(IS_FLASH_PROGRAM_ADDRESS(address)); + + /* Set PG bit */ + SET_BIT(FLASH->CR, FLASH_CR_PG); + + /* Do the thing */ + furi_hal_flash_write_dword_internal(address, &data); + + /* If the program operation is completed, disable the PG or FSTPG Bit */ + CLEAR_BIT(FLASH->CR, FLASH_CR_PG); + + furi_hal_flash_end(false); + + /* Wait for last operation to be completed */ + furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); +} + +static size_t furi_hal_flash_get_page_address(uint8_t page) { + return furi_hal_flash_get_base() + page * FURI_HAL_FLASH_PAGE_SIZE; +} + +void furi_hal_flash_program_page(const uint8_t page, const uint8_t* data, uint16_t _length) { + uint16_t length = _length; + furi_check(length <= FURI_HAL_FLASH_PAGE_SIZE); + + furi_hal_flash_erase(page); + + furi_hal_flash_begin(false); + + furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); + + /* Ensure that controller state is valid */ + furi_check(FLASH->SR == 0); + + size_t page_start_address = furi_hal_flash_get_page_address(page); + + size_t length_written = 0; + + const uint16_t FAST_PROG_BLOCK_SIZE = 512; + const uint8_t DWORD_PROG_BLOCK_SIZE = 8; + + /* Write as much data as we can in fast mode */ + if(length >= FAST_PROG_BLOCK_SIZE) { + taskENTER_CRITICAL(); + /* Enable fast flash programming mode */ + SET_BIT(FLASH->CR, FLASH_CR_FSTPG); + + while(length_written < (length / FAST_PROG_BLOCK_SIZE * FAST_PROG_BLOCK_SIZE)) { + /* No context switch in the middle of the operation */ + furi_hal_flash_write_dword_internal_nowait( + page_start_address + length_written, (uint64_t*)(data + length_written)); + length_written += DWORD_PROG_BLOCK_SIZE; + + if((length_written % FAST_PROG_BLOCK_SIZE) == 0) { + /* Wait for block operation to be completed */ + furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); + } + } + CLEAR_BIT(FLASH->CR, FLASH_CR_FSTPG); + taskEXIT_CRITICAL(); + } + + /* Enable regular (dword) programming mode */ + SET_BIT(FLASH->CR, FLASH_CR_PG); + if((length % FAST_PROG_BLOCK_SIZE) != 0) { + /* Write tail in regular, dword mode */ + while(length_written < (length / DWORD_PROG_BLOCK_SIZE * DWORD_PROG_BLOCK_SIZE)) { + furi_hal_flash_write_dword_internal( + page_start_address + length_written, (uint64_t*)&data[length_written]); + length_written += DWORD_PROG_BLOCK_SIZE; + } + } + + if((length % DWORD_PROG_BLOCK_SIZE) != 0) { + /* there are more bytes, not fitting into dwords */ + uint64_t tail_data = 0; + for(int32_t tail_i = 0; tail_i < (length % DWORD_PROG_BLOCK_SIZE); ++tail_i) { + tail_data |= (((uint64_t)data[length_written + tail_i]) << (tail_i * 8)); + } + + furi_hal_flash_write_dword_internal(page_start_address + length_written, &tail_data); + } + /* Disable the PG Bit */ + CLEAR_BIT(FLASH->CR, FLASH_CR_PG); + + furi_hal_flash_end(false); +} + +int16_t furi_hal_flash_get_page_number(size_t address) { + const size_t flash_base = furi_hal_flash_get_base(); + if((address < flash_base) || + (address > flash_base + FURI_HAL_FLASH_TOTAL_PAGES * FURI_HAL_FLASH_PAGE_SIZE)) { + return -1; + } + + return (address - flash_base) / FURI_HAL_FLASH_PAGE_SIZE; +} + +uint32_t furi_hal_flash_ob_get_word(size_t word_idx, bool complementary) { + furi_check(word_idx <= FURI_HAL_FLASH_OB_TOTAL_WORDS); + const uint32_t* ob_data = (const uint32_t*)(OPTION_BYTE_BASE); + size_t raw_word_idx = word_idx * 2; + if(complementary) { + raw_word_idx += 1; + } + return ob_data[raw_word_idx]; +} + +void furi_hal_flash_ob_unlock() { + furi_check(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) != 0U); + furi_hal_flash_begin(true); + WRITE_REG(FLASH->OPTKEYR, FURI_HAL_FLASH_OPT_KEY1); + __ISB(); + WRITE_REG(FLASH->OPTKEYR, FURI_HAL_FLASH_OPT_KEY2); + /* verify OB area is unlocked */ + furi_check(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) == 0U); +} + +void furi_hal_flash_ob_lock() { + furi_check(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) == 0U); + SET_BIT(FLASH->CR, FLASH_CR_OPTLOCK); + furi_hal_flash_end(true); + furi_check(READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) != 0U); +} + +typedef enum { + FuriHalFlashObInvalid, + FuriHalFlashObRegisterUserRead, + FuriHalFlashObRegisterPCROP1AStart, + FuriHalFlashObRegisterPCROP1AEnd, + FuriHalFlashObRegisterWRPA, + FuriHalFlashObRegisterWRPB, + FuriHalFlashObRegisterPCROP1BStart, + FuriHalFlashObRegisterPCROP1BEnd, + FuriHalFlashObRegisterIPCCMail, + FuriHalFlashObRegisterSecureFlash, + FuriHalFlashObRegisterC2Opts, +} FuriHalFlashObRegister; + +typedef struct { + FuriHalFlashObRegister ob_reg; + uint32_t* ob_register_address; +} FuriHalFlashObMapping; + +#define OB_REG_DEF(INDEX, REG) \ + { .ob_reg = INDEX, .ob_register_address = (uint32_t*)(REG) } + +static const FuriHalFlashObMapping furi_hal_flash_ob_reg_map[FURI_HAL_FLASH_OB_TOTAL_WORDS] = { + OB_REG_DEF(FuriHalFlashObRegisterUserRead, (&FLASH->OPTR)), + OB_REG_DEF(FuriHalFlashObRegisterPCROP1AStart, (&FLASH->PCROP1ASR)), + OB_REG_DEF(FuriHalFlashObRegisterPCROP1AEnd, (&FLASH->PCROP1AER)), + OB_REG_DEF(FuriHalFlashObRegisterWRPA, (&FLASH->WRP1AR)), + OB_REG_DEF(FuriHalFlashObRegisterWRPB, (&FLASH->WRP1BR)), + OB_REG_DEF(FuriHalFlashObRegisterPCROP1BStart, (&FLASH->PCROP1BSR)), + OB_REG_DEF(FuriHalFlashObRegisterPCROP1BEnd, (&FLASH->PCROP1BER)), + + OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), + OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), + OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), + OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), + OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), + OB_REG_DEF(FuriHalFlashObInvalid, (NULL)), + + OB_REG_DEF(FuriHalFlashObRegisterIPCCMail, (&FLASH->IPCCBR)), + OB_REG_DEF(FuriHalFlashObRegisterSecureFlash, (NULL)), + OB_REG_DEF(FuriHalFlashObRegisterC2Opts, (NULL)), +}; +#undef OB_REG_DEF + +void furi_hal_flash_ob_apply() { + furi_hal_flash_ob_unlock(); + /* OBL_LAUNCH: When set to 1, this bit forces the option byte reloading. + * It cannot be written if OPTLOCK is set */ + SET_BIT(FLASH->CR, FLASH_CR_OBL_LAUNCH); + furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); + furi_hal_flash_ob_lock(); +} + +bool furi_hal_flash_ob_set_word(size_t word_idx, const uint32_t value) { + furi_check(word_idx < FURI_HAL_FLASH_OB_TOTAL_WORDS); + + const FuriHalFlashObMapping* reg_def = &furi_hal_flash_ob_reg_map[word_idx]; + if(reg_def->ob_register_address == NULL) { + FURI_LOG_E(TAG, "Attempt to set RO OB word %d", word_idx); + return false; + } + + FURI_LOG_W( + TAG, + "Setting OB reg %d for word %d (addr 0x%08lX) to 0x%08lX", + reg_def->ob_reg, + word_idx, + (uint32_t)reg_def->ob_register_address, + value); + + /* 1. Clear OPTLOCK option lock bit with the clearing sequence */ + furi_hal_flash_ob_unlock(); + + /* 2. Write the desired options value in the options registers */ + *reg_def->ob_register_address = value; + + /* 3. Check that no Flash memory operation is on going by checking the BSY && PESD */ + furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); + while(LL_FLASH_IsActiveFlag_OperationSuspended()) { + furi_thread_yield(); + }; + + /* 4. Set the Options start bit OPTSTRT */ + SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT); + + /* 5. Wait for the BSY bit to be cleared. */ + furi_check(furi_hal_flash_wait_last_operation(FURI_HAL_FLASH_TIMEOUT)); + furi_hal_flash_ob_lock(); + return true; +} + +const FuriHalFlashRawOptionByteData* furi_hal_flash_ob_get_raw_ptr() { + return (const FuriHalFlashRawOptionByteData*)OPTION_BYTE_BASE; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_flash.h b/targets/f7/furi_hal/furi_hal_flash.h similarity index 92% rename from firmware/targets/f7/furi_hal/furi_hal_flash.h rename to targets/f7/furi_hal/furi_hal_flash.h index eac80d95ec8..9fa8f94af9d 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_flash.h +++ b/targets/f7/furi_hal/furi_hal_flash.h @@ -4,6 +4,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + #define FURI_HAL_FLASH_OB_RAW_SIZE_BYTES 0x80 #define FURI_HAL_FLASH_OB_SIZE_WORDS (FURI_HAL_FLASH_OB_RAW_SIZE_BYTES / sizeof(uint32_t)) #define FURI_HAL_FLASH_OB_TOTAL_VALUES (FURI_HAL_FLASH_OB_SIZE_WORDS / 2) @@ -86,10 +90,8 @@ size_t furi_hal_flash_get_free_page_count(); * @warning locking operation with critical section, stalls execution * * @param page The page to erase - * - * @return true on success */ -bool furi_hal_flash_erase(uint8_t page); +void furi_hal_flash_erase(uint8_t page); /** Write double word (64 bits) * @@ -97,10 +99,8 @@ bool furi_hal_flash_erase(uint8_t page); * * @param address destination address, must be double word aligned. * @param data data to write - * - * @return true on success */ -bool furi_hal_flash_write_dword(size_t address, uint64_t data); +void furi_hal_flash_write_dword(size_t address, uint64_t data); /** Write aligned page data (up to page size) * @@ -109,10 +109,8 @@ bool furi_hal_flash_write_dword(size_t address, uint64_t data); * @param address destination address, must be page aligned. * @param data data to write * @param length data length - * - * @return true on success */ -bool furi_hal_flash_program_page(const uint8_t page, const uint8_t* data, uint16_t length); +void furi_hal_flash_program_page(const uint8_t page, const uint8_t* data, uint16_t length); /** Get flash page number for address * @@ -142,3 +140,7 @@ void furi_hal_flash_ob_apply(); * @return pointer to read-only data of OB (raw + complementary values) */ const FuriHalFlashRawOptionByteData* furi_hal_flash_ob_get_raw_ptr(); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_gpio.c b/targets/f7/furi_hal/furi_hal_gpio.c similarity index 98% rename from firmware/targets/f7/furi_hal/furi_hal_gpio.c rename to targets/f7/furi_hal/furi_hal_gpio.c index a148a3af294..afd482f2f55 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_gpio.c +++ b/targets/f7/furi_hal/furi_hal_gpio.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #define GET_SYSCFG_EXTI_PORT(gpio) \ @@ -52,8 +53,8 @@ void furi_hal_gpio_init( const GpioPull pull, const GpioSpeed speed) { // we cannot set alternate mode in this function - furi_assert(mode != GpioModeAltFunctionPushPull); - furi_assert(mode != GpioModeAltFunctionOpenDrain); + furi_check(mode != GpioModeAltFunctionPushPull); + furi_check(mode != GpioModeAltFunctionOpenDrain); furi_hal_gpio_init_ex(gpio, mode, pull, speed, GpioAltFnUnused); } @@ -177,7 +178,7 @@ void furi_hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, vo FURI_CRITICAL_ENTER(); uint8_t pin_num = furi_hal_gpio_get_pin_num(gpio); - furi_assert(gpio_interrupt[pin_num].callback == NULL); + furi_check(gpio_interrupt[pin_num].callback == NULL); gpio_interrupt[pin_num].callback = cb; gpio_interrupt[pin_num].context = ctx; gpio_interrupt[pin_num].ready = true; @@ -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_gpio.h b/targets/f7/furi_hal/furi_hal_gpio.h similarity index 100% rename from firmware/targets/f7/furi_hal/furi_hal_gpio.h rename to targets/f7/furi_hal/furi_hal_gpio.h diff --git a/targets/f7/furi_hal/furi_hal_i2c.c b/targets/f7/furi_hal/furi_hal_i2c.c new file mode 100644 index 00000000000..8c7b054e3e4 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_i2c.c @@ -0,0 +1,416 @@ +#include +#include +#include +#include + +#include +#include +#include + +#define TAG "FuriHalI2c" + +void furi_hal_i2c_init_early() { + furi_hal_i2c_bus_power.callback(&furi_hal_i2c_bus_power, FuriHalI2cBusEventInit); +} + +void furi_hal_i2c_deinit_early() { + furi_hal_i2c_bus_power.callback(&furi_hal_i2c_bus_power, FuriHalI2cBusEventDeinit); +} + +void furi_hal_i2c_init() { + furi_hal_i2c_bus_external.callback(&furi_hal_i2c_bus_external, FuriHalI2cBusEventInit); + FURI_LOG_I(TAG, "Init OK"); +} + +void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle) { + furi_hal_power_insomnia_enter(); + // Lock bus access + handle->bus->callback(handle->bus, FuriHalI2cBusEventLock); + // Ensure that no active handle set + furi_check(handle->bus->current_handle == NULL); + // Set current handle + handle->bus->current_handle = handle; + // Activate bus + handle->bus->callback(handle->bus, FuriHalI2cBusEventActivate); + // Activate handle + handle->callback(handle, FuriHalI2cBusHandleEventActivate); +} + +void furi_hal_i2c_release(FuriHalI2cBusHandle* handle) { + // Ensure that current handle is our handle + furi_check(handle->bus->current_handle == handle); + // Deactivate handle + handle->callback(handle, FuriHalI2cBusHandleEventDeactivate); + // Deactivate bus + handle->bus->callback(handle->bus, FuriHalI2cBusEventDeactivate); + // Reset current handle + handle->bus->current_handle = NULL; + // Unlock bus + handle->bus->callback(handle->bus, FuriHalI2cBusEventUnlock); + furi_hal_power_insomnia_exit(); +} + +static bool + furi_hal_i2c_wait_for_idle(I2C_TypeDef* i2c, FuriHalI2cBegin begin, FuriHalCortexTimer timer) { + do { + if(furi_hal_cortex_timer_is_expired(timer)) { + return false; + } + } while(begin == FuriHalI2cBeginStart && LL_I2C_IsActiveFlag_BUSY(i2c)); + // Only check if the bus is busy if starting a new transaction, if not we already control the bus + + return true; +} + +static bool + furi_hal_i2c_wait_for_end(I2C_TypeDef* i2c, FuriHalI2cEnd end, FuriHalCortexTimer timer) { + // If ending the transaction with a stop condition, wait for it to be detected, otherwise wait for a transfer complete flag + bool wait_for_stop = end == FuriHalI2cEndStop; + uint32_t end_mask = (wait_for_stop) ? I2C_ISR_STOPF : (I2C_ISR_TC | I2C_ISR_TCR); + + while((i2c->ISR & end_mask) == 0) { + if(furi_hal_cortex_timer_is_expired(timer)) { + return false; + } + } + + return true; +} + +static uint32_t + furi_hal_i2c_get_start_signal(FuriHalI2cBegin begin, bool ten_bit_address, bool read) { + switch(begin) { + case FuriHalI2cBeginRestart: + if(read) { + return ten_bit_address ? LL_I2C_GENERATE_RESTART_10BIT_READ : + LL_I2C_GENERATE_RESTART_7BIT_READ; + } else { + return ten_bit_address ? LL_I2C_GENERATE_RESTART_10BIT_WRITE : + LL_I2C_GENERATE_RESTART_7BIT_WRITE; + } + case FuriHalI2cBeginResume: + return LL_I2C_GENERATE_NOSTARTSTOP; + case FuriHalI2cBeginStart: + default: + return read ? LL_I2C_GENERATE_START_READ : LL_I2C_GENERATE_START_WRITE; + } +} + +static uint32_t furi_hal_i2c_get_end_signal(FuriHalI2cEnd end) { + switch(end) { + case FuriHalI2cEndAwaitRestart: + return LL_I2C_MODE_SOFTEND; + case FuriHalI2cEndPause: + return LL_I2C_MODE_RELOAD; + case FuriHalI2cEndStop: + default: + return LL_I2C_MODE_AUTOEND; + } +} + +static bool furi_hal_i2c_transfer_is_aborted(I2C_TypeDef* i2c) { + return LL_I2C_IsActiveFlag_STOP(i2c) && + !(LL_I2C_IsActiveFlag_TC(i2c) || LL_I2C_IsActiveFlag_TCR(i2c)); +} + +static bool furi_hal_i2c_transfer( + I2C_TypeDef* i2c, + uint8_t* data, + uint32_t size, + FuriHalI2cEnd end, + bool read, + FuriHalCortexTimer timer) { + bool ret = true; + + while(size > 0) { + bool should_stop = furi_hal_cortex_timer_is_expired(timer) || + furi_hal_i2c_transfer_is_aborted(i2c); + + // Modifying the data pointer's data is UB if read is true + if(read && LL_I2C_IsActiveFlag_RXNE(i2c)) { + *data = LL_I2C_ReceiveData8(i2c); + data++; + size--; + } else if(!read && LL_I2C_IsActiveFlag_TXIS(i2c)) { + LL_I2C_TransmitData8(i2c, *data); + data++; + size--; + } + + // Exit on timeout or premature stop, probably caused by a nacked address or byte + if(should_stop) { + ret = size == 0; // If the transfer was over, still a success + break; + } + } + + if(ret) { + ret = furi_hal_i2c_wait_for_end(i2c, end, timer); + } + + LL_I2C_ClearFlag_STOP(i2c); + + return ret; +} + +static bool furi_hal_i2c_transaction( + I2C_TypeDef* i2c, + uint16_t address, + bool ten_bit, + uint8_t* data, + size_t size, + FuriHalI2cBegin begin, + FuriHalI2cEnd end, + bool read, + FuriHalCortexTimer timer) { + uint32_t addr_size = ten_bit ? LL_I2C_ADDRSLAVE_10BIT : LL_I2C_ADDRSLAVE_7BIT; + uint32_t start_signal = furi_hal_i2c_get_start_signal(begin, ten_bit, read); + + if(!furi_hal_i2c_wait_for_idle(i2c, begin, timer)) { + return false; + } + + do { + uint8_t transfer_size = size; + FuriHalI2cEnd transfer_end = end; + + if(size > 255) { + transfer_size = 255; + transfer_end = FuriHalI2cEndPause; + } + + uint32_t end_signal = furi_hal_i2c_get_end_signal(transfer_end); + + LL_I2C_HandleTransfer(i2c, address, addr_size, transfer_size, end_signal, start_signal); + + if(!furi_hal_i2c_transfer(i2c, data, transfer_size, transfer_end, read, timer)) { + return false; + } + + size -= transfer_size; + data += transfer_size; + start_signal = LL_I2C_GENERATE_NOSTARTSTOP; + } while(size > 0); + + return true; +} + +bool furi_hal_i2c_rx_ext( + FuriHalI2cBusHandle* handle, + uint16_t address, + bool ten_bit, + uint8_t* data, + size_t size, + FuriHalI2cBegin begin, + FuriHalI2cEnd end, + uint32_t timeout) { + furi_check(handle->bus->current_handle == handle); + + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); + + return furi_hal_i2c_transaction( + handle->bus->i2c, address, ten_bit, data, size, begin, end, true, timer); +} + +bool furi_hal_i2c_tx_ext( + FuriHalI2cBusHandle* handle, + uint16_t address, + bool ten_bit, + const uint8_t* data, + size_t size, + FuriHalI2cBegin begin, + FuriHalI2cEnd end, + uint32_t timeout) { + furi_check(handle->bus->current_handle == handle); + + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); + + return furi_hal_i2c_transaction( + handle->bus->i2c, address, ten_bit, (uint8_t*)data, size, begin, end, false, timer); +} + +bool furi_hal_i2c_tx( + FuriHalI2cBusHandle* handle, + uint8_t address, + const uint8_t* data, + size_t size, + uint32_t timeout) { + furi_assert(timeout > 0); + + return furi_hal_i2c_tx_ext( + handle, address, false, data, size, FuriHalI2cBeginStart, FuriHalI2cEndStop, timeout); +} + +bool furi_hal_i2c_rx( + FuriHalI2cBusHandle* handle, + uint8_t address, + uint8_t* data, + size_t size, + uint32_t timeout) { + furi_assert(timeout > 0); + + return furi_hal_i2c_rx_ext( + handle, address, false, data, size, FuriHalI2cBeginStart, FuriHalI2cEndStop, timeout); +} + +bool furi_hal_i2c_trx( + FuriHalI2cBusHandle* handle, + uint8_t address, + const uint8_t* tx_data, + size_t tx_size, + uint8_t* rx_data, + size_t rx_size, + uint32_t timeout) { + return furi_hal_i2c_tx_ext( + handle, + address, + false, + tx_data, + tx_size, + FuriHalI2cBeginStart, + FuriHalI2cEndStop, + timeout) && + furi_hal_i2c_rx_ext( + handle, + address, + false, + rx_data, + rx_size, + FuriHalI2cBeginStart, + FuriHalI2cEndStop, + timeout); +} + +bool furi_hal_i2c_is_device_ready(FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint32_t timeout) { + furi_check(handle); + furi_check(handle->bus->current_handle == handle); + furi_assert(timeout > 0); + + bool ret = true; + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout * 1000); + + if(!furi_hal_i2c_wait_for_idle(handle->bus->i2c, FuriHalI2cBeginStart, timer)) { + return false; + } + + LL_I2C_HandleTransfer( + handle->bus->i2c, + i2c_addr, + LL_I2C_ADDRSLAVE_7BIT, + 0, + LL_I2C_MODE_AUTOEND, + LL_I2C_GENERATE_START_WRITE); + + if(!furi_hal_i2c_wait_for_end(handle->bus->i2c, FuriHalI2cEndStop, timer)) { + return false; + } + + ret = !LL_I2C_IsActiveFlag_NACK(handle->bus->i2c); + + LL_I2C_ClearFlag_NACK(handle->bus->i2c); + LL_I2C_ClearFlag_STOP(handle->bus->i2c); + + return ret; +} + +bool furi_hal_i2c_read_reg_8( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint8_t* data, + uint32_t timeout) { + furi_check(handle); + + return furi_hal_i2c_trx(handle, i2c_addr, ®_addr, 1, data, 1, timeout); +} + +bool furi_hal_i2c_read_reg_16( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint16_t* data, + uint32_t timeout) { + furi_check(handle); + + uint8_t reg_data[2]; + bool ret = furi_hal_i2c_trx(handle, i2c_addr, ®_addr, 1, reg_data, 2, timeout); + *data = (reg_data[0] << 8) | (reg_data[1]); + + return ret; +} + +bool furi_hal_i2c_read_mem( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t mem_addr, + uint8_t* data, + size_t len, + uint32_t timeout) { + furi_check(handle); + + return furi_hal_i2c_trx(handle, i2c_addr, &mem_addr, 1, data, len, timeout); +} + +bool furi_hal_i2c_write_reg_8( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint8_t data, + uint32_t timeout) { + furi_check(handle); + + const uint8_t tx_data[2] = { + reg_addr, + data, + }; + + return furi_hal_i2c_tx(handle, i2c_addr, tx_data, 2, timeout); +} + +bool furi_hal_i2c_write_reg_16( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint16_t data, + uint32_t timeout) { + furi_check(handle); + + const uint8_t tx_data[3] = { + reg_addr, + (data >> 8) & 0xFF, + data & 0xFF, + }; + + return furi_hal_i2c_tx(handle, i2c_addr, tx_data, 3, timeout); +} + +bool furi_hal_i2c_write_mem( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t mem_addr, + const uint8_t* data, + size_t len, + uint32_t timeout) { + furi_check(handle); + furi_check(handle->bus->current_handle == handle); + furi_assert(timeout > 0); + + return furi_hal_i2c_tx_ext( + handle, + i2c_addr, + false, + &mem_addr, + 1, + FuriHalI2cBeginStart, + FuriHalI2cEndPause, + timeout) && + furi_hal_i2c_tx_ext( + handle, + i2c_addr, + false, + data, + len, + FuriHalI2cBeginResume, + FuriHalI2cEndStop, + timeout); +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_i2c_config.c b/targets/f7/furi_hal/furi_hal_i2c_config.c similarity index 84% rename from firmware/targets/f7/furi_hal/furi_hal_i2c_config.c rename to targets/f7/furi_hal/furi_hal_i2c_config.c index d832c4f66fb..f9d88abb376 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_i2c_config.c +++ b/targets/f7/furi_hal/furi_hal_i2c_config.c @@ -1,7 +1,8 @@ -#include "furi_hal_i2c_config.h" +#include #include #include -#include +#include + #include /** Timing register value is computed with the STM32CubeMX Tool, @@ -21,17 +22,9 @@ FuriMutex* furi_hal_i2c_bus_power_mutex = NULL; static void furi_hal_i2c_bus_power_event(FuriHalI2cBus* bus, FuriHalI2cBusEvent event) { if(event == FuriHalI2cBusEventInit) { furi_hal_i2c_bus_power_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - FURI_CRITICAL_ENTER(); - LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C1); - FURI_CRITICAL_EXIT(); bus->current_handle = NULL; } else if(event == FuriHalI2cBusEventDeinit) { furi_mutex_free(furi_hal_i2c_bus_power_mutex); - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C1); - LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C1); - FURI_CRITICAL_EXIT(); } else if(event == FuriHalI2cBusEventLock) { furi_check( furi_mutex_acquire(furi_hal_i2c_bus_power_mutex, FuriWaitForever) == FuriStatusOk); @@ -39,12 +32,11 @@ static void furi_hal_i2c_bus_power_event(FuriHalI2cBus* bus, FuriHalI2cBusEvent furi_check(furi_mutex_release(furi_hal_i2c_bus_power_mutex) == FuriStatusOk); } else if(event == FuriHalI2cBusEventActivate) { FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C1); + furi_hal_bus_enable(FuriHalBusI2C1); + LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1); FURI_CRITICAL_EXIT(); } else if(event == FuriHalI2cBusEventDeactivate) { - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C1); - FURI_CRITICAL_EXIT(); + furi_hal_bus_disable(FuriHalBusI2C1); } } @@ -58,17 +50,9 @@ FuriMutex* furi_hal_i2c_bus_external_mutex = NULL; static void furi_hal_i2c_bus_external_event(FuriHalI2cBus* bus, FuriHalI2cBusEvent event) { if(event == FuriHalI2cBusEventInit) { furi_hal_i2c_bus_external_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - FURI_CRITICAL_ENTER(); - LL_RCC_SetI2CClockSource(LL_RCC_I2C3_CLKSOURCE_PCLK1); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C3); - FURI_CRITICAL_EXIT(); bus->current_handle = NULL; } else if(event == FuriHalI2cBusEventDeinit) { furi_mutex_free(furi_hal_i2c_bus_external_mutex); - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C3); - LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C3); - FURI_CRITICAL_EXIT(); } else if(event == FuriHalI2cBusEventLock) { furi_check( furi_mutex_acquire(furi_hal_i2c_bus_external_mutex, FuriWaitForever) == FuriStatusOk); @@ -76,13 +60,11 @@ static void furi_hal_i2c_bus_external_event(FuriHalI2cBus* bus, FuriHalI2cBusEve furi_check(furi_mutex_release(furi_hal_i2c_bus_external_mutex) == FuriStatusOk); } else if(event == FuriHalI2cBusEventActivate) { FURI_CRITICAL_ENTER(); + furi_hal_bus_enable(FuriHalBusI2C3); LL_RCC_SetI2CClockSource(LL_RCC_I2C3_CLKSOURCE_PCLK1); - LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_I2C3); FURI_CRITICAL_EXIT(); } else if(event == FuriHalI2cBusEventDeactivate) { - FURI_CRITICAL_ENTER(); - LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_I2C3); - FURI_CRITICAL_EXIT(); + furi_hal_bus_disable(FuriHalBusI2C3); } } @@ -108,7 +90,7 @@ void furi_hal_i2c_bus_handle_power_event( GpioSpeedLow, GpioAltFn4I2C1); - LL_I2C_InitTypeDef I2C_InitStruct = {0}; + LL_I2C_InitTypeDef I2C_InitStruct; I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C; I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE; I2C_InitStruct.DigitalFilter = 0; @@ -152,7 +134,7 @@ void furi_hal_i2c_bus_handle_external_event( furi_hal_gpio_init_ex( &gpio_ext_pc1, GpioModeAltFunctionOpenDrain, GpioPullNo, GpioSpeedLow, GpioAltFn4I2C3); - LL_I2C_InitTypeDef I2C_InitStruct = {0}; + LL_I2C_InitTypeDef I2C_InitStruct; I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C; I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE; I2C_InitStruct.DigitalFilter = 0; diff --git a/firmware/targets/f7/furi_hal/furi_hal_i2c_config.h b/targets/f7/furi_hal/furi_hal_i2c_config.h similarity index 100% rename from firmware/targets/f7/furi_hal/furi_hal_i2c_config.h rename to targets/f7/furi_hal/furi_hal_i2c_config.h diff --git a/firmware/targets/f7/furi_hal/furi_hal_i2c_types.h b/targets/f7/furi_hal/furi_hal_i2c_types.h similarity index 100% rename from firmware/targets/f7/furi_hal/furi_hal_i2c_types.h rename to targets/f7/furi_hal/furi_hal_i2c_types.h diff --git a/targets/f7/furi_hal/furi_hal_ibutton.c b/targets/f7/furi_hal/furi_hal_ibutton.c new file mode 100644 index 00000000000..f8f7e4966dc --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_ibutton.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include + +#include + +#include + +#define TAG "FuriHalIbutton" +#define FURI_HAL_IBUTTON_TIMER TIM1 +#define FURI_HAL_IBUTTON_TIMER_BUS FuriHalBusTIM1 +#define FURI_HAL_IBUTTON_TIMER_IRQ FuriHalInterruptIdTim1UpTim16 + +typedef enum { + FuriHalIbuttonStateIdle, + FuriHalIbuttonStateRunning, +} FuriHalIbuttonState; + +typedef struct { + FuriHalIbuttonState state; + FuriHalIbuttonEmulateCallback callback; + void* context; +} FuriHalIbutton; + +FuriHalIbutton* furi_hal_ibutton = NULL; + +static void furi_hal_ibutton_emulate_isr() { + if(LL_TIM_IsActiveFlag_UPDATE(FURI_HAL_IBUTTON_TIMER)) { + LL_TIM_ClearFlag_UPDATE(FURI_HAL_IBUTTON_TIMER); + furi_hal_ibutton->callback(furi_hal_ibutton->context); + } +} + +void furi_hal_ibutton_init() { + furi_hal_ibutton = malloc(sizeof(FuriHalIbutton)); + furi_hal_ibutton->state = FuriHalIbuttonStateIdle; + + FURI_LOG_I(TAG, "Init OK"); +} + +void furi_hal_ibutton_emulate_start( + uint32_t period, + FuriHalIbuttonEmulateCallback callback, + void* context) { + furi_assert(furi_hal_ibutton); + furi_assert(furi_hal_ibutton->state == FuriHalIbuttonStateIdle); + + furi_hal_ibutton->state = FuriHalIbuttonStateRunning; + furi_hal_ibutton->callback = callback; + furi_hal_ibutton->context = context; + + furi_hal_bus_enable(FURI_HAL_IBUTTON_TIMER_BUS); + + furi_hal_interrupt_set_isr(FURI_HAL_IBUTTON_TIMER_IRQ, furi_hal_ibutton_emulate_isr, NULL); + + LL_TIM_SetPrescaler(FURI_HAL_IBUTTON_TIMER, 0); + LL_TIM_SetCounterMode(FURI_HAL_IBUTTON_TIMER, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetAutoReload(FURI_HAL_IBUTTON_TIMER, period); + LL_TIM_DisableARRPreload(FURI_HAL_IBUTTON_TIMER); + LL_TIM_SetRepetitionCounter(FURI_HAL_IBUTTON_TIMER, 0); + + LL_TIM_SetClockDivision(FURI_HAL_IBUTTON_TIMER, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetClockSource(FURI_HAL_IBUTTON_TIMER, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_GenerateEvent_UPDATE(FURI_HAL_IBUTTON_TIMER); + + LL_TIM_EnableIT_UPDATE(FURI_HAL_IBUTTON_TIMER); + + LL_TIM_EnableCounter(FURI_HAL_IBUTTON_TIMER); +} + +void furi_hal_ibutton_emulate_set_next(uint32_t period) { + LL_TIM_SetAutoReload(FURI_HAL_IBUTTON_TIMER, period); +} + +void furi_hal_ibutton_emulate_stop() { + furi_assert(furi_hal_ibutton); + + if(furi_hal_ibutton->state == FuriHalIbuttonStateRunning) { + furi_hal_ibutton->state = FuriHalIbuttonStateIdle; + LL_TIM_DisableCounter(FURI_HAL_IBUTTON_TIMER); + + furi_hal_bus_disable(FURI_HAL_IBUTTON_TIMER_BUS); + furi_hal_interrupt_set_isr(FURI_HAL_IBUTTON_TIMER_IRQ, NULL, NULL); + + furi_hal_ibutton->callback = NULL; + furi_hal_ibutton->context = NULL; + } +} + +void furi_hal_ibutton_pin_configure() { + furi_hal_gpio_write(&gpio_ibutton, true); + furi_hal_gpio_init(&gpio_ibutton, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); +} + +void furi_hal_ibutton_pin_reset() { + furi_hal_gpio_write(&gpio_ibutton, true); + furi_hal_gpio_init(&gpio_ibutton, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +void furi_hal_ibutton_pin_write(const bool state) { + furi_hal_gpio_write(&gpio_ibutton, state); +} diff --git a/targets/f7/furi_hal/furi_hal_ibutton.h b/targets/f7/furi_hal/furi_hal_ibutton.h new file mode 100644 index 00000000000..b723fe17686 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_ibutton.h @@ -0,0 +1,60 @@ +/** + * @file furi_hal_ibutton.h + * iButton HAL API + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +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(); + +/** + * Set the pin to normal mode (open collector), and sets it to float + */ +void furi_hal_ibutton_pin_configure(); + +/** + * Sets the pin to analog mode, and sets it to float + */ +void furi_hal_ibutton_pin_reset(); + +/** + * iButton write pin + * @param state true / false + */ +void furi_hal_ibutton_pin_write(const bool state); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_idle_timer.h b/targets/f7/furi_hal/furi_hal_idle_timer.h similarity index 94% rename from firmware/targets/f7/furi_hal/furi_hal_idle_timer.h rename to targets/f7/furi_hal/furi_hal_idle_timer.h index 36b45755a92..e1ffb1b25f4 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_idle_timer.h +++ b/targets/f7/furi_hal/furi_hal_idle_timer.h @@ -1,9 +1,10 @@ #pragma once #include -#include #include -#include +#include + +#include // Timer used for tickless idle #define FURI_HAL_IDLE_TIMER_MAX 0xFFFF @@ -11,6 +12,7 @@ #define FURI_HAL_IDLE_TIMER_IRQ LPTIM1_IRQn static inline void furi_hal_idle_timer_init() { + furi_hal_bus_enable(FuriHalBusLPTIM1); // Configure clock source LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM1_CLKSOURCE_LSE); // There is a theoretical possibility that we need it @@ -41,7 +43,7 @@ static inline void furi_hal_idle_timer_start(uint32_t count) { static inline void furi_hal_idle_timer_reset() { // Hard reset timer // THE ONLY RELIABLE WAY to stop it according to errata - LL_LPTIM_DeInit(FURI_HAL_IDLE_TIMER); + furi_hal_bus_reset(FuriHalBusLPTIM1); // Prevent IRQ handler call NVIC_ClearPendingIRQ(FURI_HAL_IDLE_TIMER_IRQ); } diff --git a/targets/f7/furi_hal/furi_hal_info.c b/targets/f7/furi_hal/furi_hal_info.c new file mode 100644 index 00000000000..4908cca69d1 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_info.c @@ -0,0 +1,366 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +FURI_WEAK void furi_hal_info_get_api_version(uint16_t* major, uint16_t* minor) { + *major = 0; + *minor = 0; +} + +void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) { + FuriString* key = furi_string_alloc(); + FuriString* value = furi_string_alloc(); + + PropertyValueContext property_context = { + .key = key, .value = value, .out = out, .sep = sep, .last = false, .context = context}; + + // Device Info version + if(sep == '.') { + property_value_out(&property_context, NULL, 2, "format", "major", "3"); + property_value_out(&property_context, NULL, 2, "format", "minor", "3"); + } else { + property_value_out(&property_context, NULL, 3, "device", "info", "major", "2"); + property_value_out(&property_context, NULL, 3, "device", "info", "minor", "4"); + } + + // Model name + property_value_out( + &property_context, NULL, 2, "hardware", "model", furi_hal_version_get_model_name()); + + // Unique ID + furi_string_reset(value); + const uint8_t* uid = furi_hal_version_uid(); + for(size_t i = 0; i < furi_hal_version_uid_size(); i++) { + furi_string_cat_printf(value, "%02X", uid[i]); + } + property_value_out(&property_context, NULL, 2, "hardware", "uid", furi_string_get_cstr(value)); + + // OTP Revision + property_value_out( + &property_context, "%d", 3, "hardware", "otp", "ver", furi_hal_version_get_otp_version()); + property_value_out( + &property_context, "%lu", 2, "hardware", "timestamp", furi_hal_version_get_hw_timestamp()); + + // Board Revision + property_value_out( + &property_context, "%d", 2, "hardware", "ver", furi_hal_version_get_hw_version()); + property_value_out( + &property_context, "%d", 2, "hardware", "target", furi_hal_version_get_hw_target()); + property_value_out( + &property_context, "%d", 2, "hardware", "body", furi_hal_version_get_hw_body()); + property_value_out( + &property_context, "%d", 2, "hardware", "connect", furi_hal_version_get_hw_connect()); + property_value_out( + &property_context, "%d", 2, "hardware", "display", furi_hal_version_get_hw_display()); + + // Board Personification + property_value_out( + &property_context, "%d", 2, "hardware", "color", furi_hal_version_get_hw_color()); + + if(sep == '.') { + property_value_out( + &property_context, + "%d", + 3, + "hardware", + "region", + "builtin", + furi_hal_version_get_hw_region()); + } else { + property_value_out( + &property_context, "%d", 2, "hardware", "region", furi_hal_version_get_hw_region()); + } + + property_value_out( + &property_context, + NULL, + 3, + "hardware", + "region", + "provisioned", + furi_hal_region_get_name()); + + const char* name = furi_hal_version_get_name_ptr(); + if(name) { + property_value_out(&property_context, NULL, 2, "hardware", "name", name); + } + + // Firmware version + const Version* firmware_version = furi_hal_version_get_firmware_version(); + if(firmware_version) { + if(sep == '.') { + property_value_out( + &property_context, + NULL, + 3, + "firmware", + "commit", + "hash", + version_get_githash(firmware_version)); + } else { + property_value_out( + &property_context, + NULL, + 2, + "firmware", + "commit", + version_get_githash(firmware_version)); + } + + property_value_out( + &property_context, + NULL, + 3, + "firmware", + "commit", + "dirty", + version_get_dirty_flag(firmware_version) ? "true" : "false"); + + if(sep == '.') { + property_value_out( + &property_context, + NULL, + 3, + "firmware", + "branch", + "name", + version_get_gitbranch(firmware_version)); + } else { + property_value_out( + &property_context, + NULL, + 2, + "firmware", + "branch", + version_get_gitbranch(firmware_version)); + } + + property_value_out( + &property_context, + NULL, + 3, + "firmware", + "branch", + "num", + version_get_gitbranchnum(firmware_version)); + property_value_out( + &property_context, + NULL, + 2, + "firmware", + "version", + version_get_version(firmware_version)); + property_value_out( + &property_context, + NULL, + 3, + "firmware", + "build", + "date", + version_get_builddate(firmware_version)); + property_value_out( + &property_context, "%d", 2, "firmware", "target", version_get_target(firmware_version)); + + uint16_t api_version_major, api_version_minor; + furi_hal_info_get_api_version(&api_version_major, &api_version_minor); + property_value_out( + &property_context, "%d", 3, "firmware", "api", "major", api_version_major); + property_value_out( + &property_context, "%d", 3, "firmware", "api", "minor", api_version_minor); + + property_value_out( + &property_context, + NULL, + 3, + "firmware", + "origin", + "fork", + version_get_firmware_origin(firmware_version)); + + property_value_out( + &property_context, + NULL, + 3, + "firmware", + "origin", + "git", + version_get_git_origin(firmware_version)); + } + + if(furi_hal_bt_is_alive()) { + const BleGlueC2Info* ble_c2_info = ble_glue_get_c2_info(); + property_value_out(&property_context, NULL, 2, "radio", "alive", "true"); + property_value_out( + &property_context, + NULL, + 2, + "radio", + "mode", + ble_c2_info->mode == BleGlueC2ModeFUS ? "FUS" : "Stack"); + + // FUS Info + property_value_out( + &property_context, "%d", 3, "radio", "fus", "major", ble_c2_info->FusVersionMajor); + property_value_out( + &property_context, "%d", 3, "radio", "fus", "minor", ble_c2_info->FusVersionMinor); + property_value_out( + &property_context, "%d", 3, "radio", "fus", "sub", ble_c2_info->FusVersionSub); + property_value_out( + &property_context, + "%dK", + 3, + "radio", + "fus", + "sram2b", + ble_c2_info->FusMemorySizeSram2B); + property_value_out( + &property_context, + "%dK", + 3, + "radio", + "fus", + "sram2a", + ble_c2_info->FusMemorySizeSram2A); + property_value_out( + &property_context, + "%dK", + 3, + "radio", + "fus", + "flash", + ble_c2_info->FusMemorySizeFlash * 4); + + // Stack Info + property_value_out( + &property_context, "%d", 3, "radio", "stack", "type", ble_c2_info->StackType); + property_value_out( + &property_context, "%d", 3, "radio", "stack", "major", ble_c2_info->VersionMajor); + property_value_out( + &property_context, "%d", 3, "radio", "stack", "minor", ble_c2_info->VersionMinor); + property_value_out( + &property_context, "%d", 3, "radio", "stack", "sub", ble_c2_info->VersionSub); + property_value_out( + &property_context, "%d", 3, "radio", "stack", "branch", ble_c2_info->VersionBranch); + property_value_out( + &property_context, + "%d", + 3, + "radio", + "stack", + "release", + ble_c2_info->VersionReleaseType); + property_value_out( + &property_context, "%dK", 3, "radio", "stack", "sram2b", ble_c2_info->MemorySizeSram2B); + property_value_out( + &property_context, "%dK", 3, "radio", "stack", "sram2a", ble_c2_info->MemorySizeSram2A); + property_value_out( + &property_context, "%dK", 3, "radio", "stack", "sram1", ble_c2_info->MemorySizeSram1); + property_value_out( + &property_context, + "%dK", + 3, + "radio", + "stack", + "flash", + ble_c2_info->MemorySizeFlash * 4); + + // Mac address + furi_string_reset(value); + const uint8_t* ble_mac = furi_hal_version_get_ble_mac(); + for(size_t i = 0; i < 6; i++) { + furi_string_cat_printf(value, "%02X", ble_mac[i]); + } + property_value_out( + &property_context, NULL, 3, "radio", "ble", "mac", furi_string_get_cstr(value)); + + // Signature verification + uint8_t enclave_keys = 0; + uint8_t enclave_valid_keys = 0; + bool enclave_valid = furi_hal_crypto_enclave_verify(&enclave_keys, &enclave_valid_keys); + if(sep == '.') { + property_value_out( + &property_context, "%d", 3, "enclave", "keys", "valid", enclave_valid_keys); + } else { + property_value_out( + &property_context, "%d", 3, "enclave", "valid", "keys", enclave_valid_keys); + } + + property_value_out( + &property_context, NULL, 2, "enclave", "valid", enclave_valid ? "true" : "false"); + } else { + property_value_out(&property_context, NULL, 2, "radio", "alive", "false"); + } + + // RTC flags + property_value_out( + &property_context, + "%u", + 2, + "system", + "debug", + furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)); + property_value_out( + &property_context, "%u", 2, "system", "lock", furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)); + property_value_out( + &property_context, + "%u", + 2, + "system", + "orient", + furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient)); + property_value_out( + &property_context, + "%u", + 3, + "system", + "sleep", + "legacy", + furi_hal_rtc_is_flag_set(FuriHalRtcFlagLegacySleep)); + property_value_out( + &property_context, + "%u", + 2, + "system", + "stealth", + furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)); + + property_value_out( + &property_context, "%u", 3, "system", "heap", "track", furi_hal_rtc_get_heap_track_mode()); + property_value_out(&property_context, "%u", 2, "system", "boot", furi_hal_rtc_get_boot_mode()); + property_value_out( + &property_context, + "%u", + 3, + "system", + "locale", + "time", + furi_hal_rtc_get_locale_timeformat()); + property_value_out( + &property_context, + "%u", + 3, + "system", + "locale", + "date", + furi_hal_rtc_get_locale_dateformat()); + property_value_out( + &property_context, "%u", 3, "system", "locale", "unit", furi_hal_rtc_get_locale_units()); + property_value_out( + &property_context, "%u", 3, "system", "log", "level", furi_hal_rtc_get_log_level()); + + property_value_out( + &property_context, "%u", 3, "protobuf", "version", "major", PROTOBUF_MAJOR_VERSION); + property_context.last = true; + property_value_out( + &property_context, "%u", 3, "protobuf", "version", "minor", PROTOBUF_MINOR_VERSION); + + furi_string_free(key); + furi_string_free(value); +} diff --git a/targets/f7/furi_hal/furi_hal_infrared.c b/targets/f7/furi_hal/furi_hal_infrared.c new file mode 100644 index 00000000000..3b20b6bc3a6 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_infrared.c @@ -0,0 +1,686 @@ +#include +#include +#include +#include + +#include +#include + +#include +#include + +// #define INFRARED_TX_DEBUG + +#if defined INFRARED_TX_DEBUG +#define gpio_infrared_tx gpio_ext_pa7 +#endif + +#define INFRARED_TIM_TX_DMA_BUFFER_SIZE 200 +#define INFRARED_POLARITY_SHIFT 1 + +#define INFRARED_TX_CCMR_HIGH \ + (TIM_CCMR2_OC3PE | LL_TIM_OCMODE_PWM2) /* Mark time - enable PWM2 mode */ +#define INFRARED_TX_CCMR_LOW \ + (TIM_CCMR2_OC3PE | LL_TIM_OCMODE_FORCED_INACTIVE) /* Space time - force low */ + +/* DMA Channels definition */ +#define INFRARED_DMA DMA2 +#define INFRARED_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 +#define INFRARED_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2 +#define INFRARED_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 +#define INFRARED_DMA_CH2_IRQ FuriHalInterruptIdDma2Ch2 +#define INFRARED_DMA_CH1_DEF INFRARED_DMA, INFRARED_DMA_CH1_CHANNEL +#define INFRARED_DMA_CH2_DEF INFRARED_DMA, INFRARED_DMA_CH2_CHANNEL + +/* Timers definition */ +#define INFRARED_RX_TIMER TIM2 +#define INFRARED_DMA_TIMER TIM1 +#define INFRARED_RX_TIMER_BUS FuriHalBusTIM2 +#define INFRARED_DMA_TIMER_BUS FuriHalBusTIM1 + +/* Misc */ +#define INFRARED_RX_GPIO_ALT GpioAltFn1TIM2 +#define INFRARED_RX_IRQ FuriHalInterruptIdTIM2 + +typedef struct { + FuriHalInfraredRxCaptureCallback capture_callback; + void* capture_context; + FuriHalInfraredRxTimeoutCallback timeout_callback; + void* timeout_context; +} InfraredTimRx; + +typedef struct { + uint8_t* polarity; + uint16_t* data; + size_t size; + bool packet_end; + bool last_packet_end; +} InfraredTxBuf; + +typedef struct { + float cycle_duration; + FuriHalInfraredTxGetDataISRCallback data_callback; + FuriHalInfraredTxSignalSentISRCallback signal_sent_callback; + void* data_context; + void* signal_sent_context; + InfraredTxBuf buffer[2]; + FuriSemaphore* stop_semaphore; + uint32_t + tx_timing_rest_duration; /** if timing is too long (> 0xFFFF), send it in few iterations */ + bool tx_timing_rest_level; + FuriHalInfraredTxGetDataState tx_timing_rest_status; +} InfraredTimTx; + +typedef enum { + InfraredStateIdle, /** Furi Hal Infrared is ready to start RX or TX */ + InfraredStateAsyncRx, /** Async RX started */ + InfraredStateAsyncTx, /** Async TX started, DMA and timer is on */ + InfraredStateAsyncTxStopReq, /** Async TX started, async stop request received */ + InfraredStateAsyncTxStopInProgress, /** Async TX started, stop request is processed and we wait for last data to be sent */ + InfraredStateAsyncTxStopped, /** Async TX complete, cleanup needed */ + InfraredStateMAX, +} InfraredState; + +static volatile InfraredState furi_hal_infrared_state = InfraredStateIdle; +static InfraredTimTx infrared_tim_tx; +static InfraredTimRx infrared_tim_rx; + +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); +static void furi_hal_infrared_tx_dma_set_polarity(uint8_t buf_num, uint8_t polarity_shift); +static void furi_hal_infrared_tx_dma_set_buffer(uint8_t buf_num); +static void furi_hal_infrared_tx_fill_buffer_last(uint8_t buf_num); +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(); + +static void furi_hal_infrared_tim_rx_isr() { + static uint32_t previous_captured_ch2 = 0; + + /* Timeout */ + if(LL_TIM_IsActiveFlag_CC3(INFRARED_RX_TIMER)) { + LL_TIM_ClearFlag_CC3(INFRARED_RX_TIMER); + furi_assert(furi_hal_infrared_state == InfraredStateAsyncRx); + + /* Timers CNT register starts to counting from 0 to ARR, but it is + * reseted when Channel 1 catches interrupt. It is not reseted by + * channel 2, though, so we have to distract it's values (see TimerIRQSourceCCI1 ISR). + * This can cause false timeout: when time is over, but we started + * receiving new signal few microseconds ago, because CNT register + * is reseted once per period, not per sample. */ + if(LL_GPIO_IsInputPinSet(gpio_infrared_rx.port, gpio_infrared_rx.pin) != 0) { + if(infrared_tim_rx.timeout_callback) + infrared_tim_rx.timeout_callback(infrared_tim_rx.timeout_context); + } + } + + /* Rising Edge */ + if(LL_TIM_IsActiveFlag_CC1(INFRARED_RX_TIMER)) { + LL_TIM_ClearFlag_CC1(INFRARED_RX_TIMER); + furi_assert(furi_hal_infrared_state == InfraredStateAsyncRx); + + if(READ_BIT(INFRARED_RX_TIMER->CCMR1, TIM_CCMR1_CC1S)) { + /* Low pin level is a Mark state of INFRARED signal. Invert level for further processing. */ + uint32_t duration = LL_TIM_IC_GetCaptureCH1(INFRARED_RX_TIMER) - previous_captured_ch2; + if(infrared_tim_rx.capture_callback) + infrared_tim_rx.capture_callback(infrared_tim_rx.capture_context, 1, duration); + } else { + furi_crash(); + } + } + + /* Falling Edge */ + if(LL_TIM_IsActiveFlag_CC2(INFRARED_RX_TIMER)) { + LL_TIM_ClearFlag_CC2(INFRARED_RX_TIMER); + furi_assert(furi_hal_infrared_state == InfraredStateAsyncRx); + + if(READ_BIT(INFRARED_RX_TIMER->CCMR1, TIM_CCMR1_CC2S)) { + /* High pin level is a Space state of INFRARED signal. Invert level for further processing. */ + uint32_t duration = LL_TIM_IC_GetCaptureCH2(INFRARED_RX_TIMER); + previous_captured_ch2 = duration; + if(infrared_tim_rx.capture_callback) + infrared_tim_rx.capture_callback(infrared_tim_rx.capture_context, 0, duration); + } else { + furi_crash(); + } + } +} + +void furi_hal_infrared_async_rx_start(void) { + furi_assert(furi_hal_infrared_state == InfraredStateIdle); + + furi_hal_gpio_init_ex( + &gpio_infrared_rx, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedLow, + INFRARED_RX_GPIO_ALT); + + furi_hal_bus_enable(INFRARED_RX_TIMER_BUS); + + 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_DIV1; + LL_TIM_Init(INFRARED_RX_TIMER, &TIM_InitStruct); + + LL_TIM_SetClockSource(INFRARED_RX_TIMER, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(INFRARED_RX_TIMER); + LL_TIM_SetTriggerInput(INFRARED_RX_TIMER, LL_TIM_TS_TI1FP1); + LL_TIM_SetSlaveMode(INFRARED_RX_TIMER, LL_TIM_SLAVEMODE_RESET); + LL_TIM_CC_DisableChannel(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH2); + LL_TIM_IC_SetFilter(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1); + LL_TIM_IC_SetPolarity(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_FALLING); + LL_TIM_DisableIT_TRIG(INFRARED_RX_TIMER); + LL_TIM_DisableDMAReq_TRIG(INFRARED_RX_TIMER); + LL_TIM_SetTriggerOutput(INFRARED_RX_TIMER, LL_TIM_TRGO_RESET); + LL_TIM_EnableMasterSlaveMode(INFRARED_RX_TIMER); + LL_TIM_IC_SetActiveInput(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_DIRECTTI); + LL_TIM_IC_SetPrescaler(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1); + LL_TIM_IC_SetFilter(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1); + LL_TIM_IC_SetPolarity(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_RISING); + LL_TIM_IC_SetActiveInput(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_INDIRECTTI); + LL_TIM_IC_SetPrescaler(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); + + furi_hal_interrupt_set_isr(INFRARED_RX_IRQ, furi_hal_infrared_tim_rx_isr, NULL); + furi_hal_infrared_state = InfraredStateAsyncRx; + + LL_TIM_EnableIT_CC1(INFRARED_RX_TIMER); + LL_TIM_EnableIT_CC2(INFRARED_RX_TIMER); + LL_TIM_CC_EnableChannel(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH1); + LL_TIM_CC_EnableChannel(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH2); + + LL_TIM_SetCounter(INFRARED_RX_TIMER, 0); + LL_TIM_EnableCounter(INFRARED_RX_TIMER); +} + +void furi_hal_infrared_async_rx_stop(void) { + furi_assert(furi_hal_infrared_state == InfraredStateAsyncRx); + + FURI_CRITICAL_ENTER(); + furi_hal_bus_disable(INFRARED_RX_TIMER_BUS); + furi_hal_interrupt_set_isr(INFRARED_RX_IRQ, NULL, NULL); + furi_hal_infrared_state = InfraredStateIdle; + FURI_CRITICAL_EXIT(); +} + +void furi_hal_infrared_async_rx_set_timeout(uint32_t timeout_us) { + LL_TIM_OC_SetCompareCH3(INFRARED_RX_TIMER, timeout_us); + LL_TIM_OC_SetMode(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_ACTIVE); + LL_TIM_CC_EnableChannel(INFRARED_RX_TIMER, LL_TIM_CHANNEL_CH3); + LL_TIM_EnableIT_CC3(INFRARED_RX_TIMER); +} + +bool furi_hal_infrared_is_busy(void) { + return furi_hal_infrared_state != InfraredStateIdle; +} + +void furi_hal_infrared_async_rx_set_capture_isr_callback( + FuriHalInfraredRxCaptureCallback callback, + void* ctx) { + infrared_tim_rx.capture_callback = callback; + infrared_tim_rx.capture_context = ctx; +} + +void furi_hal_infrared_async_rx_set_timeout_isr_callback( + FuriHalInfraredRxTimeoutCallback callback, + void* ctx) { + infrared_tim_rx.timeout_callback = callback; + infrared_tim_rx.timeout_context = ctx; +} + +static void furi_hal_infrared_tx_dma_terminate(void) { + LL_DMA_DisableIT_TC(INFRARED_DMA_CH1_DEF); + LL_DMA_DisableIT_HT(INFRARED_DMA_CH2_DEF); + LL_DMA_DisableIT_TC(INFRARED_DMA_CH2_DEF); + + furi_assert(furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress); + + LL_DMA_DisableIT_TC(INFRARED_DMA_CH1_DEF); + LL_DMA_DisableChannel(INFRARED_DMA_CH2_DEF); + LL_DMA_DisableChannel(INFRARED_DMA_CH1_DEF); + LL_TIM_DisableCounter(INFRARED_DMA_TIMER); + FuriStatus status = furi_semaphore_release(infrared_tim_tx.stop_semaphore); + furi_check(status == FuriStatusOk); + furi_hal_infrared_state = InfraredStateAsyncTxStopped; +} + +static uint8_t furi_hal_infrared_get_current_dma_tx_buffer(void) { + uint8_t buf_num = 0; + uint32_t buffer_adr = LL_DMA_GetMemoryAddress(INFRARED_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) { + buf_num = 1; + } else { + furi_crash(); + } + return buf_num; +} + +static void furi_hal_infrared_tx_dma_polarity_isr() { +#if INFRARED_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 + if(LL_DMA_IsActiveFlag_TE1(INFRARED_DMA)) { + LL_DMA_ClearFlag_TE1(INFRARED_DMA); + furi_crash(); + } + if(LL_DMA_IsActiveFlag_TC1(INFRARED_DMA) && LL_DMA_IsEnabledIT_TC(INFRARED_DMA_CH1_DEF)) { + LL_DMA_ClearFlag_TC1(INFRARED_DMA); + + furi_check( + (furi_hal_infrared_state == InfraredStateAsyncTx) || + (furi_hal_infrared_state == InfraredStateAsyncTxStopReq) || + (furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress)); + /* actually TC2 is processed and buffer is next buffer */ + 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 INFRARED_DMA_CH2_CHANNEL == LL_DMA_CHANNEL_2 + if(LL_DMA_IsActiveFlag_TE2(INFRARED_DMA)) { + LL_DMA_ClearFlag_TE2(INFRARED_DMA); + furi_crash(); + } + if(LL_DMA_IsActiveFlag_HT2(INFRARED_DMA) && LL_DMA_IsEnabledIT_HT(INFRARED_DMA_CH2_DEF)) { + LL_DMA_ClearFlag_HT2(INFRARED_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(INFRARED_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(INFRARED_DMA_CH2_DEF); + } + } else if(furi_hal_infrared_state == InfraredStateAsyncTxStopReq) { + /* fallthrough */ + } else { + furi_crash(); + } + } + if(LL_DMA_IsActiveFlag_TC2(INFRARED_DMA) && LL_DMA_IsEnabledIT_TC(INFRARED_DMA_CH2_DEF)) { + LL_DMA_ClearFlag_TC2(INFRARED_DMA); + furi_check( + (furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress) || + (furi_hal_infrared_state == InfraredStateAsyncTxStopReq) || + (furi_hal_infrared_state == InfraredStateAsyncTx)); + + uint8_t buf_num = furi_hal_infrared_get_current_dma_tx_buffer(); + uint8_t next_buf_num = !buf_num; + if(furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress) { + furi_hal_infrared_tx_dma_terminate(); + } else if( + infrared_tim_tx.buffer[buf_num].last_packet_end || + (infrared_tim_tx.buffer[buf_num].packet_end && + (furi_hal_infrared_state == InfraredStateAsyncTxStopReq))) { + furi_hal_infrared_state = InfraredStateAsyncTxStopInProgress; + furi_hal_infrared_tx_fill_buffer_last(next_buf_num); + furi_hal_infrared_tx_dma_set_buffer(next_buf_num); + } else { + /* if it's not end of the packet - continue receiving */ + furi_hal_infrared_tx_dma_set_buffer(next_buf_num); + } + if(infrared_tim_tx.signal_sent_callback && infrared_tim_tx.buffer[buf_num].packet_end && + (furi_hal_infrared_state != InfraredStateAsyncTxStopped)) { + 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) { + LL_TIM_DisableCounter(INFRARED_DMA_TIMER); + LL_TIM_SetRepetitionCounter(INFRARED_DMA_TIMER, 0); + LL_TIM_SetCounter(INFRARED_DMA_TIMER, 0); + LL_TIM_SetPrescaler(INFRARED_DMA_TIMER, 0); + LL_TIM_SetCounterMode(INFRARED_DMA_TIMER, LL_TIM_COUNTERMODE_UP); + LL_TIM_EnableARRPreload(INFRARED_DMA_TIMER); + LL_TIM_SetAutoReload( + INFRARED_DMA_TIMER, + __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(INFRARED_DMA_TIMER), freq)); +#if defined INFRARED_TX_DEBUG + LL_TIM_OC_SetCompareCH1( + INFRARED_DMA_TIMER, ((LL_TIM_GetAutoReload(INFRARED_DMA_TIMER) + 1) * (1 - duty_cycle))); + LL_TIM_OC_EnablePreload(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1); + /* LL_TIM_OCMODE_PWM2 set by DMA */ + LL_TIM_OC_SetMode(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_FORCED_INACTIVE); + LL_TIM_OC_SetPolarity(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1N, LL_TIM_OCPOLARITY_HIGH); + LL_TIM_OC_DisableFast(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1); + LL_TIM_CC_EnableChannel(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1N); + LL_TIM_DisableIT_CC1(INFRARED_DMA_TIMER); +#else + LL_TIM_OC_SetCompareCH3( + INFRARED_DMA_TIMER, ((LL_TIM_GetAutoReload(INFRARED_DMA_TIMER) + 1) * (1 - duty_cycle))); + LL_TIM_OC_EnablePreload(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3); + /* LL_TIM_OCMODE_PWM2 set by DMA */ + LL_TIM_OC_SetMode(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_FORCED_INACTIVE); + LL_TIM_OC_SetPolarity(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3N, LL_TIM_OCPOLARITY_HIGH); + LL_TIM_OC_DisableFast(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3); + LL_TIM_CC_EnableChannel(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3N); + LL_TIM_DisableIT_CC3(INFRARED_DMA_TIMER); +#endif + LL_TIM_DisableMasterSlaveMode(INFRARED_DMA_TIMER); + LL_TIM_EnableAllOutputs(INFRARED_DMA_TIMER); + LL_TIM_DisableIT_UPDATE(INFRARED_DMA_TIMER); + LL_TIM_EnableDMAReq_UPDATE(INFRARED_DMA_TIMER); +} + +static void furi_hal_infrared_configure_tim_cmgr2_dma_tx(void) { + LL_DMA_InitTypeDef dma_config = {0}; +#if defined INFRARED_TX_DEBUG + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (INFRARED_DMA_TIMER->CCMR1); +#else + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (INFRARED_DMA_TIMER->CCMR2); +#endif + dma_config.MemoryOrM2MDstAddress = (uint32_t)NULL; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + /* fill word to have other bits set to 0 */ + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; + dma_config.NbData = 0; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; + dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; + LL_DMA_Init(INFRARED_DMA_CH1_DEF, &dma_config); + +#if INFRARED_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 + LL_DMA_ClearFlag_TE1(INFRARED_DMA); + LL_DMA_ClearFlag_TC1(INFRARED_DMA); +#else +#error Update this code. Would you kindly? +#endif + + LL_DMA_EnableIT_TE(INFRARED_DMA_CH1_DEF); + LL_DMA_EnableIT_TC(INFRARED_DMA_CH1_DEF); + + furi_hal_interrupt_set_isr_ex( + INFRARED_DMA_CH1_IRQ, 4, furi_hal_infrared_tx_dma_polarity_isr, NULL); +} + +static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) { + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (INFRARED_DMA_TIMER->RCR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)NULL; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD; + dma_config.NbData = 0; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; + dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; + LL_DMA_Init(INFRARED_DMA_CH2_DEF, &dma_config); + +#if INFRARED_DMA_CH2_CHANNEL == LL_DMA_CHANNEL_2 + LL_DMA_ClearFlag_TC2(INFRARED_DMA); + LL_DMA_ClearFlag_HT2(INFRARED_DMA); + LL_DMA_ClearFlag_TE2(INFRARED_DMA); +#else +#error Update this code. Would you kindly? +#endif + + LL_DMA_EnableIT_TC(INFRARED_DMA_CH2_DEF); + LL_DMA_EnableIT_HT(INFRARED_DMA_CH2_DEF); + LL_DMA_EnableIT_TE(INFRARED_DMA_CH2_DEF); + + furi_hal_interrupt_set_isr_ex(INFRARED_DMA_CH2_IRQ, 5, furi_hal_infrared_tx_dma_isr, NULL); +} + +static void furi_hal_infrared_tx_fill_buffer_last(uint8_t buf_num) { + furi_assert(buf_num < 2); + furi_assert(furi_hal_infrared_state != InfraredStateAsyncRx); + furi_assert(furi_hal_infrared_state < InfraredStateMAX); + furi_assert(infrared_tim_tx.data_callback); + InfraredTxBuf* buffer = &infrared_tim_tx.buffer[buf_num]; + furi_assert(buffer->data != NULL); + (void)buffer->data; + furi_assert(buffer->polarity != NULL); + (void)buffer->polarity; + + infrared_tim_tx.buffer[buf_num].data[0] = 0; // 1 pulse + infrared_tim_tx.buffer[buf_num].polarity[0] = INFRARED_TX_CCMR_LOW; + infrared_tim_tx.buffer[buf_num].data[1] = 0; // 1 pulse + infrared_tim_tx.buffer[buf_num].polarity[1] = INFRARED_TX_CCMR_LOW; + infrared_tim_tx.buffer[buf_num].size = 2; + infrared_tim_tx.buffer[buf_num].last_packet_end = true; + infrared_tim_tx.buffer[buf_num].packet_end = true; +} + +static void furi_hal_infrared_tx_fill_buffer(uint8_t buf_num, uint8_t polarity_shift) { + furi_assert(buf_num < 2); + furi_assert(furi_hal_infrared_state != InfraredStateAsyncRx); + furi_assert(furi_hal_infrared_state < InfraredStateMAX); + furi_assert(infrared_tim_tx.data_callback); + InfraredTxBuf* buffer = &infrared_tim_tx.buffer[buf_num]; + furi_assert(buffer->data != NULL); + furi_assert(buffer->polarity != NULL); + + FuriHalInfraredTxGetDataState status = FuriHalInfraredTxGetDataStateOk; + uint32_t duration = 0; + bool level = 0; + size_t* size = &buffer->size; + size_t polarity_counter = 0; + while(polarity_shift--) { + buffer->polarity[polarity_counter++] = INFRARED_TX_CCMR_LOW; + } + + for(*size = 0; (*size < INFRARED_TIM_TX_DMA_BUFFER_SIZE) && + (status == FuriHalInfraredTxGetDataStateOk);) { + if(infrared_tim_tx.tx_timing_rest_duration > 0) { + if(infrared_tim_tx.tx_timing_rest_duration > 0xFFFF) { + buffer->data[*size] = 0xFFFF; + status = FuriHalInfraredTxGetDataStateOk; + } else { + buffer->data[*size] = infrared_tim_tx.tx_timing_rest_duration; + status = infrared_tim_tx.tx_timing_rest_status; + } + infrared_tim_tx.tx_timing_rest_duration -= buffer->data[*size]; + buffer->polarity[polarity_counter] = infrared_tim_tx.tx_timing_rest_level ? + INFRARED_TX_CCMR_HIGH : + INFRARED_TX_CCMR_LOW; + ++(*size); + ++polarity_counter; + continue; + } + + status = infrared_tim_tx.data_callback(infrared_tim_tx.data_context, &duration, &level); + + uint32_t num_of_impulses = roundf(duration / infrared_tim_tx.cycle_duration); + + if(num_of_impulses == 0) { + if((*size == 0) && (status == FuriHalInfraredTxGetDataStateDone)) { + /* if this is one sample in current buffer, but we + * have more to send - continue + */ + status = FuriHalInfraredTxGetDataStateOk; + } + } else if((num_of_impulses - 1) > 0xFFFF) { + infrared_tim_tx.tx_timing_rest_duration = num_of_impulses - 1; + infrared_tim_tx.tx_timing_rest_status = status; + infrared_tim_tx.tx_timing_rest_level = level; + status = FuriHalInfraredTxGetDataStateOk; + } else { + buffer->polarity[polarity_counter] = level ? INFRARED_TX_CCMR_HIGH : + INFRARED_TX_CCMR_LOW; + buffer->data[*size] = num_of_impulses - 1; + ++(*size); + ++polarity_counter; + } + } + + buffer->last_packet_end = (status == FuriHalInfraredTxGetDataStateLastDone); + buffer->packet_end = buffer->last_packet_end || (status == FuriHalInfraredTxGetDataStateDone); + + if(*size == 0) { + buffer->data[0] = 0; // 1 pulse + buffer->polarity[0] = INFRARED_TX_CCMR_LOW; + buffer->size = 1; + } +} + +static void furi_hal_infrared_tx_dma_set_polarity(uint8_t buf_num, uint8_t polarity_shift) { + furi_assert(buf_num < 2); + furi_assert(furi_hal_infrared_state < InfraredStateMAX); + InfraredTxBuf* buffer = &infrared_tim_tx.buffer[buf_num]; + furi_assert(buffer->polarity != NULL); + + FURI_CRITICAL_ENTER(); + bool channel_enabled = LL_DMA_IsEnabledChannel(INFRARED_DMA_CH1_DEF); + if(channel_enabled) { + LL_DMA_DisableChannel(INFRARED_DMA_CH1_DEF); + } + LL_DMA_SetMemoryAddress(INFRARED_DMA_CH1_DEF, (uint32_t)buffer->polarity); + LL_DMA_SetDataLength(INFRARED_DMA_CH1_DEF, buffer->size + polarity_shift); + if(channel_enabled) { + LL_DMA_EnableChannel(INFRARED_DMA_CH1_DEF); + } + FURI_CRITICAL_EXIT(); +} + +static void furi_hal_infrared_tx_dma_set_buffer(uint8_t buf_num) { + furi_assert(buf_num < 2); + furi_assert(furi_hal_infrared_state < InfraredStateMAX); + InfraredTxBuf* buffer = &infrared_tim_tx.buffer[buf_num]; + furi_assert(buffer->data != NULL); + + /* non-circular mode requires disabled channel before setup */ + FURI_CRITICAL_ENTER(); + bool channel_enabled = LL_DMA_IsEnabledChannel(INFRARED_DMA_CH2_DEF); + if(channel_enabled) { + LL_DMA_DisableChannel(INFRARED_DMA_CH2_DEF); + } + LL_DMA_SetMemoryAddress(INFRARED_DMA_CH2_DEF, (uint32_t)buffer->data); + LL_DMA_SetDataLength(INFRARED_DMA_CH2_DEF, buffer->size); + if(channel_enabled) { + LL_DMA_EnableChannel(INFRARED_DMA_CH2_DEF); + } + FURI_CRITICAL_EXIT(); +} + +static void furi_hal_infrared_async_tx_free_resources(void) { + furi_assert( + (furi_hal_infrared_state == InfraredStateIdle) || + (furi_hal_infrared_state == InfraredStateAsyncTxStopped)); + + furi_hal_gpio_init(&gpio_infrared_tx, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + furi_hal_interrupt_set_isr(INFRARED_DMA_CH1_IRQ, NULL, NULL); + furi_hal_interrupt_set_isr(INFRARED_DMA_CH2_IRQ, NULL, NULL); + + furi_hal_bus_disable(INFRARED_DMA_TIMER_BUS); + + furi_semaphore_free(infrared_tim_tx.stop_semaphore); + free(infrared_tim_tx.buffer[0].data); + free(infrared_tim_tx.buffer[1].data); + free(infrared_tim_tx.buffer[0].polarity); + free(infrared_tim_tx.buffer[1].polarity); + + infrared_tim_tx.buffer[0].data = NULL; + infrared_tim_tx.buffer[1].data = NULL; + infrared_tim_tx.buffer[0].polarity = NULL; + infrared_tim_tx.buffer[1].polarity = NULL; +} + +void furi_hal_infrared_async_tx_start(uint32_t freq, float duty_cycle) { + if((duty_cycle > 1) || (duty_cycle <= 0) || (freq > INFRARED_MAX_FREQUENCY) || + (freq < INFRARED_MIN_FREQUENCY) || (infrared_tim_tx.data_callback == NULL)) { + furi_crash(); + } + + furi_assert(furi_hal_infrared_state == InfraredStateIdle); + furi_assert(infrared_tim_tx.buffer[0].data == NULL); + furi_assert(infrared_tim_tx.buffer[1].data == NULL); + furi_assert(infrared_tim_tx.buffer[0].polarity == NULL); + furi_assert(infrared_tim_tx.buffer[1].polarity == NULL); + + size_t alloc_size_data = INFRARED_TIM_TX_DMA_BUFFER_SIZE * sizeof(uint16_t); + infrared_tim_tx.buffer[0].data = malloc(alloc_size_data); + infrared_tim_tx.buffer[1].data = malloc(alloc_size_data); + + size_t alloc_size_polarity = + (INFRARED_TIM_TX_DMA_BUFFER_SIZE + INFRARED_POLARITY_SHIFT) * sizeof(uint8_t); + infrared_tim_tx.buffer[0].polarity = malloc(alloc_size_polarity); + infrared_tim_tx.buffer[1].polarity = malloc(alloc_size_polarity); + + infrared_tim_tx.stop_semaphore = furi_semaphore_alloc(1, 0); + infrared_tim_tx.cycle_duration = 1000000.0 / freq; + infrared_tim_tx.tx_timing_rest_duration = 0; + + furi_hal_infrared_tx_fill_buffer(0, INFRARED_POLARITY_SHIFT); + + furi_hal_bus_enable(INFRARED_DMA_TIMER_BUS); + + furi_hal_infrared_configure_tim_pwm_tx(freq, duty_cycle); + furi_hal_infrared_configure_tim_cmgr2_dma_tx(); + furi_hal_infrared_configure_tim_rcr_dma_tx(); + furi_hal_infrared_tx_dma_set_polarity(0, INFRARED_POLARITY_SHIFT); + furi_hal_infrared_tx_dma_set_buffer(0); + + furi_hal_infrared_state = InfraredStateAsyncTx; + + LL_TIM_ClearFlag_UPDATE(INFRARED_DMA_TIMER); + LL_DMA_EnableChannel(INFRARED_DMA_CH1_DEF); + LL_DMA_EnableChannel(INFRARED_DMA_CH2_DEF); + furi_delay_us(5); + LL_TIM_GenerateEvent_UPDATE(INFRARED_DMA_TIMER); /* 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); + + FURI_CRITICAL_ENTER(); + LL_TIM_GenerateEvent_UPDATE(INFRARED_DMA_TIMER); /* TIMx_RCR -> Repetition counter */ + LL_TIM_EnableCounter(INFRARED_DMA_TIMER); + FURI_CRITICAL_EXIT(); +} + +void furi_hal_infrared_async_tx_wait_termination(void) { + furi_assert(furi_hal_infrared_state >= InfraredStateAsyncTx); + furi_assert(furi_hal_infrared_state < InfraredStateMAX); + + FuriStatus status; + status = furi_semaphore_acquire(infrared_tim_tx.stop_semaphore, FuriWaitForever); + furi_check(status == FuriStatusOk); + furi_hal_infrared_async_tx_free_resources(); + furi_hal_infrared_state = InfraredStateIdle; +} + +void furi_hal_infrared_async_tx_stop(void) { + furi_assert(furi_hal_infrared_state >= InfraredStateAsyncTx); + furi_assert(furi_hal_infrared_state < InfraredStateMAX); + + FURI_CRITICAL_ENTER(); + if(furi_hal_infrared_state == InfraredStateAsyncTx) + furi_hal_infrared_state = InfraredStateAsyncTxStopReq; + FURI_CRITICAL_EXIT(); + + furi_hal_infrared_async_tx_wait_termination(); +} + +void furi_hal_infrared_async_tx_set_data_isr_callback( + FuriHalInfraredTxGetDataISRCallback callback, + void* context) { + furi_assert(furi_hal_infrared_state == InfraredStateIdle); + infrared_tim_tx.data_callback = callback; + infrared_tim_tx.data_context = context; +} + +void furi_hal_infrared_async_tx_set_signal_sent_isr_callback( + FuriHalInfraredTxSignalSentISRCallback callback, + void* context) { + infrared_tim_tx.signal_sent_callback = callback; + infrared_tim_tx.signal_sent_context = context; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c b/targets/f7/furi_hal/furi_hal_interrupt.c similarity index 90% rename from firmware/targets/f7/furi_hal/furi_hal_interrupt.c rename to targets/f7/furi_hal/furi_hal_interrupt.c index 038ae948946..c508dac72bf 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/targets/f7/furi_hal/furi_hal_interrupt.c @@ -1,5 +1,5 @@ -#include "furi_hal_interrupt.h" -#include "furi_hal_os.h" +#include +#include #include @@ -62,7 +62,7 @@ const IRQn_Type furi_hal_interrupt_irqn[FuriHalInterruptIdMax] = { __attribute__((always_inline)) static inline void furi_hal_interrupt_call(FuriHalInterruptId index) { - furi_assert(furi_hal_interrupt_isr[index].isr); + furi_check(furi_hal_interrupt_isr[index].isr); furi_hal_interrupt_isr[index].isr(furi_hal_interrupt_isr[index].context); } @@ -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]); @@ -112,17 +127,16 @@ void furi_hal_interrupt_set_isr_ex( uint16_t priority, FuriHalInterruptISR isr, void* context) { - furi_assert(index < FuriHalInterruptIdMax); - furi_assert(priority < 15); - furi_assert(furi_hal_interrupt_irqn[index]); + furi_check(index < FuriHalInterruptIdMax); + furi_check(priority <= 15); if(isr) { // Pre ISR set - furi_assert(furi_hal_interrupt_isr[index].isr == NULL); + furi_check(furi_hal_interrupt_isr[index].isr == NULL); } else { // 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 +145,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_interrupt.h b/targets/f7/furi_hal/furi_hal_interrupt.h similarity index 100% rename from firmware/targets/f7/furi_hal/furi_hal_interrupt.h rename to targets/f7/furi_hal/furi_hal_interrupt.h diff --git a/firmware/targets/f7/furi_hal/furi_hal_light.c b/targets/f7/furi_hal/furi_hal_light.c similarity index 99% rename from firmware/targets/f7/furi_hal/furi_hal_light.c rename to targets/f7/furi_hal/furi_hal_light.c index e6b3ab7d9a6..83e1603b75a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_light.c +++ b/targets/f7/furi_hal/furi_hal_light.c @@ -1,5 +1,5 @@ #include -#include "furi_hal_resources.h" +#include #include #include #include diff --git a/targets/f7/furi_hal/furi_hal_memory.c b/targets/f7/furi_hal/furi_hal_memory.c new file mode 100644 index 00000000000..3f8df1f4409 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_memory.c @@ -0,0 +1,129 @@ +#include +#include +#include + +#define TAG "FuriHalMemory" + +typedef enum { + SRAM_A, + SRAM_B, + SRAM_MAX, +} SRAM; + +typedef struct { + void* start; + uint32_t size; +} FuriHalMemoryRegion; + +typedef struct { + FuriHalMemoryRegion region[SRAM_MAX]; +} FuriHalMemory; + +static FuriHalMemory* furi_hal_memory = NULL; + +extern const void __sram2a_start__; +extern const void __sram2a_free__; +extern const void __sram2b_start__; + +void furi_hal_memory_init() { + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { + return; + } + + FuriHalMemory* memory = malloc(sizeof(FuriHalMemory)); + + uint32_t sbrsa = (FLASH->SRRVR & FLASH_SRRVR_SBRSA_Msk) >> FLASH_SRRVR_SBRSA_Pos; + uint32_t snbrsa = (FLASH->SRRVR & FLASH_SRRVR_SNBRSA_Msk) >> FLASH_SRRVR_SNBRSA_Pos; + + // STM(TM) Copro(TM) bug(TM): SNBRSA is incorrect if stack version is higher than 1.13 and lower than 1.17.2+ + // Radio core started, but not yet ready, so we'll try to guess + // This will be true only if BLE light radio stack used, + // 0x0D is known to be incorrect, 0x0B is known to be correct since 1.17.2+ + // Lower value by 2 pages to match real memory layout + if(snbrsa > 0x0B) { + FURI_LOG_E(TAG, "SNBRSA workaround"); + snbrsa -= 2; + } + + uint32_t sram2a_busy_size = (uint32_t)&__sram2a_free__ - (uint32_t)&__sram2a_start__; + uint32_t sram2a_unprotected_size = (sbrsa)*1024; + uint32_t sram2b_unprotected_size = (snbrsa)*1024; + + memory->region[SRAM_A].start = (uint8_t*)&__sram2a_free__; + memory->region[SRAM_B].start = (uint8_t*)&__sram2b_start__; + + if(sram2a_unprotected_size > sram2a_busy_size) { + memory->region[SRAM_A].size = sram2a_unprotected_size - sram2a_busy_size; + } else { + memory->region[SRAM_A].size = 0; + } + memory->region[SRAM_B].size = sram2b_unprotected_size; + + FURI_LOG_I( + TAG, "SRAM2A: 0x%p, %lu", memory->region[SRAM_A].start, memory->region[SRAM_A].size); + FURI_LOG_I( + TAG, "SRAM2B: 0x%p, %lu", memory->region[SRAM_B].start, memory->region[SRAM_B].size); + + if((memory->region[SRAM_A].size > 0) || (memory->region[SRAM_B].size > 0)) { + if((memory->region[SRAM_A].size > 0)) { + FURI_LOG_I(TAG, "SRAM2A clear"); + memset(memory->region[SRAM_A].start, 0, memory->region[SRAM_A].size); + } + if((memory->region[SRAM_B].size > 0)) { + FURI_LOG_I(TAG, "SRAM2B clear"); + memset(memory->region[SRAM_B].start, 0, memory->region[SRAM_B].size); + } + furi_hal_memory = memory; + FURI_LOG_I(TAG, "Enabled"); + } else { + free(memory); + FURI_LOG_E(TAG, "No SRAM2 available"); + } +} + +void* furi_hal_memory_alloc(size_t size) { + if(FURI_IS_IRQ_MODE()) { + furi_crash("memmgt in ISR"); + } + + if(furi_hal_memory == NULL) { + return NULL; + } + + void* allocated_memory = NULL; + FURI_CRITICAL_ENTER(); + for(int i = 0; i < SRAM_MAX; i++) { + if(furi_hal_memory->region[i].size >= size) { + void* ptr = furi_hal_memory->region[i].start; + furi_hal_memory->region[i].start += size; + furi_hal_memory->region[i].size -= size; + allocated_memory = ptr; + break; + } + } + FURI_CRITICAL_EXIT(); + + return allocated_memory; +} + +size_t furi_hal_memory_get_free() { + if(furi_hal_memory == NULL) return 0; + + size_t free = 0; + for(int i = 0; i < SRAM_MAX; i++) { + free += furi_hal_memory->region[i].size; + } + return free; +} + +size_t furi_hal_memory_max_pool_block() { + if(furi_hal_memory == NULL) return 0; + + size_t max = 0; + for(int i = 0; i < SRAM_MAX; i++) { + if(furi_hal_memory->region[i].size > max) { + max = furi_hal_memory->region[i].size; + } + } + return max; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_mpu.c b/targets/f7/furi_hal/furi_hal_mpu.c similarity index 100% rename from firmware/targets/f7/furi_hal/furi_hal_mpu.c rename to targets/f7/furi_hal/furi_hal_mpu.c diff --git a/targets/f7/furi_hal/furi_hal_nfc.c b/targets/f7/furi_hal/furi_hal_nfc.c new file mode 100644 index 00000000000..e8a1033d515 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_nfc.c @@ -0,0 +1,621 @@ +#include "furi_hal_nfc_i.h" +#include "furi_hal_nfc_tech_i.h" + +#include + +#include +#include + +#define TAG "FuriHalNfc" + +const FuriHalNfcTechBase* furi_hal_nfc_tech[FuriHalNfcTechNum] = { + [FuriHalNfcTechIso14443a] = &furi_hal_nfc_iso14443a, + [FuriHalNfcTechIso14443b] = &furi_hal_nfc_iso14443b, + [FuriHalNfcTechIso15693] = &furi_hal_nfc_iso15693, + [FuriHalNfcTechFelica] = &furi_hal_nfc_felica, + // Add new technologies here +}; + +FuriHalNfc furi_hal_nfc; + +static FuriHalNfcError furi_hal_nfc_turn_on_osc(FuriHalSpiBusHandle* handle) { + FuriHalNfcError error = FuriHalNfcErrorNone; + furi_hal_nfc_event_start(); + + if(!st25r3916_check_reg( + handle, + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en, + ST25R3916_REG_OP_CONTROL_en)) { + st25r3916_mask_irq(handle, ~ST25R3916_IRQ_MASK_OSC); + st25r3916_set_reg_bits(handle, ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_en); + furi_hal_nfc_event_wait_for_specific_irq(handle, ST25R3916_IRQ_MASK_OSC, 10); + } + // Disable IRQs + st25r3916_mask_irq(handle, ST25R3916_IRQ_MASK_ALL); + + bool osc_on = st25r3916_check_reg( + handle, + ST25R3916_REG_AUX_DISPLAY, + ST25R3916_REG_AUX_DISPLAY_osc_ok, + ST25R3916_REG_AUX_DISPLAY_osc_ok); + if(!osc_on) { + error = FuriHalNfcErrorOscillator; + } + + return error; +} + +FuriHalNfcError furi_hal_nfc_is_hal_ready() { + FuriHalNfcError error = FuriHalNfcErrorNone; + + do { + error = furi_hal_nfc_acquire(); + if(error != FuriHalNfcErrorNone) break; + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + uint8_t chip_id = 0; + st25r3916_read_reg(handle, ST25R3916_REG_IC_IDENTITY, &chip_id); + if((chip_id & ST25R3916_REG_IC_IDENTITY_ic_type_mask) != + ST25R3916_REG_IC_IDENTITY_ic_type_st25r3916) { + FURI_LOG_E(TAG, "Wrong chip id"); + error = FuriHalNfcErrorCommunication; + } + + furi_hal_nfc_release(); + } while(false); + + return error; +} + +FuriHalNfcError furi_hal_nfc_init() { + furi_assert(furi_hal_nfc.mutex == NULL); + + furi_hal_nfc.mutex = furi_mutex_alloc(FuriMutexTypeNormal); + FuriHalNfcError error = FuriHalNfcErrorNone; + + furi_hal_nfc_event_init(); + furi_hal_nfc_event_start(); + + do { + error = furi_hal_nfc_acquire(); + if(error != FuriHalNfcErrorNone) { + furi_hal_nfc_low_power_mode_start(); + } + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + // Set default state + st25r3916_direct_cmd(handle, ST25R3916_CMD_SET_DEFAULT); + // Increase IO driver strength of MISO and IRQ + st25r3916_write_reg(handle, ST25R3916_REG_IO_CONF2, ST25R3916_REG_IO_CONF2_io_drv_lvl); + // Check chip ID + uint8_t chip_id = 0; + st25r3916_read_reg(handle, ST25R3916_REG_IC_IDENTITY, &chip_id); + if((chip_id & ST25R3916_REG_IC_IDENTITY_ic_type_mask) != + ST25R3916_REG_IC_IDENTITY_ic_type_st25r3916) { + FURI_LOG_E(TAG, "Wrong chip id"); + error = FuriHalNfcErrorCommunication; + furi_hal_nfc_low_power_mode_start(); + furi_hal_nfc_release(); + break; + } + // Clear interrupts + st25r3916_get_irq(handle); + // Mask all interrupts + st25r3916_mask_irq(handle, ST25R3916_IRQ_MASK_ALL); + // Enable interrupts + furi_hal_nfc_init_gpio_isr(); + // Disable internal overheat protection + st25r3916_change_test_reg_bits(handle, 0x04, 0x10, 0x10); + + error = furi_hal_nfc_turn_on_osc(handle); + if(error != FuriHalNfcErrorNone) { + furi_hal_nfc_low_power_mode_start(); + furi_hal_nfc_release(); + break; + } + + // Measure voltage + // Set measure power supply voltage source + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_REGULATOR_CONTROL, + ST25R3916_REG_REGULATOR_CONTROL_mpsv_mask, + ST25R3916_REG_REGULATOR_CONTROL_mpsv_vdd); + // Enable timer and interrupt register + st25r3916_mask_irq(handle, ~ST25R3916_IRQ_MASK_DCT); + st25r3916_direct_cmd(handle, ST25R3916_CMD_MEASURE_VDD); + furi_hal_nfc_event_wait_for_specific_irq(handle, ST25R3916_IRQ_MASK_DCT, 100); + st25r3916_mask_irq(handle, ST25R3916_IRQ_MASK_ALL); + uint8_t ad_res = 0; + st25r3916_read_reg(handle, ST25R3916_REG_AD_RESULT, &ad_res); + uint16_t mV = ((uint16_t)ad_res) * 23U; + mV += (((((uint16_t)ad_res) * 4U) + 5U) / 10U); + + if(mV < 3600) { + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_IO_CONF2, + ST25R3916_REG_IO_CONF2_sup3V, + ST25R3916_REG_IO_CONF2_sup3V_3V); + } else { + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_IO_CONF2, + ST25R3916_REG_IO_CONF2_sup3V, + ST25R3916_REG_IO_CONF2_sup3V_5V); + } + + // Disable MCU CLK + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_IO_CONF1, + ST25R3916_REG_IO_CONF1_out_cl_mask | ST25R3916_REG_IO_CONF1_lf_clk_off, + 0x07); + // Disable MISO pull-down + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_IO_CONF2, + ST25R3916_REG_IO_CONF2_miso_pd1 | ST25R3916_REG_IO_CONF2_miso_pd2, + 0x00); + // Set tx driver resistance to 1 Om + st25r3916_change_reg_bits( + handle, ST25R3916_REG_TX_DRIVER, ST25R3916_REG_TX_DRIVER_d_res_mask, 0x00); + // Use minimum non-overlap + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_RES_AM_MOD, + ST25R3916_REG_RES_AM_MOD_fa3_f, + ST25R3916_REG_RES_AM_MOD_fa3_f); + + // Set activation threashold + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_FIELD_THRESHOLD_ACTV, + ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_mask, + ST25R3916_REG_FIELD_THRESHOLD_ACTV_trg_105mV); + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_FIELD_THRESHOLD_ACTV, + ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_mask, + ST25R3916_REG_FIELD_THRESHOLD_ACTV_rfe_105mV); + // Set deactivation threashold + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_FIELD_THRESHOLD_DEACTV, + ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_mask, + ST25R3916_REG_FIELD_THRESHOLD_DEACTV_trg_75mV); + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_FIELD_THRESHOLD_DEACTV, + ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_mask, + ST25R3916_REG_FIELD_THRESHOLD_DEACTV_rfe_75mV); + // Enable external load modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_AUX_MOD, + ST25R3916_REG_AUX_MOD_lm_ext, + ST25R3916_REG_AUX_MOD_lm_ext); + // Enable internal load modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_AUX_MOD, + ST25R3916_REG_AUX_MOD_lm_dri, + ST25R3916_REG_AUX_MOD_lm_dri); + // Adjust FDT + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_PASSIVE_TARGET, + ST25R3916_REG_PASSIVE_TARGET_fdel_mask, + (5U << ST25R3916_REG_PASSIVE_TARGET_fdel_shift)); + // Reduce RFO resistance in Modulated state + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_PT_MOD, + ST25R3916_REG_PT_MOD_ptm_res_mask | ST25R3916_REG_PT_MOD_pt_res_mask, + 0x0f); + // Enable RX start on first 4 bits + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_EMD_SUP_CONF, + ST25R3916_REG_EMD_SUP_CONF_rx_start_emv, + ST25R3916_REG_EMD_SUP_CONF_rx_start_emv_on); + // Set antena tunning + st25r3916_change_reg_bits(handle, ST25R3916_REG_ANT_TUNE_A, 0xff, 0x82); + st25r3916_change_reg_bits(handle, ST25R3916_REG_ANT_TUNE_B, 0xff, 0x82); + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en_fd_mask, + ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); + + // Perform calibration + if(st25r3916_check_reg( + handle, + ST25R3916_REG_REGULATOR_CONTROL, + ST25R3916_REG_REGULATOR_CONTROL_reg_s, + 0x00)) { + FURI_LOG_I(TAG, "Adjusting regulators"); + // Reset logic + st25r3916_set_reg_bits( + handle, ST25R3916_REG_REGULATOR_CONTROL, ST25R3916_REG_REGULATOR_CONTROL_reg_s); + st25r3916_clear_reg_bits( + handle, ST25R3916_REG_REGULATOR_CONTROL, ST25R3916_REG_REGULATOR_CONTROL_reg_s); + st25r3916_direct_cmd(handle, ST25R3916_CMD_ADJUST_REGULATORS); + furi_delay_ms(6); + } + + furi_hal_nfc_low_power_mode_start(); + furi_hal_nfc_release(); + } while(false); + + return error; +} + +static bool furi_hal_nfc_is_mine() { + return (furi_mutex_get_owner(furi_hal_nfc.mutex) == furi_thread_get_current_id()); +} + +FuriHalNfcError furi_hal_nfc_acquire() { + furi_check(furi_hal_nfc.mutex); + + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_nfc); + + FuriHalNfcError error = FuriHalNfcErrorNone; + if(furi_mutex_acquire(furi_hal_nfc.mutex, 100) != FuriStatusOk) { + furi_hal_spi_release(&furi_hal_spi_bus_handle_nfc); + error = FuriHalNfcErrorBusy; + } + + return error; +} + +FuriHalNfcError furi_hal_nfc_release() { + furi_check(furi_hal_nfc.mutex); + furi_check(furi_hal_nfc_is_mine()); + furi_check(furi_mutex_release(furi_hal_nfc.mutex) == FuriStatusOk); + + furi_hal_spi_release(&furi_hal_spi_bus_handle_nfc); + + return FuriHalNfcErrorNone; +} + +FuriHalNfcError furi_hal_nfc_low_power_mode_start() { + FuriHalNfcError error = FuriHalNfcErrorNone; + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP); + st25r3916_clear_reg_bits( + handle, + ST25R3916_REG_OP_CONTROL, + (ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en | + ST25R3916_REG_OP_CONTROL_wu | ST25R3916_REG_OP_CONTROL_tx_en | + ST25R3916_REG_OP_CONTROL_en_fd_mask)); + furi_hal_nfc_deinit_gpio_isr(); + furi_hal_nfc_timers_deinit(); + furi_hal_nfc_event_stop(); + + return error; +} + +FuriHalNfcError furi_hal_nfc_low_power_mode_stop() { + FuriHalNfcError error = FuriHalNfcErrorNone; + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + do { + furi_hal_nfc_init_gpio_isr(); + furi_hal_nfc_timers_init(); + error = furi_hal_nfc_turn_on_osc(handle); + if(error != FuriHalNfcErrorNone) break; + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en_fd_mask, + ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); + + } while(false); + + return error; +} + +static FuriHalNfcError furi_hal_nfc_poller_init_common(FuriHalSpiBusHandle* handle) { + // Disable wake up + st25r3916_clear_reg_bits(handle, ST25R3916_REG_OP_CONTROL, ST25R3916_REG_OP_CONTROL_wu); + // Enable correlator + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_AUX, + ST25R3916_REG_AUX_dis_corr, + ST25R3916_REG_AUX_dis_corr_correlator); + + st25r3916_change_reg_bits(handle, ST25R3916_REG_ANT_TUNE_A, 0xff, 0x82); + st25r3916_change_reg_bits(handle, ST25R3916_REG_ANT_TUNE_B, 0xFF, 0x82); + + st25r3916_write_reg(handle, ST25R3916_REG_OVERSHOOT_CONF1, 0x00); + st25r3916_write_reg(handle, ST25R3916_REG_OVERSHOOT_CONF2, 0x00); + st25r3916_write_reg(handle, ST25R3916_REG_UNDERSHOOT_CONF1, 0x00); + st25r3916_write_reg(handle, ST25R3916_REG_UNDERSHOOT_CONF2, 0x00); + + return FuriHalNfcErrorNone; +} + +static FuriHalNfcError furi_hal_nfc_listener_init_common(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + // No common listener configuration + return FuriHalNfcErrorNone; +} + +FuriHalNfcError furi_hal_nfc_set_mode(FuriHalNfcMode mode, FuriHalNfcTech tech) { + furi_assert(mode < FuriHalNfcModeNum); + furi_assert(tech < FuriHalNfcTechNum); + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + FuriHalNfcError error = FuriHalNfcErrorNone; + + if(mode == FuriHalNfcModePoller) { + do { + error = furi_hal_nfc_poller_init_common(handle); + if(error != FuriHalNfcErrorNone) break; + error = furi_hal_nfc_tech[tech]->poller.init(handle); + } while(false); + + } else if(mode == FuriHalNfcModeListener) { + do { + error = furi_hal_nfc_listener_init_common(handle); + if(error != FuriHalNfcErrorNone) break; + error = furi_hal_nfc_tech[tech]->listener.init(handle); + } while(false); + } + + furi_hal_nfc.mode = mode; + furi_hal_nfc.tech = tech; + return error; +} + +FuriHalNfcError furi_hal_nfc_reset_mode() { + FuriHalNfcError error = FuriHalNfcErrorNone; + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP); + + const FuriHalNfcMode mode = furi_hal_nfc.mode; + const FuriHalNfcTech tech = furi_hal_nfc.tech; + if(mode == FuriHalNfcModePoller) { + error = furi_hal_nfc_tech[tech]->poller.deinit(handle); + } else if(mode == FuriHalNfcModeListener) { + error = furi_hal_nfc_tech[tech]->listener.deinit(handle); + } + // Set default value in mode register + st25r3916_write_reg(handle, ST25R3916_REG_MODE, ST25R3916_REG_MODE_om0); + st25r3916_write_reg(handle, ST25R3916_REG_STREAM_MODE, 0); + st25r3916_clear_reg_bits(handle, ST25R3916_REG_AUX, ST25R3916_REG_AUX_no_crc_rx); + st25r3916_clear_reg_bits( + handle, + ST25R3916_REG_BIT_RATE, + ST25R3916_REG_BIT_RATE_txrate_mask | ST25R3916_REG_BIT_RATE_rxrate_mask); + + // Write default values + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF1, 0); + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF2, + ST25R3916_REG_RX_CONF2_sqm_dyn | ST25R3916_REG_RX_CONF2_agc_en | + ST25R3916_REG_RX_CONF2_agc_m); + + st25r3916_write_reg( + handle, + ST25R3916_REG_CORR_CONF1, + ST25R3916_REG_CORR_CONF1_corr_s7 | ST25R3916_REG_CORR_CONF1_corr_s4 | + ST25R3916_REG_CORR_CONF1_corr_s1 | ST25R3916_REG_CORR_CONF1_corr_s0); + st25r3916_write_reg(handle, ST25R3916_REG_CORR_CONF2, 0); + + return error; +} + +FuriHalNfcError furi_hal_nfc_field_detect_start() { + FuriHalNfcError error = FuriHalNfcErrorNone; + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + st25r3916_write_reg( + handle, + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_en_fd_mask); + st25r3916_write_reg( + handle, ST25R3916_REG_MODE, ST25R3916_REG_MODE_targ | ST25R3916_REG_MODE_om0); + + return error; +} + +FuriHalNfcError furi_hal_nfc_field_detect_stop() { + FuriHalNfcError error = FuriHalNfcErrorNone; + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + st25r3916_clear_reg_bits( + handle, + ST25R3916_REG_OP_CONTROL, + (ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_en_fd_mask)); + + return error; +} + +bool furi_hal_nfc_field_is_present() { + bool is_present = false; + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + if(st25r3916_check_reg( + handle, + ST25R3916_REG_AUX_DISPLAY, + ST25R3916_REG_AUX_DISPLAY_efd_o, + ST25R3916_REG_AUX_DISPLAY_efd_o)) { + is_present = true; + } + + return is_present; +} + +FuriHalNfcError furi_hal_nfc_poller_field_on() { + FuriHalNfcError error = FuriHalNfcErrorNone; + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + if(!st25r3916_check_reg( + handle, + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_tx_en, + ST25R3916_REG_OP_CONTROL_tx_en)) { + // Set min guard time + st25r3916_write_reg(handle, ST25R3916_REG_FIELD_ON_GT, 0); + // Enable tx rx + st25r3916_set_reg_bits( + handle, + ST25R3916_REG_OP_CONTROL, + (ST25R3916_REG_OP_CONTROL_rx_en | ST25R3916_REG_OP_CONTROL_tx_en)); + } + + return error; +} + +FuriHalNfcError furi_hal_nfc_poller_tx_common( + FuriHalSpiBusHandle* handle, + const uint8_t* tx_data, + size_t tx_bits) { + furi_assert(tx_data); + + FuriHalNfcError err = FuriHalNfcErrorNone; + + // Prepare tx + st25r3916_direct_cmd(handle, ST25R3916_CMD_CLEAR_FIFO); + st25r3916_clear_reg_bits( + handle, ST25R3916_REG_TIMER_EMV_CONTROL, ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv); + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_ISO14443A_NFC, + (ST25R3916_REG_ISO14443A_NFC_no_tx_par | ST25R3916_REG_ISO14443A_NFC_no_rx_par), + (ST25R3916_REG_ISO14443A_NFC_no_tx_par_off | ST25R3916_REG_ISO14443A_NFC_no_rx_par_off)); + uint32_t interrupts = + (ST25R3916_IRQ_MASK_FWL | ST25R3916_IRQ_MASK_TXE | ST25R3916_IRQ_MASK_RXS | + ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_PAR | ST25R3916_IRQ_MASK_CRC | + ST25R3916_IRQ_MASK_ERR1 | ST25R3916_IRQ_MASK_ERR2 | ST25R3916_IRQ_MASK_NRE); + // Clear interrupts + st25r3916_get_irq(handle); + // Enable interrupts + st25r3916_mask_irq(handle, ~interrupts); + + st25r3916_write_fifo(handle, tx_data, tx_bits); + st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSMIT_WITHOUT_CRC); + + return err; +} + +FuriHalNfcError furi_hal_nfc_common_fifo_tx( + FuriHalSpiBusHandle* handle, + const uint8_t* tx_data, + size_t tx_bits) { + FuriHalNfcError err = FuriHalNfcErrorNone; + + st25r3916_direct_cmd(handle, ST25R3916_CMD_CLEAR_FIFO); + st25r3916_write_fifo(handle, tx_data, tx_bits); + st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSMIT_WITHOUT_CRC); + + return err; +} + +FuriHalNfcError furi_hal_nfc_poller_tx(const uint8_t* tx_data, size_t tx_bits) { + furi_assert(furi_hal_nfc.mode == FuriHalNfcModePoller); + furi_assert(furi_hal_nfc.tech < FuriHalNfcTechNum); + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + return furi_hal_nfc_tech[furi_hal_nfc.tech]->poller.tx(handle, tx_data, tx_bits); +} + +FuriHalNfcError furi_hal_nfc_poller_rx(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits) { + furi_assert(furi_hal_nfc.mode == FuriHalNfcModePoller); + furi_assert(furi_hal_nfc.tech < FuriHalNfcTechNum); + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + return furi_hal_nfc_tech[furi_hal_nfc.tech]->poller.rx(handle, rx_data, rx_data_size, rx_bits); +} + +FuriHalNfcEvent furi_hal_nfc_poller_wait_event(uint32_t timeout_ms) { + furi_assert(furi_hal_nfc.mode == FuriHalNfcModePoller); + furi_assert(furi_hal_nfc.tech < FuriHalNfcTechNum); + + return furi_hal_nfc_tech[furi_hal_nfc.tech]->poller.wait_event(timeout_ms); +} + +FuriHalNfcEvent furi_hal_nfc_listener_wait_event(uint32_t timeout_ms) { + furi_assert(furi_hal_nfc.mode == FuriHalNfcModeListener); + furi_assert(furi_hal_nfc.tech < FuriHalNfcTechNum); + + return furi_hal_nfc_tech[furi_hal_nfc.tech]->listener.wait_event(timeout_ms); +} + +FuriHalNfcError furi_hal_nfc_listener_tx(const uint8_t* tx_data, size_t tx_bits) { + furi_assert(tx_data); + + furi_assert(furi_hal_nfc.mode == FuriHalNfcModeListener); + furi_assert(furi_hal_nfc.tech < FuriHalNfcTechNum); + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + return furi_hal_nfc_tech[furi_hal_nfc.tech]->listener.tx(handle, tx_data, tx_bits); +} + +FuriHalNfcError furi_hal_nfc_common_fifo_rx( + FuriHalSpiBusHandle* handle, + uint8_t* rx_data, + size_t rx_data_size, + size_t* rx_bits) { + FuriHalNfcError error = FuriHalNfcErrorNone; + + if(!st25r3916_read_fifo(handle, rx_data, rx_data_size, rx_bits)) { + error = FuriHalNfcErrorBufferOverflow; + } + + return error; +} + +FuriHalNfcError furi_hal_nfc_listener_rx(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits) { + furi_assert(rx_data); + furi_assert(rx_bits); + + furi_assert(furi_hal_nfc.mode == FuriHalNfcModeListener); + furi_assert(furi_hal_nfc.tech < FuriHalNfcTechNum); + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + return furi_hal_nfc_tech[furi_hal_nfc.tech]->listener.rx( + handle, rx_data, rx_data_size, rx_bits); +} + +FuriHalNfcError furi_hal_nfc_trx_reset() { + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP); + + return FuriHalNfcErrorNone; +} + +FuriHalNfcError furi_hal_nfc_listener_sleep() { + furi_assert(furi_hal_nfc.mode == FuriHalNfcModeListener); + furi_assert(furi_hal_nfc.tech < FuriHalNfcTechNum); + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + return furi_hal_nfc_tech[furi_hal_nfc.tech]->listener.sleep(handle); +} + +FuriHalNfcError furi_hal_nfc_listener_idle() { + furi_assert(furi_hal_nfc.mode == FuriHalNfcModeListener); + furi_assert(furi_hal_nfc.tech < FuriHalNfcTechNum); + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + return furi_hal_nfc_tech[furi_hal_nfc.tech]->listener.idle(handle); +} + +FuriHalNfcError furi_hal_nfc_listener_enable_rx() { + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + st25r3916_direct_cmd(handle, ST25R3916_CMD_UNMASK_RECEIVE_DATA); + + return FuriHalNfcErrorNone; +} diff --git a/targets/f7/furi_hal/furi_hal_nfc_event.c b/targets/f7/furi_hal/furi_hal_nfc_event.c new file mode 100644 index 00000000000..266b606fd1b --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_nfc_event.c @@ -0,0 +1,117 @@ +#include + +FuriHalNfcEventInternal* furi_hal_nfc_event = NULL; + +void furi_hal_nfc_event_init() { + furi_hal_nfc_event = malloc(sizeof(FuriHalNfcEventInternal)); +} + +FuriHalNfcError furi_hal_nfc_event_start() { + furi_assert(furi_hal_nfc_event); + + furi_hal_nfc_event->thread = furi_thread_get_current_id(); + furi_thread_flags_clear(FURI_HAL_NFC_EVENT_INTERNAL_ALL); + + return FuriHalNfcErrorNone; +} + +FuriHalNfcError furi_hal_nfc_event_stop() { + furi_assert(furi_hal_nfc_event); + + furi_hal_nfc_event->thread = NULL; + + return FuriHalNfcErrorNone; +} + +void furi_hal_nfc_event_set(FuriHalNfcEventInternalType event) { + furi_assert(furi_hal_nfc_event); + + if(furi_hal_nfc_event->thread) { + furi_thread_flags_set(furi_hal_nfc_event->thread, event); + } +} + +FuriHalNfcError furi_hal_nfc_abort() { + furi_hal_nfc_event_set(FuriHalNfcEventInternalTypeAbort); + return FuriHalNfcErrorNone; +} + +FuriHalNfcEvent furi_hal_nfc_wait_event_common(uint32_t timeout_ms) { + furi_assert(furi_hal_nfc_event); + furi_assert(furi_hal_nfc_event->thread); + + FuriHalNfcEvent event = 0; + uint32_t event_timeout = timeout_ms == FURI_HAL_NFC_EVENT_WAIT_FOREVER ? FuriWaitForever : + timeout_ms; + uint32_t event_flag = + furi_thread_flags_wait(FURI_HAL_NFC_EVENT_INTERNAL_ALL, FuriFlagWaitAny, event_timeout); + if(event_flag != (unsigned)FuriFlagErrorTimeout) { + if(event_flag & FuriHalNfcEventInternalTypeIrq) { + furi_thread_flags_clear(FuriHalNfcEventInternalTypeIrq); + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + uint32_t irq = furi_hal_nfc_get_irq(handle); + if(irq & ST25R3916_IRQ_MASK_OSC) { + event |= FuriHalNfcEventOscOn; + } + if(irq & ST25R3916_IRQ_MASK_TXE) { + event |= FuriHalNfcEventTxEnd; + } + if(irq & ST25R3916_IRQ_MASK_RXS) { + event |= FuriHalNfcEventRxStart; + } + if(irq & ST25R3916_IRQ_MASK_RXE) { + event |= FuriHalNfcEventRxEnd; + } + if(irq & ST25R3916_IRQ_MASK_COL) { + event |= FuriHalNfcEventCollision; + } + if(irq & ST25R3916_IRQ_MASK_EON) { + event |= FuriHalNfcEventFieldOn; + } + if(irq & ST25R3916_IRQ_MASK_EOF) { + event |= FuriHalNfcEventFieldOff; + } + if(irq & ST25R3916_IRQ_MASK_WU_A) { + event |= FuriHalNfcEventListenerActive; + } + if(irq & ST25R3916_IRQ_MASK_WU_A_X) { + event |= FuriHalNfcEventListenerActive; + } + } + if(event_flag & FuriHalNfcEventInternalTypeTimerFwtExpired) { + event |= FuriHalNfcEventTimerFwtExpired; + furi_thread_flags_clear(FuriHalNfcEventInternalTypeTimerFwtExpired); + } + if(event_flag & FuriHalNfcEventInternalTypeTimerBlockTxExpired) { + event |= FuriHalNfcEventTimerBlockTxExpired; + furi_thread_flags_clear(FuriHalNfcEventInternalTypeTimerBlockTxExpired); + } + if(event_flag & FuriHalNfcEventInternalTypeAbort) { + event |= FuriHalNfcEventAbortRequest; + furi_thread_flags_clear(FuriHalNfcEventInternalTypeAbort); + } + } else { + event = FuriHalNfcEventTimeout; + } + + return event; +} + +bool furi_hal_nfc_event_wait_for_specific_irq( + FuriHalSpiBusHandle* handle, + uint32_t mask, + uint32_t timeout_ms) { + furi_assert(furi_hal_nfc_event); + furi_assert(furi_hal_nfc_event->thread); + + bool irq_received = false; + uint32_t event_flag = + furi_thread_flags_wait(FuriHalNfcEventInternalTypeIrq, FuriFlagWaitAny, timeout_ms); + if(event_flag == FuriHalNfcEventInternalTypeIrq) { + uint32_t irq = furi_hal_nfc_get_irq(handle); + irq_received = ((irq & mask) == mask); + furi_thread_flags_clear(FuriHalNfcEventInternalTypeIrq); + } + + return irq_received; +} diff --git a/targets/f7/furi_hal/furi_hal_nfc_felica.c b/targets/f7/furi_hal/furi_hal_nfc_felica.c new file mode 100644 index 00000000000..f762a0ed32b --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_nfc_felica.c @@ -0,0 +1,205 @@ +#include "furi_hal_nfc_i.h" +#include "furi_hal_nfc_tech_i.h" + +// Prevent FDT timer from starting +#define FURI_HAL_NFC_FELICA_LISTENER_FDT_COMP_FC (INT32_MAX) + +static FuriHalNfcError furi_hal_nfc_felica_poller_init(FuriHalSpiBusHandle* handle) { + // Enable Felica mode, AM modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_MODE, + ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am, + ST25R3916_REG_MODE_om_felica | ST25R3916_REG_MODE_tr_am_am); + + // 10% ASK modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_TX_DRIVER, + ST25R3916_REG_TX_DRIVER_am_mod_mask, + ST25R3916_REG_TX_DRIVER_am_mod_10percent); + + // Use regulator AM, resistive AM disabled + st25r3916_clear_reg_bits( + handle, + ST25R3916_REG_AUX_MOD, + ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am); + + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_BIT_RATE, + ST25R3916_REG_BIT_RATE_txrate_mask | ST25R3916_REG_BIT_RATE_rxrate_mask, + ST25R3916_REG_BIT_RATE_txrate_212 | ST25R3916_REG_BIT_RATE_rxrate_212); + + // Receive configuration + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF1, + ST25R3916_REG_RX_CONF1_lp0 | ST25R3916_REG_RX_CONF1_hz_12_80khz); + + // Correlator setup + st25r3916_write_reg( + handle, + ST25R3916_REG_CORR_CONF1, + ST25R3916_REG_CORR_CONF1_corr_s6 | ST25R3916_REG_CORR_CONF1_corr_s4 | + ST25R3916_REG_CORR_CONF1_corr_s3); + + return FuriHalNfcErrorNone; +} + +static FuriHalNfcError furi_hal_nfc_felica_poller_deinit(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + + return FuriHalNfcErrorNone; +} + +static FuriHalNfcError furi_hal_nfc_felica_listener_init(FuriHalSpiBusHandle* handle) { + furi_assert(handle); + st25r3916_write_reg( + handle, + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en | + ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); + + // Enable Target Felica mode, AM modulation + st25r3916_write_reg( + handle, + ST25R3916_REG_MODE, + ST25R3916_REG_MODE_targ_targ | ST25R3916_REG_MODE_om2 | ST25R3916_REG_MODE_tr_am); + + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_BIT_RATE, + ST25R3916_REG_BIT_RATE_txrate_mask | ST25R3916_REG_BIT_RATE_rxrate_mask, + ST25R3916_REG_BIT_RATE_txrate_212 | ST25R3916_REG_BIT_RATE_rxrate_212); + + // Receive configuration + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF1, + ST25R3916_REG_RX_CONF1_lp0 | ST25R3916_REG_RX_CONF1_hz_12_80khz); + + // AGC enabled, ratio 3:1, squelch after TX + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF2, + ST25R3916_REG_RX_CONF2_agc6_3 | ST25R3916_REG_RX_CONF2_agc_m | + ST25R3916_REG_RX_CONF2_agc_en | ST25R3916_REG_RX_CONF2_sqm_dyn); + // HF operation, full gain on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF3, 0x00); + // No gain reduction on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF4, 0x00); + // 10% ASK modulation + st25r3916_write_reg(handle, ST25R3916_REG_TX_DRIVER, ST25R3916_REG_TX_DRIVER_am_mod_10percent); + + // Correlator setup + st25r3916_write_reg( + handle, + ST25R3916_REG_CORR_CONF1, + ST25R3916_REG_CORR_CONF1_corr_s6 | ST25R3916_REG_CORR_CONF1_corr_s4 | + ST25R3916_REG_CORR_CONF1_corr_s2); + + // Sleep mode disable, 424kHz mode off + st25r3916_write_reg(handle, ST25R3916_REG_CORR_CONF2, 0x00); + + st25r3916_write_reg(handle, ST25R3916_REG_MASK_RX_TIMER, 0x02); + + st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP); + uint32_t interrupts = + (ST25R3916_IRQ_MASK_FWL | ST25R3916_IRQ_MASK_TXE | ST25R3916_IRQ_MASK_RXS | + ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_PAR | ST25R3916_IRQ_MASK_CRC | + ST25R3916_IRQ_MASK_ERR1 | ST25R3916_IRQ_MASK_ERR2 | ST25R3916_IRQ_MASK_NRE | + ST25R3916_IRQ_MASK_EON | ST25R3916_IRQ_MASK_EOF | ST25R3916_IRQ_MASK_WU_A_X | + ST25R3916_IRQ_MASK_WU_A); + // Clear interrupts + st25r3916_get_irq(handle); + + st25r3916_write_reg( + handle, + ST25R3916_REG_PASSIVE_TARGET, + ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a | ST25R3916_REG_PASSIVE_TARGET_d_ac_ap2p | + ST25R3916_REG_PASSIVE_TARGET_fdel_1); + // Enable interrupts + st25r3916_mask_irq(handle, ~interrupts); + st25r3916_direct_cmd(handle, ST25R3916_CMD_GOTO_SENSE); + + return FuriHalNfcErrorNone; +} + +static FuriHalNfcError furi_hal_nfc_felica_listener_deinit(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + return FuriHalNfcErrorNone; +} + +static FuriHalNfcEvent furi_hal_nfc_felica_listener_wait_event(uint32_t timeout_ms) { + UNUSED(timeout_ms); + FuriHalNfcEvent event = furi_hal_nfc_wait_event_common(timeout_ms); + + return event; +} + +FuriHalNfcError furi_hal_nfc_felica_listener_tx( + FuriHalSpiBusHandle* handle, + const uint8_t* tx_data, + size_t tx_bits) { + UNUSED(handle); + UNUSED(tx_data); + UNUSED(tx_bits); + return FuriHalNfcErrorNone; +} + +FuriHalNfcError furi_hal_nfc_felica_listener_sleep(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + return FuriHalNfcErrorNone; +} + +FuriHalNfcError furi_hal_nfc_felica_listener_idle(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + return FuriHalNfcErrorNone; +} + +FuriHalNfcError furi_hal_nfc_felica_listener_set_sensf_res_data( + const uint8_t* idm, + const uint8_t idm_len, + const uint8_t* pmm, + const uint8_t pmm_len) { + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + // Write PT Memory + uint8_t pt_memory[19] = {}; + pt_memory[2] = 0x01; + memcpy(pt_memory + 3, idm, idm_len); + memcpy(pt_memory + 3 + idm_len, pmm, pmm_len); + st25r3916_write_ptf_mem(handle, pt_memory, sizeof(pt_memory)); + return FuriHalNfcErrorNone; +} + +const FuriHalNfcTechBase furi_hal_nfc_felica = { + .poller = + { + .compensation = + { + .fdt = FURI_HAL_NFC_POLLER_FDT_COMP_FC, + .fwt = FURI_HAL_NFC_POLLER_FWT_COMP_FC, + }, + .init = furi_hal_nfc_felica_poller_init, + .deinit = furi_hal_nfc_felica_poller_deinit, + .wait_event = furi_hal_nfc_wait_event_common, + .tx = furi_hal_nfc_poller_tx_common, + .rx = furi_hal_nfc_common_fifo_rx, + }, + + .listener = + { + .compensation = + { + .fdt = FURI_HAL_NFC_FELICA_LISTENER_FDT_COMP_FC, + }, + .init = furi_hal_nfc_felica_listener_init, + .deinit = furi_hal_nfc_felica_listener_deinit, + .wait_event = furi_hal_nfc_felica_listener_wait_event, + .tx = furi_hal_nfc_felica_listener_tx, + .rx = furi_hal_nfc_common_fifo_rx, + .sleep = furi_hal_nfc_felica_listener_sleep, + .idle = furi_hal_nfc_felica_listener_idle, + }, +}; diff --git a/targets/f7/furi_hal/furi_hal_nfc_i.h b/targets/f7/furi_hal/furi_hal_nfc_i.h new file mode 100644 index 00000000000..53a25644d0d --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_nfc_i.h @@ -0,0 +1,191 @@ +/** + * @file furi_hal_nfc_i.h + * @brief NFC HAL library (private definitions). + * + * This file is an implementation detail. It must not be included in + * any public API-related headers. + */ +#pragma once + +#include +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Common frame delay time compensation for pollers. */ +#define FURI_HAL_NFC_POLLER_FDT_COMP_FC (-500) +/** @brief Common frame wait time compensation for pollers. */ +#define FURI_HAL_NFC_POLLER_FWT_COMP_FC (FURI_HAL_NFC_POLLER_FDT_COMP_FC) + +/** + * @brief Enumeration containing bitmask values for NFC HAL internal events. + */ +typedef enum { + FuriHalNfcEventInternalTypeAbort = (1U << 0), /**< Abort waiting for hardware events. */ + FuriHalNfcEventInternalTypeIrq = (1U << 1), /**< NFC hardware interrupt has occurred. */ + FuriHalNfcEventInternalTypeTimerFwtExpired = + (1U << 2), /**< Frame wait time timeout has expired. */ + FuriHalNfcEventInternalTypeTimerBlockTxExpired = + (1U << 3), /**< Transmission block timeout has expired. */ + FuriHalNfcEventInternalTypeTransparentDataReceived = + (1U << 4), /**< Data was received in transparent mode. */ +} FuriHalNfcEventInternalType; + +/** @brief Special bitmask value of all internal events. */ +#define FURI_HAL_NFC_EVENT_INTERNAL_ALL \ + ((FuriHalNfcEventInternalTypeAbort | FuriHalNfcEventInternalTypeIrq | \ + FuriHalNfcEventInternalTypeTimerFwtExpired | \ + FuriHalNfcEventInternalTypeTimerBlockTxExpired | \ + FuriHalNfcEventInternalTypeTransparentDataReceived)) + +/** + * @brief NFC HAL internal event structure. + */ +typedef struct { + FuriThreadId thread; /**< Identifier of the thread that will be receiving events. */ + void* context; /**< Pointer to the user-provided context (will be passed to the event callback). */ +} FuriHalNfcEventInternal; + +/** + * @brief NFC HAL global state structure. + */ +typedef struct { + FuriMutex* mutex; /**< Pointer to the mutex serving as global NFC HAL lock. */ + FuriHalNfcMode mode; /**< Currently selected operating mode. */ + FuriHalNfcTech tech; /**< Currently selected NFC technology. */ +} FuriHalNfc; + +/** + * @brief NFC HAL global state object declaration. + */ +extern FuriHalNfc furi_hal_nfc; + +/** + * @brief Initialise NFC HAL event system. + */ +void furi_hal_nfc_event_init(); + +/** + * @brief Forcibly emit (a) particular internal event(s). + * + * @param[in] event bitmask of one or more events to be emitted. + */ +void furi_hal_nfc_event_set(FuriHalNfcEventInternalType event); + +/** + * @brief Initialise GPIO to generate an interrupt from the NFC hardware. + */ +void furi_hal_nfc_init_gpio_isr(); + +/** + * @brief Disable interrupts from the NFC hardware. + */ +void furi_hal_nfc_deinit_gpio_isr(); + +/** + * @brief Initialise all NFC timers. + */ +void furi_hal_nfc_timers_init(); + +/** + * @brief Disable all NFC timers. + */ +void furi_hal_nfc_timers_deinit(); + +/** + * @brief Get the interrupt bitmask from the NFC hardware. + * + * @param[in,out] handle pointer to the SPI handle associated with the NFC chip. + * @returns bitmask of zero or more occurred interrupts. + */ +uint32_t furi_hal_nfc_get_irq(FuriHalSpiBusHandle* handle); + +/** + * @brief Wait until a specified type of interrupt occurs. + * + * @param[in,out] handle pointer to the SPI handle associated with the NFC chip. + * @param[in] mask bitmask of one or more interrupts to wait for. + * @param[in] timeout_ms maximum time to wait for an interrupt, in milliseconds. + * @returns true if specified interrupt(s) have occured within timeout, false otherwise. + */ +bool furi_hal_nfc_event_wait_for_specific_irq( + FuriHalSpiBusHandle* handle, + uint32_t mask, + uint32_t timeout_ms); + +/** + * @brief Wait for any event to occur. + * + * This function is common to all technologies. + * + * @param[in] timeout_ms maximum time to wait for an event, in milliseconds. + * @returns bitmask of zero or more occurred events. + */ +FuriHalNfcEvent furi_hal_nfc_wait_event_common(uint32_t timeout_ms); + +/** + * @brief Start reception in listener mode. + * + * This function is common to all technologies. + * + * @param[in,out] handle pointer to the SPI handle associated with the NFC chip. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_common_listener_rx_start(FuriHalSpiBusHandle* handle); + +/** + * @brief Transmit data using on-chip FIFO. + * + * This function is common to all technologies. + * + * @param[in,out] handle pointer to the SPI handle associated with the NFC chip. + * @param[in] tx_data pointer to a byte array containing the data to be transmitted. + * @param[in] tx_bits transmit data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_common_fifo_tx( + FuriHalSpiBusHandle* handle, + const uint8_t* tx_data, + size_t tx_bits); + +/** + * @brief Receive data using on-chip FIFO. + * + * This function is common to all technologies. + * + * @param[in,out] handle pointer to the SPI handle associated with the NFC chip. + * @param[out] rx_data pointer to a byte array to be filled with received data. + * @param[in] rx_data_size maximum received data size, in bytes. + * @param[out] rx_bits pointer to the variable to contain the received data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_common_fifo_rx( + FuriHalSpiBusHandle* handle, + uint8_t* rx_data, + size_t rx_data_size, + size_t* rx_bits); + +/** + * @brief Transmit data in poller mode. + * + * This function is common to all technologies. + * + * @param[in,out] handle pointer to the SPI handle associated with the NFC chip. + * @param[in] tx_data pointer to a byte array containing the data to be transmitted. + * @param[in] tx_bits transmit data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_poller_tx_common( + FuriHalSpiBusHandle* handle, + const uint8_t* tx_data, + size_t tx_bits); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/furi_hal/furi_hal_nfc_irq.c b/targets/f7/furi_hal/furi_hal_nfc_irq.c new file mode 100644 index 00000000000..170d8dee619 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_nfc_irq.c @@ -0,0 +1,28 @@ +#include "furi_hal_nfc_i.h" + +#include +#include + +static void furi_hal_nfc_int_callback() { + furi_hal_nfc_event_set(FuriHalNfcEventInternalTypeIrq); +} + +uint32_t furi_hal_nfc_get_irq(FuriHalSpiBusHandle* handle) { + uint32_t irq = 0; + while(furi_hal_gpio_read_port_pin(gpio_nfc_irq_rfid_pull.port, gpio_nfc_irq_rfid_pull.pin)) { + irq |= st25r3916_get_irq(handle); + } + return irq; +} + +void furi_hal_nfc_init_gpio_isr() { + furi_hal_gpio_init( + &gpio_nfc_irq_rfid_pull, GpioModeInterruptRise, GpioPullDown, GpioSpeedVeryHigh); + furi_hal_gpio_add_int_callback(&gpio_nfc_irq_rfid_pull, furi_hal_nfc_int_callback, NULL); + furi_hal_gpio_enable_int_callback(&gpio_nfc_irq_rfid_pull); +} + +void furi_hal_nfc_deinit_gpio_isr() { + furi_hal_gpio_remove_int_callback(&gpio_nfc_irq_rfid_pull); + furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} diff --git a/targets/f7/furi_hal/furi_hal_nfc_iso14443a.c b/targets/f7/furi_hal/furi_hal_nfc_iso14443a.c new file mode 100644 index 00000000000..278ecf38496 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_nfc_iso14443a.c @@ -0,0 +1,356 @@ +#include "furi_hal_nfc_i.h" +#include "furi_hal_nfc_tech_i.h" + +#include +#include + +#include + +#define TAG "FuriHalIso14443a" + +// Prevent FDT timer from starting +#define FURI_HAL_NFC_ISO14443A_LISTENER_FDT_COMP_FC (INT32_MAX) + +static Iso14443_3aSignal* iso14443_3a_signal = NULL; + +static FuriHalNfcError furi_hal_nfc_iso14443a_common_init(FuriHalSpiBusHandle* handle) { + // Common NFC-A settings, 106 kbps + + // 1st stage zero = 600kHz, 3rd stage zero = 200 kHz + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF1, ST25R3916_REG_RX_CONF1_z600k); + // AGC enabled, ratio 3:1, squelch after TX + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF2, + ST25R3916_REG_RX_CONF2_agc6_3 | ST25R3916_REG_RX_CONF2_agc_m | + ST25R3916_REG_RX_CONF2_agc_en | ST25R3916_REG_RX_CONF2_sqm_dyn); + // HF operation, full gain on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF3, 0x00); + // No gain reduction on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF4, 0x00); + // Correlator config + st25r3916_write_reg( + handle, + ST25R3916_REG_CORR_CONF1, + ST25R3916_REG_CORR_CONF1_corr_s0 | ST25R3916_REG_CORR_CONF1_corr_s4 | + ST25R3916_REG_CORR_CONF1_corr_s6); + // Sleep mode disable, 424kHz mode off + st25r3916_write_reg(handle, ST25R3916_REG_CORR_CONF2, 0x00); + + return FuriHalNfcErrorNone; +} + +static FuriHalNfcError furi_hal_nfc_iso14443a_poller_init(FuriHalSpiBusHandle* handle) { + // Enable ISO14443A mode, OOK modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_MODE, + ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am, + ST25R3916_REG_MODE_om_iso14443a | ST25R3916_REG_MODE_tr_am_ook); + + // Overshoot protection - is this necessary here? + st25r3916_change_reg_bits(handle, ST25R3916_REG_OVERSHOOT_CONF1, 0xff, 0x40); + st25r3916_change_reg_bits(handle, ST25R3916_REG_OVERSHOOT_CONF2, 0xff, 0x03); + st25r3916_change_reg_bits(handle, ST25R3916_REG_UNDERSHOOT_CONF1, 0xff, 0x40); + st25r3916_change_reg_bits(handle, ST25R3916_REG_UNDERSHOOT_CONF2, 0xff, 0x03); + + return furi_hal_nfc_iso14443a_common_init(handle); +} + +static FuriHalNfcError furi_hal_nfc_iso14443a_poller_deinit(FuriHalSpiBusHandle* handle) { + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_ISO14443A_NFC, + (ST25R3916_REG_ISO14443A_NFC_no_tx_par | ST25R3916_REG_ISO14443A_NFC_no_rx_par), + (ST25R3916_REG_ISO14443A_NFC_no_tx_par_off | ST25R3916_REG_ISO14443A_NFC_no_rx_par_off)); + + return FuriHalNfcErrorNone; +} + +static FuriHalNfcError furi_hal_nfc_iso14443a_listener_init(FuriHalSpiBusHandle* handle) { + furi_check(iso14443_3a_signal == NULL); + iso14443_3a_signal = iso14443_3a_signal_alloc(&gpio_spi_r_mosi); + + st25r3916_write_reg( + handle, + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en | + ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); + st25r3916_write_reg( + handle, ST25R3916_REG_MODE, ST25R3916_REG_MODE_targ_targ | ST25R3916_REG_MODE_om0); + st25r3916_write_reg( + handle, + ST25R3916_REG_PASSIVE_TARGET, + ST25R3916_REG_PASSIVE_TARGET_fdel_2 | ST25R3916_REG_PASSIVE_TARGET_fdel_0 | + ST25R3916_REG_PASSIVE_TARGET_d_ac_ap2p | ST25R3916_REG_PASSIVE_TARGET_d_212_424_1r); + + st25r3916_write_reg(handle, ST25R3916_REG_MASK_RX_TIMER, 0x02); + + st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP); + uint32_t interrupts = + (ST25R3916_IRQ_MASK_FWL | ST25R3916_IRQ_MASK_TXE | ST25R3916_IRQ_MASK_RXS | + ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_PAR | ST25R3916_IRQ_MASK_CRC | + ST25R3916_IRQ_MASK_ERR1 | ST25R3916_IRQ_MASK_ERR2 | ST25R3916_IRQ_MASK_NRE | + ST25R3916_IRQ_MASK_EON | ST25R3916_IRQ_MASK_EOF | ST25R3916_IRQ_MASK_WU_A_X | + ST25R3916_IRQ_MASK_WU_A); + // Clear interrupts + st25r3916_get_irq(handle); + // Enable interrupts + st25r3916_mask_irq(handle, ~interrupts); + // Enable auto collision resolution + st25r3916_clear_reg_bits( + handle, ST25R3916_REG_PASSIVE_TARGET, ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a); + st25r3916_direct_cmd(handle, ST25R3916_CMD_GOTO_SENSE); + + return furi_hal_nfc_iso14443a_common_init(handle); +} + +static FuriHalNfcError furi_hal_nfc_iso14443a_listener_deinit(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + + if(iso14443_3a_signal) { + iso14443_3a_signal_free(iso14443_3a_signal); + iso14443_3a_signal = NULL; + } + + return FuriHalNfcErrorNone; +} + +static FuriHalNfcEvent furi_hal_nfc_iso14443_3a_listener_wait_event(uint32_t timeout_ms) { + FuriHalNfcEvent event = furi_hal_nfc_wait_event_common(timeout_ms); + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + if(event & FuriHalNfcEventListenerActive) { + st25r3916_set_reg_bits( + handle, ST25R3916_REG_PASSIVE_TARGET, ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a); + } + + return event; +} + +FuriHalNfcError furi_hal_nfc_iso14443a_poller_trx_short_frame(FuriHalNfcaShortFrame frame) { + FuriHalNfcError error = FuriHalNfcErrorNone; + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + // Disable crc check + st25r3916_set_reg_bits(handle, ST25R3916_REG_AUX, ST25R3916_REG_AUX_no_crc_rx); + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_ISO14443A_NFC, + (ST25R3916_REG_ISO14443A_NFC_no_tx_par | ST25R3916_REG_ISO14443A_NFC_no_rx_par), + (ST25R3916_REG_ISO14443A_NFC_no_tx_par_off | ST25R3916_REG_ISO14443A_NFC_no_rx_par_off)); + + st25r3916_write_reg(handle, ST25R3916_REG_NUM_TX_BYTES2, 0); + uint32_t interrupts = + (ST25R3916_IRQ_MASK_FWL | ST25R3916_IRQ_MASK_TXE | ST25R3916_IRQ_MASK_RXS | + ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_PAR | ST25R3916_IRQ_MASK_CRC | + ST25R3916_IRQ_MASK_ERR1 | ST25R3916_IRQ_MASK_ERR2 | ST25R3916_IRQ_MASK_NRE); + // Clear interrupts + st25r3916_get_irq(handle); + // Enable interrupts + st25r3916_mask_irq(handle, ~interrupts); + if(frame == FuriHalNfcaShortFrameAllReq) { + st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSMIT_REQA); + } else { + st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSMIT_WUPA); + } + + return error; +} + +FuriHalNfcError furi_hal_nfc_iso14443a_tx_sdd_frame(const uint8_t* tx_data, size_t tx_bits) { + FuriHalNfcError error = FuriHalNfcErrorNone; + // No anticollision is supported in this version of library + error = furi_hal_nfc_poller_tx(tx_data, tx_bits); + + return error; +} + +FuriHalNfcError + furi_hal_nfc_iso14443a_rx_sdd_frame(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits) { + FuriHalNfcError error = FuriHalNfcErrorNone; + UNUSED(rx_data); + UNUSED(rx_bits); + UNUSED(rx_data_size); + + error = furi_hal_nfc_poller_rx(rx_data, rx_data_size, rx_bits); + // No anticollision is supported in this version of library + + return error; +} + +FuriHalNfcError + furi_hal_nfc_iso14443a_poller_tx_custom_parity(const uint8_t* tx_data, size_t tx_bits) { + furi_assert(tx_data); + + FuriHalNfcError err = FuriHalNfcErrorNone; + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + // Prepare tx + st25r3916_direct_cmd(handle, ST25R3916_CMD_CLEAR_FIFO); + st25r3916_clear_reg_bits( + handle, ST25R3916_REG_TIMER_EMV_CONTROL, ST25R3916_REG_TIMER_EMV_CONTROL_nrt_emv); + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_ISO14443A_NFC, + (ST25R3916_REG_ISO14443A_NFC_no_tx_par | ST25R3916_REG_ISO14443A_NFC_no_rx_par), + (ST25R3916_REG_ISO14443A_NFC_no_tx_par | ST25R3916_REG_ISO14443A_NFC_no_rx_par)); + uint32_t interrupts = + (ST25R3916_IRQ_MASK_FWL | ST25R3916_IRQ_MASK_TXE | ST25R3916_IRQ_MASK_RXS | + ST25R3916_IRQ_MASK_RXE | ST25R3916_IRQ_MASK_PAR | ST25R3916_IRQ_MASK_CRC | + ST25R3916_IRQ_MASK_ERR1 | ST25R3916_IRQ_MASK_ERR2 | ST25R3916_IRQ_MASK_NRE); + // Clear interrupts + st25r3916_get_irq(handle); + // Enable interrupts + st25r3916_mask_irq(handle, ~interrupts); + + st25r3916_write_fifo(handle, tx_data, tx_bits); + st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSMIT_WITHOUT_CRC); + return err; +} + +FuriHalNfcError furi_hal_nfc_iso14443a_listener_set_col_res_data( + uint8_t* uid, + uint8_t uid_len, + uint8_t* atqa, + uint8_t sak) { + furi_assert(uid); + furi_assert(atqa); + UNUSED(uid_len); + UNUSED(sak); + FuriHalNfcError error = FuriHalNfcErrorNone; + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + // Set 4 or 7 bytes UID + if(uid_len == 4) { + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_AUX, + ST25R3916_REG_AUX_nfc_id_mask, + ST25R3916_REG_AUX_nfc_id_4bytes); + } else { + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_AUX, + ST25R3916_REG_AUX_nfc_id_mask, + ST25R3916_REG_AUX_nfc_id_7bytes); + } + // Write PT Memory + uint8_t pt_memory[15] = {}; + memcpy(pt_memory, uid, uid_len); + pt_memory[10] = atqa[0]; + pt_memory[11] = atqa[1]; + if(uid_len == 4) { + pt_memory[12] = sak & ~0x04; + } else { + pt_memory[12] = 0x04; + } + pt_memory[13] = sak & ~0x04; + pt_memory[14] = sak & ~0x04; + + st25r3916_write_pta_mem(handle, pt_memory, sizeof(pt_memory)); + + return error; +} + +FuriHalNfcError furi_hal_nfc_iso4443a_listener_tx( + FuriHalSpiBusHandle* handle, + const uint8_t* tx_data, + size_t tx_bits) { + FuriHalNfcError error = FuriHalNfcErrorNone; + + do { + error = furi_hal_nfc_common_fifo_tx(handle, tx_data, tx_bits); + if(error != FuriHalNfcErrorNone) break; + + bool tx_end = furi_hal_nfc_event_wait_for_specific_irq(handle, ST25R3916_IRQ_MASK_TXE, 10); + if(!tx_end) { + error = FuriHalNfcErrorCommunicationTimeout; + break; + } + + } while(false); + + return error; +} + +FuriHalNfcError furi_hal_nfc_iso14443a_listener_tx_custom_parity( + const uint8_t* tx_data, + const uint8_t* tx_parity, + size_t tx_bits) { + furi_assert(tx_data); + furi_assert(tx_parity); + + furi_assert(iso14443_3a_signal); + + FuriHalSpiBusHandle* handle = &furi_hal_spi_bus_handle_nfc; + + st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSPARENT_MODE); + // Reconfigure gpio for Transparent mode + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc); + + // Send signal + iso14443_3a_signal_tx(iso14443_3a_signal, tx_data, tx_parity, tx_bits); + + // Exit transparent mode + furi_hal_gpio_write(&gpio_spi_r_mosi, false); + + // Configure gpio back to SPI and exit transparent + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); + st25r3916_direct_cmd(handle, ST25R3916_CMD_UNMASK_RECEIVE_DATA); + + return FuriHalNfcErrorNone; +} + +FuriHalNfcError furi_hal_nfc_iso14443_3a_listener_sleep(FuriHalSpiBusHandle* handle) { + // Enable auto collision resolution + st25r3916_clear_reg_bits( + handle, ST25R3916_REG_PASSIVE_TARGET, ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a); + st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP); + st25r3916_direct_cmd(handle, ST25R3916_CMD_GOTO_SLEEP); + + return FuriHalNfcErrorNone; +} + +FuriHalNfcError furi_hal_nfc_iso14443_3a_listener_idle(FuriHalSpiBusHandle* handle) { + // Enable auto collision resolution + st25r3916_clear_reg_bits( + handle, ST25R3916_REG_PASSIVE_TARGET, ST25R3916_REG_PASSIVE_TARGET_d_106_ac_a); + st25r3916_direct_cmd(handle, ST25R3916_CMD_STOP); + st25r3916_direct_cmd(handle, ST25R3916_CMD_GOTO_SENSE); + + return FuriHalNfcErrorNone; +} + +const FuriHalNfcTechBase furi_hal_nfc_iso14443a = { + .poller = + { + .compensation = + { + .fdt = FURI_HAL_NFC_POLLER_FDT_COMP_FC, + .fwt = FURI_HAL_NFC_POLLER_FWT_COMP_FC, + }, + .init = furi_hal_nfc_iso14443a_poller_init, + .deinit = furi_hal_nfc_iso14443a_poller_deinit, + .wait_event = furi_hal_nfc_wait_event_common, + .tx = furi_hal_nfc_poller_tx_common, + .rx = furi_hal_nfc_common_fifo_rx, + }, + + .listener = + { + .compensation = + { + .fdt = FURI_HAL_NFC_ISO14443A_LISTENER_FDT_COMP_FC, + }, + .init = furi_hal_nfc_iso14443a_listener_init, + .deinit = furi_hal_nfc_iso14443a_listener_deinit, + .wait_event = furi_hal_nfc_iso14443_3a_listener_wait_event, + .tx = furi_hal_nfc_iso4443a_listener_tx, + .rx = furi_hal_nfc_common_fifo_rx, + .sleep = furi_hal_nfc_iso14443_3a_listener_sleep, + .idle = furi_hal_nfc_iso14443_3a_listener_idle, + }, +}; diff --git a/targets/f7/furi_hal/furi_hal_nfc_iso14443b.c b/targets/f7/furi_hal/furi_hal_nfc_iso14443b.c new file mode 100644 index 00000000000..bb1a63515b2 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_nfc_iso14443b.c @@ -0,0 +1,108 @@ +#include "furi_hal_nfc_i.h" +#include "furi_hal_nfc_tech_i.h" + +static FuriHalNfcError furi_hal_nfc_iso14443b_common_init(FuriHalSpiBusHandle* handle) { + // Common NFC-B settings, 106kbps + + // 1st stage zero = 60kHz, 3rd stage zero = 200 kHz + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF1, ST25R3916_REG_RX_CONF1_h200); + + // Enable AGC + // AGC Ratio 6 + // AGC algorithm with RESET (recommended for ISO14443-B) + // AGC operation during complete receive period + // Squelch ratio 6/3 (recommended for ISO14443-B) + // Squelch automatic activation on TX end + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF2, + ST25R3916_REG_RX_CONF2_agc6_3 | ST25R3916_REG_RX_CONF2_agc_alg | + ST25R3916_REG_RX_CONF2_agc_m | ST25R3916_REG_RX_CONF2_agc_en | + ST25R3916_REG_RX_CONF2_pulz_61 | ST25R3916_REG_RX_CONF2_sqm_dyn); + + // HF operation, full gain on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF3, 0x00); + // No gain reduction on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF4, 0x00); + + // Subcarrier end detector enabled + // Subcarrier end detection level = 66% + // BPSK start 33 pilot pulses + // AM & PM summation before digitizing on + st25r3916_write_reg( + handle, + ST25R3916_REG_CORR_CONF1, + ST25R3916_REG_CORR_CONF1_corr_s0 | ST25R3916_REG_CORR_CONF1_corr_s1 | + ST25R3916_REG_CORR_CONF1_corr_s3 | ST25R3916_REG_CORR_CONF1_corr_s4); + // Sleep mode disable, 424kHz mode off + st25r3916_write_reg(handle, ST25R3916_REG_CORR_CONF2, 0x00); + + return FuriHalNfcErrorNone; +} + +static FuriHalNfcError furi_hal_nfc_iso14443b_poller_init(FuriHalSpiBusHandle* handle) { + // Enable ISO14443B mode, AM modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_MODE, + ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am, + ST25R3916_REG_MODE_om_iso14443b | ST25R3916_REG_MODE_tr_am_am); + + // 10% ASK modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_TX_DRIVER, + ST25R3916_REG_TX_DRIVER_am_mod_mask, + ST25R3916_REG_TX_DRIVER_am_mod_10percent); + + // Use regulator AM, resistive AM disabled + st25r3916_clear_reg_bits( + handle, + ST25R3916_REG_AUX_MOD, + ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am); + + // EGT = 0 etu + // SOF = 10 etu LOW + 2 etu HIGH + // EOF = 10 etu + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_ISO14443B_1, + ST25R3916_REG_ISO14443B_1_egt_mask | ST25R3916_REG_ISO14443B_1_sof_mask | + ST25R3916_REG_ISO14443B_1_eof, + (0U << ST25R3916_REG_ISO14443B_1_egt_shift) | ST25R3916_REG_ISO14443B_1_sof_0_10etu | + ST25R3916_REG_ISO14443B_1_sof_1_2etu | ST25R3916_REG_ISO14443B_1_eof_10etu); + + // TR1 = 80 / fs + // B' mode off (no_sof & no_eof = 0) + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_ISO14443B_2, + ST25R3916_REG_ISO14443B_2_tr1_mask | ST25R3916_REG_ISO14443B_2_no_sof | + ST25R3916_REG_ISO14443B_2_no_eof, + ST25R3916_REG_ISO14443B_2_tr1_80fs80fs); + + return furi_hal_nfc_iso14443b_common_init(handle); +} + +static FuriHalNfcError furi_hal_nfc_iso14443b_poller_deinit(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + return FuriHalNfcErrorNone; +} + +const FuriHalNfcTechBase furi_hal_nfc_iso14443b = { + .poller = + { + .compensation = + { + .fdt = FURI_HAL_NFC_POLLER_FDT_COMP_FC, + .fwt = FURI_HAL_NFC_POLLER_FWT_COMP_FC, + }, + .init = furi_hal_nfc_iso14443b_poller_init, + .deinit = furi_hal_nfc_iso14443b_poller_deinit, + .wait_event = furi_hal_nfc_wait_event_common, + .tx = furi_hal_nfc_poller_tx_common, + .rx = furi_hal_nfc_common_fifo_rx, + }, + + .listener = {}, +}; diff --git a/targets/f7/furi_hal/furi_hal_nfc_iso15693.c b/targets/f7/furi_hal/furi_hal_nfc_iso15693.c new file mode 100644 index 00000000000..cc936644b4e --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_nfc_iso15693.c @@ -0,0 +1,463 @@ +#include "furi_hal_nfc_i.h" +#include "furi_hal_nfc_tech_i.h" + +#include +#include + +#include + +#define FURI_HAL_NFC_ISO15693_MAX_FRAME_SIZE (1024U) +#define FURI_HAL_NFC_ISO15693_POLLER_MAX_BUFFER_SIZE (64) + +#define FURI_HAL_NFC_ISO15693_RESP_SOF_SIZE (5) +#define FURI_HAL_NFC_ISO15693_RESP_EOF_SIZE (5) +#define FURI_HAL_NFC_ISO15693_RESP_SOF_MASK (0x1FU) +#define FURI_HAL_NFC_ISO15693_RESP_SOF_PATTERN (0x17U) +#define FURI_HAL_NFC_ISO15693_RESP_EOF_PATTERN (0x1DU) + +#define FURI_HAL_NFC_ISO15693_RESP_PATTERN_MASK (0x03U) +#define FURI_HAL_NFC_ISO15693_RESP_PATTERN_0 (0x01U) +#define FURI_HAL_NFC_ISO15693_RESP_PATTERN_1 (0x02U) + +// Derived experimentally +#define FURI_HAL_NFC_ISO15693_POLLER_FWT_COMP_FC (-1300) +#define FURI_HAL_NFC_ISO15693_LISTENER_FDT_COMP_FC (2850) + +#define BITS_IN_BYTE (8U) + +#define TAG "FuriHalIso15693" + +typedef struct { + Iso15693Signal* signal; + Iso15693Parser* parser; +} FuriHalNfcIso15693Listener; + +typedef struct { + // 4 bits per data bit on transmit + uint8_t fifo_buf[FURI_HAL_NFC_ISO15693_POLLER_MAX_BUFFER_SIZE * 4]; + size_t fifo_buf_bits; + uint8_t frame_buf[FURI_HAL_NFC_ISO15693_POLLER_MAX_BUFFER_SIZE * 2]; + size_t frame_buf_bits; +} FuriHalNfcIso15693Poller; + +static FuriHalNfcIso15693Listener* furi_hal_nfc_iso15693_listener = NULL; +static FuriHalNfcIso15693Poller* furi_hal_nfc_iso15693_poller = NULL; + +static FuriHalNfcIso15693Listener* furi_hal_nfc_iso15693_listener_alloc() { + FuriHalNfcIso15693Listener* instance = malloc(sizeof(FuriHalNfcIso15693Listener)); + + instance->signal = iso15693_signal_alloc(&gpio_spi_r_mosi); + instance->parser = + iso15693_parser_alloc(&gpio_nfc_irq_rfid_pull, FURI_HAL_NFC_ISO15693_MAX_FRAME_SIZE); + + return instance; +} + +static void furi_hal_nfc_iso15693_listener_free(FuriHalNfcIso15693Listener* instance) { + furi_assert(instance); + + iso15693_signal_free(instance->signal); + iso15693_parser_free(instance->parser); + + free(instance); +} + +static FuriHalNfcIso15693Poller* furi_hal_nfc_iso15693_poller_alloc() { + FuriHalNfcIso15693Poller* instance = malloc(sizeof(FuriHalNfcIso15693Poller)); + + return instance; +} + +static void furi_hal_nfc_iso15693_poller_free(FuriHalNfcIso15693Poller* instance) { + furi_assert(instance); + + free(instance); +} + +static FuriHalNfcError furi_hal_nfc_iso15693_common_init(FuriHalSpiBusHandle* handle) { + // Common NFC-V settings, 26.48 kbps + + // 1st stage zero = 12 kHz, 3rd stage zero = 80 kHz, low-pass = 600 kHz + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF1, + ST25R3916_REG_RX_CONF1_z12k | ST25R3916_REG_RX_CONF1_h80 | + ST25R3916_REG_RX_CONF1_lp_600khz); + + // Enable AGC + // AGC Ratio 6 + // AGC algorithm with RESET (recommended for ISO15693) + // AGC operation during complete receive period + // Squelch automatic activation on TX end + st25r3916_write_reg( + handle, + ST25R3916_REG_RX_CONF2, + ST25R3916_REG_RX_CONF2_agc6_3 | ST25R3916_REG_RX_CONF2_agc_m | + ST25R3916_REG_RX_CONF2_agc_en | ST25R3916_REG_RX_CONF2_sqm_dyn); + + // HF operation, full gain on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF3, 0x00); + // No gain reduction on AM and PM channels + st25r3916_write_reg(handle, ST25R3916_REG_RX_CONF4, 0x00); + + // Collision detection level 53% + // AM & PM summation before digitizing on + st25r3916_write_reg( + handle, + ST25R3916_REG_CORR_CONF1, + ST25R3916_REG_CORR_CONF1_corr_s0 | ST25R3916_REG_CORR_CONF1_corr_s1 | + ST25R3916_REG_CORR_CONF1_corr_s4); + // 424 kHz subcarrier stream mode on + st25r3916_write_reg(handle, ST25R3916_REG_CORR_CONF2, ST25R3916_REG_CORR_CONF2_corr_s8); + return FuriHalNfcErrorNone; +} + +static FuriHalNfcError furi_hal_nfc_iso15693_poller_init(FuriHalSpiBusHandle* handle) { + furi_assert(furi_hal_nfc_iso15693_poller == NULL); + + furi_hal_nfc_iso15693_poller = furi_hal_nfc_iso15693_poller_alloc(); + + // Enable Subcarrier Stream mode, OOK modulation + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_MODE, + ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am, + ST25R3916_REG_MODE_om_subcarrier_stream | ST25R3916_REG_MODE_tr_am_ook); + + // Subcarrier 424 kHz mode + // 8 sub-carrier pulses in report period + st25r3916_write_reg( + handle, + ST25R3916_REG_STREAM_MODE, + ST25R3916_REG_STREAM_MODE_scf_sc424 | ST25R3916_REG_STREAM_MODE_stx_106 | + ST25R3916_REG_STREAM_MODE_scp_8pulses); + + // Use regulator AM, resistive AM disabled + st25r3916_clear_reg_bits( + handle, + ST25R3916_REG_AUX_MOD, + ST25R3916_REG_AUX_MOD_dis_reg_am | ST25R3916_REG_AUX_MOD_res_am); + + return furi_hal_nfc_iso15693_common_init(handle); +} + +static FuriHalNfcError furi_hal_nfc_iso15693_poller_deinit(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + furi_assert(furi_hal_nfc_iso15693_poller); + + furi_hal_nfc_iso15693_poller_free(furi_hal_nfc_iso15693_poller); + furi_hal_nfc_iso15693_poller = NULL; + + return FuriHalNfcErrorNone; +} + +static void iso15693_3_poller_encode_frame( + const uint8_t* tx_data, + size_t tx_bits, + uint8_t* frame_buf, + size_t frame_buf_size, + size_t* frame_buf_bits) { + static const uint8_t bit_patterns_1_out_of_4[] = {0x02, 0x08, 0x20, 0x80}; + size_t frame_buf_size_calc = (tx_bits / 2) + 2; + furi_assert(frame_buf_size >= frame_buf_size_calc); + + // Add SOF 1 out of 4 + frame_buf[0] = 0x21; + + size_t byte_pos = 1; + for(size_t i = 0; i < tx_bits / BITS_IN_BYTE; ++i) { + for(size_t j = 0; j < BITS_IN_BYTE; j += (BITS_IN_BYTE) / 4) { + const uint8_t bit_pair = (tx_data[i] >> j) & 0x03; + frame_buf[byte_pos++] = bit_patterns_1_out_of_4[bit_pair]; + } + } + // Add EOF + frame_buf[byte_pos++] = 0x04; + *frame_buf_bits = byte_pos * BITS_IN_BYTE; +} + +static FuriHalNfcError iso15693_3_poller_decode_frame( + const uint8_t* buf, + size_t buf_bits, + uint8_t* buf_decoded, + size_t buf_decoded_size, + size_t* buf_decoded_bits) { + FuriHalNfcError ret = FuriHalNfcErrorDataFormat; + size_t bit_pos = 0; + memset(buf_decoded, 0, buf_decoded_size); + + do { + if(buf_bits == 0) break; + // Check SOF + if((buf[0] & FURI_HAL_NFC_ISO15693_RESP_SOF_MASK) != + FURI_HAL_NFC_ISO15693_RESP_SOF_PATTERN) + break; + + if(buf_bits == BITS_IN_BYTE) { + ret = FuriHalNfcErrorIncompleteFrame; + break; + } + + // 2 response bits = 1 data bit + for(uint32_t i = FURI_HAL_NFC_ISO15693_RESP_SOF_SIZE; + i < buf_bits - FURI_HAL_NFC_ISO15693_RESP_SOF_SIZE; + i += BITS_IN_BYTE / 4) { + const size_t byte_index = i / BITS_IN_BYTE; + const size_t bit_offset = i % BITS_IN_BYTE; + const uint8_t resp_byte = (buf[byte_index] >> bit_offset) | + (buf[byte_index + 1] << (BITS_IN_BYTE - bit_offset)); + + // Check EOF + if(resp_byte == FURI_HAL_NFC_ISO15693_RESP_EOF_PATTERN) { + ret = FuriHalNfcErrorNone; + break; + } + + const uint8_t bit_pattern = resp_byte & FURI_HAL_NFC_ISO15693_RESP_PATTERN_MASK; + + if(bit_pattern == FURI_HAL_NFC_ISO15693_RESP_PATTERN_0) { + bit_pos++; + } else if(bit_pattern == FURI_HAL_NFC_ISO15693_RESP_PATTERN_1) { + buf_decoded[bit_pos / BITS_IN_BYTE] |= 1 << (bit_pos % BITS_IN_BYTE); + bit_pos++; + } else { + break; + } + if(bit_pos / BITS_IN_BYTE > buf_decoded_size) { + break; + } + } + + } while(false); + + if(ret == FuriHalNfcErrorNone) { + *buf_decoded_bits = bit_pos; + } + + return ret; +} + +static FuriHalNfcError furi_hal_nfc_iso15693_poller_tx( + FuriHalSpiBusHandle* handle, + const uint8_t* tx_data, + size_t tx_bits) { + FuriHalNfcIso15693Poller* instance = furi_hal_nfc_iso15693_poller; + iso15693_3_poller_encode_frame( + tx_data, + tx_bits, + instance->frame_buf, + sizeof(instance->frame_buf), + &instance->frame_buf_bits); + return furi_hal_nfc_poller_tx_common(handle, instance->frame_buf, instance->frame_buf_bits); +} + +static FuriHalNfcError furi_hal_nfc_iso15693_poller_rx( + FuriHalSpiBusHandle* handle, + uint8_t* rx_data, + size_t rx_data_size, + size_t* rx_bits) { + FuriHalNfcError error = FuriHalNfcErrorNone; + FuriHalNfcIso15693Poller* instance = furi_hal_nfc_iso15693_poller; + + do { + error = furi_hal_nfc_common_fifo_rx( + handle, instance->fifo_buf, sizeof(instance->fifo_buf), &instance->fifo_buf_bits); + if(error != FuriHalNfcErrorNone) break; + + error = iso15693_3_poller_decode_frame( + instance->fifo_buf, + instance->fifo_buf_bits, + instance->frame_buf, + sizeof(instance->frame_buf), + &instance->frame_buf_bits); + if(error != FuriHalNfcErrorNone) break; + + if(rx_data_size < instance->frame_buf_bits / BITS_IN_BYTE) { + error = FuriHalNfcErrorBufferOverflow; + break; + } + + memcpy(rx_data, instance->frame_buf, instance->frame_buf_bits / BITS_IN_BYTE); + *rx_bits = instance->frame_buf_bits; + } while(false); + + return error; +} + +static void furi_hal_nfc_iso15693_listener_transparent_mode_enter(FuriHalSpiBusHandle* handle) { + st25r3916_direct_cmd(handle, ST25R3916_CMD_TRANSPARENT_MODE); + + furi_hal_spi_bus_handle_deinit(handle); + furi_hal_nfc_deinit_gpio_isr(); +} + +static void furi_hal_nfc_iso15693_listener_transparent_mode_exit(FuriHalSpiBusHandle* handle) { + // Configure gpio back to SPI and exit transparent mode + furi_hal_nfc_init_gpio_isr(); + furi_hal_spi_bus_handle_init(handle); + + st25r3916_direct_cmd(handle, ST25R3916_CMD_UNMASK_RECEIVE_DATA); +} + +static FuriHalNfcError furi_hal_nfc_iso15693_listener_init(FuriHalSpiBusHandle* handle) { + furi_assert(furi_hal_nfc_iso15693_listener == NULL); + + furi_hal_nfc_iso15693_listener = furi_hal_nfc_iso15693_listener_alloc(); + + // Set default operation mode + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_MODE, + ST25R3916_REG_MODE_om_mask | ST25R3916_REG_MODE_tr_am, + ST25R3916_REG_MODE_om_targ_nfca | ST25R3916_REG_MODE_tr_am_ook); + + st25r3916_change_reg_bits( + handle, + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_rx_en, + ST25R3916_REG_OP_CONTROL_rx_en); + + // Enable passive target mode + st25r3916_change_reg_bits( + handle, ST25R3916_REG_MODE, ST25R3916_REG_MODE_targ, ST25R3916_REG_MODE_targ_targ); + + FuriHalNfcError error = furi_hal_nfc_iso15693_common_init(handle); + + furi_hal_nfc_iso15693_listener_transparent_mode_enter(handle); + + return error; +} + +static FuriHalNfcError furi_hal_nfc_iso15693_listener_deinit(FuriHalSpiBusHandle* handle) { + furi_assert(furi_hal_nfc_iso15693_listener); + + furi_hal_nfc_iso15693_listener_transparent_mode_exit(handle); + + furi_hal_nfc_iso15693_listener_free(furi_hal_nfc_iso15693_listener); + furi_hal_nfc_iso15693_listener = NULL; + + return FuriHalNfcErrorNone; +} + +static FuriHalNfcError + furi_hal_nfc_iso15693_listener_tx_transparent(const uint8_t* data, size_t data_size) { + iso15693_signal_tx( + furi_hal_nfc_iso15693_listener->signal, Iso15693SignalDataRateHi, data, data_size); + + return FuriHalNfcErrorNone; +} + +static void furi_hal_nfc_iso15693_parser_callback(Iso15693ParserEvent event, void* context) { + furi_assert(context); + + if(event == Iso15693ParserEventDataReceived) { + FuriThreadId thread_id = context; + furi_thread_flags_set(thread_id, FuriHalNfcEventInternalTypeTransparentDataReceived); + } +} + +static FuriHalNfcEvent furi_hal_nfc_iso15693_wait_event(uint32_t timeout_ms) { + FuriHalNfcEvent event = 0; + + FuriThreadId thread_id = furi_thread_get_current_id(); + iso15693_parser_start( + furi_hal_nfc_iso15693_listener->parser, furi_hal_nfc_iso15693_parser_callback, thread_id); + + while(true) { + uint32_t flag = furi_thread_flags_wait( + FuriHalNfcEventInternalTypeAbort | FuriHalNfcEventInternalTypeTransparentDataReceived, + FuriFlagWaitAny, + timeout_ms); + furi_thread_flags_clear(flag); + + if(flag & FuriHalNfcEventInternalTypeAbort) { + event = FuriHalNfcEventAbortRequest; + break; + } + if(flag & FuriHalNfcEventInternalTypeTransparentDataReceived) { + if(iso15693_parser_run(furi_hal_nfc_iso15693_listener->parser)) { + event = FuriHalNfcEventRxEnd; + break; + } + } + } + iso15693_parser_stop(furi_hal_nfc_iso15693_listener->parser); + + return event; +} + +static FuriHalNfcError furi_hal_nfc_iso15693_listener_tx( + FuriHalSpiBusHandle* handle, + const uint8_t* tx_data, + size_t tx_bits) { + UNUSED(handle); + furi_assert(furi_hal_nfc_iso15693_listener); + + FuriHalNfcError error = FuriHalNfcErrorNone; + + error = furi_hal_nfc_iso15693_listener_tx_transparent(tx_data, tx_bits / BITS_IN_BYTE); + + return error; +} + +FuriHalNfcError furi_hal_nfc_iso15693_listener_tx_sof() { + iso15693_signal_tx_sof(furi_hal_nfc_iso15693_listener->signal, Iso15693SignalDataRateHi); + + return FuriHalNfcErrorNone; +} + +static FuriHalNfcError furi_hal_nfc_iso15693_listener_rx( + FuriHalSpiBusHandle* handle, + uint8_t* rx_data, + size_t rx_data_size, + size_t* rx_bits) { + furi_assert(furi_hal_nfc_iso15693_listener); + UNUSED(handle); + + if(rx_data_size < + iso15693_parser_get_data_size_bytes(furi_hal_nfc_iso15693_listener->parser)) { + return FuriHalNfcErrorBufferOverflow; + } + + iso15693_parser_get_data( + furi_hal_nfc_iso15693_listener->parser, rx_data, rx_data_size, rx_bits); + + return FuriHalNfcErrorNone; +} + +FuriHalNfcError furi_hal_nfc_iso15693_listener_sleep(FuriHalSpiBusHandle* handle) { + UNUSED(handle); + + return FuriHalNfcErrorNone; +} + +const FuriHalNfcTechBase furi_hal_nfc_iso15693 = { + .poller = + { + .compensation = + { + .fdt = FURI_HAL_NFC_POLLER_FDT_COMP_FC, + .fwt = FURI_HAL_NFC_ISO15693_POLLER_FWT_COMP_FC, + }, + .init = furi_hal_nfc_iso15693_poller_init, + .deinit = furi_hal_nfc_iso15693_poller_deinit, + .wait_event = furi_hal_nfc_wait_event_common, + .tx = furi_hal_nfc_iso15693_poller_tx, + .rx = furi_hal_nfc_iso15693_poller_rx, + }, + + .listener = + { + .compensation = + { + .fdt = FURI_HAL_NFC_ISO15693_LISTENER_FDT_COMP_FC, + }, + .init = furi_hal_nfc_iso15693_listener_init, + .deinit = furi_hal_nfc_iso15693_listener_deinit, + .wait_event = furi_hal_nfc_iso15693_wait_event, + .tx = furi_hal_nfc_iso15693_listener_tx, + .rx = furi_hal_nfc_iso15693_listener_rx, + .sleep = furi_hal_nfc_iso15693_listener_sleep, + .idle = furi_hal_nfc_iso15693_listener_sleep, + }, +}; diff --git a/targets/f7/furi_hal/furi_hal_nfc_tech_i.h b/targets/f7/furi_hal/furi_hal_nfc_tech_i.h new file mode 100644 index 00000000000..e36dc852e8c --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_nfc_tech_i.h @@ -0,0 +1,167 @@ +/** + * @file furi_hal_nfc_tech_i.h + * @brief NFC HAL technology-related private definitions. + * + * This file is an implementation detail. It must not be included in + * any public API-related headers. + * + * This file is to be changed in an unlikely event of adding support + * for a new NFC technology. + */ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Configure the NFC chip for use with this technology. + * + * Used for init() and deinit() functions. + * + * @param[in,out] handle pointer to the NFC chip SPI handle. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +typedef FuriHalNfcError (*FuriHalNfcChipConfig)(FuriHalSpiBusHandle* handle); + +/** + * @brief Transmit data using technology-specific framing and timings. + * + * @param[in,out] handle pointer to the NFC chip SPI handle. + * @param[in] tx_data pointer to a byte array containing the data to be transmitted. + * @param[in] tx_bits transmit data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +typedef FuriHalNfcError ( + *FuriHalNfcTx)(FuriHalSpiBusHandle* handle, const uint8_t* tx_data, size_t tx_bits); + +/** + * @brief Receive data using technology-specific framing and timings. + * + * @param[in,out] handle pointer to the NFC chip SPI handle. + * @param[out] rx_data pointer to a byte array to be filled with received data. + * @param[in] rx_data_size maximum received data length, in bytes. + * @param[out] rx_bits pointer to a variable to contain received data length, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +typedef FuriHalNfcError (*FuriHalNfcRx)( + FuriHalSpiBusHandle* handle, + uint8_t* rx_data, + size_t rx_data_size, + size_t* rx_bits); + +/** + * @brief Wait for an event using technology-specific method. + * + * @param[in] timeout_ms maximum time to wait, in milliseconds. + * @return bitmask of occurred events. + */ +typedef FuriHalNfcEvent (*FuriHalNfcWaitEvent)(uint32_t timeout_ms); + +/** + * @brief Go to sleep in listener mode. + * + * Puts the passive target logic into Sleep (Halt) state. + * + * @param[in,out] handle pointer to the NFC chip SPI handle. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +typedef FuriHalNfcError (*FuriHalNfcSleep)(FuriHalSpiBusHandle* handle); + +/** + * @brief Go to idle in listener mode. + * + * Puts the passive target logic into Sense (Idle) state. + * + * @param[in,out] handle pointer to the NFC chip SPI handle. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +typedef FuriHalNfcError (*FuriHalNfcIdle)(FuriHalSpiBusHandle* handle); + +/** + * @brief Technology-specific compenstaion values for pollers. + * + * Timing compensations are needed due to execution delays not accounted for + * in standards, they are usually found out experimentally. + * + * The compensation value will be subtracted from the respective timer running + * time, so positive values shorten timeouts, and negative ones make them longer. + */ +typedef struct { + int32_t fdt; /**< Frame delay time compensation, in carrier cycles. */ + int32_t fwt; /**< Frame wait time compensaton, in carrier cycles. */ +} FuriHalNfcPollerCompensation; + +/** + * @brief Abstract technology-specific poller structure. + */ +typedef struct { + FuriHalNfcPollerCompensation compensation; /**< Compensation values in poller mode. */ + FuriHalNfcChipConfig init; /**< Pointer to the init() function. */ + FuriHalNfcChipConfig deinit; /**< Pointer to the deinit() function. */ + FuriHalNfcWaitEvent wait_event; /**< Pointer to the wait_event() function. */ + FuriHalNfcTx tx; /**< Pointer to the tx() function. */ + FuriHalNfcRx rx; /**< Pointer to the rx() function. */ +} FuriHalNfcTechPollerBase; + +/** + * @brief Technology-specific compenstaion values for listeners. + * + * Same considerations apply as with FuriHalNfcPollerCompensation. + */ +typedef struct { + int32_t fdt; /**< Frame delay time compensation, in carrier cycles. */ +} FuriHalNfcListenerCompensation; + +/** + * @brief Abstract technology-specific listener structure. + * + * If the listener operating mode is not supported for a particular + * technology, fill this structure with zeroes. + */ +typedef struct { + FuriHalNfcListenerCompensation compensation; /**< Compensation values in listener mode. */ + FuriHalNfcChipConfig init; /**< Pointer to the init() function. */ + FuriHalNfcChipConfig deinit; /**< Pointer to the deinit() function. */ + FuriHalNfcWaitEvent wait_event; /**< Pointer to the wait_event() function. */ + FuriHalNfcTx tx; /**< Pointer to the tx() function. */ + FuriHalNfcRx rx; /**< Pointer to the rx() function. */ + FuriHalNfcSleep sleep; /**< Pointer to the sleep() function. */ + FuriHalNfcIdle idle; /**< Pointer to the idle() function. */ +} FuriHalNfcTechListenerBase; + +/** + * @brief Abstract NFC technology definition structure. + * + * Each concrete technology implementation must fill this structure + * with its proper functions and constants. + */ +typedef struct { + FuriHalNfcTechPollerBase poller; /**< Structure containing the poller definition. */ + FuriHalNfcTechListenerBase listener; /**< Structure containing the listener definition. */ +} FuriHalNfcTechBase; + +/** @brief Technology declaration for ISO14443 (Type A). */ +extern const FuriHalNfcTechBase furi_hal_nfc_iso14443a; +/** @brief Technology declaration for ISO14443 (Type B). */ +extern const FuriHalNfcTechBase furi_hal_nfc_iso14443b; +/** @brief Technology declaration for ISO15693. */ +extern const FuriHalNfcTechBase furi_hal_nfc_iso15693; +/** @brief Technology declaration for FeliCa. */ +extern const FuriHalNfcTechBase furi_hal_nfc_felica; +/* Declare new tehcnologies here. */ + +/** + * @brief Array of pointers to every supported technology. + * + * This variable is defined in furi_hal_nfc.c. It will need to be modified + * in case when a new technology is to be added. + */ +extern const FuriHalNfcTechBase* furi_hal_nfc_tech[]; + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/furi_hal/furi_hal_nfc_timer.c b/targets/f7/furi_hal/furi_hal_nfc_timer.c new file mode 100644 index 00000000000..c9de9dfe856 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_nfc_timer.c @@ -0,0 +1,229 @@ +#include "furi_hal_nfc_i.h" +#include "furi_hal_nfc_tech_i.h" + +#include + +#include +#include +#include + +#define TAG "FuriHalNfcTimer" + +#define FURI_HAL_NFC_TIMER_US_IN_S (1000000UL) + +/** + * To enable timer debug output on GPIO, define the FURI_HAL_NFC_TIMER_DEBUG macro + * Example: ./fbt --extra-define=FURI_HAL_NFC_TIMER_DEBUG + */ + +typedef enum { + FuriHalNfcTimerFwt, + FuriHalNfcTimerBlockTx, + FuriHalNfcTimerCount, +} FuriHalNfcTimer; + +typedef struct { + TIM_TypeDef* timer; + FuriHalBus bus; + uint32_t prescaler; + uint32_t freq_khz; + FuriHalNfcEventInternalType event; + FuriHalInterruptId irq_id; + IRQn_Type irq_type; +#ifdef FURI_HAL_NFC_TIMER_DEBUG + const GpioPin* pin; +#endif +} FuriHalNfcTimerConfig; + +static const FuriHalNfcTimerConfig furi_hal_nfc_timers[FuriHalNfcTimerCount] = { + [FuriHalNfcTimerFwt] = + { + .timer = TIM1, + .bus = FuriHalBusTIM1, + .event = FuriHalNfcEventInternalTypeTimerFwtExpired, + .irq_id = FuriHalInterruptIdTim1UpTim16, + .irq_type = TIM1_UP_TIM16_IRQn, +#ifdef FURI_HAL_NFC_TIMER_DEBUG + .pin = &gpio_ext_pa7, +#endif + }, + [FuriHalNfcTimerBlockTx] = + { + .timer = TIM17, + .bus = FuriHalBusTIM17, + .event = FuriHalNfcEventInternalTypeTimerBlockTxExpired, + .irq_id = FuriHalInterruptIdTim1TrgComTim17, + .irq_type = TIM1_TRG_COM_TIM17_IRQn, +#ifdef FURI_HAL_NFC_TIMER_DEBUG + .pin = &gpio_ext_pa6, +#endif + }, +}; + +static void furi_hal_nfc_timer_irq_callback(void* context) { + // Returning removed const-ness + const FuriHalNfcTimerConfig* config = context; + if(LL_TIM_IsActiveFlag_UPDATE(config->timer)) { + LL_TIM_ClearFlag_UPDATE(config->timer); + furi_hal_nfc_event_set(config->event); +#ifdef FURI_HAL_NFC_TIMER_DEBUG + furi_hal_gpio_write(timer_config->pin, false); +#endif + } +} + +static void furi_hal_nfc_timer_init(FuriHalNfcTimer timer) { + const FuriHalNfcTimerConfig* config = &furi_hal_nfc_timers[timer]; + + furi_hal_bus_enable(config->bus); + + LL_TIM_SetOnePulseMode(config->timer, LL_TIM_ONEPULSEMODE_SINGLE); + LL_TIM_EnableUpdateEvent(config->timer); + LL_TIM_SetCounterMode(config->timer, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetClockSource(config->timer, LL_TIM_CLOCKSOURCE_INTERNAL); + + furi_hal_interrupt_set_isr( + config->irq_id, + furi_hal_nfc_timer_irq_callback, + // Warning: casting const-ness away + (FuriHalNfcTimerConfig*)config); + NVIC_SetPriority(config->irq_type, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(config->irq_type); +#ifdef FURI_HAL_NFC_TIMER_DEBUG + furi_hal_gpio_init(config->pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(config->pin, false); +#endif +} + +static void furi_hal_nfc_timer_deinit(FuriHalNfcTimer timer) { + const FuriHalNfcTimerConfig* config = &furi_hal_nfc_timers[timer]; + + LL_TIM_ClearFlag_UPDATE(config->timer); + furi_hal_interrupt_set_isr(config->irq_id, NULL, NULL); + NVIC_DisableIRQ(config->irq_type); + + if(furi_hal_bus_is_enabled(config->bus)) { + furi_hal_bus_disable(config->bus); + } +#ifdef FURI_HAL_NFC_TIMER_DEBUG + furi_hal_gpio_init_simple(config->pin, GpioModeAnalog); + furi_hal_gpio_write(config->pin, false); +#endif +} + +static int32_t furi_hal_nfc_timer_get_compensation(FuriHalNfcTimer timer) { + const FuriHalNfcTechBase* current_tech = furi_hal_nfc_tech[furi_hal_nfc.tech]; + + if(furi_hal_nfc.mode == FuriHalNfcModePoller) { + const FuriHalNfcPollerCompensation* comp = ¤t_tech->poller.compensation; + if(timer == FuriHalNfcTimerFwt) + return comp->fwt; + else if(timer == FuriHalNfcTimerBlockTx) + return comp->fdt; + + } else if(furi_hal_nfc.mode == FuriHalNfcModeListener) { + const FuriHalNfcListenerCompensation* comp = ¤t_tech->listener.compensation; + if(timer == FuriHalNfcTimerBlockTx) return comp->fdt; + } + + return 0; +} + +static inline bool furi_hal_nfc_timer_is_running(FuriHalNfcTimer timer) { + return LL_TIM_IsEnabledCounter(furi_hal_nfc_timers[timer].timer) != 0; +} + +static void furi_hal_nfc_timer_start_core_ticks(FuriHalNfcTimer timer, uint64_t core_ticks) { + furi_check(!furi_hal_nfc_timer_is_running(timer)); + + const FuriHalNfcTimerConfig* config = &furi_hal_nfc_timers[timer]; + furi_check(furi_hal_bus_is_enabled(config->bus)); + + const uint32_t prescaler = (core_ticks - 1) / UINT16_MAX; + furi_check(prescaler <= UINT16_MAX); + + const uint32_t arr_reg = core_ticks / (prescaler + 1); + furi_check(arr_reg <= UINT16_MAX); + + LL_TIM_DisableIT_UPDATE(config->timer); + + LL_TIM_SetPrescaler(config->timer, prescaler); + LL_TIM_SetAutoReload(config->timer, arr_reg); + + LL_TIM_GenerateEvent_UPDATE(config->timer); + while(!LL_TIM_IsActiveFlag_UPDATE(config->timer)) + ; + LL_TIM_ClearFlag_UPDATE(config->timer); + + LL_TIM_EnableIT_UPDATE(config->timer); + LL_TIM_EnableCounter(config->timer); +#ifdef FURI_HAL_NFC_TIMER_DEBUG + furi_hal_gpio_write(config->pin, true); +#endif +} + +static void furi_hal_nfc_timer_start_us(FuriHalNfcTimer timer, uint32_t time_us) { + furi_hal_nfc_timer_start_core_ticks( + timer, SystemCoreClock / FURI_HAL_NFC_TIMER_US_IN_S * time_us); +} + +static void furi_hal_nfc_timer_start_fc(FuriHalNfcTimer timer, uint32_t time_fc) { + const int32_t comp_fc = furi_hal_nfc_timer_get_compensation(timer); + // Not starting the timer if the compensation value is greater than the requested delay + if(comp_fc >= (int32_t)time_fc) return; + + furi_hal_nfc_timer_start_core_ticks( + timer, ((uint64_t)SystemCoreClock * (time_fc - comp_fc)) / FURI_HAL_NFC_CARRIER_HZ); +} + +static void furi_hal_nfc_timer_stop(FuriHalNfcTimer timer) { + const FuriHalNfcTimerConfig* config = &furi_hal_nfc_timers[timer]; + + LL_TIM_DisableIT_UPDATE(config->timer); + LL_TIM_DisableCounter(config->timer); + LL_TIM_SetCounter(config->timer, 0); + LL_TIM_SetAutoReload(config->timer, 0); + + if(LL_TIM_IsActiveFlag_UPDATE(config->timer)) { + LL_TIM_ClearFlag_UPDATE(config->timer); + } +#ifdef FURI_HAL_NFC_TIMER_DEBUG + furi_hal_gpio_write(config->pin, false); +#endif +} + +void furi_hal_nfc_timers_init() { + for(size_t i = 0; i < FuriHalNfcTimerCount; i++) { + furi_hal_nfc_timer_init(i); + } +} + +void furi_hal_nfc_timers_deinit() { + for(size_t i = 0; i < FuriHalNfcTimerCount; i++) { + furi_hal_nfc_timer_deinit(i); + } +} + +void furi_hal_nfc_timer_fwt_start(uint32_t time_fc) { + furi_hal_nfc_timer_start_fc(FuriHalNfcTimerFwt, time_fc); +} + +void furi_hal_nfc_timer_fwt_stop() { + furi_hal_nfc_timer_stop(FuriHalNfcTimerFwt); +} + +void furi_hal_nfc_timer_block_tx_start(uint32_t time_fc) { + furi_hal_nfc_timer_start_fc(FuriHalNfcTimerBlockTx, time_fc); +} + +void furi_hal_nfc_timer_block_tx_start_us(uint32_t time_us) { + furi_hal_nfc_timer_start_us(FuriHalNfcTimerBlockTx, time_us); +} + +void furi_hal_nfc_timer_block_tx_stop() { + furi_hal_nfc_timer_stop(FuriHalNfcTimerBlockTx); +} + +bool furi_hal_nfc_timer_block_tx_is_running() { + return furi_hal_nfc_timer_is_running(FuriHalNfcTimerBlockTx); +} diff --git a/targets/f7/furi_hal/furi_hal_os.c b/targets/f7/furi_hal/furi_hal_os.c new file mode 100644 index 00000000000..ea835b95fbe --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_os.c @@ -0,0 +1,215 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#define TAG "FuriHalOs" + +#define FURI_HAL_IDLE_TIMER_CLK_HZ 32768 +#define FURI_HAL_OS_TICK_HZ configTICK_RATE_HZ + +#define FURI_HAL_OS_IDLE_CNT_TO_TICKS(x) (((x)*FURI_HAL_OS_TICK_HZ) / FURI_HAL_IDLE_TIMER_CLK_HZ) +#define FURI_HAL_OS_TICKS_TO_IDLE_CNT(x) (((x)*FURI_HAL_IDLE_TIMER_CLK_HZ) / FURI_HAL_OS_TICK_HZ) + +#define FURI_HAL_IDLE_TIMER_TICK_PER_EPOCH (FURI_HAL_OS_IDLE_CNT_TO_TICKS(FURI_HAL_IDLE_TIMER_MAX)) +#define FURI_HAL_OS_MAX_SLEEP (FURI_HAL_IDLE_TIMER_TICK_PER_EPOCH - 1) + +#define FURI_HAL_OS_NVIC_IS_PENDING() (NVIC->ISPR[0] || NVIC->ISPR[1]) +#define FURI_HAL_OS_EXTI_LINE_0_31 0 +#define FURI_HAL_OS_EXTI_LINE_32_63 1 + +// Arbitrary (but small) number for better tick consistency +#define FURI_HAL_OS_EXTRA_CNT 3 + +#ifndef FURI_HAL_OS_DEBUG_AWAKE_GPIO +#define FURI_HAL_OS_DEBUG_AWAKE_GPIO (&gpio_ext_pa7) +#endif + +#ifndef FURI_HAL_OS_DEBUG_TICK_GPIO +#define FURI_HAL_OS_DEBUG_TICK_GPIO (&gpio_ext_pa6) +#endif + +#ifndef FURI_HAL_OS_DEBUG_SECOND_GPIO +#define FURI_HAL_OS_DEBUG_SECOND_GPIO (&gpio_ext_pa4) +#endif + +#ifdef FURI_HAL_OS_DEBUG +#include + +void furi_hal_os_timer_callback() { + furi_hal_gpio_write( + FURI_HAL_OS_DEBUG_SECOND_GPIO, !furi_hal_gpio_read(FURI_HAL_OS_DEBUG_SECOND_GPIO)); +} +#endif + +extern void xPortSysTickHandler(); + +static volatile uint32_t furi_hal_os_skew; + +void furi_hal_os_init() { + furi_hal_idle_timer_init(); + +#ifdef FURI_HAL_OS_DEBUG + furi_hal_gpio_init_simple(FURI_HAL_OS_DEBUG_AWAKE_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(FURI_HAL_OS_DEBUG_TICK_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(FURI_HAL_OS_DEBUG_SECOND_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_write(FURI_HAL_OS_DEBUG_AWAKE_GPIO, 1); + + FuriTimer* second_timer = + furi_timer_alloc(furi_hal_os_timer_callback, FuriTimerTypePeriodic, NULL); + furi_timer_start(second_timer, FURI_HAL_OS_TICK_HZ); +#endif + + FURI_LOG_I(TAG, "Init OK"); +} + +void furi_hal_os_tick() { + if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { +#ifdef FURI_HAL_OS_DEBUG + furi_hal_gpio_write( + FURI_HAL_OS_DEBUG_TICK_GPIO, !furi_hal_gpio_read(FURI_HAL_OS_DEBUG_TICK_GPIO)); +#endif + xPortSysTickHandler(); + } +} + +#ifdef FURI_HAL_OS_DEBUG +// Find out the IRQ number while debugging +static void furi_hal_os_nvic_dbg_trap() { + for(int32_t i = WWDG_IRQn; i <= DMAMUX1_OVR_IRQn; i++) { + if(NVIC_GetPendingIRQ(i)) { + (void)i; + // Break here + __NOP(); + } + } +} + +// Find out the EXTI line number while debugging +static void furi_hal_os_exti_dbg_trap(uint32_t exti, uint32_t val) { + for(uint32_t i = 0; val; val >>= 1U, ++i) { + if(val & 1U) { + (void)exti; + (void)i; + // Break here + __NOP(); + } + } +} +#endif + +static inline bool furi_hal_os_is_pending_irq() { + if(FURI_HAL_OS_NVIC_IS_PENDING()) { +#ifdef FURI_HAL_OS_DEBUG + furi_hal_os_nvic_dbg_trap(); +#endif + return true; + } + + uint32_t exti_lines_active; + if((exti_lines_active = LL_EXTI_ReadFlag_0_31(LL_EXTI_LINE_ALL_0_31))) { +#ifdef FURI_HAL_OS_DEBUG + furi_hal_os_exti_dbg_trap(FURI_HAL_OS_EXTI_LINE_0_31, exti_lines_active); +#endif + return true; + } else if((exti_lines_active = LL_EXTI_ReadFlag_32_63(LL_EXTI_LINE_ALL_32_63))) { +#ifdef FURI_HAL_OS_DEBUG + furi_hal_os_exti_dbg_trap(FURI_HAL_OS_EXTI_LINE_32_63, exti_lines_active); +#endif + return true; + } + + return false; +} + +static inline uint32_t furi_hal_os_sleep(TickType_t expected_idle_ticks) { + // Stop ticks + furi_hal_clock_suspend_tick(); + + // Start wakeup timer + furi_hal_idle_timer_start(FURI_HAL_OS_TICKS_TO_IDLE_CNT(expected_idle_ticks)); + +#ifdef FURI_HAL_OS_DEBUG + furi_hal_gpio_write(FURI_HAL_OS_DEBUG_AWAKE_GPIO, 0); +#endif + + // Go to sleep mode + furi_hal_power_sleep(); + +#ifdef FURI_HAL_OS_DEBUG + furi_hal_gpio_write(FURI_HAL_OS_DEBUG_AWAKE_GPIO, 1); +#endif + + // Calculate how much time we spent in the sleep + uint32_t after_cnt = furi_hal_idle_timer_get_cnt() + furi_hal_os_skew + FURI_HAL_OS_EXTRA_CNT; + uint32_t after_tick = FURI_HAL_OS_IDLE_CNT_TO_TICKS(after_cnt); + furi_hal_os_skew = after_cnt - FURI_HAL_OS_TICKS_TO_IDLE_CNT(after_tick); + + bool cmpm = LL_LPTIM_IsActiveFlag_CMPM(FURI_HAL_IDLE_TIMER); + bool arrm = LL_LPTIM_IsActiveFlag_ARRM(FURI_HAL_IDLE_TIMER); + if(cmpm && arrm) after_tick += expected_idle_ticks; + + // Prepare tick timer for new round + furi_hal_idle_timer_reset(); + + // Resume ticks + furi_hal_clock_resume_tick(); + return after_tick; +} + +void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) { + if(!furi_hal_power_sleep_available()) { + __WFI(); + return; + } + + // Core2 shenanigans takes extra time, so we want to compensate tick skew by reducing sleep duration by 1 tick + TickType_t unexpected_idle_ticks = expected_idle_ticks - 1; + + // Limit amount of ticks to maximum that timer can count + if(unexpected_idle_ticks > FURI_HAL_OS_MAX_SLEEP) { + unexpected_idle_ticks = FURI_HAL_OS_MAX_SLEEP; + } + + // Stop IRQ handling, no one should disturb us till we finish + __disable_irq(); + do { + // Confirm OS that sleep is still possible + if(eTaskConfirmSleepModeStatus() == eAbortSleep || furi_hal_os_is_pending_irq()) { + break; + } + + // Sleep and track how much ticks we spent sleeping + uint32_t completed_ticks = furi_hal_os_sleep(unexpected_idle_ticks); + // Notify system about time spent in sleep + if(completed_ticks > 0) { + if(completed_ticks > expected_idle_ticks) { +#ifdef FURI_HAL_OS_DEBUG + furi_hal_console_printf(">%lu\r\n", completed_ticks - expected_idle_ticks); +#endif + completed_ticks = expected_idle_ticks; + } + vTaskStepTick(completed_ticks); + } + } while(0); + // Reenable IRQ + __enable_irq(); +} + +void vApplicationStackOverflowHook(TaskHandle_t xTask, char* pcTaskName) { + UNUSED(xTask); + furi_hal_console_puts("\r\n\r\n stack overflow in "); + furi_hal_console_puts(pcTaskName); + furi_hal_console_puts("\r\n\r\n"); + furi_crash("StackOverflow"); +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_os.h b/targets/f7/furi_hal/furi_hal_os.h similarity index 100% rename from firmware/targets/f7/furi_hal/furi_hal_os.h rename to targets/f7/furi_hal/furi_hal_os.h diff --git a/targets/f7/furi_hal/furi_hal_power.c b/targets/f7/furi_hal/furi_hal_power.c new file mode 100644 index 00000000000..9e3a70da731 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_power.c @@ -0,0 +1,745 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define TAG "FuriHalPower" + +#ifndef FURI_HAL_POWER_DEBUG_WFI_GPIO +#define FURI_HAL_POWER_DEBUG_WFI_GPIO (&gpio_ext_pb2) +#endif + +#ifndef FURI_HAL_POWER_DEBUG_STOP_GPIO +#define FURI_HAL_POWER_DEBUG_STOP_GPIO (&gpio_ext_pc3) +#endif + +#ifndef FURI_HAL_POWER_STOP_MODE +#define FURI_HAL_POWER_STOP_MODE (LL_PWR_MODE_STOP2) +#endif + +typedef struct { + volatile uint8_t insomnia; + volatile uint8_t suppress_charge; + + bool gauge_ok; + bool charger_ok; +} FuriHalPower; + +static volatile FuriHalPower furi_hal_power = { + .insomnia = 0, + .suppress_charge = 0, + .gauge_ok = false, + .charger_ok = false, +}; + +extern const BQ27220DMData furi_hal_power_gauge_data_memory[]; + +void furi_hal_power_init() { +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_WFI_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_init_simple(FURI_HAL_POWER_DEBUG_STOP_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 0); + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 0); +#endif + + LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1); + LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN); + + LL_PWR_SetPowerMode(FURI_HAL_POWER_STOP_MODE); + LL_C2_PWR_SetPowerMode(FURI_HAL_POWER_STOP_MODE); + +#if FURI_HAL_POWER_STOP_MODE == LL_PWR_MODE_STOP0 + LL_RCC_HSI_EnableInStopMode(); // Ensure that MR is capable of work in STOP0 +#endif + + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + // Find and init gauge + size_t retry = 2; + while(retry > 0) { + furi_hal_power.gauge_ok = bq27220_init(&furi_hal_i2c_handle_power); + if(furi_hal_power.gauge_ok) { + furi_hal_power.gauge_ok = bq27220_apply_data_memory( + &furi_hal_i2c_handle_power, furi_hal_power_gauge_data_memory); + } + if(furi_hal_power.gauge_ok) { + break; + } else { + // Normal startup time is 250ms + // But if we try to access gauge at that stage it will become unresponsive + // 2 seconds timeout needed to restart communication + furi_delay_us(2020202); + } + retry--; + } + // Find and init charger + retry = 2; + while(retry > 0) { + furi_hal_power.charger_ok = bq25896_init(&furi_hal_i2c_handle_power); + if(furi_hal_power.charger_ok) { + break; + } else { + // Most likely I2C communication error + // 2 seconds should be enough for all chips on the line to timeout + // Also timing out here is very abnormal + furi_delay_us(2020202); + } + retry--; + } + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + + FURI_LOG_I(TAG, "Init OK"); +} + +bool furi_hal_power_gauge_is_ok() { + bool ret = true; + + BatteryStatus battery_status; + OperationStatus operation_status; + + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + + if(!bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) || + !bq27220_get_operation_status(&furi_hal_i2c_handle_power, &operation_status)) { + ret = false; + } else { + ret &= battery_status.BATTPRES; + ret &= operation_status.INITCOMP; + ret &= furi_hal_power.gauge_ok; + } + + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + + return ret; +} + +bool furi_hal_power_is_shutdown_requested() { + bool ret = false; + + BatteryStatus battery_status; + + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + + if(bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) != BQ27220_ERROR) { + ret = battery_status.SYSDWN; + } + + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + + return ret; +} + +uint16_t furi_hal_power_insomnia_level() { + return furi_hal_power.insomnia; +} + +void furi_hal_power_insomnia_enter() { + FURI_CRITICAL_ENTER(); + furi_assert(furi_hal_power.insomnia < UINT8_MAX); + furi_hal_power.insomnia++; + FURI_CRITICAL_EXIT(); +} + +void furi_hal_power_insomnia_exit() { + FURI_CRITICAL_ENTER(); + furi_assert(furi_hal_power.insomnia > 0); + furi_hal_power.insomnia--; + FURI_CRITICAL_EXIT(); +} + +bool furi_hal_power_sleep_available() { + return furi_hal_power.insomnia == 0; +} + +static inline bool furi_hal_power_deep_sleep_available() { + return furi_hal_bt_is_alive() && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagLegacySleep) && + !furi_hal_debug_is_gdb_session_active(); +} + +static inline void furi_hal_power_light_sleep() { + __WFI(); +} + +static inline void furi_hal_power_suspend_aux_periphs() { + // Disable USART + furi_hal_uart_suspend(FuriHalUartIdUSART1); + furi_hal_uart_suspend(FuriHalUartIdLPUART1); +} + +static inline void furi_hal_power_resume_aux_periphs() { + // Re-enable USART + furi_hal_uart_resume(FuriHalUartIdUSART1); + furi_hal_uart_resume(FuriHalUartIdLPUART1); +} + +static inline void furi_hal_power_deep_sleep() { + furi_hal_power_suspend_aux_periphs(); + + if(!furi_hal_clock_switch_pll2hse()) { + // Hello core2 my old friend + return; + } + + while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) + ; + + if(!LL_HSEM_1StepLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID)) { + if(LL_PWR_IsActiveFlag_C2DS() || LL_PWR_IsActiveFlag_C2SB()) { + // Release ENTRY_STOP_MODE semaphore + LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0); + + // The switch on HSI before entering Stop Mode is required + furi_hal_clock_switch_hse2hsi(); + } + } else { + /** + * The switch on HSI before entering Stop Mode is required + */ + furi_hal_clock_switch_hse2hsi(); + } + + /* Release RCC semaphore */ + LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); + + // Prepare deep sleep + LL_LPM_EnableDeepSleep(); + +#if defined(__CC_ARM) + // Force store operations + __force_stores(); +#endif + + __WFI(); + + LL_LPM_EnableSleep(); + + /* Release ENTRY_STOP_MODE semaphore */ + LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0); + + while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) + ; + + if(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_HSI) { + furi_hal_clock_switch_hsi2hse(); + } else { + // Ensure that we are already on HSE + furi_check(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_HSE); + } + + LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); + + furi_check(furi_hal_clock_switch_hse2pll()); + + furi_hal_power_resume_aux_periphs(); + furi_hal_rtc_sync_shadow(); +} + +void furi_hal_power_sleep() { + if(furi_hal_power_deep_sleep_available()) { +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 1); +#endif + furi_hal_power_deep_sleep(); +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_STOP_GPIO, 0); +#endif + } else { +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 1); +#endif + furi_hal_power_light_sleep(); +#ifdef FURI_HAL_POWER_DEBUG + furi_hal_gpio_write(FURI_HAL_POWER_DEBUG_WFI_GPIO, 0); +#endif + } +} + +uint8_t furi_hal_power_get_pct() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + uint8_t ret = bq27220_get_state_of_charge(&furi_hal_i2c_handle_power); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + return ret; +} + +uint8_t furi_hal_power_get_bat_health_pct() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + uint8_t ret = bq27220_get_state_of_health(&furi_hal_i2c_handle_power); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + return ret; +} + +bool furi_hal_power_is_charging() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + bool ret = bq25896_is_charging(&furi_hal_i2c_handle_power); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + return ret; +} + +bool furi_hal_power_is_charging_done() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + bool ret = bq25896_is_charging_done(&furi_hal_i2c_handle_power); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + return ret; +} + +void furi_hal_power_shutdown() { + furi_hal_power_insomnia_enter(); + + furi_hal_bt_reinit(); + + while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) + ; + + if(!LL_HSEM_1StepLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID)) { + if(LL_PWR_IsActiveFlag_C2DS() || LL_PWR_IsActiveFlag_C2SB()) { + // Release ENTRY_STOP_MODE semaphore + LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0); + } + } + + // Prepare Wakeup pin + LL_PWR_SetWakeUpPinPolarityLow(LL_PWR_WAKEUP_PIN2); + LL_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN2); + LL_C2_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN2); + + /* Release RCC semaphore */ + LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0); + + LL_PWR_DisableBootC2(); + LL_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); + LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN); + LL_LPM_EnableDeepSleep(); + + __WFI(); + furi_crash("Insomniac core2"); +} + +void furi_hal_power_off() { + // Crutch: shutting down with ext 3V3 off is causing LSE to stop + furi_hal_power_enable_external_3_3v(); + furi_hal_vibro_on(true); + furi_delay_us(50000); + // Send poweroff to charger + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + bq25896_poweroff(&furi_hal_i2c_handle_power); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + furi_hal_vibro_on(false); +} + +void furi_hal_power_reset() { + NVIC_SystemReset(); +} + +bool furi_hal_power_enable_otg() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + bq25896_set_boost_lim(&furi_hal_i2c_handle_power, BoostLim_2150); + bq25896_enable_otg(&furi_hal_i2c_handle_power); + furi_delay_ms(30); + bool ret = bq25896_is_otg_enabled(&furi_hal_i2c_handle_power); + bq25896_set_boost_lim(&furi_hal_i2c_handle_power, BoostLim_1400); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + return ret; +} + +void furi_hal_power_disable_otg() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + bq25896_disable_otg(&furi_hal_i2c_handle_power); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); +} + +bool furi_hal_power_is_otg_enabled() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + bool ret = bq25896_is_otg_enabled(&furi_hal_i2c_handle_power); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + return ret; +} + +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_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)); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); +} + +bool furi_hal_power_check_otg_fault() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + bool ret = bq25896_check_otg_fault(&furi_hal_i2c_handle_power); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + return ret; +} + +void furi_hal_power_check_otg_status() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + if(bq25896_check_otg_fault(&furi_hal_i2c_handle_power)) + bq25896_disable_otg(&furi_hal_i2c_handle_power); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); +} + +uint32_t furi_hal_power_get_battery_remaining_capacity() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + uint32_t ret = bq27220_get_remaining_capacity(&furi_hal_i2c_handle_power); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + return ret; +} + +uint32_t furi_hal_power_get_battery_full_capacity() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + uint32_t ret = bq27220_get_full_charge_capacity(&furi_hal_i2c_handle_power); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + return ret; +} + +uint32_t furi_hal_power_get_battery_design_capacity() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + uint32_t ret = bq27220_get_design_capacity(&furi_hal_i2c_handle_power); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + return ret; +} + +float furi_hal_power_get_battery_voltage(FuriHalPowerIC ic) { + float ret = 0.0f; + + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + if(ic == FuriHalPowerICCharger) { + ret = (float)bq25896_get_vbat_voltage(&furi_hal_i2c_handle_power) / 1000.0f; + } else if(ic == FuriHalPowerICFuelGauge) { + ret = (float)bq27220_get_voltage(&furi_hal_i2c_handle_power) / 1000.0f; + } + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + + return ret; +} + +float furi_hal_power_get_battery_current(FuriHalPowerIC ic) { + float ret = 0.0f; + + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + if(ic == FuriHalPowerICCharger) { + ret = (float)bq25896_get_vbat_current(&furi_hal_i2c_handle_power) / 1000.0f; + } else if(ic == FuriHalPowerICFuelGauge) { + ret = (float)bq27220_get_current(&furi_hal_i2c_handle_power) / 1000.0f; + } + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + + return ret; +} + +static float furi_hal_power_get_battery_temperature_internal(FuriHalPowerIC ic) { + float ret = 0.0f; + + if(ic == FuriHalPowerICCharger) { + // Linear approximation, +/- 5 C + ret = (71.0f - (float)bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power) / 1000) / 0.6f; + } else if(ic == FuriHalPowerICFuelGauge) { + ret = ((float)bq27220_get_temperature(&furi_hal_i2c_handle_power) - 2731.0f) / 10.0f; + } + + return ret; +} + +float furi_hal_power_get_battery_temperature(FuriHalPowerIC ic) { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + float ret = furi_hal_power_get_battery_temperature_internal(ic); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + + return ret; +} + +float furi_hal_power_get_usb_voltage() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + float ret = (float)bq25896_get_vbus_voltage(&furi_hal_i2c_handle_power) / 1000.0f; + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + return ret; +} + +void furi_hal_power_enable_external_3_3v() { + furi_hal_gpio_write(&gpio_periph_power, 1); +} + +void furi_hal_power_disable_external_3_3v() { + furi_hal_gpio_write(&gpio_periph_power, 0); +} + +void furi_hal_power_suppress_charge_enter() { + FURI_CRITICAL_ENTER(); + bool disable_charging = furi_hal_power.suppress_charge == 0; + furi_hal_power.suppress_charge++; + FURI_CRITICAL_EXIT(); + + if(disable_charging) { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + bq25896_disable_charging(&furi_hal_i2c_handle_power); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + } +} + +void furi_hal_power_suppress_charge_exit() { + FURI_CRITICAL_ENTER(); + furi_hal_power.suppress_charge--; + bool enable_charging = furi_hal_power.suppress_charge == 0; + FURI_CRITICAL_EXIT(); + + if(enable_charging) { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + bq25896_enable_charging(&furi_hal_i2c_handle_power); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + } +} + +void furi_hal_power_info_get(PropertyValueCallback out, char sep, void* context) { + furi_assert(out); + + FuriString* value = furi_string_alloc(); + FuriString* key = furi_string_alloc(); + + PropertyValueContext property_context = { + .key = key, .value = value, .out = out, .sep = sep, .last = false, .context = context}; + + if(sep == '.') { + 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", "2"); + property_value_out(&property_context, NULL, 3, "power", "info", "minor", "1"); + } + + uint8_t charge = furi_hal_power_get_pct(); + property_value_out(&property_context, "%u", 2, "charge", "level", charge); + + const char* charge_state; + if(furi_hal_power_is_charging()) { + if((charge < 100) && (!furi_hal_power_is_charging_done())) { + charge_state = "charging"; + } else { + charge_state = "charged"; + } + } else { + charge_state = "discharging"; + } + + property_value_out(&property_context, NULL, 2, "charge", "state", charge_state); + 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); + int16_t current = + (int16_t)(furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge) * 1000.f); + property_value_out(&property_context, "%d", 2, "battery", "current", current); + int16_t temperature = (int16_t)furi_hal_power_get_battery_temperature(FuriHalPowerICFuelGauge); + property_value_out(&property_context, "%d", 2, "battery", "temp", temperature); + property_value_out( + &property_context, "%u", 2, "battery", "health", furi_hal_power_get_bat_health_pct()); + property_value_out( + &property_context, + "%lu", + 2, + "capacity", + "remain", + furi_hal_power_get_battery_remaining_capacity()); + property_value_out( + &property_context, + "%lu", + 2, + "capacity", + "full", + furi_hal_power_get_battery_full_capacity()); + property_context.last = true; + property_value_out( + &property_context, + "%lu", + 2, + "capacity", + "design", + furi_hal_power_get_battery_design_capacity()); + + furi_string_free(key); + furi_string_free(value); +} + +void furi_hal_power_debug_get(PropertyValueCallback out, void* context) { + furi_assert(out); + + FuriString* value = furi_string_alloc(); + FuriString* key = furi_string_alloc(); + + PropertyValueContext property_context = { + .key = key, .value = value, .out = out, .sep = '.', .last = false, .context = context}; + + BatteryStatus battery_status; + OperationStatus operation_status; + + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + + // Power Debug version + property_value_out(&property_context, NULL, 2, "format", "major", "1"); + property_value_out(&property_context, NULL, 2, "format", "minor", "0"); + + property_value_out( + &property_context, + "%d", + 2, + "charger", + "vbus", + bq25896_get_vbus_voltage(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 2, + "charger", + "vsys", + bq25896_get_vsys_voltage(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 2, + "charger", + "vbat", + bq25896_get_vbat_voltage(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 2, + "charger", + "vreg", + bq25896_get_vreg_voltage(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 2, + "charger", + "current", + bq25896_get_vbat_current(&furi_hal_i2c_handle_power)); + + const uint32_t ntc_mpct = bq25896_get_ntc_mpct(&furi_hal_i2c_handle_power); + + if(bq27220_get_battery_status(&furi_hal_i2c_handle_power, &battery_status) && + bq27220_get_operation_status(&furi_hal_i2c_handle_power, &operation_status)) { + property_value_out(&property_context, "%lu", 2, "charger", "ntc", ntc_mpct); + property_value_out(&property_context, "%d", 2, "gauge", "calmd", operation_status.CALMD); + property_value_out(&property_context, "%d", 2, "gauge", "sec", operation_status.SEC); + property_value_out(&property_context, "%d", 2, "gauge", "edv2", operation_status.EDV2); + property_value_out(&property_context, "%d", 2, "gauge", "vdq", operation_status.VDQ); + property_value_out( + &property_context, "%d", 2, "gauge", "initcomp", operation_status.INITCOMP); + property_value_out(&property_context, "%d", 2, "gauge", "smth", operation_status.SMTH); + property_value_out(&property_context, "%d", 2, "gauge", "btpint", operation_status.BTPINT); + property_value_out( + &property_context, "%d", 2, "gauge", "cfgupdate", operation_status.CFGUPDATE); + + // Battery status register, part 1 + property_value_out(&property_context, "%d", 2, "gauge", "chginh", battery_status.CHGINH); + property_value_out(&property_context, "%d", 2, "gauge", "fc", battery_status.FC); + property_value_out(&property_context, "%d", 2, "gauge", "otd", battery_status.OTD); + property_value_out(&property_context, "%d", 2, "gauge", "otc", battery_status.OTC); + property_value_out(&property_context, "%d", 2, "gauge", "sleep", battery_status.SLEEP); + property_value_out(&property_context, "%d", 2, "gauge", "ocvfail", battery_status.OCVFAIL); + property_value_out(&property_context, "%d", 2, "gauge", "ocvcomp", battery_status.OCVCOMP); + property_value_out(&property_context, "%d", 2, "gauge", "fd", battery_status.FD); + + // Battery status register, part 2 + property_value_out(&property_context, "%d", 2, "gauge", "dsg", battery_status.DSG); + property_value_out(&property_context, "%d", 2, "gauge", "sysdwn", battery_status.SYSDWN); + property_value_out(&property_context, "%d", 2, "gauge", "tda", battery_status.TDA); + property_value_out( + &property_context, "%d", 2, "gauge", "battpres", battery_status.BATTPRES); + property_value_out(&property_context, "%d", 2, "gauge", "authgd", battery_status.AUTH_GD); + property_value_out(&property_context, "%d", 2, "gauge", "ocvgd", battery_status.OCVGD); + property_value_out(&property_context, "%d", 2, "gauge", "tca", battery_status.TCA); + property_value_out(&property_context, "%d", 2, "gauge", "rsvd", battery_status.RSVD); + + // Voltage and current info + property_value_out( + &property_context, + "%d", + 3, + "gauge", + "capacity", + "full", + bq27220_get_full_charge_capacity(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 3, + "gauge", + "capacity", + "design", + bq27220_get_design_capacity(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 3, + "gauge", + "capacity", + "remain", + bq27220_get_remaining_capacity(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 3, + "gauge", + "state", + "charge", + bq27220_get_state_of_charge(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 3, + "gauge", + "state", + "health", + bq27220_get_state_of_health(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 2, + "gauge", + "voltage", + bq27220_get_voltage(&furi_hal_i2c_handle_power)); + property_value_out( + &property_context, + "%d", + 2, + "gauge", + "current", + bq27220_get_current(&furi_hal_i2c_handle_power)); + + property_context.last = true; + const int battery_temp = + (int)furi_hal_power_get_battery_temperature_internal(FuriHalPowerICFuelGauge); + property_value_out(&property_context, "%d", 2, "gauge", "temperature", battery_temp); + } else { + property_context.last = true; + property_value_out(&property_context, "%lu", 2, "charger", "ntc", ntc_mpct); + } + + furi_string_free(key); + furi_string_free(value); + + furi_hal_i2c_release(&furi_hal_i2c_handle_power); +} diff --git a/targets/f7/furi_hal/furi_hal_power_config.c b/targets/f7/furi_hal/furi_hal_power_config.c new file mode 100644 index 00000000000..488edce91ee --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_power_config.c @@ -0,0 +1,149 @@ +#include + +const BQ27220DMGaugingConfig furi_hal_power_gauge_data_memory_gauging_config = { + .CCT = 1, + .CSYNC = 0, + .EDV_CMP = 0, + .SC = 1, + .FIXED_EDV0 = 1, + .FCC_LIM = 1, + .FC_FOR_VDQ = 1, + .IGNORE_SD = 1, + .SME0 = 0, +}; + +const BQ27220DMData furi_hal_power_gauge_data_memory[] = { + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1GaugingConfig, + .type = BQ27220DMTypePtr16, + .value.u32 = (uint32_t)&furi_hal_power_gauge_data_memory_gauging_config, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1FullChargeCapacity, + .type = BQ27220DMTypeU16, + .value.u16 = 2100, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1DesignCapacity, + .type = BQ27220DMTypeU16, + .value.u16 = 2100, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1EMF, + .type = BQ27220DMTypeU16, + .value.u16 = 3679, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1C0, + .type = BQ27220DMTypeU16, + .value.u16 = 430, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1R0, + .type = BQ27220DMTypeU16, + .value.u16 = 334, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1T0, + .type = BQ27220DMTypeU16, + .value.u16 = 4626, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1R1, + .type = BQ27220DMTypeU16, + .value.u16 = 408, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1TC, + .type = BQ27220DMTypeU8, + .value.u8 = 11, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1C1, + .type = BQ27220DMTypeU8, + .value.u8 = 0, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD0, + .type = BQ27220DMTypeU16, + .value.u16 = 4044, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD10, + .type = BQ27220DMTypeU16, + .value.u16 = 3905, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD20, + .type = BQ27220DMTypeU16, + .value.u16 = 3807, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD30, + .type = BQ27220DMTypeU16, + .value.u16 = 3718, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD40, + .type = BQ27220DMTypeU16, + .value.u16 = 3642, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD50, + .type = BQ27220DMTypeU16, + .value.u16 = 3585, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD60, + .type = BQ27220DMTypeU16, + .value.u16 = 3546, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD70, + .type = BQ27220DMTypeU16, + .value.u16 = 3514, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD80, + .type = BQ27220DMTypeU16, + .value.u16 = 3477, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD90, + .type = BQ27220DMTypeU16, + .value.u16 = 3411, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1StartDOD100, + .type = BQ27220DMTypeU16, + .value.u16 = 3299, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1EDV0, + .type = BQ27220DMTypeU16, + .value.u16 = 3300, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1EDV1, + .type = BQ27220DMTypeU16, + .value.u16 = 3321, + }, + { + .address = BQ27220DMAddressGasGaugingCEDVProfile1EDV2, + .type = BQ27220DMTypeU16, + .value.u16 = 3355, + }, + { + .address = BQ27220DMAddressCalibrationCurrentDeadband, + .type = BQ27220DMTypeU8, + .value.u8 = 1, + }, + { + .address = BQ27220DMAddressConfigurationPowerSleepCurrent, + .type = BQ27220DMTypeI16, + .value.i16 = 1, + }, + { + .type = BQ27220DMTypeEnd, + }, +}; diff --git a/targets/f7/furi_hal/furi_hal_pwm.c b/targets/f7/furi_hal/furi_hal_pwm.c new file mode 100644 index 00000000000..879460e6bd5 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_pwm.c @@ -0,0 +1,138 @@ +#include +#include +#include + +#include +#include +#include + +#include + +const uint32_t lptim_psc_table[] = { + LL_LPTIM_PRESCALER_DIV1, + LL_LPTIM_PRESCALER_DIV2, + LL_LPTIM_PRESCALER_DIV4, + LL_LPTIM_PRESCALER_DIV8, + LL_LPTIM_PRESCALER_DIV16, + LL_LPTIM_PRESCALER_DIV32, + LL_LPTIM_PRESCALER_DIV64, + LL_LPTIM_PRESCALER_DIV128, +}; + +void furi_hal_pwm_start(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty) { + if(channel == FuriHalPwmOutputIdTim1PA7) { + furi_hal_gpio_init_ex( + &gpio_ext_pa7, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn1TIM1); + + furi_hal_bus_enable(FuriHalBusTIM1); + + LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetRepetitionCounter(TIM1, 0); + LL_TIM_SetClockDivision(TIM1, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_EnableARRPreload(TIM1); + + LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH1); + LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_PWM1); + 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_EnableAllOutputs(TIM1); + + furi_hal_pwm_set_params(channel, freq, duty); + + LL_TIM_EnableCounter(TIM1); + } else if(channel == FuriHalPwmOutputIdLptim2PA4) { + furi_hal_gpio_init_ex( + &gpio_ext_pa4, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn14LPTIM2); + + furi_hal_bus_enable(FuriHalBusLPTIM2); + + LL_LPTIM_SetUpdateMode(LPTIM2, LL_LPTIM_UPDATE_MODE_ENDOFPERIOD); + LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_PCLK1); + LL_LPTIM_SetClockSource(LPTIM2, LL_LPTIM_CLK_SOURCE_INTERNAL); + LL_LPTIM_ConfigOutput( + LPTIM2, LL_LPTIM_OUTPUT_WAVEFORM_PWM, LL_LPTIM_OUTPUT_POLARITY_INVERSE); + LL_LPTIM_SetCounterMode(LPTIM2, LL_LPTIM_COUNTER_MODE_INTERNAL); + + LL_LPTIM_Enable(LPTIM2); + + furi_hal_pwm_set_params(channel, freq, duty); + + LL_LPTIM_StartCounter(LPTIM2, LL_LPTIM_OPERATING_MODE_CONTINUOUS); + } +} + +void furi_hal_pwm_stop(FuriHalPwmOutputId channel) { + if(channel == FuriHalPwmOutputIdTim1PA7) { + furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeAnalog); + furi_hal_bus_disable(FuriHalBusTIM1); + } else if(channel == FuriHalPwmOutputIdLptim2PA4) { + furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeAnalog); + furi_hal_bus_disable(FuriHalBusLPTIM2); + } +} + +bool furi_hal_pwm_is_running(FuriHalPwmOutputId channel) { + if(channel == FuriHalPwmOutputIdTim1PA7) { + return furi_hal_bus_is_enabled(FuriHalBusTIM1); + } else if(channel == FuriHalPwmOutputIdLptim2PA4) { + return furi_hal_bus_is_enabled(FuriHalBusLPTIM2); + } + return false; +} + +void furi_hal_pwm_set_params(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty) { + furi_assert(freq > 0); + uint32_t freq_div = 64000000LU / freq; + + if(channel == FuriHalPwmOutputIdTim1PA7) { + uint32_t prescaler = freq_div / 0x10000LU; + uint32_t period = freq_div / (prescaler + 1); + uint32_t compare = period * duty / 100; + + LL_TIM_SetPrescaler(TIM1, prescaler); + LL_TIM_SetAutoReload(TIM1, period - 1); + LL_TIM_OC_SetCompareCH1(TIM1, compare); + } else if(channel == FuriHalPwmOutputIdLptim2PA4) { + uint32_t prescaler = 0; + uint32_t period = 0; + + bool clock_lse = false; + + do { + period = freq_div / (1UL << prescaler); + if(period <= 0xFFFF) { + break; + } + prescaler++; + if(prescaler > 7) { + prescaler = 0; + clock_lse = true; + period = 32768LU / freq; + break; + } + } while(1); + + uint32_t compare = period * duty / 100; + + LL_LPTIM_SetPrescaler(LPTIM2, lptim_psc_table[prescaler]); + LL_LPTIM_SetAutoReload(LPTIM2, period); + LL_LPTIM_SetCompare(LPTIM2, compare); + + if(clock_lse) { + LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_LSE); + } else { + LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_PCLK1); + } + } +} diff --git a/targets/f7/furi_hal/furi_hal_pwm.h b/targets/f7/furi_hal/furi_hal_pwm.h new file mode 100644 index 00000000000..16acca05efe --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_pwm.h @@ -0,0 +1,50 @@ +/** + * @file furi_hal_pwm.h + * PWM contol HAL + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef enum { + FuriHalPwmOutputIdTim1PA7, + FuriHalPwmOutputIdLptim2PA4, +} FuriHalPwmOutputId; + +/** Enable PWM channel and set parameters + * + * @param[in] channel PWM channel (FuriHalPwmOutputId) + * @param[in] freq Frequency in Hz + * @param[in] duty Duty cycle value in % +*/ +void furi_hal_pwm_start(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty); + +/** Disable PWM channel + * + * @param[in] channel PWM channel (FuriHalPwmOutputId) +*/ +void furi_hal_pwm_stop(FuriHalPwmOutputId channel); + +/** Set PWM channel parameters + * + * @param[in] channel PWM channel (FuriHalPwmOutputId) + * @param[in] freq Frequency in Hz + * @param[in] duty Duty cycle value in % +*/ +void furi_hal_pwm_set_params(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty); + +/** Is PWM channel running? + * + * @param[in] channel PWM channel (FuriHalPwmOutputId) + * @return bool - true if running +*/ +bool furi_hal_pwm_is_running(FuriHalPwmOutputId channel); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/furi_hal/furi_hal_random.c b/targets/f7/furi_hal/furi_hal_random.c new file mode 100644 index 00000000000..225519303b1 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_random.c @@ -0,0 +1,80 @@ +#include +#include +#include + +#include +#include +#include + +#include + +#define TAG "FuriHalRandom" + +static uint32_t furi_hal_random_read_rng() { + while(LL_RNG_IsActiveFlag_CECS(RNG) && LL_RNG_IsActiveFlag_SECS(RNG) && + !LL_RNG_IsActiveFlag_DRDY(RNG)) { + /* Error handling as described in RM0434, pg. 582-583 */ + if(LL_RNG_IsActiveFlag_CECS(RNG)) { + /* Clock error occurred */ + LL_RNG_ClearFlag_CEIS(RNG); + } + + if(LL_RNG_IsActiveFlag_SECS(RNG)) { + /* Noise source error occurred */ + LL_RNG_ClearFlag_SEIS(RNG); + + for(uint32_t i = 0; i < 12; ++i) { + const volatile uint32_t discard = LL_RNG_ReadRandData32(RNG); + UNUSED(discard); + } + } + } + + return LL_RNG_ReadRandData32(RNG); +} + +void furi_hal_random_init() { + furi_hal_bus_enable(FuriHalBusRNG); + LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_CLK48); +} + +uint32_t furi_hal_random_get() { + while(LL_HSEM_1StepLock(HSEM, CFG_HW_RNG_SEMID)) + ; + LL_RNG_Enable(RNG); + + const uint32_t random_val = furi_hal_random_read_rng(); + + LL_RNG_Disable(RNG); + ; + LL_HSEM_ReleaseLock(HSEM, CFG_HW_RNG_SEMID, 0); + + return random_val; +} + +void furi_hal_random_fill_buf(uint8_t* buf, uint32_t len) { + while(LL_HSEM_1StepLock(HSEM, CFG_HW_RNG_SEMID)) + ; + LL_RNG_Enable(RNG); + + for(uint32_t i = 0; i < len; i += 4) { + const uint32_t random_val = furi_hal_random_read_rng(); + uint8_t len_cur = ((i + 4) < len) ? (4) : (len - i); + memcpy(&buf[i], &random_val, len_cur); + } + + LL_RNG_Disable(RNG); + LL_HSEM_ReleaseLock(HSEM, CFG_HW_RNG_SEMID, 0); +} + +void srand(unsigned seed) { + UNUSED(seed); +} + +int rand() { + return (furi_hal_random_get() & RAND_MAX); +} + +long random() { + return (furi_hal_random_get() & RAND_MAX); +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_region.c b/targets/f7/furi_hal/furi_hal_region.c similarity index 100% rename from firmware/targets/f7/furi_hal/furi_hal_region.c rename to targets/f7/furi_hal/furi_hal_region.c diff --git a/targets/f7/furi_hal/furi_hal_resources.c b/targets/f7/furi_hal/furi_hal_resources.c new file mode 100644 index 00000000000..fe4640d5bb1 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_resources.c @@ -0,0 +1,230 @@ +#include +#include +#include + +#include +#include + +#define TAG "FuriHalResources" + +const GpioPin gpio_swdio = {.port = GPIOA, .pin = LL_GPIO_PIN_13}; +const GpioPin gpio_swclk = {.port = GPIOA, .pin = LL_GPIO_PIN_14}; + +const GpioPin gpio_vibro = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin}; +const GpioPin gpio_ibutton = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin}; + +const GpioPin gpio_cc1101_g0 = {.port = CC1101_G0_GPIO_Port, .pin = CC1101_G0_Pin}; +const GpioPin gpio_rf_sw_0 = {.port = RF_SW_0_GPIO_Port, .pin = RF_SW_0_Pin}; + +const GpioPin gpio_subghz_cs = {.port = CC1101_CS_GPIO_Port, .pin = CC1101_CS_Pin}; +const GpioPin gpio_display_cs = {.port = DISPLAY_CS_GPIO_Port, .pin = DISPLAY_CS_Pin}; +const GpioPin gpio_display_rst_n = {.port = DISPLAY_RST_GPIO_Port, .pin = DISPLAY_RST_Pin}; +const GpioPin gpio_display_di = {.port = DISPLAY_DI_GPIO_Port, .pin = DISPLAY_DI_Pin}; +const GpioPin gpio_sdcard_cs = {.port = SD_CS_GPIO_Port, .pin = SD_CS_Pin}; +const GpioPin gpio_sdcard_cd = {.port = SD_CD_GPIO_Port, .pin = SD_CD_Pin}; +const GpioPin gpio_nfc_cs = {.port = NFC_CS_GPIO_Port, .pin = NFC_CS_Pin}; + +const GpioPin gpio_button_up = {.port = GPIOB, .pin = LL_GPIO_PIN_10}; +const GpioPin gpio_button_down = {.port = GPIOC, .pin = LL_GPIO_PIN_6}; +const GpioPin gpio_button_right = {.port = GPIOB, .pin = LL_GPIO_PIN_12}; +const GpioPin gpio_button_left = {.port = GPIOB, .pin = LL_GPIO_PIN_11}; +const GpioPin gpio_button_ok = {.port = GPIOH, .pin = LL_GPIO_PIN_3}; +const GpioPin gpio_button_back = {.port = GPIOC, .pin = LL_GPIO_PIN_13}; + +const GpioPin gpio_spi_d_miso = {.port = SPI_D_MISO_GPIO_Port, .pin = SPI_D_MISO_Pin}; +const GpioPin gpio_spi_d_mosi = {.port = SPI_D_MOSI_GPIO_Port, .pin = SPI_D_MOSI_Pin}; +const GpioPin gpio_spi_d_sck = {.port = SPI_D_SCK_GPIO_Port, .pin = SPI_D_SCK_Pin}; +const GpioPin gpio_spi_r_miso = {.port = SPI_R_MISO_GPIO_Port, .pin = SPI_R_MISO_Pin}; +const GpioPin gpio_spi_r_mosi = {.port = SPI_R_MOSI_GPIO_Port, .pin = SPI_R_MOSI_Pin}; +const GpioPin gpio_spi_r_sck = {.port = SPI_R_SCK_GPIO_Port, .pin = SPI_R_SCK_Pin}; + +const GpioPin gpio_ext_pc0 = {.port = GPIOC, .pin = LL_GPIO_PIN_0}; +const GpioPin gpio_ext_pc1 = {.port = GPIOC, .pin = LL_GPIO_PIN_1}; +const GpioPin gpio_ext_pc3 = {.port = GPIOC, .pin = LL_GPIO_PIN_3}; +const GpioPin gpio_ext_pb2 = {.port = GPIOB, .pin = LL_GPIO_PIN_2}; +const GpioPin gpio_ext_pb3 = {.port = GPIOB, .pin = LL_GPIO_PIN_3}; +const GpioPin gpio_ext_pa4 = {.port = GPIOA, .pin = LL_GPIO_PIN_4}; +const GpioPin gpio_ext_pa6 = {.port = GPIOA, .pin = LL_GPIO_PIN_6}; +const GpioPin gpio_ext_pa7 = {.port = GPIOA, .pin = LL_GPIO_PIN_7}; + +const GpioPin gpio_nfc_irq_rfid_pull = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin}; +const GpioPin gpio_rfid_carrier_out = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin}; +const GpioPin gpio_rfid_data_in = {.port = RFID_RF_IN_GPIO_Port, .pin = RFID_RF_IN_Pin}; +const GpioPin gpio_rfid_carrier = {.port = RFID_CARRIER_GPIO_Port, .pin = RFID_CARRIER_Pin}; + +const GpioPin gpio_infrared_rx = {.port = IR_RX_GPIO_Port, .pin = IR_RX_Pin}; +const GpioPin gpio_infrared_tx = {.port = IR_TX_GPIO_Port, .pin = IR_TX_Pin}; + +const GpioPin gpio_usart_tx = {.port = USART1_TX_Port, .pin = USART1_TX_Pin}; +const GpioPin gpio_usart_rx = {.port = USART1_RX_Port, .pin = USART1_RX_Pin}; + +const GpioPin gpio_i2c_power_sda = {.port = GPIOA, .pin = LL_GPIO_PIN_10}; +const GpioPin gpio_i2c_power_scl = {.port = GPIOA, .pin = LL_GPIO_PIN_9}; + +const GpioPin gpio_speaker = {.port = GPIOB, .pin = LL_GPIO_PIN_8}; + +const GpioPin gpio_periph_power = {.port = GPIOA, .pin = LL_GPIO_PIN_3}; + +const GpioPin gpio_usb_dm = {.port = GPIOA, .pin = LL_GPIO_PIN_11}; +const GpioPin gpio_usb_dp = {.port = GPIOA, .pin = LL_GPIO_PIN_12}; + +const GpioPinRecord gpio_pins[] = { + // 5V: 1 + {.pin = &gpio_ext_pa7, .name = "PA7", .number = 2, .debug = false}, + {.pin = &gpio_ext_pa6, .name = "PA6", .number = 3, .debug = false}, + {.pin = &gpio_ext_pa4, .name = "PA4", .number = 4, .debug = false}, + {.pin = &gpio_ext_pb3, .name = "PB3", .number = 5, .debug = false}, + {.pin = &gpio_ext_pb2, .name = "PB2", .number = 6, .debug = false}, + {.pin = &gpio_ext_pc3, .name = "PC3", .number = 7, .debug = false}, + // GND: 8 + // Space + // 3v3: 9 + {.pin = &gpio_swclk, .name = "PA14", .number = 10, .debug = true}, + // GND: 11 + {.pin = &gpio_swdio, .name = "PA13", .number = 12, .debug = true}, + {.pin = &gpio_usart_tx, .name = "PB6", .number = 13, .debug = true}, + {.pin = &gpio_usart_rx, .name = "PB7", .number = 14, .debug = true}, + {.pin = &gpio_ext_pc1, .name = "PC1", .number = 15, .debug = false}, + {.pin = &gpio_ext_pc0, .name = "PC0", .number = 16, .debug = false}, + {.pin = &gpio_ibutton, .name = "PB14", .number = 17, .debug = true}, + // GND: 18 + + /* Dangerous pins, may damage hardware */ + {.pin = &gpio_speaker, .name = "PB8", .debug = true}, + {.pin = &gpio_infrared_tx, .name = "PB9", .debug = true}, +}; + +const size_t gpio_pins_count = COUNT_OF(gpio_pins); + +const InputPin input_pins[] = { + {.gpio = &gpio_button_up, .key = InputKeyUp, .inverted = true, .name = "Up"}, + {.gpio = &gpio_button_down, .key = InputKeyDown, .inverted = true, .name = "Down"}, + {.gpio = &gpio_button_right, .key = InputKeyRight, .inverted = true, .name = "Right"}, + {.gpio = &gpio_button_left, .key = InputKeyLeft, .inverted = true, .name = "Left"}, + {.gpio = &gpio_button_ok, .key = InputKeyOk, .inverted = false, .name = "OK"}, + {.gpio = &gpio_button_back, .key = InputKeyBack, .inverted = true, .name = "Back"}, +}; + +const size_t input_pins_count = COUNT_OF(input_pins); + +static void furi_hal_resources_init_input_pins(GpioMode mode) { + for(size_t i = 0; i < input_pins_count; i++) { + furi_hal_gpio_init( + input_pins[i].gpio, + mode, + (input_pins[i].inverted) ? GpioPullUp : GpioPullDown, + GpioSpeedLow); + } +} + +static void furi_hal_resources_init_gpio_pins(GpioMode mode) { + for(size_t i = 0; i < gpio_pins_count; i++) { + if(!gpio_pins[i].debug) { + furi_hal_gpio_init(gpio_pins[i].pin, mode, GpioPullNo, GpioSpeedLow); + } + } +} + +void furi_hal_resources_init_early() { + furi_hal_bus_enable(FuriHalBusGPIOA); + furi_hal_bus_enable(FuriHalBusGPIOB); + furi_hal_bus_enable(FuriHalBusGPIOC); + furi_hal_bus_enable(FuriHalBusGPIOD); + furi_hal_bus_enable(FuriHalBusGPIOE); + furi_hal_bus_enable(FuriHalBusGPIOH); + + furi_hal_resources_init_input_pins(GpioModeInput); + + // Explicit, surviving reset, pulls + LL_PWR_EnablePUPDCfg(); + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_A, LL_PWR_GPIO_BIT_8); // gpio_vibro + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_8); // gpio_speaker + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_9); // gpio_infrared_tx + + // SD Card stepdown control + furi_hal_gpio_write(&gpio_periph_power, 1); + furi_hal_gpio_init(&gpio_periph_power, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); + + // Display pins + furi_hal_gpio_write(&gpio_display_rst_n, 0); + furi_hal_gpio_init_simple(&gpio_display_rst_n, GpioModeOutputPushPull); + LL_PWR_EnableGPIOPullUp(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_0); // gpio_display_rst_n + furi_hal_gpio_write(&gpio_display_di, 0); + furi_hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull); + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_1); // gpio_display_di + + // Hard reset USB + furi_hal_gpio_write(&gpio_usb_dm, 1); + furi_hal_gpio_write(&gpio_usb_dp, 1); + furi_hal_gpio_init_simple(&gpio_usb_dm, GpioModeOutputOpenDrain); + furi_hal_gpio_init_simple(&gpio_usb_dp, GpioModeOutputOpenDrain); + furi_hal_gpio_write(&gpio_usb_dm, 0); + furi_hal_gpio_write(&gpio_usb_dp, 0); + furi_delay_us(5); // Device Driven disconnect: 2.5us + extra to compensate cables + furi_hal_gpio_write(&gpio_usb_dm, 1); + furi_hal_gpio_write(&gpio_usb_dp, 1); + furi_hal_gpio_init_simple(&gpio_usb_dm, GpioModeAnalog); + furi_hal_gpio_init_simple(&gpio_usb_dp, GpioModeAnalog); + furi_hal_gpio_write(&gpio_usb_dm, 0); + furi_hal_gpio_write(&gpio_usb_dp, 0); + + // External header pins + furi_hal_resources_init_gpio_pins(GpioModeAnalog); +} + +void furi_hal_resources_deinit_early() { + furi_hal_resources_init_input_pins(GpioModeAnalog); + furi_hal_bus_disable(FuriHalBusGPIOA); + furi_hal_bus_disable(FuriHalBusGPIOB); + furi_hal_bus_disable(FuriHalBusGPIOC); + furi_hal_bus_disable(FuriHalBusGPIOD); + furi_hal_bus_disable(FuriHalBusGPIOE); + furi_hal_bus_disable(FuriHalBusGPIOH); +} + +void furi_hal_resources_init() { + // Button pins + furi_hal_resources_init_input_pins(GpioModeInterruptRiseFall); + + // SD pins + furi_hal_gpio_init(&gpio_sdcard_cd, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_sdcard_cd, 0); + + furi_hal_gpio_init(&gpio_ibutton, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeInterruptRise, GpioPullNo, GpioSpeedLow); + + furi_hal_gpio_init(&gpio_rf_sw_0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + + NVIC_SetPriority(EXTI0_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI0_IRQn); + + NVIC_SetPriority(EXTI1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI1_IRQn); + + NVIC_SetPriority(EXTI2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI2_IRQn); + + NVIC_SetPriority(EXTI3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI3_IRQn); + + NVIC_SetPriority(EXTI4_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI4_IRQn); + + NVIC_SetPriority(EXTI9_5_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI9_5_IRQn); + + NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI15_10_IRQn); + + FURI_LOG_I(TAG, "Init OK"); +} + +int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) { + for(size_t i = 0; i < gpio_pins_count; i++) { + if(gpio_pins[i].pin == gpio) { + return gpio_pins[i].number; + } + } + return -1; +} diff --git a/targets/f7/furi_hal/furi_hal_resources.h b/targets/f7/furi_hal/furi_hal_resources.h new file mode 100644 index 00000000000..6ca6f9df015 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_resources.h @@ -0,0 +1,232 @@ +#pragma once + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Input Related Constants */ +#define INPUT_DEBOUNCE_TICKS 4 + +/* Input Keys */ +typedef enum { + InputKeyUp, + InputKeyDown, + InputKeyRight, + InputKeyLeft, + InputKeyOk, + InputKeyBack, + InputKeyMAX, /**< Special value */ +} InputKey; + +/* Light */ +typedef enum { + LightRed = (1 << 0), + LightGreen = (1 << 1), + LightBlue = (1 << 2), + LightBacklight = (1 << 3), +} Light; + +typedef struct { + const GpioPin* gpio; + const InputKey key; + const bool inverted; + const char* name; +} InputPin; + +typedef struct { + const GpioPin* pin; + const char* name; + const uint8_t number; + const bool debug; +} GpioPinRecord; + +extern const InputPin input_pins[]; +extern const size_t input_pins_count; + +extern const GpioPinRecord gpio_pins[]; +extern const size_t gpio_pins_count; + +extern const GpioPin gpio_swdio; +extern const GpioPin gpio_swclk; + +extern const GpioPin gpio_vibro; +extern const GpioPin gpio_ibutton; + +extern const GpioPin gpio_cc1101_g0; +extern const GpioPin gpio_rf_sw_0; + +extern const GpioPin gpio_subghz_cs; +extern const GpioPin gpio_display_cs; +extern const GpioPin gpio_display_rst_n; +extern const GpioPin gpio_display_di; +extern const GpioPin gpio_sdcard_cs; +extern const GpioPin gpio_sdcard_cd; +extern const GpioPin gpio_nfc_cs; + +extern const GpioPin gpio_button_up; +extern const GpioPin gpio_button_down; +extern const GpioPin gpio_button_right; +extern const GpioPin gpio_button_left; +extern const GpioPin gpio_button_ok; +extern const GpioPin gpio_button_back; + +extern const GpioPin gpio_spi_d_miso; +extern const GpioPin gpio_spi_d_mosi; +extern const GpioPin gpio_spi_d_sck; +extern const GpioPin gpio_spi_r_miso; +extern const GpioPin gpio_spi_r_mosi; +extern const GpioPin gpio_spi_r_sck; + +extern const GpioPin gpio_ext_pc0; +extern const GpioPin gpio_ext_pc1; +extern const GpioPin gpio_ext_pc3; +extern const GpioPin gpio_ext_pb2; +extern const GpioPin gpio_ext_pb3; +extern const GpioPin gpio_ext_pa4; +extern const GpioPin gpio_ext_pa6; +extern const GpioPin gpio_ext_pa7; + +extern const GpioPin gpio_nfc_irq_rfid_pull; +extern const GpioPin gpio_rfid_carrier_out; +extern const GpioPin gpio_rfid_data_in; +extern const GpioPin gpio_rfid_carrier; + +extern const GpioPin gpio_infrared_rx; +extern const GpioPin gpio_infrared_tx; + +extern const GpioPin gpio_usart_tx; +extern const GpioPin gpio_usart_rx; +extern const GpioPin gpio_i2c_power_sda; +extern const GpioPin gpio_i2c_power_scl; + +extern const GpioPin gpio_speaker; + +extern const GpioPin gpio_periph_power; + +extern const GpioPin gpio_usb_dm; +extern const GpioPin gpio_usb_dp; + +#define BUTTON_BACK_GPIO_Port GPIOC +#define BUTTON_BACK_Pin LL_GPIO_PIN_13 +#define BUTTON_DOWN_GPIO_Port GPIOC +#define BUTTON_DOWN_Pin LL_GPIO_PIN_6 +#define BUTTON_LEFT_GPIO_Port GPIOB +#define BUTTON_LEFT_Pin LL_GPIO_PIN_11 +#define BUTTON_OK_GPIO_Port GPIOH +#define BUTTON_OK_Pin LL_GPIO_PIN_3 +#define BUTTON_RIGHT_GPIO_Port GPIOB +#define BUTTON_RIGHT_Pin LL_GPIO_PIN_12 +#define BUTTON_UP_GPIO_Port GPIOB +#define BUTTON_UP_Pin LL_GPIO_PIN_10 + +#define CC1101_CS_GPIO_Port GPIOD +#define CC1101_CS_Pin LL_GPIO_PIN_0 +#define CC1101_G0_GPIO_Port GPIOA +#define CC1101_G0_Pin LL_GPIO_PIN_1 + +#define DISPLAY_CS_GPIO_Port GPIOC +#define DISPLAY_CS_Pin LL_GPIO_PIN_11 +#define DISPLAY_DI_GPIO_Port GPIOB +#define DISPLAY_DI_Pin LL_GPIO_PIN_1 +#define DISPLAY_RST_GPIO_Port GPIOB +#define DISPLAY_RST_Pin LL_GPIO_PIN_0 + +#define IR_RX_GPIO_Port GPIOA +#define IR_RX_Pin LL_GPIO_PIN_0 +#define IR_TX_GPIO_Port GPIOB +#define IR_TX_Pin LL_GPIO_PIN_9 + +#define NFC_CS_GPIO_Port GPIOE +#define NFC_CS_Pin LL_GPIO_PIN_4 + +#define PA4_GPIO_Port GPIOA +#define PA4_Pin LL_GPIO_PIN_4 +#define PA6_GPIO_Port GPIOA +#define PA6_Pin LL_GPIO_PIN_6 +#define PA7_GPIO_Port GPIOA +#define PA7_Pin LL_GPIO_PIN_7 +#define PB2_GPIO_Port GPIOB +#define PB2_Pin LL_GPIO_PIN_2 +#define PB3_GPIO_Port GPIOB +#define PB3_Pin LL_GPIO_PIN_3 +#define PC0_GPIO_Port GPIOC +#define PC0_Pin LL_GPIO_PIN_0 +#define PC1_GPIO_Port GPIOC +#define PC1_Pin LL_GPIO_PIN_1 +#define PC3_GPIO_Port GPIOC +#define PC3_Pin LL_GPIO_PIN_3 + +#define QUARTZ_32MHZ_IN_GPIO_Port GPIOC +#define QUARTZ_32MHZ_IN_Pin LL_GPIO_PIN_14 +#define QUARTZ_32MHZ_OUT_GPIO_Port GPIOC +#define QUARTZ_32MHZ_OUT_Pin LL_GPIO_PIN_15 + +#define RFID_OUT_GPIO_Port GPIOB +#define RFID_OUT_Pin LL_GPIO_PIN_13 +#define RFID_PULL_GPIO_Port GPIOA +#define RFID_PULL_Pin LL_GPIO_PIN_2 +#define RFID_RF_IN_GPIO_Port GPIOC +#define RFID_RF_IN_Pin LL_GPIO_PIN_5 +#define RFID_CARRIER_GPIO_Port GPIOA +#define RFID_CARRIER_Pin LL_GPIO_PIN_15 + +#define RF_SW_0_GPIO_Port GPIOC +#define RF_SW_0_Pin LL_GPIO_PIN_4 + +#define SD_CD_GPIO_Port GPIOC +#define SD_CD_Pin LL_GPIO_PIN_10 +#define SD_CS_GPIO_Port GPIOC +#define SD_CS_Pin LL_GPIO_PIN_12 + +#define SPEAKER_GPIO_Port GPIOB +#define SPEAKER_Pin LL_GPIO_PIN_8 + +#define VIBRO_GPIO_Port GPIOA +#define VIBRO_Pin LL_GPIO_PIN_8 + +#define iBTN_GPIO_Port GPIOB +#define iBTN_Pin LL_GPIO_PIN_14 + +#define USART1_TX_Pin LL_GPIO_PIN_6 +#define USART1_TX_Port GPIOB +#define USART1_RX_Pin LL_GPIO_PIN_7 +#define USART1_RX_Port GPIOB + +#define SPI_D_MISO_GPIO_Port GPIOC +#define SPI_D_MISO_Pin LL_GPIO_PIN_2 +#define SPI_D_MOSI_GPIO_Port GPIOB +#define SPI_D_MOSI_Pin LL_GPIO_PIN_15 +#define SPI_D_SCK_GPIO_Port GPIOD +#define SPI_D_SCK_Pin LL_GPIO_PIN_1 + +#define SPI_R_MISO_GPIO_Port GPIOB +#define SPI_R_MISO_Pin LL_GPIO_PIN_4 +#define SPI_R_MOSI_GPIO_Port GPIOB +#define SPI_R_MOSI_Pin LL_GPIO_PIN_5 +#define SPI_R_SCK_GPIO_Port GPIOA +#define SPI_R_SCK_Pin LL_GPIO_PIN_5 + +#define NFC_IRQ_Pin RFID_PULL_Pin +#define NFC_IRQ_GPIO_Port RFID_PULL_GPIO_Port + +void furi_hal_resources_init_early(); + +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/targets/f7/furi_hal/furi_hal_rfid.c b/targets/f7/furi_hal/furi_hal_rfid.c new file mode 100644 index 00000000000..67f11d6ff7b --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_rfid.c @@ -0,0 +1,588 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define FURI_HAL_RFID_READ_TIMER TIM1 +#define FURI_HAL_RFID_READ_TIMER_BUS FuriHalBusTIM1 +#define FURI_HAL_RFID_READ_TIMER_CHANNEL LL_TIM_CHANNEL_CH1N +// We can't use N channel for LL_TIM_OC_Init, so... +#define FURI_HAL_RFID_READ_TIMER_CHANNEL_CONFIG LL_TIM_CHANNEL_CH1 + +#define FURI_HAL_RFID_EMULATE_TIMER TIM2 +#define FURI_HAL_RFID_EMULATE_TIMER_BUS FuriHalBusTIM2 +#define FURI_HAL_RFID_EMULATE_TIMER_IRQ FuriHalInterruptIdTIM2 +#define FURI_HAL_RFID_EMULATE_TIMER_CHANNEL LL_TIM_CHANNEL_CH3 + +#define RFID_CAPTURE_TIM TIM2 +#define RFID_CAPTURE_TIM_BUS FuriHalBusTIM2 +#define RFID_CAPTURE_IND_CH LL_TIM_CHANNEL_CH3 +#define RFID_CAPTURE_DIR_CH LL_TIM_CHANNEL_CH4 + +// Field presence detection +#define FURI_HAL_RFID_FIELD_FREQUENCY_MIN 80000 +#define FURI_HAL_RFID_FIELD_FREQUENCY_MAX 200000 + +#define FURI_HAL_RFID_FIELD_COUNTER_TIMER TIM2 +#define FURI_HAL_RFID_FIELD_COUNTER_TIMER_BUS FuriHalBusTIM2 +#define FURI_HAL_RFID_FIELD_COUNTER_TIMER_CHANNEL LL_TIM_CHANNEL_CH3 + +#define FURI_HAL_RFID_FIELD_TIMEOUT_TIMER TIM1 +#define FURI_HAL_RFID_FIELD_TIMEOUT_TIMER_BUS FuriHalBusTIM1 + +#define FURI_HAL_RFID_FIELD_DMAMUX_DMA LL_DMAMUX_REQ_TIM1_UP + +/* 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 { + uint32_t counter; + uint32_t set_tim_counter_cnt; +} FuriHalRfidField; + +typedef struct { + FuriHalRfidDMACallback dma_callback; + FuriHalRfidReadCaptureCallback read_capture_callback; + void* context; + FuriHalRfidField field; +} FuriHalRfid; + +FuriHalRfid* furi_hal_rfid = NULL; + +#define LFRFID_LL_READ_TIM TIM1 +#define LFRFID_LL_READ_CONFIG_CHANNEL LL_TIM_CHANNEL_CH1 +#define LFRFID_LL_READ_CHANNEL LL_TIM_CHANNEL_CH1N + +#define LFRFID_LL_EMULATE_TIM TIM2 +#define LFRFID_LL_EMULATE_CHANNEL LL_TIM_CHANNEL_CH3 + +void furi_hal_rfid_init() { + furi_assert(furi_hal_rfid == NULL); + furi_hal_rfid = malloc(sizeof(FuriHalRfid)); + furi_hal_rfid->field.counter = 0; + furi_hal_rfid->field.set_tim_counter_cnt = 0; + + furi_hal_rfid_pins_reset(); + + LL_COMP_InitTypeDef COMP_InitStruct = {0}; + COMP_InitStruct.PowerMode = LL_COMP_POWERMODE_MEDIUMSPEED; + COMP_InitStruct.InputPlus = LL_COMP_INPUT_PLUS_IO1; + COMP_InitStruct.InputMinus = LL_COMP_INPUT_MINUS_1_2VREFINT; + COMP_InitStruct.InputHysteresis = LL_COMP_HYSTERESIS_HIGH; + COMP_InitStruct.OutputPolarity = LL_COMP_OUTPUTPOL_NONINVERTED; + COMP_InitStruct.OutputBlankingSource = LL_COMP_BLANKINGSRC_NONE; + LL_COMP_Init(COMP1, &COMP_InitStruct); + LL_COMP_SetCommonWindowMode(__LL_COMP_COMMON_INSTANCE(COMP1), LL_COMP_WINDOWMODE_DISABLE); + + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_20); + LL_EXTI_EnableFallingTrig_0_31(LL_EXTI_LINE_20); + LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_20); + LL_EXTI_DisableEvent_0_31(LL_EXTI_LINE_20); + LL_EXTI_EnableIT_0_31(LL_EXTI_LINE_20); + + NVIC_SetPriority(COMP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(COMP_IRQn); +} + +void furi_hal_rfid_pins_reset() { + // ibutton bus disable + furi_hal_ibutton_pin_reset(); + + // pulldown rfid antenna + furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_rfid_carrier_out, false); + + // from both sides + furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, true); + + furi_hal_gpio_init_simple(&gpio_rfid_carrier, GpioModeAnalog); + + furi_hal_gpio_init(&gpio_rfid_data_in, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +static void furi_hal_rfid_pins_emulate() { + // ibutton low + furi_hal_ibutton_pin_configure(); + furi_hal_ibutton_pin_write(false); + + // pull pin to timer out + furi_hal_gpio_init_ex( + &gpio_nfc_irq_rfid_pull, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedLow, + GpioAltFn1TIM2); + + // pull rfid antenna from carrier side + furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_rfid_carrier_out, false); + + furi_hal_gpio_init_ex( + &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn2TIM2); +} + +static void furi_hal_rfid_pins_read() { + // ibutton 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); + furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, false); + + // carrier pin to timer out + furi_hal_gpio_init_ex( + &gpio_rfid_carrier_out, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedLow, + GpioAltFn1TIM1); + + // comparator in + furi_hal_gpio_init(&gpio_rfid_data_in, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +static void furi_hal_rfid_pins_field() { + // ibutton low + furi_hal_ibutton_pin_configure(); + furi_hal_ibutton_pin_write(false); + + // pull pin to timer out + furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, false); + + // pull rfid antenna from carrier side + furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_rfid_carrier_out, false); + + furi_hal_gpio_init_ex( + &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn2TIM2); +} + +void furi_hal_rfid_pin_pull_release() { + furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, true); +} + +void furi_hal_rfid_pin_pull_pulldown() { + furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, false); +} + +void furi_hal_rfid_tim_read_start(float freq, float duty_cycle) { + furi_hal_bus_enable(FURI_HAL_RFID_READ_TIMER_BUS); + + furi_hal_rfid_pins_read(); + + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + TIM_InitStruct.Autoreload = (SystemCoreClock / freq) - 1; + LL_TIM_Init(FURI_HAL_RFID_READ_TIMER, &TIM_InitStruct); + LL_TIM_DisableARRPreload(FURI_HAL_RFID_READ_TIMER); + + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = TIM_InitStruct.Autoreload * duty_cycle; + LL_TIM_OC_Init( + FURI_HAL_RFID_READ_TIMER, FURI_HAL_RFID_READ_TIMER_CHANNEL_CONFIG, &TIM_OC_InitStruct); + + LL_TIM_EnableCounter(FURI_HAL_RFID_READ_TIMER); + + furi_hal_rfid_tim_read_continue(); +} + +void furi_hal_rfid_tim_read_continue() { + LL_TIM_EnableAllOutputs(FURI_HAL_RFID_READ_TIMER); +} + +void furi_hal_rfid_tim_read_pause() { + LL_TIM_DisableAllOutputs(FURI_HAL_RFID_READ_TIMER); +} + +void furi_hal_rfid_tim_read_stop() { + furi_hal_bus_disable(FURI_HAL_RFID_READ_TIMER_BUS); +} + +static void furi_hal_rfid_tim_emulate() { + LL_TIM_SetPrescaler(FURI_HAL_RFID_EMULATE_TIMER, 0); + LL_TIM_SetCounterMode(FURI_HAL_RFID_EMULATE_TIMER, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetAutoReload(FURI_HAL_RFID_EMULATE_TIMER, 1); + LL_TIM_DisableARRPreload(FURI_HAL_RFID_EMULATE_TIMER); + LL_TIM_SetRepetitionCounter(FURI_HAL_RFID_EMULATE_TIMER, 0); + + LL_TIM_SetClockDivision(FURI_HAL_RFID_EMULATE_TIMER, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetClockSource(FURI_HAL_RFID_EMULATE_TIMER, LL_TIM_CLOCKSOURCE_EXT_MODE2); + LL_TIM_ConfigETR( + FURI_HAL_RFID_EMULATE_TIMER, + LL_TIM_ETR_POLARITY_INVERTED, + LL_TIM_ETR_PRESCALER_DIV1, + LL_TIM_ETR_FILTER_FDIV1); + + 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 = 1; + LL_TIM_OC_Init( + FURI_HAL_RFID_EMULATE_TIMER, FURI_HAL_RFID_EMULATE_TIMER_CHANNEL, &TIM_OC_InitStruct); + + LL_TIM_GenerateEvent_UPDATE(FURI_HAL_RFID_EMULATE_TIMER); +} + +static void furi_hal_capture_dma_isr(void* context) { + UNUSED(context); + + // Channel 3, positive level + if(LL_TIM_IsActiveFlag_CC3(RFID_CAPTURE_TIM)) { + LL_TIM_ClearFlag_CC3(RFID_CAPTURE_TIM); + furi_hal_rfid->read_capture_callback( + true, LL_TIM_IC_GetCaptureCH3(RFID_CAPTURE_TIM), furi_hal_rfid->context); + } + + // Channel 4, overall level + if(LL_TIM_IsActiveFlag_CC4(RFID_CAPTURE_TIM)) { + LL_TIM_ClearFlag_CC4(RFID_CAPTURE_TIM); + LL_TIM_SetCounter(RFID_CAPTURE_TIM, 0); + furi_hal_rfid->read_capture_callback( + false, LL_TIM_IC_GetCaptureCH4(RFID_CAPTURE_TIM), furi_hal_rfid->context); + } +} + +void furi_hal_rfid_tim_read_capture_start(FuriHalRfidReadCaptureCallback callback, void* context) { + furi_assert(furi_hal_rfid); + + furi_hal_rfid->read_capture_callback = callback; + furi_hal_rfid->context = context; + + furi_hal_bus_enable(RFID_CAPTURE_TIM_BUS); + + // Timer: base + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + TIM_InitStruct.Prescaler = 64 - 1; + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + TIM_InitStruct.Autoreload = UINT32_MAX; + TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; + LL_TIM_Init(RFID_CAPTURE_TIM, &TIM_InitStruct); + + // Timer: advanced + LL_TIM_SetClockSource(RFID_CAPTURE_TIM, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(RFID_CAPTURE_TIM); + LL_TIM_SetTriggerInput(RFID_CAPTURE_TIM, LL_TIM_TS_TI2FP2); + LL_TIM_SetSlaveMode(RFID_CAPTURE_TIM, LL_TIM_SLAVEMODE_DISABLED); + LL_TIM_SetTriggerOutput(RFID_CAPTURE_TIM, LL_TIM_TRGO_RESET); + LL_TIM_EnableMasterSlaveMode(RFID_CAPTURE_TIM); + LL_TIM_DisableDMAReq_TRIG(RFID_CAPTURE_TIM); + LL_TIM_DisableIT_TRIG(RFID_CAPTURE_TIM); + LL_TIM_SetRemap(RFID_CAPTURE_TIM, LL_TIM_TIM2_TI4_RMP_COMP1); + + // Timer: channel 3 indirect + LL_TIM_IC_SetActiveInput(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH, LL_TIM_ACTIVEINPUT_INDIRECTTI); + LL_TIM_IC_SetPrescaler(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH, LL_TIM_ICPSC_DIV1); + LL_TIM_IC_SetPolarity(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH, LL_TIM_IC_POLARITY_FALLING); + LL_TIM_IC_SetFilter(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH, LL_TIM_IC_FILTER_FDIV1); + + // Timer: channel 4 direct + LL_TIM_IC_SetActiveInput(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH, LL_TIM_ACTIVEINPUT_DIRECTTI); + LL_TIM_IC_SetPrescaler(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH, LL_TIM_ICPSC_DIV1); + LL_TIM_IC_SetPolarity(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH, LL_TIM_IC_POLARITY_RISING); + LL_TIM_IC_SetFilter(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH, LL_TIM_IC_FILTER_FDIV1); + + furi_hal_interrupt_set_isr(FURI_HAL_RFID_EMULATE_TIMER_IRQ, furi_hal_capture_dma_isr, NULL); + + LL_TIM_EnableIT_CC3(RFID_CAPTURE_TIM); + LL_TIM_EnableIT_CC4(RFID_CAPTURE_TIM); + LL_TIM_CC_EnableChannel(RFID_CAPTURE_TIM, RFID_CAPTURE_IND_CH); + LL_TIM_CC_EnableChannel(RFID_CAPTURE_TIM, RFID_CAPTURE_DIR_CH); + LL_TIM_SetCounter(RFID_CAPTURE_TIM, 0); + LL_TIM_EnableCounter(RFID_CAPTURE_TIM); + + furi_hal_rfid_comp_start(); +} + +void furi_hal_rfid_tim_read_capture_stop() { + furi_hal_rfid_comp_stop(); + + furi_hal_interrupt_set_isr(FURI_HAL_RFID_EMULATE_TIMER_IRQ, NULL, NULL); + furi_hal_bus_disable(RFID_CAPTURE_TIM_BUS); +} + +static void furi_hal_rfid_dma_isr() { +#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(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( + uint32_t* duration, + uint32_t* pulse, + size_t length, + FuriHalRfidDMACallback callback, + void* context) { + furi_assert(furi_hal_rfid); + + // setup interrupts + furi_hal_rfid->dma_callback = callback; + furi_hal_rfid->context = context; + + // setup pins + furi_hal_rfid_pins_emulate(); + + // configure timer + furi_hal_bus_enable(FURI_HAL_RFID_EMULATE_TIMER_BUS); + furi_hal_rfid_tim_emulate(); + LL_TIM_OC_SetPolarity( + FURI_HAL_RFID_EMULATE_TIMER, FURI_HAL_RFID_EMULATE_TIMER_CHANNEL, LL_TIM_OCPOLARITY_HIGH); + LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_RFID_EMULATE_TIMER); + + // configure DMA "mem -> ARR" channel + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (FURI_HAL_RFID_EMULATE_TIMER->ARR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)duration; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = length; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_MODE_NORMAL; + 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 + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (FURI_HAL_RFID_EMULATE_TIMER->CCR3); +#else +#error Update this code. Would you kindly? +#endif + dma_config.MemoryOrM2MDstAddress = (uint32_t)pulse; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = length; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_MODE_NORMAL; + 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(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); + + LL_TIM_SetCounter(FURI_HAL_RFID_EMULATE_TIMER, 0); + LL_TIM_EnableCounter(FURI_HAL_RFID_EMULATE_TIMER); +} + +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(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(RFID_DMA_CH1_DEF); + LL_DMA_DeInit(RFID_DMA_CH2_DEF); + + furi_hal_bus_disable(FURI_HAL_RFID_EMULATE_TIMER_BUS); + + FURI_CRITICAL_EXIT(); +} + +void furi_hal_rfid_set_read_period(uint32_t period) { + LL_TIM_SetAutoReload(FURI_HAL_RFID_READ_TIMER, period); +} + +void furi_hal_rfid_set_read_pulse(uint32_t pulse) { +#if FURI_HAL_RFID_READ_TIMER_CHANNEL == LL_TIM_CHANNEL_CH1N + LL_TIM_OC_SetCompareCH1(FURI_HAL_RFID_READ_TIMER, pulse); +#else +#error Update this code. Would you kindly? +#endif +} + +void furi_hal_rfid_comp_start() { + LL_COMP_Enable(COMP1); + // Magic + uint32_t wait_loop_index = ((80 / 10UL) * ((SystemCoreClock / (100000UL * 2UL)) + 1UL)); + while(wait_loop_index) { + wait_loop_index--; + } +} + +void furi_hal_rfid_comp_stop() { + LL_COMP_Disable(COMP1); +} + +FuriHalRfidCompCallback furi_hal_rfid_comp_callback = NULL; +void* furi_hal_rfid_comp_callback_context = NULL; + +void furi_hal_rfid_comp_set_callback(FuriHalRfidCompCallback callback, void* context) { + FURI_CRITICAL_ENTER(); + furi_hal_rfid_comp_callback = callback; + furi_hal_rfid_comp_callback_context = context; + __DMB(); + FURI_CRITICAL_EXIT(); +} + +/* Comparator trigger event */ +void COMP_IRQHandler() { + if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_20)) { + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_20); + } + if(furi_hal_rfid_comp_callback) { + furi_hal_rfid_comp_callback( + (LL_COMP_ReadOutputLevel(COMP1) == LL_COMP_OUTPUT_LEVEL_LOW), + furi_hal_rfid_comp_callback_context); + } +} + +static void furi_hal_rfid_field_tim_setup() { + // setup timer counter + furi_hal_bus_enable(FURI_HAL_RFID_FIELD_COUNTER_TIMER_BUS); + + LL_TIM_SetPrescaler(FURI_HAL_RFID_FIELD_COUNTER_TIMER, 0); + LL_TIM_SetCounterMode(FURI_HAL_RFID_FIELD_COUNTER_TIMER, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetAutoReload(FURI_HAL_RFID_FIELD_COUNTER_TIMER, 0xFFFFFFFF); + LL_TIM_DisableARRPreload(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + LL_TIM_SetRepetitionCounter(FURI_HAL_RFID_FIELD_COUNTER_TIMER, 0); + + LL_TIM_SetClockDivision(FURI_HAL_RFID_FIELD_COUNTER_TIMER, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetClockSource(FURI_HAL_RFID_FIELD_COUNTER_TIMER, LL_TIM_CLOCKSOURCE_EXT_MODE2); + LL_TIM_ConfigETR( + FURI_HAL_RFID_FIELD_COUNTER_TIMER, + LL_TIM_ETR_POLARITY_INVERTED, + LL_TIM_ETR_PRESCALER_DIV1, + LL_TIM_ETR_FILTER_FDIV1); + + 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 = 1; + LL_TIM_OC_Init( + FURI_HAL_RFID_FIELD_COUNTER_TIMER, + FURI_HAL_RFID_FIELD_COUNTER_TIMER_CHANNEL, + &TIM_OC_InitStruct); + + LL_TIM_GenerateEvent_UPDATE(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + LL_TIM_OC_SetPolarity( + FURI_HAL_RFID_FIELD_COUNTER_TIMER, + FURI_HAL_RFID_FIELD_COUNTER_TIMER_CHANNEL, + LL_TIM_OCPOLARITY_HIGH); + LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + + // setup timer timeouts dma + furi_hal_bus_enable(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER_BUS); + + LL_TIM_SetPrescaler(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, 64000 - 1); + LL_TIM_SetCounterMode(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetAutoReload(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, 100 - 1); // 100 ms + LL_TIM_SetClockDivision(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetClockSource(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, LL_TIM_CLOCKSOURCE_INTERNAL); + + LL_TIM_DisableARRPreload(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); + + LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); + LL_TIM_GenerateEvent_UPDATE(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); +} + +void furi_hal_rfid_field_detect_start(void) { + // setup pins + furi_hal_rfid_pins_field(); + + // configure timer + furi_hal_rfid_field_tim_setup(); + + // configure DMA "TIM_COUNTER_CNT -> counter" + LL_DMA_SetMemoryAddress(RFID_DMA_CH1_DEF, (uint32_t) & (furi_hal_rfid->field.counter)); + LL_DMA_SetPeriphAddress( + RFID_DMA_CH1_DEF, (uint32_t) & (FURI_HAL_RFID_FIELD_COUNTER_TIMER->CNT)); + LL_DMA_ConfigTransfer( + RFID_DMA_CH1_DEF, + LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_NOINCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | + LL_DMA_PRIORITY_MEDIUM); + LL_DMA_SetDataLength(RFID_DMA_CH1_DEF, 1); + LL_DMA_SetPeriphRequest(RFID_DMA_CH1_DEF, FURI_HAL_RFID_FIELD_DMAMUX_DMA); + LL_DMA_EnableChannel(RFID_DMA_CH1_DEF); + + // configure DMA "mem -> TIM_COUNTER_CNT" + LL_DMA_SetMemoryAddress( + RFID_DMA_CH2_DEF, (uint32_t) & (furi_hal_rfid->field.set_tim_counter_cnt)); + LL_DMA_SetPeriphAddress( + RFID_DMA_CH2_DEF, (uint32_t) & (FURI_HAL_RFID_FIELD_COUNTER_TIMER->CNT)); + LL_DMA_ConfigTransfer( + RFID_DMA_CH2_DEF, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_NOINCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | + LL_DMA_PRIORITY_LOW); + LL_DMA_SetDataLength(RFID_DMA_CH2_DEF, 1); + LL_DMA_SetPeriphRequest(RFID_DMA_CH2_DEF, FURI_HAL_RFID_FIELD_DMAMUX_DMA); + LL_DMA_EnableChannel(RFID_DMA_CH2_DEF); + + // start tim counter + LL_TIM_EnableAllOutputs(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + + LL_TIM_SetCounter(FURI_HAL_RFID_FIELD_COUNTER_TIMER, 0); + LL_TIM_EnableCounter(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + + // start tim timeout + LL_TIM_SetCounter(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, 0); + LL_TIM_EnableCounter(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); + LL_TIM_EnableIT_UPDATE(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); +} + +void furi_hal_rfid_field_detect_stop(void) { + LL_TIM_DisableCounter(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + LL_TIM_DisableAllOutputs(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + + LL_TIM_DisableCounter(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); + + FURI_CRITICAL_ENTER(); + + LL_DMA_DeInit(RFID_DMA_CH1_DEF); + LL_DMA_DeInit(RFID_DMA_CH2_DEF); + + furi_hal_bus_disable(FURI_HAL_RFID_FIELD_COUNTER_TIMER_BUS); + furi_hal_bus_disable(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER_BUS); + + furi_hal_rfid_pins_reset(); + + FURI_CRITICAL_EXIT(); +} + +bool furi_hal_rfid_field_is_present(uint32_t* frequency) { + *frequency = furi_hal_rfid->field.counter * 10; + return ( + (*frequency >= FURI_HAL_RFID_FIELD_FREQUENCY_MIN) && + (*frequency <= FURI_HAL_RFID_FIELD_FREQUENCY_MAX)); +} \ No newline at end of file diff --git a/targets/f7/furi_hal/furi_hal_rfid.h b/targets/f7/furi_hal/furi_hal_rfid.h new file mode 100644 index 00000000000..7087ba991fa --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_rfid.h @@ -0,0 +1,106 @@ +/** + * @file furi_hal_rfid.h + * RFID HAL API + */ + +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Initialize RFID subsystem + */ +void furi_hal_rfid_init(); + +/** Config rfid pins to reset state + */ +void furi_hal_rfid_pins_reset(); + +/** Release rfid pull pin + */ +void furi_hal_rfid_pin_pull_release(); + +/** Pulldown rfid pull pin + */ +void furi_hal_rfid_pin_pull_pulldown(); + +/** Start read timer + * @param freq timer frequency + * @param duty_cycle timer duty cycle, 0.0-1.0 + */ +void furi_hal_rfid_tim_read_start(float freq, float duty_cycle); + +/** Pause read timer, to be able to continue later + */ +void furi_hal_rfid_tim_read_pause(); + +/** Continue read timer + */ +void furi_hal_rfid_tim_read_continue(); + +/** Stop read timer + */ +void furi_hal_rfid_tim_read_stop(); + +typedef void (*FuriHalRfidReadCaptureCallback)(bool level, uint32_t duration, void* context); + +void furi_hal_rfid_tim_read_capture_start(FuriHalRfidReadCaptureCallback callback, void* context); + +void furi_hal_rfid_tim_read_capture_stop(); + +typedef void (*FuriHalRfidDMACallback)(bool half, void* context); + +void furi_hal_rfid_tim_emulate_dma_start( + uint32_t* duration, + uint32_t* pulse, + size_t length, + FuriHalRfidDMACallback callback, + void* context); + +void furi_hal_rfid_tim_emulate_dma_stop(); + +/** Set read timer period + * + * @param period overall duration + */ +void furi_hal_rfid_set_read_period(uint32_t period); + +/** Set read timer pulse + * + * @param pulse duration of high level + */ +void furi_hal_rfid_set_read_pulse(uint32_t pulse); + +/** Start/Enable comparator */ +void furi_hal_rfid_comp_start(); + +/** Stop/Disable comparator */ +void furi_hal_rfid_comp_stop(); + +typedef void (*FuriHalRfidCompCallback)(bool level, void* context); + +/** Set comparator callback */ +void furi_hal_rfid_comp_set_callback(FuriHalRfidCompCallback callback, void* context); + +/** Start/Enable Field Presence detect */ +void furi_hal_rfid_field_detect_start(); + +/** Stop/Disable Field Presence detect */ +void furi_hal_rfid_field_detect_stop(); + +/** Check Field Presence + * + * @param[out] frequency pointer to frequency value to be set if filed detected + * + * @return true if field is present, false if not + */ +bool furi_hal_rfid_field_is_present(uint32_t* frequency); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/furi_hal/furi_hal_rtc.c b/targets/f7/furi_hal/furi_hal_rtc.c new file mode 100644 index 00000000000..a8e25faad66 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_rtc.c @@ -0,0 +1,431 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define TAG "FuriHalRtc" + +#define FURI_HAL_RTC_LSE_STARTUP_TIME 300 + +#define FURI_HAL_RTC_CLOCK_IS_READY() (LL_RCC_LSE_IsReady() && LL_RCC_LSI1_IsReady()) + +#define FURI_HAL_RTC_HEADER_MAGIC 0x10F1 +#define FURI_HAL_RTC_HEADER_VERSION 0 + +typedef struct { + uint16_t magic; + uint8_t version; + uint8_t unused; +} FuriHalRtcHeader; + +typedef struct { + uint8_t log_level : 4; + uint8_t log_reserved : 4; + uint8_t flags; + FuriHalRtcBootMode boot_mode : 4; + FuriHalRtcHeapTrackMode heap_track_mode : 2; + FuriHalRtcLocaleUnits locale_units : 1; + FuriHalRtcLocaleTimeFormat locale_timeformat : 1; + FuriHalRtcLocaleDateFormat locale_dateformat : 2; + uint8_t reserved : 6; +} SystemReg; + +_Static_assert(sizeof(SystemReg) == 4, "SystemReg size mismatch"); + +#define FURI_HAL_RTC_SECONDS_PER_MINUTE 60 +#define FURI_HAL_RTC_SECONDS_PER_HOUR (FURI_HAL_RTC_SECONDS_PER_MINUTE * 60) +#define FURI_HAL_RTC_SECONDS_PER_DAY (FURI_HAL_RTC_SECONDS_PER_HOUR * 24) +#define FURI_HAL_RTC_MONTHS_COUNT 12 +#define FURI_HAL_RTC_EPOCH_START_YEAR 1970 + +static const uint8_t furi_hal_rtc_days_per_month[2][FURI_HAL_RTC_MONTHS_COUNT] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}}; + +static const uint16_t furi_hal_rtc_days_per_year[] = {365, 366}; + +static void furi_hal_rtc_reset() { + LL_RCC_ForceBackupDomainReset(); + LL_RCC_ReleaseBackupDomainReset(); +} + +static bool furi_hal_rtc_start_clock_and_switch() { + // Clock operation require access to Backup Domain + LL_PWR_EnableBkUpAccess(); + + // Enable LSI and LSE + LL_RCC_LSI1_Enable(); + LL_RCC_LSE_SetDriveCapability(LL_RCC_LSEDRIVE_HIGH); + LL_RCC_LSE_Enable(); + + // Wait for LSI and LSE startup + uint32_t c = 0; + while(!FURI_HAL_RTC_CLOCK_IS_READY() && c < FURI_HAL_RTC_LSE_STARTUP_TIME) { + LL_mDelay(1); + c++; + } + + if(FURI_HAL_RTC_CLOCK_IS_READY()) { + LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE); + LL_RCC_EnableRTC(); + return LL_RCC_GetRTCClockSource() == LL_RCC_RTC_CLKSOURCE_LSE; + } else { + return false; + } +} + +static void furi_hal_rtc_recover() { + FuriHalRtcDateTime datetime = {0}; + + // Handle fixable LSE failure + if(LL_RCC_LSE_IsCSSDetected()) { + furi_hal_light_sequence("rgb B"); + // Shutdown LSE and LSECSS + LL_RCC_LSE_DisableCSS(); + LL_RCC_LSE_Disable(); + } else { + furi_hal_light_sequence("rgb R"); + } + + // Temporary switch to LSI + LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSI); + if(LL_RCC_GetRTCClockSource() == LL_RCC_RTC_CLKSOURCE_LSI) { + // Get datetime before RTC Domain reset + furi_hal_rtc_get_datetime(&datetime); + } + + // Reset RTC Domain + furi_hal_rtc_reset(); + + // Start Clock + if(!furi_hal_rtc_start_clock_and_switch()) { + // Plan C: reset RTC and restart + furi_hal_light_sequence("rgb R.r.R.r.R.r"); + furi_hal_rtc_reset(); + NVIC_SystemReset(); + } + + // Set date if it valid + if(datetime.year != 0) { + furi_hal_rtc_set_datetime(&datetime); + } +} + +void furi_hal_rtc_init_early() { + // Enable RTCAPB clock + LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_RTCAPB); + + // Prepare clock + if(!furi_hal_rtc_start_clock_and_switch()) { + // Plan B: try to recover + furi_hal_rtc_recover(); + } + + // Verify header register + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterHeader); + FuriHalRtcHeader* data = (FuriHalRtcHeader*)&data_reg; + if(data->magic != FURI_HAL_RTC_HEADER_MAGIC || data->version != FURI_HAL_RTC_HEADER_VERSION) { + // Reset all our registers to ensure consistency + for(size_t i = 0; i < FuriHalRtcRegisterMAX; i++) { + furi_hal_rtc_set_register(i, 0); + } + data->magic = FURI_HAL_RTC_HEADER_MAGIC; + data->version = FURI_HAL_RTC_HEADER_VERSION; + furi_hal_rtc_set_register(FuriHalRtcRegisterHeader, data_reg); + } + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + furi_hal_debug_enable(); + } else { + furi_hal_debug_disable(); + } +} + +void furi_hal_rtc_deinit_early() { +} + +void furi_hal_rtc_init() { + LL_RTC_InitTypeDef RTC_InitStruct; + RTC_InitStruct.HourFormat = LL_RTC_HOURFORMAT_24HOUR; + RTC_InitStruct.AsynchPrescaler = 127; + RTC_InitStruct.SynchPrescaler = 255; + LL_RTC_Init(RTC, &RTC_InitStruct); + + furi_log_set_level(furi_hal_rtc_get_log_level()); + + FURI_LOG_I(TAG, "Init OK"); +} + +void furi_hal_rtc_sync_shadow() { + if(!LL_RTC_IsShadowRegBypassEnabled(RTC)) { + LL_RTC_ClearFlag_RS(RTC); + while(!LL_RTC_IsActiveFlag_RS(RTC)) { + }; + } +} + +uint32_t furi_hal_rtc_get_register(FuriHalRtcRegister reg) { + return LL_RTC_BAK_GetRegister(RTC, reg); +} + +void furi_hal_rtc_set_register(FuriHalRtcRegister reg, uint32_t value) { + LL_RTC_BAK_SetRegister(RTC, reg, value); +} + +void furi_hal_rtc_set_log_level(uint8_t level) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->log_level = level; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); + furi_log_set_level(level); +} + +uint8_t furi_hal_rtc_get_log_level() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + return data->log_level; +} + +void furi_hal_rtc_set_flag(FuriHalRtcFlag flag) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->flags |= flag; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); + + if(flag & FuriHalRtcFlagDebug) { + furi_hal_debug_enable(); + } +} + +void furi_hal_rtc_reset_flag(FuriHalRtcFlag flag) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->flags &= ~flag; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); + + if(flag & FuriHalRtcFlagDebug) { + furi_hal_debug_disable(); + } +} + +bool furi_hal_rtc_is_flag_set(FuriHalRtcFlag flag) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + return data->flags & flag; +} + +void furi_hal_rtc_set_boot_mode(FuriHalRtcBootMode mode) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->boot_mode = mode; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); +} + +FuriHalRtcBootMode furi_hal_rtc_get_boot_mode() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + return data->boot_mode; +} + +void furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackMode mode) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->heap_track_mode = mode; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); +} + +FuriHalRtcHeapTrackMode furi_hal_rtc_get_heap_track_mode() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + return data->heap_track_mode; +} + +void furi_hal_rtc_set_locale_units(FuriHalRtcLocaleUnits value) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->locale_units = value; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); +} + +FuriHalRtcLocaleUnits furi_hal_rtc_get_locale_units() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + return data->locale_units; +} + +void furi_hal_rtc_set_locale_timeformat(FuriHalRtcLocaleTimeFormat value) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->locale_timeformat = value; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); +} + +FuriHalRtcLocaleTimeFormat furi_hal_rtc_get_locale_timeformat() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + return data->locale_timeformat; +} + +void furi_hal_rtc_set_locale_dateformat(FuriHalRtcLocaleDateFormat value) { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + data->locale_dateformat = value; + furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg); +} + +FuriHalRtcLocaleDateFormat furi_hal_rtc_get_locale_dateformat() { + uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem); + SystemReg* data = (SystemReg*)&data_reg; + return data->locale_dateformat; +} + +void furi_hal_rtc_set_datetime(FuriHalRtcDateTime* datetime) { + furi_check(!FURI_IS_IRQ_MODE()); + furi_assert(datetime); + + FURI_CRITICAL_ENTER(); + /* Disable write protection */ + LL_RTC_DisableWriteProtection(RTC); + + /* Enter Initialization mode and wait for INIT flag to be set */ + LL_RTC_EnableInitMode(RTC); + while(!LL_RTC_IsActiveFlag_INIT(RTC)) { + } + + /* Set time */ + LL_RTC_TIME_Config( + RTC, + LL_RTC_TIME_FORMAT_AM_OR_24, + __LL_RTC_CONVERT_BIN2BCD(datetime->hour), + __LL_RTC_CONVERT_BIN2BCD(datetime->minute), + __LL_RTC_CONVERT_BIN2BCD(datetime->second)); + + /* Set date */ + LL_RTC_DATE_Config( + RTC, + datetime->weekday, + __LL_RTC_CONVERT_BIN2BCD(datetime->day), + __LL_RTC_CONVERT_BIN2BCD(datetime->month), + __LL_RTC_CONVERT_BIN2BCD(datetime->year - 2000)); + + /* Exit Initialization mode */ + LL_RTC_DisableInitMode(RTC); + + furi_hal_rtc_sync_shadow(); + + /* Enable write protection */ + LL_RTC_EnableWriteProtection(RTC); + FURI_CRITICAL_EXIT(); +} + +void furi_hal_rtc_get_datetime(FuriHalRtcDateTime* datetime) { + furi_check(!FURI_IS_IRQ_MODE()); + furi_assert(datetime); + + FURI_CRITICAL_ENTER(); + uint32_t time = LL_RTC_TIME_Get(RTC); // 0x00HHMMSS + uint32_t date = LL_RTC_DATE_Get(RTC); // 0xWWDDMMYY + FURI_CRITICAL_EXIT(); + + datetime->second = __LL_RTC_CONVERT_BCD2BIN((time >> 0) & 0xFF); + datetime->minute = __LL_RTC_CONVERT_BCD2BIN((time >> 8) & 0xFF); + datetime->hour = __LL_RTC_CONVERT_BCD2BIN((time >> 16) & 0xFF); + datetime->year = __LL_RTC_CONVERT_BCD2BIN((date >> 0) & 0xFF) + 2000; + datetime->month = __LL_RTC_CONVERT_BCD2BIN((date >> 8) & 0xFF); + datetime->day = __LL_RTC_CONVERT_BCD2BIN((date >> 16) & 0xFF); + datetime->weekday = __LL_RTC_CONVERT_BCD2BIN((date >> 24) & 0xFF); +} + +bool furi_hal_rtc_validate_datetime(FuriHalRtcDateTime* datetime) { + bool invalid = false; + + invalid |= (datetime->second > 59); + invalid |= (datetime->minute > 59); + invalid |= (datetime->hour > 23); + + invalid |= (datetime->year < 2000); + invalid |= (datetime->year > 2099); + + invalid |= (datetime->month == 0); + invalid |= (datetime->month > 12); + + invalid |= (datetime->day == 0); + invalid |= (datetime->day > 31); + + invalid |= (datetime->weekday == 0); + invalid |= (datetime->weekday > 7); + + return !invalid; +} + +void furi_hal_rtc_set_fault_data(uint32_t value) { + furi_hal_rtc_set_register(FuriHalRtcRegisterFaultData, value); +} + +uint32_t furi_hal_rtc_get_fault_data() { + return furi_hal_rtc_get_register(FuriHalRtcRegisterFaultData); +} + +void furi_hal_rtc_set_pin_fails(uint32_t value) { + furi_hal_rtc_set_register(FuriHalRtcRegisterPinFails, value); +} + +uint32_t furi_hal_rtc_get_pin_fails() { + return furi_hal_rtc_get_register(FuriHalRtcRegisterPinFails); +} + +uint32_t furi_hal_rtc_get_timestamp() { + FuriHalRtcDateTime datetime = {0}; + furi_hal_rtc_get_datetime(&datetime); + return furi_hal_rtc_datetime_to_timestamp(&datetime); +} + +uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime) { + uint32_t timestamp = 0; + uint8_t years = 0; + uint8_t leap_years = 0; + + for(uint16_t y = FURI_HAL_RTC_EPOCH_START_YEAR; y < datetime->year; y++) { + if(furi_hal_rtc_is_leap_year(y)) { + leap_years++; + } else { + years++; + } + } + + timestamp += + ((years * furi_hal_rtc_days_per_year[0]) + (leap_years * furi_hal_rtc_days_per_year[1])) * + FURI_HAL_RTC_SECONDS_PER_DAY; + + bool leap_year = furi_hal_rtc_is_leap_year(datetime->year); + + for(uint8_t m = 1; m < datetime->month; m++) { + timestamp += furi_hal_rtc_get_days_per_month(leap_year, m) * FURI_HAL_RTC_SECONDS_PER_DAY; + } + + timestamp += (datetime->day - 1) * FURI_HAL_RTC_SECONDS_PER_DAY; + timestamp += datetime->hour * FURI_HAL_RTC_SECONDS_PER_HOUR; + timestamp += datetime->minute * FURI_HAL_RTC_SECONDS_PER_MINUTE; + timestamp += datetime->second; + + return timestamp; +} + +uint16_t furi_hal_rtc_get_days_per_year(uint16_t year) { + return furi_hal_rtc_days_per_year[furi_hal_rtc_is_leap_year(year) ? 1 : 0]; +} + +bool furi_hal_rtc_is_leap_year(uint16_t year) { + return (((year) % 4 == 0) && ((year) % 100 != 0)) || ((year) % 400 == 0); +} + +uint8_t furi_hal_rtc_get_days_per_month(bool leap_year, uint8_t month) { + return furi_hal_rtc_days_per_month[leap_year ? 1 : 0][month - 1]; +} diff --git a/targets/f7/furi_hal/furi_hal_sd.c b/targets/f7/furi_hal/furi_hal_sd.c new file mode 100644 index 00000000000..619f6f890df --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_sd.c @@ -0,0 +1,1095 @@ +#include +#include +#include +#include +#include "../fatfs/sector_cache.h" +#define TAG "SdSpi" + +#ifdef FURI_HAL_SD_SPI_DEBUG +#define sd_spi_debug(...) FURI_LOG_I(TAG, __VA_ARGS__) +#else +#define sd_spi_debug(...) +#endif + +#define SD_CMD_LENGTH (6) +#define SD_DUMMY_BYTE (0xFF) +#define SD_ANSWER_RETRY_COUNT (8) +#define SD_IDLE_RETRY_COUNT (100) +#define SD_TIMEOUT_MS (1000) +#define SD_BLOCK_SIZE (512) + +#define FLAG_SET(x, y) (((x) & (y)) == (y)) + +static bool sd_high_capacity = false; + +typedef enum { + SdSpiDataResponceOK = 0x05, + SdSpiDataResponceCRCError = 0x0B, + SdSpiDataResponceWriteError = 0x0D, + SdSpiDataResponceOtherError = 0xFF, +} SdSpiDataResponce; + +typedef struct { + uint8_t r1; + uint8_t r2; + uint8_t r3; + uint8_t r4; + uint8_t r5; +} SdSpiCmdAnswer; + +typedef enum { + SdSpiCmdAnswerTypeR1, + SdSpiCmdAnswerTypeR1B, + SdSpiCmdAnswerTypeR2, + SdSpiCmdAnswerTypeR3, + SdSpiCmdAnswerTypeR4R5, + SdSpiCmdAnswerTypeR7, +} SdSpiCmdAnswerType; + +/* + SdSpiCmd and SdSpiToken use non-standard enum value names convention, + because it is more convenient to look for documentation on a specific command. + For example, to find out what the SD_CMD23_SET_BLOCK_COUNT command does, you need to look for + SET_BLOCK_COUNT or CMD23 in the "Part 1 Physical Layer Simplified Specification". + + Do not use that naming convention in other places. +*/ + +typedef enum { + SD_CMD0_GO_IDLE_STATE = 0, + SD_CMD1_SEND_OP_COND = 1, + SD_CMD8_SEND_IF_COND = 8, + SD_CMD9_SEND_CSD = 9, + SD_CMD10_SEND_CID = 10, + SD_CMD12_STOP_TRANSMISSION = 12, + SD_CMD13_SEND_STATUS = 13, + SD_CMD16_SET_BLOCKLEN = 16, + SD_CMD17_READ_SINGLE_BLOCK = 17, + SD_CMD18_READ_MULT_BLOCK = 18, + SD_CMD23_SET_BLOCK_COUNT = 23, + SD_CMD24_WRITE_SINGLE_BLOCK = 24, + SD_CMD25_WRITE_MULT_BLOCK = 25, + SD_CMD27_PROG_CSD = 27, + SD_CMD28_SET_WRITE_PROT = 28, + SD_CMD29_CLR_WRITE_PROT = 29, + SD_CMD30_SEND_WRITE_PROT = 30, + SD_CMD32_SD_ERASE_GRP_START = 32, + SD_CMD33_SD_ERASE_GRP_END = 33, + SD_CMD34_UNTAG_SECTOR = 34, + SD_CMD35_ERASE_GRP_START = 35, + SD_CMD36_ERASE_GRP_END = 36, + SD_CMD37_UNTAG_ERASE_GROUP = 37, + SD_CMD38_ERASE = 38, + SD_CMD41_SD_APP_OP_COND = 41, + SD_CMD55_APP_CMD = 55, + SD_CMD58_READ_OCR = 58, +} SdSpiCmd; + +/** Data tokens */ +typedef enum { + SD_TOKEN_START_DATA_SINGLE_BLOCK_READ = 0xFE, + SD_TOKEN_START_DATA_MULTIPLE_BLOCK_READ = 0xFE, + SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE = 0xFE, + SD_TOKEN_START_DATA_MULTIPLE_BLOCK_WRITE = 0xFC, + SD_TOKEN_STOP_DATA_MULTIPLE_BLOCK_WRITE = 0xFD, +} SdSpiToken; + +/** R1 answer value */ +typedef enum { + SdSpi_R1_NO_ERROR = 0x00, + SdSpi_R1_IN_IDLE_STATE = 0x01, + SdSpi_R1_ERASE_RESET = 0x02, + SdSpi_R1_ILLEGAL_COMMAND = 0x04, + SdSpi_R1_COM_CRC_ERROR = 0x08, + SdSpi_R1_ERASE_SEQUENCE_ERROR = 0x10, + SdSpi_R1_ADDRESS_ERROR = 0x20, + SdSpi_R1_PARAMETER_ERROR = 0x40, +} SdSpiR1; + +/** R2 answer value */ +typedef enum { + /* R2 answer value */ + SdSpi_R2_NO_ERROR = 0x00, + SdSpi_R2_CARD_LOCKED = 0x01, + SdSpi_R2_LOCKUNLOCK_ERROR = 0x02, + SdSpi_R2_ERROR = 0x04, + SdSpi_R2_CC_ERROR = 0x08, + SdSpi_R2_CARD_ECC_FAILED = 0x10, + SdSpi_R2_WP_VIOLATION = 0x20, + SdSpi_R2_ERASE_PARAM = 0x40, + SdSpi_R2_OUTOFRANGE = 0x80, +} SdSpiR2; + +/** + * @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 MaxBusClkFreq : 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 { + 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 */ + } v1; + struct { + uint8_t Reserved1 : 6; /* Reserved */ + uint32_t DeviceSize : 22; /* Device Size */ + uint8_t Reserved2 : 1; /* Reserved */ + } 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 structure + */ +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; + +/** Pointer to currently used SPI Handle */ +FuriHalSpiBusHandle* furi_hal_sd_spi_handle = NULL; + +static inline void sd_spi_select_card() { + furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, false); + furi_delay_us(10); // Entry guard time for some SD cards +} + +static inline void sd_spi_deselect_card() { + furi_delay_us(10); // Exit guard time for some SD cards + furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true); +} + +static void sd_spi_bus_to_ground() { + 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); + + sd_spi_select_card(); + 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); +} + +static void sd_spi_bus_rise_up() { + sd_spi_deselect_card(); + + 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); +} + +static inline uint8_t sd_spi_read_byte(void) { + uint8_t responce; + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, NULL, &responce, 1, SD_TIMEOUT_MS)); + return responce; +} + +static inline void sd_spi_write_byte(uint8_t data) { + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, &data, NULL, 1, SD_TIMEOUT_MS)); +} + +static inline uint8_t sd_spi_write_and_read_byte(uint8_t data) { + uint8_t responce; + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, &data, &responce, 1, SD_TIMEOUT_MS)); + return responce; +} + +static inline void sd_spi_write_bytes(uint8_t* data, uint32_t size) { + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, data, NULL, size, SD_TIMEOUT_MS)); +} + +static inline void sd_spi_read_bytes(uint8_t* data, uint32_t size) { + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, NULL, data, size, SD_TIMEOUT_MS)); +} + +static inline void sd_spi_write_bytes_dma(uint8_t* data, uint32_t size) { + uint32_t timeout_mul = (size / 512) + 1; + furi_check(furi_hal_spi_bus_trx_dma( + furi_hal_sd_spi_handle, data, NULL, size, SD_TIMEOUT_MS * timeout_mul)); +} + +static inline void sd_spi_read_bytes_dma(uint8_t* data, uint32_t size) { + uint32_t timeout_mul = (size / 512) + 1; + furi_check(furi_hal_spi_bus_trx_dma( + furi_hal_sd_spi_handle, NULL, data, size, SD_TIMEOUT_MS * timeout_mul)); +} + +static uint8_t sd_spi_wait_for_data_and_read(void) { + uint8_t retry_count = SD_ANSWER_RETRY_COUNT; + uint8_t responce; + + // Wait until we get a valid data + do { + responce = sd_spi_read_byte(); + retry_count--; + + } while((responce == SD_DUMMY_BYTE) && retry_count); + + return responce; +} + +static FuriStatus sd_spi_wait_for_data(uint8_t data, uint32_t timeout_ms) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout_ms * 1000); + uint8_t byte; + + do { + byte = sd_spi_read_byte(); + if(furi_hal_cortex_timer_is_expired(timer)) { + return FuriStatusErrorTimeout; + } + } while((byte != data)); + + return FuriStatusOk; +} + +static inline void sd_spi_deselect_card_and_purge() { + sd_spi_deselect_card(); + sd_spi_read_byte(); +} + +static inline void sd_spi_purge_crc() { + sd_spi_read_byte(); + sd_spi_read_byte(); +} + +static SdSpiCmdAnswer + sd_spi_send_cmd(SdSpiCmd cmd, uint32_t arg, uint8_t crc, SdSpiCmdAnswerType answer_type) { + uint8_t frame[SD_CMD_LENGTH]; + SdSpiCmdAnswer cmd_answer = { + .r1 = SD_DUMMY_BYTE, + .r2 = SD_DUMMY_BYTE, + .r3 = SD_DUMMY_BYTE, + .r4 = SD_DUMMY_BYTE, + .r5 = SD_DUMMY_BYTE, + }; + + // R1 Length = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 1 Bytes answer + NEC(0) = 15bytes + // R1b identical to R1 + Busy information + // R2 Length = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 2 Bytes answer + NEC(0) = 16bytes + + frame[0] = ((uint8_t)cmd | 0x40); + frame[1] = (uint8_t)(arg >> 24); + frame[2] = (uint8_t)(arg >> 16); + frame[3] = (uint8_t)(arg >> 8); + frame[4] = (uint8_t)(arg); + frame[5] = (crc | 0x01); + + sd_spi_select_card(); + sd_spi_write_bytes(frame, sizeof(frame)); + + switch(answer_type) { + case SdSpiCmdAnswerTypeR1: + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + break; + case SdSpiCmdAnswerTypeR1B: + // TODO FL-3507: can be wrong, at least for SD_CMD12_STOP_TRANSMISSION you need to purge one byte before reading R1 + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + + // In general this shenenigans seems suspicious, please double check SD specs if you are using SdSpiCmdAnswerTypeR1B + // reassert card + sd_spi_deselect_card(); + furi_delay_us(1000); + sd_spi_deselect_card(); + + // and wait for it to be ready + while(sd_spi_read_byte() != 0xFF) { + }; + + break; + case SdSpiCmdAnswerTypeR2: + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + cmd_answer.r2 = sd_spi_read_byte(); + break; + case SdSpiCmdAnswerTypeR3: + case SdSpiCmdAnswerTypeR7: + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + cmd_answer.r2 = sd_spi_read_byte(); + cmd_answer.r3 = sd_spi_read_byte(); + cmd_answer.r4 = sd_spi_read_byte(); + cmd_answer.r5 = sd_spi_read_byte(); + break; + default: + break; + } + return cmd_answer; +} + +static SdSpiDataResponce sd_spi_get_data_response(uint32_t timeout_ms) { + SdSpiDataResponce responce = sd_spi_read_byte(); + // read busy response byte + sd_spi_read_byte(); + + switch(responce & 0x1F) { + case SdSpiDataResponceOK: + // TODO FL-3508: check timings + sd_spi_deselect_card(); + sd_spi_select_card(); + + // wait for 0xFF + if(sd_spi_wait_for_data(0xFF, timeout_ms) == FuriStatusOk) { + return SdSpiDataResponceOK; + } else { + return SdSpiDataResponceOtherError; + } + case SdSpiDataResponceCRCError: + return SdSpiDataResponceCRCError; + case SdSpiDataResponceWriteError: + return SdSpiDataResponceWriteError; + default: + return SdSpiDataResponceOtherError; + } +} + +static FuriStatus sd_spi_init_spi_mode_v1(void) { + SdSpiCmdAnswer response; + uint8_t retry_count = 0; + + sd_spi_debug("Init SD card in SPI mode v1"); + + do { + retry_count++; + + // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors) + sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + // ACMD41 (SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) + response = sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + return FuriStatusError; + } + } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); + + sd_spi_debug("Init SD card in SPI mode v1 done"); + + return FuriStatusOk; +} + +static FuriStatus sd_spi_init_spi_mode_v2(void) { + SdSpiCmdAnswer response; + uint8_t retry_count = 0; + + sd_spi_debug("Init SD card in SPI mode v2"); + + do { + retry_count++; + // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors) + sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + // ACMD41 (APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) + response = + sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0x40000000, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + sd_spi_debug("ACMD41 failed"); + return FuriStatusError; + } + } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); + + if(FLAG_SET(response.r1, SdSpi_R1_ILLEGAL_COMMAND)) { + sd_spi_debug("ACMD41 is illegal command"); + retry_count = 0; + do { + retry_count++; + // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors) + response = sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_IN_IDLE_STATE) { + sd_spi_debug("CMD55 failed"); + return FuriStatusError; + } + // ACMD41 (SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) + response = sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + sd_spi_debug("ACMD41 failed"); + return FuriStatusError; + } + } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); + } + + sd_spi_debug("Init SD card in SPI mode v2 done"); + + return FuriStatusOk; +} + +static FuriStatus sd_spi_init_spi_mode(void) { + SdSpiCmdAnswer response; + uint8_t retry_count; + + // CMD0 (GO_IDLE_STATE) to put SD in SPI mode and + // wait for In Idle State Response (R1 Format) equal to 0x01 + retry_count = 0; + do { + retry_count++; + response = sd_spi_send_cmd(SD_CMD0_GO_IDLE_STATE, 0, 0x95, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + sd_spi_debug("CMD0 failed"); + return FuriStatusError; + } + } while(response.r1 != SdSpi_R1_IN_IDLE_STATE); + + // CMD8 (SEND_IF_COND) to check the power supply status + // and wait until response (R7 Format) equal to 0xAA and + response = sd_spi_send_cmd(SD_CMD8_SEND_IF_COND, 0x1AA, 0x87, SdSpiCmdAnswerTypeR7); + sd_spi_deselect_card_and_purge(); + + if(FLAG_SET(response.r1, SdSpi_R1_ILLEGAL_COMMAND)) { + if(sd_spi_init_spi_mode_v1() != FuriStatusOk) { + sd_spi_debug("Init mode v1 failed"); + return FuriStatusError; + } + sd_high_capacity = 0; + } else if(response.r1 == SdSpi_R1_IN_IDLE_STATE) { + if(sd_spi_init_spi_mode_v2() != FuriStatusOk) { + sd_spi_debug("Init mode v2 failed"); + return FuriStatusError; + } + + // CMD58 (READ_OCR) to initialize SDHC or SDXC cards: R3 response + response = sd_spi_send_cmd(SD_CMD58_READ_OCR, 0, 0xFF, SdSpiCmdAnswerTypeR3); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_NO_ERROR) { + sd_spi_debug("CMD58 failed"); + return FuriStatusError; + } + sd_high_capacity = (response.r2 & 0x40) >> 6; + } else { + return FuriStatusError; + } + + sd_spi_debug("SD card is %s", sd_high_capacity ? "SDHC or SDXC" : "SDSC"); + return FuriStatusOk; +} + +static FuriStatus sd_spi_get_csd(SD_CSD* csd) { + uint16_t counter = 0; + uint8_t csd_data[16]; + FuriStatus ret = FuriStatusError; + SdSpiCmdAnswer response; + + // CMD9 (SEND_CSD): R1 format (0x00 is no errors) + response = sd_spi_send_cmd(SD_CMD9_SEND_CSD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + + if(response.r1 == SdSpi_R1_NO_ERROR) { + if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, SD_TIMEOUT_MS) == + FuriStatusOk) { + // read CSD data + for(counter = 0; counter < 16; counter++) { + csd_data[counter] = sd_spi_read_byte(); + } + + sd_spi_purge_crc(); + + /************************************************************************* + CSD header decoding + *************************************************************************/ + + csd->CSDStruct = (csd_data[0] & 0xC0) >> 6; + csd->Reserved1 = csd_data[0] & 0x3F; + csd->TAAC = csd_data[1]; + csd->NSAC = csd_data[2]; + csd->MaxBusClkFreq = csd_data[3]; + csd->CardComdClasses = (csd_data[4] << 4) | ((csd_data[5] & 0xF0) >> 4); + csd->RdBlockLen = csd_data[5] & 0x0F; + csd->PartBlockRead = (csd_data[6] & 0x80) >> 7; + csd->WrBlockMisalign = (csd_data[6] & 0x40) >> 6; + csd->RdBlockMisalign = (csd_data[6] & 0x20) >> 5; + csd->DSRImpl = (csd_data[6] & 0x10) >> 4; + + /************************************************************************* + CSD v1/v2 decoding + *************************************************************************/ + + if(sd_high_capacity == 0) { + csd->version.v1.Reserved1 = ((csd_data[6] & 0x0C) >> 2); + csd->version.v1.DeviceSize = ((csd_data[6] & 0x03) << 10) | (csd_data[7] << 2) | + ((csd_data[8] & 0xC0) >> 6); + csd->version.v1.MaxRdCurrentVDDMin = (csd_data[8] & 0x38) >> 3; + csd->version.v1.MaxRdCurrentVDDMax = (csd_data[8] & 0x07); + csd->version.v1.MaxWrCurrentVDDMin = (csd_data[9] & 0xE0) >> 5; + csd->version.v1.MaxWrCurrentVDDMax = (csd_data[9] & 0x1C) >> 2; + csd->version.v1.DeviceSizeMul = ((csd_data[9] & 0x03) << 1) | + ((csd_data[10] & 0x80) >> 7); + } else { + csd->version.v2.Reserved1 = ((csd_data[6] & 0x0F) << 2) | + ((csd_data[7] & 0xC0) >> 6); + csd->version.v2.DeviceSize = ((csd_data[7] & 0x3F) << 16) | (csd_data[8] << 8) | + csd_data[9]; + csd->version.v2.Reserved2 = ((csd_data[10] & 0x80) >> 8); + } + + csd->EraseSingleBlockEnable = (csd_data[10] & 0x40) >> 6; + csd->EraseSectorSize = ((csd_data[10] & 0x3F) << 1) | ((csd_data[11] & 0x80) >> 7); + csd->WrProtectGrSize = (csd_data[11] & 0x7F); + csd->WrProtectGrEnable = (csd_data[12] & 0x80) >> 7; + csd->Reserved2 = (csd_data[12] & 0x60) >> 5; + csd->WrSpeedFact = (csd_data[12] & 0x1C) >> 2; + csd->MaxWrBlockLen = ((csd_data[12] & 0x03) << 2) | ((csd_data[13] & 0xC0) >> 6); + csd->WriteBlockPartial = (csd_data[13] & 0x20) >> 5; + csd->Reserved3 = (csd_data[13] & 0x1F); + csd->FileFormatGrouop = (csd_data[14] & 0x80) >> 7; + csd->CopyFlag = (csd_data[14] & 0x40) >> 6; + csd->PermWrProtect = (csd_data[14] & 0x20) >> 5; + csd->TempWrProtect = (csd_data[14] & 0x10) >> 4; + csd->FileFormat = (csd_data[14] & 0x0C) >> 2; + csd->Reserved4 = (csd_data[14] & 0x03); + csd->crc = (csd_data[15] & 0xFE) >> 1; + csd->Reserved5 = (csd_data[15] & 0x01); + + ret = FuriStatusOk; + } + } + + sd_spi_deselect_card_and_purge(); + + return ret; +} + +static FuriStatus sd_spi_get_cid(SD_CID* Cid) { + uint16_t counter = 0; + uint8_t cid_data[16]; + FuriStatus ret = FuriStatusError; + SdSpiCmdAnswer response; + + // CMD10 (SEND_CID): R1 format (0x00 is no errors) + response = sd_spi_send_cmd(SD_CMD10_SEND_CID, 0, 0xFF, SdSpiCmdAnswerTypeR1); + + if(response.r1 == SdSpi_R1_NO_ERROR) { + if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, SD_TIMEOUT_MS) == + FuriStatusOk) { + // read CID data + for(counter = 0; counter < 16; counter++) { + cid_data[counter] = sd_spi_read_byte(); + } + + sd_spi_purge_crc(); + + Cid->ManufacturerID = cid_data[0]; + memcpy(Cid->OEM_AppliID, cid_data + 1, 2); + memcpy(Cid->ProdName, cid_data + 3, 5); + Cid->ProdRev = cid_data[8]; + Cid->ProdSN = cid_data[9] << 24; + Cid->ProdSN |= cid_data[10] << 16; + Cid->ProdSN |= cid_data[11] << 8; + 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; + + ret = FuriStatusOk; + } + } + + sd_spi_deselect_card_and_purge(); + + return ret; +} + +static FuriStatus + sd_spi_cmd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { + uint32_t block_address = address; + uint32_t offset = 0; + + // CMD16 (SET_BLOCKLEN): R1 response (0x00: no errors) + SdSpiCmdAnswer response = + sd_spi_send_cmd(SD_CMD16_SET_BLOCKLEN, SD_BLOCK_SIZE, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_NO_ERROR) { + return FuriStatusError; + } + + if(!sd_high_capacity) { + block_address = address * SD_BLOCK_SIZE; + } + + while(blocks--) { + // CMD17 (READ_SINGLE_BLOCK): R1 response (0x00: no errors) + response = + sd_spi_send_cmd(SD_CMD17_READ_SINGLE_BLOCK, block_address, 0xFF, SdSpiCmdAnswerTypeR1); + if(response.r1 != SdSpi_R1_NO_ERROR) { + sd_spi_deselect_card_and_purge(); + return FuriStatusError; + } + + // Wait for the data start token + if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, timeout_ms) == + FuriStatusOk) { + // Read the data block + sd_spi_read_bytes_dma((uint8_t*)data + offset, SD_BLOCK_SIZE); + sd_spi_purge_crc(); + + // increase offset + offset += SD_BLOCK_SIZE; + + // increase block address + if(sd_high_capacity) { + block_address += 1; + } else { + block_address += SD_BLOCK_SIZE; + } + } else { + sd_spi_deselect_card_and_purge(); + return FuriStatusError; + } + + sd_spi_deselect_card_and_purge(); + } + + return FuriStatusOk; +} + +static FuriStatus sd_spi_cmd_write_blocks( + const uint32_t* data, + uint32_t address, + uint32_t blocks, + uint32_t timeout_ms) { + uint32_t block_address = address; + uint32_t offset = 0; + + // CMD16 (SET_BLOCKLEN): R1 response (0x00: no errors) + SdSpiCmdAnswer response = + sd_spi_send_cmd(SD_CMD16_SET_BLOCKLEN, SD_BLOCK_SIZE, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_NO_ERROR) { + return FuriStatusError; + } + + if(!sd_high_capacity) { + block_address = address * SD_BLOCK_SIZE; + } + + while(blocks--) { + // CMD24 (WRITE_SINGLE_BLOCK): R1 response (0x00: no errors) + response = sd_spi_send_cmd( + SD_CMD24_WRITE_SINGLE_BLOCK, block_address, 0xFF, SdSpiCmdAnswerTypeR1); + if(response.r1 != SdSpi_R1_NO_ERROR) { + sd_spi_deselect_card_and_purge(); + return FuriStatusError; + } + + // Send dummy byte for NWR timing : one byte between CMD_WRITE and TOKEN + // TODO FL-3509: check bytes count + sd_spi_write_byte(SD_DUMMY_BYTE); + sd_spi_write_byte(SD_DUMMY_BYTE); + + // Send the data start token + sd_spi_write_byte(SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE); + sd_spi_write_bytes_dma((uint8_t*)data + offset, SD_BLOCK_SIZE); + sd_spi_purge_crc(); + + // Read data response + SdSpiDataResponce data_responce = sd_spi_get_data_response(timeout_ms); + sd_spi_deselect_card_and_purge(); + + if(data_responce != SdSpiDataResponceOK) { + return FuriStatusError; + } + + // increase offset + offset += SD_BLOCK_SIZE; + + // increase block address + if(sd_high_capacity) { + block_address += 1; + } else { + block_address += SD_BLOCK_SIZE; + } + } + + return FuriStatusOk; +} + +static FuriStatus sd_spi_get_card_state(void) { + SdSpiCmdAnswer response; + + // Send CMD13 (SEND_STATUS) to get SD status + response = sd_spi_send_cmd(SD_CMD13_SEND_STATUS, 0, 0xFF, SdSpiCmdAnswerTypeR2); + sd_spi_deselect_card_and_purge(); + + // Return status OK if response is valid + if((response.r1 == SdSpi_R1_NO_ERROR) && (response.r2 == SdSpi_R2_NO_ERROR)) { + return FuriStatusOk; + } + + return FuriStatusError; +} + +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(); +} + +static FuriStatus sd_device_read(uint32_t* buff, uint32_t sector, uint32_t count) { + FuriStatus status = FuriStatusError; + + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); + furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; + + if(sd_spi_cmd_read_blocks(buff, sector, count, SD_TIMEOUT_MS) == FuriStatusOk) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000); + + /* wait until the read operation is finished */ + do { + status = sd_spi_get_card_state(); + + if(furi_hal_cortex_timer_is_expired(timer)) { + status = FuriStatusErrorTimeout; + break; + } + } while(status != FuriStatusOk); + } + + furi_hal_sd_spi_handle = NULL; + furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); + + return status; +} + +static FuriStatus sd_device_write(const uint32_t* buff, uint32_t sector, uint32_t count) { + FuriStatus status = FuriStatusError; + + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); + furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; + + if(sd_spi_cmd_write_blocks(buff, sector, count, SD_TIMEOUT_MS) == FuriStatusOk) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000); + + /* wait until the Write operation is finished */ + do { + status = sd_spi_get_card_state(); + + if(furi_hal_cortex_timer_is_expired(timer)) { + sd_cache_invalidate_all(); + + status = FuriStatusErrorTimeout; + break; + } + } while(status != FuriStatusOk); + } + + furi_hal_sd_spi_handle = NULL; + furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); + + return status; +} + +void furi_hal_sd_presence_init(void) { + // low speed input with pullup + furi_hal_gpio_init(&gpio_sdcard_cd, GpioModeInput, GpioPullUp, GpioSpeedLow); +} + +static void furi_hal_sd_present_pin_set_low(void) { + // low speed input with pullup + furi_hal_gpio_init_simple(&gpio_sdcard_cd, GpioModeOutputOpenDrain); + furi_hal_gpio_write(&gpio_sdcard_cd, 0); +} + +bool furi_hal_sd_is_present(void) { + bool result = !furi_hal_gpio_read(&gpio_sdcard_cd); + return result; +} + +uint8_t furi_hal_sd_max_mount_retry_count() { + return 10; +} + +FuriStatus furi_hal_sd_init(bool power_reset) { + // 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 reset card in spi_lock context, so it is safe to disturb spi bus + if(power_reset) { + sd_spi_debug("Power reset"); + + // disable power and set low on all bus pins + furi_hal_power_disable_external_3_3v(); + sd_spi_bus_to_ground(); + furi_hal_sd_present_pin_set_low(); + furi_delay_ms(250); + + // reinit bus and enable power + sd_spi_bus_rise_up(); + furi_hal_sd_presence_init(); + furi_hal_power_enable_external_3_3v(); + furi_delay_ms(100); + } + + FuriStatus status = FuriStatusError; + + // Send 80 dummy clocks with CS high + sd_spi_deselect_card(); + for(uint8_t i = 0; i < 80; i++) { + sd_spi_write_byte(SD_DUMMY_BYTE); + } + + for(uint8_t i = 0; i < 128; i++) { + status = sd_spi_init_spi_mode(); + if(status == FuriStatusOk) { + // SD initialized and init to SPI mode properly + sd_spi_debug("SD init OK after %d retries", i); + break; + } + } + + furi_hal_sd_spi_handle = NULL; + furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow); + + // Init sector cache + sector_cache_init(); + + return status; +} + +FuriStatus furi_hal_sd_get_card_state(void) { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); + furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; + + FuriStatus status = sd_spi_get_card_state(); + + furi_hal_sd_spi_handle = NULL; + furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); + + return status; +} + +FuriStatus furi_hal_sd_read_blocks(uint32_t* buff, uint32_t sector, uint32_t count) { + FuriStatus status; + bool single_sector = count == 1; + + if(single_sector) { + if(sd_cache_get(sector, buff)) { + return FuriStatusOk; + } + } + + status = sd_device_read(buff, sector, count); + + if(status != FuriStatusOk) { + uint8_t counter = furi_hal_sd_max_mount_retry_count(); + + while(status != FuriStatusOk && counter > 0 && furi_hal_sd_is_present()) { + if((counter % 2) == 0) { + // power reset sd card + status = furi_hal_sd_init(true); + } else { + status = furi_hal_sd_init(false); + } + + if(status == FuriStatusOk) { + status = sd_device_read(buff, sector, count); + } + counter--; + } + } + + if(single_sector && status == FuriStatusOk) { + sd_cache_put(sector, buff); + } + + return status; +} + +FuriStatus furi_hal_sd_write_blocks(const uint32_t* buff, uint32_t sector, uint32_t count) { + FuriStatus status; + + sd_cache_invalidate_range(sector, sector + count); + + status = sd_device_write(buff, sector, count); + + if(status != FuriStatusOk) { + uint8_t counter = furi_hal_sd_max_mount_retry_count(); + + while(status != FuriStatusOk && counter > 0 && furi_hal_sd_is_present()) { + if((counter % 2) == 0) { + // power reset sd card + status = furi_hal_sd_init(true); + } else { + status = furi_hal_sd_init(false); + } + + if(status == FuriStatusOk) { + status = sd_device_write(buff, sector, count); + } + counter--; + } + } + + return status; +} + +FuriStatus furi_hal_sd_info(FuriHalSdInfo* info) { + FuriStatus status; + SD_CSD csd; + SD_CID cid; + + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); + furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; + + do { + status = sd_spi_get_csd(&csd); + + if(status != FuriStatusOk) { + break; + } + + status = sd_spi_get_cid(&cid); + + if(status != FuriStatusOk) { + break; + } + + if(sd_high_capacity == 1) { + info->logical_block_size = 512; + info->block_size = 512; + info->capacity = ((uint64_t)csd.version.v2.DeviceSize + 1UL) * 1024UL * + (uint64_t)info->logical_block_size; + info->logical_block_count = (info->capacity) / (info->logical_block_size); + } else { + info->capacity = (csd.version.v1.DeviceSize + 1); + info->capacity *= (1UL << (csd.version.v1.DeviceSizeMul + 2)); + info->logical_block_size = 512; + info->block_size = 1UL << (csd.RdBlockLen); + info->capacity *= info->block_size; + info->logical_block_count = (info->capacity) / (info->logical_block_size); + } + + info->manufacturer_id = cid.ManufacturerID; + + memcpy(info->oem_id, cid.OEM_AppliID, sizeof(info->oem_id) - 1); + info->oem_id[sizeof(info->oem_id) - 1] = '\0'; + + memcpy(info->product_name, cid.ProdName, sizeof(info->product_name) - 1); + info->product_name[sizeof(info->product_name) - 1] = '\0'; + + info->product_revision_major = cid.ProdRev >> 4; + info->product_revision_minor = cid.ProdRev & 0x0F; + info->product_serial_number = cid.ProdSN; + info->manufacturing_year = 2000 + cid.ManufactYear; + info->manufacturing_month = cid.ManufactMonth; + + } while(false); + + furi_hal_sd_spi_handle = NULL; + furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); + + return status; +} \ No newline at end of file diff --git a/targets/f7/furi_hal/furi_hal_speaker.c b/targets/f7/furi_hal/furi_hal_speaker.c new file mode 100644 index 00000000000..ad7ed994af2 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_speaker.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include + +#include +#include + +#define TAG "FuriHalSpeaker" + +#define FURI_HAL_SPEAKER_TIMER TIM16 +#define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1 +#define FURI_HAL_SPEAKER_PRESCALER 500 +#define FURI_HAL_SPEAKER_MAX_VOLUME 60 + +static FuriMutex* furi_hal_speaker_mutex = NULL; + +// #define FURI_HAL_SPEAKER_NEW_VOLUME + +void furi_hal_speaker_init() { + furi_assert(furi_hal_speaker_mutex == NULL); + furi_hal_speaker_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + FURI_LOG_I(TAG, "Init OK"); +} + +void furi_hal_speaker_deinit() { + furi_check(furi_hal_speaker_mutex != NULL); + furi_mutex_free(furi_hal_speaker_mutex); + furi_hal_speaker_mutex = NULL; +} + +bool furi_hal_speaker_acquire(uint32_t timeout) { + furi_check(!FURI_IS_IRQ_MODE()); + + if(furi_mutex_acquire(furi_hal_speaker_mutex, timeout) == FuriStatusOk) { + furi_hal_power_insomnia_enter(); + furi_hal_bus_enable(FuriHalBusTIM16); + furi_hal_gpio_init_ex( + &gpio_speaker, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn14TIM16); + return true; + } else { + return false; + } +} + +void furi_hal_speaker_release() { + furi_check(!FURI_IS_IRQ_MODE()); + furi_check(furi_hal_speaker_is_mine()); + + furi_hal_speaker_stop(); + furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullDown, GpioSpeedLow); + + furi_hal_bus_disable(FuriHalBusTIM16); + furi_hal_power_insomnia_exit(); + + furi_check(furi_mutex_release(furi_hal_speaker_mutex) == FuriStatusOk); +} + +bool furi_hal_speaker_is_mine() { + return (FURI_IS_IRQ_MODE()) || + (furi_mutex_get_owner(furi_hal_speaker_mutex) == furi_thread_get_current_id()); +} + +static inline uint32_t furi_hal_speaker_calculate_autoreload(float frequency) { + uint32_t autoreload = (SystemCoreClock / FURI_HAL_SPEAKER_PRESCALER / frequency) - 1; + if(autoreload < 2) { + autoreload = 2; + } else if(autoreload > UINT16_MAX) { + autoreload = UINT16_MAX; + } + + return autoreload; +} + +static inline uint32_t furi_hal_speaker_calculate_compare(float volume) { + if(volume < 0) volume = 0; + if(volume > 1) volume = 1; + volume = volume * volume * volume; + +#ifdef FURI_HAL_SPEAKER_NEW_VOLUME + uint32_t compare_value = volume * FURI_HAL_SPEAKER_MAX_VOLUME; + uint32_t clip_value = volume * LL_TIM_GetAutoReload(FURI_HAL_SPEAKER_TIMER) / 2; + if(compare_value > clip_value) { + compare_value = clip_value; + } +#else + uint32_t compare_value = volume * LL_TIM_GetAutoReload(FURI_HAL_SPEAKER_TIMER) / 2; +#endif + + if(compare_value == 0) { + compare_value = 1; + } + + return compare_value; +} + +void furi_hal_speaker_start(float frequency, float volume) { + furi_check(furi_hal_speaker_is_mine()); + + if(volume <= 0) { + furi_hal_speaker_stop(); + return; + } + + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + TIM_InitStruct.Prescaler = FURI_HAL_SPEAKER_PRESCALER - 1; + TIM_InitStruct.Autoreload = furi_hal_speaker_calculate_autoreload(frequency); + LL_TIM_Init(FURI_HAL_SPEAKER_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 = furi_hal_speaker_calculate_compare(volume); + LL_TIM_OC_Init(FURI_HAL_SPEAKER_TIMER, FURI_HAL_SPEAKER_CHANNEL, &TIM_OC_InitStruct); + + LL_TIM_EnableAllOutputs(FURI_HAL_SPEAKER_TIMER); + LL_TIM_EnableCounter(FURI_HAL_SPEAKER_TIMER); +} + +void furi_hal_speaker_set_volume(float volume) { + furi_check(furi_hal_speaker_is_mine()); + if(volume <= 0) { + furi_hal_speaker_stop(); + return; + } + +#if FURI_HAL_SPEAKER_CHANNEL == LL_TIM_CHANNEL_CH1 + LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, furi_hal_speaker_calculate_compare(volume)); +#else +#error Invalid channel +#endif +} + +void furi_hal_speaker_stop() { + furi_check(furi_hal_speaker_is_mine()); + LL_TIM_DisableAllOutputs(FURI_HAL_SPEAKER_TIMER); + LL_TIM_DisableCounter(FURI_HAL_SPEAKER_TIMER); +} diff --git a/targets/f7/furi_hal/furi_hal_spi.c b/targets/f7/furi_hal/furi_hal_spi.c new file mode 100644 index 00000000000..98ca71af357 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_spi.c @@ -0,0 +1,376 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define TAG "FuriHalSpi" + +#define SPI_DMA DMA2 +#define SPI_DMA_RX_CHANNEL LL_DMA_CHANNEL_6 +#define SPI_DMA_TX_CHANNEL LL_DMA_CHANNEL_7 +#define SPI_DMA_RX_IRQ FuriHalInterruptIdDma2Ch6 +#define SPI_DMA_TX_IRQ FuriHalInterruptIdDma2Ch7 +#define SPI_DMA_RX_DEF SPI_DMA, SPI_DMA_RX_CHANNEL +#define SPI_DMA_TX_DEF SPI_DMA, SPI_DMA_TX_CHANNEL + +// For simplicity, I assume that only one SPI DMA transaction can occur at a time. +static FuriSemaphore* spi_dma_lock = NULL; +static FuriSemaphore* spi_dma_completed = NULL; + +void furi_hal_spi_dma_init() { + spi_dma_lock = furi_semaphore_alloc(1, 1); + spi_dma_completed = furi_semaphore_alloc(1, 1); +} + +void furi_hal_spi_bus_init(FuriHalSpiBus* bus) { + furi_assert(bus); + bus->callback(bus, FuriHalSpiBusEventInit); +} + +void furi_hal_spi_bus_deinit(FuriHalSpiBus* bus) { + furi_assert(bus); + bus->callback(bus, FuriHalSpiBusEventDeinit); +} + +void furi_hal_spi_bus_handle_init(FuriHalSpiBusHandle* handle) { + furi_assert(handle); + handle->callback(handle, FuriHalSpiBusHandleEventInit); +} + +void furi_hal_spi_bus_handle_deinit(FuriHalSpiBusHandle* handle) { + furi_assert(handle); + handle->callback(handle, FuriHalSpiBusHandleEventDeinit); +} + +void furi_hal_spi_acquire(FuriHalSpiBusHandle* handle) { + furi_assert(handle); + + furi_hal_power_insomnia_enter(); + + handle->bus->callback(handle->bus, FuriHalSpiBusEventLock); + handle->bus->callback(handle->bus, FuriHalSpiBusEventActivate); + + furi_assert(handle->bus->current_handle == NULL); + + handle->bus->current_handle = handle; + handle->callback(handle, FuriHalSpiBusHandleEventActivate); +} + +void furi_hal_spi_release(FuriHalSpiBusHandle* handle) { + furi_assert(handle); + furi_assert(handle->bus->current_handle == handle); + + // Handle event and unset handle + handle->callback(handle, FuriHalSpiBusHandleEventDeactivate); + handle->bus->current_handle = NULL; + + // Bus events + handle->bus->callback(handle->bus, FuriHalSpiBusEventDeactivate); + handle->bus->callback(handle->bus, FuriHalSpiBusEventUnlock); + + furi_hal_power_insomnia_exit(); +} + +static void furi_hal_spi_bus_end_txrx(FuriHalSpiBusHandle* handle, uint32_t timeout) { + UNUSED(timeout); // FIXME + while(LL_SPI_GetTxFIFOLevel(handle->bus->spi) != LL_SPI_TX_FIFO_EMPTY) + ; + while(LL_SPI_IsActiveFlag_BSY(handle->bus->spi)) + ; + while(LL_SPI_GetRxFIFOLevel(handle->bus->spi) != LL_SPI_RX_FIFO_EMPTY) { + LL_SPI_ReceiveData8(handle->bus->spi); + } +} + +bool furi_hal_spi_bus_rx( + FuriHalSpiBusHandle* handle, + uint8_t* buffer, + size_t size, + uint32_t timeout) { + furi_assert(handle); + furi_assert(handle->bus->current_handle == handle); + furi_assert(buffer); + furi_assert(size > 0); + + return furi_hal_spi_bus_trx(handle, buffer, buffer, size, timeout); +} + +bool furi_hal_spi_bus_tx( + FuriHalSpiBusHandle* handle, + const uint8_t* buffer, + size_t size, + uint32_t timeout) { + furi_assert(handle); + furi_assert(handle->bus->current_handle == handle); + furi_assert(buffer); + furi_assert(size > 0); + bool ret = true; + + while(size > 0) { + if(LL_SPI_IsActiveFlag_TXE(handle->bus->spi)) { + LL_SPI_TransmitData8(handle->bus->spi, *buffer); + buffer++; + size--; + } + } + + furi_hal_spi_bus_end_txrx(handle, timeout); + LL_SPI_ClearFlag_OVR(handle->bus->spi); + + return ret; +} + +bool furi_hal_spi_bus_trx( + FuriHalSpiBusHandle* handle, + const uint8_t* tx_buffer, + uint8_t* rx_buffer, + size_t size, + uint32_t timeout) { + furi_assert(handle); + furi_assert(handle->bus->current_handle == handle); + furi_assert(size > 0); + + bool ret = true; + size_t tx_size = size; + bool tx_allowed = true; + + while(size > 0) { + if(tx_size > 0 && LL_SPI_IsActiveFlag_TXE(handle->bus->spi) && tx_allowed) { + if(tx_buffer) { + LL_SPI_TransmitData8(handle->bus->spi, *tx_buffer); + tx_buffer++; + } else { + LL_SPI_TransmitData8(handle->bus->spi, 0xFF); + } + tx_size--; + tx_allowed = false; + } + + if(LL_SPI_IsActiveFlag_RXNE(handle->bus->spi)) { + if(rx_buffer) { + *rx_buffer = LL_SPI_ReceiveData8(handle->bus->spi); + rx_buffer++; + } else { + LL_SPI_ReceiveData8(handle->bus->spi); + } + size--; + tx_allowed = true; + } + } + + furi_hal_spi_bus_end_txrx(handle, timeout); + + return ret; +} + +static void spi_dma_isr() { +#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_6 + if(LL_DMA_IsActiveFlag_TC6(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_RX_DEF)) { + LL_DMA_ClearFlag_TC6(SPI_DMA); + furi_check(furi_semaphore_release(spi_dma_completed) == FuriStatusOk); + } +#else +#error Update this code. Would you kindly? +#endif + +#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_7 + if(LL_DMA_IsActiveFlag_TC7(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_TX_DEF)) { + LL_DMA_ClearFlag_TC7(SPI_DMA); + furi_check(furi_semaphore_release(spi_dma_completed) == FuriStatusOk); + } +#else +#error Update this code. Would you kindly? +#endif +} + +bool furi_hal_spi_bus_trx_dma( + FuriHalSpiBusHandle* handle, + uint8_t* tx_buffer, + uint8_t* rx_buffer, + size_t size, + uint32_t timeout_ms) { + furi_assert(handle); + furi_assert(handle->bus->current_handle == handle); + furi_assert(size > 0); + + // If scheduler is not running, use blocking mode + if(furi_kernel_is_running()) { + return furi_hal_spi_bus_trx(handle, tx_buffer, rx_buffer, size, timeout_ms); + } + + // Lock DMA + furi_check(furi_semaphore_acquire(spi_dma_lock, FuriWaitForever) == FuriStatusOk); + + const uint32_t dma_dummy_u32 = 0xFFFFFFFF; + + bool ret = true; + SPI_TypeDef* spi = handle->bus->spi; + uint32_t dma_rx_req; + uint32_t dma_tx_req; + + if(spi == SPI1) { + dma_rx_req = LL_DMAMUX_REQ_SPI1_RX; + dma_tx_req = LL_DMAMUX_REQ_SPI1_TX; + } else if(spi == SPI2) { + dma_rx_req = LL_DMAMUX_REQ_SPI2_RX; + dma_tx_req = LL_DMAMUX_REQ_SPI2_TX; + } else { + furi_crash(); + } + + if(rx_buffer == NULL) { + // Only TX mode, do not use RX channel + + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)tx_buffer; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; + dma_config.NbData = size; + dma_config.PeriphRequest = dma_tx_req; + dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; + LL_DMA_Init(SPI_DMA_TX_DEF, &dma_config); + +#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_7 + LL_DMA_ClearFlag_TC7(SPI_DMA); +#else +#error Update this code. Would you kindly? +#endif + + furi_hal_interrupt_set_isr(SPI_DMA_TX_IRQ, spi_dma_isr, NULL); + + bool dma_tx_was_enabled = LL_SPI_IsEnabledDMAReq_TX(spi); + if(!dma_tx_was_enabled) { + LL_SPI_EnableDMAReq_TX(spi); + } + + // acquire semaphore before enabling DMA + furi_check(furi_semaphore_acquire(spi_dma_completed, timeout_ms) == FuriStatusOk); + + LL_DMA_EnableIT_TC(SPI_DMA_TX_DEF); + LL_DMA_EnableChannel(SPI_DMA_TX_DEF); + + // and wait for it to be released (DMA transfer complete) + if(furi_semaphore_acquire(spi_dma_completed, timeout_ms) != FuriStatusOk) { + ret = false; + FURI_LOG_E(TAG, "DMA timeout\r\n"); + } + // release semaphore, because we are using it as a flag + furi_semaphore_release(spi_dma_completed); + + LL_DMA_DisableIT_TC(SPI_DMA_TX_DEF); + LL_DMA_DisableChannel(SPI_DMA_TX_DEF); + if(!dma_tx_was_enabled) { + LL_SPI_DisableDMAReq_TX(spi); + } + furi_hal_interrupt_set_isr(SPI_DMA_TX_IRQ, NULL, NULL); + + LL_DMA_DeInit(SPI_DMA_TX_DEF); + } else { + // TRX or RX mode, use both channels + uint32_t tx_mem_increase_mode; + + if(tx_buffer == NULL) { + // RX mode, use dummy data instead of TX buffer + tx_buffer = (uint8_t*)&dma_dummy_u32; + tx_mem_increase_mode = LL_DMA_MEMORY_NOINCREMENT; + } else { + tx_mem_increase_mode = LL_DMA_MEMORY_INCREMENT; + } + + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)tx_buffer; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = tx_mem_increase_mode; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; + dma_config.NbData = size; + dma_config.PeriphRequest = dma_tx_req; + dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; + LL_DMA_Init(SPI_DMA_TX_DEF, &dma_config); + + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)rx_buffer; + dma_config.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; + dma_config.NbData = size; + dma_config.PeriphRequest = dma_rx_req; + dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; + LL_DMA_Init(SPI_DMA_RX_DEF, &dma_config); + +#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_6 + LL_DMA_ClearFlag_TC6(SPI_DMA); +#else +#error Update this code. Would you kindly? +#endif + + furi_hal_interrupt_set_isr(SPI_DMA_RX_IRQ, spi_dma_isr, NULL); + + bool dma_tx_was_enabled = LL_SPI_IsEnabledDMAReq_TX(spi); + bool dma_rx_was_enabled = LL_SPI_IsEnabledDMAReq_RX(spi); + + if(!dma_tx_was_enabled) { + LL_SPI_EnableDMAReq_TX(spi); + } + + if(!dma_rx_was_enabled) { + LL_SPI_EnableDMAReq_RX(spi); + } + + // acquire semaphore before enabling DMA + furi_check(furi_semaphore_acquire(spi_dma_completed, timeout_ms) == FuriStatusOk); + + LL_DMA_EnableIT_TC(SPI_DMA_RX_DEF); + LL_DMA_EnableChannel(SPI_DMA_RX_DEF); + LL_DMA_EnableChannel(SPI_DMA_TX_DEF); + + // and wait for it to be released (DMA transfer complete) + if(furi_semaphore_acquire(spi_dma_completed, timeout_ms) != FuriStatusOk) { + ret = false; + FURI_LOG_E(TAG, "DMA timeout\r\n"); + } + // release semaphore, because we are using it as a flag + furi_semaphore_release(spi_dma_completed); + + LL_DMA_DisableIT_TC(SPI_DMA_RX_DEF); + + LL_DMA_DisableChannel(SPI_DMA_TX_DEF); + LL_DMA_DisableChannel(SPI_DMA_RX_DEF); + + if(!dma_tx_was_enabled) { + LL_SPI_DisableDMAReq_TX(spi); + } + + if(!dma_rx_was_enabled) { + LL_SPI_DisableDMAReq_RX(spi); + } + + furi_hal_interrupt_set_isr(SPI_DMA_RX_IRQ, NULL, NULL); + + LL_DMA_DeInit(SPI_DMA_TX_DEF); + LL_DMA_DeInit(SPI_DMA_RX_DEF); + } + + furi_hal_spi_bus_end_txrx(handle, timeout_ms); + + furi_check(furi_semaphore_release(spi_dma_lock) == FuriStatusOk); + + return ret; +} diff --git a/targets/f7/furi_hal/furi_hal_spi_config.c b/targets/f7/furi_hal/furi_hal_spi_config.c new file mode 100644 index 00000000000..757ac23661d --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_spi_config.c @@ -0,0 +1,437 @@ +#include +#include +#include +#include +#include + +#define TAG "FuriHalSpiConfig" + +/* SPI Presets */ + +const LL_SPI_InitTypeDef furi_hal_spi_preset_2edge_low_8m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_2EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_8m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_1EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_4m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_1EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV16, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_16m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_1EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_2m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_1EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV32, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +/* SPI Buses */ + +FuriMutex* furi_hal_spi_bus_r_mutex = NULL; + +void furi_hal_spi_config_init_early() { + furi_hal_spi_bus_init(&furi_hal_spi_bus_d); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_display); +} + +void furi_hal_spi_config_deinit_early() { + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_display); + furi_hal_spi_bus_deinit(&furi_hal_spi_bus_d); +} + +void furi_hal_spi_config_init() { + furi_hal_spi_bus_init(&furi_hal_spi_bus_r); + + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_fast); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_slow); + + FURI_LOG_I(TAG, "Init OK"); +} + +static void furi_hal_spi_bus_r_event_callback(FuriHalSpiBus* bus, FuriHalSpiBusEvent event) { + if(event == FuriHalSpiBusEventInit) { + furi_hal_spi_bus_r_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + bus->current_handle = NULL; + } else if(event == FuriHalSpiBusEventDeinit) { + furi_mutex_free(furi_hal_spi_bus_r_mutex); + } else if(event == FuriHalSpiBusEventLock) { + furi_check(furi_mutex_acquire(furi_hal_spi_bus_r_mutex, FuriWaitForever) == FuriStatusOk); + } else if(event == FuriHalSpiBusEventUnlock) { + furi_check(furi_mutex_release(furi_hal_spi_bus_r_mutex) == FuriStatusOk); + } else if(event == FuriHalSpiBusEventActivate) { + furi_hal_bus_enable(FuriHalBusSPI1); + } else if(event == FuriHalSpiBusEventDeactivate) { + furi_hal_bus_disable(FuriHalBusSPI1); + } +} + +FuriHalSpiBus furi_hal_spi_bus_r = { + .spi = SPI1, + .callback = furi_hal_spi_bus_r_event_callback, +}; + +FuriMutex* furi_hal_spi_bus_d_mutex = NULL; + +static void furi_hal_spi_bus_d_event_callback(FuriHalSpiBus* bus, FuriHalSpiBusEvent event) { + if(event == FuriHalSpiBusEventInit) { + furi_hal_spi_bus_d_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + bus->current_handle = NULL; + } else if(event == FuriHalSpiBusEventDeinit) { + furi_mutex_free(furi_hal_spi_bus_d_mutex); + } else if(event == FuriHalSpiBusEventLock) { + furi_check(furi_mutex_acquire(furi_hal_spi_bus_d_mutex, FuriWaitForever) == FuriStatusOk); + } else if(event == FuriHalSpiBusEventUnlock) { + furi_check(furi_mutex_release(furi_hal_spi_bus_d_mutex) == FuriStatusOk); + } else if(event == FuriHalSpiBusEventActivate) { + furi_hal_bus_enable(FuriHalBusSPI2); + } else if(event == FuriHalSpiBusEventDeactivate) { + furi_hal_bus_disable(FuriHalBusSPI2); + } +} + +FuriHalSpiBus furi_hal_spi_bus_d = { + .spi = SPI2, + .callback = furi_hal_spi_bus_d_event_callback, +}; + +/* SPI Bus Handles */ + +inline static void furi_hal_spi_bus_r_handle_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event, + const LL_SPI_InitTypeDef* preset) { + if(event == FuriHalSpiBusHandleEventInit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else if(event == FuriHalSpiBusHandleEventDeinit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } else if(event == FuriHalSpiBusHandleEventActivate) { + LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset); + LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER); + LL_SPI_Enable(handle->bus->spi); + + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + + furi_hal_gpio_write(handle->cs, false); + } else if(event == FuriHalSpiBusHandleEventDeactivate) { + furi_hal_gpio_write(handle->cs, true); + + furi_hal_gpio_init(handle->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + LL_SPI_Disable(handle->bus->spi); + } +} + +inline static void furi_hal_spi_bus_external_handle_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event, + const LL_SPI_InitTypeDef* preset) { + if(event == FuriHalSpiBusHandleEventInit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh); + } else if(event == FuriHalSpiBusHandleEventDeinit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } else if(event == FuriHalSpiBusHandleEventActivate) { + LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset); + LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER); + LL_SPI_Enable(handle->bus->spi); + + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullDown, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullDown, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullDown, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + + furi_hal_gpio_write(handle->cs, false); + } else if(event == FuriHalSpiBusHandleEventDeactivate) { + furi_hal_gpio_write(handle->cs, true); + + furi_hal_gpio_init(handle->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + LL_SPI_Disable(handle->bus->spi); + } +} + +inline static void furi_hal_spi_bus_nfc_handle_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event, + const LL_SPI_InitTypeDef* preset) { + if(event == FuriHalSpiBusHandleEventInit) { + // Configure GPIOs in normal SPI mode + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else if(event == FuriHalSpiBusHandleEventDeinit) { + // Configure GPIOs for st25r3916 Transparent mode + furi_hal_gpio_init(handle->sck, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_gpio_init(handle->miso, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_gpio_init(handle->cs, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_gpio_write(handle->mosi, false); + furi_hal_gpio_init(handle->mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else if(event == FuriHalSpiBusHandleEventActivate) { + LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset); + LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER); + LL_SPI_Enable(handle->bus->spi); + + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + + } else if(event == FuriHalSpiBusHandleEventDeactivate) { + furi_hal_gpio_init(handle->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + LL_SPI_Disable(handle->bus->spi); + } +} + +static void furi_hal_spi_bus_handle_subghz_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event) { + furi_hal_spi_bus_r_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_8m); +} + +FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz = { + .bus = &furi_hal_spi_bus_r, + .callback = furi_hal_spi_bus_handle_subghz_event_callback, + .miso = &gpio_spi_r_miso, + .mosi = &gpio_spi_r_mosi, + .sck = &gpio_spi_r_sck, + .cs = &gpio_subghz_cs, +}; + +static void furi_hal_spi_bus_handle_nfc_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event) { + furi_hal_spi_bus_nfc_handle_event_callback(handle, event, &furi_hal_spi_preset_2edge_low_8m); +} + +FuriHalSpiBusHandle furi_hal_spi_bus_handle_nfc = { + .bus = &furi_hal_spi_bus_r, + .callback = furi_hal_spi_bus_handle_nfc_event_callback, + .miso = &gpio_spi_r_miso, + .mosi = &gpio_spi_r_mosi, + .sck = &gpio_spi_r_sck, + .cs = &gpio_nfc_cs, +}; + +static void furi_hal_spi_bus_handle_external_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event) { + furi_hal_spi_bus_external_handle_event_callback( + handle, event, &furi_hal_spi_preset_1edge_low_2m); +} + +FuriHalSpiBusHandle furi_hal_spi_bus_handle_external = { + .bus = &furi_hal_spi_bus_r, + .callback = furi_hal_spi_bus_handle_external_event_callback, + .miso = &gpio_ext_pa6, + .mosi = &gpio_ext_pa7, + .sck = &gpio_ext_pb3, + .cs = &gpio_ext_pa4, +}; + +inline static void furi_hal_spi_bus_d_handle_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event, + const LL_SPI_InitTypeDef* preset) { + if(event == FuriHalSpiBusHandleEventInit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh); + + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + + } else if(event == FuriHalSpiBusHandleEventDeinit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeAnalog, GpioPullUp, GpioSpeedLow); + } else if(event == FuriHalSpiBusHandleEventActivate) { + LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset); + LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER); + LL_SPI_Enable(handle->bus->spi); + furi_hal_gpio_write(handle->cs, false); + } else if(event == FuriHalSpiBusHandleEventDeactivate) { + furi_hal_gpio_write(handle->cs, true); + LL_SPI_Disable(handle->bus->spi); + } +} + +static void furi_hal_spi_bus_handle_display_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event) { + furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_4m); +} + +FuriHalSpiBusHandle furi_hal_spi_bus_handle_display = { + .bus = &furi_hal_spi_bus_d, + .callback = furi_hal_spi_bus_handle_display_event_callback, + .miso = &gpio_spi_d_miso, + .mosi = &gpio_spi_d_mosi, + .sck = &gpio_spi_d_sck, + .cs = &gpio_display_cs, +}; + +static void furi_hal_spi_bus_handle_sd_fast_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event) { + furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_16m); +} + +FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast = { + .bus = &furi_hal_spi_bus_d, + .callback = furi_hal_spi_bus_handle_sd_fast_event_callback, + .miso = &gpio_spi_d_miso, + .mosi = &gpio_spi_d_mosi, + .sck = &gpio_spi_d_sck, + .cs = &gpio_sdcard_cs, +}; + +static void furi_hal_spi_bus_handle_sd_slow_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event) { + furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_2m); +} + +FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_slow = { + .bus = &furi_hal_spi_bus_d, + .callback = furi_hal_spi_bus_handle_sd_slow_event_callback, + .miso = &gpio_spi_d_miso, + .mosi = &gpio_spi_d_mosi, + .sck = &gpio_spi_d_sck, + .cs = &gpio_sdcard_cs, +}; diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi_config.h b/targets/f7/furi_hal/furi_hal_spi_config.h similarity index 100% rename from firmware/targets/f7/furi_hal/furi_hal_spi_config.h rename to targets/f7/furi_hal/furi_hal_spi_config.h diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi_types.h b/targets/f7/furi_hal/furi_hal_spi_types.h similarity index 97% rename from firmware/targets/f7/furi_hal/furi_hal_spi_types.h rename to targets/f7/furi_hal/furi_hal_spi_types.h index d2273f38ba5..ecc18d50db2 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi_types.h +++ b/targets/f7/furi_hal/furi_hal_spi_types.h @@ -6,8 +6,6 @@ #include #include -#include -#include #ifdef __cplusplus extern "C" { diff --git a/targets/f7/furi_hal/furi_hal_subghz.c b/targets/f7/furi_hal/furi_hal_subghz.c new file mode 100644 index 00000000000..a00ca7bf6d4 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_subghz.c @@ -0,0 +1,791 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#define TAG "FuriHalSubGhz" + +static uint32_t furi_hal_subghz_debug_gpio_buff[2]; + +/* DMA Channels definition */ +#define SUBGHZ_DMA DMA2 +#define SUBGHZ_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 +#define SUBGHZ_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2 +#define SUBGHZ_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 +#define SUBGHZ_DMA_CH1_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH1_CHANNEL +#define SUBGHZ_DMA_CH2_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH2_CHANNEL + +/** SubGhz state */ +typedef enum { + SubGhzStateInit, /**< Init pending */ + SubGhzStateBroken, /**< Chip power-on self test failed */ + SubGhzStateIdle, /**< Idle, energy save mode */ + + SubGhzStateAsyncRx, /**< Async RX started */ + + SubGhzStateAsyncTx, /**< Async TX started, DMA and timer is on */ + SubGhzStateAsyncTxLast, /**< Async TX continue, DMA completed and timer got last value to go */ + SubGhzStateAsyncTxEnd, /**< Async TX complete, cleanup needed */ + +} SubGhzState; + +/** SubGhz regulation, receive transmission on the current frequency for the + * region */ +typedef enum { + SubGhzRegulationOnlyRx, /**only Rx*/ + SubGhzRegulationTxRx, /**TxRx*/ +} SubGhzRegulation; + +typedef struct { + volatile SubGhzState state; + volatile SubGhzRegulation regulation; + const GpioPin* async_mirror_pin; +} FuriHalSubGhz; + +volatile FuriHalSubGhz furi_hal_subghz = { + .state = SubGhzStateInit, + .regulation = SubGhzRegulationTxRx, + .async_mirror_pin = NULL, +}; + +void furi_hal_subghz_set_async_mirror_pin(const GpioPin* pin) { + furi_hal_subghz.async_mirror_pin = pin; +} + +const GpioPin* furi_hal_subghz_get_data_gpio() { + return &gpio_cc1101_g0; +} + +void furi_hal_subghz_init() { + furi_assert(furi_hal_subghz.state == SubGhzStateInit); + furi_hal_subghz.state = SubGhzStateBroken; + + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + do { +#ifdef FURI_HAL_SUBGHZ_TX_GPIO + furi_hal_gpio_init( + &FURI_HAL_SUBGHZ_TX_GPIO, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); +#endif + + // Reset + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + cc1101_reset(&furi_hal_spi_bus_handle_subghz); + cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHighImpedance); + + // Prepare GD0 for power on self test + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + + // GD0 low + FuriHalCortexTimer timeout = furi_hal_cortex_timer_get(10000); + cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHW); + while(furi_hal_gpio_read(&gpio_cc1101_g0) != false && + !furi_hal_cortex_timer_is_expired(timeout)) + ; + + if(furi_hal_gpio_read(&gpio_cc1101_g0) != false) { + break; + } + + // GD0 high + timeout = furi_hal_cortex_timer_get(10000); + cc1101_write_reg( + &furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHW | CC1101_IOCFG_INV); + while(furi_hal_gpio_read(&gpio_cc1101_g0) != true && + !furi_hal_cortex_timer_is_expired(timeout)) + ; + + if(furi_hal_gpio_read(&gpio_cc1101_g0) != true) { + break; + } + + // Reset GD0 to floating state + cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHighImpedance); + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + // RF switches + furi_hal_gpio_init(&gpio_rf_sw_0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW); + + // Go to sleep + cc1101_shutdown(&furi_hal_spi_bus_handle_subghz); + + furi_hal_subghz.state = SubGhzStateIdle; + } while(false); + + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + + if(furi_hal_subghz.state == SubGhzStateIdle) { + FURI_LOG_I(TAG, "Init OK"); + } else { + FURI_LOG_E(TAG, "Init Fail"); + } +} + +void furi_hal_subghz_sleep() { + furi_assert(furi_hal_subghz.state == SubGhzStateIdle); + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + + cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); + + cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHighImpedance); + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + cc1101_shutdown(&furi_hal_spi_bus_handle_subghz); + + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); +} + +void furi_hal_subghz_dump_state() { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + printf( + "[furi_hal_subghz] cc1101 chip %d, version %d\r\n", + cc1101_get_partnumber(&furi_hal_spi_bus_handle_subghz), + cc1101_get_version(&furi_hal_spi_bus_handle_subghz)); + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); +} + +void furi_hal_subghz_load_custom_preset(const uint8_t* preset_data) { + //load config + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + cc1101_reset(&furi_hal_spi_bus_handle_subghz); + uint32_t i = 0; + uint8_t pa[8] = {0}; + while(preset_data[i]) { + cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, preset_data[i], preset_data[i + 1]); + i += 2; + } + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + + //load pa table + memcpy(&pa[0], &preset_data[i + 2], 8); + furi_hal_subghz_load_patable(pa); + + //show debug + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + i = 0; + FURI_LOG_D(TAG, "Loading custom preset"); + while(preset_data[i]) { + FURI_LOG_D(TAG, "Reg[%lu]: %02X=%02X", i, preset_data[i], preset_data[i + 1]); + i += 2; + } + for(uint8_t y = i; y < i + 10; y++) { + FURI_LOG_D(TAG, "PA[%u]: %02X", y, preset_data[y]); + } + } +} + +void furi_hal_subghz_load_registers(const uint8_t* data) { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + cc1101_reset(&furi_hal_spi_bus_handle_subghz); + uint32_t i = 0; + while(data[i]) { + cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, data[i], data[i + 1]); + i += 2; + } + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); +} + +void furi_hal_subghz_load_patable(const uint8_t data[8]) { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + cc1101_set_pa_table(&furi_hal_spi_bus_handle_subghz, data); + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); +} + +void furi_hal_subghz_write_packet(const uint8_t* data, uint8_t size) { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + cc1101_flush_tx(&furi_hal_spi_bus_handle_subghz); + cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_FIFO, size); + cc1101_write_fifo(&furi_hal_spi_bus_handle_subghz, data, size); + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); +} + +void furi_hal_subghz_flush_rx() { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + cc1101_flush_rx(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); +} + +void furi_hal_subghz_flush_tx() { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + cc1101_flush_tx(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); +} + +bool furi_hal_subghz_rx_pipe_not_empty() { + CC1101RxBytes status[1]; + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + cc1101_read_reg( + &furi_hal_spi_bus_handle_subghz, (CC1101_STATUS_RXBYTES) | CC1101_BURST, (uint8_t*)status); + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + if(status->NUM_RXBYTES > 0) { + return true; + } else { + return false; + } +} + +bool furi_hal_subghz_is_rx_data_crc_valid() { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + uint8_t data[1]; + cc1101_read_reg(&furi_hal_spi_bus_handle_subghz, CC1101_STATUS_LQI | CC1101_BURST, data); + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + if(((data[0] >> 7) & 0x01)) { + return true; + } else { + return false; + } +} + +void furi_hal_subghz_read_packet(uint8_t* data, uint8_t* size) { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + cc1101_read_fifo(&furi_hal_spi_bus_handle_subghz, data, size); + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); +} + +void furi_hal_subghz_shutdown() { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + // Reset and shutdown + cc1101_shutdown(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); +} + +void furi_hal_subghz_reset() { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); + cc1101_reset(&furi_hal_spi_bus_handle_subghz); + cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHighImpedance); + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); +} + +void furi_hal_subghz_idle() { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); +} + +void furi_hal_subghz_rx() { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); +} + +bool furi_hal_subghz_tx() { + if(furi_hal_subghz.regulation != SubGhzRegulationTxRx) return false; + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + cc1101_switch_to_tx(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + return true; +} + +float furi_hal_subghz_get_rssi() { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + int32_t rssi_dec = cc1101_get_rssi(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + + float rssi = rssi_dec; + if(rssi_dec >= 128) { + rssi = ((rssi - 256.0f) / 2.0f) - 74.0f; + } else { + rssi = (rssi / 2.0f) - 74.0f; + } + + return rssi; +} + +uint8_t furi_hal_subghz_get_lqi() { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + uint8_t data[1]; + cc1101_read_reg(&furi_hal_spi_bus_handle_subghz, CC1101_STATUS_LQI | CC1101_BURST, data); + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + return data[0] & 0x7F; +} + +bool furi_hal_subghz_is_frequency_valid(uint32_t value) { + if(!(value >= 299999755 && value <= 348000335) && + !(value >= 386999938 && value <= 464000000) && + !(value >= 778999847 && value <= 928000000)) { + return false; + } + + return true; +} + +uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) { + value = furi_hal_subghz_set_frequency(value); + if(value >= 299999755 && value <= 348000335) { + furi_hal_subghz_set_path(FuriHalSubGhzPath315); + } else if(value >= 386999938 && value <= 464000000) { + furi_hal_subghz_set_path(FuriHalSubGhzPath433); + } else if(value >= 778999847 && value <= 928000000) { + furi_hal_subghz_set_path(FuriHalSubGhzPath868); + } else { + furi_crash("SubGhz: Incorrect frequency during set."); + } + return 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 = SubGhzRegulationOnlyRx; + } + + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + uint32_t real_frequency = cc1101_set_frequency(&furi_hal_spi_bus_handle_subghz, value); + cc1101_calibrate(&furi_hal_spi_bus_handle_subghz); + + while(true) { + CC1101Status status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz); + if(status.STATE == CC1101StateIDLE) break; + } + + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + return real_frequency; +} + +void furi_hal_subghz_set_path(FuriHalSubGhzPath path) { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + if(path == FuriHalSubGhzPath433) { + furi_hal_gpio_write(&gpio_rf_sw_0, 0); + cc1101_write_reg( + &furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW | CC1101_IOCFG_INV); + } else if(path == FuriHalSubGhzPath315) { + furi_hal_gpio_write(&gpio_rf_sw_0, 1); + cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW); + } else if(path == FuriHalSubGhzPath868) { + furi_hal_gpio_write(&gpio_rf_sw_0, 1); + cc1101_write_reg( + &furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW | CC1101_IOCFG_INV); + } else if(path == FuriHalSubGhzPathIsolate) { + furi_hal_gpio_write(&gpio_rf_sw_0, 0); + cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW); + } else { + furi_crash("SubGhz: Incorrect path during set."); + } + furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); +} + +static bool furi_hal_subghz_start_debug() { + bool ret = false; + if(furi_hal_subghz.async_mirror_pin != NULL) { + furi_hal_gpio_init( + furi_hal_subghz.async_mirror_pin, + GpioModeOutputPushPull, + GpioPullNo, + GpioSpeedVeryHigh); + ret = true; + } + return ret; +} + +static bool furi_hal_subghz_stop_debug() { + bool ret = false; + if(furi_hal_subghz.async_mirror_pin != NULL) { + furi_hal_gpio_init( + furi_hal_subghz.async_mirror_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + ret = true; + } + return ret; +} + +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() { + // 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); + } + } +} + +void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* context) { + furi_assert(furi_hal_subghz.state == SubGhzStateIdle); + furi_hal_subghz.state = SubGhzStateAsyncRx; + + furi_hal_subghz_capture_callback = callback; + furi_hal_subghz_capture_callback_context = context; + + furi_hal_gpio_init_ex( + &gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); + + furi_hal_bus_enable(FuriHalBusTIM2); + + // 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 + LL_TIM_Init(TIM2, &TIM_InitStruct); + + // Timer: advanced + LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(TIM2); + 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); + + // 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); + LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_FALLING); + + // Timer: channel 2 direct + LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI); + LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); + LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING); + LL_TIM_IC_SetFilter( + TIM2, + LL_TIM_CHANNEL_CH2, + LL_TIM_IC_FILTER_FDIV32_N8); // Capture filter: 1/(64000000/64/4/32*8) = 16us + + // ISR setup + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_capture_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); + + // Start timer + LL_TIM_SetCounter(TIM2, 0); + LL_TIM_EnableCounter(TIM2); + + // Start debug + furi_hal_subghz_start_debug(); + + // Switch to RX + furi_hal_subghz_rx(); + + //Clear the variable after the end of the session + furi_hal_subghz_capture_delta_duration = 0; +} + +void furi_hal_subghz_stop_async_rx() { + furi_assert(furi_hal_subghz.state == SubGhzStateAsyncRx); + furi_hal_subghz.state = SubGhzStateIdle; + + // Shutdown radio + furi_hal_subghz_idle(); + + FURI_CRITICAL_ENTER(); + furi_hal_bus_disable(FuriHalBusTIM2); + + // Stop debug + furi_hal_subghz_stop_debug(); + + FURI_CRITICAL_EXIT(); + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); + + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +typedef struct { + uint32_t* buffer; + LevelDuration carry_ld; + FuriHalSubGhzAsyncTxCallback callback; + void* callback_context; + uint64_t duty_high; + uint64_t duty_low; +} FuriHalSubGhzAsyncTx; + +static FuriHalSubGhzAsyncTx furi_hal_subghz_async_tx = {0}; + +static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { + furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx); + while(samples > 0) { + bool is_odd = samples % 2; + LevelDuration ld; + if(level_duration_is_reset(furi_hal_subghz_async_tx.carry_ld)) { + ld = furi_hal_subghz_async_tx.callback(furi_hal_subghz_async_tx.callback_context); + } else { + ld = furi_hal_subghz_async_tx.carry_ld; + furi_hal_subghz_async_tx.carry_ld = level_duration_reset(); + } + + if(level_duration_is_wait(ld)) { + *buffer = API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; + buffer++; + samples--; + } else if(level_duration_is_reset(ld)) { + *buffer = 0; + buffer++; + samples--; + LL_DMA_DisableIT_HT(SUBGHZ_DMA_CH1_DEF); + LL_DMA_DisableIT_TC(SUBGHZ_DMA_CH1_DEF); + LL_TIM_EnableIT_UPDATE(TIM2); + break; + } else { + bool level = level_duration_get_level(ld); + + // Inject guard time if level is incorrect + if(is_odd != level) { + *buffer = API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; + buffer++; + samples--; + if(is_odd) { + furi_hal_subghz_async_tx.duty_high += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; + } else { + furi_hal_subghz_async_tx.duty_low += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; + } + + // Special case: prevent buffer overflow if sample is last + if(samples == 0) { + furi_hal_subghz_async_tx.carry_ld = ld; + break; + } + } + + uint32_t duration = level_duration_get_duration(ld); + furi_assert(duration > 0); + *buffer = duration; + buffer++; + samples--; + + if(is_odd) { + furi_hal_subghz_async_tx.duty_high += duration; + } else { + furi_hal_subghz_async_tx.duty_low += duration; + } + } + } +} + +static void furi_hal_subghz_async_tx_dma_isr() { + furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx); + +#if SUBGHZ_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 + if(LL_DMA_IsActiveFlag_HT1(SUBGHZ_DMA)) { + LL_DMA_ClearFlag_HT1(SUBGHZ_DMA); + furi_hal_subghz_async_tx_refill( + furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); + } + if(LL_DMA_IsActiveFlag_TC1(SUBGHZ_DMA)) { + LL_DMA_ClearFlag_TC1(SUBGHZ_DMA); + furi_hal_subghz_async_tx_refill( + furi_hal_subghz_async_tx.buffer + API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF, + API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); + } +#else +#error Update this code. Would you kindly? +#endif +} + +static void furi_hal_subghz_async_tx_timer_isr() { + if(LL_TIM_IsActiveFlag_UPDATE(TIM2)) { + LL_TIM_ClearFlag_UPDATE(TIM2); + if(LL_TIM_GetAutoReload(TIM2) == 0) { + if(furi_hal_subghz.state == SubGhzStateAsyncTx) { + furi_hal_subghz.state = SubGhzStateAsyncTxLast; + LL_DMA_DisableChannel(SUBGHZ_DMA_CH1_DEF); + } else if(furi_hal_subghz.state == SubGhzStateAsyncTxLast) { + furi_hal_subghz.state = SubGhzStateAsyncTxEnd; + //forcibly pulls the pin to the ground so that there is no carrier + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullDown, GpioSpeedLow); + LL_TIM_DisableCounter(TIM2); + } else { + furi_crash(); + } + } + } +} + +bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context) { + furi_assert(furi_hal_subghz.state == SubGhzStateIdle); + furi_assert(callback); + + //If transmission is prohibited by regional settings + if(furi_hal_subghz.regulation != SubGhzRegulationTxRx) return false; + + furi_hal_subghz_async_tx.callback = callback; + furi_hal_subghz_async_tx.callback_context = context; + + furi_hal_subghz.state = SubGhzStateAsyncTx; + + furi_hal_subghz_async_tx.duty_low = 0; + furi_hal_subghz_async_tx.duty_high = 0; + + furi_hal_subghz_async_tx.buffer = + malloc(API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * sizeof(uint32_t)); + + // Connect CC1101_GD0 to TIM2 as output + furi_hal_gpio_init_ex( + &gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullDown, GpioSpeedLow, GpioAltFn1TIM2); + + // Configure DMA + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)furi_hal_subghz_async_tx.buffer; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_MODE_NORMAL; + LL_DMA_Init(SUBGHZ_DMA_CH1_DEF, &dma_config); + furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, furi_hal_subghz_async_tx_dma_isr, NULL); + LL_DMA_EnableIT_TC(SUBGHZ_DMA_CH1_DEF); + LL_DMA_EnableIT_HT(SUBGHZ_DMA_CH1_DEF); + LL_DMA_EnableChannel(SUBGHZ_DMA_CH1_DEF); + + furi_hal_bus_enable(FuriHalBusTIM2); + + // Configure TIM2 + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + TIM_InitStruct.Prescaler = 64 - 1; + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + TIM_InitStruct.Autoreload = 1000; + TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; + LL_TIM_Init(TIM2, &TIM_InitStruct); + LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_EnableARRPreload(TIM2); + + // Configure TIM2 CH2 + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_TOGGLE; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE; + TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE; + TIM_OC_InitStruct.CompareValue = 0; + TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_LOW; + LL_TIM_OC_Init(TIM2, LL_TIM_CHANNEL_CH2, &TIM_OC_InitStruct); + LL_TIM_OC_DisableFast(TIM2, LL_TIM_CHANNEL_CH2); + LL_TIM_DisableMasterSlaveMode(TIM2); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_async_tx_timer_isr, NULL); + + furi_hal_subghz_async_tx_refill( + furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL); + + LL_TIM_EnableDMAReq_UPDATE(TIM2); + LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); + + // Start counter + LL_TIM_GenerateEvent_UPDATE(TIM2); +#ifdef FURI_HAL_SUBGHZ_TX_GPIO + furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, true); +#endif + furi_hal_subghz_tx(); + + LL_TIM_SetCounter(TIM2, 0); + LL_TIM_EnableCounter(TIM2); + + // Start debug + if(furi_hal_subghz_start_debug()) { + const GpioPin* gpio = furi_hal_subghz.async_mirror_pin; + furi_hal_subghz_debug_gpio_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER; + furi_hal_subghz_debug_gpio_buff[1] = gpio->pin; + + dma_config.MemoryOrM2MDstAddress = (uint32_t)furi_hal_subghz_debug_gpio_buff; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->BSRR); + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_CIRCULAR; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD; + dma_config.NbData = 2; + dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; + dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; + LL_DMA_Init(SUBGHZ_DMA_CH2_DEF, &dma_config); + LL_DMA_SetDataLength(SUBGHZ_DMA_CH2_DEF, 2); + LL_DMA_EnableChannel(SUBGHZ_DMA_CH2_DEF); + } + + return true; +} + +bool furi_hal_subghz_is_async_tx_complete() { + return furi_hal_subghz.state == SubGhzStateAsyncTxEnd; +} + +void furi_hal_subghz_stop_async_tx() { + furi_assert( + furi_hal_subghz.state == SubGhzStateAsyncTx || + furi_hal_subghz.state == SubGhzStateAsyncTxLast || + furi_hal_subghz.state == SubGhzStateAsyncTxEnd); + + // Shutdown radio + furi_hal_subghz_idle(); +#ifdef FURI_HAL_SUBGHZ_TX_GPIO + furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, false); +#endif + + // Deinitialize Timer + FURI_CRITICAL_ENTER(); + furi_hal_bus_disable(FuriHalBusTIM2); + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); + + // Deinitialize DMA + LL_DMA_DeInit(SUBGHZ_DMA_CH1_DEF); + + furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, NULL, NULL); + + // Deinitialize GPIO + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + // Stop debug + if(furi_hal_subghz_stop_debug()) { + LL_DMA_DisableChannel(SUBGHZ_DMA_CH2_DEF); + } + + FURI_CRITICAL_EXIT(); + + free(furi_hal_subghz_async_tx.buffer); + + float duty_cycle = + 100.0f * (float)furi_hal_subghz_async_tx.duty_high / + ((float)furi_hal_subghz_async_tx.duty_low + (float)furi_hal_subghz_async_tx.duty_high); + FURI_LOG_D( + TAG, + "Async TX Radio stats: on %0.0fus, off %0.0fus, DutyCycle: %0.0f%%", + (double)furi_hal_subghz_async_tx.duty_high, + (double)furi_hal_subghz_async_tx.duty_low, + (double)duty_cycle); + + furi_hal_subghz.state = SubGhzStateIdle; +} diff --git a/targets/f7/furi_hal/furi_hal_subghz.h b/targets/f7/furi_hal/furi_hal_subghz.h new file mode 100644 index 00000000000..855ce31619c --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_subghz.h @@ -0,0 +1,224 @@ +/** + * @file furi_hal_subghz.h + * SubGhz HAL API + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Low level buffer dimensions and guard times */ +#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL (256) +#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF (API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL / 2) +#define API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME 999 + +/** Switchable Radio Paths */ +typedef enum { + FuriHalSubGhzPathIsolate, /**< Isolate Radio from antenna */ + FuriHalSubGhzPath433, /**< Center Frequency: 433MHz. Path 1: SW1RF1-SW2RF2, LCLCL */ + FuriHalSubGhzPath315, /**< Center Frequency: 315MHz. Path 2: SW1RF2-SW2RF1, LCLCLCL */ + FuriHalSubGhzPath868, /**< Center Frequency: 868MHz. Path 3: SW1RF3-SW2RF3, LCLC */ +} FuriHalSubGhzPath; + +/* Mirror RX/TX async modulation signal to specified pin + * + * @warning Configures pin to output mode. Make sure it is not connected + * directly to power or ground. + * + * @param[in] pin pointer to the gpio pin structure or NULL to disable + */ +void furi_hal_subghz_set_async_mirror_pin(const GpioPin* pin); + +/** Get data GPIO + * + * @return pointer to the gpio pin structure + */ +const GpioPin* furi_hal_subghz_get_data_gpio(); + +/** Initialize and switch to power save mode Used by internal API-HAL + * initialization routine Can be used to reinitialize device to safe state and + * send it to sleep + */ +void furi_hal_subghz_init(); + +/** Send device to sleep mode + */ +void furi_hal_subghz_sleep(); + +/** Dump info to stdout + */ +void furi_hal_subghz_dump_state(); + +/** Load custom registers from preset + * + * @param preset_data registers to load + */ +void furi_hal_subghz_load_custom_preset(const uint8_t* preset_data); + +/** Load registers + * + * @param data Registers data + */ +void furi_hal_subghz_load_registers(const uint8_t* data); + +/** Load PATABLE + * + * @param data 8 uint8_t values + */ +void furi_hal_subghz_load_patable(const uint8_t data[8]); + +/** Write packet to FIFO + * + * @param data bytes array + * @param size size + */ +void furi_hal_subghz_write_packet(const uint8_t* data, uint8_t size); + +/** Check if receive pipe is not empty + * + * @return true if not empty + */ +bool furi_hal_subghz_rx_pipe_not_empty(); + +/** Check if received data crc is valid + * + * @return true if valid + */ +bool furi_hal_subghz_is_rx_data_crc_valid(); + +/** Read packet from FIFO + * + * @param data pointer + * @param size size + */ +void furi_hal_subghz_read_packet(uint8_t* data, uint8_t* size); + +/** Flush rx FIFO buffer + */ +void furi_hal_subghz_flush_rx(); + +/** Flush tx FIFO buffer + */ +void furi_hal_subghz_flush_tx(); + +/** Shutdown Issue SPWD command + * @warning registers content will be lost + */ +void furi_hal_subghz_shutdown(); + +/** Reset Issue reset command + * @warning registers content will be lost + */ +void furi_hal_subghz_reset(); + +/** Switch to Idle + */ +void furi_hal_subghz_idle(); + +/** Switch to Receive + */ +void furi_hal_subghz_rx(); + +/** Switch to Transmit + * + * @return true if the transfer is allowed by belonging to the region + */ +bool furi_hal_subghz_tx(); + +/** Get RSSI value in dBm + * + * @return RSSI value + */ +float furi_hal_subghz_get_rssi(); + +/** Get LQI + * + * @return LQI value + */ +uint8_t furi_hal_subghz_get_lqi(); + +/** Check if frequency is in valid range + * + * @param value frequency in Hz + * + * @return true if frequency is valid, otherwise false + */ +bool furi_hal_subghz_is_frequency_valid(uint32_t value); + +/** Set frequency and path This function automatically selects antenna matching + * network + * + * @param value frequency in Hz + * + * @return real frequency in Hz + */ +uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value); + +/** Set frequency + * + * @param value frequency in Hz + * + * @return real frequency in Hz + */ +uint32_t furi_hal_subghz_set_frequency(uint32_t value); + +/** Set path + * + * @param path path to use + */ +void furi_hal_subghz_set_path(FuriHalSubGhzPath path); + +/* High Level API */ + +/** Signal Timings Capture callback */ +typedef void (*FuriHalSubGhzCaptureCallback)(bool level, uint32_t duration, void* context); + +/** Enable signal timings capture Initializes GPIO and TIM2 for timings capture + * + * @param callback FuriHalSubGhzCaptureCallback + * @param context callback context + */ +void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* context); + +/** Disable signal timings capture Resets GPIO and TIM2 + */ +void furi_hal_subghz_stop_async_rx(); + +/** Async TX callback type + * @param context callback context + * @return LevelDuration + */ +typedef LevelDuration (*FuriHalSubGhzAsyncTxCallback)(void* context); + +/** Start async TX Initializes GPIO, TIM2 and DMA1 for signal output + * + * @param callback FuriHalSubGhzAsyncTxCallback + * @param context callback context + * + * @return true if the transfer is allowed by belonging to the region + */ +bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context); + +/** Wait for async transmission to complete + * + * @return true if TX complete + */ +bool furi_hal_subghz_is_async_tx_complete(); + +/** Stop async transmission and cleanup resources Resets GPIO, TIM2, and DMA1 + */ +void furi_hal_subghz_stop_async_tx(); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/furi_hal/furi_hal_target_hw.h b/targets/f7/furi_hal/furi_hal_target_hw.h new file mode 100644 index 00000000000..128122f8439 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_target_hw.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include +#include +#include diff --git a/firmware/targets/f7/furi_hal/furi_hal_uart.c b/targets/f7/furi_hal/furi_hal_uart.c similarity index 86% rename from firmware/targets/f7/furi_hal/furi_hal_uart.c rename to targets/f7/furi_hal/furi_hal_uart.c index cb44b6d1670..209c6be6a2e 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_uart.c +++ b/targets/f7/furi_hal/furi_hal_uart.c @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -13,6 +14,9 @@ static void (*irq_cb[2])(uint8_t ev, uint8_t data, void* context); static void* irq_ctx[2]; static void furi_hal_usart_init(uint32_t baud) { + furi_hal_bus_enable(FuriHalBusUSART1); + LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2); + furi_hal_gpio_init_ex( &gpio_usart_tx, GpioModeAltFunctionPushPull, @@ -26,7 +30,7 @@ static void furi_hal_usart_init(uint32_t baud) { GpioSpeedVeryHigh, GpioAltFn7USART1); - LL_USART_InitTypeDef USART_InitStruct = {0}; + LL_USART_InitTypeDef USART_InitStruct; USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1; USART_InitStruct.BaudRate = baud; USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B; @@ -44,11 +48,15 @@ static void furi_hal_usart_init(uint32_t baud) { while(!LL_USART_IsActiveFlag_TEACK(USART1) || !LL_USART_IsActiveFlag_REACK(USART1)) ; - LL_USART_EnableIT_RXNE_RXFNE(USART1); + LL_USART_DisableIT_ERROR(USART1); + NVIC_SetPriority(USART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); } static void furi_hal_lpuart_init(uint32_t baud) { + furi_hal_bus_enable(FuriHalBusLPUART1); + LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1); + furi_hal_gpio_init_ex( &gpio_ext_pc0, GpioModeAltFunctionPushPull, @@ -62,7 +70,7 @@ static void furi_hal_lpuart_init(uint32_t baud) { GpioSpeedVeryHigh, GpioAltFn8LPUART1); - LL_LPUART_InitTypeDef LPUART_InitStruct = {0}; + LL_LPUART_InitTypeDef LPUART_InitStruct; LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1; LPUART_InitStruct.BaudRate = 115200; LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B; @@ -79,16 +87,17 @@ static void furi_hal_lpuart_init(uint32_t baud) { ; furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud); + LL_LPUART_DisableIT_ERROR(LPUART1); - LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1); NVIC_SetPriority(LPUART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); } void furi_hal_uart_init(FuriHalUartId ch, uint32_t baud) { - if(ch == FuriHalUartIdLPUART1) + if(ch == FuriHalUartIdLPUART1) { furi_hal_lpuart_init(baud); - else if(ch == FuriHalUartIdUSART1) + } else if(ch == FuriHalUartIdUSART1) { furi_hal_usart_init(baud); + } } void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud) { @@ -125,11 +134,15 @@ void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud) { void furi_hal_uart_deinit(FuriHalUartId ch) { furi_hal_uart_set_irq_cb(ch, NULL, NULL); if(ch == FuriHalUartIdUSART1) { - LL_USART_Disable(USART1); + if(furi_hal_bus_is_enabled(FuriHalBusUSART1)) { + furi_hal_bus_disable(FuriHalBusUSART1); + } furi_hal_gpio_init(&gpio_usart_tx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); furi_hal_gpio_init(&gpio_usart_rx, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } else if(ch == FuriHalUartIdLPUART1) { - LL_LPUART_Disable(LPUART1); + if(furi_hal_bus_is_enabled(FuriHalBusLPUART1)) { + furi_hal_bus_disable(FuriHalBusLPUART1); + } furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } @@ -190,19 +203,25 @@ void furi_hal_uart_set_irq_cb( void (*cb)(UartIrqEvent ev, uint8_t data, void* ctx), void* ctx) { if(cb == NULL) { - if(ch == FuriHalUartIdUSART1) + if(ch == FuriHalUartIdUSART1) { NVIC_DisableIRQ(USART1_IRQn); - else if(ch == FuriHalUartIdLPUART1) + LL_USART_DisableIT_RXNE_RXFNE(USART1); + } else if(ch == FuriHalUartIdLPUART1) { NVIC_DisableIRQ(LPUART1_IRQn); + LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1); + } irq_cb[ch] = cb; irq_ctx[ch] = ctx; } else { irq_ctx[ch] = ctx; irq_cb[ch] = cb; - if(ch == FuriHalUartIdUSART1) + if(ch == FuriHalUartIdUSART1) { NVIC_EnableIRQ(USART1_IRQn); - else if(ch == FuriHalUartIdLPUART1) + LL_USART_EnableIT_RXNE_RXFNE(USART1); + } else if(ch == FuriHalUartIdLPUART1) { NVIC_EnableIRQ(LPUART1_IRQn); + LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1); + } } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_uart.h b/targets/f7/furi_hal/furi_hal_uart.h similarity index 100% rename from firmware/targets/f7/furi_hal/furi_hal_uart.h rename to targets/f7/furi_hal/furi_hal_uart.h diff --git a/targets/f7/furi_hal/furi_hal_usb.c b/targets/f7/furi_hal/furi_hal_usb.c new file mode 100644 index 00000000000..b88168d5d0c --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_usb.c @@ -0,0 +1,480 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "usb.h" + +#define TAG "FuriHalUsb" + +#define USB_RECONNECT_DELAY 500 + +typedef enum { + UsbApiEventTypeSetConfig, + UsbApiEventTypeGetConfig, + UsbApiEventTypeLock, + UsbApiEventTypeUnlock, + UsbApiEventTypeIsLocked, + UsbApiEventTypeEnable, + UsbApiEventTypeDisable, + UsbApiEventTypeReinit, + UsbApiEventTypeSetStateCallback, +} UsbApiEventType; + +typedef struct { + FuriHalUsbStateCallback callback; + void* context; +} UsbApiEventDataStateCallback; + +typedef struct { + FuriHalUsbInterface* interface; + void* context; +} UsbApiEventDataInterface; + +typedef union { + UsbApiEventDataStateCallback state_callback; + UsbApiEventDataInterface interface; +} UsbApiEventData; + +typedef union { + bool bool_value; + void* void_value; +} UsbApiEventReturnData; + +typedef struct { + FuriApiLock lock; + UsbApiEventType type; + UsbApiEventData data; + UsbApiEventReturnData* return_data; +} UsbApiEventMessage; + +typedef struct { + FuriThread* thread; + FuriMessageQueue* queue; + bool enabled; + bool connected; + bool mode_lock; + bool request_pending; + FuriHalUsbInterface* interface; + void* interface_context; + FuriHalUsbStateCallback callback; + void* callback_context; +} UsbSrv; + +typedef enum { + UsbEventReset = (1 << 0), + UsbEventRequest = (1 << 1), + UsbEventMessage = (1 << 2), +} UsbEvent; + +#define USB_SRV_ALL_EVENTS (UsbEventReset | UsbEventRequest | UsbEventMessage) + +PLACE_IN_SECTION("MB_MEM2") static UsbSrv usb = {0}; +PLACE_IN_SECTION("MB_MEM2") static uint32_t ubuf[0x20]; +PLACE_IN_SECTION("MB_MEM2") usbd_device udev; + +static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US); + +static int32_t furi_hal_usb_thread(void* context); +static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_t* length); +static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep); +static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep); +static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep); + +/* Low-level init */ +void furi_hal_usb_init(void) { + LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_PLLSAI1); + + LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; + LL_PWR_EnableVddUSB(); + + GPIO_InitStruct.Pin = LL_GPIO_PIN_11 | LL_GPIO_PIN_12; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; + GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + GPIO_InitStruct.Alternate = LL_GPIO_AF_10; + LL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + usbd_init(&udev, &usbd_hw, USB_EP0_SIZE, ubuf, sizeof(ubuf)); + + FURI_CRITICAL_ENTER(); + usbd_enable(&udev, true); + FURI_CRITICAL_EXIT(); + + usbd_reg_descr(&udev, usb_descriptor_get); + usbd_reg_event(&udev, usbd_evt_susp, susp_evt); + usbd_reg_event(&udev, usbd_evt_wkup, wkup_evt); + // Reset callback will be enabled after first mode change to avoid getting false reset events + + usb.enabled = false; + usb.interface = NULL; + NVIC_SetPriority(USB_LP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_SetPriority(USB_HP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0)); + NVIC_EnableIRQ(USB_LP_IRQn); + NVIC_EnableIRQ(USB_HP_IRQn); + + usb.queue = furi_message_queue_alloc(1, sizeof(UsbApiEventMessage)); + usb.thread = furi_thread_alloc_ex("UsbDriver", 1024, furi_hal_usb_thread, NULL); + furi_thread_mark_as_service(usb.thread); + furi_thread_start(usb.thread); + + FURI_LOG_I(TAG, "Init OK"); +} + +static void furi_hal_usb_send_message(UsbApiEventMessage* message) { + furi_message_queue_put(usb.queue, message, FuriWaitForever); + furi_thread_flags_set(furi_thread_get_id(usb.thread), UsbEventMessage); + api_lock_wait_unlock_and_free(message->lock); +} + +bool furi_hal_usb_set_config(FuriHalUsbInterface* new_if, void* ctx) { + UsbApiEventReturnData return_data = { + .bool_value = false, + }; + + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeSetConfig, + .data.interface = + { + .interface = new_if, + .context = ctx, + }, + .return_data = &return_data, + }; + + furi_hal_usb_send_message(&msg); + return return_data.bool_value; +} + +FuriHalUsbInterface* furi_hal_usb_get_config() { + UsbApiEventReturnData return_data = { + .void_value = NULL, + }; + + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeGetConfig, + .return_data = &return_data, + }; + + furi_hal_usb_send_message(&msg); + return return_data.void_value; +} + +void furi_hal_usb_lock() { + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeLock, + }; + + furi_hal_usb_send_message(&msg); +} + +void furi_hal_usb_unlock() { + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeUnlock, + }; + + furi_hal_usb_send_message(&msg); +} + +bool furi_hal_usb_is_locked() { + UsbApiEventReturnData return_data = { + .bool_value = false, + }; + + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeIsLocked, + .return_data = &return_data, + }; + + furi_hal_usb_send_message(&msg); + return return_data.bool_value; +} + +void furi_hal_usb_disable() { + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeDisable, + }; + + furi_hal_usb_send_message(&msg); +} + +void furi_hal_usb_enable() { + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeEnable, + }; + + furi_hal_usb_send_message(&msg); +} + +void furi_hal_usb_reinit() { + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeReinit, + }; + + furi_hal_usb_send_message(&msg); +} + +void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx) { + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeSetStateCallback, + .data.state_callback = + { + .callback = cb, + .context = ctx, + }, + }; + + furi_hal_usb_send_message(&msg); +} + +/* Get device / configuration descriptors */ +static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_t* length) { + const uint8_t dtype = req->wValue >> 8; + const uint8_t dnumber = req->wValue & 0xFF; + const void* desc; + uint16_t len = 0; + if(usb.interface == NULL) return usbd_fail; + + switch(dtype) { + case USB_DTYPE_DEVICE: + furi_thread_flags_set(furi_thread_get_id(usb.thread), UsbEventRequest); + if(usb.callback != NULL) { + usb.callback(FuriHalUsbStateEventDescriptorRequest, usb.callback_context); + } + desc = usb.interface->dev_descr; + break; + case USB_DTYPE_CONFIGURATION: + desc = usb.interface->cfg_descr; + len = ((struct usb_string_descriptor*)(usb.interface->cfg_descr))->wString[0]; + break; + case USB_DTYPE_STRING: + if(dnumber == UsbDevLang) { + desc = &dev_lang_desc; + } else if((dnumber == UsbDevManuf) && (usb.interface->str_manuf_descr != NULL)) { + desc = usb.interface->str_manuf_descr; + } else if((dnumber == UsbDevProduct) && (usb.interface->str_prod_descr != NULL)) { + desc = usb.interface->str_prod_descr; + } else if((dnumber == UsbDevSerial) && (usb.interface->str_serial_descr != NULL)) { + desc = usb.interface->str_serial_descr; + } else + return usbd_fail; + break; + default: + return usbd_fail; + } + if(desc == NULL) return usbd_fail; + + if(len == 0) { + len = ((struct usb_header_descriptor*)desc)->bLength; + } + *address = (void*)desc; + *length = len; + return usbd_ack; +} + +static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + UNUSED(event); + UNUSED(ep); + furi_thread_flags_set(furi_thread_get_id(usb.thread), UsbEventReset); + if(usb.callback != NULL) { + usb.callback(FuriHalUsbStateEventReset, usb.callback_context); + } +} + +static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + UNUSED(event); + UNUSED(ep); + if((usb.interface != NULL) && (usb.connected == true)) { + usb.connected = false; + usb.interface->suspend(&udev); + + furi_hal_power_insomnia_exit(); + } + if(usb.callback != NULL) { + usb.callback(FuriHalUsbStateEventSuspend, usb.callback_context); + } +} + +static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + UNUSED(event); + UNUSED(ep); + if((usb.interface != NULL) && (usb.connected == false)) { + usb.connected = true; + usb.interface->wakeup(&udev); + + furi_hal_power_insomnia_enter(); + } + if(usb.callback != NULL) { + usb.callback(FuriHalUsbStateEventWakeup, usb.callback_context); + } +} + +static void usb_process_mode_start(FuriHalUsbInterface* interface, void* context) { + if(usb.interface != NULL) { + usb.interface->deinit(&udev); + } + + __disable_irq(); + usb.interface = interface; + usb.interface_context = context; + __enable_irq(); + + if(interface != NULL) { + interface->init(&udev, interface, context); + usbd_reg_event(&udev, usbd_evt_reset, reset_evt); + FURI_LOG_I(TAG, "USB Mode change done"); + usb.enabled = true; + } +} + +static void usb_process_mode_change(FuriHalUsbInterface* interface, void* context) { + if((interface != usb.interface) || (context != usb.interface_context)) { + if(usb.enabled) { + // Disable current interface + susp_evt(&udev, 0, 0); + usbd_connect(&udev, false); + usb.enabled = false; + furi_delay_ms(USB_RECONNECT_DELAY); + } + usb_process_mode_start(interface, context); + } +} + +static void usb_process_mode_reinit() { + // Temporary disable callback to avoid getting false reset events + usbd_reg_event(&udev, usbd_evt_reset, NULL); + FURI_LOG_I(TAG, "USB Reinit"); + susp_evt(&udev, 0, 0); + usbd_connect(&udev, false); + usb.enabled = false; + + FURI_CRITICAL_ENTER(); + usbd_enable(&udev, false); + usbd_enable(&udev, true); + FURI_CRITICAL_EXIT(); + + furi_delay_ms(USB_RECONNECT_DELAY); + usb_process_mode_start(usb.interface, usb.interface_context); +} + +static bool usb_process_set_config(FuriHalUsbInterface* interface, void* context) { + if(usb.mode_lock) { + return false; + } else { + usb_process_mode_change(interface, context); + return true; + } +} + +static void usb_process_enable(bool enable) { + if(enable) { + if((!usb.enabled) && (usb.interface != NULL)) { + usbd_connect(&udev, true); + usb.enabled = true; + FURI_LOG_I(TAG, "USB Enable"); + } + } else { + if(usb.enabled) { + susp_evt(&udev, 0, 0); + usbd_connect(&udev, false); + usb.enabled = false; + usb.request_pending = false; + FURI_LOG_I(TAG, "USB Disable"); + } + } +} + +static void usb_process_message(UsbApiEventMessage* message) { + switch(message->type) { + case UsbApiEventTypeSetConfig: + message->return_data->bool_value = usb_process_set_config( + message->data.interface.interface, message->data.interface.context); + break; + case UsbApiEventTypeGetConfig: + message->return_data->void_value = usb.interface; + break; + case UsbApiEventTypeLock: + FURI_LOG_I(TAG, "Mode lock"); + usb.mode_lock = true; + break; + case UsbApiEventTypeUnlock: + FURI_LOG_I(TAG, "Mode unlock"); + usb.mode_lock = false; + break; + case UsbApiEventTypeIsLocked: + message->return_data->bool_value = usb.mode_lock; + break; + case UsbApiEventTypeDisable: + usb_process_enable(false); + break; + case UsbApiEventTypeEnable: + usb_process_enable(true); + break; + case UsbApiEventTypeReinit: + usb_process_mode_reinit(); + break; + case UsbApiEventTypeSetStateCallback: + usb.callback = message->data.state_callback.callback; + usb.callback_context = message->data.state_callback.context; + break; + } + + api_lock_unlock(message->lock); +} + +static int32_t furi_hal_usb_thread(void* context) { + UNUSED(context); + uint8_t usb_wait_time = 0; + + if(furi_message_queue_get_count(usb.queue) > 0) { + furi_thread_flags_set(furi_thread_get_id(usb.thread), UsbEventMessage); + } + + while(true) { + uint32_t flags = furi_thread_flags_wait(USB_SRV_ALL_EVENTS, FuriFlagWaitAny, 500); + + { + UsbApiEventMessage message; + if(furi_message_queue_get(usb.queue, &message, 0) == FuriStatusOk) { + usb_process_message(&message); + } + } + + if((flags & FuriFlagError) == 0) { + if(flags & UsbEventReset) { + if(usb.enabled) { + usb.request_pending = true; + usb_wait_time = 0; + } + } + if(flags & UsbEventRequest) { + usb.request_pending = false; + } + } else if(usb.request_pending) { + usb_wait_time++; + if(usb_wait_time > 4) { + usb_process_mode_reinit(); + usb.request_pending = false; + } + } + } + return 0; +} diff --git a/targets/f7/furi_hal/furi_hal_usb_ccid.c b/targets/f7/furi_hal/furi_hal_usb_ccid.c new file mode 100644 index 00000000000..e24713ced52 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_usb_ccid.c @@ -0,0 +1,536 @@ +#include +#include +#include +#include +#include + +#include "usb.h" +#include "usb_ccid.h" + +static const uint8_t USB_DEVICE_NO_CLASS = 0x0; +static const uint8_t USB_DEVICE_NO_SUBCLASS = 0x0; +static const uint8_t USB_DEVICE_NO_PROTOCOL = 0x0; + +#define FIXED_CONTROL_ENDPOINT_SIZE 8 +#define IF_NUM_MAX 1 + +#define CCID_VID_DEFAULT 0x1234 +#define CCID_PID_DEFAULT 0xABCD +#define CCID_TOTAL_SLOTS 1 +#define CCID_SLOT_INDEX 0 + +#define CCID_DATABLOCK_SIZE 256 + +#define ENDPOINT_DIR_IN 0x80 +#define ENDPOINT_DIR_OUT 0x00 + +#define INTERFACE_ID_CCID 0 + +#define CCID_IN_EPADDR (ENDPOINT_DIR_IN | 2) + +/** Endpoint address of the CCID data OUT endpoint, for host-to-device data transfers. */ +#define CCID_OUT_EPADDR (ENDPOINT_DIR_OUT | 1) + +/** Endpoint size in bytes of the CCID data being sent between IN and OUT endpoints. */ +#define CCID_EPSIZE 64 + +struct CcidIntfDescriptor { + struct usb_interface_descriptor ccid; + struct usb_ccid_descriptor ccid_desc; + struct usb_endpoint_descriptor ccid_bulk_in; + struct usb_endpoint_descriptor ccid_bulk_out; +} FURI_PACKED; + +struct CcidConfigDescriptor { + struct usb_config_descriptor config; + struct CcidIntfDescriptor intf_0; +} FURI_PACKED; + +enum CCID_Features_Auto_t { + CCID_Features_Auto_None = 0x0, + CCID_Features_Auto_ParameterConfiguration = 0x2, + CCID_Features_Auto_ICCActivation = 0x4, + CCID_Features_Auto_VoltageSelection = 0x8, + + CCID_Features_Auto_ICCClockFrequencyChange = 0x10, + CCID_Features_Auto_ICCBaudRateChange = 0x20, + CCID_Features_Auto_ParameterNegotiation = 0x40, + CCID_Features_Auto_PPS = 0x80, +}; + +enum CCID_Features_ExchangeLevel_t { + CCID_Features_ExchangeLevel_TPDU = 0x00010000, + CCID_Features_ExchangeLevel_ShortAPDU = 0x00020000, + CCID_Features_ExchangeLevel_ShortExtendedAPDU = 0x00040000 +}; + +/* Device descriptor */ +static struct usb_device_descriptor ccid_device_desc = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DTYPE_DEVICE, + .bcdUSB = VERSION_BCD(2, 0, 0), + .bDeviceClass = USB_DEVICE_NO_CLASS, + .bDeviceSubClass = USB_DEVICE_NO_SUBCLASS, + .bDeviceProtocol = USB_DEVICE_NO_PROTOCOL, + .bMaxPacketSize0 = FIXED_CONTROL_ENDPOINT_SIZE, + .idVendor = CCID_VID_DEFAULT, + .idProduct = CCID_PID_DEFAULT, + .bcdDevice = VERSION_BCD(1, 0, 0), + .iManufacturer = UsbDevManuf, + .iProduct = UsbDevProduct, + .iSerialNumber = UsbDevSerial, + .bNumConfigurations = 1, +}; + +/* Device configuration descriptor*/ +static const struct CcidConfigDescriptor ccid_cfg_desc = { + .config = + { + .bLength = sizeof(struct usb_config_descriptor), + .bDescriptorType = USB_DTYPE_CONFIGURATION, + .wTotalLength = sizeof(struct CcidConfigDescriptor), + .bNumInterfaces = 1, + + .bConfigurationValue = 1, + .iConfiguration = NO_DESCRIPTOR, + .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED, + .bMaxPower = USB_CFG_POWER_MA(100), + }, + .intf_0 = + { + .ccid = + {.bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + + .bInterfaceNumber = INTERFACE_ID_CCID, + .bAlternateSetting = 0x00, + .bNumEndpoints = 2, + + .bInterfaceClass = USB_CLASS_CCID, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + + .iInterface = NO_DESCRIPTOR + + }, + .ccid_desc = + {.bLength = sizeof(struct usb_ccid_descriptor), + .bDescriptorType = USB_DTYPE_CCID_FUNCTIONAL, + .bcdCCID = CCID_CURRENT_SPEC_RELEASE_NUMBER, + .bMaxSlotIndex = 0x00, + .bVoltageSupport = CCID_VOLTAGESUPPORT_5V, + .dwProtocols = 0x01, //T0 + .dwDefaultClock = 16000, //16MHz + .dwMaximumClock = 16000, //16MHz + .bNumClockSupported = 0, + .dwDataRate = 307200, + .dwMaxDataRate = 307200, + .bNumDataRatesSupported = 0, + .dwMaxIFSD = 2038, + .dwSynchProtocols = 0, + .dwMechanical = 0, + .dwFeatures = CCID_Features_ExchangeLevel_ShortAPDU | + CCID_Features_Auto_ParameterConfiguration | + CCID_Features_Auto_ICCActivation | + CCID_Features_Auto_VoltageSelection, + .dwMaxCCIDMessageLength = 0x0c00, + .bClassGetResponse = 0xff, + .bClassEnvelope = 0xff, + .wLcdLayout = 0, + .bPINSupport = 0, + .bMaxCCIDBusySlots = 1}, + .ccid_bulk_in = + {.bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = CCID_IN_EPADDR, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = CCID_EPSIZE, + .bInterval = 0x05 + + }, + .ccid_bulk_out = + {.bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = CCID_OUT_EPADDR, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = CCID_EPSIZE, + .bInterval = 0x05}, + }, +}; + +static void ccid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx); +static void ccid_deinit(usbd_device* dev); +static void ccid_on_wakeup(usbd_device* dev); +static void ccid_on_suspend(usbd_device* dev); + +FuriHalUsbInterface usb_ccid = { + .init = ccid_init, + .deinit = ccid_deinit, + .wakeup = ccid_on_wakeup, + .suspend = ccid_on_suspend, + + .dev_descr = (struct usb_device_descriptor*)&ccid_device_desc, + + .str_manuf_descr = NULL, + .str_prod_descr = NULL, + .str_serial_descr = NULL, + + .cfg_descr = (void*)&ccid_cfg_desc, +}; + +static usbd_respond ccid_ep_config(usbd_device* dev, uint8_t cfg); +static usbd_respond ccid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback); +static usbd_device* usb_dev; +static bool connected = false; +static bool smartcard_inserted = true; +static CcidCallbacks* callbacks[CCID_TOTAL_SLOTS] = {NULL}; + +static void* ccid_set_string_descr(char* str) { + furi_assert(str); + + size_t len = strlen(str); + struct usb_string_descriptor* dev_str_desc = malloc(len * 2 + 2); + dev_str_desc->bLength = len * 2 + 2; + dev_str_desc->bDescriptorType = USB_DTYPE_STRING; + for(size_t i = 0; i < len; i++) dev_str_desc->wString[i] = str[i]; + + return dev_str_desc; +} + +static void ccid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { + UNUSED(intf); + + FuriHalUsbCcidConfig* cfg = (FuriHalUsbCcidConfig*)ctx; + + usb_dev = dev; + + usb_ccid.dev_descr->iManufacturer = 0; + usb_ccid.dev_descr->iProduct = 0; + usb_ccid.str_manuf_descr = NULL; + usb_ccid.str_prod_descr = NULL; + usb_ccid.dev_descr->idVendor = CCID_VID_DEFAULT; + usb_ccid.dev_descr->idProduct = CCID_PID_DEFAULT; + + if(cfg != NULL) { + usb_ccid.dev_descr->idVendor = cfg->vid; + usb_ccid.dev_descr->idProduct = cfg->pid; + + if(cfg->manuf[0] != '\0') { + usb_ccid.str_manuf_descr = ccid_set_string_descr(cfg->manuf); + usb_ccid.dev_descr->iManufacturer = UsbDevManuf; + } + + if(cfg->product[0] != '\0') { + usb_ccid.str_prod_descr = ccid_set_string_descr(cfg->product); + usb_ccid.dev_descr->iProduct = UsbDevProduct; + } + } + + usbd_reg_config(dev, ccid_ep_config); + usbd_reg_control(dev, ccid_control); + + usbd_connect(dev, true); +} + +static void ccid_deinit(usbd_device* dev) { + usbd_reg_config(dev, NULL); + usbd_reg_control(dev, NULL); + + free(usb_ccid.str_prod_descr); + free(usb_ccid.str_serial_descr); +} + +static void ccid_on_wakeup(usbd_device* dev) { + UNUSED(dev); + connected = true; +} + +static void ccid_on_suspend(usbd_device* dev) { + UNUSED(dev); + connected = false; +} + +typedef struct ccid_bulk_message_header { + uint8_t bMessageType; + uint32_t dwLength; + uint8_t bSlot; + uint8_t bSeq; +} FURI_PACKED ccid_bulk_message_header_t; + +uint8_t SendBuffer[sizeof(ccid_bulk_message_header_t) + CCID_DATABLOCK_SIZE]; + +//stores the data p +uint8_t ReceiveBuffer[sizeof(ccid_bulk_message_header_t) + CCID_DATABLOCK_SIZE]; + +void CALLBACK_CCID_GetSlotStatus( + uint8_t slot, + uint8_t seq, + struct rdr_to_pc_slot_status* responseSlotStatus) { + responseSlotStatus->bMessageType = RDR_TO_PC_SLOTSTATUS; + + responseSlotStatus->bSlot = slot; + responseSlotStatus->bSeq = seq; + responseSlotStatus->bClockStatus = 0; + + responseSlotStatus->dwLength = 0; + + if(responseSlotStatus->bSlot == CCID_SLOT_INDEX) { + responseSlotStatus->bError = CCID_ERROR_NOERROR; + if(smartcard_inserted) { + responseSlotStatus->bStatus = CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | + CCID_ICCSTATUS_PRESENTANDACTIVE; + } else { + responseSlotStatus->bStatus = CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | + CCID_ICCSTATUS_NOICCPRESENT; + } + } else { + responseSlotStatus->bError = CCID_ERROR_SLOTNOTFOUND; + responseSlotStatus->bStatus = CCID_COMMANDSTATUS_FAILED | CCID_ICCSTATUS_NOICCPRESENT; + } +} + +void CALLBACK_CCID_SetParametersT0( + struct pc_to_rdr_set_parameters_t0* requestSetParametersT0, + struct rdr_to_pc_parameters_t0* responseSetParametersT0) { + furi_assert(requestSetParametersT0->bProtocolNum == 0x00); //T0 + responseSetParametersT0->bMessageType = RDR_TO_PC_PARAMETERS; + responseSetParametersT0->bSlot = requestSetParametersT0->bSlot; + responseSetParametersT0->bSeq = requestSetParametersT0->bSeq; + + responseSetParametersT0->dwLength = + sizeof(struct pc_to_rdr_set_parameters_t0) - sizeof(ccid_bulk_message_header_t); + + if(responseSetParametersT0->bSlot == CCID_SLOT_INDEX) { + responseSetParametersT0->bError = CCID_ERROR_NOERROR; + if(smartcard_inserted) { + responseSetParametersT0->bProtocolNum = requestSetParametersT0->bProtocolNum; + responseSetParametersT0->bStatus = CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | + CCID_ICCSTATUS_PRESENTANDACTIVE; + } else { + responseSetParametersT0->bStatus = CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | + CCID_ICCSTATUS_NOICCPRESENT; + } + } else { + responseSetParametersT0->bError = CCID_ERROR_SLOTNOTFOUND; + responseSetParametersT0->bStatus = CCID_COMMANDSTATUS_FAILED | CCID_ICCSTATUS_NOICCPRESENT; + } +} + +void CALLBACK_CCID_IccPowerOn( + uint8_t slot, + uint8_t seq, + struct rdr_to_pc_data_block* responseDataBlock) { + responseDataBlock->bMessageType = RDR_TO_PC_DATABLOCK; + responseDataBlock->dwLength = 0; + responseDataBlock->bSlot = slot; + responseDataBlock->bSeq = seq; + + if(responseDataBlock->bSlot == CCID_SLOT_INDEX) { + responseDataBlock->bError = CCID_ERROR_NOERROR; + if(smartcard_inserted) { + if(callbacks[CCID_SLOT_INDEX] != NULL) { + callbacks[CCID_SLOT_INDEX]->icc_power_on_callback( + responseDataBlock->abData, &responseDataBlock->dwLength, NULL); + responseDataBlock->bStatus = CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | + CCID_ICCSTATUS_PRESENTANDACTIVE; + } else { + responseDataBlock->bStatus = CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | + CCID_ICCSTATUS_PRESENTANDINACTIVE; + } + } else { + responseDataBlock->bStatus = CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | + CCID_ICCSTATUS_NOICCPRESENT; + } + } else { + responseDataBlock->bError = CCID_ERROR_SLOTNOTFOUND; + responseDataBlock->bStatus = CCID_COMMANDSTATUS_FAILED | CCID_ICCSTATUS_NOICCPRESENT; + } +} + +void CALLBACK_CCID_XfrBlock( + struct pc_to_rdr_xfr_block* receivedXfrBlock, + struct rdr_to_pc_data_block* responseDataBlock) { + responseDataBlock->bMessageType = RDR_TO_PC_DATABLOCK; + responseDataBlock->bSlot = receivedXfrBlock->bSlot; + responseDataBlock->bSeq = receivedXfrBlock->bSeq; + responseDataBlock->bChainParameter = 0; + + if(responseDataBlock->bSlot == CCID_SLOT_INDEX) { + responseDataBlock->bError = CCID_ERROR_NOERROR; + if(smartcard_inserted) { + if(callbacks[CCID_SLOT_INDEX] != NULL) { + callbacks[CCID_SLOT_INDEX]->xfr_datablock_callback( + (const uint8_t*)receivedXfrBlock->abData, + receivedXfrBlock->dwLength, + responseDataBlock->abData, + &responseDataBlock->dwLength, + NULL); + responseDataBlock->bStatus = CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | + CCID_ICCSTATUS_PRESENTANDACTIVE; + } else { + responseDataBlock->bStatus = CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | + CCID_ICCSTATUS_PRESENTANDINACTIVE; + } + } else { + responseDataBlock->bStatus = CCID_COMMANDSTATUS_PROCESSEDWITHOUTERROR | + CCID_ICCSTATUS_NOICCPRESENT; + } + } else { + responseDataBlock->bError = CCID_ERROR_SLOTNOTFOUND; + responseDataBlock->bStatus = CCID_COMMANDSTATUS_FAILED | CCID_ICCSTATUS_NOICCPRESENT; + } +} + +void furi_hal_ccid_ccid_insert_smartcard() { + smartcard_inserted = true; +} + +void furi_hal_ccid_ccid_remove_smartcard() { + smartcard_inserted = false; +} + +void furi_hal_ccid_set_callbacks(CcidCallbacks* cb) { + callbacks[CCID_SLOT_INDEX] = cb; +} + +static void ccid_rx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + UNUSED(event); + UNUSED(ep); +} + +static void ccid_tx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + + if(event == usbd_evt_eprx) { + if(connected == false) return; + + //read initial CCID message header + + int32_t bytes_read = usbd_ep_read( + usb_dev, ep, &ReceiveBuffer, sizeof(ccid_bulk_message_header_t) + CCID_DATABLOCK_SIZE); + //minimum request size is header size + furi_assert((uint16_t)bytes_read >= sizeof(ccid_bulk_message_header_t)); + ccid_bulk_message_header_t* message = (ccid_bulk_message_header_t*)&ReceiveBuffer; //-V641 + + if(message->bMessageType == PC_TO_RDR_ICCPOWERON) { + struct pc_to_rdr_icc_power_on* requestDataBlock = + (struct pc_to_rdr_icc_power_on*)message; //-V641 + struct rdr_to_pc_data_block* responseDataBlock = + (struct rdr_to_pc_data_block*)&SendBuffer; + + CALLBACK_CCID_IccPowerOn( + requestDataBlock->bSlot, requestDataBlock->bSeq, responseDataBlock); + + usbd_ep_write( + usb_dev, + CCID_IN_EPADDR, + responseDataBlock, + sizeof(struct rdr_to_pc_data_block) + + (sizeof(uint8_t) * responseDataBlock->dwLength)); + } else if(message->bMessageType == PC_TO_RDR_ICCPOWEROFF) { + struct pc_to_rdr_icc_power_off* requestIccPowerOff = + (struct pc_to_rdr_icc_power_off*)message; //-V641 + struct rdr_to_pc_slot_status* responseSlotStatus = + (struct rdr_to_pc_slot_status*)&SendBuffer; //-V641 + + CALLBACK_CCID_GetSlotStatus( + requestIccPowerOff->bSlot, requestIccPowerOff->bSeq, responseSlotStatus); + + usbd_ep_write( + usb_dev, CCID_IN_EPADDR, responseSlotStatus, sizeof(struct rdr_to_pc_slot_status)); + } else if(message->bMessageType == PC_TO_RDR_GETSLOTSTATUS) { + struct pc_to_rdr_get_slot_status* requestSlotStatus = + (struct pc_to_rdr_get_slot_status*)message; //-V641 + struct rdr_to_pc_slot_status* responseSlotStatus = + (struct rdr_to_pc_slot_status*)&SendBuffer; //-V641 + + CALLBACK_CCID_GetSlotStatus( + requestSlotStatus->bSlot, requestSlotStatus->bSeq, responseSlotStatus); + + usbd_ep_write( + usb_dev, CCID_IN_EPADDR, responseSlotStatus, sizeof(struct rdr_to_pc_slot_status)); + } else if(message->bMessageType == PC_TO_RDR_XFRBLOCK) { + struct pc_to_rdr_xfr_block* receivedXfrBlock = (struct pc_to_rdr_xfr_block*)message; + struct rdr_to_pc_data_block* responseDataBlock = + (struct rdr_to_pc_data_block*)&SendBuffer; + + furi_assert(receivedXfrBlock->dwLength <= CCID_DATABLOCK_SIZE); + furi_assert( + (uint16_t)bytes_read >= + sizeof(ccid_bulk_message_header_t) + receivedXfrBlock->dwLength); + + CALLBACK_CCID_XfrBlock(receivedXfrBlock, responseDataBlock); + + furi_assert(responseDataBlock->dwLength <= CCID_DATABLOCK_SIZE); + + usbd_ep_write( + usb_dev, + CCID_IN_EPADDR, + responseDataBlock, + sizeof(struct rdr_to_pc_data_block) + + (sizeof(uint8_t) * responseDataBlock->dwLength)); + } else if(message->bMessageType == PC_TO_RDR_SETPARAMETERS) { + struct pc_to_rdr_set_parameters_t0* requestSetParametersT0 = + (struct pc_to_rdr_set_parameters_t0*)message; //-V641 + struct rdr_to_pc_parameters_t0* responseSetParametersT0 = + (struct rdr_to_pc_parameters_t0*)&SendBuffer; //-V641 + + furi_assert(requestSetParametersT0->dwLength <= CCID_DATABLOCK_SIZE); + furi_assert( + (uint16_t)bytes_read >= + sizeof(ccid_bulk_message_header_t) + requestSetParametersT0->dwLength); + + CALLBACK_CCID_SetParametersT0(requestSetParametersT0, responseSetParametersT0); + + usbd_ep_write( + usb_dev, + CCID_IN_EPADDR, + responseSetParametersT0, + sizeof(struct rdr_to_pc_parameters_t0)); + } + } +} + +/* Configure endpoints */ +static usbd_respond ccid_ep_config(usbd_device* dev, uint8_t cfg) { + switch(cfg) { + case 0: + /* deconfiguring device */ + usbd_ep_deconfig(dev, CCID_IN_EPADDR); + usbd_ep_deconfig(dev, CCID_OUT_EPADDR); + usbd_reg_endpoint(dev, CCID_IN_EPADDR, 0); + usbd_reg_endpoint(dev, CCID_OUT_EPADDR, 0); + return usbd_ack; + case 1: + /* configuring device */ + usbd_ep_config(dev, CCID_IN_EPADDR, USB_EPTYPE_BULK, CCID_EPSIZE); + usbd_ep_config(dev, CCID_OUT_EPADDR, USB_EPTYPE_BULK, CCID_EPSIZE); + usbd_reg_endpoint(dev, CCID_IN_EPADDR, ccid_rx_ep_callback); + usbd_reg_endpoint(dev, CCID_OUT_EPADDR, ccid_tx_ep_callback); + return usbd_ack; + default: + return usbd_fail; + } +} + +/* Control requests handler */ +static usbd_respond ccid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { + UNUSED(callback); + /* CDC control requests */ + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == + (USB_REQ_INTERFACE | USB_REQ_CLASS) && + (req->wIndex == 0 || req->wIndex == 2)) { + switch(req->bRequest) { + case CCID_ABORT: + return usbd_fail; + case CCID_GET_CLOCK_FREQUENCIES: + dev->status.data_ptr = (void*)&(ccid_cfg_desc.intf_0.ccid_desc.dwDefaultClock); + dev->status.data_count = sizeof(ccid_cfg_desc.intf_0.ccid_desc.dwDefaultClock); + return usbd_ack; + default: + return usbd_fail; + } + } + return usbd_fail; +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_cdc.c b/targets/f7/furi_hal/furi_hal_usb_cdc.c similarity index 99% rename from firmware/targets/f7/furi_hal/furi_hal_usb_cdc.c rename to targets/f7/furi_hal/furi_hal_usb_cdc.c index 69a7fb3ce61..e9cb51e203c 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_cdc.c +++ b/targets/f7/furi_hal/furi_hal_usb_cdc.c @@ -1,7 +1,7 @@ -#include "furi_hal_version.h" -#include "furi_hal_usb_i.h" -#include "furi_hal_usb.h" -#include "furi_hal_usb_cdc_i.h" +#include +#include +#include +#include #include #include "usb.h" @@ -35,13 +35,13 @@ struct CdcIadDescriptor { struct CdcConfigDescriptorSingle { struct usb_config_descriptor config; struct CdcIadDescriptor iad_0; -} __attribute__((packed)); +} FURI_PACKED; struct CdcConfigDescriptorDual { struct usb_config_descriptor config; struct CdcIadDescriptor iad_0; struct CdcIadDescriptor iad_1; -} __attribute__((packed)); +} FURI_PACKED; static const struct usb_string_descriptor dev_manuf_desc = USB_STRING_DESC("Flipper Devices Inc."); diff --git a/targets/f7/furi_hal/furi_hal_usb_cdc.h b/targets/f7/furi_hal/furi_hal_usb_cdc.h new file mode 100644 index 00000000000..89b68991b95 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_usb_cdc.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include "usb_cdc.h" + +#define CDC_DATA_SZ 64 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + void (*tx_ep_callback)(void* context); + void (*rx_ep_callback)(void* context); + void (*state_callback)(void* context, uint8_t state); + void (*ctrl_line_callback)(void* context, uint8_t state); + void (*config_callback)(void* context, struct usb_cdc_line_coding* config); +} CdcCallbacks; + +void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb, void* context); + +struct usb_cdc_line_coding* furi_hal_cdc_get_port_settings(uint8_t if_num); + +uint8_t furi_hal_cdc_get_ctrl_line_state(uint8_t if_num); + +void furi_hal_cdc_send(uint8_t if_num, uint8_t* buf, uint16_t len); + +int32_t furi_hal_cdc_receive(uint8_t if_num, uint8_t* buf, uint16_t max_len); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/furi_hal/furi_hal_usb_hid.c b/targets/f7/furi_hal/furi_hal_usb_hid.c new file mode 100644 index 00000000000..b744e5ef72e --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_usb_hid.c @@ -0,0 +1,543 @@ +#include +#include +#include +#include +#include + +#include "usb.h" +#include "usb_hid.h" + +#define HID_EP_IN 0x81 +#define HID_EP_SZ 0x10 + +#define HID_INTERVAL 2 + +#define HID_VID_DEFAULT 0x046D +#define HID_PID_DEFAULT 0xC529 + +struct HidIntfDescriptor { + struct usb_interface_descriptor hid; + struct usb_hid_descriptor hid_desc; + struct usb_endpoint_descriptor hid_ep_in; +}; + +struct HidConfigDescriptor { + struct usb_config_descriptor config; + struct HidIntfDescriptor intf_0; +} FURI_PACKED; + +enum HidReportId { + ReportIdKeyboard = 1, + ReportIdMouse = 2, + ReportIdConsumer = 3, +}; + +/* HID report descriptor: keyboard + mouse + consumer control */ +static const uint8_t hid_report_desc[] = { + // clang-format off + HID_USAGE_PAGE(HID_PAGE_DESKTOP), + HID_USAGE(HID_DESKTOP_KEYBOARD), + HID_COLLECTION(HID_APPLICATION_COLLECTION), + HID_REPORT_ID(ReportIdKeyboard), + // Keyboard report + HID_USAGE_PAGE(HID_DESKTOP_KEYPAD), + HID_USAGE_MINIMUM(HID_KEYBOARD_L_CTRL), + HID_USAGE_MAXIMUM(HID_KEYBOARD_R_GUI), + HID_LOGICAL_MINIMUM(0), + HID_LOGICAL_MAXIMUM(1), + HID_REPORT_SIZE(1), + HID_REPORT_COUNT(8), + // Input - Modifier keys byte + HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + + HID_REPORT_COUNT(1), + HID_REPORT_SIZE(8), + // Input - Reserved byte + 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), + // Output - LEDs + HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + + HID_REPORT_COUNT(HID_KB_MAX_KEYS), + HID_REPORT_SIZE(8), + HID_LOGICAL_MINIMUM(0), + HID_LOGICAL_MAXIMUM(101), + HID_USAGE_PAGE(HID_DESKTOP_KEYPAD), + HID_USAGE_MINIMUM(0), + HID_USAGE_MAXIMUM(101), + // Input - Key codes + HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE), + HID_END_COLLECTION, + + HID_USAGE_PAGE(HID_PAGE_DESKTOP), + HID_USAGE(HID_DESKTOP_MOUSE), + HID_COLLECTION(HID_APPLICATION_COLLECTION), + HID_USAGE(HID_DESKTOP_POINTER), + HID_COLLECTION(HID_PHYSICAL_COLLECTION), + HID_REPORT_ID(ReportIdMouse), + // Mouse report + HID_USAGE_PAGE(HID_PAGE_BUTTON), + HID_USAGE_MINIMUM(1), + HID_USAGE_MAXIMUM(3), + HID_LOGICAL_MINIMUM(0), + HID_LOGICAL_MAXIMUM(1), + HID_REPORT_COUNT(3), + HID_REPORT_SIZE(1), + // Input - Mouse keys + HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + + HID_REPORT_SIZE(1), + HID_REPORT_COUNT(5), + // Input - Mouse keys padding + HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + + HID_USAGE_PAGE(HID_PAGE_DESKTOP), + HID_USAGE(HID_DESKTOP_X), + HID_USAGE(HID_DESKTOP_Y), + HID_USAGE(HID_DESKTOP_WHEEL), + HID_LOGICAL_MINIMUM(-127), + HID_LOGICAL_MAXIMUM(127), + HID_REPORT_SIZE(8), + HID_REPORT_COUNT(3), + // Input - Mouse movement data (x, y, scroll) + HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE), + HID_END_COLLECTION, + HID_END_COLLECTION, + + HID_USAGE_PAGE(HID_PAGE_CONSUMER), + HID_USAGE(HID_CONSUMER_CONTROL), + HID_COLLECTION(HID_APPLICATION_COLLECTION), + HID_REPORT_ID(ReportIdConsumer), + // Consumer report + HID_LOGICAL_MINIMUM(0), + HID_RI_LOGICAL_MAXIMUM(16, 0x3FF), + HID_USAGE_MINIMUM(0), + HID_RI_USAGE_MAXIMUM(16, 0x3FF), + HID_REPORT_COUNT(HID_CONSUMER_MAX_KEYS), + HID_REPORT_SIZE(16), + // Input - Consumer control keys + HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE), + HID_END_COLLECTION, + // clang-format on +}; + +/* Device descriptor */ +static struct usb_device_descriptor hid_device_desc = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DTYPE_DEVICE, + .bcdUSB = VERSION_BCD(2, 0, 0), + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .bDeviceSubClass = USB_SUBCLASS_NONE, + .bDeviceProtocol = USB_PROTO_NONE, + .bMaxPacketSize0 = USB_EP0_SIZE, + .idVendor = HID_VID_DEFAULT, + .idProduct = HID_PID_DEFAULT, + .bcdDevice = VERSION_BCD(1, 0, 0), + .iManufacturer = 0, + .iProduct = 0, + .iSerialNumber = 0, + .bNumConfigurations = 1, +}; + +/* Device configuration descriptor */ +static const struct HidConfigDescriptor hid_cfg_desc = { + .config = + { + .bLength = sizeof(struct usb_config_descriptor), + .bDescriptorType = USB_DTYPE_CONFIGURATION, + .wTotalLength = sizeof(struct HidConfigDescriptor), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = NO_DESCRIPTOR, + .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED, + .bMaxPower = USB_CFG_POWER_MA(100), + }, + .intf_0 = + { + .hid = + { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = USB_HID_SUBCLASS_BOOT, + .bInterfaceProtocol = USB_HID_PROTO_KEYBOARD, + .iInterface = NO_DESCRIPTOR, + }, + .hid_desc = + { + .bLength = sizeof(struct usb_hid_descriptor), + .bDescriptorType = USB_DTYPE_HID, + .bcdHID = VERSION_BCD(1, 0, 0), + .bCountryCode = USB_HID_COUNTRY_NONE, + .bNumDescriptors = 1, + .bDescriptorType0 = USB_DTYPE_HID_REPORT, + .wDescriptorLength0 = sizeof(hid_report_desc), + }, + .hid_ep_in = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = HID_EP_IN, + .bmAttributes = USB_EPTYPE_INTERRUPT, + .wMaxPacketSize = HID_EP_SZ, + .bInterval = HID_INTERVAL, + }, + }, +}; + +struct HidReportMouse { + uint8_t report_id; + uint8_t btn; + int8_t x; + int8_t y; + int8_t wheel; +} FURI_PACKED; + +struct HidReportKB { + uint8_t report_id; + struct { + uint8_t mods; + uint8_t reserved; + uint8_t btn[HID_KB_MAX_KEYS]; + } boot; +} FURI_PACKED; + +struct HidReportConsumer { + uint8_t report_id; + uint16_t btn[HID_CONSUMER_MAX_KEYS]; +} FURI_PACKED; + +struct HidReportLED { + uint8_t report_id; + uint8_t led_state; +} FURI_PACKED; + +static struct HidReport { + struct HidReportKB keyboard; + struct HidReportMouse mouse; + struct HidReportConsumer consumer; +} FURI_PACKED hid_report; + +static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx); +static void hid_deinit(usbd_device* dev); +static void hid_on_wakeup(usbd_device* dev); +static void hid_on_suspend(usbd_device* dev); + +FuriHalUsbInterface usb_hid = { + .init = hid_init, + .deinit = hid_deinit, + .wakeup = hid_on_wakeup, + .suspend = hid_on_suspend, + + .dev_descr = (struct usb_device_descriptor*)&hid_device_desc, + + .str_manuf_descr = NULL, + .str_prod_descr = NULL, + .str_serial_descr = NULL, + + .cfg_descr = (void*)&hid_cfg_desc, +}; + +static bool hid_send_report(uint8_t report_id); +static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg); +static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback); +static usbd_device* usb_dev; +static FuriSemaphore* hid_semaphore = NULL; +static bool hid_connected = false; +static HidStateCallback callback; +static void* cb_ctx; +static uint8_t led_state; +static bool boot_protocol = false; + +bool furi_hal_hid_is_connected() { + return hid_connected; +} + +uint8_t furi_hal_hid_get_led_state() { + return led_state; +} + +void furi_hal_hid_set_state_callback(HidStateCallback cb, void* ctx) { + if(callback != NULL) { + if(hid_connected == true) callback(false, cb_ctx); + } + + callback = cb; + cb_ctx = ctx; + + if(callback != NULL) { + if(hid_connected == true) callback(true, cb_ctx); + } +} + +bool furi_hal_hid_kb_press(uint16_t button) { + for(uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) { + if(hid_report.keyboard.boot.btn[key_nb] == 0) { + hid_report.keyboard.boot.btn[key_nb] = button & 0xFF; + break; + } + } + hid_report.keyboard.boot.mods |= (button >> 8); + return hid_send_report(ReportIdKeyboard); +} + +bool furi_hal_hid_kb_release(uint16_t button) { + for(uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) { + if(hid_report.keyboard.boot.btn[key_nb] == (button & 0xFF)) { + hid_report.keyboard.boot.btn[key_nb] = 0; + break; + } + } + hid_report.keyboard.boot.mods &= ~(button >> 8); + return hid_send_report(ReportIdKeyboard); +} + +bool furi_hal_hid_kb_release_all() { + for(uint8_t key_nb = 0; key_nb < HID_KB_MAX_KEYS; key_nb++) { + hid_report.keyboard.boot.btn[key_nb] = 0; + } + hid_report.keyboard.boot.mods = 0; + return hid_send_report(ReportIdKeyboard); +} + +bool furi_hal_hid_mouse_move(int8_t dx, int8_t dy) { + hid_report.mouse.x = dx; + hid_report.mouse.y = dy; + bool state = hid_send_report(ReportIdMouse); + hid_report.mouse.x = 0; + hid_report.mouse.y = 0; + return state; +} + +bool furi_hal_hid_mouse_press(uint8_t button) { + hid_report.mouse.btn |= button; + return hid_send_report(ReportIdMouse); +} + +bool furi_hal_hid_mouse_release(uint8_t button) { + hid_report.mouse.btn &= ~button; + return hid_send_report(ReportIdMouse); +} + +bool furi_hal_hid_mouse_scroll(int8_t delta) { + hid_report.mouse.wheel = delta; + bool state = hid_send_report(ReportIdMouse); + hid_report.mouse.wheel = 0; + return state; +} + +bool furi_hal_hid_consumer_key_press(uint16_t button) { + for(uint8_t key_nb = 0; key_nb < HID_CONSUMER_MAX_KEYS; key_nb++) { + if(hid_report.consumer.btn[key_nb] == 0) { + hid_report.consumer.btn[key_nb] = button; + break; + } + } + return hid_send_report(ReportIdConsumer); +} + +bool furi_hal_hid_consumer_key_release(uint16_t button) { + for(uint8_t key_nb = 0; key_nb < HID_CONSUMER_MAX_KEYS; key_nb++) { + if(hid_report.consumer.btn[key_nb] == button) { + hid_report.consumer.btn[key_nb] = 0; + break; + } + } + return hid_send_report(ReportIdConsumer); +} + +static void* hid_set_string_descr(char* str) { + furi_assert(str); + + size_t len = strlen(str); + struct usb_string_descriptor* dev_str_desc = malloc(len * 2 + 2); + dev_str_desc->bLength = len * 2 + 2; + dev_str_desc->bDescriptorType = USB_DTYPE_STRING; + for(size_t i = 0; i < len; i++) dev_str_desc->wString[i] = str[i]; + + return dev_str_desc; +} + +static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { + UNUSED(intf); + FuriHalUsbHidConfig* cfg = (FuriHalUsbHidConfig*)ctx; + if(hid_semaphore == NULL) hid_semaphore = furi_semaphore_alloc(1, 1); + usb_dev = dev; + hid_report.keyboard.report_id = ReportIdKeyboard; + hid_report.mouse.report_id = ReportIdMouse; + hid_report.consumer.report_id = ReportIdConsumer; + + usb_hid.dev_descr->iManufacturer = 0; + usb_hid.dev_descr->iProduct = 0; + usb_hid.str_manuf_descr = NULL; + usb_hid.str_prod_descr = NULL; + usb_hid.dev_descr->idVendor = HID_VID_DEFAULT; + usb_hid.dev_descr->idProduct = HID_PID_DEFAULT; + + if(cfg != NULL) { + usb_hid.dev_descr->idVendor = cfg->vid; + usb_hid.dev_descr->idProduct = cfg->pid; + + if(cfg->manuf[0] != '\0') { + usb_hid.str_manuf_descr = hid_set_string_descr(cfg->manuf); + usb_hid.dev_descr->iManufacturer = UsbDevManuf; + } + + if(cfg->product[0] != '\0') { + usb_hid.str_prod_descr = hid_set_string_descr(cfg->product); + usb_hid.dev_descr->iProduct = UsbDevProduct; + } + } + + usbd_reg_config(dev, hid_ep_config); + usbd_reg_control(dev, hid_control); + + usbd_connect(dev, true); +} + +static void hid_deinit(usbd_device* dev) { + usbd_reg_config(dev, NULL); + usbd_reg_control(dev, NULL); + + free(usb_hid.str_manuf_descr); + free(usb_hid.str_prod_descr); +} + +static void hid_on_wakeup(usbd_device* dev) { + UNUSED(dev); + if(!hid_connected) { + hid_connected = true; + if(callback != NULL) { + callback(true, cb_ctx); + } + } +} + +static void hid_on_suspend(usbd_device* dev) { + UNUSED(dev); + if(hid_connected) { + hid_connected = false; + furi_semaphore_release(hid_semaphore); + if(callback != NULL) { + callback(false, cb_ctx); + } + } +} + +static bool hid_send_report(uint8_t report_id) { + if((hid_semaphore == NULL) || (hid_connected == false)) return false; + if((boot_protocol == true) && (report_id != ReportIdKeyboard)) return false; + + FuriStatus status = furi_semaphore_acquire(hid_semaphore, HID_INTERVAL * 2); + if(status == FuriStatusErrorTimeout) { + return false; + } + furi_check(status == FuriStatusOk); + if(hid_connected == false) { + return false; + } + if(boot_protocol == true) { + usbd_ep_write( + usb_dev, HID_EP_IN, &hid_report.keyboard.boot, sizeof(hid_report.keyboard.boot)); + } else { + if(report_id == ReportIdKeyboard) + usbd_ep_write(usb_dev, HID_EP_IN, &hid_report.keyboard, sizeof(hid_report.keyboard)); + else if(report_id == ReportIdMouse) + usbd_ep_write(usb_dev, HID_EP_IN, &hid_report.mouse, sizeof(hid_report.mouse)); + else if(report_id == ReportIdConsumer) + usbd_ep_write(usb_dev, HID_EP_IN, &hid_report.consumer, sizeof(hid_report.consumer)); + } + return true; +} + +static void hid_txrx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + if(event == usbd_evt_eptx) { + furi_semaphore_release(hid_semaphore); + } else if(boot_protocol == true) { + usbd_ep_read(usb_dev, ep, &led_state, sizeof(led_state)); + } else { + struct HidReportLED leds; + usbd_ep_read(usb_dev, ep, &leds, sizeof(leds)); + led_state = leds.led_state; + } +} + +/* Configure endpoints */ +static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg) { + switch(cfg) { + case 0: + /* deconfiguring device */ + usbd_ep_deconfig(dev, HID_EP_IN); + usbd_reg_endpoint(dev, HID_EP_IN, 0); + return usbd_ack; + case 1: + /* configuring device */ + usbd_ep_config(dev, HID_EP_IN, USB_EPTYPE_INTERRUPT, HID_EP_SZ); + usbd_reg_endpoint(dev, HID_EP_IN, hid_txrx_ep_callback); + usbd_ep_write(dev, HID_EP_IN, 0, 0); + boot_protocol = false; /* BIOS will SET_PROTOCOL if it wants this */ + return usbd_ack; + default: + return usbd_fail; + } +} + +/* Control requests handler */ +static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { + UNUSED(callback); + /* HID control requests */ + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == + (USB_REQ_INTERFACE | USB_REQ_CLASS) && + req->wIndex == 0) { + switch(req->bRequest) { + case USB_HID_SETIDLE: + return usbd_ack; + case USB_HID_GETREPORT: + if(boot_protocol == true) { + dev->status.data_ptr = &hid_report.keyboard.boot; + dev->status.data_count = sizeof(hid_report.keyboard.boot); + } else { + dev->status.data_ptr = &hid_report; + dev->status.data_count = sizeof(hid_report); + } + return usbd_ack; + case USB_HID_SETPROTOCOL: + if(req->wValue == 0) + boot_protocol = true; + else if(req->wValue == 1) + boot_protocol = false; + else + return usbd_fail; + return usbd_ack; + default: + return usbd_fail; + } + } + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == + (USB_REQ_INTERFACE | USB_REQ_STANDARD) && + req->wIndex == 0 && req->bRequest == USB_STD_GET_DESCRIPTOR) { + switch(req->wValue >> 8) { + case USB_DTYPE_HID: + dev->status.data_ptr = (uint8_t*)&(hid_cfg_desc.intf_0.hid_desc); + dev->status.data_count = sizeof(hid_cfg_desc.intf_0.hid_desc); + return usbd_ack; + case USB_DTYPE_HID_REPORT: + boot_protocol = false; /* BIOS does not read this */ + dev->status.data_ptr = (uint8_t*)hid_report_desc; + dev->status.data_count = sizeof(hid_report_desc); + return usbd_ack; + default: + return usbd_fail; + } + } + return usbd_fail; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_i.h b/targets/f7/furi_hal/furi_hal_usb_i.h similarity index 100% rename from firmware/targets/f7/furi_hal/furi_hal_usb_i.h rename to targets/f7/furi_hal/furi_hal_usb_i.h diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_u2f.c b/targets/f7/furi_hal/furi_hal_usb_u2f.c similarity index 98% rename from firmware/targets/f7/furi_hal/furi_hal_usb_u2f.c rename to targets/f7/furi_hal/furi_hal_usb_u2f.c index 60068f3b43c..cc7e23b77e3 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_u2f.c +++ b/targets/f7/furi_hal/furi_hal_usb_u2f.c @@ -1,7 +1,7 @@ -#include "furi_hal_version.h" -#include "furi_hal_usb_i.h" -#include "furi_hal_usb_hid_u2f.h" -#include "furi_hal_usb.h" +#include +#include +#include +#include #include #include "usb.h" #include "usb_hid.h" @@ -25,7 +25,7 @@ struct HidIadDescriptor { struct HidConfigDescriptor { struct usb_config_descriptor config; struct HidIadDescriptor iad_0; -} __attribute__((packed)); +} FURI_PACKED; /* HID report: FIDO U2F */ static const uint8_t hid_u2f_report_desc[] = { @@ -34,13 +34,13 @@ static const uint8_t hid_u2f_report_desc[] = { HID_COLLECTION(HID_APPLICATION_COLLECTION), HID_USAGE(HID_FIDO_INPUT), HID_LOGICAL_MINIMUM(0x00), - HID_LOGICAL_MAXIMUM(0xFF), + HID_RI_LOGICAL_MAXIMUM(16, 0xFF), HID_REPORT_SIZE(8), HID_REPORT_COUNT(HID_U2F_PACKET_LEN), HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), HID_USAGE(HID_FIDO_OUTPUT), HID_LOGICAL_MINIMUM(0x00), - HID_LOGICAL_MAXIMUM(0xFF), + HID_RI_LOGICAL_MAXIMUM(16, 0xFF), HID_REPORT_SIZE(8), HID_REPORT_COUNT(HID_U2F_PACKET_LEN), HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), diff --git a/firmware/targets/f7/furi_hal/furi_hal_version.c b/targets/f7/furi_hal/furi_hal_version.c similarity index 98% rename from firmware/targets/f7/furi_hal/furi_hal_version.c rename to targets/f7/furi_hal/furi_hal_version.c index 3053ef73a50..0b7ccae0cc6 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_version.c +++ b/targets/f7/furi_hal/furi_hal_version.c @@ -6,7 +6,7 @@ #include #include -#include "ble.h" +#include #include "flipper_format.h" #include "m-string.h" @@ -269,8 +269,6 @@ void furi_hal_version_load_custom_otp() { void furi_hal_version_init() { switch(furi_hal_version_get_otp_version()) { case FuriHalVersionOtpVersionUnknown: - furi_hal_version_load_otp_default(); - break; case FuriHalVersionOtpVersionEmpty: furi_hal_version_load_otp_default(); break; @@ -284,7 +282,7 @@ void furi_hal_version_init() { furi_hal_version_load_otp_v2(); break; default: - furi_crash(NULL); + furi_crash(); } furi_hal_rtc_set_register(FuriHalRtcRegisterVersion, (uint32_t)version_get()); @@ -292,14 +290,6 @@ void furi_hal_version_init() { FURI_LOG_I(TAG, "Init OK"); } -bool furi_hal_version_do_i_belong_here() { - return furi_hal_version_get_hw_target() == 7; -} - -const char* furi_hal_version_get_model_name() { - return "Flipper Zero"; -} - FuriHalVersionOtpVersion furi_hal_version_get_otp_version() { if(*(uint64_t*)FURI_HAL_VERSION_OTP_ADDRESS == 0xFFFFFFFF) { return FuriHalVersionOtpVersionEmpty; diff --git a/targets/f7/furi_hal/furi_hal_version_device.c b/targets/f7/furi_hal/furi_hal_version_device.c new file mode 100644 index 00000000000..be3f4bf3f3c --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_version_device.c @@ -0,0 +1,25 @@ +#include + +bool furi_hal_version_do_i_belong_here() { + return (furi_hal_version_get_hw_target() == 7) || (furi_hal_version_get_hw_target() == 0); +} + +const char* furi_hal_version_get_model_name() { + return "Flipper Zero"; +} + +const char* furi_hal_version_get_model_code() { + return "FZ.1"; +} + +const char* furi_hal_version_get_fcc_id() { + return "2A2V6-FZ"; +} + +const char* furi_hal_version_get_ic_id() { + return "27624-FZ"; +} + +const char* furi_hal_version_get_mic_id() { + return "210-175991"; +} diff --git a/targets/f7/furi_hal/furi_hal_vibro.c b/targets/f7/furi_hal/furi_hal_vibro.c new file mode 100644 index 00000000000..f4678467750 --- /dev/null +++ b/targets/f7/furi_hal/furi_hal_vibro.c @@ -0,0 +1,14 @@ +#include +#include + +#define TAG "FuriHalVibro" + +void furi_hal_vibro_init() { + furi_hal_gpio_init(&gpio_vibro, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_vibro, false); + FURI_LOG_I(TAG, "Init OK"); +} + +void furi_hal_vibro_on(bool value) { + furi_hal_gpio_write(&gpio_vibro, value); +} diff --git a/firmware/targets/f7/Inc/FreeRTOSConfig.h b/targets/f7/inc/FreeRTOSConfig.h similarity index 91% rename from firmware/targets/f7/Inc/FreeRTOSConfig.h rename to targets/f7/inc/FreeRTOSConfig.h index f54d774caaa..3bc57f8f3be 100644 --- a/firmware/targets/f7/Inc/FreeRTOSConfig.h +++ b/targets/f7/inc/FreeRTOSConfig.h @@ -3,18 +3,19 @@ #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) #include #pragma GCC diagnostic ignored "-Wredundant-decls" -extern uint32_t SystemCoreClock; #endif #ifndef CMSIS_device_header #define CMSIS_device_header "stm32wbxx.h" #endif /* CMSIS_device_header */ +#include CMSIS_device_header + #define configENABLE_FPU 1 #define configENABLE_MPU 0 #define configUSE_PREEMPTION 1 -#define configSUPPORT_STATIC_ALLOCATION 0 +#define configSUPPORT_STATIC_ALLOCATION 1 #define configSUPPORT_DYNAMIC_ALLOCATION 1 #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 @@ -26,7 +27,7 @@ extern uint32_t SystemCoreClock; /* Heap size determined automatically by linker */ // #define configTOTAL_HEAP_SIZE ((size_t)0) -#define configMAX_TASK_NAME_LEN (16) +#define configMAX_TASK_NAME_LEN (32) #define configGENERATE_RUN_TIME_STATS 0 #define configUSE_TRACE_FACILITY 1 #define configUSE_16_BIT_TICKS 0 @@ -58,6 +59,7 @@ extern uint32_t SystemCoreClock; #define configTIMER_SERVICE_TASK_NAME "TimersSrv" #define configIDLE_TASK_NAME "(-_-)" +#define configIDLE_TASK_STACK_DEPTH 128 /* Set the following definitions to 1 to include the API function, or zero to exclude the API function. */ @@ -76,19 +78,8 @@ to exclude the API function. */ #define INCLUDE_xTaskGetSchedulerState 1 #define INCLUDE_xTimerPendFunctionCall 1 -/* CMSIS-RTOS V2 flags */ -#define configUSE_OS2_THREAD_SUSPEND_RESUME 1 -#define configUSE_OS2_THREAD_ENUMERATE 1 -#define configUSE_OS2_THREAD_FLAGS 1 -#define configUSE_OS2_TIMER 1 -#define configUSE_OS2_MUTEX 1 - -// NEVER TO BE USED, because of their hard realtime nature -// #define configUSE_OS2_EVENTFLAGS_FROM_ISR 1 - -/* CMSIS-RTOS */ +/* Furi-specific */ #define configTASK_NOTIFICATION_ARRAY_ENTRIES 2 -#define CMSIS_TASK_NOTIFY_INDEX 1 extern __attribute__((__noreturn__)) void furi_thread_catch(); #define configTASK_RETURN_ADDRESS (furi_thread_catch + 2) @@ -149,3 +140,7 @@ standard names. */ #define traceTASK_SWITCHED_IN() \ extern void furi_hal_mpu_set_stack_protection(uint32_t* stack); \ furi_hal_mpu_set_stack_protection((uint32_t*)pxCurrentTCB->pxStack) + +#define portCLEAN_UP_TCB(pxTCB) \ + extern void furi_thread_cleanup_tcb_event(TaskHandle_t task); \ + furi_thread_cleanup_tcb_event(pxTCB) diff --git a/firmware/targets/f7/Inc/alt_boot.h b/targets/f7/inc/alt_boot.h similarity index 80% rename from firmware/targets/f7/Inc/alt_boot.h rename to targets/f7/inc/alt_boot.h index 91bf9bdf71d..d8be3aa487d 100644 --- a/firmware/targets/f7/Inc/alt_boot.h +++ b/targets/f7/inc/alt_boot.h @@ -8,6 +8,8 @@ void flipper_boot_update_exec(); void flipper_boot_dfu_exec(); +void flipper_boot_recovery_exec(); + #ifdef __cplusplus } #endif diff --git a/targets/f7/inc/furi_config.h b/targets/f7/inc/furi_config.h new file mode 100644 index 00000000000..c935611ab7b --- /dev/null +++ b/targets/f7/inc/furi_config.h @@ -0,0 +1,3 @@ +#pragma once + +#define FURI_CONFIG_THREAD_MAX_PRIORITIES (32) \ No newline at end of file diff --git a/targets/f7/inc/stm32.h b/targets/f7/inc/stm32.h new file mode 100644 index 00000000000..8e5cc379b5d --- /dev/null +++ b/targets/f7/inc/stm32.h @@ -0,0 +1,21 @@ +#ifndef _STM32_H_ +#define _STM32_H_ + +/* modify bitfield */ +#define _BMD(reg, msk, val) (reg) = (((reg) & ~(msk)) | (val)) +/* set bitfield */ +#define _BST(reg, bits) (reg) = ((reg) | (bits)) +/* clear bitfield */ +#define _BCL(reg, bits) (reg) = ((reg) & ~(bits)) +/* wait until bitfield set */ +#define _WBS(reg, bits) while(((reg) & (bits)) == 0) +/* wait until bitfield clear */ +#define _WBC(reg, bits) while(((reg) & (bits)) != 0) +/* wait for bitfield value */ +#define _WVL(reg, msk, val) while(((reg) & (msk)) != (val)) +/* bit value */ +#define _BV(bit) (0x01 << (bit)) + +#include "stm32wbxx.h" + +#endif // _STM32_H_ diff --git a/firmware/targets/f7/Inc/stm32_assert.h b/targets/f7/inc/stm32_assert.h similarity index 100% rename from firmware/targets/f7/Inc/stm32_assert.h rename to targets/f7/inc/stm32_assert.h diff --git a/targets/f7/platform_specific/intrinsic_export.h b/targets/f7/platform_specific/intrinsic_export.h new file mode 100644 index 00000000000..d3c7be5e042 --- /dev/null +++ b/targets/f7/platform_specific/intrinsic_export.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void __clear_cache(void*, void*); +void* __aeabi_uldivmod(uint64_t, uint64_t); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f7/platform_specific/math_wrapper.h b/targets/f7/platform_specific/math_wrapper.h new file mode 100644 index 00000000000..83f5a8b75d3 --- /dev/null +++ b/targets/f7/platform_specific/math_wrapper.h @@ -0,0 +1,2 @@ +#pragma once +#include \ No newline at end of file diff --git a/targets/f7/src/dfu.c b/targets/f7/src/dfu.c new file mode 100644 index 00000000000..7e094b4c47e --- /dev/null +++ b/targets/f7/src/dfu.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +void flipper_boot_dfu_show_splash() { + // Initialize + Canvas* canvas = canvas_init(); + + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + + canvas_draw_icon(canvas, 0, 64 - 50, &I_DFU_128x50); + canvas_draw_str(canvas, 2, 8, "Update & Recovery Mode"); + canvas_draw_str(canvas, 2, 21, "DFU Started"); + canvas_commit(canvas); + + canvas_free(canvas); +} + +void flipper_boot_dfu_exec() { + // Show DFU splashscreen + flipper_boot_dfu_show_splash(); + + // Errata 2.2.9, Flash OPTVERR flag is always set after system reset + WRITE_REG(FLASH->SR, FLASH_SR_OPTVERR); + + // Cleanup before jumping to DFU + furi_hal_deinit_early(); + + // Remap memory to system bootloader + LL_SYSCFG_SetRemapMemory(LL_SYSCFG_REMAP_SYSTEMFLASH); + // Jump + furi_hal_switch(0x0); +} diff --git a/firmware/targets/f7/Src/main.c b/targets/f7/src/main.c similarity index 80% rename from firmware/targets/f7/Src/main.c rename to targets/f7/src/main.c index 5f33569ae0f..ca705fe5e42 100644 --- a/firmware/targets/f7/Src/main.c +++ b/targets/f7/src/main.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #define TAG "Main" @@ -26,12 +25,11 @@ int main() { // Flipper critical FURI HAL furi_hal_init_early(); - FuriThread* main_thread = furi_thread_alloc(); - furi_thread_set_name(main_thread, "Init"); - furi_thread_set_stack_size(main_thread, 4096); - furi_thread_set_callback(main_thread, init_task); + FuriThread* main_thread = furi_thread_alloc_ex("Init", 4096, init_task, NULL); #ifdef FURI_RAM_EXEC + // Prevent entering sleep mode when executed from RAM + furi_hal_power_insomnia_enter(); furi_thread_start(main_thread); #else furi_hal_light_sequence("RGB"); @@ -47,11 +45,16 @@ int main() { furi_hal_power_reset(); } else if(boot_mode == FuriHalRtcBootModeUpdate) { furi_hal_light_sequence("rgb BR"); + // Do update flipper_boot_update_exec(); // if things go nice, we shouldn't reach this point. // But if we do, abandon to avoid bootloops furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); furi_hal_power_reset(); + } else if(!furi_hal_gpio_read(&gpio_button_up)) { + furi_hal_light_sequence("rgb WR"); + flipper_boot_recovery_exec(); + furi_hal_power_reset(); } else { furi_hal_light_sequence("rgb G"); furi_thread_start(main_thread); diff --git a/targets/f7/src/recovery.c b/targets/f7/src/recovery.c new file mode 100644 index 00000000000..49d780d478f --- /dev/null +++ b/targets/f7/src/recovery.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define COUNTER_VALUE (136U) + +static void flipper_boot_recovery_draw_progress(Canvas* canvas, size_t progress) { + if(progress < COUNTER_VALUE) { + // Fill the progress bar while the progress is going down + canvas_draw_rframe(canvas, 59, 41, 69, 8, 2); + size_t width = (COUNTER_VALUE - progress) * 68 / COUNTER_VALUE; + canvas_draw_box(canvas, 60, 42, width, 6); + } else { + canvas_draw_rframe(canvas, 59, 41, 69, 8, 2); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 60, 42, 67, 6); + canvas_set_color(canvas, ColorBlack); + } + + canvas_commit(canvas); +} + +void flipper_boot_recovery_draw_splash(Canvas* canvas) { + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + + canvas_draw_icon(canvas, 0, 0, &I_Erase_pin_128x64); + + canvas_commit(canvas); +} + +void flipper_boot_recovery_exec() { + Canvas* canvas = canvas_init(); + + // Show recovery splashscreen + flipper_boot_recovery_draw_splash(canvas); + + size_t counter = COUNTER_VALUE; + while(counter) { + if(!furi_hal_gpio_read(&gpio_button_down)) { + break; + } + + if(!furi_hal_gpio_read(&gpio_button_right)) { + counter--; + } else { + counter = COUNTER_VALUE; + } + + flipper_boot_recovery_draw_progress(canvas, counter); + } + + if(!counter) { + furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); + furi_hal_rtc_set_pin_fails(0); + furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); + } +} diff --git a/firmware/targets/f7/Src/system_stm32wbxx.c b/targets/f7/src/system_stm32wbxx.c similarity index 100% rename from firmware/targets/f7/Src/system_stm32wbxx.c rename to targets/f7/src/system_stm32wbxx.c diff --git a/targets/f7/src/update.c b/targets/f7/src/update.c new file mode 100644 index 00000000000..e9228a6e956 --- /dev/null +++ b/targets/f7/src/update.c @@ -0,0 +1,209 @@ +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#define UPDATE_POINTER_FILE_PATH "/" UPDATE_MANIFEST_POINTER_FILE_NAME + +static FATFS* pfs = NULL; + +#define CHECK_FRESULT(result) \ + { \ + if((result) != FR_OK) { \ + return false; \ + } \ + } + +static bool flipper_update_mount_sd() { + for(int i = 0; i < furi_hal_sd_max_mount_retry_count(); ++i) { + if(furi_hal_sd_init((i % 2) == 0) != FuriStatusOk) { + /* Next attempt will be without card reset, let it settle */ + furi_delay_ms(1000); + continue; + } + + if(f_mount(pfs, "/", 1) == FR_OK) { + return true; + } + } + return false; +} + +static bool flipper_update_init() { + // TODO FL-3504: Configure missing peripherals properly + furi_hal_bus_enable(FuriHalBusHSEM); + furi_hal_bus_enable(FuriHalBusIPCC); + furi_hal_bus_enable(FuriHalBusRNG); + furi_hal_bus_enable(FuriHalBusUSART1); + + furi_hal_clock_init(); + furi_hal_rtc_init(); + furi_hal_interrupt_init(); + + furi_hal_spi_config_init(); + + fatfs_init(); + if(!furi_hal_sd_is_present()) { + return false; + } + + pfs = malloc(sizeof(FATFS)); + + return flipper_update_mount_sd(); +} + +static bool flipper_update_load_stage(const FuriString* work_dir, UpdateManifest* manifest) { + FIL file; + FILINFO stat; + + FuriString* loader_img_path; + loader_img_path = furi_string_alloc_set(work_dir); + path_append(loader_img_path, furi_string_get_cstr(manifest->staged_loader_file)); + + if((f_stat(furi_string_get_cstr(loader_img_path), &stat) != FR_OK) || + (f_open(&file, furi_string_get_cstr(loader_img_path), FA_OPEN_EXISTING | FA_READ) != + FR_OK) || + (stat.fsize == 0)) { + furi_string_free(loader_img_path); + return false; + } + furi_string_free(loader_img_path); + + void* img = malloc(stat.fsize); + uint32_t bytes_read = 0; + const uint16_t MAX_READ = 0xFFFF; + + uint32_t crc = 0; + do { + uint16_t size_read = 0; + if(f_read(&file, img + bytes_read, MAX_READ, &size_read) != FR_OK) { //-V769 + break; + } + crc = crc32_calc_buffer(crc, img + bytes_read, size_read); + bytes_read += size_read; + } while(bytes_read == MAX_READ); + + do { + if((bytes_read != stat.fsize) || (crc != manifest->staged_loader_crc)) { + break; + } + + /* Point of no return. Literally + * + * NB: we MUST disable IRQ, otherwise handlers from flash + * will change global variables (like tick count) + * that are located in .data. And we move staged loader + * to the same memory region. So, IRQ handlers will mess up + * memmove'd .text section and ruin your day. + * We don't want that to happen. + */ + __disable_irq(); + + memmove((void*)(SRAM1_BASE), img, stat.fsize); + LL_SYSCFG_SetRemapMemory(LL_SYSCFG_REMAP_SRAM); + furi_hal_switch((void*)SRAM1_BASE); + return true; + + } while(false); + + free(img); + return false; +} + +static bool flipper_update_get_manifest_path(FuriString* out_path) { + FIL file; + FILINFO stat; + uint16_t size_read = 0; + char manifest_name_buf[UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN] = {0}; + + furi_string_reset(out_path); + CHECK_FRESULT(f_stat(UPDATE_POINTER_FILE_PATH, &stat)); + CHECK_FRESULT(f_open(&file, UPDATE_POINTER_FILE_PATH, FA_OPEN_EXISTING | FA_READ)); + do { + if(f_read(&file, manifest_name_buf, UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN, &size_read) != + FR_OK) { + break; + } + + if((size_read == 0) || (size_read == UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN)) { + break; + } + furi_string_set(out_path, manifest_name_buf); + furi_string_right(out_path, strlen(STORAGE_EXT_PATH_PREFIX)); + } while(0); + f_close(&file); + return !furi_string_empty(out_path); +} + +static UpdateManifest* flipper_update_process_manifest(const FuriString* manifest_path) { + FIL file; + FILINFO stat; + + CHECK_FRESULT(f_stat(furi_string_get_cstr(manifest_path), &stat)); + CHECK_FRESULT(f_open(&file, furi_string_get_cstr(manifest_path), FA_OPEN_EXISTING | FA_READ)); + + uint8_t* manifest_data = malloc(stat.fsize); + uint32_t bytes_read = 0; + const uint16_t MAX_READ = 0xFFFF; + + do { + uint16_t size_read = 0; + if(f_read(&file, manifest_data + bytes_read, MAX_READ, &size_read) != FR_OK) { //-V769 + break; + } + bytes_read += size_read; + } while(bytes_read == MAX_READ); + + UpdateManifest* manifest = NULL; + do { + if(bytes_read != stat.fsize) { + break; + } + + manifest = update_manifest_alloc(); + if(!update_manifest_init_mem(manifest, manifest_data, bytes_read)) { + update_manifest_free(manifest); + manifest = NULL; + } + } while(false); + + f_close(&file); + free(manifest_data); + return manifest; +} + +void flipper_boot_update_exec() { + if(!flipper_update_init()) { + return; + } + + FuriString* work_dir = furi_string_alloc(); + FuriString* manifest_path = furi_string_alloc(); + + do { + if(!flipper_update_get_manifest_path(manifest_path)) { + break; + } + + UpdateManifest* manifest = flipper_update_process_manifest(manifest_path); + if(!manifest) { + break; + } + + path_extract_dirname(furi_string_get_cstr(manifest_path), work_dir); + if(!flipper_update_load_stage(work_dir, manifest)) { + update_manifest_free(manifest); + } + } while(false); + furi_string_free(manifest_path); + furi_string_free(work_dir); + free(pfs); +} diff --git a/firmware/targets/f7/startup_stm32wb55xx_cm4.s b/targets/f7/startup_stm32wb55xx_cm4.s similarity index 100% rename from firmware/targets/f7/startup_stm32wb55xx_cm4.s rename to targets/f7/startup_stm32wb55xx_cm4.s diff --git a/firmware/targets/f7/stm32wb55xx_flash.ld b/targets/f7/stm32wb55xx_flash.ld similarity index 89% rename from firmware/targets/f7/stm32wb55xx_flash.ld rename to targets/f7/stm32wb55xx_flash.ld index 20314ba3c8c..df4c5b72662 100644 --- a/firmware/targets/f7/stm32wb55xx_flash.ld +++ b/targets/f7/stm32wb55xx_flash.ld @@ -1,7 +1,7 @@ /** ***************************************************************************** ** -** File : stm32wb55xx_flash_cm4.ld +** File : stm32wb55xx_flash.ld ** ** Abstract : System Workbench Minimal System calls file ** @@ -57,7 +57,8 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K RAM1 (xrw) : ORIGIN = 0x20000008, LENGTH = 0x2FFF8 -RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K +RAM2A (xrw) : ORIGIN = 0x20030000, LENGTH = 10K +RAM2B (xrw) : ORIGIN = 0x20038000, LENGTH = 10K } /* Define output sections */ @@ -130,7 +131,7 @@ SECTIONS _sidata = LOADADDR(.data); /* Initialized data sections goes into RAM, load LMA copy after code */ - .data : + .data : { . = ALIGN(4); _sdata = .; /* create a global symbol at data start */ @@ -142,7 +143,6 @@ SECTIONS _edata = .; /* define a global symbol at data end */ } >RAM1 AT> FLASH - /* Uninitialized data section */ . = ALIGN(4); .bss : @@ -160,7 +160,8 @@ SECTIONS } >RAM1 /* User_heap_stack section, used to check that there is enough RAM left */ - ._user_heap_stack(NOLOAD): + /* DSECT: https://software-dl.ti.com/ccs/esd/documents/sdto_cgt_linker_special_section_types.html */ + ._user_heap_stack(DSECT): { . = ALIGN(8); __heap_start__ = .; @@ -171,11 +172,11 @@ SECTIONS } >RAM1 /* Free Flash space, that can be used for internal storage */ - .free_flash(NOLOAD): + .free_flash(DSECT): { __free_flash_start__ = .; . = ORIGIN(FLASH) + LENGTH(FLASH); - } >FLASH + } >FLASH /* Remove information from the standard libraries */ /DISCARD/ : @@ -186,9 +187,12 @@ SECTIONS } .ARM.attributes 0 : { *(.ARM.attributes) } - MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM_SHARED - MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM_SHARED - MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM_SHARED + ._sram2a_start : { . = ALIGN(4); __sram2a_start__ = .; } >RAM2A + MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM2A + MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM2A + MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM2A + ._sram2a_free : { . = ALIGN(4); __sram2a_free__ = .; } >RAM2A + ._sram2b_start : { . = ALIGN(4); __sram2b_start__ = .; } >RAM2B } diff --git a/firmware/targets/f7/stm32wb55xx_ram_fw.ld b/targets/f7/stm32wb55xx_ram_fw.ld similarity index 91% rename from firmware/targets/f7/stm32wb55xx_ram_fw.ld rename to targets/f7/stm32wb55xx_ram_fw.ld index 8c4d41c5b2a..0ac9be4df81 100644 --- a/firmware/targets/f7/stm32wb55xx_ram_fw.ld +++ b/targets/f7/stm32wb55xx_ram_fw.ld @@ -1,7 +1,7 @@ /** ***************************************************************************** ** -** File : stm32wb55xx_flash_cm4.ld +** File : stm32wb55xx_ram_fw.ld ** ** Abstract : System Workbench Minimal System calls file ** @@ -57,7 +57,8 @@ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K RAM1 (xrw) : ORIGIN = 0x20000000, LENGTH = 0x30000 -RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K +RAM2A (xrw) : ORIGIN = 0x20030000, LENGTH = 10K +RAM2B (xrw) : ORIGIN = 0x20038000, LENGTH = 10K } /* Define output sections */ @@ -158,7 +159,7 @@ SECTIONS } >RAM1 /* User_heap_stack section, used to check that there is enough RAM left */ - ._user_heap_stack : + ._user_heap_stack(DSECT) : { . = ALIGN(8); __heap_start__ = .; @@ -169,7 +170,7 @@ SECTIONS } >RAM1 /* Free Flash space, that can be used for internal storage */ - /*.free_flash(NOLOAD): + /*.free_flash(DSECT): { __free_flash_start__ = .; . = ORIGIN(FLASH) + LENGTH(FLASH); @@ -184,9 +185,12 @@ SECTIONS } .ARM.attributes 0 : { *(.ARM.attributes) } - MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM_SHARED - MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM_SHARED - MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM_SHARED + ._sram2a_start : { . = ALIGN(4); __sram2a_start__ = .; } >RAM2A + MAPPING_TABLE (NOLOAD) : { *(MAPPING_TABLE) } >RAM2A + MB_MEM1 (NOLOAD) : { *(MB_MEM1) } >RAM2A + MB_MEM2 (NOLOAD) : { _sMB_MEM2 = . ; *(MB_MEM2) ; _eMB_MEM2 = . ; } >RAM2A + ._sram2a_free : { . = ALIGN(4); __sram2a_free__ = .; } >RAM2A + ._sram2b_start : { . = ALIGN(4); __sram2b_start__ = .; } >RAM2B } diff --git a/targets/f7/target.json b/targets/f7/target.json new file mode 100644 index 00000000000..7a816828c78 --- /dev/null +++ b/targets/f7/target.json @@ -0,0 +1,52 @@ +{ + "include_paths": [ + "ble_glue", + "fatfs", + "furi_hal", + "inc" + ], + "sdk_header_paths": [ + "../furi_hal_include", + "furi_hal", + "platform_specific" + ], + "startup_script": "startup_stm32wb55xx_cm4.s", + "linker_script_flash": "stm32wb55xx_flash.ld", + "linker_script_ram": "stm32wb55xx_ram_fw.ld", + "linker_script_app": "application_ext.ld", + "sdk_symbols": "api_symbols.csv", + "linker_dependencies": [ + "print", + "flipper7", + "furi", + "freertos", + "stm32wb", + "hwdrivers", + "fatfs", + "littlefs", + "subghz", + "toolbox", + "nfc", + "digital_signal", + "pulse_reader", + "signal_reader", + "microtar", + "usb_stm32", + "infrared", + "appframe", + "assets", + "one_wire", + "ibutton", + "music_worker", + "mbedtls", + "lfrfid", + "flipper_application", + "toolbox", + "u8g2", + "nanopb", + "update_util", + "heatshrink", + "flipperformat", + "flipper7" + ] +} \ No newline at end of file diff --git a/targets/furi_hal_include/furi_hal.h b/targets/furi_hal_include/furi_hal.h new file mode 100644 index 00000000000..e6fd9eb1cc0 --- /dev/null +++ b/targets/furi_hal_include/furi_hal.h @@ -0,0 +1,65 @@ +/** + * @file furi_hal.h + * Furi HAL API + */ + +#pragma once + +#ifdef __cplusplus +template +struct STOP_EXTERNING_ME {}; +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Early FuriHal init, only essential subsystems */ +void furi_hal_init_early(); + +/** Early FuriHal deinit */ +void furi_hal_deinit_early(); + +/** Init FuriHal */ +void furi_hal_init(); + +/** Transfer execution to address + * + * @param[in] address pointer to new executable + */ +void furi_hal_switch(void* address); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/targets/furi_hal_include/furi_hal_bt.h similarity index 91% rename from firmware/targets/furi_hal_include/furi_hal_bt.h rename to targets/furi_hal_include/furi_hal_bt.h index 3f1169d1675..4d538265dbb 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/targets/furi_hal_include/furi_hal_bt.h @@ -5,14 +5,14 @@ #pragma once -#include +#include #include #include -#include +#include #include #include -#include "furi_hal_bt_serial.h" +#include #define FURI_HAL_BT_STACK_VERSION_MAJOR (1) #define FURI_HAL_BT_STACK_VERSION_MINOR (12) @@ -122,9 +122,9 @@ void furi_hal_bt_stop_advertising(); /** Get BT/BLE system component state * - * @param[in] buffer string_t buffer to write to + * @param[in] buffer FuriString* buffer to write to */ -void furi_hal_bt_dump_state(string_t buffer); +void furi_hal_bt_dump_state(FuriString* buffer); /** Get BT/BLE system component state * @@ -224,6 +224,19 @@ uint32_t furi_hal_bt_get_transmitted_packets(); */ bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode); +typedef struct { + uint32_t magic; + uint32_t source_pc; + uint32_t source_lr; + uint32_t source_sp; +} FuriHalBtHardfaultInfo; + +/** Get hardfault info + * + * @return hardfault info. NULL if no hardfault + */ +const FuriHalBtHardfaultInfo* furi_hal_bt_get_hardfault_info(); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/furi_hal_include/furi_hal_bt_hid.h b/targets/furi_hal_include/furi_hal_bt_hid.h similarity index 96% rename from firmware/targets/furi_hal_include/furi_hal_bt_hid.h rename to targets/furi_hal_include/furi_hal_bt_hid.h index e35edd01c06..4e74bbda751 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt_hid.h +++ b/targets/furi_hal_include/furi_hal_bt_hid.h @@ -3,6 +3,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + /** Start Hid Keyboard Profile */ void furi_hal_bt_hid_start(); @@ -81,3 +85,7 @@ bool furi_hal_bt_hid_consumer_key_release(uint16_t button); * @param button key code */ bool furi_hal_bt_hid_consumer_key_release_all(); + +#ifdef __cplusplus +} +#endif diff --git a/targets/furi_hal_include/furi_hal_bt_serial.h b/targets/furi_hal_include/furi_hal_bt_serial.h new file mode 100644 index 00000000000..0472d31d181 --- /dev/null +++ b/targets/furi_hal_include/furi_hal_bt_serial.h @@ -0,0 +1,59 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define FURI_HAL_BT_SERIAL_PACKET_SIZE_MAX SERIAL_SVC_DATA_LEN_MAX + +typedef enum { + FuriHalBtSerialRpcStatusNotActive, + FuriHalBtSerialRpcStatusActive, +} FuriHalBtSerialRpcStatus; + +/** Serial service callback type */ +typedef SerialServiceEventCallback FuriHalBtSerialCallback; + +/** Start Serial Profile + */ +void furi_hal_bt_serial_start(); + +/** Stop Serial Profile + */ +void furi_hal_bt_serial_stop(); + +/** Set Serial service events callback + * + * @param buffer_size Applicaition buffer size + * @param calback FuriHalBtSerialCallback instance + * @param context pointer to context + */ +void furi_hal_bt_serial_set_event_callback( + uint16_t buff_size, + FuriHalBtSerialCallback callback, + void* context); + +/** Set BLE RPC status + * + * @param status FuriHalBtSerialRpcStatus instance + */ +void furi_hal_bt_serial_set_rpc_status(FuriHalBtSerialRpcStatus status); + +/** Notify that application buffer is empty + */ +void furi_hal_bt_serial_notify_buffer_is_empty(); + +/** Send data through BLE + * + * @param data data buffer + * @param size data buffer size + * + * @return true on success + */ +bool furi_hal_bt_serial_tx(uint8_t* data, uint16_t size); + +#ifdef __cplusplus +} +#endif diff --git a/targets/furi_hal_include/furi_hal_cortex.h b/targets/furi_hal_include/furi_hal_cortex.h new file mode 100644 index 00000000000..ebabbabfd8f --- /dev/null +++ b/targets/furi_hal_include/furi_hal_cortex.h @@ -0,0 +1,108 @@ +/** + * @file furi_hal_cortex.h + * ARM Cortex HAL + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Cortex timer provides high precision low level expiring timer */ +typedef struct { + uint32_t start; + uint32_t value; +} FuriHalCortexTimer; + +/** Early init stage for cortex + */ +void furi_hal_cortex_init_early(); + +/** Microseconds delay + * + * @param[in] microseconds The microseconds to wait + */ +void furi_hal_cortex_delay_us(uint32_t microseconds); + +/** Get instructions per microsecond count + * + * @return instructions per microsecond count + */ +uint32_t furi_hal_cortex_instructions_per_microsecond(); + +/** Get Timer + * + * @param[in] timeout_us The expire timeout in us + * + * @return The FuriHalCortexTimer + */ +FuriHalCortexTimer furi_hal_cortex_timer_get(uint32_t timeout_us); + +/** Check if timer expired + * + * @param[in] cortex_timer The FuriHalCortexTimer + * + * @return true if expired + */ +bool furi_hal_cortex_timer_is_expired(FuriHalCortexTimer cortex_timer); + +/** Wait for timer expire + * + * @param[in] cortex_timer The FuriHalCortexTimer + */ +void furi_hal_cortex_timer_wait(FuriHalCortexTimer cortex_timer); + +typedef enum { + FuriHalCortexComp0, + FuriHalCortexComp1, + FuriHalCortexComp2, + FuriHalCortexComp3, +} FuriHalCortexComp; + +typedef enum { + FuriHalCortexCompSizeWord = 0b10, + FuriHalCortexCompSizeHalfWord = 0b01, + FuriHalCortexCompSizeByte = 0b00, +} FuriHalCortexCompSize; + +typedef enum { + FuriHalCortexCompFunctionPC = 0b100, + FuriHalCortexCompFunctionRead = 0b101, + FuriHalCortexCompFunctionWrite = 0b110, + FuriHalCortexCompFunctionReadWrite = 0b110, +} FuriHalCortexCompFunction; + +/** Enable DWT comparator + * + * Allows to programmatically set instruction/data breakpoints. + * + * More details on how it works can be found in armv7m official documentation: + * https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/The-Data-Watchpoint-and-Trace-unit/The-DWT-comparators + * https://developer.arm.com/documentation/ddi0403/d/Debug-Architecture/ARMv7-M-Debug/The-Data-Watchpoint-and-Trace-unit/Comparator-Function-registers--DWT-FUNCTIONn + * + * @param[in] comp The Comparator + * @param[in] function The Comparator Function to use + * @param[in] value The value + * @param[in] mask The mask + * @param[in] size The size + */ +void furi_hal_cortex_comp_enable( + FuriHalCortexComp comp, + FuriHalCortexCompFunction function, + uint32_t value, + uint32_t mask, + FuriHalCortexCompSize size); + +/** Reset DWT comparator + * + * @param[in] comp The Comparator + */ +void furi_hal_cortex_comp_reset(FuriHalCortexComp comp); + +#ifdef __cplusplus +} +#endif diff --git a/targets/furi_hal_include/furi_hal_crypto.h b/targets/furi_hal_include/furi_hal_crypto.h new file mode 100644 index 00000000000..5e3b4104041 --- /dev/null +++ b/targets/furi_hal_include/furi_hal_crypto.h @@ -0,0 +1,295 @@ +/** + * @file furi_hal_crypto.h + * + * Cryptography HAL API + * + * !!! READ THIS FIRST !!! + * + * Flipper was never designed to be secure, nor it passed cryptography audit. + * Despite of the fact that keys are stored in secure enclave there are some + * types of attack that can be performed against AES engine to recover + * keys(theoretical). Also there is no way to securely deliver user keys to + * device and never will be. In addition device is fully open and there is no + * way to guarantee safety of your data, it can be easily dumped with debugger + * or modified code. + * + * Secure enclave on WB series is implemented on core2 FUS side and can be used + * only if core2 alive. Enclave is responsible for storing, loading and + * unloading keys to and from enclave/AES in secure manner(AES engine key + * registers will be locked when key from enclave loaded) + * + * There are 11 keys that we provision at factory: + * - 0 - Master key for secure key delivery. Impossible to use for anything but + * key provisioning. We don't plan to use it too. + * - 1 - 10 - Keys used by firmware. All devices got the same set of keys. You + * also can use them in your applications. + * + * Also there is a slot 11 that we use for device unique key. This slot is + * intentionally left blank till the moment of first use, so you can ensure that + * we don't know your unique key. Also you can provision this key by your self + * with crypto cli or API. + * + * Other slots can be used for your needs. But since enclave is sequential + * append only, we can not guarantee you that slots you want are free. NEVER USE + * THEM FOR PUBLIC APPLICATIONS. + * + * Also you can directly load raw keys into AES engine and use it for your + * needs. + */ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Factory provisioned master key slot. Should never be used. */ +#define FURI_HAL_CRYPTO_ENCLAVE_MASTER_KEY_SLOT (0u) + +/** Factory provisioned keys slot range. All of them are exactly same on all flippers. */ +#define FURI_HAL_CRYPTO_ENCLAVE_FACTORY_KEY_SLOT_START (1u) +#define FURI_HAL_CRYPTO_ENCLAVE_FACTORY_KEY_SLOT_END (10u) + +/** Device unique key slot. This key generated on first use or provisioned by user. Use furi_hal_crypto_enclave_ensure_key before using this slot. */ +#define FURI_HAL_CRYPTO_ENCLAVE_UNIQUE_KEY_SLOT (11u) + +/** User key slot range. This slots can be used for your needs, but never use them in public apps. */ +#define FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_START (12u) +#define FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_END (100u) + +/** [Deprecated] Indicates availability of advanced crypto functions, will be dropped before v1.0 */ +#define FURI_HAL_CRYPTO_ADVANCED_AVAIL 1 + +/** FuriHalCryptoKey Type */ +typedef enum { + FuriHalCryptoKeyTypeMaster, /**< Master key */ + FuriHalCryptoKeyTypeSimple, /**< Simple unencrypted key */ + FuriHalCryptoKeyTypeEncrypted, /**< Encrypted with Master key */ +} FuriHalCryptoKeyType; + +/** FuriHalCryptoKey Size in bits */ +typedef enum { + FuriHalCryptoKeySize128, + FuriHalCryptoKeySize256, +} FuriHalCryptoKeySize; + +/** FuriHalCryptoKey */ +typedef struct { + FuriHalCryptoKeyType type; + FuriHalCryptoKeySize size; + uint8_t* data; +} FuriHalCryptoKey; + +/** FuriHalCryptoGCMState Result of a GCM operation */ +typedef enum { + FuriHalCryptoGCMStateOk, /**< operation successful */ + FuriHalCryptoGCMStateError, /**< error during encryption/decryption */ + FuriHalCryptoGCMStateAuthFailure, /**< tags do not match, auth failed */ +} FuriHalCryptoGCMState; + +/** Initialize cryptography layer(includes AES engines, PKA and RNG) */ +void furi_hal_crypto_init(); + +/** Verify factory provisioned keys + * + * @param keys_nb The keys number of + * @param valid_keys_nb The valid keys number of + * + * @return true if all enclave keys are intact, false otherwise + */ +bool furi_hal_crypto_enclave_verify(uint8_t* keys_nb, uint8_t* valid_keys_nb); + +/** Ensure that requested slot and slots before this slot contains keys. + * + * This function is used to provision FURI_HAL_CRYPTO_ENCLAVE_UNIQUE_KEY_SLOT. Also you + * may want to use it to generate some unique keys in user key slot range. + * + * @warning Because of the sequential nature of the secure enclave this + * method will generate key for all slots from + * FURI_HAL_CRYPTO_ENCLAVE_FACTORY_KEY_SLOT_END to the slot your requested. + * Keys are generated using on-chip RNG. + * + * @param[in] key_slot The key slot to enclave + * + * @return true if key exists or created, false if enclave corrupted + */ +bool furi_hal_crypto_enclave_ensure_key(uint8_t key_slot); + +/** Store key in crypto enclave + * + * @param key FuriHalCryptoKey to be stored + * @param slot pointer to int where enclave slot will be stored + * + * @return true on success + */ +bool furi_hal_crypto_enclave_store_key(FuriHalCryptoKey* key, uint8_t* slot); + +/** Init AES engine and load key from crypto enclave + * + * @warning Use only with furi_hal_crypto_enclave_unload_key() + * + * @param slot enclave slot + * @param[in] iv pointer to 16 bytes Initialization Vector data + * + * @return true on success + */ +bool furi_hal_crypto_enclave_load_key(uint8_t slot, const uint8_t* iv); + +/** Unload key and deinit AES engine + * + * @warning Use only with furi_hal_crypto_enclave_load_key() + * + * @param slot enclave slot + * + * @return true on success + */ +bool furi_hal_crypto_enclave_unload_key(uint8_t slot); + +/** Init AES engine and load supplied key + * + * @warning Use only with furi_hal_crypto_unload_key() + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 16 bytes Initialization Vector data + * + * @return true on success + */ +bool furi_hal_crypto_load_key(const uint8_t* key, const uint8_t* iv); + +/** Unload key and de-init AES engine + * + * @warning Use this function only with furi_hal_crypto_load_key() + * + * @return true on success + */ +bool furi_hal_crypto_unload_key(void); + +/** Encrypt data + * + * @param input pointer to input data + * @param output pointer to output data + * @param size input/output buffer size in bytes + * + * @return true on success + */ +bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size); + +/** Decrypt data + * + * @param input pointer to input data + * @param output pointer to output data + * @param size input/output buffer size in bytes + * + * @return true on success + */ +bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size); + +/** Encrypt the input using AES-CTR + * + * Decryption can be performed by supplying the ciphertext as input. Inits and + * deinits the AES engine internally. + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 12 bytes Initialization Vector data + * @param[in] input pointer to input data + * @param[out] output pointer to output data + * @param length length of the input and output in bytes + * + * @return true on success + */ +bool furi_hal_crypto_ctr( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* input, + uint8_t* output, + size_t length); + +/** Encrypt/decrypt the input using AES-GCM + * + * When decrypting the tag generated needs to be compared to the tag attached to + * the ciphertext in a constant-time fashion. If the tags are not equal, the + * decryption failed and the plaintext returned needs to be discarded. Inits and + * deinits the AES engine internally. + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 12 bytes Initialization Vector data + * @param[in] aad pointer to additional authentication data + * @param aad_length length of the additional authentication data in bytes + * @param[in] input pointer to input data + * @param[out] output pointer to output data + * @param length length of the input and output in bytes + * @param[out] tag pointer to 16 bytes space for the tag + * @param decrypt true for decryption, false otherwise + * + * @return true on success + */ +bool furi_hal_crypto_gcm( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + uint8_t* tag, + bool decrypt); + +/** Encrypt the input using AES-GCM and generate a tag + * + * Inits and deinits the AES engine internally. + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 12 bytes Initialization Vector data + * @param[in] aad pointer to additional authentication data + * @param aad_length length of the additional authentication data in bytes + * @param[in] input pointer to input data + * @param[out] output pointer to output data + * @param length length of the input and output in bytes + * @param[out] tag pointer to 16 bytes space for the tag + * + * @return FuriHalCryptoGCMStateOk on success, FuriHalCryptoGCMStateError on + * failure + */ +FuriHalCryptoGCMState furi_hal_crypto_gcm_encrypt_and_tag( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + uint8_t* tag); + +/** Decrypt the input using AES-GCM and verify the provided tag + * + * Inits and deinits the AES engine internally. + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 12 bytes Initialization Vector data + * @param[in] aad pointer to additional authentication data + * @param aad_length length of the additional authentication data in bytes + * @param[in] input pointer to input data + * @param[out] output pointer to output data + * @param length length of the input and output in bytes + * @param[out] tag pointer to 16 bytes tag + * + * @return FuriHalCryptoGCMStateOk on success, FuriHalCryptoGCMStateError on + * failure, FuriHalCryptoGCMStateAuthFailure if the tag does not + * match + */ +FuriHalCryptoGCMState furi_hal_crypto_gcm_decrypt_and_verify( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + const uint8_t* tag); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_debug.h b/targets/furi_hal_include/furi_hal_debug.h similarity index 76% rename from firmware/targets/furi_hal_include/furi_hal_debug.h rename to targets/furi_hal_include/furi_hal_debug.h index 88397bbbafb..befbb4f40cb 100644 --- a/firmware/targets/furi_hal_include/furi_hal_debug.h +++ b/targets/furi_hal_include/furi_hal_debug.h @@ -18,6 +18,9 @@ void furi_hal_debug_enable(); /** Disable MCU debug */ void furi_hal_debug_disable(); +/** Check if GDB debug session is active */ +bool furi_hal_debug_is_gdb_session_active(); + #ifdef __cplusplus } #endif diff --git a/targets/furi_hal_include/furi_hal_i2c.h b/targets/furi_hal_include/furi_hal_i2c.h new file mode 100644 index 00000000000..f493655b4d2 --- /dev/null +++ b/targets/furi_hal_include/furi_hal_i2c.h @@ -0,0 +1,287 @@ +/** + * @file furi_hal_i2c.h I2C HAL API + */ + +#pragma once + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Transaction beginning signal */ +typedef enum { + /*!Begin the transaction by sending a START condition followed by the + * address */ + FuriHalI2cBeginStart, + /*!Begin the transaction by sending a RESTART condition followed by the + * address + * @note Must follow a transaction ended with + * FuriHalI2cEndAwaitRestart */ + FuriHalI2cBeginRestart, + /*!Continue the previous transaction with new data + * @note Must follow a transaction ended with FuriHalI2cEndPause and + * be of the same type (RX/TX) */ + FuriHalI2cBeginResume, +} FuriHalI2cBegin; + +/** Transaction end signal */ +typedef enum { + /*!End the transaction by sending a STOP condition */ + FuriHalI2cEndStop, + /*!End the transaction by clock stretching + * @note Must be followed by a transaction using + * FuriHalI2cBeginRestart */ + FuriHalI2cEndAwaitRestart, + /*!Pauses the transaction by clock stretching + * @note Must be followed by a transaction using FuriHalI2cBeginResume */ + FuriHalI2cEndPause, +} FuriHalI2cEnd; + +/** Early Init I2C */ +void furi_hal_i2c_init_early(); + +/** Early DeInit I2C */ +void furi_hal_i2c_deinit_early(); + +/** Init I2C */ +void furi_hal_i2c_init(); + +/** Acquire I2C bus handle + * + * @param handle Pointer to FuriHalI2cBusHandle instance + */ +void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle); + +/** Release I2C bus handle + * + * @param handle Pointer to FuriHalI2cBusHandle instance acquired in + * `furi_hal_i2c_acquire` + */ +void furi_hal_i2c_release(FuriHalI2cBusHandle* handle); + +/** Perform I2C TX transfer + * + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param address I2C slave address + * @param data Pointer to data buffer + * @param size Size of data buffer + * @param timeout Timeout in milliseconds + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_tx( + FuriHalI2cBusHandle* handle, + uint8_t address, + const uint8_t* data, + size_t size, + uint32_t timeout); + +/** + * Perform I2C TX transfer, with additional settings. + * + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param address I2C slave address + * @param ten_bit Whether the address is 10 bits wide + * @param data Pointer to data buffer + * @param size Size of data buffer + * @param begin How to begin the transaction + * @param end How to end the transaction + * @param timer Timeout timer + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_tx_ext( + FuriHalI2cBusHandle* handle, + uint16_t address, + bool ten_bit, + const uint8_t* data, + size_t size, + FuriHalI2cBegin begin, + FuriHalI2cEnd end, + uint32_t timeout); + +/** Perform I2C RX transfer + * + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param address I2C slave address + * @param data Pointer to data buffer + * @param size Size of data buffer + * @param timeout Timeout in milliseconds + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_rx( + FuriHalI2cBusHandle* handle, + uint8_t address, + uint8_t* data, + size_t size, + uint32_t timeout); + +/** Perform I2C RX transfer, with additional settings. + * + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param address I2C slave address + * @param ten_bit Whether the address is 10 bits wide + * @param data Pointer to data buffer + * @param size Size of data buffer + * @param begin How to begin the transaction + * @param end How to end the transaction + * @param timer Timeout timer + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_rx_ext( + FuriHalI2cBusHandle* handle, + uint16_t address, + bool ten_bit, + uint8_t* data, + size_t size, + FuriHalI2cBegin begin, + FuriHalI2cEnd end, + uint32_t timeout); + +/** Perform I2C TX and RX transfers + * + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param address I2C slave address + * @param tx_data Pointer to TX data buffer + * @param tx_size Size of TX data buffer + * @param rx_data Pointer to RX data buffer + * @param rx_size Size of RX data buffer + * @param timeout Timeout in milliseconds + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_trx( + FuriHalI2cBusHandle* handle, + uint8_t address, + const uint8_t* tx_data, + size_t tx_size, + uint8_t* rx_data, + size_t rx_size, + uint32_t timeout); + +/** Check if I2C device presents on bus + * + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param timeout Timeout in milliseconds + * + * @return true if device present and is ready, false otherwise + */ +bool furi_hal_i2c_is_device_ready(FuriHalI2cBusHandle* handle, uint8_t i2c_addr, uint32_t timeout); + +/** Perform I2C device register read (8-bit) + * + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr Register address + * @param data Pointer to register value + * @param timeout Timeout in milliseconds + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_read_reg_8( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint8_t* data, + uint32_t timeout); + +/** Perform I2C device register read (16-bit) + * + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr Register address + * @param data Pointer to register value + * @param timeout Timeout in milliseconds + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_read_reg_16( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint16_t* data, + uint32_t timeout); + +/** Perform I2C device memory read + * + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param mem_addr Memory start address + * @param data Pointer to data buffer + * @param len Size of data buffer + * @param timeout Timeout in milliseconds + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_read_mem( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t mem_addr, + uint8_t* data, + size_t len, + uint32_t timeout); + +/** Perform I2C device register write (8-bit) + * + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr Register address + * @param data Register value + * @param timeout Timeout in milliseconds + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_write_reg_8( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint8_t data, + uint32_t timeout); + +/** Perform I2C device register write (16-bit) + * + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param reg_addr Register address + * @param data Register value + * @param timeout Timeout in milliseconds + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_write_reg_16( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t reg_addr, + uint16_t data, + uint32_t timeout); + +/** Perform I2C device memory + * + * @param handle Pointer to FuriHalI2cBusHandle instance + * @param i2c_addr I2C slave address + * @param mem_addr Memory start address + * @param data Pointer to data buffer + * @param len Size of data buffer + * @param timeout Timeout in milliseconds + * + * @return true on successful transfer, false otherwise + */ +bool furi_hal_i2c_write_mem( + FuriHalI2cBusHandle* handle, + uint8_t i2c_addr, + uint8_t mem_addr, + const uint8_t* data, + size_t len, + uint32_t timeout); + +#ifdef __cplusplus +} +#endif diff --git a/targets/furi_hal_include/furi_hal_info.h b/targets/furi_hal_include/furi_hal_info.h new file mode 100644 index 00000000000..7e8b0e1fb1a --- /dev/null +++ b/targets/furi_hal_include/furi_hal_info.h @@ -0,0 +1,29 @@ +/** + * @file furi_hal_info.h + * Device info HAL API + */ + +#pragma once + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void furi_hal_info_get_api_version(uint16_t* major, uint16_t* minor); + +/** Get device information + * + * @param[in] callback callback to provide with new data + * @param[in] sep category separator character + * @param[in] context context to pass to callback + */ +void furi_hal_info_get(PropertyValueCallback callback, char sep, void* context); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_infrared.h b/targets/furi_hal_include/furi_hal_infrared.h similarity index 100% rename from firmware/targets/furi_hal_include/furi_hal_infrared.h rename to targets/furi_hal_include/furi_hal_infrared.h diff --git a/firmware/targets/furi_hal_include/furi_hal_light.h b/targets/furi_hal_include/furi_hal_light.h similarity index 100% rename from firmware/targets/furi_hal_include/furi_hal_light.h rename to targets/furi_hal_include/furi_hal_light.h diff --git a/targets/furi_hal_include/furi_hal_memory.h b/targets/furi_hal_include/furi_hal_memory.h new file mode 100644 index 00000000000..e9efa08c158 --- /dev/null +++ b/targets/furi_hal_include/furi_hal_memory.h @@ -0,0 +1,44 @@ +/** + * @file furi_hal_memory.h + * Memory HAL API + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Init memory pool manager + */ +void furi_hal_memory_init(); + +/** + * @brief Allocate memory from separate memory pool. That memory can't be freed. + * + * @param size + * @return void* + */ +void* furi_hal_memory_alloc(size_t size); + +/** + * @brief Get free memory pool size + * + * @return size_t + */ +size_t furi_hal_memory_get_free(); + +/** + * @brief Get max free block size from memory pool + * + * @return size_t + */ +size_t furi_hal_memory_max_pool_block(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/firmware/targets/furi_hal_include/furi_hal_mpu.h b/targets/furi_hal_include/furi_hal_mpu.h similarity index 100% rename from firmware/targets/furi_hal_include/furi_hal_mpu.h rename to targets/furi_hal_include/furi_hal_mpu.h diff --git a/targets/furi_hal_include/furi_hal_nfc.h b/targets/furi_hal_include/furi_hal_nfc.h new file mode 100644 index 00000000000..3d145d10026 --- /dev/null +++ b/targets/furi_hal_include/furi_hal_nfc.h @@ -0,0 +1,474 @@ +/** + * @file furi_hal_nfc.h + * @brief NFC HAL library. + * + * This library contains functions and definitions needed for NFC hardware low-level access. + * + * Application developers should first consider using the NFC protocol stack or + * the NFC transport layer and see if the APIs provided there sufficient + * for the applicaton's intended purpose. + * + * @see nfc.h + * @see nfc_protocol.h + * + * If any of the above mentioned options is used, calling any of the functions provided by this + * library is hardly necessary, as it will be taken care of under the hood. + * + */ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief NFC carrier frequency, in Hz. + */ +#define FURI_HAL_NFC_CARRIER_HZ (13560000UL) + +/** + * @brief Special value indicating that waiting for an event shall never time out. + */ +#define FURI_HAL_NFC_EVENT_WAIT_FOREVER (0xFFFFFFFFU) + +/** + * @brief Enumeration of possible NFC HAL events. + */ +typedef enum { + FuriHalNfcEventOscOn = (1U << 0), /**< Oscillator has been started. */ + FuriHalNfcEventFieldOn = (1U << 1), /**< External field (carrier) has been detected. */ + FuriHalNfcEventFieldOff = (1U << 2), /**< External field (carrier) has been lost. */ + FuriHalNfcEventListenerActive = (1U << 3), /**< Reader has issued a wake-up command. */ + FuriHalNfcEventTxStart = (1U << 4), /**< Transmission has started. */ + FuriHalNfcEventTxEnd = (1U << 5), /**< Transmission has ended. */ + FuriHalNfcEventRxStart = (1U << 6), /**< Reception has started. */ + FuriHalNfcEventRxEnd = (1U << 7), /**< Reception has ended. */ + FuriHalNfcEventCollision = (1U << 8), /**< A collision has occurred. */ + FuriHalNfcEventTimerFwtExpired = (1U << 9), /**< Frame wait timer has expired. */ + FuriHalNfcEventTimerBlockTxExpired = (1U << 10), /**< Transmission block timer has expired. */ + FuriHalNfcEventTimeout = + (1U << 11), /**< No events have occurred in a specified time period. */ + FuriHalNfcEventAbortRequest = + (1U << 12), /**< User has requested to abort current operation. */ +} FuriHalNfcEvent; + +/** + * @brief Enumeration of possible NFC HAL errors. + */ +typedef enum { + FuriHalNfcErrorNone, /**< No error has occurred. */ + FuriHalNfcErrorBusy, /**< The communication bus is busy. */ + FuriHalNfcErrorCommunication, /**< NFC hardware did not respond or responded unexpectedly. */ + FuriHalNfcErrorOscillator, /**< Oscillator failed to start. */ + FuriHalNfcErrorCommunicationTimeout, /**< NFC hardware did not respond in time. */ + FuriHalNfcErrorBufferOverflow, /**< Receive buffer was too small for the received data. */ + FuriHalNfcErrorIncompleteFrame, /**< Not enough data was received to parse a valid frame. */ + FuriHalNfcErrorDataFormat, /**< Cannot parse a frame due to unexpected/invalid data. */ +} FuriHalNfcError; + +/** + * @brief Enumeration of possible NFC HAL operating modes. + */ +typedef enum { + FuriHalNfcModePoller, /**< Configure NFC HAL to operate as a poller. */ + FuriHalNfcModeListener, /**< Configure NFC HAL to operate as a listener. */ + + FuriHalNfcModeNum, /**< Special value equal to the operating modes count. Internal use. */ +} FuriHalNfcMode; + +/** + * @brief Enumeration of supported NFC technologies. + */ +typedef enum { + FuriHalNfcTechIso14443a, /**< Configure NFC HAL to use the ISO14443 (type A) technology. */ + FuriHalNfcTechIso14443b, /**< Configure NFC HAL to use the ISO14443 (type B) technology. */ + FuriHalNfcTechIso15693, /**< Configure NFC HAL to use the ISO15693 technology. */ + FuriHalNfcTechFelica, /**< Configure NFC HAL to use the FeliCa technology. */ + + FuriHalNfcTechNum, /**< Special value equal to the supported technologies count. Internal use. */ + FuriHalNfcTechInvalid, /**< Special value indicating the unconfigured state. Internal use. */ +} FuriHalNfcTech; + +/** + * @brief Initialise the NFC HAL and associated hardware. + * + * This function is called automatically during the firmware initialisation, + * so there is no need to call it explicitly. + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_init(); + +/** + * @brief Check whether the NFC HAL was properly initialised and is ready. + * + * @returns FuriHalNfcErrorNone if ready, any other error code if not ready. + */ +FuriHalNfcError furi_hal_nfc_is_hal_ready(); + +/** + * @brief Exclusively take over the NFC HAL and associated hardware. + * + * This function needs to be called whenever an interaction with the NFC HAL + * is to take place (usually once upon the application start). + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_acquire(); + +/** + * @brief Release the exclusive lock and make the NFC HAL available for others. + * + * This function needs to be called when the user code is done working + * with the NFC HAL (usually once upon application exit). It must be called + * from the same thread that has called furi_hal_nfc_acquire(). + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_release(); + +/** + * @brief Configure the NFC hardware to enter the low-power mode. + * + * This function must be called each time when the user code is done working + * with the NFC HAL for the time being (e.g. waiting on user input). + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_low_power_mode_start(); + +/** + * @brief Configure the NFC hardware to exit the low-power mode. + * + * This function must be called each time when the user code begins working + * with the NFC HAL, as the default state is low-power mode. + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_low_power_mode_stop(); + +/** + * @brief Configure the NFC HAL to work in a particular mode. + * + * Not all technologies implement the listener operating mode. + * + * @param[in] mode required operating mode. + * @param[in] tech required technology configuration. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_set_mode(FuriHalNfcMode mode, FuriHalNfcTech tech); + +/** + * @brief Reset the NFC HAL to its default (unconfigured) state. + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_reset_mode(); + +/** + * @brief Enable field (carrier) detection by the NFC hardware. + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_field_detect_start(); + +/** + * @brief Disable field (carrier) detection by the NFC hardware. + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_field_detect_stop(); + +/** + * @brief Check if the reader field (carrier) was detected by the NFC hardware. + * + * @returns true if the field was detected, false otherwise. + */ +bool furi_hal_nfc_field_is_present(); + +/** + * @brief Enable field (carrier) generation by the NFC hardware. + * + * No carrier modulation will occur unless a transmission is explicitly started. + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_poller_field_on(); + +/** + * @brief Wait for an NFC HAL event in poller mode. + * + * @param[in] timeout_ms time to wait (timeout) in milliseconds. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcEvent furi_hal_nfc_poller_wait_event(uint32_t timeout_ms); + +/** + * @brief Wait for an NFC HAL event in listener mode. + * @param[in] timeout_ms time to wait (timeout) in milliseconds. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcEvent furi_hal_nfc_listener_wait_event(uint32_t timeout_ms); + +/** + * @brief Transmit data in poller mode. + * + * @param[in] tx_data pointer to a byte array containing the data to be transmitted. + * @param[in] tx_bits transmit data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_poller_tx(const uint8_t* tx_data, size_t tx_bits); + +/** + * @brief Receive data in poller mode. + * + * The receive buffer must be big enough to accomodate all of the expected data. + * + * @param rx_data[out] pointer to a byte array to be filled with received data. + * @param rx_data_size[in] maximum received data size, in bytes. + * @param rx_bits[out] pointer to the variable to hold received data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_poller_rx(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits); + +/** + * @brief Transmit data in listener mode. + * + * @param[in] tx_data pointer to a byte array containing the data to be transmitted. + * @param[in] tx_bits transmit data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_listener_tx(const uint8_t* tx_data, size_t tx_bits); + +/** + * @brief Receive data in listener mode. + * + * The receive buffer must be big enough to accomodate all of the expected data. + * + * @param rx_data[out] pointer to a byte array to be filled with received data. + * @param rx_data_size[in] maximum received data size, in bytes. + * @param rx_bits[out] pointer to the variable to hold received data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_listener_rx(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits); + +/** + * @brief Go to sleep in listener mode. + * + * Puts the passive target logic into Sleep (Halt) state. + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_listener_sleep(); + +/** + * @brief Go to idle in listener mode. + * + * Puts the passive target logic into Sense (Idle) state. + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_listener_idle(); + +/** + * @brief Enable reception in listener mode. + * + * Starts hardware receivers and receive decoders. + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_listener_enable_rx(); + +/** + * @brief Reset communication. + * + * Resets the communication state and stops all activities: transmission, reception, etc. + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_trx_reset(); + +/** + * @brief Enable generation of NFC HAL events. + * + * @warning This function must be called from the same thread from which + * the the furi_hal_nfc_*_wait_event() calls will be made. + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_event_start(); + +/** + * @brief Disable generation of NFC HAL events. + * + * Unlike furi_hal_nfc_event_start(), this function may be called from any thread. + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_event_stop(); + +/** + * @brief Manually emit the FuriHalNfcEventAbortRequest event. + * + * @returns FuriHalNfcErrorNone on success, any other error code on failure. +*/ +FuriHalNfcError furi_hal_nfc_abort(); + +/** + * @brief Start frame wait timeout timer. + * + * @param[in] time_fc time to wait, in carrier cycles. + */ +void furi_hal_nfc_timer_fwt_start(uint32_t time_fc); + +/** + * @brief Stop frame wait timeout timer. + */ +void furi_hal_nfc_timer_fwt_stop(); + +/** + * @brief Start block transmit (frame delay) timer. + * + * @param[in] time_fc time to wait, in carrier cycles. +*/ +void furi_hal_nfc_timer_block_tx_start(uint32_t time_fc); + +/** + * @brief Start block transmit (frame delay) timer. + * + * @param[in] time_us time to wait, in microseconds. + */ +void furi_hal_nfc_timer_block_tx_start_us(uint32_t time_us); + +/** + * @brief Stop block transmit (frame delay) timer. + */ +void furi_hal_nfc_timer_block_tx_stop(); + +/** + * @brief Check whether block transmit (frame delay) timer is running. + * + * @returns true if timer is running, false otherwise. + */ +bool furi_hal_nfc_timer_block_tx_is_running(); + +/* + * Technology-specific functions. + * + * In a perfect world, this would not be necessary. + * However, the current implementation employs NFC hardware that partially implements + * certain protocols (e.g. ISO14443-3A), thus requiring methods to access such features. + */ + +/******************* Iso14443a specific API *******************/ + +/** + * @brief Enumeration of ISO14443 (Type A) short frame types. + */ +typedef enum { + FuriHalNfcaShortFrameAllReq, + FuriHalNfcaShortFrameSensReq, +} FuriHalNfcaShortFrame; + +/** + * @brief Transmit ISO14443 (Type A) short frame in poller mode. + * + * @param[in] frame short frame type to be transmitted. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_iso14443a_poller_trx_short_frame(FuriHalNfcaShortFrame frame); + +/** Transmit ISO14443 (Type A) SDD frame in poller mode. + * + * @param[in] tx_data pointer to a byte array containing the data to be transmitted. + * @param[in] tx_bits transmit data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_iso14443a_tx_sdd_frame(const uint8_t* tx_data, size_t tx_bits); + +/** + * Receive ISO14443 (Type A) SDD frame in poller mode. + * + * The receive buffer must be big enough to accomodate all of the expected data. + * + * @param rx_data[out] pointer to a byte array to be filled with received data. + * @param rx_data_size[in] maximum received data size, in bytes. + * @param rx_bits[out] pointer to the variable to hold received data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError + furi_hal_nfc_iso14443a_rx_sdd_frame(uint8_t* rx_data, size_t rx_data_size, size_t* rx_bits); + +/** + * @brief Transmit ISO14443 (Type A) frame with custom parity bits in poller mode. + * + * Same as furi_hal_nfc_poller_tx(), but uses the parity bits provided + * by the user code instead of calculating them automatically. + * + * @param[in] tx_data pointer to a byte array containing the data to be transmitted. + * @param[in] tx_bits transmit data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError + furi_hal_nfc_iso14443a_poller_tx_custom_parity(const uint8_t* tx_data, size_t tx_bits); + +/** + * @brief Set ISO14443 (Type A) collision resolution parameters in listener mode. + * + * Configures the NFC hardware for automatic collision resolution. + * + * @param[in] uid pointer to a byte array containing the UID. + * @param[in] uid_len UID length in bytes (must be supported by the protocol). + * @param[in] atqa ATQA byte value. + * @param[in] sak SAK byte value. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_iso14443a_listener_set_col_res_data( + uint8_t* uid, + uint8_t uid_len, + uint8_t* atqa, + uint8_t sak); + +/** + * @brief Transmit ISO14443 (Type A) frame with custom parity bits in listener mode. + * + * @param[in] tx_data pointer to a byte array containing the data to be transmitted. + * @param[in] tx_parity pointer to a (bit-packed) byte array containing the parity to be transmitted. + * @param[in] tx_bits transmit data size, in bits. + * @returns FuriHalNfcErrorNone on success, any other error code on failure. + */ +FuriHalNfcError furi_hal_nfc_iso14443a_listener_tx_custom_parity( + const uint8_t* tx_data, + const uint8_t* tx_parity, + size_t tx_bits); + +/** Send ISO15693 SOF in listener mode + * + * @return FuriHalNfcError +*/ +FuriHalNfcError furi_hal_nfc_iso15693_listener_tx_sof(); + +/** + * @brief Set FeliCa collision resolution parameters in listener mode. + * + * Configures the NFC hardware for automatic collision resolution. + * + * @param[in] idm pointer to a byte array containing the IDm. + * @param[in] idm_len IDm length in bytes. + * @param[in] pmm pointer to a byte array containing the PMm. + * @param[in] pmm_len PMm length in bytes. + * @returns NfcErrorNone on success, any other error code on failure. +*/ +FuriHalNfcError furi_hal_nfc_felica_listener_set_sensf_res_data( + const uint8_t* idm, + const uint8_t idm_len, + const uint8_t* pmm, + const uint8_t pmm_len); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_power.h b/targets/furi_hal_include/furi_hal_power.h similarity index 76% rename from firmware/targets/furi_hal_include/furi_hal_power.h rename to targets/furi_hal_include/furi_hal_power.h index 16af959cf5b..5edda6ba197 100644 --- a/firmware/targets/furi_hal_include/furi_hal_power.h +++ b/targets/furi_hal_include/furi_hal_power.h @@ -7,7 +7,8 @@ #include #include -#include +#include +#include #ifdef __cplusplus extern "C" { @@ -33,6 +34,12 @@ void furi_hal_power_init(); */ bool furi_hal_power_gauge_is_ok(); +/** Check if gauge requests system shutdown + * + * @return true if system shutdown requested + */ +bool furi_hal_power_is_shutdown_requested(); + /** Get current insomnia level * * @return insomnia level: 0 - no insomnia, >0 - insomnia, bearer count. @@ -57,12 +64,6 @@ void furi_hal_power_insomnia_exit(); */ bool furi_hal_power_sleep_available(); -/** Check if deep sleep availble - * - * @return true if available - */ -bool furi_hal_power_deep_sleep_available(); - /** Go to sleep */ void furi_hal_power_sleep(); @@ -85,6 +86,12 @@ uint8_t furi_hal_power_get_bat_health_pct(); */ bool furi_hal_power_is_charging(); +/** Get charge complete status + * + * @return true if done charging and connected to charger + */ +bool furi_hal_power_is_charging_done(); + /** Switch MCU to SHUTDOWN */ void furi_hal_power_shutdown(); @@ -98,12 +105,16 @@ void furi_hal_power_reset(); /** OTG enable */ -void furi_hal_power_enable_otg(); +bool furi_hal_power_enable_otg(); /** OTG disable */ void furi_hal_power_disable_otg(); +/** Check OTG status fault + */ +bool furi_hal_power_check_otg_fault(); + /** Check OTG status and disable it if falt happened */ void furi_hal_power_check_otg_status(); @@ -114,6 +125,22 @@ void furi_hal_power_check_otg_status(); */ bool furi_hal_power_is_otg_enabled(); +/** Get battery charge voltage limit in V + * + * @return voltage in V + */ +float furi_hal_power_get_battery_charge_voltage_limit(); + +/** Set battery charge voltage limit in V + * + * 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_charge_voltage_limit(float voltage); + /** Get remaining battery battery capacity in mAh * * @return capacity in mAh @@ -156,22 +183,12 @@ float furi_hal_power_get_battery_current(FuriHalPowerIC ic); */ float furi_hal_power_get_battery_temperature(FuriHalPowerIC ic); -/** Get System voltage in V - * - * @return voltage in V - */ -float furi_hal_power_get_system_voltage(); - /** Get USB voltage in V * * @return voltage in V */ float furi_hal_power_get_usb_voltage(); -/** Get power system component state - */ -void furi_hal_power_dump_state(); - /** Enable 3.3v on external gpio and sd card */ void furi_hal_power_enable_external_3_3v(); @@ -190,22 +207,20 @@ void furi_hal_power_suppress_charge_enter(); */ void furi_hal_power_suppress_charge_exit(); -/** Callback type called by furi_hal_power_info_get every time another key-value pair of information is ready +/** Get power information * - * @param key[in] power information type identifier - * @param value[in] power information value - * @param last[in] whether the passed key-value pair is the last one - * @param context[in] to pass to callback + * @param[in] callback callback to provide with new data + * @param[in] sep category separator character + * @param[in] context context to pass to callback */ -typedef void ( - *FuriHalPowerInfoCallback)(const char* key, const char* value, bool last, void* context); +void furi_hal_power_info_get(PropertyValueCallback callback, char sep, void* context); -/** Get power information +/** Get power debug information * * @param[in] callback callback to provide with new data * @param[in] context context to pass to callback */ -void furi_hal_power_info_get(FuriHalPowerInfoCallback callback, void* context); +void furi_hal_power_debug_get(PropertyValueCallback callback, void* context); #ifdef __cplusplus } diff --git a/firmware/targets/furi_hal_include/furi_hal_random.h b/targets/furi_hal_include/furi_hal_random.h similarity index 84% rename from firmware/targets/furi_hal_include/furi_hal_random.h rename to targets/furi_hal_include/furi_hal_random.h index ee69326f416..5ca9cb723c3 100644 --- a/firmware/targets/furi_hal_include/furi_hal_random.h +++ b/targets/furi_hal_include/furi_hal_random.h @@ -6,6 +6,9 @@ extern "C" { #endif +/** Initialize random subsystem */ +void furi_hal_random_init(); + /** Get random value * * @return random value diff --git a/firmware/targets/furi_hal_include/furi_hal_region.h b/targets/furi_hal_include/furi_hal_region.h similarity index 96% rename from firmware/targets/furi_hal_include/furi_hal_region.h rename to targets/furi_hal_include/furi_hal_region.h index 10d519ab73c..accd7e127cc 100644 --- a/firmware/targets/furi_hal_include/furi_hal_region.h +++ b/targets/furi_hal_include/furi_hal_region.h @@ -4,6 +4,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { uint32_t start; uint32_t end; @@ -71,3 +75,7 @@ bool furi_hal_region_is_frequency_allowed(uint32_t frequency); * @return { description_of_the_return_value } */ const FuriHalRegionBand* furi_hal_region_get_band(uint32_t frequency); + +#ifdef __cplusplus +} +#endif diff --git a/targets/furi_hal_include/furi_hal_rtc.h b/targets/furi_hal_include/furi_hal_rtc.h new file mode 100644 index 00000000000..c457b690347 --- /dev/null +++ b/targets/furi_hal_include/furi_hal_rtc.h @@ -0,0 +1,285 @@ +/** + * @file furi_hal_rtc.h + * Furi Hal RTC API + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + // Time + uint8_t hour; /**< Hour in 24H format: 0-23 */ + uint8_t minute; /**< Minute: 0-59 */ + uint8_t second; /**< Second: 0-59 */ + // Date + uint8_t day; /**< Current day: 1-31 */ + uint8_t month; /**< Current month: 1-12 */ + uint16_t year; /**< Current year: 2000-2099 */ + uint8_t weekday; /**< Current weekday: 1-7 */ +} FuriHalRtcDateTime; + +typedef enum { + FuriHalRtcFlagDebug = (1 << 0), + FuriHalRtcFlagFactoryReset = (1 << 1), + FuriHalRtcFlagLock = (1 << 2), + FuriHalRtcFlagC2Update = (1 << 3), + FuriHalRtcFlagHandOrient = (1 << 4), + FuriHalRtcFlagLegacySleep = (1 << 5), + FuriHalRtcFlagStealthMode = (1 << 6), + FuriHalRtcFlagDetailedFilename = (1 << 7), +} FuriHalRtcFlag; + +typedef enum { + FuriHalRtcBootModeNormal = 0, /**< Normal boot mode, default value */ + FuriHalRtcBootModeDfu, /**< Boot to DFU (MCU bootloader by ST) */ + FuriHalRtcBootModePreUpdate, /**< Boot to Update, pre update */ + FuriHalRtcBootModeUpdate, /**< Boot to Update, main */ + FuriHalRtcBootModePostUpdate, /**< Boot to Update, post update */ +} FuriHalRtcBootMode; + +typedef enum { + FuriHalRtcHeapTrackModeNone = 0, /**< Disable allocation tracking */ + FuriHalRtcHeapTrackModeMain, /**< Enable allocation tracking for main application thread */ + FuriHalRtcHeapTrackModeTree, /**< Enable allocation tracking for main and children application threads */ + FuriHalRtcHeapTrackModeAll, /**< Enable allocation tracking for all threads */ +} FuriHalRtcHeapTrackMode; + +typedef enum { + FuriHalRtcRegisterHeader, /**< RTC structure header */ + FuriHalRtcRegisterSystem, /**< Various system bits */ + FuriHalRtcRegisterVersion, /**< Pointer to Version */ + FuriHalRtcRegisterLfsFingerprint, /**< LFS geometry fingerprint */ + FuriHalRtcRegisterFaultData, /**< Pointer to last fault message */ + FuriHalRtcRegisterPinFails, /**< Failed pins count */ + /* Index of FS directory entry corresponding to FW update to be applied */ + FuriHalRtcRegisterUpdateFolderFSIndex, + + FuriHalRtcRegisterMAX, /**< Service value, do not use */ +} FuriHalRtcRegister; + +typedef enum { + FuriHalRtcLocaleUnitsMetric = 0, /**< Metric measurement units */ + FuriHalRtcLocaleUnitsImperial = 1, /**< Imperial measurement units */ +} FuriHalRtcLocaleUnits; + +typedef enum { + FuriHalRtcLocaleTimeFormat24h = 0, /**< 24-hour format */ + FuriHalRtcLocaleTimeFormat12h = 1, /**< 12-hour format */ +} FuriHalRtcLocaleTimeFormat; + +typedef enum { + FuriHalRtcLocaleDateFormatDMY = 0, /**< Day/Month/Year */ + FuriHalRtcLocaleDateFormatMDY = 1, /**< Month/Day/Year */ + FuriHalRtcLocaleDateFormatYMD = 2, /**< Year/Month/Day */ +} FuriHalRtcLocaleDateFormat; + +/** Early initialization */ +void furi_hal_rtc_init_early(); + +/** Early de-initialization */ +void furi_hal_rtc_deinit_early(); + +/** Initialize RTC subsystem */ +void furi_hal_rtc_init(); + +/** Force sync shadow registers */ +void furi_hal_rtc_sync_shadow(); + +/** 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 + * + * @param[in] mode The RTC Locale Units + */ +void furi_hal_rtc_set_locale_units(FuriHalRtcLocaleUnits value); + +/** Get RTC Locale Units + * + * @return The RTC Locale Units. + */ +FuriHalRtcLocaleUnits furi_hal_rtc_get_locale_units(); + +/** Set RTC Locale Time Format + * + * @param[in] value The RTC Locale Time Format + */ +void furi_hal_rtc_set_locale_timeformat(FuriHalRtcLocaleTimeFormat value); + +/** Get RTC Locale Time Format + * + * @return The RTC Locale Time Format. + */ +FuriHalRtcLocaleTimeFormat furi_hal_rtc_get_locale_timeformat(); + +/** Set RTC Locale Date Format + * + * @param[in] value The RTC Locale Date Format + */ +void furi_hal_rtc_set_locale_dateformat(FuriHalRtcLocaleDateFormat value); + +/** Get RTC Locale Date Format + * + * @return The RTC Locale Date Format + */ +FuriHalRtcLocaleDateFormat furi_hal_rtc_get_locale_dateformat(); + +/** Set RTC Date Time + * + * @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); + +/** Gets the number of days in the year according to the Gregorian calendar. + * + * @param year Input year. + * + * @return number of days in `year`. + */ +uint16_t furi_hal_rtc_get_days_per_year(uint16_t year); + +/** Check if a year a leap year in the Gregorian calendar. + * + * @param year Input year. + * + * @return true if `year` is a leap year. + */ +bool furi_hal_rtc_is_leap_year(uint16_t year); + +/** Get the number of days in the month. + * + * @param leap_year true to calculate based on leap years + * @param month month to check, where 1 = January + * @return the number of days in the month + */ +uint8_t furi_hal_rtc_get_days_per_month(bool leap_year, uint8_t month); + +#ifdef __cplusplus +} +#endif diff --git a/targets/furi_hal_include/furi_hal_sd.h b/targets/furi_hal_include/furi_hal_sd.h new file mode 100644 index 00000000000..645403b7f54 --- /dev/null +++ b/targets/furi_hal_include/furi_hal_sd.h @@ -0,0 +1,86 @@ +#pragma once +/** + * @file furi_hal_sd.h + * SD Card HAL API + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint64_t capacity; /*!< total capacity in bytes */ + uint32_t block_size; /*!< block size */ + uint32_t logical_block_count; /*!< logical capacity in blocks */ + uint32_t logical_block_size; /*!< logical block size in bytes */ + + uint8_t manufacturer_id; /*!< manufacturer ID */ + char oem_id[3]; /*!< OEM ID, 2 characters + null terminator */ + char product_name[6]; /*!< product name, 5 characters + null terminator */ + uint8_t product_revision_major; /*!< product revision major */ + uint8_t product_revision_minor; /*!< product revision minor */ + uint32_t product_serial_number; /*!< product serial number */ + uint8_t manufacturing_month; /*!< manufacturing month */ + uint16_t manufacturing_year; /*!< manufacturing year */ +} FuriHalSdInfo; + +/** + * @brief Init SD card presence detection + */ +void furi_hal_sd_presence_init(); + +/** + * @brief Get SD card status + * @return true if SD card is present + */ +bool furi_hal_sd_is_present(); + +/** + * @brief SD card max mount retry count + * @return uint8_t + */ +uint8_t furi_hal_sd_max_mount_retry_count(); + +/** + * @brief Init SD card + * @param power_reset reset card power + * @return FuriStatus + */ +FuriStatus furi_hal_sd_init(bool power_reset); + +/** + * @brief Read blocks from SD card + * @param buff + * @param sector + * @param count + * @return FuriStatus + */ +FuriStatus furi_hal_sd_read_blocks(uint32_t* buff, uint32_t sector, uint32_t count); + +/** + * @brief Write blocks to SD card + * @param buff + * @param sector + * @param count + * @return FuriStatus + */ +FuriStatus furi_hal_sd_write_blocks(const uint32_t* buff, uint32_t sector, uint32_t count); + +/** + * @brief Get SD card info + * @param info + * @return FuriStatus + */ +FuriStatus furi_hal_sd_info(FuriHalSdInfo* info); + +/** + * @brief Get SD card state + * @return FuriStatus + */ +FuriStatus furi_hal_sd_get_card_state(); + +#ifdef __cplusplus +} +#endif diff --git a/targets/furi_hal_include/furi_hal_speaker.h b/targets/furi_hal_include/furi_hal_speaker.h new file mode 100644 index 00000000000..0b33d923274 --- /dev/null +++ b/targets/furi_hal_include/furi_hal_speaker.h @@ -0,0 +1,68 @@ +/** + * @file furi_hal_speaker.h + * Speaker HAL + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Init speaker */ +void furi_hal_speaker_init(); + +/** Deinit speaker */ +void furi_hal_speaker_deinit(); + +/** Acquire speaker ownership + * + * @warning You must acquire speaker ownership before use + * + * @param timeout Timeout during which speaker ownership must be acquired + * + * @return bool returns true on success + */ +FURI_WARN_UNUSED bool furi_hal_speaker_acquire(uint32_t timeout); + +/** Release speaker ownership + * + * @warning You must release speaker ownership after use + */ +void furi_hal_speaker_release(); + +/** Check current process speaker ownership + * + * @warning always returns true if called from ISR + * + * @return bool returns true if process owns speaker + */ +bool furi_hal_speaker_is_mine(); + +/** Play a note + * + * @warning no ownership check if called from ISR + * + * @param frequency The frequency + * @param volume The volume + */ +void furi_hal_speaker_start(float frequency, float volume); + +/** Set volume + * + * @warning no ownership check if called from ISR + * + * @param volume The volume + */ +void furi_hal_speaker_set_volume(float volume); + +/** Stop playback + * + * @warning no ownership check if called from ISR + */ +void furi_hal_speaker_stop(); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_spi.h b/targets/furi_hal_include/furi_hal_spi.h similarity index 78% rename from firmware/targets/furi_hal_include/furi_hal_spi.h rename to targets/furi_hal_include/furi_hal_spi.h index df7ffa93d23..af15a88381a 100644 --- a/firmware/targets/furi_hal_include/furi_hal_spi.h +++ b/targets/furi_hal_include/furi_hal_spi.h @@ -8,13 +8,16 @@ extern "C" { #endif /** Early initialize SPI HAL */ -void furi_hal_spi_init_early(); +void furi_hal_spi_config_init_early(); /** Early deinitialize SPI HAL */ -void furi_hal_spi_deinit_early(); +void furi_hal_spi_config_deinit_early(); /** Initialize SPI HAL */ -void furi_hal_spi_init(); +void furi_hal_spi_config_init(); + +/** Initialize SPI DMA HAL */ +void furi_hal_spi_dma_init(); /** Initialize SPI Bus * @@ -82,7 +85,7 @@ bool furi_hal_spi_bus_rx( */ bool furi_hal_spi_bus_tx( FuriHalSpiBusHandle* handle, - uint8_t* buffer, + const uint8_t* buffer, size_t size, uint32_t timeout); @@ -98,11 +101,28 @@ bool furi_hal_spi_bus_tx( */ bool furi_hal_spi_bus_trx( FuriHalSpiBusHandle* handle, - uint8_t* tx_buffer, + const uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, uint32_t timeout); +/** SPI Transmit and Receive with DMA + * + * @param handle pointer to FuriHalSpiBusHandle instance + * @param tx_buffer pointer to tx buffer + * @param rx_buffer pointer to rx buffer + * @param size transaction size (buffer size) + * @param timeout_ms operation timeout in ms + * + * @return true on success + */ +bool furi_hal_spi_bus_trx_dma( + FuriHalSpiBusHandle* handle, + uint8_t* tx_buffer, + uint8_t* rx_buffer, + size_t size, + uint32_t timeout_ms); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/furi_hal_include/furi_hal_usb.h b/targets/furi_hal_include/furi_hal_usb.h similarity index 94% rename from firmware/targets/furi_hal_include/furi_hal_usb.h rename to targets/furi_hal_include/furi_hal_usb.h index 33115c116a3..a98797955da 100644 --- a/firmware/targets/furi_hal_include/furi_hal_usb.h +++ b/targets/furi_hal_include/furi_hal_usb.h @@ -2,6 +2,10 @@ #include "usb.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct FuriHalUsbInterface FuriHalUsbInterface; struct FuriHalUsbInterface { @@ -24,6 +28,7 @@ extern FuriHalUsbInterface usb_cdc_single; extern FuriHalUsbInterface usb_cdc_dual; extern FuriHalUsbInterface usb_hid; extern FuriHalUsbInterface usb_hid_u2f; +extern FuriHalUsbInterface usb_ccid; typedef enum { FuriHalUsbStateEventReset, @@ -81,3 +86,7 @@ void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx); /** Restart USB device */ void furi_hal_usb_reinit(); + +#ifdef __cplusplus +} +#endif diff --git a/targets/furi_hal_include/furi_hal_usb_ccid.h b/targets/furi_hal_include/furi_hal_usb_ccid.h new file mode 100644 index 00000000000..a4880e4b6c4 --- /dev/null +++ b/targets/furi_hal_include/furi_hal_usb_ccid.h @@ -0,0 +1,36 @@ +#pragma once +#include "hid_usage_desktop.h" +#include "hid_usage_button.h" +#include "hid_usage_keyboard.h" +#include "hid_usage_consumer.h" +#include "hid_usage_led.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint16_t vid; + uint16_t pid; + char manuf[32]; + char product[32]; +} FuriHalUsbCcidConfig; + +typedef struct { + void (*icc_power_on_callback)(uint8_t* dataBlock, uint32_t* dataBlockLen, void* context); + void (*xfr_datablock_callback)( + const uint8_t* dataBlock, + uint32_t dataBlockLen, + uint8_t* responseDataBlock, + uint32_t* responseDataBlockLen, + void* context); +} CcidCallbacks; + +void furi_hal_ccid_set_callbacks(CcidCallbacks* cb); + +void furi_hal_ccid_ccid_insert_smartcard(); +void furi_hal_ccid_ccid_remove_smartcard(); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_usb_hid.h b/targets/furi_hal_include/furi_hal_usb_hid.h similarity index 96% rename from firmware/targets/furi_hal_include/furi_hal_usb_hid.h rename to targets/furi_hal_include/furi_hal_usb_hid.h index 97bac7f049f..13e83ef6752 100644 --- a/firmware/targets/furi_hal_include/furi_hal_usb_hid.h +++ b/targets/furi_hal_include/furi_hal_usb_hid.h @@ -5,9 +5,16 @@ #include "hid_usage_consumer.h" #include "hid_usage_led.h" +#ifdef __cplusplus +extern "C" { +#endif + +/** Max number of simultaneously pressed keys (keyboard) */ +#define HID_KB_MAX_KEYS 6 +/** Max number of simultaneously pressed keys (consumer control) */ +#define HID_CONSUMER_MAX_KEYS 2 + #define HID_KEYBOARD_NONE 0x00 -// Remapping the colon key which is shift + ; to comma -#define HID_KEYBOARD_COMMA HID_KEYBOARD_COLON /** HID keyboard modifier keys */ enum HidKeyboardMods { @@ -251,3 +258,7 @@ bool furi_hal_hid_consumer_key_press(uint16_t button); * @param button key code */ bool furi_hal_hid_consumer_key_release(uint16_t button); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_usb_hid_u2f.h b/targets/furi_hal_include/furi_hal_usb_hid_u2f.h similarity index 91% rename from firmware/targets/furi_hal_include/furi_hal_usb_hid_u2f.h rename to targets/furi_hal_include/furi_hal_usb_hid_u2f.h index e9bf57657dc..1b20a33c487 100644 --- a/firmware/targets/furi_hal_include/furi_hal_usb_hid_u2f.h +++ b/targets/furi_hal_include/furi_hal_usb_hid_u2f.h @@ -1,5 +1,9 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + #define HID_U2F_PACKET_LEN 64 typedef enum { @@ -34,3 +38,7 @@ uint32_t furi_hal_hid_u2f_get_request(uint8_t* data); * @param len packet length */ void furi_hal_hid_u2f_send_response(uint8_t* data, uint8_t len); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_version.h b/targets/furi_hal_include/furi_hal_version.h similarity index 89% rename from firmware/targets/furi_hal_include/furi_hal_version.h rename to targets/furi_hal_include/furi_hal_version.h index 96a6f3d5daf..26fa584e903 100644 --- a/firmware/targets/furi_hal_include/furi_hal_version.h +++ b/targets/furi_hal_include/furi_hal_version.h @@ -33,6 +33,7 @@ typedef enum { FuriHalVersionColorUnknown = 0x00, FuriHalVersionColorBlack = 0x01, FuriHalVersionColorWhite = 0x02, + FuriHalVersionColorTransparent = 0x03, } FuriHalVersionColor; /** Device Regions */ @@ -67,6 +68,30 @@ bool furi_hal_version_do_i_belong_here(); */ const char* furi_hal_version_get_model_name(); +/** Get model name + * + * @return model code C-string + */ +const char* furi_hal_version_get_model_code(); + +/** Get FCC ID + * + * @return FCC id as C-string + */ +const char* furi_hal_version_get_fcc_id(); + +/** Get IC id + * + * @return IC id as C-string + */ +const char* furi_hal_version_get_ic_id(); + +/** Get MIC id + * + * @return MIC id as C-string + */ +const char* furi_hal_version_get_mic_id(); + /** Get OTP version * * @return OTP Version diff --git a/firmware/targets/furi_hal_include/furi_hal_vibro.h b/targets/furi_hal_include/furi_hal_vibro.h similarity index 100% rename from firmware/targets/furi_hal_include/furi_hal_vibro.h rename to targets/furi_hal_include/furi_hal_vibro.h